summaryrefslogtreecommitdiffstats
path: root/ldap/servers
diff options
context:
space:
mode:
Diffstat (limited to 'ldap/servers')
-rw-r--r--ldap/servers/Makefile90
-rw-r--r--ldap/servers/plugins/Makefile101
-rw-r--r--ldap/servers/plugins/acl/ACL-Notes215
-rw-r--r--ldap/servers/plugins/acl/Makefile96
-rw-r--r--ldap/servers/plugins/acl/acl.c4118
-rw-r--r--ldap/servers/plugins/acl/acl.h867
-rw-r--r--ldap/servers/plugins/acl/acl_ext.c968
-rw-r--r--ldap/servers/plugins/acl/aclanom.c536
-rw-r--r--ldap/servers/plugins/acl/acldllmain.c128
-rw-r--r--ldap/servers/plugins/acl/acleffectiverights.c674
-rw-r--r--ldap/servers/plugins/acl/aclgroup.c442
-rw-r--r--ldap/servers/plugins/acl/aclinit.c537
-rw-r--r--ldap/servers/plugins/acl/acllas.c3848
-rw-r--r--ldap/servers/plugins/acl/acllist.c940
-rw-r--r--ldap/servers/plugins/acl/aclparse.c1928
-rw-r--r--ldap/servers/plugins/acl/aclplugin.c355
-rw-r--r--ldap/servers/plugins/acl/aclproxy.c195
-rw-r--r--ldap/servers/plugins/acl/aclutil.c1475
-rw-r--r--ldap/servers/plugins/acl/libacl.def16
-rw-r--r--ldap/servers/plugins/chainingdb/Makefile93
-rw-r--r--ldap/servers/plugins/chainingdb/cb.h461
-rw-r--r--ldap/servers/plugins/chainingdb/cb_abandon.c50
-rw-r--r--ldap/servers/plugins/chainingdb/cb_acl.c60
-rw-r--r--ldap/servers/plugins/chainingdb/cb_add.c227
-rw-r--r--ldap/servers/plugins/chainingdb/cb_bind.c290
-rw-r--r--ldap/servers/plugins/chainingdb/cb_cleanup.c22
-rw-r--r--ldap/servers/plugins/chainingdb/cb_close.c67
-rw-r--r--ldap/servers/plugins/chainingdb/cb_compare.c217
-rw-r--r--ldap/servers/plugins/chainingdb/cb_config.c600
-rw-r--r--ldap/servers/plugins/chainingdb/cb_conn_stateless.c1132
-rw-r--r--ldap/servers/plugins/chainingdb/cb_controls.c289
-rw-r--r--ldap/servers/plugins/chainingdb/cb_debug.c23
-rw-r--r--ldap/servers/plugins/chainingdb/cb_delete.c203
-rw-r--r--ldap/servers/plugins/chainingdb/cb_init.c120
-rw-r--r--ldap/servers/plugins/chainingdb/cb_instance.c1871
-rw-r--r--ldap/servers/plugins/chainingdb/cb_modify.c244
-rw-r--r--ldap/servers/plugins/chainingdb/cb_modrdn.c239
-rw-r--r--ldap/servers/plugins/chainingdb/cb_monitor.c228
-rw-r--r--ldap/servers/plugins/chainingdb/cb_schema.c45
-rw-r--r--ldap/servers/plugins/chainingdb/cb_search.c698
-rw-r--r--ldap/servers/plugins/chainingdb/cb_size.c22
-rw-r--r--ldap/servers/plugins/chainingdb/cb_start.c43
-rw-r--r--ldap/servers/plugins/chainingdb/cb_temp.c15
-rw-r--r--ldap/servers/plugins/chainingdb/cb_test.c76
-rw-r--r--ldap/servers/plugins/chainingdb/cb_unbind.c23
-rw-r--r--ldap/servers/plugins/chainingdb/cb_utils.c375
-rw-r--r--ldap/servers/plugins/chainingdb/cbdllmain.c128
-rw-r--r--ldap/servers/plugins/chainingdb/libcb.def15
-rw-r--r--ldap/servers/plugins/collation/Makefile99
-rw-r--r--ldap/servers/plugins/collation/collate.c454
-rw-r--r--ldap/servers/plugins/collation/collate.h36
-rw-r--r--ldap/servers/plugins/collation/collation.def10
-rw-r--r--ldap/servers/plugins/collation/config.c178
-rw-r--r--ldap/servers/plugins/collation/config.h12
-rw-r--r--ldap/servers/plugins/collation/debug.c14
-rw-r--r--ldap/servers/plugins/collation/dllmain.c129
-rw-r--r--ldap/servers/plugins/collation/orfilter.c984
-rw-r--r--ldap/servers/plugins/collation/orfilter.h10
-rw-r--r--ldap/servers/plugins/cos/Makefile79
-rw-r--r--ldap/servers/plugins/cos/cos.c266
-rw-r--r--ldap/servers/plugins/cos/cos.def11
-rw-r--r--ldap/servers/plugins/cos/cos_cache.c3566
-rw-r--r--ldap/servers/plugins/cos/cos_cache.h19
-rw-r--r--ldap/servers/plugins/cos/dllmain.c96
-rw-r--r--ldap/servers/plugins/distrib/Makefile109
-rw-r--r--ldap/servers/plugins/distrib/Makefile.AIX44
-rw-r--r--ldap/servers/plugins/distrib/Makefile.BSDI30
-rw-r--r--ldap/servers/plugins/distrib/Makefile.HPUX27
-rw-r--r--ldap/servers/plugins/distrib/Makefile.HPUX6427
-rw-r--r--ldap/servers/plugins/distrib/Makefile.IRIX30
-rw-r--r--ldap/servers/plugins/distrib/Makefile.Linux30
-rw-r--r--ldap/servers/plugins/distrib/Makefile.OSF129
-rw-r--r--ldap/servers/plugins/distrib/Makefile.ReliantUNIX30
-rw-r--r--ldap/servers/plugins/distrib/Makefile.SOLARIS30
-rw-r--r--ldap/servers/plugins/distrib/Makefile.SOLARIS6430
-rw-r--r--ldap/servers/plugins/distrib/Makefile.SOLARISx8630
-rw-r--r--ldap/servers/plugins/distrib/Makefile.UnixWare30
-rw-r--r--ldap/servers/plugins/distrib/Makefile.UnixWareUDK30
-rw-r--r--ldap/servers/plugins/distrib/Makefile.WINNT38
-rw-r--r--ldap/servers/plugins/distrib/README23
-rw-r--r--ldap/servers/plugins/distrib/distrib.c222
-rw-r--r--ldap/servers/plugins/distrib/distrib.dsp116
-rw-r--r--ldap/servers/plugins/distrib/dllmain.c101
-rw-r--r--ldap/servers/plugins/distrib/libdistrib.def10
-rw-r--r--ldap/servers/plugins/http/Makefile80
-rw-r--r--ldap/servers/plugins/http/dllmain.c98
-rw-r--r--ldap/servers/plugins/http/http.def13
-rw-r--r--ldap/servers/plugins/http/http_client.c290
-rw-r--r--ldap/servers/plugins/http/http_client.h64
-rw-r--r--ldap/servers/plugins/http/http_impl.c1479
-rw-r--r--ldap/servers/plugins/http/http_impl.h25
-rw-r--r--ldap/servers/plugins/passthru/Makefile90
-rw-r--r--ldap/servers/plugins/passthru/PT-Notes30
-rw-r--r--ldap/servers/plugins/passthru/libpassthru.def14
-rw-r--r--ldap/servers/plugins/passthru/passthru.h131
-rw-r--r--ldap/servers/plugins/passthru/ptbind.c144
-rw-r--r--ldap/servers/plugins/passthru/ptconfig.c301
-rw-r--r--ldap/servers/plugins/passthru/ptconn.c420
-rw-r--r--ldap/servers/plugins/passthru/ptdebug.c23
-rw-r--r--ldap/servers/plugins/passthru/ptdllmain.c131
-rw-r--r--ldap/servers/plugins/passthru/ptpreop.c252
-rw-r--r--ldap/servers/plugins/passthru/ptutil.c111
-rw-r--r--ldap/servers/plugins/presence/Makefile85
-rw-r--r--ldap/servers/plugins/presence/dllmain.c96
-rw-r--r--ldap/servers/plugins/presence/images/aim-offline.gifbin0 -> 113 bytes
-rw-r--r--ldap/servers/plugins/presence/images/aim-online.gifbin0 -> 895 bytes
-rw-r--r--ldap/servers/plugins/presence/images/icq-disabled.gifbin0 -> 138 bytes
-rw-r--r--ldap/servers/plugins/presence/images/icq-offline.gifbin0 -> 198 bytes
-rw-r--r--ldap/servers/plugins/presence/images/icq-online.gifbin0 -> 198 bytes
-rw-r--r--ldap/servers/plugins/presence/images/yahoo-offline.gifbin0 -> 84 bytes
-rw-r--r--ldap/servers/plugins/presence/images/yahoo-online.gifbin0 -> 140 bytes
-rw-r--r--ldap/servers/plugins/presence/presence.c1204
-rw-r--r--ldap/servers/plugins/presence/presence.def11
-rw-r--r--ldap/servers/plugins/presence/presence.ldif44
-rw-r--r--ldap/servers/plugins/pwdstorage/Makefile115
-rw-r--r--ldap/servers/plugins/pwdstorage/clear_pwd.c27
-rw-r--r--ldap/servers/plugins/pwdstorage/crypt_pwd.c91
-rw-r--r--ldap/servers/plugins/pwdstorage/dllmain.c91
-rw-r--r--ldap/servers/plugins/pwdstorage/libpwdstorage.def24
-rw-r--r--ldap/servers/plugins/pwdstorage/md5.h63
-rw-r--r--ldap/servers/plugins/pwdstorage/md5c.c337
-rw-r--r--ldap/servers/plugins/pwdstorage/ns-mta-md5_pwd.bu405
-rw-r--r--ldap/servers/plugins/pwdstorage/ns-mta-md5_pwd.c81
-rw-r--r--ldap/servers/plugins/pwdstorage/pwd_init.c146
-rw-r--r--ldap/servers/plugins/pwdstorage/pwdstorage.h99
-rw-r--r--ldap/servers/plugins/pwdstorage/sha_pwd.c111
-rw-r--r--ldap/servers/plugins/pwdstorage/ssha_pwd.c112
-rw-r--r--ldap/servers/plugins/referint/Makefile72
-rw-r--r--ldap/servers/plugins/referint/dllmain.c95
-rw-r--r--ldap/servers/plugins/referint/referint.c808
-rw-r--r--ldap/servers/plugins/referint/referint.def12
-rw-r--r--ldap/servers/plugins/replication/Makefile152
-rw-r--r--ldap/servers/plugins/replication/cl4.h65
-rw-r--r--ldap/servers/plugins/replication/cl4_api.c797
-rw-r--r--ldap/servers/plugins/replication/cl4_api.h67
-rw-r--r--ldap/servers/plugins/replication/cl4_init.c349
-rw-r--r--ldap/servers/plugins/replication/cl5.h38
-rw-r--r--ldap/servers/plugins/replication/cl5_api.c6512
-rw-r--r--ldap/servers/plugins/replication/cl5_api.h478
-rw-r--r--ldap/servers/plugins/replication/cl5_clcache.c910
-rw-r--r--ldap/servers/plugins/replication/cl5_clcache.h22
-rw-r--r--ldap/servers/plugins/replication/cl5_config.c868
-rw-r--r--ldap/servers/plugins/replication/cl5_init.c77
-rw-r--r--ldap/servers/plugins/replication/cl5_test.c830
-rw-r--r--ldap/servers/plugins/replication/cl5_test.h21
-rw-r--r--ldap/servers/plugins/replication/csnpl.c328
-rw-r--r--ldap/servers/plugins/replication/csnpl.h23
-rw-r--r--ldap/servers/plugins/replication/dllmain.c91
-rw-r--r--ldap/servers/plugins/replication/legacy_consumer.c707
-rw-r--r--ldap/servers/plugins/replication/llist.c336
-rw-r--r--ldap/servers/plugins/replication/llist.h26
-rw-r--r--ldap/servers/plugins/replication/profile.c42
-rw-r--r--ldap/servers/plugins/replication/repl.h366
-rw-r--r--ldap/servers/plugins/replication/repl5.h480
-rw-r--r--ldap/servers/plugins/replication/repl5_agmt.c1766
-rw-r--r--ldap/servers/plugins/replication/repl5_agmtlist.c618
-rw-r--r--ldap/servers/plugins/replication/repl5_backoff.c232
-rw-r--r--ldap/servers/plugins/replication/repl5_connection.c1493
-rw-r--r--ldap/servers/plugins/replication/repl5_inc_protocol.c1759
-rw-r--r--ldap/servers/plugins/replication/repl5_init.c572
-rw-r--r--ldap/servers/plugins/replication/repl5_mtnode_ext.c194
-rw-r--r--ldap/servers/plugins/replication/repl5_plugins.c1416
-rw-r--r--ldap/servers/plugins/replication/repl5_prot_private.h66
-rw-r--r--ldap/servers/plugins/replication/repl5_protocol.c502
-rw-r--r--ldap/servers/plugins/replication/repl5_protocol_util.c468
-rw-r--r--ldap/servers/plugins/replication/repl5_replica.c3387
-rw-r--r--ldap/servers/plugins/replication/repl5_replica_config.c750
-rw-r--r--ldap/servers/plugins/replication/repl5_replica_dnhash.c189
-rw-r--r--ldap/servers/plugins/replication/repl5_replica_hash.c243
-rw-r--r--ldap/servers/plugins/replication/repl5_replsupplier.c166
-rw-r--r--ldap/servers/plugins/replication/repl5_ruv.c2022
-rw-r--r--ldap/servers/plugins/replication/repl5_ruv.h88
-rw-r--r--ldap/servers/plugins/replication/repl5_schedule.c742
-rw-r--r--ldap/servers/plugins/replication/repl5_tot_protocol.c372
-rw-r--r--ldap/servers/plugins/replication/repl5_total.c869
-rw-r--r--ldap/servers/plugins/replication/repl5_updatedn_list.c243
-rw-r--r--ldap/servers/plugins/replication/repl_add.c30
-rw-r--r--ldap/servers/plugins/replication/repl_bind.c48
-rw-r--r--ldap/servers/plugins/replication/repl_compare.c34
-rw-r--r--ldap/servers/plugins/replication/repl_connext.c91
-rw-r--r--ldap/servers/plugins/replication/repl_controls.c337
-rw-r--r--ldap/servers/plugins/replication/repl_delete.c26
-rw-r--r--ldap/servers/plugins/replication/repl_entry.c38
-rw-r--r--ldap/servers/plugins/replication/repl_ext.c113
-rw-r--r--ldap/servers/plugins/replication/repl_extop.c1134
-rw-r--r--ldap/servers/plugins/replication/repl_globals.c108
-rw-r--r--ldap/servers/plugins/replication/repl_helper.c85
-rw-r--r--ldap/servers/plugins/replication/repl_helper.h69
-rw-r--r--ldap/servers/plugins/replication/repl_init.c312
-rw-r--r--ldap/servers/plugins/replication/repl_modify.c29
-rw-r--r--ldap/servers/plugins/replication/repl_modrdn.c28
-rw-r--r--ldap/servers/plugins/replication/repl_monitor.c58
-rw-r--r--ldap/servers/plugins/replication/repl_objset.c524
-rw-r--r--ldap/servers/plugins/replication/repl_objset.h37
-rw-r--r--ldap/servers/plugins/replication/repl_opext.c97
-rw-r--r--ldap/servers/plugins/replication/repl_ops.c180
-rw-r--r--ldap/servers/plugins/replication/repl_rootdse.c79
-rw-r--r--ldap/servers/plugins/replication/repl_search.c25
-rw-r--r--ldap/servers/plugins/replication/repl_shared.h132
-rw-r--r--ldap/servers/plugins/replication/replication.def16
-rw-r--r--ldap/servers/plugins/replication/replutil.c1073
-rw-r--r--ldap/servers/plugins/replication/tests/dnp_sim.c1033
-rw-r--r--ldap/servers/plugins/replication/tests/dnp_sim2.c972
-rw-r--r--ldap/servers/plugins/replication/tests/dnp_sim3.c1489
-rwxr-xr-xldap/servers/plugins/replication/tests/makesim58
-rw-r--r--ldap/servers/plugins/replication/urp.c1282
-rw-r--r--ldap/servers/plugins/replication/urp.h45
-rw-r--r--ldap/servers/plugins/replication/urp_glue.c235
-rw-r--r--ldap/servers/plugins/replication/urp_tombstone.c210
-rw-r--r--ldap/servers/plugins/retrocl/Makefile135
-rw-r--r--ldap/servers/plugins/retrocl/dllmain.c90
-rw-r--r--ldap/servers/plugins/retrocl/linktest.c16
-rw-r--r--ldap/servers/plugins/retrocl/retrocl.c341
-rw-r--r--ldap/servers/plugins/retrocl/retrocl.def15
-rw-r--r--ldap/servers/plugins/retrocl/retrocl.h123
-rw-r--r--ldap/servers/plugins/retrocl/retrocl.txt107
-rw-r--r--ldap/servers/plugins/retrocl/retrocl_cn.c391
-rw-r--r--ldap/servers/plugins/retrocl/retrocl_create.c317
-rw-r--r--ldap/servers/plugins/retrocl/retrocl_po.c529
-rw-r--r--ldap/servers/plugins/retrocl/retrocl_rootdse.c64
-rw-r--r--ldap/servers/plugins/retrocl/retrocl_trim.c505
-rw-r--r--ldap/servers/plugins/rever/Makefile111
-rw-r--r--ldap/servers/plugins/rever/des.c465
-rw-r--r--ldap/servers/plugins/rever/dllmain.c91
-rw-r--r--ldap/servers/plugins/rever/libdes.def13
-rw-r--r--ldap/servers/plugins/rever/rever.c77
-rw-r--r--ldap/servers/plugins/rever/rever.h34
-rw-r--r--ldap/servers/plugins/roles/Makefile95
-rw-r--r--ldap/servers/plugins/roles/dllmain.c96
-rw-r--r--ldap/servers/plugins/roles/roles.def10
-rw-r--r--ldap/servers/plugins/roles/roles_cache.c2061
-rw-r--r--ldap/servers/plugins/roles/roles_cache.h52
-rw-r--r--ldap/servers/plugins/roles/roles_plugin.c254
-rw-r--r--ldap/servers/plugins/shared/Makefile56
-rw-r--r--ldap/servers/plugins/shared/plugin-utils.h77
-rw-r--r--ldap/servers/plugins/shared/utils.c467
-rw-r--r--ldap/servers/plugins/statechange/Makefile78
-rw-r--r--ldap/servers/plugins/statechange/dllmain.c96
-rw-r--r--ldap/servers/plugins/statechange/statechange.c450
-rw-r--r--ldap/servers/plugins/statechange/statechange.def10
-rw-r--r--ldap/servers/plugins/syntaxes/Makefile87
-rw-r--r--ldap/servers/plugins/syntaxes/bin.c201
-rw-r--r--ldap/servers/plugins/syntaxes/ces.c168
-rw-r--r--ldap/servers/plugins/syntaxes/cis.c298
-rw-r--r--ldap/servers/plugins/syntaxes/debug.c20
-rw-r--r--ldap/servers/plugins/syntaxes/dllmain.c133
-rw-r--r--ldap/servers/plugins/syntaxes/dn.c98
-rw-r--r--ldap/servers/plugins/syntaxes/int.c188
-rw-r--r--ldap/servers/plugins/syntaxes/libsyntax.def24
-rw-r--r--ldap/servers/plugins/syntaxes/phonetic.c461
-rw-r--r--ldap/servers/plugins/syntaxes/sicis.c139
-rw-r--r--ldap/servers/plugins/syntaxes/string.c612
-rw-r--r--ldap/servers/plugins/syntaxes/syntax.h42
-rw-r--r--ldap/servers/plugins/syntaxes/tel.c135
-rw-r--r--ldap/servers/plugins/syntaxes/value.c209
-rw-r--r--ldap/servers/plugins/uiduniq/7bit.c722
-rw-r--r--ldap/servers/plugins/uiduniq/Makefile99
-rw-r--r--ldap/servers/plugins/uiduniq/UID-Notes93
-rw-r--r--ldap/servers/plugins/uiduniq/libuiduniq.def15
-rw-r--r--ldap/servers/plugins/uiduniq/uid.c1073
-rw-r--r--ldap/servers/plugins/vattrsp_template/Makefile79
-rw-r--r--ldap/servers/plugins/vattrsp_template/dllmain.c96
-rw-r--r--ldap/servers/plugins/vattrsp_template/vattrsp.c397
-rw-r--r--ldap/servers/plugins/vattrsp_template/vattrsp.def10
-rw-r--r--ldap/servers/plugins/views/Makefile79
-rw-r--r--ldap/servers/plugins/views/dllmain.c96
-rw-r--r--ldap/servers/plugins/views/views.c1779
-rw-r--r--ldap/servers/plugins/views/views.def10
-rw-r--r--ldap/servers/slapd/Makefile278
-rw-r--r--ldap/servers/slapd/abandon.c141
-rw-r--r--ldap/servers/slapd/add.c781
-rw-r--r--ldap/servers/slapd/agtmmap.c330
-rw-r--r--ldap/servers/slapd/agtmmap.h191
-rw-r--r--ldap/servers/slapd/apibroker.c245
-rw-r--r--ldap/servers/slapd/attr.c849
-rw-r--r--ldap/servers/slapd/attrlist.c253
-rw-r--r--ldap/servers/slapd/attrsyntax.c906
-rw-r--r--ldap/servers/slapd/auditlog.c217
-rw-r--r--ldap/servers/slapd/auth.c506
-rw-r--r--ldap/servers/slapd/auth.h15
-rw-r--r--ldap/servers/slapd/ava.c129
-rw-r--r--ldap/servers/slapd/back-ldbm/Makefile190
-rw-r--r--ldap/servers/slapd/back-ldbm/ancestorid.c747
-rw-r--r--ldap/servers/slapd/back-ldbm/archive.c332
-rw-r--r--ldap/servers/slapd/back-ldbm/attrcrypt.h33
-rw-r--r--ldap/servers/slapd/back-ldbm/back-ldbm.h629
-rw-r--r--ldap/servers/slapd/back-ldbm/backentry.c89
-rw-r--r--ldap/servers/slapd/back-ldbm/cache.c1195
-rw-r--r--ldap/servers/slapd/back-ldbm/cleanup.c51
-rw-r--r--ldap/servers/slapd/back-ldbm/close.c52
-rw-r--r--ldap/servers/slapd/back-ldbm/dblayer.c5395
-rw-r--r--ldap/servers/slapd/back-ldbm/dblayer.h140
-rw-r--r--ldap/servers/slapd/back-ldbm/dbsize.c25
-rw-r--r--ldap/servers/slapd/back-ldbm/dbtest.c312
-rw-r--r--ldap/servers/slapd/back-ldbm/dbversion.c181
-rw-r--r--ldap/servers/slapd/back-ldbm/dllmain.c128
-rw-r--r--ldap/servers/slapd/back-ldbm/dn2entry.c230
-rw-r--r--ldap/servers/slapd/back-ldbm/entrystore.c12
-rw-r--r--ldap/servers/slapd/back-ldbm/filterindex.c830
-rw-r--r--ldap/servers/slapd/back-ldbm/findentry.c284
-rw-r--r--ldap/servers/slapd/back-ldbm/haschildren.c8
-rw-r--r--ldap/servers/slapd/back-ldbm/id2entry.c250
-rw-r--r--ldap/servers/slapd/back-ldbm/idl.c1599
-rw-r--r--ldap/servers/slapd/back-ldbm/idl_common.c402
-rw-r--r--ldap/servers/slapd/back-ldbm/idl_new.c671
-rw-r--r--ldap/servers/slapd/back-ldbm/idl_shim.c124
-rw-r--r--ldap/servers/slapd/back-ldbm/idlapi.h30
-rw-r--r--ldap/servers/slapd/back-ldbm/import-merge.c680
-rw-r--r--ldap/servers/slapd/back-ldbm/import-threads.c1992
-rw-r--r--ldap/servers/slapd/back-ldbm/import.c1465
-rw-r--r--ldap/servers/slapd/back-ldbm/import.h199
-rw-r--r--ldap/servers/slapd/back-ldbm/index.c1852
-rw-r--r--ldap/servers/slapd/back-ldbm/init.c255
-rw-r--r--ldap/servers/slapd/back-ldbm/instance.c353
-rw-r--r--ldap/servers/slapd/back-ldbm/ldbm_abandon.c14
-rw-r--r--ldap/servers/slapd/back-ldbm/ldbm_add.c880
-rw-r--r--ldap/servers/slapd/back-ldbm/ldbm_attr.c635
-rw-r--r--ldap/servers/slapd/back-ldbm/ldbm_attrcrypt.c870
-rw-r--r--ldap/servers/slapd/back-ldbm/ldbm_attrcrypt_config.c298
-rw-r--r--ldap/servers/slapd/back-ldbm/ldbm_bind.c245
-rw-r--r--ldap/servers/slapd/back-ldbm/ldbm_compare.c77
-rw-r--r--ldap/servers/slapd/back-ldbm/ldbm_config.c1730
-rw-r--r--ldap/servers/slapd/back-ldbm/ldbm_config.h133
-rw-r--r--ldap/servers/slapd/back-ldbm/ldbm_delete.c633
-rw-r--r--ldap/servers/slapd/back-ldbm/ldbm_index_config.c698
-rw-r--r--ldap/servers/slapd/back-ldbm/ldbm_instance_config.c997
-rw-r--r--ldap/servers/slapd/back-ldbm/ldbm_modify.c567
-rw-r--r--ldap/servers/slapd/back-ldbm/ldbm_modrdn.c1403
-rw-r--r--ldap/servers/slapd/back-ldbm/ldbm_search.c1345
-rw-r--r--ldap/servers/slapd/back-ldbm/ldbm_unbind.c14
-rw-r--r--ldap/servers/slapd/back-ldbm/ldif2ldbm.c2440
-rw-r--r--ldap/servers/slapd/back-ldbm/libback-ldbm.def13
-rw-r--r--ldap/servers/slapd/back-ldbm/matchrule.c126
-rw-r--r--ldap/servers/slapd/back-ldbm/misc.c356
-rw-r--r--ldap/servers/slapd/back-ldbm/monitor.c274
-rw-r--r--ldap/servers/slapd/back-ldbm/nextid.c204
-rw-r--r--ldap/servers/slapd/back-ldbm/parents.c105
-rw-r--r--ldap/servers/slapd/back-ldbm/perfctrs.c444
-rw-r--r--ldap/servers/slapd/back-ldbm/perfctrs.h51
-rw-r--r--ldap/servers/slapd/back-ldbm/proto-back-ldbm.h582
-rw-r--r--ldap/servers/slapd/back-ldbm/rmdb.c54
-rw-r--r--ldap/servers/slapd/back-ldbm/seq.c262
-rw-r--r--ldap/servers/slapd/back-ldbm/sort.c1031
-rw-r--r--ldap/servers/slapd/back-ldbm/start.c177
-rw-r--r--ldap/servers/slapd/back-ldbm/tools/index_dump/Makefile40
-rw-r--r--ldap/servers/slapd/back-ldbm/tools/index_dump/index_dump.c206
-rw-r--r--ldap/servers/slapd/back-ldbm/uniqueid2entry.c74
-rw-r--r--ldap/servers/slapd/back-ldbm/upgrade.c352
-rw-r--r--ldap/servers/slapd/back-ldbm/vlv.c1947
-rw-r--r--ldap/servers/slapd/back-ldbm/vlv_key.c69
-rw-r--r--ldap/servers/slapd/back-ldbm/vlv_key.h22
-rw-r--r--ldap/servers/slapd/back-ldbm/vlv_srch.c901
-rw-r--r--ldap/servers/slapd/back-ldbm/vlv_srch.h134
-rw-r--r--ldap/servers/slapd/back-ldif/Makefile81
-rw-r--r--ldap/servers/slapd/back-ldif/add.c189
-rw-r--r--ldap/servers/slapd/back-ldif/back-ldif.h94
-rw-r--r--ldap/servers/slapd/back-ldif/bind.c108
-rw-r--r--ldap/servers/slapd/back-ldif/close.c79
-rw-r--r--ldap/servers/slapd/back-ldif/compare.c82
-rw-r--r--ldap/servers/slapd/back-ldif/config.c203
-rw-r--r--ldap/servers/slapd/back-ldif/delete.c136
-rw-r--r--ldap/servers/slapd/back-ldif/dllmain.c132
-rw-r--r--ldap/servers/slapd/back-ldif/init.c114
-rw-r--r--ldap/servers/slapd/back-ldif/libback-ldif.def12
-rw-r--r--ldap/servers/slapd/back-ldif/modify.c557
-rw-r--r--ldap/servers/slapd/back-ldif/modrdn.c282
-rw-r--r--ldap/servers/slapd/back-ldif/monitor.c124
-rw-r--r--ldap/servers/slapd/back-ldif/search.c197
-rw-r--r--ldap/servers/slapd/back-ldif/start.c31
-rw-r--r--ldap/servers/slapd/back-ldif/unbind.c27
-rw-r--r--ldap/servers/slapd/backend.c505
-rw-r--r--ldap/servers/slapd/backend_manager.c770
-rw-r--r--ldap/servers/slapd/bind.c710
-rw-r--r--ldap/servers/slapd/bitset.c47
-rw-r--r--ldap/servers/slapd/bulk_import.c149
-rw-r--r--ldap/servers/slapd/ch_malloc.c657
-rw-r--r--ldap/servers/slapd/charray.c354
-rw-r--r--ldap/servers/slapd/compare.c153
-rw-r--r--ldap/servers/slapd/computed.c208
-rw-r--r--ldap/servers/slapd/config.c459
-rw-r--r--ldap/servers/slapd/configdse.c515
-rw-r--r--ldap/servers/slapd/connection.c2485
-rw-r--r--ldap/servers/slapd/conntable.c515
-rw-r--r--ldap/servers/slapd/control.c592
-rw-r--r--ldap/servers/slapd/counters.c151
-rw-r--r--ldap/servers/slapd/csn.c383
-rw-r--r--ldap/servers/slapd/csngen.c762
-rw-r--r--ldap/servers/slapd/csngen.h27
-rw-r--r--ldap/servers/slapd/csnset.c360
-rw-r--r--ldap/servers/slapd/daemon.c2694
-rw-r--r--ldap/servers/slapd/defbackend.c200
-rw-r--r--ldap/servers/slapd/delete.c324
-rw-r--r--ldap/servers/slapd/detach.c244
-rw-r--r--ldap/servers/slapd/disconnect_error_strings.h30
-rw-r--r--ldap/servers/slapd/disconnect_errors.h32
-rw-r--r--ldap/servers/slapd/dl.c219
-rw-r--r--ldap/servers/slapd/dn.c1469
-rw-r--r--ldap/servers/slapd/dse.c2304
-rw-r--r--ldap/servers/slapd/dynalib.c86
-rw-r--r--ldap/servers/slapd/entry.c3124
-rw-r--r--ldap/servers/slapd/entrywsi.c1151
-rw-r--r--ldap/servers/slapd/errormap.c186
-rw-r--r--ldap/servers/slapd/eventq.c482
-rw-r--r--ldap/servers/slapd/extendop.c297
-rw-r--r--ldap/servers/slapd/factory.c458
-rw-r--r--ldap/servers/slapd/fe.h159
-rw-r--r--ldap/servers/slapd/fedse.c1886
-rw-r--r--ldap/servers/slapd/fileio.c336
-rw-r--r--ldap/servers/slapd/filter.c1505
-rw-r--r--ldap/servers/slapd/filter.h33
-rw-r--r--ldap/servers/slapd/filtercmp.c402
-rw-r--r--ldap/servers/slapd/filterentry.c1000
-rw-r--r--ldap/servers/slapd/generation.c140
-rw-r--r--ldap/servers/slapd/getfilelist.c233
-rw-r--r--ldap/servers/slapd/getopt_ext.c236
-rw-r--r--ldap/servers/slapd/getopt_ext.h111
-rw-r--r--ldap/servers/slapd/globals.c142
-rw-r--r--ldap/servers/slapd/house.c91
-rw-r--r--ldap/servers/slapd/http.h43
-rw-r--r--ldap/servers/slapd/index_subsys.h47
-rw-r--r--ldap/servers/slapd/index_subsystem.c1286
-rw-r--r--ldap/servers/slapd/init.c63
-rw-r--r--ldap/servers/slapd/intrinsics.h112
-rw-r--r--ldap/servers/slapd/ldbmlinktest.c38
-rw-r--r--ldap/servers/slapd/lenstr.c80
-rw-r--r--ldap/servers/slapd/libglobs.c4051
-rw-r--r--ldap/servers/slapd/libmakefile174
-rw-r--r--ldap/servers/slapd/libsh_stub/Makefile63
-rw-r--r--ldap/servers/slapd/libsh_stub/libsh_stub.c7
-rw-r--r--ldap/servers/slapd/libslapd.def1147
-rw-r--r--ldap/servers/slapd/listConfigAttrs.pl106
-rw-r--r--ldap/servers/slapd/lite_entries.c106
-rw-r--r--ldap/servers/slapd/localhost.c228
-rw-r--r--ldap/servers/slapd/lock.c82
-rw-r--r--ldap/servers/slapd/log.c3664
-rw-r--r--ldap/servers/slapd/log.h186
-rw-r--r--ldap/servers/slapd/main.c2753
-rw-r--r--ldap/servers/slapd/mapping_tree.c3436
-rw-r--r--ldap/servers/slapd/match.c237
-rwxr-xr-xldap/servers/slapd/mkDBErrStrs.pl83
-rw-r--r--ldap/servers/slapd/modify.c966
-rw-r--r--ldap/servers/slapd/modrdn.c501
-rw-r--r--ldap/servers/slapd/modutil.c774
-rw-r--r--ldap/servers/slapd/monitor.c176
-rw-r--r--ldap/servers/slapd/ntmsgdll/Makefile62
-rw-r--r--ldap/servers/slapd/ntmsgdll/ntslapdmessages.c16
-rw-r--r--ldap/servers/slapd/ntmsgdll/ntslapdmessages.mc283
-rw-r--r--ldap/servers/slapd/ntperfdll/Makefile52
-rw-r--r--ldap/servers/slapd/ntperfdll/exports.def9
-rw-r--r--ldap/servers/slapd/ntperfdll/nsldapctr.cpp985
-rw-r--r--ldap/servers/slapd/ntperfdll/nsldapctrdef.h33
-rw-r--r--ldap/servers/slapd/ntperfdll/nsldapctrmc.h122
-rw-r--r--ldap/servers/slapd/ntperfdll/nsldapctrmc.mc74
-rw-r--r--ldap/servers/slapd/ntperfdll/nsldapctrmsg.h60
-rw-r--r--ldap/servers/slapd/ntperfdll/nsldapctrs.h67
-rw-r--r--ldap/servers/slapd/ntperfdll/nsldapctrs.ini57
-rw-r--r--ldap/servers/slapd/ntperfdll/nsldapctrutil.cpp364
-rw-r--r--ldap/servers/slapd/ntperfdll/nsldapctrutil.h120
-rw-r--r--ldap/servers/slapd/ntperfdll/nsldapreg.ini18
-rw-r--r--ldap/servers/slapd/ntuserpin.c178
-rw-r--r--ldap/servers/slapd/ntwdog/Makefile60
-rw-r--r--ldap/servers/slapd/ntwdog/cron_conf.c654
-rw-r--r--ldap/servers/slapd/ntwdog/cron_conf.h86
-rw-r--r--ldap/servers/slapd/ntwdog/ntcron.c156
-rw-r--r--ldap/servers/slapd/ntwdog/ntwatchdog.c1163
-rw-r--r--ldap/servers/slapd/object.c95
-rw-r--r--ldap/servers/slapd/objset.c380
-rw-r--r--ldap/servers/slapd/operation.c410
-rw-r--r--ldap/servers/slapd/opshared.c1163
-rw-r--r--ldap/servers/slapd/pblock.c2930
-rw-r--r--ldap/servers/slapd/plugin.c2833
-rw-r--r--ldap/servers/slapd/plugin_acl.c192
-rw-r--r--ldap/servers/slapd/plugin_internal_op.c864
-rw-r--r--ldap/servers/slapd/plugin_mr.c181
-rw-r--r--ldap/servers/slapd/plugin_role.c30
-rw-r--r--ldap/servers/slapd/plugin_syntax.c360
-rw-r--r--ldap/servers/slapd/poll_using_select.c122
-rw-r--r--ldap/servers/slapd/poll_using_select.h43
-rw-r--r--ldap/servers/slapd/prerrstrs.h121
-rw-r--r--ldap/servers/slapd/protect_db.c758
-rw-r--r--ldap/servers/slapd/protect_db.h75
-rw-r--r--ldap/servers/slapd/proto-slap.h1163
-rw-r--r--ldap/servers/slapd/psearch.c723
-rw-r--r--ldap/servers/slapd/pw.c1540
-rw-r--r--ldap/servers/slapd/pw.h70
-rw-r--r--ldap/servers/slapd/pw_mgmt.c398
-rw-r--r--ldap/servers/slapd/pw_retry.c253
-rw-r--r--ldap/servers/slapd/rdn.c410
-rw-r--r--ldap/servers/slapd/referral.c1205
-rw-r--r--ldap/servers/slapd/regex.c1045
-rw-r--r--ldap/servers/slapd/resourcelimit.c653
-rw-r--r--ldap/servers/slapd/result.c1794
-rw-r--r--ldap/servers/slapd/rootdse.c331
-rw-r--r--ldap/servers/slapd/rwlock.c222
-rw-r--r--ldap/servers/slapd/rwlock.h28
-rw-r--r--ldap/servers/slapd/sasl_io.c334
-rw-r--r--ldap/servers/slapd/sasl_map.c472
-rw-r--r--ldap/servers/slapd/saslbind.c908
-rw-r--r--ldap/servers/slapd/schema.c4664
-rw-r--r--ldap/servers/slapd/schemaparse.c262
-rw-r--r--ldap/servers/slapd/search.c283
-rw-r--r--ldap/servers/slapd/secerrstrs.h431
-rw-r--r--ldap/servers/slapd/security_wrappers.c339
-rw-r--r--ldap/servers/slapd/slap.h1944
-rw-r--r--ldap/servers/slapd/slapd.lite.key8
-rw-r--r--ldap/servers/slapd/slapd.normal.key9
-rw-r--r--ldap/servers/slapd/slapd_plhash.c59
-rw-r--r--ldap/servers/slapd/slapi-plugin-compat4.h132
-rw-r--r--ldap/servers/slapd/slapi-plugin.h1652
-rw-r--r--ldap/servers/slapd/slapi-private.h1212
-rw-r--r--ldap/servers/slapd/slapi2nspr.c274
-rw-r--r--ldap/servers/slapd/snmp_collator.c617
-rw-r--r--ldap/servers/slapd/snmp_collator.h15
-rw-r--r--ldap/servers/slapd/snoop.c39
-rw-r--r--ldap/servers/slapd/ssl.c1499
-rw-r--r--ldap/servers/slapd/sslerrstrs.h355
-rw-r--r--ldap/servers/slapd/start_tls_extop.c479
-rw-r--r--ldap/servers/slapd/statechange.h47
-rw-r--r--ldap/servers/slapd/str2filter.c380
-rw-r--r--ldap/servers/slapd/strdup.c25
-rw-r--r--ldap/servers/slapd/stubrepl.c42
-rw-r--r--ldap/servers/slapd/stubs.c36
-rw-r--r--ldap/servers/slapd/subentry.c56
-rw-r--r--ldap/servers/slapd/task.c1703
-rw-r--r--ldap/servers/slapd/tempnam.c44
-rw-r--r--ldap/servers/slapd/test-plugins/Makefile51
-rw-r--r--ldap/servers/slapd/test-plugins/Makefile.AIX36
-rw-r--r--ldap/servers/slapd/test-plugins/Makefile.BSDI31
-rw-r--r--ldap/servers/slapd/test-plugins/Makefile.HPUX25
-rw-r--r--ldap/servers/slapd/test-plugins/Makefile.HPUX6424
-rw-r--r--ldap/servers/slapd/test-plugins/Makefile.IRIX31
-rw-r--r--ldap/servers/slapd/test-plugins/Makefile.Linux31
-rw-r--r--ldap/servers/slapd/test-plugins/Makefile.OSF130
-rw-r--r--ldap/servers/slapd/test-plugins/Makefile.ReliantUNIX31
-rw-r--r--ldap/servers/slapd/test-plugins/Makefile.SOLARIS28
-rw-r--r--ldap/servers/slapd/test-plugins/Makefile.SOLARIS6428
-rw-r--r--ldap/servers/slapd/test-plugins/Makefile.SOLARISx8631
-rw-r--r--ldap/servers/slapd/test-plugins/Makefile.UnixWare31
-rw-r--r--ldap/servers/slapd/test-plugins/Makefile.UnixWareUDK31
-rw-r--r--ldap/servers/slapd/test-plugins/Makefile.WINNT45
-rw-r--r--ldap/servers/slapd/test-plugins/Makefile.server107
-rw-r--r--ldap/servers/slapd/test-plugins/README149
-rw-r--r--ldap/servers/slapd/test-plugins/clients/README45
-rw-r--r--ldap/servers/slapd/test-plugins/clients/ReqExtOp.java77
-rw-r--r--ldap/servers/slapd/test-plugins/clients/reqextop.c85
-rw-r--r--ldap/servers/slapd/test-plugins/dllmain.c109
-rwxr-xr-xldap/servers/slapd/test-plugins/installDse.pl129
-rw-r--r--ldap/servers/slapd/test-plugins/nicknames10
-rw-r--r--ldap/servers/slapd/test-plugins/testbind.c252
-rw-r--r--ldap/servers/slapd/test-plugins/testdatainterop.c312
-rw-r--r--ldap/servers/slapd/test-plugins/testdbinterop.c102
-rw-r--r--ldap/servers/slapd/test-plugins/testdbinterop.h22
-rw-r--r--ldap/servers/slapd/test-plugins/testentry.c133
-rw-r--r--ldap/servers/slapd/test-plugins/testextendedop.c188
-rw-r--r--ldap/servers/slapd/test-plugins/testgetip.c141
-rw-r--r--ldap/servers/slapd/test-plugins/testplugin.def16
-rw-r--r--ldap/servers/slapd/test-plugins/testplugin.dsp143
-rw-r--r--ldap/servers/slapd/test-plugins/testplugin.mak431
-rw-r--r--ldap/servers/slapd/test-plugins/testpostop.c386
-rw-r--r--ldap/servers/slapd/test-plugins/testpreop.c215
-rw-r--r--ldap/servers/slapd/test-plugins/testsaslbind.c145
-rw-r--r--ldap/servers/slapd/time.c367
-rw-r--r--ldap/servers/slapd/tools/Makefile168
-rw-r--r--ldap/servers/slapd/tools/eggencode.c57
-rw-r--r--ldap/servers/slapd/tools/keyupg.c85
-rw-r--r--ldap/servers/slapd/tools/ldif.c128
-rw-r--r--ldap/servers/slapd/tools/migratecred.c154
-rw-r--r--ldap/servers/slapd/tools/mkdep.c335
-rw-r--r--ldap/servers/slapd/tools/mmldif.c1742
-rw-r--r--ldap/servers/slapd/tools/pwenc.c400
-rw-r--r--ldap/servers/slapd/unbind.c83
-rw-r--r--ldap/servers/slapd/uniqueid.c295
-rw-r--r--ldap/servers/slapd/uniqueidgen.c219
-rw-r--r--ldap/servers/slapd/utf8compare.c2287
-rw-r--r--ldap/servers/slapd/util.c601
-rw-r--r--ldap/servers/slapd/uuid.c893
-rw-r--r--ldap/servers/slapd/uuid.h116
-rw-r--r--ldap/servers/slapd/value.c460
-rw-r--r--ldap/servers/slapd/valueset.c1303
-rw-r--r--ldap/servers/slapd/vattr.c2387
-rw-r--r--ldap/servers/slapd/vattr_spi.h54
-rw-r--r--ldap/servers/slapd/views.h29
-rw-r--r--ldap/servers/snmp/Makefile91
-rw-r--r--ldap/servers/snmp/NETWORK-SERVICES-MIB.txt650
-rw-r--r--ldap/servers/snmp/RFC-1215.txt38
-rw-r--r--ldap/servers/snmp/RFC1155-SMI.txt119
-rw-r--r--ldap/servers/snmp/SNMPv2-CONF.txt322
-rw-r--r--ldap/servers/snmp/SNMPv2-SMI.txt344
-rw-r--r--ldap/servers/snmp/SNMPv2-TC.txt772
-rw-r--r--ldap/servers/snmp/netscape-ldap.mib727
-rw-r--r--ldap/servers/snmp/ntagt/Makefile103
-rw-r--r--ldap/servers/snmp/ntagt/msrvdefs.mak492
-rw-r--r--ldap/servers/snmp/ntagt/nslagtcom_nt.h32
-rw-r--r--ldap/servers/snmp/ntagt/nsldapagt_nt.c1738
-rw-r--r--ldap/servers/snmp/ntagt/nsldapagt_nt.def24
-rw-r--r--ldap/servers/snmp/ntagt/nsldapagt_nt.h228
-rw-r--r--ldap/servers/snmp/ntagt/nsldapmib_nt.c1041
-rw-r--r--ldap/servers/snmp/ntagt/nsldapmib_nt.h130
598 files changed, 260805 insertions, 0 deletions
diff --git a/ldap/servers/Makefile b/ldap/servers/Makefile
new file mode 100644
index 00000000..a02d82c8
--- /dev/null
+++ b/ldap/servers/Makefile
@@ -0,0 +1,90 @@
+#
+# BEGIN COPYRIGHT BLOCK
+# Copyright 2001 Sun Microsystems, Inc.
+# Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+# All rights reserved.
+# END COPYRIGHT BLOCK
+#
+# GNU Makefile for ldap/servers
+#
+
+MCOM_ROOT = ../../..
+LDAP_SRC = ../
+
+NOSTDCLEAN=true # don't let nsconfig.mk define target clean
+NOSTDSTRIP=true # don't let nsconfig.mk define target strip
+NSPR20=true # probably should be defined somewhere else (not sure where)
+
+include $(MCOM_ROOT)/ldapserver/nsdefs.mk
+include $(MCOM_ROOT)/ldapserver/nsconfig.mk
+include $(LDAP_SRC)/nsldap.mk
+
+ifeq ($(ARCH), WINNT)
+PRE_SLAPD_EXTRAS=_ntmsgdll
+POST_SLAPD_EXTRAS=_ntwdog _ntperfdll
+endif
+
+ifeq ($(ARCH), SOLARIS)
+PRE_SLAPD_EXTRAS=_libsh_stub
+endif
+
+all: $(PRE_SLAPD_EXTRAS) _slapd $(POST_SLAPD_EXTRAS) _backends _plugins _snmp _slapdtools
+
+
+ifeq ($(ARCH), HPUX)
+# slapd depends on liblcoll.sl on HPUX....
+_slapd: _collation_plugin
+ cd slapd; $(MAKE) $(MFLAGS) all
+else
+_slapd:
+ cd slapd; $(MAKE) $(MFLAGS) all
+endif
+
+_collation_plugin:
+ cd plugins/collation; $(MAKE) $(MFLAGS) all
+
+_backends:
+ cd slapd/back-ldbm; $(MAKE) $(MFLAGS) all
+# we aren't using back-ldif yet
+# cd slapd/back-ldif; $(MAKE) $(MFLAGS) all
+
+_plugins:
+ cd plugins; $(MAKE) $(MFLAGS) all
+
+_snmp:
+ cd snmp; $(MAKE) $(MFLAGS) all
+
+_slapdtools:
+ cd slapd/tools; $(MAKE) $(MFLAGS) all
+
+ifeq ($(ARCH), WINNT)
+_ntmsgdll:
+ cd slapd/ntmsgdll; $(MAKE) $(MFLAGS) all
+
+_ntwdog:
+ cd slapd/ntwdog; $(MAKE) $(MFLAGS) all
+
+_ntperfdll:
+ cd slapd/ntperfdll; $(MAKE) $(MFLAGS) all
+endif
+
+ifeq ($(ARCH), SOLARIS)
+_libsh_stub:
+ cd slapd/libsh_stub; $(MAKE) $(MFLAGS) all
+endif
+
+
+clean:
+ cd slapd; $(MAKE) $(MFLAGS) clean
+ifeq ($(ARCH), WINNT)
+ cd slapd/ntmsgdll; $(MAKE) $(MFLAGS) clean
+ cd slapd/ntwdog; $(MAKE) $(MFLAGS) clean
+ cd slapd/ntperfdll; $(MAKE) $(MFLAGS) clean
+endif
+ifeq ($(ARCH), SOLARIS)
+ cd slapd/libsh_stub; $(MAKE) $(MFLAGS) clean
+endif
+ cd slapd/back-ldbm; $(MAKE) $(MFLAGS) clean
+ cd slapd/back-ldif; $(MAKE) $(MFLAGS) clean
+ cd plugins; $(MAKE) $(MFLAGS) clean
+ cd slapd/tools; $(MAKE) $(MFLAGS) clean
diff --git a/ldap/servers/plugins/Makefile b/ldap/servers/plugins/Makefile
new file mode 100644
index 00000000..051f3b70
--- /dev/null
+++ b/ldap/servers/plugins/Makefile
@@ -0,0 +1,101 @@
+#
+# BEGIN COPYRIGHT BLOCK
+# Copyright 2001 Sun Microsystems, Inc.
+# Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+# All rights reserved.
+# END COPYRIGHT BLOCK
+#
+#
+# GNU Makefile for Directory Server and LDAP SDK libraries
+#
+
+MCOM_ROOT = ../../../..
+LDAP_SRC = $(MCOM_ROOT)/ldapserver/ldap
+
+NOSTDCLEAN=true # don't let nsconfig.mk define target clean
+NOSTDSTRIP=true # don't let nsconfig.mk define target strip
+NSPR20=true # probably should be defined somewhere else (not sure where)
+
+include $(MCOM_ROOT)/ldapserver/nsconfig.mk
+include $(LDAP_SRC)/nsldap.mk
+
+all: _referint _collation _syntaxes _passthru _utils _uiduniq _roles _acl _replication _cos _pwdstorage _rever _chainingdb _distrib _retrocl _statechange _http _presence _views
+
+_utils:
+ cd shared; $(MAKE) $(MFLAGS) all
+_rever:
+ cd rever; $(MAKE) $(MFLAGS) all
+
+_chainingdb:
+ cd chainingdb; $(MAKE) $(MFLAGS) all
+
+_referint:
+ cd referint; $(MAKE) $(MFLAGS) all
+
+_collation:
+ cd collation; $(MAKE) $(MFLAGS) all
+
+_syntaxes:
+ cd syntaxes; $(MAKE) $(MFLAGS) all
+
+_passthru:
+ cd passthru; $(MAKE) $(MFLAGS) all
+
+_uiduniq:
+ cd uiduniq; $(MAKE) $(MFLAGS) all
+
+_replication:
+ cd replication; $(MAKE) $(MFLAGS) all
+
+_acl:
+ cd acl; $(MAKE) $(MFLAGS) all
+
+_pwdstorage:
+ cd pwdstorage; $(MAKE) $(MFLAGS) all
+
+_distrib:
+ cd distrib; $(MAKE) $(MFLAGS) all
+
+_roles:
+ cd roles; $(MAKE) $(MFLAGS) all
+
+_cos:
+ cd cos; $(MAKE) $(MFLAGS) all
+
+_statechange:
+ cd statechange; $(MAKE) $(MFLAGS) all
+
+_retrocl:
+ cd retrocl; $(MAKE) $(MFLAGS) all
+
+_http:
+ cd http; $(MAKE) $(MFLAGS) all
+
+_presence:
+ cd presence; $(MAKE) $(MFLAGS) all
+
+_views:
+ cd views; $(MAKE) $(MFLAGS) all
+
+clean:
+ cd rever; $(MAKE) $(MFLAGS) clean
+ cd referint; $(MAKE) $(MFLAGS) clean
+ cd collation; $(MAKE) $(MFLAGS) clean
+ cd syntaxes; $(MAKE) $(MFLAGS) clean
+ cd passthru; $(MAKE) $(MFLAGS) clean
+ cd shared; $(MAKE) $(MFLAGS) clean
+ cd uiduniq; $(MAKE) $(MFLAGS) clean
+ cd replication; $(MAKE) $(MFLAGS) clean
+ cd acl; $(MAKE) $(MFLAGS) clean
+ cd cos; $(MAKE) $(MFLAGS) clean
+ cd pwdstorage; $(MAKE) $(MFLAGS) clean
+ cd roles; $(MAKE) $(MFLAGS) clean
+ cd chainingdb; $(MAKE) $(MFLAGS) clean
+ cd distrib; $(MAKE) $(MFLAGS) clean
+ cd retrocl; $(MAKE) $(MFLAGS) clean
+ cd statechange; $(MAKE) $(MFLAGS) clean
+ cd presence; $(MAKE) $(MFLAGS) clean
+ cd http; $(MAKE) $(MFLAGS) clean
+ cd views; $(MAKE) $(MFLAGS) clean
+
+veryclean: clean
diff --git a/ldap/servers/plugins/acl/ACL-Notes b/ldap/servers/plugins/acl/ACL-Notes
new file mode 100644
index 00000000..e275c967
--- /dev/null
+++ b/ldap/servers/plugins/acl/ACL-Notes
@@ -0,0 +1,215 @@
+#
+# BEGIN COPYRIGHT BLOCK
+# Copyright 2001 Sun Microsystems, Inc.
+# Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+# All rights reserved.
+# END COPYRIGHT BLOCK
+#
+
+
+Date What ?
+===================================
+10/15/98 - Created the ACL plugin
+ - Created a new file aclplugin.c and split the old
+ acl.c to acl.c & aclparse.c files.
+ - Merged changes made upt 4.0B2
+10/21/98 - Added USERATTR rule.
+
+
+02/01/99 - Cleanup needed to be done in 5.0 to make it a real plugin
+=====================================================================================
+1. Do not use slap.h but use slapi-plugin.h. This will require
+ some work. Work involves
+ 1) Making the ACLCB an extensible object of CONN struct
+ 2) Remove reference of Connection & operation struct
+ 3) Need slapi plugin apis to get the IP and DNS so that
+ we can evaluate it in the LASes.
+ 4) Need new option to get values of conn , op & pb stuct like
+ cert, authtype,
+
+2. Make ACLPB hang from the Operation struct instead of the PBlock.
+3. Make ACLCB an extensible object of CONN struct and remove any reference
+ about acl private info.
+
+4. I implemented the Userattr rule before even deciding if we need in 5.0
+ or not. I think it is useful. The documents those were based on are
+ in http://jazz/users/prasanta/acl_manage_filter
+
+5. Move acllas_dn_parent to the libslapd. This is duplicated code and is
+ BAAAD.
+
+6. Use the new normalized dn code so that we don't have to it over and over again.
+ We have to very careful ins slapi_access_allowed() as we keep the dn around and
+ free it later ( we can use dn by ref ).
+
+7. Merge from DS4.1 ( proxy auth) to DS 5.0.
+
+8. Miscs
+ a) can we use the SDK URL parsing code ?
+ b) Merge teh printing routines ( it's all over ).
+
+My estimate for doing the above cleanup will require anywhere between 5 to 8 days.
+Run the ACL tests after all the changes -- that is a MUST.
+===============================
+04/28/99
+
+ -- All the work descibed above is done.
+ -- Also
+ a) Created a Pool pf ACLPB one of which is grabed at the init time.
+ b) Created a global lockarary which takes care of the concurreny issue between
+ aclpb & aclcb
+ c) Fixed plugin init.
+
+
+I think the userattr rule should be made generic
+
+ useAttr = "attrName#Type"
+
+ <Type> :== DN | GROUP | ROLE | URL | <value>
+ <value> :== < any printable String>
+
+Example:
+ userAttr = "manager#DN" --- similar to userdnattr
+ userAttr = "owner#GROUP" --- similar to groupdnattr
+ userAttr = "attr#ROLE" --- The value of attr contains a role definition
+ userAttr = "myattr#URL" --- The value contains a URL or filter
+ userAttr = "OU#Directory Server"
+ --- In this case the client's OU and the
+ resource entry's OU must have
+ "Directory Server" value.
+
+ This way we can get rid of userdnattr and groupdnattr and accomplish a
+ lot with a single rule.
+
+At this point, we are done with the changes and waiting for what needs to be
+done in 5.0.
+=================================
+06/01/1999
+ -- Split the code into smaller modules
+ ( aclanom, aclgroup, aclinit, ...)
+ --- The ACLs are read and kept in a AVL tree.
+ --- Few bugs fixed in the acl_scan_match code.
+
+================================================
+07/02/99
+
+ -- Added support for parameterized bind rules.
+ -- Added support for caching of ATTR rules using recompute.S
+
+ What's left for 5.0
+ -------------------
+ 1. Support for roles
+ 2. Re-architect user/group cache
+ 3. startup in multiple threads ( low priority)
+ 4. look at add/delete/modrdn operations.
+ 5. cleanup:
+ - revist all the debug statements
+ - new tests etc.
+ 6. UI work
+
+============
+commit:14/12/99 rbyrne
+
+. Added targattrfilters keyword for value based acls.
+ Required also slapi_filter_apply(), slapi_get_attribute_type()
+ and slapi_attr_syntax_normalize() in slapd (filter.c and attrsyntax.c).
+. Memory leak fix in acl.c for PListInit() call--see comments in code.
+. made access an int on it's own to give room for expansion
+ (see aci_access and aclpb_access)
+. files: ACL-Notes, acl.c acl.h acl-ext.c aclanom.c acllas.c acllist.c aclparse.c aclutil.c slapd/attrsyntax.c slapd/slapi-plugin.h slapd/filter.c slapd/libslapd.def
+
+===
+commit: Mon 20th Dec 199
+. aclparse.c: add proxy back to acl_access2str
+. filter.c: get_filter() does not recurse anymore--get_fitler_internal(), get_filter_list()
+do the recursion...this way testing for ldapsubentry works.
+. aclinit.c: now have filter (|(aci=*)(objectclass=ldapsubentry)) in
+aclinit_search_and_insert_aci(). This means that when slapi_search_internal_callback()
+stops returning subentries by default, we will still get them as we have the correct filter.
+
+===
+commit: 12/01/2000:
+. aclplugin.c: fix for proxyauth bug in aclplugin_preop_search() and
+acl_plugin_preop_modify()--the proxy_dn and dn were swapped.
+. acl_ext.c: Also, when we PListAssignValue() on DS_ATTR_USERDN in acl_init_aclpb(),
+we should pass it a dn from aclpb_sdn, NOT the dn passed into acl_init_aclpb() which
+gets freed after the call to acl_init_acpb(). JAlso here need to be careful thatif dn contains NULL that we indicate this in aclpb_sdn by setting dn to a non-NULL empty string ("") which the code takes to be anon.
+. checked that none of the PList objects (DS_PROP_ACLPB, DS_ATTR_USERDN, DS_ATTR_ENTRY) have mem leak problems.
+. acl.c, acllas.c, aclproxy.c: removed some #ifdef 0 and comments--tidy up but
+no code changes.
+. acl_ext.c: in acl__done_aclpb() we need to PListDleteProp() on ACL_ATTR_IP
+and ACL_ATTR_DNS. This is because if LASIpEval/ACL_GetAttribute() and
+LASDnsEval/ACL_GetAttribute() see that these properties exist, they do
+not bother calling the respective Getter() function. So, everytime
+the aclpb is reused and ip or dns eval is required, the old value is used (
+or whatever hjappens to be in the memory.). Tested--works fine now with ip and dns keywords. ALso tested that when the same user tries an a non-allowed machine he is not allowed by accident (as he was before).
+. in schema.c/oc_find(): normalize the objectclass name before looking for it. Otherwise
+if there's a trailing space in the oc name, you won't dfind it.
+
+===
+commit:
+
+. aclparse.c: fix for syntax.ksh tp6 test: if there is no "version" in an aci item, reject it.
+. acllas.c: in DS_UserDnEval() now call slapi_normalize_dn() when comparing param strings and
+ ordinary dns.
+. acl_ext.c: when seeting DS_USER_DN_ATTR, get the ndn, the normalized form.
+
+====
+commit: 7/02/2000
+anom profile and groupdn != don't work together! Bug 381830 in 4.X
+. acl.h: new bit in aci_type to mark as below.
+. aclparse.c: mark an aci if it's like deny() groupdn != blah
+. aclanom.c: if marked like that cancel anom profile (just like userdn !=)
+==
+. removed these for the mo...
+commit:
+. acllas.c: now get the vattrs via slapi_vattr_merge_copy() when testing the client entry.
+. vattr.c: assign i the length of the list:i = type_context.list_length;
+. entry.c: slapi_entry_add_valueset()
+
+==
+
+commit: 03/03/2000
+. support for roledn in acis.
+===
+. acllist: in slapi_sdn_free(&aciListHead->acic_sdn); gbeelato's mem leak fix.
+commited
+
+=====
+
+committed: 17/008/00
+. support for $dn: aclutil.c, aclparse.c, acllist.c, acllas.c, acl.c, acl.h
+. acl_ext.c:Make sure aclpb_search_base is initialized to NULL in aclpb__malloc()
+. acl.c: set_result_status: wrong bit masks were being used in a_eval->attrEval_s_astatus etc.
+ acl__attr_cached_result(): in the attr==NULL case, need to test for potential
+"recompute" case of attribute--this happens if it's a param or attr style aci.
+
+========
+commited
+Support for dynamic backends:
+. acllist.c, aclinit.c, libslapd.def, control.c, slapi-plugin.h:
+ acl_be_state_change_fnc(), slapi_build_control_from_berval() etc.
+. aclanom.c: logical error in aclanom_match_profile() was causing misctest4 to fail.
+. acl_ext.c:fix mem leak by calling acl_clean_aclEval_control() in acl_ext_conn_desctructor()
+.
+===
+committed:24 Aug 2000
+now SLAPI_ACL_ALL (allow(all)) does NOT include proxy right
+
+==
+committed: 30 Aug 2000
+. acl.c: new print_access_control_Summary() routine to display final acl status. Gets the proxy
+ stuff right too.
+ in acl__resource_match_aci() always test the TARGET_FILTER case, the old cod ethere was wrong.
+==
+. add support for macros to userdn ldapurl keyword.
+
+
+==
+Committed:
+. Sep 07 2000: Support for $attr in macros.
+. Sep 15 2000: Support for aci macros in targetfilter keyword.
+. Sep 18 2000: improve ret code handling in __aclinit_handler--stops spurious error message.
+
+
+--eof
diff --git a/ldap/servers/plugins/acl/Makefile b/ldap/servers/plugins/acl/Makefile
new file mode 100644
index 00000000..bdfe2dc0
--- /dev/null
+++ b/ldap/servers/plugins/acl/Makefile
@@ -0,0 +1,96 @@
+#
+# BEGIN COPYRIGHT BLOCK
+# Copyright 2001 Sun Microsystems, Inc.
+# Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+# All rights reserved.
+# END COPYRIGHT BLOCK
+#
+#
+# GNU Makefile for Directory Server acl-plugin.so acl plugins
+#
+
+LDAP_SRC = ../../..
+MCOM_ROOT = ../../../../..
+
+NOSTDCLEAN=true # don't let nsconfig.mk define target clean
+NOSTDSTRIP=true # don't let nsconfig.mk define target strip
+NSPR20=true # probably should be defined somewhere else (not sure where)
+
+OBJDEST = $(OBJDIR)/lib/libacl
+LIBDIR = $(LIB_RELDIR)
+
+include $(MCOM_ROOT)/ldapserver/nsconfig.mk
+include $(LDAP_SRC)/nsldap.mk
+
+# ACL plugin depends on libadminutil
+MCC_INCLUDE += $(ADMINUTIL_INCLUDE)
+
+ifeq ($(ARCH), WINNT)
+DEF_FILE:=./libacl.def
+endif
+
+CFLAGS+=$(SLCFLAGS)
+
+INCLUDES += -I$(LDAP_SRC)/servers/slapd -I$(ACLINC)
+
+ACL_OBJS= acl.o acllas.o aclutil.o aclplugin.o aclparse.o acl_ext.o aclproxy.o \
+ aclinit.o aclgroup.o aclanom.o acllist.o acleffectiverights.o
+
+OBJS = $(addprefix $(OBJDEST)/, $(ACL_OBJS))
+
+ifeq ($(ARCH), WINNT)
+LIBACL_DLL_OBJ = $(addprefix $(OBJDEST)/, acldllmain.o)
+endif
+
+LIBACL= $(addprefix $(LIBDIR)/, $(ACL_DLL).$(DLL_SUFFIX))
+
+ifeq ($(ARCH), WINNT)
+EXTRA_LIBS_DEP += $(LIBSLAPD_DEP)
+EXTRA_LIBS_DEP += $(LDAPSDK_DEP) $(NSPR_DEP)
+EXTRA_LIBS += $(LIBSLAPD) $(NSPRLINK) $(LDAP_LIBAVL) $(LDAP_SDK_LIBLDAP_DLL)
+endif
+
+# ACL plugin depends on libadminutil (through libns-httpd)
+EXTRA_LIBS_DEP += $(NSHTTPD_DEP) $(ADMINUTIL_DEP) $(DBM_DEP)
+EXTRA_LIBS += $(DYN_NSHTTPD) $(ADMINUTIL_LINK) $(DBMLINK)
+
+ifeq ($(ARCH), WINNT)
+EXTRA_LIBS_DEP += $(LIBACCESS_DEP)
+EXTRA_LIBS += $(LIBACCESS)
+endif
+
+ifeq ($(ARCH), WINNT)
+DLL_LDFLAGS += -def:"./libacl.def"
+endif # WINNT
+
+ifeq ($(ARCH), AIX)
+EXTRA_LIBS_DEP += $(LIBSLAPD_DEP)
+EXTRA_LIBS_DEP += $(LDAPSDK_DEP) $(NSPR_DEP)
+EXTRA_LIBS += $(LIBSLAPDLINK) $(NSPRLINK) $(LDAP_LIBAVL) $(LDAP_SDK_LIBLDAP_DLL)
+EXTRA_LIBS += $(DLL_EXTRA_LIBS)
+LD=ld
+endif
+
+ifeq ($(ARCH), HPUX)
+EXTRA_LIBS_DEP += $(LDAPSDK_DEP) $(NSPR_DEP) $(SECURITY_DEP)
+EXTRA_LIBS += $(DYN_NSHTTPD) $(ADMINUTIL_LINK) $(LDAPLINK) $(SECURITYLINK) $(NSPRLINK) $(ICULINK)
+endif
+
+clientSDK:
+
+all: $(OBJDEST) $(LIBDIR) $(LIBACL)
+
+$(LIBACL): $(OBJS) $(LIBACL_DLL_OBJ) $(DEF_FILE)
+ $(LINK_DLL) $(LIBACL_DLL_OBJ) $(PLATFORMLIBS) $(EXTRA_LIBS)
+
+veryclean: clean
+
+clean:
+ $(RM) $(OBJS)
+ifeq ($(ARCH), WINNT)
+ $(RM) $(LIBACL_DLL_OBJ)
+endif
+ $(RM) $(LIBACL)
+
+$(OBJDEST):
+ $(MKDIR) $(OBJDEST)
diff --git a/ldap/servers/plugins/acl/acl.c b/ldap/servers/plugins/acl/acl.c
new file mode 100644
index 00000000..8dfbd52a
--- /dev/null
+++ b/ldap/servers/plugins/acl/acl.c
@@ -0,0 +1,4118 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+#include "acl.h"
+
+/****************************************************************************
+*
+* acl.c
+*
+*
+* This file contains the functions related to Access Control List (ACL)
+* checking. The ACL checking is based on the ONE ACL design implemented
+* in the Web server 2.0. For more information on the ACL design look
+* into the barracuda home page.
+*
+*
+******************************************************************************/
+
+
+/****************************************************************************/
+/* Globals. Must be protected by Mutex. */
+/****************************************************************************/
+/* Signatures to see if things have changed */
+static short acl_signature = 0;
+
+/****************************************************************************/
+/* Defines, Constants, ande Declarations */
+/****************************************************************************/
+static char *ds_map_generic[2] = { NULL, NULL };
+
+/****************************************************************************/
+/* prototypes */
+/****************************************************************************/
+static int acl__resource_match_aci(struct acl_pblock *aclpb, aci_t *aci ,
+ int skip_attrEval, int *a_matched);
+static acl__TestRights(Acl_PBlock *aclpb,int access, char **right,
+ char ** map_generic, aclResultReason_t *result_reason);
+static int acl__scan_for_acis(struct acl_pblock *aclpb, int *err);
+static void acl__reset_cached_result (struct acl_pblock *aclpb );
+static int acl__scan_match_handles ( struct acl_pblock *aclpb, int type);
+static int acl__attr_cached_result (struct acl_pblock *aclpb, char *attr, int access );
+static int acl__match_handlesFromCache (struct acl_pblock *aclpb, char *attr, int access);
+static int acl__get_attrEval ( struct acl_pblock *aclpb, char *attr );
+static int acl__config_get_readonly ();
+static int acl__recompute_acl (Acl_PBlock *aclpb, AclAttrEval *a_eval,
+ int access, int aciIndex);
+static void __acl_set_aclIndex_inResult ( Acl_PBlock *aclpb,
+ int access, int index );
+static int acl__make_filter_test_entry ( Slapi_Entry **entry,
+ char *attr_type, struct berval *attr_val);
+static int acl__test_filter ( Slapi_Entry *entry, struct slapi_filter *f,
+ int filter_sense);
+static void print_access_control_summary( char * source,
+ int ret_val, char *clientDn,
+ struct acl_pblock *aclpb,
+ char *right,
+ char *attr,
+ const char *edn,
+ aclResultReason_t *acl_reason);
+static int check_rdn_access( Slapi_PBlock *pb,Slapi_Entry *e, char * newrdn,
+ int access);
+
+
+/*
+ * Check the rdn permissions for this entry:
+ * require: write access to the entry, write (add) access to the new
+ * naming attribute, write (del) access to the old naming attribute if
+ * deleteoldrdn set.
+ *
+ * Valid only for the modrdn operation.
+*/
+int
+acl_access_allowed_modrdn(
+ Slapi_PBlock *pb,
+ Slapi_Entry *e, /* The Slapi_Entry */
+ char *attr, /* Attribute of the entry */
+ struct berval *val, /* value of attr. NOT USED */
+ int access /* requested access rights */
+ )
+{
+ int retCode ;
+ char *newrdn, *oldrdn;
+ int deleteoldrdn = 0;
+
+ /*
+ * First check write permission on the entry--this is actually
+ * specially for modrdn.
+ */
+ retCode = acl_access_allowed ( pb, e, NULL /* attr */, NULL /* val */,
+ SLAPI_ACL_WRITE);
+
+ if ( retCode != LDAP_SUCCESS ) {
+ slapi_log_error( SLAPI_LOG_ACL, plugin_name,
+ "modrdn:write permission to entry not allowed\n");
+ return(retCode);
+ }
+
+ /* Now get the new rdn attribute name and value */
+
+ slapi_pblock_get( pb, SLAPI_MODRDN_TARGET, &oldrdn );
+ slapi_pblock_get( pb, SLAPI_MODRDN_NEWRDN, &newrdn );
+
+ /* Check can add the new naming attribute */
+ retCode = check_rdn_access( pb, e, newrdn, ACLPB_SLAPI_ACL_WRITE_ADD) ;
+ if ( retCode != LDAP_SUCCESS ) {
+ slapi_log_error( SLAPI_LOG_ACL, plugin_name,
+ "modrdn:write permission to add new naming attribute not allowed\n");
+ return(retCode);
+ }
+
+ /* Check can delete the new naming attribute--if required */
+ slapi_pblock_get( pb, SLAPI_MODRDN_DELOLDRDN, &deleteoldrdn );
+ if ( deleteoldrdn ) {
+ retCode = check_rdn_access( pb, e, oldrdn, ACLPB_SLAPI_ACL_WRITE_DEL) ;
+ if ( retCode != LDAP_SUCCESS ) {
+ slapi_log_error( SLAPI_LOG_ACL, plugin_name,
+ "modrdn:write permission to delete old naming attribute not allowed\n");
+ return(retCode);
+ }
+ }
+
+ return(retCode);
+
+}
+/*
+ * Test if have access to make the first rdn of dn in entry e.
+*/
+
+static int check_rdn_access( Slapi_PBlock *pb, Slapi_Entry *e, char *dn,
+ int access) {
+
+ char **dns;
+ char **rdns;
+ int retCode = LDAP_INSUFFICIENT_ACCESS;
+ int i;
+
+ if ( (dns = ldap_explode_dn( dn, 0 )) != NULL ) {
+
+ if ( (rdns = ldap_explode_rdn( dns[0], 0 )) != NULL ) {
+
+ for ( i = 0; rdns[i] != NULL; i++ ) {
+ char *type;
+ struct berval bv;
+
+ if ( slapi_rdn2typeval( rdns[i], &type, &bv ) != 0 ) {
+ char ebuf[ BUFSIZ ];
+ slapi_log_error( SLAPI_LOG_ACL, plugin_name,
+ "modrdn: rdn2typeval (%s) failed\n",
+ escape_string( rdns[i], ebuf ));
+ retCode = LDAP_INSUFFICIENT_ACCESS;
+ break;
+ } else {
+ if ( (retCode = acl_access_allowed ( pb, e, type /* attr */,
+ &bv /* val */,
+ access)) != LDAP_SUCCESS) {
+ break;
+ }
+ }
+ }
+ ldap_value_free( rdns );
+ }
+ ldap_value_free( dns );
+ }
+
+ return(retCode);
+}
+
+/***************************************************************************
+*
+* acl_access_allowed
+* Determines if access to the resource is allowed or not.
+*
+* Input:
+* 
+*
+* Returns:
+*
+* Returns success/Denied/error condition
+*
+* LDAP_SUCCESS -- access allowed
+* LDAP_INSUFFICIENT_ACCESS -- access denied
+*
+* Errors returned:
+*
+* Some of the definition of the return values used copied from
+* "ldap.h" for convienience.
+* LDAP_OPERATIONS_ERROR
+* LDAP_PROTOCOL_ERROR
+* LDAP_UNWILLING_TO_PERFORM
+*
+*
+* Error Handling:
+* Returned error code.
+**************************************************************************/
+int
+acl_access_allowed(
+ Slapi_PBlock *pb,
+ Slapi_Entry *e, /* The Slapi_Entry */
+ char *attr, /* Attribute of the entry */
+ struct berval *val, /* value of attr. NOT USED */
+ int access /* requested access rights */
+ )
+{
+ char *n_edn; /* Normalized DN of the entry */
+ int rv;
+ int err;
+ int ret_val;
+ char *right;
+ int num_handle;
+ struct acl_pblock *aclpb = NULL;
+ AclAttrEval *c_attrEval = NULL;
+ int got_reader_locked = 0;
+ int deallocate_attrEval = 0;
+ char ebuf [ BUFSIZ ];
+ char *clientDn;
+ Slapi_DN *e_sdn;
+ Slapi_Operation *op = NULL;
+ aclResultReason_t decision_reason;
+ int loglevel;
+
+ loglevel = slapi_is_loglevel_set(SLAPI_LOG_ACL) ? SLAPI_LOG_ACL : SLAPI_LOG_ACLSUMMARY;
+ slapi_pblock_get(pb, SLAPI_OPERATION, &op); /* for logging */
+
+ TNF_PROBE_1_DEBUG(acl_access_allowed_start,"ACL","",
+ tnf_int,access,access);
+
+ decision_reason.deciding_aci = NULL;
+ decision_reason.reason = ACL_REASON_NONE;
+
+ /**
+ * First, if the acl private write/delete on attribute right
+ * is requested, turn this into SLAPI_ACL_WRITE
+ * and record the original value.
+ * Need to make sure that these rights do not clash with the SLAPI
+ * public rights. This should be easy as the requested rights
+ * in the aclpb are stored in the bottom byte of aclpb_res_type,
+ * so if we keep the ACL private bits here too we make sure
+ * not to clash.
+ *
+ */
+
+ if ( access & (ACLPB_SLAPI_ACL_WRITE_ADD | ACLPB_SLAPI_ACL_WRITE_DEL) ) {
+ access |= SLAPI_ACL_WRITE;
+ }
+
+ n_edn = slapi_entry_get_ndn ( e );
+ e_sdn = slapi_entry_get_sdn ( e );
+
+ /* Check if this is a write operation and the database is readonly */
+ /* No one, even the rootdn should be allowed to write to the database */
+ /* jcm: ReadOnly only applies to the public backends, the private ones */
+ /* (the DSEs) should still be writable for configuration. */
+ if ( access & ( SLAPI_ACL_WRITE | SLAPI_ACL_ADD | SLAPI_ACL_DELETE )) {
+ int be_readonly, privateBackend;
+ Slapi_Backend *be;
+
+ slapi_pblock_get ( pb, SLAPI_BE_READONLY, &be_readonly );
+ slapi_pblock_get ( pb, SLAPI_BACKEND, &be );
+ privateBackend = slapi_be_private ( be );
+
+ if ( !privateBackend && (be_readonly || slapi_config_get_readonly () )){
+ slapi_log_error (loglevel, plugin_name,
+ "conn=%d op=%d (main): Deny %s on entry(%s)"
+ ": readonly backend\n",
+ op->o_connid, op->o_opid,
+ acl_access2str(access),
+ escape_string_with_punctuation(n_edn,ebuf));
+ return LDAP_UNWILLING_TO_PERFORM;
+ }
+ }
+
+ /* Check for things we need to skip */
+ TNF_PROBE_0_DEBUG(acl_skipaccess_start,"ACL","");
+ if ( acl_skip_access_check ( pb, e )) {
+ slapi_log_error (loglevel, plugin_name,
+ "conn=%d op=%d (main): Allow %s on entry(%s)"
+ ": root user\n",
+ op->o_connid, op->o_opid,
+ acl_access2str(access),
+ escape_string_with_punctuation(n_edn,ebuf));
+ return(LDAP_SUCCESS);
+ }
+ TNF_PROBE_0_DEBUG(acl_skipaccess_end,"ACL","");
+
+
+ /* Get the bindDN */
+ slapi_pblock_get ( pb, SLAPI_REQUESTOR_DN, &clientDn );
+
+ /* get the right acl pblock to work with */
+ if ( access & SLAPI_ACL_PROXY )
+ aclpb = acl_get_aclpb ( pb, ACLPB_PROXYDN_PBLOCK );
+ else
+ aclpb = acl_get_aclpb ( pb, ACLPB_BINDDN_PBLOCK );
+
+ if ( !aclpb ) {
+ slapi_log_error ( SLAPI_LOG_FATAL, plugin_name, "Missing aclpb 1 \n" );
+ ret_val = LDAP_OPERATIONS_ERROR;
+ goto cleanup_and_ret;
+ }
+
+ /* check if aclpb is initialized or not */
+ TNF_PROBE_0_DEBUG(acl_aclpbinit_start,"ACL","");
+ acl_init_aclpb ( pb, aclpb, clientDn, 0 );
+ TNF_PROBE_0_DEBUG(acl_aclpbinit_end,"ACL","");
+
+
+ /* Here we mean if "I am trying to add/delete "myself" ? " */
+ if (val && (access & SLAPI_ACL_WRITE) && (val->bv_len > 0) ) {
+ /* should use slapi_sdn_compare() but that'a an extra malloc/free */
+
+ char *dn_val_to_write =
+ slapi_dn_normalize(slapi_ch_strdup(val->bv_val));
+
+ if ( aclpb->aclpb_authorization_sdn &&
+ slapi_utf8casecmp((ACLUCHP)dn_val_to_write, (ACLUCHP)
+ slapi_sdn_get_ndn(aclpb->aclpb_authorization_sdn)) == 0) {
+ access |= SLAPI_ACL_SELF;
+ }
+
+ slapi_ch_free( (void **)&dn_val_to_write);
+ }
+
+ /* Convert access to string of rights eg SLAPI_ACL_ADD->"add". */
+ if ((right= acl_access2str(access)) == NULL) {
+ /* ERROR: unknown rights */
+ slapi_log_error(SLAPI_LOG_ACL, plugin_name,
+ "acl_access_allowed unknown rights:%d\n", access);
+
+ ret_val = LDAP_OPERATIONS_ERROR;
+ goto cleanup_and_ret;
+ }
+
+
+ /*
+ * Am I a anonymous dude ? then we can use our anonymous profile
+ * We don't require the aclpb to have been initialized for anom stuff
+ *
+ */
+ TNF_PROBE_0_DEBUG(acl_anon_test_start,"ACL","");
+ if ( (access & (SLAPI_ACL_SEARCH | SLAPI_ACL_READ )) &&
+ (clientDn && *clientDn == '\0')) {
+ aclanom_get_suffix_info(e, aclpb);
+ ret_val = aclanom_match_profile ( pb, aclpb, e, attr, access );
+ if (ret_val != -1 ) {
+ if (ret_val == LDAP_SUCCESS ) {
+ decision_reason.reason = ACL_REASON_ANON_ALLOWED;
+ } else if (ret_val == LDAP_INSUFFICIENT_ACCESS) {
+ decision_reason.reason = ACL_REASON_ANON_DENIED;
+ }
+ goto cleanup_and_ret;
+ }
+ }
+ TNF_PROBE_0_DEBUG(acl_anon_test_end,"ACL","");
+
+ /* copy the value into the aclpb for later checking by the value acl code */
+
+ aclpb->aclpb_curr_attrVal = val;
+
+ if (!(aclpb->aclpb_state & ACLPB_SEARCH_BASED_ON_LIST) &&
+ (access & SLAPI_ACL_SEARCH)) {
+ /* We are evaluating SEARCH right for the entry. After that
+ ** we will eval the READ right. We need to refresh the
+ ** list of acls selected for evaluation for the entry.
+ ** Initialize the array so that we indicate nothing has been
+ ** selected.
+ */
+ aclpb->aclpb_handles_index[0] = -1;
+ /* access is not allowed on entry for search -- it's for
+ ** read only.
+ */
+ aclpb->aclpb_state &= ~ACLPB_ACCESS_ALLOWED_ON_ENTRY;
+ }
+
+ /* set that this is a new entry */
+ aclpb->aclpb_res_type |= ACLPB_NEW_ENTRY;
+ aclpb->aclpb_access = 0;
+ aclpb->aclpb_access |= access;
+
+ /*
+ * stub the Slapi_Entry info first time and only it has changed
+ * or if the pblock is a psearch pblock--in this case the lifetime
+ * of entries associated with psearches is such that we cannot cache
+ * pointers to them--we must always start afresh (see psearch.c).
+ */
+ slapi_pblock_get( pb, SLAPI_OPERATION, &op);
+ if ( operation_is_flag_set(op, OP_FLAG_PS) ||
+ (aclpb->aclpb_curr_entry_sdn == NULL) ||
+ (slapi_sdn_compare ( aclpb->aclpb_curr_entry_sdn, e_sdn) != 0)) {
+
+ TNF_PROBE_0_DEBUG(acl_entry_first_touch_start,"ACL","");
+
+ slapi_log_error(loglevel, plugin_name,
+ "#### conn=%d op=%d binddn=\"%s\"\n",
+ op->o_connid, op->o_opid, clientDn);
+ aclpb->aclpb_stat_total_entries++;
+
+ if (!(access & SLAPI_ACL_PROXY) &&
+ !( aclpb->aclpb_state & ACLPB_DONOT_EVALUATE_PROXY )) {
+ Acl_PBlock *proxy_pb;
+
+ proxy_pb = acl_get_aclpb( pb, ACLPB_PROXYDN_PBLOCK );
+ if (proxy_pb) {
+ TNF_PROBE_0_DEBUG(acl_access_allowed_proxy_start,"ACL","");
+ ret_val = acl_access_allowed( pb, e, attr, val, SLAPI_ACL_PROXY );
+ TNF_PROBE_0_DEBUG(acl_access_allowed_proxy_end,"ACL","");
+
+ if (ret_val != LDAP_SUCCESS) goto cleanup_and_ret;
+ }
+ }
+ if ( access & SLAPI_ACL_SEARCH) {
+ aclpb->aclpb_num_entries++;
+
+ if ( aclpb->aclpb_num_entries == 1) {
+ aclpb->aclpb_state |= ACLPB_COPY_EVALCONTEXT;
+ } else if ( aclpb->aclpb_state & ACLPB_COPY_EVALCONTEXT ) {
+ /* We need to copy the evalContext */
+ acl_copyEval_context ( aclpb, &aclpb->aclpb_curr_entryEval_context,
+ &aclpb->aclpb_prev_entryEval_context, 0 );
+ aclpb->aclpb_state &= ~ACLPB_COPY_EVALCONTEXT;
+ }
+ acl_clean_aclEval_context ( &aclpb->aclpb_curr_entryEval_context, 1 /*scrub */);
+ }
+
+ /* reset the cached result based on the scope */
+ acl__reset_cached_result (aclpb );
+
+ /* Find all the candidate aci's that apply by scanning up the DIT tree from edn. */
+
+ TNF_PROBE_0_DEBUG(acl_aciscan_start,"ACL","");
+ slapi_sdn_done ( aclpb->aclpb_curr_entry_sdn );
+ slapi_sdn_set_dn_byval ( aclpb->aclpb_curr_entry_sdn, n_edn );
+ acllist_aciscan_update_scan ( aclpb, n_edn );
+ TNF_PROBE_0_DEBUG(acl_aciscan_end,"ACL","");
+
+ /* Keep the ptr to the current entry */
+ aclpb->aclpb_curr_entry = (Slapi_Entry *) e;
+
+ /* Get the attr info */
+ deallocate_attrEval = acl__get_attrEval ( aclpb, attr );
+
+ aclutil_print_resource ( aclpb, right, attr, clientDn );
+
+ /*
+ * Used to be PListInitProp(aclpb->aclpb_proplist, 0,
+ * DS_ATTR_ENTRY, e, 0);
+ *
+ * The difference is that PListInitProp() allocates a new property
+ * every time it's called, overwriting the old name in the PList hash
+ * table, but not freeing the original property.
+ * Now, we just create the property at aclpb_malloc() time and
+ * Assign a new value each time.
+ */
+
+ rv = PListAssignValue(aclpb->aclpb_proplist,
+ DS_ATTR_ENTRY, e, 0);
+
+ if (rv < 0) {
+ slapi_log_error(SLAPI_LOG_ACL, plugin_name,
+ "Unable to set the Slapi_Entry in the Plist\n",0,0,0);
+ ret_val = LDAP_OPERATIONS_ERROR;
+ goto cleanup_and_ret;
+ }
+
+ TNF_PROBE_0_DEBUG(acl_entry_first_touch_end,"ACL","");
+
+ } else {
+ /* we are processing the same entry but for a different
+ ** attribute. If access is already allowed on that, entry, then
+ ** it's not a new entry anymore. It's the same old one.
+ */
+
+ TNF_PROBE_0_DEBUG(acl_entry_subs_touch_start,"ACL","");
+
+ aclpb->aclpb_res_type &= ~ACLPB_NEW_ENTRY;
+
+ /* Get the attr info */
+ deallocate_attrEval = acl__get_attrEval ( aclpb, attr );
+
+ TNF_PROBE_0_DEBUG(acl_entry_subs_touch_end,"ACL","");
+
+ }
+
+ /* get a lock for the reader */
+ acllist_acicache_READ_LOCK();
+ got_reader_locked = 1;
+
+ /*
+ ** Check if we can use any cached information to determine
+ ** access to this resource
+ */
+ if ( (access & SLAPI_ACL_SEARCH) &&
+ (ret_val = acl__match_handlesFromCache ( aclpb , attr, access)) != -1) {
+ /* means got a result: allowed or not*/
+
+ if (ret_val == LDAP_SUCCESS ) {
+ decision_reason.reason = ACL_REASON_EVALCONTEXT_CACHED_ALLOW;
+ } else if (ret_val == LDAP_INSUFFICIENT_ACCESS) {
+ decision_reason.reason =
+ ACL_REASON_EVALCONTEXT_CACHED_NOT_ALLOWED;
+ }
+ goto cleanup_and_ret;
+ }
+
+ /*
+ ** Now we have all the information about the resource. Now we need to
+ ** figure out if there are any ACLs which can be applied.
+ ** If no ACLs are there, then it's a DENY as default.
+ */
+ if (!(num_handle = acl__scan_for_acis(aclpb, &err))) {
+
+ /* We might have accessed the ACL first time which could
+ ** have caused syntax error.
+ */
+ if ( err == ACL_ONEACL_TEXT_ERR)
+ ret_val = LDAP_INVALID_SYNTAX;
+ else {
+ ret_val = LDAP_INSUFFICIENT_ACCESS;
+ decision_reason.reason = ACL_REASON_NO_MATCHED_RESOURCE_ALLOWS;
+ }
+ goto cleanup_and_ret;
+ }
+
+ slapi_log_error( SLAPI_LOG_ACL, plugin_name,
+ "Processed attr:%s for entry:%s\n", attr ? attr : "NULL",
+ ACL_ESCAPE_STRING_WITH_PUNCTUATION ( n_edn, ebuf), 0);
+
+ /*
+ ** Now evaluate the rights.
+ ** This is what we have been waiting for.
+ ** The return value should be ACL_RES_DENY or ACL_RES_ALLOW.
+ */
+ rv = acl__TestRights(aclpb, access, &right, ds_map_generic,
+ &decision_reason);
+ if ( rv != ACL_RES_ALLOW && (0 == strcasecmp ( right, "selfwrite") ) ) {
+ /* If I am adding myself to a group, we don't need selfwrite always,
+ ** write priv is good enough. Since libaccess doesn't provide me a nice
+ ** way to evaluate OR rights, I have to try again with wite priv.
+ ** bug: 339051
+ */
+ right = access_str_write;
+ rv = acl__TestRights(aclpb, access, &right, ds_map_generic,
+ &decision_reason);
+ }
+
+ if (rv == ACL_RES_ALLOW) {
+ ret_val = LDAP_SUCCESS;
+ } else {
+ ret_val = LDAP_INSUFFICIENT_ACCESS;
+ }
+
+cleanup_and_ret:
+
+ TNF_PROBE_0_DEBUG(acl_cleanup_start,"ACL","");
+
+ /* I am ready to get out. */
+ if ( got_reader_locked ) acllist_acicache_READ_UNLOCK();
+
+ /* Store the status of the evaluation for this attr */
+ if ( aclpb && (c_attrEval = aclpb->aclpb_curr_attrEval )) {
+ if ( deallocate_attrEval ) {
+ /* In this case we are not caching the result as
+ ** we have too many attrs. we have malloced space.
+ ** Get rid of it.
+ */
+ slapi_ch_free ( (void **) &c_attrEval->attrEval_name );
+ slapi_ch_free ( (void **) &c_attrEval );
+ } else if (ret_val == LDAP_SUCCESS ) {
+ if ( access & SLAPI_ACL_SEARCH )
+ c_attrEval->attrEval_s_status |= ACL_ATTREVAL_SUCCESS;
+ else if ( access & SLAPI_ACL_READ )
+ c_attrEval->attrEval_r_status |= ACL_ATTREVAL_SUCCESS;
+ else
+ c_attrEval->attrEval_r_status |= ACL_ATTREVAL_INVALID;
+ } else {
+ if ( access & SLAPI_ACL_SEARCH )
+ c_attrEval->attrEval_s_status |= ACL_ATTREVAL_FAIL;
+ else if ( access & SLAPI_ACL_READ )
+ c_attrEval->attrEval_r_status |= ACL_ATTREVAL_FAIL;
+ else
+ c_attrEval->attrEval_r_status |= ACL_ATTREVAL_INVALID;
+ }
+ }
+
+ if ( aclpb ) aclpb->aclpb_curr_attrEval = NULL;
+
+ print_access_control_summary( "main", ret_val, clientDn, aclpb, right,
+ (attr ? attr : "NULL"),
+ escape_string_with_punctuation (n_edn, ebuf),
+ &decision_reason);
+ TNF_PROBE_0_DEBUG(acl_cleanup_end,"ACL","");
+
+ TNF_PROBE_0_DEBUG(acl_access_allowed_end,"ACL","");
+
+ return(ret_val);
+
+}
+
+static void print_access_control_summary( char *source, int ret_val, char *clientDn,
+ struct acl_pblock *aclpb,
+ char *right,
+ char *attr,
+ const char *edn,
+ aclResultReason_t *acl_reason)
+{
+ struct codebook {
+ int code;
+ char *text;
+ };
+
+ static struct codebook reasonbook[] = {
+ {ACL_REASON_NO_ALLOWS, "no allow acis"},
+ {ACL_REASON_RESULT_CACHED_DENY, "cached deny"},
+ {ACL_REASON_RESULT_CACHED_ALLOW, "cached allow"},
+ {ACL_REASON_EVALUATED_ALLOW, "allowed"},
+ {ACL_REASON_EVALUATED_DENY, "denied"},
+ {ACL_REASON_NO_MATCHED_RESOURCE_ALLOWS, "no aci matched the resource"},
+ {ACL_REASON_NO_MATCHED_SUBJECT_ALLOWS, "no aci matched the subject"},
+ {ACL_REASON_ANON_ALLOWED, "allow anyone aci matched anon user"},
+ {ACL_REASON_ANON_DENIED, "no matching anyone aci for anon user"},
+ {ACL_REASON_EVALCONTEXT_CACHED_ALLOW, "cached context/parent allow"},
+ {ACL_REASON_EVALCONTEXT_CACHED_NOT_ALLOWED, "cached context/parent deny"},
+ {ACL_REASON_EVALCONTEXT_CACHED_ATTR_STAR_ALLOW, "cached context/parent allow any attr"},
+ {ACL_REASON_NONE, "error occurred"},
+ };
+
+ char *anon = "anonymous";
+ char *null_user = "NULL"; /* bizare case */
+ char *real_user = NULL;
+ char *proxy_user = NULL;
+ char *access_allowed_string = "Allow";
+ char *access_not_allowed_string = "Deny";
+ char *access_error_string = "access_error";
+ char *access_status = NULL;
+ char *access_reason_none = "no reason available";
+ char *access_reason = access_reason_none;
+ char acl_info[ BUFSIZ ];
+ Slapi_Operation *op = NULL;
+ int loglevel;
+ int i;
+
+ loglevel = slapi_is_loglevel_set(SLAPI_LOG_ACL) ? SLAPI_LOG_ACL : SLAPI_LOG_ACLSUMMARY;
+
+ if ( !slapi_is_loglevel_set(loglevel) ) {
+ return;
+ }
+
+ slapi_pblock_get(aclpb->aclpb_pblock, SLAPI_OPERATION, &op); /* for logging */
+
+ if (ret_val == LDAP_INSUFFICIENT_ACCESS) {
+ access_status = access_not_allowed_string;
+ } else if ( ret_val == LDAP_SUCCESS) {
+ access_status = access_allowed_string;
+ } else { /* some kind of error */
+ access_status = access_error_string;
+ }
+
+ /* decode the reason */
+ for (i = 0; i < sizeof(reasonbook) / sizeof(struct codebook); i++) {
+ if ( acl_reason->reason == reasonbook[i].code ) {
+ access_reason = reasonbook[i].text;
+ break;
+ }
+ }
+
+ /* get the acl */
+ acl_info[0] = '\0';
+ if (acl_reason->deciding_aci) {
+ if (acl_reason->reason == ACL_REASON_RESULT_CACHED_DENY ||
+ acl_reason->reason == ACL_REASON_RESULT_CACHED_ALLOW) {
+ /* acl is in cache. Its detail must have been printed before.
+ * So no need to print out acl detail this time.
+ */
+ PR_snprintf( &acl_info[0], BUFSIZ, "%s by aci(%d)",
+ access_reason,
+ acl_reason->deciding_aci->aci_index);
+ }
+ else {
+ PR_snprintf( &acl_info[0], BUFSIZ, "%s by aci(%d): aciname=%s, acidn=\"%s\"",
+ access_reason,
+ acl_reason->deciding_aci->aci_index,
+ acl_reason->deciding_aci->aclName,
+ slapi_sdn_get_ndn (acl_reason->deciding_aci->aci_sdn) );
+ }
+ }
+
+ /* Say who was denied access */
+
+ if (clientDn) {
+ if (clientDn[0] == '\0') {
+ /* anon */
+ real_user = anon;
+ } else {
+ real_user = clientDn;
+ }
+ } else {
+ real_user = null_user;
+ }
+
+ /* Is there a proxy */
+
+ if ( aclpb != NULL && aclpb->aclpb_proxy != NULL) {
+
+ if ( aclpb->aclpb_authorization_sdn != NULL ) {
+
+ proxy_user = (char *)(aclpb->aclpb_authorization_sdn->ndn ?
+ aclpb->aclpb_authorization_sdn->ndn:
+ null_user);
+
+ slapi_log_error(loglevel, plugin_name,
+ "conn=%d op=%d (%s): %s %s on entry(%s).attr(%s) to proxy (%s)"
+ ": %s\n",
+ op->o_connid, op->o_opid,
+ source,
+ access_status,
+ right,
+ edn,
+ attr ? attr: "NULL",
+ proxy_user,
+ acl_info[0] ? acl_info : access_reason);
+ } else {
+ proxy_user = null_user;
+ slapi_log_error(loglevel, plugin_name,
+ "conn=%d op=%d (%s): %s %s on entry(%s).attr(%s) to proxy (%s)"
+ ": %s\n",
+ op->o_connid, op->o_opid,
+ source,
+ access_status,
+ right,
+ edn,
+ attr ? attr: "NULL",
+ proxy_user,
+ acl_info[0] ? acl_info : access_reason);
+ }
+ } else{
+ slapi_log_error(loglevel, plugin_name,
+ "conn=%d op=%d (%s): %s %s on entry(%s).attr(%s)"
+ ": %s\n",
+ op->o_connid, op->o_opid,
+ source,
+ access_status,
+ right,
+ edn,
+ attr ? attr: "NULL",
+ acl_info[0] ? acl_info : access_reason);
+ }
+
+
+}
+/***************************************************************************
+*
+* acl_read_access_allowed_on_entry
+* check read access control on the given entry.
+*
+* Only used during seearch to test for read access on the entry.
+* (Could be generalized).
+*
+* attrs is the list of requested attributes passed with the search.
+* If the entry has no attributes (weird case) then the routine survives.
+*
+* Input:
+*
+*
+* Returns:
+* LDAP_SUCCESS - access allowed
+* LDAP_INSUFFICIENT_ACCESS - access denied
+*
+* Error Handling:
+* None.
+*
+**************************************************************************/
+int
+acl_read_access_allowed_on_entry (
+ Slapi_PBlock *pb,
+ Slapi_Entry *e, /* The Slapi_Entry */
+ char **attrs,
+ int access /* access rights */
+ )
+{
+
+ struct acl_pblock *aclpb;
+ Slapi_Attr *currAttr;
+ Slapi_Attr *nextAttr;
+ int len;
+ int attr_index = -1;
+ char *attr_type = NULL;
+ int rv, isRoot;
+ char *clientDn;
+ unsigned long flags;
+ aclResultReason_t decision_reason;
+ int loglevel;
+
+ loglevel = slapi_is_loglevel_set(SLAPI_LOG_ACL) ? SLAPI_LOG_ACL : SLAPI_LOG_ACLSUMMARY;
+
+ TNF_PROBE_0_DEBUG(acl_read_access_allowed_on_entry_start ,"ACL","");
+
+ decision_reason.deciding_aci = NULL;
+ decision_reason.reason = ACL_REASON_NONE;
+
+ slapi_pblock_get ( pb, SLAPI_REQUESTOR_ISROOT, &isRoot );
+
+ /*
+ ** If it's the root, or acl is off or the entry is a rootdse,
+ ** Then you have the privilege to read it.
+ */
+ if ( acl_skip_access_check ( pb, e ) ) {
+ char *n_edn = slapi_entry_get_ndn ( e );
+ char ebuf [ BUFSIZ ];
+ slapi_log_error (SLAPI_LOG_ACL, plugin_name,
+ "Root access (%s) allowed on entry(%s)\n",
+ acl_access2str(access),
+ ACL_ESCAPE_STRING_WITH_PUNCTUATION (n_edn, ebuf));
+ TNF_PROBE_1_DEBUG(acl_read_access_allowed_on_entry_end ,"ACL","",
+ tnf_string,skip_access,"");
+ return LDAP_SUCCESS;
+ }
+
+ aclpb = acl_get_aclpb ( pb, ACLPB_BINDDN_PBLOCK );
+ if ( !aclpb ) {
+ slapi_log_error ( SLAPI_LOG_FATAL, plugin_name, "Missing aclpb 2 \n" );
+ TNF_PROBE_1_DEBUG(acl_read_access_allowed_on_entry_end ,"ACL","",
+ tnf_string,end,"aclpb error");
+ return LDAP_OPERATIONS_ERROR;
+ }
+
+ /*
+ * Am I a anonymous dude ? then we can use our anonympous profile
+ * We don't require the aclpb to have been initialized for anom stuff
+ *
+ */
+ slapi_pblock_get ( pb, SLAPI_REQUESTOR_DN, &clientDn );
+ if ( clientDn && *clientDn == '\0' ) {
+ int ret_val;
+ ret_val = aclanom_match_profile ( pb, aclpb, e, NULL, SLAPI_ACL_READ );
+ TNF_PROBE_1_DEBUG(acl_read_access_allowed_on_entry_end ,"ACL","",
+ tnf_string,end,"anon");
+
+ if (ret_val != -1 ) return ret_val;
+ }
+
+ aclpb->aclpb_state &= ~ACLPB_RESET_MASK;
+ if (aclpb->aclpb_state & ACLPB_MATCHES_ALL_ACLS ) {
+ int ret_val;
+ ret_val = acl__attr_cached_result (aclpb, NULL, SLAPI_ACL_READ);
+ if (ret_val != -1 ) {
+ /* print summary if loglevel set */
+ if ( slapi_is_loglevel_set(loglevel) ) {
+ char *n_edn;
+ n_edn = slapi_entry_get_ndn ( e );
+ if ( ret_val == LDAP_SUCCESS) {
+ decision_reason.reason =
+ ACL_REASON_EVALCONTEXT_CACHED_ALLOW;
+ } else {
+ decision_reason.reason =
+ ACL_REASON_EVALCONTEXT_CACHED_NOT_ALLOWED;
+ }
+ /*
+ * pass NULL as the attr as this routine is concerned with
+ * access at the entry level.
+ */
+ print_access_control_summary( "on entry",
+ ret_val, clientDn, aclpb,
+ acl_access2str(SLAPI_ACL_READ),
+ NULL, n_edn,
+ &decision_reason);
+ }
+ TNF_PROBE_1_DEBUG(acl_read_access_allowed_on_entry_end ,"ACL","",
+ tnf_string,eval_context_cached,"");
+
+ return ret_val;
+ }
+ }
+
+ /*
+ * Currently do not use this code--it results in confusing
+ * behaviour..see 529905
+ */
+#ifdef DETERMINE_ACCESS_BASED_ON_REQUESTED_ATTRIBUTES
+
+ /* Do we have access to the entry by virtue of
+ ** having access to an attr. Before that, let's find out which attrs
+ ** the user want. If the user has specified certain attributes, then
+ ** we check aginst that set of attributes.
+ */
+ if (!((aclpb->aclpb_state & ACLPB_USER_WANTS_ALL_ATTRS) ||
+ (aclpb->aclpb_state & ACLPB_USER_SPECIFIED_ATTARS))) {
+ int i;
+ if (attrs == NULL) {
+ aclpb->aclpb_state |= ACLPB_USER_WANTS_ALL_ATTRS;
+ } else {
+ for ( i = 0; attrs != NULL && attrs[i] != NULL; i++ ) {
+ if ( strcmp( LDAP_ALL_USER_ATTRS, attrs[i] ) == 0 ) {
+ aclpb->aclpb_state |= ACLPB_USER_WANTS_ALL_ATTRS;
+ break;
+ }
+ }
+ }
+
+ if (!(aclpb->aclpb_state & ACLPB_USER_WANTS_ALL_ATTRS)) {
+ for (i = 0; attrs != NULL && attrs[i] != NULL; i++ ) {
+ if ( !slapi_entry_attr_find ( e, attrs[i], &currAttr ) ) {
+ aclpb->aclpb_state |= ACLPB_USER_SPECIFIED_ATTARS;
+ break;
+ }
+ }
+ }
+ } /* end of all user test*/
+
+
+ /*
+ ** If user has specified a list of attrs, might as well use it
+ ** to determine access control.
+ */
+ currAttr = NULL;
+ attr_index = -1;
+ if ( aclpb->aclpb_state & ACLPB_USER_SPECIFIED_ATTARS) {
+ attr_index = 0;
+ attr_type = attrs[attr_index++];
+ } else {
+ /* Skip the operational attributes -- if there are any in the front */
+ slapi_entry_first_attr ( e, &currAttr );
+ if (currAttr != NULL) {
+ slapi_attr_get_flags ( currAttr, &flags );
+ while ( flags & SLAPI_ATTR_FLAG_OPATTR ) {
+ flags = 0;
+ rv = slapi_entry_next_attr ( e, currAttr, &nextAttr );
+ if ( !rv ) slapi_attr_get_flags ( nextAttr, &flags );
+ currAttr = nextAttr;
+ }
+
+ /* Get the attr type */
+ if ( currAttr ) slapi_attr_get_type ( currAttr , &attr_type );
+ }
+ }
+
+#endif /*DETERMINE_ACCESS_BASED_ON_REQUESTED_ATTRIBUTES*/
+
+#ifndef DETERMINE_ACCESS_BASED_ON_REQUESTED_ATTRIBUTES
+
+ /*
+ * Here look at each attribute in the entry and see if
+ * we have read access to it--if we do
+ * and we are not denied access to the entry then this
+ * is taken as implying access to the entry.
+ */
+ slapi_entry_first_attr ( e, &currAttr );
+ if (currAttr != NULL) {
+ slapi_attr_get_type ( currAttr , &attr_type );
+ }
+#endif
+ aclpb->aclpb_state |= ACLPB_EVALUATING_FIRST_ATTR;
+
+ while (attr_type) {
+ if (acl_access_allowed (pb, e,attr_type, NULL,
+ SLAPI_ACL_READ) == LDAP_SUCCESS) {
+ /*
+ ** We found a rule which requires us to test access
+ ** to the entry.
+ */
+ if ( aclpb->aclpb_state & ACLPB_FOUND_A_ENTRY_TEST_RULE){
+ /* Do I have access on the entry itself */
+ if (acl_access_allowed (pb, e, NULL,
+ NULL, access) != LDAP_SUCCESS) {
+ /* How was I denied ?
+ ** I could be denied on a DENY rule or because
+ ** there is no allow rule. If it's a DENY from
+ ** a DENY rule, then we don't have access to
+ ** the entry ( nice trick to get in )
+ */
+ if ( aclpb->aclpb_state &
+ ACLPB_EXECUTING_DENY_HANDLES)
+ return LDAP_INSUFFICIENT_ACCESS;
+
+ /* The other case is I don't have an
+ ** explicit allow rule -- which is fine.
+ ** Since, I am already here, it means that I have
+ ** an implicit allow to the entry.
+ */
+ }
+ }
+ aclpb->aclpb_state &= ~ACLPB_EVALUATING_FIRST_ATTR;
+
+ /*
+ ** If we are not sending all the attrs, then we must
+ ** make sure that we have right on a attr that we are
+ ** sending
+ */
+ len = strlen(attr_type);
+ if ( len > ACLPB_MAX_ATTR_LEN) {
+ slapi_ch_free ( (void **) &aclpb->aclpb_Evalattr);
+ aclpb->aclpb_Evalattr = slapi_ch_malloc(len);
+ }
+ strncpy (aclpb->aclpb_Evalattr, attr_type, len);
+ aclpb->aclpb_Evalattr[len] = '\0';
+ if ( attr_index >= 0 ) {
+ /*
+ * access was granted to one of the user specified attributes
+ * which was found in the entry and that attribute is
+ * now in aclpb_Evalattr
+ */
+ aclpb->aclpb_state |=
+ ACLPB_ACCESS_ALLOWED_USERATTR;
+ } else {
+ /*
+ * Access was granted to _an_ attribute in the entry and that
+ * attribute is now in aclpb_Evalattr
+ */
+ aclpb->aclpb_state |=
+ ACLPB_ACCESS_ALLOWED_ON_A_ATTR;
+ }
+ TNF_PROBE_1_DEBUG(acl_read_access_allowed_on_entry_end , "ACL","",
+ tnf_string,called_access_allowed,"");
+
+ return LDAP_SUCCESS;
+ } else {
+ /* try the next one */
+ attr_type = NULL;
+ if (attr_index >= 0) {
+ attr_type = attrs[attr_index++];
+ } else {
+ rv = slapi_entry_next_attr ( e, currAttr, &nextAttr );
+ if ( rv != 0 ) break;
+ currAttr = nextAttr;
+ slapi_attr_get_flags ( currAttr, &flags );
+ while ( flags & SLAPI_ATTR_FLAG_OPATTR ) {
+ flags = 0;
+ rv = slapi_entry_next_attr ( e, currAttr, &nextAttr );
+ if ( !rv ) slapi_attr_get_flags ( nextAttr, &flags );
+ currAttr = nextAttr;
+ }
+ /* Get the attr type */
+ if ( currAttr ) slapi_attr_get_type ( currAttr , &attr_type );
+ }
+ }
+ }
+
+ /*
+ ** That means. we have searched thru all the attrs and found
+ ** access is denied on all attrs.
+ **
+ ** If there were no attributes in the entry at all (can have
+ ** such entries thrown up by the b/e, then we do
+ ** not have such an implied access.
+ */
+ aclpb->aclpb_state |= ACLPB_ACCESS_DENIED_ON_ALL_ATTRS;
+ aclpb->aclpb_state &= ~ACLPB_EVALUATING_FIRST_ATTR;
+ TNF_PROBE_0_DEBUG(acl_read_access_allowed_on_entry_end ,"ACL","");
+
+ return LDAP_INSUFFICIENT_ACCESS;
+}
+
+/***************************************************************************
+*
+* acl_read_access_allowed_on_attr
+* check access control on the given attr.
+*
+* Only used during search to test for read access to an attr.
+* (Could be generalized)
+*
+* Input:
+*
+*
+* Returns:
+* LDAP_SUCCESS - access allowed
+* LDAP_INSUFFICIENT_ACCESS - access denied
+*
+* Error Handling:
+* None.
+*
+**************************************************************************/
+int
+acl_read_access_allowed_on_attr (
+ Slapi_PBlock *pb,
+ Slapi_Entry *e, /* The Slapi_Entry */
+ char *attr, /* Attribute of the entry */
+ struct berval *val, /* value of attr. NOT USED */
+ int access /* access rights */
+ )
+{
+
+ struct acl_pblock *aclpb = NULL;
+ char ebuf [ BUFSIZ ];
+ char *clientDn = NULL;
+ char *n_edn;
+ aclResultReason_t decision_reason;
+ int ret_val = -1;
+ int loglevel;
+
+ loglevel = slapi_is_loglevel_set(SLAPI_LOG_ACL) ? SLAPI_LOG_ACL : SLAPI_LOG_ACLSUMMARY;
+
+ TNF_PROBE_0_DEBUG(acl_read_access_allowed_on_attr_start ,"ACL","");
+
+ decision_reason.deciding_aci = NULL;
+ decision_reason.reason = ACL_REASON_NONE;
+
+ /* I am here, because I have access to the entry */
+
+ n_edn = slapi_entry_get_ndn ( e );
+
+ /* If it's the root or acl is off or rootdse, he has all the priv */
+ if ( acl_skip_access_check ( pb, e ) ) {
+ char ebuf [ BUFSIZ ];
+ slapi_log_error (SLAPI_LOG_ACL, plugin_name,
+ "Root access (%s) allowed on entry(%s)\n",
+ acl_access2str(access),
+ ACL_ESCAPE_STRING_WITH_PUNCTUATION (n_edn, ebuf), 0);
+ TNF_PROBE_1_DEBUG(acl_read_access_allowed_on_attr_end ,"ACL","",
+ tnf_string,skip_aclcheck,"");
+
+ return LDAP_SUCCESS;
+ }
+
+ aclpb = acl_get_aclpb ( pb, ACLPB_BINDDN_PBLOCK );
+ if ( !aclpb ) {
+ slapi_log_error ( SLAPI_LOG_FATAL, plugin_name, "Missing aclpb 3 \n" );
+ TNF_PROBE_1_DEBUG(acl_read_access_allowed_on_attr_end ,"ACL","",
+ tnf_string,aclpb_error,"");
+
+ return LDAP_OPERATIONS_ERROR;
+ }
+
+ /*
+ * Am I a anonymous dude ? then we can use our anonympous profile
+ * We don't require the aclpb to have been initialized for anom stuff
+ *
+ */
+ slapi_pblock_get (pb, SLAPI_REQUESTOR_DN ,&clientDn );
+ if ( clientDn && *clientDn == '\0' ) {
+ ret_val = aclanom_match_profile ( pb, aclpb, e, attr,
+ SLAPI_ACL_READ );
+ TNF_PROBE_1_DEBUG(acl_read_access_allowed_on_attr_end ,"ACL","",
+ tnf_string,anon_decision,"");
+ if (ret_val != -1 ) return ret_val;
+ }
+
+ /* Then I must have a access to the entry. */
+ aclpb->aclpb_state |= ACLPB_ACCESS_ALLOWED_ON_ENTRY;
+
+ if ( aclpb->aclpb_state & ACLPB_MATCHES_ALL_ACLS ) {
+
+ ret_val = acl__attr_cached_result (aclpb, attr, SLAPI_ACL_READ);
+ if (ret_val != -1 ) {
+ slapi_log_error(SLAPI_LOG_ACL, plugin_name,
+ "MATCHED HANDLE:dn:%s attr: %s val:%d\n",
+ ACL_ESCAPE_STRING_WITH_PUNCTUATION (n_edn, ebuf), attr,
+ ret_val );
+ if ( ret_val == LDAP_SUCCESS) {
+ decision_reason.reason =
+ ACL_REASON_EVALCONTEXT_CACHED_ALLOW;
+ } else {
+ decision_reason.reason =
+ ACL_REASON_EVALCONTEXT_CACHED_NOT_ALLOWED;
+ }
+ goto acl_access_allowed_on_attr_Exit;
+ } else {
+ aclpb->aclpb_state |= ACLPB_COPY_EVALCONTEXT;
+ }
+ }
+
+ if (aclpb->aclpb_state & ACLPB_ACCESS_DENIED_ON_ALL_ATTRS) {
+ /* access is denied on all the attributes */
+ TNF_PROBE_1_DEBUG(acl_read_access_allowed_on_attr_end ,"ACL","",
+ tnf_string,deny_all_attrs,"");
+
+ return LDAP_INSUFFICIENT_ACCESS;
+ }
+
+ /* do I have access to all the entries by virtue of having aci
+ ** rules with targetattr ="*". If yes, then allow access to
+ ** rest of the attributes.
+ */
+ if (aclpb->aclpb_state & ACLPB_ATTR_STAR_MATCHED) {
+ slapi_log_error(SLAPI_LOG_ACL, plugin_name,
+ "STAR Access allowed on attr:%s; entry:%s \n",
+ attr, ACL_ESCAPE_STRING_WITH_PUNCTUATION (n_edn, ebuf), 0);
+ decision_reason.reason =
+ ACL_REASON_EVALCONTEXT_CACHED_ATTR_STAR_ALLOW;
+ ret_val = LDAP_SUCCESS;
+ goto acl_access_allowed_on_attr_Exit;
+
+ }
+
+ if (aclpb->aclpb_state & ACLPB_ACCESS_ALLOWED_ON_A_ATTR) {
+
+ /* access is allowed on that attr.
+ ** for example: Slapi_Entry: cn, sn. phone, uid, passwd, address
+ ** We found that access is allowed on phone. That means the
+ ** -- access is denied on cn, sn
+ ** -- access is allowed on phone
+ ** -- Don't know about the rest. Need to evaluate.
+ */
+
+ if ( slapi_attr_type_cmp (attr, aclpb->aclpb_Evalattr, 1) == 0) {
+ /* from now on we need to evaluate access on
+ ** rest of the attrs.
+ */
+ aclpb->aclpb_state &= ~ACLPB_ACCESS_ALLOWED_ON_A_ATTR;
+ TNF_PROBE_1_DEBUG(acl_read_access_allowed_on_attr_end ,"ACL","",
+ tnf_string,aclp_Evalattr1,"");
+
+ return LDAP_SUCCESS;
+ } else {
+ /*
+ * Here, the attr that implied access to the entry (aclpb_Evalattr),
+ * is not
+ * the one we currently want evaluated--so
+ * we need to evaluate access to attr--so fall through.
+ */
+ }
+
+ } else if (aclpb->aclpb_state & ACLPB_ACCESS_ALLOWED_USERATTR) {
+ /* Only skip evaluation on the user attr on which we have
+ ** evaluated before.
+ */
+ if ( slapi_attr_type_cmp (attr, aclpb->aclpb_Evalattr, 1) == 0) {
+ aclpb->aclpb_state &= ~ACLPB_ACCESS_ALLOWED_USERATTR;
+ TNF_PROBE_1_DEBUG(acl_read_access_allowed_on_attr_end ,"ACL","",
+ tnf_string,aclp_Evalattr2,"");
+ return LDAP_SUCCESS;
+ }
+ }
+
+ /* we need to evaluate the access on this attr */
+ return ( acl_access_allowed(pb, e, attr, val, access) );
+
+ /* This exit point prints a summary and returns ret_val */
+acl_access_allowed_on_attr_Exit:
+
+ /* print summary if loglevel set */
+ if ( slapi_is_loglevel_set(loglevel) ) {
+
+ print_access_control_summary( "on attr",
+ ret_val, clientDn, aclpb,
+ acl_access2str(SLAPI_ACL_READ),
+ attr, n_edn, &decision_reason);
+ }
+ TNF_PROBE_0_DEBUG(acl_read_access_allowed_on_attr_end ,"ACL","");
+
+ return(ret_val);
+}
+/***************************************************************************
+*
+* acl_check_mods
+* check access control on the given entry to see if
+* it allows the given modifications by the user associated with op.
+*
+*
+* Input:
+*
+*
+* Returns:
+* LDAP_SUCCESS - mods allowed ok
+* <err> - same return value as acl_access_allowed()
+*
+* Error Handling:
+* None.
+*
+**************************************************************************/
+int
+acl_check_mods(
+ Slapi_PBlock *pb,
+ Slapi_Entry *e,
+ LDAPMod **mods,
+ char **errbuf
+)
+{
+ int i;
+ int rv, accessCheckDisabled;
+ int lastmod = 0;
+ Slapi_Attr *attr = NULL;
+ char *n_edn;
+ Slapi_Backend *be = NULL;
+ Slapi_DN *e_sdn;
+ Acl_PBlock *aclpb = acl_get_aclpb ( pb, ACLPB_PROXYDN_PBLOCK );
+ LDAPMod *mod;
+ Slapi_Mods smods;
+
+ rv = slapi_pblock_get ( pb, SLAPI_PLUGIN_DB_NO_ACL, &accessCheckDisabled );
+ if ( rv != -1 && accessCheckDisabled ) return LDAP_SUCCESS;
+
+ if ( NULL == aclpb )
+ aclpb = acl_get_aclpb ( pb, ACLPB_BINDDN_PBLOCK );
+
+ n_edn = slapi_entry_get_ndn ( e );
+ e_sdn = slapi_entry_get_sdn ( e );
+
+ slapi_mods_init_byref(&smods,mods);
+
+ for (mod = slapi_mods_get_first_mod(&smods);
+ mod != NULL;
+ mod = slapi_mods_get_next_mod(&smods)) {
+ switch (mod->mod_op & ~LDAP_MOD_BVALUES ) {
+
+ case LDAP_MOD_DELETE:
+ if (mod->mod_bvalues != NULL ) {
+ break;
+ }
+
+ /*
+ * Here, check that we have the right to delete all
+ * the values of the attribute in the entry.
+ */
+
+ case LDAP_MOD_REPLACE:
+ if ( !lastmod ) {
+ if (be == NULL) {
+ if (slapi_pblock_get( pb, SLAPI_BACKEND, &be )) {
+ be = NULL;
+ }
+ }
+ if (be != NULL)
+ slapi_pblock_get ( pb, SLAPI_BE_LASTMOD, &lastmod );
+ }
+ if (lastmod &&
+ (strcmp (mod->mod_type, "modifiersname")== 0 ||
+ strcmp (mod->mod_type, "modifytimestamp")== 0)) {
+ continue;
+ }
+
+ slapi_entry_attr_find (e, mod->mod_type, &attr);
+ if ( attr != NULL) {
+ Slapi_Value *sval=NULL;
+ const struct berval *attrVal=NULL;
+ int k= slapi_attr_first_value(attr,&sval);
+ while(k != -1) {
+ attrVal = slapi_value_get_berval(sval);
+ rv = slapi_access_allowed (pb, e,
+ mod->mod_type,
+ (struct berval *)attrVal, /* XXXggood had to cast away const - BAD */
+ ACLPB_SLAPI_ACL_WRITE_DEL); /* was SLAPI_ACL_WRITE */
+ if ( rv != LDAP_SUCCESS) {
+ acl_gen_err_msg (
+ SLAPI_ACL_WRITE,
+ n_edn,
+ mod->mod_type,
+ errbuf);
+ /* Cleanup */
+ slapi_mods_done(&smods);
+ return(rv);
+ }
+ k= slapi_attr_next_value(attr, k, &sval);
+ }
+ }
+ else {
+ rv = slapi_access_allowed (pb, e,
+ mod->mod_type,
+ NULL,
+ ACLPB_SLAPI_ACL_WRITE_DEL); /* was SLAPI_ACL_WRITE */
+ if ( rv != LDAP_SUCCESS) {
+ acl_gen_err_msg (
+ SLAPI_ACL_WRITE,
+ n_edn,
+ mod->mod_type,
+ errbuf);
+ /* Cleanup */
+ slapi_mods_done(&smods);
+ return(rv);
+ }
+ }
+ break;
+
+ default:
+ break;
+ } /* switch */
+
+ /*
+ * Check that we have add/delete writes on the specific values
+ * we are trying to add.
+ */
+
+ if ( aclpb && aclpb->aclpb_curr_entry_sdn )
+ slapi_sdn_done ( aclpb->aclpb_curr_entry_sdn );
+
+ if ( mod->mod_bvalues != NULL ) {
+
+ /*
+ * Here, there are specific values specified.
+ * For add and replace--we need add rights for these values.
+ * For delete we need delete rights for these values.
+ */
+
+ for ( i = 0; mod->mod_bvalues[i] != NULL; i++ ) {
+
+ if ( ((mod->mod_op & ~LDAP_MOD_BVALUES) == LDAP_MOD_ADD) ||
+ ((mod->mod_op & ~LDAP_MOD_BVALUES) == LDAP_MOD_REPLACE)) {
+
+ rv = acl_access_allowed (pb,e,
+ mod->mod_type,
+ mod->mod_bvalues[i],
+ ACLPB_SLAPI_ACL_WRITE_ADD); /*was SLAPI_ACL_WRITE*/
+ } else if ((mod->mod_op & ~LDAP_MOD_BVALUES) == LDAP_MOD_DELETE) {
+ rv = acl_access_allowed (pb,e,
+ mod->mod_type,
+ mod->mod_bvalues[i],
+ ACLPB_SLAPI_ACL_WRITE_DEL); /*was SLAPI_ACL_WRITE*/
+ } else {
+ rv = LDAP_INSUFFICIENT_ACCESS;
+ }
+
+ if ( rv != LDAP_SUCCESS ) {
+ acl_gen_err_msg (
+ SLAPI_ACL_WRITE,
+ n_edn,
+ mod->mod_type,
+ errbuf);
+ /* Cleanup */
+ slapi_mods_done(&smods);
+ return rv;
+ }
+ /* Need to check for all the values because
+ ** we may be modifying a "self<right>" value.
+ */
+
+ /* Are we adding/replacing a aci attribute
+ ** value. In that case, we need to make
+ ** sure that the new value has thr right
+ ** syntax
+ */
+ if (strcmp(mod->mod_type,
+ aci_attr_type) == 0) {
+ if ( 0 != (rv = acl_verify_syntax( e_sdn,
+ mod->mod_bvalues[i]))) {
+ aclutil_print_err(rv, e_sdn,
+ mod->mod_bvalues[i],
+ errbuf);
+ /* Cleanup */
+ slapi_mods_done(&smods);
+ return LDAP_INVALID_SYNTAX;
+ }
+ }
+ } /* for */
+ }
+ } /* end of big for */
+ /* Cleanup */
+ slapi_mods_done(&smods);
+ return( LDAP_SUCCESS );
+}
+/***************************************************************************
+*
+* acl_modified
+* Modifies ( removed, add, changes) the ACI LIST.
+*
+* Input:
+* int *optype - op code
+* char *dn - DN of the entry
+* void *change - The change struct which contais the
+* - change value
+*
+* Returns:
+* None.
+*
+* Error Handling:
+* None.
+*
+**************************************************************************/
+extern void
+acl_modified (Slapi_PBlock *pb, int optype, char *n_dn, void *change)
+{
+ struct berval **bvalue;
+ char **value;
+ int rv=0; /* returned value */
+ char* new_RDN;
+ char* parent_DN;
+ char* new_DN;
+ LDAPMod **mods;
+ struct berval b;
+ int j;
+ Slapi_Attr *attr = NULL;
+ Slapi_Entry *e = NULL;
+ char ebuf [ BUFSIZ];
+ Slapi_DN *e_sdn;
+ aclUserGroup *ugroup = NULL;
+
+ e_sdn = slapi_sdn_new_ndn_byval ( n_dn );
+ /* Before we proceed, Let's first check if we are changing any groups.
+ ** If we are, then we need to change the signature
+ */
+ switch ( optype ) {
+ case SLAPI_OPERATION_MODIFY:
+ case SLAPI_OPERATION_DELETE:
+ slapi_pblock_get(pb, SLAPI_ENTRY_PRE_OP, (void*)&e);
+ break;
+ case SLAPI_OPERATION_ADD:
+ e = (Slapi_Entry *)change;
+ break;
+ }
+
+ /* e can be null for RDN */
+ if ( e ) slapi_entry_attr_find( e, "objectclass", &attr);
+
+ if ( attr ) {
+ int group_change = 0;
+ Slapi_Value *sval=NULL;
+ const struct berval *attrVal;
+ int i;
+
+ i= slapi_attr_first_value ( attr,&sval );
+ while(i != -1) {
+ attrVal = slapi_value_get_berval ( sval );
+ if ( (strcasecmp (attrVal->bv_val, "groupOfNames") == 0 ) ||
+ (strcasecmp (attrVal->bv_val, "groupOfUniqueNames") == 0 ) ||
+ (strcasecmp (attrVal->bv_val, "groupOfCertificates") == 0 ) ||
+ (strcasecmp (attrVal->bv_val, "groupOfURLs") == 0 ) ) {
+ group_change= 1;
+ if ( optype == SLAPI_OPERATION_MODIFY ) {
+ Slapi_Attr *a = NULL;
+ int rv;
+ rv = slapi_entry_attr_find ( e, "uniqueMember", &a);
+ if ( rv != 0 ) break;
+ rv = slapi_entry_attr_find ( e, "Member", &a );
+ if ( rv != 0 ) break;
+ rv = slapi_entry_attr_find ( e, "MemberURL", &a );
+ if ( rv != 0 ) break;
+ /* That means we are not changing the member
+ ** list, so it's okay to let go this
+ ** change
+ */
+ group_change = 0;
+ }
+ break;
+ }
+ i= slapi_attr_next_value ( attr, i, &sval );
+ }
+
+ /*
+ ** We can do better here XXX, i.e invalidate the cache for users who
+ ** use this group. for now just do the whole thing.
+ */
+ if ( group_change ) {
+ slapi_log_error(SLAPI_LOG_ACL, plugin_name,
+ "Group Change: Invalidating entire UserGroup Cache\n",
+ ACL_ESCAPE_STRING_WITH_PUNCTUATION(n_dn, ebuf));
+ aclg_regen_group_signature();
+ if ( (optype == SLAPI_OPERATION_MODIFY) || (optype == SLAPI_OPERATION_DELETE ) ) {
+ /* Then we need to invalidate the acl signature also */
+ acl_signature = aclutil_gen_signature ( acl_signature );
+ }
+ }
+ }
+
+ /*
+ * Here if the target entry is in the group cache
+ * as a user then, as it's being changed it may move out of any dynamic
+ * groups it belongs to.
+ * Just remove it for now--can do better XXX by checking to see if
+ * it really needs to be removed by testing to see if he's
+ * still in th group after the change--but this requires keeping
+ * the memberURL of the group which we don't currently do.
+ * Also, if we keep
+ * the attributes that are being used in dynamic
+ * groups then we could only remove the user if a sensitive
+ * attribute was being modified (rather than scanning the whole user cache
+ * all the time). Also could do a hash lookup.
+ *
+ * aclg_find_userGroup() incs a refcnt so we can still refer to ugroup.
+ * aclg_markUgroupForRemoval() decs it and marks it for removal
+ * , so after that you cannot refer to ugroup.
+ *
+ */
+
+ if ( (ugroup = aclg_find_userGroup(n_dn)) != NULL) {
+ /*
+ * Mark this for deletion next time round--try to impact
+ * this mainline code as little as possible.
+ */
+ slapi_log_error(SLAPI_LOG_ACL, plugin_name,
+ "Marking entry %s for removal from ACL user Group Cache\n",
+ ACL_ESCAPE_STRING_WITH_PUNCTUATION(n_dn, ebuf));
+ aclg_markUgroupForRemoval (ugroup);
+ }
+
+ /*
+ * Take the write lock around all the mods--so that
+ * other operations will see the acicache either before the whole mod
+ * or after but not, as it was before, during the mod.
+ * This is in line with the LDAP concept of the operation
+ * on the whole entry being the atomic unit.
+ *
+ */
+
+ switch(optype) {
+ case SLAPI_OPERATION_DELETE:
+ /* In this case we have already checked if the user has
+ ** right to delete the entry. Part of delete of entry is
+ ** remove all the ACLs also.
+ */
+
+ acllist_acicache_WRITE_LOCK();
+ rv = acllist_remove_aci_needsLock(e_sdn, NULL);
+ acllist_acicache_WRITE_UNLOCK();
+
+ break;
+ case SLAPI_OPERATION_ADD:
+ slapi_entry_attr_find ( (Slapi_Entry *) change, aci_attr_type, &attr );
+
+ if ( attr ) {
+ Slapi_Value *sval=NULL;
+ const struct berval *attrVal;
+ int i;
+
+ acllist_acicache_WRITE_LOCK();
+ i= slapi_attr_first_value ( attr,&sval );
+ while ( i != -1 ) {
+ attrVal = slapi_value_get_berval(sval);
+ rv= acllist_insert_aci_needsLock(e_sdn, attrVal );
+ if (rv <= ACL_ERR)
+ aclutil_print_err(rv, e_sdn, attrVal, NULL);
+ /* Print the aci list */
+ i= slapi_attr_next_value ( attr, i, &sval );
+ }
+ acllist_acicache_WRITE_UNLOCK();
+ }
+ break;
+
+ case SLAPI_OPERATION_MODIFY:
+ {
+ int got_write_lock = 0;
+
+ mods = (LDAPMod **) change;
+
+ for (j=0; mods[j] != NULL; j++) {
+ if (strcasecmp(mods[j]->mod_type, aci_attr_type) == 0) {
+
+ /* Got an aci to mod in this list of mods, so
+ * take the acicache lock for the whole list of mods,
+ * remembering to free it below.
+ */
+ if ( !got_write_lock) {
+ acllist_acicache_WRITE_LOCK();
+ got_write_lock = 1;
+ }
+
+ switch (mods[j]->mod_op & ~LDAP_MOD_BVALUES) {
+ case LDAP_MOD_REPLACE:
+ /* First remove the item */
+ rv = acllist_remove_aci_needsLock(e_sdn, NULL);
+
+ /* now fall thru to add the new one */
+ case LDAP_MOD_ADD:
+ /* Add the new aci */
+ if (mods[j]->mod_op & LDAP_MOD_BVALUES) {
+ bvalue = mods[j]->mod_bvalues;
+ if (bvalue == NULL)
+ break;
+ for (; *bvalue != NULL; ++bvalue) {
+ rv=acllist_insert_aci_needsLock( e_sdn, *bvalue);
+ if (rv <= ACL_ERR) {
+ aclutil_print_err(rv, e_sdn,
+ *bvalue, NULL);
+ }
+ }
+ } else {
+ value = mods[j]->mod_values;
+ if (value == NULL)
+ break;
+ for (; *value != NULL; ++value) {
+ b.bv_len = strlen (*value);
+ b.bv_val = *value;
+ rv=acllist_insert_aci_needsLock( e_sdn, &b);
+ if (rv <= ACL_ERR) {
+ aclutil_print_err(rv, e_sdn,
+ &b, NULL);
+ }
+ }
+ }
+ break;
+ case LDAP_MOD_DELETE:
+ if (mods[j]->mod_op & LDAP_MOD_BVALUES) {
+ bvalue = mods[j]->mod_bvalues;
+ if (bvalue == NULL || *bvalue == NULL) {
+ rv = acllist_remove_aci_needsLock( e_sdn, NULL);
+ } else {
+ for (; *bvalue != NULL; ++bvalue)
+ acllist_remove_aci_needsLock( e_sdn, *bvalue);
+ }
+ } else {
+ value = mods[j]->mod_values;
+ if (value == NULL || *value == NULL) {
+ acllist_remove_aci_needsLock( e_sdn,NULL);
+ } else {
+ for (; *value != NULL; ++value) {
+ b.bv_len = strlen (*value);
+ b.bv_val = *value;
+ acllist_remove_aci_needsLock( e_sdn, &b);
+ }
+ }
+
+ }
+ break;
+
+ default:
+ break;
+ }/* modtype switch */
+ }/* attrtype is aci */
+ } /* end of for */
+ if ( got_write_lock ) {
+ acllist_acicache_WRITE_UNLOCK();
+ got_write_lock = 0;
+ }
+
+ break;
+ }/* case op is modify*/
+
+ case SLAPI_OPERATION_MODRDN:
+
+ new_RDN = (char*) change;
+ slapi_log_error (SLAPI_LOG_ACL, plugin_name,
+ "acl_modified (MODRDN %s => \"%s\"\n",
+ ACL_ESCAPE_STRING_WITH_PUNCTUATION (n_dn, ebuf), new_RDN, 0);
+
+ /* compute new_DN: */
+ parent_DN = slapi_dn_parent (n_dn);
+ if (parent_DN == NULL) {
+ new_DN = new_RDN;
+ } else {
+ new_DN = (char*) slapi_ch_malloc (strlen (new_RDN) + 3
+ + strlen (parent_DN));
+ strcpy (new_DN, new_RDN);
+ strcat (new_DN, ",");
+ strcat (new_DN, parent_DN);
+ slapi_dn_normalize (new_DN);
+ }
+
+ /* Change the acls */
+ acllist_acicache_WRITE_LOCK();
+ acllist_moddn_aci_needsLock ( e_sdn, new_DN );
+ acllist_acicache_WRITE_UNLOCK();
+
+ /* deallocat the parent_DN */
+ if (parent_DN != NULL) {
+ slapi_ch_free ( (void **) &new_DN );
+ slapi_ch_free ( (void **) &parent_DN );
+ }
+ break;
+
+ default:
+ /* print ERROR */
+ break;
+ } /*optype switch */
+
+ slapi_sdn_free ( &e_sdn );
+
+}
+/***************************************************************************
+*
+* acl__scan_for_acis
+* Scan the list and picup the correct acls for evaluation.
+*
+* Input:
+* Acl_PBlock *aclpb - Main ACL pblock
+* int *err; - Any error status
+* Returns:
+* num_handles - Number of handles matched to the
+* - resource + 1.
+* Error Handling:
+* None.
+*
+**************************************************************************/
+static int
+acl__scan_for_acis(Acl_PBlock *aclpb, int *err)
+{
+ aci_t *aci;
+ NSErr_t errp;
+ int attr_matched;
+ int deny_handle;
+ int allow_handle;
+ int gen_allow_handle = ACI_MAX_ELEVEL+1;
+ int gen_deny_handle = ACI_MAX_ELEVEL+1;
+ int i;
+ PRUint32 cookie;
+
+ TNF_PROBE_0_DEBUG(acl__scan_for_acis_start,"ACL","");
+
+ /*
+ ** Determine if we are traversing via the list Vs. we have our own
+ ** generated list
+ */
+ if ( aclpb->aclpb_state & ACLPB_SEARCH_BASED_ON_LIST ||
+ aclpb->aclpb_handles_index[0] != -1 ) {
+ int kk = 0;
+ while ( kk < ACLPB_MAX_SELECTED_ACLS && aclpb->aclpb_handles_index[kk] != -1 ) {
+ slapi_log_error(SLAPI_LOG_ACL, plugin_name, "Using ACL Cointainer:%d for evaluation\n", kk);
+ kk++;
+ }
+ }
+
+ memset (&errp, 0, sizeof(NSErr_t));
+ *err = ACL_FALSE;
+ aclpb->aclpb_num_deny_handles = -1;
+ aclpb->aclpb_num_allow_handles = -1;
+ for (i=0; i <= ACI_MAX_ELEVEL; i++) {
+ aclpb->aclpb_deny_handles [i] = NULL;
+ aclpb->aclpb_allow_handles [i] = NULL;
+ }
+
+ /* Check the signature. If it has changed, start fresh */
+ if ( aclpb->aclpb_signature != acl_signature ) {
+ slapi_log_error (SLAPI_LOG_ACL, plugin_name,
+ "Restart the scan -- due to acl changes\n");
+ acllist_init_scan ( aclpb->aclpb_pblock, LDAP_SCOPE_BASE, NULL );
+ }
+
+ attr_matched = ACL_FALSE;
+ deny_handle = 0;
+ allow_handle = 0;
+ i = 0;
+
+ aclpb->aclpb_stat_acllist_scanned++;
+ aci = acllist_get_first_aci ( aclpb, &cookie );
+
+ while( aci ) {
+ if (acl__resource_match_aci(aclpb, aci, 0, &attr_matched)) {
+ /* Generate the ACL list handle */
+ if (aci->aci_handle == NULL) {
+ aci = acllist_get_next_aci ( aclpb, aci, &cookie );
+ continue;
+ }
+ aclutil_print_aci (aci, acl_access2str (aclpb->aclpb_access));
+
+ if (aci->aci_type & ACI_HAS_DENY_RULE) {
+ if (aclpb->aclpb_deny_handles[aci->aci_elevel] == NULL ) {
+ aclpb->aclpb_deny_handles[aci->aci_elevel] = aci;
+ } else {
+ if ((gen_deny_handle + ACI_DEFAULT_ELEVEL + 1) ==
+ aclpb->aclpb_deny_handles_size ) {
+ int num = ACLPB_INCR_LIST_HANDLES +
+ aclpb->aclpb_deny_handles_size;
+ /* allocate more space */
+ aclpb->aclpb_deny_handles =
+ (aci_t **)
+ slapi_ch_realloc (
+ (void *) aclpb->aclpb_deny_handles,
+ num * sizeof (aci_t *));
+ aclpb->aclpb_deny_handles_size = num;
+ }
+ aclpb->aclpb_deny_handles [gen_deny_handle] = aci;
+ gen_deny_handle++;
+ }
+ deny_handle++;
+ }
+ /*
+ ** It's possible that a single acl is in both the camps i.e
+ ** It has a allow and a deny rule
+ ** In that case we keep the same acl in both the camps.
+ */
+ if (aci->aci_type & ACI_HAS_ALLOW_RULE) {
+ if (aclpb->aclpb_allow_handles[aci->aci_elevel] == NULL ) {
+ aclpb->aclpb_allow_handles[aci->aci_elevel] = aci;
+ } else {
+ if ((gen_allow_handle + ACI_DEFAULT_ELEVEL + 1) ==
+ aclpb->aclpb_allow_handles_size) {
+ /* allocate more space */
+ int num = ACLPB_INCR_LIST_HANDLES +
+ aclpb->aclpb_allow_handles_size;
+
+ aclpb->aclpb_allow_handles =
+ (aci_t **)
+ slapi_ch_realloc (
+ (void *) aclpb->aclpb_allow_handles,
+ num * sizeof (aci_t *));
+ aclpb->aclpb_allow_handles_size = num;
+ }
+ aclpb->aclpb_allow_handles [gen_allow_handle] = aci;
+ gen_allow_handle++;
+ }
+ allow_handle++;
+ }
+ }
+ aci = acllist_get_next_aci ( aclpb, aci, &cookie );
+ } /* end of while */
+
+ /* make the last one a null */
+ aclpb->aclpb_deny_handles [gen_deny_handle] = NULL;
+ aclpb->aclpb_allow_handles [gen_allow_handle] = NULL;
+
+ /* specify how many we found */
+ aclpb->aclpb_num_deny_handles = deny_handle;
+ aclpb->aclpb_num_allow_handles = allow_handle;
+
+ slapi_log_error(SLAPI_LOG_ACL, plugin_name, "Num of ALLOW Handles:%d, DENY handles:%d\n",
+ aclpb->aclpb_num_allow_handles, aclpb->aclpb_num_deny_handles, 0);
+
+ TNF_PROBE_0_DEBUG(acl__scan_for_acis_end,"ACL","");
+
+ return(allow_handle + deny_handle);
+}
+
+/***************************************************************************
+*
+* acl__resource_match_aci
+*
+* This compares the ACI for the given resource and determines if
+* the ACL applies to the resource or not.
+*
+* For read/search operation, we collect all the possible acls which
+* will apply, We will be using this list for future acl evaluation
+* for that entry.
+*
+* Input:
+* struct acl_pblock *aclpb - Main acl private block
+* aci_t *aci - The ACI item
+* int skip_attrEval - DOn't check for attrs
+* int *a_matched - Attribute matched
+*
+* Returns:
+*
+* ACL_TRUE - Yep, This ACL is applicable to the
+* - the resource.
+* ACL_FALSE - No it is not.
+*
+* Error Handling:
+* None.
+*
+**************************************************************************/
+#define ACL_RIGHTS_TARGETATTR_NOT_NEEDED ( SLAPI_ACL_ADD | SLAPI_ACL_DELETE | SLAPI_ACL_PROXY)
+static int
+acl__resource_match_aci( Acl_PBlock *aclpb, aci_t *aci, int skip_attrEval, int *a_matched)
+{
+
+ struct slapi_filter *f; /* filter */
+ int rv; /* return value */
+ int matches;
+ int attr_matched;
+ int attr_matched_in_targetattrfilters = 0;
+ int dn_matched;
+ char *res_attr;
+ int aci_right = 0;
+ int res_right = 0;
+ int star_matched = ACL_FALSE;
+ int num_attrs = 0;
+ AclAttrEval *c_attrEval = NULL;
+ const char *res_ndn = NULL;
+ const char *aci_ndn = NULL;
+ char *matched_val = NULL;
+ int add_matched_val_to_ht = 0;
+ char res_right_str[128];
+
+ TNF_PROBE_0_DEBUG(acl__resource_match_aci_start,"ACL","");
+
+ aclpb->aclpb_stat_aclres_matched++;
+
+ /* Assume that resource matches */
+ matches = ACL_TRUE;
+
+ /* Figure out if the acl has the correct rights or not */
+ aci_right = aci->aci_access;
+ res_right = aclpb->aclpb_access;
+ if (!(aci_right & res_right)) {
+ /* If we are looking for read/search and the acl has read/search
+ ** then go further because if targets match we may keep that
+ ** acl in the entry cache list.
+ */
+ if (!((res_right & (SLAPI_ACL_SEARCH | SLAPI_ACL_READ)) &&
+ (aci_right & (SLAPI_ACL_SEARCH | SLAPI_ACL_READ))))
+ matches = ACL_FALSE;
+ goto acl__resource_match_aci_EXIT;
+ }
+
+
+ /* first Let's see if the entry is under the subtree where the
+ ** ACL resides. We can't let somebody affect a target beyond the
+ ** scope of where the ACL resides
+ ** Example: ACL is located in "ou=engineering, o=ace industry, c=us
+ ** but if the target is "o=ace industry, c=us", then we are in trouble.
+ **
+ ** If the aci is in the rootdse and the entry is not, then we do not
+ ** match--ie. acis in the rootdse do NOT apply below...for the moment.
+ **
+ */
+ res_ndn = slapi_sdn_get_ndn ( aclpb->aclpb_curr_entry_sdn );
+ aci_ndn = slapi_sdn_get_ndn ( aci->aci_sdn );
+ if (!slapi_sdn_issuffix(aclpb->aclpb_curr_entry_sdn, aci->aci_sdn)
+ || (!slapi_is_rootdse(res_ndn) && slapi_is_rootdse(aci_ndn)) ) {
+
+ /* cant' poke around */
+ matches = ACL_FALSE;
+ goto acl__resource_match_aci_EXIT;
+ }
+
+ /*
+ ** We have a single ACI which we need to find if it applies to
+ ** the resource or not.
+ */
+ if ((aci->aci_type & ACI_TARGET_DN) &&
+ (aclpb->aclpb_curr_entry_sdn)) {
+ char *avaType;
+ struct berval *avaValue;
+
+ f = aci->target;
+ dn_matched = ACL_TRUE;
+ slapi_filter_get_ava ( f, &avaType, &avaValue );
+
+ if (!slapi_dn_issuffix( res_ndn, avaValue->bv_val)) {
+ dn_matched = ACL_FALSE;
+ }
+ if (aci->aci_type & ACI_TARGET_NOT) {
+ matches = (dn_matched ? ACL_FALSE : ACL_TRUE);
+ } else {
+ matches = (dn_matched ? ACL_TRUE: ACL_FALSE);
+ }
+ }
+
+ /* No need to look further */
+ if (matches == ACL_FALSE) {
+ goto acl__resource_match_aci_EXIT;
+ }
+
+ if (aci->aci_type & ACI_TARGET_PATTERN) {
+
+ f = aci->target;
+ dn_matched = ACL_TRUE;
+
+ if ((rv = acl_match_substring(f, (char *)res_ndn, 0 /* match suffux */)) != ACL_TRUE) {
+ dn_matched = ACL_FALSE;
+ if(rv == ACL_ERR) {
+ slapi_log_error(SLAPI_LOG_ACL, plugin_name,
+ "acl__resource_match_aci:pattern err\n",
+ 0,0,0);
+ matches = ACL_FALSE;
+ goto acl__resource_match_aci_EXIT;
+ }
+ }
+ if (aci->aci_type & ACI_TARGET_NOT) {
+ matches = (dn_matched ? ACL_FALSE : ACL_TRUE);
+ } else {
+ matches = (dn_matched ? ACL_TRUE: ACL_FALSE);
+ }
+ }
+
+ /* No need to look further */
+ if (matches == ACL_FALSE) {
+ goto acl__resource_match_aci_EXIT;
+ }
+
+ /*
+ * Is it a (target="ldap://cn=*,($dn),o=sun.com") kind of thing.
+ */
+ if (aci->aci_type & ACI_TARGET_MACRO_DN) {
+ /*
+ * See if the ($dn) component matches the string and
+ * retrieve the matched substring for later use
+ * in the userdn.
+ * The macro string is a function of the dn only, so if the
+ * entry is the same one don't recalculate it--
+ * this flag only works for search right now, could
+ * also optimise for mods by making it work for mods.
+ */
+
+ if ( (aclpb->aclpb_res_type & ACLPB_NEW_ENTRY) == 0 ) {
+ /*
+ * Here same entry so just look up the matched value,
+ * calculated from the targetdn and stored judiciously there
+ */
+ matched_val = (char *)acl_ht_lookup( aclpb->aclpb_macro_ht,
+ (PLHashNumber)aci->aci_index);
+ }
+ if ( matched_val == NULL &&
+ (aclpb->aclpb_res_type & (ACLPB_NEW_ENTRY | ACLPB_EFFECTIVE_RIGHTS))) {
+
+ slapi_log_error (SLAPI_LOG_ACL, plugin_name,
+ "Evaluating macro aci(%d)%s for resource %s\n",
+ aci->aci_index, aci->aclName,
+ aclutil__access_str(res_right, res_right_str));
+ matched_val = acl_match_macro_in_target( res_ndn,
+ aci->aci_macro->match_this,
+ aci->aci_macro->macro_ptr);
+ add_matched_val_to_ht = 1; /* may need to add matched value to ht */
+ }
+ if (matched_val == NULL) {
+ dn_matched = ACL_FALSE;
+ } else {
+ dn_matched = ACL_TRUE;
+ }
+
+ if (aci->aci_type & ACI_TARGET_NOT) {
+ matches = (dn_matched ? ACL_FALSE : ACL_TRUE);
+ } else {
+ matches = (dn_matched ? ACL_TRUE: ACL_FALSE);
+ }
+
+ if ( add_matched_val_to_ht ) {
+ if ( matches == ACL_TRUE && matched_val ) {
+ /*
+ * matched_val may be needed later for matching on
+ * other targets or on the subject--so optimistically
+ * put it in the hash table.
+ * If, at the end of this routine, we
+ * find that after all the resource did not match then
+ * that's ok--the value is freed at aclpb cleanup time.
+ * If there is already an entry for this aci in this
+ * aclpb then remove it--it's an old value for a
+ * different entry.
+ */
+
+ acl_ht_add_and_freeOld(aclpb->aclpb_macro_ht,
+ (PLHashNumber)aci->aci_index,
+ matched_val);
+ slapi_log_error (SLAPI_LOG_ACL, plugin_name,
+ "-- Added aci(%d) and matched value (%s) to macro ht\n",
+ aci->aci_index, matched_val);
+ acl_ht_display_ht(aclpb->aclpb_macro_ht);
+ } else {
+ slapi_ch_free((void **)&matched_val);
+ if (matches == ACL_FALSE) {
+ slapi_log_error (SLAPI_LOG_ACL, plugin_name,
+ "Evaluated ACL_FALSE\n");
+ }
+ }
+ }
+ } /* MACRO_DN */
+
+ /* No need to look further */
+ if (matches == ACL_FALSE) {
+ goto acl__resource_match_aci_EXIT;
+ }
+
+ /*
+ ** Here, if there's a targetfilter field, see if it matches.
+ **
+ ** The commented out code below was an erroneous attempt to skip
+ ** this test. It is wrong because: 1. you need to store
+ ** whether the last test matched or not (you cannot just assume it did)
+ ** and 2. It may not be the same aci, so the previous matched
+ ** value is a function of the aci.
+ ** May be interesting to build such a cache...but no evidence for
+ ** for that right now. See Bug 383424.
+ **
+ **
+ ** && ((aclpb->aclpb_state & ACLPB_SEARCH_BASED_ON_LIST) ||
+ ** (aclpb->aclpb_res_type & ACLPB_NEW_ENTRY))
+ */
+ if (aci->aci_type & ACI_TARGET_FILTER ) {
+ int filter_matched = ACL_TRUE;
+
+ /*
+ * Check for macros.
+ * For targetfilter we need to fake the lasinfo structure--it's
+ * created "naturally" for subjects but not targets.
+ */
+
+
+ if ( aci->aci_type & ACI_TARGET_FILTER_MACRO_DN) {
+
+ lasInfo *lasinfo = NULL;
+
+ lasinfo = (lasInfo*) slapi_ch_malloc( sizeof(lasInfo) );
+
+ lasinfo->aclpb = aclpb;
+ lasinfo->resourceEntry = aclpb->aclpb_curr_entry;
+ aclpb->aclpb_curr_aci = aci;
+ filter_matched = aclutil_evaluate_macro( aci->targetFilterStr,
+ lasinfo,
+ ACL_EVAL_TARGET_FILTER);
+ slapi_ch_free((void**)&lasinfo);
+ } else {
+
+
+ if (slapi_vattr_filter_test(NULL, aclpb->aclpb_curr_entry,
+ aci->targetFilter,
+ 0 /*don't do acess chk*/)!= 0) {
+ filter_matched = ACL_FALSE;
+ }
+
+ }
+
+ /* If it's a logical value we can do logic on it...otherwise we do not match */
+ if ( filter_matched == ACL_TRUE || filter_matched == ACL_FALSE) {
+ if (aci->aci_type & ACI_TARGET_FILTER_NOT) {
+ matches = (filter_matched == ACL_TRUE ? ACL_FALSE : ACL_TRUE);
+ } else {
+ matches = (filter_matched == ACL_TRUE ? ACL_TRUE: ACL_FALSE);
+ }
+ } else {
+ matches = ACL_FALSE;
+ slapi_log_error( SLAPI_LOG_ACL, plugin_name,
+ "Returning UNDEFINED for targetfilter evaluation.\n");
+ }
+
+ if (matches == ACL_FALSE) {
+ goto acl__resource_match_aci_EXIT;
+ }
+ }
+
+ /*
+ * Check to see if we need to evaluate any targetattrfilters.
+ * They look as follows:
+ * (targetattrfilters="add=sn:(sn=rob) && gn:(gn!=byrne),
+ * del=sn:(sn=rob) && gn:(gn=byrne)")
+ *
+ * For ADD/DELETE:
+ * If theres's a targetattrfilter then each add/del filter
+ * that applies to an attribute in the entry, must be satisfied
+ * by each value of the attribute in the entry.
+ *
+ * For MODIFY:
+ * If there's a targetattrfilter then the add/del filter
+ * must be satisfied by the attribute to be added/deleted.
+ * (MODIFY acl is evaluated one value at a time).
+ *
+ *
+ */
+
+ if ((aclpb->aclpb_access & SLAPI_ACL_ADD &&
+ aci->aci_type & ACI_TARGET_ATTR_ADD_FILTERS )||
+ (aclpb->aclpb_access & SLAPI_ACL_DELETE &&
+ aci->aci_type & ACI_TARGET_ATTR_DEL_FILTERS ) ) {
+
+ Targetattrfilter **attrFilterArray;
+
+ Targetattrfilter *attrFilter = NULL;
+
+ int found_applicable = 0;
+ Slapi_Attr *attr_ptr = NULL;
+ Slapi_Value *sval;
+ const struct berval *attrVal;
+ int k;
+ int done;
+
+
+ if (aclpb->aclpb_access & SLAPI_ACL_ADD &&
+ aci->aci_type & ACI_TARGET_ATTR_ADD_FILTERS) {
+
+ attrFilterArray = aci->targetAttrAddFilters;
+
+ } else if (aclpb->aclpb_access & SLAPI_ACL_DELETE &&
+ aci->aci_type & ACI_TARGET_ATTR_DEL_FILTERS) {
+
+ attrFilterArray = aci->targetAttrDelFilters;
+
+ }
+
+ attr_matched = ACL_TRUE;
+ num_attrs = 0;
+
+ while (attrFilterArray[num_attrs] && attr_matched) {
+ attrFilter = attrFilterArray[num_attrs];
+
+ /*
+ * If this filter applies to an attribute in the entry,
+ * apply it to the entry.
+ * Otherwise just ignore it.
+ *
+ */
+
+ if (slapi_entry_attr_find ( aclpb->aclpb_curr_entry,
+ attrFilter->attr_str,
+ &attr_ptr) == 0) {
+
+ /*
+ * This is an applicable filter.
+ * The filter is to be appplied to the entry being added
+ * or deleted.
+ * The filter needs to be satisfied by _each_ occurence
+ * of the attribute in the entry--otherwise you
+ * could satisfy the filter and then put loads of other
+ * values in on the back of it.
+ */
+
+ found_applicable = 1;
+
+ sval=NULL;
+ attrVal=NULL;
+ k= slapi_attr_first_value(attr_ptr,&sval);
+ done = 0;
+ while(k != -1 && !done) {
+ attrVal = slapi_value_get_berval(sval);
+
+ if ( acl__make_filter_test_entry(
+ &aclpb->aclpb_filter_test_entry,
+ attrFilter->attr_str,
+ (struct berval *)attrVal) == LDAP_SUCCESS ) {
+
+ attr_matched= acl__test_filter(
+ aclpb->aclpb_filter_test_entry,
+ attrFilter->filter,
+ 1 /* Do filter sense evaluation below */
+ );
+ done = !attr_matched;
+ slapi_entry_free( aclpb->aclpb_filter_test_entry );
+ }
+
+ k= slapi_attr_next_value(attr_ptr, k, &sval);
+ }/* while */
+
+ /*
+ * Here, we applied an applicable filter to the entry.
+ * So if attr_matched is ACL_TRUE then every value
+ * of the attribute in the entry satisfied the filter.
+ * Otherwise, attr_matched is ACL_FALSE and not every
+ * value satisfied the filter, so we will teminate the
+ * scan of the filter list.
+ */
+
+ }
+
+ num_attrs++;
+ } /* while */
+
+ /*
+ * Here, we've applied all the applicable filters to the entry.
+ * Each one must have been satisfied by all the values of the attribute.
+ * The result of this is stored in attr_matched.
+ */
+
+#if 0
+ /*
+ * Don't support a notion of "add != " or "del != "
+ * at the moment.
+ * To do this, need to test whether it's an add test or del test
+ * then if it's add and ACI_TARGET_ATTR_ADD_FILTERS_NOT then
+ * flip the bit. Same for del.
+ */
+
+ if (aci->aci_type & ACI_TARGET_ATTR_DEL_FILTERS_NOT) {
+ matches = (matches ? ACL_FALSE : ACL_TRUE);
+ } else {
+ matches = (matches ? ACL_TRUE: ACL_FALSE);
+ }
+#endif
+
+ /* No need to look further */
+ if (attr_matched == ACL_FALSE) {
+ matches = ACL_FALSE;
+ goto acl__resource_match_aci_EXIT;
+ }
+
+ } else if ( (aclpb->aclpb_access & ACLPB_SLAPI_ACL_WRITE_ADD &&
+ aci->aci_type & ACI_TARGET_ATTR_ADD_FILTERS) ||
+ (aclpb->aclpb_access & ACLPB_SLAPI_ACL_WRITE_DEL &&
+ aci->aci_type & ACI_TARGET_ATTR_DEL_FILTERS ) ) {
+
+
+ /*
+ * Here, it's a modify add/del and we have attr filters.
+ * So, we need to scan the add/del filter list to find the filter
+ * that applies to the current attribute.
+ * Then the (attribute,value) pair being added/deleted better
+ * match that filter.
+ *
+ *
+ */
+
+ Targetattrfilter **attrFilterArray = NULL;
+ Targetattrfilter *attrFilter;
+ int found = 0;
+
+ if (aclpb->aclpb_access & ACLPB_SLAPI_ACL_WRITE_ADD &&
+ aci->aci_type & ACI_TARGET_ATTR_ADD_FILTERS) {
+
+ attrFilterArray = aci->targetAttrAddFilters;
+
+ } else if (aclpb->aclpb_access & ACLPB_SLAPI_ACL_WRITE_DEL &&
+ aci->aci_type & ACI_TARGET_ATTR_DEL_FILTERS) {
+
+ attrFilterArray = aci->targetAttrDelFilters;
+
+ }
+
+
+ /*
+ * Scan this filter list for an applicable filter.
+ */
+
+ found = 0;
+ num_attrs = 0;
+
+ while (attrFilterArray[num_attrs] && !found) {
+ attrFilter = attrFilterArray[num_attrs];
+
+ /* If this filter applies to the attribute, stop. */
+ if ((aclpb->aclpb_curr_attrEval) &&
+ slapi_attr_type_cmp ( aclpb->aclpb_curr_attrEval->attrEval_name,
+ attrFilter->attr_str, 1) == 0) {
+ found = 1;
+ }
+ num_attrs++;
+ }
+
+ /*
+ * Here, if found an applicable filter, then apply the filter to the
+ * (attr,val) pair.
+ * Otherwise, ignore the targetattrfilters.
+ */
+
+ if (found) {
+
+ if ( acl__make_filter_test_entry(
+ &aclpb->aclpb_filter_test_entry,
+ aclpb->aclpb_curr_attrEval->attrEval_name,
+ aclpb->aclpb_curr_attrVal) == LDAP_SUCCESS ) {
+
+ attr_matched= acl__test_filter(aclpb->aclpb_filter_test_entry,
+ attrFilter->filter,
+ 1 /* Do filter sense evaluation below */
+ );
+ slapi_entry_free( aclpb->aclpb_filter_test_entry );
+ }
+
+ /* No need to look further */
+ if (attr_matched == ACL_FALSE) {
+ matches = attr_matched;
+ goto acl__resource_match_aci_EXIT;
+ }
+
+ /*
+ * Here this attribute appeared and was matched in a
+ * targetattrfilters list, so record this fact so we do
+ * not have to scan the targetattr list for the attribute.
+ */
+
+ attr_matched_in_targetattrfilters = 1;
+
+
+ }
+ } /* targetvaluefilters */
+
+
+ /* There are 3 cases by which acis are selected.
+ ** 1) By scanning the whole list and picking based on the resource.
+ ** 2) By picking a subset of the list which will be used for the whole
+ ** acl evaluation.
+ ** 3) A finer granularity, i.e, a selected list of acls which will be
+ ** used for only that entry's evaluation.
+ */
+ if ( !(skip_attrEval) && (aclpb->aclpb_state & ACLPB_SEARCH_BASED_ON_ENTRY_LIST) &&
+ (res_right & SLAPI_ACL_SEARCH) &&
+ ((aci->aci_access & SLAPI_ACL_READ) || (aci->aci_access & SLAPI_ACL_SEARCH))) {
+ int kk=0;
+
+ while ( kk < ACLPB_MAX_SELECTED_ACLS && aclpb->aclpb_handles_index[kk] >=0 ) kk++;
+ if (kk >= ACLPB_MAX_SELECTED_ACLS) {
+ aclpb->aclpb_state &= ~ACLPB_SEARCH_BASED_ON_ENTRY_LIST;
+ } else {
+ aclpb->aclpb_handles_index[kk++] = aci->aci_index;
+ aclpb->aclpb_handles_index[kk] = -1;
+ }
+ }
+
+
+ /* If we are suppose to skip attr eval, then let's skip it */
+ if ( (aclpb->aclpb_access & SLAPI_ACL_SEARCH ) && ( ! skip_attrEval ) &&
+ ( aclpb->aclpb_res_type & ACLPB_NEW_ENTRY )) {
+ aclEvalContext *c_evalContext = &aclpb->aclpb_curr_entryEval_context;
+ int nhandle = c_evalContext->acle_numof_tmatched_handles;
+
+ if ( nhandle < ACLPB_MAX_SELECTED_ACLS) {
+ c_evalContext->acle_handles_matched_target[nhandle] = aci->aci_index;
+ c_evalContext->acle_numof_tmatched_handles++;
+ }
+ }
+
+ if ( skip_attrEval ) {
+ goto acl__resource_match_aci_EXIT;
+ }
+
+ /* We need to check again because we don't want to select this handle
+ ** if the right doesn't match for now.
+ */
+ if (!(aci_right & res_right)) {
+ matches = ACL_FALSE;
+ goto acl__resource_match_aci_EXIT;
+ }
+
+ /*
+ * Here if the request is one that requires matching
+ * on a targetattr then do it here.
+ * If we have already matched an attribute in the targetattrfitlers list
+ * then we do not require a match in the targetattr so we can skip it.
+ * The operations that require targetattr are SLAPI_ACL_COMPARE,
+ * SLAPI_ACL_SEARCH, SLAPI_ACL_READ and SLAPI_ACL_WRITE, as long as
+ * c_attrEval is non-null (otherwise it's a modrdn op which
+ * does not require the targetattr list).
+ *
+ * rbyrneXXX if we had a proper permission for modrdn eg SLAPI_ACL_MODRDN
+ * then we would not need this crappy way of telling it was a MODRDN
+ * request ie. SLAPI_ACL_WRITE && !(c_attrEval).
+ */
+
+ c_attrEval = aclpb->aclpb_curr_attrEval;
+
+ /*
+ * If we've already matched on targattrfilter then do not
+ * bother to look at the attrlist.
+ */
+
+ if (!attr_matched_in_targetattrfilters) {
+
+ /* match target attr */
+ if ((c_attrEval) &&
+ (aci->aci_type & ACI_TARGET_ATTR)) {
+ /* there is a target ATTR */
+ Targetattr **attrArray = aci->targetAttr;
+ Targetattr *attr = NULL;
+
+ res_attr = c_attrEval->attrEval_name;
+ attr_matched = ACL_FALSE;
+ star_matched = ACL_FALSE;
+ num_attrs = 0;
+
+ while (attrArray[num_attrs] && !attr_matched) {
+ attr = attrArray[num_attrs];
+ if (attr->attr_type & ACL_ATTR_STRING) {
+ if (slapi_attr_type_cmp ( res_attr,
+ attr->u.attr_str, 1) == 0) {
+ attr_matched = ACL_TRUE;
+ *a_matched = ACL_TRUE;
+ }
+ } else if (attr->attr_type & ACL_ATTR_FILTER) {
+ if (ACL_TRUE == acl_match_substring (
+ attr->u.attr_filter,
+ res_attr, 1)) {
+ attr_matched = ACL_TRUE;
+ *a_matched = ACL_TRUE;
+ }
+ } else if (attr->attr_type & ACL_ATTR_STAR) {
+ attr_matched = ACL_TRUE;
+ *a_matched = ACL_TRUE;
+ star_matched = ACL_TRUE;
+ }
+ num_attrs++;
+ }
+
+ if (aci->aci_type & ACI_TARGET_ATTR_NOT) {
+ matches = (attr_matched ? ACL_FALSE : ACL_TRUE);
+ } else {
+ matches = (attr_matched ? ACL_TRUE: ACL_FALSE);
+ }
+
+
+ aclpb->aclpb_state &= ~ACLPB_ATTR_STAR_MATCHED;
+ /* figure out how it matched, i.e star matched */
+ if (matches && star_matched && num_attrs == 1 &&
+ !(aclpb->aclpb_state & ACLPB_FOUND_ATTR_RULE))
+ aclpb->aclpb_state |= ACLPB_ATTR_STAR_MATCHED;
+ else {
+ /* we are here means that there is a specific
+ ** attr in the rule for this resource.
+ ** We need to avoid this case
+ ** Rule 1: (targetattr = "uid")
+ ** Rule 2: (targetattr = "*")
+ ** we cannot use STAR optimization
+ */
+ aclpb->aclpb_state |= ACLPB_FOUND_ATTR_RULE;
+ aclpb->aclpb_state &= ~ACLPB_ATTR_STAR_MATCHED;
+ }
+ } else if ( (c_attrEval) ||
+ (aci->aci_type & ACI_TARGET_ATTR)) {
+ if ((aci_right & ACL_RIGHTS_TARGETATTR_NOT_NEEDED) &&
+ (aclpb->aclpb_access & ACL_RIGHTS_TARGETATTR_NOT_NEEDED)) {
+ /*
+ ** Targetattr rule doesn't make any sense
+ ** in this case. So select this rule
+ ** default: matches = ACL_TRUE;
+ */
+ ;
+ } else if (aci_right & SLAPI_ACL_WRITE &&
+ (aci->aci_type & ACI_TARGET_ATTR) &&
+ !(c_attrEval)) {
+ /* We need to handle modrdn operation. Modrdn doesn't
+ ** change any attrs but changes the RDN and so (attr=NULL).
+ ** Here we found an acl which has a targetattr but
+ ** the resource doesn't need one. In that case, we should
+ ** consider this acl.
+ ** default: matches = ACL_TRUE;
+ */
+ ;
+ } else {
+ matches = ACL_FALSE;
+ }
+ }
+ }/* !attr_matched_in_targetattrfilters */
+
+ /*
+ ** Here we are testing if we find a entry test rule (which should
+ ** be rare). In that case, just remember it. An entry test rule
+ ** doesn't have "(targetattr)".
+ */
+ if (aclpb && (aclpb->aclpb_state & ACLPB_EVALUATING_FIRST_ATTR) &&
+ (!(aci->aci_type & ACI_TARGET_ATTR))) {
+ aclpb->aclpb_state |= ACLPB_FOUND_A_ENTRY_TEST_RULE;
+ }
+
+ /*
+ * Generic exit point for this routine:
+ * matches is ACL_TRUE if the aci matches the target of the resource,
+ * ACL_FALSE othrewise.
+ * Apologies for the goto--this is a retro-fitted exit point.
+ */
+acl__resource_match_aci_EXIT:
+
+ /*
+ * For macro acis, there may be a partial macro string
+ * placed in the aclpb_macro_ht
+ * even if the aci did not finally match.
+ * All the partial strings will be freed at aclpb
+ * cleanup time.
+ */
+
+ TNF_PROBE_0_DEBUG(acl__resource_match_aci_end,"ACL","");
+
+ return (matches);
+}
+/* Macro to determine if the cached result is valid or not. */
+#define ACL_CACHED_RESULT_VALID( result) \
+ (((result & ACLPB_CACHE_READ_RES_ALLOW) && \
+ (result & ACLPB_CACHE_READ_RES_SKIP)) || \
+ ((result & ACLPB_CACHE_SEARCH_RES_ALLOW) && \
+ (result & ACLPB_CACHE_SEARCH_RES_SKIP))) ? 0 : 1
+/***************************************************************************
+*
+* acl__TestRights
+*
+* Test the rights and find out if access is allowed or not.
+*
+* Processing Alogorithm:
+*
+* First, process the DENY rules one by one. If the user is not explicitly
+* denied, then check if the user is allowed by processing the ALLOW handles
+* one by one.
+* The result of the evaluation is cached. Exceptions are
+* -- If an acl happens to be in both DENY and ALLOW camp.
+* -- Only interested for READ/SEARCH right.
+*
+* Input:
+* struct acl_pblock *aclpb - main acl private block
+* int access - The access bits
+* char **right - The right we are looking for
+* char ** generic - Generic rights
+*
+* Returns:
+*
+* ACL_RES_ALLOW - Access allowed
+* ACL_RES_DENY - Access denied
+* err - error condition
+*
+* Error Handling:
+* None.
+*
+**************************************************************************/
+static int
+acl__TestRights(Acl_PBlock *aclpb,int access, char **right, char ** map_generic,
+ aclResultReason_t *result_reason)
+{
+ ACLEvalHandle_t *acleval;
+ int rights_rv = ACL_RES_DENY;
+ int rv, i,j, k;
+ int index;
+ char *deny = NULL;
+ char *deny_generic = NULL;
+ char *acl_tag;
+ int expr_num;
+ char *testRights[2];
+ aci_t *aci;
+ int numHandles = 0;
+ aclEvalContext *c_evalContext = NULL;
+
+ TNF_PROBE_0_DEBUG(acl__TestRights_start,"ACL","");
+
+ c_evalContext = &aclpb->aclpb_curr_entryEval_context;
+
+ /* record the aci and reason for access decision */
+ result_reason->deciding_aci = NULL;
+ result_reason->reason = ACL_REASON_NONE;
+
+ /* If we don't have any ALLLOW handles, it's DENY by default */
+ if (aclpb->aclpb_num_allow_handles <= 0) {
+ result_reason->deciding_aci = NULL;
+ result_reason->reason = ACL_REASON_NO_MATCHED_RESOURCE_ALLOWS;
+
+ TNF_PROBE_1_DEBUG(acl__TestRights_end,"ACL","",
+ tnf_string,no_allows,"");
+
+ return ACL_RES_DENY;
+ }
+
+ /* Get the ACL evaluation Context */
+ acleval = aclpb->aclpb_acleval;
+
+ testRights[0] = *right;
+ testRights[1] = '\0';
+
+ /*
+ ** START PROCESSING DENY HANDLES
+ ** Process each handle at a time. Do not concatenate the handles or else
+ ** all the context information will be build again and we will pay a
+ ** lot of penalty. The context is built the first time the handle is
+ ** processed.
+ **
+ ** First we set the default to INVALID so that if rules are not matched, then
+ ** we get INVALID and if a rule matched, the we get DENY.
+ */
+ aclpb->aclpb_state &= ~ACLPB_EXECUTING_ALLOW_HANDLES;
+ aclpb->aclpb_state |= ACLPB_EXECUTING_DENY_HANDLES;
+ ACL_SetDefaultResult (NULL, acleval, ACL_RES_INVALID);
+
+ numHandles = ACI_MAX_ELEVEL + aclpb->aclpb_num_deny_handles;
+ for (i=0, k=0; i < numHandles && k < aclpb->aclpb_num_deny_handles ; ++i) {
+ int skip_eval = 0;
+
+ /*
+ ** If the handle has been evaluated before, we can
+ ** cache the result.
+ */
+ if (((aci = aclpb->aclpb_deny_handles[i]) == NULL) && (i <= ACI_MAX_ELEVEL))
+ continue;
+ k++;
+ index = aci->aci_index;
+ slapi_log_error(SLAPI_LOG_ACL, plugin_name,
+ "Evaluating DENY aci(%d) \"%s\"\n", index, aci->aclName);
+
+ if (access & ( SLAPI_ACL_SEARCH | SLAPI_ACL_READ)) {
+
+ /*
+ * aclpb->aclpb_cache_result[0..aclpb->aclpb_last_cache_result] is
+ * a cache of info about whether applicable acis
+ * allowed, did_not_allow or denied access
+ */
+ for (j =0; j < aclpb->aclpb_last_cache_result; j++) {
+ if (index == aclpb->aclpb_cache_result[j].aci_index) {
+ short result;
+
+ result = aclpb->aclpb_cache_result[j].result;
+ if ( result <= 0) break;
+ if (!ACL_CACHED_RESULT_VALID(result)) {
+ /* something is wrong. Need to evaluate */
+ aclpb->aclpb_cache_result[j].result = -1;
+ break;
+ }
+ /*
+ ** We have a valid cached result. Let's see if we
+ ** have what we need.
+ */
+ if (access & SLAPI_ACL_SEARCH) {
+ if ( result & ACLPB_CACHE_SEARCH_RES_DENY){
+ slapi_log_error(SLAPI_LOG_ACL, plugin_name,
+ "DENY:Found SEARCH DENY in cache\n",0,0,0);
+ __acl_set_aclIndex_inResult ( aclpb, access, index );
+ result_reason->deciding_aci = aci;
+ result_reason->reason = ACL_REASON_RESULT_CACHED_DENY;
+ TNF_PROBE_1_DEBUG(acl__TestRights_end,"ACL","",
+ tnf_string,cached_deny,"");
+ return ACL_RES_DENY;
+ } else if ((result & ACLPB_CACHE_SEARCH_RES_SKIP) ||
+ (result & ACLPB_CACHE_SEARCH_RES_ALLOW)) {
+ slapi_log_error(SLAPI_LOG_ACL, plugin_name,
+ "DENY:Found SEARCH SKIP in cache\n",0,0,0);
+ skip_eval = 1;
+ break;
+ } else {
+ break;
+ }
+ } else { /* must be READ */
+ if (result & ACLPB_CACHE_READ_RES_DENY) {
+ slapi_log_error(SLAPI_LOG_ACL, plugin_name,
+ "DENY:Found READ DENY in cache\n",0,0,0);
+ __acl_set_aclIndex_inResult ( aclpb, access, index );
+ result_reason->deciding_aci = aci;
+ result_reason->reason = ACL_REASON_RESULT_CACHED_DENY;
+ TNF_PROBE_1_DEBUG(acl__TestRights_end,"ACL","",
+ tnf_string,cached_deny,"");
+ return ACL_RES_DENY;
+ } else if ( result & ACLPB_CACHE_READ_RES_SKIP) {
+ slapi_log_error(SLAPI_LOG_ACL, plugin_name,
+ "DENY:Found READ SKIP in cache\n",0,0,0);
+ skip_eval = 1;
+ break;
+ } else {
+ break;
+ }
+ }
+ }
+ }
+ }
+ if (skip_eval) {
+ skip_eval = 0;
+ continue;
+ }
+
+ rv = ACL_EvalSetACL(NULL, acleval, aci->aci_handle);
+ if ( rv < 0) {
+ slapi_log_error(SLAPI_LOG_ACL, plugin_name,
+ "acl__TestRights:Unable to set the DENY acllist\n",
+ 0,0,0);
+ continue;
+ }
+ /*
+ ** Now we have all the information we need. We need to call
+ ** the ONE ACL to test the rights.
+ ** return value: ACL_RES_DENY, ACL_RES_ALLOW, error codes
+ */
+ aclpb->aclpb_curr_aci = aci;
+ rights_rv = ACL_EvalTestRights (NULL, acleval, testRights,
+ map_generic, &deny,
+ &deny_generic,
+ &acl_tag, &expr_num);
+
+ slapi_log_error( SLAPI_LOG_ACL, plugin_name, "Processed:%d DENY handles Result:%d\n",index, rights_rv,0);
+
+ if (rights_rv == ACL_RES_FAIL) {
+ result_reason->deciding_aci = aci;
+ result_reason->reason = ACL_REASON_NONE;
+ TNF_PROBE_1_DEBUG(acl__TestRights_end,"ACL","",
+ tnf_string,evaled_deny,"");
+ return ACL_RES_DENY;
+ }
+
+ /* have we executed an ATTR RULE */
+ if ( aci->aci_ruleType & ACI_ATTR_RULES )
+ aclpb->aclpb_state |= ACLPB_ATTR_RULE_EVALUATED;
+
+ if (access & (SLAPI_ACL_SEARCH | SLAPI_ACL_READ)) {
+
+ for (j=0; j <aclpb->aclpb_last_cache_result; ++j) {
+ if (index == aclpb->aclpb_cache_result[j].aci_index) {
+ break;
+ }
+ }
+
+ if ( j < aclpb->aclpb_last_cache_result) {
+ /* already in cache */
+ } else if ( j < ACLPB_MAX_CACHE_RESULTS ) {
+ /* j == aclpb->aclpb_last_cache_result &&
+ j < ACLPB_MAX_CACHE_RESULTS */
+ aclpb->aclpb_last_cache_result++;
+ aclpb->aclpb_cache_result[j].aci_index = index;
+ aclpb->aclpb_cache_result[j].aci_ruleType = aci->aci_ruleType;
+
+ } else { /* cache overflow */
+ if ( rights_rv == ACL_RES_DENY) {
+ result_reason->deciding_aci = aci;
+ result_reason->reason = ACL_REASON_EVALUATED_DENY;
+ TNF_PROBE_1_DEBUG(acl__TestRights_end,"ACL","",
+ tnf_string,evaled_deny,"");
+ return ACL_RES_DENY;
+ } else {
+ continue;
+ }
+ }
+
+ __acl_set_aclIndex_inResult ( aclpb, access, index );
+ if (rights_rv == ACL_RES_DENY) {
+ if (access & SLAPI_ACL_SEARCH) {
+ aclpb->aclpb_cache_result[j].result |=
+ ACLPB_CACHE_SEARCH_RES_DENY;
+ } else { /* MUST BE READ */
+ aclpb->aclpb_cache_result[j].result |=
+ ACLPB_CACHE_READ_RES_DENY;
+ }
+ /* We are done -- return */
+ result_reason->deciding_aci = aci;
+ result_reason->reason = ACL_REASON_EVALUATED_DENY;
+ TNF_PROBE_1_DEBUG(acl__TestRights_end,"ACL","",
+ tnf_string,evaled_deny,"");
+ return ACL_RES_DENY;
+ } else if (rights_rv == ACL_RES_ALLOW) {
+ /* This will happen, of we have an acl with both deny and allow
+ ** Since we may not have finished all the deny acl, go thru all
+ ** of them. We will use this cached result when we evaluate this
+ ** handle in the context of allow handles.
+ */
+ if (access & SLAPI_ACL_SEARCH) {
+ aclpb->aclpb_cache_result[j].result |=
+ ACLPB_CACHE_SEARCH_RES_ALLOW;
+ } else {
+ aclpb->aclpb_cache_result[j].result |=
+ ACLPB_CACHE_READ_RES_ALLOW;
+ }
+
+ } else {
+ if (access & SLAPI_ACL_SEARCH) {
+ aclpb->aclpb_cache_result[j].result |=
+ ACLPB_CACHE_SEARCH_RES_SKIP;
+ } else {
+ aclpb->aclpb_cache_result[j].result |=
+ ACLPB_CACHE_READ_RES_SKIP;
+ }
+ continue;
+ }
+ } else {
+ if ( rights_rv == ACL_RES_DENY ) {
+ result_reason->deciding_aci = aci;
+ result_reason->reason = ACL_REASON_EVALUATED_DENY;
+ TNF_PROBE_1_DEBUG(acl__TestRights_end,"ACL","",
+ tnf_string,evaled_deny,"");
+ return ACL_RES_DENY;
+ }
+ }
+ }
+
+
+ /*
+ ** START PROCESSING ALLOW HANDLES.
+ ** Process each handle at a time. Do not concatenate the handles or else
+ ** all the context information will be build again and we will pay a
+ ** lot of penalty. The context is built the first time the handle is
+ ** processed.
+ **
+ ** First we set the default to INVALID so that if rules are not matched, then
+ ** we get INVALID and if a rule matched, the we get ALLOW.
+ */
+ aclpb->aclpb_state &= ~ACLPB_EXECUTING_DENY_HANDLES;
+ aclpb->aclpb_state |= ACLPB_EXECUTING_ALLOW_HANDLES;
+ ACL_SetDefaultResult (NULL, acleval, ACL_RES_INVALID);
+ numHandles = ACI_MAX_ELEVEL + aclpb->aclpb_num_allow_handles;
+ for (i=0, k=0; i < numHandles && k < aclpb->aclpb_num_allow_handles ; ++i) {
+ int skip_eval = 0;
+ /*
+ ** If the handle has been evaluated before, we can
+ ** cache the result.
+ */
+ aci = aclpb->aclpb_allow_handles[i];
+ if (((aci = aclpb->aclpb_allow_handles[i]) == NULL) && (i <= ACI_MAX_ELEVEL))
+ continue;
+ k++;
+ index = aci->aci_index;
+ slapi_log_error(SLAPI_LOG_ACL, plugin_name,
+ "%d. Evaluating ALLOW aci(%d) \"%s\"\n", k, index, aci->aclName);
+
+ if (access & ( SLAPI_ACL_SEARCH | SLAPI_ACL_READ)) {
+
+ /*
+ * aclpb->aclpb_cache_result[0..aclpb->aclpb_last_cache_result] is
+ * a cache of info about whether applicable acis
+ * allowed, did_not_allow or denied access
+ */
+
+ for (j =0; j < aclpb->aclpb_last_cache_result; j++) {
+ if (index == aclpb->aclpb_cache_result[j].aci_index) {
+ short result;
+ result = aclpb->aclpb_cache_result[j].result;
+ if ( result <= 0) break;
+
+ if (!ACL_CACHED_RESULT_VALID(result)) {
+ /* something is wrong. Need to evaluate */
+ aclpb->aclpb_cache_result[j].result = -1;
+ break;
+ }
+
+ /*
+ ** We have a valid cached result. Let's see if we
+ ** have what we need.
+ */
+ if (access & SLAPI_ACL_SEARCH) {
+ if (result & ACLPB_CACHE_SEARCH_RES_ALLOW) {
+ slapi_log_error(SLAPI_LOG_ACL, plugin_name,
+ "Found SEARCH ALLOW in cache\n");
+ __acl_set_aclIndex_inResult ( aclpb, access, index );
+ result_reason->deciding_aci = aci;
+ result_reason->reason = ACL_REASON_RESULT_CACHED_ALLOW;
+ TNF_PROBE_1_DEBUG(acl__TestRights_end,"ACL","",
+ tnf_string,cached_allow,"");
+ return ACL_RES_ALLOW;
+ } else if ( result & ACLPB_CACHE_SEARCH_RES_SKIP) {
+ slapi_log_error(SLAPI_LOG_ACL, plugin_name,
+ "Found SEARCH SKIP in cache\n",0,0,0);
+ skip_eval = 1;
+ break;
+ } else {
+ /* need to evaluate */
+ break;
+ }
+ } else {
+ if ( result & ACLPB_CACHE_READ_RES_ALLOW) {
+ slapi_log_error(SLAPI_LOG_ACL, plugin_name,
+ "Found READ ALLOW in cache\n",0,0,0);
+ __acl_set_aclIndex_inResult ( aclpb, access, index );
+ result_reason->deciding_aci = aci;
+ result_reason->reason = ACL_REASON_RESULT_CACHED_ALLOW;
+ TNF_PROBE_1_DEBUG(acl__TestRights_end,"ACL","",
+ tnf_string,cached_allow,"");
+ return ACL_RES_ALLOW;
+ } else if ( result & ACLPB_CACHE_READ_RES_SKIP) {
+ slapi_log_error(SLAPI_LOG_ACL, plugin_name,
+ "Found READ SKIP in cache\n",0,0,0);
+ skip_eval = 1;
+ break;
+ } else {
+ break;
+ }
+ }
+ }
+ }
+ }
+ if ( skip_eval) {
+ skip_eval = 0;
+ continue;
+ }
+
+ TNF_PROBE_0_DEBUG(acl__libaccess_start,"ACL","");
+ rv = ACL_EvalSetACL(NULL, acleval, aci->aci_handle);
+ if ( rv < 0) {
+ slapi_log_error(SLAPI_LOG_FATAL, plugin_name,
+ "acl__TestRights:Unable to set the acllist\n",
+ 0,0,0);
+ continue;
+ }
+ /*
+ ** Now we have all the information we need. We need to call
+ ** the ONE ACL to test the rights.
+ ** return value: ACL_RES_DENY, ACL_RES_ALLOW, error codes
+ */
+ aclpb->aclpb_curr_aci = aci;
+ rights_rv = ACL_EvalTestRights (NULL, acleval, testRights,
+ map_generic, &deny,
+ &deny_generic,
+ &acl_tag, &expr_num);
+ TNF_PROBE_0_DEBUG(acl__libaccess_end,"ACL","");
+
+ if (aci->aci_ruleType & ACI_ATTR_RULES)
+ aclpb->aclpb_state |= ACLPB_ATTR_RULE_EVALUATED;
+
+ if (access & (SLAPI_ACL_SEARCH | SLAPI_ACL_READ)) {
+
+ for (j=0; j <aclpb->aclpb_last_cache_result; ++j) {
+ if (index == aclpb->aclpb_cache_result[j].aci_index) {
+ break;
+ }
+ }
+
+ if ( j < aclpb->aclpb_last_cache_result) {
+ /* already in cache */
+ } else if ( j < ACLPB_MAX_CACHE_RESULTS ) {
+ /* j == aclpb->aclpb_last_cache_result &&
+ j < ACLPB_MAX_CACHE_RESULTS */
+ aclpb->aclpb_last_cache_result++;
+ aclpb->aclpb_cache_result[j].aci_index = index;
+ aclpb->aclpb_cache_result[j].aci_ruleType = aci->aci_ruleType;
+ } else { /* cache overflow */
+ slapi_log_error (SLAPI_LOG_FATAL, "acl__TestRights", "cache overflown\n");
+ if ( rights_rv == ACL_RES_ALLOW) {
+ result_reason->deciding_aci = aci;
+ result_reason->reason = ACL_REASON_EVALUATED_ALLOW;
+ TNF_PROBE_1_DEBUG(acl__TestRights_end,"ACL","",
+ tnf_string,evaled_allow,"");
+ return ACL_RES_ALLOW;
+ } else {
+ continue;
+ }
+ }
+
+ __acl_set_aclIndex_inResult ( aclpb, access, index );
+ if (rights_rv == ACL_RES_ALLOW) {
+ if (access & SLAPI_ACL_SEARCH) {
+ aclpb->aclpb_cache_result[j].result |=
+ ACLPB_CACHE_SEARCH_RES_ALLOW;
+ } else { /* must be READ */
+ aclpb->aclpb_cache_result[j].result |=
+ ACLPB_CACHE_READ_RES_ALLOW;
+ }
+
+ /* We are done -- return */
+ result_reason->deciding_aci = aci;
+ result_reason->reason = ACL_REASON_EVALUATED_ALLOW;
+ TNF_PROBE_1_DEBUG(acl__TestRights_end,"ACL","",
+ tnf_string,evaled_allow,"");
+ return ACL_RES_ALLOW;
+
+ } else {
+ if (access & SLAPI_ACL_SEARCH) {
+ aclpb->aclpb_cache_result[j].result |=
+ ACLPB_CACHE_SEARCH_RES_SKIP;
+ } else {
+ aclpb->aclpb_cache_result[j].result |=
+ ACLPB_CACHE_READ_RES_SKIP;
+ }
+ continue;
+ }
+ } else {
+ if ( rights_rv == ACL_RES_ALLOW ) {
+ result_reason->deciding_aci = aci;
+ result_reason->reason = ACL_REASON_EVALUATED_ALLOW;
+ TNF_PROBE_1_DEBUG(acl__TestRights_end,"ACL","",
+ tnf_string,evaled_allow,"");
+ return ACL_RES_ALLOW;
+ }
+ }
+ }/* for */
+ result_reason->deciding_aci = aci;
+ result_reason->reason = ACL_REASON_NO_MATCHED_SUBJECT_ALLOWS;
+
+ TNF_PROBE_0_DEBUG(acl__TestRights_end,"ACL","");
+
+ return (ACL_RES_DENY);
+}
+/***************************************************************************
+*
+* acl_match_substring
+*
+* Compare the input string to the patteren in the filter
+*
+* Input:
+* struct slapi_filter *f - Filter which has the patteren
+* char *str - String to compare
+* int exact_match - 1; match the pattern exactly
+* - 0; match the pattern as a suffix
+*
+* Returns:
+* ACL_TRUE - The sting matches with the patteren
+* ACL_FALSE - No it doesn't
+* ACL_ERR - Error
+*
+* Error Handling:
+* None.
+*
+**************************************************************************/
+int
+acl_match_substring ( Slapi_Filter *f, char *str, int exact_match)
+{
+ int i, rc, len;
+ char *p;
+ char *end, *realval, *tmp;
+ char pat[BUFSIZ];
+ char buf[BUFSIZ];
+ char *type, *initial, *final;
+ char **any;
+
+ if ( 0 != slapi_filter_get_subfilt ( f, &type, &initial, &any, &final ) ) {
+ return (ACL_FALSE);
+ }
+
+ /* convert the input to lower. */
+ for (p = str; *p; p++)
+ *p = TOLOWER ( *p );
+
+ /* construct a regular expression corresponding to the filter: */
+ pat[0] = '\0';
+ p = pat;
+ end = pat + sizeof(pat) - 2; /* leave room for null */
+
+
+ if ( initial != NULL) {
+ strcpy (p, "^");
+ p = strchr (p, '\0');
+
+ /* 2 * in case every char is special */
+ if (p + 2 * strlen ( initial ) > end) {
+ slapi_log_error (SLAPI_LOG_ACL, plugin_name,
+ "not enough pattern space\n", 0, 0, 0);
+
+ return (ACL_ERR);
+ }
+
+ if (!exact_match) {
+ strcpy (p, ".*");
+ p = strchr (p, '\0');
+ }
+ acl_strcpy_special (p, initial);
+ p = strchr (p, '\0');
+ }
+
+ if ( any != NULL) {
+ for (i = 0; any && any[i] != NULL; i++) {
+ /* ".*" + value */
+ if (p + 2 * strlen ( any[i]) + 2 > end) {
+ slapi_log_error (SLAPI_LOG_ACL, plugin_name,
+ "not enough pattern space\n", 0, 0, 0);
+ return (ACL_ERR);
+ }
+
+ strcpy (p, ".*");
+ p = strchr (p, '\0');
+ acl_strcpy_special (p, any[i]);
+ p = strchr (p, '\0');
+ }
+ }
+
+
+ if ( final != NULL) {
+ /* ".*" + value */
+ if (p + 2 * strlen ( final ) + 2 > end) {
+ slapi_log_error (SLAPI_LOG_ACL, plugin_name,
+ "not enough pattern space\n", 0, 0, 0);
+ return (ACL_ERR);
+ }
+
+ strcpy (p, ".*");
+ p = strchr (p, '\0');
+ acl_strcpy_special (p, final);
+ p = strchr (p, '\0');
+ strcpy (p, "$");
+ }
+
+ /* see if regex matches with the input string */
+ tmp = NULL;
+ len = strlen(str);
+
+ if (len < sizeof(buf)) {
+ strcpy (buf, str);
+ realval = buf;
+ } else {
+ tmp = (char*) slapi_ch_malloc (len + 1);
+ strcpy (tmp, str);
+ realval = tmp;
+ }
+
+ slapi_dn_normalize (realval);
+
+
+ /* What we have built is a regular pattaren expression.
+ ** Now we will compile the pattern and compare wth the string to
+ ** see if the input string matches with the patteren or not.
+ */
+ slapd_re_lock();
+ if ((p = slapd_re_comp (pat)) != 0) {
+ slapi_log_error (SLAPI_LOG_ACL, plugin_name,
+ "acl_match_substring:re_comp failed (%s)\n", p, 0, 0);
+ slapd_re_unlock();
+ return (ACL_ERR);
+ }
+
+ /* re_exec() returns 1 if the string p1 matches the last compiled
+ ** regular expression, 0 if the string p1 failed to match
+ ** (see man pages)
+ **
+ ** IMPORTANT NOTE: If I use compile() and step() to do the patteren
+ ** matching, it seems that step() is leaking 1036 bytes/search
+ ** I couldn't figure out why it's leaking.
+ */
+ rc = slapd_re_exec( realval );
+
+ slapd_re_unlock();
+
+ if (tmp != NULL) {
+ slapi_ch_free ( (void **) &tmp );
+ }
+
+ if (rc == 1) {
+ return ACL_TRUE;
+ } else {
+ return ACL_FALSE;
+ }
+}
+/***************************************************************************
+*
+* acl__reset_cached_result
+*
+* Go thru the cached result and invlalidate the cached evaluation results for
+* rules which can only be cached based on the scope.
+* If we have scope ACI_CACHE_RESULT_PER_ENTRY, then we need to invalidate the
+* cacched reult whenever we hit a new entry.
+*
+* Returns:
+* Null
+*
+**************************************************************************/
+static void
+acl__reset_cached_result (struct acl_pblock *aclpb )
+{
+
+ int j;
+
+ for (j =0; j < aclpb->aclpb_last_cache_result; j++) {
+ /* Unless we have to clear the result, move on */
+ if (!( aclpb->aclpb_cache_result[j].aci_ruleType & ACI_CACHE_RESULT_PER_ENTRY))
+ continue;
+ aclpb->aclpb_cache_result[j].result = 0;
+ }
+}
+/*
+ * acl_access_allowed_disjoint_resource
+ *
+ * This is an internal module which can be used to verify
+ * access to a resource which may not be inside the search scope.
+ *
+ * Returns:
+ * - Same return val as acl_access_allowed().
+ */
+int
+acl_access_allowed_disjoint_resource(
+ Slapi_PBlock *pb,
+ Slapi_Entry *e, /* The Slapi_Entry */
+ char *attr, /* Attribute of the entry */
+ struct berval *val, /* value of attr. NOT USED */
+ int access /* access rights */
+ )
+{
+
+ int rv;
+ struct acl_pblock *aclpb = NULL;
+
+ aclpb = acl_get_aclpb ( pb, ACLPB_BINDDN_PBLOCK );
+ /*
+ ** It's possible that we already have a context of ACLs.
+ ** However once in a while, we need to test
+ ** access to a resource which (like vlv, schema) which falls
+ ** outside the search base. In that case, we need to go
+ ** thru all the ACLs and not depend upon the acls which we have
+ ** gathered.
+ */
+
+ /* If you have the right to use the resource, then we don't need to check for
+ ** proxy right on that resource.
+ */
+ if (aclpb)
+ aclpb->aclpb_state |= ( ACLPB_DONOT_USE_CONTEXT_ACLS| ACLPB_DONOT_EVALUATE_PROXY );
+
+ rv = acl_access_allowed(pb, e, attr, val, access);
+
+ if (aclpb) aclpb->aclpb_state &= ~ACLPB_DONOT_USE_CONTEXT_ACLS;
+ if (aclpb ) aclpb->aclpb_state &= ~ACLPB_DONOT_EVALUATE_PROXY;
+
+ return rv;
+}
+
+/*
+ * acl__attr_cached_result
+ * Loops thru the cached result and determines if we can use the cached value.
+ *
+ * Inputs:
+ * Slapi_pblock *aclpb - acl private block
+ * char *attr - attribute name
+ * int access - access type
+ * Returns:
+ * LDAP_SUCCESS: - access is granted
+ * LDAP_INSUFFICIENT_ACCESS - access denied
+ * ACL_ERR - no cached info about this attr.
+ * - or the attr had multiple result and so
+ * - we can't determine the outcome.
+ *
+ */
+static int
+acl__attr_cached_result (struct acl_pblock *aclpb, char *attr, int access )
+{
+
+ int i, rc;
+ aclEvalContext *c_evalContext;
+
+ if ( !(access & ( SLAPI_ACL_SEARCH | SLAPI_ACL_READ) ))
+ return ACL_ERR;
+
+ if (aclpb->aclpb_state & ACLPB_HAS_ACLCB_EVALCONTEXT ) {
+ c_evalContext = &aclpb->aclpb_prev_opEval_context;
+ slapi_log_error ( SLAPI_LOG_ACL, plugin_name,
+ "acl__attr_cached_result:Using Context: ACLPB_ACLCB\n", 0,0,0 );
+ } else {
+ c_evalContext = &aclpb->aclpb_prev_entryEval_context;
+ slapi_log_error ( SLAPI_LOG_ACL, plugin_name,
+ "acl__attr_cached_result:Using Context: ACLPB_PREV\n", 0,0,0 );
+ }
+
+ if ( attr == NULL ) {
+ int eval_read = 0;
+ /*
+ ** Do I have access to at least one attribute, then I have
+ ** access to the entry.
+ */
+ for (i=0; i < c_evalContext->acle_numof_attrs; i++ ) {
+ AclAttrEval *a_eval = &c_evalContext->acle_attrEval[i];
+
+ if ( (access & SLAPI_ACL_READ ) && a_eval->attrEval_r_status &&
+ a_eval->attrEval_r_status < ACL_ATTREVAL_DETERMINISTIC ) {
+ eval_read++;
+ if ( a_eval->attrEval_r_status & ACL_ATTREVAL_SUCCESS)
+ return LDAP_SUCCESS;
+ /* rbyrne: recompute if we have to.
+ * How does this cached result get turned off for
+ * attr style acis which acannot be cached becuase entry
+ * can result in a diff value.
+ */
+ else if ( a_eval->attrEval_r_status & ACL_ATTREVAL_RECOMPUTE ) {
+ rc = acl__recompute_acl ( aclpb, a_eval, access,
+ a_eval->attrEval_r_aciIndex);
+ if ( rc != ACL_ERR ) {
+ acl_copyEval_context ( aclpb, c_evalContext,
+ &aclpb->aclpb_curr_entryEval_context, 1);
+ }
+ if ( rc == LDAP_SUCCESS) {
+ return LDAP_SUCCESS;
+ }
+ }
+ }
+ }/* for */
+ /*
+ * If we have scanned the whole list without success then
+ * we are not granting access to this entry through access
+ * to an attribute in the list--however this does not mean
+ * that we do not have access to the entry via another attribute
+ * not already in the list, so return -1 meaning
+ * "don't know".
+ */
+ return(ACL_ERR);
+#if 0
+ if ( eval_read )
+ return LDAP_INSUFFICIENT_ACCESS;
+ else
+ return ACL_ERR;
+#endif
+ }
+
+ for (i=0; i < c_evalContext->acle_numof_attrs; i++ ) {
+ AclAttrEval *a_eval = &c_evalContext->acle_attrEval[i];
+
+ if ( a_eval == NULL ) continue;
+
+ if (strcasecmp ( attr, a_eval->attrEval_name ) == 0 ) {
+ if ( access & SLAPI_ACL_SEARCH ) {
+ if (a_eval->attrEval_s_status < ACL_ATTREVAL_DETERMINISTIC ) {
+ if ( a_eval->attrEval_s_status & ACL_ATTREVAL_SUCCESS)
+ return LDAP_SUCCESS;
+ else if ( a_eval->attrEval_s_status & ACL_ATTREVAL_FAIL)
+ return LDAP_INSUFFICIENT_ACCESS;
+ else if ( a_eval->attrEval_s_status & ACL_ATTREVAL_RECOMPUTE ) {
+ rc = acl__recompute_acl ( aclpb, a_eval, access,
+ a_eval->attrEval_s_aciIndex);
+ if ( rc != ACL_ERR ) {
+ acl_copyEval_context ( aclpb, c_evalContext,
+ &aclpb->aclpb_curr_entryEval_context, 1);
+ }
+ } else
+ return ACL_ERR;
+ } else {
+ /* This means that for the same attribute and same type of
+ ** access, we had different results at different time.
+ ** Since we are not caching per object, we can't
+ ** determine exactly. So, can't touch this
+ */
+ return ACL_ERR;
+ }
+ } else {
+ if (a_eval->attrEval_r_status < ACL_ATTREVAL_DETERMINISTIC ) {
+ if ( a_eval->attrEval_r_status & ACL_ATTREVAL_SUCCESS)
+ return LDAP_SUCCESS;
+ else if ( a_eval->attrEval_r_status & ACL_ATTREVAL_FAIL)
+ return LDAP_INSUFFICIENT_ACCESS;
+ else if ( a_eval->attrEval_r_status & ACL_ATTREVAL_RECOMPUTE ) {
+ rc = acl__recompute_acl ( aclpb, a_eval, access,
+ a_eval->attrEval_r_aciIndex);
+ if ( rc != ACL_ERR ) {
+ acl_copyEval_context ( aclpb, c_evalContext,
+ &aclpb->aclpb_curr_entryEval_context, 1);
+ }
+ } else
+ return ACL_ERR;
+ } else {
+ /* Look above for explanation */
+ return ACL_ERR;
+ }
+ }
+ }
+ }
+ return ACL_ERR;
+}
+
+/*
+ * Had to do this juggling of casting to make
+ * both Nt & unix compiler happy.
+ */
+static int
+acl__cmp(const void *a, const void *b)
+{
+ short *i = (short *) a;
+ short *j = (short *) b;
+
+ if ( (short) *i > (short) *j )
+ return (1);
+ if ( (short)*i < (short) *j)
+ return (-1);
+ return (0);
+}
+
+/*
+ * acl__scan_match_handles
+ * Go thru the ACL list and determine if the list of acls selected matches
+ * what we have in the cache.
+ *
+ * Inputs:
+ * Acl_PBlock *pb - Main pblock ( blacvk hole)
+ * int type - Which context to look on
+ *
+ * Returns:
+ * 0 - matches all the acl handles
+ * ACL_ERR - sorry; no match
+ *
+ * ASSUMPTION: A READER LOCK ON ACL LIST
+ */
+static int
+acl__scan_match_handles ( Acl_PBlock *aclpb, int type)
+{
+
+
+ int matched = 0;
+ aci_t *aci = NULL;
+ int index;
+ PRUint32 cookie;
+ aclEvalContext *c_evalContext = NULL;
+
+ if (type == ACLPB_EVALCONTEXT_PREV ) {
+ c_evalContext = &aclpb->aclpb_prev_entryEval_context;
+ } else if ( type == ACLPB_EVALCONTEXT_ACLCB ){
+ c_evalContext = &aclpb->aclpb_prev_opEval_context;
+ } else {
+ return ACL_ERR;
+ }
+
+
+ if ( !c_evalContext->acle_numof_tmatched_handles )
+ return ACL_ERR;
+
+ aclpb->aclpb_stat_acllist_scanned++;
+ aci = acllist_get_first_aci ( aclpb, &cookie );
+
+ while ( aci ) {
+ index = aci->aci_index;
+ if (acl__resource_match_aci(aclpb, aci, 1 /* skip attr matching */, NULL )) {
+ int j;
+ int s_matched = matched;
+
+ /* We have a sorted list of handles that matched the target */
+
+ for (j=0; j < c_evalContext->acle_numof_tmatched_handles ; j++ ) {
+ if ( c_evalContext->acle_handles_matched_target[j] > index )
+
+ break;
+ else if ( index == c_evalContext->acle_handles_matched_target[j] ) {
+ int jj;
+ matched++;
+
+ /* See if this is a ATTR rule that matched -- in that case we have
+ ** to nullify the cached result
+ */
+ if ( aci->aci_ruleType & ACI_ATTR_RULES ) {
+ slapi_log_error ( SLAPI_LOG_ACL, plugin_name,
+ "Found an attr Rule [Name:%s Index:%d\n", aci->aclName,
+ aci->aci_index );
+ for ( jj =0; jj < c_evalContext->acle_numof_attrs; jj++ ) {
+ AclAttrEval *a_eval = &c_evalContext->acle_attrEval[jj];
+ if ( a_eval->attrEval_r_aciIndex == aci->aci_index )
+ a_eval->attrEval_r_status = ACL_ATTREVAL_RECOMPUTE;
+ if ( a_eval->attrEval_s_aciIndex == aci->aci_index )
+ a_eval->attrEval_s_status = ACL_ATTREVAL_RECOMPUTE;
+ }
+ }
+ break;
+ }
+ }
+ if ( s_matched == matched ) return ACL_ERR;
+ }
+ aci = acllist_get_next_aci ( aclpb, aci, &cookie );
+ }
+ if ( matched == c_evalContext->acle_numof_tmatched_handles )
+ return 0;
+
+ return ACL_ERR;
+}
+/*
+ * acl_copyEval_context
+ * Copy the context info which include attr info and handles.
+ *
+ * Inputs
+ * struct acl_pblock *aclpb - acl private main block
+ * aclEvalContext *src - src context
+ * aclEvalContext *dest - dest context
+ * Returns:
+ * None.
+ *
+ */
+void
+acl_copyEval_context ( struct acl_pblock *aclpb, aclEvalContext *src,
+ aclEvalContext *dest , int copy_attr_only )
+{
+
+ int d_slot, i;
+
+ /* Do a CLEAN copy we have nothing or else do an incremental copy.*/
+ if ( src->acle_numof_attrs < 1 )
+ return;
+
+ /* Copy the attr info */
+ if ( dest->acle_numof_attrs < 1 )
+ acl_clean_aclEval_context ( dest, 0 /*clean */ );
+
+ d_slot = dest->acle_numof_attrs;
+ for (i=0; i < src->acle_numof_attrs; i++ ) {
+ int j;
+ int attr_exists = 0;
+ int dd_slot = d_slot;
+
+ if ( aclpb && (i == 0) ) aclpb->aclpb_stat_num_copycontext++;
+
+ if ( src->acle_attrEval[i].attrEval_r_status == 0 &&
+ src->acle_attrEval[i].attrEval_s_status == 0 )
+ continue;
+
+ for ( j = 0; j < dest->acle_numof_attrs; j++ ) {
+ if ( strcasecmp ( src->acle_attrEval[i].attrEval_name,
+ dest->acle_attrEval[j].attrEval_name ) == 0 ) {
+ /* We have it. skip it. */
+ attr_exists = 1;
+ dd_slot = j;
+ break;
+ }
+ }
+ if ( !attr_exists ) {
+ if ( dd_slot >= ACLPB_MAX_ATTRS -1 )
+ break;
+
+ if ( aclpb) aclpb->aclpb_stat_num_copy_attrs++;
+
+ if ( dest->acle_attrEval[dd_slot].attrEval_name )
+ slapi_ch_free ( (void **) &dest->acle_attrEval[dd_slot].attrEval_name );
+
+ dest->acle_attrEval[dd_slot].attrEval_name =
+ slapi_ch_strdup ( src->acle_attrEval[i].attrEval_name );
+ }
+ /* Copy the result status and the aci index */
+ dest->acle_attrEval[dd_slot].attrEval_r_status =
+ src->acle_attrEval[i].attrEval_r_status;
+ dest->acle_attrEval[dd_slot].attrEval_r_aciIndex =
+ src->acle_attrEval[i].attrEval_r_aciIndex;
+ dest->acle_attrEval[dd_slot].attrEval_s_status =
+ src->acle_attrEval[i].attrEval_s_status;
+ dest->acle_attrEval[dd_slot].attrEval_s_aciIndex =
+ src->acle_attrEval[i].attrEval_s_aciIndex;
+
+ if (!attr_exists ) d_slot++;
+ }
+
+ dest->acle_numof_attrs = d_slot;
+ dest->acle_attrEval[d_slot].attrEval_name = NULL;
+
+ if ( copy_attr_only )
+ return;
+
+ /* First sort the arrays which keeps the acl index numbers */
+ qsort ( (char *) src->acle_handles_matched_target,
+ (size_t)src->acle_numof_tmatched_handles, sizeof( int ), acl__cmp );
+
+ for (i=0; i < src->acle_numof_tmatched_handles; i++ ) {
+ dest->acle_handles_matched_target[i] =
+ src->acle_handles_matched_target[i];
+ }
+
+ if ( src->acle_numof_tmatched_handles ) {
+ dest->acle_numof_tmatched_handles = src->acle_numof_tmatched_handles;
+ if ( aclpb) aclpb->aclpb_stat_num_tmatched_acls = src->acle_numof_tmatched_handles;
+ }
+}
+
+/*
+ * acl_clean_aclEval_context
+ * Clean the eval context
+ *
+ * Inputs:
+ * aclEvalContext *clean_me - COntext to be cleaned
+ * int clean_type - 0: clean, 1 scrub
+ *
+ */
+
+void
+acl_clean_aclEval_context ( aclEvalContext *clean_me, int scrub_only )
+{
+ int i;
+
+ /* Copy the attr info */
+ for (i=0; i < clean_me->acle_numof_attrs; i++ ) {
+
+ char *a_name = clean_me->acle_attrEval[i].attrEval_name;
+ if ( a_name && !scrub_only) {
+ slapi_ch_free ( (void **) &a_name );
+ clean_me->acle_attrEval[i].attrEval_name = NULL;
+ }
+ clean_me->acle_attrEval[i].attrEval_r_status = 0;
+ clean_me->acle_attrEval[i].attrEval_s_status = 0;
+ clean_me->acle_attrEval[i].attrEval_r_aciIndex = 0;
+ clean_me->acle_attrEval[i].attrEval_s_aciIndex = 0;
+ }
+
+ if ( !scrub_only ) clean_me->acle_numof_attrs = 0;
+ clean_me->acle_numof_tmatched_handles = 0;
+}
+/*
+ * acl__match_handlesFromCache
+ *
+ * We have 2 cacheed information
+ * 1) cached info from the previous operation
+ * 2) cached info from the prev entry evaluation
+ *
+ * What we are doing here is going thru all the acls and see if the same
+ * set of acls apply to this resource or not. If it does, then do we have
+ * a cached info for the attr. If we don't for both of the cases then we need
+ * to evaluate all over again.
+ *
+ * Inputs:
+ * struct acl_pblock - ACL private block;
+ * char *attr - Attribute name
+ * int access - acces type
+ *
+ * returns:
+ * LDAP_SUCCESS (0) - The same acls apply and we have
+ * access ALLOWED on the attr
+ * LDAP_INSUFFICIENT_ACCESS - The same acls apply and we have
+ * access DENIED on the attr
+ * -1 - Acls doesn't match or we don't have
+ * cached info for this attr.
+ *
+ * ASSUMPTIONS: A reader lock has been obtained for the acl list.
+ */
+static int
+acl__match_handlesFromCache ( Acl_PBlock *aclpb, char *attr, int access)
+{
+
+ aclEvalContext *c_evalContext = NULL;
+ int context_type = 0;
+ int ret_val = -1; /* it doen't match by default */
+
+ /* Before we proceed, find out if we have evaluated any ATTR RULE. If we have
+ ** then we can't use any caching mechanism
+ */
+
+ if ( aclpb->aclpb_state & ACLPB_HAS_ACLCB_EVALCONTEXT ) {
+ context_type = ACLPB_EVALCONTEXT_ACLCB;
+ c_evalContext = &aclpb->aclpb_prev_opEval_context;
+ } else {
+ context_type = ACLPB_EVALCONTEXT_PREV;
+ c_evalContext = &aclpb->aclpb_prev_entryEval_context;
+ }
+
+
+ if ( aclpb->aclpb_res_type & (ACLPB_NEW_ENTRY | ACLPB_EFFECTIVE_RIGHTS) ) {
+ aclpb->aclpb_state |= ACLPB_MATCHES_ALL_ACLS;
+ ret_val = acl__scan_match_handles ( aclpb, context_type );
+ if ( -1 == ret_val ) {
+ aclpb->aclpb_state &= ~ACLPB_MATCHES_ALL_ACLS;
+ aclpb->aclpb_state |= ACLPB_UPD_ACLCB_CACHE;
+ /* Did not match */
+ if ( context_type == ACLPB_HAS_ACLCB_EVALCONTEXT ) {
+ aclpb->aclpb_state &= ~ACLPB_HAS_ACLCB_EVALCONTEXT;
+ } else {
+ aclpb->aclpb_state |= ACLPB_COPY_EVALCONTEXT;
+ c_evalContext->acle_numof_tmatched_handles = 0;
+ }
+ }
+ }
+ if ( aclpb->aclpb_state & ACLPB_MATCHES_ALL_ACLS ) {
+ /* See if we have a cached result for this attr */
+ ret_val = acl__attr_cached_result (aclpb, attr, access);
+
+ /* It's not in the ACLCB context but we might have it in the
+ ** current/prev context. Take a look at it. we might have evaluated
+ ** this attribute already.
+ */
+ if ( (-1 == ret_val ) &&
+ ( aclpb->aclpb_state & ACLPB_HAS_ACLCB_EVALCONTEXT )) {
+ aclpb->aclpb_state &= ~ACLPB_HAS_ACLCB_EVALCONTEXT ;
+ ret_val = acl__attr_cached_result (aclpb, attr, access);
+ aclpb->aclpb_state |= ACLPB_HAS_ACLCB_EVALCONTEXT ;
+
+ /* We need to do an incremental update */
+ if ( !ret_val ) aclpb->aclpb_state |= ACLPB_INCR_ACLCB_CACHE;
+ }
+ }
+ return ret_val;
+}
+/*
+ * acl__get_attrEval
+ * Get the atteval from the current context and hold the ptr in aclpb.
+ * If we have too many attrs, then allocate a new one. In that case
+ * we let the caller know about that so that it will be deallocated.
+ *
+ * Returns:
+ * int - 0: The context was indexed. So, no allocations.
+ * - 1; context was allocated - deallocate it.
+ */
+static int
+acl__get_attrEval ( struct acl_pblock *aclpb, char *attr )
+{
+
+ int j;
+ aclEvalContext *c_ContextEval = &aclpb->aclpb_curr_entryEval_context;
+ int deallocate_attrEval = 0;
+ AclAttrEval *c_attrEval = NULL;
+
+ if ( !attr ) return deallocate_attrEval;
+
+ aclpb->aclpb_curr_attrEval = NULL;
+
+ /* Go thru and see if we have the attr already */
+ for (j=0; j < c_ContextEval->acle_numof_attrs; j++) {
+ c_attrEval = &c_ContextEval->acle_attrEval[j];
+
+ if ( c_attrEval &&
+ slapi_attr_type_cmp ( c_attrEval->attrEval_name, attr, 1) == 0 ) {
+ aclpb->aclpb_curr_attrEval = c_attrEval;
+ break;
+ }
+ }
+
+ if ( !aclpb->aclpb_curr_attrEval) {
+ if ( c_ContextEval->acle_numof_attrs == ACLPB_MAX_ATTRS -1 ) {
+ /* Too many attrs. create a temp one */
+ c_attrEval = (AclAttrEval * ) slapi_ch_calloc ( 1, sizeof ( AclAttrEval ) );
+ deallocate_attrEval =1;
+ } else {
+ c_attrEval = &c_ContextEval->acle_attrEval[c_ContextEval->acle_numof_attrs++];
+ c_attrEval->attrEval_r_status = 0;
+ c_attrEval->attrEval_s_status = 0;
+ c_attrEval->attrEval_r_aciIndex = 0;
+ c_attrEval->attrEval_s_aciIndex = 0;
+ }
+ /* clean it before use */
+ c_attrEval->attrEval_name = slapi_ch_strdup ( attr );
+ aclpb->aclpb_curr_attrEval = c_attrEval;
+ }
+ return deallocate_attrEval;
+}
+/*
+ * acl_skip_access_check
+ *
+ * See if we need to go thru the ACL check or not. We don't need to if I am root
+ * or internal operation or ...
+ *
+ * returns:
+ * ACL_TRUE - Yes; skip the ACL check
+ * ACL_FALSE - No; you have to go thru ACL check
+ *
+ */
+int
+acl_skip_access_check ( Slapi_PBlock *pb, Slapi_Entry *e )
+{
+ int rv, isRoot, accessCheckDisabled;
+ void *conn = NULL;
+ Slapi_Backend *be;
+
+ slapi_pblock_get ( pb, SLAPI_REQUESTOR_ISROOT, &isRoot );
+ if ( isRoot ) return ACL_TRUE;
+
+ /* See if this is local request */
+ slapi_pblock_get ( pb, SLAPI_CONNECTION, &conn);
+
+ if ( NULL == conn ) return ACL_TRUE;
+
+ /*
+ * Turn on access checking in the rootdse--this code used
+ * to skip the access check.
+ *
+ * check if the entry is the RootDSE entry
+ if ( e ) {
+ char * edn = slapi_entry_get_ndn ( e );
+ if ( slapi_is_rootdse ( edn ) ) return ACL_TRUE;
+ }
+ */
+
+ /* GB : when referrals are directly set in the mappin tree
+ * we can reach this code without a backend in the pblock
+ * in such a case, allow access for now
+ * we may want to reconsider this is NULL DSE implementation happens
+ */
+ rv = slapi_pblock_get ( pb, SLAPI_BACKEND, &be );
+ if (be == NULL)
+ return ACL_TRUE;
+
+ rv = slapi_pblock_get ( pb, SLAPI_PLUGIN_DB_NO_ACL, &accessCheckDisabled );
+ if ( rv != -1 && accessCheckDisabled ) return ACL_TRUE;
+
+ return ACL_FALSE;
+}
+short
+acl_get_aclsignature ()
+{
+ return acl_signature;
+}
+void
+acl_set_aclsignature ( short value)
+{
+ acl_signature = value;
+}
+void
+acl_regen_aclsignature ()
+{
+ acl_signature = aclutil_gen_signature ( acl_signature );
+}
+
+
+
+static int
+acl__handle_config_entry (Slapi_Entry *e, void *callback_data )
+{
+
+ int *value = (int *) callback_data;
+
+ *value = slapi_entry_attr_get_int( e, "nsslapd-readonly");
+
+ return 0;
+}
+
+static int
+acl__config_get_readonly ()
+{
+
+ int readonly = 0;
+
+ slapi_search_internal_callback( "cn=config", LDAP_SCOPE_BASE, "(objectclass=*)",
+ NULL, 0 /* attrsonly */,
+ &readonly/* callback_data */,
+ NULL /* controls */,
+ NULL /* result_callback */,
+ acl__handle_config_entry,
+ NULL /* referral_callback */);
+
+ return readonly;
+}
+/*
+*
+* Assumptions:
+* 1) Called for read/search right.
+*/
+static int
+acl__recompute_acl ( Acl_PBlock *aclpb,
+ AclAttrEval *a_eval,
+ int access,
+ int aciIndex
+ )
+{
+
+
+ char *unused_str1, *unused_str2;
+ char *acl_tag, *testRight[2];
+ int j, expr_num;
+ int result_status, rv, cache_result;
+ PRUint32 cookie;
+ aci_t *aci;
+
+
+ PR_ASSERT ( aciIndex >= 0 );
+ PR_ASSERT ( a_eval != NULL );
+ PR_ASSERT (aclpb != NULL );
+
+
+ /* We might have evaluated this acl just now, check it there first */
+
+ for ( j =0; j < aclpb->aclpb_last_cache_result; j++) {
+ if (aciIndex == aclpb->aclpb_cache_result[j].aci_index) {
+ short result;
+ result_status =ACL_RES_INVALID;
+
+ result = aclpb->aclpb_cache_result[j].result;
+ if ( result <= 0) break;
+ if (!ACL_CACHED_RESULT_VALID(result)) {
+ /* something is wrong. Need to evaluate */
+ aclpb->aclpb_cache_result[j].result = -1;
+ break;
+ }
+
+
+ /*
+ ** We have a valid cached result. Let's see if we
+ ** have what we need.
+ */
+ if ((result & ACLPB_CACHE_SEARCH_RES_ALLOW) ||
+ (result & ACLPB_CACHE_READ_RES_ALLOW) )
+ result_status = ACL_RES_ALLOW;
+ else if ((result & ACLPB_CACHE_SEARCH_RES_DENY) ||
+ (result & ACLPB_CACHE_READ_RES_DENY) )
+ result_status = ACL_RES_DENY;
+
+ }
+ } /* end of for */
+
+ if ( result_status != ACL_RES_INVALID ) {
+ goto set_result_status;
+ }
+
+ slapi_log_error ( SLAPI_LOG_ACL, plugin_name,
+ "Recomputing the ACL Index:%d for entry:%s\n",
+ aciIndex, slapi_entry_get_ndn ( aclpb->aclpb_curr_entry) );
+
+ /* First find this one ACL and then evaluate it. */
+
+
+ aci = acllist_get_first_aci ( aclpb, &cookie );
+ while ( aci && aci->aci_index != aciIndex ) {
+ aci = acllist_get_next_aci ( aclpb, aci, &cookie );
+ }
+
+ if (NULL == aci)
+ return -1;
+
+
+ ACL_SetDefaultResult (NULL, aclpb->aclpb_acleval, ACL_RES_INVALID);
+ rv = ACL_EvalSetACL(NULL, aclpb->aclpb_acleval, aci->aci_handle);
+
+ testRight[0] = acl_access2str ( access );
+ testRight[1] = '\0';
+ aclpb->aclpb_curr_aci = aci;
+ result_status = ACL_EvalTestRights (NULL, aclpb->aclpb_acleval, testRight,
+ ds_map_generic, &unused_str1,
+ &unused_str2,
+ &acl_tag, &expr_num);
+
+ cache_result = 0;
+ if ( result_status == ACL_RES_DENY && aci->aci_type & ACI_HAS_DENY_RULE ) {
+ if ( access & SLAPI_ACL_SEARCH)
+ cache_result = ACLPB_CACHE_SEARCH_RES_DENY;
+ else
+ cache_result = ACLPB_CACHE_READ_RES_DENY;
+ } else if ( result_status == ACL_RES_ALLOW && aci->aci_type & ACI_HAS_ALLOW_RULE ) {
+ if ( access & SLAPI_ACL_SEARCH)
+ cache_result = ACLPB_CACHE_SEARCH_RES_ALLOW;
+ else
+ cache_result = ACLPB_CACHE_READ_RES_ALLOW;
+
+ } else {
+ result_status = -1;
+ }
+
+ /* Now we need to put the cached result in the aclpb */
+
+ for (j=0; j <aclpb->aclpb_last_cache_result; ++j) {
+ if (aciIndex == aclpb->aclpb_cache_result[j].aci_index) {
+ break;
+ }
+ }
+
+ if ( j < aclpb->aclpb_last_cache_result) {
+ /* already in cache */
+ } else if ( j < ACLPB_MAX_CACHE_RESULTS-1) {
+ /* rbyrneXXX: make this same as other last_cache_result code! */
+ j = ++aclpb->aclpb_last_cache_result;
+ aclpb->aclpb_cache_result[j].aci_index = aci->aci_index;
+ aclpb->aclpb_cache_result[j].aci_ruleType = aci->aci_ruleType;
+
+ } else { /* No more space */
+ goto set_result_status;
+ }
+
+ /* Add the cached result status */
+ aclpb->aclpb_cache_result[j].result |= cache_result;
+
+
+
+set_result_status:
+ if (result_status == ACL_RES_ALLOW) {
+ if (access & SLAPI_ACL_SEARCH)
+ /*wrong bit maskes were being used here--
+ a_eval->attrEval_s_status = ACLPB_CACHE_SEARCH_RES_ALLOW;*/
+ a_eval->attrEval_s_status = ACL_ATTREVAL_SUCCESS;
+ else
+ a_eval->attrEval_r_status = ACL_ATTREVAL_SUCCESS;
+
+ } else if ( result_status == ACL_RES_DENY) {
+ if (access & SLAPI_ACL_SEARCH)
+ a_eval->attrEval_s_status = ACL_ATTREVAL_FAIL;
+ else
+ a_eval->attrEval_r_status = ACL_ATTREVAL_FAIL;
+ } else {
+ /* Here, set it to recompute--try again later */
+ if (access & SLAPI_ACL_SEARCH)
+ a_eval->attrEval_s_status = ACL_ATTREVAL_RECOMPUTE;
+ else
+ a_eval->attrEval_r_status = ACL_ATTREVAL_RECOMPUTE;
+ result_status = -1;
+ }
+
+ return result_status;
+}
+
+static void
+__acl_set_aclIndex_inResult ( Acl_PBlock *aclpb, int access, int index )
+{
+ AclAttrEval *c_attrEval = aclpb->aclpb_curr_attrEval;
+
+ if ( c_attrEval ) {
+ if ( access & SLAPI_ACL_SEARCH )
+ c_attrEval->attrEval_s_aciIndex = index;
+ else if ( access & SLAPI_ACL_READ )
+ c_attrEval->attrEval_r_aciIndex = index;
+ }
+}
+
+/*
+ * If filter_sense is true then return (entry satisfies f).
+ * Otherwise, return !(entry satisfies f)
+*/
+
+static int
+acl__test_filter ( Slapi_Entry *entry, struct slapi_filter *f, int filter_sense) {
+ int filter_matched;
+
+ /* slapi_vattr_filter_test() returns 0 for match */
+
+ filter_matched = !slapi_vattr_filter_test(NULL, entry,
+ f,
+ 0 /*don't do acess chk*/);
+
+ if (filter_sense) {
+ return(filter_matched ? ACL_TRUE : ACL_FALSE);
+ } else {
+ return(filter_matched ? ACL_FALSE: ACL_TRUE);
+ }
+}
+
+/*
+ * Make an entry consisting of attr_type and attr_val and put
+ * a pointer to it in *entry.
+ * We will use this afterwards to test for against a filter.
+*/
+
+static int
+acl__make_filter_test_entry ( Slapi_Entry **entry, char *attr_type,
+ struct berval *attr_val) {
+
+ struct berval *vals_array[2];
+
+ vals_array[0] = attr_val;
+ vals_array[1] = NULL;
+
+ *entry = slapi_entry_alloc();
+ slapi_entry_init(*entry, NULL, NULL);
+
+ return (slapi_entry_add_values( *entry, (const char *)attr_type,
+ (struct berval**)&vals_array[0] ));
+
+}
+
+/*********************************************************************************/
+/* E N D */
+/*********************************************************************************/
diff --git a/ldap/servers/plugins/acl/acl.h b/ldap/servers/plugins/acl/acl.h
new file mode 100644
index 00000000..5e16a0bd
--- /dev/null
+++ b/ldap/servers/plugins/acl/acl.h
@@ -0,0 +1,867 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+/*****************************************************************************
+* acl.h
+*
+* Header file for ACL processing
+*
+*****************************************************************************/
+#ifndef _ACL_H_
+#define _ACL_H_
+
+#include <stdio.h>
+#include <string.h>
+#include <sys/types.h>
+#ifndef _WIN32
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+#endif
+
+#include <ldap.h>
+#include <las.h>
+#include <aclproto.h>
+#include <aclerror.h>
+#include "prcvar.h"
+#include "slapi-plugin.h"
+#include "slap.h"
+#include "slapi-private.h"
+#include "portable.h"
+#include "prrwlock.h"
+#include "avl.h"
+
+#include "cert.h"
+
+#include <plhash.h>
+
+#ifdef SOLARIS
+ #include <tnf/probe.h>
+#else
+ #define TNF_PROBE_0_DEBUG(a,b,c)
+ #define TNF_PROBE_1_DEBUG(a,b,c,d,e,f)
+#endif
+
+#define ACL_PLUGIN_NAME "NSACLPlugin"
+extern char *plugin_name;
+
+/*
+ * Define the OID for version 2 of the proxied authorization control if
+ * it is not already defined (it is in recent copies of ldap.h).
+ */
+#ifndef LDAP_CONTROL_PROXIEDAUTH
+#define LDAP_CONTROL_PROXIEDAUTH "2.16.840.1.113730.3.4.18"
+#endif
+
+#define ACLUCHP unsigned char *
+
+static char* const aci_attr_type = "aci";
+static char* const filter_string = "aci=*";
+static char* const aci_targetdn = "target";
+static char* const aci_targetattr = "targetattr";
+static char* const aci_targetattrfilters = "targattrfilters";
+static char* const aci_targetfilter = "targetfilter";
+
+static char* const LDAP_URL_prefix = "ldap:///";
+
+static char* const access_str_compare = "compare";
+static char* const access_str_search = "search";
+static char* const access_str_read = "read";
+static char* const access_str_write = "write";
+static char* const access_str_delete = "delete";
+static char* const access_str_add = "add";
+static char* const access_str_selfwrite = "selfwrite";
+static char* const access_str_proxy = "proxy";
+
+#define ACL_INIT_ATTR_ARRAY 5
+
+/* define the method */
+#define DS_METHOD "ds_method"
+
+#define ACL_ESCAPE_STRING_WITH_PUNCTUATION(x,y) (slapi_is_loglevel_set(SLAPI_LOG_ACL) ? escape_string_with_punctuation(x,y) : "")
+
+/* Lases */
+#define DS_LAS_USER "user"
+#define DS_LAS_GROUP "group"
+#define DS_LAS_USERDN "userdn"
+#define DS_LAS_GROUPDN "groupdn"
+#define DS_LAS_USERDNATTR "userdnattr"
+#define DS_LAS_AUTHMETHOD "authmethod"
+#define DS_LAS_GROUPDNATTR "groupdnattr"
+#define DS_LAS_USERATTR "userattr"
+#define DS_LAS_ROLEDN "roledn"
+#define DS_LAS_ROLEDNATTR "rolednattr"
+
+
+/* These define the things that aclutil_evaluate_macro() supports */
+typedef enum
+{
+ ACL_EVAL_USER,
+ ACL_EVAL_GROUP,
+ ACL_EVAL_ROLE,
+ ACL_EVAL_GROUPDNATTR,
+ ACL_EVAL_TARGET_FILTER
+}acl_eval_types;
+
+typedef enum
+{
+ ACL_RULE_MACRO_DN_TYPE,
+ ACL_RULE_MACRO_DN_LEVELS_TYPE
+}acl_rule_macro_types;
+
+#define ACL_TARGET_MACRO_DN_KEY "($dn)"
+#define ACL_RULE_MACRO_DN_KEY "($dn)"
+#define ACL_RULE_MACRO_DN_LEVELS_KEY "[$dn]"
+#define ACL_RULE_MACRO_ATTR_KEY "($attr."
+
+#define ACL_EVAL_USER 0
+#define ACL_EVAL_GROUP 1
+#define ACL_EVAL_ROLE 2
+
+/* The LASes are implemented in the libaccess library */
+#define DS_LAS_TIMEOFDAY "timeofday"
+#define DS_LAS_DAYOFWEEK "dayofweek"
+
+
+/* ACL function return codes */
+#define ACL_TRUE 1 /* evaluation results to TRUE */
+#define ACL_OK ACL_TRUE
+#define ACL_FALSE 0 /* evaluation results to FALSE */
+#define ACL_ERR -1 /* generic error */
+#define ACL_TARGET_FILTER_ERR -2 /* Target filter not set properly */
+#define ACL_TARGETATTR_FILTER_ERR -3 /* TargetAttr filter not set properly */
+#define ACL_TARGETFILTER_ERR -4 /* Target filter not set properly */
+#define ACL_SYNTAX_ERR -5 /* Syntax error */
+#define ACL_ONEACL_TEXT_ERR -6 /* ONE ACL text error */
+#define ACL_ERR_CONCAT_HANDLES -7 /* unable to concat the handles */
+#define ACL_INVALID_TARGET -8 /* invalid target */
+#define ACL_INVALID_AUTHMETHOD -9 /* multiple client auth */
+#define ACL_INVALID_AUTHORIZATION -10 /* no authorization */
+#define ACL_INCORRECT_ACI_VERSION -11 /* incorrect version # */
+#define ACL_DONT_KNOW -12 /* the world is an uncertain place */
+
+/* supported by the DS */
+#define DS_PROP_CONNECTION "connection"
+#define DS_ATTR_USERDN "userdn"
+#define DS_ATTR_ENTRY "entry"
+#define DS_PROP_ACLPB "aclblock"
+#define DS_ATTR_AUTHTYPE "authtype"
+#define DS_ATTR_CERT "clientcert"
+
+#define ACL_ANOM_MAX_ACL 40
+struct scoped_entry_anominfo {
+ short anom_e_targetInfo[ACL_ANOM_MAX_ACL];
+ short anom_e_nummatched;
+ short anom_e_isrootds;
+};
+
+typedef struct targetattr {
+ int attr_type;
+#define ACL_ATTR_FILTER 0x01
+#define ACL_ATTR_STRING 0x02
+#define ACL_ATTR_STAR 0x04 /* attr is * only */
+
+ union {
+ char *attr_str;
+ struct slapi_filter *attr_filter;
+ }u;
+}Targetattr;
+
+typedef struct targetattrfilter {
+ char *attr_str;
+ char *filterStr;
+ struct slapi_filter *filter; /* value filter */
+
+}Targetattrfilter;
+
+typedef struct Aci_Macro {
+ char *match_this;
+ char *macro_ptr; /* ptr into match_this */
+}aciMacro;
+
+typedef PLHashTable acl_ht_t;
+
+/* Access Control Item (aci): Stores information about a particular ACL */
+typedef struct aci {
+ int aci_type; /* Type of resurce */
+
+/* THE FIRST BYTE WAS USED TO KEEP THE RIGHTS. ITS BEEN MOVED TO
+** aci_access and is now free.
+**
+**
+**
+*/
+
+#define ACI_TARGET_MACRO_DN (int)0x000001
+#define ACI_TARGET_FILTER_MACRO_DN (int)0x000002
+#define ACI_TARGET_DN (int)0x000100 /* target has DN */
+#define ACI_TARGET_ATTR (int)0x000200 /* target is an attr */
+#define ACI_TARGET_PATTERN (int)0x000400 /* target has some patt */
+#define ACI_TARGET_FILTER (int)0x000800 /* target has a filter */
+#define ACI_ACLTXT (int)0x001000 /* ACI has text only */
+#define ACI_TARGET_NOT (int)0x002000 /* it's a != */
+#define ACI_TARGET_ATTR_NOT (int)0x004000 /* It's a != manager */
+#define ACI_TARGET_FILTER_NOT (int)0x008000 /* It's a != filter */
+#define ACI_UNUSED2 (int)0x010000 /* Unused */
+#define ACI_HAS_ALLOW_RULE (int)0x020000 /* allow (...) */
+#define ACI_HAS_DENY_RULE (int)0x040000 /* deny (...) */
+#define ACI_CONTAIN_NOT_USERDN (int)0x080000 /* userdn != blah */
+#define ACI_TARGET_ATTR_ADD_FILTERS (int)0x100000
+#define ACI_TARGET_ATTR_DEL_FILTERS (int)0x200000
+#define ACI_CONTAIN_NOT_GROUPDN (int)0x400000 /* groupdn != blah */
+#define ACI_CONTAIN_NOT_ROLEDN (int)0x800000
+
+ int aci_access;
+
+/*
+ * See also aclpb_access which is used to store rights too.
+*/
+
+ short aci_ruleType; /* kinds of rules in the ACL */
+
+#define ACI_USERDN_RULE (short) 0x0001
+#define ACI_USERDNATTR_RULE (short) 0x0002
+#define ACI_GROUPDN_RULE (short) 0x0004
+#define ACI_GROUPDNATTR_RULE (short) 0x0008
+#define ACI_AUTHMETHOD_RULE (short) 0x0010
+#define ACI_IP_RULE (short) 0x0020
+#define ACI_DNS_RULE (short) 0x0040
+#define ACI_TIMEOFDAY_RULE (short) 0x0080
+#define ACI_DAYOFWEEK_RULE (short) 0x0010
+#define ACI_USERATTR_RULE (short) 0x0200
+/*
+ * These are extension of USERDN/GROUPDN rule. However since the
+ * semantics are quite different, we classify them as different rules.
+ * ex: groupdn = "ldap:///cn=helpdesk, ou=$attr.dept, o=$dn.o, o=isp"
+ */
+#define ACI_PARAM_DNRULE (short) 0x0400
+#define ACI_PARAM_ATTRRULE (short) 0x0800
+#define ACI_USERDN_SELFRULE (short) 0x1000
+#define ACI_ROLEDN_RULE (short) 0x2000
+
+
+
+#define ACI_ATTR_RULES ( ACI_USERDNATTR_RULE | ACI_GROUPDNATTR_RULE | ACI_USERATTR_RULE | ACI_PARAM_DNRULE | ACI_PARAM_ATTRRULE | ACI_USERDN_SELFRULE)
+#define ACI_CACHE_RESULT_PER_ENTRY ACI_ATTR_RULES
+
+ short aci_elevel; /* Based on the aci type some idea about the
+ ** execution flow
+ */
+ int aci_index; /* index # */
+ Slapi_DN *aci_sdn; /* location */
+ Slapi_Filter *target; /* Target is a DN */
+ Targetattr **targetAttr;
+ char *targetFilterStr;
+ struct slapi_filter *targetFilter; /* Target has a filter */
+ Targetattrfilter **targetAttrAddFilters;
+ Targetattrfilter **targetAttrDelFilters;
+ char *aclName; /* ACL name */
+ struct ACLListHandle *aci_handle; /*handle of the ACL */
+ aciMacro *aci_macro;
+ struct aci *aci_next; /* next one */
+}aci_t;
+
+/* Aci excution level
+** The idea is that for each handle types, we can prioritize which one to evaluate first.
+** Evaluating the user before the group is better.
+*/
+#define ACI_ELEVEL_USERDN_ANYONE 0
+#define ACI_ELEVEL_USERDN_ALL 1
+#define ACI_ELEVEL_USERDN 2
+#define ACI_ELEVEL_USERDNATTR 3
+#define ACI_ELEVEL_GROUPDNATTR_URL 4
+#define ACI_ELEVEL_GROUPDNATTR 5
+#define ACI_ELEVEL_GROUPDN 6
+#define ACI_MAX_ELEVEL ACI_ELEVEL_GROUPDN +1
+#define ACI_DEFAULT_ELEVEL ACI_MAX_ELEVEL
+
+
+#define ACLPB_MAX_SELECTED_ACLS 200
+
+typedef struct result_cache {
+ int aci_index;
+ short aci_ruleType;
+ short result;
+#define ACLPB_CACHE_READ_RES_ALLOW (short)0x0001 /* used for ALLOW handles only */
+#define ACLPB_CACHE_READ_RES_DENY (short)0x0002 /* used for DENY handles only */
+#define ACLPB_CACHE_SEARCH_RES_ALLOW (short)0x0004 /* used for ALLOW handles only */
+#define ACLPB_CACHE_SEARCH_RES_DENY (short)0x0008 /* used for DENY handles only */
+#define ACLPB_CACHE_SEARCH_RES_SKIP (short)0x0010 /* used for both types */
+#define ACLPB_CACHE_READ_RES_SKIP (short)0x0020 /* used for both types */
+}r_cache_t;
+#define ACLPB_MAX_CACHE_RESULTS ACLPB_MAX_SELECTED_ACLS
+
+/*
+ * This is use to keep the result of the evaluation of the attr.
+ * We are only intrested in read/searc only.
+ */
+struct acl_attrEval {
+ char *attrEval_name; /* Attribute Name */
+ short attrEval_r_status; /* status of read evaluation */
+ short attrEval_s_status; /* status of search evaluation */
+ int attrEval_r_aciIndex; /* Index of the ACL which grants access*/
+ int attrEval_s_aciIndex; /* Index of the ACL which grants access*/
+
+#define ACL_ATTREVAL_SUCCESS 0x1
+#define ACL_ATTREVAL_FAIL 0x2
+#define ACL_ATTREVAL_RECOMPUTE 0x4
+#define ACL_ATTREVAL_DETERMINISTIC 7
+#define ACL_ATTREVAL_INVALID 0x8
+
+};
+typedef struct acl_attrEval AclAttrEval;
+
+
+/*
+ * Struct to keep the evaluation context information. This struct is
+ * used in multiple places ( different instance ) to keep the context for
+ * current entry evaluation, previous entry evaluation or previous operation
+ * evaluation status.
+ */
+#define ACLPB_MAX_ATTR_LEN 100
+#define ACLPB_MAX_ATTRS 100
+struct acleval_context {
+
+ /* Information about the attrs */
+ AclAttrEval acle_attrEval[ACLPB_MAX_ATTRS];
+ short acle_numof_attrs;
+
+ /* Handles information */
+ short acle_numof_tmatched_handles;
+ int acle_handles_matched_target[ACLPB_MAX_SELECTED_ACLS];
+};
+typedef struct acleval_context aclEvalContext;
+
+
+struct acl_usergroup {
+ short aclug_signature;
+/*
+ * To modify refcnt you need either the write lock on the whole cache or
+ * the reader lock on the whole cache plus this refcnt mutex
+*/
+ short aclug_refcnt;
+ PRLock *aclug_refcnt_mutex;
+
+
+ char *aclug_ndn; /* Client's normalized DN */
+
+ char **aclug_member_groups;
+ short aclug_member_group_size;
+ short aclug_numof_member_group;
+
+ char **aclug_notmember_groups;
+ short aclug_notmember_group_size;
+ short aclug_numof_notmember_group;
+ struct acl_usergroup *aclug_next;
+ struct acl_usergroup *aclug_prev;
+
+};
+typedef struct acl_usergroup aclUserGroup;
+
+#define ACLUG_INCR_GROUPS_LIST 20
+
+struct aci_container {
+ Slapi_DN *acic_sdn; /* node DN */
+ aci_t *acic_list; /* List of the ACLs for that node */
+ int acic_index; /* index to the container array */
+};
+typedef struct aci_container AciContainer;
+
+struct acl_pblock {
+ int aclpb_state;
+
+#define ACLPB_ACCESS_ALLOWED_ON_A_ATTR 0x000001
+#define ACLPB_ACCESS_DENIED_ON_ALL_ATTRS 0x000002
+#define ACLPB_ACCESS_ALLOWED_ON_ENTRY 0x000004
+#define ACLPB_ATTR_STAR_MATCHED 0x000008
+#define ACLPB_FOUND_ATTR_RULE 0x000010
+#define ACLPB_SEARCH_BASED_ON_LIST 0x000020
+#define ACLPB_EXECUTING_DENY_HANDLES 0x000040
+#define ACLPB_EXECUTING_ALLOW_HANDLES 0x000080
+#define ACLPB_ACCESS_ALLOWED_USERATTR 0x000100
+#ifdef DETERMINE_ACCESS_BASED_ON_REQUESTED_ATTRIBUTES
+ #define ACLPB_USER_SPECIFIED_ATTARS 0x000200
+ #define ACLPB_USER_WANTS_ALL_ATTRS 0x000400
+#endif
+#define ACLPB_EVALUATING_FIRST_ATTR 0x000800
+#define ACLPB_FOUND_A_ENTRY_TEST_RULE 0x001000
+#define ACLPB_SEARCH_BASED_ON_ENTRY_LIST 0x002000
+#define ACLPB_DONOT_USE_CONTEXT_ACLS 0x004000
+#define ACLPB_HAS_ACLCB_EVALCONTEXT 0x008000
+#define ACLPB_COPY_EVALCONTEXT 0x010000
+#define ACLPB_MATCHES_ALL_ACLS 0x020000
+#define ACLPB_INITIALIZED 0x040000
+#define ACLPB_INCR_ACLCB_CACHE 0x080000
+#define ACLPB_UPD_ACLCB_CACHE 0x100000
+#define ACLPB_ATTR_RULE_EVALUATED 0x200000
+#define ACLPB_DONOT_EVALUATE_PROXY 0x400000
+
+
+#define ACLPB_RESET_MASK ( ACLPB_ACCESS_ALLOWED_ON_A_ATTR | ACLPB_ACCESS_DENIED_ON_ALL_ATTRS | \
+ ACLPB_ACCESS_ALLOWED_ON_ENTRY | ACLPB_ATTR_STAR_MATCHED | \
+ ACLPB_FOUND_ATTR_RULE | ACLPB_EVALUATING_FIRST_ATTR | \
+ ACLPB_FOUND_A_ENTRY_TEST_RULE )
+#define ACLPB_STATE_ALL 0x3fffff
+
+ int aclpb_res_type;
+
+ #define ACLPB_NEW_ENTRY 0x100
+ #define ACLPB_EFFECTIVE_RIGHTS 0x200
+ #define ACLPB_RESTYPE_ALL 0x7ff
+
+ /*
+ * The bottom bye used to be for rights. It's free now as they have
+ * been moved to aclpb_access.
+ */
+
+ int aclpb_access;
+
+#define ACLPB_SLAPI_ACL_WRITE_ADD 0x200
+#define ACLPB_SLAPI_ACL_WRITE_DEL 0x400
+
+ /* stores the requested access during an operation */
+
+ short aclpb_signature;
+ short aclpb_type;
+#define ACLPB_TYPE_MAIN 1
+#define ACLPB_TYPE_MAIN_STR "Main Block"
+#define ACLPB_TYPE_PROXY 2
+#define ACLPB_TYPE_PROXY_STR "Proxy Block"
+
+ Slapi_Entry *aclpb_client_entry; /* A copy of client's entry */
+ Slapi_PBlock *aclpb_pblock; /* back to LDAP PBlock */
+ int aclpb_optype; /* current optype from pb */
+
+ /* Current entry/dn/attr evaluation info */
+ Slapi_Entry *aclpb_curr_entry; /* current Entry being processed */
+ int aclpb_num_entries;
+ Slapi_DN *aclpb_curr_entry_sdn; /* Entry's SDN */
+ Slapi_DN *aclpb_authorization_sdn; /* dn used for authorization */
+
+ AclAttrEval *aclpb_curr_attrEval; /* Current attr being evaluated */
+ struct berval *aclpb_curr_attrVal; /* Value of Current attr */
+ Slapi_Entry *aclpb_filter_test_entry; /* Scratch entry */
+ aci_t *aclpb_curr_aci;
+ char *aclpb_Evalattr; /* The last attr evaluated */
+
+ /* Plist and eval info */
+ ACLEvalHandle_t *aclpb_acleval; /* acleval handle for evaluation */
+ struct PListStruct_s *aclpb_proplist;/* All the needed property */
+
+ /* DENY ACI HANDLES */
+ aci_t **aclpb_deny_handles;
+ int aclpb_deny_handles_size;
+ int aclpb_num_deny_handles;
+
+ /* ALLOW ACI HANDLES */
+ aci_t **aclpb_allow_handles;
+ int aclpb_allow_handles_size;
+ int aclpb_num_allow_handles;
+
+ /* This is used in the groupdnattr="URL" rule
+ ** Keep a list of base where searched has been done
+ */
+ char **aclpb_grpsearchbase;
+ int aclpb_grpsearchbase_size;
+ int aclpb_numof_bases;
+
+ aclUserGroup *aclpb_groupinfo;
+
+ /* Keep the Group nesting level */
+ int aclpb_max_nesting_level;
+ int aclpb_max_member_sizelimit;
+
+
+ /* To keep the results in the cache */
+
+ int aclpb_last_cache_result;
+ struct result_cache aclpb_cache_result[ACLPB_MAX_CACHE_RESULTS];
+
+ /* Index numbers of ACLs selected based on a locality search*/
+ char *aclpb_search_base;
+ int aclpb_base_handles_index[ACLPB_MAX_SELECTED_ACLS];
+ int aclpb_handles_index[ACLPB_MAX_SELECTED_ACLS];
+
+ /* Evaluation context info
+ ** 1) Context cached from aclcb ( from connection struct )
+ ** 2) Context cached from previous entry evaluation
+ ** 3) current entry evaluation info
+ */
+ aclEvalContext aclpb_curr_entryEval_context;
+ aclEvalContext aclpb_prev_entryEval_context;
+ aclEvalContext aclpb_prev_opEval_context;
+
+ /* Currentry anom profile sumamry */
+ struct scoped_entry_anominfo aclpb_scoped_entry_anominfo;
+
+ /* Some Statistics gathering */
+ PRUint16 aclpb_stat_acllist_scanned;
+ PRUint16 aclpb_stat_aclres_matched;
+ PRUint16 aclpb_stat_total_entries;
+ PRUint16 aclpb_stat_anom_list_scanned;
+ PRUint16 aclpb_stat_num_copycontext;
+ PRUint16 aclpb_stat_num_copy_attrs;
+ PRUint16 aclpb_stat_num_tmatched_acls;
+ PRUint16 aclpb_stat_unused;
+ CERTCertificate *aclpb_clientcert;
+ AciContainer *aclpb_aclContainer;
+ struct acl_pblock *aclpb_proxy; /* Child proxy block */
+ acl_ht_t *aclpb_macro_ht; /* ht for partial macro strs */
+
+ struct acl_pblock *aclpb_prev; /* Previpous in the chain */
+ struct acl_pblock *aclpb_next; /* Next in the chain */
+};
+typedef struct acl_pblock Acl_PBlock;
+
+/* PBLCOK TYPES */
+typedef enum
+{
+ ACLPB_BINDDN_PBLOCK,
+ ACLPB_PROXYDN_PBLOCK,
+ ACLPB_ALL_PBLOCK
+}aclpb_types;
+
+
+#define ACLPB_EVALCONTEXT_CURR 1
+#define ACLPB_EVALCONTEXT_PREV 2
+#define ACLPB_EVALCONTEXT_ACLCB 3
+
+
+
+/* Cleaning/ deallocating/ ... acl_freeBlock() */
+#define ACL_CLEAN_ACLPB 1
+#define ACL_COPY_ACLCB 2
+#define ACL_CLEAN_ACLCB 3
+
+/* used to differentiate acl plugins sharing the same lib */
+#define ACL_PLUGIN_IDENTITY 1
+#define ACL_PREOP_PLUGIN_IDENTITY 2
+
+
+/* start with 50 and then add 50 more as required
+ * The first ACI_MAX_ELEVEL slots are predefined.
+ */
+#define ACLPB_INCR_LIST_HANDLES ACI_MAX_ELEVEL + 43
+
+#define ACLPB_INCR_BASES 5
+
+/*
+ * acl private block which hangs from connection structure.
+ * This is allocated the first time an operation is done and freed when the
+ * connection are cleaned.
+ *
+ */
+struct acl_cblock {
+
+ short aclcb_aclsignature;
+ short aclcb_state;
+#define ACLCB_HAS_CACHED_EVALCONTEXT 0x1
+
+ Slapi_DN *aclcb_sdn; /* Contains bind SDN */
+ aclEvalContext aclcb_eval_context;
+ PRLock *aclcb_lock; /* shared lock */
+};
+
+struct acl_groupcache {
+ short aclg_state; /* status information */
+ short aclg_signature;
+ int aclg_num_userGroups;
+ aclUserGroup *aclg_first;
+ aclUserGroup *aclg_last;
+ PRRWLock *aclg_rwlock; /* lock to monitor the group cache */
+};
+typedef struct acl_groupcache aclGroupCache;
+
+
+/* Type of extensions that can be registered */
+typedef enum
+{
+ ACL_EXT_OPERATION, /* extension for Operation object */
+ ACL_EXT_CONNECTION, /* extension for Connection object */
+ ACL_EXT_ALL
+}ext_type;
+
+/* Used to pass data around in acllas.c */
+
+typedef struct {
+ char *clientDn;
+ char *authType;
+ int anomUser;
+ Acl_PBlock *aclpb;
+ Slapi_Entry *resourceEntry;
+
+}lasInfo;
+
+
+/* reasons why the subject allowed/denied access--good for logs */
+
+typedef enum{
+ACL_REASON_NO_ALLOWS,
+ACL_REASON_RESULT_CACHED_DENY,
+ACL_REASON_EVALUATED_DENY, /* evaluated deny */
+ACL_REASON_RESULT_CACHED_ALLOW, /* cached allow */
+ACL_REASON_EVALUATED_ALLOW, /* evalauted allow */
+ACL_REASON_NO_MATCHED_RESOURCE_ALLOWS, /* were allows/denies, but none matched */
+ACL_REASON_NONE, /* no reason available */
+ACL_REASON_ANON_ALLOWED,
+ACL_REASON_ANON_DENIED,
+ACL_REASON_NO_MATCHED_SUBJECT_ALLOWS,
+ACL_REASON_EVALCONTEXT_CACHED_ALLOW,
+ACL_REASON_EVALCONTEXT_CACHED_NOT_ALLOWED,
+ACL_REASON_EVALCONTEXT_CACHED_ATTR_STAR_ALLOW
+}aclReasonCode_t;
+
+typedef struct{
+ aci_t *deciding_aci;
+ aclReasonCode_t reason;
+}aclResultReason_t;
+#define ACL_NO_DECIDING_ACI_INDEX -10
+
+
+/* Extern declaration for backend state change fnc: acllist.c and aclinit.c */
+
+void acl_be_state_change_fnc ( void *handle, char *be_name, int old_state,
+ int new_state);
+
+
+/* Extern declaration for ATTRs */
+
+extern int
+DS_LASIpGetter(NSErr_t *errp, PList_t subject, PList_t resource, PList_t
+ auth_info, PList_t global_auth, void *arg);
+extern int
+DS_LASDnsGetter(NSErr_t *errp, PList_t subject, PList_t resource, PList_t
+ auth_info, PList_t global_auth, void *arg);
+extern int
+DS_LASUserDnGetter(NSErr_t *errp, PList_t subject, PList_t resource, PList_t
+ auth_info, PList_t global_auth, void *arg);
+extern int
+DS_LASGroupDnGetter(NSErr_t *errp, PList_t subject, PList_t resource, PList_t
+ auth_info, PList_t global_auth, void *arg);
+extern int
+DS_LASEntryGetter(NSErr_t *errp, PList_t subject, PList_t resource,
+ PList_t auth_info, PList_t global_auth, void *arg);
+
+extern int
+DS_LASCertGetter(NSErr_t *errp, PList_t subject, PList_t resource,
+ PList_t auth_info, PList_t global_auth, void *arg);
+
+/* function declartion for LAses supported by DS */
+
+extern int DS_LASUserEval(NSErr_t *errp, char *attribute, CmpOp_t comparator,
+ char *pattern, int *cachable, void **las_cookie,
+ PList_t subject, PList_t resource, PList_t auth_info,
+ PList_t global_auth);
+
+extern int DS_LASGroupEval(NSErr_t *errp, char *attribute, CmpOp_t comparator,
+ char *pattern, int *cachable, void **las_cookie,
+ PList_t subject, PList_t resource, PList_t auth_info,
+ PList_t global_auth);
+
+extern int DS_LASUserDnEval(NSErr_t *errp, char *attribute, CmpOp_t comparator,
+ char *pattern, int *cachable, void **las_cookie,
+ PList_t subject, PList_t resource, PList_t auth_info,
+ PList_t global_auth);
+
+extern int DS_LASGroupDnEval(NSErr_t *errp, char *attribute, CmpOp_t comparator,
+ char *pattern, int *cachable, void **las_cookie,
+ PList_t subject, PList_t resource, PList_t auth_info,
+ PList_t global_auth);
+
+extern int DS_LASRoleDnEval(NSErr_t *errp, char *attr_name, CmpOp_t comparator,
+ char *attr_pattern, int *cachable, void **LAS_cookie,
+ PList_t subject, PList_t resource, PList_t auth_info,
+ PList_t global_auth);
+
+extern int DS_LASUserDnAttrEval(NSErr_t *errp, char *attribute,
+ CmpOp_t comparator,
+ char *pattern, int *cachable, void **las_cookie,
+ PList_t subject, PList_t resource, PList_t auth_info,
+ PList_t global_auth);
+
+extern int DS_LASAuthMethodEval(NSErr_t *errp, char *attribute,
+ CmpOp_t comparator,
+ char *pattern, int *cachable, void **las_cookie,
+ PList_t subject, PList_t resource, PList_t auth_info,
+ PList_t global_auth);
+
+extern int DS_LASGroupDnAttrEval(NSErr_t *errp, char *attribute,
+ CmpOp_t comparator,
+ char *pattern, int *cachable, void **las_cookie,
+ PList_t subject, PList_t resource, PList_t auth_info,
+ PList_t global_auth);
+
+extern int DS_LASRoleDnAttrEval(NSErr_t *errp, char *attribute,
+ CmpOp_t comparator,
+ char *pattern, int *cachable, void **las_cookie,
+ PList_t subject, PList_t resource, PList_t auth_info,
+ PList_t global_auth);
+
+extern int DS_LASUserAttrEval(NSErr_t *errp, char *attribute,
+ CmpOp_t comparator,
+ char *pattern, int *cachable, void **las_cookie,
+ PList_t subject, PList_t resource, PList_t auth_info,
+ PList_t global_auth);
+
+/* other function declaration */
+int aclinit_main();
+int acl_match_substring (struct slapi_filter *f, char *str, int match);
+void acl_print_acllib_err(NSErr_t *errp, char * str);
+void acl_initBlock ( Slapi_PBlock *pb );
+void acl_freeBlock ( Slapi_PBlock *pb, int state );
+int acl_read_access_allowed_on_entry ( Slapi_PBlock *pb, Slapi_Entry *e,
+ char **attrs, int access);
+int acl_access_allowed_modrdn ( Slapi_PBlock *pb, Slapi_Entry *e, char *attr,
+ struct berval *val, int access);
+int acl_read_access_allowed_on_attr ( Slapi_PBlock *pb, Slapi_Entry *e, char *attr,
+ struct berval *val, int access);
+void acl_set_acllist (Slapi_PBlock *pb, int scope, char *base);
+void acl_gen_err_msg(int access, char *edn, char *attr, char **errbuf);
+void acl_modified ( Slapi_PBlock *pb, int optype, char *dn, void *change);
+int acl_access_allowed_disjoint_resource( Slapi_PBlock *pb, Slapi_Entry *e,
+ char *attr, struct berval *val, int access );
+int acl_access_allowed_main ( Slapi_PBlock *pb, Slapi_Entry *e, char **attrs,
+ struct berval *val, int access , int flags, char **errbuf);
+int acl_access_allowed( Slapi_PBlock *pb, Slapi_Entry *e, char *attr,
+ struct berval *val, int access );
+int acl_verify_syntax(const Slapi_DN *e_sdn, const struct berval *bval);
+aclUserGroup * acl_get_usersGroup ( struct acl_pblock *aclpb , char *n_dn);
+void acl_print_acllib_err (NSErr_t *errp , char * str);
+int acl_check_mods( Slapi_PBlock *pb, Slapi_Entry *e, LDAPMod **mods, char **errbuf );
+int acl_verify_aci_syntax (Slapi_Entry *e, char **errbuf);
+char * acl__access2str(int access);
+void acl_strcpy_special (char *d, char *s);
+int acl_parse(char * str, aci_t *aci_item);
+char * acl_access2str ( int access );
+int acl_init_ext ();
+void * acl_get_ext (ext_type type, void *object);
+void acl_set_ext (ext_type type, void *object, void *data);
+void acl_reset_ext_status (ext_type type, void *object);
+void acl_init_op_ext ( Slapi_PBlock *pb , int type, char *dn, int copy);
+void * acl_operation_ext_constructor (void *object, void *parent );
+void acl_operation_ext_destructor ( void *ext, void *object, void *parent );
+void * acl_conn_ext_constructor (void *object, void *parent );
+void acl_conn_ext_destructor ( void *ext, void *object, void *parent );
+void acl_clean_aclEval_context ( aclEvalContext *clean_me, int scrub_only );
+void acl_copyEval_context ( struct acl_pblock *aclpb, aclEvalContext *src,
+ aclEvalContext *dest , int copy_attr_only );
+struct acl_pblock * acl_get_aclpb ( Slapi_PBlock *pb, int type );
+int acl_client_anonymous ( Slapi_PBlock *pb );
+short acl_get_aclsignature();
+void acl_set_aclsignature( short value);
+void acl_regen_aclsignature();
+struct acl_pblock * acl_new_proxy_aclpb( Slapi_PBlock *pb );
+void acl_set_authorization_dn( Slapi_PBlock *pb, char *dn, int type );
+int acl_get_proxyauth_dn( Slapi_PBlock *pb, char **proxydnp,
+ char **errtextp );
+void acl_init_aclpb ( Slapi_PBlock *pb , Acl_PBlock *aclpb,
+ const char *dn, int copy_from_aclcb);
+int acl_create_aclpb_pool ();
+int acl_skip_access_check ( Slapi_PBlock *pb, Slapi_Entry *e );
+
+int aclext_alloc_lockarray ();
+
+int aclutil_str_appened(char **str1, const char *str2);
+void aclutil_print_err (int rv , const Slapi_DN *sdn,
+ const struct berval* val, char **errbuf);
+void aclutil_print_aci (aci_t *aci_item, char *type);
+short aclutil_gen_signature ( short c_signature );
+void aclutil_print_resource( struct acl_pblock *aclpb, char *right , char *attr, char *clientdn );
+char * aclutil_expand_paramString ( char *str, Slapi_Entry *e );
+
+
+void acllist_init_scan (Slapi_PBlock *pb, int scope, char *base);
+aci_t * acllist_get_first_aci (Acl_PBlock *aclpb, PRUint32 *cookie );
+aci_t * acllist_get_next_aci ( Acl_PBlock *aclpb, aci_t *curraci, PRUint32 *cookie );
+aci_t * acllist_get_aci_new ();
+void acllist_free_aci (aci_t *item);
+void acllist_acicache_READ_UNLOCK(void);
+void acllist_acicache_READ_LOCK(void);
+void acllist_acicache_WRITE_UNLOCK(void);
+void acllist_acicache_WRITE_LOCK(void);
+void acllist_aciscan_update_scan ( Acl_PBlock *aclpb, char *edn );
+int acllist_remove_aci_needsLock( const Slapi_DN *sdn, const struct berval *attr );
+int acllist_insert_aci_needsLock( const Slapi_DN *e_sdn, const struct berval* aci_attr);
+int acllist_init ();
+int acllist_moddn_aci_needsLock ( Slapi_DN *oldsdn, char *newdn );
+void acllist_print_tree ( Avlnode *root, int *depth, char *start, char *side);
+AciContainer *acllist_get_aciContainer_new ( );
+void acllist_done_aciContainer ( AciContainer *);
+
+aclUserGroup* aclg_find_userGroup (char *n_dn);
+void aclg_regen_ugroup_signature( aclUserGroup *ugroup);
+void aclg_markUgroupForRemoval ( aclUserGroup *u_group );
+void aclg_reader_incr_ugroup_refcnt(aclUserGroup* u_group);
+int aclg_numof_usergroups(void);
+int aclgroup_init ();
+void aclg_regen_group_signature ();
+void aclg_reset_userGroup ( struct acl_pblock *aclpb );
+void aclg_init_userGroup ( struct acl_pblock *aclpb, const char *dn , int got_lock);
+aclUserGroup * aclg_get_usersGroup ( struct acl_pblock *aclpb , char *n_dn);
+
+void aclg_lock_groupCache (int type );
+void aclg_unlock_groupCache (int type );
+
+int aclanom_init();
+int aclanom_match_profile (Slapi_PBlock *pb, struct acl_pblock *aclpb,
+ Slapi_Entry *e, char *attr, int access);
+void aclanom_get_suffix_info(Slapi_Entry *e, struct acl_pblock *aclpb );
+void aclanom_invalidateProfile();
+typedef enum{
+ DONT_TAKE_ACLCACHE_READLOCK,
+ DO_TAKE_ACLCACHE_READLOCK,
+ DONT_TAKE_ACLCACHE_WRITELOCK,
+ DO_TAKE_ACLCACHE_WRITELOCK
+}acl_lock_flag_t;
+void aclanom_gen_anomProfile (acl_lock_flag_t lock_flag);
+int aclanom_is_client_anonymous ( Slapi_PBlock *pb );
+int aclinit_main ();
+typedef struct aclinit_handler_callback_data {
+#define ACL_ADD_ACIS 1
+#define ACL_REMOVE_ACIS 0
+ int op;
+ int retCode;
+ acl_lock_flag_t lock_flag;
+}aclinit_handler_callback_data_t;
+int
+aclinit_search_and_update_aci ( int thisbeonly, const Slapi_DN *base,
+ char *be_name, int scope, int op,
+ acl_lock_flag_t lock_flag);
+void *aclplugin_get_identity(int plug);
+int
+acl_dn_component_match( const char *ndn, char *match_this, int component_number);
+char *
+acl_match_macro_in_target( const char *ndn, char *match_this,
+ char *macro_ptr);
+char* get_next_component(char *dn, int *index);
+int acl_match_prefix( char *macro_prefix, const char *ndn,
+ int *exact_match);
+char *
+get_this_component(char *dn, int *index);
+int
+acl_find_comp_end( char * s);
+char *
+acl_replace_str(char * s, char *substr, char* replace_with);
+int acl_strstr(char * s, char *substr);
+int aclutil_evaluate_macro( char * rule, lasInfo *lasinfo,
+ acl_eval_types evalType );
+
+/* acl hash table functions */
+void acl_ht_add_and_freeOld(acl_ht_t * acl_ht, PLHashNumber key,char *value);
+acl_ht_t *acl_ht_new(void);
+void acl_ht_free_all_entries_and_values( acl_ht_t *acl_ht);
+void acl_ht_remove( acl_ht_t *acl_ht, PLHashNumber key);
+void *acl_ht_lookup( acl_ht_t *acl_ht, PLHashNumber key);
+void acl_ht_display_ht( acl_ht_t *acl_ht);
+
+/* acl get effective rights */
+int
+acl_get_effective_rights ( Slapi_PBlock *pb, Slapi_Entry *e,
+ char **attrs, struct berval *val, int access, char **errbuf );
+
+char* aclutil__access_str (int type , char str[]);
+
+#endif /* _ACL_H_ */
diff --git a/ldap/servers/plugins/acl/acl_ext.c b/ldap/servers/plugins/acl/acl_ext.c
new file mode 100644
index 00000000..28a9ce18
--- /dev/null
+++ b/ldap/servers/plugins/acl/acl_ext.c
@@ -0,0 +1,968 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+#include "acl.h"
+
+static void acl__done_aclpb ( struct acl_pblock *aclpb );
+static void acl__dump_stats ( struct acl_pblock *aclpb , const char *block_type);
+static Acl_PBlock * acl__get_aclpb_from_pool ( );
+static int acl__put_aclpb_back_to_pool ( Acl_PBlock *aclpb );
+static Acl_PBlock * acl__malloc_aclpb ( );
+static char * acl__get_aclpb_type ( Acl_PBlock *aclpb );
+static PRLock *aclext_get_lock ();
+
+
+struct acl_pbqueue {
+ Acl_PBlock *aclq_free;
+ Acl_PBlock *aclq_busy;
+ short aclq_nfree;
+ short aclq_nbusy;
+ PRLock *aclq_lock;
+};
+typedef struct acl_pbqueue Acl_PBqueue;
+
+static Acl_PBqueue *aclQueue;
+
+/* structure with information for each extension */
+typedef struct acl_ext
+{
+ char *object_name; /* name of the object extended */
+ int object_type; /* handle to the extended object */
+ int handle; /* extension handle */
+} acl_ext;
+
+static acl_ext acl_ext_list [ACL_EXT_ALL];
+
+/*
+ * EXTENSION INITIALIZATION, CONSTRUCTION, & DESTRUCTION
+ *
+ */
+int
+acl_init_ext ()
+{
+ int rc;
+
+ acl_ext_list[ACL_EXT_OPERATION].object_name = SLAPI_EXT_OPERATION;
+
+ rc = slapi_register_object_extension(plugin_name, SLAPI_EXT_OPERATION,
+ acl_operation_ext_constructor,
+ acl_operation_ext_destructor,
+ &acl_ext_list[ACL_EXT_OPERATION].object_type,
+ &acl_ext_list[ACL_EXT_OPERATION].handle);
+
+ if ( rc != 0 ) return rc;
+
+ acl_ext_list[ACL_EXT_CONNECTION].object_name = SLAPI_EXT_CONNECTION;
+ rc = slapi_register_object_extension(plugin_name, SLAPI_EXT_CONNECTION,
+ acl_conn_ext_constructor,
+ acl_conn_ext_destructor,
+ &acl_ext_list[ACL_EXT_CONNECTION].object_type,
+ &acl_ext_list[ACL_EXT_CONNECTION].handle);
+
+ return rc;
+
+}
+
+/* Interface to get the extensions */
+void *
+acl_get_ext (ext_type type, void *object)
+{
+ struct acl_ext ext;
+ void *data;
+
+ if ( type >= ACL_EXT_ALL ) {
+ slapi_log_error ( SLAPI_LOG_ACL, plugin_name,
+ "Invalid extension type:%d\n", type );
+ return NULL;
+ }
+
+ /* find the requested extension */
+ ext = acl_ext_list [type];
+ data = slapi_get_object_extension(ext.object_type, object, ext.handle);
+
+ return data;
+}
+
+void
+acl_set_ext (ext_type type, void *object, void *data)
+{
+ if ( type >= 0 && type < ACL_EXT_ALL )
+ {
+ struct acl_ext ext = acl_ext_list [type];
+ slapi_set_object_extension ( ext.object_type, object, ext.handle, data );
+ }
+}
+
+/****************************************************************************
+ * Global lock array so that private extension between connection and operation
+ * co-exist
+ *
+ ******************************************************************************/
+struct ext_lockArray {
+ PRLock **lockArray;
+ int numlocks;
+};
+
+static struct ext_lockArray extLockArray;
+
+/* PKBxxx: make this a configurable. Start with 2 * maxThreads */
+#define ACLEXT_MAX_LOCKS 40
+
+int
+aclext_alloc_lockarray ( )
+{
+
+ int i;
+ PRLock *lock;
+
+ extLockArray.lockArray =
+ (PRLock **) slapi_ch_calloc ( ACLEXT_MAX_LOCKS, sizeof ( PRLock *) );
+
+ for ( i =0; i < ACLEXT_MAX_LOCKS; i++) {
+ if (NULL == (lock = PR_NewLock()) ) {
+ slapi_log_error( SLAPI_LOG_FATAL, plugin_name,
+ "Unable to allocate locks used for private extension\n");
+ return 1;
+ }
+ extLockArray.lockArray[i] = lock;
+ }
+ extLockArray.numlocks = ACLEXT_MAX_LOCKS;
+ return 0;
+}
+static PRUint32 slot_id =0;
+static PRLock *
+aclext_get_lock ()
+{
+
+ PRUint16 slot = slot_id % ACLEXT_MAX_LOCKS;
+ slot_id++;
+ return ( extLockArray.lockArray[slot] );
+
+}
+/****************************************************************************/
+/* CONNECTION EXTENSION SPECIFIC */
+/****************************************************************************/
+void *
+acl_conn_ext_constructor ( void *object, void *parent )
+{
+ struct acl_cblock *ext = NULL;
+
+ ext = (struct acl_cblock * ) slapi_ch_calloc (1, sizeof (struct acl_cblock ) );
+ if (( ext->aclcb_lock = aclext_get_lock () ) == NULL ) {
+ slapi_log_error( SLAPI_LOG_FATAL, plugin_name,
+ "Unable to get Read/Write lock for CONNECTION extension\n");
+ slapi_ch_free ( (void **) &ext );
+ return NULL;
+ }
+ ext->aclcb_sdn = slapi_sdn_new ();
+ /* store the signatures */
+ ext->aclcb_aclsignature = acl_get_aclsignature();
+ ext->aclcb_state = -1;
+ return ext;
+
+
+}
+
+void
+acl_conn_ext_destructor ( void *ext, void *object, void *parent )
+{
+ struct acl_cblock *aclcb = ext;
+ PRLock *shared_lock;
+
+ if ( NULL == aclcb ) return;
+ PR_Lock ( aclcb->aclcb_lock );
+ shared_lock = aclcb->aclcb_lock;
+ acl_clean_aclEval_context ( &aclcb->aclcb_eval_context, 0 /* clean*/ );
+ slapi_sdn_free ( &aclcb->aclcb_sdn );
+ aclcb->aclcb_lock = NULL;
+ slapi_ch_free ( (void **) &aclcb );
+
+ PR_Unlock ( shared_lock );
+}
+
+/****************************************************************************/
+/* OPERATION EXTENSION SPECIFIC */
+/****************************************************************************/
+void *
+acl_operation_ext_constructor ( void *object, void *parent )
+{
+ Acl_PBlock *aclpb = NULL;
+
+ TNF_PROBE_0_DEBUG(acl_operation_ext_constructor_start ,"ACL","");
+
+ /* This means internal operations */
+ if ( NULL == parent) {
+
+ TNF_PROBE_1_DEBUG(acl_operation_ext_constructor_end ,"ACL","",
+ tnf_string,internal_op,"");
+
+ return NULL;
+ }
+
+ aclpb = acl__get_aclpb_from_pool();
+ if ( NULL == aclpb ) {
+ slapi_log_error ( SLAPI_LOG_FATAL, plugin_name,
+ "Operation extension allocation Failed\n");
+ }
+
+ TNF_PROBE_0_DEBUG(acl_operation_ext_constructor_end ,"ACL","");
+
+ return aclpb;
+
+}
+
+void
+acl_operation_ext_destructor ( void *ext, void *object, void *parent )
+{
+
+ struct acl_cblock *aclcb = NULL;
+ struct acl_pblock *aclpb = NULL;
+
+ TNF_PROBE_0_DEBUG(acl_operation_ext_destructor_start ,"ACL","");
+
+ if ( (NULL == parent ) || (NULL == ext)) {
+ TNF_PROBE_1_DEBUG(acl_operation_ext_destructor_end ,"ACL","",
+ tnf_string,internal_op,"");
+
+ return;
+ }
+
+ aclpb = (Acl_PBlock *) ext;
+
+ if ( (NULL == aclpb) ||
+ (NULL == aclpb->aclpb_pblock) ||
+ (!(aclpb->aclpb_state & ACLPB_INITIALIZED)))
+ goto clean_aclpb;
+
+ /* get the connection extension */
+ aclcb = (struct acl_cblock *) acl_get_ext ( ACL_EXT_CONNECTION, parent );
+
+ /* We are about to get out of this connection. Move all the
+ ** cached information to the acl private block which hangs
+ ** from the connection struct.
+ */
+ if ( aclcb && aclcb->aclcb_lock &&
+ ( (aclpb->aclpb_state & ACLPB_UPD_ACLCB_CACHE ) ||
+ (aclpb->aclpb_state & ACLPB_INCR_ACLCB_CACHE ) ) ) {
+
+ aclEvalContext *c_evalContext;
+ int attr_only = 0;
+ PRLock *shared_lock = aclcb->aclcb_lock;
+
+ if (aclcb->aclcb_lock ) PR_Lock ( shared_lock );
+ else {
+ goto clean_aclpb;
+ }
+ if ( !aclcb->aclcb_lock ) {
+ slapi_log_error (SLAPI_LOG_FATAL, plugin_name, "aclcb lock released! aclcb cache can't be refreshed\n");
+ PR_Unlock ( shared_lock );
+ goto clean_aclpb;
+ }
+
+ /* We need to refresh the aclcb cache */
+ if ( aclpb->aclpb_state & ACLPB_UPD_ACLCB_CACHE )
+ acl_clean_aclEval_context ( &aclcb->aclcb_eval_context, 0 /* clean*/ );
+ if ( aclpb->aclpb_prev_entryEval_context.acle_numof_attrs ) {
+ c_evalContext = &aclpb->aclpb_prev_entryEval_context;
+ } else {
+ c_evalContext = &aclpb->aclpb_curr_entryEval_context;
+ }
+
+ if (( aclpb->aclpb_state & ACLPB_INCR_ACLCB_CACHE ) &&
+ ! ( aclpb->aclpb_state & ACLPB_UPD_ACLCB_CACHE ))
+ attr_only = 1;
+
+ acl_copyEval_context ( NULL, c_evalContext, &aclcb->aclcb_eval_context, attr_only );
+
+ aclcb->aclcb_aclsignature = aclpb->aclpb_signature;
+ if ( aclcb->aclcb_sdn && aclpb->aclpb_authorization_sdn &&
+ (0 != slapi_sdn_compare ( aclcb->aclcb_sdn,
+ aclpb->aclpb_authorization_sdn ) ) ) {
+ slapi_sdn_set_ndn_byval( aclcb->aclcb_sdn,
+ slapi_sdn_get_ndn ( aclpb->aclpb_authorization_sdn ) );
+ }
+ aclcb->aclcb_state = 0;
+ aclcb->aclcb_state |= ACLCB_HAS_CACHED_EVALCONTEXT;
+
+ PR_Unlock ( shared_lock );
+ }
+
+clean_aclpb:
+ if ( aclpb ) {
+
+ if ( aclpb->aclpb_proxy ) {
+ TNF_PROBE_0_DEBUG(acl_proxy_aclpbdoneback_start ,"ACL","");
+
+ acl__done_aclpb( aclpb->aclpb_proxy );
+
+ /* Put back to the Pool */
+ acl__put_aclpb_back_to_pool ( aclpb->aclpb_proxy );
+ aclpb->aclpb_proxy = NULL;
+ TNF_PROBE_0_DEBUG(acl_proxy_aclpbdoneback_end ,"ACL","");
+
+ }
+
+ TNF_PROBE_0_DEBUG(acl_aclpbdoneback_start ,"ACL","");
+
+ acl__done_aclpb( aclpb);
+ acl__put_aclpb_back_to_pool ( aclpb );
+
+ TNF_PROBE_0_DEBUG(acl_aclpbdoneback_end ,"ACL","");
+
+ }
+
+ TNF_PROBE_0_DEBUG(acl_operation_ext_destructor_end ,"ACL","");
+
+}
+
+/****************************************************************************/
+/* FUNCTIONS TO MANAGE THE ACLPB POOL */
+/****************************************************************************/
+
+/*
+ * Get the right acl pblock
+ */
+struct acl_pblock *
+acl_get_aclpb ( Slapi_PBlock *pb, int type )
+{
+ Acl_PBlock *aclpb = NULL;
+ void *op = NULL;
+
+ slapi_pblock_get ( pb, SLAPI_OPERATION, &op );
+ aclpb = (Acl_PBlock *) acl_get_ext ( ACL_EXT_OPERATION, op );
+ if (NULL == aclpb ) return NULL;
+
+ if ( type == ACLPB_BINDDN_PBLOCK )
+ return aclpb;
+ else if ( type == ACLPB_PROXYDN_PBLOCK )
+ return aclpb->aclpb_proxy;
+ else
+ slapi_log_error ( SLAPI_LOG_FATAL, plugin_name,
+ "acl_get_aclpb: Invalid aclpb type %d\n", type );
+ return NULL;
+}
+/*
+ * Create a new proxy acl pblock
+ *
+ */
+struct acl_pblock *
+acl_new_proxy_aclpb( Slapi_PBlock *pb )
+{
+ void *op;
+ Acl_PBlock *aclpb = NULL;
+ Acl_PBlock *proxy_aclpb = NULL;
+
+ slapi_pblock_get ( pb, SLAPI_OPERATION, &op );
+ aclpb = (Acl_PBlock *) acl_get_ext ( ACL_EXT_OPERATION, op );
+ if (NULL == aclpb ) return NULL;
+
+ proxy_aclpb = acl__get_aclpb_from_pool();
+ if (NULL == proxy_aclpb) return NULL;
+ proxy_aclpb->aclpb_type = ACLPB_TYPE_PROXY;
+
+ aclpb->aclpb_proxy = proxy_aclpb;
+
+ return proxy_aclpb;
+
+}
+static int
+acl__handle_config_entry (Slapi_Entry *e, void *callback_data )
+{
+ *(int * )callback_data = slapi_entry_attr_get_int( e, "nsslapd-threadnumber");
+
+ return 0;
+}
+
+/*
+ * Create a pool of acl pblock. Created during the ACL plugin
+ * initialization.
+ */
+int
+acl_create_aclpb_pool ()
+{
+
+ Acl_PBlock *aclpb;
+ Acl_PBlock *prev_aclpb;
+ Acl_PBlock *first_aclpb;
+ int i;
+ int maxThreads= 0;
+
+ slapi_search_internal_callback( "cn=config", LDAP_SCOPE_BASE, "(objectclass=*)",
+ NULL, 0 /* attrsonly */,
+ &maxThreads/* callback_data */,
+ NULL /* controls */,
+ NULL /* result_callback */,
+ acl__handle_config_entry,
+ NULL /* referral_callback */);
+
+ /* Create a pool pf aclpb */
+ maxThreads = 2 * maxThreads;
+
+ aclQueue = ( Acl_PBqueue *) slapi_ch_calloc ( 1, sizeof (Acl_PBqueue) );
+ aclQueue->aclq_lock = PR_NewLock();
+
+ if ( NULL == aclQueue->aclq_lock ) {
+ /* ERROR */
+ return 1;
+ }
+
+ prev_aclpb = NULL;
+ first_aclpb = NULL;
+ for ( i = 0; i < maxThreads; i++ ) {
+ aclpb = acl__malloc_aclpb ();
+ if ( 0 == i) first_aclpb = aclpb;
+
+ aclpb->aclpb_prev = prev_aclpb;
+ if ( prev_aclpb ) prev_aclpb->aclpb_next = aclpb;
+ prev_aclpb = aclpb;
+ }
+
+ /* Since this is the begining, everybody is in free list */
+ aclQueue->aclq_free = first_aclpb;
+
+ aclQueue->aclq_nfree = maxThreads;
+ return 0;
+}
+
+/*
+ * Get a FREE acl pblock from the pool.
+ *
+ */
+static Acl_PBlock *
+acl__get_aclpb_from_pool ( )
+{
+ Acl_PBlock *aclpb = NULL;
+ Acl_PBlock *t_aclpb = NULL;
+
+
+ PR_Lock (aclQueue->aclq_lock );
+
+ /* Get the first aclpb from the FREE List */
+ aclpb = aclQueue->aclq_free;
+ if ( aclpb ) {
+ t_aclpb = aclpb->aclpb_next;
+ if ( t_aclpb ) t_aclpb->aclpb_prev = NULL;
+ aclQueue->aclq_free = t_aclpb;
+
+ /* make the this an orphon */
+ aclpb->aclpb_prev = aclpb->aclpb_next = NULL;
+
+ aclQueue->aclq_nfree--;
+ } else {
+ slapi_log_error ( SLAPI_LOG_ACL, plugin_name,
+ "Unable to find a free aclpb\n");
+ aclpb = acl__malloc_aclpb ();
+ }
+
+
+ /* Now move it to the FRONT of busy list */
+ t_aclpb = aclQueue->aclq_busy;
+ aclpb->aclpb_next = t_aclpb;
+ if ( t_aclpb ) t_aclpb->aclpb_prev = aclpb;
+ aclQueue->aclq_busy = aclpb;
+ aclQueue->aclq_nbusy++;
+
+ PR_Unlock (aclQueue->aclq_lock );
+
+ return aclpb;
+}
+/*
+ * Put the acl pblock into the FREE pool.
+ *
+ */
+static int
+acl__put_aclpb_back_to_pool ( Acl_PBlock *aclpb )
+{
+
+ Acl_PBlock *p_aclpb, *n_aclpb;
+
+ PR_Lock (aclQueue->aclq_lock );
+
+ /* Remove it from the busy list */
+ n_aclpb = aclpb->aclpb_next;
+ p_aclpb = aclpb->aclpb_prev;
+
+ if ( p_aclpb ) {
+ p_aclpb->aclpb_next = n_aclpb;
+ if ( n_aclpb ) n_aclpb->aclpb_prev = p_aclpb;
+ } else {
+ aclQueue->aclq_busy = n_aclpb;
+ if ( n_aclpb ) n_aclpb->aclpb_prev = NULL;
+ }
+ aclQueue->aclq_nbusy--;
+
+
+ /* Put back to the FREE list */
+ aclpb->aclpb_prev = NULL;
+ n_aclpb = aclQueue->aclq_free;
+ aclpb->aclpb_next = n_aclpb;
+ if ( n_aclpb ) n_aclpb->aclpb_prev = aclpb;
+ aclQueue->aclq_free = aclpb;
+ aclQueue->aclq_nfree++;
+
+ PR_Unlock (aclQueue->aclq_lock );
+
+ return 0;
+}
+
+/*
+ * Allocate the basic acl pb
+ *
+ */
+static Acl_PBlock *
+acl__malloc_aclpb ( )
+{
+ Acl_PBlock *aclpb = NULL;
+
+
+ aclpb = ( Acl_PBlock *) slapi_ch_calloc ( 1, sizeof ( Acl_PBlock) );
+
+ /* Now set the propert we need for ACL evaluations */
+ if ((aclpb->aclpb_proplist = PListNew(NULL)) == NULL) {
+ slapi_log_error (SLAPI_LOG_FATAL, plugin_name,
+ "Unable to allocate the aclprop PList\n");
+ return NULL;
+ }
+
+ if (PListInitProp(aclpb->aclpb_proplist, 0, DS_PROP_ACLPB, aclpb, 0) < 0) {
+ slapi_log_error(SLAPI_LOG_FATAL, plugin_name,
+ "Unable to set the ACL PBLOCK in the Plist\n");
+ return NULL;
+ }
+ if (PListInitProp(aclpb->aclpb_proplist, 0, DS_ATTR_USERDN, aclpb, 0) < 0) {
+ slapi_log_error(SLAPI_LOG_FATAL, plugin_name,
+ "Unable to set the USER DN in the Plist\n");
+ return NULL;
+ }
+ if (PListInitProp(aclpb->aclpb_proplist, 0, DS_ATTR_AUTHTYPE, aclpb, 0) < 0) {
+ slapi_log_error(SLAPI_LOG_FATAL, plugin_name,
+ "Unable to set the AUTH TYPE in the Plist\n");
+ return NULL;
+ }
+ if (PListInitProp(aclpb->aclpb_proplist, 0, DS_ATTR_ENTRY, aclpb, 0) < 0) {
+ slapi_log_error(SLAPI_LOG_FATAL, plugin_name,
+ "Unable to set the ENTRY TYPE in the Plist\n");
+ return NULL;
+ }
+
+ /*
+ * ACL_ATTR_IP and ACL_ATTR_DNS are initialized lazily in the
+ * IpGetter and DnsGetter functions.
+ * They are removed from the aclpb property list at acl__aclpb_done()
+ * time.
+ */
+
+ /* allocate the acleval struct */
+ aclpb->aclpb_acleval = (ACLEvalHandle_t *) ACL_EvalNew(NULL, NULL);
+ if (aclpb->aclpb_acleval == NULL) {
+ slapi_log_error(SLAPI_LOG_FATAL, plugin_name,
+ "Unable to allocate the acleval block\n");
+ return NULL;
+ }
+ /*
+ * This is a libaccess routine.
+ * Need to setup subject and resource property information
+ */
+
+ ACL_EvalSetSubject(NULL, aclpb->aclpb_acleval, aclpb->aclpb_proplist);
+
+ /* allocate some space for attr name */
+ aclpb->aclpb_Evalattr = (char *) slapi_ch_malloc (ACLPB_MAX_ATTR_LEN);
+
+ aclpb->aclpb_deny_handles = (aci_t **) slapi_ch_calloc (1,
+ ACLPB_INCR_LIST_HANDLES * sizeof (aci_t *));
+
+ aclpb->aclpb_allow_handles = (aci_t **) slapi_ch_calloc (1,
+ ACLPB_INCR_LIST_HANDLES * sizeof (aci_t *));
+
+ aclpb->aclpb_deny_handles_size = ACLPB_INCR_LIST_HANDLES;
+ aclpb->aclpb_allow_handles_size = ACLPB_INCR_LIST_HANDLES;
+
+ /* allocate the array for bases */
+ aclpb->aclpb_grpsearchbase = (char **)
+ slapi_ch_malloc (ACLPB_INCR_BASES * sizeof(char *));
+ aclpb->aclpb_grpsearchbase_size = ACLPB_INCR_BASES;
+ aclpb->aclpb_numof_bases = 0;
+
+ /* Make sure aclpb_search_base is initialized to NULL..tested elsewhere! */
+ aclpb->aclpb_search_base = NULL;
+
+ aclpb->aclpb_authorization_sdn = slapi_sdn_new ();
+ aclpb->aclpb_curr_entry_sdn = slapi_sdn_new();
+
+ aclpb->aclpb_aclContainer = acllist_get_aciContainer_new ();
+
+ /* hash table to store macro matched values from targets */
+ aclpb->aclpb_macro_ht = acl_ht_new();
+
+ return aclpb;
+
+}
+
+/* Initializes the aclpb */
+void
+acl_init_aclpb ( Slapi_PBlock *pb , Acl_PBlock *aclpb, const char *dn, int copy_from_aclcb)
+{
+ struct acl_cblock *aclcb = NULL;
+ char *authType;
+ void *conn;
+ unsigned long op_type;
+
+
+ if ( NULL == aclpb ) {
+ slapi_log_error ( SLAPI_LOG_FATAL, plugin_name, "acl_init_aclpb:No ACLPB\n");
+ return;
+ }
+
+ /* See if we have initialized already */
+ if (aclpb->aclpb_state & ACLPB_INITIALIZED) return;
+
+ slapi_pblock_get ( pb, SLAPI_OPERATION_TYPE, &op_type );
+ if ( op_type == SLAPI_OPERATION_BIND || op_type == SLAPI_OPERATION_UNBIND )
+ return;
+
+ /* We indicate the initialize here becuase, if something goes wrong, it's cleaned up
+ ** properly.
+ */
+ aclpb->aclpb_state = ACLPB_INITIALIZED;
+
+ /* We make an anonymous user a non null dn which is empty */
+ if (dn && *dn != '\0' )
+ slapi_sdn_set_ndn_byval ( aclpb->aclpb_authorization_sdn, dn );
+ else
+ slapi_sdn_set_ndn_byval ( aclpb->aclpb_authorization_sdn, "" );
+
+ /* reset scoped entry cache to be empty */
+ aclpb->aclpb_scoped_entry_anominfo.anom_e_nummatched = 0;
+
+ if (PListAssignValue(aclpb->aclpb_proplist, DS_ATTR_USERDN,
+ slapi_sdn_get_ndn(aclpb->aclpb_authorization_sdn), 0) < 0) {
+ slapi_log_error(SLAPI_LOG_FATAL, plugin_name,
+ "Unable to set the USER DN in the Plist\n");
+ return;
+ }
+ slapi_pblock_get ( pb, SLAPI_OPERATION_AUTHTYPE, &authType );
+ if (PListAssignValue(aclpb->aclpb_proplist, DS_ATTR_AUTHTYPE, authType, 0) < 0) {
+ slapi_log_error(SLAPI_LOG_FATAL, plugin_name,
+ "Unable to set the AUTH TYPE in the Plist\n");
+ return;
+ }
+ /* PKBxxx: We should be getting it from the OP struct */
+ slapi_pblock_get ( pb, SLAPI_CONN_CERT, &aclpb->aclpb_clientcert );
+
+ /* See if the we have already a cached info about user's group */
+ aclg_init_userGroup ( aclpb, dn, 0 /* get lock */ );
+
+ slapi_pblock_get( pb, SLAPI_BE_MAXNESTLEVEL, &aclpb->aclpb_max_nesting_level );
+ slapi_pblock_get( pb, SLAPI_SEARCH_SIZELIMIT, &aclpb->aclpb_max_member_sizelimit );
+ if ( aclpb->aclpb_max_member_sizelimit == 0 ) {
+ aclpb->aclpb_max_member_sizelimit = SLAPD_DEFAULT_LOOKTHROUGHLIMIT;
+ }
+ slapi_pblock_get( pb, SLAPI_OPERATION_TYPE, &aclpb->aclpb_optype );
+
+ aclpb->aclpb_signature = acl_get_aclsignature();
+ aclpb->aclpb_last_cache_result = 0;
+ aclpb->aclpb_pblock = pb;
+ PR_ASSERT ( aclpb->aclpb_pblock != NULL );
+
+ /* get the connection */
+ slapi_pblock_get ( pb, SLAPI_CONNECTION, &conn);
+ aclcb = (struct acl_cblock *) acl_get_ext ( ACL_EXT_CONNECTION, conn );
+
+ if (NULL == aclcb || NULL == aclcb->aclcb_lock) {
+ /* This could happen if the client is dead and we are in
+ ** process of abondoning this operation
+ */
+ slapi_log_error( SLAPI_LOG_ACL, plugin_name,
+ "No CONNECTION extension\n");
+
+ } else if ( aclcb->aclcb_state == -1 ) {
+ /* indicate that we need to update the cache */
+ aclpb->aclpb_state |= ACLPB_UPD_ACLCB_CACHE;
+ aclcb->aclcb_state = 0; /* Nore this is ACLCB and not ACLPB */
+
+ } else if ( copy_from_aclcb ){
+ char *cdn;
+ Slapi_DN *c_sdn; /* client SDN */
+
+ /* check if the operation is abandoned or not.*/
+ if ( slapi_op_abandoned ( pb ) ) {
+ return;
+ }
+
+ slapi_pblock_get ( pb, SLAPI_CONN_DN, &cdn ); /* We *must* free cdn! */
+ c_sdn = slapi_sdn_new_dn_passin( cdn );
+ PR_Lock ( aclcb->aclcb_lock );
+ /*
+ * since PR_Lock is taken,
+ * we can mark the connection extension ok to be destroyed.
+ */
+ if ( (aclcb->aclcb_aclsignature != acl_get_aclsignature()) ||
+ ( (NULL == cdn) && aclcb->aclcb_sdn ) ||
+ (cdn && (NULL == aclcb->aclcb_sdn )) ||
+ (cdn && aclcb->aclcb_sdn && ( 0 != slapi_sdn_compare ( c_sdn, aclcb->aclcb_sdn ) ))) {
+
+ /* cleanup the aclcb cache */
+ acl_clean_aclEval_context ( &aclcb->aclcb_eval_context, 0 /*clean*/ );
+ aclcb->aclcb_state = 0;
+ aclcb->aclcb_aclsignature = 0;
+ slapi_sdn_done ( aclcb->aclcb_sdn );
+ }
+ slapi_sdn_free ( &c_sdn );
+
+ /* COPY the cached information from ACLCB --> ACLPB */
+ if ( aclcb->aclcb_state & ACLCB_HAS_CACHED_EVALCONTEXT) {
+ acl_copyEval_context ( aclpb, &aclcb->aclcb_eval_context ,
+ &aclpb->aclpb_prev_opEval_context, 0 );
+ aclpb->aclpb_state |= ACLPB_HAS_ACLCB_EVALCONTEXT;
+ }
+ PR_Unlock ( aclcb->aclcb_lock );
+ }
+
+}
+
+/* Cleans up the aclpb */
+static void
+acl__done_aclpb ( struct acl_pblock *aclpb )
+{
+
+ int i;
+ int dump_aclpb_info = 0;
+ char *ds_attr_userdn=NULL; /* for finding userdn for freeing */
+ int rc=-1;
+ char *tmp_ptr=NULL;
+
+ /*
+ ** First, let's do some sanity checks to see if we have everything what
+ ** it should be.
+ */
+
+ /* Nothing needs to be cleaned up in this case */
+ if ( !aclpb->aclpb_state & ACLPB_INITIALIZED)
+ return;
+
+ /* Check the state */
+ if (aclpb->aclpb_state & ~ACLPB_STATE_ALL) {
+ slapi_log_error( SLAPI_LOG_FATAL, plugin_name,
+ "The aclpb.state value (%d) is incorrect. Exceeded the limit (%d)\n",
+ aclpb->aclpb_state, ACLPB_STATE_ALL);
+ dump_aclpb_info = 1;
+
+ }
+
+ /* acl__dump_stats ( aclpb, acl__get_aclpb_type(aclpb)); */
+
+ /* reset the usergroup cache */
+ aclg_reset_userGroup ( aclpb );
+
+ if ( aclpb->aclpb_res_type & ~ACLPB_RESTYPE_ALL ) {
+ slapi_log_error( SLAPI_LOG_FATAL, plugin_name,
+ "The aclpb res_type value (%d) has exceeded. Limit is (%d)\n",
+ aclpb->aclpb_res_type, ACLPB_RESTYPE_ALL, 0 );
+ dump_aclpb_info = 1;
+ }
+
+ if ( dump_aclpb_info ) {
+ const char *ndn;
+ slapi_log_error ( SLAPI_LOG_FATAL, plugin_name,
+ "ACLPB value is:%p\n", aclpb, 0,0 );
+
+ ndn = slapi_sdn_get_ndn ( aclpb->aclpb_curr_entry_sdn );
+ slapi_log_error ( SLAPI_LOG_FATAL, plugin_name, "curr_entry:%p num_entries:%d curr_dn:%p\n",
+ aclpb->aclpb_curr_entry ? (char *) aclpb->aclpb_curr_entry : "NULL",
+ aclpb->aclpb_num_entries,
+ ndn ? ndn : "NULL");
+
+ slapi_log_error ( SLAPI_LOG_FATAL, plugin_name, "Last attr:%p, Plist:%p acleval: %p\n",
+ aclpb->aclpb_Evalattr ? aclpb->aclpb_Evalattr : "NULL",
+ aclpb->aclpb_proplist ? (char *) aclpb->aclpb_proplist : "NULL",
+ aclpb->aclpb_acleval ? (char *) aclpb->aclpb_acleval : "NULL" );
+ }
+
+ /* Now Free the contents or clean it */
+ slapi_sdn_done ( aclpb->aclpb_curr_entry_sdn );
+ if (aclpb->aclpb_Evalattr)
+ aclpb->aclpb_Evalattr[0] = '\0';
+
+ /* deallocate the contents of the base array */
+ for (i=0; i < aclpb->aclpb_numof_bases; i++) {
+ if (aclpb->aclpb_grpsearchbase[i])
+ slapi_ch_free ( (void **)&aclpb->aclpb_grpsearchbase[i] );
+ }
+ aclpb->aclpb_numof_bases = 0;
+
+ acl_clean_aclEval_context ( &aclpb->aclpb_prev_opEval_context, 0 /*claen*/ );
+ acl_clean_aclEval_context ( &aclpb->aclpb_prev_entryEval_context, 0 /*clean*/ );
+ acl_clean_aclEval_context ( &aclpb->aclpb_curr_entryEval_context, 0/*clean*/ );
+
+ if ( aclpb->aclpb_client_entry ) slapi_entry_free ( aclpb->aclpb_client_entry );
+ aclpb->aclpb_client_entry = NULL;
+
+ slapi_sdn_done ( aclpb->aclpb_authorization_sdn );
+ aclpb->aclpb_pblock = NULL;
+
+ if ( aclpb->aclpb_search_base )
+ slapi_ch_free ( (void **) &aclpb->aclpb_search_base );
+ for ( i=0; i < aclpb->aclpb_num_deny_handles; i++ )
+ aclpb->aclpb_deny_handles[i] = NULL;
+ aclpb->aclpb_num_deny_handles = 0;
+
+ for ( i=0; i < aclpb->aclpb_num_allow_handles; i++ )
+ aclpb->aclpb_allow_handles[i] = NULL;
+ aclpb->aclpb_num_allow_handles = 0;
+
+ /* clear results cache */
+ memset((char*)aclpb->aclpb_cache_result, 0,
+ sizeof(struct result_cache)*aclpb->aclpb_last_cache_result);
+ aclpb->aclpb_last_cache_result = 0;
+ aclpb->aclpb_handles_index[0] = -1;
+ aclpb->aclpb_base_handles_index[0] = -1;
+
+ aclpb->aclpb_stat_acllist_scanned = 0;
+ aclpb->aclpb_stat_aclres_matched = 0;
+ aclpb->aclpb_stat_total_entries = 0;
+ aclpb->aclpb_stat_anom_list_scanned = 0;
+ aclpb->aclpb_stat_num_copycontext = 0;
+ aclpb->aclpb_stat_num_copy_attrs = 0;
+ aclpb->aclpb_stat_num_tmatched_acls = 0;
+
+ aclpb->aclpb_clientcert = NULL;
+ aclpb->aclpb_proxy = NULL;
+
+ acllist_done_aciContainer ( aclpb->aclpb_aclContainer );
+
+ /*
+ * Here, decide which things need to be freed/removed/whatever from the
+ * aclpb_proplist.
+ */
+
+ /*
+ * The DS_ATTR_DNS property contains the name of the client machine.
+ *
+ * The value pointed to by this property is stored in the pblock--it
+ * points to the SLAPI_CLIENT_DNS object. So, that memory will
+ * be freed elsewhere.
+ *
+ * It's removed here from the aclpb_proplist as it would be an error to
+ * allow it to persist in the aclpb which is an operation time thing.
+ * If we leave it here the next time this aclpb gets used, the DnsGetter
+ * is not called by LASDnsEval/ACL_GetAttribute() as it thinks the
+ * ACL_ATTR_DNS has already been initialized.
+ *
+ */
+
+ if ((rc = PListFindValue(aclpb->aclpb_proplist, ACL_ATTR_DNS,
+ (void **)&tmp_ptr, NULL)) > 0) {
+
+ PListDeleteProp(aclpb->aclpb_proplist, rc, NULL);
+ }
+
+ /*
+ * Remove the DS_ATTR_IP property from the property list.
+ * The value of this property is just the property pointer
+ * (an unsigned long) so that gets freed too when we delete the
+ * property.
+ * It's removed here from the aclpb_proplist as it would be an error to
+ * allow it to persist in the aclpb which is an operation time thing.
+ * If we leave it here the next time this aclpb gets used, the DnsGetter
+ * is not called by LASIpEval/ACL_GetAttribute() as it thinks the
+ * ACL_ATTR_IP has already been initialized.
+ */
+
+ if ((rc = PListFindValue(aclpb->aclpb_proplist, ACL_ATTR_IP,
+ (void **)&tmp_ptr, NULL)) > 0) {
+
+ PListDeleteProp(aclpb->aclpb_proplist, rc, NULL);
+ }
+
+ /*
+ * The DS_ATTR_USERDN value comes from aclpb_authorization_sdn.
+ * This memory
+ * is freed above using aclpb_authorization_sdn so we don't need to free it here
+ * before overwriting the old value.
+ */
+ PListAssignValue(aclpb->aclpb_proplist, DS_ATTR_USERDN, NULL, 0);
+
+ /*
+ * The DS_ATTR_AUTHTYPE value is a pointer into the pblock, so
+ * we do not need to free that memory before overwriting the value.
+ */
+ PListAssignValue(aclpb->aclpb_proplist, DS_ATTR_AUTHTYPE, NULL, 0);
+
+ /*
+ * DO NOT overwrite the aclpb pointer--it is initialized at malloc_aclpb
+ * time and is kept within the aclpb.
+ *
+ * PListAssignValue(aclpb->aclpb_proplist, DS_PROP_ACLPB, NULL, 0);
+ */
+
+ /*
+ * The DS_ATTR_ENTRY value was a pointer to the entry being evaluated
+ * by the ACL code. That entry comes from outside the context of
+ * the acl code and so is dealt with out there. Ergo, here we can just
+ * lose the pointer to that entry.
+ */
+ PListAssignValue(aclpb->aclpb_proplist, DS_ATTR_ENTRY, NULL, 0);
+
+ aclpb->aclpb_signature = 0;
+
+ /* reset scoped entry cache to be empty */
+ aclpb->aclpb_scoped_entry_anominfo.anom_e_nummatched = 0;
+
+ /* Free up any of the string values left in the macro ht and remove
+ * the entries.*/
+ acl_ht_free_all_entries_and_values(aclpb->aclpb_macro_ht);
+
+ /* Finally, set it to the no use state */
+ aclpb->aclpb_state = 0;
+
+}
+
+static char *
+acl__get_aclpb_type ( Acl_PBlock *aclpb )
+{
+
+ if (aclpb->aclpb_state & ACLPB_TYPE_PROXY)
+ return ACLPB_TYPE_PROXY_STR;
+
+ return ACLPB_TYPE_MAIN_STR;
+}
+static void
+acl__dump_stats ( struct acl_pblock *aclpb , const char *block_type)
+{
+ int connid = 0;
+ int opid = 0;
+ Slapi_PBlock *pb = NULL;
+
+ pb = aclpb->aclpb_pblock;
+ if ( pb ) {
+ slapi_pblock_get ( pb, SLAPI_CONN_ID, &connid );
+ slapi_pblock_get ( pb, SLAPI_OPERATION_ID, &opid );
+ }
+
+ /* DUMP STAT INFO */
+ slapi_log_error( SLAPI_LOG_ACL, plugin_name,
+ "**** ACL OPERATION STAT BEGIN ( aclpb:%p Block type: %s): Conn:%d Operation:%d *******\n",
+ aclpb, block_type, connid, opid );
+ slapi_log_error( SLAPI_LOG_ACL, plugin_name, "\tNumber of entries scanned: %d\n",
+ aclpb->aclpb_stat_total_entries);
+ slapi_log_error( SLAPI_LOG_ACL, plugin_name, "\tNumber of times ACL List scanned: %d\n",
+ aclpb->aclpb_stat_acllist_scanned);
+ slapi_log_error( SLAPI_LOG_ACL, plugin_name, "\tNumber of ACLs with target matched:%d\n",
+ aclpb->aclpb_stat_num_tmatched_acls);
+ slapi_log_error( SLAPI_LOG_ACL, plugin_name, "\tNumber of times acl resource matched:%d\n",
+ aclpb->aclpb_stat_aclres_matched);
+ slapi_log_error( SLAPI_LOG_ACL, plugin_name, "\tNumber of times ANOM list scanned:%d\n",
+ aclpb->aclpb_stat_anom_list_scanned);
+ slapi_log_error( SLAPI_LOG_ACL, plugin_name, "\tNumber of times Context was copied:%d\n",
+ aclpb->aclpb_stat_num_copycontext);
+ slapi_log_error( SLAPI_LOG_ACL, plugin_name, "\tNumber of times Attrs was copied:%d\n",
+ aclpb->aclpb_stat_num_copy_attrs);
+ slapi_log_error( SLAPI_LOG_ACL, plugin_name, " **** ACL OPERATION STAT END *******\n");
+}
+/****************************************************************************/
+/* E N D */
+/****************************************************************************/
+
diff --git a/ldap/servers/plugins/acl/aclanom.c b/ldap/servers/plugins/acl/aclanom.c
new file mode 100644
index 00000000..71b0c68a
--- /dev/null
+++ b/ldap/servers/plugins/acl/aclanom.c
@@ -0,0 +1,536 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+#include "acl.h"
+
+/************************************************************************
+Anonymous profile
+**************************************************************************/
+
+struct anom_targetacl {
+ int anom_type; /* defines for anom types same as aci_type */
+ int anom_access;
+ Slapi_DN *anom_target; /* target of the ACL */
+ Slapi_Filter *anom_filter; /* targetfilter part */
+ char **anom_targetAttrs; /* list of attrs */
+};
+
+
+struct anom_profile {
+ short anom_signature;
+ short anom_numacls;
+ struct anom_targetacl anom_targetinfo[ACL_ANOM_MAX_ACL];
+};
+
+static struct anom_profile *acl_anom_profile = NULL;
+
+static PRRWLock *anom_rwlock = NULL;
+#define ANOM_LOCK_READ() PR_RWLock_Rlock (anom_rwlock )
+#define ANOM_UNLOCK_READ() PR_RWLock_Unlock (anom_rwlock )
+#define ANOM_LOCK_WRITE() PR_RWLock_Wlock (anom_rwlock )
+#define ANOM_UNLOCK_WRITE() PR_RWLock_Unlock (anom_rwlock )
+
+
+static void __aclanom__del_profile ();
+
+/*
+ * aclanom_init ();
+ * Generate a profile for the anonymous user. We can use this profile
+ * later to determine what resources the client is allowed to.
+ *
+ * Dependency:
+ * Before calling this, it is assumed that all the ACLs have been read
+ * and parsed.
+ *
+ * We will go thru all the ACL and pick the ANYONE ACL and generate the anom
+ * profile.
+ *
+ */
+int
+aclanom_init ()
+{
+
+ acl_anom_profile = (struct anom_profile * )
+ slapi_ch_calloc (1, sizeof ( struct anom_profile ) );
+
+ if (( anom_rwlock = PR_NewRWLock( PR_RWLOCK_RANK_NONE,"ANOM LOCK") ) == NULL ) {
+ slapi_log_error( SLAPI_LOG_FATAL, plugin_name,
+ "Failed in getting the ANOM rwlock\n" );
+ return 1;
+ }
+ return 0;
+}
+
+/*
+ * Depending on the context, this routine may need to take the
+ * acicache read lock.
+*/
+void
+aclanom_gen_anomProfile (acl_lock_flag_t lock_flag)
+{
+ aci_t *aci = NULL;
+ int i;
+ Targetattr **srcattrArray;
+ Targetattr *attr;
+ struct anom_profile *a_profile;
+ PRUint32 cookie;
+
+ PR_ASSERT( lock_flag == DO_TAKE_ACLCACHE_READLOCK ||
+ lock_flag == DONT_TAKE_ACLCACHE_READLOCK);
+
+ /*
+ * This routine requires two locks:
+ * the one for the global cache in acllist_acicache_READ_LOCK() and
+ * the one for the anom profile.
+ * They _must_ be taken in the order presented here or there
+ * is a deadlock scenario with acllist_remove_aci_needsLock() which
+ * takes them is this order.
+ */
+
+ if ( lock_flag == DO_TAKE_ACLCACHE_READLOCK ) {
+ acllist_acicache_READ_LOCK();
+ }
+ ANOM_LOCK_WRITE ();
+ a_profile = acl_anom_profile;
+
+ if ( (!acl_get_aclsignature()) || ( !a_profile) ||
+ (a_profile->anom_signature == acl_get_aclsignature()) ) {
+ ANOM_UNLOCK_WRITE ();
+ if ( lock_flag == DO_TAKE_ACLCACHE_READLOCK ) {
+ acllist_acicache_READ_UNLOCK();
+ }
+ return;
+ }
+
+ /* D0 we have one already. If we do, then clean it up */
+ __aclanom__del_profile();
+
+ /* We have a new signature now */
+ a_profile->anom_signature = acl_get_aclsignature();
+
+ slapi_log_error(SLAPI_LOG_ACL, plugin_name, "GENERATING ANOM USER PROFILE\n", 0,0,0);
+ /*
+ ** Go thru the ACL list and find all the ACLs which apply to the
+ ** anonymous user i.e anyone. we can generate a profile for that.
+ ** We will llok at the simple case i.e it matches
+ ** cases not handled:
+ ** 1) When there is a mix if rule types ( allows & denies )
+ **
+ */
+
+ aci = acllist_get_first_aci ( NULL, &cookie );
+ while ( aci ) {
+ int a_numacl;
+ struct slapi_filter *f;
+ char **destattrArray;
+
+
+ /*
+ * We must not have a rule like: deny ( all ) userdn != "xyz"
+ * or groupdn !=
+ */
+ if ( (aci->aci_type & ACI_HAS_DENY_RULE) &&
+ ( (aci->aci_type & ACI_CONTAIN_NOT_USERDN ) ||
+ (aci->aci_type & ACI_CONTAIN_NOT_GROUPDN) ||
+ (aci->aci_type & ACI_CONTAIN_NOT_ROLEDN)) ){
+ slapi_log_error(SLAPI_LOG_ACL, plugin_name,
+ "CANCELLING ANOM USER PROFILE BECAUSE OF DENY RULE\n", 0,0,0);
+ goto cleanup;
+ }
+
+ /* Must be a anyone rule */
+ if ( aci->aci_elevel != ACI_ELEVEL_USERDN_ANYONE ) {
+ aci = acllist_get_next_aci ( NULL, aci, &cookie);
+ continue;
+ }
+ if (! (aci->aci_access & ( SLAPI_ACL_READ | SLAPI_ACL_SEARCH)) ) {
+ aci = acllist_get_next_aci ( NULL, aci, &cookie);
+ continue;
+ }
+ /* If the rule has anything other than userdn = "ldap:///anyone"
+ ** let's not consider complex rules - let's make this lean.
+ */
+ if ( aci->aci_ruleType & ~ACI_USERDN_RULE ){
+ slapi_log_error(SLAPI_LOG_ACL, plugin_name,
+ "CANCELLING ANOM USER PROFILE BECAUSE OF COMPLEX RULE\n", 0,0,0);
+ goto cleanup;
+ }
+
+ /* Must not be a or have a
+ ** 1 ) DENY RULE 2) targetfilter
+ ** 3) no target pattern ( skip monitor acl )
+ */
+ if ( aci->aci_type & ( ACI_HAS_DENY_RULE | ACI_TARGET_PATTERN |
+ ACI_TARGET_NOT | ACI_TARGET_FILTER_NOT )) {
+ const char *dn = slapi_sdn_get_dn ( aci->aci_sdn );
+
+ /* see if this is a monitor acl */
+ if (( strcasecmp ( dn, "cn=monitor") == 0 ) ||
+ ( strcasecmp ( dn, "cn=monitor,cn=ldbm") == 0 )) {
+ aci = acllist_get_next_aci ( NULL, aci, &cookie);
+ continue;
+ } else {
+ /* clean up before leaving */
+ slapi_log_error(SLAPI_LOG_ACL, plugin_name,
+ "CANCELLING ANOM USER PROFILE 1\n", 0,0,0);
+ goto cleanup;
+ }
+
+ }
+
+ /* Now we have an ALLOW ACL which applies to anyone */
+ a_numacl = a_profile->anom_numacls++;
+
+ if ( a_profile->anom_numacls == ACL_ANOM_MAX_ACL ) {
+ slapi_log_error(SLAPI_LOG_ACL, plugin_name, "CANCELLING ANOM USER PROFILE 2\n", 0,0,0);
+ goto cleanup;
+ }
+
+ if ( (f = aci->target) != NULL ) {
+ char *avaType;
+ struct berval *avaValue;
+ slapi_filter_get_ava ( f, &avaType, &avaValue );
+
+ a_profile->anom_targetinfo[a_numacl].anom_target =
+ slapi_sdn_new_dn_byval ( avaValue->bv_val );
+ } else {
+ a_profile->anom_targetinfo[a_numacl].anom_target =
+ slapi_sdn_dup ( aci->aci_sdn );
+ }
+
+ a_profile->anom_targetinfo[a_numacl].anom_filter = NULL;
+ if ( aci->targetFilterStr )
+ a_profile->anom_targetinfo[a_numacl].anom_filter = slapi_str2filter ( aci->targetFilterStr );
+
+ i = 0;
+ srcattrArray = aci->targetAttr;
+ while ( srcattrArray[i])
+ i++;
+
+ a_profile->anom_targetinfo[a_numacl].anom_targetAttrs =
+ (char **) slapi_ch_calloc ( 1, (i+1) * sizeof(char *));
+
+ srcattrArray = aci->targetAttr;
+ destattrArray = a_profile->anom_targetinfo[a_numacl].anom_targetAttrs;
+
+ i = 0;
+ while ( srcattrArray[i] ) {
+ attr = srcattrArray[i];
+ if ( attr->attr_type & ACL_ATTR_FILTER ) {
+ /* Do'nt want to support these kind now */
+ destattrArray[i] = NULL;
+ /* clean up before leaving */
+ __aclanom__del_profile ();
+ slapi_log_error(SLAPI_LOG_ACL, plugin_name,
+ "CANCELLING ANOM USER PROFILE 3\n", 0,0,0);
+ goto cleanup;
+ }
+
+ destattrArray[i] = slapi_ch_strdup ( attr->u.attr_str );
+ i++;
+ }
+
+ destattrArray[i] = NULL;
+
+ aclutil_print_aci ( aci, "anom" );
+ /* Here we are storing att the info from the acls. However
+ ** we are only interested in a few things like ACI_TARGETATTR_NOT.
+ */
+ a_profile->anom_targetinfo[a_numacl].anom_type = aci->aci_type;
+ a_profile->anom_targetinfo[a_numacl].anom_access = aci->aci_access;
+
+ aci = acllist_get_next_aci ( NULL, aci, &cookie);
+ }
+
+ ANOM_UNLOCK_WRITE ();
+ if ( lock_flag == DO_TAKE_ACLCACHE_READLOCK ) {
+ acllist_acicache_READ_UNLOCK();
+ }
+ return;
+
+cleanup:
+ __aclanom__del_profile ();
+ ANOM_UNLOCK_WRITE ();
+ if ( lock_flag == DO_TAKE_ACLCACHE_READLOCK ) {
+ acllist_acicache_READ_UNLOCK();
+ }
+}
+
+
+void
+aclanom_invalidateProfile ()
+{
+ ANOM_LOCK_WRITE();
+ if ( acl_anom_profile && acl_anom_profile->anom_numacls )
+ acl_anom_profile->anom_signature = 0;
+ ANOM_UNLOCK_WRITE();
+
+
+}
+
+/*
+ * __aclanom_del_profile
+ *
+ * Cleanup the anonymous user's profile we have.
+ *
+ * ASSUMPTION: A WRITE LOCK HAS BEEN OBTAINED
+ *
+ */
+static void
+__aclanom__del_profile (void)
+{
+ int i;
+ struct anom_profile *a_profile;
+
+
+ if ( (a_profile = acl_anom_profile) == NULL ) {
+ return;
+ }
+
+ for ( i=0; i < a_profile->anom_numacls; i++ ) {
+ int j = 0;
+ char **destArray = a_profile->anom_targetinfo[i].anom_targetAttrs;
+
+ /* Deallocate target */
+ slapi_sdn_free ( &a_profile->anom_targetinfo[i].anom_target );
+
+ /* Deallocate filter */
+ if ( a_profile->anom_targetinfo[i].anom_filter )
+ slapi_filter_free ( a_profile->anom_targetinfo[i].anom_filter, 1 );
+
+ /* Deallocate attrs */
+ if ( destArray ) {
+ while ( destArray[j] ) {
+ slapi_ch_free ( (void **) &destArray[j] );
+ j++;
+ }
+ slapi_ch_free ( (void **) &destArray );
+ }
+ a_profile->anom_targetinfo[i].anom_targetAttrs = NULL;
+ a_profile->anom_targetinfo[i].anom_type = 0;
+ a_profile->anom_targetinfo[i].anom_access = 0;
+ }
+ a_profile->anom_numacls = 0;
+
+ /* Don't clean the signatue */
+}
+
+/*
+ * This routine sets up a "context" for evaluation of access control
+ * on a given entry for an anonymous user.
+ * It just factors out the scope and targetfilter info into a list
+ * of indices of the global anom profile list, that apply to this
+ * entry, and stores them in the aclpb.
+ * It's use relies on the way that access control is checked in the mailine search
+ * code in the core server, namely: check filter, check entry, then check each
+ * attribute. So, we call this in acl_access_allowed() before calling
+ * aclanom_match_profile()--therafter, aclanom_match_profile() uses the
+ * context to evaluate access to the entry and attributes.
+ *
+ * If there are no anom profiles, or the anom profiles get cancelled
+ * due to complex anon acis, then that's OK, aclanom_match_profile()
+ * returns -1 and the mainline acl code kicks in.
+ *
+ * The lifetime of this context info is the time it takes to check
+ * access control for all parts of this entry (filter, entry, attributes).
+ * So, if for an example an entry changes and a given anom profile entry
+ * no longer applies, we will not notice until the next round of access
+ * control checking on the entry--this is acceptable.
+ *
+ * The gain on doing this factoring in the following type of search
+ * was approx 6%:
+ * anon bind, 20 threads, exact match, ~20 attributes returned,
+ * (searchrate & DirectoryMark).
+ *
+*/
+void
+aclanom_get_suffix_info(Slapi_Entry *e,
+ struct acl_pblock *aclpb ) {
+ int i;
+ char *ndn = NULL;
+ Slapi_DN *e_sdn;
+ const char *aci_ndn;
+ int populate = 0;
+ struct scoped_entry_anominfo *s_e_anominfo =
+ &aclpb->aclpb_scoped_entry_anominfo;
+
+ ANOM_LOCK_READ ();
+
+ s_e_anominfo->anom_e_nummatched=0;
+
+ ndn = slapi_entry_get_ndn ( e ) ;
+ e_sdn= slapi_entry_get_sdn ( e ) ;
+ for (i=acl_anom_profile->anom_numacls-1; i >= 0; i-- ) {
+ aci_ndn = slapi_sdn_get_ndn (acl_anom_profile->anom_targetinfo[i].anom_target);
+ if (!slapi_sdn_issuffix(e_sdn,acl_anom_profile->anom_targetinfo[i].anom_target)
+ || (!slapi_is_rootdse(ndn) && slapi_is_rootdse(aci_ndn)))
+ continue;
+ if ( acl_anom_profile->anom_targetinfo[i].anom_filter ) {
+ if ( slapi_vattr_filter_test( aclpb->aclpb_pblock, e,
+ acl_anom_profile->anom_targetinfo[i].anom_filter,
+ 0 /*don't do acess chk*/) != 0)
+ continue;
+ }
+ s_e_anominfo->anom_e_targetInfo[s_e_anominfo->anom_e_nummatched]=i;
+ s_e_anominfo->anom_e_nummatched++;
+ }
+ ANOM_UNLOCK_READ ();
+}
+
+
+/*
+ * aclanom_match_profile
+ * Look at the anonymous profile and see if we can use it or not.
+ *
+ *
+ * Inputs:
+ * Slapi_Pblock - The Pblock
+ * Slapi_Entry *e - The entry for which we are asking permission.
+ * char *attr - Attribute name
+ * int access - access type
+ *
+ * Return:
+ * LDAP_SUCCESS ( 0 ) - acess is allowed.
+ * LDAP_INSUFFICIENT_ACCESS (50 ) - access denied.
+ * -1 - didn't match the targets
+ *
+ * Assumptions:
+ * The caller of this module has to make sure that the client is
+ * an anonymous client.
+ */
+int
+aclanom_match_profile (Slapi_PBlock *pb, struct acl_pblock *aclpb, Slapi_Entry *e,
+ char *attr, int access )
+{
+
+ struct anom_profile *a_profile;
+ int result, i, k;
+ char **destArray;
+ int tmatched = 0;
+ char ebuf[ BUFSIZ ];
+ int loglevel;
+ struct scoped_entry_anominfo *s_e_anominfo =
+ &aclpb->aclpb_scoped_entry_anominfo;
+
+ loglevel = slapi_is_loglevel_set(SLAPI_LOG_ACL) ? SLAPI_LOG_ACL : SLAPI_LOG_ACLSUMMARY;
+
+ /* WE are only interested for READ/SEARCH */
+ if ( !(access & ( SLAPI_ACL_SEARCH | SLAPI_ACL_READ)) )
+ return -1;
+
+ /* If we are here means, the client is doing a anonymous read/search */
+ if ((a_profile = acl_anom_profile) == NULL ) {
+ return -1;
+ }
+
+ ANOM_LOCK_READ ();
+ /* Check the signature first */
+ if ( a_profile->anom_signature != acl_get_aclsignature () ) {
+ /* Need to regenrate the signature.
+ * Need a WRITE lock to generate the anom profile -
+ * which is obtained in acl__gen_anom_user_profile (). Since
+ * I don't have upgrade lock -- I have to do this way.
+ */
+ ANOM_UNLOCK_READ ();
+ aclanom_gen_anomProfile (DO_TAKE_ACLCACHE_READLOCK);
+ aclanom_get_suffix_info(e, aclpb );
+ ANOM_LOCK_READ ();
+ }
+
+ /* doing this early saves use a malloc/free/normalize cost */
+ if ( !a_profile->anom_numacls ) {
+ ANOM_UNLOCK_READ ();
+ return -1;
+ }
+
+ result = LDAP_INSUFFICIENT_ACCESS;
+
+ for ( k=0; k<s_e_anominfo->anom_e_nummatched; k++ ) {
+ short matched = 0;
+ short j = 0;
+
+ i = s_e_anominfo->anom_e_targetInfo[k];
+
+ /* Check for right */
+ if ( !(a_profile->anom_targetinfo[i].anom_access & access) )
+ continue;
+
+ /*
+ * XXX rbyrne Don't really understand the role of this
+ * but not causing any obvious bugs...get back to it.
+ */
+ tmatched++;
+
+ if ( attr == NULL ) {
+ result = LDAP_SUCCESS;
+ break;
+ }
+
+ destArray = a_profile->anom_targetinfo[i].anom_targetAttrs;
+ while ( destArray[j] ) {
+ if ( strcasecmp ( destArray[j], "*") == 0 ||
+ slapi_attr_type_cmp ( attr, destArray[j], 1 ) == 0 ) {
+ matched = 1;
+ break;
+ }
+ j++;
+ }
+
+ if ( a_profile->anom_targetinfo[i].anom_type & ACI_TARGET_ATTR_NOT )
+ result = matched ? LDAP_INSUFFICIENT_ACCESS : LDAP_SUCCESS;
+ else
+ result = matched ? LDAP_SUCCESS : LDAP_INSUFFICIENT_ACCESS;
+
+ if ( result == LDAP_SUCCESS )
+ break;
+ } /* for */
+
+ if ( slapi_is_loglevel_set(loglevel) ) {
+ char *ndn = NULL;
+ Slapi_Operation *op = NULL;
+
+ ndn = slapi_entry_get_ndn ( e ) ;
+ slapi_pblock_get(pb, SLAPI_OPERATION, &op);
+
+ if ( result == LDAP_SUCCESS) {
+ const char *aci_ndn;
+ aci_ndn = slapi_sdn_get_ndn (acl_anom_profile->anom_targetinfo[i].anom_target);
+
+ slapi_log_error(loglevel, plugin_name,
+ "conn=%d op=%d: Allow access on entry(%s).attr(%s) to anonymous: acidn=\"%s\"\n",
+ op->o_connid, op->o_opid,
+ escape_string_with_punctuation(ndn, ebuf),
+ attr ? attr:"NULL",
+ escape_string_with_punctuation(aci_ndn, ebuf));
+ } else {
+ slapi_log_error(loglevel, plugin_name,
+ "conn=%d op=%d: Deny access on entry(%s).attr(%s) to anonymous\n",
+ op->o_connid, op->o_opid,
+ escape_string_with_punctuation(ndn, ebuf), attr ? attr:"NULL" );
+ }
+ }
+
+ ANOM_UNLOCK_READ ();
+ if ( tmatched == 0)
+ return -1;
+ else
+ return result;
+
+}
+int
+aclanom_is_client_anonymous ( Slapi_PBlock *pb )
+{
+ char *clientDn;
+
+
+ slapi_pblock_get ( pb, SLAPI_REQUESTOR_DN, &clientDn );
+ if (acl_anom_profile->anom_numacls &&
+ acl_anom_profile->anom_signature &&
+ (( NULL == clientDn) || (clientDn && *clientDn == '\0')) )
+ return 1;
+
+ return 0;
+}
+
diff --git a/ldap/servers/plugins/acl/acldllmain.c b/ldap/servers/plugins/acl/acldllmain.c
new file mode 100644
index 00000000..21ab60c4
--- /dev/null
+++ b/ldap/servers/plugins/acl/acldllmain.c
@@ -0,0 +1,128 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+#include "acl.h"
+
+#ifdef _WIN32
+/* Lifted from Q125688
+ * How to Port a 16-bit DLL to a Win32 DLL
+ * on the MSVC 4.0 CD
+ */
+BOOL WINAPI DllMain (HANDLE hModule, DWORD fdwReason, LPVOID lpReserved)
+{
+ WSADATA wsadata;
+
+ switch (fdwReason)
+ {
+ case DLL_PROCESS_ATTACH:
+ /* Code from LibMain inserted here. Return TRUE to keep the
+ DLL loaded or return FALSE to fail loading the DLL.
+
+ You may have to modify the code in your original LibMain to
+ account for the fact that it may be called more than once.
+ You will get one DLL_PROCESS_ATTACH for each process that
+ loads the DLL. This is different from LibMain which gets
+ called only once when the DLL is loaded. The only time this
+ is critical is when you are using shared data sections.
+ If you are using shared data sections for statically
+ allocated data, you will need to be careful to initialize it
+ only once. Check your code carefully.
+
+ Certain one-time initializations may now need to be done for
+ each process that attaches. You may also not need code from
+ your original LibMain because the operating system may now
+ be doing it for you.
+ */
+ /*
+ * 16 bit code calls UnlockData()
+ * which is mapped to UnlockSegment in windows.h
+ * in 32 bit world UnlockData is not defined anywhere
+ * UnlockSegment is mapped to GlobalUnfix in winbase.h
+ * and the docs for both UnlockSegment and GlobalUnfix say
+ * ".. function is oboslete. Segments have no meaning
+ * in the 32-bit environment". So we do nothing here.
+ */
+
+ if( errno = WSAStartup(0x0101, &wsadata ) != 0 )
+ return FALSE;
+
+ break;
+
+ case DLL_THREAD_ATTACH:
+ /* Called each time a thread is created in a process that has
+ already loaded (attached to) this DLL. Does not get called
+ for each thread that exists in the process before it loaded
+ the DLL.
+
+ Do thread-specific initialization here.
+ */
+ break;
+
+ case DLL_THREAD_DETACH:
+ /* Same as above, but called when a thread in the process
+ exits.
+
+ Do thread-specific cleanup here.
+ */
+ break;
+
+ case DLL_PROCESS_DETACH:
+ /* Code from _WEP inserted here. This code may (like the
+ LibMain) not be necessary. Check to make certain that the
+ operating system is not doing it for you.
+ */
+ WSACleanup();
+
+ break;
+ }
+ /* The return value is only used for DLL_PROCESS_ATTACH; all other
+ conditions are ignored. */
+ return TRUE; // successful DLL_PROCESS_ATTACH
+}
+#else
+int CALLBACK
+LibMain( HINSTANCE hinst, WORD wDataSeg, WORD cbHeapSize, LPSTR lpszCmdLine )
+{
+ /*UnlockData( 0 );*/
+ return( 1 );
+}
+#endif
+
+#ifdef LDAP_DEBUG
+#ifndef _WIN32
+#include <stdarg.h>
+#include <stdio.h>
+
+void LDAPDebug( int level, char* fmt, ... )
+{
+ static char debugBuf[1024];
+
+ if (module_ldap_debug && (*module_ldap_debug & level))
+ {
+ va_list ap;
+ va_start (ap, fmt);
+ _snprintf (debugBuf, sizeof(debugBuf), fmt, ap);
+ va_end (ap);
+
+ OutputDebugString (debugBuf);
+ }
+}
+#endif
+#endif
+
+#ifndef _WIN32
+
+/* The 16-bit version of the RTL does not implement perror() */
+
+#include <stdio.h>
+
+void perror( const char *msg )
+{
+ char buf[128];
+ wsprintf( buf, "%s: error %d\n", msg, WSAGetLastError()) ;
+ OutputDebugString( buf );
+}
+
+#endif
diff --git a/ldap/servers/plugins/acl/acleffectiverights.c b/ldap/servers/plugins/acl/acleffectiverights.c
new file mode 100644
index 00000000..1e250e96
--- /dev/null
+++ b/ldap/servers/plugins/acl/acleffectiverights.c
@@ -0,0 +1,674 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2004 Netscape Communications Corporation
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+
+#include "acl.h"
+
+static int
+_ger_g_permission_granted ( Slapi_PBlock *pb, Slapi_Entry *e, char **errbuf )
+{
+ char *proxydn = NULL;
+ Slapi_DN *requestor_sdn, *entry_sdn;
+ char *errtext = NULL;
+ int isroot;
+ int rc;
+
+ /*
+ * Theorically, we should check if the entry has "g"
+ * permission granted to the requestor. If granted,
+ * allows the effective rights on that entry and its
+ * attributes within the entry to be returned for
+ * ANY subject.
+ *
+ * "G" permission granting has not been implemented yet,
+ * the current release assumes that "g" permission be
+ * granted to root and owner of any entry.
+ */
+
+ /*
+ * The requestor may be either the bind dn or a proxy dn
+ */
+ acl_get_proxyauth_dn ( pb, &proxydn, &errtext );
+ if ( proxydn != NULL )
+ {
+ requestor_sdn = slapi_sdn_new_dn_passin ( proxydn );
+ }
+ else
+ {
+ requestor_sdn = &(pb->pb_op->o_sdn);
+ }
+ if ( slapi_sdn_get_dn (requestor_sdn) == NULL )
+ {
+ slapi_log_error (SLAPI_LOG_ACL, plugin_name,
+ "_ger_g_permission_granted: anonymous has no g permission\n" );
+ rc = LDAP_INSUFFICIENT_ACCESS;
+ goto bailout;
+ }
+ isroot = slapi_dn_isroot ( slapi_sdn_get_dn (requestor_sdn) );
+ if ( isroot )
+ {
+ /* Root has "g" permission on any entry */
+ rc = LDAP_SUCCESS;
+ goto bailout;
+ }
+
+ entry_sdn = slapi_entry_get_sdn ( e );
+ if ( entry_sdn == NULL || slapi_sdn_get_dn (entry_sdn) == NULL )
+ {
+ rc = LDAP_SUCCESS;
+ goto bailout;
+ }
+
+ if ( slapi_sdn_compare ( requestor_sdn, entry_sdn ) == 0 )
+ {
+ /* Owner has "g" permission on his own entry */
+ rc = LDAP_SUCCESS;
+ goto bailout;
+ }
+
+ aclutil_str_appened ( errbuf, "get-effective-rights: requestor has no g permission on the entry" );
+ slapi_log_error (SLAPI_LOG_ACL, plugin_name,
+ "_ger_g_permission_granted: %s\n", *errbuf);
+ rc = LDAP_INSUFFICIENT_ACCESS;
+
+bailout:
+ if ( proxydn )
+ {
+ /* The ownership of proxydn has passed to requestor_sdn */
+ slapi_sdn_free ( &requestor_sdn );
+ }
+ return rc;
+}
+
+static int
+_ger_parse_control ( Slapi_PBlock *pb, char **subjectndn, int *iscritical, char **errbuf )
+{
+ LDAPControl **requestcontrols;
+ struct berval *subjectber;
+ BerElement *ber;
+
+ if (NULL == subjectndn)
+ {
+ return LDAP_OPERATIONS_ERROR;
+ }
+
+ *subjectndn = NULL;
+
+ /*
+ * Get the control
+ */
+ slapi_pblock_get ( pb, SLAPI_REQCONTROLS, (void *) &requestcontrols );
+ slapi_control_present ( requestcontrols,
+ LDAP_CONTROL_GET_EFFECTIVE_RIGHTS,
+ &subjectber,
+ iscritical );
+ if ( subjectber == NULL || subjectber->bv_val == NULL ||
+ subjectber->bv_len == 0 )
+ {
+ aclutil_str_appened ( errbuf, "get-effective-rights: missing subject" );
+ slapi_log_error (SLAPI_LOG_FATAL, plugin_name, "%s\n", *errbuf );
+ return LDAP_INVALID_SYNTAX;
+ }
+
+ if ( strncasecmp ( "dn:", subjectber->bv_val, 3 ) == 0 )
+ {
+ /*
+ * This is a non-standard support to allow the subject being a plain
+ * or base64 encoding string. Hence users using -J option in
+ * ldapsearch don't have to do BER encoding for the subject.
+ */
+ *subjectndn = slapi_ch_malloc ( subjectber->bv_len + 1 );
+ strncpy ( *subjectndn, subjectber->bv_val, subjectber->bv_len );
+ *(*subjectndn + subjectber->bv_len) = '\0';
+ }
+ else
+ {
+ ber = ber_init (subjectber);
+ if ( ber == NULL )
+ {
+ aclutil_str_appened ( errbuf, "get-effective-rights: ber_init failed for the subject" );
+ slapi_log_error (SLAPI_LOG_FATAL, plugin_name, "%s\n", *errbuf );
+ return LDAP_OPERATIONS_ERROR;
+ }
+ /* "a" means to allocate storage as needed for octet string */
+ if ( ber_scanf (ber, "a", subjectndn) == LBER_ERROR )
+ {
+ aclutil_str_appened ( errbuf, "get-effective-rights: invalid ber tag in the subject" );
+ slapi_log_error (SLAPI_LOG_FATAL, plugin_name, "%s\n", *errbuf );
+ ber_free ( ber, 1 );
+ return LDAP_INVALID_SYNTAX;
+ }
+ ber_free ( ber, 1 );
+ }
+
+ /*
+ * The current implementation limits the subject to authorization ID
+ * (see section 9 of RFC 2829) only. It also only supports the "dnAuthzId"
+ * flavor, which looks like "dn:<DN>" where null <DN> is for anonymous.
+ */
+ if ( NULL == *subjectndn || strlen (*subjectndn) < 3 ||
+ strncasecmp ( "dn:", *subjectndn, 3 ) != 0 )
+ {
+ aclutil_str_appened ( errbuf, "get-effective-rights: subject is not dnAuthzId" );
+ slapi_log_error (SLAPI_LOG_FATAL, plugin_name, "%s\n", *errbuf );
+ return LDAP_INVALID_SYNTAX;
+ }
+
+ strcpy ( *subjectndn, *subjectndn + 3 );
+ slapi_dn_normalize ( *subjectndn );
+ return LDAP_SUCCESS;
+}
+
+static void
+_ger_release_gerpb (
+ Slapi_PBlock **gerpb,
+ void **aclcb, /* original aclcb */
+ Slapi_PBlock *pb /* original pb */
+ )
+{
+ if ( *gerpb )
+ {
+ /* Return conn to pb */
+ slapi_pblock_set ( *gerpb, SLAPI_CONNECTION, NULL );
+ slapi_pblock_destroy ( *gerpb );
+ *gerpb = NULL;
+ }
+
+ /* Put the original aclcb back to pb */
+ if ( *aclcb )
+ {
+ Connection *conn = NULL;
+ slapi_pblock_get ( pb, SLAPI_CONNECTION, &conn );
+ if (conn)
+ {
+ struct aclcb *geraclcb;
+ geraclcb = (struct aclcb *) acl_get_ext ( ACL_EXT_CONNECTION, conn );
+ acl_conn_ext_destructor ( geraclcb, NULL, NULL );
+ acl_set_ext ( ACL_EXT_CONNECTION, conn, *aclcb );
+ *aclcb = NULL;
+ }
+ }
+}
+
+static int
+_ger_new_gerpb (
+ Slapi_PBlock *pb,
+ Slapi_Entry *e,
+ const char *subjectndn,
+ Slapi_PBlock **gerpb,
+ void **aclcb, /* original aclcb */
+ char **errbuf
+ )
+{
+ Connection *conn;
+ struct acl_cblock *geraclcb;
+ Acl_PBlock *aclpb, *geraclpb;
+ Operation *op, *gerop;
+ int rc = LDAP_SUCCESS;
+
+ *aclcb = NULL;
+ *gerpb = slapi_pblock_new ();
+ if ( *gerpb == NULL )
+ {
+ rc = LDAP_NO_MEMORY;
+ goto bailout;
+ }
+
+ {
+ /* aclpb initialization needs the backend */
+ Slapi_Backend *be;
+ slapi_pblock_get ( pb, SLAPI_BACKEND, &be );
+ slapi_pblock_set ( *gerpb, SLAPI_BACKEND, be );
+ }
+
+ {
+ int isroot = slapi_dn_isroot ( subjectndn );
+ slapi_pblock_set ( *gerpb, SLAPI_REQUESTOR_ISROOT, &isroot );
+ }
+
+ /* Save requestor's aclcb and set subjectdn's one */
+ {
+ slapi_pblock_get ( pb, SLAPI_CONNECTION, &conn );
+ slapi_pblock_set ( *gerpb, SLAPI_CONNECTION, conn );
+
+ /* Can't share the conn->aclcb because of different context */
+ geraclcb = (struct acl_cblock *) acl_conn_ext_constructor ( NULL, NULL);
+ if ( geraclcb == NULL )
+ {
+ rc = LDAP_NO_MEMORY;
+ goto bailout;
+ }
+ slapi_sdn_set_ndn_byval ( geraclcb->aclcb_sdn, subjectndn );
+ *aclcb = acl_get_ext ( ACL_EXT_CONNECTION, conn );
+ acl_set_ext ( ACL_EXT_CONNECTION, conn, (void *) geraclcb );
+ }
+
+ {
+ gerop = operation_new ( OP_FLAG_INTERNAL );
+ if ( gerop == NULL )
+ {
+ rc = LDAP_NO_MEMORY;
+ goto bailout;
+ }
+ /*
+ * conn is a no-use parameter in the functions
+ * chained down from factory_create_extension
+ */
+ gerop->o_extension = factory_create_extension ( get_operation_object_type(), (void *)gerop, (void *)conn );
+ slapi_pblock_set ( *gerpb, SLAPI_OPERATION, gerop );
+ slapi_sdn_set_dn_byval ( &gerop->o_sdn, subjectndn );
+ geraclpb = acl_get_ext ( ACL_EXT_OPERATION, (void *)gerop);
+ acl_init_aclpb ( *gerpb, geraclpb, subjectndn, 0 );
+ geraclpb->aclpb_res_type |= ACLPB_EFFECTIVE_RIGHTS;
+ }
+
+
+bailout:
+ if ( rc != LDAP_SUCCESS )
+ {
+ _ger_release_gerpb ( gerpb, aclcb, pb );
+ }
+
+ return rc;
+}
+
+/*
+ * Callers should have already allocated *gerstr to hold at least
+ * "entryLevelRights: adnvxxx\n".
+ */
+unsigned long
+_ger_get_entry_rights (
+ Slapi_PBlock *gerpb,
+ Slapi_Entry *e,
+ const char *subjectndn,
+ char *gerstr,
+ char **errbuf
+ )
+{
+ unsigned long entryrights = 0;
+ Slapi_RDN *rdn = NULL;
+ const char *rdnstr = NULL;
+ char *equalsign = NULL;
+ char *rdntype = NULL;
+
+ strcpy ( gerstr, "entryLevelRights: " );
+
+ slapi_log_error (SLAPI_LOG_ACL, plugin_name,
+ "_ger_get_entry_rights: SLAPI_ACL_READ\n" );
+ if (acl_access_allowed(gerpb, e, "*", NULL, SLAPI_ACL_READ) == LDAP_SUCCESS)
+ {
+ /* v - view e */
+ entryrights |= SLAPI_ACL_READ;
+ strcat (gerstr, "v");
+ }
+ slapi_log_error (SLAPI_LOG_ACL, plugin_name,
+ "_ger_get_entry_rights: SLAPI_ACL_ADD\n" );
+ if (acl_access_allowed(gerpb, e, NULL, NULL, SLAPI_ACL_ADD) == LDAP_SUCCESS)
+ {
+ /* a - add child entry below e */
+ entryrights |= SLAPI_ACL_ADD;
+ strcat (gerstr, "a");
+ }
+ slapi_log_error (SLAPI_LOG_ACL, plugin_name,
+ "_ger_get_entry_rights: SLAPI_ACL_DELETE\n" );
+ if (acl_access_allowed(gerpb, e, NULL, NULL, SLAPI_ACL_DELETE) == LDAP_SUCCESS)
+ {
+ /* d - delete e */
+ entryrights |= SLAPI_ACL_DELETE;
+ strcat (gerstr, "d");
+ }
+ /*
+ * Some limitation/simplification applied here:
+ * - The modrdn right requires the rights to delete the old rdn and
+ * the new one. However we have no knowledge of what the new rdn
+ * is going to be.
+ * - In multi-valued RDN case, we check the right on
+ * the first rdn type only for now.
+ */
+ rdn = slapi_rdn_new_dn ( slapi_entry_get_ndn (e) );
+ rdnstr = slapi_rdn_get_rdn ( rdn );
+ if ( NULL != (equalsign = strchr ( rdnstr, '=' )) )
+ {
+ rdntype = slapi_ch_malloc ( equalsign-rdnstr+1 );
+ strncpy ( rdntype, rdnstr, equalsign-rdnstr );
+ rdntype [ equalsign-rdnstr ] = '\0';
+ slapi_log_error (SLAPI_LOG_ACL, plugin_name,
+ "_ger_get_entry_rights: SLAPI_ACL_WRITE_DEL & _ADD %s\n", rdntype );
+ if (acl_access_allowed(gerpb, e, rdntype, NULL,
+ ACLPB_SLAPI_ACL_WRITE_DEL) == LDAP_SUCCESS &&
+ acl_access_allowed(gerpb, e, rdntype, NULL,
+ ACLPB_SLAPI_ACL_WRITE_ADD) == LDAP_SUCCESS)
+ {
+ /* n - rename e */
+ entryrights |= SLAPI_ACL_WRITE;
+ strcat (gerstr, "n");
+ }
+ slapi_ch_free ( (void**) &rdntype );
+ }
+ slapi_rdn_free ( &rdn );
+
+done:
+ if ( entryrights == 0 )
+ {
+ strcat (gerstr, "none");
+ }
+
+ strcat (gerstr, "\n");
+
+ return entryrights;
+}
+
+/*
+ * *gerstr should point to a heap buffer since it may need
+ * to expand dynamically.
+ */
+unsigned long
+_ger_get_attr_rights (
+ Slapi_PBlock *gerpb,
+ Slapi_Entry *e,
+ const char *subjectndn,
+ char *type,
+ char **gerstr,
+ int *gerstrsize,
+ int isfirstattr,
+ char **errbuf
+ )
+{
+ unsigned long attrrights = 0;
+
+ /* Enough space for " $type:rwoscxx" ? */
+ if ( (*gerstrsize - strlen(*gerstr)) < (strlen(type) + 16) )
+ {
+ /* slapi_ch_realloc() exits if realloc() failed */
+ *gerstrsize += 256;
+ *gerstr = slapi_ch_realloc ( *gerstr, *gerstrsize );
+ }
+ if (!isfirstattr)
+ {
+ strcat ( *gerstr, ", " );
+ }
+ sprintf ( *gerstr + strlen(*gerstr), "%s:", type );
+
+ slapi_log_error (SLAPI_LOG_ACL, plugin_name,
+ "_ger_get_attr_rights: SLAPI_ACL_READ %s\n", type );
+ if (acl_access_allowed(gerpb, e, type, NULL, SLAPI_ACL_READ) == LDAP_SUCCESS)
+ {
+ /* r - read the values of type */
+ attrrights |= SLAPI_ACL_READ;
+ strcat (*gerstr, "r");
+ }
+ slapi_log_error (SLAPI_LOG_ACL, plugin_name,
+ "_ger_get_attr_rights: SLAPI_ACL_SEARCH %s\n", type );
+ if (acl_access_allowed(gerpb, e, type, NULL, SLAPI_ACL_SEARCH) == LDAP_SUCCESS)
+ {
+ /* s - search the values of type */
+ attrrights |= SLAPI_ACL_SEARCH;
+ strcat (*gerstr, "s");
+ }
+ slapi_log_error (SLAPI_LOG_ACL, plugin_name,
+ "_ger_get_attr_rights: SLAPI_ACL_COMPARE %s\n", type );
+ if (acl_access_allowed(gerpb, e, type, NULL, SLAPI_ACL_COMPARE) == LDAP_SUCCESS)
+ {
+ /* c - compare the values of type */
+ attrrights |= SLAPI_ACL_COMPARE;
+ strcat (*gerstr, "c");
+ }
+ slapi_log_error (SLAPI_LOG_ACL, plugin_name,
+ "_ger_get_attr_rights: SLAPI_ACL_WRITE_ADD %s\n", type );
+ if (acl_access_allowed(gerpb, e, type, NULL, ACLPB_SLAPI_ACL_WRITE_ADD) == LDAP_SUCCESS)
+ {
+ /* w - add the values of type */
+ attrrights |= ACLPB_SLAPI_ACL_WRITE_ADD;
+ strcat (*gerstr, "w");
+ }
+ slapi_log_error (SLAPI_LOG_ACL, plugin_name,
+ "_ger_get_attr_rights: SLAPI_ACL_WRITE_DEL %s\n", type );
+ if (acl_access_allowed(gerpb, e, type, NULL, ACLPB_SLAPI_ACL_WRITE_DEL) == LDAP_SUCCESS)
+ {
+ /* o - delete the values of type */
+ attrrights |= ACLPB_SLAPI_ACL_WRITE_DEL;
+ strcat (*gerstr, "o");
+ }
+ /* If subjectdn has no general write right, check for self write */
+ if ( 0 == (attrrights & (ACLPB_SLAPI_ACL_WRITE_DEL | ACLPB_SLAPI_ACL_WRITE_ADD)) )
+ {
+ struct berval val;
+
+ val.bv_val = (char *)subjectndn;
+ val.bv_len = strlen (subjectndn);
+
+ if (acl_access_allowed(gerpb, e, type, &val, ACLPB_SLAPI_ACL_WRITE_ADD) == LDAP_SUCCESS)
+ {
+ /* W - add self to the attribute */
+ attrrights |= ACLPB_SLAPI_ACL_WRITE_ADD;
+ strcat (*gerstr, "W");
+ }
+ if (acl_access_allowed(gerpb, e, type, &val, ACLPB_SLAPI_ACL_WRITE_DEL) == LDAP_SUCCESS)
+ {
+ /* O - delete self from the attribute */
+ attrrights |= ACLPB_SLAPI_ACL_WRITE_DEL;
+ strcat (*gerstr, "O");
+ }
+ }
+
+ if ( attrrights == 0 )
+ {
+ strcat (*gerstr, "none");
+ }
+
+ return attrrights;
+}
+
+void
+_ger_get_attrs_rights (
+ Slapi_PBlock *gerpb,
+ Slapi_Entry *e,
+ const char *subjectndn,
+ char **attrs,
+ char **gerstr,
+ int *gerstrsize,
+ char **errbuf
+ )
+{
+ int isfirstattr = 1;
+
+ /* gerstr was initially allocated with enough space for one more line */
+ strcat ( *gerstr, "attributeLevelRights: " );
+
+ if (attrs && *attrs)
+ {
+ int i;
+ for ( i = 0; attrs[i]; i++ )
+ {
+ _ger_get_attr_rights ( gerpb, e, subjectndn, attrs[i], gerstr, gerstrsize, isfirstattr, errbuf );
+ isfirstattr = 0;
+ }
+ }
+ else
+ {
+ Slapi_Attr *prevattr = NULL, *attr;
+ char *type;
+
+ while ( slapi_entry_next_attr ( e, prevattr, &attr ) == 0 )
+ {
+ if ( ! slapi_attr_flag_is_set (attr, SLAPI_ATTR_FLAG_OPATTR) )
+ {
+ slapi_attr_get_type ( attr, &type );
+ _ger_get_attr_rights ( gerpb, e, subjectndn, type, gerstr, gerstrsize, isfirstattr, errbuf );
+ isfirstattr = 0;
+ }
+ prevattr = attr;
+ }
+ }
+
+ if ( isfirstattr )
+ {
+ /* not a single attribute was retrived or specified */
+ strcat ( *gerstr, "*:none" );
+ }
+ return;
+}
+
+/*
+ * controlType = LDAP_CONTROL_GET_EFFECTIVE_RIGHTS;
+ * criticality = n/a;
+ * controlValue = OCTET STRING of BER encoding of the SEQUENCE of
+ * ENUMERATED LDAP code
+ */
+void
+_ger_set_response_control (
+ Slapi_PBlock *pb,
+ int iscritical,
+ int rc
+ )
+{
+ LDAPControl **resultctrls = NULL;
+ LDAPControl gerrespctrl;
+ BerElement *ber = NULL;
+ struct berval *berval = NULL;
+ int found = 0;
+ int i;
+
+ if ( (ber = der_alloc ()) == NULL )
+ {
+ goto bailout;
+ }
+
+ /* begin sequence, enumeration, end sequence */
+ ber_printf ( ber, "{e}", rc );
+ if ( ber_flatten ( ber, &berval ) != LDAP_SUCCESS )
+ {
+ goto bailout;
+ }
+ gerrespctrl.ldctl_oid = LDAP_CONTROL_GET_EFFECTIVE_RIGHTS;
+ gerrespctrl.ldctl_iscritical = iscritical;
+ gerrespctrl.ldctl_value.bv_val = berval->bv_val;
+ gerrespctrl.ldctl_value.bv_len = berval->bv_len;
+
+ slapi_pblock_get ( pb, SLAPI_RESCONTROLS, &resultctrls );
+ for (i = 0; resultctrls && resultctrls[i]; i++)
+ {
+ if (strcmp(resultctrls[i]->ldctl_oid, LDAP_CONTROL_GET_EFFECTIVE_RIGHTS) == 0)
+ {
+ /*
+ * We get here if search returns more than one entry
+ * and this is not the first entry.
+ */
+ ldap_control_free ( resultctrls[i] );
+ resultctrls[i] = slapi_dup_control (&gerrespctrl);
+ found = 1;
+ break;
+ }
+ }
+
+ if ( !found )
+ {
+ /* slapi_pblock_set() will dup the control */
+ slapi_pblock_set ( pb, SLAPI_ADD_RESCONTROL, &gerrespctrl );
+ }
+
+bailout:
+ ber_free ( ber, 1 ); /* ber_free() checks for NULL param */
+ ber_bvfree ( berval ); /* ber_bvfree() checks for NULL param */
+}
+
+int
+acl_get_effective_rights (
+ Slapi_PBlock *pb,
+ Slapi_Entry *e, /* target entry */
+ char **attrs, /* Attribute of the entry */
+ struct berval *val, /* value of attr. NOT USED */
+ int access, /* requested access rights */
+ char **errbuf
+ )
+{
+ Slapi_PBlock *gerpb = NULL;
+ void *aclcb = NULL;
+ char *subjectndn = NULL;
+ char *gerstr = NULL;
+ int gerstrsize = 1024;
+ unsigned long entryrights;
+ int iscritical = 1;
+ int rc;
+
+ *errbuf = '\0';
+ gerstr = slapi_ch_malloc ( gerstrsize );
+
+ /*
+ * Get the subject
+ */
+ rc = _ger_parse_control (pb, &subjectndn, &iscritical, errbuf );
+ if ( rc != LDAP_SUCCESS )
+ {
+ goto bailout;
+ }
+
+ /*
+ * The requestor should have g permission on the entry
+ * to get the effective rights.
+ */
+ rc = _ger_g_permission_granted (pb, e, errbuf);
+ if ( rc != LDAP_SUCCESS )
+ {
+ goto bailout;
+ }
+
+ /*
+ * Construct a new pb
+ */
+ rc = _ger_new_gerpb ( pb, e, subjectndn, &gerpb, &aclcb, errbuf );
+ if ( rc != LDAP_SUCCESS )
+ {
+ goto bailout;
+ }
+
+ /* Get entry level effective rights */
+ entryrights = _ger_get_entry_rights ( gerpb, e, subjectndn, gerstr, errbuf );
+
+ /*
+ * Attribute level effective rights may not be NULL
+ * even if entry level's is.
+ */
+ _ger_get_attrs_rights ( gerpb, e, subjectndn, attrs, &gerstr, &gerstrsize, errbuf );
+
+bailout:
+ /*
+ * Now construct the response control
+ */
+ _ger_set_response_control ( pb, iscritical, rc );
+
+ if ( rc != LDAP_SUCCESS )
+ {
+ sprintf ( gerstr, "entryLevelRights: %d\nattributeLevelRights: *:%d", rc, rc );
+ }
+
+ slapi_log_error (SLAPI_LOG_ACLSUMMARY, plugin_name,
+ "###### Effective Rights on Entry (%s) for Subject (%s) ######\n",
+ slapi_entry_get_ndn (e), subjectndn);
+ slapi_log_error (SLAPI_LOG_ACLSUMMARY, plugin_name, "%s\n", gerstr);
+
+ /* Restore pb */
+ _ger_release_gerpb ( &gerpb, &aclcb, pb );
+
+ /*
+ * General plugin uses SLAPI_RESULT_TEXT for error text. Here
+ * SLAPI_PB_RESULT_TEXT is exclusively shared with add, dse and schema.
+ * slapi_pblock_set() will free any previous data, and
+ * pblock_done() will free SLAPI_PB_RESULT_TEXT.
+ */
+ slapi_pblock_set (pb, SLAPI_PB_RESULT_TEXT, gerstr);
+
+ if ( !iscritical )
+ {
+ /*
+ * If return code is not LDAP_SUCCESS, the server would
+ * abort sending the data of the entry to the client.
+ */
+ rc = LDAP_SUCCESS;
+ }
+
+ slapi_ch_free ( (void **) &subjectndn );
+ slapi_ch_free ( (void **) &gerstr );
+ return rc;
+}
diff --git a/ldap/servers/plugins/acl/aclgroup.c b/ldap/servers/plugins/acl/aclgroup.c
new file mode 100644
index 00000000..4cd7039f
--- /dev/null
+++ b/ldap/servers/plugins/acl/aclgroup.c
@@ -0,0 +1,442 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+#include "acl.h"
+
+/***************************************************************************
+ *
+ * This module deals with the global user group cache.
+ *
+ * A LRU queue mechanism is used to maintain the groups the user currently in.
+ * At this moment the QUEUE is invalidated if there is a group change. A better
+ * way would have been to invalidate only the one which are effected.
+ * However to accomplish that will require quite a bit of work which may not be
+ * cost-efftive.
+ **************************************************************************/
+static aclGroupCache *aclUserGroups;
+#define ACL_MAXCACHE_USERGROUPS 200
+
+#define ACLG_LOCK_GROUPCACHE_READ() PR_RWLock_Rlock ( aclUserGroups->aclg_rwlock )
+#define ACLG_LOCK_GROUPCACHE_WRITE() PR_RWLock_Wlock ( aclUserGroups->aclg_rwlock )
+#define ACLG_ULOCK_GROUPCACHE_WRITE() PR_RWLock_Unlock ( aclUserGroups->aclg_rwlock )
+#define ACLG_ULOCK_GROUPCACHE_READ() PR_RWLock_Unlock ( aclUserGroups->aclg_rwlock )
+
+
+static void __aclg__delete_userGroup ( aclUserGroup *u_group );
+
+
+int
+aclgroup_init ()
+{
+
+ aclUserGroups = ( aclGroupCache * ) slapi_ch_calloc (1, sizeof ( aclGroupCache ) );
+ if ( NULL == (aclUserGroups->aclg_rwlock = PR_NewRWLock( PR_RWLOCK_RANK_NONE,"Group LOCK"))) {
+ slapi_log_error(SLAPI_LOG_FATAL, plugin_name, "Unable to allocate RWLOCK for group cache\n");
+ return 1;
+ }
+ return 0;
+}
+
+/*
+ * aclg_init_userGroup
+ *
+ * Go thru the Global Group CACHE and see if we have group information for
+ * the user. The user's group cache is invalidated when a group is modified
+ * (in which case ALL usergroups are invalidated) or when the user's entry
+ * is modified in which case just his is invalidated.
+ *
+ * We need to scan the whole cache looking for a valid entry that matches
+ * this user. If we find invalid entries along the way.
+ *
+ * If we don't have anything it's fine. we will allocate a space when we
+ * need it i.e during the group evaluation.
+ *
+ * Inputs:
+ * struct acl_pblock - ACL private block
+ * char *dn - the client's dn
+ * int got_lock - 1: already obtained WRITE Lock
+ * - 0: Nope; get one
+ * Returns:
+ * None.
+ */
+
+void
+aclg_init_userGroup ( struct acl_pblock *aclpb, const char *n_dn , int got_lock )
+{
+ aclUserGroup *u_group = NULL;
+ aclUserGroup *next_ugroup = NULL;
+ aclUserGroup *p_group, *n_group;
+ int found = 0;
+
+ /* Check for Anonymous user */
+ if ( n_dn && *n_dn == '\0') return;
+
+ if ( !got_lock ) ACLG_LOCK_GROUPCACHE_WRITE ();
+ u_group = aclUserGroups->aclg_first;
+ aclpb->aclpb_groupinfo = NULL;
+
+ while ( u_group != NULL ) {
+ next_ugroup = u_group->aclug_next;
+ if ( aclUserGroups->aclg_signature != u_group->aclug_signature) {
+ /*
+ * This means that this usergroup is no longer valid and
+ * this operation so delete this one if no one is using it.
+ */
+
+ if ( !u_group->aclug_refcnt ) {
+ slapi_log_error( SLAPI_LOG_ACL, plugin_name,
+ "In traversal group deallocation\n", 0,0,0 );
+ __aclg__delete_userGroup (u_group);
+ }
+ } else {
+
+ /*
+ * Here, u_group is valid--if it matches then take it.
+ */
+ if ( slapi_utf8casecmp((ACLUCHP)u_group->aclug_ndn,
+ (ACLUCHP)n_dn ) == 0 ) {
+ u_group->aclug_refcnt++;
+ aclpb->aclpb_groupinfo = u_group;
+ found = 1;
+ break;
+ }
+ }
+ u_group = next_ugroup;
+ }
+
+ /* Move the new one to the top of the queue */
+ if ( found ) {
+ p_group = u_group->aclug_prev;
+ n_group = u_group->aclug_next;
+
+ if ( p_group ) {
+ aclUserGroup *t_group = NULL;
+
+ p_group->aclug_next = n_group;
+ if ( n_group ) n_group->aclug_prev = p_group;
+
+ t_group = aclUserGroups->aclg_first;
+ if ( t_group ) t_group->aclug_prev = u_group;
+
+ u_group->aclug_prev = NULL;
+ u_group->aclug_next = t_group;
+ aclUserGroups->aclg_first = u_group;
+
+ if ( u_group == aclUserGroups->aclg_last )
+ aclUserGroups->aclg_last = p_group;
+ }
+ slapi_log_error(SLAPI_LOG_ACL, plugin_name, "acl_init_userGroup: found in cache for dn:%s\n", n_dn,0,0);
+ }
+ if (!got_lock ) ACLG_ULOCK_GROUPCACHE_WRITE ();
+}
+
+
+/*
+ *
+ * aclg_reset_userGroup
+ * Reset the reference count to the user's group.
+ *
+ * Inputs:
+ * struct acl_pblock -- The acl private block.
+ * Returns:
+ * None.
+ *
+ * Note: A WRITE Lock on the GroupCache is obtained during the change:
+ */
+void
+aclg_reset_userGroup ( struct acl_pblock *aclpb )
+{
+
+ aclUserGroup *u_group;
+
+ ACLG_LOCK_GROUPCACHE_WRITE();
+
+ if ( (u_group = aclpb->aclpb_groupinfo) != NULL ) {
+ u_group->aclug_refcnt--;
+
+ /* If I am the last one but I was using an invalid group cache
+ ** in the meantime, it is time now to get rid of it so that we will
+ ** not have duplicate cache.
+ */
+ if ( !u_group->aclug_refcnt &&
+ ( aclUserGroups->aclg_signature != u_group->aclug_signature )) {
+ __aclg__delete_userGroup ( u_group );
+ }
+ }
+ ACLG_ULOCK_GROUPCACHE_WRITE();
+ aclpb->aclpb_groupinfo = NULL;
+}
+
+/*
+ * Find a user group in the global cache, returning a pointer to it,
+ * ensuring that the refcnt has been bumped to stop
+ * another thread freeing it underneath us.
+*/
+
+aclUserGroup*
+aclg_find_userGroup(char *n_dn)
+{
+ aclUserGroup *u_group = NULL;
+ int i;
+
+ /* Check for Anonymous user */
+ if ( n_dn && *n_dn == '\0') return (NULL) ;
+
+ ACLG_LOCK_GROUPCACHE_READ ();
+ u_group = aclUserGroups->aclg_first;
+
+ for ( i=0; i < aclUserGroups->aclg_num_userGroups; i++ ) {
+ if ( aclUserGroups->aclg_signature == u_group->aclug_signature &&
+ slapi_utf8casecmp((ACLUCHP)u_group->aclug_ndn,
+ (ACLUCHP)n_dn ) == 0 ) {
+ aclg_reader_incr_ugroup_refcnt(u_group);
+ break;
+ }
+ u_group = u_group->aclug_next;
+ }
+
+ ACLG_ULOCK_GROUPCACHE_READ ();
+ return(u_group);
+}
+
+/*
+ * Mark a usergroup for removal from the usergroup cache.
+ * It will be removed by the first operation traversing the cache
+ * that finds it.
+*/
+void
+aclg_markUgroupForRemoval ( aclUserGroup* u_group) {
+
+ ACLG_LOCK_GROUPCACHE_WRITE ();
+ aclg_regen_ugroup_signature(u_group);
+ u_group->aclug_refcnt--;
+ ACLG_ULOCK_GROUPCACHE_WRITE ();
+}
+
+/*
+ *
+ * aclg_get_usersGroup
+ *
+ * If we already have a the group info then we are done. If we
+ * don't, then allocate a new one and attach it.
+ *
+ * Inputs:
+ * struct acl_pblock -- The acl private block.
+ * char *n_dn - normalized client's DN
+ *
+ * Returns:
+ * aclUserGroup - The Group info block.
+ *
+ */
+aclUserGroup *
+aclg_get_usersGroup ( struct acl_pblock *aclpb , char *n_dn)
+{
+
+ aclUserGroup *u_group, *f_group;
+
+ if ( aclpb && aclpb->aclpb_groupinfo )
+ return aclpb->aclpb_groupinfo;
+
+ ACLG_LOCK_GROUPCACHE_WRITE();
+
+ /* try it one more time. We might have one in the meantime */
+ aclg_init_userGroup (aclpb, n_dn , 1 /* got the lock */);
+ if ( aclpb && aclpb->aclpb_groupinfo ) {
+ ACLG_ULOCK_GROUPCACHE_WRITE();
+ return aclpb->aclpb_groupinfo;
+ }
+
+ /*
+ * It is possible at this point that we already have a group cache for the user
+ * but is is invalid. We can't use it anayway. So, we march along and allocate a new one.
+ * That's fine as the invalid one will be deallocated when done.
+ */
+
+ slapi_log_error( SLAPI_LOG_ACL, plugin_name, "ALLOCATING GROUP FOR:%s\n", n_dn,0,0 );
+ u_group = ( aclUserGroup * ) slapi_ch_calloc ( 1, sizeof ( aclUserGroup ) );
+
+ u_group->aclug_refcnt = 1;
+ if ( (u_group->aclug_refcnt_mutex = PR_NewLock()) == NULL ) {
+ slapi_ch_free((void **)&u_group);
+ ACLG_ULOCK_GROUPCACHE_WRITE();
+ return(NULL);
+ }
+
+ u_group->aclug_member_groups = (char **)
+ slapi_ch_calloc ( 1,
+ (ACLUG_INCR_GROUPS_LIST * sizeof (char *)));
+ u_group->aclug_member_group_size = ACLUG_INCR_GROUPS_LIST;
+ u_group->aclug_numof_member_group = 0;
+
+ u_group->aclug_notmember_groups = (char **)
+ slapi_ch_calloc ( 1,
+ (ACLUG_INCR_GROUPS_LIST * sizeof (char *)));
+ u_group->aclug_notmember_group_size = ACLUG_INCR_GROUPS_LIST;
+ u_group->aclug_numof_notmember_group = 0;
+
+ u_group->aclug_ndn = slapi_ch_strdup ( n_dn ) ;
+
+ u_group->aclug_signature = aclUserGroups->aclg_signature;
+
+ /* Do we have alreday the max number. If we have then delete the last one */
+ if ( aclUserGroups->aclg_num_userGroups >= ACL_MAXCACHE_USERGROUPS - 5 ) {
+ aclUserGroup *d_group;
+
+ /* We need to traverse thru backwards and delete the one with a refcnt = 0 */
+ d_group = aclUserGroups->aclg_last;
+ while ( d_group ) {
+ if ( !d_group->aclug_refcnt ) {
+ __aclg__delete_userGroup ( d_group );
+ break;
+ } else {
+ d_group = d_group->aclug_prev;
+ }
+ }
+
+ /* If we didn't find any, which should be never,
+ ** we have 5 more tries to do it.
+ */
+ }
+ f_group = aclUserGroups->aclg_first;
+ u_group->aclug_next = f_group;
+ if ( f_group ) f_group->aclug_prev = u_group;
+
+ aclUserGroups->aclg_first = u_group;
+ if ( aclUserGroups->aclg_last == NULL )
+ aclUserGroups->aclg_last = u_group;
+
+ aclUserGroups->aclg_num_userGroups++;
+
+ /* Put it in the queue */
+ ACLG_ULOCK_GROUPCACHE_WRITE();
+
+ /* Now hang on to it */
+ aclpb->aclpb_groupinfo = u_group;
+ return u_group;
+}
+
+/*
+ *
+ * __aclg__delete_userGroup
+ *
+ * Delete the User's Group cache.
+ *
+ * Inputs:
+ * aclUserGroup - remove this one
+ * Returns:
+ * None.
+ *
+ * Note: A WRITE Lock on the GroupCache is obtained by the caller
+ */
+static void
+__aclg__delete_userGroup ( aclUserGroup *u_group )
+{
+
+ aclUserGroup *next_group, *prev_group;
+ int i;
+
+ if ( !u_group ) return;
+
+ prev_group = u_group->aclug_prev;
+ next_group = u_group->aclug_next;
+
+ /*
+ * At this point we must have a 0 refcnt or else we are in a bad shape.
+ * If we don't have one then at least remove the user's dn so that it will
+ * be in a condemned state and later deleted.
+ */
+
+ slapi_log_error( SLAPI_LOG_ACL, plugin_name, "DEALLOCATING GROUP FOR:%s\n", u_group->aclug_ndn,0,0 );
+
+ slapi_ch_free ( (void **) &u_group->aclug_ndn );
+
+ PR_DestroyLock(u_group->aclug_refcnt_mutex);
+
+ /* Remove the member GROUPS */
+ for (i=0; i < u_group->aclug_numof_member_group; i++ )
+ slapi_ch_free ( (void **) &u_group->aclug_member_groups[i] );
+ slapi_ch_free ( (void **) &u_group->aclug_member_groups );
+
+ /* Remove the NOT member GROUPS */
+ for (i=0; i < u_group->aclug_numof_notmember_group; i++ )
+ slapi_ch_free ( (void **) &u_group->aclug_notmember_groups[i] );
+ slapi_ch_free ( (void **) &u_group->aclug_notmember_groups );
+
+ slapi_ch_free ( (void **) &u_group );
+
+ if ( prev_group == NULL && next_group == NULL ) {
+ aclUserGroups->aclg_first = NULL;
+ aclUserGroups->aclg_last = NULL;
+ } else if ( prev_group == NULL ) {
+ next_group->aclug_prev = NULL;
+ aclUserGroups->aclg_first = next_group;
+ } else {
+ prev_group->aclug_next = next_group;
+ if ( next_group )
+ next_group->aclug_prev = prev_group;
+ else
+ aclUserGroups->aclg_last = prev_group;
+ }
+ aclUserGroups->aclg_num_userGroups--;
+}
+
+void
+aclg_regen_group_signature( )
+{
+ aclUserGroups->aclg_signature = aclutil_gen_signature ( aclUserGroups->aclg_signature );
+}
+
+void
+aclg_regen_ugroup_signature( aclUserGroup *ugroup)
+{
+ ugroup->aclug_signature =
+ aclutil_gen_signature ( ugroup->aclug_signature );
+}
+
+void
+aclg_lock_groupCache ( int type /* 1 for reader and 2 for writer */)
+{
+
+ if (type == 1 )
+ ACLG_LOCK_GROUPCACHE_READ();
+ else
+ ACLG_LOCK_GROUPCACHE_WRITE();
+}
+
+void
+aclg_unlock_groupCache ( int type /* 1 for reader and 2 for writer */)
+{
+
+ if (type == 1 )
+ ACLG_ULOCK_GROUPCACHE_READ();
+ else
+ ACLG_ULOCK_GROUPCACHE_WRITE();
+}
+
+
+/*
+ * If you have the write lock on the group cache, you can
+ * increment the refcnt without taking the mutex.
+ * If you just have the reader lock on the refcnt then you need to
+ * take the mutex on the refcnt to increment it--which is what this routine is
+ * for.
+ *
+*/
+
+void
+aclg_reader_incr_ugroup_refcnt(aclUserGroup* u_group) {
+
+ PR_Lock(u_group->aclug_refcnt_mutex);
+ u_group->aclug_refcnt++;
+ PR_Unlock(u_group->aclug_refcnt_mutex);
+}
+
+/* You need the usergroups read lock to call this routine*/
+int
+aclg_numof_usergroups(void) {
+
+ return(aclUserGroups->aclg_num_userGroups);
+}
+
diff --git a/ldap/servers/plugins/acl/aclinit.c b/ldap/servers/plugins/acl/aclinit.c
new file mode 100644
index 00000000..21e54337
--- /dev/null
+++ b/ldap/servers/plugins/acl/aclinit.c
@@ -0,0 +1,537 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+#include "acl.h"
+
+static int __aclinit__RegisterLases(void);
+static int __aclinit__RegisterAttributes(void);
+static int __aclinit_handler(Slapi_Entry *e, void *callback_data);
+
+/***************************************************************************
+*
+* aclinit_main()
+* Main routine which is called at the server boot up time.
+*
+* 1) Reads all the ACI entries from the database and creates
+* the ACL list.
+* 2) Registers all the LASes and the GetAttrs supported by the DS.
+* 3) Generates anonymous profiles.
+* 4) Registers proxy control
+* 5) Creates aclpb pool
+*
+* Input:
+* None.
+*
+* Returns:
+* 0 -- no error
+* 1 -- Error
+*
+* Error Handling:
+* If any error found during the ACL generation, error is logged.
+*
+**************************************************************************/
+static int acl_initialized = 0;
+int
+aclinit_main()
+{
+ char *cookie = NULL;
+ Slapi_PBlock *pb;
+ int rv;
+ Slapi_DN *sdn;
+ void *node;
+
+ if (acl_initialized) {
+ /* There is no need to do anything more */
+ return 0;
+ }
+
+ /* Initialize the LIBACCESS ACL library */
+ if (ACL_Init() != 0) {
+ slapi_log_error(SLAPI_LOG_FATAL, plugin_name,
+ "ACL Library Initialization failed\n",0,0,0);
+ return 1;
+ }
+
+ /* register all the LASes supported by the DS */
+ if (ACL_ERR == __aclinit__RegisterLases()) {
+ /* Error is already logged */
+ return 1;
+ }
+
+ /* Register all the Attrs */
+ if (ACL_ERR == __aclinit__RegisterAttributes()) {
+ /* Error is already logged */
+ return 1;
+ }
+
+ /*
+ * Register to get backend state changes so we can add/remove
+ * acis from backends that come up and go down.
+ */
+
+ slapi_register_backend_state_change((void *) NULL, acl_be_state_change_fnc);
+
+
+ /* register the extensions */
+ /* ONREPL Moved to the acl_init function because extensions
+ need to be registered before any operations are issued
+ if ( 0 != acl_init_ext() ) {
+ slapi_log_error ( SLAPI_LOG_FATAL, plugin_name,
+ "Unable to initialize the extensions\n");
+ return 1;
+ } */
+
+ /* create the mutex array */
+ if ( 0 != aclext_alloc_lockarray ( ) ) {
+ slapi_log_error ( SLAPI_LOG_FATAL, plugin_name,
+ "Unable to create the mutext array\n");
+ return 1;
+ }
+
+ /* Allocate the pool */
+ if ( 0 != acl_create_aclpb_pool () ) {
+ slapi_log_error ( SLAPI_LOG_FATAL, plugin_name,
+ "Unable to create the acl private pool\n");
+ return 1;
+ }
+
+ /*
+ * Now read all the ACLs from all the backends and put it
+ * in a list
+ */
+ /* initialize the ACLLIST sub-system */
+ if ( 0 != (rv = acllist_init ( ))) {
+ slapi_log_error ( SLAPI_LOG_FATAL, plugin_name,
+ "Unable to initialize the plugin:%d\n", rv );
+ return 1;
+ }
+
+ /* Initialize the anonymous profile i.e., generate it */
+ rv = aclanom_init ();
+
+ pb = slapi_pblock_new();
+
+ /*
+ * search for the aci_attr_type attributes of all entries.
+ *
+ * slapi_get_fist_suffix() and slapi_get_next_suffix() do not return the
+ * rootdse entry so we search for acis in there explicitly here.
+ */
+
+ sdn = slapi_sdn_new_dn_byval("");
+ slapi_log_error ( SLAPI_LOG_ACL, plugin_name,
+ "Searching for all acis(scope base) at suffix ''\n");
+ aclinit_search_and_update_aci ( 0, /* thisbeonly */
+ sdn, /* base */
+ NULL, /* be name*/
+ LDAP_SCOPE_BASE, ACL_ADD_ACIS,
+ DO_TAKE_ACLCACHE_WRITELOCK);
+ slapi_sdn_free(&sdn);
+
+ sdn = slapi_get_first_suffix( &node, 1 );
+ while (sdn)
+ {
+ slapi_log_error ( SLAPI_LOG_ACL, plugin_name,
+ "Searching for all acis(scope subtree) at suffix '%s'\n",
+ slapi_sdn_get_dn(sdn) );
+ aclinit_search_and_update_aci ( 0, /* thisbeonly */
+ sdn, /* base */
+ NULL, /* be name*/
+ LDAP_SCOPE_SUBTREE, ACL_ADD_ACIS,
+ DO_TAKE_ACLCACHE_WRITELOCK);
+ sdn = slapi_get_next_suffix( &node, 1 );
+ }
+
+ /* Initialize it. */
+ acl_initialized = 1;
+
+ /* generate the signatures */
+ acl_set_aclsignature ( aclutil_gen_signature ( 100 ) );
+
+ /* Initialize the user-group cache */
+ rv = aclgroup_init ( );
+
+ aclanom_gen_anomProfile (DO_TAKE_ACLCACHE_READLOCK);
+
+ /* Register both of the proxied authorization controls (version 1 and 2) */
+ slapi_register_supported_control( LDAP_CONTROL_PROXYAUTH,
+ SLAPI_OPERATION_SEARCH | SLAPI_OPERATION_COMPARE
+ | SLAPI_OPERATION_ADD | SLAPI_OPERATION_DELETE
+ | SLAPI_OPERATION_MODIFY | SLAPI_OPERATION_MODDN
+ | SLAPI_OPERATION_EXTENDED );
+ slapi_register_supported_control( LDAP_CONTROL_PROXIEDAUTH,
+ SLAPI_OPERATION_SEARCH | SLAPI_OPERATION_COMPARE
+ | SLAPI_OPERATION_ADD | SLAPI_OPERATION_DELETE
+ | SLAPI_OPERATION_MODIFY | SLAPI_OPERATION_MODDN
+ | SLAPI_OPERATION_EXTENDED );
+
+ slapi_pblock_destroy ( pb );
+ return 0;
+}
+/*
+ * This routine is the one that scans for acis and either adds them
+ * to the internal cache (op==ACL_ADD_ACIS) or deletes them
+ * (op==ACL_REMOVE_ACIS).
+ *
+ * If thisbeonly is 0 the search
+ * is conducted on the base with the specifed scope and be_name is ignored.
+ * This is used at startup time where we iterate over all suffixes, searching
+ * for all the acis in the DIT to load the ACL cache.
+ *
+ * If thisbeonly is 1 then then a be_name must be specified.
+ * In this case we will search in that backend ONLY.
+ * This is used in the case where a backend is turned on and off--in this
+ * case we only want to add/remove the acis in that particular backend and
+ * not for example in any backends below that one.
+*/
+
+int
+aclinit_search_and_update_aci ( int thisbeonly, const Slapi_DN *base,
+ char *be_name, int scope, int op,
+ acl_lock_flag_t lock_flag )
+{
+ char *attrs[2] = { "aci", NULL };
+ /* Tell __aclinit_handler whether it's an add or a delete */
+ int any_error = op;
+ Slapi_PBlock *aPb;
+ LDAPControl **ctrls=NULL;
+ int retval;
+ struct berval *bval;
+ aclinit_handler_callback_data_t call_back_data;
+
+ PR_ASSERT( lock_flag == DONT_TAKE_ACLCACHE_WRITELOCK ||
+ lock_flag == DO_TAKE_ACLCACHE_WRITELOCK);
+
+ if ( thisbeonly && be_name == NULL) {
+ slapi_log_error ( SLAPI_LOG_FATAL, plugin_name,
+ "Error: This be_name must be specified.\n", 0, 0, 0);
+ return -1;
+ }
+
+
+ /*
+ * We need to explicitly request (objectclass=ldapsubentry)
+ * in order to get all the subentry acis too.
+ * Note that subentries can be added under subentries (although its not
+ * recommended) so that
+ * there may be non-trivial acis under a subentry.
+ */
+
+ /* Use new search internal API */
+ /* and never retrieve aci from a remote server */
+ aPb = slapi_pblock_new ();
+
+ /*
+ * Set up the control to say "Only get acis from this Backend--
+ * there may be more backends under this one.
+ */
+
+ if ( thisbeonly ) {
+
+ bval = (struct berval *)slapi_ch_malloc(sizeof(struct berval));
+ bval->bv_len = strlen(be_name) + 1;
+ bval->bv_val = slapi_ch_strdup(be_name);
+
+ ctrls = (LDAPControl **)slapi_ch_calloc( 2, sizeof(LDAPControl *));
+ ctrls[0] = NULL;
+ ctrls[1] = NULL;
+
+ retval = slapi_build_control_from_berval(
+ MTN_CONTROL_USE_ONE_BACKEND_OID,
+ bval,
+ 1 /* is critical */,
+ ctrls);
+
+ }
+
+ slapi_search_internal_set_pb ( aPb,
+ slapi_sdn_get_dn(base),
+ scope,
+ "(|(aci=*)(objectclass=ldapsubentry))",
+ attrs,
+ 0 /* attrsonly */,
+ ctrls /* controls: SLAPI_ARGCONTROLS */,
+ NULL /* uniqueid */,
+ aclplugin_get_identity (ACL_PLUGIN_IDENTITY),
+ SLAPI_OP_FLAG_NEVER_CHAIN /* actions : get local aci only */);
+
+ if (thisbeonly) {
+ slapi_pblock_set(aPb, SLAPI_REQCONTROLS, ctrls);
+ }
+
+ call_back_data.op = op;
+ call_back_data.retCode = 0;
+ call_back_data.lock_flag = lock_flag;
+
+ slapi_search_internal_callback_pb(aPb,
+ &call_back_data /* callback_data */,
+ NULL/* result_callback */,
+ __aclinit_handler,
+ NULL /* referral_callback */);
+
+ if (thisbeonly) {
+ slapi_ch_free((void **)&bval);
+ }
+
+ /*
+ * This frees the control oid, the bv_val and the control itself and the
+ * ctrls array mem by caling ldap_controls_free()--so we
+ * don't need to do it ourselves.
+ */
+ slapi_pblock_destroy (aPb);
+
+ return call_back_data.retCode;
+
+}
+
+/***************************************************************************
+*
+* __aclinit_handler
+*
+* For each entry, finds if there is any ACL in thet entry. If there is
+* then the ACL is processed and stored in the ACL LIST.
+*
+*
+* Input:
+*
+*
+* Returns:
+* None.
+*
+* Error Handling:
+* If any error found during the ACL generation, the ACL is
+* logged. Also, set in the callback_data so that caller can act upon it.
+*
+**************************************************************************/
+static int
+__aclinit_handler ( Slapi_Entry *e, void *callback_data)
+{
+ Slapi_Attr *attr;
+ aclinit_handler_callback_data_t *call_back_data =
+ (aclinit_handler_callback_data_t*)callback_data;
+ Slapi_DN *e_sdn;
+ int rv;
+ Slapi_Value *sval=NULL;
+
+ call_back_data->retCode = 0; /* assume success--if there's an error we overwrite it */
+ if (e != NULL) {
+
+ e_sdn = slapi_entry_get_sdn ( e );
+
+ /*
+ * Take the write lock around all the mods--so that
+ * other operations will see the acicache either before the whole mod
+ * or after but not, as it was before, during the mod.
+ * This is in line with the LDAP concept of the operation
+ * on the whole entry being the atomic unit.
+ *
+ */
+
+ if ( call_back_data->op == ACL_ADD_ACIS ) {
+ slapi_log_error ( SLAPI_LOG_ACL, plugin_name,
+ "Adding acis for entry '%s'\n", slapi_sdn_get_dn(e_sdn));
+ slapi_entry_attr_find ( e, aci_attr_type, &attr );
+
+ if ( attr ) {
+
+ const struct berval *attrValue;
+
+ int i;
+ if ( call_back_data->lock_flag == DO_TAKE_ACLCACHE_WRITELOCK) {
+ acllist_acicache_WRITE_LOCK();
+ }
+ i= slapi_attr_first_value ( attr, &sval );
+ while(i != -1) {
+ attrValue = slapi_value_get_berval(sval);
+
+ if ( 0 != (rv=acllist_insert_aci_needsLock (e_sdn, attrValue))) {
+ aclutil_print_err(rv, e_sdn, attrValue, NULL);
+
+ /* We got an error; Log it and then march along */
+ slapi_log_error ( SLAPI_LOG_FATAL, plugin_name,
+ "Error: This (%s) ACL will not be considered for evaluation"
+ " because of syntax errors.\n",
+ attrValue->bv_val ? attrValue->bv_val: "NULL", 0, 0);
+ call_back_data->retCode = rv;
+ }
+ i= slapi_attr_next_value( attr, i, &sval );
+ }/* while */
+ if ( call_back_data->lock_flag == DO_TAKE_ACLCACHE_WRITELOCK) {
+ acllist_acicache_WRITE_UNLOCK();
+ }
+ }
+ } else if (call_back_data->op == ACL_REMOVE_ACIS) {
+
+ /* Here we are deleting the acis. */
+ slapi_log_error ( SLAPI_LOG_ACL, plugin_name, "Removing acis\n");
+ if ( call_back_data->lock_flag == DO_TAKE_ACLCACHE_WRITELOCK) {
+ acllist_acicache_WRITE_LOCK();
+ }
+ if ( 0 != (rv=acllist_remove_aci_needsLock(e_sdn, NULL))) {
+ aclutil_print_err(rv, e_sdn, NULL, NULL);
+
+ /* We got an error; Log it and then march along */
+ slapi_log_error ( SLAPI_LOG_FATAL, plugin_name,
+ "Error: ACls not deleted from %s\n",
+ e_sdn, 0, 0);
+ call_back_data->retCode = rv;
+ }
+ if ( call_back_data->lock_flag == DO_TAKE_ACLCACHE_WRITELOCK) {
+ acllist_acicache_WRITE_UNLOCK();
+ }
+ }
+
+ }
+
+ /*
+ * If we get here it's success.
+ * The call_back_data->error is the error code that counts as it's the
+ * one that the original caller will see--this routine is called off a callbacl.
+ */
+
+ return ACL_FALSE; /* "local" error code--it's 0 */
+}
+/***************************************************************************
+*
+* __acl__RegisterAttributes
+*
+* Register all the attributes supported by the DS.
+*
+* Input:
+* None.
+*
+* Returns:
+* ACL_OK - No error
+* ACL_ERR - in case of errror
+*
+* Error Handling:
+* None.
+*
+**************************************************************************/
+static int
+__aclinit__RegisterAttributes(void)
+{
+
+ ACLMethod_t methodinfo;
+ NSErr_t errp;
+ int rv;
+
+ memset (&errp, 0, sizeof(NSErr_t));
+
+ rv = ACL_MethodRegister(&errp, DS_METHOD, &methodinfo);
+ if (rv < 0) {
+ acl_print_acllib_err(&errp, NULL);
+ slapi_log_error(SLAPI_LOG_FATAL, plugin_name,
+ "Unable to Register the methods\n", 0,0,0);
+ return ACL_ERR;
+ }
+ rv = ACL_MethodSetDefault (&errp, methodinfo);
+ if (rv < 0) {
+ acl_print_acllib_err(&errp, NULL);
+ slapi_log_error(SLAPI_LOG_FATAL, plugin_name,
+ "Unable to Set the default method\n", 0,0,0);
+ return ACL_ERR;
+ }
+ rv = ACL_AttrGetterRegister(&errp, ACL_ATTR_IP, DS_LASIpGetter,
+ methodinfo, ACL_DBTYPE_ANY, ACL_AT_FRONT, NULL);
+ if (rv < 0) {
+ acl_print_acllib_err(&errp, NULL);
+ slapi_log_error(SLAPI_LOG_FATAL, plugin_name,
+ "Unable to Register Attr ip\n", 0,0,0);
+ return ACL_ERR;
+ }
+ rv = ACL_AttrGetterRegister(&errp, ACL_ATTR_DNS, DS_LASDnsGetter,
+ methodinfo, ACL_DBTYPE_ANY, ACL_AT_FRONT, NULL);
+ if (rv < 0) {
+ acl_print_acllib_err(&errp, NULL);
+ slapi_log_error(SLAPI_LOG_FATAL, plugin_name,
+ "Unable to Register Attr dns\n", 0,0,0);
+ return ACL_ERR;
+ }
+ return ACL_OK;
+}
+
+/***************************************************************************
+*
+* __acl__RegisterLases
+* Register all the LASes supported by the DS.
+*
+* The DS doesnot support user/group. We have defined our own LAS
+* so that we can display/print an error when the LAS is invoked.
+* Input:
+* None.
+*
+* Returns:
+* ACL_OK - No error
+* ACL_ERR - in case of errror
+*
+* Error Handling:
+* None.
+*
+**************************************************************************/
+static int
+__aclinit__RegisterLases(void)
+{
+
+ if (ACL_LasRegister(NULL, DS_LAS_USER, (LASEvalFunc_t) DS_LASUserEval,
+ (LASFlushFunc_t) NULL) < 0) {
+ slapi_log_error (SLAPI_LOG_FATAL, plugin_name,
+ "Unable to register USER Las\n",0,0,0);
+ return ACL_ERR;
+ }
+ if (ACL_LasRegister(NULL, DS_LAS_GROUP, (LASEvalFunc_t) DS_LASGroupEval,
+ (LASFlushFunc_t) NULL) < 0) {
+ slapi_log_error (SLAPI_LOG_FATAL, plugin_name,
+ "Unable to register GROUP Las\n",0,0,0);
+ return ACL_ERR;
+ }
+ if (ACL_LasRegister(NULL, DS_LAS_GROUPDN, (LASEvalFunc_t)DS_LASGroupDnEval,
+ (LASFlushFunc_t)NULL) < 0) {
+ slapi_log_error (SLAPI_LOG_FATAL, plugin_name,
+ "Unable to register GROUPDN Las\n",0,0,0);
+ return ACL_ERR;
+ }
+ if (ACL_LasRegister(NULL, DS_LAS_ROLEDN, (LASEvalFunc_t)DS_LASRoleDnEval,
+ (LASFlushFunc_t)NULL) < 0) {
+ slapi_log_error (SLAPI_LOG_FATAL, plugin_name,
+ "Unable to register ROLEDN Las\n",0,0,0);
+ return ACL_ERR;
+ }
+ if (ACL_LasRegister(NULL, DS_LAS_USERDN, (LASEvalFunc_t)DS_LASUserDnEval,
+ (LASFlushFunc_t)NULL) < 0) {
+ slapi_log_error (SLAPI_LOG_FATAL, plugin_name,
+ "Unable to register USERDN Las\n",0,0,0);
+ return ACL_ERR;
+ }
+ if (ACL_LasRegister(NULL, DS_LAS_USERDNATTR,
+ (LASEvalFunc_t)DS_LASUserDnAttrEval,
+ (LASFlushFunc_t)NULL) < 0) {
+ slapi_log_error (SLAPI_LOG_FATAL, plugin_name,
+ "Unable to register USERDNATTR Las\n",0,0,0);
+ return ACL_ERR;
+ }
+ if (ACL_LasRegister(NULL, DS_LAS_AUTHMETHOD,
+ (LASEvalFunc_t)DS_LASAuthMethodEval,
+ (LASFlushFunc_t)NULL) < 0) {
+ slapi_log_error (SLAPI_LOG_FATAL, plugin_name,
+ "Unable to register CLIENTAUTHTYPE Las\n",0,0,0);
+ return ACL_ERR;
+ }
+ if (ACL_LasRegister(NULL, DS_LAS_GROUPDNATTR,
+ (LASEvalFunc_t)DS_LASGroupDnAttrEval,
+ (LASFlushFunc_t)NULL) < 0) {
+ slapi_log_error (SLAPI_LOG_FATAL, plugin_name,
+ "Unable to register GROUPDNATTR Las\n",0,0,0);
+ return ACL_ERR;
+ }
+ if (ACL_LasRegister(NULL, DS_LAS_USERATTR,
+ (LASEvalFunc_t)DS_LASUserAttrEval,
+ (LASFlushFunc_t)NULL) < 0) {
+ slapi_log_error (SLAPI_LOG_FATAL, plugin_name,
+ "Unable to register USERATTR Las\n",0,0,0);
+ return ACL_ERR;
+ }
+ return ACL_OK;
+}
diff --git a/ldap/servers/plugins/acl/acllas.c b/ldap/servers/plugins/acl/acllas.c
new file mode 100644
index 00000000..0179b0e6
--- /dev/null
+++ b/ldap/servers/plugins/acl/acllas.c
@@ -0,0 +1,3848 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+
+#include <ipfstruct.h>
+#include "acl.h"
+
+/*
+ A word on this file:
+
+ The various routines here implement each component of the subject of an aci
+ eg. "groupdn", "userdn","roledn", "userattr" etc.
+ They are responsible for evaluating each individual keyword not for doing
+ the boolean combination of these keywords, nor for combining multiple
+ allow()/deny() statements--that's libaccess's job.
+ For example, for "groupdn", DS_LASGroupDnEval might have to evaluate
+ something like this:
+
+ "groupdn = "ldap:///cn=G1,o=sun.com || ldap:///cn=G2,o=sun.com"
+
+ The "=" here may be "!=" as well and these routines take care of the
+ comparator.
+
+ These rotuines get called via acl__TestRights(), which calls
+ ACL_EvalTestRights() a libaccess routine (the immediately calling routine is
+ ACLEvalAce() in oneeval.cpp).
+
+ They should return LAS_EVAL_TRUE, if that keyword component evaluates to
+ TRUE, LAS_EVAL_FALSE if it evaluates to FALSE and LAS_EVAL_FAIL if an
+ error occurrs during evaluation. Note that once any component of a subject
+ returns LAS_EVAL_FAIL, the evaluation in libaccess stops and the whole
+ subject does not match and that aci is not applied.
+*/
+
+/*
+
+ A word on three-valued logic:
+
+ In general when you do boolean combination of terms some of which
+ may evaluate to UNDEFINED then you need to define what the combination
+ means.
+ So, for example libaccess implements a scheme which once UNDEFINED
+ is returned for a term, it bales out of the
+ evaluation and the whole expression evaluates to UNDEFINED.
+ In this case the aci will not apply.
+ On the other hand LDAP filters (cf. rfc2251 4.5.1) say that for OR,
+ an expression will
+ evaluate to TRUE if any term is TRUE, even if some terms are UNDEFINED.
+ Other off the cuff options might be to redefine UNDEFINED to be FALSE,
+ or TRUE.
+
+ Which is best ?
+
+ Well it probably depends on exactly what is to decided based on the
+ evaluation of the logical expression. However, the final suggestion is
+ almost certainly
+ bad--you are unlikely to want to take an action based on an undefined
+ result and
+ defining UNDEFINED to be either TRUE or FALSE may result in the overall
+ expression
+ returning TRUE--a security hole. The only case this might work is if you
+ are dealing with restricted
+ expressions eg. terms may only be AND'ed togther--in this case defining
+ UNDEFINED to be FALSE would guarantee a result of FALSE.
+
+ The libaccess approach of returning UNDEFINED once an UNDEFINED is
+ encountered during
+ evaluation is not too bad--at least it guarantees that no aci will apply
+ based on an
+ undefined value. However, with an aci like this "...allow(all) A or B"
+ where A returned UNDEFINED, you might be disappointed not to receive the
+ rights if it was B that
+ was granting you the rights and evaluation of A, which has nothing to do
+ with you, returns UNDEFINED. In the case of an aci like
+ "...deny(all) A or B" then the same
+ situation is arguably a security hole. Note that this scheme also makes
+ the final result
+ dependent on the evaluation order and so if the evaluation engine does
+ anything fancy internally (eg. reordering the terms in an OR so that fast
+ to evaluate ones came first) then
+ this would need to be documented so that a user (or a tool) could look at
+ the external syntax and figure out the result of the evaluation.
+ Also it breaks commutivity and De Morgans law.
+
+ The LDAP filter scheme is starting to look good--it solves the problems of
+ the
+ libaccess approach, makes the final result of an expression independent of
+ the evaluation order and
+ gives you back commutivity of OR and AND. De Morgans is still broken, but
+ that's because of the asymmetry of behaviour of UNDEFINED with OR and AND.
+
+ So...?
+
+ For acis, in general it can look like this:
+
+ "...allow(rights)(LogicalCombinationofBindRule);
+ deny(LogicalCombinationOfBindRule)...."
+
+ A BindRule is one of the "userdn", "groupdn" or "userattr" things and it
+ can look like this:
+
+ "groupdn = "ldap:///cn=G1,o=sun.com || ldap:///cn=G2,o=sun.com"
+
+ The "=" here may be "!=" as well and these routines take care of the
+ comparator.
+
+ For "userattr" keywords a mutilvalued attribute amounts a logical OR of the
+ individual values. There is also a logical OR over the different levels
+ as specified by the "parent" keyword.
+
+ In fact there are three levels of logical combination:
+
+ 1. In the aclplugin:
+ The "||" and "!=" combinator for BindRule keywords like userdn and
+ groupdn.
+ The fact that for the "userattr" keyword, a mutilvalued attribute is
+ evaluated as "||". Same for the different levels.
+ 2. In libaccess:
+ The logical combination of BindRules.
+ 3. In libaccess:
+ The evaluation of multiple BindRules seperated by ";", which means OR.
+
+ The LDAP filter three-valued logic SHOULD be applied to each level but
+ here's the way it works right now:
+
+ 1. At this level it depends....
+
+ DS_LASIpGetter - get attr for IP -
+ returns ip address or LAS_EVAL_FAIL for error.
+ no logical combination.
+ DS_LASDnsGetter - get attr for DNS-
+ returns dns name or LAS_EVAL_FAIL for error
+ no logical combination.
+ DS_LASUserDnEval - LAS Evaluation for USERDN -
+ three-valued logic
+ logical combination: || and !=
+ DS_LASGroupDnEval - LAS Evaluation for GROUPDN -
+ three-valued logic
+ logical combination: || and !=
+ DS_LASRoleDnEval - LAS Evaluation for ROLEDN -
+ three-valued logic
+ logical combination: || and !=
+ DS_LASUserDnAttrEval - LAS Evaluation for USERDNATTR -
+ three-valued logic
+ logical combination || (over specified attribute values and
+ parent keyword levels), !=
+ DS_LASAuthMethodEval - LAS Evaluation for AUTHMETHOD -
+ three-valued logic ( logical combinations: !=)
+ DS_LASGroupDnAttrEval - LAS Evaluation for GROUPDNATTR -
+ three-valued logic
+ logical combination || (over specified attribute values and
+ parent keyword levels), !=
+ DS_LASUserAttrEval - LAS Evaluation for USERATTR -
+ USER, GROUPDN and ROLEDN as above.
+ LDAPURL -- three-valued logic (logical combinations: || over
+ specified attribute vales, !=)
+ attrname#attrvalue -- three-valued logic, logical combination:!=
+
+ 2. The libaccess scheme applies at this level.
+ 3. The LDAP filter three-valued logic applies at this level.
+
+ Example of realistic, non-bizarre things that cause evaluation of a
+ BindRule to be undefined are exceeding some resource limits (nesting level,
+ lookthrough limit) in group membership evaluation, or trying to get ADD
+ permission from the "userattr" keyword at "parent" level 0.
+ Note that not everything that might be construed as an error needs to be
+ taken as UNDEFINED. For example, things like not finding a user or an
+ attribute in an entry can be defined away as TRUE or FALSE. eg. in an
+ LDAP filter (cn=rob) applied to an entry where cn is not present is FALSE,
+ not UNDEFINED. Similarly, if the number of levels in a parent keyword
+ exceeds the allowed limit, we just ignore the rest--though this
+ is a syntax error which should be detected at parse time.
+
+
+*/
+
+/* To get around warning: declared in ldapserver/lib/ldaputil/ldaputili.h */
+extern int ldapu_member_certificate_match (void* cert, const char* desc);
+
+/****************************************************************************/
+/* Defines, Constants, ande Declarations */
+/****************************************************************************/
+static char* const type_objectClass = "objectclass";
+static char* const filter_groups = "(|(objectclass=groupOfNames) (objectclass=groupOfUniqueNames)(objectclass=groupOfCertificates)(objectclass=groupOfURLs))";
+static char* const type_member = "member";
+static char* const type_uniquemember = "uniquemember";
+static char* const type_memberURL = "memberURL";
+static char* const type_memberCert = "memberCertificateDescription";
+
+/* cache strategy for groups */
+#define ACLLAS_CACHE_MEMBER_GROUPS 0x1
+#define ACLLAS_CACHE_NOT_MEMBER_GROUPS 0x2
+#define ACLLAS_CACHE_ALL_GROUPS 0x3
+
+/****************************************************************************/
+/* prototypes */
+/****************************************************************************/
+static int acllas__handle_group_entry(Slapi_Entry *, void *);
+static int acllas__user_ismember_of_group(struct acl_pblock *aclpb,
+ char* groupDN,
+ char* clientDN,
+ int cache_status,
+ CERTCertificate *clientCert);
+static int acllas__user_has_role( struct acl_pblock *aclpb,
+ Slapi_DN *roleDN, Slapi_DN *clientDn);
+static int acllas__add_allgroups (Slapi_Entry* e, void *callback_data);
+static int acllas__eval_memberGroupDnAttr (char *attrName,
+ Slapi_Entry *e,
+ char *n_clientdn,
+ struct acl_pblock *aclpb);
+static int acllas__verify_client (Slapi_Entry* e, void *callback_data);
+static char* acllas__dn_parent( char *dn, int level);
+static int acllas__get_members (Slapi_Entry* e, void *callback_data);
+static int acllas__client_match_URL (struct acl_pblock *aclpb,
+ char *n_dn, char *url );
+static int acllas__handle_client_search (Slapi_Entry *e, void *callback_data);
+static int __acllas_setup ( NSErr_t *errp, char *attr_name, CmpOp_t comparator,
+ char *attr_pattern, int *cachable, void **LAS_cookie,
+ PList_t subject, PList_t resource, PList_t auth_info,
+ PList_t global_auth, char *lasType, char *lasName, lasInfo *linfo);
+int
+aclutil_evaluate_macro( char * user, lasInfo *lasinfo,
+ acl_eval_types evalType );
+static int
+acllas_eval_one_user( struct acl_pblock *aclpb,
+ char * clientDN, char *userKeyword);
+static int
+acllas_eval_one_group(char *group, lasInfo *lasinfo);
+static int
+acllas_eval_one_role(char *role, lasInfo *lasinfo);
+static char **
+acllas_replace_dn_macro( char *rule, char *matched_val, lasInfo *lasinfo);
+static char **
+acllas_replace_attr_macro( char *rule, lasInfo *lasinfo);
+static int
+acllas_eval_one_target_filter( char * str, Slapi_Entry *e);
+
+/****************************************************************************/
+
+int
+DS_LASIpGetter(NSErr_t *errp, PList_t subject, PList_t resource, PList_t
+ auth_info, PList_t global_auth, void *arg)
+{
+
+ struct acl_pblock *aclpb = NULL;
+ IPAddr_t ip=0;
+ PRNetAddr client_praddr;
+ struct in_addr client_addr;
+ int rv;
+
+
+ rv = ACL_GetAttribute(errp, DS_PROP_ACLPB, (void **)&aclpb,
+ subject, resource, auth_info, global_auth);
+ if ( rv != LAS_EVAL_TRUE || ( NULL == aclpb )) {
+ acl_print_acllib_err(errp, NULL);
+ slapi_log_error( SLAPI_LOG_ACL, plugin_name,
+ "DS_LASIpGetter:Unable to get the ACLPB(%d)\n", rv,0,0);
+ return LAS_EVAL_FAIL;
+ }
+
+ if ( slapi_pblock_get( aclpb->aclpb_pblock, SLAPI_CONN_CLIENTNETADDR,
+ &client_praddr ) != 0 ) {
+ slapi_log_error( SLAPI_LOG_FATAL, plugin_name, "Could not get client IP.\n" );
+ return( LAS_EVAL_FAIL );
+ }
+
+ if ( !PR_IsNetAddrType(&client_praddr, PR_IpAddrV4Mapped) ) {
+ slapi_log_error( SLAPI_LOG_ACL, plugin_name,
+ "Client address is IPv6. ACLs only support IPv4 addresses so far.\n");
+ return( LAS_EVAL_FAIL );
+ }
+
+ client_addr.s_addr = client_praddr.ipv6.ip.pr_s6_addr32[3];
+
+ ip = (IPAddr_t) ntohl( client_addr.s_addr );
+ rv = PListInitProp(subject, 0, ACL_ATTR_IP, (void *)ip, NULL);
+
+ slapi_log_error( SLAPI_LOG_ACL, plugin_name,
+ "Returning client ip address '%s'\n",
+ (slapi_is_loglevel_set(SLAPI_LOG_ACL) ? inet_ntoa(client_addr) : ""));
+
+ return LAS_EVAL_TRUE;
+
+}
+
+/*
+ * This is called from the libaccess code when it needs to find a dns name.
+ * It's called from ACL_GetAttribute() when it finds that ACL_ATTR_DNS is
+ * not already part of the proplist.
+ *
+*/
+
+int
+DS_LASDnsGetter(NSErr_t *errp, PList_t subject, PList_t resource, PList_t
+ auth_info, PList_t global_auth, void *arg)
+{
+ struct acl_pblock *aclpb = NULL;
+ PRNetAddr client_praddr;
+ PRHostEnt *hp;
+ char *dnsName = NULL;
+ int rv;
+ struct berval **clientDns;
+
+
+ rv = ACL_GetAttribute(errp, DS_PROP_ACLPB, (void **)&aclpb,
+ subject, resource, auth_info, global_auth);
+ if ( rv != LAS_EVAL_TRUE || ( NULL == aclpb )) {
+ acl_print_acllib_err(errp, NULL);
+ slapi_log_error( SLAPI_LOG_ACL, plugin_name,
+ "DS_LASDnsGetter:Unable to get the ACLPB(%d)\n", rv,0,0);
+ return LAS_EVAL_FAIL;
+ }
+
+ if ( slapi_pblock_get( aclpb->aclpb_pblock, SLAPI_CLIENT_DNS, &clientDns ) != 0 ) {
+ slapi_log_error( SLAPI_LOG_FATAL, plugin_name, "Could not get client IP.\n" );
+ return( LAS_EVAL_FAIL );
+ }
+
+ /*
+ * If the client hostname has already been put into the pblock then
+ * use that. Otherwise we work it out and add it ourselves.
+ * This info is connection-lifetime so with multiple operaitons on the same
+ * connection we will only do the calculation once.
+ *
+ * rbyrneXXX surely this code would be better in connection.c so
+ * the name would be just there waiting for us, and everyone else.
+ *
+ */
+
+ if ( clientDns && clientDns[0] != NULL && clientDns[0]->bv_val ) {
+ dnsName = clientDns[0]->bv_val;
+ } else {
+ struct berval **dnsList;
+ char buf[PR_NETDB_BUF_SIZE];
+
+ if ( slapi_pblock_get( aclpb->aclpb_pblock, SLAPI_CONN_CLIENTNETADDR, &client_praddr ) != 0 ) {
+
+ slapi_log_error( SLAPI_LOG_FATAL, plugin_name, "Could not get client IP.\n" );
+ return( LAS_EVAL_FAIL );
+ }
+ hp = (PRHostEnt *)slapi_ch_malloc( sizeof(PRHostEnt) );
+ if ( PR_GetHostByAddr( &(client_praddr), (char *)buf, sizeof(buf), hp ) == PR_SUCCESS ) {
+ if ( hp->h_name != NULL ) {
+ dnsList = (struct berval**)
+ slapi_ch_calloc (1, sizeof(struct berval*) * (1 + 1));
+ *dnsList = (struct berval*)
+ slapi_ch_calloc ( 1, sizeof(struct berval));
+ dnsName = (*dnsList)->bv_val = slapi_ch_strdup( hp->h_name );
+ (*dnsList)->bv_len = strlen ( (*dnsList)->bv_val );
+ slapi_pblock_set( aclpb->aclpb_pblock, SLAPI_CLIENT_DNS, &dnsList );
+ }
+ }
+ slapi_ch_free( (void **)&hp );
+ }
+
+ if ( NULL == dnsName ) return LAS_EVAL_FAIL;
+
+ rv = PListInitProp(subject, 0, ACL_ATTR_DNS, dnsName, NULL);
+ if (rv < 0) {
+ slapi_log_error ( SLAPI_LOG_ACL, plugin_name,
+ "DS_LASDnsGetter:Couldn't set the DNS property(%d)\n", rv );
+ return LAS_EVAL_FAIL;
+ }
+ slapi_log_error ( SLAPI_LOG_ACL, plugin_name, "DNS name: %s\n", dnsName );
+ return LAS_EVAL_TRUE;
+
+}
+/***************************************************************************/
+/* New LASes */
+/* */
+/* 1. user, groups. -- stubs to report errors. Not supported. */
+/* 2. userdn */
+/* 3. groupdn */
+/* 4. userdnattr */
+/* 5. authmethod */
+/* 6. groupdnattr */
+/* 7. roledn */
+/* */
+/* */
+/***************************************************************************/
+int
+DS_LASUserEval(NSErr_t *errp, char *attr_name, CmpOp_t comparator,
+ char *attr_pattern, int *cachable, void **LAS_cookie,
+ PList_t subject, PList_t resource, PList_t auth_info,
+ PList_t global_auth)
+{
+ slapi_log_error(SLAPI_LOG_FATAL, plugin_name,
+ "User LAS is not supported in the ACL\n",0,0,0);
+
+ return LAS_EVAL_INVALID;
+}
+
+int
+DS_LASGroupEval(NSErr_t *errp, char *attr_name, CmpOp_t comparator,
+ char *attr_pattern, int *cachable, void **LAS_cookie,
+ PList_t subject, PList_t resource, PList_t auth_info,
+ PList_t global_auth)
+{
+ slapi_log_error(SLAPI_LOG_FATAL, plugin_name,
+ "Group LAS is not supported in the ACL\n",0,0,0);
+
+ return LAS_EVAL_INVALID;
+}
+
+/***************************************************************************
+*
+* DS_LASUserDnEval
+* Evaluate the "userdn" LAS. See if the user has rights.
+*
+* Input:
+* attr_name The string "userdn" - in lower case.
+* comparator CMP_OP_EQ or CMP_OP_NE only
+* attr_pattern A comma-separated list of users
+* cachable Always set to FALSE.
+* subject Subject property list
+* resource Resource property list
+* auth_info Authentication info, if any
+*
+* Returns:
+* retcode The usual LAS return codes.
+*
+* Error Handling:
+* None.
+*
+**************************************************************************/
+int
+DS_LASUserDnEval(NSErr_t *errp, char *attr_name, CmpOp_t comparator,
+ char *attr_pattern, int *cachable, void **LAS_cookie,
+ PList_t subject, PList_t resource, PList_t auth_info,
+ PList_t global_auth)
+{
+
+ char *users = NULL;
+ char *s_user, *user = NULL;
+ char *ptr = NULL;
+ char *end_dn = NULL;
+ char *n_edn = NULL;
+ char *parent_dn = NULL;
+ int matched;
+ int rc;
+ short len;
+ char *s = NULL;
+ const size_t LDAP_URL_prefix_len = strlen(LDAP_URL_prefix);
+ lasInfo lasinfo;
+ int got_undefined = 0;
+
+ if ( 0 != (rc = __acllas_setup (errp, attr_name, comparator,
+ attr_pattern,cachable,LAS_cookie,
+ subject, resource, auth_info,global_auth,
+ DS_LAS_USERDN, "DS_LASUserDnEval", &lasinfo )) ) {
+ return LAS_EVAL_FAIL;
+ }
+
+ users = slapi_ch_strdup(attr_pattern);
+ user = users;
+ matched = ACL_FALSE;
+
+ /* check if the clientdn is one of the users */
+ while(user != 0 && *user != 0 && matched != ACL_TRUE ) {
+
+ /* ignore leading whitespace */
+ while(ldap_utf8isspace(user))
+ LDAP_UTF8INC(user);
+
+ /* Now we must see the userdn in the following
+ ** formats:
+ **
+ ** The following formats are supported:
+ **
+ ** 1. The DN itself:
+ ** allow (read) userdn = "ldap:///cn=prasanta, ..."
+ **
+ ** 2. keyword SELF:
+ ** allow (write)
+ ** userdn = "ldap:///self"
+ **
+ ** 3. Pattern:
+ ** deny (read) userdn = "ldap:///cn=*, o=netscape, c = us";
+ **
+ ** 4. Anonymous user
+ ** deny (read, write) userdn = "ldap:///anyone"
+ **
+ ** 5. All users (All authenticated users)
+ ** allow (search) ** userdn = "ldap:///all"
+ ** 6. parent "ldap:///parent"
+ ** 7. Synamic users using the URL
+ **
+ **
+ ** DNs must be separated by "||". Ex:
+ ** allow (read)
+ ** userdn = "ldap:///DN1 || ldap:///DN2"
+ */
+
+
+ /* The DN is now "ldap:///DN"
+ ** remove the "ldap:///" part
+ */
+ if (strncasecmp (user, LDAP_URL_prefix,
+ LDAP_URL_prefix_len) == 0) {
+ s_user = user;
+ user += LDAP_URL_prefix_len;
+
+ } else {
+ char ebuf[ BUFSIZ ];
+ slapi_log_error(SLAPI_LOG_FATAL, plugin_name,
+ "DS_LASUserDnEval:Syntax error(%s)\n",
+ escape_string_with_punctuation( user, ebuf ), 0,0);
+ return LAS_EVAL_FAIL;
+ }
+
+ /* Now we have the starting point of the "userdn" */
+ if ((end_dn = strstr(user, "||")) != NULL) {
+ auto char *t = end_dn;
+ LDAP_UTF8INC(end_dn);
+ LDAP_UTF8INC(end_dn);
+ *t = 0;
+ }
+
+ /* Now user is a null terminated string */
+
+ if (*user) {
+ while(ldap_utf8isspace(user))
+ LDAP_UTF8INC(user);
+ /* ignore trailing whitespace */
+ len = strlen(user);
+ ptr = user+len-1;
+ while(ldap_utf8isspace(ptr)){ *ptr = '\0'; LDAP_UTF8DEC(ptr); }
+ }
+
+ /*
+ ** Check , if the user is a anonymous user. In that case
+ ** We must find the rule "ldap:///anyone"
+ */
+ if (lasinfo.anomUser) {
+ if (strcasecmp(user, "anyone") == 0 ) {
+ /* matches -- anonymous user */
+ matched = ACL_TRUE;
+ break;
+ }
+ } else {
+ /* URL format */
+
+ if ((s = strstr (user, ACL_RULE_MACRO_DN_KEY)) != NULL ||
+ (s = strstr (user, ACL_RULE_MACRO_DN_LEVELS_KEY)) != NULL ||
+ (s = strstr (user, ACL_RULE_MACRO_ATTR_KEY)) != NULL) {
+
+ matched = aclutil_evaluate_macro( s_user, &lasinfo,
+ ACL_EVAL_USER);
+ if (matched == ACL_TRUE) {
+ break;
+ }
+
+ } else if ((s = strchr (user, '?'))!= NULL) {
+ /* URL format */
+ if (acllas__client_match_URL ( lasinfo.aclpb, lasinfo.clientDn,
+ s_user) == ACL_TRUE) {
+ matched = ACL_TRUE;
+ break;
+ }
+ } else if (strcasecmp(user, "anyone") == 0 ) {
+ /* Anyone means anyone in the world */
+ matched = ACL_TRUE;
+ break;
+ } else if (strcasecmp(user, "self") == 0) {
+ if (n_edn == NULL) {
+ n_edn = slapi_entry_get_ndn ( lasinfo.resourceEntry );
+ }
+ if (slapi_utf8casecmp((ACLUCHP)lasinfo.clientDn, (ACLUCHP)n_edn) == 0)
+ matched = ACL_TRUE;
+ break;
+ } else if (strcasecmp(user, "parent") == 0) {
+ if (n_edn == NULL) {
+ n_edn = slapi_entry_get_ndn ( lasinfo.resourceEntry );
+ }
+ /* get the parent */
+ parent_dn = slapi_dn_parent(n_edn);
+ if (parent_dn &&
+ slapi_utf8casecmp ((ACLUCHP)lasinfo.clientDn, (ACLUCHP)parent_dn) == 0)
+ matched = ACL_TRUE;
+
+ if (parent_dn) slapi_ch_free ( (void **) &parent_dn );
+ break;
+ } else if (strcasecmp(user, "all") == 0) {
+ /* matches -- */
+ matched = ACL_TRUE;
+ break;
+ } else if (strchr(user, '*')) {
+ char line[200];
+ char *lineptr = &line[0];
+ char *newline = NULL;
+ int lenu = 0;
+ Slapi_Filter *f = NULL;
+ char *tt;
+ int filterChoice;
+
+ /*
+ ** what we are doing is faking the str2simple()
+ ** function with a "userdn = "user")
+ */
+ for (tt = user; *tt; tt++)
+ *tt = TOLOWER ( *tt );
+
+ if ((lenu = strlen(user)) > 190) { /* 200 - 9 for "(userdn=%s)" */
+ newline = slapi_ch_malloc(lenu + 10);
+ lineptr = newline;
+ }
+
+ sprintf (lineptr, "(userdn=%s)", user);
+ if ((f = slapi_str2filter (lineptr)) == NULL) {
+ if (newline) slapi_ch_free((void **) &newline);
+ /* try the next one */
+ break;
+ }
+ if (newline) slapi_ch_free((void **) &newline);
+ filterChoice = slapi_filter_get_choice ( f );
+
+ if (( filterChoice != LDAP_FILTER_SUBSTRINGS) &&
+ ( filterChoice != LDAP_FILTER_PRESENT)) {
+ slapi_log_error( SLAPI_LOG_ACL, plugin_name,
+ "DS_LASUserDnEval:Error in gen. filter(%s)\n", user);
+ }
+ if ((rc = acl_match_substring( f,
+ lasinfo.clientDn,
+ 1 /*exact match */)
+ ) == ACL_TRUE) {
+ matched = ACL_TRUE;
+ slapi_filter_free(f,1);
+ break;
+ }
+ if (rc == ACL_ERR) {
+ slapi_log_error( SLAPI_LOG_ACL, plugin_name,
+ "DS_LASUserDnEval:Error in matching patteren(%s)\n",
+ user,0,0);
+ }
+ slapi_filter_free(f,1);
+ } else {
+ /* Must be a simple dn then */
+ if (slapi_utf8casecmp((ACLUCHP)lasinfo.clientDn,
+ (ACLUCHP)slapi_dn_normalize(user)) == 0) {
+ matched = ACL_TRUE;
+ break;
+ }
+ }
+ }
+
+ if ( matched == ACL_DONT_KNOW ) {
+ /* record this but keep going--maybe another user will evaluate to TRUE */
+ got_undefined = 1;
+ }
+ /* Nothing matched -- try the next DN */
+ user = end_dn;
+ } /* end of while */
+
+ slapi_ch_free ( (void **) &users);
+
+ /*
+ * If no terms were undefined, then evaluate as normal.
+ * If there was an undefined term, but another one was TRUE, then we also evaluate
+ * as normal. Otherwise, the whole expression is UNDEFINED.
+ */
+ if ( matched == ACL_TRUE || !got_undefined ) {
+ if (comparator == CMP_OP_EQ) {
+ rc = (matched == ACL_TRUE ? LAS_EVAL_TRUE : LAS_EVAL_FALSE);
+ } else {
+ rc = (matched == ACL_TRUE ? LAS_EVAL_FALSE : LAS_EVAL_TRUE);
+ }
+ } else {
+ rc = LAS_EVAL_FAIL;
+ slapi_log_error( SLAPI_LOG_ACL, plugin_name,
+ "Returning UNDEFINED for userdn evaluation.\n");
+ }
+
+ return rc;
+}
+
+/***************************************************************************
+*
+* DS_LASGroupDnEval
+*
+*
+* Input:
+* attr_name The string "userdn" - in lower case.
+* comparator CMP_OP_EQ or CMP_OP_NE only
+* attr_pattern A comma-separated list of users
+* cachable Always set to FALSE.
+* subject Subject property list
+* resource Resource property list
+* auth_info Authentication info, if any
+*
+* Returns:
+* retcode The usual LAS return code
+* If the client is in any of the groups mentioned this groupdn keywrod
+* then returns LAS_EVAL_TRUE, if he's not in any LAS_EVAL_FALSE.
+* If any of the membership evaluations fail, then it goes on to evaluate the
+* others.
+*
+* Error Handling:
+* None.
+*
+**************************************************************************/
+int
+DS_LASGroupDnEval(NSErr_t *errp, char *attr_name, CmpOp_t comparator,
+ char *attr_pattern, int *cachable, void **LAS_cookie,
+ PList_t subject, PList_t resource, PList_t auth_info,
+ PList_t global_auth)
+{
+
+ char *groups;
+ char *groupName;
+ char *ptr;
+ char *end_dn;
+ int matched;
+ int rc;
+ int len;
+ const size_t LDAP_URL_prefix_len = strlen(LDAP_URL_prefix);
+ int any_group = 0;
+ lasInfo lasinfo;
+ int got_undefined = 0;
+
+ /* the setup should not fail under normal operation */
+ if ( 0 != (rc = __acllas_setup (errp, attr_name, comparator,
+ attr_pattern,cachable,LAS_cookie,
+ subject, resource, auth_info,global_auth,
+ DS_LAS_GROUPDN, "DS_LASGroupDnEval", &lasinfo )) ) {
+ return LAS_EVAL_FAIL;
+ }
+
+ groups = slapi_ch_strdup(attr_pattern);
+ groupName = groups;
+ matched = ACL_FALSE;
+
+ /* check if the groupdn is one of the users */
+ while(groupName != 0 && *groupName != 0 && matched != ACL_TRUE) {
+
+ /* ignore leading whitespace */
+ while(ldap_utf8isspace(groupName))
+ LDAP_UTF8INC(groupName);
+
+ /*
+ ** The syntax allowed for the groupdn is
+ **
+ ** Example:
+ ** groupdn = "ldap:///dn1 || ldap:///dn2";
+ **
+ */
+
+ if (strncasecmp (groupName, LDAP_URL_prefix,
+ LDAP_URL_prefix_len) == 0) {
+ groupName += LDAP_URL_prefix_len;
+ } else {
+ char ebuf[ BUFSIZ ];
+ slapi_log_error(SLAPI_LOG_FATAL, plugin_name,
+ "DS_LASGroupDnEval:Syntax error(%s)\n",
+ escape_string_with_punctuation( groupName, ebuf ),0,0);
+ }
+
+ /* Now we have the starting point of the "groupdn" */
+ if ((end_dn = strstr(groupName, "||")) != NULL) {
+ auto char *t = end_dn;
+ LDAP_UTF8INC(end_dn);
+ LDAP_UTF8INC(end_dn);
+ *t = 0;
+ }
+
+ if (*groupName) {
+ while(ldap_utf8isspace(groupName))
+ LDAP_UTF8INC(groupName);
+ /* ignore trailing whitespace */
+ len = strlen(groupName);
+ ptr = groupName+len-1;
+ while(ldap_utf8isspace(ptr)) { *ptr = '\0'; LDAP_UTF8DEC(ptr); }
+ }
+
+ /*
+ ** Now we have the DN of the group. Evaluate the "clientdn"
+ ** and see if the user is a member of the group.
+ */
+ if (0 == (strcasecmp(groupName, "anyone"))) {
+ any_group = 1;
+ }
+
+ if (any_group) {
+ /* anyone in the world */
+ matched = ACL_TRUE;
+ break;
+ } else if ( lasinfo.anomUser &&
+ (lasinfo.aclpb->aclpb_clientcert == NULL) && (!any_group)) {
+ slapi_log_error( SLAPI_LOG_ACL, plugin_name,
+ "Group not evaluated(%s)\n", groupName);
+ break;
+ } else {
+ char *s;
+
+ if ((s = strstr (groupName, ACL_RULE_MACRO_DN_KEY)) != NULL ||
+ (s = strstr (groupName, ACL_RULE_MACRO_DN_LEVELS_KEY)) != NULL ||
+ (s = strstr (groupName, ACL_RULE_MACRO_ATTR_KEY)) != NULL) {
+
+ matched = aclutil_evaluate_macro( groupName, &lasinfo,
+ ACL_EVAL_GROUP);
+ slapi_log_error ( SLAPI_LOG_ACL, plugin_name,
+ "DS_LASGroupDnEval: Param group name:%s\n",
+ groupName);
+ } else {/* normal evaluation */
+
+ matched = acllas_eval_one_group( groupName, &lasinfo);
+
+ }
+
+ if ( matched == ACL_TRUE ) {
+ break;
+ } else if ( matched == ACL_DONT_KNOW ) {
+ /* record this but keep going--maybe another group will evaluate to TRUE */
+ got_undefined = 1;
+ }
+ }
+ /* Nothing matched -- try the next DN */
+ groupName = end_dn;
+
+ } /* end of while */
+
+ /*
+ * If no terms were undefined, then evaluate as normal.
+ * If there was an undefined term, but another one was TRUE, then we also evaluate
+ * as normal. Otherwise, the whole expression is UNDEFINED.
+ */
+ if ( matched == ACL_TRUE || !got_undefined ) {
+ if (comparator == CMP_OP_EQ) {
+ rc = (matched == ACL_TRUE ? LAS_EVAL_TRUE : LAS_EVAL_FALSE);
+ } else {
+ rc = (matched == ACL_TRUE ? LAS_EVAL_FALSE : LAS_EVAL_TRUE);
+ }
+ } else {
+ rc = LAS_EVAL_FAIL;
+ slapi_log_error( SLAPI_LOG_ACL, plugin_name,
+ "Returning UNDEFINED for groupdn evaluation.\n");
+ }
+
+ slapi_ch_free ((void**) &groups);
+ return rc;
+}
+/***************************************************************************
+*
+* DS_LASRoleDnEval
+*
+*
+* Input:
+* attr_name The string "roledn" - in lower case.
+* comparator CMP_OP_EQ or CMP_OP_NE only
+* attr_pattern A "||" sperated list of roles
+* cachable Always set to FALSE.
+* subject Subject property list
+* resource Resource property list
+* auth_info Authentication info, if any
+*
+* Returns:
+* retcode The usual LAS return codes.
+*
+* Error Handling:
+* None.
+*
+**************************************************************************/
+int
+DS_LASRoleDnEval(NSErr_t *errp, char *attr_name, CmpOp_t comparator,
+ char *attr_pattern, int *cachable, void **LAS_cookie,
+ PList_t subject, PList_t resource, PList_t auth_info,
+ PList_t global_auth)
+{
+
+ char *roles;
+ char *role;
+ char *ptr;
+ char *end_dn;
+ int matched;
+ int rc;
+ int len;
+ const size_t LDAP_URL_prefix_len = strlen(LDAP_URL_prefix);
+ int any_role = 0;
+ lasInfo lasinfo;
+ int got_undefined = 0;
+
+ if ( 0 != (rc = __acllas_setup (errp, attr_name, comparator,
+ attr_pattern,cachable,LAS_cookie,
+ subject, resource, auth_info,global_auth,
+ DS_LAS_ROLEDN, "DS_LASRoleDnEval",
+ &lasinfo )) ) {
+ return LAS_EVAL_FALSE;
+ }
+
+
+ roles = slapi_ch_strdup(attr_pattern);
+ role = roles;
+ matched = ACL_FALSE;
+
+ /* check if the roledn is one of the users */
+ while(role != 0 && *role != 0 && matched != ACL_TRUE) {
+
+ /* ignore leading whitespace */
+ while(ldap_utf8isspace(role))
+ LDAP_UTF8INC(role);
+
+ /*
+ ** The syntax allowed for the roledn is
+ **
+ ** Example:
+ ** roledn = "ldap:///roledn1 || ldap:///roledn2";
+ **
+ */
+
+ if (strncasecmp (role, LDAP_URL_prefix,
+ LDAP_URL_prefix_len) == 0) {
+ role += LDAP_URL_prefix_len;
+ } else {
+ char ebuf[ BUFSIZ ];
+ slapi_log_error(SLAPI_LOG_FATAL, plugin_name,
+ "DS_LASRoleDnEval:Syntax error(%s)\n",
+ escape_string_with_punctuation( role, ebuf ),0,0);
+ }
+
+ /* Now we have the starting point of the "roledn" */
+ if ((end_dn = strstr(role, "||")) != NULL) {
+ auto char *t = end_dn;
+ LDAP_UTF8INC(end_dn);
+ LDAP_UTF8INC(end_dn);
+ *t = 0;
+ }
+
+
+ if (*role) {
+ while(ldap_utf8isspace(role))
+ LDAP_UTF8INC(role);
+ /* ignore trailing whitespace */
+ len = strlen(role);
+ ptr = role+len-1;
+ while(ldap_utf8isspace(ptr)) { *ptr = '\0'; LDAP_UTF8DEC(ptr); }
+ }
+
+ /*
+ ** Now we have the DN of the role. Evaluate the "clientdn"
+ ** and see if the user has this role.
+ */
+ if (0 == (strcasecmp(role, "anyone"))) {
+ any_role = 1;
+ }
+
+ if (any_role) {
+ /* anyone in the world */
+ matched = ACL_TRUE;
+ break;
+ } else if ( lasinfo.anomUser &&
+ (lasinfo.aclpb->aclpb_clientcert == NULL) && (!any_role)) {
+ slapi_log_error( SLAPI_LOG_ACL, plugin_name,
+ "Role not evaluated(%s) for anon user\n", role);
+ break;
+ } else {
+
+ /* Take care of param strings */
+
+ char *s;
+
+ if ((s = strstr (role, ACL_RULE_MACRO_DN_KEY)) != NULL ||
+ (s = strstr (role, ACL_RULE_MACRO_DN_LEVELS_KEY)) != NULL ||
+ (s = strstr (role, ACL_RULE_MACRO_ATTR_KEY)) != NULL) {
+
+ matched = aclutil_evaluate_macro( role, &lasinfo,
+ ACL_EVAL_ROLE);
+ slapi_log_error ( SLAPI_LOG_ACL, plugin_name,
+ "DS_LASRoleDnEval: Param role name:%s\n",
+ role);
+ } else {/* normal evaluation */
+
+ matched = acllas_eval_one_role( role, &lasinfo);
+
+ }
+
+ if ( matched == ACL_TRUE ) {
+ break;
+ } else if ( matched == ACL_DONT_KNOW ) {
+ /* record this but keep going--maybe another role will evaluate to TRUE */
+ got_undefined = 1;
+ }
+ }
+ /* Nothing matched -- try the next DN */
+ role = end_dn;
+
+ } /* end of while */
+
+ /*
+ * If no terms were undefined, then evaluate as normal.
+ * If there was an undefined term, but another one was TRUE, then we also evaluate
+ * as normal. Otherwise, the whole expression is UNDEFINED.
+ */
+ if ( matched == ACL_TRUE || !got_undefined ) {
+ if (comparator == CMP_OP_EQ) {
+ rc = (matched == ACL_TRUE ? LAS_EVAL_TRUE : LAS_EVAL_FALSE);
+ } else {
+ rc = (matched == ACL_TRUE ? LAS_EVAL_FALSE : LAS_EVAL_TRUE);
+ }
+ } else {
+ rc = LAS_EVAL_FAIL;
+ slapi_log_error( SLAPI_LOG_ACL, plugin_name,
+ "Returning UNDEFINED for roledn evaluation.\n");
+ }
+
+ slapi_ch_free ((void**) &roles);
+ return rc;
+}
+/***************************************************************************
+*
+* DS_LASUserDnAttrEval
+*
+*
+* Input:
+* attr_name The string "userdn" - in lower case.
+* comparator CMP_OP_EQ or CMP_OP_NE only
+* attr_pattern A comma-separated list of users
+* cachable Always set to FALSE.
+* subject Subject property list
+* resource Resource property list
+* auth_info Authentication info, if any
+*
+* Returns:
+* retcode The usual LAS return codes.
+*
+* Error Handling:
+* None.
+*
+**************************************************************************/
+struct userdnattr_info {
+ char *attr;
+ int result;
+ char *clientdn;
+};
+#define ACLLAS_MAX_LEVELS 10
+int
+DS_LASUserDnAttrEval(NSErr_t *errp, char *attr_name, CmpOp_t comparator,
+ char *attr_pattern, int *cachable, void **LAS_cookie,
+ PList_t subject, PList_t resource, PList_t auth_info,
+ PList_t global_auth)
+{
+
+ char *n_currEntryDn = NULL;
+ char *s_attrName, *attrName;
+ char *ptr;
+ int matched;
+ int rc, len, i;
+ char *val;
+ Slapi_Attr *a;
+ int levels[ACLLAS_MAX_LEVELS];
+ int numOflevels =0;
+ struct userdnattr_info info;
+ char *attrs[2] = { LDAP_ALL_USER_ATTRS, NULL };
+ lasInfo lasinfo;
+ int got_undefined = 0;
+
+ if ( 0 != (rc = __acllas_setup (errp, attr_name, comparator,
+ attr_pattern,cachable,LAS_cookie,
+ subject, resource, auth_info,global_auth,
+ DS_LAS_USERDNATTR, "DS_LASUserDnAttrEval",
+ &lasinfo )) ) {
+ return LAS_EVAL_FAIL;
+ }
+
+ /*
+ ** The userdnAttr syntax is
+ ** userdnattr = <attribute> or
+ ** userdnattr = parent[0,2,4].attribute"
+ ** Ex:
+ ** userdnattr = manager; or
+ ** userdnattr = "parent[0,2,4].manager";
+ **
+ ** Here 0 means current level, 2 means grandfather and
+ ** 4 (great great grandfather)
+ **
+ ** The function of this LAS is to compare the value of the
+ ** attribute in the Slapi_Entry with the "userdn".
+ **
+ ** Ex: userdn: "cn=prasanta, o= netscape, c= us"
+ ** and in the Slapi_Entry the manager attribute has
+ ** manager = <value>. Compare the userdn with manager.value to
+ ** determine the result.
+ **
+ */
+ s_attrName = attrName = slapi_ch_strdup (attr_pattern);
+
+ /* ignore leading/trailing whitespace */
+ while(ldap_utf8isspace(attrName)) LDAP_UTF8INC(attrName);
+ len = strlen(attrName);
+ ptr = attrName+len-1;
+ while(ldap_utf8isspace(ptr)) { *ptr = '\0'; LDAP_UTF8DEC(ptr); }
+
+
+ /* See if we have a parent[2].attr" rule */
+ if ( (ptr = strstr(attrName, "parent[")) != NULL) {
+ char *word, *str, *next;
+
+ numOflevels = 0;
+ n_currEntryDn = slapi_entry_get_ndn ( lasinfo.resourceEntry );
+ str = attrName;
+
+ word = ldap_utf8strtok_r(str, "[],. ",&next);
+ /* The first word is "parent[" and so it's not important */
+
+ while ((word= ldap_utf8strtok_r(NULL, "[],.", &next)) != NULL) {
+ if (ldap_utf8isdigit(word)) {
+ while (word && ldap_utf8isspace(word)) LDAP_UTF8INC(word);
+ if (numOflevels < ACLLAS_MAX_LEVELS)
+ levels[numOflevels++] = atoi (word);
+ else {
+ /*
+ * Here, ignore the extra levels..it's really
+ * a syntax error which should have been ruled out at parse time
+ */
+ slapi_log_error( SLAPI_LOG_FATAL, plugin_name,
+ "DS_LASUserDnattr: Exceeded the ATTR LIMIT:%d: Ignoring extra levels\n",
+ ACLLAS_MAX_LEVELS);
+ }
+ } else {
+ /* Must be the attr name. We can goof of by
+ ** having parent[1,2,a] but then you have to be
+ ** stupid to do that.
+ */
+ char *p = word;
+ if (*--p == '.') {
+ attrName = word;
+ break;
+ }
+ }
+ }
+ info.attr = attrName;
+ info.clientdn = lasinfo.clientDn;
+ info.result = 0;
+ } else {
+ levels[0] = 0;
+ numOflevels = 1;
+
+ }
+
+ /* No attribute name specified--it's a syntax error and so undefined */
+ if (attrName == NULL ) {
+ slapi_ch_free ( (void**) &s_attrName);
+ return LAS_EVAL_FAIL;
+ }
+
+ slapi_log_error( SLAPI_LOG_ACL, plugin_name,"Attr:%s\n" , attrName, 0,0);
+ matched = ACL_FALSE;
+ for (i=0; i < numOflevels; i++) {
+ if ( levels[i] == 0 ) {
+ Slapi_Value *sval=NULL;
+ const struct berval *attrVal;
+ int j;
+
+ /*
+ * For the add operation, the resource itself (level 0)
+ * must never be allowed to grant access--
+ * This is because access would be granted based on a value
+ * of an attribute in the new entry--security hole.
+ *
+ */
+
+ if ( lasinfo.aclpb->aclpb_optype == SLAPI_OPERATION_ADD) {
+ slapi_log_error( SLAPI_LOG_ACL, plugin_name,
+ "ACL info: userdnAttr does not allow ADD permission at level 0.\n");
+ got_undefined = 1;
+ continue;
+ }
+ slapi_entry_attr_find( lasinfo.resourceEntry, attrName, &a);
+ if ( NULL == a ) continue;
+ j= slapi_attr_first_value ( a,&sval );
+ while ( j != -1 ) {
+ attrVal = slapi_value_get_berval ( sval );
+ /* Here if atleast 1 value matches then we are done.*/
+ val = slapi_dn_normalize (
+ slapi_ch_strdup( attrVal->bv_val));
+
+ if (slapi_utf8casecmp((ACLUCHP)val, (ACLUCHP)lasinfo.clientDn ) == 0) {
+ char ebuf [ BUFSIZ ];
+ /* Wow it matches */
+ slapi_log_error( SLAPI_LOG_ACL, plugin_name,
+ "userdnAttr matches(%s, %s) level (%d)\n",
+ val,
+ ACL_ESCAPE_STRING_WITH_PUNCTUATION (lasinfo.clientDn, ebuf),
+ 0);
+ matched = ACL_TRUE;
+ slapi_ch_free ( (void **) &val);
+ break;
+ }
+ slapi_ch_free ( (void**) &val);
+ j = slapi_attr_next_value ( a, j, &sval );
+ }
+ } else {
+ char *p_dn; /* parent dn */
+
+ p_dn = acllas__dn_parent (n_currEntryDn, levels[i]);
+ if (p_dn == NULL) continue;
+
+ /* use new search internal API */
+ {
+ Slapi_PBlock *aPb = slapi_pblock_new ();
+
+ /*
+ * This search may be chained if chaining for ACL is
+ * is enabled in the backend and the entry is in
+ * a chained backend.
+ */
+ slapi_search_internal_set_pb ( aPb,
+ p_dn,
+ LDAP_SCOPE_BASE,
+ "objectclass=*",
+ &attrs[0],
+ 0,
+ NULL /* controls */,
+ NULL /* uniqueid */,
+ aclplugin_get_identity (ACL_PLUGIN_IDENTITY),
+ 0 /* actions */);
+
+ slapi_search_internal_callback_pb(aPb,
+ &info /* callback_data */,
+ NULL/* result_callback */,
+ acllas__verify_client,
+ NULL /* referral_callback */);
+ slapi_pblock_destroy(aPb);
+ }
+
+ /*
+ * Currently info.result is boolean so
+ * we do not need to check for ACL_DONT_KNOW
+ */
+ if (info.result) {
+ matched = ACL_TRUE;
+ slapi_log_error( SLAPI_LOG_ACL, plugin_name,
+ "userdnAttr matches at level (%d)\n", levels[i]);
+ }
+ }
+ if (matched == ACL_TRUE) {
+ break;
+ }
+ }
+
+ slapi_ch_free ( (void **) &s_attrName);
+
+ /*
+ * If no terms were undefined, then evaluate as normal.
+ * If there was an undefined term, but another one was TRUE, then we also evaluate
+ * as normal. Otherwise, the whole expression is UNDEFINED.
+ */
+ if ( matched == ACL_TRUE || !got_undefined ) {
+ if (comparator == CMP_OP_EQ) {
+ rc = (matched == ACL_TRUE ? LAS_EVAL_TRUE : LAS_EVAL_FALSE);
+ } else {
+ rc = (matched == ACL_TRUE ? LAS_EVAL_FALSE : LAS_EVAL_TRUE);
+ }
+ } else {
+ rc = LAS_EVAL_FAIL;
+ slapi_log_error( SLAPI_LOG_ACL, plugin_name,
+ "Returning UNDEFINED for userdnattr evaluation.\n");
+ }
+
+ return rc;
+}
+/***************************************************************************
+*
+* DS_LASAuthMethodEval
+*
+*
+* Input:
+* attr_name The string "authmethod" - in lower case.
+* comparator CMP_OP_EQ or CMP_OP_NE only
+* attr_pattern A comma-separated list of users
+* cachable Always set to FALSE.
+* subject Subject property list
+* resource Resource property list
+* auth_info Authentication info, if any
+*
+* Returns:
+* retcode The usual LAS return codes.
+*
+* Error Handling:
+* None.
+*
+**************************************************************************/
+int
+DS_LASAuthMethodEval(NSErr_t *errp, char *attr_name, CmpOp_t comparator,
+ char *attr_pattern, int *cachable, void **LAS_cookie,
+ PList_t subject, PList_t resource, PList_t auth_info,
+ PList_t global_auth)
+{
+
+ char *attr;
+ char *ptr;
+ int len;
+ int matched;
+ int rc;
+ char *s = NULL;
+ lasInfo lasinfo;
+
+ if ( 0 != (rc = __acllas_setup (errp, attr_name, comparator,
+ attr_pattern,cachable,LAS_cookie,
+ subject, resource, auth_info,global_auth,
+ DS_LAS_AUTHMETHOD, "DS_LASAuthMethodEval",
+ &lasinfo )) ) {
+ return LAS_EVAL_FAIL;
+ }
+
+ attr = attr_pattern;
+
+ matched = ACL_FALSE;
+ /* ignore leading whitespace */
+ s = strstr (attr, SLAPD_AUTH_SASL);
+ if ( s) {
+ s +=4;
+ attr = s;
+ }
+
+ while(ldap_utf8isspace(attr)) LDAP_UTF8INC(attr);
+ len = strlen(attr);
+ ptr = attr+len-1;
+ while(ldap_utf8isspace(ptr)) { *ptr = '\0'; LDAP_UTF8DEC(ptr); }
+
+ slapi_log_error( SLAPI_LOG_ACL, plugin_name,
+ "DS_LASAuthMethodEval:authtype:%s authmethod:%s\n",
+ lasinfo.authType, attr);
+
+ /* None method means, we don't care -- otherwise we care */
+ if ((strcasecmp(attr, "none") == 0) ||
+ (strcasecmp(attr, lasinfo.authType) == 0)) {
+ matched = ACL_TRUE;
+ }
+
+ if ( matched == ACL_TRUE || matched == ACL_FALSE) {
+ if (comparator == CMP_OP_EQ) {
+ rc = (matched == ACL_TRUE ? LAS_EVAL_TRUE : LAS_EVAL_FALSE);
+ } else {
+ rc = (matched == ACL_TRUE ? LAS_EVAL_FALSE : LAS_EVAL_TRUE);
+ }
+ } else {
+ rc = LAS_EVAL_FAIL;
+ slapi_log_error( SLAPI_LOG_ACL, plugin_name,
+ "Returning UNDEFINED for authmethod evaluation.\n");
+ }
+
+ return rc;
+}
+
+/****************************************************************************
+* Struct to evaluate and keep the current members being evaluated
+*
+* 0 1 2 3 4 5
+* member: [a,b,c,d,e,f]
+* c_idx may point to 2 i.e to "c" if "c" is being evaluated to
+* see if any of "c" members is the clientDN.
+* lu_idx points to the last used spot i.e 5.
+* lu_idx++ is the next free spot.
+*
+* We allocate ACLLAS_MAX_GRP_MEMBER ptr first and then we add if it
+* is required.
+*
+***************************************************************************/
+#define ACLLAS_MAX_GRP_MEMBER 50
+struct member_info
+{
+ char *member; /* member DN */
+ struct member_info *parent; /* parent of this member */
+} member_info;
+
+struct eval_info
+{
+ int result; /* result status */
+ char *userDN; /* client's normalized DN */
+ int c_idx; /* Index to the current member being processed */
+ int lu_idx; /* Index to the slot where the last member is stored */
+ char **member; /* mmebers list */
+ struct member_info **memberInfo;/* array of memberInfo */
+ CERTCertificate *clientCert; /* ptr to cert */
+ struct acl_pblock *aclpb; /*aclpblock */
+} eval_info;
+
+static void
+dump_member_info ( struct member_info *minfo, char *buf )
+{
+ if ( minfo )
+ {
+ if ( minfo->parent )
+ {
+ dump_member_info ( minfo->parent, buf );
+ }
+ else
+ {
+ strcat ( buf, "<nil>" );
+ }
+ strcat ( buf, "->" );
+ strcat ( buf, minfo->member );
+ }
+}
+
+static void
+dump_eval_info (char *caller, struct eval_info *info, int idx)
+{
+ char buf[1024];
+ int len;
+ int i;
+
+ if ( idx < 0 )
+ {
+ sprintf ( buf, "\nuserDN=\"%s\"\nmember=", info->userDN);
+ if (info->member)
+ {
+ len = strlen (buf);
+ sprintf ( &(buf[len]), "\"%s\"", info->member );
+ }
+ len = strlen (buf);
+ sprintf ( &(buf[len]), "\nmemberinfo[%d]-[%d]:", info->c_idx, info->lu_idx );
+ if ( info->memberInfo )
+ for (i = 0; i <= info->lu_idx; i++)
+ {
+ len = strlen(buf);
+ sprintf ( &buf[len], "\n [%d]: ", i );
+ dump_member_info ( info->memberInfo[i], buf );
+ }
+ slapi_log_error ( SLAPI_LOG_FATAL, NULL, "\n======== candidate member info in eval_info ========%s\n\n", buf );
+ }
+ else
+ {
+ sprintf (buf, "evaluated candidate [%d]=", idx);
+ switch (info->result)
+ {
+ case ACL_TRUE:
+ strcat (buf, "ACL_TRUE\n");
+ break;
+ case ACL_FALSE:
+ strcat (buf, "ACL_FALSE\n");
+ break;
+ case ACL_DONT_KNOW:
+ strcat (buf, "ACL_DONT_KNOW\n");
+ break;
+ default:
+ len = strlen (buf);
+ sprintf ( &(buf[len]), "%d\n", info->result );
+ break;
+ }
+ dump_member_info ( info->memberInfo[idx], buf );
+ slapi_log_error ( SLAPI_LOG_FATAL, NULL, "%s\n", buf );
+ }
+}
+
+
+/***************************************************************************
+*
+* acllas__user_ismember_of_group
+*
+* Check if the user is a member of the group and nested groups..
+*
+* Input:
+* char *groupdn - DN of the group
+* char *clientDN - Dn of the client
+*
+* Returns:
+* ACL_TRUE - the user is a member of the group.
+* ACL_FALSE - Not a member
+* ACL_DONT_KNOW - Any errors eg. resource limits exceeded and we could
+* not compelte the evaluation.
+*
+* Error Handling:
+* None.
+*
+**************************************************************************/
+static int
+acllas__user_ismember_of_group( struct acl_pblock *aclpb,
+ char* groupDN,
+ char* clientDN,
+ int cache_status,
+ CERTCertificate *clientCert)
+{
+
+
+ char *attrs[5];
+ char *currDN;
+ int i,j;
+ int result = ACL_FALSE;
+ struct eval_info info;
+ int nesting_level;
+ int numOfMembersAtCurrentLevel;
+ int numOfMembersVisited;
+ int totalMembersVisited;
+ int numOfMembers;
+ int max_nestlevel;
+ int max_memberlimit;
+ aclUserGroup *u_group;
+ char ebuf [ BUFSIZ ];
+ struct member_info *groupMember = NULL;
+ struct member_info *parentGroup = NULL;
+
+ /*
+ ** First, Let's look thru the cached list and determine if the client is
+ ** a member of the cached list of groups.
+ */
+ if ( (u_group = aclg_get_usersGroup ( aclpb , clientDN )) == NULL) {
+ slapi_log_error( SLAPI_LOG_ACL, plugin_name,
+ "Failed to find/allocate a usergroup--aborting evaluation\n", 0, 0);
+ return(ACL_DONT_KNOW);
+ }
+
+ slapi_log_error( SLAPI_LOG_ACL, plugin_name, "Evaluating user %s in group %s?\n",
+ clientDN, groupDN );
+
+ /* Before I start using, get a reader lock on the group cache */
+ aclg_lock_groupCache ( 1 /* reader */ );
+ for ( i= 0; i < u_group->aclug_numof_member_group; i++) {
+ slapi_log_error( SLAPI_LOG_ACL, plugin_name, "-- In %s\n",
+ u_group->aclug_member_groups[i] );
+ if ( slapi_utf8casecmp((ACLUCHP)groupDN, (ACLUCHP)u_group->aclug_member_groups[i]) == 0){
+ aclg_unlock_groupCache ( 1 /* reader */ );
+ slapi_log_error( SLAPI_LOG_ACL, plugin_name, "Evaluated ACL_TRUE\n");
+ return ACL_TRUE;
+ }
+ }
+
+ /* see if we know the client is not a member of a group. */
+ for ( i= 0; i < u_group->aclug_numof_notmember_group; i++) {
+ slapi_log_error( SLAPI_LOG_ACL, plugin_name, "-- Not in %s\n",
+ u_group->aclug_notmember_groups[i] );
+ if ( slapi_utf8casecmp((ACLUCHP)groupDN, (ACLUCHP)u_group->aclug_notmember_groups[i]) == 0){
+ aclg_unlock_groupCache ( 1 /* reader */ );
+ slapi_log_error( SLAPI_LOG_ACL, plugin_name, "Evaluated ACL_FALSE\n");
+ return ACL_FALSE;
+ }
+ }
+
+ /*
+ ** That means we didn't find the the group in the cache. -- we have to add it
+ ** so no need for READ lock - need to get a WRITE lock. We will get it just before
+ ** modifying it.
+ */
+ aclg_unlock_groupCache ( 1 /* reader */ );
+
+ /* Indicate the initialization handler -- this module will be
+ ** called by the backend to evaluate the entry.
+ */
+ info.result = ACL_FALSE;
+ if (clientDN && *clientDN != '\0')
+ info.userDN = clientDN;
+ else
+ info.userDN = NULL;
+
+ info.c_idx = 0;
+ info.memberInfo = (struct member_info **) slapi_ch_malloc (ACLLAS_MAX_GRP_MEMBER * sizeof(struct member_info *));
+ groupMember = (struct member_info *) slapi_ch_malloc ( sizeof (struct member_info) );
+ groupMember->member = slapi_ch_strdup(groupDN);
+ groupMember->parent = NULL;
+ info.memberInfo[0] = groupMember;
+ info.lu_idx = 0;
+
+ attrs[0] = type_member;
+ attrs[1] = type_uniquemember;
+ attrs[2] = type_memberURL;
+ attrs[3] = type_memberCert;
+ attrs[4] = NULL;
+
+ currDN = groupMember->member;
+
+ /* nesting level is 0 to begin with */
+ nesting_level = 0;
+ numOfMembersVisited = 0;
+ totalMembersVisited = 0;
+ numOfMembersAtCurrentLevel = 1;
+
+ if (clientCert)
+ info.clientCert = clientCert;
+ else
+ info.clientCert = NULL;
+ info.aclpb = aclpb;
+
+ max_memberlimit = aclpb->aclpb_max_member_sizelimit;
+ max_nestlevel = aclpb->aclpb_max_nesting_level;
+
+ /* dump_eval_info ( "acllas__user_ismember_of_group", &info, -1 ); */
+
+eval_another_member:
+
+ numOfMembers = info.lu_idx - info.c_idx;
+
+ /* Use new search internal API */
+ {
+ Slapi_PBlock * aPb = slapi_pblock_new ();
+
+ /*
+ * This search may NOT be chained--we demand that group
+ * definition be local.
+ */
+ slapi_search_internal_set_pb ( aPb,
+ currDN,
+ LDAP_SCOPE_BASE,
+ filter_groups,
+ &attrs[0],
+ 0,
+ NULL /* controls */,
+ NULL /* uniqueid */,
+ aclplugin_get_identity (ACL_PLUGIN_IDENTITY),
+ SLAPI_OP_FLAG_NEVER_CHAIN /* actions */);
+ slapi_search_internal_callback_pb(aPb,
+ &info /* callback_data */,
+ NULL/* result_callback */,
+ acllas__handle_group_entry,
+ NULL /* referral_callback */);
+
+ if ( info.result == ACL_TRUE )
+ slapi_log_error( SLAPI_LOG_ACL, plugin_name,"-- In %s\n", info.memberInfo[info.c_idx]->member );
+ else if ( info.result == ACL_FALSE )
+ slapi_log_error( SLAPI_LOG_ACL, plugin_name,"-- Not in %s\n", info.memberInfo[info.c_idx]->member );
+
+ slapi_pblock_destroy (aPb);
+ }
+
+ if (info.result == ACL_TRUE) {
+ /*
+ ** that means the client is a member of the
+ ** group or one of the nested groups. We are done.
+ */
+ result = ACL_TRUE;
+ slapi_log_error( SLAPI_LOG_ACL, plugin_name, "Evaluated ACL_TRUE\n");
+ goto free_and_return;
+ }
+ numOfMembersVisited++;
+
+ if (numOfMembersVisited == numOfMembersAtCurrentLevel) {
+ /* This means we have looked at all the members for this level */
+ numOfMembersVisited = 0;
+
+ /* Now we are ready to look at the next level */
+ nesting_level++;
+
+ /* So, far we have visited ... */
+ totalMembersVisited += numOfMembersAtCurrentLevel;
+
+ /* How many members in the next level ? */
+ numOfMembersAtCurrentLevel =
+ info.lu_idx - totalMembersVisited +1;
+ }
+
+ if ((nesting_level > max_nestlevel)) {
+ slapi_log_error( SLAPI_LOG_ACL, plugin_name,
+ "GroupEval:Member not found within the allowed nesting level (Allowed:%d Looked at:%d)\n",
+ max_nestlevel, nesting_level, 0);
+
+ result = ACL_DONT_KNOW; /* don't try to cache info based on this result */
+ goto free_and_return;
+ }
+
+ /* limit of -1 means "no limit */
+ if (info.c_idx > max_memberlimit &&
+ max_memberlimit != -1 ) {
+ slapi_log_error( SLAPI_LOG_ACL, plugin_name,
+ "GroupEval:Looked at too many entries:(%d, %d)\n",
+ info.c_idx, info.lu_idx,0);
+ result = ACL_DONT_KNOW; /* don't try to cache info based on this result */
+ goto free_and_return;
+ }
+ if (info.lu_idx > info.c_idx) {
+ if (numOfMembers == (info.lu_idx - info.c_idx)) {
+ /* That means it's not a GROUP. It is just another
+ ** useless member which doesn't match. Remove the BAD dude.
+ */
+ groupMember = info.memberInfo[info.c_idx];
+
+ if (groupMember ) {
+ if ( groupMember->member ) slapi_ch_free ( (void **) &groupMember->member );
+ slapi_ch_free ( (void **) &groupMember );
+ info.memberInfo[info.c_idx] = NULL;
+ }
+ }
+ info.c_idx++;
+
+ /* Go thru the stack and see if we have already
+ ** evaluated this group. If we have, then skip it.
+ */
+ while (1) {
+ int evalNext=0;
+ int j;
+ if (info.c_idx > info.lu_idx) {
+ /* That means we have crossed the limit. We
+ ** may end of in this situation if we
+ ** have circular groups
+ */
+ info.c_idx = info.lu_idx;
+ goto free_and_return;
+ }
+
+ /* Break out of the loop if we have searched to the end */
+ groupMember = info.memberInfo[info.c_idx];
+ if ( (NULL == groupMember) || ((currDN = groupMember->member)!= NULL))
+ break;
+
+ for (j = 0; j < info.c_idx; j++) {
+ groupMember = info.memberInfo[j];
+ if (groupMember->member &&
+ (slapi_utf8casecmp((ACLUCHP)currDN, (ACLUCHP)groupMember->member) == 0)) {
+ /* Don't need the duplicate */
+ groupMember = info.memberInfo[info.c_idx];
+ slapi_ch_free ( (void **) &groupMember->member );
+ slapi_ch_free ( (void **) &groupMember );
+ info.memberInfo[info.c_idx] = NULL;
+ info.c_idx++;
+ evalNext=1;
+ break;
+ }
+ }
+ if (!evalNext) break;
+ }
+ /* Make sure that we have a valid DN to chug along */
+ groupMember = info.memberInfo[info.c_idx];
+ if ((info.c_idx <= info.lu_idx) && ((currDN = groupMember->member) != NULL))
+ goto eval_another_member;
+ }
+
+free_and_return:
+ /* Remove the unnecessary members from the list which
+ ** we might have accumulated during the last execution
+ ** and we don't need to look at them.
+ */
+ i = info.c_idx;
+ i++;
+ while (i <= info.lu_idx) {
+ groupMember = info.memberInfo[i];
+ slapi_ch_free ( (void **) &groupMember->member );
+ slapi_ch_free ( (void **) &groupMember );
+ info.memberInfo[i] = NULL;
+ i++;
+ }
+
+ /*
+ ** Now we have a list which has all the groups
+ ** which we need to cache
+ */
+ info.lu_idx = info.c_idx;
+
+ /* since we are updating the groupcache, get a write lock */
+ aclg_lock_groupCache ( 2 /* writer */ );
+
+ /*
+ ** Keep the result of the evaluation in the cache.
+ ** We have 2 lists: member_of and not_member_of. We can use this
+ ** cached information next time we evaluate groups.
+ */
+ if (result == ACL_TRUE &&
+ (cache_status & ACLLAS_CACHE_MEMBER_GROUPS)) {
+ int ngr = 0;
+
+ /* get the last group which the user is a member of */
+ groupMember = info.memberInfo[info.c_idx];
+
+ while ( groupMember ) {
+ int already_cached = 0;
+
+ parentGroup = groupMember->parent;
+ for (j=0; j < u_group->aclug_numof_member_group;j++){
+ if (slapi_utf8casecmp( (ACLUCHP)groupMember->member,
+ (ACLUCHP)u_group->aclug_member_groups[j]) == 0) {
+ already_cached = 1;
+ break;
+ }
+ }
+ if (already_cached) {
+ groupMember = parentGroup;
+ parentGroup = NULL;
+ continue;
+ }
+
+ ngr = u_group->aclug_numof_member_group++;
+ if (u_group->aclug_numof_member_group >=
+ u_group->aclug_member_group_size){
+ u_group->aclug_member_groups =
+ (char **) slapi_ch_realloc (
+ (void *) u_group->aclug_member_groups,
+ (u_group->aclug_member_group_size +
+ ACLUG_INCR_GROUPS_LIST) *
+ sizeof (char *));
+ u_group->aclug_member_group_size +=
+ ACLUG_INCR_GROUPS_LIST;
+ }
+ u_group->aclug_member_groups[ngr] = slapi_ch_strdup ( groupMember->member );
+ slapi_log_error ( SLAPI_LOG_ACL, plugin_name,
+ "Adding Group (%s) ParentGroup (%s) to the IN GROUP List\n",
+ groupMember->member , parentGroup ? parentGroup->member: "NULL");
+
+ groupMember = parentGroup;
+ parentGroup = NULL;
+ }
+ } else if (result == ACL_FALSE &&
+ (cache_status & ACLLAS_CACHE_NOT_MEMBER_GROUPS)) {
+ int ngr = 0;
+
+ /* NOT IN THE GROUP LIST */
+ /* get the last group which the user is a member of */
+ groupMember = info.memberInfo[info.c_idx];
+
+ while ( groupMember ) {
+ int already_cached = 0;
+
+ parentGroup = groupMember->parent;
+ for (j=0; j < u_group->aclug_numof_notmember_group;j++){
+ if (slapi_utf8casecmp( (ACLUCHP)groupMember->member,
+ (ACLUCHP)u_group->aclug_notmember_groups[j]) == 0) {
+ already_cached = 1;
+ break;
+ }
+ }
+ if (already_cached) {
+ groupMember = parentGroup;
+ parentGroup = NULL;
+ continue;
+ }
+
+ ngr = u_group->aclug_numof_notmember_group++;
+ if (u_group->aclug_numof_notmember_group >=
+ u_group->aclug_notmember_group_size){
+ u_group->aclug_notmember_groups =
+ (char **) slapi_ch_realloc (
+ (void *) u_group->aclug_notmember_groups,
+ (u_group->aclug_notmember_group_size +
+ ACLUG_INCR_GROUPS_LIST) *
+ sizeof (char *));
+ u_group->aclug_notmember_group_size +=
+ ACLUG_INCR_GROUPS_LIST;
+ }
+ u_group->aclug_notmember_groups[ngr] = slapi_ch_strdup ( groupMember->member );
+ slapi_log_error ( SLAPI_LOG_ACL, plugin_name,
+ "Adding Group (%s) ParentGroup (%s) to the NOT IN GROUP List\n",
+ groupMember->member , parentGroup ? parentGroup->member: "NULL");
+
+ groupMember = parentGroup;
+ parentGroup = NULL;
+ }
+ } else if ( result == ACL_DONT_KNOW ) {
+
+ /*
+ * We terminated the search without reaching a conclusion--so
+ * don't cache any info based on this evaluation.
+ */
+ slapi_log_error( SLAPI_LOG_ACL, plugin_name, "Evaluated ACL_DONT_KNOW\n");
+ }
+
+ /* Unlock the group cache, we are done with updating */
+ aclg_unlock_groupCache ( 2 /* writer */ );
+
+ for (i=0; i <= info.lu_idx; i++) {
+ groupMember = info.memberInfo[i];
+ if ( NULL == groupMember ) continue;
+
+ slapi_ch_free ( (void **) &groupMember->member );
+ slapi_ch_free ( (void **) &groupMember );
+ }
+
+ /* free the pointer array.*/
+ slapi_ch_free ( (void **) &info.memberInfo);
+ return result;
+}
+
+/***************************************************************************
+*
+* acllas__handle_group_entry
+*
+* handler called. Compares the userdn value and determines if it's
+* a member of not.
+*
+* Input:
+*
+*
+* Returns:
+*
+* Error Handling:
+*
+**************************************************************************/
+static int
+acllas__handle_group_entry (Slapi_Entry* e, void *callback_data)
+{
+ struct eval_info *info;
+ Slapi_Attr *currAttr, *nextAttr;
+ char *n_dn, *attrType;
+ short n;
+ int i;
+
+ info = (struct eval_info *) callback_data;
+ info->result = ACL_FALSE;
+
+ if (e == NULL) {
+ return 0;
+ }
+
+ slapi_entry_first_attr ( e, &currAttr);
+ if ( NULL == currAttr ) return 0;
+
+ slapi_attr_get_type ( currAttr, &attrType );
+ if (NULL == attrType ) return 0;
+
+ do {
+ Slapi_Value *sval=NULL;
+ const struct berval *attrVal;
+
+ if ((strcasecmp (attrType, type_member) == 0) ||
+ (strcasecmp (attrType, type_uniquemember) == 0 )) {
+
+ i = slapi_attr_first_value ( currAttr,&sval );
+ while ( i != -1 ) {
+ struct member_info *groupMember = NULL;
+ attrVal = slapi_value_get_berval ( sval );
+ n_dn = slapi_dn_normalize ( slapi_ch_strdup( attrVal->bv_val));
+ info->lu_idx++;
+ n = info->lu_idx;
+ if (!(n % ACLLAS_MAX_GRP_MEMBER)) {
+ info->memberInfo = (struct member_info **) slapi_ch_realloc(
+ (void *) info->memberInfo,
+ (n+ACLLAS_MAX_GRP_MEMBER) *
+ sizeof(struct eval_info *));
+ }
+
+ /* allocate the space for the member and attch it to the list */
+ groupMember = (struct member_info *) slapi_ch_malloc ( sizeof ( struct member_info ) );
+ groupMember->member = n_dn;
+ groupMember->parent = info->memberInfo[info->c_idx];
+ info->memberInfo[n] = groupMember;
+
+ if (info->userDN &&
+ slapi_utf8casecmp((ACLUCHP)n_dn, (ACLUCHP)info->userDN) == 0) {
+ info->result = ACL_TRUE;
+ return 0;
+ }
+ i = slapi_attr_next_value ( currAttr, i, &sval );
+ }
+ /* Evaluate Dynamic groups */
+ } else if (strcasecmp ( attrType, type_memberURL) == 0) {
+ char *memberURL, *savURL;
+
+ if (!info->userDN) continue;
+
+ i= slapi_attr_first_value ( currAttr,&sval );
+ while ( i != -1 ) {
+ attrVal = slapi_value_get_berval ( sval );
+ /*
+ * memberURL may start with "ldap:///" or "ldap://host:port"
+ * ldap://localhost:11000/o=ace industry,c=us??
+ * or
+ * ldap:///o=ace industry,c=us??
+ */
+ if (strncasecmp( attrVal->bv_val, "ldap://",7) == 0 ||
+ strncasecmp( attrVal->bv_val, "ldaps://",8) == 0) {
+ savURL = memberURL = slapi_ch_strdup ( attrVal->bv_val);
+ slapi_log_error( SLAPI_LOG_ACL, plugin_name,
+ "ACL Group Eval:MemberURL:%s\n", memberURL);
+ info->result = acllas__client_match_URL (
+ info->aclpb,
+ info->userDN,
+ memberURL);
+ slapi_ch_free ( (void**) &savURL);
+ if (info->result == ACL_TRUE)
+ return 0;
+ } else {
+ /* This means that the URL is ill-formed */
+ slapi_log_error( SLAPI_LOG_ACL, plugin_name,
+ "ACL Group Eval:Badly Formed MemberURL:%s\n", attrVal->bv_val);
+ }
+ i = slapi_attr_next_value ( currAttr, i, &sval );
+ }
+ /* Evaluate Fortezza groups */
+ } else if ((strcasecmp (attrType, type_memberCert) == 0) ) {
+ /* Do we have the certificate around */
+ if (!info->clientCert) {
+ slapi_log_error( SLAPI_LOG_ACL, plugin_name,
+ " acllas__handle_group_entry:Client Cert missing\n" );
+ continue;
+ }
+ i = slapi_attr_first_value ( currAttr,&sval );
+ while ( i != -1 ) {
+ attrVal = slapi_value_get_berval ( sval );
+ if (ldapu_member_certificate_match (
+ info->clientCert,
+ attrVal->bv_val) == LDAP_SUCCESS) {
+ info->result = ACL_TRUE;
+ return 0;
+ }
+ i = slapi_attr_next_value ( currAttr, i, &sval );
+ }
+ }
+
+ attrType = NULL;
+ /* get the next attr */
+ slapi_entry_next_attr ( e, currAttr, &nextAttr );
+ if ( NULL == nextAttr ) break;
+
+ currAttr = nextAttr;
+ slapi_attr_get_type ( currAttr, &attrType );
+
+ } while ( NULL != attrType );
+
+ return 0;
+}
+/***************************************************************************
+*
+* DS_LASGroupDnAttrEval
+*
+*
+* Input:
+* attr_name The string "groupdnattr" - in lower case.
+* comparator CMP_OP_EQ or CMP_OP_NE only
+* attr_pattern A comma-separated list of users
+* cachable Always set to FALSE.
+* subject Subject property list
+* resource Resource property list
+* auth_info Authentication info, if any
+*
+* Returns:
+* retcode The usual LAS return codes.
+*
+* Error Handling:
+* None.
+*
+**************************************************************************/
+struct groupdnattr_info
+{
+ char *attrName; /* name of the attribute */
+ int numofGroups; /* number of groups */
+ char **member;
+};
+int
+DS_LASGroupDnAttrEval(NSErr_t *errp, char *attr_name, CmpOp_t comparator,
+ char *attr_pattern, int *cachable, void **LAS_cookie,
+ PList_t subject, PList_t resource, PList_t auth_info,
+ PList_t global_auth)
+{
+
+ char *s_attrName = NULL;
+ char *attrName;
+ char *ptr;
+ int matched;
+ int rc;
+ int len;
+ Slapi_Attr *attr;
+ int levels[ACLLAS_MAX_LEVELS];
+ int numOflevels = 0;
+ char *n_currEntryDn = NULL;
+ lasInfo lasinfo;
+ int got_undefined = 0;
+
+ if ( 0 != (rc = __acllas_setup (errp, attr_name, comparator,
+ attr_pattern,cachable,LAS_cookie,
+ subject, resource, auth_info,global_auth,
+ DS_LAS_GROUPDNATTR, "DS_LASGroupDnAttrEval",
+ &lasinfo )) ) {
+ return LAS_EVAL_FAIL;
+ }
+
+ /* For anonymous client, the answer is XXX come back to this */
+ if ( lasinfo.anomUser )
+ return LAS_EVAL_FALSE;
+
+ /*
+ ** The groupdnAttr syntax is
+ ** groupdnattr = <attribute>
+ ** Ex:
+ ** groupdnattr = SIEmanager;
+ **
+ ** The function of this LAS is to find out if the client belongs
+ ** to any group that is specified in the attr.
+ */
+ attrName = attr_pattern;
+ if (strstr(attrName, LDAP_URL_prefix)) {
+ char *s;
+
+
+ /* In this case "grppupdnattr="ldap:///base??attr" */
+
+ if ((s = strstr (attrName, ACL_RULE_MACRO_DN_KEY)) != NULL ||
+ (s = strstr (attrName, ACL_RULE_MACRO_DN_LEVELS_KEY)) != NULL ||
+ (s = strstr (attrName, ACL_RULE_MACRO_ATTR_KEY)) != NULL) {
+
+ matched = aclutil_evaluate_macro( attrName, &lasinfo,
+ ACL_EVAL_GROUPDNATTR);
+ } else{
+
+ matched = acllas__eval_memberGroupDnAttr(attrName,
+ lasinfo.resourceEntry,
+ lasinfo.clientDn,
+ lasinfo.aclpb);
+ }
+ if ( matched == ACL_DONT_KNOW) {
+ got_undefined = 1;
+ }
+ } else {
+ int i;
+ char *n_groupdn;
+
+ /* ignore leading/trailing whitespace */
+ while(ldap_utf8isspace(attrName)) LDAP_UTF8INC(attrName);
+ len = strlen(attrName);
+ ptr = attrName+len-1;
+ while(ldap_utf8isspace(ptr)) { *ptr = '\0'; LDAP_UTF8DEC(ptr); }
+
+ slapi_log_error( SLAPI_LOG_ACL, plugin_name,"Attr:%s\n" , attrName, 0,0);
+
+ /* See if we have a parent[2].attr" rule */
+ if ( (ptr = strstr(attrName, "parent[")) != NULL) {
+ char *word, *str, *next;
+
+ numOflevels = 0;
+ n_currEntryDn = slapi_entry_get_ndn ( lasinfo.resourceEntry ) ;
+ s_attrName = attrName = slapi_ch_strdup ( attr_pattern );
+ str = attrName;
+
+ word = ldap_utf8strtok_r(str, "[],. ",&next);
+ /* The first word is "parent[" and so it's not important */
+
+ while ((word= ldap_utf8strtok_r(NULL, "[],.", &next)) != NULL) {
+ if (ldap_utf8isdigit(word)) {
+ while (word && ldap_utf8isspace(word)) LDAP_UTF8INC(word);
+ if (numOflevels < ACLLAS_MAX_LEVELS)
+ levels[numOflevels++] = atoi (word);
+ else {
+ /*
+ * Here, ignore the extra levels..it's really
+ * a syntax error which should have been ruled out at parse time
+ */
+ slapi_log_error( SLAPI_LOG_FATAL, plugin_name,
+ "DS_LASGroupDnattr: Exceeded the ATTR LIMIT:%d: Ignoring extra levels\n",
+ ACLLAS_MAX_LEVELS,0,0);
+ }
+ } else {
+ /* Must be the attr name. We can goof of by
+ ** having parent[1,2,a] but then you have to be
+ ** stupid to do that.
+ */
+ char *p = word;
+ if (*--p == '.') {
+ attrName = word;
+ break;
+ }
+ }
+ }
+ } else {
+ levels[0] = 0;
+ numOflevels = 1;
+ }
+
+ matched = ACL_FALSE;
+ for (i=0; i < numOflevels; i++) {
+ if ( levels[i] == 0 ) {
+ Slapi_Value *sval=NULL;
+ const struct berval *attrVal;
+ int attr_i;
+
+ /*
+ * For the add operation, the resource itself (level 0)
+ * must never be allowed to grant access--
+ * This is because access would be granted based on a value
+ * of an attribute in the new entry--security hole.
+ * XXX is this therefore FALSE or DONT_KNOW ?
+ */
+
+ if ( lasinfo.aclpb->aclpb_optype == SLAPI_OPERATION_ADD) {
+ slapi_log_error( SLAPI_LOG_ACL, plugin_name,
+ "ACL info: groupdnAttr does not allow ADD permission at level 0.\n");
+ got_undefined = 1;
+ continue;
+ }
+ slapi_entry_attr_find ( lasinfo.resourceEntry, attrName, &attr);
+ if ( !attr) continue;
+ attr_i= slapi_attr_first_value ( attr,&sval );
+ while ( attr_i != -1 ) {
+ attrVal = slapi_value_get_berval ( sval );
+ n_groupdn = slapi_dn_normalize(
+ slapi_ch_strdup( attrVal->bv_val));
+ matched = acllas__user_ismember_of_group (
+ lasinfo.aclpb, n_groupdn, lasinfo.clientDn,
+ ACLLAS_CACHE_MEMBER_GROUPS,
+ lasinfo.aclpb->aclpb_clientcert);
+ slapi_ch_free ( (void **) &n_groupdn);
+ if (matched == ACL_TRUE ) {
+ slapi_log_error( SLAPI_LOG_ACL, plugin_name,
+ "groupdnattr matches at level (%d)\n", levels[i]);
+ break;
+ } else if ( matched == ACL_DONT_KNOW ) {
+ /* record this but keep going--maybe another group will evaluate to TRUE */
+ got_undefined = 1;
+ }
+ attr_i= slapi_attr_next_value ( attr, attr_i, &sval );
+ }
+ } else {
+ char *p_dn;
+ struct groupdnattr_info info;
+ char *attrs[2];
+ int j;
+
+ info.numofGroups = 0;
+ attrs[0] = info.attrName = attrName;
+ attrs[1] = NULL;
+
+ p_dn = acllas__dn_parent (n_currEntryDn, levels[i]);
+
+ if (p_dn == NULL) continue;
+
+ /* Use new search internal API */
+ {
+
+ Slapi_PBlock *aPb = slapi_pblock_new ();
+ /*
+ * This search may NOT be chained--if the user's definition is
+ * remote and the group is dynamic and the user entry
+ * changes then we would not notice--so don't go
+ * find the user entry in the first place.
+ */
+ slapi_search_internal_set_pb ( aPb,
+ p_dn,
+ LDAP_SCOPE_BASE,
+ "objectclass=*",
+ &attrs[0],
+ 0,
+ NULL /* controls */,
+ NULL /* uniqueid */,
+ aclplugin_get_identity (ACL_PLUGIN_IDENTITY),
+ SLAPI_OP_FLAG_NEVER_CHAIN /* actions */);
+ slapi_search_internal_callback_pb(aPb,
+ &info /* callback_data */,
+ NULL/* result_callback */,
+ acllas__get_members,
+ NULL /* referral_callback */);
+ slapi_pblock_destroy (aPb);
+ }
+
+ if (info.numofGroups <= 0) {
+ continue;
+ }
+ for (j=0; j <info.numofGroups; j++) {
+ if (slapi_utf8casecmp((ACLUCHP)info.member[j],
+ (ACLUCHP)lasinfo.clientDn) == 0) {
+ matched = ACL_TRUE;
+ break;
+ }
+ matched = acllas__user_ismember_of_group (
+ lasinfo.aclpb, info.member[j],
+ lasinfo.clientDn, ACLLAS_CACHE_ALL_GROUPS,
+ lasinfo.aclpb->aclpb_clientcert);
+ if (matched == ACL_TRUE) {
+ break;
+ } else if ( matched == ACL_DONT_KNOW ) {
+ /* record this but keep going--maybe another group will evaluate to TRUE */
+ got_undefined = 1;
+ }
+ }
+ /* Deallocate the member array and the member struct */
+ for (j=0; j < info.numofGroups; j++)
+ slapi_ch_free ((void **) &info.member[j]);
+ slapi_ch_free ((void **) &info.member);
+ }
+ if (matched == ACL_TRUE) {
+ slapi_log_error( SLAPI_LOG_ACL, plugin_name,
+ "groupdnattr matches at level (%d)\n", levels[i]);
+ break;
+ } else if ( matched == ACL_DONT_KNOW ) {
+ /* record this but keep going--maybe another group at another level
+ * will evaluate to TRUE.
+ */
+ got_undefined = 1;
+ }
+
+ } /* NumofLevels */
+ }
+ if (s_attrName) slapi_ch_free ((void**) &s_attrName );
+
+ /*
+ * If no terms were undefined, then evaluate as normal.
+ * If there was an undefined term, but another one was TRUE, then we also evaluate
+ * as normal. Otherwise, the whole expression is UNDEFINED.
+ */
+ if ( matched == ACL_TRUE || !got_undefined ) {
+ if (comparator == CMP_OP_EQ) {
+ rc = (matched == ACL_TRUE ? LAS_EVAL_TRUE : LAS_EVAL_FALSE);
+ } else {
+ rc = (matched == ACL_TRUE ? LAS_EVAL_FALSE : LAS_EVAL_TRUE);
+ }
+ } else {
+ rc = LAS_EVAL_FAIL;
+ slapi_log_error( SLAPI_LOG_ACL, plugin_name,
+ "Returning UNDEFINED for groupdnattr evaluation.\n");
+ }
+
+ return rc;
+}
+
+/*
+ * acllas__eval_memberGroupDnAttr
+ *
+ * return ACL_TRUE, ACL_FALSE or ACL_DONT_KNOW
+ *
+ * Inverse group evaluation. Find all the groups that the user is a
+ * member of. Find all teh groups that contain those groups. Do an
+ * upward nested level search. By the end of it, we will know all the
+ * groups that the clinet is a member of under that search scope.
+ *
+ * This model seems to be very fast if we have few groups at the
+ * leaf level.
+ *
+ */
+static int
+acllas__eval_memberGroupDnAttr (char *attrName, Slapi_Entry *e,
+ char *n_clientdn, struct acl_pblock *aclpb)
+{
+
+ Slapi_Attr *attr;
+ char *s, *p;
+ char *str, *s_str, *base, *groupattr;
+ int i,j,k,matched, enumerate_groups;
+ aclUserGroup *u_group;
+ char ebuf [ BUFSIZ ];
+ Slapi_Value *sval=NULL;
+ const struct berval *attrVal;
+
+ /* Parse the URL -- We can't use the ldap_url_parse()
+ ** we don't follow thw complete url naming scheme
+ */
+ s_str = str = slapi_ch_strdup(attrName);
+ while (str && ldap_utf8isspace(str)) LDAP_UTF8INC( str );
+ str +=8;
+ s = strchr (str, '?');
+ if (s) {
+ p = s;
+ p++;
+ *s = '\0';
+ base = str;
+ s = strchr (p, '?');
+ if (s) *s = '\0';
+
+ groupattr = p;
+ } else {
+ slapi_ch_free ( (void **)&s_str );
+ return ACL_FALSE;
+ }
+
+ if ( (u_group = aclg_get_usersGroup ( aclpb , n_clientdn )) == NULL) {
+ slapi_log_error( SLAPI_LOG_ACL, plugin_name,
+ "Failed to find/allocate a usergroup--aborting evaluation\n", 0, 0);
+ slapi_ch_free ( (void **)&s_str );
+ return(ACL_DONT_KNOW);
+ }
+
+ /*
+ ** First find out if we have already searched this base or
+ ** if we are searching a subtree to an already enumerated base.
+ */
+ enumerate_groups = 1;
+ for (j=0; j < aclpb->aclpb_numof_bases; j++) {
+ if (slapi_dn_issuffix(aclpb->aclpb_grpsearchbase[j], base)) {
+ enumerate_groups = 0;
+ break;
+ }
+ }
+
+
+ /* See if we have already enumerated all the groups which the
+ ** client is a member of.
+ */
+ if (enumerate_groups) {
+ char filter_str[BUFSIZ];
+ char *attrs[3];
+ struct eval_info info;
+ char *curMemberDn;
+ int Done = 0;
+ int ngr, tt;
+
+ /* Add the scope to the list of scopes */
+ if (aclpb->aclpb_numof_bases >= (aclpb->aclpb_grpsearchbase_size-1)) {
+ aclpb->aclpb_grpsearchbase = (char **)
+ slapi_ch_realloc (
+ (void *) aclpb->aclpb_grpsearchbase,
+ (aclpb->aclpb_grpsearchbase_size +
+ ACLPB_INCR_BASES) *
+ sizeof (char *));
+ aclpb->aclpb_grpsearchbase_size += ACLPB_INCR_BASES;
+ }
+ aclpb->aclpb_grpsearchbase[aclpb->aclpb_numof_bases++] =
+ slapi_dn_normalize(slapi_ch_strdup(base));
+
+ /* Set up info to do a search */
+ attrs[0] = type_member;
+ attrs[1] = type_uniquemember;
+ attrs[2] = NULL;
+
+ info.c_idx = info.lu_idx = 0;
+ info.member =
+ (char **) slapi_ch_malloc (ACLLAS_MAX_GRP_MEMBER * sizeof(char *));
+ curMemberDn = n_clientdn;
+
+ while (!Done) {
+ char *filter_str_ptr = &filter_str[0];
+ char *new_filter_str = NULL;
+ int lenf = strlen(curMemberDn)<<1;
+
+ if (lenf > (BUFSIZ - 28)) { /* 28 for "(|(uniquemember=%s)(member=%s))" */
+ new_filter_str = slapi_ch_malloc(lenf + 28);
+ filter_str_ptr = new_filter_str;
+ }
+
+ /*
+ ** Search the db for groups that the client is a member of.
+ ** Once found cache it. cache only unique groups.
+ */
+ tt = info.lu_idx;
+ sprintf (filter_str_ptr,"(|(uniquemember=%s)(member=%s))",
+ curMemberDn, curMemberDn);
+
+ /* Use new search internal API */
+ {
+ Slapi_PBlock *aPb = slapi_pblock_new ();
+ /*
+ * This search may NOT be chained--we demand that group
+ * definition be local.
+ */
+ slapi_search_internal_set_pb ( aPb,
+ base,
+ LDAP_SCOPE_SUBTREE,
+ filter_str_ptr,
+ &attrs[0],
+ 0,
+ NULL /* controls */,
+ NULL /* uniqueid */,
+ aclplugin_get_identity (ACL_PLUGIN_IDENTITY),
+ SLAPI_OP_FLAG_NEVER_CHAIN /* actions */);
+ slapi_search_internal_callback_pb(aPb,
+ &info /* callback_data */,
+ NULL/* result_callback */,
+ acllas__add_allgroups,
+ NULL /* referral_callback */);
+ slapi_pblock_destroy (aPb);
+ }
+
+ if (new_filter_str) slapi_ch_free((void **) &new_filter_str);
+
+ if (tt == info.lu_idx) {
+ slapi_log_error( SLAPI_LOG_ACL, plugin_name, "currDn:(%s) \n\tNO MEMBER ADDED\n",
+ ACL_ESCAPE_STRING_WITH_PUNCTUATION (curMemberDn, ebuf) , 0,0);
+ } else {
+ for (i=tt; i < info.lu_idx; i++)
+ slapi_log_error( SLAPI_LOG_ACL, plugin_name,
+ "currDn:(%s) \n\tADDED MEMBER[%d]=%s\n",
+ ACL_ESCAPE_STRING_WITH_PUNCTUATION (curMemberDn, ebuf), i, info.member[i]);
+ }
+
+ if (info.c_idx >= info.lu_idx) {
+ for (i=0; i < info.lu_idx; i++) {
+ int already_cached = 0;
+ for (j=0; j < u_group->aclug_numof_member_group;
+ j++){
+ if (slapi_utf8casecmp(
+ (ACLUCHP)info.member[i],
+ (ACLUCHP)u_group->aclug_member_groups[j]) == 0) {
+ slapi_ch_free ((void **) &info.member[i] );
+ info.member[i] = NULL;
+ already_cached = 1;
+ break;
+ }
+
+ }
+
+ if (already_cached) continue;
+
+ ngr = u_group->aclug_numof_member_group++;
+ if (u_group->aclug_numof_member_group >=
+ u_group->aclug_member_group_size){
+ u_group->aclug_member_groups =
+ (char **) slapi_ch_realloc (
+ (void *) u_group->aclug_member_groups,
+ (u_group->aclug_member_group_size +
+ ACLUG_INCR_GROUPS_LIST) * sizeof(char *));
+
+ u_group->aclug_member_group_size +=
+ ACLUG_INCR_GROUPS_LIST;
+ }
+ u_group->aclug_member_groups[ngr] = info.member[i];
+ info.member[i] = NULL;
+ }
+ slapi_ch_free ((void **) &info.member);
+ Done = 1;
+ } else {
+ curMemberDn = info.member[info.c_idx];
+ info.c_idx++;
+ }
+ }
+ }
+
+ for (j=0; j < u_group->aclug_numof_member_group; j++)
+ slapi_log_error( SLAPI_LOG_ACL, plugin_name,
+ "acllas__eval_memberGroupDnAttr:GROUP[%d] IN CACHE:%s\n",
+ j, ACL_ESCAPE_STRING_WITH_PUNCTUATION (u_group->aclug_member_groups[j], ebuf),0);
+
+ matched = ACL_FALSE;
+ slapi_entry_attr_find( e, groupattr, &attr);
+ if (attr == NULL) {
+ slapi_ch_free ( (void **)&s_str );
+ return ACL_FALSE;
+ }
+ {
+ k = slapi_attr_first_value ( attr,&sval );
+ while ( k != -1 ) {
+ char *n_attrval;
+ attrVal = slapi_value_get_berval ( sval );
+ n_attrval = slapi_ch_strdup( attrVal->bv_val);
+ n_attrval = slapi_dn_normalize (n_attrval);
+
+ /* We support: The attribute value can be a USER or a GROUP.
+ ** Let's compare with the client, thi might be just an user. If it is not
+ ** then we test it against the list of groups.
+ */
+ if (slapi_utf8casecmp ((ACLUCHP)n_attrval, (ACLUCHP)n_clientdn) == 0 ) {
+ matched = ACL_TRUE;
+ slapi_ch_free ( (void **)&n_attrval );
+ break;
+ }
+ for (j=0; j <u_group->aclug_numof_member_group; j++) {
+ if ( slapi_utf8casecmp((ACLUCHP)n_attrval,
+ (ACLUCHP)u_group->aclug_member_groups[j]) == 0) {
+ matched = ACL_TRUE;
+ break;
+ }
+ }
+ slapi_ch_free ( (void **)&n_attrval );
+ if (matched == ACL_TRUE) break;
+ k= slapi_attr_next_value ( attr, k, &sval );
+ }
+ }
+ slapi_ch_free ( (void **)&s_str );
+ return matched;
+}
+
+static int
+acllas__add_allgroups (Slapi_Entry* e, void *callback_data)
+{
+ int i, n, m;
+ struct eval_info *info;
+ char *n_dn;
+
+ info = (struct eval_info *) callback_data;
+
+ /*
+ ** Once we are here means this is a valid group. First see
+ ** If we have already seen this group. If not, add it to the
+ ** member list.
+ */
+ n_dn = slapi_ch_strdup ( slapi_entry_get_ndn ( e ) );
+ for (i=0; i < info->lu_idx; i++) {
+ if (slapi_utf8casecmp((ACLUCHP)n_dn, (ACLUCHP)info->member[i]) == 0) {
+ slapi_ch_free ( (void **) &n_dn);
+ return 0;
+ }
+ }
+
+ m = info->lu_idx;
+ n = info->lu_idx++;
+ if (!(n % ACLLAS_MAX_GRP_MEMBER)) {
+ info->member = (char **) slapi_ch_realloc (
+ (void *) info->member,
+ (n+ACLLAS_MAX_GRP_MEMBER) * sizeof(char *));
+ }
+ info->member[m] = n_dn;
+ return 0;
+}
+/*
+ *
+ * acllas__dn_parent
+ *
+ * This code should belong to dn.c. However this is specific to acl and I had
+ * 2 choices 1) create a new API or 2) reuse the slapi_dN_parent
+ *
+ * Returns a ptr to the parent based on the level.
+ *
+ */
+#define DNSEPARATOR(c) (c == ',' || c == ';')
+static char*
+acllas__dn_parent( char *dn, int level)
+{
+ char *s, *dnstr;
+ int inquote;
+ int curLevel;
+ int lastLoop = 0;
+
+ if ( dn == NULL || *dn == '\0' ) {
+ return( NULL );
+ }
+
+ /* An X.500-style name, which looks like foo=bar,sha=baz,... */
+ /* Do we have any dn seprator or not */
+ if ((strchr(dn,',') == NULL) && (strchr(dn,';') == NULL))
+ return (NULL);
+
+ inquote = 0;
+ curLevel = 1;
+ dnstr = dn;
+ while ( curLevel <= level) {
+ if (lastLoop) break;
+ if (curLevel == level) lastLoop = 1;
+ for ( s = dnstr; *s; s++ ) {
+ if ( *s == '\\' ) {
+ if ( *(s + 1) )
+ s++;
+ continue;
+ }
+ if ( inquote ) {
+ if ( *s == '"' )
+ inquote = 0;
+ } else {
+ if ( *s == '"' )
+ inquote = 1;
+ else if ( DNSEPARATOR( *s ) ) {
+ if (curLevel == level)
+ return( s + 1 );
+ dnstr = s + 1;
+ curLevel++;
+ break;
+ }
+ }
+ }
+ if ( *s == '\0') {
+ /* Got to the end of the string without reaching level,
+ * so return NULL.
+ */
+ return(NULL);
+ }
+ }
+
+ return( NULL );
+}
+/*
+ * acllas__verify_client
+ *
+ * returns 1 if the attribute exists in the entry and
+ * it's value is equal to the client Dn.
+ * If the attribute is not in the entry, or it is and the
+ * value differs from the clientDn then returns FALSE.
+ *
+ * Verify if client's DN is stored in the attrbute or not.
+ * This is a handler from a search being done at
+ * DS_LASUserDnAttrEval().
+ *
+ */
+static int
+acllas__verify_client (Slapi_Entry* e, void *callback_data)
+{
+
+ Slapi_Attr *attr;
+ char *val;
+ struct userdnattr_info *info;
+ Slapi_Value *sval;
+ const struct berval *attrVal;
+ int i;
+
+ info = (struct userdnattr_info *) callback_data;
+
+ slapi_entry_attr_find( e, info->attr, &attr);
+ if (attr == NULL) return 0;
+
+ i = slapi_attr_first_value ( attr,&sval );
+ while ( i != -1 ) {
+ attrVal = slapi_value_get_berval ( sval );
+ val = slapi_dn_normalize (
+ slapi_ch_strdup(attrVal->bv_val));
+
+ if (slapi_utf8casecmp((ACLUCHP)val, (ACLUCHP)info->clientdn ) == 0) {
+ info->result = 1;
+ slapi_ch_free ( (void **) &val);
+ return 0;
+ }
+ slapi_ch_free ( (void **) &val);
+ i = slapi_attr_next_value ( attr, i, &sval );
+ }
+ return 0;
+}
+/*
+ *
+ * acllas__get_members
+ *
+ * Collects all the values of the specified attribute which should be group names.
+ */
+static int
+acllas__get_members (Slapi_Entry* e, void *callback_data)
+{
+
+ Slapi_Attr *attr;
+ struct groupdnattr_info *info;
+ Slapi_Value *sval=NULL;
+ const struct berval *attrVal;
+ int i;
+
+ info = (struct groupdnattr_info *) callback_data;
+ slapi_entry_attr_find (e, info->attrName, &attr);
+ if ( !attr ) return 0;
+
+ slapi_attr_get_numvalues ( attr, &info->numofGroups );
+
+ info->member = (char **) slapi_ch_malloc (info->numofGroups * sizeof(char *));
+ i = slapi_attr_first_value ( attr,&sval );
+ while ( i != -1 ) {
+ attrVal =slapi_value_get_berval ( sval );
+ info->member[i] = slapi_dn_normalize ( slapi_ch_strdup(attrVal->bv_val));
+ i = slapi_attr_next_value ( attr, i, &sval );
+ }
+ return 0;
+}
+
+/*
+ * DS_LASUserAttrEval
+ * LAS to evaluate the userattr rule
+ *
+ * userAttr = "attrName#Type"
+ *
+ * <Type> ::= "USERDN" | "GROUPDN" | "ROLEDN" | "LDAPURL" | <value>
+ * <value>::== <any printable String>
+ *
+ * Example:
+ * userAttr = "manager#USERDN" --- same as userdnattr
+ * userAttr = "owner#GROUPDN" --- same as groupdnattr
+ * = "ldap:///o=sun.com?owner#GROUPDN
+ * userAttr = "attr#ROLEDN" --- The value of attr contains a roledn
+ * userAttr = "myattr#LDAPURL" --- The value contains a LDAP URL
+ * which can have scope and filter
+ * bits.
+ * userAttr = "OU#Directory Server"
+ * --- In this case the client's OU and the
+ * resource entry's OU must have
+ * "Directory Server" value.
+ *
+ * Returns:
+ * retcode The usual LAS return codes.
+ */
+int
+DS_LASUserAttrEval(NSErr_t *errp, char *attr_name, CmpOp_t comparator,
+ char *attr_pattern, int *cachable, void **LAS_cookie,
+ PList_t subject, PList_t resource, PList_t auth_info,
+ PList_t global_auth)
+{
+
+ char *attrName;
+ char *attrValue = NULL;
+ int rc;
+ int matched = ACL_FALSE;
+ char *p;
+ int URLAttrRule = 0;
+ lasInfo lasinfo;
+ int got_undefined = 0;
+
+ if ( 0 != (rc = __acllas_setup (errp, attr_name, comparator,
+ attr_pattern,cachable,LAS_cookie,
+ subject, resource, auth_info,global_auth,
+ DS_LAS_USERATTR, "DS_LASUserAttrEval",
+ &lasinfo )) ) {
+ return LAS_EVAL_FAIL;
+ }
+
+ /* Which rule are we evaluating ? */
+ attrName = slapi_ch_strdup (attr_pattern );
+ if ( NULL == (p = strchr ( attrName, '#' ))) {
+ slapi_log_error( SLAPI_LOG_ACL, plugin_name,
+ "DS_LASUserAttrEval:Invalid value(%s)\n", attr_pattern);
+ slapi_ch_free ( (void **) &attrName );
+ return LAS_EVAL_FAIL;
+ }
+ attrValue = p;
+ attrValue++; /* skip the # */
+ *p = '\0'; /* null terminate the attr name */
+
+ if ( 0 == strncasecmp ( attrValue, "USERDN", 6)) {
+ matched = DS_LASUserDnAttrEval (errp,DS_LAS_USERDNATTR, comparator,
+ attrName, cachable, LAS_cookie,
+ subject, resource, auth_info, global_auth);
+ goto done_las;
+ } else if ( 0 == strncasecmp ( attrValue, "GROUPDN", 7)) {
+ matched = DS_LASGroupDnAttrEval (errp,DS_LAS_GROUPDNATTR, comparator,
+ attrName, cachable, LAS_cookie,
+ subject, resource, auth_info, global_auth);
+ goto done_las;
+ } else if ( 0 == strncasecmp ( attrValue, "LDAPURL", 7) ) {
+ URLAttrRule = 1;
+ } else if ( 0 == strncasecmp ( attrValue, "ROLEDN", 6)) {
+ matched = DS_LASRoleDnAttrEval (errp,DS_LAS_ROLEDN, comparator,
+ attrName, cachable, LAS_cookie,
+ subject, resource, auth_info, global_auth);
+ goto done_las;
+ }
+
+ if ( lasinfo.aclpb && ( NULL == lasinfo.aclpb->aclpb_client_entry )) {
+ /* SD 00/16/03 pass NULL in case the req is chained */
+ char **attrs=NULL;
+
+
+ /* Use new search internal API */
+ Slapi_PBlock *aPb = slapi_pblock_new ();
+ /*
+ * This search may be chained if chaining for ACL is
+ * is enabled in the backend and the entry is in
+ * a chained backend.
+ */
+ slapi_search_internal_set_pb ( aPb,
+ lasinfo.clientDn,
+ LDAP_SCOPE_BASE,
+ "objectclass=*",
+ attrs,
+ 0,
+ NULL /* controls */,
+ NULL /* uniqueid */,
+ aclplugin_get_identity (ACL_PLUGIN_IDENTITY),
+ 0 /* actions */);
+ slapi_search_internal_callback_pb(aPb,
+ lasinfo.aclpb /* callback_data */,
+ NULL/* result_callback */,
+ acllas__handle_client_search,
+ NULL /* referral_callback */);
+ slapi_pblock_destroy (aPb);
+
+ }
+
+ slapi_log_error ( SLAPI_LOG_ACL, plugin_name,
+ "DS_LASUserAttrEval: AttrName:%s, attrVal:%s\n", attrName, attrValue );
+
+ if ( URLAttrRule ) {
+ Slapi_Value *sval=NULL;
+ const struct berval *attrVal;
+ Slapi_Attr *attrs;
+ int i;
+
+ /* Get the attr from the resouce entry */
+ if ( 0 == slapi_entry_attr_find (lasinfo.resourceEntry, attrName, &attrs) ) {
+ i= slapi_attr_first_value ( attrs, &sval );
+ if ( i==-1 ) {
+ matched = ACL_FALSE; /* Attr val not there so it's value cannot equal other one */
+ goto done_acl;
+ }
+ } else {
+ matched = ACL_FALSE; /* Not there so it cannot equal another one */
+ goto done_acl;
+ }
+
+ while( matched != ACL_TRUE && (sval != NULL)) {
+ attrVal = slapi_value_get_berval ( sval );
+ matched = acllas__client_match_URL ( lasinfo.aclpb,
+ lasinfo.clientDn,
+ attrVal->bv_val);
+ if ( matched != ACL_TRUE )
+ i = slapi_attr_next_value ( attrs, i, &sval );
+ if ( matched == ACL_DONT_KNOW ) {
+ got_undefined = 1;
+ }
+ }/* while */
+ } else {
+ /*
+ * Here it's the userAttr = "OU#Directory Server" case.
+ * Allocate the Slapi_Value on the stack and init it by reference
+ * to avoid having to malloc and free memory.
+ */
+ Slapi_Value v;
+
+ slapi_value_init_string_passin(&v, attrValue);
+ rc = slapi_entry_attr_has_syntax_value ( lasinfo.resourceEntry, attrName,
+ &v );
+ if (rc) {
+ rc = slapi_entry_attr_has_syntax_value (
+ lasinfo.aclpb->aclpb_client_entry,
+ attrName, &v );
+ if (rc) matched = ACL_TRUE;
+ }
+ /* Nothing to free--cool */
+ }
+
+ /*
+ * Find out what the result is, in
+ * this case matched is one of ACL_TRUE, ACL_FALSE or ACL_DONT_KNOW
+ * and got_undefined says whether a logical term evaluated to ACL_DONT_KNOW.
+ *
+ */
+done_acl:
+ if ( matched == ACL_TRUE || !got_undefined) {
+ if (comparator == CMP_OP_EQ) {
+ rc = (matched == ACL_TRUE ? LAS_EVAL_TRUE : LAS_EVAL_FALSE);
+ } else {
+ rc = (matched == ACL_TRUE ? LAS_EVAL_FALSE : LAS_EVAL_TRUE);
+ }
+ } else {
+ rc = LAS_EVAL_FAIL;
+ }
+
+ slapi_ch_free ( (void **) &attrName );
+ return rc;
+
+done_las:
+ /*
+ * In this case matched is already LAS_EVAL_TRUE or LAS_EVAL_FALSE or
+ * LAS_EVAL_FAIL.
+ */
+ if ( matched != LAS_EVAL_FAIL ) {
+ if (comparator == CMP_OP_EQ) {
+ rc = matched;
+ } else {
+ rc = (matched == LAS_EVAL_TRUE ? LAS_EVAL_FALSE : LAS_EVAL_TRUE);
+ }
+ }
+
+ slapi_ch_free ( (void **) &attrName );
+ return rc;
+}
+
+/*
+ * acllas__client_match_URL
+ * Match a client to a URL.
+ *
+ * Returns:
+ * ACL_TRUE - matched the URL
+ * ACL_FALSE - Sorry; no match
+ *
+ */
+static int
+acllas__client_match_URL (struct acl_pblock *aclpb, char *n_clientdn, char *url )
+{
+
+ LDAPURLDesc *ludp;
+ int rc;
+ Slapi_Filter *f = NULL;
+
+
+ /* Get the client's entry if we don't have already */
+ if ( aclpb && ( NULL == aclpb->aclpb_client_entry )) {
+ /* SD 00/16/03 Get every attr in case req chained */
+ char **attrs=NULL;
+
+ /* Use new search internal API */
+ Slapi_PBlock * aPb = slapi_pblock_new ();
+ /*
+ * This search may be chained if chaining for ACL is
+ * is enabled in the backend and the entry is in
+ * a chained backend.
+ */
+ slapi_search_internal_set_pb ( aPb,
+ n_clientdn,
+ LDAP_SCOPE_BASE,
+ "objectclass=*",
+ attrs,
+ 0,
+ NULL /* controls */,
+ NULL /* uniqueid */,
+ aclplugin_get_identity (ACL_PLUGIN_IDENTITY),
+ 0 /* actions */);
+ slapi_search_internal_callback_pb(aPb,
+ aclpb /* callback_data */,
+ NULL/* result_callback */,
+ acllas__handle_client_search,
+ NULL /* referral_callback */);
+ slapi_pblock_destroy (aPb);
+ }
+
+ if ( NULL == aclpb->aclpb_client_entry ) {
+ slapi_log_error ( SLAPI_LOG_ACL, plugin_name,
+ "DS_LASUserAttrEval: Unable to get client's entry\n",0,0,0);
+ return ACL_FALSE;
+ }
+
+ if (( rc = ldap_url_parse( url, &ludp)) != 0 ) {
+ return ACL_FALSE;
+
+ }
+ if ( ( NULL == ludp->lud_dn) || ( NULL == ludp->lud_filter) ) {
+ ldap_free_urldesc( ludp );
+ return ACL_FALSE;
+ }
+
+ /* Normalize in place the dn */
+ slapi_dn_normalize ( ludp->lud_dn );
+
+ /* Check the scope */
+ if ( ludp->lud_scope == LDAP_SCOPE_SUBTREE ) {
+ if (!slapi_dn_issuffix(n_clientdn, ludp->lud_dn)) {
+ ldap_free_urldesc( ludp );
+ return ACL_FALSE;
+ }
+ } else if ( ludp->lud_scope == LDAP_SCOPE_ONELEVEL ) {
+ char *parent = slapi_dn_parent (n_clientdn);
+
+ if (slapi_utf8casecmp ((ACLUCHP)parent, (ACLUCHP)ludp->lud_dn) != 0 ) {
+ slapi_ch_free ( (void **) &parent);
+ ldap_free_urldesc( ludp );
+ return ACL_FALSE;
+ }
+ slapi_ch_free ( (void **) &parent);
+ } else { /* default */
+ if (slapi_utf8casecmp ( (ACLUCHP)n_clientdn, (ACLUCHP)ludp->lud_dn) != 0 ) {
+ ldap_free_urldesc( ludp );
+ return ACL_FALSE;
+ }
+
+ }
+
+
+ /* Convert the filter string */
+ f = slapi_str2filter ( ludp->lud_filter );
+
+ rc = ACL_TRUE;
+ if (0 != slapi_vattr_filter_test ( aclpb->aclpb_pblock,
+ aclpb->aclpb_client_entry, f, 0 /* no acces chk */ ))
+ rc = ACL_FALSE;
+
+ ldap_free_urldesc( ludp );
+ slapi_filter_free ( f, 1 ) ;
+
+ return rc;
+}
+static int
+acllas__handle_client_search ( Slapi_Entry *e, void *callback_data )
+{
+ struct acl_pblock *aclpb = (struct acl_pblock *) callback_data;
+
+ /* If we are here means we have found the entry */
+ if ( NULL == aclpb-> aclpb_client_entry)
+ aclpb->aclpb_client_entry = slapi_entry_dup ( e );
+ return 0;
+}
+/*
+*
+* Do all the necessary setup for all the
+* LASes.
+* It will only fail if it's passed garbage (which should not happen) or
+* if the data it needs to stock the lasinfo is not available, which
+* also should not happen.
+*
+*
+* Return value: 0 or one of these
+* #define LAS_EVAL_TRUE -1
+* #define LAS_EVAL_FALSE -2
+* #define LAS_EVAL_DECLINE -3
+* #define LAS_EVAL_FAIL -4
+* #define LAS_EVAL_INVALID -5
+*/
+
+static int
+__acllas_setup ( NSErr_t *errp, char *attr_name, CmpOp_t comparator,
+ char *attr_pattern, int *cachable, void **LAS_cookie,
+ PList_t subject, PList_t resource, PList_t auth_info,
+ PList_t global_auth, char *lasType, char*lasName, lasInfo *linfo)
+{
+
+ int rc;
+ memset ( linfo, 0, sizeof ( lasInfo) );
+
+ *cachable = 0;
+ *LAS_cookie = (void *)0;
+
+ if (strcmp(attr_name, lasType) != 0) {
+ slapi_log_error( SLAPI_LOG_ACL, plugin_name,
+ "%s:Invalid LAS(%s)\n", lasName, attr_name);
+ return LAS_EVAL_INVALID;
+ }
+
+ if ((comparator != CMP_OP_EQ) && (comparator != CMP_OP_NE)) {
+ slapi_log_error( SLAPI_LOG_ACL, plugin_name,
+ "%s:Invalid comparator(%d)\n", lasName, (int)comparator);
+ return LAS_EVAL_INVALID;
+ }
+
+
+ /* Get the client DN */
+ rc = ACL_GetAttribute(errp, DS_ATTR_USERDN, (void **)&linfo->clientDn,
+ subject, resource, auth_info, global_auth);
+
+ if ( rc != LAS_EVAL_TRUE ) {
+ acl_print_acllib_err(errp, NULL);
+ slapi_log_error( SLAPI_LOG_ACL, plugin_name,
+ "%s:Unable to get the clientdn attribute(%d)\n",lasName, rc);
+ return LAS_EVAL_FAIL;
+ }
+
+ /* Check if we have a user or not */
+ if (linfo->clientDn) {
+ /* See if it's a anonymous user */
+ if (*(linfo->clientDn) == '\0')
+ linfo->anomUser = ACL_TRUE;
+ } else {
+ slapi_log_error( SLAPI_LOG_ACL, plugin_name,
+ "%s: No user\n",lasName);
+ return LAS_EVAL_FAIL;
+ }
+
+ if ((rc = PListFindValue(subject, DS_ATTR_ENTRY,
+ (void **)&linfo->resourceEntry, NULL)) < 0) {
+ acl_print_acllib_err(errp, NULL);
+ slapi_log_error( SLAPI_LOG_ACL, plugin_name,
+ "%s:Unable to get the Slapi_Entry attr(%d)\n",lasName, rc);
+ return LAS_EVAL_FAIL;
+ }
+
+ /* Get ACLPB */
+ rc = ACL_GetAttribute(errp, DS_PROP_ACLPB, (void **)&linfo->aclpb,
+ subject, resource, auth_info, global_auth);
+ if ( rc != LAS_EVAL_TRUE ) {
+ acl_print_acllib_err(errp, NULL);
+ slapi_log_error( SLAPI_LOG_ACL, plugin_name,
+ "%s:Unable to get the ACLPB(%d)\n", lasName, rc);
+ return LAS_EVAL_FAIL;
+ }
+ if (NULL == attr_pattern ) {
+ slapi_log_error( SLAPI_LOG_ACL, plugin_name,
+ "%s:No rule value in the ACL\n", lasName);
+
+ return LAS_EVAL_FAIL;
+ }
+ /* get the authentication type */
+ if ((rc = PListFindValue(subject, DS_ATTR_AUTHTYPE,
+ (void **)&linfo->authType, NULL)) < 0) {
+ acl_print_acllib_err(errp, NULL);
+ slapi_log_error( SLAPI_LOG_ACL, plugin_name,
+ "%s:Unable to get the auth type(%d)\n", rc);
+ return LAS_EVAL_FAIL;
+ }
+ return 0;
+}
+
+/*
+ * See if clientDN has role roleDN.
+ * Here we know the user is not anon and that the role
+ * is not the anyone role ie. it's actually worth invoking the roles code.
+*/
+
+static int acllas__user_has_role( struct acl_pblock *aclpb,
+ Slapi_DN *roleDN, Slapi_DN *clientDn) {
+
+ int present = 0;
+ int rc = 0;
+
+ /* Get the client's entry if we don't have already */
+ if ( aclpb && ( NULL == aclpb->aclpb_client_entry )) {
+ /* SD 00/16/03 Get every attr in case req chained */
+ char **attrs=NULL;
+
+ /* Use new search internal API */
+ Slapi_PBlock * aPb = slapi_pblock_new ();
+ /*
+ * This search may NOT be chained--the user and the role definition
+ * must be co-located (chaining is not supported for the roles
+ * plugin in 5.0
+ */
+ slapi_search_internal_set_pb ( aPb,
+ slapi_sdn_get_ndn(clientDn),
+ LDAP_SCOPE_BASE,
+ "objectclass=*",
+ attrs,
+ 0,
+ NULL /* controls */,
+ NULL /* uniqueid */,
+ aclplugin_get_identity (ACL_PLUGIN_IDENTITY),
+ SLAPI_OP_FLAG_NEVER_CHAIN /* actions */);
+ slapi_search_internal_callback_pb(aPb,
+ aclpb /* callback_data */,
+ NULL/* result_callback */,
+ acllas__handle_client_search,
+ NULL /* referral_callback */);
+ slapi_pblock_destroy (aPb);
+ }
+
+ if ( NULL == aclpb->aclpb_client_entry ) {
+ slapi_log_error ( SLAPI_LOG_ACL, plugin_name,
+ "acllas__user_has_role: Unable to get client's entry\n",0,0,0);
+ return ACL_FALSE;
+ }
+
+ /* If the client has the role then it's a match, otherwise no */
+
+ rc = slapi_role_check( aclpb->aclpb_client_entry, roleDN, &present);
+ if ( present ) {
+ return(ACL_TRUE);
+ }
+
+ return(ACL_FALSE);
+}
+
+int
+DS_LASRoleDnAttrEval(NSErr_t *errp, char *attr_name, CmpOp_t comparator,
+ char *attr_pattern, int *cachable, void **LAS_cookie,
+ PList_t subject, PList_t resource, PList_t auth_info,
+ PList_t global_auth)
+{
+
+ char *s_attrName = NULL;
+ char *attrName;
+ int matched;
+ int rc;
+ Slapi_Attr *attr;
+ int numOflevels = 0;
+ char *n_currEntryDn = NULL;
+ lasInfo lasinfo;
+ Slapi_Value *sval=NULL;
+ const struct berval *attrVal;
+ int k=0;
+ int got_undefined = 0;
+
+ if ( 0 != (rc = __acllas_setup (errp, attr_name, comparator,
+ attr_pattern,cachable,LAS_cookie,
+ subject, resource, auth_info,global_auth,
+ DS_LAS_ROLEDN, "DS_LASRoleDnAttrEval",
+ &lasinfo )) ) {
+ return LAS_EVAL_FAIL;
+ }
+
+ /* For anonymous client, they have no roles so the match is false. */
+ if ( lasinfo.anomUser )
+ return LAS_EVAL_FALSE;
+
+ /*
+ **
+ ** The function of this LAS is to find out if the client has
+ ** the role specified in the attr.
+ ** attr_pattern looks like: "ROLEDN cn=role1,o=sun.com"
+ */
+ attrName = attr_pattern;
+
+ matched = ACL_FALSE;
+ slapi_entry_attr_find( lasinfo.resourceEntry, attrName, &attr);
+ if (attr == NULL) {
+ /*
+ * Here the entry does not contain the attribute so the user
+ * cannot have this "null" role
+ */
+ return LAS_EVAL_FALSE;
+ }
+
+ if (lasinfo.aclpb->aclpb_optype == SLAPI_OPERATION_ADD) {
+ /*
+ * Here the entry does not contain the attribute so the user
+ * cannot have this "null" role or
+ * For the add operation, the resource itself
+ * must never be allowed to grant access--
+ * This is because access would be granted based on a value
+ * of an attribute in the new entry--security hole.
+ * XXX is this therefore FALSE or DONT_KNOW ?
+ *
+ *
+ */
+ slapi_log_error( SLAPI_LOG_ACL, plugin_name,
+ "ACL info: userattr=XXX#ROLEDN does not allow ADD permission.\n");
+ got_undefined = 1;
+ } else {
+
+ /*
+ * Got the first value.
+ * Test all the values of this attribute--if the client has _any_
+ * of the roles then it's a match.
+ */
+ k = slapi_attr_first_value ( attr,&sval );
+ while ( k != -1 ) {
+ char *n_attrval;
+ Slapi_DN *roleDN;
+
+ attrVal = slapi_value_get_berval ( sval );
+ n_attrval = slapi_ch_strdup( attrVal->bv_val);
+ n_attrval = slapi_dn_normalize (n_attrval);
+ roleDN = slapi_sdn_new_dn_byval(n_attrval);
+
+ /* We support: The attribute value can be a USER or a GROUP.
+ ** Let's compare with the client, thi might be just an user. If it is not
+ ** then we test it against the list of groups.
+ */
+ if ((matched = acllas__user_has_role(
+ lasinfo.aclpb,
+ roleDN,
+ lasinfo.aclpb->aclpb_authorization_sdn)) == ACL_TRUE){
+ slapi_ch_free ( (void **)&n_attrval );
+ slapi_sdn_free(&roleDN );
+ break;
+ }
+ slapi_ch_free ( (void **)&n_attrval );
+ slapi_sdn_free(&roleDN );
+ if (matched == ACL_TRUE) {
+ break;
+ } else if ( matched == ACL_DONT_KNOW ) {
+ /* record this but keep going--maybe another group will evaluate to TRUE */
+ got_undefined = 1;
+ }
+ k= slapi_attr_next_value ( attr, k, &sval );
+ }/* while */
+ }
+
+ /*
+ * If no terms were undefined, then evaluate as normal.
+ * If there was an undefined term, but another one was TRUE, then we also evaluate
+ * as normal. Otherwise, the whole expression is UNDEFINED.
+ */
+ if ( matched == ACL_TRUE || !got_undefined ) {
+ if (comparator == CMP_OP_EQ) {
+ rc = (matched == ACL_TRUE ? LAS_EVAL_TRUE : LAS_EVAL_FALSE);
+ } else {
+ rc = (matched == ACL_TRUE ? LAS_EVAL_FALSE : LAS_EVAL_TRUE);
+ }
+ } else {
+ rc = LAS_EVAL_FAIL;
+ }
+ return (rc);
+}
+
+/*
+ * Here, determine if lasinfo->clientDn matches user (which contains
+ * a ($dn) or a $attr component or both.) As defined in the aci
+ * lasinfo->aclpb->aclpb_curr_aci,
+ * which is the current aci being evaluated.
+ *
+ * returns: ACL_TRUE for matched,
+ * ACL_FALSE for matched.
+ * ACL_DONT_KNOW otherwise.
+ *
+ *
+*/
+
+int
+aclutil_evaluate_macro( char * rule, lasInfo *lasinfo,
+ acl_eval_types evalType ) {
+
+ int matched = 0;
+ aci_t *aci;
+ char *matched_val = NULL;
+ char **candidate_list = NULL;
+ char **inner_list = NULL;
+ char **sptr = NULL;
+ char **tptr = NULL;
+ char *t = NULL;
+ char *s = NULL;
+ char *target_dn = NULL;
+ struct acl_pblock *aclpb = lasinfo->aclpb;
+ int found_matched_val_in_ht = 0;
+
+ aci = lasinfo->aclpb->aclpb_curr_aci;
+ /* Get a pointer to the ndn in the resouirce */
+ target_dn = slapi_entry_get_ndn ( lasinfo->resourceEntry );
+
+ /*
+ * First, get the matched value from the target resource.
+ * We have alredy done this matching once beofer at tasrget match time.
+ */
+
+ LDAPDebug ( LDAP_DEBUG_ACL, "aclutil_evaluate_macro for aci '%s'"
+ "index '%d'\n",
+ aci->aclName, aci->aci_index,0);
+
+ if ( aci->aci_macro == NULL ) {
+ /* No $dn in the target, it's a $attr type subject rule */
+ matched_val = NULL;
+ } else {
+
+ /*
+ * Look up the matched_val value calculated
+ * from the target and stored judiciously there for us.
+ */
+
+ if ( (matched_val = (char *)acl_ht_lookup( aclpb->aclpb_macro_ht,
+ (PLHashNumber)aci->aci_index)) == NULL) {
+ LDAPDebug( LDAP_DEBUG_ACL,
+ "ACL info: failed to locate the calculated target"
+ "macro for aci '%s' index '%d'\n",
+ aci->aclName, aci->aci_index,0);
+ return(ACL_FALSE); /* Not a match */
+ } else {
+ LDAPDebug( LDAP_DEBUG_ACL,
+ "ACL info: found matched_val (%s) for aci index %d"
+ "in macro ht\n",
+ aci->aclName, aci->aci_index,0);
+
+ found_matched_val_in_ht = 1;
+ }
+ }
+
+ /*
+ * Now, make a candidate
+ * list of strings to match against the client.
+ * This involves replacing ($dn) or [$dn] by either the matched
+ * value, or all the suffix substrings of matched_val.
+ * If there is no $dn then the candidate list is just
+ * user itself.
+ *
+ */
+
+ candidate_list = acllas_replace_dn_macro( rule, matched_val, lasinfo);
+
+ sptr= candidate_list;
+ while( *sptr != NULL && !matched) {
+
+ s = *sptr;
+
+ /*
+ * Now s may contain some $attr macros.
+ * So, make a candidate list, got by replacing each occurence
+ * of $attr with all the values that attribute has in
+ * the resource entry.
+ */
+
+ inner_list = acllas_replace_attr_macro( s, lasinfo);
+
+ tptr = inner_list;
+ while( *tptr != NULL && (matched != ACL_TRUE) ){
+
+ t = *tptr;
+
+ /*
+ * Now, at last t is a candidate string we can
+ * match agains the client.
+ *
+ * $dn and $attr can appear in userdn, graoupdn and roledn
+ * rules, so we we need to decide which type we
+ * currently evaluating and evaluate that.
+ *
+ * If the string generated was undefined, eg it contained
+ * ($attr.ou) and the entry did not have an ou attribute,then
+ * the empty string is returned for this. So it we find
+ * an empty string in the list, skip it--it does not match.
+ */
+
+ if ( *t != '\0') {
+ if ( evalType == ACL_EVAL_USER ) {
+
+ matched = acllas_eval_one_user( lasinfo->aclpb,
+ lasinfo->clientDn, t);
+ } else if (evalType == ACL_EVAL_GROUP) {
+
+ matched = acllas_eval_one_group(t, lasinfo);
+ } else if (evalType == ACL_EVAL_ROLE) {
+ matched = acllas_eval_one_role(t, lasinfo);
+ } else if (evalType == ACL_EVAL_GROUPDNATTR) {
+ matched = acllas__eval_memberGroupDnAttr(t,
+ lasinfo->resourceEntry,
+ lasinfo->clientDn,
+ lasinfo->aclpb);
+ } else if ( evalType == ACL_EVAL_TARGET_FILTER) {
+
+ matched = acllas_eval_one_target_filter(t,
+ lasinfo->resourceEntry);
+
+ }
+ }
+
+ tptr++;
+
+ }/*inner while*/
+ charray_free(inner_list);
+
+ sptr++;
+ }/* outer while */
+
+ charray_free(candidate_list);
+
+ return(matched);
+
+}
+
+/*
+ * Here, replace the first occurrence of $(dn) with matched_val.
+ * replace any occurrence of $[dn] with each of the suffix substrings
+ * of matched_val.
+ * Each of these strings is returned in a NULL terminated list of strings.
+ *
+ * If there is no $dn thing then the returned list just contains rule itself.
+ *
+ * eg. rule: cn=fred,ou=*, ($dn), o=sun.com
+ * matched_val: ou=People,o=icnc
+ *
+ * Then we return the list
+ * cn=fred,ou=*,ou=People,o=icnc,o=sun.com NULL
+ *
+ * eg. rule: cn=fred,ou=*,[$dn], o=sun.com
+ * matched_val: ou=People,o=icnc
+ *
+ * Then we return the list
+ * cn=fred,ou=*,ou=People,o=icnc,o=sun.com
+ * cn=fred,ou=*,o=icnc,o=sun.com
+ * NULL
+ *
+ *
+*/
+
+static char **
+acllas_replace_dn_macro( char *rule, char *matched_val, lasInfo *lasinfo) {
+
+ char **a = NULL;
+ char *str = NULL;
+ char *patched_rule = NULL;
+ char *rule_to_use = NULL;
+ char *new_patched_rule = NULL;
+ char *rule_prefix = NULL;
+ char *rule_suffix = NULL;
+ int rule_suffix_len = 0;
+ char *comp = NULL;
+ int matched_val_len = 0;
+ int macro_len = 0;
+ int j = 0;
+ int has_macro_dn = 0;
+ int has_macro_levels = 0;
+
+ /* Determine what the rule's got once */
+ if ( strstr(rule, ACL_RULE_MACRO_DN_KEY) != NULL) {
+ has_macro_dn = 1;
+ }
+
+ if ( strstr(rule, ACL_RULE_MACRO_DN_LEVELS_KEY) != NULL) {
+ has_macro_levels = 1;
+ }
+
+ if ( !has_macro_dn && !has_macro_levels ) {
+
+ /*
+ * No $dn thing, just return a list with two elements, rule and NULL.
+ * charray_add will create the list and null terminate it.
+ */
+
+ charray_add( &a, slapi_ch_strdup(rule));
+ return(a);
+ } else {
+
+ /*
+ * Have an occurrence of the macro rules
+ *
+ * First, replace all occurrencers of ($dn) with the matched_val
+ */
+
+ if ( has_macro_dn) {
+ patched_rule =
+ acl_replace_str(rule, ACL_RULE_MACRO_DN_KEY, matched_val);
+ }
+
+ /* If there are no [$dn] we're done */
+
+ if ( !has_macro_levels ) {
+ charray_add( &a, patched_rule);
+ return(a);
+ } else {
+
+ /*
+ * It's a [$dn] type, so walk matched_val, splicing in all
+ * the suffix substrings and adding each such string to
+ * to the returned list.
+ * get_next_component() does not return the commas--the
+ * prefix and suffix should come with their commas.
+ *
+ * All occurrences of each [$dn] are replaced with each level.
+ *
+ * If has_macro_dn then patched_rule is the rule to strart with,
+ * and this needs to be freed at the end, otherwise
+ * just use rule.
+ */
+
+ if (patched_rule) {
+ rule_to_use = patched_rule;
+ } else {
+ rule_to_use = rule;
+ }
+
+ matched_val_len = strlen(matched_val);
+ j = 0;
+
+ while( j < matched_val_len) {
+
+ new_patched_rule =
+ acl_replace_str(rule_to_use, ACL_RULE_MACRO_DN_LEVELS_KEY,
+ &matched_val[j]);
+ charray_add( &a, new_patched_rule);
+
+ j += acl_find_comp_end(&matched_val[j]);
+ }
+
+ if (patched_rule) {
+ slapi_ch_free((void**)&patched_rule);
+ }
+
+ return(a);
+ }
+ }
+}
+
+/*
+ * Here, replace any occurrence of $attr.attrname with the
+ * value of attrname from lasinfo->resourceEntry.
+ *
+ *
+ * If there is no $attr thing then the returned list just contains rule
+ * itself.
+ *
+ * eg. rule: cn=fred,ou=*,ou=$attr.ou,o=sun.com
+ * ou: People
+ * ou: icnc
+ *
+ * Then we return the list
+ * cn=fred,ou=*,ou=People,o=sun.com
+ * cn=fred,ou=*,ou=icnc,o=sun.com
+ *
+*/
+
+static char **
+acllas_replace_attr_macro( char *rule, lasInfo *lasinfo) {
+
+ char **a = NULL;
+ char **working_list = NULL;
+ Slapi_Entry *e = lasinfo->resourceEntry;
+ char *str, *working_rule;
+ char *macro_str, *macro_attr_name;
+ int l;
+ Slapi_Attr *attr = NULL;
+
+ str = strstr(rule, ACL_RULE_MACRO_ATTR_KEY);
+ if ( str == NULL ) {
+
+ charray_add(&a, slapi_ch_strdup(rule));
+ return(a);
+
+ } else {
+
+ working_rule = slapi_ch_strdup(rule);
+ str = strstr(working_rule, ACL_RULE_MACRO_ATTR_KEY);
+ charray_add(&working_list, working_rule );
+
+ while( str != NULL) {
+
+ /*
+ * working_rule is the first member of working_list.
+ * str points to the next $attr.attrName in working_rule.
+ * each member of working_list needs to have each occurence of
+ * $attr.atrName replaced with the value of attrName in e.
+ * If attrName is multi valued then this generates another
+ * list which replaces the old one.
+ */
+
+ l = acl_strstr(&str[0], ")");
+ macro_str = slapi_ch_malloc(l+2);
+ strncpy( macro_str, &str[0], l+1);
+ macro_str[l+1] = '\0';
+
+ str = strstr(macro_str, ".");
+ str++; /* skip the . */
+ l = acl_strstr(&str[0], ")");
+ macro_attr_name = slapi_ch_malloc(l+1);
+ strncpy( macro_attr_name, &str[0], l);
+ macro_attr_name[l] = '\0';
+
+ slapi_entry_attr_find ( e, macro_attr_name, &attr );
+ if ( NULL == attr ) {
+
+ /*
+ * Here, if a $attr.attrName is such that the attrName
+ * does not occur in the entry then return a ""--
+ * this will go back to the matching code in
+ * aclutil_evaluate_macro() where "" will
+ * be taken as the candidate.
+ */
+
+ slapi_ch_free((void **)&macro_str);
+ slapi_ch_free((void **)&macro_attr_name);
+
+ charray_free(working_list);
+ charray_add(&a, slapi_ch_strdup(""));
+ return(a);
+
+ } else{
+
+ const struct berval *attrValue;
+ Slapi_Value *sval;
+ int i, j;
+ char *patched_rule;
+
+ a = NULL;
+ i= slapi_attr_first_value ( attr, &sval );
+ while(i != -1) {
+ attrValue = slapi_value_get_berval(sval);
+
+ j = 0;
+ while( working_list[j] != NULL) {
+
+ patched_rule =
+ acl_replace_str(working_list[j],
+ macro_str, attrValue->bv_val);
+ charray_add(&a, patched_rule);
+ j++;
+ }
+
+ i= slapi_attr_next_value( attr, i, &sval );
+ }/* while */
+
+ /*
+ * Here, a is working_list, where each member has had
+ * macro_str replaced with attrVal.
+ */
+
+ charray_free(working_list);
+ working_list = a;
+ working_rule = a[0];
+ }
+ slapi_ch_free((void **)&macro_str);
+ slapi_ch_free((void **)&macro_attr_name);
+
+ str = strstr(working_rule, ACL_RULE_MACRO_ATTR_KEY);
+
+ }/* while */
+
+ return(working_list);
+ }
+
+
+}
+
+/*
+ * returns ACL_TRUE, ACL_FALSE or ACL_DONT_KNOW.
+ *
+ * user is a string from the userdn keyword which may contain
+ * * components. This routine does the compare component by component, so
+ * that * behaves differently to "normal".
+ * Any ($dn) or $attr must have been removed from user before this is called.
+*/
+static int
+acllas_eval_one_user( struct acl_pblock *aclpb, char * clientDN, char *rule) {
+
+ int exact_match = 0;
+ int ret_code = 0;
+ const size_t LDAP_URL_prefix_len = strlen(LDAP_URL_prefix);
+ char *s = NULL;
+
+
+
+ /* URL format */
+ if ((s = strchr (rule, '?'))!= NULL) {
+ /* URL format */
+ if (acllas__client_match_URL ( aclpb, clientDN,
+ rule) == ACL_TRUE) {
+ exact_match = 1;
+ }
+ } else if ( strstr(rule, "=*") == NULL ) {
+ /* Just a straight compare */
+ /* skip the ldap:/// part */
+ rule += LDAP_URL_prefix_len;
+ exact_match = !slapi_utf8casecmp((ACLUCHP)clientDN,
+ (ACLUCHP)rule);
+ } else{
+ /* Here, contains a =*, so need to match comp by comp */
+ /* skip the ldap:/// part */
+ rule += LDAP_URL_prefix_len;
+ ret_code = acl_match_prefix( rule, clientDN, &exact_match);
+ }
+ if ( exact_match) {
+ return( ACL_TRUE);
+ } else {
+ return(ACL_FALSE);
+ }
+}
+
+/*
+ * returns ACL_TRUE, ACL_FALSE and ACL_DONT_KNOW.
+ *
+ * The user string has had all ($dn) and $attr replaced
+ * so the only dodgy thing left is a *.
+ *
+ * If * appears in such a user string, then it matches only that
+ * component, not .*, like it would otherwise.
+ *
+*/
+static int
+acllas_eval_one_group(char *groupbuf, lasInfo *lasinfo) {
+
+ if (groupbuf) {
+ return( acllas__user_ismember_of_group (
+ lasinfo->aclpb,
+ groupbuf,
+ lasinfo->clientDn,
+ ACLLAS_CACHE_ALL_GROUPS,
+ lasinfo->aclpb->aclpb_clientcert
+ ));
+ } else {
+ return(ACL_FALSE); /* not in the empty group */
+ }
+}
+
+/*
+ * returns ACL_TRUE for match, ACL_FALSE for not a match, ACL_DONT_KNOW otherwise.
+*/
+static int
+acllas_eval_one_role(char *role, lasInfo *lasinfo) {
+
+ Slapi_DN *roleDN = NULL;
+ int rc = ACL_FALSE;
+ char ebuf [ BUFSIZ ];
+
+ /*
+ * See if lasinfo.clientDn has role rolebuf.
+ * Here we know it's not an anom user nor
+ * a an anyone user--the client dn must be matched against
+ * a real role.
+ */
+
+ roleDN = slapi_sdn_new_dn_byval(role);
+ if (role) {
+ rc = acllas__user_has_role(
+ lasinfo->aclpb,
+ roleDN,
+ lasinfo->aclpb->aclpb_authorization_sdn);
+ } else { /* The user does not have the empty role */
+ rc = ACL_FALSE;
+ }
+ slapi_sdn_free(&roleDN );
+
+ /* Some useful logging */
+ if (rc == ACL_TRUE ) {
+ slapi_log_error( SLAPI_LOG_ACL, plugin_name,
+ "role evaluation: user '%s' does have role '%s'\n",
+ ACL_ESCAPE_STRING_WITH_PUNCTUATION (lasinfo->clientDn, ebuf),
+ role);
+ } else {
+ slapi_log_error( SLAPI_LOG_ACL, plugin_name,
+ "role evaluation: user '%s' does NOT have role '%s'\n",
+ ACL_ESCAPE_STRING_WITH_PUNCTUATION (lasinfo->clientDn, ebuf),
+ role);
+ }
+ return(rc);
+}
+
+/*
+ * returns ACL_TRUE if e matches the filter str, ACL_FALSE if not,
+ * ACL_DONT_KNOW otherwise.
+*/
+static int acllas_eval_one_target_filter( char * str, Slapi_Entry *e) {
+
+ int rc = ACL_FALSE;
+ Slapi_Filter *f = NULL;
+
+ if ((f = slapi_str2filter(str)) == NULL) {
+ slapi_log_error(SLAPI_LOG_FATAL, plugin_name,
+ "Warning: Bad targetfilter(%s) in aci: does not match\n", str);
+ return(ACL_DONT_KNOW);
+ }
+
+ if (slapi_vattr_filter_test(NULL, e, f, 0 /*don't do acess chk*/)!= 0) {
+ rc = ACL_FALSE; /* Filter does not match */
+ } else {
+ rc = ACL_TRUE; /* filter does match */
+ }
+ slapi_filter_free(f, 1);
+
+ return(rc);
+
+}
+
+
+
+
+
+/***************************************************************************/
+/* E N D */
+/***************************************************************************/
diff --git a/ldap/servers/plugins/acl/acllist.c b/ldap/servers/plugins/acl/acllist.c
new file mode 100644
index 00000000..0147626d
--- /dev/null
+++ b/ldap/servers/plugins/acl/acllist.c
@@ -0,0 +1,940 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+
+/************************************************************************
+ *
+ * ACLLIST
+ *
+ * All the ACLs are read when the server is started. The ACLs are
+ * parsed and kept in an AVL tree. All the ACL List management are
+ * in this file.
+ *
+ * The locking on the aci cache is implemented using the acllist_acicache*()
+ * routines--a read/write lock.
+ *
+ * The granularity of the view of the cache is the entry level--ie.
+ * when an entry is being modified (mod,add,delete,modrdn) and the mod
+ * involves the aci attribute, then other operations will see the acl cache
+ * before the whole change or after the whole change, but not during the change.
+ * cf. acl.c:acl_modified()
+ *
+ * The only tricky issue is that there is also locking
+ * implemented for the anonymous profile and sometimes we need to take both
+ * locks cf. aclanom_anom_genProfile(). The rule is
+ * always take the acicache lock first, followed by the anon lock--following
+ * this rule will ensure no dead lock scenarios can arise.
+ *
+ * Some routines are called in different places with different lock
+ * contexts--for these routines acl_lock_flag_t is used to
+ * pass the context.
+ *
+ */
+#include "acl.h"
+
+static PRRWLock *aci_rwlock = NULL;
+#define ACILIST_LOCK_READ() PR_RWLock_Rlock (aci_rwlock )
+#define ACILIST_UNLOCK_READ() PR_RWLock_Unlock (aci_rwlock )
+#define ACILIST_LOCK_WRITE() PR_RWLock_Wlock (aci_rwlock )
+#define ACILIST_UNLOCK_WRITE() PR_RWLock_Unlock (aci_rwlock )
+
+
+/* Root of the TREE */
+static Avlnode *acllistRoot = NULL;
+
+#define CONTAINER_INCR 2000
+
+/* The container array */
+static AciContainer **aciContainerArray;
+static PRUint32 currContainerIndex =0;
+static PRUint32 maxContainerIndex = 0;
+static int curAciIndex = 1;
+
+/* PROTOTYPES */
+static int __acllist_add_aci ( aci_t *aci );
+static int __acllist_aciContainer_node_cmp ( caddr_t d1, caddr_t d2 );
+static int __acllist_aciContainer_node_dup ( caddr_t d1, caddr_t d2 );
+static void __acllist_free_aciContainer ( AciContainer **container);
+static void free_targetattrfilters( Targetattrfilter ***input_attrFilterArray);
+
+void my_print( Avlnode *root );
+
+
+int
+acllist_init ()
+{
+
+ if (( aci_rwlock = PR_NewRWLock( PR_RWLOCK_RANK_NONE,"ACLLIST LOCK") ) == NULL ) {
+ slapi_log_error( SLAPI_LOG_FATAL, plugin_name,
+ "acllist_init:failed in getting the rwlock\n" );
+ return 1;
+ }
+
+ aciContainerArray = (AciContainer **) slapi_ch_calloc ( 1,
+ CONTAINER_INCR * sizeof ( AciContainer * ) );
+ maxContainerIndex = CONTAINER_INCR;
+ currContainerIndex = 0;
+
+ return 0;
+}
+
+/*
+ * This is the callback for backend state changes.
+ * It needs to add/remove acis as backends come up and go down.
+ *
+ * The strategy is simple:
+ * When a backend moves to the SLAPI_BE_STATE_ON then we go get all the acis
+ * add them to the cache.
+ * When a backend moves out of the SLAPI_BE_STATE_ON then we remove them all.
+ *
+*/
+
+void acl_be_state_change_fnc ( void *handle, char *be_name, int old_state,
+ int new_state) {
+ Slapi_Backend *be=NULL;
+ const Slapi_DN *sdn;
+
+
+ if ( old_state == SLAPI_BE_STATE_ON && new_state != SLAPI_BE_STATE_ON) {
+
+ slapi_log_error ( SLAPI_LOG_ACL, plugin_name,
+ "Backend %s is no longer STARTED--deactivating it's acis\n",
+ be_name);
+
+ if ( (be = slapi_be_select_by_instance_name( be_name )) == NULL) {
+ slapi_log_error ( SLAPI_LOG_ACL, plugin_name,
+ "Failed to retreive backend--NOT activating it's acis\n");
+ return;
+ }
+
+ /*
+ * Just get the first suffix--if there are multiple XXX ?
+ */
+
+ if ( (sdn = slapi_be_getsuffix( be, 0)) == NULL ) {
+ slapi_log_error ( SLAPI_LOG_ACL, plugin_name,
+ "Failed to retreive backend--NOT activating it's acis\n");
+ return;
+ }
+
+ aclinit_search_and_update_aci ( 1, /* thisbeonly */
+ sdn, /* base */
+ be_name,/* be name */
+ LDAP_SCOPE_SUBTREE,
+ ACL_REMOVE_ACIS,
+ DO_TAKE_ACLCACHE_WRITELOCK);
+
+ } else if ( old_state != SLAPI_BE_STATE_ON && new_state == SLAPI_BE_STATE_ON) {
+ slapi_log_error ( SLAPI_LOG_ACL, plugin_name,
+ "Backend %s is now STARTED--activating it's acis\n", be_name);
+
+ if ( (be = slapi_be_select_by_instance_name( be_name )) == NULL) {
+ slapi_log_error ( SLAPI_LOG_ACL, plugin_name,
+ "Failed to retreive backend--NOT activating it's acis\n");
+ return;
+ }
+
+ /*
+ * In fact there can onlt be one sufffix here.
+ */
+
+ if ( (sdn = slapi_be_getsuffix( be, 0)) == NULL ) {
+ slapi_log_error ( SLAPI_LOG_ACL, plugin_name,
+ "Failed to retreive backend--NOT activating it's acis\n");
+ return;
+ }
+
+ aclinit_search_and_update_aci ( 1, /* thisbeonly */
+ sdn,
+ be_name, /* be name */
+ LDAP_SCOPE_SUBTREE,
+ ACL_ADD_ACIS,
+ DO_TAKE_ACLCACHE_WRITELOCK);
+ }
+
+}
+
+/* This routine must be called with the acicache write lock taken */
+int
+acllist_insert_aci_needsLock( const Slapi_DN *e_sdn, const struct berval* aci_attr)
+{
+
+ aci_t *aci;
+ char *acl_str;
+ int rv =0;
+
+ if (aci_attr->bv_len <= 0)
+ return 0;
+
+ aci = acllist_get_aci_new ();
+ slapi_sdn_set_ndn_byval ( aci->aci_sdn, slapi_sdn_get_ndn ( e_sdn ) );
+
+ acl_str = slapi_ch_strdup(aci_attr->bv_val);
+ /* Parse the ACL TEXT */
+ if ( 0 != (rv = acl_parse ( acl_str, aci )) ) {
+ slapi_log_error (SLAPI_LOG_FATAL, plugin_name,
+ "ACL PARSE ERR(rv=%d): %s\n", rv, acl_str );
+ slapi_ch_free ( (void **) &acl_str );
+ acllist_free_aci ( aci );
+
+ return 1;
+ }
+
+ /* Now add it to the list */
+ if ( 0 != (rv =__acllist_add_aci ( aci ))) {
+ slapi_log_error (SLAPI_LOG_ACL, plugin_name,
+ "ACL ADD ACI ERR(rv=%d): %s\n", rv, acl_str );
+ slapi_ch_free ( (void **) &acl_str );
+ acllist_free_aci ( aci );
+ return 1;
+ }
+
+ slapi_ch_free ( (void **) &acl_str );
+ acl_regen_aclsignature ();
+ if ( aci->aci_elevel == ACI_ELEVEL_USERDN_ANYONE)
+ aclanom_invalidateProfile ();
+ return 0;
+}
+
+/* This routine must be called with the acicache write lock taken */
+static int
+__acllist_add_aci ( aci_t *aci )
+{
+
+ int rv = 0; /* OK */
+ AciContainer *aciListHead;
+ AciContainer *head;
+ PRUint32 i;
+
+ aciListHead = acllist_get_aciContainer_new ( );
+ slapi_sdn_set_ndn_byval ( aciListHead->acic_sdn, slapi_sdn_get_ndn ( aci->aci_sdn ) );
+
+ /* insert the aci */
+ switch (avl_insert ( &acllistRoot, aciListHead, __acllist_aciContainer_node_cmp,
+ __acllist_aciContainer_node_dup ) ) {
+
+ case 1: /* duplicate ACL on the same entry */
+
+ /* Find the node that contains the acl. */
+ if ( NULL == (head = (AciContainer *) avl_find( acllistRoot, aciListHead,
+ (IFP) __acllist_aciContainer_node_cmp ) ) ) {
+ slapi_log_error ( SLAPI_PLUGIN_ACL, plugin_name,
+ "Can't insert the acl in the tree\n");
+ rv = 1;
+ } else {
+ aci_t *t_aci;;
+
+ /* Attach the list */
+ t_aci = head->acic_list;;
+ while ( t_aci && t_aci->aci_next )
+ t_aci = t_aci->aci_next;
+
+ /* Now add the new one to the end of the list */
+ t_aci->aci_next = aci;
+ }
+ slapi_log_error ( SLAPI_LOG_ACL, plugin_name, "Added the ACL:%s to existing container:[%d]%s\n",
+ aci->aclName, head->acic_index, slapi_sdn_get_ndn( head->acic_sdn ));
+
+ /* now free the tmp container */
+ aciListHead->acic_list = NULL;
+ __acllist_free_aciContainer ( &aciListHead );
+
+ break;
+ default:
+ /* The container is inserted. Now hook up the aci and setup the
+ * container index. Donot free the "aciListHead" here.
+ */
+ aciListHead->acic_list = aci;
+
+ /*
+ * First, see if we have an open slot or not - -if we have reuse it
+ */
+ i = 0;
+ while ( (i < currContainerIndex) && aciContainerArray[i] )
+ i++;
+
+ if ( currContainerIndex >= (maxContainerIndex - 2)) {
+ maxContainerIndex += CONTAINER_INCR;
+ aciContainerArray = (AciContainer **) slapi_ch_realloc ( (char *) aciContainerArray,
+ maxContainerIndex * sizeof ( AciContainer * ) );
+ }
+ aciListHead->acic_index = i;
+ /* If i < currContainerIndex, we are just re-using an old slot. */
+ /* We don't need to increase currContainerIndex if we just re-use an old one. */
+ if (i == currContainerIndex)
+ currContainerIndex++;
+
+ aciContainerArray[ aciListHead->acic_index ] = aciListHead;
+
+ slapi_log_error ( SLAPI_LOG_ACL, plugin_name, "Added %s to container:%d\n",
+ slapi_sdn_get_ndn( aciListHead->acic_sdn ), aciListHead->acic_index );
+ break;
+ }
+
+ return rv;
+}
+
+
+
+static int
+__acllist_aciContainer_node_cmp ( caddr_t d1, caddr_t d2 )
+{
+
+ int rc =0;
+ AciContainer *c1 = (AciContainer *) d1;
+ AciContainer *c2 = (AciContainer *) d2;
+
+
+ rc = slapi_sdn_compare ( c1->acic_sdn, c2->acic_sdn );
+ return rc;
+}
+
+static int
+__acllist_aciContainer_node_dup ( caddr_t d1, caddr_t d2 )
+{
+
+ /* we allow duplicates -- they are not exactly duplicates
+ ** but multiple aci value on the same node
+ */
+ return 1;
+
+}
+
+
+/*
+ * Remove the ACL
+ *
+ * This routine must be called with the aclcache write lock taken.
+ * It takes in addition the one for the anom profile taken in
+ * aclanom_invalidateProfile().
+ * They _must_ be taken in this order or there
+ * is a deadlock scenario with aclanom_gen_anomProfile() which
+ * also takes them is this order.
+*/
+
+int
+acllist_remove_aci_needsLock( const Slapi_DN *sdn, const struct berval *attr )
+{
+
+ aci_t *head, *next;
+ int rv = 0;
+ AciContainer *aciListHead, *root;
+ AciContainer *dContainer;
+ int removed_anom_acl = 0;
+
+ /* we used to delete the ACL by value but we don't do that anymore.
+ * rather we delete all the acls in that entry and then repopulate it if
+ * there are any more acls.
+ */
+
+ aciListHead = acllist_get_aciContainer_new ( );
+ slapi_sdn_set_ndn_byval ( aciListHead->acic_sdn, slapi_sdn_get_ndn ( sdn ) );
+
+ /* now find it */
+ if ( NULL == (root = (AciContainer *) avl_find( acllistRoot, aciListHead,
+ (IFP) __acllist_aciContainer_node_cmp ))) {
+ /* In that case we don't have any acl for this entry. cool !!! */
+
+ __acllist_free_aciContainer ( &aciListHead );
+ slapi_log_error ( SLAPI_LOG_ACL, plugin_name,
+ "No acis to remove in this entry\n" );
+ return 0;
+ }
+
+ head = root->acic_list;
+ if ( head)
+ next = head->aci_next;
+ while ( head ) {
+ if ( head->aci_elevel == ACI_ELEVEL_USERDN_ANYONE)
+ removed_anom_acl = 1;
+
+ /* Free the acl */
+ acllist_free_aci ( head );
+
+ head = next;
+ next = NULL;
+ if ( head && head->aci_next )
+ next = head->aci_next;
+ }
+ root->acic_list = NULL;
+
+ /* remove the container from the slot */
+ aciContainerArray[root->acic_index] = NULL;
+
+ slapi_log_error ( SLAPI_LOG_ACL, plugin_name,
+ "Removing container[%d]=%s\n", root->acic_index,
+ slapi_sdn_get_ndn ( root->acic_sdn) );
+ dContainer = (AciContainer *) avl_delete ( &acllistRoot, aciListHead,
+ __acllist_aciContainer_node_cmp );
+ __acllist_free_aciContainer ( &dContainer );
+
+ acl_regen_aclsignature ();
+ if ( removed_anom_acl )
+ aclanom_invalidateProfile ();
+
+ /*
+ * Now read back the entry and repopulate ACLs for that entry, but
+ * only if a specific aci was deleted, otherwise, we do a
+ * "When Harry met Sally" and nail 'em all.
+ */
+
+ if ( attr != NULL) {
+
+ if (0 != (rv = aclinit_search_and_update_aci ( 0, /* thisbeonly */
+ sdn, /* base */
+ NULL, /* be name */
+ LDAP_SCOPE_BASE,
+ ACL_ADD_ACIS,
+ DONT_TAKE_ACLCACHE_WRITELOCK))) {
+ slapi_log_error ( SLAPI_LOG_FATAL, plugin_name,
+ " Can't add the rest of the acls for entry:%s after delete\n",
+ slapi_sdn_get_dn ( sdn ) );
+ }
+ }
+
+ /* Now free the tmp container we used */
+ __acllist_free_aciContainer ( &aciListHead );
+
+ /*
+ * regenerate the anonymous profile if we have deleted
+ * anyone acls.
+ * We don't need the aclcache readlock because the context of
+ * this routine is we have the write lock already.
+ */
+ if ( removed_anom_acl )
+ aclanom_gen_anomProfile(DONT_TAKE_ACLCACHE_READLOCK);
+
+ return rv;
+}
+
+AciContainer *
+acllist_get_aciContainer_new ( )
+{
+
+ AciContainer *head;
+
+ head = (AciContainer * ) slapi_ch_calloc ( 1, sizeof ( AciContainer ) );
+ head->acic_sdn = slapi_sdn_new ( );
+ head->acic_index = -1;
+
+ return head;
+}
+static void
+__acllist_free_aciContainer ( AciContainer **container)
+{
+
+ PR_ASSERT ( container != NULL );
+
+ if ( (*container)->acic_index >= 0 )
+ aciContainerArray[ (*container)->acic_index] = NULL;
+ if ( (*container)->acic_sdn )
+ slapi_sdn_free ( &(*container)->acic_sdn );
+ slapi_ch_free ( (void **) container );
+
+}
+
+void
+acllist_done_aciContainer ( AciContainer *head )
+{
+
+ PR_ASSERT ( head != NULL );
+
+ slapi_sdn_done ( head->acic_sdn );
+ head->acic_index = -1;
+
+ /* The caller is responsible for taking care of list */
+ head->acic_list = NULL;
+}
+
+
+aci_t *
+acllist_get_aci_new ()
+{
+ aci_t *aci_item;
+
+ aci_item = (aci_t *) slapi_ch_calloc (1, sizeof (aci_t));
+ aci_item->aci_sdn = slapi_sdn_new ();
+ aci_item->aci_index = curAciIndex++;
+ aci_item->aci_elevel = ACI_DEFAULT_ELEVEL; /* by default it's a complex */
+ aci_item->targetAttr = (Targetattr **) slapi_ch_calloc (
+ ACL_INIT_ATTR_ARRAY,
+ sizeof (Targetattr *));
+ return aci_item;
+}
+
+void
+acllist_free_aci(aci_t *item)
+{
+
+ Targetattr **attrArray;
+
+ /* The caller is responsible for taking
+ ** care of list issue
+ */
+ if (item == NULL) return;
+
+ slapi_sdn_free ( &item->aci_sdn );
+ slapi_filter_free (item->target, 1);
+
+ /* slapi_filter_free(item->targetAttr, 1); */
+ attrArray = item->targetAttr;
+ if (attrArray) {
+ int i = 0;
+ Targetattr *attr;
+
+ while (attrArray[i] != NULL) {
+ attr = attrArray[i];
+ if (attr->attr_type & ACL_ATTR_FILTER) {
+ slapi_filter_free(attr->u.attr_filter, 1);
+ } else {
+ slapi_ch_free ( (void **) &attr->u.attr_str );
+ }
+ slapi_ch_free ( (void **) &attr );
+ i++;
+ }
+ /* Now free the array */
+ slapi_ch_free ( (void **) &attrArray );
+ }
+
+ /* Now free any targetattrfilters in this aci item */
+
+ if ( item->targetAttrAddFilters ) {
+ free_targetattrfilters(&item->targetAttrAddFilters);
+ }
+
+ if ( item->targetAttrDelFilters ) {
+ free_targetattrfilters(&item->targetAttrDelFilters);
+ }
+
+ if (item->targetFilterStr) slapi_ch_free ( (void **) &item->targetFilterStr );
+ slapi_filter_free(item->targetFilter, 1);
+
+ /* free the handle */
+ if (item->aci_handle) ACL_ListDestroy(NULL, item->aci_handle);
+
+ /* Free the name */
+ if (item->aclName) slapi_ch_free((void **) &item->aclName);
+
+ /* Free any macro info*/
+ if (item->aci_macro) {
+ slapi_ch_free((void **) &item->aci_macro->match_this);
+ slapi_ch_free((void **) &item->aci_macro);
+ }
+
+ /* free at last -- free at last */
+ slapi_ch_free ( (void **) &item );
+}
+
+static void free_targetattrfilters( Targetattrfilter ***attrFilterArray) {
+
+ if (*attrFilterArray) {
+ int i = 0;
+ Targetattrfilter *attrfilter;
+
+ while ((*attrFilterArray)[i] != NULL) {
+ attrfilter = (*attrFilterArray)[i];
+
+ if ( attrfilter->attr_str != NULL) {
+ slapi_ch_free ( (void **) &attrfilter->attr_str );
+ }
+
+ if (attrfilter->filter != NULL) {
+ slapi_filter_free(attrfilter->filter, 1);
+ }
+
+ if( attrfilter->filterStr != NULL) {
+ slapi_ch_free ( (void **) &attrfilter->filterStr );
+ }
+
+ slapi_ch_free ( (void **) &attrfilter );
+ i++;
+ }
+ /* Now free the array */
+ slapi_ch_free ( (void **) attrFilterArray );
+ }
+
+}
+
+/* SEARCH */
+void
+acllist_init_scan (Slapi_PBlock *pb, int scope, char *base)
+{
+ Acl_PBlock *aclpb;
+ int i;
+ AciContainer *root;
+ char *basedn = NULL;
+ int index;
+
+ if ( acl_skip_access_check ( pb, NULL ) ) {
+ return;
+ }
+
+ /*acllist_print_tree ( acllistRoot, &depth, "top", "top") ; */
+ /* my_print ( acllistRoot );*/
+ /* If we have an anonymous profile and I am an anom dude - let's skip it */
+ if ( aclanom_is_client_anonymous ( pb )) {
+ return;
+ }
+ aclpb = acl_get_aclpb (pb, ACLPB_BINDDN_PBLOCK );
+ if ( !aclpb ) {
+ slapi_log_error ( SLAPI_LOG_FATAL, plugin_name, "Missing aclpb 4 \n" );
+ return;
+ }
+
+ aclpb->aclpb_handles_index[0] = -1;
+
+ /* If base is NULL - it means we are going to go thru all the ACLs
+ * This is needed when we do anonymous profile generation.
+ */
+ if ( NULL == base ) {
+ return;
+ }
+
+ aclpb->aclpb_state |= ACLPB_SEARCH_BASED_ON_LIST ;
+
+ acllist_acicache_READ_LOCK();
+
+ basedn = slapi_ch_strdup (base);
+ index = 0;
+ aclpb->aclpb_search_base = slapi_ch_strdup ( base );
+
+ while (basedn ) {
+ char *tmp = NULL;
+
+ slapi_sdn_set_ndn_byref ( aclpb->aclpb_aclContainer->acic_sdn, basedn );
+
+ root = (AciContainer *) avl_find( acllistRoot,
+ (caddr_t) aclpb->aclpb_aclContainer,
+ (IFP) __acllist_aciContainer_node_cmp);
+ if ( index >= ACLPB_MAX_SELECTED_ACLS -2 ) {
+ aclpb->aclpb_handles_index[0] = -1;
+ slapi_ch_free ( (void **) &basedn);
+ break;
+ } else if ( NULL != root ) {
+ aclpb->aclpb_base_handles_index[index++] = root->acic_index;
+ aclpb->aclpb_base_handles_index[index] = -1;
+ }
+ tmp = slapi_dn_parent ( basedn );
+ slapi_ch_free ( (void **) &basedn);
+ basedn = tmp;
+ }
+
+ acllist_done_aciContainer ( aclpb->aclpb_aclContainer);
+
+ if ( aclpb->aclpb_base_handles_index[0] == -1 )
+ aclpb->aclpb_state &= ~ACLPB_SEARCH_BASED_ON_LIST ;
+
+ acllist_acicache_READ_UNLOCK();
+
+ i = 0;
+ while ( i < ACLPB_MAX_SELECTED_ACLS && aclpb->aclpb_base_handles_index[i] != -1 ) {
+ i++;
+ }
+}
+
+/*
+ * Initialize aclpb_handles_index[] (sentinel -1) to
+ * contain a list of all aci items at and above edn in the DIT tree.
+ * This list will be subsequestly scanned to find applicable aci's for
+ * the given operation.
+*/
+
+void
+acllist_aciscan_update_scan ( Acl_PBlock *aclpb, char *edn )
+{
+
+ int i, index = 0;
+ char *basedn = NULL;
+ AciContainer *root;
+ int is_not_search_base = 1;
+
+
+ /* First copy the containers indx from the base to the one which is
+ * going to be used.
+ * The base handles get done in acllist_init_scan().
+ * This stuff is only used if it's a search operation.
+ */
+ if ( aclpb && aclpb->aclpb_search_base ) {
+ while ( aclpb->aclpb_base_handles_index[index] != -1 &&
+ index < ACLPB_MAX_SELECTED_ACLS -2 ) {
+ aclpb->aclpb_handles_index[index] =
+ aclpb->aclpb_base_handles_index[index];
+ index++;
+ }
+ if ( strcasecmp ( edn, aclpb->aclpb_search_base) == 0) {
+ is_not_search_base = 0;
+ }
+ }
+ aclpb->aclpb_handles_index[index] = -1;
+
+ /*
+ * Here, make a list of all the aci's that will apply
+ * to edn ie. all aci's at and above edn in the DIT tree.
+ *
+ * Do this by walking up edn, looking at corresponding
+ * points in the acllistRoot aci tree.
+ *
+ * If is_not_search_base is true, then we need to iterate on edn, otherwise
+ * we've already got all the base handles above.
+ *
+ */
+
+ if (is_not_search_base) {
+
+ basedn = slapi_ch_strdup ( edn );
+
+ while (basedn ) {
+ char *tmp = NULL;
+
+ slapi_sdn_set_ndn_byref ( aclpb->aclpb_aclContainer->acic_sdn, basedn );
+
+ root = (AciContainer *) avl_find( acllistRoot,
+ (caddr_t) aclpb->aclpb_aclContainer,
+ (IFP) __acllist_aciContainer_node_cmp);
+
+ slapi_log_error ( SLAPI_LOG_ACL, plugin_name,
+ "Searching AVL tree for update:%s: container:%d\n", basedn ,
+ root ? root->acic_index: -1);
+ if ( index >= ACLPB_MAX_SELECTED_ACLS -2 ) {
+ aclpb->aclpb_handles_index[0] = -1;
+ slapi_ch_free ( (void **) &basedn);
+ break;
+ } else if ( NULL != root ) {
+ aclpb->aclpb_handles_index[index++] = root->acic_index;
+ aclpb->aclpb_handles_index[index] = -1;
+ }
+ tmp = slapi_dn_parent ( basedn );
+ slapi_ch_free ( (void **) &basedn);
+ basedn = tmp;
+ if ( aclpb->aclpb_search_base && tmp &&
+ ( 0 == strcasecmp ( tmp, aclpb->aclpb_search_base))) {
+ slapi_ch_free ( (void **) &basedn);
+ tmp = NULL;
+ }
+ } /* while */
+ }
+
+ acllist_done_aciContainer ( aclpb->aclpb_aclContainer );
+ i = 0;
+ while ( i < ACLPB_MAX_SELECTED_ACLS && aclpb->aclpb_handles_index[i] != -1 ) {
+ i++;
+ }
+
+}
+
+aci_t *
+acllist_get_first_aci (Acl_PBlock *aclpb, PRUint32 *cookie )
+{
+
+ int val;
+
+ *cookie = val = 0;
+ if ( aclpb && aclpb->aclpb_handles_index[0] != -1 ) {
+ val = aclpb->aclpb_handles_index[*cookie];
+ }
+ if ( NULL == aciContainerArray[val]) {
+ return ( acllist_get_next_aci ( aclpb, NULL, cookie ) );
+ }
+
+ return (aciContainerArray[val]->acic_list );
+}
+/*
+ * acllist_get_next_aci
+ * Return the next aci in the list
+ *
+ * Inputs
+ * Acl_PBlock *aclpb -- acl Main block; if aclpb= NULL,
+ * -- then we scan thru the whole list.
+ * -- which is used by anom profile code.
+ * aci_t *curaci -- the current aci
+ * PRUint32 *cookie -- cookie -- to maintain a state (what's next)
+ *
+ */
+
+aci_t *
+acllist_get_next_aci ( Acl_PBlock *aclpb, aci_t *curaci, PRUint32 *cookie )
+{
+ PRUint32 val;
+ int scan_entire_list;
+
+ /*
+ Here, if we're passed a curaci and there's another aci in the same node,
+ return that one.
+ */
+
+ if ( curaci && curaci->aci_next )
+ return ( curaci->aci_next );
+
+ /*
+ Determine if we need to scan the entire list of acis.
+ We do if the aclpb==NULL or if the first handle index is -1.
+ That means that we want to go through
+ the entire aciContainerArray up to the currContainerIndex to get
+ acis; the -1 in the first position is a special keyword which tells
+ us that the acis have changed, so we need to go through all of them.
+ */
+
+ scan_entire_list = (aclpb == NULL || aclpb->aclpb_handles_index[0] == -1);
+
+start:
+ (*cookie)++;
+ val = *cookie;
+
+ /* if we are not scanning the entire aciContainerArray list, we only want to
+ look at the indexes specified in the handles index */
+ if ( !scan_entire_list )
+ val = aclpb->aclpb_handles_index[*cookie];
+
+ /* the hard max end */
+ if ( val >= maxContainerIndex)
+ return NULL;
+
+ /* reached the end of the array */
+ if ((!scan_entire_list && (*cookie >= (ACLPB_MAX_SELECTED_ACLS-1))) ||
+ (*cookie >= currContainerIndex)) {
+ return NULL;
+ }
+
+ /* if we're only using the handles list for our aciContainerArray
+ indexes, the -1 value marks the end of that list */
+ if ( !scan_entire_list && (aclpb->aclpb_handles_index[*cookie] == -1) ) {
+ return NULL;
+ }
+
+ /* if we're scanning the entire list, and we hit a null value in the
+ middle of the list, just try the next one; this can happen if
+ an aci was deleted - it can leave "holes" in the array */
+ if ( scan_entire_list && ( NULL == aciContainerArray[val])) {
+ goto start;
+ }
+
+ if ( aciContainerArray[val] )
+ return (aciContainerArray[val]->acic_list );
+ else
+ return NULL;
+}
+
+void
+acllist_acicache_READ_UNLOCK( )
+{
+ ACILIST_UNLOCK_READ ();
+
+}
+
+void
+acllist_acicache_READ_LOCK()
+{
+ /* get a reader lock */
+ ACILIST_LOCK_READ ();
+
+}
+
+void
+acllist_acicache_WRITE_UNLOCK( )
+{
+ ACILIST_UNLOCK_WRITE ();
+
+}
+
+void
+acllist_acicache_WRITE_LOCK( )
+{
+ ACILIST_LOCK_WRITE ();
+
+}
+
+/* This routine must be called with the acicache write lock taken */
+int
+acllist_moddn_aci_needsLock ( Slapi_DN *oldsdn, char *newdn )
+{
+
+
+ AciContainer *aciListHead;
+ AciContainer *head;
+
+ /* first get the container */
+
+ aciListHead = acllist_get_aciContainer_new ( );
+ slapi_sdn_free(&aciListHead->acic_sdn);
+ aciListHead->acic_sdn = oldsdn;
+
+
+ if ( NULL == (head = (AciContainer *) avl_find( acllistRoot, aciListHead,
+ (IFP) __acllist_aciContainer_node_cmp ) ) ) {
+
+ slapi_log_error ( SLAPI_PLUGIN_ACL, plugin_name,
+ "Can't find the acl in the tree for moddn operation:olddn%s\n",
+ slapi_sdn_get_ndn ( oldsdn ));
+ aciListHead->acic_sdn = NULL;
+ __acllist_free_aciContainer ( &aciListHead );
+ return 1;
+ }
+
+
+ /* Now set the new DN */
+ slapi_sdn_done ( head->acic_sdn );
+ slapi_sdn_set_ndn_byval ( head->acic_sdn, newdn );
+
+ aciListHead->acic_sdn = NULL;
+ __acllist_free_aciContainer ( &aciListHead );
+
+ return 0;
+}
+
+void
+acllist_print_tree ( Avlnode *root, int *depth, char *start, char *side)
+{
+
+ AciContainer *aciHeadList;
+
+ if ( NULL == root ) {
+ return;
+ }
+ aciHeadList = (AciContainer *) root->avl_data;
+ slapi_log_error ( SLAPI_LOG_ACL, "plugin_name",
+ "Container[ Depth=%d%s-%s]: %s\n", *depth, start, side,
+ slapi_sdn_get_ndn ( aciHeadList->acic_sdn ) );
+
+ (*depth)++;
+
+ acllist_print_tree ( root->avl_left, depth, side, "L" );
+ acllist_print_tree ( root->avl_right, depth, side, "R" );
+
+ (*depth)--;
+
+}
+
+static
+void
+ravl_print( Avlnode *root, int depth )
+{
+ int i;
+
+ AciContainer *aciHeadList;
+ if ( root == 0 )
+ return;
+
+ ravl_print( root->avl_right, depth+1 );
+
+ for ( i = 0; i < depth; i++ )
+ printf( " " );
+ aciHeadList = (AciContainer *) root->avl_data;
+ printf( "%s\n", slapi_sdn_get_ndn ( aciHeadList->acic_sdn ) );
+
+ ravl_print( root->avl_left, depth+1 );
+}
+
+void
+my_print( Avlnode *root )
+{
+ printf( "********\n" );
+
+ if ( root == 0 )
+ printf( "\tNULL\n" );
+ else
+ ( void ) ravl_print( root, 0 );
+
+ printf( "********\n" );
+}
diff --git a/ldap/servers/plugins/acl/aclparse.c b/ldap/servers/plugins/acl/aclparse.c
new file mode 100644
index 00000000..2247471a
--- /dev/null
+++ b/ldap/servers/plugins/acl/aclparse.c
@@ -0,0 +1,1928 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+#include "acl.h"
+
+/****************************************************************************/
+/* prototypes */
+/****************************************************************************/
+static int __aclp__parse_aci(char *str, aci_t *aci_item);
+static int __aclp__sanity_check_acltxt(aci_t *aci_item, char *str);
+static char * __aclp__normalize_acltxt (aci_t *aci_item, char *str);
+static char * __aclp__getNextLASRule(aci_t *aci_item, char *str,
+ char **endOfCurrRule);
+static char * __aclp__dn_normalize( char *dn , char *end);
+static int __aclp__get_aci_right ( char *str);
+static int __aclp__init_targetattr (aci_t *aci, char *attr_val);
+static int __acl__init_targetattrfilters( aci_t *aci_item, char *str);
+static int process_filter_list( Targetattrfilter ***attrfilterarray,
+ char * str);
+static int __acl_init_targetattrfilter( Targetattrfilter *attrfilter, char *str );
+static void __aclp_chk_paramRules ( aci_t *aci_item, char *start,
+ char *end);
+static void __acl_strip_trailing_space( char *str);
+static void __acl_strip_leading_space( char **str);
+static char * __acl_trim_filterstr( char * str );
+static int acl_verify_exactly_one_attribute( char *attr_name, Slapi_Filter *f);
+static int type_compare( Slapi_Filter *f, void *arg);
+static int acl_check_for_target_macro( aci_t *aci_item, char *value);
+static int get_acl_rights_as_int( char * strValue);
+
+/***************************************************************************
+*
+* acl_parse
+*
+* Parses the input string and copies the information into the
+* correct place in the aci.
+*
+*
+* Input:
+* char *str - Input string which has the ACL
+* This is a duped copy, so here we have
+* the right to stich '\0' characters into str for
+* processing purposes. If you want to keep
+* a piece of str, you'll need to dup it
+* as it gets freed outside the scope of acl_parse.
+* aci_t *item - the aci item where the ACL info will be
+* - stored.
+*
+* Returns:
+* 0 -- Parsed okay
+* < 0 -- error codes
+*
+* Error Handling:
+* None.
+*
+**************************************************************************/
+int
+acl_parse(char * str, aci_t *aci_item)
+{
+
+ int rv=0;
+ char *next;
+ char *save;
+
+ while(*str) {
+ __acl_strip_leading_space( &str );
+ if (*str == '\0') break;
+
+ if (*str == '(') {
+ if ((next = slapi_find_matching_paren(str)) == NULL) {
+ return(ACL_SYNTAX_ERR);
+ }
+ } else {
+ /* then we have done all the processing */
+ return 0;
+ }
+ LDAP_UTF8INC(str); /* skip the "(" */
+ save = next;
+ LDAP_UTF8INC(next);
+ *save = '\0';
+
+ /* Now we have a "str)" */
+ if ( 0 != (rv = __aclp__parse_aci(str, aci_item))) {
+ return(rv);
+ }
+
+ /* Move to the next */
+ str = next;
+ }
+
+ /* check if have a ACLTXT or not */
+ if (!(aci_item->aci_type & ACI_ACLTXT))
+ return ACL_SYNTAX_ERR;
+
+ if (aci_item->target) {
+ Slapi_Filter *f;
+
+ /* Make sure that the target is a valid target.
+ ** Example: ACL is located in
+ ** "ou=engineering, o=ace industry, c=us
+ ** but if the target is "o=ace industry, c=us",
+ ** then it's an ERROR.
+ */
+ f = aci_item->target;
+ if (aci_item->aci_type & ACI_TARGET_DN) {
+ char *avaType;
+ struct berval *avaValue;
+ const char *dn;
+
+ dn = slapi_sdn_get_ndn ( aci_item->aci_sdn );
+ slapi_filter_get_ava ( f, &avaType, &avaValue );
+
+ if (!slapi_dn_issuffix( avaValue->bv_val, dn))
+ return ACL_INVALID_TARGET;
+ }
+ }
+
+ /*
+ ** We need to keep the taregetFilterStr for anyone ACL only.
+ ** same for targetValueFilterStr.
+ ** We need to keep it for macros too as it needs to be expnaded at eval time.
+ **
+ */
+ if ( (aci_item->aci_elevel != ACI_ELEVEL_USERDN_ANYONE) &&
+ !(aci_item->aci_type & ACI_TARGET_MACRO_DN) ) {
+ slapi_ch_free ( (void **) & aci_item->targetFilterStr );
+ }
+
+ /*
+ * If we parsed the aci and there was a ($dn) on the user side
+ * but none in hte taget then that's an error as the user side
+ * value is derived from the target side value.
+ */
+
+ if (!(aci_item->aci_type & ACI_TARGET_MACRO_DN) &&
+ (aci_item->aci_ruleType & ACI_PARAM_DNRULE)) {
+ slapi_log_error(SLAPI_LOG_FATAL, plugin_name,
+ "acl_parse: A macro in a subject ($dn) must have a macro in the target.\n");
+ return(ACL_INVALID_TARGET);
+ }
+
+ return 0;
+}
+
+/***************************************************************************
+*
+* __aclp__parse_aci
+*
+* Parses Each individual subset of information/
+*
+* Input:
+* char *str - Input string which has the ACL like "str)"
+* aci_t *item - the aci item where the ACL info will be
+* - stored.
+*
+* Returns:
+* 0 -- Parsed okay
+* < 0 -- error codes
+*
+* Error Handling:
+* None.
+*
+**************************************************************************/
+static int
+__aclp__parse_aci (char *str, aci_t *aci_item)
+{
+
+ int len;
+ int rv;
+ int type;
+ char *tmpstr;
+ char *s = NULL;
+ char *value = NULL;
+ Slapi_Filter *f = NULL;
+ int targetattrlen = strlen(aci_targetattr);
+ int targetdnlen = strlen (aci_targetdn);
+ int tfilterlen = strlen(aci_targetfilter);
+ int targetattrfilterslen = strlen(aci_targetattrfilters);
+
+ __acl_strip_leading_space( &str );
+
+ if (*str == '\0') {
+ return(ACL_SYNTAX_ERR);
+ }
+
+ /* The first letter should tell us something */
+ switch(*str) {
+ case 'v':
+ type = ACI_ACLTXT;
+
+ if ( 0 != (rv= __aclp__sanity_check_acltxt(aci_item, str ) ) ) {
+
+ return rv;
+ }
+ break;
+
+ case 't':
+ if (strncmp(str, aci_targetattrfilters,targetattrfilterslen ) == 0) {
+ type = ACI_TARGET_ATTR;
+
+
+ /*
+ * The targetattrfilters bit looks like this:
+ * (targetattrfilters="add= attr1:F1 && attr2:F2 ... && attrn:Fn,
+ * del= attr1:F1 && attr2:F2... && attrn:Fn")
+ */
+ if ( 0 != (rv= __acl__init_targetattrfilters(
+ aci_item, str))) {
+ return rv;
+ }
+ } else if (strncmp(str, aci_targetattr,targetattrlen ) == 0) {
+ type = ACI_TARGET_ATTR;
+
+ if ( (s = strstr( str, "!=" )) != NULL ) {
+ type |= ACI_TARGET_ATTR_NOT;
+ strncpy(s, " ", 1);
+ }
+ /* Get individual components of the targetattr.
+ * (targetattr = "cn || u* || phone ||tel:add:(tel=1234)
+ * || sn:del:(gn=5678)")
+ * If it contains a value filter, the type will also be
+ * ACI_TARGET_VALUE_ATTR.
+ */
+ if ( 0 != (rv= __aclp__init_targetattr(
+ aci_item, str))) {
+ return rv;
+ }
+ } else if (strncmp(str, aci_targetfilter,tfilterlen ) == 0) {
+ if ( aci_item->targetFilter)
+ return ACL_SYNTAX_ERR;
+
+ type = ACI_TARGET_FILTER;
+ /* we need to remove the targetfilter stuff*/
+ if ( (s = strstr( str, "!=" )) != NULL ) {
+ type |= ACI_TARGET_FILTER_NOT;
+ }
+
+ /*
+ * If it's got a macro in the targetfilter then it must
+ * have a target and it must have a macro.
+ */
+
+ if ((s = strstr (str, ACL_RULE_MACRO_DN_KEY)) != NULL ||
+ ((s = strstr(str, ACL_RULE_MACRO_DN_LEVELS_KEY)) != NULL)) {
+
+ /* Must have a targetmacro */
+ if ( !(aci_item->aci_type & ACI_TARGET_MACRO_DN)) {
+ slapi_log_error(SLAPI_LOG_FATAL, plugin_name,
+ "acl_parse: A macro in a targetfilter ($dn) must have a macro in the target.\n");
+ return(ACL_SYNTAX_ERR);
+ }
+
+ type|= ACI_TARGET_FILTER_MACRO_DN;
+ }
+
+ tmpstr = strchr(str, '=');
+ tmpstr++;
+ __acl_strip_leading_space(&tmpstr);
+
+ /*
+ * Trim off enclosing quotes and enclosing
+ * superfluous brackets.
+ * The result has been duped so it can be kept.
+ */
+
+ tmpstr = __acl_trim_filterstr( tmpstr );
+
+ f = slapi_str2filter(tmpstr);
+
+ /* save the filter string */
+ aci_item->targetFilterStr = tmpstr;
+
+ } else if (strncmp(str, aci_targetdn, targetdnlen) == 0) {
+ char *tstr = NULL;
+ const size_t LDAP_URL_prefix_len = strlen (LDAP_URL_prefix);
+ char *tt;
+ type = ACI_TARGET_DN;
+ /* Keep a copy of the target attr */
+ if (aci_item->target) {
+ return (ACL_SYNTAX_ERR);
+ }
+ if ( (s = strstr( str, "!=" )) != NULL ) {
+ type |= ACI_TARGET_NOT;
+ strncpy(s, " ", 1);
+ }
+
+ /* Convert it to lower as slapi_dn_normalize() does not */
+ for (tt = str; *tt; tt++) *tt = TOLOWER ( *tt );
+
+ if ( (s = strchr( str, '=' )) != NULL ) {
+ value = s + 1;
+ slapi_dn_normalize(value);
+ len = strlen ( value );
+ if (*value == '"' && value[len-1] == '"'){
+ value[len-1] = '\0';
+ value++;
+ }
+ __acl_strip_leading_space(&value);
+ } else {
+ return ( ACL_SYNTAX_ERR );
+ }
+
+ if ( strncasecmp ( value, LDAP_URL_prefix , LDAP_URL_prefix_len) )
+ return ( ACL_SYNTAX_ERR );
+
+ value += LDAP_URL_prefix_len;
+ len = strlen ( value );
+ tstr = (char *) slapi_ch_malloc ( targetdnlen + len + 4 );
+ sprintf ( tstr, "(target=%s)", value);
+ if ( (rv = acl_check_for_target_macro( aci_item, value)) == -1) {
+ slapi_ch_free ( (void **) &tstr );
+ return(ACL_SYNTAX_ERR);
+ } else if ( rv > 0) {
+ /* is present, so the type is now ACL_TARGET_MACRO_DN */
+ type = ACI_TARGET_MACRO_DN;
+ } else {
+ /* it's a normal target with no macros inside */
+ f = slapi_str2filter ( tstr );
+ }
+ slapi_ch_free ( (void **) &tstr );
+ } else {
+ /* did start with a 't' but was not a recognsied keyword */
+ return(ACL_SYNTAX_ERR);
+ }
+
+ /*
+ * Here, it was a recognised keyword that started with 't'.
+ * Check that the filter associated with ACI_TARGET_DN and
+ * ACI_TARGET_FILTER are OK.
+ */
+ if (f == NULL) {
+ /* The following types require a filter to have been created */
+ if (type & ACI_TARGET_DN)
+ return ACL_TARGET_FILTER_ERR;
+ else if (type & ACI_TARGET_FILTER)
+ return ACL_TARGETFILTER_ERR;
+ } else {
+ int filterChoice;
+
+ filterChoice = slapi_filter_get_choice ( f );
+ if ( (type & ACI_TARGET_DN) &&
+ ( filterChoice == LDAP_FILTER_PRESENT)) {
+ slapi_log_error(SLAPI_LOG_ACL, plugin_name,
+ "acl__parse_aci: Unsupported filter type:%d\n", filterChoice);
+ return(ACL_SYNTAX_ERR);
+ } else if (( filterChoice == LDAP_FILTER_SUBSTRINGS) &&
+ (type & ACI_TARGET_DN)) {
+ type &= ~ACI_TARGET_DN;
+ type |= ACI_TARGET_PATTERN;
+ }
+ }
+
+ if ((type & ACI_TARGET_DN) ||
+ (type & ACI_TARGET_PATTERN)) {
+ if (aci_item->target) {
+ /* There is something already. ERROR */
+ slapi_log_error(SLAPI_LOG_ACL, plugin_name,
+ "Multiple targets in the ACL syntax\n",
+ 0,0,0);
+ slapi_filter_free(f, 1);
+ return(ACL_SYNTAX_ERR);
+ } else {
+ aci_item->target = f;
+ }
+ } else if ( type & ACI_TARGET_FILTER) {
+ if (aci_item->targetFilter) {
+ slapi_log_error(SLAPI_LOG_ACL, plugin_name,
+ "Multiple target Filters in the ACL Syntax\n",
+ 0,0,0);
+ slapi_filter_free(f, 1);
+ return(ACL_SYNTAX_ERR);
+ } else {
+ aci_item->targetFilter = f;
+ }
+ }
+ break; /* 't' */
+ default:
+ /* Here the keyword did not start with 'v' ot 't' so error */
+ slapi_log_error(SLAPI_LOG_ACL, plugin_name,
+ "Unknown keyword at \"%s\"\n Expecting"
+ " \"target\", \"targetattr\", \"targetfilter\", \"targattrfilters\""
+ " or \"version\"\n", str, 0, 0);
+ return(ACL_SYNTAX_ERR);
+ }/* switch() */
+
+ /* Store the type info */
+ aci_item->aci_type |= type;
+
+ return 0;
+}
+
+/***************************************************************************
+* acl__sanity_check_acltxt
+*
+* Check the input ACL text. Reports any errors. Also forgivs if certain
+* things are missing.
+*
+* Input:
+* char *str - String containg the acl text
+* int *err - error status
+*
+* Returns:
+* 0 --- good status
+* <0 --- error
+*
+* Error Handling:
+* None.
+*
+*
+**************************************************************************/
+static int
+__aclp__sanity_check_acltxt (aci_t *aci_item, char *str)
+{
+ NSErr_t errp;
+ char *s;
+ ACLListHandle_t *handle = NULL;
+ char *newstr = NULL;
+ char *word;
+ char *next;
+
+ memset (&errp, 0, sizeof(NSErr_t));
+ newstr = str;
+
+ while ((s = strstr(newstr, "authenticate")) != NULL) {
+ char *next;
+ next = s + 12;
+ s--;
+ while (s != str && ldap_utf8isspace(s)) LDAP_UTF8DEC(s);
+ if (s && *s == ';') {
+ /* We don't support authenticate stuff */
+ return ACL_INVALID_AUTHORIZATION;
+
+ } else {
+ newstr = next;
+ }
+ }
+
+ newstr = slapi_ch_strdup (str);
+ word = ldap_utf8strtok_r(newstr, " ", &next);
+ if (strcasecmp (word, "version") == 0) {
+ word = ldap_utf8strtok_r(NULL, " ", &next);
+ if (atoi(word) != 3) {
+ slapi_ch_free ( (void **) &newstr );
+ return ACL_INCORRECT_ACI_VERSION;
+ }
+ }
+ slapi_ch_free ( (void **) &newstr );
+
+ /* We need to normalize the DNs in the userdn and group dn
+ ** so that, it's only done once.
+ */
+ if ((newstr = __aclp__normalize_acltxt (aci_item, str )) == NULL) {
+ return ACL_SYNTAX_ERR;
+ }
+ slapi_log_error(SLAPI_LOG_ACL, plugin_name, "Normalized String:%s\n", newstr, 0,0);
+
+ /* check for acl syntax error */
+ if ((handle = (ACLListHandle_t *) ACL_ParseString(&errp,
+ newstr)) == NULL) {
+ acl_print_acllib_err(&errp, str);
+ slapi_ch_free ( (void **) &newstr );
+ return ACL_SYNTAX_ERR;
+ } else {
+ /* get the rights and the aci type */
+ aci_item->aci_handle = handle;
+ nserrDispose(&errp);
+ slapi_ch_free ( (void **) &newstr );
+
+ return 0;
+ }
+}
+/******************************************************************************
+*
+* acl__normalize_acltxt
+*
+*
+* XXXrbyrne this routine should be re-written when someone eventually
+* gets sick enough of it. Same for getNextLAS() below.
+*
+* Normalize the acltxt i.e normalize all the DNs specified in the
+* Userdn and Groupdn rule so that we normalize once here and not
+* over and over again at the runtime in the LASes. We have to normalize
+* before we generate the handle otherwise it's of no use.
+* Also convert deny to deny absolute
+*
+* The string that comes in is something like:
+* version 3.0; acl "Dept domain administration"; allow (all)
+* groupdn = "ldap:///cn=Domain Administrators, o=$dn.o, o=ISP"; )
+*
+* Returns NULL on error.
+*
+******************************************************************************/
+static char *
+__aclp__normalize_acltxt ( aci_t * aci_item, char * str )
+{
+
+ char *s, *p;
+ char *end;
+ char *aclstr, *s_aclstr;
+ char *ret_str = NULL;
+ int len;
+ char *ptr, *aclName;
+ char *nextACE;
+ char *tmp_str = NULL;
+ char *acestr = NULL;
+ char *s_acestr = NULL;
+ int aci_rights_val = 0; /* bug 389975 */
+
+ /* make a copy first */
+ s_aclstr = aclstr = slapi_ch_strdup ( str );
+
+ /* The rules are like this version 3.0; acl "xyz"; rule1; rule2; */
+ s = strchr (aclstr, ';');
+ if ( NULL == s) {
+ slapi_ch_free ( (void **) &s_aclstr );
+ return NULL;
+ }
+ aclstr = ++s;
+
+ /* From DS 4.0, we support both aci (or aci) "name" -- we have to change to acl
+ ** as libaccess will not like it
+ */
+ s = aclstr;
+ while (s && ldap_utf8isspace(s)) LDAP_UTF8INC(s);
+ *(s+2 ) = 'l';
+
+ aclName = s+3;
+
+ s = strchr (aclstr, ';');
+ if ( NULL == s) {
+ slapi_ch_free ( (void **) &s_aclstr );
+ return NULL;
+ }
+
+ aclstr = s;
+ LDAP_UTF8INC(aclstr);
+ *s = '\0';
+
+ /* Here aclName is the acl description string */
+ aci_item->aclName = slapi_ch_strdup ( aclName );
+
+ aclutil_str_appened (&ret_str, s_aclstr);
+ aclutil_str_appened (&ret_str, ";");
+
+ /* start with the string */
+ acestr = aclstr;
+
+ /*
+ * Here acestr is something like:
+ *
+ * " allow (all) groupdn = "ldap:///cn=Domain Administrators, o=$dn.o, o=ISP";)"
+ *
+ *
+ */
+
+normalize_nextACERule:
+
+ /* now we are in the rule part */
+ tmp_str = acestr;
+ s = strchr (tmp_str, ';');
+ if ( s == NULL) {
+ if (ret_str) slapi_ch_free ( (void **) &ret_str );
+ slapi_ch_free ( (void **) &s_aclstr );
+ return NULL;
+ }
+ nextACE = s;
+ LDAP_UTF8INC(nextACE);
+ *s = '\0';
+
+ /* acestr now will hold copy of the ACE. Also add
+ ** some more space in case we need to add "absolute"
+ ** for deny rule. We will never need more 2 times
+ ** the len.
+ */
+ len = strlen (tmp_str);
+ s_acestr = acestr = slapi_ch_calloc ( 1, 2 * len);
+
+ __acl_strip_leading_space(&tmp_str);
+
+ /*
+ * Now it's something like:
+ * allow (all) groupdn = "ldap:///cn=Domain Administrators, o=$dn.o, o=ISP";
+ */
+ if (strncasecmp(tmp_str, "allow", 5) == 0) {
+ memcpy(acestr, tmp_str, len);
+ tmp_str += 5;
+ /* gather the rights */
+ aci_rights_val = __aclp__get_aci_right (tmp_str);/* bug 389975 */
+ aci_item->aci_type |= ACI_HAS_ALLOW_RULE;
+
+ } else if (strncasecmp(tmp_str, "deny", 4) == 0) {
+ char *d_rule ="deny absolute";
+ /* Then we have to add "absolute" to the deny rule
+ ** What we are doing here is to tackle this situation.
+ **
+ ** allow -- deny -- allow
+ ** deny -- allow
+ **
+ ** by using deny absolute we force the precedence rule
+ ** i.e deny has a precedence over allow. Since there doesn't
+ ** seem to be an easy to detect the mix, forcing this
+ ** to all the deny rules will do the job.
+ */
+ __acl_strip_leading_space(&tmp_str);
+ tmp_str += 4;
+
+ /* We might have an absolute there already */
+ if ((s = strstr (tmp_str, "absolute")) != NULL) {
+ tmp_str = s;
+ tmp_str += 8;
+ }
+ /* gather the rights */
+ aci_rights_val = __aclp__get_aci_right (tmp_str);/* bug 389975 */
+ aci_item->aci_type |= ACI_HAS_DENY_RULE;
+
+ len = strlen ( d_rule );
+ memcpy (acestr, d_rule, len );
+ memcpy (acestr+len, tmp_str, strlen (tmp_str) );
+ } else {
+ /* wrong syntax */
+ aci_rights_val = -1 ;
+ }
+ if (aci_rights_val == -1 )
+ {
+ /* wrong syntax */
+ slapi_ch_free ( (void **) &ret_str );
+ slapi_ch_free ( (void **) &s_acestr );
+ slapi_ch_free ( (void **) &s_aclstr );
+ return NULL;
+ } else
+ aci_item->aci_access |= aci_rights_val;
+
+
+ /* Normalize all the DNs in the userdn rule */
+
+ /*
+ *
+ * Here acestr starts like this:
+ * " allow (all) groupdn = "ldap:///cn=Domain Administrators, o=$dn.o, o=ISP"
+ */
+
+ s = __aclp__getNextLASRule(aci_item, acestr, &end);
+ while ( s ) {
+ if ( 0 == strncmp ( s, DS_LAS_USERDNATTR, 10) ||
+ ( 0 == strncmp ( s, DS_LAS_USERATTR, 8))) {
+ /*
+ ** For userdnattr/userattr rule, the resources changes and hence
+ ** we cannot cache the result. See above for more comments.
+ */
+ aci_item->aci_elevel = ACI_ELEVEL_USERDNATTR;
+ } else if ( 0== strncmp ( s, DS_LAS_USERDN, 6)) {
+ p = strstr ( s, "=");
+ p--;
+ if ( strncmp (p, "!=", 2) == 0)
+ aci_item->aci_type |= ACI_CONTAIN_NOT_USERDN;
+
+ /* XXXrbyrne
+ * Here we need to scan for more ldap:/// within
+ * this userdn rule type:
+ * eg. userdn = "ldap:///cn=joe,o=sun.com || ldap:///self"
+ * This is handled correctly in DS_LASUserDnEval
+ * but the bug here is not setting ACI_USERDN_SELFRULE
+ * which would ensure that acl info is not cached from
+ * one resource entry to the next. (bug 558519)
+ */
+ p = strstr ( p, "ldap");
+ if (p == NULL) {
+ /* must start with ldap */
+ if (s_acestr) slapi_ch_free ( (void **) &s_acestr );
+ if (ret_str) slapi_ch_free ( (void **) &ret_str );
+ slapi_ch_free ( (void **) &s_aclstr );
+ return (NULL);
+ }
+ p += 8; /* for ldap:/// */
+ if( __aclp__dn_normalize (p, end) == NULL) {
+ if (s_acestr) slapi_ch_free ( (void **) &s_acestr );
+ if (ret_str) slapi_ch_free ( (void **) &ret_str );
+ slapi_ch_free ( (void **) &s_aclstr );
+ return (NULL);
+ }
+
+ /* we have a rule like userdn = "ldap:///blah". s points to blah now.
+ ** let's find if we have a SELF rule like userdn = "ldap:///self".
+ ** Since the resource changes on entry basis, we can't cache the
+ ** evalation of handle for all time. The cache result is valid
+ ** within the evaluation of that resource.
+ */
+ if (strncasecmp(p, "self", 4) == 0) {
+ aci_item->aci_ruleType |= ACI_USERDN_SELFRULE;
+ } else if ( strncasecmp(p, "anyone", 6) == 0 ) {
+ aci_item->aci_elevel = ACI_ELEVEL_USERDN_ANYONE;
+
+ } else if ( strncasecmp(p, "all", 3) == 0 ) {
+ if ( aci_item->aci_elevel > ACI_ELEVEL_USERDN_ALL )
+ aci_item->aci_elevel = ACI_ELEVEL_USERDN_ALL;
+
+ } else {
+ if ( aci_item->aci_elevel > ACI_ELEVEL_USERDN )
+ aci_item->aci_elevel = ACI_ELEVEL_USERDN;
+ }
+
+ /* See if we have a parameterized rule */
+ __aclp_chk_paramRules ( aci_item, p, end );
+ } else if ( 0 == strncmp ( s, DS_LAS_GROUPDNATTR, 11)) {
+ /*
+ ** For groupdnattr rule, the resources changes and hence
+ ** we cannot cache the result. See above for more comments.
+ */
+ /* Find out if we have a URL type of rule */
+ if ((p= strstr (s, "ldap")) != NULL) {
+ if ( aci_item->aci_elevel > ACI_ELEVEL_GROUPDNATTR_URL )
+ aci_item->aci_elevel = ACI_ELEVEL_GROUPDNATTR_URL;
+ } else if ( aci_item->aci_elevel > ACI_ELEVEL_GROUPDNATTR ) {
+ aci_item->aci_elevel = ACI_ELEVEL_GROUPDNATTR;
+ }
+ aci_item->aci_ruleType |= ACI_GROUPDNATTR_RULE;
+ } else if ( 0 == strncmp ( s, DS_LAS_GROUPDN, 7)) {
+
+ p = strstr ( s, "=");
+ p--;
+ if ( strncmp (p, "!=", 2) == 0)
+ aci_item->aci_type |= ACI_CONTAIN_NOT_GROUPDN;
+
+ p = strstr ( s, "ldap");
+ if (p == NULL) {
+ /* must start with ldap */
+ if (s_acestr) slapi_ch_free ( (void **) &s_acestr );
+ if (ret_str) slapi_ch_free ( (void **) &ret_str );
+ slapi_ch_free ( (void **) &s_aclstr );
+ return (NULL);
+ }
+ p += 8;
+ if (__aclp__dn_normalize (p, end) == NULL) {
+ if (s_acestr) slapi_ch_free ( (void **) &s_acestr );
+ if (ret_str) slapi_ch_free ( (void **) &ret_str );
+ slapi_ch_free ( (void **) &s_aclstr );
+ return (NULL);
+ }
+ /* check for param rules */
+ __aclp_chk_paramRules ( aci_item, p, end );
+
+ if ( aci_item->aci_elevel > ACI_ELEVEL_GROUPDN )
+ aci_item->aci_elevel = ACI_ELEVEL_GROUPDN;
+ aci_item->aci_ruleType |= ACI_GROUPDN_RULE;
+
+ } else if ( 0 == strncmp ( s, DS_LAS_ROLEDN, 6)) {
+
+ p = strstr ( s, "=");
+ p--;
+ if ( strncmp (p, "!=", 2) == 0)
+ aci_item->aci_type |= ACI_CONTAIN_NOT_ROLEDN;
+
+ p = strstr ( s, "ldap");
+ if (p == NULL) {
+ /* must start with ldap */
+ if (s_acestr) slapi_ch_free ( (void **) &s_acestr );
+ if (ret_str) slapi_ch_free ( (void **) &ret_str );
+ slapi_ch_free ( (void **) &s_aclstr );
+ return (NULL);
+ }
+ p += 8;
+ if (__aclp__dn_normalize (p, end) == NULL) {
+ if (s_acestr) slapi_ch_free ( (void **) &s_acestr );
+ if (ret_str) slapi_ch_free ( (void **) &ret_str );
+ slapi_ch_free ( (void **) &s_aclstr );
+ return (NULL);
+ }
+ /* check for param rules */
+ __aclp_chk_paramRules ( aci_item, p, end );
+
+ /* XXX need this for roledn ?
+ if ( aci_item->aci_elevel > ACI_ELEVEL_GROUPDN )
+ aci_item->aci_elevel = ACI_ELEVEL_GROUPDN;*/
+ aci_item->aci_ruleType |= ACI_ROLEDN_RULE;
+ }
+ s = ++end;
+ s = __aclp__getNextLASRule(aci_item, s, &end);
+ }/* while */
+
+ /* get the head of the string */
+ acestr = s_acestr;
+ len = strlen( acestr);
+ ptr = acestr +len-1;
+ while (*ptr && *ptr != '\"' && *ptr != ')' ) *ptr-- = ' ';
+ ptr++;
+ *ptr = ';';
+
+ aclutil_str_appened (&ret_str, acestr);
+ if (s_acestr) {
+ slapi_ch_free ( (void **) &s_acestr );
+ }
+ s_acestr = NULL;
+
+ if (nextACE) {
+ s = strstr (nextACE, "allow");
+ if (s == NULL) s = strstr (nextACE, "deny");
+ if (s == NULL) {
+ if (nextACE && *nextACE != '\0')
+ aclutil_str_appened (&ret_str, nextACE);
+ slapi_ch_free ( (void **) &s_aclstr );
+ return (ret_str);
+ }
+ acestr = nextACE;
+ goto normalize_nextACERule;
+ }
+
+ slapi_ch_free ( (void **) &s_aclstr );
+ return (ret_str);
+}
+/*
+ *
+ * acl__getNextLASRule
+ * Find the next rule.
+ *
+ * Returns:
+ * endOfCurrRule - end of current rule
+ * nextRule - start of next rule
+ */
+static char *
+__aclp__getNextLASRule (aci_t *aci_item, char *original_str , char **endOfCurrRule)
+{
+ char *newstr, *word, *next, *start, *end;
+ char *ruleStart = NULL;
+ int len, ruleLen;
+ int in_dn_expr = 0;
+
+ *endOfCurrRule = NULL;
+ end = start = NULL;
+
+ newstr = slapi_ch_strdup (original_str);
+
+ if ( (strncasecmp(newstr, "allow", 5) == 0) ||
+ (strncasecmp(newstr, "deny", 4) == 0) ) {
+ word = ldap_utf8strtok_r(newstr, ")", &next);
+ }
+ else {
+ word = ldap_utf8strtok_r(newstr, " ", &next);
+ }
+
+ /*
+ * The first word is of no interest -- skip it
+ * it's allow or deny followed by the rights (<rights>),
+ * so skip over the rights as well or it's 'and', 'or',....
+ */
+
+ while ( (word = ldap_utf8strtok_r(NULL, " ", &next)) != NULL) {
+ int got_rule = 0;
+ int ruleType = 0;
+ /*
+ ** The next word must be one of these to be considered
+ ** a valid rule.
+ ** This is making me crazy. We might have a case like
+ ** "((userdn=". strtok is returning me that word.
+ */
+ len = strlen ( word );
+ word [len] = '\0';
+
+ if ( (ruleStart= strstr(word, DS_LAS_USERDNATTR)) != NULL) {
+ ruleType |= ACI_USERDNATTR_RULE;
+ ruleLen = strlen ( DS_LAS_USERDNATTR) ;
+ } else if ( (ruleStart = strstr(word, DS_LAS_USERDN)) != NULL) {
+ ruleType = ACI_USERDN_RULE;
+ ruleLen = strlen ( DS_LAS_USERDN);
+ in_dn_expr = 1;
+ } else if ( (ruleStart = strstr(word, DS_LAS_GROUPDNATTR)) != NULL) {
+ ruleType = ACI_GROUPDNATTR_RULE;
+ ruleLen = strlen ( DS_LAS_GROUPDNATTR) ;
+ } else if ((ruleStart= strstr(word, DS_LAS_GROUPDN)) != NULL) {
+ ruleType = ACI_GROUPDN_RULE;
+ ruleLen = strlen ( DS_LAS_GROUPDN) ;
+ in_dn_expr = 1;
+ } else if ((ruleStart = strstr(word, DS_LAS_USERATTR)) != NULL) {
+ ruleType = ACI_USERATTR_RULE;
+ ruleLen = strlen ( DS_LAS_USERATTR) ;
+ } else if ((ruleStart= strstr(word, DS_LAS_ROLEDN)) != NULL) {
+ ruleType = ACI_ROLEDN_RULE;
+ ruleLen = strlen ( DS_LAS_ROLEDN);
+ in_dn_expr = 1;
+ } else if ((ruleStart= strstr(word, DS_LAS_AUTHMETHOD)) != NULL) {
+ ruleType = ACI_AUTHMETHOD_RULE;
+ ruleLen = strlen ( DS_LAS_AUTHMETHOD);
+ } else if ((ruleStart = strstr(word, ACL_ATTR_IP)) != NULL) {
+ ruleType = ACI_IP_RULE;
+ ruleLen = strlen ( ACL_ATTR_IP) ;
+ } else if ((ruleStart = strstr(word, DS_LAS_TIMEOFDAY)) != NULL) {
+ ruleType = ACI_TIMEOFDAY_RULE;
+ ruleLen = strlen ( DS_LAS_TIMEOFDAY) ;
+ } else if ((ruleStart = strstr(word, DS_LAS_DAYOFWEEK)) != NULL) {
+ ruleType = ACI_DAYOFWEEK_RULE;
+ ruleLen = strlen ( DS_LAS_DAYOFWEEK) ;
+ } else if ((ruleStart = strstr(word, ACL_ATTR_DNS)) != NULL) {
+ ruleType = ACI_DNS_RULE;
+ ruleLen = strlen ( ACL_ATTR_DNS) ;
+ }
+ /* Here, we've found a space...if we were in in_dn_expr mode
+ * and we'vve found a closure for that ie.a '"' or a ')'
+ * eg. "'ldap:///all"' or 'ldap:///all")' then exit in_dn_expr mode.
+ */
+ if ( in_dn_expr && (word[len-1] == '"' ||
+ len>1 && word[len-2] == '"' ||
+ len>2 && word[len-3] == '"')) {
+ in_dn_expr = 0;
+ }
+
+ /*
+ * ruleStart may be NULL as word could be (all) for example.
+ * this word will just be skipped--we're really waiting for
+ * userdn or groupdn or...
+ */
+
+ if ( ruleStart && ruleType ) {
+ /* Look in the current word for "=" or else look into
+ ** the next word -- if none of them are true, then this
+ ** is not the start of the rule
+ */
+ char *tmpStr = ruleStart + ruleLen;
+ if ( strchr ( tmpStr, '=') ||
+ ((word = ldap_utf8strtok_r(NULL, " ", &next) ) &&
+ word && ((strncmp ( word, "=", 1) == 0 ) ||
+ (strncmp ( word, "!=",2) ==0) ||
+ (strncmp ( word, ">", 1) == 0 ) ||
+ (strncmp ( word, "<", 1) == 0 ) ||
+ (strncmp ( word, "<", 1) == 0 ) ||
+ (strncmp ( word, "<=",2) ==0 ) ||
+ (strncmp ( word, ">=",2) ==0) ||
+ (strncmp ( word, "=>",2) ==0) ||
+ (strncmp ( word, "=<",2) ==0))
+ ) ){
+ aci_item->aci_ruleType |= ruleType;
+ got_rule = 1;
+ }
+ }
+ if ( NULL == start && got_rule ) {
+ /*
+ * We've just found a rule start--keep going though because
+ * we need to return the end of this rule too.
+ */
+ start= ruleStart;
+ got_rule = 0;
+ } else {
+ /*
+ * Here, we have a candidate for the end of the rule we've found
+ * (the start of which is currently in start).
+ * But we need to be sure it really is the end and not a
+ * "fake end" due to a keyword bbeing embeded in a dn.
+ */
+ if (word && !in_dn_expr &&
+ ((strcasecmp(word, "and") == 0) ||
+ (strcasecmp(word, "or") == 0) ||
+ (strcasecmp(word, "not") == 0) ||
+ (strcasecmp(word, ";") == 0))) {
+ /* If we have start, then it really is the end */
+ word--;
+ if (start) {
+ end = word;
+ break;
+ } else {
+ /* We found a fake end, but we've no start so keep going */
+ }
+ }
+ }
+ } /* while */
+
+
+ if ( end ) {
+ /* Found an end to the rule and it's not the last rule */
+ len = end - newstr;
+ end = original_str +len;
+ while ( (end != original_str) && *end != '\"') end--;
+ *endOfCurrRule = end;
+ len = start - newstr;
+ ruleStart = original_str + len;
+ } else {
+ /* Walked off the end of the string so it's the last rule */
+ end = original_str + strlen(original_str)-1;
+ while ( (end != original_str) && *end != '\"') end--;
+ *endOfCurrRule = end;
+ }
+ if ( start ) {
+ /* Got a rule, fixup the pointer */
+ len = start - newstr;
+ ruleStart = original_str + len;
+ }
+ slapi_ch_free ( (void **) &newstr );
+
+ /*
+ * Here, ruleStart points to the start of the next rule in original_str.
+ * end points to the end of this rule.
+ */
+
+ return ( ruleStart );
+}
+/******************************************************************************
+*
+* __aclp__dn_normalize
+*
+* Normalize the DN INPLACE. This routine is similar to slapi_dn_normalize()
+* except various small stuff at the end.
+* Normalize until the "end" and not to the end of string.
+*
+******************************************************************************/
+static char *
+__aclp__dn_normalize( char *dn , char *end)
+{
+ char *d;
+
+ if ((end - dn) < 0) {
+ return(NULL);
+ }
+
+ d = slapi_dn_normalize_to_end ( dn, end );
+
+ /* Do I have the quotes already */
+ if (*d != '\"' ) {
+ /*
+ ** We are taking care of this situation
+ ** " ") ". We need to remove the space
+ ** infront and tack it after the quote like this.
+ ** "" ) ".
+ */
+
+ *d = '\"';
+ d++;
+ while (*d && *d != '\"') *d++ = ' ';
+ *d = ' ';
+ }
+
+ return( dn );
+}
+/***************************************************************************
+* acl__get_aci_right
+*
+* Go thru the one acl text str and figure our the rights declared.
+*
+*****************************************************************************/
+static int
+__aclp__get_aci_right (char *str)
+{
+
+ char *sav_str = slapi_ch_strdup(str);
+ char *t, *tt;
+ int type = 0;
+ char *delimiter = ",";
+ char *val = NULL;
+ int aclval = 0;
+
+ t = sav_str;
+ __acl_strip_leading_space( &t );
+
+ if (*t == '(' ) {
+ if ((tt = slapi_find_matching_paren(t)) == NULL) {
+ slapi_ch_free ( (void **) &sav_str );
+ return -1;
+ } else {
+ t++; /* skip the first character which is ( */
+ *tt = '\0';
+ }
+ } else {
+ slapi_ch_free ( (void **) &sav_str );
+ return -1;
+ }
+ /* get the tokens separated by "," */
+ val = ldap_utf8strtok_r(t,delimiter, &tt);
+ if (val == NULL )
+ {
+ slapi_ch_free ( (void **) &sav_str );
+ return -1;
+ }
+ while (val != NULL)
+ {
+ /* get the corresponding integer value */
+ aclval = get_acl_rights_as_int(val);
+ if (aclval == -1 )
+ {
+ type = -1;
+ break;
+ }
+ type |= aclval;
+ val = ldap_utf8strtok_r(NULL,delimiter, &tt); /* get the next token */
+ }
+
+ slapi_ch_free ( (void **) &sav_str );
+ return type;
+
+}
+
+static int get_acl_rights_as_int( char * strValue)
+{
+
+ if (strValue == NULL )
+ return -1;
+ /* First strip out the leading and trailing spaces */
+ __acl_strip_leading_space( &strValue );
+ __acl_strip_trailing_space( strValue );
+
+ /* We have to do a strcasecmp (case insensitive cmp) becuase we should return
+ only if it is exact match. */
+
+ if (strcasecmp (strValue, "read") == 0 )
+ return SLAPI_ACL_READ;
+ else if (strcasecmp (strValue, "write") == 0 )
+ return SLAPI_ACL_WRITE;
+ else if (strcasecmp (strValue, "search") == 0 )
+ return SLAPI_ACL_SEARCH;
+ else if (strcasecmp (strValue, "compare") == 0 )
+ return SLAPI_ACL_COMPARE;
+ else if (strcasecmp (strValue, "add") == 0 )
+ return SLAPI_ACL_ADD;
+ else if (strcasecmp (strValue, "delete") == 0 )
+ return SLAPI_ACL_DELETE;
+ else if (strcasecmp (strValue, "proxy") == 0 )
+ return SLAPI_ACL_PROXY;
+ else if (strcasecmp (strValue, "selfwrite") == 0 )
+ return (SLAPI_ACL_SELF | SLAPI_ACL_WRITE);
+ else if (strcasecmp (strValue, "all") == 0 )
+ return SLAPI_ACL_ALL;
+ else
+ return -1; /* error */
+}
+/***************************************************************************
+*
+* acl_access2str
+*
+* Convert the access bits into character strings.
+* Example: "read, self read"
+*
+* Input:
+*
+* int access - The access in bits
+* char **rights - rights in chars
+*
+* Returns:
+* NULL - No rights to start with
+* right - rights converted.
+*
+* Error Handling:
+* None.
+*
+**************************************************************************/
+char *
+acl_access2str(int access)
+{
+
+ if ( access & SLAPI_ACL_COMPARE ) {
+ return access_str_compare;
+ } else if ( access & SLAPI_ACL_SEARCH ) {
+ return access_str_search;
+ } else if ( access & SLAPI_ACL_READ ) {
+ return access_str_read;
+ } else if ( access & SLAPI_ACL_DELETE) {
+ return access_str_delete;
+ } else if ( access & SLAPI_ACL_ADD) {
+ return access_str_add;
+ } else if ( (access & SLAPI_ACL_WRITE ) && (access & SLAPI_ACL_SELF)) {
+ return access_str_selfwrite;
+ } else if (access & SLAPI_ACL_WRITE ) {
+ return access_str_write;
+ } else if (access & SLAPI_ACL_PROXY ) {
+ return access_str_proxy;
+ }
+
+ return NULL;
+}
+/***************************************************************************
+*
+* __aclp__init_targetattr
+*
+* Parse the targetattr string and create a array of attrs. This will
+* help us to do evaluation at run time little faster.
+* entry.
+* Here, also extract any target value filters.
+*
+* Input:
+* aci_t *aci -- The aci item
+* char *str -- the targetattr string
+*
+* Returns:
+* ACL_OK - everything ok
+* ACL_SYNTAX_ERROR - in case of error.
+*
+*
+***************************************************************************/
+static int
+__aclp__init_targetattr (aci_t *aci, char *attr_val)
+{
+
+ int numattr=0;
+ Targetattr **attrArray;
+ char *s, *end_attr, *str;
+ int len;
+ Targetattr *attr = NULL;
+
+ s = strchr (attr_val, '=');
+ s++;
+ __acl_strip_leading_space(&s);
+ len = strlen(s);
+ if (*s == '"' && s[len-1] == '"') {
+ s[len-1] = '\0';
+ s++;
+ }
+
+ str = s;
+ attrArray = aci->targetAttr;
+
+ if (attrArray[0] != NULL) {
+ /*
+ ** That means we are visiting more than once.
+ ** Syntax error. We have a case like: (targetattr) (targetattr)
+ */
+ return ACL_SYNTAX_ERR;
+ }
+
+ while (str != 0 && *str != 0) {
+
+ __acl_strip_leading_space(&str);
+
+ if ((end_attr = strstr(str, "||")) != NULL) {
+ /* skip the two '|' chars */
+ auto char *t = end_attr;
+ LDAP_UTF8INC(end_attr);
+ LDAP_UTF8INC(end_attr);
+ *t = 0;
+ }
+ __acl_strip_trailing_space(str);
+
+ /*
+ * Here:
+ * end_attr points to the next attribute thing.
+ *
+ * str points to the current one to be processed and it looks like this:
+ * rbyrneXXX Watchout is it OK to use : as the speperator ?
+ * cn
+ * c*n*
+ * *
+ * The attribute goes in the attrTarget list.
+ *
+ */
+
+
+ attr = (Targetattr *) slapi_ch_malloc (sizeof (Targetattr));
+ memset (attr, 0, sizeof(Targetattr));
+
+ if (strchr(str, '*')) {
+
+ /* It contains a * so it's something like * or cn* */
+ if (strcmp(str, "*" ) != 0) {
+ char line[100];
+ char *lineptr = &line[0];
+ char *newline = NULL;
+ int lenstr = 0;
+ struct slapi_filter *f = NULL;
+
+ if ((lenstr = strlen(str)) > 91) { /* 100 - 8 for "(attr =%s)" */
+ newline = slapi_ch_malloc(lenstr + 9);
+ lineptr = newline;
+ }
+
+ attr->attr_type = ACL_ATTR_FILTER;
+ sprintf (lineptr, "(attr =%s)", str);
+ f = slapi_str2filter (lineptr);
+
+ if (f == NULL) {
+ slapi_log_error(SLAPI_LOG_FATAL, plugin_name,
+ "__aclp__init_targetattr:Unable to generate filter (%s)\n", lineptr,0,0);
+ } else {
+ attr->u.attr_filter = f;
+ }
+
+ if (newline) slapi_ch_free((void **) &newline);
+ } else {
+ attr->attr_type = ACL_ATTR_STAR;
+ attr->u.attr_str = slapi_ch_strdup (str);
+ }
+
+ } else {
+ attr->u.attr_str = slapi_ch_strdup (str);
+ attr->attr_type = ACL_ATTR_STRING;
+ }
+
+
+ /*
+ * Add the attr to the targetAttr list
+ */
+
+ attrArray[numattr] = attr;
+ numattr++;
+ if (!(numattr % ACL_INIT_ATTR_ARRAY)) {
+ aci->targetAttr = (Targetattr **) slapi_ch_realloc (
+ (void *) aci->targetAttr,
+ (numattr+ACL_INIT_ATTR_ARRAY) *
+ sizeof(Targetattr *));
+ attrArray = aci->targetAttr;
+ }
+
+
+ /* Move on to the next attribute in the list */
+ str = end_attr;
+
+ } /* while */
+
+ /* NULL teminate the list */
+ attrArray[numattr] = NULL;
+ return 0;
+}
+
+void
+acl_strcpy_special (char *d, char *s)
+{
+ for (; *s; LDAP_UTF8INC(s)) {
+ switch (*s) {
+ case '.':
+ case '\\':
+ case '[':
+ case ']':
+ case '*':
+ case '+':
+ case '^':
+ case '$':
+ *d = '\\';
+ LDAP_UTF8INC(d);
+ /* FALL */
+ default:
+ d += LDAP_UTF8COPY(d, s);
+ }
+ }
+ *d = '\0';
+}
+/***************************************************************************
+*
+* acl_verify_aci_syntax
+* verify if the aci's being added for the entry has a valid syntax or not.
+*
+* Input:
+* Slapi_Entry *e - The Slapi_Entry itself
+* char **errbuf; -- error message
+*
+* Returns:
+* -1 (ACL_ERR) - Syntax error
+* 0 - No error
+*
+* Error Handling:
+* None.
+*
+**************************************************************************/
+int
+acl_verify_aci_syntax (Slapi_Entry *e, char **errbuf)
+{
+
+ if (e != NULL) {
+ Slapi_DN *e_sdn;
+ int rv;
+ Slapi_Attr *attr = NULL;
+ Slapi_Value *sval=NULL;
+ const struct berval *attrVal;
+ int i;
+
+ e_sdn = slapi_entry_get_sdn ( e );
+
+ slapi_entry_attr_find (e, aci_attr_type, &attr);
+ if (! attr ) return 0;
+
+ i= slapi_attr_first_value ( attr,&sval );
+ while ( i != -1 ) {
+ attrVal = slapi_value_get_berval ( sval );
+ rv=acl_verify_syntax ( e_sdn, attrVal);
+ if ( 0 != rv ) {
+ aclutil_print_err(rv, e_sdn, attrVal, errbuf);
+ return ACL_ERR;
+ }
+ i = slapi_attr_next_value ( attr, i, &sval );
+ }
+ }
+ return(0);
+}
+/***************************************************************************
+*
+* acl__verify_syntax
+* Called from slapi_acl_check_mods() to verify if the new aci being
+* added/replaced has the right syntax or not.
+*
+* Input:
+* Slapi_DN *e_sdn - sdn of the entry
+* berval *bval - The berval containg the aci value
+*
+* Returns:
+* return values from acl__parse_aci()
+*
+* Error Handling:
+* None.
+*
+**************************************************************************/
+int
+acl_verify_syntax(const Slapi_DN *e_sdn, const struct berval *bval)
+{
+ aci_t *aci_item;
+ int rv = 0;
+ char *str;
+ aci_item = acllist_get_aci_new ();
+ slapi_sdn_set_ndn_byval ( aci_item->aci_sdn, slapi_sdn_get_ndn ( e_sdn ) );
+
+ /* make a copy the the string */
+ str = slapi_ch_strdup(bval->bv_val);
+ rv = acl_parse(str, aci_item);
+
+ /* cleanup before you leave ... */
+ acllist_free_aci (aci_item);
+ slapi_ch_free ( (void **) &str );
+ return(rv);
+}
+static void
+__aclp_chk_paramRules ( aci_t *aci_item, char *start, char *end)
+{
+
+ size_t len;
+ char *str;
+ char *p, *s;
+
+
+ len = end - start;
+
+ s = str = (char *) slapi_ch_calloc(1, len + 1);
+ memcpy ( str, start, len);
+ while ( (p= strchr ( s, '$')) != NULL) {
+ p++; /* skip the $ */
+ if ( 0 == strncasecmp ( p, "dn", 2))
+ aci_item->aci_ruleType |= ACI_PARAM_DNRULE;
+ else if ( 0 == strncasecmp ( p, "attr", 4))
+ aci_item->aci_ruleType |= ACI_PARAM_ATTRRULE;
+
+ s = p;
+ }
+ slapi_ch_free ( (void **) &str );
+}
+
+/*
+ * Check for an ocurrence of a macro aci in the target.
+ * value is the normalized target string.
+ *
+ * this is something like:
+ * (target="ldap:///cn=*,ou=people,($dn),o=sun.com")
+ *
+ *
+ * returns 1 if there is a $dn present.
+ * returns 0 if not.
+ * returns -1 is syntax error.
+ * If succes then:
+ * ACI_TARGET_MACRO_DN is the type.
+ * type can also include, ACI_TARGET_PATTERN, ACI_TARGET_NOT.
+ * Also aci_item->aci_macro->match_this is set to be
+ * cn=*,ou=people,($dn),o=sun.com, to be used later.
+ *
+ * . we allow at most one ($dn) in a target.
+ * . if a "*" accurs with it, it must be the first component and at most
+ * once.
+ * . it's ok for ($dn) to occur on it's own in a target, but if it appears in
+ * a user rule, then it must be in the target.
+ *
+ *
+ *
+*/
+
+static int
+acl_check_for_target_macro( aci_t *aci_item, char *value)
+{
+
+ char *str = NULL;
+
+ str = strstr( value, ACL_TARGET_MACRO_DN_KEY);
+
+ if (str != NULL) {
+ aci_item->aci_type &= ~ACI_TARGET_DN;
+ aci_item->aci_type |= ACI_TARGET_MACRO_DN;
+ aci_item->aci_macro = (aciMacro *)slapi_ch_malloc(sizeof(aciMacro));
+ aci_item->aci_macro->match_this = slapi_ch_strdup(value);
+ aci_item->aci_macro->macro_ptr = strstr( aci_item->aci_macro->match_this,
+ ACL_TARGET_MACRO_DN_KEY);
+ return(1);
+ }
+
+ return(0);
+}
+
+/* Strip trailing spaces from str by writing '\0' into them */
+
+static void
+__acl_strip_trailing_space( char *str) {
+
+ char *ptr = NULL;
+ int len = 0;
+
+ if (*str) {
+ /* ignore trailing whitespace */
+ len = strlen(str);
+ ptr = str+len-1;
+ while(ldap_utf8isspace(ptr)){ *ptr = '\0'; LDAP_UTF8DEC(ptr); }
+ }
+}
+
+/*
+ * Strip leading spaces by resetting str to point to the first
+ * non-space charater.
+*/
+
+static void
+__acl_strip_leading_space( char **str) {
+
+ char *tmp_ptr = NULL;
+
+ tmp_ptr = *str;
+ while ( *tmp_ptr && ldap_utf8isspace( tmp_ptr ) ) LDAP_UTF8INC(tmp_ptr);
+ *str = tmp_ptr;
+}
+
+
+/*
+ * str is a string containing an LDAP filter.
+ * Trim off enclosing quotes and enclosing
+ * superfluous brackets.
+ * The result is duped so it can be kept.
+*/
+
+static char *
+__acl_trim_filterstr( char * str ) {
+
+ char *tmpstr;
+ int len;
+ char *end;
+
+ tmpstr = str;
+
+ /* If the last char is a "," take it out */
+
+ len = strlen (tmpstr);
+ if (len>0 && (tmpstr[len-1] == ',')) {
+ tmpstr [len-1] = '\0';
+ }
+
+
+ /* Does it have quotes around it */
+ len = strlen (tmpstr);
+ if (*tmpstr == '"' && tmpstr[len-1] == '"') {
+ tmpstr [len-1] = '\0';
+ tmpstr++;
+ }
+
+ str = tmpstr;
+
+ /* If we have a filter like
+ ** (((&(...) (...)))), we need to get rid of the
+ ** multiple parens or slapi_str2filter will not
+ ** evaluate properly. Need to package like
+ ** (filter ). probably I should fix str2filter
+ ** code.
+ */
+
+ while (*tmpstr++ == '(' && *tmpstr == '(') {
+ if ((end = slapi_find_matching_paren( str )) != NULL) {
+ *end = '\0';
+ str++;
+ }
+ }
+
+ return( slapi_ch_strdup(str));
+}
+
+/*
+ * Here str points to a targetattrfilters thing which looks tlike this:
+ *
+ * targetattrfilters="add=attr1:F1 && attr2:F2 ... && attrn:Fn,
+ * del=attr1:F1 && attr2:F2... && attrn:Fn")
+ *
+ *
+ */
+
+static int __acl__init_targetattrfilters( aci_t *aci, char *input_str) {
+
+ int numattr=0;
+ char *s, *str;
+ int len;
+ char *addlistptr = NULL;
+ char *dellistptr = NULL;
+
+ if (aci->targetAttrAddFilters != NULL ||
+ aci->targetAttrDelFilters != NULL) {
+
+ /*
+ ** That means we are visiting more than once.
+ ** Syntax error.
+ ** We have a case like: (targetattrfilters) (targetattrfilters)
+ */
+
+ return ACL_SYNTAX_ERR;
+ }
+
+ /* First, skip the "targetattrfilters" */
+
+ s = strchr (input_str, '=');
+ s++; /* skip the = */
+ __acl_strip_leading_space(&s); /* skip to next significant character */
+ len = strlen(s); /* Knock off the " and trailing ) */
+ if (*s == '"' && s[len-1] == '"') {
+ s[len-1] = '\0';
+ s++; /* skip the first " */
+ } else { /* No matching quotes */
+
+ return (ACL_SYNTAX_ERR);
+ }
+
+ str = s;
+
+ /*
+ * Here str looks like add=attr1:F1...attrn:Fn,
+ * del=attr1:F1...attrn:Fn
+ *
+ * extract the add and del filter lists and process each one
+ * in turn.
+ */
+
+ s = strchr (str, '=');
+ *s = '\0';
+ s++; /* skip the = */
+ __acl_strip_leading_space(&s); /* start of the first filter list */
+
+
+ /*
+ * Now str is add or del
+ * s points to the first filter list.
+ */
+
+ if (strcmp(str, "add") == 0) {
+ aci->aci_type |= ACI_TARGET_ATTR_ADD_FILTERS;
+ addlistptr = s;
+
+ /* Now isolate the first filter list. */
+ if ((str = strstr(s , "del=")) || ((str = strstr(s , "del ="))) ) {
+ str--;
+ *str = '\0';
+ *str++;
+ }
+
+
+ } else if (strcmp(str, "del") == 0) {
+ aci->aci_type |= ACI_TARGET_ATTR_DEL_FILTERS;
+ dellistptr = s;
+
+ /* Now isolate the first filter list. */
+ if ((str = strstr(s , "add=")) || ((str = strstr(s , "add ="))) ) {
+ str--;
+ *str = '\0';
+ *str++;
+ }
+ } else {
+ return(ACL_SYNTAX_ERR);
+ }
+
+ __acl_strip_trailing_space(s);
+
+ /*
+ * Here, we have isolated the first filter list.
+ * There may be a second one.
+ * Now, str points to the start of the
+ * string that contains the second filter list.
+ * If there is none then str is NULL.
+ */
+
+ if (str != NULL ){
+
+ __acl_strip_leading_space(&str);
+ s = strchr (str, '=');
+ *s = '\0';
+ s++;
+ __acl_strip_trailing_space(str);
+ __acl_strip_leading_space(&s);
+
+
+ /*
+ * s points to the start of the second filter list.
+ * str is add or del
+ */
+
+ if (aci->aci_type & ACI_TARGET_ATTR_ADD_FILTERS) {
+
+ if (strcmp(str, "del") == 0) {
+ aci->aci_type |= ACI_TARGET_ATTR_DEL_FILTERS;
+ dellistptr = s;
+ } else {
+ return(ACL_SYNTAX_ERR);
+ }
+ } else if ( aci->aci_type & ACI_TARGET_ATTR_DEL_FILTERS ) {
+ if (strcmp(str, "add") == 0) {
+ aci->aci_type |= ACI_TARGET_ATTR_ADD_FILTERS;
+ addlistptr = s;
+ } else {
+ return(ACL_SYNTAX_ERR);
+ }
+ }
+ }
+
+ /*
+ * addlistptr points to the add filter list.
+ * dellistptr points to the del filter list.
+ * In both cases the strings have been leading and trailing space
+ * stripped.
+ * Either may be NULL.
+ */
+
+ if (process_filter_list( &aci->targetAttrAddFilters, addlistptr)
+ == ACL_SYNTAX_ERR) {
+ return( ACL_SYNTAX_ERR);
+ }
+
+ if (process_filter_list( &aci->targetAttrDelFilters, dellistptr)
+ == ACL_SYNTAX_ERR) {
+ return( ACL_SYNTAX_ERR);
+ }
+
+ return(0);
+
+}
+
+/*
+ * We have a list of filters that looks like this:
+ * attr1:F1 &&....attrn:Fn
+ *
+ * We need to put each component into a targetattrfilter component of
+ * the array.
+ *
+*/
+
+static int process_filter_list( Targetattrfilter ***input_attrFilterArray,
+ char * input_str) {
+
+ char *str, *end_attr, *tmp_attr;
+ Targetattrfilter *attrfilter = NULL;
+ int numattr=0;
+ Targetattrfilter **attrFilterArray = NULL;
+
+ str = input_str;
+
+ while (str != 0 && *str != 0) {
+
+ if ((end_attr = strstr(str, "&&")) != NULL) {
+ /* skip the two '|' chars */
+ auto char *t = end_attr;
+ LDAP_UTF8INC(end_attr);
+ LDAP_UTF8INC(end_attr);
+ *t = 0;
+ }
+ __acl_strip_trailing_space(str);
+ __acl_strip_leading_space(&str);
+
+ /*
+ * Here:
+ * end_attr points to the next attribute thing.
+ *
+ * str points to the current one to be processed and it looks like
+ * this:
+ *
+ * attr1:F1
+ *
+ */
+
+ attrfilter = (Targetattrfilter *) slapi_ch_malloc (sizeof (Targetattrfilter));
+ memset (attrfilter, 0, sizeof(Targetattrfilter));
+
+ if ((tmp_attr = strstr( str,":")) != NULL) {
+
+ if ( __acl_init_targetattrfilter( attrfilter, str ) != 0 ) {
+ slapi_ch_free((void**)&attrfilter);
+ return(ACL_SYNTAX_ERR);
+ }
+ } else {
+ slapi_ch_free((void**)&attrfilter);
+ return(ACL_SYNTAX_ERR);
+ }
+
+
+ /*
+ * Add the attrfilte to the targetAttrFilter list
+ */
+
+
+ attrFilterArray = (Targetattrfilter **) slapi_ch_realloc (
+ (void *) attrFilterArray,
+ ((numattr+1)*sizeof(Targetattrfilter *)) );
+ attrFilterArray[numattr] = attrfilter;
+ numattr++;
+
+ /* Move on to the next attribute in the list */
+ str = end_attr;
+
+ }/* while */
+
+ /* NULL terminate the list */
+
+ attrFilterArray = (Targetattrfilter **) slapi_ch_realloc (
+ (void *) attrFilterArray,
+ ((numattr+1)*sizeof(Targetattrfilter *)) );
+ attrFilterArray[numattr] = NULL;
+
+ *input_attrFilterArray = attrFilterArray;
+ return 0;
+
+}
+
+/*
+ * Take str and put it into the attrfilter component.
+ *
+ * str looks as follows: attr1:F1
+ *
+ * It has had leading and trailing space stripped.
+*/
+
+static int __acl_init_targetattrfilter( Targetattrfilter *attrfilter,
+ char *str ) {
+
+ char *tmp_ptr, *s, *filter_ptr;
+ Slapi_Filter *f = NULL;
+
+ s = str;
+
+ /* First grab the attribute name */
+
+ if ( (tmp_ptr = strstr( str, ":")) == NULL ) {
+ /* No :, syntax error */
+ slapi_log_error(SLAPI_LOG_ACL, plugin_name,
+ "Bad targetattrfilter %s:%s\n",
+ str,"Expecting \":\"",0);
+
+ return(ACL_SYNTAX_ERR);
+ }
+ *tmp_ptr = '\0';
+ LDAP_UTF8INC(tmp_ptr);
+
+ __acl_strip_trailing_space(s);
+
+ /* s should be the attribute name-make sure it's non-empty. */
+
+ if ( *s == '\0' ) {
+ slapi_log_error(SLAPI_LOG_ACL, plugin_name,
+ "No attribute name in targattrfilters\n",
+ 0,0);
+ return(ACL_SYNTAX_ERR);
+ }
+
+ attrfilter->attr_str = slapi_ch_strdup (s);
+
+ /* Now grab the filter */
+
+ filter_ptr = tmp_ptr;
+ __acl_strip_leading_space(&filter_ptr);
+ __acl_strip_trailing_space(filter_ptr);
+
+ /* trim dups the string, so we need to free it later if it's not kept. */
+ tmp_ptr = __acl_trim_filterstr(filter_ptr);
+
+ if ((f = slapi_str2filter(tmp_ptr)) == NULL) {
+ slapi_log_error(SLAPI_LOG_ACL, plugin_name,
+ "Bad targetattr filter for attribute %s:%s\n",
+ attrfilter->attr_str,tmp_ptr,0);
+ slapi_ch_free( (void **) &attrfilter->attr_str);
+ slapi_ch_free( (void **) &tmp_ptr);
+ return(ACL_SYNTAX_ERR);
+ }
+
+ /*
+ * Here verify that the named attribute is the only one
+ * that appears in the filter.
+ */
+
+ if (acl_verify_exactly_one_attribute( attrfilter->attr_str, f) !=
+ SLAPI_FILTER_SCAN_NOMORE) {
+ slapi_log_error(SLAPI_LOG_ACL, plugin_name,
+ "Exactly one attribute type per filter allowed in targattrfilters (%s)\n",
+ attrfilter->attr_str, 0);
+ slapi_ch_free( (void **) &attrfilter->attr_str);
+ slapi_ch_free( (void **) &tmp_ptr);
+ slapi_filter_free( f, 1 );
+ return(ACL_SYNTAX_ERR);
+ }
+
+ /* free the tmp_ptr */
+ slapi_ch_free( (void **) &tmp_ptr);
+ attrfilter->filterStr = slapi_ch_strdup (filter_ptr);
+ attrfilter->filter = f;
+
+ return(LDAP_SUCCESS);
+}
+
+/*
+ * Returns 0 if attr_name is the only attribute name to
+ * appear in original_filter AND it appears at least once.
+ * Otherwise returns STOP_FILTER_SCAN.
+*/
+
+static int acl_verify_exactly_one_attribute( char *attr_name,
+ Slapi_Filter *original_filter) {
+ int error_code;
+
+ return( slapi_filter_apply( original_filter, type_compare,
+ (void *)attr_name, &error_code));
+
+}
+
+static int type_compare( Slapi_Filter *f, void *arg) {
+
+ /* Compare only the base names: eg cn and cn;lang-eb will be the same. */
+
+ char *t = (char *)arg;
+ char *filter_type;
+ int rc = SLAPI_FILTER_SCAN_STOP;
+
+ if (slapi_filter_get_attribute_type( f, &filter_type) == 0) {
+ t = slapi_attr_syntax_normalize(t);
+ filter_type = slapi_attr_syntax_normalize(filter_type);
+
+ if (slapi_attr_type_cmp(filter_type, t, 1) == 0) {
+ rc = SLAPI_FILTER_SCAN_CONTINUE;
+ }
+
+ slapi_ch_free( (void **)&t );
+ slapi_ch_free( (void **)&filter_type );
+ }
+
+ return rc;
+}
diff --git a/ldap/servers/plugins/acl/aclplugin.c b/ldap/servers/plugins/acl/aclplugin.c
new file mode 100644
index 00000000..a39ef3cd
--- /dev/null
+++ b/ldap/servers/plugins/acl/aclplugin.c
@@ -0,0 +1,355 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+/*
+ * There are 3 ACL PLUGIN points
+ * PREOP, POSTOP and ACL plugin
+ *
+ */
+#include "acl.h"
+#include "dirver.h"
+#include <dirlite_strings.h> /* PLUGIN_MAGIC_VENDOR_STR */
+
+static Slapi_PluginDesc pdesc = { "acl", PLUGIN_MAGIC_VENDOR_STR, PRODUCTTEXT, "acl access check plugin" };
+char *plugin_name = ACL_PLUGIN_NAME;
+
+/* Prototypes */
+
+static int aclplugin_preop_search ( Slapi_PBlock *pb );
+static int aclplugin_preop_modify ( Slapi_PBlock *pb );
+static int aclplugin_preop_common ( Slapi_PBlock *pb );
+
+/*******************************************************************************
+ * ACL PLUGIN Architecture
+ *
+ * There are 3 registered plugins:
+ *
+ * 1) PREOP ACL Plugin
+ * The preop plugin does all the initialization. It allocate the ACL
+ * PBlock and copies stuff from the connection if it needs to.
+ *
+ * 2) POSTOP ACL Plugin
+ * The Postop plugin cleans up the ACL PBlock. It copies Back to the
+ * connection struct. The Postop bind & Unbind cleans up the
+ * ACL CBlock ( struct hanging from conn struct ).
+ *
+ * 3) ACCESSCONTROL Plugin
+ * Main module which does the access check. There are 5 entry point
+ * from this plugin
+ * a) Initilize the ACL system i.e read all the ACLs and generate the
+ * the ACL List.
+ * b) Check for ACI syntax.
+ * c) Check for normal access.
+ * d) Check for access to a mod request.
+ * e) Update the in-memory ACL List.
+ *
+ *******************************************************************************/
+
+/*******************************************************************************
+ * PREOP
+ *******************************************************************************/
+
+/* Plugin identity is passed by the server in the plugin init function and must
+ be supplied by the plugin to all internal operations it initiates
+ */
+void* g_acl_preop_plugin_identity;
+
+int
+acl_preopInit (Slapi_PBlock *pb)
+{
+ int rc = 0;
+
+ /* save plugin identity to later pass to internal operations */
+ rc = slapi_pblock_get (pb, SLAPI_PLUGIN_IDENTITY, &g_acl_preop_plugin_identity);
+
+ /* Declare plugin version */
+ rc = slapi_pblock_set(pb, SLAPI_PLUGIN_VERSION, SLAPI_PLUGIN_VERSION_01);
+
+ /* Provide descriptive information */
+ rc |= slapi_pblock_set(pb, SLAPI_PLUGIN_DESCRIPTION, (void*)&pdesc);
+
+ /* Register functions */
+ rc |= slapi_pblock_set(pb, SLAPI_PLUGIN_PRE_SEARCH_FN, (void*)aclplugin_preop_search);
+ rc |= slapi_pblock_set(pb, SLAPI_PLUGIN_PRE_COMPARE_FN, (void*)aclplugin_preop_search);
+ rc |= slapi_pblock_set(pb, SLAPI_PLUGIN_PRE_ADD_FN, (void*)aclplugin_preop_modify);
+ rc |= slapi_pblock_set(pb, SLAPI_PLUGIN_PRE_MODIFY_FN, (void*)aclplugin_preop_modify);
+ rc |= slapi_pblock_set(pb, SLAPI_PLUGIN_PRE_MODRDN_FN, (void*)aclplugin_preop_modify);
+ rc |= slapi_pblock_set(pb, SLAPI_PLUGIN_PRE_DELETE_FN, (void*)aclplugin_preop_modify);
+
+#if 0
+ /*
+ * XXXmcs: In order to support access control checking from
+ * extended operations, we need a SLAPI_PLUGIN_PRE_EXTENDED_FN hook.
+ * But today no such entry point exists.
+ */
+ rc |= slapi_pblock_set(pb, SLAPI_PLUGIN_PRE_EXTENDED_FN, (void*)aclplugin_preop_modify);
+#endif
+
+
+ slapi_log_error( SLAPI_LOG_PLUGIN, plugin_name, "<= acl_preop_Init %d\n", rc, 0, 0 );
+ return( rc );
+}
+
+/* For preop search we do two things:
+ * 1) based on the search base, we preselect the acls.
+ * 2) also get hold of a acl_pblock for use
+ */
+static int
+aclplugin_preop_search ( Slapi_PBlock *pb )
+{
+ int scope;
+ char *base = NULL;
+ int optype;
+ int isRoot;
+ int rc = 0;
+
+ TNF_PROBE_0_DEBUG(aclplugin_preop_search_start ,"ACL","");
+
+ slapi_pblock_get ( pb, SLAPI_OPERATION_TYPE, &optype );
+ slapi_pblock_get ( pb, SLAPI_REQUESTOR_ISROOT, &isRoot );
+
+ if ( isRoot ) {
+ TNF_PROBE_1_DEBUG(aclplugin_preop_search_end ,"ACL","",
+ tnf_string,isroot,"");
+ return rc;
+ }
+
+ slapi_pblock_get( pb, SLAPI_SEARCH_TARGET, &base );
+ /* For anonymous client doing search nothing needs to be set up */
+ if ( optype == SLAPI_OPERATION_SEARCH && aclanom_is_client_anonymous ( pb ) &&
+ ! slapi_dn_issuffix( base, "cn=monitor") ) {
+ TNF_PROBE_1_DEBUG(aclplugin_preop_search_end ,"ACL","",
+ tnf_string,anon,"");
+ return rc;
+ }
+
+ if ( 0 == ( rc = aclplugin_preop_common( pb ))) {
+ slapi_pblock_get( pb, SLAPI_SEARCH_SCOPE, &scope );
+ acllist_init_scan ( pb, scope, base );
+ }
+
+ TNF_PROBE_0_DEBUG(aclplugin_preop_search_end ,"ACL","");
+
+ return rc;
+}
+
+/*
+ * For rest of the opertion type, we get a hold of the acl
+ * private Block.
+ */
+static int
+aclplugin_preop_modify ( Slapi_PBlock *pb )
+{
+ /*
+ * Note: since we don't keep the anom profile for modifies, we have to go
+ * through the regular process to check the access.
+ */
+ return aclplugin_preop_common( pb );
+}
+
+/*
+ * Common function that is called by aclplugin_preop_search() and
+ * aclplugin_preop_modify().
+ *
+ * Return values:
+ * 0 - all is well; proceed.
+ * 1 - fatal error; result has been sent to client.
+ */
+static int
+aclplugin_preop_common( Slapi_PBlock *pb )
+{
+ char *proxy_dn; /* id being assumed */
+ char *dn; /* proxy master */
+ char *errtext = NULL;
+ int lderr;
+ Acl_PBlock *aclpb;
+
+ TNF_PROBE_0_DEBUG(aclplugin_preop_common_start ,"ACL","");
+
+ aclpb = acl_get_aclpb ( pb, ACLPB_BINDDN_PBLOCK );
+
+ /*
+ * The following mallocs memory for proxy_dn, but not the dn.
+ * The proxy_dn is the id being assumed, while dn
+ * is the "proxy master".
+ */
+ proxy_dn = NULL;
+ if ( LDAP_SUCCESS != ( lderr = acl_get_proxyauth_dn( pb, &proxy_dn,
+ &errtext ))) {
+ /*
+ * Fatal error -- send a result to the client and arrange to skip
+ * any further processing.
+ */
+ slapi_send_ldap_result( pb, lderr, NULL, errtext, 0, NULL );
+ TNF_PROBE_1_DEBUG(aclplugin_preop_common_end ,"ACL","",
+ tnf_string,proxid_error,"");
+
+ return 1; /* skip any further processing */
+ }
+ slapi_pblock_get ( pb, SLAPI_REQUESTOR_DN, &dn );
+
+
+ /*
+ * The dn is copied into the aclpb during initialization.
+ */
+ if ( proxy_dn) {
+ TNF_PROBE_0_DEBUG(proxyacpb_init_start,"ACL","");
+
+ slapi_log_error( SLAPI_LOG_ACL, plugin_name,
+ "proxied authorization dn is (%s)\n", proxy_dn );
+ acl_init_aclpb ( pb, aclpb, proxy_dn, 1 );
+ aclpb = acl_new_proxy_aclpb (pb );
+ acl_init_aclpb ( pb, aclpb, dn, 0 );
+ slapi_ch_free ( (void **) &proxy_dn );
+
+ TNF_PROBE_0_DEBUG(proxyacpb_init_end,"ACL","");
+
+ } else {
+ TNF_PROBE_0_DEBUG(aclpb_init_start,"ACL","");
+ acl_init_aclpb ( pb, aclpb, dn, 1 );
+ TNF_PROBE_0_DEBUG(aclpb_init_end,"ACL","");
+
+ }
+
+ TNF_PROBE_0_DEBUG(aclplugin_preop_common_end ,"ACL","");
+
+ return 0;
+}
+
+/*******************************************************************************
+ * POSTOP
+ *******************************************************************************/
+
+/*******************************************************************************
+ * ACCESSCONTROL PLUGIN
+ *******************************************************************************/
+
+void* g_acl_plugin_identity;
+
+/* For now, the acl component is implemented as 2 different plugins */
+/* Return the right plugin identity */
+void * aclplugin_get_identity(int plug) {
+ if (plug == ACL_PLUGIN_IDENTITY)
+ return g_acl_plugin_identity;
+ if (plug == ACL_PREOP_PLUGIN_IDENTITY)
+ return g_acl_preop_plugin_identity;
+ return NULL;
+}
+
+int
+aclplugin_init (Slapi_PBlock *pb )
+{
+
+ int rc = 0; /* OK */
+ rc = aclinit_main ( pb );
+
+ return rc;
+
+}
+int
+aclplugin_stop ( Slapi_PBlock *pb )
+{
+ int rc = 0; /* OK */
+
+ /* nothing to be done now */
+ return rc;
+}
+
+int
+acl_init( Slapi_PBlock *pb )
+{
+ int rc =0;
+
+ slapi_log_error( SLAPI_LOG_PLUGIN, plugin_name, "=> acl_init\n", 0, 0, 0 );
+
+ if ( 0 != acl_init_ext() ) {
+ slapi_log_error ( SLAPI_LOG_FATAL, plugin_name,
+ "Unable to initialize the extensions\n");
+ return 1;
+ }
+
+ /* save plugin identity to later pass to internal operations */
+ rc = slapi_pblock_get (pb, SLAPI_PLUGIN_IDENTITY, &g_acl_plugin_identity);
+
+ rc = slapi_pblock_set( pb, SLAPI_PLUGIN_VERSION,
+ (void *) SLAPI_PLUGIN_VERSION_01 );
+ rc |= slapi_pblock_set( pb, SLAPI_PLUGIN_DESCRIPTION,
+ (void *)&pdesc );
+
+ rc |= slapi_pblock_set( pb, SLAPI_PLUGIN_START_FN, (void *) aclplugin_init );
+ rc = slapi_pblock_set( pb, SLAPI_PLUGIN_CLOSE_FN, (void *) aclplugin_stop );
+ rc |= slapi_pblock_set( pb, SLAPI_PLUGIN_ACL_SYNTAX_CHECK,
+ (void *) acl_verify_aci_syntax );
+ rc |= slapi_pblock_set( pb, SLAPI_PLUGIN_ACL_ALLOW_ACCESS,
+ (void *) acl_access_allowed_main );
+ rc |= slapi_pblock_set( pb, SLAPI_PLUGIN_ACL_MODS_ALLOWED,
+ (void *) acl_check_mods );
+ rc |= slapi_pblock_set( pb, SLAPI_PLUGIN_ACL_MODS_UPDATE,
+ (void *) acl_modified );
+
+ slapi_log_error( SLAPI_LOG_PLUGIN, plugin_name, "<= acl_init %d\n", rc, 0, 0 );
+ return( rc );
+}
+
+/*
+ *
+ * acl_access_allowed_main
+ * Main interface to the plugin. Calls different access check functions
+ * based on the flag.
+ *
+ *
+ * Returns:
+ * LDAP_SUCCESS -- access is granted
+ * LDAP_INSUFFICIENT_ACCESS -- access denied
+ * <other ldap error> -- ex: opererations error
+ *
+ */
+int
+acl_access_allowed_main ( Slapi_PBlock *pb, Slapi_Entry *e, char **attrs,
+ struct berval *val, int access , int flags, char **errbuf)
+{
+ int rc =0;
+ char *attr = NULL;
+
+ TNF_PROBE_0_DEBUG(acl_access_allowed_main_start,"ACL","");
+
+ if (attrs && *attrs) attr = attrs[0];
+
+ if (ACLPLUGIN_ACCESS_READ_ON_ENTRY == flags)
+ rc = acl_read_access_allowed_on_entry ( pb, e, attrs, access);
+ else if ( ACLPLUGIN_ACCESS_READ_ON_ATTR == flags)
+ rc = acl_read_access_allowed_on_attr ( pb, e, attr, val, access);
+ else if ( ACLPLUGIN_ACCESS_READ_ON_VLV == flags)
+ rc = acl_access_allowed_disjoint_resource ( pb, e, attr, val, access);
+ else if ( ACLPLUGIN_ACCESS_MODRDN == flags)
+ rc = acl_access_allowed_modrdn ( pb, e, attr, val, access);
+ else if ( ACLPLUGIN_ACCESS_GET_EFFECTIVE_RIGHTS == flags)
+ rc = acl_get_effective_rights ( pb, e, attrs, val, access, errbuf );
+ else
+ rc = acl_access_allowed ( pb, e, attr, val, access);
+
+ /* generate the appropriate error message */
+ if ( ( rc != LDAP_SUCCESS ) && errbuf &&
+ ( ACLPLUGIN_ACCESS_GET_EFFECTIVE_RIGHTS != flags ) &&
+ ( access & ( SLAPI_ACL_WRITE | SLAPI_ACL_ADD | SLAPI_ACL_DELETE ))) {
+
+ char *edn = slapi_entry_get_dn ( e );
+
+ acl_gen_err_msg(access, edn, attr, errbuf);
+ }
+
+ TNF_PROBE_0_DEBUG(acl_access_allowed_main_end,"ACL","");
+
+ return rc;
+}
+#ifdef _WIN32
+
+int *module_ldap_debug = 0;
+void plugin_init_debug_level ( int *level_ptr )
+{
+ module_ldap_debug = level_ptr;
+}
+#endif
+
diff --git a/ldap/servers/plugins/acl/aclproxy.c b/ldap/servers/plugins/acl/aclproxy.c
new file mode 100644
index 00000000..9065bddc
--- /dev/null
+++ b/ldap/servers/plugins/acl/aclproxy.c
@@ -0,0 +1,195 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+#include "acl.h"
+
+#define BEGIN do {
+#define END } while(0);
+
+/* ------------------------------------------------------------
+ * LDAPProxyAuth
+ *
+ * ProxyAuthControl ::= SEQUENCE {
+ * authorizationDN LDAPDN
+ * }
+ */
+struct LDAPProxyAuth
+{
+ char *auth_dn;
+};
+typedef struct LDAPProxyAuth LDAPProxyAuth;
+
+/*
+ * delete_LDAPProxyAuth
+ */
+static void
+delete_LDAPProxyAuth(LDAPProxyAuth *spec)
+{
+ if (!spec) return;
+
+ slapi_ch_free((void**)&spec->auth_dn);
+ slapi_ch_free((void**)&spec);
+}
+
+/*
+ * parse_LDAPProxyAuth
+ *
+ * Parse a BER encoded value into the compoents of the LDAP ProxyAuth control.
+ * The 'version' parameter should be 1 or 2.
+ *
+ * Returns an LDAP error code (LDAP_SUCCESS if all goes well) and sets
+ * *errtextp if appropriate.
+ */
+static int
+parse_LDAPProxyAuth(struct berval *spec_ber, int version, char **errtextp,
+ LDAPProxyAuth **out)
+{
+ int lderr = LDAP_OPERATIONS_ERROR; /* pessimistic */
+ LDAPProxyAuth *spec = NULL;
+ BerElement *ber = NULL;
+ char *errstring = "unable to parse proxied authorization control";
+
+
+ BEGIN
+ unsigned long tag;
+
+ if ( version != 1 && version != 2 ) {
+ break;
+ }
+
+ /* create_LDAPProxyAuth */
+ spec = (LDAPProxyAuth*)slapi_ch_calloc(1,sizeof (LDAPProxyAuth));
+ if (!spec) {
+ break;
+ }
+
+ ber = ber_init(spec_ber);
+ if (!ber) {
+ break;
+ }
+
+ if ( version == 1 ) {
+ tag = ber_scanf(ber, "{a}", &spec->auth_dn);
+ } else {
+ tag = ber_scanf(ber, "a", &spec->auth_dn);
+ }
+ if (tag == LBER_ERROR) {
+ lderr = LDAP_PROTOCOL_ERROR;
+ break;
+ }
+
+ /*
+ * In version 2 of the control, the control value is actually an
+ * authorization ID (see section 9 of RFC 2829). We only support
+ * the "dnAuthzId" flavor, which looks like "dn:<DN>" where <DN> is
+ * an actual DN, e.g., "dn:uid=bjensen,dc=example,dc=com". So we
+ * need to strip off the dn: if present and reject the operation if
+ * not.
+ */
+ if (2 == version) {
+ if ( NULL == spec->auth_dn || strlen( spec->auth_dn ) < 3 ||
+ strncmp( "dn:", spec->auth_dn, 3 ) != 0 ) {
+ lderr = LDAP_INSUFFICIENT_ACCESS; /* per Proxied Auth. I-D */
+ errstring = "proxied authorization id must be a DN (dn:...)";
+ break;
+ }
+ strcpy( spec->auth_dn, spec->auth_dn + 3 );
+ }
+
+ slapi_dn_normalize(spec->auth_dn);
+ lderr = LDAP_SUCCESS; /* got it! */
+ END
+
+ /* Cleanup */
+ if (ber) ber_free(ber, 0);
+
+ if ( LDAP_SUCCESS != lderr)
+ {
+ if (spec) delete_LDAPProxyAuth(spec);
+ spec = 0;
+ if ( NULL != errtextp ) {
+ *errtextp = errstring;
+ }
+ }
+
+ *out = spec;
+
+ return lderr;
+}
+
+/*
+ * proxyauth_dn - find the users DN in the proxyauth control if it is
+ * present. The return value has been malloced for you.
+ *
+ * Returns an LDAP error code. If anything than LDAP_SUCCESS is returned,
+ * the error should be returned to the client. LDAP_SUCCESS is always
+ * returned if the proxy auth control is not present or not critical.
+ */
+int
+acl_get_proxyauth_dn( Slapi_PBlock *pb, char **proxydnp, char **errtextp )
+{
+ char *dn = 0;
+ LDAPProxyAuth *spec = 0;
+ int rv, lderr = LDAP_SUCCESS; /* optimistic */
+
+ BEGIN
+ struct berval *spec_ber;
+ LDAPControl **controls;
+ int present;
+ int critical;
+ int version = 1;
+
+ rv = slapi_pblock_get( pb, SLAPI_REQCONTROLS, &controls );
+ if (rv) break;
+
+ present = slapi_control_present( controls, LDAP_CONTROL_PROXYAUTH,
+ &spec_ber, &critical );
+ if (!present) {
+ present = slapi_control_present( controls, LDAP_CONTROL_PROXIEDAUTH,
+ &spec_ber, &critical );
+ if (!present) break;
+ version = 2;
+ /*
+ * Note the according to the Proxied Authorization I-D, the
+ * control is always supposed to be marked critical by the
+ * client. If it is not, we return a protocolError.
+ */
+ if ( !critical ) {
+ lderr = LDAP_PROTOCOL_ERROR;
+ if ( NULL != errtextp ) {
+ *errtextp = "proxy control must be marked critical";
+ }
+ break;
+ }
+ }
+
+ rv = parse_LDAPProxyAuth(spec_ber, version, errtextp, &spec);
+ if (LDAP_SUCCESS != rv) {
+ if ( critical ) {
+ lderr = rv;
+ }
+ break;
+ }
+
+ dn = slapi_ch_strdup(spec->auth_dn);
+ if (slapi_dn_isroot(dn) ) {
+ lderr = LDAP_UNWILLING_TO_PERFORM;
+ *errtextp = "Proxy dn should not be rootdn";
+ break;
+
+ }
+ END
+
+ if (spec) delete_LDAPProxyAuth(spec);
+
+ if ( NULL != proxydnp ) {
+ *proxydnp = dn;
+ } else {
+ slapi_ch_free( (void **)&dn );
+ }
+
+ return lderr;
+}
+
diff --git a/ldap/servers/plugins/acl/aclutil.c b/ldap/servers/plugins/acl/aclutil.c
new file mode 100644
index 00000000..168ca482
--- /dev/null
+++ b/ldap/servers/plugins/acl/aclutil.c
@@ -0,0 +1,1475 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+#include "acl.h"
+
+/**************************************************************************
+* Defines and usefuls stuff
+****************************************************************************/
+
+/*************************************************************************
+* Prototypes
+*************************************************************************/
+static void aclutil__typestr (int type , char str[]);
+static void aclutil__Ruletypestr (int type , char str[]);
+static char* __aclutil_extract_dn_component ( char **e_dns, int position,
+ char *attrName );
+static char* acl_get_final_component(char *macro_prefix) ;
+static char* acl_match_component( char *start, char *component);
+static int aclutil_compare_components( char * comp1, char *comp2);
+static int acl_find_comp_start(char * s, int pos );
+static PRIntn acl_ht_free_entry_and_value(PLHashEntry *he, PRIntn i,
+ void *arg);
+static PLHashNumber acl_ht_hash( const void *key);
+static PRIntn acl_ht_display_entry(PLHashEntry *he, PRIntn i, void *arg);
+
+/***************************************************************************/
+/* UTILITY FUNCTIONS */
+/***************************************************************************/
+int
+aclutil_str_appened(char **str1, const char *str2)
+{
+ int new_len;
+
+ if ( str1 == NULL || str2 == NULL )
+ return(0);
+
+ if ( *str1 == NULL ) {
+ new_len = strlen(str2) + 1;
+ *str1 = (char *)slapi_ch_malloc(new_len);
+ *str1[0] = 0;
+ } else {
+ new_len = strlen(*str1) + strlen(str2) + 1;
+ *str1 = (char *)slapi_ch_realloc(*str1, new_len);
+ }
+ if ( *str1 == NULL )
+ return(-1);
+
+ strcat(*str1, str2);
+ return(0);
+}
+
+/***************************************************************************/
+/* Print routines */
+/***************************************************************************/
+
+/* print eroror message returned from the ACL Library */
+#define ACLUTIL_ACLLIB_MSGBUF_LEN 200
+void
+acl_print_acllib_err (NSErr_t *errp , char * str)
+{
+ char msgbuf[ ACLUTIL_ACLLIB_MSGBUF_LEN ];
+
+ if ((NULL == errp ) || !slapi_is_loglevel_set ( SLAPI_LOG_ACL ) )
+ return;
+
+ aclErrorFmt(errp, msgbuf, ACLUTIL_ACLLIB_MSGBUF_LEN, 1);
+ msgbuf[ACLUTIL_ACLLIB_MSGBUF_LEN-1] = '\0';
+
+ if (msgbuf)
+ slapi_log_error(SLAPI_LOG_ACL, plugin_name,"ACL LIB ERR:(%s)(%s)\n",
+ msgbuf, str ? str: "NULL",0);
+}
+void
+aclutil_print_aci (aci_t *aci_item, char *type)
+{
+ char str[BUFSIZ];
+ const char *dn;
+
+ if ( ! slapi_is_loglevel_set ( SLAPI_LOG_ACL ) )
+ return;
+
+ if (!aci_item) {
+
+ slapi_log_error (SLAPI_LOG_ACL, plugin_name,
+ "acl__print_aci: Null item\n",0,0,0);
+ return;
+ }
+
+
+ slapi_log_error (SLAPI_LOG_ACL, plugin_name,
+ "***BEGIN ACL INFO[ Name:%s]***\n", aci_item->aclName);
+
+ slapi_log_error (SLAPI_LOG_ACL, plugin_name,
+ "ACL Index:%d ACL_ELEVEL:%d\n", aci_item->aci_index, aci_item->aci_elevel );
+ aclutil__access_str (aci_item->aci_access, str);
+ aclutil__typestr (aci_item->aci_type, &str[strlen(str)]);
+ slapi_log_error (SLAPI_LOG_ACL, plugin_name,
+ "ACI type:(%s)\n", str);
+
+ aclutil__Ruletypestr (aci_item->aci_ruleType, str);
+ slapi_log_error (SLAPI_LOG_ACL, plugin_name,
+ "ACI RULE type:(%s)\n",str);
+
+ dn = slapi_sdn_get_dn ( aci_item->aci_sdn );
+ slapi_log_error (SLAPI_LOG_ACL, plugin_name,
+ "Slapi_Entry DN:%s\n", escape_string_with_punctuation (dn, str));
+
+ slapi_log_error (SLAPI_LOG_ACL, plugin_name,
+ "***END ACL INFO*****************************\n");
+
+}
+void
+aclutil_print_err (int rv , const Slapi_DN *sdn, const struct berval* val,
+ char **errbuf)
+{
+ char ebuf [BUFSIZ];
+ /*
+ * The maximum size of line is ebuf_size + the log message
+ * itself (less than 200 characters for all but potentially ACL_INVALID_TARGET)
+ */
+ char line [BUFSIZ + 200];
+ char str [1024];
+ const char *dn;
+ char *lineptr = line;
+ char *newline = NULL;
+
+ if ( rv >= 0)
+ return;
+
+ if (val->bv_len > 0 && val->bv_val != NULL) {
+ sprintf (str, "%.1023s", val->bv_val);
+ } else {
+ str[0] = '\0';
+ }
+
+ dn = slapi_sdn_get_dn ( sdn );
+ if (dn && (rv == ACL_INVALID_TARGET) && ((strlen(dn) + strlen(str)) > BUFSIZ)) {
+ /*
+ * if (str_length + dn_length + 200 char message) > (BUFSIZ + 200) line
+ * we have to make space for a bigger line...
+ */
+ newline = slapi_ch_malloc(strlen(dn) + strlen(str) + 200);
+ lineptr = newline;
+ }
+
+ switch (rv) {
+ case ACL_TARGET_FILTER_ERR:
+ sprintf (line, "ACL Internal Error(%d): "
+ "Error in generating the target filter for the ACL(%s)\n",
+ rv, escape_string_with_punctuation (str, ebuf));
+ break;
+ case ACL_TARGETATTR_FILTER_ERR:
+ sprintf (line, "ACL Internal Error(%d): "
+ "Error in generating the targetattr filter for the ACL(%s)\n",
+ rv, escape_string_with_punctuation (str, ebuf));
+ break;
+ case ACL_TARGETFILTER_ERR:
+ sprintf (line, "ACL Internal Error(%d): "
+ "Error in generating the targetfilter filter for the ACL(%s)\n",
+ rv, escape_string_with_punctuation (str, ebuf));
+ break;
+ case ACL_SYNTAX_ERR:
+ sprintf (line, "ACL Syntax Error(%d):%s\n",
+ rv, escape_string_with_punctuation (str, ebuf));
+ break;
+ case ACL_ONEACL_TEXT_ERR:
+ sprintf (line, "ACL Syntax Error in the Bind Rules(%d):%s\n",
+ rv, escape_string_with_punctuation (str, ebuf));
+ break;
+ case ACL_ERR_CONCAT_HANDLES:
+ sprintf (line, "ACL Internal Error(%d): "
+ "Error in Concatenating List handles\n",
+ rv);
+ break;
+ case ACL_INVALID_TARGET:
+ sprintf (lineptr, "ACL Invalid Target Error(%d): "
+ "Target is beyond the scope of the ACL(SCOPE:%s)",
+ rv, dn ? escape_string_with_punctuation (dn, ebuf) : "NULL");
+ sprintf (lineptr + strlen(lineptr), " %s\n", escape_string_with_punctuation (str, ebuf));
+ break;
+ case ACL_INVALID_AUTHMETHOD:
+ sprintf (line, "ACL Multiple auth method Error(%d):"
+ "Multiple Authentication Metod in the ACL(%s)\n",
+ rv, escape_string_with_punctuation (str, ebuf));
+ break;
+ case ACL_INVALID_AUTHORIZATION:
+ sprintf (line, "ACL Syntax Error(%d):"
+ "Invalid Authorization statement in the ACL(%s)\n",
+ rv, escape_string_with_punctuation (str, ebuf));
+ break;
+ case ACL_INCORRECT_ACI_VERSION:
+ sprintf (line, "ACL Syntax Error(%d):"
+ "Incorrect version Number in the ACL(%s)\n",
+ rv, escape_string_with_punctuation (str, ebuf));
+ break;
+ default:
+ sprintf (line, "ACL Internal Error(%d):"
+ "ACL generic error (%s)\n",
+ rv, escape_string_with_punctuation (str, ebuf));
+ break;
+ }
+
+ if (errbuf) {
+ /* If a buffer is provided, then copy the error */
+ aclutil_str_appened(errbuf, lineptr );
+ }
+
+ slapi_log_error( SLAPI_LOG_FATAL, plugin_name, "%s", lineptr);
+ if (newline) slapi_ch_free((void **) &newline);
+}
+
+/***************************************************************************
+* Convert access to str
+***************************************************************************/
+char*
+aclutil__access_str (int type , char str[])
+{
+ char *p;
+
+ str[0] = '\0';
+ p = str;
+
+ if (type & SLAPI_ACL_COMPARE) {
+ strcpy (p, "compare ");
+ p = strchr (p, '\0');
+ }
+ if (type & SLAPI_ACL_SEARCH) {
+ strcpy (p, "search ");
+ p = strchr (p, '\0');
+ }
+ if (type & SLAPI_ACL_READ) {
+ strcpy (p, "read ");
+ p = strchr (p, '\0');
+ }
+ if (type & SLAPI_ACL_WRITE) {
+ strcpy (p, "write ");
+ p = strchr (p, '\0');
+ }
+ if (type & SLAPI_ACL_DELETE) {
+ strcpy (p, "delete ");
+ p = strchr (p, '\0');
+ }
+ if (type & SLAPI_ACL_ADD) {
+ strcpy (p, "add ");
+ p = strchr (p, '\0');
+ }
+ if (type & SLAPI_ACL_SELF) {
+ strcpy (p, "self ");
+ p = strchr (p, '\0');
+ }
+ if (type & SLAPI_ACL_PROXY) {
+ strcpy (p, "proxy ");
+ }
+ return str;
+}
+
+/***************************************************************************
+* Convert type to str
+***************************************************************************/
+static void
+aclutil__typestr (int type , char str[])
+{
+ char *p;
+
+ /* Start copying in at whatever location is passed in */
+
+ p = str;
+
+ if (type & ACI_TARGET_DN) {
+ strcpy (p, "target_DN ");
+ p = strchr (p, '\0');
+ }
+ if (type & ACI_TARGET_ATTR) {
+ strcpy (p, "target_attr ");
+ p = strchr (p, '\0');
+ }
+ if (type & ACI_TARGET_PATTERN) {
+ strcpy (p, "target_patt ");
+ p = strchr (p, '\0');
+ }
+ if ((type & ACI_TARGET_ATTR_ADD_FILTERS) | (type & ACI_TARGET_ATTR_DEL_FILTERS)) {
+ strcpy (p, "targetattrfilters ");
+ p = strchr (p, '\0');
+ }
+ if (type & ACI_TARGET_FILTER) {
+ strcpy (p, "target_filter ");
+ p = strchr (p, '\0');
+ }
+ if (type & ACI_ACLTXT) {
+ strcpy (p, "acltxt ");
+ p = strchr (p, '\0');
+ }
+ if (type & ACI_TARGET_NOT) {
+ strcpy (p, "target_not ");
+ p = strchr (p, '\0');
+ }
+ if (type & ACI_TARGET_ATTR_NOT) {
+ strcpy (p, "target_attr_not ");
+ p = strchr (p, '\0');
+ }
+ if (type & ACI_TARGET_FILTER_NOT) {
+ strcpy (p, "target_filter_not ");
+ p = strchr (p, '\0');
+ }
+
+ if (type & ACI_HAS_ALLOW_RULE) {
+ strcpy (p, "allow_rule ");
+ p = strchr (p, '\0');
+ }
+ if (type & ACI_HAS_DENY_RULE) {
+ strcpy (p, "deny_rule ");
+ p = strchr (p, '\0');
+ }
+}
+static void
+aclutil__Ruletypestr (int type , char str[])
+{
+ char *p;
+
+ str[0] = '\0';
+ p = str;
+ if ( type & ACI_USERDN_RULE) {
+ strcpy (p, "userdn ");
+ p = strchr (p, '\0');
+ }
+ if ( type & ACI_USERDNATTR_RULE) {
+ strcpy (p, "userdnattr ");
+ p = strchr (p, '\0');
+ }
+ if ( type & ACI_USERATTR_RULE) {
+ strcpy (p, "userattr ");
+ p = strchr (p, '\0');
+ }
+ if ( type & ACI_GROUPDN_RULE) {
+ strcpy (p, "groupdn ");
+ p = strchr (p, '\0');
+ }
+ if ( type & ACI_GROUPDNATTR_RULE) {
+ strcpy (p, "groupdnattr ");
+ p = strchr (p, '\0');
+ }
+ if ( type & ACI_ROLEDN_RULE) {
+ strcpy (p, "roledn ");
+ p = strchr (p, '\0');
+ }
+ if ( type & ACI_IP_RULE) {
+ strcpy (p, "ip ");
+ p = strchr (p, '\0');
+ }
+ if ( type & ACI_DNS_RULE) {
+ strcpy (p, "dns ");
+ p = strchr (p, '\0');
+ }
+ if ( type & ACI_TIMEOFDAY_RULE) {
+ strcpy (p, "timeofday ");
+ p = strchr (p, '\0');
+ }
+ if ( type & ACI_DAYOFWEEK_RULE) {
+ strcpy (p, "dayofweek ");
+ p = strchr (p, '\0');
+ }
+ if ( type & ACI_AUTHMETHOD_RULE) {
+ strcpy (p, "authmethod ");
+ p = strchr (p, '\0');
+ }
+ if ( type & ACI_PARAM_DNRULE) {
+ strcpy (p, "paramdn ");
+ p = strchr (p, '\0');
+ }
+ if ( type & ACI_PARAM_ATTRRULE) {
+ strcpy (p, "paramAttr ");
+ p = strchr (p, '\0');
+ }
+}
+/*
+** acl_gen_err_msg
+** This function is called by backend to generate the error message
+** if access is denied.
+*/
+void
+acl_gen_err_msg(int access, char *edn, char *attr, char **errbuf)
+{
+ char *line = NULL;
+
+ if (access & SLAPI_ACL_WRITE) {
+ line = PR_smprintf(
+ "Insufficient 'write' privilege to the '%s' attribute of entry '%s'.\n",
+ attr ? attr: "NULL", edn);
+ } else if ( access & SLAPI_ACL_ADD ) {
+ line = PR_smprintf(
+ "Insufficient 'add' privilege to add the entry '%s'.\n",edn);
+
+ } else if ( access & SLAPI_ACL_DELETE ) {
+ line = PR_smprintf(
+ "Insufficient 'delete' privilege to delete the entry '%s'.\n",edn);
+ }
+ aclutil_str_appened(errbuf, line );
+
+ if (line) {
+ PR_smprintf_free(line);
+ line = NULL;
+ }
+}
+short
+aclutil_gen_signature ( short c_signature )
+{
+ short o_signature;
+ o_signature = c_signature ^ (slapi_rand() % 32768);
+ if (!o_signature)
+ o_signature = c_signature ^ (slapi_rand() % 32768);
+
+ return o_signature;
+}
+
+void
+aclutil_print_resource( struct acl_pblock *aclpb, char *right , char *attr, char *clientdn )
+{
+
+ char str[BUFSIZ];
+ const char *dn;
+
+
+ if ( aclpb == NULL) return;
+
+ if ( ! slapi_is_loglevel_set ( SLAPI_LOG_ACL ) )
+ return;
+
+ slapi_log_error (SLAPI_LOG_ACL, plugin_name, " ************ RESOURCE INFO STARTS *********\n",0,0,0);
+ slapi_log_error (SLAPI_LOG_ACL, plugin_name, " Client DN: %s\n",
+ clientdn ? escape_string_with_punctuation (clientdn, str) : "NULL", 0,0);
+ aclutil__access_str (aclpb->aclpb_access, str);
+ aclutil__typestr (aclpb->aclpb_res_type, &str[strlen(str)]);
+ slapi_log_error (SLAPI_LOG_ACL, plugin_name, " resource type:%d(%s)\n",
+ aclpb->aclpb_res_type, str, 0);
+
+ dn = slapi_sdn_get_dn ( aclpb->aclpb_curr_entry_sdn );
+ slapi_log_error (SLAPI_LOG_ACL, plugin_name, " Slapi_Entry DN: %s\n",
+ dn ? escape_string_with_punctuation ( dn , str) : "NULL",0,0);
+
+ slapi_log_error (SLAPI_LOG_ACL, plugin_name, " ATTR: %s\n", attr ? attr : "NULL",0,0);
+ slapi_log_error (SLAPI_LOG_ACL, plugin_name, " rights:%s\n", right ? right: "NULL",0,0);
+ slapi_log_error (SLAPI_LOG_ACL, plugin_name, " ************ RESOURCE INFO ENDS *********\n",0,0,0);
+}
+/*
+ * The input string contains a rule like
+ * "cn=helpdesk, ou=$attr.deptName, o=$dn.o, o=ISP"
+ *
+ * Where $attr -- means look into the attribute list for values
+ * $dn -- means look into the entry's dn
+ *
+ * We extract the values from the entry and returned a string
+ * with the values added.
+ * For "$attr" rule - if we find multiple values then it is
+ * the pattern is not expanded.
+ * For "$dn" rule, if we find multiple of them, we use the relative
+ * position.
+ * NOTE: The caller is responsible in freeing the memory.
+ */
+char *
+aclutil_expand_paramString ( char *str, Slapi_Entry *e )
+{
+
+ char **e_dns;
+ char **a_dns;
+ char *attrName;
+ char *s, *p;
+ char *attrVal;
+ int i, len;
+ int ncomponents, type;
+ int rc = -1;
+ char *buf = NULL;
+
+
+ e_dns = ldap_explode_dn ( slapi_entry_get_ndn ( e ), 0 );
+ a_dns = ldap_explode_dn ( str, 0 );
+
+ i = 0;
+ ncomponents = 0;
+ while ( a_dns[ncomponents] )
+ ncomponents++;
+
+
+ for (i=0; i < ncomponents; i++ ) {
+
+ /* Look for"$" char */
+ if ( (s = strchr ( a_dns[i], '$') ) != NULL) {
+ p = s;
+ s++;
+ if ( strncasecmp (s, "dn", 2) == 0 )
+ type = 1;
+ else if ( strncasecmp (s, "attr", 4) == 0 )
+ type = 2;
+ else {
+ /* error */
+ goto cleanup;
+ }
+ *p = '\0';
+ aclutil_str_appened ( &buf,a_dns[i]);
+
+ if ( type == 1 ) {
+ /* xyz = $dn.o */
+ s +=3;
+ attrName = s;
+
+ attrVal = __aclutil_extract_dn_component (e_dns,
+ ncomponents-i, attrName);
+ if ( NULL == attrVal ) /*error*/
+ goto cleanup;
+
+ } else {
+ Slapi_Attr *attr;
+ const struct berval *attrValue;
+ int kk;
+ Slapi_Value *sval, *t_sval;
+
+
+ /* The pattern is x=$attr.o" */
+ s +=5;
+ attrName = s;
+
+ slapi_entry_attr_find ( e, attrName, &attr );
+ if ( NULL == attr )
+ goto cleanup;
+
+ kk= slapi_attr_first_value ( attr, &sval );
+ if ( kk != -1 ) {
+ t_sval = sval;
+ kk= slapi_attr_next_value( attr, kk, &sval );
+ if ( kk != -1 ) /* can't handle multiple --error */
+ goto cleanup;
+ }
+ attrValue = slapi_value_get_berval ( t_sval );
+ attrVal = attrValue->bv_val;
+ }
+ } else {
+ attrVal = a_dns[i];
+ }
+ aclutil_str_appened ( &buf, attrVal);
+ aclutil_str_appened ( &buf, ",");
+ }
+ rc = 0; /* everything is okay*/
+ /* remove the last comma */
+ len = strlen ( buf);
+ buf[len-1] = '\0';
+
+cleanup:
+
+ ldap_value_free ( a_dns );
+ ldap_value_free ( e_dns );
+ if ( 0 != rc ) /* error */ {
+ slapi_ch_free ( (void **) &buf );
+ buf = NULL;
+ }
+
+ return buf;
+}
+static char *
+__aclutil_extract_dn_component ( char **e_dns, int position, char *attrName )
+{
+
+ int i, matched, len;
+ char *s;
+ int matchedPosition;
+
+ len = strlen ( attrName );
+
+ /* First check if there thare are multiple of these */
+ i = matched = 0;
+ while ( e_dns[i] ) {
+ if (0 == strncasecmp (e_dns[i], attrName, len) ) {
+ matched++;
+ matchedPosition = i;
+ }
+ i++;
+ }
+
+ if (!matched )
+ return NULL;
+
+ if ( matched > 1 ) {
+ matchedPosition = i - position;
+ }
+
+ if ( NULL == e_dns[matchedPosition])
+ return NULL;
+
+ s = strstr ( e_dns[matchedPosition], "=");
+ if ( NULL == s)
+ return NULL;
+ else
+ return s+1;
+}
+
+/*
+ * Does the first component of ndn match the first component of match_this ?
+*/
+
+int
+acl_dn_component_match( const char *ndn, char *match_this, int component_number) {
+
+ return(1);
+}
+
+/*
+ * Here, ndn is a resource dn and match_this is a dn, containing a macro, ($dn).
+ *
+ * eg. ndn is cn=fred,ou=groups,ou=people,ou=icnc,o=ISP and
+ * match_this is "ou=Groups,($dn),o=ISP" or
+ * "cn=*,ou=Groups,($dn),o=ISP".
+ *
+ * They match if:
+ * match_this is a suffix of ndn
+ *
+ * It returns NULL, if they do not match.
+ * Otherwise it returns a copy of the substring of ndn that matches the ($dn).
+ *
+ * eg. in the above example, "ou=people,ou=icnc"
+*/
+
+char *
+acl_match_macro_in_target( const char *ndn, char * match_this,
+ char *macro_ptr) {
+
+ char *macro_prefix = NULL;
+ int macro_prefix_len = 0;
+ char *macro_suffix = NULL;
+ char *tmp_ptr = NULL;
+ char *matched_val = NULL;
+ char *ndn_suffix_start = NULL;
+ char *macro_prefix_final_component = NULL;
+ char *ret_val = NULL;
+ int ndn_len = 0;
+ int macro_suffix_len = 0;
+ int ndn_prefix_len = 0;
+ int ndn_prefix_end = 0;
+ int matched_val_len = 0;
+
+ /*
+ * First, grab the macro_suffix--the bit after the ($dn)
+ *
+ */
+
+ if (strlen(macro_ptr) == strlen(ACL_TARGET_MACRO_DN_KEY)) {
+ macro_suffix = NULL; /* just ($dn) */
+ } else {
+ if ( macro_ptr[strlen(ACL_TARGET_MACRO_DN_KEY)] == ',') {
+ macro_suffix = &macro_ptr[strlen(ACL_TARGET_MACRO_DN_KEY) + 1];
+ } else {
+ macro_suffix = &macro_ptr[strlen(ACL_TARGET_MACRO_DN_KEY)];
+ }
+ }
+
+ /*
+ * First ensure that the suffix of match_this is
+ * a suffix of ndn.
+ */
+
+ ndn_len = strlen(ndn);
+ if ( macro_suffix != NULL) {
+ macro_suffix_len = strlen(macro_suffix);
+ if( macro_suffix_len >= ndn_len ) {
+
+ /*
+ * eg ndn: o=icnc,o=sun.com
+ * match_this: ($dn),o=icnc,o=sun.com
+ */
+ return(NULL); /* ($dn) must match something. */
+ } else {
+ /*
+ * eg ndn: ou=People,o=icnc,o=sun.com
+ * match_this: ($dn),o=icnc,o=sun.com
+ *
+ * we can do a direct strncmp() because we know that
+ * there can be no "*" after the ($dn)...by definition.
+ */
+ if (strncasecmp( macro_suffix, &ndn[ndn_len-macro_suffix_len],
+ macro_suffix_len) != 0) {
+ return(NULL); /* suffix must match */
+ }
+ }
+ }
+
+ /* Start of the suffix in ndn...and it matched. */
+ ndn_suffix_start = (char*)&ndn[ndn_len-macro_suffix_len];
+
+ /* Here, macro_suffix is a suffix of ndn.
+ *
+ *
+ * Now, look at macro_prefix, if it is NULL, then ($dn) matches
+ * ndn[0..ndn_len-macro_suffix_len].
+ * (eg, ndn: cn=fred,ou=People,o=sun.com
+ * match_this: ($dn),o=sun.com.
+ *
+ */
+
+ macro_prefix = slapi_ch_strdup(match_this);
+
+ /* we know it's got a $(dn) */
+ tmp_ptr = strstr(macro_prefix, ACL_TARGET_MACRO_DN_KEY);
+ *tmp_ptr = '\0';
+ /* There may be a NULL prefix eg. match_this: ($dn),o=sun.com */
+ macro_prefix_len = strlen(macro_prefix);
+ if (macro_prefix_len == 0) {
+ slapi_ch_free((void **) &macro_prefix);
+ macro_prefix = NULL;
+ }
+
+ if (macro_prefix == NULL ) {
+ /*
+ * ($dn) matches ndn[0..ndn_len-macro_suffix_len]
+ */
+ int matched_val_len = 0;
+
+ matched_val_len = ndn_len-macro_suffix_len;
+
+ matched_val = (char *)slapi_ch_malloc(matched_val_len + 1);
+ strncpy(matched_val, ndn, ndn_len-macro_suffix_len);
+ /*
+ * Null terminate matched_val, removing trailing "," if there is
+ * one.
+ */
+ if (matched_val_len > 1) {
+ if (matched_val[matched_val_len-1] == ',' ) {
+ matched_val[matched_val_len-1] = '\0';
+ } else {
+ matched_val[matched_val_len] = '\0';
+ }
+ }
+ ret_val = matched_val;
+ } else {
+
+
+ /*
+ * If it is not NULL, then if macro_prefix contains a * then
+ * it needs to be an exact prefix of ndn (modulo the * component
+ * which matches anything) becuase that's the semantics
+ * of target patterns containing *'s, except that we just
+ * make it match one component.
+ * If it is such a prefix then ($dn) matches that portion of ndn
+ * from the end of the prefix, &ndn[ndn_prefix_end] to
+ * ndn_suffix_start.
+ * If ndn_prefix_len > ndn_len-macro_suffix_len then return(NULL),
+ * otherwise $(dn) matches ndn[ndn_prefix_len..ndn_len-macro_suffix_len].
+ *
+ *
+ * eg. ndn: cn=fred,ou=P,o=sun.com
+ * match_this: cn=*,($dn),o=sun.com
+ */
+
+ if ( strstr(macro_prefix, "=*") != NULL ) {
+ int exact_match = 0;
+
+ ndn_prefix_len = acl_match_prefix( macro_prefix, ndn, &exact_match);
+ if ( ndn_prefix_len != -1 ) {
+
+ /*
+ * ndn[0..ndn_prefix_len] is the prefix in ndn.
+ * ndn[ndn_prefix_len..ndn_len-macro_suffix_len] is the
+ * matched string.
+ */
+ if (ndn_prefix_len >= ndn_len-macro_suffix_len) {
+
+ /*
+ * eg ndn: cn=fred,ou=People,o=icnc,o=sun.com
+ * cn=*,ou=People,o=icnc,($dn),o=icnc,o=sun.com
+ */
+
+ ret_val = NULL; /* matched string is empty */
+ } else {
+
+ /*
+ * eg ndn: cn=fred,ou=People,o=icnc,o=sun.com
+ * cn=*,ou=People,($dn),o=sun.com
+ */
+
+ matched_val_len = ndn_len-macro_suffix_len-ndn_prefix_len;
+ matched_val = (char *)slapi_ch_malloc(matched_val_len + 1);
+ strncpy(matched_val, &ndn[ndn_prefix_len], matched_val_len);
+ if (matched_val_len > 1) {
+ if (matched_val[matched_val_len-1] == ',' ) {
+ matched_val[matched_val_len-1] = '\0';
+ } else {
+ matched_val[matched_val_len] = '\0';
+ }
+ }
+ matched_val[matched_val_len] = '\0';
+ ret_val = matched_val;
+ }
+ } else {
+ /* Was not a prefix so not a match */
+ ret_val = NULL;
+ }
+ } else {
+
+ /*
+ *
+ * If macro_prefix is not NULL and it does not
+ * contain a =* then
+ * we need to ensure that macro_prefix is a substring
+ * ndn.
+ * If it is and the position of the character after it's end in
+ * ndn is
+ * ndn_prefix_end then ($dn) matches
+ * ndn[ndn_prefix_end..ndn_len-macro_suffix_len].
+ *
+ *
+ * One important principal is that ($dn) matches a maximal
+ * chunk--this way it will serve to make the link
+ * between resources and users at each level of the structure.
+ *
+ * eg. ndn: ou=Groups,ou=Groups,ou=Groups,c=fr
+ * macro_prefix: ou=Groups,($dn),c=fr
+ *
+ * then ($dn) matches ou=Groups,ou=Groups.
+ *
+ *
+ *
+ * If it is not a substring, then there is no match.
+ * If it is a substring and
+ * ndn[ndn_prefix_end..ndn_len-macro_suffix_len] is empty then
+ * it's also not a match as we demand that ($dn) match a non-empty
+ * string.
+ *
+ *
+ *
+ * (eg. ndn: cn=fred,o=icnc,ou=People,o=sun.com
+ * match_this: o=icnc,($dn),o=sun.com.)
+ *
+ *
+ * (eg. ndn: cn=fred,o=menlo park,ou=People,o=icnc,o=sun.com
+ * match_this: o=menlo park,ou=People,($dn),o=sun.com
+ *
+ */
+
+ ndn_prefix_end = acl_strstr((char *)ndn, macro_prefix);
+ if ( ndn_prefix_end == -1) {
+ ret_val = NULL;
+ } else {
+ /* Is a substring */
+
+ ndn_prefix_end += macro_prefix_len;
+
+ /*
+ * make sure the matching part is non-empty:
+ *
+ * ndn[ndn_prefix_end..mndn_len-macro_suffix_len].
+ */
+
+ if ( ndn_prefix_end >= ndn_len-macro_suffix_len) {
+ ret_val = NULL;
+ } else {
+ /*
+ * ($dn) matches the non-empty string segment
+ * ndn[ndn_prefix_end..mndn_len-macro_suffix_len]
+ * the -1 is because macro_suffix_eln does not include
+ * the coma before the suffix.
+ */
+
+ matched_val_len = ndn_len-macro_suffix_len-
+ ndn_prefix_end - 1;
+
+ matched_val = (char *)slapi_ch_malloc(matched_val_len + 1);
+ strncpy(matched_val, &ndn[ndn_prefix_end],
+ matched_val_len);
+ matched_val[matched_val_len] = '\0';
+
+ ret_val = matched_val;
+ }
+ }
+ }/* contains an =* */
+ slapi_ch_free((void **) &macro_prefix);
+ }/* macro_prefix != NULL */
+
+ return(ret_val);
+}
+
+/*
+ * Checks to see if macro_prefix is an exact prefix of ndn.
+ * macro_prefix may contain a * component.
+ *
+ * The length of the matched prefix in ndn is returned.
+ * If it was not a match, a negative int is returned.
+ * Also, if the string matched exactly,
+ * exact_match is set to 1, other wise it was a proper prefix.
+ *
+*/
+
+int
+acl_match_prefix( char *macro_prefix, const char *ndn, int *exact_match) {
+
+ int macro_index = 0;
+ int ndn_index = 0;
+ int ret_code = -1;
+ char *curr_macro_component = NULL;
+ char *curr_ndn_component = NULL;
+ int matched = 0;
+ int macro_prefix_len = 0;
+ int ndn_len = 0;
+ int i = 0;
+ int j = 0;
+ int done = 0;
+ int t = 0;
+ char * tmp_str = NULL;
+ int k,l = 0;
+
+ *exact_match = 0; /* default to not an exact match */
+
+ /* The NULL prefix matches everthing*/
+ if (macro_prefix == NULL) {
+ if ( ndn == NULL ) {
+ *exact_match = 1;
+ }
+ return(0);
+ } else {
+ /* macro_prefix is not null, so if ndn is NULL, it's not a match. */
+ if ( ndn == NULL) {
+ return(-1);
+ }
+ }
+ /*
+ * Here, neither macro_prefix nor ndn are NULL.
+ *
+ * eg. macro_prefix: cn=*,ou=people,o=sun.com
+ * ndn : cn=fred,ou=people,o=sun.com
+ */
+
+
+ /*
+ * Here, there is a component with a * (eg. cn=* ) so
+ * we need to step through the macro_prefix, and where there is
+ * such a * match on that component,
+ * when we run out of * componenets, jsut do a straight match.
+ *
+ * Out of interest, the following extended regular expression
+ * will match just one ou rdn value from a string:
+ * "^uid=admin,ou=\([^,]*\\\,\)*[^,]*,o=sun.com$"
+ *
+ *
+ * eg. cn=fred,ou=People,o=sun.com
+ *
+ *
+ * s points to the = of the component.
+ */
+
+ macro_prefix_len = strlen(macro_prefix);
+ ndn_len = strlen(ndn);
+ i = 0;
+ j = 0;
+ done = 0;
+ while ( !done ) {
+
+ /* Here ndn[0..i] has matched macro_prefix[0..j] && j<= i
+ * i<=ndn_len j<=macro_prefix_len */
+
+ if ( (t = acl_strstr(&macro_prefix[j], "=*")) < 0 ) {
+ /*
+ * No more *'s, do a straight match on
+ * macro_prefix[j..macro_prefix_len] and
+ * ndn[i..macro_prefix_len]
+ */
+
+ if( macro_prefix_len-j > ndn_len-i) {
+ /* Not a prefix, nor a match */
+ *exact_match = 0;
+ ret_code = -1;
+ done = 1;
+ } else {
+ /*
+ * ndn_len-i >= macro_prefix_len - j
+ * if macro_prefix_len-j is 0, then
+ * it's a null prefix, so it matches.
+ * If in addition ndn_len-i is 0 then it's
+ * an exact match.
+ * Otherwise, do the cmp.
+ */
+
+ if ( macro_prefix_len-j == 0) {
+ done = 1;
+ ret_code = i;
+ if ( ndn_len-i == 0) {
+ *exact_match = 1;
+ }
+ }else {
+
+ if (strncasecmp(&macro_prefix[j], &ndn[i],
+ macro_prefix_len-j) == 0) {
+ *exact_match = (macro_prefix_len-j == ndn_len-i);
+ ret_code = i + macro_prefix_len -j;
+ done = 1;
+ } else {
+ /* not a prefix not a match */
+ *exact_match = 0;
+ ret_code = -1;
+ done = 1;
+ }
+ }
+ }
+ }else {
+ /*
+ * Is another * component, so:
+ * 1. match that component in macro_prefix (at pos k say)
+ * with the corresponding compoent (at pos l say ) in ndn
+ *
+ * 2. match the intervening string ndn[i..l] and
+ * macro_prefix[j..k].
+ */
+
+ /* First, find the start of the component in macro_prefix. */
+
+ t++; /* move to the--this way we will look for "ou=" in ndn */
+ k = acl_find_comp_start(macro_prefix, t);
+
+ /* Now identify that component in ndn--if it's not there no match */
+ tmp_str = slapi_ch_malloc(t-k+1);
+ strncpy(tmp_str, &macro_prefix[k], t-k);
+ tmp_str[t-k] = '\0';
+ l = acl_strstr((char*)&ndn[i], tmp_str);
+ if (l == -1) {
+ *exact_match = 0;
+ ret_code = -1;
+ done = 1;
+ } else {
+ /*
+ * Found the comp in ndn, so the comp matches.
+ * Now test the intervening string segments:
+ * ndn[i..l] and macro_prefix[j..k]
+ */
+
+ if ( k-j != l-i ) {
+ *exact_match = 0;
+ ret_code = -1;
+ done = 1;
+ } else{
+ if (strncasecmp(&macro_prefix[j], &ndn[i], k-j) != 0) {
+ *exact_match = 0;
+ ret_code = -1;
+ done = 1;
+ } else {
+ /* Matched, so bump i and j and keep going.*/
+ i += acl_find_comp_end((char*)&ndn[l]);
+ j += acl_find_comp_end((char*)&macro_prefix[k]);
+ }
+ }
+ }
+ slapi_ch_free((void **)&tmp_str);
+ }
+ }/* while */
+
+ return(ret_code);
+
+}
+
+/*
+ * returns the index in s of where the component at position
+ * s[pos] starts.
+ * This is the index of the character after the first unescaped comma
+ * moving backwards in s from pos.
+ * If this is not found then return 0, ie. the start of the string.
+ * If the index returned is > strlen(s) then could not find it.
+ * only such case is if you pass ",", in which case there is no component start.
+*/
+
+static int
+acl_find_comp_start(char * s, int pos ) {
+
+ int i =0;
+ int comp_start = 0;
+
+ i = pos;
+ while( i > 0 && (s[i] != ',' ||
+ s[i-1] == '\\')) {
+ i--;
+ }
+ /*
+ * i == 0 || (s[i] == ',' && s[i-1] != '\\')
+ */
+ if (i==0) {
+ /* Got all the way with no unescaped comma */
+ if (s[i] == ',') {
+ comp_start = i+1;
+ } else {
+ comp_start = i;
+ }
+ } else { /* Found an unescaped comma */
+ comp_start = i + 1;
+ }
+
+ return( comp_start);
+}
+
+/*
+ * returns the index in s of the first character after the
+ * first unescaped comma.
+ * If ther is no such character, returns strlen(s);
+*/
+
+int
+acl_find_comp_end( char * s) {
+
+ int i = 0;
+ int s_len = 0;
+
+ s_len = strlen(s);
+
+ if ( s_len == 0 || s_len == 1) {
+ return(s_len);
+ }
+
+ /* inv: i+1<s_len && (s[i] == '\\' || s[i+1] != ',')*/
+
+ i = 0;
+ while( i+1 < s_len && (s[i] == '\\' ||
+ s[i+1] != ',')) {
+ i++;
+ }
+ if ( i + 1 == s_len) {
+ return(s_len);
+ } else {
+ return(i+2);
+ }
+}
+
+/*
+ * return the index in s where substr occurs, if none
+ * returns -1.
+*/
+
+int
+acl_strstr(char * s, char *substr) {
+
+ char *t = NULL;
+ char *tmp_str = NULL;
+
+ tmp_str = slapi_ch_strdup(s);
+
+ if ( (t = strstr(tmp_str, substr)) == NULL ) {
+ slapi_ch_free((void **)&tmp_str);
+ return(-1);
+ } else {
+ int l = 0;
+ *t = '\0';
+ l = strlen(tmp_str);
+ slapi_ch_free((void **)&tmp_str);
+ return(l);
+ }
+}
+
+/*
+ * replace all occurences of substr in s with replace_str.
+ *
+ * returns a malloced version of the patched string.
+*/
+
+char *
+acl_replace_str(char * s, char *substr, char* replace_with_str) {
+
+ char *str = NULL;
+ char *working_s, *suffix, *prefix, *patched;
+ int replace_with_len, substr_len, prefix_len, suffix_len;
+
+ if ( (str = strstr(s, substr)) == NULL) {
+ return(slapi_ch_strdup(s));
+ } else {
+
+
+ replace_with_len = strlen(replace_with_str);
+ substr_len = strlen(substr);
+
+ working_s = slapi_ch_strdup(s);
+ prefix = working_s;
+ str = strstr(prefix, substr);
+
+ while (str != NULL) {
+
+ /*
+ * working_s is a copy of the string to be patched
+ * str points to a substr to be patched
+ * prefix points to working_s
+ */
+
+ *str = '\0';
+
+ suffix = &str[substr_len];
+ prefix_len = strlen(prefix);
+ suffix_len = strlen(suffix);
+
+ patched = (char *)slapi_ch_malloc(prefix_len +
+ replace_with_len +
+ suffix_len +1 );
+ strcpy(patched, prefix);
+ strcat(patched, replace_with_str);
+ strcat(patched, suffix);
+
+ slapi_ch_free((void **)&working_s);
+
+ working_s = patched;
+ prefix = working_s;
+ str = strstr(prefix, substr);
+
+ }
+
+ return(working_s);
+ }
+
+}
+
+
+/*
+ * Start at index and return a malloced string that is the
+ * next component in dn (eg. "ou=People"),
+ * or NULL if couldn't find the next one.
+*/
+
+char *
+get_next_component(char *dn, int *index) {
+
+ int dn_len = strlen(dn);
+ int start_next = -1;
+ int i = 0;
+ char *ret_comp;
+
+ if (*index>= dn_len) {
+ return(NULL);
+ }
+
+ start_next = acl_find_comp_end( &dn[*index]);
+
+ if ( start_next >= dn_len ) {
+ *index = start_next;
+ return(NULL); /* no next comp */
+ }
+
+ /*
+ *Here, start_next should be the start of the next
+ * component--so far have not run off the end.
+ */
+
+ i = acl_find_comp_end( &dn[start_next]);
+
+ /*
+ * Here, the matched string is all from start_next to i.
+ */
+
+ ret_comp = (char *)slapi_ch_malloc(i - start_next +1);
+ memcpy( ret_comp, &dn[start_next], i-start_next);
+ ret_comp[i-start_next] = '\0';
+
+ return(ret_comp);
+}
+
+char *
+get_this_component(char *dn, int *index) {
+
+ int dn_len = strlen(dn);
+ int i = 0;
+ char *ret_comp;
+
+ if (*index>= dn_len) {
+ return(NULL);
+ }
+
+ if (dn_len == *index + 1) {
+ /* Just return a copy of the string. */
+ return(slapi_ch_strdup(dn));
+ }else {
+ /* *index + 1 < dn_len */
+ i = *index+1;
+ while( (dn[i] != '\0') && dn[i] != ',' && dn[i-1] != '\\') {
+ i += 1;
+ }
+
+ /*
+ * Here, the matched string is all from *index to i.
+ */
+
+ ret_comp = (char *)slapi_ch_malloc(i - *index +1);
+ memcpy( ret_comp, &dn[*index], i - *index);
+ ret_comp[i-*index] = '\0';
+
+ if (i < dn_len) {
+ /* Found a comma before the end */
+ *index = i + 1; /* skip it */
+ }
+
+ return(ret_comp);
+ }
+
+}
+
+/*
+ * return 1 if comp1==comp2,
+ * return 0 otherwise.
+ *
+ * the components might have *'s.
+ *
+ * eg: comp1: cn=*
+ * comp2: cn=fred
+ *
+ *
+*/
+
+static int
+aclutil_compare_components( char * comp1, char *comp2) {
+
+ char *tmp_str = NULL;
+
+ tmp_str = strstr( comp1, "=*");
+ if ( tmp_str == NULL) {
+
+ /* Just a straight cmp */
+
+ if (slapi_utf8casecmp((ACLUCHP)comp1, (ACLUCHP)comp2) == 0) {
+ return(1);
+ } else {
+ return(0);
+ }
+ } else {
+
+ char *tmp_comp1= NULL;
+ char *tmp_comp2 = NULL;
+ int ret_code = 0;
+
+ /* Here, just compare the bit before the = */
+
+ tmp_comp1 = slapi_ch_strdup(comp1);
+ tmp_comp2 = slapi_ch_strdup(comp2);
+
+ /*
+ * Probably need to verify it's not escaped--see code for looking for
+ * unescaped commas.
+ */
+
+ tmp_str = strstr(tmp_comp1, "=");
+ *tmp_str = '\0';
+
+ tmp_str = strstr(tmp_comp2, "=");
+ if ( tmp_str == NULL) {
+ ret_code = 0;
+ } else{
+
+ *tmp_str = '\0';
+
+ if (slapi_utf8casecmp((ACLUCHP)comp1, (ACLUCHP)comp2) == 0) {
+ ret_code = 1;
+ } else {
+ ret_code = 0;
+ }
+
+ slapi_ch_free((void **)&tmp_comp1);
+ slapi_ch_free((void **)&tmp_comp2);
+
+ return(ret_code);
+
+ }
+
+ }
+}
+
+/*
+ * return a pointer to the final component of macro_prefix.
+*/
+
+static char *
+acl_get_final_component(char *macro_prefix) {
+
+ return(NULL);
+}
+
+/*
+ *
+ *
+*/
+
+static char *
+acl_match_component( char *start, char *component) {
+
+
+ return(NULL);
+}
+
+/* acl hash table funcs */
+
+/*
+ * Add the key adn value to the ht.
+ * If it already exists then remove the old one and free
+ * the value.
+*/
+void acl_ht_add_and_freeOld(acl_ht_t * acl_ht,
+ PLHashNumber key,
+ char *value){
+ char *old_value = NULL;
+
+ if ( (old_value = (char *)acl_ht_lookup( acl_ht, key)) != NULL ) {
+ acl_ht_remove( acl_ht, key);
+ slapi_ch_free((void **)&old_value);
+ }
+
+ PL_HashTableAdd( acl_ht, (const void *)key, value);
+}
+
+/*
+ * Return a new acl_ht_t *
+*/
+acl_ht_t *acl_ht_new(void) {
+
+ return(PL_NewHashTable(30, acl_ht_hash, /* key hasher */
+ PL_CompareValues, /* keyCompare */
+ PL_CompareStrings, 0, 0)); /* value compare */
+}
+
+static PLHashNumber acl_ht_hash( const void *key) {
+
+ return( (PLHashNumber)key );
+}
+
+/* Free all the values in the ht */
+void acl_ht_free_all_entries_and_values( acl_ht_t *acl_ht) {
+
+ PL_HashTableEnumerateEntries( acl_ht, acl_ht_free_entry_and_value,
+ NULL);
+}
+
+static PRIntn
+acl_ht_free_entry_and_value(PLHashEntry *he, PRIntn i, void *arg)
+{
+
+ slapi_ch_free((void **)&he->value); /* free value */
+
+ /* Free this entry anfd go on to next one */
+ return ( HT_ENUMERATE_NEXT | HT_ENUMERATE_REMOVE);
+}
+
+/* Free all the values in the ht */
+void acl_ht_display_ht( acl_ht_t *acl_ht) {
+
+#ifdef DEBUG
+ PL_HashTableEnumerateEntries( acl_ht, acl_ht_display_entry, NULL);
+#endif
+}
+
+static PRIntn
+acl_ht_display_entry(PLHashEntry *he, PRIntn i, void *arg)
+{
+ PLHashNumber aci_index = (PLHashNumber)he->key;
+ char *matched_val = (char *)he->value;
+
+ LDAPDebug(LDAP_DEBUG_ACL,"macro ht entry: key='%d' matched_val='%s'"
+ "keyhash='%d'\n",
+ aci_index, (matched_val ? matched_val: "NULL"),
+ (PLHashNumber)he->keyHash);
+
+ return HT_ENUMERATE_NEXT;
+
+}
+
+/* remove this entry from the ht--doesn't free the value.*/
+void acl_ht_remove( acl_ht_t *acl_ht, PLHashNumber key) {
+
+ PL_HashTableRemove( acl_ht, (const void *)key);
+}
+
+/* Retrieve a pointer to the value of the entry with key */
+void *acl_ht_lookup( acl_ht_t *acl_ht,
+ PLHashNumber key) {
+
+ return( PL_HashTableLookup( acl_ht, (const void *)key) );
+}
+
+
+/***************************************************************************/
+/* E N D */
+/***************************************************************************/
+
diff --git a/ldap/servers/plugins/acl/libacl.def b/ldap/servers/plugins/acl/libacl.def
new file mode 100644
index 00000000..947638cd
--- /dev/null
+++ b/ldap/servers/plugins/acl/libacl.def
@@ -0,0 +1,16 @@
+; BEGIN COPYRIGHT BLOCK
+; Copyright 2001 Sun Microsystems, Inc.
+; Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+; All rights reserved.
+; END COPYRIGHT BLOCK
+;
+;
+;
+DESCRIPTION 'Netscape Directory Server 7.0 ACL Plugin'
+;CODE SHARED READ EXECUTE
+;DATA SHARED READ WRITE
+EXPORTS
+ acl_preopInit @1
+; unused @2
+ acl_init @3
+ plugin_init_debug_level @4
diff --git a/ldap/servers/plugins/chainingdb/Makefile b/ldap/servers/plugins/chainingdb/Makefile
new file mode 100644
index 00000000..4f47e480
--- /dev/null
+++ b/ldap/servers/plugins/chainingdb/Makefile
@@ -0,0 +1,93 @@
+#
+# BEGIN COPYRIGHT BLOCK
+# Copyright 2001 Sun Microsystems, Inc.
+# Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+# All rights reserved.
+# END COPYRIGHT BLOCK
+#
+#
+# GNU Makefile for Directory Server "Chaining Backend" plugin
+#
+
+LDAP_SRC = ../../..
+
+MCOM_ROOT = ../../../../..
+
+NOSTDCLEAN=true # don't let nsconfig.mk define target clean
+NOSTDSTRIP=true # don't let nsconfig.mk define target strip
+NSPR20=true # probably should be defined somewhere else (not sure where)
+
+OBJDEST = $(OBJDIR)/lib/libcb
+LIBDIR = $(LIB_RELDIR)
+
+include $(MCOM_ROOT)/ldapserver/nsconfig.mk
+include $(LDAP_SRC)/nsldap.mk
+
+ifeq ($(ARCH), WINNT)
+DEF_FILE:=./libcb.def
+endif
+
+CFLAGS+=$(SLCFLAGS)
+
+INCLUDES += -I$(LDAP_SRC)/servers/slapd
+
+CB_OBJS= cb_temp.o cb_init.o cb_config.o cb_instance.o cb_start.o cb_search.o cb_utils.o cb_add.o cb_delete.o cb_schema.o \
+cb_acl.o cb_modify.o cb_compare.o cb_modrdn.o cb_abandon.o cb_conn_stateless.o cb_bind.o cb_unbind.o cb_monitor.o \
+cb_controls.o cb_size.o cb_test.o cb_close.o cb_cleanup.o cb_debug.o
+
+OBJS = $(addprefix $(OBJDEST)/, $(CB_OBJS))
+
+ifeq ($(ARCH), WINNT)
+LIBCB_DLL_OBJ = $(addprefix $(OBJDEST)/, cbdllmain.o)
+endif
+
+LIBCB= $(addprefix $(LIBDIR)/, $(CB_DLL).$(DLL_SUFFIX))
+
+ifeq ($(ARCH), WINNT)
+EXTRA_LIBS_DEP += $(LIBSLAPD_DEP) $(LDAP_LIBUTIL_DEP) $(LDAP_COMMON_LIBS_DEP)
+EXTRA_LIBS_DEP += $(LDAPSDK_DEP) $(SECURITY_DEP) $(NSPR_DEP)
+EXTRA_LIBS += $(LIBSLAPD) $(LDAP_SDK_LIBLDAP_DLL) $(LDAP_LIBUTIL) $(LDAP_COMMON_LIBS) $(SECURITYLINK) $(NSPRLINK)
+
+endif
+
+
+ifeq ($(ARCH), WINNT)
+DLL_LDFLAGS += -def:"./libcb.def"
+endif # WINNT
+
+ifeq ($(ARCH), AIX)
+EXTRA_LIBS_DEP += $(LIBSLAPD_DEP) $(LDAP_LIBUTIL_DEP) $(LDAP_COMMON_LIBS_DEP)
+EXTRA_LIBS_DEP += $(LDAPSDK_DEP) $(SECURITY_DEP) $(NSPR_DEP)
+EXTRA_LIBS += $(LIBSLAPD) $(LDAP_SDK_LIBLDAP_DLL) $(LIBUTIL) $(LDAP_COMMON_LIBS) $(SECURITYLINK) $(NSPRLINK)
+EXTRA_LIBS += $(DLL_EXTRA_LIBS)
+LD=ld
+endif
+
+ifeq ($(ARCH), HPUX)
+EXTRA_LIBS_DEP += $(LDAPSDK_DEP) $(NSPR_DEP) $(SECURITY_DEP)
+EXTRA_LIBS += $(DYN_NSHTTPD) $(ADMINUTIL_LINK) $(LDAPLINK) $(SECURITYLINK) $(NSPRLINK) $(ICULINK)
+endif
+
+clientSDK:
+
+all: $(OBJDEST) $(LIBDIR) $(LIBCB)
+
+$(LIBCB): $(OBJS) $(LIBCB_DLL_OBJ) $(DEF_FILE)
+ $(LINK_DLL) $(LIBCB_DLL_OBJ) $(PLATFORMLIBS) $(EXTRA_LIBS)
+
+veryclean: clean
+
+clean:
+ $(RM) $(OBJS)
+ifeq ($(ARCH), WINNT)
+ $(RM) $(LIBCB_DLL_OBJ)
+endif
+ $(RM) $(LIBCB)
+
+$(OBJDEST):
+ $(MKDIR) $(OBJDEST)
+
+#
+# header file dependencies (incomplete)
+#
+$(OBJS): cb.h
diff --git a/ldap/servers/plugins/chainingdb/cb.h b/ldap/servers/plugins/chainingdb/cb.h
new file mode 100644
index 00000000..8aed412c
--- /dev/null
+++ b/ldap/servers/plugins/chainingdb/cb.h
@@ -0,0 +1,461 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+#ifndef CBHFILE
+#define CBHFILE
+
+/*** #define CB_YIELD ***/
+
+#include <stdio.h>
+#include <string.h>
+#include <prlock.h>
+#include <prcvar.h>
+#include "slapi-plugin.h"
+#include "slapi-private.h"
+#include "portable.h"
+#include <dirlite_strings.h> /* PLUGIN_MAGIC_VENDOR_STR */
+#include "dirver.h"
+
+/* Constants */
+
+#define CB_DIRECTORY_MANAGER_DN "cn=directory manager"
+#define CB_CHAINING_BACKEND_TYPE "chaining database"
+#define CB_PLUGIN_NAME "chaining database"
+#define CB_PLUGIN_SUBSYSTEM "chaining database"
+#define CB_PLUGIN_DESCRIPTION "LDAP chaining backend database plugin"
+
+#define CB_LDAP_SECURE_PORT 636
+#define CB_BUFSIZE 2048
+
+
+/* Macros */
+
+#define CB_LDAP_CONN_ERROR( err ) ( (err) == LDAP_SERVER_DOWN || \
+ (err) == LDAP_CONNECT_ERROR )
+#define CB_ASSERT( expr ) PR_ASSERT( expr )
+
+/* Innosoft chaining extension for loop detection */
+
+#define CB_LDAP_CONTROL_CHAIN_SERVER "1.3.6.1.4.1.1466.29539.12"
+
+/* Chaining backend configuration attributes */
+
+/* Monitor entry */
+#define CB_MONITOR_EXTENSIBLEOCL "extensibleObject"
+#define CB_MONITOR_INSTNAME "cn"
+#define CB_MONITOR_ADDCOUNT "nsAddCount"
+#define CB_MONITOR_DELETECOUNT "nsDeleteCount"
+#define CB_MONITOR_MODIFYCOUNT "nsModifyCount"
+#define CB_MONITOR_MODRDNCOUNT "nsRenameCount"
+#define CB_MONITOR_SEARCHBASECOUNT "nsSearchBaseCount"
+#define CB_MONITOR_SEARCHONELEVELCOUNT "nsSearchOneLevelCount"
+#define CB_MONITOR_SEARCHSUBTREECOUNT "nsSearchSubtreeCount"
+#define CB_MONITOR_ABANDONCOUNT "nsAbandonCount"
+#define CB_MONITOR_BINDCOUNT "nsBindCount"
+#define CB_MONITOR_UNBINDCOUNT "nsUnbindCount"
+#define CB_MONITOR_COMPARECOUNT "nsCompareCount"
+#define CB_MONITOR_OUTGOINGCONN "nsOpenOpConnectionCount"
+#define CB_MONITOR_OUTGOINGBINDCOUNT "nsOpenBindConnectionCount"
+
+/* Global configuration */
+#define CB_CONFIG_GLOBAL_FORWARD_CTRLS "nsTransmittedControls"
+#define CB_CONFIG_GLOBAL_CHAINING_COMPONENTS "nsActiveChainingComponents"
+#define CB_CONFIG_GLOBAL_CHAINABLE_COMPONENTS "nsPossibleChainingComponents"
+/* not documented */
+#define CB_CONFIG_GLOBAL_DEBUG "nsDebug"
+
+
+/* Instance-specific configuration */
+#define CB_CONFIG_CHAINING_COMPONENTS CB_CONFIG_GLOBAL_CHAINING_COMPONENTS
+#define CB_CONFIG_EXTENSIBLEOCL "extensibleObject"
+/* XXXSD to be changed */
+#define CB_CONFIG_INSTANCE_FILTER "(objectclass=nsBackendInstance)"
+#define CB_CONFIG_INSTNAME "cn"
+#define CB_CONFIG_SUFFIX "nsslapd-suffix"
+#define CB_CONFIG_SIZELIMIT "nsslapd-sizelimit"
+#define CB_CONFIG_TIMELIMIT "nsslapd-timelimit"
+#define CB_CONFIG_HOSTURL "nsFarmServerURL"
+
+#define CB_CONFIG_BINDUSER "nsMultiplexorBindDn"
+#define CB_CONFIG_USERPASSWORD "nsMultiplexorCredentials"
+#define CB_CONFIG_MAXBINDCONNECTIONS "nsBindConnectionsLimit"
+#define CB_CONFIG_MAXCONNECTIONS "nsOperationConnectionsLimit"
+#define CB_CONFIG_MAXCONCURRENCY "nsConcurrentOperationsLimit"
+#define CB_CONFIG_MAXBINDCONCURRENCY "nsConcurrentBindLimit"
+
+#define CB_CONFIG_IMPERSONATION "nsProxiedAuthorization"
+
+#define CB_CONFIG_BINDTIMEOUT "nsBindTimeout"
+#define CB_CONFIG_TIMEOUT "nsOperationTimeout"
+#define CB_CONFIG_MAX_IDLE_TIME "nsMaxResponseDelay"
+#define CB_CONFIG_MAX_TEST_TIME "nsMaxTestResponseDelay"
+
+#define CB_CONFIG_REFERRAL "nsReferralOnScopedSearch"
+
+#define CB_CONFIG_CONNLIFETIME "nsConnectionLife"
+#define CB_CONFIG_ABANDONTIMEOUT "nsAbandonedSearchCheckInterval "
+#define CB_CONFIG_BINDRETRY "nsBindRetryLimit"
+#define CB_CONFIG_LOCALACL "nsCheckLocalACI"
+#define CB_CONFIG_HOPLIMIT "nsHopLimit"
+
+/* not documented */
+#define CB_CONFIG_ILLEGAL_ATTRS "nsServerDefinedAttributes"
+
+/* Default configuration values (as string) */
+
+/*
+ * CB_DEF_MAXCONNECTIONS and CB_DEF_MAXCONCURRENCY used to be 10.
+ * Reduced CB_DEF_MAXCONCURRENCY to 2 to workaround bug 623793 -
+ * err=1 in accesslogs and ber parsing errors in errors logs.
+ */
+#define CB_DEF_MAXCONNECTIONS "20" /* CB_CONFIG_MAXCONNECTIONS */
+#define CB_DEF_MAXCONCURRENCY "2" /* CB_CONFIG_MAXCONCURRENCY */
+#define CB_DEF_BIND_MAXCONNECTIONS "3" /* CB_CONFIG_MAXBINDCONNECTIONS */
+#define CB_DEF_BIND_MAXCONCURRENCY "10" /* CB_CONFIG_MAXBINDCONCURRENCY */
+#define CB_DEF_BINDTIMEOUT "15" /* CB_CONFIG_BINDTIMEOUT */
+#define CB_DEF_CONNLIFETIME "0" /* CB_CONFIG_CONNLIFETIME */
+#define CB_DEF_IMPERSONATION "on" /* CB_CONFIG_IMPERSONATION */
+#define CB_DEF_SEARCHREFERRAL "off" /* CB_CONFIG_REFERRAL */
+#define CB_DEF_ABANDON_TIMEOUT "1" /* CB_CONFIG_ABANDONTIMEOUT */
+#define CB_DEF_BINDRETRY "3" /* CB_CONFIG_BINDRETRY */
+#define CB_DEF_LOCALACL "off" /* CB_CONFIG_LOCALACL */
+#define CB_DEF_TIMELIMIT "3600"
+#define CB_DEF_SIZELIMIT "2000"
+#define CB_DEF_HOPLIMIT "10" /* CB_CONFIG_HOPLIMIT */
+#define CB_DEF_MAX_IDLE_TIME "60" /* CB_CONFIG_MAX_IDLE_TIME */
+#define CB_DEF_MAX_TEST_TIME "15" /* CB_CONFIG_MAX_TEST_TIME */
+
+typedef void *cb_config_get_fn_t(void *arg);
+typedef int cb_config_set_fn_t(void *arg, void *value, char *errorbuf, int phase, int apply);
+typedef struct _cb_instance_config_info {
+ char *config_name;
+ int config_type;
+ char *config_default_value;
+ cb_config_get_fn_t *config_get_fn;
+ cb_config_set_fn_t *config_set_fn;
+ int config_flags;
+} cb_instance_config_info;
+
+#define CB_CONFIG_TYPE_ONOFF 1 /* val = (int) value */
+#define CB_CONFIG_TYPE_STRING 2 /* val = (char *) value - The get functions
+ * for this type must return alloced memory
+ * that should be freed by the caller. */
+#define CB_CONFIG_TYPE_INT 3 /* val = (int) value */
+#define CB_CONFIG_TYPE_LONG 4 /* val = (long) value */
+#define CB_CONFIG_TYPE_INT_OCTAL 5 /* Same as CONFIG_TYPE_INT, but shown in octal*/
+#define CB_PREVIOUSLY_SET 1
+#define CB_ALWAYS_SHOW 2
+#define CB_CONFIG_PHASE_INITIALIZATION 1
+#define CB_CONFIG_PHASE_STARTUP 2
+#define CB_CONFIG_PHASE_RUNNING 3
+#define CB_CONFIG_PHASE_INTERNAL 4
+
+/*jarnou: default amount of time in seconds during wich the chaining backend will be unavailable */
+#define CB_UNAVAILABLE_PERIOD 30 /* CB_CONFIG_UNAVAILABLE_PERIOD */
+#define CB_INFINITE_TIME 360000 /* must be enough ... */
+/*jarnou: default number of connections failed from which the farm is declared unavailable */
+#define CB_NUM_CONN_BEFORE_UNAVAILABILITY 1
+#define FARMSERVER_UNAVAILABLE 1
+#define FARMSERVER_AVAILABLE 0
+
+/* Internal data structures */
+
+/* cb_backend represents the chaining backend type. */
+/* Only one instance is created when the plugin is */
+/* loaded. Contain global conf */
+typedef struct _cb_backend {
+
+ /*
+ ** keep track of plugin identity.
+ ** Used for internal operations
+ */
+
+ void *identity;
+ char * pluginDN;
+ char * configDN;
+
+ /*
+ ** There are times when we need a pointer to the chaining database
+ ** plugin, so we will store a pointer to it here. Examples of
+ ** when we need it are when we create a new instance and when
+ ** we need the name of the plugin to do internal ops.
+ */
+
+ struct slapdplugin *plugin;
+
+ /*
+ ** Global config. shared by all chaining db instances
+ */
+
+ struct {
+ char ** forward_ctrls; /* List of forwardable controls */
+ char ** chaining_components; /* List of plugins that chains */
+ char ** chainable_components; /* List of plugins allowed to chain*/
+ /* internal operations. */
+ PRRWLock *rwl_config_lock; /* Protect the global config */
+ } config;
+
+ int started; /* TRUE when started */
+
+} cb_backend;
+
+
+/* Connection management */
+
+/* states */
+#define CB_CONNSTATUS_OK 1 /* Open */
+#define CB_CONNSTATUS_DOWN 2 /* Down */
+#define CB_CONNSTATUS_STALE 3
+
+#define ENABLE_MULTITHREAD_PER_CONN 1 /* to allow multiple threads to perform LDAP operations on a connection */
+#define DISABLE_MULTITHREAD_PER_CONN 0 /* to allow only one thread to perform LDAP operations on a connection */
+
+/************** WARNING: Be careful if you want to change this constant. It is used in hexadecimal in cb_conn_stateless.c in the function PR_ThreadSelf() ************/
+#define MAX_CONN_ARRAY 2048 /* we suppose the number of threads in the server not to exceed this limit*/
+/**********************************************************************************************************/
+typedef struct _cb_outgoing_conn{
+ LDAP *ld;
+ unsigned long refcount;
+ struct _cb_outgoing_conn *next;
+ time_t opentime;
+ int status;
+ int ThreadId ; /* usefull to identify the thread when SSL is enabled */
+} cb_outgoing_conn;
+
+typedef struct {
+ char *hostname; /* Farm server name */
+ char *url;
+ unsigned int port;
+ int secure;
+ char *binddn; /* normalized */
+ char *binddn2; /* not normalized, value returned to the client */
+ char *password;
+ int bindit; /* If true, open AND bind */
+ char ** waste_basket; /* stale char * */
+
+ struct {
+ unsigned int maxconnections;
+ unsigned int maxconcurrency;
+ unsigned int connlifetime;
+ struct timeval op_timeout;
+ struct timeval bind_timeout;
+
+ Slapi_Mutex *conn_list_mutex;
+ Slapi_CondVar *conn_list_cv;
+ cb_outgoing_conn *conn_list;
+ unsigned int conn_list_count;
+
+ } conn;
+
+ cb_outgoing_conn *connarray[MAX_CONN_ARRAY]; /* array of secure connections */
+
+ /* To protect the config set by LDAP */
+ PRRWLock * rwl_config_lock;
+} cb_conn_pool;
+
+
+/* _cb_backend_instance represents a instance of the chaining */
+/* backend. */
+
+typedef struct _cb_backend_instance {
+
+ char *inst_name; /* Unique name */
+ Slapi_Backend *inst_be; /* Slapi_Bakedn associated with it */
+ cb_backend *backend_type; /* pointer to the backend type */
+
+ /* configuration */
+
+ PRRWLock *rwl_config_lock; /* protect the config */
+ char *configDn; /* config entry dn */
+ char *monitorDn; /* monitor entry dn */
+ int local_acl; /* True if local acl evaluation */
+ /* sometimes a chaining backend may be associated with a local backend
+ 1) The chaining backend is the backend of a sub suffix, and the
+ parent suffix has a local backend
+ 2) Entry distribution is being used to distribute write operations to
+ a chaining backend and other operations to a local backend
+ (e.g. a replication hub or consumer)
+ If the associated local backend is being initialized (import), it will be
+ disabled, and it will be impossible to evaluate local acls. In this case,
+ we still want to be able to chain operations to a farm server or another
+ database chain. But the current code will not allow cascading without
+ local acl evaluation (cb_controls.c). The following variable allows us to relax that
+ restriction while the associated backend is disabled
+ */
+ int associated_be_is_disabled; /* true if associated backend is disabled */
+ int isconfigured; /* True when valid config entry */
+ int impersonate; /* TRUE to impersonate users */
+ int searchreferral; /* TRUE to return referral for scoped searches */
+ int bind_retry;
+ struct timeval abandon_timeout; /* check for abandoned op periodically */
+ struct timeval op_timeout;
+ char **url_array; /* list of urls to farm servers */
+ char **chaining_components; /* List of plugins using chaining */
+ char **illegal_attributes; /* Attributes not forwarded */
+ char **every_attribute; /* attr list to get every attr, including op attrs */
+ int sizelimit;
+ int timelimit;
+ int hoplimit;
+ int max_idle_time; /* how long we wait before pinging the farm server */
+ int max_test_time; /* how long we wait during ping */
+
+ cb_conn_pool *pool; /* Operation cnx pool */
+ cb_conn_pool *bind_pool; /* Bind cnx pool */
+
+ Slapi_Eq_Context eq_ctx; /* Use to identify the function put in the queue */
+
+ /* Monitoring */
+
+ struct {
+ Slapi_Mutex *mutex;
+ unsigned long addcount;
+ unsigned long deletecount;
+ unsigned long modifycount;
+ unsigned long modrdncount;
+ unsigned long searchbasecount;
+ unsigned long searchonelevelcount;
+ unsigned long searchsubtreecount;
+ unsigned long abandoncount;
+ unsigned long bindcount;
+ unsigned long unbindcount;
+ unsigned long comparecount;
+ } monitor;
+
+ /* Monitoring the chaining BE availability */
+ /* Principle: as soon as we detect an abnormal pb with an ldap operation, and we close the connection
+ or if we can't open a connection, we increment a counter (cpt). This counter represents the number of
+ continuously pbs we can notice. Before forwarding an LDAP operation, wether the farmserver is available or not,
+ through the value of the counter. If the farmserver is not available, we just return an error msg to the client */
+
+ struct {
+ int unavailable_period ; /* how long we wait as soon as the farm is declared unavailable */
+ int max_num_conn_failed ; /* max number of consecutive failed/aborted connections before we declared the farm as unreachable */
+ time_t unavailableTimeLimit ; /* time from which the chaining BE becomes available */
+ int farmserver_state ; /* FARMSERVER_AVAILABLE if the chaining is available, FARMSERVER_UNAVAILABLE else */
+ int cpt ; /* count the number of consecutive failed/aborted connexions */
+ Slapi_Mutex *cpt_lock ; /* lock to protect the counter cpt */
+ Slapi_Mutex *lock_timeLimit ; /* lock to protect the unavailableTimeLimit variable*/
+ } monitor_availability;
+
+
+} cb_backend_instance;
+
+/* Data structure for the search operation to carry candidates */
+
+#define CB_SEARCHCONTEXT_ENTRY 2
+
+typedef struct _cb_searchContext {
+ int type;
+ void *data;
+ int msgid;
+ LDAP *ld;
+ cb_outgoing_conn *cnx;
+ Slapi_Entry *tobefreed;
+ LDAPMessage *pending_result;
+ int pending_result_type;
+} cb_searchContext;
+
+#define CB_REOPEN_CONN -1968 /* Different from any LDAP_XXX errors */
+
+/* Forward declarations */
+
+/* for ctrl_flags on cb_update_controls */
+#define CB_UPDATE_CONTROLS_ADDAUTH 1
+#define CB_UPDATE_CONTROLS_ISABANDON 2
+
+
+int cb_get_connection(cb_conn_pool * pool, LDAP ** ld, cb_outgoing_conn ** cnx, struct timeval * tmax, char **errmsg);
+int cb_config(cb_backend_instance * cb, int argc, char ** argv );
+int cb_update_controls( Slapi_PBlock *pb, LDAP * ld, LDAPControl *** controls, int ctrl_flags);
+int cb_is_control_forwardable(cb_backend * cb, char *controloid);
+int cb_access_allowed (Slapi_PBlock *pb,Slapi_Entry *e,char *type,struct berval * bval, int op, char ** buf);
+int cb_forward_operation(Slapi_PBlock * op);
+int cb_parse_instance_config_entry(cb_backend * cb, Slapi_Entry * e);
+int cb_abandon_connection(cb_backend_instance * cb, Slapi_PBlock * pb, LDAP ** ld);
+int cb_atoi(char *str);
+int cb_check_forward_abandon(cb_backend_instance * cb,Slapi_PBlock * pb, LDAP * ld, int msgid );
+int cb_search_monitor_callback(Slapi_PBlock *pb, Slapi_Entry *e, Slapi_Entry *e2, int *ret, char *t,void *a);
+int cb_config_load_dse_info(Slapi_PBlock * pb);
+int cb_config_add_dse_entries(cb_backend *cb, char **entries, char *string1, char *string2, char *string3);
+int cb_add_suffix(cb_backend_instance *inst, struct berval **bvals, int apply_mod, char *returntext);
+int cb_create_default_backend_instance_config(cb_backend * cb);
+int cb_build_backend_instance_config(cb_backend_instance *inst, Slapi_Entry * conf,int apply);
+int cb_instance_delete_config_callback(Slapi_PBlock *pb, Slapi_Entry* e, Slapi_Entry* e2,
+ int *returncode, char *returntext, void *arg);
+int cb_instance_search_config_callback(Slapi_PBlock *pb, Slapi_Entry* entryBefore, Slapi_Entry* e,
+ int *returncode, char *returntext, void *arg);
+int cb_instance_add_config_callback(Slapi_PBlock *pb, Slapi_Entry* e, Slapi_Entry* e2,
+ int *returncode, char *returntext, void *arg);
+int cb_instance_modify_config_callback(Slapi_PBlock *pb, Slapi_Entry* entryBefore, Slapi_Entry* e,
+ int *returncode, char *returntext, void *arg);
+int cb_dont_allow_that(Slapi_PBlock *pb, Slapi_Entry* entryBefore, Slapi_Entry* e,
+ int *returncode, char *returntext, void *arg);
+int cb_config_search_callback(Slapi_PBlock *pb, Slapi_Entry* e1, Slapi_Entry* e2, int *returncode,
+ char *returntext, void *arg);
+int cb_config_add_callback(Slapi_PBlock *pb, Slapi_Entry* entryBefore, Slapi_Entry* e, int *returncode, char *returntext, void *arg);
+int cb_config_delete_instance_callback(Slapi_PBlock *pb, Slapi_Entry* entryBefore, Slapi_Entry* e, int *returncode, char *returntext, void *arg);
+int cb_config_modify_callback(Slapi_PBlock *pb, Slapi_Entry* entryBefore, Slapi_Entry* e, int *returncode, char *returntext, void *arg);
+int cb_config_add_instance_callback(Slapi_PBlock *pb, Slapi_Entry* entryBefore, Slapi_Entry* e, int *returncode, char *returntext, void *arg);
+int cb_delete_monitor_callback(Slapi_PBlock * pb, Slapi_Entry * e, Slapi_Entry * entryAfter, int * returnCode, char * returnText, void * arg);
+int cb_config_add_check_callback(Slapi_PBlock *pb, Slapi_Entry* e, Slapi_Entry* e2, int *returncode,
+ char *returntext, void *arg);
+int cb_instance_add_config_check_callback(Slapi_PBlock *pb, Slapi_Entry* e, Slapi_Entry* e2,
+ int *returncode, char *returntext, void *arg);
+int cb_config_modify_check_callback(Slapi_PBlock *pb, Slapi_Entry* entryBefore, Slapi_Entry* e, int *returncode,
+ char *returntext, void *arg);
+
+void cb_eliminate_illegal_attributes(cb_backend_instance * inst, Slapi_Entry * e);
+void cb_release_op_connection(cb_conn_pool * pool, LDAP *ldd, int dispose);
+void cb_register_supported_control( cb_backend * cb, char *controloid, unsigned long controlops );
+void cb_unregister_all_supported_control( cb_backend * cb );
+void cb_register_supported_control( cb_backend * cb, char *controloid, unsigned long controlops );
+void cb_unregister_supported_control( cb_backend * cb, char *controloid, unsigned long controlops );
+void cb_set_acl_policy(Slapi_PBlock *pb);
+void cb_close_conn_pool(cb_conn_pool * pool);
+void cb_update_monitor_info(Slapi_PBlock * pb, cb_backend_instance * inst,int op);
+void cb_send_ldap_result(Slapi_PBlock *pb, int err, char *m,char *t, int ne, struct berval **urls );
+void cb_stale_all_connections( cb_backend_instance * be);
+int
+cb_config_add_instance_check_callback(Slapi_PBlock *pb, Slapi_Entry* entryBefore, Slapi_Entry* e,
+ int *returncode, char *returntext, void *arg);
+
+
+int chaining_back_add ( Slapi_PBlock *pb );
+int chaining_back_delete ( Slapi_PBlock *pb );
+int chaining_back_compare ( Slapi_PBlock *pb );
+int chaining_back_modify ( Slapi_PBlock *pb );
+int chaining_back_modrdn ( Slapi_PBlock *pb );
+int chaining_back_abandon ( Slapi_PBlock *pb );
+int chaining_back_entry_release ( Slapi_PBlock *pb );
+int chainingdb_next_search_entry( Slapi_PBlock *pb );
+int chainingdb_build_candidate_list ( Slapi_PBlock *pb );
+int chainingdb_start (Slapi_PBlock *pb );
+int chainingdb_bind (Slapi_PBlock *pb );
+int cb_db_size (Slapi_PBlock *pb );
+int cb_back_close (Slapi_PBlock *pb );
+int cb_back_cleanup (Slapi_PBlock *pb );
+
+long cb_atol(char *str);
+
+Slapi_Entry * cb_LDAPMessage2Entry(LDAP * ctx, LDAPMessage * msg, int attrsonly);
+char * cb_urlparse_err2string( int err );
+char * cb_get_rootdn();
+struct berval ** referrals2berval(char ** referrals);
+cb_backend_instance * cb_get_instance(Slapi_Backend * be);
+cb_backend * cb_get_backend_type();
+int cb_debug_on();
+void cb_set_debug(int on);
+int cb_ping_farm(cb_backend_instance *cb,cb_outgoing_conn * cnx,time_t end);
+void cb_update_failed_conn_cpt ( cb_backend_instance *cb ) ;
+void cb_reset_conn_cpt( cb_backend_instance *cb ) ;
+int cb_check_availability( cb_backend_instance *cb, Slapi_PBlock *pb ) ;
+
+time_t current_time();
+char* get_localhost_DNS();
+
+/* this function is called when state of a backend changes */
+void cb_be_state_change (void *handle, char *be_name, int old_be_state, int new_be_state);
+
+#endif
diff --git a/ldap/servers/plugins/chainingdb/cb_abandon.c b/ldap/servers/plugins/chainingdb/cb_abandon.c
new file mode 100644
index 00000000..ca0cfc09
--- /dev/null
+++ b/ldap/servers/plugins/chainingdb/cb_abandon.c
@@ -0,0 +1,50 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+#include "cb.h"
+
+/*
+ * Perform an abandon operation
+ *
+ * Returns:
+ * 0 - success
+ * <0 - fail
+ *
+ */
+
+int
+chaining_back_abandon ( Slapi_PBlock *pb )
+{
+ /*
+ * Abandon forwarded to the farm server for scoped
+ * searches only. Done in cb_search.c
+ */
+ return 0;
+}
+
+int cb_check_forward_abandon(cb_backend_instance * cb,Slapi_PBlock * pb, LDAP * ld, int msgid ) {
+
+ int rc;
+ LDAPControl ** ctrls=NULL;
+
+ if (slapi_op_abandoned( pb )) {
+
+ if ((rc=cb_forward_operation(pb)) != LDAP_SUCCESS ) {
+ return 0;
+ }
+
+ if ((rc = cb_update_controls( pb,ld,&ctrls,CB_UPDATE_CONTROLS_ISABANDON )) != LDAP_SUCCESS ) {
+ if ( NULL != ctrls)
+ ldap_controls_free(ctrls);
+ return 0;
+ }
+ rc = ldap_abandon_ext(ld, msgid, ctrls, NULL );
+ cb_release_op_connection(cb->pool,ld,CB_LDAP_CONN_ERROR(rc));
+ if ( NULL != ctrls)
+ ldap_controls_free(ctrls);
+ return 1;
+ }
+ return 0;
+}
diff --git a/ldap/servers/plugins/chainingdb/cb_acl.c b/ldap/servers/plugins/chainingdb/cb_acl.c
new file mode 100644
index 00000000..ce0a6793
--- /dev/null
+++ b/ldap/servers/plugins/chainingdb/cb_acl.c
@@ -0,0 +1,60 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+#include "cb.h"
+
+/*
+** generic function to send back results
+** Turn off acl eval on front-end when needed
+*/
+
+void cb_set_acl_policy(Slapi_PBlock *pb) {
+
+ Slapi_Backend *be;
+ cb_backend_instance *cb;
+ int noacl;
+
+ slapi_pblock_get( pb, SLAPI_BACKEND, &be );
+ cb = cb_get_instance(be);
+
+ /* disable acl checking if the local_acl flag is not set
+ or if the associated backend is disabled */
+ noacl=!(cb->local_acl) || cb->associated_be_is_disabled;
+
+ if (noacl) {
+ slapi_pblock_set(pb, SLAPI_PLUGIN_DB_NO_ACL, &noacl);
+ } else {
+ /* Be very conservative about acl evaluation */
+ slapi_pblock_set(pb, SLAPI_PLUGIN_DB_NO_ACL, &noacl);
+ }
+}
+
+int cb_access_allowed(
+ Slapi_PBlock *pb,
+ Slapi_Entry *e, /* The Slapi_Entry */
+ char *attr, /* Attribute of the entry */
+ struct berval *val, /* value of attr. NOT USED */
+ int access, /* access rights */
+ char **errbuf
+ )
+
+{
+
+switch (access) {
+
+ case SLAPI_ACL_ADD:
+ case SLAPI_ACL_DELETE:
+ case SLAPI_ACL_COMPARE:
+ case SLAPI_ACL_WRITE:
+ case SLAPI_ACL_PROXY:
+
+ /* Keep in mind some entries are NOT */
+ /* available for acl evaluation */
+
+ return slapi_access_allowed(pb,e,attr,val,access);
+ default:
+ return LDAP_INSUFFICIENT_ACCESS;
+}
+}
diff --git a/ldap/servers/plugins/chainingdb/cb_add.c b/ldap/servers/plugins/chainingdb/cb_add.c
new file mode 100644
index 00000000..e0b49f7c
--- /dev/null
+++ b/ldap/servers/plugins/chainingdb/cb_add.c
@@ -0,0 +1,227 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+#include "cb.h"
+
+/*
+ * Perform an add operation
+ *
+ * Returns:
+ * 0 - success
+ * <0 - fail
+ *
+ */
+
+int
+chaining_back_add ( Slapi_PBlock *pb )
+{
+
+ Slapi_Backend *be;
+ Slapi_Entry *e;
+ cb_backend_instance *cb;
+ LDAPControl **serverctrls=NULL;
+ LDAPControl **ctrls=NULL;
+ int rc,parse_rc,msgid,i;
+ LDAP *ld=NULL;
+ char **referrals=NULL;
+ LDAPMod ** mods;
+ LDAPMessage * res;
+ char *dn,* matched_msg, *error_msg;
+ char *cnxerrbuf=NULL;
+ time_t endtime;
+ cb_outgoing_conn *cnx;
+
+ if ( (rc=cb_forward_operation(pb)) != LDAP_SUCCESS ) {
+ cb_send_ldap_result( pb, rc, NULL, "Remote data access disabled", 0, NULL );
+ return -1;
+ }
+
+ slapi_pblock_get( pb, SLAPI_BACKEND, &be );
+ cb = cb_get_instance(be);
+
+ /* Update monitor info */
+ cb_update_monitor_info(pb,cb,SLAPI_OPERATION_ADD);
+
+ /* Check wether the chaining BE is available or not */
+ if ( cb_check_availability( cb, pb ) == FARMSERVER_UNAVAILABLE ){
+ return -1;
+ }
+
+
+ slapi_pblock_get( pb, SLAPI_ADD_TARGET, &dn );
+ slapi_pblock_get( pb, SLAPI_ADD_ENTRY, &e );
+
+ /* Check local access controls */
+ if (cb->local_acl && !cb->associated_be_is_disabled) {
+ char * errbuf=NULL;
+ rc = cb_access_allowed (pb, e, NULL, NULL, SLAPI_ACL_ADD, &errbuf);
+ if ( rc != LDAP_SUCCESS ) {
+ cb_send_ldap_result( pb, rc, NULL, errbuf, 0, NULL );
+ slapi_ch_free((void **)&errbuf);
+ return -1;
+ }
+ }
+
+ /* Build LDAPMod from the SLapi_Entry */
+ cb_eliminate_illegal_attributes(cb,e);
+
+ if ((rc = slapi_entry2mods ((const Slapi_Entry *)e, NULL, &mods)) != LDAP_SUCCESS) {
+ cb_send_ldap_result( pb, rc,NULL,NULL, 0, NULL);
+ return -1;
+ }
+
+ /* Grab a connection handle */
+ if ((rc = cb_get_connection(cb->pool,&ld,&cnx,NULL,&cnxerrbuf)) != LDAP_SUCCESS) {
+ cb_send_ldap_result( pb, LDAP_OPERATIONS_ERROR,NULL,cnxerrbuf, 0, NULL);
+ ldap_mods_free(mods,1);
+ slapi_ch_free((void **)&cnxerrbuf);
+ /* ping the farm. If the farm is unreachable, we increment the counter */
+ cb_ping_farm(cb,NULL,0);
+
+ return -1;
+ }
+
+ /* Control management */
+ if ( (rc = cb_update_controls( pb,ld,&ctrls,CB_UPDATE_CONTROLS_ADDAUTH)) != LDAP_SUCCESS ) {
+ cb_send_ldap_result( pb, rc, NULL,NULL, 0, NULL);
+ cb_release_op_connection(cb->pool,ld,CB_LDAP_CONN_ERROR(rc));
+ ldap_mods_free(mods,1);
+ return -1;
+ }
+
+ if ( slapi_op_abandoned( pb )) {
+ cb_release_op_connection(cb->pool,ld,0);
+ ldap_mods_free(mods,1);
+ if ( NULL != ctrls)
+ ldap_controls_free(ctrls);
+ return -1;
+ }
+
+ /* heart-beat management */
+ if (cb->max_idle_time>0)
+ endtime=current_time() + cb->max_idle_time;
+
+ /* Send LDAP operation to the remote host */
+ rc = ldap_add_ext( ld, dn, mods, ctrls, NULL, &msgid );
+
+ if ( NULL != ctrls)
+ ldap_controls_free(ctrls);
+
+ if ( rc != LDAP_SUCCESS ) {
+
+ cb_send_ldap_result( pb, LDAP_OPERATIONS_ERROR, NULL,
+ ldap_err2string(rc), 0, NULL);
+ cb_release_op_connection(cb->pool,ld,CB_LDAP_CONN_ERROR(rc));
+ ldap_mods_free(mods,1);
+ return -1;
+ }
+
+ /*
+ * Poll the server for the results of the add operation.
+ * Check for abandoned operation regularly.
+ */
+
+ while ( 1 ) {
+
+ if (cb_check_forward_abandon(cb,pb,ld,msgid)) {
+ /* connection handle released in cb_check_forward_abandon() */
+ ldap_mods_free(mods,1);
+ return -1;
+ }
+
+ rc = ldap_result( ld, msgid, 0, &cb->abandon_timeout, &res );
+ switch ( rc ) {
+ case -1:
+ cb_send_ldap_result(pb,LDAP_OPERATIONS_ERROR, NULL,
+ ldap_err2string(rc), 0, NULL);
+ cb_release_op_connection(cb->pool,ld,CB_LDAP_CONN_ERROR(rc));
+ ldap_mods_free(mods,1);
+ if (res)
+ ldap_msgfree(res);
+ return -1;
+ case 0:
+
+ if ((rc=cb_ping_farm(cb,cnx,endtime)) != LDAP_SUCCESS) {
+
+ /* does not respond. give up and return a*/
+ /* error to the client. */
+
+ /*cb_send_ldap_result(pb,LDAP_OPERATIONS_ERROR, NULL,
+ ldap_err2string(rc), 0, NULL);*/
+ cb_send_ldap_result(pb,LDAP_OPERATIONS_ERROR, NULL, "FARM SERVER TEMPORARY UNAVAILABLE", 0, NULL);
+ cb_release_op_connection(cb->pool,ld,CB_LDAP_CONN_ERROR(rc));
+ ldap_mods_free(mods,1);
+ if (res)
+ ldap_msgfree(res);
+ return -1;
+ }
+#ifdef CB_YIELD
+ DS_Sleep(PR_INTERVAL_NO_WAIT);
+#endif
+ break;
+ default:
+ serverctrls=NULL;
+ matched_msg=error_msg=NULL;
+ referrals=NULL;
+
+ parse_rc = ldap_parse_result( ld, res, &rc, &matched_msg,
+ &error_msg, &referrals, &serverctrls, 1 );
+
+ if ( parse_rc != LDAP_SUCCESS ) {
+ cb_send_ldap_result( pb, LDAP_OPERATIONS_ERROR, NULL,
+ ldap_err2string(parse_rc), 0, NULL);
+ cb_release_op_connection(cb->pool,ld,CB_LDAP_CONN_ERROR(parse_rc));
+ ldap_mods_free(mods,1);
+ slapi_ch_free((void **)&matched_msg);
+ slapi_ch_free((void **)&error_msg);
+ if (serverctrls)
+ ldap_controls_free(serverctrls);
+ /* jarnou: free referrals */
+ if (referrals)
+ charray_free(referrals);
+ return -1;
+ }
+
+ if ( rc != LDAP_SUCCESS ) {
+ struct berval ** refs = referrals2berval(referrals);
+ cb_send_ldap_result( pb, rc, matched_msg, error_msg, 0, refs);
+ cb_release_op_connection(cb->pool,ld,CB_LDAP_CONN_ERROR(rc));
+ ldap_mods_free(mods,1);
+ slapi_ch_free((void **)&matched_msg);
+ slapi_ch_free((void **)&error_msg);
+ if (refs)
+ ber_bvecfree(refs);
+ if (referrals)
+ charray_free(referrals);
+ if (serverctrls)
+ ldap_controls_free(serverctrls);
+ return -1;
+ }
+
+ ldap_mods_free(mods,1 );
+ cb_release_op_connection(cb->pool,ld,0);
+
+ /* Add control response sent by the farm server */
+
+ for (i=0; serverctrls && serverctrls[i];i++)
+ slapi_pblock_set( pb, SLAPI_ADD_RESCONTROL, serverctrls[i]);
+ if (serverctrls)
+ ldap_controls_free(serverctrls);
+ /* jarnou: free matched_msg, error_msg, and referrals if necessary */
+ slapi_ch_free((void **)&matched_msg);
+ slapi_ch_free((void **)&error_msg);
+ if (referrals)
+ charray_free(referrals);
+ cb_send_ldap_result( pb, LDAP_SUCCESS, NULL, NULL, 0, NULL );
+
+ slapi_entry_free(e);
+ slapi_pblock_set( pb, SLAPI_ADD_ENTRY, NULL );
+
+ return 0;
+ }
+ }
+
+ /* Never reached */
+}
diff --git a/ldap/servers/plugins/chainingdb/cb_bind.c b/ldap/servers/plugins/chainingdb/cb_bind.c
new file mode 100644
index 00000000..495b672f
--- /dev/null
+++ b/ldap/servers/plugins/chainingdb/cb_bind.c
@@ -0,0 +1,290 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+#include "cb.h"
+
+static void
+cb_free_bervals( struct berval **bvs );
+
+
+static int
+cb_sasl_bind_once_s( cb_conn_pool *pool, char *dn, int method, char * mechanism,
+ struct berval *creds, LDAPControl **reqctrls,
+ char **matcheddnp, char **errmsgp, struct berval ***refurlsp,
+ LDAPControl ***resctrlsp , int * status);
+
+/*
+ * Attempt to chain a bind request off to "srvr." We return an LDAP error
+ * code that indicates whether we successfully got a response from the
+ * other server or not. If we succeed, we return LDAP_SUCCESS and *lderrnop
+ * is set to the result code from the remote server.
+ *
+ * Note that in the face of "ldap server down" or "ldap connect failed" errors
+ * we make up to "tries" attempts to bind to the remote server. Since we
+ * are only interested in recovering silently when the remote server is up
+ * but decided to close our connection, we retry without pausing between
+ * attempts.
+ */
+
+static int
+cb_sasl_bind_s(Slapi_PBlock * pb, cb_conn_pool *pool, int tries,
+ char *dn, int method,char * mechanism, struct berval *creds, LDAPControl **reqctrls,
+ char **matcheddnp, char **errmsgp, struct berval ***refurlsp,
+ LDAPControl ***resctrlsp ,int *status) {
+
+ int rc;
+
+ do {
+ /* check to see if operation has been abandoned...*/
+
+ if (LDAP_AUTH_SIMPLE!=method)
+ return LDAP_AUTH_METHOD_NOT_SUPPORTED;
+
+ if ( slapi_op_abandoned( pb )) {
+ rc = LDAP_USER_CANCELLED;
+ } else {
+ rc = cb_sasl_bind_once_s( pool, dn, method,mechanism, creds, reqctrls,
+ matcheddnp, errmsgp, refurlsp, resctrlsp ,status);
+ }
+ } while ( CB_LDAP_CONN_ERROR( rc ) && --tries > 0 );
+
+ return( rc );
+}
+
+static int
+cb_sasl_bind_once_s( cb_conn_pool *pool, char *dn, int method, char * mechanism,
+ struct berval *creds, LDAPControl **reqctrls,
+ char **matcheddnp, char **errmsgp, struct berval ***refurlsp,
+ LDAPControl ***resctrlsp , int * status) {
+
+ int rc, msgid;
+ char **referrals;
+ struct timeval timeout_copy, *timeout;
+ LDAPMessage *result=NULL;
+ LDAP *ld=NULL;
+ char *cnxerrbuf=NULL;
+ cb_outgoing_conn *cnx;
+ int version=LDAP_VERSION3;
+
+ /* Grab an LDAP connection to use for this bind. */
+
+ PR_RWLock_Rlock(pool->rwl_config_lock);
+ timeout_copy.tv_sec = pool->conn.bind_timeout.tv_sec;
+ timeout_copy.tv_usec = pool->conn.bind_timeout.tv_usec;
+ PR_RWLock_Unlock(pool->rwl_config_lock);
+
+ if (( rc = cb_get_connection( pool, &ld ,&cnx, NULL, &cnxerrbuf)) != LDAP_SUCCESS ) {
+ *errmsgp=cnxerrbuf;
+ goto release_and_return;
+ }
+
+ /* Send the bind operation (need to retry on LDAP_SERVER_DOWN) */
+
+ ldap_set_option( ld, LDAP_OPT_PROTOCOL_VERSION, &version );
+
+ if (( rc = ldap_sasl_bind( ld, dn, LDAP_SASL_SIMPLE, creds, reqctrls,
+ NULL, &msgid )) != LDAP_SUCCESS ) {
+ goto release_and_return;
+ }
+
+ /* XXXSD what is the exact semantics of bind_to ? it is used to get a
+ connection handle and later to bind ==> bind op may last 2*bind_to
+ from the user point of view
+ confusion comes from teh fact that bind to is used 2for 3 differnt thinks,
+ */
+
+ /*
+ * determine timeout value (how long we will wait for a response)
+ * if timeout is zero'd, we wait indefinitely.
+ */
+ if ( timeout_copy.tv_sec == 0 && timeout_copy.tv_usec == 0 ) {
+ timeout = NULL;
+ } else {
+ timeout = &timeout_copy;
+ }
+
+ /*
+ * Wait for a result.
+ */
+ rc = ldap_result( ld, msgid, 1, timeout, &result );
+
+ /*
+ * Interpret the result.
+ */
+
+ if ( rc == 0 ) { /* timeout */
+ /*
+ * Timed out waiting for a reply from the server.
+ */
+ rc = LDAP_TIMEOUT;
+ } else if ( rc < 0 ) {
+
+ /* Some other error occurred (no result received). */
+ char * matcheddnp2, * errmsgp2;
+ matcheddnp2=errmsgp2=NULL;
+
+ rc = ldap_get_lderrno( ld, &matcheddnp2, &errmsgp2 );
+
+ /* Need to allocate errmsgs */
+ if (matcheddnp2)
+ *matcheddnp=slapi_ch_strdup(matcheddnp2);
+ if (errmsgp2)
+ *errmsgp=slapi_ch_strdup(errmsgp2);
+
+ if ( LDAP_SUCCESS != rc ) {
+ slapi_log_error( SLAPI_LOG_PLUGIN, CB_PLUGIN_SUBSYSTEM,
+ "cb_sasl_bind_once_s failed (%s)\n",ldap_err2string(rc));
+ }
+ } else {
+
+ /* Got a result from remote server -- parse it.*/
+
+ char * matcheddnp2, * errmsgp2;
+ matcheddnp2=errmsgp2=NULL;
+ *resctrlsp=NULL;
+ rc = ldap_parse_result( ld, result, status, matcheddnp, errmsgp,
+ &referrals, resctrlsp, 1 );
+ if ( referrals != NULL ) {
+ *refurlsp = referrals2berval( referrals );
+ ldap_value_free( referrals );
+ }
+ /* realloc matcheddn & errmsg because the mem alloc model */
+ /* may differ from malloc */
+ if (matcheddnp2) {
+ *matcheddnp=slapi_ch_strdup(matcheddnp2);
+ ldap_memfree(matcheddnp2);
+ }
+ if (errmsgp2) {
+ *errmsgp=slapi_ch_strdup(errmsgp2);
+ ldap_memfree(errmsgp2);
+ }
+
+ }
+
+release_and_return:
+ if ( ld != NULL ) {
+ cb_release_op_connection( pool, ld, CB_LDAP_CONN_ERROR( rc ));
+ }
+
+ return( rc );
+}
+
+int
+chainingdb_bind( Slapi_PBlock *pb ) {
+
+ int status=LDAP_SUCCESS;
+ int allocated_errmsg;
+ int rc=LDAP_SUCCESS;
+ cb_backend_instance *cb;
+ Slapi_Backend *be;
+ char *dn;
+ int method;
+ struct berval *creds, **urls;
+ char *matcheddn,*errmsg;
+ LDAPControl **reqctrls, **resctrls, **ctrls;
+ char * mechanism;
+ int freectrls=1;
+ int bind_retry;
+
+ if ( LDAP_SUCCESS != (rc = cb_forward_operation(pb) )) {
+ cb_send_ldap_result( pb, rc, NULL, "Chaining forbidden", 0, NULL );
+ return SLAPI_BIND_FAIL;
+ }
+
+ ctrls=NULL;
+ /* don't add proxy auth control. use this call to check for supported */
+ /* controls only. */
+ if ( LDAP_SUCCESS != ( rc = cb_update_controls( pb, NULL, &ctrls, 0 )) ) {
+ cb_send_ldap_result( pb, rc, NULL, NULL, 0, NULL );
+ if (ctrls)
+ ldap_controls_free(ctrls);
+ return SLAPI_BIND_FAIL;
+ }
+ if (ctrls)
+ ldap_controls_free(ctrls);
+
+ slapi_pblock_get( pb, SLAPI_BACKEND, &be );
+ slapi_pblock_get( pb, SLAPI_BIND_TARGET, &dn );
+ slapi_pblock_get( pb, SLAPI_BIND_METHOD, &method );
+ slapi_pblock_get( pb, SLAPI_BIND_SASLMECHANISM, &mechanism);
+ slapi_pblock_get( pb, SLAPI_BIND_CREDENTIALS, &creds );
+ slapi_pblock_get( pb, SLAPI_REQCONTROLS, &reqctrls );
+ cb = cb_get_instance(be);
+
+ if ( NULL == dn )
+ dn="";
+
+ /* always allow noauth simple binds */
+ if (( method == LDAP_AUTH_SIMPLE) && creds->bv_len == 0 ) {
+ return( SLAPI_BIND_ANONYMOUS );
+ }
+
+ cb_update_monitor_info(pb,cb,SLAPI_OPERATION_BIND);
+
+ matcheddn=errmsg=NULL;
+ allocated_errmsg = 0;
+ resctrls=NULL;
+ urls=NULL;
+
+ /* Check wether the chaining BE is available or not */
+ if ( cb_check_availability( cb, pb ) == FARMSERVER_UNAVAILABLE ){
+ return -1;
+ }
+
+ PR_RWLock_Rlock(cb->rwl_config_lock);
+ bind_retry=cb->bind_retry;
+ PR_RWLock_Unlock(cb->rwl_config_lock);
+
+ if ( LDAP_SUCCESS == (rc = cb_sasl_bind_s(pb, cb->bind_pool, bind_retry, dn,method,mechanism,
+ creds,reqctrls,&matcheddn,&errmsg,&urls,&resctrls, &status))) {
+ rc = status;
+ allocated_errmsg = 1;
+ } else
+ if ( LDAP_USER_CANCELLED != rc ) {
+ errmsg = ldap_err2string( rc );
+ if (rc == LDAP_TIMEOUT) {
+ cb_ping_farm(cb,NULL,NULL);
+ }
+ rc = LDAP_OPERATIONS_ERROR;
+ }
+
+ if ( rc != LDAP_USER_CANCELLED ) { /* not abandoned */
+ if ( resctrls != NULL ) {
+ slapi_pblock_set( pb, SLAPI_RESCONTROLS, resctrls );
+ freectrls=0;
+ }
+
+ if ( rc != LDAP_SUCCESS ) {
+ cb_send_ldap_result( pb, rc, matcheddn, errmsg, 0, urls );
+ }
+ }
+
+ if ( urls != NULL ) {
+ cb_free_bervals( urls );
+ }
+ if ( freectrls && ( resctrls != NULL )) {
+ ldap_controls_free( resctrls );
+ }
+ slapi_ch_free((void **)& matcheddn );
+ if ( allocated_errmsg && errmsg != NULL ) {
+ slapi_ch_free((void **)& errmsg );
+ }
+
+ return ((rc == LDAP_SUCCESS ) ? SLAPI_BIND_SUCCESS : SLAPI_BIND_FAIL );
+}
+
+static void
+cb_free_bervals( struct berval **bvs )
+{
+ int i;
+
+ if ( bvs != NULL ) {
+ for ( i = 0; bvs[ i ] != NULL; ++i ) {
+ slapi_ch_free( (void **)&bvs[ i ] );
+ }
+ }
+ slapi_ch_free( (void **)&bvs );
+}
+
diff --git a/ldap/servers/plugins/chainingdb/cb_cleanup.c b/ldap/servers/plugins/chainingdb/cb_cleanup.c
new file mode 100644
index 00000000..b034b0a1
--- /dev/null
+++ b/ldap/servers/plugins/chainingdb/cb_cleanup.c
@@ -0,0 +1,22 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+#include "cb.h"
+
+/*
+** cLeanup a chaining backend instance
+*/
+
+int cb_back_cleanup( Slapi_PBlock *pb )
+{
+
+ /*
+ ** Connections have been closed in cb_back_close()
+ ** For now, don't do more
+ */
+
+ return 0;
+}
+
diff --git a/ldap/servers/plugins/chainingdb/cb_close.c b/ldap/servers/plugins/chainingdb/cb_close.c
new file mode 100644
index 00000000..3ef52e32
--- /dev/null
+++ b/ldap/servers/plugins/chainingdb/cb_close.c
@@ -0,0 +1,67 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+#include "cb.h"
+
+/*
+** Close a chaining backend instance
+** Should be followed by a cleanup
+*/
+
+int cb_back_close( Slapi_PBlock *pb )
+{
+ Slapi_Backend * be;
+ cb_backend_instance * inst;
+ int rc;
+
+ slapi_pblock_get( pb, SLAPI_BACKEND, &be );
+ if (be == NULL) {
+
+ cb_backend * cb = cb_get_backend_type();
+ CB_ASSERT(cb!=NULL);
+
+ slapi_config_remove_callback(SLAPI_OPERATION_MODIFY, DSE_FLAG_POSTOP, cb->configDN, LDAP_SCOPE_BASE,
+ "(objectclass=*)",cb_config_modify_callback);
+ slapi_config_remove_callback(SLAPI_OPERATION_MODIFY, DSE_FLAG_PREOP, cb->configDN, LDAP_SCOPE_BASE,
+ "(objectclass=*)",cb_config_modify_check_callback);
+
+ slapi_config_remove_callback(SLAPI_OPERATION_ADD, DSE_FLAG_POSTOP, cb->configDN, LDAP_SCOPE_BASE,
+ "(objectclass=*)",cb_config_add_callback);
+ slapi_config_remove_callback(SLAPI_OPERATION_ADD, DSE_FLAG_PREOP, cb->configDN, LDAP_SCOPE_BASE,
+ "(objectclass=*)",cb_config_add_check_callback);
+
+ slapi_config_remove_callback(SLAPI_OPERATION_SEARCH, DSE_FLAG_PREOP, cb->configDN, LDAP_SCOPE_BASE,
+ "(objectclass=*)",cb_config_search_callback);
+
+ slapi_config_remove_callback(SLAPI_OPERATION_ADD, DSE_FLAG_POSTOP, cb->pluginDN,
+ LDAP_SCOPE_SUBTREE, CB_CONFIG_INSTANCE_FILTER, cb_config_add_instance_callback);
+
+ return 0;
+ }
+
+ /* XXXSD: temp fix . Sometimes, this functions */
+ /* gets called with a ldbm backend instance... */
+
+ {
+ const char * betype = slapi_be_gettype(be);
+ if (!betype || strcasecmp(betype,CB_CHAINING_BACKEND_TYPE)) {
+
+ slapi_log_error( SLAPI_LOG_FATAL, CB_PLUGIN_SUBSYSTEM,
+ "Wrong database type.\n");
+ return 0;
+ }
+ }
+
+ inst = cb_get_instance(be);
+ CB_ASSERT( inst!=NULL );
+
+ slapi_log_error( SLAPI_LOG_PLUGIN, CB_PLUGIN_SUBSYSTEM,"Stopping chaining database instance %s\n",
+ inst->configDn);
+ /* emulate a backend instance deletion */
+ /* to clean up everything */
+ cb_instance_delete_config_callback(NULL, NULL,NULL, &rc, NULL, inst);
+
+ return 0;
+}
diff --git a/ldap/servers/plugins/chainingdb/cb_compare.c b/ldap/servers/plugins/chainingdb/cb_compare.c
new file mode 100644
index 00000000..ebce1645
--- /dev/null
+++ b/ldap/servers/plugins/chainingdb/cb_compare.c
@@ -0,0 +1,217 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+#include "cb.h"
+
+/*
+ * Perform a compare operation
+ *
+ * Returns:
+ * 0 - success
+ * <0 - fail
+ *
+ */
+
+int
+chaining_back_compare ( Slapi_PBlock *pb )
+{
+
+ Slapi_Backend * be;
+ cb_backend_instance *cb=NULL;
+ struct berval *bval=NULL;
+ LDAPControl **ctrls, **serverctrls;
+ int rc,parse_rc,msgid,i,checkacl;
+ LDAP *ld=NULL;
+ char **referrals=NULL;
+ LDAPMessage * res;
+ char *type,*dn,* matched_msg, *error_msg;
+ char *cnxerrbuf=NULL;
+ time_t endtime;
+ cb_outgoing_conn *cnx;
+
+ if ( LDAP_SUCCESS != (rc=cb_forward_operation(pb) )) {
+ cb_send_ldap_result( pb, rc, NULL, "Chaining forbidden", 0, NULL );
+ return -1;
+ }
+
+ slapi_pblock_get( pb, SLAPI_BACKEND, &be );
+ cb = cb_get_instance(be);
+
+ cb_update_monitor_info(pb,cb,SLAPI_OPERATION_COMPARE);
+
+ /* Check wether the chaining BE is available or not */
+ if ( cb_check_availability( cb, pb ) == FARMSERVER_UNAVAILABLE ){
+ return -1;
+ }
+
+ slapi_pblock_get( pb, SLAPI_COMPARE_TARGET, &dn );
+ slapi_pblock_get( pb, SLAPI_COMPARE_TYPE, &type );
+ slapi_pblock_get( pb, SLAPI_COMPARE_VALUE, &bval );
+
+ /*
+ * Check local acls
+ * No need to lock the config to access cb->local_acl
+ */
+
+ checkacl=cb->local_acl && !cb->associated_be_is_disabled;
+
+ if (checkacl) {
+ char * errbuf=NULL;
+ Slapi_Entry *te = slapi_entry_alloc();
+ slapi_entry_set_dn(te,slapi_ch_strdup(dn));
+ rc = cb_access_allowed (pb, te, type, bval, SLAPI_ACL_COMPARE,&errbuf);
+ slapi_entry_free(te);
+
+ if ( rc != LDAP_SUCCESS ) {
+ cb_send_ldap_result( pb, rc, NULL, errbuf, 0, NULL );
+ slapi_ch_free((void **) &errbuf);
+ return 1;
+ }
+ }
+
+ /*
+ * Grab a connection handle
+ */
+
+ if ((rc = cb_get_connection(cb->pool,&ld,&cnx,NULL,&cnxerrbuf)) != LDAP_SUCCESS) {
+ cb_send_ldap_result( pb, LDAP_OPERATIONS_ERROR, NULL, cnxerrbuf, 0, NULL);
+ slapi_ch_free((void **)&cnxerrbuf);
+ /* ping the farm. If the farm is unreachable, we increment the counter */
+ cb_ping_farm(cb,NULL,0);
+ return 1;
+ }
+
+ /*
+ * Control management
+ */
+
+ if ( (rc = cb_update_controls( pb,ld,&ctrls,CB_UPDATE_CONTROLS_ADDAUTH )) != LDAP_SUCCESS ) {
+ cb_send_ldap_result( pb, rc, NULL,NULL, 0, NULL);
+ cb_release_op_connection(cb->pool,ld,CB_LDAP_CONN_ERROR(rc));
+ return 1;
+ }
+
+ if ( slapi_op_abandoned( pb )) {
+ cb_release_op_connection(cb->pool,ld,0);
+ if ( NULL != ctrls)
+ ldap_controls_free(ctrls);
+ return -1;
+ }
+
+ /* heart-beat management */
+ if (cb->max_idle_time>0)
+ endtime=current_time() + cb->max_idle_time;
+
+ /*
+ * Send LDAP operation to the remote host
+ */
+
+ rc = ldap_compare_ext( ld, dn, type, bval, ctrls, NULL, &msgid );
+ if ( NULL != ctrls)
+ ldap_controls_free(ctrls);
+
+ if ( rc != LDAP_SUCCESS ) {
+ cb_send_ldap_result( pb, LDAP_OPERATIONS_ERROR, NULL,
+ ldap_err2string(rc), 0, NULL);
+ cb_release_op_connection(cb->pool,ld,CB_LDAP_CONN_ERROR(rc));
+ return 1;
+ }
+
+ while ( 1 ) {
+
+ if (cb_check_forward_abandon(cb,pb,ld,msgid)) {
+ return -1;
+ }
+
+ /* No need to lock the config to access cb->abandon_timeout */
+ rc = ldap_result( ld, msgid, 0, &cb->abandon_timeout, &res );
+ switch ( rc ) {
+ case -1:
+ cb_send_ldap_result( pb, LDAP_OPERATIONS_ERROR, NULL,
+ ldap_err2string(rc), 0, NULL);
+ cb_release_op_connection(cb->pool,ld,CB_LDAP_CONN_ERROR(rc));
+ if (res)
+ ldap_msgfree(res);
+ return 1;
+ case 0:
+ if ((rc=cb_ping_farm(cb,cnx,endtime)) != LDAP_SUCCESS) {
+
+ /* does not respond. give up and return a*/
+ /* error to the client. */
+
+ /*cb_send_ldap_result(pb,LDAP_OPERATIONS_ERROR, NULL,
+ ldap_err2string(rc), 0, NULL);*/
+ cb_send_ldap_result(pb,LDAP_OPERATIONS_ERROR, NULL, "FARM SERVER TEMPORARY UNAVAILABLE", 0, NULL);
+ cb_release_op_connection(cb->pool,ld,CB_LDAP_CONN_ERROR(rc));
+ if (res)
+ ldap_msgfree(res);
+ return 1;
+ }
+#ifdef CB_YIELD
+ DS_Sleep(PR_INTERVAL_NO_WAIT);
+#endif
+ break;
+ default:
+ matched_msg=error_msg=NULL;
+ parse_rc = ldap_parse_result( ld, res, &rc, &matched_msg,
+ &error_msg, &referrals, &serverctrls, 1 );
+ if ( parse_rc != LDAP_SUCCESS ) {
+
+ cb_send_ldap_result( pb, LDAP_OPERATIONS_ERROR, NULL,
+ ldap_err2string(parse_rc), 0, NULL);
+ cb_release_op_connection(cb->pool,ld,CB_LDAP_CONN_ERROR(parse_rc));
+ slapi_ch_free((void **)&matched_msg);
+ slapi_ch_free((void **)&error_msg);
+ if (serverctrls)
+ ldap_controls_free(serverctrls);
+ /* jarnou: free referrals */
+ if (referrals)
+ charray_free(referrals);
+ return 1;
+ }
+
+ switch ( rc ) {
+
+ case LDAP_COMPARE_TRUE:
+ case LDAP_COMPARE_FALSE:
+ break;
+ default: {
+ struct berval ** refs = referrals2berval(referrals);
+
+ cb_send_ldap_result( pb, rc, matched_msg, error_msg, 0, refs);
+ cb_release_op_connection(cb->pool,ld,CB_LDAP_CONN_ERROR(rc));
+ slapi_ch_free((void **)&matched_msg);
+ slapi_ch_free((void **)&error_msg);
+ if (refs)
+ ber_bvecfree(refs);
+ if (referrals)
+ charray_free(referrals);
+ if (serverctrls)
+ ldap_controls_free(serverctrls);
+ return 1;
+ }
+ }
+
+ /* Add control response sent by the farm server */
+
+ for (i=0; serverctrls && serverctrls[i];i++)
+ slapi_pblock_set( pb, SLAPI_ADD_RESCONTROL, serverctrls[i]);
+ if (serverctrls)
+ ldap_controls_free(serverctrls);
+ /* jarnou: free matched_msg, error_msg, and referrals if necessary */
+ slapi_ch_free((void **)&matched_msg);
+ slapi_ch_free((void **)&error_msg);
+ if (referrals)
+ charray_free(referrals);
+
+ cb_send_ldap_result( pb, rc , NULL, NULL, 0, NULL );
+ cb_release_op_connection(cb->pool,ld,0);
+ return 0;
+ }
+ }
+
+ /* Never reached */
+ /* return 0; */
+}
diff --git a/ldap/servers/plugins/chainingdb/cb_config.c b/ldap/servers/plugins/chainingdb/cb_config.c
new file mode 100644
index 00000000..13dd7b22
--- /dev/null
+++ b/ldap/servers/plugins/chainingdb/cb_config.c
@@ -0,0 +1,600 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+#include "cb.h"
+#include <errno.h>
+
+/* Forward declarations */
+
+static int cb_parse_config_entry(cb_backend * cb, Slapi_Entry *e);
+
+/* body starts here */
+
+/* Used to add an array of entries, like the one above and
+** cb_instance_skeleton_entries to the dse.
+** Returns 0 on success.
+*/
+
+
+
+int cb_config_add_dse_entries(cb_backend *cb, char **entries, char *string1, char *string2, char *string3)
+{
+ int x;
+ Slapi_Entry *e;
+ Slapi_PBlock *util_pb = NULL;
+ int res, rc = 0;
+ char entry_string[CB_BUFSIZE];
+
+ for(x = 0; strlen(entries[x]) > 0; x++) {
+ util_pb = slapi_pblock_new();
+ sprintf(entry_string, entries[x], string1, string2, string3);
+ e = slapi_str2entry(entry_string, 0);
+ slapi_add_entry_internal_set_pb(util_pb, e, NULL, cb->identity, 0);
+ slapi_add_internal_pb(util_pb);
+ slapi_pblock_get(util_pb, SLAPI_PLUGIN_INTOP_RESULT, &res);
+ if ( LDAP_SUCCESS != res && LDAP_ALREADY_EXISTS != res ) {
+ char ebuf[ BUFSIZ ];
+ slapi_log_error(SLAPI_LOG_PLUGIN, CB_PLUGIN_SUBSYSTEM,
+ "Unable to add config entry (%s) to the DSE: %s\n",
+ escape_string(slapi_entry_get_dn(e), ebuf),
+ ldap_err2string(res));
+ rc = res;
+ slapi_pblock_destroy(util_pb);
+ break;
+ }
+ slapi_pblock_destroy(util_pb);
+ }
+ return rc;
+}
+
+/*
+** Try to read the entry cn=config,cn=chaining database,cn=plugins,cn=config
+** If the entry is there, then process the configuration information it stores.
+** If it is missing, create it with default configuration.
+** The default configuration is taken from the default entry if it exists
+*/
+
+int cb_config_load_dse_info(Slapi_PBlock * pb) {
+
+ Slapi_PBlock *search_pb,*default_pb;
+ Slapi_Entry **entries = NULL;
+ Slapi_Entry *configEntry=NULL;
+ int res,default_res,i;
+ char defaultDn[CB_BUFSIZE];
+ cb_backend *cb;
+
+ slapi_pblock_get( pb, SLAPI_PLUGIN_PRIVATE, &cb );
+
+ /* Get global configuration entry */
+ search_pb = slapi_pblock_new();
+ slapi_search_internal_set_pb(search_pb, cb->configDN, LDAP_SCOPE_BASE,
+ "objectclass=*", NULL, 0, NULL, NULL, cb->identity, 0);
+ slapi_search_internal_pb (search_pb);
+ slapi_pblock_get(search_pb, SLAPI_PLUGIN_INTOP_RESULT, &res);
+
+ if ( LDAP_SUCCESS == res ) {
+ slapi_pblock_get(search_pb, SLAPI_PLUGIN_INTOP_SEARCH_ENTRIES, &entries);
+ if (NULL == entries || entries[0] == NULL) {
+ slapi_log_error(SLAPI_LOG_PLUGIN, CB_PLUGIN_SUBSYSTEM,
+ "Error accessing entry <%s>\n",cb->configDN);
+ slapi_free_search_results_internal(search_pb);
+ slapi_pblock_destroy(search_pb);
+ return 1;
+ }
+ configEntry=entries[0];
+ } else
+ if ( LDAP_NO_SUCH_OBJECT == res ) {
+ /* Don't do anything. The default conf is used */
+ configEntry=NULL;
+ } else {
+ slapi_free_search_results_internal(search_pb);
+ slapi_pblock_destroy(search_pb);
+ slapi_log_error(SLAPI_LOG_PLUGIN, CB_PLUGIN_SUBSYSTEM,
+ "Error accessing entry <%s> (%s)\n",cb->configDN,ldap_err2string(res));
+ return 1;
+ }
+
+ /* Parse the configuration entry */
+ /* Default config if configEntry is NULL*/
+
+ cb_parse_config_entry(cb, configEntry);
+ slapi_free_search_results_internal(search_pb);
+ slapi_pblock_destroy(search_pb);
+
+ /*
+ ** Parse the chaining backend instances
+ ** Immediate subordinates of cn=<plugin name>,cn=plugins,cn=config
+ */
+
+ search_pb = slapi_pblock_new();
+
+ slapi_search_internal_set_pb(search_pb, cb->pluginDN, LDAP_SCOPE_ONELEVEL,
+ CB_CONFIG_INSTANCE_FILTER,NULL,0,NULL,NULL,cb->identity, 0);
+ slapi_search_internal_pb (search_pb);
+ slapi_pblock_get(search_pb, SLAPI_PLUGIN_INTOP_RESULT, &res);
+ if (res != LDAP_SUCCESS) {
+ slapi_log_error(SLAPI_LOG_PLUGIN, CB_PLUGIN_SUBSYSTEM,
+ "Error accessing the config DSE (%s)\n",ldap_err2string(res));
+ slapi_free_search_results_internal(search_pb);
+ slapi_pblock_destroy(search_pb);
+ return 1;
+ }
+
+ /* Get the default instance value entry if it exists */
+ /* else create it */
+
+ sprintf(defaultDn,"cn=default instance config,%s",cb->pluginDN);
+
+ default_pb = slapi_pblock_new();
+ slapi_search_internal_set_pb(default_pb, defaultDn, LDAP_SCOPE_BASE,
+ "objectclass=*", NULL, 0, NULL, NULL, cb->identity, 0);
+ slapi_search_internal_pb (default_pb);
+ slapi_pblock_get(default_pb, SLAPI_PLUGIN_INTOP_RESULT, &default_res);
+ if (LDAP_SUCCESS != default_res) {
+ cb_create_default_backend_instance_config(cb);
+ }
+
+ slapi_free_search_results_internal(default_pb);
+ slapi_pblock_destroy(default_pb);
+
+ slapi_pblock_get(search_pb, SLAPI_PLUGIN_INTOP_SEARCH_ENTRIES, &entries);
+ for (i=0; entries && entries[i]; i++) {
+ int retcode;
+ char * aDn=slapi_entry_get_dn(entries[i]);
+ slapi_dn_normalize(aDn);
+
+ cb_instance_add_config_callback(pb,entries[i],NULL,&retcode,NULL,cb);
+ }
+
+ slapi_free_search_results_internal(search_pb);
+ slapi_pblock_destroy(search_pb);
+
+
+ /* Add callbacks */
+ slapi_config_register_callback(SLAPI_OPERATION_MODIFY, DSE_FLAG_PREOP, cb->configDN,
+ LDAP_SCOPE_BASE, "(objectclass=*)",cb_config_modify_check_callback, (void *) cb);
+ slapi_config_register_callback(SLAPI_OPERATION_MODIFY, DSE_FLAG_POSTOP, cb->configDN,
+ LDAP_SCOPE_BASE, "(objectclass=*)",cb_config_modify_callback, (void *) cb);
+
+ slapi_config_register_callback(SLAPI_OPERATION_ADD, DSE_FLAG_PREOP, cb->configDN,
+ LDAP_SCOPE_BASE, "(objectclass=*)",cb_config_add_check_callback, (void *) cb);
+ slapi_config_register_callback(SLAPI_OPERATION_ADD, DSE_FLAG_POSTOP, cb->configDN,
+ LDAP_SCOPE_BASE, "(objectclass=*)",cb_config_add_callback, (void *) cb);
+
+ slapi_config_register_callback(SLAPI_OPERATION_SEARCH, DSE_FLAG_PREOP, cb->configDN,
+ LDAP_SCOPE_BASE, "(objectclass=*)",cb_config_search_callback, (void *) cb);
+
+ /* instance creation */
+ slapi_config_register_callback(SLAPI_OPERATION_ADD, DSE_FLAG_PREOP, cb->pluginDN,
+ LDAP_SCOPE_SUBTREE, CB_CONFIG_INSTANCE_FILTER, cb_config_add_instance_check_callback, (void *) cb);
+
+ slapi_config_register_callback(SLAPI_OPERATION_ADD, DSE_FLAG_POSTOP, cb->pluginDN,
+ LDAP_SCOPE_SUBTREE, CB_CONFIG_INSTANCE_FILTER, cb_config_add_instance_callback, (void *) cb);
+
+ return 0;
+}
+
+/* Check validity of the modification */
+
+int cb_config_add_check_callback(Slapi_PBlock *pb, Slapi_Entry* e, Slapi_Entry* e2, int *returncode,
+ char *returntext, void *arg)
+{
+ Slapi_Attr *attr = NULL;
+ Slapi_Value *sval;
+ struct berval * bval;
+ int i;
+ cb_backend *cb = (cb_backend *) arg;
+
+ CB_ASSERT (cb!=NULL);
+
+ for (slapi_entry_first_attr(e, &attr); attr; slapi_entry_next_attr(e, attr, &attr)) {
+ char * attr_name=NULL;
+ slapi_attr_get_type(attr, &attr_name);
+
+ if ( !strcasecmp ( attr_name, CB_CONFIG_GLOBAL_FORWARD_CTRLS )) {
+ /* First, parse the values to make sure they are valid */
+ i = slapi_attr_first_value(attr, &sval);
+ while (i != -1 ) {
+ bval = (struct berval *) slapi_value_get_berval(sval);
+ if (!cb_is_control_forwardable(cb,bval->bv_val)) {
+ slapi_log_error(SLAPI_LOG_PLUGIN,CB_PLUGIN_SUBSYSTEM,
+ "control %s can't be forwarded.\n",bval->bv_val);
+ *returncode=LDAP_CONSTRAINT_VIOLATION;
+ return SLAPI_DSE_CALLBACK_ERROR;
+ }
+ i = slapi_attr_next_value(attr, i, &sval);
+ }
+ }
+ }
+ *returncode=LDAP_SUCCESS;
+ return SLAPI_DSE_CALLBACK_OK;
+}
+
+/*
+** Global config is beeing added
+** Take the new values into account
+*/
+
+int
+cb_config_add_callback(Slapi_PBlock *pb, Slapi_Entry* e, Slapi_Entry* e2, int *returncode,
+ char *returntext, void *arg)
+{
+ Slapi_Attr *attr = NULL;
+ Slapi_Value *sval;
+ struct berval * bval;
+ int i;
+ cb_backend *cb = (cb_backend *) arg;
+
+ CB_ASSERT (cb!=NULL);
+
+ for (slapi_entry_first_attr(e, &attr); attr; slapi_entry_next_attr(e, attr, &attr)) {
+ char * attr_name=NULL;
+ slapi_attr_get_type(attr, &attr_name);
+
+ if ( !strcasecmp ( attr_name, CB_CONFIG_GLOBAL_FORWARD_CTRLS )) {
+ /* First, parse the values to make sure they are valid */
+ i = slapi_attr_first_value(attr, &sval);
+ while (i != -1 ) {
+ bval = (struct berval *) slapi_value_get_berval(sval);
+ if (!cb_is_control_forwardable(cb,bval->bv_val)) {
+ slapi_log_error(SLAPI_LOG_PLUGIN,CB_PLUGIN_SUBSYSTEM,
+ "control %s can't be forwarded.\n",bval->bv_val);
+ *returncode=LDAP_CONSTRAINT_VIOLATION;
+ return SLAPI_DSE_CALLBACK_ERROR;
+ }
+ i = slapi_attr_next_value(attr, i, &sval);
+ }
+ /* second pass. apply changes */
+ cb_unregister_all_supported_control(cb);
+ i = slapi_attr_first_value(attr, &sval);
+ while (i != -1 ) {
+ bval = (struct berval *) slapi_value_get_berval(sval);
+ cb_register_supported_control(cb,bval->bv_val,0);
+ i = slapi_attr_next_value(attr, i, &sval);
+ }
+ }
+ }
+ *returncode=LDAP_SUCCESS;
+ return SLAPI_DSE_CALLBACK_OK;
+}
+
+int
+cb_config_search_callback(Slapi_PBlock *pb, Slapi_Entry* e, Slapi_Entry* e2, int *returncode,
+ char *returntext, void *arg) {
+
+ cb_backend *cb = (cb_backend *) arg;
+ struct berval val;
+ struct berval *vals[2];
+ int i = 0;
+
+ CB_ASSERT (cb!=NULL);
+
+ vals[0] = &val;
+ vals[1] = NULL;
+
+ /* naming attribute */
+ val.bv_val = "config";
+ val.bv_len = strlen( val.bv_val );
+ slapi_entry_attr_replace( e, "cn", (struct berval **)vals );
+
+ /* objectclass attribute */
+ val.bv_val = "top";
+ val.bv_len = strlen( val.bv_val );
+ slapi_entry_attr_replace( e, "objectclass", (struct berval **)vals );
+ val.bv_val = CB_CONFIG_EXTENSIBLEOCL;
+ val.bv_len = strlen( val.bv_val );
+ slapi_entry_attr_merge( e, "objectclass", (struct berval **)vals );
+
+ /* other attributes */
+
+ PR_RWLock_Rlock(cb->config.rwl_config_lock);
+
+ for (i=0; cb->config.forward_ctrls && cb->config.forward_ctrls[i] ; i++) {
+ val.bv_val=cb->config.forward_ctrls[i];
+ val.bv_len = strlen( val.bv_val );
+ if (i==0)
+ slapi_entry_attr_replace( e, CB_CONFIG_GLOBAL_FORWARD_CTRLS, (struct berval **)vals );
+ else
+ slapi_entry_attr_merge( e, CB_CONFIG_GLOBAL_FORWARD_CTRLS, (struct berval **)vals );
+ }
+
+ for (i=0;cb->config.chaining_components && cb->config.chaining_components[i];i++) {
+ val.bv_val=cb->config.chaining_components[i];
+ val.bv_len = strlen( val.bv_val );
+ if (i==0)
+ slapi_entry_attr_replace( e, CB_CONFIG_GLOBAL_CHAINING_COMPONENTS,
+ (struct berval **)vals );
+ else
+ slapi_entry_attr_merge( e, CB_CONFIG_GLOBAL_CHAINING_COMPONENTS,
+ (struct berval **)vals );
+ }
+
+ for (i=0; cb->config.chainable_components && cb->config.chainable_components[i]; i++) {
+ val.bv_val=cb->config.chainable_components[i];
+ val.bv_len = strlen( val.bv_val );
+ if (i==0)
+ slapi_entry_attr_replace( e, CB_CONFIG_GLOBAL_CHAINABLE_COMPONENTS,
+ (struct berval **)vals );
+ else
+ slapi_entry_attr_merge( e, CB_CONFIG_GLOBAL_CHAINABLE_COMPONENTS,
+ (struct berval **)vals );
+ }
+
+
+ PR_RWLock_Unlock(cb->config.rwl_config_lock);
+
+ *returncode = LDAP_SUCCESS;
+ return SLAPI_DSE_CALLBACK_OK;
+}
+
+/* Check validity of the modification */
+
+int
+cb_config_modify_check_callback(Slapi_PBlock *pb, Slapi_Entry* entryBefore, Slapi_Entry* e, int *returncode,
+ char *returntext, void *arg)
+{
+ LDAPMod **mods;
+ char *attr_name;
+ int i,j;
+ cb_backend *cb = (cb_backend *) arg;
+
+ CB_ASSERT (cb!=NULL);
+
+ slapi_pblock_get( pb, SLAPI_MODIFY_MODS, &mods );
+
+ for (i = 0; mods[i] ; i++) {
+ attr_name = mods[i]->mod_type;
+
+ if ( !strcasecmp ( attr_name, CB_CONFIG_GLOBAL_FORWARD_CTRLS )) {
+ char * config_attr_value;
+ for (j = 0; mods[i]->mod_bvalues && mods[i]->mod_bvalues[j]; j++) {
+ config_attr_value = (char *) mods[i]->mod_bvalues[j]->bv_val;
+ if (!cb_is_control_forwardable(cb,config_attr_value)) {
+ slapi_log_error(SLAPI_LOG_PLUGIN,CB_PLUGIN_SUBSYSTEM,
+ "control %s can't be forwarded.\n",config_attr_value);
+ *returncode=LDAP_CONSTRAINT_VIOLATION;
+ return SLAPI_DSE_CALLBACK_ERROR;
+ }
+ }
+ }
+ }
+ *returncode=LDAP_SUCCESS;
+ return SLAPI_DSE_CALLBACK_OK;
+}
+
+int
+cb_config_modify_callback(Slapi_PBlock *pb, Slapi_Entry* entryBefore, Slapi_Entry* e, int *returncode,
+ char *returntext, void *arg)
+{
+ LDAPMod **mods;
+ char *attr_name;
+ int i,j;
+ cb_backend *cb = (cb_backend *) arg;
+
+ CB_ASSERT (cb!=NULL);
+
+ slapi_pblock_get( pb, SLAPI_MODIFY_MODS, &mods );
+
+ for (i = 0; mods[i] ; i++) {
+ attr_name = mods[i]->mod_type;
+
+ if ( !strcasecmp ( attr_name, CB_CONFIG_GLOBAL_FORWARD_CTRLS )) {
+ char * config_attr_value;
+ int done=0;
+ for (j = 0; mods[i]->mod_bvalues && mods[i]->mod_bvalues[j]; j++) {
+ config_attr_value = (char *) mods[i]->mod_bvalues[j]->bv_val;
+ if (!cb_is_control_forwardable(cb,config_attr_value)) {
+ slapi_log_error(SLAPI_LOG_PLUGIN,CB_PLUGIN_SUBSYSTEM,
+ "control %s can't be forwarded.\n",config_attr_value);
+ *returncode=LDAP_CONSTRAINT_VIOLATION;
+ return SLAPI_DSE_CALLBACK_ERROR;
+ }
+
+ if ( mods[i]->mod_op & LDAP_MOD_REPLACE) {
+ if (!done) {
+ cb_unregister_all_supported_control(cb);
+ done=1;
+ }
+ cb_register_supported_control(cb,config_attr_value,0);
+ } else
+ if ( (mods[i]->mod_op & ~LDAP_MOD_BVALUES) == LDAP_MOD_ADD) {
+ cb_register_supported_control(cb,config_attr_value,0);
+ } else
+ if ( (mods[i]->mod_op & ~LDAP_MOD_BVALUES) == LDAP_MOD_DELETE) {
+ cb_unregister_supported_control(cb,config_attr_value,0);
+ }
+ }
+ if (NULL == mods[i]->mod_bvalues)
+ cb_unregister_all_supported_control(cb);
+ } else
+ if ( !strcasecmp ( attr_name, CB_CONFIG_GLOBAL_DEBUG )) {
+ /* assume single-valued */
+ if (mods[i]->mod_op & LDAP_MOD_DELETE)
+ cb_set_debug(0);
+ else if ((mods[i]->mod_op & ~LDAP_MOD_BVALUES) == LDAP_MOD_ADD)
+ cb_set_debug(1);
+ } else
+ if ( !strcasecmp ( attr_name, CB_CONFIG_GLOBAL_CHAINING_COMPONENTS )) {
+ char * config_attr_value;
+ int done=0;
+
+ PR_RWLock_Wlock(cb->config.rwl_config_lock);
+
+ for (j = 0; mods[i]->mod_bvalues && mods[i]->mod_bvalues[j]; j++) {
+ config_attr_value = (char *) mods[i]->mod_bvalues[j]->bv_val;
+ if ( mods[i]->mod_op & LDAP_MOD_REPLACE) {
+ if (!done) {
+ charray_free(cb->config.chaining_components);
+ cb->config.chaining_components=NULL;
+ done=1;
+ }
+ /* XXXSD assume dn. Normalize it */
+ charray_add(&cb->config.chaining_components,
+ slapi_dn_normalize(slapi_ch_strdup(config_attr_value)));
+ } else
+ if ( (mods[i]->mod_op & ~LDAP_MOD_BVALUES) == LDAP_MOD_ADD) {
+ charray_add(&cb->config.chaining_components,
+ slapi_dn_normalize(slapi_ch_strdup(config_attr_value)));
+ } else
+ if ( (mods[i]->mod_op & ~LDAP_MOD_BVALUES) == LDAP_MOD_DELETE) {
+ charray_remove(cb->config.chaining_components,
+ slapi_dn_normalize(slapi_ch_strdup(config_attr_value)));
+ }
+ }
+ if (NULL == mods[i]->mod_bvalues) {
+ charray_free(cb->config.chaining_components);
+ cb->config.chaining_components=NULL;
+ }
+
+ PR_RWLock_Unlock(cb->config.rwl_config_lock);
+ } else
+ if ( !strcasecmp ( attr_name, CB_CONFIG_GLOBAL_CHAINABLE_COMPONENTS )) {
+ char * config_attr_value;
+ int done=0;
+
+ PR_RWLock_Wlock(cb->config.rwl_config_lock);
+
+ for (j = 0; mods[i]->mod_bvalues && mods[i]->mod_bvalues[j]; j++) {
+ config_attr_value = (char *) mods[i]->mod_bvalues[j]->bv_val;
+ if ( mods[i]->mod_op & LDAP_MOD_REPLACE) {
+ if (!done) {
+ charray_free(cb->config.chainable_components);
+ cb->config.chainable_components=NULL;
+ done=1;
+ }
+ charray_add(&cb->config.chainable_components,
+ slapi_dn_normalize(slapi_ch_strdup(config_attr_value)
+));
+ } else
+ if ( (mods[i]->mod_op & ~LDAP_MOD_BVALUES) == LDAP_MOD_ADD) {
+ charray_add(&cb->config.chainable_components,
+ slapi_dn_normalize(slapi_ch_strdup(config_attr_value)
+));
+ } else
+ if ( (mods[i]->mod_op & ~LDAP_MOD_BVALUES) == LDAP_MOD_DELETE) {
+ charray_remove(cb->config.chainable_components,
+ slapi_dn_normalize(slapi_ch_strdup(config_attr_value)
+));
+ }
+ }
+ if (NULL == mods[i]->mod_bvalues) {
+ charray_free(cb->config.chainable_components);
+ cb->config.chainable_components=NULL;
+ }
+
+ PR_RWLock_Unlock(cb->config.rwl_config_lock);
+ }
+
+
+ }
+ *returncode=LDAP_SUCCESS;
+ return SLAPI_DSE_CALLBACK_OK;
+}
+
+/*
+** Creation of a new backend instance
+*/
+
+int
+cb_config_add_instance_callback(Slapi_PBlock *pb, Slapi_Entry* entryBefore, Slapi_Entry* e, int *returncode,
+ char *returntext, void *arg)
+{
+ cb_backend *cb=(cb_backend *)arg;
+ CB_ASSERT(cb!=NULL);
+ cb_instance_add_config_callback(pb,entryBefore,NULL,returncode,returntext,cb);
+ return SLAPI_DSE_CALLBACK_OK;
+}
+
+int
+cb_config_add_instance_check_callback(Slapi_PBlock *pb, Slapi_Entry* entryBefore, Slapi_Entry* e, int *returncode,
+ char *returntext, void *arg)
+{
+ cb_backend *cb=(cb_backend *)arg;
+ CB_ASSERT(cb!=NULL);
+ return cb_instance_add_config_check_callback(pb,entryBefore,NULL,returncode,returntext,cb);
+}
+
+/*
+** Parse the global chaining backend configuration
+*/
+
+static int cb_parse_config_entry(cb_backend * cb, Slapi_Entry *e)
+{
+ Slapi_Attr *attr = NULL;
+ Slapi_Value *sval;
+ struct berval *bval;
+ int i;
+
+ if (e == NULL)
+ return LDAP_SUCCESS;
+
+ cb_set_debug(0);
+
+ for (slapi_entry_first_attr(e, &attr); attr; slapi_entry_next_attr(e, attr, &attr)) {
+ char * attr_name=NULL;
+ slapi_attr_get_type(attr, &attr_name);
+
+ if ( !strcasecmp ( attr_name, CB_CONFIG_GLOBAL_FORWARD_CTRLS )) {
+ i = slapi_attr_first_value(attr, &sval);
+
+ PR_RWLock_Wlock(cb->config.rwl_config_lock);
+ if (cb->config.forward_ctrls) {
+ charray_free(cb->config.forward_ctrls);
+ cb->config.forward_ctrls=NULL;
+ }
+ PR_RWLock_Unlock(cb->config.rwl_config_lock);
+
+ while (i != -1 ) {
+ bval = (struct berval *) slapi_value_get_berval(sval);
+ /* For now, don't support operation type */
+ cb_register_supported_control(cb,bval->bv_val,
+ SLAPI_OPERATION_SEARCH | SLAPI_OPERATION_COMPARE |
+ SLAPI_OPERATION_ADD | SLAPI_OPERATION_DELETE |
+ SLAPI_OPERATION_MODIFY | SLAPI_OPERATION_MODDN);
+ i = slapi_attr_next_value(attr, i, &sval);
+ }
+ } else
+ if ( !strcasecmp ( attr_name, CB_CONFIG_GLOBAL_CHAINING_COMPONENTS )) {
+ i = slapi_attr_first_value(attr, &sval);
+ PR_RWLock_Wlock(cb->config.rwl_config_lock);
+ if (cb->config.chaining_components) {
+ charray_free(cb->config.chaining_components);
+ cb->config.chaining_components=NULL;
+ }
+ while (i != -1 ) {
+ bval = (struct berval *) slapi_value_get_berval(sval);
+ /* XXXSD assume dn. Normalize it */
+ charray_add( &cb->config.chaining_components,
+ slapi_dn_normalize(slapi_ch_strdup(bval->bv_val)));
+ i = slapi_attr_next_value(attr, i, &sval);
+ }
+ PR_RWLock_Unlock(cb->config.rwl_config_lock);
+ } else
+ if ( !strcasecmp ( attr_name, CB_CONFIG_GLOBAL_CHAINABLE_COMPONENTS )) {
+ i = slapi_attr_first_value(attr, &sval);
+ PR_RWLock_Wlock(cb->config.rwl_config_lock);
+ if (cb->config.chainable_components) {
+ charray_free(cb->config.chainable_components);
+ cb->config.chainable_components=NULL;
+ }
+ while (i != -1 ) {
+ bval = (struct berval *) slapi_value_get_berval(sval);
+ charray_add( &cb->config.chainable_components,
+ slapi_dn_normalize(slapi_ch_strdup(bval->bv_val)));
+ i = slapi_attr_next_value(attr, i, &sval);
+ }
+ PR_RWLock_Unlock(cb->config.rwl_config_lock);
+ } else
+ if ( !strcasecmp ( attr_name, CB_CONFIG_GLOBAL_DEBUG )) {
+ i = slapi_attr_first_value(attr, &sval);
+ if (i != -1 ) {
+ bval = (struct berval *) slapi_value_get_berval(sval);
+ /* any value */
+ cb_set_debug(1);
+ }
+ }
+ }
+ return LDAP_SUCCESS;
+}
diff --git a/ldap/servers/plugins/chainingdb/cb_conn_stateless.c b/ldap/servers/plugins/chainingdb/cb_conn_stateless.c
new file mode 100644
index 00000000..d97f947e
--- /dev/null
+++ b/ldap/servers/plugins/chainingdb/cb_conn_stateless.c
@@ -0,0 +1,1132 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+#include "cb.h"
+
+/*
+ * Most of the complicated connection-related code lives in this file. Some
+ * general notes about how we manage our connections to "remote" LDAP servers:
+ *
+ * 1) Each farm server we have a relationship with is managed independently.
+ *
+ * 2) We may simultaneously issue multiple requests on a single LDAP
+ * connection. Each server has a "maxconcurrency" configuration
+ * parameter associated with it that caps the number of outstanding operations
+ * per connection. For each connection we maintain a "usecount"
+ * which is used to track the number of threads using the connection.
+ *
+ * 3) IMPORTANT NOTE: This connexion management is stateless i.e there is no garanty that
+ * operation from the same incoming client connections are sent to the same
+ * outgoing connection to the farm server. Today, this is not a problem because
+ * all controls we support are stateless. The implementation of the abandon
+ * operation takes this limitation into account.
+ *
+ * 4) We may open more than one connection to a server. Each farm server
+ * has a "maxconnections" configuration parameter associated with it
+ * that caps the number of connections.
+ *
+ * 5) If no connection is available to service a request , threads
+ * go to sleep on a condition variable and one is woken up each time
+ * a connection's "usecount" is decremented.
+ *
+ * 6) If we see an LDAP_CONNECT_ERROR or LDAP_SERVER_DOWN error on a
+ * session handle, we mark its status as CB_LDAP_STATUS_DOWN and
+ * close it as soon as all threads using it release it. Connections
+ * marked as "down" are not counted against the "maxconnections" limit.
+ *
+ * 7) We close and reopen connections that have been open for more than
+ * the server's configured connection lifetime. This is done to ensure
+ * that we reconnect to a primary server after failover occurs. If no
+ * lifetime is configured or it is set to 0, we never close and reopen
+ * connections.
+ */
+
+static void cb_close_and_dispose_connection ( cb_outgoing_conn * conn );
+static void cb_check_for_stale_connections(cb_conn_pool * pool);
+
+PRUint32 PR_GetThreadID(PRThread *thread);
+
+/* returns the threadId of the current thread modulo MAX_CONN_ARRAY
+=> gives the position of the thread in the array of secure connections */
+static int PR_ThreadSelf() {
+ PRThread *thr = PR_GetCurrentThread();
+ PRUint32 myself = PR_GetThreadID(thr);
+ myself &= 0x000007FF ;
+ return myself;
+}
+
+static int PR_MyThreadId() {
+ PRThread *thr = PR_GetCurrentThread();
+ PRUint32 myself = PR_GetThreadID(thr);
+ return myself;
+}
+
+/*
+** Close outgoing connections
+*/
+
+void cb_close_conn_pool(cb_conn_pool * pool) {
+
+ cb_outgoing_conn *conn, *nextconn;
+ int secure = pool->secure;
+ int i = 0;
+
+ slapi_lock_mutex( pool->conn.conn_list_mutex );
+
+ if (secure) {
+ for (i=0; i< MAX_CONN_ARRAY; i++) {
+ for (conn = pool->connarray[i]; conn != NULL; conn = nextconn) {
+ if ( conn->status != CB_CONNSTATUS_OK ) {
+ slapi_log_error( SLAPI_LOG_PLUGIN, CB_PLUGIN_SUBSYSTEM,
+ "cb_close_conn_pool: unexpected connection state (%d)\n",conn->status);
+ }
+ nextconn=conn->next;
+ cb_close_and_dispose_connection(conn);
+ }
+ }
+ }
+ else {
+ for ( conn = pool->conn.conn_list; conn != NULL; conn = nextconn ) {
+ if ( conn->status != CB_CONNSTATUS_OK ) {
+ slapi_log_error( SLAPI_LOG_PLUGIN, CB_PLUGIN_SUBSYSTEM,
+ "cb_close_conn_pool: unexpected connection state (%d)\n",conn->status);
+ }
+ nextconn=conn->next;
+ cb_close_and_dispose_connection(conn);
+ }
+ }
+
+ pool->conn.conn_list=NULL;
+ pool->conn.conn_list_count=0;
+
+ slapi_unlock_mutex( pool->conn.conn_list_mutex );
+}
+
+/*
+ * Get an LDAP session handle for communicating with the farm servers.
+ *
+ * Returns an LDAP eror code, typically:
+ * LDAP_SUCCESS
+ * LDAP_TIMELIMIT_EXCEEDED
+ * LDAP_CONNECT_ERROR
+ * NOTE : if maxtime NULL, use operation timeout
+ */
+
+int cb_get_connection(cb_conn_pool * pool, LDAP ** lld, cb_outgoing_conn ** cc,struct timeval * maxtime, char **errmsg) {
+
+ int rc=LDAP_SUCCESS; /* optimistic */
+ cb_outgoing_conn *conn=NULL;
+ cb_outgoing_conn *connprev=NULL;
+ LDAP *ld=NULL;
+ time_t endbefore=0;
+ int checktime=0;
+ struct timeval bind_to, op_to;
+ unsigned int maxconcurrency,maxconnections;
+ char *password,*binddn,*hostname;
+ unsigned int port;
+ int secure;
+ static char *error1="Can't contact remote server : %s";
+ static char *error2="Can't bind to remote server : %s";
+ int isMultiThread = ENABLE_MULTITHREAD_PER_CONN ; /* by default, we enable multiple operations per connection */
+
+ /*
+ ** return an error if we can't get a connection
+ ** before the operation timeout has expired
+ ** bind_timeout: timeout for the bind operation (if bind needed)
+ ** ( checked in ldap_result )
+ ** op_timeout: timeout for the op that needs a connection
+ ** ( checked in the loop )
+ */
+ *cc=NULL;
+
+ PR_RWLock_Rlock(pool->rwl_config_lock);
+ maxconcurrency=pool->conn.maxconcurrency;
+ maxconnections=pool->conn.maxconnections;
+ bind_to.tv_sec = pool->conn.bind_timeout.tv_sec;
+ bind_to.tv_usec = pool->conn.bind_timeout.tv_usec;
+ op_to.tv_sec = pool->conn.op_timeout.tv_sec;
+ op_to.tv_usec = pool->conn.op_timeout.tv_usec;
+
+ /* SD 02/10/2000 temp fix */
+ /* allow dynamic update of the binddn & password */
+ /* host, port and security mode */
+ /* previous values are NOT freed when changed */
+ /* won't likely to be changed often */
+ /* pointers put in the waste basket fields and */
+ /* freed when the backend is stopped. */
+
+ password=pool->password;
+ binddn=pool->binddn;
+ hostname=pool->hostname;
+ port=pool->port;
+ secure=pool->secure;
+
+ PR_RWLock_Unlock(pool->rwl_config_lock);
+
+ if (secure) {
+ isMultiThread = DISABLE_MULTITHREAD_PER_CONN ;
+ }
+
+ /* For stupid admins */
+ if (maxconnections <=0) {
+ slapi_log_error( SLAPI_LOG_PLUGIN, CB_PLUGIN_SUBSYSTEM,
+ "<== cb_get_connection error (no connection available)\n");
+ if ( errmsg ) {
+ *errmsg = slapi_ch_malloc(CB_BUFSIZE);
+ sprintf(*errmsg,error1,"no connection available");
+ }
+ return LDAP_CONNECT_ERROR;
+ }
+
+ if (maxtime) {
+ if (maxtime->tv_sec != 0) {
+ checktime=1;
+ endbefore = current_time() + maxtime->tv_sec;
+
+ /* make sure bind to <= operation timeout */
+ if ((bind_to.tv_sec==0) || (bind_to.tv_sec > maxtime->tv_sec))
+ bind_to.tv_sec=maxtime->tv_sec;
+ }
+ } else {
+ if (op_to.tv_sec != 0) {
+ checktime=1;
+ endbefore = current_time() + op_to.tv_sec;
+
+ /* make sure bind to <= operation timeout */
+ if ((bind_to.tv_sec==0) || (bind_to.tv_sec > op_to.tv_sec))
+ bind_to.tv_sec=op_to.tv_sec;
+ }
+ }
+
+ /*
+ * Close (or mark to be closed) any connections for this farm server that have
+ * exceeded the maximum connection lifetime.
+ */
+
+ cb_check_for_stale_connections(pool);
+
+ /*
+ * Look for an available, already open connection
+ */
+
+ slapi_lock_mutex( pool->conn.conn_list_mutex );
+
+ if (cb_debug_on()) {
+ slapi_log_error( SLAPI_LOG_PLUGIN, CB_PLUGIN_SUBSYSTEM,
+ "==> cb_get_connection server %s conns: %d maxconns: %d\n",
+ hostname, pool->conn.conn_list_count, maxconnections );
+ }
+
+ for (;;) {
+
+ /* time limit mgmt */
+ if (checktime) {
+ if (current_time() > endbefore ) {
+ slapi_log_error( SLAPI_LOG_PLUGIN, CB_PLUGIN_SUBSYSTEM,
+ "cb_get_connection server %s expired.\n", hostname );
+ if ( errmsg ) {
+ *errmsg = slapi_ch_malloc(CB_BUFSIZE);
+ sprintf(*errmsg,error1,"timelimit exceeded");
+ }
+ rc=LDAP_TIMELIMIT_EXCEEDED;
+ conn=NULL;
+ ld=NULL;
+ goto unlock_and_return;
+ }
+ }
+
+ /*
+ * First, look for an available, already open/bound connection
+ */
+
+ if (secure) {
+ for (conn = pool->connarray[PR_ThreadSelf()]; conn != NULL; conn = conn->next) {
+ if ((conn->ThreadId == PR_MyThreadId()) && (conn->status == CB_CONNSTATUS_OK &&
+ conn->refcount < maxconcurrency)){
+ if (cb_debug_on()) {
+ slapi_log_error( SLAPI_LOG_PLUGIN, CB_PLUGIN_SUBSYSTEM,
+ "<= cb_get_connection server found conn 0x%x to use)\n", conn );
+ }
+ goto unlock_and_return; /* found one */
+ }
+ }
+ }
+ else {
+ connprev = NULL;
+ for ( conn = pool->conn.conn_list; conn != NULL; conn = conn->next ) {
+ if (cb_debug_on()) {
+ slapi_log_error( SLAPI_LOG_PLUGIN, CB_PLUGIN_SUBSYSTEM,
+ "list: conn 0x%x status %d refcount %d\n", conn,
+ conn->status, conn->refcount );
+ }
+
+ if ( conn->status == CB_CONNSTATUS_OK
+ && conn->refcount < maxconcurrency ) {
+ if (cb_debug_on()) {
+ slapi_log_error( SLAPI_LOG_PLUGIN, CB_PLUGIN_SUBSYSTEM,
+ "<= cb_get_connection server found conn 0x%x to use)\n", conn );
+ }
+ goto unlock_and_return; /* found one */
+ }
+ connprev = conn;
+ }
+ }
+
+ if ( secure || pool->conn.conn_list_count <maxconnections) {
+
+ int version=LDAP_VERSION3;
+
+ /* check wether the security libraries are correctly initialized */
+ if (secure && slapd_security_library_is_initialized() != 1) {
+ slapi_log_error(
+ SLAPI_LOG_FATAL, CB_PLUGIN_SUBSYSTEM,
+ "SSL Not Initialized, Chaining Backend over SSL FAILED\n");
+ rc = LDAP_CONNECT_ERROR;
+ goto unlock_and_return;
+ }
+
+ /*
+ * we have not exceeded the maximum number of connections allowed,
+ * so we initialize a new one and add it to the end of our list.
+ */
+
+ /* No need to lock. url can't be changed dynamically */
+ if ((ld=slapi_ldap_init(hostname,port,secure,isMultiThread))== NULL) {
+ if (cb_debug_on()) {
+ slapi_log_error( SLAPI_LOG_PLUGIN, CB_PLUGIN_SUBSYSTEM,
+ "Can't contact server <%s> port <%d>.\n", hostname, port);
+ }
+ if ( errmsg ) {
+ *errmsg = slapi_ch_malloc(CB_BUFSIZE);
+ sprintf(*errmsg,error1,"unknown reason");
+ }
+ rc = LDAP_CONNECT_ERROR;
+ goto unlock_and_return;
+ }
+
+ ldap_set_option( ld, LDAP_OPT_PROTOCOL_VERSION, &version );
+ /* Don't chase referrals */
+ ldap_set_option( ld, LDAP_OPT_REFERRALS, LDAP_OPT_OFF );
+
+ /* no controls and simple bind only */
+ /* For now, bind even if no user to detect error */
+ /* earlier */
+ if (pool->bindit) {
+ int msgid;
+ LDAPMessage *res=NULL;
+ int parse_rc;
+ PRErrorCode prerr = 0;
+ LDAPControl **serverctrls=NULL;
+ char **referrals=NULL;
+
+ char *plain = NULL;
+ int ret = -1;
+
+ rc=LDAP_SUCCESS;
+
+ if (cb_debug_on()) {
+ slapi_log_error( SLAPI_LOG_PLUGIN, CB_PLUGIN_SUBSYSTEM,
+ "Bind to to server <%s> port <%d> as <%s>\n",
+ hostname, port, binddn);
+ }
+
+ ret = pw_rever_decode(password, &plain, CB_CONFIG_USERPASSWORD);
+
+ /* Pb occured in decryption: stop now, binding will fail */
+ if ( ret == -1 )
+ {
+ if (cb_debug_on()) {
+ slapi_log_error( SLAPI_LOG_PLUGIN, CB_PLUGIN_SUBSYSTEM,
+ "Internal credentials decoding error\n.",
+ 0, 0, 0);
+ }
+ rc = LDAP_LOCAL_ERROR;
+ goto unlock_and_return;
+ }
+
+ /* Password-based client authentication */
+
+ if (( msgid = ldap_simple_bind( ld, binddn, plain)) <0) {
+ rc=ldap_get_lderrno( ld, NULL, NULL );
+ prerr=PR_GetError();
+ }
+ if ( ret == 0 ) free(plain); /* free plain only if it has been duplicated */
+
+ if ( rc != LDAP_SUCCESS ) {
+ if (cb_debug_on()) {
+ slapi_log_error( SLAPI_LOG_PLUGIN, CB_PLUGIN_SUBSYSTEM,
+ "Can't bind to server <%s> port <%d>. "
+ "(LDAP error %d - %s; "
+ SLAPI_COMPONENT_NAME_NSPR " error %d - %s)\n",
+ hostname, port, rc,
+ ldap_err2string(rc),
+ prerr, slapd_pr_strerror(prerr));
+ }
+ if ( errmsg ) {
+ *errmsg = slapi_ch_malloc(CB_BUFSIZE);
+ sprintf(*errmsg,error2, ldap_err2string(rc));
+ }
+ rc = LDAP_CONNECT_ERROR;
+ goto unlock_and_return;
+ }
+
+ rc = ldap_result( ld, msgid, 0, &bind_to, &res );
+ switch (rc) {
+ case -1:
+ rc = ldap_get_lderrno( ld, NULL, NULL );
+ if (cb_debug_on()) {
+ slapi_log_error( SLAPI_LOG_PLUGIN, CB_PLUGIN_SUBSYSTEM,
+ "Can't bind to server <%s> port <%d>. "
+ "(LDAP error %d - %s; "
+ SLAPI_COMPONENT_NAME_NSPR " error %d - %s)\n",
+ hostname, port, rc,
+ ldap_err2string(rc),
+ prerr, slapd_pr_strerror(prerr));
+ }
+ if ( errmsg ) {
+ *errmsg = slapi_ch_malloc(CB_BUFSIZE);
+ sprintf(*errmsg,error2,ldap_err2string(rc));
+ }
+ rc = LDAP_CONNECT_ERROR;
+ goto unlock_and_return;
+ case 0:
+ if (cb_debug_on()) {
+ slapi_log_error( SLAPI_LOG_PLUGIN, CB_PLUGIN_SUBSYSTEM,
+ "Can't bind to server <%s> port <%d>. (%s)\n",
+ hostname, port, "time-out expired");
+ }
+ rc = LDAP_CONNECT_ERROR;
+ goto unlock_and_return;
+ default:
+
+ parse_rc = ldap_parse_result( ld, res, &rc, NULL,
+ NULL, &referrals, &serverctrls, 1 );
+
+ if ( parse_rc != LDAP_SUCCESS ) {
+ if (cb_debug_on()) {
+ slapi_log_error( SLAPI_LOG_PLUGIN, CB_PLUGIN_SUBSYSTEM,
+ "Can't bind to server <%s> port <%d>. (%s)\n",
+ hostname, port, ldap_err2string(parse_rc));
+ }
+ if ( errmsg ) {
+ *errmsg = slapi_ch_malloc(CB_BUFSIZE);
+ sprintf(*errmsg,error2,ldap_err2string(parse_rc));
+ }
+ rc = parse_rc;
+ goto unlock_and_return;
+ }
+
+ if ( rc != LDAP_SUCCESS ) {
+ if (cb_debug_on()) {
+ slapi_log_error( SLAPI_LOG_PLUGIN, CB_PLUGIN_SUBSYSTEM,
+ "Can't bind to server <%s> port <%d>. (%s)\n",
+ hostname, port, ldap_err2string(rc));
+ }
+ if ( errmsg ) {
+ *errmsg = slapi_ch_malloc(CB_BUFSIZE);
+ sprintf(*errmsg,error2, ldap_err2string(rc));
+ }
+ goto unlock_and_return;
+ }
+
+ if ( serverctrls )
+ {
+ int i;
+ for( i = 0; serverctrls[ i ] != NULL; ++i )
+ {
+ if ( !(strcmp( serverctrls[ i ]->ldctl_oid, LDAP_CONTROL_PWEXPIRED)) )
+ {
+ /* Bind is successful but password has expired */
+ slapi_log_error(SLAPI_LOG_FATAL, CB_PLUGIN_SUBSYSTEM,
+ "Succesfully bound as %s to remote server %s:%d, "
+ "but password has expired.\n",
+ binddn, hostname, port);
+ }
+ else if ( !(strcmp( serverctrls[ i ]->ldctl_oid, LDAP_CONTROL_PWEXPIRING)) )
+ {
+ /* The password is expiring in n seconds */
+ if ( (serverctrls[ i ]->ldctl_value.bv_val != NULL) &&
+ (serverctrls[ i ]->ldctl_value.bv_len > 0) )
+ {
+ int password_expiring = atoi( serverctrls[ i ]->ldctl_value.bv_val );
+ slapi_log_error(SLAPI_LOG_FATAL, CB_PLUGIN_SUBSYSTEM,
+ "Succesfully bound as %s to remote server %s:%d, "
+ "but password is expiring in %d seconds.\n",
+ binddn, hostname, port, password_expiring);
+ }
+ }
+ }
+ ldap_controls_free(serverctrls);
+ }
+
+ if (referrals)
+ charray_free(referrals);
+ }
+ }
+
+ conn = (cb_outgoing_conn *) slapi_ch_malloc(sizeof(cb_outgoing_conn));
+ conn->ld=ld;
+ conn->status=CB_CONNSTATUS_OK;
+ conn->refcount=0; /* incremented below */
+ conn->opentime=current_time();
+ conn->ThreadId=PR_MyThreadId(); /* store the thread id */
+ conn->next=NULL;
+ if (secure) {
+ if (pool->connarray[PR_ThreadSelf()] == NULL) {
+ pool->connarray[PR_ThreadSelf()] = conn;
+ }
+ else {
+ conn->next = pool->connarray[PR_ThreadSelf()];
+ pool->connarray[PR_ThreadSelf()] = conn ;
+ }
+ }
+ else {
+ if ( NULL == connprev ) {
+ pool->conn.conn_list = conn;
+ } else {
+ connprev->next=conn;
+ }
+ }
+
+ ++pool->conn.conn_list_count;
+
+ if (cb_debug_on()) {
+ slapi_log_error( SLAPI_LOG_PLUGIN, CB_PLUGIN_SUBSYSTEM,
+ "<= cb_get_connection added new conn 0x%x, "
+ "conn count now %d\n", conn->ld, pool->conn.conn_list_count );
+ }
+ goto unlock_and_return; /* got a new one */
+ }
+
+ if (cb_debug_on()) {
+ slapi_log_error( SLAPI_LOG_PLUGIN, CB_PLUGIN_SUBSYSTEM,
+ "... cb_get_connection waiting for conn to free up\n" );
+ }
+
+ if (!secure) slapi_wait_condvar( pool->conn.conn_list_cv, NULL );
+
+ if (cb_debug_on()) {
+ slapi_log_error( SLAPI_LOG_PLUGIN, CB_PLUGIN_SUBSYSTEM,
+ "... cb_get_connection awake again\n" );
+ }
+ }
+
+unlock_and_return:
+ if ( conn != NULL ) {
+ ++conn->refcount;
+ *lld=conn->ld;
+ *cc=conn;
+ if (cb_debug_on()) {
+ slapi_log_error( SLAPI_LOG_PLUGIN, CB_PLUGIN_SUBSYSTEM,
+ "<== cb_get_connection ld=0x%x (concurrency now %d)\n",*lld, conn->refcount );
+ }
+
+ } else {
+ if ( NULL != ld ) {
+ slapi_ldap_unbind( ld );
+ }
+
+ if (cb_debug_on()) {
+ slapi_log_error( SLAPI_LOG_PLUGIN, CB_PLUGIN_SUBSYSTEM,
+ "<== cb_get_connection error %d\n", rc );
+ }
+ }
+
+ slapi_unlock_mutex(pool->conn.conn_list_mutex);
+ return( rc );
+}
+
+/*
+ * We are done with the connection handle because the
+ * LDAP operation has completed.
+ */
+
+void cb_release_op_connection(cb_conn_pool* pool, LDAP *lld, int dispose) {
+
+ cb_outgoing_conn *conn;
+ cb_outgoing_conn *connprev = NULL;
+ int secure = pool->secure;
+ int myself = 0;
+
+ slapi_lock_mutex(pool->conn.conn_list_mutex);
+ /*
+ * find the connection structure this ld is part of
+ */
+
+ if (secure) {
+ myself = PR_ThreadSelf();
+ for (conn = pool->connarray[myself]; conn != NULL; conn = conn->next ) {
+ if ( lld == conn->ld )
+ break;
+ connprev = conn;
+ }
+ }
+ else {
+ for ( conn = pool->conn.conn_list; conn != NULL; conn = conn->next ){
+ if ( lld == conn->ld )
+ break;
+ connprev = conn;
+ }
+ }
+
+ if ( conn == NULL ) { /* ld not found -- unexpected */
+ slapi_log_error( SLAPI_LOG_PLUGIN, CB_PLUGIN_SUBSYSTEM,
+ "==> cb_release_op_connection ld=0x%x not found\n", lld );
+ } else {
+
+ --conn->refcount;
+
+ if (cb_debug_on()) {
+ slapi_log_error( SLAPI_LOG_PLUGIN, CB_PLUGIN_SUBSYSTEM,
+ "release conn 0x%x status %d refcount after release %d\n", conn,
+ conn->status, conn->refcount );
+ }
+
+ if ( dispose ) {
+ conn->status = CB_CONNSTATUS_DOWN;
+ }
+
+ if ( conn->status != CB_CONNSTATUS_OK && conn->refcount == 0 ) {
+
+ /*
+ * remove from server's connection list
+ */
+
+ if (!secure) {
+ if ( connprev == NULL ) {
+ pool->conn.conn_list = conn->next;
+ } else {
+ connprev->next = conn->next;
+ }
+ }
+ else {
+ if ( connprev == NULL ) {
+ pool->connarray[myself] = conn->next;
+ } else {
+ connprev->next = conn->next;
+ }
+ }
+
+ --pool->conn.conn_list_count;
+
+ /*
+ * close connection and free memory
+ */
+ cb_close_and_dispose_connection( conn );
+ }
+ }
+
+ /*
+ * wake up a thread that is waiting for a connection
+ */
+
+ if (!secure) slapi_notify_condvar( pool->conn.conn_list_cv, 0 );
+
+ slapi_unlock_mutex( pool->conn.conn_list_mutex );
+}
+
+
+static void
+cb_close_and_dispose_connection( cb_outgoing_conn *conn )
+{
+ slapi_ldap_unbind( conn->ld );
+ conn->ld = NULL;
+ slapi_ch_free( (void **)&conn );
+}
+
+static void cb_check_for_stale_connections(cb_conn_pool * pool) {
+
+ cb_outgoing_conn * connprev, *conn, *conn_next;
+ time_t curtime;
+ int connlifetime;
+ int myself;
+
+ PR_RWLock_Rlock(pool->rwl_config_lock);
+ connlifetime=pool->conn.connlifetime;
+ PR_RWLock_Unlock(pool->rwl_config_lock);
+
+ connprev = NULL;
+ conn_next = NULL;
+
+ slapi_lock_mutex(pool->conn.conn_list_mutex);
+
+ if (connlifetime > 0)
+ curtime=current_time();
+
+ if (pool->secure) {
+ myself = PR_ThreadSelf();
+ for (conn = pool->connarray[myself]; conn != NULL; conn = conn_next){
+ if ((conn->status == CB_CONNSTATUS_STALE) ||
+ (( connlifetime > 0) && (curtime - conn->opentime > connlifetime))) {
+ if ( conn->refcount == 0 ) {
+ if (cb_debug_on()) {
+ slapi_log_error( SLAPI_LOG_PLUGIN, CB_PLUGIN_SUBSYSTEM,
+ "cb_check_for_stale_connections: conn 0x%x idle and stale\n",conn);
+ }
+ --pool->conn.conn_list_count;
+ if (connprev == NULL) {
+ pool->connarray[myself] = conn->next ;
+ }
+ else {
+ connprev->next = conn->next ;
+ }
+ conn_next = conn->next ;
+ cb_close_and_dispose_connection( conn );
+ continue;
+ }
+ /* Connection is stale but in use */
+ /* Mark to be disposed later but let it in the backend list */
+ /* so that it is counted as a valid connection */
+ else {
+ conn->status = CB_CONNSTATUS_STALE;
+ }
+ if (cb_debug_on()) {
+ slapi_log_error( SLAPI_LOG_PLUGIN, CB_PLUGIN_SUBSYSTEM,
+ "cb_check_for_stale_connections: conn 0x%x stale\n",conn);
+ }
+ }
+ connprev = conn ;
+ conn_next = conn->next;
+ }
+ slapi_unlock_mutex(pool->conn.conn_list_mutex);
+ return;
+ }
+
+ for ( conn = pool->conn.conn_list; conn != NULL; conn=conn_next ) {
+ if ((conn->status == CB_CONNSTATUS_STALE) ||
+ (( connlifetime > 0) && (curtime - conn->opentime > connlifetime))) {
+ if ( conn->refcount == 0 ) {
+
+ /* Connection idle & stale. Remove and free. */
+
+ if ( NULL == connprev )
+ pool->conn.conn_list = conn->next;
+ else
+ connprev->next=conn->next;
+
+ if (cb_debug_on()) {
+ slapi_log_error( SLAPI_LOG_PLUGIN, CB_PLUGIN_SUBSYSTEM,
+ "cb_check_for_stale_connections: conn 0x%x idle and stale\n",conn);
+ }
+ --pool->conn.conn_list_count;
+ conn_next=conn->next;
+ cb_close_and_dispose_connection( conn );
+ continue;
+ }
+
+ /* Connection is stale but in use */
+ /* Mark to be disposed later but let it in the backend list */
+ /* so that it is counted as a valid connection */
+ else {
+ conn->status = CB_CONNSTATUS_STALE;
+ }
+ if (cb_debug_on()) {
+ slapi_log_error( SLAPI_LOG_PLUGIN, CB_PLUGIN_SUBSYSTEM,
+ "cb_check_for_stale_connections: conn 0x%x stale\n",conn);
+ }
+ }
+ connprev = conn;
+ conn_next=conn->next;
+ }
+
+ /* Generate an event to wake up threads waiting */
+ /* for a conn to be released. Useful to detect */
+ /* exceeded time limit. May be expensive */
+
+ slapi_notify_condvar( pool->conn.conn_list_cv, 0 );
+
+ slapi_unlock_mutex(pool->conn.conn_list_mutex);
+}
+
+/*
+ * close all open connections in preparation for server shutdown, etc.
+ * WARNING: Don't wait for current operations to complete
+ */
+
+void
+cb_close_all_connections( Slapi_Backend * be )
+{
+
+ cb_outgoing_conn *conn, *next_conn;
+ cb_backend_instance * cb= cb_get_instance(be);
+ int i;
+
+ slapi_lock_mutex(cb->pool->conn.conn_list_mutex);
+ if (cb->pool->secure) {
+ for (i=0; i< MAX_CONN_ARRAY; i++) {
+ for (conn = cb->pool->connarray[i]; conn != NULL; conn = next_conn ){
+ next_conn = conn->next;
+ cb_close_and_dispose_connection(conn);
+ }
+ }
+ } else {
+ for ( conn = cb->pool->conn.conn_list; conn != NULL; conn = next_conn ) {
+ next_conn=conn->next;
+ cb_close_and_dispose_connection(conn);
+ }
+ }
+ slapi_unlock_mutex(cb->pool->conn.conn_list_mutex);
+
+ slapi_lock_mutex(cb->bind_pool->conn.conn_list_mutex);
+ if (cb->bind_pool->secure) {
+ for (i=0; i< MAX_CONN_ARRAY; i++) {
+ for (conn = cb->bind_pool->connarray[i]; conn != NULL; conn = next_conn ){
+ next_conn=conn->next;
+ cb_close_and_dispose_connection(conn);
+ }
+ }
+ } else {
+ for ( conn = cb->bind_pool->conn.conn_list; conn != NULL; conn = next_conn ) {
+ next_conn=conn->next;
+ cb_close_and_dispose_connection(conn);
+ }
+ }
+ slapi_unlock_mutex(cb->bind_pool->conn.conn_list_mutex);
+}
+
+/* Mark used connections as stale and close unsued connections */
+/* Called when the target farm url has changed */
+
+void cb_stale_all_connections( cb_backend_instance * cb)
+{
+ cb_outgoing_conn *conn, *next_conn, *prev_conn;
+ int notify=0;
+ int i, j;
+ cb_conn_pool *pools[3];
+
+ pools[0]=cb->pool;
+ pools[1]=cb->bind_pool;
+ pools[2]=NULL;
+
+ for (i=0; pools[i]; i++) {
+ slapi_lock_mutex(pools[i]->conn.conn_list_mutex);
+ for (j=0; j< MAX_CONN_ARRAY; j++) {
+ prev_conn=NULL;
+ for (conn = pools[i]->connarray[j]; conn != NULL; conn=next_conn) {
+ next_conn=conn->next;
+ if (conn->refcount > 0) {
+ /*
+ ** Connection is stale but in use
+ ** Mark to be disposed later but let it in the backend list
+ ** so that it is counted as a valid connection
+ */
+ conn->status = CB_CONNSTATUS_STALE;
+ prev_conn=conn;
+ } else {
+ if (prev_conn == NULL) {
+ pools[i]->connarray[j]=next_conn;
+ } else {
+ prev_conn->next=next_conn;
+ }
+ cb_close_and_dispose_connection(conn);
+ pools[i]->conn.conn_list_count--;
+ }
+ }
+ }
+ prev_conn = NULL ;
+ for ( conn = pools[i]->conn.conn_list; conn != NULL; conn = next_conn ) {
+ next_conn=conn->next;
+ if (conn->refcount > 0) {
+ /*
+ ** Connection is stale but in use
+ ** Mark to be disposed later but let it in the backend list
+ ** so that it is counted as a valid connection
+ */
+ conn->status = CB_CONNSTATUS_STALE;
+ prev_conn=conn;
+ }
+ else {
+ if (conn==pools[i]->conn.conn_list) {
+ pools[i]->conn.conn_list=next_conn;
+ } else {
+ prev_conn->next=next_conn;
+ }
+ cb_close_and_dispose_connection(conn);
+ pools[i]->conn.conn_list_count--;
+ notify=1;
+ }
+ }
+ if (notify && (! pools[i]->secure)) {
+ slapi_notify_condvar( pools[i]->conn.conn_list_cv, 0 );
+ }
+ slapi_unlock_mutex(pools[i]->conn.conn_list_mutex);
+ }
+}
+
+
+
+/**************************************************************************/
+/* Need to use our own connect function until we've switched to C-SDK 4.1 */
+/* to have a timeout in the connect system call. */
+/**************************************************************************/
+
+static int global_connect_to;
+
+#if 0
+
+/* Taken from C-SDK 4.1 */
+#include <fcntl.h>
+#include <errno.h>
+#define LDAP_X_IO_TIMEOUT_NO_TIMEOUT (-1)
+
+static int
+nsldapi_os_connect_with_to(LBER_SOCKET sockfd, struct sockaddr *saptr,
+ int salen)
+{
+#ifndef _WIN32
+ int flags;
+#endif /* _WIN32 */
+ int n, error;
+ int len;
+ fd_set rset, wset;
+ struct timeval tval;
+#ifdef _WIN32
+ int nonblock = 1;
+ int block = 0;
+ fd_set eset;
+#endif /* _WIN32 */
+
+ int msec=global_connect_to; /* global */
+
+#ifdef _WIN32
+ ioctlsocket(sockfd, FIONBIO, &nonblock);
+#else
+ flags = fcntl(sockfd, F_GETFL, 0);
+ fcntl(sockfd, F_SETFL, flags | O_NONBLOCK);
+#endif /* _WIN32 */
+
+ error = 0;
+ if ((n = connect(sockfd, saptr, salen)) < 0)
+#ifdef _WIN32
+ if ((n != SOCKET_ERROR) && (WSAGetLastError() != WSAEWOULDBLOCK)) {
+#else
+ if (errno != EINPROGRESS) {
+#endif /* _WIN32 */
+ return (-1);
+ }
+
+ /* success */
+ if (n == 0)
+ goto done;
+
+ FD_ZERO(&rset);
+ FD_SET(sockfd, &rset);
+ wset = rset;
+
+#ifdef _WIN32
+ eset = rset;
+#endif /* _WIN32 */
+
+ if (msec < 0 && msec != LDAP_X_IO_TIMEOUT_NO_TIMEOUT) {
+ msec = LDAP_X_IO_TIMEOUT_NO_TIMEOUT;
+ } else {
+ if (msec != 0)
+ tval.tv_sec = msec / 1000;
+ else
+ tval.tv_sec = 0;
+ tval.tv_usec = 0;
+ }
+
+ /* if timeval structure == NULL, select will block indefinitely */
+ /* != NULL, and value == 0, select will */
+ /* not block */
+ /* Windows is a bit quirky on how it behaves w.r.t nonblocking */
+ /* connects. If the connect fails, the exception fd, eset, is */
+ /* set to show the failure. The first argument in select is */
+ /* ignored */
+
+#ifdef _WIN32
+ if ((n = select(sockfd +1, &rset, &wset, &eset,
+ (msec != LDAP_X_IO_TIMEOUT_NO_TIMEOUT) ? &tval : NULL)) == 0) {
+ errno = WSAETIMEDOUT;
+ return (-1);
+ }
+ /* if wset is set, the connect worked */
+ if (FD_ISSET(sockfd, &wset) || FD_ISSET(sockfd, &rset)) {
+ len = sizeof(error);
+ if (getsockopt(sockfd, SOL_SOCKET, SO_ERROR, (char *)&error, &len)
+ < 0)
+ return (-1);
+ goto done;
+ }
+
+ /* if eset is set, the connect failed */
+ if (FD_ISSET(sockfd, &eset)) {
+ return (-1);
+ }
+
+ /* failure on select call */
+ if (n == SOCKET_ERROR) {
+ return (-1);
+ }
+#else
+ if ((n = select(sockfd +1, &rset, &wset, NULL,
+ (msec != LDAP_X_IO_TIMEOUT_NO_TIMEOUT) ? &tval : NULL)) == 0) {
+ errno = ETIMEDOUT;
+ return (-1);
+ }
+ if (FD_ISSET(sockfd, &rset) || FD_ISSET(sockfd, &wset)) {
+ len = sizeof(error);
+ if (getsockopt(sockfd, SOL_SOCKET, SO_ERROR, (char *)&error, &len)
+ < 0)
+ return (-1);
+ }
+#endif /* _WIN32 */
+done:
+#ifdef _WIN32
+ ioctlsocket(sockfd, FIONBIO, &block);
+#else
+ fcntl(sockfd, F_SETFL, flags);
+#endif /* _WIN32 */
+
+ if (error) {
+ errno = error;
+ return (-1);
+ }
+
+ return (0);
+}
+
+#endif
+
+/* Try to figure out if a farm server is still alive */
+
+int cb_ping_farm(cb_backend_instance *cb, cb_outgoing_conn * cnx,time_t end_time) {
+
+ char *attrs[] ={"1.1",NULL};
+ int rc;
+ struct timeval timeout;
+ LDAP *ld;
+ LDAPMessage *result;
+#if 0
+ struct ldap_io_fns iof;
+#endif
+ time_t now;
+ if (cb->max_idle_time <=0) /* Heart-beat disabled */
+ return LDAP_SUCCESS;
+
+ if (cnx && (cnx->status != CB_CONNSTATUS_OK )) /* Known problem */
+ return LDAP_SERVER_DOWN;
+
+ now = current_time();
+ if (end_time && ((now <= end_time) || (end_time <0))) return LDAP_SUCCESS;
+
+ ld=slapi_ldap_init(cb->pool->hostname,cb->pool->port,cb->pool->secure,0);
+ if (NULL == ld) {
+ cb_update_failed_conn_cpt( cb );
+ return LDAP_SERVER_DOWN;
+ }
+
+#if 0
+ memset(&iof,0,sizeof(struct ldap_io_fns));
+ if (LDAP_SUCCESS !=ldap_get_option(ld,LDAP_OPT_IO_FN_PTRS,&iof)) {
+ slapi_ldap_unbind( ld );
+ cb_update_failed_conn_cpt( cb );
+ return LDAP_SERVER_DOWN;
+ }
+
+ iof.liof_connect = nsldapi_os_connect_with_to;
+ if (LDAP_SUCCESS !=ldap_set_option(ld,LDAP_OPT_IO_FN_PTRS,&iof)) {
+ slapi_ldap_unbind( ld );
+ cb_update_failed_conn_cpt( cb );
+ return LDAP_SERVER_DOWN;
+ }
+
+#endif
+
+ timeout.tv_sec=cb->max_test_time;
+ timeout.tv_usec=0;
+
+ global_connect_to=cb->max_test_time * 1000; /* Reuse the same for the connect */
+ rc=ldap_search_ext_s(ld ,NULL,LDAP_SCOPE_BASE,"objectclass=*",attrs,1,NULL,
+ NULL, &timeout, 1,&result);
+ if ( LDAP_SUCCESS != rc ) {
+ slapi_ldap_unbind( ld );
+ cb_update_failed_conn_cpt( cb );
+ return LDAP_SERVER_DOWN;
+ }
+
+ ldap_msgfree(result);
+ slapi_ldap_unbind( ld );
+ cb_reset_conn_cpt( cb );
+ return LDAP_SUCCESS;
+}
+
+
+
+void cb_update_failed_conn_cpt ( cb_backend_instance *cb ) {
+ /* if the chaining BE is already unavailable, we do nothing*/
+ time_t now;
+ if (cb->monitor_availability.farmserver_state == FARMSERVER_AVAILABLE) {
+ slapi_lock_mutex(cb->monitor_availability.cpt_lock);
+ cb->monitor_availability.cpt ++;
+ slapi_unlock_mutex(cb->monitor_availability.cpt_lock);
+ if (cb->monitor_availability.cpt >= CB_NUM_CONN_BEFORE_UNAVAILABILITY ) {
+ /* we reach the limit of authorized failed connections => we setup the chaining BE state to unavailable */
+ now = current_time();
+ slapi_lock_mutex(cb->monitor_availability.lock_timeLimit);
+ cb->monitor_availability.unavailableTimeLimit = now + CB_UNAVAILABLE_PERIOD ;
+ slapi_unlock_mutex(cb->monitor_availability.lock_timeLimit);
+ cb->monitor_availability.farmserver_state = FARMSERVER_UNAVAILABLE ;
+ slapi_log_error( SLAPI_LOG_PLUGIN, CB_PLUGIN_SUBSYSTEM,
+ "cb_update_failed_conn_cpt: Farm server unavailable");
+ }
+
+ }
+}
+
+void cb_reset_conn_cpt( cb_backend_instance *cb ) {
+ if (cb->monitor_availability.cpt > 0) {
+ slapi_lock_mutex(cb->monitor_availability.cpt_lock);
+ cb->monitor_availability.cpt = 0 ;
+ if (cb->monitor_availability.farmserver_state == FARMSERVER_UNAVAILABLE) {
+ cb->monitor_availability.farmserver_state = FARMSERVER_AVAILABLE ;
+ slapi_log_error( SLAPI_LOG_PLUGIN, CB_PLUGIN_SUBSYSTEM,
+ "cb_reset_conn_cpt: Farm server is back");
+ }
+ slapi_unlock_mutex(cb->monitor_availability.cpt_lock);
+ }
+}
+
+int cb_check_availability( cb_backend_instance *cb, Slapi_PBlock *pb ) {
+ /* check wether the farmserver is available or not */
+ int rc ;
+ time_t now ;
+ if ( cb->monitor_availability.farmserver_state == FARMSERVER_UNAVAILABLE ){
+ slapi_lock_mutex(cb->monitor_availability.lock_timeLimit);
+ now = current_time();
+ if (now >= cb->monitor_availability.unavailableTimeLimit) {
+ cb->monitor_availability.unavailableTimeLimit = now + CB_INFINITE_TIME ; /* to be sure only one thread can do the test */
+ slapi_unlock_mutex(cb->monitor_availability.lock_timeLimit);
+ }
+ else {
+ slapi_unlock_mutex(cb->monitor_availability.lock_timeLimit);
+ cb_send_ldap_result( pb, LDAP_OPERATIONS_ERROR, NULL, "FARM SERVER TEMPORARY UNAVAILABLE", 0, NULL) ;
+ return FARMSERVER_UNAVAILABLE ;
+ }
+ slapi_log_error( SLAPI_LOG_PLUGIN, CB_PLUGIN_SUBSYSTEM,
+ "cb_check_availability: ping the farm server and check if it's still unavailable");
+ if ((rc = cb_ping_farm(cb, NULL, 0)) != LDAP_SUCCESS) { /* farm still unavailable... Just change the timelimit */
+ slapi_lock_mutex(cb->monitor_availability.lock_timeLimit);
+ now = current_time();
+ cb->monitor_availability.unavailableTimeLimit = now + CB_UNAVAILABLE_PERIOD ;
+ slapi_unlock_mutex(cb->monitor_availability.lock_timeLimit);
+ cb_send_ldap_result( pb, LDAP_OPERATIONS_ERROR, NULL, "FARM SERVER TEMPORARY UNAVAILABLE", 0, NULL) ;
+ slapi_log_error( SLAPI_LOG_PLUGIN, CB_PLUGIN_SUBSYSTEM,
+ "cb_check_availability: Farm server still unavailable");
+ return FARMSERVER_UNAVAILABLE ;
+ }
+ else {
+ /* farm is back !*/
+ slapi_lock_mutex(cb->monitor_availability.lock_timeLimit);
+ now = current_time();
+ cb->monitor_availability.unavailableTimeLimit = now ; /* the unavailable period is finished */
+ slapi_unlock_mutex(cb->monitor_availability.lock_timeLimit);
+ /* The farmer server state backs to FARMSERVER_AVAILABLE, but this already done in cb_ping_farm, and also the reset of cpt*/
+ return FARMSERVER_AVAILABLE ;
+ }
+ }
+ return FARMSERVER_AVAILABLE ;
+}
diff --git a/ldap/servers/plugins/chainingdb/cb_controls.c b/ldap/servers/plugins/chainingdb/cb_controls.c
new file mode 100644
index 00000000..ab612099
--- /dev/null
+++ b/ldap/servers/plugins/chainingdb/cb_controls.c
@@ -0,0 +1,289 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+#include "cb.h"
+
+/*
+** Controls that can't be forwarded due to the current implementation
+*/
+
+static char * unsupported_ctrls[] = {LDAP_CONTROL_PERSISTENTSEARCH,NULL};
+
+int cb_is_control_forwardable(cb_backend * cb, char *controloid) {
+ return (!(charray_inlist(unsupported_ctrls,controloid)));
+}
+
+void
+cb_register_supported_control( cb_backend * cb, char *controloid, unsigned long controlops )
+{
+ /* For now, ignore controlops */
+ if ( controloid != NULL ) {
+ PR_RWLock_Wlock(cb->config.rwl_config_lock);
+ charray_add( &cb->config.forward_ctrls,slapi_ch_strdup( controloid ));
+ PR_RWLock_Unlock(cb->config.rwl_config_lock);
+ }
+}
+
+
+void
+cb_unregister_all_supported_control( cb_backend * cb ) {
+
+ PR_RWLock_Wlock(cb->config.rwl_config_lock);
+ charray_free(cb->config.forward_ctrls);
+ cb->config.forward_ctrls=NULL;
+ PR_RWLock_Unlock(cb->config.rwl_config_lock);
+}
+
+void
+cb_unregister_supported_control( cb_backend * cb, char *controloid, unsigned long controlops )
+{
+
+ /* For now, ignore controlops */
+ if ( controloid != NULL ) {
+ int i;
+ PR_RWLock_Wlock(cb->config.rwl_config_lock);
+ for ( i = 0; cb->config.forward_ctrls != NULL && cb->config.forward_ctrls[i] != NULL; ++i ) {
+ if ( strcmp( cb->config.forward_ctrls[i], controloid ) == 0 ) {
+ break;
+ }
+ }
+ if ( cb->config.forward_ctrls == NULL || cb->config.forward_ctrls[i] == NULL) {
+ PR_RWLock_Unlock(cb->config.rwl_config_lock);
+ return;
+ }
+ if ( controlops == 0 ) {
+ charray_remove(cb->config.forward_ctrls,controloid);
+ }
+ PR_RWLock_Unlock(cb->config.rwl_config_lock);
+ }
+}
+
+int cb_create_loop_control (
+ const int hops,
+ LDAPControl **ctrlp)
+
+{
+ BerElement *ber;
+ int rc;
+
+ if ((ber = ber_alloc()) == NULL)
+ return -1;
+
+ if ( ber_printf( ber, "i", hops ) < 0) {
+ ber_free(ber,1);
+ return -1;
+ }
+
+ rc = slapi_build_control( CB_LDAP_CONTROL_CHAIN_SERVER, ber, 0, ctrlp);
+
+ ber_free(ber,1);
+
+ return rc;
+}
+
+/*
+** Return the controls to be passed to the remote
+** farm server and the LDAP error to return.
+**
+** Add the Proxied Authorization control when impersonation
+** is enabled. Other controls present in the request are added
+** to the control list
+**
+** #622885 .abandon should not inherit the to-be-abandoned-operation's controls
+** .controls attached to abandon should not be critical
+*/
+
+int cb_update_controls( Slapi_PBlock * pb,
+ LDAP * ld,
+ LDAPControl *** controls,
+ int ctrl_flags
+ )
+{
+
+ int cCount=0;
+ int dCount=0;
+ int i;
+ char * proxyDN=NULL;
+ LDAPControl ** reqControls = NULL;
+ LDAPControl ** ctrls = NULL;
+ cb_backend_instance * cb;
+ cb_backend * cbb;
+ Slapi_Backend * be;
+ int rc=LDAP_SUCCESS;
+ int hops=0;
+ int useloop=0;
+ int addauth = (ctrl_flags & CB_UPDATE_CONTROLS_ADDAUTH);
+ int isabandon = (ctrl_flags & CB_UPDATE_CONTROLS_ISABANDON);
+ int op_type = 0;
+
+ *controls = NULL;
+ slapi_pblock_get(pb, SLAPI_OPERATION_TYPE, &op_type);
+ if (!isabandon || op_type == SLAPI_OPERATION_ABANDON) {
+ /* if not abandon or abandon sent by client */
+ slapi_pblock_get( pb, SLAPI_REQCONTROLS, &reqControls );
+ }
+ slapi_pblock_get( pb, SLAPI_BACKEND, &be );
+ slapi_pblock_get( pb, SLAPI_PLUGIN_PRIVATE, &cbb );
+ cb = cb_get_instance(be);
+
+ /*****************************************/
+ /* First, check for unsupported controls */
+ /* Return an error if critical control */
+ /* else remove it from the control list */
+ /*****************************************/
+
+ for ( cCount=0; reqControls && reqControls[cCount]; cCount++ );
+ ctrls = (LDAPControl **)slapi_ch_calloc(1,sizeof(LDAPControl *) * (cCount +3));
+
+ PR_RWLock_Rlock(cbb->config.rwl_config_lock);
+
+ for ( cCount=0; reqControls && reqControls[cCount]; cCount++ ) {
+
+ /* XXXSD CASCADING */
+ /* For now, allow PROXY_AUTH control forwarding only when */
+ /* local acl evaluation to prevent unauthorized access */
+
+ if (!strcmp(reqControls[cCount]->ldctl_oid,LDAP_CONTROL_PROXYAUTH)) {
+
+ /* we have to force remote acl checking if the associated backend to this
+ chaining backend is disabled - disabled == no acl check possible */
+ if (!cb->local_acl && !cb->associated_be_is_disabled) {
+ slapi_log_error( SLAPI_LOG_PLUGIN,CB_PLUGIN_SUBSYSTEM,
+ "local aci check required to handle proxied auth control. Deny access.\n");
+ rc= LDAP_INSUFFICIENT_ACCESS;
+ break;
+ }
+
+ /* XXXSD Not safe to use proxied authorization with Directory Manager */
+ /* checked earlier when impersonation is on */
+
+ if (!cb->impersonate) {
+ char * requestor,*rootdn;
+ char * requestorCopy=NULL;
+
+ rootdn=cb_get_rootdn();
+ slapi_pblock_get( pb, SLAPI_REQUESTOR_DN, &requestor );
+ requestorCopy=slapi_ch_strdup(requestor);
+ slapi_dn_normalize_case(requestorCopy);
+
+ if (!strcmp( requestorCopy, rootdn )) { /* UTF8- aware */
+ slapi_log_error( SLAPI_LOG_PLUGIN,CB_PLUGIN_SUBSYSTEM,
+ "Use of user <%s> incompatible with proxied auth. control\n",rootdn);
+ rc=LDAP_UNAVAILABLE_CRITICAL_EXTENSION;
+ slapi_ch_free((void **)&requestorCopy);
+ break;
+ }
+ slapi_ch_free((void **)&rootdn);
+ slapi_ch_free((void **)&requestorCopy);
+ }
+
+ addauth=0;
+ ctrls[dCount]=slapi_dup_control(reqControls[cCount]);
+ dCount++;
+
+ } else
+ if (!strcmp(reqControls[cCount]->ldctl_oid,CB_LDAP_CONTROL_CHAIN_SERVER)) {
+
+ /* Max hop count reached ? */
+ /* Checked realier by a call to cb_forward_operation() */
+
+ BerElement *ber = NULL;
+
+ ber = ber_init(&(reqControls[cCount]->ldctl_value));
+ ber_scanf(ber,"i",&hops);
+ ber_free(ber,1);
+ useloop=1;
+
+ /* Add to the control list later */
+
+ } else {
+
+ int i;
+ for ( i = 0; cbb->config.forward_ctrls != NULL
+ && cbb->config.forward_ctrls[i] != NULL; ++i ) {
+ if ( strcmp( cbb->config.forward_ctrls[i], reqControls[cCount]->ldctl_oid ) == 0 ) {
+ break;
+ }
+ }
+ /* For now, ignore optype */
+ if ( cbb->config.forward_ctrls == NULL || cbb->config.forward_ctrls[i] == NULL) {
+ if (reqControls[cCount]->ldctl_iscritical) {
+ rc = LDAP_UNAVAILABLE_CRITICAL_EXTENSION;
+ break;
+ }
+ /* Skip it */
+ } else {
+ ctrls[dCount]=slapi_dup_control(reqControls[cCount]);
+ dCount++;
+ }
+ }
+ }
+
+ PR_RWLock_Unlock(cbb->config.rwl_config_lock);
+
+ if (LDAP_SUCCESS != rc) {
+ ldap_controls_free(ctrls);
+ return rc;
+ }
+
+ /***************************************/
+ /* add impersonation control if needed */
+ /***************************************/
+
+ if ( !(cb->impersonate) ) {
+
+ /* don't add proxy control */
+ addauth=0;
+ }
+
+ if (addauth) {
+ slapi_pblock_get( pb, SLAPI_REQUESTOR_DN, &proxyDN );
+
+ if ( ldap_create_proxyauth_control(ld, proxyDN, isabandon?0:1, &ctrls[dCount] )) {
+ ldap_controls_free(ctrls);
+ slapi_log_error( SLAPI_LOG_PLUGIN, CB_PLUGIN_SUBSYSTEM,
+ "LDAP_CONTROL_PROXYAUTH control encoding failed.\n");
+ return LDAP_OPERATIONS_ERROR;
+ }
+ dCount++;
+ }
+
+ /***********************************************************/
+ /* add loop control if needed */
+ /* Don't add it if not in the list of forwardable controls */
+ /***********************************************************/
+
+ if (!useloop) {
+ for ( i = 0; cbb->config.forward_ctrls != NULL
+ && cbb->config.forward_ctrls[i] != NULL; ++i ) {
+ if ( strcmp( cbb->config.forward_ctrls[i],
+ CB_LDAP_CONTROL_CHAIN_SERVER) == 0 ) {
+ break;
+ }
+ }
+ }
+ if ( useloop || (cbb->config.forward_ctrls !=NULL && cbb->config.forward_ctrls[i] !=NULL)){
+
+ if (hops > 0) {
+ hops--;
+ } else {
+ hops = cb->hoplimit;
+ }
+
+ /* loop control's critical flag is 0;
+ * no special treatment is needed for abandon */
+ cb_create_loop_control(hops,&ctrls[dCount]);
+ dCount++;
+ }
+
+ if (dCount==0) {
+ ldap_controls_free(ctrls);
+ } else {
+ *controls = ctrls;
+ }
+
+ return LDAP_SUCCESS;
+
+}
diff --git a/ldap/servers/plugins/chainingdb/cb_debug.c b/ldap/servers/plugins/chainingdb/cb_debug.c
new file mode 100644
index 00000000..8a33e440
--- /dev/null
+++ b/ldap/servers/plugins/chainingdb/cb_debug.c
@@ -0,0 +1,23 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+/*
+ * cb_debug.c - debugging-related code for Chaining backend
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <sys/types.h>
+#include "cb.h"
+
+#ifdef _WIN32
+int *module_ldap_debug = 0;
+
+void plugin_init_debug_level(int *level_ptr)
+{
+ module_ldap_debug = level_ptr;
+}
+#endif
+
diff --git a/ldap/servers/plugins/chainingdb/cb_delete.c b/ldap/servers/plugins/chainingdb/cb_delete.c
new file mode 100644
index 00000000..f7eb77f6
--- /dev/null
+++ b/ldap/servers/plugins/chainingdb/cb_delete.c
@@ -0,0 +1,203 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+#include "cb.h"
+
+/*
+ * Perform an delete operation
+ *
+ * Returns:
+ * 0 - success
+ * <0 - fail
+ *
+ */
+
+int
+chaining_back_delete ( Slapi_PBlock *pb )
+{
+
+ Slapi_Backend * be;
+ cb_backend_instance *cb;
+ LDAPControl **ctrls, **serverctrls;
+ int rc,parse_rc,msgid,i;
+ LDAP *ld=NULL;
+ char **referrals=NULL;
+ LDAPMessage * res;
+ char *dn,* matched_msg, *error_msg;
+ char *cnxerrbuf=NULL;
+ time_t endtime;
+ cb_outgoing_conn *cnx;
+
+ if ( LDAP_SUCCESS != (rc=cb_forward_operation(pb) )) {
+ cb_send_ldap_result( pb, rc, NULL, "Chaining forbidden", 0, NULL );
+ return -1;
+ }
+
+ slapi_pblock_get( pb, SLAPI_BACKEND, &be );
+ cb = cb_get_instance(be);
+
+ cb_update_monitor_info(pb,cb,SLAPI_OPERATION_DELETE);
+
+ /* Check wether the chaining BE is available or not */
+ if ( cb_check_availability( cb, pb ) == FARMSERVER_UNAVAILABLE ){
+ return -1;
+ }
+
+ slapi_pblock_get( pb, SLAPI_DELETE_TARGET, &dn );
+
+ /*
+ * Check local acls
+ */
+
+ if (cb->local_acl && !cb->associated_be_is_disabled) {
+ char * errbuf=NULL;
+ Slapi_Entry *te = slapi_entry_alloc();
+ slapi_entry_set_dn(te,slapi_ch_strdup(dn));
+ rc = cb_access_allowed (pb, te, NULL, NULL, SLAPI_ACL_DELETE,&errbuf);
+ slapi_entry_free(te);
+
+ if ( rc != LDAP_SUCCESS ) {
+ cb_send_ldap_result( pb, rc, NULL, errbuf, 0, NULL );
+ slapi_ch_free((void **)&errbuf);
+ return -1;
+ }
+ }
+
+ /*
+ * Grab a connection handle
+ */
+
+ if ((rc = cb_get_connection(cb->pool,&ld,&cnx,NULL,&cnxerrbuf)) != LDAP_SUCCESS) {
+ cb_send_ldap_result( pb, LDAP_OPERATIONS_ERROR, NULL, cnxerrbuf, 0, NULL);
+ slapi_ch_free((void **)&cnxerrbuf);
+ /* ping the farm. If the farm is unreachable, we increment the counter */
+ cb_ping_farm(cb,NULL,0);
+ return -1;
+ }
+
+ /*
+ * Control management
+ */
+
+ if ( (rc = cb_update_controls( pb,ld,&ctrls,CB_UPDATE_CONTROLS_ADDAUTH )) != LDAP_SUCCESS ) {
+ cb_send_ldap_result( pb, rc, NULL,NULL, 0, NULL);
+ cb_release_op_connection(cb->pool,ld,CB_LDAP_CONN_ERROR(rc));
+ return -1;
+ }
+
+ if ( slapi_op_abandoned( pb )) {
+ cb_release_op_connection(cb->pool,ld,0);
+ if ( NULL != ctrls)
+ ldap_controls_free(ctrls);
+ return -1;
+ }
+
+ /* heart-beat management */
+ if (cb->max_idle_time>0)
+ endtime=current_time() + cb->max_idle_time;
+
+ /*
+ * Send LDAP operation to the remote host
+ */
+
+ rc = ldap_delete_ext( ld, dn, ctrls, NULL, &msgid );
+ if ( NULL != ctrls)
+ ldap_controls_free(ctrls);
+ if ( rc != LDAP_SUCCESS ) {
+
+ cb_send_ldap_result( pb, LDAP_OPERATIONS_ERROR, NULL,
+ ldap_err2string(rc), 0, NULL);
+ cb_release_op_connection(cb->pool,ld,CB_LDAP_CONN_ERROR(rc));
+ return -1;
+ }
+
+ while ( 1 ) {
+
+ if (cb_check_forward_abandon(cb,pb,ld,msgid)) {
+ return -1;
+ }
+
+ rc = ldap_result( ld, msgid, 0, &cb->abandon_timeout, &res );
+ switch ( rc ) {
+ case -1:
+ cb_send_ldap_result( pb, LDAP_OPERATIONS_ERROR, NULL,
+ ldap_err2string(rc), 0, NULL);
+ cb_release_op_connection(cb->pool,ld,CB_LDAP_CONN_ERROR(rc));
+ if (res)
+ ldap_msgfree(res);
+ return -1;
+ case 0:
+ if ((rc=cb_ping_farm(cb,cnx,endtime)) != LDAP_SUCCESS) {
+
+ /* does not respond. give up and return a*/
+ /* error to the client. */
+
+ /*cb_send_ldap_result(pb,LDAP_OPERATIONS_ERROR, NULL,
+ ldap_err2string(rc), 0, NULL);*/
+ cb_send_ldap_result(pb,LDAP_OPERATIONS_ERROR, NULL, "FARM SERVER TEMPORARY UNAVAILABLE", 0, NULL);
+ cb_release_op_connection(cb->pool,ld,CB_LDAP_CONN_ERROR(rc));
+ if (res)
+ ldap_msgfree(res);
+ return -1;
+ }
+#ifdef CB_YIELD
+ DS_Sleep(PR_INTERVAL_NO_WAIT);
+#endif
+ break;
+ default:
+ matched_msg=error_msg=NULL;
+ parse_rc = ldap_parse_result( ld, res, &rc, &matched_msg,
+ &error_msg, &referrals, &serverctrls, 1 );
+ if ( parse_rc != LDAP_SUCCESS ) {
+ cb_send_ldap_result( pb, LDAP_OPERATIONS_ERROR, NULL,
+ ldap_err2string(parse_rc), 0, NULL);
+ cb_release_op_connection(cb->pool,ld,CB_LDAP_CONN_ERROR(parse_rc));
+ slapi_ch_free((void **)&matched_msg);
+ slapi_ch_free((void **)&error_msg);
+ if (serverctrls)
+ ldap_controls_free(serverctrls);
+ /* jarnou: free referrals */
+ if (referrals)
+ charray_free(referrals);
+ return -1;
+ }
+
+ if ( rc != LDAP_SUCCESS ) {
+ struct berval ** refs = referrals2berval(referrals);
+
+ cb_send_ldap_result( pb, rc, matched_msg, error_msg, 0, refs);
+ cb_release_op_connection(cb->pool,ld,CB_LDAP_CONN_ERROR(rc));
+ slapi_ch_free((void **)&matched_msg);
+ slapi_ch_free((void **)&error_msg);
+ if (refs)
+ ber_bvecfree(refs);
+ if (referrals)
+ charray_free(referrals);
+ if (serverctrls)
+ ldap_controls_free(serverctrls);
+ return -1;
+ }
+
+ cb_release_op_connection(cb->pool,ld,0);
+
+ /* Add control response sent by the farm server */
+
+ for (i=0; serverctrls && serverctrls[i];i++)
+ slapi_pblock_set( pb, SLAPI_ADD_RESCONTROL, serverctrls[i]);
+ if (serverctrls)
+ ldap_controls_free(serverctrls);
+ /* jarnou: free matched_msg, error_msg, and referrals if necessary */
+ slapi_ch_free((void **)&matched_msg);
+ slapi_ch_free((void **)&error_msg);
+ if (referrals)
+ charray_free(referrals);
+ cb_send_ldap_result( pb, LDAP_SUCCESS, NULL, NULL, 0, NULL );
+ return 0;
+ }
+ }
+
+ /* Never reached */
+ /* return 0; */
+}
diff --git a/ldap/servers/plugins/chainingdb/cb_init.c b/ldap/servers/plugins/chainingdb/cb_init.c
new file mode 100644
index 00000000..70af3d42
--- /dev/null
+++ b/ldap/servers/plugins/chainingdb/cb_init.c
@@ -0,0 +1,120 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+#include "cb.h"
+
+Slapi_PluginDesc chainingdbdesc = { CB_PLUGIN_NAME,
+ PLUGIN_MAGIC_VENDOR_STR,
+ PRODUCTTEXT,
+ CB_PLUGIN_DESCRIPTION };
+
+
+static cb_backend * cb_backend_type=NULL;
+
+cb_backend * cb_get_backend_type() {
+ return cb_backend_type;
+}
+
+static void cb_set_backend_type(cb_backend * cb) {
+ cb_backend_type=cb;
+}
+
+/* Initialization function */
+#ifdef _WIN32
+__declspec(dllexport)
+#endif
+
+int
+chaining_back_init( Slapi_PBlock *pb )
+{
+
+ int rc=0;
+ cb_backend *cb;
+ struct slapdplugin *p;
+
+ cb = (cb_backend *) slapi_ch_calloc( 1, sizeof(cb_backend));
+
+ /* Record the identity of the chaining plugin. used during internal ops.*/
+ slapi_pblock_get(pb, SLAPI_PLUGIN_IDENTITY, &(cb->identity));
+
+ /* keep a pointer back to the plugin */
+ slapi_pblock_get(pb, SLAPI_PLUGIN, &p);
+ cb->plugin = p;
+
+ /* Initialize misc. fields */
+ cb->config.rwl_config_lock = PR_NewRWLock(PR_RWLOCK_RANK_NONE, "chaining_db");
+ rc = slapi_pblock_set( pb, SLAPI_PLUGIN_PRIVATE, (void *) cb );
+ cb->pluginDN=slapi_ch_calloc( 1,strlen(PLUGIN_BASE_DN)+strlen(CB_PLUGIN_NAME)+5);
+ sprintf(cb->pluginDN,"cn=%s,%s",CB_PLUGIN_NAME,PLUGIN_BASE_DN);
+
+ cb->configDN=slapi_ch_calloc( 1,strlen(cb->pluginDN)+11);
+ sprintf(cb->configDN,"cn=config,%s",cb->pluginDN);
+
+ /* Set backend callback functions */
+ rc |= slapi_pblock_set( pb, SLAPI_PLUGIN_VERSION, SLAPI_PLUGIN_VERSION_03 );
+ rc |= slapi_pblock_set( pb, SLAPI_PLUGIN_DESCRIPTION, (void *)&chainingdbdesc );
+ rc |= slapi_pblock_set( pb, SLAPI_PLUGIN_DB_SEARCH_FN,
+ (void *) chainingdb_build_candidate_list );
+ rc |= slapi_pblock_set( pb, SLAPI_PLUGIN_DB_NEXT_SEARCH_ENTRY_FN,
+ (void *) chainingdb_next_search_entry );
+ rc |= slapi_pblock_set( pb, SLAPI_PLUGIN_START_FN,
+ (void *) chainingdb_start ) ;
+ rc |= slapi_pblock_set( pb, SLAPI_PLUGIN_DB_BIND_FN,
+ (void *) chainingdb_bind ) ;
+ rc |= slapi_pblock_set( pb, SLAPI_PLUGIN_DB_ADD_FN,
+ (void *) chaining_back_add );
+ rc |= slapi_pblock_set( pb, SLAPI_PLUGIN_DB_DELETE_FN,
+ (void *) chaining_back_delete );
+ rc |= slapi_pblock_set( pb, SLAPI_PLUGIN_DB_COMPARE_FN,
+ (void *) chaining_back_compare );
+ rc |= slapi_pblock_set( pb, SLAPI_PLUGIN_DB_MODIFY_FN,
+ (void *) chaining_back_modify );
+ rc |= slapi_pblock_set( pb, SLAPI_PLUGIN_DB_MODRDN_FN,
+ (void *) chaining_back_modrdn );
+ rc |= slapi_pblock_set( pb, SLAPI_PLUGIN_DB_ABANDON_FN,
+ (void *) chaining_back_abandon );
+ rc |= slapi_pblock_set( pb, SLAPI_PLUGIN_DB_SIZE_FN,
+ (void *) cb_db_size );
+ rc |= slapi_pblock_set( pb, SLAPI_PLUGIN_CLOSE_FN,
+ (void *) cb_back_close );
+ rc |= slapi_pblock_set( pb, SLAPI_PLUGIN_CLEANUP_FN,
+ (void *) cb_back_cleanup );
+
+/****
+ rc |= slapi_pblock_set( pb, SLAPI_PLUGIN_DB_ENTRY_RELEASE_FN,
+ (void *) chaining_back_entry_release );
+ rc |= slapi_pblock_set( pb, SLAPI_PLUGIN_DB_INIT_INSTANCE_FN,
+ (void *) chaining_back_init);
+ rc |= slapi_pblock_set( pb, SLAPI_PLUGIN_DB_TEST_FN,
+ (void *) cb_back_test );
+****/
+
+ /*
+ ** The following callbacks are not implemented
+ ** by the chaining backend
+ ** - SLAPI_PLUGIN_DB_FLUSH_FN
+ ** - SLAPI_PLUGIN_DB_SEQ_FN
+ ** - SLAPI_PLUGIN_DB_RMDB_FN
+ ** - SLAPI_PLUGIN_DB_DB2INDEX_FN
+ ** - SLAPI_PLUGIN_DB_LDIF2DB_FN
+ ** - SLAPI_PLUGIN_DB_DB2LDIF_FN
+ ** - SLAPI_PLUGIN_DB_ARCHIVE2DB_FN
+ ** - SLAPI_PLUGIN_DB_DB2ARCHIVE_FN
+ ** - SLAPI_PLUGIN_DB_BEGIN_FN
+ ** - SLAPI_PLUGIN_DB_COMMIT_FN
+ ** - SLAPI_PLUGIN_DB_ABORT_FN
+ ** - SLAPI_PLUGIN_DB_NEXT_SEARCH_ENTRY_EXT_FN
+ */
+
+ if ( rc != 0 ) {
+ slapi_log_error( SLAPI_LOG_FATAL, CB_PLUGIN_SUBSYSTEM, "chaining_back_init failed\n");
+ return( -1 );
+ }
+
+ cb_set_backend_type(cb);
+
+ return (0);
+}
+
diff --git a/ldap/servers/plugins/chainingdb/cb_instance.c b/ldap/servers/plugins/chainingdb/cb_instance.c
new file mode 100644
index 00000000..60af726f
--- /dev/null
+++ b/ldap/servers/plugins/chainingdb/cb_instance.c
@@ -0,0 +1,1871 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+#include "cb.h"
+
+/*
+** 1 set/get function for each parameter of a backend instance
+** NOTE: Some parameters won't be taken into account until the server has restarted
+** In such cases, the internal conf is not updated but the new value is stored in the
+** dse.ldif file.
+**/
+
+/* Get functions */
+
+static void *cb_instance_hosturl_get(void *arg);
+static void *cb_instance_binduser_get(void *arg);
+static void *cb_instance_userpassword_get(void *arg);
+static void *cb_instance_maxbconn_get(void *arg);
+static void *cb_instance_maxconn_get(void *arg);
+static void *cb_instance_abandonto_get(void *arg);
+static void *cb_instance_maxbconc_get(void *arg);
+static void *cb_instance_maxconc_get(void *arg);
+static void *cb_instance_imperson_get(void *arg);
+static void *cb_instance_connlife_get(void *arg);
+static void *cb_instance_bindto_get(void *arg);
+static void *cb_instance_opto_get(void *arg);
+static void *cb_instance_ref_get(void *arg);
+static void *cb_instance_acl_get(void *arg);
+static void *cb_instance_bindretry_get(void *arg);
+static void *cb_instance_sizelimit_get(void *arg);
+static void *cb_instance_timelimit_get(void *arg);
+static void *cb_instance_hoplimit_get(void *arg);
+static void *cb_instance_max_idle_get(void *arg);
+static void *cb_instance_max_test_get(void *arg);
+
+
+/* Set functions */
+
+static int cb_instance_hosturl_set(void *arg, void *value, char *errorbuf, int phase, int apply);
+static int cb_instance_binduser_set(void *arg, void *value, char *errorbuf, int phase, int apply);
+static int cb_instance_userpassword_set(void *arg, void *value, char *errorbuf, int phase, int apply);
+static int cb_instance_maxbconn_set(void *arg, void *value, char *errorbuf, int phase, int apply);
+static int cb_instance_maxconn_set(void *arg, void *value, char *errorbuf, int phase, int apply);
+static int cb_instance_abandonto_set(void *arg, void *value, char *errorbuf, int phase, int apply);
+static int cb_instance_maxbconc_set(void *arg, void *value, char *errorbuf, int phase, int apply);
+static int cb_instance_maxconc_set(void *arg, void *value, char *errorbuf, int phase, int apply);
+static int cb_instance_imperson_set(void *arg, void *value, char *errorbuf, int phase, int apply);
+static int cb_instance_connlife_set(void *arg, void *value, char *errorbuf, int phase, int apply);
+static int cb_instance_bindto_set(void *arg, void *value, char *errorbuf, int phase, int apply);
+static int cb_instance_opto_set(void *arg, void *value, char *errorbuf, int phase, int apply);
+static int cb_instance_ref_set(void *arg, void *value, char *errorbuf, int phase, int apply);
+static int cb_instance_acl_set(void *arg, void *value, char *errorbuf, int phase, int apply);
+static int cb_instance_bindretry_set(void *arg, void *value, char *errorbuf, int phase, int apply);
+static int cb_instance_sizelimit_set(void *arg, void *value, char *errorbuf, int phase, int apply);
+static int cb_instance_timelimit_set(void *arg, void *value, char *errorbuf, int phase, int apply);
+static int cb_instance_hoplimit_set(void *arg, void *value, char *errorbuf, int phase, int apply);
+static int cb_instance_max_idle_set(void *arg, void *value, char *errorbuf, int phase, int apply);
+static int cb_instance_max_test_set(void *arg, void *value, char *errorbuf, int phase, int apply);
+
+/* Default hardwired values */
+
+cb_instance_config_info cb_the_instance_config[] = {
+{CB_CONFIG_HOSTURL,CB_CONFIG_TYPE_STRING,"",&cb_instance_hosturl_get,&cb_instance_hosturl_set,CB_ALWAYS_SHOW},
+{CB_CONFIG_BINDUSER, CB_CONFIG_TYPE_STRING, "", &cb_instance_binduser_get, &cb_instance_binduser_set,CB_ALWAYS_SHOW},
+{CB_CONFIG_USERPASSWORD,CB_CONFIG_TYPE_STRING,"",&cb_instance_userpassword_get,&cb_instance_userpassword_set,CB_ALWAYS_SHOW},
+{CB_CONFIG_MAXBINDCONNECTIONS,CB_CONFIG_TYPE_INT,CB_DEF_BIND_MAXCONNECTIONS,&cb_instance_maxbconn_get, &cb_instance_maxbconn_set,CB_ALWAYS_SHOW},
+{CB_CONFIG_MAXCONNECTIONS,CB_CONFIG_TYPE_INT,CB_DEF_MAXCONNECTIONS,&cb_instance_maxconn_get, &cb_instance_maxconn_set,CB_ALWAYS_SHOW},
+{CB_CONFIG_ABANDONTIMEOUT,CB_CONFIG_TYPE_INT,CB_DEF_ABANDON_TIMEOUT,&cb_instance_abandonto_get, &cb_instance_abandonto_set,CB_ALWAYS_SHOW},
+{CB_CONFIG_MAXBINDCONCURRENCY,CB_CONFIG_TYPE_INT,CB_DEF_BIND_MAXCONCURRENCY,&cb_instance_maxbconc_get, &cb_instance_maxbconc_set,CB_ALWAYS_SHOW},
+{CB_CONFIG_MAXCONCURRENCY,CB_CONFIG_TYPE_INT,CB_DEF_MAXCONCURRENCY,&cb_instance_maxconc_get, &cb_instance_maxconc_set,CB_ALWAYS_SHOW},
+{CB_CONFIG_IMPERSONATION,CB_CONFIG_TYPE_ONOFF,CB_DEF_IMPERSONATION,&cb_instance_imperson_get, &cb_instance_imperson_set,CB_ALWAYS_SHOW},
+{CB_CONFIG_CONNLIFETIME,CB_CONFIG_TYPE_INT,CB_DEF_CONNLIFETIME,&cb_instance_connlife_get, &cb_instance_connlife_set,CB_ALWAYS_SHOW},
+{CB_CONFIG_BINDTIMEOUT,CB_CONFIG_TYPE_INT,CB_DEF_BINDTIMEOUT,&cb_instance_bindto_get, &cb_instance_bindto_set,CB_ALWAYS_SHOW},
+{CB_CONFIG_TIMEOUT,CB_CONFIG_TYPE_INT,"0",&cb_instance_opto_get, &cb_instance_opto_set,0},
+{CB_CONFIG_REFERRAL,CB_CONFIG_TYPE_ONOFF,CB_DEF_SEARCHREFERRAL,&cb_instance_ref_get, &cb_instance_ref_set,CB_ALWAYS_SHOW},
+{CB_CONFIG_LOCALACL,CB_CONFIG_TYPE_ONOFF,CB_DEF_LOCALACL,&cb_instance_acl_get, &cb_instance_acl_set,CB_ALWAYS_SHOW},
+{CB_CONFIG_BINDRETRY,CB_CONFIG_TYPE_INT,CB_DEF_BINDRETRY,&cb_instance_bindretry_get, &cb_instance_bindretry_set,CB_ALWAYS_SHOW},
+{CB_CONFIG_SIZELIMIT,CB_CONFIG_TYPE_INT,CB_DEF_SIZELIMIT,&cb_instance_sizelimit_get, &cb_instance_sizelimit_set,CB_ALWAYS_SHOW},
+{CB_CONFIG_TIMELIMIT,CB_CONFIG_TYPE_INT,CB_DEF_TIMELIMIT,&cb_instance_timelimit_get, &cb_instance_timelimit_set,CB_ALWAYS_SHOW},
+{CB_CONFIG_HOPLIMIT,CB_CONFIG_TYPE_INT,CB_DEF_HOPLIMIT,&cb_instance_hoplimit_get, &cb_instance_hoplimit_set,CB_ALWAYS_SHOW},
+{CB_CONFIG_MAX_IDLE_TIME,CB_CONFIG_TYPE_INT,CB_DEF_MAX_IDLE_TIME,&cb_instance_max_idle_get, &cb_instance_max_idle_set,CB_ALWAYS_SHOW},
+{CB_CONFIG_MAX_TEST_TIME,CB_CONFIG_TYPE_INT,CB_DEF_MAX_TEST_TIME,&cb_instance_max_test_get, &cb_instance_max_test_set,CB_ALWAYS_SHOW},
+{NULL, 0, NULL, NULL, NULL, 0}
+};
+
+/* Others forward declarations */
+
+int cb_instance_delete_config_callback(Slapi_PBlock *pb, Slapi_Entry* e, Slapi_Entry* e2,
+ int *returncode, char *returntext, void *arg);
+int cb_instance_search_config_callback(Slapi_PBlock *pb, Slapi_Entry* entryBefore, Slapi_Entry* e,
+ int *returncode, char *returntext, void *arg);
+int cb_instance_add_config_callback(Slapi_PBlock *pb, Slapi_Entry* e, Slapi_Entry* e2,
+ int *returncode, char *returntext, void *arg);
+static int
+cb_instance_config_set(void *arg, char *attr_name, cb_instance_config_info *config_array,
+struct berval *bval, char *err_buf, int phase, int apply_mod);
+
+
+int
+cb_dont_allow_that(Slapi_PBlock *pb, Slapi_Entry* entryBefore, Slapi_Entry* e,
+ int *returncode, char *returntext, void *arg)
+{
+ *returncode=LDAP_UNWILLING_TO_PERFORM;
+ return SLAPI_DSE_CALLBACK_ERROR;
+}
+
+static char *cb_skeleton_entries[] =
+{
+
+ "dn:cn=monitor, cn=%s, cn=%s, cn=plugins, cn=config\n"
+ "objectclass:top\n"
+ "objectclass:extensibleObject\n"
+ "cn:monitor\n",
+
+ ""
+};
+
+/*
+** Initialize a backend instance with a default configuration
+*/
+
+static void cb_instance_config_set_default(cb_backend_instance *inst)
+{
+ cb_instance_config_info *config;
+ char err_buf[CB_BUFSIZE];
+
+ for (config = cb_the_instance_config; config->config_name != NULL; config++) {
+ cb_instance_config_set((void *)inst,
+ config->config_name, cb_the_instance_config, NULL /* use default */, err_buf,
+ CB_CONFIG_PHASE_INITIALIZATION, 1 /* apply */);
+ }
+
+ /* Set backend instance flags */
+ if (inst->inst_be)
+ slapi_be_set_flag(inst->inst_be,SLAPI_BE_FLAG_REMOTE_DATA);
+}
+
+/*
+** Allocate a new chaining backend instance. Internal structure
+*/
+
+static cb_backend_instance * cb_instance_alloc(cb_backend * cb, char * name, char * basedn) {
+
+ int i;
+
+ cb_backend_instance * inst =
+ (cb_backend_instance *)slapi_ch_calloc(1, sizeof(cb_backend_instance));
+
+ /* associated_be_is_disabled defaults to 0 - this may be a problem if the associated
+ be is disabled at instance creation time
+ */
+ inst->inst_name = slapi_ch_strdup(name);
+ inst->monitor.mutex = slapi_new_mutex();
+ inst->monitor_availability.cpt_lock = slapi_new_mutex();
+ inst->monitor_availability.lock_timeLimit = slapi_new_mutex();
+ inst->pool= (cb_conn_pool *) slapi_ch_calloc(1,sizeof(cb_conn_pool));
+ inst->pool->conn.conn_list_mutex = slapi_new_mutex();
+ inst->pool->conn.conn_list_cv = slapi_new_condvar(inst->pool->conn.conn_list_mutex);
+ inst->pool->bindit=1;
+
+ inst->bind_pool= (cb_conn_pool *) slapi_ch_calloc(1,sizeof(cb_conn_pool));
+ inst->bind_pool->conn.conn_list_mutex = slapi_new_mutex();
+ inst->bind_pool->conn.conn_list_cv = slapi_new_condvar(inst->bind_pool->conn.conn_list_mutex);
+
+ inst->backend_type=cb;
+ /* initialize monitor_availability */
+ inst->monitor_availability.farmserver_state = FARMSERVER_AVAILABLE ; /* we expect the farm to be available */
+ inst->monitor_availability.cpt = 0 ; /* set up the failed conn counter to 0 */
+
+ /* create RW lock to protect the config */
+ inst->rwl_config_lock = PR_NewRWLock(PR_RWLOCK_RANK_NONE, slapi_ch_strdup(name));
+
+ /* quick hack */
+ /* put a ref to the config lock in the pool */
+ /* so that the connection mgmt module can */
+ /* access the config safely */
+
+ inst->pool->rwl_config_lock = inst->rwl_config_lock;
+ inst->bind_pool->rwl_config_lock = inst->rwl_config_lock;
+
+ for (i=0; i < MAX_CONN_ARRAY; i++) {
+ inst->pool->connarray[i] = NULL;
+ inst->bind_pool->connarray[i] = NULL;
+ }
+
+ /* Config is now merged with the backend entry */
+ inst->configDn=slapi_ch_strdup(basedn);
+
+ inst->monitorDn=(char *) slapi_ch_calloc(1,strlen(basedn)+15);
+ sprintf(inst->monitorDn,"cn=monitor,%s",basedn);
+
+ inst->eq_ctx = NULL;
+
+ return inst;
+}
+
+void cb_instance_free(cb_backend_instance * inst) {
+
+ if (inst) {
+ PR_RWLock_Wlock(inst->rwl_config_lock);
+
+ if ( inst->eq_ctx != NULL )
+ {
+ slapi_eq_cancel(inst->eq_ctx);
+ inst->eq_ctx = NULL;
+ }
+
+ if (inst->bind_pool)
+ cb_close_conn_pool(inst->bind_pool);
+ if (inst->pool)
+ cb_close_conn_pool(inst->pool);
+
+ slapi_destroy_condvar(inst->bind_pool->conn.conn_list_cv);
+ slapi_destroy_condvar(inst->pool->conn.conn_list_cv);
+ slapi_destroy_mutex(inst->monitor.mutex);
+ slapi_destroy_mutex(inst->bind_pool->conn.conn_list_mutex);
+ slapi_destroy_mutex(inst->pool->conn.conn_list_mutex);
+ slapi_destroy_mutex(inst->monitor_availability.cpt_lock);
+ slapi_destroy_mutex(inst->monitor_availability.lock_timeLimit);
+ slapi_ch_free((void **) &inst->configDn);
+ slapi_ch_free((void **) &inst->monitorDn);
+ slapi_ch_free((void **) &inst->inst_name);
+ charray_free(inst->every_attribute);
+
+ slapi_ch_free((void **) &inst->bind_pool);
+ slapi_ch_free((void **) &inst->pool);
+
+ PR_RWLock_Unlock(inst->rwl_config_lock);
+
+ PR_DestroyRWLock(inst->rwl_config_lock);
+
+ slapi_ch_free((void **) &inst);
+ }
+/* XXXSD */
+}
+
+/*
+** Check the change the configuration of an existing chaining
+** backend instance.
+*/
+
+int cb_instance_modify_config_check_callback(Slapi_PBlock *pb, Slapi_Entry* entryBefore, Slapi_Entry* e,
+ int *returncode, char *returntext, void *arg) {
+
+ cb_backend_instance * inst = (cb_backend_instance *) arg;
+ LDAPMod **mods;
+ int rc = LDAP_SUCCESS;
+ int i;
+ char * attr_name;
+ returntext[0] = '\0';
+
+ CB_ASSERT(inst!=NULL);
+ slapi_pblock_get( pb, SLAPI_MODIFY_MODS, &mods );
+
+ /* First pass to validate input */
+ for (i = 0; mods[i] && LDAP_SUCCESS == rc; i++) {
+ attr_name = mods[i]->mod_type;
+
+ /* specific processing for multi-valued attributes */
+ if ( !strcasecmp ( attr_name, CB_CONFIG_SUFFIX )) {
+ sprintf(returntext, "suffix modification not allowed\n");
+ rc = LDAP_UNWILLING_TO_PERFORM;
+ continue;
+ } else
+ if ( !strcasecmp ( attr_name, CB_CONFIG_ILLEGAL_ATTRS )) {
+ continue;
+ } else
+ if ( !strcasecmp ( attr_name, CB_CONFIG_CHAINING_COMPONENTS )) {
+ continue;
+ } else
+
+ /* CB_CONFIG_BINDUSER & CB_CONFIG_USERPASSWORD may be added */
+ /* or deleted */
+
+ if ( !strcasecmp ( attr_name, CB_CONFIG_USERPASSWORD )) {
+ continue;
+ } else
+ if ( !strcasecmp ( attr_name, CB_CONFIG_BINDUSER )) {
+
+ /* Make sure value is not forbidden */
+ if ((mods[i]->mod_op & LDAP_MOD_REPLACE) ||
+ ((mods[i]->mod_op & ~LDAP_MOD_BVALUES) == LDAP_MOD_ADD)) {
+
+ rc = cb_instance_config_set((void *) inst, attr_name,
+ cb_the_instance_config, mods[i]->mod_bvalues[0], returntext,
+ CB_CONFIG_PHASE_RUNNING, 0);
+ continue;
+ }
+ }
+
+ if ((mods[i]->mod_op & LDAP_MOD_DELETE) ||
+ ((mods[i]->mod_op & ~LDAP_MOD_BVALUES) == LDAP_MOD_ADD)) {
+ rc= LDAP_UNWILLING_TO_PERFORM;
+ sprintf(returntext, "%s attributes is not allowed",
+ (mods[i]->mod_op & LDAP_MOD_DELETE) ? "Deleting" : "Adding");
+ } else if (mods[i]->mod_op & LDAP_MOD_REPLACE) {
+ /* This assumes there is only one bval for this mod. */
+ rc = cb_instance_config_set((void *) inst, attr_name,
+ cb_the_instance_config, mods[i]->mod_bvalues[0], returntext,
+ CB_CONFIG_PHASE_RUNNING, 0);
+ }
+ }
+ *returncode= rc;
+ return ((LDAP_SUCCESS == rc) ? 1:-1);
+}
+
+
+/*
+** Change the configuration of an existing chaining
+** backend instance.
+*/
+
+int cb_instance_modify_config_callback(Slapi_PBlock *pb, Slapi_Entry* entryBefore, Slapi_Entry* e,
+ int *returncode, char *returntext, void *arg) {
+
+ cb_backend_instance * inst = (cb_backend_instance *) arg;
+ LDAPMod **mods;
+ int rc = LDAP_SUCCESS;
+ int i;
+ int reopen_conn=0;
+ char * attr_name;
+ returntext[0] = '\0';
+
+ CB_ASSERT(inst!=NULL);
+ slapi_pblock_get( pb, SLAPI_MODIFY_MODS, &mods );
+
+ /* input checked in the preop modify callback */
+
+ for (i = 0; mods[i] && LDAP_SUCCESS == rc; i++) {
+ attr_name = mods[i]->mod_type;
+
+ /* specific processing for multi-valued attributes */
+ if ( !strcasecmp ( attr_name, CB_CONFIG_ILLEGAL_ATTRS )) {
+ char * config_attr_value;
+ int done=0;
+ int j;
+
+ PR_RWLock_Wlock(inst->rwl_config_lock);
+ for (j = 0; mods[i]->mod_bvalues && mods[i]->mod_bvalues[j]; j++) {
+ config_attr_value = (char *) mods[i]->mod_bvalues[j]->bv_val;
+ if ( mods[i]->mod_op & LDAP_MOD_REPLACE) {
+ if (!done) {
+ charray_free(inst->illegal_attributes);
+ inst->illegal_attributes=NULL;
+ done=1;
+ }
+ charray_add(&inst->illegal_attributes,
+ slapi_ch_strdup(config_attr_value));
+ } else
+ if ( (mods[i]->mod_op & ~LDAP_MOD_BVALUES) == LDAP_MOD_ADD) {
+ charray_add(&inst->illegal_attributes,
+ slapi_ch_strdup(config_attr_value));
+ } else
+ if ( (mods[i]->mod_op & ~LDAP_MOD_BVALUES) == LDAP_MOD_DELETE) {
+ charray_remove(inst->illegal_attributes,
+ slapi_ch_strdup(config_attr_value));
+ }
+ }
+ if (NULL == mods[i]->mod_bvalues) {
+ charray_free(inst->illegal_attributes);
+ inst->illegal_attributes=NULL;
+ }
+ PR_RWLock_Unlock(inst->rwl_config_lock);
+ continue;
+ }
+ if ( !strcasecmp ( attr_name, CB_CONFIG_CHAINING_COMPONENTS )) {
+ char * config_attr_value;
+ int done=0;
+ int j;
+
+ PR_RWLock_Wlock(inst->rwl_config_lock);
+ for (j = 0; mods[i]->mod_bvalues && mods[i]->mod_bvalues[j]; j++) {
+ config_attr_value = (char *) mods[i]->mod_bvalues[j]->bv_val;
+ if ( mods[i]->mod_op & LDAP_MOD_REPLACE) {
+ if (!done) {
+ charray_free(inst->chaining_components);
+ inst->chaining_components=NULL;
+ done=1;
+ }
+ /* XXXSD assume dns */
+ charray_add(&inst->chaining_components,
+ slapi_dn_normalize(slapi_ch_strdup(config_attr_value)));
+ } else
+ if ( (mods[i]->mod_op & ~LDAP_MOD_BVALUES) == LDAP_MOD_ADD) {
+ charray_add(&inst->chaining_components,
+ slapi_dn_normalize(slapi_ch_strdup(config_attr_value)));
+ } else
+ if ( (mods[i]->mod_op & ~LDAP_MOD_BVALUES) == LDAP_MOD_DELETE) {
+ charray_remove(inst->chaining_components,
+ slapi_dn_normalize(slapi_ch_strdup(config_attr_value)));
+ }
+ }
+ if (NULL == mods[i]->mod_bvalues) {
+ charray_free(inst->chaining_components);
+ inst->chaining_components=NULL;
+ }
+ PR_RWLock_Unlock(inst->rwl_config_lock);
+ continue;
+ }
+
+
+
+ if ((mods[i]->mod_op & LDAP_MOD_DELETE) ||
+ ((mods[i]->mod_op & ~LDAP_MOD_BVALUES) == LDAP_MOD_ADD)) {
+
+ /* Special processing for binddn & password */
+ /* because they are optional */
+
+ if (( !strcasecmp ( attr_name, CB_CONFIG_BINDUSER )) ||
+ ( !strcasecmp ( attr_name, CB_CONFIG_USERPASSWORD ))) {
+
+ if (mods[i]->mod_op & LDAP_MOD_DELETE) {
+ rc = cb_instance_config_set((void *) inst, attr_name,
+ cb_the_instance_config, NULL, returntext,
+ CB_CONFIG_PHASE_RUNNING, 1); }
+ else {
+ rc = cb_instance_config_set((void *) inst, attr_name,
+ cb_the_instance_config, mods[i]->mod_bvalues[0], returntext,
+ CB_CONFIG_PHASE_RUNNING, 1);
+ }
+ if (rc==CB_REOPEN_CONN) {
+ reopen_conn=1;
+ rc=LDAP_SUCCESS;
+ }
+ continue;
+ }
+
+ rc= LDAP_UNWILLING_TO_PERFORM;
+ sprintf(returntext, "%s attributes is not allowed",
+ (mods[i]->mod_op & LDAP_MOD_DELETE) ? "Deleting" : "Adding");
+ } else if (mods[i]->mod_op & LDAP_MOD_REPLACE) {
+ /* This assumes there is only one bval for this mod. */
+ rc = cb_instance_config_set((void *) inst, attr_name,
+ cb_the_instance_config, mods[i]->mod_bvalues[0], returntext,
+ CB_CONFIG_PHASE_RUNNING, 1);
+
+ /* Update requires to reopen connections */
+ /* Expensive operation so do it only once */
+ if (rc==CB_REOPEN_CONN) {
+ reopen_conn=1;
+ rc=LDAP_SUCCESS;
+ }
+ }
+ }
+
+ *returncode= rc;
+
+ if (reopen_conn) {
+ cb_stale_all_connections(inst);
+ }
+
+ return ((LDAP_SUCCESS == rc) ? SLAPI_DSE_CALLBACK_OK:SLAPI_DSE_CALLBACK_ERROR);
+}
+
+/*
+** Parse and instantiate backend instances
+*/
+
+int
+cb_parse_instance_config_entry(cb_backend * cb, Slapi_Entry * e) {
+
+ int rc =LDAP_SUCCESS;
+ Slapi_Attr *attr = NULL;
+ Slapi_Value *sval;
+ const struct berval *attrValue;
+ cb_backend_instance *inst=NULL;
+ char *instname;
+ Slapi_PBlock *search_pb=NULL;
+ char retmsg[CB_BUFSIZE];
+
+ CB_ASSERT(e!=NULL);
+
+ /*
+ ** Retrieve the instance name and make sure it is not
+ ** already declared.
+ */
+
+ if ( 0 == slapi_entry_attr_find( e, CB_CONFIG_INSTNAME, &attr )) {
+ slapi_attr_first_value(attr, &sval);
+ attrValue = slapi_value_get_berval(sval);
+ instname=attrValue->bv_val;
+ } else {
+ slapi_log_error( SLAPI_LOG_PLUGIN, CB_PLUGIN_SUBSYSTEM,
+ "Malformed backend instance (<%s> missing)>\n", CB_CONFIG_INSTNAME);
+ return LDAP_LOCAL_ERROR;
+ }
+
+ /* Allocate a new backend internal data structure */
+ inst = cb_instance_alloc(cb,instname,slapi_entry_get_dn(e));
+
+ /* Emulate a add config entry to configure */
+ /* this backend instance. */
+
+ cb_instance_add_config_callback(NULL,e,NULL,&rc,retmsg,inst);
+ if ( rc != LDAP_SUCCESS ) {
+ cb_instance_free(inst);
+ }
+ return rc;
+}
+
+/*
+** Update the instance configuration
+*/
+
+static int
+cb_instance_config_initialize(cb_backend_instance * inst, Slapi_Entry * e , int phase, int apply) {
+
+ int rc =LDAP_SUCCESS;
+ Slapi_Attr *attr = NULL;
+ Slapi_Value *sval;
+ struct berval * bval;
+ int using_def_connlifetime,i;
+ char err_buf[CB_BUFSIZE];
+ int urlfound=0;
+ char *rootdn;
+
+ using_def_connlifetime=1;
+
+ for (slapi_entry_first_attr(e, &attr); attr; slapi_entry_next_attr(e, attr, &attr)) {
+ char * attr_name=NULL;
+ slapi_attr_get_type(attr, &attr_name);
+
+ if ( !strcasecmp ( attr_name, CB_CONFIG_SUFFIX )) {
+ if (apply && ( inst->inst_be != NULL )) {
+ Slapi_DN *suffix;
+ suffix = slapi_sdn_new();
+ i = slapi_attr_first_value(attr, &sval);
+ while (i != -1 ) {
+ bval = (struct berval *) slapi_value_get_berval(sval);
+ slapi_sdn_init_dn_byref(suffix, bval->bv_val);
+
+ if (!slapi_be_issuffix(inst->inst_be, suffix)) {
+ slapi_be_addsuffix(inst->inst_be, suffix);
+ }
+ slapi_sdn_done(suffix);
+ slapi_sdn_free(&suffix);
+ i = slapi_attr_next_value(attr, i, &sval);
+ }
+ }
+ continue;
+ } else
+ if ( !strcasecmp ( attr_name, CB_CONFIG_CHAINING_COMPONENTS )) {
+
+ if (apply) {
+ PR_RWLock_Wlock(inst->rwl_config_lock);
+ i = slapi_attr_first_value(attr, &sval);
+ charray_free(inst->chaining_components);
+ inst->chaining_components=NULL;
+ while (i != -1 ) {
+ bval = (struct berval *) slapi_value_get_berval(sval);
+ charray_add(&inst->chaining_components,
+ slapi_dn_normalize(slapi_ch_strdup(bval->bv_val)));
+ i = slapi_attr_next_value(attr, i, &sval);
+ }
+ PR_RWLock_Unlock(inst->rwl_config_lock);
+ }
+ continue;
+ } else
+ if ( !strcasecmp ( attr_name, CB_CONFIG_ILLEGAL_ATTRS )) {
+
+ if (apply) {
+ PR_RWLock_Wlock(inst->rwl_config_lock);
+ i = slapi_attr_first_value(attr, &sval);
+ charray_free(inst->illegal_attributes);
+ inst->illegal_attributes=NULL;
+ while (i != -1 ) {
+ bval = (struct berval *) slapi_value_get_berval(sval);
+ charray_add(&inst->illegal_attributes,
+ slapi_ch_strdup(bval->bv_val));
+ i = slapi_attr_next_value(attr, i, &sval);
+ }
+ PR_RWLock_Unlock(inst->rwl_config_lock);
+ }
+ continue;
+ }
+
+
+ if ( !strcasecmp ( attr_name, CB_CONFIG_HOSTURL )) {
+ urlfound=1;
+ }
+
+
+ /* We are assuming that each of these attributes are to have
+ * only one value. If they have more than one value, like
+ * the nsslapd-suffix attribute, then they need to be
+ * handled differently. */
+
+ slapi_attr_first_value(attr, &sval);
+ bval = (struct berval *) slapi_value_get_berval(sval);
+
+ if (cb_instance_config_set((void *) inst, attr_name,
+ cb_the_instance_config, bval, err_buf, phase, apply ) != LDAP_SUCCESS) {
+ slapi_log_error( SLAPI_LOG_FATAL,
+ CB_PLUGIN_SUBSYSTEM,"Error with config attribute %s : %s\n",
+ attr_name, err_buf);
+ rc=LDAP_LOCAL_ERROR;
+ break;
+ }
+ if ( !strcasecmp ( attr_name, CB_CONFIG_CONNLIFETIME )) {
+ using_def_connlifetime=0;
+ }
+ }
+
+
+ /*
+ ** Check for mandatory attributes
+ ** Post-Processing
+ */
+
+ if (LDAP_SUCCESS == rc) {
+ if (!urlfound) {
+ slapi_log_error( SLAPI_LOG_FATAL, CB_PLUGIN_SUBSYSTEM,
+ "Malformed backend instance entry. Mandatory attr <%s> missing\n",
+ CB_CONFIG_HOSTURL);
+ rc= LDAP_LOCAL_ERROR;
+ }
+
+ if (apply ) {
+ if ( using_def_connlifetime &&
+ strchr( inst->pool->hostname, ' ' ) != NULL ) {
+
+ cb_instance_config_set((void *)inst, CB_CONFIG_CONNLIFETIME,
+ cb_the_instance_config, NULL /* use default */, err_buf,
+ CB_CONFIG_PHASE_INITIALIZATION, 1 );
+ }
+ }
+ }
+
+ /*
+ ** Additional checks
+ ** It is forbidden to use directory manager as proxy user
+ ** due to a bug in the acl check
+ */
+
+ rootdn=cb_get_rootdn();
+
+ if (inst->impersonate && inst->pool && inst->pool->binddn &&
+ !strcmp(inst->pool->binddn,rootdn)) { /* UTF8 aware */
+ slapi_log_error( SLAPI_LOG_FATAL,
+ CB_PLUGIN_SUBSYSTEM,"Error with config attribute %s (%s: forbidden value)\n",
+ CB_CONFIG_BINDUSER, rootdn);
+ rc=LDAP_LOCAL_ERROR;
+ }
+ slapi_ch_free((void **)&rootdn);
+
+ return rc;
+}
+
+/********************************************************
+** Get and set functions for chaining backend instances *
+*********************************************************
+*/
+
+static void *cb_instance_hosturl_get(void *arg)
+{
+ cb_backend_instance * inst=(cb_backend_instance *) arg;
+ char * data;
+
+ PR_RWLock_Rlock(inst->rwl_config_lock);
+ data = slapi_ch_strdup(inst->pool->url);
+ PR_RWLock_Unlock(inst->rwl_config_lock);
+ return data;
+}
+
+static int cb_instance_hosturl_set(void *arg, void *value, char *errorbuf, int phase, int apply)
+{
+ cb_backend_instance *inst=(cb_backend_instance *) arg;
+ char *url = (char *) value;
+ LDAPURLDesc *ludp=NULL;
+ int rc=LDAP_SUCCESS;
+
+ if (( rc = ldap_url_parse( url, &ludp )) != 0 ) {
+ strcpy(errorbuf,cb_urlparse_err2string( rc ));
+ if (CB_CONFIG_PHASE_INITIALIZATION == phase)
+ inst->pool->url=slapi_ch_strdup("");
+ return(LDAP_INVALID_SYNTAX);
+ }
+
+ if (apply) {
+
+ char * ptr;
+
+ PR_RWLock_Wlock(inst->rwl_config_lock);
+
+ if (( phase != CB_CONFIG_PHASE_INITIALIZATION ) &&
+ ( phase != CB_CONFIG_PHASE_STARTUP )) {
+
+ /* Dynamic modification */
+ /* Don't free char * pointer now */
+ /* STore them in a waste basket */
+ /* Will be relesase when the backend stops */
+
+ if (inst->pool->hostname)
+ charray_add(&inst->pool->waste_basket,inst->pool->hostname);
+ if (inst->pool->url)
+ charray_add(&inst->pool->waste_basket,inst->pool->url);
+
+ if (inst->bind_pool->hostname)
+ charray_add(&inst->bind_pool->waste_basket,inst->bind_pool->hostname);
+ if (inst->bind_pool->url)
+ charray_add(&inst->bind_pool->waste_basket,inst->bind_pool->url);
+
+ /* Require connection cleanup */
+ rc=CB_REOPEN_CONN;
+ }
+
+ /* Normal case. Extract useful data from */
+ /* the url and update the configuration */
+
+ if ((ludp->lud_host==NULL) || (strlen(ludp->lud_host)==0)) {
+ inst->pool->hostname=(char *)slapi_ch_strdup((char *)get_localhost_DNS());
+ } else {
+ inst->pool->hostname = slapi_ch_strdup( ludp->lud_host );
+ }
+ inst->pool->url = slapi_ch_strdup( url);
+ inst->pool->secure = (( ludp->lud_options & LDAP_URL_OPT_SECURE ) != 0 );
+
+ if ((ludp->lud_port==0) && inst->pool->secure)
+ inst->pool->port=CB_LDAP_SECURE_PORT;
+ else
+ inst->pool->port = ludp->lud_port;
+
+ /* Build a charray of <host>:<port> */
+ /* hostname is of the form <host>[:port] <host>[:port] */
+
+ { char * aBufCopy, * aHostName;
+ char * iter = NULL;
+ aBufCopy= aBufCopy=slapi_ch_strdup(inst->pool->hostname);
+
+ aHostName=ldap_utf8strtok_r(aBufCopy," ", &iter);
+ charray_free(inst->url_array);
+ inst->url_array=NULL;
+ while (aHostName) {
+
+ char * aHostPort = slapi_ch_calloc(1,strlen(aHostName)+30);
+ if ( NULL == ( ptr=strstr(aHostName,":")))
+ sprintf(aHostPort,"%s://%s:%d/",
+ inst->pool->secure ? "ldaps" : "ldap",
+ aHostName,inst->pool->port);
+ else
+ sprintf(aHostPort,"%s://%s/",
+ inst->pool->secure ? "ldaps" : "ldap",
+ aHostName);
+
+ charray_add(&inst->url_array,aHostPort);
+ aHostName=ldap_utf8strtok_r(NULL," ", &iter);
+ }
+
+ slapi_ch_free((void **) &aBufCopy);
+ }
+
+ inst->bind_pool->port=inst->pool->port;
+ inst->bind_pool->secure=inst->pool->secure;
+ inst->bind_pool->hostname=slapi_ch_strdup(inst->pool->hostname);
+
+ PR_RWLock_Unlock(inst->rwl_config_lock);
+ }
+
+ if ( ludp != NULL ) {
+ ldap_free_urldesc( ludp );
+ }
+ return rc;
+}
+
+static void *cb_instance_binduser_get(void *arg)
+{
+ cb_backend_instance * inst=(cb_backend_instance *) arg;
+ char * data;
+
+ PR_RWLock_Rlock(inst->rwl_config_lock);
+ data = slapi_ch_strdup(inst->pool->binddn2); /* not normalized */
+ PR_RWLock_Unlock(inst->rwl_config_lock);
+ return data;
+}
+
+static int cb_instance_binduser_set(void *arg, void *value, char *errorbuf, int phase, int apply)
+{
+ cb_backend_instance * inst=(cb_backend_instance *) arg;
+ int rc=LDAP_SUCCESS;
+
+ if (apply) {
+
+ PR_RWLock_Wlock(inst->rwl_config_lock);
+ if (( phase != CB_CONFIG_PHASE_INITIALIZATION ) &&
+ ( phase != CB_CONFIG_PHASE_STARTUP )) {
+
+ /* Dynamic modif */
+ /* Free user later */
+
+ charray_add(&inst->pool->waste_basket,inst->pool->binddn);
+ charray_add(&inst->pool->waste_basket,inst->pool->binddn2);
+ rc=CB_REOPEN_CONN;
+ }
+
+ inst->pool->binddn=slapi_ch_strdup((char *) value);
+ inst->pool->binddn2=slapi_ch_strdup((char *) value);
+ slapi_dn_normalize_case(inst->pool->binddn);
+ PR_RWLock_Unlock(inst->rwl_config_lock);
+ } else {
+
+ /* Security check */
+ /* directory manager of the farm server should not be used as */
+ /* proxing user. This is hard to check, so assume same directory */
+ /* manager across servers. */
+
+ char * rootdn = cb_get_rootdn();
+ char * theValueCopy = NULL;
+
+ if (value) {
+ theValueCopy=slapi_ch_strdup((char *) value);
+ slapi_dn_normalize_case(theValueCopy);
+ }
+
+ PR_RWLock_Rlock(inst->rwl_config_lock);
+ if (inst->impersonate && theValueCopy &&
+ !strcmp(theValueCopy,rootdn)) { /* UTF8-aware. See cb_get_dn() */
+ rc=LDAP_UNWILLING_TO_PERFORM;
+ if (errorbuf) {
+ sprintf(errorbuf,"value %s not allowed",rootdn);
+ }
+ }
+ PR_RWLock_Unlock(inst->rwl_config_lock);
+
+ slapi_ch_free((void **)&theValueCopy);
+ slapi_ch_free((void **)&rootdn);
+ }
+
+ return rc;
+}
+
+
+static void *cb_instance_userpassword_get(void *arg)
+{
+ cb_backend_instance * inst=(cb_backend_instance *) arg;
+ char * data;
+
+ PR_RWLock_Rlock(inst->rwl_config_lock);
+ data = slapi_ch_strdup(inst->pool->password);
+ PR_RWLock_Unlock(inst->rwl_config_lock);
+ return data;
+}
+
+static int cb_instance_userpassword_set(void *arg, void *value, char *errorbuf, int phase, int apply)
+{
+ cb_backend_instance * inst=(cb_backend_instance *) arg;
+ int rc=LDAP_SUCCESS;
+
+ if (apply) {
+ PR_RWLock_Wlock(inst->rwl_config_lock);
+ if (( phase != CB_CONFIG_PHASE_INITIALIZATION ) &&
+ ( phase != CB_CONFIG_PHASE_STARTUP )) {
+
+ /* Dynamic modif */
+ charray_add(&inst->pool->waste_basket,inst->pool->password);
+ rc=CB_REOPEN_CONN;
+ }
+
+ inst->pool->password=slapi_ch_strdup((char *) value);
+ PR_RWLock_Unlock(inst->rwl_config_lock);
+ }
+ return rc;
+}
+
+static void *cb_instance_sizelimit_get(void *arg)
+{
+ cb_backend_instance * inst=(cb_backend_instance *) arg;
+ int data;
+
+ PR_RWLock_Rlock(inst->rwl_config_lock);
+ data = inst->sizelimit;
+ PR_RWLock_Unlock(inst->rwl_config_lock);
+ return (void *) data;
+}
+
+static int cb_instance_sizelimit_set(void *arg, void *value, char *errorbuf, int phase, int apply)
+{
+ cb_backend_instance * inst=(cb_backend_instance *) arg;
+ if (apply) {
+ PR_RWLock_Wlock(inst->rwl_config_lock);
+ inst->sizelimit=(int) value;
+ PR_RWLock_Unlock(inst->rwl_config_lock);
+ if (inst->inst_be)
+ be_set_sizelimit(inst->inst_be, (int) value);
+ }
+ return LDAP_SUCCESS;
+}
+
+static void *cb_instance_timelimit_get(void *arg)
+{
+ cb_backend_instance * inst=(cb_backend_instance *) arg;
+ int data;
+
+ PR_RWLock_Rlock(inst->rwl_config_lock);
+ data = inst->timelimit;
+ PR_RWLock_Unlock(inst->rwl_config_lock);
+ return (void *) data;
+}
+
+static int cb_instance_timelimit_set(void *arg, void *value, char *errorbuf, int phase, int apply)
+{
+ cb_backend_instance * inst=(cb_backend_instance *) arg;
+ if (apply) {
+ PR_RWLock_Wlock(inst->rwl_config_lock);
+ inst->timelimit=(int) value;
+ PR_RWLock_Unlock(inst->rwl_config_lock);
+ if (inst->inst_be)
+ be_set_timelimit(inst->inst_be, (int) value);
+ }
+ return LDAP_SUCCESS;
+}
+
+static void *cb_instance_max_test_get(void *arg)
+{
+ cb_backend_instance * inst=(cb_backend_instance *) arg;
+ int data;
+
+ PR_RWLock_Rlock(inst->rwl_config_lock);
+ data = inst->max_test_time;
+ PR_RWLock_Unlock(inst->rwl_config_lock);
+ return (void *) data;
+}
+
+static int cb_instance_max_test_set(void *arg, void *value, char *errorbuf, int phase, int apply)
+{
+ cb_backend_instance * inst=(cb_backend_instance *) arg;
+ if (apply) {
+ PR_RWLock_Wlock(inst->rwl_config_lock);
+ inst->max_test_time=(int) value;
+ PR_RWLock_Unlock(inst->rwl_config_lock);
+ }
+ return LDAP_SUCCESS;
+}
+
+static void *cb_instance_max_idle_get(void *arg)
+{
+ cb_backend_instance * inst=(cb_backend_instance *) arg;
+ int data;
+
+ PR_RWLock_Rlock(inst->rwl_config_lock);
+ data = inst->max_idle_time;
+ PR_RWLock_Unlock(inst->rwl_config_lock);
+ return (void *) data;
+}
+
+static int cb_instance_max_idle_set(void *arg, void *value, char *errorbuf, int phase, int apply)
+{
+ cb_backend_instance * inst=(cb_backend_instance *) arg;
+ if (apply) {
+ PR_RWLock_Wlock(inst->rwl_config_lock);
+ inst->max_idle_time=(int) value;
+ PR_RWLock_Unlock(inst->rwl_config_lock);
+ }
+ return LDAP_SUCCESS;
+}
+
+
+static void *cb_instance_hoplimit_get(void *arg)
+{
+ cb_backend_instance * inst=(cb_backend_instance *) arg;
+ int data;
+
+ PR_RWLock_Rlock(inst->rwl_config_lock);
+ data = inst->hoplimit;
+ PR_RWLock_Unlock(inst->rwl_config_lock);
+ return (void *) data;
+}
+
+static int cb_instance_hoplimit_set(void *arg, void *value, char *errorbuf, int phase, int apply)
+{
+ cb_backend_instance * inst=(cb_backend_instance *) arg;
+ if (apply) {
+ PR_RWLock_Wlock(inst->rwl_config_lock);
+ inst->hoplimit=(int) value;
+ PR_RWLock_Unlock(inst->rwl_config_lock);
+ }
+ return LDAP_SUCCESS;
+}
+
+static void *cb_instance_maxbconn_get(void *arg)
+{
+ cb_backend_instance * inst=(cb_backend_instance *) arg;
+ int data;
+
+ PR_RWLock_Rlock(inst->rwl_config_lock);
+ data = inst->bind_pool->conn.maxconnections;
+ PR_RWLock_Unlock(inst->rwl_config_lock);
+ return (void *) data;
+}
+
+static int cb_instance_maxbconn_set(void *arg, void *value, char *errorbuf, int phase, int apply)
+{
+ cb_backend_instance * inst=(cb_backend_instance *) arg;
+ if (apply) {
+ PR_RWLock_Wlock(inst->rwl_config_lock);
+ inst->bind_pool->conn.maxconnections=(int) value;
+ PR_RWLock_Unlock(inst->rwl_config_lock);
+ }
+ return LDAP_SUCCESS;
+}
+
+static void *cb_instance_maxconn_get(void *arg)
+{
+ cb_backend_instance * inst=(cb_backend_instance *) arg;
+ int data;
+
+ PR_RWLock_Rlock(inst->rwl_config_lock);
+ data = inst->pool->conn.maxconnections;
+ PR_RWLock_Unlock(inst->rwl_config_lock);
+ return (void *) data;
+}
+
+static int cb_instance_maxconn_set(void *arg, void *value, char *errorbuf, int phase, int apply)
+{
+ cb_backend_instance * inst=(cb_backend_instance *) arg;
+ if (apply) {
+ PR_RWLock_Wlock(inst->rwl_config_lock);
+ inst->pool->conn.maxconnections=(int) value;
+ PR_RWLock_Unlock(inst->rwl_config_lock);
+ }
+ return LDAP_SUCCESS;
+}
+
+static void *cb_instance_abandonto_get(void *arg)
+{
+ cb_backend_instance * inst=(cb_backend_instance *) arg;
+ int data;
+
+ PR_RWLock_Rlock(inst->rwl_config_lock);
+ data = inst->abandon_timeout.tv_sec;
+ PR_RWLock_Unlock(inst->rwl_config_lock);
+ return (void *) data;
+}
+
+static int cb_instance_abandonto_set(void *arg, void *value, char *errorbuf, int phase, int apply)
+{
+ cb_backend_instance * inst=(cb_backend_instance *) arg;
+
+ if (apply) {
+ if (( phase != CB_CONFIG_PHASE_INITIALIZATION ) &&
+ ( phase != CB_CONFIG_PHASE_STARTUP )) {
+
+ /* Dynamic modif not supported */
+ /* Stored in ldif only */
+ return LDAP_SUCCESS;
+ }
+
+ PR_RWLock_Wlock(inst->rwl_config_lock);
+ inst->abandon_timeout.tv_sec=(int) value;
+ inst->abandon_timeout.tv_usec=0;
+ PR_RWLock_Unlock(inst->rwl_config_lock);
+ }
+ return LDAP_SUCCESS;
+}
+
+static void *cb_instance_maxbconc_get(void *arg)
+{
+ cb_backend_instance * inst=(cb_backend_instance *) arg;
+ int data;
+
+ PR_RWLock_Rlock(inst->rwl_config_lock);
+ data = inst->bind_pool->conn.maxconcurrency;
+ PR_RWLock_Unlock(inst->rwl_config_lock);
+ return (void *) data;
+}
+
+static int cb_instance_maxbconc_set(void *arg, void *value, char *errorbuf, int phase, int apply)
+{
+ cb_backend_instance * inst=(cb_backend_instance *) arg;
+ if (apply) {
+ PR_RWLock_Wlock(inst->rwl_config_lock);
+ inst->bind_pool->conn.maxconcurrency=(int) value;
+ PR_RWLock_Unlock(inst->rwl_config_lock);
+ }
+ return LDAP_SUCCESS;
+}
+
+static void *cb_instance_maxconc_get(void *arg)
+{
+ cb_backend_instance * inst=(cb_backend_instance *) arg;
+ int data;
+
+ PR_RWLock_Rlock(inst->rwl_config_lock);
+ data = inst->pool->conn.maxconcurrency;
+ PR_RWLock_Unlock(inst->rwl_config_lock);
+ return (void *) data;
+}
+
+static int cb_instance_maxconc_set(void *arg, void *value, char *errorbuf, int phase, int apply)
+{
+ cb_backend_instance * inst=(cb_backend_instance *) arg;
+ if (apply) {
+ PR_RWLock_Wlock(inst->rwl_config_lock);
+ inst->pool->conn.maxconcurrency=(int) value;
+ PR_RWLock_Unlock(inst->rwl_config_lock);
+ }
+ return LDAP_SUCCESS;
+}
+
+static void *cb_instance_imperson_get(void *arg)
+{
+ cb_backend_instance * inst=(cb_backend_instance *) arg;
+ int data;
+
+ PR_RWLock_Rlock(inst->rwl_config_lock);
+ data = inst->impersonate;
+ PR_RWLock_Unlock(inst->rwl_config_lock);
+ return (void *) data;
+}
+
+static int cb_instance_imperson_set(void *arg, void *value, char *errorbuf, int phase, int apply)
+{
+ cb_backend_instance * inst=(cb_backend_instance *) arg;
+ int rc=LDAP_SUCCESS;
+
+ if (apply) {
+ PR_RWLock_Wlock(inst->rwl_config_lock);
+ inst->impersonate=(int) value;
+ PR_RWLock_Unlock(inst->rwl_config_lock);
+ } else {
+ /* Security check: Make sure the proxing user is */
+ /* not the directory manager. */
+
+ char * rootdn=cb_get_rootdn();
+
+ PR_RWLock_Rlock(inst->rwl_config_lock);
+ if (((int) value) && inst->pool && inst->pool->binddn &&
+ !strcmp(inst->pool->binddn,rootdn)) { /* UTF-8 aware */
+ rc=LDAP_UNWILLING_TO_PERFORM;
+ if (errorbuf)
+ sprintf(errorbuf,"Proxy mode incompatible with %s value (%s not allowed)",
+ CB_CONFIG_BINDUSER,rootdn);
+ }
+ PR_RWLock_Unlock(inst->rwl_config_lock);
+ slapi_ch_free((void **)&rootdn);
+ }
+
+ return rc;
+}
+
+static void *cb_instance_connlife_get(void *arg)
+{
+ cb_backend_instance * inst=(cb_backend_instance *) arg;
+ int data;
+
+ PR_RWLock_Rlock(inst->rwl_config_lock);
+ data=inst->pool->conn.connlifetime;
+ PR_RWLock_Unlock(inst->rwl_config_lock);
+ return (void *) data;
+}
+
+static int cb_instance_connlife_set(void *arg, void *value, char *errorbuf, int phase, int apply)
+{
+ cb_backend_instance * inst=(cb_backend_instance *) arg;
+ if (apply) {
+ PR_RWLock_Wlock(inst->rwl_config_lock);
+ inst->pool->conn.connlifetime=(int) value;
+ PR_RWLock_Unlock(inst->rwl_config_lock);
+ }
+ return LDAP_SUCCESS;
+}
+
+static void *cb_instance_bindto_get(void *arg)
+{
+ cb_backend_instance * inst=(cb_backend_instance *) arg;
+ int data;
+
+ PR_RWLock_Rlock(inst->rwl_config_lock);
+ data=inst->bind_pool->conn.op_timeout.tv_sec;
+ PR_RWLock_Unlock(inst->rwl_config_lock);
+ return (void *) data;
+}
+
+static int cb_instance_bindto_set(void *arg, void *value, char *errorbuf, int phase, int apply)
+{
+ cb_backend_instance * inst=(cb_backend_instance *) arg;
+ if (apply) {
+ PR_RWLock_Wlock(inst->rwl_config_lock);
+ inst->bind_pool->conn.op_timeout.tv_sec=(int) value;
+ inst->bind_pool->conn.op_timeout.tv_usec=0;
+ inst->bind_pool->conn.bind_timeout.tv_sec=(int) value;
+ inst->bind_pool->conn.bind_timeout.tv_usec=0;
+ /* Used to bind to the farm server */
+ inst->pool->conn.bind_timeout.tv_sec=(int) value;
+ inst->pool->conn.bind_timeout.tv_usec=0;
+ PR_RWLock_Unlock(inst->rwl_config_lock);
+ }
+ return LDAP_SUCCESS;
+}
+
+static void *cb_instance_opto_get(void *arg)
+{
+ cb_backend_instance * inst=(cb_backend_instance *) arg;
+ int data;
+
+ PR_RWLock_Rlock(inst->rwl_config_lock);
+ data=inst->pool->conn.op_timeout.tv_sec;
+ PR_RWLock_Unlock(inst->rwl_config_lock);
+ return (void *) data;
+}
+
+static int cb_instance_opto_set(void *arg, void *value, char *errorbuf, int phase, int apply)
+{
+ cb_backend_instance * inst=(cb_backend_instance *) arg;
+ if (apply) {
+ PR_RWLock_Wlock(inst->rwl_config_lock);
+ inst->pool->conn.op_timeout.tv_sec=(int) value;
+ inst->pool->conn.op_timeout.tv_usec=0;
+ PR_RWLock_Unlock(inst->rwl_config_lock);
+ }
+ return LDAP_SUCCESS;
+}
+
+static void *cb_instance_ref_get(void *arg)
+{
+ cb_backend_instance * inst=(cb_backend_instance *) arg;
+ int data;
+
+ PR_RWLock_Rlock(inst->rwl_config_lock);
+ data=inst->searchreferral;
+ PR_RWLock_Unlock(inst->rwl_config_lock);
+ return (void *) data;
+}
+
+static int cb_instance_ref_set(void *arg, void *value, char *errorbuf, int phase, int apply)
+{
+ cb_backend_instance * inst=(cb_backend_instance *) arg;
+ if (apply) {
+ PR_RWLock_Wlock(inst->rwl_config_lock);
+ inst->searchreferral=(int) value;
+ PR_RWLock_Unlock(inst->rwl_config_lock);
+ }
+ return LDAP_SUCCESS;
+}
+
+static void *cb_instance_acl_get(void *arg)
+{
+ cb_backend_instance * inst=(cb_backend_instance *) arg;
+ int data;
+
+ PR_RWLock_Rlock(inst->rwl_config_lock);
+ data=inst->local_acl;
+ PR_RWLock_Unlock(inst->rwl_config_lock);
+ return (void *) data;
+}
+
+static int cb_instance_acl_set(void *arg, void *value, char *errorbuf, int phase, int apply)
+{
+ cb_backend_instance * inst=(cb_backend_instance *) arg;
+
+ if (apply) {
+ if (( phase != CB_CONFIG_PHASE_INITIALIZATION ) &&
+ ( phase != CB_CONFIG_PHASE_STARTUP )) {
+
+ /* Dynamic modif not supported */
+ /* Stored in ldif only */
+ return LDAP_SUCCESS;
+ }
+ PR_RWLock_Wlock(inst->rwl_config_lock);
+ inst->local_acl=(int) value;
+ PR_RWLock_Unlock(inst->rwl_config_lock);
+ }
+ return LDAP_SUCCESS;
+}
+
+static void *cb_instance_bindretry_get(void *arg)
+{
+ cb_backend_instance * inst=(cb_backend_instance *) arg;
+ int data;
+
+ PR_RWLock_Rlock(inst->rwl_config_lock);
+ data=inst->bind_retry;
+ PR_RWLock_Unlock(inst->rwl_config_lock);
+ return (void *) data;
+}
+
+static int cb_instance_bindretry_set(void *arg, void *value, char *errorbuf, int phase, int apply)
+{
+ cb_backend_instance * inst=(cb_backend_instance *) arg;
+ if (apply) {
+ PR_RWLock_Wlock(inst->rwl_config_lock);
+ inst->bind_retry=(int) value;
+ PR_RWLock_Unlock(inst->rwl_config_lock);
+ }
+ return LDAP_SUCCESS;
+}
+
+
+
+
+/* Finds an entry in a config_info array with the given name. Returns
+ * the entry on success and NULL when not found.
+ */
+static cb_instance_config_info *cb_get_config_info(cb_instance_config_info *config_array, char *attr_name)
+{
+ int x;
+
+ for(x = 0; config_array[x].config_name != NULL; x++) {
+ if (!strcasecmp(config_array[x].config_name, attr_name)) {
+ return &(config_array[x]);
+ }
+ }
+ return NULL;
+}
+
+/*
+** Update an attribute value
+** For now, unknown attributes are ignored
+** Return a LDAP error code OR CB_REOPEN_CONN when the
+** update requires to close open connections.
+*/
+
+static int
+cb_instance_config_set(void *arg, char *attr_name, cb_instance_config_info *config_array,
+struct berval *bval, char *err_buf, int phase, int apply_mod)
+{
+ cb_instance_config_info *config;
+ int use_default;
+ int int_val;
+ long long_val;
+ int retval=LDAP_LOCAL_ERROR;
+
+ config = cb_get_config_info(config_array, attr_name);
+ if (NULL == config) {
+ /* Ignore unknown attributes */
+ return LDAP_SUCCESS;
+ }
+
+ /* If the config phase is initialization or if bval is NULL, we will use
+ * the default value for the attribute. */
+ if (CB_CONFIG_PHASE_INITIALIZATION == phase || NULL == bval) {
+ use_default = 1;
+ } else {
+ use_default = 0;
+ /* Since we are setting the value for the config attribute, we
+ * need to turn on the CB_PREVIOUSLY_SET flag to make
+ * sure this attribute is shown. */
+ config->config_flags |= CB_PREVIOUSLY_SET;
+ }
+
+ switch(config->config_type) {
+ case CB_CONFIG_TYPE_INT:
+ if (use_default) {
+ int_val = cb_atoi(config->config_default_value);
+ } else {
+ int_val = cb_atoi((char *)bval->bv_val);
+ }
+ retval = config->config_set_fn(arg, (void *) int_val, err_buf, phase, apply_mod);
+ break;
+ case CB_CONFIG_TYPE_INT_OCTAL:
+ if (use_default) {
+ int_val = (int) strtol(config->config_default_value, NULL, 8);
+ } else {
+ int_val = (int) strtol((char *)bval->bv_val, NULL, 8);
+ }
+ retval = config->config_set_fn(arg, (void *) int_val, err_buf, phase, apply_mod);
+ break;
+ case CB_CONFIG_TYPE_LONG:
+ if (use_default) {
+ long_val = cb_atol(config->config_default_value);
+ } else {
+ long_val = cb_atol((char *)bval->bv_val);
+ }
+ retval = config->config_set_fn(arg, (void *) long_val, err_buf, phase, apply_mod);
+ break;
+ case CB_CONFIG_TYPE_STRING:
+ if (use_default) {
+ retval = config->config_set_fn(arg, config->config_default_value, err_buf, phase, apply_mod);
+ } else {
+ retval = config->config_set_fn(arg, bval->bv_val, err_buf, phase, apply_mod);
+ }
+ break;
+ case CB_CONFIG_TYPE_ONOFF:
+ if (use_default) {
+ int_val = !strcasecmp(config->config_default_value, "on");
+ } else {
+ int_val = !strcasecmp((char *) bval->bv_val, "on");
+ }
+ retval = config->config_set_fn(arg, (void *) int_val, err_buf, phase, apply_mod);
+ break;
+ }
+ return retval;
+}
+
+/* Utility function used in creating config entries. Using the
+ * config_info, this function gets info and formats in the correct
+ * way.
+ */
+void cb_instance_config_get(void *arg, cb_instance_config_info *config, char *buf)
+{
+ char *tmp_string;
+
+ if (config == NULL) {
+ buf[0] = '\0';
+ }
+
+ switch(config->config_type) {
+ case CB_CONFIG_TYPE_INT:
+ sprintf(buf, "%d", (int) config->config_get_fn(arg));
+ break;
+ case CB_CONFIG_TYPE_INT_OCTAL:
+ sprintf(buf, "%o", (int) config->config_get_fn(arg));
+ break;
+ case CB_CONFIG_TYPE_LONG:
+ sprintf(buf, "%d", (long) config->config_get_fn(arg));
+ break;
+ case CB_CONFIG_TYPE_STRING:
+ /* Remember the get function for strings returns memory
+ * that must be freed. */
+ tmp_string = (char *) config->config_get_fn(arg);
+ sprintf(buf, "%s", (char *) tmp_string);
+ slapi_ch_free((void **)&tmp_string);
+ break;
+ case CB_CONFIG_TYPE_ONOFF:
+ if ((int) config->config_get_fn(arg)) {
+ sprintf(buf,"%s","on");
+ } else {
+ sprintf(buf,"%s","off");
+ }
+ break;
+ default:
+ slapi_log_error( SLAPI_LOG_PLUGIN, CB_PLUGIN_SUBSYSTEM,
+ "Invalid attribute syntax.\n");
+
+ }
+}
+
+/*
+** Search for instance config entry
+** Always return 'active' values because some configuration changes
+** won't be taken into account until the server restarts
+*/
+
+int cb_instance_search_config_callback(Slapi_PBlock *pb, Slapi_Entry* e, Slapi_Entry* entryAfter,
+ int *returncode, char *returntext, void *arg) {
+
+ char buf[CB_BUFSIZE];
+ struct berval val;
+ struct berval *vals[2];
+ int i = 0;
+ cb_backend_instance *inst = (cb_backend_instance *)arg;
+ cb_instance_config_info * config;
+
+ vals[0] = &val;
+ vals[1] = NULL;
+
+ /* suffixes */
+
+ PR_RWLock_Rlock(inst->rwl_config_lock);
+
+ {
+ const Slapi_DN *aSuffix;
+ i=0;
+ if (inst->inst_be) {
+ while ((aSuffix=slapi_be_getsuffix(inst->inst_be,i))) {
+ val.bv_val = (char *)slapi_sdn_get_dn(aSuffix);
+ val.bv_len = strlen( val.bv_val );
+ if (val.bv_len) {
+ if (i==0)
+ slapi_entry_attr_replace(e,CB_CONFIG_SUFFIX,(struct berval **)vals );
+ else
+ slapi_entry_attr_merge(e,CB_CONFIG_SUFFIX,(struct berval **)vals );
+ }
+ i++;
+ }
+ }
+ }
+
+ for (i=0; inst->chaining_components && inst->chaining_components[i]; i++) {
+ val.bv_val = inst->chaining_components[i];
+ val.bv_len = strlen( val.bv_val );
+ if (val.bv_len) {
+ if (i==0)
+ slapi_entry_attr_replace(e,CB_CONFIG_CHAINING_COMPONENTS,(struct berval **)vals );
+ else
+ slapi_entry_attr_merge(e,CB_CONFIG_CHAINING_COMPONENTS,(struct berval **)vals );
+ }
+ }
+
+ for (i=0; inst->illegal_attributes && inst->illegal_attributes[i]; i++) {
+ val.bv_val = inst->illegal_attributes[i];
+ val.bv_len = strlen( val.bv_val );
+ if (val.bv_len) {
+ if (i==0)
+ slapi_entry_attr_replace(e,CB_CONFIG_ILLEGAL_ATTRS,(struct berval **)vals );
+ else
+ slapi_entry_attr_merge(e,CB_CONFIG_ILLEGAL_ATTRS,(struct berval **)vals );
+ }
+ }
+
+ PR_RWLock_Unlock(inst->rwl_config_lock);
+
+ /* standard attributes */
+ for(config = cb_the_instance_config; config->config_name != NULL; config++) {
+ if (!(config->config_flags & (CB_ALWAYS_SHOW | CB_PREVIOUSLY_SET))) {
+ /* This config option shouldn't be shown */
+ continue;
+ }
+
+ cb_instance_config_get((void *) inst, config, buf);
+
+ val.bv_val = buf;
+ val.bv_len = strlen(buf);
+ if (val.bv_len)
+ slapi_entry_attr_replace(e, config->config_name, vals);
+ }
+
+ *returncode = LDAP_SUCCESS;
+ return SLAPI_DSE_CALLBACK_OK;
+}
+
+/*
+** Ooops!!! The backend instance is beeing deleted
+*/
+
+int cb_instance_delete_config_callback(Slapi_PBlock *pb, Slapi_Entry* e, Slapi_Entry* e2,
+ int *returncode, char *returntext, void *arg) {
+
+ cb_backend_instance * inst = (cb_backend_instance *) arg;
+ int rc;
+ Slapi_Entry * anEntry=NULL;
+ Slapi_DN * aDn;
+
+ CB_ASSERT( inst!=NULL );
+
+ /* notify the front-end */
+ slapi_mtn_be_stopping(inst->inst_be);
+
+ /* Now it is safe to stop */
+ /* No pending op */
+
+
+ /* unregister callbacks */
+ slapi_config_remove_callback(SLAPI_OPERATION_SEARCH, DSE_FLAG_PREOP, inst->configDn,
+ LDAP_SCOPE_BASE, "(objectclass=*)", cb_instance_search_config_callback);
+
+ slapi_config_remove_callback(SLAPI_OPERATION_DELETE, DSE_FLAG_POSTOP, inst->configDn,
+ LDAP_SCOPE_BASE, "(objectclass=*)", cb_instance_delete_config_callback);
+
+ slapi_config_remove_callback(SLAPI_OPERATION_MODIFY, DSE_FLAG_PREOP, inst->configDn,
+ LDAP_SCOPE_BASE, "(objectclass=*)", cb_instance_modify_config_check_callback);
+ slapi_config_remove_callback(SLAPI_OPERATION_MODIFY, DSE_FLAG_POSTOP, inst->configDn,
+ LDAP_SCOPE_BASE, "(objectclass=*)", cb_instance_modify_config_callback);
+
+ /* At this point, the monitor entry should have been removed */
+ /* If not, manually call delete callback */
+
+ aDn = slapi_sdn_new_dn_byref(inst->monitorDn);
+ if ( LDAP_SUCCESS==(slapi_search_internal_get_entry(aDn,NULL, &anEntry,inst->backend_type->identity))) {
+ cb_delete_monitor_callback( NULL, anEntry, NULL, &rc , NULL, inst );
+ if (anEntry)
+ slapi_entry_free(anEntry);
+ }
+ slapi_sdn_done(aDn);
+ slapi_sdn_free(&aDn);
+
+ /* free resources */
+ cb_close_conn_pool(inst->bind_pool);
+ cb_close_conn_pool(inst->pool);
+ slapi_be_free(&(inst->inst_be));
+ cb_instance_free(inst);
+
+ return SLAPI_DSE_CALLBACK_OK;
+}
+
+static void cb_instance_add_monitor_later(time_t when, void *arg) {
+
+ cb_backend_instance * inst = (cb_backend_instance *) arg;
+
+ if ( inst != NULL )
+ {
+ PR_RWLock_Rlock(inst->rwl_config_lock);
+
+ /* create the monitor entry if it is not there yet */
+ if (LDAP_SUCCESS == cb_config_add_dse_entries(inst->backend_type, cb_skeleton_entries,
+ inst->inst_name,CB_PLUGIN_NAME, NULL))
+ {
+
+ /* add monitor callbacks */
+ slapi_config_register_callback(SLAPI_OPERATION_SEARCH, DSE_FLAG_PREOP, inst->monitorDn, LDAP_SCOPE_BASE,
+ "(objectclass=*)", cb_search_monitor_callback, (void *) inst);
+
+ slapi_config_register_callback(SLAPI_OPERATION_MODIFY, DSE_FLAG_PREOP, inst->monitorDn, LDAP_SCOPE_BASE,
+ "(objectclass=*)", cb_dont_allow_that, (void *) NULL);
+
+ slapi_config_register_callback(SLAPI_OPERATION_DELETE, DSE_FLAG_PREOP , inst->monitorDn, LDAP_SCOPE_BASE,
+ "(objectclass=*)", cb_delete_monitor_callback, (void *) inst);
+ }
+ PR_RWLock_Unlock(inst->rwl_config_lock);
+ }
+}
+
+
+int cb_instance_add_config_check_callback(Slapi_PBlock *pb, Slapi_Entry* e, Slapi_Entry* e2,
+ int *returncode, char *returntext, void *arg) {
+
+ int rc=LDAP_SUCCESS;
+ cb_backend_instance *inst;
+ cb_backend *cb=(cb_backend *) arg;
+ Slapi_Attr *attr = NULL;
+ Slapi_Value *sval;
+ const struct berval *attrValue;
+ char *instname=NULL;
+
+ if (returntext)
+ returntext[0]='\0';
+
+ /* Basic entry check */
+ if ( 0 == slapi_entry_attr_find( e, CB_CONFIG_INSTNAME, &attr )) {
+ slapi_attr_first_value(attr, &sval);
+ attrValue = slapi_value_get_berval(sval);
+ instname=attrValue->bv_val;
+ }
+ if ( instname == NULL ) {
+ slapi_log_error( SLAPI_LOG_PLUGIN, CB_PLUGIN_SUBSYSTEM,
+ "Malformed backend instance (<%s> missing)>\n", CB_CONFIG_INSTNAME);
+ *returncode = LDAP_LOCAL_ERROR;
+ return SLAPI_DSE_CALLBACK_ERROR;
+ }
+
+ /* Allocate a new backend internal data structure */
+ inst = cb_instance_alloc(cb,instname,slapi_entry_get_dn(e));
+
+ /* build the backend instance from the default hardcoded conf, */
+ /* the default instance config and the specific entry specified */
+ if ((rc=cb_build_backend_instance_config(inst,e,0))
+ != LDAP_SUCCESS) {
+ slapi_log_error( SLAPI_LOG_FATAL, CB_PLUGIN_SUBSYSTEM,
+ "Can't instantiate chaining backend instance %s.\n",inst->inst_name);
+ *returncode=rc;
+ cb_instance_free(inst);
+ return SLAPI_DSE_CALLBACK_ERROR;
+ }
+
+ /* Free the dummy instance */
+ *returncode=rc;
+ cb_instance_free(inst);
+
+ return SLAPI_DSE_CALLBACK_OK;
+}
+
+
+/* Create the default instance config from hard-coded values */
+/*
+** Initialize the backend instance with the config entry
+** passed in arguments.
+** <arg> : (cb_backend *)
+*/
+
+int cb_instance_add_config_callback(Slapi_PBlock *pb, Slapi_Entry* e, Slapi_Entry* e2,
+ int *returncode, char *returntext, void *arg) {
+
+ int rc=LDAP_SUCCESS;
+ cb_backend_instance *inst;
+ cb_backend *cb=(cb_backend *) arg;
+ Slapi_Attr *attr = NULL;
+ Slapi_Value *sval;
+ const struct berval *attrValue;
+ char *instname=NULL;
+
+ if (returntext)
+ returntext[0]='\0';
+
+ /* Basic entry check */
+ if ( 0 == slapi_entry_attr_find( e, CB_CONFIG_INSTNAME, &attr )) {
+ slapi_attr_first_value(attr, &sval);
+ attrValue = slapi_value_get_berval(sval);
+ instname=attrValue->bv_val;
+ }
+ if ( instname == NULL ) {
+ slapi_log_error( SLAPI_LOG_PLUGIN, CB_PLUGIN_SUBSYSTEM,
+ "Malformed backend instance (<%s> missing)>\n", CB_CONFIG_INSTNAME);
+ *returncode = LDAP_LOCAL_ERROR;
+ return SLAPI_DSE_CALLBACK_ERROR;
+ }
+
+ /* Allocate a new backend internal data structure */
+ inst = cb_instance_alloc(cb,instname,slapi_entry_get_dn(e));
+
+ /* build the backend instance from the default hardcoded conf, */
+ /* the default instance config and the specific entry specified */
+ if ((rc=cb_build_backend_instance_config(inst,e,0))
+ != LDAP_SUCCESS) {
+ slapi_log_error( SLAPI_LOG_FATAL, CB_PLUGIN_SUBSYSTEM,
+ "Can't instantiate chaining backend instance %s.\n",inst->inst_name);
+ *returncode=rc;
+ cb_instance_free(inst);
+ return SLAPI_DSE_CALLBACK_ERROR;
+ }
+
+ /* Instantiate a Slapi_Backend if necessary */
+ if (!inst->isconfigured) {
+
+ Slapi_PBlock *aPb=NULL;
+
+ inst->inst_be = slapi_be_new(CB_CHAINING_BACKEND_TYPE,slapi_ch_strdup(inst->inst_name),0,0);
+ aPb=slapi_pblock_new();
+ slapi_pblock_set(aPb, SLAPI_PLUGIN, inst->backend_type->plugin);
+ slapi_be_setentrypoint(inst->inst_be,0,(void *)NULL,aPb);
+ slapi_be_set_instance_info(inst->inst_be,inst);
+ slapi_pblock_set(aPb, SLAPI_PLUGIN, NULL);
+ slapi_pblock_destroy(aPb);
+ }
+
+ cb_build_backend_instance_config(inst,e,1);
+
+ /* kexcoff: the order of the following calls is very important to prevent the deletion of the
+ instance to happen before the creation of the monitor part of the config.
+ However, not sure it solves all the situations, but at least it is worth to maintain
+ this order. */
+
+ if (!inst->isconfigured)
+ {
+ /* Add monitor entry and callback on it
+ * called from an add...
+ * we can't call recursively into the DSE to do more adds, they'll
+ * silently fail. instead, schedule the adds to happen in 1 second.
+ */
+ inst->eq_ctx = slapi_eq_once(cb_instance_add_monitor_later, (void *)inst, time(NULL)+1);
+ }
+
+ /* Get the list of operational attrs defined in the schema */
+ /* see cb_search file for a reason for that */
+
+ inst->every_attribute=slapi_schema_list_attribute_names(SLAPI_ATTR_FLAG_OPATTR);
+ charray_add(&inst->every_attribute,slapi_ch_strdup(LDAP_ALL_USER_ATTRS));
+
+ if (!inst->isconfigured)
+ {
+ slapi_config_register_callback(SLAPI_OPERATION_MODIFY, DSE_FLAG_PREOP, inst->configDn,
+ LDAP_SCOPE_BASE,"(objectclass=*)",cb_instance_modify_config_check_callback, (void *) inst);
+ slapi_config_register_callback(SLAPI_OPERATION_MODIFY, DSE_FLAG_POSTOP, inst->configDn,
+ LDAP_SCOPE_BASE,"(objectclass=*)",cb_instance_modify_config_callback, (void *) inst);
+
+ slapi_config_register_callback(SLAPI_OPERATION_SEARCH, DSE_FLAG_PREOP, inst->configDn,
+ LDAP_SCOPE_BASE,"(objectclass=*)", cb_instance_search_config_callback, (void *) inst);
+
+ /* allow deletion otherwise impossible to remote a backend instance */
+ /* dynamically... */
+ slapi_config_register_callback(SLAPI_OPERATION_DELETE, DSE_FLAG_POSTOP, inst->configDn,
+ LDAP_SCOPE_BASE,"(objectclass=*)", cb_instance_delete_config_callback, (void *) inst);
+ }
+
+ /* Notify the front-end */
+ /* After the call below, we can receive ops */
+ slapi_mtn_be_started(inst->inst_be);
+
+ inst->isconfigured=1;
+ return SLAPI_DSE_CALLBACK_OK;
+}
+
+
+/* Create the default instance config from hard-coded values */
+
+int cb_create_default_backend_instance_config(cb_backend * cb) {
+
+
+ int rc;
+ cb_backend_instance *dummy;
+ Slapi_Entry *e=slapi_entry_alloc();
+ char defaultDn[CB_BUFSIZE];
+ char *olddn;
+ struct berval val;
+ struct berval *vals[2];
+ Slapi_PBlock *pb;
+
+ dummy = cb_instance_alloc(cb, "dummy", "o=dummy");
+ cb_instance_config_set_default(dummy);
+ cb_instance_search_config_callback(NULL,e,NULL, &rc, NULL,(void *) dummy);
+
+
+ /* set right dn and objectclass */
+
+ sprintf(defaultDn,"cn=default instance config,%s",cb->pluginDN);
+ olddn = slapi_entry_get_dn(e);
+ slapi_ch_free((void **) &olddn);
+
+ slapi_entry_set_dn(e,slapi_ch_strdup(defaultDn));
+
+ vals[0] = &val;
+ vals[1] = NULL;
+
+ val.bv_val = "top";
+ val.bv_len = strlen( val.bv_val );
+ slapi_entry_attr_replace( e, "objectclass", (struct berval **)vals );
+ val.bv_val = CB_CONFIG_EXTENSIBLEOCL;
+ val.bv_len = strlen( val.bv_val );
+ slapi_entry_attr_merge( e, "objectclass", (struct berval **)vals );
+ val.bv_val = "default instance config";
+ val.bv_len = strlen( val.bv_val );
+ slapi_entry_attr_replace( e, "cn", (struct berval **)vals );
+
+ /* create entry */
+ pb = slapi_pblock_new();
+ slapi_add_entry_internal_set_pb(pb, e, NULL, cb->identity, 0);
+ slapi_add_internal_pb(pb);
+ slapi_pblock_get(pb, SLAPI_PLUGIN_INTOP_RESULT, &rc);
+ if ( LDAP_SUCCESS != rc ) {
+ slapi_log_error(SLAPI_LOG_PLUGIN, CB_PLUGIN_SUBSYSTEM,
+ "Add %s failed (%s)\n",defaultDn,ldap_err2string(rc));
+ }
+
+ slapi_pblock_destroy(pb);
+ /* cleanup */
+ cb_instance_free(dummy);
+ /* BEWARE: entry is consummed */
+ return rc;
+}
+
+/* Extract backend instance configuration from the LDAP entry */
+
+int cb_build_backend_instance_config(cb_backend_instance *inst, Slapi_Entry * conf, int apply) {
+
+ cb_backend *cb = inst->backend_type;
+ Slapi_PBlock *default_pb;
+ Slapi_Entry **default_entries = NULL;
+ Slapi_Entry *default_conf=NULL;
+ int default_res, rc;
+ char defaultDn[CB_BUFSIZE];
+ cb_backend_instance * current_inst;
+
+ rc=LDAP_SUCCESS;
+
+ if (apply)
+ current_inst=inst;
+ else
+ current_inst=cb_instance_alloc(cb,inst->inst_name,"cn=dummy");
+
+ /* set default configuration */
+ cb_instance_config_set_default(current_inst);
+
+ /* 2: Overwrite values present in the default instance config */
+
+ sprintf(defaultDn,"cn=default instance config,%s",cb->pluginDN);
+
+ default_pb = slapi_pblock_new();
+ slapi_search_internal_set_pb(default_pb, defaultDn, LDAP_SCOPE_BASE,
+ "objectclass=*", NULL, 0, NULL, NULL, cb->identity, 0);
+ slapi_search_internal_pb (default_pb);
+ slapi_pblock_get(default_pb, SLAPI_PLUGIN_INTOP_RESULT, &default_res);
+ if ( LDAP_SUCCESS == default_res ) {
+ slapi_pblock_get(default_pb, SLAPI_PLUGIN_INTOP_SEARCH_ENTRIES, &default_entries);
+ if (default_entries && default_entries[0] ) {
+
+ struct berval val;
+ struct berval *vals[2];
+ vals[0] = &val;
+ vals[1] = NULL;
+ default_conf=default_entries[0];
+
+ /* hack: add a dummy url (mandatory) to avoid error */
+ /* will be overwritten by the one in conf entry */
+ val.bv_val = "ldap://localhost/";
+ val.bv_len = strlen( val.bv_val );
+ slapi_entry_attr_replace( default_conf, CB_CONFIG_HOSTURL, (struct berval **)vals );
+
+ rc=cb_instance_config_initialize(current_inst,default_conf,CB_CONFIG_PHASE_STARTUP,1);
+ }
+ }
+ slapi_free_search_results_internal(default_pb);
+ slapi_pblock_destroy(default_pb);
+
+ if (rc == LDAP_SUCCESS)
+ rc=cb_instance_config_initialize(current_inst,conf,CB_CONFIG_PHASE_STARTUP,1);
+
+ if (!apply)
+ cb_instance_free(current_inst);
+
+ return rc;
+}
diff --git a/ldap/servers/plugins/chainingdb/cb_modify.c b/ldap/servers/plugins/chainingdb/cb_modify.c
new file mode 100644
index 00000000..51aed6a3
--- /dev/null
+++ b/ldap/servers/plugins/chainingdb/cb_modify.c
@@ -0,0 +1,244 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+#include "cb.h"
+
+static void cb_remove_illegal_mods(cb_backend_instance * inst, LDAPMod **mods);
+
+/*
+ * Perform a modify operation
+ *
+ * Returns:
+ * 0 - success
+ * <0 - fail
+ *
+ */
+
+int
+chaining_back_modify ( Slapi_PBlock *pb )
+{
+
+ Slapi_Backend *be;
+ cb_backend_instance *cb;
+ LDAPControl **ctrls, **serverctrls;
+ int rc,parse_rc,msgid,i;
+ LDAP *ld=NULL;
+ char **referrals=NULL;
+ LDAPMod ** mods;
+ LDAPMessage * res;
+ char *dn,* matched_msg, *error_msg;
+ char *cnxerrbuf=NULL;
+ time_t endtime;
+ cb_outgoing_conn *cnx;
+
+ if ( LDAP_SUCCESS != (rc=cb_forward_operation(pb) )) {
+ cb_send_ldap_result( pb, rc, NULL, "Chaining forbidden", 0, NULL );
+ return -1;
+ }
+
+ slapi_pblock_get( pb, SLAPI_BACKEND, &be );
+ cb = cb_get_instance(be);
+
+ cb_update_monitor_info(pb,cb,SLAPI_OPERATION_MODIFY);
+
+ /* Check wether the chaining BE is available or not */
+ if ( cb_check_availability( cb, pb ) == FARMSERVER_UNAVAILABLE ){
+ return -1;
+ }
+
+ slapi_pblock_get( pb, SLAPI_MODIFY_TARGET, &dn );
+
+ if (cb_debug_on()) {
+ slapi_log_error( SLAPI_LOG_PLUGIN, CB_PLUGIN_SUBSYSTEM,"modify: target:<%s>\n",dn);
+ }
+
+
+ ctrls=serverctrls=NULL;
+ slapi_pblock_get( pb, SLAPI_MODIFY_MODS, &mods );
+ slapi_pblock_get( pb, SLAPI_REQCONTROLS, &ctrls );
+
+ /* Check acls */
+
+ if ( cb->local_acl && !cb->associated_be_is_disabled ) {
+ char * errbuf=NULL;
+ Slapi_Entry *te = slapi_entry_alloc();
+ slapi_entry_set_dn(te,slapi_ch_strdup(dn));
+ rc = slapi_acl_check_mods( pb, te, mods, &errbuf);
+ slapi_entry_free(te);
+
+ if ( rc != LDAP_SUCCESS ) {
+ cb_send_ldap_result( pb, rc, NULL, errbuf, 0, NULL );
+ slapi_ch_free((void **)&errbuf);
+ return -1;
+ }
+ }
+
+
+ /* Grab a connection handle */
+ if ((rc = cb_get_connection(cb->pool,&ld,&cnx,NULL,&cnxerrbuf)) != LDAP_SUCCESS) {
+ cb_send_ldap_result( pb, LDAP_OPERATIONS_ERROR, NULL, cnxerrbuf, 0, NULL);
+ slapi_ch_free((void **)&cnxerrbuf);
+ /* ping the farm. If the farm is unreachable, we increment the counter */
+ cb_ping_farm(cb,NULL,0);
+ return -1;
+ }
+
+ /* Control management */
+ if ( (rc = cb_update_controls( pb,ld,&ctrls,CB_UPDATE_CONTROLS_ADDAUTH )) != LDAP_SUCCESS ) {
+ cb_send_ldap_result( pb, rc, NULL,NULL, 0, NULL);
+ cb_release_op_connection(cb->pool,ld,CB_LDAP_CONN_ERROR(rc));
+ /* Don't free mods here: are freed at the do_modify level */
+ return -1;
+ }
+
+ if ( slapi_op_abandoned( pb )) {
+ cb_release_op_connection(cb->pool,ld,0);
+ /* Don't free mods here: are freed at the do_modify level */
+ if ( NULL != ctrls)
+ ldap_controls_free(ctrls);
+ return -1;
+ }
+
+ /* Remove illegal attributes from the mods */
+ cb_remove_illegal_mods(cb,mods);
+
+ /* heart-beat management */
+ if (cb->max_idle_time>0)
+ endtime=current_time() + cb->max_idle_time;
+
+ /* Send LDAP operation to the remote host */
+ rc = ldap_modify_ext( ld, dn, mods, ctrls, NULL, &msgid );
+ if ( NULL != ctrls)
+ ldap_controls_free(ctrls);
+
+ if ( rc != LDAP_SUCCESS ) {
+ cb_send_ldap_result( pb, LDAP_OPERATIONS_ERROR, NULL, ldap_err2string(rc), 0, NULL);
+ cb_release_op_connection(cb->pool,ld,CB_LDAP_CONN_ERROR(rc));
+ return -1;
+ }
+
+ while ( 1 ) {
+
+ if (cb_check_forward_abandon(cb,pb,ld,msgid)) {
+ /* connection handle released */
+ return -1;
+ }
+
+ rc = ldap_result( ld, msgid, 0, &cb->abandon_timeout, &res );
+ switch ( rc ) {
+ case -1:
+ cb_send_ldap_result( pb, LDAP_OPERATIONS_ERROR, NULL,
+ ldap_err2string(rc), 0, NULL);
+ cb_release_op_connection(cb->pool,ld,CB_LDAP_CONN_ERROR(rc));
+ if (res)
+ ldap_msgfree(res);
+ return -1;
+ case 0:
+ if ((rc=cb_ping_farm(cb,cnx,endtime)) != LDAP_SUCCESS) {
+
+ /* does not respond. give up and return a*/
+ /* error to the client. */
+
+ /*cb_send_ldap_result(pb,LDAP_OPERATIONS_ERROR, NULL,
+ ldap_err2string(rc), 0, NULL);*/
+ cb_send_ldap_result(pb,LDAP_OPERATIONS_ERROR, NULL, "FARM SERVER TEMPORARY UNAVAILABLE", 0, NULL);
+ cb_release_op_connection(cb->pool,ld,CB_LDAP_CONN_ERROR(rc));
+ if (res)
+ ldap_msgfree(res);
+ return -1;
+ }
+#ifdef CB_YIELD
+ DS_Sleep(PR_INTERVAL_NO_WAIT);
+#endif
+ break;
+ default:
+
+ matched_msg=error_msg=NULL;
+ serverctrls=NULL;
+ parse_rc = ldap_parse_result( ld, res, &rc, &matched_msg,
+ &error_msg, &referrals, &serverctrls, 1 );
+ if ( parse_rc != LDAP_SUCCESS ) {
+ cb_send_ldap_result( pb, LDAP_OPERATIONS_ERROR, NULL,
+ ldap_err2string(parse_rc), 0, NULL);
+ cb_release_op_connection(cb->pool,ld,CB_LDAP_CONN_ERROR(parse_rc));
+ slapi_ch_free((void **)&matched_msg);
+ slapi_ch_free((void **)&error_msg);
+ if (serverctrls)
+ ldap_controls_free(serverctrls);
+ /* jarnou: free referrals */
+ if (referrals)
+ charray_free(referrals);
+ return -1;
+ }
+
+ if ( rc != LDAP_SUCCESS ) {
+ struct berval ** refs = referrals2berval(referrals);
+ cb_send_ldap_result( pb, rc, matched_msg, error_msg, 0, refs);
+ cb_release_op_connection(cb->pool,ld,CB_LDAP_CONN_ERROR(rc));
+ slapi_ch_free((void **)&matched_msg);
+ slapi_ch_free((void **)&error_msg);
+ if (refs)
+ ber_bvecfree(refs);
+ if (referrals)
+ charray_free(referrals);
+ if (serverctrls)
+ ldap_controls_free(serverctrls);
+ return -1;
+ }
+
+ cb_release_op_connection(cb->pool,ld,0);
+
+ /* Add control response sent by the farm server */
+
+ for (i=0; serverctrls && serverctrls[i];i++)
+ slapi_pblock_set( pb, SLAPI_ADD_RESCONTROL, serverctrls[i]);
+ /* SLAPI_ADD_RESCONTROL dups controls */
+ if (serverctrls)
+ ldap_controls_free(serverctrls);
+ /* jarnou: free matched_msg, error_msg, and referrals if necessary */
+ slapi_ch_free((void **)&matched_msg);
+ slapi_ch_free((void **)&error_msg);
+ if (referrals)
+ charray_free(referrals);
+ cb_send_ldap_result( pb, LDAP_SUCCESS, NULL, NULL, 0, NULL );
+ return 0;
+ }
+ }
+
+ /* Never reached */
+ /* return 0; */
+}
+
+/* Function removes mods which are not allowed over-the-wire */
+static void
+cb_remove_illegal_mods(cb_backend_instance *inst, LDAPMod **mods)
+{
+ int i, j;
+ LDAPMod *tmp;
+
+ if ( inst->illegal_attributes != NULL ) { /* Unlikely to happen */
+
+ PR_RWLock_Wlock(inst->rwl_config_lock);
+
+ for (j=0; inst->illegal_attributes[j]; j++) {
+ for ( i = 0; mods[i] != NULL; i++ ) {
+ if (slapi_attr_types_equivalent(inst->illegal_attributes[j],mods[i]->mod_type)) {
+ tmp = mods[i];
+ for ( j = i; mods[j] != NULL; j++ ) {
+ mods[j] = mods[j + 1];
+ }
+ slapi_ch_free( (void**)&(tmp->mod_type) );
+ if ( tmp->mod_bvalues != NULL ) {
+ ber_bvecfree( tmp->mod_bvalues );
+ }
+ slapi_ch_free( (void**)&tmp );
+ i--;
+ }
+ }
+ }
+
+ PR_RWLock_Unlock(inst->rwl_config_lock);
+ }
+}
diff --git a/ldap/servers/plugins/chainingdb/cb_modrdn.c b/ldap/servers/plugins/chainingdb/cb_modrdn.c
new file mode 100644
index 00000000..e6b5dadb
--- /dev/null
+++ b/ldap/servers/plugins/chainingdb/cb_modrdn.c
@@ -0,0 +1,239 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+#include "cb.h"
+
+/*
+ * Perform a modrdn operation
+ *
+ * Returns:
+ * 0 - success
+ * <0 - fail
+ *
+ */
+
+int
+chaining_back_modrdn ( Slapi_PBlock *pb )
+{
+
+ Slapi_Backend * be;
+ cb_backend_instance *cb;
+ LDAPControl **ctrls, **serverctrls;
+ int rc,parse_rc,msgid,i;
+ LDAP *ld=NULL;
+ char **referrals=NULL;
+ LDAPMessage * res;
+ char * matched_msg, *error_msg,* pdn, *newdn, *dn;
+ int deleteoldrdn=0;
+ char * newsuperior, *newrdn;
+ char * cnxerrbuf=NULL;
+ time_t endtime;
+ cb_outgoing_conn * cnx;
+
+ if ( LDAP_SUCCESS != (rc=cb_forward_operation(pb) )) {
+ cb_send_ldap_result( pb, rc, NULL, "Chaining forbidden", 0, NULL );
+ return -1;
+ }
+
+ slapi_pblock_get( pb, SLAPI_BACKEND, &be );
+ cb = cb_get_instance(be);
+
+ cb_update_monitor_info(pb,cb,SLAPI_OPERATION_MODRDN);
+
+ /* Check wether the chaining BE is available or not */
+ if ( cb_check_availability( cb, pb ) == FARMSERVER_UNAVAILABLE ){
+ return -1;
+ }
+
+ newsuperior=newdn=newrdn=dn=NULL;
+ slapi_pblock_get( pb, SLAPI_MODRDN_TARGET, &dn );
+ slapi_pblock_get( pb, SLAPI_MODRDN_NEWRDN, &newrdn );
+ slapi_pblock_get( pb, SLAPI_MODRDN_NEWSUPERIOR, &newsuperior );
+ slapi_pblock_get( pb, SLAPI_MODRDN_DELOLDRDN, &deleteoldrdn );
+
+ /*
+ * Construct the new dn
+ */
+
+ dn = slapi_dn_normalize_case(dn);
+ if ( (pdn = slapi_dn_parent( dn )) != NULL ) {
+ /* parent + rdn + separator(s) + null */
+ newdn = (char *) slapi_ch_malloc( strlen( pdn ) + strlen( newrdn ) + 3 );
+ strcpy( newdn, newrdn );
+ strcat( newdn, "," );
+ strcat( newdn, pdn );
+
+ slapi_ch_free((void **)&pdn );
+
+ } else {
+ newdn = slapi_ch_strdup( newrdn );
+ }
+
+ /*
+ * Make sure the current backend is managing
+ * the new dn. We won't support moving entries
+ * across backend this way.
+ * Done in the front-end.
+ */
+
+ slapi_ch_free((void **)&newdn);
+
+ if (cb->local_acl && !cb->associated_be_is_disabled) {
+ /*
+ * Check local acls
+ * Keep in mind We don't have the entry for acl evaluation
+ */
+
+ char * errbuf=NULL;
+ Slapi_Entry *te = slapi_entry_alloc();
+ slapi_entry_set_dn(te,slapi_ch_strdup(dn));
+ rc = cb_access_allowed (pb, te, NULL, NULL, SLAPI_ACL_WRITE,&errbuf);
+ slapi_entry_free(te);
+
+ if ( rc != LDAP_SUCCESS ) {
+ cb_send_ldap_result( pb, rc, NULL, errbuf, 0, NULL );
+ slapi_ch_free((void **)&errbuf);
+ return -1;
+ }
+ }
+
+ /*
+ * Grab a connection handle
+ */
+
+ if ((rc = cb_get_connection(cb->pool,&ld,&cnx,NULL,&cnxerrbuf)) != LDAP_SUCCESS) {
+ cb_send_ldap_result( pb, LDAP_OPERATIONS_ERROR, NULL, cnxerrbuf, 0, NULL);
+ slapi_ch_free((void **)&cnxerrbuf);
+ /* ping the farm. If the farm is unreachable, we increment the counter */
+ cb_ping_farm(cb,NULL,0);
+ return -1;
+ }
+
+ /*
+ * Control management
+ */
+
+ if ( (rc = cb_update_controls( pb,ld,&ctrls,CB_UPDATE_CONTROLS_ADDAUTH )) != LDAP_SUCCESS ) {
+ cb_send_ldap_result( pb, rc, NULL,NULL, 0, NULL);
+ cb_release_op_connection(cb->pool,ld,CB_LDAP_CONN_ERROR(rc));
+ return -1;
+ }
+
+
+ if ( slapi_op_abandoned( pb )) {
+ cb_release_op_connection(cb->pool,ld,0);
+ if ( NULL != ctrls)
+ ldap_controls_free(ctrls);
+ return -1;
+ }
+
+ /* heart-beat management */
+ if (cb->max_idle_time>0)
+ endtime=current_time() + cb->max_idle_time;
+
+ /*
+ * Send LDAP operation to the remote host
+ */
+
+ rc = ldap_rename ( ld,dn,newrdn,newsuperior,deleteoldrdn,ctrls,NULL,&msgid);
+
+ if ( NULL != ctrls)
+ ldap_controls_free(ctrls);
+ if ( rc != LDAP_SUCCESS ) {
+ cb_send_ldap_result( pb, LDAP_OPERATIONS_ERROR, NULL,
+ ldap_err2string(rc), 0, NULL);
+ cb_release_op_connection(cb->pool,ld,CB_LDAP_CONN_ERROR(rc));
+ return -1;
+ }
+
+ while ( 1 ) {
+
+ if (cb_check_forward_abandon(cb,pb,ld,msgid)) {
+ return -1;
+ }
+
+ rc = ldap_result( ld, msgid, 0, &cb->abandon_timeout, &res );
+ switch ( rc ) {
+ case -1:
+ cb_send_ldap_result( pb, LDAP_OPERATIONS_ERROR, NULL,
+ ldap_err2string(rc), 0, NULL);
+ cb_release_op_connection(cb->pool,ld,CB_LDAP_CONN_ERROR(rc));
+ if (res)
+ ldap_msgfree(res);
+ return -1;
+ case 0:
+ if ((rc=cb_ping_farm(cb,cnx,endtime)) != LDAP_SUCCESS) {
+
+ /* does not respond. give up and return a*/
+ /* error to the client. */
+
+ /*cb_send_ldap_result(pb,LDAP_OPERATIONS_ERROR, NULL,
+ ldap_err2string(rc), 0, NULL);*/
+ cb_send_ldap_result(pb,LDAP_OPERATIONS_ERROR, NULL, "FARM SERVER TEMPORARY UNAVAILABLE", 0, NULL);
+ cb_release_op_connection(cb->pool,ld,CB_LDAP_CONN_ERROR(rc));
+ if (res)
+ ldap_msgfree(res);
+ return -1;
+ }
+#ifdef CB_YIELD
+ DS_Sleep(PR_INTERVAL_NO_WAIT);
+#endif
+ break;
+ default:
+ matched_msg=error_msg=NULL;
+ parse_rc = ldap_parse_result( ld, res, &rc, &matched_msg,
+ &error_msg, &referrals, &serverctrls, 1 );
+
+ if ( parse_rc != LDAP_SUCCESS ) {
+ cb_send_ldap_result( pb, LDAP_OPERATIONS_ERROR, NULL,
+ ldap_err2string(parse_rc), 0, NULL);
+ cb_release_op_connection(cb->pool,ld,CB_LDAP_CONN_ERROR(parse_rc));
+ slapi_ch_free((void **)&matched_msg);
+ slapi_ch_free((void **)&error_msg);
+ if (serverctrls)
+ ldap_controls_free(serverctrls);
+ /* jarnou: free referrals */
+ if (referrals)
+ charray_free(referrals);
+ return -1;
+ }
+
+ if ( rc != LDAP_SUCCESS ) {
+ struct berval ** refs = referrals2berval(referrals);
+
+ cb_send_ldap_result( pb, rc, matched_msg, error_msg, 0, refs);
+ cb_release_op_connection(cb->pool,ld,CB_LDAP_CONN_ERROR(rc));
+ slapi_ch_free((void **)&matched_msg);
+ slapi_ch_free((void **)&error_msg);
+ if (refs)
+ ber_bvecfree(refs);
+ if (referrals)
+ charray_free(referrals);
+ if (serverctrls)
+ ldap_controls_free(serverctrls);
+ return -1;
+ }
+
+ cb_release_op_connection(cb->pool,ld,0);
+
+ /* Add control response sent by the farm server */
+
+ for (i=0; serverctrls && serverctrls[i];i++)
+ slapi_pblock_set( pb, SLAPI_ADD_RESCONTROL, serverctrls[i]);
+ if (serverctrls)
+ ldap_controls_free(serverctrls);
+ /* jarnou: free matched_msg, error_msg, and referrals if necessary */
+ slapi_ch_free((void **)&matched_msg);
+ slapi_ch_free((void **)&error_msg);
+ if (referrals)
+ charray_free(referrals);
+ cb_send_ldap_result( pb, LDAP_SUCCESS, NULL, NULL, 0, NULL );
+ return 0;
+ }
+ }
+
+ /* Never reached */
+ /* return 0; */
+}
diff --git a/ldap/servers/plugins/chainingdb/cb_monitor.c b/ldap/servers/plugins/chainingdb/cb_monitor.c
new file mode 100644
index 00000000..11ef3bda
--- /dev/null
+++ b/ldap/servers/plugins/chainingdb/cb_monitor.c
@@ -0,0 +1,228 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+#include "cb.h"
+
+extern cb_instance_config_info cb_the_instance_config[];
+/*
+** Chaining backend instance monitor function
+** This function wraps up backend specific monitoring information
+** and return it to the client as an LDAP entry.
+** This function is usually called upon receipt of the monitor
+** dn for this backend
+**
+** Monitor information:
+**
+** Database misc
+** database
+**
+** Number of hits for each operation
+** addopcount
+** modifyopcount
+** deleteopcount
+** modrdnopcount
+** compareopcount
+** searchsubtreeopcount
+** searchonelevelopcount
+** searchbaseopcount
+** bindopcount
+** unbindopcount
+** abandonopcount
+**
+** Outgoing connections
+** outgoingopconnections
+** outgoingbindconnections
+**
+*/
+
+int
+cb_search_monitor_callback(Slapi_PBlock * pb, Slapi_Entry * e, Slapi_Entry * entryAfter, int * returnCode, char * returnText, void * arg)
+{
+
+ char buf[CB_BUFSIZE];
+ struct berval val;
+ struct berval *vals[2];
+ int deletecount,addcount,modifycount,modrdncount,searchbasecount,searchonelevelcount;
+ int searchsubtreecount,abandoncount,bindcount,unbindcount,comparecount;
+ int outgoingconn, outgoingbindconn;
+ cb_backend_instance *inst = (cb_backend_instance *)arg;
+
+ /* First make sure the backend instance is configured */
+ /* If not, don't return anything */
+
+ PR_RWLock_Rlock(inst->rwl_config_lock);
+ if (!inst->isconfigured) {
+ *returnCode= LDAP_NO_SUCH_OBJECT;
+ PR_RWLock_Unlock(inst->rwl_config_lock);
+ return SLAPI_DSE_CALLBACK_ERROR;
+ }
+ PR_RWLock_Unlock(inst->rwl_config_lock);
+
+ vals[0] = &val;
+ vals[1] = NULL;
+
+ slapi_lock_mutex(inst->monitor.mutex);
+
+ addcount =inst->monitor.addcount;
+ deletecount =inst->monitor.deletecount;
+ modifycount =inst->monitor.modifycount;
+ modrdncount =inst->monitor.modrdncount;
+ searchbasecount =inst->monitor.searchbasecount;
+ searchonelevelcount =inst->monitor.searchonelevelcount;
+ searchsubtreecount =inst->monitor.searchsubtreecount;
+ abandoncount =inst->monitor.abandoncount;
+ bindcount =inst->monitor.bindcount;
+ unbindcount =inst->monitor.unbindcount;
+ comparecount =inst->monitor.comparecount;
+
+ slapi_unlock_mutex(inst->monitor.mutex);
+
+ /*
+ ** Get connection information
+ */
+
+ slapi_lock_mutex(inst->pool->conn.conn_list_mutex);
+ outgoingconn= inst->pool->conn.conn_list_count;
+ slapi_unlock_mutex(inst->pool->conn.conn_list_mutex);
+
+ slapi_lock_mutex(inst->bind_pool->conn.conn_list_mutex);
+ outgoingbindconn= inst->bind_pool->conn.conn_list_count;
+ slapi_unlock_mutex(inst->bind_pool->conn.conn_list_mutex);
+
+ sprintf( buf, "%lu", addcount );
+ val.bv_val = buf;
+ val.bv_len = strlen( buf );
+ slapi_entry_attr_replace( e, CB_MONITOR_ADDCOUNT, ( struct berval **)vals );
+
+ sprintf( buf, "%lu", deletecount );
+ val.bv_val = buf;
+ val.bv_len = strlen( buf );
+ slapi_entry_attr_replace( e, CB_MONITOR_DELETECOUNT, ( struct berval **)vals );
+
+ sprintf( buf, "%lu", modifycount );
+ val.bv_val = buf;
+ val.bv_len = strlen( buf );
+ slapi_entry_attr_replace( e, CB_MONITOR_MODIFYCOUNT, ( struct berval **)vals );
+
+ sprintf( buf, "%lu", modrdncount );
+ val.bv_val = buf;
+ val.bv_len = strlen( buf );
+ slapi_entry_attr_replace( e, CB_MONITOR_MODRDNCOUNT, ( struct berval **)vals );
+
+ sprintf( buf, "%lu", searchbasecount );
+ val.bv_val = buf;
+ val.bv_len = strlen( buf );
+ slapi_entry_attr_replace( e, CB_MONITOR_SEARCHBASECOUNT, ( struct berval **)vals );
+
+ sprintf( buf, "%lu", searchonelevelcount );
+ val.bv_val = buf;
+ val.bv_len = strlen( buf );
+ slapi_entry_attr_replace( e, CB_MONITOR_SEARCHONELEVELCOUNT, ( struct berval **)vals );
+
+ sprintf( buf, "%lu", searchsubtreecount );
+ val.bv_val = buf;
+ val.bv_len = strlen( buf );
+ slapi_entry_attr_replace( e, CB_MONITOR_SEARCHSUBTREECOUNT, ( struct berval **)vals );
+
+ sprintf( buf, "%lu", abandoncount );
+ val.bv_val = buf;
+ val.bv_len = strlen( buf );
+ slapi_entry_attr_replace( e, CB_MONITOR_ABANDONCOUNT, ( struct berval **)vals );
+
+ sprintf( buf, "%lu", bindcount );
+ val.bv_val = buf;
+ val.bv_len = strlen( buf );
+ slapi_entry_attr_replace( e, CB_MONITOR_BINDCOUNT, ( struct berval **)vals );
+
+ sprintf( buf, "%lu", unbindcount );
+ val.bv_val = buf;
+ val.bv_len = strlen( buf );
+ slapi_entry_attr_replace( e, CB_MONITOR_UNBINDCOUNT, ( struct berval **)vals );
+
+ sprintf( buf, "%lu", comparecount );
+ val.bv_val = buf;
+ val.bv_len = strlen( buf );
+ slapi_entry_attr_replace( e, CB_MONITOR_COMPARECOUNT, ( struct berval **)vals );
+
+ sprintf( buf, "%d", outgoingconn );
+ val.bv_val = buf;
+ val.bv_len = strlen( buf );
+ slapi_entry_attr_replace( e, CB_MONITOR_OUTGOINGCONN, ( struct berval **)vals );
+
+ sprintf( buf, "%d", outgoingbindconn );
+ val.bv_val = buf;
+ val.bv_len = strlen( buf );
+ slapi_entry_attr_replace( e, CB_MONITOR_OUTGOINGBINDCOUNT, ( struct berval **)vals );
+
+ *returnCode= LDAP_SUCCESS;
+ return(SLAPI_DSE_CALLBACK_OK);
+}
+
+void
+cb_update_monitor_info(Slapi_PBlock * pb, cb_backend_instance * inst,int op)
+{
+
+ int scope;
+
+ slapi_lock_mutex(inst->monitor.mutex);
+ switch (op) {
+ case SLAPI_OPERATION_ADD:
+ inst->monitor.addcount++;
+ break;
+ case SLAPI_OPERATION_MODIFY:
+ inst->monitor.modifycount++;
+ break;
+ case SLAPI_OPERATION_DELETE:
+ inst->monitor.deletecount++;
+ break;
+ case SLAPI_OPERATION_MODRDN:
+/** case SLAPI_OPERATION_MODDN: **/
+ inst->monitor.modrdncount++;
+ break;
+ case SLAPI_OPERATION_COMPARE:
+ inst->monitor.comparecount++;
+ break;
+ case SLAPI_OPERATION_ABANDON:
+ inst->monitor.abandoncount++;
+ break;
+ case SLAPI_OPERATION_BIND:
+ inst->monitor.bindcount++;
+ break;
+ case SLAPI_OPERATION_UNBIND:
+ inst->monitor.unbindcount++;
+ break;
+ case SLAPI_OPERATION_SEARCH:
+ slapi_pblock_get( pb, SLAPI_SEARCH_SCOPE, &scope );
+ if ( LDAP_SCOPE_BASE == scope )
+ inst->monitor.searchbasecount++;
+ else
+ if ( LDAP_SCOPE_ONELEVEL == scope )
+ inst->monitor.searchonelevelcount++;
+ else
+ inst->monitor.searchsubtreecount++;
+ break;
+ default:
+ slapi_log_error( SLAPI_LOG_PLUGIN, CB_PLUGIN_SUBSYSTEM,"cb_update_monitor_info: invalid op type <%d>\n",op);
+ }
+ slapi_unlock_mutex(inst->monitor.mutex);
+}
+
+
+int
+cb_delete_monitor_callback(Slapi_PBlock * pb, Slapi_Entry * e, Slapi_Entry * entryAfter, int * returnCode, char * returnText, void * arg)
+{
+
+ cb_backend_instance *inst = (cb_backend_instance *)arg;
+
+ slapi_config_remove_callback(SLAPI_OPERATION_SEARCH, DSE_FLAG_PREOP, inst->monitorDn, LDAP_SCOPE_BASE,
+ "(objectclass=*)", cb_search_monitor_callback);
+ slapi_config_remove_callback(SLAPI_OPERATION_MODIFY, DSE_FLAG_PREOP, inst->monitorDn, LDAP_SCOPE_BASE,
+ "(objectclass=*)", cb_dont_allow_that);
+ slapi_config_remove_callback(SLAPI_OPERATION_DELETE, DSE_FLAG_PREOP, inst->monitorDn, LDAP_SCOPE_BASE,
+ "(objectclass=*)", cb_delete_monitor_callback);
+
+ *returnCode= LDAP_SUCCESS;
+ return(SLAPI_DSE_CALLBACK_OK);
+}
diff --git a/ldap/servers/plugins/chainingdb/cb_schema.c b/ldap/servers/plugins/chainingdb/cb_schema.c
new file mode 100644
index 00000000..2337b977
--- /dev/null
+++ b/ldap/servers/plugins/chainingdb/cb_schema.c
@@ -0,0 +1,45 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+#include "cb.h"
+
+void cb_eliminate_illegal_attributes(cb_backend_instance * inst, Slapi_Entry * e) {
+
+ /* get rid of illegal attributes before sending op to the */
+ /* farm server. (Add) */
+
+ int rc,j;
+ Slapi_Attr *attr=NULL;
+ char *tobefreed=NULL;
+
+ if (inst->illegal_attributes != NULL ) { /* Unlikely to happen */
+
+ PR_RWLock_Wlock(inst->rwl_config_lock);
+
+ for (j=0; inst->illegal_attributes[j]; j++) {
+ char * aType=NULL;
+ rc=slapi_entry_first_attr(e,&attr);
+ while (rc==0) {
+ if (tobefreed) {
+ slapi_entry_attr_delete( e, tobefreed);
+ tobefreed=NULL;
+ }
+ slapi_attr_get_type(attr,&aType);
+ if (aType && slapi_attr_types_equivalent(inst->illegal_attributes[j],aType)) {
+ tobefreed=aType;
+ slapi_log_error( SLAPI_LOG_PLUGIN, CB_PLUGIN_SUBSYSTEM,
+ "attribute <%s> not forwarded.\n",aType);
+ }
+ rc = slapi_entry_next_attr(e, attr, &attr);
+ }
+ if (tobefreed) {
+ slapi_entry_attr_delete( e, tobefreed);
+ tobefreed=NULL;
+ }
+ }
+
+ PR_RWLock_Unlock(inst->rwl_config_lock);
+ }
+}
diff --git a/ldap/servers/plugins/chainingdb/cb_search.c b/ldap/servers/plugins/chainingdb/cb_search.c
new file mode 100644
index 00000000..d9cf6ef7
--- /dev/null
+++ b/ldap/servers/plugins/chainingdb/cb_search.c
@@ -0,0 +1,698 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+#include "cb.h"
+
+/*
+ * Build a candidate list for this backentry and scope.
+ * Could be a BASE, ONELEVEL, or SUBTREE search.
+ *
+ * Returns:
+ * 0 - success
+ * <0 - fail
+ *
+ */
+
+int
+chainingdb_build_candidate_list ( Slapi_PBlock *pb )
+{
+
+ Slapi_Backend * be;
+ Slapi_Operation * op;
+ char *target, *filter;
+ int scope,attrsonly,sizelimit,timelimit,rc,searchreferral;
+ char **attrs=NULL;
+ LDAPControl **controls=NULL;
+ LDAPControl **ctrls=NULL;
+ LDAP *ld=NULL;
+ cb_backend_instance *cb = NULL;
+ cb_searchContext *ctx=NULL;
+ struct timeval timeout;
+ time_t optime;
+ int doit,parse_rc;
+ LDAPMessage *res=NULL;
+ char *matched_msg,*error_msg;
+ LDAPControl **serverctrls=NULL;
+ char **referrals=NULL;
+ char *cnxerrbuf=NULL;
+ time_t endbefore=0;
+ time_t endtime;
+ cb_outgoing_conn *cnx;
+
+ slapi_pblock_get( pb, SLAPI_BACKEND, &be );
+ cb = cb_get_instance(be);
+
+ slapi_pblock_get( pb, SLAPI_OPERATION, &op );
+ slapi_pblock_get( pb, SLAPI_SEARCH_STRFILTER, &filter );
+ slapi_pblock_get( pb, SLAPI_SEARCH_SCOPE, &scope );
+ slapi_pblock_get( pb, SLAPI_OPINITIATED_TIME, &optime );
+ slapi_pblock_get( pb, SLAPI_SEARCH_TARGET, &target );
+
+ if ( LDAP_SUCCESS != (parse_rc=cb_forward_operation(pb) )) {
+
+ /* Don't return errors */
+
+ if (cb_debug_on()) {
+ slapi_log_error( SLAPI_LOG_PLUGIN, CB_PLUGIN_SUBSYSTEM,
+ "local search: base:<%s> scope:<%s> filter:<%s>\n",target,
+ scope==LDAP_SCOPE_SUBTREE?"SUBTREE":scope==LDAP_SCOPE_ONELEVEL ? "ONE-LEVEL" : "BASE" , filter);
+ }
+
+ ctx = (cb_searchContext *)slapi_ch_calloc(1,sizeof(cb_searchContext));
+ ctx->type = CB_SEARCHCONTEXT_ENTRY;
+ ctx->data=NULL;
+
+ slapi_pblock_set( pb, SLAPI_SEARCH_RESULT_SET,ctx);
+ return 0;
+ }
+
+ cb_update_monitor_info(pb,cb,SLAPI_OPERATION_SEARCH);
+
+ /* Check wether the chaining BE is available or not */
+ if ( cb_check_availability( cb, pb ) == FARMSERVER_UNAVAILABLE ){
+ return -1;
+ }
+
+ if (cb_debug_on()) {
+ slapi_log_error( SLAPI_LOG_PLUGIN, CB_PLUGIN_SUBSYSTEM,
+ "chained search: base:<%s> scope:<%s> filter:<%s>\n",target,
+ scope==LDAP_SCOPE_SUBTREE?"SUBTREE":scope==LDAP_SCOPE_ONELEVEL ? "ONE-LEVEL" : "BASE" , filter);
+ }
+
+ slapi_pblock_get( pb, SLAPI_SEARCH_ATTRS, &attrs );
+ slapi_pblock_get( pb, SLAPI_SEARCH_ATTRSONLY, &attrsonly );
+ slapi_pblock_get( pb, SLAPI_REQCONTROLS, &controls );
+ slapi_pblock_get( pb, SLAPI_SEARCH_TIMELIMIT, &timelimit );
+ slapi_pblock_get( pb, SLAPI_SEARCH_SIZELIMIT, &sizelimit );
+ slapi_pblock_set( pb, SLAPI_SEARCH_RESULT_SET,NULL);
+
+
+ if ((scope != LDAP_SCOPE_BASE) && (scope != LDAP_SCOPE_ONELEVEL) && (scope != LDAP_SCOPE_SUBTREE)) {
+ cb_send_ldap_result( pb, LDAP_PROTOCOL_ERROR, NULL, "Bad scope", 0, NULL );
+ return 1;
+ }
+
+ searchreferral=cb->searchreferral;
+
+ if (( scope != LDAP_SCOPE_BASE ) && ( searchreferral )) {
+
+ int i;
+ struct berval bv,*bvals[2];
+ Slapi_Entry ** aciArray=(Slapi_Entry **) slapi_ch_malloc(2*sizeof(Slapi_Entry *));
+ Slapi_Entry *anEntry = slapi_entry_alloc();
+
+ slapi_entry_set_dn(anEntry,slapi_ch_strdup(target));
+
+ bvals[1]=NULL;
+ bvals[0]=&bv;
+ bv.bv_val="referral";
+ bv.bv_len=strlen(bv.bv_val);
+ slapi_entry_add_values( anEntry, "objectclass", bvals);
+
+ PR_RWLock_Rlock(cb->rwl_config_lock);
+ for (i=0; cb->url_array && cb->url_array[i]; i++) {
+ char * anUrl= slapi_ch_calloc(1,strlen(cb->url_array[i])+strlen(target)+1);
+ sprintf(anUrl,"%s%s",cb->url_array[i],target);
+ bv.bv_val=anUrl;
+ bv.bv_len=strlen(bv.bv_val);
+ slapi_entry_attr_merge( anEntry, "ref", bvals);
+ slapi_ch_free((void **)&anUrl);
+ }
+ PR_RWLock_Unlock(cb->rwl_config_lock);
+
+ aciArray[0]=anEntry;
+ aciArray[1]=NULL;
+
+ ctx = (cb_searchContext *)slapi_ch_calloc(1,sizeof(cb_searchContext));
+ ctx->type = CB_SEARCHCONTEXT_ENTRY;
+ ctx->data=aciArray;
+
+ slapi_pblock_set( pb, SLAPI_SEARCH_RESULT_SET,ctx);
+ return 0;
+ }
+
+ /*
+ ** Time limit management.
+ ** Make sure the operation has not expired
+ */
+
+ if ( timelimit == -1 ) {
+ timeout.tv_sec = timeout.tv_usec = 0;
+ } else {
+ time_t now=current_time();
+ endbefore=optime + timelimit;
+ if (now >= endbefore) {
+ cb_send_ldap_result( pb, LDAP_TIMELIMIT_EXCEEDED, NULL,NULL, 0, NULL);
+ slapi_pblock_set( pb, SLAPI_SEARCH_RESULT_ENTRY, NULL );
+ return 1;
+ }
+ timeout.tv_sec=timelimit-(now-optime);
+ timeout.tv_usec=0;
+ }
+
+ /* Operational attribute support for internal searches: */
+ /* The front-end relies on the fact that operational attributes */
+ /* are returned along with standard attrs when the attr list is */
+ /* NULL. To make it work, we need to explicitly request for all*/
+ /* possible operational attrs. Too bad. */
+
+ if ( (attrs == NULL) && operation_is_flag_set(op, OP_FLAG_INTERNAL) ) {
+ attrs = cb->every_attribute;
+
+ }
+ else
+ {
+ int i;
+ if ( attrs != NULL )
+ {
+ for ( i = 0; attrs[i] != NULL; i++ ) {
+ if ( strcasecmp( "nsrole", attrs[i] ) == 0 )
+ {
+ attrs = cb->every_attribute;
+ break;
+ }
+ }
+ }
+ }
+
+ /* Grab a connection handle */
+
+ if ( LDAP_SUCCESS != (rc = cb_get_connection(cb->pool,&ld,&cnx,&timeout,&cnxerrbuf))) {
+ if (rc == LDAP_TIMELIMIT_EXCEEDED)
+ cb_send_ldap_result( pb, rc, NULL,cnxerrbuf, 0, NULL);
+ else
+ cb_send_ldap_result( pb, LDAP_OPERATIONS_ERROR, NULL,cnxerrbuf, 0, NULL);
+
+ slapi_ch_free((void **)&cnxerrbuf);
+ /* ping the farm. If the farm is unreachable, we increment the counter */
+ cb_ping_farm(cb,NULL,0);
+ return 1;
+ }
+
+ /*
+ * Control management
+ */
+
+ if ( LDAP_SUCCESS != (rc = cb_update_controls( pb,ld,&ctrls,CB_UPDATE_CONTROLS_ADDAUTH ))) {
+ cb_send_ldap_result( pb, rc, NULL,NULL, 0, NULL);
+ cb_release_op_connection(cb->pool,ld,0);
+ return 1;
+ }
+
+ if ( slapi_op_abandoned( pb )) {
+ cb_release_op_connection(cb->pool,ld,0);
+ if ( NULL != ctrls)
+ ldap_controls_free(ctrls);
+ return 1;
+ }
+
+ ctx = (cb_searchContext *) slapi_ch_calloc(1,sizeof(cb_searchContext));
+
+ /*
+ ** We need to store the connection handle in the search context
+ ** to make sure we reuse it in the next_entry iteration
+ ** Indeed, if another thread on this connection detects a problem
+ ** on this connection, it may reallocate a new connection and
+ ** a call to get_connection may return a new cnx. Too bad.
+ */
+
+ ctx->ld=ld;
+ ctx->cnx=cnx;
+
+ /* for some reasons, it is an error to pass in a zero'd timeval */
+ /* to ldap_search_ext() */
+ if ((timeout.tv_sec==0) && (timeout.tv_usec==0))
+ timeout.tv_sec=timeout.tv_usec=-1;
+
+ /* heart-beat management */
+ if (cb->max_idle_time>0)
+ endtime=current_time() + cb->max_idle_time;
+
+ rc=ldap_search_ext(ld ,target,scope,filter,attrs,attrsonly,
+ ctrls, NULL, &timeout,sizelimit, &(ctx->msgid) );
+
+ if ( NULL != ctrls)
+ ldap_controls_free(ctrls);
+
+ if ( LDAP_SUCCESS != rc ) {
+ cb_send_ldap_result( pb, LDAP_OPERATIONS_ERROR, NULL, ldap_err2string(rc), 0, NULL);
+ cb_release_op_connection(cb->pool,ld,CB_LDAP_CONN_ERROR(rc));
+ slapi_ch_free((void **) &ctx);
+ return 1;
+ }
+
+ /*
+ ** Need to get the very first result to handle
+ ** errors properly, especially no search base.
+ */
+
+ doit=1;
+ while (doit) {
+
+ if (cb_check_forward_abandon(cb,pb,ctx->ld,ctx->msgid)) {
+ slapi_ch_free((void **) &ctx);
+ return 1;
+ }
+
+ rc=ldap_result(ld,ctx->msgid,LDAP_MSG_ONE,&cb->abandon_timeout,&res);
+ switch ( rc ) {
+ case -1:
+ /* An error occurred. return now */
+ rc = ldap_get_lderrno(ld,NULL,NULL);
+ /* tuck away some errors in a OPERATION_ERROR */
+ if (CB_LDAP_CONN_ERROR(rc)) {
+ cb_send_ldap_result(pb,LDAP_OPERATIONS_ERROR, NULL,
+ ldap_err2string( rc ), 0, NULL);
+ } else {
+ cb_send_ldap_result(pb,rc, NULL, NULL,0,NULL);
+ }
+ cb_release_op_connection(cb->pool,ld,CB_LDAP_CONN_ERROR(rc));
+ if (res)
+ ldap_msgfree(res);
+ slapi_ch_free((void **)&ctx);
+ return 1;
+ case 0:
+
+ /* Local timeout management */
+ if (timelimit != -1) {
+ if (current_time() > endbefore) {
+
+ slapi_log_error( SLAPI_LOG_PLUGIN, CB_PLUGIN_SUBSYSTEM,
+ "Local timeout expiration\n");
+
+ cb_send_ldap_result(pb,LDAP_TIMELIMIT_EXCEEDED,
+ NULL,NULL, 0, NULL);
+ /* Force connection close */
+ cb_release_op_connection(cb->pool,ld,1);
+ if (res)
+ ldap_msgfree(res);
+ slapi_ch_free((void **)&ctx);
+ return 1;
+ }
+ }
+ /* heart-beat management */
+ if ((rc=cb_ping_farm(cb,cnx,endtime)) != LDAP_SUCCESS) {
+ cb_send_ldap_result(pb,LDAP_OPERATIONS_ERROR, NULL,
+ ldap_err2string(rc), 0, NULL);
+ cb_release_op_connection(cb->pool,ld,CB_LDAP_CONN_ERROR(rc));
+ if (res)
+ ldap_msgfree(res);
+ slapi_ch_free((void **)&ctx);
+ return 1;
+ }
+
+#ifdef CB_YIELD
+ DS_Sleep(PR_INTERVAL_NO_WAIT);
+#endif
+ break;
+ case LDAP_RES_SEARCH_ENTRY:
+ case LDAP_RES_SEARCH_REFERENCE:
+ /* Some results received */
+ /* don't parse result here */
+ ctx->pending_result=res;
+ ctx->pending_result_type=rc;
+ doit=0;
+ break;
+ case LDAP_RES_SEARCH_RESULT:
+ matched_msg=NULL;
+ error_msg=NULL;
+ referrals=NULL;
+ serverctrls=NULL;
+ parse_rc=ldap_parse_result(ld,res,&rc,&matched_msg,
+ &error_msg,&referrals, &serverctrls, 0 );
+ if ( parse_rc != LDAP_SUCCESS ) {
+ cb_send_ldap_result(pb,parse_rc,
+ matched_msg,error_msg,0,NULL);
+ rc=-1;
+ } else
+ if ( rc != LDAP_SUCCESS ) {
+ ldap_get_lderrno( ctx->ld, &matched_msg, &error_msg );
+ cb_send_ldap_result( pb, rc, matched_msg,
+ error_msg,0,NULL);
+ /* BEWARE: matched_msg and error_msg points */
+ /* to ld fields. */
+ matched_msg=NULL;
+ error_msg=NULL;
+ rc=-1;
+ }
+
+ slapi_ch_free((void **)&matched_msg);
+ slapi_ch_free((void **)&error_msg);
+ if (serverctrls)
+ ldap_controls_free(serverctrls);
+ if (referrals)
+ charray_free(referrals);
+
+ if (rc!=LDAP_SUCCESS) {
+ cb_release_op_connection(cb->pool,ld,
+ CB_LDAP_CONN_ERROR(rc));
+ ldap_msgfree(res);
+ slapi_ch_free((void **)&ctx);
+ return -1;
+ }
+
+ /* Store the msg in the ctx */
+ /* Parsed in iterate. */
+
+ ctx->pending_result=res;
+ ctx->pending_result_type=LDAP_RES_SEARCH_RESULT;
+ doit=0;
+ }
+ }
+
+ slapi_pblock_set( pb, SLAPI_SEARCH_RESULT_SET,ctx);
+ return 0;
+}
+
+/*
+ * Return the next entry in the result set. The entry is returned
+ * in the pblock.
+ * Returns 0 normally. If -1 is returned, it means that some
+ * exceptional condition, e.g. timelimit exceeded has occurred,
+ * and this routine has sent a result to the client. If zero
+ * is returned and no entry is available in the PBlock, then
+ * we've iterated through all the entries.
+ */
+
+int
+chainingdb_next_search_entry ( Slapi_PBlock *pb )
+{
+
+ char *target;
+ int sizelimit,timelimit, rc, parse_rc, optime,i,retcode, attrsonly;
+ LDAPMessage *res=NULL;
+ char *matched_msg,*error_msg;
+ cb_searchContext *ctx=NULL;
+ Slapi_Entry *entry;
+ LDAPControl **serverctrls=NULL;
+ char **referrals=NULL;
+ cb_backend_instance * cb=NULL;
+ Slapi_Backend * be;
+ time_t endtime;
+
+ matched_msg=error_msg=NULL;
+
+ slapi_pblock_get( pb, SLAPI_SEARCH_RESULT_SET, &ctx );
+ slapi_pblock_get( pb, SLAPI_BACKEND, &be );
+ slapi_pblock_get( pb, SLAPI_SEARCH_TIMELIMIT, &timelimit );
+ slapi_pblock_get( pb, SLAPI_SEARCH_SIZELIMIT, &sizelimit );
+ slapi_pblock_get( pb, SLAPI_SEARCH_TARGET, &target );
+ slapi_pblock_get( pb, SLAPI_OPINITIATED_TIME, &optime );
+ slapi_pblock_get( pb, SLAPI_SEARCH_ATTRSONLY, &attrsonly );
+
+ cb = cb_get_instance(be);
+
+ if ( NULL == ctx ) {
+ /* End of local search */
+ slapi_pblock_set( pb, SLAPI_SEARCH_RESULT_SET,NULL);
+ slapi_pblock_set( pb, SLAPI_SEARCH_RESULT_ENTRY,NULL);
+ slapi_log_error( SLAPI_LOG_PLUGIN, CB_PLUGIN_SUBSYSTEM,
+ "Unexpected NULL ctx in chainingdb_next_search_entry\n");
+ return 0;
+ }
+
+ if ( NULL != ctx->tobefreed ) {
+ slapi_entry_free(ctx->tobefreed);
+ ctx->tobefreed=NULL;
+ }
+
+ if ( ctx->type == CB_SEARCHCONTEXT_ENTRY ) {
+
+ int n;
+ Slapi_Entry ** ptr;
+ if ( (timelimit != -1) && (timelimit != 0)) {
+ time_t now=current_time();
+
+ if (now > (optime + timelimit)) {
+ cb_send_ldap_result( pb, LDAP_TIMELIMIT_EXCEEDED, NULL,NULL, 0, NULL);
+ slapi_pblock_set( pb, SLAPI_SEARCH_RESULT_SET,NULL );
+ slapi_pblock_set( pb, SLAPI_SEARCH_RESULT_ENTRY,NULL);
+
+ for ( n = 0, ptr=(Slapi_Entry **)ctx->data; ptr != NULL && ptr[n] != NULL; n++ ) {
+ slapi_entry_free(ptr[n]);
+ }
+ if (ctx->data)
+ slapi_ch_free((void **)&ctx->data);
+ slapi_ch_free((void **)&ctx);
+ return -1;
+ }
+ }
+
+ /*
+ ** Return the Slapi_Entry of the result set one
+ ** by one
+ */
+
+ for ( n = 0, ptr=(Slapi_Entry **)ctx->data; ptr != NULL && ptr[n] != NULL; n++ );
+ if ( n != 0) {
+ Slapi_Entry * anEntry=ptr[n-1];
+ ptr[n-1]=NULL;
+ slapi_pblock_set( pb, SLAPI_SEARCH_RESULT_ENTRY,anEntry);
+ slapi_pblock_set( pb, SLAPI_SEARCH_RESULT_SET,ctx);
+ cb_set_acl_policy(pb);
+ ctx->tobefreed=anEntry;
+ } else {
+ slapi_ch_free((void **) &ctx);
+ slapi_pblock_set( pb, SLAPI_SEARCH_RESULT_SET,NULL );
+ slapi_pblock_set( pb, SLAPI_SEARCH_RESULT_ENTRY,NULL);
+ }
+ return 0;
+ }
+
+ /*
+ * Grab a connection handle. Should be the same as the one
+ * used in the build_candidate list. To be certain of that, grab it from
+ * the context.
+ */
+
+ /* Poll the server for the results of the search operation.
+ * Passing LDAP_MSG_ONE indicates that you want to receive
+ * the entries one at a time, as they come in. If the next
+ * entry that you retrieve is NULL, there are no more entries.
+ */
+
+ /* heart-beat management */
+ if (cb->max_idle_time>0)
+ endtime=current_time() + cb->max_idle_time;
+
+ while (1) {
+
+ if (cb_check_forward_abandon(cb,pb,ctx->ld,ctx->msgid)) {
+ /* cnx handle released */
+ if (ctx->pending_result)
+ ldap_msgfree(ctx->pending_result);
+ slapi_ch_free((void **) &ctx);
+ slapi_pblock_set( pb, SLAPI_SEARCH_RESULT_SET,NULL );
+ slapi_pblock_set( pb, SLAPI_SEARCH_RESULT_ENTRY,NULL);
+ return -1;
+ }
+
+ /* Check for time limit done by the remote farm server */
+ /* Check for size limit done by the remote farm server */
+
+ /* Use pending msg if one is available */
+ if (ctx->pending_result) {
+ res=ctx->pending_result;
+ rc=ctx->pending_result_type;
+ ctx->pending_result=NULL;
+ } else {
+
+
+ rc=ldap_result(ctx->ld,ctx->msgid,
+ LDAP_MSG_ONE, &cb->abandon_timeout, &res );
+ }
+
+ /* The server can return three types of results back to the client,
+ * and the return value of ldap_result() indicates the result type:
+ * LDAP_RES_SEARCH_ENTRY identifies an entry found by the search,
+ * LDAP_RES_SEARCH_REFERENCE identifies a search reference returned
+ * by the server, and LDAP_RES_SEARCH_RESULT is the last result
+ * sent from the server to the client after the operation completes.
+ * We need to check for each of these types of results.
+ */
+
+ switch ( rc ) {
+ case -1:
+
+ /* An error occurred. */
+ rc = ldap_get_lderrno( ctx->ld, NULL, NULL );
+ slapi_pblock_set( pb, SLAPI_SEARCH_RESULT_SET,NULL);
+ slapi_pblock_set( pb, SLAPI_SEARCH_RESULT_ENTRY,NULL);
+
+ cb_send_ldap_result( pb, LDAP_OPERATIONS_ERROR, NULL, ldap_err2string( rc ), 0, NULL);
+
+ if (res)
+ ldap_msgfree(res);
+ cb_release_op_connection(cb->pool,ctx->ld,CB_LDAP_CONN_ERROR(rc));
+ slapi_ch_free((void **)&ctx);
+ return -1;
+ case 0:
+ /* heart-beat management */
+ if ((rc=cb_ping_farm(cb,ctx->cnx,endtime)) != LDAP_SUCCESS) {
+
+ slapi_pblock_set( pb, SLAPI_SEARCH_RESULT_SET,NULL);
+ slapi_pblock_set( pb, SLAPI_SEARCH_RESULT_ENTRY,NULL);
+
+ cb_send_ldap_result(pb,LDAP_OPERATIONS_ERROR, NULL,
+ ldap_err2string(rc), 0, NULL);
+
+ if (res)
+ ldap_msgfree(res);
+ cb_release_op_connection(cb->pool,ctx->ld,CB_LDAP_CONN_ERROR(rc));
+ slapi_ch_free((void **)&ctx);
+ return -1;
+ }
+#ifdef CB_YIELD
+ DS_Sleep(PR_INTERVAL_NO_WAIT);
+#endif
+ break;
+
+ case LDAP_RES_SEARCH_ENTRY:
+
+ /* heart-beat management */
+ if (cb->max_idle_time>0)
+ endtime=current_time() + cb->max_idle_time;
+
+ /* The server sent one of the entries found by the search */
+ if ((entry = cb_LDAPMessage2Entry(ctx->ld,res,attrsonly)) == NULL) {
+ slapi_log_error( SLAPI_LOG_PLUGIN, CB_PLUGIN_SUBSYSTEM,"Invalid entry received.\n");
+ slapi_pblock_set( pb, SLAPI_SEARCH_RESULT_SET,NULL);
+ slapi_pblock_set( pb, SLAPI_SEARCH_RESULT_ENTRY,NULL);
+
+ cb_send_ldap_result( pb, LDAP_OPERATIONS_ERROR, NULL, NULL , 0, NULL);
+
+ ldap_msgfree(res);
+ cb_release_op_connection(cb->pool,ctx->ld,0);
+ slapi_ch_free((void **)&ctx);
+ return -1;
+ }
+
+ ctx->tobefreed=entry;
+ slapi_pblock_set( pb, SLAPI_SEARCH_RESULT_SET,ctx);
+ slapi_pblock_set( pb, SLAPI_SEARCH_RESULT_ENTRY,entry);
+ cb_set_acl_policy(pb);
+ ldap_msgfree(res);
+ return 0;
+
+ case LDAP_RES_SEARCH_REFERENCE:
+
+ /* The server sent a search reference encountered during the
+ * search operation.
+ */
+
+ /* heart-beat management */
+ if (cb->max_idle_time>0)
+ endtime=current_time() + cb->max_idle_time;
+
+ parse_rc = ldap_parse_reference( ctx->ld, res, &referrals, NULL, 1 );
+ if ( parse_rc != LDAP_SUCCESS ) {
+ cb_send_ldap_result( pb, LDAP_OPERATIONS_ERROR, NULL,
+ ldap_err2string( parse_rc ), 0, NULL);
+ cb_release_op_connection(cb->pool,ctx->ld,CB_LDAP_CONN_ERROR(parse_rc));
+ slapi_ch_free((void **)&ctx);
+
+ slapi_pblock_set( pb, SLAPI_SEARCH_RESULT_SET,NULL);
+ slapi_pblock_set( pb, SLAPI_SEARCH_RESULT_ENTRY,NULL);
+ return -1;
+ }
+
+ /*
+ ** build a dummy entry on the fly with a ref attribute
+ */
+
+ {
+
+ struct berval bv;
+ int i;
+ struct berval *bvals[2];
+ Slapi_Entry *anEntry = slapi_entry_alloc();
+ slapi_entry_set_dn(anEntry,slapi_ch_strdup(target));
+
+ bvals[1]=NULL;
+ bvals[0]=&bv;
+
+ bv.bv_val="referral";
+ bv.bv_len=strlen(bv.bv_val);
+ slapi_entry_add_values( anEntry, "objectclass", bvals);
+
+ for (i=0;referrals[i] != NULL; i++) {
+ bv.bv_val=referrals[i];
+ bv.bv_len=strlen(bv.bv_val);
+ slapi_entry_add_values( anEntry, "ref", bvals);
+ }
+
+ slapi_pblock_set( pb, SLAPI_SEARCH_RESULT_SET,ctx);
+ slapi_pblock_set( pb, SLAPI_SEARCH_RESULT_ENTRY,anEntry);
+ cb_set_acl_policy(pb);
+ }
+
+ if (referrals != NULL) {
+ ldap_value_free( referrals );
+ }
+
+ return 0;
+
+ case LDAP_RES_SEARCH_RESULT:
+
+ /* Parse the final result received from the server. Note the last
+ * argument is a non-zero value, which indicates that the
+ * LDAPMessage structure will be freed when done.
+ */
+
+ slapi_pblock_set( pb, SLAPI_SEARCH_RESULT_SET,NULL);
+ slapi_pblock_set( pb, SLAPI_SEARCH_RESULT_ENTRY,NULL);
+
+ parse_rc = ldap_parse_result( ctx->ld, res,
+ &rc,&matched_msg,&error_msg, &referrals, &serverctrls, 1 );
+ if ( parse_rc != LDAP_SUCCESS ) {
+ cb_send_ldap_result( pb, LDAP_OPERATIONS_ERROR, matched_msg,
+ ldap_err2string( parse_rc ), 0, NULL);
+
+ retcode=-1;
+ } else
+ if ( rc != LDAP_SUCCESS ) {
+ ldap_get_lderrno( ctx->ld, &matched_msg, &error_msg );
+ cb_send_ldap_result( pb, rc, matched_msg, NULL, 0, NULL);
+
+ /* BEWARE: Don't free matched_msg && error_msg */
+ /* Points to the ld fields */
+ matched_msg=NULL;
+ error_msg=NULL;
+ retcode=-1;
+ } else {
+ /* Add control response sent by the farm server */
+ for (i=0; serverctrls && serverctrls[i];i++)
+ slapi_pblock_set( pb, SLAPI_ADD_RESCONTROL, serverctrls[i]);
+ retcode=0;
+ }
+
+ if (serverctrls)
+ ldap_controls_free(serverctrls);
+ slapi_ch_free((void **)&matched_msg);
+ slapi_ch_free((void **)&error_msg);
+ if (referrals)
+ charray_free(referrals);
+
+ cb_release_op_connection(cb->pool,ctx->ld,0);
+ slapi_ch_free((void **)&ctx);
+ return retcode;
+
+ default:
+ slapi_log_error( SLAPI_LOG_PLUGIN, CB_PLUGIN_SUBSYSTEM,
+ "chainingdb_next_search_entry:default case.\n");
+
+ }
+ }
+
+ /* Not reached */
+ /* return 0; */
+}
+
+int
+chaining_back_entry_release ( Slapi_PBlock *pb ) {
+ slapi_log_error( SLAPI_LOG_PLUGIN, CB_PLUGIN_SUBSYSTEM, "chaining_back_entry_release\n");
+ return 0;
+}
+
diff --git a/ldap/servers/plugins/chainingdb/cb_size.c b/ldap/servers/plugins/chainingdb/cb_size.c
new file mode 100644
index 00000000..a6f1fb20
--- /dev/null
+++ b/ldap/servers/plugins/chainingdb/cb_size.c
@@ -0,0 +1,22 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+#include "cb.h"
+
+int
+cb_db_size( Slapi_PBlock *pb )
+{
+
+ /*
+ ** Return the size in byte of the local database storage
+ ** Size is 0 for a chaining backend
+ */
+
+ unsigned int size=0;
+
+ slapi_pblock_set( pb, SLAPI_DBSIZE, &size );
+ return 0;
+}
+
diff --git a/ldap/servers/plugins/chainingdb/cb_start.c b/ldap/servers/plugins/chainingdb/cb_start.c
new file mode 100644
index 00000000..c4a4538b
--- /dev/null
+++ b/ldap/servers/plugins/chainingdb/cb_start.c
@@ -0,0 +1,43 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+#include "cb.h"
+
+int
+chainingdb_start ( Slapi_PBlock *pb ) {
+
+ cb_backend * cb;
+
+ slapi_pblock_get( pb, SLAPI_PLUGIN_PRIVATE, &cb );
+
+ if (cb->started) {
+ /* We may be called multiple times due to */
+ /* plugin dependency resolution */
+ return 0;
+ }
+
+ /*
+ ** Reads in any configuration information held in the dse for the
+ ** chaining plugin. Create dse entries used to configure the
+ ** chaining plugin if they don't exist. Registers plugins to maintain
+ ** those dse entries.
+ */
+
+ cb_config_load_dse_info(pb);
+
+ /* Register new LDAPv3 controls supported by the chaining backend */
+
+ slapi_register_supported_control( CB_LDAP_CONTROL_CHAIN_SERVER,
+ SLAPI_OPERATION_SEARCH | SLAPI_OPERATION_COMPARE
+ | SLAPI_OPERATION_ADD | SLAPI_OPERATION_DELETE
+ | SLAPI_OPERATION_MODIFY | SLAPI_OPERATION_MODDN );
+
+ /* register to be notified when backend state changes */
+ slapi_register_backend_state_change((void *)cb_be_state_change,
+ cb_be_state_change);
+
+ cb->started=1;
+ return 0;
+}
diff --git a/ldap/servers/plugins/chainingdb/cb_temp.c b/ldap/servers/plugins/chainingdb/cb_temp.c
new file mode 100644
index 00000000..fc5407ff
--- /dev/null
+++ b/ldap/servers/plugins/chainingdb/cb_temp.c
@@ -0,0 +1,15 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+#include "cb.h"
+
+/*
+** Temp wrappers until the appropriate functions
+** are implemented in the slapi interface
+*/
+
+cb_backend_instance * cb_get_instance(Slapi_Backend * be) {
+ return (cb_backend_instance *)slapi_be_get_instance_info(be);
+}
diff --git a/ldap/servers/plugins/chainingdb/cb_test.c b/ldap/servers/plugins/chainingdb/cb_test.c
new file mode 100644
index 00000000..cb075664
--- /dev/null
+++ b/ldap/servers/plugins/chainingdb/cb_test.c
@@ -0,0 +1,76 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+#include "cb.h"
+
+int cb_back_test( Slapi_PBlock *pb )
+{
+
+ Slapi_Backend * be;
+ cb_backend * cb;
+ cb_backend_instance * inst;
+ Slapi_PBlock * apb;
+ int res;
+ int rc=0;
+ const Slapi_DN *aSuffix=NULL;
+ const char * aSuffixString;
+ char * theTarget;
+
+
+ slapi_pblock_get( pb, SLAPI_PLUGIN_PRIVATE, &cb );
+ slapi_pblock_get( pb, SLAPI_BACKEND, &be );
+ inst = cb_get_instance(be);
+ apb = slapi_pblock_new();
+
+ /*
+ ** Try to open a connection to the farm server
+ ** Try to get a dummy entry BELOW the suffix managed
+ ** by the chaining backend, in case the local root is shared
+ ** across different backend
+ */
+
+ printf("Begin test instance %s.\n",inst->inst_name);
+
+ aSuffix = slapi_be_getsuffix(be,0);
+ aSuffixString=slapi_sdn_get_dn(aSuffix);
+ /* Remove leading white spaces */
+ for (aSuffixString; *aSuffixString==' ';aSuffixString++) {}
+ theTarget=slapi_ch_calloc(1,strlen(aSuffixString)+20);
+ sprintf(theTarget,"cn=test,%s",aSuffixString);
+
+ /* XXXSD make sure chaining allowed for this plugin... */
+ slapi_search_internal_set_pb (apb, theTarget, LDAP_SCOPE_BASE, "objectclass=*", NULL, 0, NULL, NULL,
+ cb->identity,0 );
+ slapi_search_internal_pb (apb);
+
+ slapi_ch_free((void **)&theTarget);
+
+ if ( NULL == apb ) {
+ printf("Can't contact farm server. (Internal error).\n");
+ rc=-1;
+ goto the_end;
+ }
+
+ slapi_pblock_get(apb, SLAPI_PLUGIN_INTOP_RESULT, &res);
+ /* OPERATIONS ERRORS also returned when bind failed */
+ if (CB_LDAP_CONN_ERROR(res) || (res==LDAP_OPERATIONS_ERROR ))
+ {
+ printf("Can't contact the remote farm server %s. (%s).\n",inst->pool->hostname,ldap_err2string(res));
+ rc=-1;
+ goto the_end;
+ } else {
+ printf("Connection established with the remote farm server %s.\n",inst->pool->hostname);
+ }
+
+the_end:
+ if (apb)
+ {
+ slapi_free_search_results_internal(apb);
+ slapi_pblock_destroy (apb);
+ }
+
+ return rc;
+}
+
diff --git a/ldap/servers/plugins/chainingdb/cb_unbind.c b/ldap/servers/plugins/chainingdb/cb_unbind.c
new file mode 100644
index 00000000..1400ae1f
--- /dev/null
+++ b/ldap/servers/plugins/chainingdb/cb_unbind.c
@@ -0,0 +1,23 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+#include "cb.h"
+
+int
+chainingdb_unbind( Slapi_PBlock *pb ) {
+
+ /* Nothing to do because connection mgmt is stateless*/
+
+ Slapi_Backend * be;
+ cb_backend_instance * cb;
+
+ slapi_pblock_get( pb, SLAPI_BACKEND, &be );
+ cb = cb_get_instance(be);
+
+ cb_update_monitor_info(pb,cb,SLAPI_OPERATION_UNBIND);
+
+ cb_send_ldap_result( pb, LDAP_SUCCESS, NULL, NULL, 0, NULL );
+ return SLAPI_BIND_SUCCESS;
+}
diff --git a/ldap/servers/plugins/chainingdb/cb_utils.c b/ldap/servers/plugins/chainingdb/cb_utils.c
new file mode 100644
index 00000000..b73effd0
--- /dev/null
+++ b/ldap/servers/plugins/chainingdb/cb_utils.c
@@ -0,0 +1,375 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+#include "cb.h"
+
+/* return the rootdn configured in the server */
+
+char * cb_get_rootdn() {
+
+ char * ret=slapi_get_rootdn();
+ if (ret == NULL)
+ ret = slapi_ch_strdup(CB_DIRECTORY_MANAGER_DN);
+ if (ret)
+ slapi_dn_normalize_case(ret); /* UTF8-aware */
+ return ret;
+}
+
+void
+cb_send_ldap_result(Slapi_PBlock *pb, int err, char *matched,char *text, int nentries, struct berval **urls )
+{
+ cb_set_acl_policy(pb);
+ slapi_send_ldap_result( pb, err, matched, text, nentries ,urls);
+}
+
+Slapi_Entry * cb_LDAPMessage2Entry(LDAP * ld, LDAPMessage * msg, int attrsonly) {
+
+ Slapi_Entry * e = slapi_entry_alloc();
+ char * a=NULL;
+ BerElement * ber=NULL;
+
+ if ( e == NULL ) return NULL;
+ if (msg == NULL) {
+ slapi_entry_free(e);
+ return NULL;
+ }
+
+ /*
+ * dn not allocated by slapi
+ * attribute type and values ARE allocated
+ */
+
+ slapi_entry_set_dn( e, ldap_get_dn( ld, msg ) );
+
+ for ( a = ldap_first_attribute( ld, msg, &ber ); a!=NULL;
+ a=ldap_next_attribute( ld, msg, ber ) ) {
+ if(attrsonly) {
+ slapi_entry_add_value(e, a, (Slapi_Value *)NULL);
+ ldap_memfree(a);
+ } else {
+ struct berval ** aVal = ldap_get_values_len( ld, msg, a);
+ slapi_entry_add_values( e, a, aVal);
+
+ ldap_memfree(a);
+ ldap_value_free_len(aVal);
+ }
+ }
+ if ( NULL != ber )
+ ldap_ber_free( ber, 0 );
+
+ return e;
+}
+
+struct berval ** referrals2berval(char ** referrals) {
+
+ int i;
+ struct berval ** val=NULL;
+
+ if (referrals == NULL)
+ return NULL;
+
+ for (i=0;referrals[i];i++) {}
+
+ val = (struct berval **) slapi_ch_calloc(1,(i+1)*sizeof(struct berval *));
+
+ for (i=0;referrals[i];i++) {
+
+ val[i]=(struct berval *) slapi_ch_malloc(sizeof(struct berval));
+ val[i]->bv_len= strlen(referrals[i]);
+ val[i]->bv_val = slapi_ch_strdup(referrals[i]);
+ }
+
+ return val;
+}
+
+char *
+cb_urlparse_err2string( int err )
+{
+ char *s="internal error";
+
+ switch( err ) {
+ case 0:
+ s = "no error";
+ break;
+ case LDAP_URL_ERR_NOTLDAP:
+ s = "missing ldap:// or ldaps://";
+ break;
+ case LDAP_URL_ERR_NODN:
+ s = "missing suffix";
+ break;
+ case LDAP_URL_ERR_BADSCOPE:
+ s = "invalid search scope";
+ break;
+ case LDAP_URL_ERR_MEM:
+ s = "unable to allocate memory";
+ break;
+ case LDAP_URL_ERR_PARAM:
+ s = "bad parameter to an LDAP URL function";
+ break;
+ }
+
+ return( s );
+}
+
+/*
+** Return LDAP_SUCCESS if an internal operation needs to be forwarded to
+** the farm server. We check chaining policy for internal operations
+** We also check max hop count for loop detection for both internal
+** and external operations
+*/
+
+int cb_forward_operation(Slapi_PBlock * pb ) {
+
+ Slapi_Operation *op=NULL;
+ Slapi_Backend *be;
+ struct slapi_componentid *cid = NULL;
+ char *pname;
+ cb_backend_instance *cb;
+ int retcode;
+ LDAPControl **ctrls=NULL;
+
+ slapi_pblock_get (pb, SLAPI_OPERATION, &op);
+
+ /* Loop detection */
+ slapi_pblock_get( pb, SLAPI_REQCONTROLS, &ctrls );
+
+ if ( NULL != ctrls ) {
+ struct berval *ctl_value=NULL;
+ int iscritical=0;
+
+ if (slapi_control_present(ctrls,CB_LDAP_CONTROL_CHAIN_SERVER,&ctl_value,&iscritical)) {
+
+ /* Decode control data */
+ /* hop INTEGER (0 .. maxInt) */
+
+ int hops = 0;
+ int rc;
+ BerElement *ber = NULL;
+
+ if ((ber = ber_init(ctl_value)) == NULL) {
+ slapi_log_error(SLAPI_LOG_PLUGIN, CB_PLUGIN_SUBSYSTEM,
+ "cb_forward_operation: ber_init: Memory allocation failed");
+ return LDAP_NO_MEMORY;
+ }
+ rc = ber_scanf(ber,"i",&hops);
+ if (LBER_ERROR == rc) {
+ slapi_log_error(SLAPI_LOG_PLUGIN, CB_PLUGIN_SUBSYSTEM,
+ "Loop detection control badly encoded.");
+ ber_free(ber,1);
+ return LDAP_LOOP_DETECT;
+ }
+
+ if (hops <=0) {
+ slapi_log_error(SLAPI_LOG_PLUGIN, CB_PLUGIN_SUBSYSTEM,
+ "Max hop count exceeded. Loop detected.\n");
+ ber_free(ber,1);
+ return LDAP_LOOP_DETECT;
+ }
+ ber_free(ber,1);
+ }
+ }
+
+ if ( !operation_is_flag_set(op, OP_FLAG_INTERNAL))
+ return LDAP_SUCCESS;
+
+ slapi_pblock_get (pb, SLAPI_PLUGIN_IDENTITY, &cid);
+ if ( cid == NULL ) {
+ /* programming error in the front-end */
+ slapi_log_error(SLAPI_LOG_FATAL, CB_PLUGIN_SUBSYSTEM,
+ "NULL component identity in an internal operation.");
+ return LDAP_UNWILLING_TO_PERFORM;
+ }
+ pname=cid->sci_component_name;
+
+ if (cb_debug_on()) {
+ slapi_log_error(SLAPI_LOG_PLUGIN, CB_PLUGIN_SUBSYSTEM,
+ "internal op received from %s component \n",pname ? pname : "NULL");
+ }
+
+ /* First, make sure chaining is not denied */
+ if (operation_is_flag_set(op, SLAPI_OP_FLAG_NEVER_CHAIN))
+ return LDAP_UNWILLING_TO_PERFORM;
+
+ /* unidentified caller. should not happen */
+ if (pname == NULL)
+ return LDAP_UNWILLING_TO_PERFORM;
+
+ slapi_pblock_get( pb, SLAPI_BACKEND, &be );
+ cb = cb_get_instance(be);
+
+ /* Local policy */
+ PR_RWLock_Rlock(cb->rwl_config_lock);
+ if ( cb->chaining_components != NULL ) {
+ retcode=charray_inlist(cb->chaining_components,pname);
+ PR_RWLock_Unlock(cb->rwl_config_lock);
+ if ( retcode )
+ retcode=LDAP_SUCCESS;
+ else
+ retcode=LDAP_UNWILLING_TO_PERFORM;
+ return retcode;
+ }
+ PR_RWLock_Unlock(cb->rwl_config_lock);
+
+ /* Global policy */
+ PR_RWLock_Rlock(cb->backend_type->config.rwl_config_lock);
+ retcode=charray_inlist(cb->backend_type->config.chaining_components,pname);
+ PR_RWLock_Unlock(cb->backend_type->config.rwl_config_lock);
+
+ if ( retcode )
+ retcode=LDAP_SUCCESS;
+ else
+ retcode=LDAP_UNWILLING_TO_PERFORM;
+ return retcode;
+}
+
+/* better atol -- it understands a trailing multiplier k/m/g
+ * for example, "32k" will be returned as 32768
+ */
+long cb_atol(char *str)
+{
+ long multiplier = 1;
+ char *x = str;
+
+ /* find possible trailing k/m/g */
+ while ((*x >= '0') && (*x <= '9')) x++;
+ switch (*x) {
+ case 'g':
+ case 'G':
+ multiplier *= 1024;
+ case 'm':
+ case 'M':
+ multiplier *= 1024;
+ case 'k':
+ case 'K':
+ multiplier *= 1024;
+ }
+ return (atol(str) * multiplier);
+}
+
+int cb_atoi(char *str)
+{
+ return (int)cb_atol(str);
+}
+
+
+/* This function is used by the instance modify callback to add a new
+ * suffix. It return LDAP_SUCCESS on success.
+ */
+int cb_add_suffix(cb_backend_instance *inst, struct berval **bvals, int apply_mod, char *returntext)
+{
+ Slapi_DN *suffix;
+ int x;
+
+ returntext[0] = '\0';
+ for (x = 0; bvals[x]; x++) {
+ suffix=slapi_sdn_new_dn_byval(bvals[x]->bv_val);
+ if (!slapi_be_issuffix(inst->inst_be, suffix) && apply_mod) {
+ slapi_be_addsuffix(inst->inst_be, suffix);
+ }
+ slapi_sdn_free(&suffix);
+ }
+
+ return LDAP_SUCCESS;
+}
+
+static int debug_on=0;
+
+int cb_debug_on()
+{
+ return debug_on;
+}
+
+void cb_set_debug(int on) {
+ debug_on=on;
+}
+
+/* this function is called when state of a backend changes */
+/* The purpose of this function is to handle the associated_be_is_disabled
+ flag in the cb instance structure. The associated database is used to
+ perform local acl evaluations. The associated database can be
+ 1) The chaining backend is the backend of a sub suffix, and the
+ parent suffix has a local backend
+ 2) Entry distribution is being used to distribute write operations to
+ a chaining backend and other operations to a local backend
+ (e.g. a replication hub or consumer)
+ If the associated local backend is being initialized (import), it will be
+ disabled, and it will be impossible to evaluate local acls. In this case,
+ we still want to be able to chain operations to a farm server or another
+ database chain. But the current code will not allow cascading without
+ local acl evaluation (cb_controls.c). associated_be_is_disabled allows
+ us to relax that restriction while the associated backend is disabled
+*/
+/*
+ The first thing we need to do is to determine what our associated backends
+ are. An associated backend is defined as a backend used by the same
+ suffix which uses this cb instance or a backend used by any
+ parent suffix of the suffix which uses this cb instance
+
+ We first see if the be_name is for a local database. If not, then just return.
+ So for the given be_name, we find the suffix which uses it, then the mapping tree
+ entry for that suffix. Then
+ get cb instances used by the suffix and set associated_be_is_disabled
+ get cb instances used by sub suffixes of this suffix and
+ set associated_be_is_disabled
+*/
+void
+cb_be_state_change (void *handle, char *be_name, int old_be_state, int new_be_state)
+{
+ const Slapi_DN *tmpsdn;
+ Slapi_DN *the_be_suffix;
+ char *cookie = NULL;
+ Slapi_Backend *chainbe;
+ Slapi_Backend *the_be = slapi_be_select_by_instance_name(be_name);
+
+ /* no backend? */
+ if (!the_be) {
+ return;
+ }
+
+ /* ignore chaining backends - associated backends must be local */
+ if (slapi_be_is_flag_set(the_be, SLAPI_BE_FLAG_REMOTE_DATA)) {
+ return;
+ }
+
+ /* get the suffix for the local backend */
+ tmpsdn = slapi_be_getsuffix(the_be, 0);
+ if (!tmpsdn) {
+ return;
+ } else {
+ the_be_suffix = slapi_sdn_dup(tmpsdn);
+ }
+
+ /* now, iterate through the chaining backends */
+ for (chainbe = slapi_get_first_backend(&cookie);
+ chainbe; chainbe = slapi_get_next_backend(cookie)) {
+ /* only look at chaining backends */
+ if (slapi_be_is_flag_set(chainbe, SLAPI_BE_FLAG_REMOTE_DATA)) {
+ /* get the suffix */
+ const Slapi_DN *tmpcbsuf = slapi_be_getsuffix(chainbe, 0);
+ if (tmpcbsuf) {
+ /* make a copy - to be safe */
+ Slapi_DN *cbsuffix = slapi_sdn_dup(tmpcbsuf);
+ /* if the suffixes are equal, or the_be_suffix is a suffix
+ of cbsuffix, apply the flag */
+ if (!slapi_sdn_compare(cbsuffix, the_be_suffix) ||
+ slapi_sdn_issuffix(cbsuffix, the_be_suffix)) {
+ cb_backend_instance *cbinst = cb_get_instance(chainbe);
+ if (cbinst) {
+ /* the backend is disabled if the state is not ON */
+ cbinst->associated_be_is_disabled = (new_be_state != SLAPI_BE_STATE_ON);
+ slapi_log_error(SLAPI_LOG_PLUGIN, "chainbe", "cb_be_state_change: set the "
+ "state of chainbe for %s to %d\n",
+ slapi_sdn_get_dn(cbsuffix), (new_be_state != SLAPI_BE_STATE_ON));
+ }
+ }
+ slapi_sdn_free(&cbsuffix);
+ }
+ }
+ }
+
+ /* clean up */
+ slapi_sdn_free(&the_be_suffix);
+ slapi_ch_free_string(&cookie);
+}
diff --git a/ldap/servers/plugins/chainingdb/cbdllmain.c b/ldap/servers/plugins/chainingdb/cbdllmain.c
new file mode 100644
index 00000000..cacf9cb5
--- /dev/null
+++ b/ldap/servers/plugins/chainingdb/cbdllmain.c
@@ -0,0 +1,128 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+#include "cb.h"
+
+#ifdef _WIN32
+/* Lifted from Q125688
+ * How to Port a 16-bit DLL to a Win32 DLL
+ * on the MSVC 4.0 CD
+ */
+BOOL WINAPI DllMain (HANDLE hModule, DWORD fdwReason, LPVOID lpReserved)
+{
+ WSADATA wsadata;
+
+ switch (fdwReason)
+ {
+ case DLL_PROCESS_ATTACH:
+ /* Code from LibMain inserted here. Return TRUE to keep the
+ DLL loaded or return FALSE to fail loading the DLL.
+
+ You may have to modify the code in your original LibMain to
+ account for the fact that it may be called more than once.
+ You will get one DLL_PROCESS_ATTACH for each process that
+ loads the DLL. This is different from LibMain which gets
+ called only once when the DLL is loaded. The only time this
+ is critical is when you are using shared data sections.
+ If you are using shared data sections for statically
+ allocated data, you will need to be careful to initialize it
+ only once. Check your code carefully.
+
+ Certain one-time initializations may now need to be done for
+ each process that attaches. You may also not need code from
+ your original LibMain because the operating system may now
+ be doing it for you.
+ */
+ /*
+ * 16 bit code calls UnlockData()
+ * which is mapped to UnlockSegment in windows.h
+ * in 32 bit world UnlockData is not defined anywhere
+ * UnlockSegment is mapped to GlobalUnfix in winbase.h
+ * and the docs for both UnlockSegment and GlobalUnfix say
+ * ".. function is oboslete. Segments have no meaning
+ * in the 32-bit environment". So we do nothing here.
+ */
+
+ if( errno = WSAStartup(0x0101, &wsadata ) != 0 )
+ return FALSE;
+
+ break;
+
+ case DLL_THREAD_ATTACH:
+ /* Called each time a thread is created in a process that has
+ already loaded (attached to) this DLL. Does not get called
+ for each thread that exists in the process before it loaded
+ the DLL.
+
+ Do thread-specific initialization here.
+ */
+ break;
+
+ case DLL_THREAD_DETACH:
+ /* Same as above, but called when a thread in the process
+ exits.
+
+ Do thread-specific cleanup here.
+ */
+ break;
+
+ case DLL_PROCESS_DETACH:
+ /* Code from _WEP inserted here. This code may (like the
+ LibMain) not be necessary. Check to make certain that the
+ operating system is not doing it for you.
+ */
+ WSACleanup();
+
+ break;
+ }
+ /* The return value is only used for DLL_PROCESS_ATTACH; all other
+ conditions are ignored. */
+ return TRUE; // successful DLL_PROCESS_ATTACH
+}
+#else
+int CALLBACK
+LibMain( HINSTANCE hinst, WORD wDataSeg, WORD cbHeapSize, LPSTR lpszCmdLine )
+{
+ /*UnlockData( 0 );*/
+ return( 1 );
+}
+#endif
+
+#ifdef LDAP_DEBUG
+#ifndef _WIN32
+#include <stdarg.h>
+#include <stdio.h>
+
+void LDAPDebug( int level, char* fmt, ... )
+{
+ static char debugBuf[1024];
+
+ if (module_ldap_debug && (*module_ldap_debug & level))
+ {
+ va_list ap;
+ va_start (ap, fmt);
+ _snprintf (debugBuf, sizeof(debugBuf), fmt, ap);
+ va_end (ap);
+
+ OutputDebugString (debugBuf);
+ }
+}
+#endif
+#endif
+
+#ifndef _WIN32
+
+/* The 16-bit version of the RTL does not implement perror() */
+
+#include <stdio.h>
+
+void perror( const char *msg )
+{
+ char buf[128];
+ wsprintf( buf, "%s: error %d\n", msg, WSAGetLastError()) ;
+ OutputDebugString( buf );
+}
+
+#endif
diff --git a/ldap/servers/plugins/chainingdb/libcb.def b/ldap/servers/plugins/chainingdb/libcb.def
new file mode 100644
index 00000000..71fe8482
--- /dev/null
+++ b/ldap/servers/plugins/chainingdb/libcb.def
@@ -0,0 +1,15 @@
+; BEGIN COPYRIGHT BLOCK
+; Copyright 2001 Sun Microsystems, Inc.
+; Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+; All rights reserved.
+; END COPYRIGHT BLOCK
+;
+;
+;
+DESCRIPTION 'Netscape Directory Server 7 Chaining Database Plugin'
+;CODE SHARED READ EXECUTE
+;DATA SHARED READ WRITE
+EXPORTS
+ chaining_back_init @1
+ plugin_init_debug_level @2
+ cb_be_state_change @3
diff --git a/ldap/servers/plugins/collation/Makefile b/ldap/servers/plugins/collation/Makefile
new file mode 100644
index 00000000..14619af7
--- /dev/null
+++ b/ldap/servers/plugins/collation/Makefile
@@ -0,0 +1,99 @@
+#
+# BEGIN COPYRIGHT BLOCK
+# Copyright 2001 Sun Microsystems, Inc.
+# Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+# All rights reserved.
+# END COPYRIGHT BLOCK
+#
+LDAP_SRC= ../../..
+MCOM_ROOT= ../../../../..
+
+NOSTDCLEAN=true # don't let nsconfig.mk define target clean
+NOSTDSTRIP=true # don't let nsconfig.mk define target strip
+NSPR20=true # probably should be defined somewhere else (not sure where)
+
+OBJDEST= $(OBJDIR)/lib/liblcoll
+LIBDIR= $(LIB_RELDIR)
+
+include $(MCOM_ROOT)/ldapserver/nsconfig.mk
+include $(LDAP_SRC)/nsldap.mk
+
+INCLUDES+= -I../../slapd -I../../../include
+CFLAGS+=$(SLCFLAGS) -DSLAPD_LOGGING
+
+COLLATION_OBJS= collate.o config.o orfilter.o
+
+ifeq ($(ARCH), WINNT)
+COLLATION_OBJS+= debug.o
+COLLATION_DLL_OBJ=$(addprefix $(OBJDEST)/, dllmain.o)
+DEF_FILE:=./collation.def
+EXTRA_LIBS+= $(NSPRLINK) $(LDAP_SDK_LIBLDAP_DLL) $(LIBSLAPD)
+EXTRA_LIBS_DEP+= $(LIBSLAPD_DEP)
+EXTRA_LIBS_DEP+=$(LDAPSDK_DEP)
+endif
+
+# INCLUDES+= -I. -I$(ACLINC) -I$(MCOM_ROOT)/ldapserver/lib
+
+# ICU stuff
+INCLUDES+= $(ICU_INCLUDE)
+EXTRA_LIBS+=$(ICULINK)
+
+ifeq ($(ARCH), HPUX)
+EXTRA_LIBS_DEP += $(LDAPSDK_DEP) $(NSPR_DEP) $(SECURITY_DEP)
+EXTRA_LIBS += $(DYN_NSHTTPD) $(ADMINUTIL_LINK) $(LDAPLINK) $(SECURITYLINK) $(NSPRLINK) $(ICULINK)
+endif
+
+ifeq ($(ARCH), AIX)
+EXTRA_LIBS+= $(LIBSLAPDLINK) $(NSPRLINK) $(LDAPLINK)
+EXTRA_LIBS_DEP+= $(LIBSLAPD_DEP)
+EXTRA_LIBS_DEP+=$(LDAPSDK_DEP)
+LD=ld
+endif
+
+OBJS= $(addprefix $(OBJDEST)/, $(COLLATION_OBJS))
+COLLATION= $(addprefix $(LIBDIR)/, $(COLLATION_DLL).$(DLL_SUFFIX))
+
+clientSDK:
+
+all: $(OBJDEST) $(LIBDIR) $(COLLATION)
+ifeq (0, 1)
+# Where the heck did the compiler options come from?
+ @echo ARCH=$(ARCH)
+ @echo DEBUG=$(DEBUG)
+ @echo BUILD_OPT=$(BUILD_OPT)
+ @echo CFLAGS=$(CFLAGS)
+ @echo " MCC_DEBUG="$(MCC_DEBUG)
+ @echo " PLATFORMCFLAGS="$(PLATFORMCFLAGS)
+ @echo " ACFLAGS="$(ACFLAGS)
+ @echo " EXTRACFLAGS="$(EXTRACFLAGS)
+ @echo " UNPROTOCFLAGS="$(UNPROTOCFLAGS)
+ @echo " SLCFLAGS="$(SLCFLAGS)
+ @echo "ALDFLAGS="$(ALDFLAGS)
+ @echo "DLL_LDFLAGS="$(DLL_LDFLAGS)
+ @echo "DLL_EXPORT_FLAGS="$(DLL_EXPORT_FLAGS)
+endif
+
+ifeq ($(ARCH), WINNT)
+$(COLLATION): $(OBJS) $(COLLATION_DLL_OBJ) $(EXTRA_LIBS_DEP) $(DEF_FILE)
+ $(LINK_DLL) $(COLLATION_DLL_OBJ) $(EXTRA_LIBS) /DEF:$(DEF_FILE)
+else
+ifeq ($(ARCH), AIX)
+$(COLLATION): $(OBJS) $(COLLATION_DLL_OBJ) $(EXTRA_LIBS_DEP)
+ $(LINK_DLL) $(COLLATION_DLL_OBJ) $(EXTRA_LIBS)
+else
+$(COLLATION): $(OBJS) $(EXTRA_LIBS_DEP)
+ $(LINK_DLL) $(EXTRA_LIBS)
+endif
+endif
+
+veryclean: clean
+
+clean:
+ $(RM) $(OBJS)
+ifeq ($(ARCH), WINNT)
+ $(RM) $(COLLATION_DLL_OBJ)
+endif
+ $(RM) $(COLLATION)
+
+$(OBJDEST):
+ $(MKDIR) $(OBJDEST)
diff --git a/ldap/servers/plugins/collation/collate.c b/ldap/servers/plugins/collation/collate.c
new file mode 100644
index 00000000..603caf53
--- /dev/null
+++ b/ldap/servers/plugins/collation/collate.c
@@ -0,0 +1,454 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+/* collate.c - implementation of indexing, using a Collation */
+
+#include "collate.h"
+#include <string.h> /* memcpy */
+
+#include <unicode/ucol.h> /* Collation */
+#include <unicode/ucnv.h> /* Conversion */
+#include <unicode/ustring.h> /* UTF8 conversion */
+
+#include <ldap.h> /* LDAP_UTF8LEN */
+#include <slap.h> /* for strcasecmp on non-UNIX platforms and correct debug macro */
+
+void
+collation_init( char *configpath )
+ /* Called once per process, to initialize globals. */
+{
+ /* ICU needs no initialization? */
+}
+
+typedef struct coll_profile_t { /* Collator characteristics */
+ const char* language;
+ const char* country;
+ const char* variant;
+ UColAttributeValue strength; /* one of UCOL_PRIMARY = 0, UCOL_SECONDARY = 1, UCOL_TERTIARY = 2, UCOL_QUATERNARY = 3, UCOL_IDENTICAL = 4 */
+ UColAttributeValue decomposition; /* one of UCOL_OFF = 0, UCOL_DEFAULT = 1, UCOL_ON = 2 */
+} coll_profile_t;
+
+typedef struct coll_id_t { /* associates an OID with a coll_profile_t */
+ char* oid;
+ coll_profile_t* profile;
+} coll_id_t;
+
+/* A list of all OIDs that identify collator profiles: */
+static const coll_id_t** collation_id = NULL;
+static size_t collation_ids = 0;
+
+int
+collation_config (size_t cargc, char** cargv,
+ const char* fname, size_t lineno)
+ /* Process one line from a configuration file.
+ Return 0 if it's OK, -1 if it's not recognized.
+ Any other return value is a process exit code.
+ */
+{
+ if (cargc <= 0) { /* Bizarre. Oh, well... */
+ } else if (!strcasecmp (cargv[0], "NLS")) {
+ /* ignore - not needed anymore with ICU - was used to get path for NLS_Initialize */
+ } else if (!strcasecmp (cargv[0], "collation")) {
+ if ( cargc < 7 ) {
+ LDAPDebug (LDAP_DEBUG_ANY,
+ "%s: line %lu ignored: only %lu arguments (expected "
+ "collation language country variant strength decomposition oid ...)\n",
+ fname, (unsigned long)lineno, (unsigned long)cargc );
+ } else {
+ auto size_t arg;
+ auto coll_profile_t* profile = (coll_profile_t*) slapi_ch_calloc (1, sizeof (coll_profile_t));
+ if (*cargv[1]) profile->language = slapi_ch_strdup (cargv[1]);
+ if (*cargv[2]) profile->country = slapi_ch_strdup (cargv[2]);
+ if (*cargv[3]) profile->variant = slapi_ch_strdup (cargv[3]);
+ switch (atoi(cargv[4])) {
+ case 1: profile->strength = UCOL_PRIMARY; break;
+ case 2: profile->strength = UCOL_SECONDARY; /* no break here? fall through? wtf? */
+ case 3: profile->strength = UCOL_TERTIARY; break;
+ case 4: profile->strength = UCOL_IDENTICAL; break;
+ default: profile->strength = UCOL_SECONDARY;
+ LDAPDebug (LDAP_DEBUG_ANY,
+ "%s: line %lu: strength \"%s\" not supported (will use 2)\n",
+ fname, (unsigned long)lineno, cargv[4]);
+ break;
+ }
+ switch (atoi(cargv[5])) {
+ case 1: profile->decomposition = UCOL_OFF; break;
+ case 2: profile->decomposition = UCOL_DEFAULT; /* no break here? fall through? wtf? */
+ case 3: profile->decomposition = UCOL_ON; break;
+ default: profile->decomposition = UCOL_DEFAULT;
+ LDAPDebug (LDAP_DEBUG_ANY,
+ "%s: line %lu: decomposition \"%s\" not supported (will use 2)\n",
+ fname, (unsigned long)lineno, cargv[5]);
+ break;
+ }
+
+ {
+ char descStr[256];
+ char nameOrder[256];
+ char nameSubstring[256];
+ char oidString[256];
+ char *tmpStr=NULL;
+ Slapi_MatchingRuleEntry *mrentry=slapi_matchingrule_new();
+
+ if(UCOL_PRIMARY == profile->strength) {
+ strcpy(nameOrder,"caseIgnoreOrderingMatch");
+ strcpy(nameSubstring,"caseIgnoreSubstringMatch");
+ }
+ else {
+ strcpy(nameOrder,"caseExactOrderingMatch");
+ strcpy(nameSubstring,"caseExactSubstringMatch");
+ }
+
+ if(cargc > 7) {
+ strcat(nameOrder,"-");
+ strcat(nameOrder,cargv[7]);
+ strcat(nameSubstring,"-");
+ strcat(nameSubstring,cargv[7]);
+ slapi_matchingrule_set(mrentry,SLAPI_MATCHINGRULE_NAME,
+ (void *)slapi_ch_strdup(nameOrder));
+ }
+ else {
+ if(0 != cargv[1][0]) {
+ strcat(nameOrder,"-");
+ strcat(nameSubstring,"-");
+ }
+ strcat(nameOrder,cargv[1]);
+ strcat(nameSubstring,cargv[1]);
+ slapi_matchingrule_set(mrentry,SLAPI_MATCHINGRULE_NAME,
+ (void *)slapi_ch_strdup(nameOrder));
+ }
+ strcpy(oidString,cargv[6]);
+ slapi_matchingrule_set(mrentry,SLAPI_MATCHINGRULE_OID,
+ (void *)slapi_ch_strdup(oidString));
+ if(0 != cargv[2][0]) {
+ sprintf(descStr,"%s-%s",cargv[1],cargv[2]);
+ }
+ else {
+ strcpy(descStr,cargv[1]);
+ }
+ slapi_matchingrule_set(mrentry,SLAPI_MATCHINGRULE_DESC,
+ (void *)slapi_ch_strdup(descStr));
+ slapi_matchingrule_set(mrentry,SLAPI_MATCHINGRULE_SYNTAX,
+ (void *)slapi_ch_strdup(DIRSTRING_SYNTAX_OID));
+ slapi_matchingrule_register(mrentry);
+ slapi_matchingrule_get(mrentry,SLAPI_MATCHINGRULE_NAME,
+ (void *)&tmpStr);
+ slapi_ch_free((void **)&tmpStr);
+ slapi_matchingrule_get(mrentry,SLAPI_MATCHINGRULE_OID,
+ (void *)&tmpStr);
+ slapi_ch_free((void **)&tmpStr);
+ slapi_matchingrule_set(mrentry,SLAPI_MATCHINGRULE_NAME,
+ (void *)slapi_ch_strdup(nameSubstring));
+ strcat(oidString,".6");
+ slapi_matchingrule_set(mrentry,SLAPI_MATCHINGRULE_OID,
+ (void *)slapi_ch_strdup(oidString));
+ slapi_matchingrule_register(mrentry);
+ slapi_matchingrule_free(&mrentry,1);
+ }
+
+
+ for (arg = 6; arg < cargc; ++arg) {
+ auto coll_id_t* id = (coll_id_t*) slapi_ch_malloc (sizeof (coll_id_t));
+ id->oid = slapi_ch_strdup (cargv[arg]);
+ id->profile = profile;
+ if (collation_ids <= 0) {
+ collation_id = (const coll_id_t**) slapi_ch_malloc (2 * sizeof (coll_id_t*));
+ } else {
+ collation_id = (const coll_id_t**) slapi_ch_realloc
+ ((void*)collation_id, (collation_ids + 2) * sizeof (coll_id_t*));
+ }
+ collation_id [collation_ids++] = id;
+ collation_id [collation_ids] = NULL;
+ }
+ }
+ } else {
+ return -1; /* unrecognized */
+ }
+ return 0; /* success */
+}
+
+typedef struct collation_indexer_t
+ /* A kind of indexer, implemented using an ICU Collator */
+{
+ UCollator* collator;
+ UConverter* converter;
+ struct berval** ix_keys;
+ int is_default_collator;
+} collation_indexer_t;
+
+/*
+ Caller must ensure that U == NULL and Ulen == 0 the first time called
+*/
+static UErrorCode
+SetUnicodeStringFromUTF_8 (UChar** U, int32_t* Ulen, int *isAlloced, const struct berval* bv)
+ /* Copy the UTF-8 string bv into the UnicodeString U,
+ but remove leading and trailing whitespace, and
+ convert consecutive whitespaces into a single space.
+ Ulen is set to the number of UChars in the array (not necessarily the number of bytes!)
+ */
+{
+ size_t n;
+ int32_t len = 0; /* length of non-space string */
+ int32_t needLen = 0; /* number of bytes needed for string */
+ UErrorCode err = U_ZERO_ERROR;
+ const char* s = bv->bv_val;
+ const char* begin = NULL; /* will point to beginning of non-space in val */
+ const char* end = NULL; /* will point to the first space after the last non-space char in val */
+ int32_t nUchars = 0;
+
+ if (!bv->bv_len) { /* no value? */
+ return U_INVALID_FORMAT_ERROR; /* don't know what else to use here */
+ }
+
+ /* first, set s to the first non-space char in bv->bv_val */
+ for (n = 0; (n < bv->bv_len) && ldap_utf8isspace((char *)s); ) { /* cast away const */
+ const char *next = LDAP_UTF8NEXT((char *)s); /* cast away const */
+ n += (next - s); /* count bytes, not chars */
+ s = next;
+ }
+ begin = s; /* begin points to first non-space char in val */
+
+ if (n >= bv->bv_len) { /* value is all spaces? */
+ return U_INVALID_FORMAT_ERROR; /* don't know what else to use here */
+ }
+
+ s = bv->bv_val + (bv->bv_len-1); /* move s to last char of bv_val */
+ end = s; /* end points at last char of bv_val - may change below */
+ /* find the last non-null and non-space char of val */
+ for (n = bv->bv_len; (n > 0) && (!*s || ldap_utf8isspace((char *)s));) {
+ const char *prev = LDAP_UTF8PREV((char *)s);
+ end = prev;
+ n -= (s - prev); /* count bytes, not chars */
+ s = prev;
+ }
+
+ /* end now points at last non-null/non-space of val */
+ if (n < 0) { /* bogus */
+ return U_INVALID_FORMAT_ERROR; /* don't know what else to use here */
+ }
+
+ len = LDAP_UTF8NEXT((char *)end) - begin;
+
+ u_strFromUTF8(*U, *Ulen, &nUchars, begin, len, &err);
+ if (nUchars > *Ulen) { /* need more space */
+ if (*isAlloced) { /* realloc space */
+ *U = (UChar *)slapi_ch_realloc((char *)*U, sizeof(UChar) * nUchars);
+ } else { /* must use malloc */
+ *U = (UChar *)slapi_ch_malloc(sizeof(UChar) * nUchars);
+ *isAlloced = 1; /* no longer using fixed buffer */
+ }
+ *Ulen = nUchars;
+ err = U_ZERO_ERROR; /* reset */
+ u_strFromUTF8(*U, *Ulen, NULL, begin, len, &err);
+ } else {
+ *Ulen = nUchars;
+ }
+
+ return err;
+}
+
+static struct berval**
+collation_index (indexer_t* ix, struct berval** bvec, struct berval** prefixes)
+{
+ collation_indexer_t* etc = (collation_indexer_t*) ix->ix_etc;
+ struct berval** keys = NULL;
+ if (bvec) {
+ char keyBuffer[128]; /* try to use static space buffer to avoid malloc */
+ int32_t keyLen = sizeof(keyBuffer);
+ char* key = keyBuffer; /* but key can grow if necessary */
+ size_t keyn = 0;
+ struct berval** bv;
+ UChar charBuffer[128]; /* try to use static space buffer */
+ int32_t nChars = sizeof(charBuffer)/sizeof(UChar); /* but grow if necessary */
+ UChar *chars = charBuffer; /* try to reuse this */
+ int isAlloced = 0; /* using fixed buffer */
+
+ for (bv = bvec; *bv; ++bv) {
+ /* if chars is allocated, nChars will be the capacity and the number of chars in chars */
+ /* otherwise, nChars will be the number of chars, which may be less than the capacity */
+ if (!isAlloced) {
+ nChars = sizeof(charBuffer)/sizeof(UChar); /* reset */
+ }
+ if (U_ZERO_ERROR == SetUnicodeStringFromUTF_8 (&chars, &nChars, &isAlloced, *bv)) {
+ /* nChars is now the number of UChar in chars, which may be less than the
+ capacity of charBuffer if not allocated */
+ struct berval* prefix = prefixes ? prefixes[bv-bvec] : NULL;
+ const size_t prefixLen = prefix ? prefix->bv_len : 0;
+ struct berval* bk = NULL;
+ int32_t realLen; /* real length of key, not keyLen which is buffer size */
+
+ /* try to get the sort key using key and keyLen; only grow key
+ if we need to */
+ /* can use -1 for char len since the conversion from UTF8
+ null terminates the string */
+ realLen = ucol_getSortKey(etc->collator, chars, nChars, (uint8_t *)key, keyLen);
+ if (realLen > keyLen) { /* need more space */
+ if (key == keyBuffer) {
+ key = (char*)slapi_ch_malloc(sizeof(char) * realLen);
+ } else {
+ key = (char*)slapi_ch_realloc(key, sizeof(char) * realLen);
+ }
+ keyLen = ucol_getSortKey(etc->collator, chars, nChars, (uint8_t *)key, realLen);
+ }
+ if (realLen > 0) {
+ bk = (struct berval*) slapi_ch_malloc (sizeof(struct berval));
+
+ bk->bv_len = prefixLen + realLen;
+ bk->bv_val = slapi_ch_malloc (bk->bv_len + 1);
+ if (prefixLen) {
+ memcpy(bk->bv_val, prefix->bv_val, prefixLen);
+ }
+ memcpy(bk->bv_val + prefixLen, key, realLen);
+ bk->bv_val[bk->bv_len] = '\0';
+ LDAPDebug (LDAP_DEBUG_FILTER, "collation_index(%.*s) %lu bytes\n",
+ bk->bv_len, bk->bv_val, (unsigned long)bk->bv_len);
+ keys = (struct berval**)
+ slapi_ch_realloc ((void*)keys, sizeof(struct berval*) * (keyn + 2));
+ keys[keyn++] = bk;
+ keys[keyn] = NULL;
+ }
+ }
+ }
+ if (chars != charBuffer) { /* realloc'ed, need to free */
+ slapi_ch_free((void **)&chars);
+ }
+ if (key != keyBuffer) { /* realloc'ed, need to free */
+ slapi_ch_free_string(&key);
+ }
+ }
+ if (etc->ix_keys != NULL) ber_bvecfree (etc->ix_keys);
+ etc->ix_keys = keys;
+ return keys;
+}
+
+static void
+collation_indexer_destroy (indexer_t* ix)
+ /* The destructor function for a collation-based indexer. */
+{
+ collation_indexer_t* etc = (collation_indexer_t*) ix->ix_etc;
+ if (etc->converter) {
+ ucnv_close(etc->converter);
+ etc->converter = NULL;
+ }
+ if (!etc->is_default_collator) {
+ /* Don't delete the default collation - it seems to cause problems */
+ ucol_close(etc->collator);
+ etc->collator = NULL;
+ }
+ if (etc->ix_keys != NULL) {
+ ber_bvecfree (etc->ix_keys);
+ etc->ix_keys = NULL;
+ }
+ slapi_ch_free((void**)&ix->ix_etc);
+ ix->ix_etc = NULL; /* just for hygiene */
+}
+
+static UErrorCode
+s_newNamedLocaleFromComponents(char **locale, const char *lang, const char *country, const char *variant)
+{
+ UErrorCode err = U_ZERO_ERROR;
+ int hasLang = (lang && *lang);
+ int hasC = (country && *country);
+ int hasVar = (variant && *variant);
+
+ *locale = NULL;
+ if (hasLang) {
+ *locale = PR_smprintf("%s%s%s%s%s", lang, (hasC ? "_" : ""), (hasC ? country : ""),
+ (hasVar ? "_" : ""), (hasVar ? variant : ""));
+ } else {
+ err = U_INVALID_FORMAT_ERROR; /* don't know what else to use here */
+ }
+
+ return err;
+}
+
+indexer_t*
+collation_indexer_create (const char* oid)
+ /* Return a new indexer, based on the collation identified by oid.
+ Return NULL if this can't be done.
+ */
+{
+ indexer_t* ix = NULL;
+ const coll_id_t** id = collation_id;
+ char* locale = NULL; /* NULL == default locale */
+ if (id) for (; *id; ++id) {
+ if (!strcasecmp (oid, (*id)->oid)) {
+ const coll_profile_t* profile = (*id)->profile;
+ const int is_default = (profile->language == NULL &&
+ profile->country == NULL &&
+ profile->variant == NULL);
+ UErrorCode err = U_ZERO_ERROR;
+ if ( ! is_default) {
+ if (locale) {
+ PR_smprintf_free(locale);
+ locale = NULL;
+ }
+ err = s_newNamedLocaleFromComponents(&locale,
+ profile->language,
+ profile->country,
+ profile->variant);
+ }
+ if (err == U_ZERO_ERROR) {
+ UCollator* coll = ucol_open(locale, &err);
+ /*
+ * If we found exactly the right collator for this locale,
+ * or if we found a fallback one, or if we are happy with
+ * the default, use it.
+ */
+ if (err == U_ZERO_ERROR || err == U_USING_FALLBACK_WARNING ||
+ (err == U_USING_DEFAULT_WARNING && is_default)) {
+ collation_indexer_t* etc = (collation_indexer_t*)
+ slapi_ch_calloc (1, sizeof (collation_indexer_t));
+ ix = (indexer_t*) slapi_ch_calloc (1, sizeof (indexer_t));
+ ucol_setAttribute (coll, UCOL_STRENGTH, profile->strength, &err);
+ if (err != U_ZERO_ERROR) {
+ LDAPDebug (LDAP_DEBUG_ANY, "collation_indexer_create: could not "
+ "set the collator strength for oid %s to %d: err %d\n",
+ oid, profile->strength, err);
+ }
+ ucol_setAttribute (coll, UCOL_DECOMPOSITION_MODE, profile->decomposition, &err);
+ if (err != U_ZERO_ERROR) {
+ LDAPDebug (LDAP_DEBUG_ANY, "collation_indexer_create: could not "
+ "set the collator decomposition mode for oid %s to %d: err %d\n",
+ oid, profile->decomposition, err);
+ }
+ etc->collator = coll;
+ etc->is_default_collator = is_default;
+ for (id = collation_id; *id; ++id) {
+ if ((*id)->profile == profile) {
+ break; /* found the 'official' id */
+ }
+ }
+ ix->ix_etc = etc;
+ ix->ix_oid = (*id)->oid;
+ ix->ix_index = collation_index;
+ ix->ix_destroy = collation_indexer_destroy;
+ break; /* return */
+ /* free (etc); */
+ /* free (ix); */
+ } else if (err == U_USING_DEFAULT_WARNING) {
+ LDAPDebug (LDAP_DEBUG_FILTER, "collation_indexer_create: could not "
+ "create an indexer for OID %s for locale %s and could not "
+ "use default locale\n",
+ oid, (locale ? locale : "(default)"), NULL);
+ } else { /* error */
+ LDAPDebug (LDAP_DEBUG_FILTER, "collation_indexer_create: could not "
+ "create an indexer for OID %s for locale %s: err = %d\n",
+ oid, (locale ? locale : "(default)"), err);
+ }
+ if (coll) {
+ ucol_close (coll);
+ coll = NULL;
+ }
+ }
+ break; /* failed to create the specified collator */
+ }
+ }
+ if (locale) {
+ PR_smprintf_free(locale);
+ locale = NULL;
+ }
+ return ix;
+}
diff --git a/ldap/servers/plugins/collation/collate.h b/ldap/servers/plugins/collation/collate.h
new file mode 100644
index 00000000..29e51022
--- /dev/null
+++ b/ldap/servers/plugins/collation/collate.h
@@ -0,0 +1,36 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+
+#ifndef _COLLATE_H_
+#define _COLLATE_H_
+
+#include <stddef.h> /* size_t */
+#include <dirlite_strings.h> /* PLUGIN_MAGIC_VENDOR_STR */
+
+struct indexer_t;
+
+typedef void (*ix_destroy_t) (struct indexer_t*);
+typedef struct berval** (*ix_index_t) (struct indexer_t*, struct berval** values,
+ struct berval** prefixes /* inserted into each key */);
+
+typedef struct indexer_t
+{
+ char* ix_oid;
+ ix_index_t ix_index; /* map values to index keys */
+ ix_destroy_t ix_destroy;
+ void* ix_etc; /* whatever state the implementation needs */
+} indexer_t;
+
+extern void
+collation_init( char *configpath );
+
+extern int
+collation_config (size_t argc, char** argv, const char* fname, size_t lineno);
+
+extern indexer_t*
+collation_indexer_create (const char* oid);
+
+#endif
diff --git a/ldap/servers/plugins/collation/collation.def b/ldap/servers/plugins/collation/collation.def
new file mode 100644
index 00000000..bd5d531b
--- /dev/null
+++ b/ldap/servers/plugins/collation/collation.def
@@ -0,0 +1,10 @@
+; BEGIN COPYRIGHT BLOCK
+; Copyright 2001 Sun Microsystems, Inc.
+; Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+; All rights reserved.
+; END COPYRIGHT BLOCK
+;
+DESCRIPTION 'Netscape Directory Server 7 Collation Plugin'
+EXPORTS
+ orderingRule_init @2
+ plugin_init_debug_level @3
diff --git a/ldap/servers/plugins/collation/config.c b/ldap/servers/plugins/collation/config.c
new file mode 100644
index 00000000..ef3e66cf
--- /dev/null
+++ b/ldap/servers/plugins/collation/config.c
@@ -0,0 +1,178 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+#include "collate.h"
+#include "config.h"
+#include <stdio.h>
+#include <string.h>
+#include "slap.h"
+
+#define MAXARGS 16
+
+static char *
+strtok_quote( char *line, char *sep )
+{
+ int inquote;
+ char *tmp, *d;
+ static char *next;
+
+ if ( line != NULL ) {
+ next = line;
+ }
+ while ( *next && strchr( sep, *next ) ) {
+ next++;
+ }
+
+ if ( *next == '\0' ) {
+ next = NULL;
+ return( NULL );
+ }
+
+ d = tmp = next;
+ for ( inquote = 0; *next; next++ ) {
+ switch ( *next ) {
+ case '"':
+ if ( inquote ) {
+ inquote = 0;
+ } else {
+ inquote = 1;
+ }
+ break;
+
+#ifndef _WIN32
+ case '\\':
+ *d++ = *++next;
+ break;
+#endif
+
+ default:
+ if ( ! inquote ) {
+ if ( strchr( sep, *next ) != NULL ) {
+ *d++ = '\0';
+ next++;
+ return( tmp );
+ }
+ }
+ *d++ = *next;
+ break;
+ }
+ }
+ *d = '\0';
+
+ return( tmp );
+}
+
+static void
+fp_parse_line(
+ char *line,
+ int *argcp,
+ char **argv
+)
+{
+ char * token;
+
+ *argcp = 0;
+ for ( token = strtok_quote( line, " \t" ); token != NULL;
+ token = strtok_quote( NULL, " \t" ) ) {
+ if ( *argcp == MAXARGS ) {
+ LDAPDebug( LDAP_DEBUG_ANY, "Too many tokens (max %d)\n",
+ MAXARGS, 0, 0 );
+ exit( 1 );
+ }
+ argv[(*argcp)++] = token;
+ }
+ argv[*argcp] = NULL;
+}
+
+static char buf[BUFSIZ];
+static char *line;
+static int lmax, lcur;
+
+static void
+fp_getline_init( int *lineno )
+{
+ *lineno = -1;
+ buf[0] = '\0';
+}
+
+#define CATLINE( buf ) { \
+ int len; \
+ len = strlen( buf ); \
+ while ( lcur + len + 1 > lmax ) { \
+ lmax += BUFSIZ; \
+ line = (char *) slapi_ch_realloc( line, lmax ); \
+ } \
+ strcpy( line + lcur, buf ); \
+ lcur += len; \
+}
+
+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 ) {
+ if ( (p = strchr( buf, '\n' )) != NULL ) {
+ *p = '\0';
+ }
+ if ( ! isspace( buf[0] ) ) {
+ return( line );
+ }
+
+ CATLINE( buf );
+ (*lineno)++;
+ }
+ buf[0] = '\0';
+
+ return( line[0] ? line : NULL );
+}
+
+void
+collation_read_config( char *fname )
+{
+ FILE* fp;
+ char* line;
+ int cargc;
+ char* cargv[MAXARGS];
+ int lineno;
+
+ fp = fopen( fname, "r" );
+ if ( fp == NULL ) {
+ LDAPDebug( LDAP_DEBUG_ANY,
+ "could not open config file \"%s\" - absolute path?\n",
+ fname, 0, 0 );
+ return; /* Do not exit */
+ }
+
+ LDAPDebug( LDAP_DEBUG_CONFIG, "reading config file %s\n", fname, 0, 0 );
+
+ fp_getline_init( &lineno );
+ while ( (line = fp_getline( fp, &lineno )) != NULL ) {
+ /* skip comments and blank lines */
+ if ( line[0] == '#' || line[0] == '\0' ) {
+ continue;
+ }
+ LDAPDebug( LDAP_DEBUG_CONFIG, "line %d: %s\n", lineno, line, 0 );
+ fp_parse_line( line, &cargc, cargv );
+ if ( cargc < 1 ) {
+ LDAPDebug( LDAP_DEBUG_ANY,
+ "%s: line %d: bad config line (ignored)\n",
+ fname, lineno, 0 );
+ continue;
+ }
+ collation_config (cargc, cargv, fname, lineno);
+ }
+ fclose(fp);
+}
diff --git a/ldap/servers/plugins/collation/config.h b/ldap/servers/plugins/collation/config.h
new file mode 100644
index 00000000..1a4aef66
--- /dev/null
+++ b/ldap/servers/plugins/collation/config.h
@@ -0,0 +1,12 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+#ifndef _COLL_CONFIG_H_
+#define _COLL_CONFIG_H_
+
+extern void
+collation_read_config( char *fname );
+
+#endif
diff --git a/ldap/servers/plugins/collation/debug.c b/ldap/servers/plugins/collation/debug.c
new file mode 100644
index 00000000..99266a33
--- /dev/null
+++ b/ldap/servers/plugins/collation/debug.c
@@ -0,0 +1,14 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+#ifdef _WIN32
+int *module_ldap_debug = 0;
+
+void
+plugin_init_debug_level (int *level_ptr)
+{
+ module_ldap_debug = level_ptr;
+}
+#endif
diff --git a/ldap/servers/plugins/collation/dllmain.c b/ldap/servers/plugins/collation/dllmain.c
new file mode 100644
index 00000000..fa158500
--- /dev/null
+++ b/ldap/servers/plugins/collation/dllmain.c
@@ -0,0 +1,129 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+/*
+ * Microsoft Windows specifics for collation DLL
+ */
+#include "ldap.h"
+
+#ifdef _WIN32
+/* Lifted from Q125688
+ * How to Port a 16-bit DLL to a Win32 DLL
+ * on the MSVC 4.0 CD
+ */
+BOOL WINAPI DllMain (HANDLE hModule, DWORD fdwReason, LPVOID lpReserved)
+{
+ WSADATA wsadata;
+
+ switch (fdwReason)
+ {
+ case DLL_PROCESS_ATTACH:
+ /* Code from LibMain inserted here. Return TRUE to keep the
+ DLL loaded or return FALSE to fail loading the DLL.
+
+ You may have to modify the code in your original LibMain to
+ account for the fact that it may be called more than once.
+ You will get one DLL_PROCESS_ATTACH for each process that
+ loads the DLL. This is different from LibMain which gets
+ called only once when the DLL is loaded. The only time this
+ is critical is when you are using shared data sections.
+ If you are using shared data sections for statically
+ allocated data, you will need to be careful to initialize it
+ only once. Check your code carefully.
+
+ Certain one-time initializations may now need to be done for
+ each process that attaches. You may also not need code from
+ your original LibMain because the operating system may now
+ be doing it for you.
+ */
+ /*
+ * 16 bit code calls UnlockData()
+ * which is mapped to UnlockSegment in windows.h
+ * in 32 bit world UnlockData is not defined anywhere
+ * UnlockSegment is mapped to GlobalUnfix in winbase.h
+ * and the docs for both UnlockSegment and GlobalUnfix say
+ * ".. function is oboslete. Segments have no meaning
+ * in the 32-bit environment". So we do nothing here.
+ */
+
+ if( errno = WSAStartup(0x0101, &wsadata ) != 0 )
+ return FALSE;
+
+ break;
+
+ case DLL_THREAD_ATTACH:
+ /* Called each time a thread is created in a process that has
+ already loaded (attached to) this DLL. Does not get called
+ for each thread that exists in the process before it loaded
+ the DLL.
+
+ Do thread-specific initialization here.
+ */
+ break;
+
+ case DLL_THREAD_DETACH:
+ /* Same as above, but called when a thread in the process
+ exits.
+
+ Do thread-specific cleanup here.
+ */
+ break;
+
+ case DLL_PROCESS_DETACH:
+ /* Code from _WEP inserted here. This code may (like the
+ LibMain) not be necessary. Check to make certain that the
+ operating system is not doing it for you.
+ */
+ WSACleanup();
+
+ break;
+ }
+ /* The return value is only used for DLL_PROCESS_ATTACH; all other
+ conditions are ignored. */
+ return TRUE; // successful DLL_PROCESS_ATTACH
+}
+#else
+int CALLBACK
+LibMain( HINSTANCE hinst, WORD wDataSeg, WORD cbHeapSize, LPSTR lpszCmdLine )
+{
+ /*UnlockData( 0 );*/
+ return( 1 );
+}
+#endif
+
+#ifdef LDAP_DEBUG
+#ifndef _WIN32
+#include <stdarg.h>
+#include <stdio.h>
+
+void LDAPDebug( int level, char* fmt, ... )
+{
+ static char debugBuf[1024];
+
+ if (module_ldap_debug && (*module_ldap_debug & level))
+ {
+ va_list ap;
+ va_start (ap, fmt);
+ _snprintf (debugBuf, sizeof(debugBuf), fmt, ap);
+ va_end (ap);
+
+ OutputDebugString (debugBuf);
+ }
+}
+#endif
+#endif
+
+#ifndef _WIN32
+/* The 16-bit version of the RTL does not implement perror() */
+#include <stdio.h>
+
+void perror( const char *msg )
+{
+ char buf[128];
+ wsprintf( buf, "%s: error %d\n", msg, WSAGetLastError()) ;
+ OutputDebugString( buf );
+}
+
+#endif
diff --git a/ldap/servers/plugins/collation/orfilter.c b/ldap/servers/plugins/collation/orfilter.c
new file mode 100644
index 00000000..65222baa
--- /dev/null
+++ b/ldap/servers/plugins/collation/orfilter.c
@@ -0,0 +1,984 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+/* orfilter.c - implementation of ordering rule filter */
+
+#include <ldap.h> /* LDAP_UTF8INC */
+#include <slap.h> /* for debug macros */
+#include <slapi-plugin.h> /* slapi_berval_cmp, SLAPI_BERVAL_EQ */
+#include "collate.h" /* indexer_t, collation_xxx */
+#include "config.h" /* collation_read_config */
+#include "orfilter.h"
+
+#ifdef HPUX11
+#include <dl.h>
+#endif /* HPUX11 */
+
+static indexer_t*
+indexer_create (const char* oid)
+{
+ return collation_indexer_create (oid);
+}
+
+static void
+indexer_free (indexer_t* ix)
+{
+ if (ix->ix_destroy != NULL) {
+ ix->ix_destroy (ix);
+ }
+ slapi_ch_free((void**)&ix);
+}
+
+typedef struct or_filter_t {
+ /* implements a filter, using an indexer */
+ char* or_type;
+ int or_op; /* LDAPI_OP_xxx */
+ char* or_oid;
+ struct berval** or_values;
+ struct berval** or_match_keys;
+ struct berval** or_index_keys;
+ indexer_t* or_indexer; /* used to construct or_match_keys and or_index_keys */
+} or_filter_t;
+
+static or_filter_t*
+or_filter_get (Slapi_PBlock* pb)
+{
+ auto void* obj = NULL;
+ if ( ! slapi_pblock_get (pb, SLAPI_PLUGIN_OBJECT, &obj)) {
+ return (or_filter_t*)obj;
+ }
+ return NULL;
+}
+
+static int
+or_filter_destroy (Slapi_PBlock* pb)
+{
+ auto or_filter_t* or = or_filter_get (pb);
+ LDAPDebug (LDAP_DEBUG_FILTER, "or_filter_destroy(%p)\n", (void*)or, 0, 0);
+ if (or != NULL) {
+ slapi_ch_free((void**)&or->or_type);
+ slapi_ch_free((void**)&or->or_oid);
+ if (or->or_values != NULL) {
+ ber_bvecfree (or->or_values);
+ or->or_values = NULL;
+ }
+ if (or->or_match_keys != NULL) {
+ ber_bvecfree (or->or_match_keys);
+ or->or_match_keys = NULL;
+ }
+ if (or->or_index_keys != NULL) {
+ ber_bvecfree (or->or_index_keys);
+ or->or_index_keys = NULL;
+ }
+ if (or->or_indexer != NULL) {
+ indexer_free (or->or_indexer);
+ or->or_indexer = NULL;
+ }
+ slapi_ch_free((void**)&or);
+ }
+ return 0;
+}
+
+#define MAX_CHAR_COMBINING 3
+/* The maximum number of Unicode characters that may combine
+ to form a single collation element.
+*/
+
+static int
+ss_match (struct berval* value,
+ const struct berval* key0,
+ indexer_t* ix)
+/* returns: 0 a prefix of value matched key
+ * 1 a subsequent substring might match; try again
+ * -1 nothing in value will match; give up
+ */
+{
+ auto struct berval* vals[2];
+ auto struct berval val;
+ auto struct berval key;
+ auto size_t attempts = MAX_CHAR_COMBINING;
+
+ vals[0] = &val;
+ vals[1] = NULL;
+ val.bv_val = value->bv_val;
+ val.bv_len = 0;
+ key.bv_val = key0->bv_val;
+ key.bv_len = key0->bv_len - 1;
+ while (1) {
+ auto struct berval** vkeys = ix->ix_index (ix, vals, NULL);
+ if (vkeys && vkeys[0]) {
+ auto const struct berval* vkey = vkeys[0];
+ if (vkey->bv_len > key.bv_len) {
+ if (--attempts <= 0) {
+ break; /* No match at this starting point */
+ } /* else Try looking at another character;
+ it may combine, and produce a shorter key.
+ */
+ } else if (SLAPI_BERVAL_EQ (vkey, &key)) {
+ value->bv_len -= val.bv_len;
+ value->bv_val += val.bv_len;
+ return 0;
+ }
+ }
+ if (val.bv_len >= value->bv_len) {
+ break;
+ }
+ val.bv_len += LDAP_UTF8LEN (val.bv_val + val.bv_len);
+ }
+ if (value->bv_len > 0) {
+ auto size_t one = LDAP_UTF8LEN (value->bv_val);
+ value->bv_len -= one;
+ value->bv_val += one;
+ return 1;
+ }
+ return -1;
+}
+
+static int
+ss_filter_match (or_filter_t* or, struct berval** vals)
+/* returns: 0 filter matched
+ * -1 filter did not match
+ * >0 an LDAP error code
+ */
+{
+ auto int rc = -1; /* no match */
+ auto indexer_t* ix = or->or_indexer;
+ if (vals != NULL) for (; *vals; ++vals) {
+ auto struct berval v;
+ auto struct berval** k = or->or_match_keys;
+ if (k == NULL || *k == NULL) {
+ rc = 0; /* present */
+ break;
+ }
+ v.bv_len = (*vals)->bv_len;
+ v.bv_val = (*vals)->bv_val;
+ if ((*k)->bv_len > 0 && ss_match (&v, *k, ix) != 0) {
+ break; /* initial failed */
+ }
+ rc = 0; /* so far, so good */
+ while (*++k) {
+ if ((*k)->bv_len <= 0) {
+ rc = 0;
+ } else if (k[1]) { /* middle */
+ do {
+ rc = ss_match (&v, *k, ix);
+ } while (rc > 0);
+ if (rc < 0) {
+ break;
+ }
+ } else { /* final */
+ auto size_t attempts = MAX_CHAR_COMBINING;
+ auto char* limit = v.bv_val;
+ auto struct berval** vkeys;
+ auto struct berval* vals[2];
+ auto struct berval key;
+ rc = -1;
+ vals[0] = &v;
+ vals[1] = NULL;
+ key.bv_val = (*k)->bv_val;
+ key.bv_len = (*k)->bv_len - 1;
+ v.bv_val = (*vals)->bv_val + (*vals)->bv_len;
+ while(1) {
+ v.bv_len = (*vals)->bv_len - (v.bv_val - (*vals)->bv_val);
+ vkeys = ix->ix_index (ix, vals, NULL);
+ if (vkeys && vkeys[0]) {
+ auto const struct berval* vkey = vkeys[0];
+ if (vkey->bv_len > key.bv_len) {
+ if (--attempts <= 0) {
+ break;
+ } /* else Try looking at another character;
+ it may combine, and produce a shorter key.
+ */
+ } else if (SLAPI_BERVAL_EQ (vkey, &key)) {
+ rc = 0;
+ break;
+ }
+ }
+ if (v.bv_val <= limit) break;
+ LDAP_UTF8DEC (v.bv_val);
+ }
+ break;
+ }
+ }
+ if (rc != -1) break;
+ }
+ return rc;
+}
+
+static int
+op_filter_match (or_filter_t* or, struct berval** vals)
+{
+ auto indexer_t* ix = or->or_indexer;
+ auto struct berval** v = ix->ix_index (ix, vals, NULL);
+ if (v != NULL) for (; *v; ++v) {
+ auto struct berval** k = or->or_match_keys;
+ if (k != NULL) for (; *k; ++k) {
+ switch (or->or_op) {
+ case SLAPI_OP_LESS:
+ if (slapi_berval_cmp (*v, *k) < 0) return 0; break;
+ case SLAPI_OP_LESS_OR_EQUAL:
+ if (slapi_berval_cmp (*v, *k) <= 0) return 0; break;
+ case SLAPI_OP_EQUAL:
+ if (SLAPI_BERVAL_EQ (*v, *k)) return 0; break;
+ case SLAPI_OP_GREATER_OR_EQUAL:
+ if (slapi_berval_cmp (*v, *k) >= 0) return 0; break;
+ case SLAPI_OP_GREATER:
+ if (slapi_berval_cmp (*v, *k) > 0) return 0; break;
+ default:
+ break;
+ }
+ }
+ }
+ return -1;
+}
+
+static int
+or_filter_match (void* obj, Slapi_Entry* entry, Slapi_Attr* attr)
+/* returns: 0 filter matched
+ * -1 filter did not match
+ * >0 an LDAP error code
+ */
+{
+ auto int rc = -1; /* no match */
+ auto or_filter_t* or = (or_filter_t*)obj;
+ for (; attr != NULL; slapi_entry_next_attr (entry, attr, &attr)) {
+ auto char* type = NULL;
+ auto struct berval** vals = NULL;
+
+/*
+ * XXXmcs 1-March-2001: This code would perform better if it did not make
+ * a copy of the values here, but that would require re-writing the code
+ * in this file to use Slapi_ValueSet's instead of struct berval **'s
+ * (and that is not a small project).
+ */
+ if (!slapi_attr_get_type (attr, &type) && type != NULL &&
+ !slapi_attr_type_cmp (or->or_type, type, 2/*match subtypes*/) &&
+ !slapi_attr_get_bervals_copy(attr, &vals) && vals != NULL) {
+
+ if (or->or_op == SLAPI_OP_SUBSTRING) {
+ rc = ss_filter_match (or, vals);
+ } else {
+ rc = op_filter_match (or, vals);
+ }
+
+ ber_bvecfree( vals );
+ vals = NULL;
+ if (rc >= 0) break;
+ }
+ }
+ return rc;
+}
+
+#define WILDCARD '*'
+/* If you want a filter value to contain a non-wildcard '*' or '\'
+ you write "\2a" or "\5c" (the ASCII codes, in hexadecimal).
+ For example, "4\2a4*flim\5cflam"
+ matches a value that begins with "4*4" and ends with "flim\flam"
+ (except that all the "\" should be doubled in C string literals).
+ This conforms to <draft-ietf-asid-ldapv3-attributes-08> section 8.3.
+*/
+
+static void
+ss_unescape (struct berval* val)
+{
+ char* s = val->bv_val;
+ char* t = s;
+ char* limit = s + val->bv_len;
+ while (s < limit) {
+ if (!memcmp (s, "\\2a", 3) ||
+ !memcmp (s, "\\2A", 3)) {
+ *t++ = WILDCARD;
+ s += 3;
+ } else if (!memcmp (s, "\\5c", 3) ||
+ !memcmp (s, "\\5C", 3)) {
+ *t++ = '\\';
+ s += 3;
+ } else {
+ if (t == s) LDAP_UTF8INC (t);
+ else t += LDAP_UTF8COPY (t, s);
+ LDAP_UTF8INC (s);
+ }
+ }
+ val->bv_len = t - val->bv_val;
+}
+
+static struct berval*
+slapi_ch_bvdup0 (struct berval* val)
+ /* Return a copy of val, with a 0 byte following the end. */
+{
+ auto struct berval* result = (struct berval*)
+ slapi_ch_malloc (sizeof (struct berval));
+ result->bv_len = val->bv_len;
+ result->bv_val = slapi_ch_malloc (result->bv_len + 1);
+ if (result->bv_len > 0) {
+ memcpy (result->bv_val, val->bv_val, result->bv_len);
+ }
+ result->bv_val[result->bv_len] = '\0';
+ return result;
+}
+
+static struct berval*
+ss_filter_value (const char* s, const size_t len, struct berval* val)
+{
+ val->bv_len = len;
+ if (len > 0) memcpy (val->bv_val, s, len);
+ ss_unescape (val);
+ return slapi_ch_bvdup0 (val);
+}
+
+static struct berval**
+ss_filter_values (struct berval* pattern, int* query_op)
+ /* Split the pattern into its substrings and return them. */
+{
+ auto struct berval** result;
+ auto struct berval val;
+ auto size_t n;
+ auto char* s;
+ auto char* p;
+ auto char* plimit = pattern->bv_val + pattern->bv_len;
+
+ /* Compute the length of the result array, and
+ the maximum bv_len of any of its elements. */
+ val.bv_len = 0;
+ n = 2; /* one key, plus NULL terminator */
+ s = pattern->bv_val;
+ for (p = s; p < plimit; LDAP_UTF8INC(p)) {
+ switch (*p) {
+ case WILDCARD:
+ ++n;
+ {
+ auto const size_t len = (p - s);
+ if (val.bv_len < len)
+ val.bv_len = len;
+ }
+ while (++p != plimit && *p == WILDCARD);
+ s = p;
+ break;
+ default: break;
+ }
+ }
+ if (n == 2) { /* no wildcards in pattern */
+ auto struct berval** pvec = (struct berval**) slapi_ch_malloc (sizeof (struct berval*) * 2);
+ auto struct berval* pv = (struct berval*) slapi_ch_malloc (sizeof (struct berval));
+ pvec[0] = pv;
+ pvec[1] = NULL;
+ pv->bv_len = pattern->bv_len;
+ pv->bv_val = slapi_ch_malloc (pv->bv_len);
+ memcpy (pv->bv_val, pattern->bv_val, pv->bv_len);
+ ss_unescape (pv);
+ *query_op = SLAPI_OP_EQUAL;
+ return pvec;
+ } else if (n == 3 && pattern->bv_len <= 1) { /* entire pattern is one wildcard */
+ return NULL; /* presence */
+ }
+ {
+ auto const size_t len = (p - s);
+ if (val.bv_len < len)
+ val.bv_len = len;
+ }
+ result = (struct berval**) slapi_ch_malloc (n * sizeof (struct berval*));
+ val.bv_val = slapi_ch_malloc (val.bv_len);
+ n = 0;
+ s = pattern->bv_val;
+ for (p = s; p < plimit; LDAP_UTF8INC(p)) {
+ switch (*p) {
+ case WILDCARD:
+ result[n++] = ss_filter_value (s, p-s, &val);
+ while (++p != plimit && *p == WILDCARD);
+ s = p;
+ break;
+ default: break;
+ }
+ }
+ if (p != s || s == plimit) {
+ result[n++] = ss_filter_value (s, p-s, &val);
+ }
+ result[n] = NULL;
+ slapi_ch_free((void**)&val.bv_val);
+ return result;
+}
+
+static struct berval*
+ss_filter_key (indexer_t* ix, struct berval* val)
+{
+ struct berval* key = (struct berval*) slapi_ch_calloc (1, sizeof(struct berval));
+ if (val->bv_len > 0) {
+ struct berval** keys = NULL;
+ auto struct berval* vals[2];
+ vals[0] = val;
+ vals[1] = NULL;
+ keys = ix->ix_index (ix, vals, NULL);
+ if (keys && keys[0]) {
+ key->bv_len = keys[0]->bv_len + 1;
+ key->bv_val = slapi_ch_malloc (key->bv_len);
+ memcpy (key->bv_val, keys[0]->bv_val, keys[0]->bv_len);
+ key->bv_val[key->bv_len-1] = '\0';
+ }
+ }
+ return key;
+}
+
+static struct berval**
+ss_filter_keys (indexer_t* ix, struct berval** values)
+ /* Index the substrings and return the key values,
+ with an extra byte appended to each key, so that
+ an empty key definitely implies an absent value.
+ */
+{
+ auto struct berval** keys = NULL;
+ if (values != NULL) {
+ auto size_t n; /* how many substring values */
+ auto struct berval** val;
+ for (n=0,val=values; *val != NULL; ++n,++val);
+ keys = (struct berval**) slapi_ch_malloc ((n+1) * sizeof (struct berval*));
+ for (n=0,val=values; *val != NULL; ++n,++val) {
+ keys[n] = ss_filter_key (ix, *val);
+ }
+ keys[n] = NULL;
+ }
+ return keys;
+}
+
+static int or_filter_index (Slapi_PBlock* pb);
+
+static int
+or_filter_create (Slapi_PBlock* pb)
+{
+ auto int rc = LDAP_UNAVAILABLE_CRITICAL_EXTENSION; /* failed to initialize */
+ auto char* mrOID = NULL;
+ auto char* mrTYPE = NULL;
+ auto struct berval* mrVALUE = NULL;
+ auto or_filter_t* or = NULL;
+
+ if (!slapi_pblock_get (pb, SLAPI_PLUGIN_MR_OID, &mrOID) && mrOID != NULL &&
+ !slapi_pblock_get (pb, SLAPI_PLUGIN_MR_TYPE, &mrTYPE) && mrTYPE != NULL &&
+ !slapi_pblock_get (pb, SLAPI_PLUGIN_MR_VALUE, &mrVALUE) && mrVALUE != NULL) {
+ auto size_t len = mrVALUE->bv_len;
+ auto indexer_t* ix = NULL;
+ auto int op = SLAPI_OP_EQUAL;
+ auto struct berval bv;
+ auto int reusable = MRF_ANY_TYPE;
+
+ LDAPDebug (LDAP_DEBUG_FILTER, "=> or_filter_create(oid %s; type %s)\n",
+ mrOID, mrTYPE, 0);
+ if (len > 1 && (ix = indexer_create (mrOID)) != NULL) {
+ auto char* val = mrVALUE->bv_val;
+ switch (val[0]) {
+ case '=': break;
+ case '<': op = (val[1] == '=') ?
+ SLAPI_OP_LESS_OR_EQUAL : SLAPI_OP_LESS; break;
+ case '>': op = (val[1] == '=') ?
+ SLAPI_OP_GREATER_OR_EQUAL : SLAPI_OP_GREATER; break;
+ case WILDCARD: op = SLAPI_OP_SUBSTRING; break;
+ default:
+ break;
+ }
+ for (; len > 0 && *val != ' '; ++val, --len);
+ if (len > 0) ++val, --len; /* skip the space */
+ bv.bv_len = len;
+ bv.bv_val = (len > 0) ? val : NULL;
+ } else { /* mrOID does not identify an ordering rule. */
+ /* Is it an ordering rule OID with a relational operator suffix? */
+ auto size_t oidlen = strlen (mrOID);
+ if (oidlen > 2 && mrOID[oidlen-2] == '.') {
+ op = atoi (mrOID + oidlen - 1);
+ switch (op) {
+ case SLAPI_OP_LESS:
+ case SLAPI_OP_LESS_OR_EQUAL:
+ case SLAPI_OP_EQUAL:
+ case SLAPI_OP_GREATER_OR_EQUAL:
+ case SLAPI_OP_GREATER:
+ case SLAPI_OP_SUBSTRING:
+ {
+ auto char* or_oid = slapi_ch_strdup (mrOID);
+ or_oid [oidlen-2] = '\0';
+ ix = indexer_create (or_oid);
+ if (ix != NULL) {
+ memcpy (&bv, mrVALUE, sizeof(struct berval));
+ reusable |= MRF_ANY_VALUE;
+ }
+ slapi_ch_free((void**)&or_oid);
+ }
+ break;
+ default: /* not a relational operator */
+ break;
+ }
+ }
+ }
+ if (ix != NULL) {
+ or = (or_filter_t*) slapi_ch_calloc (1, sizeof (or_filter_t));
+ or->or_type = slapi_ch_strdup (mrTYPE);
+ or->or_indexer = ix;
+ or->or_op = op;
+ if (op == SLAPI_OP_SUBSTRING) {
+ or->or_values = ss_filter_values (&bv, &(or->or_op));
+ } else {
+ or->or_values = (struct berval**)
+ slapi_ch_malloc (2 * sizeof (struct berval*));
+ or->or_values[0] = slapi_ch_bvdup0 (&bv);
+ or->or_values[1] = NULL;
+ }
+ {
+ auto struct berval** val = or->or_values;
+ if (val) for (; *val; ++val) {
+ LDAPDebug (LDAP_DEBUG_FILTER, "value \"%s\"\n", (*val)->bv_val, 0, 0);
+ }
+ }
+ if (or->or_op == SLAPI_OP_SUBSTRING) {
+ or->or_match_keys = ss_filter_keys (ix, or->or_values);
+ } else {
+ or->or_match_keys = slapi_ch_bvecdup (
+ ix->ix_index (ix, or->or_values, NULL));
+ }
+ slapi_pblock_set (pb, SLAPI_PLUGIN_OBJECT, or);
+ slapi_pblock_set (pb, SLAPI_PLUGIN_DESTROY_FN, (void*)or_filter_destroy);
+ slapi_pblock_set (pb, SLAPI_PLUGIN_MR_FILTER_MATCH_FN, (void*)or_filter_match);
+ slapi_pblock_set (pb, SLAPI_PLUGIN_MR_FILTER_INDEX_FN, (void*)or_filter_index);
+/* slapi_pblock_set (pb, SLAPI_PLUGIN_MR_FILTER_REUSABLE, &reusable); */
+/* slapi_pblock_set (pb, SLAPI_PLUGIN_MR_FILTER_RESET_FN, ?); to be implemented */
+ rc = LDAP_SUCCESS;
+ }
+ } else {
+ LDAPDebug (LDAP_DEBUG_FILTER, "=> or_filter_create missing parameter(s)\n", 0, 0, 0);
+ }
+ LDAPDebug (LDAP_DEBUG_FILTER, "<= or_filter_create(%p) %i\n", (void*)or, rc, 0);
+ return rc;
+}
+
+static indexer_t*
+op_indexer_get (Slapi_PBlock* pb)
+{
+ auto void* obj = NULL;
+ if ( ! slapi_pblock_get (pb, SLAPI_PLUGIN_OBJECT, &obj)) {
+ return (indexer_t*)obj;
+ }
+ return NULL;
+}
+
+static int
+op_indexer_destroy (Slapi_PBlock* pb)
+{
+ auto indexer_t* ix = op_indexer_get (pb);
+ LDAPDebug (LDAP_DEBUG_FILTER, "op_indexer_destroy(%p)\n", (void*)ix, 0, 0);
+ if (ix != NULL) {
+ indexer_free (ix);
+ }
+ return 0;
+}
+
+static int
+op_index_entry (Slapi_PBlock* pb)
+ /* Compute collation keys (when writing an entry). */
+{
+ auto indexer_t* ix = op_indexer_get (pb);
+ auto int rc;
+ struct berval** values;
+ if (ix != NULL && ix->ix_index != NULL &&
+ !slapi_pblock_get (pb, SLAPI_PLUGIN_MR_VALUES, &values) &&
+ !slapi_pblock_set (pb, SLAPI_PLUGIN_MR_KEYS, ix->ix_index (ix, values, NULL))) {
+ rc = 0;
+ } else {
+ rc = LDAP_OPERATIONS_ERROR;
+ }
+ LDAPDebug (LDAP_DEBUG_FILTER, "op_index_entry(%p) %i\n", (void*)ix, rc, 0);
+ return rc;
+}
+
+static int
+op_index_search (Slapi_PBlock* pb)
+ /* Compute collation keys (when searching for entries). */
+{
+ auto or_filter_t* or = or_filter_get (pb);
+ auto int rc = LDAP_OPERATIONS_ERROR;
+ if (or != NULL) {
+ auto indexer_t* ix = or->or_indexer;
+ struct berval** values;
+ if (or->or_index_keys == NULL && ix != NULL && ix->ix_index != NULL &&
+ !slapi_pblock_get (pb, SLAPI_PLUGIN_MR_VALUES, &values)) {
+ or->or_index_keys = slapi_ch_bvecdup (
+ ix->ix_index (ix, values, NULL));
+ }
+ if (or->or_index_keys) {
+ rc = slapi_pblock_set (pb, SLAPI_PLUGIN_MR_KEYS, or->or_index_keys);
+ }
+ }
+ LDAPDebug (LDAP_DEBUG_FILTER, "op_index_search(%p) %i\n", (void*)or, rc, 0);
+ return rc;
+}
+
+typedef struct ss_indexer_t {
+ char* ss_oid; /* ss_indexer->ix_oid && ".6" */
+ indexer_t* ss_indexer;
+} ss_indexer_t;
+
+static void
+ss_indexer_free (ss_indexer_t* ss)
+{
+ slapi_ch_free((void**)&ss->ss_oid);
+ if (ss->ss_indexer != NULL) {
+ indexer_free (ss->ss_indexer);
+ ss->ss_indexer = NULL;
+ }
+ slapi_ch_free((void**)&ss);
+}
+
+static ss_indexer_t*
+ss_indexer_get (Slapi_PBlock* pb)
+{
+ auto void* obj = NULL;
+ if ( ! slapi_pblock_get (pb, SLAPI_PLUGIN_OBJECT, &obj)) {
+ return (ss_indexer_t*)obj;
+ }
+ return NULL;
+}
+
+static void
+ss_indexer_destroy (Slapi_PBlock* pb)
+{
+ auto ss_indexer_t* ss = ss_indexer_get (pb);
+ LDAPDebug (LDAP_DEBUG_FILTER, "ss_indexer_destroy(%p)\n", (void*)ss, 0, 0);
+ if (ss) {
+ ss_indexer_free (ss);
+ }
+}
+
+#define SS_INDEX_LENGTH 3 /* characters */
+
+static char ss_prefixI = '[';
+static char ss_prefixM = '|';
+static char ss_prefixF = '}';
+
+static struct berval ss_index_initial = {1, &ss_prefixI};
+static struct berval ss_index_middle = {1, &ss_prefixM};
+static struct berval ss_index_final = {1, &ss_prefixF};
+
+static int
+long_enough (struct berval* bval, size_t enough)
+{
+ if (bval) {
+ auto size_t len = 0;
+ auto char* next = bval->bv_val;
+ auto char* last = next + bval->bv_len;
+ while (next < last) {
+ LDAP_UTF8INC (next);
+ if (++len >= enough) {
+ if (next > last) next = last;
+ bval->bv_len = next - bval->bv_val;
+ return 1;
+ }
+ }
+ }
+ return !enough;
+}
+
+static int
+ss_index_entry (Slapi_PBlock* pb)
+ /* Compute substring index keys (when writing an entry). */
+{
+ auto int rc = LDAP_OPERATIONS_ERROR;
+ auto size_t substringsLen = 0;
+ struct berval** values;
+ auto ss_indexer_t* ss = ss_indexer_get (pb);
+ auto indexer_t* ix = ss ? ss->ss_indexer : NULL;
+ if (ix != NULL && ix->ix_index != NULL &&
+ !slapi_pblock_get (pb, SLAPI_PLUGIN_MR_VALUES, &values)) {
+ auto struct berval* substrings = NULL;
+ auto struct berval** prefixes = NULL;
+ auto struct berval** value;
+ for (value = values; *value != NULL; ++value) {
+ auto struct berval substring;
+ substring.bv_val = (*value)->bv_val;
+ substring.bv_len = (*value)->bv_len;
+ if (long_enough (&substring, SS_INDEX_LENGTH-1)) {
+ auto struct berval* prefix = &ss_index_initial;
+ auto size_t offset;
+ for (offset = 0; 1; ++offset) {
+ ++substringsLen;
+ substrings = (struct berval*)
+ slapi_ch_realloc ((void*)substrings, substringsLen * sizeof(struct berval));
+ memcpy (&(substrings[substringsLen-1]), &substring, sizeof (struct berval));
+ prefixes = (struct berval**)
+ slapi_ch_realloc ((void*)prefixes, substringsLen * sizeof(struct berval*));
+ prefixes[substringsLen-1] = prefix;
+
+ if (offset != 0) LDAP_UTF8INC (substring.bv_val);
+ substring.bv_len = (*value)->bv_len - (substring.bv_val - (*value)->bv_val);
+ if (long_enough (&substring, SS_INDEX_LENGTH)) {
+ prefix = &ss_index_middle;
+ } else if (long_enough (&substring, SS_INDEX_LENGTH-1)) {
+ prefix = &ss_index_final;
+ } else {
+ break;
+ }
+ }
+ }
+ }
+ if (substrings != NULL) {
+ auto struct berval** vector = (struct berval**)
+ slapi_ch_malloc ((substringsLen+1) * sizeof(struct berval*));
+ auto size_t i;
+ for (i = 0; i < substringsLen; ++i) vector[i] = &(substrings[i]);
+ vector[substringsLen] = NULL;
+ rc = slapi_pblock_set (pb, SLAPI_PLUGIN_MR_KEYS, ix->ix_index (ix, vector, prefixes));
+ slapi_ch_free((void**)&vector);
+ slapi_ch_free((void**)&substrings);
+ slapi_ch_free((void**)&prefixes);
+ }
+ }
+ LDAPDebug (LDAP_DEBUG_FILTER, "ss_index_entry(%p) %i %lu substrings\n",
+ (void*)ss, rc, (unsigned long)substringsLen);
+ return rc;
+}
+
+static int
+ss_index_search (Slapi_PBlock* pb)
+ /* Compute substring search keys (when searching for entries). */
+{
+ auto int rc = LDAP_OPERATIONS_ERROR;
+ auto or_filter_t* or = or_filter_get (pb);
+ if (or) {
+ if (or->or_index_keys == NULL /* not yet computed */ &&
+ or->or_values && or->or_indexer && or->or_indexer->ix_index) {
+ auto size_t substringsLen = 0;
+ auto struct berval* substrings = NULL;
+ auto struct berval** prefixes = NULL;
+ auto struct berval** value;
+ for (value = or->or_values; *value != NULL; ++value) {
+ auto size_t offset;
+ auto struct berval substring;
+ substring.bv_val = (*value)->bv_val;
+ for (offset = 0; 1; ++offset, LDAP_UTF8INC (substring.bv_val)) {
+ auto struct berval* prefix = NULL;
+ substring.bv_len = (*value)->bv_len - (substring.bv_val - (*value)->bv_val);
+ if (offset == 0 && value == or->or_values) {
+ if (long_enough (&substring, SS_INDEX_LENGTH - 1)) {
+ prefix = &ss_index_initial;
+ }
+ } else if (value[1] != NULL) {
+ if (long_enough (&substring, SS_INDEX_LENGTH)) {
+ prefix = &ss_index_middle;
+ }
+ } else if (long_enough (&substring, SS_INDEX_LENGTH)) {
+ prefix = &ss_index_middle;
+ } else if (long_enough (&substring, SS_INDEX_LENGTH-1)) {
+ prefix = &ss_index_final;
+ }
+ if (prefix == NULL) break;
+ ++substringsLen;
+ substrings = (struct berval*)
+ slapi_ch_realloc ((void*)substrings, substringsLen * sizeof(struct berval));
+ memcpy (&(substrings[substringsLen-1]), &substring, sizeof (struct berval));
+ prefixes = (struct berval**)
+ slapi_ch_realloc ((void*)prefixes, substringsLen * sizeof(struct berval*));
+ prefixes[substringsLen-1] = prefix;
+ }
+ }
+ if (substrings != NULL) {
+ auto indexer_t* ix = or->or_indexer;
+ auto struct berval** vector = (struct berval**)
+ slapi_ch_malloc ((substringsLen+1) * sizeof(struct berval*));
+ auto size_t i;
+ for (i = 0; i < substringsLen; ++i) vector[i] = &(substrings[i]);
+ vector[substringsLen] = NULL;
+ or->or_index_keys = slapi_ch_bvecdup (
+ ix->ix_index (ix, vector, prefixes));
+ slapi_ch_free((void**)&vector);
+ slapi_ch_free((void**)&substrings);
+ slapi_ch_free((void**)&prefixes);
+ }
+ }
+ if (or->or_index_keys) {
+ rc = slapi_pblock_set (pb, SLAPI_PLUGIN_MR_KEYS, or->or_index_keys);
+ }
+ }
+ LDAPDebug (LDAP_DEBUG_FILTER, "ss_index_search(%p) %i\n", (void*)or, rc, 0);
+ return rc;
+}
+
+static int
+ss_indexable (struct berval** values)
+ /* at least one of the values is long enough to index */
+{
+ auto struct berval** val = values;
+ if (val) for (; *val; ++val) {
+ auto struct berval value;
+ value.bv_val = (*val)->bv_val;
+ value.bv_len = (*val)->bv_len;
+ if (val == values) { /* initial */
+ if (long_enough (&value, SS_INDEX_LENGTH-1)) return 1;
+ } else if (val[1]) { /* middle */
+ if (long_enough (&value, SS_INDEX_LENGTH)) return 1;
+ } else { /* final */
+ if (long_enough (&value, SS_INDEX_LENGTH-1)) return 1;
+ }
+ }
+ return 0;
+}
+
+static struct berval ss_one_berval = {0,0};
+static struct berval* ss_one_value[2] = {&ss_one_berval, NULL};
+
+static int
+or_filter_index (Slapi_PBlock* pb)
+ /* Return an indexer and values that accelerate the given filter. */
+{
+ auto or_filter_t* or = or_filter_get (pb);
+ auto int rc = LDAP_UNAVAILABLE_CRITICAL_EXTENSION;
+ auto IFP mrINDEX_FN = NULL;
+ auto struct berval** mrVALUES = NULL;
+ auto char* mrOID = NULL;
+ auto int mrQUERY_OPERATOR;
+ if (or && or->or_indexer && or->or_indexer->ix_index) {
+ switch (or->or_op) {
+ case SLAPI_OP_LESS:
+ case SLAPI_OP_LESS_OR_EQUAL:
+ case SLAPI_OP_EQUAL:
+ case SLAPI_OP_GREATER_OR_EQUAL:
+ case SLAPI_OP_GREATER:
+ mrINDEX_FN = op_index_search;
+ mrVALUES = or->or_values;
+ mrOID = or->or_indexer->ix_oid;
+ mrQUERY_OPERATOR = or->or_op;
+ break;
+ case SLAPI_OP_SUBSTRING:
+ if (ss_indexable (or->or_values)) {
+ if (or->or_oid == NULL) {
+ auto const size_t len = strlen (or->or_indexer->ix_oid);
+ or->or_oid = slapi_ch_malloc (len + 3);
+ memcpy (or->or_oid, or->or_indexer->ix_oid, len);
+ sprintf (or->or_oid + len, ".%1i", SLAPI_OP_SUBSTRING);
+ }
+ mrINDEX_FN = ss_index_search;
+ mrVALUES = ss_one_value;
+ mrOID = or->or_oid;
+ mrQUERY_OPERATOR = SLAPI_OP_EQUAL;
+ }
+ break;
+ default: /* unsupported operator */
+ break;
+ }
+ }
+ if (mrINDEX_FN != NULL &&
+ !(rc = slapi_pblock_set (pb, SLAPI_PLUGIN_OBJECT, or)) &&
+ !(rc = slapi_pblock_set (pb, SLAPI_PLUGIN_MR_TYPE, or->or_type)) &&
+ !(rc = slapi_pblock_set (pb, SLAPI_PLUGIN_MR_INDEX_FN, (void*)mrINDEX_FN)) &&
+ !(rc = slapi_pblock_set (pb, SLAPI_PLUGIN_MR_VALUES, mrVALUES)) &&
+ !(rc = slapi_pblock_set (pb, SLAPI_PLUGIN_MR_OID, mrOID))) {
+ rc = slapi_pblock_set (pb, SLAPI_PLUGIN_MR_QUERY_OPERATOR, &mrQUERY_OPERATOR);
+ }
+ LDAPDebug (LDAP_DEBUG_FILTER, "or_filter_index(%p) %i\n",
+ (void*)(or ? or->or_indexer : NULL), rc, 0);
+ return rc;
+}
+
+static int
+or_indexer_create (Slapi_PBlock* pb)
+{
+ auto int rc = LDAP_UNAVAILABLE_CRITICAL_EXTENSION; /* failed to initialize */
+ auto char* mrOID = NULL;
+ auto void* mrOBJECT = NULL;
+ if (slapi_pblock_get (pb, SLAPI_PLUGIN_MR_OID, &mrOID) || mrOID == NULL) {
+ LDAPDebug (LDAP_DEBUG_FILTER, "=> or_indexer_create: no OID parameter\n", 0, 0, 0);
+ } else {
+ auto indexer_t* ix = indexer_create (mrOID);
+ auto char* mrTYPE = NULL;
+ slapi_pblock_get (pb, SLAPI_PLUGIN_MR_TYPE, &mrTYPE);
+ LDAPDebug (LDAP_DEBUG_FILTER, "=> or_indexer_create(oid %s; type %s)\n",
+ mrOID, mrTYPE ? mrTYPE : "<NULL>", 0);
+ if (ix != NULL) {
+ if (ix->ix_index != NULL &&
+ !slapi_pblock_set (pb, SLAPI_PLUGIN_OBJECT, ix) &&
+ !slapi_pblock_set (pb, SLAPI_PLUGIN_MR_OID, ix->ix_oid) &&
+ !slapi_pblock_set (pb, SLAPI_PLUGIN_MR_INDEX_FN, (void*)op_index_entry) &&
+ !slapi_pblock_set (pb, SLAPI_PLUGIN_DESTROY_FN, (void*)op_indexer_destroy)) {
+ mrOBJECT = ix;
+ rc = 0; /* success */
+ } else {
+ indexer_free (ix);
+ }
+ } else { /* mrOID does not identify an ordering rule. */
+ /* Is it an ordering rule OID with the substring suffix? */
+ auto size_t oidlen = strlen (mrOID);
+ if (oidlen > 2 && mrOID[oidlen-2] == '.' &&
+ atoi (mrOID + oidlen - 1) == SLAPI_OP_SUBSTRING) {
+ auto char* or_oid = slapi_ch_strdup (mrOID);
+ or_oid [oidlen-2] = '\0';
+ ix = indexer_create (or_oid);
+ if (ix != NULL) {
+ auto ss_indexer_t* ss = (ss_indexer_t*) slapi_ch_malloc (sizeof (ss_indexer_t));
+ ss->ss_indexer = ix;
+ oidlen = strlen (ix->ix_oid);
+ ss->ss_oid = slapi_ch_malloc (oidlen + 3);
+ memcpy (ss->ss_oid, ix->ix_oid, oidlen);
+ sprintf (ss->ss_oid + oidlen, ".%1i", SLAPI_OP_SUBSTRING);
+ if (ix->ix_index != NULL &&
+ !slapi_pblock_set (pb, SLAPI_PLUGIN_OBJECT, ss) &&
+ !slapi_pblock_set (pb, SLAPI_PLUGIN_MR_OID, ss->ss_oid) &&
+ !slapi_pblock_set (pb, SLAPI_PLUGIN_MR_INDEX_FN, (void*)ss_index_entry) &&
+ !slapi_pblock_set (pb, SLAPI_PLUGIN_DESTROY_FN, (void*)ss_indexer_destroy)) {
+ mrOBJECT = ss;
+ rc = 0; /* success */
+ } else {
+ ss_indexer_free (ss);
+ }
+ }
+ slapi_ch_free((void**)&or_oid);
+ }
+ }
+ }
+ LDAPDebug (LDAP_DEBUG_FILTER, "<= or_indexer_create(%p) %i\n", mrOBJECT, rc, 0);
+ return rc;
+}
+
+static Slapi_PluginDesc pdesc = { "orderingrule", PLUGIN_MAGIC_VENDOR_STR, PRODUCTTEXT,
+ "internationalized ordering rule plugin" };
+
+#define SLAPI_ORPLUGIN_NAME pdesc.spd_description
+
+int /* LDAP error code */
+orderingRule_init (Slapi_PBlock* pb)
+{
+ int rc;
+ int argc;
+ char** argv;
+ char* cfgpath;
+
+/* if (!(rc = slapi_pblock_set (pb, SLAPI_PLUGIN_PRIVATE, ...)) &&
+ !(rc = slapi_pblock_set (pb, SLAPI_PLUGIN_CLOSE_FN, ...)))
+*/
+
+#ifdef USE_HPUX_CC
+ /* not needed with ICU
+ shl_load ( "../lib/libnsbrk30.sl", BIND_IMMEDIATE, 0L );
+ shl_load ( "../lib/libnscnv30.sl", BIND_IMMEDIATE, 0L );
+ shl_load ( "../lib/libnscol30.sl", BIND_IMMEDIATE, 0L );
+ shl_load ( "../lib/libnsfmt30.sl", BIND_IMMEDIATE, 0L );
+ shl_load ( "../lib/libnsres30.sl", BIND_IMMEDIATE, 0L );
+ shl_load ( "../lib/libnsuni30.sl", BIND_IMMEDIATE, 0L );
+ */
+#endif
+
+ if ( slapi_pblock_get( pb, SLAPI_CONFIG_DIRECTORY, &cfgpath ) != 0 ) {
+ slapi_log_error( SLAPI_LOG_FATAL, SLAPI_ORPLUGIN_NAME,
+ "Unable to retrieve slapd configuration pathname; using default\n" );
+ cfgpath = NULL;
+ }
+
+ collation_init( cfgpath );
+ if (!slapi_pblock_get (pb, SLAPI_PLUGIN_ARGC, &argc) &&
+ !slapi_pblock_get (pb, SLAPI_PLUGIN_ARGV, &argv) &&
+ argc > 0) {
+ collation_read_config (argv[0]);
+ }
+ {
+ slapi_pblock_set (pb, SLAPI_PLUGIN_MR_INDEXER_CREATE_FN, (void*)or_indexer_create);
+ rc = slapi_pblock_set (pb, SLAPI_PLUGIN_MR_FILTER_CREATE_FN, (void*)or_filter_create);
+ }
+ if ( rc == 0 ) {
+ rc = slapi_pblock_set( pb, SLAPI_PLUGIN_DESCRIPTION, (void *)&pdesc );
+ }
+ LDAPDebug (LDAP_DEBUG_FILTER, "orderingRule_init %i\n", rc, 0, 0);
+ return rc;
+}
diff --git a/ldap/servers/plugins/collation/orfilter.h b/ldap/servers/plugins/collation/orfilter.h
new file mode 100644
index 00000000..85a32592
--- /dev/null
+++ b/ldap/servers/plugins/collation/orfilter.h
@@ -0,0 +1,10 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+
+#ifndef _ORFILTER_H_
+#define _ORFILTER_H_
+
+#endif
diff --git a/ldap/servers/plugins/cos/Makefile b/ldap/servers/plugins/cos/Makefile
new file mode 100644
index 00000000..de540f5a
--- /dev/null
+++ b/ldap/servers/plugins/cos/Makefile
@@ -0,0 +1,79 @@
+#
+# BEGIN COPYRIGHT BLOCK
+# Copyright 2001 Sun Microsystems, Inc.
+# Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+# All rights reserved.
+# END COPYRIGHT BLOCK
+#
+LDAP_SRC = ../../..
+MCOM_ROOT = ../../../../..
+
+NOSTDCLEAN=true # don't let nsconfig.mk define target clean
+NOSTDSTRIP=true # don't let nsconfig.mk define target strip
+NSPR20=true # probably should be defined somewhere else (not sure where)
+
+OBJDEST = $(OBJDIR)/lib/libcos
+LIBDIR = $(LIB_RELDIR)
+
+include $(MCOM_ROOT)/ldapserver/nsdefs.mk
+include $(MCOM_ROOT)/ldapserver/nsconfig.mk
+include $(LDAP_SRC)/nsldap.mk
+
+ifeq ($(ARCH), WINNT)
+DEF_FILE:=./cos.def
+endif
+
+COS_OBJS = cos.o cos_cache.o
+OBJS = $(addprefix $(OBJDEST)/, $(COS_OBJS))
+
+COS_DLL = cos-plugin
+
+INCLUDES += -I../../slapd -I../../../include
+CFLAGS+=$(SLCFLAGS) -DSLAPD_LOGGING
+
+ifeq ($(ARCH), WINNT)
+EXTRA_LIBS_DEP += $(LIBSLAPD) $(NSPR_DEP) $(LDAPSDK_DEP)
+EXTRA_LIBS += $(NSPRLINK) $(LIBSLAPD) $(LDAP_SDK_LIBLDAP_DLL)
+COS_DLL_OBJ = $(addprefix $(OBJDEST)/, dllmain.o)
+endif
+
+ifeq ($(ARCH), AIX)
+EXTRA_LIBS_DEP += $(LIBSLAPD) $(NSPR_DEP) $(LDAPSDK_DEP)
+EXTRA_LIBS += $(LIBSLAPDLINK) $(NSPRLINK) $(LDAP_SDK_LIBLDAP_DLL)
+LD=ld
+endif
+
+ifeq ($(ARCH), HPUX)
+EXTRA_LIBS_DEP += $(LIBSLAPD_DEP) $(LDAPSDK_DEP) $(NSPR_DEP) $(SECURITY_DEP)
+EXTRA_LIBS += $(DYN_NSHTTPD) $(ADMINUTIL_LINK) $(LDAPLINK) $(SECURITYLINK) $(NSPRLINK) $(ICULINK)
+endif
+
+COS= $(addprefix $(LIBDIR)/, $(COS_DLL).$(DLL_SUFFIX))
+
+clientSDK:
+
+all: $(OBJDEST) $(LIBDIR) $(COS)
+
+ifeq ($(ARCH), WINNT)
+$(COS): $(OBJS) $(COS_DLL_OBJ) $(DEF_FILE)
+ $(LINK_DLL) $(COS_DLL_OBJ) $(EXTRA_LIBS) /DEF:$(DEF_FILE)
+else
+$(COS): $(OBJS) $(COS_DLL_OBJ)
+ $(LINK_DLL) $(COS_DLL_OBJ) $(EXTRA_LIBS)
+endif
+
+
+veryclean: clean
+
+clean:
+ $(RM) $(OBJS)
+ifeq ($(ARCH), WINNT)
+ $(RM) $(COS_DLL_OBJ)
+endif
+ $(RM) $(COS)
+
+$(OBJDEST):
+ $(MKDIR) $(OBJDEST)
+
+$(LIBDIR):
+ $(MKDIR) $(LIBDIR)
diff --git a/ldap/servers/plugins/cos/cos.c b/ldap/servers/plugins/cos/cos.c
new file mode 100644
index 00000000..5a077646
--- /dev/null
+++ b/ldap/servers/plugins/cos/cos.c
@@ -0,0 +1,266 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+#include <stdio.h>
+#include <string.h>
+#include "portable.h"
+#include "nspr.h"
+#include "slapi-plugin.h"
+#include "slapi-private.h"
+#include <dirlite_strings.h> /* PLUGIN_MAGIC_VENDOR_STR */
+#include "dirver.h"
+#include "cos_cache.h"
+#include "vattr_spi.h"
+
+/* get file mode flags for unix */
+#ifndef _WIN32
+#include <sys/stat.h>
+#endif
+
+/*** secret slapd stuff ***/
+
+/*
+ these are required here because they are not available
+ in any public header. They must exactly match their
+ counterparts in the server or they will fail to work
+ correctly.
+*/
+
+/*** from proto-slap.h ***/
+
+int slapd_log_error_proc( char *subsystem, char *fmt, ... );
+
+/*** from ldaplog.h ***/
+
+/* edited ldaplog.h for LDAPDebug()*/
+#ifndef _LDAPLOG_H
+#define _LDAPLOG_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define LDAP_DEBUG_TRACE 0x00001 /* 1 */
+#define LDAP_DEBUG_ANY 0x04000 /* 16384 */
+#define LDAP_DEBUG_PLUGIN 0x10000 /* 65536 */
+
+/* debugging stuff */
+# ifdef _WIN32
+ extern int *module_ldap_debug;
+# define LDAPDebug( level, fmt, arg1, arg2, arg3 ) \
+ { \
+ if ( *module_ldap_debug & level ) { \
+ slapd_log_error_proc( NULL, fmt, arg1, arg2, arg3 ); \
+ } \
+ }
+# else /* _WIN32 */
+ extern int slapd_ldap_debug;
+# define LDAPDebug( level, fmt, arg1, arg2, arg3 ) \
+ { \
+ if ( slapd_ldap_debug & level ) { \
+ slapd_log_error_proc( NULL, fmt, arg1, arg2, arg3 ); \
+ } \
+ }
+# endif /* Win32 */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _LDAP_H */
+
+/*** end secrets ***/
+
+#define COS_PLUGIN_SUBSYSTEM "cos-plugin" /* used for logging */
+
+/* subrelease in the following version info is for odd-ball cos releases
+ * which do not fit into a general release, this can be used for beta releases
+ * and other (this version stuff is really to help outside applications which
+ * may wish to update cos decide whether the cos version they want to update to
+ * is a higher release than the installed plugin)
+ *
+ * note: release origin is 00 for directory server
+ * sub-release should be:
+ * 50 for initial RTM products
+ * from 0 increasing for alpha/beta releases
+ * from 51 increasing for patch releases
+ */
+#define COS_VERSION 0x00050050 /* version format: 0x release origin 00 major 05 minor 00 sub-release 00 */
+
+/* other function prototypes */
+int cos_init( Slapi_PBlock *pb );
+int cos_compute(computed_attr_context *c,char* type,Slapi_Entry *e,slapi_compute_output_t outputfn);
+int cos_start( Slapi_PBlock *pb );
+int cos_close( Slapi_PBlock *pb );
+int cos_post_op( Slapi_PBlock *pb );
+
+
+static Slapi_PluginDesc pdesc = { "cos", PLUGIN_MAGIC_VENDOR_STR, PRODUCTTEXT,
+ "class of service plugin" };
+
+static void * cos_plugin_identity = NULL;
+
+
+#ifdef _WIN32
+int *module_ldap_debug = 0;
+
+void plugin_init_debug_level(int *level_ptr)
+{
+ module_ldap_debug = level_ptr;
+}
+#endif
+
+/*
+** Plugin identity mgmt
+*/
+
+void cos_set_plugin_identity(void * identity)
+{
+ cos_plugin_identity=identity;
+}
+
+void * cos_get_plugin_identity()
+{
+ return cos_plugin_identity;
+}
+
+int cos_version()
+{
+ return COS_VERSION;
+}
+
+/*
+ cos_init
+ --------
+ adds our callbacks to the list
+*/
+int cos_init( Slapi_PBlock *pb )
+{
+ int ret = 0;
+ void * plugin_identity=NULL;
+
+ LDAPDebug( LDAP_DEBUG_TRACE, "--> cos_init\n",0,0,0);
+
+ /*
+ ** Store the plugin identity for later use.
+ ** Used for internal operations
+ */
+
+ slapi_pblock_get (pb, SLAPI_PLUGIN_IDENTITY, &plugin_identity);
+ PR_ASSERT (plugin_identity);
+ cos_set_plugin_identity(plugin_identity);
+
+ if ( slapi_pblock_set( pb, SLAPI_PLUGIN_VERSION,
+ SLAPI_PLUGIN_VERSION_01 ) != 0 ||
+ slapi_pblock_set(pb, SLAPI_PLUGIN_START_FN,
+ (void *) cos_start ) != 0 ||
+ slapi_pblock_set(pb, SLAPI_PLUGIN_POST_MODIFY_FN,
+ (void *) cos_post_op ) != 0 ||
+ slapi_pblock_set(pb, SLAPI_PLUGIN_POST_MODRDN_FN,
+ (void *) cos_post_op ) != 0 ||
+ slapi_pblock_set(pb, SLAPI_PLUGIN_POST_ADD_FN,
+ (void *) cos_post_op ) != 0 ||
+ slapi_pblock_set(pb, SLAPI_PLUGIN_POST_DELETE_FN,
+ (void *) cos_post_op ) != 0 ||
+ slapi_pblock_set(pb, SLAPI_PLUGIN_CLOSE_FN,
+ (void *) cos_close ) != 0 ||
+ slapi_pblock_set( pb, SLAPI_PLUGIN_DESCRIPTION,
+ (void *)&pdesc ) != 0 )
+ {
+ slapi_log_error( SLAPI_LOG_FATAL, COS_PLUGIN_SUBSYSTEM,
+ "cos_init: failed to register plugin\n" );
+ ret = -1;
+ }
+
+ LDAPDebug( LDAP_DEBUG_TRACE, "<-- cos_init\n",0,0,0);
+ return ret;
+}
+
+
+/*
+ cos_start
+ ---------
+ This function registers the computed attribute evaluator
+ and inits the cos cache.
+ It is called after cos_init.
+*/
+int cos_start( Slapi_PBlock *pb )
+{
+ int ret = 0;
+
+ LDAPDebug( LDAP_DEBUG_TRACE, "--> cos_start\n",0,0,0);
+
+ if( !cos_cache_init() )
+ {
+ LDAPDebug( LDAP_DEBUG_PLUGIN, "cos: ready for service\n",0,0,0);
+ }
+ else
+ {
+ /* problems we are hosed */
+ cos_cache_stop();
+ LDAPDebug( LDAP_DEBUG_ANY, "cos_start: failed to initialise\n",0,0,0);
+ ret = -1;
+ }
+
+ LDAPDebug( LDAP_DEBUG_TRACE, "<-- cos_start\n",0,0,0);
+ return ret;
+}
+
+/*
+ cos_close
+ ---------
+ closes down the cache
+*/
+int cos_close( Slapi_PBlock *pb )
+{
+ LDAPDebug( LDAP_DEBUG_TRACE, "--> cos_close\n",0,0,0);
+
+ cos_cache_stop();
+
+ LDAPDebug( LDAP_DEBUG_TRACE, "<-- cos_close\n",0,0,0);
+
+ return 0;
+}
+
+/*
+ cos_compute
+ -----------
+ called when evaluating named attributes in a search
+ and attributes remain unfound in the entry,
+ this function checks the attribute for a match with
+ those in the class of service definitions, and if a
+ match is found, adds the attribute and value to the
+ output list
+
+ returns
+ 0 on success
+ 1 on outright failure
+ -1 when doesn't know about attribute
+*/
+int cos_compute(computed_attr_context *c,char* type,Slapi_Entry *e,slapi_compute_output_t outputfn)
+{
+ int ret = -1;
+
+ return ret;
+}
+
+
+/*
+ cos_post_op
+ -----------
+ Catch all for all post operations that change entries
+ in some way - this simply notifies the cache of a
+ change - the cache decides if action is necessary
+*/
+int cos_post_op( Slapi_PBlock *pb )
+{
+ LDAPDebug( LDAP_DEBUG_TRACE, "--> cos_post_op\n",0,0,0);
+
+ cos_cache_change_notify(pb);
+
+ LDAPDebug( LDAP_DEBUG_TRACE, "<-- cos_post_op\n",0,0,0);
+ return 0; /* always succeed */
+}
+
diff --git a/ldap/servers/plugins/cos/cos.def b/ldap/servers/plugins/cos/cos.def
new file mode 100644
index 00000000..484d9c6a
--- /dev/null
+++ b/ldap/servers/plugins/cos/cos.def
@@ -0,0 +1,11 @@
+; BEGIN COPYRIGHT BLOCK
+; Copyright 2001 Sun Microsystems, Inc.
+; Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+; All rights reserved.
+; END COPYRIGHT BLOCK
+;
+DESCRIPTION 'Netscape Directory Server 7 Class Of Service Plugin'
+EXPORTS
+ cos_init @2
+ plugin_init_debug_level @3
+ cos_version @4
diff --git a/ldap/servers/plugins/cos/cos_cache.c b/ldap/servers/plugins/cos/cos_cache.c
new file mode 100644
index 00000000..165f47d1
--- /dev/null
+++ b/ldap/servers/plugins/cos/cos_cache.c
@@ -0,0 +1,3566 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+/*
+ The cos cache keeps in memory all of
+ the data related to cos. This allows
+ very fast lookups at the expense of RAM.
+ All meta data is indexed, allowing fast
+ binary search lookups.
+ The cache is not dynamic, in the sense
+ that it does not iteratively modify
+ itself as changes are made to the cos
+ meta-data. Rather, it is designed to
+ be fast to read, with non-locking
+ multiple thread access to the cache,
+ at the expense of modification speed.
+ This means that when changes do occur,
+ the cache must be rebuilt from scratch.
+ However, this is achieved in such a way,
+ so as to allow cache queries during the
+ building of the new cache - so once a
+ cache has been built, there is no down
+ time.
+ Of course, the configuration of the cos meta
+ data is likely to be a thing which does not
+ happen often. Any other use, is probably a
+ mis-use of the mechanism, and certainly will
+ suffer from performance problems.
+*/
+
+#include <stdio.h>
+#include <string.h>
+#include "portable.h"
+#include "slapi-plugin.h"
+#include <dirlite_strings.h> /* PLUGIN_MAGIC_VENDOR_STR */
+#include "dirver.h"
+
+/* this is naughty, but the api for backend state change is currently here */
+#include "slapi-private.h"
+
+/* include NSPR header files */
+#include "prthread.h"
+#include "prlock.h"
+#include "prerror.h"
+#include "prcvar.h"
+#include "prio.h"
+#include "vattr_spi.h"
+
+#include "cos_cache.h"
+
+#include "views.h"
+static void **views_api;
+
+/*** secret functions and structs in slapd ***/
+
+/*
+ these are required here because they are not available
+ in any public header. They must exactly match their
+ counterparts in the server or they will fail to work
+ correctly.
+*/
+
+/*** from slap.h ***/
+
+struct objclass {
+ char *oc_name; /* NAME */
+ char *oc_desc; /* DESC */
+ char *oc_oid; /* object identifier */
+ char *oc_superior; /* SUP -- XXXmcs: should be an array */
+ PRUint8 oc_kind; /* ABSTRACT/STRUCTURAL/AUXILIARY */
+ PRUint8 oc_flags; /* misc. flags, e.g., OBSOLETE */
+ char **oc_required;
+ char **oc_allowed;
+ char **oc_orig_required; /* MUST */
+ char **oc_orig_allowed; /* MAY */
+ char **oc_origin; /* X-ORIGIN extension */
+ struct objclass *oc_next;
+};
+
+/*** from proto-slap.h ***/
+
+int config_get_schemacheck();
+void oc_lock_read( void );
+void oc_unlock( void );
+struct objclass* g_get_global_oc_nolock();
+int slapd_log_error_proc( char *subsystem, char *fmt, ... );
+
+/*** from ldaplog.h ***/
+
+/* edited ldaplog.h for LDAPDebug()*/
+#ifndef _LDAPLOG_H
+#define _LDAPLOG_H
+
+/* defined in cos.c */
+void * cos_get_plugin_identity();
+
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define LDAP_DEBUG_TRACE 0x00001 /* 1 */
+#define LDAP_DEBUG_ANY 0x04000 /* 16384 */
+#define LDAP_DEBUG_PLUGIN 0x10000 /* 65536 */
+
+/* debugging stuff */
+# ifdef _WIN32
+ extern int *module_ldap_debug;
+# define LDAPDebug( level, fmt, arg1, arg2, arg3 ) \
+ { \
+ if ( *module_ldap_debug & level ) { \
+ slapd_log_error_proc( NULL, fmt, arg1, arg2, arg3 ); \
+ } \
+ }
+# else /* _WIN32 */
+ extern int slapd_ldap_debug;
+# define LDAPDebug( level, fmt, arg1, arg2, arg3 ) \
+ { \
+ if ( slapd_ldap_debug & level ) { \
+ slapd_log_error_proc( NULL, fmt, arg1, arg2, arg3 ); \
+ } \
+ }
+# endif /* Win32 */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _LDAP_H */
+
+/*** end secrets ***/
+
+#define COS_PLUGIN_SUBSYSTEM "cos-plugin" /* used for logging */
+
+#define COSTYPE_BADTYPE 0
+#define COSTYPE_CLASSIC 1
+#define COSTYPE_POINTER 2
+#define COSTYPE_INDIRECT 3
+#define COS_DEF_ERROR_NO_TEMPLATES -2
+
+/* the global plugin handle */
+static volatile vattr_sp_handle *vattr_handle = NULL;
+
+static cos_cache_notify_flag = 0;
+
+/* service definition cache structs */
+
+/* cosIndexedLinkedList: provides an object oriented type interface to
+ link lists where each element contains an index for the entire
+ list. All structures that contain this one must specify this one
+ as the first member otherwise the supporting functions will not work.
+
+ {PARPAR} The indexing ability is not currently used since the
+ fastest lookup is achieved via a cache level index of all attributes,
+ however this mechanism may prove useful in the future
+*/
+struct _cosIndexedLinkedList
+{
+ void *pNext;
+ void **index;
+};
+typedef struct _cosIndexedLinkedList cosIndexedLinkedList;
+
+struct _cosAttrValue
+{
+ cosIndexedLinkedList list;
+ char *val;
+};
+typedef struct _cosAttrValue cosAttrValue;
+
+struct _cosAttribute
+{
+ cosIndexedLinkedList list;
+ char *pAttrName;
+ cosAttrValue *pAttrValue;
+ cosAttrValue *pObjectclasses;
+ int attr_override;
+ int attr_operational;
+ int attr_operational_default;
+ int attr_cos_merge;
+ void *pParent;
+};
+typedef struct _cosAttribute cosAttributes;
+
+struct _cosTemplate
+{
+ cosIndexedLinkedList list;
+ cosAttrValue *pDn;
+ cosAttrValue *pObjectclasses;
+ cosAttributes *pAttrs;
+ char *cosGrade;
+ int template_default;
+ void *pParent;
+ unsigned long cosPriority;
+};
+
+typedef struct _cosTemplate cosTemplates;
+
+struct _cosDefinition
+{
+ cosIndexedLinkedList list;
+ int cosType;
+ cosAttrValue *pDn;
+ cosAttrValue *pCosTargetTree;
+ cosAttrValue *pCosTemplateDn;
+ cosAttrValue *pCosSpecifier;
+ cosAttrValue *pCosAttrs;
+ cosAttrValue *pCosOverrides;
+ cosAttrValue *pCosOperational;
+ cosAttrValue *pCosOpDefault;
+ cosAttrValue *pCosMerge;
+ cosTemplates *pCosTmps;
+};
+typedef struct _cosDefinition cosDefinitions;
+
+struct _cos_cache
+{
+ cosDefinitions *pDefs;
+ cosAttributes **ppAttrIndex;
+ int attrCount;
+ char **ppTemplateList;
+ int templateCount;
+ int refCount;
+ int vattr_cacheable;
+};
+typedef struct _cos_cache cosCache;
+
+/* cache manipulation function prototypes*/
+static cosCache *pCache; /* always the current global cache, only use getref to get */
+
+/* the place to start if you want a new cache */
+static int cos_cache_create();
+
+/* cache index related functions */
+static int cos_cache_index_all(cosCache *pCache);
+static int cos_cache_attr_compare(const void *e1, const void *e2);
+static int cos_cache_template_index_compare(const void *e1, const void *e2);
+static int cos_cache_string_compare(const void *e1, const void *e2);
+static int cos_cache_template_index_bsearch(const char *dn);
+static int cos_cache_attr_index_bsearch( const cosCache *pCache, const cosAttributes *key, int lower, int upper );
+
+/* the multi purpose list creation function, pass it something and it links it */
+static void cos_cache_add_ll_entry(void **attrval, void *theVal, int ( *compare )(const void *elem1, const void *elem2 ));
+
+/* cosAttrValue manipulation */
+static int cos_cache_add_attrval(cosAttrValue **attrval, char *val);
+static void cos_cache_del_attrval_list(cosAttrValue **pVal);
+static int cos_cache_attrval_count(cosAttrValue *pVal);
+static int cos_cache_attrval_exists(cosAttrValue *pAttrs, const char *val);
+
+/* cosAttributes manipulation */
+static int cos_cache_add_attr(cosAttributes **pAttrs, char *name, cosAttrValue *val);
+static void cos_cache_del_attr_list(cosAttributes **pAttrs);
+static int cos_cache_find_attr(cosCache *pCache, char *type);
+static int cos_cache_total_attr_count(cosCache *pCache);
+static int cos_cache_cos_2_slapi_valueset(cosAttributes *pAttr, Slapi_ValueSet **out_vs);
+static int cos_cache_cmp_attr(cosAttributes *pAttr, Slapi_Value *test_this, int *result);
+
+/* cosTemplates manipulation */
+static int cos_cache_add_dn_tmpls(char *dn, cosAttrValue *pCosSpecifier, cosAttrValue *pAttrs, cosTemplates **pTmpls);
+static int cos_cache_add_tmpl(cosTemplates **pTemplates, cosAttrValue *dn, cosAttrValue *objclasses, cosAttrValue *pCosSpecifier, cosAttributes *pAttrs,cosAttrValue *cosPriority);
+#if 0
+static int cos_cache_del_tmpl(cosTemplates *pTemplates, char *dn);
+#endif
+
+/* cosDefinitions manipulation */
+static int cos_cache_build_definition_list(cosDefinitions **pDefs, int *vattr_cacheable);
+static int cos_cache_add_dn_defs(char *dn, cosDefinitions **pDefs, int *vattr_cacheable);
+static int cos_cache_add_defn(cosDefinitions **pDefs, cosAttrValue **dn, int cosType, cosAttrValue **tree, cosAttrValue **tmpDn, cosAttrValue **spec, cosAttrValue **pAttrs, cosAttrValue **pOverrides, cosAttrValue **pOperational, cosAttrValue **pCosMerge, cosAttrValue **pCosOpDefault);
+static int cos_cache_entry_is_cos_related( Slapi_Entry *e);
+#if 0
+static int cos_cache_del_defn(cosDefinitions *pDefs, char *dn);
+#endif
+
+/* schema checking */
+static int cos_cache_schema_check(cosCache *pCache, int cache_attr_index, Slapi_Attr *pObjclasses);
+static int cos_cache_schema_build(cosCache *pCache);
+static void cos_cache_del_schema(cosCache *pCache);
+
+/* special cos scheme implimentations (special = other than cos classic) */
+static int cos_cache_follow_pointer( vattr_context *context, const char *dn, char *type, Slapi_ValueSet **out_vs, Slapi_Value *test_this, int *result, int flags );
+
+
+/* this dude is the thread function which performs dynamic config of the cache */
+static void cos_cache_wait_on_change(void *arg);
+
+/* this gets called when a backend changes state */
+void cos_cache_backend_state_change(void *handle, char *be_name,
+ int old_be_state, int new_be_state);
+
+/* operation callbacks for vattr service */
+static int cos_cache_vattr_get(vattr_sp_handle *handle, vattr_context *c, Slapi_Entry *e, char *type, Slapi_ValueSet** results,int *type_name_disposition, char** actual_type_name, int flags, int *free_flags, void *hint);
+static int cos_cache_vattr_compare(vattr_sp_handle *handle, vattr_context *c, Slapi_Entry *e, char *type, Slapi_Value *test_this, int* result, int flags, void *hint);
+static int cos_cache_vattr_types(vattr_sp_handle *handle,Slapi_Entry *e,vattr_type_list_context *type_context,int flags);
+static int cos_cache_query_attr(cos_cache *ptheCache, vattr_context *context, Slapi_Entry *e, char *type, Slapi_ValueSet **out_attr, Slapi_Value *test_this, int *result, int *ops);
+static void cos_cache_query_attr_free(struct berval ***vals); /* deprecated */
+
+
+/*
+ compares s2 to s1 starting from end of string until the beginning of either
+ matches result in the s2 value being clipped from s1 with a NULL char
+ and 1 being returned as opposed to 0
+*/
+static int cos_cache_backwards_stricmp_and_clip(char*s1,char*s2);
+
+/* module level thread control stuff */
+
+static PRThread *cos_tid = NULL;
+static int keeprunning = 0;
+static int started = 0;
+
+static Slapi_Mutex *cache_lock;
+static Slapi_Mutex *change_lock;
+static Slapi_Mutex *start_lock;
+static Slapi_Mutex *stop_lock;
+static Slapi_CondVar *something_changed = NULL;
+static Slapi_CondVar *start_cond = NULL;
+
+
+/*
+ cos_cache_init
+ --------------
+ starts up the thread which waits for changes and
+ fires off the cache re-creation when one is detected
+ also registers vattr callbacks
+*/
+int cos_cache_init()
+{
+ int ret = 0;
+
+ LDAPDebug( LDAP_DEBUG_TRACE, "--> cos_cache_init\n",0,0,0);
+
+ slapi_vattrcache_cache_none();
+ cache_lock = slapi_new_mutex();
+ change_lock = slapi_new_mutex();
+ stop_lock = slapi_new_mutex();
+ something_changed = slapi_new_condvar(change_lock);
+ keeprunning =1;
+ start_lock = slapi_new_mutex();
+ start_cond = slapi_new_condvar(start_lock);
+ started = 0;
+
+ if ( stop_lock == NULL ||
+ change_lock == NULL ||
+ cache_lock == NULL ||
+ stop_lock == NULL ||
+ start_lock == NULL ||
+ start_cond == NULL ||
+ something_changed == NULL)
+ {
+ slapi_log_error( SLAPI_LOG_FATAL, COS_PLUGIN_SUBSYSTEM,
+ "cos_cache_init: cannot create mutexes\n" );
+ ret = -1;
+ goto out;
+ }
+
+ /* grab the views interface */
+ if(slapi_apib_get_interface(Views_v1_0_GUID, &views_api))
+ {
+ /* lets be tolerant if views is disabled */
+ views_api = 0;
+ }
+
+ if (slapi_vattrspi_register((vattr_sp_handle **)&vattr_handle,
+ cos_cache_vattr_get,
+ cos_cache_vattr_compare,
+ cos_cache_vattr_types) != 0)
+ {
+ slapi_log_error( SLAPI_LOG_FATAL, COS_PLUGIN_SUBSYSTEM,
+ "cos_cache_init: cannot register as service provider\n" );
+ ret = -1;
+ goto out;
+ }
+
+ if ((cos_tid = PR_CreateThread (PR_USER_THREAD,
+ cos_cache_wait_on_change,
+ NULL,
+ PR_PRIORITY_NORMAL,
+ PR_GLOBAL_THREAD,
+ PR_UNJOINABLE_THREAD,
+ SLAPD_DEFAULT_THREAD_STACKSIZE)) == NULL )
+ {
+ slapi_log_error( SLAPI_LOG_FATAL, COS_PLUGIN_SUBSYSTEM,
+ "cos_cache_init: PR_CreateThread failed\n" );
+ ret = -1;
+ goto out;
+ }
+
+ /* wait for that thread to get started */
+ if (ret == 0) {
+ slapi_lock_mutex(start_lock);
+ while (!started) {
+ while (slapi_wait_condvar(start_cond, NULL) == 0);
+ }
+ slapi_unlock_mutex(start_lock);
+ }
+
+
+out:
+ LDAPDebug( LDAP_DEBUG_TRACE, "<-- cos_cache_init\n",0,0,0);
+ return ret;
+}
+
+/*
+ cos_cache_wait_on_change
+ ------------------------
+ sit around waiting on a notification that something has
+ changed, then fires off the cache re-creation
+
+ The way this stuff is written, we can look for the
+ template for a definiton, before the template has been added--I think
+ that's OK--we'll see it when it arrives--get this error message:
+ "skipping cos definition cn=cosPointerGenerateSt,ou=People,o=cosAll--no templates found"
+
+*/
+static void cos_cache_wait_on_change(void *arg)
+{
+ LDAPDebug( LDAP_DEBUG_TRACE, "--> cos_cache_wait_on_change thread\n",0,0,0);
+
+ slapi_lock_mutex(stop_lock);
+ slapi_lock_mutex(change_lock);
+
+ /* first register our backend state change func (we'll use func pointer as handle) */
+ slapi_register_backend_state_change((void *)cos_cache_backend_state_change, cos_cache_backend_state_change);
+
+ pCache = 0;
+
+ /* create initial cache */
+ cos_cache_create();
+
+ slapi_lock_mutex(start_lock);
+ started = 1;
+ slapi_notify_condvar(start_cond, 1);
+ slapi_unlock_mutex(start_lock);
+
+ while(keeprunning)
+ {
+ slapi_unlock_mutex(change_lock);
+ slapi_lock_mutex(change_lock);
+ if ( !cos_cache_notify_flag && keeprunning) {
+ /*
+ * Nothing to do right now, so go to sleep--as
+ * we have the mutex, we are sure to wait before any modify
+ * thread notifies our condvar, and so we will not miss any
+ * notifications, including the shutdown notification.
+ */
+ slapi_wait_condvar( something_changed, NULL );
+ } else {
+ /* Something to do...do it below */
+ }
+ /*
+ * We're here because:
+ * 1. we were asleep and got a signal, on our condvar OR
+ * 2. we were about to wait on the condvar and noticed that a modfiy
+ * thread
+ * had passed, setting the cos_cache_notify_flag and notifying us--
+ * we did not wait in that case as we would have missed the notify
+ * (notify when noone is waiting == no-op).
+ * before we go running off doing lots of stuff lets check if we should stop
+ */
+ if(keeprunning) {
+ cos_cache_create();
+ }
+ cos_cache_notify_flag = 0; /* Dealt with it */
+ }/* while */
+
+ /* shut down the cache */
+ slapi_unlock_mutex(change_lock);
+ slapi_unlock_mutex(stop_lock);
+
+ LDAPDebug( LDAP_DEBUG_TRACE, "<-- cos_cache_wait_on_change thread exit\n",0,0,0);
+}
+
+/*
+ cos_cache_create
+ ---------------------
+ Walks the definitions in the DIT and creates the cache.
+ Once created, it swaps the new cache for the old one,
+ releasing its refcount to the old cache and allowing it
+ to be destroyed.
+*/
+static int cos_cache_create()
+{
+ int ret = -1;
+ cosCache *pNewCache;
+ static int firstTime = 1;
+ int cache_built = 0;
+
+ LDAPDebug( LDAP_DEBUG_TRACE, "--> cos_cache_create\n",0,0,0);
+
+ pNewCache = (cosCache*)slapi_ch_malloc(sizeof(cosCache));
+ if(pNewCache)
+ {
+ pNewCache->pDefs = 0;
+ pNewCache->refCount = 1; /* 1 is for us */
+ pNewCache->vattr_cacheable = 0; /* default is not cacheable */
+
+ ret = cos_cache_build_definition_list(&(pNewCache->pDefs), &(pNewCache->vattr_cacheable));
+ if(!ret)
+ {
+ /* OK, we have a cache, lets add indexing for
+ that faster than slow feeling */
+
+ ret = cos_cache_index_all(pNewCache);
+ if(ret == 0)
+ {
+ /* right, indexed cache, lets do our duty for the schema */
+
+ ret = cos_cache_schema_build(pNewCache);
+ if(ret == 0)
+ {
+ /* now to swap the new cache for the old cache */
+ cosCache *pOldCache;
+
+ slapi_lock_mutex(cache_lock);
+
+ /* turn off caching until the old cache is done */
+ if(pCache)
+ {
+ slapi_vattrcache_cache_none();
+
+ /*
+ * be sure not to uncache other stuff
+ * like roles if there is no change in
+ * state
+ */
+ if(pCache->vattr_cacheable)
+ slapi_entrycache_vattrcache_watermark_invalidate();
+ }
+ else
+ {
+ if(pNewCache && pNewCache->vattr_cacheable)
+ {
+ slapi_vattrcache_cache_all();
+ }
+ }
+
+ pOldCache = pCache;
+ pCache = pNewCache;
+
+ slapi_unlock_mutex(cache_lock);
+
+ if(pOldCache)
+ cos_cache_release(pOldCache);
+
+ cache_built = 1;
+ }
+ else
+ {
+ /* we should not go on without proper schema checking */
+ cos_cache_release(pNewCache);
+ LDAPDebug( LDAP_DEBUG_ANY, "cos_cache_create: failed to cache the schema\n",0,0,0);
+ }
+ }
+ else
+ {
+ /* currently we cannot go on without the indexes */
+ cos_cache_release(pNewCache);
+ LDAPDebug( LDAP_DEBUG_ANY, "cos_cache_create: failed to index cache\n",0,0,0);
+ }
+ }
+ else
+ {
+ if(firstTime)
+ {
+ LDAPDebug( LDAP_DEBUG_PLUGIN, "cos_cache_create: cos disabled\n",0,0,0);
+ firstTime = 0;
+ }
+
+ slapi_ch_free((void**)&pNewCache);
+ }
+ }
+ else
+ LDAPDebug( LDAP_DEBUG_ANY, "cos_cache_create: memory allocation failure\n",0,0,0);
+
+
+ /* make sure we have a new cache */
+ if(!cache_built)
+ {
+ /* we do not have a new cache, must make sure the old cache is destroyed */
+
+ cosCache *pOldCache;
+
+ slapi_lock_mutex(cache_lock);
+
+ slapi_vattrcache_cache_none();
+
+ /*
+ * be sure not to uncache other stuff
+ * like roles if there is no change in
+ * state
+ */
+ if(pCache && pCache->vattr_cacheable)
+ slapi_entrycache_vattrcache_watermark_invalidate();
+
+ pOldCache = pCache;
+ pCache = NULL;
+
+ slapi_unlock_mutex(cache_lock);
+
+ if(pOldCache)
+ cos_cache_release(pOldCache); /* release our reference to the old cache */
+
+ }
+
+ LDAPDebug( LDAP_DEBUG_TRACE, "<-- cos_cache_create\n",0,0,0);
+ return ret;
+}
+
+
+/*
+ cos_cache_build_definition_list
+ -------------------------------
+ builds the list of cos definitions by searching for them throughout the DIT
+*/
+static int cos_cache_build_definition_list(cosDefinitions **pDefs, int *vattr_cacheable)
+{
+ int ret = 0;
+ Slapi_PBlock *pSuffixSearch = 0;
+ Slapi_Entry **pSuffixList = 0;
+ Slapi_Attr *suffixAttr;
+ struct berval **suffixVals;
+ char *attrType = 0;
+ char *attrs[2];
+ int suffixIndex = 0;
+ int valIndex = 0;
+ int cos_def_available = 0;
+ static int firstTime = 1;
+
+ LDAPDebug( LDAP_DEBUG_TRACE, "--> cos_cache_build_definition_list\n",0,0,0);
+
+ /*
+ the class of service definitions may be anywhere in the DIT,
+ so our first task is to find them.
+ */
+
+ attrs[0] = "namingcontexts";
+ attrs[1] = 0;
+
+ LDAPDebug( LDAP_DEBUG_PLUGIN, "cos: Building class of service cache after status change.\n",0,0,0);
+
+ /*
+ * XXXrbyrne: this looks really ineficient--should be using
+ * slapi_get_next_suffix(), rather than searching for namingcontexts.
+ */
+
+ pSuffixSearch = slapi_search_internal("",LDAP_SCOPE_BASE,"(objectclass=*)",NULL,attrs,0);
+ if(pSuffixSearch)
+ slapi_pblock_get( pSuffixSearch, SLAPI_PLUGIN_INTOP_RESULT, &ret);
+
+ if(pSuffixSearch && ret == LDAP_SUCCESS)
+ {
+ /* iterate through the suffixes and search for cos definitions */
+ slapi_pblock_get( pSuffixSearch, SLAPI_PLUGIN_INTOP_SEARCH_ENTRIES, &pSuffixList);
+ if(pSuffixList)
+ {
+ while(pSuffixList[suffixIndex])
+ {
+ if(!slapi_entry_first_attr(pSuffixList[suffixIndex], &suffixAttr))
+ {
+ do
+ {
+ attrType = 0;
+ slapi_attr_get_type(suffixAttr, &attrType);
+ if(attrType && !slapi_utf8casecmp((unsigned char*)attrType, (unsigned char*)"namingcontexts"))
+ {
+ if(!slapi_attr_get_bervals_copy(suffixAttr, &suffixVals))
+ {
+ valIndex = 0;
+
+ if(suffixVals)
+ {
+ while(suffixVals[valIndex])
+ {
+ /* here's a suffix, lets search it... */
+ if(suffixVals[valIndex]->bv_val)
+ if(!cos_cache_add_dn_defs(suffixVals[valIndex]->bv_val ,pDefs, vattr_cacheable))
+ cos_def_available = 1;
+
+ valIndex++;
+ }
+
+
+ ber_bvecfree( suffixVals );
+ suffixVals = NULL;
+ }
+ }
+ }
+
+ } while(!slapi_entry_next_attr(pSuffixList[suffixIndex], suffixAttr, &suffixAttr));
+ }
+ suffixIndex++;
+ }
+ }
+ }
+ else
+ {
+ LDAPDebug( LDAP_DEBUG_ANY, "cos_cache_build_definition_list: failed to find suffixes\n",0,0,0);
+ ret = -1;
+ }
+
+ if(cos_def_available == 0)
+ {
+ if(firstTime)
+ {
+ LDAPDebug( LDAP_DEBUG_PLUGIN, "cos_cache_build_definition_list: Found no cos definitions, cos disabled while waiting for updates\n",0,0,0);
+ firstTime = 0;
+ }
+
+ ret = -1;
+ }
+ else
+ LDAPDebug( LDAP_DEBUG_PLUGIN, "cos: Class of service cache built.\n",0,0,0);
+
+ /* clean up */
+ if(pSuffixSearch)
+ {
+ slapi_free_search_results_internal(pSuffixSearch);
+ slapi_pblock_destroy(pSuffixSearch);
+ }
+
+
+ LDAPDebug( LDAP_DEBUG_TRACE, "<-- cos_cache_build_definition_list\n",0,0,0);
+ return ret;
+}
+
+
+/* struct to support search callback API */
+struct dn_defs_info {
+ cosDefinitions **pDefs;
+ int vattr_cacheable;
+ int ret;
+};
+
+/*
+ * Currently, always returns 0 to continue the search for definitions, even
+ * if a particular attempt to add a definition fails: info.ret gets set to
+ * zero only if we succed to add a def.
+*/
+static int cos_dn_defs_cb (Slapi_Entry* e, void *callback_data) {
+ struct dn_defs_info *info;
+ cosAttrValue **pSneakyVal = 0;
+ cosAttrValue *pObjectclass = 0;
+ cosAttrValue *pCosTargetTree = 0;
+ cosAttrValue *pCosTemplateDn = 0;
+ cosAttrValue *pCosSpecifier = 0;
+ cosAttrValue *pCosAttribute = 0;
+ cosAttrValue *pCosOverrides = 0;
+ cosAttrValue *pCosOperational = 0;
+ cosAttrValue *pCosOpDefault = 0;
+ cosAttrValue *pCosMerge = 0;
+ cosAttrValue *pDn = 0;
+ struct berval **dnVals;
+ int cosType = 0;
+ int valIndex = 0;
+ Slapi_Attr *dnAttr;
+ char *attrType = 0;
+ char *attrs[7];
+
+ attrs[0] = "objectclass";
+ attrs[1] = "cosTargetTree";
+ attrs[2] = "cosTemplateDn";
+ attrs[3] = "cosSpecifier";
+ attrs[4] = "cosAttribute";
+ attrs[5] = 0;
+ info=(struct dn_defs_info *)callback_data;
+
+
+ /* assume cacheable */
+ info->vattr_cacheable = -1;
+
+ cos_cache_add_attrval(&pDn, slapi_entry_get_dn(e));
+ if(!slapi_entry_first_attr(e, &dnAttr))
+ {
+ do
+ {
+ attrType = 0;
+ /* we need to fill in the details of the definition now */
+ slapi_attr_get_type(dnAttr, &attrType);
+ if(attrType)
+ {
+ pSneakyVal = 0;
+ if(!slapi_utf8casecmp((unsigned char*)attrType, (unsigned char*)"objectclass"))
+ pSneakyVal = &pObjectclass;
+ else if(!slapi_utf8casecmp((unsigned char*)attrType, (unsigned char*)"cosTargetTree"))
+ pSneakyVal = &pCosTargetTree;
+ else if(!slapi_utf8casecmp((unsigned char*)attrType, (unsigned char*)"cosTemplateDn"))
+ pSneakyVal = &pCosTemplateDn;
+ else if(!slapi_utf8casecmp((unsigned char*)attrType, (unsigned char*)"cosSpecifier"))
+ pSneakyVal = &pCosSpecifier;
+ else if(!slapi_utf8casecmp((unsigned char*)attrType, (unsigned char*)"cosAttribute"))
+ pSneakyVal = &pCosAttribute;
+ else if(!slapi_utf8casecmp((unsigned char*)attrType, (unsigned char*)"cosIndirectSpecifier"))
+ pSneakyVal = &pCosSpecifier;
+ if(pSneakyVal)
+ {
+ /* It's a type we're interested in */
+ if(!slapi_attr_get_bervals_copy(dnAttr, &dnVals))
+ {
+ valIndex = 0;
+ if(dnVals)
+ {
+ while(dnVals[valIndex])
+ {
+ if(dnVals[valIndex]->bv_val)
+ {
+ /*
+ parse any overide or default values
+ and deal with them
+ */
+ if(pSneakyVal == &pCosAttribute)
+ {
+ cosAttrValue *pTmpTargetTree = 0;
+ int qualifier_hit = 0;
+ int op_qualifier_hit = 0;
+ int merge_schemes_qualifier_hit = 0;
+ int override_qualifier_hit =0;
+ int default_qualifier_hit = 0;
+ int operational_default_qualifier_hit = 0;
+ do
+ {
+ qualifier_hit = 0;
+
+ if(cos_cache_backwards_stricmp_and_clip(dnVals[valIndex]->bv_val, " operational"))
+ {
+ /* matched */
+ op_qualifier_hit = 1;
+ qualifier_hit = 1;
+ }
+
+ if(cos_cache_backwards_stricmp_and_clip(dnVals[valIndex]->bv_val, " merge-schemes"))
+ {
+ /* matched */
+ merge_schemes_qualifier_hit = 1;
+ qualifier_hit = 1;
+ }
+
+ if(cos_cache_backwards_stricmp_and_clip(dnVals[valIndex]->bv_val, " override"))
+ {
+ /* matched */
+ override_qualifier_hit = 1;
+ qualifier_hit = 1;
+ }
+
+ if(cos_cache_backwards_stricmp_and_clip(dnVals[valIndex]->bv_val, " default")) {
+ default_qualifier_hit = 1;
+ qualifier_hit = 1;
+ }
+
+ if(cos_cache_backwards_stricmp_and_clip(dnVals[valIndex]->bv_val, " operational-default")) {
+ operational_default_qualifier_hit = 1;
+ qualifier_hit = 1;
+ }
+ }
+ while(qualifier_hit == 1);
+
+ /*
+ * At this point, dnVals[valIndex]->bv_val
+ * is the value of cosAttribute, stripped of
+ * any qualifiers, so add this pure attribute type to
+ * the appropriate lists.
+ */
+
+ if ( op_qualifier_hit ) {
+ cos_cache_add_attrval(&pCosOperational,
+ dnVals[valIndex]->bv_val);
+ }
+ if ( merge_schemes_qualifier_hit ) {
+ cos_cache_add_attrval(&pCosMerge,
+ dnVals[valIndex]->bv_val);
+ }
+ if ( override_qualifier_hit ) {
+ cos_cache_add_attrval(&pCosOverrides,
+ dnVals[valIndex]->bv_val);
+ }
+ if ( default_qualifier_hit ) {
+ /* attr is added below in pSneakyVal, in any case */
+ }
+
+ if ( operational_default_qualifier_hit ) {
+ cos_cache_add_attrval(&pCosOpDefault,
+ dnVals[valIndex]->bv_val);
+ }
+
+ if(!pCosTargetTree)
+ {
+ /* get the parent of the definition */
+
+ char *parent = slapi_dn_parent(pDn->val);
+ slapi_dn_normalize( parent );
+
+ cos_cache_add_attrval(&pCosTargetTree, parent);
+ if(!pCosTemplateDn)
+ cos_cache_add_attrval(&pCosTemplateDn, parent);
+
+ slapi_ch_free((void**)&parent);
+ }
+
+ pTmpTargetTree = pCosTargetTree;
+
+ slapi_vattrspi_regattr((vattr_sp_handle *)vattr_handle, dnVals[valIndex]->bv_val, NULL, NULL);
+ } /* if(attrType is cosAttribute) */
+
+ /*
+ * Add the attributetype to the appropriate
+ * list.
+ */
+ cos_cache_add_attrval(pSneakyVal,
+ dnVals[valIndex]->bv_val);
+ }/*if(dnVals[valIndex]->bv_val)*/
+
+ valIndex++;
+ }/* while(dnVals[valIndex]) */
+
+ ber_bvecfree( dnVals );
+ dnVals = NULL;
+ }/*if(dnVals)*/
+ }
+ }/*if(pSneakyVal)*/
+ }/*if(attrType)*/
+
+ } while(!slapi_entry_next_attr(e, dnAttr, &dnAttr));
+
+ /*
+ determine the type of class of service scheme
+ */
+
+ if(pObjectclass)
+ {
+ if(cos_cache_attrval_exists(pObjectclass, "cosDefinition"))
+ {
+ cosType = COSTYPE_CLASSIC;
+ }
+ else if(cos_cache_attrval_exists(pObjectclass, "cosClassicDefinition"))
+ {
+ cosType = COSTYPE_CLASSIC;
+
+ }
+ else if(cos_cache_attrval_exists(pObjectclass, "cosPointerDefinition"))
+ {
+ cosType = COSTYPE_POINTER;
+
+ }
+ else if(cos_cache_attrval_exists(pObjectclass, "cosIndirectDefinition"))
+ {
+ cosType = COSTYPE_INDIRECT;
+
+ }
+ else
+ cosType = COSTYPE_BADTYPE;
+ }
+
+ /*
+ we should now have a full definition,
+ do some sanity checks because we don't
+ want bogus entries in the cache
+ then ship it
+ */
+
+ /* these must exist */
+ if( pDn &&
+ pObjectclass &&
+
+ (
+ (cosType == COSTYPE_CLASSIC &&
+ pCosTemplateDn &&
+ pCosSpecifier &&
+ pCosAttribute )
+ ||
+ (cosType == COSTYPE_POINTER &&
+ pCosTemplateDn &&
+ pCosAttribute )
+ ||
+ (cosType == COSTYPE_INDIRECT &&
+ pCosSpecifier &&
+ pCosAttribute )
+ )
+ )
+ {
+ int rc = 0;
+ /*
+ we'll leave the referential integrity stuff
+ up to the referint plug-in and assume all
+ is good - if it's not then we just have a
+ useless definition and we'll nag copiously later.
+ */
+ char *pTmpDn = slapi_ch_strdup(pDn->val); /* because dn gets hosed on error */
+ char ebuf[ BUFSIZ ];
+
+ if(!(rc = cos_cache_add_defn(info->pDefs, &pDn, cosType,
+ &pCosTargetTree, &pCosTemplateDn,
+ &pCosSpecifier, &pCosAttribute,
+ &pCosOverrides, &pCosOperational,
+ &pCosMerge, &pCosOpDefault))) {
+ info->ret = 0; /* we have succeeded to add the defn*/
+ } else {
+ /*
+ * Failed but we will continue the search for other defs
+ * Don't reset info->ret....it keeps track of any success
+ */
+ if ( rc == COS_DEF_ERROR_NO_TEMPLATES) {
+ LDAPDebug(LDAP_DEBUG_ANY, "skipping cos definition %s"
+ "--no templates found\n",
+ escape_string(pTmpDn, ebuf),0,0);
+ } else {
+ LDAPDebug(LDAP_DEBUG_ANY, "skipping cos definition %s\n"
+ ,escape_string(pTmpDn, ebuf),0,0);
+ }
+ }
+
+ slapi_ch_free_string(&pTmpDn);
+ }
+ else
+ {
+ /*
+ this definition is brain dead - bail
+ if we have a dn use it to report, if not then *really* bad
+ things are going on
+ */
+ if(pDn)
+ {
+ LDAPDebug( LDAP_DEBUG_ANY, "cos_cache_add_dn_defs: incomplete cos definition detected in %s, discarding from cache.\n",pDn->val,0,0);
+ }
+ else
+ LDAPDebug( LDAP_DEBUG_ANY, "cos_cache_add_dn_defs: incomplete cos definition detected, no DN to report, discarding from cache.\n",0,0,0);
+
+ if(pCosTargetTree)
+ cos_cache_del_attrval_list(&pCosTargetTree);
+ if(pCosTemplateDn)
+ cos_cache_del_attrval_list(&pCosTemplateDn);
+ if(pCosSpecifier)
+ cos_cache_del_attrval_list(&pCosSpecifier);
+ if(pCosAttribute)
+ cos_cache_del_attrval_list(&pCosAttribute);
+ if(pDn)
+ cos_cache_del_attrval_list(&pDn);
+ }
+ }/*if(!slapi_entry_first_attr(e, &dnAttr))*/
+ /* we don't keep the objectclasses, so lets free them */
+ if(pObjectclass) {
+ cos_cache_del_attrval_list(&pObjectclass);
+ }
+ /* This particular definition may not have yielded anything
+ * worth caching (eg. no template was found for it) but
+ * that should not cause us to abort the search for other more well behaved
+ * definitions.
+ * return info->ret;
+ */
+ return (0);
+
+}
+
+/*
+ cos_cache_add_dn_defs
+ -------------------------
+ takes a dn as argument and searches the dn for cos definitions,
+ adding any found to the definition list. Change to use search callback API.
+
+ Returns: 0: found at least one definition entry that got added to the
+ cache successfully.
+ non-zero: added no cos defs to the cache.
+*/
+
+#define DN_DEF_FILTER "(&(|(objectclass=cosSuperDefinition)(objectclass=cosDefinition))(objectclass=ldapsubentry))"
+
+static int cos_cache_add_dn_defs(char *dn, cosDefinitions **pDefs, int *vattr_cacheable)
+{
+ Slapi_PBlock *pDnSearch = 0;
+ struct dn_defs_info info;
+ pDnSearch = slapi_pblock_new();
+ if (pDnSearch) {
+ info.ret=-1; /* assume no good defs */
+ info.pDefs=pDefs;
+ info.vattr_cacheable = 0; /* assume not cacheable */
+ slapi_search_internal_set_pb(pDnSearch, dn, LDAP_SCOPE_SUBTREE,
+ DN_DEF_FILTER,NULL,0,
+ NULL,NULL,cos_get_plugin_identity(),0);
+ slapi_search_internal_callback_pb(pDnSearch,
+ &info /* callback_data */,
+ NULL/* result_callback */,
+ cos_dn_defs_cb,
+ NULL /* referral_callback */);
+ slapi_pblock_destroy (pDnSearch);
+ }
+
+ *vattr_cacheable = info.vattr_cacheable;
+
+ return info.ret;
+}
+
+
+
+/* struct to support call back API */
+
+struct tmpl_info {
+ cosAttrValue *pCosSpecifier;
+ cosAttrValue *pAttrs;
+ cosTemplates **pTmpls;
+ int ret;
+};
+
+
+/*
+ * Currently, always returns 0 to continue the search for templates, even
+ * if a particular attempt to add a template fails: info.ret gets set to
+ * zero only if we succed to add at least one tmpl.
+*/
+static int cos_dn_tmpl_entries_cb (Slapi_Entry* e, void *callback_data) {
+ cosAttrValue *pDn = 0;
+ cosAttrValue *pCosPriority = 0;
+ cosAttributes *pAttributes = 0;
+ cosAttrValue *pObjectclass = 0;
+ cosAttrValue *pCosAttribute = 0;
+ Slapi_Attr *dnAttr;
+ struct berval **dnVals;
+ int itsAnAttr = 0;
+ int valIndex = 0;
+ cosAttrValue **pSneakyVal = 0;
+ char *attrType = 0;
+ struct tmpl_info *info;
+ info = (struct tmpl_info *)callback_data;
+
+ pDn = 0;
+ cos_cache_add_attrval(&pDn, slapi_entry_get_dn(e));
+ pAttributes = 0;
+ pObjectclass = 0;
+ pCosPriority = 0;
+
+ if(!slapi_entry_first_attr(e, &dnAttr))
+ {
+ int attrs_present = 0;
+
+ do
+ {
+ attrType = 0;
+ pCosAttribute = 0;
+
+ /* we need to fill in the details of the template now */
+ slapi_attr_get_type(dnAttr, &attrType);
+
+ if(attrType)
+ {
+ itsAnAttr = 0;
+ pSneakyVal = 0;
+
+ if(!slapi_utf8casecmp((unsigned char*)attrType,
+ (unsigned char*)"objectclass"))
+ pSneakyVal = &pObjectclass;
+
+ if(!slapi_utf8casecmp((unsigned char*)attrType,
+ (unsigned char*)"cosPriority"))
+ pSneakyVal = &pCosPriority;
+
+ if(pSneakyVal == NULL)
+ {
+ /* look for the atrribute in the dynamic attributes */
+ if(cos_cache_attrval_exists(info->pAttrs, attrType))
+ {
+ pSneakyVal = &pCosAttribute;
+ itsAnAttr = 1;
+ attrs_present = 1;
+ }
+ }
+
+ if(pSneakyVal)
+ {
+ if(!slapi_attr_get_bervals_copy(dnAttr, &dnVals))
+ {
+ valIndex = 0;
+
+ if(dnVals)
+ {
+ while(dnVals[valIndex])
+ {
+ if(dnVals[valIndex]->bv_val)
+ cos_cache_add_attrval(pSneakyVal,
+ dnVals[valIndex]->bv_val);
+
+ valIndex++;
+ }
+
+ if(itsAnAttr)
+ {
+ /* got all vals, add this attribute to the attribute list */
+ cos_cache_add_attr(&pAttributes, attrType,
+ *pSneakyVal);
+ }
+
+ ber_bvecfree( dnVals );
+ dnVals = NULL;
+ }
+ }
+ }
+ }
+
+ } while(!slapi_entry_next_attr(e, dnAttr, &dnAttr));
+
+ /*
+ we should now have a full template,
+ do some sanity checks because we don't
+ want bogus entries in the cache if we can help it
+ - then ship it
+ */
+
+ /* these must exist */
+ if(
+ attrs_present &&
+ pObjectclass &&
+ pAttributes &&
+ pDn
+ )
+ {
+ /*
+ we'll leave the referential integrity stuff
+ up to the referint plug-in if set up and assume all
+ is good - if it's not then we just have a
+ useless definition and we'll nag copiously later.
+ */
+
+ if(!cos_cache_add_tmpl(info->pTmpls, pDn, pObjectclass,
+ info->pCosSpecifier, pAttributes,pCosPriority)){
+ info->ret = 0; /* we have succeed to add the tmpl */
+ } else {
+ /* Don't reset info->ret....it keeps track of any success */
+ LDAPDebug(LDAP_DEBUG_ANY, "cos_cache_add_dn_tmpls:"
+ "could not cache cos template %s\n",pDn,0,0);
+ }
+ }
+ else
+ {
+ /*
+ this template is brain dead - bail
+ if we have a dn use it to report, if not then *really* bad
+ things are going on
+ - of course it might not be a template, so lets
+ not tell the world unless the world wants to know,
+ we'll make it a plugin level message
+ */
+ if(pDn)
+ {
+ LDAPDebug( LDAP_DEBUG_PLUGIN, "cos_cache_add_dn_tmpls: incomplete cos template detected in %s, discarding from cache.\n",pDn->val,0,0);
+ }
+ else
+ LDAPDebug( LDAP_DEBUG_PLUGIN, "cos_cache_add_dn_tmpls: incomplete cos template detected, no DN to report, discarding from cache.\n",0,0,0);
+
+ if(pObjectclass)
+ cos_cache_del_attrval_list(&pObjectclass);
+ if(pCosAttribute)
+ cos_cache_del_attrval_list(&pCosAttribute);
+ if(pDn)
+ cos_cache_del_attrval_list(&pDn);
+ if(pAttributes)
+ cos_cache_del_attr_list(&pAttributes);
+ }
+ }
+ /*
+ * Always contine the search even if a particular attempt
+ * to add a template failed.
+ */
+ return 0;
+}
+
+/*
+ cos_cache_add_dn_tmpls
+ -------------------------
+ takes a dn as argument and searches the dn for cos templates,
+ adding any found to the template list
+ This is the new version using call back search API
+
+ Returns: zero for success--found at least one good tmpl for this def.
+ non-zero: failed to add any templs for this def.
+*/
+
+#define TMPL_FILTER "(&(objectclass=costemplate)(|(objectclass=costemplate)(objectclass=ldapsubentry)))"
+
+static int cos_cache_add_dn_tmpls(char *dn, cosAttrValue *pCosSpecifier, cosAttrValue *pAttrs, cosTemplates **pTmpls)
+{
+ void *plugin_id;
+ int scope;
+ struct tmpl_info info;
+ Slapi_PBlock *pDnSearch = 0;
+
+ LDAPDebug( LDAP_DEBUG_TRACE, "--> cos_cache_add_dn_tmpls\n",0,0,0);
+
+ /* no cos specifier means this is an indirect scheme */
+ if(pCosSpecifier)
+ scope = LDAP_SCOPE_ONELEVEL;
+ else
+ scope = LDAP_SCOPE_BASE;
+
+ /* Use new internal operation API */
+ pDnSearch = slapi_pblock_new();
+ plugin_id=cos_get_plugin_identity();
+ if (pDnSearch) {
+ info.pAttrs=pAttrs;
+ info.pTmpls=pTmpls;
+ info.pCosSpecifier=pCosSpecifier;
+ info.ret=-1; /* assume no good tmpls */
+ slapi_search_internal_set_pb(pDnSearch, dn, scope,
+ TMPL_FILTER,NULL,0,
+ NULL,NULL,plugin_id,0);
+ slapi_search_internal_callback_pb(pDnSearch,
+ &info /* callback_data */,
+ NULL/* result_callback */,
+ cos_dn_tmpl_entries_cb,
+ NULL /* referral_callback */);
+ slapi_pblock_destroy (pDnSearch);
+ }
+ /*
+ * info.ret comes out zero only if we succeed to add at least one
+ * tmpl to the cache.
+ */
+ return (info.ret);
+}
+
+/*
+ cos_cache_add_defn
+ ------------------
+ Add a cos definition to the list and create the template
+ cache for this definition
+ returns: 0: successfully added the definition to the cache
+ non-zero: failed to add the definition to the cache (eg. because
+ there was no template for it.)
+ ret==COS_DEF_ERROR_NO_TEMPLATES then no templs were found
+ for thsi def. We make a special case of this and pass the
+ back the error so we can roll two messages into one--this
+ is to reduce the number of error messages at cos definiton
+ load time--it is common to see the defs before the tmpls
+ arrive.
+
+*/
+static int cos_cache_add_defn(
+ cosDefinitions **pDefs,
+ cosAttrValue **dn,
+ int cosType,
+ cosAttrValue **tree,
+ cosAttrValue **tmpDn,
+ cosAttrValue **spec,
+ cosAttrValue **pAttrs,
+ cosAttrValue **pOverrides,
+ cosAttrValue **pOperational,
+ cosAttrValue **pCosMerge,
+ cosAttrValue **pCosOpDefault
+ )
+{
+ int ret = 0;
+ int tmplCount = 0;
+ cosDefinitions *theDef = 0;
+ cosAttrValue *pTmpTmplDn = *tmpDn;
+ cosAttrValue *pDummyAttrVal = 0;
+ cosAttrValue *pAttrsIter = 0;
+ cosAttributes *pDummyAttributes = 0;
+ cosAttrValue *pSpecsIter = *spec;
+
+ LDAPDebug( LDAP_DEBUG_TRACE, "--> cos_cache_add_defn\n",0,0,0);
+
+ /* we don't want cosspecifiers that can be supplied by the same scheme */
+ while( pSpecsIter )
+ {
+ if( cos_cache_attrval_exists(*pAttrs, pSpecsIter->val ) )
+ {
+ /* no, this is not sane, lets reject the whole darn scheme in disgust */
+ LDAPDebug( LDAP_DEBUG_ANY, "cos definition %s supplies its own cosspecifier, rejecting scheme\n",(*dn)->val,0,0);
+ ret = -1;
+ }
+
+ pSpecsIter = pSpecsIter->list.pNext;
+ }
+
+ /* create the definition */
+ if(0 == ret)
+ {
+ theDef = (cosDefinitions*) slapi_ch_malloc(sizeof(cosDefinitions));
+ if(theDef)
+ {
+ theDef->pCosTmps = NULL;
+
+ /* process each template in turn */
+
+ LDAPDebug( LDAP_DEBUG_PLUGIN, "Processing cosDefinition %s\n",(*dn)->val,0,0);
+
+ while(pTmpTmplDn && cosType != COSTYPE_INDIRECT)
+ {
+ /* create the template */
+ if(!cos_cache_add_dn_tmpls(pTmpTmplDn->val, *spec, *pAttrs, &(theDef->pCosTmps)))
+ tmplCount++;
+
+ pTmpTmplDn = pTmpTmplDn->list.pNext;
+ }
+
+ if(tmplCount == 0 && cosType != COSTYPE_INDIRECT)
+ {
+ /* without our golden templates we are nothing
+ LDAPDebug( LDAP_DEBUG_ANY, "cos_cache_add_defn:"
+ "no templates for cos definition at %s.\n",(*dn)->val,0,0);*/
+ ret = COS_DEF_ERROR_NO_TEMPLATES;
+ }
+ else
+ {
+ if(cosType == COSTYPE_INDIRECT)
+ {
+ /*
+ Indirect cos schemes have no templates,
+ however, in order to take advantage of existing code
+ which is optimized to do a binary search on attributes
+ which are found through their templates, we add a dummy
+ template and dummy attributes. The value of the attributes
+ will be ignored when later assessing a query.
+ */
+ pAttrsIter = *pAttrs;
+
+ while(pAttrsIter)
+ {
+ pDummyAttrVal = NULL;
+ cos_cache_add_attrval(&pDummyAttrVal, "not used");
+ cos_cache_add_attr(&pDummyAttributes, pAttrsIter->val, pDummyAttrVal);
+
+ pAttrsIter = pAttrsIter->list.pNext;
+ }
+
+ cos_cache_add_attrval(tmpDn, "cn=dummy,");
+
+ cos_cache_add_tmpl(&(theDef->pCosTmps), *tmpDn, NULL, *spec, pDummyAttributes,NULL);
+ *tmpDn = 0;
+ }
+
+ theDef->pDn = *dn;
+ theDef->cosType = cosType;
+ theDef->pCosTargetTree = *tree;
+ theDef->pCosTemplateDn = *tmpDn;
+ theDef->pCosSpecifier = *spec;
+ theDef->pCosAttrs = *pAttrs;
+ theDef->pCosOverrides = *pOverrides;
+ theDef->pCosOperational = *pOperational;
+ theDef->pCosMerge = *pCosMerge;
+ theDef->pCosOpDefault = *pCosOpDefault;
+
+ cos_cache_add_ll_entry((void**)pDefs, theDef, NULL);
+ LDAPDebug( LDAP_DEBUG_PLUGIN, "Added cosDefinition %s\n",(*dn)->val,0,0);
+ }
+ }
+ else
+ {
+ ret = -1;
+ }
+ }
+
+ if(ret == -1)
+ {
+ if(dn)
+ cos_cache_del_attrval_list(dn);
+ if(tree)
+ cos_cache_del_attrval_list(tree);
+ if(tmpDn)
+ cos_cache_del_attrval_list(tmpDn);
+ if(spec)
+ cos_cache_del_attrval_list(spec);
+ if(pAttrs)
+ cos_cache_del_attrval_list(pAttrs);
+ if(theDef)
+ slapi_ch_free((void**)&theDef);
+ }
+
+ LDAPDebug( LDAP_DEBUG_TRACE, "<-- cos_cache_add_defn\n",0,0,0);
+ return ret;
+}
+
+/*
+ cos_cache_del_attrval_list
+ --------------------------
+ walks the list deleting as it goes
+*/
+static void cos_cache_del_attrval_list(cosAttrValue **pVal)
+{
+ LDAPDebug( LDAP_DEBUG_TRACE, "--> cos_cache_del_attrval_list\n",0,0,0);
+
+ while(*pVal)
+ {
+ cosAttrValue *pTmp = (*pVal)->list.pNext;
+
+ slapi_ch_free((void**)&((*pVal)->val));
+ slapi_ch_free((void**)&(*pVal));
+ *pVal = pTmp;
+ }
+
+ LDAPDebug( LDAP_DEBUG_TRACE, "<-- cos_cache_del_attrval_list\n",0,0,0);
+}
+
+
+/*
+ cos_cache_add_attrval
+ ---------------------
+ adds a value to an attribute value list
+*/
+static int cos_cache_add_attrval(cosAttrValue **attrval, char *val)
+{
+ int ret = 0;
+ cosAttrValue *theVal;
+
+ LDAPDebug( LDAP_DEBUG_TRACE, "--> cos_cache_add_attrval\n",0,0,0);
+
+ /* create the attrvalue */
+ theVal = (cosAttrValue*) slapi_ch_malloc(sizeof(cosAttrValue));
+ if(theVal)
+ {
+ theVal->val = slapi_ch_strdup(val);
+ if(theVal->val)
+ {
+ cos_cache_add_ll_entry((void**)attrval, theVal, NULL);
+ }
+ else
+ {
+ slapi_ch_free((void**)&theVal);
+ LDAPDebug( LDAP_DEBUG_ANY, "cos_cache_add_attrval: failed to allocate memory\n",0,0,0);
+ ret = -1;
+ }
+ }
+ else
+ {
+ LDAPDebug( LDAP_DEBUG_ANY, "cos_cache_add_attrval: failed to allocate memory\n",0,0,0);
+ ret = -1;
+ }
+
+ LDAPDebug( LDAP_DEBUG_TRACE, "<-- cos_cache_add_attrval\n",0,0,0);
+ return ret;
+}
+
+/*
+ cos_cache_add_ll_entry - RECURSIVE for sorted lists
+ ---------------------------------------------------
+ if a compare function is passed as argument, the element
+ is added to the linked list in the sorted order according
+ to that compare functions algorithm. This prepares the list
+ for ultra fast indexing - requiring only to walk the list once
+ to build the index.
+ if no compare function is passed, the element is added
+ to the head of the linked list
+ the index is created after the linked list is complete,
+ and so is always null until explicitly indexed
+
+ *NOTE* this function assumes and *requires* that the structures
+ passed to it in "attrval" and "theVal" have a cosIndexedLinkedList
+ member, and it is the *first* member of the structure. This
+ is safe because this is a module level function, and all functions
+ which call this one are part of the same sub-system.
+
+ The compare function will also make a similar assumption, but
+ likely one that recognizes the full structure or type, it is
+ the responsibility of the caller to match input structures with
+ the appropriate compare function.
+
+ WARNING: current recursive sorting code never used, never tested
+*/
+static void cos_cache_add_ll_entry(void** attrval, void *theVal, int ( *compare )(const void *elem1, const void *elem2 ))
+{
+ static cosIndexedLinkedList *pLastList = 0;
+ static cosIndexedLinkedList *first_element;
+ static int call_count = 0;
+
+ call_count++;
+ LDAPDebug( LDAP_DEBUG_TRACE, "--> cos_cache_add_ll_entry - recursion level %d\n",call_count,0,0);
+
+
+ /*
+ need to save the first element of the list
+ we update this first element whenever an entry
+ is added to the start of the list, this way
+ we can ensure that the head of the list is always
+ *attrval - callers pass us the head of the list
+ and can expect that what they get back is also
+ the head of the list
+ */
+ if(call_count == 1)
+ first_element = *attrval;
+
+ if(*attrval)
+ {
+ if(compare == NULL)
+ {
+ /* we dont want this list sorted */
+ /* push this to the start of the list (because its quick) */
+ ((cosIndexedLinkedList*)theVal)->pNext = *attrval;
+ ((cosIndexedLinkedList*)theVal)->index = NULL;
+ *attrval = theVal;
+ }
+ else
+ {
+ /* insert this in the list in sorted order
+ (because its quicker for building indexes later) */
+ int comp_ret = 0;
+
+ comp_ret = compare(*attrval, theVal);
+ if(comp_ret == 0 || comp_ret > 0)
+ {
+ /* insert prior to this element */
+ if(pLastList)
+ pLastList->pNext = theVal;
+ else
+ first_element = theVal;
+
+ ((cosIndexedLinkedList*)theVal)->pNext = *attrval;
+ ((cosIndexedLinkedList*)theVal)->index = NULL;
+ }
+ else
+ {
+ /* still looking - recurse on next element */
+ pLastList = (cosIndexedLinkedList*)attrval;
+ cos_cache_add_ll_entry(&(((cosIndexedLinkedList*)attrval)->pNext), theVal, compare);
+ }
+
+ if(call_count == 1)
+ *attrval = first_element;
+ }
+ }
+ else
+ {
+ /* new or end of list */
+ ((cosIndexedLinkedList*)theVal)->pNext = NULL;
+ ((cosIndexedLinkedList*)theVal)->index = NULL;
+ if(call_count == 1) /* if new list */
+ *attrval = theVal;
+ }
+
+ LDAPDebug( LDAP_DEBUG_TRACE, "<-- cos_cache_add_ll_entry - recursion level %d\n",call_count,0,0);
+ call_count--;
+}
+
+
+/*
+ cos_cache_getref
+ ----------------
+ retrieves a reference to the current cache and adds to the cache reference count
+*/
+int cos_cache_getref(cos_cache **pptheCache)
+{
+ int ret = -1;
+ static int first_time = 1;
+ cosCache **ppCache = (cosCache**)pptheCache;
+
+ LDAPDebug( LDAP_DEBUG_TRACE, "--> cos_cache_getref\n",0,0,0);
+
+ if(first_time)
+ {
+ first_time = 0;
+ /* first customer, create the cache */
+ slapi_lock_mutex(change_lock);
+ if(pCache == NULL)
+ {
+ if(cos_cache_create())
+ {
+ /* there was a problem or no COS definitions were found */
+ LDAPDebug( LDAP_DEBUG_PLUGIN, "cos_cache_getref: no cos cache created\n",0,0,0);
+ }
+ }
+ slapi_unlock_mutex(change_lock);
+ }
+
+ slapi_lock_mutex(cache_lock);
+
+ *ppCache = pCache;
+
+ if(pCache)
+ ret = ++((*ppCache)->refCount);
+
+ slapi_unlock_mutex(cache_lock);
+
+
+
+ LDAPDebug( LDAP_DEBUG_TRACE, "<-- cos_cache_getref\n",0,0,0);
+ return ret;
+}
+
+/*
+ cos_cache_addref
+ ----------------
+ adds 1 to the cache reference count
+*/
+int cos_cache_addref(cos_cache *ptheCache)
+{
+ int ret;
+ cosCache *pCache = (cosCache*)ptheCache;
+
+ LDAPDebug( LDAP_DEBUG_TRACE, "--> cos_cache_addref\n",0,0,0);
+
+ slapi_lock_mutex(cache_lock);
+
+ if(pCache)
+ ret = ++(pCache->refCount);
+
+ slapi_unlock_mutex(cache_lock);
+
+ LDAPDebug( LDAP_DEBUG_TRACE, "<-- cos_cache_addref\n",0,0,0);
+
+ return ret;
+}
+
+
+/*
+ cos_cache_release
+ -----------------
+ subtracts 1 from the cache reference count, if the count falls
+ below 1, the cache is destroyed.
+*/
+int cos_cache_release(cos_cache *ptheCache)
+{
+ int ret = 0;
+ int destroy = 0;
+ cosCache *pOldCache = (cosCache*)ptheCache;
+
+ LDAPDebug( LDAP_DEBUG_TRACE, "--> cos_cache_release\n",0,0,0);
+
+ slapi_lock_mutex(cache_lock);
+
+ if(pOldCache)
+ {
+ ret = --(pOldCache->refCount);
+ if(ret == 0)
+ destroy = 1;
+ }
+
+ slapi_unlock_mutex(cache_lock);
+
+ if(destroy)
+ {
+ cosDefinitions *pDef = pOldCache->pDefs;
+
+ /* now is the first time it is
+ * safe to assess whether
+ * vattr caching can be turned on
+ */
+ if(pCache && pCache->vattr_cacheable)
+ {
+ slapi_vattrcache_cache_all();
+ }
+
+ /* destroy the cache here - no locking required, no references outstanding */
+
+ if(pDef)
+ cos_cache_del_schema(pOldCache);
+
+ while(pDef)
+ {
+ cosDefinitions *pTmpD = pDef;
+ cosTemplates *pCosTmps = pDef->pCosTmps;
+
+ while(pCosTmps)
+ {
+ cosTemplates *pTmpT = pCosTmps;
+
+ pCosTmps = pCosTmps->list.pNext;
+
+ cos_cache_del_attr_list(&(pTmpT->pAttrs));
+ cos_cache_del_attrval_list(&(pTmpT->pObjectclasses));
+ cos_cache_del_attrval_list(&(pTmpT->pDn));
+ slapi_ch_free((void**)&(pTmpT->cosGrade));
+ slapi_ch_free((void**)&pTmpT);
+ }
+
+ pDef = pDef->list.pNext;
+
+ cos_cache_del_attrval_list(&(pTmpD->pDn));
+ cos_cache_del_attrval_list(&(pTmpD->pCosTargetTree));
+ cos_cache_del_attrval_list(&(pTmpD->pCosTemplateDn));
+ cos_cache_del_attrval_list(&(pTmpD->pCosSpecifier));
+ cos_cache_del_attrval_list(&(pTmpD->pCosAttrs));
+ cos_cache_del_attrval_list(&(pTmpD->pCosOverrides));
+ cos_cache_del_attrval_list(&(pTmpD->pCosOperational));
+ cos_cache_del_attrval_list(&(pTmpD->pCosMerge));
+ cos_cache_del_attrval_list(&(pTmpD->pCosOpDefault));
+ slapi_ch_free((void**)&pTmpD);
+ }
+
+ if(pOldCache->ppAttrIndex)
+ slapi_ch_free((void**)&(pOldCache->ppAttrIndex));
+ if(pOldCache->ppTemplateList)
+ slapi_ch_free((void**)&(pOldCache->ppTemplateList));
+ slapi_ch_free((void**)&pOldCache);
+ }
+
+ LDAPDebug( LDAP_DEBUG_TRACE, "<-- cos_cache_release\n",0,0,0);
+
+ return ret;
+}
+
+/*
+ cos_cache_attrval_count
+ -----------------------
+ counts the number of values in the list
+*/
+
+static int cos_cache_attrval_count(cosAttrValue *pVal)
+{
+ int ret = 0;
+
+ LDAPDebug( LDAP_DEBUG_TRACE, "--> cos_cache_attrval_count\n",0,0,0);
+
+ while(pVal)
+ {
+ ret++;
+ pVal = pVal->list.pNext;
+ }
+
+ LDAPDebug( LDAP_DEBUG_TRACE, "<-- cos_cache_attrval_count\n",0,0,0);
+ return ret;
+}
+
+/*
+ cos_cache_del_attr_list
+ -----------------------
+ walk the list deleting as we go
+*/
+static void cos_cache_del_attr_list(cosAttributes **pAttrs)
+{
+ LDAPDebug( LDAP_DEBUG_TRACE, "--> cos_cache_del_attr_list\n",0,0,0);
+
+ while(*pAttrs)
+ {
+ cosAttributes *pTmp = (*pAttrs)->list.pNext;
+
+ cos_cache_del_attrval_list(&((*pAttrs)->pAttrValue));
+ slapi_ch_free((void**)&((*pAttrs)->pAttrName));
+ slapi_ch_free((void**)&(*pAttrs));
+ *pAttrs = pTmp;
+ }
+
+
+ LDAPDebug( LDAP_DEBUG_TRACE, "<-- cos_cache_del_attr_list\n",0,0,0);
+}
+
+
+/*
+ cos_cache_del_schema
+ --------------------
+ delete the object class lists used for schema checking
+*/
+static void cos_cache_del_schema(cosCache *pCache)
+{
+ char *pLastName = 0;
+ cosAttrValue *pLastRef = 0;
+ int attr_index = 0;
+
+ LDAPDebug( LDAP_DEBUG_TRACE, "--> cos_cache_del_schema\n",0,0,0);
+
+ if(pCache && pCache->attrCount && pCache->ppAttrIndex)
+ {
+ pLastName = pCache->ppAttrIndex[0]->pAttrName;
+ pLastRef = pCache->ppAttrIndex[0]->pObjectclasses;
+
+ for(attr_index=1; attr_index<pCache->attrCount; attr_index++)
+ {
+ if(slapi_utf8casecmp((unsigned char*)pCache->ppAttrIndex[attr_index]->pAttrName, (unsigned char*)pLastName))
+ {
+ /* remember what went before */
+ pLastName = pCache->ppAttrIndex[attr_index]->pAttrName;
+
+ /* then blow it away */
+ cos_cache_del_attrval_list(&(pCache->ppAttrIndex[attr_index]->pObjectclasses));
+ }
+ }
+ }
+
+ LDAPDebug( LDAP_DEBUG_TRACE, "<-- cos_cache_del_schema\n",0,0,0);
+}
+
+
+/*
+ cos_cache_add_attr
+ ------------------
+ Adds an attribute to the list
+*/
+static int cos_cache_add_attr(cosAttributes **pAttrs, char *name, cosAttrValue *val)
+{
+ int ret =0;
+ cosAttributes *theAttr;
+
+ LDAPDebug( LDAP_DEBUG_TRACE, "--> cos_cache_add_attr\n",0,0,0);
+
+ /* create the attribute */
+ theAttr = (cosAttributes*) slapi_ch_malloc(sizeof(cosAttributes));
+ if(theAttr)
+ {
+ theAttr->pAttrValue = val;
+ theAttr->pObjectclasses = 0; /* schema issues come later */
+ theAttr->pAttrName = slapi_ch_strdup(name);
+ if(theAttr->pAttrName)
+ {
+ cos_cache_add_ll_entry((void**)pAttrs, theAttr, NULL);
+ LDAPDebug( LDAP_DEBUG_PLUGIN, "cos_cache_add_attr: Added attribute %s\n",name,0,0);
+ }
+ else
+ {
+ slapi_ch_free((void**)&theAttr);
+ LDAPDebug( LDAP_DEBUG_ANY, "cos_cache_add_attr: failed to allocate memory\n",0,0,0);
+ ret = -1;
+ }
+ }
+ else
+ {
+ LDAPDebug( LDAP_DEBUG_ANY, "cos_cache_add_attr: failed to allocate memory\n",0,0,0);
+ ret = -1;
+ }
+
+ LDAPDebug( LDAP_DEBUG_TRACE, "<-- cos_cache_add_attr\n",0,0,0);
+ return ret;
+}
+
+
+/*
+ cos_cache_add_tmpl
+ ------------------
+ Adds a template to the list
+*/
+static int cos_cache_add_tmpl(cosTemplates **pTemplates, cosAttrValue *dn, cosAttrValue *objclasses, cosAttrValue *pCosSpecifier, cosAttributes *pAttrs,cosAttrValue *cosPriority)
+{
+ int ret =0;
+ cosTemplates *theTemp;
+
+ LDAPDebug( LDAP_DEBUG_TRACE, "--> cos_cache_add_tmpl\n",0,0,0);
+
+ /* create the attribute */
+ theTemp = (cosTemplates*) slapi_ch_malloc(sizeof(cosTemplates));
+ if(theTemp)
+ {
+ char *grade = (char*)slapi_ch_malloc(strlen(dn->val)+1);
+ int grade_index = 0;
+ int index = 0;
+ int template_default = 0;
+
+ slapi_dn_normalize(dn->val);
+
+ /* extract the cos grade */
+ while(dn->val[index] != '=' && dn->val[index] != '\0')
+ index++;
+
+ if(dn->val[index] == '=')
+ {
+ int quotes = 0;
+
+ index++;
+
+ /* copy the grade (supports one level of quote nesting in rdn) */
+ while(dn->val[index] != ',' || dn->val[index-1] == '\\' || quotes == 1)
+ {
+ if(dn->val[index] == '"')
+ {
+ if(quotes == 0)
+ quotes = 1;
+ else
+ quotes = 0;
+ }
+ else
+ {
+ if(dn->val[index] != '\\') /* skip escape chars */
+ {
+ grade[grade_index] = dn->val[index];
+ grade_index++;
+ }
+ }
+
+ index++;
+ }
+
+ grade[grade_index] = '\0';
+
+ /* ok, grade copied, is it the default template? */
+
+ if(pCosSpecifier) /* some schemes don't have one */
+ {
+ char tmpGrade[BUFSIZ];
+
+ if (strlen(pCosSpecifier->val) < (BUFSIZ - 9)) { /* 9 for "-default" */
+ strcpy(tmpGrade, pCosSpecifier->val);
+ strcat(tmpGrade, "-default");
+ if(!slapi_utf8casecmp((unsigned char*)grade, (unsigned char*)tmpGrade))
+ template_default = 1;
+ }
+ else
+ {
+ /*
+ * We shouldn't pass ever through this code as we expect
+ * pCosSpecifier values to be reasonably smaller than BUFSIZ
+ *
+ * only 2 lines of code -> no need to set an indirect char *
+ * duplicate the lines of code for clearness instead
+ */
+ char * newTmpGrade = (char*) slapi_ch_malloc(
+ strlen((pCosSpecifier->val) + 9));
+ strcpy(newTmpGrade, pCosSpecifier->val);
+ strcat(newTmpGrade, "-default");
+ if(!slapi_utf8casecmp((unsigned char*)grade, (unsigned char*)newTmpGrade))
+ template_default = 1;
+ slapi_ch_free((void**)&newTmpGrade);
+ }
+ }
+
+ }
+ else
+ {
+ /* mmm, should never get here, it means the dn is whacky */
+ LDAPDebug( LDAP_DEBUG_ANY, "cos_cache_add_tmpl: malformed dn detected: %s\n",dn,0,0);
+ grade[0] = '\0';
+ }
+
+ /* now fill in the blanks */
+ theTemp->pDn = dn;
+ theTemp->pObjectclasses = objclasses;
+ theTemp->pAttrs = pAttrs;
+ theTemp->cosGrade = slapi_ch_strdup(grade);
+ theTemp->template_default = template_default;
+ theTemp->cosPriority = (unsigned long)-1;
+
+ if(cosPriority)
+ {
+ theTemp->cosPriority = atol(cosPriority->val);
+ cos_cache_del_attrval_list(&cosPriority);
+ }
+
+ cos_cache_add_ll_entry((void**)pTemplates, theTemp, NULL);
+ LDAPDebug( LDAP_DEBUG_PLUGIN, "cos_cache_add_tmpl: Added template %s\n",dn->val,0,0);
+
+ slapi_ch_free((void**)&grade);
+ }
+ else
+ {
+ LDAPDebug( LDAP_DEBUG_ANY, "cos_cache_add_tmpl: failed to allocate memory\n",0,0,0);
+ ret = -1;
+ }
+
+ LDAPDebug( LDAP_DEBUG_TRACE, "<-- cos_cache_add_tmpl\n",0,0,0);
+ return ret;
+}
+
+/*
+ cos_cache_attrval_exists
+ ------------------------
+ performs linear search on the list for a
+ cosAttrValue that matches val
+ however, if the "index" member of cosAttrValue
+ is non-null then a binary search is performed
+ on the index {PARPAR: TO BE DONE}
+
+ return 1 on found, 0 otherwise
+*/
+static int cos_cache_attrval_exists(cosAttrValue *pAttrs, const char *val)
+{
+ int ret = 0;
+
+ LDAPDebug( LDAP_DEBUG_TRACE, "--> cos_cache_attrval_exists\n",0,0,0);
+
+ while(pAttrs)
+ {
+ if(!slapi_utf8casecmp((unsigned char*)pAttrs->val,(unsigned char*)val))
+ {
+ ret = 1;
+ break;
+ }
+
+ pAttrs = pAttrs->list.pNext;
+ }
+
+ LDAPDebug( LDAP_DEBUG_TRACE, "<-- cos_cache_attrval_exists\n",0,0,0);
+ return ret;
+}
+
+
+
+static int cos_cache_vattr_get(vattr_sp_handle *handle, vattr_context *c, Slapi_Entry *e, char *type, Slapi_ValueSet** results,int *type_name_disposition, char** actual_type_name, int flags, int *free_flags, void *hint)
+{
+ int ret = -1;
+ cos_cache *pCache = 0;
+
+ LDAPDebug( LDAP_DEBUG_TRACE, "--> cos_cache_vattr_get\n",0,0,0);
+
+ if(cos_cache_getref(&pCache) < 1)
+ {
+ /* problems we are hosed */
+ LDAPDebug( LDAP_DEBUG_PLUGIN, "cos_cache_vattr_get: failed to get class of service reference\n",0,0,0);
+ goto bail;
+ }
+
+ ret = cos_cache_query_attr(pCache, c, e, type, results, NULL, NULL, NULL);
+ if(ret == 0)
+ {
+ *free_flags = SLAPI_VIRTUALATTRS_RETURNED_COPIES;
+ *actual_type_name = slapi_ch_strdup(type);
+ *type_name_disposition = SLAPI_VIRTUALATTRS_TYPE_NAME_MATCHED_EXACTLY_OR_ALIAS;
+ }
+ cos_cache_release(pCache);
+
+bail:
+
+ LDAPDebug( LDAP_DEBUG_TRACE, "<-- cos_cache_vattr_get\n",0,0,0);
+ return ret;
+}
+
+
+static int cos_cache_vattr_compare(vattr_sp_handle *handle, vattr_context *c, Slapi_Entry *e, char *type, Slapi_Value *test_this, int* result, int flags, void *hint)
+{
+ int ret = -1;
+ cos_cache *pCache = 0;
+
+ LDAPDebug( LDAP_DEBUG_TRACE, "--> cos_cache_vattr_compare\n",0,0,0);
+
+ if(cos_cache_getref(&pCache) < 1)
+ {
+ /* problems we are hosed */
+ LDAPDebug( LDAP_DEBUG_PLUGIN, "cos_cache_vattr_compare: failed to get class of service reference\n",0,0,0);
+ goto bail;
+ }
+
+ ret = cos_cache_query_attr(pCache, c, e, type, NULL, test_this, result, NULL);
+
+ cos_cache_release(pCache);
+
+bail:
+
+ LDAPDebug( LDAP_DEBUG_TRACE, "<-- cos_cache_vattr_compare\n",0,0,0);
+ return ret;
+}
+
+/*
+ * this imp is damn slow
+ *
+ */
+static int cos_cache_vattr_types(vattr_sp_handle *handle,Slapi_Entry *e,
+ vattr_type_list_context *type_context,int flags)
+{
+ int ret = 0;
+ int index = 0;
+ cosCache *pCache;
+ char *lastattr = "thisisfakeforcos";
+ int props = 0;
+
+ LDAPDebug( LDAP_DEBUG_TRACE, "--> cos_cache_vattr_types\n",0,0,0);
+
+ if(cos_cache_getref((cos_cache **)&pCache) < 1)
+ {
+ /* problems we are hosed */
+ LDAPDebug( LDAP_DEBUG_PLUGIN, "cos_cache_vattr_types: failed to get class of service reference\n",0,0,0);
+ goto bail;
+ }
+
+ while(index < pCache->attrCount )
+ {
+ if(slapi_utf8casecmp(
+ (unsigned char *)pCache->ppAttrIndex[index]->pAttrName,
+ (unsigned char *)lastattr))
+ {
+ lastattr = pCache->ppAttrIndex[index]->pAttrName;
+
+ if(1 == cos_cache_query_attr(pCache, NULL, e, lastattr, NULL, NULL,
+ NULL, &props))
+ {
+ /* entry contains this attr */
+ vattr_type_thang thang = {0};
+
+ thang.type_name = lastattr;
+ thang.type_flags = props;
+
+ slapi_vattrspi_add_type(type_context,&thang,0);
+ }
+
+ }
+
+ index++;
+ }
+
+ cos_cache_release(pCache);
+
+bail:
+
+ LDAPDebug( LDAP_DEBUG_TRACE, "<-- cos_cache_vattr_types\n",0,0,0);
+ return ret;
+}
+
+
+/*
+ cos_cache_query_attr
+ --------------------
+ queries the cache to determine if this entry
+ should have an attribute of "type", and if so,
+ returns the attribute values in "vals" - which
+ should be subsequently freed by a call to
+ cos_cache_query_attr_free()
+
+ returns
+ 0 on success, we added a computed attribute
+ 1 on outright failure
+ -1 when doesn't know about attribute
+
+ {PARPAR} must also check the attribute does not exist if we are not
+ overriding and allow the DS logic to pick it up by denying knowledge
+ of attribute
+*/
+static int cos_cache_query_attr(cos_cache *ptheCache, vattr_context *context, Slapi_Entry *e, char *type, Slapi_ValueSet **out_attr, Slapi_Value *test_this, int *result, int *props)
+{
+ int ret = -1;
+ cosCache *pCache = (cosCache*)ptheCache;
+ char *pDn = 0;
+ Slapi_Attr *pObjclasses = 0;
+ int attr_index = 0; /* for looping through attributes */
+ int attr_matched_index = 0; /* for identifying the matched attribute */
+ int hit = 0;
+ cosAttributes *pDefAttr = 0;
+ Slapi_ValueSet* results = 0;
+ Slapi_Value *val;
+/* int type_name_disposition;
+ char *actual_type_name;
+ int flags = 0;
+ int free_flags;*/
+ Slapi_Attr *pTmpVals;
+ int using_default = 0;
+ int entry_has_value = 0;
+ int merge_mode = 0;
+
+ LDAPDebug( LDAP_DEBUG_TRACE, "--> cos_cache_query_attr\n",0,0,0);
+
+
+ if(out_attr)
+ *out_attr = 0;
+
+ /*
+ to perform this operation we need to know:
+
+ * if we know about the attribute, if not just exit
+ --
+ * dn,to determine its relevancy to any cos definition,
+ it must be a child of cosTargetTree
+ --
+ * objectclasses,to determine if the cos attribute will
+ violate schema (only when schema checking is on)
+ --
+ * class of service specifier, for matching definitions
+ - cosSpecifier is the attribute name and is used to
+ determine the cosDefinition to use, its value determines
+ the template to use
+ --
+ * the cosAttribute(s) (from the cosDefinition(s)) that match
+ the attribute name.
+ ($$)If these have a postfix of "default", then it is the same
+ as no postfix i.e. this acts as the default value. If it has
+ a postfix of "override", then the value in the matching template
+ is used regardless of any value stored in the entry. This has
+ been worked out previously so we can use a bool indicator in
+ the cosDefinition structure to determine what to do.
+ --
+ * the value of the attribute in the entry -
+ if present it overrides any default template value (see $$)
+
+ Also this ordering ensures least work done to fail (in this
+ implementation)
+ */
+
+ /** attribute **/
+ /*
+ lets be sure we need to do something
+ most of the time we probably don't
+ */
+ attr_index = cos_cache_find_attr(pCache, type);
+ if(attr_index == -1)
+ {
+ /* we don't know about this attribute */
+ goto bail;
+ }
+
+ /*
+ if there is a value in the entry the outcome
+ of the cos attribute resolution may be different
+ */
+ slapi_entry_attr_find(e, type, &pTmpVals);
+ if(pTmpVals)
+ entry_has_value = 1;
+
+ /** dn **/
+ pDn = slapi_entry_get_dn(e);
+
+ if(pDn == 0)
+ {
+ LDAPDebug( LDAP_DEBUG_ANY, "cos_cache_query_attr: failed to get entry dn\n",0,0,0);
+ ret = 1;
+ goto bail;
+ }
+
+ slapi_dn_normalize( pDn );
+
+ /** objectclasses **/
+ if(pCache->ppAttrIndex[attr_index]->attr_operational == 0 && config_get_schemacheck() &&
+ pCache->ppAttrIndex[attr_index]->attr_operational_default == 0)
+ {
+ /* does this entry violate schema? */
+
+ if(slapi_entry_attr_find( e, "objectclass", &pObjclasses ))
+ {
+ LDAPDebug( LDAP_DEBUG_ANY, "cos_cache_query_attr: failed to get objectclass from %s\n",pDn,0,0);
+ goto bail;
+ }
+
+ if(!cos_cache_schema_check(pCache, attr_index, pObjclasses))
+ {
+ LDAPDebug( LDAP_DEBUG_PLUGIN, "cos_cache_query_attr: cos attribute %s failed schema check on dn: %s\n",type,pDn,0);
+ goto bail;
+ }
+ }
+
+ /** class of service specifier **/
+ /*
+ now we need to iterate through the attributes to discover
+ if one fits all the criteria, we'll take the first that does
+ and blow off the rest
+ */
+ do
+ {
+ /* for convenience, define some pointers */
+ cosAttributes *pAttr = pCache->ppAttrIndex[attr_index];
+ cosTemplates *pTemplate = (cosTemplates*)pAttr->pParent;
+ cosDefinitions *pDef = (cosDefinitions*)pTemplate->pParent;
+ cosAttrValue *pTargetTree = pDef->pCosTargetTree;
+
+ /* now for the tests */
+
+ /* would we be allowed to supply this attribute if we had one? */
+ if(entry_has_value && pAttr->attr_override == 0 && pAttr->attr_operational == 0)
+ {
+ /* answer: no, move on to the next attribute */
+ attr_index++;
+ continue;
+ }
+
+ /* if we are in merge_mode, can the attribute be merged? */
+ if(merge_mode && pAttr->attr_cos_merge == 0)
+ {
+ /* answer: no, move on to the next attribute */
+ attr_index++;
+ continue;
+ }
+
+ /* is this entry a child of the target tree(s)? */
+ do
+ {
+ if(pTargetTree)
+ slapi_dn_normalize( pTargetTree->val );
+
+ if( pTargetTree->val == 0 ||
+ slapi_dn_issuffix(pDn, pTargetTree->val) != 0 ||
+ (views_api && views_entry_exists(views_api, pTargetTree->val, e)) /* might be in a view */
+ )
+ {
+ cosAttrValue *pSpec = pDef->pCosSpecifier;
+ Slapi_ValueSet *pAttrSpecs = 0;
+
+
+ /* Does this entry have a correct cosSpecifier? */
+ do
+ {
+ Slapi_ValueSet *results = 0;
+ int type_name_disposition = 0;
+ char *actual_type_name = 0;
+ int free_flags = 0;
+
+ if(pSpec && pSpec->val) {
+ slapi_vattr_values_get_sp(context, e, pSpec->val, &pAttrSpecs, &type_name_disposition, &actual_type_name, 0, &free_flags);
+ /* MAB: We need to free actual_type_name here !!!
+ XXX BAD--should use slapi_vattr_values_free() */
+ slapi_ch_free((void **) &actual_type_name);
+ }
+
+ if(pAttrSpecs || pDef->cosType == COSTYPE_POINTER)
+ {
+ int index = 0;
+
+ /* does the cosSpecifier value correspond to this template? */
+ if(pDef->cosType == COSTYPE_INDIRECT)
+ {
+ /*
+ it always does correspond for indirect schemes (it's a dummy value)
+ now we must follow the dn of our pointer and retrieve a value to
+ return
+ Note: we support one dn only, the result of multiple pointers is undefined
+ */
+ Slapi_Value *indirectdn;
+ int pointer_flags = 0;
+
+ slapi_valueset_first_value( pAttrSpecs, &indirectdn );
+
+ if(props)
+ pointer_flags = *props;
+
+ if( indirectdn != NULL &&
+ !cos_cache_follow_pointer( context, (char*)slapi_value_get_string(indirectdn), type, out_attr, test_this, result, pointer_flags))
+ hit = 1;
+ }
+ else
+ {
+ if(pDef->cosType != COSTYPE_POINTER)
+ index = slapi_valueset_first_value( pAttrSpecs, &val );
+
+ while(pDef->cosType == COSTYPE_POINTER || val)
+ {
+ if(pDef->cosType == COSTYPE_POINTER || !slapi_utf8casecmp((unsigned char*)pTemplate->cosGrade, (unsigned char*)slapi_value_get_string(val)))
+ {
+ /* we have a hit */
+
+
+ if(out_attr)
+ {
+ if(cos_cache_cos_2_slapi_valueset(pAttr, out_attr) == 0)
+ hit = 1;
+ else
+ {
+ LDAPDebug( LDAP_DEBUG_ANY, "cos_cache_query_attr: could not create values to return\n",0,0,0);
+ goto bail;
+ }
+
+ if(pAttr->attr_cos_merge)
+ {
+ merge_mode = 1;
+ attr_matched_index = attr_index;
+ }
+ }
+ else
+ {
+ if(test_this && result)
+ {
+ /* compare op */
+ if(cos_cache_cmp_attr(pAttr, test_this, result))
+ {
+ hit = 1;
+ }
+ }
+ else
+ {
+ /* well, this must be a request for type only */
+ hit = 1;
+ }
+ }
+
+ break;
+ }
+
+ if(pDef->cosType != COSTYPE_POINTER)
+ index = slapi_valueset_next_value( pAttrSpecs, index, &val );
+ }
+ }
+ }
+
+ if(pSpec)
+ pSpec = pSpec->list.pNext;
+
+ } while(hit == 0 && pSpec);
+
+ /* MAB: We need to free pAttrSpecs here !!!
+ XXX BAD--should use slapi_vattr_values_free()*/
+ slapi_valueset_free(pAttrSpecs);
+
+ /* is the cosTemplate the default template? */
+ if(hit == 0 && pTemplate->template_default && !pDefAttr)
+ {
+ /* then lets save the attr in case we need it later */
+ pDefAttr = pAttr;
+ }
+ }
+
+ pTargetTree = pTargetTree->list.pNext;
+
+ } while(hit == 0 && pTargetTree);
+
+
+ if(hit==0 || merge_mode)
+ attr_index++;
+
+ } while(
+ (hit == 0 || merge_mode) &&
+ pCache->attrCount > attr_index &&
+ !slapi_utf8casecmp((unsigned char*)type, (unsigned char*)pCache->ppAttrIndex[attr_index]->pAttrName)
+ );
+
+ if(!merge_mode)
+ attr_matched_index = attr_index;
+
+ /* should we use a default value? */
+ if(hit == 0 && pDefAttr)
+ {
+ /* we have a hit */
+
+ using_default = 1;
+
+ if(out_attr)
+ {
+ if(cos_cache_cos_2_slapi_valueset(pDefAttr, out_attr) == 0)
+ hit = 1;
+ else
+ {
+ LDAPDebug( LDAP_DEBUG_ANY, "cos_cache_query_attr: could not create values to return\n",0,0,0);
+ goto bail;
+ }
+ }
+ else
+ {
+ if(test_this && result)
+ {
+ /* compare op */
+ if(cos_cache_cmp_attr(pDefAttr, test_this, result))
+ hit = 1;
+ }
+ else
+ {
+ /* well, this must be a request for type only and the entry gets default template value */
+ hit = 1;
+ }
+ }
+ }
+
+ if(hit == 1 && out_attr == NULL && test_this == NULL)
+ ret = 1;
+ else if(hit == 1)
+ ret = 0;
+
+ if(props)
+ *props = 0;
+
+ if(hit == 1 && props && pDefAttr) {
+ if (
+ ((using_default && pDefAttr->attr_operational == 1) ||
+ (!using_default && pCache->ppAttrIndex[attr_matched_index]->attr_operational == 1)) ||
+ ((using_default && pDefAttr->attr_operational_default == 1) ||
+ (!using_default && pCache->ppAttrIndex[attr_matched_index]->attr_operational_default == 1)) )
+ {
+ /* this is an operational attribute, lets mark it so */
+ *props |= SLAPI_ATTR_FLAG_OPATTR;
+ }
+ }
+
+bail:
+
+ LDAPDebug( LDAP_DEBUG_TRACE, "<-- cos_cache_query_attr\n",0,0,0);
+ return ret;
+}
+
+/*
+ cos_cache_query_attr_free
+ -------------------------
+ frees the memory allocated for the data returned
+ by cos_cache_query_attr
+*/
+static void cos_cache_query_attr_free(struct berval ***vals)
+{
+ int index = 0;
+
+ while((*vals)[index])
+ {
+ slapi_ch_free((void**)&((*vals)[index]));
+ index++;
+ }
+
+ slapi_ch_free((void**)*vals);
+}
+
+/*
+ cos_cache_find_attr
+ -------------------
+ searches for the attribute "type", and if found returns the index
+ of the first occurrance of the attribute in the cache top level
+ indexed attribute list.
+*/
+static int cos_cache_find_attr(cosCache *pCache, char *type)
+{
+ int ret = -1; /* assume failure */
+ cosAttributes attr;
+
+ LDAPDebug( LDAP_DEBUG_TRACE, "--> cos_cache_find_attr\n",0,0,0);
+
+ attr.pAttrName = type;
+
+ if(pCache->attrCount-1 != 0)
+ ret = cos_cache_attr_index_bsearch(pCache, &attr, 0, pCache->attrCount-1);
+ else
+ {
+ /* only one attribute (that will fool our bsearch) lets check it here */
+ if(!slapi_utf8casecmp((unsigned char*)type, (unsigned char*)(pCache->ppAttrIndex)[0]->pAttrName))
+ {
+ ret = 0;
+ }
+ }
+
+ LDAPDebug( LDAP_DEBUG_TRACE, "<-- cos_cache_find_attr\n",0,0,0);
+ return ret;
+}
+
+
+/*
+ cos_cache_schema_check
+ ----------------------
+ check those object classes which match in the input list and the
+ cached set for allowed attribute types
+
+ return non-null for schema matches, zero otherwise
+*/
+static int cos_cache_schema_check(cosCache *pCache, int attr_index, Slapi_Attr *pObjclasses)
+{
+ int ret = 0; /* assume failure */
+ Slapi_Value *val;
+ int hint;
+
+ LDAPDebug( LDAP_DEBUG_TRACE, "--> cos_cache_schema_check\n",0,0,0);
+
+ hint = slapi_attr_first_value( pObjclasses, &val );
+ while(hint != -1)
+ {
+ ret = cos_cache_attrval_exists(pCache->ppAttrIndex[attr_index]->pObjectclasses, (char*) slapi_value_get_string(val));
+ if(ret)
+ break;
+
+ hint = slapi_attr_next_value( pObjclasses, hint, &val );
+ }
+
+ LDAPDebug( LDAP_DEBUG_TRACE, "<-- cos_cache_schema_check\n",0,0,0);
+ return ret;
+}
+
+/*
+ cos_cache_schema_build
+ ----------------------
+ For each attribute in our global cache add the objectclasses which allow it.
+ This may be referred to later to check schema is not being violated.
+*/
+static int cos_cache_schema_build(cosCache *pCache)
+{
+ int ret = 0; /* we assume success, with operational attributes not supplied in schema we might fail otherwise */
+ struct objclass *oc;
+ char *pLastName = 0;
+ cosAttrValue *pLastRef = 0;
+ int attr_index = 0;
+
+ LDAPDebug( LDAP_DEBUG_TRACE, "--> cos_cache_schema_build\n",0,0,0);
+
+ if(!config_get_schemacheck())
+ ret = 0;
+
+ /*
+ it is expected that in all but the most hard core cases, the number of
+ objectclasses will out number the attributes we look after - so we make
+ the objectclasses the outside loop. However, even if they are not, we
+ perform binary searches on the attribute list anyway, so it should be
+ considerably faster to search than the linked list of objectclasses (even
+ with the string comparisons going on)
+ */
+ oc_lock_read();
+ for ( oc = g_get_global_oc_nolock(); oc != NULL; oc = oc->oc_next )
+ {
+ char **pppAttrs[2];
+ int index;
+ int attrType = 0;
+
+ pppAttrs[0] = oc->oc_required;
+ pppAttrs[1] = oc->oc_allowed;
+
+ /* we need to check both required and allowed attributes I think */
+ while(attrType < 2)
+ {
+ if(pppAttrs[attrType])
+ {
+ index = 0;
+
+ while(pppAttrs[attrType][index])
+ {
+ attr_index = cos_cache_find_attr(pCache, pppAttrs[attrType][index]);
+ if(attr_index != -1)
+ {
+ /*
+ this attribute is one of ours, add this
+ objectclass to the objectclass list
+ note the index refers to the first
+ occurrence of this attribute in the list,
+ later we will copy over references to this
+ list to all the other attribute duplicates.
+ */
+
+ cos_cache_add_attrval(&(pCache->ppAttrIndex[attr_index]->pObjectclasses), oc->oc_name);
+ ret = 0;
+ }
+ index++;
+ }
+ }
+
+ attrType++;
+ }
+ }
+ oc_unlock();
+
+ /*
+ OK, now we need to add references to the real
+ lists to the duplicate attribute entries.
+ (this allows the schema check to be a little
+ less complex and just a little quicker)
+ */
+ pLastName = pCache->ppAttrIndex[0]->pAttrName;
+ pLastRef = pCache->ppAttrIndex[0]->pObjectclasses;
+
+ for(attr_index=1; attr_index<pCache->attrCount; attr_index++)
+ {
+ if(!slapi_utf8casecmp((unsigned char*)pCache->ppAttrIndex[attr_index]->pAttrName, (unsigned char*)pLastName))
+ {
+ /* copy over reference */
+ pCache->ppAttrIndex[attr_index]->pObjectclasses = pLastRef;
+ }
+ else
+ {
+ /* remember what went before */
+ pLastName = pCache->ppAttrIndex[attr_index]->pAttrName;
+ pLastRef = pCache->ppAttrIndex[attr_index]->pObjectclasses;
+ }
+ }
+
+ LDAPDebug( LDAP_DEBUG_TRACE, "<-- cos_cache_schema_build\n",0,0,0);
+ return ret;
+}
+
+
+/*
+ cos_cache_index_all
+ -------------------
+ Indexes every attribute in the cache for fast binary lookup
+ on attributes from the top level of the cache.
+ Also fixes up all parent pointers so that a single attribute
+ lookup will allow access to all information regarding that attribute.
+ Attributes that appear more than once in the cache will also
+ be indexed more than once - this means that a pure binary
+ search is not possible, but it is possible to make use of a
+ duplicate entry aware binary search function - which are rare beasts,
+ so we'll need to provide cos_cache_attr_bsearch()
+
+ This is also a handy time to mark the attributes as overides if
+ necessary
+*/
+
+static int cos_cache_index_all(cosCache *pCache)
+{
+ int ret = -1;
+
+ LDAPDebug( LDAP_DEBUG_TRACE, "--> cos_cache_index_all\n",0,0,0);
+
+ /*
+ first fixup the index array so we can use qsort()
+ also fixup the parent pointers
+ */
+
+ pCache->ppTemplateList = 0;
+ pCache->templateCount = 0;
+ pCache->ppAttrIndex = 0;
+
+ pCache->attrCount = cos_cache_total_attr_count(pCache);
+ if(pCache->attrCount && pCache->templateCount)
+ {
+ int tmpindex = 0;
+ int cmpindex = 0;
+ int actualCount = 0;
+
+ pCache->ppAttrIndex = (cosAttributes**)slapi_ch_malloc(sizeof(cosAttributes*) * pCache->attrCount);
+ pCache->ppTemplateList = (char**)slapi_ch_calloc((pCache->templateCount + 1) * 2, sizeof(char*));
+ if(pCache->ppAttrIndex && pCache->ppTemplateList)
+ {
+ int attrcount = 0;
+ cosDefinitions *pDef = pCache->pDefs;
+ cosAttrValue *pAttrVal = 0;
+
+ while(pDef)
+ {
+ cosTemplates *pCosTmps = pDef->pCosTmps;
+
+ while(pCosTmps)
+ {
+ cosAttributes *pAttrs = pCosTmps->pAttrs;
+
+ pCosTmps->pParent = pDef;
+
+ while(pAttrs)
+ {
+ pAttrs->pParent = pCosTmps;
+ (pCache->ppAttrIndex)[attrcount] = pAttrs;
+
+ if(cos_cache_attrval_exists(pDef->pCosOverrides, pAttrs->pAttrName))
+ pAttrs->attr_override = 1;
+ else
+ pAttrs->attr_override = 0;
+
+ if(cos_cache_attrval_exists(pDef->pCosOperational, pAttrs->pAttrName))
+ pAttrs->attr_operational = 1;
+ else
+ pAttrs->attr_operational = 0;
+
+ if(cos_cache_attrval_exists(pDef->pCosMerge, pAttrs->pAttrName))
+ pAttrs->attr_cos_merge = 1;
+ else
+ pAttrs->attr_cos_merge = 0;
+
+ if(cos_cache_attrval_exists(pDef->pCosOpDefault, pAttrs->pAttrName))
+ pAttrs->attr_operational_default = 1;
+ else
+ pAttrs->attr_operational_default = 0;
+
+ attrcount++;
+
+ pAttrs = pAttrs->list.pNext;
+ }
+
+ pCosTmps = pCosTmps->list.pNext;
+ }
+
+ /*
+ we need to build the template dn list too,
+ we are going to take care that we do not
+ add duplicate dns or dns that have
+ ancestors elsewhere in the list since this
+ list will be binary searched (with a special
+ BS alg) to find an ancestor tree for a target
+ that has been modified - that comes later in
+ this function however - right now we'll just
+ slap them in the list
+ */
+ pAttrVal = pDef->pCosTemplateDn;
+
+ while(pAttrVal)
+ {
+ slapi_dn_normalize(pAttrVal->val);
+ pCache->ppTemplateList[tmpindex] = pAttrVal->val;
+
+ tmpindex++;
+ pAttrVal = pAttrVal->list.pNext;
+ }
+
+ pDef = pDef->list.pNext;
+ }
+
+ /* now sort the index array */
+ qsort(pCache->ppAttrIndex, attrcount, sizeof(cosAttributes*), cos_cache_attr_compare);
+ qsort(pCache->ppTemplateList, tmpindex, sizeof(char*), cos_cache_string_compare);
+
+ /*
+ now we have the sorted template dn list, we can get rid of
+ duplicates and entries that have an ancestor elsewhere in
+ the list - all this in the name of faster searches
+ */
+
+ /* first go through zapping the useless PARPAR - THIS DOES NOT WORK */
+ tmpindex = 1;
+ cmpindex = 0;
+ actualCount = pCache->templateCount;
+
+ while(tmpindex < pCache->templateCount)
+ {
+ if(
+ !slapi_utf8casecmp((unsigned char*)pCache->ppTemplateList[tmpindex],(unsigned char*)pCache->ppTemplateList[cmpindex]) ||
+ slapi_dn_issuffix(pCache->ppTemplateList[tmpindex], pCache->ppTemplateList[cmpindex])
+ )
+ {
+ /* this guy is a waste of space */
+ pCache->ppTemplateList[tmpindex] = 0;
+ actualCount--;
+ }
+ else
+ cmpindex = tmpindex;
+
+ tmpindex++;
+ }
+
+ /* now shuffle everything up to the front to cover the bald spots */
+ tmpindex = 1;
+ cmpindex = 0;
+
+ while(tmpindex < pCache->templateCount)
+ {
+ if(pCache->ppTemplateList[tmpindex] != 0)
+ {
+ if(cmpindex)
+ {
+ pCache->ppTemplateList[cmpindex] = pCache->ppTemplateList[tmpindex];
+ pCache->ppTemplateList[tmpindex] = 0;
+ cmpindex++;
+ }
+ }
+ else
+ {
+ if(cmpindex == 0)
+ cmpindex = tmpindex;
+ }
+
+ tmpindex++;
+ }
+
+ pCache->templateCount = actualCount;
+
+ LDAPDebug( LDAP_DEBUG_PLUGIN, "cos: cos cache index built\n",0,0,0);
+
+ ret = 0;
+ }
+ else
+ {
+ if(pCache->ppAttrIndex)
+ slapi_ch_free((void**)(&pCache->ppAttrIndex));
+
+ if(pCache->ppTemplateList)
+ slapi_ch_free((void**)(&pCache->ppTemplateList));
+
+ LDAPDebug( LDAP_DEBUG_ANY, "cos_cache_index_all: failed to allocate index memory\n",0,0,0);
+ }
+ }
+ else
+ LDAPDebug( LDAP_DEBUG_ANY, "cos_cache_index_all: no attributes to index\n",0,0,0);
+
+ LDAPDebug( LDAP_DEBUG_TRACE, "<-- cos_cache_index_all\n",0,0,0);
+ return ret;
+}
+
+
+/*
+ cos_cache_total_attr_count
+ --------------------------
+ walks the entire cache counting all attributes
+ note: this is coded so that it may be called
+ prior to the cache indexing of attributes - in
+ fact it is called by the code that creates the
+ index. Once indexing has been performed, it is
+ *much* *much* faster to get the count from the
+ cache object itself - cosCache::attrCount.
+
+ Additionally - a side effect is that the template
+ target trees are counted and placed in the cache level
+ target tree count - probably should be renamed,
+ but lets let it slide for now
+
+ returns the number of attributes counted
+*/
+static int cos_cache_total_attr_count(cosCache *pCache)
+{
+ int count = 0;
+ cosDefinitions *pDef = pCache->pDefs;
+
+ LDAPDebug( LDAP_DEBUG_TRACE, "--> cos_cache_total_attr_count\n",0,0,0);
+
+ pCache->templateCount = 0;
+
+ while(pDef)
+ {
+ cosTemplates *pCosTmps = pDef->pCosTmps;
+
+ while(pCosTmps)
+ {
+ cosAttributes *pAttrs = pCosTmps->pAttrs;
+
+ while(pAttrs)
+ {
+ count++;
+ pAttrs = pAttrs->list.pNext;
+ }
+
+ pCache->templateCount++;
+ pCosTmps = pCosTmps->list.pNext;
+ }
+
+ pDef = pDef->list.pNext;
+ }
+
+ LDAPDebug( LDAP_DEBUG_TRACE, "<-- cos_cache_total_attr_count\n",0,0,0);
+ return count;
+}
+
+
+/*
+ cos_cache_XXX_compare
+ ---------------------
+ this set of functions are passed to sorting and searching
+ functions to provide an ordering comparison between to structures
+*/
+#if 0
+int cos_cache_attrval_compare(const void *e1, const void *e2)
+{
+ return slapi_utf8casecmp((unsigned char*)(*(cosAttrValue**)e1)->val,(unsigned char*)(*(cosAttrValue**)e2)->val);
+}
+#endif
+
+static int cos_cache_attr_compare(const void *e1, const void *e2)
+{
+ int com_Result;
+ cosAttributes *pAttr = (*(cosAttributes**)e1);
+ cosTemplates *pTemplate = (cosTemplates*)pAttr->pParent;
+ cosDefinitions *pDef = (cosDefinitions*)pTemplate->pParent;
+ cosAttrValue *pcostt = pDef->pCosTargetTree;
+ cosAttributes *pAttr1 = (*(cosAttributes**)e2);
+ cosTemplates *pTemplate1 = (cosTemplates*)pAttr1->pParent;
+ cosDefinitions *pDef1 = (cosDefinitions*)pTemplate1->pParent;
+ cosAttrValue *pcostt1 = pDef1->pCosTargetTree;
+
+ /* Now compare the names of the attributes */
+ com_Result = slapi_utf8casecmp((unsigned char*)(*(cosAttributes**)e1)->pAttrName,(unsigned char*)(*(cosAttributes**)e2)->pAttrName);
+ if(0 == com_Result)
+ /* Now compare the definition Dn parents */
+ com_Result = slapi_utf8casecmp((unsigned char*)pcostt1->val,(unsigned char*)pcostt->val);
+ if(0 == com_Result)
+ /* Now compare the cosPririoties */
+ com_Result = pTemplate->cosPriority - pTemplate1->cosPriority;
+ /* Now compare the prirority */
+ if(0 == com_Result)
+ return -1;
+ return com_Result;
+}
+
+#if 0
+int cos_cache_tmpl_compare(const void *e1, const void *e2)
+{
+ return slapi_utf8casecmp((unsigned char*)(*(cosTemplates**)e1)->cosGrade,(unsigned char*)(*(cosTemplates**)e2)->cosGrade);
+}
+#endif
+
+static int cos_cache_string_compare(const void *e1, const void *e2)
+{
+ return slapi_utf8casecmp((*(unsigned char**)e1),(*(unsigned char**)e2));
+}
+
+static int cos_cache_template_index_compare(const void *e1, const void *e2)
+{
+ int ret = 0;
+
+ if(0 == slapi_dn_issuffix((const char*)e1,*(const char**)e2))
+ ret = slapi_utf8casecmp(*(unsigned char**)e2,(unsigned char*)e1);
+ else
+ ret = 0;
+
+ return ret;
+}
+
+/*
+ cos_cache_template_index_bsearch
+ --------------------------------
+ searches the template dn index for a match
+*/
+static int cos_cache_template_index_bsearch(const char *dn)
+{
+ int ret = 0;
+ cosCache *pCache;
+
+ LDAPDebug( LDAP_DEBUG_TRACE, "--> cos_cache_template_index_bsearch\n",0,0,0);
+
+ if(-1 != cos_cache_getref((cos_cache**)&pCache))
+ {
+ if(bsearch(dn, pCache->ppTemplateList, pCache->templateCount, sizeof(char*), cos_cache_template_index_compare))
+ ret = 1;
+
+ cos_cache_release((cos_cache*)pCache);
+ }
+
+ LDAPDebug( LDAP_DEBUG_TRACE, "<-- cos_cache_template_index_bsearch\n",0,0,0);
+
+ return ret;
+}
+
+/*
+ cos_cache_attr_index_bsearch - RECURSIVE
+ ----------------------------------------
+ performs a binary search on the cache attribute index
+ return -1 if key is not found
+ the index into attribute index array of the first occurrance
+ of that attribute type otherwise
+*/
+static int cos_cache_attr_index_bsearch( const cosCache *pCache, const cosAttributes *key, int lower, int upper )
+{
+ int ret = -1;
+ int index = 0;
+
+ LDAPDebug( LDAP_DEBUG_TRACE, "--> cos_cache_attr_index_bsearch\n",0,0,0);
+
+ if(upper >= lower)
+ {
+ if(upper != 0)
+ index = ((upper-lower)/2) + lower;
+ else
+ index = 0;
+
+ ret = slapi_utf8casecmp((unsigned char*)key->pAttrName, (unsigned char*)(pCache->ppAttrIndex)[index]->pAttrName);
+ if(ret == 0)
+ {
+ /*
+ we have a match, backtrack to the
+ first occurrance of this attribute
+ type
+ */
+ do
+ {
+ index--;
+ if(index >= 0)
+ ret = slapi_utf8casecmp((unsigned char*)key->pAttrName, (unsigned char*)(pCache->ppAttrIndex)[index]->pAttrName);
+ } while(index >= 0 && ret == 0);
+
+ index++;
+ }
+ else
+ {
+ /* seek elsewhere */
+ if(ret < 0)
+ {
+ /* take the low road */
+ index = cos_cache_attr_index_bsearch(pCache, key, lower, index-1);
+ }
+ else
+ {
+ /* go high */
+ index = cos_cache_attr_index_bsearch(pCache, key, index+1, upper);
+ }
+ }
+ }
+ else
+ index = -1;
+
+ LDAPDebug( LDAP_DEBUG_TRACE, "<-- cos_cache_attr_index_bsearch\n",0,0,0);
+ return index;
+}
+
+
+static int cos_cache_cmp_attr(cosAttributes *pAttr, Slapi_Value *test_this, int *result)
+{
+ int ret = 0;
+ int index = 0;
+ cosAttrValue *pAttrVal = pAttr->pAttrValue;
+ char *the_cmp = (char *)slapi_value_get_string(test_this);
+
+ LDAPDebug( LDAP_DEBUG_TRACE, "--> cos_cache_cmp_attr\n",0,0,0);
+
+ *result = 0;
+
+ while( pAttrVal )
+ {
+ if(!slapi_utf8casecmp((unsigned char*)the_cmp, (unsigned char*)pAttrVal->val))
+ {
+ /* compare match */
+ *result = 1;
+ break;
+ }
+
+ pAttrVal = pAttrVal->list.pNext;
+ index++;
+ }
+
+ LDAPDebug( LDAP_DEBUG_TRACE, "<-- cos_cache_cmp_attr\n",0,0,0);
+ return ret;
+}
+
+
+/*
+ cos_cache_cos_2_slapi_attr
+ ----------------------
+ converts a cosAttributes structure to a Slapi_Attribute
+*/
+static int cos_cache_cos_2_slapi_valueset(cosAttributes *pAttr, Slapi_ValueSet **out_vs)
+{
+ int ret = 0;
+ int index = 0;
+ cosAttrValue *pAttrVal = pAttr->pAttrValue;
+ int add_mode = 0;
+ static Slapi_Attr *attr = 0; /* allocated once, never freed */
+ static done_once = 0;
+
+ LDAPDebug( LDAP_DEBUG_TRACE, "--> cos_cache_cos_2_slapi_attr\n",0,0,0);
+
+ /* test out_vs for existing values */
+ if(*out_vs)
+ {
+ add_mode = 1;
+ if(!done_once)
+ {
+ attr = slapi_attr_new(); /* lord knows why this is needed by slapi_valueset_find*/
+ slapi_attr_init(attr, "cos-bogus");
+ done_once = 1;
+ }
+ }
+ else
+ *out_vs = slapi_valueset_new();
+
+ if(*out_vs)
+ {
+ if(!add_mode)
+ slapi_valueset_init(*out_vs);
+
+ while( pAttrVal )
+ {
+ Slapi_Value *val = slapi_value_new_string(pAttrVal->val);
+ if(val) {
+ if(!add_mode || !slapi_valueset_find(attr, *out_vs, val)) {
+ slapi_valueset_add_value_ext(*out_vs, val, SLAPI_VALUE_FLAG_PASSIN);
+ }
+ else {
+ slapi_value_free(&val);
+ }
+ }
+ else
+ {
+ LDAPDebug( LDAP_DEBUG_ANY, "cos_cache_cos_2_slapi_attr: memory allocation error\n",0,0,0);
+ ret = -1;
+ goto bail;
+ }
+
+ pAttrVal = pAttrVal->list.pNext;
+ index++;
+ }
+ }
+ else
+ {
+ LDAPDebug( LDAP_DEBUG_ANY, "cos_cache_cos_2_slapi_attr: memory allocation error\n",0,0,0);
+ ret = -1;
+ }
+
+bail:
+
+ LDAPDebug( LDAP_DEBUG_TRACE, "<-- cos_cache_cos_2_slapi_attr\n",0,0,0);
+ return ret;
+}
+
+
+/*
+ cos_cache_change_notify
+ -----------------------
+ determines if the change effects the cache and if so
+ signals a rebuild.
+
+ XXXrbyrne This whole mechanism needs to be revisited--it means that
+ the modifying client gets his LDAP response, and an unspecified and
+ variable
+ period of time later, his mods get taken into account in the cos cache.
+ This makes it hard to program reliable admin tools for COS--DSAME
+ has already indicated this is an issue for them.
+ Additionally, it regenerates the _whole_ cache even for eeny weeny mods--
+ does it really neeed to ? Additionally, in order to ensure we
+ do not miss any mods, we may tend to regen the cache, even if we've already
+ taken a mod into account in an earlier regeneration--currently there is no
+ way to know we've already dealt with the mod.
+ The right thing is something like: figure out what's being changed
+ and change only that in the cos cache and do it _before_ the response
+ goes to the client....or do a task that he can poll.
+*/
+void cos_cache_change_notify(Slapi_PBlock *pb)
+{
+ char *dn;
+ Slapi_Attr *pObjclasses = 0;
+ int index = 0;
+ int do_update = 0;
+ struct slapi_entry *e;
+ Slapi_Backend *be=NULL;
+ int rc = 0;
+ int optype = -1;
+
+ LDAPDebug( LDAP_DEBUG_TRACE, "--> cos_cache_change_notify\n",0,0,0);
+
+ /* Don't update local cache when remote entries */
+ /* are updated. */
+ slapi_pblock_get( pb, SLAPI_BACKEND, &be );
+ if ( ( be!=NULL ) && (slapi_be_is_flag_set(be,SLAPI_BE_FLAG_REMOTE_DATA)))
+ goto bail;
+
+ /* need to work out if a cache rebuild is necessary */
+ if(slapi_pblock_get( pb, SLAPI_TARGET_DN, &dn ))
+ {
+ LDAPDebug( LDAP_DEBUG_ANY, "cos_cache_change_notify: failed to get dn of changed entry",0,0,0);
+ goto bail;
+ }
+
+ slapi_pblock_get(pb, SLAPI_PLUGIN_OPRETURN, &rc);
+ if (0 != rc) {
+ /* The operation did not succeed. As far as the cos cache is concerned, no need to update anything */
+ goto bail;
+ }
+
+ /*
+ * For DELETE, MODIFY, MODRDN: see if the pre-op entry was cos significant.
+ * For ADD, MODIFY, MODRDN: see if the post-op was cos significant.
+ * Touching a cos significant entry triggers the update
+ * of the whole cache.
+ */
+ slapi_pblock_get ( pb, SLAPI_OPERATION_TYPE, &optype );
+ if ( optype == SLAPI_OPERATION_DELETE ||
+ optype == SLAPI_OPERATION_MODIFY ||
+ optype == SLAPI_OPERATION_MODRDN ) {
+
+ slapi_pblock_get(pb, SLAPI_ENTRY_PRE_OP, &e);
+ if ( cos_cache_entry_is_cos_related(e)) {
+ do_update = 1;
+ }
+ }
+ if ( !do_update &&
+ (optype == SLAPI_OPERATION_ADD ||
+ optype == SLAPI_OPERATION_MODIFY ||
+ optype == SLAPI_OPERATION_MODRDN )) {
+
+ /* Adds have null pre-op entries */
+ slapi_pblock_get(pb, SLAPI_ENTRY_POST_OP, &e);
+ if ( cos_cache_entry_is_cos_related(e)) {
+ do_update = 1;
+ }
+ }
+
+ /*
+ * Check if this was an entry in a template tree (dn contains
+ * the old dn value).
+ * It's only relevant for indirect templates, which will
+ * not usually contain the "objectclass: costemplate" pair
+ * and so will not get detected by the above code.
+ * In fact, everything would still work fine if
+ * we just ignored a mod of one of these indirect templates,
+ * as we do not cache values from them, but the advantage of
+ * triggering an update here is that
+ * we can maintain the invariant that we only ever cache
+ * definitions that have _valid_ templates--the active cache
+ * stays lean in the face of errors.
+ */
+ if( !do_update && cos_cache_template_index_bsearch(dn)) {
+ LDAPDebug( LDAP_DEBUG_PLUGIN, "cos_cache_change_notify:"
+ "updating due to indirect template change(%s)\n",
+ dn,0,0);
+ do_update = 1;
+ }
+
+ /* Do the update if required */
+ if(do_update)
+ {
+ slapi_lock_mutex(change_lock);
+ slapi_notify_condvar( something_changed, 1 );
+ cos_cache_notify_flag = 1;
+ slapi_unlock_mutex(change_lock);
+ }
+
+bail:
+ LDAPDebug( LDAP_DEBUG_TRACE, "<-- cos_cache_change_notify\n",0,0,0);
+}
+
+/*
+ cos_cache_stop
+ --------------
+ notifies the cache thread we are stopping
+*/
+void cos_cache_stop()
+{
+ LDAPDebug( LDAP_DEBUG_TRACE, "--> cos_cache_stop\n",0,0,0);
+
+ /* first deregister our state change func */
+ slapi_unregister_backend_state_change((void *)cos_cache_backend_state_change);
+
+ slapi_lock_mutex(change_lock);
+ keeprunning = 0;
+ slapi_notify_condvar( something_changed, 1 );
+ slapi_unlock_mutex(change_lock);
+
+ /* wait on shutdown */
+ slapi_lock_mutex(stop_lock);
+
+ /* release the caches reference to the cache */
+ cos_cache_release(pCache);
+
+ slapi_destroy_mutex(cache_lock);
+ slapi_destroy_mutex(change_lock);
+ slapi_destroy_condvar(something_changed);
+
+ slapi_unlock_mutex(stop_lock);
+ slapi_destroy_mutex(stop_lock);
+ slapi_destroy_condvar(start_cond);
+ slapi_destroy_mutex(start_lock);
+
+ LDAPDebug( LDAP_DEBUG_TRACE, "<-- cos_cache_stop\n",0,0,0);
+}
+
+/*
+ cos_cache_backwards_stricmp_and_clip
+ ------------------------------------
+ compares s2 to s1 starting from end of the strings until the beginning of
+ either matches result in the s2 value being clipped from s1 with a NULL char
+ and 1 being returned as opposed to 0
+
+*/
+static int cos_cache_backwards_stricmp_and_clip(char*s1,char*s2)
+{
+ int ret = 0;
+ int s1len = 0;
+ int s2len = 0;
+
+ LDAPDebug( LDAP_DEBUG_TRACE, "--> cos_cache_backwards_stricmp_and_clip\n",0,0,0);
+
+ s1len = strlen(s1);
+ s2len = strlen(s2);
+
+ if(s1len > s2len && s2len > 0)
+ {
+ while(s1len > -1 && s2len > -1)
+ {
+ s1len--;
+ s2len--;
+
+ if(s1[s1len] != s2[s2len])
+ break;
+ else
+ {
+ if(s2len == 0)
+ {
+ /* hit! now clip */
+ ret = 1;
+ s1[s1len] = '\0';
+ }
+ }
+ }
+ }
+
+ LDAPDebug( LDAP_DEBUG_TRACE, "<-- cos_cache_backwards_stricmp_and_clip\n",0,0,0);
+ return ret;
+}
+
+
+static int cos_cache_follow_pointer( vattr_context *c, const char *dn, char *type, Slapi_ValueSet **out_vs, Slapi_Value *test_this, int *result, int flags)
+{
+ int ret = -1; /* assume failure */
+ Slapi_PBlock *pDnSearch = 0;
+ Slapi_Entry **pEntryList = 0;
+ char *attrs[2];
+ int entryIndex = 0;
+ int op = 0;
+ int type_test = 0;
+ int type_name_disposition = 0;
+ char *actual_type_name = 0;
+ int free_flags = 0;
+ Slapi_ValueSet *tmp_vs = 0;
+
+ attrs[0] = type;
+ attrs[1] = 0;
+
+ /* Use new internal operation API */
+ pDnSearch = slapi_pblock_new();
+ if (pDnSearch) {
+ slapi_search_internal_set_pb(pDnSearch, dn, LDAP_SCOPE_BASE,"(|(objectclass=*)(objectclass=ldapsubentry))",attrs,
+ 0,NULL,NULL,cos_get_plugin_identity(),0);
+ slapi_search_internal_pb(pDnSearch);
+ slapi_pblock_get( pDnSearch, SLAPI_PLUGIN_INTOP_RESULT, &ret);
+ }
+
+ if(pDnSearch && (ret == LDAP_SUCCESS))
+ {
+ ret = -1;
+
+ slapi_pblock_get( pDnSearch, SLAPI_PLUGIN_INTOP_SEARCH_ENTRIES, &pEntryList);
+ if(pEntryList)
+ {
+ if(out_vs) /* if this set, a value is required */
+ op = 1;
+ else if(test_this && result) /* compare op */
+ op = 2;
+ else
+ {
+ /* requires only type present */
+ op = 1;
+ type_test = 1;
+ }
+
+ switch(op)
+ {
+ case 1:
+ /* straight value return or type test */
+ if(type_test)
+ out_vs = &tmp_vs;
+
+ ret = slapi_vattr_values_get_sp(c, pEntryList[0], type, out_vs,&type_name_disposition, &actual_type_name, flags, &free_flags);
+
+ if(actual_type_name)
+ slapi_ch_free((void **) &actual_type_name);
+
+ if(type_test && free_flags == SLAPI_VIRTUALATTRS_RETURNED_COPIES)
+ slapi_valueset_free(*out_vs);
+
+ break;
+
+ case 2:
+ /* this must be a compare op */
+ ret = slapi_vattr_value_compare_sp(c, pEntryList[0],type, test_this, result, flags);
+ break;
+
+ default:
+ goto bail;
+ }
+ }
+ }
+
+bail:
+ /* clean up */
+ if(pDnSearch)
+ {
+ slapi_free_search_results_internal(pDnSearch);
+ slapi_pblock_destroy(pDnSearch);
+ }
+
+ return ret;
+}
+
+
+/*
+ * cos_cache_backend_state_change()
+ * --------------------------------
+ * This is called when a backend changes state
+ * We simply signal to rebuild the cos cache in this case
+ *
+ */
+void cos_cache_backend_state_change(void *handle, char *be_name,
+ int old_be_state, int new_be_state)
+{
+ slapi_lock_mutex(change_lock);
+ slapi_notify_condvar( something_changed, 1 );
+ slapi_unlock_mutex(change_lock);
+}
+
+/*
+ * returns non-zero: entry is cos significant (note does not detect indirect
+ * template entries).
+ * 0 : entry is not cos significant.
+*/
+static int cos_cache_entry_is_cos_related( Slapi_Entry *e) {
+
+ int rc = 0;
+ Slapi_Attr *pObjclasses = NULL;
+
+ if ( e == NULL ) {
+ LDAPDebug( LDAP_DEBUG_ANY, "cos_cache_change_notify:"
+ "modified entry is NULL--updating cache just in case!",
+ 0,0,0);
+ rc = 1;
+ } else {
+
+ if(slapi_entry_attr_find( e, "objectclass", &pObjclasses ))
+ {
+ LDAPDebug( LDAP_DEBUG_ANY, "cos_cache_change_notify:"
+ " failed to get objectclass from %s",
+ slapi_entry_get_dn(e),0,0);
+ rc = 0;
+ } else {
+
+ Slapi_Value *val = NULL;
+ int index = 0;
+ char *pObj;
+
+ /* check out the object classes to see if this was a cosDefinition */
+
+ index = slapi_attr_first_value( pObjclasses, &val );
+ while(!rc && val)
+ {
+ pObj = (char*)slapi_value_get_string(val);
+
+ /*
+ * objectclasses are ascii--maybe strcasecmp() is faster than
+ * slapi_utf8casecmp()
+ */
+ if( !strcasecmp(pObj, "cosdefinition") ||
+ !strcasecmp(pObj, "cossuperdefinition") ||
+ !strcasecmp(pObj, "costemplate")
+ )
+ {
+ rc = 1;
+ }
+
+ index = slapi_attr_next_value( pObjclasses, index, &val );
+ }
+ }
+ }
+ return(rc);
+}
diff --git a/ldap/servers/plugins/cos/cos_cache.h b/ldap/servers/plugins/cos/cos_cache.h
new file mode 100644
index 00000000..ef47a9ab
--- /dev/null
+++ b/ldap/servers/plugins/cos/cos_cache.h
@@ -0,0 +1,19 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+
+#if !defined( _COS_CACHE_H )
+#define _COS_CACHE_H
+
+typedef void cos_cache;
+
+int cos_cache_init();
+void cos_cache_stop();
+int cos_cache_getref(cos_cache **ppCache);
+int cos_cache_addref(cos_cache *pCache);
+int cos_cache_release(cos_cache *pCache);
+void cos_cache_change_notify(Slapi_PBlock *pb);
+
+#endif /* _COS_CACHE_H */
diff --git a/ldap/servers/plugins/cos/dllmain.c b/ldap/servers/plugins/cos/dllmain.c
new file mode 100644
index 00000000..fabf8677
--- /dev/null
+++ b/ldap/servers/plugins/cos/dllmain.c
@@ -0,0 +1,96 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+/*
+ * Microsoft Windows specifics for BACK-LDBM DLL
+ */
+#include "ldap.h"
+#include "lber.h"
+
+
+#ifdef _WIN32
+/* Lifted from Q125688
+ * How to Port a 16-bit DLL to a Win32 DLL
+ * on the MSVC 4.0 CD
+ */
+BOOL WINAPI DllMain (HANDLE hModule, DWORD fdwReason, LPVOID lpReserved)
+{
+ WSADATA wsadata;
+
+ switch (fdwReason)
+ {
+ case DLL_PROCESS_ATTACH:
+ /* Code from LibMain inserted here. Return TRUE to keep the
+ DLL loaded or return FALSE to fail loading the DLL.
+
+ You may have to modify the code in your original LibMain to
+ account for the fact that it may be called more than once.
+ You will get one DLL_PROCESS_ATTACH for each process that
+ loads the DLL. This is different from LibMain which gets
+ called only once when the DLL is loaded. The only time this
+ is critical is when you are using shared data sections.
+ If you are using shared data sections for statically
+ allocated data, you will need to be careful to initialize it
+ only once. Check your code carefully.
+
+ Certain one-time initializations may now need to be done for
+ each process that attaches. You may also not need code from
+ your original LibMain because the operating system may now
+ be doing it for you.
+ */
+ /*
+ * 16 bit code calls UnlockData()
+ * which is mapped to UnlockSegment in windows.h
+ * in 32 bit world UnlockData is not defined anywhere
+ * UnlockSegment is mapped to GlobalUnfix in winbase.h
+ * and the docs for both UnlockSegment and GlobalUnfix say
+ * ".. function is oboslete. Segments have no meaning
+ * in the 32-bit environment". So we do nothing here.
+ */
+
+ if( errno = WSAStartup(0x0101, &wsadata ) != 0 )
+ return FALSE;
+
+ break;
+
+ case DLL_THREAD_ATTACH:
+ /* Called each time a thread is created in a process that has
+ already loaded (attached to) this DLL. Does not get called
+ for each thread that exists in the process before it loaded
+ the DLL.
+
+ Do thread-specific initialization here.
+ */
+ break;
+
+ case DLL_THREAD_DETACH:
+ /* Same as above, but called when a thread in the process
+ exits.
+
+ Do thread-specific cleanup here.
+ */
+ break;
+
+ case DLL_PROCESS_DETACH:
+ /* Code from _WEP inserted here. This code may (like the
+ LibMain) not be necessary. Check to make certain that the
+ operating system is not doing it for you.
+ */
+ WSACleanup();
+
+ break;
+ }
+ /* The return value is only used for DLL_PROCESS_ATTACH; all other
+ conditions are ignored. */
+ return TRUE; // successful DLL_PROCESS_ATTACH
+}
+#else
+int CALLBACK
+LibMain( HINSTANCE hinst, WORD wDataSeg, WORD cbHeapSize, LPSTR lpszCmdLine )
+{
+ /*UnlockData( 0 );*/
+ return( 1 );
+}
+#endif
diff --git a/ldap/servers/plugins/distrib/Makefile b/ldap/servers/plugins/distrib/Makefile
new file mode 100644
index 00000000..3144e8cb
--- /dev/null
+++ b/ldap/servers/plugins/distrib/Makefile
@@ -0,0 +1,109 @@
+#
+# PROPRIETARY/CONFIDENTIAL. Use of this product is subject to
+# license terms. Copyright 2001 Sun Microsystems, Inc.
+# Some preexisting portions Copyright 2001 Netscape Communications Corp.
+# All rights reserved.
+#
+#
+# GNU Makefile for Directory Server distribution plugin
+#
+
+LDAP_SRC = ../../..
+MCOM_ROOT = ../../../../..
+
+NOSTDCLEAN=true # don't let nsconfig.mk define target clean
+NOSTDSTRIP=true # don't let nsconfig.mk define target strip
+NSPR20=true # probably should be defined somewhere else (not sure where)
+
+OBJDEST = $(OBJDIR)/lib/libdistrib
+LIBDIR = $(LIB_RELDIR)
+
+include $(MCOM_ROOT)/ldapserver/nsconfig.mk
+include $(LDAP_SRC)/nsldap.mk
+
+ifeq ($(ARCH), WINNT)
+DEF_FILE:=./libdistrib.def
+endif
+
+CFLAGS+=$(SLCFLAGS)
+
+INCLUDES += -I$(LDAP_SRC)/servers/slapd
+
+DIS_OBJS= \
+ distrib.o
+
+
+OBJS = $(addprefix $(OBJDEST)/, $(DIS_OBJS))
+
+ifeq ($(ARCH), WINNT)
+LIBDIS_DLL_OBJ = $(addprefix $(OBJDEST)/, dllmain.o)
+endif
+
+# The sample distribution plugin is not part of DS.
+# So we generate the shared library outside of $(LIBDIR)
+# so that it's not retreived by the packaging makefiles.
+#LIBDIS = $(addprefix $(LIBDIR)/, $(DIS_DLL).$(DLL_SUFFIX))
+LIBDIS = $(addprefix $(OBJDEST)/, $(DIS_DLL).$(DLL_SUFFIX))
+
+ifeq ($(ARCH), WINNT)
+EXTRA_LIBS_DEP += \
+ $(LIBSLAPD_DEP) \
+ $(LDAP_LIBUTIL_DEP) \
+ $(LDAP_COMMON_LIBS_DEP)
+EXTRA_LIBS_DEP += \
+ $(LDAPSDK_DEP) \
+ $(SECURITY_DEP)
+EXTRA_LIBS += \
+ $(LIBSLAPD) \
+ $(LDAP_SDK_LIBLDAP_DLL) \
+ $(LIBUTIL) \
+ $(NSPRLINK) \
+ $(LDAP_COMMON_LIBS)
+endif
+ifeq ($(ARCH), AIX)
+EXTRA_LIBS_DEP += \
+ $(LIBSLAPD_DEP) \
+ $(LDAP_LIBUTIL_DEP) \
+ $(LDAP_COMMON_LIBS_DEP)
+EXTRA_LIBS_DEP += \
+ $(LDAPSDK_DEP)
+EXTRA_LIBS += \
+ $(LIBSLAPDLINK) \
+ $(LDAP_SDK_LIBLDAP_DLL) \
+ $(LIBUTIL) \
+ $(NSPRLINK) \
+ $(LDAP_COMMON_LIBS)
+endif
+
+ifeq ($(ARCH), HPUX)
+EXTRA_LIBS_DEP += $(LIBSLAPD_DEP) $(LDAPSDK_DEP) $(NSPR_DEP)
+EXTRA_LIBS += $(DYN_NSHTTPD) $(ADMINUTIL_LINK) $(LDAPLINK) $(NSPRLINK) $(ICULINK)
+endif
+
+ifeq ($(ARCH), WINNT)
+DLL_LDFLAGS += -def:"./libdistrib.def"
+CFLAGS+= /WX
+endif # WINNT
+
+ifeq ($(ARCH), AIX)
+LD=ld
+endif
+
+clientSDK:
+
+all: $(OBJDEST) $(LIBDIR) $(LIBDIS)
+
+$(LIBDIS): $(OBJS) $(LIBDIS_DLL_OBJ) $(DEF_FILE)
+ $(LINK_DLL) $(LIBDIS_DLL_OBJ) $(EXTRA_LIBS)
+
+veryclean: clean
+
+clean:
+ $(RM) $(OBJS)
+ifeq ($(ARCH), WINNT)
+ $(RM) $(LIBDIS_DLL_OBJ)
+endif
+ $(RM) $(LIBDIS)
+
+$(OBJDEST):
+ $(MKDIR) $(OBJDEST)
diff --git a/ldap/servers/plugins/distrib/Makefile.AIX b/ldap/servers/plugins/distrib/Makefile.AIX
new file mode 100644
index 00000000..d155626d
--- /dev/null
+++ b/ldap/servers/plugins/distrib/Makefile.AIX
@@ -0,0 +1,44 @@
+#
+# PROPRIETARY/CONFIDENTIAL. Use of this product is subject to
+# license terms. Copyright 2001 Sun Microsystems, Inc.
+# Some preexisting portions Copyright 2001 Netscape Communications Corp.
+# All rights reserved.
+#
+# AIX Makefile for Directory Server plug-in examples
+# NOTE: Make sure to set the DSLIB variable to the path
+# to the libslapd_shr.a file (for example,
+# DSLIB = /usr/netscape/suitespot/lib/libslapd_shr.a
+
+CC = xlC_r
+LD = ld
+
+# Set this to the path to the libslapd_shr.a file
+DSLIB =
+
+INCLUDE_FLAGS= -I../../include
+CFLAGS= $(INCLUDE_FLAGS) -qarch=com
+LIBPATH=/usr/lib/threads:/usr/lpp/xlC/lib:/usr/lib:/lib:..:../../../../lib
+EXTRA_LIBS= -bI:/usr/lib/lowsys.exp -lC_r -lC -lpthreads -lc_r -lm \
+ /usr/lib/libc.a $(DSLIB)
+LDFLAGS= -bE:libtest-plugin_shr.exp -bM:SRE -bnoentry -blibpath:$(LIBPATH) \
+ $(EXTRA_LIBS)
+
+OBJS = distrib
+
+all: libtest-plugin_shr.a
+
+
+libtest-plugin_shr.a: $(OBJS)
+ rm -f libtest-plugin_shr.exp
+ echo "#!" > libtest-plugin_shr.exp
+ nm -B -C -g $(OBJS) | \
+ awk '/ [B,T,D] / {print $$3}' | \
+ sed -e 's/^\.//' | sort -u >> libtest-plugin_shr.exp
+ $(LD) $(LDFLAGS) -o $@ $(OBJS)
+
+.c.o:
+ $(CC) $(CFLAGS) -c $<
+
+clean:
+ -rm -f $(OBJS) libtest-plugin_shr.a
+
diff --git a/ldap/servers/plugins/distrib/Makefile.BSDI b/ldap/servers/plugins/distrib/Makefile.BSDI
new file mode 100644
index 00000000..c8016c9e
--- /dev/null
+++ b/ldap/servers/plugins/distrib/Makefile.BSDI
@@ -0,0 +1,30 @@
+#
+# PROPRIETARY/CONFIDENTIAL. Use of this product is subject to
+# license terms. Copyright 2001 Sun Microsystems, Inc.
+# Some preexisting portions Copyright 2001 Netscape Communications Corp.
+# All rights reserved.
+#
+# SOLARIS Makefile for Directory Server plug-in examples
+#
+
+CC = cc
+LD = ld
+
+INCLUDE_FLAGS = -I../../include
+CFLAGS = $(INCLUDE_FLAGS) -D_REENTRANT -KPIC
+LDFLAGS = -G
+
+OBJS = distrib.o
+
+all: libtest-plugin.so
+
+
+libtest-plugin.so: $(OBJS)
+ $(LD) $(LDFLAGS) -o $@ $(OBJS)
+
+.c.o:
+ $(CC) $(CFLAGS) -c $<
+
+clean:
+ -rm -f $(OBJS) libtest-plugin.so
+
diff --git a/ldap/servers/plugins/distrib/Makefile.HPUX b/ldap/servers/plugins/distrib/Makefile.HPUX
new file mode 100644
index 00000000..34c24521
--- /dev/null
+++ b/ldap/servers/plugins/distrib/Makefile.HPUX
@@ -0,0 +1,27 @@
+#
+# PROPRIETARY/CONFIDENTIAL. Use of this product is subject to
+# license terms. Copyright 2001 Sun Microsystems, Inc.
+# Some preexisting portions Copyright 2001 Netscape Communications Corp.
+# All rights reserved.
+#
+# HPUX Makefile for Directory Server plug-in examples
+
+CC = cc
+LD = ld
+
+INCLUDE = -I../../include
+CFLAGS=$(INCLUDE) -D_HPUX_SOURCE -Aa +DA1.0 +z
+LDFLAGS = -b
+
+OBJS = distrib.o
+
+all: libtest-plugin.sl
+
+libtest-plugin.sl: $(OBJS)
+ $(LD) $(LDFLAGS) -o $@ $(OBJS)
+
+.c.o:
+ $(CC) $(CFLAGS) -c $<
+
+clean:
+ -rm -f $(OBJS) libtest-plugin.sl
diff --git a/ldap/servers/plugins/distrib/Makefile.HPUX64 b/ldap/servers/plugins/distrib/Makefile.HPUX64
new file mode 100644
index 00000000..d6a09339
--- /dev/null
+++ b/ldap/servers/plugins/distrib/Makefile.HPUX64
@@ -0,0 +1,27 @@
+#
+# PROPRIETARY/CONFIDENTIAL. Use of this product is subject to
+# license terms. Copyright 2001 Sun Microsystems, Inc.
+# Some preexisting portions Copyright 2001 Netscape Communications Corp.
+# All rights reserved.
+#
+# HPUX Makefile for Directory Server plug-in examples
+
+CC = cc
+LD = ld
+
+INCLUDE = -I../../include
+CFLAGS=$(INCLUDE) -D_HPUX_SOURCE -Aa +DA2.0W +z
+LDFLAGS = -b
+
+OBJS = distrib.o
+
+all: libtest-plugin.sl
+
+libtest-plugin.sl: $(OBJS)
+ $(LD) $(LDFLAGS) -o $@ $(OBJS)
+
+.c.o:
+ $(CC) $(CFLAGS) -c $<
+
+clean:
+ -rm -f $(OBJS) libtest-plugin.sl
diff --git a/ldap/servers/plugins/distrib/Makefile.IRIX b/ldap/servers/plugins/distrib/Makefile.IRIX
new file mode 100644
index 00000000..f2ffc0c7
--- /dev/null
+++ b/ldap/servers/plugins/distrib/Makefile.IRIX
@@ -0,0 +1,30 @@
+#
+# PROPRIETARY/CONFIDENTIAL. Use of this product is subject to
+# license terms. Copyright 2001 Sun Microsystems, Inc.
+# Some preexisting portions Copyright 2001 Netscape Communications Corp.
+# All rights reserved.
+#
+# IRIX Makefile for Directory Server plug-in examples
+#
+
+CC = cc
+LD = ld
+
+INCLUDE_FLAGS = -I../../include
+CFLAGS = $(INCLUDE_FLAGS) -D_SGI_MP_SOURCE -fullwarn
+LDFLAGS = -32 -shared
+
+OBJS = distrib.o
+
+all: libtest-plugin.so
+
+
+libtest-plugin.so: $(OBJS)
+ $(LD) $(LDFLAGS) -o $@ $(OBJS)
+
+.c.o:
+ $(CC) $(CFLAGS) -c $<
+
+clean:
+ -rm -f $(OBJS) libtest-plugin.so
+
diff --git a/ldap/servers/plugins/distrib/Makefile.Linux b/ldap/servers/plugins/distrib/Makefile.Linux
new file mode 100644
index 00000000..b5fe839f
--- /dev/null
+++ b/ldap/servers/plugins/distrib/Makefile.Linux
@@ -0,0 +1,30 @@
+#
+# PROPRIETARY/CONFIDENTIAL. Use of this product is subject to
+# license terms. Copyright 2001 Sun Microsystems, Inc.
+# Some preexisting portions Copyright 2001 Netscape Communications Corp.
+# All rights reserved.
+#
+# SOLARIS Makefile for Directory Server plug-in examples
+#
+
+CC = gcc
+LD = ld
+
+INCLUDE_FLAGS = -I../../include
+CFLAGS = $(INCLUDE_FLAGS) -D_REENTRANT -KPIC
+LDFLAGS = -G
+
+OBJS = distrib.o
+
+all: libtest-plugin.so
+
+
+libtest-plugin.so: $(OBJS)
+ $(LD) $(LDFLAGS) -o $@ $(OBJS)
+
+.c.o:
+ $(CC) $(CFLAGS) -c $<
+
+clean:
+ -rm -f $(OBJS) libtest-plugin.so
+
diff --git a/ldap/servers/plugins/distrib/Makefile.OSF1 b/ldap/servers/plugins/distrib/Makefile.OSF1
new file mode 100644
index 00000000..2c2c4660
--- /dev/null
+++ b/ldap/servers/plugins/distrib/Makefile.OSF1
@@ -0,0 +1,29 @@
+#
+# PROPRIETARY/CONFIDENTIAL. Use of this product is subject to
+# license terms. Copyright 2001 Sun Microsystems, Inc.
+# Some preexisting portions Copyright 2001 Netscape Communications Corp.
+# All rights reserved.
+#
+# OSF1 Makefile for Directory Server plug-in examples
+
+CC = cc
+LD = ld
+
+INCLUDE = -I../../include
+CFLAGS = $(INCLUDE) -DIS_64 -ieee_with_inexact -pthread -DOSF1
+LDFLAGS = -shared -all -expect_unresolved "*" -taso
+
+
+OBJS = distrib.o
+
+all: libtest-plugin.so
+
+libtest-plugin.so: $(OBJS)
+ $(LD) $(LDFLAGS) -o $@ $(OBJS)
+
+.c.o:
+ $(CC) $(CFLAGS) -c $<
+
+clean:
+ -rm -f $(OBJS) libtest-plugin.so
+
diff --git a/ldap/servers/plugins/distrib/Makefile.ReliantUNIX b/ldap/servers/plugins/distrib/Makefile.ReliantUNIX
new file mode 100644
index 00000000..c8016c9e
--- /dev/null
+++ b/ldap/servers/plugins/distrib/Makefile.ReliantUNIX
@@ -0,0 +1,30 @@
+#
+# PROPRIETARY/CONFIDENTIAL. Use of this product is subject to
+# license terms. Copyright 2001 Sun Microsystems, Inc.
+# Some preexisting portions Copyright 2001 Netscape Communications Corp.
+# All rights reserved.
+#
+# SOLARIS Makefile for Directory Server plug-in examples
+#
+
+CC = cc
+LD = ld
+
+INCLUDE_FLAGS = -I../../include
+CFLAGS = $(INCLUDE_FLAGS) -D_REENTRANT -KPIC
+LDFLAGS = -G
+
+OBJS = distrib.o
+
+all: libtest-plugin.so
+
+
+libtest-plugin.so: $(OBJS)
+ $(LD) $(LDFLAGS) -o $@ $(OBJS)
+
+.c.o:
+ $(CC) $(CFLAGS) -c $<
+
+clean:
+ -rm -f $(OBJS) libtest-plugin.so
+
diff --git a/ldap/servers/plugins/distrib/Makefile.SOLARIS b/ldap/servers/plugins/distrib/Makefile.SOLARIS
new file mode 100644
index 00000000..c8016c9e
--- /dev/null
+++ b/ldap/servers/plugins/distrib/Makefile.SOLARIS
@@ -0,0 +1,30 @@
+#
+# PROPRIETARY/CONFIDENTIAL. Use of this product is subject to
+# license terms. Copyright 2001 Sun Microsystems, Inc.
+# Some preexisting portions Copyright 2001 Netscape Communications Corp.
+# All rights reserved.
+#
+# SOLARIS Makefile for Directory Server plug-in examples
+#
+
+CC = cc
+LD = ld
+
+INCLUDE_FLAGS = -I../../include
+CFLAGS = $(INCLUDE_FLAGS) -D_REENTRANT -KPIC
+LDFLAGS = -G
+
+OBJS = distrib.o
+
+all: libtest-plugin.so
+
+
+libtest-plugin.so: $(OBJS)
+ $(LD) $(LDFLAGS) -o $@ $(OBJS)
+
+.c.o:
+ $(CC) $(CFLAGS) -c $<
+
+clean:
+ -rm -f $(OBJS) libtest-plugin.so
+
diff --git a/ldap/servers/plugins/distrib/Makefile.SOLARIS64 b/ldap/servers/plugins/distrib/Makefile.SOLARIS64
new file mode 100644
index 00000000..170d2b47
--- /dev/null
+++ b/ldap/servers/plugins/distrib/Makefile.SOLARIS64
@@ -0,0 +1,30 @@
+#
+# PROPRIETARY/CONFIDENTIAL. Use of this product is subject to
+# license terms. Copyright 2001 Sun Microsystems, Inc.
+# Some preexisting portions Copyright 2001 Netscape Communications Corp.
+# All rights reserved.
+#
+# SOLARIS Makefile for Directory Server plug-in examples
+#
+
+CC = cc
+LD = ld
+
+INCLUDE_FLAGS = -I../../include
+CFLAGS = $(INCLUDE_FLAGS) -D_REENTRANT -KPIC -xarch=v9
+LDFLAGS = -G -xarch=v9
+
+OBJS = distrib.o
+
+all: libtest-plugin.so
+
+
+libtest-plugin.so: $(OBJS)
+ $(LD) $(LDFLAGS) -o $@ $(OBJS)
+
+.c.o:
+ $(CC) $(CFLAGS) -c $<
+
+clean:
+ -rm -f $(OBJS) libtest-plugin.so
+
diff --git a/ldap/servers/plugins/distrib/Makefile.SOLARISx86 b/ldap/servers/plugins/distrib/Makefile.SOLARISx86
new file mode 100644
index 00000000..c8016c9e
--- /dev/null
+++ b/ldap/servers/plugins/distrib/Makefile.SOLARISx86
@@ -0,0 +1,30 @@
+#
+# PROPRIETARY/CONFIDENTIAL. Use of this product is subject to
+# license terms. Copyright 2001 Sun Microsystems, Inc.
+# Some preexisting portions Copyright 2001 Netscape Communications Corp.
+# All rights reserved.
+#
+# SOLARIS Makefile for Directory Server plug-in examples
+#
+
+CC = cc
+LD = ld
+
+INCLUDE_FLAGS = -I../../include
+CFLAGS = $(INCLUDE_FLAGS) -D_REENTRANT -KPIC
+LDFLAGS = -G
+
+OBJS = distrib.o
+
+all: libtest-plugin.so
+
+
+libtest-plugin.so: $(OBJS)
+ $(LD) $(LDFLAGS) -o $@ $(OBJS)
+
+.c.o:
+ $(CC) $(CFLAGS) -c $<
+
+clean:
+ -rm -f $(OBJS) libtest-plugin.so
+
diff --git a/ldap/servers/plugins/distrib/Makefile.UnixWare b/ldap/servers/plugins/distrib/Makefile.UnixWare
new file mode 100644
index 00000000..c8016c9e
--- /dev/null
+++ b/ldap/servers/plugins/distrib/Makefile.UnixWare
@@ -0,0 +1,30 @@
+#
+# PROPRIETARY/CONFIDENTIAL. Use of this product is subject to
+# license terms. Copyright 2001 Sun Microsystems, Inc.
+# Some preexisting portions Copyright 2001 Netscape Communications Corp.
+# All rights reserved.
+#
+# SOLARIS Makefile for Directory Server plug-in examples
+#
+
+CC = cc
+LD = ld
+
+INCLUDE_FLAGS = -I../../include
+CFLAGS = $(INCLUDE_FLAGS) -D_REENTRANT -KPIC
+LDFLAGS = -G
+
+OBJS = distrib.o
+
+all: libtest-plugin.so
+
+
+libtest-plugin.so: $(OBJS)
+ $(LD) $(LDFLAGS) -o $@ $(OBJS)
+
+.c.o:
+ $(CC) $(CFLAGS) -c $<
+
+clean:
+ -rm -f $(OBJS) libtest-plugin.so
+
diff --git a/ldap/servers/plugins/distrib/Makefile.UnixWareUDK b/ldap/servers/plugins/distrib/Makefile.UnixWareUDK
new file mode 100644
index 00000000..c8016c9e
--- /dev/null
+++ b/ldap/servers/plugins/distrib/Makefile.UnixWareUDK
@@ -0,0 +1,30 @@
+#
+# PROPRIETARY/CONFIDENTIAL. Use of this product is subject to
+# license terms. Copyright 2001 Sun Microsystems, Inc.
+# Some preexisting portions Copyright 2001 Netscape Communications Corp.
+# All rights reserved.
+#
+# SOLARIS Makefile for Directory Server plug-in examples
+#
+
+CC = cc
+LD = ld
+
+INCLUDE_FLAGS = -I../../include
+CFLAGS = $(INCLUDE_FLAGS) -D_REENTRANT -KPIC
+LDFLAGS = -G
+
+OBJS = distrib.o
+
+all: libtest-plugin.so
+
+
+libtest-plugin.so: $(OBJS)
+ $(LD) $(LDFLAGS) -o $@ $(OBJS)
+
+.c.o:
+ $(CC) $(CFLAGS) -c $<
+
+clean:
+ -rm -f $(OBJS) libtest-plugin.so
+
diff --git a/ldap/servers/plugins/distrib/Makefile.WINNT b/ldap/servers/plugins/distrib/Makefile.WINNT
new file mode 100644
index 00000000..ddb02e29
--- /dev/null
+++ b/ldap/servers/plugins/distrib/Makefile.WINNT
@@ -0,0 +1,38 @@
+# Makefile for Directory Server plug-in
+#
+
+CC = cl
+LD = link
+
+
+TARGET=libdistrib
+
+OBJS=distrib.obj
+
+
+INC = ..\..\include
+CFLAGS = /nologo -I $(INC) /c
+LDFLAGS = /dll /nologo
+LIBS=/DEFAULTLIB:kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib wsock32.lib ..\..\lib\libslapd.lib ..\..\lib\libnspr4.lib
+
+
+all: \
+ init \
+ $(TARGET).dll
+
+init:
+ "c:\program files\microsoft visual studio\vc98\bin\vcvars32.bat"
+
+
+$(TARGET).dll: $(OBJS)
+ $(LD) $(LDFLAGS) /def:$(TARGET).def /out:$(TARGET).dll $(LIBS) $(OBJS)
+ -rm -f $(OBJS2) *~
+
+.c.obj:
+ $(CC) $(CFLAGS) $<
+
+clean:
+ del -f $(OBJS) $(TARGET).dll *~
+
+
+
diff --git a/ldap/servers/plugins/distrib/README b/ldap/servers/plugins/distrib/README
new file mode 100644
index 00000000..3ee6ff88
--- /dev/null
+++ b/ldap/servers/plugins/distrib/README
@@ -0,0 +1,23 @@
+ ----------------------------
+ Sample pluggable distribution logic
+ for Netscape Directory Server
+ ----------------------------
+
+This directory contains code for some sample server plug-ins intended for
+use with the Netscape Directory Server 7.
+
+ NOTE: Before you compile and run these examples, make sure
+ to change any server-specific data in the examples to
+ values applicable to your Directory Server.
+
+distrib.c
+----------
+This is an example of a distribution function that can be used
+to distribute a flat namespace into several backends
+
+/**
+ * PROPRIETARY/CONFIDENTIAL. Use of this product is subject to
+ * license terms. Copyright 2001 Sun Microsystems, Inc.
+ * Some preexisting portions Copyright 2001 Netscape Communications Corp.
+ * All rights reserved.
+ */
diff --git a/ldap/servers/plugins/distrib/distrib.c b/ldap/servers/plugins/distrib/distrib.c
new file mode 100644
index 00000000..fd8ea5b6
--- /dev/null
+++ b/ldap/servers/plugins/distrib/distrib.c
@@ -0,0 +1,222 @@
+/**
+ * PROPRIETARY/CONFIDENTIAL. Use of this product is subject to
+ * license terms. Copyright 2001 Sun Microsystems, Inc.
+ * Some preexisting portions Copyright 2001 Netscape Communications Corp.
+ * All rights reserved.
+ */
+#include <ctype.h>
+#include <string.h>
+#include "slapi-plugin.h"
+
+/*
+ * These are examples of distribution function as declared in mapping tree node
+ * This function will be called for every operations
+ * reaching this node, including subtree search operations that are started
+ * above this node
+ *
+ * Parameters :
+ * . pb is the pblock of the operation
+ * . target_dn is the target DN of the operation
+ * . mtn_be_names is the list of names of backends declared for this node
+ * . be_count is the number of backends declared
+ * . node_dn is the node where the distribution function is set
+ *
+ * The return value of the functions should be the indice of the backend
+ * in the mtn_be_names table
+ * For search operation, the value SLAPI_BE_ALL_BACKENDS can be used to
+ * specify that all backends must be searched
+ * The use of value SLAPI_BE_ALL_BACKENDS for operation other than search
+ * is not supported and may give random results
+ *
+ */
+
+/*
+ * Distribute the entries based on the first letter of their rdn
+ *
+ * . Entries starting with anything other that a-z or A-Z will always
+ * go in backend 0
+ * . Entries starting with letter (a-z or A-Z) will be shared between
+ * the backends depending following the alphabetic order
+ * Example : if 3 backends are used, entries starting with A-I will go
+ * in backend 0, entries starting with J-R will go in backend 1, entries
+ * starting with S-Z will go in backend 2
+ *
+ * Of course this won't work for all locales...
+ *
+ * This example only works for a flat namespace below the node DN
+ */
+int alpha_distribution(Slapi_PBlock *pb, Slapi_DN * target_dn,
+ char **mtn_be_names, int be_count, Slapi_DN * node_dn)
+{
+ unsigned long op_type;
+ Slapi_Operation *op;
+ char *rdn_type;
+ char *rdn_value;
+ Slapi_RDN *rdn = NULL;
+ char c;
+
+ /* first check the operation type
+ * searches at node level or above it should go in all backends
+ * searches below node level should go in only one backend
+ */
+ slapi_pblock_get(pb, SLAPI_OPERATION, &op);
+ op_type = slapi_op_get_type(op);
+ if ((op_type == SLAPI_OPERATION_SEARCH) &&
+ slapi_sdn_issuffix(node_dn, target_dn))
+ return SLAPI_BE_ALL_BACKENDS;
+
+ /* now choose the backend
+ * anything starting with a char different from a-z or A-Z will
+ * go in backend 0
+ */
+
+ /* get the first char of first value of rdn */
+ rdn = slapi_rdn_new();
+ slapi_sdn_get_rdn(target_dn, rdn);
+ slapi_rdn_get_first(rdn, &rdn_type, &rdn_value);
+ c = rdn_value[0];
+
+ if (!(((c >= 'a') && (c <= 'z')) ||
+ ((c >= 'A') && (c <= 'Z')) ))
+ {
+ return 0;
+ }
+
+ slapi_rdn_free(&rdn);
+
+ /* for entries with rdn starting with alphabetic characters
+ * use the formula : (c - 'A') * be_count/26
+ * to calculate the backend number
+ */
+ return (toupper(c) - 'A') * be_count/26;
+}
+
+/*
+ * Distribute the entries based on a simple hash algorithme
+ */
+int hash_distribution(Slapi_PBlock *pb, Slapi_DN * target_dn,
+ char **mtn_be_names, int be_count, Slapi_DN * node_dn)
+{
+ unsigned long op_type;
+ Slapi_Operation *op;
+ char *rdn_type;
+ char *rdn_value;
+ Slapi_RDN *rdn = NULL;
+ int hash_value;
+
+ /* first check the operation type
+ * searches at node level or above it should go in all backends
+ * searches below node level should go in only one backend
+ */
+ slapi_pblock_get(pb, SLAPI_OPERATION, &op);
+ op_type = slapi_op_get_type(op);
+ if ((op_type == SLAPI_OPERATION_SEARCH) &&
+ slapi_sdn_issuffix(node_dn, target_dn))
+ return SLAPI_BE_ALL_BACKENDS;
+
+ /* now choose the backend
+ */
+
+ /* get the rdn and hash it to compute the backend number
+ * use a simple hash for this example
+ */
+ rdn = slapi_rdn_new();
+ slapi_sdn_get_rdn(target_dn, rdn);
+ slapi_rdn_get_first(rdn, &rdn_type, &rdn_value);
+
+ /* compute the hash value */
+ hash_value = 0;
+ while (*rdn_value)
+ {
+ hash_value += *rdn_value;
+ rdn_value++;
+ }
+ hash_value = hash_value % be_count;
+
+ slapi_rdn_free(&rdn);
+
+ /* use the hash_value as the returned backend number */
+ return hash_value;
+}
+
+/*
+ * This plugin allows to use a local backend in conjonction with
+ * a chaining backend
+ * The ldbm backend is considered a read-only replica of the data
+ * The chaining backend point to a red-write replica of the data
+ * This distribution logic forward the update request to the chaining
+ * backend, and send the search request to the local dbm database
+ *
+ * The mechanism for updating the local read-only replica is not
+ * taken into account by this plugin
+ *
+ * To be able to use it one must define one ldbm backend and one chaining
+ * backend in the mapping tree node
+ *
+ */
+int chaining_distribution(Slapi_PBlock *pb, Slapi_DN * target_dn,
+ char **mtn_be_names, int be_count, Slapi_DN * node_dn)
+{
+ char * requestor_dn;
+ unsigned long op_type;
+ Slapi_Operation *op;
+ int repl_op = 0;
+ int local_backend = -1;
+ int chaining_backend = -1;
+ int i;
+ char * name;
+
+ /* first, we have to decide which backend is the local backend
+ * and which is the chaining one
+ * For the purpose of this example use the backend name :
+ * the backend with name starting with ldbm is local
+ * the bakend with name starting with chaining is remote
+ */
+ local_backend = -1;
+ chaining_backend = -1;
+ for (i=0; i<be_count; i++)
+ {
+ name = mtn_be_names[i];
+ if ((0 == strncmp(name, "ldbm", 4)) ||
+ (0 == strncmp(name, "user", 4)))
+ local_backend = i;
+ else if (0 == strncmp(name, "chaining", 8))
+ chaining_backend = i;
+ }
+
+ /* Check the operation type
+ * read-only operation will go to the local backend
+ * updates operation will go to the chaining backend
+ */
+ slapi_pblock_get(pb, SLAPI_OPERATION, &op);
+ op_type = slapi_op_get_type(op);
+ if ((op_type == SLAPI_OPERATION_SEARCH) ||
+ (op_type == SLAPI_OPERATION_BIND) ||
+ (op_type == SLAPI_OPERATION_UNBIND) ||
+ (op_type == SLAPI_OPERATION_COMPARE))
+ return local_backend;
+
+ /* if the operation is done by directory manager
+ * use local database even for updates because it is an administrative
+ * operation
+ * remarks : one could also use an update DN in the same way
+ * to let update operation go to the local backend when they are done
+ * by specific administrator user but let all the other user
+ * go to the read-write replica
+ */
+ slapi_pblock_get( pb, SLAPI_REQUESTOR_DN, &requestor_dn );
+ if (slapi_dn_isroot(requestor_dn))
+ return local_backend;
+
+ /* if the operation is a replicated operation
+ * use local database even for updates to avoid infinite loops
+ */
+ slapi_pblock_get (pb, SLAPI_IS_REPLICATED_OPERATION, &repl_op);
+ if (repl_op)
+ return local_backend;
+
+ /* all other case (update while not directory manager) :
+ * use the chaining backend
+ */
+ return chaining_backend;
+}
diff --git a/ldap/servers/plugins/distrib/distrib.dsp b/ldap/servers/plugins/distrib/distrib.dsp
new file mode 100644
index 00000000..ebf11f1e
--- /dev/null
+++ b/ldap/servers/plugins/distrib/distrib.dsp
@@ -0,0 +1,116 @@
+# Microsoft Developer Studio Project File - Name="distrib" - Package Owner=<4>
+# Microsoft Developer Studio Generated Build File, Format Version 5.00
+# ** DO NOT EDIT **
+
+# TARGTYPE "Win32 (x86) Dynamic-Link Library" 0x0102
+
+CFG=distrib - Win32 Release
+!MESSAGE This is not a valid makefile. To build this project using NMAKE,
+!MESSAGE use the Export Makefile command and run
+!MESSAGE
+!MESSAGE NMAKE /f "distrib.mak".
+!MESSAGE
+!MESSAGE You can specify a configuration when running NMAKE
+!MESSAGE by defining the macro CFG on the command line. For example:
+!MESSAGE
+!MESSAGE NMAKE /f "distrib.mak" CFG="distrib - Win32 Release"
+!MESSAGE
+!MESSAGE Possible choices for configuration are:
+!MESSAGE
+!MESSAGE "distrib - Win32 Release" (based on\
+ "Win32 (x86) Dynamic-Link Library")
+!MESSAGE "distrib - Win32 Debug" (based on\
+ "Win32 (x86) Dynamic-Link Library")
+!MESSAGE
+
+# Begin Project
+# PROP Scc_ProjName ""
+# PROP Scc_LocalPath ""
+CPP=cl.exe
+MTL=midl.exe
+RSC=rc.exe
+
+!IF "$(CFG)" == "distrib - Win32 Release"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 0
+# PROP BASE Output_Dir ".\Release"
+# PROP BASE Intermediate_Dir ".\Release"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 0
+# PROP Output_Dir ".\Release"
+# PROP Intermediate_Dir ".\Release"
+# PROP Ignore_Export_Lib 0
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /MT /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /YX /c
+# ADD CPP /nologo /MD /W3 /GX /O2 /I "..\..\include" /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_WIN32" /YX /FD /c
+# ADD BASE MTL /nologo /D "NDEBUG" /win32
+# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32
+# ADD BASE RSC /l 0x409 /d "NDEBUG"
+# ADD RSC /l 0x409 /d "NDEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LINK32=link.exe
+# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /dll /machine:I386
+# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib wsock32.lib ..\..\lib\libslapd.lib /nologo /subsystem:windows /dll /machine:I386
+
+!ELSEIF "$(CFG)" == "distrib - Win32 Debug"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 1
+# PROP BASE Output_Dir ".\Debug"
+# PROP BASE Intermediate_Dir ".\Debug"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 1
+# PROP Output_Dir ".\Debug"
+# PROP Intermediate_Dir ".\Debug"
+# PROP Ignore_Export_Lib 0
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /MTd /W3 /Gm /GX /Zi /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /YX /c
+# ADD CPP /nologo /MD /W3 /Gm /GX /Zi /Od /I "..\..\include" /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_WIN32" /YX /FD /c
+# ADD BASE MTL /nologo /D "_DEBUG" /win32
+# ADD MTL /nologo /D "_DEBUG" /mktyplib203 /win32
+# ADD BASE RSC /l 0x409 /d "_DEBUG"
+# ADD RSC /l 0x409 /d "_DEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LINK32=link.exe
+# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /dll /debug /machine:I386
+# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib wsock32.lib ..\..\lib\libslapd.lib /nologo /subsystem:windows /dll /debug /machine:I386
+
+!ENDIF
+
+# Begin Target
+
+# Name "distrib - Win32 Release"
+# Name "distrib - Win32 Debug"
+# Begin Group "Source Files"
+
+# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;hpj;bat;for;f90"
+# Begin Source File
+
+SOURCE=.\dllmain.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\distrib.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\libdistrib.def
+# End Source File
+# End Group
+# Begin Group "Header Files"
+
+# PROP Default_Filter "h;hpp;hxx;hm;inl;fi;fd"
+# End Group
+# Begin Group "Resource Files"
+
+# PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;cnt;rtf;gif;jpg;jpeg;jpe"
+# End Group
+# End Target
+# End Project
diff --git a/ldap/servers/plugins/distrib/dllmain.c b/ldap/servers/plugins/distrib/dllmain.c
new file mode 100644
index 00000000..bce5eed7
--- /dev/null
+++ b/ldap/servers/plugins/distrib/dllmain.c
@@ -0,0 +1,101 @@
+/**
+ * PROPRIETARY/CONFIDENTIAL. Use of this product is subject to
+ * license terms. Copyright 2001 Sun Microsystems, Inc.
+ * Some preexisting portions Copyright 2001 Netscape Communications Corp.
+ * All rights reserved.
+ */
+/*
+ * Copyright (C) 2000 Sun Microsystems Inc.
+ *
+ * Use of this Source Code is subject to the terms of the applicable license
+ * agreement from Sun Microsystems Inc.
+ *
+ * The copyright notice(s) in this Source Code does not indicate actual or
+ * intended publication of this Source Code.
+ */
+
+ /*
+ * Microsoft Windows specifics
+ */
+#include "ldap.h"
+
+
+#ifdef _WIN32
+/* Lifted from Q125688
+ * How to Port a 16-bit DLL to a Win32 DLL
+ * on the MSVC 4.0 CD
+ */
+BOOL WINAPI DllMain (HANDLE hModule, DWORD fdwReason, LPVOID lpReserved)
+{
+
+ switch (fdwReason)
+ {
+ case DLL_PROCESS_ATTACH:
+ /* Code from LibMain inserted here. Return TRUE to keep the
+ DLL loaded or return FALSE to fail loading the DLL.
+
+ You may have to modify the code in your original LibMain to
+ account for the fact that it may be called more than once.
+ You will get one DLL_PROCESS_ATTACH for each process that
+ loads the DLL. This is different from LibMain which gets
+ called only once when the DLL is loaded. The only time this
+ is critical is when you are using shared data sections.
+ If you are using shared data sections for statically
+ allocated data, you will need to be careful to initialize it
+ only once. Check your code carefully.
+
+ Certain one-time initializations may now need to be done for
+ each process that attaches. You may also not need code from
+ your original LibMain because the operating system may now
+ be doing it for you.
+ */
+ /*
+ * 16 bit code calls UnlockData()
+ * which is mapped to UnlockSegment in windows.h
+ * in 32 bit world UnlockData is not defined anywhere
+ * UnlockSegment is mapped to GlobalUnfix in winbase.h
+ * and the docs for both UnlockSegment and GlobalUnfix say
+ * ".. function is oboslete. Segments have no meaning
+ * in the 32-bit environment". So we do nothing here.
+ */
+
+ break;
+
+ case DLL_THREAD_ATTACH:
+ /* Called each time a thread is created in a process that has
+ already loaded (attached to) this DLL. Does not get called
+ for each thread that exists in the process before it loaded
+ the DLL.
+
+ Do thread-specific initialization here.
+ */
+ break;
+
+ case DLL_THREAD_DETACH:
+ /* Same as above, but called when a thread in the process
+ exits.
+
+ Do thread-specific cleanup here.
+ */
+ break;
+
+ case DLL_PROCESS_DETACH:
+ /* Code from _WEP inserted here. This code may (like the
+ LibMain) not be necessary. Check to make certain that the
+ operating system is not doing it for you.
+ */
+
+ break;
+ }
+ /* The return value is only used for DLL_PROCESS_ATTACH; all other
+ conditions are ignored. */
+ return TRUE; /* successful DLL_PROCESS_ATTACH */
+}
+#else
+int CALLBACK
+LibMain( HINSTANCE hinst, WORD wDataSeg, WORD cbHeapSize, LPSTR lpszCmdLine )
+{
+ /*UnlockData( 0 );*/
+ return( 1 );
+}
+#endif
diff --git a/ldap/servers/plugins/distrib/libdistrib.def b/ldap/servers/plugins/distrib/libdistrib.def
new file mode 100644
index 00000000..baef2027
--- /dev/null
+++ b/ldap/servers/plugins/distrib/libdistrib.def
@@ -0,0 +1,10 @@
+;-------------------------------------------------------------------------
+; PROPRIETARY/CONFIDENTIAL. Use of this product is subject to
+; license terms. Copyright 2001 Sun Microsystems, Inc.
+; Some preexisting portions Copyright 2001 Netscape Communications Corp.
+; All rights reserved.
+;-------------------------------------------------------------------------
+DESCRIPTION 'Netscape Directory Server 7 distribution logic example'
+EXPORTS
+ alpha_distribution @1
+ hash_distribution @2
diff --git a/ldap/servers/plugins/http/Makefile b/ldap/servers/plugins/http/Makefile
new file mode 100644
index 00000000..d23c26a5
--- /dev/null
+++ b/ldap/servers/plugins/http/Makefile
@@ -0,0 +1,80 @@
+#
+# PROPRIETARY/CONFIDENTIAL. Use of this product is subject to
+# license terms. Copyright 2001 Sun Microsystems, Inc.
+# Some preexisting portions Copyright 2001 Netscape Communications Corp.
+# All rights reserved.
+#
+LDAP_SRC = ../../..
+MCOM_ROOT = ../../../../..
+
+NOSTDCLEAN=true # don't let nsconfig.mk define target clean
+NOSTDSTRIP=true # don't let nsconfig.mk define target strip
+NSPR20=true # probably should be defined somewhere else (not sure where)
+
+OBJDEST = $(OBJDIR)/lib/libhttpclient
+LIBDIR = $(LIB_RELDIR)
+
+include $(MCOM_ROOT)/ldapserver/nsdefs.mk
+include $(MCOM_ROOT)/ldapserver/nsconfig.mk
+include $(LDAP_SRC)/nsldap.mk
+
+ifeq ($(ARCH), WINNT)
+DEF_FILE:=./http.def
+endif
+
+HTTP_OBJS = http_client.o http_impl.o
+
+OBJS = $(addprefix $(OBJDEST)/, $(HTTP_OBJS))
+
+HTTP_DLL = http-client-plugin
+
+INCLUDES += -I../../slapd -I../../../include
+
+CFLAGS+=$(SLCFLAGS) -DSLAPD_LOGGING
+
+ifeq ($(ARCH), WINNT)
+CFLAGS+=-D_WIN32 -DXP_WIN -DXP_WIN32
+EXTRA_LIBS_DEP += $(LIBSLAPD_DEP) $(NSPR_DEP) $(SECURITY_DEP)
+EXTRA_LIBS += $(NSPRLINK) $(LIBSLAPD) $(SECURITYLINK)
+HTTP_DLL_OBJ = $(addprefix $(OBJDEST)/, dllmain.o)
+endif
+
+ifeq ($(ARCH), HPUX)
+EXTRA_LIBS_DEP += $(LIBSLAPD_DEP) $(LDAPSDK_DEP) $(NSPR_DEP) $(SECURITY_DEP)
+EXTRA_LIBS += $(DYN_NSHTTPD) $(ADMINUTIL_LINK) $(LDAPLINK) $(SECURITYLINK) $(NSPRLINK) $(ICULINK)
+endif
+
+ifeq ($(ARCH), AIX)
+LD=ld
+EXTRA_LIBS += $(LIBSLAPD)
+endif
+
+HTTP= $(addprefix $(LIBDIR)/, $(HTTP_DLL).$(DLL_SUFFIX))
+
+clientSDK:
+
+all: $(OBJDEST) $(LIBDIR) $(HTTP)
+
+ifeq ($(ARCH), WINNT)
+$(HTTP): $(OBJS) $(HTTP_DLL_OBJ) $(DEF_FILE)
+ $(LINK_DLL) $(HTTP_DLL_OBJ) $(EXTRA_LIBS) /DEF:$(DEF_FILE)
+else
+$(HTTP): $(OBJS) $(HTTP_DLL_OBJ)
+ $(LINK_DLL) $(HTTP_DLL_OBJ) $(EXTRA_LIBS)
+endif
+
+
+veryclean: clean
+
+clean:
+ $(RM) $(OBJS)
+ifeq ($(ARCH), WINNT)
+ $(RM) $(HTTP_DLL_OBJ)
+endif
+ $(RM) $(HTTP)
+
+$(OBJDEST):
+ $(MKDIR) $(OBJDEST)
+
+$(LIBDIR):
+ $(MKDIR) $(LIBDIR)
diff --git a/ldap/servers/plugins/http/dllmain.c b/ldap/servers/plugins/http/dllmain.c
new file mode 100644
index 00000000..ef1f637a
--- /dev/null
+++ b/ldap/servers/plugins/http/dllmain.c
@@ -0,0 +1,98 @@
+/**
+ * PROPRIETARY/CONFIDENTIAL. Use of this product is subject to
+ * license terms. Copyright 2001 Sun Microsystems, Inc.
+ * Some preexisting portions Copyright 2001 Netscape Communications Corp.
+ * All rights reserved.
+ */
+/*
+ * Microsoft Windows specifics for BACK-LDBM DLL
+ */
+#include "ldap.h"
+#include "lber.h"
+
+#ifdef _WIN32
+
+/* Lifted from Q125688
+ * How to Port a 16-bit DLL to a Win32 DLL
+ * on the MSVC 4.0 CD
+ */
+
+BOOL WINAPI DllMain (HANDLE hModule, DWORD fdwReason, LPVOID lpReserved)
+{
+ WSADATA wsadata;
+
+ switch (fdwReason)
+ {
+ case DLL_PROCESS_ATTACH:
+ /* Code from LibMain inserted here. Return TRUE to keep the
+ DLL loaded or return FALSE to fail loading the DLL.
+
+ You may have to modify the code in your original LibMain to
+ account for the fact that it may be called more than once.
+ You will get one DLL_PROCESS_ATTACH for each process that
+ loads the DLL. This is different from LibMain which gets
+ called only once when the DLL is loaded. The only time this
+ is critical is when you are using shared data sections.
+ If you are using shared data sections for statically
+ allocated data, you will need to be careful to initialize it
+ only once. Check your code carefully.
+
+ Certain one-time initializations may now need to be done for
+ each process that attaches. You may also not need code from
+ your original LibMain because the operating system may now
+ be doing it for you.
+ */
+ /*
+ * 16 bit code calls UnlockData()
+ * which is mapped to UnlockSegment in windows.h
+ * in 32 bit world UnlockData is not defined anywhere
+ * UnlockSegment is mapped to GlobalUnfix in winbase.h
+ * and the docs for both UnlockSegment and GlobalUnfix say
+ * ".. function is oboslete. Segments have no meaning
+ * in the 32-bit environment". So we do nothing here.
+ */
+
+ if( errno = WSAStartup(0x0101, &wsadata ) != 0 )
+ return FALSE;
+
+ break;
+
+ case DLL_THREAD_ATTACH:
+ /* Called each time a thread is created in a process that has
+ already loaded (attached to) this DLL. Does not get called
+ for each thread that exists in the process before it loaded
+ the DLL.
+
+ Do thread-specific initialization here.
+ */
+ break;
+
+ case DLL_THREAD_DETACH:
+ /* Same as above, but called when a thread in the process
+ exits.
+
+ Do thread-specific cleanup here.
+ */
+ break;
+
+ case DLL_PROCESS_DETACH:
+ /* Code from _WEP inserted here. This code may (like the
+ LibMain) not be necessary. Check to make certain that the
+ operating system is not doing it for you.
+ */
+ WSACleanup();
+
+ break;
+ }
+ /* The return value is only used for DLL_PROCESS_ATTACH; all other
+ conditions are ignored. */
+ return TRUE; /* successful DLL_PROCESS_ATTACH */
+}
+#else
+int CALLBACK
+LibMain( HINSTANCE hinst, WORD wDataSeg, WORD cbHeapSize, LPSTR lpszCmdLine )
+{
+ /*UnlockData( 0 );*/
+ return( 1 );
+}
+#endif
diff --git a/ldap/servers/plugins/http/http.def b/ldap/servers/plugins/http/http.def
new file mode 100644
index 00000000..ac2f3e05
--- /dev/null
+++ b/ldap/servers/plugins/http/http.def
@@ -0,0 +1,13 @@
+;-------------------------------------------------------------------------
+; PROPRIETARY/CONFIDENTIAL. Use of this product is subject to
+; license terms. Copyright 2001 Sun Microsystems, Inc.
+; Some preexisting portions Copyright 2001 Netscape Communications Corp.
+; All rights reserved.
+;-------------------------------------------------------------------------
+DESCRIPTION 'Netscape Directory Server Http Client'
+EXPORTS
+ http_client_init @2
+ plugin_init_debug_level @3
+ http_client_version @4
+
+
diff --git a/ldap/servers/plugins/http/http_client.c b/ldap/servers/plugins/http/http_client.c
new file mode 100644
index 00000000..2fbbdd52
--- /dev/null
+++ b/ldap/servers/plugins/http/http_client.c
@@ -0,0 +1,290 @@
+/**
+ * PROPRIETARY/CONFIDENTIAL. Use of this product is subject to
+ * license terms. Copyright 2001 Sun Microsystems, Inc.
+ * Some preexisting portions Copyright 2001 Netscape Communications Corp.
+ * All rights reserved.
+ */
+
+/**
+ * Simple Http Client API broker plugin
+ */
+
+#include <stdio.h>
+#include <string.h>
+
+#include "portable.h"
+#include "nspr.h"
+
+#include "slapi-plugin.h"
+#include "slapi-private.h"
+#include "dirlite_strings.h"
+#include "dirver.h"
+
+#include "http_client.h"
+#include "http_impl.h"
+
+/* get file mode flags for unix */
+#ifndef _WIN32
+#include <sys/stat.h>
+#endif
+
+/*** from proto-slap.h ***/
+
+int slapd_log_error_proc( char *subsystem, char *fmt, ... );
+
+/*** from ldaplog.h ***/
+
+/* edited ldaplog.h for LDAPDebug()*/
+#ifndef _LDAPLOG_H
+#define _LDAPLOG_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define LDAP_DEBUG_TRACE 0x00001 /* 1 */
+#define LDAP_DEBUG_ANY 0x04000 /* 16384 */
+#define LDAP_DEBUG_PLUGIN 0x10000 /* 65536 */
+
+/* debugging stuff */
+# ifdef _WIN32
+ extern int *module_ldap_debug;
+# define LDAPDebug( level, fmt, arg1, arg2, arg3 ) \
+ { \
+ if ( *module_ldap_debug & level ) { \
+ slapd_log_error_proc( NULL, fmt, arg1, arg2, arg3 ); \
+ } \
+ }
+# else /* _WIN32 */
+ extern int slapd_ldap_debug;
+# define LDAPDebug( level, fmt, arg1, arg2, arg3 ) \
+ { \
+ if ( slapd_ldap_debug & level ) { \
+ slapd_log_error_proc( NULL, fmt, arg1, arg2, arg3 ); \
+ } \
+ }
+# endif /* Win32 */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _LDAP_H */
+
+#define HTTP_PLUGIN_SUBSYSTEM "http-client-plugin" /* used for logging */
+#define HTTP_PLUGIN_VERSION 0x00050050
+
+#define HTTP_SUCCESS 0
+#define HTTP_FAILURE -1
+
+/**
+ * Implementation functions
+ */
+static void *api[7];
+
+/**
+ * Plugin identifiers
+ */
+static Slapi_PluginDesc pdesc = { "http-client",
+ PLUGIN_MAGIC_VENDOR_STR,
+ PRODUCTTEXT,
+ "HTTP Client plugin" };
+
+static Slapi_ComponentId *plugin_id = NULL;
+
+/**
+ **
+ ** Http plug-in management functions
+ **
+ **/
+int http_client_init(Slapi_PBlock *pb);
+static int http_client_start(Slapi_PBlock *pb);
+static int http_client_close(Slapi_PBlock *pb);
+
+/**
+ * our functions
+ */
+static void _http_init(Slapi_ComponentId *plugin_id);
+static int _http_get_text(char *url, char **data, int *bytesRead);
+static int _http_get_binary(char *url, char **data, int *bytesRead);
+static int _http_get_redirected_uri(char *url, char **data, int *bytesRead);
+static int _http_post(char *url, httpheader **httpheaderArray, char *body, char **data, int *bytesRead);
+static void _http_shutdown( void );
+
+#ifdef _WIN32
+int *module_ldap_debug = 0;
+
+void plugin_init_debug_level(int *level_ptr)
+{
+ module_ldap_debug = level_ptr;
+}
+#endif
+
+/**
+ *
+ * Get the presence plug-in version
+ *
+ */
+int http_client_version()
+{
+ return HTTP_PLUGIN_VERSION;
+}
+
+int http_client_init(Slapi_PBlock *pb)
+{
+ int status = HTTP_SUCCESS;
+ PRUint32 nssFlags = 0;
+ LDAPDebug( LDAP_DEBUG_PLUGIN, "--> http_client_init -- BEGIN\n",0,0,0);
+
+ if ( slapi_pblock_set( pb, SLAPI_PLUGIN_VERSION,
+ SLAPI_PLUGIN_VERSION_01 ) != 0 ||
+ slapi_pblock_set(pb, SLAPI_PLUGIN_START_FN,
+ (void *) http_client_start ) != 0 ||
+ slapi_pblock_set(pb, SLAPI_PLUGIN_CLOSE_FN,
+ (void *) http_client_close ) != 0 ||
+ slapi_pblock_set( pb, SLAPI_PLUGIN_DESCRIPTION,
+ (void *)&pdesc ) != 0 )
+ {
+ slapi_log_error( SLAPI_LOG_FATAL, HTTP_PLUGIN_SUBSYSTEM,
+ "http_client_init: failed to register plugin\n" );
+ status = HTTP_FAILURE;
+ }
+
+ /* Retrieve and save the plugin identity to later pass to
+ internal operations */
+ if (slapi_pblock_get(pb, SLAPI_PLUGIN_IDENTITY, &plugin_id) != 0) {
+ slapi_log_error(SLAPI_LOG_FATAL, HTTP_PLUGIN_SUBSYSTEM,
+ "http_client_init: Failed to retrieve SLAPI_PLUGIN_IDENTITY\n");
+ return HTTP_FAILURE;
+ }
+
+ LDAPDebug( LDAP_DEBUG_PLUGIN, "<-- http_client_init -- END\n",0,0,0);
+ return status;
+}
+
+static int http_client_start(Slapi_PBlock *pb)
+{
+ int status = HTTP_SUCCESS;
+ /**
+ * do some init work here
+ */
+ LDAPDebug( LDAP_DEBUG_PLUGIN, "--> http_client_start -- BEGIN\n",0,0,0);
+
+ api[0] = 0; /* reserved for api broker use, must be zero */
+ api[1] = (void *)_http_init;
+ api[2] = (void *)_http_get_text;
+ api[3] = (void *)_http_get_binary;
+ api[4] = (void *)_http_get_redirected_uri;
+ api[5] = (void *)_http_shutdown;
+ api[6] = (void *)_http_post;
+
+ if( slapi_apib_register(HTTP_v1_0_GUID, api) ) {
+ slapi_log_error( SLAPI_LOG_FATAL, HTTP_PLUGIN_SUBSYSTEM,
+ "http_client_start: failed to register functions\n" );
+ status = HTTP_FAILURE;
+ }
+
+ _http_init(plugin_id);
+
+ LDAPDebug( LDAP_DEBUG_PLUGIN, "<-- http_client_start -- END\n",0,0,0);
+ return status;
+}
+
+static int http_client_close(Slapi_PBlock *pb)
+{
+ int status = HTTP_SUCCESS;
+ /**
+ * do cleanup
+ */
+ LDAPDebug( LDAP_DEBUG_PLUGIN, "--> http_client_close -- BEGIN\n",0,0,0);
+
+ slapi_apib_unregister(HTTP_v1_0_GUID);
+
+ LDAPDebug( LDAP_DEBUG_PLUGIN, "<-- http_client_close -- END\n",0,0,0);
+
+ return status;
+}
+
+/**
+ * perform http initialization here
+ */
+static void _http_init(Slapi_ComponentId *plugin_id)
+{
+ LDAPDebug( LDAP_DEBUG_PLUGIN, "--> _http_init -- BEGIN\n",0,0,0);
+
+ http_impl_init(plugin_id);
+
+ LDAPDebug( LDAP_DEBUG_PLUGIN, "<-- _http_init -- END\n",0,0,0);
+}
+
+/**
+ * This method gets the data in a text format based on the
+ * URL send.
+ */
+static int _http_get_text(char *url, char **data, int *bytesRead)
+{
+ int status = HTTP_SUCCESS;
+ LDAPDebug( LDAP_DEBUG_PLUGIN, "--> _http_get_text -- BEGIN\n",0,0,0);
+
+ status = http_impl_get_text(url, data, bytesRead);
+
+ LDAPDebug( LDAP_DEBUG_PLUGIN, "<-- _http_get_text -- END\n",0,0,0);
+ return status;
+}
+
+/**
+ * This method gets the data in a binary format based on the
+ * URL send.
+ */
+static int _http_get_binary(char *url, char **data, int *bytesRead)
+{
+ int status = HTTP_SUCCESS;
+ LDAPDebug( LDAP_DEBUG_PLUGIN, "--> _http_get_binary -- BEGIN\n",0,0,0);
+
+ status = http_impl_get_binary(url, data, bytesRead);
+
+ LDAPDebug( LDAP_DEBUG_PLUGIN, "<-- _http_get_binary -- END\n",0,0,0);
+ return status;
+}
+
+/**
+ * This method intercepts the redirected URI and returns the location
+ * information.
+ */
+static int _http_get_redirected_uri(char *url, char **data, int *bytesRead)
+{
+ int status = HTTP_SUCCESS;
+ LDAPDebug( LDAP_DEBUG_PLUGIN, "--> _http_get_redirected_uri -- BEGIN\n",0,0,0);
+
+ status = http_impl_get_redirected_uri(url, data, bytesRead);
+
+ LDAPDebug( LDAP_DEBUG_PLUGIN, "<-- _http_get_redirected_uri -- END\n",0,0,0);
+ return status;
+}
+
+/**
+ * This method posts the data based on the URL send.
+ */
+static int _http_post(char *url, httpheader ** httpheaderArray, char *body, char **data, int *bytesRead)
+{
+ int status = HTTP_SUCCESS;
+ LDAPDebug( LDAP_DEBUG_PLUGIN, "--> _http_post -- BEGIN\n",0,0,0);
+
+ status = http_impl_post(url, httpheaderArray, body, data, bytesRead);
+
+ LDAPDebug( LDAP_DEBUG_PLUGIN, "<-- _http_post -- END\n",0,0,0);
+ return status;
+}
+
+/**
+ * perform http shutdown here
+ */
+static void _http_shutdown( void )
+{
+ LDAPDebug( LDAP_DEBUG_PLUGIN, "--> _http_shutdown -- BEGIN\n",0,0,0);
+
+ http_impl_shutdown();
+
+ LDAPDebug( LDAP_DEBUG_PLUGIN, "<-- _http_shutdown -- END\n",0,0,0);
+}
+
diff --git a/ldap/servers/plugins/http/http_client.h b/ldap/servers/plugins/http/http_client.h
new file mode 100644
index 00000000..d849e18d
--- /dev/null
+++ b/ldap/servers/plugins/http/http_client.h
@@ -0,0 +1,64 @@
+/**
+ * PROPRIETARY/CONFIDENTIAL. Use of this product is subject to
+ * license terms. Copyright 2001 Sun Microsystems, Inc.
+ * Some preexisting portions Copyright 2001 Netscape Communications Corp.
+ * All rights reserved.
+ */
+
+#ifndef _HTTP_CLIENT_H_
+#define _HTTP_CLIENT_H_
+
+/* Error codes */
+#define HTTP_CLIENT_ERROR_BAD_URL -1
+#define HTTP_CLIENT_ERROR_NET_ADDR -2
+#define HTTP_CLIENT_ERROR_SOCKET_CREATE -3
+#define HTTP_CLIENT_ERROR_CONNECT_FAILED -4
+#define HTTP_CLIENT_ERROR_SEND_REQ -5
+#define HTTP_CLIENT_ERROR_BAD_RESPONSE -6
+#define HTTP_CLIENT_ERROR_SSLSOCKET_CREATE -7
+ #define HTTP_CLIENT_ERROR_NSS_INITIALIZE -8
+
+/*Structure to store HTTP Headers */
+typedef struct {
+ char *name;
+ char *value;
+} httpheader;
+
+
+/* mechanics */
+
+
+typedef void (*api_http_init)(Slapi_ComponentId *plugin_id);
+typedef int (*api_http_get_text)(char *url, char **data, int *bytesRead);
+typedef int (*api_http_get_binary)(char *url, char **data, int *bytesRead);
+typedef int (*api_http_get_redirected_uri)(char *url, char **data, int *bytesRead);
+typedef void (*api_http_shutdown)();
+typedef int (*api_http_post)(char *url, httpheader **httpheaderArray, char *body, char **data, int *bytesRead);
+
+/* API ID for http_apib_get_interface */
+
+#define HTTP_v1_0_GUID "811c5ea2-fef4-4f1c-9ab4-fcf746cd6efc"
+
+/* API */
+
+/* the api broker reserves api[0] for its use */
+
+#define http_init(api) \
+ ((api_http_init*)(api))[1](Slapi_ComponentId *plugin_id)
+
+#define http_get_text(api, url, data, bytesRead) \
+ ((api_http_get_text*)(api))[2]( url, data, bytesRead)
+
+#define http_get_binary(api, url, data, bytesRead) \
+ ((api_http_get_binary*)(api))[3](url, data, bytesRead)
+
+#define http_get_redirected_uri(api, url, data, bytesRead) \
+ ((api_http_get_redirected_uri*)(api))[4](url, data, bytesRead)
+
+#define http_shutdown(api) \
+ ((api_http_shutdown*)(api))[5]()
+
+#define http_post(api, url, httpheaderArray, body, data, bytesRead) \
+ ((api_http_post*)(api))[6](url, httpheaderArray, body, data, bytesRead)
+
+#endif /*_HTTP_CLIENT_H_*/
diff --git a/ldap/servers/plugins/http/http_impl.c b/ldap/servers/plugins/http/http_impl.c
new file mode 100644
index 00000000..bad8315c
--- /dev/null
+++ b/ldap/servers/plugins/http/http_impl.c
@@ -0,0 +1,1479 @@
+/**
+ * PROPRIETARY/CONFIDENTIAL. Use of this product is subject to
+ * license terms. Copyright 2001 Sun Microsystems, Inc.
+ * Some preexisting portions Copyright 2001 Netscape Communications Corp.
+ * All rights reserved.
+ */
+/**
+ * Implementation of a Simple HTTP Client
+ */
+#include <stdio.h>
+#include <string.h>
+
+#include "nspr.h"
+#include "nss.h"
+#include "pk11func.h"
+#include "ssl.h"
+#include "prprf.h"
+#include "plstr.h"
+#include "slapi-plugin.h"
+#include "http_client.h"
+#include "secerr.h"
+#include "sslerr.h"
+#include "slapi-private.h"
+#include "slapi-plugin-compat4.h"
+/* get file mode flags for unix */
+#ifndef _WIN32
+#include <sys/stat.h>
+#endif
+
+/*** from proto-slap.h ***/
+
+int slapd_log_error_proc( char *subsystem, char *fmt, ... );
+char *config_get_instancedir();
+
+/*** from ldaplog.h ***/
+
+/* edited ldaplog.h for LDAPDebug()*/
+#ifndef _LDAPLOG_H
+#define _LDAPLOG_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifdef BUILD_STANDALONE
+#define slapi_log_error(a,b,c,d) printf((c),(d))
+#define stricmp strcasecmp
+#endif
+
+#define LDAP_DEBUG_TRACE 0x00001 /* 1 */
+#define LDAP_DEBUG_ANY 0x04000 /* 16384 */
+#define LDAP_DEBUG_PLUGIN 0x10000 /* 65536 */
+
+/* debugging stuff */
+# ifdef _WIN32
+ extern int *module_ldap_debug;
+# define LDAPDebug( level, fmt, arg1, arg2, arg3 ) \
+ { \
+ if ( *module_ldap_debug & level ) { \
+ slapd_log_error_proc( NULL, fmt, arg1, arg2, arg3 ); \
+ } \
+ }
+# else /* _WIN32 */
+ extern int slapd_ldap_debug;
+# define LDAPDebug( level, fmt, arg1, arg2, arg3 ) \
+ { \
+ if ( slapd_ldap_debug & level ) { \
+ slapd_log_error_proc( NULL, fmt, arg1, arg2, arg3 ); \
+ } \
+ }
+# endif /* Win32 */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _LDAP_H */
+
+#define HTTP_PLUGIN_SUBSYSTEM "http-client-plugin" /* used for logging */
+
+#define HTTP_IMPL_SUCCESS 0
+#define HTTP_IMPL_FAILURE -1
+
+#define HTTP_REQ_TYPE_GET 1
+#define HTTP_REQ_TYPE_REDIRECT 2
+#define HTTP_REQ_TYPE_POST 3
+
+#define HTTP_GET "GET"
+#define HTTP_POST "POST"
+#define HTTP_PROTOCOL "HTTP/1.0"
+#define HTTP_CONTENT_LENGTH "Content-length:"
+#define HTTP_CONTENT_TYPE_URL_ENCODED "Content-type: application/x-www-form-urlencoded"
+#define HTTP_GET_STD_LEN 18
+#define HTTP_POST_STD_LEN 85
+#define HTTP_DEFAULT_BUFFER_SIZE 4096
+#define HTTP_RESPONSE_REDIRECT (retcode == 302 || retcode == 301)
+
+/**
+ * Error strings used for logging error messages
+ */
+#define HTTP_ERROR_BAD_URL " Badly formatted URL"
+#define HTTP_ERROR_NET_ADDR " NetAddr initialization failed"
+#define HTTP_ERROR_SOCKET_CREATE " Creation of socket failed"
+#define HTTP_ERROR_SSLSOCKET_CREATE " Creation of SSL socket failed"
+#define HTTP_ERROR_CONNECT_FAILED " Couldn't connect to remote host"
+#define HTTP_ERROR_SEND_REQ " Send request failed"
+#define HTTP_ERROR_BAD_RESPONSE " Invalid response from remote host"
+
+#define HTTP_PLUGIN_DN "cn=HTTP Client,cn=plugins,cn=config"
+#define CONFIG_DN "cn=config"
+#define ATTR_CONNECTION_TIME_OUT "nsHTTPConnectionTimeOut"
+#define ATTR_READ_TIME_OUT "nsHTTPReadTimeOut"
+#define ATTR_RETRY_COUNT "nsHTTPRetryCount"
+#define ATTR_DS_SECURITY "nsslapd-security"
+#define ATTR_INSTANCE_PATH "nsslapd-errorlog"
+
+/*static Slapi_ComponentId *plugin_id = NULL;*/
+
+typedef struct {
+ int retryCount;
+ int connectionTimeOut;
+ int readTimeOut;
+ int nssInitialized;
+ char *DS_sslOn;
+} httpPluginConfig;
+
+httpPluginConfig *httpConfig;
+
+/**
+ * Public functions
+ */
+int http_impl_init(Slapi_ComponentId *plugin_id);
+int http_impl_get_text(char *url, char **data, int *bytesRead);
+int http_impl_get_binary(char *url, char **data, int *bytesRead);
+int http_impl_get_redirected_uri(char *url, char **data, int *bytesRead);
+int http_impl_post(char *url, httpheader **httpheaderArray, char *body, char **data, int *bytesRead);
+void http_impl_shutdown();
+
+/**
+ * Http handling functions
+ */
+static int doRequest(const char *url, httpheader **httpheaderArray, char *body, char **buf, int *bytesRead, int reqType);
+static int doRequestRetry(const char *url, httpheader **httpheaderArray, char *body, char **buf, int *bytesRead, int reqType);
+static void setTCPNoDelay(PRFileDesc* fd);
+static PRStatus sendGetReq(PRFileDesc *fd, const char *path);
+static PRStatus sendPostReq(PRFileDesc *fd, const char *path, httpheader **httpheaderArray, char *body);
+static PRStatus processResponse(PRFileDesc *fd, char **resBUF, int *bytesRead, int reqType);
+static PRStatus getChar(PRFileDesc *fd, char *buf);
+static PRInt32 http_read(PRFileDesc *fd, char *buf, int size);
+static PRStatus getBody(PRFileDesc *fd, char **buf, int *actualBytesRead);
+static PRBool isWhiteSpace(char ch);
+static PRStatus sendFullData( PRFileDesc *fd, char *buf, int timeOut);
+
+/**
+ * Helper functions to parse URL
+ */
+static PRStatus parseURI(const char *url, char **host, PRInt32 *port, char **path, int *sslOn);
+static void toLowerCase(char* str);
+static PRStatus parseAtPort(const char* url, PRInt32 *port, char **path);
+static PRStatus parseAtPath(const char *url, char **path);
+static PRInt32 getPort(const char* src);
+static PRBool isAsciiSpace(char aChar);
+static PRBool isAsciiDigit(char aChar);
+static char * isHttpReq(const char *url, int *sslOn);
+
+/*To get config from entry*/
+static int readConfigLDAPurl(Slapi_ComponentId *plugin_id, char *plugindn);
+static int parseHTTPConfigEntry(Slapi_Entry *e);
+static int parseConfigEntry(Slapi_Entry *e);
+
+static int nssReinitializationRequired();
+
+/*SSL functions */
+PRFileDesc* setupSSLSocket(PRFileDesc* fd);
+
+/*SSL callback functions */
+SECStatus badCertHandler(void *arg, PRFileDesc *socket);
+SECStatus authCertificate(void *arg, PRFileDesc *socket, PRBool checksig, PRBool isServer);
+SECStatus getClientAuthData(void *arg, PRFileDesc *socket,struct CERTDistNamesStr *caNames, struct CERTCertificateStr **pRetCert, struct SECKEYPrivateKeyStr **pRetKey);
+SECStatus handshakeCallback(PRFileDesc *socket, void *arg);
+
+static int doRequestRetry(const char *url, httpheader **httpheaderArray, char *body, char **buf, int *bytesRead, int reqType)
+{
+ int status = HTTP_IMPL_SUCCESS;
+ int retrycnt = 0;
+ int i = 1;
+
+ retrycnt = httpConfig->retryCount;
+
+ if (retrycnt == 0) {
+ LDAPDebug( LDAP_DEBUG_PLUGIN, "doRequestRetry: Retry Count cannot be read. Setting to default value of 3 \n", 0,0,0);
+ retrycnt = 3;
+ }
+ status = doRequest(url, httpheaderArray, body, buf, bytesRead, reqType);
+ if (status != HTTP_IMPL_SUCCESS) {
+ LDAPDebug( LDAP_DEBUG_PLUGIN, "doRequestRetry: Failed to perform http request \n", 0,0,0);
+ while (retrycnt > 0) {
+ LDAPDebug( LDAP_DEBUG_PLUGIN, "doRequestRetry: Retrying http request %d.\n", i,0,0);
+ status = doRequest(url, httpheaderArray, body, buf, bytesRead, reqType);
+ if (status == HTTP_IMPL_SUCCESS) {
+ break;
+ }
+ retrycnt--;
+ i++;
+ }
+ if (status != HTTP_IMPL_SUCCESS) {
+ LDAPDebug( LDAP_DEBUG_ANY, "doRequestRetry: Failed to perform http request after %d attempts.\n", i,0,0);
+ LDAPDebug( LDAP_DEBUG_ANY, "doRequestRetry: Verify plugin URI configuration and contact Directory Administrator.\n",0,0,0);
+ }
+
+ }
+ return status;
+}
+
+static int doRequest(const char *url, httpheader **httpheaderArray, char *body, char **buf, int *bytesRead, int reqType)
+{
+ PRStatus status = PR_SUCCESS;
+
+ char *host = NULL;
+ char *path = NULL;
+ char *val = NULL;
+ char *defaultprefix = NULL;
+ PRFileDesc *fd = NULL;
+ PRNetAddr addr;
+ PRInt32 port;
+ PRInt32 errcode = 0;
+ PRInt32 http_connection_time_out = 0;
+ PRInt32 sslOn;
+ PRInt32 nssStatus;
+ PRUint32 nssFlags = 0;
+ char certDir[1024];
+ char certPref[1024];
+ char keyPref[1024];
+
+ LDAPDebug( LDAP_DEBUG_PLUGIN, "--> doRequest -- BEGIN\n",0,0,0);
+
+ LDAPDebug( LDAP_DEBUG_PLUGIN, "----------> url=[%s] \n",url,0,0);
+
+ /* Parse the URL and initialize the host, port, path */
+ if (parseURI(url, &host, &port, &path, &sslOn) == PR_FAILURE) {
+ slapi_log_error( SLAPI_LOG_FATAL, HTTP_PLUGIN_SUBSYSTEM,
+ "doRequest: %s \n", HTTP_ERROR_BAD_URL);
+ status = PR_FAILURE;
+ goto bail;
+ }
+
+ LDAPDebug( LDAP_DEBUG_PLUGIN, "----------> host=[%s] port[%d] path[%s] \n",host,port,path);
+
+ /* Initialize the Net Addr */
+ if (PR_StringToNetAddr(host, &addr) == PR_FAILURE) {
+ char buf[PR_NETDB_BUF_SIZE];
+ PRHostEnt ent;
+
+ status = PR_GetIPNodeByName(host, PR_AF_INET, PR_AI_DEFAULT, buf, sizeof(buf), &ent);
+ if (status == PR_SUCCESS) {
+ PR_EnumerateHostEnt(0, &ent, (PRUint16)port, &addr);
+ } else {
+ slapi_log_error( SLAPI_LOG_FATAL, HTTP_PLUGIN_SUBSYSTEM,
+ "doRequest: %s\n", HTTP_ERROR_NET_ADDR);
+ status = HTTP_CLIENT_ERROR_NET_ADDR;
+ goto bail;
+ }
+ } else {
+ addr.inet.port = (PRUint16)port;
+ }
+
+ LDAPDebug( LDAP_DEBUG_PLUGIN, "----------> Successfully created NetAddr \n",0,0,0);
+
+ /* open a TCP connection to the server */
+ fd = PR_NewTCPSocket();
+ if (!fd) {
+ slapi_log_error( SLAPI_LOG_FATAL, HTTP_PLUGIN_SUBSYSTEM,
+ "doRequest: %s\n", HTTP_ERROR_SOCKET_CREATE);
+ status = HTTP_CLIENT_ERROR_SOCKET_CREATE;
+ goto bail;
+ }
+
+ LDAPDebug( LDAP_DEBUG_PLUGIN, "----------> Successfully created New TCP Socket \n",0,0,0);
+
+ /* immediately send the response */
+ setTCPNoDelay(fd);
+
+ if (sslOn) {
+
+ /* Have to reinitialize NSS is the DS security is set to off.
+ This is because the HTTPS required the cert dbs to be created.
+ The default prefixes are used as per DS norm */
+
+ if (PL_strcasecmp(httpConfig->DS_sslOn, "off") == 0) {
+ if (!httpConfig->nssInitialized) {
+ if (nssReinitializationRequired())
+ {
+ NSS_Shutdown();
+ nssFlags &= (~NSS_INIT_READONLY);
+ val = config_get_instancedir();
+ strcpy(certDir, val);
+ defaultprefix = strrchr(certDir, '/');
+ if (!defaultprefix)
+ defaultprefix = strrchr(certDir, '\\');
+ if (!defaultprefix) /* still could not find it . . . */
+ goto bail; /* . . . can't do anything */
+ defaultprefix++;
+ sprintf(certPref, "%s-",defaultprefix);
+ strcpy(keyPref, certPref);
+ *defaultprefix= '\0';
+ sprintf(certDir, "%salias", certDir);
+ nssStatus = NSS_Initialize(certDir, certPref, keyPref, "secmod.db", nssFlags);
+ slapi_ch_free((void **)&val);
+
+ if (nssStatus != 0) {
+ slapi_log_error(SLAPI_LOG_FATAL, HTTP_PLUGIN_SUBSYSTEM,
+ "doRequest: Unable to initialize NSS Cert/Key Database\n");
+ status = HTTP_CLIENT_ERROR_NSS_INITIALIZE;
+ goto bail;
+ }
+ }
+ httpConfig->nssInitialized = 1;
+ }
+ }
+
+ NSS_SetDomesticPolicy();
+
+ fd = setupSSLSocket(fd);
+ if (fd == NULL) {
+ slapi_log_error( SLAPI_LOG_FATAL, HTTP_PLUGIN_SUBSYSTEM,
+ "doRequest: %s\n", HTTP_ERROR_SSLSOCKET_CREATE);
+ status = HTTP_CLIENT_ERROR_SSLSOCKET_CREATE;
+ goto bail;
+ }
+
+ if (SSL_SetURL(fd, host) != 0) {
+ errcode = PR_GetError();
+ slapi_log_error( SLAPI_LOG_FATAL, HTTP_PLUGIN_SUBSYSTEM,
+ "doRequest: SSL_SetURL -> NSPR Error code (%d) \n", errcode);
+ status = HTTP_CLIENT_ERROR_SSLSOCKET_CREATE;
+ goto bail;
+ }
+
+ }
+
+ http_connection_time_out = httpConfig->connectionTimeOut;
+ /* connect to the host */
+ if (PR_Connect(fd, &addr, PR_MillisecondsToInterval(http_connection_time_out)) == PR_FAILURE) {
+ errcode = PR_GetError();
+ slapi_log_error( SLAPI_LOG_FATAL, HTTP_PLUGIN_SUBSYSTEM,
+ "doRequest: %s (%s:%d) -> NSPR Error code (%d)\n",
+ HTTP_ERROR_CONNECT_FAILED, host, addr.inet.port, errcode);
+ status = HTTP_CLIENT_ERROR_CONNECT_FAILED;
+ goto bail;
+ }
+
+ LDAPDebug( LDAP_DEBUG_PLUGIN, "----------> Successfully connected to host [%s] \n",host,0,0);
+
+ /* send the request to the server */
+ if (reqType == HTTP_REQ_TYPE_POST) {
+ if (sendPostReq(fd, path, httpheaderArray, body) == PR_FAILURE) {
+ slapi_log_error( SLAPI_LOG_FATAL, HTTP_PLUGIN_SUBSYSTEM,
+ "doRequest-sendPostReq: %s (%s)\n", HTTP_ERROR_SEND_REQ, path);
+ status = HTTP_CLIENT_ERROR_SEND_REQ;
+ goto bail;
+ }
+ }
+ else {
+ if (sendGetReq(fd, path) == PR_FAILURE) {
+ slapi_log_error( SLAPI_LOG_FATAL, HTTP_PLUGIN_SUBSYSTEM,
+ "doRequest-sendGetReq: %s (%s)\n", HTTP_ERROR_SEND_REQ, path);
+ status = HTTP_CLIENT_ERROR_SEND_REQ;
+ goto bail;
+ }
+ }
+
+ LDAPDebug( LDAP_DEBUG_PLUGIN, "----------> Successfully sent the request [%s] \n",path,0,0);
+
+ /* read the response */
+ if (processResponse(fd, buf, bytesRead, reqType) == PR_FAILURE) {
+ slapi_log_error( SLAPI_LOG_FATAL, HTTP_PLUGIN_SUBSYSTEM,
+ "doRequest: %s (%s)\n", HTTP_ERROR_BAD_RESPONSE, url);
+ status = HTTP_CLIENT_ERROR_BAD_RESPONSE;
+ goto bail;
+ }
+ LDAPDebug( LDAP_DEBUG_PLUGIN, "----------> Successfully read the response\n",0,0,0);
+bail:
+ if (host) {
+ PR_Free(host);
+ }
+ if (path) {
+ PR_Free(path);
+ }
+ if (fd) {
+ PR_Close(fd);
+ fd = NULL;
+ }
+ LDAPDebug( LDAP_DEBUG_PLUGIN, "<-- doRequest -- END\n",0,0,0);
+ return status;
+}
+
+static PRStatus processResponse(PRFileDesc *fd, char **resBUF, int *bytesRead, int reqType)
+{
+ PRStatus status = PR_SUCCESS;
+ char *location = NULL;
+ char *protocol = NULL;
+ char *statusNum = NULL;
+ char *statusString = NULL;
+ char *headers = NULL;
+
+ char tmp[HTTP_DEFAULT_BUFFER_SIZE];
+ int pos=0;
+ char ch;
+ int index;
+ int retcode;
+
+ PRBool doneParsing = PR_FALSE;
+ PRBool isRedirect = PR_FALSE;
+ char name[HTTP_DEFAULT_BUFFER_SIZE];
+ char value[HTTP_DEFAULT_BUFFER_SIZE];
+ PRBool atEOL = PR_FALSE;
+ PRBool inName = PR_TRUE;
+
+ /* PKBxxx: If we are getting a redirect and the response is more the
+ * the HTTP_DEFAULT_BUFFER_SIZE, it will cause the server to crash. A 4k
+ * buffer should be good enough.
+ */
+ LDAPDebug( LDAP_DEBUG_PLUGIN, "--> processResponse -- BEGIN\n",0,0,0);
+
+ headers = (char *)PR_Calloc(1, 4 * HTTP_DEFAULT_BUFFER_SIZE);
+ /* Get protocol string */
+ index = 0;
+ while (1) {
+ status = getChar(fd, headers+pos);
+ if (status == PR_FAILURE) {
+ /* Error : */
+ goto bail;
+ }
+ ch = (char)headers[pos];
+ pos++;
+ if (!isWhiteSpace(ch)) {
+ tmp[index++] = ch;
+ } else {
+ break;
+ }
+ }
+ tmp[index] = '\0';
+ protocol = PL_strdup(tmp);
+
+ LDAPDebug( LDAP_DEBUG_PLUGIN, "----------> protocol=[%s] \n",protocol,0,0);
+
+ /* Get status num */
+ index = 0;
+ while (1) {
+ status = getChar(fd, headers+pos);
+ if (status == PR_FAILURE) {
+ /* Error : */
+ goto bail;
+ }
+ ch = (char)headers[pos];
+ pos++;
+ if (!isWhiteSpace(ch)) {
+ tmp[index++] = ch;
+ } else {
+ break;
+ }
+ }
+ tmp[index] = '\0';
+ statusNum = PL_strdup(tmp);
+ retcode=atoi(tmp);
+
+ LDAPDebug( LDAP_DEBUG_PLUGIN, "----------> statusNum=[%s] \n",statusNum,0,0);
+
+ if (HTTP_RESPONSE_REDIRECT && (reqType == HTTP_REQ_TYPE_REDIRECT)) {
+ isRedirect = PR_TRUE;
+ }
+ /* Get status string */
+ if (ch != '\r')
+ {
+ index = 0;
+ while (ch != '\r') {
+ status = getChar(fd, headers+pos);
+ if (status == PR_FAILURE) {
+ /* Error : */
+ goto bail;
+ }
+ ch = (char)headers[pos];
+ pos++;
+ tmp[index++] = ch;
+ }
+ tmp[index] = '\0';
+ statusString = PL_strdup(tmp);
+ LDAPDebug( LDAP_DEBUG_PLUGIN, "----------> statusString [%s] \n",statusString,0,0);
+ }
+
+ /**
+ * Skip CRLF
+ */
+ status = getChar(fd, headers+pos);
+ if (status == PR_FAILURE) {
+ /* Error : */
+ goto bail;
+ }
+ ch = (char)headers[pos];
+ pos++;
+
+ /**
+ * loop over response headers
+ */
+ index = 0;
+ while (!doneParsing) {
+ status = getChar(fd, headers+pos);
+ if (status == PR_FAILURE) {
+ /* Error : */
+ goto bail;
+ }
+ ch = (char)headers[pos];
+ pos++;
+ switch(ch)
+ {
+ case ':':
+ if (inName) {
+ name[index] = '\0';
+ index = 0;
+ inName = PR_FALSE;
+
+ /* skip whitespace */
+ ch = ' ';
+
+ /* status = getChar(fd, headers+pos);
+ if (status == PR_FAILURE) {
+ goto bail;
+ }
+ ch = (char)headers[pos];
+ pos++; */
+
+ while(isWhiteSpace(ch)) {
+ status = getChar(fd, headers+pos);
+ if (status == PR_FAILURE) {
+ /* Error : */
+ goto bail;
+ }
+ ch = (char)headers[pos];
+ pos++;
+ }
+ value[index++] = ch;
+ } else {
+ value[index++] = ch;
+ }
+ break;
+ case '\r':
+ if (inName && !atEOL) {
+ return PR_FALSE;
+ }
+ break;
+ case '\n':
+ if (atEOL) {
+ doneParsing = PR_TRUE;
+ break;
+ }
+ if (inName) {
+ return PR_FALSE;
+ }
+ value[index] = '\0';
+ index = 0;
+ inName = PR_TRUE;
+ LDAPDebug( LDAP_DEBUG_PLUGIN, "----------> name=[%s] value=[%s]\n",name,value,0);
+ if (isRedirect && !PL_strcasecmp(name,"location")) {
+ location = PL_strdup(value);
+ }
+ atEOL = PR_TRUE;
+ break;
+ default:
+ atEOL = PR_FALSE;
+ if (inName) {
+ name[index++] = ch;
+ } else {
+ value[index++] = ch;
+ }
+ break;
+ }
+ }
+
+ if (!isRedirect) {
+ getBody(fd, resBUF, bytesRead);
+ } else {
+ *resBUF = PL_strdup(location);
+ *bytesRead = strlen(location);
+ }
+ LDAPDebug( LDAP_DEBUG_PLUGIN, "----------> Response Buffer=[%s] bytesRead=[%d] \n",*resBUF,*bytesRead,0);
+
+bail:
+
+ if (headers) {
+ PR_Free(headers);
+ }
+ if (protocol) {
+ PL_strfree(protocol);
+ }
+ if (statusNum) {
+ PL_strfree(statusNum);
+ }
+ if (statusString) {
+ PL_strfree(statusString);
+ }
+ if (location) {
+ PL_strfree(location);
+ }
+
+ LDAPDebug( LDAP_DEBUG_PLUGIN, "<-- processResponse -- END\n",0,0,0);
+ return status;
+}
+
+static int nssReinitializationRequired()
+{
+ int nssReinitializationRequired = 0;
+ int err = 0;
+ int str_len = 0;
+ float version = 0;
+ const float DSVERSION = 6.1;
+ char *str = NULL;
+ char *value = NULL;
+ char *ver_value = NULL;
+ Slapi_Entry **entry = NULL;
+ Slapi_PBlock *resultpb= NULL;
+
+ resultpb= slapi_search_internal( "", LDAP_SCOPE_BASE, "objectclass=*", NULL, NULL, 0);
+ slapi_pblock_get( resultpb, SLAPI_PLUGIN_INTOP_SEARCH_ENTRIES, &entry );
+ slapi_pblock_get( resultpb, SLAPI_PLUGIN_INTOP_RESULT, &err);
+ if ( err == LDAP_SUCCESS && entry!=NULL && entry[0]!=NULL)
+ {
+ value = slapi_entry_attr_get_charptr(entry[0], "vendorVersion");
+ if (value == NULL || strncmp(value, "Netscape", strlen("Netscape")))
+ {
+ slapi_log_error( SLAPI_LOG_PLUGIN, HTTP_PLUGIN_SUBSYSTEM,
+ "nssReinitializationRequired: vendor is not Netscape \n");
+ slapi_log_error( SLAPI_LOG_PLUGIN, HTTP_PLUGIN_SUBSYSTEM,
+ "or version is earlier than 6.0\n", value);
+ nssReinitializationRequired = 1;
+ slapi_free_search_results_internal(resultpb);
+ slapi_pblock_destroy(resultpb);
+ slapi_ch_free((void **)&value);
+ return nssReinitializationRequired;
+ }
+
+ if ( (str = strstr(value,"/")) != NULL )
+ {
+ str++;
+ version = atof(str);
+ slapi_log_error( SLAPI_LOG_PLUGIN, HTTP_PLUGIN_SUBSYSTEM,
+ "nssReinitializationRequired: version is %f. \n", version);
+ }
+
+
+ if (str == NULL || version < DSVERSION)
+ {
+ nssReinitializationRequired = 1;
+ }
+ slapi_ch_free((void **)&value);
+
+ }
+ slapi_free_search_results_internal(resultpb);
+ slapi_pblock_destroy(resultpb);
+ return nssReinitializationRequired;
+
+}
+
+static PRStatus sendGetReq(PRFileDesc *fd, const char *path)
+{
+ PRStatus status = PR_SUCCESS;
+ char *reqBUF = NULL;
+ PRInt32 http_connection_time_out = 0;
+ int buflen = (HTTP_GET_STD_LEN + strlen(path));
+
+ reqBUF = (char *)PR_Calloc(1, buflen);
+
+ strcpy(reqBUF, HTTP_GET);
+ strcat(reqBUF, " ");
+ strcat(reqBUF, path);
+ strcat(reqBUF, " ");
+ strcat(reqBUF, HTTP_PROTOCOL);
+ strcat(reqBUF, "\r\n\r\n\0");
+
+ http_connection_time_out = httpConfig->connectionTimeOut;
+ status = sendFullData( fd, reqBUF, http_connection_time_out);
+
+bail:
+ if (reqBUF) {
+ PR_Free(reqBUF);
+ reqBUF = 0;
+ }
+ return status;
+}
+
+static PRStatus sendFullData( PRFileDesc *fd, char *buf, int timeOut)
+{
+ int dataSent = 0;
+ int bufLen = strlen(buf);
+ int retVal = 0;
+ PRInt32 errcode = 0;
+ while (dataSent < bufLen)
+ {
+ retVal = PR_Send(fd, buf+dataSent, bufLen-dataSent, 0, PR_MillisecondsToInterval(timeOut));
+ if (retVal == -1 )
+ break;
+ dataSent += retVal;
+ }
+ if (dataSent == bufLen )
+ return PR_SUCCESS;
+ else
+ {
+ errcode = PR_GetError();
+ slapi_log_error( SLAPI_LOG_FATAL, HTTP_PLUGIN_SUBSYSTEM,
+ "sendFullData: dataSent=%d bufLen=%d -> NSPR Error code (%d)\n",
+ dataSent, bufLen, errcode);
+ LDAPDebug( LDAP_DEBUG_PLUGIN, "---------->NSPR Error code (%d) \n", errcode,0,0);
+ return PR_FAILURE;
+ }
+}
+
+static PRStatus sendPostReq(PRFileDesc *fd, const char *path, httpheader **httpheaderArray, char *body)
+{
+ PRStatus status = PR_SUCCESS;
+ char body_len_str[20];
+ char *reqBUF = NULL;
+ PRInt32 http_connection_time_out = 0;
+ int i = 0;
+ int body_len, buflen = 0;
+
+ body_len = strlen(body);
+ PR_snprintf(body_len_str, sizeof(body_len_str), "%d", body_len);
+
+ buflen = (HTTP_POST_STD_LEN + strlen(path) + body_len + strlen(body_len_str));
+
+ for (i = 0; httpheaderArray[i] != NULL; i++) {
+
+ if (httpheaderArray[i]->name != NULL)
+ {
+ buflen += strlen(httpheaderArray[i]->name) + 2;
+ if (httpheaderArray[i]->value != NULL)
+ buflen += strlen(httpheaderArray[i]->value) + 2;
+ }
+
+ }
+
+ reqBUF = (char *)PR_Calloc(1, buflen);
+
+ strcpy(reqBUF, HTTP_POST);
+ strcat(reqBUF, " ");
+ strcat(reqBUF, path);
+ strcat(reqBUF, " ");
+ strcat(reqBUF, HTTP_PROTOCOL);
+ strcat(reqBUF, "\r\n");
+ strcat(reqBUF, HTTP_CONTENT_LENGTH);
+ strcat(reqBUF, " ");
+ strcat(reqBUF, body_len_str);
+ strcat(reqBUF, "\r\n");
+ strcat(reqBUF, HTTP_CONTENT_TYPE_URL_ENCODED);
+ strcat(reqBUF, "\r\n");
+
+ for (i = 0; httpheaderArray[i] != NULL; i++) {
+
+ if (httpheaderArray[i]->name != NULL)
+ strcat(reqBUF, httpheaderArray[i]->name);
+ strcat(reqBUF, ": ");
+ if (httpheaderArray[i]->value != NULL)
+ strcat(reqBUF, httpheaderArray[i]->value);
+ strcat(reqBUF, "\r\n");
+
+ }
+
+ strcat(reqBUF, "\r\n");
+ strcat(reqBUF, body);
+ strcat(reqBUF, "\0");
+
+ LDAPDebug( LDAP_DEBUG_PLUGIN, "---------->reqBUF is %s \n",reqBUF,0,0);
+ http_connection_time_out = httpConfig->connectionTimeOut;
+
+ status = sendFullData( fd, reqBUF, http_connection_time_out);
+
+bail:
+ if (reqBUF) {
+ PR_Free(reqBUF);
+ reqBUF = 0;
+ }
+ return status;
+}
+
+
+static PRStatus getChar(PRFileDesc *fd, char *buf)
+{
+ PRInt32 bytesRead = http_read(fd, buf, 1);
+ if (bytesRead <=0) {
+ PRInt32 errcode = PR_GetError();
+ slapi_log_error( SLAPI_LOG_FATAL, HTTP_PLUGIN_SUBSYSTEM,
+ "getChar: NSPR Error code (%d)\n", errcode);
+ return PR_FAILURE;
+ }
+ return PR_SUCCESS;
+}
+
+static PRStatus getBody(PRFileDesc *fd, char **buf, int *actualBytesRead)
+{
+ int totalBytesRead = 0;
+ int size = 4 * HTTP_DEFAULT_BUFFER_SIZE;
+ int bytesRead = size;
+ char *data = (char *) PR_Calloc(1, size);
+ while (bytesRead == size) {
+ bytesRead = http_read(fd, (data+totalBytesRead), size);
+ if (bytesRead <= 0) {
+ /* Read error */
+ return PR_FAILURE;
+ }
+ if (bytesRead == size) {
+ /* more data to be read so increase the buffer */
+ size = size * 2 ;
+ data = (char *) PR_Realloc(data, size);
+ }
+ totalBytesRead += bytesRead;
+ }
+ *buf = data;
+ *actualBytesRead = totalBytesRead;
+
+ return PR_SUCCESS;
+}
+
+static PRInt32 http_read(PRFileDesc *fd, char *buf, int size)
+{
+ PRInt32 http_read_time_out = 0;
+ http_read_time_out = httpConfig->readTimeOut;
+ return PR_Recv(fd, buf, size, 0, PR_MillisecondsToInterval(http_read_time_out));
+}
+
+static PRBool isWhiteSpace(char ch)
+{
+ PRBool b = PR_FALSE;
+ if (ch == ' ') {
+ b = PR_TRUE;
+ }
+ return b;
+}
+
+static PRStatus parseURI(const char *urlstr, char **host, PRInt32 *port, char **path, int *sslOn)
+{
+ PRStatus status = PR_SUCCESS;
+ char *brk;
+ int len;
+ static const char delimiters[] = ":/?#";
+ char *url = isHttpReq(urlstr, sslOn);
+
+ if (*sslOn) {
+ *port = 443;
+ }
+ else {
+ *port = 80;
+ }
+ if (url == NULL) {
+ /* Error : */
+ status = PR_FAILURE;
+ goto bail;
+ }
+ len = PL_strlen(url);
+ /* Currently we do not support Ipv6 addresses */
+ brk = PL_strpbrk(url, delimiters);
+ if (!brk) {
+ *host = PL_strndup(url, len);
+ toLowerCase(*host);
+ goto bail;
+ }
+ switch (*brk)
+ {
+ case '/' :
+ case '?' :
+ case '#' :
+ /* Get the Host, the rest is Path */
+ *host = PL_strndup(url, (brk - url));
+ toLowerCase(*host);
+ status = parseAtPath(brk, path);
+ break;
+ case ':' :
+ /* Get the Host and process port, path */
+ *host = PL_strndup(url, (brk - url));
+ toLowerCase(*host);
+ status = parseAtPort(brk+1, port, path);
+ break;
+ default:
+ /* Error : HTTP_BAD_URL */
+ break;
+ }
+
+bail:
+ if (url) {
+ PR_Free(url);
+ }
+ return status;
+}
+
+static PRStatus parseAtPort(const char* url, PRInt32 *port, char **path)
+{
+ PRStatus status = PR_SUCCESS;
+ static const char delimiters[] = "/?#";
+ char* brk = PL_strpbrk(url, delimiters);
+ if (!brk) /* everything is a Port */
+ {
+ *port = getPort(url);
+ if (*port <= 0) {
+ /* Error : HTTP_BAD_URL */
+ return PR_FAILURE;
+ } else {
+ return status;
+ }
+ }
+
+ switch (*brk)
+ {
+ case '/' :
+ case '?' :
+ case '#' :
+ /* Get the Port, the rest is Path */
+ *port = getPort(url);
+ if (*port <= 0) {
+ /* Error : HTTP_BAD_URL */
+ return PR_FAILURE;
+ }
+ status = parseAtPath(brk, path);
+ break;
+ default:
+ /* Error : HTTP_BAD_URL */
+ break;
+ }
+ return status;
+}
+
+static PRStatus parseAtPath(const char *url, char **path)
+{
+ PRStatus status = PR_SUCCESS;
+ char *dir = "%s%s";
+ *path = (char *)PR_Calloc(1, (strlen(dir) + 1024));
+
+ /* Just write the path and check for a starting / */
+ if ('/' != *url) {
+ PR_sscanf(*path, dir, "/", url);
+ } else {
+ strcpy(*path, url);
+ }
+ if (!*path) {
+ /* Error : HTTP_BAD_URL */
+ status = PR_FAILURE;
+ }
+ return status;
+}
+
+static void toLowerCase(char* str)
+{
+ if (str) {
+ char* lstr = str;
+ PRInt8 shift = 'a' - 'A';
+ for(; (*lstr != '\0'); ++lstr) {
+ if ((*(lstr) <= 'Z') && (*(lstr) >= 'A')) {
+ *(lstr) = *(lstr) + shift;
+ }
+ }
+ }
+}
+
+static PRInt32 getPort(const char* src)
+{
+ /* search for digits up to a slash or the string ends */
+ const char* port = src;
+ PRInt32 returnValue = -1;
+ char c;
+
+ /* skip leading white space */
+ while (isAsciiSpace(*port))
+ port++;
+
+ while ((c = *port++) != '\0') {
+ /* stop if slash or ? or # reached */
+ if (c == '/' || c == '?' || c == '#')
+ break;
+ else if (!isAsciiDigit(c))
+ return returnValue;
+ }
+ return (0 < PR_sscanf(src, "%d", &returnValue)) ? returnValue : -1;
+}
+
+
+static PRBool isAsciiSpace(char aChar)
+{
+ if ((aChar == ' ') || (aChar == '\r') || (aChar == '\n') || (aChar == '\t')) {
+ return PR_TRUE;
+ }
+ return PR_FALSE;
+}
+
+static PRBool isAsciiDigit(char aChar)
+{
+ if ((aChar >= '0') && (aChar <= '9')) {
+ return PR_TRUE;
+ }
+ return PR_FALSE;
+}
+
+static void setTCPNoDelay(PRFileDesc* fd)
+{
+ PRStatus status = PR_SUCCESS;
+ PRSocketOptionData opt;
+
+ opt.option = PR_SockOpt_NoDelay;
+ opt.value.no_delay = PR_FALSE;
+
+ status = PR_GetSocketOption(fd, &opt);
+ if (status == PR_FAILURE) {
+ return;
+ }
+
+ opt.option = PR_SockOpt_NoDelay;
+ opt.value.no_delay = PR_TRUE;
+ status = PR_SetSocketOption(fd, &opt);
+ if (status == PR_FAILURE) {
+ return;
+ }
+ return;
+}
+
+static char * isHttpReq(const char *url, int *sslOn)
+{
+ static const char http_protopol_header[] = "http://";
+ static const char https_protopol_header[] = "https://";
+ char *newstr = NULL;
+ /* skip leading white space */
+ while (isAsciiSpace(*url))
+ url++;
+
+ if (strncmp(url, http_protopol_header, strlen(http_protopol_header)) == 0) {
+ newstr = (char *)PR_Calloc(1, (strlen(url)-strlen(http_protopol_header) + 1));
+ strcpy(newstr, url+7);
+ strcat(newstr,"\0");
+ *sslOn = 0;
+ }
+ else if (strncmp(url, https_protopol_header, strlen(https_protopol_header)) == 0) {
+ newstr = (char *)PR_Calloc(1, (strlen(url)-strlen(https_protopol_header) + 1));
+ strcpy(newstr, url+8);
+ strcat(newstr,"\0");
+ *sslOn = 1;
+ }
+
+ return newstr;
+}
+
+PRFileDesc* setupSSLSocket(PRFileDesc* fd)
+{
+ SECStatus secStatus;
+ PRFileDesc* sslSocket;
+ PRSocketOptionData socketOption;
+ char *certNickname = NULL;
+
+ socketOption.option = PR_SockOpt_Nonblocking;
+ socketOption.value.non_blocking = PR_FALSE;
+ if( PR_SetSocketOption(fd, &socketOption) != 0) {
+ slapi_log_error( SLAPI_LOG_PLUGIN, HTTP_PLUGIN_SUBSYSTEM,
+ "Cannot set socket option NSS \n");
+ return NULL;
+ }
+
+ sslSocket = SSL_ImportFD(NULL, fd);
+ if (!sslSocket) {
+ slapi_log_error( SLAPI_LOG_PLUGIN, HTTP_PLUGIN_SUBSYSTEM,
+ "setupSSLSocket: Cannot import to SSL Socket\n" );
+ goto sslbail;
+ }
+
+ slapi_log_error( SLAPI_LOG_FATAL, HTTP_PLUGIN_SUBSYSTEM,
+ "setupSSLSocket: setupssl socket created\n" );
+
+ secStatus = SSL_OptionSet(sslSocket, SSL_SECURITY, 1);
+ if (SECSuccess != secStatus) {
+ slapi_log_error( SLAPI_LOG_PLUGIN, HTTP_PLUGIN_SUBSYSTEM,
+ "setupSSLSocket: Cannot set SSL_SECURITY option\n");
+ goto sslbail;
+ }
+
+ secStatus = SSL_OptionSet(sslSocket, SSL_HANDSHAKE_AS_CLIENT, 1);
+ if (SECSuccess != secStatus) {
+ slapi_log_error( SLAPI_LOG_PLUGIN, HTTP_PLUGIN_SUBSYSTEM,
+ "setupSSLSocket: CAnnot set SSL_HANDSHAKE_AS_CLIENT option\n");
+ goto sslbail;
+ }
+
+ /* Set SSL callback routines. */
+
+ secStatus = SSL_GetClientAuthDataHook(sslSocket,
+ (SSLGetClientAuthData) getClientAuthData,
+ (void *)certNickname);
+ if (secStatus != SECSuccess) {
+ slapi_log_error( SLAPI_LOG_PLUGIN, HTTP_PLUGIN_SUBSYSTEM,
+ "setupSSLSocket: SSL_GetClientAuthDataHook Failed\n");
+ goto sslbail;
+ }
+
+ secStatus = SSL_AuthCertificateHook(sslSocket,
+ (SSLAuthCertificate) authCertificate,
+ (void *)CERT_GetDefaultCertDB());
+ if (secStatus != SECSuccess) {
+ slapi_log_error( SLAPI_LOG_PLUGIN, HTTP_PLUGIN_SUBSYSTEM,
+ "setupSSLSocket: SSL_AuthCertificateHook Failed\n");
+ goto sslbail;
+ }
+
+ secStatus = SSL_BadCertHook(sslSocket,
+ (SSLBadCertHandler) badCertHandler, NULL);
+ if (secStatus != SECSuccess) {
+ slapi_log_error( SLAPI_LOG_PLUGIN, HTTP_PLUGIN_SUBSYSTEM,
+ "setupSSLSocket: SSL_BadCertHook Failed\n");
+ goto sslbail;
+ }
+
+ secStatus = SSL_HandshakeCallback(sslSocket,
+ (SSLHandshakeCallback) handshakeCallback, NULL);
+ if (secStatus != SECSuccess) {
+ slapi_log_error( SLAPI_LOG_PLUGIN, HTTP_PLUGIN_SUBSYSTEM,
+ "setupSSLSocket: SSL_HandshakeCallback Failed\n");
+ goto sslbail;
+ }
+
+ return sslSocket;
+
+sslbail:
+ PR_Close(fd);
+ return NULL;
+}
+
+SECStatus
+ authCertificate(void *arg, PRFileDesc *socket,
+ PRBool checksig, PRBool isServer)
+{
+
+ SECCertUsage certUsage;
+ CERTCertificate * cert;
+ void * pinArg;
+ char * hostName;
+ SECStatus secStatus;
+
+ if (!arg || !socket) {
+ slapi_log_error(SLAPI_LOG_PLUGIN, HTTP_PLUGIN_SUBSYSTEM,
+ " authCertificate: Faulty socket in callback function \n");
+ return SECFailure;
+ }
+
+ /* Define how the cert is being used based upon the isServer flag. */
+
+ certUsage = isServer ? certUsageSSLClient : certUsageSSLServer;
+
+ cert = SSL_PeerCertificate(socket);
+
+ pinArg = SSL_RevealPinArg(socket);
+
+ secStatus = CERT_VerifyCertNow((CERTCertDBHandle *)arg,
+ cert,
+ checksig,
+ certUsage,
+ pinArg);
+
+ /* If this is a server, we're finished. */
+ if (isServer || secStatus != SECSuccess) {
+ return secStatus;
+ }
+
+ hostName = SSL_RevealURL(socket);
+
+ if (hostName && hostName[0]) {
+ secStatus = CERT_VerifyCertName(cert, hostName);
+ } else {
+ PR_SetError(SSL_ERROR_BAD_CERT_DOMAIN, 0);
+ secStatus = SECFailure;
+ }
+
+ if (hostName)
+ PR_Free(hostName);
+
+ return secStatus;
+}
+
+SECStatus
+ badCertHandler(void *arg, PRFileDesc *socket)
+{
+
+ SECStatus secStatus = SECFailure;
+ PRErrorCode err;
+
+ /* log invalid cert here */
+
+ if (!arg) {
+ return secStatus;
+ }
+
+ *(PRErrorCode *)arg = err = PORT_GetError();
+ switch (err) {
+ case SEC_ERROR_INVALID_AVA:
+ case SEC_ERROR_INVALID_TIME:
+ case SEC_ERROR_BAD_SIGNATURE:
+ case SEC_ERROR_EXPIRED_CERTIFICATE:
+ case SEC_ERROR_UNKNOWN_ISSUER:
+ case SEC_ERROR_UNTRUSTED_CERT:
+ case SEC_ERROR_CERT_VALID:
+ case SEC_ERROR_EXPIRED_ISSUER_CERTIFICATE:
+ case SEC_ERROR_CRL_EXPIRED:
+ case SEC_ERROR_CRL_BAD_SIGNATURE:
+ case SEC_ERROR_EXTENSION_VALUE_INVALID:
+ case SEC_ERROR_CA_CERT_INVALID:
+ case SEC_ERROR_CERT_USAGES_INVALID:
+ case SEC_ERROR_UNKNOWN_CRITICAL_EXTENSION:
+ secStatus = SECSuccess;
+ break;
+ default:
+ secStatus = SECFailure;
+ break;
+ }
+
+ slapi_log_error(SLAPI_LOG_PLUGIN, HTTP_PLUGIN_SUBSYSTEM,
+ "Bad certificate: %d\n", err);
+
+ return secStatus;
+}
+
+SECStatus
+ getClientAuthData(void *arg,
+ PRFileDesc *socket,
+ struct CERTDistNamesStr *caNames,
+ struct CERTCertificateStr **pRetCert,
+ struct SECKEYPrivateKeyStr **pRetKey)
+{
+ CERTCertificate * cert;
+ SECKEYPrivateKey * privKey;
+ char * chosenNickName = (char *)arg;
+ void * proto_win = NULL;
+ SECStatus secStatus = SECFailure;
+ proto_win = SSL_RevealPinArg(socket);
+
+ if (chosenNickName) {
+ cert = PK11_FindCertFromNickname(chosenNickName, proto_win);
+ if (cert) {
+ privKey = PK11_FindKeyByAnyCert(cert, proto_win);
+ if (privKey) {
+ secStatus = SECSuccess;
+ } else {
+ CERT_DestroyCertificate(cert);
+ }
+ }
+ } else { /* no nickname given, automatically find the right cert */
+ CERTCertNicknames *names;
+ int i;
+
+ names = CERT_GetCertNicknames(CERT_GetDefaultCertDB(),
+ SEC_CERT_NICKNAMES_USER, proto_win);
+
+ if (names != NULL) {
+ for(i = 0; i < names->numnicknames; i++ ) {
+
+ cert = PK11_FindCertFromNickname(names->nicknames[i],
+ proto_win);
+ if (!cert) {
+ continue;
+ }
+
+ /* Only check unexpired certs */
+ if (CERT_CheckCertValidTimes(cert, PR_Now(), PR_FALSE)
+ != secCertTimeValid ) {
+ CERT_DestroyCertificate(cert);
+ continue;
+ }
+
+ secStatus = NSS_CmpCertChainWCANames(cert, caNames);
+ if (secStatus == SECSuccess) {
+ privKey = PK11_FindKeyByAnyCert(cert, proto_win);
+ if (privKey) {
+ break;
+ }
+ secStatus = SECFailure;
+ break;
+ }
+ CERT_FreeNicknames(names);
+ } /* for loop */
+ }
+ }
+
+ if (secStatus == SECSuccess) {
+ *pRetCert = cert;
+ *pRetKey = privKey;
+ }
+
+ return secStatus;
+}
+
+SECStatus
+ handshakeCallback(PRFileDesc *socket, void *arg)
+{
+ slapi_log_error(SLAPI_LOG_PLUGIN, HTTP_PLUGIN_SUBSYSTEM,
+ "----------> Handshake has completed, ready to send data securely.\n");
+ return SECSuccess;
+}
+
+
+/**
+ * PUBLIC FUNCTIONS IMPLEMENTATION
+ */
+int http_impl_init(Slapi_ComponentId *plugin_id)
+{
+ int status = HTTP_IMPL_SUCCESS;
+ slapi_log_error(SLAPI_LOG_PLUGIN, HTTP_PLUGIN_SUBSYSTEM,
+ "-> http_impl_init \n");
+ httpConfig = NULL;
+
+ httpConfig = (httpPluginConfig *) slapi_ch_calloc(1, sizeof(httpPluginConfig));
+
+ status = readConfigLDAPurl(plugin_id, HTTP_PLUGIN_DN);
+ if (status != 0) {
+ slapi_log_error(SLAPI_LOG_FATAL, HTTP_PLUGIN_SUBSYSTEM,
+ "http_impl_start: Unable to get HTTP config information \n");
+ return HTTP_IMPL_FAILURE;
+ }
+
+ status = readConfigLDAPurl(plugin_id, CONFIG_DN);
+ if (status != 0) {
+ slapi_log_error(SLAPI_LOG_FATAL, HTTP_PLUGIN_SUBSYSTEM,
+ "http_impl_start: Unable to get config information \n");
+ return HTTP_IMPL_FAILURE;
+ }
+
+ slapi_log_error(SLAPI_LOG_PLUGIN, HTTP_PLUGIN_SUBSYSTEM,
+ "<- http_impl_init \n");
+
+ return status;
+
+}
+
+
+int http_impl_get_text(char *url, char **data, int *bytesRead)
+{
+ int status = HTTP_IMPL_SUCCESS;
+ status = doRequestRetry(url, NULL, NULL, data, bytesRead, HTTP_REQ_TYPE_GET);
+ return status;
+}
+
+int http_impl_get_binary(char *url, char **data, int *bytesRead)
+{
+ int status = HTTP_IMPL_SUCCESS;
+ status = doRequestRetry(url, NULL, NULL, data, bytesRead, HTTP_REQ_TYPE_GET);
+ return status;
+}
+
+int http_impl_get_redirected_uri(char *url, char **data, int *bytesRead)
+{
+ int status = HTTP_IMPL_SUCCESS;
+ status = doRequestRetry(url, NULL, NULL, data, bytesRead, HTTP_REQ_TYPE_REDIRECT);
+ return status;
+}
+
+int http_impl_post(char *url, httpheader **httpheaderArray, char *body, char **data, int *bytesRead)
+{
+ int status = HTTP_IMPL_SUCCESS;
+ status = doRequestRetry(url, httpheaderArray, body, data, bytesRead, HTTP_REQ_TYPE_POST);
+ return status;
+}
+
+void http_impl_shutdown()
+{
+ int status = HTTP_IMPL_SUCCESS;
+ /**
+ * Put cleanup code here
+ */
+}
+
+static int readConfigLDAPurl(Slapi_ComponentId *plugin_id, char *plugindn) {
+
+ int rc = LDAP_SUCCESS;
+ Slapi_DN *sdn = NULL;
+ int status = HTTP_IMPL_SUCCESS;
+ Slapi_Entry *entry = NULL;
+
+ sdn = slapi_sdn_new_dn_byref(plugindn);
+ rc = slapi_search_internal_get_entry(sdn, NULL, &entry, plugin_id);
+ slapi_sdn_free(&sdn);
+ if (rc != LDAP_SUCCESS) {
+ slapi_log_error( SLAPI_LOG_PLUGIN, HTTP_PLUGIN_SUBSYSTEM,
+ "readConfigLDAPurl: Could not find entry %s (error %d)\n", plugindn, rc);
+ status = HTTP_IMPL_FAILURE;
+ return status;
+ }
+ if (NULL == entry)
+ {
+ slapi_log_error( SLAPI_LOG_PLUGIN, HTTP_PLUGIN_SUBSYSTEM,
+ "readConfigLDAPurl: No entries found for <%s>\n", plugindn);
+
+ status = HTTP_IMPL_FAILURE;
+ return status;
+ }
+
+ if ((PL_strcasecmp(plugindn, HTTP_PLUGIN_DN) == 0))
+ status = parseHTTPConfigEntry(entry);
+ else
+ status = parseConfigEntry(entry);
+
+ slapi_entry_free(entry);
+ return status;
+
+}
+
+/* Retrieves the plugin configuration info */
+
+/* Retrieves security info as well as the path info required for the SSL
+config dir */
+static int parseConfigEntry(Slapi_Entry *e)
+{
+ char *value = NULL;
+
+ value = slapi_entry_attr_get_charptr(e, ATTR_DS_SECURITY);
+ if (value) {
+ httpConfig->DS_sslOn = value;
+ }
+
+ return HTTP_IMPL_SUCCESS;
+
+}
+
+
+static int parseHTTPConfigEntry(Slapi_Entry *e)
+{
+ int value = 0;
+
+
+ value = slapi_entry_attr_get_int(e, ATTR_RETRY_COUNT);
+ if (value) {
+ httpConfig->retryCount = value;
+ }
+
+ value = slapi_entry_attr_get_int(e, ATTR_CONNECTION_TIME_OUT);
+ if (value) {
+ httpConfig->connectionTimeOut = value;
+ }
+ else {
+ LDAPDebug( LDAP_DEBUG_PLUGIN, "parseHTTPConfigEntry: HTTP Connection Time Out cannot be read. Setting to default value of 5000 ms \n", 0,0,0);
+ httpConfig->connectionTimeOut = 5000;
+ }
+
+
+ value = slapi_entry_attr_get_int(e, ATTR_READ_TIME_OUT);
+ if (value) {
+ httpConfig->readTimeOut = value;
+ }
+ else {
+ LDAPDebug( LDAP_DEBUG_PLUGIN, "parseHTTPConfigEntry: HTTP Read Time Out cannot be read. Setting to default value of 5000 ms \n", 0,0,0);
+ httpConfig->readTimeOut = 5000;
+ }
+
+ httpConfig->nssInitialized = 0;
+
+ return HTTP_IMPL_SUCCESS;
+
+}
+
+/**
+ * Self Testing
+ */
+#ifdef BUILD_STANDALONE
+int main(int argc, char **argv)
+{
+ PRStatus status = PR_SUCCESS;
+ char *buf;
+ int bytes;
+ char *host;
+ PRInt32 port;
+ char *path;
+ if (argc < 2) {
+ printf("URL missing\n");
+ return -1;
+ }
+ PR_Init(PR_USER_THREAD,PR_PRIORITY_NORMAL, 0);
+ doRequest(argv[1], &buf, &bytes, 2);
+ printf( "%s\n", buf );
+ return -1;
+}
+#endif
+
diff --git a/ldap/servers/plugins/http/http_impl.h b/ldap/servers/plugins/http/http_impl.h
new file mode 100644
index 00000000..0bca3ca2
--- /dev/null
+++ b/ldap/servers/plugins/http/http_impl.h
@@ -0,0 +1,25 @@
+/**
+ * PROPRIETARY/CONFIDENTIAL. Use of this product is subject to
+ * license terms. Copyright 2001 Sun Microsystems, Inc.
+ * Some preexisting portions Copyright 2001 Netscape Communications Corp.
+ * All rights reserved.
+ */
+#ifndef HTTP_IMPL_H__
+#define HTTP_IMPL_H__
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+int http_impl_init(Slapi_ComponentId *plugin_id);
+int http_impl_get_text(char *url, char **data, int *bytesRead);
+int http_impl_get_binary(char *url, char **data, int *bytesRead);
+int http_impl_get_redirected_uri(char *url, char **data, int *bytesRead);
+int http_impl_post(char *url, httpheader **httpheaderArray, char *body, char **data, int *bytesRead);
+void http_impl_shutdown();
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/ldap/servers/plugins/passthru/Makefile b/ldap/servers/plugins/passthru/Makefile
new file mode 100644
index 00000000..11540915
--- /dev/null
+++ b/ldap/servers/plugins/passthru/Makefile
@@ -0,0 +1,90 @@
+#
+# BEGIN COPYRIGHT BLOCK
+# Copyright 2001 Sun Microsystems, Inc.
+# Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+# All rights reserved.
+# END COPYRIGHT BLOCK
+#
+#
+# GNU Makefile for Directory Server "Pass Through Authentication" plugin
+#
+#
+
+LDAP_SRC = ../../..
+MCOM_ROOT = ../../../../..
+
+NOSTDCLEAN=true # don't let nsconfig.mk define target clean
+NOSTDSTRIP=true # don't let nsconfig.mk define target strip
+NSPR20=true # probably should be defined somewhere else (not sure where)
+
+OBJDEST = $(OBJDIR)/lib/libpassthru
+LIBDIR = $(LIB_RELDIR)
+
+include $(MCOM_ROOT)/ldapserver/nsconfig.mk
+include $(LDAP_SRC)/nsldap.mk
+
+ifeq ($(ARCH), WINNT)
+DEF_FILE:=./libpassthru.def
+endif
+
+CFLAGS+=$(SLCFLAGS)
+
+INCLUDES += -I$(LDAP_SRC)/servers/slapd
+
+PASSTHRU_OBJS= ptbind.o ptconfig.o ptconn.o ptdebug.o ptpreop.o ptutil.o
+
+OBJS = $(addprefix $(OBJDEST)/, $(PASSTHRU_OBJS))
+
+ifeq ($(ARCH), WINNT)
+LIBPASSTHRU_DLL_OBJ = $(addprefix $(OBJDEST)/, ptdllmain.o)
+endif
+
+LIBPASSTHRU= $(addprefix $(LIBDIR)/, $(PASSTHRU_DLL).$(DLL_SUFFIX))
+
+ifeq ($(ARCH), WINNT)
+EXTRA_LIBS_DEP += $(LIBSLAPD_DEP)
+EXTRA_LIBS_DEP += $(LDAPSDK_DEP) $(NSPR_DEP)
+EXTRA_LIBS += $(LIBSLAPD) $(LDAP_SDK_LIBLDAP_DLL) $(NSPRLINK)
+endif
+
+
+ifeq ($(ARCH), WINNT)
+DLL_LDFLAGS += -def:"./libpassthru.def"
+endif # WINNT
+
+ifeq ($(ARCH), AIX)
+EXTRA_LIBS_DEP += $(LIBSLAPD_DEP)
+EXTRA_LIBS_DEP += $(LDAPSDK_DEP) $(NSPR_DEP)
+EXTRA_LIBS += $(LIBSLAPDLINK) $(LDAP_SDK_LIBLDAP_DLL) $(NSPRLINK)
+EXTRA_LIBS += $(DLL_EXTRA_LIBS)
+LD=ld
+endif
+
+ifeq ($(ARCH), HPUX)
+EXTRA_LIBS_DEP += $(LIBSLAPD_DEP) $(LDAPSDK_DEP) $(NSPR_DEP) $(SECURITY_DEP)
+EXTRA_LIBS += $(DYN_NSHTTPD) $(ADMINUTIL_LINK) $(LDAPLINK) $(SECURITYLINK) $(NSPRLINK) $(ICULINK)
+endif
+
+clientSDK:
+
+all: $(OBJDEST) $(LIBDIR) $(LIBPASSTHRU)
+
+$(LIBPASSTHRU): $(OBJS) $(LIBPASSTHRU_DLL_OBJ) $(DEF_FILE)
+ $(LINK_DLL) $(LIBPASSTHRU_DLL_OBJ) $(PLATFORMLIBS) $(EXTRA_LIBS)
+
+veryclean: clean
+
+clean:
+ $(RM) $(OBJS)
+ifeq ($(ARCH), WINNT)
+ $(RM) $(LIBPASSTHRU_DLL_OBJ)
+endif
+ $(RM) $(LIBPASSTHRU)
+
+$(OBJDEST):
+ $(MKDIR) $(OBJDEST)
+
+#
+# header file dependencies (incomplete)
+#
+$(OBJS): passthru.h
diff --git a/ldap/servers/plugins/passthru/PT-Notes b/ldap/servers/plugins/passthru/PT-Notes
new file mode 100644
index 00000000..2e3cea10
--- /dev/null
+++ b/ldap/servers/plugins/passthru/PT-Notes
@@ -0,0 +1,30 @@
+#
+# BEGIN COPYRIGHT BLOCK
+# Copyright 2001 Sun Microsystems, Inc.
+# Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+# All rights reserved.
+# END COPYRIGHT BLOCK
+#
+ Pass Through Authentication Plugin Notes
+
+Key
+ r required feature
+ n nice-to-have
+ ? undecided whether this is a good idea or not
+
+Missing features:
+n U/I for configuration.
+
+Loose ends:
+n Resolve any remaining code that is marked with XXX.
+n Put some thought into cases we do not handle (SASL, no DN, no passwd) and
+ make sure we do the right thing in terms of errors, letting server's
+ standard mechanism handle the bind, etc.
+? Protect against server connecting back to itself recursively.
+
+Testing:
+r Basic tests (all platforms: NT,Sol,IRIX,AIX,HP/UX,OSF/1).
+r Controls (both coming (e.g., ?) and going (e.g., password policy).
+r SSL connections to remote servers.
+r LDAPv2/v3 compatiblity
+r Stress tests.
diff --git a/ldap/servers/plugins/passthru/libpassthru.def b/ldap/servers/plugins/passthru/libpassthru.def
new file mode 100644
index 00000000..b08d907a
--- /dev/null
+++ b/ldap/servers/plugins/passthru/libpassthru.def
@@ -0,0 +1,14 @@
+; BEGIN COPYRIGHT BLOCK
+; Copyright 2001 Sun Microsystems, Inc.
+; Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+; All rights reserved.
+; END COPYRIGHT BLOCK
+;
+;
+;
+DESCRIPTION 'Netscape Directory Server 7 Pass Through Authentication Plugin'
+;CODE SHARED READ EXECUTE
+;DATA SHARED READ WRITE
+EXPORTS
+ passthruauth_init @1
+ plugin_init_debug_level @2
diff --git a/ldap/servers/plugins/passthru/passthru.h b/ldap/servers/plugins/passthru/passthru.h
new file mode 100644
index 00000000..fdf30d65
--- /dev/null
+++ b/ldap/servers/plugins/passthru/passthru.h
@@ -0,0 +1,131 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+/*
+ * passthru.h - Pass Through Authentication shared definitions
+ *
+ */
+
+#ifndef _PASSTHRU_H_
+#define _PASSTHRU_H_
+
+#include <stdio.h>
+#include <string.h>
+#include <time.h>
+#include <sys/types.h>
+#include "portable.h"
+#include "slapi-plugin.h"
+#include <dirlite_strings.h> /* PLUGIN_MAGIC_VENDOR_STR */
+#include "dirver.h"
+#include <nspr.h>
+
+/* Private API: to get slapd_pr_strerror() and SLAPI_COMPONENT_NAME_NSPR */
+#include "slapi-private.h"
+
+/*
+ * macros
+ */
+#define PASSTHRU_PLUGIN_SUBSYSTEM "passthru-plugin" /* for logging */
+
+#define PASSTHRU_ASSERT( expr ) PR_ASSERT( expr )
+
+#define PASSTHRU_LDAP_CONN_ERROR( err ) ( (err) == LDAP_SERVER_DOWN || \
+ (err) == LDAP_CONNECT_ERROR )
+
+#define PASSTHRU_OP_NOT_HANDLED 0
+#define PASSTHRU_OP_HANDLED 1
+
+#define PASSTHRU_CONN_TRIES 2
+
+/* #define PASSTHRU_VERBOSE_LOGGING */
+
+/* defaults */
+#define PASSTHRU_DEF_SRVR_MAXCONNECTIONS 3
+#define PASSTHRU_DEF_SRVR_MAXCONCURRENCY 5
+#define PASSTHRU_DEF_SRVR_TIMEOUT 300 /* seconds */
+#define PASSTHRU_DEF_SRVR_PROTOCOL_VERSION LDAP_VERSION3
+#define PASSTHRU_DEF_SRVR_CONNLIFETIME 0 /* seconds */
+#define PASSTHRU_DEF_SRVR_FAILOVERCONNLIFETIME 300 /* seconds */
+
+/*
+ * structs
+ */
+typedef struct passthrusuffix {
+ int ptsuffix_len;
+ char *ptsuffix_normsuffix; /* not case normalized */
+ struct passthrusuffix *ptsuffix_next;
+} PassThruSuffix;
+
+typedef struct passthruconnection {
+ LDAP *ptconn_ld;
+ int ptconn_ldapversion;
+ int ptconn_usecount;
+#define PASSTHRU_CONNSTATUS_OK 0
+#define PASSTHRU_CONNSTATUS_DOWN 1
+#define PASSTHRU_CONNSTATUS_STALE 2
+ int ptconn_status;
+ time_t ptconn_opentime;
+ struct passthruconnection *ptconn_prev;
+ struct passthruconnection *ptconn_next;
+} PassThruConnection;
+
+typedef struct passthruserver {
+ char *ptsrvr_url; /* copy from argv[i] */
+ char *ptsrvr_hostname;
+ int ptsrvr_port;
+ int ptsrvr_secure; /* use SSL? */
+ int ptsrvr_ldapversion;
+ int ptsrvr_maxconnections;
+ int ptsrvr_maxconcurrency;
+ int ptsrvr_connlifetime; /* in seconds */
+ struct timeval *ptsrvr_timeout; /* for ldap_result() */
+ PassThruSuffix *ptsrvr_suffixes;
+ Slapi_CondVar *ptsrvr_connlist_cv;
+ Slapi_Mutex *ptsrvr_connlist_mutex; /* protects connlist */
+ int ptsrvr_connlist_count;
+ PassThruConnection *ptsrvr_connlist;
+ struct passthruserver *ptsrvr_next;
+} PassThruServer;
+
+typedef struct passthruconfig {
+ PassThruServer *ptconfig_serverlist;
+} PassThruConfig;
+
+
+/*
+ * public functions
+ */
+/*
+ * ptbind.c:
+ */
+int passthru_simple_bind_s( Slapi_PBlock *pb, PassThruServer *srvr, int tries,
+ char *dn, struct berval *creds, LDAPControl **reqctrls, int *lderrnop,
+ char **matcheddnp, char **errmsgp, struct berval ***refurlsp,
+ LDAPControl ***resctrlsp );
+
+/*
+ * ptconfig.c:
+ */
+int passthru_config( int argc, char **argv );
+PassThruConfig *passthru_get_config( void );
+
+/*
+ * ptconn.c:
+ */
+int passthru_dn2server( PassThruConfig *cfg, char *normdn,
+ PassThruServer **srvrp );
+int passthru_get_connection( PassThruServer *srvr, LDAP **ldp );
+void passthru_release_connection( PassThruServer *srvr, LDAP *ld, int dispose );
+void passthru_close_all_connections( PassThruConfig *cfg );
+
+/*
+ * ptutil.c:
+ */
+struct berval **passthru_strs2bervals( char **ss );
+char ** passthru_bervals2strs( struct berval **bvs );
+void passthru_free_bervals( struct berval **bvs );
+char *passthru_urlparse_err2string( int err );
+
+#endif /* _PASSTHRU_H_ */
diff --git a/ldap/servers/plugins/passthru/ptbind.c b/ldap/servers/plugins/passthru/ptbind.c
new file mode 100644
index 00000000..f9da57a1
--- /dev/null
+++ b/ldap/servers/plugins/passthru/ptbind.c
@@ -0,0 +1,144 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+/*
+ * ptbind.c - LDAP bind-related code for Pass Through Authentication
+ *
+ */
+
+#include "passthru.h"
+
+static int
+passthru_simple_bind_once_s( PassThruServer *srvr, char *dn,
+ struct berval *creds, LDAPControl **reqctrls, int *lderrnop,
+ char **matcheddnp, char **errmsgp, struct berval ***refurlsp,
+ LDAPControl ***resctrlsp );
+
+
+/*
+ * Attempt to chain a bind request off to "srvr." We return an LDAP error
+ * code that indicates whether we successfully got a response from the
+ * other server or not. If we succeed, we return LDAP_SUCCESS and *lderrnop
+ * is set to the result code from the remote server.
+ *
+ * Note that in the face of "ldap server down" or "ldap connect failed" errors
+ * we make up to "tries" attempts to bind to the remote server. Since we
+ * are only interested in recovering silently when the remote server is up
+ * but decided to close our connection, we retry without pausing between
+ * attempts.
+ */
+int
+passthru_simple_bind_s( Slapi_PBlock *pb, PassThruServer *srvr, int tries,
+ char *dn, struct berval *creds, LDAPControl **reqctrls, int *lderrnop,
+ char **matcheddnp, char **errmsgp, struct berval ***refurlsp,
+ LDAPControl ***resctrlsp )
+{
+ int rc;
+
+ PASSTHRU_ASSERT( srvr != NULL );
+ PASSTHRU_ASSERT( tries > 0 );
+ PASSTHRU_ASSERT( creds != NULL );
+ PASSTHRU_ASSERT( lderrnop != NULL );
+ PASSTHRU_ASSERT( refurlsp != NULL );
+
+ do {
+ /*
+ * check to see if operation has been abandoned...
+ */
+ if ( slapi_op_abandoned( pb )) {
+ slapi_log_error( SLAPI_LOG_PLUGIN, PASSTHRU_PLUGIN_SUBSYSTEM,
+ "operation abandoned\n" );
+ rc = LDAP_USER_CANCELLED;
+ } else {
+ rc = passthru_simple_bind_once_s( srvr, dn, creds, reqctrls,
+ lderrnop, matcheddnp, errmsgp, refurlsp, resctrlsp );
+ }
+ } while ( PASSTHRU_LDAP_CONN_ERROR( rc ) && --tries > 0 );
+
+ return( rc );
+}
+
+
+/*
+ * like passthru_simple_bind_s() but only makes one attempt.
+ */
+static int
+passthru_simple_bind_once_s( PassThruServer *srvr, char *dn,
+ struct berval *creds, LDAPControl **reqctrls, int *lderrnop,
+ char **matcheddnp, char **errmsgp, struct berval ***refurlsp,
+ LDAPControl ***resctrlsp )
+{
+ int rc, msgid;
+ char **referrals;
+ struct timeval tv, *timeout;
+ LDAPMessage *result;
+ LDAP *ld;
+
+ /*
+ * Grab an LDAP connection to use for this bind.
+ */
+ ld = NULL;
+ if (( rc = passthru_get_connection( srvr, &ld )) != LDAP_SUCCESS ) {
+ goto release_and_return;
+ }
+
+ /*
+ * Send the bind operation (need to retry on LDAP_SERVER_DOWN)
+ */
+ if (( rc = ldap_sasl_bind( ld, dn, LDAP_SASL_SIMPLE, creds, reqctrls,
+ NULL, &msgid )) != LDAP_SUCCESS ) {
+ goto release_and_return;
+ }
+
+ /*
+ * determine timeout value (how long we will wait for a response)
+ * if timeout is NULL or zero'd, we wait indefinitely.
+ */
+ if ( srvr->ptsrvr_timeout == NULL || ( srvr->ptsrvr_timeout->tv_sec == 0
+ && srvr->ptsrvr_timeout->tv_usec == 0 )) {
+ timeout = NULL;
+ } else {
+ tv = *srvr->ptsrvr_timeout; /* struct copy */
+ timeout = &tv;
+ }
+
+ /*
+ * Wait for a result.
+ */
+ rc = ldap_result( ld, msgid, 1, timeout, &result );
+
+ /*
+ * Interpret the result.
+ */
+ if ( rc == 0 ) { /* timeout */
+ /*
+ * Timed out waiting for a reply from the server.
+ */
+ rc = LDAP_TIMEOUT;
+ } else if ( rc < 0 ) {
+ /*
+ * Some other error occurred (no result received).
+ */
+ rc = ldap_get_lderrno( ld, matcheddnp, errmsgp );
+ } else {
+ /*
+ * Got a result from remote server -- parse it.
+ */
+ rc = ldap_parse_result( ld, result, lderrnop, matcheddnp, errmsgp,
+ &referrals, resctrlsp, 1 );
+ if ( referrals != NULL ) {
+ *refurlsp = passthru_strs2bervals( referrals );
+ ldap_value_free( referrals );
+ }
+ }
+
+
+release_and_return:
+ if ( ld != NULL ) {
+ passthru_release_connection( srvr, ld, PASSTHRU_LDAP_CONN_ERROR( rc ));
+ }
+
+ return( rc );
+}
diff --git a/ldap/servers/plugins/passthru/ptconfig.c b/ldap/servers/plugins/passthru/ptconfig.c
new file mode 100644
index 00000000..c3653f66
--- /dev/null
+++ b/ldap/servers/plugins/passthru/ptconfig.c
@@ -0,0 +1,301 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+/*
+ * ptconfig.c - configuration-related code for Pass Through Authentication
+ *
+ */
+
+#include "passthru.h"
+
+/*
+ * Configuration is a bit complicated to fit into a single slapd config file
+ * line, but for now that's how it works. The format is:
+ *
+ * plugin preoperation on PTA NSHOME/passthru-plugin.so passthruauth_init ARGS
+ *
+ * where each ARGS provides configuration for one host. Each ARG should
+ * be of the form:
+ *
+ * "ldap://hosts/suffixes maxconns,maxconcurrency,timeout,ldver,connlifetime"
+ * OR
+ * "ldaps://hosts/suffixes maxconns,maxconcurrency,timeout,ldver,connlifetime"
+ *
+ * where:
+ * hosts is a space-separated list of remote servers (with optional port
+ * numbers) to be used. Each one is tried in order when opening an
+ * LDAP connection.
+ * suffixes is a semicolon separated list of DNs (if a DN contains a
+ * semicolon it must be represented \3B),
+ * maxconns is a limit on how many connections will be made,
+ * maxconcurrency is a limit on how many operations can share a connection,
+ * timeout is a time limit in seconds for bind operations to complete (use
+ * 0 to specify an infinite limit).
+ * ldver is the LDAP protocol version to use to talk to the server (2 or 3)
+ * connlifetime is a time limit time in seconds for a connection to be
+ * used before it is closed and reopened (use 0 to specify an infinite
+ * limit). connlifetime can be omitted in which case a default value
+ * is used; this is for compatibility with DS 4.0 which did not support
+ * connlifetime.
+ */
+
+
+/*
+ * function prototypes
+ */
+static char **get_backend_suffixes( void );
+static int is_underneath_backend_suffix( char *normdn, char **besuffixes );
+
+/*
+ * static variables
+ */
+/* for now, there is only one configuration and it is global to the plugin */
+static PassThruConfig theConfig;
+static int inited = 0;
+
+
+/*
+ * Read configuration and create a configuration data structure.
+ * This is called after the server has configured itself so we can check
+ * for things like collisions between our suffixes and backend's suffixes.
+ * Returns an LDAP error code (LDAP_SUCCESS if all goes well).
+ * XXXmcs: this function leaks memory if any errors occur.
+ */
+int
+passthru_config( int argc, char **argv )
+{
+ int i, j, rc, tosecs, using_def_connlifetime;
+ char *p, **suffixarray;
+ PassThruServer *prevsrvr, *srvr;
+ PassThruSuffix *suffix, *prevsuffix;
+ LDAPURLDesc *ludp;
+
+ if ( inited ) {
+ slapi_log_error( SLAPI_LOG_FATAL, PASSTHRU_PLUGIN_SUBSYSTEM,
+ "only one pass through plugin instance can be used\n" );
+ return( LDAP_PARAM_ERROR );
+ }
+
+ inited = 1;
+
+ /*
+ * It doesn't make sense to configure a pass through plugin without
+ * providing at least one remote server. Return an error if attempted.
+ */
+ if ( argc < 1 ) {
+ slapi_log_error( SLAPI_LOG_FATAL, PASSTHRU_PLUGIN_SUBSYSTEM,
+ "no pass through servers found in configuration"
+ " (at least one must be listed)\n" );
+ return( LDAP_PARAM_ERROR );
+ }
+
+ /*
+ * Parse argv[] values.
+ */
+ prevsrvr = NULL;
+ for ( i = 0; i < argc; ++i ) {
+ srvr = (PassThruServer *)slapi_ch_calloc( 1, sizeof( PassThruServer ));
+ srvr->ptsrvr_url = slapi_ch_strdup( argv[i] );
+
+ if (( p = strchr( srvr->ptsrvr_url, ' ' )) == NULL ) {
+ /*
+ * use defaults for maxconnections, maxconcurrency, timeout,
+ * LDAP version, and connlifetime.
+ */
+ srvr->ptsrvr_maxconnections = PASSTHRU_DEF_SRVR_MAXCONNECTIONS;
+ srvr->ptsrvr_maxconcurrency = PASSTHRU_DEF_SRVR_MAXCONCURRENCY;
+ srvr->ptsrvr_timeout = (struct timeval *)slapi_ch_calloc( 1,
+ sizeof( struct timeval ));
+ srvr->ptsrvr_timeout->tv_sec = PASSTHRU_DEF_SRVR_TIMEOUT;
+ srvr->ptsrvr_ldapversion = PASSTHRU_DEF_SRVR_PROTOCOL_VERSION;
+ using_def_connlifetime = 1;
+ } else {
+ /*
+ * parse parameters. format is:
+ * maxconnections,maxconcurrency,timeout,ldapversion
+ * OR maxconnections,maxconcurrency,timeout,ldapversion,lifetime
+ */
+ *p++ = '\0';
+ rc = sscanf( p, "%d,%d,%d,%d,%d", &srvr->ptsrvr_maxconnections,
+ &srvr->ptsrvr_maxconcurrency, &tosecs,
+ &srvr->ptsrvr_ldapversion, &srvr->ptsrvr_connlifetime );
+ if ( rc < 4 ) {
+ slapi_log_error( SLAPI_LOG_FATAL, PASSTHRU_PLUGIN_SUBSYSTEM,
+ "server parameters should be in the form "
+ "\"maxconnections,maxconcurrency,timeout,ldapversion,"
+ "connlifetime\" (got \"%s\")\n", p );
+ return( LDAP_PARAM_ERROR );
+ } else if ( rc < 5 ) {
+ using_def_connlifetime = 1;
+ srvr->ptsrvr_connlifetime = PASSTHRU_DEF_SRVR_CONNLIFETIME;
+ } else {
+ using_def_connlifetime = 0;
+ }
+
+ if ( srvr->ptsrvr_ldapversion != LDAP_VERSION2
+ && srvr->ptsrvr_ldapversion != LDAP_VERSION3 ) {
+ slapi_log_error( SLAPI_LOG_FATAL, PASSTHRU_PLUGIN_SUBSYSTEM,
+ "LDAP protocol version should be %d or %d (got %d)\n",
+ LDAP_VERSION2, LDAP_VERSION3,
+ srvr->ptsrvr_ldapversion );
+ return( LDAP_PARAM_ERROR );
+ }
+
+ if ( srvr->ptsrvr_maxconnections <= 0 ) {
+ slapi_log_error( SLAPI_LOG_FATAL, PASSTHRU_PLUGIN_SUBSYSTEM,
+ "maximum connections must be greater than "
+ "zero (got %d)\n", srvr->ptsrvr_maxconnections );
+ return( LDAP_PARAM_ERROR );
+ }
+
+ if ( srvr->ptsrvr_maxconcurrency <= 0 ) {
+ slapi_log_error( SLAPI_LOG_FATAL, PASSTHRU_PLUGIN_SUBSYSTEM,
+ "maximum concurrency must be greater than "
+ "zero (got %d)\n", srvr->ptsrvr_maxconcurrency );
+ return( LDAP_PARAM_ERROR );
+ }
+
+ if ( tosecs <= 0 ) {
+ srvr->ptsrvr_timeout = NULL;
+ } else {
+ srvr->ptsrvr_timeout = (struct timeval *)slapi_ch_calloc( 1,
+ sizeof( struct timeval ));
+ srvr->ptsrvr_timeout->tv_sec = tosecs;
+ }
+ }
+
+ /*
+ * parse the LDAP URL
+ */
+ if (( rc = ldap_url_parse( srvr->ptsrvr_url, &ludp )) != 0 ) {
+ slapi_log_error( SLAPI_LOG_FATAL, PASSTHRU_PLUGIN_SUBSYSTEM,
+ "unable to parse LDAP URL \"%s\" (%s)\n",
+ srvr->ptsrvr_url, passthru_urlparse_err2string( rc ));
+ return( LDAP_PARAM_ERROR );
+ }
+
+ if ( ludp->lud_dn == NULL || *ludp->lud_dn == '\0' ) {
+ slapi_log_error( SLAPI_LOG_FATAL, PASSTHRU_PLUGIN_SUBSYSTEM,
+ "missing suffix in LDAP URL \"%s\"\n",
+ srvr->ptsrvr_url );
+ return( LDAP_PARAM_ERROR );
+ }
+
+ srvr->ptsrvr_hostname = slapi_ch_strdup( ludp->lud_host );
+ srvr->ptsrvr_port = ludp->lud_port;
+ srvr->ptsrvr_secure =
+ (( ludp->lud_options & LDAP_URL_OPT_SECURE ) != 0 );
+
+ /*
+ * If a space-separated list of hosts is configured for failover,
+ * use a different (non infinite) default for connection lifetime.
+ */
+ if ( using_def_connlifetime &&
+ strchr( srvr->ptsrvr_hostname, ' ' ) != NULL ) {
+ srvr->ptsrvr_connlifetime =
+ PASSTHRU_DEF_SRVR_FAILOVERCONNLIFETIME;
+ }
+
+ /*
+ * split the DN into multiple suffixes (separated by ';')
+ */
+ if (( suffixarray = ldap_str2charray( ludp->lud_dn, ";" )) == NULL ) {
+ slapi_log_error( SLAPI_LOG_FATAL, PASSTHRU_PLUGIN_SUBSYSTEM,
+ "unable to parse suffix string \"%s\" within \"%s\"\n",
+ ludp->lud_dn, srvr->ptsrvr_url );
+ return( LDAP_PARAM_ERROR );
+ }
+
+ /*
+ * free our LDAP URL descriptor
+ */
+ ldap_free_urldesc( ludp );
+ ludp = NULL;
+
+ /*
+ * reorganize the suffixes into a linked list and normalize them
+ */
+ prevsuffix = NULL;
+ for ( j = 0; suffixarray[ j ] != NULL; ++j ) {
+
+ /*
+ * allocate a new PassThruSuffix structure and fill it in.
+ */
+ suffix = (PassThruSuffix *)slapi_ch_malloc(
+ sizeof( PassThruSuffix ));
+ suffix->ptsuffix_normsuffix =
+ slapi_dn_normalize( suffixarray[ j ] );
+ suffixarray[ j ] = NULL;
+ suffix->ptsuffix_len = strlen( suffix->ptsuffix_normsuffix );
+ suffix->ptsuffix_next = NULL;
+
+ /*
+ * add to end of list
+ */
+ if ( prevsuffix == NULL ) {
+ srvr->ptsrvr_suffixes = suffix;
+ } else {
+ prevsuffix->ptsuffix_next = suffix;
+ }
+ prevsuffix = suffix;
+ }
+ ldap_memfree( suffixarray );
+
+ /*
+ * create mutexes and condition variables for this server
+ */
+ if (( srvr->ptsrvr_connlist_mutex = slapi_new_mutex()) == NULL ||
+ ( srvr->ptsrvr_connlist_cv = slapi_new_condvar(
+ srvr->ptsrvr_connlist_mutex )) == NULL ) {
+ return( LDAP_LOCAL_ERROR );
+ }
+
+ /*
+ * add this server to the end of our list
+ */
+ if ( prevsrvr == NULL ) {
+ theConfig.ptconfig_serverlist = srvr;
+ } else {
+ prevsrvr->ptsrvr_next = srvr;
+ }
+ prevsrvr = srvr;
+
+#ifdef PASSTHRU_VERBOSE_LOGGING
+ /*
+ * log configuration for debugging purposes
+ */
+ slapi_log_error( SLAPI_LOG_PLUGIN, PASSTHRU_PLUGIN_SUBSYSTEM,
+ "PTA server host: \"%s\", port: %d, secure: %d,"
+ " maxconnections: %d, maxconcurrency: %d, timeout: %d,"
+ " ldversion: %d, connlifetime: %d\n",
+ srvr->ptsrvr_hostname, srvr->ptsrvr_port,
+ srvr->ptsrvr_secure, srvr->ptsrvr_maxconnections,
+ srvr->ptsrvr_maxconcurrency,
+ srvr->ptsrvr_timeout == NULL ? -1
+ : srvr->ptsrvr_timeout->tv_sec, srvr->ptsrvr_ldapversion,
+ srvr->ptsrvr_connlifetime );
+ for ( prevsuffix = srvr->ptsrvr_suffixes; prevsuffix != NULL;
+ prevsuffix = prevsuffix->ptsuffix_next ) {
+ slapi_log_error( SLAPI_LOG_FATAL, PASSTHRU_PLUGIN_SUBSYSTEM,
+ " normalized suffix: \"%s\"\n",
+ prevsuffix->ptsuffix_normsuffix );
+ }
+#endif
+
+ }
+
+ return( LDAP_SUCCESS );
+}
+
+
+/*
+ * Get the pass though configuration data. For now, there is only one
+ * configuration and it is global to the plugin.
+ */
+PassThruConfig *
+passthru_get_config( void )
+{
+ return( &theConfig );
+}
diff --git a/ldap/servers/plugins/passthru/ptconn.c b/ldap/servers/plugins/passthru/ptconn.c
new file mode 100644
index 00000000..56e2e0cc
--- /dev/null
+++ b/ldap/servers/plugins/passthru/ptconn.c
@@ -0,0 +1,420 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+/*
+ * ptconn.c - LDAP connection-related code for Pass Through Authentication
+ *
+ */
+
+#include "passthru.h"
+
+/*
+ * function prototypes
+ */
+static int dn_is_underneath_suffix( PassThruSuffix *suffix, char *normdn,
+ int dnlen );
+static void close_and_dispose_connection( PassThruConnection *conn );
+static void check_for_stale_connections( PassThruServer *srvr );
+
+
+/*
+ * Most of the complicated connection-related code lives in this file. Some
+ * general notes about how we manage our connections to "remote" LDAP servers:
+ *
+ * 1) Each server we have a relationship with is managed independently.
+ *
+ * 2) We may simultaneously issue multiple bind requests on a single LDAP
+ * connection. Each server has a "maxconcurrency" configuration
+ * parameter associated with it that caps the number of outstanding
+ * binds per connection. For each connection we maintain a "usecount"
+ * which is used to track the number of threads using the connection.
+ *
+ * 3) We may open more than one connection to a server. This is only done
+ * when "maxconcurrency" is exceeded for all the connections we already
+ * have open. Each server has a "maxconnections" configuration
+ * parameter associated with it that caps the number of connections.
+ * We also maintain a "connlist_count" for each server so we know when
+ * we have reached the maximum number of open connections allowed.
+ *
+ * 4) If no connection is available to service a request (and we have
+ * reached the limit of how many we are supposed to open), threads
+ * go to sleep on a condition variable and one is woken up each time
+ * a connection's "usecount" is decremented.
+ *
+ * 5) If we see an LDAP_CONNECT_ERROR or LDAP_SERVER_DOWN error on a
+ * session handle, we mark its status as PASSTHRU_CONNSTATUS_DOWN and
+ * close it as soon as all threads using it release it. Connections
+ * marked as "down" are not counted against the "maxconnections" limit.
+ *
+ * 6) We close and reopen connections that have been open for more than
+ * the server's configured connection lifetime. This is done to ensure
+ * that we reconnect to a primary server after failover occurs. If no
+ * lifetime is configured or it is set to 0, we never close and reopen
+ * connections.
+ */
+
+
+/*
+ * Given a normalized target dn, see if it we should "pass through"
+ * authentication to another LDAP server. The answer is "yes" if the
+ * target dn resides under one of the suffixes we have that is associated
+ * with an LDAP server we know about.
+ *
+ * This function assumes that normdn is normalized and the the suffixes in the
+ * cfg structure have also been normalized.
+ *
+ * Returns an LDAP error code, typically:
+ * LDAP_SUCCESS should pass though; *srvrp set.
+ * LDAP_NO_SUCH_OBJECT let this server handle the bind.
+ */
+int
+passthru_dn2server( PassThruConfig *cfg, char *normdn, PassThruServer **srvrp )
+{
+ PassThruServer *ptsrvr;
+ PassThruSuffix *ptsuffix;
+ int dnlen;
+
+ PASSTHRU_ASSERT( cfg != NULL );
+ PASSTHRU_ASSERT( normdn != NULL );
+ PASSTHRU_ASSERT( srvrp != NULL );
+
+ dnlen = strlen( normdn );
+
+ for ( ptsrvr = cfg->ptconfig_serverlist; ptsrvr != NULL;
+ ptsrvr = ptsrvr->ptsrvr_next ) {
+ for ( ptsuffix = ptsrvr->ptsrvr_suffixes; ptsuffix != NULL;
+ ptsuffix = ptsuffix->ptsuffix_next ) {
+ if ( dn_is_underneath_suffix( ptsuffix, normdn, dnlen )) {
+ *srvrp = ptsrvr;
+ return( LDAP_SUCCESS ); /* got it */
+ }
+ }
+ }
+
+ *srvrp = NULL;
+ return( LDAP_NO_SUCH_OBJECT ); /* no match */
+}
+
+
+/*
+ * Get an LDAP session handle for communicating with srvr.
+ *
+ * Returns an LDAP eror code, typically:
+ * LDAP_SUCCESS
+ * other
+ */
+int
+passthru_get_connection( PassThruServer *srvr, LDAP **ldp )
+{
+ int rc;
+ PassThruConnection *conn, *connprev;
+ LDAP *ld;
+
+ PASSTHRU_ASSERT( srvr != NULL );
+ PASSTHRU_ASSERT( ldp != NULL );
+
+ check_for_stale_connections( srvr );
+
+ slapi_lock_mutex( srvr->ptsrvr_connlist_mutex );
+ rc = LDAP_SUCCESS; /* optimistic */
+
+ slapi_log_error( SLAPI_LOG_PLUGIN, PASSTHRU_PLUGIN_SUBSYSTEM,
+ "=> passthru_get_connection server %s:%d conns: %d maxconns: %d\n",
+ srvr->ptsrvr_hostname, srvr->ptsrvr_port, srvr->ptsrvr_connlist_count,
+ srvr->ptsrvr_maxconnections );
+
+ for ( ;; ) {
+ /*
+ * look for an available, already open connection
+ */
+ connprev = NULL;
+ for ( conn = srvr->ptsrvr_connlist; conn != NULL;
+ conn = conn->ptconn_next ) {
+ if ( conn->ptconn_status == PASSTHRU_CONNSTATUS_OK
+ && conn->ptconn_usecount < srvr->ptsrvr_maxconcurrency ) {
+#ifdef PASSTHRU_VERBOSE_LOGGING
+ slapi_log_error( SLAPI_LOG_PLUGIN, PASSTHRU_PLUGIN_SUBSYSTEM,
+ "<= passthru_get_connection server found "
+ "conn 0x%x to use)\n", conn->ptconn_ld );
+#endif
+ goto unlock_and_return; /* found one */
+ }
+ connprev = conn;
+ }
+
+ if ( srvr->ptsrvr_connlist_count < srvr->ptsrvr_maxconnections ) {
+ /*
+ * we have not exceeded the maximum number of connections allowed,
+ * so we initialize a new one and add it to the end of our list.
+ */
+ if (( ld = slapi_ldap_init( srvr->ptsrvr_hostname,
+ srvr->ptsrvr_port, srvr->ptsrvr_secure, 1 )) == NULL ) {
+#ifdef PASSTHRU_VERBOSE_LOGGING
+ slapi_log_error( SLAPI_LOG_PLUGIN, PASSTHRU_PLUGIN_SUBSYSTEM,
+ "<= passthru_get_connection slapi_ldap_init failed\n" );
+#endif
+ rc = LDAP_LOCAL_ERROR;
+ goto unlock_and_return;
+ }
+
+ /*
+ * set protocol version to correct value for this server
+ */
+ if ( ldap_set_option( ld, LDAP_OPT_PROTOCOL_VERSION,
+ &srvr->ptsrvr_ldapversion ) != 0 ) {
+ slapi_ldap_unbind( ld );
+ }
+
+ conn = (PassThruConnection *)slapi_ch_malloc(
+ sizeof( PassThruConnection ));
+ conn->ptconn_ld = ld;
+ conn->ptconn_status = PASSTHRU_CONNSTATUS_OK;
+ time( &conn->ptconn_opentime );
+ conn->ptconn_ldapversion = srvr->ptsrvr_ldapversion;
+ conn->ptconn_usecount = 0;
+ conn->ptconn_next = NULL;
+ conn->ptconn_prev = connprev;
+ if ( connprev == NULL ) {
+ srvr->ptsrvr_connlist = conn;
+ conn->ptconn_prev = NULL;
+ } else {
+ connprev->ptconn_next = conn;
+ conn->ptconn_prev = connprev;
+ }
+
+ ++srvr->ptsrvr_connlist_count;
+
+#ifdef PASSTHRU_VERBOSE_LOGGING
+ slapi_log_error( SLAPI_LOG_PLUGIN, PASSTHRU_PLUGIN_SUBSYSTEM,
+ "<= passthru_get_connection added new conn 0x%x, "
+ "conn count now %d\n", ld, srvr->ptsrvr_connlist_count );
+#endif
+ goto unlock_and_return; /* got a new one */
+ }
+
+#ifdef PASSTHRU_VERBOSE_LOGGING
+ slapi_log_error( SLAPI_LOG_PLUGIN, PASSTHRU_PLUGIN_SUBSYSTEM,
+ "... passthru_get_connection waiting for conn to free up\n" );
+#endif
+ slapi_wait_condvar( srvr->ptsrvr_connlist_cv, NULL );
+
+#ifdef PASSTHRU_VERBOSE_LOGGING
+ slapi_log_error( SLAPI_LOG_PLUGIN, PASSTHRU_PLUGIN_SUBSYSTEM,
+ "... passthru_get_connection awake again\n" );
+#endif
+ }
+
+unlock_and_return:
+ if ( conn != NULL ) {
+ ++conn->ptconn_usecount;
+ *ldp = conn->ptconn_ld;
+ slapi_log_error( SLAPI_LOG_PLUGIN, PASSTHRU_PLUGIN_SUBSYSTEM,
+ "<= passthru_get_connection ld=0x%x (concurrency now %d)\n",
+ *ldp, conn->ptconn_usecount );
+ } else {
+ slapi_log_error( SLAPI_LOG_PLUGIN, PASSTHRU_PLUGIN_SUBSYSTEM,
+ "<= passthru_get_connection error %d\n", rc );
+ }
+
+ slapi_unlock_mutex( srvr->ptsrvr_connlist_mutex );
+ return( rc );
+}
+
+
+/*
+ * Mark the connection ld is associated with as free to be used again.
+ * If dispose is non-zero, we mark the connection as "bad" and dispose
+ * of it and its ld once the use count becomes zero.
+ */
+void
+passthru_release_connection( PassThruServer *srvr, LDAP *ld, int dispose )
+{
+ PassThruConnection *conn, *connprev;
+
+ PASSTHRU_ASSERT( srvr != NULL );
+ PASSTHRU_ASSERT( ld != NULL );
+
+#ifdef PASSTHRU_VERBOSE_LOGGING
+ slapi_log_error( SLAPI_LOG_PLUGIN, PASSTHRU_PLUGIN_SUBSYSTEM,
+ "=> passthru_release_connection ld=0x%x%s\n", ld,
+ dispose ? " (disposing)" : "" );
+#endif
+
+ slapi_lock_mutex( srvr->ptsrvr_connlist_mutex );
+
+ /*
+ * find the connection structure this ld is part of
+ */
+ connprev = NULL;
+ for ( conn = srvr->ptsrvr_connlist; conn != NULL;
+ conn = conn->ptconn_next ) {
+ if ( ld == conn->ptconn_ld ) {
+ break;
+ }
+ connprev = conn;
+ }
+
+ if ( conn == NULL ) { /* ld not found -- unexpected */
+ slapi_log_error( SLAPI_LOG_PLUGIN, PASSTHRU_PLUGIN_SUBSYSTEM,
+ "=> passthru_release_connection ld=0x%x not found\n", ld );
+ } else {
+ PASSTHRU_ASSERT( conn->ptconn_usecount > 0 );
+ --conn->ptconn_usecount;
+ if ( dispose ) {
+ conn->ptconn_status = PASSTHRU_CONNSTATUS_DOWN;
+ }
+
+ if ( conn->ptconn_status != PASSTHRU_CONNSTATUS_OK
+ && conn->ptconn_usecount == 0 ) {
+ /*
+ * remove from server's connection list
+ */
+ if ( connprev == NULL ) {
+ srvr->ptsrvr_connlist = conn->ptconn_next;
+ } else {
+ connprev->ptconn_next = conn->ptconn_next;
+ }
+ --srvr->ptsrvr_connlist_count;
+
+ /*
+ * close connection and free memory
+ */
+ close_and_dispose_connection( conn );
+ }
+ }
+
+ /*
+ * wake up a thread that is waiting for a connection (there may not be
+ * any but the slapi_notify_condvar() call should be cheap in any event).
+ */
+ slapi_notify_condvar( srvr->ptsrvr_connlist_cv, 0 );
+
+ /*
+ * unlock and return
+ */
+ slapi_unlock_mutex( srvr->ptsrvr_connlist_mutex );
+}
+
+
+/*
+ * close all open connections in preparation for server shutdown, etc.
+ */
+void
+passthru_close_all_connections( PassThruConfig *cfg )
+{
+ PassThruServer *srvr;
+ PassThruConnection *conn, *nextconn;
+
+ PASSTHRU_ASSERT( cfg != NULL );
+
+ for ( srvr = cfg->ptconfig_serverlist; srvr != NULL;
+ srvr = srvr->ptsrvr_next ) {
+ for ( conn = srvr->ptsrvr_connlist; conn != NULL; conn = nextconn ) {
+ nextconn = conn->ptconn_next;
+ close_and_dispose_connection( conn );
+ }
+ }
+}
+
+
+/*
+ * return non-zero value if normdn falls underneath a suffix
+ */
+static int
+dn_is_underneath_suffix( PassThruSuffix *suffix, char *normdn, int dnlen )
+{
+ PASSTHRU_ASSERT( suffix != NULL );
+ PASSTHRU_ASSERT( normdn != NULL );
+ PASSTHRU_ASSERT( dnlen >= 0 );
+
+ return ( suffix->ptsuffix_len <= dnlen &&
+ slapi_UTF8CASECMP( suffix->ptsuffix_normsuffix,
+ normdn + ( dnlen - suffix->ptsuffix_len )) == 0 );
+}
+
+
+/*
+ * Unbind from server and dispose of a connection.
+ */
+static void
+close_and_dispose_connection( PassThruConnection *conn )
+{
+ PASSTHRU_ASSERT( conn != NULL );
+ PASSTHRU_ASSERT( conn->ptconn_ld != NULL );
+
+ slapi_ldap_unbind( conn->ptconn_ld );
+ conn->ptconn_ld = NULL;
+ slapi_ch_free( (void **)&conn );
+}
+
+
+/*
+ * Close (or mark to be closed) any connections for this srvr that have
+ * exceeded the maximum connection lifetime.
+ */
+static void
+check_for_stale_connections( PassThruServer *srvr )
+{
+ PassThruConnection *conn, *prevconn, *nextconn;
+ time_t curtime;
+
+ PASSTHRU_ASSERT( srvr != NULL );
+
+#ifdef PASSTHRU_VERBOSE_LOGGING
+ slapi_log_error( SLAPI_LOG_PLUGIN, PASSTHRU_PLUGIN_SUBSYSTEM,
+ "check_for_stale_connections: server %s (lifetime %d secs)\n",
+ srvr->ptsrvr_url, srvr->ptsrvr_connlifetime );
+#endif
+
+
+ if ( srvr->ptsrvr_connlifetime <= 0 ) {
+ return;
+ }
+
+ time( &curtime );
+
+ slapi_lock_mutex( srvr->ptsrvr_connlist_mutex );
+
+ prevconn = NULL;
+ for ( conn = srvr->ptsrvr_connlist; conn != NULL; conn = nextconn ) {
+ nextconn = conn->ptconn_next;
+
+ if ( curtime - conn->ptconn_opentime > srvr->ptsrvr_connlifetime ) {
+ if ( conn->ptconn_usecount == 0 ) {
+ /*
+ * connection is idle and stale -- remove from server's list
+ */
+#ifdef PASSTHRU_VERBOSE_LOGGING
+ slapi_log_error( SLAPI_LOG_PLUGIN, PASSTHRU_PLUGIN_SUBSYSTEM,
+ "check_for_stale_connections: discarding idle, "
+ "stale connection 0x%x\n", conn->ptconn_ld );
+#endif
+ if ( prevconn == NULL ) {
+ srvr->ptsrvr_connlist = nextconn;
+ } else {
+ prevconn->ptconn_next = nextconn;
+ }
+ --srvr->ptsrvr_connlist_count;
+ close_and_dispose_connection( conn );
+ } else {
+ /*
+ * connection is stale but in use -- mark to be disposed later
+ */
+#ifdef PASSTHRU_VERBOSE_LOGGING
+ slapi_log_error( SLAPI_LOG_PLUGIN, PASSTHRU_PLUGIN_SUBSYSTEM,
+ "check_for_stale_connections: marking connection 0x%x "
+ "stale (use count %d)\n", conn->ptconn_ld,
+ conn->ptconn_usecount );
+#endif
+ conn->ptconn_status = PASSTHRU_CONNSTATUS_STALE;
+ prevconn = conn;
+ }
+ } else {
+ prevconn = conn;
+ }
+ }
+
+ slapi_unlock_mutex( srvr->ptsrvr_connlist_mutex );
+}
diff --git a/ldap/servers/plugins/passthru/ptdebug.c b/ldap/servers/plugins/passthru/ptdebug.c
new file mode 100644
index 00000000..0f01c4a7
--- /dev/null
+++ b/ldap/servers/plugins/passthru/ptdebug.c
@@ -0,0 +1,23 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+/*
+ * ptdebug.c - debugging-related code for Pass Through Authentication
+ *
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <sys/types.h>
+#include "passthru.h"
+
+#ifdef _WIN32
+int *module_ldap_debug = 0;
+
+void plugin_init_debug_level(int *level_ptr)
+{
+ module_ldap_debug = level_ptr;
+}
+#endif
diff --git a/ldap/servers/plugins/passthru/ptdllmain.c b/ldap/servers/plugins/passthru/ptdllmain.c
new file mode 100644
index 00000000..0e9eaccf
--- /dev/null
+++ b/ldap/servers/plugins/passthru/ptdllmain.c
@@ -0,0 +1,131 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+#include "ldap.h"
+#include "lber.h"
+#include "passthru.h"
+
+
+#ifdef _WIN32
+/* Lifted from Q125688
+ * How to Port a 16-bit DLL to a Win32 DLL
+ * on the MSVC 4.0 CD
+ */
+BOOL WINAPI DllMain (HANDLE hModule, DWORD fdwReason, LPVOID lpReserved)
+{
+ WSADATA wsadata;
+
+ switch (fdwReason)
+ {
+ case DLL_PROCESS_ATTACH:
+ /* Code from LibMain inserted here. Return TRUE to keep the
+ DLL loaded or return FALSE to fail loading the DLL.
+
+ You may have to modify the code in your original LibMain to
+ account for the fact that it may be called more than once.
+ You will get one DLL_PROCESS_ATTACH for each process that
+ loads the DLL. This is different from LibMain which gets
+ called only once when the DLL is loaded. The only time this
+ is critical is when you are using shared data sections.
+ If you are using shared data sections for statically
+ allocated data, you will need to be careful to initialize it
+ only once. Check your code carefully.
+
+ Certain one-time initializations may now need to be done for
+ each process that attaches. You may also not need code from
+ your original LibMain because the operating system may now
+ be doing it for you.
+ */
+ /*
+ * 16 bit code calls UnlockData()
+ * which is mapped to UnlockSegment in windows.h
+ * in 32 bit world UnlockData is not defined anywhere
+ * UnlockSegment is mapped to GlobalUnfix in winbase.h
+ * and the docs for both UnlockSegment and GlobalUnfix say
+ * ".. function is oboslete. Segments have no meaning
+ * in the 32-bit environment". So we do nothing here.
+ */
+
+ if( errno = WSAStartup(0x0101, &wsadata ) != 0 )
+ return FALSE;
+
+ break;
+
+ case DLL_THREAD_ATTACH:
+ /* Called each time a thread is created in a process that has
+ already loaded (attached to) this DLL. Does not get called
+ for each thread that exists in the process before it loaded
+ the DLL.
+
+ Do thread-specific initialization here.
+ */
+ break;
+
+ case DLL_THREAD_DETACH:
+ /* Same as above, but called when a thread in the process
+ exits.
+
+ Do thread-specific cleanup here.
+ */
+ break;
+
+ case DLL_PROCESS_DETACH:
+ /* Code from _WEP inserted here. This code may (like the
+ LibMain) not be necessary. Check to make certain that the
+ operating system is not doing it for you.
+ */
+ WSACleanup();
+
+ break;
+ }
+ /* The return value is only used for DLL_PROCESS_ATTACH; all other
+ conditions are ignored. */
+ return TRUE; // successful DLL_PROCESS_ATTACH
+}
+#else
+int CALLBACK
+LibMain( HINSTANCE hinst, WORD wDataSeg, WORD cbHeapSize, LPSTR lpszCmdLine )
+{
+ /*UnlockData( 0 );*/
+ return( 1 );
+}
+#endif
+
+#ifdef LDAP_DEBUG
+#ifndef _WIN32
+#include <stdarg.h>
+#include <stdio.h>
+
+void LDAPDebug( int level, char* fmt, ... )
+{
+ static char debugBuf[1024];
+
+ if (module_ldap_debug && (*module_ldap_debug & level))
+ {
+ va_list ap;
+ va_start (ap, fmt);
+ _snprintf (debugBuf, sizeof(debugBuf), fmt, ap);
+ va_end (ap);
+
+ OutputDebugString (debugBuf);
+ }
+}
+#endif
+#endif
+
+#ifndef _WIN32
+
+/* The 16-bit version of the RTL does not implement perror() */
+
+#include <stdio.h>
+
+void perror( const char *msg )
+{
+ char buf[128];
+ wsprintf( buf, "%s: error %d\n", msg, WSAGetLastError()) ;
+ OutputDebugString( buf );
+}
+
+#endif
diff --git a/ldap/servers/plugins/passthru/ptpreop.c b/ldap/servers/plugins/passthru/ptpreop.c
new file mode 100644
index 00000000..aaad0621
--- /dev/null
+++ b/ldap/servers/plugins/passthru/ptpreop.c
@@ -0,0 +1,252 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+/*
+ * ptpreop.c - bind pre-operation plugin for Pass Through Authentication
+ *
+ */
+
+#include "passthru.h"
+
+static Slapi_PluginDesc pdesc = { "passthruauth", PLUGIN_MAGIC_VENDOR_STR, PRODUCTTEXT,
+ "pass through authentication plugin" };
+
+/*
+ * function prototypes
+ */
+static int passthru_bindpreop( Slapi_PBlock *pb );
+static int passthru_bindpreop_start( Slapi_PBlock *pb );
+static int passthru_bindpreop_close( Slapi_PBlock *pb );
+
+
+/*
+ * Plugin initialization function (which must be listed in the appropriate
+ * slapd config file).
+ */
+int
+passthruauth_init( Slapi_PBlock *pb )
+{
+ PASSTHRU_ASSERT( pb != NULL );
+
+ slapi_log_error( SLAPI_LOG_PLUGIN, PASSTHRU_PLUGIN_SUBSYSTEM,
+ "=> passthruauth_init\n" );
+
+ if ( slapi_pblock_set( pb, SLAPI_PLUGIN_VERSION,
+ (void *)SLAPI_PLUGIN_VERSION_01 ) != 0
+ || slapi_pblock_set( pb, SLAPI_PLUGIN_DESCRIPTION,
+ (void *)&pdesc ) != 0
+ || slapi_pblock_set( pb, SLAPI_PLUGIN_START_FN,
+ (void *)passthru_bindpreop_start ) != 0
+ || slapi_pblock_set( pb, SLAPI_PLUGIN_PRE_BIND_FN,
+ (void *)passthru_bindpreop ) != 0
+ || slapi_pblock_set( pb, SLAPI_PLUGIN_CLOSE_FN,
+ (void *)passthru_bindpreop_close ) != 0 ) {
+ slapi_log_error( SLAPI_LOG_FATAL, PASSTHRU_PLUGIN_SUBSYSTEM,
+ "passthruauth_init failed\n" );
+ return( -1 );
+ }
+
+ slapi_log_error( SLAPI_LOG_PLUGIN, PASSTHRU_PLUGIN_SUBSYSTEM,
+ "<= passthruauth_init succeeded\n" );
+
+ return( 0 );
+}
+
+
+/*
+ * passthru_bindpreop_start() is called before the directory server
+ * is fully up. We parse our configuration and initialize any mutexes, etc.
+ */
+static int
+passthru_bindpreop_start( Slapi_PBlock *pb )
+{
+ int argc, rc;
+ char **argv;
+
+ PASSTHRU_ASSERT( pb != NULL );
+
+ slapi_log_error( SLAPI_LOG_PLUGIN, PASSTHRU_PLUGIN_SUBSYSTEM,
+ "=> passthru_bindpreop_start\n" );
+
+ if ( slapi_pblock_get( pb, SLAPI_PLUGIN_ARGC, &argc ) != 0 ||
+ slapi_pblock_get( pb, SLAPI_PLUGIN_ARGV, &argv ) != 0 ) {
+ slapi_log_error( SLAPI_LOG_FATAL, PASSTHRU_PLUGIN_SUBSYSTEM,
+ "unable to get arguments\n" );
+ return( -1 );
+ }
+
+ if (( rc = passthru_config( argc, argv )) != LDAP_SUCCESS ) {
+ slapi_log_error( SLAPI_LOG_FATAL, PASSTHRU_PLUGIN_SUBSYSTEM,
+ "configuration failed (%s)\n", ldap_err2string( rc ));
+ return( -1 );
+ }
+
+ return( 0 );
+}
+
+
+/*
+ * Called right before the Directory Server shuts down.
+ */
+static int
+passthru_bindpreop_close( Slapi_PBlock *pb )
+{
+ PASSTHRU_ASSERT( pb != NULL );
+
+ slapi_log_error( SLAPI_LOG_PLUGIN, PASSTHRU_PLUGIN_SUBSYSTEM,
+ "=> passthru_bindpreop_close\n" );
+
+ /*
+ * close all our open connections.
+ * XXXmcs: free any memory, mutexes, etc.
+ */
+ passthru_close_all_connections( passthru_get_config() );
+
+ return( 0 );
+}
+
+
+static int
+passthru_bindpreop( Slapi_PBlock *pb )
+{
+ int rc, method;
+ char *normbinddn, *matcheddn;
+ char *libldap_errmsg, *pr_errmsg, *errmsg;
+ PassThruConfig *cfg;
+ PassThruServer *srvr;
+ struct berval *creds, **urls;
+ LDAPControl **reqctrls, **resctrls;
+
+ PASSTHRU_ASSERT( pb != NULL );
+
+ slapi_log_error( SLAPI_LOG_PLUGIN, PASSTHRU_PLUGIN_SUBSYSTEM,
+ "=> passthru_bindpreop\n" );
+
+ /*
+ * retrieve parameters for bind operation
+ */
+ if ( slapi_pblock_get( pb, SLAPI_BIND_METHOD, &method ) != 0 ||
+ slapi_pblock_get( pb, SLAPI_BIND_TARGET, &normbinddn ) != 0 ||
+ slapi_pblock_get( pb, SLAPI_BIND_CREDENTIALS, &creds ) != 0 ) {
+ slapi_log_error( SLAPI_LOG_FATAL, PASSTHRU_PLUGIN_SUBSYSTEM,
+ "<= not handled (unable to retrieve bind parameters)\n" );
+ return( PASSTHRU_OP_NOT_HANDLED );
+ }
+ if ( normbinddn == NULL ) {
+ normbinddn = "";
+ }
+
+ /*
+ * We only handle simple bind requests that include non-NULL binddn and
+ * credentials. Let the Directory Server itself handle everything else.
+ */
+ if ( method != LDAP_AUTH_SIMPLE || *normbinddn == '\0'
+ || creds->bv_len == 0 ) {
+ slapi_log_error( SLAPI_LOG_PLUGIN, PASSTHRU_PLUGIN_SUBSYSTEM,
+ "<= not handled (not simple bind or NULL dn/credentials)\n" );
+ return( PASSTHRU_OP_NOT_HANDLED );
+ }
+
+ /*
+ * Get pass through authentication configuration.
+ */
+ cfg = passthru_get_config();
+
+ /*
+ * Check to see if the target DN is one we should "pass through" to
+ * another server.
+ */
+ if ( passthru_dn2server( cfg, normbinddn, &srvr ) != LDAP_SUCCESS ) {
+ slapi_log_error( SLAPI_LOG_PLUGIN, PASSTHRU_PLUGIN_SUBSYSTEM,
+ "<= not handled (not one of our suffixes)\n" );
+ return( PASSTHRU_OP_NOT_HANDLED );
+ }
+
+ /*
+ * We are now committed to handling this bind request.
+ * Chain it off to another server.
+ */
+ matcheddn = errmsg = libldap_errmsg = pr_errmsg = NULL;
+ urls = NULL;
+ resctrls = NULL;
+ if ( slapi_pblock_get( pb, SLAPI_REQCONTROLS, &reqctrls ) != 0 ) {
+ rc = LDAP_OPERATIONS_ERROR;
+ errmsg = "unable to retrieve bind controls";
+ slapi_log_error( SLAPI_LOG_FATAL, PASSTHRU_PLUGIN_SUBSYSTEM, "%s\n",
+ errmsg );
+ } else {
+ int lderrno;
+
+ if (( rc = passthru_simple_bind_s( pb, srvr, PASSTHRU_CONN_TRIES,
+ normbinddn, creds, reqctrls, &lderrno, &matcheddn,
+ &libldap_errmsg, &urls, &resctrls )) == LDAP_SUCCESS ) {
+ rc = lderrno;
+ errmsg = libldap_errmsg;
+ } else if ( rc != LDAP_USER_CANCELLED ) { /* not abandoned */
+ PRErrorCode prerr = PR_GetError();
+ pr_errmsg = PR_smprintf( "error %d - %s %s ("
+ SLAPI_COMPONENT_NAME_NSPR " error %d - %s)",
+ rc, ldap_err2string( rc ), srvr->ptsrvr_url,
+ prerr, slapd_pr_strerror(prerr));
+ if ( NULL != pr_errmsg ) {
+ errmsg = pr_errmsg;
+ } else {
+ errmsg = ldap_err2string( rc );
+ }
+ rc = LDAP_OPERATIONS_ERROR;
+ }
+ }
+
+ /*
+ * If bind succeeded, change authentication information associated
+ * with this connection.
+ */
+ if ( rc == LDAP_SUCCESS ) {
+ char *ndn = slapi_ch_strdup( normbinddn );
+ if (slapi_pblock_set(pb, SLAPI_CONN_DN, ndn) != 0 ||
+ slapi_pblock_set(pb, SLAPI_CONN_AUTHMETHOD,
+ SLAPD_AUTH_SIMPLE) != 0) {
+ slapi_ch_free((void **)&ndn);
+ rc = LDAP_OPERATIONS_ERROR;
+ errmsg = "unable to set connection DN or AUTHTYPE";
+ slapi_log_error( SLAPI_LOG_FATAL, PASSTHRU_PLUGIN_SUBSYSTEM,
+ "%s\n", errmsg );
+ }
+ }
+
+ if ( rc != LDAP_USER_CANCELLED ) { /* not abandoned */
+ /*
+ * Send a result to our client.
+ */
+ if ( resctrls != NULL ) {
+ (void)slapi_pblock_set( pb, SLAPI_RESCONTROLS, &resctrls );
+ }
+ slapi_send_ldap_result( pb, rc, matcheddn, errmsg, 0, urls );
+ }
+
+ /*
+ * Clean up -- free allocated memory, etc.
+ */
+ if ( urls != NULL ) {
+ passthru_free_bervals( urls );
+ }
+ if ( libldap_errmsg != NULL ) {
+ ldap_memfree( errmsg );
+ }
+ if ( pr_errmsg != NULL ) {
+ PR_smprintf_free( pr_errmsg );
+ }
+ if ( resctrls != NULL ) {
+ ldap_controls_free( resctrls );
+ }
+ if ( matcheddn != NULL ) {
+ ldap_memfree( matcheddn );
+ }
+
+ slapi_log_error( SLAPI_LOG_PLUGIN, PASSTHRU_PLUGIN_SUBSYSTEM,
+ "<= handled (error %d - %s)\n", rc, ldap_err2string( rc ));
+
+ return( PASSTHRU_OP_HANDLED );
+}
diff --git a/ldap/servers/plugins/passthru/ptutil.c b/ldap/servers/plugins/passthru/ptutil.c
new file mode 100644
index 00000000..4a1b307b
--- /dev/null
+++ b/ldap/servers/plugins/passthru/ptutil.c
@@ -0,0 +1,111 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+/*
+ * ptutil.c - utility functions for Pass Through Authentication
+ *
+ */
+
+#include "passthru.h"
+
+
+/*
+ * Convert a char * array into a struct berval * array.
+ * Always succeeds.
+ */
+struct berval **
+passthru_strs2bervals( char **ss )
+{
+ int i;
+ struct berval **bvs;
+
+ if ( ss == NULL || ss[0] == NULL ) {
+ return( NULL );
+ }
+
+ for ( i = 0; ss[i] != NULL; ++i ) {
+ ;
+ }
+
+ bvs = (struct berval **)slapi_ch_calloc( i + 1, sizeof( struct berval * ));
+ for ( i = 0; ss[i] != NULL; ++i ) {
+ bvs[i] = (struct berval *)slapi_ch_malloc( sizeof( struct berval ));
+ bvs[i]->bv_val = slapi_ch_strdup( ss[i] );
+ bvs[i]->bv_len = strlen( ss[i] );
+ }
+
+ return( bvs );
+}
+
+
+/*
+ * Convert a struct berval * array into a char * array.
+ * Always succeeds.
+ */
+char **
+passthru_bervals2strs( struct berval **bvs )
+{
+ int i;
+ char **strs;
+
+ if ( bvs == NULL || bvs[0] == NULL ) {
+ return( NULL );
+ }
+
+ for ( i = 0; bvs[i] != NULL; ++i ) {
+ ;
+ }
+
+ strs = (char **)slapi_ch_calloc( i + 1, sizeof( char * ));
+ for ( i = 0; bvs[i] != NULL; ++i ) {
+ strs[i] = slapi_ch_strdup( bvs[i]->bv_val );
+ }
+
+ return( strs );
+}
+
+
+void
+passthru_free_bervals( struct berval **bvs )
+{
+ int i;
+
+ if ( bvs != NULL ) {
+ for ( i = 0; bvs[ i ] != NULL; ++i ) {
+ slapi_ch_free( (void **)&bvs[ i ] );
+ }
+ }
+ slapi_ch_free( (void **)&bvs );
+}
+
+
+char *
+passthru_urlparse_err2string( int err )
+{
+ char *s;
+
+ switch( err ) {
+ case 0:
+ s = "no error";
+ break;
+ case LDAP_URL_ERR_NOTLDAP:
+ s = "missing ldap:// or ldaps://";
+ break;
+ case LDAP_URL_ERR_NODN:
+ s = "missing suffix";
+ break;
+ case LDAP_URL_ERR_BADSCOPE:
+ s = "invalid search scope";
+ break;
+ case LDAP_URL_ERR_MEM:
+ s = "unable to allocate memory";
+ break;
+ case LDAP_URL_ERR_PARAM:
+ s = "bad parameter to an LDAP URL function";
+ break;
+ }
+
+ return( s );
+}
diff --git a/ldap/servers/plugins/presence/Makefile b/ldap/servers/plugins/presence/Makefile
new file mode 100644
index 00000000..9b15bc96
--- /dev/null
+++ b/ldap/servers/plugins/presence/Makefile
@@ -0,0 +1,85 @@
+#
+# BEGIN COPYRIGHT BLOCK
+# Copyright 2001 Sun Microsystems, Inc.
+# Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+# All rights reserved.
+# END COPYRIGHT BLOCK
+#
+LDAP_SRC = ../../..
+MCOM_ROOT = ../../../../..
+
+NOSTDCLEAN=true # don't let nsconfig.mk define target clean
+NOSTDSTRIP=true # don't let nsconfig.mk define target strip
+NSPR20=true # probably should be defined somewhere else (not sure where)
+
+OBJDEST = $(OBJDIR)/lib/libpresence
+LIBDIR = $(LIB_RELDIR)
+
+include $(MCOM_ROOT)/ldapserver/nsdefs.mk
+include $(MCOM_ROOT)/ldapserver/nsconfig.mk
+include $(LDAP_SRC)/nsldap.mk
+
+ifeq ($(ARCH), WINNT)
+DEF_FILE:=./presence.def
+endif
+
+PRESENCE_OBJS = presence.o
+OBJS = $(addprefix $(OBJDEST)/, $(PRESENCE_OBJS))
+
+PRESENCE_DLL = presence-plugin
+
+INCLUDES += -I../http -I../../slapd -I../../../include
+CFLAGS+=$(SLCFLAGS) -DSLAPD_LOGGING
+
+ifeq ($(ARCH), WINNT)
+EXTRA_LIBS_DEP += $(LIBSLAPD) $(NSPR_DEP) $(LDAPSDK_DEP)
+EXTRA_LIBS_DEP += $(LDAP_COMMON_LIBS_DEP)
+EXTRA_LIBS += $(NSPRLINK) $(LIBSLAPD) $(LDAP_SDK_LIBLDAP_DLL)
+EXTRA_LIBS += $(LDAP_COMMON_LIBS)
+PRESENCE_DLL_OBJ = $(addprefix $(OBJDEST)/, dllmain.o)
+endif
+
+ifeq ($(ARCH), AIX)
+EXTRA_LIBS_DEP += $(LIBSLAPD) $(NSPR_DEP) $(LDAPSDK_DEP)
+EXTRA_LIBS_DEP += $(LDAP_COMMON_LIBS_DEP)
+EXTRA_LIBS += $(LIBSLAPDLINK) $(NSPRLINK) $(LDAP_SDK_LIBLDAP_DLL)
+EXTRA_LIBS += $(LDAP_COMMON_LIBS)
+LD=ld
+endif
+
+ifeq ($(ARCH), HPUX)
+EXTRA_LIBS_DEP += $(LIBSLAPD_DEP) $(LDAPSDK_DEP) $(NSPR_DEP) $(SECURITY_DEP)
+EXTRA_LIBS_DEP += $(LDAP_COMMON_LIBS_DEP)
+EXTRA_LIBS += $(DYN_NSHTTPD) $(ADMINUTIL_LINK) $(LDAPLINK) $(SECURITYLINK) $(NSPRLINK) $(ICULINK)
+EXTRA_LIBS += $(LDAP_COMMON_LIBS)
+endif
+
+PRESENCE= $(addprefix $(LIBDIR)/, $(PRESENCE_DLL).$(DLL_SUFFIX))
+
+clientSDK:
+
+all: $(OBJDEST) $(LIBDIR) $(PRESENCE)
+
+ifeq ($(ARCH), WINNT)
+$(PRESENCE): $(OBJS) $(PRESENCE_DLL_OBJ) $(DEF_FILE)
+ $(LINK_DLL) $(PRESENCE_DLL_OBJ) $(EXTRA_LIBS) /DEF:$(DEF_FILE)
+else
+$(PRESENCE): $(OBJS) $(PRESENCE_DLL_OBJ)
+ $(LINK_DLL) $(PRESENCE_DLL_OBJ) $(EXTRA_LIBS)
+endif
+
+
+veryclean: clean
+
+clean:
+ $(RM) $(OBJS)
+ifeq ($(ARCH), WINNT)
+ $(RM) $(PRESENCE_DLL_OBJ)
+endif
+ $(RM) $(PRESENCE)
+
+$(OBJDEST):
+ $(MKDIR) $(OBJDEST)
+
+$(LIBDIR):
+ $(MKDIR) $(LIBDIR)
diff --git a/ldap/servers/plugins/presence/dllmain.c b/ldap/servers/plugins/presence/dllmain.c
new file mode 100644
index 00000000..fabf8677
--- /dev/null
+++ b/ldap/servers/plugins/presence/dllmain.c
@@ -0,0 +1,96 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+/*
+ * Microsoft Windows specifics for BACK-LDBM DLL
+ */
+#include "ldap.h"
+#include "lber.h"
+
+
+#ifdef _WIN32
+/* Lifted from Q125688
+ * How to Port a 16-bit DLL to a Win32 DLL
+ * on the MSVC 4.0 CD
+ */
+BOOL WINAPI DllMain (HANDLE hModule, DWORD fdwReason, LPVOID lpReserved)
+{
+ WSADATA wsadata;
+
+ switch (fdwReason)
+ {
+ case DLL_PROCESS_ATTACH:
+ /* Code from LibMain inserted here. Return TRUE to keep the
+ DLL loaded or return FALSE to fail loading the DLL.
+
+ You may have to modify the code in your original LibMain to
+ account for the fact that it may be called more than once.
+ You will get one DLL_PROCESS_ATTACH for each process that
+ loads the DLL. This is different from LibMain which gets
+ called only once when the DLL is loaded. The only time this
+ is critical is when you are using shared data sections.
+ If you are using shared data sections for statically
+ allocated data, you will need to be careful to initialize it
+ only once. Check your code carefully.
+
+ Certain one-time initializations may now need to be done for
+ each process that attaches. You may also not need code from
+ your original LibMain because the operating system may now
+ be doing it for you.
+ */
+ /*
+ * 16 bit code calls UnlockData()
+ * which is mapped to UnlockSegment in windows.h
+ * in 32 bit world UnlockData is not defined anywhere
+ * UnlockSegment is mapped to GlobalUnfix in winbase.h
+ * and the docs for both UnlockSegment and GlobalUnfix say
+ * ".. function is oboslete. Segments have no meaning
+ * in the 32-bit environment". So we do nothing here.
+ */
+
+ if( errno = WSAStartup(0x0101, &wsadata ) != 0 )
+ return FALSE;
+
+ break;
+
+ case DLL_THREAD_ATTACH:
+ /* Called each time a thread is created in a process that has
+ already loaded (attached to) this DLL. Does not get called
+ for each thread that exists in the process before it loaded
+ the DLL.
+
+ Do thread-specific initialization here.
+ */
+ break;
+
+ case DLL_THREAD_DETACH:
+ /* Same as above, but called when a thread in the process
+ exits.
+
+ Do thread-specific cleanup here.
+ */
+ break;
+
+ case DLL_PROCESS_DETACH:
+ /* Code from _WEP inserted here. This code may (like the
+ LibMain) not be necessary. Check to make certain that the
+ operating system is not doing it for you.
+ */
+ WSACleanup();
+
+ break;
+ }
+ /* The return value is only used for DLL_PROCESS_ATTACH; all other
+ conditions are ignored. */
+ return TRUE; // successful DLL_PROCESS_ATTACH
+}
+#else
+int CALLBACK
+LibMain( HINSTANCE hinst, WORD wDataSeg, WORD cbHeapSize, LPSTR lpszCmdLine )
+{
+ /*UnlockData( 0 );*/
+ return( 1 );
+}
+#endif
diff --git a/ldap/servers/plugins/presence/images/aim-offline.gif b/ldap/servers/plugins/presence/images/aim-offline.gif
new file mode 100644
index 00000000..7403ea29
--- /dev/null
+++ b/ldap/servers/plugins/presence/images/aim-offline.gif
Binary files differ
diff --git a/ldap/servers/plugins/presence/images/aim-online.gif b/ldap/servers/plugins/presence/images/aim-online.gif
new file mode 100644
index 00000000..d90c2910
--- /dev/null
+++ b/ldap/servers/plugins/presence/images/aim-online.gif
Binary files differ
diff --git a/ldap/servers/plugins/presence/images/icq-disabled.gif b/ldap/servers/plugins/presence/images/icq-disabled.gif
new file mode 100644
index 00000000..78b748cd
--- /dev/null
+++ b/ldap/servers/plugins/presence/images/icq-disabled.gif
Binary files differ
diff --git a/ldap/servers/plugins/presence/images/icq-offline.gif b/ldap/servers/plugins/presence/images/icq-offline.gif
new file mode 100644
index 00000000..40b35c16
--- /dev/null
+++ b/ldap/servers/plugins/presence/images/icq-offline.gif
Binary files differ
diff --git a/ldap/servers/plugins/presence/images/icq-online.gif b/ldap/servers/plugins/presence/images/icq-online.gif
new file mode 100644
index 00000000..bd5452c1
--- /dev/null
+++ b/ldap/servers/plugins/presence/images/icq-online.gif
Binary files differ
diff --git a/ldap/servers/plugins/presence/images/yahoo-offline.gif b/ldap/servers/plugins/presence/images/yahoo-offline.gif
new file mode 100644
index 00000000..315a2261
--- /dev/null
+++ b/ldap/servers/plugins/presence/images/yahoo-offline.gif
Binary files differ
diff --git a/ldap/servers/plugins/presence/images/yahoo-online.gif b/ldap/servers/plugins/presence/images/yahoo-online.gif
new file mode 100644
index 00000000..1c2b16d8
--- /dev/null
+++ b/ldap/servers/plugins/presence/images/yahoo-online.gif
Binary files differ
diff --git a/ldap/servers/plugins/presence/presence.c b/ldap/servers/plugins/presence/presence.c
new file mode 100644
index 00000000..6c75e745
--- /dev/null
+++ b/ldap/servers/plugins/presence/presence.c
@@ -0,0 +1,1204 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+
+/**
+ * IM Presence plug-in
+ */
+#include <stdio.h>
+#include <ctype.h>
+#include <string.h>
+#include "portable.h"
+#include "nspr.h"
+#include "slapi-plugin.h"
+#include "slapi-private.h"
+#include "dirlite_strings.h"
+#include "dirver.h"
+#include "vattr_spi.h"
+#include "plhash.h"
+#include "ldif.h"
+
+#include "http_client.h"
+
+/* get file mode flags for unix */
+#ifndef _WIN32
+#include <sys/stat.h>
+#endif
+
+/*** from proto-slap.h ***/
+
+int slapd_log_error_proc( char *subsystem, char *fmt, ... );
+
+/*** from ldaplog.h ***/
+
+/* edited ldaplog.h for LDAPDebug()*/
+#ifndef _LDAPLOG_H
+#define _LDAPLOG_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define LDAP_DEBUG_TRACE 0x00001 /* 1 */
+#define LDAP_DEBUG_ANY 0x04000 /* 16384 */
+#define LDAP_DEBUG_PLUGIN 0x10000 /* 65536 */
+
+/* debugging stuff */
+# ifdef _WIN32
+ extern int *module_ldap_debug;
+# define LDAPDebugLevelIsSet( level ) ( *module_ldap_debug & level )
+# else /* _WIN32 */
+ extern int slapd_ldap_debug;
+# define LDAPDebugLevelIsSet( level ) ( slapd_ldap_debug & level )
+# endif /* Win32 */
+
+#define LDAPDebug( level, fmt, arg1, arg2, arg3 ) \
+ { \
+ if ( LDAPDebugLevelIsSet( level )) { \
+ slapd_log_error_proc( NULL, fmt, arg1, arg2, arg3 ); \
+ } \
+ }
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _LDAP_H */
+
+#define PRESENCE_PLUGIN_SUBSYSTEM "presence-plugin"
+#define PRESENCE_PLUGIN_VERSION 0x00050050
+
+/**
+ * this may become unnecessary when we are able to get
+ * the plug-in DN dynamically (pete?)
+ */
+#define PRESENCE_DN "cn=Presence,cn=plugins,cn=config" /* temporary */
+
+#define PRESENCE_SUCCESS 0
+#define PRESENCE_FAILURE -1
+
+/**
+ * Presence vendor specific config parameters
+ */
+
+#define NS_IM_ID "nsIM-ID"
+
+#define NS_IM_URL_TEXT "nsIM-URLText"
+#define NS_IM_URL_GRAPHIC "nsIM-URLGraphic"
+
+#define NS_IM_ON_VALUE_MAP_TEXT "nsIM-OnValueMapText"
+#define NS_IM_OFF_VALUE_MAP_TEXT "nsIM-OffValueMapText"
+
+#define NS_IM_ON_VALUE_MAP_GRAPHIC "nsIM-OnValueMapGraphic"
+#define NS_IM_OFF_VALUE_MAP_GRAPHIC "nsIM-OffValueMapGraphic"
+#define NS_IM_DISABLED_VALUE_MAP_GRAPHIC "nsIM-disabledValueMapGraphic"
+
+#define NS_IM_REQUEST_METHOD "nsIM-RequestMethod"
+
+#define NS_IM_URL_TEXT_RETURN_TYPE "nsIM-URLTextReturnType"
+#define NS_IM_URL_GRAPHIC_RETURN_TYPE "nsIM-URLGraphicReturnType"
+
+#define NS_IM_STATUS_TEXT "nsIM-StatusText"
+#define NS_IM_STATUS_GRAPHIC "nsIM-StatusGraphic"
+
+#define PRESENCE_STRING 1
+#define PRESENCE_BINARY 2
+
+#define PRESENCE_TEXT_RETURN_TYPE "TEXT"
+#define PRESENCE_BINARY_RETURN_TYPE "BINARY"
+
+#define PRESENCE_REQUEST_METHOD_GET "GET"
+#define PRESENCE_REQUEST_METHOD_REDIRECT "REDIRECT"
+
+#define PRESENCE_RETURNED_ON_TEXT "ONLINE"
+#define PRESENCE_RETURNED_OFF_TEXT "OFFLINE"
+#define PRESENCE_RETURNED_ERROR_TEXT "ERROR"
+
+static Slapi_PluginDesc pdesc = { "IM Presence",
+ PLUGIN_MAGIC_VENDOR_STR,
+ PRODUCTTEXT,
+ "presence plugin" };
+
+/**
+ * struct used to pass the argument to PL_Enumerator Callback
+ */
+struct _vattrtypes
+{
+ Slapi_Entry *entry;
+ vattr_type_list_context *context;
+};
+
+/**
+ * This structure holds the mapping between the virtual attributes and
+ * the IM IDs. This information is used to find out whether this plugin
+ * should service the attributes it was asked to. Also, it stores the
+ * syntax of the attribute. 1 is String and 2 is binary.
+ */
+struct _vattrmap {
+ char *imID;
+ int syntax;
+};
+typedef struct _vattrmap _Vmap;
+
+/**
+ * struct to store the config values for each presence vendor
+ */
+struct _defs {
+ char *textURL;
+ char *graphicURL;
+ char *onTextMap;
+ char *offTextMap;
+ Slapi_Attr *onGraphicMap;
+ Slapi_Attr *offGraphicMap;
+ Slapi_Attr *disabledGraphicMap;
+ char *requestMethod;
+ char *textReturnType;
+ char *graphicReturnType;
+};
+typedef struct _defs _ConfigEntry;
+
+static vattr_sp_handle *_VattrHandle = NULL;
+static void *_PluginID = NULL;
+static void *_PluginDN = NULL;
+static PLHashTable *_IdVattrMapTable = NULL;
+static PLHashTable *_IdConfigMapTable = NULL;
+static void **_HttpAPI = NULL;
+
+/**
+ *
+ * Presence plug-in management functions
+ *
+ */
+int presence_init(Slapi_PBlock *pb);
+int presence_start(Slapi_PBlock *pb);
+int presence_close(Slapi_PBlock *pb);
+
+/**
+ *
+ * Vattr operation callbacks functions
+ *
+ */
+static int presence_vattr_get(vattr_sp_handle *handle, vattr_context *c, Slapi_Entry *e, char *type, Slapi_ValueSet** results,int *type_name_disposition, char** actual_type_name, int flags, int *free_flags, void *hint);
+static int presence_vattr_compare(vattr_sp_handle *handle, vattr_context *c, Slapi_Entry *e, char *type, Slapi_Value *test_this, int* result, int flags, void *hint);
+static int presence_vattr_types(vattr_sp_handle *handle,Slapi_Entry *e,vattr_type_list_context *type_context,int flags);
+
+/**
+ *
+ * Local operation functions
+ *
+ */
+static int loadPluginConfig();
+static int parseConfigEntry(Slapi_Entry *e);
+static int imIDExists(Slapi_Entry *e, char *type, char **value, _Vmap **map, _ConfigEntry **entry);
+static int makeHttpRequest(char *id, _Vmap *map, _ConfigEntry *info, char **buf, int *size);
+static char * replaceIdWithValue(char *str, char *id, char *value);
+static int setIMStatus(char *id, _Vmap *map, _ConfigEntry *info, char *returnedBUF, int size, Slapi_ValueSet **results);
+static int setTypes(PLHashEntry *he, PRIntn i, void *arg);
+
+static void deleteMapTables();
+static PRIntn destroyHashEntry(PLHashEntry *he, PRIntn index, void *arg);
+static void logGraphicAttributeValue( Slapi_Attr *attr, const char *attrname );
+static void toLowerCase(char* str);
+
+/**
+ * utility function
+ */
+void printMapTable();
+PRIntn printIdVattrMapTable(PLHashEntry *he, PRIntn i, void *arg);
+PRIntn printIdConfigMapTable(PLHashEntry *he, PRIntn i, void *arg);
+
+/**
+ * set the debug level
+ */
+#ifdef _WIN32
+int *module_ldap_debug = 0;
+
+void plugin_init_debug_level(int *level_ptr)
+{
+ module_ldap_debug = level_ptr;
+}
+#endif
+
+/**
+ *
+ * Get the presence plug-in version
+ *
+ */
+int presence_version()
+{
+ return PRESENCE_PLUGIN_VERSION;
+}
+
+/**
+ * Plugin identity mgmt
+ */
+void setPluginID(void * pluginID)
+{
+ _PluginID=pluginID;
+}
+
+void * getPluginID()
+{
+ return _PluginID;
+}
+
+void setPluginDN(void *pluginDN)
+{
+ _PluginDN = pluginDN;
+}
+
+void * getPluginDN()
+{
+ return _PluginDN;
+}
+
+/*
+ presence_init
+ -------------
+ adds our callbacks to the list
+*/
+int presence_init( Slapi_PBlock *pb )
+{
+ int status = PRESENCE_SUCCESS;
+ char * plugin_identity=NULL;
+
+ LDAPDebug( LDAP_DEBUG_PLUGIN, "--> presence_init -- BEGIN\n",0,0,0);
+
+ /**
+ * Store the plugin identity for later use.
+ * Used for internal operations
+ */
+
+ slapi_pblock_get (pb, SLAPI_PLUGIN_IDENTITY, &plugin_identity);
+ PR_ASSERT (plugin_identity);
+ setPluginID(plugin_identity);
+
+ if ( slapi_pblock_set( pb, SLAPI_PLUGIN_VERSION,
+ SLAPI_PLUGIN_VERSION_01 ) != 0 ||
+ slapi_pblock_set(pb, SLAPI_PLUGIN_START_FN,
+ (void *) presence_start ) != 0 ||
+ slapi_pblock_set(pb, SLAPI_PLUGIN_CLOSE_FN,
+ (void *) presence_close ) != 0 ||
+ slapi_pblock_set( pb, SLAPI_PLUGIN_DESCRIPTION,
+ (void *)&pdesc ) != 0 )
+ {
+ slapi_log_error( SLAPI_LOG_FATAL, PRESENCE_PLUGIN_SUBSYSTEM,
+ "presence_init: failed to register plugin\n" );
+ status = PRESENCE_FAILURE;
+ }
+
+ LDAPDebug( LDAP_DEBUG_PLUGIN, "<-- presence_init -- END\n",0,0,0);
+ return status;
+}
+
+/*
+ presence_start
+ --------------
+ This function registers the computed attribute evaluator
+ and loads the configuration parameters in the local cache.
+ It is called after presence_init.
+*/
+int presence_start( Slapi_PBlock *pb )
+{
+ char * plugindn = NULL;
+ char * httpRootDir = NULL;
+
+ LDAPDebug( LDAP_DEBUG_PLUGIN, "--> presence_start -- begin\n",0,0,0);
+
+ if(slapi_apib_get_interface(HTTP_v1_0_GUID, &_HttpAPI))
+ {
+ /**
+ * error cannot proceeed
+ */
+ return PRESENCE_FAILURE;
+ }
+
+ /**
+ * register our vattr callbacks
+ */
+ if (slapi_vattrspi_register((vattr_sp_handle **)&_VattrHandle,
+ presence_vattr_get,
+ presence_vattr_compare,
+ presence_vattr_types) != 0)
+ {
+ slapi_log_error( SLAPI_LOG_FATAL, PRESENCE_PLUGIN_SUBSYSTEM,
+ "presence_start: cannot register as service provider\n" );
+ return PRESENCE_FAILURE;
+ }
+
+ /**
+ * Get the plug-in target dn from the system
+ * and store it for future use. This should avoid
+ * hardcoding of DN's in the code.
+ */
+ slapi_pblock_get(pb, SLAPI_TARGET_DN, &plugindn);
+ if (plugindn == NULL || strlen(plugindn) == 0)
+ {
+ /**
+ * This is not required as the above statement
+ * should work and give you a valid DN for this
+ * plugin. ??? remove it later
+ */
+ plugindn = PRESENCE_DN;
+ }
+ setPluginDN(plugindn);
+
+ /**
+ * Load the config info for our plug-in in memory
+ * In the 6.0 release this information will be stored
+ * statically and if any change is done to this info a server
+ * restart is necessary :-(. Probably if time permits then
+ * state change plug-in would be used to notify the state
+ * change. We also register the virtual attributes we are
+ * interested in here.
+ */
+ if (loadPluginConfig() != PRESENCE_SUCCESS)
+ {
+ slapi_log_error( SLAPI_LOG_FATAL, PRESENCE_PLUGIN_SUBSYSTEM,
+ "presence_start: unable to load plug-in configuration\n" );
+ return PRESENCE_FAILURE;
+ }
+
+ LDAPDebug( LDAP_DEBUG_PLUGIN, "presence: ready for service\n",0,0,0);
+ LDAPDebug( LDAP_DEBUG_PLUGIN, "<-- presence_start -- end\n",0,0,0);
+
+ return PRESENCE_SUCCESS;
+}
+
+/*
+ presence_close
+ --------------
+ closes down the cache
+*/
+int presence_close( Slapi_PBlock *pb )
+{
+ LDAPDebug( LDAP_DEBUG_PLUGIN, "--> presence_close\n",0,0,0);
+
+ deleteMapTables();
+
+ LDAPDebug( LDAP_DEBUG_PLUGIN, "<-- presence_close\n",0,0,0);
+
+ return PRESENCE_SUCCESS;
+}
+
+static int presence_vattr_get(vattr_sp_handle *handle,
+ vattr_context *c,
+ Slapi_Entry *e,
+ char *type,
+ Slapi_ValueSet** results,
+ int *type_name_disposition,
+ char** actual_type_name,
+ int flags,
+ int *free_flags,
+ void *hint)
+{
+
+ int status = PRESENCE_SUCCESS;
+ char *id = NULL;
+ char *returnedBUF = NULL;
+ int size = 0;
+ _Vmap *map = NULL;
+ _ConfigEntry *info = NULL;
+
+ LDAPDebug( LDAP_DEBUG_PLUGIN, "--> presence_vattr_get \n",0,0,0);
+
+ LDAPDebug( LDAP_DEBUG_PLUGIN, "----------> Type=[%s] \n",type,0,0);
+
+ if (imIDExists(e, type, &id, &map, &info) != PRESENCE_SUCCESS)
+ {
+ /**
+ * we didn't find any valid matching nsimid in this
+ * entry so since we cannot process a request without
+ * a valid nsimid we just return.
+ */
+ status = PRESENCE_FAILURE;
+ goto cleanup;
+ }
+ LDAPDebug( LDAP_DEBUG_PLUGIN, "----------> ID=[%s] \n",id,0,0);
+
+ /**
+ * Now since we got a valid id we do a quick schema check
+ * if schema checking is on to make sure that there is no
+ * schema violation ?
+ */
+ /* do_schema_check() */
+
+ /**
+ * At this stage we have a valid attribute and we have to
+ * get its value from the IM Server. so make an Http request
+ * depending on whether it is a request for Text or Graphic
+ */
+
+ status = makeHttpRequest(id, map, info, &returnedBUF, &size);
+
+ LDAPDebug( LDAP_DEBUG_PLUGIN, "----------> size=[%d] \n",size,0,0);
+ LDAPDebug( LDAP_DEBUG_PLUGIN, "----------> buffer=[%s]\n",(returnedBUF) ? returnedBUF : "NULL",0,0);
+
+
+ if(status == PRESENCE_SUCCESS)
+ {
+ status = setIMStatus(id, map, info, returnedBUF, size, results);
+ }
+ else
+ {
+ /**
+ * Report all HTTP failures as a single predefined value of the
+ * attribute
+ */
+ Slapi_Value *value =
+ slapi_value_new_string(PRESENCE_RETURNED_ERROR_TEXT);
+ if (!*results) {
+ *results = slapi_valueset_new();
+ }
+ slapi_valueset_add_value(*results, value);
+ slapi_value_free(&value); /* slapi_valueset_add_value copies value */
+ /**
+ * It's a success only in the sense that we are returning a value
+ */
+ status = PRESENCE_SUCCESS;
+ }
+ if(status == PRESENCE_SUCCESS)
+ {
+ *free_flags = SLAPI_VIRTUALATTRS_RETURNED_COPIES;
+ *actual_type_name = slapi_ch_strdup(type);
+ *type_name_disposition = SLAPI_VIRTUALATTRS_TYPE_NAME_MATCHED_EXACTLY_OR_ALIAS;
+ }
+
+cleanup:
+ LDAPDebug( LDAP_DEBUG_PLUGIN, "----------> Processed ID=[%s] \n",id,0,0);
+ if (id != NULL ) {
+ slapi_ch_free((void **)&id);
+ }
+ if (returnedBUF != NULL ) {
+ PR_Free(returnedBUF);
+ }
+ LDAPDebug( LDAP_DEBUG_PLUGIN, "<-- presence_vattr_get \n",0,0,0);
+ return status;
+}
+
+
+static int presence_vattr_compare(vattr_sp_handle *handle, vattr_context *c, Slapi_Entry *e, char *type, Slapi_Value *test_this, int* result, int flags, void *hint)
+{
+ int status = PRESENCE_SUCCESS;
+ /**
+ * not yet implemented ???
+ */
+ LDAPDebug( LDAP_DEBUG_PLUGIN, "--> presence_vattr_compare \n",0,0,0);
+ LDAPDebug( LDAP_DEBUG_PLUGIN, "<-- presence_vattr_compare \n",0,0,0);
+
+ return status;
+}
+
+static int presence_vattr_types(vattr_sp_handle *handle,Slapi_Entry *e,vattr_type_list_context *type_context,int flags)
+{
+ int status = PRESENCE_SUCCESS;
+ struct _vattrtypes args;
+ args.entry = e;
+ args.context = type_context;
+
+ LDAPDebug( LDAP_DEBUG_PLUGIN, "--> presence_vattr_types\n",0,0,0);
+
+ PL_HashTableEnumerateEntries(_IdVattrMapTable, setTypes, &args);
+
+ LDAPDebug( LDAP_DEBUG_PLUGIN, "<-- presence_vattr_types\n",0,0,0);
+ return status;
+}
+
+static int loadPluginConfig()
+{
+ int status = PRESENCE_SUCCESS;
+ int result;
+ int i;
+ Slapi_PBlock *search_pb;
+ Slapi_Entry **entries = NULL;
+
+ LDAPDebug( LDAP_DEBUG_PLUGIN, "--> loadPluginConfig\n",0,0,0);
+
+ search_pb = slapi_pblock_new();
+
+ slapi_search_internal_set_pb(search_pb, PRESENCE_DN, LDAP_SCOPE_ONELEVEL,
+ "objectclass=*", NULL, 0, NULL, NULL, getPluginID(), 0);
+ slapi_search_internal_pb(search_pb);
+ slapi_pblock_get(search_pb, SLAPI_PLUGIN_INTOP_RESULT, &result);
+
+ if (status != PRESENCE_SUCCESS)
+ {
+ slapi_log_error(SLAPI_LOG_FATAL, PRESENCE_PLUGIN_SUBSYSTEM,
+ "Error getting level1 presence configurations<%s>\n", getPluginDN());
+ status = PRESENCE_FAILURE;
+ goto cleanup;
+ }
+
+ slapi_pblock_get(search_pb, SLAPI_PLUGIN_INTOP_SEARCH_ENTRIES, &entries);
+ if (NULL == entries || entries[0] == NULL)
+ {
+ slapi_log_error(SLAPI_LOG_FATAL, PRESENCE_PLUGIN_SUBSYSTEM,
+ "No entries found for <%s>\n", getPluginDN());
+
+ status = PRESENCE_FAILURE;
+ goto cleanup;
+ }
+
+ _IdVattrMapTable = PL_NewHashTable( 0,
+ PL_HashString,
+ PL_CompareStrings,
+ PL_CompareValues,
+ NULL,
+ NULL
+ );
+
+ _IdConfigMapTable = PL_NewHashTable(0,
+ PL_HashString,
+ PL_CompareStrings,
+ PL_CompareValues,
+ NULL,
+ NULL
+ );
+
+ LDAPDebug( LDAP_DEBUG_PLUGIN, "--> parseConfigEntry \n",0,0,0);
+
+ for (i = 0; (entries[i] != NULL); i++)
+ {
+ status = parseConfigEntry(entries[i]);
+ if (status != PRESENCE_SUCCESS)
+ {
+ deleteMapTables();
+ goto cleanup;
+ }
+ }
+ LDAPDebug( LDAP_DEBUG_PLUGIN, "<-- parseConfigEntry \n",0,0,0);
+
+ LDAPDebug( LDAP_DEBUG_PLUGIN, "<-- loadPluginConfig\n",0,0,0);
+
+cleanup:
+ slapi_free_search_results_internal(search_pb);
+ slapi_pblock_destroy(search_pb);
+ return status;
+}
+
+static int parseConfigEntry(Slapi_Entry *e)
+{
+ char *key = NULL;
+ char *value = NULL;
+ _ConfigEntry *entry = NULL;
+ _Vmap *map = NULL;
+ Slapi_Attr *attr = NULL;
+
+ key = slapi_entry_attr_get_charptr(e, NS_IM_ID);
+ if (!key) {
+ /**
+ * IM Id not defined in the config, unfortunately
+ * cannot do anything without it so better not to
+ * load the plug-in.
+ */
+ return PRESENCE_FAILURE;
+ }
+ LDAPDebug( LDAP_DEBUG_PLUGIN, "----------> key [%s] \n",key,0,0);
+ /**
+ * Now create the config entry which will hold all the
+ * attributes of a presence vendor
+ */
+ entry = (_ConfigEntry*) slapi_ch_calloc(1, sizeof(_ConfigEntry));
+
+ /**
+ * Next 2 are the virtual attributes for which this plug-in
+ * is responsible. Register them with the vattr system so
+ * that the system can call us whenever their
+ * values are requested. Also update these entries in the
+ * map table for later access.
+ */
+ value = slapi_entry_attr_get_charptr(e, NS_IM_STATUS_TEXT);
+ if (value) {
+ slapi_vattrspi_regattr(_VattrHandle, value, "", NULL);
+ map = (_Vmap*) slapi_ch_calloc(1, sizeof(_Vmap));
+ map->imID = key;
+ map->syntax = PRESENCE_STRING;
+ toLowerCase(value);
+ PL_HashTableAdd(_IdVattrMapTable, value, map);
+ }
+ LDAPDebug( LDAP_DEBUG_PLUGIN, "----------> nsIMStatusText [%s] \n",value,0,0);
+
+ value = slapi_entry_attr_get_charptr(e, NS_IM_STATUS_GRAPHIC);
+ if (value) {
+ slapi_vattrspi_regattr(_VattrHandle, value, "", NULL);
+ map = (_Vmap*) slapi_ch_calloc(1, sizeof(_Vmap));
+ map->imID = key;
+ map->syntax = PRESENCE_BINARY;
+ toLowerCase(value);
+ PL_HashTableAdd(_IdVattrMapTable, value, map);
+ }
+
+ LDAPDebug( LDAP_DEBUG_PLUGIN, "----------> nsIMStatusGraphic [%s] \n",value,0,0);
+
+ value = slapi_entry_attr_get_charptr(e, NS_IM_URL_TEXT);
+ if (value) {
+ entry->textURL = value;
+ }
+
+ LDAPDebug( LDAP_DEBUG_PLUGIN, "----------> nsIMURLText [%s] \n",value,0,0);
+
+ value = slapi_entry_attr_get_charptr(e, NS_IM_URL_GRAPHIC);
+ if (value) {
+ entry->graphicURL = value;
+ }
+
+ LDAPDebug( LDAP_DEBUG_PLUGIN, "----------> nsIMStatusGraphic [%s] \n",value,0,0);
+
+ value = slapi_entry_attr_get_charptr(e, NS_IM_ON_VALUE_MAP_TEXT);
+ if (value) {
+ entry->onTextMap = value;
+ }
+
+ LDAPDebug( LDAP_DEBUG_PLUGIN, "----------> nsIMOnValueMapText [%s] \n",value,0,0);
+
+ value = slapi_entry_attr_get_charptr(e, NS_IM_OFF_VALUE_MAP_TEXT);
+ if (value) {
+ entry->offTextMap = value;
+ }
+
+ LDAPDebug( LDAP_DEBUG_PLUGIN, "----------> nsIMOffValueMapText [%s] \n",value,0,0);
+
+ /**
+ * Next 3 are binary syntax types so needs special handling
+ */
+ slapi_entry_attr_find(e, NS_IM_ON_VALUE_MAP_GRAPHIC, &attr);
+ if (attr) {
+ entry->onGraphicMap = slapi_attr_dup(attr);
+ logGraphicAttributeValue(attr,NS_IM_ON_VALUE_MAP_GRAPHIC);
+ }
+
+ slapi_entry_attr_find(e, NS_IM_OFF_VALUE_MAP_GRAPHIC, &attr);
+ if (attr) {
+ entry->offGraphicMap = slapi_attr_dup(attr);
+ logGraphicAttributeValue(attr,NS_IM_OFF_VALUE_MAP_GRAPHIC);
+ }
+
+ slapi_entry_attr_find(e, NS_IM_DISABLED_VALUE_MAP_GRAPHIC, &attr);
+ if (attr) {
+ entry->disabledGraphicMap = slapi_attr_dup(attr);
+ logGraphicAttributeValue(attr,NS_IM_DISABLED_VALUE_MAP_GRAPHIC);
+ }
+
+ value = slapi_entry_attr_get_charptr(e, NS_IM_REQUEST_METHOD);
+ if (value) {
+ entry->requestMethod = value;
+ }
+
+ LDAPDebug( LDAP_DEBUG_PLUGIN, "----------> nsIMRequestMethod [%s] \n",value,0,0);
+
+ value = slapi_entry_attr_get_charptr(e, NS_IM_URL_TEXT_RETURN_TYPE);
+ if (value) {
+ entry->textReturnType = value;
+ }
+
+ LDAPDebug( LDAP_DEBUG_PLUGIN, "----------> nsIMURLTextReturnType [%s] \n",value,0,0);
+
+ value = slapi_entry_attr_get_charptr(e, NS_IM_URL_GRAPHIC_RETURN_TYPE);
+ if (value) {
+ entry->graphicReturnType = value;
+ }
+ LDAPDebug( LDAP_DEBUG_PLUGIN, "----------> nsIMURLGraphicReturnType [%s] \n",value,0,0);
+
+ /**
+ * Finally add the entry to the map table
+ */
+ PL_HashTableAdd(_IdConfigMapTable, key, entry);
+
+ return PRESENCE_SUCCESS;
+}
+
+/**
+ * this function goes thru the valid stored ids
+ * and return the correct one for which we have to
+ * do further processing
+ */
+static int imIDExists(Slapi_Entry *e, char *type, char **value, _Vmap **map, _ConfigEntry **entry)
+{
+ int status = PRESENCE_SUCCESS;
+ char *tValue = NULL;
+ _ConfigEntry *tEntry = NULL;
+ _Vmap *tMap = NULL;
+
+ LDAPDebug( LDAP_DEBUG_PLUGIN, "--> imIDExists \n",0,0,0);
+ LDAPDebug( LDAP_DEBUG_PLUGIN, "----------> Type [%s] \n",type,0,0);
+
+ /**
+ * The public function PL_HashTableLookup modifies the
+ * the table while reading. so using this private function
+ * which just does a lookup and doesn't modifies the
+ * hashtable
+ */
+ toLowerCase(type);
+ tMap = PL_HashTableLookupConst(_IdVattrMapTable, type);
+ if (!tMap)
+ {
+ /**
+ * this should not happen but no harm we just return
+ */
+ status = PRESENCE_FAILURE;
+ slapi_log_error(SLAPI_LOG_FATAL, PRESENCE_PLUGIN_SUBSYSTEM,
+ "No hashtable for vattr types\n");
+ goto bail;
+ }
+ /**
+ * We found a matching id in the map table
+ * now see if that id exists in the Slapi_Entry
+ */
+ tValue = slapi_entry_attr_get_charptr(e, tMap->imID);
+ if (!tValue)
+ {
+ /**
+ * we don't do anything here but just return
+ */
+ status = PRESENCE_FAILURE;
+ goto bail;
+ }
+ LDAPDebug( LDAP_DEBUG_PLUGIN, "----------> Value [%s] \n",tValue,0,0);
+
+ tEntry = PL_HashTableLookupConst(_IdConfigMapTable, tMap->imID);
+ *value = tValue;
+ *entry = tEntry;
+ *map = tMap;
+
+ LDAPDebug( LDAP_DEBUG_PLUGIN, "<-- imIDExists \n",0,0,0);
+
+bail:
+ return status;
+}
+
+static int makeHttpRequest(char *id, _Vmap *map, _ConfigEntry *info, char **BUF, int *size)
+{
+ int status = PRESENCE_SUCCESS;
+ char *buf = NULL;
+ char *url = NULL;
+ char *urltosend = NULL;
+ int bytesRead;
+
+ LDAPDebug( LDAP_DEBUG_PLUGIN, "--> makeHttpRequest:: \n",0,0,0);
+
+ if (map->syntax == PRESENCE_STRING) {
+ url = info->textURL;
+ } else {
+ url = info->graphicURL;
+ }
+ if (url == NULL) {
+ status = PRESENCE_FAILURE;
+ goto bail;
+ }
+ urltosend = replaceIdWithValue(url, map->imID, id);
+
+ LDAPDebug( LDAP_DEBUG_PLUGIN, "----------> URL [%s] \n",urltosend,0,0);
+ /**
+ * make an actual HTTP call now
+ */
+ LDAPDebug( LDAP_DEBUG_PLUGIN, "----------> RequestMethod [%s] \n", info->requestMethod,0,0);
+ LDAPDebug( LDAP_DEBUG_PLUGIN, "----------> Syntax [%d] \n", map->syntax,0,0);
+ LDAPDebug( LDAP_DEBUG_PLUGIN, "----------> TextReturnType [%s] \n", info->textReturnType,0,0);
+ LDAPDebug( LDAP_DEBUG_PLUGIN, "----------> GraphicReturnType [%s] \n", info->graphicReturnType,0,0);
+ if (!strcasecmp(info->requestMethod, PRESENCE_REQUEST_METHOD_GET)) {
+ if (map->syntax == PRESENCE_STRING) {
+ if (!strcasecmp(info->textReturnType, PRESENCE_TEXT_RETURN_TYPE)) {
+ status = http_get_text(_HttpAPI, urltosend, &buf, &bytesRead);
+ } else {
+ status = http_get_binary(_HttpAPI, urltosend, &buf, &bytesRead);
+ }
+ } else {
+ if (!strcasecmp(info->graphicReturnType, PRESENCE_TEXT_RETURN_TYPE)) {
+ status = http_get_text(_HttpAPI, urltosend, &buf, &bytesRead);
+ } else {
+ status = http_get_binary(_HttpAPI, urltosend, &buf, &bytesRead);
+ }
+ }
+ } else if (!strcasecmp(info->requestMethod, PRESENCE_REQUEST_METHOD_REDIRECT)) {
+ status = http_get_redirected_uri(_HttpAPI, urltosend, &buf, &bytesRead);
+ } else {
+ /**
+ * error : unknown method
+ * probably we should check at the time of loading
+ * of the plugin itself that the config values are
+ * properly checked and throw warning/errors in case
+ * of any invalid entry
+ */
+ slapi_log_error(SLAPI_LOG_FATAL, PRESENCE_PLUGIN_SUBSYSTEM,
+ "Unknown request type <%s>\n", info->requestMethod);
+ status = PRESENCE_FAILURE;
+ goto bail;
+ }
+ if (buf && status == PRESENCE_SUCCESS)
+ {
+ *BUF = buf;
+ *size = bytesRead;
+ }
+
+bail:
+ LDAPDebug( LDAP_DEBUG_PLUGIN, "<-- makeHttpRequest:: <%d>\n",status,0,0);
+
+ slapi_ch_free((void**)&urltosend);
+ return status;
+}
+
+/**
+ * This function replaces the occurrence of $ns[<vendor>]imid with its
+ * actual value
+ * e.g.
+ * URL : http://opi.yahoo.com/online?u=$nsyimid
+ * after replacing
+ * newURL : http://opi.yahoo.com/online?u=srajam
+ */
+static char * replaceIdWithValue(char *str, char *id, char *value)
+{
+ int i=0;
+ int k=0;
+ char *newstr = NULL;
+ char c;
+ if (!str || !id || !value)
+ {
+ return NULL;
+ }
+ /* extra space for userids */
+ newstr = (char *)slapi_ch_malloc(strlen(str) + strlen(value));
+ while ((c=str[i]) != '\0')
+ {
+ if (c == '$')
+ {
+ int j = 0;
+ i++; /*skip one char */
+ /**
+ * we found the begining of the string to be
+ * substituted. Now skip the chars we want to replace
+ */
+ while (str[i] != '\0' && id[j] != '\0' &&
+ (toupper(str[i]) == toupper(id[j])))
+ {
+ i++;
+ j++;
+ }
+ j=0;
+ while (value[j] != '\0')
+ {
+ newstr[k++] = value[j++];
+ }
+ }
+ else
+ {
+ newstr[k++]=c;
+ i++;
+ }
+ }
+
+ newstr[k] = '\0';
+ return newstr;
+}
+
+static int setIMStatus(char *id, _Vmap *map, _ConfigEntry *info,
+ char *returnedBUF, int size, Slapi_ValueSet **results)
+{
+ int status = PRESENCE_SUCCESS;
+ char *ontxtmap = NULL;
+ char *offtxtmap = NULL;
+ Slapi_Value *value = NULL;
+ Slapi_Value *value1 = NULL;
+ Slapi_Value *value2 = NULL;
+ struct berval bval;
+ Slapi_Attr *attr = NULL;
+ const struct berval *tmp = NULL;
+
+ LDAPDebug( LDAP_DEBUG_PLUGIN, "--> setIMStatus \n", 0,0,0);
+ /**
+ * we got some data back so lets try to map it to
+ * the existing set of on/off data
+ *
+ * first we need to take a look at the
+ * returned type and depending upon that parse
+ * the data
+ */
+
+ if (map->syntax == PRESENCE_STRING) {
+ /**
+ * we had send a request for text
+ * but chances are we might end up
+ * getting an image back. So we need
+ * to compare it to existing set of
+ * images that we have in store ???
+ */
+ if (!strcasecmp(info->textReturnType, PRESENCE_TEXT_RETURN_TYPE)) {
+ /* return value is in text format */
+ ontxtmap = replaceIdWithValue(info->onTextMap, map->imID, id);
+ offtxtmap = replaceIdWithValue(info->offTextMap, map->imID, id);
+ if (!strcasecmp(ontxtmap, returnedBUF)) {
+ /**
+ * set the on value
+ */
+ value = slapi_value_new_string(PRESENCE_RETURNED_ON_TEXT);
+ } else if (!strcasecmp(offtxtmap, returnedBUF)) {
+ /**
+ * set the off value
+ */
+ value = slapi_value_new_string(PRESENCE_RETURNED_OFF_TEXT);
+ } else {
+ value = slapi_value_new_string(PRESENCE_RETURNED_ERROR_TEXT);
+ }
+ } else if (!strcasecmp(info->textReturnType, PRESENCE_BINARY_RETURN_TYPE)) {
+ /**
+ * call binary compare method
+ */
+ bval.bv_len = size;
+ bval.bv_val = returnedBUF;
+ value1 = slapi_value_new_berval(&bval);
+
+ LDAPDebug( LDAP_DEBUG_PLUGIN, "----------> returned size [%d] \n", bval.bv_len,0,0);
+ LDAPDebug( LDAP_DEBUG_PLUGIN, "----------> returned value [%s] \n", bval.bv_val,0,0);
+
+ attr = info->onGraphicMap;
+ if (attr) {
+ slapi_attr_first_value(attr, &value2);
+ tmp = slapi_value_get_berval(value2);
+ LDAPDebug( LDAP_DEBUG_PLUGIN, "----------> Stored size [%d] \n", tmp->bv_len,0,0);
+ LDAPDebug( LDAP_DEBUG_PLUGIN, "----------> Stored value [%s] \n", tmp->bv_val,0,0);
+ if (!slapi_value_compare(attr, value1, value2)) {
+ value = slapi_value_new_string(PRESENCE_RETURNED_ON_TEXT);
+ }
+ }
+ if (!value) {
+ attr = info->offGraphicMap;
+ if (attr) {
+ slapi_attr_first_value(attr, &value2);
+ if (!slapi_value_compare(attr, value1, value2)) {
+ value = slapi_value_new_string(PRESENCE_RETURNED_OFF_TEXT);
+ }
+ }
+ }
+ if (!value) {
+ attr = info->disabledGraphicMap;
+ if (attr) {
+ slapi_attr_first_value(attr, &value2);
+ if (!slapi_value_compare(attr, value1, value2)) {
+ value = slapi_value_new_string(PRESENCE_RETURNED_OFF_TEXT);
+ }
+ }
+ }
+ if (!value) {
+ /* some error */
+ value = slapi_value_new_string(PRESENCE_RETURNED_ERROR_TEXT);
+ }
+ } else {
+ /**
+ * set the error condition
+ */
+ value = slapi_value_new_string(PRESENCE_RETURNED_ERROR_TEXT);
+ }
+ LDAPDebug( LDAP_DEBUG_PLUGIN, "----------> value [%s] \n", returnedBUF,0,0);
+ } else {
+ /**
+ * we had send a request for image
+ * so whatever we get back we just
+ * return instead of analyzing it
+ */
+ if (!strcasecmp(info->graphicReturnType, PRESENCE_TEXT_RETURN_TYPE)) {
+ LDAPDebug( LDAP_DEBUG_PLUGIN, "----------> value [%s] \n", returnedBUF,0,0);
+ if (!strcasecmp(info->requestMethod, PRESENCE_REQUEST_METHOD_REDIRECT)) {
+ /**
+ * a redirect case in which we should probably have a
+ * gif in store so return that value
+ *
+ * for now
+ */
+
+ ontxtmap = replaceIdWithValue(info->onTextMap, map->imID, id);
+ offtxtmap = replaceIdWithValue(info->offTextMap, map->imID, id);
+ if (!strcasecmp(ontxtmap, returnedBUF)) {
+ /**
+ * set the on value
+ */
+ attr = info->onGraphicMap;
+ } else if (!strcasecmp(offtxtmap, returnedBUF)) {
+ /**
+ * set the off value
+ */
+ attr = info->offGraphicMap;
+ } else {
+ attr = info->disabledGraphicMap;
+ }
+ if (attr) {
+ slapi_attr_first_value(attr, &value);
+ }
+ } else {
+ /**
+ * for now just set the returned value
+ * should not happen in our case
+ * ERROR
+ */
+ }
+ } else {
+ LDAPDebug( LDAP_DEBUG_PLUGIN, "----------> value [%s] \n", returnedBUF,0,0);
+ bval.bv_len = size;
+ bval.bv_val = returnedBUF;
+ value = slapi_value_new_berval(&bval);
+ }
+ }
+ if (!*results) {
+ *results = slapi_valueset_new();
+ }
+
+ slapi_valueset_add_value(*results, value);
+
+ if (ontxtmap) {
+ slapi_ch_free((void **)&ontxtmap);
+ }
+ if (offtxtmap) {
+ slapi_ch_free((void **)&offtxtmap);
+ }
+ if (value && map->syntax == PRESENCE_STRING) {
+ slapi_value_free(&value);
+ }
+ LDAPDebug( LDAP_DEBUG_PLUGIN, "<-- setIMStatus \n", 0,0,0);
+
+ return status;
+}
+
+static int setTypes(PLHashEntry *he, PRIntn i, void *arg)
+{
+ int status;
+ int props = SLAPI_ATTR_FLAG_OPATTR;
+ Slapi_Attr *attr = NULL;
+ Slapi_ValueSet *results = NULL;
+ int type_name_disposition = 0;
+ char *actual_type_name = 0;
+ int free_flags = 0;
+
+ struct _vattrtypes *args = arg;
+ char *type = (char *)he->key;
+ _Vmap *map = (_Vmap *)he->value;
+ char *id = map->imID;
+
+ LDAPDebug( LDAP_DEBUG_PLUGIN, "--> setTypes \n", 0,0,0);
+
+ status = slapi_vattr_values_get_sp(NULL, args->entry, id, &results, &type_name_disposition, &actual_type_name, 0, &free_flags);
+ if(status == PRESENCE_SUCCESS)
+ {
+ /* entry contains this attr */
+ vattr_type_thang thang = {0};
+
+ thang.type_name = type;
+ thang.type_flags = props;
+
+ slapi_vattrspi_add_type(args->context,&thang,0);
+
+ slapi_vattr_values_free(&results, &actual_type_name, free_flags);
+
+ LDAPDebug( LDAP_DEBUG_PLUGIN, "----------> ID [%s] Type[%s]\n", actual_type_name,type,0);
+ }
+ LDAPDebug( LDAP_DEBUG_PLUGIN, "<-- setTypes \n", 0,0,0);
+
+ return HT_ENUMERATE_NEXT;
+}
+
+
+static void
+logGraphicAttributeValue( Slapi_Attr *attr, const char *attrname )
+{
+ Slapi_Value *val = NULL;
+ const struct berval *v = NULL;
+
+ if ( LDAPDebugLevelIsSet( LDAP_DEBUG_PLUGIN )) {
+ slapi_attr_first_value(attr, &val);
+ v = slapi_value_get_berval(val);
+ if (v) {
+ char *ldifvalue;
+ size_t attrnamelen = strlen( attrname );
+
+ LDAPDebug( LDAP_DEBUG_PLUGIN, "----------> %s size [%d] \n",
+ attrname,v->bv_len,0);
+
+ ldifvalue = ldif_type_and_value_with_options(
+ (char *)attrname, /* XXX: had to cast away const */
+ v->bv_val, v->bv_len, 0 );
+ if ( NULL != ldifvalue ) {
+ LDAPDebug( LDAP_DEBUG_PLUGIN, "----------> %s value [\n%s]\n",
+ attrname,ldifvalue,0);
+ slapi_ch_free_string( &ldifvalue );
+ }
+ }
+ }
+}
+
+
+static void deleteMapTables()
+{
+ PL_HashTableEnumerateEntries(_IdConfigMapTable, destroyHashEntry, 0);
+ if (_IdConfigMapTable)
+ {
+ PL_HashTableDestroy(_IdConfigMapTable);
+ }
+
+ PL_HashTableEnumerateEntries(_IdVattrMapTable, destroyHashEntry, 0);
+ if (_IdVattrMapTable)
+ {
+ PL_HashTableDestroy(_IdVattrMapTable);
+ }
+ return;
+}
+
+static PRIntn destroyHashEntry(PLHashEntry *he, PRIntn index, void *arg)
+{
+ void *value = NULL;
+ if (he == NULL)
+ {
+ return HT_ENUMERATE_NEXT;
+ }
+ value = he->value;
+ if (value)
+ {
+ slapi_ch_free(&value);
+ }
+ return HT_ENUMERATE_REMOVE;
+}
+
+static void toLowerCase(char* str)
+{
+ if (str) {
+ char* lstr = str;
+ for(; (*lstr != '\0'); ++lstr) {
+ *lstr = tolower(*lstr);
+ }
+ }
+}
+
+
+/**
+ * utility function to print the array
+ */
+void printMapTable()
+{
+ PL_HashTableEnumerateEntries(_IdVattrMapTable, printIdVattrMapTable, 0);
+ PL_HashTableEnumerateEntries(_IdConfigMapTable, printIdConfigMapTable, 0);
+}
+
+PRIntn printIdVattrMapTable(PLHashEntry *he, PRIntn i, void *arg)
+{
+ char *key = (char *)he->key;
+ _Vmap *map = (_Vmap *)he->value;
+ printf("<---- Key -------> %s\n", key);
+ printf("<---- ImId ------> %s\n", map->imID);
+ printf("<---- syntax ----> %d\n", map->syntax);
+ return HT_ENUMERATE_NEXT;
+}
+
+PRIntn printIdConfigMapTable(PLHashEntry *he, PRIntn i, void *arg)
+{
+ char *key = (char *)he->key;
+ _ConfigEntry *value = (_ConfigEntry *)he->value;
+ printf("<- Key ---------------------> %s\n", key);
+ printf("<---- text_url -------------> %s\n", value->textURL);
+ printf("<---- graphic_url ----------> %s\n", value->graphicURL);
+ printf("<---- on_text_map ----------> %s\n", value->onTextMap);
+ printf("<---- off_text_map ---------> %s\n", value->offTextMap);
+ printf("<---- request_method -------> %s\n", value->requestMethod);
+ printf("<---- text_return_type -----> %s\n", value->textReturnType);
+ printf("<---- graphic_return_type --> %s\n", value->graphicReturnType);
+
+ return HT_ENUMERATE_NEXT;
+}
+
diff --git a/ldap/servers/plugins/presence/presence.def b/ldap/servers/plugins/presence/presence.def
new file mode 100644
index 00000000..4d083d26
--- /dev/null
+++ b/ldap/servers/plugins/presence/presence.def
@@ -0,0 +1,11 @@
+; BEGIN COPYRIGHT BLOCK
+; Copyright 2001 Sun Microsystems, Inc.
+; Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+; All rights reserved.
+; END COPYRIGHT BLOCK
+;
+DESCRIPTION 'Netscape Directory Server 6.2.1 Presence Plugin'
+EXPORTS
+ presence_init @2
+ plugin_init_debug_level @3
+ presence_version @4
diff --git a/ldap/servers/plugins/presence/presence.ldif b/ldap/servers/plugins/presence/presence.ldif
new file mode 100644
index 00000000..67bb977b
--- /dev/null
+++ b/ldap/servers/plugins/presence/presence.ldif
@@ -0,0 +1,44 @@
+dn:cn=ICQ Presence,cn=Presence,cn=plugins,cn=config
+changeType:modify
+replace:nsim-onvaluemapgraphic
+nsim-onvaluemapgraphic: D:/dev/ds60cvs/ldapserver/ldap/servers/plugins/presence/icq-online.gif
+
+dn:cn=ICQ Presence,cn=Presence,cn=plugins,cn=config
+changeType:modify
+replace:nsim-offvaluemapgraphic
+nsim-offvaluemapgraphic: D:/dev/ds60cvs/ldapserver/ldap/servers/plugins/presence/icq-offline.gif
+
+dn:cn=ICQ Presence,cn=Presence,cn=plugins,cn=config
+changeType:modify
+replace:nsim-disabledvaluemapgraphic
+nsim-disabledvaluemapgraphic: D:/dev/ds60cvs/ldapserver/ldap/servers/plugins/presence/icq-disabled.gif
+
+dn:cn=AIM Presence,cn=Presence,cn=plugins,cn=config
+changeType:modify
+replace:nsim-onvaluemapgraphic
+nsim-onvaluemapgraphic: D:/dev/ds60cvs/ldapserver/ldap/servers/plugins/presence/aim-online.gif
+
+dn:cn=AIM Presence,cn=Presence,cn=plugins,cn=config
+changeType:modify
+replace:nsim-offvaluemapgraphic
+nsim-offvaluemapgraphic: D:/dev/ds60cvs/ldapserver/ldap/servers/plugins/presence/aim-offline.gif
+
+dn:cn=ICQ Presence,cn=Presence,cn=plugins,cn=config
+changeType:modify
+replace:nsim-disabledvaluemapgraphic
+nsim-disabledvaluemapgraphic: D:/dev/ds60cvs/ldapserver/ldap/servers/plugins/presence/aim-offline.gif
+
+dn:cn=Yahoo Presence,cn=Presence,cn=plugins,cn=config
+changeType:modify
+replace:nsim-offvaluemapgraphic
+nsim-offvaluemapgraphic: D:/dev/ds60cvs/ldapserver/ldap/servers/plugins/presence/yahoo-offline.gif
+
+dn:cn=Yahoo Presence,cn=Presence,cn=plugins,cn=config
+changeType:modify
+replace:nsim-onvaluemapgraphic
+nsim-onvaluemapgraphic: D:/dev/ds60cvs/ldapserver/ldap/servers/plugins/presence/yahoo-online.gif
+
+dn:cn=Yahoo Presence,cn=Presence,cn=plugins,cn=config
+changeType:modify
+replace:nsim-disabledvaluemapgraphic
+nsim-disabledvaluemapgraphic: D:/dev/ds60cvs/ldapserver/ldap/servers/plugins/presence/yahoo-offline.gif
diff --git a/ldap/servers/plugins/pwdstorage/Makefile b/ldap/servers/plugins/pwdstorage/Makefile
new file mode 100644
index 00000000..efad0788
--- /dev/null
+++ b/ldap/servers/plugins/pwdstorage/Makefile
@@ -0,0 +1,115 @@
+#
+# BEGIN COPYRIGHT BLOCK
+# Copyright 2001 Sun Microsystems, Inc.
+# Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+# All rights reserved.
+# END COPYRIGHT BLOCK
+#
+#
+# GNU Makefile for Directory Server password_storaged-plugin.so password storage scheme plugins
+#
+
+LDAP_SRC = ../../..
+MCOM_ROOT = ../../../../..
+
+NOSTDCLEAN=true # don't let nsconfig.mk define target clean
+NOSTDSTRIP=true # don't let nsconfig.mk define target strip
+NSPR20=true # probably should be defined somewhere else (not sure where)
+
+OBJDEST = $(OBJDIR)/lib/libpwdstorage
+LIBDIR = $(LIB_RELDIR)
+
+include $(MCOM_ROOT)/ldapserver/nsconfig.mk
+include $(LDAP_SRC)/nsldap.mk
+
+ifeq ($(ARCH), WINNT)
+DEF_FILE:=./libpwdstorage.def
+endif
+
+CFLAGS+=$(SLCFLAGS)
+
+INCLUDES += -I$(LDAP_SRC)/servers/slapd
+
+PWD_OBJS= \
+ pwd_init.o \
+ clear_pwd.o \
+ crypt_pwd.o \
+ ns-mta-md5_pwd.o \
+ sha_pwd.o \
+ ssha_pwd.o \
+ md5c.o
+
+
+OBJS = $(addprefix $(OBJDEST)/, $(PWD_OBJS))
+
+ifeq ($(ARCH), WINNT)
+LIBPWD_DLL_OBJ = $(addprefix $(OBJDEST)/, dllmain.o)
+endif
+
+LIBPWD = $(addprefix $(LIBDIR)/, $(PWD_DLL).$(DLL_SUFFIX))
+
+ifeq ($(ARCH), WINNT)
+EXTRA_LIBS_DEP += \
+ $(LIBSLAPD_DEP) \
+ $(LDAP_LIBUTIL_DEP) \
+ $(LDAP_COMMON_LIBS_DEP)
+EXTRA_LIBS_DEP += \
+ $(LDAPSDK_DEP) \
+ $(SECURITY_DEP)
+EXTRA_LIBS += \
+ $(LIBSLAPD) \
+ $(LDAP_SDK_LIBLDAP_DLL) \
+ $(LIBUTIL) \
+ $(NSPRLINK) \
+ $(LDAP_COMMON_LIBS) \
+ $(SECURITYLINK)
+endif
+ifeq ($(ARCH), AIX)
+EXTRA_LIBS_DEP += \
+ $(LIBSLAPD_DEP) \
+ $(LDAP_LIBUTIL_DEP) \
+ $(LDAP_COMMON_LIBS_DEP)
+EXTRA_LIBS_DEP += \
+ $(LDAPSDK_DEP) \
+ $(SECURITY_DEP)
+EXTRA_LIBS += \
+ $(LIBSLAPDLINK) \
+ $(LDAP_SDK_LIBLDAP_DLL) \
+ $(LIBUTIL) \
+ $(NSPRLINK) \
+ $(LDAP_COMMON_LIBS) \
+ $(SECURITYLINK)
+endif
+
+ifeq ($(ARCH), HPUX)
+EXTRA_LIBS_DEP += $(LIBSLAPD_DEP) $(LDAPSDK_DEP) $(NSPR_DEP) $(SECURITY_DEP)
+EXTRA_LIBS += $(DYN_NSHTTPD) $(ADMINUTIL_LINK) $(LDAPLINK) $(SECURITYLINK) $(NSPRLINK) $(ICULINK)
+endif
+
+ifeq ($(ARCH), WINNT)
+DLL_LDFLAGS += -def:"./libpwdstorage.def"
+CFLAGS+= /WX
+endif # WINNT
+
+ifeq ($(ARCH), AIX)
+LD=ld
+endif
+
+clientSDK:
+
+all: $(OBJDEST) $(LIBDIR) $(LIBPWD)
+
+$(LIBPWD): $(OBJS) $(LIBPWD_DLL_OBJ) $(DEF_FILE)
+ $(LINK_DLL) $(LIBPWD_DLL_OBJ) $(EXTRA_LIBS)
+
+veryclean: clean
+
+clean:
+ $(RM) $(OBJS)
+ifeq ($(ARCH), WINNT)
+ $(RM) $(LIBPWD_DLL_OBJ)
+endif
+ $(RM) $(LIBPWD)
+
+$(OBJDEST):
+ $(MKDIR) $(OBJDEST)
diff --git a/ldap/servers/plugins/pwdstorage/clear_pwd.c b/ldap/servers/plugins/pwdstorage/clear_pwd.c
new file mode 100644
index 00000000..4b2a3ca5
--- /dev/null
+++ b/ldap/servers/plugins/pwdstorage/clear_pwd.c
@@ -0,0 +1,27 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+/*
+ * slapd hashed password routines
+ *
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <sys/types.h>
+
+#include "pwdstorage.h"
+
+int
+clear_pw_cmp( char *userpwd, char *dbpwd )
+{
+ return( strcmp( userpwd, dbpwd ));
+}
+
+char *
+clear_pw_enc( char *pwd )
+{
+ return( slapi_ch_strdup( pwd ));
+}
diff --git a/ldap/servers/plugins/pwdstorage/crypt_pwd.c b/ldap/servers/plugins/pwdstorage/crypt_pwd.c
new file mode 100644
index 00000000..df179ef6
--- /dev/null
+++ b/ldap/servers/plugins/pwdstorage/crypt_pwd.c
@@ -0,0 +1,91 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+/*
+ * slapd hashed password routines
+ *
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <sys/types.h>
+#ifdef _WIN32
+char *crypt(char *key, char *salt);
+#else
+#include <sys/socket.h>
+#if defined( hpux ) || defined ( AIX ) || defined (LINUX) || defined (OSF1)
+#define __USE_XOPEN /* linux */
+#include <unistd.h>
+#else /* hpux */
+#include <crypt.h>
+#endif /* hpux */
+#endif /* _WIN32 */
+
+#include "pwdstorage.h"
+
+static PRLock *cryptlock; /* Some implementations of crypt are not thread safe. ie. ours & Irix */
+
+/* characters used in crypt encoding */
+static unsigned char itoa64[] = /* 0 ... 63 => ascii - 64 */
+ "./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
+
+
+
+void
+crypt_init()
+{
+ cryptlock = PR_NewLock();
+}
+
+int
+crypt_pw_cmp( char *userpwd, char *dbpwd )
+{
+ int rc;
+ char *cp;
+ PR_Lock(cryptlock);
+ /* we use salt (first 2 chars) of encoded password in call to crypt() */
+ cp = crypt( userpwd, dbpwd );
+ if (cp) {
+ rc= strcmp( dbpwd, cp);
+ } else {
+ rc = -1;
+ }
+ PR_Unlock(cryptlock);
+ return rc;
+}
+
+char *
+crypt_pw_enc( char *pwd )
+{
+ char *cry, salt[3];
+ char *enc= NULL;
+ long v;
+ static unsigned int seed = 0;
+
+ if ( seed == 0)
+ {
+ seed = (unsigned int)slapi_rand();
+ }
+ v = slapi_rand_r(&seed);
+
+ salt[0] = itoa64[v & 0x3f];
+ v >>= 6;
+ salt[1] = itoa64[v & 0x3f];
+ salt[2] = '\0';
+
+ PR_Lock(cryptlock);
+ cry = crypt( pwd, salt );
+ if ( cry != NULL )
+ {
+ enc = slapi_ch_malloc( 3 + CRYPT_NAME_LEN + strlen( cry ));
+ if ( enc != NULL )
+ {
+ sprintf( enc, "%c%s%c%s", PWD_HASH_PREFIX_START, CRYPT_SCHEME_NAME, PWD_HASH_PREFIX_END, cry );
+ }
+ }
+ PR_Unlock(cryptlock);
+ return( enc );
+}
+
diff --git a/ldap/servers/plugins/pwdstorage/dllmain.c b/ldap/servers/plugins/pwdstorage/dllmain.c
new file mode 100644
index 00000000..71530805
--- /dev/null
+++ b/ldap/servers/plugins/pwdstorage/dllmain.c
@@ -0,0 +1,91 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+
+ /*
+ * Microsoft Windows specifics for LIBPWDSTORAGE DLL
+ */
+#include "ldap.h"
+
+
+#ifdef _WIN32
+/* Lifted from Q125688
+ * How to Port a 16-bit DLL to a Win32 DLL
+ * on the MSVC 4.0 CD
+ */
+BOOL WINAPI DllMain (HANDLE hModule, DWORD fdwReason, LPVOID lpReserved)
+{
+
+ switch (fdwReason)
+ {
+ case DLL_PROCESS_ATTACH:
+ /* Code from LibMain inserted here. Return TRUE to keep the
+ DLL loaded or return FALSE to fail loading the DLL.
+
+ You may have to modify the code in your original LibMain to
+ account for the fact that it may be called more than once.
+ You will get one DLL_PROCESS_ATTACH for each process that
+ loads the DLL. This is different from LibMain which gets
+ called only once when the DLL is loaded. The only time this
+ is critical is when you are using shared data sections.
+ If you are using shared data sections for statically
+ allocated data, you will need to be careful to initialize it
+ only once. Check your code carefully.
+
+ Certain one-time initializations may now need to be done for
+ each process that attaches. You may also not need code from
+ your original LibMain because the operating system may now
+ be doing it for you.
+ */
+ /*
+ * 16 bit code calls UnlockData()
+ * which is mapped to UnlockSegment in windows.h
+ * in 32 bit world UnlockData is not defined anywhere
+ * UnlockSegment is mapped to GlobalUnfix in winbase.h
+ * and the docs for both UnlockSegment and GlobalUnfix say
+ * ".. function is oboslete. Segments have no meaning
+ * in the 32-bit environment". So we do nothing here.
+ */
+
+ break;
+
+ case DLL_THREAD_ATTACH:
+ /* Called each time a thread is created in a process that has
+ already loaded (attached to) this DLL. Does not get called
+ for each thread that exists in the process before it loaded
+ the DLL.
+
+ Do thread-specific initialization here.
+ */
+ break;
+
+ case DLL_THREAD_DETACH:
+ /* Same as above, but called when a thread in the process
+ exits.
+
+ Do thread-specific cleanup here.
+ */
+ break;
+
+ case DLL_PROCESS_DETACH:
+ /* Code from _WEP inserted here. This code may (like the
+ LibMain) not be necessary. Check to make certain that the
+ operating system is not doing it for you.
+ */
+
+ break;
+ }
+ /* The return value is only used for DLL_PROCESS_ATTACH; all other
+ conditions are ignored. */
+ return TRUE; /* successful DLL_PROCESS_ATTACH */
+}
+#else
+int CALLBACK
+LibMain( HINSTANCE hinst, WORD wDataSeg, WORD cbHeapSize, LPSTR lpszCmdLine )
+{
+ /*UnlockData( 0 );*/
+ return( 1 );
+}
+#endif
diff --git a/ldap/servers/plugins/pwdstorage/libpwdstorage.def b/ldap/servers/plugins/pwdstorage/libpwdstorage.def
new file mode 100644
index 00000000..e19305d5
--- /dev/null
+++ b/ldap/servers/plugins/pwdstorage/libpwdstorage.def
@@ -0,0 +1,24 @@
+; BEGIN COPYRIGHT BLOCK
+; Copyright 2001 Sun Microsystems, Inc.
+; Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+; All rights reserved.
+; END COPYRIGHT BLOCK
+;
+DESCRIPTION 'Netscape Directory Server 7 password storage scheme Plugin'
+EXPORTS
+ sha_pwd_storage_scheme_init @2
+ ssha_pwd_storage_scheme_init @3
+ crypt_pwd_storage_scheme_init @4
+ clear_pwd_storage_scheme_init @5
+ ns_mta_md5_pwd_storage_scheme_init @6
+ clear_pw_cmp @7
+ crypt_pw_cmp @8
+ ns_mta_md5_pw_cmp @9
+ sha1_pw_cmp @10
+ sha1_pw_enc @11
+ salted_sha1_pw_enc @12
+ crypt_pw_enc @13
+ clear_pw_enc @14
+ mta_MD5Init @15
+ mta_MD5Update @16
+ mta_MD5Final @17
diff --git a/ldap/servers/plugins/pwdstorage/md5.h b/ldap/servers/plugins/pwdstorage/md5.h
new file mode 100644
index 00000000..6f7ec036
--- /dev/null
+++ b/ldap/servers/plugins/pwdstorage/md5.h
@@ -0,0 +1,63 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+/*
+ * MD5 algorithm used by Netscape Mail Server
+ */
+
+/* MD5 code taken from reference implementation published in RFC 1321 */
+
+#ifndef _RFC1321_MD5_H_
+#define _RFC1321_MD5_H_
+
+/* Copyright (C) 1991-2, RSA Data Security, Inc. Created 1991. All
+ rights reserved.
+
+ License to copy and use this software is granted provided that it
+ is identified as the "RSA Data Security, Inc. MD5 Message-Digest
+ Algorithm" in all material mentioning or referencing this software
+ or this function.
+
+ License is also granted to make and use derivative works provided
+ that such works are identified as "derived from the RSA Data
+ Security, Inc. MD5 Message-Digest Algorithm" in all material
+ mentioning or referencing the derived work.
+
+ RSA Data Security, Inc. makes no representations concerning either
+ the merchantability of this software or the suitability of this
+ software for any particular purpose. It is provided "as is"
+ without express or implied warranty of any kind.
+
+ These notices must be retained in any copies of any part of this
+ documentation and/or software.
+ */
+
+#include "nspr.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+typedef unsigned char * POINTER;
+typedef PRUint16 UINT2;
+typedef PRUint32 UINT4;
+
+/* MD5 context. */
+typedef struct {
+ UINT4 state[4]; /* state (ABCD) */
+ UINT4 count[2]; /* number of bits, modulo 2^64 (lsb first) */
+ unsigned char buffer[64]; /* input buffer */
+} mta_MD5_CTX;
+
+void mta_MD5Init (mta_MD5_CTX *);
+void mta_MD5Update (mta_MD5_CTX *, const unsigned char *, unsigned int);
+void mta_MD5Final (unsigned char [16], mta_MD5_CTX *);
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif /* end of _RFC1321_MD5_H_ */
+
diff --git a/ldap/servers/plugins/pwdstorage/md5c.c b/ldap/servers/plugins/pwdstorage/md5c.c
new file mode 100644
index 00000000..d78b772c
--- /dev/null
+++ b/ldap/servers/plugins/pwdstorage/md5c.c
@@ -0,0 +1,337 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+/* MD5 code taken from reference implementation published in RFC 1321 */
+
+/* MD5C.C - RSA Data Security, Inc., MD5 message-digest algorithm */
+
+/* Copyright (C) 1991-2, RSA Data Security, Inc. Created 1991. All
+ rights reserved.
+
+ License to copy and use this software is granted provided that it
+ is identified as the "RSA Data Security, Inc. MD5 Message-Digest
+ Algorithm" in all material mentioning or referencing this software
+ or this function.
+
+ License is also granted to make and use derivative works provided
+ that such works are identified as "derived from the RSA Data
+ Security, Inc. MD5 Message-Digest Algorithm" in all material
+ mentioning or referencing the derived work.
+
+ RSA Data Security, Inc. makes no representations concerning either
+ the merchantability of this software or the suitability of this
+ software for any particular purpose. It is provided "as is"
+ without express or implied warranty of any kind.
+
+ These notices must be retained in any copies of any part of this
+ documentation and/or software.
+ */
+
+#include "md5.h"
+
+/* Constants for MD5Transform routine. */
+
+#define S11 7
+#define S12 12
+#define S13 17
+#define S14 22
+#define S21 5
+#define S22 9
+#define S23 14
+#define S24 20
+#define S31 4
+#define S32 11
+#define S33 16
+#define S34 23
+#define S41 6
+#define S42 10
+#define S43 15
+#define S44 21
+
+static void MD5Transform (UINT4 [4], const unsigned char [64]);
+static void Encode (unsigned char *, const UINT4 *, unsigned int);
+static void Decode (UINT4 *, const unsigned char *, unsigned int);
+static void MD5_memcpy (POINTER, const POINTER, unsigned int);
+static void MD5_memset (POINTER, int, unsigned int);
+
+static unsigned char PADDING[64] = {
+ 0x80, 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
+};
+
+/* F, G, H and I are basic MD5 functions.
+ */
+#define F(x, y, z) (((x) & (y)) | ((~(x)) & (z)))
+#define G(x, y, z) (((x) & (z)) | ((y) & (~(z))))
+#define H(x, y, z) ((x) ^ (y) ^ (z))
+#define I(x, y, z) ((y) ^ ((x) | (~(z))))
+
+/* ROTATE_LEFT rotates x left n bits.
+ */
+#define ROTATE_LEFT(x, n) (((x) << (n)) | ((x) >> (32-(n))))
+
+/* FF, GG, HH, and II transformations for rounds 1, 2, 3, and 4.
+Rotation is separate from addition to prevent recomputation.
+ */
+#define FF(a, b, c, d, x, s, ac) { \
+ (a) += F ((b), (c), (d)) + (x) + (UINT4)(ac); \
+ (a) = ROTATE_LEFT ((a), (s)); \
+ (a) += (b); \
+ }
+#define GG(a, b, c, d, x, s, ac) { \
+ (a) += G ((b), (c), (d)) + (x) + (UINT4)(ac); \
+ (a) = ROTATE_LEFT ((a), (s)); \
+ (a) += (b); \
+ }
+#define HH(a, b, c, d, x, s, ac) { \
+ (a) += H ((b), (c), (d)) + (x) + (UINT4)(ac); \
+ (a) = ROTATE_LEFT ((a), (s)); \
+ (a) += (b); \
+ }
+#define II(a, b, c, d, x, s, ac) { \
+ (a) += I ((b), (c), (d)) + (x) + (UINT4)(ac); \
+ (a) = ROTATE_LEFT ((a), (s)); \
+ (a) += (b); \
+ }
+
+/* MD5 initialization. Begins an MD5 operation, writing a new context.
+ */
+void mta_MD5Init (context)
+mta_MD5_CTX *context; /* context */
+{
+ context->count[0] = context->count[1] = 0;
+ /* Load magic initialization constants.
+*/
+ context->state[0] = 0x67452301;
+ context->state[1] = 0xefcdab89;
+ context->state[2] = 0x98badcfe;
+ context->state[3] = 0x10325476;
+}
+
+/* MD5 block update operation. Continues an MD5 message-digest
+ operation, processing another message block, and updating the
+ context.
+ */
+void mta_MD5Update (context, input, inputLen)
+mta_MD5_CTX *context; /* context */
+const unsigned char *input; /* input block */
+unsigned int inputLen; /* length of input block */
+{
+ unsigned int i, index, partLen;
+
+ /* Compute number of bytes mod 64 */
+ index = (unsigned int)((context->count[0] >> 3) & 0x3F);
+
+ /* Update number of bits */
+ if ((context->count[0] += ((UINT4)inputLen << 3))
+ < ((UINT4)inputLen << 3))
+ context->count[1]++;
+ context->count[1] += ((UINT4)inputLen >> 29);
+
+ partLen = 64 - index;
+
+ /* Transform as many times as possible.
+*/
+ if (inputLen >= partLen) {
+ MD5_memcpy
+ ((POINTER)&context->buffer[index], (POINTER)input, partLen);
+ MD5Transform (context->state, context->buffer);
+
+ for (i = partLen; i + 63 < inputLen; i += 64)
+ MD5Transform (context->state, &input[i]);
+
+ index = 0;
+ }
+ else
+ i = 0;
+
+ /* Buffer remaining input */
+ MD5_memcpy
+ ((POINTER)&context->buffer[index], (POINTER)&input[i],
+ inputLen-i);
+}
+
+/* MD5 finalization. Ends an MD5 message-digest operation, writing the
+ the message digest and zeroizing the context.
+ */
+void mta_MD5Final (digest, context)
+unsigned char digest[16]; /* message digest */
+mta_MD5_CTX *context; /* context */
+{
+ unsigned char bits[8];
+ unsigned int index, padLen;
+
+ /* Save number of bits */
+ Encode (bits, context->count, 8);
+
+ /* Pad out to 56 mod 64.
+*/
+ index = (unsigned int)((context->count[0] >> 3) & 0x3f);
+ padLen = (index < 56) ? (56 - index) : (120 - index);
+ mta_MD5Update (context, PADDING, padLen);
+
+ /* Append length (before padding) */
+ mta_MD5Update (context, bits, 8);
+
+ /* Store state in digest */
+ Encode (digest, context->state, 16);
+
+ /* Zeroize sensitive information.
+*/
+ MD5_memset ((POINTER)context, 0, sizeof (*context));
+}
+
+/* MD5 basic transformation. Transforms state based on block.
+ */
+static void MD5Transform (state, block)
+UINT4 state[4];
+const unsigned char block[64];
+{
+ UINT4 a = state[0], b = state[1], c = state[2], d = state[3], x[16];
+
+ Decode (x, block, 64);
+
+ /* Round 1 */
+ FF (a, b, c, d, x[ 0], S11, 0xd76aa478); /* 1 */
+ FF (d, a, b, c, x[ 1], S12, 0xe8c7b756); /* 2 */
+ FF (c, d, a, b, x[ 2], S13, 0x242070db); /* 3 */
+ FF (b, c, d, a, x[ 3], S14, 0xc1bdceee); /* 4 */
+ FF (a, b, c, d, x[ 4], S11, 0xf57c0faf); /* 5 */
+ FF (d, a, b, c, x[ 5], S12, 0x4787c62a); /* 6 */
+ FF (c, d, a, b, x[ 6], S13, 0xa8304613); /* 7 */
+ FF (b, c, d, a, x[ 7], S14, 0xfd469501); /* 8 */
+ FF (a, b, c, d, x[ 8], S11, 0x698098d8); /* 9 */
+ FF (d, a, b, c, x[ 9], S12, 0x8b44f7af); /* 10 */
+ FF (c, d, a, b, x[10], S13, 0xffff5bb1); /* 11 */
+ FF (b, c, d, a, x[11], S14, 0x895cd7be); /* 12 */
+ FF (a, b, c, d, x[12], S11, 0x6b901122); /* 13 */
+ FF (d, a, b, c, x[13], S12, 0xfd987193); /* 14 */
+ FF (c, d, a, b, x[14], S13, 0xa679438e); /* 15 */
+ FF (b, c, d, a, x[15], S14, 0x49b40821); /* 16 */
+
+ /* Round 2 */
+ GG (a, b, c, d, x[ 1], S21, 0xf61e2562); /* 17 */
+ GG (d, a, b, c, x[ 6], S22, 0xc040b340); /* 18 */
+ GG (c, d, a, b, x[11], S23, 0x265e5a51); /* 19 */
+ GG (b, c, d, a, x[ 0], S24, 0xe9b6c7aa); /* 20 */
+ GG (a, b, c, d, x[ 5], S21, 0xd62f105d); /* 21 */
+ GG (d, a, b, c, x[10], S22, 0x2441453); /* 22 */
+ GG (c, d, a, b, x[15], S23, 0xd8a1e681); /* 23 */
+ GG (b, c, d, a, x[ 4], S24, 0xe7d3fbc8); /* 24 */
+ GG (a, b, c, d, x[ 9], S21, 0x21e1cde6); /* 25 */
+ GG (d, a, b, c, x[14], S22, 0xc33707d6); /* 26 */
+ GG (c, d, a, b, x[ 3], S23, 0xf4d50d87); /* 27 */
+ GG (b, c, d, a, x[ 8], S24, 0x455a14ed); /* 28 */
+ GG (a, b, c, d, x[13], S21, 0xa9e3e905); /* 29 */
+ GG (d, a, b, c, x[ 2], S22, 0xfcefa3f8); /* 30 */
+ GG (c, d, a, b, x[ 7], S23, 0x676f02d9); /* 31 */
+ GG (b, c, d, a, x[12], S24, 0x8d2a4c8a); /* 32 */
+
+ /* Round 3 */
+ HH (a, b, c, d, x[ 5], S31, 0xfffa3942); /* 33 */
+ HH (d, a, b, c, x[ 8], S32, 0x8771f681); /* 34 */
+ HH (c, d, a, b, x[11], S33, 0x6d9d6122); /* 35 */
+ HH (b, c, d, a, x[14], S34, 0xfde5380c); /* 36 */
+ HH (a, b, c, d, x[ 1], S31, 0xa4beea44); /* 37 */
+ HH (d, a, b, c, x[ 4], S32, 0x4bdecfa9); /* 38 */
+ HH (c, d, a, b, x[ 7], S33, 0xf6bb4b60); /* 39 */
+ HH (b, c, d, a, x[10], S34, 0xbebfbc70); /* 40 */
+ HH (a, b, c, d, x[13], S31, 0x289b7ec6); /* 41 */
+ HH (d, a, b, c, x[ 0], S32, 0xeaa127fa); /* 42 */
+ HH (c, d, a, b, x[ 3], S33, 0xd4ef3085); /* 43 */
+ HH (b, c, d, a, x[ 6], S34, 0x4881d05); /* 44 */
+ HH (a, b, c, d, x[ 9], S31, 0xd9d4d039); /* 45 */
+ HH (d, a, b, c, x[12], S32, 0xe6db99e5); /* 46 */
+ HH (c, d, a, b, x[15], S33, 0x1fa27cf8); /* 47 */
+ HH (b, c, d, a, x[ 2], S34, 0xc4ac5665); /* 48 */
+
+ /* Round 4 */
+ II (a, b, c, d, x[ 0], S41, 0xf4292244); /* 49 */
+ II (d, a, b, c, x[ 7], S42, 0x432aff97); /* 50 */
+ II (c, d, a, b, x[14], S43, 0xab9423a7); /* 51 */
+ II (b, c, d, a, x[ 5], S44, 0xfc93a039); /* 52 */
+ II (a, b, c, d, x[12], S41, 0x655b59c3); /* 53 */
+ II (d, a, b, c, x[ 3], S42, 0x8f0ccc92); /* 54 */
+ II (c, d, a, b, x[10], S43, 0xffeff47d); /* 55 */
+ II (b, c, d, a, x[ 1], S44, 0x85845dd1); /* 56 */
+ II (a, b, c, d, x[ 8], S41, 0x6fa87e4f); /* 57 */
+ II (d, a, b, c, x[15], S42, 0xfe2ce6e0); /* 58 */
+ II (c, d, a, b, x[ 6], S43, 0xa3014314); /* 59 */
+ II (b, c, d, a, x[13], S44, 0x4e0811a1); /* 60 */
+ II (a, b, c, d, x[ 4], S41, 0xf7537e82); /* 61 */
+ II (d, a, b, c, x[11], S42, 0xbd3af235); /* 62 */
+ II (c, d, a, b, x[ 2], S43, 0x2ad7d2bb); /* 63 */
+ II (b, c, d, a, x[ 9], S44, 0xeb86d391); /* 64 */
+
+ state[0] += a;
+ state[1] += b;
+ state[2] += c;
+ state[3] += d;
+
+ /* Zeroize sensitive information.
+*/
+ MD5_memset ((POINTER)x, 0, sizeof (x));
+}
+
+/* Encodes input (UINT4) into output (unsigned char). Assumes len is
+ a multiple of 4.
+ */
+static void Encode (output, input, len)
+unsigned char *output;
+const UINT4 *input;
+unsigned int len;
+{
+ unsigned int i, j;
+
+ for (i = 0, j = 0; j < len; i++, j += 4) {
+ output[j] = (unsigned char)(input[i] & 0xff);
+ output[j+1] = (unsigned char)((input[i] >> 8) & 0xff);
+ output[j+2] = (unsigned char)((input[i] >> 16) & 0xff);
+ output[j+3] = (unsigned char)((input[i] >> 24) & 0xff);
+ }
+}
+
+/* Decodes input (unsigned char) into output (UINT4). Assumes len is
+ a multiple of 4.
+ */
+static void Decode (output, input, len)
+UINT4 *output;
+const unsigned char *input;
+unsigned int len;
+{
+ unsigned int i, j;
+
+ for (i = 0, j = 0; j < len; i++, j += 4)
+ output[i] = ((UINT4)input[j]) | (((UINT4)input[j+1]) << 8) |
+ (((UINT4)input[j+2]) << 16) | (((UINT4)input[j+3]) << 24);
+}
+
+/* Note: Replace "for loop" with standard memcpy if possible.
+ */
+
+static void MD5_memcpy (output, input, len)
+POINTER output;
+const POINTER input;
+unsigned int len;
+{
+ unsigned int i;
+
+ for (i = 0; i < len; i++)
+ output[i] = input[i];
+}
+
+/* Note: Replace "for loop" with standard memset if possible.
+ */
+static void MD5_memset (output, value, len)
+POINTER output;
+int value;
+unsigned int len;
+{
+ unsigned int i;
+
+ for (i = 0; i < len; i++)
+ ((char *)output)[i] = (char)value;
+}
diff --git a/ldap/servers/plugins/pwdstorage/ns-mta-md5_pwd.bu b/ldap/servers/plugins/pwdstorage/ns-mta-md5_pwd.bu
new file mode 100644
index 00000000..7cdd74b3
--- /dev/null
+++ b/ldap/servers/plugins/pwdstorage/ns-mta-md5_pwd.bu
@@ -0,0 +1,405 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+/*
+ * slapd hashed password routines
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <sys/types.h>
+
+#include "pwd.h"
+
+
+/*
+ * Netscape Mail Server MD5 support (compare-only; no support for encoding)
+ */
+
+static char * ns_mta_hextab = "0123456789abcdef";
+
+static void
+ns_mta_hexify(char *buffer, char *str, int len)
+{
+ char *pch = str;
+ char ch;
+ int i;
+
+ for(i = 0;i < len; i ++) {
+ ch = pch[i];
+ buffer[2*i] = ns_mta_hextab[(ch>>4)&15];
+ buffer[2*i+1] = ns_mta_hextab[ch&15];
+ }
+
+ return;
+}
+
+static char *
+ns_mta_hash_alg(char *buffer, char *salt, char *passwd)
+{
+ mta_MD5_CTX context;
+ char saltstr[2048];
+ unsigned char digest[16];
+
+ sprintf(saltstr,"%s%c%s%c%s",salt,89,passwd,247,salt);
+
+ mta_MD5Init(&context);
+ mta_MD5Update(&context,(unsigned char *)saltstr,strlen(saltstr));
+ mta_MD5Final(digest,&context);
+ ns_mta_hexify(buffer,(char*)digest,16);
+ buffer[32] = '\0';
+ return(buffer);
+
+}
+
+int
+ns_mta_md5_pw_cmp(char * clear, char *mangled)
+{
+ char mta_hash[33];
+ char mta_salt[33];
+ char buffer[65];
+
+ strncpy(mta_hash,mangled,32);
+ strncpy(mta_salt,&mangled[32],32);
+
+ mta_hash[32] = mta_salt[32] = 0;
+
+ return( strcmp(mta_hash,ns_mta_hash_alg(buffer,mta_salt,clear)));
+}
+
+
+/* MD5 code taken from reference implementation published in RFC 1321 */
+
+/* MD5C.C - RSA Data Security, Inc., MD5 message-digest algorithm */
+
+/* Copyright (C) 1991-2, RSA Data Security, Inc. Created 1991. All
+ rights reserved.
+
+ License to copy and use this software is granted provided that it
+ is identified as the "RSA Data Security, Inc. MD5 Message-Digest
+ Algorithm" in all material mentioning or referencing this software
+ or this function.
+
+ License is also granted to make and use derivative works provided
+ that such works are identified as "derived from the RSA Data
+ Security, Inc. MD5 Message-Digest Algorithm" in all material
+ mentioning or referencing the derived work.
+
+ RSA Data Security, Inc. makes no representations concerning either
+ the merchantability of this software or the suitability of this
+ software for any particular purpose. It is provided "as is"
+ without express or implied warranty of any kind.
+
+ These notices must be retained in any copies of any part of this
+ documentation and/or software.
+ */
+
+#include "pw.h"
+
+/* Constants for MD5Transform routine. */
+
+#define S11 7
+#define S12 12
+#define S13 17
+#define S14 22
+#define S21 5
+#define S22 9
+#define S23 14
+#define S24 20
+#define S31 4
+#define S32 11
+#define S33 16
+#define S34 23
+#define S41 6
+#define S42 10
+#define S43 15
+#define S44 21
+
+static void MD5Transform (UINT4 [4], const unsigned char [64]);
+static void Encode (unsigned char *, const UINT4 *, unsigned int);
+static void Decode (UINT4 *, const unsigned char *, unsigned int);
+static void MD5_memcpy (POINTER, const POINTER, unsigned int);
+static void MD5_memset (POINTER, int, unsigned int);
+
+static unsigned char PADDING[64] = {
+ 0x80, 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
+};
+
+/* F, G, H and I are basic MD5 functions.
+ */
+#define F(x, y, z) (((x) & (y)) | ((~(x)) & (z)))
+#define G(x, y, z) (((x) & (z)) | ((y) & (~(z))))
+#define H(x, y, z) ((x) ^ (y) ^ (z))
+#define I(x, y, z) ((y) ^ ((x) | (~(z))))
+
+/* ROTATE_LEFT rotates x left n bits.
+ */
+#define ROTATE_LEFT(x, n) (((x) << (n)) | ((x) >> (32-(n))))
+
+/* FF, GG, HH, and II transformations for rounds 1, 2, 3, and 4.
+Rotation is separate from addition to prevent recomputation.
+ */
+#define FF(a, b, c, d, x, s, ac) { \
+ (a) += F ((b), (c), (d)) + (x) + (UINT4)(ac); \
+ (a) = ROTATE_LEFT ((a), (s)); \
+ (a) += (b); \
+ }
+#define GG(a, b, c, d, x, s, ac) { \
+ (a) += G ((b), (c), (d)) + (x) + (UINT4)(ac); \
+ (a) = ROTATE_LEFT ((a), (s)); \
+ (a) += (b); \
+ }
+#define HH(a, b, c, d, x, s, ac) { \
+ (a) += H ((b), (c), (d)) + (x) + (UINT4)(ac); \
+ (a) = ROTATE_LEFT ((a), (s)); \
+ (a) += (b); \
+ }
+#define II(a, b, c, d, x, s, ac) { \
+ (a) += I ((b), (c), (d)) + (x) + (UINT4)(ac); \
+ (a) = ROTATE_LEFT ((a), (s)); \
+ (a) += (b); \
+ }
+
+/* MD5 initialization. Begins an MD5 operation, writing a new context.
+ */
+void mta_MD5Init (context)
+mta_MD5_CTX *context; /* context */
+{
+ context->count[0] = context->count[1] = 0;
+ /* Load magic initialization constants.
+*/
+ context->state[0] = 0x67452301;
+ context->state[1] = 0xefcdab89;
+ context->state[2] = 0x98badcfe;
+ context->state[3] = 0x10325476;
+}
+
+/* MD5 block update operation. Continues an MD5 message-digest
+ operation, processing another message block, and updating the
+ context.
+ */
+void mta_MD5Update (context, input, inputLen)
+mta_MD5_CTX *context; /* context */
+const unsigned char *input; /* input block */
+unsigned int inputLen; /* length of input block */
+{
+ unsigned int i, index, partLen;
+
+ /* Compute number of bytes mod 64 */
+ index = (unsigned int)((context->count[0] >> 3) & 0x3F);
+
+ /* Update number of bits */
+ if ((context->count[0] += ((UINT4)inputLen << 3))
+ < ((UINT4)inputLen << 3))
+ context->count[1]++;
+ context->count[1] += ((UINT4)inputLen >> 29);
+
+ partLen = 64 - index;
+
+ /* Transform as many times as possible.
+*/
+ if (inputLen >= partLen) {
+ MD5_memcpy
+ ((POINTER)&context->buffer[index], (POINTER)input, partLen);
+ MD5Transform (context->state, context->buffer);
+
+ for (i = partLen; i + 63 < inputLen; i += 64)
+ MD5Transform (context->state, &input[i]);
+
+ index = 0;
+ }
+ else
+ i = 0;
+
+ /* Buffer remaining input */
+ MD5_memcpy
+ ((POINTER)&context->buffer[index], (POINTER)&input[i],
+ inputLen-i);
+}
+
+/* MD5 finalization. Ends an MD5 message-digest operation, writing the
+ the message digest and zeroizing the context.
+ */
+void mta_MD5Final (digest, context)
+unsigned char digest[16]; /* message digest */
+mta_MD5_CTX *context; /* context */
+{
+ unsigned char bits[8];
+ unsigned int index, padLen;
+
+ /* Save number of bits */
+ Encode (bits, context->count, 8);
+
+ /* Pad out to 56 mod 64.
+*/
+ index = (unsigned int)((context->count[0] >> 3) & 0x3f);
+ padLen = (index < 56) ? (56 - index) : (120 - index);
+ mta_MD5Update (context, PADDING, padLen);
+
+ /* Append length (before padding) */
+ mta_MD5Update (context, bits, 8);
+
+ /* Store state in digest */
+ Encode (digest, context->state, 16);
+
+ /* Zeroize sensitive information.
+*/
+ MD5_memset ((POINTER)context, 0, sizeof (*context));
+}
+
+/* MD5 basic transformation. Transforms state based on block.
+ */
+static void MD5Transform (state, block)
+UINT4 state[4];
+const unsigned char block[64];
+{
+ UINT4 a = state[0], b = state[1], c = state[2], d = state[3], x[16];
+
+ Decode (x, block, 64);
+
+ /* Round 1 */
+ FF (a, b, c, d, x[ 0], S11, 0xd76aa478); /* 1 */
+ FF (d, a, b, c, x[ 1], S12, 0xe8c7b756); /* 2 */
+ FF (c, d, a, b, x[ 2], S13, 0x242070db); /* 3 */
+ FF (b, c, d, a, x[ 3], S14, 0xc1bdceee); /* 4 */
+ FF (a, b, c, d, x[ 4], S11, 0xf57c0faf); /* 5 */
+ FF (d, a, b, c, x[ 5], S12, 0x4787c62a); /* 6 */
+ FF (c, d, a, b, x[ 6], S13, 0xa8304613); /* 7 */
+ FF (b, c, d, a, x[ 7], S14, 0xfd469501); /* 8 */
+ FF (a, b, c, d, x[ 8], S11, 0x698098d8); /* 9 */
+ FF (d, a, b, c, x[ 9], S12, 0x8b44f7af); /* 10 */
+ FF (c, d, a, b, x[10], S13, 0xffff5bb1); /* 11 */
+ FF (b, c, d, a, x[11], S14, 0x895cd7be); /* 12 */
+ FF (a, b, c, d, x[12], S11, 0x6b901122); /* 13 */
+ FF (d, a, b, c, x[13], S12, 0xfd987193); /* 14 */
+ FF (c, d, a, b, x[14], S13, 0xa679438e); /* 15 */
+ FF (b, c, d, a, x[15], S14, 0x49b40821); /* 16 */
+
+ /* Round 2 */
+ GG (a, b, c, d, x[ 1], S21, 0xf61e2562); /* 17 */
+ GG (d, a, b, c, x[ 6], S22, 0xc040b340); /* 18 */
+ GG (c, d, a, b, x[11], S23, 0x265e5a51); /* 19 */
+ GG (b, c, d, a, x[ 0], S24, 0xe9b6c7aa); /* 20 */
+ GG (a, b, c, d, x[ 5], S21, 0xd62f105d); /* 21 */
+ GG (d, a, b, c, x[10], S22, 0x2441453); /* 22 */
+ GG (c, d, a, b, x[15], S23, 0xd8a1e681); /* 23 */
+ GG (b, c, d, a, x[ 4], S24, 0xe7d3fbc8); /* 24 */
+ GG (a, b, c, d, x[ 9], S21, 0x21e1cde6); /* 25 */
+ GG (d, a, b, c, x[14], S22, 0xc33707d6); /* 26 */
+ GG (c, d, a, b, x[ 3], S23, 0xf4d50d87); /* 27 */
+ GG (b, c, d, a, x[ 8], S24, 0x455a14ed); /* 28 */
+ GG (a, b, c, d, x[13], S21, 0xa9e3e905); /* 29 */
+ GG (d, a, b, c, x[ 2], S22, 0xfcefa3f8); /* 30 */
+ GG (c, d, a, b, x[ 7], S23, 0x676f02d9); /* 31 */
+ GG (b, c, d, a, x[12], S24, 0x8d2a4c8a); /* 32 */
+
+ /* Round 3 */
+ HH (a, b, c, d, x[ 5], S31, 0xfffa3942); /* 33 */
+ HH (d, a, b, c, x[ 8], S32, 0x8771f681); /* 34 */
+ HH (c, d, a, b, x[11], S33, 0x6d9d6122); /* 35 */
+ HH (b, c, d, a, x[14], S34, 0xfde5380c); /* 36 */
+ HH (a, b, c, d, x[ 1], S31, 0xa4beea44); /* 37 */
+ HH (d, a, b, c, x[ 4], S32, 0x4bdecfa9); /* 38 */
+ HH (c, d, a, b, x[ 7], S33, 0xf6bb4b60); /* 39 */
+ HH (b, c, d, a, x[10], S34, 0xbebfbc70); /* 40 */
+ HH (a, b, c, d, x[13], S31, 0x289b7ec6); /* 41 */
+ HH (d, a, b, c, x[ 0], S32, 0xeaa127fa); /* 42 */
+ HH (c, d, a, b, x[ 3], S33, 0xd4ef3085); /* 43 */
+ HH (b, c, d, a, x[ 6], S34, 0x4881d05); /* 44 */
+ HH (a, b, c, d, x[ 9], S31, 0xd9d4d039); /* 45 */
+ HH (d, a, b, c, x[12], S32, 0xe6db99e5); /* 46 */
+ HH (c, d, a, b, x[15], S33, 0x1fa27cf8); /* 47 */
+ HH (b, c, d, a, x[ 2], S34, 0xc4ac5665); /* 48 */
+
+ /* Round 4 */
+ II (a, b, c, d, x[ 0], S41, 0xf4292244); /* 49 */
+ II (d, a, b, c, x[ 7], S42, 0x432aff97); /* 50 */
+ II (c, d, a, b, x[14], S43, 0xab9423a7); /* 51 */
+ II (b, c, d, a, x[ 5], S44, 0xfc93a039); /* 52 */
+ II (a, b, c, d, x[12], S41, 0x655b59c3); /* 53 */
+ II (d, a, b, c, x[ 3], S42, 0x8f0ccc92); /* 54 */
+ II (c, d, a, b, x[10], S43, 0xffeff47d); /* 55 */
+ II (b, c, d, a, x[ 1], S44, 0x85845dd1); /* 56 */
+ II (a, b, c, d, x[ 8], S41, 0x6fa87e4f); /* 57 */
+ II (d, a, b, c, x[15], S42, 0xfe2ce6e0); /* 58 */
+ II (c, d, a, b, x[ 6], S43, 0xa3014314); /* 59 */
+ II (b, c, d, a, x[13], S44, 0x4e0811a1); /* 60 */
+ II (a, b, c, d, x[ 4], S41, 0xf7537e82); /* 61 */
+ II (d, a, b, c, x[11], S42, 0xbd3af235); /* 62 */
+ II (c, d, a, b, x[ 2], S43, 0x2ad7d2bb); /* 63 */
+ II (b, c, d, a, x[ 9], S44, 0xeb86d391); /* 64 */
+
+ state[0] += a;
+ state[1] += b;
+ state[2] += c;
+ state[3] += d;
+
+ /* Zeroize sensitive information.
+*/
+ MD5_memset ((POINTER)x, 0, sizeof (x));
+}
+
+/* Encodes input (UINT4) into output (unsigned char). Assumes len is
+ a multiple of 4.
+ */
+static void Encode (output, input, len)
+unsigned char *output;
+const UINT4 *input;
+unsigned int len;
+{
+ unsigned int i, j;
+
+ for (i = 0, j = 0; j < len; i++, j += 4) {
+ output[j] = (unsigned char)(input[i] & 0xff);
+ output[j+1] = (unsigned char)((input[i] >> 8) & 0xff);
+ output[j+2] = (unsigned char)((input[i] >> 16) & 0xff);
+ output[j+3] = (unsigned char)((input[i] >> 24) & 0xff);
+ }
+}
+
+/* Decodes input (unsigned char) into output (UINT4). Assumes len is
+ a multiple of 4.
+ */
+static void Decode (output, input, len)
+UINT4 *output;
+const unsigned char *input;
+unsigned int len;
+{
+ unsigned int i, j;
+
+ for (i = 0, j = 0; j < len; i++, j += 4)
+ output[i] = ((UINT4)input[j]) | (((UINT4)input[j+1]) << 8) |
+ (((UINT4)input[j+2]) << 16) | (((UINT4)input[j+3]) << 24);
+}
+
+/* Note: Replace "for loop" with standard memcpy if possible.
+ */
+
+static void MD5_memcpy (output, input, len)
+POINTER output;
+const POINTER input;
+unsigned int len;
+{
+ unsigned int i;
+
+ for (i = 0; i < len; i++)
+ output[i] = input[i];
+}
+
+/* Note: Replace "for loop" with standard memset if possible.
+ */
+static void MD5_memset (output, value, len)
+POINTER output;
+int value;
+unsigned int len;
+{
+ unsigned int i;
+
+ for (i = 0; i < len; i++)
+ ((char *)output)[i] = (char)value;
+}
+
diff --git a/ldap/servers/plugins/pwdstorage/ns-mta-md5_pwd.c b/ldap/servers/plugins/pwdstorage/ns-mta-md5_pwd.c
new file mode 100644
index 00000000..f3c11a10
--- /dev/null
+++ b/ldap/servers/plugins/pwdstorage/ns-mta-md5_pwd.c
@@ -0,0 +1,81 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+/*
+ * slapd hashed password routines
+ *
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <sys/types.h>
+
+#include "pwdstorage.h"
+
+#include "md5.h" /* JCM - This is a core server header... These functions could be made part of the slapi API. */
+
+
+/*
+ * Netscape Mail Server MD5 support (compare-only; no support for encoding)
+ */
+
+static char * ns_mta_hextab = "0123456789abcdef";
+
+static void
+ns_mta_hexify(char *buffer, char *str, int len)
+{
+ char *pch = str;
+ char ch;
+ int i;
+
+ for(i = 0;i < len; i ++) {
+ ch = pch[i];
+ buffer[2*i] = ns_mta_hextab[(ch>>4)&15];
+ buffer[2*i+1] = ns_mta_hextab[ch&15];
+ }
+
+ return;
+}
+
+static char *
+ns_mta_hash_alg(char *buffer, char *salt, char *passwd)
+{
+ mta_MD5_CTX context;
+ char *saltstr;
+ unsigned char digest[16];
+
+
+ if ( (saltstr = slapi_ch_malloc(strlen(salt)*2 + strlen(passwd) + 3))
+ == NULL ) {
+ return( NULL );
+ }
+
+ sprintf(saltstr,"%s%c%s%c%s",salt,89,passwd,247,salt);
+
+ mta_MD5Init(&context);
+ mta_MD5Update(&context,(unsigned char *)saltstr,strlen(saltstr));
+ mta_MD5Final(digest,&context);
+ ns_mta_hexify(buffer,(char*)digest,16);
+ buffer[32] = '\0';
+ slapi_ch_free((void**)&saltstr);
+ return(buffer);
+
+}
+
+int
+ns_mta_md5_pw_cmp(char * clear, char *mangled)
+{
+ char mta_hash[33];
+ char mta_salt[33];
+ char buffer[65];
+
+ strncpy(mta_hash,mangled,32);
+ strncpy(mta_salt,&mangled[32],32);
+
+ mta_hash[32] = mta_salt[32] = 0;
+
+ return( strcmp(mta_hash,ns_mta_hash_alg(buffer,mta_salt,clear)));
+}
+
diff --git a/ldap/servers/plugins/pwdstorage/pwd_init.c b/ldap/servers/plugins/pwdstorage/pwd_init.c
new file mode 100644
index 00000000..4ee5138b
--- /dev/null
+++ b/ldap/servers/plugins/pwdstorage/pwd_init.c
@@ -0,0 +1,146 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+
+#include <stdio.h>
+#include <string.h>
+#include <sys/types.h>
+
+#include "pwdstorage.h"
+#include "dirver.h"
+
+static Slapi_PluginDesc sha_pdesc = { "sha-password-storage-scheme", PLUGIN_MAGIC_VENDOR_STR, PRODUCTTEXT, "Secure Hashing Algorithm (SHA)" };
+
+static Slapi_PluginDesc ssha_pdesc = { "ssha-password-storage-scheme", PLUGIN_MAGIC_VENDOR_STR, PRODUCTTEXT, "Salted Secure Hashing Algorithm (SSHA)" };
+
+static Slapi_PluginDesc crypt_pdesc = { "crypt-password-storage-scheme", PLUGIN_MAGIC_VENDOR_STR, PRODUCTTEXT, "Unix crypt algorithm (CRYPT)" };
+
+static Slapi_PluginDesc clear_pdesc = { "clear-password-storage-scheme", PLUGIN_MAGIC_VENDOR_STR, PRODUCTTEXT, "No encryption (CLEAR)" };
+
+static Slapi_PluginDesc ns_mta_md5_pdesc = { "NS-MTA-MD5-password-storage-scheme", PLUGIN_MAGIC_VENDOR_STR, PRODUCTTEXT, "Netscape MD5 (NS-MTA-MD5)" };
+
+static char *plugin_name = "NSPwdStoragePlugin";
+
+int
+sha_pwd_storage_scheme_init( Slapi_PBlock *pb )
+{
+ int rc;
+ char *name;
+
+ slapi_log_error( SLAPI_LOG_PLUGIN, plugin_name, "=> sha_pwd_storage_scheme_init\n" );
+
+ rc = slapi_pblock_set( pb, SLAPI_PLUGIN_VERSION,
+ (void *) SLAPI_PLUGIN_VERSION_01 );
+ rc |= slapi_pblock_set( pb, SLAPI_PLUGIN_DESCRIPTION,
+ (void *)&sha_pdesc );
+ rc |= slapi_pblock_set( pb, SLAPI_PLUGIN_PWD_STORAGE_SCHEME_ENC_FN,
+ (void *) sha1_pw_enc);
+ rc |= slapi_pblock_set( pb, SLAPI_PLUGIN_PWD_STORAGE_SCHEME_CMP_FN,
+ (void *) sha1_pw_cmp );
+ name = slapi_ch_strdup("SHA");
+ rc |= slapi_pblock_set( pb, SLAPI_PLUGIN_PWD_STORAGE_SCHEME_NAME,
+ name );
+
+ slapi_log_error( SLAPI_LOG_PLUGIN, plugin_name, "<= sha_pwd_storage_scheme_init %d\n\n", rc );
+
+ return( rc );
+}
+
+int
+ssha_pwd_storage_scheme_init( Slapi_PBlock *pb )
+{
+ int rc;
+ char *name;
+
+ slapi_log_error( SLAPI_LOG_PLUGIN, plugin_name, "=> ssha_pwd_storage_scheme_init\n" );
+
+ rc = slapi_pblock_set( pb, SLAPI_PLUGIN_VERSION,
+ (void *) SLAPI_PLUGIN_VERSION_01 );
+ rc |= slapi_pblock_set( pb, SLAPI_PLUGIN_DESCRIPTION,
+ (void *)&ssha_pdesc );
+ rc |= slapi_pblock_set( pb, SLAPI_PLUGIN_PWD_STORAGE_SCHEME_ENC_FN,
+ (void *) salted_sha1_pw_enc );
+ rc |= slapi_pblock_set( pb, SLAPI_PLUGIN_PWD_STORAGE_SCHEME_CMP_FN,
+ (void *) sha1_pw_cmp );
+ name = slapi_ch_strdup("SSHA");
+ rc |= slapi_pblock_set( pb, SLAPI_PLUGIN_PWD_STORAGE_SCHEME_NAME,
+ name );
+
+ slapi_log_error( SLAPI_LOG_PLUGIN, plugin_name, "<= ssha_pwd_storage_scheme_init %d\n\n", rc );
+ return( rc );
+}
+
+int
+crypt_pwd_storage_scheme_init( Slapi_PBlock *pb )
+{
+ int rc;
+ char *name;
+
+ slapi_log_error( SLAPI_LOG_PLUGIN, plugin_name, "=> crypt_pwd_storage_scheme_init\n" );
+
+ crypt_init();
+ rc = slapi_pblock_set( pb, SLAPI_PLUGIN_VERSION,
+ (void *) SLAPI_PLUGIN_VERSION_01 );
+ rc |= slapi_pblock_set( pb, SLAPI_PLUGIN_DESCRIPTION,
+ (void *)&crypt_pdesc );
+ rc |= slapi_pblock_set( pb, SLAPI_PLUGIN_PWD_STORAGE_SCHEME_ENC_FN,
+ (void *) crypt_pw_enc );
+ rc |= slapi_pblock_set( pb, SLAPI_PLUGIN_PWD_STORAGE_SCHEME_CMP_FN,
+ (void *) crypt_pw_cmp );
+ name = slapi_ch_strdup("CRYPT");
+ rc |= slapi_pblock_set( pb, SLAPI_PLUGIN_PWD_STORAGE_SCHEME_NAME,
+ name );
+
+ slapi_log_error( SLAPI_LOG_PLUGIN, plugin_name, "<= crypt_pwd_storage_scheme_init %d\n\n", rc );
+ return( rc );
+}
+
+int
+clear_pwd_storage_scheme_init( Slapi_PBlock *pb )
+{
+ int rc;
+ char *name;
+
+ slapi_log_error( SLAPI_LOG_PLUGIN, plugin_name, "=> clear_pwd_storage_scheme_init\n" );
+
+ rc = slapi_pblock_set( pb, SLAPI_PLUGIN_VERSION,
+ (void *) SLAPI_PLUGIN_VERSION_01 );
+ rc |= slapi_pblock_set( pb, SLAPI_PLUGIN_DESCRIPTION,
+ (void *)&clear_pdesc );
+ rc |= slapi_pblock_set( pb, SLAPI_PLUGIN_PWD_STORAGE_SCHEME_ENC_FN,
+ (void *) clear_pw_enc );
+ rc |= slapi_pblock_set( pb, SLAPI_PLUGIN_PWD_STORAGE_SCHEME_CMP_FN,
+ (void *) clear_pw_cmp );
+ name = slapi_ch_strdup("CLEAR");
+ rc |= slapi_pblock_set( pb, SLAPI_PLUGIN_PWD_STORAGE_SCHEME_NAME,
+ name );
+
+ slapi_log_error( SLAPI_LOG_PLUGIN, plugin_name, "<= clear_pwd_storage_scheme_init %d\n\n", rc );
+ return( rc );
+}
+
+int
+ns_mta_md5_pwd_storage_scheme_init( Slapi_PBlock *pb )
+{
+ int rc;
+ char *name;
+
+ slapi_log_error( SLAPI_LOG_PLUGIN, plugin_name, "=> ns_mta_md5_pwd_storage_scheme_init\n" );
+
+ rc = slapi_pblock_set( pb, SLAPI_PLUGIN_VERSION,
+ (void *) SLAPI_PLUGIN_VERSION_01 );
+ rc |= slapi_pblock_set( pb, SLAPI_PLUGIN_DESCRIPTION,
+ (void *)&ns_mta_md5_pdesc );
+ rc |= slapi_pblock_set( pb, SLAPI_PLUGIN_PWD_STORAGE_SCHEME_ENC_FN,
+ (void *) NULL );
+ rc |= slapi_pblock_set( pb, SLAPI_PLUGIN_PWD_STORAGE_SCHEME_CMP_FN,
+ (void *) ns_mta_md5_pw_cmp );
+ name = slapi_ch_strdup("NS-MTA-MD5");
+ rc |= slapi_pblock_set( pb, SLAPI_PLUGIN_PWD_STORAGE_SCHEME_NAME,
+ name );
+
+ slapi_log_error( SLAPI_LOG_PLUGIN, plugin_name, "<= ns_mta_md5_pwd_storage_scheme_init %d\n\n", rc );
+ return( rc );
+}
diff --git a/ldap/servers/plugins/pwdstorage/pwdstorage.h b/ldap/servers/plugins/pwdstorage/pwdstorage.h
new file mode 100644
index 00000000..0e938cb9
--- /dev/null
+++ b/ldap/servers/plugins/pwdstorage/pwdstorage.h
@@ -0,0 +1,99 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+#ifndef _PWDSTORAGE_H
+#define _PWDSTORAGE_H
+
+#include "slapi-plugin.h"
+#include <ssl.h>
+#include "nspr.h"
+#include "ldif.h"
+#include "md5.h"
+
+#include <dirlite_strings.h> /* PLUGIN_MAGIC_VENDOR_STR */
+
+#define PWD_HASH_PREFIX_START '{'
+#define PWD_HASH_PREFIX_END '}'
+
+#define SHA1_SCHEME_NAME "SHA"
+#define SHA1_NAME_LEN 3
+#define SALTED_SHA1_SCHEME_NAME "SSHA"
+#define SALTED_SHA1_NAME_LEN 4
+#define CRYPT_SCHEME_NAME "crypt"
+#define CRYPT_NAME_LEN 5
+#define NS_MTA_MD5_SCHEME_NAME "NS-MTA-MD5"
+#define NS_MTA_MD5_NAME_LEN 10
+#define CLEARTEXT_SCHEME_NAME "clear"
+#define CLEARTEXT_NAME_LEN 5
+
+SECStatus sha1_salted_hash(unsigned char *hash_out, char *pwd, struct berval *salt);
+int sha1_pw_cmp( char *userpwd, char *dbpwd );
+char * sha1_pw_enc( char *pwd );
+char * salted_sha1_pw_enc( char *pwd );
+int clear_pw_cmp( char *userpwd, char *dbpwd );
+char *clear_pw_enc( char *pwd );
+void crypt_init();
+int crypt_pw_cmp( char *userpwd, char *dbpwd );
+char *crypt_pw_enc( char *pwd );
+int ns_mta_md5_pw_cmp( char *userpwd, char *dbpwd );
+
+
+#if !defined(NET_SSL)
+/******************************************/
+/*
+ * Some of the stuff below depends on a definition for uint32, so
+ * we include one here. Other definitions appear in nspr/prtypes.h,
+ * at least. All the platforms we support use 32-bit ints.
+ */
+typedef unsigned int uint32;
+
+
+/******************************************/
+/*
+ * The following is from ds.h, which the libsec sec.h stuff depends on (see
+ * comment below).
+ */
+/*
+** A status code. Status's are used by procedures that return status
+** values. Again the motivation is so that a compiler can generate
+** warnings when return values are wrong. Correct testing of status codes:
+**
+** DSStatus rv;
+** rv = some_function (some_argument);
+** if (rv != DSSuccess)
+** do_an_error_thing();
+**
+*/
+typedef enum DSStatusEnum {
+ DSWouldBlock = -2,
+ DSFailure = -1,
+ DSSuccess = 0
+} DSStatus;
+
+
+/******************************************/
+/*
+ * All of the SHA1-related defines are from libsec's "sec.h" -- including
+ * it directly pulls in way too much stuff that we conflict with. Ugh.
+ */
+
+/*
+ * Number of bytes each hash algorithm produces
+ */
+#define SHA1_LENGTH 20
+
+/******************************************/
+/*
+** SHA-1 secure hash function
+*/
+
+/*
+** Hash a null terminated string "src" into "dest" using SHA-1
+*/
+DSStatus SHA1_Hash(unsigned char *dest, char *src);
+
+#endif /* !defined(NET_SSL) */
+
+#endif /* _PWDSTORAGE_H */
diff --git a/ldap/servers/plugins/pwdstorage/sha_pwd.c b/ldap/servers/plugins/pwdstorage/sha_pwd.c
new file mode 100644
index 00000000..c8cf435d
--- /dev/null
+++ b/ldap/servers/plugins/pwdstorage/sha_pwd.c
@@ -0,0 +1,111 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+/*
+ * slapd hashed password routines
+ *
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <sys/types.h>
+
+#include "pwdstorage.h"
+
+#if defined(NET_SSL)
+#include <sechash.h>
+#endif /* NET_SSL */
+
+#define SHA1_SALT_LENGTH 8 /* number of bytes of data in salt */
+#define NOT_FIRST_TIME (time_t)1 /* not the first logon */
+
+static char *hasherrmsg = "pw_cmp: %s userPassword \"%s\" is the wrong length or is not properly encoded BASE64\n";
+
+static char *plugin_name = "NSPwdStoragePlugin";
+
+#define DS40B1_SALTED_SHA_LENGTH 18
+/* Directory Server 4.0 Beta 1 implemented a scheme that stored
+ * 8 bytes of salt plus the first 10 bytes of the SHA-1 digest.
+ * It's obsolescent now, but we still handle such stored values.
+ */
+
+int
+sha1_pw_cmp (char *userpwd, char *dbpwd )
+{
+ /*
+ * SHA1 passwords are stored in the database as SHA1_LENGTH bytes of
+ * hash, followed by zero or more bytes of salt, all BASE64 encoded.
+ */
+ int result = 1; /* failure */
+ unsigned char userhash[SHA1_LENGTH];
+ unsigned char quick_dbhash[SHA1_LENGTH + SHA1_SALT_LENGTH + 3];
+ unsigned char *dbhash = quick_dbhash;
+ struct berval salt;
+ int hash_len; /* must be a signed valued -- see below */
+
+ /*
+ * Decode hash stored in database.
+ *
+ * Note that ldif_base64_decode() returns a value less than zero to
+ * indicate that a decoding error occurred, so it is critical that
+ * hash_len be a signed value.
+ */
+ hash_len = (((strlen(dbpwd) + 3) / 4) * 3); /* maybe less */
+ if ( hash_len > sizeof(quick_dbhash) ) { /* get more space: */
+ dbhash = (unsigned char*) slapi_ch_malloc( hash_len );
+ if ( dbhash == NULL ) goto loser;
+ }
+ hash_len = ldif_base64_decode( dbpwd, dbhash );
+ if ( hash_len >= SHA1_LENGTH ) {
+ salt.bv_val = (void*)(dbhash + SHA1_LENGTH);
+ salt.bv_len = hash_len - SHA1_LENGTH;
+ } else if ( hash_len == DS40B1_SALTED_SHA_LENGTH ) {
+ salt.bv_val = (void*)dbhash;
+ salt.bv_len = 8;
+ } else { /* unsupported, invalid BASE64 (hash_len < 0), or similar */
+ slapi_log_error( SLAPI_LOG_PLUGIN, plugin_name, hasherrmsg, SHA1_SCHEME_NAME, dbpwd );
+ goto loser;
+ }
+
+ /* SHA1 hash the user's key */
+ if ( sha1_salted_hash( userhash, userpwd, &salt ) != SECSuccess ) {
+ slapi_log_error( SLAPI_LOG_PLUGIN, plugin_name, "sha1_pw_cmp: SHA1_Hash() failed\n");
+ goto loser;
+ }
+
+ /* the proof is in the comparison... */
+ result = ( hash_len == DS40B1_SALTED_SHA_LENGTH ) ?
+ ( memcmp( userhash, dbhash + 8, hash_len - 8 )) :
+ ( memcmp( userhash, dbhash, SHA1_LENGTH ));
+
+ loser:
+ if ( dbhash && dbhash != quick_dbhash ) slapi_ch_free( (void**)&dbhash );
+ return result;
+}
+
+
+char *
+sha1_pw_enc( char *pwd )
+{
+ unsigned char hash[ SHA1_LENGTH ];
+ char *enc;
+
+ /* SHA1 hash the user's key */
+ if ( sha1_salted_hash( hash, pwd, NULL ) != SECSuccess ) {
+ return( NULL );
+ }
+
+ if (( enc = slapi_ch_malloc( 3 + SHA1_NAME_LEN +
+ LDIF_BASE64_LEN( SHA1_LENGTH ))) == NULL ) {
+ return( NULL );
+ }
+
+ sprintf( enc, "%c%s%c", PWD_HASH_PREFIX_START, SHA1_SCHEME_NAME,
+ PWD_HASH_PREFIX_END );
+ (void)ldif_base64_encode( hash, enc + 2 + SHA1_NAME_LEN,
+ SHA1_LENGTH, -1 );
+
+ return( enc );
+}
diff --git a/ldap/servers/plugins/pwdstorage/ssha_pwd.c b/ldap/servers/plugins/pwdstorage/ssha_pwd.c
new file mode 100644
index 00000000..b3c82d6d
--- /dev/null
+++ b/ldap/servers/plugins/pwdstorage/ssha_pwd.c
@@ -0,0 +1,112 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+/*
+ * slapd hashed password routines
+ *
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <sys/types.h>
+
+#include "pwdstorage.h"
+#include "prtime.h"
+#include "prlong.h"
+
+#if defined(NET_SSL)
+#include <pk11func.h>
+#include <pk11pqg.h>
+#endif /* NET_SSL */
+
+#define SHA1_SALT_LENGTH 8 /* number of bytes of data in salt */
+
+static void ssha_rand_array(void *randx, size_t len);
+
+
+/* ***************************************************
+ Identical function to slapi_rand_array in util.c, but can't use
+ that here since this module is included in libds_admin, which doesn't
+ link to libslapd.
+ *************************************************** */
+static void
+ssha_rand_array(void *randx, size_t len)
+{
+ PK11_RandomUpdate(randx, len);
+ PK11_GenerateRandom((unsigned char *)randx, (int)len);
+}
+
+/*
+ * A salted SHA1 hash
+ * if salt is null, no salt is used (this is for backward compatibility)
+*/
+SECStatus
+sha1_salted_hash(unsigned char *hash_out, char *pwd, struct berval *salt)
+{
+ PK11Context *ctx;
+ unsigned int outLen;
+ SECStatus rc;
+
+ if (salt && salt->bv_len) {
+ ctx = PK11_CreateDigestContext(SEC_OID_SHA1);
+ if (ctx == NULL) {
+ rc = SECFailure;
+ }
+ else {
+ PK11_DigestBegin(ctx);
+ PK11_DigestOp(ctx, (unsigned char*)pwd, strlen(pwd));
+ PK11_DigestOp(ctx, (unsigned char*)(salt->bv_val), salt->bv_len);
+ PK11_DigestFinal(ctx, hash_out, &outLen, SHA1_LENGTH);
+ PK11_DestroyContext(ctx, 1);
+ if (outLen == SHA1_LENGTH)
+ rc = SECSuccess;
+ else
+ rc = SECFailure;
+ }
+ }
+ else {
+ /*backward compatibility*/
+ rc = PK11_HashBuf(SEC_OID_SHA1, hash_out, (unsigned char *)pwd, strlen(pwd));
+ }
+
+ return rc;
+}
+
+char *
+salted_sha1_pw_enc( char *pwd )
+{
+ unsigned char hash[ SHA1_LENGTH + SHA1_SALT_LENGTH ];
+ unsigned char *salt = hash + SHA1_LENGTH;
+ struct berval saltval;
+ char *enc;
+
+ saltval.bv_val = (void*)salt;
+ saltval.bv_len = SHA1_SALT_LENGTH;
+
+ /* generate a new random salt */
+ /* Note: the uninitialized salt array provides a little extra entropy
+ * to the random array generation, but it is not really needed since
+ * PK11_GenerateRandom takes care of seeding. In any case, it doesn't
+ * hurt. */
+ ssha_rand_array( salt, SHA1_SALT_LENGTH );
+
+ /* SHA1 hash the user's key */
+ if ( sha1_salted_hash( hash, pwd, &saltval ) != SECSuccess ) {
+ return( NULL );
+ }
+
+ if (( enc = PR_Malloc( 3 + SALTED_SHA1_NAME_LEN +
+ LDIF_BASE64_LEN(sizeof(hash)))) == NULL ) {
+ return( NULL );
+ }
+
+ sprintf( enc, "%c%s%c", PWD_HASH_PREFIX_START, SALTED_SHA1_SCHEME_NAME,
+ PWD_HASH_PREFIX_END );
+ (void)ldif_base64_encode( hash, enc + 2 + SALTED_SHA1_NAME_LEN,
+ sizeof(hash), -1 );
+
+ return( enc );
+}
+
diff --git a/ldap/servers/plugins/referint/Makefile b/ldap/servers/plugins/referint/Makefile
new file mode 100644
index 00000000..acf09c42
--- /dev/null
+++ b/ldap/servers/plugins/referint/Makefile
@@ -0,0 +1,72 @@
+#
+# BEGIN COPYRIGHT BLOCK
+# Copyright 2001 Sun Microsystems, Inc.
+# Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+# All rights reserved.
+# END COPYRIGHT BLOCK
+#
+LDAP_SRC = ../../..
+MCOM_ROOT = ../../../../..
+
+NOSTDCLEAN=true # don't let nsconfig.mk define target clean
+NOSTDSTRIP=true # don't let nsconfig.mk define target strip
+NSPR20=true # probably should be defined somewhere else (not sure where)
+
+OBJDEST = $(OBJDIR)/lib/referint-plugin
+LIBDIR = $(LIB_RELDIR)
+
+include $(MCOM_ROOT)/ldapserver/nsconfig.mk
+include $(LDAP_SRC)/nsldap.mk
+
+ifeq ($(ARCH), WINNT)
+DEF_FILE:=./referint.def
+endif
+
+REFERINT_OBJS = referint.o
+OBJS = $(addprefix $(OBJDEST)/, $(REFERINT_OBJS))
+
+INCLUDES += -I../../slapd -I../../../include
+CFLAGS+=$(SLCFLAGS) -DSLAPD_LOGGING
+
+EXTRA_LIBS_DEP += $(LIBSLAPD_DEP) $(LDAPLINK_DEP) $(NSPRLINK_DEP)
+EXTRA_LIBS += $(LIBSLAPD) $(LDAPLINK) $(NSPRLINK)
+
+ifeq ($(ARCH), HPUX)
+EXTRA_LIBS_DEP += $(SECURITY_DEP)
+EXTRA_LIBS += $(SECURITYLINK)
+endif
+
+ifeq ($(ARCH), WINNT)
+REFERINT_DLL_OBJ = $(addprefix $(OBJDEST)/, dllmain.o)
+endif
+
+ifeq ($(ARCH), AIX)
+LD=ld
+endif
+
+REFERINT= $(addprefix $(LIBDIR)/, $(REFERINT_DLL).$(DLL_SUFFIX))
+
+clientSDK:
+
+all: $(OBJDEST) $(LIBDIR) $(REFERINT)
+
+ifeq ($(ARCH), WINNT)
+$(REFERINT): $(OBJS) $(REFERINT_DLL_OBJ) $(DEF_FILE)
+ $(LINK_DLL) $(REFERINT_DLL_OBJ) $(EXTRA_LIBS) /DEF:$(DEF_FILE)
+else
+$(REFERINT): $(OBJS) $(REFERINT_DLL_OBJ)
+ $(LINK_DLL) $(REFERINT_DLL_OBJ) $(EXTRA_LIBS)
+endif
+
+
+veryclean: clean
+
+clean:
+ $(RM) $(OBJS)
+ifeq ($(ARCH), WINNT)
+ $(RM) $(REFERINT_DLL_OBJ)
+endif
+ $(RM) $(REFERINT)
+
+$(OBJDEST):
+ $(MKDIR) $(OBJDEST)
diff --git a/ldap/servers/plugins/referint/dllmain.c b/ldap/servers/plugins/referint/dllmain.c
new file mode 100644
index 00000000..96bfcbb0
--- /dev/null
+++ b/ldap/servers/plugins/referint/dllmain.c
@@ -0,0 +1,95 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+/*
+ * Microsoft Windows specifics for BACK-LDBM DLL
+ */
+#include "ldap.h"
+
+
+#ifdef _WIN32
+/* Lifted from Q125688
+ * How to Port a 16-bit DLL to a Win32 DLL
+ * on the MSVC 4.0 CD
+ */
+BOOL WINAPI DllMain (HANDLE hModule, DWORD fdwReason, LPVOID lpReserved)
+{
+ WSADATA wsadata;
+
+ switch (fdwReason)
+ {
+ case DLL_PROCESS_ATTACH:
+ /* Code from LibMain inserted here. Return TRUE to keep the
+ DLL loaded or return FALSE to fail loading the DLL.
+
+ You may have to modify the code in your original LibMain to
+ account for the fact that it may be called more than once.
+ You will get one DLL_PROCESS_ATTACH for each process that
+ loads the DLL. This is different from LibMain which gets
+ called only once when the DLL is loaded. The only time this
+ is critical is when you are using shared data sections.
+ If you are using shared data sections for statically
+ allocated data, you will need to be careful to initialize it
+ only once. Check your code carefully.
+
+ Certain one-time initializations may now need to be done for
+ each process that attaches. You may also not need code from
+ your original LibMain because the operating system may now
+ be doing it for you.
+ */
+ /*
+ * 16 bit code calls UnlockData()
+ * which is mapped to UnlockSegment in windows.h
+ * in 32 bit world UnlockData is not defined anywhere
+ * UnlockSegment is mapped to GlobalUnfix in winbase.h
+ * and the docs for both UnlockSegment and GlobalUnfix say
+ * ".. function is oboslete. Segments have no meaning
+ * in the 32-bit environment". So we do nothing here.
+ */
+
+ if( errno = WSAStartup(0x0101, &wsadata ) != 0 )
+ return FALSE;
+
+ break;
+
+ case DLL_THREAD_ATTACH:
+ /* Called each time a thread is created in a process that has
+ already loaded (attached to) this DLL. Does not get called
+ for each thread that exists in the process before it loaded
+ the DLL.
+
+ Do thread-specific initialization here.
+ */
+ break;
+
+ case DLL_THREAD_DETACH:
+ /* Same as above, but called when a thread in the process
+ exits.
+
+ Do thread-specific cleanup here.
+ */
+ break;
+
+ case DLL_PROCESS_DETACH:
+ /* Code from _WEP inserted here. This code may (like the
+ LibMain) not be necessary. Check to make certain that the
+ operating system is not doing it for you.
+ */
+ WSACleanup();
+
+ break;
+ }
+ /* The return value is only used for DLL_PROCESS_ATTACH; all other
+ conditions are ignored. */
+ return TRUE; // successful DLL_PROCESS_ATTACH
+}
+#else
+int CALLBACK
+LibMain( HINSTANCE hinst, WORD wDataSeg, WORD cbHeapSize, LPSTR lpszCmdLine )
+{
+ /*UnlockData( 0 );*/
+ return( 1 );
+}
+#endif
diff --git a/ldap/servers/plugins/referint/referint.c b/ldap/servers/plugins/referint/referint.c
new file mode 100644
index 00000000..e0ad15fd
--- /dev/null
+++ b/ldap/servers/plugins/referint/referint.c
@@ -0,0 +1,808 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+#include <stdio.h>
+#include <string.h>
+#include "portable.h"
+#include "slapi-plugin.h"
+#include "slap.h"
+#include <dirlite_strings.h> /* PLUGIN_MAGIC_VENDOR_STR */
+#include "dirver.h"
+
+/* include NSPR header files */
+#include "prthread.h"
+#include "prlock.h"
+#include "prerror.h"
+#include "prcvar.h"
+#include "prio.h"
+
+/* get file mode flags for unix */
+#ifndef _WIN32
+#include <sys/stat.h>
+#endif
+
+#define REFERINT_PLUGIN_SUBSYSTEM "referint-plugin" /* used for logging */
+
+#ifdef _WIN32
+#define REFERINT_DEFAULT_FILE_MODE 0
+#else
+#define REFERINT_DEFAULT_FILE_MODE S_IRUSR | S_IWUSR
+#endif
+
+
+#define MAX_LINE 2048
+#define READ_BUFSIZE 4096
+#define MY_EOF 0
+
+/* function prototypes */
+int referint_postop_init( Slapi_PBlock *pb );
+int referint_postop_del( Slapi_PBlock *pb );
+int referint_postop_modrdn( Slapi_PBlock *pb );
+int referint_postop_start( Slapi_PBlock *pb);
+int referint_postop_close( Slapi_PBlock *pb);
+int update_integrity(char **argv, char *origDN, char *newrDN, int logChanges);
+void referint_thread_func(void *arg);
+int GetNextLine(char *dest, int size_dest, PRFileDesc *stream);
+void writeintegritylog(char *logfilename, char *dn, char *newrdn);
+int my_fgetc(PRFileDesc *stream);
+
+/* global thread control stuff */
+
+static PRLock *referint_mutex = NULL;
+static PRThread *referint_tid = NULL;
+int keeprunning = 0;
+
+static PRLock *keeprunning_mutex = NULL;
+static PRCondVar *keeprunning_cv = NULL;
+
+static Slapi_PluginDesc pdesc = { "referint", PLUGIN_MAGIC_VENDOR_STR, PRODUCTTEXT,
+ "referential integrity plugin" };
+
+static void* referint_plugin_identity = NULL;
+
+#ifdef _WIN32
+int *module_ldap_debug = 0;
+
+void plugin_init_debug_level(int *level_ptr)
+{
+ module_ldap_debug = level_ptr;
+}
+#endif
+
+int
+referint_postop_init( Slapi_PBlock *pb )
+{
+
+ /*
+ * Get plugin identity and stored it for later use
+ * Used for internal operations
+ */
+
+ slapi_pblock_get (pb, SLAPI_PLUGIN_IDENTITY, &referint_plugin_identity);
+ PR_ASSERT (referint_plugin_identity);
+
+ if ( slapi_pblock_set( pb, SLAPI_PLUGIN_VERSION,
+ SLAPI_PLUGIN_VERSION_01 ) != 0 ||
+ slapi_pblock_set( pb, SLAPI_PLUGIN_DESCRIPTION,
+ (void *)&pdesc ) != 0 ||
+ slapi_pblock_set( pb, SLAPI_PLUGIN_POST_DELETE_FN,
+ (void *) referint_postop_del ) != 0 ||
+ slapi_pblock_set( pb, SLAPI_PLUGIN_POST_MODRDN_FN,
+ (void *) referint_postop_modrdn ) != 0 ||
+ slapi_pblock_set(pb, SLAPI_PLUGIN_START_FN,
+ (void *) referint_postop_start ) != 0 ||
+ slapi_pblock_set(pb, SLAPI_PLUGIN_CLOSE_FN,
+ (void *) referint_postop_close ) != 0)
+ {
+ slapi_log_error( SLAPI_LOG_FATAL, REFERINT_PLUGIN_SUBSYSTEM,
+ "referint_postop_init failed\n" );
+ return( -1 );
+ }
+
+ return( 0 );
+}
+
+
+int
+referint_postop_del( Slapi_PBlock *pb )
+{
+ char *dn;
+ int rc;
+ int oprc;
+ char **argv;
+ int argc;
+ int delay;
+ int logChanges=0;
+ int isrepop = 0;
+
+ if ( slapi_pblock_get( pb, SLAPI_IS_REPLICATED_OPERATION, &isrepop ) != 0 ||
+ slapi_pblock_get( pb, SLAPI_DELETE_TARGET, &dn ) != 0 ||
+ slapi_pblock_get(pb, SLAPI_PLUGIN_OPRETURN, &oprc) != 0)
+ {
+ slapi_log_error( SLAPI_LOG_FATAL, REFERINT_PLUGIN_SUBSYSTEM,
+ "referint_postop_del: could not get parameters\n" );
+ return( -1 );
+ }
+
+ /* this plugin should only execute if the delete was successful
+ and this is not a replicated op
+ */
+ if(oprc != 0 || isrepop)
+ {
+ return( 0 );
+ }
+ /* get args */
+ if ( slapi_pblock_get( pb, SLAPI_PLUGIN_ARGC, &argc ) != 0) {
+ slapi_log_error( SLAPI_LOG_FATAL, REFERINT_PLUGIN_SUBSYSTEM,
+ "referint_postop failed to get argc\n" );
+ return( -1 );
+ }
+ if ( slapi_pblock_get( pb, SLAPI_PLUGIN_ARGV, &argv ) != 0) {
+ slapi_log_error( SLAPI_LOG_FATAL, REFERINT_PLUGIN_SUBSYSTEM,
+ "referint_postop failed to get argv\n" );
+ return( -1 );
+ }
+
+ if(argv == NULL){
+ slapi_log_error( SLAPI_LOG_FATAL, REFERINT_PLUGIN_SUBSYSTEM,
+ "referint_postop_modrdn, args are NULL\n" );
+ return( -1 );
+ }
+
+ if (argc >= 3) {
+ /* argv[0] will be the delay */
+ delay = atoi(argv[0]);
+
+ /* argv[2] will be wether or not to log changes */
+ logChanges = atoi(argv[2]);
+
+ if(delay == -1){
+ /* integrity updating is off */
+ rc = 0;
+ }else if(delay == 0){
+ /* no delay */
+ /* call function to update references to entry */
+ rc = update_integrity(argv, dn, NULL, logChanges);
+ }else{
+ /* write the entry to integrity log */
+ writeintegritylog(argv[1],dn, NULL);
+ rc = 0;
+ }
+ } else {
+ slapi_log_error( SLAPI_LOG_FATAL, REFERINT_PLUGIN_SUBSYSTEM,
+ "referint_postop insufficient arguments supplied\n" );
+ return( -1 );
+ }
+
+ return( rc );
+
+}
+
+int
+referint_postop_modrdn( Slapi_PBlock *pb )
+{
+ char *dn;
+ char *newrdn;
+ int oprc;
+ int rc;
+ char **argv;
+ int argc = 0;
+ int delay;
+ int logChanges=0;
+ int isrepop = 0;
+
+ if ( slapi_pblock_get( pb, SLAPI_IS_REPLICATED_OPERATION, &isrepop ) != 0 ||
+ slapi_pblock_get( pb, SLAPI_MODRDN_TARGET, &dn ) != 0 ||
+ slapi_pblock_get( pb, SLAPI_MODRDN_NEWRDN, &newrdn ) != 0 ||
+ slapi_pblock_get(pb, SLAPI_PLUGIN_OPRETURN, &oprc) != 0 ){
+
+ slapi_log_error( SLAPI_LOG_FATAL, REFERINT_PLUGIN_SUBSYSTEM,
+ "referint_postop_modrdn: could not get parameters\n" );
+ return( -1 );
+ }
+
+ /* this plugin should only execute if the delete was successful
+ and this is not a replicated op
+ */
+ if(oprc != 0 || isrepop){
+ return( 0 );
+ }
+ /* get args */
+ if ( slapi_pblock_get( pb, SLAPI_PLUGIN_ARGC, &argc ) != 0) {
+ slapi_log_error( SLAPI_LOG_FATAL, REFERINT_PLUGIN_SUBSYSTEM,
+ "referint_postop failed to get argv\n" );
+ return( -1 );
+ }
+ if ( slapi_pblock_get( pb, SLAPI_PLUGIN_ARGV, &argv ) != 0) {
+ slapi_log_error( SLAPI_LOG_FATAL, REFERINT_PLUGIN_SUBSYSTEM,
+ "referint_postop failed to get argv\n" );
+ return( -1 );
+ }
+
+ if(argv == NULL){
+ slapi_log_error( SLAPI_LOG_FATAL, REFERINT_PLUGIN_SUBSYSTEM,
+ "referint_postop_modrdn, args are NULL\n" );
+ return( -1 );
+ }
+
+ if (argc >= 3) {
+ /* argv[0] will always be the delay */
+ delay = atoi(argv[0]);
+
+ /* argv[2] will be wether or not to log changes */
+ logChanges = atoi(argv[2]);
+ } else {
+ slapi_log_error( SLAPI_LOG_FATAL, REFERINT_PLUGIN_SUBSYSTEM,
+ "referint_postop_modrdn insufficient arguments supplied\n" );
+ return( -1 );
+ }
+
+ if(delay == -1){
+ /* integrity updating is off */
+ rc = 0;
+ }else if(delay == 0){
+ /* no delay */
+ /* call function to update references to entry */
+ rc = update_integrity(argv, dn, newrdn, logChanges);
+ }else{
+ /* write the entry to integrity log */
+ writeintegritylog(argv[1],dn, newrdn);
+ rc = 0;
+ }
+
+ return( rc );
+}
+
+int isFatalSearchError(int search_result)
+{
+
+ /* Make sure search result is fatal
+ * Some conditions that happen quite often are not fatal
+ * for example if you have two suffixes and one is null, you will always
+ * get no such object, howerever this is not a fatal error.
+ * Add other conditions to the if statement as they are found
+ */
+
+ /* NPCTE fix for bugid 531225, esc 0. <P.R> <30-May-2001> */
+ switch(search_result) {
+ case LDAP_REFERRAL:
+ case LDAP_NO_SUCH_OBJECT: return 0 ;
+ }
+ return 1;
+ /* end of NPCTE fix for bugid 531225 */
+
+}
+
+int update_integrity(char **argv, char *origDN, char *newrDN, int logChanges){
+
+ Slapi_PBlock *search_result_pb = NULL;
+ Slapi_PBlock *mod_result_pb = NULL;
+ Slapi_Entry **search_entries = NULL;
+ int search_result;
+ Slapi_DN *sdn = NULL;
+ void *node = NULL;
+ LDAPMod attribute1, attribute2;
+ const LDAPMod *list_of_mods[3];
+ char *values_del[2];
+ char *values_add[2];
+ char *filter = NULL;
+ int i, j;
+ const char *search_base = NULL;
+ char *newDN=NULL;
+ char **dnParts=NULL;
+ int dnsize;
+ int x;
+ int rc;
+ int valcount = 0;
+
+ if ( argv == NULL ) {
+ slapi_log_error( SLAPI_LOG_FATAL, REFERINT_PLUGIN_SUBSYSTEM,
+ "referint_postop required config file arguments missing\n" );
+ rc = -1;
+ goto free_and_return;
+ }
+
+ /* for now, just putting attributes to keep integrity on in conf file,
+ until resolve the other timing mode issue */
+
+ /* Search each namingContext in turn */
+ for ( sdn = slapi_get_first_suffix( &node, 0 ); sdn != NULL;
+ sdn = slapi_get_next_suffix( &node, 0 ))
+ {
+ search_base = slapi_sdn_get_dn( sdn );
+
+
+ for(i=3; argv[i] != NULL; i++)
+ {
+ unsigned long filtlen = strlen(argv[i]) + (strlen(origDN) * 3 ) + 4;
+ filter = (char *)slapi_ch_calloc( filtlen, sizeof(char ));
+ if (( search_result = ldap_create_filter( filter, filtlen, "(%a=%e)",
+ NULL, NULL, argv[i], origDN, NULL )) == LDAP_SUCCESS ) {
+
+ /* Don't need any attribute */
+ char * attrs[2];
+ attrs[0]="1.1";
+ attrs[1]=NULL;
+
+ /* Use new search API */
+ search_result_pb = slapi_pblock_new();
+ slapi_search_internal_set_pb(search_result_pb, search_base, LDAP_SCOPE_SUBTREE,
+ filter, attrs, 0 /* attrs only */, NULL,NULL,referint_plugin_identity,0);
+ slapi_search_internal_pb(search_result_pb);
+
+ slapi_pblock_get( search_result_pb, SLAPI_PLUGIN_INTOP_RESULT, &search_result);
+ }
+
+
+ /* if search successfull then do integrity update */
+ if(search_result == 0)
+ {
+ slapi_pblock_get( search_result_pb,SLAPI_PLUGIN_INTOP_SEARCH_ENTRIES,
+ &search_entries);
+
+ for(j=0; search_entries[j] != NULL; j++)
+ {
+ /* no matter what mode in always going to delete old dn so set that up */
+ values_del[0]= origDN;
+ values_del[1]= NULL;
+ attribute1.mod_type = argv[i];
+ attribute1.mod_op = LDAP_MOD_DELETE;
+ attribute1.mod_values = values_del;
+ list_of_mods[0] = &attribute1;
+
+ if(newrDN == NULL){
+ /* in delete mode so terminate list of mods cause this is the only one */
+ list_of_mods[1] = NULL;
+ }else if(newrDN != NULL){
+ /* in modrdn mode */
+
+ /* need to put together rdn into a dn */
+ dnParts = ldap_explode_dn( origDN, 0 );
+
+ /* skip original rdn so start at 1*/
+ dnsize = 0;
+ for(x=1; dnParts[x] != NULL; x++)
+ {
+ /* +2 for space and comma adding later */
+ dnsize += strlen(dnParts[x]) + 2;
+ }
+ /* add the newrDN length */
+ dnsize += strlen(newrDN) + 1;
+
+ newDN = slapi_ch_calloc(dnsize, sizeof(char));
+ strcat(newDN, newrDN);
+ for(x=1; dnParts[x] != NULL; x++)
+ {
+ strcat(newDN, ", ");
+ strcat(newDN, dnParts[x]);
+ }
+
+ values_add[0]=newDN;
+ values_add[1]=NULL;
+ attribute2.mod_type = argv[i];
+ attribute2.mod_op = LDAP_MOD_ADD;
+ attribute2.mod_values = values_add;
+
+ /* add the new dn to list of mods and terminate list of mods */
+ list_of_mods[1] = &attribute2;
+ list_of_mods[2] = NULL;
+
+ }
+
+ /* try to cleanup entry */
+
+ /* Use new internal operation API */
+ mod_result_pb=slapi_pblock_new();
+ slapi_modify_internal_set_pb(mod_result_pb,slapi_entry_get_dn(search_entries[j]),
+ (LDAPMod **)list_of_mods,NULL,NULL,referint_plugin_identity,0);
+ slapi_modify_internal_pb(mod_result_pb);
+
+ /* could check the result code here if want to log it or something later
+ for now, continue no matter what result is */
+
+ slapi_pblock_destroy(mod_result_pb);
+
+ /* cleanup memory allocated for dnParts and newDN */
+ if(dnParts != NULL){
+ for(x=0; dnParts[x] != NULL; x++)
+ {
+ free(dnParts[x]);
+ }
+ free(dnParts);
+ }
+ if(newDN != NULL){
+ slapi_ch_free((void**)&newDN);
+ }
+
+ }
+
+
+
+ }else{
+ if(isFatalSearchError(search_result))
+ {
+ /* NPCTE fix for bugid 531225, esc 0. <P.R> <30-May-2001> */
+ slapi_log_error( SLAPI_LOG_FATAL, REFERINT_PLUGIN_SUBSYSTEM,
+ "referint_postop search (base=%s filter=%s) returned error %d\n", search_base,filter,search_result );
+ /* end of NPCTE fix for bugid 531225 */
+ rc = -1;
+ goto free_and_return;
+ }
+
+ }
+
+ slapi_ch_free((void**)&filter);
+
+ if(search_result_pb != NULL){
+ slapi_free_search_results_internal(search_result_pb);
+ slapi_pblock_destroy(search_result_pb);
+ search_result_pb= NULL;
+ }
+
+ }
+ }
+ /* if got here, then everything good rc = 0 */
+ rc = 0;
+
+free_and_return:
+
+ /* free filter and search_results_pb */
+ if(filter != NULL)
+ {
+ free(filter);
+ }
+
+ if(search_result_pb != NULL)
+ {
+ slapi_free_search_results_internal(search_result_pb);
+ free(search_result_pb);
+ }
+
+ return(rc);
+}
+
+int referint_postop_start( Slapi_PBlock *pb)
+{
+
+ char **argv;
+ int argc = 0;
+
+ /* get args */
+ if ( slapi_pblock_get( pb, SLAPI_PLUGIN_ARGC, &argc ) != 0 ) {
+ slapi_log_error( SLAPI_LOG_FATAL, REFERINT_PLUGIN_SUBSYSTEM,
+ "referint_postop failed to get argv\n" );
+ return( -1 );
+ }
+ if ( slapi_pblock_get( pb, SLAPI_PLUGIN_ARGV, &argv ) != 0 ) {
+ slapi_log_error( SLAPI_LOG_FATAL, REFERINT_PLUGIN_SUBSYSTEM,
+ "referint_postop failed to get argv\n" );
+ return( -1 );
+ }
+
+ if(argv == NULL){
+ slapi_log_error( SLAPI_LOG_FATAL, REFERINT_PLUGIN_SUBSYSTEM,
+ "args were null in referint_postop_start\n" );
+ return( -1 );
+ }
+
+ /* only bother to start the thread if you are in delay mode.
+ 0 = no delay,
+ -1 = integrity off */
+
+ if (argc >= 1) {
+ if(atoi(argv[0]) > 0){
+
+ /* initialize cv and lock */
+
+ referint_mutex = PR_NewLock();
+ keeprunning_mutex = PR_NewLock();
+ keeprunning_cv = PR_NewCondVar(keeprunning_mutex);
+ keeprunning =1;
+
+ if (( referint_tid = PR_CreateThread (PR_USER_THREAD,
+ referint_thread_func,
+ (void *)argv,
+ PR_PRIORITY_NORMAL,
+ PR_GLOBAL_THREAD,
+ PR_UNJOINABLE_THREAD,
+ SLAPD_DEFAULT_THREAD_STACKSIZE)) == NULL ) {
+ slapi_log_error( SLAPI_LOG_FATAL, REFERINT_PLUGIN_SUBSYSTEM,
+ "referint_postop_start PR_CreateThread failed\n" );
+ exit( 1 );
+ }
+ }
+ } else {
+ slapi_log_error( SLAPI_LOG_FATAL, REFERINT_PLUGIN_SUBSYSTEM,
+ "referint_postop_start insufficient arguments supplied\n" );
+ return( -1 );
+ }
+
+ return(0);
+
+}
+
+int referint_postop_close( Slapi_PBlock *pb)
+{
+
+ /* signal the thread to exit */
+ if (NULL != keeprunning_mutex) {
+ PR_Lock(keeprunning_mutex);
+ keeprunning=0;
+ if (NULL != keeprunning_cv) {
+ PR_NotifyCondVar(keeprunning_cv);
+ }
+ PR_Unlock(keeprunning_mutex);
+ }
+
+ return(0);
+}
+
+void referint_thread_func(void *arg){
+
+ char **plugin_argv = (char **)arg;
+ PRFileDesc *prfd;
+ char *logfilename;
+ char thisline[MAX_LINE];
+ int delay;
+ int no_changes;
+ char delimiter[]="\t\n";
+ char *ptoken;
+ char *tmpdn, *tmprdn;
+ int logChanges=0;
+ char * iter = NULL;
+
+ if(plugin_argv == NULL){
+ slapi_log_error( SLAPI_LOG_FATAL, REFERINT_PLUGIN_SUBSYSTEM,
+ "referint_thread_func not get args \n" );
+ return;
+ }
+
+
+ delay = atoi(plugin_argv[0]);
+ logfilename = plugin_argv[1];
+ logChanges = atoi(plugin_argv[2]);
+
+ /* keep running this thread until plugin is signalled to close */
+
+ while(1){
+
+ no_changes=1;
+
+ while(no_changes){
+
+ PR_Lock(keeprunning_mutex);
+ if(keeprunning == 0){
+ PR_Unlock(keeprunning_mutex);
+ break;
+ }
+ PR_Unlock(keeprunning_mutex);
+
+
+ PR_Lock(referint_mutex);
+ if (( prfd = PR_Open( logfilename, PR_RDONLY,
+ REFERINT_DEFAULT_FILE_MODE )) == NULL )
+ {
+ PR_Unlock(referint_mutex);
+ /* go back to sleep and wait for this file */
+ PR_Lock(keeprunning_mutex);
+ PR_WaitCondVar(keeprunning_cv, PR_SecondsToInterval(delay));
+ PR_Unlock(keeprunning_mutex);
+ }else{
+ no_changes = 0;
+ }
+ }
+
+ /* check keep running here, because after break out of no
+ * changes loop on shutdown, also need to break out of this
+ * loop before trying to do the changes. The server
+ * will pick them up on next startup as file still exists
+ */
+ PR_Lock(keeprunning_mutex);
+ if(keeprunning == 0){
+ PR_Unlock(keeprunning_mutex);
+ break;
+ }
+ PR_Unlock(keeprunning_mutex);
+
+
+ while( GetNextLine(thisline, MAX_LINE, prfd) ){
+ ptoken = ldap_utf8strtok_r(thisline, delimiter, &iter);
+ tmpdn = slapi_ch_calloc(strlen(ptoken) + 1, sizeof(char));
+ strcpy(tmpdn, ptoken);
+
+ ptoken = ldap_utf8strtok_r (NULL, delimiter, &iter);
+ if(!strcasecmp(ptoken, "NULL")){
+ tmprdn = NULL;
+ }else{
+ tmprdn = slapi_ch_calloc(strlen(ptoken) + 1, sizeof(char));
+ strcpy(tmprdn, ptoken);
+ }
+
+
+ update_integrity(plugin_argv, tmpdn, tmprdn, logChanges);
+
+ slapi_ch_free((void **) &tmpdn);
+ slapi_ch_free((void **) &tmprdn);
+ }
+
+ PR_Close(prfd);
+
+ /* remove the original file */
+ if( PR_SUCCESS != PR_Delete(logfilename) )
+ {
+ slapi_log_error( SLAPI_LOG_FATAL, REFERINT_PLUGIN_SUBSYSTEM,
+ "referint_postop_close could not delete \"%s\"\n",
+ logfilename );
+ }
+
+ /* unlock and let other writers back at the file */
+ PR_Unlock(referint_mutex);
+
+ /* wait on condition here */
+ PR_Lock(keeprunning_mutex);
+ PR_WaitCondVar(keeprunning_cv, PR_SecondsToInterval(delay));
+ PR_Unlock(keeprunning_mutex);
+ }
+
+ /* cleanup resources allocated in start */
+ if (NULL != keeprunning_mutex) {
+ PR_DestroyLock(keeprunning_mutex);
+ }
+ if (NULL != referint_mutex) {
+ PR_DestroyLock(referint_mutex);
+ }
+ if (NULL != keeprunning_cv) {
+ PR_DestroyCondVar(keeprunning_cv);
+ }
+
+
+}
+
+int my_fgetc(PRFileDesc *stream)
+{
+ static char buf[READ_BUFSIZE] = "\0";
+ static int position = READ_BUFSIZE;
+ int retval;
+ int err;
+
+ /* check if we need to load the buffer */
+
+ if( READ_BUFSIZE == position )
+ {
+ memset(buf, '\0', READ_BUFSIZE);
+ if( ( err = PR_Read(stream, buf, READ_BUFSIZE) ) >= 0)
+ {
+ /* it read some data */;
+ position = 0;
+ }else{
+ /* an error occurred */
+ return err;
+ }
+ }
+
+ /* try to read some data */
+ if( '\0' == buf[position])
+ {
+ /* out of data, return eof */
+ retval = MY_EOF;
+ position = READ_BUFSIZE;
+ }else{
+ retval = buf[position];
+ position++;
+ }
+
+ return retval;
+
+}
+
+int
+GetNextLine(char *dest, int size_dest, PRFileDesc *stream) {
+
+ char nextchar ='\0';
+ int done = 0;
+ int i = 0;
+
+ while(!done)
+ {
+ if( ( nextchar = my_fgetc(stream) ) != 0)
+ {
+ if( i < (size_dest - 1) )
+ {
+ dest[i] = nextchar;
+ i++;
+
+ if(nextchar == '\n')
+ {
+ /* end of line reached */
+ done = 1;
+ }
+
+ }else{
+ /* no more room in buffer */
+ done = 1;
+ }
+
+ }else{
+ /* error or end of file */
+ done = 1;
+ }
+ }
+
+ dest[i] = '\0';
+
+ /* return size of string read */
+ return i;
+}
+
+void writeintegritylog(char *logfilename, char *dn, char *newrdn){
+ PRFileDesc *prfd;
+ char buffer[MAX_LINE];
+ int len_to_write = 0;
+ int rc;
+ /* write this record to the file */
+
+ /* use this lock to protect file data when update integrity is occuring */
+ /* should hopefully not be a big issue on concurrency */
+
+ PR_Lock(referint_mutex);
+ if (( prfd = PR_Open( logfilename, PR_WRONLY | PR_CREATE_FILE | PR_APPEND,
+ REFERINT_DEFAULT_FILE_MODE )) == NULL )
+ {
+ slapi_log_error( SLAPI_LOG_FATAL, REFERINT_PLUGIN_SUBSYSTEM,
+ "referint_postop could not write integrity log \"%s\" "
+ SLAPI_COMPONENT_NAME_NSPR " %d (%s)\n",
+ logfilename, PR_GetError(), slapd_pr_strerror(PR_GetError()) );
+
+ PR_Unlock(referint_mutex);
+ return;
+ }
+
+ /* make sure we have enough room in our buffer
+ before trying to write it
+ */
+
+ /* add length of dn + 4(two tabs, a newline, and terminating \0) */
+ len_to_write = strlen(dn) + 4;
+
+ if(newrdn == NULL)
+ {
+ /* add the length of "NULL" */
+ len_to_write += 4;
+ }else{
+ /* add the length of the newrdn */
+ len_to_write += strlen(newrdn);
+ }
+
+ if(len_to_write > MAX_LINE )
+ {
+ slapi_log_error( SLAPI_LOG_FATAL, REFERINT_PLUGIN_SUBSYSTEM,
+ "referint_postop could not write integrity log:"
+ " line length exceeded. It will not be able"
+ " to update references to this entry.\n");
+ }else{
+ PRInt32 rv;
+ sprintf(buffer, "%s\t%s\t\n",
+ dn,
+ (newrdn != NULL) ? newrdn : "NULL");
+ if ((rv = PR_Write(prfd,buffer,strlen(buffer))) < 0){
+ slapi_log_error(SLAPI_LOG_FATAL,REFERINT_PLUGIN_SUBSYSTEM,
+ " writeintegritylog: PR_Write failed : The disk"
+ " may be full or the file is unwritable :: NSPR error - %d\n",
+ PR_GetError());
+ }
+ }
+
+ /* If file descriptor is closed successfully, PR_SUCCESS */
+
+ rc = PR_Close(prfd);
+ if (rc != PR_SUCCESS)
+ {
+ slapi_log_error(SLAPI_LOG_FATAL,REFERINT_PLUGIN_SUBSYSTEM,
+ " writeintegritylog: failed to close the file"
+ " descriptor prfd; NSPR error - %d\n",
+ PR_GetError());
+ }
+ PR_Unlock(referint_mutex);
+ }
diff --git a/ldap/servers/plugins/referint/referint.def b/ldap/servers/plugins/referint/referint.def
new file mode 100644
index 00000000..75861791
--- /dev/null
+++ b/ldap/servers/plugins/referint/referint.def
@@ -0,0 +1,12 @@
+; BEGIN COPYRIGHT BLOCK
+; Copyright 2001 Sun Microsystems, Inc.
+; Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+; All rights reserved.
+; END COPYRIGHT BLOCK
+;
+DESCRIPTION 'Netscape Directory Server 7 Referint Plugin'
+CODE SHARED READ EXECUTE
+DATA SHARED READ WRITE
+EXPORTS
+ referint_postop_init @2
+ plugin_init_debug_level @3
diff --git a/ldap/servers/plugins/replication/Makefile b/ldap/servers/plugins/replication/Makefile
new file mode 100644
index 00000000..6f5341e8
--- /dev/null
+++ b/ldap/servers/plugins/replication/Makefile
@@ -0,0 +1,152 @@
+#
+# BEGIN COPYRIGHT BLOCK
+# Copyright 2001 Sun Microsystems, Inc.
+# Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+# All rights reserved.
+# END COPYRIGHT BLOCK
+#
+#
+# GNU Makefile for Directory Server "Replication" plugin
+#
+#
+
+LDAP_SRC = ../../..
+MCOM_ROOT = ../../../../..
+
+NOSTDCLEAN=true # don't let nsconfig.mk define target clean
+NOSTDSTRIP=true # don't let nsconfig.mk define target strip
+NSPR20=true # probably should be defined somewhere else (not sure where)
+
+OBJDEST = $(OBJDIR)/lib/replication-plugin
+LIBDIR = $(LIB_RELDIR)
+
+include $(MCOM_ROOT)/ldapserver/nsconfig.mk
+include $(LDAP_SRC)/nsldap.mk
+include $(MCOM_ROOT)/ldapserver/ns_usedb.mk
+
+ifeq ($(ARCH), WINNT)
+DEF_FILE:=./replication.def
+endif
+
+CFLAGS += $(SLCFLAGS) -DSLAPD_LOGGING
+
+ifeq ($(ARCH), WINNT)
+CFLAGS += /WX
+endif
+
+ifdef TEST_CL5
+CFLAGS += -DTEST_CL5
+endif
+
+INCLUDES += -I$(LDAP_SRC)/servers/slapd -I$(DB_INCLUDE)
+
+LOCAL_OBJS= \
+ cl5_api.o \
+ cl5_clcache.o \
+ cl5_config.o \
+ cl5_init.o \
+ csnpl.o\
+ legacy_consumer.o \
+ llist.o\
+ repl5_agmt.o \
+ repl5_agmtlist.o \
+ repl5_backoff.o \
+ repl5_connection.o \
+ repl5_inc_protocol.o \
+ repl5_init.o\
+ repl5_protocol.o \
+ repl5_protocol_util.o \
+ repl5_replica.o\
+ repl5_replica_config.o\
+ repl5_ruv.o\
+ repl5_schedule.o \
+ repl5_tot_protocol.o \
+ repl5_total.o\
+ repl5_mtnode_ext.o\
+ repl5_plugins.o \
+ repl_add.o \
+ repl_bind.o \
+ repl_compare.o \
+ repl_connext.o \
+ repl_controls.o \
+ repl_delete.o \
+ repl_entry.o \
+ repl_ext.o \
+ repl_extop.o \
+ repl_globals.o \
+ repl_init.o \
+ repl_modify.o \
+ repl_modrdn.o \
+ repl_monitor.o \
+ repl_objset.o \
+ repl_opext.o \
+ repl_ops.o \
+ repl_rootdse.o \
+ repl_search.o \
+ replutil.o \
+ urp.o \
+ urp_glue.o \
+ urp_tombstone.o \
+ repl5_replica_hash.o\
+ repl5_replica_dnhash.o\
+ repl5_updatedn_list.o\
+
+LIBREPLICATION_OBJS = $(addprefix $(OBJDEST)/, $(LOCAL_OBJS))
+
+ifeq ($(ARCH), WINNT)
+REPLICATION_DLL_OBJ = $(addprefix $(OBJDEST)/, dllmain.o)
+endif
+
+LIBREPLICATION= $(addprefix $(LIBDIR)/, $(REPLICATION_DLL).$(DLL_SUFFIX))
+
+ifeq ($(ARCH), WINNT)
+EXTRA_LIBS_DEP += $(LIBSLAPD_DEP)
+EXTRA_LIBS_DEP += $(LDAPSDK_DEP) $(DB_LIB_DEP) $(NSPR_DEP)
+EXTRA_LIBS += $(LIBSLAPD) $(LDAPLINK) $(DB_LIB)
+endif
+
+ifeq ($(ARCH), AIX)
+EXTRA_LIBS_DEP += $(LIBSLAPD_DEP)
+EXTRA_LIBS_DEP += $(LDAPSDK_DEP) $(DB_LIB_DEP) $(NSPR_DEP)
+EXTRA_LIBS += $(LIBSLAPD) $(LDAP_SDK_LIBLDAP_DLL) $(DB_LIB)
+endif
+
+ifeq ($(ARCH), HPUX)
+EXTRA_LIBS_DEP += $(LIBSLAPD_DEP) $(LDAPSDK_DEP) $(NSPR_DEP) $(SECURITY_DEP)
+EXTRA_LIBS += $(DYN_NSHTTPD) $(ADMINUTIL_LINK) $(LDAPLINK) $(SECURITYLINK) $(NSPRLINK) $(ICULINK)
+endif
+
+ifeq ($(ARCH), WINNT)
+DLL_LDFLAGS += -def:"./replication.def"
+endif # WINNT
+
+ifeq ($(ARCH), AIX)
+EXTRA_LIBS += $(DLL_EXTRA_LIBS)
+LD=ld
+endif
+
+clientSDK:
+
+all: $(OBJDEST) $(LIBDIR) $(LIBREPLICATION)
+
+$(LIBREPLICATION): $(LIBREPLICATION_OBJS) $(REPLICATION_DLL_OBJ) $(DEF_FILE)
+ $(LINK_DLL) $(LIBREPLICATION_OBJS) $(REPLICATION_DLL_OBJ) $(PLATFORMLIBS) $(EXTRA_LIBS) $(LDAP_LIBLDIF) $(NSPRLINK)
+
+tests: $(TEST_PROGS)
+
+veryclean: clean
+
+clean:
+ $(RM) $(LIBREPLICATION_OBJS)
+ifeq ($(ARCH), WINNT)
+ $(RM) $(REPLICATION_DLL_OBJ)
+endif
+ $(RM) $(LIBREPLICATION)
+
+$(OBJDEST):
+ $(MKDIR) $(OBJDEST)
+
+#
+# header file dependencies (incomplete)
+#
+$(LIBREPLICATION_OBJS):
diff --git a/ldap/servers/plugins/replication/cl4.h b/ldap/servers/plugins/replication/cl4.h
new file mode 100644
index 00000000..0dbcece2
--- /dev/null
+++ b/ldap/servers/plugins/replication/cl4.h
@@ -0,0 +1,65 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+/* cl4.h - global declarations used by the 4.0 style changelog module
+ */
+
+#ifndef CL4_H
+#define CL4_H
+
+#include "slapi-private.h"
+#include "portable.h" /* GGOODREPL - is this cheating? */
+
+#define CONFIG_CHANGELOG_SUFFIX_ATTRIBUTE "nsslapd-changelogsuffix"
+
+/* A place to store changelog config info */
+typedef struct _chglog4Info chglog4Info;
+
+/* in cl4.c */
+chglog4Info* changelog4_new (Slapi_Entry *e, char *errorbuf);
+void changelog4_free (chglog4Info** cl4);
+void changelog4_lock (Object *obj, PRBool write);
+void changelog4_unlock (Object *obj);
+const char * changelog4_get_dir (const chglog4Info* cl4);
+const char * changelog4_get_suffix (const chglog4Info* cl4);
+time_t changelog4_get_maxage (const chglog4Info* cl4);
+unsigned long changelog4_get_maxentries (const chglog4Info* cl4);
+void changelog4_set_dir (chglog4Info* cl4, const char *dir);
+void changelog4_set_suffix (chglog4Info* cl4, const char *suffix);
+void changelog4_set_maxage (chglog4Info* cl4, const char *maxage);
+void changelog4_set_maxentries (chglog4Info* cl4, const char* maxentries);
+
+/* In cl4_suffix.c */
+char *get_changelog_dataversion(const chglog4Info* cl4);
+void set_changelog_dataversion(chglog4Info* cl4, const char *dataversion);
+
+/* In cl4_config.c */
+int changelog4_config_init();
+void changelog4_config_destroy();
+
+/*
+ * backend configuration information
+ * Previously, these two typedefs were in ../../slapd/slapi-plugin.h but
+ * the CL4 code is the only remaining code that references these definitions.
+ */
+typedef struct config_directive
+{
+ char *file_name; /* file from which to read directive */
+ int lineno; /* line to read */
+ int argc; /* number of argvs */
+ char **argv; /* directive in agrv format */
+} slapi_config_directive;
+
+typedef struct be_config
+{
+ char *type; /* type of the backend */
+ char *suffix; /* suffix of the backend */
+ int is_private; /* 1 - private, 0 -not */
+ int log_change; /* 1 - write change to the changelog; 0 - don't */
+ slapi_config_directive *directives;/* configuration directives */
+ int dir_count; /* number of directives */
+} slapi_be_config;
+
+#endif
diff --git a/ldap/servers/plugins/replication/cl4_api.c b/ldap/servers/plugins/replication/cl4_api.c
new file mode 100644
index 00000000..135b4a5b
--- /dev/null
+++ b/ldap/servers/plugins/replication/cl4_api.c
@@ -0,0 +1,797 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+/* cl4_api.h - implementation of the minimal interface to 4.0 changelog necessary to
+ link 4.0 changelog to 5.0 replication
+ */
+
+#include "repl.h"
+#include "cl4_api.h"
+#include "csnpl.h"
+#include "cl4.h"
+
+/*** Data Structures ***/
+
+/* changelog internal data */
+typedef struct cl4priv
+{
+ CSNPL *csnPL; /* csn pending list */
+ int regID; /* csn function registration id */
+}CL4Private;
+
+/* callback data to get result of internal operations */
+typedef struct cl4ret
+{
+ int err; /* error code */
+ Slapi_Entry *e; /* target entry */
+}CL4Ret;
+
+/* Global Data */
+static CL4Private s_cl4Desc; /* represents changelog state */
+
+/*** Helper functions forward declarations ***/
+static int _cl4WriteOperation (const slapi_operation_parameters *op);
+static void _cl4AssignCSNCallback (const CSN *csn, void *data);
+static void _cl4AbortCSNCallback (const CSN *csn, void *data);
+static char* _cl4MakeCSNDN (const CSN* csn);
+static int _cl4GetEntry (const CSN *csn, Slapi_Entry **entry);
+static void _cl4ResultCallback (int err, void *callback_data);
+static int _cl4EntryCallback (Slapi_Entry *e, void *callback_data);
+static PRBool _cl4CanAssignChangeNumber (const CSN *csn);
+static int _cl4ResolveTargetDN (Slapi_Entry *entry, Slapi_DN **newTargetDN);
+static int _cl4GetTargetEntry (Slapi_DN *targetDN, const char *uniqueid, Slapi_Entry **entry);
+static int _cl4FindTargetDN (const CSN *csn, const char *uniqueid,
+ const Slapi_DN *targetSDN, Slapi_DN **newTargetDN);
+static int _cl4AssignChangeNumber (changeNumber *cnum);
+static int _cl4UpdateEntry (const CSN *csn, const char *changeType, const Slapi_DN *newTargetDN, changeNumber cnum);
+
+/*** API ***/
+int cl4Init ()
+{
+ s_cl4Desc.csnPL = csnplNew ();
+ if (s_cl4Desc.csnPL == NULL)
+ {
+ slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name, "cl4Init: failed to create CSN pending list\n");
+ return CL4_CSNPL_ERROR;
+ }
+
+ s_cl4Desc.regID = csnRegisterNewCSNCb(_cl4AssignCSNCallback, NULL,
+ _cl4AbortCSNCallback, NULL);
+
+ return CL4_SUCCESS;
+}
+
+void cl4Cleanup ()
+{
+ if (s_cl4Desc.regID >= 0)
+ {
+ csnRemoveNewCSNCb(s_cl4Desc.regID);
+ s_cl4Desc.regID = -1;
+ }
+
+ if (s_cl4Desc.csnPL == NULL)
+ csnplFree (&s_cl4Desc.csnPL);
+}
+
+int cl4WriteOperation (const slapi_operation_parameters *op)
+{
+ int rc;
+ ReplicaId rd;
+
+ if (op == NULL || !IsValidOperation (op))
+ {
+ slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name, "cl4WriteEntry: invalid entry\n");
+ return CL4_BAD_DATA;
+ }
+
+ rc = _cl4WriteOperation (op);
+ if (rc != CL4_SUCCESS)
+ {
+ slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name, "cl4WriteEntry: failed to write changelog entry\n");
+ return rc;
+ }
+
+ /* the entry is generated by this server - remove the entry from the pending list */
+ rd= csn_get_replicaid(op->csn);
+ if (rd == slapi_get_replicaid ())
+ {
+ rc = csnplRemove (s_cl4Desc.csnPL, op->csn);
+
+ if (rc != 0)
+ {
+ slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name,
+ "cl4WriteEntry: failed to remove CSN from the pending list\n");
+ rc = CL4_CSNPL_ERROR;
+ }
+ }
+
+ return rc;
+}
+
+int cl4ChangeTargetDN (const CSN *csn, const char *newDN)
+{
+ Slapi_PBlock *pb;
+ char *changeEntryDN;
+ Slapi_Mods smods;
+ int res;
+
+ if (csn == NULL || newDN == NULL)
+ {
+ slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name, "cl4ChangeTargetDN: invalid argument\n");
+ return CL4_BAD_DATA;
+ }
+
+ /* construct dn of the change entry */
+ changeEntryDN = _cl4MakeCSNDN (csn);
+ if (changeEntryDN == NULL)
+ {
+ slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name,
+ "cl4ChangeTargetDN: failed to construct change entry dn\n");
+ return CL4_MEMORY_ERROR;
+ }
+
+ pb = slapi_pblock_new ();
+
+ slapi_mods_init(&smods, 1);
+ slapi_mods_add(&smods, LDAP_MOD_REPLACE | LDAP_MOD_BVALUES, attr_targetdn,
+ strlen (newDN), newDN);
+ slapi_modify_internal_set_pb(pb, changeEntryDN, slapi_mods_get_ldapmods_byref(&smods),
+ NULL, NULL, repl_get_plugin_identity(PLUGIN_LEGACY_REPLICATION), 0);
+ slapi_modify_internal_pb (pb);
+
+ slapi_mods_done(&smods);
+ slapi_ch_free ((void**)&changeEntryDN);
+
+ slapi_pblock_get(pb, SLAPI_PLUGIN_INTOP_RESULT, &res);
+ slapi_pblock_destroy(pb);
+
+ if (res != LDAP_SUCCESS)
+ {
+ char s[CSN_STRSIZE];
+ slapi_log_error( SLAPI_LOG_FATAL, repl_plugin_name,
+ "cl4ChangeTargetDN: an error occured while modifying change entry with csn %s: %s. "
+ "Logging of changes is disabled.\n", csn_as_string(csn,PR_FALSE,s), ldap_err2string(res));
+ /* GGOODREPL g_set_repl_backend( NULL ); */
+ return CL4_LDAP_ERROR;
+ }
+
+ return CL4_SUCCESS;
+}
+
+void cl4AssignChangeNumbers (time_t when, void *arg)
+{
+ int rc = CL4_SUCCESS;
+ Slapi_Entry *entry;
+ CSN *csn = NULL;
+ Slapi_DN *newTargetDN;
+ changeNumber cnum;
+ char *changetype;
+
+ /* we are looping though the entries ready to be commited updating there target dn
+ and assigning change numbers */
+ while (_cl4GetEntry (csn, &entry) == CL4_SUCCESS)
+ {
+ /* ONREPL - I think we need to free previous csn */
+ csn = csn_new_by_string(slapi_entry_attr_get_charptr (entry, attr_csn));
+ /* all conflicts involving this entry have been resolved */
+ if (_cl4CanAssignChangeNumber (csn))
+ {
+ /* figure out the name of the target entry that corresponds to change csn */
+ rc = _cl4ResolveTargetDN (entry, &newTargetDN);
+ slapi_entry_free (entry);
+ if (rc != CL4_SUCCESS)
+ {
+ slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name, "cl4AssignChangeNumbers: failed to resolve target dn\n");
+ break;
+ }
+
+ _cl4AssignChangeNumber (&cnum);
+
+ changetype = slapi_entry_attr_get_charptr (entry, attr_changetype);
+
+ /* update change entry: write change number and remove csn attribute.
+ Note that we leave uniqueid in the entry to avoid an extra update.
+ This is ok since uniqueid is an operational attribute not returned
+ to the client by default. */
+ rc = _cl4UpdateEntry (csn, changetype, newTargetDN, cnum);
+ if (newTargetDN)
+ {
+ slapi_sdn_free (&newTargetDN);
+ }
+
+ slapi_ch_free ((void**)&changetype);
+
+ if (rc != CL4_SUCCESS)
+ {
+ slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name,
+ "cl4AssignChangeNumbers: failed to update changelog entry\n");
+ break;
+ }
+ }
+ else /* went too far */
+ {
+ slapi_entry_free (entry);
+ break;
+ }
+ }
+}
+
+
+/*** Helper Functions ***/
+
+/* adds new change record to 4.0 changelog */
+static int _cl4WriteOperation (const slapi_operation_parameters *op)
+{
+ int rc = CL4_SUCCESS, res;
+ char *changeEntryDN, *timeStr;
+ Slapi_Entry *e;
+ Slapi_PBlock *pb = NULL;
+ Slapi_Value *values[3];
+ char s[CSN_STRSIZE];
+
+ slapi_log_error (SLAPI_LOG_PLUGIN, repl_plugin_name,
+ "_cl4WriteEntry: writing change record with csn %s for dn: \"%s\"\n",
+ csn_as_string(op->csn,PR_FALSE,s), op->target_address.dn);
+
+ /* create change entry dn */
+ changeEntryDN = _cl4MakeCSNDN (op->csn);
+ if (changeEntryDN == NULL)
+ {
+ slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name,
+ "_cl4WriteEntry: failed to create entry dn\n");
+ return CL4_MEMORY_ERROR;
+ }
+
+ /*
+ * Create the entry struct, and fill in fields common to all types
+ * of change records.
+ */
+ e = slapi_entry_alloc();
+ if (e == NULL)
+ {
+ slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name,
+ "_cl4WriteEntry: failed to allocate change entry\n");
+ return CL4_MEMORY_ERROR;
+ }
+
+ slapi_entry_set_dn(e, slapi_ch_strdup (changeEntryDN));
+
+ /* Set the objectclass attribute */
+ values [0] = slapi_value_new (NULL);
+ values [1] = slapi_value_new (NULL);
+ values [2] = NULL;
+ slapi_value_set_string(values[0], "top");
+ slapi_value_set_string(values[1], "changelogentry");
+ slapi_entry_add_values_sv (e, "objectclass", values);
+
+ /* ONREPL - for now we have to free Slapi_Values since api makes copy;
+ this will change when a new set of api is added */
+ slapi_value_free (&(values[0]));
+ slapi_value_free (&(values[1]));
+
+ /* Set the changeNumber attribute */
+ /* Need to set this because it is required by schema */
+ slapi_entry_attr_set_charptr (e, attr_changenumber, "0");
+
+ /* Set the targetentrydn attribute */
+ if (op->operation_type == SLAPI_OPERATION_ADD) /* use raw dn */
+ slapi_entry_attr_set_charptr (e, attr_targetdn, slapi_entry_get_dn (op->p.p_add.target_entry));
+ else /* use normolized dn */
+ slapi_entry_attr_set_charptr (e, attr_targetdn, op->target_address.dn);
+
+ /* ONREPL - set dbid attribute */
+
+ /* Set the changeTime attribute */
+ timeStr = format_localTime (current_time());
+ slapi_entry_attr_set_charptr (e, attr_changetime, timeStr);
+ slapi_ch_free((void**)&timeStr);
+
+ /*
+ * Finish constructing the entry. How to do it depends on the type
+ * of modification being logged.
+ */
+ switch (op->operation_type)
+ {
+ case SLAPI_OPERATION_ADD: if (entry2reple(e, op->p.p_add.target_entry) != 0 )
+ {
+ rc = CL4_INTERNAL_ERROR;
+ goto done;
+ }
+
+ break;
+
+ case SLAPI_OPERATION_MODIFY: if (mods2reple(e, op->p.p_modify.modify_mods) != 0)
+ {
+ rc = CL4_INTERNAL_ERROR;
+ goto done;
+ }
+
+ break;
+
+ case SLAPI_OPERATION_MODDN: if (modrdn2reple(e, op->p.p_modrdn.modrdn_newrdn,
+ op->p.p_modrdn.modrdn_deloldrdn, op->p.p_modrdn.modrdn_mods) != 0)
+ {
+ rc = CL4_INTERNAL_ERROR;
+ goto done;
+ }
+
+ break;
+
+ case SLAPI_OPERATION_DELETE: /* Set the changetype attribute */
+ slapi_entry_attr_set_charptr (e, attr_changetype, "delete");
+ break;
+ }
+
+ pb = slapi_pblock_new (pb);
+ slapi_add_entry_internal_set_pb (pb, e, NULL, repl_get_plugin_identity (PLUGIN_LEGACY_REPLICATION), 0);
+ slapi_add_internal_pb (pb);
+
+ slapi_pblock_get(pb, SLAPI_PLUGIN_INTOP_RESULT, &res);
+ slapi_pblock_destroy(pb);
+
+ if (res != LDAP_SUCCESS)
+ {
+ char s[CSN_STRSIZE];
+ slapi_log_error( SLAPI_LOG_FATAL, repl_plugin_name,
+ "_cl4WriteEntry: an error occured while adding change entry with csn %s, dn = %s: %s. "
+ "Logging of changes is disabled.\n", csn_as_string(op->csn,PR_FALSE,s), op->target_address.dn,
+ ldap_err2string(res));
+ /* GGOODREPL g_set_repl_backend( NULL ); */
+ rc = CL4_LDAP_ERROR;
+ }
+
+done:
+ if (changeEntryDN)
+ slapi_ch_free((void **) &changeEntryDN);
+
+ return rc;
+}
+
+static void _cl4AssignCSNCallback (const CSN *csn, void *data)
+{
+ int rc;
+
+ if (csn == NULL)
+ {
+ slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name, "_cl4AssignCSNCallback: null csn\n");
+ return;
+ }
+
+ rc = csnplInsert (s_cl4Desc.csnPL, csn);
+
+ if (rc == -1)
+ {
+ char s[CSN_STRSIZE];
+ slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name,
+ "_cl4AssignCSNCallback: failed to insert csn %s to the pending list\n",
+ csn_as_string(csn,PR_FALSE,s));
+ }
+}
+
+static void _cl4AbortCSNCallback (const CSN *csn, void *data)
+{
+ int rc;
+
+ if (csn == NULL)
+ {
+ slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name, "_cl4AbortCSNCallback: null csn\n");
+ return;
+ }
+
+ rc = csnplRemove (s_cl4Desc.csnPL, csn);
+ if (rc == -1)
+ {
+ char s[CSN_STRSIZE];
+ slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name,
+ "_cl4AbortCSNCallback: failed to remove csn %s from the pending list\n",
+ csn_as_string(csn,PR_FALSE,s));
+ }
+}
+
+/* initial dn format: csn=<csn>,<changelog suffix>. For instance, csn=013744022939465,cn=changelog4 */
+static char* _cl4MakeCSNDN (const CSN* csn)
+{
+ char *pat, *edn;
+ char *suffix = changelog4_get_suffix ();
+ char s[CSN_STRSIZE];
+
+ if (suffix == NULL)
+ return NULL;
+
+ /* Construct the dn of this change record */
+ pat = "%s=%s,%s";
+ edn = slapi_ch_malloc(strlen(pat) + strlen(attr_csn) + strlen(suffix) + CSN_STRSIZE + 1);
+ if (edn)
+ sprintf(edn, pat, attr_csn, csn_as_string(csn,PR_FALSE,s), suffix);
+ slapi_ch_free ((void **)&suffix);
+
+ return edn;
+}
+
+static int _cl4GetEntry (const CSN *csn, Slapi_Entry **entry)
+{
+ int rc;
+ char *suffix = changelog4_get_suffix ();
+ int type;
+ const char *value;
+ CL4Ret ret;
+ char s[CSN_STRSIZE];
+
+ if (csn == NULL) /* entry with smallest csn */
+ {
+ type = SLAPI_SEQ_FIRST;
+ value = NULL;
+ }
+ else /* entry with next csn */
+ {
+ type = SLAPI_SEQ_NEXT;
+ value = csn_as_string(csn,PR_FALSE,s);
+ }
+
+ rc = slapi_seq_callback(suffix, type, attr_csn, (char*)value, NULL, 0, &ret, NULL,
+ _cl4ResultCallback, _cl4EntryCallback, NULL);
+ slapi_ch_free ((void**)&suffix);
+
+ if (rc != 0 || ret.err != 0)
+ {
+ slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name, "_cl4GetEntry: failed to get next changelog entry\n");
+ return CL4_INTERNAL_ERROR;
+ }
+
+ *entry = ret.e;
+ return CL4_SUCCESS;
+}
+
+static void _cl4ResultCallback (int err, void *callback_data)
+{
+ CL4Ret *ret = (CL4Ret *)callback_data;
+
+ if (ret)
+ {
+ ret->err = err;
+ }
+}
+
+static int _cl4EntryCallback (Slapi_Entry *e, void *callback_data)
+{
+ CL4Ret *ret = (CL4Ret *)callback_data;
+
+ if (ret)
+ {
+ ret->e = slapi_entry_dup (e);
+ }
+
+ return 0;
+}
+
+static PRBool _cl4CanAssignChangeNumber (const CSN *csn)
+{
+ CSN *commitCSN = NULL;
+
+ /* th CSN is withtin region that can be commited */
+ if (csn && csn_compare(csn, commitCSN) < 0)
+ return PR_TRUE;
+
+ return PR_FALSE;
+}
+
+/* ONREPL - describe algorithm */
+static int _cl4ResolveTargetDN (Slapi_Entry *entry, Slapi_DN **newTargetDN)
+{
+ int rc;
+ char *csnStr = slapi_entry_attr_get_charptr (entry, attr_csn);
+ char *targetdn = slapi_entry_attr_get_charptr (entry, attr_targetdn);
+ const char *uniqueid = slapi_entry_get_uniqueid (entry);
+ char *changetype = slapi_entry_attr_get_charptr (entry, attr_changetype);
+ CSN *csn = csn_new_by_string (csnStr);
+ Slapi_Entry *targetEntry = NULL;
+ const Slapi_DN *teSDN;
+ Slapi_DN *targetSDN;
+ const CSN *teDNCSN = NULL;
+
+ *newTargetDN = NULL;
+
+ targetSDN = slapi_sdn_new();
+ if (strcasecmp (changetype, "add") == 0) /* this is add operation - we have rawdn */
+ slapi_sdn_set_dn_byref (targetSDN, targetdn);
+ else
+ slapi_sdn_set_ndn_byref (targetSDN, targetdn);
+
+ /* read the entry to which the change was applied */
+ rc = _cl4GetTargetEntry (targetSDN, uniqueid, &targetEntry);
+ if (rc != CL4_SUCCESS)
+ {
+ slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name, "_cl4ResolveTargetDN: failed to get target entry\n");
+ goto done;
+ }
+
+ teDNCSN = entry_get_dncsn(targetEntry);
+ if (teDNCSN == NULL)
+ {
+ slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name, "_cl4ResolveTargetDN: failed to get target entry dn\n");
+ rc = CL4_BAD_FORMAT;
+ goto done;
+ }
+
+ if (csn_compare(teDNCSN, csn) <= 0)
+ {
+ /* the change entry target dn should be the same as target entry dn */
+ teSDN = slapi_entry_get_sdn_const(targetEntry);
+
+ /* target dn of change entry is not the same as dn of the target entry - update */
+ if (slapi_sdn_compare (teSDN, targetSDN) != 0)
+ {
+ *newTargetDN = slapi_sdn_dup (targetSDN);
+ }
+ }
+ else /* the target entry was renamed since this change occur - find the right target dn */
+ {
+ rc = _cl4FindTargetDN (csn, uniqueid, targetSDN, newTargetDN);
+ }
+
+done:;
+ if (csnStr)
+ slapi_ch_free ((void**)&csnStr);
+
+ if (targetdn)
+ slapi_ch_free ((void**)&targetdn);
+
+ if (uniqueid)
+ slapi_ch_free ((void**)&uniqueid);
+
+ if (changetype)
+ slapi_ch_free ((void**)&changetype);
+
+ if (targetEntry)
+ slapi_entry_free (targetEntry);
+
+ if (targetSDN)
+ slapi_sdn_free (&targetSDN);
+
+ return rc;
+}
+
+static int _cl4GetTargetEntry (Slapi_DN *sdn, const char *uniqueid, Slapi_Entry **entry)
+{
+ Slapi_PBlock *pb;
+ char filter [128];
+ int res, rc = CL4_SUCCESS;
+ Slapi_Entry **entries = NULL;
+
+ /* read corresponding database entry based on its uniqueid */
+ sprintf (filter, "uniqueid=%s", uniqueid);
+ pb = slapi_pblock_new ();
+ slapi_search_internal_set_pb (pb, (char*)slapi_sdn_get_ndn(sdn), LDAP_SCOPE_SUBTREE, filter, NULL, 0, NULL, NULL,
+ repl_get_plugin_identity (PLUGIN_LEGACY_REPLICATION), 0);
+ slapi_search_internal_pb (pb);
+
+ if (pb == NULL)
+ {
+ rc = CL4_LDAP_ERROR;
+ goto done;
+ }
+
+ slapi_pblock_get(pb, SLAPI_PLUGIN_INTOP_RESULT, &res);
+ if (res == LDAP_NO_SUCH_OBJECT) /* entry not found */
+ {
+ slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name, "_cl4GetTargetEntry: entry (%s) not found\n",
+ slapi_sdn_get_ndn(sdn));
+ rc = CL4_NOT_FOUND;
+ goto done;
+ }
+
+ if (res != LDAP_SUCCESS)
+ {
+ slapi_log_error( SLAPI_LOG_FATAL, repl_plugin_name,
+ "_cl4ResolveTargetDN: an error occured while searching for directory entry with uniqueid %s: %s. "
+ "Logging of changes is disabled.\n", uniqueid, ldap_err2string(res));
+ /* GGOODREPL g_set_repl_backend( NULL ); */
+ rc = CL4_LDAP_ERROR;
+ goto done;
+ }
+
+ slapi_pblock_get(pb, SLAPI_PLUGIN_INTOP_SEARCH_ENTRIES, &entries);
+ if (entries == NULL || entries [0] == NULL)
+ {
+ slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name, "_cl4GetTargetEntry: entry (%s) not found\n",
+ slapi_sdn_get_ndn(sdn));
+ rc = CL4_NOT_FOUND;
+ goto done;
+ }
+
+ *entry = slapi_entry_dup (entries[0]);
+
+done:
+ if (pb)
+ {
+ slapi_free_search_results_internal(pb);
+ slapi_pblock_destroy (pb);
+ }
+
+ return rc;
+}
+
+static int _cl4FindTargetDN (const CSN *csn, const char *uniqueid,
+ const Slapi_DN *targetSDN, Slapi_DN **newTargetDN)
+{
+ int rc = CL4_SUCCESS;
+ int res, i;
+ Slapi_PBlock *pb;
+ char *suffix = changelog4_get_suffix ();
+ char filter [128];
+ Slapi_Entry **entries;
+ int minIndex = 0;
+ CSN *minCSN = NULL, *curCSN;
+ char *curType;
+ const Slapi_DN *sdn;
+ char s[CSN_STRSIZE];
+
+ *newTargetDN = NULL;
+
+ /* Look for all modifications to the target entry with csn larger than
+ this csn. We are only interested in rename operations, but change type
+ is currently not indexed */
+ sprintf (filter, "&(uniqueid=%s)(csn>%s)", uniqueid, csn_as_string(csn,PR_FALSE,s));
+ pb = slapi_pblock_new ();
+ slapi_search_internal_set_pb (pb, suffix, LDAP_SCOPE_SUBTREE, filter, NULL, 0, NULL, NULL,
+ repl_get_plugin_identity (PLUGIN_LEGACY_REPLICATION), 0);
+ slapi_search_internal_pb (pb);
+ slapi_ch_free ((void**)&suffix);
+ if (pb == NULL)
+ {
+ rc = CL4_LDAP_ERROR;
+ goto done;
+ }
+
+ slapi_pblock_get(pb, SLAPI_PLUGIN_INTOP_RESULT, &res);
+ if (res == LDAP_NO_SUCH_OBJECT) /* entry not found */
+ {
+ slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name, "_cl4FindTargetDN: no entries much filter (%s)\n",
+ filter);
+ rc = CL4_NOT_FOUND;
+ goto done;
+ }
+
+ if (res != LDAP_SUCCESS)
+ {
+ slapi_log_error( SLAPI_LOG_FATAL, repl_plugin_name,
+ "_cl4ResolveTargetDN: an error occured while searching change entries matching filter %s: %s. "
+ "Logging of changes is disabled.\n", filter, ldap_err2string(res));
+ /* GGOODREPL g_set_repl_backend( NULL ); */
+ rc = CL4_LDAP_ERROR;
+ goto done;
+ }
+
+ slapi_pblock_get(pb, SLAPI_PLUGIN_INTOP_SEARCH_ENTRIES, &entries);
+ if (entries == NULL)
+ {
+ slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name, "_cl4FindTargetDN: no entries much filter (%s)\n",
+ filter);
+ rc = CL4_NOT_FOUND;
+ goto done;
+ }
+
+ i = 0;
+
+ /* find rename operation with smallest csn - its target dn should be the name
+ of our change entry */
+ while (entries[i])
+ {
+ curType = slapi_entry_attr_get_charptr (entries[i], attr_changetype);
+ if (curType && strcasecmp (curType, "modrdn") == 0)
+ {
+ curCSN = csn_new_by_string (slapi_entry_attr_get_charptr (entries[i], attr_csn));
+ if (minCSN == NULL || csn_compare (curCSN, minCSN) < 0)
+ {
+ minCSN = curCSN;
+ minIndex = i;
+ }
+ }
+
+ if (curType)
+ slapi_ch_free ((void**)&curType);
+
+ i ++;
+ }
+
+ if (curCSN == NULL)
+ {
+ rc = CL4_NOT_FOUND;
+ goto done;
+ }
+
+ /* update targetDN of our entry if necessary */
+ sdn = slapi_entry_get_sdn_const(entries[minIndex]);
+
+ /* target dn does not match to renaming operation - rename change entry */
+ if (slapi_sdn_compare (sdn, targetSDN) != 0)
+ *newTargetDN = slapi_sdn_dup (sdn);
+
+done:
+ if (pb)
+ {
+ slapi_free_search_results_internal(pb);
+ slapi_pblock_destroy (pb);
+ }
+
+ return rc;
+}
+
+static int _cl4AssignChangeNumber (changeNumber *cnum)
+{
+ *cnum = ldapi_assign_changenumber();
+ return CL4_SUCCESS;
+}
+
+static int _cl4UpdateEntry (const CSN *csn, const char *changeType,
+ const Slapi_DN *newDN, changeNumber cnum)
+{
+ Slapi_PBlock *pb;
+ char *dn;
+ const char *dnTemp;
+ int res;
+ Slapi_Mods smods;
+ char cnumbuf[32];
+
+ if (csn == NULL || changeType == NULL)
+ {
+ slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name, "_cl4UpdateEntry: invalid argument\n");
+ return CL4_BAD_DATA;
+ }
+
+ dn = _cl4MakeCSNDN (csn);
+ if (dn == NULL)
+ {
+ slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name, "_cl4UpdateEntry: failed to create entry dn\n");
+ return CL4_MEMORY_ERROR;
+ }
+
+ slapi_mods_init(&smods, 2);
+ if (newDN)
+ {
+ if (strcasecmp (changeType, "add") == 0)
+ dnTemp = slapi_sdn_get_dn (newDN);
+ else
+ dnTemp = slapi_sdn_get_ndn (newDN);
+
+ slapi_mods_add(&smods, LDAP_MOD_REPLACE | LDAP_MOD_BVALUES, attr_targetdn,
+ strlen (dnTemp), dnTemp);
+ }
+ /* Set the changeNumber attribute */
+ sprintf(cnumbuf, "%lu", cnum);
+ slapi_mods_add (&smods, LDAP_MOD_REPLACE | LDAP_MOD_BVALUES, attr_changenumber,
+ strlen (cnumbuf), cnumbuf);
+ pb = slapi_pblock_new ();
+ slapi_modify_internal_set_pb (pb, dn, slapi_mods_get_ldapmods_byref(&smods), NULL, NULL,
+ repl_get_plugin_identity (PLUGIN_LEGACY_REPLICATION), 0);
+ slapi_modify_internal_pb (pb);
+ slapi_mods_done(&smods);
+ slapi_ch_free ((void**)&dn);
+
+ if (pb == NULL)
+ {
+ return CL4_LDAP_ERROR;
+ }
+
+ slapi_pblock_get(pb, SLAPI_PLUGIN_INTOP_RESULT, &res);
+ slapi_pblock_destroy(pb);
+ if (res != LDAP_SUCCESS)
+ {
+ char s[CSN_STRSIZE];
+ slapi_log_error( SLAPI_LOG_FATAL, repl_plugin_name,
+ "cl4ChangeTargetDN: an error occured while modifying change entry with csn %s: %s. "
+ "Logging of changes is disabled.\n", csn_as_string(csn,PR_FALSE,s), ldap_err2string(res));
+ /* GGOODREPL g_set_repl_backend( NULL ); */
+ return CL4_LDAP_ERROR;
+ }
+
+ if ( ldapi_get_first_changenumber() == (changeNumber) 0L )
+ {
+ ldapi_set_first_changenumber( cnum );
+ }
+
+ ldapi_commit_changenumber(cnum);
+ return CL4_SUCCESS;
+}
diff --git a/ldap/servers/plugins/replication/cl4_api.h b/ldap/servers/plugins/replication/cl4_api.h
new file mode 100644
index 00000000..c0b20e57
--- /dev/null
+++ b/ldap/servers/plugins/replication/cl4_api.h
@@ -0,0 +1,67 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+/* cl4_api.h - minimal interface to 4.0 changelog necessary to link 4.0 changelog
+ to 5.0 replication
+ */
+
+#ifndef CL4_API_H
+#define CL4_API_H
+
+#include "repl.h"
+
+/*** Error Codes ***/
+enum
+{
+ CL4_SUCCESS,
+ CL4_BAD_DATA,
+ CL4_BAD_FORMAT,
+ CL4_NOT_FOUND,
+ CL4_MEMORY_ERROR,
+ CL4_CSNPL_ERROR,
+ CL4_LDAP_ERROR,
+ CL4_INTERNAL_ERROR
+};
+
+/*** APIs ***/
+/* Name: cl4Init
+ Description: initializes 4.0 changelog subsystem
+ Parameters: none
+ Return: ????
+ */
+int cl4Init ();
+
+/* Name: cl4WriteOperation
+ Description: logs operation to 4.0 changelog; operation must go through CD&R engine first
+ Parameters: op - operation to be logged
+
+ Return: ????
+ */
+int cl4WriteOperation (const slapi_operation_parameters *op);
+
+/* Name: cl4ChangeTargetDN
+ Description: modifies change entry target dn; should be called for conflicts due to naming collisions;
+ raw dn should be passed for add operations; normolized dn otherwise.
+ Parameters: csn - csn of the change entry to be modified
+ newDN - new target dn of the entry
+ Return: ????
+ */
+int cl4ChangeTargetDN (const CSN* csn, const char *newDN);
+
+/* Name: cl4AssignChangeNumbers
+ Description: this function should be called periodically to assign change numbers to changelog
+ entries. Intended for use with event queue
+ Parameters: parameters are not currently used
+ Return: none
+ */
+void cl4AssignChangeNumbers (time_t when, void *arg);
+
+/* Name: cl4Cleanup
+ Description: frees memory held by 4.0 changelog subsystem
+ Parameters: none
+ Return: none
+ */
+void cl4Clean ();
+#endif
diff --git a/ldap/servers/plugins/replication/cl4_init.c b/ldap/servers/plugins/replication/cl4_init.c
new file mode 100644
index 00000000..6c12f0b0
--- /dev/null
+++ b/ldap/servers/plugins/replication/cl4_init.c
@@ -0,0 +1,349 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+
+/* cl4_init.c - implments initialization/cleanup functions for
+ 4.0 style changelog
+ */
+
+#include <string.h>
+
+#include "slapi-plugin.h"
+#include "cl4.h"
+#include "repl.h"
+
+/* forward declarations */
+static int changelog4_create_be();
+static int changelog4_start_be ();
+static int changelog4_close();
+static int changelog4_remove();
+
+/*
+ * Initialise the 4.0 Changelog
+ */
+int changelog4_init ()
+{
+ int rc= 0; /* OK */
+ Slapi_Backend *rbe;
+ changeNumber first_change = 0UL, last_change = 0UL;
+ int lderr;
+
+ if (changelog4_create_be() < 0 )
+ {
+ rc= -1;
+ }
+ else
+ {
+ rc = changelog4_start_be ();
+ }
+
+ if(rc == 0)
+ {
+ rbe = get_repl_backend();
+ if(rbe!=NULL)
+ {
+ /* We have a Change Log. Check it's valid. */
+ /* changelog has to be started before its
+ data version can be read */
+ const char *sdv= get_server_dataversion();
+ const char *cdv= get_changelog_dataversion();
+ char *suffix = changelog4_get_suffix ();
+ if(!cdv || strcmp(sdv,cdv)!=0)
+ {
+
+ /* The SDV and CDV are not the same. The Change Log is invalid.
+ It must be removed. */
+ /* ONREPL - currently we go through this code when the changelog
+ is first created because we can't tell new backend from the
+ existing one.*/
+ rc = changelog4_close();
+ rc = changelog4_remove();
+
+ /* now restart the changelog */
+ changelog4_start_be ();
+
+ create_entity( suffix, "extensibleobject");
+ /* Write the Server Data Version onto the changelog suffix entry */
+ /* JCMREPL - And the changelog database version number */
+ set_changelog_dataversion(sdv);
+ slapi_ch_free ((void **)&suffix);
+
+ }
+ }
+ }
+
+ if(rc != 0)
+ {
+ slapi_log_error( SLAPI_LOG_PLUGIN, repl_plugin_name,
+ "An error occurred configuring the changelog database\n" );
+ }
+
+ first_change = replog_get_firstchangenum( &lderr );
+ last_change = replog_get_lastchangenum( &lderr );
+ ldapi_initialize_changenumbers( first_change, last_change );
+
+ return rc;
+}
+
+static int
+
+changelog4_close()
+{
+ int rc= 0 /* OK */;
+ Slapi_Backend *rbe= get_repl_backend();
+ Slapi_PBlock *pb = slapi_pblock_new ();
+ IFP closefn = NULL;
+
+ rc = slapi_be_getentrypoint (rbe, SLAPI_PLUGIN_CLOSE_FN, (void**)&closefn, pb);
+ if (rc != 0)
+ {
+ slapi_log_error( SLAPI_LOG_FATAL, repl_plugin_name,
+ "Error: backend close entry point is missing. "
+ "Replication subsystem disabled.\n");
+ slapi_pblock_destroy (pb);
+ set_repl_backend( NULL );
+ return -1;
+ }
+
+ rc = closefn (pb);
+
+ if (rc != 0)
+ {
+
+ slapi_log_error( SLAPI_LOG_FATAL, repl_plugin_name, "Error: the changelog database could "
+ "not be closed. Replication subsystem disabled.\n");
+ set_repl_backend( NULL );
+ rc = -1;
+ }
+
+ slapi_pblock_destroy (pb);
+ return rc;
+
+}
+
+static int
+changelog4_remove()
+{
+ int rc= 0 /* OK */;
+ Slapi_Backend *rbe= get_repl_backend();
+ Slapi_PBlock *pb = slapi_pblock_new ();
+ IFP rmdbfn = NULL;
+
+ rc = slapi_be_getentrypoint (rbe, SLAPI_PLUGIN_DB_RMDB_FN, (void**)&rmdbfn, pb);
+ if (rc != 0)
+ {
+ slapi_log_error( SLAPI_LOG_FATAL, repl_plugin_name,
+ "Error: backend rmdb entry point is missing. "
+ "Replication subsystem disabled.\n");
+ slapi_pblock_destroy (pb);
+ set_repl_backend( NULL );
+ return -1;
+ }
+
+ rc = rmdbfn (pb);
+
+ if (rc != 0)
+ {
+
+ slapi_log_error( SLAPI_LOG_FATAL, repl_plugin_name, "Error: the changelog database could "
+ "not be removed. Replication subsystem disabled.\n");
+ rc = -1;
+ }
+ else
+ {
+ slapi_log_error( SLAPI_LOG_REPL, repl_plugin_name, "New database generation computed. "
+ "Changelog database removed.\n");
+ }
+
+ slapi_pblock_destroy (pb);
+ return rc;
+}
+
+static Slapi_Backend *repl_backend = NULL;
+
+Slapi_Backend
+*get_repl_backend()
+{
+ return repl_backend;
+}
+
+void
+set_repl_backend(Slapi_Backend *be)
+{
+ repl_backend = be;
+}
+
+
+int changelog4_shutdown ()
+{
+ /* ONREPL - will shutdown the backend */
+ int rc = 1;
+
+ return rc;
+}
+
+static void changelog4_init_trimming ()
+{
+ char *cl_maxage = changelog4_get_maxage ();
+ unsigned long cl_maxentries = changelog4_get_maxentries ();
+ time_t ageval = age_str2time (cl_maxage);
+
+ slapi_ch_free ((void **)&cl_maxage);
+
+ init_changelog_trimming(cl_maxentries, ageval );
+}
+
+
+
+/*
+ * Function: changelog4_create_be
+ * Arguments: none
+ * Returns: 0 on success, non-0 on error
+ * Description: configures changelog backend instance.
+ */
+
+static int
+changelog4_create_be()
+{
+ int i, dir_count = 5;
+ Slapi_Backend *rbe;
+ slapi_be_config config;
+ char *cl_dir = changelog4_get_dir ();
+ char *cl_suffix;
+
+ if ( cl_dir == NULL ) {
+ slapi_log_error( SLAPI_LOG_FATAL, repl_plugin_name,
+ "Error: no directory specified for changelog database.\n");
+ return -1;
+ }
+
+ cl_suffix = changelog4_get_suffix ();
+
+ if ( cl_suffix == NULL ) {
+ slapi_ch_free ((void **)&cl_dir);
+ slapi_log_error( SLAPI_LOG_FATAL, repl_plugin_name,
+ "Error: no suffix specified for changelog database.\n");
+ return -1;
+ }
+
+ /* setup configuration parameters for backend initialization */
+ config.type = CHANGELOG_LDBM_TYPE;
+ config.suffix = cl_suffix;
+ config.is_private = 1; /* yes */
+ config.log_change = 0; /* no */
+ config.directives = (slapi_config_directive*)slapi_ch_calloc(
+ dir_count, sizeof(slapi_config_directive));
+ config.dir_count = dir_count;
+
+ for (i = 0; i < dir_count; i++)
+ {
+ config.directives[i].file_name = "(internal)";
+ config.directives[i].lineno = 0;
+ }
+
+ /* setup indexes */
+ config.directives[0].argv = NULL;
+ config.directives[0].argc = 3;
+ charray_add( &(config.directives[0].argv), slapi_ch_strdup( "index" ));
+ charray_add( &(config.directives[0].argv), slapi_ch_strdup( attr_changenumber ));
+ charray_add( &(config.directives[0].argv), slapi_ch_strdup( "eq" ));
+
+ /* Set up the database directory */
+ config.directives[1].argv = NULL;
+ config.directives[1].argc = 2;
+ charray_add( &(config.directives[1].argv), slapi_ch_strdup( "directory" ));
+ charray_add( &(config.directives[1].argv), slapi_ch_strdup( cl_dir ));
+
+ /* Override the entry cache size */
+ config.directives[2].argv = NULL;
+ config.directives[2].argc = 2;
+ charray_add( &(config.directives[2].argv), slapi_ch_strdup( "cachesize" ));
+ charray_add( &(config.directives[2].argv), slapi_ch_strdup( "10" ));
+
+ /* Override the database cache size */
+ config.directives[3].argv = NULL;
+ config.directives[3].argc = 2;
+ charray_add( &(config.directives[3].argv), slapi_ch_strdup( "dbcachesize" ));
+ charray_add( &(config.directives[3].argv), slapi_ch_strdup( "1000000" ));
+
+ /* Override the allids threshold */
+ config.directives[4].argv = NULL;
+ config.directives[4].argc = 2;
+ charray_add( &(config.directives[4].argv), slapi_ch_strdup( "allidsthreshold" ));
+ /* assumes sizeof(int) >= 32 bits */
+ charray_add( &(config.directives[4].argv), slapi_ch_strdup( "2147483647" ));
+
+ /* rbe = slapi_be_create_instance(&config, LDBM_TYPE); */
+ rbe= NULL;
+
+ /* free memory allocated to argv */
+ for (i = 0; i < dir_count; i++)
+ {
+ charray_free (config.directives[i].argv);
+ }
+
+ slapi_ch_free ((void **)&config.directives);
+ slapi_ch_free ((void **)&cl_dir);
+ slapi_ch_free ((void **)&cl_suffix);
+
+ if (rbe == NULL)
+ {
+ slapi_log_error( SLAPI_LOG_FATAL, repl_plugin_name,
+ "Error: failed to create changelog backend. "
+ "Replication disabled.\n");
+ return -1;
+ }
+
+ set_repl_backend (rbe);
+
+ changelog4_init_trimming ();
+
+ return 0;
+}
+
+/* Name: changelog4_start_be
+ * Parameters: none
+ * Return: 0 if successful, non 0 otherwise
+ * Description: starts the changelog backend; backend must be configured
+ * first via call to changelog4_create_be
+ */
+static int
+changelog4_start_be ()
+{
+ int rc;
+ IFP startfn = NULL;
+ Slapi_PBlock *pb;
+ Slapi_Backend *rbe = get_repl_backend ();
+
+ if (rbe)
+ {
+ pb = slapi_pblock_new();
+ rc = slapi_be_getentrypoint(rbe, SLAPI_PLUGIN_START_FN, (void**)&startfn, pb);
+ if (rc != 0)
+ {
+ slapi_log_error( SLAPI_LOG_FATAL, repl_plugin_name,
+ "Error: backend start entry point is missing. "
+ "Replication subsystem disabled.\n");
+ slapi_pblock_destroy (pb);
+ set_repl_backend( NULL );
+ return -1;
+ }
+
+ rc = startfn (pb);
+ slapi_pblock_destroy (pb);
+
+ if (rc != 0)
+ {
+ slapi_log_error( SLAPI_LOG_FATAL, repl_plugin_name,
+ "Error: Failed to start changelog backend. "
+ "Replication subsystem disabled.\n");
+ set_repl_backend( NULL );
+ return -1;
+ }
+ }
+
+ return 0;
+}
+
diff --git a/ldap/servers/plugins/replication/cl5.h b/ldap/servers/plugins/replication/cl5.h
new file mode 100644
index 00000000..a80c666b
--- /dev/null
+++ b/ldap/servers/plugins/replication/cl5.h
@@ -0,0 +1,38 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+/* cl5.h - changelog related function */
+
+#ifndef CL5_H
+#define CL5_H
+
+#include "cl5_api.h" /* changelog access APIs */
+
+typedef struct changelog5Config
+{
+ char *dir;
+/* These 2 parameters are needed for changelog trimming. Already present in 5.0 */
+ char *maxAge;
+ int maxEntries;
+/* the changelog DB configuration parameters are defined as CL5DBConfig in cl5_api.h */
+ CL5DBConfig dbconfig;
+}changelog5Config;
+
+/* initializes changelog*/
+int changelog5_init();
+/* cleanups changelog data */
+void changelog5_cleanup();
+/* initializes changelog configurationd */
+int changelog5_config_init();
+/* cleanups config data */
+void changelog5_config_cleanup();
+/* reads changelog configuration */
+int changelog5_read_config (changelog5Config *config);
+/* cleanups the content of the config structure */
+void changelog5_config_done (changelog5Config *config);
+/* frees the content and the config structure */
+void changelog5_config_free (changelog5Config **config);
+
+#endif
diff --git a/ldap/servers/plugins/replication/cl5_api.c b/ldap/servers/plugins/replication/cl5_api.c
new file mode 100644
index 00000000..792d3646
--- /dev/null
+++ b/ldap/servers/plugins/replication/cl5_api.c
@@ -0,0 +1,6512 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+
+/* cl5_api.c - implementation of 5.0 style changelog API */
+
+#include <errno.h>
+#include <sys/stat.h>
+#if defined( OS_solaris ) || defined( hpux )
+#include <sys/types.h>
+#include <sys/statvfs.h>
+#endif
+#if defined( linux )
+#include <sys/vfs.h>
+#endif
+
+
+#include "cl5_api.h"
+#include "plhash.h"
+
+#include "db.h"
+#include "cl5_clcache.h" /* To use the Changelog Cache */
+#include "repl5.h" /* for agmt_get_consumer_rid() */
+
+#define CL5_TYPE "Changelog5" /* changelog type */
+#define VERSION_SIZE 127 /* size of the buffer to hold changelog version */
+#define GUARDIAN_FILE "guardian" /* name of the guardian file */
+#define VERSION_FILE "DBVERSION" /* name of the version file */
+#define MAX_TRIALS 50 /* number of retries on db operations */
+#define V_5 5 /* changelog entry version */
+#define CHUNK_SIZE 64*1024
+#define DBID_SIZE 64
+#define FILE_SEP "_" /* separates parts of the db file name */
+
+#define T_CSNSTR "csn"
+#define T_UNIQUEIDSTR "nsuniqueid"
+#define T_PARENTIDSTR "parentuniqueid"
+#define T_NEWSUPERIORDNSTR "newsuperiordn"
+#define T_NEWSUPERIORIDSTR "newsuperioruniqueid"
+#define T_REPLGEN "replgen"
+
+#define ENTRY_COUNT_TIME 111 /* this time is used to construct csn
+ used to store/retrieve entry count */
+#define PURGE_RUV_TIME 222 /* this time is used to construct csn
+ used to store purge RUV vector */
+#define MAX_RUV_TIME 333 /* this time is used to construct csn
+ used to store upper boundary RUV vector */
+
+#define DB_EXTENSION_DB3 "db3"
+#define DB_EXTENSION "db4"
+
+#define HASH_BACKETS_COUNT 16 /* number of buckets in a hash table */
+
+#if 1000*DB_VERSION_MAJOR + 100*DB_VERSION_MINOR >= 4100
+#define DEFAULT_DB_OP_FLAGS DB_AUTO_COMMIT
+#define DB_OPEN(oflags, db, txnid, file, database, type, flags, mode, rval) \
+{ \
+ if (((oflags) & DB_INIT_TXN) && ((oflags) & DB_INIT_LOG)) \
+ { \
+ (rval) = (db)->open((db), (txnid), (file), (database), (type), (flags)|DB_AUTO_COMMIT, (mode)); \
+ } \
+ else \
+ { \
+ (rval) = (db)->open((db), (txnid), (file), (database), (type), (flags), (mode)); \
+ } \
+}
+#else /* older then db 41 */
+#define DEFAULT_DB_OP_FLAGS 0
+#define DB_OPEN(oflags, db, txnid, file, database, type, flags, mode, rval) \
+ (rval) = (db)->open((db), (file), (database), (type), (flags), (mode))
+#endif
+
+#if 1000*DB_VERSION_MAJOR + 100*DB_VERSION_MINOR >= 4000
+#define DB_ENV_SET_REGION_INIT(env) (env)->set_flags((env), DB_REGION_INIT, 1)
+#define DB_ENV_SET_TAS_SPINS(env, tas_spins) \
+ (env)->set_tas_spins((env), (tas_spins))
+#define TXN_BEGIN(env, parent_txn, tid, flags) \
+ (env)->txn_begin((env), (parent_txn), (tid), (flags))
+#define TXN_COMMIT(txn, flags) (txn)->commit((txn), (flags))
+#define TXN_ABORT(txn) (txn)->abort(txn)
+#define TXN_CHECKPOINT(env, kbyte, min, flags) \
+ (env)->txn_checkpoint((env), (kbyte), (min), (flags))
+#define MEMP_STAT(env, gsp, fsp, flags, malloc) \
+ (env)->memp_stat((env), (gsp), (fsp), (flags))
+#define MEMP_TRICKLE(env, pct, nwrotep) \
+ (env)->memp_trickle((env), (pct), (nwrotep))
+#define LOG_ARCHIVE(env, listp, flags, malloc) \
+ (env)->log_archive((env), (listp), (flags))
+#define LOG_FLUSH(env, lsn) (env)->log_flush((env), (lsn))
+#define LOCK_DETECT(env, flags, atype, aborted) \
+ (env)->lock_detect((env), (flags), (atype), (aborted))
+
+#else /* older than db 4.0 */
+#define DB_ENV_SET_REGION_INIT(env) db_env_set_region_init(1)
+#define DB_ENV_SET_TAS_SPINS(env, tas_spins) \
+ db_env_set_tas_spins((tas_spins))
+#define TXN_BEGIN(env, parent_txn, tid, flags) \
+ txn_begin((env), (parent_txn), (tid), (flags))
+#define TXN_COMMIT(txn, flags) txn_commit((txn), (flags))
+#define TXN_ABORT(txn) txn_abort((txn))
+#define TXN_CHECKPOINT(env, kbyte, min, flags) \
+ txn_checkpoint((env), (kbyte), (min), (flags))
+#define MEMP_TRICKLE(env, pct, nwrotep) memp_trickle((env), (pct), (nwrotep))
+#define LOG_FLUSH(env, lsn) log_flush((env), (lsn))
+#define LOCK_DETECT(env, flags, atype, aborted) \
+ lock_detect((env), (flags), (atype), (aborted))
+
+#if 1000*DB_VERSION_MAJOR + 100*DB_VERSION_MINOR >= 3300
+#define MEMP_STAT(env, gsp, fsp, flags, malloc) memp_stat((env), (gsp), (fsp))
+#define LOG_ARCHIVE(env, listp, flags, malloc) \
+ log_archive((env), (listp), (flags))
+
+#else /* older than db 3.3 */
+#define MEMP_STAT(env, gsp, fsp, flags, malloc) \
+ memp_stat((env), (gsp), (fsp), (malloc))
+#define LOG_ARCHIVE(env, listp, flags, malloc) \
+ log_archive((env), (listp), (flags), (malloc))
+#endif
+#endif
+/*
+ * The defult thread stacksize for nspr21 is 64k. For OSF, we require
+ * a larger stacksize as actual storage allocation is higher i.e
+ * pointers are allocated 8 bytes but lower 4 bytes are used.
+ * The value 0 means use the default stacksize.
+ */
+#if defined (OSF1) || defined (__LP64__) || defined (_LP64) /* 64-bit architectures need bigger stacks */
+#define DEFAULT_THREAD_STACKSIZE 131072L
+#else
+#define DEFAULT_THREAD_STACKSIZE 0
+#endif
+
+#ifdef _WIN32
+#define FILE_CREATE_MODE S_IREAD | S_IWRITE
+#define DIR_CREATE_MODE 0755
+#else /* _WIN32 */
+#define FILE_CREATE_MODE S_IRUSR | S_IWUSR
+#define DIR_CREATE_MODE 0755
+#endif
+
+#define NO_DISK_SPACE 1024
+#define MIN_DISK_SPACE 10485760 /* 10 MB */
+
+/***** Data Definitions *****/
+
+/* possible changelog open modes */
+typedef enum
+{
+ CL5_OPEN_NONE, /* nothing specified */
+ CL5_OPEN_NORMAL, /* open for normal read/write use */
+ CL5_OPEN_RESTORE_RECOVER, /* restore from archive and recover */
+ CL5_OPEN_RESTORE, /* restore, but no recovery */
+ CL5_OPEN_LDIF2CL, /* open as part of ldif2cl: no locking,
+ recovery, checkpointing */
+ CL5_OPEN_CLEAN_RECOVER /* remove env after recover open (upgrade) */
+} CL5OpenMode;
+
+#define DB_FILE_DELETED 0x1
+#define DB_FILE_INIT 0x2
+/* this structure represents one changelog file, Each changelog file contains
+ changes applied to a single backend. Files are named by the database id */
+typedef struct cl5dbfile
+{
+ char *name; /* file name (with the extension) */
+ char *replGen; /* replica generation of the data */
+ char *replName; /* replica name */
+ DB *db; /* db handle to the changelog file*/
+ int entryCount; /* number of entries in the file */
+ int flags; /* currently used to mark the file as deleted
+ * or as initialized */
+ RUV *purgeRUV; /* ruv to which the file has been purged */
+ RUV *maxRUV; /* ruv that marks the upper boundary of the data */
+ char *semaName; /* semaphore name */
+ PRSem *sema; /* semaphore for max concurrent cl writes */
+}CL5DBFile;
+
+/* structure that allows to iterate through entries to be sent to a consumer
+ that originated on a particular supplier. */
+struct cl5replayiterator
+{
+ Object *fileObj;
+ CLC_Buffer *clcache; /* changelog cache */
+ ReplicaId consumerRID; /* consumer's RID */
+ const RUV *consumerRuv; /* consumer's update vector */
+ Object *supplierRuvObj;/* supplier's update vector object */
+};
+
+typedef struct cl5iterator
+{
+ DBC *cursor; /* current position in the db file */
+ Object *file; /* handle to release db file object */
+}CL5Iterator;
+
+/* changelog trimming configuration */
+typedef struct cl5trim
+{
+ time_t maxAge; /* maximum entry age in seconds */
+ int maxEntries; /* maximum number of entries across all changelog files */
+ PRLock* lock; /* controls access to trimming configuration */
+} CL5Trim;
+
+/* this structure defines 5.0 changelog internals */
+typedef struct cl5desc
+{
+ char *dbDir; /* absolute path to changelog directory */
+ DB_ENV *dbEnv; /* db environment shared by all db files */
+ int dbEnvOpenFlags;/* openflag used for env->open */
+ Objset *dbFiles; /* ref counted set of changelog files (CL5DBFile) */
+ PRLock *fileLock; /* ensures that changelog file is not added twice */
+ CL5OpenMode dbOpenMode; /* how we open db */
+ CL5DBConfig dbConfig; /* database configuration params */
+ CL5Trim dbTrim; /* trimming parameters */
+ CL5State dbState; /* changelog current state */
+ PRRWLock *stLock; /* lock that controls access to the changelog state */
+ PRBool dbRmOnClose;/* indicates whether changelog should be removed when
+ it is closed */
+ PRBool fatalError; /* bad stuff happened like out of disk space; don't
+ write guardian file on close - UnUsed so far */
+ int threadCount;/* threads that globally access changelog like
+ deadlock detection, etc. */
+ PRLock *clLock; /* Lock associated to clVar, used to notify threads on close */
+ PRCondVar *clCvar; /* Condition Variable used to notify threads on close */
+} CL5Desc;
+
+typedef void (*VFP)(void *);
+
+int g_get_shutdown(); /* declared in proto-slap.h */
+
+/***** Global Variables *****/
+static CL5Desc s_cl5Desc;
+
+/***** Forward Declarations *****/
+
+/* changelog initialization and cleanup */
+static int _cl5Open (const char *dir, const CL5DBConfig *config, CL5OpenMode openMode);
+static int _cl5AppInit (PRBool *didRecovery);
+static int _cl5DBOpen ();
+static void _cl5SetDefaultDBConfig ();
+static void _cl5SetDBConfig (const CL5DBConfig *config);
+static void _cl5InitDBEnv(DB_ENV *dbEnv);
+static int _cl5CheckDBVersion ();
+static int _cl5ReadDBVersion (const char *dir, char *clVersion);
+static int _cl5WriteDBVersion ();
+static int _cl5CheckGuardian ();
+static int _cl5ReadGuardian (char *buff);
+static int _cl5WriteGuardian ();
+static int _cl5RemoveGuardian ();
+static void _cl5Close ();
+static int _cl5Delete (const char *dir, PRBool rmDir);
+static void _cl5DBClose ();
+
+/* thread management */
+static int _cl5DispatchDBThreads ();
+static int _cl5AddThread ();
+static void _cl5RemoveThread ();
+static int _cl5DeadlockMain (void *param);
+static int _cl5CheckpointMain (void *param);
+static int _cl5TrickleMain (void *param);
+
+/* functions that work with individual changelog files */
+static int _cl5NewDBFile (const char *replName, const char *replGen, CL5DBFile** dbFile);
+static int _cl5DBOpenFile (Object *replica, Object **obj, PRBool checkDups);
+static int _cl5DBOpenFileByReplicaName (const char *replName, const char *replGen,
+ Object **obj, PRBool checkDups);
+static void _cl5DBCloseFile (void **data);
+static void _cl5DBDeleteFile (Object *obj);
+static void _cl5DBFileInitialized (Object *obj);
+static int _cl5GetDBFile (Object *replica, Object **obj);
+static int _cl5GetDBFileByReplicaName (const char *replName, const char *replGen,
+ Object **obj);
+static int _cl5AddDBFile (CL5DBFile *file, Object **obj);
+static int _cl5CompareDBFile (Object *el1, const void *el2);
+static int _cl5CopyDBFiles (const char *srcDir, const char *distDir, Object **replicas);
+static char* _cl5Replica2FileName (Object *replica);
+static char* _cl5MakeFileName (const char *replName, const char *replGen);
+static PRBool _cl5FileName2Replica (const char *fileName, Object **replica);
+static int _cl5ExportFile (PRFileDesc *prFile, Object *obj);
+static PRBool _cl5ReplicaInList (Object *replica, Object **replicas);
+
+/* data storage and retrieval */
+static int _cl5Entry2DBData (const CL5Entry *entry, char **data, PRUint32 *len);
+static int _cl5WriteOperation(const char *replName, const char *replGen,
+ const slapi_operation_parameters *op, PRBool local);
+static int _cl5GetFirstEntry (Object *obj, CL5Entry *entry, void **iterator, DB_TXN *txnid);
+static int _cl5GetNextEntry (CL5Entry *entry, void *iterator);
+static int _cl5CurrentDeleteEntry (void *iterator);
+static PRBool _cl5IsValidIterator (const CL5Iterator *iterator);
+static int _cl5GetOperation (Object *replica, slapi_operation_parameters *op);
+static const char* _cl5OperationType2Str (int type);
+static int _cl5Str2OperationType (const char *str);
+static void _cl5WriteString (const char *str, char **buff);
+static void _cl5ReadString (char **str, char **buff);
+static void _cl5WriteMods (LDAPMod **mods, char **buff);
+static void _cl5WriteMod (LDAPMod *mod, char **buff);
+static int _cl5ReadMods (LDAPMod ***mods, char **buff);
+static int _cl5ReadMod (Slapi_Mod *mod, char **buff);
+static int _cl5GetModsSize (LDAPMod **mods);
+static int _cl5GetModSize (LDAPMod *mod);
+static void _cl5ReadBerval (struct berval *bv, char** buff);
+static void _cl5WriteBerval (struct berval *bv, char** buff);
+static int _cl5ReadBervals (struct berval ***bv, char** buff, unsigned int size);
+static int _cl5WriteBervals (struct berval **bv, char** buff, unsigned int *size);
+
+/* replay iteration */
+static PRBool _cl5ValidReplayIterator (const CL5ReplayIterator *iterator);
+static int _cl5PositionCursorForReplay (ReplicaId consumerRID, const RUV *consumerRuv,
+ Object *replica, Object *fileObject, CL5ReplayIterator **iterator);
+static int _cl5CheckMissingCSN (const CSN *minCsn, const RUV *supplierRUV, CL5DBFile *file);
+
+/* changelog trimming */
+static int _cl5TrimInit ();
+static void _cl5TrimCleanup ();
+static int _cl5TrimMain (void *param);
+static void _cl5DoTrimming ();
+static void _cl5TrimFile (Object *obj, long *numToTrim);
+static PRBool _cl5CanTrim (time_t time, long *numToTrim);
+static int _cl5ReadRUV (const char *replGen, Object *obj, PRBool purge);
+static int _cl5WriteRUV (CL5DBFile *file, PRBool purge);
+static int _cl5ConstructRUV (const char *replGen, Object *obj, PRBool purge);
+static int _cl5UpdateRUV (Object *obj, CSN *csn, PRBool newReplica, PRBool purge);
+static int _cl5GetRUV2Purge2 (Object *fileObj, RUV **ruv);
+
+/* db error processing */
+static void _cl5DBLogPrint(const char* prefix, char *buffer);
+
+/* bakup/recovery, import/export */
+static PRBool _cl5IsLogFile (const char *name);
+static int _cl5Recover (int open_flags, DB_ENV *dbEnv);
+static int _cl5LDIF2Operation (char *ldifEntry, slapi_operation_parameters *op,
+ char **replGen);
+static int _cl5Operation2LDIF (const slapi_operation_parameters *op, const char *replGen,
+ char **ldifEntry, PRInt32 *lenLDIF);
+
+/* entry count */
+static int _cl5GetEntryCount (CL5DBFile *file);
+static int _cl5WriteEntryCount (CL5DBFile *file);
+
+/* misc */
+static char* _cl5GetHelperEntryKey (int type, char *csnStr);
+static Object* _cl5GetReplica (const slapi_operation_parameters *op, const char* replGen);
+static int _cl5FileEndsWith(const char *filename, const char *ext);
+
+/* Callback function for libdb to spit error info into our log */
+static void dblayer_log_print(const char* prefix, char *buffer)
+{
+ /* We ignore the prefix since we know who we are anyway */
+ slapi_log_error(SLAPI_LOG_REPL, repl_plugin_name_cl, "libdb: %s\n", buffer);
+}
+
+static PRLock *cl5_diskfull_lock = NULL;
+static int cl5_diskfull_flag = 0;
+
+static void cl5_set_diskfull();
+static void cl5_set_no_diskfull();
+
+/***** Module APIs *****/
+
+/* Name: cl5Init
+ Description: initializes changelog module; must be called by a single thread
+ before any other changelog function.
+ Parameters: none
+ Return: CL5_SUCCESS if function is successful;
+ CL5_SYSTEM_ERROR error if NSPR call fails.
+ */
+int cl5Init ()
+{
+ s_cl5Desc.stLock = PR_NewRWLock(PR_RWLOCK_RANK_NONE, "state_lock");
+ if (s_cl5Desc.stLock == NULL)
+ {
+ slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name_cl,
+ "cl5Init: failed to create state lock; NSPR error - %d\n",
+ PR_GetError ());
+ return CL5_SYSTEM_ERROR;
+ }
+ if ((s_cl5Desc.clLock = PR_NewLock()) == NULL)
+ {
+ slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name_cl,
+ "cl5Init: failed to create on close lock; NSPR error - %d\n",
+ PR_GetError ());
+ return CL5_SYSTEM_ERROR;
+
+ }
+ if ((s_cl5Desc.clCvar = PR_NewCondVar(s_cl5Desc.clLock)) == NULL)
+ {
+ slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name_cl,
+ "cl5Init: failed to create on close cvar; NSPR error - %d\n",
+ PR_GetError ());
+ return CL5_SYSTEM_ERROR;
+ }
+
+ if (( clcache_init (&s_cl5Desc.dbEnv) != 0 )) {
+ return CL5_SYSTEM_ERROR;
+ }
+
+ s_cl5Desc.dbState = CL5_STATE_CLOSED;
+ s_cl5Desc.fatalError = PR_FALSE;
+ s_cl5Desc.dbRmOnClose = PR_FALSE;
+ s_cl5Desc.threadCount = 0;
+
+ if (NULL == cl5_diskfull_lock)
+ {
+ cl5_diskfull_lock = PR_NewLock ();
+ }
+
+ return CL5_SUCCESS;
+}
+
+/* Name: cl5Cleanup
+ Description: performs cleanup of the changelog module; must be called by a single
+ thread; it closes changelog if it is still open.
+ Parameters: none
+ Return: none
+ */
+void cl5Cleanup ()
+{
+ /* close db if it is still open */
+ if (s_cl5Desc.dbState == CL5_STATE_OPEN)
+ {
+ cl5Close ();
+ }
+
+ if (s_cl5Desc.stLock)
+ PR_DestroyRWLock (s_cl5Desc.stLock);
+ s_cl5Desc.stLock = NULL;
+
+ if (cl5_diskfull_lock)
+ {
+ PR_DestroyLock (cl5_diskfull_lock);
+ cl5_diskfull_lock = NULL;
+ }
+
+ memset (&s_cl5Desc, 0, sizeof (s_cl5Desc));
+}
+
+/* Name: cl5Open
+ Description: opens changelog; must be called after changelog is
+ initialized using cl5Init. It is thread safe and the second
+ call is ignored.
+ Parameters: dir - changelog dir
+ config - db configuration parameters; currently not used
+ Return: CL5_SUCCESS if successfull;
+ CL5_BAD_DATA if invalid directory is passed;
+ CL5_BAD_STATE if changelog is not initialized;
+ CL5_BAD_DBVERSION if dbversion file is missing or has unexpected data
+ CL5_SYSTEM_ERROR if NSPR error occured (during db directory creation);
+ CL5_MEMORY_ERROR if memory allocation fails;
+ CL5_DB_ERROR if db initialization fails.
+ */
+int cl5Open (const char *dir, const CL5DBConfig *config)
+{
+ int rc;
+
+ if (dir == NULL)
+ {
+ slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name_cl, "cl5Open: null directory\n");
+ return CL5_BAD_DATA;
+ }
+
+ if (s_cl5Desc.dbState == CL5_STATE_NONE)
+ {
+ slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name_cl,
+ "cl5Open: changelog is not initialized\n");
+ return CL5_BAD_STATE;
+ }
+
+ /* prevent state from changing */
+ PR_RWLock_Wlock (s_cl5Desc.stLock);
+
+ /* already open - ignore */
+ if (s_cl5Desc.dbState == CL5_STATE_OPEN)
+ {
+ slapi_log_error(SLAPI_LOG_PLUGIN, repl_plugin_name_cl,
+ "cl5Open: changelog already opened; request ignored\n");
+ rc = CL5_SUCCESS;
+ goto done;
+ }
+ else if (s_cl5Desc.dbState != CL5_STATE_CLOSED)
+ {
+ slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name_cl,
+ "cl5Open: invalid state - %d\n", s_cl5Desc.dbState);
+ rc = CL5_BAD_STATE;
+ goto done;
+ }
+
+ rc = _cl5Open (dir, config, CL5_OPEN_NORMAL);
+ if (rc != CL5_SUCCESS)
+ {
+ slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name_cl,
+ "cl5Open: failed to open changelog\n");
+ goto done;
+ }
+
+ /* dispatch global threads like deadlock detection, trimming, etc */
+ rc = _cl5DispatchDBThreads ();
+ if (rc != CL5_SUCCESS)
+ {
+ slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name_cl,
+ "cl5Open: failed to start database monitoring threads\n");
+
+ _cl5Close ();
+ }
+ else
+ {
+ s_cl5Desc.dbState = CL5_STATE_OPEN;
+ }
+
+done:;
+ PR_RWLock_Unlock (s_cl5Desc.stLock);
+
+ return rc;
+}
+
+/* Name: cl5Close
+ Description: closes changelog; waits until all threads are done using changelog;
+ call is ignored if changelog is already closed.
+ Parameters: none
+ Return: CL5_SUCCESS if successful;
+ CL5_BAD_STATE if db is not in the open or closed state;
+ CL5_SYSTEM_ERROR if NSPR call fails;
+ CL5_DB_ERROR if db shutdown fails
+ */
+int cl5Close ()
+{
+ int rc = CL5_SUCCESS;
+
+ if (s_cl5Desc.dbState == CL5_STATE_NONE)
+ {
+ slapi_log_error(SLAPI_LOG_REPL, repl_plugin_name_cl,
+ "cl5Close: changelog is not initialized\n");
+ return CL5_BAD_STATE;
+ }
+
+ PR_RWLock_Wlock (s_cl5Desc.stLock);
+
+ /* already closed - ignore */
+ if (s_cl5Desc.dbState == CL5_STATE_CLOSED)
+ {
+ slapi_log_error(SLAPI_LOG_PLUGIN, repl_plugin_name_cl,
+ "cl5Close: changelog closed; request ignored\n");
+ PR_RWLock_Unlock (s_cl5Desc.stLock);
+ return CL5_SUCCESS;
+ }
+ else if (s_cl5Desc.dbState != CL5_STATE_OPEN)
+ {
+ slapi_log_error(SLAPI_LOG_REPL, repl_plugin_name_cl,
+ "cl5Close: invalid state - %d\n", s_cl5Desc.dbState);
+ PR_RWLock_Unlock (s_cl5Desc.stLock);
+ return CL5_BAD_STATE;
+ }
+
+ /* signal changelog closing to all threads */
+ s_cl5Desc.dbState = CL5_STATE_CLOSING;
+
+ PR_Lock(s_cl5Desc.clLock);
+ PR_NotifyCondVar(s_cl5Desc.clCvar);
+ PR_Unlock(s_cl5Desc.clLock);
+
+ _cl5Close ();
+
+ s_cl5Desc.dbState = CL5_STATE_CLOSED;
+
+ PR_RWLock_Unlock (s_cl5Desc.stLock);
+
+ return rc;
+}
+
+/* Name: cl5Delete
+ Description: removes changelog; changelog must be in the closed state.
+ Parameters: dir - changelog directory
+ Return: CL5_SUCCESS if successful;
+ CL5_BAD_STATE if the changelog is not in closed state;
+ CL5_BAD_DATA if invalid directory supplied
+ CL5_SYSTEM_ERROR if NSPR call fails
+ */
+int cl5Delete (const char *dir)
+{
+ int rc;
+
+ if (dir == NULL)
+ {
+ slapi_log_error(SLAPI_LOG_PLUGIN, repl_plugin_name_cl, "cl5Delete: null directory\n");
+ return CL5_BAD_DATA;
+ }
+
+ if (s_cl5Desc.dbState == CL5_STATE_NONE)
+ {
+ slapi_log_error(SLAPI_LOG_REPL, repl_plugin_name_cl,
+ "cl5Delete: changelog is not initialized\n");
+ return CL5_BAD_STATE;
+ }
+
+ PR_RWLock_Wlock (s_cl5Desc.stLock);
+
+ if (s_cl5Desc.dbState != CL5_STATE_CLOSED)
+ {
+ slapi_log_error(SLAPI_LOG_REPL, repl_plugin_name_cl,
+ "cl5Delete: invalid state - %d\n", s_cl5Desc.dbState);
+ PR_RWLock_Unlock (s_cl5Desc.stLock);
+ return CL5_BAD_STATE;
+ }
+
+ rc = _cl5Delete (dir, PR_TRUE /* remove changelog dir */);
+ if (rc != CL5_SUCCESS)
+ {
+ slapi_log_error(SLAPI_LOG_REPL, repl_plugin_name_cl,
+ "cl5Delete: failed to remove changelog\n");
+ }
+
+ PR_RWLock_Unlock (s_cl5Desc.stLock);
+ return rc;
+}
+
+/* Name: cl5OpenDB
+ Description: opens changelog file for specified file
+ Parameters: replica - replica whose file we wish to open
+ Return: CL5_SUCCESS if successful;
+ CL5_BAD_STATE if the changelog is not initialized;
+ CL5_BAD_DATA - if NULL id is supplied
+ */
+int cl5OpenDB (Object *replica)
+{
+ int rc;
+
+ if (replica == NULL)
+ {
+ slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name_cl, "cl5OpenDB: null replica\n");
+ return CL5_BAD_DATA;
+ }
+
+ if (s_cl5Desc.dbState == CL5_STATE_NONE)
+ {
+ slapi_log_error(SLAPI_LOG_REPL, repl_plugin_name_cl,
+ "cl5OpenDB: changelog is not initialized\n");
+ return CL5_BAD_STATE;
+ }
+
+ /* make sure that changelog stays open while operation is in progress */
+ rc = _cl5AddThread ();
+ if (rc != CL5_SUCCESS)
+ return rc;
+
+ rc = _cl5DBOpenFile (replica, NULL /* file object */, PR_TRUE /* check for duplicates */);
+
+ _cl5RemoveThread ();
+
+ return rc;
+}
+
+/* Name: cl5CloseDB
+ Description: closes changelog file for the specified replica
+ Parameters: replica - replica whose file we wish to close
+ Return: CL5_SUCCESS if successful;
+ CL5_BAD_STATE if the changelog is not initialized;
+ CL5_BAD_DATA - if NULL id is supplied
+ CL5_NOTFOUND - nothing is known about specified database
+ */
+int cl5CloseDB (Object *replica)
+{
+ int rc;
+ Object *obj;
+
+ if (replica == NULL)
+ {
+ slapi_log_error(SLAPI_LOG_REPL, repl_plugin_name_cl, "cl5CloseDB: null replica\n");
+ return CL5_BAD_DATA;
+ }
+
+ if (s_cl5Desc.dbState == CL5_STATE_NONE)
+ {
+ slapi_log_error(SLAPI_LOG_REPL, repl_plugin_name_cl,
+ "cl5CloseDB: changelog is not initialized\n");
+ return CL5_BAD_STATE;
+ }
+
+ /* make sure that changelog is open while operation is in progress */
+ rc = _cl5AddThread ();
+ if (rc != CL5_SUCCESS)
+ return rc;
+
+ rc = _cl5GetDBFile (replica, &obj);
+ if (rc == CL5_SUCCESS)
+ {
+ rc = objset_remove_obj(s_cl5Desc.dbFiles, obj);
+ object_release (obj);
+ }
+ else
+ {
+ Replica *r;
+
+ r = (Replica*)object_get_data (replica);
+ PR_ASSERT (r);
+ slapi_log_error(SLAPI_LOG_REPL, repl_plugin_name_cl,
+ "cl5CloseDB: failed to close file for replica at (%s)\n",
+ slapi_sdn_get_dn (replica_get_root (r)));
+ }
+
+ _cl5RemoveThread ();
+ return rc;
+}
+
+/* Name: cl5DeleteDB
+ Description: asynchronously removes changelog file for the specified replica.
+ The file is physically removed when it is no longer in use.
+ This function is called when a backend is removed or reloaded.
+ Parameters: replica - replica whose file we wish to delete
+ Return: CL5_SUCCESS if successful;
+ CL5_BAD_STATE if the changelog is not initialized;
+ CL5_BAD_DATA - if NULL id is supplied
+ CL5_NOTFOUND - nothing is known about specified database
+ */
+int cl5DeleteDB (Object *replica)
+{
+ Object *obj;
+ int rc;
+
+ if (replica == NULL)
+ {
+ slapi_log_error(SLAPI_LOG_REPL, repl_plugin_name_cl,
+ "cl5DeleteDB: invalid database id\n");
+ return CL5_BAD_DATA;
+ }
+
+ /* changelog is not initialized */
+ if (s_cl5Desc.dbState == CL5_STATE_NONE)
+ {
+ slapi_log_error(SLAPI_LOG_REPL, repl_plugin_name_cl, "cl5DeleteDB: "
+ "changelog is not initialized\n");
+ return CL5_BAD_STATE;
+ }
+
+ /* make sure that changelog stays open while operation is in progress */
+ rc = _cl5AddThread ();
+ if (rc != CL5_SUCCESS)
+ return rc;
+
+ rc = _cl5GetDBFile (replica, &obj);
+ if (rc == CL5_SUCCESS)
+ {
+ _cl5DBDeleteFile (obj);
+ }
+ else
+ {
+ Replica *r = (Replica*)object_get_data (replica);
+ PR_ASSERT (r);
+ slapi_log_error(SLAPI_LOG_REPL, repl_plugin_name_cl, "cl5DeleteDB: "
+ "file for replica at (%s) not found\n",
+ slapi_sdn_get_dn (replica_get_root (r)));
+ }
+
+ _cl5RemoveThread ();
+ return rc;
+}
+
+/* Name: cl5DeleteDBSync
+ Description: The same as cl5DeleteDB except the function does not return
+ until the file is removed.
+*/
+int cl5DeleteDBSync (Object *replica)
+{
+ Object *obj;
+ int rc;
+ CL5DBFile *file;
+ char fName [MAXPATHLEN + 1];
+
+ if (replica == NULL)
+ {
+ slapi_log_error(SLAPI_LOG_REPL, repl_plugin_name_cl,
+ "cl5DeleteDBSync: invalid database id\n");
+ return CL5_BAD_DATA;
+ }
+
+ /* changelog is not initialized */
+ if (s_cl5Desc.dbState == CL5_STATE_NONE)
+ {
+ slapi_log_error(SLAPI_LOG_REPL, repl_plugin_name_cl, "cl5DeleteDBSync: "
+ "changelog is not initialized\n");
+ return CL5_BAD_STATE;
+ }
+
+ /* make sure that changelog stays open while operation is in progress */
+ rc = _cl5AddThread ();
+ if (rc != CL5_SUCCESS)
+ return rc;
+
+ rc = _cl5GetDBFile (replica, &obj);
+ if (rc == CL5_SUCCESS)
+ {
+ file = (CL5DBFile*)object_get_data (obj);
+ PR_ASSERT (file);
+
+ PR_snprintf (fName, MAXPATHLEN, "%s/%s", s_cl5Desc.dbDir, file->name);
+
+ _cl5DBDeleteFile (obj);
+
+ /* wait until the file is gone */
+ while (PR_Access (fName, PR_ACCESS_EXISTS) == PR_SUCCESS)
+ {
+ DS_Sleep (PR_MillisecondsToInterval(100));
+ }
+
+ }
+ else
+ {
+ Replica *r = (Replica*)object_get_data (replica);
+ PR_ASSERT (r);
+ slapi_log_error(SLAPI_LOG_REPL, repl_plugin_name_cl, "cl5DeleteDBSync: "
+ "file for replica at (%s) not found\n",
+ slapi_sdn_get_dn (replica_get_root (r)));
+ }
+
+ _cl5RemoveThread ();
+ return rc;
+}
+
+/* Name: cl5GetUpperBoundRUV
+ Description: retrieves vector for that represnts the upper bound of the changes for a replica.
+ Parameters: r - replica for which the purge vector is requested
+ ruv - contains a copy of the purge ruv if function is successful;
+ unchanged otherwise. It is responsobility pf the caller to free
+ the ruv when it is no longer is in use
+ Return: CL5_SUCCESS if function is successfull
+ CL5_BAD_STATE if the changelog is not initialized;
+ CL5_BAD_DATA - if NULL id is supplied
+ CL5_NOTFOUND, if changelog file for replica is not found
+ */
+int cl5GetUpperBoundRUV (Replica *r, RUV **ruv)
+{
+ int rc;
+ Object *r_obj, *file_obj;
+ CL5DBFile *file;
+
+ if (r == NULL || ruv == NULL)
+ {
+ slapi_log_error(SLAPI_LOG_REPL, repl_plugin_name_cl,
+ "cl5GetUpperBoundRUV: invalid parameters\n");
+ return CL5_BAD_DATA;
+ }
+
+ /* changelog is not initialized */
+ if (s_cl5Desc.dbState == CL5_STATE_NONE)
+ {
+ slapi_log_error(SLAPI_LOG_REPL, repl_plugin_name_cl, "cl5GetUpperBoundRUV: "
+ "changelog is not initialized\n");
+ return CL5_BAD_STATE;
+ }
+
+ /* make sure that changelog stays open while operation is in progress */
+ rc = _cl5AddThread ();
+ if (rc != CL5_SUCCESS)
+ return rc;
+
+ /* create a temporary replica object because of the interface we have */
+ r_obj = object_new (r, NULL);
+
+ rc = _cl5GetDBFile (r_obj, &file_obj);
+ if (rc == CL5_SUCCESS)
+ {
+ file = (CL5DBFile*)object_get_data (file_obj);
+ PR_ASSERT (file && file->maxRUV);
+
+ *ruv = ruv_dup (file->maxRUV);
+
+ object_release (file_obj);
+ }
+
+ object_release (r_obj);
+
+ _cl5RemoveThread ();
+ return rc;
+}
+
+/* Name: cl5Backup
+ Description: makes a backup of the changelog including *.db2,
+ log files, and dbversion. Can be called with the changelog in either open or
+ closed state.
+ Parameters: bkDir - directory to which the data is backed up;
+ created if it does not exist
+ replicas - optional list of replicas whose changes should be backed up;
+ if the list is NULL, entire changelog is backed up.
+ Return: CL5_SUCCESS if function is successful;
+ CL5_BAD_DATA if invalid directory is passed;
+ CL5_BAD_STATE if changelog has not been initialized;
+ CL5_DB_ERROR if db call fails;
+ CL5_SYSTEM_ERROR if NSPR call or file copy failes.
+ */
+int cl5Backup (const char *bkDir, Object **replicas)
+{
+ int rc;
+ char **list = NULL;
+ char **logFile;
+ char srcFile [MAXPATHLEN + 1];
+ char destFile[MAXPATHLEN + 1];
+ DB_TXN *txn = NULL;
+
+ if (bkDir == NULL)
+ {
+ slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name_cl, "cl5Backup: null backup directory\n");
+ return CL5_BAD_DATA;
+ }
+
+ /* changelog must be initialized */
+ if (s_cl5Desc.dbState == CL5_STATE_NONE)
+ {
+ slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name_cl,
+ "cl5Backup: changelog is not initialized\n");
+ return CL5_BAD_STATE;
+ }
+
+ /* make sure that changelog stays open while operation is in progress */
+ rc = _cl5AddThread ();
+ if (rc != CL5_SUCCESS)
+ return rc;
+
+ /* create backup directory if necessary */
+ rc = cl5CreateDirIfNeeded (bkDir);
+ if (rc != CL5_SUCCESS)
+ {
+ slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name_cl,
+ "cl5Backup: failed to create backup directory\n");
+ goto done;
+ }
+
+ /* start transaction to tempararily prevent transaction log
+ from being trimmed
+ */
+ rc = TXN_BEGIN(s_cl5Desc.dbEnv, NULL /*pid*/, &txn, 0);
+ if (rc != 0)
+ {
+ slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name_cl,
+ "cl5Backup: failed to begin transaction; db error - %d %s\n",
+ rc, db_strerror(rc));
+ rc = CL5_DB_ERROR;
+ goto done;
+ }
+
+ slapi_log_error(SLAPI_LOG_PLUGIN, repl_plugin_name_cl,
+ "cl5Backup: starting changelog backup from %s to %s ...\n", s_cl5Desc.dbDir, bkDir);
+
+ /* The following files are backed up: *.<dbext>, log files, dbversion file */
+
+ /* copy db file */
+ /* ONREPL currently, list of replicas is ignored because db code can't handle
+ discrepancy between transaction log and present files; should be fixed before 5.0 ships */
+ rc = _cl5CopyDBFiles (s_cl5Desc.dbDir, bkDir, replicas);
+ if (rc != CL5_SUCCESS)
+ {
+ slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name_cl,
+ "cl5Backup : failed to copy database files from %s to %s\n", s_cl5Desc.dbDir, bkDir);
+ goto done;
+ }
+
+ /* copy db log files */
+ rc = LOG_ARCHIVE(s_cl5Desc.dbEnv, &list, DB_ARCH_LOG, malloc);
+ if (rc != 0)
+ {
+ slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name_cl,
+ "cl5Backup: failed to get list of log files; db error - %d %s\n",
+ rc, db_strerror(rc));
+ rc = CL5_SYSTEM_ERROR;
+ goto done;
+ }
+
+ if (list)
+ {
+ logFile = list;
+ while (*logFile)
+ {
+ PR_snprintf(srcFile, MAXPATHLEN, "%s/%s", s_cl5Desc.dbDir, *logFile);
+ PR_snprintf(destFile, MAXPATHLEN, "%s/%s", bkDir, *logFile);
+ rc = copyfile(srcFile, destFile, 0, FILE_CREATE_MODE);
+ if (rc != 0)
+ {
+ slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name_cl,
+ "cl5Backup: failed to copy %s\n", *logFile);
+ rc = CL5_SYSTEM_ERROR;
+ goto done;
+ }
+
+ logFile ++;
+ }
+
+ free(list);
+ }
+
+ /* now, copy the version file */
+ PR_snprintf(srcFile, MAXPATHLEN, "%s/%s", s_cl5Desc.dbDir, VERSION_FILE);
+ PR_snprintf(destFile, MAXPATHLEN, "%s/%s", bkDir, VERSION_FILE);
+ rc = copyfile(srcFile, destFile, 0, FILE_CREATE_MODE);
+ if (rc != 0)
+ {
+ slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name_cl,
+ "cl5Backup: failed to copy %s\n", VERSION_FILE);
+ rc = CL5_SYSTEM_ERROR;
+ goto done;
+ }
+
+ rc = CL5_SUCCESS;
+ slapi_log_error(SLAPI_LOG_PLUGIN, repl_plugin_name_cl,
+ "cl5Backup: changelog backup is finished \n");
+done:;
+ if (txn && TXN_ABORT (txn) != 0)
+ {
+ slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name_cl,
+ "cl5Backup: failed to abort transaction; db error - %d %s\n",
+ rc, db_strerror(rc));
+ rc = CL5_DB_ERROR;
+ }
+
+ _cl5RemoveThread ();
+
+ return rc;
+}
+
+/* Name: cl5Restore
+ Description: restores changelog from the backed up copy. Changelog must be ibnitalized and closed.
+ Parameters: clDir - changelog dir
+ bkDir - directory that contains the backup
+ replicas - optional list of replicas whose changes should be recovered;
+ if the list is NULL, entire changelog is recovered.
+ Return: CL5_SUCCESS if function is successfull;
+ CL5_BAD_DATA if invalid parameter is passed;
+ CL5_BAD_STATE if changelog is open or not initialized;
+ CL5_DB_ERROR if db call fails;
+ CL5_SYSTEM_ERROR if NSPR call of file copy fails
+ */
+int cl5Restore (const char *clDir, const char *bkDir, Object **replicas)
+{
+ int rc;
+ char srcFile[MAXPATHLEN + 1];
+ char destFile[MAXPATHLEN + 1];
+ PRDir *prDir;
+ PRDirEntry *prDirEntry;
+ int seenLog = 0; /* Tells us if we restored any logfiles */
+
+ if (clDir == NULL || bkDir == NULL)
+ {
+ slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name_cl, "cl5Restore: null parameter\n");
+ return CL5_BAD_DATA;
+ }
+
+ if (s_cl5Desc.dbState == CL5_STATE_NONE)
+ {
+ slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name_cl,
+ "cl5Restore: changelog is not initialized\n");
+ return CL5_BAD_STATE;
+ }
+
+ /* prevent state change while recovery is in progress */
+ PR_RWLock_Wlock (s_cl5Desc.stLock);
+
+ if (s_cl5Desc.dbState != CL5_STATE_CLOSED)
+ {
+ slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name_cl,
+ "cl5Restore: changelog must be closed\n");
+ PR_RWLock_Unlock (s_cl5Desc.stLock);
+ return CL5_BAD_STATE;
+ }
+
+ slapi_log_error(SLAPI_LOG_PLUGIN, repl_plugin_name_cl,
+ "cl5Restore: starting changelog recovery from %s to %s ...\n", bkDir, clDir);
+
+ /* delete current changelog content */
+ rc = _cl5Delete (clDir, PR_FALSE);
+ if (rc != CL5_SUCCESS)
+ {
+ slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name_cl,
+ "cl5Restore: failed to remove changelog\n");
+ goto done;
+ }
+
+ /* We copy the files over from the staging area */
+ prDir = PR_OpenDir(bkDir);
+ if (prDir == NULL)
+ {
+ rc = CL5_SYSTEM_ERROR;
+ slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name_cl,
+ "cl5Restore: unable to access backup directory %s; NSPR error - %d\n",
+ bkDir, PR_GetError ());
+ goto done;
+ }
+
+ while (NULL != (prDirEntry = PR_ReadDir(prDir, PR_SKIP_DOT | PR_SKIP_DOT_DOT)))
+ {
+ if (NULL == prDirEntry->name) /* NSPR doesn't behave like the docs say it should */
+ {
+ break;
+ }
+
+ /* Log files have names of the form "log.xxxxx". We detect these by looking for
+ the prefix "log." and the lack of the ".<dbext>" suffix */
+ seenLog |= _cl5IsLogFile(prDirEntry->name);
+
+ /* ONREPL currently, list of replicas is ignored because db code can't handle discrepancy
+ between transaction log and present files; this should change before 5.0 ships */
+ PR_snprintf(destFile, MAXPATHLEN, "%s/%s", clDir, prDirEntry->name);
+ PR_snprintf(srcFile, MAXPATHLEN, "%s/%s", bkDir, prDirEntry->name);
+ rc = copyfile(srcFile, destFile, 0, FILE_CREATE_MODE);
+ if (rc != 0)
+ {
+ rc = CL5_SYSTEM_ERROR;
+ slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name_cl,
+ "cl5Restore: failed to copy %s\n", prDirEntry->name);
+ PR_CloseDir(prDir);
+ goto done;
+ }
+ }
+
+ PR_CloseDir(prDir);
+
+ /* now open and close changelog to create all necessary files */
+ if (seenLog)
+ rc = _cl5Open (clDir, NULL, CL5_OPEN_RESTORE_RECOVER);
+ else
+ rc = _cl5Open (clDir, NULL, CL5_OPEN_RESTORE);
+
+ if (rc == CL5_SUCCESS)
+ {
+ _cl5Close ();
+
+ slapi_log_error(SLAPI_LOG_PLUGIN, repl_plugin_name_cl,
+ "cl5Restore: changelog recovery is finished \n");
+ }
+ else
+ {
+ slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name_cl,
+ "cl5Restore: failed open changelog after recovery\n");
+ }
+
+done:;
+ PR_RWLock_Unlock (s_cl5Desc.stLock);
+ return rc;
+}
+
+/* Name: cl5ExportLDIF
+ Description: dumps changelog to an LDIF file; changelog can be open or closed.
+ Parameters: clDir - changelog dir
+ ldifFile - full path to ldif file to write
+ replicas - optional list of replicas whose changes should be exported;
+ if the list is NULL, entire changelog is exported.
+ Return: CL5_SUCCESS if function is successfull;
+ CL5_BAD_DATA if invalid parameter is passed;
+ CL5_BAD_STATE if changelog is not initialized;
+ CL5_DB_ERROR if db api fails;
+ CL5_SYSTEM_ERROR if NSPR call fails;
+ CL5_MEMORY_ERROR if memory allocation fials.
+ */
+int cl5ExportLDIF (const char *ldifFile, Object **replicas)
+{
+ int i;
+ int rc;
+ PRFileDesc *prFile = NULL;
+ Object *obj;
+
+ if (ldifFile == NULL)
+ {
+ slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name_cl,
+ "cl5ExportLDIF: null ldif file name\n");
+ return CL5_BAD_DATA;
+ }
+
+ if (s_cl5Desc.dbState == CL5_STATE_NONE)
+ {
+ slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name_cl,
+ "cl5ExportLDIF: changelog is not initialized\n");
+ return CL5_BAD_STATE;
+ }
+
+ /* make sure that changelog is open while operation is in progress */
+ rc = _cl5AddThread ();
+ if (rc != CL5_SUCCESS)
+ return rc;
+
+ prFile = PR_Open (ldifFile, PR_WRONLY | PR_CREATE_FILE | PR_TRUNCATE, 0600);
+ if (prFile == NULL)
+ {
+ slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name_cl,
+ "cl5ExportLDIF: failed to open (%s) file; NSPR error - %d\n",
+ ldifFile, PR_GetError ());
+ rc = CL5_SYSTEM_ERROR;
+ goto done;
+ }
+
+ slapi_log_error(SLAPI_LOG_PLUGIN, repl_plugin_name_cl,
+ "cl5ExportLDIF: starting changelog export to (%s) ...\n", ldifFile);
+
+ if (replicas) /* export only selected files */
+ {
+ for (i = 0; replicas[i]; i++)
+ {
+ rc = _cl5GetDBFile (replicas[i], &obj);
+ if (rc == CL5_SUCCESS)
+ {
+ rc = _cl5ExportFile (prFile, obj);
+ object_release (obj);
+ }
+ else
+ {
+ Replica *r = (Replica*)object_get_data (replicas[i]);
+
+ PR_ASSERT (r);
+
+ slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name_cl, "cl5ExportLDIF: "
+ "failed to locate changelog file for replica at (%s)\n",
+ slapi_sdn_get_dn (replica_get_root (r)));
+ }
+ }
+ }
+ else /* export all files */
+ {
+ for (obj = objset_first_obj(s_cl5Desc.dbFiles); obj;
+ obj = objset_next_obj(s_cl5Desc.dbFiles, obj))
+ {
+ rc = _cl5ExportFile (prFile, obj);
+ object_release (obj);
+ }
+ }
+
+ rc = CL5_SUCCESS;
+done:;
+
+ _cl5RemoveThread ();
+
+ if (rc == CL5_SUCCESS)
+ slapi_log_error(SLAPI_LOG_PLUGIN, repl_plugin_name_cl,
+ "cl5ExportLDIF: changelog export is finished.\n");
+
+ if (prFile)
+ PR_Close (prFile);
+
+ return rc;
+}
+
+/* Name: cl5ImportLDIF
+ Description: imports ldif file into changelog; changelog must be in the closed state
+ Parameters: clDir - changelog dir
+ ldifFile - absolute path to the ldif file to import
+ replicas - optional list of replicas whose data should be imported;
+ if the list is NULL, all data in the file is imported.
+ Return: CL5_SUCCESS if function is successfull;
+ CL5_BAD_DATA if invalid parameter is passed;
+ CL5_BAD_STATE if changelog is open or not inititalized;
+ CL5_DB_ERROR if db api fails;
+ CL5_SYSTEM_ERROR if NSPR call fails;
+ CL5_MEMORY_ERROR if memory allocation fials.
+ */
+int cl5ImportLDIF (const char *clDir, const char *ldifFile, Object **replicas)
+{
+ FILE *file;
+ int rc;
+ char *buff;
+ int lineno = 0;
+ slapi_operation_parameters op;
+ Object *replica = NULL;
+ char *replGen = NULL;
+
+ /* validate params */
+ if (ldifFile == NULL)
+ {
+ slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name_cl,
+ "cl5ImportLDIF: null ldif file name\n");
+ return CL5_BAD_DATA;
+ }
+
+ if (s_cl5Desc.dbState == CL5_STATE_NONE)
+ {
+ slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name_cl,
+ "cl5ImportLDIF: changelog is not initialized\n");
+ return CL5_BAD_STATE;
+ }
+
+ /* make sure that nobody change changelog state while import is in progress */
+ PR_RWLock_Wlock (s_cl5Desc.stLock);
+
+ /* make sure changelog is closed */
+ if (s_cl5Desc.dbState != CL5_STATE_CLOSED)
+ {
+ slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name_cl,
+ "cl5ImportLDIF: invalid state - %d \n", s_cl5Desc.dbState);
+
+ PR_RWLock_Unlock (s_cl5Desc.stLock);
+ return CL5_BAD_STATE;
+ }
+
+ /* open LDIF file */
+ file = fopen (ldifFile, "r"); /* XXXggood Does fopen reliably work if > 255 files open? */
+ if (file == NULL)
+ {
+ slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name_cl,
+ "cl5ImportLDIF: failed to open (%s) ldif file; system error - %d\n",
+ ldifFile, errno);
+ rc = CL5_SYSTEM_ERROR;
+ goto done;
+ }
+
+ /* remove changelog */
+ rc = _cl5Delete (clDir, PR_FALSE);
+ if (rc != CL5_SUCCESS)
+ {
+ slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name_cl,
+ "cl5ImportLDIF: failed to remove changelog\n");
+ goto done;
+ }
+
+ /* open changelog */
+ rc = _cl5Open (clDir, NULL, CL5_OPEN_LDIF2CL);
+ if (rc != CL5_SUCCESS)
+ {
+ slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name_cl,
+ "cl5ImportLDIF: failed to open changelog\n");
+ goto done;
+ }
+
+ /* read entries and write them to changelog */
+ while ((buff = ldif_get_entry( file, &lineno )) != NULL)
+ {
+ rc = _cl5LDIF2Operation (buff, &op, &replGen);
+ slapi_ch_free ((void**)&buff);
+ if (rc != CL5_SUCCESS)
+ {
+ slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name_cl,
+ "cl5ImportLDIF: failed to convert LDIF fragment to LDAP operation; "
+ "end of fragment line number - %d\n", lineno);
+ goto done;
+ }
+
+ /* if we perform selective import, check if the operation should be wriiten to changelog */
+ replica = _cl5GetReplica (&op, replGen);
+ if (replica == NULL)
+ {
+ slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name_cl,
+ "cl5ImportLDIF: failed to locate replica for target dn (%s) and "
+ "replica generation %s\n", op.target_address.dn, replGen);
+
+ slapi_ch_free ((void**)&replGen);
+ operation_parameters_done (&op);
+ goto done;
+ }
+
+ if (!replicas || _cl5ReplicaInList (replica, replicas))
+ {
+ /* write operation creates the file if it does not exist */
+ rc = _cl5WriteOperation (replica_get_name ((Replica*)object_get_data(replica)),
+ replGen, &op, 1);
+ if (rc != CL5_SUCCESS)
+ {
+ slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name_cl,
+ "cl5ImportLDIF: failed to write operation to the changelog\n");
+ object_release (replica);
+ slapi_ch_free ((void**)&replGen);
+ operation_parameters_done (&op);
+ goto done;
+ }
+ }
+
+ object_release (replica);
+ slapi_ch_free ((void**)&replGen);
+ operation_parameters_done (&op);
+ }
+
+done:;
+ _cl5Close ();
+ PR_RWLock_Unlock (s_cl5Desc.stLock);
+ return rc;
+}
+
+/* Name: cl5GetState
+ Description: returns database state
+ Parameters: none
+ Return: changelog state
+ */
+int cl5GetState ()
+{
+ return s_cl5Desc.dbState;
+}
+
+/* Name: cl5ConfigTrimming
+ Description: sets changelog trimming parameters; changelog must be open.
+ Parameters: maxEntries - maximum number of entries in the chnagelog (in all files);
+ maxAge - maximum entry age;
+ Return: CL5_SUCCESS if successful;
+ CL5_BAD_STATE if changelog is not open
+ */
+int cl5ConfigTrimming (int maxEntries, const char *maxAge)
+{
+ if (s_cl5Desc.dbState == CL5_STATE_NONE)
+ {
+ slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name_cl,
+ "cl5ConfigTrimming: changelog is not initialized\n");
+ return CL5_BAD_STATE;
+ }
+
+ /* make sure changelog is not closed while trimming configuration
+ is updated.*/
+ _cl5AddThread ();
+
+ PR_Lock (s_cl5Desc.dbTrim.lock);
+
+ if (maxAge)
+ {
+ /* don't ignore this argument */
+ if (strcmp (maxAge, CL5_STR_IGNORE) != 0)
+ {
+ s_cl5Desc.dbTrim.maxAge = age_str2time (maxAge);
+ }
+ }
+ else
+ {
+ /* unlimited */
+ s_cl5Desc.dbTrim.maxAge = 0;
+ }
+
+ if (maxEntries != CL5_NUM_IGNORE)
+ {
+ s_cl5Desc.dbTrim.maxEntries = maxEntries;
+ }
+
+ PR_Unlock (s_cl5Desc.dbTrim.lock);
+
+ _cl5RemoveThread ();
+
+ return CL5_SUCCESS;
+}
+
+/* Name: cl5GetOperation
+ Description: retireves operation specified by its csn and databaseid
+ Parameters: op - must contain csn and databaseid; the rest of data is
+ filled if function is successfull
+ Return: CL5_SUCCESS if function is successfull;
+ CL5_BAD_DATA if invalid op is passed;
+ CL5_BAD_STATE if db has not been initialized;
+ CL5_NOTFOUND if entry was not found;
+ CL5_DB_ERROR if any other db error occured;
+ CL5_BADFORMAT if db data format does not match entry format.
+ */
+int cl5GetOperation (Object *replica, slapi_operation_parameters *op)
+{
+ int rc;
+ char *agmt_name;
+
+ agmt_name = get_thread_private_agmtname();
+
+ if (replica == NULL)
+ {
+ slapi_log_error(SLAPI_LOG_REPL, repl_plugin_name_cl, "cl5GetOperation: NULL replica\n");
+ return CL5_BAD_DATA;
+ }
+
+ if (op == NULL)
+ {
+ slapi_log_error(SLAPI_LOG_REPL, repl_plugin_name_cl, "cl5GetOperation: NULL operation\n");
+ return CL5_BAD_DATA;
+ }
+
+ if (op->csn == NULL)
+ {
+ slapi_log_error(SLAPI_LOG_REPL, repl_plugin_name_cl, "%s: cl5GetOperation: operation contains no CSN\n", agmt_name);
+ return CL5_BAD_DATA;
+ }
+
+ if (s_cl5Desc.dbState == CL5_STATE_NONE)
+ {
+ slapi_log_error(SLAPI_LOG_REPL, repl_plugin_name_cl,
+ "%s: cl5GetOperation: changelog is not initialized\n", agmt_name);
+ return CL5_BAD_STATE;
+ }
+
+ /* make sure that changelog is open while operation is in progress */
+ rc = _cl5AddThread ();
+ if (rc != CL5_SUCCESS)
+ return rc;
+
+ rc = _cl5GetOperation (replica, op);
+
+ _cl5RemoveThread ();
+
+ return rc;
+}
+
+/* Name: cl5GetFirstOperation
+ Description: retrieves first operation for a particular database
+ replica - replica for which the operation should be retrieved.
+ Parameters: op - buffer to store the operation;
+ iterator - to be passed to the call to cl5GetNextOperation
+ Return: CL5_SUCCESS, if successful
+ CL5_BADDATA, if operation is NULL
+ CL5_BAD_STATE, if changelog is not open
+ CL5_DB_ERROR, if db call fails
+ */
+int cl5GetFirstOperation (Object *replica, slapi_operation_parameters *op, void **iterator)
+{
+ int rc;
+ CL5Entry entry;
+ Object *obj;
+ char *agmt_name;
+
+ if (replica == NULL || op == NULL || iterator == NULL)
+ {
+ slapi_log_error(SLAPI_LOG_REPL, repl_plugin_name_cl,
+ "cl5GetFirstOperation: invalid argument\n");
+ return CL5_BAD_DATA;
+ }
+
+ *iterator = NULL;
+
+ if (s_cl5Desc.dbState == CL5_STATE_NONE)
+ {
+ agmt_name = get_thread_private_agmtname();
+ slapi_log_error(SLAPI_LOG_REPL, repl_plugin_name_cl,
+ "%s: cl5GetFirstOperation: changelog is not initialized\n", agmt_name);
+ return CL5_BAD_STATE;
+ }
+
+ /* make sure that changelog stays open while operation is in progress */
+ rc = _cl5AddThread ();
+ if (rc != CL5_SUCCESS)
+ return rc;
+
+ rc = _cl5GetDBFile (replica, &obj);
+ if (rc != CL5_SUCCESS)
+ {
+ _cl5RemoveThread ();
+ return rc;
+ }
+
+ entry.op = op;
+ /* Callers of this function should cl5_operation_parameters_done(op) */
+ rc = _cl5GetFirstEntry (obj, &entry, iterator, NULL);
+ object_release (obj);
+
+ _cl5RemoveThread ();
+
+ return rc;
+}
+
+/* Name: cl5GetNextOperation
+ Description: retrieves the next op from the changelog as defined by the iterator;
+ changelog must be open.
+ Parameters: op - returned operation, if function is successful
+ iterator - in: identifies op to retrieve; out: identifies next op
+ Return: CL5_SUCCESS, if successful
+ CL5_BADDATA, if op is NULL
+ CL5_BAD_STATE, if changelog is not open
+ CL5_NOTFOUND, empty changelog
+ CL5_DB_ERROR, if db call fails
+ */
+int cl5GetNextOperation (slapi_operation_parameters *op, void *iterator)
+{
+ CL5Entry entry;
+
+ if (op == NULL || iterator == NULL || !_cl5IsValidIterator (iterator))
+ {
+ slapi_log_error(SLAPI_LOG_REPL, repl_plugin_name_cl,
+ "cl5GetNextOperation: invalid argument\n");
+ return CL5_BAD_DATA;
+ }
+
+ if (s_cl5Desc.dbState != CL5_STATE_OPEN)
+ {
+ slapi_log_error(SLAPI_LOG_REPL, repl_plugin_name_cl,
+ "cl5GetNextOperation: changelog is not open\n");
+ return CL5_BAD_STATE;
+ }
+
+ /* we don't need to increment thread count since cl5GetFirstOperation
+ locked the file through which we are iterating */
+ entry.op = op;
+ /* Callers of this function should cl5_operation_parameters_done(op) */
+ return _cl5GetNextEntry (&entry, iterator);
+}
+
+/* Name: cl5DestroyIterator
+ Description: destroys iterator once iteration through changelog is done
+ Parameters: iterator - iterator to destroy
+ Return: none
+ */
+void cl5DestroyIterator (void *iterator)
+{
+ CL5Iterator *it = (CL5Iterator*)iterator;
+
+ if (it == NULL)
+ return;
+
+ /* close cursor */
+ if (it->cursor)
+ it->cursor->c_close (it->cursor);
+
+ if (it->file)
+ object_release (it->file);
+
+ slapi_ch_free ((void**)&it);
+}
+
+/* Name: cl5WriteOperation
+ Description: writes operation to changelog
+ Parameters: replName - name of the replica to which operation applies
+ replGen - replica generation for the operation
+ !!!Note that we pass name and generation rather than
+ replica object since generation can change while operation
+ is in progress (if the data is reloaded). !!!
+ op - operation to write
+ local - this is a non-replicated operation
+ Return: CL5_SUCCESS if function is successfull;
+ CL5_BAD_DATA if invalid op is passed;
+ CL5_BAD_STATE if db has not been initialized;
+ CL5_MEMORY_ERROR if memory allocation failed;
+ CL5_DB_ERROR if any other db error occured;
+ */
+int cl5WriteOperation(const char *replName, const char *replGen,
+ const slapi_operation_parameters *op, PRBool local)
+{
+ int rc;
+
+ if (op == NULL)
+ {
+ slapi_log_error(SLAPI_LOG_REPL, repl_plugin_name_cl,
+ "cl5WriteOperation: NULL operation passed\n");
+ return CL5_BAD_DATA;
+ }
+
+ if (!IsValidOperation (op))
+ {
+ return CL5_BAD_DATA;
+ }
+
+
+ if (s_cl5Desc.dbState == CL5_STATE_NONE)
+ {
+ slapi_log_error(SLAPI_LOG_REPL, repl_plugin_name_cl,
+ "cl5WriteOperation: changelog is not initialized\n");
+ return CL5_BAD_STATE;
+ }
+
+ /* make sure that changelog is open while operation is in progress */
+ rc = _cl5AddThread ();
+ if (rc != CL5_SUCCESS)
+ return rc;
+
+ rc = _cl5WriteOperation(replName, replGen, op, local);
+
+ /* update the upper bound ruv vector */
+ if (rc == CL5_SUCCESS)
+ {
+ Object *file_obj = NULL;
+
+ if ( _cl5GetDBFileByReplicaName (replName, replGen, &file_obj) == CL5_SUCCESS) {
+ rc = _cl5UpdateRUV (file_obj, op->csn, PR_FALSE, PR_FALSE);
+ object_release (file_obj);
+ }
+
+ }
+
+ _cl5RemoveThread ();
+
+ return rc;
+}
+
+/* Name: cl5CreateReplayIterator
+ Description: creates an iterator that allows to retireve changes that should
+ to be sent to the consumer identified by ruv. The iteration is peformed by
+ repeated calls to cl5GetNextOperationToReplay.
+ Parameters: replica - replica whose data we wish to iterate;
+ ruv - consumer ruv;
+ iterator - iterator to be passed to cl5GetNextOperationToReplay call
+ Return: CL5_SUCCESS, if function is successfull;
+ CL5_MISSING_DATA, if data that should be in the changelog is missing
+ CL5_PURGED_DATA, if some data that consumer needs has been purged.
+ Note that the iterator can be non null if the supplier contains
+ some data that needs to be sent to the consumer
+ CL5_NOTFOUND if the consumer is up to data with respect to the supplier
+ CL5_BAD_DATA if invalid parameter is passed;
+ CL5_BAD_STATE if db has not been open;
+ CL5_DB_ERROR if any other db error occured;
+ CL5_MEMORY_ERROR if memory allocation fails.
+ Algorithm: Build a list of csns from consumer's and supplier's ruv. For each element
+ of the consumer's ruv put max csn into the csn list. For each element
+ of the supplier's ruv not in the consumer's ruv put min csn from the
+ supplier's ruv into the list. The list contains, for each known replica,
+ the starting point for changes to be sent to the consumer.
+ Sort the list in accending order.
+ Build a hash which contains, for each known replica, whether the
+ supplier can bring the consumer up to data with respect to that replica.
+ The hash is used to decide whether a change can be sent to the consumer
+ Find the replica with the smallest csn in the list for which
+ we can bring the consumer up to date.
+ Position the db cursor on the change entry that corresponds to this csn.
+ Hash entries are created for each replica traversed so far. sendChanges
+ flag is set to FALSE for all repolicas except the last traversed.
+
+ */
+int cl5CreateReplayIterator (Private_Repl_Protocol *prp, const RUV *consumerRuv,
+ CL5ReplayIterator **iterator)
+{
+ int rc;
+ Object *replica;
+ Object *obj = NULL;
+
+ replica = prp->replica_object;
+ if (replica == NULL || consumerRuv == NULL || iterator == NULL)
+ {
+ slapi_log_error(SLAPI_LOG_REPL, repl_plugin_name_cl,
+ "cl5CreateReplayIterator: invalid parameter\n");
+ return CL5_BAD_DATA;
+ }
+
+ *iterator = NULL;
+
+ if (s_cl5Desc.dbState == CL5_STATE_NONE)
+ {
+ slapi_log_error(SLAPI_LOG_REPL, repl_plugin_name_cl,
+ "cl5CreateReplayIterator: changelog is not initialized\n");
+ return CL5_BAD_STATE;
+ }
+
+ /* make sure that changelog is open while operation is in progress */
+ rc = _cl5AddThread ();
+ if (rc != CL5_SUCCESS ) return rc;
+
+
+ rc = _cl5GetDBFile (replica, &obj);
+ if (rc == CL5_SUCCESS)
+ {
+ /* iterate through the ruv in csn order to find first master for which
+ we can replay changes */
+ ReplicaId consumerRID = agmt_get_consumer_rid ( prp->agmt, prp->conn );
+ rc = _cl5PositionCursorForReplay (consumerRID, consumerRuv, replica, obj, iterator);
+ if (rc != CL5_SUCCESS)
+ {
+ if (obj)
+ object_release (obj);
+ }
+ }
+
+ _cl5RemoveThread ();
+
+ return rc;
+}
+
+/* Name: cl5GetNextOperationToReplay
+ Description: retrieves next operation to be sent to a particular consumer and
+ that was created on a particular master. Consumer and master info
+ is encoded in the iterator parameter that must be created by call
+ to cl5CreateReplayIterator.
+ Parameters: iterator - iterator that identifies next entry to retrieve;
+ op - operation retrieved if function is successful
+ Return: CL5_SUCCESS if function is successfull;
+ CL5_BAD_DATA if invalid parameter is passed;
+ CL5_NOTFOUND if end of iteration list is reached
+ CL5_DB_ERROR if any other db error occured;
+ CL5_BADFORMAT if data in db is of unrecognized format;
+ CL5_MEMORY_ERROR if memory allocation fails.
+ Algorithm: Iterate through changelog entries until a change is found that
+ originated at the replica for which we are sending changes
+ (based on the information in the iteration hash) and
+ whose csn is larger than the csn already seen by the consumer
+ If change originated at the replica not in the hash,
+ determine whether we should send changes originated at the replica
+ and add replica entry into the hash. We can send the changes for
+ the replica if the current csn is smaller or equal to the csn
+ in the consumer's ruv (if present) or if it is equal to the min
+ csn in the supplier's ruv.
+ */
+int
+cl5GetNextOperationToReplay (CL5ReplayIterator *iterator, CL5Entry *entry)
+{
+ CSN *csn;
+ char *key, *data;
+ size_t keylen, datalen;
+ char *agmt_name;
+ int rc = 0;
+
+ agmt_name = get_thread_private_agmtname();
+
+ if (entry == NULL)
+ {
+ slapi_log_error(SLAPI_LOG_REPL, repl_plugin_name_cl,
+ "%s: cl5GetNextOperationToReplay: invalid parameter passed\n", agmt_name);
+ return CL5_BAD_DATA;
+ }
+
+ rc = clcache_get_next_change (iterator->clcache, (void **)&key, &keylen, (void **)&data, &datalen, &csn);
+
+ if (rc == DB_NOTFOUND)
+ {
+ /*
+ * Abort means we've figured out that we've passed the replica Min CSN,
+ * so we should stop looping through the changelog
+ */
+ return CL5_NOTFOUND;
+ }
+
+ if (rc != 0)
+ {
+ slapi_log_error(SLAPI_LOG_FATAL, NULL, "%s: cl5GetNextOperationToReplay: "
+ "failed to read next entry; DB error %d\n", agmt_name, rc);
+ return CL5_DB_ERROR;
+ }
+
+ /* there is an entry we should return */
+ /* Callers of this function should cl5_operation_parameters_done(op) */
+ if ( 0 != cl5DBData2Entry ( data, datalen, entry ) )
+ {
+ slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name_cl,
+ "%s: cl5GetNextOperationToReplay: failed to format entry rc=%d\n", agmt_name, rc);
+ return rc;
+ }
+
+ return CL5_SUCCESS;
+}
+
+/* Name: cl5DestroyReplayIterator
+ Description: destorys iterator
+ Parameters: iterator - iterator to destory
+ Return: none
+ */
+void cl5DestroyReplayIterator (CL5ReplayIterator **iterator)
+{
+ if (iterator == NULL)
+ {
+ slapi_log_error(SLAPI_LOG_REPL, repl_plugin_name_cl,
+ "cl5DestroyReplayIterator: invalid iterartor passed\n");
+ return;
+ }
+
+ clcache_return_buffer ( &(*iterator)->clcache );
+
+ if ((*iterator)->fileObj)
+ object_release ((*iterator)->fileObj);
+
+ /* release supplier's ruv */
+ if ((*iterator)->supplierRuvObj)
+ object_release ((*iterator)->supplierRuvObj);
+
+ slapi_ch_free ((void **)iterator);
+}
+
+/* Name: cl5DeleteOnClose
+ Description: marks changelog for deletion when it is closed
+ Parameters: flag; if flag = 1 then delete else don't
+ Return: none
+ */
+void cl5DeleteOnClose (PRBool rm)
+{
+ s_cl5Desc.dbRmOnClose = rm;
+}
+
+/* Name: cl5GetDir
+ Description: returns changelog directory
+ Parameters: none
+ Return: copy of the directory; caller needs to free the string
+ */
+ char *cl5GetDir ()
+{
+ if (s_cl5Desc.dbDir == NULL)
+ {
+ return NULL;
+ }
+ else
+ {
+ return slapi_ch_strdup (s_cl5Desc.dbDir);
+ }
+}
+
+/* Name: cl5Exist
+ Description: checks if a changelog exists in the specified directory;
+ We consider changelog to exist if it contains the dbversion file.
+ Parameters: clDir - directory to check
+ Return: 1 - if changelog exists; 0 - otherwise
+ */
+PRBool cl5Exist (const char *clDir)
+{
+ char fName [MAXPATHLEN + 1];
+ int rc;
+
+ PR_snprintf (fName, MAXPATHLEN, "%s/%s", clDir, VERSION_FILE);
+ rc = PR_Access (fName, PR_ACCESS_EXISTS);
+
+ return (rc == PR_SUCCESS);
+}
+
+/* Name: cl5GetOperationCount
+ Description: returns number of entries in the changelog. The changelog must be
+ open for the value to be meaningful.
+ Parameters: replica - optional parameter that specifies the replica whose operations
+ we wish to count; if NULL all changelog entries are counted
+ Return: number of entries in the changelog
+ */
+
+int cl5GetOperationCount (Object *replica)
+{
+ Object *obj;
+ CL5DBFile *file;
+ int count = 0;
+ int rc;
+
+ if (s_cl5Desc.dbState == CL5_STATE_NONE)
+ {
+ slapi_log_error(SLAPI_LOG_REPL, repl_plugin_name_cl,
+ "cl5GetOperationCount: changelog is not initialized\n");
+ return -1;
+ }
+
+ /* make sure that changelog is open while operation is in progress */
+ rc = _cl5AddThread ();
+ if (rc != CL5_SUCCESS)
+ return -1;
+
+ if (replica == NULL) /* compute total entry count */
+ {
+ obj = objset_first_obj (s_cl5Desc.dbFiles);
+ while (obj)
+ {
+ file = (CL5DBFile*)object_get_data (obj);
+ PR_ASSERT (file);
+ count += file->entryCount;
+ obj = objset_next_obj (s_cl5Desc.dbFiles, obj);
+ }
+ }
+ else /* return count for particular db */
+ {
+ /* select correct db file */
+ rc = _cl5GetDBFile (replica, &obj);
+ if (rc == CL5_SUCCESS)
+ {
+ file = (CL5DBFile*)object_get_data (obj);
+ PR_ASSERT (file);
+
+ count = file->entryCount;
+ object_release (obj);
+ }
+ else
+ {
+ count = 0;
+ }
+ }
+
+ _cl5RemoveThread ();
+ return count;
+}
+
+/***** Helper Functions *****/
+
+/* this call happens under state lock */
+static int _cl5Open (const char *dir, const CL5DBConfig *config, CL5OpenMode openMode)
+{
+ int rc;
+ PRBool didRecovery;
+
+ PR_ASSERT (dir);
+
+ /* setup db configuration parameters */
+ if (config)
+ {
+ _cl5SetDBConfig (config);
+ }
+ else
+ {
+ _cl5SetDefaultDBConfig ();
+ }
+
+ /* initialize trimming */
+ rc = _cl5TrimInit ();
+ if (rc != CL5_SUCCESS)
+ {
+ slapi_log_error(SLAPI_LOG_REPL, repl_plugin_name_cl,
+ "_cl5Open: failed to initialize trimming\n");
+ goto done;
+ }
+
+ /* create the changelog directory if it does not exist */
+ rc = cl5CreateDirIfNeeded (dir);
+ if (rc != CL5_SUCCESS)
+ {
+ slapi_log_error(SLAPI_LOG_REPL, repl_plugin_name_cl,
+ "_cl5Open: failed to create changelog directory (%s)\n", dir);
+ goto done;
+ }
+
+ s_cl5Desc.dbDir = slapi_ch_strdup (dir);
+
+ /* check database version */
+ rc = _cl5CheckDBVersion ();
+ if (rc != CL5_SUCCESS)
+ {
+ slapi_log_error(SLAPI_LOG_REPL, repl_plugin_name_cl, "_cl5Open: invalid db version\n");
+ goto done;
+ }
+
+ s_cl5Desc.dbOpenMode = openMode;
+
+ /* initialize db environment */
+ rc = _cl5AppInit (&didRecovery);
+ if (rc != CL5_SUCCESS)
+ {
+ slapi_log_error(SLAPI_LOG_REPL, repl_plugin_name_cl,
+ "_cl5Open: failed to initialize db environment\n");
+ goto done;
+ }
+
+ /* open database files */
+ rc = _cl5DBOpen (!didRecovery);
+ if (rc != CL5_SUCCESS)
+ {
+ slapi_log_error(SLAPI_LOG_REPL, repl_plugin_name_cl,
+ "_cl5Open: failed to open changelog database\n");
+
+ goto done;
+ }
+
+done:;
+
+ if (rc != CL5_SUCCESS)
+ {
+ _cl5Close ();
+ }
+
+ return rc;
+}
+
+int cl5CreateDirIfNeeded (const char *dirName)
+{
+ int rc;
+ char buff [MAXPATHLEN + 1];
+ char *t;
+
+ PR_ASSERT (dirName);
+
+ rc = PR_Access(dirName, PR_ACCESS_EXISTS);
+ if (rc == PR_SUCCESS)
+ {
+ return CL5_SUCCESS;
+ }
+
+ /* directory does not exist - try to create */
+ strncpy (buff, dirName, MAXPATHLEN);
+ t = strchr (buff, '/');
+
+ /* skip first slash */
+ if (t)
+ {
+ t = strchr (t+1, '/');
+ }
+
+ while (t)
+ {
+ *t = '\0';
+ if (PR_Access (buff, PR_ACCESS_EXISTS) != PR_SUCCESS)
+ {
+ rc = PR_MkDir (buff, DIR_CREATE_MODE);
+ if (rc != PR_SUCCESS)
+ {
+ slapi_log_error(SLAPI_LOG_REPL, repl_plugin_name_cl,
+ "cl5CreateDirIfNeeded: failed to create dir (%s); NSPR error - %d\n",
+ dirName, PR_GetError ());
+ return CL5_SYSTEM_ERROR;
+ }
+ }
+
+ *t++ = FILE_PATHSEP;
+
+ t = strchr (t, '/');
+ }
+
+ /* last piece */
+ rc = PR_MkDir (buff, DIR_CREATE_MODE);
+ if (rc != PR_SUCCESS)
+ {
+ slapi_log_error(SLAPI_LOG_REPL, repl_plugin_name_cl,
+ "cl5CreateDirIfNeeded: failed to create dir; NSPR error - %d\n",
+ PR_GetError ());
+ return CL5_SYSTEM_ERROR;
+ }
+
+ return CL5_SUCCESS;
+}
+
+static int _cl5RemoveEnv ()
+{
+ DB_ENV *dbEnv = NULL;
+ int rc = 0;
+
+ if ((rc = db_env_create(&dbEnv, 0)) != 0)
+ dbEnv = NULL;
+
+ if (dbEnv == NULL)
+ {
+ char *errstr = db_strerror(rc);
+ slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name_cl,
+ "_cl5RemoveEnv: failed to allocate db environment; "
+ "db error - %d %s\n", rc, errstr ? errstr : "unknown");
+ return CL5_MEMORY_ERROR;
+ }
+ rc = dbEnv->remove(dbEnv, s_cl5Desc.dbDir, DB_FORCE);
+ if (0 != rc)
+ {
+ char *errstr = db_strerror(rc);
+ slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name_cl,
+ "_cl5AppInit: failed to remove db environment; "
+ "db error - %d %s\n", rc, errstr ? errstr : "unknown");
+ return CL5_DB_ERROR;
+ }
+ return CL5_SUCCESS;
+}
+
+static int _cl5AppInit (PRBool *didRecovery)
+{
+ int rc;
+ unsigned int flags = DB_CREATE | DB_INIT_MPOOL | DB_THREAD;
+ DB_ENV *dbEnv;
+ if ((rc = db_env_create(&dbEnv, 0)) != 0)
+ dbEnv = NULL;
+
+ if (dbEnv == NULL)
+ {
+ char *errstr = db_strerror(rc);
+ slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name_cl,
+ "_cl5AppInit: failed to allocate db environment; db error - %d (%s)\n",
+ rc, errstr ? errstr : "unknown");
+ return CL5_MEMORY_ERROR;
+ }
+
+ _cl5InitDBEnv (dbEnv);
+
+ if (didRecovery)
+ *didRecovery = PR_FALSE;
+
+ /* decide how two open based on the mode in which db is open */
+ switch (s_cl5Desc.dbOpenMode)
+ {
+ case CL5_OPEN_NORMAL:
+ flags |= DB_INIT_LOCK | DB_INIT_TXN | DB_INIT_LOG;
+ /* check if need to initiate recovery */
+ rc = _cl5CheckGuardian ();
+ if (rc != CL5_SUCCESS)
+ {
+ slapi_log_error(SLAPI_LOG_REPL, repl_plugin_name_cl,
+ "_cl5AppInit: recovering changelog after disorderly shutdown\n");
+ flags |= DB_RECOVER;
+ }
+ break;
+
+ case CL5_OPEN_RESTORE:
+ flags |= DB_INIT_LOCK | DB_INIT_TXN | DB_INIT_LOG;
+ break;
+
+ case CL5_OPEN_CLEAN_RECOVER:
+ flags |= DB_INIT_LOCK | DB_INIT_TXN | DB_INIT_LOG | DB_RECOVER;
+ break;
+
+ case CL5_OPEN_RESTORE_RECOVER:
+ flags |= DB_INIT_LOCK | DB_INIT_TXN | DB_INIT_LOG | DB_RECOVER_FATAL;
+ break;
+
+ case CL5_OPEN_LDIF2CL:
+ /* ONREPL - don't think we need any extra flags here */
+ break;
+ default:
+ /* fixme? CL5_OPEN_NONE */
+ break;
+ }
+
+ if (!s_cl5Desc.dbConfig.durableTrans)
+ {
+#if 1000*DB_VERSION_MAJOR + 100*DB_VERSION_MINOR >= 3200
+ dbEnv->set_flags(dbEnv, DB_TXN_NOSYNC, 1);
+#else
+ flags |= DB_TXN_NOSYNC;
+#endif
+ }
+
+ dbEnv->set_errcall(dbEnv, dblayer_log_print);
+
+ /* do recovery if necessary */
+ if ((flags & DB_RECOVER) || (flags & DB_RECOVER_FATAL))
+ {
+ if (CL5_OPEN_CLEAN_RECOVER == s_cl5Desc.dbOpenMode)
+ _cl5RemoveEnv();
+
+ rc = _cl5Recover (flags, dbEnv);
+ if (rc != CL5_SUCCESS)
+ {
+ char *errstr = db_strerror(rc);
+ slapi_log_error(SLAPI_LOG_REPL, repl_plugin_name_cl,
+ "_cl5AppInit: failed to recover changelog; db error - %d %s\n",
+ rc, errstr ? errstr : "unknown");
+
+ slapi_ch_free ((void **)&dbEnv);
+
+ return rc;
+ }
+
+ if (didRecovery)
+ *didRecovery = PR_TRUE;
+ flags &= ~(DB_RECOVER | DB_RECOVER_FATAL);
+ /* Need to reset the env */
+ /* Does this leak the dbEnv? */
+ if ((rc = db_env_create(&dbEnv, 0)) != 0)
+ dbEnv = NULL;
+
+ if (dbEnv == NULL)
+ {
+ char *errstr = db_strerror(rc);
+ slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name_cl,
+ "_cl5AppInit: failed to allocate db environment after recovery; "
+ "db error - %d %s\n", rc, errstr ? errstr : "unknown");
+ return CL5_MEMORY_ERROR;
+ }
+ _cl5InitDBEnv (dbEnv);
+ }
+
+ rc = dbEnv->open(dbEnv, s_cl5Desc.dbDir, flags,
+ s_cl5Desc.dbConfig.fileMode);
+ if (rc == 0)
+ {
+ s_cl5Desc.dbEnv = dbEnv;
+ s_cl5Desc.dbEnvOpenFlags = flags;
+ return CL5_SUCCESS;
+ }
+ else
+ {
+ char *errstr = db_strerror(rc);
+ char flagstr[20];
+
+ flagstr[0] = 0;
+ /* EINVAL return means bad flags - let's see what the flags are */
+ if (rc == EINVAL)
+ {
+ sprintf(flagstr, "%u", flags);
+ }
+ slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name_cl,
+ "_cl5AppInit: db environment open failed; db error - %d %s %s\n",
+ rc, errstr ? errstr : "unknown", flagstr);
+ slapi_ch_free ((void **)&dbEnv);
+ return CL5_DB_ERROR;
+ }
+}
+
+static int _cl5DBOpen ()
+{
+ PRBool dbFile;
+ PRDir *dir;
+ PRDirEntry *entry = NULL;
+ int rc;
+ Object *replica;
+
+ /* create lock that guarantees that each file is only added once to the list */
+ s_cl5Desc.fileLock = PR_NewLock ();
+
+ /* loop over all db files and open them; file name format is cl5_<dbid>.<dbext> */
+ dir = PR_OpenDir(s_cl5Desc.dbDir);
+ if (dir == NULL)
+ {
+ slapi_log_error(SLAPI_LOG_REPL, repl_plugin_name_cl,
+ "_cl5DBOpen: failed to open changelog dir; NSPR error - %d\n",
+ PR_GetError ());
+ return CL5_SYSTEM_ERROR;
+
+ }
+
+ /* initialize set of db file objects */
+ s_cl5Desc.dbFiles = objset_new(NULL);
+ while (NULL != (entry = PR_ReadDir(dir, PR_SKIP_DOT | PR_SKIP_DOT_DOT)))
+ {
+ if (NULL == entry->name)
+ {
+ break;
+ }
+
+ dbFile = _cl5FileName2Replica (entry->name, &replica);
+ if (dbFile) /* this is db file, not a log or dbversion; those are just skipped */
+ {
+ /* we only open files for existing replicas */
+ if (replica)
+ {
+ rc = _cl5DBOpenFile (replica, NULL /* file object */,
+ PR_FALSE /* check for duplicates */);
+ if (rc != CL5_SUCCESS)
+ {
+ slapi_log_error(SLAPI_LOG_REPL, repl_plugin_name_cl, "_cl5DBOpen: "
+ "Error opening file %s\n",
+ entry->name);
+ return rc;
+ }
+
+ object_release (replica);
+ }
+ else /* there is no matching replica for the file - remove */
+ {
+ char fullpathname[MAXPATHLEN];
+ slapi_log_error(SLAPI_LOG_REPL, repl_plugin_name_cl, "_cl5DBOpen: "
+ "file %s has no matching replica; removing\n", entry->name);
+
+ PR_snprintf(fullpathname, MAXPATHLEN, "%s/%s", s_cl5Desc.dbDir, entry->name);
+ if (PR_Delete(fullpathname) != PR_SUCCESS)
+ {
+ slapi_log_error(SLAPI_LOG_REPL, repl_plugin_name_cl, "_cl5DBOpen: "
+ "failed to remove (%s) file; NSPR error - %d\n",
+ entry->name, PR_GetError ());
+
+ }
+ }
+ }
+ }
+
+ PR_CloseDir(dir);
+
+ return CL5_SUCCESS;
+}
+
+/* this function assumes that the entry was validated
+ using IsValidOperation
+
+ Data in db format:
+ ------------------
+ <1 byte version><1 byte change_type><sizeof time_t time><null terminated csn>
+ <null terminated uniqueid><null terminated targetdn>
+ [<null terminated newrdn><1 byte deleteoldrdn>][<4 byte mod count><mod1><mod2>....]
+
+ mod format:
+ -----------
+ <1 byte modop><null terminated attr name><4 byte value count>
+ <4 byte value size><value1><4 byte value size><value2>
+*/
+static int _cl5Entry2DBData (const CL5Entry *entry, char **data, PRUint32 *len)
+{
+ int size = 1 /* version */ + 1 /* operation type */ + sizeof (time_t);
+ char *pos;
+ PRUint32 t;
+ slapi_operation_parameters *op;
+ LDAPMod **add_mods = NULL;
+ char *rawDN = NULL;
+ char s[CSN_STRSIZE];
+
+ PR_ASSERT (entry && entry->op && data && len);
+
+ op = entry->op;
+
+ /* compute size of the buffer needed to hold the data */
+ size += CSN_STRSIZE;
+ size += strlen (op->target_address.uniqueid) + 1;
+
+ switch (op->operation_type)
+ {
+ case SLAPI_OPERATION_ADD: if (op->p.p_add.parentuniqueid)
+ size += strlen (op->p.p_add.parentuniqueid) + 1;
+ else
+ size ++; /* we just store NULL char */
+ slapi_entry2mods (op->p.p_add.target_entry, &rawDN/* dn */, &add_mods);
+ size += strlen (rawDN) + 1;
+ size += _cl5GetModsSize (add_mods);
+ break;
+
+ case SLAPI_OPERATION_MODIFY: size += strlen (op->target_address.dn) + 1;
+ size += _cl5GetModsSize (op->p.p_modify.modify_mods);
+ break;
+
+ case SLAPI_OPERATION_MODRDN: size += strlen (op->target_address.dn) + 1;
+ /* 1 for deleteoldrdn */
+ size += strlen (op->p.p_modrdn.modrdn_newrdn) + 2;
+ if (op->p.p_modrdn.modrdn_newsuperior_address.dn)
+ size += strlen (op->p.p_modrdn.modrdn_newsuperior_address.dn) + 1;
+ else
+ size ++; /* for NULL char */
+ if (op->p.p_modrdn.modrdn_newsuperior_address.uniqueid)
+ size += strlen (op->p.p_modrdn.modrdn_newsuperior_address.uniqueid) + 1;
+ else
+ size ++; /* for NULL char */
+ size += _cl5GetModsSize (op->p.p_modrdn.modrdn_mods);
+ break;
+
+ case SLAPI_OPERATION_DELETE: size += strlen (op->target_address.dn) + 1;
+ break;
+ }
+
+ /* allocate data buffer */
+ (*data) = (char *) slapi_ch_malloc (size);
+ if ((*data) == NULL)
+ {
+ slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name_cl,
+ "_cl5Entry2DBData: failed to allocate data buffer\n");
+ return CL5_MEMORY_ERROR;
+ }
+
+ /* fill in the data buffer */
+ pos = *data;
+ /* write a byte of version */
+ (*pos) = V_5;
+ pos ++;
+ /* write change type */
+ (*pos) = (unsigned char)op->operation_type;
+ pos ++;
+ /* write time */
+ t = PR_htonl((PRUint32)entry->time);
+ memcpy (pos, &t, sizeof (t));
+ pos += sizeof (t);
+ /* write csn */
+ _cl5WriteString (csn_as_string(op->csn,PR_FALSE,s), &pos);
+ /* write UniqueID */
+ _cl5WriteString (op->target_address.uniqueid, &pos);
+
+ /* figure out what else we need to write depending on the operation type */
+ switch (op->operation_type)
+ {
+ case SLAPI_OPERATION_ADD: _cl5WriteString (op->p.p_add.parentuniqueid, &pos);
+ _cl5WriteString (rawDN, &pos);
+ _cl5WriteMods (add_mods, &pos);
+ slapi_ch_free ((void**)&rawDN);
+ ldap_mods_free (add_mods, 1);
+ break;
+
+ case SLAPI_OPERATION_MODIFY: _cl5WriteString (op->target_address.dn, &pos);
+ _cl5WriteMods (op->p.p_modify.modify_mods, &pos);
+ break;
+
+ case SLAPI_OPERATION_MODRDN: _cl5WriteString (op->target_address.dn, &pos);
+ _cl5WriteString (op->p.p_modrdn.modrdn_newrdn, &pos);
+ *pos = (PRUint8)op->p.p_modrdn.modrdn_deloldrdn;
+ pos ++;
+ _cl5WriteString (op->p.p_modrdn.modrdn_newsuperior_address.dn, &pos);
+ _cl5WriteString (op->p.p_modrdn.modrdn_newsuperior_address.uniqueid, &pos);
+ _cl5WriteMods (op->p.p_modrdn.modrdn_mods, &pos);
+ break;
+
+ case SLAPI_OPERATION_DELETE: _cl5WriteString (op->target_address.dn, &pos);
+ break;
+ }
+
+ (*len) = size;
+
+ return CL5_SUCCESS;
+}
+
+/*
+ Data in db format:
+ ------------------
+ <1 byte version><1 byte change_type><sizeof time_t time><null terminated dbid>
+ <null terminated csn><null terminated uniqueid><null terminated targetdn>
+ [<null terminated newrdn><1 byte deleteoldrdn>][<4 byte mod count><mod1><mod2>....]
+
+ mod format:
+ -----------
+ <1 byte modop><null terminated attr name><4 byte value count>
+ <4 byte value size><value1><4 byte value size><value2>
+*/
+
+
+int
+cl5DBData2Entry (const char *data, PRUint32 len, CL5Entry *entry)
+{
+ int rc;
+ PRUint8 version;
+ char *pos = (char *)data;
+ char *strCSN;
+ PRUint32 thetime;
+ slapi_operation_parameters *op;
+ LDAPMod **add_mods;
+ char *rawDN;
+ char s[CSN_STRSIZE];
+
+ PR_ASSERT (data && entry && entry->op);
+
+ /* ONREPL - check that we do not go beyond the end of the buffer */
+
+ /* read byte of version */
+ version = (PRUint8)(*pos);
+ if (version != V_5)
+ {
+ slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name_cl,
+ "cl5DBData2Entry: invalid data version\n");
+ return CL5_BAD_FORMAT;
+ }
+
+ op = entry->op;
+
+ pos += sizeof(version);
+
+ /* read change type */
+ op->operation_type = (PRUint8)(*pos);
+ pos ++;
+
+ /* need to do the copy first, to skirt around alignment problems on
+ certain architectures */
+ memcpy((char *)&thetime,pos,sizeof(thetime));
+ entry->time = (time_t)PR_ntohl(thetime);
+ pos += sizeof (thetime);
+
+ /* read csn */
+ _cl5ReadString (&strCSN, &pos);
+ if (op->csn == NULL || strcmp (strCSN, csn_as_string(op->csn,PR_FALSE,s)) != 0)
+ {
+ op->csn = csn_new_by_string (strCSN);
+ }
+ slapi_ch_free ((void**)&strCSN);
+
+ /* read UniqueID */
+ _cl5ReadString (&op->target_address.uniqueid, &pos);
+
+ /* figure out what else we need to read depending on the operation type */
+ switch (op->operation_type)
+ {
+ case SLAPI_OPERATION_ADD: _cl5ReadString (&op->p.p_add.parentuniqueid, &pos);
+ /* richm: need to free parentuniqueid */
+ _cl5ReadString (&rawDN, &pos);
+ op->target_address.dn = rawDN;
+ /* convert mods to entry */
+ rc = _cl5ReadMods (&add_mods, &pos);
+ slapi_mods2entry (&(op->p.p_add.target_entry), rawDN, add_mods);
+ ldap_mods_free (add_mods, 1);
+ break;
+
+ case SLAPI_OPERATION_MODIFY: _cl5ReadString (&op->target_address.dn, &pos);
+ rc = _cl5ReadMods (&op->p.p_modify.modify_mods, &pos);
+ break;
+
+ case SLAPI_OPERATION_MODRDN: _cl5ReadString (&op->target_address.dn, &pos);
+ _cl5ReadString (&op->p.p_modrdn.modrdn_newrdn, &pos);
+ op->p.p_modrdn.modrdn_deloldrdn = *pos;
+ pos ++;
+ _cl5ReadString (&op->p.p_modrdn.modrdn_newsuperior_address.dn, &pos);
+ _cl5ReadString (&op->p.p_modrdn.modrdn_newsuperior_address.uniqueid, &pos);
+ rc = _cl5ReadMods (&op->p.p_modrdn.modrdn_mods, &pos);
+ break;
+
+ case SLAPI_OPERATION_DELETE: _cl5ReadString (&op->target_address.dn, &pos);
+ rc = CL5_SUCCESS;
+ break;
+
+ default: rc = CL5_BAD_FORMAT;
+ slapi_log_error(SLAPI_LOG_FATAL,
+ repl_plugin_name_cl,
+ "cl5DBData2Entry: failed to format entry\n");
+ break;
+ }
+
+ return rc;
+}
+
+/* thread management functions */
+static int _cl5DispatchDBThreads ()
+{
+ if (NULL == PR_CreateThread (PR_USER_THREAD, (VFP)(void *)_cl5DeadlockMain,
+ NULL, PR_PRIORITY_NORMAL, PR_GLOBAL_THREAD,
+ PR_UNJOINABLE_THREAD, DEFAULT_THREAD_STACKSIZE))
+ {
+ slapi_log_error(SLAPI_LOG_REPL, repl_plugin_name_cl,
+ "_cl5DispatchDBThreads: failed to create deadlock thread; "
+ "NSPR error - %d\n", PR_GetError ());
+ return CL5_SYSTEM_ERROR;
+ }
+
+ if (NULL == PR_CreateThread (PR_USER_THREAD, (VFP)(void *)_cl5CheckpointMain,
+ NULL, PR_PRIORITY_NORMAL, PR_GLOBAL_THREAD,
+ PR_UNJOINABLE_THREAD, DEFAULT_THREAD_STACKSIZE))
+ {
+ slapi_log_error(SLAPI_LOG_REPL, repl_plugin_name_cl,
+ "_cl5DispatchDBThreads: failed to create checkpoint thread; "
+ "NSPR error - %d\n", PR_GetError ());
+ return CL5_SYSTEM_ERROR;
+ }
+
+ if (NULL == PR_CreateThread (PR_USER_THREAD, (VFP)(void *)_cl5TrickleMain,
+ NULL, PR_PRIORITY_NORMAL, PR_GLOBAL_THREAD,
+ PR_UNJOINABLE_THREAD, DEFAULT_THREAD_STACKSIZE) )
+ {
+ slapi_log_error(SLAPI_LOG_REPL, repl_plugin_name_cl,
+ "_cl5DispatchDBThreads: failed to create trickle thread; "
+ "NSPR error - %d\n", PR_GetError ());
+ return CL5_SYSTEM_ERROR;
+ }
+
+ if (NULL == PR_CreateThread (PR_USER_THREAD, (VFP)(void*)_cl5TrimMain,
+ NULL, PR_PRIORITY_NORMAL, PR_GLOBAL_THREAD,
+ PR_UNJOINABLE_THREAD, DEFAULT_THREAD_STACKSIZE) )
+ {
+ slapi_log_error(SLAPI_LOG_REPL, repl_plugin_name_cl,
+ "_cl5DispatchDBThreads: failed to create trimming thread; "
+ "NSPR error - %d\n", PR_GetError ());
+ return CL5_SYSTEM_ERROR;
+ }
+
+ return CL5_SUCCESS;
+}
+
+static int _cl5AddThread ()
+{
+ /* lock the state lock so that nobody can change the state
+ while backup is in progress
+ */
+ PR_RWLock_Rlock (s_cl5Desc.stLock);
+
+ /* open changelog if it is not already open */
+ if (s_cl5Desc.dbState != CL5_STATE_OPEN)
+ {
+ slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name_cl,
+ "_cl5AddThread: invalid changelog state - %d\n", s_cl5Desc.dbState);
+ PR_RWLock_Unlock (s_cl5Desc.stLock);
+ return CL5_BAD_STATE;
+ }
+
+ /* increment global thread count to make sure that changelog does not close while
+ backup is in progress */
+ PR_AtomicIncrement (&s_cl5Desc.threadCount);
+
+ PR_RWLock_Unlock (s_cl5Desc.stLock);
+
+ return CL5_SUCCESS;
+}
+
+static void _cl5RemoveThread ()
+{
+ PR_ASSERT (s_cl5Desc.threadCount > 0);
+ PR_AtomicDecrement (&s_cl5Desc.threadCount);
+}
+
+/* data conversion functions */
+static void _cl5WriteString (const char *str, char **buff)
+{
+ if (str)
+ {
+ strcpy (*buff, str);
+ (*buff) += strlen (str) + 1;
+ }
+ else /* just write NULL char */
+ {
+ (**buff) = '\0';
+ (*buff) ++;
+ }
+}
+
+static void _cl5ReadString (char **str, char **buff)
+{
+ if (str)
+ {
+ int len = strlen (*buff);
+
+ if (len)
+ {
+ *str = slapi_ch_strdup (*buff);
+ (*buff) += len + 1;
+ }
+ else /* just null char - skip it */
+ {
+ *str = NULL;
+ (*buff) ++;
+ }
+ }
+ else /* just skip this string */
+ {
+ (*buff) += strlen (*buff) + 1;
+ }
+}
+
+/* mods format:
+ -----------
+ <4 byte mods count><mod1><mod2>...
+
+ mod format:
+ -----------
+ <1 byte modop><null terminated attr name><4 byte count>
+ <4 byte size><value1><4 byte size><value2>...
+ */
+static void _cl5WriteMods (LDAPMod **mods, char **buff)
+{
+ PRInt32 i;
+ char *mod_start;
+ PRInt32 count;
+
+ if (mods == NULL)
+ return;
+
+ /* skip mods count */
+ mod_start = (*buff) + sizeof (count);
+
+ /* write mods*/
+ for (i=0; mods[i]; i++)
+ {
+ _cl5WriteMod (mods[i], &mod_start);
+ }
+
+ count = PR_htonl(i);
+ memcpy (*buff, &count, sizeof (count));
+
+ (*buff) = mod_start;
+}
+
+static void _cl5WriteMod (LDAPMod *mod, char **buff)
+{
+ char *pos;
+ PRInt32 count;
+ struct berval *bv;
+ Slapi_Mod smod;
+
+ slapi_mod_init_byref(&smod, mod);
+
+ pos = *buff;
+ /* write mod op */
+ *pos = (PRUint8)slapi_mod_get_operation (&smod);
+ pos ++;
+ /* write attribute name */
+ _cl5WriteString (slapi_mod_get_type (&smod), &pos);
+
+ /* write value count */
+ count = PR_htonl(slapi_mod_get_num_values(&smod));
+ memcpy (pos, &count, sizeof (count));
+ pos += sizeof (PRInt32);
+
+ bv = slapi_mod_get_first_value (&smod);
+ while (bv)
+ {
+ _cl5WriteBerval (bv, &pos);
+ bv = slapi_mod_get_next_value (&smod);
+ }
+
+ (*buff) = pos;
+
+ slapi_mod_done (&smod);
+}
+
+/* mods format:
+ -----------
+ <4 byte mods count><mod1><mod2>...
+
+ mod format:
+ -----------
+ <1 byte modop><null terminated attr name><4 byte count>
+ {<4 byte size><value1><4 byte size><value2>... ||
+ <null terminated str1> <null terminated str2>...}
+ */
+
+static int _cl5ReadMods (LDAPMod ***mods, char **buff)
+{
+ char *pos = *buff;
+ int i;
+ int rc;
+ PRInt32 mod_count;
+ Slapi_Mods smods;
+ Slapi_Mod smod;
+
+ /* need to copy first, to skirt around alignment problems on certain
+ architectures */
+ memcpy((char *)&mod_count,*buff,sizeof(mod_count));
+ mod_count = PR_ntohl(mod_count);
+ pos += sizeof (mod_count);
+
+ slapi_mods_init (&smods , mod_count);
+
+ for (i = 0; i < mod_count; i++)
+ {
+ rc = _cl5ReadMod (&smod, &pos);
+ if (rc != CL5_SUCCESS)
+ {
+ slapi_mods_done(&smods);
+ return rc;
+ }
+
+ slapi_mods_add_smod(&smods, &smod);
+ }
+
+ *buff = pos;
+
+ *mods = slapi_mods_get_ldapmods_passout (&smods);
+ slapi_mods_done(&smods);
+
+ return CL5_SUCCESS;
+}
+
+static int _cl5ReadMod (Slapi_Mod *smod, char **buff)
+{
+ char *pos = *buff;
+ int i;
+ PRInt32 val_count;
+ char *type;
+ int op;
+ struct berval bv;
+
+ op = (*pos) & 0x000000FF;
+ pos ++;
+ _cl5ReadString (&type, &pos);
+
+ /* need to do the copy first, to skirt around alignment problems on
+ certain architectures */
+ memcpy((char *)&val_count,pos,sizeof(val_count));
+ val_count = PR_ntohl(val_count);
+ pos += sizeof (PRInt32);
+
+ slapi_mod_init(smod, val_count);
+ slapi_mod_set_operation (smod, op|LDAP_MOD_BVALUES);
+ slapi_mod_set_type (smod, type);
+ slapi_ch_free ((void**)&type);
+
+ for (i = 0; i < val_count; i++)
+ {
+ _cl5ReadBerval (&bv, &pos);
+ slapi_mod_add_value (smod, &bv);
+ slapi_ch_free((void **) &bv.bv_val);
+ }
+
+ (*buff) = pos;
+
+ return CL5_SUCCESS;
+}
+
+static int _cl5GetModsSize (LDAPMod **mods)
+{
+ int size;
+ int i;
+
+ if (mods == NULL)
+ return 0;
+
+ size = sizeof (PRInt32);
+ for (i=0; mods[i]; i++)
+ {
+ size += _cl5GetModSize (mods[i]);
+ }
+
+ return size;
+}
+
+static int _cl5GetModSize (LDAPMod *mod)
+{
+ int size;
+ int i;
+
+ size = 1 + strlen (mod->mod_type) + 1 + sizeof (mod->mod_op);
+ i = 0;
+ if (mod->mod_op & LDAP_MOD_BVALUES) /* values are in binary form */
+ {
+ while (mod->mod_bvalues != NULL && mod->mod_bvalues[i] != NULL)
+ {
+ size += mod->mod_bvalues[i]->bv_len + sizeof (mod->mod_bvalues[i]->bv_len);
+ i++;
+ }
+ }
+ else /* string data */
+ {
+ PR_ASSERT(0); /* ggood string values should never be used in the server */
+ }
+
+ return size;
+}
+
+static void _cl5ReadBerval (struct berval *bv, char** buff)
+{
+ PRUint32 length = 0;
+ PRUint32 net_length = 0;
+
+ PR_ASSERT (bv && buff);
+
+ /***PINAKI need to do the copy first, to skirt around alignment problems on
+ certain architectures */
+ /* DBDB : struct berval.bv_len is defined as unsigned long
+ * But code here expects it to be 32-bits in size.
+ * On 64-bit machines, this is not the case.
+ * I changed the code to consistently use 32-bit (4-byte)
+ * values on the encoded side. This means that it's
+ * possible to generate a huge berval that will not
+ * be encoded properly. However, this seems unlikely
+ * to happen in reality, and I felt that retaining the
+ * old on-disk format for the changely in the 64-bit
+ * version of the server was important.
+ */
+
+ memcpy((char *)&net_length, *buff, sizeof(net_length));
+ length = PR_ntohl(net_length);
+ *buff += sizeof(net_length);
+ bv->bv_len = length;
+
+ if (bv->bv_len > 0) {
+ bv->bv_val = (char*)slapi_ch_malloc (bv->bv_len);
+ memcpy (bv->bv_val, *buff, bv->bv_len);
+ *buff += bv->bv_len;
+ }
+ else {
+ bv->bv_val = NULL;
+ }
+}
+
+static void _cl5WriteBerval (struct berval *bv, char** buff)
+{
+ PRUint32 length = 0;
+ PRUint32 net_length = 0;
+
+ length = (PRUint32) bv->bv_len;
+ net_length = PR_htonl(length);
+
+ memcpy(*buff, &net_length, sizeof (net_length));
+ *buff += sizeof (net_length);
+ memcpy (*buff, bv->bv_val, length);
+ *buff += length;
+}
+
+/* data format: <value count> <value size> <value> <value size> <value> ..... */
+static int _cl5ReadBervals (struct berval ***bv, char** buff, unsigned int size)
+{
+ PRInt32 count;
+ int i;
+ char *pos;
+
+ PR_ASSERT (bv && buff);
+
+ /* ONREPL - need to check that we don't go beyond the end of the buffer */
+
+ pos = *buff;
+ memcpy((char *)&count, pos, sizeof(count));
+ count = PR_htonl (count);
+ pos += sizeof(count);
+
+ /* allocate bervals */
+ *bv = (struct berval **)slapi_ch_malloc ((count + 1) * sizeof (struct berval*));
+ if (*bv == NULL)
+ {
+ return CL5_MEMORY_ERROR;
+ }
+
+ for (i = 0; i < count; i++)
+ {
+ (*bv)[i] = (struct berval *)slapi_ch_malloc (sizeof (struct berval));
+ if ((*bv)[i] == NULL)
+ {
+ ber_bvecfree(*bv);
+ return CL5_MEMORY_ERROR;
+ }
+
+ _cl5ReadBerval ((*bv)[i], &pos);
+ }
+
+ (*bv)[count] = NULL;
+ *buff = pos;
+
+ return CL5_SUCCESS;
+}
+
+/* data format: <value count> <value size> <value> <value size> <value> ..... */
+static int _cl5WriteBervals (struct berval **bv, char** buff, unsigned int *size)
+{
+ PRInt32 count, net_count;
+ char *pos;
+ int i;
+
+ PR_ASSERT (bv && buff && size);
+
+ /* compute number of values and size of the buffer to hold them */
+ *size = sizeof (count);
+ for (count = 0; bv[count]; count ++)
+ {
+ *size += sizeof (bv[count]->bv_len) + bv[count]->bv_len;
+ }
+
+ /* allocate buffer */
+ *buff = (char*) slapi_ch_malloc (*size);
+ if (*buff == NULL)
+ {
+ *size = 0;
+ return CL5_MEMORY_ERROR;
+ }
+
+ /* fill the buffer */
+ pos = *buff;
+ net_count = PR_htonl(count);
+ memcpy (pos, &net_count, sizeof (net_count));
+ pos += sizeof (net_count);
+ for (i = 0; i < count; i ++)
+ {
+ _cl5WriteBerval (bv[i], &pos);
+ }
+
+ return CL5_SUCCESS;
+}
+
+static int _cl5DeadlockMain (void *param)
+{
+ PRIntervalTime interval;
+ int rc;
+
+ PR_AtomicIncrement (&s_cl5Desc.threadCount);
+ interval = PR_MillisecondsToInterval(100);
+ while (s_cl5Desc.dbState != CL5_STATE_CLOSING)
+ {
+ int aborted;
+ if ((rc = LOCK_DETECT(s_cl5Desc.dbEnv, 0, DB_LOCK_YOUNGEST, &aborted)) != 0)
+ {
+ slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name_cl,
+ "_cl5DeadlockMain: lock_detect failed (%d transaction%s aborted); db error - %d %s\n",
+ aborted, (aborted == 1)? "":"s", rc, db_strerror(rc));
+ }
+ else if (aborted)
+ {
+ slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name_cl,
+ "_cl5DeadlockMain: lock_detect succeeded, but %d transaction%s ha%s been aborted\n",
+ aborted, (aborted == 1)? "":"s", (aborted == 1)? "s":"ve");
+ }
+
+ DS_Sleep(interval);
+ }
+
+ PR_AtomicDecrement (&s_cl5Desc.threadCount);
+ return 0;
+}
+
+static int _cl5CheckpointMain (void *param)
+{
+ time_t lastCheckpointCompletion = 0;
+ PRIntervalTime interval;
+ int rc = -1;
+
+ PR_AtomicIncrement (&s_cl5Desc.threadCount);
+
+ interval = PR_MillisecondsToInterval(1000);
+ lastCheckpointCompletion = current_time();
+
+ while (s_cl5Desc.dbState != CL5_STATE_CLOSING)
+ {
+ /* Check to see if the checkpoint interval has elapsed */
+ if (current_time() - lastCheckpointCompletion > s_cl5Desc.dbConfig.checkpointInterval)
+ {
+ rc = TXN_CHECKPOINT(s_cl5Desc.dbEnv, 0, 0, 0);
+ if (rc == 0)
+ {
+ lastCheckpointCompletion = current_time();
+ }
+#if 1000*DB_VERSION_MAJOR + 100*DB_VERSION_MINOR < 4100
+ else if (rc != DB_INCOMPLETE) /* real error happened */
+ {
+ slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name_cl,
+ "_cl5CheckpointMain: checkpoint failed, db error - %d %s\n",
+ rc, db_strerror(rc));
+ }
+#endif
+
+ /* According to dboreham, we are doing checkpoint twice
+ to reduce the number of transaction log files which need
+ to be retained at any time. */
+ rc = TXN_CHECKPOINT(s_cl5Desc.dbEnv, 0, 0, 0);
+ if (rc == 0)
+ {
+ lastCheckpointCompletion = current_time();
+ }
+#if 1000*DB_VERSION_MAJOR + 100*DB_VERSION_MINOR < 4100
+ else if (rc != DB_INCOMPLETE) /* real error happened */
+ {
+ slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name_cl,
+ "_cl5CheckpointMain: checkpoint failed, db error - %d %s\n",
+ rc, db_strerror(rc));
+ }
+#endif
+
+ /* check if we should truncate logs */
+ if (s_cl5Desc.dbConfig.circularLogging)
+ {
+ char **list = NULL;
+ char **listp = NULL;
+ int rc = -1;
+ char filename[MAXPATHLEN + 1];
+
+ /* find out which log files don't contain active txns */
+ rc = LOG_ARCHIVE(s_cl5Desc.dbEnv, &list, 0, malloc);
+ if (0 == rc && NULL != list)
+ {
+ /* zap 'em ! */
+ for (listp = list; *listp != NULL; ++listp)
+ {
+ PR_snprintf(filename, MAXPATHLEN, "%s/%s", s_cl5Desc.dbDir,*listp);
+ PR_Delete (filename);
+ }
+ slapi_ch_free((void **)&list);
+ }
+ }
+ }
+
+ /* sleep for a while */
+ /* why aren't we sleeping exactly the right amount of time ? */
+ /* answer---because the interval might be changed after the server starts up */
+ DS_Sleep(interval);
+ }
+
+ PR_AtomicDecrement (&s_cl5Desc.threadCount);
+ return 0;
+}
+
+static int _cl5TrickleMain (void *param)
+{
+ PRIntervalTime interval;
+ int pages_written;
+ int rc;
+
+ PR_AtomicIncrement (&s_cl5Desc.threadCount);
+ interval = PR_MillisecondsToInterval(1000);
+ while (s_cl5Desc.dbState != CL5_STATE_CLOSING)
+ {
+ if ((rc = MEMP_TRICKLE(s_cl5Desc.dbEnv,
+ s_cl5Desc.dbConfig.tricklePercentage, &pages_written)) != 0)
+ {
+ slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name_cl,
+ "_cl5TrickleMain: memp_trickle failed; db error - %d %s\n",
+ rc, db_strerror(rc));
+ }
+
+ DS_Sleep(interval);
+ }
+
+ PR_AtomicDecrement (&s_cl5Desc.threadCount);
+
+ return 0;
+}
+
+/* upgrade from db33 to db41
+ * 1. Run recovery on the database environment using the DB_ENV->open method
+ * 2. Remove any Berkeley DB environment using the DB_ENV->remove method
+ * 3. extention .db3 -> .db4 ### koko kara !!!
+ */
+static int _cl5Upgrade3_4(char *fromVersion, char *toVersion)
+{
+ PRDir *dir = NULL;
+ PRDirEntry *entry = NULL;
+ DB *thisdb = NULL;
+ CL5OpenMode backup;
+ int rc = 0;
+
+ backup = s_cl5Desc.dbOpenMode;
+ s_cl5Desc.dbOpenMode = CL5_OPEN_CLEAN_RECOVER;
+ /* CL5_OPEN_CLEAN_RECOVER does 1 and 2 */
+ rc = _cl5AppInit (NULL);
+ if (rc != CL5_SUCCESS)
+ {
+ slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name_cl,
+ "_cl5Upgrade3_4: failed to open the db env\n");
+ return rc;
+ }
+
+ dir = PR_OpenDir(s_cl5Desc.dbDir);
+ if (dir == NULL)
+ {
+ slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name_cl,
+ "_cl5Upgrade3_4: failed to open changelog dir %s; NSPR error - %d\n",
+ s_cl5Desc.dbDir, PR_GetError ());
+ goto out;
+ }
+
+ while (NULL != (entry = PR_ReadDir(dir, PR_SKIP_DOT | PR_SKIP_DOT_DOT)))
+ {
+ if (NULL == entry->name)
+ {
+ break;
+ }
+ if (_cl5FileEndsWith(entry->name, DB_EXTENSION_DB3))
+ {
+ char oName [MAXPATHLEN + 1];
+ char nName [MAXPATHLEN + 1];
+ char *p = NULL;
+ char c;
+ int baselen = 0;
+ PR_snprintf(oName, MAXPATHLEN, "%s/%s", s_cl5Desc.dbDir, entry->name);
+ p = strstr(oName, DB_EXTENSION_DB3);
+ if (NULL == p)
+ {
+ continue;
+ }
+ /* db->rename closes DB; need to create every time */
+ rc = db_create(&thisdb, s_cl5Desc.dbEnv, 0);
+ if (0 != rc) {
+ slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name_cl,
+ "_cl5Upgrade3_4: failed to get db handle\n");
+ goto out;
+ }
+
+ baselen = p - oName;
+ c = *p;
+ *p = '\0';
+ PR_snprintf(nName, MAXPATHLEN+1, "%s", oName);
+ PR_snprintf(nName + baselen, MAXPATHLEN+1-baselen, "%s", DB_EXTENSION);
+ *p = c;
+ slapi_log_error(SLAPI_LOG_REPL, repl_plugin_name_cl,
+ "_cl5Upgrade3_4: renaming %s to %s\n", oName, nName);
+ rc = thisdb->rename(thisdb, (const char *)oName, NULL /* subdb */,
+ (const char *)nName, 0);
+ if (rc != PR_SUCCESS)
+ {
+ slapi_log_error(SLAPI_LOG_REPL, repl_plugin_name_cl,
+ "_cl5Upgrade3_4: failed to rename file (%s -> %s); "
+ "db error - %d %s\n", oName, nName, rc, db_strerror(rc));
+ break;
+ }
+ }
+ }
+ /* update the version file */
+ _cl5WriteDBVersion ();
+
+ /* update the guardian file */
+ _cl5WriteGuardian ();
+ slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name_cl,
+ "Upgrading from %s to %s is successfully done (%s)\n",
+ fromVersion, toVersion, s_cl5Desc.dbDir);
+out:
+ if (NULL != dir)
+ {
+ PR_CloseDir(dir);
+ }
+ if (s_cl5Desc.dbEnv)
+ {
+ DB_ENV *dbEnv = s_cl5Desc.dbEnv;
+ dbEnv->close(dbEnv, 0);
+ s_cl5Desc.dbEnv = NULL;
+ }
+ return rc;
+}
+
+static int _cl5CheckDBVersion ()
+{
+ char clVersion [VERSION_SIZE + 1];
+ char dbVersion [VERSION_SIZE + 1];
+ int rc;
+
+ if (!cl5Exist (s_cl5Desc.dbDir))
+ {
+ /* this is new changelog - write DB version and guardian file */
+ rc = _cl5WriteDBVersion ();
+ if (rc == CL5_SUCCESS) {
+ rc = _cl5WriteGuardian();
+ }
+ }
+ else
+ {
+ PR_snprintf (clVersion, VERSION_SIZE, "%s/%s/%s", CL5_TYPE, REPL_PLUGIN_NAME,
+ CHANGELOG_DB_VERSION);
+ rc = _cl5ReadDBVersion (s_cl5Desc.dbDir, dbVersion);
+
+ if (rc != CL5_SUCCESS)
+ {
+ slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name_cl,
+ "_cl5CheckDBVersion: invalid dbversion\n");
+ rc = CL5_BAD_DBVERSION;
+ }
+ else if (strcasecmp (clVersion, dbVersion) != 0)
+ {
+ char prevClVersion [VERSION_SIZE + 1];
+ PR_snprintf (prevClVersion, VERSION_SIZE, "%s/%s/%s",
+ CL5_TYPE, REPL_PLUGIN_NAME, CHANGELOG_DB_VERSION_PREV);
+ if (strcasecmp (prevClVersion, dbVersion) == 0)
+ {
+ /* upgrade */
+ rc = _cl5Upgrade3_4(prevClVersion, clVersion);
+ if (rc != CL5_SUCCESS)
+ {
+ slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name_cl,
+ "_cl5CheckDBVersion: upgrade %s -> %s failed\n",
+ CHANGELOG_DB_VERSION_PREV, CHANGELOG_DB_VERSION);
+ rc = CL5_BAD_DBVERSION;
+ }
+ }
+ else
+ {
+ slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name_cl,
+ "_cl5CheckDBVersion: invalid dbversion\n");
+ rc = CL5_BAD_DBVERSION;
+ }
+ }
+
+ }
+
+ return rc;
+}
+
+static int _cl5ReadDBVersion (const char *dir, char *clVersion)
+{
+ int rc;
+ PRFileDesc *file;
+ char fName [MAXPATHLEN + 1];
+ char buff [BUFSIZ];
+ PRInt32 size;
+ char *tok;
+ char * iter = NULL;
+
+ if (clVersion)
+ {
+ clVersion [0] = '\0';
+ }
+
+ PR_snprintf (fName, MAXPATHLEN, "%s/%s", dir, VERSION_FILE);
+
+ file = PR_Open (fName, PR_RDONLY, 777);
+ if (file == NULL)
+ {
+ slapi_log_error(SLAPI_LOG_REPL, repl_plugin_name_cl,
+ "_cl5ReadDBVersion: failed to open DBVERSION; NSPR error - %d\n",
+ PR_GetError ());
+ return CL5_SYSTEM_ERROR;
+ }
+
+ size = slapi_read_buffer (file, buff, BUFSIZ);
+ if (size < 0)
+ {
+ slapi_log_error(SLAPI_LOG_REPL, repl_plugin_name_cl,
+ "_cl5ReadDBVersion: failed to read DBVERSION; NSPR error - %d\n",
+ PR_GetError ());
+ PR_Close (file);
+ return CL5_SYSTEM_ERROR;
+ }
+
+ /* parse the data */
+ buff[size]= '\0';
+ tok = ldap_utf8strtok_r (buff, "\n", &iter);
+ if (tok)
+ {
+ if (clVersion)
+ {
+ strcpy(clVersion, tok);
+ }
+ }
+
+ rc = PR_Close (file);
+ if (rc != PR_SUCCESS)
+ {
+ slapi_log_error(SLAPI_LOG_REPL, repl_plugin_name_cl,
+ "_cl5ReadDBVersion: failed to close DBVERSION; NSPR error - %d\n",
+ PR_GetError ());
+ return CL5_SYSTEM_ERROR;
+ }
+
+ return CL5_SUCCESS;
+}
+
+static int _cl5WriteDBVersion ()
+{
+ int rc;
+ PRFileDesc *file;
+ char fName [MAXPATHLEN + 1];
+ char clVersion [VERSION_SIZE + 1];
+ PRInt32 len, size;
+
+ PR_snprintf (fName, MAXPATHLEN, "%s/%s", s_cl5Desc.dbDir, VERSION_FILE);
+
+ file = PR_Open (fName, PR_WRONLY | PR_CREATE_FILE, s_cl5Desc.dbConfig.fileMode);
+ if (file == NULL)
+ {
+ slapi_log_error(SLAPI_LOG_REPL, repl_plugin_name_cl,
+ "_cl5WriteDBVersion: failed to open DBVERSION; NSPR error - %d\n",
+ PR_GetError ());
+ return CL5_SYSTEM_ERROR;
+ }
+
+ /* write changelog version */
+ PR_snprintf (clVersion, VERSION_SIZE, "%s/%s/%s\n", CL5_TYPE, REPL_PLUGIN_NAME,
+ CHANGELOG_DB_VERSION);
+
+ len = strlen (clVersion);
+ size = slapi_write_buffer (file, clVersion, len);
+ if (size != len)
+ {
+ slapi_log_error(SLAPI_LOG_REPL, repl_plugin_name_cl,
+ "_cl5WriteDBVersion: failed to write DBVERSION; NSPR error - %d\n",
+ PR_GetError ());
+ PR_Close (file);
+ return CL5_SYSTEM_ERROR;
+ }
+
+ rc = PR_Close (file);
+ if (rc != PR_SUCCESS)
+ {
+ slapi_log_error(SLAPI_LOG_REPL, repl_plugin_name_cl,
+ "_cl5WriteDBVersion: failed to close DBVERSION; NSPR error - %d\n",
+ PR_GetError ());
+ return CL5_SYSTEM_ERROR;
+ }
+
+ return CL5_SUCCESS;
+}
+
+/* for now guardian file is just like dbversion file */
+static int _cl5CheckGuardian ()
+{
+ char plVersion [VERSION_SIZE + 1];
+ char dbVersion [VERSION_SIZE + 1];
+ int rc;
+
+ /* new changelog - no guardian file */
+ if (!cl5Exist(s_cl5Desc.dbDir))
+ {
+ return CL5_SUCCESS;
+ }
+ else
+ {
+ PR_snprintf (plVersion, VERSION_SIZE, "%s/%s/%s", CL5_TYPE, REPL_PLUGIN_NAME,
+ CHANGELOG_DB_VERSION);
+ rc = _cl5ReadGuardian (dbVersion);
+
+ if (rc != CL5_SUCCESS || strcasecmp (plVersion, dbVersion) != 0)
+ {
+ slapi_log_error(SLAPI_LOG_REPL, repl_plugin_name_cl,
+ "_cl5CheckGuardian: missing or invalid guardian file\n");
+ return (CL5_BAD_FORMAT);
+ }
+
+ /* remove guardian file */
+ rc = _cl5RemoveGuardian ();
+ if (rc != CL5_SUCCESS)
+ {
+ slapi_log_error(SLAPI_LOG_REPL, repl_plugin_name_cl,
+ "_cl5CheckGuardian: failed to remove guardian file\n");
+ }
+ }
+
+ return rc;
+}
+
+static int _cl5WriteGuardian ()
+{
+ int rc;
+ PRFileDesc *file;
+ char fName [MAXPATHLEN + 1];
+ char version [VERSION_SIZE];
+ PRInt32 len, size;
+
+ PR_snprintf (fName, MAXPATHLEN, "%s/%s", s_cl5Desc.dbDir, GUARDIAN_FILE);
+
+ file = PR_Open (fName, PR_WRONLY | PR_CREATE_FILE, s_cl5Desc.dbConfig.fileMode);
+ if (file == NULL)
+ {
+ slapi_log_error(SLAPI_LOG_REPL, repl_plugin_name_cl,
+ "_cl5WriteGuardian: failed to open guardian file; NSPR error - %d\n",
+ PR_GetError());
+ return CL5_SYSTEM_ERROR;
+ }
+
+ PR_snprintf (version, VERSION_SIZE, "%s/%s/%s\n", CL5_TYPE, REPL_PLUGIN_NAME,
+ CHANGELOG_DB_VERSION);
+
+ len = strlen (version);
+ size = slapi_write_buffer (file, version, len);
+ if (size != len)
+ {
+ slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name_cl,
+ "_cl5WriteGuardian: failed to write guardian file; NSPR error - %d\n",
+ PR_GetError());
+ PR_Close (file);
+ return CL5_SYSTEM_ERROR;
+ }
+
+ rc = PR_Close (file);
+ if (rc != PR_SUCCESS)
+ {
+ slapi_log_error(SLAPI_LOG_REPL, repl_plugin_name_cl,
+ "_cl5WriteGuardian: failed to close guardian file; NSPR error - %d\n",
+ PR_GetError());
+ return CL5_SYSTEM_ERROR;
+ }
+
+ return CL5_SUCCESS;
+}
+
+static int _cl5ReadGuardian (char *buff)
+{
+ int rc;
+ PRFileDesc *file;
+ char fName [MAXPATHLEN + 1];
+ PRInt32 size;
+
+ PR_snprintf (fName, MAXPATHLEN, "%s/%s", s_cl5Desc.dbDir, GUARDIAN_FILE);
+
+ file = PR_Open (fName, PR_RDONLY, 0);
+ if (file == NULL)
+ {
+ slapi_log_error(SLAPI_LOG_REPL, repl_plugin_name_cl,
+ "_cl5ReadGuardian: failed to open guardian file; NSPR error - %d\n",
+ PR_GetError());
+ return CL5_SYSTEM_ERROR;
+ }
+
+ size = slapi_read_buffer (file, buff, VERSION_SIZE);
+ if (size <= 0)
+ {
+ slapi_log_error(SLAPI_LOG_REPL, repl_plugin_name_cl,
+ "_cl5ReadGuardian: failed to read guardian file; NSPR error - %d\n",
+ PR_GetError());
+ PR_Close (file);
+ return CL5_SYSTEM_ERROR;
+ }
+
+ buff [size-1] = '\0';
+
+ rc = PR_Close (file);
+ if (rc != PR_SUCCESS)
+ {
+ slapi_log_error(SLAPI_LOG_REPL, repl_plugin_name_cl,
+ "_cl5ReadGuardian: failed to close guardian file; NSPR error - %d\n",
+ PR_GetError());
+ return CL5_SYSTEM_ERROR;
+ }
+
+ return CL5_SUCCESS;
+}
+
+static int _cl5RemoveGuardian ()
+{
+ char fName [MAXPATHLEN + 1];
+ int rc;
+
+ PR_snprintf (fName, MAXPATHLEN, "%s/%s", s_cl5Desc.dbDir, GUARDIAN_FILE);
+
+ rc = PR_Delete (fName);
+ if (rc != PR_SUCCESS)
+ {
+ slapi_log_error(SLAPI_LOG_REPL, repl_plugin_name_cl,
+ "_cl5RemoveGuardian: failed to remove guardian file; NSPR error - %d\n",
+ PR_GetError());
+ return CL5_SYSTEM_ERROR;
+ }
+
+ return CL5_SUCCESS;
+}
+
+/* must be called under the state lock */
+static void _cl5Close ()
+{
+ int rc2 = 0;
+ PRIntervalTime interval;
+
+ if (s_cl5Desc.dbState != CL5_STATE_CLOSED) /* Don't try to close twice */
+ {
+
+ /* close db files */
+ _cl5DBClose ();
+
+ /* stop global threads */
+ interval = PR_MillisecondsToInterval(100);
+ while (s_cl5Desc.threadCount > 0)
+ {
+ slapi_log_error( SLAPI_LOG_PLUGIN, repl_plugin_name_cl,
+ "_cl5Close: waiting for threads to exit: %d thread(s) still active\n",
+ s_cl5Desc.threadCount);
+ DS_Sleep(interval);
+ }
+
+ /* cleanup trimming */
+ _cl5TrimCleanup ();
+
+ /* shutdown db environment */
+ if (s_cl5Desc.dbEnv)
+ {
+ DB_ENV *dbEnv = s_cl5Desc.dbEnv;
+ rc2 = dbEnv->close(dbEnv, 0);
+ s_cl5Desc.dbEnv = NULL;
+ }
+
+ /* record successful close by writing guardian file;
+ we do it in all case accept incomplete open due to an error */
+ if (s_cl5Desc.dbState == CL5_STATE_CLOSING || s_cl5Desc.dbOpenMode != CL5_OPEN_NORMAL)
+ {
+ _cl5WriteGuardian ();
+ }
+
+ /* remove changelog if requested */
+ if (s_cl5Desc.dbRmOnClose)
+ {
+
+ if (_cl5Delete (s_cl5Desc.dbDir, 1) != CL5_SUCCESS)
+ {
+ slapi_log_error(SLAPI_LOG_REPL, repl_plugin_name_cl,
+ "cl5Close: failed to remove changelog\n");
+ }
+ s_cl5Desc.dbRmOnClose = PR_FALSE;
+ }
+
+ slapi_ch_free ((void **)&s_cl5Desc.dbDir);
+ memset (&s_cl5Desc.dbConfig, 0, sizeof (s_cl5Desc.dbConfig));
+ s_cl5Desc.fatalError = PR_FALSE;
+ s_cl5Desc.threadCount = 0;
+ s_cl5Desc.dbOpenMode = CL5_OPEN_NONE;
+ }
+}
+
+static void _cl5DBClose ()
+{
+ if (NULL != s_cl5Desc.dbFiles)
+ {
+ objset_delete (&s_cl5Desc.dbFiles);
+ }
+ if (NULL != s_cl5Desc.fileLock)
+ {
+ PR_DestroyLock (s_cl5Desc.fileLock);
+ }
+}
+
+/* state lock must be locked */
+static int _cl5Delete (const char *clDir, int rmDir)
+{
+ PRDir *dir;
+ char filename[MAXPATHLEN + 1];
+ PRDirEntry *entry = NULL;
+ int rc;
+
+ /* remove all files in the directory and the directory */
+ dir = PR_OpenDir(clDir);
+ if (dir == NULL)
+ {
+ slapi_log_error(SLAPI_LOG_REPL, repl_plugin_name_cl,
+ "_cl5Delete: failed to open changelog dir; NSPR error - %d\n",
+ PR_GetError ());
+ return CL5_SYSTEM_ERROR;
+
+ }
+
+ while (NULL != (entry = PR_ReadDir(dir, PR_SKIP_DOT | PR_SKIP_DOT_DOT)))
+ {
+ if (NULL == entry->name)
+ {
+ break;
+ }
+ PR_snprintf(filename, MAXPATHLEN, "%s/%s", clDir, entry->name);
+ rc = PR_Delete(filename);
+ if (rc != PR_SUCCESS)
+ {
+ slapi_log_error(SLAPI_LOG_REPL, repl_plugin_name_cl,
+ "_cl5Delete: failed to remove (%s) file; NSPR error - %d\n",
+ filename, PR_GetError ());
+ }
+ }
+
+ rc = PR_CloseDir(dir);
+ if (rc != PR_SUCCESS)
+ {
+ slapi_log_error(SLAPI_LOG_REPL, repl_plugin_name_cl,
+ "_cl5Delete: failed to close changelog dir (%s); NSPR error - %d\n",
+ clDir, PR_GetError ());
+ return CL5_SYSTEM_ERROR;
+ }
+
+ if (rmDir)
+ {
+ rc = PR_RmDir (clDir);
+ if (rc != 0)
+ {
+ slapi_log_error(SLAPI_LOG_REPL, repl_plugin_name_cl,
+ "_cl5Delete: failed to remove changelog dir (%s); errno = %d\n",
+ clDir, errno);
+ return CL5_SYSTEM_ERROR;
+ }
+ }
+
+ return CL5_SUCCESS;
+}
+
+static void _cl5SetDefaultDBConfig ()
+{
+ s_cl5Desc.dbConfig.cacheSize = CL5_DEFAULT_CONFIG_DB_DBCACHESIZE;
+ s_cl5Desc.dbConfig.durableTrans = CL5_DEFAULT_CONFIG_DB_DURABLE_TRANSACTIONS;
+ s_cl5Desc.dbConfig.checkpointInterval = CL5_DEFAULT_CONFIG_DB_CHECKPOINT_INTERVAL;
+ s_cl5Desc.dbConfig.circularLogging = CL5_DEFAULT_CONFIG_DB_CIRCULAR_LOGGING;
+ s_cl5Desc.dbConfig.pageSize = CL5_DEFAULT_CONFIG_DB_PAGE_SIZE;
+ s_cl5Desc.dbConfig.logfileSize = CL5_DEFAULT_CONFIG_DB_LOGFILE_SIZE;
+ s_cl5Desc.dbConfig.maxTxnSize = CL5_DEFAULT_CONFIG_DB_TXN_MAX;
+ s_cl5Desc.dbConfig.verbose = CL5_DEFAULT_CONFIG_DB_VERBOSE;
+ s_cl5Desc.dbConfig.debug = CL5_DEFAULT_CONFIG_DB_DEBUG;
+ s_cl5Desc.dbConfig.tricklePercentage = CL5_DEFAULT_CONFIG_DB_TRICKLE_PERCENTAGE;
+ s_cl5Desc.dbConfig.spinCount = CL5_DEFAULT_CONFIG_DB_SPINCOUNT;
+ s_cl5Desc.dbConfig.nb_lock_config = CL5_DEFAULT_CONFIG_NB_LOCK;
+ s_cl5Desc.dbConfig.fileMode = FILE_CREATE_MODE;
+}
+
+static void _cl5SetDBConfig (const CL5DBConfig *config)
+{
+ /* through CL5DBConfig, we have access to all the LDAP configurable Changelog DB parameters */
+ s_cl5Desc.dbConfig.cacheSize = config->cacheSize;
+ s_cl5Desc.dbConfig.durableTrans = config->durableTrans;
+ s_cl5Desc.dbConfig.checkpointInterval = config->checkpointInterval;
+ s_cl5Desc.dbConfig.circularLogging = config->circularLogging;
+ s_cl5Desc.dbConfig.pageSize = config->pageSize;
+ s_cl5Desc.dbConfig.logfileSize = config->logfileSize;
+ s_cl5Desc.dbConfig.maxTxnSize = config->maxTxnSize;
+ s_cl5Desc.dbConfig.verbose = config->verbose;
+ s_cl5Desc.dbConfig.debug = config->debug;
+ s_cl5Desc.dbConfig.tricklePercentage = config->tricklePercentage;
+ s_cl5Desc.dbConfig.spinCount = config->spinCount;
+ s_cl5Desc.dbConfig.nb_lock_config = config->nb_lock_config;
+ s_cl5Desc.dbConfig.maxConcurrentWrites = config->maxConcurrentWrites;
+
+ if (config->spinCount != 0)
+ {
+ DB_ENV_SET_TAS_SPINS(s_cl5Desc.dbEnv, config->spinCount);
+ }
+
+ /* Some other configuration parameters are hardcoded... */
+ s_cl5Desc.dbConfig.fileMode = FILE_CREATE_MODE;
+}
+
+#define ONEG 1073741824 /* one giga bytes */
+static void _cl5InitDBEnv(DB_ENV *dbEnv)
+{
+ dbEnv->set_errpfx(dbEnv, "ns-slapd");
+ dbEnv->set_lg_max(dbEnv, s_cl5Desc.dbConfig.logfileSize);
+ dbEnv->set_tx_max(dbEnv, s_cl5Desc.dbConfig.maxTxnSize);
+ dbEnv->set_cachesize(dbEnv, s_cl5Desc.dbConfig.cacheSize/ONEG,
+ s_cl5Desc.dbConfig.cacheSize%ONEG,
+ 0);
+ /* Set default number of locks */
+ dbEnv->set_lk_max_locks(dbEnv, s_cl5Desc.dbConfig.nb_lock_config);
+
+ if (s_cl5Desc.dbConfig.verbose)
+ {
+ int on = 1;
+ dbEnv->set_verbose(dbEnv, DB_VERB_CHKPOINT, on);
+ dbEnv->set_verbose(dbEnv, DB_VERB_DEADLOCK, on);
+ dbEnv->set_verbose(dbEnv, DB_VERB_RECOVERY, on);
+ dbEnv->set_verbose(dbEnv, DB_VERB_WAITSFOR, on);
+ }
+ if (s_cl5Desc.dbConfig.debug)
+ {
+ dbEnv->set_errcall(dbEnv, _cl5DBLogPrint);
+ }
+#if 1000*DB_VERSION_MAJOR + 100*DB_VERSION_MINOR >= 3300
+ dbEnv->set_alloc(dbEnv, malloc, realloc, free);
+#endif
+}
+
+static void _cl5DBLogPrint(const char* prefix, char *buffer)
+{
+ /* We ignore the prefix since we know who we are anyway */
+ slapi_log_error (SLAPI_LOG_FATAL, repl_plugin_name_cl, "cl5: %s\n", buffer);
+}
+
+static PRBool _cl5IsLogFile (const char *path)
+{
+ int rc;
+
+ /* Is the filename at least 4 characters long ? */
+ if (strlen(path) < 4)
+ {
+ return PR_FALSE; /* Not a log file then */
+ }
+
+ /* Are the first 4 characters "log." ? */
+ rc = strncmp(path,"log.",4);
+ if (0 == rc)
+ {
+ /* Now, are the last 4 characters _not_ .db# ? */
+ const char *piece = path + (strlen(path) - 4);
+ rc = strcmp(piece, DB_EXTENSION);
+ if (0 != rc)
+ {
+ /* Is */
+ return PR_TRUE;
+ }
+ }
+ return PR_FALSE; /* Is not */
+}
+
+static int _cl5Recover (int open_flags, DB_ENV *dbEnv)
+{
+ /* If we're doing recovery, we MUST open the env single-threaded ! */
+ int recover_flags = open_flags & ~DB_THREAD;
+ int rc;
+
+ rc = dbEnv->open(dbEnv, s_cl5Desc.dbDir, recover_flags, s_cl5Desc.dbConfig.fileMode);
+
+ if (rc != 0)
+ {
+ slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name_cl,
+ "_cl5Recover: appinit failed; db error - %d %s\n",
+ rc, db_strerror(rc));
+ return CL5_DB_ERROR;
+ }
+
+ /* Now close it so we can re-open it again... */
+ dbEnv->close(dbEnv, 0);
+
+ return CL5_SUCCESS;
+}
+
+/* Trimming helper functions */
+static int _cl5TrimInit ()
+{
+ /* just create the lock while we are singlethreaded */
+ s_cl5Desc.dbTrim.lock = PR_NewLock();
+
+ if (s_cl5Desc.dbTrim.lock == NULL)
+ {
+ slapi_log_error(SLAPI_LOG_REPL, repl_plugin_name_cl,
+ "_cl5InitTrimming: failed to create lock; NSPR error - %d\n",
+ PR_GetError ());
+ return CL5_SYSTEM_ERROR;
+ }
+ else
+ {
+ return CL5_SUCCESS;
+ }
+}
+
+static void _cl5TrimCleanup ()
+{
+ if (s_cl5Desc.dbTrim.lock)
+ PR_DestroyLock (s_cl5Desc.dbTrim.lock);
+
+ memset (&s_cl5Desc.dbTrim, 0, sizeof (s_cl5Desc.dbTrim));
+}
+
+static int _cl5TrimMain (void *param)
+{
+ PRIntervalTime interval;
+ time_t timePrev = current_time ();
+ time_t timeNow;
+
+ PR_AtomicIncrement (&s_cl5Desc.threadCount);
+ interval = PR_SecondsToInterval(CHANGELOGDB_TRIM_INTERVAL);
+
+ while (s_cl5Desc.dbState != CL5_STATE_CLOSING)
+ {
+ timeNow = current_time ();
+ if (timeNow - timePrev >= CHANGELOGDB_TRIM_INTERVAL)
+ {
+ /* time to trim */
+ timePrev = timeNow;
+ _cl5DoTrimming ();
+ }
+ if (NULL == s_cl5Desc.clLock)
+ {
+ /* most likely, emergency */
+ break;
+ }
+
+ PR_Lock(s_cl5Desc.clLock);
+ PR_WaitCondVar(s_cl5Desc.clCvar, interval);
+ PR_Unlock(s_cl5Desc.clLock);
+ }
+
+ PR_AtomicDecrement (&s_cl5Desc.threadCount);
+
+ return 0;
+}
+
+/* We remove an entry if it has been replayed to all consumers and
+ and the number of entries in the changelog is larger than maxEntries
+ or age of the entry is larger than maxAge.
+ Also we can't purge entries which correspond to max csns in the
+ supplier's ruv. Here is a example where we can get into trouble:
+ The server is setup with time based trimming and no consumer's
+ At some point all the entries are trimmed from the changelog.
+ At a later point a consumer is added and initialized online
+ Then a change is made on the supplier.
+ To update the consumer, the supplier would attempt to locate
+ the last change sent to the consumer in the changelog and will
+ fail because the change was removed.
+
+ */
+
+static void _cl5DoTrimming ()
+{
+ Object *obj;
+ long numToTrim;
+
+ PR_Lock (s_cl5Desc.dbTrim.lock);
+
+ /* ONREPL We trim file by file which means that some files will be
+ trimmed more often than other. We might have to fix that by, for
+ example, randomizing starting point */
+ obj = objset_first_obj (s_cl5Desc.dbFiles);
+ while (obj && _cl5CanTrim ((time_t)0, &numToTrim))
+ {
+ _cl5TrimFile (obj, &numToTrim);
+ obj = objset_next_obj (s_cl5Desc.dbFiles, obj);
+ }
+
+ if (obj)
+ object_release (obj);
+
+ PR_Unlock (s_cl5Desc.dbTrim.lock);
+
+ return;
+}
+
+/* Note that each file contains changes for a single replicated area.
+ trimming algorithm:
+*/
+#define CL5_TRIM_MAX_PER_TRANSACTION 10
+
+static void _cl5TrimFile (Object *obj, long *numToTrim)
+{
+ DB_TXN *txnid;
+ RUV *ruv = NULL;
+ CL5Entry entry;
+ slapi_operation_parameters op = {0};
+ void *it;
+ int finished = 0, totalTrimmed = 0, count;
+ PRBool abort;
+ char strCSN[CSN_STRSIZE];
+ int rc;
+
+ PR_ASSERT (obj);
+
+ /* construct the ruv up to which we can purge */
+ rc = _cl5GetRUV2Purge2 (obj, &ruv);
+ if (rc != CL5_SUCCESS || ruv == NULL)
+ {
+ return;
+ }
+
+ entry.op = &op;
+
+ while ( !finished && !g_get_shutdown() )
+ {
+ it = NULL;
+ count = 0;
+ txnid = NULL;
+ abort = PR_FALSE;
+
+ /* DB txn lock accessed pages until the end of the transaction. */
+
+ rc = TXN_BEGIN(s_cl5Desc.dbEnv, NULL, &txnid, 0);
+ if (rc != 0)
+ {
+ slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name_cl,
+ "_cl5TrimFile: failed to begin transaction; db error - %d %s\n",
+ rc, db_strerror(rc));
+ finished = PR_TRUE;
+ break;
+ }
+
+ finished = _cl5GetFirstEntry (obj, &entry, &it, txnid);
+ while ( !finished )
+ {
+ /*
+ * This change can be trimmed if it exceeds purge
+ * parameters and has been seen by all consumers.
+ */
+ if ( (*numToTrim > 0 || _cl5CanTrim (entry.time, numToTrim)) &&
+ ruv_covers_csn_strict (ruv, op.csn) )
+ {
+ rc = _cl5CurrentDeleteEntry (it);
+ if ( rc == CL5_SUCCESS )
+ {
+ /* update purge vector */
+ rc = _cl5UpdateRUV (obj, op.csn, PR_FALSE, PR_TRUE);
+ }
+ if ( rc == CL5_SUCCESS)
+ {
+ if (*numToTrim > 0) (*numToTrim)--;
+ count++;
+ }
+ else
+ {
+ /* The above two functions have logged the error */
+ abort = PR_TRUE;
+ }
+
+ }
+ else
+ {
+ /* The changelog DB is time ordered. If we can not trim
+ * a CSN, we will not be allowed to trim the rest of the
+ * CSNs generally. However, the maxcsn of each replica ID
+ * is always kept in the changelog as an anchor for
+ * replaying future changes. We have to skip those anchor
+ * CSNs, otherwise a non-active replica ID could block
+ * the trim forever.
+ */
+ CSN *maxcsn = NULL;
+ ReplicaId rid;
+
+ rid = csn_get_replicaid (op.csn);
+ ruv_get_largest_csn_for_replica (ruv, rid, &maxcsn);
+ if ( csn_compare (op.csn, maxcsn) != 0 )
+ {
+ /* op.csn is not anchor CSN */
+ finished = 1;
+ }
+ else
+ {
+ slapi_log_error (SLAPI_LOG_REPL, NULL,
+ "Changelog purge skipped anchor csn %s\n",
+ csn_as_string (maxcsn, PR_FALSE, strCSN));
+
+ /* extra read to skip the current record */
+ cl5_operation_parameters_done (&op);
+ finished =_cl5GetNextEntry (&entry, it);
+ }
+ if (maxcsn) csn_free (&maxcsn);
+ }
+ cl5_operation_parameters_done (&op);
+ if (finished || abort || count >= CL5_TRIM_MAX_PER_TRANSACTION)
+ {
+ /* If we reach CL5_TRIM_MAX_PER_TRANSACTION,
+ * we close the cursor,
+ * commit the transaction and restart a new transaction
+ */
+ break;
+ }
+ finished = _cl5GetNextEntry (&entry, it);
+ }
+
+ /* MAB: We need to close the cursor BEFORE the txn commits/aborts.
+ * If we don't respect this order, we'll screw up the database,
+ * placing it in DB_RUNRECOVERY mode
+ */
+ cl5DestroyIterator (it);
+
+ if (abort)
+ {
+ finished = 1;
+ rc = TXN_ABORT (txnid);
+ if (rc != 0)
+ {
+ slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name_cl,
+ "_cl5TrimFile: failed to abort transaction; db error - %d %s\n",
+ rc, db_strerror(rc));
+ }
+ }
+ else
+ {
+ rc = TXN_COMMIT (txnid, 0);
+ if (rc != 0)
+ {
+ finished = 1;
+ slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name_cl,
+ "_cl5TrimFile: failed to commit transaction; db error - %d %s\n",
+ rc, db_strerror(rc));
+ }
+ else
+ {
+ totalTrimmed += count;
+ }
+ }
+
+ } /* While (!finished) */
+
+ if (ruv)
+ ruv_destroy (&ruv);
+
+ if (totalTrimmed)
+ {
+ slapi_log_error (SLAPI_LOG_REPL, NULL, "Trimmed %d changes from the changelog\n", totalTrimmed);
+ }
+}
+
+static PRBool _cl5CanTrim (time_t time, long *numToTrim)
+{
+ *numToTrim = 0;
+
+ if (s_cl5Desc.dbTrim.maxAge == 0 && s_cl5Desc.dbTrim.maxEntries == 0)
+ return PR_FALSE;
+
+ if (s_cl5Desc.dbTrim.maxAge == 0)
+ {
+ *numToTrim = cl5GetOperationCount (NULL) - s_cl5Desc.dbTrim.maxEntries;
+ return ( *numToTrim > 0 );
+ }
+
+ if (s_cl5Desc.dbTrim.maxEntries > 0 &&
+ (*numToTrim = cl5GetOperationCount (NULL) - s_cl5Desc.dbTrim.maxEntries) > 0)
+ return PR_TRUE;
+
+ if (time)
+ return (current_time () - time > s_cl5Desc.dbTrim.maxAge);
+ else
+ return PR_TRUE;
+}
+
+static int _cl5ReadRUV (const char *replGen, Object *obj, PRBool purge)
+{
+ int rc;
+ char csnStr [CSN_STRSIZE];
+ DBT key={0}, data={0};
+ struct berval **vals;
+ CL5DBFile *file;
+ char *pos;
+ char *agmt_name;
+
+
+ PR_ASSERT (replGen && obj);
+
+ file = (CL5DBFile*)object_get_data (obj);
+ PR_ASSERT (file);
+
+ agmt_name = get_thread_private_agmtname();
+
+ if (purge) /* read purge vector entry */
+ key.data = _cl5GetHelperEntryKey (PURGE_RUV_TIME, csnStr);
+ else /* read upper bound vector */
+ key.data = _cl5GetHelperEntryKey (MAX_RUV_TIME, csnStr);
+
+ key.size = CSN_STRSIZE;
+
+ data.flags = DB_DBT_MALLOC;
+
+ rc = file->db->get(file->db, NULL/*txn*/, &key, &data, 0);
+ switch (rc)
+ {
+ case 0: pos = data.data;
+ rc = _cl5ReadBervals (&vals, &pos, data.size);
+ free (data.data);
+ if (rc != CL5_SUCCESS)
+ return rc;
+
+ if (purge)
+ rc = ruv_init_from_bervals(vals, &file->purgeRUV);
+ else
+ rc = ruv_init_from_bervals(vals, &file->maxRUV);
+
+ if (rc != RUV_SUCCESS)
+ {
+ slapi_log_error(SLAPI_LOG_REPL, repl_plugin_name_cl,
+ "%s: _cl5ReadRUV: failed to initialize %s ruv; "
+ "RUV error %d\n", agmt_name, purge? "purge" : "upper bound", rc);
+
+ return CL5_RUV_ERROR;
+ }
+
+ ber_bvecfree(vals);
+
+ /* delete the entry; it is re-added when file
+ is successfully closed */
+ file->db->del (file->db, NULL, &key, DEFAULT_DB_OP_FLAGS);
+
+ return CL5_SUCCESS;
+
+ case DB_NOTFOUND: /* RUV is lost - need to construct */
+ rc = _cl5ConstructRUV (replGen, obj, purge);
+ return rc;
+
+ default: slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name_cl,
+ "%s: _cl5ReadRUV: failed to get purge RUV; "
+ "db error - %d %s\n", agmt_name, rc, db_strerror(rc));
+ return CL5_DB_ERROR;
+ }
+}
+
+static int _cl5WriteRUV (CL5DBFile *file, PRBool purge)
+{
+ int rc;
+ DBT key={0}, data={0};
+ char csnStr [CSN_STRSIZE];
+ struct berval **vals;
+ DB_TXN *txnid = NULL;
+
+ if ((purge && file->purgeRUV == NULL) || (!purge && file->maxRUV == NULL))
+ return CL5_SUCCESS;
+
+ if (purge)
+ {
+ key.data = _cl5GetHelperEntryKey (PURGE_RUV_TIME, csnStr);
+ rc = ruv_to_bervals(file->purgeRUV, &vals);
+ }
+ else
+ {
+ key.data = _cl5GetHelperEntryKey (MAX_RUV_TIME, csnStr);
+ rc = ruv_to_bervals(file->maxRUV, &vals);
+ }
+
+ key.size = CSN_STRSIZE;
+
+ rc = _cl5WriteBervals (vals, (char**)&data.data, &data.size);
+ ber_bvecfree(vals);
+ if (rc != CL5_SUCCESS)
+ {
+ return rc;
+ }
+
+#if 1000*DB_VERSION_MAJOR + 100*DB_VERSION_MINOR < 4100
+ rc = txn_begin(s_cl5Desc.dbEnv, NULL, &txnid, 0);
+ if (rc != 0)
+ {
+ slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name_cl,
+ "_cl5WriteRUV: failed to begin transaction; db error - %d %s\n",
+ rc, db_strerror(rc));
+ return CL5_DB_ERROR;
+ }
+#endif
+ rc = file->db->put(file->db, txnid, &key, &data, DEFAULT_DB_OP_FLAGS);
+
+ slapi_ch_free ((void**)&data.data);
+ if ( rc == 0 )
+ {
+#if 1000*DB_VERSION_MAJOR + 100*DB_VERSION_MINOR < 4100
+ rc = txn_commit (txnid, 0);
+ if (rc != 0)
+ {
+ slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name_cl,
+ "_cl5WriteRUV: failed to commit transaction; db error - %d %s\n",
+ rc, db_strerror(rc));
+ return CL5_DB_ERROR;
+ }
+#endif
+ return CL5_SUCCESS;
+ }
+ else
+ {
+ slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name_cl,
+ "_cl5WriteRUV: failed to write %s RUV for file %s; db error - %d\n",
+ purge? "purge" : "upper bound", file->name, rc);
+
+ if (CL5_OS_ERR_IS_DISKFULL(rc))
+ {
+ cl5_set_diskfull();
+ return CL5_DB_ERROR;
+ }
+#if 1000*DB_VERSION_MAJOR + 100*DB_VERSION_MINOR < 4100
+ rc = txn_abort (txnid);
+ if (rc != 0)
+ {
+ slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name_cl,
+ "_cl5WriteRUV: failed to abort transaction; db error - %d %s\n",
+ rc, db_strerror(rc));
+ }
+#endif
+ return CL5_DB_ERROR;
+ }
+}
+
+/* This is a very slow process since we have to read every changelog entry.
+ Hopefully, this function is not called too often */
+static int _cl5ConstructRUV (const char *replGen, Object *obj, PRBool purge)
+{
+ int rc;
+ CL5Entry entry;
+ void *iterator = NULL;
+ slapi_operation_parameters op = {0};
+ CL5DBFile *file;
+
+ PR_ASSERT (replGen && obj);
+
+ file = (CL5DBFile*)object_get_data (obj);
+ PR_ASSERT (file);
+
+ /* construct the RUV */
+ if (purge)
+ rc = ruv_init_new (replGen, 0, NULL, &file->purgeRUV);
+ else
+ rc = ruv_init_new (replGen, 0, NULL, &file->maxRUV);
+ if (rc != RUV_SUCCESS)
+ {
+ slapi_log_error(SLAPI_LOG_REPL, repl_plugin_name_cl, "_cl5ConstructRUV: "
+ "failed to initialize %s RUV for file %s; ruv error - %d\n",
+ purge? "purge" : "upper bound", file->name, rc);
+ return CL5_RUV_ERROR;
+ }
+
+ entry.op = &op;
+ rc = _cl5GetFirstEntry (obj, &entry, &iterator, NULL);
+ while (rc == CL5_SUCCESS)
+ {
+ if (purge)
+ rc = ruv_set_csns_keep_smallest(file->purgeRUV, op.csn);
+ else
+ rc = ruv_set_csns (file->maxRUV, op.csn, NULL);
+
+ cl5_operation_parameters_done (&op);
+ if (rc != RUV_SUCCESS)
+ {
+ slapi_log_error(SLAPI_LOG_REPL, repl_plugin_name_cl, "_cl5ConstructRUV: "
+ "failed to updated %s RUV for file %s; ruv error - %d\n",
+ purge ? "purge" : "upper bound", file->name, rc);
+ rc = CL5_RUV_ERROR;
+ continue;
+ }
+
+ rc = _cl5GetNextEntry (&entry, iterator);
+ }
+
+ cl5_operation_parameters_done (&op);
+
+ if (iterator)
+ cl5DestroyIterator (iterator);
+
+ if (rc == CL5_NOTFOUND)
+ {
+ rc = CL5_SUCCESS;
+ }
+ else
+ {
+ if (purge)
+ ruv_destroy (&file->purgeRUV);
+ else
+ ruv_destroy (&file->maxRUV);
+ }
+
+ return rc;
+}
+
+static int _cl5UpdateRUV (Object *obj, CSN *csn, PRBool newReplica, PRBool purge)
+{
+ ReplicaId rid;
+ int rc = RUV_SUCCESS; /* initialize rc to avoid erroneous logs */
+ CL5DBFile *file;
+
+ PR_ASSERT (obj && csn);
+
+ file = (CL5DBFile*)object_get_data (obj);
+
+ /* if purge is TRUE, file->purgeRUV must be set;
+ if purge is FALSE, maxRUV must be set */
+ PR_ASSERT (file && ((purge && file->purgeRUV) || (!purge && file->maxRUV)));
+
+ /* update vector only if this replica is not yet part of RUV */
+ if (purge && newReplica)
+ {
+ rid = csn_get_replicaid(csn);
+ if (ruv_contains_replica (file->purgeRUV, rid))
+ return CL5_SUCCESS;
+ else
+ {
+ /* if the replica is not part of the purgeRUV yet, add it */
+ ruv_add_replica (file->purgeRUV, rid, multimaster_get_local_purl());
+ }
+ }
+ else
+ {
+ if (purge)
+ rc = ruv_set_csns(file->purgeRUV, csn, NULL);
+ else
+ rc = ruv_set_csns(file->maxRUV, csn, NULL);
+ }
+
+ if (rc != RUV_SUCCESS)
+ {
+ slapi_log_error(SLAPI_LOG_REPL, repl_plugin_name_cl, "_cl5UpdatePurgeRUV: "
+ "failed to update %s RUV for file %s; ruv error - %d\n",
+ purge ? "purge" : "upper bound", file->name, rc);
+ return CL5_RUV_ERROR;
+ }
+
+ return CL5_SUCCESS;
+}
+
+static int _cl5EnumConsumerRUV (const ruv_enum_data *element, void *arg)
+{
+ int rc;
+ RUV *ruv;
+ CSN *csn = NULL;
+
+ PR_ASSERT (element && element->csn && arg);
+
+ ruv = (RUV*)arg;
+
+ rc = ruv_get_largest_csn_for_replica(ruv, csn_get_replicaid (element->csn), &csn);
+ if (rc != RUV_SUCCESS || csn == NULL || csn_compare (element->csn, csn) < 0)
+ {
+ ruv_set_max_csn(ruv, element->csn, NULL);
+ }
+
+ if (csn)
+ csn_free (&csn);
+
+ return 0;
+}
+
+static int _cl5GetRUV2Purge2 (Object *fileObj, RUV **ruv)
+{
+ int rc = CL5_SUCCESS;
+ CL5DBFile *dbFile;
+ Object *rObj = NULL;
+ Replica *r = NULL;
+ Object *agmtObj = NULL;
+ Repl_Agmt *agmt;
+ Object *consRUVObj, *supRUVObj;
+ RUV *consRUV, *supRUV;
+ CSN *csn;
+
+ PR_ASSERT (fileObj && ruv);
+
+ dbFile = (CL5DBFile*)object_get_data (fileObj);
+ PR_ASSERT (dbFile);
+
+ rObj = replica_get_by_name (dbFile->replName);
+ PR_ASSERT (rObj);
+ r = (Replica*)object_get_data (rObj);
+ PR_ASSERT (r);
+
+ /* We start with this replica's RUV. See note in _cl5DoTrimming */
+ supRUVObj = replica_get_ruv (r);
+ PR_ASSERT (supRUVObj);
+
+ supRUV = (RUV*)object_get_data (supRUVObj);
+ PR_ASSERT (supRUV);
+
+ *ruv = ruv_dup (supRUV);
+
+ object_release (supRUVObj);
+
+ agmtObj = agmtlist_get_first_agreement_for_replica (r);
+ while (agmtObj)
+ {
+ agmt = (Repl_Agmt*)object_get_data (agmtObj);
+ PR_ASSERT (agmt);
+
+ consRUVObj = agmt_get_consumer_ruv (agmt);
+ if (consRUVObj)
+ {
+ consRUV = (RUV*)object_get_data (consRUVObj);
+ rc = ruv_enumerate_elements (consRUV, _cl5EnumConsumerRUV, *ruv);
+ if (rc != RUV_SUCCESS)
+ {
+ slapi_log_error(SLAPI_LOG_REPL, repl_plugin_name_cl, "_cl5GetRUV2Purge2: "
+ "failed to construct ruv; ruv error - %d\n", rc);
+ rc = CL5_RUV_ERROR;
+ object_release (consRUVObj);
+ object_release (agmtObj);
+ break;
+ }
+
+ object_release (consRUVObj);
+ }
+
+ agmtObj = agmtlist_get_next_agreement_for_replica (r, agmtObj);
+ }
+
+ /* check if there is any data in the constructed ruv - otherwise get rid of it */
+ if (ruv_get_max_csn(*ruv, &csn) != RUV_SUCCESS || csn == NULL)
+ {
+ ruv_destroy (ruv);
+ }
+ else
+ {
+ csn_free (&csn);
+ }
+
+ if (rObj)
+ object_release (rObj);
+
+ if (rc != CL5_SUCCESS && ruv)
+ ruv_destroy (ruv);
+
+ return rc;
+}
+
+static int _cl5GetEntryCount (CL5DBFile *file)
+{
+ int rc;
+ char csnStr [CSN_STRSIZE];
+ DBT key={0}, data={0};
+ DB_BTREE_STAT *stats = NULL;
+
+ PR_ASSERT (file);
+
+ /* read entry count. if the entry is there - the file was successfully closed
+ last time it was used */
+ key.data = _cl5GetHelperEntryKey (ENTRY_COUNT_TIME, csnStr);
+ key.size = CSN_STRSIZE;
+
+ data.flags = DB_DBT_MALLOC;
+
+ rc = file->db->get(file->db, NULL/*txn*/, &key, &data, 0);
+ switch (rc)
+ {
+ case 0: file->entryCount = *(int*)data.data;
+ free (data.data);
+
+ /* delete the entry. the entry is re-added when file
+ is successfully closed */
+ file->db->del (file->db, NULL, &key, DEFAULT_DB_OP_FLAGS);
+ slapi_log_error(SLAPI_LOG_REPL, repl_plugin_name_cl,
+ "_cl5GetEntryCount: %d changes for replica %s\n",
+ file->entryCount, file->replName);
+ return CL5_SUCCESS;
+
+ case DB_NOTFOUND: file->entryCount = 0;
+#if 1000*DB_VERSION_MAJOR + 100*DB_VERSION_MINOR >= 3300
+ rc = file->db->stat(file->db, (void*)&stats, 0);
+#else
+ rc = file->db->stat(file->db, (void*)&stats, malloc, 0);
+#endif
+ if (rc != 0)
+ {
+ slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name_cl,
+ "_cl5GetEntryCount: failed to get changelog statistics; "
+ "db error - %d %s\n", rc, db_strerror(rc));
+ return CL5_DB_ERROR;
+ }
+
+#ifdef DB30
+ file->entryCount = stats->bt_nrecs;
+#else /* DB31 */
+ file->entryCount = stats->bt_ndata;
+#endif
+ slapi_log_error(SLAPI_LOG_REPL, repl_plugin_name_cl,
+ "_cl5GetEntryCount: %d changes for replica %s\n",
+ file->entryCount, file->replName);
+
+ free (stats);
+ return CL5_SUCCESS;
+
+ default: slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name_cl,
+ "_cl5GetEntryCount: failed to get count entry; "
+ "db error - %d %s\n", rc, db_strerror(rc));
+ return CL5_DB_ERROR;
+ }
+}
+
+static int _cl5WriteEntryCount (CL5DBFile *file)
+{
+ int rc;
+ DBT key={0}, data={0};
+ char csnStr [CSN_STRSIZE];
+ DB_TXN *txnid = NULL;
+
+ key.data = _cl5GetHelperEntryKey (ENTRY_COUNT_TIME, csnStr);
+ key.size = CSN_STRSIZE;
+ data.data = (void*)&file->entryCount;
+ data.size = sizeof (file->entryCount);
+
+#if 1000*DB_VERSION_MAJOR + 100*DB_VERSION_MINOR < 4100
+ rc = txn_begin(s_cl5Desc.dbEnv, NULL, &txnid, 0);
+ if (rc != 0)
+ {
+ slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name_cl,
+ "_cl5WriteEntryCount: failed to begin transaction; db error - %d %s\n",
+ rc, db_strerror(rc));
+ return CL5_DB_ERROR;
+ }
+#endif
+ rc = file->db->put(file->db, txnid, &key, &data, DEFAULT_DB_OP_FLAGS);
+ if (rc == 0)
+ {
+#if 1000*DB_VERSION_MAJOR + 100*DB_VERSION_MINOR < 4100
+ rc = txn_commit (txnid, 0);
+ if (rc != 0)
+ {
+ slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name_cl,
+ "_cl5WriteEntryCount: failed to commit transaction; db error - %d %s\n",
+ rc, db_strerror(rc));
+ return CL5_DB_ERROR;
+ }
+#endif
+ return CL5_SUCCESS;
+ }
+ else
+ {
+ slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name_cl,
+ "_cl5WriteEntryCount: "
+ "failed to write count entry for file %s; db error - %d %s\n",
+ file->name, rc, db_strerror(rc));
+ if (CL5_OS_ERR_IS_DISKFULL(rc))
+ {
+ cl5_set_diskfull();
+ return CL5_DB_ERROR;
+ }
+#if 1000*DB_VERSION_MAJOR + 100*DB_VERSION_MINOR < 4100
+ rc = txn_abort (txnid);
+ if (rc != 0)
+ {
+ slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name_cl,
+ "_cl5WriteEntryCount: failed to abort transaction; db error - %d %s\n",
+ rc, db_strerror(rc));
+ }
+#endif
+ return CL5_DB_ERROR;
+ }
+}
+
+static const char* _cl5OperationType2Str (int type)
+{
+ switch (type)
+ {
+ case SLAPI_OPERATION_ADD: return T_ADDCTSTR;
+ case SLAPI_OPERATION_MODIFY: return T_MODIFYCTSTR;
+ case SLAPI_OPERATION_MODRDN: return T_MODRDNCTSTR;
+ case SLAPI_OPERATION_DELETE: return T_DELETECTSTR;
+ default: return NULL;
+ }
+}
+
+static int _cl5Str2OperationType (const char *str)
+{
+ if (strcasecmp (str, T_ADDCTSTR) == 0)
+ return SLAPI_OPERATION_ADD;
+
+ if (strcasecmp (str, T_MODIFYCTSTR) == 0)
+ return SLAPI_OPERATION_MODIFY;
+
+ if (strcasecmp (str, T_MODRDNCTSTR) == 0)
+ return SLAPI_OPERATION_MODRDN;
+
+ if (strcasecmp (str, T_DELETECTSTR) == 0)
+ return SLAPI_OPERATION_DELETE;
+
+ return -1;
+}
+
+static int _cl5Operation2LDIF (const slapi_operation_parameters *op, const char *replGen,
+ char **ldifEntry, PRInt32 *lenLDIF)
+{
+ int len = 2;
+ lenstr *l = NULL;
+ const char *strType;
+ char *strDeleteOldRDN;
+ char *buff, *start;
+ LDAPMod **add_mods;
+ char *rawDN;
+ char strCSN[CSN_STRSIZE];
+
+ PR_ASSERT (op && replGen && ldifEntry && IsValidOperation (op));
+
+ strType = _cl5OperationType2Str (op->operation_type);
+ csn_as_string(op->csn,PR_FALSE,strCSN);
+
+ /* find length of the buffer */
+ len += LDIF_SIZE_NEEDED(strlen (T_CHANGETYPESTR), strlen (strType));
+ len += LDIF_SIZE_NEEDED(strlen (T_REPLGEN), strlen (replGen));
+ len += LDIF_SIZE_NEEDED(strlen (T_CSNSTR), strlen (strCSN));
+ len += LDIF_SIZE_NEEDED(strlen (T_UNIQUEIDSTR), strlen (op->target_address.uniqueid));
+
+ switch (op->operation_type)
+ {
+ case SLAPI_OPERATION_ADD: if (op->p.p_add.parentuniqueid)
+ len += LDIF_SIZE_NEEDED(strlen (T_PARENTIDSTR),
+ strlen (op->p.p_add.parentuniqueid));
+ slapi_entry2mods (op->p.p_add.target_entry, &rawDN, &add_mods);
+ len += LDIF_SIZE_NEEDED(strlen (T_DNSTR), strlen (rawDN));
+ l = make_changes_string(add_mods, NULL);
+ len += LDIF_SIZE_NEEDED(strlen (T_CHANGESTR), l->ls_len);
+ ldap_mods_free (add_mods, 1);
+ break;
+
+ case SLAPI_OPERATION_MODIFY: len += LDIF_SIZE_NEEDED(strlen (T_DNSTR), strlen (op->target_address.dn));
+ l = make_changes_string(op->p.p_modify.modify_mods, NULL);
+ len += LDIF_SIZE_NEEDED(strlen (T_CHANGESTR), l->ls_len);
+ break;
+
+ case SLAPI_OPERATION_MODRDN: len += LDIF_SIZE_NEEDED(strlen (T_DNSTR), strlen (op->target_address.dn));
+ len += LDIF_SIZE_NEEDED(strlen (T_NEWRDNSTR),
+ strlen (op->p.p_modrdn.modrdn_newrdn));
+ strDeleteOldRDN = (op->p.p_modrdn.modrdn_deloldrdn ? "true" : "false");
+ len += LDIF_SIZE_NEEDED(strlen (T_DRDNFLAGSTR),
+ strlen (strDeleteOldRDN));
+ if (op->p.p_modrdn.modrdn_newsuperior_address.dn)
+ len += LDIF_SIZE_NEEDED(strlen (T_NEWSUPERIORDNSTR),
+ strlen (op->p.p_modrdn.modrdn_newsuperior_address.dn));
+ if (op->p.p_modrdn.modrdn_newsuperior_address.uniqueid)
+ len += LDIF_SIZE_NEEDED(strlen (T_NEWSUPERIORIDSTR),
+ strlen (op->p.p_modrdn.modrdn_newsuperior_address.uniqueid));
+ l = make_changes_string(op->p.p_modrdn.modrdn_mods, NULL);
+ len += LDIF_SIZE_NEEDED(strlen (T_CHANGESTR), l->ls_len);
+ break;
+
+ case SLAPI_OPERATION_DELETE: len += LDIF_SIZE_NEEDED(strlen (T_DNSTR), strlen (op->target_address.dn));
+ break;
+
+ default: slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name_cl,
+ "_cl5Operation2LDIF: invalid operation type - %d\n", op->operation_type);
+
+ return CL5_BAD_FORMAT;
+ }
+
+ /* allocate buffer */
+ buff = (char*)slapi_ch_malloc (len);
+ start = buff;
+ if (buff == NULL)
+ {
+ slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name_cl,
+ "_cl5Operation2LDIF: memory allocation failed\n");
+ return CL5_MEMORY_ERROR;
+ }
+
+ /* fill buffer */
+ ldif_put_type_and_value(&buff, T_CHANGETYPESTR, (char*)strType, strlen (strType));
+ ldif_put_type_and_value(&buff, T_REPLGEN, (char*)replGen, strlen (replGen));
+ ldif_put_type_and_value(&buff, T_CSNSTR, (char*)strCSN, strlen (strCSN));
+ ldif_put_type_and_value(&buff, T_UNIQUEIDSTR, op->target_address.uniqueid,
+ strlen (op->target_address.uniqueid));
+
+ switch (op->operation_type)
+ {
+ case SLAPI_OPERATION_ADD: if (op->p.p_add.parentuniqueid)
+ ldif_put_type_and_value(&buff, T_PARENTIDSTR,
+ op->p.p_add.parentuniqueid, strlen (op->p.p_add.parentuniqueid));
+ ldif_put_type_and_value(&buff, T_DNSTR, rawDN, strlen (rawDN));
+ ldif_put_type_and_value(&buff, T_CHANGESTR, l->ls_buf, l->ls_len);
+ slapi_ch_free ((void**)&rawDN);
+ break;
+
+ case SLAPI_OPERATION_MODIFY: ldif_put_type_and_value(&buff, T_DNSTR, op->target_address.dn,
+ strlen (op->target_address.dn));
+ ldif_put_type_and_value(&buff, T_CHANGESTR, l->ls_buf, l->ls_len);
+ break;
+
+ case SLAPI_OPERATION_MODRDN: ldif_put_type_and_value(&buff, T_DNSTR, op->target_address.dn,
+ strlen (op->target_address.dn));
+ ldif_put_type_and_value(&buff, T_NEWRDNSTR, op->p.p_modrdn.modrdn_newrdn,
+ strlen (op->p.p_modrdn.modrdn_newrdn));
+ ldif_put_type_and_value(&buff, T_DRDNFLAGSTR, strDeleteOldRDN,
+ strlen (strDeleteOldRDN));
+ if (op->p.p_modrdn.modrdn_newsuperior_address.dn)
+ ldif_put_type_and_value(&buff, T_NEWSUPERIORDNSTR,
+ op->p.p_modrdn.modrdn_newsuperior_address.dn,
+ strlen (op->p.p_modrdn.modrdn_newsuperior_address.dn));
+ if (op->p.p_modrdn.modrdn_newsuperior_address.uniqueid)
+ ldif_put_type_and_value(&buff, T_NEWSUPERIORIDSTR,
+ op->p.p_modrdn.modrdn_newsuperior_address.uniqueid,
+ strlen (op->p.p_modrdn.modrdn_newsuperior_address.uniqueid));
+ ldif_put_type_and_value(&buff, T_CHANGESTR, l->ls_buf, l->ls_len);
+ break;
+
+ case SLAPI_OPERATION_DELETE: ldif_put_type_and_value(&buff, T_DNSTR, op->target_address.dn,
+ strlen (op->target_address.dn));
+ break;
+ }
+
+ *buff = '\n';
+ buff ++;
+ *buff = '\0';
+
+ *ldifEntry = start;
+ *lenLDIF = buff - start;
+
+ if (l)
+ lenstr_free(&l);
+
+ return CL5_SUCCESS;
+}
+
+static int
+_cl5LDIF2Operation (char *ldifEntry, slapi_operation_parameters *op, char **replGen)
+{
+ int rc;
+ int vlen;
+ char *next, *line;
+ char *type, *value;
+ Slapi_Mods *mods;
+ char *rawDN;
+
+ PR_ASSERT (op && ldifEntry && replGen);
+
+ memset (op, 0, sizeof (*op));
+
+ next = ldifEntry;
+ while ((line = ldif_getline(&next)) != NULL)
+ {
+ char *errmsg = NULL;
+
+ if ( *line == '\n' || *line == '\0' )
+ {
+ break;
+ }
+
+ /* this call modifies ldifEntry */
+ rc = ldif_parse_line(line, &type, &value, &vlen, &errmsg);
+ if (rc != 0)
+ {
+ if ( errmsg != NULL ) {
+ slapi_log_error(SLAPI_LOG_PARSE, repl_plugin_name_cl, "%s", errmsg);
+ slapi_ch_free( (void**)&errmsg );
+ }
+ slapi_log_error(SLAPI_LOG_REPL, repl_plugin_name_cl,
+ "_cl5LDIF2Operation: warning - failed to parse ldif line\n");
+ continue;
+ }
+
+ if (strcasecmp (type, T_CHANGETYPESTR) == 0)
+ {
+ op->operation_type = _cl5Str2OperationType (value);
+ }
+ else if (strcasecmp (type, T_REPLGEN) == 0)
+ {
+ *replGen = slapi_ch_strdup (value);
+ }
+ else if (strcasecmp (type, T_CSNSTR) == 0)
+ {
+ op->csn = csn_new_by_string(value);
+ }
+ else if (strcasecmp (type, T_UNIQUEIDSTR) == 0)
+ {
+ op->target_address.uniqueid = slapi_ch_strdup (value);
+ }
+ else if (strcasecmp (type, T_DNSTR) == 0)
+ {
+ PR_ASSERT (op->operation_type);
+
+ if (op->operation_type == SLAPI_OPERATION_ADD)
+ {
+ rawDN = slapi_ch_strdup (value);
+ op->target_address.dn = slapi_ch_strdup(rawDN);
+ }
+ else
+ op->target_address.dn = slapi_ch_strdup (value);
+ }
+ else if (strcasecmp (type, T_PARENTIDSTR) == 0)
+ {
+ op->p.p_add.parentuniqueid = slapi_ch_strdup (value);
+ }
+ else if (strcasecmp (type, T_NEWRDNSTR) == 0)
+ {
+ op->p.p_modrdn.modrdn_newrdn = slapi_ch_strdup (value);
+ }
+ else if (strcasecmp (type, T_DRDNFLAGSTR) == 0)
+ {
+ op->p.p_modrdn.modrdn_deloldrdn = (strcasecmp (value, "true") ? PR_FALSE : PR_TRUE);
+ }
+ else if (strcasecmp (type, T_NEWSUPERIORDNSTR) == 0)
+ {
+ op->p.p_modrdn.modrdn_newsuperior_address.dn = slapi_ch_strdup (value);
+ }
+ else if (strcasecmp (type, T_NEWSUPERIORIDSTR) == 0)
+ {
+ op->p.p_modrdn.modrdn_newsuperior_address.uniqueid = slapi_ch_strdup (value);
+ }
+ else if (strcasecmp (type, T_CHANGESTR) == 0)
+ {
+ PR_ASSERT (op->operation_type);
+
+ switch (op->operation_type)
+ {
+ case SLAPI_OPERATION_ADD: mods = parse_changes_string(value);
+ slapi_mods2entry (&(op->p.p_add.target_entry), rawDN,
+ slapi_mods_get_ldapmods_byref(mods));
+ slapi_ch_free ((void**)&rawDN);
+ slapi_mods_free (&mods);
+ break;
+
+ case SLAPI_OPERATION_MODIFY: mods = parse_changes_string(value);
+ PR_ASSERT (mods);
+ op->p.p_modify.modify_mods = slapi_mods_get_ldapmods_passout (mods);
+ slapi_mods_free (&mods);
+ break;
+
+ case SLAPI_OPERATION_MODRDN: mods = parse_changes_string(value);
+ PR_ASSERT (mods);
+ op->p.p_modrdn.modrdn_mods = slapi_mods_get_ldapmods_passout (mods);
+ slapi_mods_free (&mods);
+ break;
+
+ default: slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name_cl,
+ "_cl5LDIF2Operation: invalid operation type - %d\n",
+ op->operation_type);
+ return CL5_BAD_FORMAT;
+ }
+ }
+ }
+
+ if (IsValidOperation (op))
+ return CL5_SUCCESS;
+
+ slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name_cl,
+ "_cl5LDIF2Operation: invalid data format\n");
+ return CL5_BAD_FORMAT;
+}
+
+static int _cl5WriteOperation(const char *replName, const char *replGen,
+ const slapi_operation_parameters *op, PRBool local)
+{
+ int rc;
+ int cnt;
+ DBT key={0};
+ DBT * data=NULL;
+ char csnStr [CSN_STRSIZE];
+ PRIntervalTime interval;
+ CL5Entry entry;
+ CL5DBFile *file = NULL;
+ Object *file_obj = NULL;
+ DB_TXN *txnid = NULL;
+
+ rc = _cl5GetDBFileByReplicaName (replName, replGen, &file_obj);
+ if (rc == CL5_NOTFOUND)
+ {
+ rc = _cl5DBOpenFileByReplicaName (replName, replGen, &file_obj,
+ PR_TRUE /* check for duplicates */);
+ if (rc != CL5_SUCCESS)
+ {
+ return rc;
+ }
+ }
+ else if (rc != CL5_SUCCESS)
+ {
+ slapi_log_error(SLAPI_LOG_PLUGIN, repl_plugin_name_cl,
+ "_cl5WriteOperation: failed to get db file for target dn (%s)",
+ op->target_address.dn);
+ return CL5_OBJSET_ERROR;
+ }
+
+ /* assign entry time - used for trimming */
+ entry.time = current_time ();
+ entry.op = (slapi_operation_parameters *)op;
+
+ /* construct the key */
+ key.data = csn_as_string(op->csn, PR_FALSE, csnStr);
+ key.size = CSN_STRSIZE;
+
+ /* construct the data */
+ data = (DBT *) slapi_ch_calloc(1, sizeof(DBT));
+ rc = _cl5Entry2DBData (&entry, (char**)&data->data, &data->size);
+ if (rc != CL5_SUCCESS)
+ {
+ char s[CSN_STRSIZE];
+ slapi_log_error(SLAPI_LOG_REPL, repl_plugin_name_cl,
+ "_cl5WriteOperation: failed to convert entry with csn (%s) "
+ "to db format\n", csn_as_string(op->csn,PR_FALSE,s));
+ goto done;
+ }
+
+ file = (CL5DBFile*)object_get_data (file_obj);
+ PR_ASSERT (file);
+
+ /* if this is part of ldif2cl - just write the entry without transaction */
+ if (s_cl5Desc.dbOpenMode == CL5_OPEN_LDIF2CL)
+ {
+ rc = file->db->put(file->db, NULL, &key, data, 0);
+ if (rc != 0)
+ {
+ slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name_cl,
+ "_cl5WriteOperation: failed to write entry; db error - %d %s\n",
+ rc, db_strerror(rc));
+ if (CL5_OS_ERR_IS_DISKFULL(rc))
+ {
+ cl5_set_diskfull();
+ }
+ rc = CL5_DB_ERROR;
+ }
+ goto done;
+ }
+
+ /* write the entry */
+ rc = EAGAIN;
+ cnt = 0;
+
+ while ((rc == EAGAIN || rc == DB_LOCK_DEADLOCK) && cnt < MAX_TRIALS)
+ {
+ if (cnt != 0)
+ {
+#if 1000*DB_VERSION_MAJOR + 100*DB_VERSION_MINOR < 4100
+ /* abort previous transaction */
+ rc = txn_abort (txnid);
+ if (rc != 0)
+ {
+ slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name_cl,
+ "_cl5WriteOperation: failed to abort transaction; db error - %d %s\n",
+ rc, db_strerror(rc));
+ rc = CL5_DB_ERROR;
+ goto done;
+ }
+#endif
+ /* back off */
+ interval = PR_MillisecondsToInterval(slapi_rand() % 100);
+ DS_Sleep(interval);
+ }
+#if 1000*DB_VERSION_MAJOR + 100*DB_VERSION_MINOR < 4100
+ /* begin transaction */
+ rc = txn_begin(s_cl5Desc.dbEnv, NULL /*pid*/, &txnid, 0);
+ if (rc != 0)
+ {
+ slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name_cl,
+ "_cl5WriteOperation: failed to start transaction; db error - %d %s\n",
+ rc, db_strerror(rc));
+ rc = CL5_DB_ERROR;
+ goto done;
+ }
+#endif
+
+ if ( file->sema )
+ {
+ PR_WaitSemaphore(file->sema);
+ }
+ rc = file->db->put(file->db, txnid, &key, data, DEFAULT_DB_OP_FLAGS);
+ if ( file->sema )
+ {
+ PR_PostSemaphore(file->sema);
+ }
+ if (CL5_OS_ERR_IS_DISKFULL(rc))
+ {
+ slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name_cl,
+ "_cl5WriteOperation: changelog (%s) DISK FULL; db error - %d %s\n",
+ s_cl5Desc.dbDir, rc, db_strerror(rc));
+ cl5_set_diskfull();
+ rc = CL5_DB_ERROR;
+ goto done;
+ }
+ if (cnt != 0)
+ {
+ if (rc == 0)
+ {
+ slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name_cl, "_cl5WriteOperation: retry (%d) the transaction (csn=%s) succeeded\n", cnt, (char*)key.data);
+ }
+ else if ((cnt + 1) >= MAX_TRIALS)
+ {
+ slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name_cl, "_cl5WriteOperation: retry (%d) the transaction (csn=%s) failed (rc=%d)\n", cnt, (char*)key.data, rc);
+ }
+ }
+ cnt ++;
+ }
+
+ if (rc == 0) /* we successfully added entry */
+ {
+#if 1000*DB_VERSION_MAJOR + 100*DB_VERSION_MINOR < 4100
+ rc = txn_commit (txnid, 0);
+#endif
+ }
+ else
+ {
+ char s[CSN_STRSIZE];
+ slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name_cl,
+ "_cl5WriteOperation: failed to write entry with csn (%s); "
+ "db error - %d %s\n", csn_as_string(op->csn,PR_FALSE,s),
+ rc, db_strerror(rc));
+#if 1000*DB_VERSION_MAJOR + 100*DB_VERSION_MINOR < 4100
+ rc = txn_abort (txnid);
+ if (rc != 0)
+ {
+ slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name_cl,
+ "_cl5WriteOperation: failed to abort transaction; db error - %d %s\n",
+ rc, db_strerror(rc));
+ }
+#endif
+ rc = CL5_DB_ERROR;
+ goto done;
+ }
+
+ /* update entry count - we assume that all entries are new */
+ PR_AtomicIncrement (&file->entryCount);
+
+ /* update purge vector if we have not seen any changes from this replica before */
+ _cl5UpdateRUV (file_obj, op->csn, PR_TRUE, PR_TRUE);
+
+ slapi_log_error(SLAPI_LOG_PLUGIN, repl_plugin_name_cl,
+ "cl5WriteOperation: successfully written entry with csn (%s)\n", csnStr);
+ rc = CL5_SUCCESS;
+done:
+ if (data->data)
+ slapi_ch_free ((void**)&data->data);
+ slapi_ch_free((void**) &data);
+
+ if (file_obj)
+ object_release (file_obj);
+
+ return rc;
+}
+
+static int _cl5GetFirstEntry (Object *obj, CL5Entry *entry, void **iterator, DB_TXN *txnid)
+{
+ int rc;
+ DBC *cursor = NULL;
+ DBT key={0}, data={0};
+ CL5Iterator *it;
+ CL5DBFile *file;
+
+ PR_ASSERT (obj && entry && iterator);
+
+ file = (CL5DBFile*)object_get_data (obj);
+ PR_ASSERT (file);
+ /* create cursor */
+ rc = file->db->cursor(file->db, txnid, &cursor, 0);
+ if (rc != 0)
+ {
+ slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name_cl,
+ "_cl5GetFirstEntry: failed to create cursor; db error - %d %s\n", rc, db_strerror(rc));
+ rc = CL5_DB_ERROR;
+ goto done;
+ }
+
+ key.flags = DB_DBT_MALLOC;
+ data.flags = DB_DBT_MALLOC;
+ while ((rc = cursor->c_get(cursor, &key, &data, DB_NEXT)) == 0)
+ {
+ /* skip service entries */
+ if (cl5HelperEntry ((char*)key.data, NULL))
+ {
+ free (key.data);
+ free (data.data);
+ continue;
+ }
+
+ /* format entry */
+ free (key.data);
+ rc = cl5DBData2Entry (data.data, data.size, entry);
+ free (data.data);
+ if (rc != 0)
+ {
+ slapi_log_error(SLAPI_LOG_REPL, repl_plugin_name_cl,
+ "_cl5GetFirstOperation: failed to format entry\n", rc);
+ goto done;
+ }
+
+ it = (CL5Iterator*)slapi_ch_malloc (sizeof (CL5Iterator));
+ it->cursor = cursor;
+ object_acquire (obj);
+ it->file = obj;
+ *(CL5Iterator**)iterator = it;
+
+ return CL5_SUCCESS;
+ }
+
+ /* walked of the end of the file */
+ if (rc == DB_NOTFOUND)
+ {
+ rc = CL5_NOTFOUND;
+ goto done;
+ }
+
+ /* db error occured while iterating */
+ if (rc != 0)
+ {
+ slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name_cl,
+ "_cl5GetFirstEntry: failed to get entry; db error - %d %s\n", rc, db_strerror(rc));
+ rc = CL5_DB_ERROR;
+ goto done;
+ }
+
+ /* successfully retrieved next entry but it was out of range */
+ if (rc == CL5_SUCCESS)
+ {
+ free (key.data);
+ free (data.data);
+ rc = CL5_NOTFOUND;
+ goto done;
+ }
+
+done:;
+ /* error occured */
+ /* We didn't success in assigning this cursor to the iterator,
+ * so we need to free the cursor here */
+ if (cursor)
+ cursor->c_close(cursor);
+
+ return rc;
+}
+
+static int _cl5GetNextEntry (CL5Entry *entry, void *iterator)
+{
+ int rc;
+ CL5Iterator *it;
+ DBT key={0}, data={0};
+
+ PR_ASSERT (entry && iterator);
+
+ it = (CL5Iterator*) iterator;
+
+ key.flags = DB_DBT_MALLOC;
+ data.flags = DB_DBT_MALLOC;
+ while ((rc = it->cursor->c_get(it->cursor, &key, &data, DB_NEXT)) == 0)
+ {
+ if (cl5HelperEntry ((char*)key.data, NULL))
+ {
+ free (key.data);
+ free (data.data);
+ continue;
+ }
+
+ free (key.data);
+ /* format entry */
+ rc = cl5DBData2Entry (data.data, data.size, entry);
+ free (data.data);
+ if (rc != 0)
+ {
+ slapi_log_error(SLAPI_LOG_REPL, repl_plugin_name_cl,
+ "_cl5GetNextEntry: failed to format entry\n", rc);
+ }
+
+ return rc;
+ }
+
+ /* walked of the end of the file or entry is out of range */
+ if (rc == 0 || rc == DB_NOTFOUND)
+ {
+ return CL5_NOTFOUND;
+ }
+
+ /* cursor operation failed */
+ if (rc != 0)
+ {
+ slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name_cl,
+ "_cl5GetNextEntry: failed to get entry; db error - %d %s\n", rc, db_strerror(rc));
+
+ return CL5_DB_ERROR;
+ }
+
+ return rc;
+}
+
+static int _cl5CurrentDeleteEntry (void *iterator)
+{
+ int rc;
+ CL5Iterator *it;
+ CL5DBFile *file;
+
+ PR_ASSERT (iterator);
+
+ it = (CL5Iterator*)iterator;
+
+ rc = it->cursor->c_del (it->cursor, 0);
+
+ if (rc == 0) {
+ /* decrement entry count */
+ file = (CL5DBFile*)object_get_data (it->file);
+ PR_AtomicDecrement (&file->entryCount);
+ return CL5_SUCCESS;
+ } else {
+ slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name_cl,
+ "_cl5CurrentDeleteEntry failed, err=%d %s\n",
+ rc, db_strerror(rc));
+ /* We don't free(close) the cursor here, as the caller will free it by a call to cl5DestroyIterator */
+ /* Freeing it here is a potential bug, as the cursor can't be referenced later once freed */
+ return CL5_DB_ERROR;
+ }
+}
+
+static PRBool _cl5IsValidIterator (const CL5Iterator *iterator)
+{
+ return (iterator && iterator->cursor && iterator->file);
+}
+
+static int _cl5GetOperation (Object *replica, slapi_operation_parameters *op)
+{
+ int rc;
+ DBT key={0}, data={0};
+ CL5DBFile *file;
+ CL5Entry entry;
+ Object *obj = NULL;
+ char csnStr[CSN_STRSIZE];
+
+ rc = _cl5GetDBFile (replica, &obj);
+ if (rc != CL5_SUCCESS)
+ {
+ return rc;
+ }
+
+ file = (CL5DBFile*)object_get_data (obj);
+ PR_ASSERT (file);
+
+ /* construct the key */
+ key.data = csn_as_string(op->csn, PR_FALSE, csnStr);
+ key.size = CSN_STRSIZE;
+
+ data.flags = DB_DBT_MALLOC;
+
+ rc = file->db->get(file->db, NULL/*txn*/, &key, &data, 0);
+ switch (rc)
+ {
+ case 0: entry.op = op;
+ /* Callers of this function should cl5_operation_parameters_done(op) */
+ rc = cl5DBData2Entry (data.data, data.size, &entry);
+ if (rc == CL5_SUCCESS)
+ {
+ slapi_log_error(SLAPI_LOG_PLUGIN, repl_plugin_name_cl,
+ "_cl5GetOperation: successfully retrieved operation with csn (%s)\n",
+ csnStr);
+ }
+ else
+ {
+ slapi_log_error(SLAPI_LOG_REPL, repl_plugin_name_cl,
+ "_cl5GetOperation: failed to convert db data to operation;"
+ " csn - %s\n", csnStr);
+ }
+ goto done;
+
+ case DB_NOTFOUND: slapi_log_error(SLAPI_LOG_REPL, repl_plugin_name_cl,
+ "_cl5GetOperation: operation for csn (%s) is not found in db that should contain dn (%s)\n",
+ csnStr, op->target_address.dn);
+ rc = CL5_NOTFOUND;
+ goto done;
+
+ default: slapi_log_error(SLAPI_LOG_REPL, repl_plugin_name_cl,
+ "_cl5GetOperation: failed to get entry for csn (%s); "
+ "db error - %d %s\n", csnStr, rc, db_strerror(rc));
+ rc = CL5_DB_ERROR;
+ goto done;
+ }
+
+done:;
+ if (obj)
+ object_release (obj);
+
+ if (data.data)
+ free (data.data);
+
+ return rc;
+}
+
+PRBool cl5HelperEntry (const char *csnstr, CSN *csnp)
+{
+ CSN *csn;
+ time_t csnTime;
+ PRBool retval = PR_FALSE;
+
+ if (csnp)
+ {
+ csn = csnp;
+ }
+ else
+ {
+ csn= csn_new_by_string(csnstr);
+ }
+ if (csn == NULL)
+ {
+ slapi_log_error(SLAPI_LOG_REPL, repl_plugin_name_cl,
+ "cl5HelperEntry: failed to get csn time; csn error\n");
+ return PR_FALSE;
+ }
+ csnTime= csn_get_time(csn);
+
+ if (csnTime == ENTRY_COUNT_TIME || csnTime == PURGE_RUV_TIME)
+ {
+ retval = PR_TRUE;
+ }
+
+ if (NULL == csnp)
+ csn_free(&csn);
+ return retval;
+}
+
+/* Replay iteration helper functions */
+static PRBool _cl5ValidReplayIterator (const CL5ReplayIterator *iterator)
+{
+ if (iterator == NULL ||
+ iterator->consumerRuv == NULL || iterator->supplierRuvObj == NULL ||
+ iterator->fileObj == NULL)
+ return PR_FALSE;
+
+ return PR_TRUE;
+}
+
+/* Algorithm: ONREPL!!!
+ */
+struct replica_hash_entry
+{
+ ReplicaId rid; /* replica id */
+ PRBool sendChanges; /* indicates whether changes should be sent for this replica */
+};
+
+
+static int _cl5PositionCursorForReplay (ReplicaId consumerRID, const RUV *consumerRuv,
+ Object *replica, Object *fileObj, CL5ReplayIterator **iterator)
+{
+ CLC_Buffer *clcache = NULL;
+ CL5DBFile *file;
+ int i;
+ CSN **csns = NULL;
+ CSN *startCSN = NULL;
+ char csnStr [CSN_STRSIZE];
+ int rc = CL5_SUCCESS;
+ Object *supplierRuvObj = NULL;
+ RUV *supplierRuv = NULL;
+ ReplicaId supplierRID;
+ PRBool newReplica;
+ PRBool haveChanges = PR_FALSE;
+ char *agmt_name;
+ ReplicaId rid;
+
+ PR_ASSERT (consumerRuv && replica && fileObj && iterator);
+ csnStr[0] = '\0';
+
+ file = (CL5DBFile*)object_get_data (fileObj);
+ supplierRID = replica_get_rid((Replica*)object_get_data(replica));
+
+ /* get supplier's RUV */
+ supplierRuvObj = replica_get_ruv((Replica*)object_get_data(replica));
+ PR_ASSERT (supplierRuvObj);
+ supplierRuv = (RUV*)object_get_data (supplierRuvObj);
+ PR_ASSERT (supplierRuv);
+
+ agmt_name = get_thread_private_agmtname();
+ slapi_log_error(SLAPI_LOG_REPL, NULL, "_cl5PositionCursorForReplay (%s): Consumer RUV:\n", agmt_name);
+ ruv_dump (consumerRuv, agmt_name, NULL);
+ slapi_log_error(SLAPI_LOG_REPL, NULL, "_cl5PositionCursorForReplay (%s): Supplier RUV:\n", agmt_name);
+ ruv_dump (supplierRuv, agmt_name, NULL);
+
+ /*
+ * get the sorted list of SupplierMinCSN (if no ConsumerMaxCSN)
+ * and ConsumerMaxCSN for those RIDs where consumer is not
+ * up-to-date.
+ */
+ csns = cl5BuildCSNList (consumerRuv, supplierRuv);
+ if (csns == NULL)
+ {
+ rc = CL5_NOTFOUND;
+ goto done;
+ }
+
+ /* iterate over elements of consumer's (and/or supplier's) ruv */
+ for (i = 0; csns[i]; i++)
+ {
+ CSN *consumerMaxCSN = NULL;
+
+ rid = csn_get_replicaid(csns[i]);
+
+ /*
+ * Skip CSN that is originated from the consumer.
+ * If RID==65535, the CSN is originated from a
+ * legacy consumer. In this case the supplier
+ * and the consumer may have the same RID.
+ */
+ if (rid == consumerRID && rid != MAX_REPLICA_ID)
+ continue;
+
+ startCSN = csns[i];
+ csn_as_string(startCSN, PR_FALSE, csnStr);
+
+ rc = clcache_get_buffer ( &clcache, file->db, consumerRID, consumerRuv, supplierRuv );
+ if ( rc != 0 ) goto done;
+
+ /* This is the first loading of this iteration. For replicas
+ * already known to the consumer, we exclude the last entry
+ * sent to the consumer by using DB_NEXT. However, for
+ * replicas new to the consumer, we include the first change
+ * ever generated by that replica.
+ */
+ newReplica = ruv_get_largest_csn_for_replica (consumerRuv, rid, &consumerMaxCSN);
+ csn_free(&consumerMaxCSN);
+ rc = clcache_load_buffer (clcache, startCSN, (newReplica ? DB_SET : DB_NEXT));
+
+ /* there is a special case which can occur just after migration - in this case,
+ the consumer RUV will contain the last state of the supplier before migration,
+ but the supplier will have an empty changelog, or the supplier changelog will
+ not contain any entries within the consumer min and max CSN - also, since
+ the purge RUV contains no CSNs, the changelog has never been purged
+ ASSUMPTIONS - it is assumed that the supplier had no pending changes to send
+ to any consumers; that is, we can assume that no changes were lost due to
+ either changelog purging or database reload - bug# 603061 - richm@netscape.com
+ */
+ if (rc == 0 || (rc == DB_NOTFOUND && !ruv_has_csns(file->purgeRUV)))
+ {
+ haveChanges = PR_TRUE;
+ rc = CL5_SUCCESS;
+ slapi_log_error(SLAPI_LOG_REPL, repl_plugin_name_cl,
+ "%s: CSN %s found, position set for replay\n", agmt_name, csnStr);
+ break;
+ }
+ else if (rc == DB_NOTFOUND) /* entry not found */
+ {
+ /* check whether this csn should be present */
+ rc = _cl5CheckMissingCSN (startCSN, supplierRuv, file);
+ if (rc == CL5_MISSING_DATA) /* we should have had the change but we don't */
+ {
+ slapi_log_error(SLAPI_LOG_REPL, repl_plugin_name_cl,
+ "%s: CSN %s not found, seems to be missing\n", agmt_name, csnStr);
+ break;
+ }
+ else /* we are not as up to date or we purged */
+ {
+ slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name_cl,
+ "%s: CSN %s not found, we aren't as up to date, or we purged\n",
+ agmt_name, csnStr);
+ continue;
+ }
+ }
+ else
+ {
+
+ /* db error */
+ slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name_cl,
+ "%s: Failed to retrieve change with CSN %s; db error - %d %s\n",
+ agmt_name, csnStr, rc, db_strerror(rc));
+ rc = CL5_DB_ERROR;
+ break;
+ }
+
+ } /* end for */
+
+ /* setup the iterator */
+ if (haveChanges)
+ {
+ *iterator = (CL5ReplayIterator*) slapi_ch_calloc (1, sizeof (CL5ReplayIterator));
+
+ if (*iterator == NULL)
+ {
+ slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name_cl,
+ "%s: _cl5PositionCursorForReplay: failed to allocate iterator\n", agmt_name);
+ rc = CL5_MEMORY_ERROR;
+ goto done;
+ }
+
+ /* ONREPL - should we make a copy of both RUVs here ?*/
+ (*iterator)->fileObj = fileObj;
+ (*iterator)->clcache = clcache; clcache = NULL;
+ (*iterator)->consumerRID = consumerRID;
+ (*iterator)->consumerRuv = consumerRuv;
+ (*iterator)->supplierRuvObj = supplierRuvObj;
+ }
+ else if (rc == CL5_SUCCESS)
+ {
+ /* we have no changes to send */
+ rc = CL5_NOTFOUND;
+ }
+
+done:
+ if ( clcache )
+ clcache_return_buffer ( &clcache );
+
+ if (csns)
+ cl5DestroyCSNList (&csns);
+
+ if (rc != CL5_SUCCESS)
+ {
+ if (supplierRuvObj)
+ object_release (supplierRuvObj);
+ }
+
+ return rc;
+}
+
+struct ruv_it
+{
+ CSN **csns; /* csn list */
+ int alloc; /* allocated size */
+ int pos; /* position in the list */
+};
+
+static int ruv_consumer_iterator (const ruv_enum_data *enum_data, void *arg)
+{
+ struct ruv_it *data = (struct ruv_it*)arg;
+
+ PR_ASSERT (data);
+
+ /* check if we have space for one more element */
+ if (data->pos >= data->alloc - 2)
+ {
+ data->alloc += 4;
+ data->csns = (CSN**) slapi_ch_realloc ((void*)data->csns, data->alloc * sizeof (CSN*));
+ }
+
+ data->csns [data->pos] = csn_dup (enum_data->csn);
+ data->pos ++;
+
+ return 0;
+}
+
+
+static int ruv_supplier_iterator (const ruv_enum_data *enum_data, void *arg)
+{
+ int i;
+ PRBool found = PR_FALSE;
+ ReplicaId rid;
+ struct ruv_it *data = (struct ruv_it*)arg;
+
+ PR_ASSERT (data);
+
+ rid = csn_get_replicaid (enum_data->min_csn);
+ /* check if the replica that generated the csn is already in the list */
+ for (i = 0; i < data->pos; i++)
+ {
+ if (rid == csn_get_replicaid (data->csns[i]))
+ {
+ found = PR_TRUE;
+
+ /* remove datacsn[i] if it is greater or equal to the supplier's maxcsn */
+ if ( csn_compare ( data->csns[i], enum_data->csn ) >= 0 )
+ {
+ int j;
+
+ csn_free ( & data->csns[i] );
+ for (j = i+1; j < data->pos; j++)
+ {
+ data->csns [j-1] = data->csns [j];
+ }
+ data->pos --;
+ }
+ break;
+ }
+ }
+
+ if (!found)
+ {
+ /* check if we have space for one more element */
+ if (data->pos >= data->alloc - 2)
+ {
+ data->alloc += 4;
+ data->csns = (CSN**)slapi_ch_realloc ((void*)data->csns,
+ data->alloc * sizeof (CSN*));
+ }
+
+ data->csns [data->pos] = csn_dup (enum_data->min_csn);
+ data->pos ++;
+ }
+ return 0;
+}
+
+
+
+static int
+my_csn_compare(const void *arg1, const void *arg2)
+{
+ return(csn_compare(*((CSN **)arg1), *((CSN **)arg2)));
+}
+
+
+
+/* builds CSN ordered list of all csns in the RUV */
+CSN** cl5BuildCSNList (const RUV *consRuv, const RUV *supRuv)
+{
+ struct ruv_it data;
+ int count, rc;
+ CSN **csns;
+
+ PR_ASSERT (consRuv);
+
+ count = ruv_replica_count (consRuv);
+ csns = (CSN**)slapi_ch_calloc (count + 1, sizeof (CSN*));
+
+ data.csns = csns;
+ data.alloc = count + 1;
+ data.pos = 0;
+
+ /* add consumer elements to the list */
+ rc = ruv_enumerate_elements (consRuv, ruv_consumer_iterator, &data);
+ if (rc == 0 && supRuv)
+ {
+ /* add supplier elements to the list */
+ rc = ruv_enumerate_elements (supRuv, ruv_supplier_iterator, &data);
+ }
+
+ /* we have no csns */
+ if (data.csns[0] == NULL)
+ {
+ /* csns might have been realloced in ruv_supplier_iterator() */
+ slapi_ch_free ((void**)&data.csns);
+ csns = NULL;
+ }
+ else
+ {
+ csns = data.csns;
+ data.csns [data.pos] = NULL;
+ if (rc == 0)
+ {
+ qsort (csns, data.pos, sizeof (CSN*), my_csn_compare);
+ }
+ else
+ {
+ cl5DestroyCSNList (&csns);
+ }
+ }
+
+ return csns;
+}
+
+void cl5DestroyCSNList (CSN*** csns)
+{
+ if (csns && *csns)
+ {
+ int i;
+
+ for (i = 0; (*csns)[i]; i++)
+ {
+ csn_free (&(*csns)[i]);
+ }
+
+ slapi_ch_free ((void**)csns);
+ }
+}
+
+/* A csn should be in the changelog if it is larger than purge vector csn for the same
+ replica and is smaller than the csn in supplier's ruv for the same replica.
+ The functions returns
+ CL5_PURGED if data was purged from the changelog or was never logged
+ because it was loaded as part of replica initialization
+ CL5_MISSING if the data erouneously missing
+ CL5_SUCCESS if that has not and should not been seen by the server
+ */
+static int _cl5CheckMissingCSN (const CSN *csn, const RUV *supplierRuv, CL5DBFile *file)
+{
+ ReplicaId rid;
+ CSN *supplierCsn = NULL;
+ CSN *purgeCsn = NULL;
+ int rc = CL5_SUCCESS;
+ char csnStr [CSN_STRSIZE];
+
+ PR_ASSERT (csn && supplierRuv && file);
+
+ rid = csn_get_replicaid (csn);
+ ruv_get_largest_csn_for_replica (supplierRuv, rid, &supplierCsn);
+ if (supplierCsn == NULL)
+ {
+ /* we have not seen any changes from this replica so it is
+ ok not to have this csn */
+ slapi_log_error(SLAPI_LOG_REPL, repl_plugin_name_cl, "_cl5CheckMissingCSN: "
+ "can't locate %s csn: we have not seen any changes for replica %d\n",
+ csn_as_string (csn, PR_FALSE, csnStr), rid);
+ return CL5_SUCCESS;
+ }
+
+ ruv_get_largest_csn_for_replica (file->purgeRUV, rid, &purgeCsn);
+ if (purgeCsn == NULL)
+ {
+ /* changelog never contained any changes for this replica */
+ if (csn_compare (csn, supplierCsn) <= 0)
+ {
+ slapi_log_error(SLAPI_LOG_REPL, repl_plugin_name_cl, "_cl5CheckMissingCSN: "
+ "the change with %s csn was never logged because it was imported "
+ "during replica initialization\n", csn_as_string (csn, PR_FALSE, csnStr));
+ rc = CL5_PURGED_DATA; /* XXXggood is that the correct return value? */
+ }
+ else
+ {
+ slapi_log_error(SLAPI_LOG_REPL, repl_plugin_name_cl, "_cl5CheckMissingCSN: "
+ "change with %s csn has not yet been seen by this server; "
+ " last csn seen from that replica is %s\n",
+ csn_as_string (csn, PR_FALSE, csnStr),
+ csn_as_string (supplierCsn, PR_FALSE, csnStr));
+ rc = CL5_SUCCESS;
+ }
+ }
+ else /* we have both purge and supplier csn */
+ {
+ if (csn_compare (csn, purgeCsn) < 0) /* the csn is below the purge point */
+ {
+ rc = CL5_PURGED_DATA;
+ }
+ else
+ {
+ if (csn_compare (csn, supplierCsn) <= 0) /* we should have the data but we don't */
+ {
+ slapi_log_error(SLAPI_LOG_REPL, repl_plugin_name_cl, "_cl5CheckMissingCSN: "
+ "change with %s csn has been purged by this server; "
+ "the current purge point for that replica is %s\n",
+ csn_as_string (csn, PR_FALSE, csnStr),
+ csn_as_string (purgeCsn, PR_FALSE, csnStr));
+ rc = CL5_MISSING_DATA;
+ }
+ else
+ {
+ slapi_log_error(SLAPI_LOG_REPL, repl_plugin_name_cl, "_cl5CheckMissingCSN: "
+ "change with %s csn has not yet been seen by this server; "
+ " last csn seen from that replica is %s\n",
+ csn_as_string (csn, PR_FALSE, csnStr),
+ csn_as_string (supplierCsn, PR_FALSE, csnStr));
+ rc = CL5_SUCCESS;
+ }
+ }
+ }
+
+ if (supplierCsn)
+ csn_free (&supplierCsn);
+
+ if (purgeCsn)
+ csn_free (&purgeCsn);
+
+ return rc;
+}
+
+/* Helper functions that work with individual changelog files */
+
+/* file name format : <replica name>_<replica generation>db{2,3} */
+static PRBool _cl5FileName2Replica (const char *file_name, Object **replica)
+{
+ Replica *r;
+ char *repl_name, *file_gen, *repl_gen;
+ int len;
+
+ PR_ASSERT (file_name && replica);
+
+ *replica = NULL;
+
+ /* this is database file */
+ if (_cl5FileEndsWith (file_name, DB_EXTENSION) ||
+ _cl5FileEndsWith (file_name, DB_EXTENSION_DB3) )
+ {
+ repl_name = slapi_ch_strdup (file_name);
+ file_gen = strstr(repl_name, FILE_SEP);
+ if (file_gen)
+ {
+ int extlen = strlen(DB_EXTENSION);
+ *file_gen = '\0';
+ file_gen += strlen (FILE_SEP);
+ len = strlen (file_gen);
+ if (len <= extlen + 1)
+ {
+ slapi_log_error(SLAPI_LOG_REPL, repl_plugin_name_cl, "_cl5FileName2Replica "
+ "invalid file name (%s)\n", file_name);
+ }
+ else
+ {
+ /* get rid of the file extension */
+ file_gen [len - extlen - 1] = '\0';
+ *replica = replica_get_by_name (repl_name);
+ if (*replica)
+ {
+ /* check that generation matches the one in replica object */
+ r = (Replica*)object_get_data (*replica);
+ repl_gen = replica_get_generation (r);
+ PR_ASSERT (repl_gen);
+ if (strcmp (file_gen, repl_gen) != 0)
+ {
+ slapi_log_error(SLAPI_LOG_REPL, repl_plugin_name_cl, "_cl5FileName2Replica "
+ "replica generation mismatch for replica at (%s), "
+ "file generation %s, new replica generation %s\n",
+ slapi_sdn_get_dn (replica_get_root (r)), file_gen, repl_gen);
+
+ object_release (*replica);
+ *replica = NULL;
+ }
+ slapi_ch_free ((void**)&repl_gen);
+ }
+ }
+ slapi_ch_free ((void**)&repl_name);
+ }
+ else
+ {
+ slapi_log_error(SLAPI_LOG_REPL, repl_plugin_name_cl, "_cl5FileName2Replica "
+ "malformed file name - %s\n", file_name);
+ }
+
+ return PR_TRUE;
+ }
+ else
+ return PR_FALSE;
+}
+
+/* file name format : <replica name>_<replica generation>db{2,3} */
+static char* _cl5Replica2FileName (Object *replica)
+{
+ const char *replName;
+ char *replGen, *fileName;
+ Replica *r;
+
+ PR_ASSERT (replica);
+
+ r = (Replica*)object_get_data (replica);
+ PR_ASSERT (r);
+
+ replName = replica_get_name (r);
+ replGen = replica_get_generation (r);
+
+ fileName = _cl5MakeFileName (replName, replGen) ;
+
+ slapi_ch_free ((void**)&replGen);
+
+ return fileName;
+}
+
+static char* _cl5MakeFileName (const char *replName, const char *replGen)
+{
+ char *fileName;
+ fileName = slapi_ch_malloc (strlen (replName) + strlen (replGen) +
+ strlen (DB_EXTENSION) + 3/* '_' + '.' + '\0' */);
+ sprintf (fileName, "%s%s%s.%s", replName, FILE_SEP, replGen, DB_EXTENSION);
+
+ return fileName;
+}
+
+/* open file that corresponds to a particular database */
+static int _cl5DBOpenFile (Object *replica, Object **obj, PRBool checkDups)
+{
+ int rc;
+ const char *replName;
+ char *replGen;
+ Replica *r;
+
+ PR_ASSERT (replica);
+
+ r = (Replica*)object_get_data (replica);
+ replName = replica_get_name (r);
+ PR_ASSERT (replName);
+ replGen = replica_get_generation (r);
+ PR_ASSERT (replGen);
+
+ rc = _cl5DBOpenFileByReplicaName (replName, replGen, obj, checkDups);
+
+ slapi_ch_free ((void**)&replGen);
+
+ return rc;
+}
+
+static int _cl5DBOpenFileByReplicaName (const char *replName, const char *replGen,
+ Object **obj, PRBool checkDups)
+{
+ int rc = CL5_SUCCESS;
+ Object *tmpObj;
+ CL5DBFile *file;
+ char *file_name;
+
+ PR_ASSERT (replName && replGen);
+
+ if (checkDups)
+ {
+ PR_Lock (s_cl5Desc.fileLock);
+ file_name = _cl5MakeFileName (replName, replGen);
+ tmpObj = objset_find (s_cl5Desc.dbFiles, _cl5CompareDBFile, file_name);
+ slapi_ch_free((void **)&file_name);
+ file_name = NULL;
+ if (tmpObj) /* this file already exist */
+ {
+ /* if we were asked for file handle - keep the handle */
+ if (obj)
+ {
+ *obj = tmpObj;
+ }
+ else
+ {
+ object_release (tmpObj);
+ }
+
+ rc = CL5_SUCCESS;
+ goto done;
+ }
+ }
+
+ rc = _cl5NewDBFile (replName, replGen, &file);
+ if (rc == CL5_SUCCESS)
+ {
+ /* This creates the file but doesn't set the init flag
+ * The flag is set later when the purge and max ruvs are set.
+ * This is to prevent some thread to get file access before the
+ * structure is fully initialized */
+ rc = _cl5AddDBFile (file, &tmpObj);
+ if (rc == CL5_SUCCESS)
+ {
+ /* read purge RUV - done here because it needs file object rather than file pointer */
+ rc = _cl5ReadRUV (replGen, tmpObj, PR_TRUE);
+ if (rc != CL5_SUCCESS)
+ {
+ slapi_log_error(SLAPI_LOG_REPL, repl_plugin_name_cl,
+ "_cl5DBOpenFileByReplicaName: failed to get purge RUV\n");
+ goto done;
+ }
+
+ /* read ruv that represents the upper bound of the changes stored in the file */
+ rc = _cl5ReadRUV (replGen, tmpObj, PR_FALSE);
+ if (rc != CL5_SUCCESS)
+ {
+ slapi_log_error(SLAPI_LOG_REPL, repl_plugin_name_cl,
+ "_cl5DBOpenFileByReplicaName: failed to get upper bound RUV\n");
+ goto done;
+ }
+
+ /* Mark the DB File initialize */
+ _cl5DBFileInitialized(tmpObj);
+
+ if (obj)
+ {
+ *obj = tmpObj;
+ }
+ else
+ {
+ object_release (tmpObj);
+ }
+ }
+ }
+
+done:;
+ if (rc != CL5_SUCCESS)
+ {
+ if (file)
+ _cl5DBCloseFile ((void**)&file);
+ }
+
+ if (checkDups)
+ {
+ PR_Unlock (s_cl5Desc.fileLock);
+ }
+
+ return rc;
+}
+
+/* adds file to the db file list */
+static int _cl5AddDBFile (CL5DBFile *file, Object **obj)
+{
+ int rc;
+ Object *tmpObj;
+
+ PR_ASSERT (file);
+
+ tmpObj = object_new (file, _cl5DBCloseFile);
+ rc = objset_add_obj(s_cl5Desc.dbFiles, tmpObj);
+ if (rc != OBJSET_SUCCESS)
+ {
+ slapi_log_error(SLAPI_LOG_REPL, repl_plugin_name_cl,
+ "_cl5AddDBFile: failed to add db file to the list; "
+ "repl_objset error - %d\n", rc);
+ object_release (tmpObj);
+ return CL5_OBJSET_ERROR;
+ }
+
+ if (obj)
+ {
+ *obj = tmpObj;
+ }
+ else
+ object_release (tmpObj);
+
+ return CL5_SUCCESS;
+}
+
+static int _cl5NewDBFile (const char *replName, const char *replGen, CL5DBFile** dbFile)
+{
+ int rc;
+ DB *db = NULL;
+ char *name;
+ char *semadir;
+#ifdef HPUX
+ char cwd [PATH_MAX+1];
+#endif
+
+ PR_ASSERT (replName && replGen && dbFile);
+
+ (*dbFile) = (CL5DBFile *)slapi_ch_calloc (1, sizeof (CL5DBFile));
+ if (*dbFile == NULL)
+ {
+ slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name_cl,
+ "_cl5NewDBFile: memory allocation failed\n");
+ return CL5_MEMORY_ERROR;
+ }
+
+ name = _cl5MakeFileName (replName, replGen);
+ {
+ /* The subname argument allows applications to have
+ * subdatabases, i.e., multiple databases inside of a single
+ * physical file. This is useful when the logical databases
+ * are both numerous and reasonably small, in order to
+ * avoid creating a large number of underlying files.
+ */
+ char *subname = NULL;
+ DB_ENV *dbEnv = s_cl5Desc.dbEnv;
+
+ rc = db_create(&db, dbEnv, 0);
+ if (0 != rc) {
+ goto out;
+ }
+
+ rc = db->set_pagesize(
+ db,
+ s_cl5Desc.dbConfig.pageSize);
+
+ if (0 != rc) {
+ goto out;
+ }
+
+#if 1000*DB_VERSION_MAJOR + 100*DB_VERSION_MINOR < 3300
+ rc = db->set_malloc(db, malloc);
+ if (0 != rc) {
+ goto out;
+ }
+#endif
+
+ DB_OPEN(s_cl5Desc.dbEnvOpenFlags,
+ db, NULL /* txnid */, name, subname, DB_BTREE,
+ DB_CREATE | DB_THREAD, s_cl5Desc.dbConfig.fileMode, rc);
+ }
+out:
+ if (rc != 0)
+ {
+ slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name_cl,
+ "_cl5NewDBFile: db_open failed; db error - %d %s\n",
+ rc, db_strerror(rc));
+ rc = CL5_DB_ERROR;
+ goto done;
+ }
+
+ (*dbFile)->db = db;
+ (*dbFile)->name = name;
+ (*dbFile)->replName = slapi_ch_strdup (replName);
+ (*dbFile)->replGen = slapi_ch_strdup (replGen);
+
+ /*
+ * Considerations for setting up cl semaphore:
+ * (1) The NT version of SleepyCat uses test-and-set mutexes
+ * at the DB page level instead of blocking mutexes. That has
+ * proven to be a killer for the changelog DB, as this DB is
+ * accessed by multiple a reader threads (the repl thread) and
+ * writer threads (the server ops threads) usually at the last
+ * pages of the DB, due to the sequential nature of the changelog
+ * keys. To avoid the test-and-set mutexes, we could use semaphore
+ * to serialize the writers and avoid the high mutex contention
+ * that SleepyCat is unable to avoid.
+ * (2) [610948] Linux master hangs for 2 hours
+ * [611239] _cl5DeadlockMain: lock_detect succeeded
+ * (3) DS 6.2 introduced the semaphore on all platforms (replaced
+ * the serial lock used on Windows and Linux described above).
+ * The number of the concurrent writes now is configurable by
+ * nsslapd-changelogmaxconcurrentwrites (the server needs to
+ * be restarted).
+ */
+
+ semadir = s_cl5Desc.dbDir;
+#ifdef HPUX
+ /*
+ * HP sem_open() does not allow pathname component "./" or "../"
+ * in the semaphore name. For simplicity and to avoid doing
+ * chdir() in multi-thread environment, current working dir
+ * (log dir) is used to replace the original semaphore dir
+ * if it contains "./".
+ */
+ if ( strstr ( semadir, "./" ) != NULL && getcwd ( cwd, PATH_MAX+1 ) != NULL )
+ {
+ semadir = cwd;
+ }
+#endif
+
+ if ( semadir != NULL )
+ {
+ (*dbFile)->semaName = slapi_ch_malloc (strlen(semadir) + strlen(replName) + strlen(".sema") + 10);
+ sprintf ((*dbFile)->semaName, "%s/%s.sema", semadir, replName);
+ slapi_log_error(SLAPI_LOG_REPL, repl_plugin_name_cl,
+ "_cl5NewDBFile: semaphore %s\n", (*dbFile)->semaName);
+ (*dbFile)->sema = PR_OpenSemaphore((*dbFile)->semaName, PR_SEM_CREATE, 0666, s_cl5Desc.dbConfig.maxConcurrentWrites );
+ slapi_log_error (SLAPI_LOG_REPL, repl_plugin_name_cl, "_cl5NewDBFile: maxConcurrentWrites=%d\n", s_cl5Desc.dbConfig.maxConcurrentWrites );
+ }
+
+ if ((*dbFile)->sema == NULL )
+ {
+ slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name_cl,
+ "_cl5NewDBFile: failed to create semaphore %s; NSPR error - %d\n",
+ (*dbFile)->semaName ? (*dbFile)->semaName : "(nil)", PR_GetError ());
+ rc = CL5_SYSTEM_ERROR;
+ goto done;
+ }
+
+ /* compute number of entries in the file */
+ /* ONREPL - to improve performance, we keep entry count in memory
+ and write it down during shutdown. Problem: this will not
+ work with multiple processes. Do we have to worry about that?
+ */
+ if (s_cl5Desc.dbOpenMode == CL5_OPEN_NORMAL)
+ {
+ rc = _cl5GetEntryCount (*dbFile);
+ if (rc != CL5_SUCCESS)
+ {
+ slapi_log_error(SLAPI_LOG_REPL, repl_plugin_name_cl,
+ "_cl5NewDBFile: failed to get entry count\n");
+ goto done;
+ }
+ }
+
+done:
+ if (rc != CL5_SUCCESS)
+ {
+ if (dbFile)
+ _cl5DBCloseFile ((void**)dbFile);
+ /* slapi_ch_free accepts NULL pointer */
+ slapi_ch_free ((void**)&name);
+
+ slapi_ch_free ((void**)dbFile);
+ }
+
+ return rc;
+}
+
+static void _cl5DBCloseFile (void **data)
+{
+ CL5DBFile *file;
+ char fullpathname[MAXPATHLEN];
+
+ PR_ASSERT (data);
+
+ file = *(CL5DBFile**)data;
+
+ /* close the file */
+ /* if this is normal close or close after import, update entry count */
+ if ((s_cl5Desc.dbOpenMode == CL5_OPEN_NORMAL && s_cl5Desc.dbState == CL5_STATE_CLOSING) ||
+ s_cl5Desc.dbOpenMode == CL5_OPEN_LDIF2CL)
+ {
+ _cl5WriteEntryCount (file);
+ _cl5WriteRUV (file, PR_TRUE);
+ _cl5WriteRUV (file, PR_FALSE);
+ }
+
+ /* close file */
+ if (file->db)
+ file->db->close(file->db, 0);
+
+ if (file->flags & DB_FILE_DELETED)
+ {
+ PR_snprintf(fullpathname, MAXPATHLEN, "%s/%s", s_cl5Desc.dbDir, file->name);
+ if (PR_Delete(fullpathname) != PR_SUCCESS)
+ {
+ slapi_log_error(SLAPI_LOG_REPL, repl_plugin_name_cl, "_cl5DBCloseFile: "
+ "failed to remove (%s) file; NSPR error - %d\n", file->name, PR_GetError ());
+
+ }
+ }
+
+ /* slapi_ch_free accepts NULL pointer */
+ slapi_ch_free ((void**)&file->name);
+ slapi_ch_free ((void**)&file->replName);
+ slapi_ch_free ((void**)&file->replGen);
+ if (file->sema) {
+ PR_CloseSemaphore (file->sema);
+ PR_DeleteSemaphore (file->semaName);
+ file->sema = NULL;
+ }
+ slapi_ch_free ((void**)&file->semaName);
+
+ slapi_ch_free (data);
+}
+
+static int _cl5GetDBFile (Object *replica, Object **obj)
+{
+ char *fileName;
+
+ PR_ASSERT (replica && obj);
+
+ fileName = _cl5Replica2FileName (replica);
+
+ *obj = objset_find(s_cl5Desc.dbFiles, _cl5CompareDBFile, fileName);
+ slapi_ch_free ((void**)&fileName);
+ if (*obj)
+ {
+ return CL5_SUCCESS;
+ }
+ else
+ {
+ return CL5_NOTFOUND;
+ }
+}
+
+static int _cl5GetDBFileByReplicaName (const char *replName, const char *replGen,
+ Object **obj)
+{
+ char *fileName;
+
+ PR_ASSERT (replName && replGen && obj);
+
+ fileName = _cl5MakeFileName (replName, replGen);
+
+ *obj = objset_find(s_cl5Desc.dbFiles, _cl5CompareDBFile, fileName);
+ slapi_ch_free ((void**)&fileName);
+ if (*obj)
+ {
+ return CL5_SUCCESS;
+ }
+ else
+ {
+ return CL5_NOTFOUND;
+ }
+}
+
+static void _cl5DBDeleteFile (Object *obj)
+{
+ CL5DBFile *file;
+
+ PR_ASSERT (obj);
+
+ file = (CL5DBFile*)object_get_data (obj);
+ PR_ASSERT (file);
+ file->flags |= DB_FILE_DELETED;
+ objset_remove_obj(s_cl5Desc.dbFiles, obj);
+ object_release (obj);
+}
+
+static void _cl5DBFileInitialized (Object *obj)
+{
+ CL5DBFile *file;
+
+ PR_ASSERT (obj);
+
+ file = (CL5DBFile*)object_get_data (obj);
+ PR_ASSERT (file);
+ file->flags |= DB_FILE_INIT;
+}
+
+static int _cl5CompareDBFile (Object *el1, const void *el2)
+{
+ CL5DBFile *file;
+ const char *name;
+
+ PR_ASSERT (el1 && el2);
+
+ file = (CL5DBFile*) object_get_data (el1);
+ name = (const char*) el2;
+ return ((file->flags & DB_FILE_INIT) ? strcmp (file->name, name) : 1);
+}
+
+static int _cl5CopyDBFiles (const char *srcDir, const char *destDir, Object **replicas)
+{
+ char srcFile [MAXPATHLEN + 1];
+ char destFile[MAXPATHLEN + 1];
+ int rc;
+ Object *obj;
+ CL5DBFile *file;
+
+ /* ONREPL currently, dbidlist is ignored because db code can't handle discrepancy between
+ transaction log and present files; this should change before 5.0 ships */
+ obj = objset_first_obj (s_cl5Desc.dbFiles);
+ while (obj)
+ {
+ file = (CL5DBFile*)object_get_data (obj);
+ PR_ASSERT (file);
+
+ PR_snprintf(srcFile, MAXPATHLEN, "%s/%s", srcDir, file->name);
+ PR_snprintf(destFile, MAXPATHLEN, "%s/%s", destDir, file->name);
+ rc = copyfile(srcFile, destFile, 0, FILE_CREATE_MODE);
+ if (rc != 0)
+ {
+ object_release (obj);
+ slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name_cl,
+ "_cl5CopyDBFiles: failed to copy %s from %s to %s\n",
+ file, srcDir, destDir);
+ return CL5_SYSTEM_ERROR;
+ }
+
+ obj = objset_next_obj (s_cl5Desc.dbFiles, obj);
+ }
+
+ return CL5_SUCCESS;
+}
+
+/*
+ * return 1: true (the "filename" ends with "ext")
+ * return 0: false
+ */
+static int _cl5FileEndsWith(const char *filename, const char *ext)
+{
+ char *p = NULL;
+ int flen = strlen(filename);
+ int elen = strlen(ext);
+ if (0 == flen || 0 == elen)
+ {
+ return 0;
+ }
+ p = strstr(filename, ext);
+ if (NULL == p)
+ {
+ return 0;
+ }
+ if (p - filename + elen == flen)
+ {
+ return 1;
+ }
+ return 0;
+}
+
+static int _cl5ExportFile (PRFileDesc *prFile, Object *obj)
+{
+ int rc;
+ void *iterator = NULL;
+ slapi_operation_parameters op = {0};
+ char *buff;
+ PRInt32 len, wlen;
+ CL5Entry entry;
+ CL5DBFile *file;
+
+ PR_ASSERT (prFile && obj);
+
+ file = (CL5DBFile*)object_get_data (obj);
+ PR_ASSERT (file);
+
+ ruv_dump (file->purgeRUV, "clpurgeruv", prFile);
+ ruv_dump (file->maxRUV, "clmaxruv", prFile);
+ slapi_write_buffer (prFile, "\n", strlen("\n"));
+
+ entry.op = &op;
+ rc = _cl5GetFirstEntry (obj, &entry, &iterator, NULL);
+ while (rc == CL5_SUCCESS)
+ {
+ rc = _cl5Operation2LDIF (&op, file->replGen, &buff, &len);
+ if (rc != CL5_SUCCESS)
+ {
+ slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name_cl,
+ "_cl5ExportLDIF: failed to convert operation to ldif\n");
+ operation_parameters_done (&op);
+ break;
+ }
+
+ wlen = slapi_write_buffer (prFile, buff, len);
+ slapi_ch_free((void **)&buff);
+ if (wlen < len)
+ {
+ slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name_cl,
+ "_cl5ExportLDIF: failed to write to ldif file\n");
+ rc = CL5_SYSTEM_ERROR;
+ operation_parameters_done (&op);
+ break;
+ }
+
+ cl5_operation_parameters_done (&op);
+
+ rc = _cl5GetNextEntry (&entry, iterator);
+ }
+
+ cl5_operation_parameters_done (&op);
+
+ if (iterator)
+ cl5DestroyIterator (iterator);
+
+ if (rc != CL5_NOTFOUND)
+ {
+ slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name_cl,
+ "_cl5ExportLDIF: failed to retrieve changelog entry\n");
+ }
+ else
+ {
+ rc = CL5_SUCCESS;
+ }
+
+ return rc;
+}
+
+static PRBool _cl5ReplicaInList (Object *replica, Object **replicas)
+{
+ int i;
+
+ PR_ASSERT (replica && replicas);
+
+ /* ONREPL I think it should be sufficient to just compare replica pointers */
+ for (i=0; replicas[i]; i++)
+ {
+ if (replica == replicas[i])
+ return PR_TRUE;
+ }
+
+ return PR_FALSE;
+}
+
+static char* _cl5GetHelperEntryKey (int type, char *csnStr)
+{
+ CSN *csn= csn_new();
+ char *rt;
+
+ csn_set_time(csn, type);
+ csn_set_replicaid(csn, 0);
+
+ rt = csn_as_string(csn, PR_FALSE, csnStr);
+ csn_free(&csn);
+
+ return rt;
+}
+
+static Object* _cl5GetReplica (const slapi_operation_parameters *op, const char* replGen)
+{
+ Slapi_DN *sdn;
+ Object *replObj;
+ Replica *replica;
+ char *newGen;
+
+ PR_ASSERT (op && replGen);
+
+ sdn = slapi_sdn_new_dn_byref(op->target_address.dn);
+
+ replObj = replica_get_replica_from_dn (sdn);
+ if (replObj)
+ {
+ /* check to see if replica generation has not change */
+ replica = (Replica*)object_get_data (replObj);
+ PR_ASSERT (replica);
+ newGen = replica_get_generation (replica);
+ PR_ASSERT (newGen);
+ if (strcmp (replGen, newGen) != 0)
+ {
+ object_release (replObj);
+ replObj = NULL;
+ }
+
+ slapi_ch_free ((void**)&replGen);
+ }
+
+ slapi_sdn_free (&sdn);
+
+ return replObj;
+}
+
+int
+cl5_is_diskfull()
+{
+ int rc;
+ PR_Lock(cl5_diskfull_lock);
+ rc = cl5_diskfull_flag;
+ PR_Unlock(cl5_diskfull_lock);
+ return rc;
+}
+
+static void
+cl5_set_diskfull()
+{
+ PR_Lock(cl5_diskfull_lock);
+ cl5_diskfull_flag = 1;
+ PR_Unlock(cl5_diskfull_lock);
+}
+
+static void
+cl5_set_no_diskfull()
+{
+ PR_Lock(cl5_diskfull_lock);
+ cl5_diskfull_flag = 0;
+ PR_Unlock(cl5_diskfull_lock);
+}
+
+int
+cl5_diskspace_is_available()
+{
+ int rval = 1;
+
+#if defined( OS_solaris ) || defined( hpux )
+ struct statvfs fsbuf;
+ if (statvfs(s_cl5Desc.dbDir, &fsbuf) < 0)
+ {
+ slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name_cl,
+ "cl5_diskspace_is_available: Cannot get file system info\n");
+ rval = 0;
+ }
+ else
+ {
+ unsigned long fsiz = fsbuf.f_bavail * fsbuf.f_frsize;
+ if (fsiz < NO_DISK_SPACE)
+ {
+ slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name_cl,
+ "cl5_diskspace_is_available: No enough diskspace for changelog: (%u bytes free)\n", fsiz);
+ rval = 0;
+ }
+ else if (fsiz > MIN_DISK_SPACE)
+ {
+ /* assume recovered */
+ cl5_set_no_diskfull();
+ }
+ }
+#endif
+#if defined( linux )
+ struct statfs fsbuf;
+ if (statfs(s_cl5Desc.dbDir, &fsbuf) < 0)
+ {
+ slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name_cl,
+ "cl5_diskspace_is_available: Cannot get file system info\n");
+ rval = 0;
+ }
+ else
+ {
+ unsigned long fsiz = fsbuf.f_bavail * fsbuf.f_bsize;
+ if (fsiz < NO_DISK_SPACE)
+ {
+ slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name_cl,
+ "cl5_diskspace_is_available: No enough diskspace for changelog: (%u bytes free)\n", fsiz);
+ rval = 0;
+ }
+ else if (fsiz > MIN_DISK_SPACE)
+ {
+ /* assume recovered */
+ cl5_set_no_diskfull();
+ }
+ }
+#endif
+ return rval;
+}
diff --git a/ldap/servers/plugins/replication/cl5_api.h b/ldap/servers/plugins/replication/cl5_api.h
new file mode 100644
index 00000000..49296df2
--- /dev/null
+++ b/ldap/servers/plugins/replication/cl5_api.h
@@ -0,0 +1,478 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+/* cl5_api.h - interface to 5.0 changelog */
+
+#ifndef CL5_API_H
+#define CL5_API_H
+
+#include "repl5.h"
+#include "repl5_prot_private.h"
+
+#define CL5_TYPE "Changelog5" /* changelog type */
+#define VERSION_SIZE 127 /* size of the buffer to hold changelog version */
+#define CL5_DEFAULT_CONFIG -1 /* value that indicates to changelog to use default */
+#define CL5_STR_IGNORE "-1" /* tels function to ignore this parameter */
+#define CL5_NUM_IGNORE -1 /* tels function to ignore this parameter */
+#define CL5_STR_UNLIMITED "0" /* represent unlimited value (trimming ) */
+#define CL5_NUM_UNLIMITED 0 /* represent unlimited value (trimming ) */
+
+#define CL5_OS_ERR_IS_DISKFULL(err) ((err)==ENOSPC || (err)==EFBIG)
+
+/***** Data Structures *****/
+
+/* changelog configuration structure */
+typedef struct cl5dbconfig
+{
+ size_t cacheSize; /* cache size in bytes */
+ PRBool durableTrans; /* flag that tells not to sync log when trans commits */
+ PRInt32 checkpointInterval; /* checkpoint interval in seconds */
+ PRBool circularLogging; /* flag to archive and trancate log */
+ size_t pageSize; /* page size in bytes */
+ size_t logfileSize; /* maximum log size in bytes */
+ size_t maxTxnSize; /* maximum txn table size in count*/
+ PRInt32 fileMode; /* file mode */
+ PRBool verbose; /* Get libdb to exhale debugging info */
+ PRBool debug; /* Will libdb emit debugging info into our log ? */
+ PRInt32 tricklePercentage; /* guaranteed percentage of clean cache pages; 0 - 100 */
+ PRInt32 spinCount; /* DB Mutex spin count */
+ PRUint32 nb_lock_config; /* Number of locks in the DB lock table. New in 5.1 */
+/* The next 2 parameters are needed for configuring the changelog cache. New in 5.1 */
+ PRUint32 maxChCacheEntries;
+ PRUint32 maxChCacheSize;
+ PRUint32 maxConcurrentWrites; /* 6.2 max number of concurrent cl writes */
+} CL5DBConfig;
+
+/* changelog entry format */
+typedef struct cl5entry
+{
+ slapi_operation_parameters *op; /* operation applied to the server */
+ time_t time; /* time added to the cl; used for trimming */
+} CL5Entry;
+
+/* default values for the changelog configuration structure above */
+/*
+ * For historical reasons, dbcachesize refers to number of bytes at the DB level,
+ * whereas cachesize refers to number of entries at the changelog cache level (cachememsize is the
+ * one refering to number of bytes at the changelog cache level)
+ */
+#define CL5_DEFAULT_CONFIG_DB_DBCACHESIZE 10485760 /* 10M bytes */
+#define CL5_DEFAULT_CONFIG_DB_DURABLE_TRANSACTIONS 1
+#define CL5_DEFAULT_CONFIG_DB_CHECKPOINT_INTERVAL 60
+#define CL5_DEFAULT_CONFIG_DB_CIRCULAR_LOGGING 1
+#define CL5_DEFAULT_CONFIG_DB_PAGE_SIZE 8*1024
+#define CL5_DEFAULT_CONFIG_DB_LOGFILE_SIZE 0
+#define CL5_DEFAULT_CONFIG_DB_VERBOSE 0
+#define CL5_DEFAULT_CONFIG_DB_DEBUG 0
+#define CL5_DEFAULT_CONFIG_DB_TRICKLE_PERCENTAGE 40
+#define CL5_DEFAULT_CONFIG_DB_SPINCOUNT 0
+#define CL5_DEFAULT_CONFIG_DB_TXN_MAX 200
+#define CL5_DEFAULT_CONFIG_CACHESIZE 3000 /* number of entries */
+#define CL5_DEFAULT_CONFIG_CACHEMEMSIZE 1048576 /* 1 M bytes */
+#define CL5_DEFAULT_CONFIG_NB_LOCK 1000 /* Number of locks in the lock table of the DB */
+
+/*
+ * Small number of concurrent writes degradate the throughput.
+ * Large one increases deadlock.
+ */
+#ifdef SOLARIS
+#define CL5_DEFAULT_CONFIG_MAX_CONCURRENT_WRITES 10
+#else
+#define CL5_DEFAULT_CONFIG_MAX_CONCURRENT_WRITES 2
+#endif
+
+
+#define CL5_MIN_DB_DBCACHESIZE 524288 /* min 500K bytes */
+#define CL5_MIN_CACHESIZE 500 /* min number of entries */
+#define CL5_MIN_CACHEMEMSIZE 262144 /* min 250K bytes */
+#define CL5_MIN_NB_LOCK 1000 /* The minimal number of locks in the DB (Same as default) */
+
+/* data structure that allows iteration through changelog */
+typedef struct cl5replayiterator CL5ReplayIterator;
+
+/* changelog state */
+typedef enum
+{
+ CL5_STATE_NONE, /* changelog has not been initialized */
+ CL5_STATE_CLOSING, /* changelog is about to close; all threads must exit */
+ CL5_STATE_CLOSED, /* changelog has been initialized, but not opened, or open and then closed */
+ CL5_STATE_OPEN /* changelog is opened */
+} CL5State;
+
+/* error codes */
+enum
+{
+ CL5_SUCCESS, /* successful operation */
+ CL5_BAD_DATA, /* invalid parameter passed to the function */
+ CL5_BAD_FORMAT, /* db data has unexpected format */
+ CL5_BAD_STATE, /* changelog is in an incorrect state for attempted operation */
+ CL5_BAD_DBVERSION, /* changelog has invalid dbversion */
+ CL5_DB_ERROR, /* database error */
+ CL5_NOTFOUND, /* requested entry or value was not found */
+ CL5_MEMORY_ERROR, /* memory allocation failed */
+ CL5_SYSTEM_ERROR, /* NSPR error occured, use PR_Error for furhter info */
+ CL5_CSN_ERROR, /* CSN API failed */
+ CL5_RUV_ERROR, /* RUV API failed */
+ CL5_OBJSET_ERROR, /* namedobjset api failed */
+ CL5_PURGED_DATA, /* requested data has been purged */
+ CL5_MISSING_DATA, /* data should be in the changelog, but is missing */
+ CL5_UNKNOWN_ERROR /* unclassified error */
+};
+
+/***** Module APIs *****/
+
+/* Name: cl5Init
+ Description: initializes changelog module; must be called by a single thread
+ before any function of the module.
+ Parameters: none
+ Return: CL5_SUCCESS if function is successful;
+ CL5_BAD_DATA if invalid directory is passed;
+ CL5_SYSTEM error if NSPR call fails.
+ */
+int cl5Init ();
+
+/* Name: cl5Cleanup
+ Description: performs cleanup of the changelog module. Must be called by a single
+ thread. It will closed db if it is still open.
+ Parameters: none
+ Return: none
+ */
+void cl5Cleanup ();
+
+/* Name: cl5Open
+ Description: opens changelog ; must be called after changelog is
+ initialized using cl5Init. It is thread safe and the second
+ call is ignored.
+ Parameters: dir - changelog dir
+ config - db configuration parameters; currently not used
+ openMode - open mode
+ Return: CL5_SUCCESS if successfull;
+ CL5_BAD_DATA if invalid directory is passed;
+ CL5_BAD_DBVERSION if dbversion file is missing or has unexpected data
+ CL5_SYSTEM_ERROR if NSPR error occured (during db directory creation);
+ CL5_MEMORY_ERROR if memory allocation fails;
+ CL5_DB_ERROR if db initialization or open fails.
+ */
+int cl5Open (const char *dir, const CL5DBConfig *config);
+
+/* Name: cl5Close
+ Description: closes changelog and cleanups changelog module; waits until
+ all threads are done using changelog
+ Parameters: none
+ Return: CL5_SUCCESS if successful;
+ CL5_BAD_STATE if db is not in the open state;
+ CL5_SYSTEM_ERROR if NSPR call fails
+ */
+int cl5Close ();
+
+/* Name: cl5Delete
+ Description: removes changelog
+ Parameters: dir - changelog directory
+ Return: CL5_SUCCESS if successful;
+ CL5_BAD_STATE if the changelog is not in closed state;
+ CL5_BAD_DATA if invalid directory supplied
+ CL5_SYSTEM_ERROR if NSPR call fails
+ */
+int cl5Delete (const char *dir);
+
+/* Name: cl5OpenDB
+ Description: opens changelog file for specified file
+ Parameters: replica - replica whose file we wish to open
+ Return: CL5_SUCCESS if successful;
+ CL5_BAD_STATE if the changelog is not initialized;
+ CL5_BAD_DATA - if NULL id is supplied
+ */
+int cl5OpenDB (Object *replica);
+
+/* Name: cl5CloseDB
+ Description: closes changelog file for the specified replica
+ Parameters: replica - replica whose file we wish to close
+ Return: CL5_SUCCESS if successful;
+ CL5_BAD_STATE if the changelog is not initialized;
+ CL5_BAD_DATA - if NULL id is supplied
+ CL5_NOTFOUND - nothing is known about specified database
+ */
+int cl5CloseDB (Object *replica);
+
+/* Name: cl5DeleteDB
+ Description: asynchronously removes changelog file for the specified replica.
+ The file is physically removed when it is no longer in use.
+ This function is called when a backend is removed or reloaded.
+ Parameters: replica - replica whose file we wish to delete
+ Return: CL5_SUCCESS if successful;
+ CL5_BAD_STATE if the changelog is not initialized;
+ CL5_BAD_DATA - if NULL id is supplied
+ CL5_NOTFOUND - nothing is known about specified database
+ */
+int cl5DeleteDB (Object *replica);
+
+/* Name: cl5DeleteDBSync
+ Description: The same as cl5DeleteDB except the function does not return
+ until the file is removed.
+*/
+int cl5DeleteDBSync (Object *replica);
+
+/* Name: cl5GetUpperBoundRUV
+ Description: retrieves vector that represent the upper bound of changes
+ stored in the changelog for the replica.
+ Parameters: r - replica for which the vector is requested
+ ruv - contains a copy of the upper bound ruv if function is successful;
+ unchanged otherwise. It is responsobility pf the caller to free
+ the ruv when it is no longer is in use
+ Return: CL5_SUCCESS if function is successfull
+ CL5_BAD_STATE if the changelog is not initialized;
+ CL5_BAD_DATA - if NULL id is supplied
+ CL5_NOTFOUND, if changelog file for replica is not found
+ */
+int cl5GetUpperBoundRUV (Replica *r, RUV **ruv);
+
+/* Name: cl5Backup
+ Description: makes a backup of the changelog including *.db2,
+ log files, and dbversion. Can be called with the changelog in either open or
+ closed state.
+ Parameters: bkDir - directory to which the data is backed up;
+ created if it does not exist
+ replicas - optional list of replicas whose changes should be backed up;
+ if the list is NULL, entire changelog is backed up.
+ Return: CL5_SUCCESS if function is successful;
+ CL5_BAD_DATA if invalid directory is passed;
+ CL5_BAD_STATE if changelog has not been initialized;
+ CL5_DB_ERROR if db call fails;
+ CL5_SYSTEM_ERROR if NSPR call or file copy failes.
+ */
+int cl5Backup (const char *bkDir, Object **replicas);
+
+/* Name: cl5Restore
+ Description: restores changelog from the backed up copy. Changelog must be ibnitalized and closed.
+ Parameters: clDir - changelog dir
+ bkDir - directory that contains the backup
+ replicas - optional list of replicas whose changes should be recovered;
+ if the list is NULL, entire changelog is recovered.
+ Return: CL5_SUCCESS if function is successfull;
+ CL5_BAD_DATA if invalid parameter is passed;
+ CL5_BAD_STATE if changelog is open or not initialized;
+ CL5_DB_ERROR if db call fails;
+ CL5_SYSTEM_ERROR if NSPR call of file copy fails
+ */
+int cl5Restore (const char *clDir, const char *bkDir, Object **replicas);
+
+/* Name: cl5ExportLDIF
+ Description: dumps changelog to an LDIF file; changelog can be open or closed.
+ Parameters: clDir - changelog dir
+ ldifFile - full path to ldif file to write
+ replicas - optional list of replicas whose changes should be exported;
+ if the list is NULL, entire changelog is exported.
+ Return: CL5_SUCCESS if function is successfull;
+ CL5_BAD_DATA if invalid parameter is passed;
+ CL5_BAD_STATE if changelog is not initialized;
+ CL5_DB_ERROR if db api fails;
+ CL5_SYSTEM_ERROR if NSPR call fails;
+ CL5_MEMORY_ERROR if memory allocation fials.
+ */
+int cl5ExportLDIF (const char *ldifFile, Object **replicas);
+
+/* Name: cl5ImportLDIF
+ Description: imports ldif file into changelog; changelog must be in the closed state
+ Parameters: clDir - changelog dir
+ ldifFile - absolute path to the ldif file to import
+ replicas - optional list of replicas whose data should be imported;
+ if the list is NULL, all data in the file is imported.
+ Return: CL5_SUCCESS if function is successfull;
+ CL5_BAD_DATA if invalid parameter is passed;
+ CL5_BAD_STATE if changelog is open or not inititalized;
+ CL5_DB_ERROR if db api fails;
+ CL5_SYSTEM_ERROR if NSPR call fails;
+ CL5_MEMORY_ERROR if memory allocation fials.
+ */
+int cl5ImportLDIF (const char *clDir, const char *ldifFile, Object **replicas);
+
+/* Name: cl5GetState
+ Description: returns database state
+ Parameters: none
+ Return: changelog state
+ */
+
+int cl5GetState ();
+
+/* Name: cl5ConfigTrimming
+ Description: sets changelog trimming parameters
+ Parameters: maxEntries - maximum number of entries in the log;
+ maxAge - maximum entry age;
+ Return: CL5_SUCCESS if successful;
+ CL5_BAD_STATE if changelog has not been open
+ */
+int cl5ConfigTrimming (int maxEntries, const char *maxAge);
+
+/* Name: cl5GetOperation
+ Description: retireves operation specified by its csn and databaseid
+ Parameters: op - must contain csn and databaseid; the rest of data is
+ filled if function is successfull
+ Return: CL5_SUCCESS if function is successfull;
+ CL5_BAD_DATA if invalid op is passed;
+ CL5_BAD_STATE if db has not been initialized;
+ CL5_NOTFOUND if entry was not found;
+ CL5_DB_ERROR if any other db error occured;
+ CL5_BADFORMAT if db data format does not match entry format.
+ */
+int cl5GetOperation (Object *replica, slapi_operation_parameters *op);
+
+/* Name: cl5GetFirstOperation
+ Description: retrieves first operation for a particular database
+ replica - replica for which the operation should be retrieved.
+ Parameters: op - buffer to store the operation;
+ iterator - to be passed to the call to cl5GetNextOperation
+ Return: CL5_SUCCESS, if successful
+ CL5_BADDATA, if operation is NULL
+ CL5_BAD_STATE, if changelog is not open
+ CL5_DB_ERROR, if db call fails
+ */
+int cl5GetFirstOperation (Object *replica, slapi_operation_parameters *op, void **iterator);
+
+/* Name: cl5GetNextOperation
+ Description: retrieves the next op from the changelog as defined by the iterator
+ Parameters: replica - replica for which the operation should be retrieved.
+ op - returned operation, if function is successful
+ iterator - in: identifies op to retrieve; out: identifies next op
+ Return: CL5_SUCCESS, if successful
+ CL5_BADDATA, if invalid parameter is supplied
+ CL5_BAD_STATE, if changelog is not open
+ CL5_NOTFOUND, empty changelog
+ CL5_DB_ERROR, if db call fails
+ */
+int cl5GetNextOperation (slapi_operation_parameters *op, void *iterator);
+
+/* Name: cl5DestroyIterator
+ Description: destroys iterator once iteration through changelog is done
+ Parameters: iterator - iterator to destroy
+ Return: CL5_SUCCESS, if successful
+ CL5_BADDATA, if invalid parameters is supplied
+ CL5_BAD_STATE, if changelog is not open
+ CL5_DB_ERROR, if db call fails
+ */
+void cl5DestroyIterator (void *iterator);
+
+/* Name: cl5WriteOperation
+ Description: writes operation to changelog
+ Parameters: repl_name - name of the replica to which operation applies
+ repl_gen - replica generation for the operation
+ !!!Note that we pass name and generation rather than
+ replica object since generation can change while operation
+ is in progress (if the data is reloaded). !!!
+ op - operation to write
+ local - this is a non-replicated operation
+ Return: CL5_SUCCESS if function is successfull;
+ CL5_BAD_DATA if invalid op is passed;
+ CL5_BAD_STATE if db has not been initialized;
+ CL5_MEMORY_ERROR if memory allocation failed;
+ CL5_DB_ERROR if any other db error occured;
+ */
+int cl5WriteOperation(const char *repl_name, const char *repl_gen,
+ const slapi_operation_parameters *op, PRBool local);
+
+/* Name: cl5CreateReplayIterator
+ Description: creates an iterator that allows to retireve changes that should
+ to be sent to the consumer identified by ruv The iteration is peformed by
+ repeated calls to cl5GetNextOperationToReplay.
+ Parameters: replica - replica whose data we wish to iterate;
+ ruv - consumer ruv;
+ iterator - iterator to be passed to cl5GetNextOperationToReplay call
+ Return: CL5_SUCCESS, if function is successfull;
+ CL5_MISSING_DATA, if data that should be in the changelog is missing
+ CL5_PURGED_DATA, if some data that consumer needs has been purged.
+ Note that the iterator can be non null if the supplier contains
+ some data that needs to be sent to the consumer
+ CL5_NOTFOUND if the consumer is up to data with respect to the supplier
+ CL5_BAD_DATA if invalid parameter is passed;
+ CL5_BAD_STATE if db has not been open;
+ CL5_DB_ERROR if any other db error occured;
+ CL5_MEMORY_ERROR if memory allocation fails.
+ */
+int cl5CreateReplayIterator (Private_Repl_Protocol *prp, const RUV *ruv,
+ CL5ReplayIterator **iterator);
+
+/* Name: cl5GetNextOperationToReplay
+ Description: retrieves next operation to be sent to the consumer and
+ that was created on a particular master. Consumer and master info
+ is encoded in the iterator parameter that must be created by calling
+ to cl5CreateIterator.
+ Parameters: iterator - iterator that identifies next entry to retrieve;
+ op - operation retireved if function is successful
+ Return: CL5_SUCCESS if function is successfull;
+ CL5_BAD_DATA if invalid parameter is passed;
+ CL5_NOTFOUND if end of iteration list is reached
+ CL5_DB_ERROR if any other db error occured;
+ CL5_BADFORMAT if data in db is of unrecognized format;
+ CL5_MEMORY_ERROR if memory allocation fails.
+ */
+int cl5GetNextOperationToReplay (CL5ReplayIterator *iterator,
+ CL5Entry *entry);
+
+/* Name: cl5DestroyReplayIterator
+ Description: destorys iterator
+ Parameters: iterator - iterator to destory
+ Return: none
+ */
+void cl5DestroyReplayIterator (CL5ReplayIterator **iterator);
+
+/* Name: cl5DeleteOnClose
+ Description: marks changelog for deletion when it is closed
+ Parameters: flag; if flag = 1 then delete else don't
+ Return: none
+ */
+
+void cl5DeleteOnClose (PRBool rm);
+
+/* Name: cl5GetDir
+ Description: returns changelog directory; must be freed by the caller;
+ Parameters: none
+ Return: copy of the directory; caller needs to free the string
+ */
+
+char *cl5GetDir ();
+
+/* Name: cl5Exist
+ Description: checks if a changelog exists in the specified directory
+ Parameters: clDir - directory to check;
+ Return: 1 - if changelog exists; 0 - otherwise
+ */
+
+PRBool cl5Exist (const char *clDir);
+
+/* Name: cl5GetOperationCount
+ Description: returns number of entries in the changelog. The changelog must be
+ open for the value to be meaningful.
+ Parameters: replica - optional parameter that specifies the replica whose operations
+ we wish to count; if NULL all changelog entries are counted
+ Return: number of entries in the changelog
+ */
+
+int cl5GetOperationCount (Object *replica);
+
+/* Name: cl5_operation_parameters_done
+ Description: frees all parameters that are not freed by operation_parameters_done
+ function in the server.
+
+ */
+
+void cl5_operation_parameters_done (struct slapi_operation_parameters *sop);
+
+/* Name: cl5CreateDirIfNeeded
+ Description: Create the directory if it doesn't exist yet
+ Parameters: dir - Contains the name of the directory to create. Must not be NULL
+ Return: CL5_SUCCESS if succeeded or existed,
+ CL5_SYSTEM_ERROR if failed.
+*/
+
+int cl5CreateDirIfNeeded (const char *dir);
+int cl5DBData2Entry (const char *data, PRUint32 len, CL5Entry *entry);
+
+PRBool cl5HelperEntry (const char *csnstr, CSN *csn);
+CSN** cl5BuildCSNList (const RUV *consRuv, const RUV *supRuv);
+void cl5DestroyCSNList (CSN*** csns);
+
+int cl5_is_diskfull();
+int cl5_diskspace_is_available();
+
+#endif
diff --git a/ldap/servers/plugins/replication/cl5_clcache.c b/ldap/servers/plugins/replication/cl5_clcache.c
new file mode 100644
index 00000000..585a7266
--- /dev/null
+++ b/ldap/servers/plugins/replication/cl5_clcache.c
@@ -0,0 +1,910 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2003 Netscape Communications Corporation
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+
+#include "errno.h" /* ENOMEM, EVAL used by Berkeley DB */
+#include "db.h" /* Berkeley DB */
+#include "cl5.h" /* changelog5Config */
+#include "cl5_clcache.h"
+
+/*
+ * Constants for the buffer pool:
+ *
+ * DEFAULT_CLC_BUFFER_PAGE_COUNT
+ * Little performance boost if it is too small.
+ *
+ * DEFAULT_CLC_BUFFER_PAGE_SIZE
+ * Its value is determined based on the DB requirement that
+ * the buffer size should be the multiple of 1024.
+ */
+#define DEFAULT_CLC_BUFFER_COUNT_MIN 10
+#define DEFAULT_CLC_BUFFER_COUNT_MAX 0
+#define DEFAULT_CLC_BUFFER_PAGE_COUNT 32
+#define DEFAULT_CLC_BUFFER_PAGE_SIZE 1024
+
+static enum {
+ CLC_STATE_READY = 0, /* ready to iterate */
+ CLC_STATE_UP_TO_DATE, /* remote RUV already covers the CSN */
+ CLC_STATE_CSN_GT_RUV, /* local RUV doesn't conver the CSN */
+ CLC_STATE_NEW_RID, /* unknown RID to local RUVs */
+ CLC_STATE_UNSAFE_RUV_CHANGE,/* (RUV1 < maxcsn-in-buffer) && (RUV1 < RUV1') */
+ CLC_STATE_DONE, /* no more change */
+ CLC_STATE_ABORTING /* abort replication session */
+};
+
+typedef struct clc_busy_list CLC_Busy_List;
+
+struct csn_seq_ctrl_block {
+ ReplicaId rid; /* RID this block serves */
+ CSN *consumer_maxcsn; /* Don't send CSN <= this */
+ CSN *local_maxcsn; /* Don't send CSN > this */
+ CSN *prev_local_maxcsn; /* */
+ int state; /* CLC_STATE_* */
+};
+
+/*
+ * Each cl5replayiterator acquires a buffer from the buffer pool
+ * at the beginning of a replication session, and returns it back
+ * at the end.
+ */
+struct clc_buffer {
+ char *buf_agmt_name; /* agreement acquired this buffer */
+ ReplicaId buf_consumer_rid; /* help checking threshold csn */
+ const RUV *buf_consumer_ruv; /* used to skip change */
+ const RUV *buf_local_ruv; /* used to refresh local_maxcsn */
+
+ /*
+ * fields for retriving data from DB
+ */
+ int buf_state;
+ CSN *buf_current_csn;
+ int buf_load_flag; /* db flag DB_MULTIPLE_KEY, DB_SET, DB_NEXT */
+ DBC *buf_cursor;
+ DBT buf_key; /* current csn string */
+ DBT buf_data; /* data retrived from db */
+ void *buf_record_ptr; /* ptr to the current record in data */
+ CSN *buf_missing_csn; /* used to detect persistent missing of CSN */
+
+ /* fields for control the CSN sequence sent to the consumer */
+ struct csn_seq_ctrl_block *buf_cscbs [MAX_NUM_OF_MASTERS];
+ int buf_num_cscbs; /* number of csn sequence ctrl blocks */
+
+ /* fields for debugging stat */
+ int buf_load_cnt; /* number of loads for session */
+ int buf_record_cnt; /* number of changes for session */
+ int buf_record_skipped; /* number of changes skipped */
+
+ /*
+ * fields that should be accessed via bl_lock or pl_lock
+ */
+ CLC_Buffer *buf_next; /* next buffer in the same list */
+ CLC_Busy_List *buf_busy_list; /* which busy list I'm in */
+};
+
+/*
+ * Each changelog has a busy buffer list
+ */
+struct clc_busy_list {
+ PRLock *bl_lock;
+ DB *bl_db; /* changelog db handle */
+ CLC_Buffer *bl_buffers; /* busy buffers of this list */
+ CLC_Busy_List *bl_next; /* next busy list in the pool */
+};
+
+/*
+ * Each process has a buffer pool
+ */
+struct clc_pool {
+ PRRWLock *pl_lock; /* cl writer and agreements */
+ DB_ENV **pl_dbenv; /* pointer to DB_ENV for all the changelog files */
+ CLC_Busy_List *pl_busy_lists; /* busy buffer lists, one list per changelog file */
+ int pl_buffer_cnt_now; /* total number of buffers */
+ int pl_buffer_cnt_min; /* free a newly returned buffer if _now > _min */
+ int pl_buffer_cnt_max; /* no use */
+ int pl_buffer_default_pages; /* num of pages in a new buffer */
+};
+
+/* static variables */
+static struct clc_pool *_pool = NULL; /* process's buffer pool */
+
+/* static prototypes */
+static int clcache_adjust_anchorcsn ( CLC_Buffer *buf );
+static void clcache_refresh_consumer_maxcsns ( CLC_Buffer *buf );
+static int clcache_refresh_local_maxcsns ( CLC_Buffer *buf );
+static int clcache_skip_change ( CLC_Buffer *buf );
+static int clcache_load_buffer_bulk ( CLC_Buffer *buf, int flag );
+static int clcache_open_cursor ( DB_TXN *txn, CLC_Buffer *buf, DBC **cursor );
+static int clcache_cursor_get ( DBC *cursor, CLC_Buffer *buf, int flag );
+static struct csn_seq_ctrl_block *clcache_new_cscb ();
+static void clcache_free_cscb ( struct csn_seq_ctrl_block ** cscb );
+static CLC_Buffer *clcache_new_buffer ( ReplicaId consumer_rid );
+static void clcache_delete_buffer ( CLC_Buffer **buf );
+static CLC_Busy_List *clcache_new_busy_list ();
+static void clcache_delete_busy_list ( CLC_Busy_List **bl );
+static int clcache_enqueue_busy_list( DB *db, CLC_Buffer *buf );
+static void csn_dup_or_init_by_csn ( CSN **csn1, CSN *csn2 );
+
+/*
+ * Initiates the process buffer pool. This should be done
+ * once and only once when process starts.
+ */
+int
+clcache_init ( DB_ENV **dbenv )
+{
+ _pool = (struct clc_pool*) slapi_ch_calloc ( 1, sizeof ( struct clc_pool ));
+ _pool->pl_dbenv = dbenv;
+ _pool->pl_buffer_cnt_min = DEFAULT_CLC_BUFFER_COUNT_MIN;
+ _pool->pl_buffer_cnt_max = DEFAULT_CLC_BUFFER_COUNT_MAX;
+ _pool->pl_buffer_default_pages = DEFAULT_CLC_BUFFER_COUNT_MAX;
+ _pool->pl_lock = PR_NewRWLock (PR_RWLOCK_RANK_NONE, "clcache_pl_lock");
+ return 0;
+}
+
+/*
+ * This is part of a callback function when changelog configuration
+ * is read or updated.
+ */
+void
+clcache_set_config ( CL5DBConfig *config )
+{
+ if ( config == NULL ) return;
+
+ PR_RWLock_Wlock ( _pool->pl_lock );
+
+ _pool->pl_buffer_cnt_max = config->maxChCacheEntries;
+
+ /*
+ * According to http://www.sleepycat.com/docs/api_c/dbc_get.html,
+ * data buffer should be a multiple of 1024 bytes in size
+ * for DB_MULTIPLE_KEY operation.
+ */
+ _pool->pl_buffer_default_pages = config->maxChCacheSize / DEFAULT_CLC_BUFFER_PAGE_SIZE + 1;
+ _pool->pl_buffer_default_pages = DEFAULT_CLC_BUFFER_PAGE_COUNT;
+ if ( _pool->pl_buffer_default_pages <= 0 ) {
+ _pool->pl_buffer_default_pages = DEFAULT_CLC_BUFFER_PAGE_COUNT;
+ }
+
+ PR_RWLock_Unlock ( _pool->pl_lock );
+}
+
+/*
+ * Gets the pointer to a thread dedicated buffer, or allocates
+ * a new buffer if there is no buffer allocated yet for this thread.
+ *
+ * This is called when a cl5replayiterator is created for
+ * a replication session.
+ */
+int
+clcache_get_buffer ( CLC_Buffer **buf, DB *db, ReplicaId consumer_rid, const RUV *consumer_ruv, const RUV *local_ruv )
+{
+ int rc = 0;
+
+ if ( buf == NULL ) return CL5_BAD_DATA;
+
+ *buf = NULL;
+
+ if ( NULL != ( *buf = (CLC_Buffer*) get_thread_private_cache()) ) {
+ (*buf)->buf_state = CLC_STATE_READY;
+ (*buf)->buf_load_cnt = 0;
+ (*buf)->buf_record_cnt = 0;
+ (*buf)->buf_record_skipped = 0;
+ (*buf)->buf_cursor = NULL;
+ (*buf)->buf_num_cscbs = 0;
+ }
+ else {
+ *buf = clcache_new_buffer ( consumer_rid );
+ if ( *buf ) {
+ if ( 0 == clcache_enqueue_busy_list ( db, *buf ) ) {
+ set_thread_private_cache ( (void*) (*buf) );
+ }
+ else {
+ clcache_delete_buffer ( buf );
+ }
+ }
+ }
+
+ if ( NULL != *buf ) {
+ (*buf)->buf_consumer_ruv = consumer_ruv;
+ (*buf)->buf_local_ruv = local_ruv;
+ }
+ else {
+ slapi_log_error ( SLAPI_LOG_FATAL, get_thread_private_agmtname(),
+ "clcache_get_buffer: can't allocate new buffer\n" );
+ rc = ENOMEM;
+ }
+
+ return rc;
+}
+
+/*
+ * Returns a buffer back to the buffer pool.
+ */
+void
+clcache_return_buffer ( CLC_Buffer **buf )
+{
+ int i;
+
+ slapi_log_error ( SLAPI_LOG_REPL, (*buf)->buf_agmt_name,
+ "session end: state=%d load=%d sent=%d skipped=%d\n",
+ (*buf)->buf_state,
+ (*buf)->buf_load_cnt,
+ (*buf)->buf_record_cnt - (*buf)->buf_record_skipped,
+ (*buf)->buf_record_skipped );
+
+ for ( i = 0; i < (*buf)->buf_num_cscbs; i++ ) {
+ clcache_free_cscb ( &(*buf)->buf_cscbs[i] );
+ }
+ (*buf)->buf_num_cscbs = 0;
+
+ if ( (*buf)->buf_cursor ) {
+
+ (*buf)->buf_cursor->c_close ( (*buf)->buf_cursor );
+ (*buf)->buf_cursor = NULL;
+ }
+}
+
+/*
+ * Loads a buffer from DB.
+ *
+ * anchorcsn - passed in for the first load of a replication session;
+ * flag - DB_SET to load in the key CSN record.
+ * DB_NEXT to load in the records greater than key CSN.
+ * return - DB error code instead of cl5 one because of the
+ * historic reason.
+ */
+int
+clcache_load_buffer ( CLC_Buffer *buf, CSN *anchorcsn, int flag )
+{
+ int rc = 0;
+
+ clcache_refresh_local_maxcsns ( buf );
+
+ /* Set the loading key */
+ if ( anchorcsn ) {
+ clcache_refresh_consumer_maxcsns ( buf );
+ buf->buf_load_flag = DB_MULTIPLE_KEY;
+ csn_as_string ( anchorcsn, 0, (char*)buf->buf_key.data );
+ slapi_log_error ( SLAPI_LOG_REPL, buf->buf_agmt_name,
+ "session start: anchorcsn=%s\n", (char*)buf->buf_key.data );
+ }
+ else if ( csn_get_time(buf->buf_current_csn) == 0 ) {
+ /* time == 0 means this csn has never been set */
+ rc = DB_NOTFOUND;
+ }
+ else if ( clcache_adjust_anchorcsn ( buf ) != 0 ) {
+ rc = DB_NOTFOUND;
+ }
+ else {
+ csn_as_string ( buf->buf_current_csn, 0, (char*)buf->buf_key.data );
+ slapi_log_error ( SLAPI_LOG_REPL, buf->buf_agmt_name,
+ "load next: anchorcsn=%s\n", (char*)buf->buf_key.data );
+ }
+
+ if ( rc == 0 ) {
+
+ buf->buf_state = CLC_STATE_READY;
+ rc = clcache_load_buffer_bulk ( buf, flag );
+
+ /* Reset some flag variables */
+ if ( rc == 0 ) {
+ int i;
+ for ( i = 0; i < buf->buf_num_cscbs; i++ ) {
+ buf->buf_cscbs[i]->state = CLC_STATE_READY;
+ }
+ }
+ else if ( anchorcsn ) {
+ /* Report error only when the missing is persistent */
+ if ( buf->buf_missing_csn && csn_compare (buf->buf_missing_csn, anchorcsn) == 0 ) {
+ slapi_log_error ( SLAPI_LOG_FATAL, buf->buf_agmt_name,
+ "Can't locate CSN %s in the changelog (DB rc=%d). The consumer may need to be reinitialized.\n",
+ (char*)buf->buf_key.data, rc );
+ }
+ else {
+ csn_dup_or_init_by_csn (&buf->buf_missing_csn, anchorcsn);
+ }
+ }
+ }
+ if ( rc != 0 ) {
+ slapi_log_error ( SLAPI_LOG_REPL, buf->buf_agmt_name,
+ "clcache_load_buffer: rc=%d\n", rc );
+ }
+
+ return rc;
+}
+
+static int
+clcache_load_buffer_bulk ( CLC_Buffer *buf, int flag )
+{
+ DB_TXN *txn = NULL;
+ DBC *cursor = NULL;
+ int rc;
+
+ /* txn control seems not improving anything so turn it off */
+ /*
+ if ( *(_pool->pl_dbenv) ) {
+ txn_begin( *(_pool->pl_dbenv), NULL, &txn, 0 );
+ }
+ */
+
+ PR_Lock ( buf->buf_busy_list->bl_lock );
+ if ( 0 == ( rc = clcache_open_cursor ( txn, buf, &cursor )) ) {
+
+ if ( flag == DB_NEXT ) {
+ /* For bulk read, position the cursor before read the next block */
+ rc = cursor->c_get ( cursor,
+ & buf->buf_key,
+ & buf->buf_data,
+ DB_SET );
+ }
+
+ /*
+ * Continue if the error is no-mem since we don't need to
+ * load in the key record anyway with DB_SET.
+ */
+ if ( 0 == rc || ENOMEM == rc )
+ rc = clcache_cursor_get ( cursor, buf, flag );
+
+ }
+
+ /*
+ * Don't keep a cursor open across the whole replication session.
+ * That had caused noticable DB resource contention.
+ */
+ if ( cursor ) {
+ cursor->c_close ( cursor );
+ }
+
+ if ( txn ) {
+ txn->commit ( txn, DB_TXN_NOSYNC );
+ }
+
+ PR_Unlock ( buf->buf_busy_list->bl_lock );
+
+ buf->buf_record_ptr = NULL;
+ if ( 0 == rc ) {
+ DB_MULTIPLE_INIT ( buf->buf_record_ptr, &buf->buf_data );
+ if ( NULL == buf->buf_record_ptr )
+ rc = DB_NOTFOUND;
+ else
+ buf->buf_load_cnt++;
+ }
+
+ return rc;
+}
+
+/*
+ * Gets the next change from the buffer.
+ * *key : output - key of the next change, or NULL if no more change
+ * *data: output - data of the next change, or NULL if no more change
+ */
+int
+clcache_get_next_change ( CLC_Buffer *buf, void **key, size_t *keylen, void **data, size_t *datalen, CSN **csn )
+{
+ int skip = 1;
+ int rc = 0;
+
+ do {
+ *key = *data = NULL;
+ *keylen = *datalen = 0;
+
+ if ( buf->buf_record_ptr ) {
+ DB_MULTIPLE_KEY_NEXT ( buf->buf_record_ptr, &buf->buf_data,
+ *key, *keylen, *data, *datalen );
+ }
+
+ /*
+ * We're done with the current buffer. Now load the next chunk.
+ */
+ if ( NULL == *key && CLC_STATE_READY == buf->buf_state ) {
+ rc = clcache_load_buffer ( buf, NULL, DB_NEXT );
+ if ( 0 == rc && buf->buf_record_ptr ) {
+ DB_MULTIPLE_KEY_NEXT ( buf->buf_record_ptr, &buf->buf_data,
+ *key, *keylen, *data, *datalen );
+ }
+ }
+
+ /* Compare the new change to the local and remote RUVs */
+ if ( NULL != *key ) {
+ buf->buf_record_cnt++;
+ csn_init_by_string ( buf->buf_current_csn, (char*)*key );
+ skip = clcache_skip_change ( buf );
+ if (skip) buf->buf_record_skipped++;
+ }
+ }
+ while ( rc == 0 && *key && skip );
+
+ if ( NULL == *key ) {
+ *key = NULL;
+ *csn = NULL;
+ rc = DB_NOTFOUND;
+ }
+ else {
+ *csn = buf->buf_current_csn;
+ slapi_log_error ( SLAPI_LOG_REPL, buf->buf_agmt_name,
+ "load=%d rec=%d csn=%s\n",
+ buf->buf_load_cnt, buf->buf_record_cnt, (char*)*key );
+ }
+
+ return rc;
+}
+
+static void
+clcache_refresh_consumer_maxcsns ( CLC_Buffer *buf )
+{
+ int i;
+
+ for ( i = 0; i < buf->buf_num_cscbs; i++ ) {
+ ruv_get_largest_csn_for_replica (
+ buf->buf_consumer_ruv,
+ buf->buf_cscbs[i]->rid,
+ &buf->buf_cscbs[i]->consumer_maxcsn );
+ }
+}
+
+static int
+clcache_refresh_local_maxcsn ( const ruv_enum_data *rid_data, void *data )
+{
+ CLC_Buffer *buf = (CLC_Buffer*) data;
+ ReplicaId rid;
+ int rc = 0;
+ int i;
+
+ rid = csn_get_replicaid ( rid_data->csn );
+
+ /*
+ * No need to create cscb for consumer's RID.
+ * If RID==65535, the CSN is originated from a
+ * legacy consumer. In this case the supplier
+ * and the consumer may have the same RID.
+ */
+ if ( rid == buf->buf_consumer_rid && rid != MAX_REPLICA_ID )
+ return rc;
+
+ for ( i = 0; i < buf->buf_num_cscbs; i++ ) {
+ if ( buf->buf_cscbs[i]->rid == rid )
+ break;
+ }
+ if ( i >= buf->buf_num_cscbs ) {
+ buf->buf_cscbs[i] = clcache_new_cscb ();
+ if ( buf->buf_cscbs[i] == NULL ) {
+ return -1;
+ }
+ buf->buf_cscbs[i]->rid = rid;
+ buf->buf_num_cscbs++;
+ }
+
+ csn_dup_or_init_by_csn ( &buf->buf_cscbs[i]->local_maxcsn, rid_data->csn );
+
+ if ( buf->buf_cscbs[i]->consumer_maxcsn &&
+ csn_compare (buf->buf_cscbs[i]->consumer_maxcsn, rid_data->csn) >= 0 ) {
+ /* No change need to be sent for this RID */
+ buf->buf_cscbs[i]->state = CLC_STATE_UP_TO_DATE;
+ }
+
+ return rc;
+}
+
+static int
+clcache_refresh_local_maxcsns ( CLC_Buffer *buf )
+{
+ int i;
+
+ for ( i = 0; i < buf->buf_num_cscbs; i++ ) {
+ csn_dup_or_init_by_csn ( &buf->buf_cscbs[i]->prev_local_maxcsn,
+ buf->buf_cscbs[i]->local_maxcsn );
+ }
+ return ruv_enumerate_elements ( buf->buf_local_ruv, clcache_refresh_local_maxcsn, buf );
+}
+
+/*
+ * Algorithm:
+ *
+ * 1. Snapshot local RUVs;
+ * 2. Load buffer;
+ * 3. Send to the consumer only those CSNs that are covered
+ * by the RUVs snapshot taken in the first step;
+ * All CSNs that are covered by the RUVs snapshot taken in the
+ * first step are guaranteed in consecutive order for the respected
+ * RIDs because of the the CSN pending list control;
+ * A CSN that is not covered by the RUVs snapshot may be out of order
+ * since it is possible that a smaller CSN might not have committed
+ * yet by the time the buffer was loaded.
+ * 4. Determine anchorcsn for each RID:
+ *
+ * Case| Local vs. Buffer | New Local | Next
+ * | MaxCSN MaxCSN | MaxCSN | Anchor-CSN
+ * ----+-------------------+-----------+----------------
+ * 1 | Cl >= Cb | * | Cb
+ * 2 | Cl < Cb | Cl | Cb
+ * 3 | Cl < Cb | Cl2 | Cl
+ *
+ * 5. Determine anchorcsn for next load:
+ * Anchor-CSN = min { all Next-Anchor-CSN, Buffer-MaxCSN }
+ */
+static int
+clcache_adjust_anchorcsn ( CLC_Buffer *buf )
+{
+ PRBool hasChange = PR_FALSE;
+ struct csn_seq_ctrl_block *cscb;
+ int rc = 0;
+ int i;
+
+ if ( buf->buf_state == CLC_STATE_READY ) {
+ for ( i = 0; i < buf->buf_num_cscbs; i++ ) {
+ cscb = buf->buf_cscbs[i];
+
+ if ( cscb->state == CLC_STATE_UP_TO_DATE )
+ continue;
+
+ /*
+ * Case 3 unsafe ruv change: next buffer load should start
+ * from where the maxcsn in the old ruv was. Since each
+ * cscb has remembered the maxcsn sent to the consumer,
+ * CSNs that may be loaded again could easily be skipped.
+ */
+ if ( cscb->prev_local_maxcsn &&
+ csn_compare (cscb->prev_local_maxcsn, buf->buf_current_csn) < 0 &&
+ csn_compare (cscb->local_maxcsn, cscb->prev_local_maxcsn) != 0 ) {
+ hasChange = PR_TRUE;
+ cscb->state = CLC_STATE_READY;
+ csn_init_by_csn ( buf->buf_current_csn, cscb->prev_local_maxcsn );
+ csn_as_string ( cscb->prev_local_maxcsn, 0, (char*)buf->buf_key.data );
+ slapi_log_error ( SLAPI_LOG_REPL, buf->buf_agmt_name,
+ "adjust anchor csn upon %s\n",
+ ( cscb->state == CLC_STATE_CSN_GT_RUV ? "out of sequence csn" : "unsafe ruv change") );
+ continue;
+ }
+
+ /*
+ * check if there are still changes to send for this RID
+ * Assume we had compared the local maxcsn and the consumer
+ * max csn before this function was called and hence the
+ * cscb->state had been set accordingly.
+ */
+ if ( hasChange == PR_FALSE &&
+ csn_compare (cscb->local_maxcsn, buf->buf_current_csn) > 0 ) {
+ hasChange = PR_TRUE;
+ }
+ }
+ }
+
+ if ( !hasChange ) {
+ buf->buf_state = CLC_STATE_DONE;
+ }
+
+ return buf->buf_state;
+}
+
+static int
+clcache_skip_change ( CLC_Buffer *buf )
+{
+ struct csn_seq_ctrl_block *cscb = NULL;
+ ReplicaId rid;
+ int skip = 1;
+ int i;
+
+ do {
+
+ rid = csn_get_replicaid ( buf->buf_current_csn );
+
+ /*
+ * Skip CSN that is originated from the consumer.
+ * If RID==65535, the CSN is originated from a
+ * legacy consumer. In this case the supplier
+ * and the consumer may have the same RID.
+ */
+ if (rid == buf->buf_consumer_rid && rid != MAX_REPLICA_ID)
+ break;
+
+ /* Skip helper entry (ENTRY_COUNT, PURGE_RUV and so on) */
+ if ( cl5HelperEntry ( NULL, buf->buf_current_csn ) == PR_TRUE ) {
+ slapi_log_error ( SLAPI_LOG_REPL, buf->buf_agmt_name,
+ "Skip helper entry type=%d\n", csn_get_time( buf->buf_current_csn ));
+ break;
+ }
+
+ /* Find csn sequence control block for the current rid */
+ for (i = 0; i < buf->buf_num_cscbs && buf->buf_cscbs[i]->rid != rid; i++);
+
+ /* Skip CSN whose RID is unknown to the local RUV snapshot */
+ if ( i >= buf->buf_num_cscbs ) {
+ buf->buf_state = CLC_STATE_NEW_RID;
+ break;
+ }
+
+ cscb = buf->buf_cscbs[i];
+
+ /* Skip if the consumer is already up-to-date for the RID */
+ if ( cscb->state == CLC_STATE_UP_TO_DATE ) {
+ break;
+ }
+
+ /* Skip CSN whose preceedents are not covered by local RUV snapshot */
+ if ( cscb->state == CLC_STATE_CSN_GT_RUV ) {
+ break;
+ }
+
+ /* Skip CSNs already covered by consumer RUV */
+ if ( cscb->consumer_maxcsn &&
+ csn_compare ( buf->buf_current_csn, cscb->consumer_maxcsn ) <= 0 ) {
+ break;
+ }
+
+ /* Send CSNs that are covered by the local RUV snapshot */
+ if ( csn_compare ( buf->buf_current_csn, cscb->local_maxcsn ) <= 0 ) {
+ skip = 0;
+ csn_dup_or_init_by_csn ( &cscb->consumer_maxcsn, buf->buf_current_csn );
+ break;
+ }
+
+ /*
+ * Promote the local maxcsn to its next neighbor
+ * to keep the current session going. Skip if we
+ * are not sure if current_csn is the neighbor.
+ */
+ if ( csn_time_difference(buf->buf_current_csn, cscb->local_maxcsn) == 0 &&
+ (csn_get_seqnum(buf->buf_current_csn) ==
+ csn_get_seqnum(cscb->local_maxcsn) + 1) ) {
+ csn_init_by_csn ( cscb->local_maxcsn, buf->buf_current_csn );
+ csn_init_by_csn ( cscb->consumer_maxcsn, buf->buf_current_csn );
+ skip = 0;
+ break;
+ }
+
+ /* Skip CSNs not covered by local RUV snapshot */
+ cscb->state = CLC_STATE_CSN_GT_RUV;
+
+ } while (0);
+
+#ifdef DEBUG
+ if (skip && cscb) {
+ char consumer[24] = {'\0'};
+ char local[24] = {'\0'};
+ char current[24] = {'\0'};
+
+ if ( cscb->consumer_maxcsn )
+ csn_as_string ( cscb->consumer_maxcsn, PR_FALSE, consumer );
+ if ( cscb->local_maxcsn )
+ csn_as_string ( cscb->local_maxcsn, PR_FALSE, local );
+ csn_as_string ( buf->buf_current_csn, PR_FALSE, current );
+ slapi_log_error ( SLAPI_LOG_REPL, buf->buf_agmt_name,
+ "Skip %s consumer=%s local=%s\n", current, consumer, local );
+ }
+#endif
+
+ return skip;
+}
+
+static struct csn_seq_ctrl_block *
+clcache_new_cscb ()
+{
+ struct csn_seq_ctrl_block *cscb;
+
+ cscb = (struct csn_seq_ctrl_block *) slapi_ch_calloc ( 1, sizeof (struct csn_seq_ctrl_block) );
+ if (cscb == NULL) {
+ slapi_log_error ( SLAPI_LOG_FATAL, NULL, "clcache: malloc failure\n" );
+ }
+ return cscb;
+}
+
+static void
+clcache_free_cscb ( struct csn_seq_ctrl_block ** cscb )
+{
+ csn_free ( & (*cscb)->consumer_maxcsn );
+ csn_free ( & (*cscb)->local_maxcsn );
+ csn_free ( & (*cscb)->prev_local_maxcsn );
+ slapi_ch_free ( (void **) cscb );
+}
+
+/*
+ * Allocate and initialize a new buffer
+ * It is called when there is a request for a buffer while
+ * buffer free list is empty.
+ */
+static CLC_Buffer *
+clcache_new_buffer ( ReplicaId consumer_rid )
+{
+ CLC_Buffer *buf = NULL;
+ int page_count = 0;
+ int welldone = 0;
+ int rc = 0;
+
+ do {
+
+ buf = (CLC_Buffer*) slapi_ch_calloc (1, sizeof(CLC_Buffer));
+ if ( NULL == buf )
+ break;
+
+ buf->buf_key.flags = DB_DBT_USERMEM;
+ buf->buf_key.ulen = CSN_STRSIZE + 1;
+ buf->buf_key.size = CSN_STRSIZE;
+ buf->buf_key.data = slapi_ch_calloc( 1, buf->buf_key.ulen );
+ if ( NULL == buf->buf_key.data )
+ break;
+
+ buf->buf_data.flags = DB_DBT_USERMEM;
+ buf->buf_data.ulen = _pool->pl_buffer_default_pages * DEFAULT_CLC_BUFFER_PAGE_SIZE;
+ buf->buf_data.data = slapi_ch_malloc( buf->buf_data.ulen );
+ if ( NULL == buf->buf_data.data )
+ break;
+
+ if ( NULL == ( buf->buf_current_csn = csn_new()) )
+ break;
+
+ buf->buf_state = CLC_STATE_READY;
+ buf->buf_agmt_name = get_thread_private_agmtname();
+ buf->buf_consumer_rid = consumer_rid;
+ buf->buf_num_cscbs = 0;
+
+ welldone = 1;
+
+ } while (0);
+
+ if ( !welldone ) {
+ clcache_delete_buffer ( &buf );
+ }
+
+ return buf;
+}
+
+/*
+ * Deallocates a buffer.
+ * It is called when a buffer is returned to the buffer pool
+ * and the pool size is over the limit.
+ */
+static void
+clcache_delete_buffer ( CLC_Buffer **buf )
+{
+ if ( buf && *buf ) {
+ slapi_ch_free (&( (*buf)->buf_key.data ));
+ slapi_ch_free (&( (*buf)->buf_data.data ));
+ csn_free (&( (*buf)->buf_current_csn ));
+ csn_free (&( (*buf)->buf_missing_csn ));
+ slapi_ch_free ( (void **) buf );
+ }
+}
+
+static CLC_Busy_List *
+clcache_new_busy_list ()
+{
+ CLC_Busy_List *bl;
+ int welldone = 0;
+
+ do {
+ if ( NULL == (bl = ( CLC_Busy_List* ) slapi_ch_calloc (1, sizeof(CLC_Busy_List)) ))
+ break;
+
+ if ( NULL == (bl->bl_lock = PR_NewLock ()) )
+ break;
+
+ /*
+ if ( NULL == (bl->bl_max_csn = csn_new ()) )
+ break;
+ */
+
+ welldone = 1;
+ }
+ while (0);
+
+ if ( !welldone ) {
+ clcache_delete_busy_list ( &bl );
+ }
+
+ return bl;
+}
+
+static void
+clcache_delete_busy_list ( CLC_Busy_List **bl )
+{
+ if ( bl && *bl ) {
+ if ( (*bl)->bl_lock ) {
+ PR_DestroyLock ( (*bl)->bl_lock );
+ }
+ /* csn_free (&( (*bl)->bl_max_csn )); */
+ slapi_ch_free ( (void **) bl );
+ }
+}
+
+static int
+clcache_enqueue_busy_list ( DB *db, CLC_Buffer *buf )
+{
+ CLC_Busy_List *bl;
+ int rc = 0;
+
+ PR_RWLock_Rlock ( _pool->pl_lock );
+ for ( bl = _pool->pl_busy_lists; bl && bl->bl_db != db; bl = bl->bl_next );
+ PR_RWLock_Unlock ( _pool->pl_lock );
+
+ if ( NULL == bl ) {
+ if ( NULL == ( bl = clcache_new_busy_list ()) ) {
+ rc = ENOMEM;
+ }
+ else {
+ PR_RWLock_Wlock ( _pool->pl_lock );
+ bl->bl_db = db;
+ bl->bl_next = _pool->pl_busy_lists;
+ _pool->pl_busy_lists = bl;
+ PR_RWLock_Unlock ( _pool->pl_lock );
+ }
+ }
+
+ if ( NULL != bl ) {
+ PR_Lock ( bl->bl_lock );
+ buf->buf_busy_list = bl;
+ buf->buf_next = bl->bl_buffers;
+ bl->bl_buffers = buf;
+ PR_Unlock ( bl->bl_lock );
+ }
+
+ return rc;
+}
+
+static int
+clcache_open_cursor ( DB_TXN *txn, CLC_Buffer *buf, DBC **cursor )
+{
+ int rc;
+
+ rc = buf->buf_busy_list->bl_db->cursor ( buf->buf_busy_list->bl_db, txn, cursor, 0 );
+ if ( rc != 0 ) {
+ slapi_log_error ( SLAPI_LOG_FATAL, get_thread_private_agmtname(),
+ "clcache: failed to open cursor; db error - %d %s\n",
+ rc, db_strerror(rc));
+ }
+
+ return rc;
+}
+
+static int
+clcache_cursor_get ( DBC *cursor, CLC_Buffer *buf, int flag )
+{
+ int rc;
+
+ rc = cursor->c_get ( cursor,
+ & buf->buf_key,
+ & buf->buf_data,
+ buf->buf_load_flag | flag );
+ if ( ENOMEM == rc ) {
+ /*
+ * The record takes more space than the current size of the
+ * buffer. Fortunately, buf->buf_data.size has been set by
+ * c_get() to the actual data size needed. So we can
+ * reallocate the data buffer and try to read again.
+ */
+ buf->buf_data.ulen = ( buf->buf_data.size / DEFAULT_CLC_BUFFER_PAGE_SIZE + 1 ) * DEFAULT_CLC_BUFFER_PAGE_SIZE;
+ buf->buf_data.data = slapi_ch_realloc ( buf->buf_data.data, buf->buf_data.ulen );
+ if ( buf->buf_data.data != NULL ) {
+ rc = cursor->c_get ( cursor,
+ &( buf->buf_key ),
+ &( buf->buf_data ),
+ buf->buf_load_flag | flag );
+ slapi_log_error ( SLAPI_LOG_REPL, buf->buf_agmt_name,
+ "clcache: (%d | %d) %s reallocated and retry returns %d\n", buf->buf_load_flag, flag, buf->buf_key.data, rc );
+ }
+ }
+
+ switch ( rc ) {
+ case EINVAL:
+ slapi_log_error ( SLAPI_LOG_FATAL, buf->buf_agmt_name,
+ "clcache_cursor_get: invalid parameter\n" );
+ break;
+
+ case ENOMEM:
+ slapi_log_error ( SLAPI_LOG_FATAL, buf->buf_agmt_name,
+ "clcache_cursor_get: cann't allocate %u bytes\n", buf->buf_data.ulen );
+ break;
+
+ default:
+ break;
+ }
+
+ return rc;
+}
+
+static void
+csn_dup_or_init_by_csn ( CSN **csn1, CSN *csn2 )
+{
+ if ( *csn1 == NULL )
+ *csn1 = csn_new();
+ csn_init_by_csn ( *csn1, csn2 );
+}
diff --git a/ldap/servers/plugins/replication/cl5_clcache.h b/ldap/servers/plugins/replication/cl5_clcache.h
new file mode 100644
index 00000000..93024d1e
--- /dev/null
+++ b/ldap/servers/plugins/replication/cl5_clcache.h
@@ -0,0 +1,22 @@
+#ifndef CL5_CLCACHE_H
+#define CL5_CLCACHE_H
+
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+
+#include "db.h"
+#include "slapi-private.h"
+
+typedef struct clc_buffer CLC_Buffer;
+
+int clcache_init ( DB_ENV **dbenv );
+void clcache_set_config ( CL5DBConfig * config );
+int clcache_get_buffer ( CLC_Buffer **buf, DB *db, ReplicaId consumer_rid, const RUV *consumer_ruv, const RUV *local_ruv );
+int clcache_load_buffer ( CLC_Buffer *buf, CSN *startCSN, int flag );
+void clcache_return_buffer ( CLC_Buffer **buf );
+int clcache_get_next_change ( CLC_Buffer *buf, void **key, size_t *keylen, void **data, size_t *datalen, CSN **csn );
+
+#endif
diff --git a/ldap/servers/plugins/replication/cl5_config.c b/ldap/servers/plugins/replication/cl5_config.c
new file mode 100644
index 00000000..58c79dc1
--- /dev/null
+++ b/ldap/servers/plugins/replication/cl5_config.c
@@ -0,0 +1,868 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+/* cl5_config.c - functions to process changelog configuration
+ */
+
+#include <string.h>
+#include <prio.h>
+#include "repl5.h"
+#include "cl5.h"
+#include "cl5_clcache.h" /* To configure the Changelog Cache */
+#include "intrinsics.h" /* JCMREPL - Is this bad? */
+#ifdef TEST_CL5
+#include "cl5_test.h"
+#endif
+
+#define CONFIG_BASE "cn=changelog5,cn=config" /*"cn=changelog,cn=supplier,cn=replication5.0,cn=replication,cn=config"*/
+#define CONFIG_FILTER "(objectclass=*)"
+
+static PRRWLock *s_configLock; /* guarantees that only on thread at a time
+ modifies changelog configuration */
+
+/* Forward Declartions */
+static int changelog5_config_add (Slapi_PBlock *pb, Slapi_Entry* e, Slapi_Entry* entryAfter, int *returncode, char *returntext, void *arg);
+static int changelog5_config_modify (Slapi_PBlock *pb, Slapi_Entry* e, Slapi_Entry* entryAfter, int *returncode, char *returntext, void *arg);
+static int changelog5_config_delete (Slapi_PBlock *pb, Slapi_Entry* e, Slapi_Entry* entryAfter, int *returncode, char *returntext, void *arg);
+static int dont_allow_that(Slapi_PBlock *pb, Slapi_Entry* entryBefore, Slapi_Entry* e, int *returncode, char *returntext, void *arg);
+
+static void changelog5_extract_config(Slapi_Entry* entry, changelog5Config *config);
+static changelog5Config * changelog5_dup_config(changelog5Config *config);
+static void replace_bslash (char *dir);
+static int notify_replica (Replica *r, void *arg);
+static int _is_absolutepath (char *dir);
+
+int changelog5_config_init()
+{
+ /* The FE DSE *must* be initialised before we get here */
+
+ /* create the configuration lock */
+ s_configLock = PR_NewRWLock(PR_RWLOCK_RANK_NONE, "config_lock");
+ if (s_configLock == NULL)
+ {
+ slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name_cl,
+ "changelog5_config_init: failed to create configurationlock; "
+ "NSPR error - %d\n",PR_GetError ());
+ return 1;
+ }
+
+ slapi_config_register_callback(SLAPI_OPERATION_ADD, DSE_FLAG_PREOP, CONFIG_BASE, LDAP_SCOPE_BASE,
+ CONFIG_FILTER, changelog5_config_add, NULL);
+ slapi_config_register_callback(SLAPI_OPERATION_MODIFY, DSE_FLAG_PREOP, CONFIG_BASE, LDAP_SCOPE_BASE,
+ CONFIG_FILTER, changelog5_config_modify, NULL);
+ slapi_config_register_callback(SLAPI_OPERATION_MODRDN, DSE_FLAG_PREOP, CONFIG_BASE, LDAP_SCOPE_BASE,
+ CONFIG_FILTER, dont_allow_that, NULL);
+ slapi_config_register_callback(SLAPI_OPERATION_DELETE, DSE_FLAG_PREOP, CONFIG_BASE, LDAP_SCOPE_BASE,
+ CONFIG_FILTER, changelog5_config_delete, NULL);
+
+ return 0;
+}
+
+void changelog5_config_cleanup()
+{
+ slapi_config_remove_callback(SLAPI_OPERATION_ADD, DSE_FLAG_PREOP, CONFIG_BASE, LDAP_SCOPE_BASE,
+ CONFIG_FILTER, changelog5_config_add);
+ slapi_config_remove_callback(SLAPI_OPERATION_MODIFY, DSE_FLAG_PREOP, CONFIG_BASE, LDAP_SCOPE_BASE,
+ CONFIG_FILTER, changelog5_config_modify);
+ slapi_config_remove_callback(SLAPI_OPERATION_MODRDN, DSE_FLAG_PREOP, CONFIG_BASE, LDAP_SCOPE_BASE,
+ CONFIG_FILTER, dont_allow_that);
+ slapi_config_remove_callback(SLAPI_OPERATION_DELETE, DSE_FLAG_PREOP, CONFIG_BASE, LDAP_SCOPE_BASE,
+ CONFIG_FILTER, changelog5_config_delete);
+
+ if (s_configLock)
+ {
+ PR_DestroyRWLock (s_configLock);
+ s_configLock = NULL;
+ }
+}
+
+int changelog5_read_config (changelog5Config *config)
+{
+ int rc = LDAP_SUCCESS;
+ Slapi_PBlock *pb;
+
+ pb = slapi_pblock_new ();
+ slapi_search_internal_set_pb (pb, CONFIG_BASE, LDAP_SCOPE_BASE, CONFIG_FILTER, NULL, 0, NULL,
+ NULL, repl_get_plugin_identity (PLUGIN_MULTIMASTER_REPLICATION), 0);
+ slapi_search_internal_pb (pb);
+ slapi_pblock_get( pb, SLAPI_PLUGIN_INTOP_RESULT, &rc );
+ if ( LDAP_SUCCESS == rc )
+ {
+ Slapi_Entry **entries = NULL;
+ slapi_pblock_get( pb, SLAPI_PLUGIN_INTOP_SEARCH_ENTRIES, &entries );
+ if ( NULL != entries && NULL != entries[0])
+ {
+ /* Extract the config info from the changelog entry */
+ changelog5_extract_config(entries[0], config);
+ }
+ }
+ else
+ {
+ memset (config, 0, sizeof (*config));
+ rc = LDAP_SUCCESS;
+ }
+
+ slapi_free_search_results_internal(pb);
+ slapi_pblock_destroy(pb);
+
+ return rc;
+}
+
+void changelog5_config_done (changelog5Config *config)
+{
+ if (config) {
+ /* slapi_ch_free_string accepts NULL pointer */
+ slapi_ch_free_string (&config->maxAge);
+ slapi_ch_free_string (&config->dir);
+ }
+}
+
+void changelog5_config_free (changelog5Config **config)
+{
+ changelog5_config_done(*config);
+ slapi_ch_free((void **)config);
+}
+
+static int
+changelog5_config_add (Slapi_PBlock *pb, Slapi_Entry* e, Slapi_Entry* entryAfter,
+ int *returncode, char *returntext, void *arg)
+{
+ int rc;
+ changelog5Config config;
+
+ *returncode = LDAP_SUCCESS;
+
+ PR_RWLock_Wlock (s_configLock);
+
+ /* we already have a configured changelog - don't need to do anything
+ since add operation will fail */
+ if (cl5GetState () == CL5_STATE_OPEN)
+ {
+ *returncode = 1;
+ if (returntext)
+ {
+ strcpy (returntext, "attempt to add changelog when it already exists");
+ }
+
+ slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name_cl,
+ "changelog5_config_add: changelog already exist; "
+ "request ignored\n");
+ goto done;
+ }
+
+ changelog5_extract_config(e, &config);
+ if (config.dir == NULL)
+ {
+ *returncode = 1;
+ if (returntext)
+ {
+ sprintf (returntext, "NULL changelog directory");
+ }
+
+ slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name_cl,
+ "changelog5_config_add: NULL changelog directory\n");
+ goto done;
+ }
+
+ /* start the changelog */
+ rc = cl5Open (config.dir, &config.dbconfig);
+ if (rc != CL5_SUCCESS)
+ {
+ *returncode = 1;
+ if (returntext)
+ {
+ sprintf (returntext, "failed to start changelog; error - %d", rc);
+ }
+
+ slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name_cl,
+ "changelog5_config_add: failed to start changelog\n");
+ goto done;
+ }
+
+ /* set trimming parameters */
+ rc = cl5ConfigTrimming (config.maxEntries, config.maxAge);
+ if (rc != CL5_SUCCESS)
+ {
+ *returncode = 1;
+ if (returntext)
+ {
+ sprintf (returntext, "failed to configure changelog trimming; error - %d", rc);
+ }
+ slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name_cl,
+ "changelog5_config_add: failed to configure changelog trimming\n");
+ goto done;
+ }
+
+ /* notify all the replicas that the changelog is configured
+ so that the can log dummy changes if necessary. */
+ replica_enumerate_replicas (notify_replica, NULL);
+
+#ifdef TEST_CL5
+ testChangelog (TEST_ITERATION);
+#endif
+
+done:;
+ PR_RWLock_Unlock (s_configLock);
+ changelog5_config_done (&config);
+ if (*returncode == LDAP_SUCCESS)
+ {
+ if (returntext)
+ {
+ returntext[0] = '\0';
+ }
+
+ return SLAPI_DSE_CALLBACK_OK;
+ }
+
+ return SLAPI_DSE_CALLBACK_ERROR;
+}
+
+static int
+changelog5_config_modify (Slapi_PBlock *pb, Slapi_Entry* entryBefore, Slapi_Entry* e,
+ int *returncode, char *returntext, void *arg)
+{
+ int rc= 0;
+ LDAPMod **mods;
+ int i;
+ changelog5Config config;
+ changelog5Config * originalConfig = NULL;
+ char *currentDir = NULL;
+
+ *returncode = LDAP_SUCCESS;
+
+ /* changelog must be open before its parameters can be modified */
+ if (cl5GetState() != CL5_STATE_OPEN)
+ {
+ if (returntext)
+ {
+ strcpy (returntext, "changelog is not configured");
+ }
+
+ slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name_cl,
+ "changelog5_config_modify: changelog is not configured\n");
+ return SLAPI_DSE_CALLBACK_ERROR;
+ }
+
+ PR_RWLock_Wlock (s_configLock);
+
+ /* changelog must be open before its parameters can be modified */
+ if (cl5GetState() != CL5_STATE_OPEN)
+ {
+ *returncode = 1;
+ if (returntext)
+ {
+ strcpy (returntext, "changelog is not configured");
+ }
+
+ slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name_cl,
+ "changelog5_config_modify: changelog is not configured\n");
+ goto done;
+ }
+
+ /*
+ * Extract all the original configuration: This is needed to ensure that the configuration
+ * is trully reloaded. This was not needed before 091401 because the changelog configuration
+ * was always hardcoded (NULL was being passed to cl5Open). Now we need to ensure we pass to
+ * cl5Open the proper configuration...
+ */
+ changelog5_extract_config(e, &config);
+ originalConfig = changelog5_dup_config(&config);
+
+ /* Reset all the attributes that have been potentially modified by the current MODIFY operation */
+ slapi_ch_free_string(&config.dir);
+ config.dir = NULL;
+ config.maxEntries = CL5_NUM_IGNORE;
+ slapi_ch_free_string(&config.maxAge);
+ config.maxAge = slapi_ch_strdup(CL5_STR_IGNORE);
+ config.dbconfig.maxChCacheEntries = 0;
+ config.dbconfig.maxChCacheSize = CL5_NUM_IGNORE;
+
+ slapi_pblock_get( pb, SLAPI_MODIFY_MODS, &mods );
+ for (i = 0; mods[i] != NULL; i++)
+ {
+ if (mods[i]->mod_op & LDAP_MOD_DELETE)
+ {
+ /* We don't support deleting changelog attributes */
+ }
+ else
+ {
+ int j;
+ for (j = 0; ((mods[i]->mod_values[j]) && (LDAP_SUCCESS == rc)); j++)
+ {
+ char *config_attr, *config_attr_value;
+ config_attr = (char *) mods[i]->mod_type;
+ config_attr_value = (char *) mods[i]->mod_bvalues[j]->bv_val;
+
+#define ATTR_MODIFIERSNAME "modifiersname"
+#define ATTR_MODIFYTIMESTAMP "modifytimestamp"
+
+ if ( strcasecmp ( config_attr, ATTR_MODIFIERSNAME ) == 0 ) {
+ continue;
+ }
+ if ( strcasecmp ( config_attr, ATTR_MODIFYTIMESTAMP ) == 0 ) {
+ continue;
+ }
+ /* replace existing value */
+ if ( strcasecmp (config_attr, CONFIG_CHANGELOG_DIR_ATTRIBUTE ) == 0 )
+ {
+ if (config_attr_value && config_attr_value[0] != '\0')
+ {
+ slapi_ch_free_string(&config.dir);
+ config.dir = slapi_ch_strdup(config_attr_value);
+ replace_bslash (config.dir);
+ }
+ else
+ {
+ *returncode = 1;
+ if (returntext)
+ {
+ strcpy (returntext, "null changelog directory");
+ }
+ goto done;
+ }
+ }
+ else if ( strcasecmp ( config_attr, CONFIG_CHANGELOG_MAXENTRIES_ATTRIBUTE ) == 0 )
+ {
+ if (config_attr_value && config_attr_value[0] != '\0')
+ {
+ config.maxEntries = atoi (config_attr_value);
+ }
+ else
+ {
+ config.maxEntries = 0;
+ }
+ }
+ else if ( strcasecmp ( config_attr, CONFIG_CHANGELOG_MAXAGE_ATTRIBUTE ) == 0 )
+ {
+ slapi_ch_free_string(&config.maxAge);
+ config.maxAge = slapi_ch_strdup(config_attr_value);
+ }
+ else if ( strcasecmp ( config_attr, CONFIG_CHANGELOG_CACHESIZE ) == 0 )
+ { /* The Changelog Cache Size parameters can be modified online without a need for restart */
+ if (config_attr_value && config_attr_value[0] != '\0')
+ {
+ config.dbconfig.maxChCacheEntries = atoi (config_attr_value);
+ }
+ else
+ {
+ config.dbconfig.maxChCacheEntries = 0;
+ }
+ }
+ else if ( strcasecmp ( config_attr, CONFIG_CHANGELOG_CACHEMEMSIZE ) == 0 )
+ { /* The Changelog Cache Size parameters can be modified online without a need for restart */
+ if (config_attr_value && config_attr_value[0] != '\0')
+ {
+ config.dbconfig.maxChCacheSize = atoi (config_attr_value);
+ }
+ else
+ {
+ config.dbconfig.maxChCacheSize = 0;
+ }
+ }
+ else
+ {
+ *returncode = LDAP_UNWILLING_TO_PERFORM;
+ if (returntext)
+ {
+ sprintf (returntext, "Unwilling to apply %s mods while the server is running", config_attr);
+ }
+ goto done;
+ }
+ }
+ }
+ }
+ /* Undo the reset above for all the modifiable attributes that were not modified
+ * except config.dir */
+ if (config.maxEntries == CL5_NUM_IGNORE)
+ config.maxEntries = originalConfig->maxEntries;
+ if (strcmp (config.maxAge, CL5_STR_IGNORE) == 0) {
+ slapi_ch_free_string(&config.maxAge);
+ if (originalConfig->maxAge)
+ config.maxAge = slapi_ch_strdup(originalConfig->maxAge);
+ }
+ if (config.dbconfig.maxChCacheEntries == 0)
+ config.dbconfig.maxChCacheEntries = originalConfig->dbconfig.maxChCacheEntries;
+ if (config.dbconfig.maxChCacheSize == CL5_NUM_IGNORE)
+ config.dbconfig.maxChCacheSize = originalConfig->dbconfig.maxChCacheSize;
+
+
+ /* attempt to change chagelog dir */
+ if (config.dir)
+ {
+ currentDir = cl5GetDir ();
+ if (currentDir == NULL)
+ {
+ /* something is wrong: we should never be here */
+ *returncode = 1;
+ if (returntext)
+ {
+ strcpy (returntext, "internal failure");
+ }
+
+ goto done;
+ }
+
+#ifdef _WIN32
+ if (strcasecmp (currentDir, config.dir) != 0)
+#else /* On Unix, path are case sensitive */
+ if (strcmp (currentDir, config.dir) != 0)
+#endif
+ {
+ if (!_is_absolutepath(config.dir) || (CL5_SUCCESS != cl5CreateDirIfNeeded(config.dir)))
+ {
+ *returncode = 1;
+ if (returntext)
+ {
+ strcpy (returntext, "invalid changelog directory or insufficient access");
+ }
+
+ goto done;
+ }
+
+ /* changelog directory changed - need to remove the
+ previous changelog and create new one */
+
+ slapi_log_error(SLAPI_LOG_PLUGIN, repl_plugin_name_cl,
+ "changelog5_config_modify: changelog directory changed; "
+ "old dir - %s, new dir - %s; recreating changelog.\n",
+ currentDir, config.dir);
+
+ /* this call will block until all threads using changelog
+ release changelog by calling cl5RemoveThread () */
+ rc = cl5Close ();
+ if (rc != CL5_SUCCESS)
+ {
+ *returncode = 1;
+ if (returntext)
+ {
+ sprintf (returntext, "failed to close changelog; error - %d", rc);
+ }
+
+ slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name_cl,
+ "changelog5_config_modify: failed to close changelog\n");
+ goto done;
+ }
+
+ rc = cl5Delete (currentDir);
+ if (rc != CL5_SUCCESS)
+ {
+ *returncode = 1;
+ if (returntext)
+ {
+ sprintf (returntext, "failed to remove changelog; error - %d", rc);
+ }
+
+ slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name_cl,
+ "changelog5_config_modify: failed to remove changelog\n");
+ goto done;
+ }
+
+ rc = cl5Open (config.dir, &config.dbconfig);
+ if (rc != CL5_SUCCESS)
+ {
+ *returncode = 1;
+ if (returntext)
+ {
+ sprintf (returntext, "failed to restart changelog; error - %d", rc);
+ }
+
+ slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name_cl,
+ "changelog5_config_modify: failed to restart changelog\n");
+ /* before finishing, let's try to do some error recovery */
+ if (CL5_SUCCESS != cl5Open(currentDir, &config.dbconfig)) {
+ slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name_cl,
+ "changelog5_config_modify: failed to restore previous changelog\n");
+ }
+ goto done;
+ }
+ }
+ }
+
+ /* one of the changelog parameters is modified */
+ if (config.maxEntries != CL5_NUM_IGNORE ||
+ strcmp (config.maxAge, CL5_STR_IGNORE) != 0)
+ {
+ rc = cl5ConfigTrimming (config.maxEntries, config.maxAge);
+ if (rc != CL5_SUCCESS)
+ {
+ *returncode = 1;
+ if (returntext)
+ {
+ sprintf (returntext, "failed to configure changelog trimming; error - %d", rc);
+ }
+
+ slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name_cl,
+ "changelog5_config_modify: failed to configure changelog trimming\n");
+ goto done;
+ }
+ }
+
+ if (config.dbconfig.maxChCacheEntries != 0 || config.dbconfig.maxChCacheSize != CL5_NUM_IGNORE)
+ clcache_set_config(&config.dbconfig);
+
+done:;
+ PR_RWLock_Unlock (s_configLock);
+
+ changelog5_config_done (&config);
+ changelog5_config_free (&originalConfig);
+
+ /* slapi_ch_free accepts NULL pointer */
+ slapi_ch_free ((void**)&currentDir);
+
+ if (*returncode == LDAP_SUCCESS)
+ {
+
+ if (returntext)
+ {
+ returntext[0] = '\0';
+ }
+
+ return SLAPI_DSE_CALLBACK_OK;
+ }
+
+ return SLAPI_DSE_CALLBACK_ERROR;
+}
+
+static int
+changelog5_config_delete (Slapi_PBlock *pb, Slapi_Entry* e, Slapi_Entry* entryAfter,
+ int *returncode, char *returntext, void *arg)
+{
+ int rc;
+ char *currentDir = NULL;
+ *returncode = LDAP_SUCCESS;
+
+ /* changelog must be open before it can be deleted */
+ if (cl5GetState () != CL5_STATE_OPEN)
+ {
+ *returncode = 1;
+ if (returntext)
+ {
+ strcpy (returntext, "changelog is not configured");
+ }
+
+ slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name_cl,
+ "changelog5_config_delete: chagelog is not configured\n");
+ return SLAPI_DSE_CALLBACK_ERROR;
+ }
+
+ PR_RWLock_Wlock (s_configLock);
+
+ /* changelog must be open before it can be deleted */
+ if (cl5GetState () != CL5_STATE_OPEN)
+ {
+ *returncode = 1;
+ if (returntext)
+ {
+ strcpy (returntext, "changelog is not configured");
+ }
+
+ slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name_cl,
+ "changelog5_config_delete: chagelog is not configured\n");
+ goto done;
+ }
+
+ currentDir = cl5GetDir ();
+
+ if (currentDir == NULL)
+ {
+ /* something is wrong: we should never be here */
+ *returncode = 1;
+ if (returntext)
+ {
+ strcpy (returntext, "internal failure");
+ }
+
+ slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name_cl,
+ "changelog5_config_delete: NULL directory\n");
+ goto done;
+ }
+
+ /* this call will block until all threads using changelog
+ release changelog by calling cl5RemoveThread () */
+ rc = cl5Close ();
+ if (rc != CL5_SUCCESS)
+ {
+ *returncode = 1;
+ if (returntext)
+ {
+ sprintf (returntext, "failed to close changelog; error - %d", rc);
+ }
+
+ slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name_cl,
+ "changelog5_config_delete: failed to close changelog\n");
+ goto done;
+ }
+
+ rc = cl5Delete (currentDir);
+ if (rc != CL5_SUCCESS)
+ {
+ *returncode = 1;
+ if (returntext)
+ {
+ sprintf (returntext, "failed to remove changelog; error - %d", rc);
+ }
+
+ slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name_cl,
+ "changelog5_config_delete: failed to remove changelog\n");
+ goto done;
+ }
+
+done:;
+ PR_RWLock_Unlock (s_configLock);
+
+ /* slapi_ch_free accepts NULL pointer */
+ slapi_ch_free ((void**)&currentDir);
+
+ if (*returncode == LDAP_SUCCESS)
+ {
+ if (returntext)
+ {
+ returntext[0] = '\0';
+ }
+
+ return SLAPI_DSE_CALLBACK_OK;
+ }
+
+ return SLAPI_DSE_CALLBACK_ERROR;
+}
+
+static int dont_allow_that(Slapi_PBlock *pb, Slapi_Entry* entryBefore, Slapi_Entry* e,
+ int *returncode, char *returntext, void *arg)
+{
+ *returncode = LDAP_UNWILLING_TO_PERFORM;
+ return SLAPI_DSE_CALLBACK_ERROR;
+}
+
+static changelog5Config * changelog5_dup_config(changelog5Config *config)
+{
+ changelog5Config *dup = (changelog5Config *) slapi_ch_calloc(1, sizeof(changelog5Config));
+
+ if (config->dir)
+ dup->dir = slapi_ch_strdup(config->dir);
+ if (config->maxAge)
+ dup->maxAge = slapi_ch_strdup(config->maxAge);
+
+ dup->maxEntries = config->maxEntries;
+
+ /*memcpy((void *) &dup->dbconfig, (const void *) &config->dbconfig, sizeof(CL5DBConfig));*/
+ dup->dbconfig.cacheSize = config->dbconfig.cacheSize;
+ dup->dbconfig.durableTrans = config->dbconfig.durableTrans;
+ dup->dbconfig.checkpointInterval = config->dbconfig.checkpointInterval;
+ dup->dbconfig.circularLogging = config->dbconfig.circularLogging;
+ dup->dbconfig.pageSize = config->dbconfig.pageSize;
+ dup->dbconfig.logfileSize = config->dbconfig.logfileSize;
+ dup->dbconfig.maxTxnSize = config->dbconfig.maxTxnSize;
+ dup->dbconfig.fileMode = config->dbconfig.fileMode;
+ dup->dbconfig.verbose = config->dbconfig.verbose;
+ dup->dbconfig.debug = config->dbconfig.debug;
+ dup->dbconfig.tricklePercentage = config->dbconfig.tricklePercentage;
+ dup->dbconfig.spinCount = config->dbconfig.spinCount;
+ dup->dbconfig.maxChCacheEntries = config->dbconfig.maxChCacheEntries;
+ dup->dbconfig.maxChCacheSize = config->dbconfig.maxChCacheSize;
+ dup->dbconfig.nb_lock_config = config->dbconfig.nb_lock_config;
+
+ return dup;
+}
+
+
+/*
+ * Given the changelog configuration entry, extract the configuration directives.
+ */
+static void changelog5_extract_config(Slapi_Entry* entry, changelog5Config *config)
+{
+ char *arg;
+
+ memset (config, 0, sizeof (*config));
+ config->dir = slapi_entry_attr_get_charptr(entry,CONFIG_CHANGELOG_DIR_ATTRIBUTE);
+ replace_bslash (config->dir);
+
+ arg= slapi_entry_attr_get_charptr(entry,CONFIG_CHANGELOG_MAXENTRIES_ATTRIBUTE);
+ if (arg)
+ {
+ config->maxEntries = atoi (arg);
+ slapi_ch_free_string(&arg);
+ }
+
+ config->maxAge = slapi_entry_attr_get_charptr(entry,CONFIG_CHANGELOG_MAXAGE_ATTRIBUTE);
+
+ /*
+ * Read the Changelog Internal Configuration Parameters for the Changelog DB
+ * (db cache size, db settings...)
+ */
+
+ /* Set configuration default values first... */
+ config->dbconfig.cacheSize = CL5_DEFAULT_CONFIG_DB_DBCACHESIZE;
+ config->dbconfig.durableTrans = CL5_DEFAULT_CONFIG_DB_DURABLE_TRANSACTIONS;
+ config->dbconfig.checkpointInterval = CL5_DEFAULT_CONFIG_DB_CHECKPOINT_INTERVAL;
+ config->dbconfig.circularLogging = CL5_DEFAULT_CONFIG_DB_CIRCULAR_LOGGING;
+ config->dbconfig.pageSize = CL5_DEFAULT_CONFIG_DB_PAGE_SIZE;
+ config->dbconfig.logfileSize = CL5_DEFAULT_CONFIG_DB_LOGFILE_SIZE;
+ config->dbconfig.maxTxnSize = CL5_DEFAULT_CONFIG_DB_TXN_MAX;
+ config->dbconfig.verbose = CL5_DEFAULT_CONFIG_DB_VERBOSE;
+ config->dbconfig.debug = CL5_DEFAULT_CONFIG_DB_DEBUG;
+ config->dbconfig.tricklePercentage = CL5_DEFAULT_CONFIG_DB_TRICKLE_PERCENTAGE;
+ config->dbconfig.spinCount = CL5_DEFAULT_CONFIG_DB_SPINCOUNT;
+ config->dbconfig.nb_lock_config = CL5_DEFAULT_CONFIG_NB_LOCK;
+
+ /* Now read from the entry to override default values if needed */
+ arg= slapi_entry_attr_get_charptr(entry, CONFIG_CHANGELOG_DB_DBCACHESIZE);
+ if (arg)
+ {
+ size_t theSize = atoi (arg);
+ if (theSize > CL5_MIN_DB_DBCACHESIZE)
+ config->dbconfig.cacheSize = theSize;
+ else {
+ config->dbconfig.cacheSize = CL5_MIN_DB_DBCACHESIZE;
+ slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name_cl,
+ "Warning: Changelog dbcache size too small. "
+ "Increasing the Memory Size to %d bytes\n",
+ CL5_MIN_DB_DBCACHESIZE);
+ }
+ slapi_ch_free_string(&arg);
+ }
+
+ arg= slapi_entry_attr_get_charptr(entry, CONFIG_CHANGELOG_DB_DURABLE_TRANSACTIONS);
+ if (arg)
+ {
+ config->dbconfig.durableTrans = atoi (arg);
+ slapi_ch_free_string(&arg);
+ }
+ arg= slapi_entry_attr_get_charptr(entry, CONFIG_CHANGELOG_DB_CHECKPOINT_INTERVAL);
+ if (arg)
+ {
+ config->dbconfig.checkpointInterval = atoi (arg);
+ slapi_ch_free_string(&arg);
+ }
+ arg= slapi_entry_attr_get_charptr(entry, CONFIG_CHANGELOG_DB_CIRCULAR_LOGGING);
+ if (arg)
+ {
+ config->dbconfig.circularLogging = atoi (arg);
+ slapi_ch_free_string(&arg);
+ }
+ arg= slapi_entry_attr_get_charptr(entry, CONFIG_CHANGELOG_DB_PAGE_SIZE);
+ if (arg)
+ {
+ config->dbconfig.pageSize = atoi (arg);
+ slapi_ch_free_string(&arg);
+ }
+ arg= slapi_entry_attr_get_charptr(entry, CONFIG_CHANGELOG_DB_LOGFILE_SIZE);
+ if (arg)
+ {
+ config->dbconfig.logfileSize = atoi (arg);
+ slapi_ch_free_string(&arg);
+ }
+ arg= slapi_entry_attr_get_charptr(entry, CONFIG_CHANGELOG_DB_MAXTXN_SIZE);
+ if (arg)
+ {
+ config->dbconfig.maxTxnSize = atoi (arg);
+ slapi_ch_free_string(&arg);
+ }
+ arg= slapi_entry_attr_get_charptr(entry, CONFIG_CHANGELOG_DB_VERBOSE);
+ if (arg)
+ {
+ config->dbconfig.verbose = atoi (arg);
+ slapi_ch_free_string(&arg);
+ }
+ arg= slapi_entry_attr_get_charptr(entry, CONFIG_CHANGELOG_DB_DEBUG);
+ if (arg)
+ {
+ config->dbconfig.debug = atoi (arg);
+ slapi_ch_free_string(&arg);
+ }
+ arg= slapi_entry_attr_get_charptr(entry, CONFIG_CHANGELOG_DB_TRICKLE_PERCENTAGE);
+ if (arg)
+ {
+ config->dbconfig.tricklePercentage = atoi (arg);
+ slapi_ch_free_string(&arg);
+ }
+ arg= slapi_entry_attr_get_charptr(entry, CONFIG_CHANGELOG_DB_SPINCOUNT);
+ if (arg)
+ {
+ config->dbconfig.spinCount = atoi (arg);
+ slapi_ch_free_string(&arg);
+ }
+ arg= slapi_entry_attr_get_charptr(entry, CONFIG_CHANGELOG_MAX_CONCURRENT_WRITES);
+ if (arg)
+ {
+ config->dbconfig.maxConcurrentWrites = atoi (arg);
+ slapi_ch_free_string(&arg);
+ }
+ if ( config->dbconfig.maxConcurrentWrites <= 0 )
+ {
+ config->dbconfig.maxConcurrentWrites = CL5_DEFAULT_CONFIG_MAX_CONCURRENT_WRITES;
+ }
+
+ /*
+ * Read the Changelog Internal Configuration Parameters for the Changelog Cache
+ */
+
+ /* Set configuration default values first... */
+ config->dbconfig.maxChCacheEntries = CL5_DEFAULT_CONFIG_CACHESIZE;
+ config->dbconfig.maxChCacheSize = CL5_DEFAULT_CONFIG_CACHEMEMSIZE;
+
+ /* Now read from the entry to override default values if needed */
+ arg= slapi_entry_attr_get_charptr(entry, CONFIG_CHANGELOG_CACHESIZE);
+ if (arg)
+ {
+ config->dbconfig.maxChCacheEntries = atoi (arg);
+ slapi_ch_free_string(&arg);
+ }
+ arg= slapi_entry_attr_get_charptr(entry, CONFIG_CHANGELOG_CACHEMEMSIZE);
+ if (arg)
+ {
+ config->dbconfig.maxChCacheSize = atoi (arg);
+ slapi_ch_free_string(&arg);
+ }
+ arg = slapi_entry_attr_get_charptr(entry, CONFIG_CHANGELOG_NB_LOCK);
+ if (arg)
+ {
+ size_t theSize = atoi(arg);
+ if (theSize < CL5_MIN_NB_LOCK)
+ {
+ slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name_cl,
+ "Warning: Changelog %s value is too low (%d). Set to minimal value instead (%d)\n",
+ CONFIG_CHANGELOG_NB_LOCK, theSize, CL5_MIN_NB_LOCK);
+ config->dbconfig.nb_lock_config = CL5_MIN_NB_LOCK;
+ }
+ else
+ {
+ config->dbconfig.nb_lock_config = theSize;
+ }
+ slapi_ch_free_string(&arg);
+ }
+
+ clcache_set_config(&config->dbconfig);
+}
+
+static void replace_bslash (char *dir)
+{
+ char *bslash;
+
+ if (dir == NULL)
+ return;
+
+ bslash = strchr (dir, '\\');
+ while (bslash)
+ {
+ *bslash = '/';
+ bslash = strchr (bslash, '\\');
+ }
+}
+
+static int notify_replica (Replica *r, void *arg)
+{
+ return replica_log_ruv_elements (r);
+}
+
+static int _is_absolutepath (char * dir)
+{
+ if (dir[0] == '/')
+ return 1;
+#if defined(_WIN32)
+ if (dir[2] == '/' && dir[1] == ':')
+ return 1;
+#endif
+ return 0;
+}
diff --git a/ldap/servers/plugins/replication/cl5_init.c b/ldap/servers/plugins/replication/cl5_init.c
new file mode 100644
index 00000000..435299c0
--- /dev/null
+++ b/ldap/servers/plugins/replication/cl5_init.c
@@ -0,0 +1,77 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+
+/* cl5_init.c - implments initialization/cleanup functions for
+ 4.0 style changelog
+ */
+
+#include "slapi-plugin.h"
+#include "cl5.h"
+#include "repl5.h"
+
+/* initializes changelog*/
+int changelog5_init()
+{
+ int rc;
+ changelog5Config config;
+
+ rc = cl5Init ();
+ if (rc != CL5_SUCCESS)
+ {
+ slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name_cl,
+ "changelog5_init: failed to initialize changelog\n");
+ return 1;
+ }
+
+ /* read changelog configuration */
+ changelog5_config_init ();
+ changelog5_read_config (&config);
+
+ if (config.dir == NULL)
+ {
+ /* changelog is not configured - bail out */
+ rc = 0; /* OK */
+ goto done;
+ }
+
+ /* start changelog */
+ rc = cl5Open (config.dir, &config.dbconfig);
+ if (rc != CL5_SUCCESS)
+ {
+ slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name_cl,
+ "changelog5_init: failed to start changelog at %s\n",
+ config.dir);
+ rc = 1;
+ goto done;
+ }
+
+ /* set trimming parameters */
+ rc = cl5ConfigTrimming (config.maxEntries, config.maxAge);
+ if (rc != CL5_SUCCESS)
+ {
+ slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name_cl,
+ "changelog5_init: failed to configure changelog trimming\n");
+ rc = 1;
+ goto done;
+ }
+
+ rc = 0;
+
+done:
+ changelog5_config_done (&config);
+ return rc;
+}
+
+/* cleanups changelog data */
+void changelog5_cleanup()
+{
+ /* close changelog */
+ cl5Close ();
+ cl5Cleanup ();
+
+ /* cleanup config */
+ changelog5_config_cleanup ();
+}
diff --git a/ldap/servers/plugins/replication/cl5_test.c b/ldap/servers/plugins/replication/cl5_test.c
new file mode 100644
index 00000000..b64a60f5
--- /dev/null
+++ b/ldap/servers/plugins/replication/cl5_test.c
@@ -0,0 +1,830 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+/* cl5_test.c - changelog test cases */
+#include "cl5_test.h"
+#include "slapi-plugin.h"
+#include "cl5.h"
+
+#define REPLICA_ROOT "dc=example,dc=com" /* replica root */
+#define OP_COUNT 4 /* number of ops generated at a time */
+#define MOD_COUNT 5
+#define VALUE_COUNT 5
+#define ENTRY_COUNT 50
+#define CL_DN "cn=changelog5,cn=config"
+#define INSTANCE_ATTR "nsslapd-instancedir"
+#define REPLICA_OC "nsds5Replica"
+#define REPLICA_RDN "cn=replica"
+
+static void testBasic ();
+static void testBackupRestore ();
+static void testIteration ();
+static void testTrimming ();
+static void testPerformance ();
+static void testPerformanceMT ();
+static void testLDIF ();
+static void testAll ();
+static int configureChangelog ();
+static int configureReplica ();
+static int populateChangelogOp ();
+static int populateChangelog (int entryCount, CSN ***csnList);
+static int processEntries (int entryCount, CSN **csnList);
+static void clearCSNList (CSN ***csnList, int count);
+static void threadMain (void *data);
+static char* getBaseDir (const char *dir);
+static LDAPMod **buildMods ();
+
+void testChangelog (TestType type)
+{
+ switch (type)
+ {
+ case TEST_BASIC: testBasic ();
+ break;
+ case TEST_BACKUP_RESTORE: testBackupRestore ();
+ break;
+ case TEST_ITERATION: testIteration ();
+ break;
+ case TEST_TRIMMING: testTrimming ();
+ break;
+ case TEST_PERFORMANCE: testPerformance ();
+ break;
+ case TEST_PERFORMANCE_MT: testPerformanceMT ();
+ break;
+ case TEST_LDIF: testLDIF ();
+ break;
+ case TEST_ALL: testAll ();
+ break;
+ default: printf ("Taste case %d is not supported\n", type);
+ }
+}
+
+/* tests Open/Close, normal recovery, read/write/remove
+ of an entry */
+static void testBasic ()
+{
+ int rc = 0;
+
+ slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name_cl, "Starting basic test ...\n");
+
+ /* ONREPL - we can't run the tests from the startup code because
+ operations can't be issued until all plugins are started. So,
+ instead, we do it when changelog is created
+ rc = configureChangelog (); */
+ if (rc == 0)
+ {
+ rc = configureReplica ();
+ if (rc == 0)
+ {
+ rc = populateChangelogOp ();
+ }
+ }
+
+ if (rc == 0)
+ slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name_cl,
+ "Basic test completed successfully\n");
+ else
+ slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name_cl,
+ "Basic test failed\n");
+}
+
+static void testBackupRestore ()
+{
+ char *dir;
+ int rc = -1;
+ char *baseDir;
+ char bkDir [MAXPATHLEN + 1];
+
+ slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name_cl, "Starting backup and recovery test ...\n");
+
+ dir = cl5GetDir ();
+
+ if (dir)
+ {
+ baseDir = getBaseDir (dir);
+ sprintf (bkDir, "%s/clbackup", baseDir);
+ slapi_ch_free ((void**)&baseDir);
+ rc = cl5Backup (bkDir, NULL);
+
+ if (rc == CL5_SUCCESS)
+ {
+ cl5Close ();
+ rc = cl5Restore (dir, bkDir, NULL);
+ if (rc == CL5_SUCCESS)
+ rc = cl5Open (dir, NULL);
+
+ /* PR_RmDir (bkDir);*/
+ }
+ }
+
+ if (rc == CL5_SUCCESS)
+ slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name_cl,
+ "Backup and Restore test completed successfully\n");
+ else
+ slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name_cl,
+ "Backup and Restore test failed\n");
+}
+
+static void testIteration ()
+{
+ Object *r_obj;
+ Slapi_DN *r_root;
+ Replica *r;
+ char *replGen;
+ RUV *ruv;
+ CL5ReplayIterator *it = NULL;
+ slapi_operation_parameters op;
+ int rc;
+ int i;
+
+ slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name_cl, "Starting iteration test ...\n");
+
+ /* get replica object */
+ r_root = slapi_sdn_new_dn_byval(REPLICA_ROOT);
+ r_obj = replica_get_replica_from_dn (r_root);
+ slapi_sdn_free (&r_root);
+ if (r_obj == NULL)
+ {
+ slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name_cl, "replica is not configured for (%s)\n",
+ REPLICA_ROOT);
+ return;
+ }
+
+ slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name_cl, "Starting first iteration pass ...\n");
+
+ /* configure empty consumer ruv */
+ r = (Replica*)object_get_data (r_obj);
+ PR_ASSERT (r);
+ replGen = replica_get_generation (r);
+ ruv_init_new (replGen, 0, NULL, &ruv);
+
+ /* create replay iterator */
+ rc = cl5CreateReplayIterator (r_obj, ruv, &it);
+ if (it)
+ {
+ i = 0;
+ while ((rc = cl5GetNextOperationToReplay (it, &op)) == CL5_SUCCESS)
+ {
+ ruv_set_csns (ruv, op.csn, NULL);
+ operation_parameters_done (&op);
+ i ++;
+ }
+ }
+
+ if (it)
+ cl5DestroyReplayIterator (&it);
+
+ if (rc == CL5_NOTFOUND)
+ {
+ if (i == 0) /* success */
+ slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name_cl, "First iteration pass completed "
+ "successfully: no changes to replay\n");
+ else /* incorrect number of entries traversed */
+ slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name_cl, "First iteration pass failed: "
+ "traversed %d entries; expected none\n", i);
+ }
+ else /* general error */
+ slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name_cl, "First iteration pass failed\n");
+
+ slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name_cl, "Starting second iteration pass ...\n");
+
+ /* add some entries */
+ populateChangelogOp ();
+
+ /* create replay iterator */
+ rc = cl5CreateReplayIterator (r_obj, ruv, &it);
+ if (it)
+ {
+ i = 0;
+ while ((rc = cl5GetNextOperationToReplay (it, &op)) == CL5_SUCCESS)
+ {
+ ruv_set_csns (ruv, op.csn, NULL);
+ operation_parameters_done (&op);
+ i ++;
+ }
+ }
+
+ if (it)
+ cl5DestroyReplayIterator (&it);
+
+ if (rc == CL5_NOTFOUND)
+ {
+ if (i == OP_COUNT) /* success */
+ slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name_cl, "Second iteration pass completed "
+ "successfully: %d entries traversed\n", i);
+ else /* incorrect number of entries traversed */
+ slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name_cl, "Second iteration pass failed: "
+ "traversed %d entries; expected %d\n", i, OP_COUNT);
+ }
+ else /* general error */
+ slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name_cl, "Second iteration pass failed\n");
+
+ slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name_cl, "Starting third iteration pass ...\n");
+ /* add more entries */
+ populateChangelogOp ();
+
+ /* create replay iterator */
+ rc = cl5CreateReplayIterator (r_obj, ruv, &it);
+ if (it)
+ {
+ i = 0;
+ while ((rc = cl5GetNextOperationToReplay (it, &op)) == CL5_SUCCESS)
+ {
+ ruv_set_csns (ruv, op.csn, NULL);
+ operation_parameters_done (&op);
+ i ++;
+ }
+ }
+
+ if (it)
+ cl5DestroyReplayIterator (&it);
+
+ if (rc == CL5_NOTFOUND)
+ {
+ if (i == OP_COUNT) /* success */
+ slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name_cl, "Third iteration pass completed "
+ "successfully: %d entries traversed\n", i);
+ else /* incorrect number of entries traversed */
+ slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name_cl, "Third iteration pass failed: "
+ "traversed %d entries; expected %d\n", i, OP_COUNT);
+ }
+ else /* general error */
+ slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name_cl, "Second iteration pass failed\n");
+
+ slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name_cl, "Iteration test is complete\n");
+
+ ruv_destroy (&ruv);
+ object_release (r_obj);
+ slapi_ch_free ((void**)&replGen);
+}
+
+static void testTrimming ()
+{
+ PRIntervalTime interval;
+ int count;
+ int rc;
+
+ slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name_cl, "Starting trimming test ...\n");
+
+ rc = populateChangelog (200, NULL);
+
+ if (rc == 0)
+ {
+ interval = PR_SecondsToInterval(2);
+ DS_Sleep (interval);
+
+ rc = populateChangelog (300, NULL);
+
+ if (rc == 0)
+ rc = cl5ConfigTrimming (300, "1d");
+
+ interval = PR_SecondsToInterval(300); /* 5 min is default trimming interval */
+ slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name_cl,
+ "Trimming test: sleeping for 5 minutes until trimming kicks in\n");
+ DS_Sleep (interval);
+
+ count = cl5GetOperationCount (NULL);
+ }
+
+ if (rc == 0 && count == 300)
+ {
+ slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name_cl,
+ "Trimming test completed successfully: changelog contains 300 entries\n");
+ }
+ else if (rc == 0)
+ {
+ slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name_cl,
+ "Trimming test failed: changelog contains %d entries; expected - 300\n",
+ count);
+ }
+ else /* general failure */
+ slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name_cl, "Trimming test failed\n");
+}
+
+static void testPerformance ()
+{
+ PRTime starttime, endtime, totaltime;
+ int entryCount = 5000;
+ CSN **csnList = NULL;
+ int rc;
+
+ starttime = PR_Now();
+
+ rc = populateChangelog (entryCount, &csnList);
+
+ endtime = PR_Now();
+
+ totaltime = (endtime - starttime) / 1000; /* ms */
+ slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name_cl, "Write performance:\n"
+ "entry count - %d, total time - %ldms\n"
+ "latency = %d msec\nthroughput = %d entry/sec\n",
+ entryCount, totaltime,
+ totaltime / entryCount, entryCount * 1000 / totaltime);
+
+
+ starttime = endtime;
+
+ rc = processEntries (entryCount, csnList);
+
+ endtime = PR_Now();
+
+ totaltime = (endtime - starttime) / 1000; /* ms */
+ slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name_cl, "Read performance:\n"
+ "entry count - %d, total time - %ld\n"
+ "latency = %d msec\nthroughput = %d entry/sec\n",
+ entryCount, totaltime,
+ totaltime / entryCount, entryCount * 1000 / totaltime);
+
+ clearCSNList (&csnList, entryCount);
+}
+
+static int threadsLeft;
+static void testPerformanceMT ()
+{
+ PRTime starttime, endtime, totaltime;
+ int entryCount = 200;
+ int threadCount = 10;
+ int entryTotal;
+ int i;
+ PRIntervalTime interval;
+
+ interval = PR_MillisecondsToInterval(100);
+ threadsLeft = threadCount * 2;
+ entryTotal = threadCount * entryCount;
+ starttime = PR_Now();
+
+ for (i = 0; i < threadCount; i++)
+ {
+ PR_CreateThread(PR_USER_THREAD, threadMain, (void*)&entryCount,
+ PR_PRIORITY_NORMAL, PR_GLOBAL_THREAD,
+ PR_UNJOINABLE_THREAD, 0);
+ }
+
+ while (threadsLeft > 5)
+ DS_Sleep (interval);
+
+ endtime = PR_Now();
+
+ totaltime = (endtime - starttime) / 1000; /* ms */
+ slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name_cl, "Write performance:\n"
+ "entry count - %d, total time - %ld\n"
+ "latency = %d msec per entry\nthroughput = %d entry/sec\n",
+ entryCount, totaltime,
+ totaltime / entryTotal, entryTotal * 1000 / totaltime);
+
+
+ starttime = endtime;
+
+ while (threadsLeft != 0)
+ DS_Sleep (interval);
+
+ endtime = PR_Now();
+
+ totaltime = (endtime - starttime) / 1000; /* ms */
+ slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name_cl, "Read performance:\n"
+ "entry count - %d, total time - %ld\n"
+ "latency = %d msec per entry\nthroughput = %d entry/sec\n",
+ entryCount, totaltime,
+ totaltime / entryTotal, entryTotal * 1000 / totaltime);
+}
+
+static void testLDIF ()
+{
+ char *clDir = cl5GetDir ();
+ int rc;
+ char *baseDir;
+ char ldifFile [MAXPATHLEN + 1];
+
+ slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name_cl, "Starting LDIF test ...\n");
+
+ baseDir = getBaseDir (clDir);
+ sprintf (ldifFile, "%s/cl5.ldif", baseDir);
+ slapi_ch_free ((void**)&baseDir);
+ rc = populateChangelog (ENTRY_COUNT, NULL);
+
+ if (rc == CL5_SUCCESS)
+ {
+ rc = cl5ExportLDIF (ldifFile, NULL);
+ if (rc == CL5_SUCCESS)
+ {
+ cl5Close();
+ rc = cl5ImportLDIF (clDir, ldifFile, NULL);
+ if (rc == CL5_SUCCESS)
+ cl5Open(clDir, NULL);
+ }
+ }
+
+ PR_Delete (ldifFile);
+
+ if (rc == CL5_SUCCESS)
+ slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name_cl,
+ "LDIF test completed successfully\n");
+ else
+ slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name_cl, "LDIF test failed\n");
+}
+
+static void testAll ()
+{
+ testBasic ();
+
+ testIteration ();
+
+ testBackupRestore ();
+
+ testLDIF ();
+
+ /* testTrimming ();*/
+
+#if 0
+ /* xxxPINAKI */
+ /* these tests are not working correctly...the call to db->put() */
+ /* just hangs forever */
+ slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name_cl,
+ "Starting single threaded performance measurement ...\n");
+ testPerformance ();
+
+ slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name_cl,
+ "Starting multi threaded performance measurement ...\n");
+ testPerformanceMT ();
+#endif
+
+}
+
+static int populateChangelog (int entryCount, CSN ***csnList)
+{
+ CSN *csn;
+ int i;
+ slapi_operation_parameters op;
+ int rc;
+ char *uniqueid;
+
+ if (csnList)
+ {
+ (*csnList) = (CSN**)slapi_ch_calloc (entryCount, sizeof (CSN*));
+ }
+
+ /* generate entries */
+ for (i = 0; i < entryCount; i++)
+ {
+ /* ONREPL need to get replica object
+ rc = csnGetNewCSNForRepl (&csn);
+ if (rc != CL5_SUCCESS) */
+ return -1;
+
+ if (csnList)
+ (*csnList) [i] = csn_dup (csn);
+ memset (&op, 0, sizeof (op));
+ op.csn = csn;
+ slapi_uniqueIDGenerateString(&uniqueid);
+ op.target_address.uniqueid = uniqueid;
+ op.target_address.dn = slapi_ch_strdup ("cn=entry,dc=example,dc=com");
+ if (i % 5 == 0)
+ {
+ op.operation_type = SLAPI_OPERATION_MODRDN;
+ op.p.p_modrdn.modrdn_deloldrdn = 1;
+ op.p.p_modrdn.modrdn_newrdn = slapi_ch_strdup("cn=entry2,dc=example,dc=com");
+ op.p.p_modrdn.modrdn_newsuperior_address.dn = NULL;
+ op.p.p_modrdn.modrdn_newsuperior_address.uniqueid = NULL;
+ op.p.p_modrdn.modrdn_mods = buildMods ();
+ }
+ else if (i % 4 == 0)
+ {
+ op.operation_type = SLAPI_OPERATION_DELETE;
+ }
+ else if (i % 3 == 0)
+ {
+
+ op.operation_type = SLAPI_OPERATION_ADD;
+ op.p.p_add.target_entry = slapi_entry_alloc ();
+ slapi_entry_set_dn (op.p.p_add.target_entry, slapi_ch_strdup(op.target_address.dn));
+ slapi_entry_set_uniqueid (op.p.p_add.target_entry, slapi_ch_strdup(op.target_address.uniqueid));
+ slapi_entry_attr_set_charptr(op.p.p_add.target_entry, "objectclass", "top");
+ slapi_entry_attr_set_charptr(op.p.p_add.target_entry, "cn", "entry");
+ }
+ else
+ {
+ op.operation_type = SLAPI_OPERATION_MODIFY;
+ op.p.p_modify.modify_mods = buildMods ();
+ }
+
+ /* ONREPL rc = cl5WriteOperation (&op, 1);*/
+ operation_parameters_done (&op);
+
+ if (rc != CL5_SUCCESS)
+ return -1;
+ }
+
+ slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name_cl,
+ "Successfully populated changelog with %d entries\n", entryCount);
+ return 0;
+}
+
+static int processEntries (int entryCount, CSN **csnList)
+{
+ int i;
+ int rc = 0;
+ slapi_operation_parameters op;
+
+ for (i = 0; i < entryCount; i++)
+ {
+ memset (&op, 0, sizeof (op));
+
+ op.csn = csn_dup (csnList [i]);
+
+ /* rc = cl5GetOperation (&op);*/
+ if (rc != CL5_SUCCESS)
+ return -1;
+
+ operation_parameters_done (&op);
+ }
+
+ slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name_cl,
+ "Successfully read %d entries from the changelog\n", entryCount);
+ return 0;
+}
+
+void clearCSNList (CSN ***csnList, int count)
+{
+ int i;
+
+ for (i = 0; i < count; i++)
+ {
+ csn_free (&((*csnList)[i]));
+ }
+
+ slapi_ch_free ((void**)csnList);
+}
+
+static void threadMain (void *data)
+{
+ int entryCount = *(int*)data;
+ CSN **csnList;
+
+ populateChangelog (entryCount, &csnList);
+ PR_AtomicDecrement (&threadsLeft);
+
+ processEntries (entryCount, csnList);
+ PR_AtomicDecrement (&threadsLeft);
+
+ clearCSNList (&csnList, entryCount);
+}
+
+static char* getBaseDir (const char *dir)
+{
+ char *baseDir = slapi_ch_strdup (dir);
+ char *ch;
+
+ ch = &(baseDir [strlen (dir) - 2]);
+
+ while (ch >= baseDir && *ch != '\\' && *ch != '/')
+ ch --;
+
+ if (ch >= baseDir)
+ {
+ *ch = '\0';
+ }
+
+ return baseDir;
+}
+
+static LDAPMod **buildMods ()
+{
+ Slapi_Mods smods;
+ Slapi_Mod smod;
+ LDAPMod **mods;
+ struct berval bv;
+ int j, k;
+
+ slapi_mods_init (&smods, MOD_COUNT);
+
+ for (j = 0; j < MOD_COUNT; j++)
+ {
+ slapi_mod_init (&smod, VALUE_COUNT);
+ slapi_mod_set_operation (&smod, LDAP_MOD_ADD | LDAP_MOD_BVALUES);
+ slapi_mod_set_type (&smod, "attr");
+
+ for (k = 0; k < VALUE_COUNT; k++)
+ {
+ bv.bv_val = "bvalue";
+ bv.bv_len = strlen (bv.bv_val) + 1;
+ slapi_mod_add_value (&smod, &bv);
+ }
+
+ slapi_mods_add_smod (&smods, &smod);
+ /* ONREPL slapi_mod_done (&smod); */
+ }
+
+ mods = slapi_mods_get_ldapmods_passout (&smods);
+ slapi_mods_done (&smods);
+ return mods;
+}
+
+/* Format:
+ dn: cn=changelog5,cn=config
+ objectclass: top
+ objectclass: extensibleObject
+ cn: changelog5
+ nsslapd-changelogDir: d:/netscape/server4/slapd-elf/cl5 */
+static int configureChangelog ()
+{
+ Slapi_PBlock *pb = slapi_pblock_new ();
+ Slapi_Entry *e = slapi_entry_alloc ();
+ int rc;
+ char *attrs[] = {INSTANCE_ATTR, NULL};
+ Slapi_Entry **entries;
+ char cl_dir [256];
+ char *str = NULL;
+
+ /* set changelog dn */
+ slapi_entry_set_dn (e, slapi_ch_strdup (CL_DN));
+
+ /* set object classes */
+ slapi_entry_add_string(e, "objectclass", "top");
+ slapi_entry_add_string(e, "objectclass", "extensibleObject");
+
+ /* get directory instance dir */
+ slapi_search_internal_set_pb (pb, "cn=config", LDAP_SCOPE_BASE, "objectclass=*",
+ attrs, 0, NULL, NULL,
+ repl_get_plugin_identity (PLUGIN_MULTIMASTER_REPLICATION), 0);
+ slapi_search_internal_pb (pb);
+
+ slapi_pblock_get(pb, SLAPI_PLUGIN_INTOP_RESULT, &rc);
+ if (rc != LDAP_SUCCESS)
+ {
+ slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name_cl, "failed to get server instance "
+ "directory; LDAP error - %d\n", rc);
+ rc = -1;
+ goto done;
+ }
+
+ slapi_pblock_get(pb, SLAPI_PLUGIN_INTOP_SEARCH_ENTRIES, &entries);
+ str = slapi_entry_attr_get_charptr(entries[0], INSTANCE_ATTR);
+ sprintf (cl_dir, "%s/%s", str, "cl5db");
+ slapi_ch_free((void **)&str);
+ slapi_entry_add_string (e, CONFIG_CHANGELOG_DIR_ATTRIBUTE, cl_dir);
+
+ slapi_free_search_results_internal(pb);
+ slapi_pblock_destroy (pb);
+
+ pb = slapi_pblock_new ();
+
+ slapi_add_entry_internal_set_pb (pb, e, NULL,
+ repl_get_plugin_identity (PLUGIN_MULTIMASTER_REPLICATION), 0);
+
+ slapi_add_internal_pb (pb);
+ slapi_pblock_get(pb, SLAPI_PLUGIN_INTOP_RESULT, &rc);
+ if (rc != LDAP_SUCCESS)
+ {
+ slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name_cl, "failed to add changelog "
+ "configuration entry; LDAP error - %d\n", rc);
+ rc = -1;
+ }
+ else
+ rc = 0;
+
+done:
+ slapi_pblock_destroy (pb);
+
+ return rc;
+}
+
+/* Format:
+ dn: cn=replica,cn="o=NetscapeRoot",cn= mapping tree,cn=config
+ objectclass: top
+ objectclass: nsds5Replica
+ objectclass: extensibleObject
+ nsds5ReplicaRoot: o=NetscapeRoot
+ nsds5ReplicaId: 2
+ nsds5flags: 1
+ cn: replica
+ */
+static int configureReplica ()
+{
+ Slapi_PBlock *pb = slapi_pblock_new ();
+ Slapi_Entry *e = slapi_entry_alloc ();
+ int rc;
+ char dn [128];
+
+ /* set changelog dn */
+ sprintf (dn, "%s,cn=\"%s\",%s", REPLICA_RDN, REPLICA_ROOT,
+ slapi_get_mapping_tree_config_root ());
+ slapi_entry_set_dn (e, slapi_ch_strdup (dn));
+
+ /* set object classes */
+ slapi_entry_add_string(e, "objectclass", "top");
+ slapi_entry_add_string(e, "objectclass", REPLICA_OC);
+ slapi_entry_add_string(e, "objectclass", "extensibleObject");
+
+ /* set other attributes */
+ slapi_entry_add_string (e, attr_replicaRoot, REPLICA_ROOT);
+ slapi_entry_add_string (e, attr_replicaId, "1");
+ slapi_entry_add_string (e, attr_flags, "1");
+
+ slapi_add_entry_internal_set_pb (pb, e, NULL,
+ repl_get_plugin_identity (PLUGIN_MULTIMASTER_REPLICATION), 0);
+
+ slapi_add_internal_pb (pb);
+ slapi_pblock_get(pb, SLAPI_PLUGIN_INTOP_RESULT, &rc);
+ if (rc != LDAP_SUCCESS)
+ {
+ slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name_cl, "failed to add replica for (%s) "
+ "configuration entry; LDAP error - %d\n", REPLICA_ROOT, rc);
+ rc = -1;
+ }
+ else
+ rc = 0;
+
+ slapi_pblock_destroy (pb);
+
+ return rc;
+}
+
+/* generates one of each ldap operations */
+static int populateChangelogOp ()
+{
+ Slapi_PBlock *pb = slapi_pblock_new ();
+ Slapi_Entry *e = slapi_entry_alloc ();
+ int rc;
+ char dn [128], newrdn [64];
+ LDAPMod *mods[2];
+ Slapi_Mod smod;
+ struct berval bv;
+ time_t cur_time;
+
+ /* add entry */
+ cur_time = time(NULL);
+ sprintf (dn, "cn=%s,%s", ctime(&cur_time), REPLICA_ROOT);
+ slapi_entry_set_dn (e, slapi_ch_strdup (dn));
+ slapi_entry_add_string(e, "objectclass", "top");
+ slapi_entry_add_string(e, "objectclass", "extensibleObject");
+ slapi_entry_add_string (e, "mail", "jsmith@netscape.com");
+
+ slapi_add_entry_internal_set_pb (pb, e, NULL,
+ repl_get_plugin_identity (PLUGIN_MULTIMASTER_REPLICATION), 0);
+ slapi_add_internal_pb (pb);
+ slapi_pblock_get(pb, SLAPI_PLUGIN_INTOP_RESULT, &rc);
+ slapi_pblock_destroy (pb);
+ if (rc != LDAP_SUCCESS)
+ {
+ slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name_cl, "failed to add entry (%s); "
+ "LDAP error - %d\n", dn, rc);
+ return -1;
+ }
+
+ /* modify entry */
+ pb = slapi_pblock_new ();
+ slapi_mod_init (&smod, 1);
+ slapi_mod_set_type (&smod, "mail");
+ slapi_mod_set_operation (&smod, LDAP_MOD_REPLACE | LDAP_MOD_BVALUES);
+ bv.bv_val = "jsmith@aol.com";
+ bv.bv_len = strlen (bv.bv_val);
+ slapi_mod_add_value(&smod, &bv);
+ mods[0] = (LDAPMod*)slapi_mod_get_ldapmod_byref(&smod);
+ mods[1] = NULL;
+ slapi_modify_internal_set_pb (pb, dn, mods, NULL, NULL,
+ repl_get_plugin_identity (PLUGIN_MULTIMASTER_REPLICATION), 0);
+ slapi_modify_internal_pb (pb);
+ slapi_pblock_get(pb, SLAPI_PLUGIN_INTOP_RESULT, &rc);
+ slapi_mod_done (&smod);
+ slapi_pblock_destroy (pb);
+ if (rc != LDAP_SUCCESS)
+ {
+ slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name_cl, "failed to modify entry (%s); "
+ "LDAP error - %d\n", dn, rc);
+ return -1;
+ }
+
+ /* rename entry */
+ pb = slapi_pblock_new ();
+ cur_time = time (NULL);
+ sprintf (newrdn, "cn=renamed%s", ctime(&cur_time));
+ slapi_rename_internal_set_pb (pb, dn, newrdn, NULL, 1, NULL, NULL,
+ repl_get_plugin_identity (PLUGIN_MULTIMASTER_REPLICATION), 0);
+ slapi_modrdn_internal_pb (pb);
+ slapi_pblock_get(pb, SLAPI_PLUGIN_INTOP_RESULT, &rc);
+ slapi_pblock_destroy (pb);
+ if (rc != LDAP_SUCCESS)
+ {
+ slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name_cl, "failed to rename entry (%s); "
+ "LDAP error - %d\n", dn, rc);
+ return -1;
+ }
+
+ /* delete the entry */
+ pb = slapi_pblock_new ();
+ sprintf (dn, "%s,%s", newrdn, REPLICA_ROOT);
+ slapi_delete_internal_set_pb (pb, dn, NULL, NULL,
+ repl_get_plugin_identity (PLUGIN_MULTIMASTER_REPLICATION), 0);
+ slapi_delete_internal_pb (pb);
+ slapi_pblock_get(pb, SLAPI_PLUGIN_INTOP_RESULT, &rc);
+ slapi_pblock_destroy (pb);
+ if (rc != LDAP_SUCCESS)
+ {
+ slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name_cl, "failed to delete entry (%s); "
+ "LDAP error - %d\n", dn, rc);
+ return -1;
+ }
+
+ return 0;
+}
diff --git a/ldap/servers/plugins/replication/cl5_test.h b/ldap/servers/plugins/replication/cl5_test.h
new file mode 100644
index 00000000..57d8435c
--- /dev/null
+++ b/ldap/servers/plugins/replication/cl5_test.h
@@ -0,0 +1,21 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+/* cl5_test.h - changelog test cases */
+
+typedef enum
+{
+ TEST_BASIC, /* open-close-delete, read-write-delete */
+ TEST_BACKUP_RESTORE,/* test backup and recovery */
+ TEST_ITERATION, /* similar to iteration used by replica upsate protocol */
+ TEST_TRIMMING, /* test changelog trimming */
+ TEST_PERFORMANCE, /* test read/write performance */
+ TEST_PERFORMANCE_MT,/* test multithreaded performance */
+ TEST_LDIF, /* test cl2ldif and ldif2cl */
+ TEST_ALL /* collective test */
+} TestType;
+
+void testChangelog (TestType type);
+
diff --git a/ldap/servers/plugins/replication/csnpl.c b/ldap/servers/plugins/replication/csnpl.c
new file mode 100644
index 00000000..7180af67
--- /dev/null
+++ b/ldap/servers/plugins/replication/csnpl.c
@@ -0,0 +1,328 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+
+#include "csnpl.h"
+#include "llist.h"
+#include "repl_shared.h"
+
+struct csnpl
+{
+ LList* csnList; /* pending list */
+ PRRWLock* csnLock; /* lock to serialize access to PL */
+};
+
+typedef struct _csnpldata
+{
+ PRBool committed; /* True if CSN committed */
+ CSN *csn; /* The actual CSN */
+} csnpldata;
+
+/* forward declarations */
+#ifdef DEBUG
+static void _csnplDumpContentNoLock(CSNPL *csnpl, const char *caller);
+#endif
+
+CSNPL* csnplNew ()
+{
+ CSNPL *csnpl;
+
+ csnpl = (CSNPL *)slapi_ch_malloc (sizeof (CSNPL));
+ if (csnpl == NULL)
+ {
+ slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name,
+ "csnplNew: failed to allocate pending list\n");
+ return NULL;
+ }
+
+ csnpl->csnList = llistNew ();
+ if (csnpl->csnList == NULL)
+ {
+ slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name,
+ "csnplNew: failed to allocate pending list\n");
+ slapi_ch_free ((void**)&csnpl);
+ return NULL;
+ }
+
+ /* ONREPL: do locks need different names */
+ csnpl->csnLock = PR_NewRWLock(PR_RWLOCK_RANK_NONE, "pl_lock");
+
+ if (csnpl->csnLock == NULL)
+ {
+ slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name,
+ "csnplNew: failed to create lock; NSPR error - %d\n",
+ PR_GetError ());
+ slapi_ch_free ((void**)&(csnpl->csnList));
+ slapi_ch_free ((void**)&csnpl);
+ return NULL;
+ }
+
+ return csnpl;
+}
+
+
+void
+csnpldata_free(void **data)
+{
+ csnpldata **data_to_free = (csnpldata **)data;
+ if (NULL != data_to_free)
+ {
+ if (NULL != (*data_to_free)->csn)
+ {
+ csn_free(&(*data_to_free)->csn);
+ }
+ slapi_ch_free((void **)data_to_free);
+ }
+}
+
+void csnplFree (CSNPL **csnpl)
+{
+ if ((csnpl == NULL) || (*csnpl == NULL))
+ return;
+
+ /* free all remaining nodes */
+ llistDestroy (&((*csnpl)->csnList), (FNFree)csnpldata_free);
+
+ if ((*csnpl)->csnLock);
+ PR_DestroyRWLock ((*csnpl)->csnLock);
+
+ slapi_ch_free ((void**)csnpl);
+}
+
+/* This function isnerts a CSN into the pending list
+ * Returns: 0 if the csn was successfully inserted
+ * 1 if the csn has already been seen
+ * -1 for any other kind of errors
+ */
+int csnplInsert (CSNPL *csnpl, const CSN *csn)
+{
+ int rc;
+ csnpldata *csnplnode;
+ char csn_str[CSN_STRSIZE];
+
+ if (csnpl == NULL || csn == NULL)
+ {
+ slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name,
+ "csnplInsert: invalid argument\n");
+ return -1;
+ }
+
+ PR_RWLock_Wlock (csnpl->csnLock);
+
+ /* check to see if this csn is larger than the last csn in the
+ pending list. It has to be if we have not seen it since
+ the csns are always added in the accending order. */
+ csnplnode = llistGetTail (csnpl->csnList);
+ if (csnplnode && csn_compare (csnplnode->csn, csn) >= 0)
+ {
+ PR_RWLock_Unlock (csnpl->csnLock);
+ return 1;
+ }
+
+ csnplnode = (csnpldata *)slapi_ch_malloc(sizeof(csnpldata));
+ csnplnode->committed = PR_FALSE;
+ csnplnode->csn = csn_dup(csn);
+ csn_as_string(csn, PR_FALSE, csn_str);
+ rc = llistInsertTail (csnpl->csnList, csn_str, csnplnode);
+
+#ifdef DEBUG
+ _csnplDumpContentNoLock(csnpl, "csnplInsert");
+#endif
+
+ PR_RWLock_Unlock (csnpl->csnLock);
+ if (rc != 0)
+ {
+ char s[CSN_STRSIZE];
+ slapi_log_error(SLAPI_LOG_REPL, repl_plugin_name,
+ "csnplInsert: failed to insert csn (%s) into pending list\n", csn_as_string(csn,PR_FALSE,s));
+ return -1;
+ }
+
+ return 0;
+}
+
+int csnplRemove (CSNPL *csnpl, const CSN *csn)
+{
+ csnpldata *data;
+ char csn_str[CSN_STRSIZE];
+
+ if (csnpl == NULL || csn == NULL)
+ {
+ slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name,
+ "csnplRemove: invalid argument\n");
+ return -1;
+ }
+
+ csn_as_string(csn, PR_FALSE, csn_str);
+ PR_RWLock_Wlock (csnpl->csnLock);
+
+ data = (csnpldata *)llistRemove (csnpl->csnList, csn_str);
+ if (data == NULL)
+ {
+ PR_RWLock_Unlock (csnpl->csnLock);
+ return -1;
+ }
+
+#ifdef DEBUG
+ _csnplDumpContentNoLock(csnpl, "csnplRemove");
+#endif
+
+ csn_free(&data->csn);
+ slapi_ch_free((void **)&data);
+
+ PR_RWLock_Unlock (csnpl->csnLock);
+
+ return 0;
+}
+
+int csnplCommit (CSNPL *csnpl, const CSN *csn)
+{
+ csnpldata *data;
+ char csn_str[CSN_STRSIZE];
+
+ if (csnpl == NULL || csn == NULL)
+ {
+ slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name,
+ "csnplCommit: invalid argument\n");
+ return -1;
+ }
+ csn_as_string(csn, PR_FALSE, csn_str);
+
+ PR_RWLock_Wlock (csnpl->csnLock);
+
+#ifdef DEBUG
+ _csnplDumpContentNoLock(csnpl, "csnplCommit");
+#endif
+
+ data = (csnpldata*)llistGet (csnpl->csnList, csn_str);
+ if (data == NULL)
+ {
+ /*
+ * In the scenario "4.x master -> 6.x legacy-consumer -> 6.x consumer"
+ * csn will have rid=65535. Hence 6.x consumer will get here trying
+ * to commit r->min_csn_pl because its rid matches that in the csn.
+ * However, r->min_csn_pl is always empty for a dedicated consumer.
+ * Exclude READ-ONLY replica ID here from error logging.
+ */
+ ReplicaId rid = csn_get_replicaid (csn);
+ if (rid < MAX_REPLICA_ID)
+ {
+ slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name,
+ "csnplCommit: can't find csn %s\n", csn_str);
+ }
+ PR_RWLock_Unlock (csnpl->csnLock);
+ return -1;
+ }
+ else
+ {
+ data->committed = PR_TRUE;
+ }
+
+ PR_RWLock_Unlock (csnpl->csnLock);
+
+ return 0;
+}
+
+
+
+CSN* csnplGetMinCSN (CSNPL *csnpl, PRBool *committed)
+{
+ csnpldata *data;
+ CSN *csn = NULL;
+ PR_RWLock_Rlock (csnpl->csnLock);
+ if ((data = (csnpldata*)llistGetHead (csnpl->csnList)) != NULL)
+ {
+ csn = csn_dup(data->csn);
+ if (NULL != committed)
+ {
+ *committed = data->committed;
+ }
+ }
+ PR_RWLock_Unlock (csnpl->csnLock);
+
+ return csn;
+}
+
+
+/*
+ * Roll up the list of pending CSNs, removing all of the CSNs at the
+ * head of the the list that are committed and contiguous. Returns
+ * the largest committed CSN, or NULL if no contiguous block of
+ * committed CSNs appears at the beginning of the list. The caller
+ * is responsible for freeing the CSN returned.
+ */
+CSN *
+csnplRollUp(CSNPL *csnpl, CSN **first_commited)
+{
+ CSN *largest_committed_csn = NULL;
+ csnpldata *data;
+ PRBool freeit = PR_TRUE;
+
+ PR_RWLock_Wlock (csnpl->csnLock);
+ if (first_commited) {
+ /* Avoid non-initialization issues due to careless callers */
+ *first_commited = NULL;
+ }
+ data = (csnpldata *)llistGetHead(csnpl->csnList);
+ while (NULL != data && data->committed)
+ {
+ if (NULL != largest_committed_csn && freeit)
+ {
+ csn_free(&largest_committed_csn);
+ }
+ freeit = PR_TRUE;
+ largest_committed_csn = data->csn; /* Save it */
+ if (first_commited && (*first_commited == NULL)) {
+ *first_commited = data->csn;
+ freeit = PR_FALSE;
+ }
+ data = (csnpldata*)llistRemoveHead (csnpl->csnList);
+ slapi_ch_free((void **)&data);
+ data = (csnpldata *)llistGetHead(csnpl->csnList);
+ }
+
+#ifdef DEBUG
+ _csnplDumpContentNoLock(csnpl, "csnplRollUp");
+#endif
+
+ PR_RWLock_Unlock (csnpl->csnLock);
+ return largest_committed_csn;
+}
+
+#ifdef DEBUG
+/* Dump current content of the list - for debugging */
+void
+csnplDumpContent(CSNPL *csnpl, const char *caller)
+{
+ if (csnpl)
+ {
+ PR_RWLock_Rlock (csnpl->csnLock);
+ _csnplDumpContentNoLock (csnpl, caller);
+ PR_RWLock_Unlock (csnpl->csnLock);
+ }
+}
+
+/* helper function */
+static void _csnplDumpContentNoLock(CSNPL *csnpl, const char *caller)
+{
+ csnpldata *data;
+ void *iterator;
+ char csn_str[CSN_STRSIZE];
+
+ data = (csnpldata *)llistGetFirst(csnpl->csnList, &iterator);
+ if (data) {
+ slapi_log_error(SLAPI_LOG_REPL, repl_plugin_name, "%s: CSN Pending list content:\n",
+ caller ? caller : "");
+ }
+ while (data)
+ {
+ slapi_log_error(SLAPI_LOG_REPL, repl_plugin_name, "%s, %s\n",
+ csn_as_string(data->csn, PR_FALSE, csn_str),
+ data->committed ? "committed" : "not committed");
+ data = (csnpldata *)llistGetNext (csnpl->csnList, &iterator);
+ }
+}
+#endif
+
diff --git a/ldap/servers/plugins/replication/csnpl.h b/ldap/servers/plugins/replication/csnpl.h
new file mode 100644
index 00000000..ae1b4c85
--- /dev/null
+++ b/ldap/servers/plugins/replication/csnpl.h
@@ -0,0 +1,23 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+/* csnpl.h - interface for csn pending list */
+
+#ifndef CSNPL_H
+#define CSNPL_H
+
+#include "slapi-private.h"
+
+typedef struct csnpl CSNPL;
+
+CSNPL* csnplNew ();
+void csnplFree (CSNPL **csnpl);
+int csnplInsert (CSNPL *csnpl, const CSN *csn);
+int csnplRemove (CSNPL *csnpl, const CSN *csn);
+CSN* csnplGetMinCSN (CSNPL *csnpl, PRBool *committed);
+int csnplCommit (CSNPL *csnpl, const CSN *csn);
+CSN *csnplRollUp(CSNPL *csnpl, CSN ** first);
+void csnplDumpContent(CSNPL *csnpl, const char *caller);
+#endif
diff --git a/ldap/servers/plugins/replication/dllmain.c b/ldap/servers/plugins/replication/dllmain.c
new file mode 100644
index 00000000..3f17b14c
--- /dev/null
+++ b/ldap/servers/plugins/replication/dllmain.c
@@ -0,0 +1,91 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+
+ /*
+ * Microsoft Windows specifics for LIBREPLICATION DLL
+ */
+#include "ldap.h"
+
+
+#ifdef _WIN32
+/* Lifted from Q125688
+ * How to Port a 16-bit DLL to a Win32 DLL
+ * on the MSVC 4.0 CD
+ */
+BOOL WINAPI DllMain (HANDLE hModule, DWORD fdwReason, LPVOID lpReserved)
+{
+
+ switch (fdwReason)
+ {
+ case DLL_PROCESS_ATTACH:
+ /* Code from LibMain inserted here. Return TRUE to keep the
+ DLL loaded or return FALSE to fail loading the DLL.
+
+ You may have to modify the code in your original LibMain to
+ account for the fact that it may be called more than once.
+ You will get one DLL_PROCESS_ATTACH for each process that
+ loads the DLL. This is different from LibMain which gets
+ called only once when the DLL is loaded. The only time this
+ is critical is when you are using shared data sections.
+ If you are using shared data sections for statically
+ allocated data, you will need to be careful to initialize it
+ only once. Check your code carefully.
+
+ Certain one-time initializations may now need to be done for
+ each process that attaches. You may also not need code from
+ your original LibMain because the operating system may now
+ be doing it for you.
+ */
+ /*
+ * 16 bit code calls UnlockData()
+ * which is mapped to UnlockSegment in windows.h
+ * in 32 bit world UnlockData is not defined anywhere
+ * UnlockSegment is mapped to GlobalUnfix in winbase.h
+ * and the docs for both UnlockSegment and GlobalUnfix say
+ * ".. function is oboslete. Segments have no meaning
+ * in the 32-bit environment". So we do nothing here.
+ */
+
+ break;
+
+ case DLL_THREAD_ATTACH:
+ /* Called each time a thread is created in a process that has
+ already loaded (attached to) this DLL. Does not get called
+ for each thread that exists in the process before it loaded
+ the DLL.
+
+ Do thread-specific initialization here.
+ */
+ break;
+
+ case DLL_THREAD_DETACH:
+ /* Same as above, but called when a thread in the process
+ exits.
+
+ Do thread-specific cleanup here.
+ */
+ break;
+
+ case DLL_PROCESS_DETACH:
+ /* Code from _WEP inserted here. This code may (like the
+ LibMain) not be necessary. Check to make certain that the
+ operating system is not doing it for you.
+ */
+
+ break;
+ }
+ /* The return value is only used for DLL_PROCESS_ATTACH; all other
+ conditions are ignored. */
+ return TRUE; /* successful DLL_PROCESS_ATTACH */
+}
+#else
+int CALLBACK
+LibMain( HINSTANCE hinst, WORD wDataSeg, WORD cbHeapSize, LPSTR lpszCmdLine )
+{
+ /*UnlockData( 0 );*/
+ return( 1 );
+}
+#endif
diff --git a/ldap/servers/plugins/replication/legacy_consumer.c b/ldap/servers/plugins/replication/legacy_consumer.c
new file mode 100644
index 00000000..8bf45ee1
--- /dev/null
+++ b/ldap/servers/plugins/replication/legacy_consumer.c
@@ -0,0 +1,707 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+
+/*
+ * repl_legacy_consumer.c - support for legacy replication (consumer-side)
+ *
+ * Support for legacy replication involves correctly dealing with
+ * the addition and removal of attribute types "copiedFrom" and
+ * "copyingFrom". The behavior is:
+ * 1) If a copiedFrom appears in an entry, and that entry is the root
+ * of a replicated area, then put the backend into "refer on update"
+ * mode and install a referral corresponding to the URL contained
+ * in the copiedFrom attribute. This referral overrides the mode
+ * of the replica, e.g. if it was previously an updateable replica,
+ * it now becomes read-only except for the updatedn.
+ * 2) If a copiedFrom disappears from an entry, or the entry containing
+ * the copiedFrom is removed, restore the backend to the state
+ * determined by the DS 5.0 replica configuration.
+ * 3) If a "copyingFrom" referral appears in an entry, and that entry
+ * is the root of a replicated area, then put the backend into
+ * "refer all operations" mode and install a referral corresponding
+ * to the URL contained in the copyingFrom attribute. This referral
+ * overrides the mode of the replica, e.g if it was previously an
+ * updateable replica, it now becomes read-only and refers all
+ * operations except for the updatedn.
+ * 4) If a copyingFrom disappears from an entry, or the entry containing
+ * the copyingFrom is removed, restore the backend to the state
+ * determined by the DS 5.0 replica configuration.
+ */
+
+
+#include "repl5.h"
+#include "repl.h"
+
+/* Forward Declarations */
+static int legacy_consumer_config_add (Slapi_PBlock *pb, Slapi_Entry* e, Slapi_Entry* entryAfter, int *returncode, char *returntext, void *arg);
+static int legacy_consumer_config_modify (Slapi_PBlock *pb, Slapi_Entry* e, Slapi_Entry* entryAfter, int *returncode, char *returntext, void *arg);
+static int legacy_consumer_config_delete (Slapi_PBlock *pb, Slapi_Entry* e, Slapi_Entry* entryAfter, int *returncode, char *returntext, void *arg);
+
+static int legacy_consumer_extract_config(Slapi_Entry* entry, char *returntext);
+static int legacy_consumer_read_config ();
+static void legacy_consumer_encode_pw (Slapi_Entry *e);
+static void set_legacy_purl (Slapi_PBlock *pb, const char *purl);
+static int get_legacy_referral (Slapi_Entry *e, char **referral, char **state);
+
+#define LEGACY_CONSUMER_CONFIG_DN "cn=legacy consumer," REPL_CONFIG_TOP
+#define LEGACY_CONSUMER_FILTER "(objectclass=*)"
+
+/* Configuration parameters local to this module */
+static Slapi_DN *legacy_consumer_replicationdn = NULL;
+static char *legacy_consumer_replicationpw = NULL;
+/* Lock which protects the above config parameters */
+PRRWLock *legacy_consumer_config_lock = NULL;
+
+static PRBool
+target_is_a_replica_root(Slapi_PBlock *pb, const Slapi_DN **root)
+{
+ char *dn;
+ Slapi_DN *sdn;
+ PRBool return_value;
+ Object *repl_obj;
+
+ slapi_pblock_get(pb, SLAPI_TARGET_DN, &dn);
+ sdn = slapi_sdn_new_dn_byref(dn);
+ repl_obj = replica_get_replica_from_dn(sdn);
+ if (NULL != repl_obj)
+ {
+ Replica *r = object_get_data(repl_obj);
+ *root = replica_get_root(r);
+ return_value = PR_TRUE;
+ object_release(repl_obj);
+ }
+ else
+ {
+ *root = NULL;
+ return_value = PR_FALSE;
+ }
+ slapi_sdn_free(&sdn);
+ return return_value;
+}
+
+
+
+static int
+parse_cfstring(const char *cfstring, char **referral, char **generation, char **lastreplayed)
+{
+ int return_value = -1;
+ char *ref, *gen, *lastplayed;
+
+ if (cfstring != NULL)
+ {
+ char *tmp;
+ char *cfcopy = slapi_ch_strdup(cfstring);
+ ref = cfcopy;
+ tmp = strchr(cfcopy, ' ');
+ if (NULL != tmp)
+ {
+ *tmp++ = '\0';
+ while ('\0' != *tmp && ' ' == *tmp) tmp++;
+ gen = tmp;
+ tmp = strchr(gen, ' ');
+ if (NULL != tmp)
+ {
+ *tmp++ = '\0';
+ while ('\0' != *tmp && ' ' == *tmp) tmp++;
+ lastplayed = tmp;
+ return_value = 0;
+ }
+ }
+
+ if (return_value == 0)
+ {
+ if (referral)
+ *referral = slapi_ch_strdup(ref);
+ if (generation)
+ *generation = slapi_ch_strdup(gen);
+ if (lastreplayed)
+ *lastreplayed = slapi_ch_strdup(lastplayed);
+ }
+ slapi_ch_free((void **)&cfcopy);
+ }
+ return return_value;
+}
+
+
+
+/*
+ * This is called from the consumer post-op plugin point.
+ * It's called if:
+ * 1) The operation is an add or modify operation, and a
+ * copiedfrom/copyingfrom was found in the entry/mods, or
+ * 2) the operation is a delete operation, or
+ * 3) the operation is a moddn operation.
+ */
+
+void
+process_legacy_cf(Slapi_PBlock *pb)
+{
+ consumer_operation_extension *opext;
+ Slapi_Operation *op;
+ char *referral_array[2] = {0};
+ char *referral;
+ char *state;
+ int rc;
+ const Slapi_DN *replica_root_sdn = NULL;
+ Slapi_Entry *e;
+
+ slapi_pblock_get(pb, SLAPI_OPERATION, &op);
+ opext = (consumer_operation_extension*) repl_con_get_ext (REPL_CON_EXT_OP, op);
+
+ if (opext->has_cf)
+ {
+ PR_ASSERT (operation_get_type (op) == SLAPI_OPERATION_ADD ||
+ operation_get_type (op) == SLAPI_OPERATION_MODIFY);
+
+ if ((PR_FALSE == target_is_a_replica_root(pb, &replica_root_sdn)) ||
+ (NULL == replica_root_sdn)){
+ return;
+ }
+
+ slapi_pblock_get (pb, SLAPI_ENTRY_POST_OP, &e);
+ PR_ASSERT (e);
+
+ if (NULL == e)
+ return;
+
+ rc = get_legacy_referral (e, &referral, &state);
+ if (rc == 0)
+ {
+ referral_array[0] = referral;
+ referral_array[1] = NULL;
+ repl_set_mtn_state_and_referrals(replica_root_sdn, state, NULL, NULL,
+ referral_array);
+ /* set partial url in the replica_object */
+ set_legacy_purl (pb, referral);
+
+ slapi_ch_free((void **)&referral);
+ }
+
+ }
+}
+
+void legacy_consumer_be_state_change (void *handle, char *be_name,
+ int old_be_state, int new_be_state)
+{
+ Object *r_obj;
+ Replica *r;
+
+ /* we only interested when a backend is coming online */
+ if (new_be_state == SLAPI_BE_STATE_ON)
+ {
+ r_obj = replica_get_for_backend (be_name);
+ if (r_obj)
+ {
+ r = (Replica*)object_get_data (r_obj);
+ PR_ASSERT (r);
+
+ if (replica_is_legacy_consumer (r))
+ legacy_consumer_init_referrals (r);
+
+ object_release (r_obj);
+ }
+ }
+}
+
+
+static int
+dont_allow_that(Slapi_PBlock *pb, Slapi_Entry* entryBefore, Slapi_Entry* e, int *returncode, char *returntext, void *arg)
+{
+ *returncode = LDAP_UNWILLING_TO_PERFORM;
+ return SLAPI_DSE_CALLBACK_ERROR;
+}
+
+int
+legacy_consumer_config_init()
+{
+ /* The FE DSE *must* be initialised before we get here */
+ int rc;
+
+ if ((legacy_consumer_config_lock = PR_NewRWLock(PR_RWLOCK_RANK_NONE, "legacy_consumer_config_lock")) == NULL) {
+ slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name,
+ "Failed to create legacy_consumer config read-write lock\n");
+ exit(1);
+ }
+
+ rc = legacy_consumer_read_config ();
+ if (rc != LDAP_SUCCESS)
+ {
+ slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name,
+ "Failed to initialize legacy replication configuration\n");
+ return 1;
+ }
+
+ slapi_config_register_callback(SLAPI_OPERATION_ADD,DSE_FLAG_PREOP,LEGACY_CONSUMER_CONFIG_DN,LDAP_SCOPE_SUBTREE,LEGACY_CONSUMER_FILTER,legacy_consumer_config_add,NULL);
+ slapi_config_register_callback(SLAPI_OPERATION_MODIFY,DSE_FLAG_PREOP,LEGACY_CONSUMER_CONFIG_DN,LDAP_SCOPE_SUBTREE,LEGACY_CONSUMER_FILTER,legacy_consumer_config_modify,NULL);
+ slapi_config_register_callback(SLAPI_OPERATION_MODRDN,DSE_FLAG_PREOP,LEGACY_CONSUMER_CONFIG_DN,LDAP_SCOPE_SUBTREE,LEGACY_CONSUMER_FILTER,dont_allow_that,NULL);
+ slapi_config_register_callback(SLAPI_OPERATION_DELETE,DSE_FLAG_PREOP,LEGACY_CONSUMER_CONFIG_DN,LDAP_SCOPE_SUBTREE,LEGACY_CONSUMER_FILTER,legacy_consumer_config_delete,NULL);
+
+ return 0;
+}
+
+static int
+legacy_consumer_config_add (Slapi_PBlock *pb, Slapi_Entry* e, Slapi_Entry* entryAfter, int *returncode, char *returntext, void *arg)
+{
+ int rc;
+
+ rc = legacy_consumer_extract_config(e, returntext);
+ if (rc != LDAP_SUCCESS)
+ {
+ *returncode = rc;
+ slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name, "Failed to configure legacy replication\n");
+ return SLAPI_DSE_CALLBACK_ERROR;
+ }
+ /* make sure that the password is encoded */
+ legacy_consumer_encode_pw(e);
+
+ slapi_log_error(SLAPI_LOG_REPL, repl_plugin_name, "legacy_consumer_config_add: "
+ "successfully configured legacy consumer credentials\n");
+
+ return SLAPI_DSE_CALLBACK_OK;
+}
+
+#define config_copy_strval( s ) s ? slapi_ch_strdup (s) : NULL;
+
+static int
+legacy_consumer_config_modify (Slapi_PBlock *pb, Slapi_Entry* entryBefore, Slapi_Entry* e, int *returncode, char *returntext, void *arg)
+{
+ int rc= 0;
+ LDAPMod **mods;
+ int not_allowed = 0;
+ int i;
+
+ if (returntext)
+ {
+ returntext[0] = '\0';
+ }
+ *returncode = LDAP_SUCCESS;
+
+
+ slapi_pblock_get( pb, SLAPI_MODIFY_MODS, &mods );
+ PR_RWLock_Wlock (legacy_consumer_config_lock);
+
+ for (i = 0; (mods[i] && (!not_allowed)); i++)
+ {
+ if (mods[i]->mod_op & LDAP_MOD_DELETE)
+ {
+ /* We don't support deleting an attribute from cn=config */
+ }
+ else
+ {
+ int j;
+ for (j = 0; ((mods[i]->mod_values[j]) && (LDAP_SUCCESS == rc)); j++)
+ {
+ char *config_attr, *config_attr_value;
+ int mod_type;
+ config_attr = (char *) mods[i]->mod_type;
+ config_attr_value = (char *) mods[i]->mod_bvalues[j]->bv_val;
+ /* replace existing value */
+ mod_type = mods[i]->mod_op & ~LDAP_MOD_BVALUES;
+ if ( strcasecmp (config_attr, CONFIG_LEGACY_REPLICATIONDN_ATTRIBUTE ) == 0 )
+ {
+ if (legacy_consumer_replicationdn)
+ slapi_sdn_free (&legacy_consumer_replicationdn);
+
+ if (mod_type == LDAP_MOD_REPLACE)
+ {
+ if (config_attr_value)
+ legacy_consumer_replicationdn = slapi_sdn_new_dn_byval (config_attr_value);
+ }
+ else if (mod_type == LDAP_MOD_DELETE)
+ {
+ legacy_consumer_replicationdn = NULL;
+ }
+ else if (mod_type == LDAP_MOD_ADD)
+ {
+ if (legacy_consumer_replicationdn != NULL)
+ {
+ not_allowed = 1;
+ slapi_log_error( SLAPI_LOG_FATAL, repl_plugin_name,
+ "Multiple replicationdns not permitted." );
+ }
+ else
+ {
+ if (config_attr_value)
+ legacy_consumer_replicationdn = slapi_sdn_new_dn_byval (config_attr_value);
+ }
+ }
+ }
+ else if ( strcasecmp ( config_attr, CONFIG_LEGACY_REPLICATIONPW_ATTRIBUTE ) == 0 )
+ {
+ if (mod_type == LDAP_MOD_REPLACE)
+ {
+ legacy_consumer_replicationpw = config_copy_strval(config_attr_value);
+ }
+ else if (mod_type == LDAP_MOD_DELETE)
+ {
+ legacy_consumer_replicationpw = NULL;
+ }
+ else if (mod_type == LDAP_MOD_ADD)
+ {
+ if (legacy_consumer_replicationpw != NULL)
+ {
+ not_allowed = 1;
+ slapi_log_error( SLAPI_LOG_FATAL, repl_plugin_name,
+ "Multiple replicationpws not permitted." );
+ }
+ else
+ {
+ legacy_consumer_replicationpw = config_copy_strval(config_attr_value);
+ }
+ }
+ }
+ }
+ }
+ }
+
+ PR_RWLock_Unlock (legacy_consumer_config_lock);
+
+
+ if (not_allowed)
+ {
+ slapi_log_error( SLAPI_LOG_FATAL, repl_plugin_name,
+ "Failed to modify legacy replication configuration\n" );
+ *returncode= LDAP_CONSTRAINT_VIOLATION;
+ return SLAPI_DSE_CALLBACK_ERROR;
+ }
+
+ /* make sure that the password is encoded */
+ legacy_consumer_encode_pw (e);
+
+ return SLAPI_DSE_CALLBACK_OK;
+}
+
+static int
+legacy_consumer_config_delete (Slapi_PBlock *pb, Slapi_Entry* e, Slapi_Entry* entryAfter, int *returncode, char *returntext, void *arg)
+{
+
+ PR_RWLock_Wlock (legacy_consumer_config_lock);
+ if (legacy_consumer_replicationdn)
+ slapi_sdn_free (&legacy_consumer_replicationdn);
+ if (legacy_consumer_replicationpw)
+ slapi_ch_free ((void**)&legacy_consumer_replicationpw);
+
+ legacy_consumer_replicationdn = NULL;
+ legacy_consumer_replicationpw = NULL;
+ PR_RWLock_Unlock (legacy_consumer_config_lock);
+
+ *returncode = LDAP_SUCCESS;
+ return SLAPI_DSE_CALLBACK_OK;
+}
+
+/*
+ * Given the changelog configuration entry, extract the configuration directives.
+ */
+static int
+legacy_consumer_extract_config(Slapi_Entry* entry, char *returntext)
+{
+ int rc = LDAP_SUCCESS; /* OK */
+ char *arg;
+
+ PR_RWLock_Wlock (legacy_consumer_config_lock);
+
+ arg= slapi_entry_attr_get_charptr(entry,CONFIG_LEGACY_REPLICATIONDN_ATTRIBUTE);
+ if (arg)
+ legacy_consumer_replicationdn = slapi_sdn_new_dn_passin (arg);
+
+ arg= slapi_entry_attr_get_charptr(entry,CONFIG_LEGACY_REPLICATIONPW_ATTRIBUTE);
+ legacy_consumer_replicationpw = arg;
+
+ PR_RWLock_Unlock (legacy_consumer_config_lock);
+
+ return rc;
+}
+
+
+
+
+static int
+legacy_consumer_read_config ()
+{
+ int rc = LDAP_SUCCESS;
+ int scope= LDAP_SCOPE_BASE;
+ Slapi_PBlock *pb;
+
+ pb = slapi_pblock_new ();
+ slapi_search_internal_set_pb (pb, LEGACY_CONSUMER_CONFIG_DN, scope,
+ "(objectclass=*)", NULL /*attrs*/, 0 /* attrs only */,
+ NULL /* controls */, NULL /* uniqueid */,
+ repl_get_plugin_identity(PLUGIN_LEGACY_REPLICATION), 0 /* actions */);
+ slapi_search_internal_pb (pb);
+ slapi_pblock_get( pb, SLAPI_PLUGIN_INTOP_RESULT, &rc );
+ if ( LDAP_SUCCESS == rc )
+ {
+ Slapi_Entry **entries = NULL;
+ slapi_pblock_get( pb, SLAPI_PLUGIN_INTOP_SEARCH_ENTRIES, &entries );
+ if ( NULL != entries && NULL != entries[0])
+ {
+ /* Extract the config info from the changelog entry */
+ rc = legacy_consumer_extract_config(entries[0], NULL);
+ }
+ }
+ else
+ {
+ rc = LDAP_SUCCESS;
+ }
+ slapi_free_search_results_internal(pb);
+ slapi_pblock_destroy(pb);
+
+ return rc;
+}
+
+
+int
+legacy_consumer_is_replicationdn(char *dn)
+{
+ int return_value = 0; /* Assume not */
+
+ if (NULL != dn && '\0' != dn[0])
+ {
+ if (NULL != legacy_consumer_replicationdn)
+ {
+ Slapi_DN *sdn = slapi_sdn_new_dn_byref (dn);
+
+ if (slapi_sdn_compare (legacy_consumer_replicationdn, sdn) == 0) {
+ return_value = 1;
+ }
+
+ slapi_sdn_free (&sdn);
+ }
+ }
+ return return_value;
+}
+
+
+int
+legacy_consumer_is_replicationpw(struct berval *pwval)
+{
+ int return_value = 0; /* Assume not */
+
+ if (NULL != pwval && NULL != pwval->bv_val)
+ {
+ if (NULL != legacy_consumer_replicationpw &&
+ '\0' != legacy_consumer_replicationpw[0]) {
+ struct berval *pwvals[2];
+ struct berval config_pw;
+
+ config_pw.bv_val = legacy_consumer_replicationpw;
+ config_pw.bv_len = strlen(legacy_consumer_replicationpw);
+ pwvals[0] = &config_pw;
+ pwvals[1] = NULL;
+
+ return_value = slapi_pw_find(pwvals, pwval) == 0;
+ }
+ }
+ return return_value;
+}
+
+static void
+legacy_consumer_free_config ()
+{
+ if (NULL != legacy_consumer_replicationdn) {
+ slapi_sdn_free(&legacy_consumer_replicationdn);
+ }
+ if (NULL != legacy_consumer_replicationpw) {
+ slapi_ch_free((void **) &legacy_consumer_replicationpw);
+ }
+}
+
+
+
+static void
+legacy_consumer_encode_pw (Slapi_Entry *e)
+{
+ char *updatepw = slapi_entry_attr_get_charptr(e,
+ CONFIG_LEGACY_REPLICATIONPW_ATTRIBUTE);
+ int is_encoded;
+ char *encoded_value = NULL;
+
+ if (updatepw != NULL)
+ {
+ is_encoded = slapi_is_encoded (updatepw);
+
+ if (!is_encoded)
+ {
+ encoded_value = slapi_encode (updatepw, "SHA");
+ }
+
+ if (encoded_value)
+ {
+ slapi_entry_attr_set_charptr(e,
+ CONFIG_LEGACY_REPLICATIONPW_ATTRIBUTE, encoded_value);
+ }
+ }
+}
+
+static void
+set_legacy_purl (Slapi_PBlock *pb, const char *purl)
+{
+ Object *r_obj;
+ Replica *r;
+
+ r_obj = replica_get_replica_for_op (pb);
+ PR_ASSERT (r_obj);
+ r = (Replica*)object_get_data (r_obj);
+ PR_ASSERT (r && replica_is_legacy_consumer(r));
+
+ replica_set_legacy_purl (r, purl);
+
+ object_release (r_obj);
+}
+
+/* this function get referrals from an entry.
+ Returns 0 if successful
+ 1 if no referrals are present
+ -1 in case of error
+ */
+static int
+get_legacy_referral (Slapi_Entry *e, char **referral, char **state)
+{
+ char* pat = "ldap://%s";
+ const char *val = NULL;
+ char *hostport;
+ int rc = 1;
+ Slapi_Attr *attr;
+ const Slapi_Value *sval;
+
+ PR_ASSERT (e && referral && state);
+
+ /* Find any copiedFrom/copyingFrom attributes -
+ copyingFrom has priority */
+ if (slapi_entry_attr_find(e, type_copyingFrom, &attr) == 0)
+ {
+ slapi_attr_first_value(attr, (Slapi_Value **)&sval);
+ val = slapi_value_get_string(sval);
+ *state = STATE_REFERRAL;
+ }
+ else if (slapi_entry_attr_find(e, type_copiedFrom, &attr) == 0)
+ {
+ slapi_attr_first_value(attr, (Slapi_Value **)&sval);
+ val = slapi_value_get_string(sval);
+ *state = STATE_UPDATE_REFERRAL;
+ }
+
+ if (val)
+ {
+ rc = parse_cfstring(val, &hostport, NULL, NULL);
+ if (rc != 0)
+ {
+ const char *target_dn = slapi_entry_get_dn_const(e);
+ slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name, "Warning: a copiedFrom "
+ "or copyingFrom attribute was added to or removed from an "
+ "entry that is not the root of a replicated area. It is possible "
+ "that a legacy replication supplier is incorrectly configured "
+ "to supply updates to the subtree rooted at %s\n",
+ target_dn == NULL ? "null" : target_dn);
+ }
+ else
+ {
+ *referral = slapi_ch_malloc (strlen (pat) + strlen (hostport));
+
+ sprintf (*referral, pat, hostport);
+
+ slapi_ch_free ((void**)&hostport);
+ }
+ }
+ else
+ {
+ rc = 1; /* no copiedFrom or copyingFrom int the entry */
+ }
+
+ return rc;
+}
+
+/* this function is called during server startup or when replica's data
+ is reloaded. It sets up referrals in the mapping tree based on the
+ copiedFrom and copyingFrom attributes. It also sets up partial url in
+ the replica object used to update RUV.
+ Returns 0 if successful and -1 otherwise
+
+ */
+int
+legacy_consumer_init_referrals (Replica *r)
+{
+ Slapi_PBlock *pb;
+ const Slapi_DN *root_sdn;
+ const char *root_dn;
+ char *attrs[] = {"copiedFrom", "copyingFrom"};
+ int rc;
+ Slapi_Entry **entries = NULL;
+ char *referral = NULL;
+ char *referral_array[2];
+ char *state = NULL;
+
+ PR_ASSERT (r);
+
+ pb = slapi_pblock_new ();
+ PR_ASSERT (pb);
+
+ root_sdn = replica_get_root(r);
+ PR_ASSERT (root_sdn);
+
+ root_dn = slapi_sdn_get_ndn(root_sdn);
+ PR_ASSERT (root_dn);
+
+ slapi_search_internal_set_pb (pb, root_dn, LDAP_SCOPE_BASE, "objectclass=*",attrs,
+ 0 /* attrsonly */, NULL /* controls */,
+ NULL /* uniqueid */,
+ repl_get_plugin_identity (PLUGIN_LEGACY_REPLICATION),
+ 0 /* flags */);
+
+ slapi_search_internal_pb (pb);
+
+ slapi_pblock_get (pb, SLAPI_PLUGIN_INTOP_RESULT, &rc);
+ if (rc != LDAP_SUCCESS)
+ {
+ if (rc == LDAP_REFERRAL)
+ {
+ /* We are in referral mode, probably because ORC failed */
+ slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name, "legacy_consumer_init_referrals "
+ "data for replica %s is in referral mode due to failed "
+ "initialization. Replica need to be reinitialized\n",
+ root_dn);
+ rc = 0;
+ }
+ else
+ {
+ slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name, "legacy_consumer_init_referrals "
+ "failed to obtain root entry for replica %s; LDAP error - %d\n",
+ root_dn, rc);
+ rc = -1;
+ }
+
+ goto done;
+ }
+
+ slapi_pblock_get (pb, SLAPI_PLUGIN_INTOP_SEARCH_ENTRIES, &entries);
+
+ PR_ASSERT (entries && entries[0]);
+
+ rc = get_legacy_referral (entries[0], &referral, &state);
+ if (rc == 0)
+ {
+ referral_array[0] = referral;
+ referral_array[1] = NULL;
+ repl_set_mtn_state_and_referrals(root_sdn, state, NULL, NULL, referral_array);
+
+ /* set purtial url in the replica_object */
+ replica_set_legacy_purl (r, referral);
+
+ slapi_ch_free((void **)&referral);
+ }
+ else if (rc == 1) /* no referrals - treat as success */
+ {
+ rc = 0;
+ }
+
+ slapi_free_search_results_internal (pb);
+
+done:
+
+ slapi_pblock_destroy (pb);
+ return rc;
+}
+
diff --git a/ldap/servers/plugins/replication/llist.c b/ldap/servers/plugins/replication/llist.c
new file mode 100644
index 00000000..175ea48f
--- /dev/null
+++ b/ldap/servers/plugins/replication/llist.c
@@ -0,0 +1,336 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+/* llist.c - single link list implementation */
+
+#include <string.h>
+#include "slapi-plugin.h"
+#include "slapi-private.h"
+#include "llist.h"
+#include "repl_shared.h"
+
+/* data structures */
+
+/* link list node */
+typedef struct lnode
+{
+ char *key;
+ void *data;
+ struct lnode *next;
+} LNode;
+
+/* This structure defines a one-way linked list with head and tail pointers.
+ The list contains a "dummy" head node which makes sure that every node
+ has a previous node. This allows to remove a node during iteration without
+ breaking the list */
+struct llist
+{
+ LNode *head;
+ LNode *tail;
+};
+
+/* forward declarations */
+static LNode* _llistNewNode (const char *key, void *data);
+static void _llistDestroyNode (LNode **node, FNFree fnFree);
+
+LList* llistNew ()
+{
+ LList *list = (LList*) slapi_ch_calloc (1, sizeof (LList));
+
+ /* allocate a special head node - it contains no data but just
+ fulfills the requirement that every node has a previous one.
+ This is used during iteration with removal */
+ if (list)
+ {
+ list->head = (LNode*)slapi_ch_calloc (1, sizeof (LNode));
+ if (list->head == NULL)
+ {
+ slapi_ch_free ((void**)&list);
+ }
+ }
+
+ return list;
+}
+
+void llistDestroy (LList **list, FNFree fnFree)
+{
+ LNode *node = NULL, *prev_node;
+
+ if (list == NULL || *list == NULL)
+ return;
+
+ if ((*list)->head)
+ node = (*list)->head->next;
+
+ while (node)
+ {
+ prev_node = node;
+ node = node->next;
+ _llistDestroyNode (&prev_node, fnFree);
+ }
+
+ slapi_ch_free ((void**)&((*list)->head));
+ slapi_ch_free ((void**)list);
+}
+
+void* llistGetFirst(LList *list, void **iterator)
+{
+ if (list == NULL || iterator == NULL || list->head == NULL || list->head->next == NULL)
+ {
+ /* empty list or error */
+ return NULL;
+ }
+
+ /* Iterator points to the previous element (so that we can remove current element
+ and still keep the list in tact. In case of the first element, iterator points
+ to the dummy head element */
+ (*iterator) = list->head;
+ return list->head->next->data;
+}
+
+void* llistGetNext (LList *list, void **iterator)
+{
+ LNode *node;
+
+ if (list == NULL || list->head == NULL || iterator == NULL || *iterator == NULL)
+ {
+ /* end of the list or error */
+ return NULL;
+ }
+
+ /* Iterator points to the previous element (so that we can
+ remove current element and still keep list in tact. */
+ node = *(LNode **)iterator;
+ node = node->next;
+
+ (*iterator) = node;
+
+ if (node && node->next)
+ return node->next->data;
+ else
+ return NULL;
+}
+
+void* llistRemoveCurrentAndGetNext (LList *list, void **iterator)
+{
+ LNode *prevNode, *node;
+
+ /* end of the list is reached or error occured */
+ if (list == NULL || iterator == NULL || *iterator == NULL)
+ return NULL;
+
+ /* Iterator points to the previous element (so that we can
+ remove current element and still keep list in tact. */
+ prevNode = *(LNode **)iterator;
+ node = prevNode->next;
+ if (node)
+ {
+ prevNode->next = node->next;
+ _llistDestroyNode (&node, NULL);
+ node = prevNode->next;
+ if (node)
+ return node->data;
+ else
+ return NULL;
+ }
+ else
+ return NULL;
+}
+
+void* llistGetHead (LList *list)
+{
+ if (list == NULL || list->head == NULL || list->head->next == NULL)
+ {
+ /* empty list or error */
+ return NULL;
+ }
+
+ return list->head->next->data;
+}
+
+void* llistGetTail (LList *list)
+{
+ if (list == NULL || list->tail == NULL)
+ {
+ /* empty list or error */
+ return NULL;
+ }
+
+ return list->tail->data;
+}
+
+void* llistGet (LList *list, const char* key)
+{
+ LNode *node;
+
+ /* empty list or invalid input */
+ if (list == NULL || list->head == NULL || list->head->next == NULL || key == NULL)
+ return NULL;
+
+ node = list->head->next;
+ while (node)
+ {
+ if (node->key && strcmp (key, node->key) == 0)
+ {
+ return node->data;
+ }
+
+ node = node->next;
+ }
+
+ /* node with specified key is not found */
+ return NULL;
+}
+
+int llistInsertHead (LList *list, const char *key, void *data)
+{
+ LNode *node;
+ if (list == NULL || list->head == NULL || data == NULL)
+ {
+ slapi_log_error(SLAPI_LOG_PLUGIN, repl_plugin_name, "llistInsertHead: invalid argument\n");
+ return -1;
+ }
+
+ node = _llistNewNode (key, data);
+ if (node == NULL)
+ {
+ slapi_log_error(SLAPI_LOG_PLUGIN, repl_plugin_name, "llistInsertHead: failed to allocate list node\n");
+ return -1;
+ }
+
+ if (list->head->next == NULL) /* empty list */
+ {
+ list->head->next = node;
+ list->tail = node;
+ }
+ else
+ {
+ node->next = list->head->next;
+ list->head->next = node;
+ }
+
+ return 0;
+}
+
+int llistInsertTail (LList *list, const char *key, void *data)
+{
+ LNode *node;
+ if (list == NULL || list->head == NULL || data == NULL)
+ {
+ slapi_log_error(SLAPI_LOG_PLUGIN, repl_plugin_name, "llistInsertHead: invalid argument\n");
+ return -1;
+ }
+
+ node = _llistNewNode (key, data);
+ if (node == NULL)
+ {
+ slapi_log_error(SLAPI_LOG_PLUGIN, repl_plugin_name, "llistInsertHead: failed to allocate list node\n");
+ return -1;
+ }
+
+ if (list->head->next == NULL) /* empty list */
+ {
+ list->head->next = node;
+ list->tail = node;
+ }
+ else
+ {
+ list->tail->next = node;
+ list->tail = node;
+ }
+
+ return 0;
+}
+
+void* llistRemoveHead (LList *list)
+{
+ LNode *node;
+ void *data;
+
+ if (list == NULL || list->head == NULL || list->head->next == NULL)
+ return NULL;
+
+ node = list->head->next;
+ data = node->data;
+
+ list->head->next = node->next;
+
+ /* last element removed */
+ if (list->head->next == NULL)
+ list->tail = NULL;
+
+ _llistDestroyNode (&node, NULL);
+
+ return data;
+}
+
+void* llistRemove (LList *list, const char *key)
+{
+ LNode *node, *prev_node;
+ void *data;
+
+ if (list == NULL || list->head == NULL || list->head->next == NULL || key == NULL)
+ return NULL;
+
+ node = list->head->next;
+ prev_node = list->head;
+ while (node)
+ {
+ if (node->key && strcmp (key, node->key) == 0)
+ {
+ prev_node->next = node->next;
+ /* last element removed */
+ if (node->next == NULL)
+ {
+ /* no more elements in the list */
+ if (list->head->next == NULL)
+ {
+ list->tail = NULL;
+ }
+ else
+ {
+ list->tail = prev_node;
+ }
+ }
+
+ data = node->data;
+ _llistDestroyNode (&node, NULL);
+ return data;
+ }
+
+ prev_node = node;
+ node = node->next;
+ }
+
+ /* node with specified key is not found */
+ return NULL;
+}
+
+static LNode* _llistNewNode (const char *key, void *data)
+{
+ LNode *node = (LNode*) slapi_ch_malloc (sizeof (LNode));
+ if (node == NULL)
+ return NULL;
+
+ if (key)
+ node->key = slapi_ch_strdup (key);
+ else
+ node->key = NULL;
+
+ node->data = data;
+ node->next = NULL;
+
+ return node;
+}
+
+static void _llistDestroyNode (LNode **node, FNFree fnFree)
+{
+ if ((*node)->data && fnFree)
+ fnFree (&(*node)->data);
+ if ((*node)->key)
+ slapi_ch_free ((void**)&((*node)->key));
+
+ slapi_ch_free ((void**)node);
+}
diff --git a/ldap/servers/plugins/replication/llist.h b/ldap/servers/plugins/replication/llist.h
new file mode 100644
index 00000000..3b196ef8
--- /dev/null
+++ b/ldap/servers/plugins/replication/llist.h
@@ -0,0 +1,26 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+/* llist.h - single link list interface */
+
+#ifndef LLIST_H
+#define LLIST_H
+typedef struct llist LList;
+
+LList* llistNew ();
+void llistDestroy (LList **list, FNFree fnFree);
+void* llistGetFirst(LList *list, void **iterator);
+void* llistGetNext (LList *list, void **iterator);
+void* llistRemoveCurrentAndGetNext (LList *list, void **iterator);
+void* llistGetHead (LList *list);
+void* llistGetTail (LList *list);
+void* llistGet (LList *list, const char* key);
+int llistInsertHead (LList *list, const char *key, void *data);
+int llistInsertTail (LList *list, const char *key, void *data);
+void* llistRemoveHead (LList *list);
+void* llistRemove (LList *list, const char *key);
+
+#endif
+
diff --git a/ldap/servers/plugins/replication/profile.c b/ldap/servers/plugins/replication/profile.c
new file mode 100644
index 00000000..0a7de374
--- /dev/null
+++ b/ldap/servers/plugins/replication/profile.c
@@ -0,0 +1,42 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+
+#include "slapi-plugin.h"
+#include "repl.h"
+
+
+/* module: provide an interface to the profile file */
+
+static FILE *profile_fd=NULL;
+
+/* JCMREPL - Could build up in an AVL tree and dump out to disk at the end... */
+
+void profile_log(char *file,int line)
+{
+ if (profile_fd==NULL)
+ slapi_log_error(,"profile_log: profile file not open.");
+ else
+ {
+ /* JCMREPL - Probably need a lock around here */
+ fprintf(profile_fd,"%s %d\n",file,line);
+ }
+}
+
+void profile_open()
+{
+ char filename[MAX_FILENAME];
+ strncpy(filename,CFG_rootpath,MAX_FILENAME);
+ strcat(filename,CFG_profilefile);
+ profile_fd= textfile_open(filename,"a");
+}
+
+void profile_close()
+{
+ if (profile_fd==NULL)
+ slapi_log_error(,"profile_close: profile file not open.");
+ else
+ textfile_close(profile_fd);
+}
diff --git a/ldap/servers/plugins/replication/repl.h b/ldap/servers/plugins/replication/repl.h
new file mode 100644
index 00000000..8e502816
--- /dev/null
+++ b/ldap/servers/plugins/replication/repl.h
@@ -0,0 +1,366 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+
+#ifndef _REPL_H_
+#define _REPL_H_
+
+#include <limits.h>
+#include <time.h>
+#include <stdio.h>
+#include <string.h>
+#ifndef _WIN32
+#include <sys/param.h>
+#endif /* _WIN32 */
+
+#include "portable.h" /* GGOODREPL - is this cheating? */
+#include "ldaplog.h"
+#include "repl_shared.h"
+#include "cl4.h"
+
+typedef struct schedule_item
+{
+ unsigned long sch_start; /* seconds after midnight */
+ unsigned long sch_length; /* sec */
+ unsigned int sch_weekdays; /* bit mask; LSB = Sunday */
+ struct schedule_item* sch_next;
+} schedule_item;
+
+/* XXXggood - copied from slap.h - bad */
+#if defined( XP_WIN32 )
+#define NO_TIME (time_t)0 /* cannot be -1, NT's localtime( -1 ) returns NULL */
+#else
+#define NO_TIME (time_t)-1 /* a value that time() does not return */
+#endif
+
+/*
+ * A status message contains a time, the textual message,
+ * and a count of the number of times the message occured.
+ */
+typedef struct _status_message {
+ time_t sm_time;
+ char *sm_message;
+ int sm_occurances;
+} status_message;
+
+/*
+ * A status_message_list is a circular array of status messages.
+ * Old messages roll off the end and are discarded.
+ */
+typedef struct _status_message_list {
+ int sml_size; /* number of slots in array */
+ int sml_tail; /* next slot to be written */
+ status_message *sml_messages; /* array of messages */
+} sm_list;
+#define NUM_REPL_MESSAGES 20 /* max # of messages to save */
+
+/* Selective attribute Inclusion states. ORDERING IS SIGNIFICANT */
+#define IMPLICITLY_INCLUDED 1
+#define IMPLICITLY_EXCLUDED 2
+#define EXPLICITLY_EXCLUDED 3
+#define EXPLICITLY_INCLUDED 4
+
+#if defined(__JCMREPL_FILTER__)
+/*
+ * Structure used to implement selective attribute filtering.
+ * sa_filter nodes are arranged in a linked list.
+ */
+typedef struct _sa_filter {
+ Slapi_Filter *sa_filter; /* Filter to apply */
+ int sa_isexclude; /* non-zero if list is exclude list */
+ char **sa_attrlist; /* array - attrs to replicate */
+ struct _sa_filter *sa_next; /* Link to next struct */
+} sa_filter;
+#endif
+
+typedef unsigned long changeNumber;
+#define a2changeNumber( a ) strtoul(( a ), (char **)NULL, 10 )
+
+#define AUTH_SIMPLE 1
+#define AUTH_KERBEROS 2
+
+typedef struct modinfo {
+ char *type;
+ char *value;
+ int len;
+} modinfo;
+
+/*
+ * Representation of one change entry from the replog file.
+ */
+typedef struct repl {
+ char *time; /* time of modification */
+ changeNumber change; /* number of this change */
+ char *dn; /* dn of entry being modified - normalized */
+ char *raw_dn; /* dn of entry - not normalized */
+ int changetype; /* type of change */
+ modinfo *mods; /* modifications to make */
+ char *newrdn; /* new rdn for modrdn */
+ int deleteoldrdn; /* flag for modrdn */
+
+} repl;
+
+#define BIND_OK 0
+#define BIND_ERR_BADLDP 1
+#define BIND_ERR_OPEN 2
+#define BIND_ERR_BAD_ATYPE 3
+#define BIND_ERR_SIMPLE_FAILED 4
+#define BIND_ERR_KERBEROS_FAILED 5
+#define BIND_ERR_SSL_INIT_FAILED 6
+#define BIND_ERR_RACE 7
+
+#define MAX_CHANGENUMBER ULONG_MAX
+
+#define REPLICATION_SUBSYSTEM "replication"
+#define REPL_LDAP_TIMEOUT 30L /* Wait 30 seconds for responses */
+
+/* Update the copiedFrom attribute every <n> updates */
+#define UPDATE_COPIEDFROM_INTERVAL 10
+#define REPL_ERROR_REPL_HALTED "REPLICATION HALTED"
+#define ATTR_NETSCAPEMDSUFFIX "netscapemdsuffix"
+
+#define CONFIG_LEGACY_REPLICATIONDN_ATTRIBUTE "nsslapd-legacy-updatedn"
+#define CONFIG_LEGACY_REPLICATIONPW_ATTRIBUTE "nsslapd-legacy-updatepw"
+
+#define LDAP_CONTROL_REPL_MODRDN_EXTRAMODS "2.16.840.1.113730.3.4.999"
+
+/* Operation types */
+#define OP_MODIFY 1
+#define OP_ADD 2
+#define OP_DELETE 3
+#define OP_MODDN 4
+#define OP_SEARCH 5
+#define OP_COMPARE 6
+
+/* 4.0-style housekeeping interval */
+#define REPLICATION_HOUSEKEEPING_INTERVAL (30 * 1000) /* 30 seconds */
+
+/* Top of tree for replication configuration information */
+#define REPL_CONFIG_TOP "cn=replication,cn=config"
+
+/* Functions */
+
+/* repl_rootdse.c */
+int repl_rootdse_init();
+
+/* In repl.c */
+Slapi_Entry *get_changerecord(const chglog4Info *cl4, changeNumber cnum, int *err);
+changeNumber replog_get_firstchangenum(const chglog4Info *cl4, int *err);
+changeNumber replog_get_lastchangenum(const chglog4Info *cl4, int *err);
+void changelog_housekeeping(time_t cur_time );
+
+/* In repl_config.c */
+int repl_config_init ();
+
+/* Legacy Plugin Functions */
+
+int legacy_preop_bind( Slapi_PBlock *pb );
+int legacy_bepreop_bind( Slapi_PBlock *pb );
+int legacy_postop_bind( Slapi_PBlock *pb );
+int legacy_preop_add( Slapi_PBlock *pb );
+int legacy_bepreop_add( Slapi_PBlock *pb );
+int legacy_postop_add( Slapi_PBlock *pb );
+int legacy_preop_modify( Slapi_PBlock *pb );
+int legacy_bepreop_modify( Slapi_PBlock *pb );
+int legacy_postop_modify( Slapi_PBlock *pb );
+int legacy_preop_modrdn( Slapi_PBlock *pb );
+int legacy_bepreop_modrdn( Slapi_PBlock *pb );
+int legacy_postop_modrdn( Slapi_PBlock *pb );
+int legacy_preop_delete( Slapi_PBlock *pb );
+int legacy_bepreop_delete( Slapi_PBlock *pb );
+int legacy_postop_delete( Slapi_PBlock *pb );
+int legacy_preop_search( Slapi_PBlock *pb );
+int legacy_preop_compare( Slapi_PBlock *pb );
+int legacy_pre_entry( Slapi_PBlock *pb );
+int legacy_bepostop_assignchangenum( Slapi_PBlock *pb );
+
+int replication_plugin_start( Slapi_PBlock *pb );
+int replication_plugin_poststart( Slapi_PBlock *pb );
+int replication_plugin_stop( Slapi_PBlock *pb );
+
+/* In repl.c */
+void replog( Slapi_PBlock *pb, int optype );
+void init_changelog_trimming( changeNumber max_changes, time_t max_age );
+
+/* From repl_globals.c */
+
+extern char *attr_changenumber;
+extern char *attr_targetdn;
+extern char *attr_changetype;
+extern char *attr_newrdn;
+extern char *attr_deleteoldrdn;
+extern char *attr_changes;
+extern char *attr_newsuperior;
+extern char *attr_changetime;
+extern char *attr_dataversion;
+extern char *attr_csn;
+
+extern char *changetype_add;
+extern char *changetype_delete;
+extern char *changetype_modify;
+extern char *changetype_modrdn;
+extern char *changetype_moddn;
+
+extern char *type_copyingFrom;
+extern char *type_copiedFrom;
+extern char *filter_copyingFrom;
+extern char *filter_copiedFrom;
+extern char *filter_objectclass;
+
+extern char *type_cn;
+extern char *type_objectclass;
+
+/* JCMREPL - IFP should be defined centrally */
+
+#ifndef _IFP
+#define _IFP
+typedef int (*IFP)();
+#endif
+
+/* In cl4.c */
+
+changeNumber ldapi_assign_changenumber(chglog4Info *cl4);
+changeNumber ldapi_get_last_changenumber(chglog4Info *cl4);
+changeNumber ldapi_get_first_changenumber(chglog4Info *cl4);
+void ldapi_commit_changenumber(chglog4Info *cl4, changeNumber cnum);
+void ldapi_set_first_changenumber(chglog4Info *cl4, changeNumber cnum);
+void ldapi_set_last_changenumber(chglog4Info *cl4, changeNumber cnum);
+void ldapi_initialize_changenumbers(chglog4Info *cl4, changeNumber first, changeNumber last);
+
+#define LDBM_TYPE "ldbm"
+#define CHANGELOG_LDBM_TYPE "changelog-ldbm"
+
+#define MAX_RETRY_INTERVAL 3600 /* sec = 1 hour */
+
+#define REPL_PROTOCOL_UNKNOWN 0
+#define REPL_PROTOCOL_40 1
+#define REPL_PROTOCOL_50_INCREMENTAL 2
+#define REPL_PROTOCOL_50_TOTALUPDATE 3
+
+/* In repl_globals.c */
+int decrement_repl_active_threads();
+int increment_repl_active_threads();
+
+/* operation extensions */
+
+/* Type of extensions that can be registered */
+typedef enum
+{
+ REPL_SUP_EXT_OP, /* extension for Operation object, replication supplier */
+ REPL_SUP_EXT_CONN, /* extension for Connection object, replication supplier */
+ REPL_CON_EXT_OP, /* extension for Operation object, replication consumer */
+ REPL_CON_EXT_CONN, /* extension for Connection object, replication consumer */
+ REPL_CON_EXT_MTNODE,/* extension for mapping_tree_node object, replication consumer */
+ REPL_EXT_ALL
+} ext_type;
+
+/* general extension functions - repl_ext.c */
+void repl_sup_init_ext (); /* initializes registrations - must be called first */
+void repl_con_init_ext (); /* initializes registrations - must be called first */
+int repl_sup_register_ext (ext_type type); /* registers an extension of the specified type */
+int repl_con_register_ext (ext_type type); /* registers an extension of the specified type */
+void* repl_sup_get_ext (ext_type type, void *object); /* retireves the extension from the object */
+void* repl_con_get_ext (ext_type type, void *object); /* retireves the extension from the object */
+
+/* Operation extension functions - supplier_operation_extension.c */
+
+/* --- supplier operation extension --- */
+typedef struct supplier_operation_extension
+{
+ int prevent_recursive_call;
+ struct slapi_operation_parameters *operation_parameters;
+ char *repl_gen;
+} supplier_operation_extension;
+
+/* extension construct/destructor */
+void* supplier_operation_extension_constructor (void *object, void *parent);
+void supplier_operation_extension_destructor (void* ext,void *object, void *parent);
+
+/* --- consumer operation extension --- */
+typedef struct consumer_operation_extension
+{
+ int has_cf; /* non-zero if the operation contains a copiedFrom/copyingFrom attr */
+ void *search_referrals;
+} consumer_operation_extension;
+
+/* extension construct/destructor */
+void* consumer_operation_extension_constructor (void *object, void *parent);
+void consumer_operation_extension_destructor (void* ext,void *object, void *parent);
+
+/* Connection extension functions - repl_connext.c */
+
+/* --- connection extension --- */
+/* ONREPL - some pointers are void* because they represent 5.0 data structures
+ not known in this header. Fix */
+typedef struct consumer_connection_extension
+{
+ int is_legacy_replication_dn;
+ int repl_protocol_version; /* the replication protocol version number the supplier is talking. */
+ void *replica_acquired; /* Object* for replica */
+ void *supplier_ruv; /* RUV* */
+ int isreplicationsession;
+ Slapi_Connection *connection;
+} consumer_connection_extension;
+
+/* extension construct/destructor */
+void* consumer_connection_extension_constructor (void *object,void *parent);
+void consumer_connection_extension_destructor (void* ext,void *object,void *parent);
+
+/* mapping tree extension - stores replica object */
+typedef struct multimaster_mtnode_extension
+{
+ Object *replica;
+} multimaster_mtnode_extension;
+void* multimaster_mtnode_extension_constructor (void *object,void *parent);
+void multimaster_mtnode_extension_destructor (void* ext,void *object,void *parent);
+
+/* In repl_init.c */
+
+int get_legacy_stop();
+
+/* In repl_entry.c */
+void repl_entry_init(int argc, char** argv);
+
+/* In repl_ops.c */
+int legacy_preop( Slapi_PBlock *pb, const char* caller, int operation_type);
+int legacy_postop( Slapi_PBlock *pb, const char* caller, int operation_type);
+
+/* In profile.c */
+
+#ifdef PROFILE
+#define PROFILE_POINT if (CFG_profile) profile_log(__FILE__,__LINE__) /* JCMREPL - Where is the profiling flag stored? */
+#else
+#define PROFILE_POINT ((void)0)
+#endif
+
+void profile_log(char *file,int line);
+void profile_open();
+void profile_close();
+
+/* in repl_controls.c */
+void add_repl_control_mods( Slapi_PBlock *pb, Slapi_Mods *smods );
+
+/* ... */
+void create_entity (char* DN, const char* oclass);
+
+void write_replog_db( int optype, char *dn, void *change, int flag, changeNumber changenum, time_t curtime, LDAPMod **modrdn_mods );
+int entry2reple( Slapi_Entry *e, Slapi_Entry *oe );
+int mods2reple( Slapi_Entry *e, LDAPMod **ldm );
+int modrdn2reple( Slapi_Entry *e, char *newrdn, int deloldrdn, LDAPMod **ldm );
+
+/* In legacy_consumer.c */
+void process_legacy_cf(Slapi_PBlock *pb);
+int legacy_consumer_is_replicationdn(char *dn);
+int legacy_consumer_is_replicationpw(struct berval *creds);
+int legacy_consumer_config_init();
+
+/* function that gets called when a backend state is changed */
+void legacy_consumer_be_state_change (void *handle, char *be_name,
+ int old_be_state, int new_be_state);
+
+#endif /* _REPL_H_ */
+
+
+
diff --git a/ldap/servers/plugins/replication/repl5.h b/ldap/servers/plugins/replication/repl5.h
new file mode 100644
index 00000000..d936cbea
--- /dev/null
+++ b/ldap/servers/plugins/replication/repl5.h
@@ -0,0 +1,480 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+
+/* repl5.h - 5.0 replication header */
+
+#ifndef _REPL5_H_
+#define _REPL5_H_
+
+#include <limits.h>
+#include <time.h>
+#include <stdio.h>
+#include <string.h>
+#ifndef _WIN32
+#include <sys/param.h>
+#endif /* _WIN32 */
+
+#include "portable.h" /* GGOODREPL - is this cheating? */
+#include "repl_shared.h"
+#include "llist.h"
+#include "repl5_ruv.h"
+#include "cl4.h"
+
+/* DS 5.0 replication protocol OIDs */
+#define REPL_START_NSDS50_REPLICATION_REQUEST_OID "2.16.840.1.113730.3.5.3"
+#define REPL_END_NSDS50_REPLICATION_REQUEST_OID "2.16.840.1.113730.3.5.5"
+#define REPL_NSDS50_REPLICATION_ENTRY_REQUEST_OID "2.16.840.1.113730.3.5.6"
+#define REPL_NSDS50_REPLICATION_RESPONSE_OID "2.16.840.1.113730.3.5.4"
+#define REPL_NSDS50_UPDATE_INFO_CONTROL_OID "2.16.840.1.113730.3.4.13"
+#define REPL_NSDS50_INCREMENTAL_PROTOCOL_OID "2.16.840.1.113730.3.6.1"
+#define REPL_NSDS50_TOTAL_PROTOCOL_OID "2.16.840.1.113730.3.6.2"
+
+/* DS 5.0 replication protocol error codes */
+#define NSDS50_REPL_REPLICA_READY 0x00 /* Replica ready, go ahead */
+#define NSDS50_REPL_REPLICA_BUSY 0x01 /* Replica busy, try later */
+#define NSDS50_REPL_EXCESSIVE_CLOCK_SKEW 0x02 /* Supplier clock too far ahead */
+#define NSDS50_REPL_PERMISSION_DENIED 0x03 /* Bind DN not allowed to send updates */
+#define NSDS50_REPL_DECODING_ERROR 0x04 /* Consumer couldn't decode extended operation */
+#define NSDS50_REPL_UNKNOWN_UPDATE_PROTOCOL 0x05 /* Consumer doesn't understand suplier's update protocol */
+#define NSDS50_REPL_NO_SUCH_REPLICA 0x06 /* Consumer holds no such replica */
+#define NSDS50_REPL_BELOW_PURGEPOINT 0x07 /* Supplier provided a CSN below the consumer's purge point */
+#define NSDS50_REPL_INTERNAL_ERROR 0x08 /* Something bad happened on consumer */
+#define NSDS50_REPL_REPLICA_RELEASE_SUCCEEDED 0x09 /* Replica released successfully */
+#define NSDS50_REPL_LEGACY_CONSUMER 0x0A /* replica is a legacy consumer */
+#define NSDS50_REPL_REPLICAID_ERROR 0x0B /* replicaID doesn't seem to be unique */
+#define NSDS50_REPL_DISABLED 0x0C /* replica suffix is disabled */
+#define NSDS50_REPL_UPTODATE 0x0D /* replica is uptodate */
+#define NSDS50_REPL_REPLICA_NO_RESPONSE 0xff /* No response received */
+
+/* Protocol status */
+#define PROTOCOL_STATUS_UNKNOWN 701
+#define PROTOCOL_STATUS_INCREMENTAL_AWAITING_CHANGES 702
+#define PROTOCOL_STATUS_INCREMENTAL_ACQUIRING_REPLICA 703
+#define PROTOCOL_STATUS_INCREMENTAL_RELEASING_REPLICA 704
+#define PROTOCOL_STATUS_INCREMENTAL_SENDING_UPDATES 705
+#define PROTOCOL_STATUS_INCREMENTAL_BACKING_OFF 706
+#define PROTOCOL_STATUS_INCREMENTAL_NEEDS_TOTAL_UPDATE 707
+#define PROTOCOL_STATUS_INCREMENTAL_FATAL_ERROR 708
+#define PROTOCOL_STATUS_TOTAL_ACQUIRING_REPLICA 709
+#define PROTOCOL_STATUS_TOTAL_RELEASING_REPLICA 710
+#define PROTOCOL_STATUS_TOTAL_SENDING_DATA 711
+
+/* To Allow Consumer Initialisation when adding an agreement - */
+#define STATE_PERFORMING_TOTAL_UPDATE 501
+#define STATE_PERFORMING_INCREMENTAL_UPDATE 502
+
+#define MAX_NUM_OF_MASTERS 64
+#define REPL_SESSION_ID_SIZE 64
+
+/* Attribute names for replication agreement attributes */
+extern const char *type_nsds5ReplicaHost;
+extern const char *type_nsds5ReplicaPort;
+extern const char *type_nsds5TransportInfo;
+extern const char *type_nsds5ReplicaBindDN;
+extern const char *type_nsds5ReplicaCredentials;
+extern const char *type_nsds5ReplicaBindMethod;
+extern const char *type_nsds5ReplicaRoot;
+extern const char *type_nsds5ReplicatedAttributeList;
+extern const char *type_nsds5ReplicaUpdateSchedule;
+extern const char *type_nsds5ReplicaInitialize;
+extern const char *type_nsds5ReplicaTimeout;
+extern const char *type_nsds5ReplicaBusyWaitTime;
+extern const char *type_nsds5ReplicaSessionPauseTime;
+
+/* To Allow Consumer Initialisation when adding an agreement - */
+extern const char *type_nsds5BeginReplicaRefresh;
+
+/* replica related attributes */
+extern const char *attr_replicaId;
+extern const char *attr_replicaRoot;
+extern const char *attr_replicaType;
+extern const char *attr_replicaBindDn;
+extern const char *attr_state;
+extern const char *attr_flags;
+extern const char *attr_replicaName;
+extern const char *attr_replicaReferral;
+extern const char *type_ruvElement;
+extern const char *type_replicaPurgeDelay;
+extern const char *type_replicaChangeCount;
+extern const char *type_replicaTombstonePurgeInterval;
+extern const char *type_replicaLegacyConsumer;
+extern const char *type_ruvElementUpdatetime;
+
+/* multimaster plugin points */
+int multimaster_preop_bind (Slapi_PBlock *pb);
+int multimaster_preop_add (Slapi_PBlock *pb);
+int multimaster_preop_delete (Slapi_PBlock *pb);
+int multimaster_preop_modify (Slapi_PBlock *pb);
+int multimaster_preop_modrdn (Slapi_PBlock *pb);
+int multimaster_preop_search (Slapi_PBlock *pb);
+int multimaster_preop_compare (Slapi_PBlock *pb);
+int multimaster_bepreop_add (Slapi_PBlock *pb);
+int multimaster_bepreop_delete (Slapi_PBlock *pb);
+int multimaster_bepreop_modify (Slapi_PBlock *pb);
+int multimaster_bepreop_modrdn (Slapi_PBlock *pb);
+int multimaster_bepostop_modrdn (Slapi_PBlock *pb);
+int multimaster_bepostop_delete (Slapi_PBlock *pb);
+int multimaster_postop_bind (Slapi_PBlock *pb);
+int multimaster_postop_add (Slapi_PBlock *pb);
+int multimaster_postop_delete (Slapi_PBlock *pb);
+int multimaster_postop_modify (Slapi_PBlock *pb);
+int multimaster_postop_modrdn (Slapi_PBlock *pb);
+
+/* In repl5_init.c */
+char* get_thread_private_agmtname ();
+void set_thread_private_agmtname (const char *agmtname);
+void* get_thread_private_cache ();
+void set_thread_private_cache (void *buf);
+char* get_repl_session_id (Slapi_PBlock *pb, char *id, CSN **opcsn);
+
+/* In repl_extop.c */
+int multimaster_extop_StartNSDS50ReplicationRequest(Slapi_PBlock *pb);
+int multimaster_extop_EndNSDS50ReplicationRequest(Slapi_PBlock *pb);
+int extop_noop(Slapi_PBlock *pb);
+struct berval *NSDS50StartReplicationRequest_new(const char *protocol_oid,
+ const char *repl_root, char **extra_referrals, CSN *csn);
+struct berval *NSDS50EndReplicationRequest_new(char *repl_root);
+int decode_repl_ext_response(struct berval *data, int *response_code,
+ struct berval ***ruv_bervals);
+
+/* In repl5_total.c */
+int multimaster_extop_NSDS50ReplicationEntry(Slapi_PBlock *pb);
+
+/* In repl_controls.c */
+int create_NSDS50ReplUpdateInfoControl(const char *uuid,
+ const char *superior_uuid, const CSN *csn,
+ LDAPMod **modify_mods, LDAPControl **ctrlp);
+void destroy_NSDS50ReplUpdateInfoControl(LDAPControl **ctrlp);
+int decode_NSDS50ReplUpdateInfoControl(LDAPControl **controlsp,
+ char **uuid, char **newsuperior_uuid, CSN **csn, LDAPMod ***modrdn_mods);
+
+/* In repl5_replsupplier.c */
+typedef struct repl_supplier Repl_Supplier;
+Repl_Supplier *replsupplier_init(Slapi_Entry *e);
+void replsupplier_configure(Repl_Supplier *rs, Slapi_PBlock *pb);
+void replsupplier_start(Repl_Supplier *rs);
+void replsupplier_stop(Repl_Supplier *rs);
+void replsupplier_destroy(Repl_Supplier **rs);
+void replsupplier_notify(Repl_Supplier *rs, PRUint32 eventmask);
+PRUint32 replsupplier_get_status(Repl_Supplier *rs);
+
+/* In repl5_plugins.c */
+int multimaster_set_local_purl();
+const char *multimaster_get_local_purl();
+PRBool multimaster_started();
+
+/* In repl5_schedule.c */
+typedef struct schedule Schedule;
+typedef void (*window_state_change_callback)(void *arg, PRBool opened);
+Schedule *schedule_new(window_state_change_callback callback_fn, void *callback_arg, const char *session_id);
+void schedule_destroy(Schedule *s);
+int schedule_set(Schedule *sch, Slapi_Attr *attr);
+char **schedule_get(Schedule *sch);
+int schedule_in_window_now(Schedule *sch);
+PRTime schedule_next(Schedule *sch);
+int schedule_notify(Schedule *sch, Slapi_PBlock *pb);
+void schedule_set_priority_attributes(Schedule *sch, char **prio_attrs, int override_schedule);
+void schedule_set_startup_delay(Schedule *sch, size_t startup_delay);
+void schedule_set_maximum_backlog(Schedule *sch, size_t max_backlog);
+void schedule_notify_session(Schedule *sch, PRTime session_end_time, unsigned int flags);
+#define REPLICATION_SESSION_SUCCESS 0
+
+/* In repl5_bos.c */
+typedef struct repl_bos Repl_Bos;
+
+/* In repl5_agmt.c */
+typedef struct repl5agmt Repl_Agmt;
+#define TRANSPORT_FLAG_SSL 1
+#define TRANSPORT_FLAG_TLS 2
+#define BINDMETHOD_SIMPLE_AUTH 1
+#define BINDMETHOD_SSL_CLIENTAUTH 2
+Repl_Agmt *agmt_new_from_entry(Slapi_Entry *e);
+Repl_Agmt *agmt_new_from_pblock(Slapi_PBlock *pb);
+void agmt_delete(void **ra);
+const Slapi_DN *agmt_get_dn_byref(const Repl_Agmt *ra);
+int agmt_get_auto_initialize(const Repl_Agmt *ra);
+long agmt_get_timeout(const Repl_Agmt *ra);
+long agmt_get_busywaittime(const Repl_Agmt *ra);
+long agmt_get_pausetime(const Repl_Agmt *ra);
+int agmt_start(Repl_Agmt *ra);
+int agmt_stop(Repl_Agmt *ra);
+int agmt_replicate_now(Repl_Agmt *ra);
+char *agmt_get_hostname(const Repl_Agmt *ra);
+int agmt_get_port(const Repl_Agmt *ra);
+PRUint32 agmt_get_transport_flags(const Repl_Agmt *ra);
+char *agmt_get_binddn(const Repl_Agmt *ra);
+struct berval *agmt_get_credentials(const Repl_Agmt *ra);
+int agmt_get_bindmethod(const Repl_Agmt *ra);
+Slapi_DN *agmt_get_replarea(const Repl_Agmt *ra);
+int agmt_is_fractional(const Repl_Agmt *ra);
+int agmt_is_fractional_attr(const Repl_Agmt *ra, const char *attrname);
+int agmt_is_50_mm_protocol(const Repl_Agmt *ra);
+int agmt_matches_name(const Repl_Agmt *ra, const Slapi_DN *name);
+int agmt_replarea_matches(const Repl_Agmt *ra, const Slapi_DN *name);
+int agmt_schedule_in_window_now(const Repl_Agmt *ra);
+int agmt_set_schedule_from_entry( Repl_Agmt *ra, const Slapi_Entry *e );
+int agmt_set_timeout_from_entry( Repl_Agmt *ra, const Slapi_Entry *e );
+int agmt_set_busywaittime_from_entry( Repl_Agmt *ra, const Slapi_Entry *e );
+int agmt_set_pausetime_from_entry( Repl_Agmt *ra, const Slapi_Entry *e );
+int agmt_set_credentials_from_entry( Repl_Agmt *ra, const Slapi_Entry *e );
+int agmt_set_binddn_from_entry( Repl_Agmt *ra, const Slapi_Entry *e );
+int agmt_set_bind_method_from_entry( Repl_Agmt *ra, const Slapi_Entry *e );
+int agmt_set_transportinfo_from_entry( Repl_Agmt *ra, const Slapi_Entry *e );
+const char *agmt_get_long_name(const Repl_Agmt *ra);
+int agmt_initialize_replica(const Repl_Agmt *agmt);
+void agmt_replica_init_done (const Repl_Agmt *agmt);
+void agmt_notify_change(Repl_Agmt *ra, Slapi_PBlock *pb);
+Object* agmt_get_consumer_ruv (Repl_Agmt *ra);
+ReplicaId agmt_get_consumer_rid ( Repl_Agmt *ra, void *conn );
+int agmt_set_consumer_ruv (Repl_Agmt *ra, RUV *ruv);
+void agmt_update_consumer_ruv (Repl_Agmt *ra);
+CSN* agmt_get_consumer_schema_csn (Repl_Agmt *ra);
+void agmt_set_consumer_schema_csn (Repl_Agmt *ra, CSN *csn);
+void agmt_set_last_update_in_progress (Repl_Agmt *ra, PRBool in_progress);
+void agmt_set_last_update_start (Repl_Agmt *ra, time_t start_time);
+void agmt_set_last_update_end (Repl_Agmt *ra, time_t end_time);
+void agmt_set_last_update_status (Repl_Agmt *ra, int ldaprc, int replrc, const char *msg);
+void agmt_set_update_in_progress (Repl_Agmt *ra, PRBool in_progress);
+void agmt_set_last_init_start (Repl_Agmt *ra, time_t start_time);
+void agmt_set_last_init_end (Repl_Agmt *ra, time_t end_time);
+void agmt_set_last_init_status (Repl_Agmt *ra, int ldaprc, int replrc, const char *msg);
+void agmt_inc_last_update_changecount (Repl_Agmt *ra, ReplicaId rid, int skipped);
+void agmt_get_changecount_string (Repl_Agmt *ra, char *buf, int bufsize);
+
+typedef struct replica Replica;
+
+/* In repl5_agmtlist.c */
+int agmtlist_config_init();
+void agmtlist_shutdown();
+void agmtlist_notify_all(Slapi_PBlock *pb);
+Object* agmtlist_get_first_agreement_for_replica (Replica *r);
+Object* agmtlist_get_next_agreement_for_replica (Replica *r, Object *prev);
+
+
+/* In repl5_backoff.c */
+typedef struct backoff_timer Backoff_Timer;
+#define BACKOFF_FIXED 1
+#define BACKOFF_EXPONENTIAL 2
+#define BACKOFF_RANDOM 3
+Backoff_Timer *backoff_new(int timer_type, int initial_interval, int max_interval);
+time_t backoff_reset(Backoff_Timer *bt, slapi_eq_fn_t callback, void *callback_data);
+time_t backoff_step(Backoff_Timer *bt);
+int backoff_expired(Backoff_Timer *bt, int margin);
+void backoff_delete(Backoff_Timer **btp);
+
+/* In repl5_connection.c */
+typedef struct repl_connection Repl_Connection;
+typedef enum
+{
+ CONN_OPERATION_SUCCESS,
+ CONN_OPERATION_FAILED,
+ CONN_NOT_CONNECTED,
+ CONN_SUPPORTS_DS5_REPL,
+ CONN_DOES_NOT_SUPPORT_DS5_REPL,
+ CONN_SCHEMA_UPDATED,
+ CONN_SCHEMA_NO_UPDATE_NEEDED,
+ CONN_LOCAL_ERROR,
+ CONN_BUSY,
+ CONN_SSL_NOT_ENABLED,
+ CONN_TIMEOUT
+} ConnResult;
+Repl_Connection *conn_new(Repl_Agmt *agmt);
+ConnResult conn_connect(Repl_Connection *conn);
+void conn_disconnect(Repl_Connection *conn);
+void conn_delete(Repl_Connection *conn);
+void conn_get_error(Repl_Connection *conn, int *operation, int *error);
+ConnResult conn_send_add(Repl_Connection *conn, const char *dn, LDAPMod **attrs,
+ LDAPControl *update_control, LDAPControl ***returned_controls);
+ConnResult conn_send_delete(Repl_Connection *conn, const char *dn,
+ LDAPControl *update_control, LDAPControl ***returned_controls);
+ConnResult conn_send_modify(Repl_Connection *conn, const char *dn, LDAPMod **mods,
+ LDAPControl *update_control, LDAPControl ***returned_controls);
+ConnResult conn_send_rename(Repl_Connection *conn, const char *dn,
+ const char *newrdn, const char *newparent, int deleteoldrdn,
+ LDAPControl *update_control, LDAPControl ***returned_controls);
+ConnResult conn_send_extended_operation(Repl_Connection *conn, const char *extop_oid,
+ struct berval *payload, char **retoidp, struct berval **retdatap,
+ LDAPControl *update_control, LDAPControl ***returned_controls);
+const char *conn_get_status(Repl_Connection *conn);
+void conn_start_linger(Repl_Connection *conn);
+void conn_cancel_linger(Repl_Connection *conn);
+ConnResult conn_replica_supports_ds5_repl(Repl_Connection *conn);
+ConnResult conn_read_entry_attribute(Repl_Connection *conn, const char *dn, char *type,
+ struct berval ***returned_bvals);
+ConnResult conn_push_schema(Repl_Connection *conn, CSN **remotecsn);
+void conn_set_timeout(Repl_Connection *conn, long timeout);
+void conn_set_agmt_changed(Repl_Connection *conn);
+
+/* In repl5_protocol.c */
+typedef struct repl_protocol Repl_Protocol;
+Repl_Protocol *prot_new(Repl_Agmt *agmt, int protocol_state);
+void prot_start(Repl_Protocol *rp);
+Repl_Agmt *prot_get_agreement(Repl_Protocol *rp);
+/* initiate total protocol */
+void prot_initialize_replica(Repl_Protocol *rp);
+/* stop protocol session in progress */
+void prot_stop(Repl_Protocol *rp);
+void prot_delete(Repl_Protocol **rpp);
+void prot_free(Repl_Protocol **rpp);
+PRBool prot_set_active_protocol (Repl_Protocol *rp, PRBool total);
+void prot_clear_active_protocol (Repl_Protocol *rp);
+Repl_Connection *prot_get_connection(Repl_Protocol *rp);
+void prot_resume(Repl_Protocol *rp, int wakeup_action);
+void prot_notify_update(Repl_Protocol *rp);
+void prot_notify_agmt_changed(Repl_Protocol *rp, char * agmt_name);
+void prot_notify_window_opened (Repl_Protocol *rp);
+void prot_notify_window_closed (Repl_Protocol *rp);
+Object *prot_get_replica_object(Repl_Protocol *rp);
+void prot_replicate_now(Repl_Protocol *rp);
+
+/* In repl5_replica.c */
+typedef enum
+{
+ REPLICA_TYPE_UNKNOWN,
+ REPLICA_TYPE_PRIMARY,
+ REPLICA_TYPE_READONLY,
+ REPLICA_TYPE_UPDATABLE,
+ REPLICA_TYPE_END
+} ReplicaType;
+
+#define RUV_STORAGE_ENTRY_UNIQUEID "ffffffff-ffffffff-ffffffff-ffffffff"
+#define START_ITERATION_ENTRY_UNIQUEID "00000000-00000000-00000000-00000000"
+#define START_ITERATION_ENTRY_DN "cn=start iteration"
+
+typedef int (*FNEnumReplica) (Replica *r, void *arg);
+
+/* this function should be called to construct the replica object
+ from the data already in the DIT */
+Replica *replica_new(const Slapi_DN *root);
+/* this function should be called to construct the replica object
+ during addition of the replica over LDAP */
+Replica *replica_new_from_entry (Slapi_Entry *e, char *errortext, PRBool is_add_operation);
+void replica_destroy(void **arg);
+PRBool replica_get_exclusive_access(Replica *r, PRBool *isInc, int connid, int opid,
+ const char *locking_purl,
+ char **current_purl);
+void replica_relinquish_exclusive_access(Replica *r, int connid, int opid);
+PRBool replica_get_tombstone_reap_active(const Replica *r);
+const Slapi_DN *replica_get_root(const Replica *r);
+const char *replica_get_name(const Replica *r);
+ReplicaId replica_get_rid (const Replica *r);
+void replica_set_rid (Replica *r, ReplicaId rid);
+PRBool replica_is_initialized (const Replica *r);
+Object *replica_get_ruv (const Replica *r);
+/* replica now owns the RUV */
+void replica_set_ruv (Replica *r, RUV *ruv);
+Object *replica_get_csngen (const Replica *r);
+ReplicaType replica_get_type (const Replica *r);
+void replica_set_type (Replica *r, ReplicaType type);
+PRBool replica_is_legacy_consumer (const Replica *r);
+void replica_set_legacy_consumer (Replica *r, PRBool legacy);
+char *replica_get_legacy_purl (const Replica *r);
+void replica_set_legacy_purl (Replica *r, const char *purl);
+PRBool replica_is_updatedn (const Replica *r, const Slapi_DN *sdn);
+void replica_set_updatedn (Replica *r, const Slapi_ValueSet *vs, int mod_op);
+char *replica_get_generation (const Replica *r);
+/* currently supported flags */
+#define REPLICA_LOG_CHANGES 0x1 /* enable change logging */
+PRBool replica_is_flag_set (const Replica *r, PRUint32 flag);
+void replica_set_flag (Replica *r, PRUint32 flag, PRBool clear);
+void replica_replace_flags (Replica *r, PRUint32 flags);
+void replica_dump(Replica *r);
+void replica_set_enabled (Replica *r, PRBool enable);
+Object *replica_get_replica_from_dn (const Slapi_DN *dn);
+void replica_update_ruv(Replica *replica, const CSN *csn, const char *replica_purl);
+Object *replica_get_replica_for_op (Slapi_PBlock *pb);
+/* the functions below manipulate replica hash */
+int replica_init_name_hash ();
+void replica_destroy_name_hash ();
+int replica_add_by_name (const char *name, Object *replica);
+int replica_delete_by_name (const char *name);
+Object* replica_get_by_name (const char *name);
+void replica_flush(Replica *r);
+void replica_get_referrals(const Replica *r, char ***referrals);
+void replica_set_referrals(Replica *r,const Slapi_ValueSet *vs);
+int replica_update_csngen_state (Replica *r, const RUV *ruv);
+CSN *replica_get_purge_csn(const Replica *r);
+int replica_log_ruv_elements (const Replica *r);
+void replica_enumerate_replicas (FNEnumReplica fn, void *arg);
+int replica_reload_ruv (Replica *r);
+int replica_check_for_data_reload (Replica *r, void *arg);
+/* the functions below manipulate replica dn hash */
+int replica_init_dn_hash ();
+void replica_destroy_dn_hash ();
+int replica_add_by_dn (const char *dn);
+int replica_delete_by_dn (const char *dn);
+int replica_is_being_configured (const char *dn);
+const CSN * _get_deletion_csn(Slapi_Entry *e);
+int legacy_consumer_init_referrals (Replica *r);
+void consumer5_set_mapping_tree_state_for_replica(const Replica *r, RUV *supplierRuv);
+Object *replica_get_for_backend (const char *be_name);
+void replica_set_purge_delay (Replica *r, PRUint32 purge_delay);
+void replica_set_tombstone_reap_interval (Replica *r, long interval);
+void replica_update_ruv_consumer (Replica *r, RUV *supplier_ruv);
+void replica_set_ruv_dirty (Replica *r);
+void replica_write_ruv (Replica *r);
+/* The functions below handles the state flag */
+/* Current internal state flags */
+/* The replica can be busy and not other flag,
+ * it means that the protocol has ended, but the work is not done yet.
+ * It happens on total protocol, the end protocol has been received,
+ * and the thread waits for import to finish
+ */
+#define REPLICA_IN_USE 1 /* The replica is busy */
+#define REPLICA_INCREMENTAL_IN_PROGRESS 2 /* Set only between start and stop inc */
+#define REPLICA_TOTAL_IN_PROGRESS 4 /* Set only between start and stop total */
+#define REPLICA_AGREEMENTS_DISABLED 8 /* Replica is offline */
+PRBool replica_is_state_flag_set(Replica *r, PRInt32 flag);
+void replica_set_state_flag (Replica *r, PRUint32 flag, PRBool clear);
+void replica_enable_replication (Replica *r);
+void replica_disable_replication (Replica *r, Object *r_obj);
+int replica_start_agreement(Replica *r, Repl_Agmt *ra);
+
+CSN* replica_generate_next_csn ( Slapi_PBlock *pb, const CSN *basecsn );
+int replica_get_attr ( Slapi_PBlock *pb, const char *type, void *value );
+
+/* mapping tree extensions manipulation */
+void multimaster_mtnode_extension_init ();
+void multimaster_mtnode_extension_destroy ();
+void multimaster_mtnode_construct_replicas ();
+
+void multimaster_be_state_change (void *handle, char *be_name, int old_be_state, int new_be_state);
+
+/* In repl5_replica_config.c */
+int replica_config_init();
+void replica_config_destroy ();
+
+/* replutil.c */
+LDAPControl* create_managedsait_control ();
+LDAPControl* create_backend_control(Slapi_DN *sdn);
+void repl_set_mtn_state_and_referrals(const Slapi_DN *sdn, const char *mtn_state,
+ const RUV *ruv, char **ruv_referrals,
+ char **other_referrals);
+void repl_set_repl_plugin_path(const char *path);
+
+/* repl5_updatedn_list.c */
+typedef void *ReplicaUpdateDNList;
+typedef int (*FNEnumDN)(Slapi_DN *dn, void *arg);
+ReplicaUpdateDNList replica_updatedn_list_new(const Slapi_Entry *entry);
+void replica_updatedn_list_free(ReplicaUpdateDNList list);
+void replica_updatedn_list_replace(ReplicaUpdateDNList list, const Slapi_ValueSet *vs);
+void replica_updatedn_list_delete(ReplicaUpdateDNList list, const Slapi_ValueSet *vs);
+void replica_updatedn_list_add(ReplicaUpdateDNList list, const Slapi_ValueSet *vs);
+PRBool replica_updatedn_list_ismember(ReplicaUpdateDNList list, const Slapi_DN *dn);
+char *replica_updatedn_list_to_string(ReplicaUpdateDNList list, const char *delimiter);
+void replica_updatedn_list_enumerate(ReplicaUpdateDNList list, FNEnumDN fn, void *arg);
+
+/* enabling developper traces for MMR to understand the total/inc protocol state machines */
+#ifdef DEV_DEBUG
+#define SLAPI_LOG_DEV_DEBUG SLAPI_LOG_FATAL
+#define dev_debug(a) slapi_log_error(SLAPI_LOG_DEV_DEBUG, "DEV_DEBUG", "%s\n", a)
+#else
+#define dev_debug(a)
+#endif
+
+void repl5_set_debug_timeout(const char *val);
+
+#endif /* _REPL5_H_ */
diff --git a/ldap/servers/plugins/replication/repl5_agmt.c b/ldap/servers/plugins/replication/repl5_agmt.c
new file mode 100644
index 00000000..2992fc11
--- /dev/null
+++ b/ldap/servers/plugins/replication/repl5_agmt.c
@@ -0,0 +1,1766 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+/* repl5_agmt.c */
+/*
+
+ Support for 5.0-style replication agreements.
+
+ Directory Server 5.0 replication agreements contain information about
+ replication consumers that we are supplying.
+
+ This module encapsulates the methods available for adding, deleting,
+ modifying, and firing replication agreements.
+
+ Methods:
+
+ agmt_new - Create a new replication agreement, in response to a new
+ replication agreement being added over LDAP.
+ agmt_delete - Destroy an agreement. It is an error to destroy an
+ agreement that has not been stopped.
+ agmt_getstatus - get the status of this replication agreement.
+ agmt_replicate_now - initiate a replication session asap, even if the
+ schedule says we shouldn't.
+ agmt_start - start replicating, according to schedule. Starts a new
+ thread to handle replication.
+ agmt_stop - stop replicating asap and end replication thread.
+ agmt_notify_change - notify the replication agreement about a change that
+ has been logged. The replication agreement will
+ decide if it needs to take some action, e.g. start a
+ replication session.
+ agmt_initialize_replica - start a complete replica refresh.
+ agmt_set_schedule_from_entry - (re)set the schedule associated with this
+ replication agreement based on a RA entry's contents.
+ agmt_set_credentials_from_entry - (re)set the credentials used to bind
+ to the remote replica.
+ agmt_set_binddn_from_entry - (re)set the DN used to bind
+ to the remote replica.
+ agmt_set_bind_method_from_entry - (re)set the bind method used to bind
+ to the remote replica (SIMPLE or SSLCLIENTAUTH).
+ agmt_set_transportinfo_from_entry - (re)set the transport used to bind
+ to the remote replica (SSL or not)
+
+*/
+
+#include "repl5.h"
+#include "repl5_prot_private.h"
+#include "cl5_api.h"
+
+#define DEFAULT_TIMEOUT 600 /* (seconds) default outbound LDAP connection */
+#define TRANSPORT_FLAG_SSL 1
+#define STATUS_LEN 1024
+
+struct changecounter {
+ ReplicaId rid;
+ PRUint32 num_replayed;
+ PRUint32 num_skipped;
+};
+
+typedef struct repl5agmt {
+ char *hostname; /* remote hostname */
+ int port; /* port of remote server */
+ PRUint32 transport_flags; /* SSL, TLS, etc. */
+ char *binddn; /* DN to bind as */
+ struct berval *creds; /* Password, or certificate */
+ int bindmethod; /* Bind method - simple, SSL */
+ Slapi_DN *replarea; /* DN of replicated area */
+ char **frac_attrs; /* list of fractional attributes to be replicated */
+ Schedule *schedule; /* Scheduling information */
+ int auto_initialize; /* 1 = automatically re-initialize replica */
+ const Slapi_DN *dn; /* DN of replication agreement entry */
+ const Slapi_RDN *rdn; /* RDN of replication agreement entry */
+ char *long_name; /* Long name (rdn + host, port) of entry, for logging */
+ Repl_Protocol *protocol; /* Protocol object - manages protocol */
+ struct changecounter *changecounters[MAX_NUM_OF_MASTERS]; /* changes sent/skipped since server start up */
+ int num_changecounters;
+ time_t last_update_start_time; /* Local start time of last update session */
+ time_t last_update_end_time; /* Local end time of last update session */
+ char last_update_status[STATUS_LEN]; /* Status of last update. Format = numeric code <space> textual description */
+ PRBool update_in_progress;
+ time_t last_init_start_time; /* Local start time of last total init */
+ time_t last_init_end_time; /* Local end time of last total init */
+ char last_init_status[STATUS_LEN]; /* Status of last total init. Format = numeric code <space> textual description */
+ PRLock *lock;
+ Object *consumerRUV; /* last RUV received from the consumer - used for changelog purging */
+ CSN *consumerSchemaCSN; /* last schema CSN received from the consumer */
+ ReplicaId consumerRID; /* indicates if the consumer is the originator of a CSN */
+ long timeout; /* timeout (in seconds) for outbound LDAP connections to remote server */
+ PRBool stop_in_progress; /* set by agmt_stop when shutting down */
+ long busywaittime; /* time in seconds to wait after getting a REPLICA BUSY from the consumer -
+ to allow another supplier to finish sending its updates -
+ if set to 0, this means to use the default value if we get a busy
+ signal from the consumer */
+ long pausetime; /* time in seconds to pause after sending updates -
+ to allow another supplier to send its updates -
+ should be greater than busywaittime -
+ if set to 0, this means do not pause */
+} repl5agmt;
+
+/* Forward declarations */
+void agmt_delete(void **rap);
+static void update_window_state_change_callback (void *arg, PRBool opened);
+static int get_agmt_status(Slapi_PBlock *pb, Slapi_Entry* e,
+ Slapi_Entry* entryAfter, int *returncode, char *returntext, void *arg);
+static int agmt_set_bind_method_no_lock(Repl_Agmt *ra, const Slapi_Entry *e);
+static int agmt_set_transportinfo_no_lock(Repl_Agmt *ra, const Slapi_Entry *e);
+
+/*
+Schema for replication agreement:
+
+cn
+nsds5ReplicaHost - hostname
+nsds5ReplicaPort - port number
+nsds5ReplicaTransportInfo - "SSL", "startTLS", or may be absent;
+nsds5ReplicaBindDN
+nsds5ReplicaCredentials
+nsds5ReplicaBindMethod - "SIMPLE" or "SSLCLIENTAUTH".
+nsds5ReplicaRoot - Replicated suffix
+nsds5ReplicatedAttributeList - Unused so far (meant for fractional repl).
+nsds5ReplicaUpdateSchedule
+nsds5ReplicaTimeout - Outbound repl operations timeout
+nsds50ruv - consumer's RUV
+nsds5ReplicaBusyWaitTime - time to wait after getting a REPLICA BUSY from the consumer
+nsds5ReplicaSessionPauseTime - time to pause after sending updates to allow another supplier to send
+*/
+
+
+/*
+ * Validate an agreement, making sure that it's valid.
+ * Return 1 if the agreement is valid, 0 otherwise.
+ */
+static int
+agmt_is_valid(Repl_Agmt *ra)
+{
+ int return_value = 1; /* assume valid, initially */
+ PR_ASSERT(NULL != ra);
+ PR_ASSERT(NULL != ra->dn);
+
+ if (NULL == ra->hostname)
+ {
+ slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name, "Replication agreement \"%s\" "
+ "is malformed: missing host name.\n", slapi_sdn_get_dn(ra->dn));
+ return_value = 0;
+ }
+ if (ra->port <= 0)
+ {
+ slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name, "Replication agreement \"%s\" "
+ "is malformed: invalid port number %d.\n", slapi_sdn_get_dn(ra->dn), ra->port);
+ return_value = 0;
+ }
+ if (ra->timeout < 0)
+ {
+ slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name, "Replication agreement \"%s\" "
+ "is malformed: invalid timeout %d.\n", slapi_sdn_get_dn(ra->dn), ra->timeout);
+ return_value = 0;
+ }
+ if (ra->busywaittime < 0)
+ {
+ slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name, "Replication agreement \"%s\" "
+ "is malformed: invalid busy wait time %d.\n", slapi_sdn_get_dn(ra->dn), ra->busywaittime);
+ return_value = 0;
+ }
+ if (ra->pausetime < 0)
+ {
+ slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name, "Replication agreement \"%s\" "
+ "is malformed: invalid pausetime %d.\n", slapi_sdn_get_dn(ra->dn), ra->pausetime);
+ return_value = 0;
+ }
+ return return_value;
+}
+
+
+Repl_Agmt *
+agmt_new_from_entry(Slapi_Entry *e)
+{
+ Repl_Agmt *ra;
+ char *tmpstr;
+ Slapi_Attr *sattr;
+
+ char *auto_initialize = NULL;
+ char *val_nsds5BeginReplicaRefresh = "start";
+
+ ra = (Repl_Agmt *)slapi_ch_calloc(1, sizeof(repl5agmt));
+ if ((ra->lock = PR_NewLock()) == NULL)
+ {
+ slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name, "Unable to create new lock "
+ "for replication agreement \"%s\" - agreement ignored.\n",
+ slapi_entry_get_dn_const(e));
+ goto loser;
+ }
+
+ /* Find all the stuff we need for the agreement */
+
+ /* To Allow Consumer Initialisation when adding an agreement: */
+
+ /*
+ Using 'auto_initialize' member of 'repl5agmt' structure to
+ store the effect of 'nsds5BeginReplicaRefresh' attribute's value
+ in it.
+ */
+ auto_initialize = slapi_entry_attr_get_charptr(e, type_nsds5BeginReplicaRefresh);
+ if ((auto_initialize != NULL) && (strcasecmp(auto_initialize, val_nsds5BeginReplicaRefresh) == 0))
+ {
+ ra->auto_initialize = STATE_PERFORMING_TOTAL_UPDATE;
+ }
+ else
+ {
+ ra->auto_initialize = STATE_PERFORMING_INCREMENTAL_UPDATE;
+ }
+
+ if (auto_initialize)
+ {
+ slapi_ch_free_string (&auto_initialize);
+ }
+
+ /* Host name of remote replica */
+ ra->hostname = slapi_entry_attr_get_charptr(e, type_nsds5ReplicaHost);
+ /* Port number for remote replica instance */
+ ra->port = slapi_entry_attr_get_int(e, type_nsds5ReplicaPort);
+ /* SSL, TLS, or other transport stuff */
+ ra->transport_flags = 0;
+ agmt_set_transportinfo_no_lock(ra, e);
+
+ /* DN to use when binding. May be empty if cert-based auth is to be used. */
+ ra->binddn = slapi_entry_attr_get_charptr(e, type_nsds5ReplicaBindDN);
+ if (NULL == ra->binddn)
+ {
+ ra->binddn = slapi_ch_strdup("");
+ }
+ /* Credentials to use when binding. */
+ ra->creds = (struct berval *)slapi_ch_malloc(sizeof(struct berval));
+ ra->creds->bv_val = NULL;
+ ra->creds->bv_len = 0;
+ if (slapi_entry_attr_find(e, type_nsds5ReplicaCredentials, &sattr) == 0)
+ {
+ Slapi_Value *sval;
+ if (slapi_attr_first_value(sattr, &sval) == 0)
+ {
+ const struct berval *bv = slapi_value_get_berval(sval);
+ if (NULL != bv)
+ {
+ ra->creds->bv_val = slapi_ch_malloc(bv->bv_len + 1);
+ memcpy(ra->creds->bv_val, bv->bv_val, bv->bv_len);
+ ra->creds->bv_len = bv->bv_len;
+ ra->creds->bv_val[bv->bv_len] = '\0'; /* be safe */
+ }
+ }
+ }
+ /* How to bind */
+ (void)agmt_set_bind_method_no_lock(ra, e);
+
+ /* timeout. */
+ ra->timeout = DEFAULT_TIMEOUT;
+ if (slapi_entry_attr_find(e, type_nsds5ReplicaTimeout, &sattr) == 0)
+ {
+ Slapi_Value *sval;
+ if (slapi_attr_first_value(sattr, &sval) == 0)
+ {
+ ra->timeout = slapi_value_get_long(sval);
+ }
+ }
+
+ /* DN of entry at root of replicated area */
+ tmpstr = slapi_entry_attr_get_charptr(e, type_nsds5ReplicaRoot);
+ if (NULL != tmpstr)
+ {
+ ra->replarea = slapi_sdn_new_dn_passin(tmpstr);
+ }
+ /* XXXggood get fractional attribute include/exclude lists here */
+ /* Replication schedule */
+ ra->schedule = schedule_new(update_window_state_change_callback, ra, agmt_get_long_name(ra));
+ if (slapi_entry_attr_find(e, type_nsds5ReplicaUpdateSchedule, &sattr) == 0)
+ {
+ schedule_set(ra->schedule, sattr);
+ }
+
+ /* busy wait time - time to wait after getting REPLICA BUSY from consumer */
+ ra->busywaittime = slapi_entry_attr_get_long(e, type_nsds5ReplicaBusyWaitTime);
+
+ /* pause time - time to pause after a session has ended */
+ ra->pausetime = slapi_entry_attr_get_long(e, type_nsds5ReplicaSessionPauseTime);
+
+ /* consumer's RUV */
+ if (slapi_entry_attr_find(e, type_ruvElement, &sattr) == 0)
+ {
+ RUV *ruv;
+
+ if (ruv_init_from_slapi_attr(sattr, &ruv) == 0)
+ {
+ ra->consumerRUV = object_new (ruv, (FNFree)ruv_destroy);
+ }
+ }
+
+ ra->consumerRID = 0;
+
+ /* DN and RDN of the replication agreement entry itself */
+ ra->dn = slapi_sdn_dup(slapi_entry_get_sdn((Slapi_Entry *)e));
+ ra->rdn = slapi_rdn_new_sdn(ra->dn);
+
+ /* Compute long name */
+ {
+ const char *agmtname = slapi_rdn_get_rdn(ra->rdn);
+ char hostname[128];
+ char *dot;
+
+ strncpy(hostname, ra->hostname ? ra->hostname : "(unknown)", sizeof(hostname));
+ hostname[sizeof(hostname)-1] = '\0';
+ dot = strchr(hostname, '.');
+ if (dot) {
+ *dot = '\0';
+ }
+ ra->long_name = slapi_ch_malloc(strlen(agmtname) +
+ strlen(hostname) + 25);
+ sprintf(ra->long_name, "agmt=\"%s\" (%s:%d)", agmtname, hostname, ra->port);
+ }
+
+ /* Initialize status information */
+ ra->last_update_start_time = 0UL;
+ ra->last_update_end_time = 0UL;
+ ra->num_changecounters = 0;
+ ra->last_update_status[0] = '\0';
+ ra->update_in_progress = PR_FALSE;
+ ra->stop_in_progress = PR_FALSE;
+ ra->last_init_end_time = 0UL;
+ ra->last_init_start_time = 0UL;
+ ra->last_init_status[0] = '\0';
+
+ if (!agmt_is_valid(ra))
+ {
+ goto loser;
+ }
+
+ /* Now that the agreement is done, just check if changelog is configured */
+ if (cl5GetState() != CL5_STATE_OPEN) {
+ slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name, "WARNING: "
+ "Replication agreement added but there is no changelog configured. "
+ "No change will be replicated until a changelog is configured.\n");
+ }
+
+ /*
+ * Establish a callback for this agreement's entry, so we can
+ * adorn it with status information when read.
+ */
+ slapi_config_register_callback(SLAPI_OPERATION_SEARCH, DSE_FLAG_PREOP, slapi_sdn_get_ndn(ra->dn),
+ LDAP_SCOPE_BASE, "(objectclass=*)", get_agmt_status, ra);
+
+ return ra;
+loser:
+ agmt_delete((void **)&ra);
+ return NULL;
+}
+
+
+
+Repl_Agmt *
+agmt_new_from_pblock(Slapi_PBlock *pb)
+{
+ Slapi_Entry *e;
+
+ slapi_pblock_get(pb, SLAPI_ADD_ENTRY, &e);
+ return agmt_new_from_entry(e);
+}
+
+
+/*
+ This should never be called directly - only should be called
+ as a destructor. XXXggood this is not finished
+ */
+void
+agmt_delete(void **rap)
+{
+ Repl_Agmt *ra;
+ PR_ASSERT(NULL != rap);
+ PR_ASSERT(NULL != *rap);
+
+ ra = (Repl_Agmt *)*rap;
+
+ /* do prot_delete first - we may be doing some processing using this
+ replication agreement, and prot_delete will make sure the
+ processing is complete - then it should be safe to clean up the
+ other fields below
+ */
+ prot_delete(&ra->protocol);
+
+ /*
+ * Remove the callback for this agreement's entry
+ */
+ slapi_config_remove_callback(SLAPI_OPERATION_SEARCH, DSE_FLAG_PREOP,
+ slapi_sdn_get_ndn(ra->dn),
+ LDAP_SCOPE_BASE, "(objectclass=*)",
+ get_agmt_status);
+
+ /* slapi_ch_free accepts NULL pointer */
+ slapi_ch_free((void **)&(ra->hostname));
+ slapi_ch_free((void **)&(ra->binddn));
+
+ if (NULL != ra->creds)
+ {
+ /* XXX free berval */
+ }
+ if (NULL != ra->replarea)
+ {
+ slapi_sdn_free(&ra->replarea);
+ }
+
+ if (NULL != ra->consumerRUV)
+ {
+ object_release (ra->consumerRUV);
+ }
+
+ csn_free (&ra->consumerSchemaCSN);
+ while ( --(ra->num_changecounters) >= 0 )
+ {
+ slapi_ch_free((void **)&ra->changecounters[ra->num_changecounters]);
+ }
+
+ schedule_destroy(ra->schedule);
+ slapi_ch_free((void **)&ra->long_name);
+ slapi_ch_free((void **)rap);
+}
+
+
+/*
+ * Allow replication for this replica to begin. Replication will
+ * occur at the next scheduled time. Returns 0 on success, -1 on
+ * failure.
+ */
+int
+agmt_start(Repl_Agmt *ra)
+{
+ Repl_Protocol *prot = NULL;
+
+ int protocol_state;
+
+ /* To Allow Consumer Initialisation when adding an agreement: */
+ if (ra->auto_initialize == STATE_PERFORMING_TOTAL_UPDATE)
+ {
+ protocol_state = STATE_PERFORMING_TOTAL_UPDATE;
+ }
+ else
+ {
+ protocol_state = STATE_PERFORMING_INCREMENTAL_UPDATE;
+ }
+
+ /* First, create a new protocol object */
+ if ((prot = prot_new(ra, protocol_state)) == NULL) {
+ return -1;
+ }
+
+ /* Now it is safe to own the agreement lock */
+ PR_Lock(ra->lock);
+
+ /* Check that replication is not already started */
+ if (ra->protocol != NULL) {
+ slapi_log_error(SLAPI_LOG_REPL, repl_plugin_name, "replication already started for agreement \"%s\"\n", agmt_get_long_name(ra));
+ PR_Unlock(ra->lock);
+ prot_free(&prot);
+ return 0;
+ }
+
+ ra->protocol = prot;
+
+ /* Start the protocol thread */
+ prot_start(ra->protocol);
+
+ PR_Unlock(ra->lock);
+ return 0;
+}
+
+/*
+Cease replicating to this replica as soon as possible.
+*/
+int
+agmt_stop(Repl_Agmt *ra)
+{
+ int return_value = 0;
+ Repl_Protocol *rp = NULL;
+
+ PR_Lock(ra->lock);
+ if (ra->stop_in_progress)
+ {
+ PR_Unlock(ra->lock);
+ return return_value;
+ }
+ ra->stop_in_progress = PR_TRUE;
+ rp = ra->protocol;
+ PR_Unlock(ra->lock);
+ if (NULL != rp) /* we use this pointer outside the lock - dangerous? */
+ {
+ prot_stop(rp);
+ }
+ PR_Lock(ra->lock);
+ ra->stop_in_progress = PR_FALSE;
+ /* we do not reuse the protocol object so free it */
+ prot_free(&ra->protocol);
+ PR_Unlock(ra->lock);
+ return return_value;
+}
+
+/*
+Send any pending updates as soon as possible, ignoring any replication
+schedules.
+*/
+int
+agmt_replicate_now(Repl_Agmt *ra)
+{
+ int return_value = 0;
+
+ return return_value;
+}
+
+/*
+ * Return a copy of the remote replica's hostname.
+ */
+char *
+agmt_get_hostname(const Repl_Agmt *ra)
+{
+ char *return_value;
+ PR_ASSERT(NULL != ra);
+ PR_Lock(ra->lock);
+ return_value = slapi_ch_strdup(ra->hostname);
+ PR_Unlock(ra->lock);
+ return return_value;
+}
+
+/*
+ * Return the port number of the remote replica's instance.
+ */
+int
+agmt_get_port(const Repl_Agmt *ra)
+{
+ int return_value;
+ PR_ASSERT(NULL != ra);
+ PR_Lock(ra->lock);
+ return_value = ra->port;
+ PR_Unlock(ra->lock);
+ return return_value;
+}
+
+/*
+ * Return the transport flags for this agreement.
+ */
+PRUint32
+agmt_get_transport_flags(const Repl_Agmt *ra)
+{
+ unsigned int return_value;
+ PR_ASSERT(NULL != ra);
+ PR_Lock(ra->lock);
+ return_value = ra->transport_flags;
+ PR_Unlock(ra->lock);
+ return return_value;
+}
+
+/*
+ * Return a copy of the bind dn to be used with this
+ * agreement (may return NULL if no binddn is required,
+ * e.g. SSL client auth.
+ */
+char *
+agmt_get_binddn(const Repl_Agmt *ra)
+{
+ char *return_value;
+ PR_ASSERT(NULL != ra);
+ PR_Lock(ra->lock);
+ return_value = ra->binddn == NULL ? NULL : slapi_ch_strdup(ra->binddn);
+ PR_Unlock(ra->lock);
+ return return_value;
+}
+
+/*
+ * Return a copy of the credentials.
+ */
+struct berval *
+agmt_get_credentials(const Repl_Agmt *ra)
+{
+ struct berval *return_value;
+ PR_ASSERT(NULL != ra);
+ PR_Lock(ra->lock);
+ return_value = (struct berval *)slapi_ch_malloc(sizeof(struct berval));
+ return_value->bv_val = (char *)slapi_ch_malloc(ra->creds->bv_len + 1);
+ return_value->bv_len = ra->creds->bv_len;
+ memcpy(return_value->bv_val, ra->creds->bv_val, ra->creds->bv_len);
+ return_value->bv_val[return_value->bv_len] = '\0'; /* just in case */
+ PR_Unlock(ra->lock);
+ return return_value;
+}
+
+int
+agmt_get_bindmethod(const Repl_Agmt *ra)
+{
+ int return_value;
+ PR_ASSERT(NULL != ra);
+ PR_Lock(ra->lock);
+ return_value = ra->bindmethod;
+ PR_Unlock(ra->lock);
+ return return_value;
+}
+
+/*
+ * Return a copy of the dn at the top of the replicated area.
+ */
+Slapi_DN *
+agmt_get_replarea(const Repl_Agmt *ra)
+{
+ Slapi_DN *return_value;
+ PR_ASSERT(NULL != ra);
+ PR_Lock(ra->lock);
+ return_value = slapi_sdn_new();
+ slapi_sdn_copy(ra->replarea, return_value);
+ PR_Unlock(ra->lock);
+ return return_value;
+}
+
+int
+agmt_is_fractional(const Repl_Agmt *ra)
+{
+ int return_value;
+ PR_ASSERT(NULL != ra);
+ PR_Lock(ra->lock);
+ return_value = ra->frac_attrs != NULL;
+ PR_Unlock(ra->lock);
+ return return_value;
+}
+
+int
+agmt_is_fractional_attr(const Repl_Agmt *ra, const char *attrname)
+{
+ int return_value;
+ PR_ASSERT(NULL != ra);
+ PR_Lock(ra->lock);
+ return_value = 1; /* XXXggood finish this */
+ PR_Unlock(ra->lock);
+ return return_value;
+}
+
+int
+agmt_get_auto_initialize(const Repl_Agmt *ra)
+{
+ int return_value;
+ PR_ASSERT(NULL != ra);
+ PR_Lock(ra->lock);
+ return_value = ra->auto_initialize;
+ PR_Unlock(ra->lock);
+ return return_value;
+}
+
+long
+agmt_get_timeout(const Repl_Agmt *ra)
+{
+ long return_value;
+ PR_ASSERT(NULL != ra);
+ PR_Lock(ra->lock);
+ return_value = ra->timeout;
+ PR_Unlock(ra->lock);
+ return return_value;
+}
+
+long
+agmt_get_busywaittime(const Repl_Agmt *ra)
+{
+ long return_value;
+ PR_ASSERT(NULL != ra);
+ PR_Lock(ra->lock);
+ return_value = ra->busywaittime;
+ PR_Unlock(ra->lock);
+ return return_value;
+}
+long
+agmt_get_pausetime(const Repl_Agmt *ra)
+{
+ long return_value;
+ PR_ASSERT(NULL != ra);
+ PR_Lock(ra->lock);
+ return_value = ra->pausetime;
+ PR_Unlock(ra->lock);
+ return return_value;
+}
+
+/*
+ * Warning - reference to the long name of the agreement is returned.
+ * The long name of an agreement is the DN of the agreement entry,
+ * followed by the host/port for the replica.
+ */
+const char *
+agmt_get_long_name(const Repl_Agmt *ra)
+{
+ char *return_value = NULL;
+
+ return_value = ra ? ra->long_name : "";
+ return return_value;
+}
+
+/*
+ * Warning - reference to dn is returned. However, since the dn of
+ * the replication agreement is its name, it won't change during the
+ * lifetime of the replication agreement object.
+ */
+const Slapi_DN *
+agmt_get_dn_byref(const Repl_Agmt *ra)
+{
+ const Slapi_DN *return_value = NULL;
+
+ PR_ASSERT(NULL != ra);
+ if (NULL != ra)
+ {
+ return_value = ra->dn;
+ }
+ return return_value;
+}
+
+/* Return 1 if name matches the replication Dn, 0 otherwise */
+int
+agmt_matches_name(const Repl_Agmt *ra, const Slapi_DN *name)
+{
+ int return_value = 0;
+ PR_ASSERT(NULL != ra);
+ if (NULL != ra)
+ {
+ PR_Lock(ra->lock);
+ if (slapi_sdn_compare(name, ra->dn) == 0)
+ {
+ return_value = 1;
+ }
+ PR_Unlock(ra->lock);
+ }
+ return return_value;
+}
+
+/* Return 1 if name matches the replication area, 0 otherwise */
+int
+agmt_replarea_matches(const Repl_Agmt *ra, const Slapi_DN *name)
+{
+ int return_value = 0;
+ PR_ASSERT(NULL != ra);
+ if (NULL != ra)
+ {
+ PR_Lock(ra->lock);
+ if (slapi_sdn_compare(name, ra->replarea) == 0)
+ {
+ return_value = 1;
+ }
+ PR_Unlock(ra->lock);
+ }
+ return return_value;
+}
+
+
+int
+agmt_schedule_in_window_now(const Repl_Agmt *ra)
+{
+ int return_value;
+ PR_ASSERT(NULL != ra);
+ PR_Lock(ra->lock);
+ if (NULL != ra->schedule && schedule_in_window_now(ra->schedule))
+ {
+ return_value = 1;
+ }
+ else
+ {
+ return_value = 0;
+ }
+ PR_Unlock(ra->lock);
+ return return_value;
+}
+
+
+/*
+ * Set or reset the credentials used to bind to the remote replica.
+ *
+ * Returns 0 if credentials set, or -1 if an error occurred.
+ */
+int
+agmt_set_credentials_from_entry(Repl_Agmt *ra, const Slapi_Entry *e)
+{
+ Slapi_Attr *sattr = NULL;
+ int return_value = 0;
+
+ PR_ASSERT(NULL != ra);
+ slapi_entry_attr_find(e, type_nsds5ReplicaCredentials, &sattr);
+ PR_Lock(ra->lock);
+ slapi_ch_free((void **)&ra->creds->bv_val);
+ ra->creds->bv_len = 0;
+ if (NULL != sattr)
+ {
+ Slapi_Value *sval = NULL;
+ slapi_attr_first_value(sattr, &sval);
+ if (NULL != sval)
+ {
+ const struct berval *bv = slapi_value_get_berval(sval);
+ ra->creds->bv_val = slapi_ch_calloc(1, bv->bv_len + 1);
+ memcpy(ra->creds->bv_val, bv->bv_val, bv->bv_len);
+ ra->creds->bv_len = bv->bv_len;
+ }
+ }
+ /* If no credentials set, set to zero-length string */
+ ra->creds->bv_val = NULL == ra->creds->bv_val ? slapi_ch_strdup("") : ra->creds->bv_val;
+ PR_Unlock(ra->lock);
+ prot_notify_agmt_changed(ra->protocol, ra->long_name);
+ return return_value;
+}
+
+/*
+ * Set or reset the DN used to bind to the remote replica.
+ *
+ * Returns 0 if DN set, or -1 if an error occurred.
+ */
+int
+agmt_set_binddn_from_entry(Repl_Agmt *ra, const Slapi_Entry *e)
+{
+ Slapi_Attr *sattr = NULL;
+ int return_value = 0;
+
+ PR_ASSERT(NULL != ra);
+ slapi_entry_attr_find(e, type_nsds5ReplicaBindDN, &sattr);
+ PR_Lock(ra->lock);
+ slapi_ch_free((void **)&ra->binddn);
+ ra->binddn = NULL;
+ if (NULL != sattr)
+ {
+ Slapi_Value *sval = NULL;
+ slapi_attr_first_value(sattr, &sval);
+ if (NULL != sval)
+ {
+ const char *val = slapi_value_get_string(sval);
+ ra->binddn = strdup(val);
+ }
+ }
+ /* If no BindDN set, set to zero-length string */
+ if (ra->binddn == NULL) {
+ ra->binddn = strdup("");
+ }
+ PR_Unlock(ra->lock);
+ prot_notify_agmt_changed(ra->protocol, ra->long_name);
+ return return_value;
+}
+
+/*
+ * Set or reset the bind method used to bind to the remote replica.
+ *
+ * Returns 0 if bind method set, or -1 if an error occurred.
+ */
+static int
+agmt_set_bind_method_no_lock(Repl_Agmt *ra, const Slapi_Entry *e)
+{
+ char *tmpstr = NULL;
+ int return_value = 0;
+
+ PR_ASSERT(NULL != ra);
+ tmpstr = slapi_entry_attr_get_charptr(e, type_nsds5ReplicaBindMethod);
+
+ if (NULL == tmpstr || strcasecmp(tmpstr, "SIMPLE") == 0)
+ {
+ ra->bindmethod = BINDMETHOD_SIMPLE_AUTH;
+ }
+ else if (strcasecmp(tmpstr, "SSLCLIENTAUTH") == 0)
+ {
+ ra->bindmethod = BINDMETHOD_SSL_CLIENTAUTH;
+ }
+ else
+ {
+ ra->bindmethod = BINDMETHOD_SIMPLE_AUTH;
+ }
+ slapi_ch_free((void **)&tmpstr);
+ return return_value;
+}
+
+int
+agmt_set_bind_method_from_entry(Repl_Agmt *ra, const Slapi_Entry *e)
+{
+ char *tmpstr = NULL;
+ int return_value = 0;
+
+ PR_ASSERT(NULL != ra);
+ PR_Lock(ra->lock);
+ if (ra->stop_in_progress)
+ {
+ PR_Unlock(ra->lock);
+ return return_value;
+ }
+ return_value = agmt_set_bind_method_no_lock(ra, e);
+ PR_Unlock(ra->lock);
+ prot_notify_agmt_changed(ra->protocol, ra->long_name);
+ return return_value;
+}
+
+/*
+ * Set or reset the transport used to bind to the remote replica.
+ *
+ * Returns 0 if transport set, or -1 if an error occurred.
+ */
+static int
+agmt_set_transportinfo_no_lock(Repl_Agmt *ra, const Slapi_Entry *e)
+{
+ char *tmpstr;
+ int rc = 0;
+
+ tmpstr = slapi_entry_attr_get_charptr(e, type_nsds5TransportInfo);
+ if (NULL != tmpstr && strcasecmp(tmpstr, "SSL") == 0)
+ {
+ ra->transport_flags |= TRANSPORT_FLAG_SSL;
+ } else {
+ ra->transport_flags &= ~TRANSPORT_FLAG_SSL;
+ }
+
+ slapi_ch_free((void **)&tmpstr);
+ return (rc);
+}
+
+int
+agmt_set_transportinfo_from_entry(Repl_Agmt *ra, const Slapi_Entry *e)
+{
+ int return_value = 0;
+
+ PR_ASSERT(NULL != ra);
+ PR_Lock(ra->lock);
+ if (ra->stop_in_progress)
+ {
+ PR_Unlock(ra->lock);
+ return return_value;
+ }
+ return_value = agmt_set_transportinfo_no_lock(ra, e);
+ PR_Unlock(ra->lock);
+ prot_notify_agmt_changed(ra->protocol, ra->long_name);
+
+ return return_value;
+}
+
+
+/*
+ * Set or reset the replication schedule. Notify the protocol handler
+ * that a change has been made.
+ *
+ * Returns 0 if schedule was set or -1 if an error occurred.
+ */
+int
+agmt_set_schedule_from_entry( Repl_Agmt *ra, const Slapi_Entry *e )
+{
+ Slapi_Attr *sattr;
+ int return_value = 0;
+
+ PR_ASSERT(NULL != ra);
+ PR_Lock(ra->lock);
+ if (ra->stop_in_progress)
+ {
+ PR_Unlock(ra->lock);
+ return return_value;
+ }
+ PR_Unlock(ra->lock);
+
+ if (slapi_entry_attr_find(e, type_nsds5ReplicaUpdateSchedule, &sattr) != 0)
+ {
+ sattr = NULL; /* no schedule ==> delete any existing one */
+ }
+
+ /* make it so */
+ return_value = schedule_set(ra->schedule, sattr);
+
+ if ( 0 == return_value ) {
+ /* schedule set OK -- spread the news */
+ prot_notify_agmt_changed(ra->protocol, ra->long_name);
+ }
+
+ return return_value;
+}
+
+/*
+ * Set or reset the timeout used to bind to the remote replica.
+ *
+ * Returns 0 if timeout set, or -1 if an error occurred.
+ */
+int
+agmt_set_timeout_from_entry(Repl_Agmt *ra, const Slapi_Entry *e)
+{
+ Slapi_Attr *sattr = NULL;
+ int return_value = -1;
+
+ PR_ASSERT(NULL != ra);
+ PR_Lock(ra->lock);
+ if (ra->stop_in_progress)
+ {
+ PR_Unlock(ra->lock);
+ return return_value;
+ }
+
+ slapi_entry_attr_find(e, type_nsds5ReplicaTimeout, &sattr);
+ if (NULL != sattr)
+ {
+ Slapi_Value *sval = NULL;
+ slapi_attr_first_value(sattr, &sval);
+ if (NULL != sval)
+ {
+ long tmpval = slapi_value_get_long(sval);
+ if (tmpval >= 0) {
+ ra->timeout = tmpval;
+ return_value = 0; /* success! */
+ }
+ }
+ }
+ PR_Unlock(ra->lock);
+ if (return_value == 0)
+ {
+ prot_notify_agmt_changed(ra->protocol, ra->long_name);
+ }
+ return return_value;
+}
+
+/*
+ * Set or reset the busywaittime
+ *
+ * Returns 0 if busywaittime set, or -1 if an error occurred.
+ */
+int
+agmt_set_busywaittime_from_entry(Repl_Agmt *ra, const Slapi_Entry *e)
+{
+ Slapi_Attr *sattr = NULL;
+ int return_value = -1;
+
+ PR_ASSERT(NULL != ra);
+ PR_Lock(ra->lock);
+ if (ra->stop_in_progress)
+ {
+ PR_Unlock(ra->lock);
+ return return_value;
+ }
+
+ slapi_entry_attr_find(e, type_nsds5ReplicaBusyWaitTime, &sattr);
+ if (NULL != sattr)
+ {
+ Slapi_Value *sval = NULL;
+ slapi_attr_first_value(sattr, &sval);
+ if (NULL != sval)
+ {
+ long tmpval = slapi_value_get_long(sval);
+ if (tmpval >= 0) {
+ ra->busywaittime = tmpval;
+ return_value = 0; /* success! */
+ }
+ }
+ }
+ PR_Unlock(ra->lock);
+ if (return_value == 0)
+ {
+ prot_notify_agmt_changed(ra->protocol, ra->long_name);
+ }
+ return return_value;
+}
+
+/*
+ * Set or reset the pausetime
+ *
+ * Returns 0 if pausetime set, or -1 if an error occurred.
+ */
+int
+agmt_set_pausetime_from_entry(Repl_Agmt *ra, const Slapi_Entry *e)
+{
+ Slapi_Attr *sattr = NULL;
+ int return_value = -1;
+
+ PR_ASSERT(NULL != ra);
+ PR_Lock(ra->lock);
+ if (ra->stop_in_progress)
+ {
+ PR_Unlock(ra->lock);
+ return return_value;
+ }
+
+ slapi_entry_attr_find(e, type_nsds5ReplicaSessionPauseTime, &sattr);
+ if (NULL != sattr)
+ {
+ Slapi_Value *sval = NULL;
+ slapi_attr_first_value(sattr, &sval);
+ if (NULL != sval)
+ {
+ long tmpval = slapi_value_get_long(sval);
+ if (tmpval >= 0) {
+ ra->pausetime = tmpval;
+ return_value = 0; /* success! */
+ }
+ }
+ }
+ PR_Unlock(ra->lock);
+ if (return_value == 0)
+ {
+ prot_notify_agmt_changed(ra->protocol, ra->long_name);
+ }
+ return return_value;
+}
+
+/* XXXggood - also make this pass an arg that tells if there was
+ * an update to a priority attribute */
+void
+agmt_notify_change(Repl_Agmt *agmt, Slapi_PBlock *pb)
+{
+ if (NULL != pb)
+ {
+ /* Is the entry within our replicated area? */
+ char *target_dn;
+ Slapi_DN *target_sdn;
+ int change_is_relevant = 0;
+
+ PR_ASSERT(NULL != agmt);
+ PR_Lock(agmt->lock);
+ if (agmt->stop_in_progress)
+ {
+ PR_Unlock(agmt->lock);
+ return;
+ }
+
+ slapi_pblock_get(pb, SLAPI_TARGET_DN, &target_dn);
+ target_sdn = slapi_sdn_new_dn_byref(target_dn); /* XXX see if you can avoid allocating this */
+
+ if (slapi_sdn_issuffix(target_sdn, agmt->replarea))
+ {
+ /*
+ * Yep, it's in our replicated area. Is this a fractional
+ * replication agreement?
+ */
+ if (NULL != agmt->frac_attrs)
+ {
+ /*
+ * Yep, it's fractional. See if the change should be
+ * tossed because it doesn't affect any of the replicated
+ * attributes.
+ */
+ int optype;
+ int affects_fractional_attribute = 0;
+
+ slapi_pblock_get(pb, SLAPI_OPERATION_TYPE, &optype);
+ if (SLAPI_OPERATION_MODIFY == optype)
+ {
+ LDAPMod **mods;
+ int i, j;
+
+ slapi_pblock_get(pb, SLAPI_MODIFY_MODS, &mods);
+ for (i = 0; !affects_fractional_attribute && NULL != agmt->frac_attrs[i]; i++)
+ {
+ for (j = 0; !affects_fractional_attribute && NULL != mods[j]; j++)
+ {
+ if (slapi_attr_types_equivalent(agmt->frac_attrs[i],
+ mods[i]->mod_type))
+ {
+ affects_fractional_attribute = 1;
+ }
+ }
+ }
+ }
+ else
+ {
+ /*
+ * Add, delete, and modrdn always cause some sort of
+ * operation replay, even if agreement is fractional.
+ */
+ affects_fractional_attribute = 1;
+ }
+ if (affects_fractional_attribute)
+ {
+ change_is_relevant = 1;
+ }
+ }
+ else
+ {
+ /* Not a fractional agreement */
+ change_is_relevant = 1;
+ }
+ }
+ PR_Unlock(agmt->lock);
+ slapi_sdn_free(&target_sdn);
+ if (change_is_relevant)
+ {
+ /* Notify the protocol that a change has occurred */
+ prot_notify_update(agmt->protocol);
+ }
+ }
+}
+
+
+
+int
+agmt_is_50_mm_protocol(const Repl_Agmt *agmt)
+{
+ return 1; /* XXXggood could support > 1 protocol */
+}
+
+
+
+int
+agmt_initialize_replica(const Repl_Agmt *agmt)
+{
+ PR_ASSERT(NULL != agmt);
+ PR_Lock(agmt->lock);
+ if (agmt->stop_in_progress)
+ {
+ PR_Unlock(agmt->lock);
+ return 0;
+ }
+ PR_Unlock(agmt->lock);
+ /* Call prot_initialize_replica only if the suffix is enabled (agmt->protocol != NULL) */
+ if (NULL != agmt->protocol) {
+ prot_initialize_replica(agmt->protocol);
+ }
+ else {
+ /* agmt->protocol == NULL --> Suffix is disabled */
+ return -1;
+ }
+ return 0;
+}
+
+/* delete nsds5BeginReplicaRefresh attribute to indicate to the clients
+ that replica initialization have completed */
+void
+agmt_replica_init_done (const Repl_Agmt *agmt)
+{
+ int rc;
+ Slapi_PBlock *pb = slapi_pblock_new ();
+ LDAPMod *mods [2];
+ LDAPMod mod;
+
+ mods[0] = &mod;
+ mods[1] = NULL;
+ mod.mod_op = LDAP_MOD_DELETE | LDAP_MOD_BVALUES;
+ mod.mod_type = (char*)type_nsds5ReplicaInitialize;
+ mod.mod_bvalues = NULL;
+
+ slapi_modify_internal_set_pb(pb, slapi_sdn_get_dn (agmt->dn), mods, NULL/* controls */,
+ NULL/* uniqueid */, repl_get_plugin_identity (PLUGIN_MULTIMASTER_REPLICATION), 0/* flags */);
+ slapi_modify_internal_pb (pb);
+
+ slapi_pblock_get(pb, SLAPI_PLUGIN_INTOP_RESULT, &rc);
+ if (rc != LDAP_SUCCESS && rc != LDAP_NO_SUCH_ATTRIBUTE)
+ {
+ slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name, "agmt_replica_init_done: "
+ "failed to remove (%s) attribute from (%s) entry; LDAP error - %d\n",
+ type_nsds5ReplicaInitialize, slapi_sdn_get_ndn (agmt->dn), rc);
+ }
+
+ slapi_pblock_destroy (pb);
+}
+
+/* Agreement object is acquired on behalf of the caller.
+ The caller is responsible for releasing the object
+ when it is no longer used */
+
+Object*
+agmt_get_consumer_ruv (Repl_Agmt *ra)
+{
+ Object *rt = NULL;
+
+ PR_ASSERT(NULL != ra);
+
+ PR_Lock(ra->lock);
+ if (ra->consumerRUV)
+ {
+ object_acquire (ra->consumerRUV);
+ rt = ra->consumerRUV;
+ }
+
+ PR_Unlock(ra->lock);
+
+ return rt;
+}
+
+int
+agmt_set_consumer_ruv (Repl_Agmt *ra, RUV *ruv)
+{
+ if (ra == NULL || ruv == NULL)
+ {
+ slapi_log_error(SLAPI_LOG_REPL, repl_plugin_name, "agmt_set_consumer_ruv: invalid argument"
+ " agmt - %p, ruv - %p\n", ra, ruv);
+ return -1;
+ }
+
+ PR_Lock(ra->lock);
+
+ if (ra->consumerRUV)
+ {
+ object_release (ra->consumerRUV);
+ }
+
+ ra->consumerRUV = object_new (ruv_dup (ruv), (FNFree)ruv_destroy);
+
+ PR_Unlock(ra->lock);
+
+ return 0;
+}
+
+void
+agmt_update_consumer_ruv (Repl_Agmt *ra)
+{
+ int rc;
+ RUV *ruv;
+ Slapi_Mod smod;
+ Slapi_Mod smod_last_modified;
+ Slapi_PBlock *pb;
+ LDAPMod *mods[3];
+
+ PR_ASSERT (ra);
+ PR_Lock(ra->lock);
+
+ if (ra->consumerRUV)
+ {
+ ruv = (RUV*) object_get_data (ra->consumerRUV);
+ PR_ASSERT (ruv);
+
+ ruv_to_smod(ruv, &smod);
+ ruv_last_modified_to_smod(ruv, &smod_last_modified);
+
+ /* it is ok to release the lock here because we are done with the agreement data.
+ we have to do it before issuing the modify operation because it causes
+ agmtlist_notify_all to be called which uses the same lock - hence the deadlock */
+ PR_Unlock(ra->lock);
+
+ pb = slapi_pblock_new ();
+ mods[0] = (LDAPMod *)slapi_mod_get_ldapmod_byref(&smod);
+ mods[1] = (LDAPMod *)slapi_mod_get_ldapmod_byref(&smod_last_modified);
+ mods[2] = NULL;
+
+ slapi_modify_internal_set_pb (pb, (char*)slapi_sdn_get_dn(ra->dn), mods, NULL, NULL,
+ repl_get_plugin_identity(PLUGIN_MULTIMASTER_REPLICATION), 0);
+ slapi_modify_internal_pb (pb);
+
+ slapi_pblock_get(pb, SLAPI_PLUGIN_INTOP_RESULT, &rc);
+ if (rc != LDAP_SUCCESS && rc != LDAP_NO_SUCH_ATTRIBUTE)
+ {
+ slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name, "%s: agmt_update_consumer_ruv: "
+ "failed to update consumer's RUV; LDAP error - %d\n",
+ ra->long_name, rc);
+ }
+
+ slapi_mod_done (&smod);
+ slapi_mod_done (&smod_last_modified);
+ slapi_pblock_destroy (pb);
+ }
+ else
+ PR_Unlock(ra->lock);
+}
+
+CSN*
+agmt_get_consumer_schema_csn (Repl_Agmt *ra)
+{
+ CSN *rt;
+
+ PR_ASSERT(NULL != ra);
+
+ PR_Lock(ra->lock);
+ rt = ra->consumerSchemaCSN;
+ PR_Unlock(ra->lock);
+
+ return rt;
+}
+
+void
+agmt_set_consumer_schema_csn (Repl_Agmt *ra, CSN *csn)
+{
+ PR_ASSERT(NULL != ra);
+
+ PR_Lock(ra->lock);
+ csn_free(&ra->consumerSchemaCSN);
+ ra->consumerSchemaCSN = csn;
+ PR_Unlock(ra->lock);
+}
+
+void
+agmt_set_last_update_start (Repl_Agmt *ra, time_t start_time)
+{
+ PR_ASSERT(NULL != ra);
+ if (NULL != ra)
+ {
+ ra->last_update_start_time = start_time;
+ ra->last_update_end_time = 0UL;
+ }
+}
+
+
+void
+agmt_set_last_update_end (Repl_Agmt *ra, time_t end_time)
+{
+ PR_ASSERT(NULL != ra);
+ if (NULL != ra)
+ {
+ ra->last_update_end_time = end_time;
+ }
+}
+
+void
+agmt_set_last_init_start (Repl_Agmt *ra, time_t start_time)
+{
+ PR_ASSERT(NULL != ra);
+ if (NULL != ra)
+ {
+ ra->last_init_start_time = start_time;
+ ra->last_init_end_time = 0UL;
+ }
+}
+
+
+void
+agmt_set_last_init_end (Repl_Agmt *ra, time_t end_time)
+{
+ PR_ASSERT(NULL != ra);
+ if (NULL != ra)
+ {
+ ra->last_init_end_time = end_time;
+ }
+}
+
+void
+agmt_set_last_update_status (Repl_Agmt *ra, int ldaprc, int replrc, const char *message)
+{
+ PR_ASSERT(NULL != ra);
+ if (NULL != ra)
+ {
+ if (replrc == NSDS50_REPL_UPTODATE)
+ {
+ /* no session started, no status update */
+ }
+ else if (ldaprc != LDAP_SUCCESS)
+ {
+ char *replmsg = NULL;
+ if ( replrc ) {
+ replmsg = protocol_response2string(replrc);
+ /* Do not mix the unknown replication error with the known ldap one */
+ if ( strcasecmp(replmsg, "unknown error") == 0 ) {
+ replmsg = NULL;
+ }
+ }
+ if (ldaprc > 0) {
+ PR_snprintf(ra->last_update_status, STATUS_LEN,
+ "%d %s%sLDAP error: %s%s%s",
+ ldaprc,
+ message?message:"",message?"":" - ",
+ ldap_err2string(ldaprc),
+ replmsg ? " - " : "", replmsg ? replmsg : "");
+ } else { /* ldaprc is < 0 */
+ PR_snprintf(ra->last_update_status, STATUS_LEN,
+ "%d %s%sSystem error%s%s",
+ ldaprc,message?message:"",message?"":" - ",
+ replmsg ? " - " : "", replmsg ? replmsg : "");
+ }
+ }
+ else if (replrc != 0)
+ {
+ if (replrc == NSDS50_REPL_REPLICA_READY)
+ {
+ PR_snprintf(ra->last_update_status, STATUS_LEN, "%d %s",
+ ldaprc, "Replica acquired successfully");
+ }
+ else if (replrc == NSDS50_REPL_REPLICA_BUSY)
+ {
+ PR_snprintf(ra->last_update_status, STATUS_LEN,
+ "%d Can't acquire busy replica", replrc );
+ }
+ else if (replrc == NSDS50_REPL_REPLICA_RELEASE_SUCCEEDED)
+ {
+ PR_snprintf(ra->last_update_status, STATUS_LEN, "%d %s",
+ ldaprc, "Replication session successful");
+ }
+ else if (replrc == NSDS50_REPL_DISABLED)
+ {
+ PR_snprintf(ra->last_update_status, STATUS_LEN, "%d Total update aborted: "
+ "Replication agreement for %s\n can not be updated while the replica is disabled.\n"
+ "(If the suffix is disabled you must enable it then restart the server for replication to take place).",
+ replrc, ra->long_name ? ra->long_name : "a replica");
+ /* Log into the errors log, as "ra->long_name" is not accessible from the caller */
+ slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name,
+ "Total update aborted: Replication agreement for \"%s\" "
+ "can not be updated while the replica is disabled\n", ra->long_name ? ra->long_name : "a replica");
+ slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name,
+ "(If the suffix is disabled you must enable it then restart the server for replication to take place).\n");
+ }
+ else
+ {
+ PR_snprintf(ra->last_update_status, STATUS_LEN,
+ "%d Replication error acquiring replica: %s%s%s",
+ replrc, protocol_response2string(replrc),
+ message?" - ":"",message?message:"");
+ }
+ }
+ else if (message != NULL)
+ {
+ PR_snprintf(ra->last_update_status, STATUS_LEN, "%d %s", ldaprc, message);
+ }
+ else { /* agmt_set_last_update_status(0,0,NULL) to reset agmt */
+ PR_snprintf(ra->last_update_status, STATUS_LEN, "%d", ldaprc);
+ }
+ }
+}
+
+void
+agmt_set_last_init_status (Repl_Agmt *ra, int ldaprc, int replrc, const char *message)
+{
+ PR_ASSERT(NULL != ra);
+ if (NULL != ra)
+ {
+ if (ldaprc != LDAP_SUCCESS)
+ {
+ char *replmsg = NULL;
+ if ( replrc ) {
+ replmsg = protocol_response2string(replrc);
+ /* Do not mix the unknown replication error with the known ldap one */
+ if ( strcasecmp(replmsg, "unknown error") == 0 ) {
+ replmsg = NULL;
+ }
+ }
+ if (ldaprc > 0) {
+ PR_snprintf(ra->last_init_status, STATUS_LEN,
+ "%d %s%sLDAP error: %s%s%s",
+ ldaprc,
+ message?message:"",message?"":" - ",
+ ldap_err2string(ldaprc),
+ replmsg ? " - " : "", replmsg ? replmsg : "");
+ } else { /* ldaprc is < 0 */
+ PR_snprintf(ra->last_init_status, STATUS_LEN,
+ "%d %s%sSystem error%s%s",
+ ldaprc,message?message:"",message?"":" - ",
+ replmsg ? " - " : "", replmsg ? replmsg : "");
+ }
+ }
+ else if (replrc != 0)
+ {
+ if (replrc == NSDS50_REPL_REPLICA_READY)
+ {
+ PR_snprintf(ra->last_init_status, STATUS_LEN, "%d %s",
+ ldaprc, "Replica acquired successfully");
+ }
+ else if (replrc == NSDS50_REPL_REPLICA_RELEASE_SUCCEEDED)
+ {
+ PR_snprintf(ra->last_init_status, STATUS_LEN, "%d %s",
+ ldaprc, "Replication session successful");
+ }
+ else if (replrc == NSDS50_REPL_DISABLED)
+ {
+ PR_snprintf(ra->last_init_status, STATUS_LEN, "%d Total update aborted: "
+ "Replication agreement for %s\n can not be updated while the replica is disabled.\n"
+ "(If the suffix is disabled you must enable it then restart the server for replication to take place).",
+ replrc, ra->long_name ? ra->long_name : "a replica");
+ /* Log into the errors log, as "ra->long_name" is not accessible from the caller */
+ slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name,
+ "Total update aborted: Replication agreement for \"%s\" "
+ "can not be updated while the replica is disabled\n", ra->long_name ? ra->long_name : "a replica");
+ slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name,
+ "(If the suffix is disabled you must enable it then restart the server for replication to take place).\n");
+ }
+ else
+ {
+ PR_snprintf(ra->last_init_status, STATUS_LEN,
+ "%d Replication error acquiring replica: %s%s%s",
+ replrc, protocol_response2string(replrc),
+ message?" - ":"",message?message:"");
+ }
+ }
+ else if (message != NULL)
+ {
+ PR_snprintf(ra->last_init_status, STATUS_LEN, "%d %s", ldaprc, message);
+ }
+ else { /* agmt_set_last_init_status(0,0,NULL) to reset agmt */
+ PR_snprintf(ra->last_init_status, STATUS_LEN, "%d", ldaprc);
+ }
+ }
+}
+
+
+void
+agmt_set_update_in_progress (Repl_Agmt *ra, PRBool in_progress)
+{
+ PR_ASSERT(NULL != ra);
+ if (NULL != ra)
+ {
+ ra->update_in_progress = in_progress;
+ }
+}
+
+void
+agmt_inc_last_update_changecount (Repl_Agmt *ra, ReplicaId rid, int skipped)
+{
+ PR_ASSERT(NULL != ra);
+ if (NULL != ra)
+ {
+ int i;
+
+ for ( i = 0; i < ra->num_changecounters; i++ )
+ {
+ if ( ra->changecounters[i]->rid == rid )
+ break;
+ }
+
+ if ( i < ra->num_changecounters )
+ {
+ if ( skipped )
+ ra->changecounters[i]->num_skipped ++;
+ else
+ ra->changecounters[i]->num_replayed ++;
+ }
+ else
+ {
+ ra->num_changecounters ++;
+ ra->changecounters[i] = (struct changecounter*) slapi_ch_calloc(1, sizeof(struct changecounter));
+ ra->changecounters[i]->rid = rid;
+ if ( skipped )
+ ra->changecounters[i]->num_skipped = 1;
+ else
+ ra->changecounters[i]->num_replayed = 1;
+ }
+ }
+}
+
+void
+agmt_get_changecount_string (Repl_Agmt *ra, char *buf, int bufsize)
+{
+ char tmp_buf[32]; /* 5 digit RID, 10 digit each replayed and skipped */
+ int i;
+ int buflen = 0;
+
+ *buf = '\0';
+ if (NULL != ra)
+ {
+ for ( i = 0; i < ra->num_changecounters; i++ )
+ {
+ PR_snprintf (tmp_buf, sizeof(tmp_buf), "%u:%u/%u ",
+ ra->changecounters[i]->rid,
+ ra->changecounters[i]->num_replayed,
+ ra->changecounters[i]->num_skipped);
+ PR_snprintf (buf+buflen, bufsize-buflen, "%s", tmp_buf);
+ buflen += strlen (tmp_buf);
+ }
+ }
+}
+
+static int
+get_agmt_status(Slapi_PBlock *pb, Slapi_Entry* e, Slapi_Entry* entryAfter,
+ int *returncode, char *returntext, void *arg)
+{
+ char *time_tmp = NULL;
+ char changecount_string[BUFSIZ];
+ Repl_Agmt *ra = (Repl_Agmt *)arg;
+
+ PR_ASSERT(NULL != ra);
+ if (NULL != ra)
+ {
+ PRBool reapActive = PR_FALSE;
+ Slapi_DN *replarea_sdn = NULL;
+ Object *repl_obj = NULL;
+
+ replarea_sdn = agmt_get_replarea(ra);
+ repl_obj = replica_get_replica_from_dn(replarea_sdn);
+ slapi_sdn_free(&replarea_sdn);
+ if (repl_obj) {
+ Replica *replica = (Replica*)object_get_data (repl_obj);
+ reapActive = replica_get_tombstone_reap_active(replica);
+ object_release(repl_obj);
+ }
+ slapi_entry_attr_set_int(e, "nsds5replicaReapActive", (int)reapActive);
+
+ /* these values persist in the dse.ldif file, so we delete them
+ here to avoid multi valued attributes */
+ slapi_entry_attr_delete(e, "nsds5replicaLastUpdateStart");
+ slapi_entry_attr_delete(e, "nsds5replicaLastUpdateEnd");
+ slapi_entry_attr_delete(e, "nsds5replicaChangesSentSinceStartup");
+ slapi_entry_attr_delete(e, "nsds5replicaLastUpdateStatus");
+ slapi_entry_attr_delete(e, "nsds5replicaUpdateInProgress");
+ slapi_entry_attr_delete(e, "nsds5replicaLastInitStart");
+ slapi_entry_attr_delete(e, "nsds5replicaLastInitStatus");
+ slapi_entry_attr_delete(e, "nsds5replicaLastInitEnd");
+
+ /* now, add the real values (singly) */
+ if (ra->last_update_start_time == 0)
+ {
+ slapi_entry_add_string(e, "nsds5replicaLastUpdateStart", "0");
+ }
+ else
+ {
+ time_tmp = format_genTime(ra->last_update_start_time);
+ slapi_entry_add_string(e, "nsds5replicaLastUpdateStart", time_tmp);
+ slapi_ch_free((void **)&time_tmp);
+ }
+ if (ra->last_update_end_time == 0)
+ {
+ slapi_entry_add_string(e, "nsds5replicaLastUpdateEnd", "0");
+ }
+ else
+ {
+ time_tmp = format_genTime(ra->last_update_end_time);
+ slapi_entry_add_string(e, "nsds5replicaLastUpdateEnd", time_tmp);
+ slapi_ch_free((void **)&time_tmp);
+ }
+ agmt_get_changecount_string (ra, changecount_string, sizeof (changecount_string) );
+ slapi_entry_add_string(e, "nsds5replicaChangesSentSinceStartup", changecount_string);
+ if (ra->last_update_status[0] == '\0')
+ {
+ slapi_entry_add_string(e, "nsds5replicaLastUpdateStatus", "0 No replication sessions started since server startup");
+ }
+ else
+ {
+ slapi_entry_add_string(e, "nsds5replicaLastUpdateStatus", ra->last_update_status);
+ }
+ slapi_entry_add_string(e, "nsds5replicaUpdateInProgress", ra->update_in_progress ? "TRUE" : "FALSE");
+ if (ra->last_init_start_time == 0)
+ {
+ slapi_entry_add_string(e, "nsds5replicaLastInitStart", "0");
+ }
+ else
+ {
+ time_tmp = format_genTime(ra->last_init_start_time);
+ slapi_entry_add_string(e, "nsds5replicaLastInitStart", time_tmp);
+ slapi_ch_free((void **)&time_tmp);
+ }
+ if (ra->last_init_end_time == 0)
+ {
+ slapi_entry_add_string(e, "nsds5replicaLastInitEnd", "0");
+ }
+ else
+ {
+ time_tmp = format_genTime(ra->last_init_end_time);
+ slapi_entry_add_string(e, "nsds5replicaLastInitEnd", time_tmp);
+ slapi_ch_free((void **)&time_tmp);
+ }
+ if (ra->last_init_status[0] != '\0')
+ {
+ slapi_entry_add_string(e, "nsds5replicaLastInitStatus", ra->last_init_status);
+ }
+ }
+ return SLAPI_DSE_CALLBACK_OK;
+}
+
+static void
+update_window_state_change_callback (void *arg, PRBool opened)
+{
+ Repl_Agmt *agmt = (Repl_Agmt*)arg;
+
+ PR_ASSERT (agmt);
+
+ if (opened)
+ {
+ prot_notify_window_opened (agmt->protocol);
+ }
+ else
+ {
+ prot_notify_window_closed (agmt->protocol);
+ }
+}
+
+ReplicaId
+agmt_get_consumer_rid ( Repl_Agmt *agmt, void *conn )
+{
+ if ( agmt->consumerRID <= 0 ) {
+
+ char mapping_tree_node[512];
+ struct berval **bvals = NULL;
+
+ PR_snprintf ( mapping_tree_node,
+ sizeof (mapping_tree_node),
+ "cn=replica,cn=\"%s\",cn=mapping tree,cn=config",
+ slapi_sdn_get_dn (agmt->replarea) );
+ conn_read_entry_attribute ( conn, mapping_tree_node, "nsDS5ReplicaID", &bvals );
+ if ( NULL != bvals && NULL != bvals[0] ) {
+ char *ridstr = slapi_ch_malloc( bvals[0]->bv_len + 1 );
+ memcpy ( ridstr, bvals[0]->bv_val, bvals[0]->bv_len );
+ ridstr[bvals[0]->bv_len] = '\0';
+ agmt->consumerRID = atoi (ridstr);
+ slapi_ch_free ( (void**) &ridstr );
+ ber_bvecfree ( bvals );
+ }
+ }
+
+ return agmt->consumerRID;
+}
diff --git a/ldap/servers/plugins/replication/repl5_agmtlist.c b/ldap/servers/plugins/replication/repl5_agmtlist.c
new file mode 100644
index 00000000..5c7213a6
--- /dev/null
+++ b/ldap/servers/plugins/replication/repl5_agmtlist.c
@@ -0,0 +1,618 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+/* repl5_agmtlist.c */
+/*
+
+ Replication agreements are held in object set (objset.c).
+
+*/
+
+#include "repl5.h"
+
+#define AGMT_CONFIG_BASE "cn=mapping tree, cn=config"
+#define CONFIG_FILTER "(objectclass=nsds5replicationagreement)"
+
+PRCallOnceType once = {0};
+static Objset *agmt_set = NULL; /* The set of replication agreements */
+
+typedef struct agmt_wrapper {
+ Repl_Agmt *agmt;
+ void *handle;
+} agmt_wrapper;
+
+
+
+/*
+ * Find the replication agreement whose entry DN matches the given DN.
+ * Object is returned referenced, so be sure to release it when
+ * finished.
+ */
+Repl_Agmt *
+agmtlist_get_by_agmt_name(const Slapi_DN *agmt_name)
+{
+ Repl_Agmt *ra;
+ Object *ro;
+
+ for (ro = objset_first_obj(agmt_set); NULL != ro;
+ ro = objset_next_obj(agmt_set, ro))
+ {
+ ra = (Repl_Agmt *)object_get_data(ro);
+ if (agmt_matches_name(ra, agmt_name))
+ {
+ break;
+ }
+ }
+ return ra;
+}
+
+
+static int
+agmt_ptr_cmp(Object *ro, const void *arg)
+{
+ Repl_Agmt *ra;
+ Repl_Agmt *provided_ra = (Repl_Agmt *)arg;
+
+ ra = object_get_data(ro);
+
+ if (ra == provided_ra)
+ return 0;
+ else
+ return 1;
+}
+
+
+
+static int
+agmt_dn_cmp(Object *ro, const void *arg)
+{
+ Repl_Agmt *ra;
+ Slapi_DN *sdn = (Slapi_DN *)arg;
+
+ ra = object_get_data(ro);
+ return(slapi_sdn_compare(sdn, agmt_get_dn_byref(ra)));
+}
+
+void
+agmtlist_release_agmt(Repl_Agmt *ra)
+{
+ Object *ro;
+
+ PR_ASSERT(NULL != agmt_set);
+ PR_ASSERT(NULL != ra);
+
+ ro = objset_find(agmt_set, agmt_ptr_cmp, (const void *)ra);
+ if (NULL != ro)
+ {
+ /*
+ * Release twice - once for the reference we got when finding
+ * it, and once for the reference we got when we called
+ * agmtlist_get_*().
+ */
+ object_release(ro);
+ object_release(ro);
+ }
+}
+
+
+/*
+ * Note: when we add the new object, we have a reference to it. We hold
+ * on to this reference until the agreement is deleted (or until the
+ * server is shut down).
+ */
+static int
+add_new_agreement(Slapi_Entry *e)
+{
+ int rc = 0;
+ Repl_Agmt *ra = agmt_new_from_entry(e);
+ Slapi_DN *replarea_sdn = NULL;
+ Replica *replica = NULL;
+ Object *repl_obj = NULL;
+ Object *ro = NULL;
+
+ if (ra == NULL) return 0;
+
+ ro = object_new((void *)ra, agmt_delete);
+ objset_add_obj(agmt_set, ro);
+ object_release(ro); /* Object now owned by objset */
+
+ /* get the replica for this agreement */
+ replarea_sdn = agmt_get_replarea(ra);
+ repl_obj = replica_get_replica_from_dn(replarea_sdn);
+ slapi_sdn_free(&replarea_sdn);
+ if (repl_obj) {
+ replica = (Replica*)object_get_data (repl_obj);
+ }
+
+ rc = replica_start_agreement(replica, ra);
+
+ if (repl_obj) object_release(repl_obj);
+
+ return rc;
+}
+
+static int
+agmtlist_add_callback(Slapi_PBlock *pb, Slapi_Entry *e, Slapi_Entry *entryAfter,
+ int *returncode, char *returntext, void *arg)
+{
+ int rc;
+ slapi_log_error(SLAPI_LOG_REPL, repl_plugin_name, "agmt_add: begin\n");
+
+ rc = add_new_agreement(e);
+ if (0 != rc) {
+ char *dn;
+ slapi_pblock_get(pb, SLAPI_TARGET_DN, &dn);
+ slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name, "agmtlist_add_callback: "
+ "Can't start agreement \"%s\"\n", dn);
+ *returncode = LDAP_UNWILLING_TO_PERFORM;
+ return SLAPI_DSE_CALLBACK_ERROR;
+ }
+ *returncode = LDAP_SUCCESS;
+ return SLAPI_DSE_CALLBACK_OK;
+}
+
+static int
+agmtlist_modify_callback(Slapi_PBlock *pb, Slapi_Entry *entryBefore, Slapi_Entry *e,
+ int *returncode, char *returntext, void *arg)
+{
+ int i;
+ char *dn;
+ Slapi_DN *sdn = NULL;
+ int start_initialize = 0, stop_initialize = 0, cancel_initialize = 0;
+ int update_the_schedule = 0; /* do we need to update the repl sched? */
+ Repl_Agmt *agmt = NULL;
+ LDAPMod **mods;
+ char buff [BUFSIZ];
+ char *errortext = returntext ? returntext : buff;
+ int rc = SLAPI_DSE_CALLBACK_OK;
+ Slapi_Operation *op;
+ void *identity;
+
+ *returncode = LDAP_SUCCESS;
+
+ /* just let internal operations originated from replication plugin to go through */
+ slapi_pblock_get (pb, SLAPI_OPERATION, &op);
+ slapi_pblock_get (pb, SLAPI_PLUGIN_IDENTITY, &identity);
+
+ if (operation_is_flag_set(op, OP_FLAG_INTERNAL) &&
+ (identity == repl_get_plugin_identity (PLUGIN_MULTIMASTER_REPLICATION)))
+ {
+ goto done;
+ }
+
+ slapi_pblock_get(pb, SLAPI_TARGET_DN, &dn);
+ sdn= slapi_sdn_new_dn_byref(dn);
+ agmt = agmtlist_get_by_agmt_name(sdn);
+ if (NULL == agmt)
+ {
+ slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name, "agmtlist_modify_callback: received "
+ "a modification for unknown replication agreement \"%s\"\n", dn);
+ goto done;
+ }
+
+ slapi_pblock_get(pb, SLAPI_MODIFY_MODS, &mods);
+ for (i = 0; NULL != mods && NULL != mods[i]; i++)
+ {
+ if (slapi_attr_types_equivalent(mods[i]->mod_type, type_nsds5ReplicaInitialize))
+ {
+ /* we don't allow delete attribute operations unless it was issued by
+ the replication plugin - handled above */
+ if (mods[i]->mod_op & LDAP_MOD_DELETE)
+ {
+ slapi_log_error(SLAPI_LOG_REPL, repl_plugin_name, "agmtlist_modify_callback: "
+ "deletion of %s attribute is not allowed\n", type_nsds5ReplicaInitialize);
+ *returncode = LDAP_UNWILLING_TO_PERFORM;
+ rc = SLAPI_DSE_CALLBACK_ERROR;
+ break;
+ }
+ else
+ {
+ char *val;
+
+ if (mods[i]->mod_bvalues && mods[i]->mod_bvalues[0])
+ val = slapi_berval_get_string_copy (mods[i]->mod_bvalues[0]);
+ else
+ {
+ slapi_log_error(SLAPI_LOG_REPL, repl_plugin_name, "agmtlist_modify_callback: "
+ "no value provided for %s attribute\n", type_nsds5ReplicaInitialize);
+ *returncode = LDAP_UNWILLING_TO_PERFORM;
+ rc = SLAPI_DSE_CALLBACK_ERROR;
+ break;
+ }
+
+ /* Start replica initialization */
+ if (val == NULL)
+ {
+ sprintf (errortext, "No value supplied for attr (%s)", mods[i]->mod_type);
+ slapi_log_error(SLAPI_LOG_REPL, repl_plugin_name, "agmtlist_modify_callback: %s\n",
+ errortext);
+ *returncode = LDAP_UNWILLING_TO_PERFORM;
+ rc = SLAPI_DSE_CALLBACK_ERROR;
+ break;
+ }
+
+ if (strcasecmp (val, "start") == 0)
+ {
+ start_initialize = 1;
+ }
+ else if (strcasecmp (val, "stop") == 0)
+ {
+ stop_initialize = 1;
+ }
+ else if (strcasecmp (val, "cancel") == 0)
+ {
+ cancel_initialize = 1;
+ }
+ else
+ {
+ sprintf (errortext, "Invalid value (%s) value supplied for attr (%s)",
+ val, mods[i]->mod_type);
+ slapi_log_error(SLAPI_LOG_REPL, repl_plugin_name, "agmtlist_modify_callback: %s\n",
+ errortext);
+ }
+
+ slapi_ch_free ((void**)&val);
+ }
+ }
+ else if (slapi_attr_types_equivalent(mods[i]->mod_type,
+ type_nsds5ReplicaUpdateSchedule))
+ {
+ /*
+ * Request to update the replication schedule. Set a flag so
+ * we know to update the schedule later.
+ */
+ update_the_schedule = 1;
+ }
+ else if (slapi_attr_types_equivalent(mods[i]->mod_type,
+ type_nsds5ReplicaCredentials))
+ {
+ /* New replica credentials */
+ if (agmt_set_credentials_from_entry(agmt, e) != 0)
+ {
+ slapi_log_error(SLAPI_LOG_REPL, repl_plugin_name, "agmtlist_modify_callback: "
+ "failed to update credentials for agreement %s\n",
+ agmt_get_long_name(agmt));
+ *returncode = LDAP_OPERATIONS_ERROR;
+ rc = SLAPI_DSE_CALLBACK_ERROR;
+ }
+ }
+ else if (slapi_attr_types_equivalent(mods[i]->mod_type,
+ type_nsds5ReplicaTimeout))
+ {
+ /* New replica timeout */
+ if (agmt_set_timeout_from_entry(agmt, e) != 0)
+ {
+ slapi_log_error(SLAPI_LOG_REPL, repl_plugin_name, "agmtlist_modify_callback: "
+ "failed to update timeout for agreement %s\n",
+ agmt_get_long_name(agmt));
+ *returncode = LDAP_OPERATIONS_ERROR;
+ rc = SLAPI_DSE_CALLBACK_ERROR;
+ }
+ }
+ else if (slapi_attr_types_equivalent(mods[i]->mod_type,
+ type_nsds5ReplicaBusyWaitTime))
+ {
+ /* New replica busywaittime */
+ if (agmt_set_busywaittime_from_entry(agmt, e) != 0)
+ {
+ slapi_log_error(SLAPI_LOG_REPL, repl_plugin_name, "agmtlist_modify_callback: "
+ "failed to update busy wait time for agreement %s\n",
+ agmt_get_long_name(agmt));
+ *returncode = LDAP_OPERATIONS_ERROR;
+ rc = SLAPI_DSE_CALLBACK_ERROR;
+ }
+ }
+ else if (slapi_attr_types_equivalent(mods[i]->mod_type,
+ type_nsds5ReplicaSessionPauseTime))
+ {
+ /* New replica pausetime */
+ if (agmt_set_pausetime_from_entry(agmt, e) != 0)
+ {
+ slapi_log_error(SLAPI_LOG_REPL, repl_plugin_name, "agmtlist_modify_callback: "
+ "failed to update session pause time for agreement %s\n",
+ agmt_get_long_name(agmt));
+ *returncode = LDAP_OPERATIONS_ERROR;
+ rc = SLAPI_DSE_CALLBACK_ERROR;
+ }
+ }
+ else if (slapi_attr_types_equivalent(mods[i]->mod_type,
+ type_nsds5ReplicaBindDN))
+ {
+ /* New replica Bind DN */
+ if (agmt_set_binddn_from_entry(agmt, e) != 0)
+ {
+ slapi_log_error(SLAPI_LOG_REPL, repl_plugin_name, "agmtlist_modify_callback: "
+ "failed to update bind DN for agreement %s\n",
+ agmt_get_long_name(agmt));
+ *returncode = LDAP_OPERATIONS_ERROR;
+ rc = SLAPI_DSE_CALLBACK_ERROR;
+ }
+ }
+ else if (slapi_attr_types_equivalent(mods[i]->mod_type,
+ type_nsds5TransportInfo))
+ {
+ /* New Transport info */
+ if (agmt_set_transportinfo_from_entry(agmt, e) != 0)
+ {
+ slapi_log_error(SLAPI_LOG_REPL, repl_plugin_name, "agmtlist_modify_callback: "
+ "failed to update transport info for agreement %s\n",
+ agmt_get_long_name(agmt));
+ *returncode = LDAP_OPERATIONS_ERROR;
+ rc = SLAPI_DSE_CALLBACK_ERROR;
+ }
+ }
+ else if (slapi_attr_types_equivalent(mods[i]->mod_type,
+ type_nsds5ReplicaBindMethod))
+ {
+ /* New replica bind method */
+ if (agmt_set_bind_method_from_entry(agmt, e) != 0)
+ {
+ slapi_log_error(SLAPI_LOG_REPL, repl_plugin_name, "agmtlist_modify_callback: "
+ "failed to update bind method for agreement %s\n",
+ agmt_get_long_name(agmt));
+ *returncode = LDAP_OPERATIONS_ERROR;
+ rc = SLAPI_DSE_CALLBACK_ERROR;
+ }
+ }
+ else if (slapi_attr_types_equivalent(mods[i]->mod_type,
+ "nsds5debugreplicatimeout"))
+ {
+ char *val = slapi_entry_attr_get_charptr(e, "nsds5debugreplicatimeout");
+ repl5_set_debug_timeout(val);
+ slapi_ch_free_string(&val);
+ }
+ else if (strcasecmp (mods[i]->mod_type, "modifytimestamp") == 0 ||
+ strcasecmp (mods[i]->mod_type, "modifiersname") == 0 ||
+ strcasecmp (mods[i]->mod_type, "description") == 0)
+ {
+ /* ignore modifier's name and timestamp attributes and the description. */
+ continue;
+ }
+ else
+ {
+ slapi_log_error(SLAPI_LOG_REPL, repl_plugin_name, "agmtlist_modify_callback: "
+ "modification of %s attribute is not allowed\n", mods[i]->mod_type);
+ *returncode = LDAP_UNWILLING_TO_PERFORM;
+ rc = SLAPI_DSE_CALLBACK_ERROR;
+ break;
+ }
+ }
+
+ if (stop_initialize)
+ {
+ agmt_stop (agmt);
+ }
+ else if (start_initialize)
+ {
+ if (agmt_initialize_replica(agmt) != 0) {
+ /* The suffix is disabled */
+ agmt_set_last_init_status(agmt, 0, NSDS50_REPL_DISABLED, NULL);
+ }
+ }
+ else if (cancel_initialize)
+ {
+ agmt_replica_init_done(agmt);
+ }
+
+ if (update_the_schedule)
+ {
+ if (agmt_set_schedule_from_entry(agmt, e) != 0)
+ {
+ slapi_log_error(SLAPI_LOG_REPL, repl_plugin_name, "agmtlist_modify_callback: "
+ "failed to update replication schedule for agreement %s\n",
+ agmt_get_long_name(agmt));
+ *returncode = LDAP_OPERATIONS_ERROR;
+ rc = SLAPI_DSE_CALLBACK_ERROR;
+ }
+ }
+
+done:
+ if (NULL != agmt)
+ {
+ agmtlist_release_agmt(agmt);
+ }
+
+ if (sdn)
+ slapi_sdn_free(&sdn);
+ return rc;
+}
+
+static int
+agmtlist_delete_callback(Slapi_PBlock *pb, Slapi_Entry *e, Slapi_Entry *entryAfter,
+ int *returncode, char *returntext, void *arg)
+{
+ Repl_Agmt *ra;
+ Object *ro;
+
+ slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name, "agmt_delete: begin\n");
+ ro = objset_find(agmt_set, agmt_dn_cmp, (const void *)slapi_entry_get_sdn_const(e));
+ ra = (NULL == ro) ? NULL : (Repl_Agmt *)object_get_data(ro);
+ if (NULL == ra)
+ {
+ slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name, "agmtlist_delete: "
+ "Tried to delete replication agreement \"%s\", but no such "
+ "agreement was configured.\n", slapi_sdn_get_dn(slapi_entry_get_sdn_const(e)));
+ }
+ else
+ {
+ agmt_stop(ra);
+ object_release(ro); /* Release ref acquired in objset_find */
+ objset_remove_obj(agmt_set, ro); /* Releases a reference (should be final reference */
+ }
+ *returncode = LDAP_SUCCESS;
+ return SLAPI_DSE_CALLBACK_OK;
+}
+
+static int
+agmtlist_rename_callback(Slapi_PBlock *pb, Slapi_Entry *entryBefore, Slapi_Entry *e,
+ int *returncode, char *returntext, void *arg)
+{
+ slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name, "agmt_rename: begin\n");
+
+ *returncode = LDAP_SUCCESS;
+ return SLAPI_DSE_CALLBACK_OK;
+}
+
+
+static int
+handle_agmt_search(Slapi_Entry *e, void *callback_data)
+{
+ int *agmtcount = (int *)callback_data;
+ int rc;
+
+ slapi_log_error(SLAPI_LOG_REPL, repl_plugin_name,
+ "Found replication agreement named \"%s\".\n",
+ slapi_sdn_get_dn(slapi_entry_get_sdn(e)));
+ rc = add_new_agreement(e);
+ if (0 == rc)
+ {
+ (*agmtcount)++;
+ }
+ else
+ {
+ slapi_log_error(SLAPI_LOG_REPL, repl_plugin_name, "The replication "
+ "agreement named \"%s\" could not be correctly parsed. No "
+ "replication will occur with this replica.\n",
+ slapi_sdn_get_dn(slapi_entry_get_sdn(e)));
+ }
+
+ return rc;
+}
+
+
+static void
+agmtlist_objset_destructor(void **o)
+{
+ /* XXXggood Nothing to do, I think. */
+}
+
+
+int
+agmtlist_config_init()
+{
+ Slapi_PBlock *pb;
+ int agmtcount = 0;
+
+ agmt_set = objset_new(agmtlist_objset_destructor);
+
+ /* Register callbacks so we're informed about updates */
+ slapi_config_register_callback(SLAPI_OPERATION_ADD, DSE_FLAG_PREOP, AGMT_CONFIG_BASE,
+ LDAP_SCOPE_SUBTREE, CONFIG_FILTER, agmtlist_add_callback, NULL);
+ slapi_config_register_callback(SLAPI_OPERATION_MODIFY, DSE_FLAG_PREOP, AGMT_CONFIG_BASE,
+ LDAP_SCOPE_SUBTREE, CONFIG_FILTER, agmtlist_modify_callback, NULL);
+ slapi_config_register_callback(SLAPI_OPERATION_DELETE, DSE_FLAG_PREOP, AGMT_CONFIG_BASE,
+ LDAP_SCOPE_SUBTREE, CONFIG_FILTER, agmtlist_delete_callback, NULL);
+ slapi_config_register_callback(SLAPI_OPERATION_MODRDN, DSE_FLAG_PREOP, AGMT_CONFIG_BASE,
+ LDAP_SCOPE_SUBTREE, CONFIG_FILTER, agmtlist_rename_callback, NULL);
+
+ /* Search the DIT and find all the replication agreements */
+ pb = slapi_pblock_new();
+ slapi_search_internal_set_pb(pb, AGMT_CONFIG_BASE, LDAP_SCOPE_SUBTREE,
+ CONFIG_FILTER, NULL /* attrs */, 0 /* attrsonly */,
+ NULL, /* controls */ NULL /* uniqueid */,
+ repl_get_plugin_identity(PLUGIN_MULTIMASTER_REPLICATION), 0 /* actions */);
+ slapi_search_internal_callback_pb(pb,
+ (void *)&agmtcount /* callback data */,
+ NULL /* result_callback */,
+ handle_agmt_search /* search entry cb */,
+ NULL /* referral callback */);
+ slapi_pblock_destroy(pb);
+
+ slapi_log_error(SLAPI_LOG_REPL, repl_plugin_name, "agmtlist_config_init: found %d replication agreements in DIT\n", agmtcount);
+
+ return 0;
+}
+
+
+
+void
+agmtlist_shutdown()
+{
+ Repl_Agmt *ra;
+ Object *ro;
+ Object *next_ro;
+
+ ro = objset_first_obj(agmt_set);
+ while (NULL != ro)
+ {
+ next_ro = objset_next_obj(agmt_set, ro);
+ ra = (Repl_Agmt *)object_get_data(ro);
+ agmt_stop(ra);
+ agmt_update_consumer_ruv (ra);
+ objset_remove_obj(agmt_set, ro);
+ ro = next_ro;
+ }
+ objset_delete(&agmt_set);
+ agmt_set = NULL;
+}
+
+
+
+/*
+ * Notify each replication agreement about an update.
+ */
+void
+agmtlist_notify_all(Slapi_PBlock *pb)
+{
+ Repl_Agmt *ra;
+ Object *ro;
+
+ if (NULL != agmt_set)
+ {
+ ro = objset_first_obj(agmt_set);
+ while (NULL != ro)
+ {
+ ra = (Repl_Agmt *)object_get_data(ro);
+ agmt_notify_change(ra, pb);
+ ro = objset_next_obj(agmt_set, ro);
+ }
+ }
+}
+
+Object* agmtlist_get_first_agreement_for_replica (Replica *r)
+{
+ return agmtlist_get_next_agreement_for_replica (r, NULL) ;
+}
+
+Object* agmtlist_get_next_agreement_for_replica (Replica *r, Object *prev)
+{
+ const Slapi_DN *replica_root;
+ Slapi_DN *agmt_root;
+ Object *obj;
+ Repl_Agmt *agmt;
+
+ if (r == NULL)
+ {
+ /* ONREPL - log error */
+ return NULL;
+ }
+
+ replica_root = replica_get_root(r);
+
+ if (prev)
+ obj = objset_next_obj(agmt_set, prev);
+ else
+ obj = objset_first_obj(agmt_set);
+
+ while (obj)
+ {
+ agmt = (Repl_Agmt*)object_get_data (obj);
+ PR_ASSERT (agmt);
+
+ agmt_root = agmt_get_replarea(agmt);
+ PR_ASSERT (agmt_root);
+
+ if (slapi_sdn_compare (replica_root, agmt_root) == 0)
+ {
+ slapi_sdn_free (&agmt_root);
+ return obj;
+ }
+
+ slapi_sdn_free (&agmt_root);
+ obj = objset_next_obj(agmt_set, obj);
+ }
+
+ return NULL;
+}
diff --git a/ldap/servers/plugins/replication/repl5_backoff.c b/ldap/servers/plugins/replication/repl5_backoff.c
new file mode 100644
index 00000000..d0a90878
--- /dev/null
+++ b/ldap/servers/plugins/replication/repl5_backoff.c
@@ -0,0 +1,232 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+/* repl5_backoff.c */
+/*
+
+ The backoff object implements a backoff timer. The timer can operate
+ with a fixed interval, an expontially increasing interval, or a
+ random interval.
+
+ The caller creates a new backoff timer, specifying the backoff behavior
+ desired (fixed, exponential, or random), the initial backoff value,
+ and the maximum backoff interval. This does not start the timer - the
+ backoff_reset() function must be used to actually start the timer.
+
+ The backoff_reset() function takes an optional function that
+ will be called when the backoff time has expired, and a void *
+ that can be used to pass arguments into the callback function.
+
+ When the time expires, the callback function will be called. If no
+ callback function has been provided, the timer simply expires.
+ A timer does not recompute the next interval and begin timing until
+ the backoff_step() function is called. Therefore, callers that
+ do not install a callback function may use the timer by polling.
+ When a callback function is provided, the timer is typically reset
+ inside the callback function.
+
+*/
+
+#include "repl5.h"
+
+
+typedef struct backoff_timer {
+ int type;
+ int running;
+ slapi_eq_fn_t callback;
+ void *callback_arg;
+ time_t initial_interval;
+ time_t next_interval;
+ time_t max_interval;
+ time_t last_fire_time;
+ Slapi_Eq_Context pending_event;
+ PRLock *lock;
+
+} backoff_timer;
+
+/* Forward declarations */
+static PRIntervalTime random_interval_in_range(time_t lower_bound, time_t upper_bound);
+
+
+/*
+ Create a new backoff timer. The timer is initialized, but is not
+ started.
+ */
+Backoff_Timer *
+backoff_new(int timer_type, int initial_interval, int max_interval)
+{
+ Backoff_Timer *bt;
+
+ bt = (Backoff_Timer *)slapi_ch_calloc(1, sizeof(struct backoff_timer));
+ bt->type = timer_type;
+ bt->initial_interval = initial_interval;
+ bt->next_interval = bt->initial_interval;
+ bt->max_interval = max_interval;
+ bt->running = 0;
+ if ((bt->lock = PR_NewLock()) == NULL)
+ {
+ slapi_ch_free((void **)&bt);
+ }
+ return bt;
+}
+
+
+/*
+ * Reset and start the timer. Returns the time (as a time_t) when the
+ * time will next expire.
+ */
+time_t
+backoff_reset(Backoff_Timer *bt, slapi_eq_fn_t callback, void *callback_data)
+{
+ time_t return_value = 0UL;
+
+ PR_ASSERT(NULL != bt);
+ PR_ASSERT(NULL != callback);
+
+ PR_Lock(bt->lock);
+ bt->running = 1;
+ bt->callback = callback;
+ bt->callback_arg = callback_data;
+ /* Cancel any pending events in the event queue */
+ if (NULL != bt->pending_event)
+ {
+ slapi_eq_cancel(bt->pending_event);
+ bt->pending_event = NULL;
+ }
+ /* Compute the first fire time */
+ if (BACKOFF_RANDOM == bt->type)
+ {
+ bt->next_interval = random_interval_in_range(bt->initial_interval,
+ bt->max_interval);
+ }
+ else
+ {
+ bt->next_interval = bt->initial_interval;
+ }
+ /* Schedule the callback */
+ time(&bt->last_fire_time);
+ return_value = bt->last_fire_time + bt->next_interval;
+ bt->pending_event = slapi_eq_once(bt->callback, bt->callback_arg,
+ return_value);
+ PR_Unlock(bt->lock);
+ return return_value;
+}
+
+
+/*
+ Step the timer - compute the new backoff interval and start
+ counting. Note that the next expiration time is based on the
+ last timer expiration time, *not* the current time.
+
+ Returns the time (as a time_t) when the timer will next expire.
+ */
+time_t
+backoff_step(Backoff_Timer *bt)
+{
+ time_t return_value = 0UL;
+
+ PR_ASSERT(NULL != bt);
+
+ /* If the timer has never been reset, then return 0 */
+ PR_Lock(bt->lock);
+ if (bt->running)
+ {
+ time_t previous_interval = bt->next_interval;
+ switch (bt->type) {
+ case BACKOFF_FIXED:
+ /* Interval stays the same */
+ break;
+ case BACKOFF_EXPONENTIAL:
+ /* Interval doubles, up to a maximum */
+ if (bt->next_interval < bt->max_interval)
+ {
+ bt->next_interval *= 2;
+ if (bt->next_interval > bt->max_interval)
+ {
+ bt->next_interval = bt->max_interval;
+ }
+ }
+ break;
+ case BACKOFF_RANDOM:
+ /* Compute the new random interval time */
+ bt->next_interval = random_interval_in_range(bt->initial_interval,
+ bt->max_interval);
+ break;
+ }
+ /* Schedule the callback, if any */
+ bt->last_fire_time += previous_interval;
+ return_value = bt->last_fire_time + bt->next_interval;
+ bt->pending_event = slapi_eq_once(bt->callback, bt->callback_arg,
+ return_value);
+ }
+ PR_Unlock(bt->lock);
+ return return_value;
+}
+
+
+/*
+ * Return 1 if the backoff timer has expired, 0 otherwise.
+ */
+int
+backoff_expired(Backoff_Timer *bt, int margin)
+{
+ int return_value = 0;
+
+ PR_ASSERT(NULL != bt);
+ PR_Lock(bt->lock);
+ return_value = (current_time() >= (bt->last_fire_time + bt->next_interval + margin));
+ PR_Unlock(bt->lock);
+ return return_value;
+}
+
+
+/*
+ Destroy and deallocate a timer object
+ */
+void
+backoff_delete(Backoff_Timer **btp)
+{
+ Backoff_Timer *bt;
+
+ PR_ASSERT(NULL != btp && NULL != *btp);
+ bt = *btp;
+ PR_Lock(bt->lock);
+ /* Cancel any pending events in the event queue */
+ if (NULL != bt->pending_event)
+ {
+ slapi_eq_cancel(bt->pending_event);
+ }
+ PR_Unlock(bt->lock);
+ PR_DestroyLock(bt->lock);
+ slapi_ch_free((void **)btp);
+}
+
+
+/*
+ * Return the next fire time for the timer.
+ */
+time_t
+backoff_get_next_fire_time(Backoff_Timer *bt)
+{
+ time_t return_value;
+
+ PR_ASSERT(NULL != bt);
+ PR_Lock(bt->lock);
+ return_value = bt->last_fire_time + bt->next_interval;
+ PR_Unlock(bt->lock);
+ return return_value;
+}
+
+static PRIntervalTime
+random_interval_in_range(time_t lower_bound, time_t upper_bound)
+{
+ /*
+ * slapi_rand() provides some entropy from two or three system timer
+ * calls (depending on the platform) down in NSS. If more entropy is
+ * required, slapi_rand_r(unsigned int *seed) can be called instead.
+ */
+ return(lower_bound + (slapi_rand() % (upper_bound - lower_bound)));
+}
+
diff --git a/ldap/servers/plugins/replication/repl5_connection.c b/ldap/servers/plugins/replication/repl5_connection.c
new file mode 100644
index 00000000..a50c163a
--- /dev/null
+++ b/ldap/servers/plugins/replication/repl5_connection.c
@@ -0,0 +1,1493 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+
+/* repl5_connection.c */
+/*
+
+ The connection object manages a connection to a single replication
+ consumer.
+
+XXXggood what to do on timeout? If we close connection, then we won't leave a
+replica locked. Seems like right thing to do.
+*/
+
+#include "repl5.h"
+#include "ldappr.h"
+
+typedef struct repl_connection
+{
+ char *hostname;
+ int port;
+ char *binddn;
+ int bindmethod;
+ int state;
+ int last_operation;
+ int last_ldap_error;
+ const char *status;
+ char *last_ldap_errmsg;
+ PRUint32 transport_flags;
+ LDAP *ld;
+ int supports_ldapv3; /* 1 if does, 0 if doesn't, -1 if not determined */
+ int supports_ds50_repl; /* 1 if does, 0 if doesn't, -1 if not determined */
+ int supports_ds40_repl; /* 1 if does, 0 if doesn't, -1 if not determined */
+ int linger_time; /* time in seconds to leave an idle connection open */
+ PRBool linger_active;
+ Slapi_Eq_Context *linger_event;
+ PRBool delete_after_linger;
+ int refcnt;
+ const Repl_Agmt *agmt;
+ PRLock *lock;
+ struct timeval timeout;
+ int flag_agmt_changed;
+ char *plain;
+} repl_connection;
+
+/* #define DEFAULT_LINGER_TIME (5 * 60) */ /* 5 minutes */
+#define DEFAULT_LINGER_TIME (60)
+
+/* Controls we add on every outbound operation */
+
+static LDAPControl manageDSAITControl = {LDAP_CONTROL_MANAGEDSAIT, {0, ""}, '\0'};
+static int attribute_string_value_present(LDAP *ld, LDAPMessage *entry,
+ const char *type, const char *value);
+static int bind_and_check_pwp(Repl_Connection *conn, char * binddn, char *password);
+static int do_simple_bind (Repl_Connection *conn, LDAP *ld, char * binddn, char *password);
+
+static int s_debug_timeout = 0;
+static int s_debug_level = 0;
+static Slapi_Eq_Context repl5_start_debug_timeout(int *setlevel);
+static void repl5_stop_debug_timeout(Slapi_Eq_Context eqctx, int *setlevel);
+static void repl5_debug_timeout_callback(time_t when, void *arg);
+#ifndef DSE_RETURNTEXT_SIZE
+#define SLAPI_DSE_RETURNTEXT_SIZE 512
+#endif
+
+#define STATE_CONNECTED 600
+#define STATE_DISCONNECTED 601
+
+#define STATUS_DISCONNECTED "disconnected"
+#define STATUS_CONNECTED "connected"
+#define STATUS_PROCESSING_ADD "processing add operation"
+#define STATUS_PROCESSING_DELETE "processing delete operation"
+#define STATUS_PROCESSING_MODIFY "processing modify operation"
+#define STATUS_PROCESSING_RENAME "processing rename operation"
+#define STATUS_PROCESSING_EXTENDED_OPERATION "processing extended operation"
+#define STATUS_LINGERING "lingering"
+#define STATUS_SHUTTING_DOWN "shutting down"
+#define STATUS_BINDING "connecting and binding"
+#define STATUS_SEARCHING "processing search operation"
+
+#define CONN_NO_OPERATION 0
+#define CONN_ADD 1
+#define CONN_DELETE 2
+#define CONN_MODIFY 3
+#define CONN_RENAME 4
+#define CONN_EXTENDED_OPERATION 5
+#define CONN_BIND 6
+#define CONN_INIT 7
+
+/* These are errors returned from ldap operations which should cause us to disconnect and
+ retry the connection later */
+#define IS_DISCONNECT_ERROR(rc) (rc == LDAP_SERVER_DOWN || rc == LDAP_CONNECT_ERROR || rc == LDAP_INVALID_CREDENTIALS || rc == LDAP_INAPPROPRIATE_AUTH || rc == LDAP_LOCAL_ERROR)
+
+/* Forward declarations */
+static void close_connection_internal(Repl_Connection *conn);
+
+/*
+ * Create a new conenction object. Returns a pointer to the object, or
+ * NULL if an error occurs.
+ */
+Repl_Connection *
+conn_new(Repl_Agmt *agmt)
+{
+ Repl_Connection *rpc;
+
+ rpc = (Repl_Connection *)slapi_ch_malloc(sizeof(repl_connection));
+ if ((rpc->lock = PR_NewLock()) == NULL)
+ {
+ goto loser;
+ }
+ rpc->hostname = agmt_get_hostname(agmt);
+ rpc->port = agmt_get_port(agmt);
+ rpc->binddn = agmt_get_binddn(agmt);
+ rpc->bindmethod = agmt_get_bindmethod(agmt);
+ rpc->transport_flags = agmt_get_transport_flags(agmt);
+ rpc->ld = NULL;
+ rpc->state = STATE_DISCONNECTED;
+ rpc->last_operation = CONN_NO_OPERATION;
+ rpc->last_ldap_error = LDAP_SUCCESS;
+ rpc->last_ldap_errmsg = NULL;
+ rpc->supports_ldapv3 = -1;
+ rpc->supports_ds40_repl = -1;
+ rpc->supports_ds50_repl = -1;
+ rpc->linger_active = PR_FALSE;
+ rpc->delete_after_linger = PR_FALSE;
+ rpc->linger_event = NULL;
+ rpc->linger_time = DEFAULT_LINGER_TIME;
+ rpc->status = STATUS_DISCONNECTED;
+ rpc->agmt = agmt;
+ rpc->refcnt = 1;
+ rpc->timeout.tv_sec = agmt_get_timeout(agmt);
+ rpc->timeout.tv_usec = 0;
+ rpc->flag_agmt_changed = 0;
+ rpc->plain = NULL;
+ return rpc;
+loser:
+ conn_delete(rpc);
+ return NULL;
+}
+
+
+/*
+ * Return PR_TRUE if the connection is in the connected state
+ */
+static PRBool
+conn_connected(Repl_Connection *conn)
+{
+ PRBool return_value;
+ PR_Lock(conn->lock);
+ return_value = STATE_CONNECTED == conn->state;
+ PR_Unlock(conn->lock);
+ return return_value;
+}
+
+
+/*
+ * Destroy a connection object.
+ */
+static void
+conn_delete_internal(Repl_Connection *conn)
+{
+ PR_ASSERT(NULL != conn);
+ close_connection_internal(conn);
+ /* slapi_ch_free accepts NULL pointer */
+ slapi_ch_free((void **)&conn->hostname);
+ slapi_ch_free((void **)&conn->binddn);
+ slapi_ch_free((void **)&conn->plain);
+}
+
+/*
+ * Destroy a connection. It is an error to use the connection object
+ * after conn_delete() has been called.
+ */
+void
+conn_delete(Repl_Connection *conn)
+{
+ PRBool destroy_it = PR_FALSE;
+
+ PR_ASSERT(NULL != conn);
+ PR_Lock(conn->lock);
+ if (conn->linger_active)
+ {
+ if (slapi_eq_cancel(conn->linger_event) == 1)
+ {
+ /* Event was found and cancelled. Destroy the connection object. */
+ PR_Unlock(conn->lock);
+ destroy_it = PR_TRUE;
+ }
+ else
+ {
+ /*
+ * The event wasn't found, but we think it's still active.
+ * That means an event is in the process of being fired
+ * off, so arrange for the event to destroy the object .
+ */
+ conn->delete_after_linger = PR_TRUE;
+ PR_Unlock(conn->lock);
+ }
+ }
+ if (destroy_it)
+ {
+ conn_delete_internal(conn);
+ }
+}
+
+
+/*
+ * Return the last operation type processed by the connection
+ * object, and the LDAP error encountered.
+ */
+void
+conn_get_error(Repl_Connection *conn, int *operation, int *error)
+{
+ PR_Lock(conn->lock);
+ *operation = conn->last_operation;
+ *error = conn->last_ldap_error;
+ PR_Unlock(conn->lock);
+}
+
+
+/*
+ * Common code to send an LDAPv3 operation and collect the result.
+ * Return values:
+ * CONN_OPERATION_SUCCESS - the operation succeeded
+ * CONN_OPERATION_FAILED - the operation was sent to the consumer
+ * and failed. Use conn_get_error() to determine the LDAP error
+ * code.
+ * CONN_NOT_CONNECTED - no connection is active. The caller should
+ * use conn_connect() to connect to the replica and bind, then should
+ * reacquire the replica (if needed).
+ * CONN_BUSY - the server is busy with previous requests, must wait for a while
+ * before retrying
+ */
+static ConnResult
+perform_operation(Repl_Connection *conn, int optype, const char *dn,
+ LDAPMod **attrs, const char *newrdn, const char *newparent,
+ int deleteoldrdn, LDAPControl *update_control,
+ const char *extop_oid, struct berval *extop_payload, char **retoidp,
+ struct berval **retdatap, LDAPControl ***returned_controls)
+{
+ int rc;
+ ConnResult return_value;
+ LDAPControl *server_controls[3];
+ LDAPControl **loc_returned_controls;
+ const char *op_string = NULL;
+ const char *extra_op_string = NULL;
+
+ server_controls[0] = &manageDSAITControl;
+ server_controls[1] = update_control;
+ server_controls[2] = NULL;
+
+ if (conn_connected(conn))
+ {
+ int msgid;
+
+ conn->last_operation = optype;
+ switch (optype)
+ {
+ case CONN_ADD:
+ conn->status = STATUS_PROCESSING_ADD;
+ op_string = "add";
+ rc = ldap_add_ext(conn->ld, dn, attrs, server_controls,
+ NULL /* clientctls */, &msgid);
+ break;
+ case CONN_MODIFY:
+ conn->status = STATUS_PROCESSING_MODIFY;
+ op_string = "modify";
+ rc = ldap_modify_ext(conn->ld, dn, attrs, server_controls,
+ NULL /* clientctls */, &msgid);
+ break;
+ case CONN_DELETE:
+ conn->status = STATUS_PROCESSING_DELETE;
+ op_string = "delete";
+ rc = ldap_delete_ext(conn->ld, dn, server_controls,
+ NULL /* clientctls */, &msgid);
+ break;
+ case CONN_RENAME:
+ conn->status = STATUS_PROCESSING_RENAME;
+ op_string = "rename";
+ rc = ldap_rename(conn->ld, dn, newrdn, newparent, deleteoldrdn,
+ server_controls, NULL /* clientctls */, &msgid);
+ break;
+ case CONN_EXTENDED_OPERATION:
+ conn->status = STATUS_PROCESSING_EXTENDED_OPERATION;
+ op_string = "extended";
+ extra_op_string = extop_oid;
+ rc = ldap_extended_operation(conn->ld, extop_oid, extop_payload,
+ server_controls, NULL /* clientctls */, &msgid);
+ }
+ if (LDAP_SUCCESS == rc)
+ {
+ LDAPMessage *res = NULL;
+ int setlevel = 0;
+ Slapi_Eq_Context eqctx = repl5_start_debug_timeout(&setlevel);
+
+ rc = ldap_result(conn->ld, msgid, 1, &conn->timeout, &res);
+ repl5_stop_debug_timeout(eqctx, &setlevel);
+ if (0 == rc)
+ {
+ /* Timeout */
+ rc = ldap_get_lderrno(conn->ld, NULL, NULL);
+ conn->last_ldap_error = LDAP_TIMEOUT;
+ return_value = CONN_TIMEOUT;
+ }
+ else if (-1 == rc)
+ {
+ /* Error */
+ char *s = NULL;
+
+ rc = ldap_get_lderrno(conn->ld, NULL, &s);
+ slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name,
+ "%s: Received error %d: %s for %s operation\n",
+ agmt_get_long_name(conn->agmt),
+ rc, s ? s : "NULL",
+ op_string ? op_string : "NULL");
+ conn->last_ldap_error = rc;
+ /* some errors will require a disconnect and retry the connection
+ later */
+ if (IS_DISCONNECT_ERROR(rc))
+ {
+ conn_disconnect(conn);
+ return_value = CONN_NOT_CONNECTED;
+ }
+ else
+ {
+ conn->status = STATUS_CONNECTED;
+ return_value = CONN_OPERATION_FAILED;
+ }
+ }
+ else
+ {
+ int err;
+ char *errmsg = NULL;
+ char **referrals = NULL;
+ char *matched = NULL;
+
+ rc = ldap_parse_result(conn->ld, res, &err, &matched,
+ &errmsg, &referrals, &loc_returned_controls,
+ 0 /* Don't free the result */);
+ if (IS_DISCONNECT_ERROR(rc))
+ {
+ conn->last_ldap_error = rc;
+ conn_disconnect(conn);
+ return_value = CONN_NOT_CONNECTED;
+ }
+ else if (IS_DISCONNECT_ERROR(err))
+ {
+ conn->last_ldap_error = err;
+ conn_disconnect(conn);
+ return_value = CONN_NOT_CONNECTED;
+ }
+ /* Got a result */
+ else if (CONN_EXTENDED_OPERATION == optype)
+ {
+ if ((rc == LDAP_SUCCESS) && (err == LDAP_BUSY))
+ return_value = CONN_BUSY;
+ else {
+ if (rc == LDAP_SUCCESS) {
+ rc = ldap_parse_extended_result(conn->ld, res, retoidp,
+ retdatap, 0 /* Don't Free it */);
+ }
+ conn->last_ldap_error = rc;
+ return_value = (LDAP_SUCCESS == conn->last_ldap_error ?
+ CONN_OPERATION_SUCCESS : CONN_OPERATION_FAILED);
+ }
+ }
+ else /* regular operation, result returned */
+ {
+ if (NULL != returned_controls)
+ {
+ *returned_controls = loc_returned_controls;
+ }
+ if (LDAP_SUCCESS != rc)
+ {
+ conn->last_ldap_error = rc;
+ }
+ else
+ {
+ conn->last_ldap_error = err;
+ }
+ return_value = LDAP_SUCCESS == conn->last_ldap_error ? CONN_OPERATION_SUCCESS : CONN_OPERATION_FAILED;
+ }
+ slapi_log_error(SLAPI_LOG_REPL, repl_plugin_name,
+ "%s: Received result code %d for %s operation %s%s\n",
+ agmt_get_long_name(conn->agmt),
+ conn->last_ldap_error,
+ op_string == NULL ? "" : op_string,
+ extra_op_string == NULL ? "" : extra_op_string,
+ extra_op_string == NULL ? "" : " ");
+ /*
+ * XXXggood do I need to free matched, referrals,
+ * anything else? Or can I pass NULL for the args
+ * I'm not interested in?
+ */
+ /* Good question! Meanwhile, as RTM aproaches, let's free them... */
+ slapi_ch_free((void **) &errmsg);
+ slapi_ch_free((void **) &matched);
+ charray_free(referrals);
+ conn->status = STATUS_CONNECTED;
+ }
+ if (res) ldap_msgfree(res);
+ }
+ else
+ {
+ slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name,
+ "%s: Failed to send %s operation: LDAP error %d (%s)\n",
+ agmt_get_long_name(conn->agmt),
+ op_string ? op_string : "NULL", rc, ldap_err2string(rc));
+ conn->last_ldap_error = rc;
+ if (IS_DISCONNECT_ERROR(rc))
+ {
+ conn_disconnect(conn);
+ return_value = CONN_NOT_CONNECTED;
+ }
+ else
+ {
+ conn->status = STATUS_CONNECTED;
+ return_value = CONN_OPERATION_FAILED;
+ }
+ }
+ }
+ else
+ {
+ /* conn->last_ldap_error has been set to a more specific value
+ * in conn_connected()
+ * conn->last_ldap_error = LDAP_SERVER_DOWN;
+ */
+ return_value = CONN_NOT_CONNECTED;
+ }
+ return return_value;
+}
+
+/*
+ * Send an LDAP add operation.
+ */
+ConnResult
+conn_send_add(Repl_Connection *conn, const char *dn, LDAPMod **attrs,
+ LDAPControl *update_control, LDAPControl ***returned_controls)
+{
+ return perform_operation(conn, CONN_ADD, dn, attrs, NULL /* newrdn */,
+ NULL /* newparent */, 0 /* deleteoldrdn */, update_control,
+ NULL /* extop OID */, NULL /* extop payload */, NULL /* retoidp */,
+ NULL /* retdatap */, returned_controls);
+}
+
+
+/*
+ * Send an LDAP delete operation.
+ */
+ConnResult
+conn_send_delete(Repl_Connection *conn, const char *dn,
+ LDAPControl *update_control, LDAPControl ***returned_controls)
+{
+ return perform_operation(conn, CONN_DELETE, dn, NULL /* attrs */,
+ NULL /* newrdn */, NULL /* newparent */, 0 /* deleteoldrdn */,
+ update_control, NULL /* extop OID */, NULL /* extop payload */,
+ NULL /* retoidp */, NULL /* retdatap */, returned_controls);
+}
+
+
+/*
+ * Send an LDAP modify operation.
+ */
+ConnResult
+conn_send_modify(Repl_Connection *conn, const char *dn, LDAPMod **mods,
+ LDAPControl *update_control, LDAPControl ***returned_controls)
+{
+ return perform_operation(conn, CONN_MODIFY, dn, mods, NULL /* newrdn */,
+ NULL /* newparent */, 0 /* deleteoldrdn */, update_control,
+ NULL /* extop OID */, NULL /* extop payload */, NULL /* retoidp */,
+ NULL /* retdatap */, returned_controls);
+}
+
+/*
+ * Send an LDAP moddn operation.
+ */
+ConnResult
+conn_send_rename(Repl_Connection *conn, const char *dn,
+ const char *newrdn, const char *newparent, int deleteoldrdn,
+ LDAPControl *update_control, LDAPControl ***returned_controls)
+{
+ return perform_operation(conn, CONN_RENAME, dn, NULL /* attrs */,
+ newrdn, newparent, deleteoldrdn, update_control,
+ NULL /* extop OID */, NULL /* extop payload */, NULL /* retoidp */,
+ NULL /* retdatap */, returned_controls);
+}
+
+
+/*
+ * Send an LDAP extended operation.
+ */
+ConnResult
+conn_send_extended_operation(Repl_Connection *conn, const char *extop_oid,
+ struct berval *payload, char **retoidp, struct berval **retdatap,
+ LDAPControl *update_control, LDAPControl ***returned_controls)
+{
+ return perform_operation(conn, CONN_EXTENDED_OPERATION, NULL /* dn */, NULL /* attrs */,
+ NULL /* newrdn */, NULL /* newparent */, 0 /* deleteoldrdn */,
+ update_control, extop_oid, payload, retoidp, retdatap,
+ returned_controls);
+}
+
+
+/*
+ * Synchronously read an entry and return a specific attribute's values.
+ * Returns CONN_OPERATION_SUCCESS if successful. Returns
+ * CONN_OPERATION_FAILED if the operation was sent but an LDAP error
+ * occurred (conn->last_ldap_error is set in this case), and
+ * CONN_NOT_CONNECTED if no connection was active.
+ *
+ * The caller must free the returned_bvals.
+ */
+ConnResult
+conn_read_entry_attribute(Repl_Connection *conn, const char *dn,
+ char *type, struct berval ***returned_bvals)
+{
+ ConnResult return_value;
+ int ldap_rc;
+ LDAPControl *server_controls[2];
+ LDAPMessage *res = NULL;
+ char *attrs[2];
+
+ PR_ASSERT(NULL != type);
+ if (conn_connected(conn))
+ {
+ server_controls[0] = &manageDSAITControl;
+ server_controls[1] = NULL;
+ attrs[0] = type;
+ attrs[1] = NULL;
+ ldap_rc = ldap_search_ext_s(conn->ld, dn, LDAP_SCOPE_BASE,
+ "(objectclass=*)", attrs, 0 /* attrsonly */,
+ server_controls, NULL /* client controls */,
+ &conn->timeout, 0 /* sizelimit */, &res);
+ if (LDAP_SUCCESS == ldap_rc)
+ {
+ LDAPMessage *entry = ldap_first_entry(conn->ld, res);
+ if (NULL != entry)
+ {
+ *returned_bvals = ldap_get_values_len(conn->ld, entry, type);
+ }
+ return_value = CONN_OPERATION_SUCCESS;
+ }
+ else if (IS_DISCONNECT_ERROR(ldap_rc))
+ {
+ conn_disconnect(conn);
+ return_value = CONN_NOT_CONNECTED;
+ }
+ else
+ {
+ return_value = CONN_OPERATION_FAILED;
+ }
+ conn->last_ldap_error = ldap_rc;
+ if (NULL != res)
+ {
+ ldap_msgfree(res);
+ res = NULL;
+ }
+ }
+ else
+ {
+ return_value = CONN_NOT_CONNECTED;
+ }
+ return return_value;
+}
+
+
+/*
+ * Return an pointer to a string describing the connection's status.
+*/
+
+const char *
+conn_get_status(Repl_Connection *conn)
+{
+ return conn->status;
+}
+
+
+
+/*
+ * Cancel any outstanding linger timer. Should be called when
+ * a replication session is beginning.
+ */
+void
+conn_cancel_linger(Repl_Connection *conn)
+{
+ PR_ASSERT(NULL != conn);
+ PR_Lock(conn->lock);
+ if (conn->linger_active)
+ {
+ slapi_log_error(SLAPI_LOG_REPL, repl_plugin_name,
+ "%s: Cancelling linger on the connection\n",
+ agmt_get_long_name(conn->agmt));
+ conn->linger_active = PR_FALSE;
+ if (slapi_eq_cancel(conn->linger_event) == 1)
+ {
+ conn->refcnt--;
+ }
+ conn->linger_event = NULL;
+ conn->status = STATUS_CONNECTED;
+ }
+ else
+ {
+ slapi_log_error(SLAPI_LOG_REPL, repl_plugin_name,
+ "%s: No linger to cancel on the connection\n",
+ agmt_get_long_name(conn->agmt));
+ }
+ PR_Unlock(conn->lock);
+}
+
+
+/*
+ * Called when our linger timeout timer expires. This means
+ * we should check to see if perhaps the connection's become
+ * active again, in which case we do nothing. Otherwise,
+ * we close the connection.
+ */
+static void
+linger_timeout(time_t event_time, void *arg)
+{
+ PRBool delete_now;
+ Repl_Connection *conn = (Repl_Connection *)arg;
+
+ PR_ASSERT(NULL != conn);
+ slapi_log_error(SLAPI_LOG_REPL, repl_plugin_name,
+ "%s: Linger timeout has expired on the connection\n",
+ agmt_get_long_name(conn->agmt));
+ PR_Lock(conn->lock);
+ if (conn->linger_active)
+ {
+ conn->linger_active = PR_FALSE;
+ conn->linger_event = NULL;
+ close_connection_internal(conn);
+ }
+ delete_now = conn->delete_after_linger;
+ PR_Unlock(conn->lock);
+ if (delete_now)
+ {
+ conn_delete_internal(conn);
+ }
+}
+
+
+/*
+ * Indicate that a session is ending. The linger timer starts when
+ * this function is called.
+ */
+void
+conn_start_linger(Repl_Connection *conn)
+{
+ time_t now;
+
+ PR_ASSERT(NULL != conn);
+ slapi_log_error(SLAPI_LOG_REPL, repl_plugin_name,
+ "%s: Beginning linger on the connection\n",
+ agmt_get_long_name(conn->agmt));
+ if (!conn_connected(conn))
+ {
+ slapi_log_error(SLAPI_LOG_REPL, repl_plugin_name,
+ "%s: No linger on the closed conn\n",
+ agmt_get_long_name(conn->agmt));
+ return;
+ }
+ time(&now);
+ PR_Lock(conn->lock);
+ if (conn->linger_active)
+ {
+ slapi_log_error(SLAPI_LOG_REPL, repl_plugin_name,
+ "%s: Linger already active on the connection\n",
+ agmt_get_long_name(conn->agmt));
+ }
+ else
+ {
+ conn->linger_active = PR_TRUE;
+ conn->linger_event = slapi_eq_once(linger_timeout, conn, now + conn->linger_time);
+ conn->status = STATUS_LINGERING;
+ }
+ PR_Unlock(conn->lock);
+}
+
+
+
+/*
+ * If no connection is currently active, opens a connection and binds to
+ * the remote server. If a connection is open (e.g. lingering) then
+ * this is a no-op.
+ *
+ * Returns CONN_OPERATION_SUCCESS on success, or CONN_OPERATION_FAILED
+ * on failure. Sets conn->last_ldap_error and conn->last_operation;
+ */
+ConnResult
+conn_connect(Repl_Connection *conn)
+{
+ int ldap_rc;
+ int optdata;
+ int secure = 0;
+ char* binddn = NULL;
+ struct berval *creds;
+ ConnResult return_value = CONN_OPERATION_SUCCESS;
+ int pw_ret = 1;
+
+ /** Connection already open just return SUCCESS **/
+ if(conn->state == STATE_CONNECTED) return return_value;
+
+ PR_Lock(conn->lock);
+ if (conn->flag_agmt_changed) {
+ /* So far we cannot change Hostname and Port */
+ /* slapi_ch_free((void **)&conn->hostname); */
+ /* conn->hostname = agmt_get_hostname(conn->agmt); */
+ /* conn->port = agmt_get_port(conn->agmt); */
+ slapi_ch_free((void **)&conn->binddn);
+ conn->binddn = agmt_get_binddn(conn->agmt);
+ conn->bindmethod = agmt_get_bindmethod(conn->agmt);
+ conn->transport_flags = agmt_get_transport_flags(conn->agmt);
+ conn->timeout.tv_sec = agmt_get_timeout(conn->agmt);
+ conn->flag_agmt_changed = 0;
+ slapi_ch_free((void **)&conn->plain);
+ }
+ PR_Unlock(conn->lock);
+
+ creds = agmt_get_credentials(conn->agmt);
+
+ if (conn->plain == NULL) {
+
+ char *plain = NULL;
+
+ /* kexcoff: for reversible encryption */
+ /* We need to test the return code of pw_rever_decode in order to decide
+ * if a free for plain will be needed (pw_ret == 0) or not (pw_ret != 0) */
+ pw_ret = pw_rever_decode(creds->bv_val, &plain, type_nsds5ReplicaCredentials);
+ /* Pb occured in decryption: stop now, binding will fail */
+ if ( pw_ret == -1 )
+ {
+ slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name,
+ "%s: Decoding of the credentials failed.\n",
+ agmt_get_long_name(conn->agmt));
+
+ return_value = CONN_OPERATION_FAILED;
+ conn->last_ldap_error = LDAP_INVALID_CREDENTIALS;
+ conn->state = STATE_DISCONNECTED;
+ return (return_value);
+ } /* Else, does not mean that the plain is correct, only means the we had no internal
+ decoding pb */
+ conn->plain = slapi_ch_strdup (plain);
+ if (!pw_ret) slapi_ch_free((void**)&plain);
+ }
+
+
+ /* ugaston: if SSL has been selected in the replication agreement, SSL client
+ * initialisation should be done before ever trying to open any connection at all.
+ */
+ if (conn->transport_flags == TRANSPORT_FLAG_TLS)
+ {
+ slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name,
+ "%s: Replication secured by StartTLS not currently supported\n",
+ agmt_get_long_name(conn->agmt));
+
+ return_value = CONN_OPERATION_FAILED;
+ conn->last_ldap_error = LDAP_STRONG_AUTH_NOT_SUPPORTED;
+ conn->state = STATE_DISCONNECTED;
+ } else if(conn->transport_flags == TRANSPORT_FLAG_SSL)
+ {
+
+ /** Make sure the SSL Library has been initialized before anything else **/
+ if(slapd_security_library_is_initialized() != 1)
+ {
+ slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name,
+ "%s: SSL Not Initialized, Replication over SSL FAILED\n",
+ agmt_get_long_name(conn->agmt));
+ conn->last_ldap_error = LDAP_INAPPROPRIATE_AUTH;
+ conn->last_operation = CONN_INIT;
+ ber_bvfree(creds);
+ creds = NULL;
+ return CONN_SSL_NOT_ENABLED;
+ } else
+ {
+ secure = 1;
+ }
+ }
+
+ if (return_value == CONN_OPERATION_SUCCESS) {
+ int io_timeout_ms;
+ /* Now we initialize the LDAP Structure and set options */
+
+ slapi_log_error(SLAPI_LOG_REPL, repl_plugin_name,
+ "%s: Trying %s slapi_ldap_init\n",
+ agmt_get_long_name(conn->agmt),
+ secure ? "secure" : "non-secure");
+
+ conn->ld = slapi_ldap_init(conn->hostname, conn->port, secure, 0);
+ if (NULL == conn->ld)
+ {
+ return_value = CONN_OPERATION_FAILED;
+ conn->state = STATE_DISCONNECTED;
+ conn->last_operation = CONN_INIT;
+ conn->last_ldap_error = LDAP_LOCAL_ERROR;
+ slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name,
+ "%s: Failed to establish %sconnection to the consumer\n",
+ agmt_get_long_name(conn->agmt),
+ secure ? "secure " : "");
+ ber_bvfree(creds);
+ creds = NULL;
+ return return_value;
+ }
+
+ /* slapi_ch_strdup is OK with NULL strings */
+ binddn = slapi_ch_strdup(conn->binddn);
+
+ slapi_log_error(SLAPI_LOG_REPL, repl_plugin_name,
+ "%s: binddn = %s, passwd = %s\n",
+ agmt_get_long_name(conn->agmt),
+ binddn?binddn:"NULL", creds->bv_val?creds->bv_val:"NULL");
+
+ /* Set some options for the connection. */
+ optdata = LDAP_DEREF_NEVER; /* Don't dereference aliases */
+ ldap_set_option(conn->ld, LDAP_OPT_DEREF, &optdata);
+
+ optdata = LDAP_VERSION3; /* We need LDAP version 3 */
+ ldap_set_option(conn->ld, LDAP_OPT_PROTOCOL_VERSION, &optdata);
+
+ /* Don't chase any referrals (although we shouldn't get any) */
+ ldap_set_option(conn->ld, LDAP_OPT_REFERRALS, LDAP_OPT_OFF);
+
+ /* override the default timeout with the specified timeout */
+ io_timeout_ms = conn->timeout.tv_sec * 1000 + conn->timeout.tv_usec / 1000;
+ prldap_set_session_option(conn->ld, NULL, PRLDAP_OPT_IO_MAX_TIMEOUT,
+ io_timeout_ms);
+
+ /* We've got an ld. Now bind to the server. */
+ conn->last_operation = CONN_BIND;
+
+ }
+
+ if ( bind_and_check_pwp(conn, binddn, conn->plain) == CONN_OPERATION_FAILED )
+ {
+ conn->last_ldap_error = ldap_get_lderrno (conn->ld, NULL, NULL);
+ conn->state = STATE_DISCONNECTED;
+ return_value = CONN_OPERATION_FAILED;
+ }
+ else
+ {
+ conn->last_ldap_error = ldap_rc = LDAP_SUCCESS;
+ conn->state = STATE_CONNECTED;
+ return_value = CONN_OPERATION_SUCCESS;
+ }
+
+
+ ber_bvfree(creds);
+ creds = NULL;
+
+ slapi_ch_free((void**)&binddn);
+
+ if(return_value == CONN_OPERATION_FAILED)
+ {
+ close_connection_internal(conn);
+ } else
+ {
+ conn->last_ldap_error = ldap_rc = LDAP_SUCCESS;
+ conn->state = STATE_CONNECTED;
+ }
+
+ return return_value;
+}
+
+
+static void
+close_connection_internal(Repl_Connection *conn)
+{
+ if (NULL != conn->ld)
+ {
+ /* Since we call slapi_ldap_init,
+ we must call slapi_ldap_unbind */
+ slapi_ldap_unbind(conn->ld);
+ }
+ conn->ld = NULL;
+ conn->state = STATE_DISCONNECTED;
+ conn->status = STATUS_DISCONNECTED;
+ conn->supports_ds50_repl = -1;
+ slapi_log_error(SLAPI_LOG_REPL, repl_plugin_name,
+ "%s: Disconnected from the consumer\n", agmt_get_long_name(conn->agmt));
+}
+
+void
+conn_disconnect(Repl_Connection *conn)
+{
+ PR_ASSERT(NULL != conn);
+ PR_Lock(conn->lock);
+ close_connection_internal(conn);
+ PR_Unlock(conn->lock);
+}
+
+
+/*
+ * Determine if the remote replica supports DS 5.0 replication.
+ * Return codes:
+ * CONN_SUPPORTS_DS5_REPL - the remote replica suport DS5 replication
+ * CONN_DOES_NOT_SUPPORT_DS5_REPL - the remote replica does not
+ * support DS5 replication.
+ * CONN_OPERATION_FAILED - it could not be determined if the remote
+ * replica supports DS5 replication.
+ * CONN_NOT_CONNECTED - no connection was active.
+ */
+ConnResult
+conn_replica_supports_ds5_repl(Repl_Connection *conn)
+{
+ ConnResult return_value;
+ int ldap_rc;
+
+ if (conn_connected(conn))
+ {
+ if (conn->supports_ds50_repl == -1) {
+ LDAPMessage *res = NULL;
+ LDAPMessage *entry = NULL;
+ char *attrs[] = {"supportedcontrol", "supportedextension", NULL};
+
+ conn->status = STATUS_SEARCHING;
+ ldap_rc = ldap_search_ext_s(conn->ld, "", LDAP_SCOPE_BASE,
+ "(objectclass=*)", attrs, 0 /* attrsonly */,
+ NULL /* server controls */, NULL /* client controls */,
+ &conn->timeout, LDAP_NO_LIMIT, &res);
+ if (LDAP_SUCCESS == ldap_rc)
+ {
+ conn->supports_ds50_repl = 0;
+ entry = ldap_first_entry(conn->ld, res);
+ if (!attribute_string_value_present(conn->ld, entry, "supportedcontrol", REPL_NSDS50_UPDATE_INFO_CONTROL_OID))
+ {
+ return_value = CONN_DOES_NOT_SUPPORT_DS5_REPL;
+ }
+ else if (!attribute_string_value_present(conn->ld, entry, "supportedextension", REPL_START_NSDS50_REPLICATION_REQUEST_OID))
+ {
+ return_value = CONN_DOES_NOT_SUPPORT_DS5_REPL;
+ }
+ else if (!attribute_string_value_present(conn->ld, entry, "supportedextension", REPL_END_NSDS50_REPLICATION_REQUEST_OID))
+ {
+ return_value = CONN_DOES_NOT_SUPPORT_DS5_REPL;
+ }
+ else if (!attribute_string_value_present(conn->ld, entry, "supportedextension", REPL_NSDS50_REPLICATION_ENTRY_REQUEST_OID))
+ {
+ return_value = CONN_DOES_NOT_SUPPORT_DS5_REPL;
+ }
+ else if (!attribute_string_value_present(conn->ld, entry, "supportedextension", REPL_NSDS50_REPLICATION_RESPONSE_OID))
+ {
+ return_value = CONN_DOES_NOT_SUPPORT_DS5_REPL;
+ }
+ else
+ {
+ conn->supports_ds50_repl = 1;
+ return_value = CONN_SUPPORTS_DS5_REPL;
+ }
+ }
+ else
+ {
+ if (IS_DISCONNECT_ERROR(ldap_rc))
+ {
+ conn->last_ldap_error = ldap_rc; /* specific reason */
+ conn_disconnect(conn);
+ return_value = CONN_NOT_CONNECTED;
+ }
+ else
+ {
+ return_value = CONN_OPERATION_FAILED;
+ }
+ }
+ if (NULL != res)
+ ldap_msgfree(res);
+ }
+ else {
+ return_value = conn->supports_ds50_repl ? CONN_SUPPORTS_DS5_REPL : CONN_DOES_NOT_SUPPORT_DS5_REPL;
+ }
+ }
+ else
+ {
+ /* Not connected */
+ return_value = CONN_NOT_CONNECTED;
+ }
+ return return_value;
+}
+
+
+
+
+
+/*
+ * Return 1 if "value" is a value of attribute type "type" in entry "entry".
+ * Otherwise, return 0.
+ */
+static int
+attribute_string_value_present(LDAP *ld, LDAPMessage *entry, const char *type,
+ const char *value)
+{
+ int return_value = 0;
+
+ if (NULL != entry)
+ {
+ char *atype = NULL;
+ BerElement *ber = NULL;
+
+ atype = ldap_first_attribute(ld, entry, &ber);
+ while (NULL != atype && 0 == return_value)
+ {
+ if (strcasecmp(atype, type) == 0)
+ {
+ char **strvals = ldap_get_values(ld, entry, atype);
+ int i;
+ for (i = 0; return_value == 0 && NULL != strvals && NULL != strvals[i]; i++)
+ {
+ if (strcmp(strvals[i], value) == 0)
+ {
+ return_value = 1;
+ }
+ }
+ if (NULL != strvals)
+ {
+ ldap_value_free(strvals);
+ }
+ }
+ ldap_memfree(atype);
+ atype = ldap_next_attribute(ld, entry, ber);
+ }
+ if (NULL != ber)
+ ldap_ber_free(ber, 0);
+ /* The last atype has not been freed yet */
+ if (NULL != atype)
+ ldap_memfree(atype);
+ }
+ return return_value;
+}
+
+
+
+
+/*
+ * Read the remote server's schema entry, then read the local schema entry,
+ * and compare the nsschemacsn attribute. If the local csn is newer, or
+ * the remote csn is absent, push the schema down to the consumer.
+ * Return codes:
+ * CONN_SCHEMA_UPDATED if the schema was pushed successfully
+ * CONN_SCHEMA_NO_UPDATE_NEEDED if the schema was as new or newer than
+ * the local server's schema
+ * CONN_OPERATION_FAILED if an error occurred
+ * CONN_NOT_CONNECTED if no connection was active
+ * NOTE: Should only be called when a replication session has been
+ * established by sending a startReplication extended operation.
+ */
+ConnResult
+conn_push_schema(Repl_Connection *conn, CSN **remotecsn)
+{
+ ConnResult return_value = CONN_OPERATION_SUCCESS;
+ char *nsschemacsn = "nsschemacsn";
+ Slapi_Entry **entries = NULL;
+ Slapi_Entry *schema_entry = NULL;
+ int push_schema = 1; /* Assume we need to push for now */
+ int local_error = 0; /* No local error encountered yet */
+ int remote_error = 0; /* No remote error encountered yet */
+ CSN *localcsn = NULL;
+ Slapi_PBlock *spb = NULL;
+ char localcsnstr[CSN_STRSIZE + 1] = {0};
+
+ if (!conn_connected(conn))
+ {
+ return_value = CONN_NOT_CONNECTED;
+ slapi_log_error(SLAPI_LOG_REPL, repl_plugin_name,
+ "%s: Schema replication update failed: not connected to consumer\n",
+ agmt_get_long_name(conn->agmt));
+ }
+ else
+ {
+ localcsn = dup_global_schema_csn();
+ if (NULL == localcsn)
+ {
+ /* Local server has epoch CSN, so don't push schema */
+ return_value = CONN_SCHEMA_NO_UPDATE_NEEDED;
+ }
+ else if ( remotecsn && *remotecsn && csn_compare(localcsn, *remotecsn) <= 0 )
+ {
+ /* Local server schema is not newer than the remote one */
+ return_value = CONN_SCHEMA_NO_UPDATE_NEEDED;
+ }
+ else
+ {
+ struct berval **remote_schema_csn_bervals = NULL;
+ /* Get remote server's schema */
+ return_value = conn_read_entry_attribute(conn, "cn=schema", nsschemacsn,
+ &remote_schema_csn_bervals);
+ if (CONN_OPERATION_SUCCESS == return_value)
+ {
+ if (NULL != remote_schema_csn_bervals && NULL != remote_schema_csn_bervals[0])
+ {
+ char remotecsnstr[CSN_STRSIZE + 1] = {0};
+ memcpy(remotecsnstr, remote_schema_csn_bervals[0]->bv_val,
+ remote_schema_csn_bervals[0]->bv_len);
+ remotecsnstr[remote_schema_csn_bervals[0]->bv_len] = '\0';
+ *remotecsn = csn_new_by_string(remotecsnstr);
+ if (NULL != remotecsn && (csn_compare(localcsn, *remotecsn) <= 0))
+ {
+ return_value = CONN_SCHEMA_NO_UPDATE_NEEDED;
+ }
+ /* Need to free the remote_schema_csn_bervals */
+ ber_bvecfree(remote_schema_csn_bervals);
+ }
+ }
+ }
+ }
+ if (CONN_OPERATION_SUCCESS == return_value)
+ {
+ /* We know we need to push the schema out. */
+ LDAPMod ocmod = {0};
+ LDAPMod atmod = {0};
+ LDAPMod csnmod = {0};
+ LDAPMod *attrs[4] = {0};
+ int numvalues = 0;
+ Slapi_Attr *attr = NULL;
+ char *csnvalues[2];
+
+ ocmod.mod_type = "objectclasses";
+ ocmod.mod_op = LDAP_MOD_REPLACE | LDAP_MOD_BVALUES;
+ ocmod.mod_bvalues = NULL;
+ atmod.mod_type = "attributetypes";
+ atmod.mod_op = LDAP_MOD_REPLACE | LDAP_MOD_BVALUES;
+ atmod.mod_bvalues = NULL;
+ csnmod.mod_type = nsschemacsn;
+ csnmod.mod_op = LDAP_MOD_REPLACE;
+ csn_as_string (localcsn, PR_FALSE, localcsnstr);
+ csnvalues[0] = localcsnstr;
+ csnvalues[1] = NULL;
+ csnmod.mod_values = csnvalues;
+ attrs[0] = &ocmod;
+ attrs[1] = &atmod;
+ attrs[2] = &csnmod;
+ attrs[3] = NULL;
+
+ return_value = CONN_OPERATION_FAILED; /* assume failure */
+
+ /* Get local schema */
+ spb = slapi_search_internal("cn=schema", LDAP_SCOPE_BASE, "(objectclass=*)",
+ NULL /* controls */, NULL /* schema_csn_attrs */, 0 /* attrsonly */);
+ slapi_pblock_get(spb, SLAPI_PLUGIN_INTOP_SEARCH_ENTRIES, &entries);
+ if (NULL == entries || NULL == entries[0])
+ {
+ /* Whoops - couldn't read our own schema! */
+ slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name,
+ "%s: Error: unable to read local schema definitions.\n",
+ agmt_get_long_name(conn->agmt));
+ return_value = CONN_OPERATION_FAILED;
+ }
+ else
+ {
+ schema_entry = entries[0];
+ if (slapi_entry_attr_find(schema_entry, "objectclasses", &attr) != -1)
+ {
+ int i, ind;
+ Slapi_Value *value;
+ slapi_attr_get_numvalues(attr, &numvalues);
+ ocmod.mod_bvalues = (struct berval **)slapi_ch_malloc((numvalues + 1) *
+ sizeof(struct berval *));
+ for (i = 0, ind = slapi_attr_first_value(attr, &value);
+ ind != -1; ind = slapi_attr_next_value(attr, ind, &value), i++)
+ {
+ /* XXXggood had to cast away const below */
+ ocmod.mod_bvalues[i] = (struct berval *)slapi_value_get_berval(value);
+ }
+ ocmod.mod_bvalues[numvalues] = NULL;
+ if (slapi_entry_attr_find(schema_entry, "attributetypes", &attr) != -1)
+ {
+ ConnResult result;
+ slapi_attr_get_numvalues(attr, &numvalues);
+ atmod.mod_bvalues = (struct berval **)slapi_ch_malloc((numvalues + 1) *
+ sizeof(struct berval *));
+ for (i = 0, ind = slapi_attr_first_value(attr, &value);
+ ind != -1; ind = slapi_attr_next_value(attr, ind, &value), i++)
+ {
+ /* XXXggood had to cast away const below */
+ atmod.mod_bvalues[i] = (struct berval *)slapi_value_get_berval(value);
+ }
+ atmod.mod_bvalues[numvalues] = NULL;
+
+ result = conn_send_modify(conn, "cn=schema", attrs, NULL, NULL);
+ switch (result)
+ {
+ case CONN_OPERATION_FAILED:
+ {
+ int ldaperr = -1, optype = -1;
+ conn_get_error(conn, &optype, &ldaperr);
+ slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name,
+ "%s: Schema replication update failed: %s\n",
+ agmt_get_long_name(conn->agmt),
+ ldaperr == -1 ? "Unknown Error" : ldap_err2string(ldaperr));
+ }
+ case CONN_NOT_CONNECTED:
+ return_value = CONN_NOT_CONNECTED;
+ break;
+ case CONN_OPERATION_SUCCESS:
+ return_value = CONN_SCHEMA_UPDATED;
+ break;
+ }
+ }
+ }
+ else
+ {
+ slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name,
+ "%s: Schema replication update failed: "
+ "unable to prepare schema entry for transmission.\n",
+ agmt_get_long_name(conn->agmt));
+ }
+ }
+ /* slapi_ch_free accepts NULL pointer */
+ slapi_ch_free((void **)&ocmod.mod_bvalues);
+ slapi_ch_free((void **)&atmod.mod_bvalues);
+ }
+ if (NULL != spb)
+ {
+ slapi_free_search_results_internal(spb);
+ slapi_pblock_destroy(spb);
+ spb = NULL;
+ }
+ if (NULL != localcsn)
+ {
+ csn_free(&localcsn);
+ }
+ return return_value;
+}
+
+void
+conn_set_timeout(Repl_Connection *conn, long timeout)
+{
+ PR_ASSERT(NULL != conn);
+ PR_ASSERT(timeout >= 0);
+ PR_Lock(conn->lock);
+ conn->timeout.tv_sec = timeout;
+ PR_Unlock(conn->lock);
+}
+
+void conn_set_agmt_changed(Repl_Connection *conn)
+{
+ PR_ASSERT(NULL != conn);
+ PR_Lock(conn->lock);
+ if (NULL != conn->agmt)
+ conn->flag_agmt_changed = 1;
+ PR_Unlock(conn->lock);
+}
+
+/*
+ * Check the result of an ldap_simple_bind operation to see we it
+ * contains the expiration controls
+ * return: -1 error, not bound
+ * 0, OK bind has succeeded
+ */
+static int
+bind_and_check_pwp(Repl_Connection *conn, char * binddn, char *password)
+{
+
+ LDAPControl **ctrls = NULL;
+ LDAPMessage *res = NULL;
+ char *errmsg = NULL;
+ LDAP *ld = conn->ld;
+ int msgid;
+ int *msgidAdr = &msgid;
+ int rc;
+
+ char * optype; /* ldap_simple_bind or slapd_SSL_client_bind */
+
+ if ( conn->transport_flags == TRANSPORT_FLAG_SSL )
+ {
+ char *auth;
+ optype = "ldap_sasl_bind";
+
+ if ( conn->bindmethod == BINDMETHOD_SSL_CLIENTAUTH )
+ {
+ rc = slapd_sasl_ext_client_bind(conn->ld, &msgidAdr);
+ auth = "SSL client authentication";
+
+ if ( rc == LDAP_SUCCESS )
+ {
+ if (conn->last_ldap_error != rc)
+ {
+ conn->last_ldap_error = rc;
+ slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name,
+ "%s: Replication bind with %s resumed\n",
+ agmt_get_long_name(conn->agmt), auth);
+ }
+ }
+ else
+ {
+ /* Do not report the same error over and over again */
+ if (conn->last_ldap_error != rc)
+ {
+ conn->last_ldap_error = rc;
+ slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name,
+ "%s: Replication bind with %s failed: LDAP error %d (%s)\n",
+ agmt_get_long_name(conn->agmt), auth, rc,
+ ldap_err2string(rc));
+ }
+
+ return (CONN_OPERATION_FAILED);
+ }
+ }
+ else
+ {
+ if( ( msgid = do_simple_bind( conn, ld, binddn, password ) ) == -1 )
+ {
+ return (CONN_OPERATION_FAILED);
+ }
+ }
+ }
+ else
+ {
+ optype = "ldap_simple_bind";
+ if( ( msgid = do_simple_bind( conn, ld, binddn, password ) ) == -1 )
+ {
+ return (CONN_OPERATION_FAILED);
+ }
+ }
+
+ /* Wait for the result */
+ if ( ldap_result( ld, msgid, LDAP_MSG_ALL, NULL, &res ) == -1 )
+ {
+ slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name,
+ "%s: Received error from consumer for %s operation\n",
+ agmt_get_long_name(conn->agmt), optype);
+
+ return (CONN_OPERATION_FAILED);
+ }
+ /* Don't check ldap_result against 0 because, no timeout is specified */
+
+ /* Free res as we won't use it any longer */
+ if ( ldap_parse_result( ld, res, &rc, NULL, NULL, NULL, &ctrls, 1 /* Free res */)
+ != LDAP_SUCCESS )
+ {
+ slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name,
+ "%s: Received error from consumer for %s operation\n",
+ agmt_get_long_name(conn->agmt), optype);
+
+ return (CONN_OPERATION_FAILED);
+ }
+
+ if ( rc == LDAP_SUCCESS )
+ {
+ if ( ctrls )
+ {
+ int i;
+ for( i = 0; ctrls[ i ] != NULL; ++i )
+ {
+ if ( !(strcmp( ctrls[ i ]->ldctl_oid, LDAP_CONTROL_PWEXPIRED)) )
+ {
+ /* Bind is successfull but password has expired */
+ slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name,
+ "%s: Succesfully bound %s to consumer, "
+ "but password has expired on consumer.\n",
+ agmt_get_long_name(conn->agmt), binddn);
+ }
+ else if ( !(strcmp( ctrls[ i ]->ldctl_oid, LDAP_CONTROL_PWEXPIRING)) )
+ {
+ /* The password is expiring in n seconds */
+ if ( (ctrls[ i ]->ldctl_value.bv_val != NULL) &&
+ (ctrls[ i ]->ldctl_value.bv_len > 0) )
+ {
+ int password_expiring = atoi( ctrls[ i ]->ldctl_value.bv_val );
+ slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name,
+ "%s: Succesfully bound %s to consumer, "
+ "but password is expiring on consumer in %d seconds.\n",
+ agmt_get_long_name(conn->agmt), binddn, password_expiring);
+ }
+ }
+ }
+ ldap_controls_free( ctrls );
+ }
+
+ return (CONN_OPERATION_SUCCESS);
+ }
+ else
+ {
+ /* errmsg is a pointer directly into the ld structure - do not free */
+ rc = ldap_get_lderrno( ld, NULL, &errmsg );
+ slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name,
+ "%s: Replication bind to %s on consumer failed: %d (%s)\n",
+ agmt_get_long_name(conn->agmt), binddn, rc, errmsg);
+
+ conn->last_ldap_error = rc; /* specific error */
+ return (CONN_OPERATION_FAILED);
+ }
+}
+
+static int
+do_simple_bind (Repl_Connection *conn, LDAP *ld, char * binddn, char *password)
+{
+ int msgid;
+
+ if( ( msgid = ldap_simple_bind( ld, binddn, password ) ) == -1 )
+ {
+ char *ldaperrtext = NULL;
+ int ldaperr;
+ int prerr = PR_GetError();
+
+ ldaperr = ldap_get_lderrno( ld, NULL, &ldaperrtext );
+ /* Do not report the same error over and over again */
+ if (conn->last_ldap_error != ldaperr)
+ {
+ conn->last_ldap_error = ldaperr;
+ slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name,
+ "%s: Simple bind failed, "
+ SLAPI_COMPONENT_NAME_LDAPSDK " error %d (%s), "
+ SLAPI_COMPONENT_NAME_NSPR " error %d (%s)\n",
+ agmt_get_long_name(conn->agmt),
+ ldaperr, ldaperrtext ? ldaperrtext : ldap_err2string(ldaperr),
+ prerr, slapd_pr_strerror(prerr));
+ }
+ }
+ else if (conn->last_ldap_error != LDAP_SUCCESS)
+ {
+ conn->last_ldap_error = LDAP_SUCCESS;
+ slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name,
+ "%s: Simple bind resumed\n",
+ agmt_get_long_name(conn->agmt));
+ }
+ return msgid;
+}
+
+void
+repl5_set_debug_timeout(const char *val)
+{
+ /* val looks like this: seconds[:debuglevel] */
+ /* seconds is the number of seconds to wait until turning on the debug level */
+ /* this should be less than the ldap connection timeout (default 10 minutes) */
+ /* the optional debug level is the error log debugging level to use (default repl) */
+ if (val) {
+ const char *p = strchr(val, ':');
+ s_debug_timeout = atoi(val);
+ if (p) {
+ s_debug_level = atoi(p+1);
+ } else {
+ s_debug_level = 8192;
+ }
+ }
+}
+
+static time_t
+PRTime2time_t (PRTime tm)
+{
+ PRInt64 rt;
+
+ PR_ASSERT (tm);
+
+ LL_DIV(rt, tm, PR_USEC_PER_SEC);
+
+ return (time_t)rt;
+}
+
+static Slapi_Eq_Context
+repl5_start_debug_timeout(int *setlevel)
+{
+ Slapi_Eq_Context eqctx = 0;
+ if (s_debug_timeout && s_debug_level) {
+ time_t now = time(NULL);
+ eqctx = slapi_eq_once(repl5_debug_timeout_callback, setlevel,
+ s_debug_timeout + now);
+ }
+ return eqctx;
+}
+
+static void
+repl5_stop_debug_timeout(Slapi_Eq_Context eqctx, int *setlevel)
+{
+ char buf[20];
+ char msg[SLAPI_DSE_RETURNTEXT_SIZE];
+
+ if (eqctx && !*setlevel) {
+ int found = slapi_eq_cancel(eqctx);
+ }
+
+ if (s_debug_timeout && s_debug_level && *setlevel) {
+ void config_set_errorlog_level(const char *type, char *buf, char *msg, int apply);
+ sprintf(buf, "%d", 0);
+ config_set_errorlog_level("nsslapd-errorlog-level", buf, msg, 1);
+ }
+}
+
+static void
+repl5_debug_timeout_callback(time_t when, void *arg)
+{
+ int *setlevel = (int *)arg;
+ void config_set_errorlog_level(const char *type, char *buf, char *msg, int apply);
+ char buf[20];
+ char msg[SLAPI_DSE_RETURNTEXT_SIZE];
+
+ *setlevel = 1;
+ sprintf(buf, "%d", s_debug_level);
+ config_set_errorlog_level("nsslapd-errorlog-level", buf, msg, 1);
+
+ slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name,
+ "repl5_debug_timeout_callback: set debug level to %d at %d\n",
+ s_debug_level, when);
+}
diff --git a/ldap/servers/plugins/replication/repl5_inc_protocol.c b/ldap/servers/plugins/replication/repl5_inc_protocol.c
new file mode 100644
index 00000000..a9905a34
--- /dev/null
+++ b/ldap/servers/plugins/replication/repl5_inc_protocol.c
@@ -0,0 +1,1759 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+
+/* repl5_inc_protocol.c */
+/*
+
+ The Prot_Incremental object implements the DS 5.0 multi-master incremental
+ replication protocol.
+
+
+Stuff to do:
+
+- Need to figure out how asynchronous events end up in here. They are:
+ - entry updated in replicated area.
+ - backoff timeout
+ - enter/leave.
+
+Perhaps these events should be properties of the main protocol.
+*/
+
+#include "repl.h"
+#include "repl5.h"
+#include "repl5_ruv.h"
+#include "repl5_prot_private.h"
+#include "cl5_api.h"
+
+extern int slapi_log_urp;
+
+/*** from proto-slap.h ***/
+void ava_done(struct ava *ava);
+
+typedef struct repl5_inc_private
+{
+ char *ruv; /* RUV on remote replica (use diff type for this? - ggood */
+ Backoff_Timer *backoff;
+ Repl_Protocol *rp;
+ PRLock *lock;
+ PRUint32 eventbits;
+} repl5_inc_private;
+
+
+/* Various states the incremental protocol can pass through */
+#define STATE_START 0 /* ONREPL - should we rename this - we don't use it just to start up? */
+#define STATE_WAIT_WINDOW_OPEN 1
+#define STATE_WAIT_CHANGES 2
+#define STATE_READY_TO_ACQUIRE 3
+#define STATE_BACKOFF_START 4 /* ONREPL - can we combine BACKOFF_START and BACKOFF states? */
+#define STATE_BACKOFF 5
+#define STATE_SENDING_UPDATES 6
+#define STATE_STOP_FATAL_ERROR 7
+#define STATE_STOP_FATAL_ERROR_PART2 8
+#define STATE_STOP_NORMAL_TERMINATION 9
+
+/* Events (synchronous and asynchronous; these are bits) */
+#define EVENT_WINDOW_OPENED 1
+#define EVENT_WINDOW_CLOSED 2
+#define EVENT_TRIGGERING_CRITERIA_MET 4 /* ONREPL - should we rename this to EVENT_CHANGE_AVAILABLE */
+#define EVENT_BACKOFF_EXPIRED 8
+#define EVENT_REPLICATE_NOW 16
+#define EVENT_PROTOCOL_SHUTDOWN 32
+#define EVENT_AGMT_CHANGED 64
+
+#define UPDATE_NO_MORE_UPDATES 201
+#define UPDATE_TRANSIENT_ERROR 202
+#define UPDATE_FATAL_ERROR 203
+#define UPDATE_SCHEDULE_WINDOW_CLOSED 204
+#define UPDATE_CONNECTION_LOST 205
+#define UPDATE_TIMEOUT 206
+#define UPDATE_YIELD 207
+
+/* Return codes from examine_update_vector */
+#define EXAMINE_RUV_PRISTINE_REPLICA 401
+#define EXAMINE_RUV_GENERATION_MISMATCH 402
+#define EXAMINE_RUV_REPLICA_TOO_OLD 403
+#define EXAMINE_RUV_OK 404
+#define EXAMINE_RUV_PARAM_ERROR 405
+
+#define MAX_CHANGES_PER_SESSION 10000
+/*
+ * Maximum time to wait between replication sessions. If we
+ * don't see any updates for a period equal to this interval,
+ * we go ahead and start a replication session, just to be safe
+ */
+#define MAX_WAIT_BETWEEN_SESSIONS PR_SecondsToInterval(60 * 5) /* 5 minutes */
+
+/*
+ * tests if the protocol has been shutdown and we need to quit
+ * event_occurred resets the bits in the bit flag, so whoever tests for shutdown
+ * resets the flags, so the next one who tests for shutdown won't get it, so we
+ * also look at the terminate flag
+ */
+#define PROTOCOL_IS_SHUTDOWN(prp) (event_occurred(prp, EVENT_PROTOCOL_SHUTDOWN) || prp->terminate)
+
+/* Forward declarations */
+static PRUint32 event_occurred(Private_Repl_Protocol *prp, PRUint32 event);
+static void reset_events (Private_Repl_Protocol *prp);
+static void protocol_sleep(Private_Repl_Protocol *prp, PRIntervalTime duration);
+static int send_updates(Private_Repl_Protocol *prp, RUV *ruv, PRUint32 *num_changes_sent);
+static void repl5_inc_backoff_expired(time_t timer_fire_time, void *arg);
+static int examine_update_vector(Private_Repl_Protocol *prp, RUV *ruv);
+static PRBool ignore_error_and_keep_going(int error);
+static const char* state2name (int state);
+static const char* event2name (int event);
+static const char* op2string (int op);
+
+/*
+ * It's specifically ok to delete a protocol instance that
+ * is currently running. The instance will be shut down, and
+ * then resources will be freed. Since a graceful shutdown is
+ * attempted, this function may take some time to complete.
+ */
+static void
+repl5_inc_delete(Private_Repl_Protocol **prpp)
+{
+ /* First, stop the protocol if it isn't already stopped */
+ /* Then, delete all resources used by the protocol */
+}
+
+/* helper function */
+void
+set_pause_and_busy_time(long *pausetime, long *busywaittime)
+{
+ /* If neither are set, set busy time to its default */
+ if (!*pausetime && !*busywaittime)
+ {
+ *busywaittime = PROTOCOL_BUSY_BACKOFF_MINIMUM;
+ }
+ /* pause time must be at least 1 more than the busy backoff time */
+ if (*pausetime && !*busywaittime)
+ {
+ /*
+ * user specified a pause time but no busy wait time - must
+ * set busy wait time to 1 less than pause time - if pause
+ * time is 1, we must set it to 2
+ */
+ if (*pausetime < 2)
+ {
+ *pausetime = 2;
+ }
+ *busywaittime = *pausetime - 1;
+ }
+ else if (!*pausetime && *busywaittime)
+ {
+ /*
+ * user specified a busy wait time but no pause time - must
+ * set pause time to 1 more than busy wait time
+ */
+ *pausetime = *busywaittime + 1;
+ }
+ else if (*pausetime && *busywaittime && *pausetime <= *busywaittime)
+ {
+ /*
+ * user specified both pause and busy wait times, but the pause
+ * time was <= busy wait time - pause time must be at least
+ * 1 more than the busy wait time
+ */
+ *pausetime = *busywaittime + 1;
+ }
+}
+
+/*
+ * Do the incremental protocol.
+ *
+ * What's going on here? This thing is a state machine. It has the
+ * following states:
+ *
+ * State transition table:
+ *
+ * Curr State Condition/Event Next State
+ * ---------- ------------ -----------
+ * START schedule window is open ACQUIRE_REPLICA
+ * schedule window is closed WAIT_WINDOW_OPEN
+ * WAIT_WINDOW_OPEN schedule change START
+ * replicate now ACQUIRE_REPLICA
+ * schedule window opens ACQUIRE_REPLICA
+ * ACQUIRE_REPLICA acquired replica SEND_CHANGES
+ * failed to acquire - transient error START_BACKOFF
+ * failed to acquire - fatal error STOP_FATAL_ERROR
+ * SEND_CHANGES can't update CONSUMER_NEEDS_REINIT
+ * no changes to send WAIT_CHANGES
+ * can't send - thransient error START_BACKOF
+ * can't send - window closed WAIT_WINDOW_OPEN
+ * can'r send - fatal error STOP_FATAL_ERROR
+ * START_BACKOF replicate now ACQUIRE_REPLICA
+ * schedule changes START
+ * schedule window closes WAIT_WINDOW_OPEN
+ * backoff expires & can acquire SEND_CHANGES
+ * backoff expires & can't acquire-trans BACKOFF
+ * backoff expires & can't acquire-fatal STOP_FATAL_ERROR
+ * BACKOF replicate now ACQUIRE_REPLICA
+ * schedule changes START
+ * schedule window closes WAIT_WINDOW_OPEN
+ * backoff expires & can acquire SEND_CHANGES
+ * backoff expires & can't acquire-trans BACKOFF
+ * backoff expires & can't acquire-fatal STOP_FATAL_ERROR
+ * WAIT_CHANGES schedule window closes WAIT_WINDOW_OPEN
+ * replicate_now ACQUIRE_REPLICA
+ * change available ACQUIRE_REPLICA
+ * schedule_change START
+ */
+
+/*
+ * Main state machine for the incremental protocol. This routine will,
+ * under normal circumstances, not return until the protocol is shut
+ * down.
+ */
+static void
+repl5_inc_run(Private_Repl_Protocol *prp)
+{
+ int current_state = STATE_START;
+ int next_state = STATE_START;
+ repl5_inc_private *prp_priv = (repl5_inc_private *)prp->private;
+ int done;
+ int e1;
+ RUV *ruv = NULL;
+ CSN *cons_schema_csn;
+ Replica *replica;
+ int wait_change_timer_set = 0;
+ time_t last_start_time;
+ PRUint32 num_changes_sent;
+ char *hostname = NULL;
+ int portnum = 0;
+ /* use a different backoff timer strategy for ACQUIRE_REPLICA_BUSY errors */
+ PRBool use_busy_backoff_timer = PR_FALSE;
+ long pausetime = 0;
+ long busywaittime = 0;
+
+ prp->stopped = 0;
+ prp->terminate = 0;
+ hostname = agmt_get_hostname(prp->agmt);
+ portnum = agmt_get_port(prp->agmt);
+
+ /* establish_protocol_callbacks(prp); */
+ done = 0;
+ do {
+ int rc;
+
+ /* Take action, based on current state, and compute new state. */
+ switch (current_state)
+ {
+ case STATE_START:
+
+ dev_debug("repl5_inc_run(STATE_START)");
+ if (PROTOCOL_IS_SHUTDOWN(prp))
+ {
+ done = 1;
+ break;
+ }
+
+ /*
+ * Our initial state. See if we're in a schedule window. If
+ * so, then we're ready to acquire the replica and see if it
+ * needs any updates from us. If not, then wait for the window
+ * to open.
+ */
+ if (agmt_schedule_in_window_now(prp->agmt))
+ {
+ next_state = STATE_READY_TO_ACQUIRE;
+ }
+ else
+ {
+ next_state = STATE_WAIT_WINDOW_OPEN;
+ }
+
+ /* we can get here from other states because some events happened and were
+ not cleared. For instance when we wake up in STATE_WAIT_CHANGES state.
+ Since this is a fresh start state, we should clear all events */
+ /* ONREPL - this does not feel right - we should take another look
+ at this state machine */
+ reset_events (prp);
+
+ /* Cancel any linger timer that might be in effect... */
+ conn_cancel_linger(prp->conn);
+ /* ... and disconnect, if currently connected */
+ conn_disconnect(prp->conn);
+ /* get the new pause time, if any */
+ pausetime = agmt_get_pausetime(prp->agmt);
+ /* get the new busy wait time, if any */
+ busywaittime = agmt_get_busywaittime(prp->agmt);
+ if (pausetime || busywaittime)
+ {
+ /* helper function to make sure they are set correctly */
+ set_pause_and_busy_time(&pausetime, &busywaittime);
+ }
+ break;
+ case STATE_WAIT_WINDOW_OPEN:
+ /*
+ * We're waiting for a schedule window to open. If one did,
+ * or we receive a "replicate now" event, then start a protocol
+ * session immediately. If the replication schedule changed, go
+ * back to start. Otherwise, go back to sleep.
+ */
+ dev_debug("repl5_inc_run(STATE_WAIT_WINDOW_OPEN)");
+ if (PROTOCOL_IS_SHUTDOWN(prp))
+ {
+ done = 1;
+ break;
+ }
+ else if (event_occurred(prp, EVENT_WINDOW_OPENED))
+ {
+ next_state = STATE_READY_TO_ACQUIRE;
+ }
+ else if (event_occurred(prp, EVENT_REPLICATE_NOW))
+ {
+ next_state = STATE_READY_TO_ACQUIRE;
+ }
+ else if (event_occurred(prp, EVENT_AGMT_CHANGED))
+ {
+ next_state = STATE_START;
+ conn_set_agmt_changed(prp->conn);
+ }
+ else if (event_occurred(prp, EVENT_TRIGGERING_CRITERIA_MET)) /* change available */
+ {
+ /* just ignore it and go to sleep */
+ protocol_sleep(prp, PR_INTERVAL_NO_TIMEOUT);
+ }
+ else if (e1 = event_occurred(prp, EVENT_WINDOW_CLOSED) ||
+ event_occurred(prp, EVENT_BACKOFF_EXPIRED))
+ {
+ /* this events - should not occur - log a warning and go to sleep */
+ slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name,
+ "%s: Incremental protocol: "
+ "event %s should not occur in state %s; going to sleep\n",
+ agmt_get_long_name(prp->agmt),
+ e1 ? event2name(EVENT_WINDOW_CLOSED) : event2name(EVENT_BACKOFF_EXPIRED),
+ state2name(current_state));
+ protocol_sleep(prp, PR_INTERVAL_NO_TIMEOUT);
+ }
+ else
+ {
+ /* wait until window opens or an event occurs */
+ slapi_log_error(SLAPI_LOG_REPL, repl_plugin_name,
+ "%s: Incremental protocol: "
+ "waiting for update window to open\n", agmt_get_long_name(prp->agmt));
+ protocol_sleep(prp, PR_INTERVAL_NO_TIMEOUT);
+ }
+ break;
+ case STATE_WAIT_CHANGES:
+ /*
+ * We're in a replication window, but we're waiting for more
+ * changes to accumulate before we actually hook up and send
+ * them.
+ */
+ dev_debug("repl5_inc_run(STATE_WAIT_CHANGES)");
+ if (PROTOCOL_IS_SHUTDOWN(prp))
+ {
+ dev_debug("repl5_inc_run(STATE_WAIT_CHANGES): PROTOCOL_IS_SHUTING_DOWN -> end repl5_inc_run\n");
+ done = 1;
+ break;
+ }
+ else if (event_occurred(prp, EVENT_REPLICATE_NOW))
+ {
+ dev_debug("repl5_inc_run(STATE_WAIT_CHANGES): EVENT_REPLICATE_NOW received -> STATE_READY_TO_ACQUIRE\n");
+ next_state = STATE_READY_TO_ACQUIRE;
+ wait_change_timer_set = 0;
+ }
+ else if (event_occurred(prp, EVENT_AGMT_CHANGED))
+ {
+ dev_debug("repl5_inc_run(STATE_WAIT_CHANGES): EVENT_AGMT_CHANGED received -> STATE_START\n");
+ next_state = STATE_START;
+ conn_set_agmt_changed(prp->conn);
+ wait_change_timer_set = 0;
+ }
+ else if (event_occurred(prp, EVENT_WINDOW_CLOSED))
+ {
+ dev_debug("repl5_inc_run(STATE_WAIT_CHANGES): EVENT_WINDOW_CLOSED received -> STATE_WAIT_WINDOW_OPEN\n");
+ next_state = STATE_WAIT_WINDOW_OPEN;
+ wait_change_timer_set = 0;
+ }
+ else if (event_occurred(prp, EVENT_TRIGGERING_CRITERIA_MET))
+ {
+ dev_debug("repl5_inc_run(STATE_WAIT_CHANGES): EVENT_TRIGGERING_CRITERIA_MET received -> STATE_READY_TO_ACQUIRE\n");
+ next_state = STATE_READY_TO_ACQUIRE;
+ wait_change_timer_set = 0;
+ }
+ else if (e1 = event_occurred(prp, EVENT_WINDOW_OPENED) ||
+ event_occurred(prp, EVENT_BACKOFF_EXPIRED))
+ {
+ /* this events - should not occur - log a warning and clear the event */
+ slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name, "%s: Incremental protocol: "
+ "event %s should not occur in state %s\n",
+ agmt_get_long_name(prp->agmt),
+ e1 ? event2name(EVENT_WINDOW_OPENED) : event2name(EVENT_BACKOFF_EXPIRED),
+ state2name(current_state));
+ wait_change_timer_set = 0;
+ }
+ else
+ {
+ if (wait_change_timer_set)
+ {
+ /* We are here because our timer expired */
+ dev_debug("repl5_inc_run(STATE_WAIT_CHANGES): wait_change_timer_set expired -> STATE_START\n");
+ next_state = STATE_START;
+ wait_change_timer_set = 0;
+ }
+ else
+ {
+ /* We are here because the last replication session
+ * finished or aborted.
+ */
+ wait_change_timer_set = 1;
+ protocol_sleep(prp, MAX_WAIT_BETWEEN_SESSIONS);
+ }
+ }
+ break;
+ case STATE_READY_TO_ACQUIRE:
+
+ dev_debug("repl5_inc_run(STATE_READY_TO_ACQUIRE)");
+ if (PROTOCOL_IS_SHUTDOWN(prp))
+ {
+ done = 1;
+ break;
+ }
+
+ /* ONREPL - at this state we unconditionally acquire the replica
+ ignoring all events. Not sure if this is good */
+ object_acquire(prp->replica_object);
+ replica = object_get_data(prp->replica_object);
+
+ rc = acquire_replica(prp, REPL_NSDS50_INCREMENTAL_PROTOCOL_OID, &ruv);
+ use_busy_backoff_timer = PR_FALSE; /* default */
+ if (rc == ACQUIRE_SUCCESS)
+ {
+ next_state = STATE_SENDING_UPDATES;
+ }
+ else if (rc == ACQUIRE_REPLICA_BUSY)
+ {
+ next_state = STATE_BACKOFF_START;
+ use_busy_backoff_timer = PR_TRUE;
+ }
+ else if (rc == ACQUIRE_CONSUMER_WAS_UPTODATE)
+ {
+ next_state = STATE_WAIT_CHANGES;
+ }
+ else if (rc == ACQUIRE_TRANSIENT_ERROR)
+ {
+ next_state = STATE_BACKOFF_START;
+ }
+ else if (rc == ACQUIRE_FATAL_ERROR)
+ {
+ next_state = STATE_STOP_FATAL_ERROR;
+ }
+ if (rc != ACQUIRE_SUCCESS)
+ {
+ int optype, ldaprc;
+ conn_get_error(prp->conn, &optype, &ldaprc);
+ agmt_set_last_update_status(prp->agmt, ldaprc,
+ prp->last_acquire_response_code, NULL);
+ }
+
+ object_release(prp->replica_object); replica = NULL;
+ break;
+ case STATE_BACKOFF_START:
+ dev_debug("repl5_inc_run(STATE_BACKOFF_START)");
+ if (PROTOCOL_IS_SHUTDOWN(prp))
+ {
+ done = 1;
+ break;
+ }
+ if (event_occurred(prp, EVENT_REPLICATE_NOW))
+ {
+ next_state = STATE_READY_TO_ACQUIRE;
+ }
+ else if (event_occurred(prp, EVENT_AGMT_CHANGED))
+ {
+ next_state = STATE_START;
+ conn_set_agmt_changed(prp->conn);
+ }
+ else if (event_occurred (prp, EVENT_WINDOW_CLOSED))
+ {
+ next_state = STATE_WAIT_WINDOW_OPEN;
+ }
+ else if (event_occurred (prp, EVENT_TRIGGERING_CRITERIA_MET))
+ {
+ /* consume and ignore */
+ }
+ else if (e1 = event_occurred (prp, EVENT_WINDOW_OPENED) ||
+ event_occurred (prp, EVENT_BACKOFF_EXPIRED))
+ {
+ /* This should never happen */
+ /* this events - should not occur - log a warning and go to sleep */
+ slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name,
+ "%s: Incremental protocol: event %s should not occur in state %s\n",
+ agmt_get_long_name(prp->agmt),
+ e1 ? event2name(EVENT_WINDOW_OPENED) : event2name(EVENT_BACKOFF_EXPIRED),
+ state2name(current_state));
+ }
+ else
+ {
+ /* Set up the backoff timer to wake us up at the appropriate time */
+ if (use_busy_backoff_timer)
+ {
+ /* we received a busy signal from the consumer, wait for a while */
+ if (!busywaittime)
+ {
+ busywaittime = PROTOCOL_BUSY_BACKOFF_MINIMUM;
+ }
+ prp_priv->backoff = backoff_new(BACKOFF_FIXED, busywaittime,
+ busywaittime);
+ }
+ else
+ {
+ prp_priv->backoff = backoff_new(BACKOFF_EXPONENTIAL, PROTOCOL_BACKOFF_MINIMUM,
+ PROTOCOL_BACKOFF_MAXIMUM);
+ }
+ next_state = STATE_BACKOFF;
+ backoff_reset(prp_priv->backoff, repl5_inc_backoff_expired, (void *)prp);
+ protocol_sleep(prp, PR_INTERVAL_NO_TIMEOUT);
+ use_busy_backoff_timer = PR_FALSE;
+ }
+ break;
+ case STATE_BACKOFF:
+ /*
+ * We're in a backoff state.
+ */
+ dev_debug("repl5_inc_run(STATE_BACKOFF)");
+ if (PROTOCOL_IS_SHUTDOWN(prp))
+ {
+ if (prp_priv->backoff)
+ backoff_delete(&prp_priv->backoff);
+ done = 1;
+ break;
+ }
+ else if (event_occurred(prp, EVENT_REPLICATE_NOW))
+ {
+ next_state = STATE_READY_TO_ACQUIRE;
+ }
+ else if (event_occurred(prp, EVENT_AGMT_CHANGED))
+ {
+ next_state = STATE_START;
+
+ conn_set_agmt_changed(prp->conn);
+ /* Destroy the backoff timer, since we won't need it anymore */
+ if (prp_priv->backoff)
+ backoff_delete(&prp_priv->backoff);
+ }
+ else if (event_occurred(prp, EVENT_WINDOW_CLOSED))
+ {
+ next_state = STATE_WAIT_WINDOW_OPEN;
+ /* Destroy the backoff timer, since we won't need it anymore */
+ if (prp_priv->backoff)
+ backoff_delete(&prp_priv->backoff);
+ }
+ else if (event_occurred(prp, EVENT_BACKOFF_EXPIRED))
+ {
+ rc = acquire_replica(prp, REPL_NSDS50_INCREMENTAL_PROTOCOL_OID, &ruv);
+ use_busy_backoff_timer = PR_FALSE;
+ if (rc == ACQUIRE_SUCCESS)
+ {
+ next_state = STATE_SENDING_UPDATES;
+ }
+ else if (rc == ACQUIRE_REPLICA_BUSY)
+ {
+ next_state = STATE_BACKOFF;
+ use_busy_backoff_timer = PR_TRUE;
+ }
+ else if (rc == ACQUIRE_CONSUMER_WAS_UPTODATE)
+ {
+ next_state = STATE_WAIT_CHANGES;
+ }
+ else if (rc == ACQUIRE_TRANSIENT_ERROR)
+ {
+ next_state = STATE_BACKOFF;
+ }
+ else if (rc == ACQUIRE_FATAL_ERROR)
+ {
+ next_state = STATE_STOP_FATAL_ERROR;
+ }
+ if (rc != ACQUIRE_SUCCESS)
+ {
+ int optype, ldaprc;
+ conn_get_error(prp->conn, &optype, &ldaprc);
+ agmt_set_last_update_status(prp->agmt, ldaprc,
+ prp->last_acquire_response_code, NULL);
+ }
+ /*
+ * We either need to step the backoff timer, or
+ * destroy it if we don't need it anymore.
+ */
+ if (STATE_BACKOFF == next_state)
+ {
+ time_t next_fire_time;
+ time_t now;
+ /* Step the backoff timer */
+ time(&now);
+ next_fire_time = backoff_step(prp_priv->backoff);
+ /* And go back to sleep */
+ slapi_log_error(SLAPI_LOG_REPL, repl_plugin_name,
+ "%s: Replication session backing off for %d seconds\n",
+ agmt_get_long_name(prp->agmt),
+ next_fire_time - now);
+
+ protocol_sleep(prp, PR_INTERVAL_NO_TIMEOUT);
+ }
+ else
+ {
+ /* Destroy the backoff timer, since we won't need it anymore */
+ backoff_delete(&prp_priv->backoff);
+ }
+ use_busy_backoff_timer = PR_FALSE;
+ }
+ else if (event_occurred(prp, EVENT_TRIGGERING_CRITERIA_MET))
+ {
+ /* changes are available */
+ if ( prp_priv->backoff == NULL || backoff_expired (prp_priv->backoff, 60) )
+ {
+ /*
+ * Have seen cases that the agmt stuck here forever since
+ * somehow the backoff timer was not in event queue anymore.
+ * If the backoff timer has expired more than 60 seconds,
+ * destroy it.
+ */
+ if ( prp_priv->backoff )
+ backoff_delete(&prp_priv->backoff);
+ next_state = STATE_READY_TO_ACQUIRE;
+ }
+ else
+ {
+ /* ignore changes and go to sleep */
+ protocol_sleep(prp, PR_INTERVAL_NO_TIMEOUT);
+ }
+ }
+ else if (event_occurred(prp, EVENT_WINDOW_OPENED))
+ {
+ /* this should never happen - log an error and go to sleep */
+ slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name, "%s: Incremental protocol: "
+ "event %s should not occur in state %s; going to sleep\n",
+ agmt_get_long_name(prp->agmt),
+ event2name(EVENT_WINDOW_OPENED), state2name(current_state));
+ protocol_sleep(prp, PR_INTERVAL_NO_TIMEOUT);
+ }
+ break;
+ case STATE_SENDING_UPDATES:
+ dev_debug("repl5_inc_run(STATE_SENDING_UPDATES)");
+ agmt_set_update_in_progress(prp->agmt, PR_TRUE);
+ num_changes_sent = 0;
+ last_start_time = current_time();
+ agmt_set_last_update_start(prp->agmt, last_start_time);
+ /*
+ * We've acquired the replica, and are ready to send any
+ * needed updates.
+ */
+ if (PROTOCOL_IS_SHUTDOWN(prp))
+ {
+ release_replica (prp);
+ done = 1;
+ agmt_set_update_in_progress(prp->agmt, PR_FALSE);
+ agmt_set_last_update_end(prp->agmt, current_time());
+ /* MAB: I don't find the following status correct. How do we know it has
+ been stopped by an admin and not by a total update request, for instance?
+ In any case, how is this protocol shutdown situation different from all the
+ other ones that are present in this state machine? */
+ /* richm: We at least need to let monitors know that the protocol has been
+ shutdown - maybe they can figure out why */
+ agmt_set_last_update_status(prp->agmt, 0, 0, "Protocol stopped");
+ break;
+ }
+
+ agmt_set_last_update_status(prp->agmt, 0, 0, "Incremental update started");
+
+ /* ONREPL - in this state we send changes no matter what other events occur.
+ This is because we can get because of the REPLICATE_NOW event which
+ has high priority. Is this ok? */
+ /* First, push new schema to the consumer if needed */
+ /* ONREPL - should we push schema after we examine the RUV? */
+ /*
+ * GGOOREPL - I don't see why we should wait until we've
+ * examined the RUV. The schema entry has its own CSN that is
+ * used to decide if the remote schema needs to be updated.
+ */
+ cons_schema_csn = agmt_get_consumer_schema_csn ( prp->agmt );
+ rc = conn_push_schema(prp->conn, &cons_schema_csn);
+ if ( cons_schema_csn != agmt_get_consumer_schema_csn ( prp->agmt ))
+ {
+ agmt_set_consumer_schema_csn ( prp->agmt, cons_schema_csn );
+ }
+ if (CONN_SCHEMA_UPDATED != rc && CONN_SCHEMA_NO_UPDATE_NEEDED != rc)
+ {
+ slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name,
+ "%s: Warning: unable to replicate schema: rc=%d\n",
+ agmt_get_long_name(prp->agmt), rc);
+ /* But keep going */
+ }
+ dev_debug("repl5_inc_run(STATE_SENDING_UPDATES) -> examine_update_vector");
+ rc = examine_update_vector(prp, ruv);
+ /*
+ * Decide what to do next - proceed with incremental,
+ * backoff, or total update
+ */
+ switch (rc)
+ {
+ case EXAMINE_RUV_PARAM_ERROR:
+ /* this is really bad - we have NULL prp! */
+ next_state = STATE_STOP_FATAL_ERROR;
+ break;
+ case EXAMINE_RUV_PRISTINE_REPLICA:
+ slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name,
+ "%s: Replica has no update vector. It has never been initialized.\n",
+ agmt_get_long_name(prp->agmt));
+ next_state = STATE_BACKOFF_START;
+ break;
+ case EXAMINE_RUV_GENERATION_MISMATCH:
+ slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name,
+ "%s: Replica has a different generation ID than the local data.\n",
+ agmt_get_long_name(prp->agmt));
+ next_state = STATE_BACKOFF_START;
+ break;
+ case EXAMINE_RUV_REPLICA_TOO_OLD:
+ slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name,
+ "%s: Replica update vector is too out of date to bring "
+ "into sync using the incremental protocol. The replica "
+ "must be reinitialized.\n", agmt_get_long_name(prp->agmt));
+ next_state = STATE_BACKOFF_START;
+ break;
+ case EXAMINE_RUV_OK:
+ /* update our csn generator state with the consumer's ruv data */
+ dev_debug("repl5_inc_run(STATE_SENDING_UPDATES) -> examine_update_vector OK");
+ object_acquire(prp->replica_object);
+ replica = object_get_data(prp->replica_object);
+ rc = replica_update_csngen_state (replica, ruv);
+ object_release (prp->replica_object);
+ replica = NULL;
+ if (rc != 0) /* too much skew */
+ {
+ slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name,
+ "%s: Incremental protocol: fatal error - too much time skew between replicas!\n",
+ agmt_get_long_name(prp->agmt));
+ next_state = STATE_STOP_FATAL_ERROR;
+ }
+ else
+ {
+ rc = send_updates(prp, ruv, &num_changes_sent);
+ if (rc == UPDATE_NO_MORE_UPDATES)
+ {
+ dev_debug("repl5_inc_run(STATE_SENDING_UPDATES) -> send_updates = UPDATE_NO_MORE_UPDATES -> STATE_WAIT_CHANGES");
+ agmt_set_last_update_status(prp->agmt, 0, 0, "Incremental update succeeded");
+ next_state = STATE_WAIT_CHANGES;
+ }
+ else if (rc == UPDATE_YIELD)
+ {
+ dev_debug("repl5_inc_run(STATE_SENDING_UPDATES) -> send_updates = UPDATE_YIELD -> STATE_BACKOFF_START");
+ agmt_set_last_update_status(prp->agmt, 0, 0, "Incremental update succeeded and yielded");
+ next_state = STATE_BACKOFF_START;
+ }
+ else if (rc == UPDATE_TRANSIENT_ERROR)
+ {
+ dev_debug("repl5_inc_run(STATE_SENDING_UPDATES) -> send_updates = UPDATE_TRANSIENT_ERROR -> STATE_BACKOFF_START");
+ next_state = STATE_BACKOFF_START;
+ }
+ else if (rc == UPDATE_FATAL_ERROR)
+ {
+ dev_debug("repl5_inc_run(STATE_SENDING_UPDATES) -> send_updates = UPDATE_FATAL_ERROR -> STATE_STOP_FATAL_ERROR");
+ next_state = STATE_STOP_FATAL_ERROR;
+ }
+ else if (rc == UPDATE_SCHEDULE_WINDOW_CLOSED)
+ {
+ dev_debug("repl5_inc_run(STATE_SENDING_UPDATES) -> send_updates = UPDATE_SCHEDULE_WINDOW_CLOSED -> STATE_WAIT_WINDOW_OPEN");
+ /* ONREPL - I don't think we should check this. We might be
+ here because of replicate_now event - so we don't care
+ about the schedule */
+ next_state = STATE_WAIT_WINDOW_OPEN;
+ /* ONREPL - do we need to release the replica here ? */
+ conn_disconnect (prp->conn);
+ }
+ else if (rc == UPDATE_CONNECTION_LOST)
+ {
+ dev_debug("repl5_inc_run(STATE_SENDING_UPDATES) -> send_updates = UPDATE_CONNECTION_LOST -> STATE_BACKOFF_START");
+ next_state = STATE_BACKOFF_START;
+ }
+ else if (rc == UPDATE_TIMEOUT)
+ {
+ dev_debug("repl5_inc_run(STATE_SENDING_UPDATES) -> send_updates = UPDATE_TIMEOUT -> STATE_BACKOFF_START");
+ next_state = STATE_BACKOFF_START;
+ }
+ }
+ last_start_time = 0UL;
+ break;
+ }
+ if (NULL != ruv)
+ {
+ ruv_destroy(&ruv); ruv = NULL;
+ }
+ agmt_set_last_update_end(prp->agmt, current_time());
+ agmt_set_update_in_progress(prp->agmt, PR_FALSE);
+ /* If timed out, close the connection after released the replica */
+ release_replica(prp);
+ if (rc == UPDATE_TIMEOUT) {
+ conn_disconnect(prp->conn);
+ }
+ if (rc == UPDATE_NO_MORE_UPDATES && num_changes_sent > 0)
+ {
+ if (pausetime > 0)
+ {
+ /* richm - 20020219 - If we have acquired the consumer, and another master has gone
+ into backoff waiting for us to release it, we may acquire the replica sooner
+ than the other master has a chance to, and the other master may not be able
+ to acquire the consumer for a long time (hours, days?) if this server is
+ under a heavy load (see reliab06 et. al. system tests)
+ So, this sleep gives the other master(s) a chance to acquire the consumer
+ replica */
+ long loops = pausetime;
+ /* the while loop is so that we don't just sleep and sleep if an
+ event comes in that we should handle immediately (like shutdown) */
+ slapi_log_error(SLAPI_LOG_REPL, repl_plugin_name,
+ "%s: Pausing updates for %ld seconds to allow other suppliers to update consumer\n",
+ agmt_get_long_name(prp->agmt), pausetime);
+ while (loops-- && !(PROTOCOL_IS_SHUTDOWN(prp)))
+ {
+ DS_Sleep(PR_SecondsToInterval(1));
+ }
+ }
+ else if (num_changes_sent > 10)
+ {
+ /* wait for consumer to write its ruv if the replication was busy */
+ /* When asked, consumer sends its ruv in cache to the supplier. */
+ /* DS_Sleep ( PR_SecondsToInterval(1) ); */
+ }
+ }
+ break;
+ case STATE_STOP_FATAL_ERROR:
+ /*
+ * We encountered some sort of a fatal error. Suspend.
+ */
+ /* XXXggood update state in replica */
+ agmt_set_last_update_status(prp->agmt, -1, 0, "Incremental update has failed and requires administrator action");
+ dev_debug("repl5_inc_run(STATE_STOP_FATAL_ERROR)");
+ slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name,
+ "%s: Incremental update failed and requires administrator action\n",
+ agmt_get_long_name(prp->agmt));
+ next_state = STATE_STOP_FATAL_ERROR_PART2;
+ break;
+ case STATE_STOP_FATAL_ERROR_PART2:
+ if (PROTOCOL_IS_SHUTDOWN(prp))
+ {
+ done = 1;
+ break;
+ }
+
+ /* MAB: This state is the FATAL state where we are supposed to get
+ as a result of a FATAL error on send_updates. But, as bug
+ states, send_updates was always returning TRANSIENT errors and never
+ FATAL... In other words, this code has never been tested before...
+
+ As of 01/16/01, this piece of code was in a very dangerous state. In particular,
+ 1) it does not catch any events
+ 2) it is a terminal state (once reached it never transitions to a different state)
+
+ Both things combined make this state to become a consuming infinite loop
+ that is useless after all (we are in a fatal place requiring manual admin jobs */
+
+ /* MAB: The following lines fix problem number 1 above... When the code gets
+ into this state, it should only get a chance to get out of it by an
+ EVENT_AGMT_CHANGED event... All other events should be ignored */
+ else if (event_occurred(prp, EVENT_AGMT_CHANGED))
+ {
+ dev_debug("repl5_inc_run(STATE_STOP_FATAL_ERROR): EVENT_AGMT_CHANGED received\n");
+ /* Chance to recover for the EVENT_AGMT_CHANGED event.
+ This is not mandatory, but fixes problem 2 above */
+ next_state = STATE_STOP_NORMAL_TERMINATION;
+ }
+ else
+ {
+ dev_debug("repl5_inc_run(STATE_STOP_FATAL_ERROR): Event received. Clearing it\n");
+ reset_events (prp);
+ }
+
+ protocol_sleep (prp, PR_INTERVAL_NO_TIMEOUT);
+ break;
+
+ case STATE_STOP_NORMAL_TERMINATION:
+ /*
+ * We encountered some sort of a fatal error. Return.
+ */
+ /* XXXggood update state in replica */
+ dev_debug("repl5_inc_run(STATE_STOP_NORMAL_TERMINATION)");
+ done = 1;
+ break;
+ }
+
+ slapi_log_error(SLAPI_LOG_REPL, repl_plugin_name,
+ "%s: State: %s -> %s\n",
+ agmt_get_long_name(prp->agmt),
+ state2name(current_state), state2name(next_state));
+
+ current_state = next_state;
+ } while (!done);
+ slapi_ch_free((void**)&hostname);
+ /* remove_protocol_callbacks(prp); */
+ prp->stopped = 1;
+ /* Cancel any linger timer that might be in effect... */
+ conn_cancel_linger(prp->conn);
+ /* ... and disconnect, if currently connected */
+ conn_disconnect(prp->conn);
+}
+
+
+
+/*
+ * Go to sleep until awakened.
+ */
+static void
+protocol_sleep(Private_Repl_Protocol *prp, PRIntervalTime duration)
+{
+ PR_ASSERT(NULL != prp);
+ PR_Lock(prp->lock);
+ /* we should not go to sleep if there are events available to be processed.
+ Otherwise, we can miss the event that suppose to wake us up */
+ if (prp->eventbits == 0)
+ PR_WaitCondVar(prp->cvar, duration);
+ else
+ {
+ slapi_log_error(SLAPI_LOG_REPL, repl_plugin_name,
+ "%s: Incremental protocol: can't go to sleep: event bits - %x\n",
+ agmt_get_long_name(prp->agmt), prp->eventbits);
+ }
+ PR_Unlock(prp->lock);
+}
+
+
+/*
+ * Notify the protocol about some event. Signal the condition
+ * variable in case the protocol is sleeping. Multiple occurences
+ * of a single event type are not remembered (e.g. no stack
+ * of events is maintained).
+ */
+static void
+event_notify(Private_Repl_Protocol *prp, PRUint32 event)
+{
+ PR_ASSERT(NULL != prp);
+ PR_Lock(prp->lock);
+ prp->eventbits |= event;
+ PR_NotifyCondVar(prp->cvar);
+ PR_Unlock(prp->lock);
+}
+
+
+/*
+ * Test to see if an event occurred. The event is cleared when
+ * read.
+ */
+static PRUint32
+event_occurred(Private_Repl_Protocol *prp, PRUint32 event)
+{
+ PRUint32 return_value;
+ PR_ASSERT(NULL != prp);
+ PR_Lock(prp->lock);
+ return_value = (prp->eventbits & event);
+ prp->eventbits &= ~event; /* Clear event */
+ PR_Unlock(prp->lock);
+ return return_value;
+}
+
+static void
+reset_events (Private_Repl_Protocol *prp)
+{
+ PR_ASSERT(NULL != prp);
+ PR_Lock(prp->lock);
+ prp->eventbits = 0;
+ PR_Unlock(prp->lock);
+}
+
+
+/*
+ * Replay the actual update to the consumer. Construct an appropriate LDAP
+ * operation, attach the baggage LDAPv3 control that contains the CSN, etc.,
+ * and send the operation to the consumer.
+ */
+ConnResult
+replay_update(Private_Repl_Protocol *prp, slapi_operation_parameters *op)
+{
+ ConnResult return_value;
+ LDAPControl *update_control;
+ char *parentuniqueid;
+ LDAPMod **modrdn_mods = NULL;
+ char csn_str[CSN_STRSIZE]; /* For logging only */
+
+ csn_as_string(op->csn, PR_FALSE, csn_str);
+
+ /* Construct the replication info control that accompanies the operation */
+ if (SLAPI_OPERATION_ADD == op->operation_type)
+ {
+ parentuniqueid = op->p.p_add.parentuniqueid;
+ }
+ else if (SLAPI_OPERATION_MODRDN == op->operation_type)
+ {
+ /*
+ * For modrdn operations, we need to send along modified attributes, e.g.
+ * modifytimestamp.
+ * And the superior_uniqueid !
+ */
+ modrdn_mods = op->p.p_modrdn.modrdn_mods;
+ parentuniqueid = op->p.p_modrdn.modrdn_newsuperior_address.uniqueid;
+ }
+ else
+ {
+ parentuniqueid = NULL;
+ }
+ if (create_NSDS50ReplUpdateInfoControl(op->target_address.uniqueid,
+ parentuniqueid, op->csn, modrdn_mods, &update_control) != LDAP_SUCCESS)
+ {
+ slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name,
+ "%s: replay_update: Unable to create NSDS50ReplUpdateInfoControl "
+ "for operation with csn %s. Skipping update.\n",
+ agmt_get_long_name(prp->agmt), csn_str);
+ }
+ else
+ {
+ slapi_log_error(SLAPI_LOG_REPL, repl_plugin_name,
+ "%s: replay_update: Sending %s operation (dn=\"%s\" csn=%s)\n",
+ agmt_get_long_name(prp->agmt),
+ op2string(op->operation_type), op->target_address.dn, csn_str);
+ /* What type of operation is it? */
+ switch (op->operation_type)
+ {
+ case SLAPI_OPERATION_ADD:
+ {
+ LDAPMod **entryattrs;
+ /* Convert entry to mods */
+ (void)slapi_entry2mods (op->p.p_add.target_entry,
+ NULL /* &entrydn : We don't need it */,
+ &entryattrs);
+ if (NULL == entryattrs)
+ {
+ slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name,
+ "%s: replay_update: Cannot convert entry to LDAPMods.\n",
+ agmt_get_long_name(prp->agmt));
+ return_value = CONN_LOCAL_ERROR;
+ }
+ else
+ {
+ return_value = conn_send_add(prp->conn, op->target_address.dn,
+ entryattrs, update_control, NULL /* returned controls */);
+ ldap_mods_free(entryattrs, 1);
+ }
+ break;
+ }
+ case SLAPI_OPERATION_MODIFY:
+ return_value = conn_send_modify(prp->conn, op->target_address.dn,
+ op->p.p_modify.modify_mods, update_control,
+ NULL /* returned controls */);
+ break;
+ case SLAPI_OPERATION_DELETE:
+ return_value = conn_send_delete(prp->conn, op->target_address.dn,
+ update_control, NULL /* returned controls */);
+ break;
+ case SLAPI_OPERATION_MODRDN:
+ /* XXXggood need to pass modrdn mods in update control! */
+ return_value = conn_send_rename(prp->conn, op->target_address.dn,
+ op->p.p_modrdn.modrdn_newrdn,
+ op->p.p_modrdn.modrdn_newsuperior_address.dn,
+ op->p.p_modrdn.modrdn_deloldrdn,
+ update_control, NULL /* returned controls */);
+ break;
+ default:
+ slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name, "%s: replay_update: Unknown "
+ "operation type %d found in changelog - skipping change.\n",
+ agmt_get_long_name(prp->agmt), op->operation_type);
+ }
+
+ destroy_NSDS50ReplUpdateInfoControl(&update_control);
+ }
+
+ if (CONN_OPERATION_SUCCESS == return_value)
+ {
+ slapi_log_error(SLAPI_LOG_REPL, repl_plugin_name,
+ "%s: replay_update: Consumer successfully replayed operation with csn %s\n",
+ agmt_get_long_name(prp->agmt), csn_str);
+ }
+ else
+ {
+ slapi_log_error(SLAPI_LOG_REPL, repl_plugin_name,
+ "%s: replay_update: Consumer could not replay operation with csn %s\n",
+ agmt_get_long_name(prp->agmt), csn_str);
+ }
+ return return_value;
+}
+
+static PRBool
+is_dummy_operation (const slapi_operation_parameters *op)
+{
+ return (strcmp (op->target_address.uniqueid, START_ITERATION_ENTRY_UNIQUEID) == 0);
+}
+
+
+
+void
+cl5_operation_parameters_done (struct slapi_operation_parameters *sop)
+{
+ if(sop!=NULL) {
+ switch(sop->operation_type)
+ {
+ case SLAPI_OPERATION_BIND:
+ slapi_ch_free((void **)&(sop->p.p_bind.bind_saslmechanism));
+ if (sop->p.p_bind.bind_creds)
+ ber_bvecfree((struct berval**)&(sop->p.p_bind.bind_creds));
+ if (sop->p.p_bind.bind_ret_saslcreds)
+ ber_bvecfree((struct berval**)&(sop->p.p_bind.bind_ret_saslcreds));
+ sop->p.p_bind.bind_creds = NULL;
+ sop->p.p_bind.bind_ret_saslcreds = NULL;
+ break;
+ case SLAPI_OPERATION_COMPARE:
+ ava_done((struct ava *)&(sop->p.p_compare.compare_ava));
+ break;
+ case SLAPI_OPERATION_SEARCH:
+ slapi_ch_free((void **)&(sop->p.p_search.search_strfilter));
+ charray_free(sop->p.p_search.search_attrs);
+ slapi_filter_free(sop->p.p_search.search_filter,1);
+ break;
+ case SLAPI_OPERATION_MODRDN:
+ sop->p.p_modrdn.modrdn_deloldrdn = 0;
+ break;
+ case SLAPI_OPERATION_EXTENDED:
+ slapi_ch_free((void **)&(sop->p.p_extended.exop_oid));
+ if (sop->p.p_extended.exop_value)
+ ber_bvecfree((struct berval**)&(sop->p.p_extended.exop_value));
+ sop->p.p_extended.exop_value = NULL;
+ break;
+ default:
+ break;
+ }
+ }
+ operation_parameters_done(sop);
+
+}
+
+
+
+/*
+ * Send a set of updates to the replica. Assumes that (1) the replica
+ * has already been acquired, (2) that the consumer's update vector has
+ * been checked and (3) that it's ok to send incremental updates.
+ * Returns:
+ * UPDATE_NO_MORE_UPDATES - all updates were sent succussfully
+ * UPDATE_TRANSIENT_ERROR - some non-permanent error occurred. Try again later.
+ * UPDATE_FATAL_ERROR - some bad, permanent error occurred.
+ * UPDATE_SCHEDULE_WINDOW_CLOSED - the schedule window closed on us.
+ */
+static int
+send_updates(Private_Repl_Protocol *prp, RUV *remote_update_vector, PRUint32 *num_changes_sent)
+{
+ CL5Entry entry;
+ slapi_operation_parameters op;
+ int return_value;
+ int rc;
+ CL5ReplayIterator *changelog_iterator;
+
+ *num_changes_sent = 0;
+ /*
+ * Iterate over the changelog. Retrieve each update,
+ * construct an appropriate LDAP operation,
+ * attaching the CSN, and send the change.
+ */
+
+ rc = cl5CreateReplayIterator(prp, remote_update_vector, &changelog_iterator);
+ if (CL5_SUCCESS != rc)
+ {
+ switch (rc)
+ {
+ case CL5_BAD_DATA: /* invalid parameter passed to the function */
+ slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name,
+ "%s: Invalid parameter passed to cl5CreateReplayIterator\n",
+ agmt_get_long_name(prp->agmt));
+ return_value = UPDATE_FATAL_ERROR;
+ break;
+ case CL5_BAD_FORMAT: /* db data has unexpected format */
+ slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name,
+ "%s: Unexpected format encountered in changelog database\n",
+ agmt_get_long_name(prp->agmt));
+ return_value = UPDATE_FATAL_ERROR;
+ break;
+ case CL5_BAD_STATE: /* changelog is in an incorrect state for attempted operation */
+ slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name,
+ "%s: Changelog database was in an incorrect state\n",
+ agmt_get_long_name(prp->agmt));
+ return_value = UPDATE_FATAL_ERROR;
+ break;
+ case CL5_BAD_DBVERSION: /* changelog has invalid dbversion */
+ slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name,
+ "%s: Incorrect dbversion found in changelog database\n",
+ agmt_get_long_name(prp->agmt));
+ return_value = UPDATE_FATAL_ERROR;
+ break;
+ case CL5_DB_ERROR: /* database error */
+ slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name,
+ "%s: A changelog database error was encountered\n",
+ agmt_get_long_name(prp->agmt));
+ return_value = UPDATE_FATAL_ERROR;
+ break;
+ case CL5_NOTFOUND: /* we have no changes to send */
+ slapi_log_error(SLAPI_LOG_REPL, repl_plugin_name,
+ "%s: No changes to send\n",
+ agmt_get_long_name(prp->agmt));
+ return_value = UPDATE_NO_MORE_UPDATES;
+ break;
+ case CL5_MEMORY_ERROR: /* memory allocation failed */
+ slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name,
+ "%s: Memory allocation error occurred\n",
+ agmt_get_long_name(prp->agmt));
+ return_value = UPDATE_FATAL_ERROR;
+ break;
+ case CL5_SYSTEM_ERROR: /* NSPR error occurred: use PR_GetError for furhter info */
+ slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name,
+ "%s: An NSPR error (%d) occurred\n",
+ agmt_get_long_name(prp->agmt), PR_GetError());
+ return_value = UPDATE_TRANSIENT_ERROR;
+ break;
+ case CL5_CSN_ERROR: /* CSN API failed */
+ slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name,
+ "%s: A CSN API failure was encountered\n",
+ agmt_get_long_name(prp->agmt));
+ return_value = UPDATE_TRANSIENT_ERROR;
+ break;
+ case CL5_RUV_ERROR: /* RUV API failed */
+ slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name,
+ "%s: An RUV API failure occurred\n",
+ agmt_get_long_name(prp->agmt));
+ return_value = UPDATE_TRANSIENT_ERROR;
+ break;
+ case CL5_OBJSET_ERROR: /* namedobjset api failed */
+ slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name,
+ "%s: A namedobject API failure occurred\n",
+ agmt_get_long_name(prp->agmt));
+ return_value = UPDATE_TRANSIENT_ERROR;
+ break;
+ case CL5_PURGED_DATA: /* requested data has been purged */
+ slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name,
+ "%s: Data required to update replica has been purged. "
+ "The replica must be reinitialized.\n",
+ agmt_get_long_name(prp->agmt));
+ return_value = UPDATE_FATAL_ERROR;
+ break;
+ case CL5_MISSING_DATA: /* data should be in the changelog, but is missing */
+ slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name,
+ "%s: Missing data encountered\n",
+ agmt_get_long_name(prp->agmt));
+ return_value = UPDATE_FATAL_ERROR;
+ break;
+ case CL5_UNKNOWN_ERROR: /* unclassified error */
+ slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name,
+ "%s: An unknown error was ecountered\n",
+ agmt_get_long_name(prp->agmt));
+ return_value = UPDATE_TRANSIENT_ERROR;
+ break;
+ default:
+ slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name,
+ "%s: An unknown error (%d) occurred "
+ "(cl5CreateReplayIterator)\n",
+ agmt_get_long_name(prp->agmt), rc);
+ return_value = UPDATE_TRANSIENT_ERROR;
+ }
+ }
+ else
+ {
+ int finished = 0;
+ ConnResult replay_crc;
+ char csn_str[CSN_STRSIZE];
+
+ memset ( (void*)&op, 0, sizeof (op) );
+ entry.op = &op;
+ do {
+ cl5_operation_parameters_done ( entry.op );
+ memset ( (void*)entry.op, 0, sizeof (op) );
+ rc = cl5GetNextOperationToReplay(changelog_iterator, &entry);
+ switch (rc)
+ {
+ case CL5_SUCCESS:
+ /* check that we don't return dummy entries */
+ if (is_dummy_operation (entry.op))
+ {
+ slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name,
+ "%s: changelog iteration code returned a dummy entry with csn %s, "
+ "skipping ...\n",
+ agmt_get_long_name(prp->agmt), csn_as_string(entry.op->csn, PR_FALSE, csn_str));
+ continue;
+ }
+ replay_crc = replay_update(prp, entry.op);
+ if (CONN_OPERATION_SUCCESS != replay_crc)
+ {
+ int operation, error;
+ conn_get_error(prp->conn, &operation, &error);
+ csn_as_string(entry.op->csn, PR_FALSE, csn_str);
+ /* Figure out what to do next */
+ if (CONN_OPERATION_FAILED == replay_crc)
+ {
+ /* Map ldap error code to return value */
+ if (!ignore_error_and_keep_going(error))
+ {
+ return_value = UPDATE_TRANSIENT_ERROR;
+ finished = 1;
+ }
+ else
+ {
+ agmt_inc_last_update_changecount (prp->agmt, csn_get_replicaid(entry.op->csn), 1 /*skipped*/);
+ }
+ slapi_log_error(finished ? SLAPI_LOG_FATAL : slapi_log_urp, repl_plugin_name,
+ "%s: Consumer failed to replay change (uniqueid %s, CSN %s): %s. %s.\n",
+ agmt_get_long_name(prp->agmt),
+ entry.op->target_address.uniqueid, csn_str,
+ ldap_err2string(error),
+ finished ? "Will retry later" : "Skipping");
+ }
+ else if (CONN_NOT_CONNECTED == replay_crc)
+ {
+ /* We lost the connection - enter backoff state */
+
+ return_value = UPDATE_TRANSIENT_ERROR;
+ finished = 1;
+ slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name,
+ "%s: Consumer failed to replay change (uniqueid %s, CSN %s): "
+ "%s. Will retry later.\n",
+ agmt_get_long_name(prp->agmt),
+ entry.op->target_address.uniqueid, csn_str,
+ error ? ldap_err2string(error) : "Connection lost");
+ }
+ else if (CONN_TIMEOUT == replay_crc)
+ {
+ return_value = UPDATE_TIMEOUT;
+ finished = 1;
+ slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name,
+ "%s: Consumer timed out to replay change (uniqueid %s, CSN %s): "
+ "%s.\n",
+ agmt_get_long_name(prp->agmt),
+ entry.op->target_address.uniqueid, csn_str,
+ error ? ldap_err2string(error) : "Timeout");
+ }
+ else if (CONN_LOCAL_ERROR == replay_crc)
+ {
+ /*
+ * Something bad happened on the local server - enter
+ * backoff state.
+ */
+ return_value = UPDATE_TRANSIENT_ERROR;
+ finished = 1;
+ slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name,
+ "%s: Failed to replay change (uniqueid %s, CSN %s): "
+ "Local error. Will retry later.\n",
+ agmt_get_long_name(prp->agmt),
+ entry.op->target_address.uniqueid, csn_str);
+ }
+
+ }
+ else
+ {
+ /* Positive response received */
+ (*num_changes_sent)++;
+ agmt_inc_last_update_changecount (prp->agmt, csn_get_replicaid(entry.op->csn), 0 /*replayed*/);
+ }
+ break;
+ case CL5_BAD_DATA:
+ slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name,
+ "%s: Invalid parameter passed to cl5GetNextOperationToReplay\n",
+ agmt_get_long_name(prp->agmt));
+ return_value = UPDATE_FATAL_ERROR;
+ finished = 1;
+ break;
+ case CL5_NOTFOUND:
+ slapi_log_error(SLAPI_LOG_REPL, repl_plugin_name,
+ "%s: No more updates to send (cl5GetNextOperationToReplay)\n",
+ agmt_get_long_name(prp->agmt));
+ return_value = UPDATE_NO_MORE_UPDATES;
+ finished = 1;
+ break;
+ case CL5_DB_ERROR:
+ slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name,
+ "%s: A database error occurred (cl5GetNextOperationToReplay)\n",
+ agmt_get_long_name(prp->agmt));
+ return_value = UPDATE_FATAL_ERROR;
+ finished = 1;
+ break;
+ case CL5_BAD_FORMAT:
+ slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name,
+ "%s: A malformed changelog entry was encountered (cl5GetNextOperationToReplay)\n",
+ agmt_get_long_name(prp->agmt));
+ break;
+ case CL5_MEMORY_ERROR:
+ slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name,
+ "%s: A memory allocation error occurred (cl5GetNextOperationToRepla)\n",
+ agmt_get_long_name(prp->agmt));
+ return_value = UPDATE_FATAL_ERROR;
+ break;
+ default:
+ slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name,
+ "%s: Unknown error code (%d) returned from cl5GetNextOperationToReplay\n",
+ agmt_get_long_name(prp->agmt), rc);
+ return_value = UPDATE_TRANSIENT_ERROR;
+ break;
+ }
+ /* Check for protocol shutdown */
+ if (prp->terminate)
+ {
+ return_value = UPDATE_NO_MORE_UPDATES;
+ finished = 1;
+ }
+ if (*num_changes_sent >= MAX_CHANGES_PER_SESSION)
+ {
+ return_value = UPDATE_YIELD;
+ finished = 1;
+ }
+ } while (!finished);
+ cl5_operation_parameters_done ( entry.op );
+ cl5DestroyReplayIterator(&changelog_iterator);
+ }
+ return return_value;
+}
+
+
+
+/*
+ * XXXggood this should probably be in the superclass, since the full update
+ * protocol is going to need it too.
+ */
+static int
+repl5_inc_stop(Private_Repl_Protocol *prp)
+{
+ int return_value;
+ PRIntervalTime start, maxwait, now;
+ int seconds = 1200;
+
+ maxwait = PR_SecondsToInterval(seconds);
+ prp->terminate = 1;
+ event_notify(prp, EVENT_PROTOCOL_SHUTDOWN);
+ start = PR_IntervalNow();
+ now = start;
+ while (!prp->stopped && ((now - start) < maxwait))
+ {
+ DS_Sleep(PR_SecondsToInterval(1));
+ now = PR_IntervalNow();
+ }
+ if (!prp->stopped)
+ {
+ /* Isn't listening. Do something drastic. */
+ return_value = -1;
+ slapi_log_error(SLAPI_LOG_REPL, repl_plugin_name,
+ "%s: repl5_inc_stop: protocol does not stop after %d seconds\n",
+ agmt_get_long_name(prp->agmt), seconds);
+ }
+ else
+ {
+ return_value = 0;
+ slapi_log_error(SLAPI_LOG_REPL, repl_plugin_name,
+ "%s: repl5_inc_stop: protocol stopped after %d seconds\n",
+ agmt_get_long_name(prp->agmt),
+ PR_IntervalToSeconds(now-start));
+ }
+ return return_value;
+}
+
+
+
+static int
+repl5_inc_status(Private_Repl_Protocol *prp)
+{
+ int return_value = 0;
+
+ return return_value;
+}
+
+
+
+static void
+repl5_inc_notify_update(Private_Repl_Protocol *prp)
+{
+ event_notify(prp, EVENT_TRIGGERING_CRITERIA_MET);
+}
+
+
+static void
+repl5_inc_update_now(Private_Repl_Protocol *prp)
+{
+ event_notify(prp, EVENT_REPLICATE_NOW);
+}
+
+
+static void
+repl5_inc_notify_agmt_changed(Private_Repl_Protocol *prp)
+{
+ event_notify(prp, EVENT_AGMT_CHANGED);
+}
+
+static void
+repl5_inc_notify_window_opened (Private_Repl_Protocol *prp)
+{
+ event_notify(prp, EVENT_WINDOW_OPENED);
+}
+
+static void
+repl5_inc_notify_window_closed (Private_Repl_Protocol *prp)
+{
+ event_notify(prp, EVENT_WINDOW_CLOSED);
+}
+
+Private_Repl_Protocol *
+Repl_5_Inc_Protocol_new(Repl_Protocol *rp)
+{
+ repl5_inc_private *rip = NULL;
+ Private_Repl_Protocol *prp = (Private_Repl_Protocol *)slapi_ch_malloc(sizeof(Private_Repl_Protocol));
+ prp->delete = repl5_inc_delete;
+ prp->run = repl5_inc_run;
+ prp->stop = repl5_inc_stop;
+ prp->status = repl5_inc_status;
+ prp->notify_update = repl5_inc_notify_update;
+ prp->notify_agmt_changed = repl5_inc_notify_agmt_changed;
+ prp->notify_window_opened = repl5_inc_notify_window_opened;
+ prp->notify_window_closed = repl5_inc_notify_window_closed;
+ prp->update_now = repl5_inc_update_now;
+ prp->replica_object = prot_get_replica_object(rp);
+ if ((prp->lock = PR_NewLock()) == NULL)
+ {
+ goto loser;
+ }
+ if ((prp->cvar = PR_NewCondVar(prp->lock)) == NULL)
+ {
+ goto loser;
+ }
+ prp->stopped = 0;
+ prp->terminate = 0;
+ prp->eventbits = 0;
+ prp->conn = prot_get_connection(rp);
+ prp->agmt = prot_get_agreement(rp);
+ prp->last_acquire_response_code = NSDS50_REPL_REPLICA_READY;
+ rip = (void *)slapi_ch_malloc(sizeof(repl5_inc_private));
+ rip->ruv = NULL;
+ rip->backoff = NULL;
+ rip->rp = rp;
+ prp->private = (void *)rip;
+ prp->replica_acquired = PR_FALSE;
+ return prp;
+loser:
+ repl5_inc_delete(&prp);
+ return NULL;
+}
+
+
+
+
+static void
+repl5_inc_backoff_expired(time_t timer_fire_time, void *arg)
+{
+ Private_Repl_Protocol *prp = (Private_Repl_Protocol *)arg;
+ PR_ASSERT(NULL != prp);
+ event_notify(prp, EVENT_BACKOFF_EXPIRED);
+}
+
+
+
+/*
+ * Examine the update vector and determine our course of action.
+ * There are 3 different possibilities, plus a catch-all error:
+ * 1 - no update vector (ruv is NULL). The consumer's replica is
+ * pristine, so it needs to be initialized. Return
+ * EXAMINE_RUV_PRISTINE_REPLICA.
+ * 2 - ruv is present, but its database generation ID doesn't
+ * match the local generation ID. This means that either
+ * the local replica must be reinitialized from the remote
+ * replica or vice-versa. Return
+ * EXAMINE_RUV_GENERATION_MISMATCH.
+ * 3 - ruv is present, and we have all updates needed to bring
+ * the replica up to date using the incremental protocol.
+ * return EXAMINE_RUV_OK.
+ * 4 - parameter error. Return EXAMINE_RUV_PARAM_ERROR
+ */
+static int
+examine_update_vector(Private_Repl_Protocol *prp, RUV *remote_ruv)
+{
+ int return_value;
+
+ PR_ASSERT(NULL != prp);
+ if (NULL == prp)
+ {
+ return_value = EXAMINE_RUV_PARAM_ERROR;
+ }
+ else if (NULL == remote_ruv)
+ {
+ return_value = EXAMINE_RUV_PRISTINE_REPLICA;
+ }
+ else
+ {
+ char *local_gen = NULL;
+ char *remote_gen = ruv_get_replica_generation(remote_ruv);
+ Object *local_ruv_obj;
+ RUV *local_ruv;
+ Replica *replica;
+
+ PR_ASSERT(NULL != prp->replica_object);
+ replica = object_get_data(prp->replica_object);
+ PR_ASSERT(NULL != replica);
+ local_ruv_obj = replica_get_ruv (replica);
+ if (NULL != local_ruv_obj)
+ {
+ local_ruv = (RUV*)object_get_data (local_ruv_obj);
+ PR_ASSERT (local_ruv);
+ local_gen = ruv_get_replica_generation(local_ruv);
+ object_release (local_ruv_obj);
+ }
+ if (NULL == remote_gen || NULL == local_gen || strcmp(remote_gen, local_gen) != 0)
+ {
+ return_value = EXAMINE_RUV_GENERATION_MISMATCH;
+ }
+ else
+ {
+ return_value = EXAMINE_RUV_OK;
+ }
+ slapi_ch_free((void**)&remote_gen);
+ slapi_ch_free((void**)&local_gen);
+ }
+ return return_value;
+}
+
+
+/*
+ * When we get an error from an LDAP operation, we call this
+ * function to decide if we should just keep replaying
+ * updates, or if we should stop, back off, and try again
+ * later.
+ * Returns PR_TRUE if we shoould keep going, PR_FALSE if
+ * we should back off and try again later.
+ *
+ * In general, we keep going if the return code is consistent
+ * with some sort of bug in URP that causes the consumer to
+ * emit an error code that it shouldn't have, e.g. LDAP_ALREADY_EXISTS.
+ *
+ * We stop if there's some indication that the server just completely
+ * failed to process the operation, e.g. LDAP_OPERATIONS_ERROR.
+ */
+static PRBool
+ignore_error_and_keep_going(int error)
+{
+ int return_value;
+
+ switch (error)
+ {
+ /* Cases where we keep going */
+ case LDAP_SUCCESS:
+ case LDAP_NO_SUCH_ATTRIBUTE:
+ case LDAP_UNDEFINED_TYPE:
+ case LDAP_CONSTRAINT_VIOLATION:
+ case LDAP_TYPE_OR_VALUE_EXISTS:
+ case LDAP_INVALID_SYNTAX:
+ case LDAP_NO_SUCH_OBJECT:
+ case LDAP_INVALID_DN_SYNTAX:
+ case LDAP_IS_LEAF:
+ case LDAP_INSUFFICIENT_ACCESS:
+ case LDAP_NAMING_VIOLATION:
+ case LDAP_OBJECT_CLASS_VIOLATION:
+ case LDAP_NOT_ALLOWED_ON_NONLEAF:
+ case LDAP_NOT_ALLOWED_ON_RDN:
+ case LDAP_ALREADY_EXISTS:
+ case LDAP_NO_OBJECT_CLASS_MODS:
+ return_value = PR_TRUE;
+ break;
+
+ /* Cases where we stop and retry */
+ case LDAP_OPERATIONS_ERROR:
+ case LDAP_PROTOCOL_ERROR:
+ case LDAP_TIMELIMIT_EXCEEDED:
+ case LDAP_SIZELIMIT_EXCEEDED:
+ case LDAP_STRONG_AUTH_NOT_SUPPORTED:
+ case LDAP_STRONG_AUTH_REQUIRED:
+ case LDAP_PARTIAL_RESULTS:
+ case LDAP_REFERRAL:
+ case LDAP_ADMINLIMIT_EXCEEDED:
+ case LDAP_UNAVAILABLE_CRITICAL_EXTENSION:
+ case LDAP_CONFIDENTIALITY_REQUIRED:
+ case LDAP_SASL_BIND_IN_PROGRESS:
+ case LDAP_INAPPROPRIATE_MATCHING:
+ case LDAP_ALIAS_PROBLEM:
+ case LDAP_ALIAS_DEREF_PROBLEM:
+ case LDAP_INAPPROPRIATE_AUTH:
+ case LDAP_INVALID_CREDENTIALS:
+ case LDAP_BUSY:
+ case LDAP_UNAVAILABLE:
+ case LDAP_UNWILLING_TO_PERFORM:
+ case LDAP_LOOP_DETECT:
+ case LDAP_SORT_CONTROL_MISSING:
+ case LDAP_INDEX_RANGE_ERROR:
+ case LDAP_RESULTS_TOO_LARGE:
+ case LDAP_AFFECTS_MULTIPLE_DSAS:
+ case LDAP_OTHER:
+ case LDAP_SERVER_DOWN:
+ case LDAP_LOCAL_ERROR:
+ case LDAP_ENCODING_ERROR:
+ case LDAP_DECODING_ERROR:
+ case LDAP_TIMEOUT:
+ case LDAP_AUTH_UNKNOWN:
+ case LDAP_FILTER_ERROR:
+ case LDAP_USER_CANCELLED:
+ case LDAP_PARAM_ERROR:
+ case LDAP_NO_MEMORY:
+ case LDAP_CONNECT_ERROR:
+ case LDAP_NOT_SUPPORTED:
+ case LDAP_CONTROL_NOT_FOUND:
+ case LDAP_NO_RESULTS_RETURNED:
+ case LDAP_MORE_RESULTS_TO_RETURN:
+ case LDAP_CLIENT_LOOP:
+ case LDAP_REFERRAL_LIMIT_EXCEEDED:
+ return_value = PR_FALSE;
+ break;
+ }
+ return return_value;
+}
+
+/* this function converts a state to its name - for debug output */
+static const char*
+state2name (int state)
+{
+ switch (state)
+ {
+ case STATE_START: return "start";
+ case STATE_WAIT_WINDOW_OPEN: return "wait_for_window_to_open";
+ case STATE_WAIT_CHANGES: return "wait_for_changes";
+ case STATE_READY_TO_ACQUIRE: return "ready_to_acquire_replica";
+ case STATE_BACKOFF_START: return "start_backoff";
+ case STATE_BACKOFF: return "backoff";
+ case STATE_SENDING_UPDATES: return "sending_updates";
+ case STATE_STOP_FATAL_ERROR: return "stop_fatal_error";
+ case STATE_STOP_FATAL_ERROR_PART2: return "stop_fatal_error";
+ case STATE_STOP_NORMAL_TERMINATION: return "stop_normal_termination";
+ default: return "invalid_state";
+ }
+}
+
+/* this function convert s an event to its name - for debug output */
+static const char*
+event2name (int event)
+{
+ switch (event)
+ {
+ case EVENT_WINDOW_OPENED: return "update_window_opened";
+ case EVENT_WINDOW_CLOSED: return "update_window_closed";
+ case EVENT_TRIGGERING_CRITERIA_MET: return "data_modified";
+ case EVENT_BACKOFF_EXPIRED: return "backoff_timer_expired";
+ case EVENT_REPLICATE_NOW: return "replicate_now";
+ case EVENT_PROTOCOL_SHUTDOWN: return "protocol_shutdown";
+ case EVENT_AGMT_CHANGED: return "agreement_changed";
+ default: return "invalid_event";
+ }
+}
+
+static const char*
+op2string(int op)
+{
+ switch (op) {
+ case SLAPI_OPERATION_ADD:
+ return "add";
+ case SLAPI_OPERATION_MODIFY:
+ return "modify";
+ case SLAPI_OPERATION_DELETE:
+ return "delete";
+ case SLAPI_OPERATION_MODRDN:
+ return "rename";
+ case SLAPI_OPERATION_EXTENDED:
+ return "extended";
+ }
+
+ return "unknown";
+}
diff --git a/ldap/servers/plugins/replication/repl5_init.c b/ldap/servers/plugins/replication/repl5_init.c
new file mode 100644
index 00000000..eae3b238
--- /dev/null
+++ b/ldap/servers/plugins/replication/repl5_init.c
@@ -0,0 +1,572 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+
+/*
+ repl5_init.c - plugin initialization functions
+*/
+
+/*
+ * Add an entry like the following to dse.ldif to enable this plugin:
+
+dn: cn=Multi-Master Replication Plugin,cn=plugins,cn=config
+objectclass: top
+objectclass: nsSlapdPlugin
+objectclass: extensibleObject
+cn: Legacy Replication Plugin
+nsslapd-pluginpath: /export2/servers/Hydra-supplier/lib/replication-plugin.so
+nsslapd-plugininitfunc: replication_multimaster_plugin_init
+nsslapd-plugintype: object
+nsslapd-pluginenabled: on
+nsslapd-plugin-depends-on-type: database
+nsslapd-plugin-depends-on-named: Class of Service
+nsslapd-pluginid: replication-multimaster
+nsslapd-pluginversion: 5.0b1
+nsslapd-pluginvendor: Netscape Communications
+nsslapd-plugindescription: Multi-Master Replication Plugin
+
+*/
+
+#include "slapi-plugin.h"
+#include "repl.h"
+#include "repl5.h"
+#include "cl5.h" /* changelog interface */
+#include "dirver.h"
+#include <dirlite_strings.h> /* PLUGIN_MAGIC_VENDOR_STR */
+
+/* #ifdef _WIN32
+int *module_ldap_debug = 0;
+
+void plugin_init_debug_level(int *level_ptr)
+{
+ module_ldap_debug = level_ptr;
+}
+#endif*/
+
+#define NSDS_REPL_NAME_PREFIX "Netscape Replication"
+
+static char *start_oid_list[] = {
+ REPL_START_NSDS50_REPLICATION_REQUEST_OID,
+ NULL
+};
+static char *start_name_list[] = {
+ NSDS_REPL_NAME_PREFIX " Start Session",
+ NULL
+};
+static char *end_oid_list[] = {
+ REPL_END_NSDS50_REPLICATION_REQUEST_OID,
+ NULL
+};
+static char *end_name_list[] = {
+ NSDS_REPL_NAME_PREFIX " End Session",
+ NULL
+};
+static char *total_oid_list[] = {
+ REPL_NSDS50_REPLICATION_ENTRY_REQUEST_OID,
+ NULL
+};
+static char *total_name_list[] = {
+ NSDS_REPL_NAME_PREFIX " Total Update Entry",
+ NULL
+};
+static char *response_oid_list[] = {
+ REPL_NSDS50_REPLICATION_RESPONSE_OID,
+ NULL
+};
+static char *response_name_list[] = {
+ NSDS_REPL_NAME_PREFIX " Response",
+ NULL
+};
+
+/* List of plugin identities for every plugin registered. Plugin identity
+ is passed by the server in the plugin init function and must be supplied
+ by the plugin to all internal operations it initiates
+ */
+
+/* ----------------------------- Multi-Master Replication Plugin */
+
+static Slapi_PluginDesc multimasterdesc = {"replication-multimaster", PLUGIN_MAGIC_VENDOR_STR, PRODUCTTEXT, "Multi-master Replication Plugin"};
+static Slapi_PluginDesc multimasterpreopdesc = {"replication-multimaster-preop", PLUGIN_MAGIC_VENDOR_STR, PRODUCTTEXT, "Multi-master replication pre-operation plugin"};
+static Slapi_PluginDesc multimasterpostopdesc = {"replication-multimaster-postop", PLUGIN_MAGIC_VENDOR_STR, PRODUCTTEXT, "Multi-master replication post-operation plugin"};
+static Slapi_PluginDesc multimasterinternalpreopdesc = {"replication-multimaster-internalpreop", PLUGIN_MAGIC_VENDOR_STR, PRODUCTTEXT, "Multi-master replication internal pre-operation plugin"};
+static Slapi_PluginDesc multimasterinternalpostopdesc = {"replication-multimaster-internalpostop", PLUGIN_MAGIC_VENDOR_STR, PRODUCTTEXT, "Multimaster replication internal post-operation plugin"};
+static Slapi_PluginDesc multimasterbepreopdesc = {"replication-multimaster-bepreop", PLUGIN_MAGIC_VENDOR_STR, PRODUCTTEXT, "Multimaster replication bepre-operation plugin"};
+static Slapi_PluginDesc multimasterbepostopdesc = {"replication-multimaster-bepostop", PLUGIN_MAGIC_VENDOR_STR, PRODUCTTEXT, "Multimaster replication bepost-operation plugin"};
+static Slapi_PluginDesc multimasterextopdesc = { "replication-multimaster-extop", PLUGIN_MAGIC_VENDOR_STR, PRODUCTTEXT, "Multimaster replication extended-operation plugin" };
+
+static int multimaster_stopped_flag; /* A flag which is set when all the plugin threads are to stop */
+static int multimaster_started_flag = 0;
+
+/* Thread private data and interface */
+static PRUintn thread_private_agmtname; /* thread private index for logging*/
+static PRUintn thread_private_cache;
+
+char*
+get_thread_private_agmtname()
+{
+ char *agmtname = NULL;
+ if (thread_private_agmtname)
+ agmtname = PR_GetThreadPrivate(thread_private_agmtname);
+ return (agmtname ? agmtname : "");
+}
+
+void
+set_thread_private_agmtname(const char *agmtname)
+{
+ if (thread_private_agmtname)
+ PR_SetThreadPrivate(thread_private_agmtname, (void *)agmtname);
+}
+
+void*
+get_thread_private_cache ()
+{
+ void *buf = NULL;
+ if ( thread_private_cache )
+ buf = PR_GetThreadPrivate ( thread_private_cache );
+ return buf;
+}
+
+void
+set_thread_private_cache ( void *buf )
+{
+ if ( thread_private_cache )
+ PR_SetThreadPrivate ( thread_private_cache, buf );
+}
+
+char*
+get_repl_session_id (Slapi_PBlock *pb, char *idstr, CSN **csn)
+{
+ int connid=-1, opid=-1;
+ CSN *opcsn;
+ char opcsnstr[CSN_STRSIZE];
+
+ *idstr = '\0';
+ opcsn = NULL;
+ opcsnstr[0] = '\0';
+
+ if (pb) {
+ Slapi_Operation *op;
+ slapi_pblock_get (pb, SLAPI_OPERATION_ID, &opid);
+ /* Avoid "Connection is NULL and hence cannot access SLAPI_CONN_ID" */
+ if (opid) {
+ slapi_pblock_get (pb, SLAPI_CONN_ID, &connid);
+ sprintf (idstr, "conn=%d op=%d", connid, opid);
+ }
+
+ slapi_pblock_get ( pb, SLAPI_OPERATION, &op );
+ opcsn = operation_get_csn (op);
+ if (opcsn) {
+ csn_as_string (opcsn, PR_FALSE, opcsnstr);
+ strcat (idstr, " csn=");
+ strcat (idstr, opcsnstr);
+ }
+ }
+ if (csn) {
+ *csn = opcsn;
+ }
+ return idstr;
+}
+
+
+int
+multimaster_preop_init( Slapi_PBlock *pb )
+{
+ int rc= 0; /* OK */
+
+ if( slapi_pblock_set( pb, SLAPI_PLUGIN_VERSION, SLAPI_PLUGIN_VERSION_01 ) != 0 ||
+ slapi_pblock_set( pb, SLAPI_PLUGIN_DESCRIPTION, (void *)&multimasterpreopdesc ) != 0 ||
+ slapi_pblock_set( pb, SLAPI_PLUGIN_PRE_BIND_FN, (void *) multimaster_preop_bind ) != 0 ||
+ slapi_pblock_set( pb, SLAPI_PLUGIN_PRE_ADD_FN, (void *) multimaster_preop_add ) != 0 ||
+ slapi_pblock_set( pb, SLAPI_PLUGIN_PRE_DELETE_FN, (void *) multimaster_preop_delete ) != 0 ||
+ slapi_pblock_set( pb, SLAPI_PLUGIN_PRE_MODIFY_FN, (void *) multimaster_preop_modify ) != 0 ||
+ slapi_pblock_set( pb, SLAPI_PLUGIN_PRE_MODRDN_FN, (void *) multimaster_preop_modrdn ) != 0 ||
+ slapi_pblock_set( pb, SLAPI_PLUGIN_PRE_SEARCH_FN, (void *) multimaster_preop_search ) != 0 ||
+ slapi_pblock_set( pb, SLAPI_PLUGIN_PRE_COMPARE_FN, (void *) multimaster_preop_compare ) != 0)
+ {
+ slapi_log_error( SLAPI_LOG_PLUGIN, repl_plugin_name, "multimaster_preop_init failed\n" );
+ rc= -1;
+ }
+ return rc;
+}
+
+int
+multimaster_postop_init( Slapi_PBlock *pb )
+{
+ int rc= 0; /* OK */
+
+ if( slapi_pblock_set( pb, SLAPI_PLUGIN_VERSION, SLAPI_PLUGIN_VERSION_01 ) != 0 ||
+ slapi_pblock_set( pb, SLAPI_PLUGIN_DESCRIPTION, (void *)&multimasterpostopdesc ) != 0 ||
+ slapi_pblock_set( pb, SLAPI_PLUGIN_POST_BIND_FN, (void *) multimaster_postop_bind ) != 0 ||
+ slapi_pblock_set( pb, SLAPI_PLUGIN_POST_ADD_FN, (void *) multimaster_postop_add ) != 0 ||
+ slapi_pblock_set( pb, SLAPI_PLUGIN_POST_DELETE_FN, (void *) multimaster_postop_delete ) != 0 ||
+ slapi_pblock_set( pb, SLAPI_PLUGIN_POST_MODIFY_FN, (void *) multimaster_postop_modify ) != 0 ||
+ slapi_pblock_set( pb, SLAPI_PLUGIN_POST_MODRDN_FN, (void *) multimaster_postop_modrdn ) != 0 )
+ {
+ slapi_log_error( SLAPI_LOG_PLUGIN, repl_plugin_name, "multimaster_postop_init failed\n" );
+ rc= -1;
+ }
+
+ return rc;
+}
+
+int
+multimaster_internalpreop_init( Slapi_PBlock *pb )
+{
+ int rc= 0; /* OK */
+
+ if( slapi_pblock_set( pb, SLAPI_PLUGIN_VERSION, SLAPI_PLUGIN_VERSION_01 ) != 0 ||
+ slapi_pblock_set( pb, SLAPI_PLUGIN_DESCRIPTION, (void *)&multimasterinternalpreopdesc ) != 0 ||
+ slapi_pblock_set( pb, SLAPI_PLUGIN_INTERNAL_PRE_ADD_FN, (void *) multimaster_preop_add ) != 0 ||
+ slapi_pblock_set( pb, SLAPI_PLUGIN_INTERNAL_PRE_DELETE_FN, (void *) multimaster_preop_delete ) != 0 ||
+ slapi_pblock_set( pb, SLAPI_PLUGIN_INTERNAL_PRE_MODIFY_FN, (void *) multimaster_preop_modify ) != 0 ||
+ slapi_pblock_set( pb, SLAPI_PLUGIN_INTERNAL_PRE_MODRDN_FN, (void *) multimaster_preop_modrdn ) != 0 )
+ {
+ slapi_log_error( SLAPI_LOG_PLUGIN, repl_plugin_name, "multimaster_internalpreop_init failed\n" );
+ rc= -1;
+ }
+ return rc;
+}
+
+int
+multimaster_internalpostop_init( Slapi_PBlock *pb )
+{
+ int rc= 0; /* OK */
+
+ if( slapi_pblock_set( pb, SLAPI_PLUGIN_VERSION, SLAPI_PLUGIN_VERSION_01 ) != 0 ||
+ slapi_pblock_set( pb, SLAPI_PLUGIN_DESCRIPTION, (void *)&multimasterinternalpostopdesc ) != 0 ||
+ slapi_pblock_set( pb, SLAPI_PLUGIN_INTERNAL_POST_ADD_FN, (void *) multimaster_postop_add ) != 0 ||
+ slapi_pblock_set( pb, SLAPI_PLUGIN_INTERNAL_POST_DELETE_FN, (void *) multimaster_postop_delete ) != 0 ||
+ slapi_pblock_set( pb, SLAPI_PLUGIN_INTERNAL_POST_MODIFY_FN, (void *) multimaster_postop_modify ) != 0 ||
+ slapi_pblock_set( pb, SLAPI_PLUGIN_INTERNAL_POST_MODRDN_FN, (void *) multimaster_postop_modrdn ) != 0 )
+ {
+ slapi_log_error( SLAPI_LOG_PLUGIN, repl_plugin_name, "multimaster_internalpostop_init failed\n" );
+ rc= -1;
+ }
+
+ return rc;
+}
+
+int
+multimaster_bepreop_init( Slapi_PBlock *pb )
+{
+ int rc= 0; /* OK */
+
+ if( slapi_pblock_set( pb, SLAPI_PLUGIN_VERSION, SLAPI_PLUGIN_VERSION_01 ) != 0 ||
+ slapi_pblock_set( pb, SLAPI_PLUGIN_DESCRIPTION, (void *)&multimasterbepreopdesc ) != 0 ||
+ slapi_pblock_set( pb, SLAPI_PLUGIN_BE_PRE_ADD_FN, (void *) multimaster_bepreop_add ) != 0 ||
+ slapi_pblock_set( pb, SLAPI_PLUGIN_BE_PRE_DELETE_FN, (void *) multimaster_bepreop_delete ) != 0 ||
+ slapi_pblock_set( pb, SLAPI_PLUGIN_BE_PRE_MODIFY_FN, (void *) multimaster_bepreop_modify ) != 0 ||
+ slapi_pblock_set( pb, SLAPI_PLUGIN_BE_PRE_MODRDN_FN, (void *) multimaster_bepreop_modrdn ) != 0 )
+ {
+ slapi_log_error( SLAPI_LOG_PLUGIN, repl_plugin_name, "multimaster_bepreop_init failed\n" );
+ rc= -1;
+ }
+
+ return rc;
+}
+
+int
+multimaster_bepostop_init( Slapi_PBlock *pb )
+{
+ int rc= 0; /* OK */
+
+ if( slapi_pblock_set( pb, SLAPI_PLUGIN_VERSION, SLAPI_PLUGIN_VERSION_01 ) != 0 ||
+ slapi_pblock_set( pb, SLAPI_PLUGIN_DESCRIPTION, (void *)&multimasterbepostopdesc ) != 0 ||
+ slapi_pblock_set( pb, SLAPI_PLUGIN_BE_POST_MODRDN_FN, (void *) multimaster_bepostop_modrdn ) != 0 ||
+ slapi_pblock_set( pb, SLAPI_PLUGIN_BE_POST_DELETE_FN, (void *) multimaster_bepostop_delete ) != 0 )
+ {
+ slapi_log_error( SLAPI_LOG_PLUGIN, repl_plugin_name, "multimaster_bepostop_init failed\n" );
+ rc= -1;
+ }
+
+ return rc;
+}
+
+int
+multimaster_start_extop_init( Slapi_PBlock *pb )
+{
+ int rc= 0; /* OK */
+
+ if ( slapi_pblock_set( pb, SLAPI_PLUGIN_VERSION, SLAPI_PLUGIN_VERSION_01 ) != 0 ||
+ slapi_pblock_set( pb, SLAPI_PLUGIN_DESCRIPTION, (void *)&multimasterextopdesc ) != 0 ||
+ slapi_pblock_set( pb, SLAPI_PLUGIN_EXT_OP_OIDLIST, (void *)start_oid_list ) != 0 ||
+ slapi_pblock_set( pb, SLAPI_PLUGIN_EXT_OP_NAMELIST, (void *)start_name_list ) != 0 ||
+ slapi_pblock_set( pb, SLAPI_PLUGIN_EXT_OP_FN, (void *)multimaster_extop_StartNSDS50ReplicationRequest ))
+ {
+ slapi_log_error( SLAPI_LOG_PLUGIN, repl_plugin_name, "multimaster_start_extop_init (StartNSDS50ReplicationRequest) failed\n" );
+ rc= -1;
+ }
+
+
+ return rc;
+}
+
+
+int
+multimaster_end_extop_init( Slapi_PBlock *pb )
+{
+ int rc= 0; /* OK */
+
+
+ if ( slapi_pblock_set( pb, SLAPI_PLUGIN_VERSION, SLAPI_PLUGIN_VERSION_01 ) != 0 ||
+ slapi_pblock_set( pb, SLAPI_PLUGIN_DESCRIPTION, (void *)&multimasterextopdesc ) != 0 ||
+ slapi_pblock_set( pb, SLAPI_PLUGIN_EXT_OP_OIDLIST, (void *)end_oid_list ) != 0 ||
+ slapi_pblock_set( pb, SLAPI_PLUGIN_EXT_OP_NAMELIST, (void *)end_name_list ) != 0 ||
+ slapi_pblock_set( pb, SLAPI_PLUGIN_EXT_OP_FN, (void *)multimaster_extop_EndNSDS50ReplicationRequest ))
+ {
+ slapi_log_error( SLAPI_LOG_PLUGIN, repl_plugin_name, "multimaster_end_extop_init (EndNSDS50ReplicationRequest) failed\n" );
+ rc= -1;
+ }
+
+ return rc;
+}
+
+
+int
+multimaster_total_extop_init( Slapi_PBlock *pb )
+{
+ int rc= 0; /* OK */
+ void *identity = NULL;
+
+ /* get plugin identity and store it to pass to internal operations */
+ slapi_pblock_get (pb, SLAPI_PLUGIN_IDENTITY, &identity);
+ PR_ASSERT (identity);
+
+ if ( slapi_pblock_set( pb, SLAPI_PLUGIN_VERSION, SLAPI_PLUGIN_VERSION_01 ) != 0 ||
+ slapi_pblock_set( pb, SLAPI_PLUGIN_DESCRIPTION, (void *)&multimasterextopdesc ) != 0 ||
+ slapi_pblock_set( pb, SLAPI_PLUGIN_EXT_OP_OIDLIST, (void *)total_oid_list ) != 0 ||
+ slapi_pblock_set( pb, SLAPI_PLUGIN_EXT_OP_NAMELIST, (void *)total_name_list ) != 0 ||
+ slapi_pblock_set( pb, SLAPI_PLUGIN_EXT_OP_FN, (void *)multimaster_extop_NSDS50ReplicationEntry ))
+ {
+ slapi_log_error( SLAPI_LOG_PLUGIN, repl_plugin_name, "multimaster_start_extop_init (NSDS50ReplicationEntry failed\n" );
+ rc= -1;
+ }
+
+ return rc;
+}
+
+int
+multimaster_response_extop_init( Slapi_PBlock *pb )
+{
+ int rc= 0; /* OK */
+ void *identity = NULL;
+
+ /* get plugin identity and store it to pass to internal operations */
+ slapi_pblock_get (pb, SLAPI_PLUGIN_IDENTITY, &identity);
+ PR_ASSERT (identity);
+
+ if ( slapi_pblock_set( pb, SLAPI_PLUGIN_VERSION, SLAPI_PLUGIN_VERSION_01 ) != 0 ||
+ slapi_pblock_set( pb, SLAPI_PLUGIN_DESCRIPTION, (void *)&multimasterextopdesc ) != 0 ||
+ slapi_pblock_set( pb, SLAPI_PLUGIN_EXT_OP_OIDLIST, (void *)response_oid_list ) != 0 ||
+ slapi_pblock_set( pb, SLAPI_PLUGIN_EXT_OP_NAMELIST, (void *)response_name_list ) != 0 ||
+ slapi_pblock_set( pb, SLAPI_PLUGIN_EXT_OP_FN, (void *)extop_noop ))
+ {
+ slapi_log_error( SLAPI_LOG_PLUGIN, repl_plugin_name, "multimaster_start_extop_init (NSDS50ReplicationResponse failed\n" );
+ rc= -1;
+ }
+
+ return rc;
+}
+
+
+static PRBool
+check_for_ldif_dump(Slapi_PBlock *pb)
+{
+ int i;
+ int argc;
+ char **argv;
+ PRBool return_value = PR_FALSE;
+
+ slapi_pblock_get( pb, SLAPI_ARGC, &argc);
+ slapi_pblock_get( pb, SLAPI_ARGV, &argv);
+
+ for (i = 1; i < argc && !return_value; i++)
+ {
+ if (strcmp(argv[i], "db2ldif") == 0)
+ {
+ return_value = PR_TRUE;
+ }
+ }
+ return return_value;
+}
+
+
+static PRBool is_ldif_dump = PR_FALSE;
+
+int
+multimaster_start( Slapi_PBlock *pb )
+{
+ int rc= 0; /* OK */
+
+ if (!multimaster_started_flag)
+ {
+ /* Initialize thread private data for logging. Ignore if fails */
+ PR_NewThreadPrivateIndex (&thread_private_agmtname, NULL);
+ PR_NewThreadPrivateIndex (&thread_private_cache, NULL);
+
+ /* Decode the command line args to see if we're dumping to LDIF */
+ is_ldif_dump = check_for_ldif_dump(pb);
+
+ /* allow online replica configuration */
+ rc = replica_config_init ();
+ if (rc != 0)
+ goto out;
+
+ slapi_register_supported_control(REPL_NSDS50_UPDATE_INFO_CONTROL_OID,
+ SLAPI_OPERATION_ADD | SLAPI_OPERATION_DELETE |
+ SLAPI_OPERATION_MODIFY | SLAPI_OPERATION_MODDN);
+
+ /* Stash away our partial URL, used in RUVs */
+ rc = multimaster_set_local_purl();
+ if (rc != 0)
+ goto out;
+
+ /* Initialise support for cn=monitor */
+ rc = repl_monitor_init();
+ if (rc != 0)
+ goto out;
+
+ /* initialize name hash */
+ rc = replica_init_name_hash ();
+ if (rc != 0)
+ goto out;
+
+ /* initialize dn hash */
+ rc = replica_init_dn_hash ();
+ if (rc != 0)
+ goto out;
+
+ /* create replicas */
+ multimaster_mtnode_construct_replicas ();
+
+ /* Initialise the 5.0 Changelog */
+ rc = changelog5_init();
+ if (rc != 0)
+ goto out;
+
+ /* Initialize the replication agreements, unless we're dumping LDIF */
+ if (!is_ldif_dump)
+ {
+ rc = agmtlist_config_init();
+ if (rc != 0)
+ goto out;
+ }
+
+ /* check if the replica's data was reloaded offline and we need
+ to reinitialize replica's changelog. This should be done
+ after the changelog is initialized */
+
+ replica_enumerate_replicas (replica_check_for_data_reload, NULL);
+
+ /* register to be notified when backend state changes */
+ slapi_register_backend_state_change((void *)multimaster_be_state_change,
+ multimaster_be_state_change);
+
+ multimaster_started_flag = 1;
+ multimaster_stopped_flag = 0;
+ }
+out:
+ return rc;
+}
+
+int
+multimaster_stop( Slapi_PBlock *pb )
+{
+ int rc= 0; /* OK */
+
+ if (!multimaster_stopped_flag)
+ {
+ if (!is_ldif_dump)
+ {
+ agmtlist_shutdown(); /* Shut down replication agreements */
+ }
+
+ /* unregister backend state change notification */
+ slapi_unregister_backend_state_change((void *)multimaster_be_state_change);
+
+ changelog5_cleanup(); /* Shut down the changelog */
+ multimaster_mtnode_extension_destroy(); /* Destroy mapping tree node exts */
+ replica_destroy_name_hash(); /* destroy the hash and its remaining content */
+ replica_config_destroy (); /* Destroy replica config info */
+ multimaster_stopped_flag = 1;
+ /* JCMREPL - Wait for all our threads to stop */
+ /* JCMREPL - Shut down the replication plugin */
+ /* JCMREPL - Mark all the replication plugin interfaces at not enabled. */
+ }
+ return rc;
+}
+
+
+PRBool
+multimaster_started()
+{
+ return(multimaster_started_flag != 0);
+}
+
+
+/*
+ * Initialize the multimaster replication plugin.
+ */
+int replication_multimaster_plugin_init(Slapi_PBlock *pb)
+{
+ static int multimaster_initialised= 0;
+ int rc= 0; /* OK */
+ void *identity = NULL;
+
+ slapi_pblock_get (pb, SLAPI_PLUGIN_IDENTITY, &identity);
+ PR_ASSERT (identity);
+ repl_set_plugin_identity (PLUGIN_MULTIMASTER_REPLICATION, identity);
+
+ /* need the repl plugin path for the chain on update function */
+/* slapi_pblock_get(pb, SLAPI_ADD_ENTRY, &entry);
+ PR_ASSERT(entry);
+ path = slapi_entry_attr_get_charptr(entry, "nsslapd-pluginpath");
+ repl_set_repl_plugin_path(path);
+ slapi_ch_free_string(&path);
+*/
+ multimaster_mtnode_extension_init ();
+
+ if(config_is_slapd_lite())
+ {
+ slapi_log_error( SLAPI_LOG_FATAL, repl_plugin_name,
+ "replication plugin not approved for restricted"
+ " mode Directory Server.\n" );
+ rc= -1;
+ }
+ if(rc==0 && !multimaster_initialised)
+ {
+ /* initialize replica hash - has to be done before mapping tree is
+ initialized so we can't do it in the start function */
+
+ if (rc != 0)
+ {
+ slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name,
+ "replication_multimaster_plugin_init: failed to initialize replica hash\n");
+ return -1;
+ }
+
+ /* Initialize extensions */
+ repl_con_init_ext();
+ repl_sup_init_ext();
+
+ rc= slapi_pblock_set( pb, SLAPI_PLUGIN_VERSION, SLAPI_PLUGIN_VERSION_01 );
+ rc= slapi_pblock_set( pb, SLAPI_PLUGIN_DESCRIPTION, (void *)&multimasterdesc );
+ rc= slapi_pblock_set( pb, SLAPI_PLUGIN_START_FN, (void *) multimaster_start );
+ rc= slapi_pblock_set( pb, SLAPI_PLUGIN_CLOSE_FN, (void *) multimaster_stop );
+
+ /* Register the plugin interfaces we implement */
+ rc= slapi_register_plugin("preoperation", 1 /* Enabled */, "multimaster_preop_init", multimaster_preop_init, "Multimaster replication preoperation plugin", NULL, identity);
+ rc= slapi_register_plugin("postoperation", 1 /* Enabled */, "multimaster_postop_init", multimaster_postop_init, "Multimaster replication postoperation plugin", NULL, identity);
+ rc= slapi_register_plugin("bepreoperation", 1 /* Enabled */, "multimaster_bepreop_init", multimaster_bepreop_init, "Multimaster replication bepreoperation plugin", NULL, identity);
+ rc= slapi_register_plugin("bepostoperation", 1 /* Enabled */, "multimaster_bepostop_init", multimaster_bepostop_init, "Multimaster replication bepostoperation plugin", NULL, identity);
+ rc= slapi_register_plugin("internalpreoperation", 1 /* Enabled */, "multimaster_internalpreop_init", multimaster_internalpreop_init, "Multimaster replication internal preoperation plugin", NULL, identity);
+ rc= slapi_register_plugin("internalpostoperation", 1 /* Enabled */, "multimaster_internalpostop_init", multimaster_internalpostop_init, "Multimaster replication internal postoperation plugin", NULL, identity);
+ rc= slapi_register_plugin("extendedop", 1 /* Enabled */, "multimaster_start_extop_init", multimaster_start_extop_init, "Multimaster replication start extended operation plugin", NULL, identity);
+ rc= slapi_register_plugin("extendedop", 1 /* Enabled */, "multimaster_end_extop_init", multimaster_end_extop_init, "Multimaster replication end extended operation plugin", NULL, identity);
+ rc= slapi_register_plugin("extendedop", 1 /* Enabled */, "multimaster_total_extop_init", multimaster_total_extop_init, "Multimaster replication total update extended operation plugin", NULL, identity);
+ rc= slapi_register_plugin("extendedop", 1 /* Enabled */, "multimaster_response_extop_init", multimaster_response_extop_init, "Multimaster replication extended response plugin", NULL, identity);
+ }
+ return rc;
+}
diff --git a/ldap/servers/plugins/replication/repl5_mtnode_ext.c b/ldap/servers/plugins/replication/repl5_mtnode_ext.c
new file mode 100644
index 00000000..e677927c
--- /dev/null
+++ b/ldap/servers/plugins/replication/repl5_mtnode_ext.c
@@ -0,0 +1,194 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+
+/* repl5_replica.c */
+
+#include "repl.h" /* ONREPL - this is bad */
+#include "repl5.h"
+#include "cl5_api.h"
+
+/* global data */
+static DataList *root_list;
+
+/*
+ * Mapping tree node extension management. Node stores replica object
+ */
+
+void
+multimaster_mtnode_extension_init ()
+{
+ /* Initialize list that store node roots. It is used during
+ plugin startup to create replica objects */
+ root_list = dl_new ();
+ dl_init (root_list, 0);
+}
+
+void
+multimaster_mtnode_extension_destroy ()
+{
+ dl_cleanup (root_list, (FREEFN)slapi_sdn_free);
+ dl_free (&root_list);
+}
+
+/* This function loops over the list of node roots, constructing replica objects
+ where exist */
+void
+multimaster_mtnode_construct_replicas ()
+{
+ Slapi_DN *root;
+ int cookie;
+ Replica *r;
+ mapping_tree_node *mtnode;
+ multimaster_mtnode_extension *ext;
+
+ for (root = dl_get_first (root_list, &cookie); root;
+ root = dl_get_next (root_list, &cookie))
+ {
+ r = replica_new(root);
+ if (r)
+ {
+
+ mtnode = slapi_get_mapping_tree_node_by_dn(root);
+ if (mtnode == NULL)
+ {
+ slapi_log_error(SLAPI_LOG_REPL, repl_plugin_name,
+ "multimaster_mtnode_construct_replicas: "
+ "failed to locate mapping tree node for %s\n",
+ slapi_sdn_get_dn (root));
+ continue;
+ }
+
+ ext = (multimaster_mtnode_extension *)repl_con_get_ext (REPL_CON_EXT_MTNODE, mtnode);
+ if (ext == NULL)
+ {
+ slapi_log_error(SLAPI_LOG_REPL, repl_plugin_name, "multimaster_mtnode_construct_replicas: "
+ "failed to locate replication extension of mapping tree node for %s\n",
+ slapi_sdn_get_dn (root));
+ continue;
+ }
+
+ ext->replica = object_new(r, replica_destroy);
+ if (replica_add_by_name (replica_get_name (r), ext->replica) != 0)
+ {
+ object_release (ext->replica);
+ ext->replica = NULL;
+ }
+ }
+ }
+}
+
+void *
+multimaster_mtnode_extension_constructor (void *object, void *parent)
+{
+ mapping_tree_node *node;
+ const Slapi_DN *root;
+ multimaster_mtnode_extension *ext;
+
+ ext = (multimaster_mtnode_extension *)slapi_ch_calloc (1, sizeof (multimaster_mtnode_extension));
+
+ node = (mapping_tree_node *)object;
+
+ /* replica can be attached only to local public data */
+ if (slapi_mapping_tree_node_is_set (node, SLAPI_MTN_LOCAL) &&
+ !slapi_mapping_tree_node_is_set (node, SLAPI_MTN_PRIVATE))
+ {
+ root = slapi_get_mapping_tree_node_root (node);
+ /* ONREPL - we don't create replica object here because
+ we can't fully initialize replica here since backends
+ are not yet started. Instead, replica objects are created
+ during replication plugin startup */
+ if (root)
+ {
+ /* for now just store node root in the root list */
+ dl_add (root_list, slapi_sdn_dup (root));
+ }
+ }
+
+ return ext;
+}
+
+void
+multimaster_mtnode_extension_destructor (void* ext, void *object, void *parent)
+{
+ if (ext)
+ {
+ multimaster_mtnode_extension *mtnode_ext = (multimaster_mtnode_extension *)ext;
+ if (mtnode_ext->replica)
+ {
+ object_release (mtnode_ext->replica);
+ mtnode_ext->replica = NULL;
+ }
+ }
+}
+
+Object *
+replica_get_replica_from_dn (const Slapi_DN *dn)
+{
+ mapping_tree_node *mtnode;
+ multimaster_mtnode_extension *ext;
+
+ if (dn == NULL)
+ return NULL;
+
+ mtnode = slapi_get_mapping_tree_node_by_dn(dn);
+ if (mtnode == NULL)
+ {
+ slapi_log_error(SLAPI_LOG_REPL, repl_plugin_name, "replica_get_replica_from_dn: "
+ "failed to locate mapping tree node for %s\n",
+ slapi_sdn_get_dn (dn));
+ return NULL;
+ }
+
+ ext = (multimaster_mtnode_extension *)repl_con_get_ext (REPL_CON_EXT_MTNODE, mtnode);
+ if (ext == NULL)
+ {
+ slapi_log_error(SLAPI_LOG_REPL, repl_plugin_name, "replica_get_replica_from_dn: "
+ "failed to locate replication extension of mapping tree node for %s\n",
+ slapi_sdn_get_dn (dn));
+ return NULL;
+ }
+
+ if (ext->replica)
+ object_acquire (ext->replica);
+
+ return ext->replica;
+}
+
+Object *replica_get_replica_for_op (Slapi_PBlock *pb)
+{
+ char *dn;
+ Slapi_DN *sdn;
+ Object *repl_obj = NULL;
+
+ if (pb)
+ {
+ /* get replica generation for this operation */
+ slapi_pblock_get (pb, SLAPI_TARGET_DN, &dn);
+ sdn = slapi_sdn_new_dn_byref(dn);
+ repl_obj = replica_get_replica_from_dn (sdn);
+
+ slapi_sdn_free (&sdn);
+ }
+
+ return repl_obj;
+}
+
+Object *replica_get_for_backend (const char *be_name)
+{
+ Slapi_Backend *be;
+ const Slapi_DN *suffix;
+ Object *r_obj;
+
+ be = slapi_be_select_by_instance_name(be_name);
+ if (NULL == be)
+ return NULL;
+
+ suffix = slapi_be_getsuffix(be, 0);
+
+ r_obj = replica_get_replica_from_dn (suffix);
+
+ return r_obj;
+}
diff --git a/ldap/servers/plugins/replication/repl5_plugins.c b/ldap/servers/plugins/replication/repl5_plugins.c
new file mode 100644
index 00000000..afee321a
--- /dev/null
+++ b/ldap/servers/plugins/replication/repl5_plugins.c
@@ -0,0 +1,1416 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+
+/*
+ * repl5_consumer.c - consumer plugin functions
+ */
+
+/*
+ * LDAP Data Model Constraints...
+ *
+ * 1) Parent entry must exist.
+ * 2) RDN must be unique.
+ * 3) RDN components must be attribute values.
+ * 4) Must conform to the schema constraints - Single Valued Attributes.
+ * 5) Must conform to the schema constraints - Required Attributes.
+ * 6) Must conform to the schema constraints - No Extra Attributes.
+ * 7) Duplicate attribute values are not permited.
+ * 8) Cycle in the ancestry graph not permitted.
+ *
+ * Update Resolution Procedures...
+ * 1) ...
+ * 2) Add the UniqueID to the RDN.
+ * 3) Remove the not present RDN component.
+ * Use the UniqueID if the RDN becomes empty.
+ * 4) Keep the most recent value.
+ * 5) Ignore.
+ * 6) Ignore.
+ * 7) Keep the largest CSN for the duplicate value.
+ * 8) Don't check for this.
+ */
+
+#include "repl5.h"
+#include "repl.h"
+#include "cl5_api.h"
+#include "urp.h"
+
+static char *local_purl = NULL;
+static char *purl_attrs[] = {"nsslapd-localhost", "nsslapd-port", "nsslapd-secureport", NULL};
+
+/* Forward declarations */
+static void copy_operation_parameters(Slapi_PBlock *pb);
+static int write_changelog_and_ruv(Slapi_PBlock *pb);
+static int process_postop (Slapi_PBlock *pb);
+static int cancel_opcsn (Slapi_PBlock *pb);
+static int ruv_tombstone_op (Slapi_PBlock *pb);
+static PRBool process_operation (Slapi_PBlock *pb, const CSN *csn);
+static PRBool is_mmr_replica (Slapi_PBlock *pb);
+static const char *replica_get_purl_for_op (const Replica *r, Slapi_PBlock *pb, const CSN *opcsn);
+static void strip_legacy_info (slapi_operation_parameters *op_params);
+static void close_changelog_for_replica (Object *r_obj);
+static void process_new_ruv_for_replica (Replica *r);
+
+/*
+ * XXXggood - what to do if both ssl and non-ssl ports available? How
+ * do you know which to use? Offer a choice in replication config?
+ */
+int
+multimaster_set_local_purl()
+{
+ int rc = 0;
+ Slapi_Entry **entries;
+ Slapi_PBlock *pb = NULL;
+
+ pb = slapi_pblock_new ();
+
+ slapi_search_internal_set_pb (pb, "cn=config", LDAP_SCOPE_BASE,
+ "objectclass=*", purl_attrs, 0, NULL, NULL,
+ repl_get_plugin_identity (PLUGIN_MULTIMASTER_REPLICATION), 0);
+ slapi_search_internal_pb (pb);
+ slapi_pblock_get(pb, SLAPI_PLUGIN_INTOP_RESULT, &rc);
+ if (rc != 0)
+ {
+ slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name, "multimaster_set_local_purl: "
+ "unable to read server configuration: error %d\n", rc);
+ }
+ else
+ {
+ slapi_pblock_get(pb, SLAPI_PLUGIN_INTOP_SEARCH_ENTRIES, &entries);
+ if (NULL == entries || NULL == entries[0])
+ {
+ slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name, "multimaster_set_local_purl: "
+ "server configuration missing\n");
+ rc = -1;
+ }
+ else
+ {
+ char *host = slapi_entry_attr_get_charptr(entries[0], "nsslapd-localhost");
+ char *port = slapi_entry_attr_get_charptr(entries[0], "nsslapd-port");
+ char *sslport = slapi_entry_attr_get_charptr(entries[0], "nsslapd-secureport");
+ if (host == NULL || ((port == NULL && sslport == NULL)))
+ {
+ slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name,
+ "multimaster_set_local_purl: invalid server "
+ "configuration\n");
+ }
+ else
+ {
+ int len = 0;
+ char *patt = "ldap://%s:%s";
+ len += strlen(host);
+ len += strlen(port);
+ len += strlen(patt);
+ len++; /* for \0 */
+ local_purl = slapi_ch_malloc(len);
+ sprintf(local_purl, patt, host, port);
+ }
+
+ /* slapi_ch_free acceptS NULL pointer */
+ slapi_ch_free ((void**)&host);
+ slapi_ch_free ((void**)&port);
+ slapi_ch_free ((void**)&sslport);
+ }
+ }
+ slapi_free_search_results_internal(pb);
+ slapi_pblock_destroy (pb);
+
+ return rc;
+}
+
+
+const char *
+multimaster_get_local_purl()
+{
+ return local_purl;
+}
+
+
+/* ================= Multimaster Pre-Op Plugin Points ================== */
+
+
+int
+multimaster_preop_bind (Slapi_PBlock *pb)
+{
+ return 0;
+}
+
+int
+multimaster_preop_add (Slapi_PBlock *pb)
+{
+ Slapi_Operation *op;
+ int is_replicated_operation;
+ int is_fixup_operation;
+ int is_legacy_operation;
+
+ slapi_pblock_get(pb, SLAPI_OPERATION, &op);
+
+ /* If there is no replica or it is a legacy consumer - we don't need to continue.
+ Legacy plugin is handling 4.0 consumer code */
+ /* but if it is legacy, csngen_handler needs to be assigned here */
+ is_legacy_operation =
+ operation_is_flag_set(op,OP_FLAG_LEGACY_REPLICATION_DN);
+ if (is_legacy_operation)
+ {
+ copy_operation_parameters(pb);
+ slapi_operation_set_csngen_handler(op,
+ (void*)replica_generate_next_csn);
+ return 0;
+ }
+
+ if (!is_mmr_replica (pb))
+ {
+ copy_operation_parameters(pb);
+ return 0;
+ }
+
+ is_replicated_operation= operation_is_flag_set(op,OP_FLAG_REPLICATED);
+ is_fixup_operation= operation_is_flag_set(op,OP_FLAG_REPL_FIXUP);
+
+ if(is_replicated_operation)
+ {
+ if(!is_fixup_operation)
+ {
+ LDAPControl **ctrlp;
+ char sessionid[REPL_SESSION_ID_SIZE];
+ get_repl_session_id (pb, sessionid, NULL);
+ slapi_pblock_get(pb, SLAPI_REQCONTROLS, &ctrlp);
+ if (NULL != ctrlp)
+ {
+ CSN *csn = NULL;
+ char *target_uuid = NULL;
+ char *superior_uuid= NULL;
+ int drc = decode_NSDS50ReplUpdateInfoControl(ctrlp, &target_uuid, &superior_uuid, &csn, NULL /* modrdn_mods */);
+ if (-1 == drc)
+ {
+ slapi_log_error(SLAPI_LOG_FATAL, REPLICATION_SUBSYSTEM,
+ "%s An error occurred while decoding the replication update "
+ "control - Add\n", sessionid);
+ }
+ else if (1 == drc)
+ {
+ /*
+ * For add operations, we just set the operation csn. The entry's
+ * uniqueid should already be an attribute of the replicated entry.
+ */
+ struct slapi_operation_parameters *op_params;
+
+ /* we don't want to process replicated operations with csn smaller
+ than the corresponding csn in the consumer's ruv */
+ if (!process_operation (pb, csn))
+ {
+ slapi_send_ldap_result(pb, LDAP_SUCCESS, 0,
+ "replication operation not processed, replica unavailable "
+ "or csn ignored", 0, 0);
+ csn_free (&csn);
+ slapi_ch_free ((void**)&target_uuid);
+ slapi_ch_free ((void**)&superior_uuid);
+
+ return -1;
+ }
+
+ operation_set_csn(op, csn);
+ slapi_pblock_set( pb, SLAPI_TARGET_UNIQUEID, target_uuid);
+ slapi_pblock_get( pb, SLAPI_OPERATION_PARAMETERS, &op_params );
+ /* JCMREPL - Complain if there's no superior uuid */
+ op_params->p.p_add.parentuniqueid= superior_uuid; /* JCMREPL - Not very elegant */
+ /* JCMREPL - When do these things get free'd? */
+ if(target_uuid!=NULL)
+ {
+ /*
+ * Make sure that the Unique Identifier in the Control is the
+ * same as the one in the entry.
+ */
+ Slapi_Entry *addentry;
+ char *entry_uuid;
+ slapi_pblock_get( pb, SLAPI_ADD_ENTRY, &addentry );
+ entry_uuid= slapi_entry_attr_get_charptr( addentry, SLAPI_ATTR_UNIQUEID);
+ if(entry_uuid==NULL)
+ {
+ /* Odd that the entry doesn't have a Unique Identifier. But, we can fix it. */
+ slapi_entry_set_uniqueid(addentry, slapi_ch_strdup(target_uuid)); /* JCMREPL - strdup EVIL! There should be a uuid dup function. */
+ }
+ else
+ {
+ if(strcasecmp(entry_uuid,target_uuid)!=0)
+ {
+ slapi_log_error(SLAPI_LOG_FATAL, REPLICATION_SUBSYSTEM,
+ "%s Replicated Add received with Control_UUID=%s and Entry_UUID=%s.\n",
+ sessionid, target_uuid,entry_uuid);
+ }
+
+ slapi_ch_free ((void**)&entry_uuid);
+ }
+ }
+ }
+ }
+ else
+ {
+ PR_ASSERT(0); /* JCMREPL - A Replicated Operation with no Repl Baggage control... What does that mean? */
+ }
+ }
+ else
+ {
+ /* Replicated & Fixup Operation */
+ }
+ }
+ else
+ {
+ slapi_operation_set_csngen_handler ( op, (void*)replica_generate_next_csn );
+ }
+
+ copy_operation_parameters(pb);
+
+ return 0;
+}
+
+int
+multimaster_preop_delete (Slapi_PBlock *pb)
+{
+ Slapi_Operation *op;
+ int is_replicated_operation;
+ int is_fixup_operation;
+ int is_legacy_operation;
+
+ slapi_pblock_get(pb, SLAPI_OPERATION, &op);
+
+ /* If there is no replica or it is a legacy consumer - we don't need to continue.
+ Legacy plugin is handling 4.0 consumer code */
+ /* but if it is legacy, csngen_handler needs to be assigned here */
+ is_legacy_operation =
+ operation_is_flag_set(op,OP_FLAG_LEGACY_REPLICATION_DN);
+ if (is_legacy_operation)
+ {
+ copy_operation_parameters(pb);
+ slapi_operation_set_replica_attr_handler ( op, (void*)replica_get_attr );
+ slapi_operation_set_csngen_handler(op,
+ (void*)replica_generate_next_csn);
+ return 0;
+ }
+
+ if (!is_mmr_replica (pb))
+ {
+ copy_operation_parameters(pb);
+ return 0;
+ }
+
+ is_replicated_operation= operation_is_flag_set(op,OP_FLAG_REPLICATED);
+ is_fixup_operation= operation_is_flag_set(op,OP_FLAG_REPL_FIXUP);
+
+ if(is_replicated_operation)
+ {
+ if(!is_fixup_operation)
+ {
+ LDAPControl **ctrlp;
+ char sessionid[REPL_SESSION_ID_SIZE];
+ get_repl_session_id (pb, sessionid, NULL);
+ slapi_pblock_get(pb, SLAPI_REQCONTROLS, &ctrlp);
+ if (NULL != ctrlp)
+ {
+ CSN *csn = NULL;
+ char *target_uuid = NULL;
+ int drc = decode_NSDS50ReplUpdateInfoControl(ctrlp, &target_uuid, NULL, &csn, NULL /* modrdn_mods */);
+ if (-1 == drc)
+ {
+ slapi_log_error(SLAPI_LOG_FATAL, REPLICATION_SUBSYSTEM,
+ "%s An error occurred while decoding the replication update "
+ "control - Delete\n", sessionid);
+ }
+ else if (1 == drc)
+ {
+ /* we don't want to process replicated operations with csn smaller
+ than the corresponding csn in the consumer's ruv */
+ if (!process_operation (pb, csn))
+ {
+ slapi_send_ldap_result(pb, LDAP_SUCCESS, 0,
+ "replication operation not processed, replica unavailable "
+ "or csn ignored", 0, 0);
+ slapi_log_error(SLAPI_LOG_REPL, REPLICATION_SUBSYSTEM,
+ "%s replication operation not processed, replica unavailable "
+ "or csn ignored\n", sessionid);
+ csn_free (&csn);
+ slapi_ch_free ((void**)&target_uuid);
+
+ return -1;
+ }
+
+ /*
+ * For delete operations, we pass the uniqueid of the deleted entry
+ * to the backend and let it sort out which entry to really delete.
+ * We also set the operation csn.
+ */
+ operation_set_csn(op, csn);
+ slapi_pblock_set(pb, SLAPI_TARGET_UNIQUEID, target_uuid);
+ }
+ }
+ else
+ {
+ PR_ASSERT(0); /* JCMREPL - A Replicated Operation with no Repl Baggage control... What does that mean? */
+ }
+ }
+ else
+ {
+ /* Replicated & Fixup Operation */
+ }
+ }
+ else
+ {
+ slapi_operation_set_csngen_handler ( op, (void*)replica_generate_next_csn );
+ }
+
+ copy_operation_parameters(pb);
+ slapi_operation_set_replica_attr_handler ( op, (void*)replica_get_attr );
+
+ return 0;
+}
+
+int
+multimaster_preop_modify (Slapi_PBlock *pb)
+{
+ Slapi_Operation *op;
+ int is_replicated_operation;
+ int is_fixup_operation;
+ int is_legacy_operation;
+
+ slapi_pblock_get(pb, SLAPI_OPERATION, &op);
+
+ /* If there is no replica or it is a legacy consumer - we don't need to continue.
+ Legacy plugin is handling 4.0 consumer code */
+ /* but if it is legacy, csngen_handler needs to be assigned here */
+ is_legacy_operation =
+ operation_is_flag_set(op,OP_FLAG_LEGACY_REPLICATION_DN);
+ if (is_legacy_operation)
+ {
+ copy_operation_parameters(pb);
+ slapi_operation_set_csngen_handler(op,
+ (void*)replica_generate_next_csn);
+ return 0;
+ }
+
+ if (!is_mmr_replica (pb))
+ {
+ copy_operation_parameters(pb);
+ return 0;
+ }
+
+ is_replicated_operation= operation_is_flag_set(op,OP_FLAG_REPLICATED);
+ is_fixup_operation= operation_is_flag_set(op,OP_FLAG_REPL_FIXUP);
+
+ if(is_replicated_operation)
+ {
+ if(!is_fixup_operation)
+ {
+ LDAPControl **ctrlp;
+ char sessionid[REPL_SESSION_ID_SIZE];
+ get_repl_session_id (pb, sessionid, NULL);
+ slapi_pblock_get(pb, SLAPI_REQCONTROLS, &ctrlp);
+ if (NULL != ctrlp)
+ {
+ CSN *csn = NULL;
+ char *target_uuid = NULL;
+ int drc = decode_NSDS50ReplUpdateInfoControl(ctrlp, &target_uuid, NULL, &csn, NULL /* modrdn_mods */);
+ if (-1 == drc)
+ {
+ slapi_log_error(SLAPI_LOG_FATAL, REPLICATION_SUBSYSTEM,
+ "%s An error occurred while decoding the replication update "
+ "control- Modify\n", sessionid);
+ }
+ else if (1 == drc)
+ {
+ /* we don't want to process replicated operations with csn smaller
+ than the corresponding csn in the consumer's ruv */
+ if (!process_operation (pb, csn))
+ {
+ slapi_send_ldap_result(pb, LDAP_SUCCESS, 0,
+ "replication operation not processed, replica unavailable "
+ "or csn ignored", 0, 0);
+ slapi_log_error(SLAPI_LOG_REPL, REPLICATION_SUBSYSTEM,
+ "%s replication operation not processed, replica unavailable "
+ "or csn ignored\n", sessionid);
+ csn_free (&csn);
+ slapi_ch_free ((void**)&target_uuid);
+
+ return -1;
+ }
+
+ /*
+ * For modify operations, we pass the uniqueid of the modified entry
+ * to the backend and let it sort out which entry to really modify.
+ * We also set the operation csn.
+ */
+ operation_set_csn(op, csn);
+ slapi_pblock_set(pb, SLAPI_TARGET_UNIQUEID, target_uuid);
+ }
+ }
+ else
+ {
+ PR_ASSERT(0); /* JCMREPL - A Replicated Operation with no Repl Baggage control... What does that mean? */
+ }
+ }
+ else
+ {
+ /* Replicated & Fixup Operation */
+ }
+ }
+ else
+ {
+ slapi_operation_set_csngen_handler ( op, (void*)replica_generate_next_csn );
+ }
+
+ copy_operation_parameters(pb);
+ return 0;
+}
+
+int
+multimaster_preop_modrdn (Slapi_PBlock *pb)
+{
+ Slapi_Operation *op;
+ int is_replicated_operation;
+ int is_fixup_operation;
+ int is_legacy_operation;
+
+ slapi_pblock_get(pb, SLAPI_OPERATION, &op);
+
+ /* If there is no replica or it is a legacy consumer - we don't need to continue.
+ Legacy plugin is handling 4.0 consumer code */
+ /* but if it is legacy, csngen_handler needs to be assigned here */
+ is_legacy_operation =
+ operation_is_flag_set(op,OP_FLAG_LEGACY_REPLICATION_DN);
+ if (is_legacy_operation)
+ {
+ copy_operation_parameters(pb);
+ slapi_operation_set_csngen_handler(op,
+ (void*)replica_generate_next_csn);
+ return 0;
+ }
+
+ if (!is_mmr_replica (pb))
+ {
+ copy_operation_parameters(pb);
+ return 0;
+ }
+
+ is_replicated_operation= operation_is_flag_set(op,OP_FLAG_REPLICATED);
+ is_fixup_operation= operation_is_flag_set(op,OP_FLAG_REPL_FIXUP);
+
+ if(is_replicated_operation)
+ {
+ if(!is_fixup_operation)
+ {
+ LDAPControl **ctrlp;
+ char sessionid[REPL_SESSION_ID_SIZE];
+ get_repl_session_id (pb, sessionid, NULL);
+ slapi_pblock_get(pb, SLAPI_REQCONTROLS, &ctrlp);
+ if (NULL != ctrlp)
+ {
+ CSN *csn = NULL;
+ char *target_uuid = NULL;
+ char *newsuperior_uuid = NULL;
+ LDAPMod **modrdn_mods = NULL;
+ int drc = decode_NSDS50ReplUpdateInfoControl(ctrlp, &target_uuid, &newsuperior_uuid,
+ &csn, &modrdn_mods);
+ if (-1 == drc)
+ {
+ slapi_log_error(SLAPI_LOG_FATAL, REPLICATION_SUBSYSTEM,
+ "%s An error occurred while decoding the replication update "
+ "control - ModRDN\n", sessionid);
+ }
+ else if (1 == drc)
+ {
+ /*
+ * For modrdn operations, we pass the uniqueid of the entry being
+ * renamed to the backend and let it sort out which entry to really
+ * rename. We also set the operation csn, and if the newsuperior_uuid
+ * was sent, we decode that as well.
+ */
+ struct slapi_operation_parameters *op_params;
+
+ /* we don't want to process replicated operations with csn smaller
+ than the corresponding csn in the consumer's ruv */
+ if (!process_operation (pb, csn))
+ {
+ slapi_send_ldap_result(pb, LDAP_SUCCESS, 0,
+ "replication operation not processed, replica unavailable "
+ "or csn ignored", 0, 0);
+ csn_free (&csn);
+ slapi_ch_free ((void**)&target_uuid);
+ slapi_ch_free ((void**)&newsuperior_uuid);
+ ldap_mods_free (modrdn_mods, 1);
+
+ return -1;
+ }
+
+ operation_set_csn(op, csn);
+ slapi_pblock_set(pb, SLAPI_TARGET_UNIQUEID, target_uuid);
+ slapi_pblock_get( pb, SLAPI_OPERATION_PARAMETERS, &op_params );
+ op_params->p.p_modrdn.modrdn_newsuperior_address.uniqueid= newsuperior_uuid; /* JCMREPL - Not very elegant */
+ }
+
+ /*
+ * The replicated modrdn operation may also contain a sequence
+ * that contains side effects of the modrdn operation, e.g. the
+ * modifiersname and modifytimestamp are updated.
+ */
+ if (NULL != modrdn_mods)
+ {
+ LDAPMod **mods;
+ Slapi_Mods smods;
+ int i;
+ slapi_pblock_get(pb, SLAPI_MODIFY_MODS, &mods);
+ slapi_mods_init_passin(&smods, mods);
+ for (i = 0; NULL != modrdn_mods[i]; i++)
+ {
+ slapi_mods_add_ldapmod(&smods, modrdn_mods[i]); /* Does not copy mod */
+ }
+ mods = slapi_mods_get_ldapmods_passout(&smods);
+ slapi_pblock_set(pb, SLAPI_MODIFY_MODS, mods);
+ slapi_mods_done(&smods);
+ slapi_ch_free((void **)&modrdn_mods); /* Free container only - contents are referred to by array "mods" */
+ }
+ }
+ else
+ {
+ PR_ASSERT(0); /* JCMREPL - A Replicated Operation with no Repl Baggage control... What does that mean? */
+ }
+ }
+ else
+ {
+ /* Replicated & Fixup Operation */
+ }
+ }
+ else
+ {
+ slapi_operation_set_csngen_handler ( op, (void*)replica_generate_next_csn );
+ }
+
+ copy_operation_parameters(pb);
+
+ return 0;
+}
+
+int
+multimaster_preop_search (Slapi_PBlock *pb)
+{
+ return 0;
+}
+
+int
+multimaster_preop_compare (Slapi_PBlock *pb)
+{
+ return 0;
+}
+
+static void
+purge_entry_state_information (Slapi_PBlock *pb)
+{
+ CSN *purge_csn;
+ Object *repl_obj;
+ Replica *replica;
+
+ /* we don't want to trim RUV tombstone because we will
+ deadlock with ourself */
+ if (ruv_tombstone_op (pb))
+ return;
+
+ repl_obj = replica_get_replica_for_op(pb);
+ if (NULL != repl_obj)
+ {
+ replica = object_get_data(repl_obj);
+ if (NULL != replica)
+ {
+ purge_csn = replica_get_purge_csn(replica);
+ }
+ if (NULL != purge_csn)
+ {
+ Slapi_Entry *e;
+ int optype;
+
+ slapi_pblock_get(pb, SLAPI_OPERATION_TYPE, &optype);
+ switch (optype)
+ {
+ case SLAPI_OPERATION_MODIFY:
+ slapi_pblock_get(pb, SLAPI_MODIFY_EXISTING_ENTRY, &e);
+ break;
+ case SLAPI_OPERATION_MODRDN:
+ /* XXXggood - the following always gives a NULL entry - why? */
+ slapi_pblock_get(pb, SLAPI_MODRDN_EXISTING_ENTRY, &e);
+ break;
+ case SLAPI_OPERATION_DELETE:
+ slapi_pblock_get(pb, SLAPI_DELETE_EXISTING_ENTRY, &e);
+ break;
+ default:
+ e = NULL; /* Don't purge on ADD - not needed */
+ break;
+ }
+ if (NULL != e)
+ {
+ char csn_str[CSN_STRSIZE];
+ entry_purge_state_information(e, purge_csn);
+ /* conntion is always null */
+ slapi_log_error(SLAPI_LOG_REPL, repl_plugin_name,
+ "Purged state information from entry %s up to "
+ "CSN %s\n", slapi_entry_get_dn(e),
+ csn_as_string(purge_csn, PR_FALSE, csn_str));
+ }
+ csn_free(&purge_csn);
+ }
+ object_release(repl_obj);
+ }
+}
+
+int
+multimaster_bepreop_add (Slapi_PBlock *pb)
+{
+ int rc= 0;
+ Slapi_Operation *op;
+ int is_replicated_operation;
+ int is_fixup_operation;
+ int is_legacy_operation;
+
+ slapi_pblock_get(pb, SLAPI_OPERATION, &op);
+ is_replicated_operation= operation_is_flag_set(op,OP_FLAG_REPLICATED);
+ is_fixup_operation= operation_is_flag_set(op,OP_FLAG_REPL_FIXUP);
+ is_legacy_operation= operation_is_flag_set(op,OP_FLAG_LEGACY_REPLICATION_DN);
+
+ /* For replicated operations, apply URP algorithm */
+ if (is_replicated_operation && !is_fixup_operation)
+ {
+ rc = urp_add_operation(pb);
+ }
+
+ return rc;
+}
+
+int
+multimaster_bepreop_delete (Slapi_PBlock *pb)
+{
+ int rc= 0;
+ Slapi_Operation *op;
+ int is_replicated_operation;
+ int is_fixup_operation;
+ int is_legacy_operation;
+
+ slapi_pblock_get(pb, SLAPI_OPERATION, &op);
+ is_replicated_operation= operation_is_flag_set(op,OP_FLAG_REPLICATED);
+ is_fixup_operation= operation_is_flag_set(op,OP_FLAG_REPL_FIXUP);
+ is_legacy_operation= operation_is_flag_set(op,OP_FLAG_LEGACY_REPLICATION_DN);
+
+ /* For replicated operations, apply URP algorithm */
+ if(is_replicated_operation && !is_fixup_operation)
+ {
+ rc = urp_delete_operation(pb);
+ }
+
+ return rc;
+}
+
+int
+multimaster_bepreop_modify (Slapi_PBlock *pb)
+{
+ int rc= 0;
+ Slapi_Operation *op;
+ int is_replicated_operation;
+ int is_fixup_operation;
+ int is_legacy_operation;
+
+ slapi_pblock_get(pb, SLAPI_OPERATION, &op);
+ is_replicated_operation= operation_is_flag_set(op,OP_FLAG_REPLICATED);
+ is_fixup_operation= operation_is_flag_set(op,OP_FLAG_REPL_FIXUP);
+ is_legacy_operation= operation_is_flag_set(op,OP_FLAG_LEGACY_REPLICATION_DN);
+
+ /* For replicated operations, apply URP algorithm */
+ if(is_replicated_operation && !is_fixup_operation)
+ {
+ rc = urp_modify_operation(pb);
+ }
+
+ /* Clean up old state information */
+ purge_entry_state_information(pb);
+
+ return rc;
+}
+
+int
+multimaster_bepreop_modrdn (Slapi_PBlock *pb)
+{
+ int rc= 0;
+ Slapi_Operation *op;
+ int is_replicated_operation;
+ int is_fixup_operation;
+ int is_legacy_operation;
+
+ slapi_pblock_get(pb, SLAPI_OPERATION, &op);
+ is_replicated_operation= operation_is_flag_set(op,OP_FLAG_REPLICATED);
+ is_fixup_operation= operation_is_flag_set(op,OP_FLAG_REPL_FIXUP);
+ is_legacy_operation= operation_is_flag_set(op,OP_FLAG_LEGACY_REPLICATION_DN);
+
+ /* For replicated operations, apply URP algorithm */
+ if(is_replicated_operation && !is_fixup_operation)
+ {
+ rc = urp_modrdn_operation(pb);
+ }
+
+ /* Clean up old state information */
+ purge_entry_state_information(pb);
+
+ return rc;
+}
+
+int
+multimaster_bepostop_modrdn (Slapi_PBlock *pb)
+{
+ return 0;
+}
+
+int
+multimaster_bepostop_delete (Slapi_PBlock *pb)
+{
+ return 0;
+}
+
+/* postop - write to changelog */
+int
+multimaster_postop_bind (Slapi_PBlock *pb)
+{
+ return 0;
+}
+
+int
+multimaster_postop_add (Slapi_PBlock *pb)
+{
+ return process_postop(pb);
+}
+
+int
+multimaster_postop_delete (Slapi_PBlock *pb)
+{
+ int rc;
+ Slapi_Operation *op;
+
+ slapi_pblock_get(pb, SLAPI_OPERATION, &op);
+ if ( ! operation_is_flag_set (op, OP_FLAG_REPL_FIXUP) )
+ {
+ urp_post_delete_operation (pb);
+ }
+ rc = process_postop(pb);
+ return rc;
+}
+
+int
+multimaster_postop_modify (Slapi_PBlock *pb)
+{
+ return process_postop(pb);
+}
+
+int
+multimaster_postop_modrdn (Slapi_PBlock *pb)
+{
+ int rc;
+ Slapi_Operation *op;
+
+ slapi_pblock_get(pb, SLAPI_OPERATION, &op);
+ if ( ! operation_is_flag_set (op, OP_FLAG_REPL_FIXUP) )
+ {
+ urp_post_modrdn_operation (pb);
+ }
+ rc = process_postop(pb);
+ return rc;
+}
+
+
+/* Helper functions */
+
+/*
+ * This function makes a copy of the operation parameters
+ * and stashes them in the consumer operation extension.
+ * This is where the 5.0 Change Log will get the operation
+ * details from.
+ */
+static void
+copy_operation_parameters(Slapi_PBlock *pb)
+{
+ Slapi_Operation *op = NULL;
+ struct slapi_operation_parameters *op_params;
+ supplier_operation_extension *opext;
+ Object *repl_obj;
+ Replica *replica;
+
+ repl_obj = replica_get_replica_for_op (pb);
+
+ /* we are only interested in the updates to replicas */
+ if (repl_obj)
+ {
+ /* we only save the original operation parameters for replicated operations
+ since client operations don't go through urp engine and pblock data can be logged */
+ slapi_pblock_get( pb, SLAPI_OPERATION, &op );
+ PR_ASSERT (op);
+
+ replica = (Replica*)object_get_data (repl_obj);
+ PR_ASSERT (replica);
+
+ opext = (supplier_operation_extension*) repl_sup_get_ext (REPL_SUP_EXT_OP, op);
+ if (operation_is_flag_set(op,OP_FLAG_REPLICATED) &&
+ !operation_is_flag_set(op, OP_FLAG_REPL_FIXUP))
+ {
+ slapi_pblock_get (pb, SLAPI_OPERATION_PARAMETERS, &op_params);
+ opext->operation_parameters= operation_parameters_dup(op_params);
+ }
+
+ /* this condition is needed to avoid re-entering lock when
+ ruv state is updated */
+ if (!operation_is_flag_set(op, OP_FLAG_REPL_FIXUP))
+ {
+ /* save replica generation in case it changes */
+ opext->repl_gen = replica_get_generation (replica);
+ }
+
+ object_release (repl_obj);
+ }
+}
+
+/*
+ * Helper function: update the RUV so that it reflects the
+ * locally-processed update. This is called for both replicated
+ * and non-replicated operations.
+ */
+static void
+update_ruv_component(Replica *replica, CSN *opcsn, Slapi_PBlock *pb)
+{
+ PRBool legacy;
+ char *purl;
+
+ if (!replica || !opcsn)
+ return;
+
+ /* Replica configured, so update its ruv */
+ legacy = replica_is_legacy_consumer (replica);
+ if (legacy)
+ purl = replica_get_legacy_purl (replica);
+ else
+ purl = (char*)replica_get_purl_for_op (replica, pb, opcsn);
+
+ replica_update_ruv(replica, opcsn, purl);
+
+ if (legacy)
+ {
+ slapi_ch_free ((void**)&purl);
+ }
+}
+
+/*
+ * Write the changelog. Note: it is an error to call write_changelog_and_ruv() for fixup
+ * operations. The caller should avoid calling this function if the operation is
+ * a fixup op.
+ * Also update the ruv - we need to do this while we have the replica lock acquired
+ * so that the csn is written to the changelog and the ruv is updated with the csn
+ * in one atomic operation - if there is no changelog, we still need to update
+ * the ruv (e.g. for consumers)
+ */
+static int
+write_changelog_and_ruv (Slapi_PBlock *pb)
+{
+ int rc;
+ slapi_operation_parameters *op_params = NULL;
+ Object *repl_obj;
+ int return_value = 0;
+ Replica *r;
+
+ /* we only log changes for operations applied to a replica */
+ repl_obj = replica_get_replica_for_op (pb);
+ if (repl_obj == NULL)
+ return 0;
+
+ r = (Replica*)object_get_data (repl_obj);
+ PR_ASSERT (r);
+
+ if (replica_is_flag_set (r, REPLICA_LOG_CHANGES) &&
+ (cl5GetState () == CL5_STATE_OPEN))
+ {
+ supplier_operation_extension *opext = NULL;
+ const char *repl_name;
+ char *repl_gen;
+ Slapi_Operation *op;
+
+ slapi_pblock_get(pb, SLAPI_OPERATION, &op);
+ opext = (supplier_operation_extension*) repl_sup_get_ext (REPL_SUP_EXT_OP, op);
+ PR_ASSERT (opext);
+
+ /* get replica generation and replica name to pass to the write function */
+ repl_name = replica_get_name (r);
+ repl_gen = opext->repl_gen;
+ PR_ASSERT (repl_name && repl_gen);
+
+ /* for replicated operations, we log the original, non-urp data which is
+ saved in the operation extension */
+ if (operation_is_flag_set(op,OP_FLAG_REPLICATED))
+ {
+ PR_ASSERT (opext->operation_parameters);
+ op_params = opext->operation_parameters;
+ }
+ else /* since client operations don't go through urp, we log the operation data in pblock */
+ {
+ Slapi_Entry *e = NULL;
+ const char *uniqueid = NULL;
+
+ slapi_pblock_get (pb, SLAPI_OPERATION_PARAMETERS, &op_params);
+ PR_ASSERT (op_params);
+
+ /* need to set uniqueid operation parameter */
+ /* we try to use the appropriate entry (pre op or post op)
+ depending on the type of operation (add, delete, modify)
+ However, in some cases, the backend operation may fail in
+ a non fatal way (e.g. attempt to remove an attribute value
+ that does not exist) but we still need to log the change.
+ So, the POST_OP entry may not have been set in the FE modify
+ code. In that case, we use the PRE_OP entry.
+ */
+ slapi_pblock_get (pb, SLAPI_ENTRY_POST_OP, &e);
+ if ((e == NULL) ||
+ (op_params->operation_type == SLAPI_OPERATION_DELETE))
+ {
+ slapi_pblock_get (pb, SLAPI_ENTRY_PRE_OP, &e);
+ }
+
+ PR_ASSERT (e);
+
+ uniqueid = slapi_entry_get_uniqueid (e);
+ PR_ASSERT (uniqueid);
+
+ op_params->target_address.uniqueid = slapi_ch_strdup (uniqueid);
+ }
+
+ /* we might have stripped all the mods - in that case we do not
+ log the operation */
+ if (op_params->operation_type != SLAPI_OPERATION_MODIFY ||
+ op_params->p.p_modify.modify_mods != NULL)
+ {
+ if (cl5_is_diskfull() && !cl5_diskspace_is_available())
+ {
+ slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name,
+ "write_changelog_and_ruv: Skipped due to DISKFULL\n");
+ return 0;
+ }
+ rc = cl5WriteOperation(repl_name, repl_gen, op_params,
+ !operation_is_flag_set(op, OP_FLAG_REPLICATED));
+ if (rc != CL5_SUCCESS)
+ {
+ char csn_str[CSN_STRSIZE];
+ /* ONREPL - log error */
+ slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name,
+ "write_changelog_and_ruv: can't add a change for "
+ "%s (uniqid: %s, optype: %u) to changelog csn %s\n",
+ op_params->target_address.dn,
+ op_params->target_address.uniqueid,
+ op_params->operation_type,
+ csn_as_string(op_params->csn, PR_FALSE, csn_str));
+ return_value = 1;
+ }
+ }
+
+ if (!operation_is_flag_set(op,OP_FLAG_REPLICATED))
+ {
+ slapi_ch_free((void**)&op_params->target_address.uniqueid);
+ }
+ }
+
+ /*
+ This was moved because we need to write the changelog and update
+ the ruv in one atomic operation - I have seen cases where the inc
+ protocol thread interrupts this thread between the time the changelog
+ is written and the ruv is updated - this causes confusion in several
+ places, especially in _cl5SkipReplayEntry since it cannot find the csn it
+ just read from the changelog in either the supplier or consumer ruv
+ */
+ if (0 == return_value) {
+ Slapi_Operation *op;
+ CSN *opcsn;
+
+ slapi_pblock_get( pb, SLAPI_OPERATION, &op );
+ opcsn = operation_get_csn(op);
+ update_ruv_component(r, opcsn, pb);
+ }
+
+ object_release (repl_obj);
+ return return_value;
+}
+
+/*
+ * Postop processing - write the changelog if the operation resulted in
+ * an LDAP_SUCCESS result code, update the RUV, and notify the replication
+ * agreements about the change.
+ * If the result code is not LDAP_SUCCESS, then cancel the operation CSN.
+ */
+static int
+process_postop (Slapi_PBlock *pb)
+{
+ int rc = LDAP_SUCCESS;
+ Slapi_Operation *op;
+ Slapi_Backend *be;
+ int is_replicated_operation = 0;
+ CSN *opcsn = NULL;
+ char sessionid[REPL_SESSION_ID_SIZE];
+
+ /* we just let fixup operations through */
+ slapi_pblock_get( pb, SLAPI_OPERATION, &op );
+ if ((operation_is_flag_set(op, OP_FLAG_REPL_FIXUP)) ||
+ (operation_is_flag_set(op, OP_FLAG_TOMBSTONE_ENTRY)))
+ {
+ return 0;
+ }
+
+ /* ignore operations intended for chaining backends - they will be
+ replicated back to us or should be ignored anyway
+ replicated operations should be processed normally, as they should
+ be going to a local backend */
+ is_replicated_operation= operation_is_flag_set(op,OP_FLAG_REPLICATED);
+ slapi_pblock_get(pb, SLAPI_BACKEND, &be);
+ if (!is_replicated_operation &&
+ slapi_be_is_flag_set(be,SLAPI_BE_FLAG_REMOTE_DATA))
+ {
+ return 0;
+ }
+
+ get_repl_session_id (pb, sessionid, &opcsn);
+
+ slapi_pblock_get(pb, SLAPI_RESULT_CODE, &rc);
+ /*
+ * Don't abandon writing changelog since we'd do everything
+ * possible to keep the changelog in sync with the backend
+ * db which was committed before this function was called.
+ *
+ * if (rc == LDAP_SUCCESS && !slapi_op_abandoned(pb))
+ */
+ if (rc == LDAP_SUCCESS)
+ {
+ rc = write_changelog_and_ruv(pb);
+ if (rc == 0)
+ {
+ agmtlist_notify_all(pb);
+ }
+ else
+ {
+ slapi_log_error(SLAPI_LOG_REPL, repl_plugin_name, "%s process postop: error writing changelog and ruv\n", sessionid);
+ }
+ }
+ else if (opcsn)
+ {
+ rc = cancel_opcsn (pb);
+
+ /* Don't try to get session id since conn is always null */
+ slapi_log_error(SLAPI_LOG_REPL, repl_plugin_name,
+ "%s process postop: canceling operation csn\n", sessionid);
+ }
+
+ /* the target unique id is set in the modify_preop above, so
+ we need to free it */
+ /* The following bunch of frees code does not belong to this place
+ * but rather to operation_free who should be responsible for calling
+ * operation_parameters_free and it doesn't. I guess this is like this
+ * because several crashes happened in the past regarding some opparams
+ * that were getting individually freed before they should. Whatever
+ * the case, this is not the place and we should make sure that this
+ * code gets removed for 5.next and substituted by the strategy (operation_free).
+ * For 5.0, such change is too risky, so this will be done here */
+ if (is_replicated_operation)
+ {
+ slapi_operation_parameters *op_params = NULL;
+ int optype = 0;
+ /* target uid and csn are set for all repl operations. Free them */
+ char *target_uuid = NULL;
+ slapi_pblock_get(pb, SLAPI_OPERATION_TYPE, &optype);
+ slapi_pblock_get(pb, SLAPI_TARGET_UNIQUEID, &target_uuid);
+ slapi_pblock_set(pb, SLAPI_TARGET_UNIQUEID, NULL);
+ slapi_ch_free((void**)&target_uuid);
+ if (optype == SLAPI_OPERATION_ADD) {
+ slapi_pblock_get( pb, SLAPI_OPERATION_PARAMETERS, &op_params );
+ slapi_ch_free((void **) &op_params->p.p_add.parentuniqueid);
+ }
+ if (optype == SLAPI_OPERATION_MODRDN) {
+ slapi_pblock_get( pb, SLAPI_OPERATION_PARAMETERS, &op_params );
+ slapi_ch_free((void **) &op_params->p.p_modrdn.modrdn_newsuperior_address.uniqueid);
+ }
+ }
+ if (NULL == opcsn)
+ opcsn = operation_get_csn(op);
+ if (opcsn)
+ csn_free(&opcsn);
+
+ return rc;
+}
+
+
+/*
+ * Cancel an operation CSN. This removes it from any CSN pending lists.
+ * This function is called when a previously-generated CSN will not
+ * be needed, e.g. if the update operation produced an error.
+ */
+static int
+cancel_opcsn (Slapi_PBlock *pb)
+{
+ Object *repl_obj;
+ Slapi_Operation *op = NULL;
+
+ PR_ASSERT (pb);
+
+ repl_obj = replica_get_replica_for_op (pb);
+ slapi_pblock_get(pb, SLAPI_OPERATION, &op);
+ PR_ASSERT (op);
+ if (repl_obj)
+ {
+ Replica *r;
+ Object *gen_obj;
+ CSNGen *gen;
+ CSN *opcsn;
+
+ r = (Replica*)object_get_data (repl_obj);
+ PR_ASSERT (r);
+ opcsn = operation_get_csn(op);
+
+ if (!operation_is_flag_set(op,OP_FLAG_REPLICATED))
+ {
+ /* get csn generator for the replica */
+ gen_obj = replica_get_csngen (r);
+ PR_ASSERT (gen_obj);
+ gen = (CSNGen*)object_get_data (gen_obj);
+
+ if (NULL != opcsn)
+ {
+ csngen_abort_csn (gen, operation_get_csn(op));
+ }
+
+ object_release (gen_obj);
+ }
+ else if (!operation_is_flag_set(op,OP_FLAG_REPL_FIXUP))
+ {
+ Object *ruv_obj;
+
+ ruv_obj = replica_get_ruv (r);
+ PR_ASSERT (ruv_obj);
+ ruv_cancel_csn_inprogress ((RUV*)object_get_data (ruv_obj), opcsn);
+ object_release (ruv_obj);
+ }
+
+ object_release (repl_obj);
+ }
+
+ return 0;
+}
+
+
+
+/*
+ * Return non-zero if the target entry DN is the DN of the RUV tombstone
+ * entry.
+ * The entry has rdn of nsuniqueid = ffffffff-ffffffff-ffffffff-ffffffff
+ */
+static int
+ruv_tombstone_op (Slapi_PBlock *pb)
+{
+ char *uniqueid;
+ int rc;
+
+ slapi_pblock_get (pb, SLAPI_TARGET_UNIQUEID, &uniqueid);
+
+ rc = uniqueid && strcasecmp (uniqueid, RUV_STORAGE_ENTRY_UNIQUEID) == 0;
+
+ return rc;
+}
+
+/* we don't want to process replicated operations with csn smaller
+ than the corresponding csn in the consumer's ruv */
+static PRBool
+process_operation (Slapi_PBlock *pb, const CSN *csn)
+{
+ Object *r_obj;
+ Replica *r;
+ Object *ruv_obj;
+ RUV *ruv;
+ int rc;
+
+ r_obj = replica_get_replica_for_op(pb);
+ if (r_obj == NULL)
+ {
+ char sessionid[REPL_SESSION_ID_SIZE];
+ get_repl_session_id (pb, sessionid, NULL);
+ slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name, "%s process_operation: "
+ "can't locate replica for the replicated operation\n",
+ sessionid );
+ return PR_FALSE;
+ }
+
+ r = (Replica*)object_get_data (r_obj);
+ PR_ASSERT (r);
+
+ ruv_obj = replica_get_ruv (r);
+ PR_ASSERT (ruv_obj);
+
+ ruv = (RUV*)object_get_data (ruv_obj);
+ PR_ASSERT (ruv);
+
+ rc = ruv_add_csn_inprogress (ruv, csn);
+
+ object_release (ruv_obj);
+ object_release (r_obj);
+
+ return (rc == RUV_SUCCESS);
+}
+
+static PRBool
+is_mmr_replica (Slapi_PBlock *pb)
+{
+ Object *r_obj;
+ Replica *r;
+ PRBool mmr;
+
+ r_obj = replica_get_replica_for_op(pb);
+ if (r_obj == NULL)
+ {
+ return PR_FALSE;
+ }
+
+ r = (Replica*)object_get_data (r_obj);
+ PR_ASSERT (r);
+
+ mmr = !replica_is_legacy_consumer (r);
+
+ object_release (r_obj);
+
+ return mmr;
+}
+
+static const char *replica_get_purl_for_op (const Replica *r, Slapi_PBlock *pb, const CSN *opcsn)
+{
+ int is_replicated_op;
+ const char *purl;
+
+ slapi_pblock_get(pb, SLAPI_IS_MMR_REPLICATED_OPERATION, &is_replicated_op);
+
+ if (!is_replicated_op)
+ {
+ purl = multimaster_get_local_purl();
+ }
+ else
+ {
+ /* Get the appropriate partial URL from the supplier RUV */
+ Slapi_Connection *conn;
+ consumer_connection_extension *connext;
+ slapi_pblock_get(pb, SLAPI_CONNECTION, &conn);
+ connext = (consumer_connection_extension *)repl_con_get_ext(
+ REPL_CON_EXT_CONN, conn);
+ if (NULL == connext || NULL == connext->supplier_ruv)
+ {
+ char sessionid[REPL_SESSION_ID_SIZE];
+ get_repl_session_id (pb, sessionid, NULL);
+ slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name, "%s replica_get_purl_for_op: "
+ "cannot obtain consumer connection extension or supplier_ruv.\n",
+ sessionid);
+ }
+ else
+ {
+ purl = ruv_get_purl_for_replica(connext->supplier_ruv,
+ csn_get_replicaid(opcsn));
+ }
+ }
+
+ return purl;
+}
+
+/* ONREPL at the moment, I decided not to trim copiedFrom and copyingFrom
+ attributes when sending operation to replicas. This is because, each
+ operation results in a state information stored in the database and
+ if we don't replay all operations we will endup with state inconsistency.
+
+ Keeping the function just in case
+ */
+static void strip_legacy_info (slapi_operation_parameters *op_params)
+{
+ switch (op_params->operation_type)
+ {
+ case SLAPI_OPERATION_ADD:
+ slapi_entry_delete_values_sv(op_params->p.p_add.target_entry,
+ type_copiedFrom, NULL);
+ slapi_entry_delete_values_sv(op_params->p.p_add.target_entry,
+ type_copyingFrom, NULL);
+ break;
+ case SLAPI_OPERATION_MODIFY:
+ {
+ Slapi_Mods smods;
+ LDAPMod *mod;
+
+ slapi_mods_init_byref(&smods, op_params->p.p_modify.modify_mods);
+ mod = slapi_mods_get_first_mod(&smods);
+ while (mod)
+ {
+ /* modify just to update copiedFrom or copyingFrom attribute
+ does not contain modifiersname or modifytime - so we don't
+ have to strip them */
+ if (strcasecmp (mod->mod_type, type_copiedFrom) == 0 ||
+ strcasecmp (mod->mod_type, type_copyingFrom) == 0)
+ slapi_mods_remove(&smods);
+ mod = slapi_mods_get_next_mod(&smods);
+ }
+
+ op_params->p.p_modify.modify_mods = slapi_mods_get_ldapmods_passout (&smods);
+ slapi_mods_done (&smods);
+
+ break;
+ }
+
+ default: break;
+ }
+}
+
+/* this function is called when state of a backend changes */
+void
+multimaster_be_state_change (void *handle, char *be_name, int old_be_state, int new_be_state)
+{
+ Object *r_obj;
+ Replica *r;
+
+ /* check if we have replica associated with the backend */
+ r_obj = replica_get_for_backend (be_name);
+ if (r_obj == NULL)
+ return;
+
+ r = (Replica*)object_get_data (r_obj);
+ PR_ASSERT (r);
+
+ if (new_be_state == SLAPI_BE_STATE_ON)
+ {
+ /* backend came back online - restart replication */
+ slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name, "multimaster_be_state_change: "
+ "replica %s is coming online; enabling replication\n",
+ slapi_sdn_get_ndn (replica_get_root (r)));
+ replica_enable_replication (r);
+ }
+ else if (new_be_state == SLAPI_BE_STATE_OFFLINE)
+ {
+ /* backend is about to be taken down - disable replication */
+ slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name, "multimaster_be_state_change: "
+ "replica %s is going offline; disabling replication\n",
+ slapi_sdn_get_ndn (replica_get_root (r)));
+ replica_disable_replication (r, r_obj);
+ }
+ else if (new_be_state == SLAPI_BE_STATE_DELETE)
+ {
+ /* backend is about to be removed - disable replication */
+ if (old_be_state == SLAPI_BE_STATE_ON)
+ {
+ slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name, "multimaster_be_state_change: "
+ "replica %s is about to be deleted; disabling replication\n",
+ slapi_sdn_get_ndn (replica_get_root (r)));
+ replica_disable_replication (r, r_obj);
+ }
+ }
+
+ object_release (r_obj);
+}
+
+static void
+close_changelog_for_replica (Object *r_obj)
+{
+ if (cl5GetState () == CL5_STATE_OPEN)
+ cl5CloseDB (r_obj);
+}
diff --git a/ldap/servers/plugins/replication/repl5_prot_private.h b/ldap/servers/plugins/replication/repl5_prot_private.h
new file mode 100644
index 00000000..fbaf96e4
--- /dev/null
+++ b/ldap/servers/plugins/replication/repl5_prot_private.h
@@ -0,0 +1,66 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+
+#ifndef _REPL5_PROT_PRIVATE_H_
+#define _REPL5_PROT_PRIVATE_H_
+
+#define ACQUIRE_SUCCESS 101
+#define ACQUIRE_REPLICA_BUSY 102
+#define ACQUIRE_FATAL_ERROR 103
+#define ACQUIRE_CONSUMER_WAS_UPTODATE 104
+#define ACQUIRE_TRANSIENT_ERROR 105
+
+typedef struct private_repl_protocol
+{
+ void (*delete)(struct private_repl_protocol **);
+ void (*run)(struct private_repl_protocol *);
+ int (*stop)(struct private_repl_protocol *);
+ int (*status)(struct private_repl_protocol *);
+ void (*notify_update)(struct private_repl_protocol *);
+ void (*notify_agmt_changed)(struct private_repl_protocol *);
+ void (*notify_window_opened)(struct private_repl_protocol *);
+ void (*notify_window_closed)(struct private_repl_protocol *);
+ void (*update_now)(struct private_repl_protocol *);
+ PRLock *lock;
+ PRCondVar *cvar;
+ int stopped;
+ int terminate;
+ PRUint32 eventbits;
+ Repl_Connection *conn;
+ int last_acquire_response_code;
+ Repl_Agmt *agmt;
+ Object *replica_object;
+ void *private;
+ PRBool replica_acquired;
+} Private_Repl_Protocol;
+
+extern Private_Repl_Protocol *Repl_5_Inc_Protocol_new();
+extern Private_Repl_Protocol *Repl_5_Tot_Protocol_new();
+
+#define PROTOCOL_TERMINATION_NORMAL 301
+#define PROTOCOL_TERMINATION_ABNORMAL 302
+#define PROTOCOL_TERMINATION_NEEDS_TOTAL_UPDATE 303
+
+#define RESUME_DO_TOTAL_UPDATE 401
+#define RESUME_DO_INCREMENTAL_UPDATE 402
+#define RESUME_TERMINATE 403
+#define RESUME_SUSPEND 404
+
+/* Backoff timer settings for reconnect */
+#define PROTOCOL_BACKOFF_MINIMUM 3 /* 3 seconds */
+#define PROTOCOL_BACKOFF_MAXIMUM (60 * 5) /* 5 minutes */
+/* Backoff timer settings for replica busy reconnect */
+#define PROTOCOL_BUSY_BACKOFF_MINIMUM PROTOCOL_BACKOFF_MINIMUM
+#define PROTOCOL_BUSY_BACKOFF_MAXIMUM PROTOCOL_BUSY_BACKOFF_MINIMUM
+
+/* protocol related functions */
+void release_replica(Private_Repl_Protocol *prp);
+int acquire_replica(Private_Repl_Protocol *prp, char *prot_oid, RUV **ruv);
+BerElement *entry2bere(const Slapi_Entry *e);
+CSN *get_current_csn(Slapi_DN *replarea_sdn);
+char* protocol_response2string (int response);
+
+#endif /* _REPL5_PROT_PRIVATE_H_ */
diff --git a/ldap/servers/plugins/replication/repl5_protocol.c b/ldap/servers/plugins/replication/repl5_protocol.c
new file mode 100644
index 00000000..725bf3f2
--- /dev/null
+++ b/ldap/servers/plugins/replication/repl5_protocol.c
@@ -0,0 +1,502 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+
+/* repl5_protocol.c */
+/*
+
+ The replication protocol object manages the replication protocol for
+ a given replica. It determines which protocol(s) are appropriate to
+ use when updating a given replica. It also knows how to arbitrate
+ incremental and total update protocols for a given replica.
+
+*/
+
+#include "repl5.h"
+#include "repl5_prot_private.h"
+
+#define PROTOCOL_5_INCREMENTAL 1
+#define PROTOCOL_5_TOTAL 2
+#define PROTOCOL_4_INCREMENTAL 3
+#define PROTOCOL_4_TOTAL 4
+
+typedef struct repl_protocol
+{
+ Private_Repl_Protocol *prp_incremental; /* inc protocol to use */
+ Private_Repl_Protocol *prp_total; /* total protocol to use */
+ Private_Repl_Protocol *prp_active_protocol; /* Pointer to active protocol */
+ Repl_Agmt *agmt; /* The replication agreement we're servicing */
+ Repl_Connection *conn; /* Connection to remote server */
+ Object *replica_object; /* Local replica. If non-NULL, replica object is acquired */
+ int state;
+ int next_state;
+ PRLock *lock;
+} repl_protocol;
+
+
+/* States */
+#define STATE_FINISHED 503
+#define STATE_BAD_STATE_SHOULD_NEVER_HAPPEN 599
+
+/* Forward declarations */
+static Private_Repl_Protocol *private_protocol_factory(Repl_Protocol *rp, int type);
+
+
+
+
+/*
+ * Create a new protocol instance.
+ */
+Repl_Protocol *
+prot_new(Repl_Agmt *agmt, int protocol_state)
+{
+ Slapi_DN *replarea_sdn = NULL;
+ Repl_Protocol *rp = (Repl_Protocol *)slapi_ch_malloc(sizeof(Repl_Protocol));
+
+ rp->prp_incremental = rp->prp_total = rp->prp_active_protocol = NULL;
+ if (protocol_state == STATE_PERFORMING_TOTAL_UPDATE)
+ {
+ rp->state = STATE_PERFORMING_TOTAL_UPDATE;
+ }
+ else
+ {
+ rp->state = STATE_PERFORMING_INCREMENTAL_UPDATE;
+ }
+ rp->next_state = STATE_PERFORMING_INCREMENTAL_UPDATE;
+ if ((rp->lock = PR_NewLock()) == NULL)
+ {
+ goto loser;
+ }
+ rp->agmt = agmt;
+ if ((rp->conn = conn_new(agmt)) == NULL)
+ {
+ goto loser;
+ }
+ /* Acquire the local replica object */
+ replarea_sdn = agmt_get_replarea(agmt);
+ rp->replica_object = replica_get_replica_from_dn(replarea_sdn);
+ if (NULL == rp->replica_object)
+ {
+ /* Whoa, no local replica!?!? */
+ slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name,
+ "%s: Unable to locate replica object for local replica %s\n",
+ agmt_get_long_name(agmt),
+ slapi_sdn_get_dn(replarea_sdn));
+ goto loser;
+ }
+ rp->prp_incremental = private_protocol_factory(rp, PROTOCOL_5_INCREMENTAL);
+ rp->prp_total = private_protocol_factory(rp, PROTOCOL_5_TOTAL);
+ /* XXXggood register callback handlers for entries updated, and
+ schedule window enter/leave. */
+ slapi_sdn_free(&replarea_sdn);
+
+ return rp;
+loser:
+ prot_delete(&rp);
+ return NULL;
+}
+
+
+
+
+
+Object *
+prot_get_replica_object(Repl_Protocol *rp)
+{
+ PR_ASSERT(NULL != rp);
+ return rp->replica_object;
+}
+
+
+
+
+
+Repl_Agmt *
+prot_get_agreement(Repl_Protocol *rp)
+{
+ /* MAB: rp might be NULL for disabled suffixes. Don't ASSERT on it */
+ if (NULL == rp) return NULL;
+ return rp->agmt;
+}
+
+
+
+
+void
+prot_free(Repl_Protocol **rpp)
+{
+ Repl_Protocol *rp = NULL;
+
+ if (rpp == NULL || *rpp == NULL) return;
+
+ rp = *rpp;
+
+ PR_Lock(rp->lock);
+ if (NULL != rp->prp_incremental)
+ {
+ rp->prp_incremental->delete(&rp->prp_incremental);
+ }
+ if (NULL != rp->prp_total)
+ {
+ rp->prp_total->delete(&rp->prp_total);
+ }
+ if (NULL != rp->replica_object)
+ {
+ object_release(rp->replica_object);
+ }
+ if (NULL != rp->conn)
+ {
+ conn_delete(rp->conn);
+ }
+ rp->prp_active_protocol = NULL;
+ PR_Unlock(rp->lock);
+ slapi_ch_free((void **)rpp);
+}
+
+/*
+ * Destroy a protocol instance XXXggood not complete
+ */
+void
+prot_delete(Repl_Protocol **rpp)
+{
+ Repl_Protocol *rp;
+
+ PR_ASSERT(NULL != rpp);
+ rp = *rpp;
+ /* MAB: rp might be NULL for disabled suffixes. Don't ASSERT on it */
+ if (NULL != rp)
+ {
+ prot_stop(rp);
+ prot_free(rpp);
+ }
+}
+
+
+
+
+
+/*
+ * Get the connection object.
+ */
+Repl_Connection *
+prot_get_connection(Repl_Protocol *rp)
+{
+ Repl_Connection *return_value;
+
+ PR_ASSERT(NULL != rp);
+ PR_Lock(rp->lock);
+ return_value = rp->conn;
+ PR_Unlock(rp->lock);
+ return return_value;
+}
+
+
+
+
+/*
+ * This function causes the total protocol to start.
+ * This is accomplished by registering a state transition
+ * to a new state, and then signaling the incremental
+ * protocol to stop.
+ */
+void
+prot_initialize_replica(Repl_Protocol *rp)
+{
+ PR_ASSERT(NULL != rp);
+
+ PR_Lock(rp->lock);
+ /* check that total protocol is not running */
+ rp->next_state = STATE_PERFORMING_TOTAL_UPDATE;
+ /* Stop the incremental protocol, if running */
+ rp->prp_incremental->stop(rp->prp_incremental);
+ if (rp->prp_total) agmt_set_last_init_status(rp->prp_total->agmt, 0, 0, NULL);
+ PR_Unlock(rp->lock);
+}
+
+
+
+
+
+/*
+ * Main thread for protocol manager.
+
+This is a simple state machine. State transition table:
+
+Initial state: incremental update
+
+STATE EVENT NEXT STATE
+----- ----- ----------
+incremental update shutdown finished
+incremental update total update requested total update
+total update shutdown finished
+total update update complete incremental update
+finished (any) finished
+
+*/
+
+static void
+prot_thread_main(void *arg)
+{
+ Repl_Protocol *rp = (Repl_Protocol *)arg;
+ int done;
+
+ PR_ASSERT(NULL != rp);
+
+ if (rp->agmt) {
+ set_thread_private_agmtname (agmt_get_long_name(rp->agmt));
+ }
+
+ done = 0;
+
+ while (!done)
+ {
+ switch (rp->state)
+ {
+ case STATE_PERFORMING_INCREMENTAL_UPDATE:
+ /* Run the incremental update protocol */
+ PR_Lock(rp->lock);
+ dev_debug("prot_thread_main(STATE_PERFORMING_INCREMENTAL_UPDATE): begin");
+ rp->prp_active_protocol = rp->prp_incremental;
+ PR_Unlock(rp->lock);
+ rp->prp_incremental->run(rp->prp_incremental);
+ dev_debug("prot_thread_main(STATE_PERFORMING_INCREMENTAL_UPDATE): end");
+ break;
+ case STATE_PERFORMING_TOTAL_UPDATE:
+ PR_Lock(rp->lock);
+
+ /* stop incremental protocol if running */
+ rp->prp_active_protocol = rp->prp_total;
+ /* After total protocol finished, return to incremental */
+ rp->next_state = STATE_PERFORMING_INCREMENTAL_UPDATE;
+ PR_Unlock(rp->lock);
+ /* Run the total update protocol */
+ dev_debug("prot_thread_main(STATE_PERFORMING_TOTAL_UPDATE): begin");
+ rp->prp_total->run(rp->prp_total);
+ dev_debug("prot_thread_main(STATE_PERFORMING_TOTAL_UPDATE): end");
+ /* update the agreement entry to notify clients that
+ replica initialization is completed. */
+ agmt_replica_init_done (rp->agmt);
+
+ break;
+ case STATE_FINISHED:
+ dev_debug("prot_thread_main(STATE_FINISHED): exiting prot_thread_main");
+ done = 1;
+ break;
+ }
+ rp->state = rp->next_state;
+ }
+}
+
+
+/*
+ * Start a thread to handle the replication protocol.
+ */
+void
+prot_start(Repl_Protocol *rp)
+{
+ PR_ASSERT(NULL != rp);
+ if (NULL != rp)
+ {
+ if (PR_CreateThread(PR_USER_THREAD, prot_thread_main, (void *)rp,
+ PR_PRIORITY_NORMAL, PR_GLOBAL_THREAD, PR_UNJOINABLE_THREAD, SLAPD_DEFAULT_THREAD_STACKSIZE) == NULL)
+ {
+ PRErrorCode prerr = PR_GetError();
+
+ slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name,
+ "%s: Unable to create protocol thread; NSPR error - %d, %s\n",
+ agmt_get_long_name(rp->agmt),
+ prerr, slapd_pr_strerror(prerr));
+ }
+ }
+ else
+ {
+ slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name, "Unable to start "
+ "protocol object - NULL protocol object passed to prot_start.\n");
+ }
+}
+
+
+
+
+
+/*
+ * Stop a protocol instance.
+ */
+void
+prot_stop(Repl_Protocol *rp)
+{
+ PR_ASSERT(NULL != rp);
+ if (NULL != rp)
+ {
+ PR_Lock(rp->lock);
+ rp->next_state = STATE_FINISHED;
+ if (NULL != rp->prp_incremental)
+ {
+ if (rp->prp_incremental->stop(rp->prp_incremental) != 0)
+ {
+ slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name,
+ "Warning: incremental protocol for replica \"%s\" "
+ "did not shut down properly.\n",
+ agmt_get_long_name(rp->agmt));
+ }
+ }
+ if (NULL != rp->prp_total)
+ {
+ if (rp->prp_total->stop(rp->prp_total) != 0)
+ {
+ slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name,
+ "Warning: total protocol for replica \"%s\" "
+ "did not shut down properly.\n",
+ agmt_get_long_name(rp->agmt));
+ }
+ }
+ PR_Unlock(rp->lock);
+ }
+ else
+ {
+ slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name, "Error: prot_stop() "
+ " called on NULL protocol instance.\n");
+ }
+}
+
+
+
+
+
+/*
+ * Call the notify_update method of the incremental or total update
+ * protocol, is either is active.
+ */
+void
+prot_notify_update(Repl_Protocol *rp)
+{
+ /* MAB: rp might be NULL for disabled suffixes. Don't ASSERT on it */
+ if (NULL == rp) return;
+
+ PR_Lock(rp->lock);
+ if (NULL != rp->prp_active_protocol)
+ {
+ rp->prp_active_protocol->notify_update(rp->prp_active_protocol);
+ }
+ PR_Unlock(rp->lock);
+}
+
+
+/*
+ * Call the notify_agmt_changed method of the incremental or total update
+ * protocol, is either is active.
+ */
+void
+prot_notify_agmt_changed(Repl_Protocol *rp, char * agmt_name)
+{
+ /* MAB: rp might be NULL for disabled suffixes. Don't ASSERT on it */
+ if (NULL == rp) {
+ slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name,
+ "Replication agreement for %s could not be updated. "
+ "For replication to take place, please enable the suffix "
+ "and restart the server\n", agmt_name);
+ return;
+ }
+
+ PR_Lock(rp->lock);
+ if (NULL != rp->prp_active_protocol)
+ {
+ rp->prp_active_protocol->notify_agmt_changed(rp->prp_active_protocol);
+ }
+ PR_Unlock(rp->lock);
+}
+
+
+void
+prot_notify_window_opened (Repl_Protocol *rp)
+{
+ /* MAB: rp might be NULL for disabled suffixes. Don't ASSERT on it */
+ if (NULL == rp) return;
+
+ PR_Lock(rp->lock);
+ if (NULL != rp->prp_active_protocol)
+ {
+ rp->prp_active_protocol->notify_window_opened(rp->prp_active_protocol);
+ }
+ PR_Unlock(rp->lock);
+}
+
+
+void
+prot_notify_window_closed (Repl_Protocol *rp)
+{
+ /* MAB: rp might be NULL for disabled suffixes. Don't ASSERT on it */
+ if (NULL == rp) return;
+
+ PR_Lock(rp->lock);
+ if (NULL != rp->prp_active_protocol)
+ {
+ rp->prp_active_protocol->notify_window_closed(rp->prp_active_protocol);
+ }
+ PR_Unlock(rp->lock);
+}
+
+
+int
+prot_status(Repl_Protocol *rp)
+{
+ int return_status = PROTOCOL_STATUS_UNKNOWN;
+
+ /* MAB: rp might be NULL for disabled suffixes. Don't ASSERT on it */
+ if (NULL != rp)
+ {
+ PR_Lock(rp->lock);
+ if (NULL != rp->prp_active_protocol)
+ {
+ return_status = rp->prp_active_protocol->status(rp->prp_active_protocol);
+ }
+ PR_Unlock(rp->lock);
+ }
+ return return_status;
+}
+
+
+/*
+ * Start an incremental protocol session, even if we're not
+ * currently in a schedule window.
+ * If the total protocol is active, do nothing.
+ * Otherwise, notify the incremental protocol that it should
+ * run once.
+ */
+void
+prot_replicate_now(Repl_Protocol *rp)
+{
+ /* MAB: rp might be NULL for disabled suffixes. Don't ASSERT on it */
+
+ if (NULL != rp)
+ {
+ PR_Lock(rp->lock);
+ if (rp->prp_incremental == rp->prp_active_protocol)
+ {
+ rp->prp_active_protocol->update_now(rp->prp_active_protocol);
+ }
+ PR_Unlock(rp->lock);
+ }
+}
+
+/*
+ * A little factory function to create a protocol
+ * instance of the correct type.
+ */
+static Private_Repl_Protocol *
+private_protocol_factory(Repl_Protocol *rp, int type)
+{
+ Private_Repl_Protocol *prp;
+ switch (type)
+ {
+ case PROTOCOL_5_INCREMENTAL:
+ prp = Repl_5_Inc_Protocol_new(rp);
+ break;
+ case PROTOCOL_5_TOTAL:
+ prp = Repl_5_Tot_Protocol_new(rp);
+ break;
+ }
+ return prp;
+}
diff --git a/ldap/servers/plugins/replication/repl5_protocol_util.c b/ldap/servers/plugins/replication/repl5_protocol_util.c
new file mode 100644
index 00000000..16f65485
--- /dev/null
+++ b/ldap/servers/plugins/replication/repl5_protocol_util.c
@@ -0,0 +1,468 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+
+/* repl5_protocol_util.c */
+/*
+
+Code common to both incremental and total protocols.
+
+*/
+
+#include "repl5.h"
+#include "repl5_prot_private.h"
+
+
+/*
+ * Obtain a current CSN (e.g. one that would have been
+ * generated for an operation occurring at this time)
+ * for a given replica.
+ */
+CSN *
+get_current_csn(Slapi_DN *replarea_sdn)
+{
+ Object *replica_obj;
+ Replica *replica;
+ Object *gen_obj;
+ CSNGen *gen;
+ CSN *current_csn = NULL;
+
+ if (NULL != replarea_sdn)
+ {
+ replica_obj = replica_get_replica_from_dn(replarea_sdn);
+ if (NULL != replica_obj)
+ {
+ replica = object_get_data(replica_obj);
+ if (NULL != replica)
+ {
+ gen_obj = replica_get_csngen(replica);
+ if (NULL != gen_obj)
+ {
+ gen = (CSNGen *)object_get_data(gen_obj);
+ if (NULL != gen)
+ {
+ if (csngen_new_csn(gen, &current_csn,
+ PR_FALSE /* notify */) != CSN_SUCCESS)
+ {
+ current_csn = NULL;
+
+ }
+ object_release(gen_obj);
+ }
+ }
+ }
+ }
+ }
+ return current_csn;
+}
+
+
+/*
+ * Acquire exclusive access to a replica. Send a start replication extended
+ * operation to the replica. The response will contain a success code, and
+ * optionally the replica's update vector if acquisition is successful.
+ * This function returns one of the following:
+ * ACQUIRE_SUCCESS - the replica was acquired, and we have exclusive update access
+ * ACQUIRE_REPLICA_BUSY - another master was updating the replica
+ * ACQUIRE_FATAL_ERROR - something bad happened, and it's not likely to improve
+ * if we wait.
+ * ACQUIRE_TRANSIENT_ERROR - something bad happened, but it's probably worth
+ * another try after waiting a while.
+ * If ACQUIRE_SUCCESS is returned, then ruv will point to the replica's update
+ * vector. It's possible that the replica does something goofy and doesn't
+ * return us an update vector, so be prepared for ruv to be NULL (but this is
+ * an error).
+ */
+int
+acquire_replica(Private_Repl_Protocol *prp, char *prot_oid, RUV **ruv)
+{
+ int return_value;
+ ConnResult crc;
+ Repl_Connection *conn;
+
+ PR_ASSERT(prp && prot_oid);
+
+ if (prp->replica_acquired) /* we already acquire replica */
+ {
+ slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name,
+ "%s: Remote replica already acquired\n",
+ agmt_get_long_name(prp->agmt));
+ return_value = ACQUIRE_FATAL_ERROR;
+ return ACQUIRE_SUCCESS;
+ }
+
+ if (NULL != ruv)
+ {
+ ruv_destroy ( ruv );
+ }
+
+ if (strcmp(prot_oid, REPL_NSDS50_INCREMENTAL_PROTOCOL_OID) == 0)
+ {
+ Replica *replica;
+ Object *supl_ruv_obj, *cons_ruv_obj;
+ PRBool is_newer = PR_FALSE;
+
+ object_acquire(prp->replica_object);
+ replica = object_get_data(prp->replica_object);
+ supl_ruv_obj = replica_get_ruv ( replica );
+ cons_ruv_obj = agmt_get_consumer_ruv ( prp->agmt );
+ is_newer = ruv_is_newer ( supl_ruv_obj, cons_ruv_obj );
+ if ( supl_ruv_obj ) object_release ( supl_ruv_obj );
+ if ( cons_ruv_obj ) object_release ( cons_ruv_obj );
+ object_release (prp->replica_object);
+ replica = NULL;
+
+ if (is_newer == PR_FALSE) {
+ prp->last_acquire_response_code = NSDS50_REPL_UPTODATE;
+ return ACQUIRE_CONSUMER_WAS_UPTODATE;
+ }
+ }
+
+ prp->last_acquire_response_code = NSDS50_REPL_REPLICA_NO_RESPONSE;
+
+ /* Get the connection */
+ conn = prp->conn;
+
+ crc = conn_connect(conn);
+ if (CONN_OPERATION_FAILED == crc)
+ {
+ return_value = ACQUIRE_TRANSIENT_ERROR;
+ }
+ else if (CONN_SSL_NOT_ENABLED == crc)
+ {
+ return_value = ACQUIRE_FATAL_ERROR;
+ }
+ else
+ {
+ /* we don't want the timer to go off in the middle of an operation */
+ conn_cancel_linger(conn);
+ /* Does the remote replica support the 5.0 protocol? */
+ crc = conn_replica_supports_ds5_repl(conn);
+ if (CONN_DOES_NOT_SUPPORT_DS5_REPL == crc)
+ {
+ return_value = ACQUIRE_FATAL_ERROR;
+ }
+ else if (CONN_NOT_CONNECTED == crc || CONN_OPERATION_FAILED == crc)
+ {
+ /* We don't know anything about the remote replica. Try again later. */
+ return_value = ACQUIRE_TRANSIENT_ERROR;
+ }
+ else
+ {
+ /* Good to go. Start the protocol. */
+ CSN *current_csn = NULL;
+ struct berval *retdata = NULL;
+ char *retoid = NULL;
+ Slapi_DN *replarea_sdn;
+
+ /* Obtain a current CSN */
+ replarea_sdn = agmt_get_replarea(prp->agmt);
+ current_csn = get_current_csn(replarea_sdn);
+ if (NULL != current_csn)
+ {
+ struct berval *payload = NSDS50StartReplicationRequest_new(
+ prot_oid, slapi_sdn_get_ndn(replarea_sdn),
+ NULL /* XXXggood need to provide referral(s) */, current_csn);
+ /* JCMREPL - Need to extract the referrals from the RUV */
+ csn_free(&current_csn);
+ current_csn = NULL;
+ crc = conn_send_extended_operation(conn,
+ REPL_START_NSDS50_REPLICATION_REQUEST_OID, payload, &retoid,
+ &retdata, NULL /* update control */, NULL /* returned controls */);
+ ber_bvfree(payload);
+ payload = NULL;
+ /* Look at the response we got. */
+ if (CONN_OPERATION_SUCCESS == crc)
+ {
+ /*
+ * Extop was processed. Look at extop response to see if we're
+ * permitted to go ahead.
+ */
+ struct berval **ruv_bervals = NULL;
+ int extop_result;
+ int extop_rc = decode_repl_ext_response(retdata, &extop_result,
+ &ruv_bervals);
+ if (0 == extop_rc)
+ {
+ prp->last_acquire_response_code = extop_result;
+ switch (extop_result)
+ {
+ /* XXXggood handle other error codes here */
+ case NSDS50_REPL_INTERNAL_ERROR:
+ slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name,
+ "%s: Unable to acquire replica: "
+ "an internal error occurred on the remote replica. "
+ "Replication is aborting.\n",
+ agmt_get_long_name(prp->agmt));
+ return_value = ACQUIRE_FATAL_ERROR;
+ break;
+ case NSDS50_REPL_PERMISSION_DENIED:
+ /* Not allowed to send updates */
+ {
+ char *repl_binddn = agmt_get_binddn(prp->agmt);
+ slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name,
+ "%s: Unable to acquire replica: permission denied. "
+ "The bind dn \"%s\" does not have permission to "
+ "supply replication updates to the replica. "
+ "Will retry later.\n",
+ agmt_get_long_name(prp->agmt), repl_binddn);
+ slapi_ch_free((void **)&repl_binddn);
+ return_value = ACQUIRE_TRANSIENT_ERROR;
+ break;
+ }
+ case NSDS50_REPL_NO_SUCH_REPLICA:
+ /* There is no such replica on the consumer */
+ {
+ Slapi_DN *repl_root = agmt_get_replarea(prp->agmt);
+ slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name,
+ "%s: Unable to acquire replica: there is no "
+ "replicated area \"%s\" on the consumer server. "
+ "Replication is aborting.\n",
+ agmt_get_long_name(prp->agmt),
+ slapi_sdn_get_dn(repl_root));
+ slapi_sdn_free(&repl_root);
+ return_value = ACQUIRE_FATAL_ERROR;
+ break;
+ }
+ case NSDS50_REPL_EXCESSIVE_CLOCK_SKEW:
+ /* Large clock skew between the consumer and the supplier */
+ slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name,
+ "%s: Unable to acquire replica: "
+ "Excessive clock skew between the supplier and "
+ "the consumer. Replication is aborting.\n",
+ agmt_get_long_name(prp->agmt));
+ return_value = ACQUIRE_FATAL_ERROR;
+ break;
+ case NSDS50_REPL_DECODING_ERROR:
+ /* We sent something the replica couldn't understand. */
+ slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name,
+ "%s: Unable to acquire replica: "
+ "the consumer was unable to decode the "
+ "startReplicationRequest extended operation sent by the "
+ "supplier. Replication is aborting.\n",
+ agmt_get_long_name(prp->agmt));
+ return_value = ACQUIRE_FATAL_ERROR;
+ break;
+ case NSDS50_REPL_REPLICA_BUSY:
+ /* Someone else is updating the replica. Try later. */
+ slapi_log_error(SLAPI_LOG_REPL, repl_plugin_name,
+ "%s: Unable to acquire replica: "
+ "the replica is currently being updated"
+ "by another supplier. Will try later\n",
+ agmt_get_long_name(prp->agmt));
+ return_value = ACQUIRE_REPLICA_BUSY;
+ break;
+ case NSDS50_REPL_LEGACY_CONSUMER:
+ /* remote replica is a legacy consumer */
+ slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name,
+ "%s: Unable to acquire replica: the replica "
+ "is supplied by a legacy supplier. "
+ "Replication is aborting.\n", agmt_get_long_name(prp->agmt));
+ return_value = ACQUIRE_FATAL_ERROR;
+ break;
+ case NSDS50_REPL_REPLICAID_ERROR:
+ /* remote replica detected a duplicate ReplicaID */
+ slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name,
+ "%s: Unable to aquire replica: the replica "
+ "has the same Replica ID as this one. "
+ "Replication is aborting.\n",
+ agmt_get_long_name(prp->agmt));
+ return_value = ACQUIRE_FATAL_ERROR;
+ break;
+ case NSDS50_REPL_REPLICA_READY:
+ /* We've acquired the replica. */
+ slapi_log_error(SLAPI_LOG_REPL, repl_plugin_name,
+ "%s: Replica was successfully acquired.\n",
+ agmt_get_long_name(prp->agmt));
+ /* Parse the update vector */
+ if (NULL != ruv_bervals && NULL != ruv)
+ {
+ if (ruv_init_from_bervals(ruv_bervals, ruv) != RUV_SUCCESS)
+ {
+ /* Couldn't parse the update vector */
+ *ruv = NULL;
+ slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name,
+ "%s: Warning: acquired replica, "
+ "but could not parse update vector. "
+ "The replica must be reinitialized.\n",
+ agmt_get_long_name(prp->agmt));
+ }
+ }
+
+ /* Save consumer's RUV in the replication agreement.
+ It is used by the changelog trimming code */
+ if (ruv && *ruv)
+ agmt_set_consumer_ruv (prp->agmt, *ruv);
+
+ return_value = ACQUIRE_SUCCESS;
+ break;
+ default:
+ return_value = ACQUIRE_FATAL_ERROR;
+ }
+ }
+ else
+ {
+ /* Couldn't parse the response */
+ slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name,
+ "%s: Unable to parse the response to the "
+ "startReplication extended operation. "
+ "Replication is aborting.\n",
+ agmt_get_long_name(prp->agmt));
+ prp->last_acquire_response_code = NSDS50_REPL_INTERNAL_ERROR;
+ return_value = ACQUIRE_FATAL_ERROR;
+ }
+ if (NULL != ruv_bervals)
+ ber_bvecfree(ruv_bervals);
+ }
+ else
+ {
+ int operation, error;
+ conn_get_error(conn, &operation, &error);
+
+ /* Couldn't send the extended operation */
+ return_value = ACQUIRE_TRANSIENT_ERROR; /* XXX right return value? */
+ slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name,
+ "%s: Unable to send a startReplication "
+ "extended operation to consumer (%s). Will retry later.\n",
+ agmt_get_long_name(prp->agmt),
+ error ? ldap_err2string(error) : "unknown error");
+ }
+ }
+ else
+ {
+ /* Couldn't get a current CSN */
+ slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name,
+ "%s: Unable to obtain current CSN. "
+ "Replication is aborting.\n",
+ agmt_get_long_name(prp->agmt));
+ return_value = ACQUIRE_FATAL_ERROR;
+ }
+ slapi_sdn_free(&replarea_sdn);
+ if (NULL != retoid)
+ ldap_memfree(retoid);
+ if (NULL != retdata)
+ ber_bvfree(retdata);
+ }
+ }
+
+ if (ACQUIRE_SUCCESS != return_value)
+ {
+ /* could not acquire the replica, so reinstate the linger timer, since this
+ means we won't call release_replica, which also reinstates the timer */
+ conn_start_linger(conn);
+ }
+ else
+ {
+ /* replica successfully acquired */
+ prp->replica_acquired = PR_TRUE;
+ }
+
+ return return_value;
+}
+
+
+/*
+ * Release a replica by sending an "end replication" extended request.
+ */
+void
+release_replica(Private_Repl_Protocol *prp)
+{
+ int rc;
+ struct berval *retdata = NULL;
+ char *retoid = NULL;
+ struct berval *payload = NULL;
+ Slapi_DN *replarea_sdn = NULL;
+
+ PR_ASSERT(NULL != prp);
+ PR_ASSERT(NULL != prp->conn);
+
+ if (!prp->replica_acquired)
+ return;
+
+ replarea_sdn = agmt_get_replarea(prp->agmt);
+ payload = NSDS50EndReplicationRequest_new((char *)slapi_sdn_get_dn(replarea_sdn)); /* XXXggood had to cast away const */
+ slapi_sdn_free(&replarea_sdn);
+ rc = conn_send_extended_operation(prp->conn,
+ REPL_END_NSDS50_REPLICATION_REQUEST_OID, payload, &retoid,
+ &retdata, NULL /* update control */, NULL /* returned controls */);
+ if (0 != rc)
+ {
+ int operation, error;
+ conn_get_error(prp->conn, &operation, &error);
+ slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name,
+ "%s: Warning: unable to send endReplication extended operation (%s)\n",
+ agmt_get_long_name(prp->agmt),
+ error ? ldap_err2string(error) : "unknown error");
+ }
+ else
+ {
+ struct berval **ruv_bervals = NULL; /* Shouldn't actually be returned */
+ int extop_result;
+ int extop_rc = decode_repl_ext_response(retdata, &extop_result,
+ (struct berval ***)&ruv_bervals);
+ if (0 == extop_rc)
+ {
+ if (NSDS50_REPL_REPLICA_RELEASE_SUCCEEDED == extop_result)
+ {
+ slapi_log_error(SLAPI_LOG_REPL, repl_plugin_name,
+ "%s: Successfully released consumer\n", agmt_get_long_name(prp->agmt));
+ }
+ else
+ {
+ slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name,
+ "%s: Unable to release consumer: response code %d\n",
+ agmt_get_long_name(prp->agmt), extop_result);
+ /* disconnect from the consumer so that it does not stay locked */
+ conn_disconnect (prp->conn);
+ }
+ }
+ else
+ {
+ /* Couldn't parse the response */
+ slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name,
+ "%s: Warning: Unable to parse the response "
+ " to the endReplication extended operation.\n",
+ agmt_get_long_name(prp->agmt));
+ }
+ if (NULL != ruv_bervals)
+ ber_bvecfree(ruv_bervals);
+ /* XXXggood free ruv_bervals if we got them for some reason */
+ }
+ if (NULL != payload)
+ ber_bvfree(payload);
+ if (NULL != retoid)
+ ldap_memfree(retoid);
+ if (NULL != retdata)
+ ber_bvfree(retdata);
+
+ /* replica is released, start the linger timer on the connection, which
+ was stopped in acquire_replica */
+ conn_start_linger(prp->conn);
+
+ prp->replica_acquired = PR_FALSE;
+}
+
+/* converts consumer's response to a string */
+char *
+protocol_response2string (int response)
+{
+ switch (response)
+ {
+ case NSDS50_REPL_REPLICA_READY: return "replica acquired";
+ case NSDS50_REPL_REPLICA_BUSY: return "replica busy";
+ case NSDS50_REPL_EXCESSIVE_CLOCK_SKEW: return "excessive clock skew";
+ case NSDS50_REPL_PERMISSION_DENIED: return "permission denied";
+ case NSDS50_REPL_DECODING_ERROR: return "decoding error";
+ case NSDS50_REPL_UNKNOWN_UPDATE_PROTOCOL: return "unknown update protocol";
+ case NSDS50_REPL_NO_SUCH_REPLICA: return "no such replica";
+ case NSDS50_REPL_BELOW_PURGEPOINT: return "csn below purge point";
+ case NSDS50_REPL_INTERNAL_ERROR: return "internal error";
+ case NSDS50_REPL_REPLICA_RELEASE_SUCCEEDED: return "replica released";
+ case NSDS50_REPL_LEGACY_CONSUMER: return "replica is a legacy consumer";
+ case NSDS50_REPL_REPLICAID_ERROR: return "duplicate replica ID detected";
+ case NSDS50_REPL_UPTODATE: return "no change to send";
+ default: return "unknown error";
+ }
+}
diff --git a/ldap/servers/plugins/replication/repl5_replica.c b/ldap/servers/plugins/replication/repl5_replica.c
new file mode 100644
index 00000000..5bc3e8ee
--- /dev/null
+++ b/ldap/servers/plugins/replication/repl5_replica.c
@@ -0,0 +1,3387 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+/* repl5_replica.c */
+
+#include "slapi-plugin.h"
+#include "repl.h" /* ONREPL - this is bad */
+#include "repl5.h"
+#include "repl_shared.h"
+#include "csnpl.h"
+#include "cl5_api.h"
+
+/* from proto-slap.h */
+int g_get_shutdown();
+
+#define RUV_SAVE_INTERVAL (30 * 1000) /* 30 seconds */
+#define START_UPDATE_DELAY 2 /* 2 second */
+#define START_REAP_DELAY 3600 /* 1 hour */
+
+#define REPLICA_RDN "cn=replica"
+#define CHANGELOG_RDN "cn=legacy changelog"
+
+/*
+ * A replica is a locally-held copy of a portion of the DIT.
+ */
+struct replica {
+ Slapi_DN *repl_root; /* top of the replicated area */
+ char *repl_name; /* unique replica name */
+ PRBool new_name; /* new name was generated - need to be saved */
+ ReplicaUpdateDNList updatedn_list; /* list of dns with which a supplier should bind
+ to update this replica */
+ ReplicaType repl_type; /* is this replica read-only ? */
+ PRBool legacy_consumer; /* if true, this replica is supplied by 4.0 consumer */
+ char* legacy_purl; /* partial url of the legacy supplier */
+ ReplicaId repl_rid; /* replicaID */
+ Object *repl_ruv; /* replica update vector */
+ PRBool repl_ruv_dirty; /* Dirty flag for ruv */
+ CSNPL *min_csn_pl; /* Pending list for minimal CSN */
+ void *csn_pl_reg_id; /* registration assignment for csn callbacks */
+ unsigned long repl_state_flags; /* state flags */
+ PRUint32 repl_flags; /* persistent, externally visible flags */
+ PRLock *repl_lock; /* protects entire structure */
+ Slapi_Eq_Context repl_eqcxt_rs; /* context to cancel event that saves ruv */
+ Slapi_Eq_Context repl_eqcxt_tr; /* context to cancel event that reaps tombstones */
+ Object *repl_csngen; /* CSN generator for this replica */
+ PRBool repl_csn_assigned; /* Flag set when new csn is assigned. */
+ PRUint32 repl_purge_delay; /* When purgeable, CSNs are held on to for this many extra seconds */
+ PRBool tombstone_reap_stop; /* TRUE when the tombstone reaper should stop */
+ PRBool tombstone_reap_active; /* TRUE when the tombstone reaper is running */
+ long tombstone_reap_interval; /* Time in seconds between tombstone reaping */
+ Slapi_ValueSet *repl_referral; /* A list of administrator provided referral URLs */
+ PRBool state_update_inprogress; /* replica state is being updated */
+ PRLock *agmt_lock; /* protects agreement creation, start and stop */
+ char *locking_purl; /* supplier who has exclusive access */
+};
+
+
+typedef struct reap_callback_data
+{
+ int rc;
+ unsigned long num_entries;
+ unsigned long num_purged_entries;
+ CSN *purge_csn;
+ PRBool *tombstone_reap_stop;
+} reap_callback_data;
+
+
+/* Forward declarations of helper functions*/
+static Slapi_Entry* _replica_get_config_entry (const Slapi_DN *root);
+static int _replica_check_validity (const Replica *r);
+static int _replica_init_from_config (Replica *r, Slapi_Entry *e, char *errortext);
+static int _replica_update_entry (Replica *r, Slapi_Entry *e, char *errortext);
+static int _replica_configure_ruv (Replica *r, PRBool isLocked);
+static void _replica_update_state (time_t when, void *arg);
+static char * _replica_get_config_dn (const Slapi_DN *root);
+static char * _replica_type_as_string (const Replica *r);
+static int replica_create_ruv_tombstone(Replica *r);
+static void assign_csn_callback(const CSN *csn, void *data);
+static void abort_csn_callback(const CSN *csn, void *data);
+static void eq_cb_reap_tombstones(time_t when, void *arg);
+static CSN *_replica_get_purge_csn_nolock (const Replica *r);
+static void replica_get_referrals_nolock (const Replica *r, char ***referrals);
+static void replica_clear_legacy_referrals (const Slapi_DN *repl_root_sdn, char **referrals, const char *state);
+static void replica_remove_legacy_attr (const Slapi_DN *repl_root_sdn, const char *attr);
+static int replica_log_ruv_elements_nolock (const Replica *r);
+static void replica_replace_ruv_tombstone(Replica *r);
+static void start_agreements_for_replica (Replica *r, PRBool start);
+
+/* Allocates new replica and reads its state and state of its component from
+ * various parts of the DIT.
+ */
+Replica *
+replica_new(const Slapi_DN *root)
+{
+ Replica *r = NULL;
+ Slapi_Entry *e = NULL;
+ char errorbuf[BUFSIZ];
+ char ebuf[BUFSIZ];
+
+ PR_ASSERT (root);
+
+ /* check if there is a replica associated with the tree */
+ e = _replica_get_config_entry (root);
+ if (e)
+ {
+ errorbuf[0] = '\0';
+ r = replica_new_from_entry(e, errorbuf,
+ PR_FALSE /* not a newly added entry */);
+
+ if (NULL == r)
+ {
+ slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name, "Unable to "
+ "configure replica %s: %s\n",
+ escape_string(slapi_sdn_get_dn(root), ebuf),
+ errorbuf);
+ }
+
+ slapi_entry_free (e);
+ }
+
+ return r;
+}
+
+/* constructs the replica object from the newly added entry */
+Replica *
+replica_new_from_entry (Slapi_Entry *e, char *errortext, PRBool is_add_operation)
+{
+ int rc = 0;
+ Replica *r;
+ RUV *ruv;
+ char *repl_name = NULL;
+
+ if (e == NULL)
+ {
+ if (NULL != errortext)
+ {
+ sprintf (errortext, "NULL entry");
+ }
+ return NULL;
+ }
+
+ r = (Replica *)slapi_ch_calloc(1, sizeof(Replica));
+
+ if ((r->repl_lock = PR_NewLock()) == NULL)
+ {
+ if (NULL != errortext)
+ {
+ sprintf (errortext, "failed to create replica lock");
+ }
+ rc = -1;
+ goto done;
+ }
+
+ if ((r->agmt_lock = PR_NewLock()) == NULL)
+ {
+ if (NULL != errortext)
+ {
+ sprintf (errortext, "failed to create replica lock");
+ }
+ rc = -1;
+ goto done;
+ }
+
+ /* read parameters from the replica config entry */
+ rc = _replica_init_from_config (r, e, errortext);
+ if (rc != 0)
+ {
+ goto done;
+ }
+
+ /* configure ruv */
+ rc = _replica_configure_ruv (r, PR_FALSE);
+ if (rc != 0)
+ {
+ goto done;
+ }
+
+ /* If smallest csn exists in RUV for our local replica, it's ok to begin iteration */
+ ruv = (RUV*) object_get_data (r->repl_ruv);
+ PR_ASSERT (ruv);
+
+ if (is_add_operation)
+ {
+ /*
+ * This is called by an ldap add operation.
+ * Update the entry to contain information generated
+ * during replica initialization
+ */
+ rc = _replica_update_entry (r, e, errortext);
+ }
+ else
+ {
+ /*
+ * Entry is already in dse.ldif - update it on the disk
+ * (done by the update state event scheduled below)
+ */
+ }
+ if (rc != 0)
+ goto done;
+
+ /* ONREPL - the state update can occur before the entry is added to the DIT.
+ In that case the updated would fail but nothing bad would happen. The next
+ scheduled update would save the state */
+ repl_name = slapi_ch_strdup (r->repl_name);
+ r->repl_eqcxt_rs = slapi_eq_repeat(_replica_update_state, repl_name,
+ current_time () + START_UPDATE_DELAY, RUV_SAVE_INTERVAL);
+
+ if (r->tombstone_reap_interval > 0)
+ {
+ /*
+ * Reap Tombstone should be started some time after the plugin started.
+ * This will allow the server to fully start before consuming resources.
+ */
+ repl_name = slapi_ch_strdup (r->repl_name);
+ r->repl_eqcxt_tr = slapi_eq_repeat(eq_cb_reap_tombstones, repl_name, current_time() + START_REAP_DELAY, 1000 * r->tombstone_reap_interval);
+ }
+
+ if (r->legacy_consumer)
+ {
+ char ebuf[BUFSIZ];
+
+ legacy_consumer_init_referrals (r);
+ slapi_log_error(SLAPI_LOG_REPL, repl_plugin_name, "replica_new_from_entry: "
+ "replica for %s was configured as legacy consumer\n",
+ escape_string(slapi_sdn_get_dn(r->repl_root),ebuf));
+ }
+
+done:
+ if (rc != 0 && r)
+ {
+ replica_destroy ((void**)&r);
+ }
+
+ return r;
+}
+
+
+void
+replica_flush(Replica *r)
+{
+ PR_ASSERT(NULL != r);
+ if (NULL != r)
+ {
+ PR_Lock(r->repl_lock);
+ /* Make sure we dump the CSNGen state */
+ r->repl_csn_assigned = PR_TRUE;
+ PR_Unlock(r->repl_lock);
+ /* This function take the Lock Inside */
+ /* And also write the RUV */
+ _replica_update_state((time_t)0, r->repl_name);
+ }
+}
+
+
+/*
+ * Deallocate a replica. arg should point to the address of a
+ * pointer that points to a replica structure.
+ */
+void
+replica_destroy(void **arg)
+{
+ Replica *r;
+ void *repl_name;
+
+ if (arg == NULL)
+ return;
+
+ r = *((Replica **)arg);
+
+ PR_ASSERT(r);
+
+ slapi_log_error (SLAPI_LOG_REPL, NULL, "replica_destroy\n");
+
+ /*
+ * The function will not be called unless the refcnt of its
+ * wrapper object is 0. Hopefully this refcnt could sync up
+ * this destruction and the events such as tombstone reap
+ * and ruv updates.
+ */
+
+ if (r->repl_eqcxt_rs)
+ {
+ repl_name = slapi_eq_get_arg (r->repl_eqcxt_rs);
+ slapi_ch_free (&repl_name);
+ slapi_eq_cancel(r->repl_eqcxt_rs);
+ r->repl_eqcxt_rs = NULL;
+ }
+
+ if (r->repl_eqcxt_tr)
+ {
+ repl_name = slapi_eq_get_arg (r->repl_eqcxt_tr);
+ slapi_ch_free (&repl_name);
+ slapi_eq_cancel(r->repl_eqcxt_tr);
+ r->repl_eqcxt_tr = NULL;
+ }
+
+ if (r->repl_root)
+ {
+ slapi_sdn_free(&r->repl_root);
+ }
+
+ slapi_ch_free_string(&r->locking_purl);
+
+ if (r->updatedn_list)
+ {
+ replica_updatedn_list_free(r->updatedn_list);
+ r->updatedn_list = NULL;
+ }
+
+ /* slapi_ch_free accepts NULL pointer */
+ slapi_ch_free ((void**)&r->repl_name);
+ slapi_ch_free ((void**)&r->legacy_purl);
+
+ if (r->repl_lock)
+ {
+ PR_DestroyLock(r->repl_lock);
+ r->repl_lock = NULL;
+ }
+
+ if (r->agmt_lock)
+ {
+ PR_DestroyLock(r->agmt_lock);
+ r->agmt_lock = NULL;
+ }
+
+ if(NULL != r->repl_ruv)
+ {
+ object_release(r->repl_ruv);
+ }
+
+ if(NULL != r->repl_csngen)
+ {
+ if (r->csn_pl_reg_id)
+ {
+ csngen_unregister_callbacks((CSNGen *)object_get_data (r->repl_csngen), r->csn_pl_reg_id);
+ }
+ object_release(r->repl_csngen);
+ }
+
+ if (NULL != r->repl_referral)
+ {
+ slapi_valueset_free(r->repl_referral);
+ }
+
+ if (NULL != r->min_csn_pl)
+ {
+ csnplFree(&r->min_csn_pl);;
+ }
+
+ slapi_ch_free((void **)arg);
+}
+
+/*
+ * Attempt to obtain exclusive access to replica (advisory only)
+ *
+ * Returns PR_TRUE if exclusive access was granted,
+ * PR_FALSE otherwise
+ * The parameter isInc tells whether or not the replica is being
+ * locked for an incremental update session - if the replica is
+ * successfully locked, this value is used - if the replica is already
+ * in use, this value will be set to TRUE or FALSE, depending on what
+ * type of update session has the replica in use currently
+ * locking_purl is the supplier who is attempting to acquire access
+ * current_purl is the supplier who already has access, if any
+ */
+PRBool
+replica_get_exclusive_access(Replica *r, PRBool *isInc, int connid, int opid,
+ const char *locking_purl,
+ char **current_purl)
+{
+ char ebuf[BUFSIZ];
+ PRBool rval = PR_TRUE;
+
+ PR_ASSERT(r);
+
+ PR_Lock(r->repl_lock);
+ if (r->repl_state_flags & REPLICA_IN_USE)
+ {
+ if (isInc)
+ *isInc = (r->repl_state_flags & REPLICA_INCREMENTAL_IN_PROGRESS);
+
+ slapi_log_error(SLAPI_LOG_REPL, repl_plugin_name,
+ "conn=%d op=%d repl=\"%s\": "
+ "Replica in use locking_purl=%s\n",
+ connid, opid,
+ escape_string(slapi_sdn_get_dn(r->repl_root),ebuf),
+ r->locking_purl ? r->locking_purl : "unknown");
+ rval = PR_FALSE;
+ if (current_purl)
+ {
+ *current_purl = slapi_ch_strdup(r->locking_purl);
+ }
+ }
+ else
+ {
+ slapi_log_error(SLAPI_LOG_REPL, repl_plugin_name,
+ "conn=%d op=%d repl=\"%s\": Acquired replica\n",
+ connid, opid,
+ escape_string(slapi_sdn_get_dn(r->repl_root),ebuf));
+ r->repl_state_flags |= REPLICA_IN_USE;
+ if (isInc && *isInc)
+ {
+ r->repl_state_flags |= REPLICA_INCREMENTAL_IN_PROGRESS;
+ }
+ else
+ {
+ /* if connid or opid != 0, it's a total update */
+ /* Both set to 0 means we're disabling replication */
+ if (connid || opid)
+ {
+ r->repl_state_flags |= REPLICA_TOTAL_IN_PROGRESS;
+ }
+ }
+ slapi_ch_free_string(&r->locking_purl);
+ r->locking_purl = slapi_ch_strdup(locking_purl);
+ }
+ PR_Unlock(r->repl_lock);
+ return rval;
+}
+
+/*
+ * Relinquish exclusive access to the replica
+ */
+void
+replica_relinquish_exclusive_access(Replica *r, int connid, int opid)
+{
+ char ebuf[BUFSIZ];
+ PRBool isInc;
+
+ PR_ASSERT(r);
+
+ PR_Lock(r->repl_lock);
+ isInc = (r->repl_state_flags & REPLICA_INCREMENTAL_IN_PROGRESS);
+ /* check to see if the replica is in use and log a warning if not */
+ if (!(r->repl_state_flags & REPLICA_IN_USE))
+ {
+ slapi_log_error(SLAPI_LOG_REPL, repl_plugin_name,
+ "conn=%d op=%d repl=\"%s\": "
+ "Replica not in use\n",
+ connid, opid,
+ escape_string(slapi_sdn_get_dn(r->repl_root),ebuf));
+ } else {
+ slapi_log_error(SLAPI_LOG_REPL, repl_plugin_name,
+ "conn=%d op=%d repl=\"%s\": "
+ "Released replica\n",
+ connid, opid,
+ escape_string(slapi_sdn_get_dn(r->repl_root),ebuf));
+ slapi_ch_free_string(&r->locking_purl);
+ r->repl_state_flags &= ~(REPLICA_IN_USE);
+ if (isInc)
+ r->repl_state_flags &= ~(REPLICA_INCREMENTAL_IN_PROGRESS);
+ else
+ r->repl_state_flags &= ~(REPLICA_TOTAL_IN_PROGRESS);
+ }
+ PR_Unlock(r->repl_lock);
+}
+
+/*
+ * Returns root of the replicated area
+ */
+PRBool
+replica_get_tombstone_reap_active(const Replica *r)
+{
+ PR_ASSERT(r);
+
+ return(r->tombstone_reap_active);
+}
+
+/*
+ * Returns root of the replicated area
+ */
+const Slapi_DN *
+replica_get_root(const Replica *r) /* ONREPL - should we return copy instead? */
+{
+ PR_ASSERT(r);
+
+ /* replica root never changes so we don't have to lock */
+ return(r->repl_root);
+}
+
+/*
+ * Returns normalized dn of the root of the replicated area
+ */
+const char *
+replica_get_name(const Replica *r) /* ONREPL - should we return copy instead? */
+{
+ PR_ASSERT(r);
+
+ /* replica name never changes so we don't have to lock */
+ return(r->repl_name);
+}
+
+/*
+ * Returns replicaid of this replica
+ */
+ReplicaId
+replica_get_rid (const Replica *r)
+{
+ ReplicaId rid;
+ PR_ASSERT(r);
+
+ PR_Lock(r->repl_lock);
+ rid = r->repl_rid;
+ PR_Unlock(r->repl_lock);
+ return rid;
+}
+
+/*
+ * Sets replicaid of this replica - should only be used when also changing the type
+ */
+void
+replica_set_rid (Replica *r, ReplicaId rid)
+{
+ PR_ASSERT(r);
+
+ PR_Lock(r->repl_lock);
+ r->repl_rid = rid;
+ PR_Unlock(r->repl_lock);
+}
+
+/* Returns true if replica was initialized through ORC or import;
+ * otherwise, false. An uninitialized replica should return
+ * LDAP_UNWILLING_TO_PERFORM to all client requests
+ */
+PRBool
+replica_is_initialized (const Replica *r)
+{
+ PR_ASSERT(r);
+ return (r->repl_ruv != NULL);
+}
+
+/*
+ * Returns refcounted object that contains RUV. The caller should release the
+ * object once it is no longer used. To release, call object_release
+ */
+Object *
+replica_get_ruv (const Replica *r)
+{
+ Object *ruv = NULL;
+
+ PR_ASSERT(r);
+
+ PR_Lock(r->repl_lock);
+
+ PR_ASSERT (r->repl_ruv);
+
+ object_acquire (r->repl_ruv);
+
+ ruv = r->repl_ruv;
+
+ PR_Unlock(r->repl_lock);
+
+ return ruv;
+}
+
+/*
+ * Sets RUV vector. This function should be called during replica
+ * (re)initialization. During normal operation, the RUV is read from
+ * the root of the replicated in the replica_new call
+ */
+void
+replica_set_ruv (Replica *r, RUV *ruv)
+{
+ PR_ASSERT(r && ruv);
+
+ PR_Lock(r->repl_lock);
+
+ if(NULL != r->repl_ruv)
+ {
+ object_release(r->repl_ruv);
+ }
+
+ /* if the local replica is not in the RUV and it is writable - add it
+ and reinitialize min_csn pending list */
+ if (r->repl_type == REPLICA_TYPE_UPDATABLE)
+ {
+ CSN *csn = NULL;
+ if (r->min_csn_pl)
+ csnplFree (&r->min_csn_pl);
+
+ if (ruv_contains_replica (ruv, r->repl_rid))
+ {
+ ruv_get_smallest_csn_for_replica(ruv, r->repl_rid, &csn);
+ if (csn)
+ csn_free (&csn);
+ else
+ r->min_csn_pl = csnplNew ();
+ /* We need to make sure the local ruv element is the 1st. */
+ ruv_move_local_supplier_to_first(ruv, r->repl_rid);
+ }
+ else
+ {
+ r->min_csn_pl = csnplNew ();
+ /* To be sure that the local is in first */
+ ruv_add_index_replica(ruv, r->repl_rid, multimaster_get_local_purl(), 1);
+ }
+ }
+
+ r->repl_ruv = object_new((void*)ruv, (FNFree)ruv_destroy);
+ r->repl_ruv_dirty = PR_TRUE;
+
+ PR_Unlock(r->repl_lock);
+}
+
+/*
+ * Update one particular CSN in an RUV. This is meant to be called
+ * whenever (a) the server has processed a client operation and
+ * needs to update its CSN, or (b) the server is completing an
+ * inbound replication session operation, and needs to update its
+ * local RUV.
+ */
+void
+replica_update_ruv(Replica *r, const CSN *updated_csn, const char *replica_purl)
+{
+ char csn_str[CSN_STRSIZE];
+ char ebuf[BUFSIZ];
+
+ PR_ASSERT(NULL != r);
+ PR_ASSERT(NULL != updated_csn);
+#ifdef DEBUG
+ slapi_log_error(SLAPI_LOG_REPL, repl_plugin_name,
+ "replica_update_ruv: csn %s\n",
+ csn_as_string(updated_csn, PR_FALSE, csn_str)); /* XXXggood remove debugging */
+#endif
+ if (NULL == r)
+ {
+ slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name, "replica_update_ruv: replica "
+ "is NULL\n");
+ }
+ else if (NULL == updated_csn)
+ {
+ slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name, "replica_update_ruv: csn "
+ "is NULL when updating replica %s\n", escape_string(slapi_sdn_get_dn(r->repl_root),ebuf));
+ }
+ else
+ {
+ RUV *ruv;
+ PR_Lock(r->repl_lock);
+
+ if (r->repl_ruv != NULL)
+ {
+ ruv = object_get_data(r->repl_ruv);
+ if (NULL != ruv)
+ {
+ ReplicaId rid = csn_get_replicaid(updated_csn);
+ if (rid == r->repl_rid)
+ {
+ if (NULL != r->min_csn_pl)
+ {
+ CSN *min_csn;
+ PRBool committed;
+ (void)csnplCommit(r->min_csn_pl, updated_csn);
+ min_csn = csnplGetMinCSN(r->min_csn_pl, &committed);
+ if (NULL != min_csn)
+ {
+ if (committed)
+ {
+ ruv_set_min_csn(ruv, min_csn, replica_purl);
+ csnplFree(&r->min_csn_pl);
+ }
+ csn_free(&min_csn);
+ }
+ }
+ }
+ /* Update max csn for local and remote replicas */
+ if (ruv_update_ruv (ruv, updated_csn, replica_purl, rid == r->repl_rid)
+ != RUV_SUCCESS)
+ {
+ slapi_log_error(SLAPI_LOG_FATAL,
+ repl_plugin_name, "replica_update_ruv: unable "
+ "to update RUV for replica %s, csn = %s\n",
+ escape_string(slapi_sdn_get_dn(r->repl_root),ebuf),
+ csn_as_string(updated_csn, PR_FALSE, csn_str));
+ }
+
+ r->repl_ruv_dirty = PR_TRUE;
+ }
+ else
+ {
+ slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name,
+ "replica_update_ruv: unable to get RUV object for replica "
+ "%s\n", escape_string(slapi_sdn_get_dn(r->repl_root),ebuf));
+ }
+ }
+ else
+ {
+ slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name, "replica_update_ruv: "
+ "unable to initialize RUV for replica %s\n",
+ escape_string(slapi_sdn_get_dn(r->repl_root),ebuf));
+ }
+ PR_Unlock(r->repl_lock);
+ }
+}
+
+/*
+ * Returns refcounted object that contains csn generator. The caller should release the
+ * object once it is no longer used. To release, call object_release
+ */
+Object *
+replica_get_csngen (const Replica *r)
+{
+ Object *csngen;
+
+ PR_ASSERT(r);
+
+ PR_Lock(r->repl_lock);
+
+ object_acquire (r->repl_csngen);
+ csngen = r->repl_csngen;
+
+ PR_Unlock(r->repl_lock);
+
+ return csngen;
+}
+
+/*
+ * Returns the replica type.
+ */
+ReplicaType
+replica_get_type (const Replica *r)
+{
+ PR_ASSERT(r);
+ return r->repl_type;
+}
+
+/*
+ * Sets the replica type.
+ */
+void
+replica_set_type (Replica *r, ReplicaType type)
+{
+ PR_ASSERT(r);
+
+ PR_Lock(r->repl_lock);
+ r->repl_type = type;
+ PR_Unlock(r->repl_lock);
+}
+
+/*
+ * Returns PR_TRUE if this replica is a consumer of 4.0 server
+ * and PR_FALSE otherwise
+ */
+PRBool
+replica_is_legacy_consumer (const Replica *r)
+{
+ PR_ASSERT(r);
+ return r->legacy_consumer;
+}
+
+/*
+ * Sets the replica type.
+ */
+void
+replica_set_legacy_consumer (Replica *r, PRBool legacy_consumer)
+{
+ int legacy2mmr;
+ Slapi_DN *repl_root_sdn = NULL;
+ char **referrals = NULL;
+ char *replstate = NULL;
+ PR_ASSERT(r);
+
+ PR_Lock(r->repl_lock);
+
+ legacy2mmr = r->legacy_consumer && !legacy_consumer;
+
+ /* making the server a regular 5.0 replica */
+ if (legacy2mmr)
+ {
+ slapi_ch_free ((void**)&r->legacy_purl);
+ /* Remove copiedFrom/copyingFrom attributes from the root entry */
+ /* set the right state in the mapping tree */
+ if (r->repl_type == REPLICA_TYPE_READONLY)
+ {
+ replica_get_referrals_nolock (r, &referrals);
+ replstate = STATE_UPDATE_REFERRAL;
+ }
+ else /* updateable */
+ {
+ replstate = STATE_BACKEND;
+ }
+ }
+
+ r->legacy_consumer = legacy_consumer;
+ repl_root_sdn = slapi_sdn_dup(r->repl_root);
+ PR_Unlock(r->repl_lock);
+
+ if (legacy2mmr)
+ {
+ replica_clear_legacy_referrals(repl_root_sdn, referrals, replstate);
+ /* Also change state of the mapping tree node and/or referrals */
+ replica_remove_legacy_attr (repl_root_sdn, type_copiedFrom);
+ replica_remove_legacy_attr (repl_root_sdn, type_copyingFrom);
+ }
+ charray_free(referrals);
+ slapi_sdn_free(&repl_root_sdn);
+}
+
+/* Gets partial url of the legacy supplier - applicable for legacy consumer only */
+char *
+replica_get_legacy_purl (const Replica *r)
+{
+ char *purl;
+
+ PR_Lock (r->repl_lock);
+
+ PR_ASSERT (r->legacy_consumer);
+
+ purl = slapi_ch_strdup (r->legacy_purl);
+
+ PR_Unlock (r->repl_lock);
+
+ return purl;
+}
+
+void
+replica_set_legacy_purl (Replica *r, const char *purl)
+{
+ PR_Lock (r->repl_lock);
+
+ PR_ASSERT (r->legacy_consumer);
+
+ /* slapi_ch_free accepts NULL pointer */
+ slapi_ch_free ((void**)&r->legacy_purl);
+
+ r->legacy_purl = slapi_ch_strdup (purl);
+
+ PR_Unlock (r->repl_lock);
+}
+
+/*
+ * Returns true if sdn is the same as updatedn and false otherwise
+ */
+PRBool
+replica_is_updatedn (const Replica *r, const Slapi_DN *sdn)
+{
+ PRBool result;
+
+ PR_ASSERT (r);
+
+ PR_Lock(r->repl_lock);
+
+ if (sdn == NULL)
+ {
+ result = (r->updatedn_list == NULL);
+ }
+ else if (r->updatedn_list == NULL)
+ {
+ result = PR_FALSE;
+ }
+ else
+ {
+ result = replica_updatedn_list_ismember(r->updatedn_list, sdn);
+ }
+
+ PR_Unlock(r->repl_lock);
+
+ return result;
+}
+
+/*
+ * Sets updatedn list for this replica
+ */
+void
+replica_set_updatedn (Replica *r, const Slapi_ValueSet *vs, int mod_op)
+{
+ PR_ASSERT (r);
+
+ PR_Lock(r->repl_lock);
+
+ if (!r->updatedn_list)
+ r->updatedn_list = replica_updatedn_list_new(NULL);
+
+ if (mod_op & LDAP_MOD_DELETE || vs == NULL ||
+ (0 == slapi_valueset_count(vs))) /* null value also causes list deletion */
+ replica_updatedn_list_delete(r->updatedn_list, vs);
+ else if (mod_op & LDAP_MOD_REPLACE)
+ replica_updatedn_list_replace(r->updatedn_list, vs);
+ else if (mod_op & LDAP_MOD_ADD)
+ replica_updatedn_list_add(r->updatedn_list, vs);
+
+ PR_Unlock(r->repl_lock);
+}
+
+/* gets current replica generation for this replica */
+char *replica_get_generation (const Replica *r)
+{
+ int rc = 0;
+ char *gen = NULL;
+
+ if (r)
+ {
+ PR_Lock(r->repl_lock);
+
+ PR_ASSERT (r->repl_ruv);
+
+ if (rc == 0)
+ gen = ruv_get_replica_generation ((RUV*)object_get_data (r->repl_ruv));
+
+ PR_Unlock(r->repl_lock);
+ }
+
+ return gen;
+}
+
+PRBool replica_is_flag_set (const Replica *r, PRUint32 flag)
+{
+ if (r)
+ return (r->repl_flags & flag);
+ else
+ return PR_FALSE;
+}
+
+void replica_set_flag (Replica *r, PRUint32 flag, PRBool clear)
+{
+ if (r == NULL)
+ return;
+
+ PR_Lock(r->repl_lock);
+
+ if (clear)
+ {
+ r->repl_flags &= ~flag;
+ }
+ else
+ {
+ r->repl_flags |= flag;
+ }
+
+ PR_Unlock(r->repl_lock);
+}
+
+void replica_replace_flags (Replica *r, PRUint32 flags)
+{
+ if (r)
+ {
+ PR_Lock(r->repl_lock);
+ r->repl_flags = flags;
+ PR_Unlock(r->repl_lock);
+ }
+}
+
+void
+replica_get_referrals(const Replica *r, char ***referrals)
+{
+ PR_Lock(r->repl_lock);
+ replica_get_referrals_nolock (r, referrals);
+ PR_Unlock(r->repl_lock);
+}
+
+void
+replica_set_referrals(Replica *r,const Slapi_ValueSet *vs)
+{
+ int ii = 0;
+ Slapi_Value *vv = NULL;
+ if (r->repl_referral == NULL)
+ {
+ r->repl_referral = slapi_valueset_new();
+ }
+ else
+ {
+ slapi_valueset_done(r->repl_referral);
+ }
+ slapi_valueset_set_valueset(r->repl_referral, vs);
+ /* make sure the DN is included in the referral LDAP URL */
+ if (r->repl_referral)
+ {
+ Slapi_ValueSet *newvs = slapi_valueset_new();
+ const char *repl_root = slapi_sdn_get_dn(r->repl_root);
+ int rootlen = strlen(repl_root);
+ ii = slapi_valueset_first_value(r->repl_referral, &vv);
+ while (vv)
+ {
+ const char *ref = slapi_value_get_string(vv);
+ struct ldap_url_desc *lud = NULL;
+ int myrc = ldap_url_parse(ref, &lud);
+ /* see if the dn is already in the referral URL */
+ if (myrc == LDAP_URL_ERR_NODN || !lud || !lud->lud_dn) {
+ /* add the dn */
+ Slapi_Value *newval = NULL;
+ int len = strlen(ref);
+ char *tmpref = NULL;
+ int need_slash = 0;
+ if (ref[len-1] != '/') {
+ len++; /* add another one for the slash */
+ need_slash = 1;
+ }
+ len += rootlen + 2;
+ tmpref = slapi_ch_malloc(len);
+ sprintf(tmpref, "%s%s%s", ref, (need_slash ? "/" : ""),
+ repl_root);
+ newval = slapi_value_new_string(tmpref);
+ slapi_ch_free_string(&tmpref); /* sv_new_string makes a copy */
+ slapi_valueset_add_value(newvs, newval);
+ slapi_value_free(&newval); /* s_vs_add_value makes a copy */
+ }
+ if (lud)
+ ldap_free_urldesc(lud);
+ ii = slapi_valueset_next_value(r->repl_referral, ii, &vv);
+ }
+ if (slapi_valueset_count(newvs) > 0) {
+ slapi_valueset_done(r->repl_referral);
+ slapi_valueset_set_valueset(r->repl_referral, newvs);
+ }
+ slapi_valueset_free(newvs); /* s_vs_set_vs makes a copy */
+ }
+}
+
+int
+replica_update_csngen_state (Replica *r, const RUV *ruv)
+{
+ int rc = 0;
+ CSNGen *gen;
+ CSN *csn = NULL;
+
+ PR_ASSERT (r && ruv);
+
+ rc = ruv_get_max_csn(ruv, &csn);
+ if (rc != RUV_SUCCESS)
+ {
+ return -1;
+ }
+
+ if (csn == NULL) /* ruv contains no csn - we are done */
+ {
+ return 0;
+ }
+
+ PR_Lock(r->repl_lock);
+
+ gen = (CSNGen *)object_get_data (r->repl_csngen);
+ PR_ASSERT (gen);
+
+ rc = csngen_adjust_time (gen, csn);
+ if (rc != CSN_SUCCESS)
+ {
+ rc = -1;
+ goto done;
+ }
+
+ rc = 0;
+
+done:
+
+ PR_Unlock(r->repl_lock);
+ if (csn)
+ csn_free (&csn);
+
+ return rc;
+}
+
+/*
+ * dumps replica state for debugging purpose
+ */
+void
+replica_dump(Replica *r)
+{
+ char *updatedn_list = NULL;
+ PR_ASSERT (r);
+
+ PR_Lock(r->repl_lock);
+
+ slapi_log_error(SLAPI_LOG_REPL, repl_plugin_name, "Replica state:\n");
+ slapi_log_error(SLAPI_LOG_REPL, repl_plugin_name, "\treplica root: %s\n",
+ slapi_sdn_get_ndn (r->repl_root));
+ slapi_log_error(SLAPI_LOG_REPL, repl_plugin_name, "\treplica type: %s\n",
+ _replica_type_as_string (r));
+ slapi_log_error(SLAPI_LOG_REPL, repl_plugin_name, "\treplica id: %d\n", r->repl_rid);
+ slapi_log_error(SLAPI_LOG_REPL, repl_plugin_name, "\tflags: %d\n", r->repl_flags);
+ slapi_log_error(SLAPI_LOG_REPL, repl_plugin_name, "\tstate flags: %d\n", r->repl_state_flags);
+ if (r->updatedn_list)
+ updatedn_list = replica_updatedn_list_to_string(r->updatedn_list, "\n\t\t");
+ slapi_log_error(SLAPI_LOG_REPL, repl_plugin_name, "\tupdate dn: %s\n",
+ updatedn_list? updatedn_list : "not configured");
+ slapi_ch_free_string(&updatedn_list);
+ slapi_log_error(SLAPI_LOG_REPL, repl_plugin_name, "\truv: %s configured and is %sdirty\n",
+ r->repl_ruv ? "" : "not", r->repl_ruv_dirty ? "" : "not ");
+ slapi_log_error(SLAPI_LOG_REPL, repl_plugin_name, "\tCSN generator: %s configured\n",
+ r->repl_csngen ? "" : "not");
+ /* JCMREPL - Dump Referrals */
+
+ PR_Unlock(r->repl_lock);
+}
+
+
+/*
+ * Return the CSN of the purge point. Any CSNs smaller than the
+ * purge point can be safely removed from entries within this
+ * this replica. Returns an allocated CSN that must be freed by
+ * the caller, or NULL if purging is disabled.
+ */
+
+CSN *
+replica_get_purge_csn(const Replica *r)
+{
+ CSN *csn;
+
+ PR_Lock(r->repl_lock);
+
+ csn= _replica_get_purge_csn_nolock(r);
+
+ PR_Unlock(r->repl_lock);
+
+ return csn;
+}
+
+
+/*
+ * This function logs a dummy entry for the smallest csn in the RUV.
+ * This is necessary because, to get the next change, we need to position
+ * changelog on the previous change. So this function insures that we always have one.
+ */
+
+/* ONREPL we will need to change this function to log all the
+ * ruv elements not just the smallest when changelog iteration
+ * algoritm changes to iterate replica by replica
+*/
+int
+replica_log_ruv_elements (const Replica *r)
+{
+ int rc = 0;
+
+ PR_ASSERT (r);
+
+ PR_Lock(r->repl_lock);
+
+ rc = replica_log_ruv_elements_nolock (r);
+
+ PR_Unlock(r->repl_lock);
+
+ return rc;
+}
+
+void
+consumer5_set_mapping_tree_state_for_replica(const Replica *r, RUV *supplierRuv)
+{
+ const Slapi_DN *repl_root_sdn= replica_get_root(r);
+ char **ruv_referrals= NULL;
+ char **replica_referrals= NULL;
+ RUV *ruv;
+ int state_backend = -1;
+ const char *mtn_state = NULL;
+
+ PR_Lock (r->repl_lock);
+
+ if ( supplierRuv == NULL )
+ {
+ ruv = (RUV*)object_get_data (r->repl_ruv);
+ PR_ASSERT (ruv);
+
+ ruv_referrals= ruv_get_referrals(ruv); /* ruv_referrals has to be free'd */
+ }
+ else
+ {
+ ruv_referrals = ruv_get_referrals(supplierRuv);
+ }
+
+ replica_get_referrals_nolock (r, &replica_referrals); /* replica_referrals has to be free'd */
+
+ /* JCMREPL - What if there's a Total update in progress? */
+ if( (r->repl_type==REPLICA_TYPE_READONLY) || (r->legacy_consumer) )
+ {
+ state_backend = 0;
+ }
+ else if (r->repl_type==REPLICA_TYPE_UPDATABLE)
+ {
+ state_backend = 1;
+ }
+ /* Unlock to avoid changing MTN state under repl lock */
+ PR_Unlock (r->repl_lock);
+
+ if(state_backend == 0 )
+ {
+ /* Read-Only - The mapping tree should be refering all update operations. */
+ mtn_state = STATE_UPDATE_REFERRAL;
+ }
+ else if (state_backend == 1)
+ {
+ /* Updatable - The mapping tree should be accepting all update operations. */
+ mtn_state = STATE_BACKEND;
+ }
+
+ /* JCMREPL - Check the return code. */
+ repl_set_mtn_state_and_referrals(repl_root_sdn, mtn_state, NULL,
+ ruv_referrals, replica_referrals);
+ charray_free(ruv_referrals);
+ charray_free(replica_referrals);
+}
+
+void
+replica_set_enabled (Replica *r, PRBool enable)
+{
+ char *repl_name = NULL;
+
+ PR_ASSERT (r);
+
+ PR_Lock (r->repl_lock);
+
+ if (enable)
+ {
+ if (r->repl_eqcxt_rs == NULL) /* event is not already registered */
+ {
+ repl_name = slapi_ch_strdup (r->repl_name);
+ r->repl_eqcxt_rs = slapi_eq_repeat(_replica_update_state, repl_name,
+ current_time() + START_UPDATE_DELAY, RUV_SAVE_INTERVAL);
+ }
+ }
+ else /* disable */
+ {
+ if (r->repl_eqcxt_rs) /* event is still registerd */
+ {
+ repl_name = slapi_eq_get_arg (r->repl_eqcxt_rs);
+ slapi_ch_free ((void**)&repl_name);
+ slapi_eq_cancel(r->repl_eqcxt_rs);
+ r->repl_eqcxt_rs = NULL;
+ }
+ }
+
+ PR_Unlock (r->repl_lock);
+}
+
+/* This function is generally called when replica's data store
+ is reloaded. It retrieves new RUV from the datastore. If new
+ RUV does not exist or if it is not as up to date as the purge RUV
+ of the corresponding changelog file, we need to remove */
+
+/* the function minimizes the use of replica lock where ever possible.
+ Locking replica lock while calling changelog functions
+ causes a deadlock because changelog calls replica functions that
+ that lock the same lock */
+
+int
+replica_reload_ruv (Replica *r)
+{
+ int rc = 0;
+ Object *old_ruv_obj = NULL, *new_ruv_obj = NULL;
+ RUV *upper_bound_ruv = NULL;
+ RUV *new_ruv = NULL;
+ Object *r_obj;
+
+ PR_ASSERT (r);
+
+ PR_Lock (r->repl_lock);
+
+ old_ruv_obj = r->repl_ruv;
+
+ r->repl_ruv = NULL;
+
+ rc = _replica_configure_ruv (r, PR_TRUE);
+
+ PR_Unlock (r->repl_lock);
+
+ if (rc != 0)
+ {
+ return rc;
+ }
+
+ /* check if there is a changelog and whether this replica logs changes */
+ if (cl5GetState () == CL5_STATE_OPEN && r->repl_flags & REPLICA_LOG_CHANGES)
+ {
+
+ /* Compare new ruv to the changelog's upper bound ruv. We could only keep
+ the existing changelog if its upper bound is the same as replica's RUV.
+ This is because if changelog has changes not in RUV, they will be
+ eventually sent to the consumer's which will cause a state mismatch
+ (because the supplier does not actually contain the changes in its data store.
+ If, on the other hand, the changelog is not as up to date as the supplier,
+ it is not really useful since out of sync consumer's can't be brought
+ up to date using this changelog and hence will need to be reinitialized */
+
+ /* replace ruv to make sure we work with the correct changelog file */
+ PR_Lock (r->repl_lock);
+
+ new_ruv_obj = r->repl_ruv;
+ r->repl_ruv = old_ruv_obj;
+
+ PR_Unlock (r->repl_lock);
+
+ rc = cl5GetUpperBoundRUV (r, &upper_bound_ruv);
+ if (rc != CL5_SUCCESS && rc != CL5_NOTFOUND)
+ {
+ return -1;
+ }
+
+ if (upper_bound_ruv)
+ {
+ new_ruv = object_get_data (new_ruv_obj);
+ PR_ASSERT (new_ruv);
+
+ /* ONREPL - there are more efficient ways to establish RUV equality.
+ However, because this is not in the critical path and we at most
+ have 2 elements in the RUV, this will not effect performance */
+
+ if (!ruv_covers_ruv (new_ruv, upper_bound_ruv) ||
+ !ruv_covers_ruv (upper_bound_ruv, new_ruv))
+ {
+ char ebuf[BUFSIZ];
+
+ /* create a temporary replica object to conform to the interface */
+ r_obj = object_new (r, NULL);
+
+ /* We can't use existing changelog - remove existing file */
+ slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name, "replica_reload_ruv: "
+ "Warning: new data for replica %s does not match the data in the changelog.\n"
+ " Recreating the changelog file. This could affect replication with replica's "
+ " consumers in which case the consumers should be reinitialized.\n",
+ escape_string(slapi_sdn_get_dn(r->repl_root),ebuf));
+ rc = cl5DeleteDBSync (r_obj);
+
+ /* reinstate new ruv */
+ PR_Lock (r->repl_lock);
+
+ r->repl_ruv = new_ruv_obj;
+
+ object_release (r_obj);
+
+ if (rc == CL5_SUCCESS)
+ {
+ /* log changes to mark starting point for replication */
+ rc = replica_log_ruv_elements_nolock (r);
+ }
+
+ PR_Unlock (r->repl_lock);
+ }
+ else
+ {
+ /* we just need to reinstate new ruv */
+ PR_Lock (r->repl_lock);
+
+ r->repl_ruv = new_ruv_obj;
+
+ PR_Unlock (r->repl_lock);
+ }
+ }
+ else /* upper bound vector is not there - we have no changes logged */
+ {
+ /* reinstate new ruv */
+ PR_Lock (r->repl_lock);
+
+ r->repl_ruv = new_ruv_obj;
+
+ /* just log elements of the current RUV. This is to have
+ a starting point for iteration through the changes */
+ rc = replica_log_ruv_elements_nolock (r);
+
+ PR_Unlock (r->repl_lock);
+ }
+ }
+
+ if (rc == 0)
+ {
+ consumer5_set_mapping_tree_state_for_replica(r, NULL);
+ /* reset mapping tree referrals based on new local RUV */
+ }
+
+ if (old_ruv_obj)
+ object_release (old_ruv_obj);
+
+ if (upper_bound_ruv)
+ ruv_destroy (&upper_bound_ruv);
+
+ return rc;
+}
+
+/* this function is called during server startup for each replica
+ to check whether the replica's data was reloaded offline and
+ whether replica's changelog needs to be reinitialized */
+
+/* the function does not use replica lock but all functions it calls are
+ thread safe. Locking replica lock while calling changelog functions
+ causes a deadlock because changelog calls replica functions that
+ that lock the same lock */
+int replica_check_for_data_reload (Replica *r, void *arg)
+{
+ int rc = 0;
+ RUV *upper_bound_ruv = NULL;
+ RUV *r_ruv = NULL;
+ Object *r_obj, *ruv_obj;
+ int cl_cover_be, be_cover_cl;
+
+ PR_ASSERT (r);
+
+ /* check that we have a changelog and if this replica logs changes */
+ if (cl5GetState () == CL5_STATE_OPEN && r->repl_flags & REPLICA_LOG_CHANGES)
+ {
+ /* Compare new ruv to the purge ruv. If the new contains csns which
+ are smaller than those in purge ruv, we need to remove old and
+ create new changelog file for this replica. This is because we
+ will not have sufficient changes to incrementally update a consumer
+ to the current state of the supplier. */
+
+ rc = cl5GetUpperBoundRUV (r, &upper_bound_ruv);
+ if (rc != CL5_SUCCESS && rc != CL5_NOTFOUND)
+ {
+ return -1;
+ }
+
+ if (upper_bound_ruv)
+ {
+ ruv_obj = replica_get_ruv (r);
+ r_ruv = object_get_data (ruv_obj);
+ PR_ASSERT (r_ruv);
+
+ /* Compare new ruv to the changelog's upper bound ruv. We could only keep
+ the existing changelog if its upper bound is the same as replica's RUV.
+ This is because if changelog has changes not in RUV, they will be
+ eventually sent to the consumer's which will cause a state mismatch
+ (because the supplier does not actually contain the changes in its data store.
+ If, on the other hand, the changelog is not as up to date as the supplier,
+ it is not really useful since out of sync consumer's can't be brought
+ up to date using this changelog and hence will need to be reinitialized */
+
+ /*
+ * Actually we can ignore the scenario that the changelog's upper
+ * bound ruv covers data store's ruv for two reasons: (1) a change
+ * is always written to the changelog after it is committed to the
+ * data store; (2) a change will be ignored if the server has seen
+ * it before - this happens frequently at the beginning of replication
+ * sessions.
+ */
+
+ be_cover_cl = ruv_covers_ruv (r_ruv, upper_bound_ruv);
+ cl_cover_be = ruv_covers_ruv (upper_bound_ruv, r_ruv);
+ if (!cl_cover_be)
+ {
+ /* the data was reloaded and we can no longer use existing changelog */
+ char ebuf[BUFSIZ];
+
+ /* create a temporary replica object to conform to the interface */
+ r_obj = object_new (r, NULL);
+
+ /* We can't use existing changelog - remove existing file */
+ slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name, "replica_check_for_data_reload: "
+ "Warning: data for replica %s was reloaded and it no longer matches the data "
+ "in the changelog (replica data %s changelog). Recreating the changelog file. This could affect replication "
+ "with replica's consumers in which case the consumers should be reinitialized.\n",
+ escape_string(slapi_sdn_get_dn(r->repl_root),ebuf),
+ ((!be_cover_cl && !cl_cover_be) ? "<>" : (!be_cover_cl ? "<" : ">")) );
+
+ rc = cl5DeleteDBSync (r_obj);
+
+ object_release (r_obj);
+
+ if (rc == CL5_SUCCESS)
+ {
+ /* log changes to mark starting point for replication */
+ rc = replica_log_ruv_elements (r);
+ }
+ }
+
+ object_release (ruv_obj);
+ }
+ else /* we have no changes currently logged for this replica */
+ {
+ /* log changes to mark starting point for replication */
+ rc = replica_log_ruv_elements (r);
+ }
+ }
+
+ if (rc == 0)
+ {
+ /* reset mapping tree referrals based on new local RUV */
+ consumer5_set_mapping_tree_state_for_replica(r, NULL);
+ }
+
+ if (upper_bound_ruv)
+ ruv_destroy (&upper_bound_ruv);
+
+ return rc;
+}
+
+/* Helper functions */
+/* reads replica configuration entry. The entry is the child of the
+ mapping tree node for the replica's backend */
+
+static Slapi_Entry*
+_replica_get_config_entry (const Slapi_DN *root)
+{
+ int rc = 0;
+ char *dn = NULL;
+ Slapi_Entry **entries;
+ Slapi_Entry *e = NULL;
+ Slapi_PBlock *pb = NULL;
+
+ dn = _replica_get_config_dn (root);
+ pb = slapi_pblock_new ();
+
+ slapi_search_internal_set_pb (pb, dn, LDAP_SCOPE_BASE, "objectclass=*", NULL, 0, NULL,
+ NULL, repl_get_plugin_identity (PLUGIN_MULTIMASTER_REPLICATION), 0);
+ slapi_search_internal_pb (pb);
+ slapi_pblock_get(pb, SLAPI_PLUGIN_INTOP_RESULT, &rc);
+ if (rc == 0)
+ {
+ slapi_pblock_get(pb, SLAPI_PLUGIN_INTOP_SEARCH_ENTRIES, &entries);
+ e = slapi_entry_dup (entries [0]);
+ }
+
+ slapi_free_search_results_internal(pb);
+ slapi_pblock_destroy (pb);
+ slapi_ch_free_string(&dn);
+
+ return e;
+}
+
+static int
+_replica_check_validity (const Replica *r)
+{
+ PR_ASSERT (r);
+
+ if (r->repl_root == NULL || r->repl_type == 0 || r->repl_rid == 0 ||
+ r->repl_rid > MAX_REPLICA_ID || r->repl_csngen == NULL || r->repl_name == NULL)
+ {
+ return -1;
+ }
+ else
+ {
+ return 0;
+ }
+}
+
+/* replica configuration entry has the following format:
+ dn: cn=replica,<mapping tree node dn>
+ objectclass: top
+ objectclass: nsds5Replica
+ objectclass: extensibleObject
+ nsds5ReplicaRoot: <root of the replica>
+ nsds5ReplicaId: <replica id>
+ nsds5ReplicaType: <type of the replica: primary, read-write or read-only>
+ nsState: <state of the csn generator> missing the first time replica is started
+ nsds5ReplicaBindDN: <supplier update dn> consumers only
+ nsds5ReplicaReferral: <referral URL to updatable replica> consumers only
+ nsds5ReplicaPurgeDelay: <time, in seconds, to keep purgeable CSNs, 0 == keep forever>
+ nsds5ReplicaTombstonePurgeInterval: <time, in seconds, between tombstone purge runs, 0 == don't reap>
+ nsds5ReplicaLegacyConsumer: <TRUE | FALSE>
+
+ richm: changed slapi entry from const to editable - if the replica id is supplied for a read
+ only replica, we ignore it and replace the value with the READ_ONLY_REPLICA_ID
+ */
+static int
+_replica_init_from_config (Replica *r, Slapi_Entry *e, char *errortext)
+{
+ int rc;
+ Slapi_Attr *attr;
+ char *val;
+ CSNGen *gen;
+ char buf [BUFSIZ];
+ char *errormsg = errortext? errortext : buf;
+ Slapi_Attr *a = NULL;
+ char dnescape[BUFSIZ]; /* for escape_string */
+
+ PR_ASSERT (r && e);
+
+ /* get replica root */
+ val = slapi_entry_attr_get_charptr (e, attr_replicaRoot);
+ if (val == NULL)
+ {
+ sprintf (errormsg, "failed to retrieve %s attribute from (%s)\n",
+ attr_replicaRoot,
+ escape_string((char*)slapi_entry_get_dn ((Slapi_Entry*)e), dnescape));
+ slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name, "_replica_init_from_config: %s\n",
+ errormsg);
+
+ return -1;
+ }
+
+ r->repl_root = slapi_sdn_new_dn_passin (val);
+
+ /* get replica type */
+ val = slapi_entry_attr_get_charptr (e, attr_replicaType);
+ if (val)
+ {
+ r->repl_type = atoi(val);
+ slapi_ch_free ((void**)&val);
+ }
+ else
+ {
+ r->repl_type = REPLICA_TYPE_READONLY;
+ }
+
+ /* get legacy consumer flag */
+ val = slapi_entry_attr_get_charptr (e, type_replicaLegacyConsumer);
+ if (val)
+ {
+ if (strcasecmp (val, "on") == 0 || strcasecmp (val, "yes") == 0 ||
+ strcasecmp (val, "true") == 0 || strcasecmp (val, "1") == 0)
+ {
+ r->legacy_consumer = PR_TRUE;
+ }
+ else
+ {
+ r->legacy_consumer = PR_FALSE;
+ }
+
+ slapi_ch_free ((void**)&val);
+ }
+ else
+ {
+ r->legacy_consumer = PR_FALSE;
+ }
+
+ /* get replica flags */
+ r->repl_flags = slapi_entry_attr_get_ulong(e, attr_flags);
+
+ /* get replicaid */
+ /* the replica id is ignored for read only replicas and is set to the
+ special value READ_ONLY_REPLICA_ID */
+ if (r->repl_type == REPLICA_TYPE_READONLY)
+ {
+ r->repl_rid = READ_ONLY_REPLICA_ID;
+ slapi_entry_attr_set_uint(e, attr_replicaId, (unsigned int)READ_ONLY_REPLICA_ID);
+ }
+ /* a replica id is required for updatable and primary replicas */
+ else if (r->repl_type == REPLICA_TYPE_UPDATABLE ||
+ r->repl_type == REPLICA_TYPE_PRIMARY)
+ {
+ if ((val = slapi_entry_attr_get_charptr (e, attr_replicaId)))
+ {
+ int temprid = atoi (val);
+ slapi_ch_free ((void**)&val);
+ if (temprid <= 0 || temprid >= READ_ONLY_REPLICA_ID)
+ {
+ sprintf (errormsg,
+ "attribute %s must have a value greater than 0 "
+ "and less than %d: entry %s",
+ attr_replicaId, READ_ONLY_REPLICA_ID,
+ escape_string((char*)slapi_entry_get_dn ((Slapi_Entry*)e),
+ dnescape));
+ slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name,
+ "_replica_init_from_config: %s\n",
+ errormsg);
+ return -1;
+ }
+ else
+ {
+ r->repl_rid = (ReplicaId)temprid;
+ }
+ }
+ else
+ {
+ sprintf (errormsg, "failed to retrieve required %s attribute from %s",
+ attr_replicaId,
+ escape_string((char*)slapi_entry_get_dn ((Slapi_Entry*)e),
+ dnescape));
+ slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name,
+ "_replica_init_from_config: %s\n",
+ errormsg);
+ return -1;
+ }
+ }
+
+ attr = NULL;
+ rc = slapi_entry_attr_find(e, attr_state, &attr);
+ gen = csngen_new (r->repl_rid, attr);
+ if (gen == NULL)
+ {
+ sprintf (errormsg, "failed to create csn generator for replica (%s)",
+ escape_string((char*)slapi_entry_get_dn ((Slapi_Entry*)e),
+ dnescape));
+ slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name,
+ "_replica_init_from_config: %s\n",
+ errormsg);
+ return -1;
+ }
+ r->repl_csngen = object_new((void*)gen, (FNFree)csngen_free);
+
+ /* Hook generator so we can maintain min/max CSN info */
+ r->csn_pl_reg_id = csngen_register_callbacks(gen, assign_csn_callback, r, abort_csn_callback, r);
+
+ /* get replication bind dn */
+ r->updatedn_list = replica_updatedn_list_new(e);
+
+ /* get replica name */
+ val = slapi_entry_attr_get_charptr (e, attr_replicaName);
+ if (val) {
+ r->repl_name = val;
+ }
+ else
+ {
+ rc = slapi_uniqueIDGenerateString (&r->repl_name);
+ if (rc != UID_SUCCESS)
+ {
+ sprintf (errormsg, "failed to assign replica name for replica (%s); "
+ "uuid generator error - %d ",
+ escape_string((char*)slapi_entry_get_dn ((Slapi_Entry*)e), dnescape),
+ rc);
+ slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name, "_replica_init_from_config: %s\n",
+ errormsg);
+ return -1;
+ }
+ else
+ r->new_name = PR_TRUE;
+ }
+
+ /* get the list of referrals */
+ slapi_entry_attr_find( e, attr_replicaReferral, &attr );
+ if(attr!=NULL)
+ {
+ slapi_attr_get_valueset(attr, &r->repl_referral);
+ }
+
+ /*
+ * Set the purge offset (default 7 days). This is the extra
+ * time we allow purgeable CSNs to stick around, in case a
+ * replica regresses. Could also be useful when LCUP happens,
+ * since we don't know about LCUP replicas, and they can just
+ * turn up whenever they want to.
+ */
+ if (slapi_entry_attr_find(e, type_replicaPurgeDelay, &a) == -1)
+ {
+ /* No purge delay provided, so use default */
+ r->repl_purge_delay = 60 * 60 * 24 * 7; /* One week, in seconds */
+ }
+ else
+ {
+ r->repl_purge_delay = slapi_entry_attr_get_uint(e, type_replicaPurgeDelay);
+ }
+
+ if (slapi_entry_attr_find(e, type_replicaTombstonePurgeInterval, &a) == -1)
+ {
+ /* No reap interval provided, so use default */
+ r->tombstone_reap_interval = 3600 * 24; /* One day */
+ }
+ else
+ {
+ r->tombstone_reap_interval = slapi_entry_attr_get_int(e, type_replicaTombstonePurgeInterval);
+ }
+
+ r->tombstone_reap_stop = r->tombstone_reap_active = PR_FALSE;
+
+ return (_replica_check_validity (r));
+}
+
+/* This function updates the entry to contain information generated
+ during replica initialization.
+ Returns 0 if successful and -1 otherwise */
+static int
+_replica_update_entry (Replica *r, Slapi_Entry *e, char *errortext)
+{
+ int rc;
+ Slapi_Mod smod;
+ Slapi_Value *val;
+
+ PR_ASSERT (r);
+
+ /* add attribute that stores state of csn generator */
+ rc = csngen_get_state ((CSNGen*)object_get_data (r->repl_csngen), &smod);
+ if (rc != CSN_SUCCESS)
+ {
+ sprintf (errortext, "failed to get csn generator's state; csn error - %d", rc);
+ slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name,
+ "_replica_update_entry: %s\n", errortext);
+ return -1;
+ }
+
+ val = slapi_value_new_berval(slapi_mod_get_first_value(&smod));
+
+ rc = slapi_entry_add_value (e, slapi_mod_get_type (&smod), val);
+
+ slapi_value_free(&val);
+ slapi_mod_done (&smod);
+
+ if (rc != 0)
+ {
+ sprintf (errortext, "failed to update replica entry");
+ slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name,
+ "_replica_update_entry: %s\n", errortext);
+ return -1;
+ }
+
+ /* add attribute that stores replica name */
+ rc = slapi_entry_add_string (e, attr_replicaName, r->repl_name);
+ if (rc != 0)
+ {
+ sprintf (errortext, "failed to update replica entry");
+ slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name,
+ "_replica_update_entry: %s\n", errortext);
+ return -1;
+ }
+ else
+ r->new_name = PR_FALSE;
+
+ return 0;
+}
+
+/* DN format: cn=replica,cn=\"<root>\",cn=mapping tree,cn=config */
+static char*
+_replica_get_config_dn (const Slapi_DN *root)
+{
+ char *dn;
+ const char *mp_base = slapi_get_mapping_tree_config_root ();
+ int len;
+
+ PR_ASSERT (root);
+
+ len = strlen (REPLICA_RDN) + strlen (slapi_sdn_get_dn (root)) +
+ strlen (mp_base) + 8; /* 8 = , + cn= + \" + \" + , + \0 */
+
+ dn = (char*)slapi_ch_malloc (len);
+ sprintf (dn, "%s,cn=\"%s\",%s", REPLICA_RDN, slapi_sdn_get_dn (root), mp_base);
+
+ return dn;
+}
+
+/* This function retrieves RUV from the root of the replicated tree.
+ * The attribute can be missing if
+ * (1) this replica is the first supplier and replica generation has not been assigned
+ * or
+ * (2) this is a consumer that has not been yet initialized
+ * In either case, replica_set_ruv should be used to further initialize the replica.
+ * Returns 0 on success, -1 on failure. If 0 is returned, the RUV is present in the replica.
+ */
+static int
+_replica_configure_ruv (Replica *r, PRBool isLocked)
+{
+ Slapi_PBlock *pb = NULL;
+ char *attrs[2];
+ int rc;
+ int return_value = -1;
+ Slapi_Entry **entries = NULL;
+ Slapi_Attr *attr;
+ RUV *ruv = NULL;
+ CSN *csn = NULL;
+ ReplicaId rid = 0;
+ char ebuf[BUFSIZ];
+
+ /* read ruv state from the ruv tombstone entry */
+ pb = slapi_pblock_new();
+ attrs[0] = (char*)type_ruvElement;
+ attrs[1] = NULL;
+ slapi_search_internal_set_pb(
+ pb,
+ slapi_sdn_get_dn(r->repl_root),
+ LDAP_SCOPE_BASE,
+ "objectclass=*",
+ attrs,
+ 0, /* attrsonly */
+ NULL, /* controls */
+ RUV_STORAGE_ENTRY_UNIQUEID,
+ repl_get_plugin_identity (PLUGIN_MULTIMASTER_REPLICATION),
+ OP_FLAG_REPLICATED); /* flags */
+ slapi_search_internal_pb (pb);
+
+ slapi_pblock_get(pb, SLAPI_PLUGIN_INTOP_RESULT, &rc);
+ if (rc == LDAP_SUCCESS)
+ {
+ /* get RUV attributes and construct the RUV */
+ slapi_pblock_get(pb, SLAPI_PLUGIN_INTOP_SEARCH_ENTRIES, &entries);
+ if (NULL == entries || NULL == entries[0])
+ {
+ slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name,
+ "_replica_configure_ruv: replica ruv tombstone entry for "
+ "replica %s not found\n",
+ escape_string(slapi_sdn_get_dn(r->repl_root),ebuf));
+ goto done;
+ }
+
+ rc = slapi_entry_attr_find(entries[0], type_ruvElement, &attr);
+ if (rc != 0) /* ruv attribute is missing - this not allowed */
+ {
+ slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name,
+ "_replica_configure_ruv: replica ruv tombstone entry for "
+ "replica %s does not contain %s\n",
+ escape_string(slapi_sdn_get_dn(r->repl_root),ebuf), type_ruvElement);
+ goto done;
+ }
+
+ /* Check in the tombstone we have retrieved if the local purl is
+ already present:
+ rid == 0: the local purl is not present
+ rid != 0: the local purl is present ==> nothing to do
+ */
+ ruv_init_from_slapi_attr_and_check_purl (attr, &ruv, &rid);
+ if (ruv)
+ {
+ char *generation = NULL;
+ generation = ruv_get_replica_generation(ruv);
+ if (NULL != generation)
+ {
+ r->repl_ruv = object_new((void*)ruv, (FNFree)ruv_destroy);
+
+ /* Is the local purl in the ruv? (the port or the host could have
+ changed)
+ */
+ /* A consumer only doesn't have its purl in its ruv */
+ if (r->repl_type == REPLICA_TYPE_UPDATABLE)
+ {
+ int need_update = 0;
+ if (rid == 0)
+ {
+ /* We can not have more than 1 ruv with the same rid
+ so we replace it */
+ const char *purl = NULL;
+
+ purl = multimaster_get_local_purl();
+ ruv_delete_replica(ruv, r->repl_rid);
+ ruv_add_index_replica(ruv, r->repl_rid, purl, 1);
+ need_update = 1; /* ruv changed, so write tombstone */
+ }
+ else /* bug 540844: make sure the local supplier rid is first in the ruv */
+ {
+ /* make sure local supplier is first in list */
+ ReplicaId first_rid = 0;
+ char *first_purl = NULL;
+ ruv_get_first_id_and_purl(ruv, &first_rid, &first_purl);
+ /* if the local supplier is not first in the list . . . */
+ if (rid != first_rid)
+ {
+ /* . . . move the local supplier to the beginning of the list */
+ ruv_move_local_supplier_to_first(ruv, rid);
+ need_update = 1; /* must update tombstone also */
+ }
+ }
+
+ /* Update also the directory entry */
+ if (need_update) {
+ /* richm 20010821 bug 556498
+ replica_replace_ruv_tombstone acquires the repl_lock, so release
+ the lock then reacquire it if locked */
+ if (isLocked) PR_Unlock(r->repl_lock);
+ replica_replace_ruv_tombstone(r);
+ if (isLocked) PR_Lock(r->repl_lock);
+ }
+ }
+
+ slapi_ch_free((void **)&generation);
+ return_value = 0;
+ }
+ else
+ {
+ slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name,
+ "RUV for replica %s is missing replica generation\n",
+ escape_string(slapi_sdn_get_dn(r->repl_root),ebuf));
+ goto done;
+ }
+ }
+ else
+ {
+ slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name,
+ "Unable to convert %s attribute in entry %s to a replica update vector.\n",
+ type_ruvElement, escape_string(slapi_sdn_get_dn(r->repl_root),ebuf));
+ goto done;
+ }
+
+ }
+ else /* search failed */
+ {
+ if (LDAP_NO_SUCH_OBJECT == rc)
+ {
+ /* The entry doesn't exist: create it */
+ rc = replica_create_ruv_tombstone(r);
+ if (LDAP_SUCCESS != rc)
+ {
+ /*
+ * XXXggood - the following error appears on startup if we try
+ * to initialize replica RUVs before the backend instance is up.
+ * It's alarming to see this error, and we should suppress it
+ * (or avoid trying to configure it) if the backend instance is
+ * not yet online.
+ */
+ /*
+ * XXXrichm - you can also get this error when the backend is in
+ * read only mode c.f. bug 539782
+ */
+ slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name,
+ "_replica_configure_ruv: failed to create replica ruv tombstone "
+ "entry (%s); LDAP error - %d\n",
+ escape_string(slapi_sdn_get_dn(r->repl_root),ebuf), rc);
+ goto done;
+ }
+ else
+ {
+ slapi_log_error(SLAPI_LOG_REPL, repl_plugin_name,
+ "_replica_configure_ruv: No ruv tombstone found for replica %s. "
+ "Created a new one\n",
+ escape_string(slapi_sdn_get_dn(r->repl_root),ebuf));
+ return_value = 0;
+ }
+ }
+ else
+ {
+ /* see if the suffix is disabled */
+ char *state = slapi_mtn_get_state(r->repl_root);
+ if (state && !strcasecmp(state, "disabled"))
+ {
+ slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name,
+ "_replica_configure_ruv: replication disabled for "
+ "entry (%s); LDAP error - %d\n",
+ escape_string(slapi_sdn_get_dn(r->repl_root),ebuf), rc);
+ slapi_ch_free_string(&state);
+ goto done;
+ }
+ else if (!r->repl_ruv) /* other error */
+ {
+ slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name,
+ "_replica_configure_ruv: replication broken for "
+ "entry (%s); LDAP error - %d\n",
+ escape_string(slapi_sdn_get_dn(r->repl_root),ebuf), rc);
+ slapi_ch_free_string(&state);
+ goto done;
+ }
+ else /* some error but continue anyway? */
+ {
+ slapi_log_error(SLAPI_LOG_REPL, repl_plugin_name,
+ "_replica_configure_ruv: Error %d reading tombstone for replica %s.\n",
+ rc, escape_string(slapi_sdn_get_dn(r->repl_root),ebuf));
+ return_value = 0;
+ }
+ slapi_ch_free_string(&state);
+ }
+ }
+
+ if (NULL != r->min_csn_pl)
+ {
+ csnplFree (&r->min_csn_pl);
+ }
+
+ /* create pending list for min csn if necessary */
+ if (ruv_get_smallest_csn_for_replica ((RUV*)object_get_data (r->repl_ruv),
+ r->repl_rid, &csn) == RUV_SUCCESS)
+ {
+ csn_free (&csn);
+ r->min_csn_pl = NULL;
+ }
+ else
+ {
+ /*
+ * The local replica has not generated any of its own CSNs yet.
+ * We need to watch CSNs being generated and note the first
+ * locally-generated CSN that's committed. Once that event occurs,
+ * the RUV is suitable for iteration over locally generated
+ * changes.
+ */
+ r->min_csn_pl = csnplNew();
+ }
+
+done:
+ if (NULL != pb)
+ {
+ slapi_free_search_results_internal(pb);
+ slapi_pblock_destroy (pb);
+ }
+ if (return_value != 0)
+ {
+ if (ruv)
+ ruv_destroy (&ruv);
+ }
+
+ return return_value;
+}
+
+/* NOTE - this is the only non-api function that performs locking because
+ it is called by the event queue */
+static void
+_replica_update_state (time_t when, void *arg)
+{
+ int rc;
+ const char *replica_name = (const char *)arg;
+ Object *replica_object = NULL;
+ Replica *r;
+ Slapi_Mod smod;
+ LDAPMod *mods[3];
+ Slapi_PBlock *pb = NULL;
+ char *dn = NULL;
+
+ if (NULL == replica_name)
+ return;
+
+ /*
+ * replica_get_by_name() will acquire the replica object
+ * and that could prevent the replica from being destroyed
+ * until the object_release is called.
+ */
+ replica_object = replica_get_by_name(replica_name);
+ if (NULL == replica_object)
+ {
+ return;
+ }
+
+ /* We have a reference, so replica won't vanish on us. */
+ r = (Replica *)object_get_data(replica_object);
+ if (NULL == r)
+ {
+ goto done;
+ }
+
+ PR_Lock(r->repl_lock);
+
+ /* replica state is currently being updated
+ or no CSN was assigned - bail out */
+ if (r->state_update_inprogress)
+ {
+ PR_Unlock(r->repl_lock);
+ goto done;
+ }
+
+ /* This might be a consumer */
+ if (!r->repl_csn_assigned)
+ {
+ /* EY: the consumer needs to flush ruv to disk. */
+ PR_Unlock(r->repl_lock);
+ replica_write_ruv(r);
+ goto done;
+ }
+
+ /* ONREPL update csn generator state of an updatable replica only */
+ /* ONREPL state always changes because we update time every second and
+ we write state to the disk less frequently */
+ rc = csngen_get_state ((CSNGen*)object_get_data (r->repl_csngen), &smod);
+ if (rc != 0)
+ {
+ PR_Unlock(r->repl_lock);
+ goto done;
+ }
+
+ r->state_update_inprogress = PR_TRUE;
+ r->repl_csn_assigned = PR_FALSE;
+
+ dn = _replica_get_config_dn (r->repl_root);
+ pb = slapi_pblock_new();
+ mods[0] = (LDAPMod*)slapi_mod_get_ldapmod_byref(&smod);
+
+ /* we don't want to held lock during operations since it causes lock contention
+ and sometimes deadlock. So releasing lock here */
+
+ PR_Unlock(r->repl_lock);
+
+ /* replica repl_name and new_name attributes do not get changed once
+ the replica is configured - so it is ok that they are outside replica lock */
+
+ /* write replica name if it has not been written before */
+ if (r->new_name)
+ {
+ struct berval *vals[2];
+ struct berval val;
+ LDAPMod mod;
+
+ mods[1] = &mod;
+
+ mod.mod_op = LDAP_MOD_REPLACE;
+ mod.mod_type = (char*)attr_replicaName;
+ mod.mod_bvalues = vals;
+ vals [0] = &val;
+ vals [1] = NULL;
+ val.bv_val = r->repl_name;
+ val.bv_len = strlen (val.bv_val);
+ mods[2] = NULL;
+ }
+ else
+ {
+ mods[1] = NULL;
+ }
+
+ slapi_modify_internal_set_pb (pb, dn, mods, NULL, NULL,
+ repl_get_plugin_identity (PLUGIN_MULTIMASTER_REPLICATION), 0);
+ slapi_modify_internal_pb (pb);
+ slapi_pblock_get(pb, SLAPI_PLUGIN_INTOP_RESULT, &rc);
+ if (rc != LDAP_SUCCESS)
+ {
+ char ebuf[BUFSIZ];
+
+ slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name, "_replica_update_state: "
+ "failed to update state of csn generator for replica %s: LDAP "
+ "error - %d\n", escape_string(slapi_sdn_get_dn(r->repl_root),ebuf), rc);
+ }
+ else
+ {
+ r->new_name = PR_FALSE;
+ }
+
+ /* update RUV - performs its own locking */
+ replica_write_ruv (r);
+
+ /* since this is the only place this value is changed and we are
+ guaranteed that only one thread enters the function, its ok
+ to change it outside replica lock */
+ r->state_update_inprogress = PR_FALSE;
+
+ slapi_ch_free ((void**)&dn);
+ slapi_pblock_destroy (pb);
+ slapi_mod_done (&smod);
+
+done:
+ if (replica_object)
+ object_release (replica_object);
+}
+
+void
+replica_write_ruv (Replica *r)
+{
+ int rc;
+ Slapi_Mod smod;
+ Slapi_Mod smod_last_modified;
+ LDAPMod *mods [3];
+ Slapi_PBlock *pb;
+
+ PR_ASSERT(r);
+
+ PR_Lock(r->repl_lock);
+
+ if (!r->repl_ruv_dirty)
+ {
+ PR_Unlock(r->repl_lock);
+ return;
+ }
+
+ PR_ASSERT (r->repl_ruv);
+
+ ruv_to_smod ((RUV*)object_get_data(r->repl_ruv), &smod);
+ ruv_last_modified_to_smod ((RUV*)object_get_data(r->repl_ruv), &smod_last_modified);
+
+ PR_Unlock (r->repl_lock);
+
+ mods [0] = (LDAPMod *)slapi_mod_get_ldapmod_byref(&smod);
+ mods [1] = (LDAPMod *)slapi_mod_get_ldapmod_byref(&smod_last_modified);
+ mods [2] = NULL;
+ pb = slapi_pblock_new();
+
+ /* replica name never changes so it is ok to reference it outside the lock */
+ slapi_modify_internal_set_pb(
+ pb,
+ slapi_sdn_get_dn(r->repl_root), /* only used to select be */
+ mods,
+ NULL, /* controls */
+ RUV_STORAGE_ENTRY_UNIQUEID,
+ repl_get_plugin_identity (PLUGIN_MULTIMASTER_REPLICATION),
+ /* Add OP_FLAG_TOMBSTONE_ENTRY so that this doesn't get logged in the Retro ChangeLog */
+ OP_FLAG_REPLICATED | OP_FLAG_REPL_FIXUP | OP_FLAG_TOMBSTONE_ENTRY);
+ slapi_modify_internal_pb (pb);
+ slapi_pblock_get(pb, SLAPI_PLUGIN_INTOP_RESULT, &rc);
+
+ /* ruv does not exist - create one */
+ PR_Lock(r->repl_lock);
+
+ if (rc == LDAP_SUCCESS)
+ {
+ r->repl_ruv_dirty = PR_FALSE;
+ }
+ else if (rc == LDAP_NO_SUCH_OBJECT)
+ {
+ /* this includes an internal operation - but since this only happens
+ during server startup - its ok that we have lock around it */
+ rc = _replica_configure_ruv (r, PR_TRUE);
+ if (rc == 0)
+ r->repl_ruv_dirty = PR_FALSE;
+ }
+ else /* error */
+ {
+ char ebuf[BUFSIZ];
+
+ slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name,
+ "replica_write_ruv: failed to update RUV tombstone for %s; "
+ "LDAP error - %d\n",
+ escape_string(slapi_sdn_get_dn(r->repl_root),ebuf), rc);
+ PR_ASSERT (0);
+ }
+
+ PR_Unlock(r->repl_lock);
+
+ slapi_mod_done (&smod);
+ slapi_mod_done (&smod_last_modified);
+ slapi_pblock_destroy (pb);
+}
+
+
+const CSN *
+_get_deletion_csn(Slapi_Entry *e)
+{
+ const CSN *deletion_csn = NULL;
+
+ PR_ASSERT(NULL != e);
+ if (NULL != e)
+ {
+ Slapi_Attr *oc_attr = NULL;
+ if (entry_attr_find_wsi(e, SLAPI_ATTR_OBJECTCLASS, &oc_attr) == ATTRIBUTE_PRESENT)
+ {
+ Slapi_Value *tombstone_value = NULL;
+ struct berval v;
+ v.bv_val = SLAPI_ATTR_VALUE_TOMBSTONE;
+ v.bv_len = strlen(SLAPI_ATTR_VALUE_TOMBSTONE);
+ if (attr_value_find_wsi(oc_attr, &v, &tombstone_value) == VALUE_PRESENT)
+ {
+ deletion_csn = value_get_csn(tombstone_value, CSN_TYPE_VALUE_UPDATED);
+ }
+ }
+ }
+ return deletion_csn;
+}
+
+
+static void
+_delete_tombstone(const char *tombstone_dn, const char *uniqueid)
+{
+
+ PR_ASSERT(NULL != tombstone_dn && NULL != uniqueid);
+ if (NULL == tombstone_dn || NULL == uniqueid)
+ {
+ slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name, "_delete_tombstone: "
+ "NULL tombstone_dn or uniqueid provided.\n");
+ }
+ else
+ {
+ int ldaprc;
+ Slapi_PBlock *pb = slapi_pblock_new();
+ slapi_delete_internal_set_pb(pb, tombstone_dn, NULL, /* controls */
+ uniqueid, repl_get_plugin_identity(PLUGIN_MULTIMASTER_REPLICATION),
+ OP_FLAG_TOMBSTONE_ENTRY);
+ slapi_delete_internal_pb(pb);
+ slapi_pblock_get(pb, SLAPI_PLUGIN_INTOP_RESULT, &ldaprc);
+ if (LDAP_SUCCESS != ldaprc)
+ {
+ slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name,
+ "_delete_tombstone: unable to delete tombstone %s, "
+ "uniqueid %s: %s.\n", tombstone_dn, uniqueid,
+ ldap_err2string(ldaprc));
+ }
+ slapi_pblock_destroy(pb);
+ }
+}
+
+static
+void get_reap_result (int rc, void *cb_data)
+{
+ PR_ASSERT (cb_data);
+
+ ((reap_callback_data*)cb_data)->rc = rc;
+}
+
+static
+int process_reap_entry (Slapi_Entry *entry, void *cb_data)
+{
+ char ebuf[BUFSIZ];
+ char deletion_csn_str[CSN_STRSIZE];
+ char purge_csn_str[CSN_STRSIZE];
+ unsigned long *num_entriesp = &((reap_callback_data *)cb_data)->num_entries;
+ unsigned long *num_purged_entriesp = &((reap_callback_data *)cb_data)->num_purged_entries;
+ CSN *purge_csn = ((reap_callback_data *)cb_data)->purge_csn;
+ PRBool *tombstone_reap_stop = ((reap_callback_data *)cb_data)->tombstone_reap_stop;
+ /* we only ask for the objectclass in the search - the deletion csn is in the
+ objectclass attribute values - if we need more attributes returned by the
+ search in the future, see _replica_reap_tombstones below and add more to the
+ attrs array */
+ const CSN *deletion_csn = _get_deletion_csn(entry);
+
+ if ((NULL == deletion_csn || csn_compare(deletion_csn, purge_csn) < 0) &&
+ (!is_ruv_tombstone_entry(entry))) {
+ slapi_log_error(SLAPI_LOG_REPL, repl_plugin_name,
+ "_replica_reap_tombstones: removing tombstone %s "
+ "because its deletion csn (%s) is less than the "
+ "purge csn (%s).\n",
+ escape_string(slapi_entry_get_dn(entry), ebuf),
+ csn_as_string(deletion_csn, PR_FALSE, deletion_csn_str),
+ csn_as_string(purge_csn, PR_FALSE, purge_csn_str));
+ _delete_tombstone(slapi_entry_get_dn(entry),
+ slapi_entry_get_uniqueid(entry));
+ (*num_purged_entriesp)++;
+ }
+ else {
+ slapi_log_error(SLAPI_LOG_REPL, repl_plugin_name,
+ "_replica_reap_tombstones: NOT removing tombstone "
+ "%s\n", escape_string(slapi_entry_get_dn(entry),ebuf));
+ }
+ (*num_entriesp)++;
+ if (*tombstone_reap_stop || g_get_shutdown()) {
+ return -1;
+ }
+
+ return 0;
+}
+
+
+
+
+/* This does the actual work of searching for tombstones and deleting them.
+ This must be called in a separate thread because it may take a long time.
+*/
+static void
+_replica_reap_tombstones(void *arg)
+{
+ const char *replica_name = (const char *)arg;
+ Slapi_PBlock *pb = NULL;
+ Object *replica_object = NULL;
+ Replica *replica = NULL;
+ CSN *purge_csn = NULL;
+ char ebuf[BUFSIZ];
+
+ slapi_log_error(SLAPI_LOG_REPL, repl_plugin_name,
+ "Info: Beginning tombstone reap for replica %s.\n",
+ replica_name ? replica_name : "(null)");
+
+ if (NULL == replica_name)
+ {
+ slapi_log_error(SLAPI_LOG_REPL, repl_plugin_name,
+ "Warning: Replica name is null in tombstone reap\n");
+ goto done;
+ }
+
+ /*
+ * replica_get_by_name() will acquire the replica object
+ * and that could prevent the replica from being destroyed
+ * until the object_release is called.
+ */
+ replica_object = replica_get_by_name(replica_name);
+ if (NULL == replica_object)
+ {
+ slapi_log_error(SLAPI_LOG_REPL, repl_plugin_name,
+ "Warning: Replica object %s is null in tombstone reap\n", replica_name);
+ goto done;
+ }
+
+ /* We have a reference, so replica won't vanish on us. */
+ replica = (Replica *)object_get_data(replica_object);
+ if (NULL == replica)
+ {
+ slapi_log_error(SLAPI_LOG_REPL, repl_plugin_name,
+ "Warning: Replica %s is null in tombstone reap\n", replica_name);
+ goto done;
+ }
+
+ if (replica->tombstone_reap_stop)
+ {
+ slapi_log_error(SLAPI_LOG_REPL, repl_plugin_name,
+ "Info: Replica %s reap stop flag is set for tombstone reap\n", replica_name);
+ goto done;
+ }
+
+ purge_csn = replica_get_purge_csn(replica);
+ if (NULL != purge_csn)
+ {
+ LDAPControl **ctrls;
+ int oprc;
+ reap_callback_data cb_data;
+ char **attrs = NULL;
+
+ /* we just need the objectclass - for the deletion csn
+ and the dn and nsuniqueid - for possible deletion
+ saves time to return only 2 attrs
+ */
+ charray_add(&attrs, slapi_ch_strdup("objectclass"));
+ charray_add(&attrs, slapi_ch_strdup("nsuniqueid"));
+
+ ctrls = (LDAPControl **)slapi_ch_calloc (3, sizeof (LDAPControl *));
+ ctrls[0] = create_managedsait_control();
+ ctrls[1] = create_backend_control(replica->repl_root);
+ ctrls[2] = NULL;
+ pb = slapi_pblock_new();
+ slapi_search_internal_set_pb(pb, slapi_sdn_get_dn(replica->repl_root),
+ LDAP_SCOPE_SUBTREE, "(&(objectclass=nstombstone)(nscpentrydn=*))",
+ attrs, 0, ctrls, NULL,
+ repl_get_plugin_identity(PLUGIN_MULTIMASTER_REPLICATION), 0);
+
+ cb_data.rc = 0;
+ cb_data.num_entries = 0UL;
+ cb_data.num_purged_entries = 0UL;
+ cb_data.purge_csn = purge_csn;
+ cb_data.tombstone_reap_stop = &(replica->tombstone_reap_stop);
+
+ slapi_search_internal_callback_pb (pb, &cb_data /* callback data */,
+ get_reap_result /* result callback */,
+ process_reap_entry /* entry callback */,
+ NULL /* referral callback*/);
+
+ charray_free(attrs);
+
+ oprc = cb_data.rc;
+
+ if (LDAP_SUCCESS != oprc)
+ {
+ slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name,
+ "_replica_reap_tombstones: failed when searching for "
+ "tombstones in replica %s: %s. Will try again in %d "
+ "seconds.\n", escape_string(slapi_sdn_get_dn(replica->repl_root),ebuf),
+ ldap_err2string(oprc), replica->tombstone_reap_interval);
+ }
+ else
+ {
+ slapi_log_error(SLAPI_LOG_REPL, repl_plugin_name,
+ "_replica_reap_tombstones: purged %d of %d tombstones "
+ "in replica %s. Will try again in %d "
+ "seconds.\n", cb_data.num_purged_entries, cb_data.num_entries,
+ escape_string(slapi_sdn_get_dn(replica->repl_root),ebuf),
+ replica->tombstone_reap_interval);
+ }
+ }
+ else
+ {
+ slapi_log_error(SLAPI_LOG_REPL, repl_plugin_name,
+ "Info: No purge CSN for tombstone reap for replica %s.\n",
+ replica_name ? replica_name : "(null)");
+ }
+
+ PR_Lock(replica->repl_lock);
+ replica->tombstone_reap_active = PR_FALSE;
+ PR_Unlock(replica->repl_lock);
+
+done:
+ if (NULL != purge_csn)
+ {
+ csn_free(&purge_csn);
+ }
+ if (NULL != pb)
+ {
+ slapi_free_search_results_internal(pb);
+ slapi_pblock_destroy(pb);
+ }
+ if (NULL != replica_object)
+ {
+ object_release(replica_object);
+ replica_object = NULL;
+ replica = NULL;
+ }
+
+ slapi_log_error(SLAPI_LOG_REPL, repl_plugin_name,
+ "Info: Finished tombstone reap for replica %s.\n",
+ replica_name ? replica_name : "(null)");
+
+}
+
+/*
+ We don't want to run the reaper function directly from the event
+ queue since it may hog the event queue, starving other events.
+ See bug 604441
+ The function eq_cb_reap_tombstones will fire off the actual thread
+ that does the real work.
+*/
+static void
+eq_cb_reap_tombstones(time_t when, void *arg)
+{
+ const char *replica_name = (const char *)arg;
+ Object *replica_object = NULL;
+ Replica *replica = NULL;
+
+ if (NULL != replica_name)
+ {
+ /*
+ * replica_get_by_name() will acquire the replica object
+ * and that could prevent the replica from being destroyed
+ * until the object_release is called.
+ */
+ replica_object = replica_get_by_name(replica_name);
+ if (NULL != replica_object)
+ {
+ /* We have a reference, so replica won't vanish on us. */
+ replica = (Replica *)object_get_data(replica_object);
+ if (replica)
+ {
+
+ PR_Lock(replica->repl_lock);
+
+ /* No action if purge is disabled or the previous purge is not done yet */
+ if (replica->tombstone_reap_interval != 0 &&
+ replica->tombstone_reap_active == PR_FALSE)
+ {
+ /* set the flag here to minimize race conditions */
+ replica->tombstone_reap_active = PR_TRUE;
+ if (PR_CreateThread(PR_USER_THREAD,
+ _replica_reap_tombstones, (void *)replica_name,
+ PR_PRIORITY_NORMAL, PR_GLOBAL_THREAD, PR_UNJOINABLE_THREAD,
+ SLAPD_DEFAULT_THREAD_STACKSIZE) == NULL)
+ {
+ replica->tombstone_reap_active = PR_FALSE;
+ slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name,
+ "Error: unable to create the tombstone reap thread for replica %s. "
+ "Possible system resources problem\n",
+ replica_name);
+ }
+ }
+ /* reap thread will wait until this lock is released */
+ PR_Unlock(replica->repl_lock);
+ }
+ object_release(replica_object);
+ replica_object = NULL;
+ replica = NULL;
+ }
+ }
+}
+
+static char *
+_replica_type_as_string (const Replica *r)
+{
+ switch (r->repl_type)
+ {
+ case REPLICA_TYPE_PRIMARY: return "primary";
+ case REPLICA_TYPE_READONLY: return "read-only";
+ case REPLICA_TYPE_UPDATABLE: return "updatable";
+ default: return "unknown";
+ }
+}
+
+
+static const char *root_glue =
+ "dn: %s\n"
+ "objectclass: top\n"
+ "objectclass: nsTombstone\n"
+ "objectclass: extensibleobject\n"
+ "nsuniqueid: %s\n";
+
+static int
+replica_create_ruv_tombstone(Replica *r)
+{
+ int return_value = LDAP_LOCAL_ERROR;
+ char *root_entry_str;
+ Slapi_Entry *e;
+ const char *purl = NULL;
+ RUV *ruv;
+ struct berval **bvals = NULL;
+ Slapi_PBlock *pb = NULL;
+ int rc;
+ char ebuf[BUFSIZ];
+
+ PR_ASSERT(NULL != r && NULL != r->repl_root);
+ root_entry_str = slapi_ch_malloc(strlen(root_glue) +
+ slapi_sdn_get_ndn_len(r->repl_root) +
+ strlen(RUV_STORAGE_ENTRY_UNIQUEID) + 1);
+ sprintf(root_entry_str, root_glue, slapi_sdn_get_ndn(r->repl_root),
+ RUV_STORAGE_ENTRY_UNIQUEID);
+
+ e = slapi_str2entry(root_entry_str, SLAPI_STR2ENTRY_TOMBSTONE_CHECK);
+ if (e == NULL)
+ goto done;
+
+ /* Add ruv */
+ if (r->repl_ruv == NULL)
+ {
+ CSNGen *gen;
+ CSN *csn;
+ char csnstr [CSN_STRSIZE];
+
+ /* first attempt to write RUV tombstone - need to create RUV */
+ gen = (CSNGen *)object_get_data(r->repl_csngen);
+ PR_ASSERT (gen);
+
+ if (csngen_new_csn(gen, &csn, PR_FALSE /* notify */) == CSN_SUCCESS)
+ {
+ (void)csn_as_string(csn, PR_FALSE, csnstr);
+ csn_free(&csn);
+
+ /* if this is an updateable replica - add its own
+ element to the RUV so that referrals work correctly */
+ if (r->repl_type == REPLICA_TYPE_UPDATABLE)
+ purl = multimaster_get_local_purl();
+
+ if (ruv_init_new(csnstr, r->repl_rid, purl, &ruv) == RUV_SUCCESS)
+ {
+ r->repl_ruv = object_new((void*)ruv, (FNFree)ruv_destroy);
+ r->repl_ruv_dirty = PR_TRUE;
+ return_value = LDAP_SUCCESS;
+ }
+ else
+ {
+ slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name,
+ "Cannot create new replica update vector for %s\n",
+ escape_string(slapi_sdn_get_dn(r->repl_root),ebuf));
+ goto done;
+ }
+ }
+ else
+ {
+ slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name,
+ "Cannot obtain CSN for new replica update vector for %s\n",
+ escape_string(slapi_sdn_get_dn(r->repl_root),ebuf));
+ goto done;
+ }
+ }
+ else /* failed to write the entry because DB was not initialized - retry */
+ {
+ ruv = (RUV*) object_get_data (r->repl_ruv);
+ PR_ASSERT (ruv);
+ }
+
+ PR_ASSERT (r->repl_ruv);
+
+ rc = ruv_to_bervals(ruv, &bvals);
+ if (rc != RUV_SUCCESS)
+ {
+ goto done;
+ }
+
+ /* ONREPL this is depricated function but there is currently no better API to use */
+ rc = slapi_entry_add_values(e, type_ruvElement, bvals);
+ if (rc != 0)
+ {
+ goto done;
+ }
+
+
+ pb = slapi_pblock_new();
+ slapi_add_entry_internal_set_pb(
+ pb,
+ e,
+ NULL /* controls */,
+ repl_get_plugin_identity(PLUGIN_MULTIMASTER_REPLICATION),
+ OP_FLAG_TOMBSTONE_ENTRY | OP_FLAG_REPLICATED | OP_FLAG_REPL_FIXUP);
+ slapi_add_internal_pb(pb);
+ slapi_pblock_get(pb, SLAPI_PLUGIN_INTOP_RESULT, &return_value);
+ if (return_value == LDAP_SUCCESS)
+ r->repl_ruv_dirty = PR_FALSE;
+
+done:
+ if (return_value != LDAP_SUCCESS)
+ {
+ slapi_entry_free (e);
+ }
+
+ if (bvals)
+ ber_bvecfree(bvals);
+
+ if (pb)
+ slapi_pblock_destroy(pb);
+
+ slapi_ch_free((void **) &root_entry_str);
+
+ return return_value;
+}
+
+
+static void
+assign_csn_callback(const CSN *csn, void *data)
+{
+ Replica *r = (Replica *)data;
+ Object *ruv_obj;
+ RUV *ruv;
+
+ PR_ASSERT(NULL != csn);
+ PR_ASSERT(NULL != r);
+
+ ruv_obj = replica_get_ruv (r);
+ PR_ASSERT (ruv_obj);
+ ruv = (RUV*)object_get_data (ruv_obj);
+ PR_ASSERT (ruv);
+
+ PR_Lock(r->repl_lock);
+
+ r->repl_csn_assigned = PR_TRUE;
+
+ if (NULL != r->min_csn_pl)
+ {
+ if (csnplInsert(r->min_csn_pl, csn) != 0)
+ {
+ char ebuf[BUFSIZ];
+ char csn_str[CSN_STRSIZE]; /* For logging only */
+ /* Ack, we can't keep track of min csn. Punt. */
+ slapi_log_error(SLAPI_LOG_REPL, repl_plugin_name, "assign_csn_callback: "
+ "failed to insert csn %s for replica %s\n",
+ csn_as_string(csn, PR_FALSE, csn_str),
+ escape_string(slapi_sdn_get_dn(r->repl_root), ebuf));
+ csnplFree(&r->min_csn_pl);
+ }
+ }
+
+ ruv_add_csn_inprogress (ruv, csn);
+
+ PR_Unlock(r->repl_lock);
+
+ object_release (ruv_obj);
+}
+
+
+static void
+abort_csn_callback(const CSN *csn, void *data)
+{
+ Replica *r = (Replica *)data;
+ Object *ruv_obj;
+ RUV *ruv;
+ int rc;
+
+ PR_ASSERT(NULL != csn);
+ PR_ASSERT(NULL != data);
+
+ ruv_obj = replica_get_ruv (r);
+ PR_ASSERT (ruv_obj);
+ ruv = (RUV*)object_get_data (ruv_obj);
+ PR_ASSERT (ruv);
+
+ PR_Lock(r->repl_lock);
+
+ if (NULL != r->min_csn_pl)
+ {
+ rc = csnplRemove(r->min_csn_pl, csn);
+ PR_ASSERT(rc == 0);
+ }
+
+ ruv_cancel_csn_inprogress (ruv, csn);
+ PR_Unlock(r->repl_lock);
+
+ object_release (ruv_obj);
+}
+
+static CSN *
+_replica_get_purge_csn_nolock(const Replica *r)
+{
+ static unsigned long a_week = 3600*24*7;
+ CSN *purge_csn = NULL;
+ CSN **csns = NULL;
+ RUV *ruv;
+ time_t cutoff_time;
+ time_t max_time_in_csn_list;
+ int i;
+
+ if (r->repl_purge_delay > 0)
+ {
+ /*
+ * Don't let inactive or obsolete masters in the ruv hold back
+ * the purge forever:
+ * - set a graceful period of at least 7 days;
+ * - set cutoff_time = max(maxcsns) - gracefule_period;
+ * - the first maxcsn that was generated at or after the cutoff
+ * time would be the purge csn.
+ */
+
+ /* get a sorted list of all maxcsns in ruv in ascend order */
+ object_acquire(r->repl_ruv);
+ ruv = object_get_data(r->repl_ruv);
+ csns = cl5BuildCSNList (ruv, NULL);
+ object_release(r->repl_ruv);
+
+ if (csns == NULL)
+ return NULL;
+
+ /* locate the max csn in the csn list */
+ for (i = 0; csns[i]; i++);
+ max_time_in_csn_list = csn_get_time (csns[i-1]);
+
+ if ( r->repl_purge_delay > a_week )
+ {
+ cutoff_time = max_time_in_csn_list - r->repl_purge_delay;
+ }
+ else
+ {
+ cutoff_time = max_time_in_csn_list - a_week;
+ }
+ for (i = 0; csns[i]; i++)
+ {
+ if ( csn_get_time (csns[i]) >= cutoff_time )
+ {
+ purge_csn = csn_dup (csns[i]);
+ break;
+ }
+ }
+
+ /* Subtract purge delay */
+ if (purge_csn)
+ {
+ csn_set_time(purge_csn, csn_get_time(purge_csn) - r->repl_purge_delay);
+ }
+ }
+
+ if (csns)
+ cl5DestroyCSNList (&csns);
+
+ return purge_csn;
+}
+
+static void
+replica_get_referrals_nolock (const Replica *r, char ***referrals)
+{
+ if(referrals!=NULL)
+ {
+
+ int hint;
+ int i= 0;
+ Slapi_Value *v= NULL;
+
+ if (NULL == r->repl_referral)
+ {
+ *referrals = NULL;
+ }
+ else
+ {
+ /* richm: +1 for trailing NULL */
+ *referrals= (char**)slapi_ch_calloc(sizeof(char*),1+slapi_valueset_count(r->repl_referral));
+ hint= slapi_valueset_first_value( r->repl_referral, &v );
+ while(v!=NULL)
+ {
+ const char *s= slapi_value_get_string(v);
+ if(s!=NULL && s[0]!='\0')
+ {
+ (*referrals)[i]= slapi_ch_strdup(s);
+ i++;
+ }
+ hint= slapi_valueset_next_value( r->repl_referral, hint, &v);
+ }
+ (*referrals)[i] = NULL;
+ }
+
+ }
+}
+
+static void
+replica_clear_legacy_referrals(const Slapi_DN *repl_root_sdn,
+ char **referrals, const char *state)
+{
+ repl_set_mtn_state_and_referrals(repl_root_sdn, state, NULL, NULL, referrals);
+}
+
+static void
+replica_remove_legacy_attr (const Slapi_DN *repl_root_sdn, const char *attr)
+{
+ Slapi_PBlock *pb;
+ Slapi_Mods smods;
+ LDAPControl **ctrls;
+ int rc;
+
+ pb = slapi_pblock_new ();
+
+ slapi_mods_init(&smods, 1);
+ slapi_mods_add(&smods, LDAP_MOD_DELETE, attr, 0, NULL);
+
+
+ ctrls = (LDAPControl**)slapi_ch_malloc (2 * sizeof (LDAPControl*));
+ ctrls[0] = create_managedsait_control ();
+ ctrls[1] = NULL;
+
+ /* remove copiedFrom/copyingFrom first */
+ slapi_modify_internal_set_pb (pb, slapi_sdn_get_dn (repl_root_sdn),
+ slapi_mods_get_ldapmods_passout (&smods), ctrls,
+ NULL /*uniqueid */,
+ repl_get_plugin_identity (PLUGIN_MULTIMASTER_REPLICATION) ,
+ 0 /* operation_flags */);
+
+ slapi_modify_internal_pb (pb);
+ slapi_pblock_get(pb, SLAPI_PLUGIN_INTOP_RESULT, &rc);
+ if (rc != LDAP_SUCCESS)
+ {
+ char ebuf[BUFSIZ];
+
+ /* this is not a fatal error because the attribute may not be there */
+ slapi_log_error(SLAPI_LOG_REPL, repl_plugin_name, "replica_remove_legacy_attr: "
+ "failed to remove legacy attribute %s for replica %s; LDAP error - %d\n",
+ attr, escape_string(slapi_sdn_get_dn(repl_root_sdn),ebuf), rc);
+ }
+
+ slapi_mods_done (&smods);
+ slapi_pblock_destroy (pb);
+}
+
+static int
+replica_log_ruv_elements_nolock (const Replica *r)
+{
+ int rc = 0;
+ slapi_operation_parameters op_params;
+ RUV *ruv;
+ char *repl_gen;
+ CSN *csn = NULL;
+
+ ruv = (RUV*) object_get_data (r->repl_ruv);
+ PR_ASSERT (ruv);
+
+ if ((ruv_get_min_csn(ruv, &csn) == RUV_SUCCESS) && csn)
+ {
+ /* we log it as a delete operation to have the least number of fields
+ to set. the entry can be identified by a special target uniqueid and
+ special target dn */
+ memset (&op_params, 0, sizeof (op_params));
+ op_params.operation_type = SLAPI_OPERATION_DELETE;
+ op_params.target_address.dn = START_ITERATION_ENTRY_DN;
+ op_params.target_address.uniqueid = START_ITERATION_ENTRY_UNIQUEID;
+ op_params.csn = csn;
+ repl_gen = ruv_get_replica_generation (ruv);
+
+ rc = cl5WriteOperation(r->repl_name, repl_gen, &op_params, PR_FALSE);
+ if (rc == CL5_SUCCESS)
+ rc = 0;
+ else
+ rc = -1;
+
+ slapi_ch_free ((void**)&repl_gen);
+ csn_free (&csn);
+ }
+
+ return rc;
+}
+
+void
+replica_set_purge_delay(Replica *r, PRUint32 purge_delay)
+{
+ PR_ASSERT(r);
+ PR_Lock(r->repl_lock);
+ r->repl_purge_delay = purge_delay;
+ PR_Unlock(r->repl_lock);
+}
+
+void
+replica_set_tombstone_reap_interval (Replica *r, long interval)
+{
+ char *repl_name;
+
+ PR_Lock(r->repl_lock);
+
+ /*
+ * Leave the event there to purge the existing tombstones
+ * if we are about to turn off tombstone creation
+ */
+ if (interval > 0 && r->repl_eqcxt_tr && r->tombstone_reap_interval != interval)
+ {
+ int found;
+
+ repl_name = slapi_eq_get_arg (r->repl_eqcxt_tr);
+ slapi_ch_free ((void**)&repl_name);
+ found = slapi_eq_cancel (r->repl_eqcxt_tr);
+ slapi_log_error (SLAPI_LOG_REPL, NULL,
+ "tombstone_reap event (interval=%d) was %s\n",
+ r->tombstone_reap_interval, (found ? "cancelled" : "not found"));
+ r->repl_eqcxt_tr = NULL;
+ }
+ r->tombstone_reap_interval = interval;
+ if ( interval > 0 && r->repl_eqcxt_tr == NULL )
+ {
+ repl_name = slapi_ch_strdup (r->repl_name);
+ r->repl_eqcxt_tr = slapi_eq_repeat (eq_cb_reap_tombstones, repl_name, current_time() + START_REAP_DELAY, 1000 * r->tombstone_reap_interval);
+ slapi_log_error (SLAPI_LOG_REPL, NULL,
+ "tombstone_reap event (interval=%d) was %s\n",
+ r->tombstone_reap_interval, (r->repl_eqcxt_tr ? "scheduled" : "not scheduled successfully"));
+ }
+ PR_Unlock(r->repl_lock);
+}
+
+/* Update the tombstone entry to reflect the content of the ruv */
+static void
+replica_replace_ruv_tombstone(Replica *r)
+{
+ Slapi_PBlock *pb = NULL;
+ char *dn;
+ int rc;
+
+ Slapi_Mod smod;
+ Slapi_Mod smod_last_modified;
+ LDAPMod *mods [3];
+
+ PR_ASSERT(NULL != r && NULL != r->repl_root);
+
+ PR_Lock(r->repl_lock);
+
+ PR_ASSERT (r->repl_ruv);
+ ruv_to_smod ((RUV*)object_get_data(r->repl_ruv), &smod);
+ ruv_last_modified_to_smod ((RUV*)object_get_data(r->repl_ruv), &smod_last_modified);
+
+ dn = _replica_get_config_dn (r->repl_root);
+ mods[0] = (LDAPMod*)slapi_mod_get_ldapmod_byref(&smod);
+ mods[1] = (LDAPMod*)slapi_mod_get_ldapmod_byref(&smod_last_modified);
+
+ PR_Unlock (r->repl_lock);
+
+ mods [2] = NULL;
+ pb = slapi_pblock_new();
+
+ slapi_modify_internal_set_pb(
+ pb,
+ (char*)slapi_sdn_get_dn (r->repl_root), /* only used to select be */
+ mods,
+ NULL, /* controls */
+ RUV_STORAGE_ENTRY_UNIQUEID,
+ repl_get_plugin_identity (PLUGIN_MULTIMASTER_REPLICATION),
+ OP_FLAG_REPLICATED | OP_FLAG_REPL_FIXUP);
+
+ slapi_modify_internal_pb (pb);
+
+ slapi_pblock_get(pb, SLAPI_PLUGIN_INTOP_RESULT, &rc);
+
+ if (rc != LDAP_SUCCESS)
+ {
+ if ((rc != LDAP_NO_SUCH_OBJECT) || !replica_is_state_flag_set(r, REPLICA_IN_USE))
+ {
+ slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name, "replica_replace_ruv_tombstone: "
+ "failed to update replication update vector for replica %s: LDAP "
+ "error - %d\n", (char*)slapi_sdn_get_dn (r->repl_root), rc);
+ }
+ }
+
+ slapi_ch_free ((void**)&dn);
+ slapi_pblock_destroy (pb);
+ slapi_mod_done (&smod);
+ slapi_mod_done (&smod_last_modified);
+}
+
+void
+replica_update_ruv_consumer(Replica *r, RUV *supplier_ruv)
+{
+ ReplicaId supplier_id = 0;
+ char *supplier_purl = NULL;
+
+ if ( ruv_get_first_id_and_purl(supplier_ruv, &supplier_id, &supplier_purl) == RUV_SUCCESS )
+ {
+ RUV *local_ruv = NULL;
+
+ PR_Lock(r->repl_lock);
+
+ local_ruv = (RUV*)object_get_data (r->repl_ruv);
+ PR_ASSERT (local_ruv);
+
+ if ( ruv_local_contains_supplier(local_ruv, supplier_id) == 0 )
+ {
+ if ( r->repl_type == REPLICA_TYPE_UPDATABLE )
+ {
+ /* Add the new ruv right after the consumer own purl */
+ ruv_add_index_replica(local_ruv, supplier_id, supplier_purl, 2);
+ }
+ else
+ {
+ /* This is a consumer only, add it first */
+ ruv_add_index_replica(local_ruv, supplier_id, supplier_purl, 1);
+ }
+ }
+ else
+ {
+ /* Replace it */
+ ruv_replace_replica_purl(local_ruv, supplier_id, supplier_purl);
+ }
+ PR_Unlock(r->repl_lock);
+
+ /* Update also the directory entry */
+ replica_replace_ruv_tombstone(r);
+ }
+}
+
+void
+replica_set_ruv_dirty(Replica *r)
+{
+ PR_ASSERT(r);
+ PR_Lock(r->repl_lock);
+ r->repl_ruv_dirty = PR_TRUE;
+ PR_Unlock(r->repl_lock);
+}
+
+PRBool
+replica_is_state_flag_set(Replica *r, PRInt32 flag)
+{
+ PR_ASSERT(r);
+ if (r)
+ return (r->repl_state_flags & flag);
+ else
+ return PR_FALSE;
+}
+
+void
+replica_set_state_flag (Replica *r, PRUint32 flag, PRBool clear)
+{
+ if (r == NULL)
+ return;
+
+ PR_Lock(r->repl_lock);
+
+ if (clear)
+ {
+ r->repl_state_flags &= ~flag;
+ }
+ else
+ {
+ r->repl_state_flags |= flag;
+ }
+
+ PR_Unlock(r->repl_lock);
+}
+
+/* replica just came back online, probably after data was reloaded */
+void
+replica_enable_replication (Replica *r)
+{
+ int rc;
+
+ PR_ASSERT(r);
+
+ /* prevent creation of new agreements until the replica is enabled */
+ PR_Lock(r->agmt_lock);
+
+ /* retrieve new ruv */
+ rc = replica_reload_ruv (r);
+ if (rc) {
+ slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name, "replica_enable_replication: "
+ "reloading ruv failed\n");
+ /* What to do ? */
+ }
+
+ /* Replica came back online, Check if the total update was terminated.
+ If flag is still set, it was not terminated, therefore the data is
+ very likely to be incorrect, and we should not restart Replication threads...
+ */
+ if (!replica_is_state_flag_set(r, REPLICA_TOTAL_IN_PROGRESS)){
+ /* restart outbound replication */
+ start_agreements_for_replica (r, PR_TRUE);
+
+ /* enable ruv state update */
+ replica_set_enabled (r, PR_TRUE);
+ }
+
+ /* mark the replica as being available for updates */
+ replica_relinquish_exclusive_access(r, 0, 0);
+
+ replica_set_state_flag(r, REPLICA_AGREEMENTS_DISABLED, PR_TRUE /* clear */);
+ PR_Unlock(r->agmt_lock);
+
+ slapi_log_error(SLAPI_LOG_REPL, repl_plugin_name, "replica_enable_replication: "
+ "replica %s is relinquished\n",
+ slapi_sdn_get_ndn (replica_get_root (r)));
+}
+
+/* replica is about to be taken offline */
+void
+replica_disable_replication (Replica *r, Object *r_obj)
+{
+ char *current_purl = NULL;
+ char *p_locking_purl = NULL;
+ char *locking_purl = NULL;
+ int junkrc;
+ ReplicaId junkrid;
+ PRBool isInc = PR_FALSE; /* get exclusive access, but not for inc update */
+ RUV *repl_ruv = NULL;
+
+ /* prevent creation of new agreements until the replica is disabled */
+ PR_Lock(r->agmt_lock);
+
+ /* stop ruv update */
+ replica_set_enabled (r, PR_FALSE);
+
+ /* disable outbound replication */
+ start_agreements_for_replica (r, PR_FALSE);
+
+ /* close the corresponding changelog file */
+ /* close_changelog_for_replica (r_obj); */
+
+ /* mark the replica as being unavailable for updates */
+ /* If an incremental update is in progress, we want to wait until it is
+ finished until we get exclusive access to the replica, because we have
+ to make sure no operations are in progress - it messes up replication
+ when a restore is in progress but we are still adding replicated entries
+ from a supplier
+ */
+ repl_ruv = (RUV*) object_get_data (r->repl_ruv);
+ junkrc = ruv_get_first_id_and_purl(repl_ruv, &junkrid, &p_locking_purl);
+ locking_purl = slapi_ch_strdup(p_locking_purl);
+ p_locking_purl = NULL;
+ repl_ruv = NULL;
+ while (!replica_get_exclusive_access(r, &isInc, 0, 0, "replica_disable_replication",
+ &current_purl)) {
+ if (!isInc) /* already locked, but not by inc update - break */
+ break;
+ isInc = PR_FALSE;
+ slapi_log_error(SLAPI_LOG_REPL, repl_plugin_name,
+ "replica_disable_replication: "
+ "replica %s is already locked by (%s) for incoming "
+ "incremental update; sleeping 100ms\n",
+ slapi_sdn_get_ndn (replica_get_root (r)),
+ current_purl ? current_purl : "unknown");
+ slapi_ch_free_string(&current_purl);
+ DS_Sleep(PR_MillisecondsToInterval(100));
+ }
+
+ slapi_ch_free_string(&current_purl);
+ slapi_ch_free_string(&locking_purl);
+ replica_set_state_flag(r, REPLICA_AGREEMENTS_DISABLED, PR_FALSE);
+ PR_Unlock(r->agmt_lock);
+
+ slapi_log_error(SLAPI_LOG_REPL, repl_plugin_name, "replica_disable_replication: "
+ "replica %s is acquired\n",
+ slapi_sdn_get_ndn (replica_get_root (r)));
+}
+
+static void
+start_agreements_for_replica (Replica *r, PRBool start)
+{
+ Object *agmt_obj;
+ Repl_Agmt *agmt;
+
+ agmt_obj = agmtlist_get_first_agreement_for_replica (r);
+ while (agmt_obj)
+ {
+ agmt = (Repl_Agmt*)object_get_data (agmt_obj);
+ PR_ASSERT (agmt);
+
+ if (start)
+ agmt_start (agmt);
+ else /* stop */
+ agmt_stop (agmt);
+
+ agmt_obj = agmtlist_get_next_agreement_for_replica (r, agmt_obj);
+ }
+}
+
+int replica_start_agreement(Replica *r, Repl_Agmt *ra)
+{
+ int ret = 0;
+
+ if (r == NULL) return -1;
+
+ PR_Lock(r->agmt_lock);
+
+ if (!replica_is_state_flag_set(r, REPLICA_AGREEMENTS_DISABLED)) {
+ ret = agmt_start(ra); /* Start the replication agreement */
+ }
+
+ PR_Unlock(r->agmt_lock);
+ return ret;
+}
+
+/*
+ * A callback function registed as op->o_csngen_handler and
+ * called by backend ops to generate opcsn.
+ */
+CSN *
+replica_generate_next_csn ( Slapi_PBlock *pb, const CSN *basecsn )
+{
+ CSN *opcsn = NULL;
+ Object *replica_obj;
+
+ replica_obj = replica_get_replica_for_op (pb);
+ if (NULL != replica_obj)
+ {
+ Replica *replica = (Replica*) object_get_data (replica_obj);
+ if ( NULL != replica )
+ {
+ Slapi_Operation *op;
+ slapi_pblock_get (pb, SLAPI_OPERATION, &op);
+ if ( replica->repl_type != REPLICA_TYPE_READONLY ||
+ operation_is_flag_set (op, OP_FLAG_LEGACY_REPLICATION_DN ))
+ {
+ Object *gen_obj = replica_get_csngen (replica);
+ if (NULL != gen_obj)
+ {
+ CSNGen *gen = (CSNGen*) object_get_data (gen_obj);
+ if (NULL != gen)
+ {
+ /* The new CSN should be greater than the base CSN */
+ csngen_new_csn (gen, &opcsn, PR_FALSE /* don't notify */);
+ if (csn_compare (opcsn, basecsn) <= 0)
+ {
+ char opcsnstr[CSN_STRSIZE], basecsnstr[CSN_STRSIZE];
+ char opcsn2str[CSN_STRSIZE];
+
+ csn_as_string (opcsn, PR_FALSE, opcsnstr);
+ csn_as_string (basecsn, PR_FALSE, basecsnstr);
+ csn_free ( &opcsn );
+ csngen_adjust_time (gen, basecsn);
+ csngen_new_csn (gen, &opcsn, PR_FALSE /* don't notify */);
+ csn_as_string (opcsn, PR_FALSE, opcsn2str);
+ slapi_log_error (SLAPI_LOG_FATAL, NULL,
+ "replica_generate_next_csn: "
+ "opcsn=%s <= basecsn=%s, adjusted opcsn=%s\n",
+ opcsnstr, basecsnstr, opcsn2str);
+ }
+ /*
+ * Insert opcsn into the csn pending list.
+ * This is the notify effect in csngen_new_csn().
+ */
+ assign_csn_callback (opcsn, (void *)replica);
+ }
+ object_release (gen_obj);
+ }
+ }
+ }
+ object_release (replica_obj);
+ }
+
+ return opcsn;
+}
+
+/*
+ * A callback function registed as op->o_replica_attr_handler and
+ * called by backend ops to get replica attributes.
+ */
+int
+replica_get_attr ( Slapi_PBlock *pb, const char* type, void *value )
+{
+ int rc = -1;
+
+ Object *replica_obj;
+ replica_obj = replica_get_replica_for_op (pb);
+ if (NULL != replica_obj)
+ {
+ Replica *replica = (Replica*) object_get_data (replica_obj);
+ if ( NULL != replica )
+ {
+ if (strcasecmp (type, type_replicaTombstonePurgeInterval) == 0)
+ {
+ *((int*)value) = replica->tombstone_reap_interval;
+ rc = 0;
+ }
+ else if (strcasecmp (type, type_replicaPurgeDelay) == 0)
+ {
+ *((int*)value) = replica->repl_purge_delay;
+ rc = 0;
+ }
+ }
+ object_release (replica_obj);
+ }
+
+ return rc;
+}
diff --git a/ldap/servers/plugins/replication/repl5_replica_config.c b/ldap/servers/plugins/replication/repl5_replica_config.c
new file mode 100644
index 00000000..df2573a0
--- /dev/null
+++ b/ldap/servers/plugins/replication/repl5_replica_config.c
@@ -0,0 +1,750 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+
+/* repl5_replica_config.c - replica configuration over ldap */
+#include <ctype.h> /* for isdigit() */
+#include "repl.h" /* ONREPL - this is bad */
+#include "repl5.h"
+#include "cl5_api.h"
+
+#define CONFIG_BASE "cn=mapping tree,cn=config"
+#define CONFIG_FILTER "(objectclass=nsDS5Replica)"
+#define TASK_ATTR "nsds5Task"
+#define CL2LDIF_TASK "CL2LDIF"
+#define CLEANRUV "CLEANRUV"
+#define CLEANRUVLEN 8
+
+int slapi_log_urp = SLAPI_LOG_REPL;
+
+/* Forward Declartions */
+static int replica_config_add (Slapi_PBlock *pb, Slapi_Entry* e, Slapi_Entry* entryAfter, int *returncode, char *returntext, void *arg);
+static int replica_config_modify (Slapi_PBlock *pb, Slapi_Entry* e, Slapi_Entry* entryAfter, int *returncode, char *returntext, void *arg);
+static int replica_config_delete (Slapi_PBlock *pb, Slapi_Entry* e, Slapi_Entry* entryAfter, int *returncode, char *returntext, void *arg);
+static int replica_config_search (Slapi_PBlock *pb, Slapi_Entry* e, Slapi_Entry* entryAfter, int *returncode, char *returntext, void *arg);
+
+static int replica_config_change_type_and_id (Replica *r, const char *new_type, const char *new_id, char *returntext, int apply_mods);
+static int replica_config_change_updatedn (Replica *r, const LDAPMod *mod, char *returntext, int apply_mods);
+static int replica_config_change_flags (Replica *r, const char *new_flags, char *returntext, int apply_mods);
+static int replica_execute_task (Object *r, const char *task_name, char *returntext, int apply_mods);
+static int replica_execute_cl2ldif_task (Object *r, char *returntext);
+static int replica_execute_cleanruv_task (Object *r, ReplicaId rid, char *returntext);
+
+static multimaster_mtnode_extension * _replica_config_get_mtnode_ext (const Slapi_Entry *e);
+
+static PRLock *s_configLock;
+
+static int
+dont_allow_that(Slapi_PBlock *pb, Slapi_Entry* entryBefore, Slapi_Entry* e, int *returncode, char *returntext, void *arg)
+{
+ *returncode = LDAP_UNWILLING_TO_PERFORM;
+ return SLAPI_DSE_CALLBACK_ERROR;
+}
+
+int
+replica_config_init()
+{
+ s_configLock = PR_NewLock ();
+ if (s_configLock == NULL)
+ {
+ slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name, "replica_config_init: "
+ "failed to cretate configuration lock; NSPR error - %d\n",
+ PR_GetError ());
+ return -1;
+ }
+
+ /* config DSE must be initialized before we get here */
+ slapi_config_register_callback(SLAPI_OPERATION_ADD, DSE_FLAG_PREOP, CONFIG_BASE, LDAP_SCOPE_SUBTREE,
+ CONFIG_FILTER, replica_config_add, NULL);
+ slapi_config_register_callback(SLAPI_OPERATION_MODIFY, DSE_FLAG_PREOP, CONFIG_BASE, LDAP_SCOPE_SUBTREE,
+ CONFIG_FILTER, replica_config_modify,NULL);
+ slapi_config_register_callback(SLAPI_OPERATION_MODRDN, DSE_FLAG_PREOP, CONFIG_BASE, LDAP_SCOPE_SUBTREE,
+ CONFIG_FILTER, dont_allow_that, NULL);
+ slapi_config_register_callback(SLAPI_OPERATION_DELETE, DSE_FLAG_PREOP, CONFIG_BASE, LDAP_SCOPE_SUBTREE,
+ CONFIG_FILTER, replica_config_delete,NULL);
+ slapi_config_register_callback(SLAPI_OPERATION_SEARCH, DSE_FLAG_PREOP, CONFIG_BASE, LDAP_SCOPE_SUBTREE,
+ CONFIG_FILTER, replica_config_search,NULL);
+ return 0;
+}
+
+void
+replica_config_destroy ()
+{
+ if (s_configLock)
+ {
+ PR_DestroyLock (s_configLock);
+ s_configLock = NULL;
+ }
+
+ /* config DSE must be initialized before we get here */
+ slapi_config_remove_callback(SLAPI_OPERATION_ADD, DSE_FLAG_PREOP, CONFIG_BASE, LDAP_SCOPE_SUBTREE,
+ CONFIG_FILTER, replica_config_add);
+ slapi_config_remove_callback(SLAPI_OPERATION_MODIFY, DSE_FLAG_PREOP, CONFIG_BASE, LDAP_SCOPE_SUBTREE,
+ CONFIG_FILTER, replica_config_modify);
+ slapi_config_remove_callback(SLAPI_OPERATION_MODRDN, DSE_FLAG_PREOP, CONFIG_BASE, LDAP_SCOPE_SUBTREE,
+ CONFIG_FILTER, dont_allow_that);
+ slapi_config_remove_callback(SLAPI_OPERATION_DELETE, DSE_FLAG_PREOP, CONFIG_BASE, LDAP_SCOPE_SUBTREE,
+ CONFIG_FILTER, replica_config_delete);
+ slapi_config_remove_callback(SLAPI_OPERATION_SEARCH, DSE_FLAG_PREOP, CONFIG_BASE, LDAP_SCOPE_SUBTREE,
+ CONFIG_FILTER, replica_config_search);
+}
+
+static int
+replica_config_add (Slapi_PBlock *pb, Slapi_Entry* e, Slapi_Entry* entryAfter,
+ int *returncode, char *errorbuf, void *arg)
+{
+ Replica *r = NULL;
+ multimaster_mtnode_extension *mtnode_ext;
+ char *replica_root = (char*)slapi_entry_attr_get_charptr (e, attr_replicaRoot);
+ char buf [BUFSIZ];
+ char *errortext = errorbuf ? errorbuf : buf;
+
+ if (errorbuf)
+ {
+ errorbuf[0] = '\0';
+ }
+
+ *returncode = LDAP_SUCCESS;
+
+ PR_Lock (s_configLock);
+
+ /* add the dn to the dn hash so we can tell this replica is being configured */
+ replica_add_by_dn(replica_root);
+
+ mtnode_ext = _replica_config_get_mtnode_ext (e);
+ PR_ASSERT (mtnode_ext);
+
+ if (mtnode_ext->replica)
+ {
+ sprintf (errortext, "replica already configured for %s", replica_root);
+ slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name, "replica_config_add: %s\n", errortext);
+ *returncode = LDAP_UNWILLING_TO_PERFORM;
+ goto done;
+ }
+
+ /* create replica object */
+ r = replica_new_from_entry (e, errortext, PR_TRUE /* is a newly added entry */);
+ if (r == NULL)
+ {
+ *returncode = LDAP_OPERATIONS_ERROR;
+ goto done;
+ }
+
+ /* Set the mapping tree node state, and the referrals from the RUV */
+ /* if this server is a 4.0 consumer the referrals are set by legacy plugin */
+ if (!replica_is_legacy_consumer (r))
+ consumer5_set_mapping_tree_state_for_replica(r, NULL);
+
+ /* ONREPL if replica is added as writable we need to execute protocol that
+ introduces new writable replica to the topology */
+
+ mtnode_ext->replica = object_new (r, replica_destroy); /* Refcnt is 1 */
+
+ /* add replica object to the hash */
+ *returncode = replica_add_by_name (replica_get_name (r), mtnode_ext->replica); /* Increments object refcnt */
+ /* delete the dn from the dn hash - done with configuration */
+ replica_delete_by_dn(replica_root);
+
+done:
+
+ PR_Unlock (s_configLock);
+ /* slapi_ch_free accepts NULL pointer */
+ slapi_ch_free ((void**)&replica_root);
+
+ if (*returncode != LDAP_SUCCESS)
+ {
+ if (mtnode_ext->replica)
+ object_release (mtnode_ext->replica);
+ return SLAPI_DSE_CALLBACK_ERROR;
+ }
+ else
+ return SLAPI_DSE_CALLBACK_OK;
+}
+
+static int
+replica_config_modify (Slapi_PBlock *pb, Slapi_Entry* entryBefore, Slapi_Entry* e,
+ int *returncode, char *returntext, void *arg)
+{
+ int rc= 0;
+ LDAPMod **mods;
+ int i, apply_mods;
+ multimaster_mtnode_extension *mtnode_ext;
+ Replica *r = NULL;
+ char *replica_root = NULL;
+ char buf [BUFSIZ];
+ char *errortext = returntext ? returntext : buf;
+ char *config_attr, *config_attr_value;
+ Slapi_Operation *op;
+ void *identity;
+
+ if (returntext)
+ {
+ returntext[0] = '\0';
+ }
+ *returncode = LDAP_SUCCESS;
+
+ /* just let internal operations originated from replication plugin to go through */
+ slapi_pblock_get (pb, SLAPI_OPERATION, &op);
+ slapi_pblock_get (pb, SLAPI_PLUGIN_IDENTITY, &identity);
+
+ if (operation_is_flag_set(op, OP_FLAG_INTERNAL) &&
+ (identity == repl_get_plugin_identity (PLUGIN_MULTIMASTER_REPLICATION)))
+ {
+ *returncode = LDAP_SUCCESS;
+ return SLAPI_DSE_CALLBACK_OK;
+ }
+
+ replica_root = (char*)slapi_entry_attr_get_charptr (e, attr_replicaRoot);
+
+ PR_Lock (s_configLock);
+
+ mtnode_ext = _replica_config_get_mtnode_ext (e);
+ PR_ASSERT (mtnode_ext);
+
+ if (mtnode_ext->replica)
+ object_acquire (mtnode_ext->replica);
+
+ if (mtnode_ext->replica == NULL)
+ {
+ sprintf (errortext, "replica does not exist for %s", replica_root);
+ slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name, "replica_config_modify: %s\n",
+ errortext);
+ *returncode = LDAP_OPERATIONS_ERROR;
+ goto done;
+ }
+
+ r = object_get_data (mtnode_ext->replica);
+ PR_ASSERT (r);
+
+ slapi_pblock_get(pb, SLAPI_MODIFY_MODS, &mods);
+ for (apply_mods = 0; apply_mods <= 1; apply_mods++)
+ {
+ /* we only allow the replica ID and type to be modified together e.g.
+ if converting a read only replica to a master or vice versa -
+ we will need to change both the replica ID and the type at the same
+ time - we must disallow changing the replica ID if the type is not
+ being changed and vice versa
+ */
+ char *new_repl_id = NULL;
+ char *new_repl_type = NULL;
+
+ if (*returncode != LDAP_SUCCESS)
+ break;
+
+ for (i = 0; (mods[i] && (LDAP_SUCCESS == rc)); i++)
+ {
+ if (*returncode != LDAP_SUCCESS)
+ break;
+
+ config_attr = (char *) mods[i]->mod_type;
+ PR_ASSERT (config_attr);
+
+ /* disallow modifications or removal of replica root,
+ replica name and replica state attributes */
+ if (strcasecmp (config_attr, attr_replicaRoot) == 0 ||
+ strcasecmp (config_attr, attr_replicaName) == 0 ||
+ strcasecmp (config_attr, attr_state) == 0)
+ {
+ *returncode = LDAP_UNWILLING_TO_PERFORM;
+ sprintf (errortext, "modification of %s attribute is not allowed",
+ config_attr);
+ slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name, "replica_config_modify: %s\n",
+ errortext);
+ }
+ /* this is a request to delete an attribute */
+ else if (mods[i]->mod_op & LDAP_MOD_DELETE || mods[i]->mod_bvalues == NULL
+ || mods[i]->mod_bvalues[0]->bv_val == NULL)
+ {
+ /* currently, you can only remove referral,
+ legacy consumer or bind dn attribute */
+ if (strcasecmp (config_attr, attr_replicaBindDn) == 0)
+ {
+ *returncode = replica_config_change_updatedn (r, mods[i], errortext, apply_mods);
+ }
+ else if (strcasecmp (config_attr, attr_replicaReferral) == 0)
+ {
+ if (apply_mods) {
+ replica_set_referrals(r, NULL);
+ if (!replica_is_legacy_consumer (r)) {
+ consumer5_set_mapping_tree_state_for_replica(r, NULL);
+ }
+ }
+ }
+ else if (strcasecmp (config_attr, type_replicaLegacyConsumer) == 0)
+ {
+ if (apply_mods)
+ replica_set_legacy_consumer (r, PR_FALSE);
+ }
+ else
+ {
+ *returncode = LDAP_UNWILLING_TO_PERFORM;
+ sprintf (errortext, "deletion of %s attribute is not allowed", config_attr);
+ slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name, "replica_config_modify: %s\n",
+ errortext);
+ }
+ }
+ else /* modify an attribute */
+ {
+ config_attr_value = (char *) mods[i]->mod_bvalues[0]->bv_val;
+
+ if (strcasecmp (config_attr, attr_replicaBindDn) == 0)
+ {
+ *returncode = replica_config_change_updatedn (r, mods[i],
+ errortext, apply_mods);
+ }
+ else if (strcasecmp (config_attr, attr_replicaType) == 0)
+ {
+ new_repl_type = slapi_ch_strdup(config_attr_value);
+ }
+ else if (strcasecmp (config_attr, attr_replicaId) == 0)
+ {
+ new_repl_id = slapi_ch_strdup(config_attr_value);
+ }
+ else if (strcasecmp (config_attr, attr_flags) == 0)
+ {
+ *returncode = replica_config_change_flags (r, config_attr_value,
+ errortext, apply_mods);
+ }
+ else if (strcasecmp (config_attr, TASK_ATTR) == 0)
+ {
+ *returncode = replica_execute_task (mtnode_ext->replica, config_attr_value,
+ errortext, apply_mods);
+ }
+ else if (strcasecmp (config_attr, attr_replicaReferral) == 0)
+ {
+ if (apply_mods)
+ {
+ Slapi_Mod smod;
+ Slapi_ValueSet *vs= slapi_valueset_new();
+ slapi_mod_init_byref(&smod,mods[i]);
+ slapi_valueset_set_from_smod(vs, &smod);
+ replica_set_referrals (r, vs);
+ slapi_mod_done(&smod);
+ slapi_valueset_free(vs);
+ if (!replica_is_legacy_consumer (r)) {
+ consumer5_set_mapping_tree_state_for_replica(r, NULL);
+ }
+ }
+ }
+ else if (strcasecmp (config_attr, type_replicaPurgeDelay) == 0)
+ {
+ if (apply_mods && config_attr_value && config_attr_value[0])
+ {
+ PRUint32 delay;
+ if (isdigit (config_attr_value[0]))
+ {
+ delay = (unsigned int)atoi(config_attr_value);
+ replica_set_purge_delay(r, delay);
+ }
+ else
+ *returncode = LDAP_OPERATIONS_ERROR;
+ }
+ }
+ else if (strcasecmp (config_attr, type_replicaTombstonePurgeInterval) == 0)
+ {
+ if (apply_mods && config_attr_value && config_attr_value[0])
+ {
+ long interval;
+ interval = atol (config_attr_value);
+ replica_set_tombstone_reap_interval (r, interval);
+ }
+ }
+ else if (strcasecmp (config_attr, type_replicaLegacyConsumer) == 0)
+ {
+ if (apply_mods)
+ {
+ PRBool legacy = (strcasecmp (config_attr_value, "on") == 0) ||
+ (strcasecmp (config_attr_value, "true") == 0) ||
+ (strcasecmp (config_attr_value, "yes") == 0) ||
+ (strcasecmp (config_attr_value, "1") == 0);
+
+ replica_set_legacy_consumer (r, legacy);
+ }
+ }
+ /* ignore modifiers attributes added by the server */
+ else if (strcasecmp (config_attr, "modifytimestamp") == 0 ||
+ strcasecmp (config_attr, "modifiersname") == 0)
+ {
+ *returncode = LDAP_SUCCESS;
+ }
+ else
+ {
+ *returncode = LDAP_UNWILLING_TO_PERFORM;
+ sprintf (errortext, "modification of attribute %s is not allowed in replica entry", config_attr);
+ slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name, "replica_config_modify: %s\n",
+ errortext);
+ }
+ }
+ }
+
+ if (new_repl_id || new_repl_type)
+ {
+ *returncode = replica_config_change_type_and_id(r, new_repl_type,
+ new_repl_id, errortext,
+ apply_mods);
+ slapi_ch_free_string(&new_repl_id);
+ slapi_ch_free_string(&new_repl_type);
+ }
+ }
+
+done:
+ if (mtnode_ext->replica)
+ object_release (mtnode_ext->replica);
+
+ /* slapi_ch_free accepts NULL pointer */
+ slapi_ch_free ((void**)&replica_root);
+
+ PR_Unlock (s_configLock);
+
+ if (*returncode != LDAP_SUCCESS)
+ {
+ return SLAPI_DSE_CALLBACK_ERROR;
+ }
+ else
+ {
+ return SLAPI_DSE_CALLBACK_OK;
+ }
+}
+
+static int
+replica_config_delete (Slapi_PBlock *pb, Slapi_Entry* e, Slapi_Entry* entryAfter,
+ int *returncode, char *returntext, void *arg)
+{
+ multimaster_mtnode_extension *mtnode_ext;
+ Replica *r;
+
+ PR_Lock (s_configLock);
+
+ mtnode_ext = _replica_config_get_mtnode_ext (e);
+ PR_ASSERT (mtnode_ext);
+
+ if (mtnode_ext->replica)
+ {
+ /* remove object from the hash */
+ r = (Replica*)object_get_data (mtnode_ext->replica);
+ PR_ASSERT (r);
+ replica_delete_by_name (replica_get_name (r));
+ object_release (mtnode_ext->replica);
+ mtnode_ext->replica = NULL;
+ }
+
+ PR_Unlock (s_configLock);
+
+ *returncode = LDAP_SUCCESS;
+ return SLAPI_DSE_CALLBACK_OK;
+}
+
+static int
+replica_config_search (Slapi_PBlock *pb, Slapi_Entry* e, Slapi_Entry* entryAfter, int *returncode,
+ char *returntext, void *arg)
+{
+ multimaster_mtnode_extension *mtnode_ext;
+ int changeCount = 0;
+ char val [64];
+
+ /* add attribute that contains number of entries in the changelog for this replica */
+
+ PR_Lock (s_configLock);
+
+ /* if we have no changelog - we have no changes */
+ if (cl5GetState () == CL5_STATE_OPEN)
+ {
+ mtnode_ext = _replica_config_get_mtnode_ext (e);
+ PR_ASSERT (mtnode_ext);
+
+ if (mtnode_ext->replica)
+ {
+ object_acquire (mtnode_ext->replica);
+ changeCount = cl5GetOperationCount (mtnode_ext->replica);
+ object_release (mtnode_ext->replica);
+ }
+ }
+
+ sprintf (val, "%d", changeCount);
+ slapi_entry_add_string (e, type_replicaChangeCount, val);
+
+ PR_Unlock (s_configLock);
+
+ return SLAPI_DSE_CALLBACK_OK;
+}
+
+static int
+replica_config_change_type_and_id (Replica *r, const char *new_type,
+ const char *new_id, char *returntext,
+ int apply_mods)
+{
+ int type;
+ ReplicaType oldtype;
+ ReplicaId rid;
+ ReplicaId oldrid;
+
+ PR_ASSERT (r);
+
+ oldtype = replica_get_type(r);
+ oldrid = replica_get_rid(r);
+ if (new_type == NULL) /* by default - replica is read-only */
+ {
+ type = REPLICA_TYPE_READONLY;
+ }
+ else
+ {
+ type = atoi (new_type);
+ if (type <= REPLICA_TYPE_UNKNOWN || type >= REPLICA_TYPE_END)
+ {
+ sprintf (returntext, "invalid replica type %d", type);
+ return LDAP_OPERATIONS_ERROR;
+ }
+ }
+
+ /* disallow changing type to itself just to permit a replica ID change */
+ if (oldtype == type)
+ {
+ sprintf (returntext, "replica type is already %d - not changing", type);
+ return LDAP_OPERATIONS_ERROR;
+ }
+
+ if (type == REPLICA_TYPE_READONLY)
+ {
+ rid = READ_ONLY_REPLICA_ID; /* default rid for read only */
+ }
+ else if (!new_id)
+ {
+ sprintf(returntext, "a replica ID is required when changing replica type to read-write");
+ return LDAP_UNWILLING_TO_PERFORM;
+ }
+ else
+ {
+ int temprid = atoi (new_id);
+ if (temprid <= 0 || temprid >= READ_ONLY_REPLICA_ID)
+ {
+ sprintf(returntext,
+ "attribute %s must have a value greater than 0 "
+ "and less than %d",
+ attr_replicaId, READ_ONLY_REPLICA_ID);
+ return LDAP_UNWILLING_TO_PERFORM;
+ }
+ else
+ {
+ rid = (ReplicaId)temprid;
+ }
+ }
+
+ /* error if old rid == new rid */
+ if (oldrid == rid)
+ {
+ sprintf (returntext, "replica ID is already %d - not changing", rid);
+ return LDAP_OPERATIONS_ERROR;
+ }
+
+ if (apply_mods)
+ {
+ replica_set_type (r, type);
+ replica_set_rid(r, rid);
+
+ /* Set the mapping tree node, and the list of referrals */
+ /* if this server is a 4.0 consumer the referrals are set by legacy plugin */
+ if (!replica_is_legacy_consumer(r))
+ consumer5_set_mapping_tree_state_for_replica(r, NULL);
+ }
+
+ return LDAP_SUCCESS;
+}
+
+static int
+replica_config_change_updatedn (Replica *r, const LDAPMod *mod, char *returntext,
+ int apply_mods)
+{
+ PR_ASSERT (r);
+
+ if (apply_mods)
+ {
+ Slapi_Mod smod;
+ Slapi_ValueSet *vs= slapi_valueset_new();
+ slapi_mod_init_byref(&smod, (LDAPMod *)mod); /* cast away const */
+ slapi_valueset_set_from_smod(vs, &smod);
+ replica_set_updatedn(r, vs, mod->mod_op);
+ slapi_mod_done(&smod);
+ slapi_valueset_free(vs);
+ }
+
+ return LDAP_SUCCESS;
+}
+
+static int replica_config_change_flags (Replica *r, const char *new_flags,
+ char *returntext, int apply_mods)
+{
+ PR_ASSERT (r);
+
+ if (apply_mods)
+ {
+ PRUint32 flags;
+
+ flags = atol (new_flags);
+
+ replica_replace_flags (r, flags);
+ }
+
+ return LDAP_SUCCESS;
+}
+
+static int replica_execute_task (Object *r, const char *task_name, char *returntext,
+ int apply_mods)
+{
+
+ if (strcasecmp (task_name, CL2LDIF_TASK) == 0)
+ {
+ if (apply_mods)
+ {
+ return replica_execute_cl2ldif_task (r, returntext);
+ }
+ else
+ return LDAP_SUCCESS;
+ }
+ else if (strncasecmp (task_name, CLEANRUV, CLEANRUVLEN) == 0)
+ {
+ int temprid = atoi(&(task_name[CLEANRUVLEN]));
+ if (temprid <= 0 || temprid >= READ_ONLY_REPLICA_ID){
+ sprintf(returntext, "Invalid replica id for task - %s", task_name);
+ slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name,
+ "replica_execute_task: %s\n", returntext);
+ return LDAP_OPERATIONS_ERROR;
+ }
+ if (apply_mods)
+ {
+ return replica_execute_cleanruv_task (r, (ReplicaId)temprid, returntext);
+ }
+ else
+ return LDAP_SUCCESS;
+ }
+ else
+ {
+ sprintf (returntext, "unsupported replica task - %s", task_name);
+ slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name,
+ "replica_execute_task: %s\n", returntext);
+ return LDAP_OPERATIONS_ERROR;
+ }
+
+}
+
+static int replica_execute_cl2ldif_task (Object *r, char *returntext)
+{
+ int rc;
+ Object *rlist [2];
+ Replica *replica;
+ char fName [MAXPATHLEN];
+ char *clDir;
+
+ if (cl5GetState () != CL5_STATE_OPEN)
+ {
+ sprintf (returntext, "changelog is not open");
+ slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name,
+ "replica_execute_cl2ldif_task: %s\n", returntext);
+ return LDAP_OPERATIONS_ERROR;
+ }
+
+ rlist[0] = r;
+ rlist[1] = NULL;
+
+ /* file is stored in the changelog directory and is named
+ <replica name>.ldif */
+ clDir = cl5GetDir ();
+ PR_ASSERT (clDir);
+
+ replica = (Replica*)object_get_data (r);
+ PR_ASSERT (replica);
+
+ sprintf (fName, "%s/%s.ldif", clDir, replica_get_name (replica));
+ slapi_ch_free ((void**)&clDir);
+
+ rc = cl5ExportLDIF (fName, rlist);
+ if (rc != CL5_SUCCESS)
+ {
+ sprintf (returntext, "failed to export changelog data to file %s; "
+ "changelog error - %d", fName, rc);
+ slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name,
+ "replica_execute_cl2ldif_task: %s\n", returntext);
+ return LDAP_OPERATIONS_ERROR;
+ }
+
+ return LDAP_SUCCESS;
+}
+
+static multimaster_mtnode_extension *
+_replica_config_get_mtnode_ext (const Slapi_Entry *e)
+{
+ const char *replica_root;
+ Slapi_DN *sdn = NULL;
+ mapping_tree_node *mtnode;
+ multimaster_mtnode_extension *ext = NULL;
+ char ebuf[BUFSIZ];
+
+ /* retirve root of the tree for which replica is configured */
+ replica_root = slapi_entry_attr_get_charptr (e, attr_replicaRoot);
+ if (replica_root == NULL)
+ {
+ slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name, "replica_config_add: "
+ "configuration entry %s missing %s attribute\n",
+ escape_string(slapi_entry_get_dn((Slapi_Entry *)e), ebuf),
+ attr_replicaRoot);
+ return NULL;
+ }
+
+ sdn = slapi_sdn_new_dn_passin (replica_root);
+
+ /* locate mapping tree node for the specified subtree */
+ mtnode = slapi_get_mapping_tree_node_by_dn (sdn);
+ if (mtnode == NULL)
+ {
+ slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name, "replica_config_add: "
+ "failed to locate mapping tree node for dn %s\n",
+ escape_string(slapi_sdn_get_dn(sdn), ebuf));
+ }
+ else
+ {
+ /* check if replica object already exists for the specified subtree */
+ ext = (multimaster_mtnode_extension *)repl_con_get_ext (REPL_CON_EXT_MTNODE, mtnode);
+ }
+
+ slapi_sdn_free (&sdn);
+
+ return ext;
+}
+
+static int
+replica_execute_cleanruv_task (Object *r, ReplicaId rid, char *returntext)
+{
+ int rc = 0;
+ Object *RUVObj;
+ RUV *local_ruv = NULL;
+ Replica *replica = (Replica*)object_get_data (r);
+
+ PR_ASSERT (replica);
+
+ RUVObj = replica_get_ruv(replica);
+ PR_ASSERT(RUVObj);
+ local_ruv = (RUV*)object_get_data (RUVObj);
+ /* Need to check that :
+ * - rid is not the local one
+ * - rid is not the last one
+ */
+ if ((replica_get_rid(replica) == rid) ||
+ (ruv_replica_count(local_ruv) <= 1)) {
+ return LDAP_UNWILLING_TO_PERFORM;
+ }
+ rc = ruv_delete_replica(local_ruv, rid);
+ replica_set_ruv_dirty(replica);
+ replica_write_ruv(replica);
+ object_release(RUVObj);
+
+ /* Update Mapping Tree to reflect RUV changes */
+ consumer5_set_mapping_tree_state_for_replica(replica, NULL);
+
+ if (rc != RUV_SUCCESS){
+ return LDAP_OPERATIONS_ERROR;
+ }
+ return LDAP_SUCCESS;
+}
+
+
diff --git a/ldap/servers/plugins/replication/repl5_replica_dnhash.c b/ldap/servers/plugins/replication/repl5_replica_dnhash.c
new file mode 100644
index 00000000..4b9f42b9
--- /dev/null
+++ b/ldap/servers/plugins/replication/repl5_replica_dnhash.c
@@ -0,0 +1,189 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+/* repl5_replica_dnhash.c */
+
+#include "repl5.h"
+#include "plhash.h"
+
+/* global data */
+static PLHashTable *s_hash;
+static PRRWLock *s_lock;
+
+/* Forward declarations */
+static PRIntn replica_destroy_hash_entry (PLHashEntry *he, PRIntn index, void *arg);
+
+int replica_init_dn_hash ()
+{
+ /* allocate table */
+ s_hash = PL_NewHashTable(0, PL_HashString, PL_CompareStrings,
+ PL_CompareValues, NULL, NULL);
+ if (s_hash == NULL)
+ {
+ slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name, "replica_init_dn_hash: "
+ "failed to allocate hash table; NSPR error - %d\n",
+ PR_GetError ());
+ return -1;
+ }
+
+ /* create lock */
+ s_lock = PR_NewRWLock(PR_RWLOCK_RANK_NONE, "replica_dnhash_lock");
+ if (s_lock == NULL)
+ {
+ slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name, "replica_init_dn_hash: "
+ "failed to create lock; NSPR error - %d\n",
+ PR_GetError ());
+ replica_destroy_dn_hash ();
+ return -1;
+ }
+
+ return 0;
+}
+
+void replica_destroy_dn_hash ()
+{
+ /* destroy the content */
+ PL_HashTableEnumerateEntries(s_hash, replica_destroy_hash_entry, NULL);
+
+ if (s_hash)
+ PL_HashTableDestroy(s_hash);
+
+ if (s_lock)
+ PR_DestroyRWLock (s_lock);
+}
+
+int replica_add_by_dn (const char *dn)
+{
+ char *dn_copy = NULL;
+
+ if (dn == NULL)
+ {
+ slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name, "replica_add_by_dn: NULL argument\n");
+ return -1;
+ }
+
+ if (s_hash == NULL || s_lock == NULL)
+ {
+ slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name, "replica_add_by_dn: "
+ "replica hash is not initialized\n");
+ return -1;
+ }
+
+ PR_RWLock_Wlock (s_lock);
+
+ /* make sure that the dn is unique */
+ if (PL_HashTableLookup(s_hash, dn) != NULL)
+ {
+ slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name, "replica_add_by_dn: "
+ "replica with dn (%s) already in the hash\n", dn);
+ PR_RWLock_Unlock (s_lock);
+ return -1 ;
+ }
+
+ /* add dn */
+ dn_copy = slapi_ch_strdup(dn);
+ if (PL_HashTableAdd(s_hash, dn_copy, dn_copy) == NULL)
+ {
+ slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name, "replica_add_by_dn: "
+ "failed to add dn (%s); NSPR error - %d\n",
+ dn_copy, PR_GetError ());
+ slapi_ch_free((void **)&dn_copy);
+ PR_RWLock_Unlock (s_lock);
+ return -1;
+ }
+
+ slapi_log_error(SLAPI_LOG_REPL, repl_plugin_name, "replica_add_by_dn: "
+ "added dn (%s)\n",
+ dn_copy);
+ PR_RWLock_Unlock (s_lock);
+ return 0;
+}
+
+int replica_delete_by_dn (const char *dn)
+{
+ char *dn_copy = NULL;
+
+ if (dn == NULL)
+ {
+ slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name, "replica_delete_by_dn: "
+ "NULL argument\n");
+ return -1;
+ }
+
+ if (s_hash == NULL || s_lock == NULL)
+ {
+ slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name, "replica_delete_by_dn: "
+ "replica hash is not initialized\n");
+ return -1;
+ }
+
+ PR_RWLock_Wlock (s_lock);
+
+ /* locate object */
+ if (NULL == (dn_copy = (char *)PL_HashTableLookup(s_hash, dn)))
+ {
+ slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name, "replica_delete_by_dn: "
+ "dn (%s) is not in the hash.\n", dn);
+ PR_RWLock_Unlock (s_lock);
+ return -1;
+ }
+
+ /* remove from hash */
+ PL_HashTableRemove(s_hash, dn);
+ slapi_ch_free((void **)&dn_copy);
+
+ slapi_log_error(SLAPI_LOG_REPL, repl_plugin_name, "replica_delete_by_dn: "
+ "removed dn (%s)\n",
+ dn);
+ PR_RWLock_Unlock (s_lock);
+
+ return 0;
+}
+
+int replica_is_being_configured (const char *dn)
+{
+ if (dn == NULL)
+ {
+ slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name, "replica_is_dn_in_hash: "
+ "NULL argument\n");
+ return 0;
+ }
+
+ if (s_hash == NULL || s_lock == NULL)
+ {
+ slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name, "replica_is_dn_in_hash: "
+ "dn hash is not initialized\n");
+ return 0;
+ }
+
+ PR_RWLock_Wlock (s_lock);
+
+ /* locate object */
+ if (NULL == PL_HashTableLookup(s_hash, dn))
+ {
+ PR_RWLock_Unlock (s_lock);
+ return 0;
+ }
+
+ PR_RWLock_Unlock (s_lock);
+
+ return 1;
+}
+
+/* Helper functions */
+
+/* this function called for each hash node during hash destruction */
+static PRIntn replica_destroy_hash_entry (PLHashEntry *he, PRIntn index, void *arg)
+{
+ char *dn_copy;
+
+ if (he == NULL)
+ return HT_ENUMERATE_NEXT;
+
+ dn_copy = (char*)he->value;
+ slapi_ch_free((void **)&dn_copy);
+
+ return HT_ENUMERATE_REMOVE;
+}
diff --git a/ldap/servers/plugins/replication/repl5_replica_hash.c b/ldap/servers/plugins/replication/repl5_replica_hash.c
new file mode 100644
index 00000000..92ea87f4
--- /dev/null
+++ b/ldap/servers/plugins/replication/repl5_replica_hash.c
@@ -0,0 +1,243 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+
+/* repl5_replica_hash.c */
+
+#include "repl5.h"
+#include "plhash.h"
+
+/* global data */
+static PLHashTable *s_hash;
+static PRRWLock *s_lock;
+
+struct repl_enum_data
+{
+ FNEnumReplica fn;
+ void *arg;
+};
+
+/* Forward declarations */
+static PRIntn replica_destroy_hash_entry (PLHashEntry *he, PRIntn index, void *arg);
+static PRIntn replica_enumerate (PLHashEntry *he, PRIntn index, void *hash_data);
+
+
+int replica_init_name_hash ()
+{
+ /* allocate table */
+ s_hash = PL_NewHashTable(0, PL_HashString, PL_CompareStrings,
+ PL_CompareValues, NULL, NULL);
+ if (s_hash == NULL)
+ {
+ slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name, "replica_init_name_hash: "
+ "failed to allocate hash table; NSPR error - %d\n",
+ PR_GetError ());
+ return -1;
+ }
+
+ /* create lock */
+ s_lock = PR_NewRWLock(PR_RWLOCK_RANK_NONE, "replica_hash_lock");
+ if (s_lock == NULL)
+ {
+ slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name, "replica_init_name_hash: "
+ "failed to create lock; NSPR error - %d\n",
+ PR_GetError ());
+ replica_destroy_name_hash ();
+ return -1;
+ }
+
+ return 0;
+}
+
+void replica_destroy_name_hash ()
+{
+ /* destroy the content */
+ PL_HashTableEnumerateEntries(s_hash, replica_destroy_hash_entry, NULL);
+
+ if (s_hash)
+ PL_HashTableDestroy(s_hash);
+
+ if (s_lock)
+ PR_DestroyRWLock (s_lock);
+}
+
+int replica_add_by_name (const char *name, Object *replica)
+{
+ if (name == NULL || replica == NULL)
+ {
+ slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name, "replica_add_by_name: NULL argument\n");
+ return -1;
+ }
+
+ if (s_hash == NULL || s_lock == NULL)
+ {
+ slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name, "replica_add_by_name: "
+ "replica hash is not initialized\n");
+ return -1;
+ }
+
+ PR_RWLock_Wlock (s_lock);
+
+ /* make sure that the name is unique */
+ if (PL_HashTableLookup(s_hash, name) != NULL)
+ {
+ slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name, "replica_add_by_name: "
+ "replica with name (%s) already in the hash\n", name);
+ PR_RWLock_Unlock (s_lock);
+ return -1 ;
+ }
+
+ /* acquire replica object */
+ object_acquire (replica);
+
+ /* add replica */
+ if (PL_HashTableAdd(s_hash, name, replica) == NULL)
+ {
+ slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name, "replica_add_by_name: "
+ "failed to add replica with name (%s); NSPR error - %d\n",
+ name, PR_GetError ());
+ object_release (replica);
+ PR_RWLock_Unlock (s_lock);
+ return -1;
+ }
+
+ PR_RWLock_Unlock (s_lock);
+ return 0;
+}
+
+int replica_delete_by_name (const char *name)
+{
+ Object *replica;
+
+ if (name == NULL)
+ {
+ slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name, "replica_delete_by_name: "
+ "NULL argument\n");
+ return -1;
+ }
+
+ if (s_hash == NULL || s_lock == NULL)
+ {
+ slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name, "replica_delete_by_name: "
+ "replica hash is not initialized\n");
+ return -1;
+ }
+
+ PR_RWLock_Wlock (s_lock);
+
+ /* locate object */
+ replica = (Object*)PL_HashTableLookup(s_hash, name);
+ if (replica == NULL)
+ {
+ slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name, "replica_delete_by_name: "
+ "replica with name (%s) is not in the hash.\n", name);
+ PR_RWLock_Unlock (s_lock);
+ return -1;
+ }
+
+ /* remove from hash */
+ PL_HashTableRemove(s_hash, name);
+
+ /* release replica */
+ object_release (replica);
+
+ PR_RWLock_Unlock (s_lock);
+
+ return 0;
+}
+
+Object* replica_get_by_name (const char *name)
+{
+ Object *replica;
+
+ if (name == NULL)
+ {
+ slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name, "replica_get_by_name: "
+ "NULL argument\n");
+ return NULL;
+ }
+
+ if (s_hash == NULL || s_lock == NULL)
+ {
+ slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name, "replica_get_by_name: "
+ "replica hash is not initialized\n");
+ return NULL;
+ }
+
+ PR_RWLock_Rlock (s_lock);
+
+ /* locate object */
+ replica = (Object*)PL_HashTableLookup(s_hash, name);
+ if (replica == NULL)
+ {
+ PR_RWLock_Unlock (s_lock);
+ return NULL;
+ }
+
+ object_acquire (replica);
+
+ PR_RWLock_Unlock (s_lock);
+
+ return replica;
+}
+
+void replica_enumerate_replicas (FNEnumReplica fn, void *arg)
+{
+ struct repl_enum_data data;
+
+ PR_ASSERT (fn);
+
+ data.fn = fn;
+ data.arg = arg;
+
+ PR_RWLock_Wlock (s_lock);
+ PL_HashTableEnumerateEntries(s_hash, replica_enumerate, &data);
+ PR_RWLock_Unlock (s_lock);
+}
+
+/* Helper functions */
+
+/* this function called for each hash node during hash destruction */
+static PRIntn replica_destroy_hash_entry (PLHashEntry *he, PRIntn index, void *arg)
+{
+ Object *r_obj;
+ Replica *r;
+
+ if (he == NULL)
+ return HT_ENUMERATE_NEXT;
+
+ r_obj = (Object*)he->value;
+ r = (Replica*)object_get_data (r_obj);
+ PR_ASSERT (r);
+
+ /* flash replica state to the disk */
+ replica_flush (r);
+
+ /* release replica object */
+ object_release (r_obj);
+
+ return HT_ENUMERATE_REMOVE;
+}
+
+static PRIntn replica_enumerate (PLHashEntry *he, PRIntn index, void *hash_data)
+{
+ Object *r_obj;
+ Replica *r;
+ struct repl_enum_data *data = hash_data;
+
+ r_obj = (Object*)he->value;
+ PR_ASSERT (r_obj);
+
+ object_acquire (r_obj);
+ r = (Replica*)object_get_data (r_obj);
+ PR_ASSERT (r);
+
+ data->fn (r, data->arg);
+
+ object_release (r_obj);
+
+ return HT_ENUMERATE_NEXT;
+}
+
diff --git a/ldap/servers/plugins/replication/repl5_replsupplier.c b/ldap/servers/plugins/replication/repl5_replsupplier.c
new file mode 100644
index 00000000..e98d0e58
--- /dev/null
+++ b/ldap/servers/plugins/replication/repl5_replsupplier.c
@@ -0,0 +1,166 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+/* repl5_replsupplier.c */
+/*
+
+A replsupplier is an object that knows how to manage outbound replication
+for one consumer.
+
+Methods:
+init()
+configure()
+start()
+stop()
+destroy()
+status()
+notify()
+
+*/
+
+#include "slapi-plugin.h"
+#include "repl5.h"
+
+typedef struct repl_supplier {
+ PRUint32 client_change_count; /* # of client-supplied changes */
+ PRUint32 repl_change_count; /* # of replication updates */
+
+ PRLock *lock;
+
+} repl_supplier;
+
+
+static void repl_supplier_free(Repl_Supplier **rsp);
+
+/*
+ * Create and initialize this replsupplier object.
+ */
+Repl_Supplier *
+replsupplier_init(Slapi_Entry *e)
+{
+ Repl_Supplier *rs;
+
+ if ((rs = (Repl_Supplier *)slapi_ch_malloc(sizeof(Repl_Supplier))) == NULL)
+ {
+ goto loser;
+ }
+ if ((rs->lock = PR_NewLock()) == NULL)
+ {
+ goto loser;
+ }
+ return rs;
+
+loser:
+ repl_supplier_free(&rs);
+ return NULL;
+}
+
+
+
+static void
+repl_supplier_free(Repl_Supplier **rsp)
+{
+ if (NULL != rsp)
+ {
+ Repl_Supplier *rs = *rsp;
+ if (NULL != rs)
+ {
+ if (NULL != rs->lock)
+ {
+ PR_DestroyLock(rs->lock);
+ rs->lock = NULL;
+ }
+ slapi_ch_free((void **)rsp);
+ }
+ }
+}
+
+
+
+/*
+ * Configure a repl_supplier object.
+ */
+void
+replsupplier_configure(Repl_Supplier *rs, Slapi_PBlock *pb)
+{
+ PR_ASSERT(NULL != rs);
+
+}
+
+
+
+/*
+ * Start a repl_supplier object. This means that it's ok for
+ * the repl_supplier to begin normal replication duties. It does
+ * not necessarily mean that a replication session will occur
+ * immediately.
+ */
+void
+replsupplier_start(Repl_Supplier *rs)
+{
+ PR_ASSERT(NULL != rs);
+}
+
+
+
+
+/*
+ * Stop a repl_supplier object. This causes any active replication
+ * sessions to be stopped ASAP, and puts the repl_supplier into a
+ * stopped state. No additional replication activity will occur
+ * until the replsupplier_start() function is called.
+ */
+void
+replsupplier_stop(Repl_Supplier *rs)
+{
+ PR_ASSERT(NULL != rs);
+}
+
+
+
+
+/*
+ * Destroy a repl_supplier object. The object will be stopped, if it
+ * is not already stopped.
+ */
+void
+replsupplier_destroy(Repl_Supplier **rsp)
+{
+ Repl_Supplier *rs;
+
+ PR_ASSERT(NULL != rsp && NULL != *rsp);
+
+ rs = *rsp;
+
+ slapi_ch_free((void **)rsp);
+}
+
+
+
+/*
+ * This method should be called by the repl_bos whenever it determines
+ * that a change to the replicated area serviced by this repl_supplier
+ * has occurred. This gives the repl_supplier a chance to implement a
+ * scheduling policy.
+ */
+void
+replsupplier_notify(Repl_Supplier *rs, PRUint32 eventmask)
+{
+ PR_ASSERT(NULL != rs);
+}
+
+
+
+/*
+ * This method is used to obtain the status of this particular
+ * repl_supplier object. Eventually it will return some object containing
+ * status information. For now, it's just a placeholder function.
+ */
+PRUint32
+replsupplier_get_status(Repl_Supplier *rs)
+{
+ PR_ASSERT(NULL != rs);
+ return 0;
+}
diff --git a/ldap/servers/plugins/replication/repl5_ruv.c b/ldap/servers/plugins/replication/repl5_ruv.c
new file mode 100644
index 00000000..b8cb79c9
--- /dev/null
+++ b/ldap/servers/plugins/replication/repl5_ruv.c
@@ -0,0 +1,2022 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+
+/* repl5_ruv.c - implementation of replica update vector */
+/*
+ * The replica update vector is stored in the nsds50ruv attribute. The LDIF
+ * representation of the ruv is:
+ * nsds50ruv: {replicageneration} <gen-id-for-this-replica>
+ * nsds50ruv: {replica <rid>[ <url>]}[ <mincsn> <maxcsn>]
+ * nsds50ruv: {replica <rid>[ <url>]}[ <mincsn> <maxcsn>]
+ * ...
+ * nsds50ruv: {replica <rid>[ <url>]}[ <mincsn> <maxcsn>]
+ *
+ * nsruvReplicaLastModified: {replica <rid>[ <url>]} lastModifiedTime
+ * nsruvReplicaLastModified: {replica <rid>[ <url>]} lastModifiedTime
+ * ...
+ *
+ * For readability, ruv_dump appends nsruvReplicaLastModified to nsds50ruv:
+ * nsds50ruv: {replica <rid>[ <url>]}[ <mincsn> <maxcsn> [<lastModifiedTime>]]
+ */
+
+#include <string.h>
+#include <ctype.h> /* For isdigit() */
+#include "csnpl.h"
+#include "repl5_ruv.h"
+#include "repl_shared.h"
+#include "repl5.h"
+
+#define RIDSTR_SIZE 16 /* string large enough to hold replicaid*/
+#define RUVSTR_SIZE 256 /* string large enough to hold ruv and lastmodifiedtime */
+
+/* Data Structures */
+
+/* replica */
+typedef struct ruvElement
+{
+ ReplicaId rid; /* replica id for this element */
+ CSN *csn; /* largest csn that we know about that originated at the master */
+ CSN *min_csn; /* smallest csn that originated at the master */
+ char *replica_purl; /* Partial URL for replica */
+ CSNPL *csnpl; /* list of operations in progress */
+ time_t last_modified; /* timestamp the modification of csn */
+} RUVElement;
+
+/* replica update vector */
+struct _ruv
+{
+ char *replGen; /* replicated area generation: identifies replica
+ in space and in time */
+ DataList *elements; /* replicas */
+ PRRWLock *lock; /* concurrency control */
+};
+
+/* forward declarations */
+static int ruvInit (RUV **ruv, int initCount);
+static void ruvFreeReplica (void **data);
+static RUVElement* ruvGetReplica (const RUV *ruv, ReplicaId rid);
+static RUVElement* ruvAddReplica (RUV *ruv, const CSN *csn, const char *replica_purl);
+static RUVElement* ruvAddReplicaNoCSN (RUV *ruv, ReplicaId rid, const char *replica_purl);
+static RUVElement* ruvAddIndexReplicaNoCSN (RUV *ruv, ReplicaId rid, const char *replica_purl, int index);
+static int ruvReplicaCompare (const void *el1, const void *el2);
+static RUVElement *get_ruvelement_from_berval(const struct berval *bval);
+static char *get_replgen_from_berval(const struct berval *bval);
+
+static const char * const prefix_replicageneration = "{replicageneration}";
+static const char * const prefix_ruvcsn = "{replica "; /* intentionally missing '}' */
+
+
+/* API implementation */
+
+
+/*
+ * Allocate a new ruv and set its replica generation to the given generation.
+ */
+int
+ruv_init_new(const char *replGen, ReplicaId rid, const char *purl, RUV **ruv)
+{
+ int rc;
+ RUVElement *replica;
+
+ if (ruv == NULL || replGen == NULL)
+ {
+ slapi_log_error(SLAPI_LOG_REPL, repl_plugin_name, "ruv_init_new: NULL argument\n");
+ return RUV_BAD_DATA;
+ }
+
+ rc = ruvInit (ruv, 0);
+ if (rc != RUV_SUCCESS)
+ return rc;
+
+ (*ruv)->replGen = slapi_ch_strdup (replGen);
+
+ /* we want to add the local writable replica to the RUV before any csns are created */
+ /* this is so that it can be referred to even before it accepted any changes */
+ if (purl)
+ {
+ replica = ruvAddReplicaNoCSN (*ruv, rid, purl);
+
+ if (replica == NULL)
+ return RUV_MEMORY_ERROR;
+ }
+
+ return RUV_SUCCESS;
+}
+
+
+/*
+ * Create a new RUV and initialize its contents from the provided Slapi_Attr.
+ * Returns:
+ * RUV_BAD_DATA if the values in the attribute were malformed.
+ * RUV_SUCCESS if all went well
+ */
+int
+ruv_init_from_slapi_attr(Slapi_Attr *attr, RUV **ruv)
+{
+ ReplicaId dummy = 0;
+
+ return (ruv_init_from_slapi_attr_and_check_purl(attr, ruv, &dummy));
+}
+
+/*
+ * Create a new RUV and initialize its contents from the provided Slapi_Attr.
+ * Returns:
+ * RUV_BAD_DATA if the values in the attribute were malformed.
+ * RUV_SUCCESS if all went well
+ * contain_purl is 0 if the ruv doesn't contain the local purl
+ * contain_purl is != 0 if the ruv contains the local purl (contains the rid)
+ */
+int
+ruv_init_from_slapi_attr_and_check_purl(Slapi_Attr *attr, RUV **ruv, ReplicaId *contain_purl)
+{
+ int return_value;
+
+ PR_ASSERT(NULL != attr && NULL != ruv);
+
+ if (NULL == ruv || NULL == attr)
+ {
+ slapi_log_error(SLAPI_LOG_REPL, repl_plugin_name,
+ "ruv_init_from_slapi_attr: NULL argument\n");
+ return_value = RUV_BAD_DATA;
+ }
+ else
+ {
+ int rc;
+ int numvalues;
+ slapi_attr_get_numvalues(attr, &numvalues);
+ if ((rc = ruvInit(ruv, numvalues)) != RUV_SUCCESS)
+ {
+ return_value = rc;
+ }
+ else
+ {
+ int hint;
+ Slapi_Value *value;
+ const struct berval *bval;
+ const char *purl = NULL;
+
+ return_value = RUV_SUCCESS;
+
+ purl = multimaster_get_local_purl();
+ *contain_purl = 0;
+
+ for (hint = slapi_attr_first_value(attr, &value);
+ hint != -1; hint = slapi_attr_next_value(attr, hint, &value))
+ {
+ bval = slapi_value_get_berval(value);
+ if (NULL != bval && NULL != bval->bv_val)
+ {
+ if (strncmp(bval->bv_val, prefix_replicageneration, strlen(prefix_replicageneration)) == 0) {
+ if (NULL == (*ruv)->replGen)
+ {
+ (*ruv)->replGen = get_replgen_from_berval(bval);
+ } else {
+ /* Twice replicageneration is wrong, just log and ignore */
+ slapi_log_error(SLAPI_LOG_REPL, repl_plugin_name,
+ "ruv_init_from_slapi_attr: %s is present more than once\n",
+ prefix_replicageneration);
+ }
+ }
+ else
+ {
+ RUVElement *ruve = get_ruvelement_from_berval(bval);
+ if (NULL != ruve)
+ {
+ /* Is the local purl already in the ruv ? */
+ if ( (*contain_purl==0) && (strncmp(ruve->replica_purl, purl, strlen(purl))==0) )
+ {
+ *contain_purl = ruve->rid;
+ }
+ dl_add ((*ruv)->elements, ruve);
+ }
+ }
+ }
+ }
+ }
+ }
+ return return_value;
+}
+
+
+
+/*
+ * Same as ruv_init_from_slapi_attr, but takes an array of pointers to bervals.
+ * I wish this wasn't a cut-n-paste of the above function, but I don't see a
+ * clean way to define one API in terms of the other.
+ */
+int
+ruv_init_from_bervals(struct berval **vals, RUV **ruv)
+{
+ int return_value;
+
+ PR_ASSERT(NULL != vals && NULL != ruv);
+
+ if (NULL == ruv || NULL == vals)
+ {
+ slapi_log_error(SLAPI_LOG_REPL, repl_plugin_name,
+ "ruv_init_from_slapi_value: NULL argument\n");
+ return_value = RUV_BAD_DATA;
+ }
+ else
+ {
+ int i, rc;
+ i = 0;
+ while (vals[i] != NULL)
+ {
+ i++;
+ }
+ if ((rc = ruvInit (ruv, i)) != RUV_SUCCESS)
+ {
+ return_value = rc;
+ }
+ else
+ {
+ return_value = RUV_SUCCESS;
+ for (i = 0; NULL != vals[i]; i++)
+ {
+ if (NULL != vals[i]->bv_val)
+ {
+ if (strncmp(vals[i]->bv_val, prefix_replicageneration, strlen(prefix_replicageneration)) == 0) {
+ if (NULL == (*ruv)->replGen)
+ {
+ (*ruv)->replGen = get_replgen_from_berval(vals[i]);
+ } else {
+ /* Twice replicageneration is wrong, just log and ignore */
+ slapi_log_error(SLAPI_LOG_REPL, repl_plugin_name,
+ "ruv_init_from_slapi_value: %s is present more than once\n",
+ prefix_replicageneration);
+ }
+ }
+ else
+ {
+ RUVElement *ruve = get_ruvelement_from_berval(vals[i]);
+ if (NULL != ruve)
+ {
+ dl_add ((*ruv)->elements, ruve);
+ }
+ }
+ }
+ }
+ }
+ }
+ return return_value;
+}
+
+
+
+RUV*
+ruv_dup (const RUV *ruv)
+{
+ int rc;
+ RUVElement *replica, *dupReplica;
+ int cookie;
+ RUV *dupRUV = NULL;
+
+ if (ruv == NULL)
+ return NULL;
+
+ PR_RWLock_Rlock (ruv->lock);
+
+ rc = ruvInit (&dupRUV, dl_get_count (ruv->elements));
+ if (rc != RUV_SUCCESS || dupRUV == NULL)
+ goto done;
+
+ dupRUV->replGen = slapi_ch_strdup (ruv->replGen);
+
+ for (replica = dl_get_first (ruv->elements, &cookie); replica;
+ replica = dl_get_next (ruv->elements, &cookie))
+ {
+ dupReplica = (RUVElement *)slapi_ch_calloc (1, sizeof (*dupReplica));
+ dupReplica->rid = replica->rid;
+ if (replica->csn)
+ dupReplica->csn = csn_dup (replica->csn);
+ if (replica->min_csn)
+ dupReplica->min_csn = csn_dup (replica->min_csn);
+ if (replica->replica_purl)
+ dupReplica->replica_purl = slapi_ch_strdup (replica->replica_purl);
+ dupReplica->last_modified = replica->last_modified;
+
+ /* ONREPL - we don't make copy of the pernding list. For now
+ we don't need it. */
+
+ dl_add (dupRUV->elements, dupReplica);
+ }
+
+done:
+ PR_RWLock_Unlock (ruv->lock);
+
+ return dupRUV;
+}
+
+void
+ruv_destroy (RUV **ruv)
+{
+ if (ruv != NULL && *ruv != NULL)
+ {
+ if ((*ruv)->elements)
+ {
+ dl_cleanup ((*ruv)->elements, ruvFreeReplica);
+ dl_free (&(*ruv)->elements);
+ }
+ /* slapi_ch_free accepts NULL pointer */
+ slapi_ch_free ((void **)&((*ruv)->replGen));
+
+ if ((*ruv)->lock)
+ {
+ PR_DestroyRWLock ((*ruv)->lock);
+ }
+
+ slapi_ch_free ((void**)ruv);
+ }
+}
+
+/*
+ * [610948]
+ * copy elements in srcruv to destruv
+ * destruv is the live wrapper, which could be referred by other threads.
+ * srcruv is cleaned up after copied.
+ */
+void
+ruv_copy_and_destroy (RUV **srcruv, RUV **destruv)
+{
+ DataList *elemp = NULL;
+ char *replgp = NULL;
+
+ if (NULL == srcruv || NULL == *srcruv || NULL == destruv)
+ {
+ return;
+ }
+
+ if (NULL == *destruv)
+ {
+ *destruv = *srcruv;
+ *srcruv = NULL;
+ }
+ else
+ {
+ PR_RWLock_Wlock((*destruv)->lock);
+ elemp = (*destruv)->elements;
+ (*destruv)->elements = (*srcruv)->elements;
+ if (elemp)
+ {
+ dl_cleanup (elemp, ruvFreeReplica);
+ dl_free (&elemp);
+ }
+
+ /* slapi_ch_free accepts NULL pointer */
+ replgp = (*destruv)->replGen;
+ (*destruv)->replGen = (*srcruv)->replGen;
+ slapi_ch_free ((void **)&replgp);
+
+ if ((*srcruv)->lock)
+ {
+ PR_DestroyRWLock ((*srcruv)->lock);
+ }
+ slapi_ch_free ((void**)srcruv);
+
+ PR_RWLock_Unlock((*destruv)->lock);
+ }
+ PR_ASSERT (*destruv != NULL && *srcruv == NULL);
+}
+
+int
+ruv_delete_replica (RUV *ruv, ReplicaId rid)
+{
+ int return_value;
+ if (ruv == NULL)
+ {
+ slapi_log_error(SLAPI_LOG_REPL, repl_plugin_name, "ruv_delete_replica: NULL argument\n");
+ return_value = RUV_BAD_DATA;
+ }
+ else
+ {
+ /* check for duplicates */
+ PR_RWLock_Wlock (ruv->lock);
+ dl_delete (ruv->elements, (const void*)&rid, ruvReplicaCompare, ruvFreeReplica);
+ PR_RWLock_Unlock (ruv->lock);
+ return_value = RUV_SUCCESS;
+ }
+ return return_value;
+}
+
+int
+ruv_add_replica (RUV *ruv, ReplicaId rid, const char *replica_purl)
+{
+ RUVElement* replica;
+
+ PR_ASSERT (ruv && replica_purl);
+
+ PR_RWLock_Wlock (ruv->lock);
+ replica = ruvGetReplica (ruv, rid);
+ if (replica == NULL)
+ {
+ replica = ruvAddReplicaNoCSN (ruv, rid, replica_purl);
+ }
+
+ PR_RWLock_Unlock (ruv->lock);
+
+ if (replica)
+ return RUV_SUCCESS;
+ else
+ return RUV_MEMORY_ERROR;
+}
+
+int
+ruv_replace_replica_purl (RUV *ruv, ReplicaId rid, const char *replica_purl)
+{
+ RUVElement* replica;
+ int rc = RUV_NOTFOUND;
+
+ PR_ASSERT (ruv && replica_purl);
+
+ PR_RWLock_Wlock (ruv->lock);
+ replica = ruvGetReplica (ruv, rid);
+ if (replica != NULL)
+ {
+ slapi_ch_free((void **)&(replica->replica_purl));
+ replica->replica_purl = slapi_ch_strdup(replica_purl);
+ rc = RUV_SUCCESS;
+ }
+
+ PR_RWLock_Unlock (ruv->lock);
+ return rc;
+}
+
+int
+ruv_add_index_replica (RUV *ruv, ReplicaId rid, const char *replica_purl, int index)
+{
+ RUVElement* replica;
+
+ PR_ASSERT (ruv && replica_purl);
+
+ PR_RWLock_Wlock (ruv->lock);
+ replica = ruvGetReplica (ruv, rid);
+ if (replica == NULL)
+ {
+ replica = ruvAddIndexReplicaNoCSN (ruv, rid, replica_purl, index);
+ }
+
+ PR_RWLock_Unlock (ruv->lock);
+
+ if (replica)
+ return RUV_SUCCESS;
+ else
+ return RUV_MEMORY_ERROR;
+}
+
+
+PRBool
+ruv_contains_replica (const RUV *ruv, ReplicaId rid)
+{
+ RUVElement *replica;
+
+ if (ruv == NULL)
+ return PR_FALSE;
+
+ PR_RWLock_Rlock (ruv->lock);
+ replica = ruvGetReplica (ruv, rid);
+ PR_RWLock_Unlock (ruv->lock);
+
+ return replica != NULL;
+}
+
+
+
+
+#define GET_LARGEST_CSN 231
+#define GET_SMALLEST_CSN 232
+static int
+get_csn_internal(const RUV *ruv, ReplicaId rid, CSN **csn, int whichone)
+{
+ RUVElement *replica;
+ int return_value = RUV_SUCCESS;
+
+ if (ruv == NULL || csn == NULL)
+ {
+ slapi_log_error(SLAPI_LOG_REPL, repl_plugin_name, "ruv_get_largest_csn_for_replica: NULL argument\n");
+ return_value = RUV_BAD_DATA;
+ }
+ else
+ {
+ *csn = NULL;
+ /* prevent element from being destroyed while we get its data */
+ PR_RWLock_Rlock (ruv->lock);
+
+ replica = ruvGetReplica (ruv, rid);
+ /* replica without min csn is treated as a non-existent replica */
+ if (replica == NULL || replica->min_csn == NULL)
+ {
+ return_value = RUV_NOTFOUND;
+ }
+ else
+ {
+ switch (whichone)
+ {
+ case GET_LARGEST_CSN:
+ *csn = replica->csn ? csn_dup (replica->csn) : NULL;
+ break;
+ case GET_SMALLEST_CSN:
+ *csn = replica->min_csn ? csn_dup (replica->min_csn) : NULL;
+ break;
+ default:
+ *csn = NULL;
+ }
+ }
+ PR_RWLock_Unlock (ruv->lock);
+ }
+ return return_value;
+}
+
+
+int
+ruv_get_largest_csn_for_replica(const RUV *ruv, ReplicaId rid, CSN **csn)
+{
+ return get_csn_internal(ruv, rid, csn, GET_LARGEST_CSN);
+}
+
+int
+ruv_get_smallest_csn_for_replica(const RUV *ruv, ReplicaId rid, CSN **csn)
+{
+ return get_csn_internal(ruv, rid, csn, GET_SMALLEST_CSN);
+}
+
+const char *
+ruv_get_purl_for_replica(const RUV *ruv, ReplicaId rid)
+{
+ RUVElement *replica;
+ const char *return_value = NULL;
+
+ PR_RWLock_Rlock (ruv->lock);
+
+ replica = ruvGetReplica (ruv, rid);
+ if (replica != NULL)
+ {
+ return_value = replica->replica_purl;
+ }
+
+ PR_RWLock_Unlock (ruv->lock);
+
+ return return_value;
+}
+
+
+static int
+set_min_csn_nolock(RUV *ruv, const CSN *min_csn, const char *replica_purl)
+{
+ int return_value;
+ ReplicaId rid = csn_get_replicaid (min_csn);
+ RUVElement *replica = ruvGetReplica (ruv, rid);
+ if (NULL == replica)
+ {
+ replica = ruvAddReplica (ruv, min_csn, replica_purl);
+ if (replica)
+ return_value = RUV_SUCCESS;
+ else
+ return_value = RUV_MEMORY_ERROR;
+ }
+ else
+ {
+ if (replica->min_csn == NULL || csn_compare (min_csn, replica->min_csn) < 0)
+ {
+ csn_free(&replica->min_csn);
+ replica->min_csn = csn_dup(min_csn);
+ }
+
+ return_value = RUV_SUCCESS;
+ }
+
+ return return_value;
+}
+
+static int
+set_max_csn_nolock(RUV *ruv, const CSN *max_csn, const char *replica_purl)
+{
+ int return_value;
+ ReplicaId rid = csn_get_replicaid (max_csn);
+ RUVElement *replica = ruvGetReplica (ruv, rid);
+ if (NULL == replica)
+ {
+ replica = ruvAddReplica (ruv, max_csn, replica_purl);
+ if (replica)
+ return_value = RUV_SUCCESS;
+ else
+ return_value = RUV_MEMORY_ERROR;
+ }
+ else
+ {
+ if (replica_purl && replica->replica_purl == NULL)
+ replica->replica_purl = slapi_ch_strdup (replica_purl);
+ csn_free(&replica->csn);
+ replica->csn = csn_dup(max_csn);
+ replica->last_modified = current_time();
+ return_value = RUV_SUCCESS;
+ }
+ return return_value;
+}
+
+int
+ruv_set_min_csn(RUV *ruv, const CSN *min_csn, const char *replica_purl)
+{
+ int return_value;
+ PR_RWLock_Wlock (ruv->lock);
+ return_value = set_min_csn_nolock(ruv, min_csn, replica_purl);
+ PR_RWLock_Unlock (ruv->lock);
+ return return_value;
+}
+
+
+int
+ruv_set_max_csn(RUV *ruv, const CSN *max_csn, const char *replica_purl)
+{
+ int return_value;
+ PR_RWLock_Wlock (ruv->lock);
+ return_value = set_max_csn_nolock(ruv, max_csn, replica_purl);
+ PR_RWLock_Unlock (ruv->lock);
+ return return_value;
+}
+
+int
+ruv_set_csns(RUV *ruv, const CSN *csn, const char *replica_purl)
+{
+ RUVElement *replica;
+ ReplicaId rid;
+ int return_value;
+
+ if (ruv == NULL || csn == NULL)
+ {
+ slapi_log_error(SLAPI_LOG_REPL, repl_plugin_name, "ruv_set_csns: NULL argument\n");
+ return_value = RUV_BAD_DATA;
+ }
+ else
+ {
+ rid = csn_get_replicaid (csn);
+
+ /* prevent element from being destroyed while we get its data */
+ PR_RWLock_Wlock (ruv->lock);
+
+ replica = ruvGetReplica (ruv, rid);
+ if (replica == NULL) /* add new replica */
+ {
+ replica = ruvAddReplica (ruv, csn, replica_purl);
+ if (replica)
+ return_value = RUV_SUCCESS;
+ else
+ return_value = RUV_MEMORY_ERROR;
+ }
+ else
+ {
+ if (csn_compare (csn, replica->csn) > 0)
+ {
+ if (replica->csn != NULL)
+ {
+ csn_init_by_csn ( replica->csn, csn );
+ }
+ else
+ {
+ replica->csn = csn_dup(csn);
+ }
+ replica->last_modified = current_time();
+ if (replica_purl && (NULL == replica->replica_purl ||
+ strcmp(replica->replica_purl, replica_purl) != 0))
+ {
+ /* slapi_ch_free accepts NULL pointer */
+ slapi_ch_free((void **)&replica->replica_purl);
+
+ replica->replica_purl = slapi_ch_strdup(replica_purl);
+ }
+ }
+ /* XXXggood only need to worry about this if real min csn not committed to changelog yet */
+ if (csn_compare (csn, replica->min_csn) < 0)
+ {
+ csn_free(&replica->min_csn);
+ replica->min_csn = csn_dup(csn);
+ }
+ return_value = RUV_SUCCESS;
+ }
+
+ PR_RWLock_Unlock (ruv->lock);
+ }
+ return return_value;
+}
+
+/* This function, for each replica keeps the smallest CSN its seen so far.
+ Used for initial setup of changelog purge vector */
+
+int
+ruv_set_csns_keep_smallest(RUV *ruv, const CSN *csn)
+{
+ RUVElement *replica;
+ ReplicaId rid;
+ int return_value;
+
+ if (ruv == NULL || csn == NULL)
+ {
+ slapi_log_error(SLAPI_LOG_REPL, repl_plugin_name,
+ "ruv_set_csns_keep_smallest: NULL argument\n");
+ return_value = RUV_BAD_DATA;
+ }
+ else
+ {
+ rid = csn_get_replicaid (csn);
+
+ /* prevent element from being destroyed while we get its data */
+ PR_RWLock_Wlock (ruv->lock);
+
+ replica = ruvGetReplica (ruv, rid);
+ if (replica == NULL) /* add new replica */
+ {
+ replica = ruvAddReplica (ruv, csn, NULL);
+ if (replica)
+ return_value = RUV_SUCCESS;
+ else
+ return_value = RUV_MEMORY_ERROR;
+ }
+ else
+ {
+ if (csn_compare (csn, replica->csn) < 0)
+ {
+ csn_free(&replica->csn);
+ replica->csn = csn_dup(csn);
+ replica->last_modified = current_time();
+ }
+
+ return_value = RUV_SUCCESS;
+ }
+
+ PR_RWLock_Unlock (ruv->lock);
+ }
+ return return_value;
+}
+
+
+void
+ruv_set_replica_generation(RUV *ruv, const char *csnstr)
+{
+ if (NULL != csnstr && NULL != ruv)
+ {
+ PR_RWLock_Wlock (ruv->lock);
+
+ if (NULL != ruv->replGen)
+ {
+ slapi_ch_free((void **)&ruv->replGen);
+ }
+ ruv->replGen = slapi_ch_strdup(csnstr);
+
+ PR_RWLock_Unlock (ruv->lock);
+ }
+}
+
+
+char *
+ruv_get_replica_generation(const RUV *ruv)
+{
+ char *return_str = NULL;
+
+ PR_RWLock_Rlock (ruv->lock);
+
+ if (ruv != NULL && ruv->replGen != NULL)
+ {
+ return_str = slapi_ch_strdup(ruv->replGen);
+ }
+
+ PR_RWLock_Unlock (ruv->lock);
+
+ return return_str;
+}
+
+static PRBool
+ruv_covers_csn_internal(const RUV *ruv, const CSN *csn, PRBool strict)
+{
+ RUVElement *replica;
+ ReplicaId rid;
+ PRBool return_value;
+
+ if (ruv == NULL || csn == NULL)
+ {
+ slapi_log_error(SLAPI_LOG_REPL, repl_plugin_name, "ruv_covers_csn: NULL argument\n");
+ return_value = PR_FALSE;
+ }
+ else
+ {
+ rid = csn_get_replicaid(csn);
+ replica = ruvGetReplica (ruv, rid);
+ if (replica == NULL)
+ {
+ slapi_log_error(SLAPI_LOG_REPL, repl_plugin_name, "ruv_covers_csn: replica for id %d not found\n", rid);
+ return_value = PR_FALSE;
+ }
+ else
+ {
+ if (strict)
+ {
+ return_value = (csn_compare (csn, replica->csn) < 0);
+ }
+ else
+ {
+ return_value = (csn_compare (csn, replica->csn) <= 0);
+ }
+ }
+ }
+ return return_value;
+}
+
+PRBool
+ruv_covers_csn(const RUV *ruv, const CSN *csn)
+{
+ PRBool rc;
+
+ PR_RWLock_Rlock (ruv->lock);
+ rc = ruv_covers_csn_internal(ruv, csn, PR_FALSE);
+ PR_RWLock_Unlock (ruv->lock);
+
+ return rc;
+}
+
+PRBool
+ruv_covers_csn_strict(const RUV *ruv, const CSN *csn)
+{
+ PRBool rc;
+
+ PR_RWLock_Rlock (ruv->lock);
+ rc = ruv_covers_csn_internal(ruv, csn, PR_TRUE);
+ PR_RWLock_Unlock (ruv->lock);
+
+ return rc;
+}
+
+
+/*
+ * The function gets min{maxcsns of all ruv elements} if get_the_max=0,
+ * or max{maxcsns of all ruv elements} if get_the_max != 0.
+ */
+static int
+ruv_get_min_or_max_csn(const RUV *ruv, CSN **csn, int get_the_max)
+{
+ int return_value;
+
+ if (ruv == NULL || csn == NULL)
+ {
+ slapi_log_error(SLAPI_LOG_REPL, repl_plugin_name, "ruv_get_min_or_max_csn: NULL argument\n");
+ return_value = RUV_BAD_DATA;
+ }
+ else
+ {
+ CSN *found = NULL;
+ RUVElement *replica;
+ int cookie;
+ PR_RWLock_Rlock (ruv->lock);
+ for (replica = dl_get_first (ruv->elements, &cookie); replica;
+ replica = dl_get_next (ruv->elements, &cookie))
+ {
+ /*
+ * Skip replica whose maxcsn is NULL otherwise
+ * the code will return different min_csn if
+ * the sequence of the replicas is altered.
+ *
+ * don't use READ_ONLY replicas for computing the value of
+ * "found", as they seem to have NULL csn and min_csn
+ */
+ if (replica->csn == NULL || replica->rid == READ_ONLY_REPLICA_ID)
+ {
+ continue;
+ }
+
+ if (found == NULL ||
+ (!get_the_max && csn_compare(found, replica->csn)>0) ||
+ ( get_the_max && csn_compare(found, replica->csn)<0))
+ {
+ found = replica->csn;
+ }
+ }
+ if (found == NULL)
+ {
+ *csn = NULL;
+ }
+ else
+ {
+ *csn = csn_dup (found);
+ }
+ PR_RWLock_Unlock (ruv->lock);
+ return_value = RUV_SUCCESS;
+ }
+ return return_value;
+}
+
+int
+ruv_get_max_csn(const RUV *ruv, CSN **csn)
+{
+ return ruv_get_min_or_max_csn(ruv, csn, 1 /* get the max */);
+}
+
+int
+ruv_get_min_csn(const RUV *ruv, CSN **csn)
+{
+ return ruv_get_min_or_max_csn(ruv, csn, 0 /* get the min */);
+}
+
+int
+ruv_enumerate_elements (const RUV *ruv, FNEnumRUV fn, void *arg)
+{
+ int cookie;
+ RUVElement *elem;
+ int rc = 0;
+ ruv_enum_data enum_data = {0};
+
+ if (ruv == NULL || fn == NULL)
+ {
+ /* ONREPL - log error */
+ return -1;
+ }
+
+ PR_RWLock_Rlock (ruv->lock);
+ for (elem = (RUVElement*)dl_get_first (ruv->elements, &cookie); elem;
+ elem = (RUVElement*)dl_get_next (ruv->elements, &cookie))
+ {
+ /* we only return elements that contains both minimal and maximal CSNs */
+ if (elem->csn && elem->min_csn)
+ {
+ enum_data.csn = elem->csn;
+ enum_data.min_csn = elem->min_csn;
+ rc = fn (&enum_data, arg);
+ if (rc != 0)
+ break;
+ }
+ }
+
+ PR_RWLock_Unlock (ruv->lock);
+
+ return rc;
+}
+
+/*
+ * Convert a replica update vector to a NULL-terminated array
+ * of bervals. The caller is responsible for freeing the bervals.
+ */
+int
+ruv_to_bervals(const RUV *ruv, struct berval ***bvals)
+{
+ struct berval **returned_bervals = NULL;
+ int return_value;
+ if (ruv == NULL || bvals == NULL)
+ {
+ slapi_log_error(SLAPI_LOG_REPL, repl_plugin_name, "ruv_to_bervals: NULL argument\n");
+ return_value = RUV_BAD_DATA;
+ }
+ else
+ {
+ int count;
+ int i;
+ RUVElement *replica;
+ char csnStr1 [CSN_STRSIZE];
+ char csnStr2 [CSN_STRSIZE];
+ int cookie;
+ PR_RWLock_Rlock (ruv->lock);
+ count = dl_get_count (ruv->elements) + 2;
+ returned_bervals = (struct berval **)slapi_ch_malloc(sizeof(struct berval *) * count);
+ returned_bervals[count - 1] = NULL;
+ returned_bervals[0] = (struct berval *)slapi_ch_malloc(sizeof(struct berval));
+ returned_bervals[0]->bv_val = slapi_ch_malloc(strlen(prefix_replicageneration) + strlen(ruv->replGen) + 2);
+ sprintf(returned_bervals[0]->bv_val, "%s %s",
+ prefix_replicageneration, ruv->replGen);
+ returned_bervals[0]->bv_len = strlen(returned_bervals[0]->bv_val);
+ for (i = 1, replica = dl_get_first (ruv->elements, &cookie); replica;
+ i++, replica = dl_get_next (ruv->elements, &cookie))
+ {
+ returned_bervals[i] = (struct berval *)slapi_ch_malloc(sizeof(struct berval));
+ returned_bervals[i]->bv_val = slapi_ch_malloc(strlen(prefix_ruvcsn) + RIDSTR_SIZE +
+ ((replica->replica_purl == NULL) ? 0 : strlen(replica->replica_purl)) +
+ ((replica->min_csn == NULL) ? 0 : CSN_STRSIZE) +
+ ((replica->csn == NULL) ? 0 : CSN_STRSIZE) + 5);
+ sprintf(returned_bervals[i]->bv_val, "%s%d%s%s}%s%s%s%s",
+ prefix_ruvcsn, replica->rid,
+ replica->replica_purl == NULL ? "" : " ",
+ replica->replica_purl == NULL ? "" : replica->replica_purl,
+ replica->min_csn == NULL ? "" : " ",
+ replica->min_csn == NULL ? "" : csn_as_string (replica->min_csn, PR_FALSE, csnStr1),
+ replica->csn == NULL ? "" : " ",
+ replica->csn == NULL ? "" : csn_as_string (replica->csn, PR_FALSE, csnStr2));
+ returned_bervals[i]->bv_len = strlen(returned_bervals[i]->bv_val);
+ }
+ PR_RWLock_Unlock (ruv->lock);
+ return_value = RUV_SUCCESS;
+ *bvals = returned_bervals;
+ }
+ return return_value;
+}
+
+int
+ruv_to_smod(const RUV *ruv, Slapi_Mod *smod)
+{
+ int return_value;
+
+ if (ruv == NULL || smod == NULL)
+ {
+ slapi_log_error(SLAPI_LOG_REPL, repl_plugin_name, "ruv_to_smod: NULL argument\n");
+ return_value = RUV_BAD_DATA;
+ }
+ else
+ {
+ struct berval val;
+ RUVElement *replica;
+ int cookie;
+ char csnStr1 [CSN_STRSIZE];
+ char csnStr2 [CSN_STRSIZE];
+#define B_SIZ 1024
+ char buf[B_SIZ];
+ PR_RWLock_Rlock (ruv->lock);
+ slapi_mod_init (smod, dl_get_count (ruv->elements) + 1);
+ slapi_mod_set_type (smod, type_ruvElement);
+ slapi_mod_set_operation (smod, LDAP_MOD_REPLACE | LDAP_MOD_BVALUES);
+ PR_snprintf(buf, B_SIZ, "%s %s", prefix_replicageneration, ruv->replGen);
+ val.bv_val = buf;
+ val.bv_len = strlen(buf);
+ slapi_mod_add_value(smod, &val);
+ for (replica = dl_get_first (ruv->elements, &cookie); replica;
+ replica = dl_get_next (ruv->elements, &cookie))
+ {
+
+ PR_snprintf(buf, B_SIZ, "%s%d%s%s}%s%s%s%s", prefix_ruvcsn, replica->rid,
+ replica->replica_purl == NULL ? "" : " ",
+ replica->replica_purl == NULL ? "" : replica->replica_purl,
+ replica->min_csn == NULL ? "" : " ",
+ replica->min_csn == NULL ? "" : csn_as_string (replica->min_csn, PR_FALSE, csnStr1),
+ replica->csn == NULL ? "" : " ",
+ replica->csn == NULL ? "" : csn_as_string (replica->csn, PR_FALSE, csnStr2));
+ val.bv_len = strlen(buf);
+ slapi_mod_add_value(smod, &val);
+ }
+ PR_RWLock_Unlock (ruv->lock);
+ return_value = RUV_SUCCESS;
+ }
+ return return_value;
+}
+
+int
+ruv_last_modified_to_smod(const RUV *ruv, Slapi_Mod *smod)
+{
+ int return_value;
+
+ if (ruv == NULL || smod == NULL)
+ {
+ slapi_log_error(SLAPI_LOG_REPL, repl_plugin_name, "ruv_last_modified_to_smod: NULL argument\n");
+ return_value = RUV_BAD_DATA;
+ }
+ else
+ {
+ struct berval val;
+ RUVElement *replica;
+ int cookie;
+ char buf[B_SIZ];
+ PR_RWLock_Rlock (ruv->lock);
+ slapi_mod_init (smod, dl_get_count (ruv->elements));
+ slapi_mod_set_type (smod, type_ruvElementUpdatetime);
+ slapi_mod_set_operation (smod, LDAP_MOD_REPLACE | LDAP_MOD_BVALUES);
+ val.bv_val = buf;
+ for (replica = dl_get_first (ruv->elements, &cookie); replica;
+ replica = dl_get_next (ruv->elements, &cookie))
+ {
+ PR_snprintf(buf, B_SIZ, "%s%d%s%s} %08lx", prefix_ruvcsn, replica->rid,
+ replica->replica_purl == NULL ? "" : " ",
+ replica->replica_purl == NULL ? "" : replica->replica_purl,
+ replica->last_modified);
+ val.bv_len = strlen(buf);
+ slapi_mod_add_value(smod, &val);
+ }
+ PR_RWLock_Unlock (ruv->lock);
+ return_value = RUV_SUCCESS;
+ }
+ return return_value;
+}
+
+/*
+ * XXXggood do we need "ruv_covers_ruv_strict" ???? */
+PRBool
+ruv_covers_ruv(const RUV *covering_ruv, const RUV *covered_ruv)
+{
+ PRBool return_value = PR_TRUE;
+ RUVElement *replica;
+ int cookie;
+
+ /* compare replica generations first */
+ if (covering_ruv->replGen == NULL)
+ {
+ if (covered_ruv->replGen)
+ return PR_FALSE;
+ }
+ else
+ {
+ if (covered_ruv->replGen == NULL)
+ return PR_FALSE;
+ }
+
+ if (strcasecmp (covered_ruv->replGen, covering_ruv->replGen))
+ return PR_FALSE;
+
+ /* replica generation is the same, now compare element by element */
+ for (replica = dl_get_first (covered_ruv->elements, &cookie);
+ NULL != replica;
+ replica = dl_get_next (covered_ruv->elements, &cookie))
+ {
+ if (replica->csn &&
+ (ruv_covers_csn(covering_ruv, replica->csn) == PR_FALSE))
+ {
+ return_value = PR_FALSE;
+ /* Don't break here - may leave something referenced? */
+ }
+ }
+ return return_value;
+}
+
+PRInt32
+ruv_replica_count (const RUV *ruv)
+{
+ if (ruv == NULL)
+ return 0;
+ else
+ {
+ int count;
+
+ PR_RWLock_Rlock (ruv->lock);
+ count = dl_get_count (ruv->elements);
+ PR_RWLock_Unlock (ruv->lock);
+
+ return count;
+ }
+}
+
+/*
+ * Extract all the referral URL's from the RUV (but self URL),
+ * returning them in an array of strings, that
+ * the caller must free.
+ */
+char **
+ruv_get_referrals(const RUV *ruv)
+{
+ char **r= NULL;
+ int n;
+ const char *mypurl = multimaster_get_local_purl();
+
+ PR_RWLock_Rlock (ruv->lock);
+
+ n = ruv_replica_count(ruv);
+ if(n>0)
+ {
+ RUVElement *replica;
+ int cookie;
+ int i= 0;
+ r= (char**)slapi_ch_calloc(sizeof(char*),n+1);
+ for (replica = dl_get_first (ruv->elements, &cookie); replica;
+ replica = dl_get_next (ruv->elements, &cookie))
+ {
+ /* Add URL into referrals if doesn't match self URL */
+ if((replica->replica_purl!=NULL) &&
+ (slapi_utf8casecmp((unsigned char *)replica->replica_purl,
+ (unsigned char *)mypurl) != 0))
+ {
+ r[i]= slapi_ch_strdup(replica->replica_purl);
+ i++;
+ }
+ }
+ }
+
+ PR_RWLock_Unlock (ruv->lock);
+
+ return r; /* Caller must free this */
+}
+
+void
+ruv_dump(const RUV *ruv, char *ruv_name, PRFileDesc *prFile)
+{
+ RUVElement *replica;
+ int cookie;
+ char csnstr1[CSN_STRSIZE];
+ char csnstr2[CSN_STRSIZE];
+ char buff[RUVSTR_SIZE];
+ int len = sizeof (buff);
+
+ PR_ASSERT(NULL != ruv);
+
+ PR_RWLock_Rlock (ruv->lock);
+
+ PR_snprintf (buff, len, "%s: {replicageneration} %s\n",
+ ruv_name ? ruv_name : type_ruvElement,
+ ruv->replGen == NULL ? "" : ruv->replGen);
+
+ if (prFile)
+ {
+ slapi_write_buffer (prFile, buff, strlen(buff));
+ }
+ else
+ {
+ slapi_log_error(SLAPI_LOG_REPL, repl_plugin_name, buff);
+ }
+ for (replica = dl_get_first (ruv->elements, &cookie); replica;
+ replica = dl_get_next (ruv->elements, &cookie))
+ {
+ /* prefix_ruvcsn = "{replica " */
+ PR_snprintf (buff, len, "%s: %s%d%s%s} %s %s\n",
+ ruv_name ? ruv_name : type_ruvElement,
+ prefix_ruvcsn, replica->rid,
+ replica->replica_purl == NULL ? "" : " ",
+ replica->replica_purl == NULL ? "" : replica->replica_purl,
+ csn_as_string(replica->min_csn, PR_FALSE, csnstr1),
+ csn_as_string(replica->csn, PR_FALSE, csnstr2));
+ if (strlen (csnstr1) > 0) {
+ PR_snprintf (buff + strlen(buff) - 1, len - strlen(buff), " %08lx\n",
+ replica->last_modified);
+ }
+ if (prFile)
+ {
+ slapi_write_buffer (prFile, buff, strlen(buff));
+ }
+ else
+ {
+ slapi_log_error(SLAPI_LOG_REPL, repl_plugin_name, buff);
+ }
+ }
+
+ PR_RWLock_Unlock (ruv->lock);
+}
+
+/* this function notifies the ruv that there are operations in progress so that
+ they can be added to the pending list for the appropriate client. */
+int ruv_add_csn_inprogress (RUV *ruv, const CSN *csn)
+{
+ RUVElement* replica;
+ char csn_str[CSN_STRSIZE];
+ int rc = RUV_SUCCESS;
+
+ PR_ASSERT (ruv && csn);
+
+ /* locate ruvElement */
+ PR_RWLock_Wlock (ruv->lock);
+ replica = ruvGetReplica (ruv, csn_get_replicaid (csn));
+ if (replica == NULL)
+ {
+ replica = ruvAddReplicaNoCSN (ruv, csn_get_replicaid (csn), NULL/*purl*/);
+ if (replica == NULL)
+ {
+ slapi_log_error(SLAPI_LOG_REPL, repl_plugin_name, "ruv_add_csn_inprogress: failed to add replica"
+ " that created csn %s\n", csn_as_string (csn, PR_FALSE, csn_str));
+ rc = RUV_MEMORY_ERROR;
+ goto done;
+ }
+ }
+
+ /* check first that this csn is not already covered by this RUV */
+ if (ruv_covers_csn_internal(ruv, csn, PR_FALSE))
+ {
+ slapi_log_error(SLAPI_LOG_REPL, repl_plugin_name, "ruv_add_csn_inprogress: "
+ "the csn %s has already be seen - ignoring\n",
+ csn_as_string (csn, PR_FALSE, csn_str));
+ rc = RUV_COVERS_CSN;
+ goto done;
+ }
+
+ rc = csnplInsert (replica->csnpl, csn);
+ if (rc == 1) /* we already seen this csn */
+ {
+ slapi_log_error(SLAPI_LOG_REPL, repl_plugin_name, "ruv_add_csn_inprogress: "
+ "the csn %s has already be seen - ignoring\n",
+ csn_as_string (csn, PR_FALSE, csn_str));
+ rc = RUV_COVERS_CSN;
+ }
+ else if(rc != 0)
+ {
+ slapi_log_error(SLAPI_LOG_REPL, repl_plugin_name, "ruv_add_csn_inprogress: failed to insert csn %s"
+ " into pending list\n", csn_as_string (csn, PR_FALSE, csn_str));
+ rc = RUV_UNKNOWN_ERROR;
+ }
+ else
+ {
+ slapi_log_error(SLAPI_LOG_REPL, repl_plugin_name, "ruv_add_csn_inprogress: successfully inserted csn %s"
+ " into pending list\n", csn_as_string (csn, PR_FALSE, csn_str));
+ rc = RUV_SUCCESS;
+ }
+
+done:
+ PR_RWLock_Unlock (ruv->lock);
+ return rc;
+}
+
+int ruv_cancel_csn_inprogress (RUV *ruv, const CSN *csn)
+{
+ RUVElement* replica;
+ int rc = RUV_SUCCESS;
+
+ PR_ASSERT (ruv && csn);
+
+ /* locate ruvElement */
+ PR_RWLock_Wlock (ruv->lock);
+ replica = ruvGetReplica (ruv, csn_get_replicaid (csn));
+ if (replica == NULL)
+ {
+ /* ONREPL - log error */
+ rc = RUV_NOTFOUND;
+ goto done;
+ }
+
+ rc = csnplRemove (replica->csnpl, csn);
+ if (rc != 0)
+ rc = RUV_NOTFOUND;
+ else
+ rc = RUV_SUCCESS;
+
+done:
+ PR_RWLock_Unlock (ruv->lock);
+ return rc;
+}
+
+int ruv_update_ruv (RUV *ruv, const CSN *csn, const char *replica_purl, PRBool isLocal)
+{
+ int rc=RUV_SUCCESS;
+ char csn_str[CSN_STRSIZE];
+ CSN *max_csn;
+ CSN *first_csn = NULL;
+ RUVElement *replica;
+
+ PR_ASSERT (ruv && csn);
+
+ PR_RWLock_Wlock (ruv->lock);
+
+ replica = ruvGetReplica (ruv, csn_get_replicaid (csn));
+ if (replica == NULL)
+ {
+ /* we should have a ruv element at this point because it would have
+ been added by ruv_add_inprogress function */
+ slapi_log_error(SLAPI_LOG_REPL, repl_plugin_name, "ruv_update_ruv: "
+ "can't locate RUV element for replica %d\n", csn_get_replicaid (csn));
+ goto done;
+ }
+
+ if (csnplCommit(replica->csnpl, csn) != 0)
+ {
+ slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name, "ruv_update_ruv: cannot commit csn %s\n",
+ csn_as_string(csn, PR_FALSE, csn_str));
+ rc = RUV_CSNPL_ERROR;
+ goto done;
+ }
+ else
+ {
+ slapi_log_error(SLAPI_LOG_REPL, repl_plugin_name, "ruv_update_ruv: "
+ "successfully committed csn %s\n", csn_as_string(csn, PR_FALSE, csn_str));
+ }
+
+ if ((max_csn = csnplRollUp(replica->csnpl, &first_csn)) != NULL)
+ {
+#ifdef DEBUG
+ slapi_log_error(SLAPI_LOG_REPL, repl_plugin_name, "ruv_update_ruv: rolled up to csn %s\n",
+ csn_as_string(max_csn, PR_FALSE, csn_str)); /* XXXggood remove debugging */
+#endif
+ /* replica object sets min csn for local replica */
+ if (!isLocal && replica->min_csn == NULL) {
+ /* bug 559223 - it seems that, under huge stress, a server might pass
+ * through this code when more than 1 change has already been sent and commited into
+ * the pending lists... Therefore, as we are trying to set the min_csn ever
+ * generated by this replica, we need to set the first_csn as the min csn in the
+ * ruv */
+ set_min_csn_nolock(ruv, first_csn, replica_purl);
+ }
+ set_max_csn_nolock(ruv, max_csn, replica_purl);
+ /* It is possible that first_csn points to max_csn.
+ We need to free it once */
+ if (max_csn != first_csn) {
+ csn_free(&first_csn);
+ }
+ csn_free(&max_csn);
+ }
+
+done:
+ PR_RWLock_Unlock (ruv->lock);
+
+ return rc;
+}
+
+/* Helper functions */
+
+static int
+ruvInit (RUV **ruv, int initCount)
+{
+ PR_ASSERT (ruv);
+
+ /* allocate new RUV */
+ *ruv = (RUV *)slapi_ch_calloc (1, sizeof (RUV));
+ if (ruv == NULL)
+ {
+ slapi_log_error(SLAPI_LOG_REPL, repl_plugin_name,
+ "ruvInit: memory allocation failed\n");
+ return RUV_MEMORY_ERROR;
+ }
+
+ /* allocate elements */
+ (*ruv)->elements = dl_new ();
+ if ((*ruv)->elements == NULL)
+ {
+ slapi_log_error(SLAPI_LOG_REPL, repl_plugin_name,
+ "ruvInit: memory allocation failed\n");
+ return RUV_MEMORY_ERROR;
+ }
+
+ dl_init ((*ruv)->elements, initCount);
+
+ /* create lock */
+ (*ruv)->lock = PR_NewRWLock(PR_RWLOCK_RANK_NONE, "ruv_lock");
+ if ((*ruv)->lock == NULL)
+ {
+ slapi_log_error(SLAPI_LOG_REPL, repl_plugin_name,
+ "ruvInit: failed to create lock\n");
+ return RUV_NSPR_ERROR;
+ }
+
+ return RUV_SUCCESS;
+}
+
+static void
+ruvFreeReplica (void **data)
+{
+ RUVElement *element = *(RUVElement**)data;
+
+ if (NULL != element)
+ {
+ if (NULL != element->csn)
+ {
+ csn_free (&element->csn);
+ }
+ if (NULL != element->min_csn)
+ {
+ csn_free (&element->min_csn);
+ }
+ /* slapi_ch_free accepts NULL pointer */
+ slapi_ch_free((void **)&element->replica_purl);
+
+ if (element->csnpl)
+ {
+ csnplFree (&(element->csnpl));
+ }
+ slapi_ch_free ((void **)&element);
+ }
+}
+
+static RUVElement*
+ruvAddReplica (RUV *ruv, const CSN *csn, const char *replica_purl)
+{
+ RUVElement *replica;
+
+ PR_ASSERT (NULL != ruv);
+ PR_ASSERT (NULL != csn);
+
+ replica = (RUVElement *)slapi_ch_calloc (1, sizeof (RUVElement));
+ if (replica == NULL)
+ {
+ slapi_log_error(SLAPI_LOG_REPL, repl_plugin_name,
+ "ruvAddReplica: memory allocation failed\n");
+ return NULL;
+ }
+
+ replica->rid = csn_get_replicaid (csn);
+/* PR_ASSERT(replica->rid != READ_ONLY_REPLICA_ID); */
+
+ replica->csn = csn_dup (csn);
+ replica->last_modified = current_time();
+ replica->min_csn = csn_dup (csn);
+
+ replica->replica_purl = slapi_ch_strdup(replica_purl);
+ replica->csnpl = csnplNew ();
+
+ dl_add (ruv->elements, replica);
+
+ return replica;
+}
+
+static RUVElement*
+ruvAddReplicaNoCSN (RUV *ruv, ReplicaId rid, const char *replica_purl)
+{
+ RUVElement *replica;
+
+ PR_ASSERT (NULL != ruv);
+/* PR_ASSERT(rid != READ_ONLY_REPLICA_ID); */
+
+ replica = (RUVElement *)slapi_ch_calloc (1, sizeof (RUVElement));
+ if (replica == NULL)
+ {
+ slapi_log_error(SLAPI_LOG_REPL, repl_plugin_name,
+ "ruvAddReplicaNoCSN: memory allocation failed\n");
+ return NULL;
+ }
+
+ replica->rid = rid;
+ replica->replica_purl = slapi_ch_strdup(replica_purl);
+ replica->csnpl = csnplNew ();
+
+ dl_add (ruv->elements, replica);
+
+ return replica;
+}
+
+static RUVElement*
+ruvAddIndexReplicaNoCSN (RUV *ruv, ReplicaId rid, const char *replica_purl, int index)
+{
+ RUVElement *replica;
+
+ PR_ASSERT (NULL != ruv);
+/* PR_ASSERT(rid != READ_ONLY_REPLICA_ID); */
+
+ replica = (RUVElement *)slapi_ch_calloc (1, sizeof (RUVElement));
+ if (replica == NULL)
+ {
+ slapi_log_error(SLAPI_LOG_REPL, repl_plugin_name,
+ "ruvAddIndexReplicaNoCSN: memory allocation failed\n");
+ return NULL;
+ }
+
+ replica->rid = rid;
+ replica->replica_purl = slapi_ch_strdup(replica_purl);
+ replica->csnpl = csnplNew ();
+
+ dl_add_index (ruv->elements, replica, index);
+
+ return replica;
+}
+
+static RUVElement *
+ruvGetReplica (const RUV *ruv, ReplicaId rid)
+{
+ PR_ASSERT (ruv /* && rid >= 0 -- rid can't be negative */);
+
+ return (RUVElement *)dl_get (ruv->elements, (const void*)&rid, ruvReplicaCompare);
+}
+
+static int
+ruvReplicaCompare (const void *el1, const void *el2)
+{
+ RUVElement *replica = (RUVElement*)el1;
+ ReplicaId *rid1 = (ReplicaId*) el2;
+
+ if (replica == NULL || rid1 == NULL)
+ return -1;
+
+ if (*rid1 == replica->rid)
+ return 0;
+
+ if (*rid1 < replica->rid)
+ return -1;
+ else
+ return 1;
+}
+
+
+
+/*
+ * Given a berval that points to a string of the form:
+ * "{dbgen} generation-id", return a copy of the
+ * "generation-id" part in a null-terminated string.
+ * Returns NULL if the berval is malformed.
+ */
+static char *
+get_replgen_from_berval(const struct berval *bval)
+{
+ char *ret_string = NULL;
+
+ if (NULL != bval && NULL != bval->bv_val &&
+ (bval->bv_len > strlen(prefix_replicageneration)) &&
+ strncasecmp(bval->bv_val, prefix_replicageneration,
+ strlen(prefix_replicageneration)) == 0)
+ {
+ unsigned int index = strlen(prefix_replicageneration);
+ /* Skip any whitespace */
+ while (index++ < bval->bv_len && bval->bv_val[index] == ' ');
+ if (index < bval->bv_len)
+ {
+ unsigned int ret_len = bval->bv_len - index;
+ ret_string = slapi_ch_malloc(ret_len + 1);
+ memcpy(ret_string, &bval->bv_val[index], ret_len);
+ ret_string[ret_len] = '\0';
+ }
+ }
+ return ret_string;
+}
+
+
+
+/*
+ * Given a berval that points to a string of the form:
+ * "{replica ldap[s]//host:port} <min_csn> <csn>", parse out the
+ * partial URL and the CSNs into an RUVElement, and return
+ * a pointer to the copy. Returns NULL if the berval is
+ * malformed.
+ */
+static RUVElement *
+get_ruvelement_from_berval(const struct berval *bval)
+{
+ RUVElement *ret_ruve = NULL;
+ char *purl = NULL;
+ ReplicaId rid = 0;
+ char ridbuff [RIDSTR_SIZE];
+ int i;
+
+ if (NULL != bval && NULL != bval->bv_val &&
+ bval->bv_len > strlen(prefix_ruvcsn) &&
+ strncasecmp(bval->bv_val, prefix_ruvcsn, strlen(prefix_ruvcsn)) == 0)
+ {
+ unsigned int urlbegin = strlen(prefix_ruvcsn);
+ unsigned int urlend;
+ unsigned int mincsnbegin;
+
+ /* replica id must be here */
+ i = 0;
+ while (isdigit (bval->bv_val[urlbegin]))
+ {
+ ridbuff [i] = bval->bv_val[urlbegin];
+ i++;
+ urlbegin ++;
+ }
+
+ if (i == 0) /* replicaid is missing */
+ goto loser;
+
+ ridbuff[i] = '\0';
+ rid = atoi (ridbuff);
+
+ if (bval->bv_val[urlbegin] == '}')
+ {
+ /* No purl in this value */
+ purl = NULL;
+ mincsnbegin = urlbegin + 1;
+ }
+ else
+ {
+ while (urlbegin++ < bval->bv_len && bval->bv_val[urlbegin] == ' ');
+ urlend = urlbegin;
+ while (urlend++ < bval->bv_len && bval->bv_val[urlend] != '}');
+ purl = slapi_ch_malloc(urlend - urlbegin + 1);
+ memcpy(purl, &bval->bv_val[urlbegin], urlend - urlbegin);
+ purl[urlend - urlbegin] = '\0';
+ mincsnbegin = urlend;
+ }
+ /* Skip any whitespace before the first (minimum) CSN */
+ while (mincsnbegin++ < (bval->bv_len-1) && bval->bv_val[mincsnbegin] == ' ');
+ /* Now, mincsnbegin should contain the index of the beginning of the first csn */
+ if (mincsnbegin >= bval->bv_len)
+ {
+ /* Missing the entire content*/
+ if (purl == NULL)
+ goto loser;
+ else /* we have just purl - no changes from the replica has been seen */
+ {
+ ret_ruve = (RUVElement *)slapi_ch_calloc(1, sizeof(RUVElement));
+ ret_ruve->rid = rid;
+ ret_ruve->replica_purl = purl;
+ }
+ }
+ else
+ {
+ if (bval->bv_len - mincsnbegin != (_CSN_VALIDCSN_STRLEN * 2) + 1)
+ {
+ /* Malformed - incorrect length for 2 CSNs + space */
+ goto loser;
+ }
+ else
+ {
+ char mincsnstr[CSN_STRSIZE];
+ char maxcsnstr[CSN_STRSIZE];
+
+ memset(mincsnstr, '\0', CSN_STRSIZE);
+ memset(maxcsnstr, '\0', CSN_STRSIZE);
+ memcpy(mincsnstr, &bval->bv_val[mincsnbegin], _CSN_VALIDCSN_STRLEN);
+ memcpy(maxcsnstr, &bval->bv_val[mincsnbegin + _CSN_VALIDCSN_STRLEN + 1], _CSN_VALIDCSN_STRLEN);
+ ret_ruve = (RUVElement *)slapi_ch_calloc(1, sizeof(RUVElement));
+ ret_ruve->min_csn = csn_new_by_string(mincsnstr);
+ ret_ruve->csn = csn_new_by_string(maxcsnstr);
+ ret_ruve->rid = rid;
+ ret_ruve->replica_purl = purl;
+ if (NULL == ret_ruve->min_csn || NULL == ret_ruve->csn)
+ {
+ goto loser;
+ }
+ }
+ }
+ }
+
+ /* initialize csn pending list */
+ ret_ruve->csnpl = csnplNew ();
+ if (ret_ruve->csnpl == NULL)
+ {
+ slapi_log_error(SLAPI_LOG_REPL, repl_plugin_name,
+ "get_ruvelement_from_berval: failed to create csn pending list\n");
+ goto loser;
+ }
+
+ return ret_ruve;
+
+loser:
+ /* slapi_ch_free accepts NULL pointer */
+ slapi_ch_free((void **)&purl);
+ if (NULL != ret_ruve)
+ {
+ if (NULL != ret_ruve->min_csn)
+ {
+ csn_free(&ret_ruve->min_csn);
+ }
+ if (NULL != ret_ruve->csn)
+ {
+ csn_free(&ret_ruve->csn);
+ }
+ slapi_ch_free((void **)&ret_ruve);
+ }
+ return NULL;
+}
+
+int
+ruv_move_local_supplier_to_first(RUV *ruv, ReplicaId aRid)
+{
+ RUVElement * elem = NULL;
+ int rc = RUV_NOTFOUND;
+
+ PR_ASSERT(ruv);
+
+ PR_RWLock_Wlock (ruv->lock);
+
+ elem = (RUVElement *)dl_delete(ruv->elements,(const void*)&aRid, ruvReplicaCompare, 0);
+ if (elem) {
+ dl_add_index(ruv->elements, elem, 1);
+ rc = RUV_SUCCESS;
+ }
+
+ PR_RWLock_Unlock (ruv->lock);
+
+ return rc;
+}
+
+
+int
+ruv_get_first_id_and_purl(RUV *ruv, ReplicaId *rid, char **replica_purl )
+{
+ RUVElement * first = NULL;
+ int cookie;
+ int rc;
+
+ PR_ASSERT(ruv);
+
+ PR_RWLock_Rlock (ruv->lock);
+ first = dl_get_first(ruv->elements, &cookie);
+ if ( first == NULL )
+ {
+ rc = RUV_MEMORY_ERROR;
+ }
+ else
+ {
+ *rid = first->rid;
+ *replica_purl = first->replica_purl;
+ rc = RUV_SUCCESS;
+ }
+ PR_RWLock_Unlock (ruv->lock);
+ return rc;
+}
+
+int ruv_local_contains_supplier(RUV *ruv, ReplicaId rid)
+{
+ int cookie;
+ RUVElement *elem = NULL;
+
+ PR_ASSERT(ruv);
+
+ PR_RWLock_Rlock (ruv->lock);
+ for (elem = dl_get_first (ruv->elements, &cookie);
+ elem;
+ elem = dl_get_next (ruv->elements, &cookie))
+ {
+ if (elem->rid == rid){
+ PR_RWLock_Unlock (ruv->lock);
+ return 1;
+ }
+ }
+ PR_RWLock_Unlock (ruv->lock);
+ return 0;
+}
+
+PRBool ruv_has_csns(const RUV *ruv)
+{
+ PRBool retval = PR_TRUE;
+ CSN *mincsn = NULL;
+ CSN *maxcsn = NULL;
+
+ ruv_get_min_csn(ruv, &mincsn);
+ ruv_get_max_csn(ruv, &maxcsn);
+ if (mincsn) {
+ csn_free(&mincsn);
+ csn_free(&maxcsn);
+ } else if (maxcsn) {
+ csn_free(&maxcsn);
+ } else {
+ retval = PR_FALSE; /* both min and max are false */
+ }
+
+ return retval;
+}
+
+/* Check if the first ruv is newer than the second one */
+PRBool
+ruv_is_newer (Object *sruvobj, Object *cruvobj)
+{
+ RUV *sruv, *cruv;
+ RUVElement *sreplica, *creplica;
+ int scookie, ccookie;
+ int is_newer = PR_FALSE;
+
+ if ( sruvobj == NULL ) {
+ return 0;
+ }
+ if ( cruvobj == NULL ) {
+ return 1;
+ }
+ sruv = (RUV *) object_get_data ( sruvobj );
+ cruv = (RUV *) object_get_data ( cruvobj );
+
+ for (sreplica = dl_get_first (sruv->elements, &scookie); sreplica;
+ sreplica = dl_get_next (sruv->elements, &scookie))
+ {
+ /* A hub may have a dummy ruv with rid 65535 */
+ if ( sreplica->csn == NULL ) continue;
+
+ for (creplica = dl_get_first (cruv->elements, &ccookie); creplica;
+ creplica = dl_get_next (cruv->elements, &ccookie))
+ {
+ if ( sreplica->rid == creplica->rid ) {
+ if ( csn_compare ( sreplica->csn, creplica->csn ) > 0 ) {
+ is_newer = PR_TRUE;
+ }
+ break;
+ }
+ }
+ if ( creplica == NULL || is_newer ) {
+ is_newer = PR_TRUE;
+ break;
+ }
+ }
+
+ return is_newer;
+}
+
+#ifdef TESTING /* Some unit tests for code in this file */
+
+static void
+ruv_dump_internal(RUV *ruv)
+{
+ RUVElement *replica;
+ int cookie;
+ char csnstr1[CSN_STRSIZE];
+ char csnstr2[CSN_STRSIZE];
+
+ PR_ASSERT(NULL != ruv);
+ printf("{replicageneration} %s\n", ruv->replGen == NULL ? "NULL" : ruv->replGen);
+ for (replica = dl_get_first (ruv->elements, &cookie); replica;
+ replica = dl_get_next (ruv->elements, &cookie))
+ {
+ printf("{replica%s%s} %s %s\n",
+ replica->replica_purl == NULL ? "" : " ",
+ replica->replica_purl == NULL ? "" : replica->replica_purl,
+ csn_as_string(replica->min_csn, PR_FALSE, csnstr1),
+ csn_as_string(replica->csn, PR_FALSE, csnstr2));
+ }
+}
+
+void
+ruv_test()
+{
+ const struct berval *vals[5];
+ struct berval val0, val1, val2, val3;
+ RUV *ruv;
+ Slapi_Attr *attr;
+ Slapi_Value *sv0, *sv1, *sv2, *sv3;
+ int rc;
+ char csnstr[CSN_STRSIZE];
+ char *gen;
+ CSN *newcsn;
+ ReplicaId *ids;
+ int nids;
+ Slapi_Mod smods;
+ PRBool covers;
+
+ vals[0] = &val0;
+ vals[1] = &val1;
+ vals[2] = &val2;
+ vals[3] = &val3;
+ vals[4] = NULL;
+
+ val0.bv_val = "{replicageneration} 0440FDC0A33F";
+ val0.bv_len = strlen(val0.bv_val);
+
+ val1.bv_val = "{replica ldap://ggood.mcom.com:389} 12345670000000FE0000 12345671000000FE0000";
+ val1.bv_len = strlen(val1.bv_val);
+
+ val2.bv_val = "{replica ldaps://an-impossibly-long-host-name-that-drags-on-forever-and-forever.mcom.com:389} 11112110000000FF0000 11112111000000FF0000";
+ val2.bv_len = strlen(val2.bv_val);
+
+ val3.bv_val = "{replica} 12345672000000FD0000 12345673000000FD0000";
+ val3.bv_len = strlen(val3.bv_val);
+
+ rc = ruv_init_from_bervals(vals, &ruv);
+ ruv_dump_internal(ruv);
+
+ attr = slapi_attr_new();
+ attr = slapi_attr_init(attr, "ruvelement");
+ sv0 = slapi_value_new();
+ sv1 = slapi_value_new();
+ sv2 = slapi_value_new();
+ sv3 = slapi_value_new();
+ slapi_value_init_berval(sv0, &val0);
+ slapi_value_init_berval(sv1, &val1);
+ slapi_value_init_berval(sv2, &val2);
+ slapi_value_init_berval(sv3, &val3);
+ slapi_attr_add_value(attr, sv0);
+ slapi_attr_add_value(attr, sv1);
+ slapi_attr_add_value(attr, sv2);
+ slapi_attr_add_value(attr, sv3);
+ rc = ruv_init_from_slapi_attr(attr, &ruv);
+ ruv_dump_internal(ruv);
+
+ rc = ruv_delete_replica(ruv, 0xFF);
+ /* Should delete one replica */
+ ruv_dump_internal(ruv);
+
+ rc = ruv_delete_replica(ruv, 0xAA);
+ /* No such replica - should not do anything */
+ ruv_dump_internal(ruv);
+
+ rc = ruv_get_largest_csn_for_replica(ruv, 0xFE, &newcsn);
+ if (NULL != newcsn)
+ {
+ csn_as_string(newcsn, PR_FALSE, csnstr);
+ printf("Replica 0x%X has largest csn \"%s\"\n", 0xFE, csnstr);
+ }
+ else
+ {
+ printf("BAD - can't get largest CSN for replica 0x%X\n", 0xFE);
+ }
+
+ rc = ruv_get_smallest_csn_for_replica(ruv, 0xFE, &newcsn);
+ if (NULL != newcsn)
+ {
+ csn_as_string(newcsn, PR_FALSE, csnstr);
+ printf("Replica 0x%X has smallest csn \"%s\"\n", 0xFE, csnstr);
+ }
+ else
+ {
+ printf("BAD - can't get smallest CSN for replica 0x%X\n", 0xFE);
+ }
+ rc = ruv_get_largest_csn_for_replica(ruv, 0xAA, &newcsn);
+ printf("ruv_get_largest_csn_for_replica on non-existent replica ID returns %d\n", rc);
+
+ rc = ruv_get_smallest_csn_for_replica(ruv, 0xAA, &newcsn);
+ printf("ruv_get_smallest_csn_for_replica on non-existent replica ID returns %d\n", rc);
+
+ newcsn = csn_new_by_string("12345674000000FE0000"); /* Old replica 0xFE */
+ rc = ruv_set_csns(ruv, newcsn, "ldaps://foobar.mcom.com");
+ /* Should update replica FE's CSN */
+ ruv_dump_internal(ruv);
+
+ newcsn = csn_new_by_string("12345675000000FB0000"); /* New replica 0xFB */
+ rc = ruv_set_csns(ruv, newcsn, "ldaps://foobar.mcom.com");
+ /* Should get a new replica in the list with min == max csn */
+ ruv_dump_internal(ruv);
+
+ newcsn = csn_new_by_string("12345676000000FD0000"); /* Old replica 0xFD */
+ rc = ruv_set_csns(ruv, newcsn, "ldaps://foobar.mcom.com");
+ /* Should update replica 0xFD so new CSN is newer than min CSN */
+ ruv_dump_internal(ruv);
+
+ gen = ruv_get_replica_generation(ruv);
+ printf("replica generation is \"%s\"\n", gen);
+
+ newcsn = csn_new_by_string("12345673000000FE0000"); /* Old replica 0xFE */
+ covers = ruv_covers_csn(ruv, newcsn); /* should say "true" */
+
+ newcsn = csn_new_by_string("12345675000000FE0000"); /* Old replica 0xFE */
+ covers = ruv_covers_csn(ruv, newcsn); /* Should say "false" */
+
+ newcsn = csn_new_by_string("123456700000000A0000"); /* New replica 0A */
+ rc = ruv_set_min_csn(ruv, newcsn, "ldap://repl0a.mcom.com");
+ ruv_dump_internal(ruv);
+
+ newcsn = csn_new_by_string("123456710000000A0000"); /* New replica 0A */
+ rc = ruv_set_max_csn(ruv, newcsn, "ldap://repl0a.mcom.com");
+ ruv_dump_internal(ruv);
+
+ newcsn = csn_new_by_string("123456700000000B0000"); /* New replica 0B */
+ rc = ruv_set_max_csn(ruv, newcsn, "ldap://repl0b.mcom.com");
+ ruv_dump_internal(ruv);
+
+ newcsn = csn_new_by_string("123456710000000B0000"); /* New replica 0B */
+ rc = ruv_set_min_csn(ruv, newcsn, "ldap://repl0b.mcom.com");
+ ruv_dump_internal(ruv);
+
+ /* ONREPL test ruv enumeration */
+
+ rc = ruv_to_smod(ruv, &smods);
+
+ ruv_destroy(&ruv);
+}
+#endif /* TESTING */
diff --git a/ldap/servers/plugins/replication/repl5_ruv.h b/ldap/servers/plugins/replication/repl5_ruv.h
new file mode 100644
index 00000000..8484e74b
--- /dev/null
+++ b/ldap/servers/plugins/replication/repl5_ruv.h
@@ -0,0 +1,88 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+
+/* repl5_ruv.h - interface for replica update vector */
+
+#ifndef REPL5_RUV
+#define REPL5_RUV
+
+#include "slapi-private.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef struct _ruv RUV;
+
+enum
+{
+ RUV_SUCCESS=0,
+ RUV_BAD_DATA,
+ RUV_NOTFOUND,
+ RUV_MEMORY_ERROR,
+ RUV_NSPR_ERROR,
+ RUV_BAD_FORMAT,
+ RUV_UNKNOWN_ERROR,
+ RUV_ALREADY_EXIST,
+ RUV_CSNPL_ERROR,
+ RUV_COVERS_CSN
+};
+
+typedef struct ruv_enum_data
+{
+ CSN *csn;
+ CSN *min_csn;
+} ruv_enum_data;
+
+typedef int (*FNEnumRUV) (const ruv_enum_data *element, void *arg);
+int ruv_init_new (const char *replGen, ReplicaId rid, const char *purl, RUV **ruv);
+int ruv_init_from_bervals(struct berval** vals, RUV **ruv);
+int ruv_init_from_slapi_attr(Slapi_Attr *attr, RUV **ruv);
+int ruv_init_from_slapi_attr_and_check_purl(Slapi_Attr *attr, RUV **ruv, ReplicaId *rid);
+RUV* ruv_dup (const RUV *ruv);
+void ruv_destroy (RUV **ruv);
+void ruv_copy_and_destroy (RUV **srcruv, RUV **destruv);
+int ruv_replace_replica_purl (RUV *ruv, ReplicaId rid, const char *replica_purl);
+int ruv_delete_replica (RUV *ruv, ReplicaId rid);
+int ruv_add_replica (RUV *ruv, ReplicaId rid, const char *replica_purl);
+int ruv_add_index_replica (RUV *ruv, ReplicaId rid, const char *replica_purl, int index);
+PRBool ruv_contains_replica (const RUV *ruv, ReplicaId rid);
+int ruv_get_largest_csn_for_replica(const RUV *ruv, ReplicaId rid, CSN **csn);
+int ruv_get_smallest_csn_for_replica(const RUV *ruv, ReplicaId rid, CSN **csn);
+int ruv_set_csns(RUV *ruv, const CSN *csn, const char *replica_purl);
+int ruv_set_csns_keep_smallest(RUV *ruv, const CSN *csn);
+int ruv_set_max_csn(RUV *ruv, const CSN *max_csn, const char *replica_purl);
+int ruv_set_min_csn(RUV *ruv, const CSN *min_csn, const char *replica_purl);
+const char *ruv_get_purl_for_replica(const RUV *ruv, ReplicaId rid);
+char *ruv_get_replica_generation (const RUV *ruv);
+void ruv_set_replica_generation (RUV *ruv, const char *generation);
+PRBool ruv_covers_ruv(const RUV *covering_ruv, const RUV *covered_ruv);
+PRBool ruv_covers_csn(const RUV *ruv, const CSN *csn);
+PRBool ruv_covers_csn_strict(const RUV *ruv, const CSN *csn);
+int ruv_get_min_csn(const RUV *ruv, CSN **csn);
+int ruv_get_max_csn(const RUV *ruv, CSN **csn);
+int ruv_enumerate_elements (const RUV *ruv, FNEnumRUV fn, void *arg);
+int ruv_to_smod(const RUV *ruv, Slapi_Mod *smod);
+int ruv_last_modified_to_smod(const RUV *ruv, Slapi_Mod *smod);
+int ruv_to_bervals(const RUV *ruv, struct berval ***bvals);
+PRInt32 ruv_replica_count (const RUV *ruv);
+char **ruv_get_referrals(const RUV *ruv);
+void ruv_dump(const RUV *ruv, char *ruv_name, PRFileDesc *prFile);
+int ruv_add_csn_inprogress (RUV *ruv, const CSN *csn);
+int ruv_cancel_csn_inprogress (RUV *ruv, const CSN *csn);
+int ruv_update_ruv (RUV *ruv, const CSN *csn, const char *replica_purl, PRBool isLocal);
+int ruv_move_local_supplier_to_first(RUV *ruv, ReplicaId rid);
+int ruv_get_first_id_and_purl(RUV *ruv, ReplicaId *rid, char **replica_purl );
+int ruv_local_contains_supplier(RUV *ruv, ReplicaId rid);
+/* returns true if the ruv has any csns, false otherwise - used for testing
+ whether or not an RUV is empty */
+PRBool ruv_has_csns(const RUV *ruv);
+PRBool ruv_is_newer (Object *sruv, Object *cruv);
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/ldap/servers/plugins/replication/repl5_schedule.c b/ldap/servers/plugins/replication/repl5_schedule.c
new file mode 100644
index 00000000..427e79a9
--- /dev/null
+++ b/ldap/servers/plugins/replication/repl5_schedule.c
@@ -0,0 +1,742 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+/* repl5_schedule.c */
+/*
+
+The schedule object implements the scheduling policy for a DS 5.0 replication
+supplier.
+
+Methods:
+schedule_set() - sets the schedule
+schedule_get() - gets the schedule
+schedule_in_window_now() - returns TRUE if a replication session
+ should commence.
+schedule_next() - returns the next time that replication is
+ scheduled to occur.
+schedule_notify() - called to inform the scheduler when entries
+ have been updated.
+schedule_set_priority_attributes() - sets the attributes that are
+ considered "high priority". A modification to one of these attributes
+ will cause replication to commence asap, overriding the startup
+ delay and maximum backlog. Also includes an additional parameter
+ that controls whether priority attributes are propagated regardless
+ of the scheduling window, e.g. it's possible to configure things
+ so that password changes get propagated even if we're not in a
+ replication window.
+schedule_set_startup_delay() - sets the time that replication should
+ wait before commencing replication sessions.
+schedule_set_maximum_backlog() - sets the maximum number of updates
+ which can occur before replication will commence. If the backlog
+ threshhold is exceeded, then replication will commence ASAP,
+ overriding the startup delay.
+
+*/
+
+/* ONREPL - I made a simplifying assumption that a schedule item does not
+ cross day boundaries. Implementing this is hard because we search
+ for the items for a particular day only based on the item's staring time.
+ For instance if the current time is tuesday morning, we would not consider
+ the item that started on monday and continued through tuesday.
+ To simulate an item that crosses day boundaries, you can create 2 items -
+ one for the time in the first day and one for the time in the second.
+ We could do this internally by allowing items do span 2 days and
+ splitting them ourselves. This, however, is not currently implemented */
+
+#include "slapi-plugin.h"
+#include "repl5.h"
+
+#include <ctype.h> /* For isdigit() */
+
+/* from proto-slap.h */
+char *get_timestring(time_t *t);
+void free_timestring(char *timestr);
+
+typedef struct schedule_item {
+ struct schedule_item *next;
+ PRUint32 start; /* Start time, given as seconds after midnight */
+ PRUint32 end; /* End time */
+ unsigned char dow; /* Days of week, LSB = Sunday */
+} schedule_item;
+
+typedef struct schedule {
+ const char *session_id;
+ size_t max_backlog;
+ size_t startup_delay;
+ schedule_item *schedule_list; /* Linked list of schedule windows */
+ char **prio_attrs; /* Priority attributes - start replication now */
+ int prio_attrs_override_schedule;
+ PRTime last_session_end;
+ int last_session_status;
+ PRTime last_successful_session_end;
+ window_state_change_callback callback_fn; /* function to call when window opens/closes */
+ void *callback_arg; /* argument to pass to the window state change callback */
+ Slapi_Eq_Context pending_event; /* event scheduled with the event queue */
+ PRLock *lock;
+} schedule;
+
+/* Forward declarations */
+static schedule_item *parse_schedule_value(const Slapi_Value *v);
+static void schedule_window_state_change_event (Schedule *sch);
+static void unschedule_window_state_change_event (Schedule *sch);
+static void window_state_changed (time_t when, void *arg);
+static int schedule_in_window_now_nolock(Schedule *sch);
+static schedule_item* get_current_schedule_item (Schedule *sch);
+static time_t PRTime2time_t (PRTime tm);
+static PRTime schedule_next_nolock (Schedule *sch, PRBool start);
+static void free_schedule_list(schedule_item **schedule_list);
+
+#define SECONDS_PER_MINUTE 60
+#define SECONDS_PER_HOUR (60 * SECONDS_PER_MINUTE)
+#define SECONDS_PER_DAY (24 * SECONDS_PER_HOUR)
+#define DAYS_PER_WEEK 7
+#define ALL_DAYS 0x7F /* Bit mask */
+
+
+
+/*
+ * Create a new schedule object and return a pointer to it.
+ */
+Schedule*
+schedule_new(window_state_change_callback callback_fn, void *callback_arg, const char *session_id)
+{
+ Schedule *sch = NULL;
+ sch = (Schedule *)slapi_ch_calloc(1, sizeof(struct schedule));
+
+ sch->session_id = session_id ? session_id : "";
+ sch->callback_fn = callback_fn;
+ sch->callback_arg = callback_arg;
+
+ if ((sch->lock = PR_NewLock()) == NULL)
+ {
+ slapi_ch_free((void **)&sch);
+ }
+
+ return sch;
+}
+
+
+void
+schedule_destroy(Schedule *s)
+{
+ int i;
+
+ /* unschedule update window event if exists */
+ unschedule_window_state_change_event (s);
+
+ if (s->schedule_list)
+ {
+ free_schedule_list (&s->schedule_list);
+ }
+
+ if (NULL != s->prio_attrs)
+ {
+ for (i = 0; NULL != s->prio_attrs[i]; i++)
+ {
+ slapi_ch_free((void **)&(s->prio_attrs[i]));
+ }
+ slapi_ch_free((void **)&(s->prio_attrs));
+ }
+ PR_DestroyLock(s->lock);
+ s->lock = NULL;
+ slapi_ch_free((void **)&s);
+}
+
+static void
+free_schedule_list(schedule_item **schedule_list)
+{
+ schedule_item *si = *schedule_list;
+ schedule_item *tmp_si;
+ while (NULL != si)
+ {
+ tmp_si = si->next;
+ slapi_ch_free((void **)&si);
+ si = tmp_si;
+ }
+ *schedule_list = NULL;
+}
+
+
+
+/*
+ * Sets the schedule. Returns 0 if all of the schedule lines were
+ * correctly parsed and the new schedule has been put into effect.
+ * Returns -1 if one or more of the schedule items could not be
+ * parsed. If -1 is returned, then no changes have been made to the
+ * current schedule.
+ */
+int
+schedule_set(Schedule *sch, Slapi_Attr *attr)
+{
+ int return_value;
+ schedule_item *si = NULL;
+ schedule_item *new_schedule_list = NULL;
+ int valid = 1;
+
+ if (NULL != attr)
+ {
+ int ind;
+ Slapi_Value *sval;
+ ind = slapi_attr_first_value(attr, &sval);
+ while (ind >= 0)
+ {
+ si = parse_schedule_value(sval);
+ if (NULL == si)
+ {
+ valid = 0;
+ break;
+ }
+ /* Put at head of linked list */
+ si->next = new_schedule_list;
+ new_schedule_list = si;
+ ind = slapi_attr_next_value(attr, ind, &sval);
+ }
+ }
+
+ if (!valid)
+ {
+ /* deallocate any new schedule items */
+ free_schedule_list(&new_schedule_list);
+ return_value = -1;
+ }
+ else
+ {
+ PR_Lock(sch->lock);
+
+ /* if there is an update window event scheduled - unschedule it */
+ unschedule_window_state_change_event (sch);
+
+ free_schedule_list(&sch->schedule_list);
+ sch->schedule_list = new_schedule_list;
+
+ /* schedule an event to notify the caller about openning/closing of the update window */
+ schedule_window_state_change_event (sch);
+
+ PR_Unlock(sch->lock);
+ return_value = 0;
+ }
+ return return_value;
+}
+
+
+
+/*
+ * Returns the schedule.
+ */
+char **
+schedule_get(Schedule *sch)
+{
+ char **return_value = NULL;
+
+ return return_value;
+}
+
+
+
+/*
+ * Return an integer corresponding to the day of the week for
+ * "when".
+ */
+static PRInt32
+day_of_week(PRTime when)
+{
+
+ PRExplodedTime exp;
+
+ PR_ExplodeTime(when, PR_LocalTimeParameters, &exp);
+ return(exp.tm_wday);
+}
+
+
+/*
+ * Return the number of seconds between "when" and the
+ * most recent midnight.
+ */
+static PRUint32
+seconds_since_midnight(PRTime when)
+{
+ PRExplodedTime exp;
+
+ PR_ExplodeTime(when, PR_LocalTimeParameters, &exp);
+ return(exp.tm_hour * 3600 + exp.tm_min * 60 + exp.tm_sec);
+}
+
+
+/*
+ * Return 1 if "now" is within the schedule window
+ * specified by "si", 0 otherwise.
+ */
+static int
+time_in_window(PRTime now, schedule_item *si)
+{
+ unsigned char dow = 1 << day_of_week(now);
+ int return_value = 0;
+
+ if (dow & si->dow)
+ {
+ PRUint32 nowsec = seconds_since_midnight(now);
+
+ return_value = (nowsec >= si->start) && (nowsec <= si->end);
+ }
+
+ return return_value;
+}
+
+
+
+/*
+ * Returns a non-zero value if the current time is within a
+ * replication window and if scheduling constraints are all met.
+ * Otherwise, returns zero.
+ */
+
+int
+schedule_in_window_now (Schedule *sch)
+{
+ int rc;
+
+ PR_ASSERT(NULL != sch);
+ PR_Lock(sch->lock);
+
+ rc = schedule_in_window_now_nolock(sch);
+
+ PR_Unlock(sch->lock);
+
+ return rc;
+}
+
+/* Must be called under sch->lock */
+static int
+schedule_in_window_now_nolock(Schedule *sch)
+{
+ int return_value = 0;
+
+ if (NULL == sch->schedule_list)
+ {
+ /* Absence of a schedule is the same as 0000-2359 0123456 */
+ return_value = 1;
+ }
+ else
+ {
+ schedule_item *si = sch->schedule_list;
+ PRTime now;
+ now = PR_Now();
+ while (NULL != si)
+ {
+ if (time_in_window(now, si))
+ {
+ /* XXX check backoff timers??? */
+ return_value = 1;
+ break;
+ }
+ si = si->next;
+ }
+ }
+
+ return return_value;
+}
+
+
+
+/*
+ * Calculate the next time (expressed as a PRTime) when this
+ * schedule item will change state (from open to close or vice versa).
+ */
+static PRTime
+next_change_time(schedule_item *si, PRTime now, PRBool start)
+{
+ PRUint32 nowsec = seconds_since_midnight(now);
+ PRUint32 sec_til_change;
+ PRUint32 change_time;
+ PRExplodedTime exp;
+ PRInt32 dow = day_of_week(now);
+ unsigned char dow_bit = 1 << dow;
+ unsigned char next_dow;
+
+ if (start) /* we are looking for the next window opening */
+ {
+ change_time = si->start;
+ }
+ else /* we are looking for the next window closing */
+ {
+ /* open range is inclusive - so we need to add a minute if we are looking for close time */
+ change_time = si->end + SECONDS_PER_MINUTE;
+ }
+
+ /* we are replicating today and next change is also today */
+ if ((dow_bit & si->dow) && (nowsec < change_time))
+ {
+ sec_til_change = change_time - nowsec;
+ }
+ else /* not replicating today or the change already occured today */
+ {
+ int i;
+
+ /* find next day when we replicate */
+ for (i = 1; i <= DAYS_PER_WEEK; i++)
+ {
+ next_dow = 1 << ((dow + i) % DAYS_PER_WEEK);
+ if (next_dow & si->dow)
+ break;
+ }
+
+ sec_til_change = change_time + i * SECONDS_PER_DAY - nowsec;
+ }
+
+ PR_ExplodeTime(now, PR_LocalTimeParameters, &exp);
+ exp.tm_sec += sec_til_change;
+
+
+ PR_NormalizeTime(&exp, PR_LocalTimeParameters);
+ return PR_ImplodeTime(&exp);
+}
+
+
+
+/*
+ * Returns the next time that replication is scheduled to occur.
+ * Returns 0 if there is no time in the future that replication
+ * will begin (e.g. there's no schedule at all).
+ */
+PRTime
+schedule_next(Schedule *sch)
+{
+ PRTime tm;
+
+ PR_ASSERT(NULL != sch);
+ PR_Lock(sch->lock);
+
+ tm = schedule_next_nolock (sch, PR_TRUE);
+
+ PR_Unlock(sch->lock);
+
+ return tm;
+}
+
+/* Must be called under sch->lock */
+static PRTime
+schedule_next_nolock (Schedule *sch, PRBool start)
+{
+
+ PRTime closest_time = LL_Zero();
+
+ if (NULL != sch->schedule_list)
+ {
+ schedule_item *si = sch->schedule_list;
+ PRTime now = PR_Now();
+ unsigned char dow = 1 << day_of_week(now);
+
+ while (NULL != si)
+ {
+ PRTime tmp_time;
+
+ /* Check if this item's change time is sooner than the others */
+ tmp_time = next_change_time(si, now, start);
+ if (LL_IS_ZERO(closest_time))
+ {
+ LL_ADD(closest_time, tmp_time, LL_Zero()); /* Really just an asignment */
+ }
+ else if (LL_CMP(tmp_time, <, closest_time))
+ {
+ LL_ADD(closest_time, tmp_time, LL_Zero()); /* Really just an asignment */
+ }
+
+ si = si->next;
+ }
+ }
+
+ return closest_time;
+}
+
+
+
+
+/*
+ * Called by the enclosing object (replsupplier) when a change within the
+ * replicated area has occurred. This allows the scheduler to update its
+ * internal counters, timers, etc. Returns a non-zero value if replication
+ * should commence, zero if it should not.
+ */
+int
+schedule_notify(Schedule *sch, Slapi_PBlock *pb)
+{
+ int return_value = 0;
+
+ return return_value;
+}
+
+
+
+
+/*
+ * Provide a list of attributes which, if changed,
+ * will cause replication to commence as soon as possible. There
+ * is also a flag that tells the scheduler if the update of a
+ * priority attribute should cause the schedule to be overridden,
+ * e.g. if the administrator wants password changes to propagate
+ * even if not in a replication window.
+ *
+ * This function consumes "prio_attrs" and assumes management
+ * of the memory.
+ */
+void
+schedule_set_priority_attributes(Schedule *sch, char **prio_attrs, int override_schedule)
+{
+ PR_ASSERT(NULL != sch);
+ PR_Lock(sch->lock);
+ if (NULL != sch->prio_attrs)
+ {
+ int i;
+ for (i = 0; NULL != prio_attrs[i]; i++) {
+ slapi_ch_free((void **)&sch->prio_attrs[i]);
+ }
+ slapi_ch_free((void **)&sch->prio_attrs);
+ }
+ sch->prio_attrs = prio_attrs;
+ sch->prio_attrs_override_schedule = override_schedule;
+
+ PR_Unlock(sch->lock);
+}
+
+
+
+
+
+/*
+ * Set the time, in seconds, that replication will wait after a change is
+ * available before propagating it. This capability will allow multiple
+ * updates to be coalesced into a single replication session.
+ */
+void
+schedule_set_startup_delay(Schedule *sch, size_t startup_delay)
+{
+ PR_ASSERT(NULL != sch);
+ PR_Lock(sch->lock);
+ sch->startup_delay = startup_delay;
+ PR_Unlock(sch->lock);
+}
+
+
+
+
+
+/*
+ * Set the maximum number of pending changes allowed to accumulate
+ * before a replication session is begun.
+ */
+void
+schedule_set_maximum_backlog(Schedule *sch, size_t max_backlog)
+{
+ PR_ASSERT(NULL != sch);
+ PR_Lock(sch->lock);
+ sch->max_backlog = max_backlog;
+ PR_Unlock(sch->lock);
+}
+
+
+
+
+
+/*
+ * Notify the scheduler that a replication session completed at a certain
+ * time. There is also a status argument that says more about the session's
+ * termination (normal, abnormal), which the scheduler uses in determining
+ * the backoff strategy.
+ */
+void
+schedule_notify_session(Schedule *sch, PRTime session_end_time, unsigned int status)
+{
+ PR_ASSERT(NULL != sch);
+ PR_Lock(sch->lock);
+ sch->last_session_end = session_end_time;
+ sch->last_session_status = status;
+ if (REPLICATION_SESSION_SUCCESS == status)
+ {
+ sch->last_successful_session_end = session_end_time;
+ }
+ PR_Unlock(sch->lock);
+}
+
+/* schedule an event that will fire the next time the update window state
+ changes from open to closed or vice versa */
+static void
+schedule_window_state_change_event (Schedule *sch)
+{
+ time_t wakeup_time;
+ PRTime tm;
+ int window_opened;
+ char *timestr = NULL;
+
+ /* if we have a schedule and a callback function is registerd -
+ register an event with the event queue */
+ if (sch->schedule_list && sch->callback_fn)
+ {
+ /* ONREPL what if the window is really small and by the time we are done
+ with the computation - we cross window boundary.
+ I think we should put some constrains on schedule to avoid that */
+
+ window_opened = schedule_in_window_now_nolock(sch);
+
+ tm = schedule_next_nolock(sch, !window_opened);
+
+ wakeup_time = PRTime2time_t (tm);
+
+ /* schedule the event */
+ sch->pending_event = slapi_eq_once(window_state_changed, sch, wakeup_time);
+
+ timestr = get_timestring(&wakeup_time);
+ slapi_log_error (SLAPI_LOG_REPL, repl_plugin_name, "%s: Update window will %s at %s\n",
+ sch->session_id,
+ window_opened ? "close" : "open", timestr);
+ free_timestring(timestr);
+ timestr = NULL;
+ }
+}
+
+/* this function is called by the even queue the next time
+ the window is opened or closed */
+static void
+window_state_changed (time_t when, void *arg)
+{
+ Schedule *sch = (Schedule*)arg;
+ int open;
+
+ PR_ASSERT (sch);
+
+ PR_Lock(sch->lock);
+
+ open = schedule_in_window_now_nolock(sch);
+
+ slapi_log_error(SLAPI_LOG_REPL, repl_plugin_name, "%s: Update window is now %s\n",
+ sch->session_id,
+ open ? "open" : "closed");
+
+ /* schedule next event */
+ schedule_window_state_change_event (sch);
+
+ /* notify the agreement */
+ sch->callback_fn (sch->callback_arg, open);
+
+ PR_Unlock(sch->lock);
+}
+
+/* cancel the event registered with the event queue */
+static void
+unschedule_window_state_change_event (Schedule *sch)
+{
+ if (sch->pending_event)
+ {
+ slapi_eq_cancel(sch->pending_event);
+ sch->pending_event = NULL;
+ }
+}
+
+static time_t
+PRTime2time_t (PRTime tm)
+{
+ PRInt64 rt;
+
+ PR_ASSERT (tm);
+
+ LL_DIV(rt, tm, PR_USEC_PER_SEC);
+
+ return (time_t)rt;
+}
+
+/*
+ * Parse a schedule line.
+ * The format is:
+ * <start>-<end> <day_of_week>
+ * <start> and <end> are in 24-hour time
+ * <day_of_week> is like cron(5): 0 = Sunday, 1 = Monday, etc.
+ *
+ * The schedule item "*" is equivalen to 0000-2359 0123456
+ *
+ * Returns a pointer to a schedule item on success, NULL if the
+ * schedule item cannot be parsed.
+ */
+static schedule_item *
+parse_schedule_value(const Slapi_Value *v)
+{
+#define RANGE_VALID(p, limit) \
+ ((p + 9) < limit && \
+ isdigit(p[0]) && \
+ isdigit(p[1]) && \
+ isdigit(p[2]) && \
+ isdigit(p[3]) && \
+ ('-' == p[4]) && \
+ isdigit(p[5]) && \
+ isdigit(p[6]) && \
+ isdigit(p[7]) && \
+ isdigit(p[8]))
+
+ schedule_item *si = NULL;
+ int valid = 0;
+ const struct berval *sch_bval;
+
+ if (NULL != v && (sch_bval = slapi_value_get_berval(v)) != NULL &&
+ NULL != sch_bval && sch_bval->bv_len > 0 && NULL != sch_bval->bv_val )
+ {
+ char *p = sch_bval->bv_val;
+ char *limit = p + sch_bval->bv_len;
+
+ si = (schedule_item *)slapi_ch_malloc(sizeof(schedule_item));
+ si->next = NULL;
+ si->start = 0UL;
+ si->end = SECONDS_PER_DAY;
+ si->dow = ALL_DAYS;
+
+ if (*p == '*')
+ {
+ valid = 1;
+ goto done;
+ }
+ else
+ {
+ if (RANGE_VALID(p, limit))
+ {
+ si->start = ((strntoul(p, 2, 10) * 60) +
+ strntoul(p + 2, 2, 10)) * 60;
+ p += 5;
+ si->end = ((strntoul(p, 2, 10) * 60) +
+ strntoul(p + 2, 2, 10)) * 60;
+ p += 4;
+
+ /* ONREPL - for now wi don't allow items that span multiple days.
+ See note in the beginning of the file for more details. */
+ /* ONREPL - we should also decide on the minimum of the item size */
+ if (si->start > si->end)
+ {
+ valid = 0;
+ goto done;
+ }
+
+ if (p < limit && ' ' == *p)
+ {
+ /* Specific days of week */
+ si->dow = 0;
+ while (++p < limit)
+ {
+ if (!isdigit(*p))
+ {
+ valid = 0;
+ goto done;
+ }
+ si->dow |= (1 << strntoul(p, 1, 10));
+
+ }
+ }
+ valid = 1;
+ }
+ }
+ }
+
+done:
+ if (!valid)
+ {
+ slapi_ch_free((void **)&si);
+ }
+ return si;
+}
diff --git a/ldap/servers/plugins/replication/repl5_tot_protocol.c b/ldap/servers/plugins/replication/repl5_tot_protocol.c
new file mode 100644
index 00000000..45e91f3b
--- /dev/null
+++ b/ldap/servers/plugins/replication/repl5_tot_protocol.c
@@ -0,0 +1,372 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+
+/* repl5_tot_protocol.c */
+/*
+
+ The tot_protocol object implements the DS 5.0 multi-master total update
+ replication protocol, used to (re)populate a replica.
+
+*/
+
+#include "repl.h"
+#include "repl5.h"
+#include "repl5_prot_private.h"
+
+/* Private data structures */
+typedef struct repl5_tot_private
+{
+ Repl_Protocol *rp;
+ Repl_Agmt *ra;
+ PRLock *lock;
+ PRUint32 eventbits;
+} repl5_tot_private;
+
+typedef struct callback_data
+{
+ Private_Repl_Protocol *prp;
+ int rc;
+ unsigned long num_entries;
+ time_t sleep_on_busy;
+ time_t last_busy;
+} callback_data;
+
+/*
+ * Number of window seconds to wait until we programmatically decide
+ * that the replica has got out of BUSY state
+ */
+#define SLEEP_ON_BUSY_WINDOW (10)
+
+/* Helper functions */
+static void get_result (int rc, void *cb_data);
+static int send_entry (Slapi_Entry *e, void *callback_data);
+static void repl5_tot_delete(Private_Repl_Protocol **prp);
+
+/*
+ * Completely refresh a replica. The basic protocol interaction goes
+ * like this:
+ * - Acquire Replica by sending a StartReplicationRequest extop, with the
+ * total update protocol OID and supplier's ruv.
+ * - Send a series of extended operations containing entries.
+ * - send an EndReplicationRequest extended operation
+ */
+static void
+repl5_tot_run(Private_Repl_Protocol *prp)
+{
+ int rc;
+ callback_data cb_data;
+ Slapi_PBlock *pb;
+ LDAPControl **ctrls;
+ PRBool replica_acquired = PR_FALSE;
+ char *hostname = NULL;
+ int portnum = 0;
+ Slapi_DN *area_sdn = NULL;
+ CSN *remote_schema_csn = NULL;
+
+ PR_ASSERT(NULL != prp);
+
+ prp->stopped = 0;
+ if (prp->terminate)
+ {
+ prp->stopped = 1;
+ goto done;
+ }
+
+ conn_set_timeout(prp->conn, agmt_get_timeout(prp->agmt));
+
+ /* acquire remote replica */
+ agmt_set_last_init_start(prp->agmt, current_time());
+ rc = acquire_replica (prp, REPL_NSDS50_TOTAL_PROTOCOL_OID, NULL /* ruv */);
+ /* We never retry total protocol, even in case a transient error.
+ This is because if somebody already updated the replica we don't
+ want to do it again */
+ if (rc != ACQUIRE_SUCCESS)
+ {
+ int optype, ldaprc;
+ conn_get_error(prp->conn, &optype, &ldaprc);
+ agmt_set_last_init_status(prp->agmt, ldaprc,
+ prp->last_acquire_response_code, NULL);
+ goto done;
+ }
+ else if (prp->terminate)
+ {
+ conn_disconnect(prp->conn);
+ prp->stopped = 1;
+ goto done;
+ }
+
+ hostname = agmt_get_hostname(prp->agmt);
+ portnum = agmt_get_port(prp->agmt);
+
+ agmt_set_last_init_status(prp->agmt, 0, 0, "Total schema update in progress");
+ remote_schema_csn = agmt_get_consumer_schema_csn ( prp->agmt );
+ rc = conn_push_schema(prp->conn, &remote_schema_csn);
+ if (CONN_SCHEMA_UPDATED != rc && CONN_SCHEMA_NO_UPDATE_NEEDED != rc)
+ {
+ slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name, "Warning: unable to "
+ "replicate schema to host %s, port %d. Continuing with "
+ "total update session.\n",
+ hostname, portnum);
+ /* But keep going */
+ agmt_set_last_init_status(prp->agmt, 0, rc, "Total schema update failed");
+ }
+ else
+ {
+ agmt_set_last_init_status(prp->agmt, 0, 0, "Total schema update succeeded");
+ }
+
+ /* ONREPL - big assumption here is that entries a returned in the id order
+ and that the order implies that perent entry is always ahead of the
+ child entry in the list. Otherwise, the consumer would not be
+ properly updated because bulk import at the moment skips orphand entries. */
+ /* XXXggood above assumption may not be valid if orphaned entry moved???? */
+
+ agmt_set_last_init_status(prp->agmt, 0, 0, "Total update in progress");
+
+ slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name, "Beginning total update of replica "
+ "\"%s\".\n", agmt_get_long_name(prp->agmt));
+ pb = slapi_pblock_new ();
+
+ /* RMREPL - need to send schema here */
+
+ area_sdn = agmt_get_replarea(prp->agmt);
+ /* we need to provide managedsait control so that referral entries can
+ be replicated */
+ ctrls = (LDAPControl **)slapi_ch_calloc (3, sizeof (LDAPControl *));
+ ctrls[0] = create_managedsait_control ();
+ ctrls[1] = create_backend_control(area_sdn);
+
+ slapi_search_internal_set_pb (pb, slapi_sdn_get_dn (area_sdn),
+ LDAP_SCOPE_SUBTREE, "(|(objectclass=ldapsubentry)(objectclass=nstombstone)(nsuniqueid=*))", NULL, 0, ctrls, NULL,
+ repl_get_plugin_identity (PLUGIN_MULTIMASTER_REPLICATION), 0);
+ cb_data.prp = prp;
+ cb_data.rc = 0;
+ cb_data.num_entries = 0UL;
+ cb_data.sleep_on_busy = 0UL;
+ cb_data.last_busy = current_time ();
+
+ /* this search get all the entries from the replicated area including tombstones
+ and referrals */
+ slapi_search_internal_callback_pb (pb, &cb_data /* callback data */,
+ get_result /* result callback */,
+ send_entry /* entry callback */,
+ NULL /* referral callback*/);
+ slapi_pblock_destroy (pb);
+ agmt_set_last_init_end(prp->agmt, current_time());
+ rc = cb_data.rc;
+ release_replica(prp);
+ slapi_sdn_free(&area_sdn);
+
+ if (rc != LDAP_SUCCESS)
+ {
+ slapi_log_error (SLAPI_LOG_REPL, repl_plugin_name, "%s: repl5_tot_run: "
+ "failed to obtain data to send to the consumer; LDAP error - %d\n",
+ agmt_get_long_name(prp->agmt), rc);
+ agmt_set_last_init_status(prp->agmt, rc, 0, "Total update aborted");
+ } else {
+ slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name, "Finished total update of replica "
+ "\"%s\". Sent %d entries.\n", agmt_get_long_name(prp->agmt), cb_data.num_entries);
+ agmt_set_last_init_status(prp->agmt, 0, 0, "Total update succeeded");
+ }
+
+done:
+ slapi_ch_free_string(&hostname);
+ prp->stopped = 1;
+}
+
+static int
+repl5_tot_stop(Private_Repl_Protocol *prp)
+{
+ int return_value;
+ int seconds = 600;
+ PRIntervalTime start, maxwait, now;
+
+ prp->terminate = 1;
+ maxwait = PR_SecondsToInterval(seconds);
+ start = PR_IntervalNow();
+ now = start;
+ while (!prp->stopped && ((now - start) < maxwait))
+ {
+ DS_Sleep(PR_SecondsToInterval(1));
+ now = PR_IntervalNow();
+ }
+ if (!prp->stopped)
+ {
+ /* Isn't listening. Disconnect from the replica. */
+ slapi_log_error (SLAPI_LOG_REPL, repl_plugin_name, "repl5_tot_run: "
+ "protocol not stopped after waiting for %d seconds "
+ "for agreement %s\n", PR_IntervalToSeconds(now-start),
+ agmt_get_long_name(prp->agmt));
+ conn_disconnect(prp->conn);
+ return_value = -1;
+ }
+ else
+ {
+ return_value = 0;
+ }
+
+ return return_value;
+}
+
+
+
+static int
+repl5_tot_status(Private_Repl_Protocol *prp)
+{
+ int return_value = 0;
+ return return_value;
+}
+
+
+
+static void
+repl5_tot_noop(Private_Repl_Protocol *prp)
+{
+ /* noop */
+}
+
+
+Private_Repl_Protocol *
+Repl_5_Tot_Protocol_new(Repl_Protocol *rp)
+{
+ repl5_tot_private *rip = NULL;
+ Private_Repl_Protocol *prp = (Private_Repl_Protocol *)slapi_ch_malloc(sizeof(Private_Repl_Protocol));
+ prp->delete = repl5_tot_delete;
+ prp->run = repl5_tot_run;
+ prp->stop = repl5_tot_stop;
+ prp->status = repl5_tot_status;
+ prp->notify_update = repl5_tot_noop;
+ prp->notify_agmt_changed = repl5_tot_noop;
+ prp->notify_window_opened = repl5_tot_noop;
+ prp->notify_window_closed = repl5_tot_noop;
+ prp->update_now = repl5_tot_noop;
+ if ((prp->lock = PR_NewLock()) == NULL)
+ {
+ goto loser;
+ }
+ if ((prp->cvar = PR_NewCondVar(prp->lock)) == NULL)
+ {
+ goto loser;
+ }
+ prp->stopped = 1;
+ prp->terminate = 0;
+ prp->eventbits = 0;
+ prp->conn = prot_get_connection(rp);
+ prp->agmt = prot_get_agreement(rp);
+ rip = (void *)slapi_ch_malloc(sizeof(repl5_tot_private));
+ rip->rp = rp;
+ prp->private = (void *)rip;
+ prp->replica_acquired = PR_FALSE;
+ return prp;
+loser:
+ repl5_tot_delete(&prp);
+ return NULL;
+}
+
+static void
+repl5_tot_delete(Private_Repl_Protocol **prp)
+{
+}
+
+static
+void get_result (int rc, void *cb_data)
+{
+ PR_ASSERT (cb_data);
+ ((callback_data*)cb_data)->rc = rc;
+}
+
+static
+int send_entry (Slapi_Entry *e, void *cb_data)
+{
+ int rc;
+ Private_Repl_Protocol *prp;
+ BerElement *bere;
+ struct berval *bv;
+ unsigned long *num_entriesp;
+ time_t *sleep_on_busyp;
+ time_t *last_busyp;
+
+ PR_ASSERT (cb_data);
+
+ prp = ((callback_data*)cb_data)->prp;
+ num_entriesp = &((callback_data *)cb_data)->num_entries;
+ sleep_on_busyp = &((callback_data *)cb_data)->sleep_on_busy;
+ last_busyp = &((callback_data *)cb_data)->last_busy;
+ PR_ASSERT (prp);
+
+ if (prp->terminate)
+ {
+ conn_disconnect(prp->conn);
+ prp->stopped = 1;
+ ((callback_data*)cb_data)->rc = -1;
+ return -1;
+ }
+
+ /* skip ruv tombstone - need to do this because it might be
+ more up to date then the data we are sending to the client.
+ RUV is sent separately via the protocol */
+ if (is_ruv_tombstone_entry (e))
+ return 0;
+
+ /* ONREPL we would purge copiedFrom and copyingFrom here but I decided against it.
+ Instead, it will get removed when this replica stops being 4.0 consumer and
+ then propagated to all its consumer */
+
+ /* convert the entry to the on the wire format */
+ bere = entry2bere(e);
+ if (bere == NULL)
+ {
+ slapi_log_error(SLAPI_LOG_REPL, repl_plugin_name, "%s: send_entry: Encoding Error\n",
+ agmt_get_long_name(prp->agmt));
+ ((callback_data*)cb_data)->rc = -1;
+ return -1;
+ }
+
+ rc = ber_flatten(bere, &bv);
+ ber_free (bere, 1);
+ if (rc != 0)
+ {
+ ((callback_data*)cb_data)->rc = -1;
+ return -1;
+ }
+
+ do {
+ /* push the entry to the consumer */
+ rc = conn_send_extended_operation(prp->conn, REPL_NSDS50_REPLICATION_ENTRY_REQUEST_OID,
+ bv /* payload */, NULL /* retoidp */,
+ NULL /* retdatap */, NULL /* update_control */,
+ NULL /* returned_controls */);
+
+ if (rc == CONN_BUSY) {
+ time_t now = current_time ();
+ if ((now - *last_busyp) < (*sleep_on_busyp + 10)) {
+ *sleep_on_busyp +=5;
+ }
+ else {
+ *sleep_on_busyp = 5;
+ }
+ *last_busyp = now;
+
+ slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name,
+ "Replica \"%s\" is busy. Waiting %ds while"
+ " it finishes processing its current import queue\n",
+ agmt_get_long_name(prp->agmt), *sleep_on_busyp);
+ DS_Sleep(PR_SecondsToInterval(*sleep_on_busyp));
+ }
+ }
+ while (rc == CONN_BUSY);
+
+ ber_bvfree(bv);
+ (*num_entriesp)++;
+
+ if (CONN_OPERATION_SUCCESS == rc) {
+ return 0;
+ } else {
+ ((callback_data*)cb_data)->rc = rc;
+ return -1;
+ }
+}
+
diff --git a/ldap/servers/plugins/replication/repl5_total.c b/ldap/servers/plugins/replication/repl5_total.c
new file mode 100644
index 00000000..66dcc353
--- /dev/null
+++ b/ldap/servers/plugins/replication/repl5_total.c
@@ -0,0 +1,869 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+
+
+/*
+ repl5_total.c - code that implements a total replica update.
+
+ The requestValue of the NSDS50ReplicationEntry looks like this:
+
+ requestValue ::= SEQUENCE {
+ uniqueid OCTET STRING,
+ dn LDAPDN,
+ annotatedAttributes AnnotatedAttributeList
+ }
+
+ AnnotatedAttributeList ::= SET OF SEQUENCE {
+ attributeType AttributeDescription,
+ attributeDeletionCSN OCTET STRING OPTIONAL,
+ attributeDeleted BOOLEAN DEFAULT FALSE,
+ annotatedValues SET OF AnnotatedValue
+ }
+
+ AnnotatedValue ::= SEQUENCE {
+ value AttributeValue,
+ valueDeleted BOOLEAN DEFAULT FALSE,
+ valueCSNSet SEQUENCE OF ValueCSN,
+ }
+
+ ValueCSN ::= SEQUENCE {
+ CSNType ENUMERATED {
+ valuePresenceCSN (1),
+ valueDeletionCSN (2),
+ valueDistinguishedCSN (3)
+ }
+ CSN OCTET STRING,
+ }
+*/
+
+#include "repl5.h"
+
+#define CSN_TYPE_VALUE_UPDATED_ON_WIRE 1
+#define CSN_TYPE_VALUE_DELETED_ON_WIRE 2
+#define CSN_TYPE_VALUE_DISTINGUISHED_ON_WIRE 3
+
+/* #define GORDONS_PATENTED_BER_DEBUG 1 */
+#ifdef GORDONS_PATENTED_BER_DEBUG
+#define BER_DEBUG(a) printf(a)
+#else
+#define BER_DEBUG(a)
+#endif
+
+/* Forward declarations */
+static int my_ber_printf_csn(BerElement *ber, const CSN *csn, const CSNType t);
+static int my_ber_printf_value(BerElement *ber, const char *type,
+ const Slapi_Value *value, PRBool deleted);
+static int my_ber_printf_attr (BerElement *ber, Slapi_Attr *attr, PRBool deleted);
+static int my_ber_scanf_attr (BerElement *ber, Slapi_Attr **attr, PRBool *deleted);
+static int my_ber_scanf_value(BerElement *ber, Slapi_Value **value, PRBool *deleted);
+
+/*
+ * Get a Slapi_Entry ready to send over the wire as part of
+ * a total update protocol stream. Convert the entry and all
+ * of its state information to a BerElement which will be the
+ * payload of an extended LDAP operation.
+ *
+ * Entries consist of:
+ * - An entry DN
+ * - A uniqueID
+ * - A set of present attributes, each of which consists of:
+ * - A set of present values, each of which consists of:
+ * - A value
+ * - A set of CSNs
+ * - A set of deleted values, each of which consists of:
+ * - A value
+ * - A set of CSNs
+ * - A set of deleted attibutes, each of which consists of:
+ * - An attribute type
+ * - A set of CSNs. Note that this list of CSNs will always contain exactly one CSN.
+ *
+ * This all gets mashed into one BerElement, ready to be blasted over the wire to
+ * a replica.
+ *
+ */
+BerElement *
+entry2bere(const Slapi_Entry *e)
+{
+ BerElement *ber = NULL;
+ const char *str = NULL;
+ const char *dnstr = NULL;
+ char *type;
+ Slapi_DN *sdn = NULL;
+ Slapi_Attr *attr = NULL, *prev_attr;
+ int rc;
+
+ PR_ASSERT(NULL != e);
+
+ if ((ber = ber_alloc()) == NULL)
+ {
+ goto loser;
+ }
+ BER_DEBUG("{");
+ if (ber_printf(ber, "{") == -1) /* Begin outer sequence */
+ {
+ goto loser;
+ }
+
+ /* Get the entry's uniqueid */
+ if ((str = slapi_entry_get_uniqueid(e)) == NULL)
+ {
+ goto loser;
+ }
+ BER_DEBUG("s(uniqueid)");
+ if (ber_printf(ber, "s", str) == -1)
+ {
+ goto loser;
+ }
+
+ /* Get the entry's DN */
+ if ((sdn = slapi_entry_get_sdn((Slapi_Entry *)e)) == NULL) /* XXXggood had to cast away const */
+ {
+ goto loser;
+ }
+ if ((dnstr = slapi_sdn_get_dn(sdn)) == NULL)
+ {
+ goto loser;
+ }
+ BER_DEBUG("s(dn)");
+ if (ber_printf(ber, "s", dnstr) == -1)
+ {
+ goto loser;
+ }
+
+ /* Next comes the annoted list of the entry's attributes */
+ BER_DEBUG("[");
+ if (ber_printf(ber, "[") == -1) /* Begin set of attributes */
+ {
+ goto loser;
+ }
+ /*
+ * We iterate over all of the non-deleted attributes first.
+ */
+ slapi_entry_first_attr(e, &attr);
+ while (NULL != attr)
+ {
+ /* ONREPL - skip uniqueid attribute since we already sent uniqueid
+ This is a hack; need to figure a better way of storing uniqueid
+ in an entry */
+ slapi_attr_get_type (attr, &type);
+ if (strcasecmp (type, SLAPI_ATTR_UNIQUEID) != 0)
+ {
+ /* Process this attribute */
+ rc = my_ber_printf_attr (ber, attr, PR_FALSE);
+ if (rc != 0)
+ {
+ goto loser;
+ }
+ }
+
+ prev_attr = attr;
+ slapi_entry_next_attr(e, prev_attr, &attr);
+ }
+
+ /*
+ * Now iterate over the deleted attributes.
+ */
+ entry_first_deleted_attribute(e, &attr);
+ while (attr != NULL)
+ {
+ /* Process this attribute */
+ rc = my_ber_printf_attr (ber, attr, PR_TRUE);
+ if (rc != 0)
+ {
+ goto loser;
+ }
+ entry_next_deleted_attribute(e, &attr);
+ }
+ BER_DEBUG("]");
+ if (ber_printf(ber, "]") == -1) /* End set for attributes */
+ {
+ goto loser;
+ }
+ BER_DEBUG("}");
+ if (ber_printf(ber, "}") == -1) /* End sequence for this entry */
+ {
+ goto loser;
+ }
+
+ /* If we get here, everything went ok */
+ BER_DEBUG("\n");
+ goto free_and_return;
+loser:
+ if (NULL != ber)
+ {
+ ber_free(ber, 1);
+ ber = NULL;
+ }
+
+free_and_return:
+ return ber;
+}
+
+
+/*
+ * Helper function - convert a CSN to a string and ber_printf() it.
+ */
+static int
+my_ber_printf_csn(BerElement *ber, const CSN *csn, const CSNType t)
+{
+ char csn_str[CSN_STRSIZE];
+ unsigned long len;
+ int rc = -1;
+ int csn_type_as_ber = -1;
+
+ switch (t)
+ {
+ case CSN_TYPE_VALUE_UPDATED:
+ csn_type_as_ber = CSN_TYPE_VALUE_UPDATED_ON_WIRE;
+ break;
+ case CSN_TYPE_VALUE_DELETED:
+ csn_type_as_ber = CSN_TYPE_VALUE_DELETED_ON_WIRE;
+ break;
+ case CSN_TYPE_VALUE_DISTINGUISHED:
+ csn_type_as_ber = CSN_TYPE_VALUE_DISTINGUISHED_ON_WIRE;
+ break;
+ case CSN_TYPE_ATTRIBUTE_DELETED:
+ break;
+ default:
+ slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name, "my_ber_printf_csn: unknown "
+ "csn type %d encountered.\n", (int)t);
+ return -1;
+ }
+
+ csn_as_string(csn,PR_FALSE,csn_str);
+
+ /* we don't send type for attr csn since there is only one */
+ if (t == CSN_TYPE_ATTRIBUTE_DELETED)
+ {
+ rc = ber_printf(ber, "s", csn_str);
+ BER_DEBUG("s(csn_str)");
+ }
+ else
+ {
+ len = CSN_STRSIZE;
+ rc = ber_printf(ber, "{es}", csn_type_as_ber, csn_str);
+ BER_DEBUG("{e(csn type)s(csn)}");
+ }
+
+ return rc;
+}
+
+
+/*
+ * Send a single annotated attribute value.
+ */
+static int
+my_ber_printf_value(BerElement *ber, const char *type, const Slapi_Value *value, PRBool deleted)
+{
+ const struct berval *bval = NULL;
+ int rc = -1;
+ const CSNSet *csnset;
+ void *cookie;
+ CSN *csn;
+ CSNType t;
+
+ bval = slapi_value_get_berval(value);
+ BER_DEBUG("{o(value)");
+ if (ber_printf(ber, "{o", bval->bv_val, bval->bv_len) == -1) /* Start sequence */
+ {
+ goto done;
+ }
+
+/* if (ber_printf(ber, "o", bval->bv_val, bval->bv_len) == -1)
+ {
+ goto done;
+ } */
+
+ if (deleted)
+ {
+ BER_DEBUG("b(deleted flag)");
+ if (ber_printf (ber, "b", PR_TRUE) == -1)
+ {
+ goto done;
+ }
+ }
+ /* Send value CSN list */
+ BER_DEBUG("{");
+ if (ber_printf(ber, "{") == -1) /* Start set */
+ {
+ goto done;
+ }
+
+ /* Iterate over the sequence of CSNs. */
+ csnset = value_get_csnset (value);
+ if (csnset)
+ {
+ for (cookie = csnset_get_first_csn (csnset, &csn, &t); NULL != cookie;
+ cookie = csnset_get_next_csn (csnset, cookie, &csn, &t))
+ {
+ /* Don't send any adcsns, since that was already sent */
+ if (t != CSN_TYPE_ATTRIBUTE_DELETED)
+ {
+ if (my_ber_printf_csn(ber, csn, t) == -1)
+ {
+ goto done;
+ }
+ }
+ }
+ }
+
+ BER_DEBUG("}");
+ if (ber_printf(ber, "}") == -1) /* End CSN sequence */
+ {
+ goto done;
+ }
+ BER_DEBUG("}");
+ if (ber_printf(ber, "}") == -1) /* End sequence */
+ {
+ goto done;
+ }
+
+ /* Everything's ok */
+ rc = 0;
+
+done:
+ return rc;
+
+}
+
+/* send a single attribute */
+static int
+my_ber_printf_attr (BerElement *ber, Slapi_Attr *attr, PRBool deleted)
+{
+ Slapi_Value *value;
+ char *type;
+ int i;
+ const CSN *csn;
+
+ /* First, send the type */
+ slapi_attr_get_type(attr, &type);
+ BER_DEBUG("{s(type ");
+ BER_DEBUG(type);
+ BER_DEBUG(")");
+ if (ber_printf(ber, "{s", type) == -1) /* Begin sequence for this type */
+ {
+ goto loser;
+ }
+
+ /* Send the attribute deletion CSN if present */
+ csn = attr_get_deletion_csn(attr);
+ if (csn)
+ {
+ if (my_ber_printf_csn(ber, csn, CSN_TYPE_ATTRIBUTE_DELETED) == -1)
+ {
+ goto loser;
+ }
+ }
+
+ /* only send "is deleted" flag for deleted attributes since it defaults to false */
+ if (deleted)
+ {
+ BER_DEBUG("b(del flag)");
+ if (ber_printf (ber, "b", PR_TRUE) == -1)
+ {
+ goto loser;
+ }
+ }
+
+ /*
+ * Iterate through all the values.
+ */
+ BER_DEBUG("[");
+ if (ber_printf(ber, "[") == -1) /* Begin set */
+ {
+ goto loser;
+ }
+
+ /*
+ * Process the non-deleted values first.
+ */
+ i = slapi_attr_first_value(attr, &value);
+ while (i != -1)
+ {
+ if (my_ber_printf_value(ber, type, value, PR_FALSE) == -1)
+ {
+ goto loser;
+ }
+ i= slapi_attr_next_value(attr, i, &value);
+ }
+
+ /*
+ * Now iterate over all of the deleted values.
+ */
+ i= attr_first_deleted_value(attr, &value);
+ while (i != -1)
+ {
+ if (my_ber_printf_value(ber, type, value, PR_TRUE) == -1)
+ {
+ goto loser;
+ }
+ i= attr_next_deleted_value(attr, i, &value);
+ }
+ BER_DEBUG("]");
+ if (ber_printf(ber, "]") == -1) /* End set */
+ {
+ goto loser;
+ }
+
+ BER_DEBUG("}");
+ if (ber_printf(ber, "}") == -1) /* End sequence for this type */
+ {
+ goto loser;
+ }
+
+ return 0;
+loser:
+ return -1;
+}
+
+/*
+ * Get an annotated value from the BerElement. Returns 0 on
+ * success, -1 on failure.
+ */
+static int
+my_ber_scanf_value(BerElement *ber, Slapi_Value **value, PRBool *deleted)
+{
+ struct berval *attrval = NULL;
+ unsigned long len;
+ unsigned long tag;
+ CSN *csn = NULL;
+ char csnstring[CSN_STRSIZE + 1];
+ CSNType csntype;
+ char *lasti;
+
+ PR_ASSERT(ber && value && deleted);
+
+ *value = NULL;
+
+ if (NULL == ber && NULL == value)
+ {
+ slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name, "my_ber_scanf_value BAD 1\n");
+ goto loser;
+ }
+
+ /* Each value is a sequence */
+ if (ber_scanf(ber, "{O", &attrval) == -1)
+ {
+ slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name, "my_ber_scanf_value BAD 2\n");
+ goto loser;
+ }
+ /* Allocate and fill in the attribute value */
+ if ((*value = slapi_value_new_berval(attrval)) == NULL)
+ {
+ slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name, "my_ber_scanf_value BAD 3\n");
+ goto loser;
+ }
+
+ /* check if this is a deleted value */
+ if (ber_peek_tag(ber, &len) == LBER_BOOLEAN)
+ {
+ if (ber_scanf(ber, "b", deleted) == -1)
+ {
+ slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name, "my_ber_scanf_value BAD 4\n");
+ goto loser;
+ }
+ }
+
+ else /* default is present value */
+ {
+ *deleted = PR_FALSE;
+ }
+
+ /* Read the sequence of CSNs */
+ for (tag = ber_first_element(ber, &len, &lasti);
+ tag != LBER_ERROR && tag != LBER_END_OF_SEQORSET;
+ tag = ber_next_element(ber, &len, lasti))
+ {
+ long csntype_tmp;
+ /* Each CSN is in a sequence that includes a csntype and CSN */
+ len = CSN_STRSIZE;
+ if (ber_scanf(ber, "{es}", &csntype_tmp, csnstring, &len) == -1)
+ {
+ slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name, "my_ber_scanf_value BAD 7 - bval is %s\n", attrval->bv_val);
+ goto loser;
+ }
+ switch (csntype_tmp)
+ {
+ case CSN_TYPE_VALUE_UPDATED_ON_WIRE:
+ csntype = CSN_TYPE_VALUE_UPDATED;
+ break;
+ case CSN_TYPE_VALUE_DELETED_ON_WIRE:
+ csntype = CSN_TYPE_VALUE_DELETED;
+ break;
+ case CSN_TYPE_VALUE_DISTINGUISHED_ON_WIRE:
+ csntype = CSN_TYPE_VALUE_DISTINGUISHED;
+ break;
+ default:
+ slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name, "Error: preposterous CSN type "
+ "%d received during total update.\n", csntype_tmp);
+ goto loser;
+ }
+ csn = csn_new_by_string(csnstring);
+ if (csn == NULL)
+ {
+ slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name, "my_ber_scanf_value BAD 8\n");
+ goto loser;
+ }
+ value_add_csn(*value, csntype, csn);
+ csn_free (&csn);
+ }
+
+ if (ber_scanf(ber, "}") == -1) /* End of annotated attribute value seq */
+ {
+ slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name, "my_ber_scanf_value BAD 10\n");
+ goto loser;
+ }
+
+ if (attrval)
+ ber_bvfree(attrval);
+ return 0;
+
+loser:
+ /* Free any stuff we allocated */
+ if (csn)
+ csn_free (&csn);
+ if (attrval)
+ ber_bvfree(attrval);
+ if (value)
+ {
+ slapi_value_free (value);
+ }
+
+ return -1;
+}
+
+static int
+my_ber_scanf_attr (BerElement *ber, Slapi_Attr **attr, PRBool *deleted)
+{
+ char *attrtype = NULL;
+ CSN *attr_deletion_csn = NULL;
+ PRBool val_deleted;
+ char *lasti;
+ unsigned long len;
+ unsigned long tag;
+ char *str;
+ int rc;
+ Slapi_Value *value;
+
+ PR_ASSERT (ber && attr && deleted);
+
+ /* allocate the attribute */
+ *attr = slapi_attr_new ();
+ if (attr == NULL)
+ {
+ goto loser;
+ }
+
+ if (ber_scanf(ber, "{a", &attrtype) == -1) /* Begin sequence for this attr */
+ {
+ goto loser;
+ }
+
+
+ slapi_attr_init(*attr, attrtype);
+ slapi_ch_free ((void **)&attrtype);
+
+ /* The attribute deletion CSN is next and is optional? */
+ if (ber_peek_tag(ber, &len) == LBER_OCTETSTRING)
+ {
+ if (ber_scanf(ber, "a", &str) == -1)
+ {
+ goto loser;
+ }
+ attr_deletion_csn = csn_new_by_string(str);
+ slapi_ch_free((void **)&str);
+ }
+
+ if (attr_deletion_csn)
+ {
+ rc = attr_set_deletion_csn(*attr, attr_deletion_csn);
+ csn_free (&attr_deletion_csn);
+ if (rc != 0)
+ {
+ goto loser;
+ }
+ }
+
+ /* The "attribute deleted" flag is next, and is optional */
+ if (ber_peek_tag(ber, &len) == LBER_BOOLEAN)
+ {
+ if (ber_scanf(ber, "b", deleted) == -1)
+ {
+ goto loser;
+ }
+ }
+ else /* default is present */
+ {
+ *deleted = PR_FALSE;
+ }
+
+ /* loop over the list of attribute values */
+ for (tag = ber_first_element(ber, &len, &lasti);
+ tag != LBER_ERROR && tag != LBER_END_OF_SEQORSET;
+ tag = ber_next_element(ber, &len, lasti))
+ {
+
+ value = NULL;
+ if (my_ber_scanf_value(ber, &value, &val_deleted) == -1)
+ {
+ goto loser;
+ }
+
+ if (val_deleted)
+ {
+ /* Add the value to the attribute */
+ if (attr_add_deleted_value(*attr, value) == -1) /* attr has ownership of value */
+ {
+ goto loser;
+ }
+ }
+ else
+ {
+ /* Add the value to the attribute */
+ if (slapi_attr_add_value(*attr, value) == -1) /* attr has ownership of value */
+ {
+ goto loser;
+ }
+ }
+ if (value)
+ slapi_value_free(&value);
+ }
+
+ if (ber_scanf(ber, "}") == -1) /* End sequence for this attribute */
+ {
+ goto loser;
+ }
+
+ return 0;
+loser:
+ if (*attr)
+ slapi_attr_free (attr);
+ if (value)
+ slapi_value_free (&value);
+
+ return -1;
+}
+
+/*
+ * Extract the payload from a total update extended operation,
+ * decode it, and produce a Slapi_Entry structure representing a new
+ * entry to be added to the local database.
+ */
+static int
+decode_total_update_extop(Slapi_PBlock *pb, Slapi_Entry **ep)
+{
+ BerElement *tmp_bere = NULL;
+ Slapi_Entry *e = NULL;
+ Slapi_Attr *attr = NULL;
+ char *str = NULL;
+ CSN *dn_csn = NULL;
+ struct berval *extop_value = NULL;
+ char *extop_oid = NULL;
+ unsigned long len;
+ char *lasto;
+ unsigned long tag;
+ int rc;
+ PRBool deleted;
+
+ PR_ASSERT(NULL != pb);
+ PR_ASSERT(NULL != ep);
+
+ slapi_pblock_get(pb, SLAPI_EXT_OP_REQ_OID, &extop_oid);
+ slapi_pblock_get(pb, SLAPI_EXT_OP_REQ_VALUE, &extop_value);
+
+ if (NULL == extop_oid ||
+ strcmp(extop_oid, REPL_NSDS50_REPLICATION_ENTRY_REQUEST_OID) != 0 ||
+ NULL == extop_value)
+ {
+ /* Bogus */
+ goto loser;
+ }
+
+ if ((tmp_bere = ber_init(extop_value)) == NULL)
+ {
+ goto loser;
+ }
+
+ if ((e = slapi_entry_alloc()) == NULL)
+ {
+ goto loser;
+ }
+
+ if (ber_scanf(tmp_bere, "{") == -1) /* Begin outer sequence */
+ {
+ goto loser;
+ }
+
+ /* The entry's uniqueid is first */
+ if (ber_scanf(tmp_bere, "a", &str) == -1)
+ {
+ goto loser;
+ }
+ slapi_entry_set_uniqueid(e, str);
+ str = NULL; /* Slapi_Entry now owns the uniqueid */
+
+ /* The entry's DN is next */
+ if (ber_scanf(tmp_bere, "a", &str) == -1)
+ {
+ goto loser;
+ }
+ slapi_entry_set_dn(e, str);
+ str = NULL; /* Slapi_Entry now owns the dn */
+
+ /* Get the attributes */
+ for ( tag = ber_first_element( tmp_bere, &len, &lasto );
+ tag != LBER_ERROR && tag != LBER_END_OF_SEQORSET;
+ tag = ber_next_element( tmp_bere, &len, lasto ) )
+ {
+
+ if (my_ber_scanf_attr (tmp_bere, &attr, &deleted) != 0)
+ {
+ goto loser;
+ }
+
+ /* Add the attribute to the entry */
+ if (deleted)
+ entry_add_deleted_attribute_wsi(e, attr); /* entry now owns attr */
+ else
+ entry_add_present_attribute_wsi(e, attr); /* entry now owns attr */
+ attr = NULL;
+ }
+
+ if (ber_scanf(tmp_bere, "}") == -1) /* End sequence for this entry */
+ {
+ goto loser;
+ }
+
+ /* Check for ldapsubentries and tombstone entries to set flags properly */
+ slapi_entry_attr_find(e, "objectclass", &attr);
+ if (attr != NULL) {
+ struct berval bv;
+ bv.bv_val = "ldapsubentry";
+ bv.bv_len = strlen(bv.bv_val);
+ if (slapi_attr_value_find(attr, &bv) == 0) {
+ slapi_entry_set_flag(e, SLAPI_ENTRY_LDAPSUBENTRY);
+ }
+ bv.bv_val = SLAPI_ATTR_VALUE_TOMBSTONE;
+ bv.bv_len = strlen(bv.bv_val);
+ if (slapi_attr_value_find(attr, &bv) == 0) {
+ slapi_entry_set_flag(e, SLAPI_ENTRY_FLAG_TOMBSTONE);
+ }
+ }
+
+ /* If we get here, the entry is properly constructed. Return it. */
+
+ rc = 0;
+ *ep = e;
+ goto free_and_return;
+
+loser:
+ rc = -1;
+ /* slapi_ch_free accepts NULL pointer */
+ slapi_ch_free((void **)&str);
+
+ if (NULL != dn_csn)
+ {
+ csn_free(&dn_csn);
+ }
+ if (attr != NULL)
+ {
+ slapi_attr_free (&attr);
+ }
+
+ if (NULL != e)
+ {
+ slapi_entry_free (e);
+ }
+ *ep = NULL;
+ slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name, "Error: could not decode extended "
+ "operation containing entry for total update.\n");
+
+free_and_return:
+ if (NULL != tmp_bere)
+ {
+ ber_free(tmp_bere, 1);
+ tmp_bere = NULL;
+ }
+ return rc;
+}
+
+/*
+ * This plugin entry point is called whenever an NSDS50ReplicationEntry
+ * extended operation is received.
+ */
+int
+multimaster_extop_NSDS50ReplicationEntry(Slapi_PBlock *pb)
+{
+ int rc;
+ Slapi_Entry *e = NULL;
+ Slapi_Connection *conn = NULL;
+ int connid, opid;
+
+ connid = 0;
+ slapi_pblock_get(pb, SLAPI_CONN_ID, &connid);
+ opid = 0;
+ slapi_pblock_get(pb, SLAPI_OPERATION_ID, &opid);
+
+ /* Decode the extended operation */
+ rc = decode_total_update_extop(pb, &e);
+
+ if (0 == rc)
+ {
+#ifdef notdef
+ /*
+ * Just spew LDIF so we're sure we got it right. Later we'll firehose
+ * this into the database import code
+ */
+ int len;
+ char *str = slapi_entry2str_with_options(e, &len,SLAPI_DUMP_UNIQUEID);
+ puts(str);
+ free(str);
+#endif
+
+ rc = slapi_import_entry (pb, e);
+ /* slapi_import_entry return an LDAP error in case of problem
+ * LDAP_BUSY is used to indicate that the import queue is full
+ * and that flow control must happen to stop the supplier
+ * from sending entries
+ */
+ if ((rc != LDAP_SUCCESS) && (rc != LDAP_BUSY))
+ {
+ const char *dn = slapi_entry_get_dn_const(e);
+ slapi_log_error(SLAPI_LOG_REPL, repl_plugin_name,
+ "Error %d: could not import entry dn %s "
+ "for total update operation conn=%d op=%d\n",
+ rc, dn, connid, opid);
+ rc = -1;
+ }
+
+ }
+ else
+ {
+ slapi_log_error(SLAPI_LOG_REPL, repl_plugin_name,
+ "Error %d: could not decode the total update extop "
+ "for total update operation conn=%d op=%d\n",
+ rc, connid, opid);
+ }
+
+ if ((rc != 0) && (rc != LDAP_BUSY))
+ {
+ /* just disconnect from the supplier. bulk import is stopped when
+ connection object is destroyed */
+ slapi_pblock_get (pb, SLAPI_CONNECTION, &conn);
+ if (conn)
+ {
+ slapi_disconnect_server(conn);
+ }
+
+ /* cleanup */
+ if (e)
+ {
+ slapi_entry_free (e);
+ }
+ }
+
+ return rc;
+}
diff --git a/ldap/servers/plugins/replication/repl5_updatedn_list.c b/ldap/servers/plugins/replication/repl5_updatedn_list.c
new file mode 100644
index 00000000..02304d19
--- /dev/null
+++ b/ldap/servers/plugins/replication/repl5_updatedn_list.c
@@ -0,0 +1,243 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+
+/* repl5_updatedn_list.c */
+
+/*
+ This is the internal representation for the list of update DNs in the replica.
+ The list is implemented as a hash table - the key is the normalized DN, and the
+ value is the Slapi_DN representation of the DN
+*/
+
+#include "repl5.h"
+#include "plhash.h"
+
+/* global data */
+
+/* typedef ReplicaUpdateDNList PLHashTable; */
+
+struct repl_enum_data
+{
+ FNEnumDN fn;
+ void *arg;
+};
+
+/* Forward declarations */
+static PRIntn replica_destroy_hash_entry (PLHashEntry *he, PRIntn index, void *arg);
+static PRIntn updatedn_list_enumerate (PLHashEntry *he, PRIntn index, void *hash_data);
+
+static int
+updatedn_compare_dns(const void *d1, const void *d2)
+{
+ return (0 == slapi_sdn_compare((const Slapi_DN *)d1, (const Slapi_DN *)d2));
+}
+
+/* create a new updatedn list - if the entry is given, initialize the list from
+ the replicabinddn values given in the entry */
+ReplicaUpdateDNList
+replica_updatedn_list_new(const Slapi_Entry *entry)
+{
+ /* allocate table */
+ PLHashTable *hash = PL_NewHashTable(4, PL_HashString, PL_CompareStrings,
+ updatedn_compare_dns, NULL, NULL);
+ if (hash == NULL) {
+ slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name, "replica_new_updatedn_list: "
+ "failed to allocate hash table; NSPR error - %d\n",
+ PR_GetError ());
+ return NULL;
+ }
+
+ if (entry) {
+ Slapi_Attr *attr = NULL;
+ if (!slapi_entry_attr_find(entry, attr_replicaBindDn, &attr)) {
+ Slapi_ValueSet *vs = NULL;
+ slapi_attr_get_valueset(attr, &vs);
+ replica_updatedn_list_replace(hash, vs);
+ slapi_valueset_free(vs);
+ }
+ }
+
+ return (ReplicaUpdateDNList)hash;
+}
+
+void
+replica_updatedn_list_free(ReplicaUpdateDNList list)
+{
+ /* destroy the content */
+ PLHashTable *hash = list;
+ PL_HashTableEnumerateEntries(hash, replica_destroy_hash_entry, NULL);
+
+ if (hash)
+ PL_HashTableDestroy(hash);
+}
+
+void
+replica_updatedn_list_replace(ReplicaUpdateDNList list, const Slapi_ValueSet *vs)
+{
+ replica_updatedn_list_delete(list, NULL); /* delete all values */
+ replica_updatedn_list_add(list, vs);
+}
+
+/* if vs is given, delete only those values - otherwise, delete all values */
+void
+replica_updatedn_list_delete(ReplicaUpdateDNList list, const Slapi_ValueSet *vs)
+{
+ PLHashTable *hash = list;
+ if (!vs || slapi_valueset_count(vs) == 0) { /* just delete everything */
+ PL_HashTableEnumerateEntries(hash, replica_destroy_hash_entry, NULL);
+ } else {
+ Slapi_ValueSet *vs_nc = (Slapi_ValueSet *)vs; /* cast away const */
+ Slapi_Value *val = NULL;
+ int index = 0;
+ for (index = slapi_valueset_first_value(vs_nc, &val); val;
+ index = slapi_valueset_next_value(vs_nc, index, &val)) {
+ Slapi_DN *dn = slapi_sdn_new_dn_byval(slapi_value_get_string(val));
+ /* locate object */
+ Slapi_DN *deldn = (Slapi_DN *)PL_HashTableLookup(hash, slapi_sdn_get_ndn(dn));
+ if (deldn == NULL)
+ {
+ slapi_log_error(SLAPI_LOG_REPL, repl_plugin_name, "replica_updatedn_list_delete: "
+ "update DN with value (%s) is not in the update DN list.\n",
+ slapi_sdn_get_ndn(dn));
+ } else {
+ /* remove from hash */
+ PL_HashTableRemove(hash, slapi_sdn_get_ndn(dn));
+ /* free the pointer */
+ slapi_sdn_free(&deldn);
+ }
+ /* free the temp dn */
+ slapi_sdn_free(&dn);
+ }
+ }
+
+ return;
+}
+
+void
+replica_updatedn_list_add(ReplicaUpdateDNList list, const Slapi_ValueSet *vs)
+{
+ PLHashTable *hash = list;
+ Slapi_ValueSet *vs_nc = (Slapi_ValueSet *)vs; /* cast away const */
+ Slapi_Value *val = NULL;
+ int index = 0;
+
+ PR_ASSERT(list && vs);
+
+ for (index = slapi_valueset_first_value(vs_nc, &val); val;
+ index = slapi_valueset_next_value(vs_nc, index, &val)) {
+ Slapi_DN *dn = slapi_sdn_new_dn_byval(slapi_value_get_string(val));
+ const char *ndn = slapi_sdn_get_ndn(dn);
+
+ /* make sure that the name is unique */
+ if (PL_HashTableLookup(hash, ndn) != NULL)
+ {
+ slapi_log_error(SLAPI_LOG_REPL, repl_plugin_name, "replica_updatedn_list_add: "
+ "update DN with value (%s) already in the update DN list\n",
+ ndn);
+ slapi_sdn_free(&dn);
+ } else {
+ PL_HashTableAdd(hash, ndn, dn);
+ }
+ }
+
+ return;
+}
+
+PRBool
+replica_updatedn_list_ismember(ReplicaUpdateDNList list, const Slapi_DN *dn)
+{
+ PLHashTable *hash = list;
+ PRBool ret = PR_FALSE;
+
+ const char *ndn = slapi_sdn_get_ndn(dn);
+
+ /* Bug 605169 - null ndn would cause core dump */
+ if ( ndn ) {
+ ret = (PRBool)PL_HashTableLookupConst(hash, ndn);
+ }
+
+ return ret;
+}
+
+struct list_to_string_data {
+ char *string;
+ const char *delimiter;
+};
+
+static int
+convert_to_string(Slapi_DN *dn, void *arg)
+{
+ struct list_to_string_data *data = (struct list_to_string_data *)arg;
+ int newlen = strlen(slapi_sdn_get_dn(dn)) + strlen(data->delimiter) + 1;
+ if (data->string) {
+ newlen += strlen(data->string);
+ data->string = slapi_ch_realloc(data->string, newlen);
+ } else {
+ data->string = slapi_ch_calloc(1, newlen);
+ }
+ strcat(data->string, slapi_sdn_get_dn(dn));
+ strcat(data->string, data->delimiter);
+
+ return 1;
+}
+
+/* caller must slapi_ch_free_string the returned string */
+char *
+replica_updatedn_list_to_string(ReplicaUpdateDNList list, const char *delimiter)
+{
+ struct list_to_string_data data;
+ data.string = NULL;
+ data.delimiter = delimiter;
+ replica_updatedn_list_enumerate(list, convert_to_string, (void *)&data);
+ return data.string;
+}
+
+void
+replica_updatedn_list_enumerate(ReplicaUpdateDNList list, FNEnumDN fn, void *arg)
+{
+ PLHashTable *hash = list;
+ struct repl_enum_data data;
+
+ PR_ASSERT (fn);
+
+ data.fn = fn;
+ data.arg = arg;
+
+ PL_HashTableEnumerateEntries(hash, updatedn_list_enumerate, &data);
+}
+
+/* Helper functions */
+
+/* this function called for each hash node during hash destruction */
+static PRIntn
+replica_destroy_hash_entry(PLHashEntry *he, PRIntn index, void *arg)
+{
+ Slapi_DN *dn = NULL;
+
+ if (he == NULL)
+ return HT_ENUMERATE_NEXT;
+
+ dn = (Slapi_DN *)he->value;
+ PR_ASSERT (dn);
+
+ slapi_sdn_free(&dn);
+
+ return HT_ENUMERATE_REMOVE;
+}
+
+static PRIntn
+updatedn_list_enumerate(PLHashEntry *he, PRIntn index, void *hash_data)
+{
+ Slapi_DN *dn = NULL;
+ struct repl_enum_data *data = hash_data;
+
+ dn = (Slapi_DN*)he->value;
+ PR_ASSERT (dn);
+
+ data->fn(dn, data->arg);
+
+ return HT_ENUMERATE_NEXT;
+}
diff --git a/ldap/servers/plugins/replication/repl_add.c b/ldap/servers/plugins/replication/repl_add.c
new file mode 100644
index 00000000..3d47c1db
--- /dev/null
+++ b/ldap/servers/plugins/replication/repl_add.c
@@ -0,0 +1,30 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+
+#include "slapi-plugin.h"
+#include "repl.h"
+
+
+/* Add Operation Plugin Functions for legacy replication plugin */
+
+int
+legacy_preop_add( Slapi_PBlock *pb )
+{
+ return legacy_preop( pb, "legacy_preop_add", OP_ADD );
+}
+
+int
+legacy_bepreop_add( Slapi_PBlock *pb )
+{
+ int rc= 0; /* OK */
+ return rc;
+}
+
+int
+legacy_postop_add( Slapi_PBlock *pb )
+{
+ return legacy_postop( pb, "legacy_postop_add", OP_ADD );
+}
diff --git a/ldap/servers/plugins/replication/repl_bind.c b/ldap/servers/plugins/replication/repl_bind.c
new file mode 100644
index 00000000..e317e311
--- /dev/null
+++ b/ldap/servers/plugins/replication/repl_bind.c
@@ -0,0 +1,48 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+
+
+#include "slapi-plugin.h"
+#include "repl.h"
+#include "repl5.h"
+
+
+int
+legacy_preop_bind( Slapi_PBlock *pb )
+{
+ int return_value = 0;
+ char *dn = NULL;
+ struct berval *cred = NULL;
+ int method;
+ int one = 1;
+
+ slapi_pblock_get(pb, SLAPI_BIND_METHOD, &method);
+ slapi_pblock_get(pb, SLAPI_BIND_TARGET, &dn);
+ slapi_pblock_get(pb, SLAPI_BIND_CREDENTIALS, &cred);
+
+ if (LDAP_AUTH_SIMPLE == method)
+ {
+ if (legacy_consumer_is_replicationdn(dn) && legacy_consumer_is_replicationpw(cred))
+ {
+ /* Successful bind as replicationdn */
+ void *conn = NULL;
+ consumer_connection_extension *connext = NULL;
+#ifdef DEBUG
+ slapi_log_error(SLAPI_LOG_REPL, REPLICATION_SUBSYSTEM, "legacy_preop_bind: begin\n");
+#endif
+ slapi_pblock_get( pb, SLAPI_CONNECTION, &conn );
+ connext = (consumer_connection_extension*) repl_con_get_ext (REPL_CON_EXT_CONN, conn);
+ if (NULL != connext)
+ {
+ connext->is_legacy_replication_dn = 1;
+ }
+ slapi_send_ldap_result(pb, LDAP_SUCCESS, NULL, NULL, 0, NULL);
+ return_value = 1; /* Prevent further processing in front end */
+ }
+ }
+ return return_value;
+
+}
diff --git a/ldap/servers/plugins/replication/repl_compare.c b/ldap/servers/plugins/replication/repl_compare.c
new file mode 100644
index 00000000..34f2b944
--- /dev/null
+++ b/ldap/servers/plugins/replication/repl_compare.c
@@ -0,0 +1,34 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+
+#include "slapi-plugin.h"
+#include "repl.h"
+
+int
+legacy_preop_compare( Slapi_PBlock *pb )
+{
+ int is_replicated_operation = 0;
+ char *compare_base = NULL;
+ struct berval **referral = NULL;
+ int return_code = 0;
+ Slapi_DN *basesdn;
+
+ slapi_pblock_get(pb, SLAPI_IS_REPLICATED_OPERATION, &is_replicated_operation);
+ slapi_pblock_get(pb, SLAPI_COMPARE_TARGET, &compare_base);
+ basesdn= slapi_sdn_new_dn_byref(compare_base);
+ referral = get_data_source(pb, basesdn, 1, NULL);
+ slapi_sdn_free(&basesdn);
+ if (NULL != referral && !is_replicated_operation)
+ {
+ /*
+ * There is a copyingFrom in this entry or an ancestor.
+ * Return a referral to the supplier, and we're all done.
+ */
+ slapi_send_ldap_result(pb, LDAP_REFERRAL, NULL, NULL, 0, referral);
+ return_code = 1; /* return 1 to prevent further search processing */
+ }
+ return return_code;
+}
diff --git a/ldap/servers/plugins/replication/repl_connext.c b/ldap/servers/plugins/replication/repl_connext.c
new file mode 100644
index 00000000..8b0c0551
--- /dev/null
+++ b/ldap/servers/plugins/replication/repl_connext.c
@@ -0,0 +1,91 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+/* repl_connext.c - replication extension to the Connection object
+ */
+
+
+#include "repl.h"
+#include "repl5.h"
+
+
+/* ***** Supplier side ***** */
+
+/* NOT NEEDED YET */
+
+/* ***** Consumer side ***** */
+
+/* consumer connection extension constructor */
+void* consumer_connection_extension_constructor (void *object, void *parent)
+{
+ consumer_connection_extension *ext = (consumer_connection_extension*) slapi_ch_malloc (sizeof (consumer_connection_extension));
+ if (ext == NULL)
+ {
+ slapi_log_error( SLAPI_LOG_PLUGIN, repl_plugin_name, "unable to create replication consumer connection extension - out of memory\n" );
+ }
+ else
+ {
+ ext->is_legacy_replication_dn= 0;
+ ext->repl_protocol_version = REPL_PROTOCOL_UNKNOWN;
+ ext->replica_acquired = NULL;
+ ext->isreplicationsession= 0;
+ ext->supplier_ruv = NULL;
+ ext->connection = NULL;
+ }
+
+ return ext;
+}
+
+/* consumer connection extension destructor */
+void consumer_connection_extension_destructor (void *ext, void *object, void *parent)
+{
+ int connid = 0;
+ if (ext)
+ {
+ /* Check to see if this replication session has acquired
+ * a replica. If so, release it here.
+ */
+ consumer_connection_extension *connext = (consumer_connection_extension *)ext;
+ if (NULL != connext->replica_acquired)
+ {
+ Replica *r = object_get_data ((Object*)connext->replica_acquired);
+ /* If a total update was in progress, abort it */
+ if (REPL_PROTOCOL_50_TOTALUPDATE == connext->repl_protocol_version)
+ {
+ Slapi_PBlock *pb = slapi_pblock_new();
+ const Slapi_DN *repl_root_sdn = replica_get_root(r);
+ PR_ASSERT(NULL != repl_root_sdn);
+ if (NULL != repl_root_sdn)
+ {
+ slapi_pblock_set(pb, SLAPI_CONNECTION, connext->connection);
+ slapi_pblock_set(pb, SLAPI_TARGET_DN, (void*)slapi_sdn_get_dn(repl_root_sdn));
+ slapi_pblock_get(pb, SLAPI_CONN_ID, &connid);
+ slapi_log_error(SLAPI_LOG_REPL, repl_plugin_name,
+ "Aborting total update in progress for replicated "
+ "area %s connid=%d\n", slapi_sdn_get_dn(repl_root_sdn),
+ connid);
+ slapi_stop_bulk_import(pb);
+ }
+ else
+ {
+ slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name,
+ "consumer_connection_extension_destructor: can't determine root "
+ "of replicated area.\n");
+ }
+ slapi_pblock_destroy(pb);
+ }
+ replica_relinquish_exclusive_access(r, connid, -1);
+ object_release ((Object*)connext->replica_acquired);
+ connext->replica_acquired = NULL;
+ }
+
+ if (connext->supplier_ruv)
+ {
+ ruv_destroy ((RUV **)&connext->supplier_ruv);
+ }
+ connext->connection = NULL;
+ slapi_ch_free((void **)&ext);
+ }
+}
diff --git a/ldap/servers/plugins/replication/repl_controls.c b/ldap/servers/plugins/replication/repl_controls.c
new file mode 100644
index 00000000..ae1cb119
--- /dev/null
+++ b/ldap/servers/plugins/replication/repl_controls.c
@@ -0,0 +1,337 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+
+#include "slapi-plugin.h"
+#include "repl5.h"
+#include "repl.h" /* For LDAP_CONTROL_REPL_MODRDN_EXTRAMODS */
+
+/*
+ * repl_controls.c - convenience functions for creating and
+ * decoding controls that implement 5.0-style replication
+ * protocol operations.
+ *
+ * TODO: Send modrdn mods with modrdn operation
+ * Fix ber_printf() and ber_scanf() format strings - some are
+ * the wrong types.
+ */
+
+/*
+ * Return a pointer to a NSDS50ReplUpdateInfoControl.
+ * The control looks like this:
+ *
+ * NSDS50ReplUpdateInfoControl ::= SEQUENCE {
+ * uuid OCTET STRING,
+ * csn OCTET STRING,
+ * OPTIONAL [new]superior-uuid OCTET STRING
+ * OPTIONAL modrdn_mods XXXggood WHAT TYPE???
+ * }
+ */
+int
+create_NSDS50ReplUpdateInfoControl(const char *uuid,
+ const char *superior_uuid, const CSN *csn,
+ LDAPMod **modrdn_mods, LDAPControl **ctrlp)
+{
+ int retval;
+ BerElement *tmp_bere = NULL;
+ struct berval tmpval = {0};
+ char csn_str[CSN_STRSIZE];
+
+ if (NULL == ctrlp)
+ {
+ retval = LDAP_PARAM_ERROR;
+ goto loser;
+ }
+ else
+ {
+ if ((tmp_bere = ber_alloc()) == NULL)
+ {
+ retval = LDAP_NO_MEMORY;
+ goto loser;
+ }
+ else
+ {
+ /* Stuff uuid and csn into BerElement */
+ if (ber_printf(tmp_bere, "{") == -1)
+ {
+ retval = LDAP_ENCODING_ERROR;
+ goto loser;
+ }
+
+ /* Stuff uuid of this entry into BerElement */
+ if (ber_printf(tmp_bere, "s", uuid) == -1)
+ {
+ retval = LDAP_ENCODING_ERROR;
+ goto loser;
+ }
+
+ /* Stuff csn of this change into BerElement */
+ csn_as_string(csn, PR_FALSE, csn_str);
+ if (ber_printf(tmp_bere, "s", csn_str) == -1)
+ {
+ retval = LDAP_ENCODING_ERROR;
+ goto loser;
+ }
+
+ /* If present, stuff uuid of parent entry into BerElement */
+ if (NULL != superior_uuid)
+ {
+ if (ber_printf(tmp_bere, "s", superior_uuid) == -1)
+ {
+ retval = LDAP_ENCODING_ERROR;
+ goto loser;
+ }
+ }
+
+ /* If present, add the modrdn mods */
+ if (NULL != modrdn_mods)
+ {
+ int i;
+ if (ber_printf(tmp_bere, "{" ) == -1)
+ {
+ retval = LDAP_ENCODING_ERROR;
+ goto loser;
+ }
+ /* for each modification to be performed... */
+ for (i = 0; NULL != modrdn_mods[i]; i++)
+ {
+ if (ber_printf(tmp_bere, "{e{s[V]}}",
+ modrdn_mods[i]->mod_op & ~LDAP_MOD_BVALUES,
+ modrdn_mods[i]->mod_type, modrdn_mods[i]->mod_bvalues ) == -1)
+ {
+ retval = LDAP_ENCODING_ERROR;
+ goto loser;
+ }
+ }
+ if (ber_printf(tmp_bere, "}") == -1)
+ {
+ retval = LDAP_ENCODING_ERROR;
+ goto loser;
+ }
+ }
+
+ /* Close the sequence */
+ if (ber_printf(tmp_bere, "}") == -1)
+ {
+ retval = LDAP_ENCODING_ERROR;
+ goto loser;
+ }
+
+ retval = slapi_build_control( REPL_NSDS50_UPDATE_INFO_CONTROL_OID,
+ tmp_bere, 1 /* is critical */, ctrlp);
+ }
+ }
+loser:
+ if (NULL != tmp_bere)
+ {
+ ber_free(tmp_bere, 1);
+ tmp_bere = NULL;
+ }
+ return retval;
+}
+
+
+/*
+ * Destroy a ReplUpdateInfoControl and set the pointer to NULL.
+ */
+void
+destroy_NSDS50ReplUpdateInfoControl(LDAPControl **ctrlp)
+{
+ if (NULL != ctrlp && NULL != *ctrlp)
+ {
+ ldap_control_free(*ctrlp);
+ *ctrlp = NULL;
+ }
+}
+
+
+
+
+/*
+ * Look through the array of controls. If an NSDS50ReplUpdateInfoControl
+ * is present, decode it and return pointers to the broken-out
+ * components. The caller is responsible for freeing pointers to
+ * the returned objects. The caller may indicate that it is not
+ * interested in any of the output parameters by passing NULL
+ * for that parameter.
+ *
+ * Returns 0 if the control is not present, 1 if it is present, and
+ * -1 if an error occurs.
+ */
+int
+decode_NSDS50ReplUpdateInfoControl(LDAPControl **controlsp,
+ char **uuid, char **superior_uuid,
+ CSN **csn, LDAPMod ***modrdn_mods)
+{
+ struct berval *ctl_value = NULL;
+ int iscritical = 0;
+ int rc = -1;
+ struct berval uuid_val = {0};
+ struct berval superior_uuid_val = {0};
+ struct berval csn_val = {0};
+ BerElement *tmp_bere = NULL;
+ Slapi_Mods modrdn_smods;
+ PRBool got_modrdn_mods = PR_FALSE;
+ unsigned long len;
+
+ slapi_mods_init(&modrdn_smods, 4);
+ if (slapi_control_present(controlsp, REPL_NSDS50_UPDATE_INFO_CONTROL_OID,
+ &ctl_value, &iscritical))
+ {
+ if ((tmp_bere = ber_init(ctl_value)) == NULL)
+ {
+ rc = -1;
+ goto loser;
+ }
+ if (ber_scanf(tmp_bere, "{oo", &uuid_val, &csn_val) == -1)
+ {
+ rc = -1;
+ goto loser;
+ }
+ if (ber_peek_tag(tmp_bere, &len) == LBER_OCTETSTRING)
+ {
+ /* The optional superior_uuid is present */
+ if (ber_scanf(tmp_bere, "o", &superior_uuid_val) == -1)
+ {
+ rc = -1;
+ goto loser;
+ }
+ }
+ if (ber_peek_tag(tmp_bere, &len) == LBER_SEQUENCE)
+ {
+ unsigned long emtag, emlen;
+ char *emlast;
+
+ for ( emtag = ber_first_element( tmp_bere, &emlen, &emlast );
+ emtag != LBER_ERROR && emtag != LBER_END_OF_SEQORSET;
+ emtag = ber_next_element( tmp_bere, &emlen, emlast ))
+ {
+ struct berval **embvals;
+ long op;
+ char *type;
+ if ( ber_scanf( tmp_bere, "{i{a[V]}}", &op, &type, &embvals ) == LBER_ERROR )
+ {
+ rc = -1;
+ goto loser;
+ }
+ slapi_mods_add_modbvps(&modrdn_smods, op, type, embvals);
+ free( type );
+ ber_bvecfree( embvals );
+ }
+ got_modrdn_mods = PR_TRUE;
+ }
+ if (ber_scanf(tmp_bere, "}") == -1)
+ {
+ rc = -1;
+ goto loser;
+ }
+
+ if (NULL != uuid)
+ {
+ *uuid = slapi_ch_malloc(uuid_val.bv_len + 1);
+ strncpy(*uuid, uuid_val.bv_val, uuid_val.bv_len);
+ (*uuid)[uuid_val.bv_len] = '\0';
+ }
+
+ if (NULL != csn)
+ {
+ char *csnstr = slapi_ch_malloc(csn_val.bv_len + 1);
+ strncpy(csnstr, csn_val.bv_val, csn_val.bv_len);
+ csnstr[csn_val.bv_len] = '\0';
+ *csn = csn_new_by_string(csnstr);
+ slapi_ch_free((void **)&csnstr);
+ }
+
+ if (NULL != superior_uuid && NULL != superior_uuid_val.bv_val)
+ {
+ *superior_uuid = slapi_ch_malloc(superior_uuid_val.bv_len + 1);
+ strncpy(*superior_uuid, superior_uuid_val.bv_val,
+ superior_uuid_val.bv_len);
+ (*superior_uuid)[superior_uuid_val.bv_len] = '\0';
+ }
+
+ if (NULL != modrdn_mods && got_modrdn_mods)
+ {
+ *modrdn_mods = slapi_mods_get_ldapmods_passout(&modrdn_smods);
+ }
+ slapi_mods_done(&modrdn_smods);
+
+ rc = 1;
+ }
+ else
+ {
+ rc = 0;
+ }
+loser:
+ /* XXXggood free CSN here if allocated */
+
+ if (NULL != tmp_bere)
+ {
+ ber_free(tmp_bere, 1);
+ tmp_bere = NULL;
+ }
+ if (NULL != uuid_val.bv_val)
+ {
+ ldap_memfree(uuid_val.bv_val);
+ uuid_val.bv_val = NULL;
+ }
+ if (NULL != superior_uuid_val.bv_val)
+ {
+ ldap_memfree(superior_uuid_val.bv_val);
+ superior_uuid_val.bv_val = NULL;
+ }
+ if (NULL != csn_val.bv_val)
+ {
+ ldap_memfree(csn_val.bv_val);
+ csn_val.bv_val = NULL;
+ }
+ return rc;
+}
+
+
+
+void
+add_repl_control_mods( Slapi_PBlock *pb, Slapi_Mods *smods )
+{
+ struct berval *embvp;
+ LDAPControl **controls = NULL;
+
+ slapi_pblock_get( pb, SLAPI_REQCONTROLS, &controls);
+ if ( slapi_control_present( controls,
+ LDAP_CONTROL_REPL_MODRDN_EXTRAMODS,
+ &embvp, NULL ))
+ {
+ if ( embvp != NULL && embvp->bv_len > 0 && embvp->bv_val != NULL )
+ {
+ /* Parse the extramods stuff */
+ long op;
+ char *type;
+ unsigned long emlen;
+ unsigned long emtag;
+ char *emlast;
+ BerElement *ember = ber_init( embvp );
+ if ( ember != NULL )
+ {
+ for ( emtag = ber_first_element( ember, &emlen, &emlast );
+ emtag != LBER_ERROR && emtag != LBER_END_OF_SEQORSET;
+ emtag = ber_next_element( ember, &emlen, emlast ))
+ {
+ struct berval **embvals;
+ if ( ber_scanf( ember, "{i{a[V]}}", &op, &type, &embvals ) == LBER_ERROR )
+ {
+ continue;
+ /* GGOODREPL I suspect this will cause two sets of lastmods attr values
+ to end up in the entry. We need to remove the old ones.
+ */
+ }
+ slapi_mods_add_modbvps( smods, op, type, embvals);
+ free( type );
+ ber_bvecfree( embvals );
+ }
+ }
+ ber_free( ember, 1 );
+ }
+ }
+}
diff --git a/ldap/servers/plugins/replication/repl_delete.c b/ldap/servers/plugins/replication/repl_delete.c
new file mode 100644
index 00000000..6f8e07df
--- /dev/null
+++ b/ldap/servers/plugins/replication/repl_delete.c
@@ -0,0 +1,26 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+
+#include "slapi-plugin.h"
+#include "repl.h"
+
+int
+legacy_preop_delete( Slapi_PBlock *pb )
+{
+ return legacy_preop(pb, "legacy_preop_delete", OP_DELETE);
+}
+
+int
+legacy_bepreop_delete( Slapi_PBlock *pb )
+{
+ return 0; /* OK */
+}
+
+int
+legacy_postop_delete( Slapi_PBlock *pb )
+{
+ return legacy_postop(pb, "legacy_preop_delete", OP_DELETE);
+}
diff --git a/ldap/servers/plugins/replication/repl_entry.c b/ldap/servers/plugins/replication/repl_entry.c
new file mode 100644
index 00000000..83bf2857
--- /dev/null
+++ b/ldap/servers/plugins/replication/repl_entry.c
@@ -0,0 +1,38 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+
+#include "slapi-plugin.h"
+#include "repl.h"
+
+static int dumping_to_ldif= 0;
+static int doing_replica_init= 0;
+static char **include_suffix= NULL;
+
+/*
+ * This is passed the slapd command line arguments.
+ */
+void
+repl_entry_init(int argc, char** argv)
+{
+ int i;
+ for(i=1;i<argc;i++)
+ {
+ if(strcmp(argv[i],"db2ldif")==0)
+ {
+ dumping_to_ldif= 1;
+ }
+ if(strcmp(argv[i],"-r")==0)
+ {
+ doing_replica_init= 1;
+ }
+ if(strcmp(argv[i],"-s")==0)
+ {
+ char *s= slapi_dn_normalize ( slapi_ch_strdup(argv[i+1]) );
+ charray_add(&include_suffix,s);
+ i++;
+ }
+ }
+}
diff --git a/ldap/servers/plugins/replication/repl_ext.c b/ldap/servers/plugins/replication/repl_ext.c
new file mode 100644
index 00000000..4ad28726
--- /dev/null
+++ b/ldap/servers/plugins/replication/repl_ext.c
@@ -0,0 +1,113 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+/* repl_ext.c - manages operation extensions created by the
+ * replication system
+ */
+
+
+#include "repl.h"
+
+/* structure with information for each extension */
+typedef struct repl_ext
+{
+ char *object_name; /* name of the object extended */
+ int object_type; /* handle to the extended object */
+ int handle; /* extension handle */
+} repl_ext;
+
+/* ----------------------------- Supplier ----------------------------- */
+
+static repl_ext repl_sup_ext_list [REPL_EXT_ALL];
+
+/* initializes replication extensions */
+void repl_sup_init_ext ()
+{
+ int rc;
+
+ /* populate the extension list */
+ repl_sup_ext_list[REPL_SUP_EXT_OP].object_name = SLAPI_EXT_OPERATION;
+
+ rc = slapi_register_object_extension(repl_plugin_name,
+ SLAPI_EXT_OPERATION,
+ supplier_operation_extension_constructor,
+ supplier_operation_extension_destructor,
+ &repl_sup_ext_list[REPL_SUP_EXT_OP].object_type,
+ &repl_sup_ext_list[REPL_SUP_EXT_OP].handle);
+
+ if(rc!=0)
+ {
+ PR_ASSERT(0); /* JCMREPL Argh */
+ }
+}
+
+void* repl_sup_get_ext (ext_type type, void *object)
+{
+ /* find the requested extension */
+ repl_ext ext = repl_sup_ext_list [type];
+
+ void* data = slapi_get_object_extension(ext.object_type, object, ext.handle);
+
+ return data;
+}
+
+/* ----------------------------- Consumer ----------------------------- */
+
+static repl_ext repl_con_ext_list [REPL_EXT_ALL];
+
+/* initializes replication extensions */
+void repl_con_init_ext ()
+{
+ int rc;
+
+ /* populate the extension list */
+ repl_con_ext_list[REPL_CON_EXT_OP].object_name = SLAPI_EXT_OPERATION;
+ rc = slapi_register_object_extension(repl_plugin_name,
+ SLAPI_EXT_OPERATION,
+ consumer_operation_extension_constructor,
+ consumer_operation_extension_destructor,
+ &repl_con_ext_list[REPL_CON_EXT_OP].object_type,
+ &repl_con_ext_list[REPL_CON_EXT_OP].handle);
+ if(rc!=0)
+ {
+ PR_ASSERT(0); /* JCMREPL Argh */
+ }
+
+ repl_con_ext_list[REPL_CON_EXT_CONN].object_name = SLAPI_EXT_CONNECTION;
+ rc = slapi_register_object_extension(repl_plugin_name,
+ SLAPI_EXT_CONNECTION,
+ consumer_connection_extension_constructor,
+ consumer_connection_extension_destructor,
+ &repl_con_ext_list[REPL_CON_EXT_CONN].object_type,
+ &repl_con_ext_list[REPL_CON_EXT_CONN].handle);
+ if(rc!=0)
+ {
+ PR_ASSERT(0); /* JCMREPL Argh */
+ }
+
+ repl_con_ext_list[REPL_CON_EXT_MTNODE].object_name = SLAPI_EXT_MTNODE;
+ rc = slapi_register_object_extension(repl_plugin_name,
+ SLAPI_EXT_MTNODE,
+ multimaster_mtnode_extension_constructor,
+ multimaster_mtnode_extension_destructor,
+ &repl_con_ext_list[REPL_CON_EXT_MTNODE].object_type,
+ &repl_con_ext_list[REPL_CON_EXT_MTNODE].handle);
+ if(rc!=0)
+ {
+ PR_ASSERT(0); /* JCMREPL Argh */
+ }
+}
+
+void* repl_con_get_ext (ext_type type, void *object)
+{
+ /* find the requested extension */
+ repl_ext ext = repl_con_ext_list [type];
+
+ void* data = slapi_get_object_extension(ext.object_type, object, ext.handle);
+
+ return data;
+}
+
+
diff --git a/ldap/servers/plugins/replication/repl_extop.c b/ldap/servers/plugins/replication/repl_extop.c
new file mode 100644
index 00000000..b13ad6ac
--- /dev/null
+++ b/ldap/servers/plugins/replication/repl_extop.c
@@ -0,0 +1,1134 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+
+#include "slapi-plugin.h"
+#include "repl.h"
+#include "repl5.h"
+#include "repl5_prot_private.h"
+#include "cl5_api.h"
+
+
+/*
+ * repl_extop.c - there are two types of functions in this file:
+ * - Code that implements an extended operation plugin.
+ * The replication DLL arranges for this code to
+ * be called when a StartNSDS50ReplicationRequest
+ * or an EndNSDS50ReplicationRequest extended operation
+ * is received.
+ * - Code that sends extended operations on an already-
+ * established client connection.
+ *
+ * The requestValue portion of the StartNSDS50ReplicationRequest
+ * looks like this:
+ *
+ * requestValue ::= SEQUENCE {
+ * replProtocolOID LDAPOID,
+ * replicatedTree LDAPDN,
+ supplierRUV OCTET STRING
+ * referralURLs SET of LDAPURL OPTIONAL
+ * csn OCTET STRING OPTIONAL
+ * }
+ *
+ */
+static int check_replica_id_uniqueness(Replica *replica, RUV *supplier_ruv);
+
+static int
+encode_ruv (BerElement *ber, const RUV *ruv)
+{
+ int rc = LDAP_SUCCESS;
+ struct berval **bvals = NULL;
+
+ PR_ASSERT (ber);
+ PR_ASSERT (ruv);
+
+ if (ruv_to_bervals(ruv, &bvals) != 0)
+ {
+ rc = LDAP_OPERATIONS_ERROR;
+ goto done;
+ }
+
+ if (ber_printf(ber, "[V]", bvals) == -1)
+ {
+ rc = LDAP_ENCODING_ERROR;
+ goto done;
+ }
+
+ rc = LDAP_SUCCESS;
+
+done:
+ if (bvals)
+ ber_bvecfree (bvals);
+
+ return rc;
+}
+
+static struct berval *
+create_NSDS50ReplicationExtopPayload(const char *protocol_oid,
+ const char *repl_root, char **extra_referrals, CSN *csn,
+ int send_end)
+{
+ struct berval *req_data = NULL;
+ BerElement *tmp_bere = NULL;
+ int rc = 0;
+ const char *csnstr = NULL;
+ Object *repl_obj, *ruv_obj = NULL;
+ Replica *repl;
+ RUV *ruv;
+ Slapi_DN *sdn;
+
+ PR_ASSERT(protocol_oid != NULL || send_end);
+ PR_ASSERT(repl_root != NULL);
+
+ /* Create the request data */
+
+ if ((tmp_bere = der_alloc()) == NULL)
+ {
+ rc = LDAP_ENCODING_ERROR;
+ goto loser;
+ }
+ if (!send_end)
+ {
+ if (ber_printf(tmp_bere, "{ss", protocol_oid, repl_root) == -1)
+ {
+ rc = LDAP_ENCODING_ERROR;
+ goto loser;
+ }
+ }
+ else
+ {
+ if (ber_printf(tmp_bere, "{s", repl_root) == -1)
+ {
+ rc = LDAP_ENCODING_ERROR;
+ goto loser;
+ }
+ }
+
+ sdn = slapi_sdn_new_dn_byref(repl_root);
+ repl_obj = replica_get_replica_from_dn (sdn);
+ if (repl_obj == NULL)
+ {
+ rc = LDAP_OPERATIONS_ERROR;
+ goto loser;
+ }
+
+ repl = (Replica*)object_get_data (repl_obj);
+ PR_ASSERT (repl);
+ ruv_obj = replica_get_ruv (repl);
+ if (ruv_obj == NULL)
+ {
+ rc = LDAP_OPERATIONS_ERROR;
+ goto loser;
+ }
+ ruv = object_get_data(ruv_obj);
+ PR_ASSERT(ruv);
+
+ /* send supplier's ruv so that consumer can build its own referrals.
+ In case of total protocol, it is also used as consumer's ruv once
+ protocol successfully completes */
+ /* We need to encode and send each time the local ruv in case we have changed it */
+ rc = encode_ruv (tmp_bere, ruv);
+ if (rc != 0)
+ {
+ goto loser;
+ }
+
+ if (!send_end)
+ {
+ char s[CSN_STRSIZE];
+ ReplicaId rid;
+ char *local_replica_referral[2] = {0};
+ char **referrals_to_send = NULL;
+ /* Add the referral URL(s), if present */
+ rid = replica_get_rid(repl);
+ if (!ruv_contains_replica(ruv, rid))
+ {
+ /*
+ * In the event that there is no RUV component for this replica (e.g.
+ * if the database was just loaded from LDIF and no local CSNs have been
+ * generated), then we need to explicitly add this server to the list
+ * of referrals, since it wouldn't have been sent with the RUV.
+ */
+ local_replica_referral[0] = (char *)multimaster_get_local_purl(); /* XXXggood had to cast away const */
+ }
+ charray_merge(&referrals_to_send, extra_referrals, 0);
+ charray_merge(&referrals_to_send, local_replica_referral, 0);
+ if (NULL != referrals_to_send)
+ {
+ if (ber_printf(tmp_bere, "[v]", referrals_to_send) == -1)
+ {
+ rc = LDAP_ENCODING_ERROR;
+ goto loser;
+ }
+ slapi_ch_free((void **)&referrals_to_send);
+ }
+ /* Add the CSN */
+ PR_ASSERT(NULL != csn);
+ if (ber_printf(tmp_bere, "s", csnstr = csn_as_string(csn,PR_FALSE,s)) == -1)
+ {
+ rc = LDAP_ENCODING_ERROR;
+ goto loser;
+ }
+ }
+
+ if (ber_printf(tmp_bere, "}") == -1)
+ {
+ rc = LDAP_ENCODING_ERROR;
+ goto loser;
+ }
+
+ if (ber_flatten(tmp_bere, &req_data) == -1)
+ {
+ rc = LDAP_LOCAL_ERROR;
+ goto loser;
+ }
+ /* Success */
+ goto done;
+
+loser:
+ /* Free stuff we allocated */
+ if (NULL != req_data)
+ {
+ ber_bvfree(req_data); req_data = NULL;
+ }
+
+done:
+ if (NULL != tmp_bere)
+ {
+ ber_free(tmp_bere, 1); tmp_bere = NULL;
+ }
+ if (NULL != sdn)
+ {
+ slapi_sdn_free (&sdn); /* Put on stack instead of allocating? */
+ }
+ if (NULL != repl_obj)
+ {
+ object_release (repl_obj);
+ }
+ if (NULL != ruv_obj)
+ {
+ object_release (ruv_obj);
+ }
+ return req_data;
+}
+
+
+struct berval *
+NSDS50StartReplicationRequest_new(const char *protocol_oid,
+ const char *repl_root, char **extra_referrals, CSN *csn)
+{
+ return(create_NSDS50ReplicationExtopPayload(protocol_oid,
+ repl_root, extra_referrals, csn, 0));
+}
+
+struct berval *
+NSDS50EndReplicationRequest_new(char *repl_root)
+{
+ return(create_NSDS50ReplicationExtopPayload(NULL, repl_root, NULL, NULL, 1));
+}
+
+static int
+decode_ruv (BerElement *ber, RUV **ruv)
+{
+ int rc = -1;
+ struct berval **bvals = NULL;
+
+ PR_ASSERT (ber && ruv);
+
+ if (ber_scanf(ber, "[V]", &bvals) == -1)
+ {
+ goto done;
+ }
+
+ if (ruv_init_from_bervals(bvals, ruv) != 0)
+ {
+ goto done;
+ }
+
+ rc = 0;
+done:
+ if (bvals)
+ ber_bvecfree (bvals);
+
+ return rc;
+}
+
+/*
+ * Decode an NSDS50 Start Replication Request extended
+ * operation. Returns 0 on success, -1 on decoding error.
+ * The caller is responsible for freeing protocol_oid,
+ * repl_root, referrals, and csn.
+ */
+static int
+decode_startrepl_extop(Slapi_PBlock *pb, char **protocol_oid, char **repl_root,
+ RUV **supplier_ruv, char ***extra_referrals, char **csnstr)
+{
+ char *extop_oid = NULL;
+ struct berval *extop_value = NULL;
+ BerElement *tmp_bere = NULL;
+ unsigned long len;
+ int rc = 0;
+
+ PR_ASSERT (pb && protocol_oid && repl_root && supplier_ruv && extra_referrals && csnstr);
+
+ *protocol_oid = NULL;
+ *repl_root = NULL;
+ *supplier_ruv = NULL;
+ *extra_referrals = NULL;
+ *csnstr = NULL;
+
+ slapi_pblock_get(pb, SLAPI_EXT_OP_REQ_OID, &extop_oid);
+ slapi_pblock_get(pb, SLAPI_EXT_OP_REQ_VALUE, &extop_value);
+
+ if (NULL == extop_oid ||
+ strcmp(extop_oid, REPL_START_NSDS50_REPLICATION_REQUEST_OID) != 0 ||
+ NULL == extop_value)
+ {
+ /* bogus */
+ rc = -1;
+ goto free_and_return;
+ }
+
+ if ((tmp_bere = ber_init(extop_value)) == NULL)
+ {
+ rc = -1;
+ goto free_and_return;
+ }
+ if (ber_scanf(tmp_bere, "{") == -1)
+ {
+ rc = -1;
+ goto free_and_return;
+ }
+ /* Get the required protocol OID and root of replicated subtree */
+ if (ber_get_stringa(tmp_bere, protocol_oid) == -1)
+ {
+ rc = -1;
+ goto free_and_return;
+ }
+ if (ber_get_stringa(tmp_bere, repl_root) == -1)
+ {
+ rc = -1;
+ goto free_and_return;
+ }
+
+ /* get supplier's ruv */
+ if (decode_ruv (tmp_bere, supplier_ruv) == -1)
+ {
+ rc = -1;
+ goto free_and_return;
+ }
+
+ /* Get the optional set of referral URLs */
+ if (ber_peek_tag(tmp_bere, &len) == LBER_SET)
+ {
+ if (ber_scanf(tmp_bere, "[v]", extra_referrals) == -1)
+ {
+ rc = -1;
+ goto free_and_return;
+ }
+ }
+ /* Get the optional CSN */
+ if (ber_peek_tag(tmp_bere, &len) == LBER_OCTETSTRING)
+ {
+ if (ber_get_stringa(tmp_bere, csnstr) == -1)
+ {
+ rc = -1;
+ goto free_and_return;
+ }
+ }
+ if (ber_scanf(tmp_bere, "}") == -1)
+ {
+ rc = -1;
+ goto free_and_return;
+ }
+
+free_and_return:
+ if (-1 == rc)
+ {
+ /* Free everything when error encountered */
+
+ /* slapi_ch_free accepts NULL pointer */
+ slapi_ch_free ((void**)protocol_oid);
+ slapi_ch_free ((void**)repl_root);
+ slapi_ch_free ((void **)extra_referrals);
+ slapi_ch_free ((void**)csnstr);
+
+ if (*supplier_ruv)
+ {
+ ruv_destroy (supplier_ruv);
+ }
+
+ }
+ if (NULL != tmp_bere)
+ {
+ ber_free(tmp_bere, 1);
+ tmp_bere = NULL;
+ }
+
+ return rc;
+}
+
+
+/*
+ * Decode an NSDS50 End Replication Request extended
+ * operation. Returns 0 on success, -1 on decoding error.
+ * The caller is responsible for freeing repl_root.
+ */
+static int
+decode_endrepl_extop(Slapi_PBlock *pb, char **repl_root)
+{
+ char *extop_oid = NULL;
+ struct berval *extop_value = NULL;
+ BerElement *tmp_bere = NULL;
+ int rc = 0;
+
+ slapi_pblock_get(pb, SLAPI_EXT_OP_REQ_OID, &extop_oid);
+ slapi_pblock_get(pb, SLAPI_EXT_OP_REQ_VALUE, &extop_value);
+
+ if (NULL == extop_oid ||
+ strcmp(extop_oid, REPL_END_NSDS50_REPLICATION_REQUEST_OID) != 0 ||
+ NULL == extop_value)
+ {
+ /* bogus */
+ rc = -1;
+ goto free_and_return;
+ }
+
+ if ((tmp_bere = ber_init(extop_value)) == NULL)
+ {
+ rc = -1;
+ goto free_and_return;
+ }
+ if (ber_scanf(tmp_bere, "{") == -1)
+ {
+ rc = -1;
+ goto free_and_return;
+ }
+ /* Get the required root of replicated subtree */
+ if (ber_get_stringa(tmp_bere, repl_root) == -1)
+ {
+ rc = -1;
+ goto free_and_return;
+ }
+ if (ber_scanf(tmp_bere, "}") == -1)
+ {
+ rc = -1;
+ goto free_and_return;
+ }
+
+free_and_return:
+ if (NULL != tmp_bere)
+ {
+ ber_free(tmp_bere, 1);
+ tmp_bere = NULL;
+ }
+
+ return rc;
+}
+
+
+
+
+/*
+ * Decode an NSDS50ReplicationResponse extended response.
+ * The extended response just contains a sequence that contains:
+ * 1) An integer response code
+ * 2) An optional array of bervals representing the consumer
+ * replica's update vector
+ * Returns 0 on success, or -1 if the response could not be parsed.
+ */
+int
+decode_repl_ext_response(struct berval *data, int *response_code,
+ struct berval ***ruv_bervals)
+{
+ BerElement *tmp_bere = NULL;
+ int return_value = 0;
+
+ PR_ASSERT(NULL != response_code);
+ PR_ASSERT(NULL != ruv_bervals);
+
+ if (NULL == data || NULL == response_code || NULL == ruv_bervals)
+ {
+ return_value = -1;
+ }
+ else
+ {
+ unsigned long len, tag = 0;
+ long temp_response_code = 0;
+ *ruv_bervals = NULL;
+ if ((tmp_bere = ber_init(data)) == NULL)
+ {
+ return_value = -1;
+ }
+ else if (ber_scanf(tmp_bere, "{e", &temp_response_code) == -1)
+ {
+ return_value = -1;
+ }
+ else if ((tag = ber_peek_tag(tmp_bere, &len)) == LBER_SEQUENCE)
+ {
+ if (ber_scanf(tmp_bere, "{V}}", ruv_bervals) == -1)
+ {
+ return_value = -1;
+ }
+ } else if (ber_scanf(tmp_bere, "}") == -1)
+ {
+ return_value = -1;
+ }
+ *response_code = (int)temp_response_code;
+ }
+ if (0 != return_value)
+ {
+ if (NULL != *ruv_bervals)
+ {
+ ber_bvecfree(*ruv_bervals);
+ }
+ }
+ if (NULL != tmp_bere)
+ {
+ ber_free(tmp_bere, 1); tmp_bere = NULL;
+ }
+ return return_value;
+}
+
+
+/*
+ * This plugin entry point is called whenever a
+ * StartNSDS50ReplicationRequest is received.
+ */
+int
+multimaster_extop_StartNSDS50ReplicationRequest(Slapi_PBlock *pb)
+{
+ int return_value = SLAPI_PLUGIN_EXTENDED_NOT_HANDLED;
+ int response = 0;
+ int rc = 0;
+ BerElement *resp_bere = NULL;
+ struct berval *resp_bval = NULL;
+ char *protocol_oid = NULL;
+ char *repl_root = NULL;
+ Slapi_DN *repl_root_sdn = NULL;
+ char **referrals = NULL;
+ Object *replica_object = NULL;
+ Replica *replica = NULL;
+ void *conn;
+ consumer_connection_extension *connext = NULL;
+ CSN *mycsn = NULL;
+ char *replicacsnstr = NULL;
+ CSN *replicacsn = NULL;
+ int zero = 0;
+ int one = 1;
+ RUV *ruv = NULL;
+ struct berval **ruv_bervals = NULL;
+ CSNGen *gen = NULL;
+ Object *gen_obj = NULL;
+ Slapi_DN *bind_sdn = NULL;
+ char *bind_dn = NULL;
+ Object *ruv_object = NULL;
+ RUV *supplier_ruv = NULL;
+ int connid, opid;
+ PRBool isInc = PR_FALSE; /* true if incremental update */
+ char *locking_purl = NULL; /* the supplier contacting us */
+ char *current_purl = NULL; /* the supplier which already has exclusive access */
+ char locking_session[24];
+
+ /* Decode the extended operation */
+ if (decode_startrepl_extop(pb, &protocol_oid, &repl_root, &supplier_ruv,
+ &referrals, &replicacsnstr) == -1)
+ {
+ response = NSDS50_REPL_DECODING_ERROR;
+ goto send_response;
+ }
+ if (NULL == protocol_oid || NULL == repl_root || NULL == replicacsnstr)
+ {
+ response = NSDS50_REPL_DECODING_ERROR;
+ goto send_response;
+ }
+
+ connid = 0;
+ slapi_pblock_get(pb, SLAPI_CONN_ID, &connid);
+ opid = 0;
+ slapi_pblock_get(pb, SLAPI_OPERATION_ID, &opid);
+
+ /*
+ * Get a hold of the connection extension object and
+ * make sure it's there.
+ */
+ slapi_pblock_get(pb, SLAPI_CONNECTION, &conn);
+ connext = (consumer_connection_extension *)repl_con_get_ext(
+ REPL_CON_EXT_CONN, conn);
+ if (NULL == connext)
+ {
+ /* Something bad happened. Don't go any further */
+ response = NSDS50_REPL_INTERNAL_ERROR;
+ goto send_response;
+ }
+
+ /* Verify that we know about this replication protocol OID */
+ if (strcmp(protocol_oid, REPL_NSDS50_INCREMENTAL_PROTOCOL_OID) == 0)
+ {
+ /* Stash info that this is an incremental update session */
+ connext->repl_protocol_version = REPL_PROTOCOL_50_INCREMENTAL;
+ slapi_log_error(SLAPI_LOG_REPL, repl_plugin_name,
+ "conn=%d op=%d repl=\"%s\": Begin incremental protocol\n",
+ connid, opid, repl_root);
+ isInc = PR_TRUE;
+ }
+ else if (strcmp(protocol_oid, REPL_NSDS50_TOTAL_PROTOCOL_OID) == 0)
+ {
+ /* Stash info that this is a total update session */
+ if (NULL != connext)
+ {
+ connext->repl_protocol_version = REPL_PROTOCOL_50_TOTALUPDATE;
+ }
+ slapi_log_error(SLAPI_LOG_REPL, repl_plugin_name,
+ "conn=%d op=%d repl=\"%s\": Begin total protocol\n",
+ connid, opid, repl_root);
+ isInc = PR_FALSE;
+ }
+ else
+ {
+ /* Unknown replication protocol */
+ response = NSDS50_REPL_UNKNOWN_UPDATE_PROTOCOL;
+ goto send_response;
+ }
+
+ /* Verify that repl_root names a valid replicated area */
+ if ((repl_root_sdn = slapi_sdn_new_dn_byval(repl_root)) == NULL)
+ {
+ response = NSDS50_REPL_INTERNAL_ERROR;
+ goto send_response;
+ }
+
+ /* see if this replica is being configured and wait for it */
+ if (replica_is_being_configured(repl_root))
+ {
+ slapi_log_error(SLAPI_LOG_REPL, repl_plugin_name,
+ "conn=%d op=%d replica=\"%s\": "
+ "Replica is being configured: try again later\n",
+ connid, opid, repl_root);
+ response = NSDS50_REPL_REPLICA_BUSY;
+ goto send_response;
+ }
+
+ replica_object = replica_get_replica_from_dn(repl_root_sdn);
+ if (NULL != replica_object)
+ {
+ replica = object_get_data(replica_object);
+ }
+ if (NULL == replica)
+ {
+ response = NSDS50_REPL_NO_SUCH_REPLICA;
+ goto send_response;
+ }
+
+ /* check that this replica is not a 4.0 consumer */
+ if (replica_is_legacy_consumer (replica))
+ {
+ response = NSDS50_REPL_LEGACY_CONSUMER;
+ goto send_response;
+ }
+
+ /* Check that bind dn is authorized to supply replication updates */
+ slapi_pblock_get(pb, SLAPI_CONN_DN, &bind_dn); /* bind_dn is allocated */
+ bind_sdn = slapi_sdn_new_dn_passin(bind_dn);
+ if (replica_is_updatedn(replica, bind_sdn) == PR_FALSE)
+ {
+ response = NSDS50_REPL_PERMISSION_DENIED;
+ goto send_response;
+ }
+
+ /* Check received CSN for clock skew */
+ gen_obj = replica_get_csngen(replica);
+ if (NULL != gen_obj)
+ {
+ gen = object_get_data(gen_obj);
+ if (NULL != gen)
+ {
+ if (csngen_new_csn(gen, &mycsn, PR_FALSE /* notify */) == CSN_SUCCESS)
+ {
+ replicacsn = csn_new_by_string(replicacsnstr);
+ if (NULL != replicacsn)
+ {
+ /* ONREPL - we used to manage clock skew here. However, csn generator
+ code already does it. The csngen also manages local skew caused by
+ system clock reset, so to keep it consistent, I removed code from here */
+ time_t diff = 0L;
+ diff = csn_time_difference(mycsn, replicacsn);
+ if (diff > 0)
+ {
+ /* update the state of the csn generator */
+ rc = csngen_adjust_time (gen, replicacsn);
+ if (rc == CSN_LIMIT_EXCEEDED) /* too much skew */
+ {
+ response = NSDS50_REPL_EXCESSIVE_CLOCK_SKEW;
+ goto send_response;
+ }
+ }
+ else if (diff <= 0)
+ {
+ /* Supplier's clock is behind ours */
+ /* XXXggood check if CSN smaller than purge point */
+ /* response = NSDS50_REPL_BELOW_PURGEPOINT; */
+ /* goto send_response; */
+ }
+ }
+ else
+ {
+ /* Oops, csnstr couldn't be converted */
+ response = NSDS50_REPL_INTERNAL_ERROR;
+ goto send_response;
+ }
+ }
+ else
+ {
+ /* Oops, csn generator failed */
+ response = NSDS50_REPL_INTERNAL_ERROR;
+ goto send_response;
+ }
+
+ /* update csn generator's state from the supplier's ruv */
+ rc = replica_update_csngen_state (replica, supplier_ruv); /* too much skew */
+ if (rc != 0)
+ {
+ response = NSDS50_REPL_EXCESSIVE_CLOCK_SKEW;
+ goto send_response;
+ }
+ }
+ else
+ {
+ /* Oops, no csn generator */
+ response = NSDS50_REPL_INTERNAL_ERROR;
+ goto send_response;
+ }
+ }
+ else
+ {
+ /* Oops, no csn generator object */
+ response = NSDS50_REPL_INTERNAL_ERROR;
+ goto send_response;
+ }
+
+ if (check_replica_id_uniqueness(replica, supplier_ruv) != 0){
+ slapi_log_error(SLAPI_LOG_REPL, repl_plugin_name,
+ "conn=%d op=%d repl=\"%s\": "
+ "Replica has same replicaID %d as supplier\n",
+ connid, opid, repl_root, replica_get_rid(replica));
+ response = NSDS50_REPL_REPLICAID_ERROR;
+ goto send_response;
+ }
+
+ /* Attempt to acquire exclusive access to the replicated area */
+ /* Since partial URL is always the master, this locking_purl does not
+ * help us to know the true locker when it is a hub. Change to use
+ * the session's conn id and op id to identify the the supplier.
+ */
+ /* junkrc = ruv_get_first_id_and_purl(supplier_ruv, &junkrid, &locking_purl); */
+ sprintf(locking_session, "conn=%d id=%d", connid, opid);
+ locking_purl = &locking_session[0];
+ if (replica_get_exclusive_access(replica, &isInc, connid, opid,
+ locking_purl,
+ &current_purl) == PR_FALSE)
+ {
+ locking_purl = NULL; /* no dangling pointers */
+ response = NSDS50_REPL_REPLICA_BUSY;
+ goto send_response;
+ }
+ else
+ {
+ locking_purl = NULL; /* no dangling pointers */
+ /* Stick the replica object pointer in the connection extension */
+ connext->replica_acquired = (void *)replica_object;
+ replica_object = NULL;
+ }
+
+ /* If this is incremental protocol get replica's ruv to return to the supplier */
+ if (connext->repl_protocol_version == REPL_PROTOCOL_50_INCREMENTAL)
+ {
+ ruv_object = replica_get_ruv(replica);
+ if (NULL != ruv_object)
+ {
+ ruv = object_get_data(ruv_object);
+ (void)ruv_to_bervals(ruv, &ruv_bervals);
+ object_release(ruv_object);
+ }
+ }
+
+ /*
+ * Save the supplier ruv in the connection extension so it can
+ * either (a) be installed upon successful initialization (if this
+ * is a total update session) or used to update referral information
+ * for new replicas that show up in the supplier's RUV.
+ */
+ /*
+ * the supplier_ruv may have been set before, so free it here
+ * (in ruv_copy_and_destroy)
+ */
+ ruv_copy_and_destroy(&supplier_ruv, (RUV **)&connext->supplier_ruv);
+
+ if (connext->repl_protocol_version == REPL_PROTOCOL_50_INCREMENTAL)
+ {
+ /* The supplier ruv may have changed, so let's update the referrals */
+ consumer5_set_mapping_tree_state_for_replica(replica, connext->supplier_ruv);
+ }
+ else /* full protocol */
+ {
+ char *mtnstate = slapi_mtn_get_state(repl_root_sdn);
+ char **mtnreferral = slapi_mtn_get_referral(repl_root_sdn);
+
+ /* richm 20010831 - set the mapping tree to the referral state *before*
+ we invoke slapi_start_bulk_import - see bug 556992 -
+ slapi_start_bulk_import sets the database offline, if an operation comes
+ in while the database is offline but the mapping tree is not referring yet,
+ the server gets confused
+ */
+ /* During a total update we refer *all* operations */
+ repl_set_mtn_state_and_referrals(repl_root_sdn, STATE_REFERRAL,
+ connext->supplier_ruv, NULL, referrals);
+ /* LPREPL - check the return code.
+ * But what do we do if mapping tree could not be updated ? */
+
+ /* start the bulk import */
+ slapi_pblock_set (pb, SLAPI_TARGET_DN, repl_root);
+ rc = slapi_start_bulk_import (pb);
+ if (rc != LDAP_SUCCESS)
+ {
+ response = NSDS50_REPL_INTERNAL_ERROR;
+ /* reset the mapping tree state to what it was before
+ we tried to do the bulk import */
+ repl_set_mtn_state_and_referrals(repl_root_sdn, mtnstate,
+ NULL, NULL, mtnreferral);
+ slapi_ch_free_string(&mtnstate);
+ charray_free(mtnreferral);
+ mtnreferral = NULL;
+
+ goto send_response;
+ }
+ slapi_ch_free_string(&mtnstate);
+ charray_free(mtnreferral);
+ mtnreferral = NULL;
+ }
+
+ response = NSDS50_REPL_REPLICA_READY;
+ /* Set the "is replication session" flag in the connection extension */
+ slapi_pblock_set( pb, SLAPI_CONN_IS_REPLICATION_SESSION, &one );
+ connext->isreplicationsession = 1;
+ /* Save away the connection */
+ slapi_pblock_get(pb, SLAPI_CONNECTION, &connext->connection);
+
+send_response:
+ if (response != NSDS50_REPL_REPLICA_READY)
+ {
+ int resp_log_level = SLAPI_LOG_FATAL;
+ char purlstr[1024] = {0};
+ if (current_purl)
+ sprintf(purlstr, " locked by %s for %s update", current_purl,
+ isInc ? "incremental" : "total");
+
+ /* Don't log replica busy as errors - these are almost always not
+ errors - use the replication monitoring tools to determine if
+ a replica is not converging, then look for pathological replica
+ busy errors by turning on the replication log level */
+ if (response == NSDS50_REPL_REPLICA_BUSY) {
+ resp_log_level = SLAPI_LOG_REPL;
+ }
+
+ slapi_log_error (resp_log_level, repl_plugin_name,
+ "conn=%d op=%d replica=\"%s\": "
+ "Unable to acquire replica: error: %s%s\n",
+ connid, opid,
+ (replica ? slapi_sdn_get_dn(replica_get_root(replica)) : "unknown"),
+ protocol_response2string (response), purlstr);
+ }
+ /* Send the response */
+ if ((resp_bere = der_alloc()) == NULL)
+ {
+ /* ONREPL - not sure what we suppose to do here */
+ }
+ ber_printf(resp_bere, "{e", response);
+ if (NULL != ruv_bervals)
+ {
+ ber_printf(resp_bere, "{V}", ruv_bervals);
+ }
+ ber_printf(resp_bere, "}");
+ ber_flatten(resp_bere, &resp_bval);
+ slapi_pblock_set(pb, SLAPI_EXT_OP_RET_OID, REPL_NSDS50_REPLICATION_RESPONSE_OID);
+ slapi_pblock_set(pb, SLAPI_EXT_OP_RET_VALUE, resp_bval);
+ slapi_log_error(SLAPI_LOG_REPL, repl_plugin_name,
+ "conn=%d op=%d repl=\"%s\": "
+ "StartNSDS50ReplicationRequest: response=%d rc=%d\n",
+ connid, opid, repl_root,
+ response, rc);
+ slapi_send_ldap_result(pb, LDAP_SUCCESS, NULL, NULL, 0, NULL);
+
+ return_value = SLAPI_PLUGIN_EXTENDED_SENT_RESULT;
+
+ slapi_ch_free_string(&current_purl);
+
+ /* protocol_oid */
+ /* slapi_ch_free accepts NULL pointer */
+ slapi_ch_free((void **)&protocol_oid);
+
+ /* repl_root */
+ slapi_ch_free((void **)&repl_root);
+
+ /* supplier's ruv */
+ if (supplier_ruv)
+ {
+ ruv_destroy (&supplier_ruv);
+ }
+ /* referrals */
+ slapi_ch_free((void **)&referrals);
+
+ /* replicacsnstr */
+ slapi_ch_free((void **)&replicacsnstr);
+
+ /* repl_root_sdn */
+ if (NULL != repl_root_sdn)
+ {
+ slapi_sdn_free(&repl_root_sdn);
+ }
+ if (NSDS50_REPL_REPLICA_READY != response)
+ {
+ /*
+ * Something went wrong, and we never told the other end that the
+ * replica had been acquired, so we'd better release it.
+ */
+ if (NULL != connext && NULL != connext->replica_acquired)
+ {
+ Object *r_obj = (Object*)connext->replica_acquired;
+ replica_relinquish_exclusive_access((Replica*)object_get_data (r_obj),
+ connid, opid);
+ }
+ /* Remove any flags that would indicate repl session in progress */
+ if (NULL != connext)
+ {
+ connext->repl_protocol_version = REPL_PROTOCOL_UNKNOWN;
+ connext->isreplicationsession = 0;
+ }
+ slapi_pblock_set( pb, SLAPI_CONN_IS_REPLICATION_SESSION, &zero );
+ }
+ /* Release reference to replica_object */
+ if (NULL != replica_object)
+ {
+ object_release(replica_object);
+ }
+ /* bind_sdn */
+ if (NULL != bind_sdn)
+ {
+ slapi_sdn_free(&bind_sdn);
+ }
+ /* Release reference to gen_obj */
+ if (NULL != gen_obj)
+ {
+ object_release(gen_obj);
+ }
+ /* mycsn */
+ if (NULL != mycsn)
+ {
+ csn_free(&mycsn);
+ }
+ /* replicacsn */
+ if (NULL != replicacsn)
+ {
+ csn_free(&replicacsn);
+ }
+ /* resp_bere */
+ if (NULL != resp_bere)
+ {
+ ber_free(resp_bere, 1);
+ }
+ /* resp_bval */
+ if (NULL != resp_bval)
+ {
+ ber_bvfree(resp_bval);
+ }
+ /* ruv_bervals */
+ if (NULL != ruv_bervals)
+ {
+ ber_bvecfree(ruv_bervals);
+ }
+
+ return return_value;
+}
+
+/*
+ * This plugin entry point is called whenever an
+ * EndNSDS50ReplicationRequest is received.
+ * XXXggood this code is not finished.
+ */
+int
+multimaster_extop_EndNSDS50ReplicationRequest(Slapi_PBlock *pb)
+{
+ int return_value = SLAPI_PLUGIN_EXTENDED_NOT_HANDLED;
+ char *repl_root = NULL;
+ BerElement *resp_bere = NULL;
+ struct berval *resp_bval = NULL;
+ int response;
+ void *conn;
+ consumer_connection_extension *connext = NULL;
+ int rc;
+ int connid=-1, opid=-1;
+
+ /* Decode the extended operation */
+ if (decode_endrepl_extop(pb, &repl_root) == -1)
+ {
+ response = NSDS50_REPL_DECODING_ERROR;
+ }
+ else
+ {
+
+ /* First, verify that the current connection is a replication session */
+ /* XXXggood - do we need to wait around for any pending updates to complete?
+ I suppose it's possible that the end request may arrive asynchronously, before
+ we're really done processing all the updates.
+ */
+ /* Get a hold of the connection extension object */
+ slapi_pblock_get(pb, SLAPI_CONNECTION, &conn);
+ connext = (consumer_connection_extension *)repl_con_get_ext(
+ REPL_CON_EXT_CONN, conn);
+ if (NULL != connext && NULL != connext->replica_acquired)
+ {
+ int zero= 0;
+ Replica *r = (Replica*)object_get_data ((Object*)connext->replica_acquired);
+
+ /* if this is total protocol we need to install suppliers ruv for the replica */
+ if (connext->repl_protocol_version == REPL_PROTOCOL_50_TOTALUPDATE)
+ {
+ /* We no longer need to refer all operations...
+ * and update the referrals on the mapping tree node
+ */
+ consumer5_set_mapping_tree_state_for_replica(r, NULL);
+
+ /* LPREPL - First we clear the total in progress flag
+ Like this we know it's a normal termination of import. This is required by
+ the replication function that responds to backend state change.
+ If the flag is not clear, the callback knows that replication should not be
+ enabled again */
+ replica_set_state_flag(r, REPLICA_TOTAL_IN_PROGRESS, PR_TRUE /* clear flag */);
+
+ slapi_pblock_set (pb, SLAPI_TARGET_DN, repl_root);
+ slapi_stop_bulk_import (pb);
+
+ /* ONREPL - this is a bit of a hack. Once bulk import is finished,
+ the replication function that responds to backend state change
+ will be called. That function normally do all ruv and changelog
+ processing. However, in the case of replica initalization, it
+ will not do the right thing because supplier does not send its
+ ruv tombstone to the consumer. So that's why we need to do the
+ second processing here.
+ The supplier does not send its RUV entry because it could be
+ more up to date then the data send to the consumer.
+ The best solution I think, would be to "fake" on the supplier
+ an entry that corresponds to the ruv sent to the consumer and then
+ send it as part of the data */
+
+ if (cl5GetState () == CL5_STATE_OPEN)
+ {
+ rc = cl5DeleteDBSync (connext->replica_acquired);
+ }
+
+ replica_set_ruv (r, connext->supplier_ruv);
+ connext->supplier_ruv = NULL;
+
+ /* if changelog is enabled, we need to log a dummy change for the
+ smallest csn in the new ruv, so that this replica ca supply
+ other servers.
+ */
+ if (cl5GetState () == CL5_STATE_OPEN)
+ {
+ replica_log_ruv_elements (r);
+ }
+
+ /* ONREPL code that dealt with new RUV, etc was moved into the code
+ that enables replication when a backend comes back online. This
+ code is called once the bulk import is finished */
+ }
+ else if (connext->repl_protocol_version == REPL_PROTOCOL_50_INCREMENTAL)
+ {
+ /* The ruv from the supplier may have changed. Report the change on the
+ consumer side */
+
+ replica_update_ruv_consumer(r, connext->supplier_ruv);
+ }
+
+ /* Relinquish control of the replica */
+ slapi_pblock_get (pb, SLAPI_OPERATION_ID, &opid);
+ if (opid) slapi_pblock_get (pb, SLAPI_CONN_ID, &connid);
+ replica_relinquish_exclusive_access(r, connid, opid);
+ object_release ((Object*)connext->replica_acquired);
+ connext->replica_acquired = NULL;
+ connext->isreplicationsession= 0;
+ slapi_pblock_set( pb, SLAPI_CONN_IS_REPLICATION_SESSION, &zero );
+ response = NSDS50_REPL_REPLICA_RELEASE_SUCCEEDED;
+ /* Outbound replication agreements need to all be restarted now */
+ /* XXXGGOOD RESTART REEPL AGREEMENTS */
+ }
+ }
+
+ /* Send the response code */
+ if ((resp_bere = der_alloc()) == NULL)
+ {
+ rc = LDAP_ENCODING_ERROR;
+ goto free_and_return;
+ }
+ ber_printf(resp_bere, "{e}", response);
+ ber_flatten(resp_bere, &resp_bval);
+ slapi_pblock_set(pb, SLAPI_EXT_OP_RET_OID, REPL_NSDS50_REPLICATION_RESPONSE_OID);
+ slapi_pblock_set(pb, SLAPI_EXT_OP_RET_VALUE, resp_bval);
+ slapi_send_ldap_result(pb, LDAP_SUCCESS, NULL, NULL, 0, NULL);
+
+ return_value = SLAPI_PLUGIN_EXTENDED_SENT_RESULT;
+
+free_and_return:
+ /* repl_root */
+ slapi_ch_free((void **)&repl_root);
+
+ /* BerElement */
+ if (NULL != resp_bere)
+ {
+ ber_free(resp_bere, 1);
+ }
+ /* response */
+ if (NULL != resp_bval)
+ {
+ ber_bvfree(resp_bval);
+ }
+
+ return return_value;
+}
+
+/*
+ * This plugin entry point is a noop entry
+ * point. It's used when registering extops that
+ * are only used as responses. We'll never receive
+ * one of those, unsolicited, but we still want to
+ * register them so they appear in the
+ * supportedextension attribute in the root DSE.
+ */
+int
+extop_noop(Slapi_PBlock *pb)
+{
+ return SLAPI_PLUGIN_EXTENDED_NOT_HANDLED;
+}
+
+
+static int
+check_replica_id_uniqueness(Replica *replica, RUV *supplier_ruv)
+{
+ ReplicaId local_rid = replica_get_rid(replica);
+ ReplicaId sup_rid = 0;
+ char *sup_purl = NULL;
+
+ if (ruv_get_first_id_and_purl(supplier_ruv, &sup_rid, &sup_purl) == RUV_SUCCESS) {
+ /* ReplicaID Uniqueness is checked only on Masters */
+ if ((replica_get_type(replica) == REPLICA_TYPE_UPDATABLE) &&
+ (sup_rid == local_rid)) {
+ return 1;
+ }
+ }
+ return 0;
+}
+
+
+
diff --git a/ldap/servers/plugins/replication/repl_globals.c b/ldap/servers/plugins/replication/repl_globals.c
new file mode 100644
index 00000000..bee5dc4a
--- /dev/null
+++ b/ldap/servers/plugins/replication/repl_globals.c
@@ -0,0 +1,108 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+
+#include "nspr.h"
+#include "repl.h"
+
+char *repl_plugin_name = REPL_PLUGIN_NAME;
+char *repl_plugin_name_cl = REPL_PLUGIN_NAME " - changelog program";
+
+/* String constants (no need to change these for I18N) */
+
+#define CHANGETYPE_ADD "add"
+#define CHANGETYPE_DELETE "delete"
+#define CHANGETYPE_MODIFY "modify"
+#define CHANGETYPE_MODRDN "modrdn"
+#define CHANGETYPE_MODDN "moddn"
+#define ATTR_CHANGENUMBER "changenumber"
+#define ATTR_TARGETDN "targetdn"
+#define ATTR_CHANGETYPE "changetype"
+#define ATTR_NEWRDN "newrdn"
+#define ATTR_DELETEOLDRDN "deleteoldrdn"
+#define ATTR_CHANGES "changes"
+#define ATTR_NEWSUPERIOR "newsuperior"
+#define ATTR_CHANGETIME "changetime"
+#define ATTR_DATAVERSION "dataVersion"
+#define ATTR_CSN "csn"
+#define TYPE_COPYINGFROM "copyingFrom"
+#define TYPE_COPIEDFROM "copiedFrom"
+#define FILTER_COPYINGFROM "copyingFrom=*"
+#define FILTER_COPIEDFROM "copiedFrom=*"
+#define FILTER_OBJECTCLASS "objectclass=*"
+
+
+char *changetype_add = CHANGETYPE_ADD;
+char *changetype_delete = CHANGETYPE_DELETE;
+char *changetype_modify = CHANGETYPE_MODIFY;
+char *changetype_modrdn = CHANGETYPE_MODRDN;
+char *changetype_moddn = CHANGETYPE_MODDN;
+char *attr_changenumber = ATTR_CHANGENUMBER;
+char *attr_targetdn = ATTR_TARGETDN;
+char *attr_changetype = ATTR_CHANGETYPE;
+char *attr_newrdn = ATTR_NEWRDN;
+char *attr_deleteoldrdn = ATTR_DELETEOLDRDN;
+char *attr_changes = ATTR_CHANGES;
+char *attr_newsuperior = ATTR_NEWSUPERIOR;
+char *attr_changetime = ATTR_CHANGETIME;
+char *attr_dataversion = ATTR_DATAVERSION;
+char *attr_csn = ATTR_CSN;
+char *type_copyingFrom = TYPE_COPYINGFROM;
+char *type_copiedFrom = TYPE_COPIEDFROM;
+char *filter_copyingFrom = FILTER_COPYINGFROM;
+char *filter_copiedFrom = FILTER_COPIEDFROM;
+char *filter_objectclass = FILTER_OBJECTCLASS;
+char *type_cn = "cn";
+char *type_objectclass = "objectclass";
+
+/* Names for replica attributes */
+const char *attr_replicaId = "nsDS5ReplicaId";
+const char *attr_replicaRoot = "nsDS5ReplicaRoot";
+const char *attr_replicaType = "nsDS5ReplicaType";
+const char *attr_replicaBindDn = "nsDS5ReplicaBindDn";
+const char *attr_state = "nsState";
+const char *attr_flags = "nsds5Flags";
+const char *attr_replicaName = "nsds5ReplicaName";
+const char *attr_replicaReferral = "nsds5ReplicaReferral";
+const char *type_ruvElement = "nsds50ruv";
+const char *type_replicaPurgeDelay = "nsds5ReplicaPurgeDelay";
+const char *type_replicaChangeCount = "nsds5ReplicaChangeCount";
+const char *type_replicaTombstonePurgeInterval = "nsds5ReplicaTombstonePurgeInterval";
+const char *type_replicaLegacyConsumer = "nsds5ReplicaLegacyConsumer";
+const char *type_ruvElementUpdatetime = "nsruvReplicaLastModified";
+
+/* Attribute names for replication agreement attributes */
+const char *type_nsds5ReplicaHost = "nsds5ReplicaHost";
+const char *type_nsds5ReplicaPort = "nsds5ReplicaPort";
+const char *type_nsds5TransportInfo = "nsds5ReplicaTransportInfo";
+const char *type_nsds5ReplicaBindDN = "nsds5ReplicaBindDN";
+const char *type_nsds5ReplicaCredentials = "nsds5ReplicaCredentials";
+const char *type_nsds5ReplicaBindMethod = "nsds5ReplicaBindMethod";
+const char *type_nsds5ReplicaRoot = "nsds5ReplicaRoot";
+const char *type_nsds5ReplicatedAttributeList = "nsds5ReplicatedAttributeList";
+const char *type_nsds5ReplicaUpdateSchedule = "nsds5ReplicaUpdateSchedule";
+const char *type_nsds5ReplicaInitialize = "nsds5BeginReplicaRefresh";
+const char *type_nsds5ReplicaTimeout = "nsds5ReplicaTimeout";
+const char *type_nsds5ReplicaBusyWaitTime = "nsds5ReplicaBusyWaitTime";
+const char *type_nsds5ReplicaSessionPauseTime = "nsds5ReplicaSessionPauseTime";
+
+/* To Allow Consumer Initialisation when adding an agreement - */
+const char *type_nsds5BeginReplicaRefresh = "nsds5BeginReplicaRefresh";
+
+static int repl_active_threads;
+
+int
+decrement_repl_active_threads()
+{
+ PR_AtomicIncrement(&repl_active_threads);
+ return repl_active_threads;
+}
+
+int
+increment_repl_active_threads()
+{
+ PR_AtomicDecrement(&repl_active_threads);
+ return repl_active_threads;
+}
diff --git a/ldap/servers/plugins/replication/repl_helper.c b/ldap/servers/plugins/replication/repl_helper.c
new file mode 100644
index 00000000..05616cf1
--- /dev/null
+++ b/ldap/servers/plugins/replication/repl_helper.c
@@ -0,0 +1,85 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+#include "repl_helper.h"
+
+ReplGenericList *
+ReplGenericListNew(void)
+{
+ ReplGenericList *list=NULL;
+ if(NULL == (list = (ReplGenericList *)
+ slapi_ch_calloc(1,sizeof(ReplGenericList)))) {
+ return(NULL);
+ }
+ list->object = NULL;
+ list->next = NULL;
+ list->prev = NULL;
+ return(list);
+}
+
+void
+ReplGenericListAddObject(ReplGenericList *list,
+ void *newObject)
+{
+ if(list) {
+ ReplGenericList *new_struct = (ReplGenericList *)
+ slapi_ch_calloc(1, sizeof(ReplGenericList));
+
+ if (!new_struct)
+ return;
+ /* set back pointer of old first element */
+ if(list->next) {
+ list->next->prev = new_struct;
+ }
+
+ /* we might have a next but since we are the first we WONT have
+ a previous */
+ new_struct->object = newObject;
+ new_struct->next = list->next;
+ new_struct->prev = NULL;
+
+ /* the new element is the first one */
+ list->next = new_struct;
+
+ /* if this is the only element it is the end too */
+ if(NULL == list->prev)
+ list->prev = new_struct;
+
+ }
+ return;
+}
+
+ReplGenericList *
+ReplGenericListFindObject(ReplGenericList *list,
+ void *object)
+{
+ if(!list)
+ return(NULL);
+ list = list->next; /* the first list item never has data */
+
+ while (list) {
+ if(list->object == object)
+ return(list);
+ list = list->next;
+ }
+ return(NULL);
+}
+
+void
+ReplGenericListDestroy(ReplGenericList *list,
+ ReplGenericListObjectDestroyFn destroyFn)
+{
+ ReplGenericList *list_ptr;
+
+ while (list) {
+ list_ptr = list;
+ list = list->next;
+ if(destroyFn && list_ptr->object) {
+ (destroyFn)(list_ptr->object);
+ }
+ slapi_ch_free((void **)(&list_ptr));
+ }
+ return;
+}
diff --git a/ldap/servers/plugins/replication/repl_helper.h b/ldap/servers/plugins/replication/repl_helper.h
new file mode 100644
index 00000000..076710ce
--- /dev/null
+++ b/ldap/servers/plugins/replication/repl_helper.h
@@ -0,0 +1,69 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+/*
+ * repl_helper.h - Helper functions (should actually be repl_utils.h)
+ *
+ *
+ *
+ */
+
+#ifndef _REPL_HELPER_H
+#define _REPL_HELPER_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include "nspr.h"
+#include "slapi-plugin.h"
+
+/*
+ * shamelessly stolen from the xp library
+ *
+ */
+
+/*
+ Linked list manipulation routines
+
+ this is a very standard linked list structure
+ used by many many programmers all over the world
+
+ The lists have been modified to be doubly linked. The
+ first element in a list is always the header. The 'next'
+ pointer of the header is the first element in the list.
+ The 'prev' pointer of the header is the last element in
+ the list.
+
+ The 'prev' pointer of the first real element in the list
+ is NULL as is the 'next' pointer of the last real element
+ in the list
+
+ */
+
+
+typedef struct _repl_genericList {
+ void *object;
+ struct _repl_genericList *next;
+ struct _repl_genericList *prev;
+} ReplGenericList;
+
+typedef void *(ReplGenericListObjectDestroyFn)(void *obj);
+
+ReplGenericList *ReplGenericListNew(void);
+void ReplGenericListDestroy(ReplGenericList *list, ReplGenericListObjectDestroyFn destroyFn);
+
+void ReplGenericListAddObject(ReplGenericList *list,
+ void *newObject);
+ReplGenericList *ReplGenericListFindObject(ReplGenericList *list,
+ void *obj);
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
+
diff --git a/ldap/servers/plugins/replication/repl_init.c b/ldap/servers/plugins/replication/repl_init.c
new file mode 100644
index 00000000..42da2074
--- /dev/null
+++ b/ldap/servers/plugins/replication/repl_init.c
@@ -0,0 +1,312 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+
+/*
+ * Add an entry like the following to dse.ldif to enable this plugin:
+
+dn: cn=Legacy Replication Plugin,cn=plugins,cn=config
+objectclass: top
+objectclass: nsSlapdPlugin
+objectclass: extensibleObject
+cn: Legacy Replication Plugin
+nsslapd-pluginpath: /export2/servers/Hydra-supplier/lib/replication-plugin.so
+nsslapd-plugininitfunc: replication_legacy_plugin_init
+nsslapd-plugintype: object
+nsslapd-pluginenabled: on
+nsslapd-plugin-depends-on-type: database
+nsslapd-plugin-depends-on-named: Class of Service
+nsslapd-plugin-depends-on-named: Multi-Master Replication Plugin
+nsslapd-pluginid: replication-legacy
+nsslapd-pluginversion: 5.0b1
+nsslapd-pluginvendor: Netscape Communications
+nsslapd-plugindescription: Legacy Replication Plugin
+
+NOTE: This plugin depends on the Multi-Master Replication Plugin
+
+*/
+
+#include "slapi-plugin.h"
+#include "repl.h"
+#include "repl5.h"
+#include "repl_shared.h"
+#include "cl4.h" /* changelog interface */
+#include "dirver.h"
+#include <dirlite_strings.h> /* PLUGIN_MAGIC_VENDOR_STR */
+
+#ifdef _WIN32
+int *module_ldap_debug = 0;
+
+void plugin_init_debug_level(int *level_ptr)
+{
+ module_ldap_debug = level_ptr;
+}
+#endif
+
+/* ----------------------------- Legacy Replication Plugin */
+
+static Slapi_PluginDesc legacydesc = { "replication-legacy", PLUGIN_MAGIC_VENDOR_STR, PRODUCTTEXT, "Legacy Replication Plugin" };
+static Slapi_PluginDesc legacypreopdesc = { "replication-legacy-preop", PLUGIN_MAGIC_VENDOR_STR, PRODUCTTEXT, "Legacy replication pre-operation plugin" };
+static Slapi_PluginDesc legacypostopdesc = { "replication-legacy-postop", PLUGIN_MAGIC_VENDOR_STR, PRODUCTTEXT, "Legacy replication post-operation plugin" };
+static Slapi_PluginDesc legacyinternalpreopdesc = { "replication-legacy-internalpreop", PLUGIN_MAGIC_VENDOR_STR, PRODUCTTEXT, "Legacy replication internal pre-operation plugin" };
+static Slapi_PluginDesc legacyinternalpostopdesc = { "replication-legacy-internalpostop", PLUGIN_MAGIC_VENDOR_STR, PRODUCTTEXT, "Legacy replication internal post-operation plugin" };
+static Slapi_PluginDesc legacybepostopdesc = { "replication-legacy-bepostop", PLUGIN_MAGIC_VENDOR_STR, PRODUCTTEXT, "Legacy replication bepost-operation plugin" };
+static Slapi_PluginDesc legacyentrydesc = { "replication-legacy-entry", PLUGIN_MAGIC_VENDOR_STR, PRODUCTTEXT, "Legacy replication entry plugin" };
+
+static int legacy_stopped; /* A flag which is set when all the plugin threads are to stop */
+
+
+/* Initialize preoperation plugin points */
+int
+legacy_preop_init( Slapi_PBlock *pb )
+{
+ int rc= 0; /* OK */
+
+ if( slapi_pblock_set( pb, SLAPI_PLUGIN_VERSION, SLAPI_PLUGIN_VERSION_01 ) != 0 ||
+ slapi_pblock_set( pb, SLAPI_PLUGIN_DESCRIPTION, (void *)&legacypreopdesc ) != 0 ||
+ slapi_pblock_set( pb, SLAPI_PLUGIN_PRE_BIND_FN, (void *) legacy_preop_bind ) != 0 ||
+ slapi_pblock_set( pb, SLAPI_PLUGIN_PRE_ADD_FN, (void *) legacy_preop_add ) != 0 ||
+ slapi_pblock_set( pb, SLAPI_PLUGIN_PRE_DELETE_FN, (void *) legacy_preop_delete ) != 0 ||
+ slapi_pblock_set( pb, SLAPI_PLUGIN_PRE_MODIFY_FN, (void *) legacy_preop_modify ) != 0 ||
+ slapi_pblock_set( pb, SLAPI_PLUGIN_PRE_MODRDN_FN, (void *) legacy_preop_modrdn ) != 0 ||
+ slapi_pblock_set( pb, SLAPI_PLUGIN_PRE_SEARCH_FN, (void *) legacy_preop_search ) != 0 ||
+ slapi_pblock_set( pb, SLAPI_PLUGIN_PRE_COMPARE_FN, (void *) legacy_preop_compare ) != 0 ||
+ slapi_pblock_set( pb, SLAPI_PLUGIN_PRE_ENTRY_FN, (void *) legacy_pre_entry ))
+ {
+ slapi_log_error( SLAPI_LOG_PLUGIN, repl_plugin_name, "legacy_preop_init failed\n" );
+ rc= -1;
+ }
+ return rc;
+}
+
+
+
+/* Initialize postoperation plugin points */
+static int
+legacy_postop_init( Slapi_PBlock *pb )
+{
+ int rc= 0; /* OK */
+
+ if( slapi_pblock_set( pb, SLAPI_PLUGIN_VERSION, SLAPI_PLUGIN_VERSION_01 ) != 0 ||
+ slapi_pblock_set( pb, SLAPI_PLUGIN_DESCRIPTION, (void *)&legacypostopdesc ) != 0 ||
+ slapi_pblock_set( pb, SLAPI_PLUGIN_POST_ADD_FN, (void *) legacy_postop_add ) != 0 ||
+ slapi_pblock_set( pb, SLAPI_PLUGIN_POST_DELETE_FN, (void *) legacy_postop_delete ) != 0 ||
+ slapi_pblock_set( pb, SLAPI_PLUGIN_POST_MODIFY_FN, (void *) legacy_postop_modify ) != 0 ||
+ slapi_pblock_set( pb, SLAPI_PLUGIN_POST_MODRDN_FN, (void *) legacy_postop_modrdn ) != 0 )
+ {
+ slapi_log_error( SLAPI_LOG_PLUGIN, repl_plugin_name, "legacy_postop_init failed\n" );
+ rc= -1;
+ }
+
+ return rc;
+}
+
+
+
+/* Initialize internal preoperation plugin points (called for internal operations) */
+static int
+legacy_internalpreop_init( Slapi_PBlock *pb )
+{
+ int rc= 0; /* OK */
+
+ if( slapi_pblock_set( pb, SLAPI_PLUGIN_VERSION, SLAPI_PLUGIN_VERSION_01 ) != 0 ||
+ slapi_pblock_set( pb, SLAPI_PLUGIN_DESCRIPTION, (void *)&legacyinternalpreopdesc ) != 0 ||
+ slapi_pblock_set( pb, SLAPI_PLUGIN_INTERNAL_PRE_ADD_FN, (void *) legacy_preop_add ) != 0 ||
+ slapi_pblock_set( pb, SLAPI_PLUGIN_INTERNAL_PRE_DELETE_FN, (void *) legacy_preop_delete ) != 0 ||
+ slapi_pblock_set( pb, SLAPI_PLUGIN_INTERNAL_PRE_MODIFY_FN, (void *) legacy_preop_modify ) != 0 ||
+ slapi_pblock_set( pb, SLAPI_PLUGIN_INTERNAL_PRE_MODRDN_FN, (void *) legacy_preop_modrdn ) != 0 )
+ {
+ slapi_log_error( SLAPI_LOG_PLUGIN, repl_plugin_name, "legacy_internalpreop_init failed\n" );
+ rc= -1;
+ }
+ return rc;
+}
+
+
+
+/* Initialize internal postoperation plugin points (called for internal operations) */
+static int
+legacy_internalpostop_init( Slapi_PBlock *pb )
+{
+ int rc= 0; /* OK */
+
+ if( slapi_pblock_set( pb, SLAPI_PLUGIN_VERSION, SLAPI_PLUGIN_VERSION_01 ) != 0 ||
+ slapi_pblock_set( pb, SLAPI_PLUGIN_DESCRIPTION, (void *)&legacyinternalpostopdesc ) != 0 ||
+ slapi_pblock_set( pb, SLAPI_PLUGIN_INTERNAL_POST_ADD_FN, (void *) legacy_postop_add ) != 0 ||
+ slapi_pblock_set( pb, SLAPI_PLUGIN_INTERNAL_POST_DELETE_FN, (void *) legacy_postop_delete ) != 0 ||
+ slapi_pblock_set( pb, SLAPI_PLUGIN_INTERNAL_POST_MODIFY_FN, (void *) legacy_postop_modify ) != 0 ||
+ slapi_pblock_set( pb, SLAPI_PLUGIN_INTERNAL_POST_MODRDN_FN, (void *) legacy_postop_modrdn ) != 0 )
+ {
+ slapi_log_error( SLAPI_LOG_PLUGIN, repl_plugin_name, "legacy_internalpostop_init failed\n" );
+ rc= -1;
+ }
+
+ return rc;
+}
+
+
+
+/* Initialize the entry plugin point for the legacy replication plugin */
+static int
+legacy_entry_init( Slapi_PBlock *pb )
+{
+ int rc= 0; /* OK */
+
+ /* Set up the fn pointers for the preop and postop operations we're interested in */
+ if( slapi_pblock_set( pb, SLAPI_PLUGIN_VERSION, SLAPI_PLUGIN_VERSION_01 ) != 0 ||
+ slapi_pblock_set( pb, SLAPI_PLUGIN_DESCRIPTION, (void *)&legacyentrydesc ) != 0 )
+ {
+ slapi_log_error( SLAPI_LOG_PLUGIN, repl_plugin_name, "legacy_entry_init failed\n" );
+ rc= -1;
+ }
+ return rc;
+}
+
+
+
+
+/*
+ * Create the entry at the top of the replication configuration subtree.
+ */
+static int
+create_config_top()
+{
+ const char *dn = REPL_CONFIG_TOP;
+ char *entry_string = slapi_ch_strdup("dn: cn=replication,cn=config\nobjectclass: top\nobjectclass: extensibleobject\ncn: replication\n");
+ Slapi_PBlock *pb = slapi_pblock_new();
+ Slapi_Entry *e = slapi_str2entry(entry_string, 0);
+ int return_value;
+
+ slapi_add_entry_internal_set_pb(pb, e, NULL, /* controls */
+ repl_get_plugin_identity(PLUGIN_MULTIMASTER_REPLICATION), 0 /* flags */);
+ slapi_add_internal_pb(pb);
+ slapi_pblock_get(pb, SLAPI_PLUGIN_INTOP_RESULT, &return_value);
+ slapi_pblock_destroy(pb);
+ slapi_ch_free((void **)&entry_string);
+ return return_value;
+}
+
+
+/* Start the legacy replication plugin */
+static int
+legacy_start( Slapi_PBlock *pb )
+{
+ static int legacy_started = 0;
+ int rc= 0; /* OK */
+
+ if (!legacy_started)
+ {
+ int ctrc;
+
+ /* Initialise support for cn=monitor */
+ repl_monitor_init();
+
+ /* Initialise support for "" (the rootdse) */
+ /* repl_rootdse_init(); */
+
+ /* Decode the command line args to see if we're dumping to LDIF */
+ {
+ int argc;
+ char **argv;
+ slapi_pblock_get( pb, SLAPI_ARGC, &argc);
+ slapi_pblock_get( pb, SLAPI_ARGV, &argv);
+ repl_entry_init(argc,argv);
+ }
+
+ /* Create the entry at the top of the config area, if it doesn't exist */
+ /* XXXggood this should be in the 5.0 plugin! */
+ ctrc = create_config_top();
+ if (ctrc != LDAP_SUCCESS && ctrc != LDAP_ALREADY_EXISTS)
+ {
+ slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name, "Warning: unable to "
+ "create configuration entry %s: %s\n", REPL_CONFIG_TOP,
+ ldap_err2string(ctrc));
+ }
+ (void)legacy_consumer_config_init();
+
+ /* register to be notified when backend state changes */
+ slapi_register_backend_state_change((void *)legacy_consumer_be_state_change,
+ legacy_consumer_be_state_change);
+
+ legacy_started = 1;
+ legacy_stopped = 0;
+ }
+ return rc;
+}
+
+
+/* Post-start function for the legacy replication plugin */
+static int
+legacy_poststart( Slapi_PBlock *pb )
+{
+ int rc = 0; /* OK */
+ return rc;
+}
+
+
+/* Stop the legacy replication plugin */
+static int
+legacy_stop( Slapi_PBlock *pb )
+{
+ int rc= 0; /* OK */
+
+ if (!legacy_stopped)
+ {
+ /*csnShutdown();*/
+ legacy_stopped = 1;
+ }
+
+ /* unregister backend state change notification */
+ slapi_unregister_backend_state_change((void *)legacy_consumer_be_state_change);
+
+ return rc;
+}
+
+
+/* Initialize the legacy replication plugin */
+int
+replication_legacy_plugin_init(Slapi_PBlock *pb)
+{
+ static int legacy_initialised= 0;
+ int rc= 0; /* OK */
+ void *identity = NULL;
+
+ slapi_pblock_get (pb, SLAPI_PLUGIN_IDENTITY, &identity);
+ PR_ASSERT (identity);
+ repl_set_plugin_identity (PLUGIN_LEGACY_REPLICATION, identity);
+
+ if(config_is_slapd_lite())
+ {
+ slapi_log_error( SLAPI_LOG_FATAL, repl_plugin_name,
+ "replication plugin not approved for restricted"
+ " mode Directory Server.\n" );
+ rc= -1;
+ }
+ if(rc==0 && !legacy_initialised)
+ {
+ rc= slapi_pblock_set( pb, SLAPI_PLUGIN_VERSION, SLAPI_PLUGIN_VERSION_01 );
+ rc= slapi_pblock_set( pb, SLAPI_PLUGIN_DESCRIPTION, (void *)&legacydesc );
+ rc= slapi_pblock_set( pb, SLAPI_PLUGIN_START_FN, (void *) legacy_start );
+ rc= slapi_pblock_set( pb, SLAPI_PLUGIN_CLOSE_FN, (void *) legacy_stop );
+ rc= slapi_pblock_set( pb, SLAPI_PLUGIN_POSTSTART_FN, (void *) legacy_poststart );
+
+ /* Register the plugin interfaces we implement */
+ rc= slapi_register_plugin("preoperation", 1 /* Enabled */, "legacy_preop_init", legacy_preop_init, "Legacy replication preoperation plugin", NULL, identity);
+ rc= slapi_register_plugin("postoperation", 1 /* Enabled */, "legacy_postop_init", legacy_postop_init, "Legacy replication postoperation plugin", NULL, identity);
+ rc= slapi_register_plugin("internalpreoperation", 1 /* Enabled */, "legacy_internalpreop_init", legacy_internalpreop_init, "Legacy replication internal preoperation plugin", NULL, identity);
+ rc= slapi_register_plugin("internalpostoperation", 1 /* Enabled */, "legacy_internalpostop_init", legacy_internalpostop_init, "Legacy replication internal postoperation plugin", NULL, identity);
+ rc= slapi_register_plugin("entry", 1 /* Enabled */, "legacy_entry_init", legacy_entry_init, "Legacy replication entry plugin", NULL, identity);
+
+ legacy_initialised= 1;
+ }
+ return rc;
+}
+
+
+int
+get_legacy_stop()
+{
+ return legacy_stopped;
+}
diff --git a/ldap/servers/plugins/replication/repl_modify.c b/ldap/servers/plugins/replication/repl_modify.c
new file mode 100644
index 00000000..26753cc0
--- /dev/null
+++ b/ldap/servers/plugins/replication/repl_modify.c
@@ -0,0 +1,29 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+
+#include "slapi-plugin.h"
+#include "repl.h"
+
+/* The modify operation plugin functions for the legacy replication plugin */
+
+int
+legacy_preop_modify( Slapi_PBlock *pb )
+{
+ return legacy_preop( pb, "legacy_preop_modify", OP_MODIFY );
+}
+
+int
+legacy_bepreop_modify( Slapi_PBlock *pb )
+{
+ int rc= 0; /* OK */
+ return rc;
+}
+
+int
+legacy_postop_modify( Slapi_PBlock *pb )
+{
+ return legacy_postop( pb, "legacy_postop_modify", OP_MODIFY );
+}
diff --git a/ldap/servers/plugins/replication/repl_modrdn.c b/ldap/servers/plugins/replication/repl_modrdn.c
new file mode 100644
index 00000000..653aff0a
--- /dev/null
+++ b/ldap/servers/plugins/replication/repl_modrdn.c
@@ -0,0 +1,28 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+
+#include "slapi-plugin.h"
+#include "repl.h"
+
+/* The modrdn plugin points for the legacy replication plugin */
+
+int
+legacy_preop_modrdn( Slapi_PBlock *pb )
+{
+ return legacy_preop(pb, "legacy_preop_modrdn", OP_MODDN);
+}
+
+int
+legacy_bepreop_modrdn( Slapi_PBlock *pb )
+{
+ return 0; /* OK */
+}
+
+int
+legacy_postop_modrdn( Slapi_PBlock *pb )
+{
+ return legacy_postop(pb, "legacy_postop_modrdn", OP_MODDN);
+}
diff --git a/ldap/servers/plugins/replication/repl_monitor.c b/ldap/servers/plugins/replication/repl_monitor.c
new file mode 100644
index 00000000..ec52d611
--- /dev/null
+++ b/ldap/servers/plugins/replication/repl_monitor.c
@@ -0,0 +1,58 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+
+#include <string.h>
+
+#include "repl.h"
+#include "slapi-plugin.h"
+
+/* Forward Declartions */
+static int repl_monitor_search (Slapi_PBlock *pb, Slapi_Entry* e, Slapi_Entry* entryAfter, int *returncode, char *returntext, void *arg);
+
+int
+repl_monitor_init()
+{
+ /* The FE DSE *must* be initialised before we get here */
+ int return_value= LDAP_SUCCESS;
+ static int initialized = 0;
+
+ if (!initialized)
+ {
+ /* ONREPL - this is commented until we implement 4.0 style changelog
+ slapi_config_register_callback(SLAPI_OPERATION_SEARCH,DSE_FLAG_PREOP,"cn=monitor",LDAP_SCOPE_BASE,"(objectclass=*)",repl_monitor_search,NULL); */
+ initialized = 1;
+ }
+
+ return return_value;
+}
+
+static int
+repl_monitor_search(Slapi_PBlock *pb, Slapi_Entry* e, Slapi_Entry* entryAfter, int *returncode, char *returntext, void *arg)
+{
+ const char *sdv = get_server_dataversion();
+ if ( sdv != NULL )
+ {
+ int port;
+ char buf[BUFSIZ];
+ struct berval val;
+ struct berval *vals[2];
+ vals[0] = &val;
+ vals[1] = NULL;
+ port= config_get_port();
+ if(port==0)
+ {
+ port= config_get_secureport();
+ }
+ /* ONREPL - how do we publish changenumbers now with multiple changelogs?
+ sprintf( buf, "%s:%lu %s% lu", get_localhost_DNS(), port, sdv, ldapi_get_last_changenumber());
+ */
+ val.bv_val = buf;
+ val.bv_len = strlen( buf );
+ slapi_entry_attr_replace( e, attr_dataversion, vals );
+ }
+ return SLAPI_DSE_CALLBACK_OK;
+}
+
diff --git a/ldap/servers/plugins/replication/repl_objset.c b/ldap/servers/plugins/replication/repl_objset.c
new file mode 100644
index 00000000..f0a68097
--- /dev/null
+++ b/ldap/servers/plugins/replication/repl_objset.c
@@ -0,0 +1,524 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+
+/* repl_objset.c */
+/*
+ * Support for lifetime management of sets of objects.
+ * Objects are refcounted. NOTE: this API is deprecated.
+ * Use the object/objset API provided by libslapd.
+ */
+
+#include "slapi-plugin.h"
+#include "slapi-private.h"
+#include "repl_objset.h"
+#include <prlock.h>
+
+#define REPL_OBJSET_OBJ_FLAG_DELETED 0x1
+
+
+typedef struct repl_objset_object
+{
+ void *data; /* pointer to actual node data */
+ char *key; /* key for this object. null-terminated string */
+ int refcnt; /* reference count for this object */
+ unsigned long flags; /* state of this object */
+} Repl_Objset_object;
+
+typedef struct repl_objset
+{
+ LList *objects;
+ FNFree destructor; /* destructor for objects - provided by caller */
+ PRLock *lock;
+} repl_objset;
+
+
+/* Forward declarations */
+static void removeObjectNolock(Repl_Objset *o, Repl_Objset_object *co);
+static Repl_Objset_object *removeCurrentObjectAndGetNextNolock (Repl_Objset *o,
+ Repl_Objset_object *co, void *iterator);
+
+/*
+ * Create a new set.
+ *
+ * Arguments:
+ * destructor: a function to be called when an object is to be destroyed
+ *
+ * Returns:
+ * A pointer to the object set, or NULL if an error occured.
+ */
+Repl_Objset *
+repl_objset_new(FNFree destructor)
+{
+ Repl_Objset *p;
+
+ p = (Repl_Objset *)slapi_ch_malloc(sizeof(Repl_Objset));
+ p->lock = PR_NewLock();
+ if (NULL == p->lock)
+ {
+ free(p); p = NULL;
+ }
+ p->objects = llistNew();
+ p->destructor = destructor;
+ return p;
+}
+
+
+/*
+ * Destroy a Repl_Objset.
+ * Arguments:
+ * o: the object set to be destroyed
+ * maxwait: the maximum time to wait for all object refcnts to
+ * go to zero.
+ * panic_fn: a function to be called if, after waiting "maxwait"
+ * seconds, not all object refcnts are zero.
+ * The caller must ensure that no one else holds references to the
+ * set or any objects it contains.
+ */
+void
+repl_objset_destroy(Repl_Objset **o, time_t maxwait, FNFree panic_fn)
+{
+ Repl_Objset_object *co = NULL;
+ time_t now, stop_time;
+ int really_gone;
+ int loopcount;
+ void *cookie;
+
+ PR_ASSERT(NULL != o);
+ PR_ASSERT(NULL != *o);
+
+ time(&now);
+ stop_time = now + maxwait;
+
+ /*
+ * Loop over the objects until they all are actually gone,
+ * or until maxwait seconds have passed.
+ */
+ really_gone = 0;
+ loopcount = 0;
+
+ while (now < stop_time)
+ {
+ void *cookie;
+
+ PR_Lock((*o)->lock);
+
+ if ((co = llistGetFirst((*o)->objects, &cookie)) == NULL)
+ {
+ really_gone = 1;
+ PR_Unlock((*o)->lock);
+ break;
+ }
+ while (NULL != co)
+ {
+ /* Set the deleted flag so the object isn't returned by iterator */
+ co->flags |= REPL_OBJSET_OBJ_FLAG_DELETED;
+ if (0 == co->refcnt)
+ {
+ /* Remove the object */
+ co = removeCurrentObjectAndGetNextNolock ((*o), co, cookie);
+
+ }
+ else
+ co = llistGetNext((*o)->objects, &cookie);
+ }
+ PR_Unlock((*o)->lock);
+ time(&now);
+ if (loopcount > 0)
+ {
+ DS_Sleep(PR_TicksPerSecond());
+ }
+ loopcount++;
+ }
+
+ if (!really_gone)
+ {
+ if (NULL != panic_fn)
+ {
+ /*
+ * Call the "aargh, this thing won't go away" panic
+ * function for each remaining object.
+ */
+ PR_Lock((*o)->lock);
+ if ((co = llistGetFirst((*o)->objects, &cookie)) == NULL)
+ {
+ panic_fn(co->data);
+ while (NULL != co)
+ {
+ panic_fn(co->data);
+ co = llistGetNext((*o)->objects, &cookie);
+ }
+ }
+ PR_Unlock((*o)->lock);
+ }
+ }
+ else
+ {
+ /* Free the linked list */
+ llistDestroy(&(*o)->objects, (*o)->destructor);
+ PR_DestroyLock((*o)->lock);
+ free(*o); *o = NULL;
+ }
+}
+
+
+
+/*
+ * Add an object to an object set.
+ *
+ * Arguments:
+ * o: The object set to which the object is to be added.
+ * name: a null-terminated string that names the object. Must
+ * be unique.
+ * obj: pointer to the object to be added.
+ *
+ * Return codes:
+ * REPL_OBJSET_SUCCESS: the item was added to the object set
+ * REPL_OBJSET_DUPLICATE_KEY: an item with the same key is already
+ * in the object set.
+ * REPL_OBJSET_INTERNAL_ERROR: something bad happened.
+ */
+int
+repl_objset_add(Repl_Objset *o, const char *name, void *obj)
+{
+ Repl_Objset_object *co = NULL;
+ Repl_Objset_object *tmp = NULL;
+ int rc = REPL_OBJSET_SUCCESS;
+
+ PR_ASSERT(NULL != o);
+ PR_ASSERT(NULL != name);
+ PR_ASSERT(NULL != obj);
+
+ PR_Lock(o->lock);
+ tmp = llistGet(o->objects, name);
+ if (NULL != tmp)
+ {
+ rc = REPL_OBJSET_DUPLICATE_KEY;
+ goto loser;
+ }
+ co = (Repl_Objset_object *)slapi_ch_malloc(sizeof(Repl_Objset_object));
+ co->data = obj;
+ co->key = slapi_ch_strdup(name);
+ co->refcnt = 0;
+ co->flags = 0UL;
+ if (llistInsertHead(o->objects, name, co) != 0)
+ {
+ rc = REPL_OBJSET_INTERNAL_ERROR;
+ goto loser;
+ }
+ PR_Unlock(o->lock);
+ return rc;
+
+loser:
+ PR_Unlock(o->lock);
+ if (NULL != co)
+ {
+ if (NULL != co->key)
+ {
+ slapi_ch_free((void **)&co->key);
+ }
+ slapi_ch_free((void **)&co);
+ }
+ return rc;
+}
+
+
+/* Must be called with the repl_objset locked */
+static void
+removeObjectNolock(Repl_Objset *o, Repl_Objset_object *co)
+{
+ /* Remove from list */
+ llistRemove(o->objects, co->key);
+ /* Destroy the object */
+ o->destructor(&(co->data));
+ free(co->key);
+ /* Deallocate the container */
+ free(co);
+}
+
+static Repl_Objset_object *
+removeCurrentObjectAndGetNextNolock (Repl_Objset *o, Repl_Objset_object *co, void *iterator)
+{
+ Repl_Objset_object *ro;
+
+ PR_ASSERT (o);
+ PR_ASSERT (co);
+ PR_ASSERT (iterator);
+
+ ro = llistRemoveCurrentAndGetNext (o->objects, &iterator);
+
+ o->destructor(&(co->data));
+ free(co->key);
+ /* Deallocate the container */
+ free(co);
+
+ return ro;
+}
+
+/* Must be called with the repl_objset locked */
+static void
+acquireNoLock(Repl_Objset_object *co)
+{
+ co->refcnt++;
+}
+
+
+/* Must be called with the repl_objset locked */
+static void
+releaseNoLock(Repl_Objset *o, Repl_Objset_object *co)
+{
+ PR_ASSERT(co->refcnt >= 1);
+ if (--co->refcnt == 0)
+ {
+ if (co->flags & REPL_OBJSET_OBJ_FLAG_DELETED)
+ {
+ /* Remove the object */
+ removeObjectNolock(o, co);
+ }
+ }
+}
+
+/*
+ * Retrieve an object from the object set. If an object with
+ * the given key is found, its reference count is incremented,
+ * a pointer to the object is returned, and a handle to use
+ * to refer to the object is returned.
+ *
+ * Arguments:
+ * o: The object set to be searched.
+ * key: key of the object to be retrieved
+ * obj: pointer to void * that will be set to point to the
+ * object, if found.
+ * handle: pointer to void * that will be set to point to a
+ * handle, used to refer to the object, if found.
+ *
+ * Returns:
+ * REPL_OBJSET_SUCCESS: an item was found.
+ * REPL_OBJSET_KEY_NOT_FOUND: no item with the given key was found.
+ */
+int
+repl_objset_acquire(Repl_Objset *o, const char *key, void **obj, void **handle)
+{
+ Repl_Objset_object *co = NULL;
+ int rc = REPL_OBJSET_KEY_NOT_FOUND;
+
+ PR_ASSERT(NULL != o);
+ PR_ASSERT(NULL != key);
+ PR_ASSERT(NULL != obj);
+ PR_ASSERT(NULL != handle);
+
+ PR_Lock(o->lock);
+ co = llistGet(o->objects, key);
+ if (NULL != co && !(co->flags & REPL_OBJSET_OBJ_FLAG_DELETED))
+ {
+ acquireNoLock(co);
+ *obj = (void *)co->data;
+ *handle = (void *)co;
+ rc = REPL_OBJSET_SUCCESS;
+ }
+ PR_Unlock(o->lock);
+ return rc;
+}
+
+
+/*
+ * Return an object to the object set.
+ *
+ * Arguments:
+ * o: The object set containing the objct
+ * handle: reference to the object.
+ *
+ */
+void
+repl_objset_release(Repl_Objset *o, void *handle)
+{
+ Repl_Objset_object *co;
+
+ PR_ASSERT(NULL != o);
+ PR_ASSERT(NULL != handle);
+
+ co = (Repl_Objset_object *)handle;
+ PR_Lock(o->lock);
+ releaseNoLock(o, co);
+ PR_Unlock(o->lock);
+}
+
+
+
+/*
+ * Delete an object from the object set
+ *
+ * Arguments:
+ * o: The object set containing the object.
+ * handle: reference to the object.
+ */
+void
+repl_objset_delete(Repl_Objset *o, void *handle)
+{
+ Repl_Objset_object *co = (Repl_Objset_object *)handle;
+
+ PR_ASSERT(NULL != o);
+ PR_ASSERT(NULL != co);
+
+ PR_Lock(o->lock);
+ if (co->refcnt == 0)
+ {
+ removeObjectNolock(o, co);
+ }
+ else
+ {
+ /* Set deleted flag, clean up later */
+ co->flags |= REPL_OBJSET_OBJ_FLAG_DELETED;
+ }
+ PR_Unlock(o->lock);
+}
+
+
+typedef struct _iterator
+{
+ Repl_Objset *o; /* set for which iterator was created */
+ void *cookie; /* for linked list */
+ Repl_Objset_object *co; /* our wrapper */
+} iterator;
+
+/*
+ * Get the first object in an object set.
+ * Used when enumerating all of the objects in a set.
+ * Arguments:
+ * o: The object set being enumerated
+ * itcontext: an iteration context, to be passed back to subsequent calls
+ * to repl_objset_next_object.
+ * handle: a pointer to pointer to void. This will be filled in with
+ * a reference to the object's enclosing object.
+ * Returns:
+ * A pointer to the next object in the set, or NULL if there are no
+ * objects in the set.
+ *
+ */
+void *
+repl_objset_first_object(Repl_Objset *o, void **itcontext, void **handle)
+{
+ Repl_Objset_object *co = NULL;
+ void *cookie;
+ void *retptr = NULL;
+ iterator *it;
+
+ PR_ASSERT(NULL != o);
+ PR_ASSERT(NULL != itcontext);
+
+ *itcontext = NULL;
+
+ if (NULL == o->objects) {
+ return(NULL);
+ }
+
+ /* Find the first non-deleted object */
+ PR_Lock(o->lock);
+ co = llistGetFirst(o->objects, &cookie);
+ while (NULL != co && (co->flags & REPL_OBJSET_OBJ_FLAG_DELETED))
+ {
+ co = llistGetNext(o->objects, &cookie);
+ }
+
+ if (NULL != co)
+ {
+ /* Increment refcnt until item given back to us */
+ acquireNoLock(co);
+
+ /* Save away context */
+ it = (iterator *)slapi_ch_malloc(sizeof(iterator));
+ *itcontext = it;
+ it->o = o;
+ it->cookie = cookie;
+ it->co = co;
+ retptr = co->data;
+ }
+
+ PR_Unlock(o->lock);
+ if (NULL != handle)
+ {
+ *handle = co;
+ }
+
+ return retptr;
+}
+
+
+
+/*
+ * Get the next object in the set.
+ * Arguments:
+ * o: The object set being enumerated
+ * itcontext: an iteration context, to be passed back to subsequent calls
+ * to repl_objset_next_object.
+ * handle: a pointer to pointer to void. This will be filled in with
+ * a reference to the object's enclosing object.
+ *
+ * Returns:
+ * A pointer to the next object in the set, or NULL if there are no more
+ * objects.
+ */
+void *
+repl_objset_next_object(Repl_Objset *o, void *itcontext, void **handle)
+{
+ Repl_Objset_object *co = NULL;
+ Repl_Objset_object *tmp_co;
+ void *retptr = NULL;
+ iterator *it = (iterator *)itcontext;
+
+ PR_ASSERT(NULL != o);
+ PR_ASSERT(NULL != it);
+ PR_ASSERT(NULL != it->co);
+
+ PR_Lock(o->lock);
+ tmp_co = it->co;
+
+ /* Find the next non-deleted object */
+ while ((co = llistGetNext(o->objects, &it->cookie)) != NULL &&
+ !(co->flags & REPL_OBJSET_OBJ_FLAG_DELETED));
+
+ if (NULL != co)
+ {
+ acquireNoLock(co);
+ it->co = co;
+ retptr = co->data;
+ }
+ else
+ {
+ /*
+ * No more non-deleted objects - erase context (freeing
+ * it is responsibility of caller.
+ */
+ it->cookie = NULL;
+ it->co = NULL;
+ }
+ releaseNoLock(o, tmp_co);
+ PR_Unlock(o->lock);
+ if (NULL != handle)
+ {
+ *handle = co;
+ }
+ return retptr;
+}
+
+
+
+/*
+ * Destroy an itcontext iterator
+ */
+void
+repl_objset_iterator_destroy(void **itcontext)
+{
+ if (NULL != itcontext && NULL != *itcontext)
+ {
+ /* check if we did not iterate through the entire list
+ and need to release last accessed element */
+ iterator *it = *(iterator**)itcontext;
+ if (it->co)
+ repl_objset_release (it->o, it->co);
+
+ slapi_ch_free((void **)itcontext);
+ }
+}
diff --git a/ldap/servers/plugins/replication/repl_objset.h b/ldap/servers/plugins/replication/repl_objset.h
new file mode 100644
index 00000000..72af5109
--- /dev/null
+++ b/ldap/servers/plugins/replication/repl_objset.h
@@ -0,0 +1,37 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+/*
+ */
+
+/* repl_objset.h */
+ /*
+ * Support for lifetime management of sets of objects.
+ * Objects are refcounted. NOTE: This API should go away
+ * in favor of the objset API provided by libslapd.
+ */
+#ifndef _REPL_OBJSET_H
+#define __REPL_OBJSET_H
+
+#include "llist.h"
+
+#define REPL_OBJSET_SUCCESS 0
+#define REPL_OBJSET_DUPLICATE_KEY 1
+#define REPL_OBJSET_INTERNAL_ERROR 2
+#define REPL_OBJSET_KEY_NOT_FOUND 3
+
+typedef struct repl_objset Repl_Objset;
+
+Repl_Objset *repl_objset_new(FNFree destructor);
+void repl_objset_destroy(Repl_Objset **o, time_t maxwait, FNFree panic_fn);
+int repl_objset_add(Repl_Objset *o, const char *name, void *obj);
+int repl_objset_acquire(Repl_Objset *o, const char *key, void **obj, void **handle);
+void repl_objset_release(Repl_Objset *o, void *handle);
+void repl_objset_delete(Repl_Objset *o, void *handle);
+void *repl_objset_next_object(Repl_Objset *o, void *cookie, void **handle);
+void *repl_objset_first_object(Repl_Objset *o, void **cookie, void **handle);
+void repl_objset_iterator_destroy(void **itcontext);
+
+#endif /* _REPL_OBJSET_H */
diff --git a/ldap/servers/plugins/replication/repl_opext.c b/ldap/servers/plugins/replication/repl_opext.c
new file mode 100644
index 00000000..d9c8d1ed
--- /dev/null
+++ b/ldap/servers/plugins/replication/repl_opext.c
@@ -0,0 +1,97 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+/* supplier_operation_extension.c - replication extension to the Operation object
+ */
+
+
+#include "repl.h"
+#include "repl5.h"
+
+/* ***** Supplier side ***** */
+
+/* JCMREPL -> PINAKIxxx The interface to the referral stuff is not correct */
+void ref_array_dup_free(void *the_copy); /* JCMREPL - should be #included */
+
+/* supplier operation extension constructor */
+void* supplier_operation_extension_constructor (void *object, void *parent)
+{
+ supplier_operation_extension *ext = (supplier_operation_extension*) slapi_ch_calloc (1, sizeof (supplier_operation_extension));
+ if (ext == NULL)
+ {
+ slapi_log_error( SLAPI_LOG_PLUGIN, repl_plugin_name, "unable to create replication supplier operation extension - out of memory\n" );
+ }
+ else
+ {
+ ext->prevent_recursive_call= 0;
+ }
+ return ext;
+}
+
+/* supplier operation extension destructor */
+void supplier_operation_extension_destructor (void *ext,void *object, void *parent)
+{
+ if (ext)
+ {
+ supplier_operation_extension *supext = (supplier_operation_extension *)ext;
+ if (supext->operation_parameters)
+ operation_parameters_free (&(supext->operation_parameters));
+ if (supext->repl_gen)
+ slapi_ch_free ((void**)&supext->repl_gen);
+ slapi_ch_free((void **)&ext);
+ }
+}
+
+/* ***** Consumer side ***** */
+
+/* consumer operation extension constructor */
+void* consumer_operation_extension_constructor (void *object, void *parent)
+{
+ consumer_operation_extension *ext = (consumer_operation_extension*) slapi_ch_calloc (1, sizeof (consumer_operation_extension));
+ if (ext == NULL)
+ {
+ slapi_log_error( SLAPI_LOG_PLUGIN, repl_plugin_name, "unable to create replication consumer operation extension - out of memory\n" );
+ }
+ if(object!=NULL && parent!=NULL)
+ {
+ consumer_connection_extension *connext;
+ connext = (consumer_connection_extension *)repl_con_get_ext(REPL_CON_EXT_CONN, parent);
+ if(NULL != connext)
+ {
+ /* We copy the Connection Replicated Session flag to the Replicated Operation flag */
+ if (connext->isreplicationsession)
+ {
+ operation_set_flag((Slapi_Operation *)object,OP_FLAG_REPLICATED);
+ }
+ /* We set the Replication DN flag if session bound as replication dn */
+ if (connext->is_legacy_replication_dn)
+ {
+ operation_set_flag((Slapi_Operation *)object, OP_FLAG_LEGACY_REPLICATION_DN);
+ }
+ }
+ }
+ else
+ {
+ /* (parent==NULL) for internal operations */
+ PR_ASSERT(object!=NULL);
+ }
+
+ return ext;
+}
+
+/* consumer operation extension destructor */
+void consumer_operation_extension_destructor (void *ext,void *object, void *parent)
+{
+ if (NULL != ext)
+ {
+ consumer_operation_extension *opext = (consumer_operation_extension *)ext;
+ if (NULL != opext->search_referrals)
+ {
+ ref_array_dup_free(opext->search_referrals); /* JCMREPL - undefined */
+ opext->search_referrals = NULL;
+ }
+ slapi_ch_free((void **)&ext);
+ }
+}
diff --git a/ldap/servers/plugins/replication/repl_ops.c b/ldap/servers/plugins/replication/repl_ops.c
new file mode 100644
index 00000000..e1e51355
--- /dev/null
+++ b/ldap/servers/plugins/replication/repl_ops.c
@@ -0,0 +1,180 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+
+#include "slapi-plugin.h"
+#include "repl.h"
+#include "repl5.h"
+
+int
+legacy_postop( Slapi_PBlock *pb, const char *caller, int operation_type)
+{
+ int rc = 0;
+ Object *r_obj;
+ Replica *r;
+
+ r_obj = replica_get_replica_for_op (pb);
+ if (r_obj == NULL) /* there is no replica configured for this operations */
+ return 0;
+ else
+ {
+ /* check if this replica is 4.0 consumer */
+ r = (Replica*)object_get_data (r_obj);
+ PR_ASSERT (r);
+
+ /* this replica is not a 4.0 consumer - so we don't need to do any processing */
+ if (!replica_is_legacy_consumer (r))
+ {
+ object_release (r_obj);
+ return 0;
+ }
+
+ object_release (r_obj);
+ }
+
+ slapi_pblock_get(pb, SLAPI_PLUGIN_OPRETURN, &rc);
+ if (0 == rc)
+ {
+ if (OP_ADD == operation_type || OP_MODIFY == operation_type)
+ {
+ void *op;
+ consumer_operation_extension *opext = NULL;
+
+ /* Optimise out traversal of mods/entry if no cop{ied|ying}From present */
+ slapi_pblock_get(pb, SLAPI_OPERATION, &op);
+ opext = (consumer_operation_extension*) repl_con_get_ext (REPL_CON_EXT_OP, op);
+ if (NULL != opext && opext->has_cf)
+ {
+ process_legacy_cf( pb );
+ }
+ }
+ }
+
+ return 0;
+}
+
+
+
+static char *not_replicationdn_errmsg =
+ "An operation was submitted that contained copiedFrom or "
+ "copyingFrom attributes, but the connection was not bound "
+ "as the replicationdn.";
+
+int
+legacy_preop(Slapi_PBlock *pb, const char *caller, int operation_type)
+{
+ int rc = 0;
+ Slapi_Operation *operation = NULL;
+ consumer_operation_extension *opext = NULL;
+ int has_cf = 0;
+ Object *r_obj;
+ Replica *r;
+ int is_legacy_op = 0;
+
+ slapi_pblock_get( pb, SLAPI_OPERATION, &operation );
+ is_legacy_op = operation_is_flag_set(operation,OP_FLAG_LEGACY_REPLICATION_DN);
+ r_obj = replica_get_replica_for_op (pb);
+
+ if (r_obj == NULL) { /* there is no replica configured for this operations */
+ if (is_legacy_op){
+ /* This is a legacy replication operation but there are NO replica defined
+ Just refuse it */
+ slapi_send_ldap_result(pb, LDAP_UNWILLING_TO_PERFORM, NULL,
+ "Replication operation refused because the consumer is not defined as a replica", 0, NULL);
+ slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name,
+ "Incoming replication operation was refused because "
+ "there's no replica defined for this operation\n");
+ return -1;
+ }
+ else {
+ return 0;
+ }
+ }
+ else
+ {
+ /* check if this replica is 4.0 consumer */
+ r = (Replica*)object_get_data (r_obj);
+ PR_ASSERT (r);
+
+ if (!replica_is_legacy_consumer (r))
+ {
+ object_release (r_obj);
+ if (is_legacy_op) {
+ /* This is a legacy replication operation
+ but the replica is doesn't accept from legacy
+ Just refuse it */
+ slapi_send_ldap_result(pb, LDAP_UNWILLING_TO_PERFORM, NULL,
+ "Replication operation refused because "
+ "the consumer is not defined as a legacy replica", 0, NULL);
+ slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name,
+ "Incoming replication operation was refused because "
+ "there's no legacy replica defined for this operation\n");
+ return -1;
+ } else {
+ return 0;
+ }
+ }
+
+ object_release (r_obj);
+ }
+
+ opext = (consumer_operation_extension*) repl_con_get_ext (REPL_CON_EXT_OP, operation);
+
+ switch (operation_type) {
+ case OP_ADD:
+ {
+ Slapi_Entry *e = NULL;
+ Slapi_Attr *attr;
+ /*
+ * Check if the entry being added has copiedFrom/copyingFrom
+ * attributes.
+ */
+ slapi_pblock_get(pb, SLAPI_ADD_ENTRY, &e);
+ if (NULL != e)
+ {
+ if (slapi_entry_attr_find(e, type_copiedFrom, &attr) == 0)
+ {
+ has_cf = 1;
+ }
+ else
+ if (slapi_entry_attr_find(e, type_copyingFrom, &attr) == 0)
+ {
+ has_cf = 1;
+ }
+ }
+ /* JCMREPL - If this is a replicated operation then the baggage control also contains the Unique Identifier of the superior entry. */
+ }
+ break;
+ case OP_MODIFY:
+ {
+ LDAPMod **mods = NULL;
+ int i;
+
+ /*
+ * Check if the modification contains copiedFrom/copyingFrom
+ * attributes.
+ */
+ slapi_pblock_get(pb, SLAPI_MODIFY_MODS, &mods);
+ for (i = 0; NULL != mods && NULL != mods[i]; i++)
+ {
+ if ((strcasecmp(mods[i]->mod_type, type_copiedFrom) == 0) ||
+ (strcasecmp(mods[i]->mod_type, type_copyingFrom) == 0))
+ {
+ has_cf = 1;
+ }
+ }
+ }
+ break;
+ case OP_DELETE:
+ break;
+ case OP_MODDN:
+ break;
+ }
+
+ /* Squirrel away an optimization hint for the postop plugin */
+ opext->has_cf = has_cf;
+
+ return rc;
+}
diff --git a/ldap/servers/plugins/replication/repl_rootdse.c b/ldap/servers/plugins/replication/repl_rootdse.c
new file mode 100644
index 00000000..2bd1d8e8
--- /dev/null
+++ b/ldap/servers/plugins/replication/repl_rootdse.c
@@ -0,0 +1,79 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+
+#include <string.h>
+
+#include "repl.h"
+#include "cl4.h"
+#include "slapi-plugin.h"
+
+/* Forward Declartions */
+static int repl_rootdse_search (Slapi_PBlock *pb, Slapi_Entry* e, Slapi_Entry* entryAfter, int *returncode, char *returntext, void *arg);
+
+int
+repl_rootdse_init()
+{
+ /* The FE DSE *must* be initialised before we get here */
+ int return_value= LDAP_SUCCESS;
+
+ slapi_config_register_callback(SLAPI_OPERATION_SEARCH,DSE_FLAG_PREOP,"",LDAP_SCOPE_BASE,"(objectclass=*)",repl_rootdse_search,NULL);
+
+ return return_value;
+}
+
+static int
+repl_rootdse_search(Slapi_PBlock *pb, Slapi_Entry* e, Slapi_Entry* entryAfter, int *returncode, char *returntext, void *arg)
+{
+
+#if 0
+ struct berval val;
+ struct berval *vals[2];
+ vals[0] = &val;
+ vals[1] = NULL;
+
+ /* machine data suffix */
+ val.bv_val = REPL_CONFIG_TOP;
+ val.bv_len = strlen( val.bv_val );
+ slapi_entry_attr_replace( e, ATTR_NETSCAPEMDSUFFIX, vals );
+
+ /* Changelog information */
+/* ONREPL because we now support multiple 4.0 changelogs we no longer publish
+ info in the rootdse */
+ if ( get_repl_backend() != NULL )
+ {
+ char buf[BUFSIZ];
+ changeNumber cnum;
+
+ /* Changelog suffix */
+ val.bv_val = changelog4_get_suffix ();
+ if ( val.bv_val != NULL )
+ {
+ val.bv_len = strlen( val.bv_val );
+ slapi_entry_attr_replace( e, "changelog", vals );
+ }
+ slapi_ch_free ((void **)&val.bv_val);
+
+ /* First change number contained in log */
+ cnum = ldapi_get_first_changenumber();
+ sprintf( buf, "%lu", cnum );
+ val.bv_val = buf;
+ val.bv_len = strlen( val.bv_val );
+ slapi_entry_attr_replace( e, "firstchangenumber", vals );
+
+ /* Last change number contained in log */
+ cnum = ldapi_get_last_changenumber();
+ sprintf( buf, "%lu", cnum );
+ val.bv_val = buf;
+ val.bv_len = strlen( val.bv_val );
+ slapi_entry_attr_replace( e, "lastchangenumber", vals );
+ }
+#endif
+
+ return SLAPI_DSE_CALLBACK_OK;
+}
+
+
+
diff --git a/ldap/servers/plugins/replication/repl_search.c b/ldap/servers/plugins/replication/repl_search.c
new file mode 100644
index 00000000..cfa21222
--- /dev/null
+++ b/ldap/servers/plugins/replication/repl_search.c
@@ -0,0 +1,25 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+
+#include "slapi-plugin.h"
+#include "repl.h"
+
+/* XXXggood I think we no longer need this - the mapping tree should do it for us */
+int
+legacy_preop_search( Slapi_PBlock *pb )
+{
+ int return_code = 0;
+ return return_code;
+}
+
+
+/* XXXggood I think we no longer need this - the mapping tree should do it for us */
+int
+legacy_pre_entry( Slapi_PBlock *pb )
+{
+ int return_code = 0;
+ return return_code;
+}
diff --git a/ldap/servers/plugins/replication/repl_shared.h b/ldap/servers/plugins/replication/repl_shared.h
new file mode 100644
index 00000000..0c7454ab
--- /dev/null
+++ b/ldap/servers/plugins/replication/repl_shared.h
@@ -0,0 +1,132 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+/* repl_shared.h - definitions shared between 4.0 and 5.0 replication
+ modules
+ */
+
+#ifndef REPL_SHARED_H
+#define REPL_SHARED_H
+
+#include "slapi-private.h"
+#include "slapi-plugin.h"
+#include "ldif.h" /* GGOODREPL - is this cheating? */
+
+#ifdef _WIN32
+#define FILE_PATHSEP '\\'
+#else
+#define FILE_PATHSEP '/'
+#endif
+
+#define CHANGELOGDB_TRIM_INTERVAL 300 /* 5 minutes */
+
+#define CONFIG_CHANGELOG_DIR_ATTRIBUTE "nsslapd-changelogdir"
+#define CONFIG_CHANGELOG_MAXENTRIES_ATTRIBUTE "nsslapd-changelogmaxentries"
+#define CONFIG_CHANGELOG_MAXAGE_ATTRIBUTE "nsslapd-changelogmaxage"
+/* Changelog Internal Configuration Parameters -> DB related */
+#define CONFIG_CHANGELOG_DB_DBCACHESIZE "nsslapd-dbcachesize"
+#define CONFIG_CHANGELOG_DB_DURABLE_TRANSACTIONS "nsslapd-db-durable-transaction"
+#define CONFIG_CHANGELOG_DB_CHECKPOINT_INTERVAL "nsslapd-db-checkpoint-interval"
+#define CONFIG_CHANGELOG_DB_CIRCULAR_LOGGING "nsslapd-db-circular-logging"
+#define CONFIG_CHANGELOG_DB_PAGE_SIZE "nsslapd-db-page-size"
+#define CONFIG_CHANGELOG_DB_LOGFILE_SIZE "nsslapd-db-logfile-size"
+#define CONFIG_CHANGELOG_DB_MAXTXN_SIZE "nsslapd-db-max-txn"
+#define CONFIG_CHANGELOG_DB_VERBOSE "nsslapd-db-verbose"
+#define CONFIG_CHANGELOG_DB_DEBUG "nsslapd-db-debug"
+#define CONFIG_CHANGELOG_DB_TRICKLE_PERCENTAGE "nsslapd-db-trickle-percentage"
+#define CONFIG_CHANGELOG_DB_SPINCOUNT "nsslapd-db-spin-count"
+/* Changelog Internal Configuration Parameters -> Changelog Cache related */
+#define CONFIG_CHANGELOG_CACHESIZE "nsslapd-cachesize"
+#define CONFIG_CHANGELOG_CACHEMEMSIZE "nsslapd-cachememsize"
+#define CONFIG_CHANGELOG_NB_LOCK "nsslapd-db-locks"
+#define CONFIG_CHANGELOG_MAX_CONCURRENT_WRITES "nsslapd-changelogmaxconcurrentwrites"
+
+#define T_CHANGETYPESTR "changetype"
+#define T_CHANGETYPE 1
+#define T_TIMESTR "time"
+#define T_TIME 2
+#define T_DNSTR "dn"
+#define T_DN 3
+#define T_CHANGESTR "change"
+#define T_CHANGE 4
+
+#define T_ADDCTSTR "add"
+#define T_ADDCT 4
+#define T_MODIFYCTSTR "modify"
+#define T_MODIFYCT 5
+#define T_DELETECTSTR "delete"
+#define T_DELETECT 6
+#define T_MODRDNCTSTR "modrdn"
+#define T_MODRDNCT 7
+#define T_MODDNCTSTR "moddn"
+#define T_MODDNCT 8
+
+#define T_MODOPADDSTR "add"
+#define T_MODOPADD 9
+#define T_MODOPREPLACESTR "replace"
+#define T_MODOPREPLACE 10
+#define T_MODOPDELETESTR "delete"
+#define T_MODOPDELETE 11
+#define T_MODSEPSTR "-"
+#define T_MODSEP 12
+
+#define T_NEWRDNSTR "newrdn"
+#define T_NEWSUPERIORSTR ATTR_NEWSUPERIOR
+#define T_DRDNFLAGSTR "deleteoldrdn"
+
+#define T_ERR -1
+#define AWAITING_OP -1
+
+#define STATE_REFERRAL "referral"
+#define STATE_UPDATE_REFERRAL "referral on update"
+#define STATE_BACKEND "backend"
+
+#define REPL_PLUGIN_NAME "NSMMReplicationPlugin"
+/*
+ * Changed version from 1.0 to 2.0 when we switched from libdb32 to libdb33
+ * richm 20020708
+ * also changed name from REPL_PLUGIN_VERSION to CHANGELOG_DB_VERSION since we use
+ * a different version for the plugin itself and this particular version is only
+ * used for the changelog database
+*/
+/*
+ * Changed version from 2.0 to 3.0 when we switched from libdb33 to libdb41
+ * noriko 20021203
+ */
+#define CHANGELOG_DB_VERSION_PREV "3.0"
+#define CHANGELOG_DB_VERSION "4.0"
+extern char *repl_plugin_name;
+extern char *repl_plugin_name_cl;
+
+/* repl_monitor.c */
+int repl_monitor_init();
+
+/* In replutil.c */
+char ** get_cleattrs();
+unsigned long strntoul( char *from, size_t len, int base );
+void freepmods( LDAPMod **pmods );
+char *copy_berval (struct berval* from);
+void entry_print(Slapi_Entry *e);
+int copyfile(char* source, char *destination, int overwrite, int mode);
+time_t age_str2time (const char *age);
+const char* changeType2Str (int type);
+int str2ChangeType (const char *str);
+lenstr *make_changes_string(LDAPMod **ldm, char **includeattrs);
+Slapi_Mods* parse_changes_string(char *str);
+PRBool IsValidOperation (const slapi_operation_parameters *op);
+const char *map_repl_root_to_dbid(Slapi_DN *repl_root);
+PRBool is_ruv_tombstone_entry (Slapi_Entry *e);
+
+/* replication plugins */
+enum {
+ PLUGIN_LEGACY_REPLICATION,
+ PLUGIN_MULTIMASTER_REPLICATION,
+ PLUGIN_MAX
+};
+
+void* repl_get_plugin_identity (int pluginID);
+void repl_set_plugin_identity (int pluginID, void *identity);
+
+#endif
diff --git a/ldap/servers/plugins/replication/replication.def b/ldap/servers/plugins/replication/replication.def
new file mode 100644
index 00000000..e71be4f6
--- /dev/null
+++ b/ldap/servers/plugins/replication/replication.def
@@ -0,0 +1,16 @@
+; BEGIN COPYRIGHT BLOCK
+; Copyright 2001 Sun Microsystems, Inc.
+; Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+; All rights reserved.
+; END COPYRIGHT BLOCK
+;
+;
+;
+DESCRIPTION 'Netscape Directory Server 7.0 Replication Plugin'
+;CODE SHARED READ EXECUTE
+;DATA SHARED READ WRITE
+EXPORTS
+ plugin_init_debug_level @1
+ replication_legacy_plugin_init @2
+ replication_multimaster_plugin_init @3
+ repl_chain_on_update @4
diff --git a/ldap/servers/plugins/replication/replutil.c b/ldap/servers/plugins/replication/replutil.c
new file mode 100644
index 00000000..f8a23f93
--- /dev/null
+++ b/ldap/servers/plugins/replication/replutil.c
@@ -0,0 +1,1073 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+
+ /*
+ * replutil.c - various utility functions common to all replication methods.
+ */
+
+#include <nspr.h>
+#include <stdio.h>
+#include <string.h>
+#include <sys/types.h>
+#include <errno.h>
+#ifndef _WIN32
+#include <sys/file.h>
+#include <sys/socket.h>
+#include <unistd.h>
+#include <fcntl.h>
+#endif
+#ifdef OS_solaris
+#include <dlfcn.h> /* needed for dlopen and dlsym */
+#endif /* solaris: dlopen */
+#include <time.h>
+#ifdef LINUX
+#include <errno.h> /* weird use of errno */
+#endif
+
+#include "slapi-plugin.h"
+#include "repl5.h"
+#include "repl.h"
+
+typedef int (*open_fn)(const char *path, int flags, ...);
+
+/* this is set during replication plugin initialization from the plugin entry */
+static char *replpluginpath = NULL;
+static PRBool is_chain_on_update_setup(const Slapi_DN *replroot);
+
+/*
+ * All standard changeLogEntry attributes (initialized in get_cleattrs)
+ */
+static char *cleattrs[ 10 ] = { NULL, NULL, NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL };
+
+/*
+ * Function: get_cleattrs
+ *
+ * Returns: an array of pointers to attribute names.
+ *
+ * Arguments: None.
+ *
+ * Description: Initializes, if necessary, and returns an array of char *s
+ * with attribute names used for retrieving changeLogEntry
+ * entries from the directory.
+ */
+char **
+get_cleattrs()
+{
+ if ( cleattrs[ 0 ] == NULL ) {
+ cleattrs[ 0 ] = type_objectclass;
+ cleattrs[ 1 ] = attr_changenumber;
+ cleattrs[ 2 ] = attr_targetdn;
+ cleattrs[ 3 ] = attr_changetype;
+ cleattrs[ 4 ] = attr_newrdn;
+ cleattrs[ 5 ] = attr_deleteoldrdn;
+ cleattrs[ 6 ] = attr_changes;
+ cleattrs[ 7 ] = attr_newsuperior;
+ cleattrs[ 8 ] = attr_changetime;
+ cleattrs[ 9 ] = NULL;
+ }
+ return cleattrs;
+}
+
+/*
+ * Function: add_bval2mods
+ *
+ * Description: same as add_val2mods, but sticks in a bval instead.
+ * val can be null.
+ */
+void
+add_bval2mods(LDAPMod **mod, char *type, char *val, int mod_op)
+{
+ *mod = (LDAPMod *) slapi_ch_calloc(1, sizeof (LDAPMod));
+ memset (*mod, 0, sizeof(LDAPMod));
+ (*mod)->mod_op = mod_op | LDAP_MOD_BVALUES;
+ (*mod)->mod_type = slapi_ch_strdup(type);
+
+ if (val != NULL){
+ (*mod)->mod_bvalues = (struct berval **) slapi_ch_calloc(2, sizeof(struct berval *));
+ (*mod)->mod_bvalues[0] = (struct berval *) slapi_ch_malloc (sizeof(struct berval));
+ (*mod)->mod_bvalues[1] = NULL;
+ (*mod)->mod_bvalues[0]->bv_len = strlen(val);
+ (*mod)->mod_bvalues[0]->bv_val = slapi_ch_strdup(val);
+ } else {
+ (*mod)->mod_bvalues = NULL;
+ }
+}
+
+
+char*
+copy_berval (struct berval* from)
+{
+ char* s = slapi_ch_malloc (from->bv_len + 1);
+ memcpy (s, from->bv_val, from->bv_len);
+ s [from->bv_len] = '\0';
+ return s;
+}
+
+
+/*
+ * Function: entry_print
+ * Arguments: e - entry to print
+ * Returns: nothing
+ * Description: Prints the contents of an Slapi_Entry struct. Used for debugging.
+ */
+void
+entry_print( Slapi_Entry *e )
+{
+ int sz;
+ char *p;
+
+ printf( "Slapi_Entry dump:\n" );
+
+ if ( e == NULL ) {
+ printf( "Slapi_Entry is NULL\n" );
+ return;
+ }
+
+ if (( p = slapi_entry2str( e, &sz )) == NULL ) {
+ printf( "slapi_entry2str returned NULL\n" );
+ return;
+ }
+ puts( p );
+ fflush( stdout );
+ free( p );
+ return;
+}
+
+/* NSPR supports large file, but, according to dboreham, it does not work.
+ The backed has its own functions to deal with large files. I thought
+ about making them slapi function, but I don't think it makes sense because
+ server should only export function which have to do with its operation
+ and copying files is not one of them. So, instead, I made a copy of it in the
+ replication module. I will switch it to NSPR once that stuff works.
+*/
+
+int copyfile(char* source, char * destination, int overwrite, int mode)
+{
+#if defined _WIN32
+ return (0 == CopyFile(source,destination,overwrite ? FALSE : TRUE));
+#else
+#ifdef DB_USE_64LFS
+#define OPEN_FUNCTION dblayer_open_large
+#else
+#define OPEN_FUNCTION open
+#endif
+ int source_fd = -1;
+ int dest_fd = -1;
+ char *buffer = NULL;
+ int return_value = -1;
+ int bytes_to_write = 0;
+
+ /* malloc the buffer */
+ buffer = (char*) malloc(64*1024);
+ if (NULL == buffer)
+ {
+ slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name, "copy file: memory allocation failed\n");
+ goto error;
+ }
+ /* Open source file */
+ source_fd = OPEN_FUNCTION(source,O_RDONLY,0);
+ if (-1 == source_fd)
+ {
+ slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name,
+ "copyfile: failed to open source file %s\n", source);
+ goto error;
+ }
+ /* Open destination file */
+ dest_fd = OPEN_FUNCTION(destination,O_CREAT | O_WRONLY, mode);
+ if (-1 == dest_fd)
+ {
+ slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name,
+ "copyfile: failed to open destination file %s\n", destination);
+ goto error;
+ }
+ /* Loop round reading data and writing it */
+ while (1)
+ {
+ return_value = read(source_fd,buffer,64*1024);
+ if (return_value <= 0)
+ {
+ /* means error or EOF */
+ break;
+ }
+ bytes_to_write = return_value;
+ return_value = write(dest_fd,buffer,bytes_to_write);
+ if (return_value != bytes_to_write)
+ {
+ /* means error */
+ slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name,
+ "copyfile: failed to write to destination file %s\n");
+ return_value = -1;
+ break;
+ }
+ }
+error:
+ if (source_fd != -1)
+ {
+ close(source_fd);
+ }
+ if (dest_fd != -1)
+ {
+ close(dest_fd);
+ }
+ if (NULL != buffer)
+ {
+ free(buffer);
+ }
+ return return_value;
+#endif
+}
+
+/* convert time from string like 1h (1 hour) to corresponding time in seconds */
+time_t
+age_str2time (const char *age)
+{
+ char *maxage;
+ char unit;
+ time_t ageval;
+
+ if (age == NULL || age[0] == '\0' || strcmp (age, "0") == 0)
+ {
+ return 0;
+ }
+
+ maxage = slapi_ch_strdup ( age );
+ unit = maxage[ strlen( maxage ) - 1 ];
+ maxage[ strlen( maxage ) - 1 ] = '\0';
+ ageval = strntoul( maxage, strlen( maxage ), 10 );
+ if ( maxage)
+ {
+ slapi_ch_free ( (void **) &maxage );
+ }
+ switch ( unit )
+ {
+ case 's':
+ break;
+ case 'm':
+ ageval *= 60;
+ break;
+ case 'h':
+ ageval *= ( 60 * 60 );
+ break;
+ case 'd':
+ ageval *= ( 24 * 60 * 60 );
+ break;
+ case 'w':
+ ageval *= ( 7 * 24 * 60 * 60 );
+ break;
+ default:
+ slapi_log_error( SLAPI_LOG_PLUGIN, repl_plugin_name,
+ "age_str2time: unknown unit \"%c\" "
+ "for maxiumum changelog age\n", unit );
+ ageval = -1;
+ }
+
+ return ageval;
+}
+
+const char*
+changeType2Str (int type)
+{
+ switch (type)
+ {
+ case T_ADDCT: return T_ADDCTSTR;
+ case T_MODIFYCT: return T_MODIFYCTSTR;
+ case T_MODRDNCT: return T_MODRDNCTSTR;
+ case T_DELETECT: return T_DELETECTSTR;
+ default: return NULL;
+ }
+}
+
+int
+str2ChangeType (const char *str)
+{
+ if (strcasecmp (str, T_ADDCTSTR) == 0)
+ return T_ADDCT;
+
+ if (strcasecmp (str, T_MODIFYCTSTR) == 0)
+ return T_MODIFYCT;
+
+ if (strcasecmp (str, T_MODRDNCTSTR) == 0)
+ return T_MODRDNCT;
+
+ if (strcasecmp (str, T_DELETECTSTR) == 0)
+ return T_DELETECT;
+
+ return -1;
+}
+
+lenstr *
+make_changes_string(LDAPMod **ldm, char **includeattrs)
+{
+ lenstr *l;
+ int i, j, len;
+ int skip;
+
+ /* loop through the LDAPMod struct and construct the changes attribute */
+ l = lenstr_new();
+
+ for ( i = 0; ldm[ i ] != NULL; i++ ) {
+ /* If a list of explicit attributes was given, only add those */
+ if ( NULL != includeattrs ) {
+ skip = 1;
+ for ( j = 0; includeattrs[ j ] != NULL; j++ ) {
+ if ( strcasecmp( includeattrs[ j ], ldm[ i ]->mod_type ) == 0 ) {
+ skip = 0;
+ break;
+ }
+ }
+ if ( skip ) {
+ continue;
+ }
+ }
+ switch ( ldm[ i ]->mod_op & ~LDAP_MOD_BVALUES ) {
+ case LDAP_MOD_ADD:
+ addlenstr( l, "add: " );
+ addlenstr( l, ldm[ i ]->mod_type );
+ addlenstr( l, "\n" );
+ break;
+ case LDAP_MOD_DELETE:
+ addlenstr( l, "delete: " );
+ addlenstr( l, ldm[ i ]->mod_type );
+ addlenstr( l, "\n" );
+ break;
+ case LDAP_MOD_REPLACE:
+ addlenstr( l, "replace: " );
+ addlenstr( l, ldm[ i ]->mod_type );
+ addlenstr( l, "\n" );
+ break;
+ }
+ for ( j = 0; ldm[ i ]->mod_bvalues != NULL &&
+ ldm[ i ]->mod_bvalues[ j ] != NULL; j++ ) {
+ char *buf = NULL;
+ char *bufp = NULL;
+
+ len = strlen( ldm[ i ]->mod_type );
+ len = LDIF_SIZE_NEEDED( len,
+ ldm[ i ]->mod_bvalues[ j ]->bv_len ) + 1;
+ buf = slapi_ch_malloc( len );
+ bufp = buf;
+ ldif_put_type_and_value( &bufp, ldm[ i ]->mod_type,
+ ldm[ i ]->mod_bvalues[ j ]->bv_val,
+ ldm[ i ]->mod_bvalues[ j ]->bv_len );
+ *bufp = '\0';
+
+ addlenstr( l, buf );
+
+ free( buf );
+ }
+ addlenstr( l, "-\n" );
+ }
+ return l;
+}
+
+/* note that the string get modified by ldif_parse*** functions */
+Slapi_Mods *
+parse_changes_string(char *str)
+{
+ int rc;
+ Slapi_Mods *mods;
+ Slapi_Mod mod;
+ char *line, *next;
+ char *type, *value;
+ int vlen;
+ struct berval bv;
+
+ /* allocate mods array */
+ mods = slapi_mods_new ();
+ if (mods == NULL)
+ return NULL;
+
+ slapi_mods_init (mods, 16); /* JCMREPL - ONREPL : 16 bigger than needed? */
+
+ /* parse mods */
+ next = str;
+ line = ldif_getline (&next);
+ while (line)
+ {
+ slapi_mod_init (&mod, 0);
+ while (line)
+ {
+ char * errmsg = NULL;
+
+ if (strcasecmp (line, "-") == 0)
+ {
+ if (slapi_mod_isvalid (&mod))
+ {
+ slapi_mods_add_smod (mods, &mod);
+ /* JCMREPL - ONREPL - slapi_mod_done(&mod) ??? */
+ }
+ else
+ {
+ /* ONREPL - need to cleanup */
+ }
+
+ line = ldif_getline (&next);
+ break;
+ }
+
+ rc = ldif_parse_line(line, &type, &value, &vlen, &errmsg);
+ if (rc != 0)
+ {
+ /* ONREPL - log warning */
+ if ( errmsg != NULL ) {
+ slapi_log_error( SLAPI_LOG_PARSE, repl_plugin_name, "%s", errmsg );
+ slapi_ch_free( (void**)&errmsg );
+ }
+ slapi_log_error( SLAPI_LOG_REPL, repl_plugin_name,
+ "Failed to parse the ldif line.\n");
+ continue;
+ }
+
+ if (strcasecmp (type, "add") == 0)
+ {
+ slapi_mod_set_operation (&mod, LDAP_MOD_ADD | LDAP_MOD_BVALUES);
+ }
+ else if (strcasecmp (type, "delete") == 0)
+ {
+ slapi_mod_set_operation (&mod, LDAP_MOD_DELETE | LDAP_MOD_BVALUES);
+ }
+ else if (strcasecmp (type, "replace") == 0)
+ {
+ slapi_mod_set_operation (&mod, LDAP_MOD_REPLACE | LDAP_MOD_BVALUES);
+ }
+ else /* attr: value pair */
+ {
+ /* adding first value */
+ if (slapi_mod_get_type (&mod) == NULL)
+ {
+ slapi_mod_set_type (&mod, type);
+ }
+
+ bv.bv_val = value;
+ bv.bv_len = vlen;
+
+ slapi_mod_add_value (&mod, &bv);
+ }
+
+ line = ldif_getline (&next);
+ }
+ }
+
+ return mods;
+}
+
+static void* g_plg_identity [PLUGIN_MAX];
+
+void*
+repl_get_plugin_identity (int pluginID)
+{
+ PR_ASSERT (pluginID < PLUGIN_MAX);
+ return g_plg_identity [pluginID];
+}
+
+void
+repl_set_plugin_identity (int pluginID, void *identity)
+{
+ PR_ASSERT (pluginID < PLUGIN_MAX);
+ g_plg_identity [pluginID] = identity;
+}
+
+/* this function validates operation parameters */
+PRBool
+IsValidOperation (const slapi_operation_parameters *op)
+{
+ if (op == NULL)
+ {
+ slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name,
+ "IsValidOperation: NULL operation\n");
+ return PR_FALSE;
+ }
+
+ if (op->csn == NULL)
+ {
+ slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name,
+ "IsValidOperation: NULL operation CSN\n");
+ return PR_FALSE;
+ }
+
+ if (op->target_address.uniqueid == NULL)
+ {
+ slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name,
+ "IsValidOperation: NULL entry uniqueid\n");
+ return PR_FALSE;
+ }
+
+ if (op->target_address.dn == NULL)
+ {
+ slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name,
+ "IsValidOperation: NULL entry DN\n");
+ return PR_FALSE;
+ }
+
+ switch (op->operation_type)
+ {
+ case SLAPI_OPERATION_ADD: if (op->p.p_add.target_entry == NULL)
+ {
+ slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name,
+ "IsValidOperation: NULL entry for add operation\n");
+ return PR_FALSE;
+ }
+ else
+ break;
+
+ case SLAPI_OPERATION_MODIFY: if (op->p.p_modify.modify_mods == NULL ||
+ op->p.p_modify.modify_mods[0] == NULL)
+ {
+ slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name,
+ "IsValidOperation: NULL mods for modify operation\n");
+ return PR_FALSE;
+ }
+ else
+ break;
+
+ case SLAPI_OPERATION_MODRDN: if (op->p.p_modrdn.modrdn_mods == NULL ||
+ op->p.p_modrdn.modrdn_mods[0] == NULL)
+ {
+ slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name,
+ "IsValidOperation: NULL mods for modrdn operation\n");
+ return PR_FALSE;
+ }
+ if (op->p.p_modrdn.modrdn_newrdn == NULL)
+ {
+ slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name,
+ "IsValidOperation: NULL new rdn for modrdn operation\n");
+ return PR_FALSE;
+ }
+ else
+ break;
+
+ case SLAPI_OPERATION_DELETE: break;
+
+ default: return PR_FALSE;
+ }
+
+ return PR_TRUE;
+}
+
+
+
+const char *
+map_repl_root_to_dbid(Slapi_DN *repl_root)
+{
+ const char *return_ptr;
+
+ PR_ASSERT(NULL != repl_root);
+ if (NULL != repl_root)
+ {
+ /* XXXggood get per-database ID here, when code available */
+ }
+ return_ptr = get_server_dataversion(); /* XXXggood temporary hack until we have per-database instance dbids */
+ return return_ptr;
+}
+
+
+
+PRBool
+is_ruv_tombstone_entry (Slapi_Entry *e)
+{
+ char *dn;
+ char *match;
+ PR_ASSERT (e);
+
+ dn = slapi_entry_get_dn (e);
+ PR_ASSERT (dn);
+
+ /* tombstone has rdn: nsuniqueid=ffffffff-ffffffff-ffffffff-ffffffff */
+ match = strstr (dn, RUV_STORAGE_ENTRY_UNIQUEID);
+
+ return (match != NULL);
+}
+
+LDAPControl* create_managedsait_control ()
+{
+ LDAPControl *control;
+
+ control = (LDAPControl*)slapi_ch_malloc (sizeof (LDAPControl));
+
+ control->ldctl_oid = slapi_ch_strdup (LDAP_CONTROL_MANAGEDSAIT);
+ control->ldctl_value.bv_val = NULL;
+ control->ldctl_value.bv_len = 0;
+ control->ldctl_iscritical = '\0';
+
+ return control;
+}
+
+LDAPControl* create_backend_control (Slapi_DN *sdn)
+{
+ LDAPControl *control = NULL;
+ const char *be_name = slapi_mtn_get_backend_name(sdn);
+ if (NULL != be_name) {
+ control = (LDAPControl*)slapi_ch_malloc (sizeof (LDAPControl));
+
+ control->ldctl_oid = slapi_ch_strdup ("2.16.840.1.113730.3.4.14");
+ control->ldctl_value.bv_val = strdup(be_name);
+ control->ldctl_value.bv_len = strlen (be_name);
+ control->ldctl_iscritical = 1;
+ }
+
+ return control;
+}
+
+/*
+ * HREF_CHAR_ACCEPTABLE was copied from slapd/referral.c
+ * which was copied from libldap/tmplout.c.
+ */
+/* Note: an identical function is in ../../slapd/referral.c */
+#define HREF_CHAR_ACCEPTABLE( c ) (( c >= '-' && c <= '9' ) || \
+ ( c >= '@' && c <= 'Z' ) || \
+ ( c == '_' ) || \
+ ( c >= 'a' && c <= 'z' ))
+
+/*
+ * Function: strcat_escaped
+ *
+ * Returns: nothing
+ *
+ * Description: Appends string s2 to s1, URL-escaping (%HH) unsafe
+ * characters in s2 as appropriate. This function was
+ * copied from slapd/referral.c.
+ * which was copied from libldap/tmplout.c.
+ * added const qualifier
+ *
+ * Author: MCS
+ */
+/*
+ * append s2 to s1, URL-escaping (%HH) unsafe characters
+ */
+/* Note: an identical function is in ../../slapd/referral.c */
+static void
+strcat_escaped( char *s1, const char *s2 )
+{
+ char *p, *q;
+ char *hexdig = "0123456789ABCDEF";
+
+ p = s1 + strlen( s1 );
+ for ( q = (char*)s2; *q != '\0'; ++q ) {
+ if ( HREF_CHAR_ACCEPTABLE( *q )) {
+ *p++ = *q;
+ } else {
+ *p++ = '%';
+ *p++ = hexdig[ 0x0F & ((*(unsigned char*)q) >> 4) ];
+ *p++ = hexdig[ 0x0F & *q ];
+ }
+ }
+
+ *p = '\0';
+}
+
+/*
+ This function appends the replication root to the purl referrals found
+ in the given ruv and the other given referrals, merges the lists, and sets the
+ referrals in the mapping tree node corresponding to the given sdn, which is the
+ repl_root
+ This function also sets the mapping tree state (e.g. disabled, backend, referral,
+ referral on update) - the mapping tree has very specific rules about how states
+ can be set in the presence of referrals - specifically:
+ 1) the nsslapd-referral attribute must be set before changing the state to referral
+ or referral on update
+ 2) the state must be set to backend or disabled before removing referrals
+*/
+void
+repl_set_mtn_state_and_referrals(
+ const Slapi_DN *repl_root_sdn,
+ const char *mtn_state,
+ const RUV *ruv,
+ char **ruv_referrals,
+ char **other_referrals
+)
+{
+ int rc = 0;
+ int ii = 0;
+ char **referrals_to_set = NULL;
+ PRBool chain_on_update = is_chain_on_update_setup(repl_root_sdn);
+
+ /* Fix for blackflag bug 601440: We want the new behaviour of DS,
+ ** going forward, to now be that if the nsds5replicareferral attrib
+ ** has values, it should be the only values in nsslapd-referral (as
+ ** opposed to older behaviour of concatenating with RUV-based
+ ** referrals). -jay@netscape.com
+ */
+ if (other_referrals) {
+ /* use the referrals passed in, instead of RUV-based referrals */
+ charray_merge(&referrals_to_set, other_referrals, 1);
+ /* Do copies. referrals is freed at the end */
+ }
+ else
+ {
+ /* use the referrals from the RUV */
+ ruv_referrals= (ruv ? ruv_get_referrals(ruv) : ruv_referrals);
+ if (ruv_referrals) {
+ charray_merge(&referrals_to_set, ruv_referrals, 1);
+ if (ruv) /* free referrals from ruv_get_referrals() */
+ charray_free(ruv_referrals);
+ }
+ }
+
+ /* next, add the repl root dn to each referral if not present */
+ for (ii = 0; referrals_to_set && referrals_to_set[ii]; ++ii) {
+ struct ldap_url_desc *lud = NULL;
+ int myrc = ldap_url_parse(referrals_to_set[ii], &lud);
+ /* see if the dn is already in the referral URL */
+ if (myrc == LDAP_URL_ERR_NODN || !lud || !lud->lud_dn) {
+ /* add the dn */
+ int len = strlen(referrals_to_set[ii]);
+ const char *cdn = slapi_sdn_get_dn(repl_root_sdn);
+ char *tmpref = NULL;
+ int need_slash = 0;
+ if (referrals_to_set[ii][len-1] != '/') {
+ len++; /* add another one for the slash */
+ need_slash = 1;
+ }
+ len += (strlen(cdn) * 3) + 2; /* 3 for %HH possible per char */
+ tmpref = slapi_ch_malloc(len);
+ sprintf(tmpref, "%s%s", referrals_to_set[ii], (need_slash ? "/" : ""));
+ strcat_escaped(tmpref, cdn);
+ slapi_ch_free((void **)&referrals_to_set[ii]);
+ referrals_to_set[ii] = tmpref;
+ }
+ if (lud)
+ ldap_free_urldesc(lud);
+ }
+
+ if (!referrals_to_set) { /* deleting referrals */
+ /* Set state before */
+ if (!chain_on_update) {
+ slapi_mtn_set_state(repl_root_sdn, (char *)mtn_state);
+ }
+ /* We should delete referral only if we want to set the
+ replica database in backend state mode */
+ /* if chain on update mode, go ahead and set the referrals anyway */
+ if (strcasecmp(mtn_state, STATE_BACKEND) == 0 || chain_on_update) {
+ rc = slapi_mtn_set_referral(repl_root_sdn, referrals_to_set);
+ if (rc == LDAP_NO_SUCH_ATTRIBUTE) {
+ /* we will get no such attribute (16) if we try to set the referrals to NULL if
+ there are no referrals - not an error */
+ rc = LDAP_SUCCESS;
+ }
+ }
+ } else { /* Replacing */
+ rc = slapi_mtn_set_referral(repl_root_sdn, referrals_to_set);
+ if (rc == LDAP_SUCCESS && !chain_on_update){
+ slapi_mtn_set_state(repl_root_sdn, (char *)mtn_state);
+ }
+ }
+
+ if (rc != LDAP_SUCCESS) {
+ char ebuf[BUFSIZ];
+ slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name, "repl_set_mtn_referrals: could "
+ "not set referrals for replica %s: %d\n",
+ escape_string(slapi_sdn_get_dn(repl_root_sdn), ebuf), rc);
+ }
+
+ charray_free(referrals_to_set);
+ return;
+}
+
+/*
+ * This function allows to use a local backend in conjunction with
+ * a chaining backend
+ * The local ldbm backend is the replication consumer database
+ * (e.g. on a hub or consumer) - it is read-only except for supplier updates
+ * The chaining backend points to the supplier(s)
+ * This distribution logic forwards the update request to the chaining
+ * backend, and sends the search request to the local ldbm database
+ *
+ * To be able to use it one must define one ldbm backend and one chaining
+ * backend in the mapping tree node - the ldbm backend will usually
+ * already be there
+ *
+ */
+int
+repl_chain_on_update(Slapi_PBlock *pb, Slapi_DN * target_dn,
+ char **mtn_be_names, int be_count,
+ Slapi_DN * node_dn, int *mtn_be_states)
+{
+ char * requestor_dn;
+ unsigned long op_type;
+ Slapi_Operation *op;
+ int repl_op = 0;
+ int local_backend = -1; /* index of local backend */
+ int chaining_backend = -1; /* index of chain backend */
+ PRBool local_online = PR_FALSE; /* true if the local db is online */
+ PRBool chain_online = PR_FALSE; /* true if the chain db is online */
+ int ii;
+ int opid, connid;
+
+ slapi_pblock_get(pb, SLAPI_CONN_ID, &connid);
+ slapi_pblock_get(pb, SLAPI_OPERATION_ID, &opid);
+ /* first, we have to decide which backend is the local backend
+ * and which is the chaining one
+ * also find out if any are not online (e.g. during import)
+ */
+ for (ii = 0; ii < be_count; ++ii)
+ {
+ Slapi_Backend *be = slapi_be_select_by_instance_name(mtn_be_names[ii]);
+ if (slapi_be_is_flag_set(be,SLAPI_BE_FLAG_REMOTE_DATA))
+ {
+ chaining_backend = ii;
+ if (mtn_be_states[ii] == SLAPI_BE_STATE_ON)
+ {
+ chain_online = PR_TRUE;
+ }
+ }
+ else
+ {
+ local_backend = ii;
+ if (mtn_be_states[ii] == SLAPI_BE_STATE_ON)
+ {
+ local_online = PR_TRUE;
+ }
+ }
+/*
+ slapi_log_error(SLAPI_LOG_REPL, repl_plugin_name, "repl_chain_on_update: conn=%d op=%d be "
+ "%s is the %s backend and is %s\n",
+ connid, opid,
+ mtn_be_names[ii], (chaining_backend == ii) ? "chaining" : "local",
+ (mtn_be_states[ii] == SLAPI_BE_STATE_ON) ? "online" : "offline");
+*/
+ }
+
+ /* if no chaining backends are defined, just use the local one */
+ if (chaining_backend == -1) {
+ return local_backend;
+ }
+
+ slapi_pblock_get(pb, SLAPI_OPERATION, &op);
+
+ /* All internal operations go to the local backend */
+ if (operation_is_flag_set(op, OP_FLAG_INTERNAL)) {
+ return local_backend;
+ }
+
+ /* Check the operation type
+ * read-only operation will go to the local backend if online
+ */
+ op_type = slapi_op_get_type(op);
+ if (local_online &&
+ ((op_type == SLAPI_OPERATION_SEARCH) ||
+ (op_type == SLAPI_OPERATION_BIND) ||
+ (op_type == SLAPI_OPERATION_UNBIND) ||
+ (op_type == SLAPI_OPERATION_COMPARE))) {
+/*
+ slapi_log_error(SLAPI_LOG_REPL, repl_plugin_name, "repl_chain_on_update: conn=%d op=%d op is "
+ "%d: using local backend\n",
+ connid, opid, op_type);
+*/
+ return local_backend;
+ }
+
+ /* if the operation is done by directory manager
+ * use local database even for updates because it is an administrative
+ * operation
+ * remarks : one could also use an update DN in the same way
+ * to let update operation go to the local backend when they are done
+ * by specific administrator user but let all the other user
+ * go to the read-write replica
+ * also - I don't think it is possible to chain directory manager
+ */
+ slapi_pblock_get(pb, SLAPI_REQUESTOR_DN, &requestor_dn);
+ if (slapi_dn_isroot(requestor_dn)) {
+/*
+ slapi_log_error(SLAPI_LOG_REPL, repl_plugin_name, "repl_chain_on_update: conn=%d op=%d requestor "
+ "is root: using local backend\n", connid, opid);
+*/
+ return local_backend;
+ }
+
+ /* if the operation is a replicated operation
+ * use local database even for updates to avoid infinite loops
+ */
+ slapi_pblock_get(pb, SLAPI_IS_REPLICATED_OPERATION, &repl_op);
+ if (repl_op) {
+/*
+ slapi_log_error(SLAPI_LOG_REPL, repl_plugin_name, "repl_chain_on_update: conn=%d op=%d op is "
+ "replicated: using local backend\n", connid, opid);
+*/
+ return local_backend;
+ }
+
+ /* all other case (update while not directory manager) :
+ * or any normal non replicated client operation while local is disabled (import) :
+ * use the chaining backend
+ */
+/*
+ slapi_log_error(SLAPI_LOG_REPL, repl_plugin_name, "repl_chain_on_update: conn=%d op=%d using "
+ "chaining backend\n", connid, opid);
+*/
+ return chaining_backend;
+}
+
+int
+repl_enable_chain_on_update(Slapi_DN *suffix)
+{
+ /* Submit a Modify operation to add the distribution function to the mapping tree
+ node for the given suffix */
+ slapi_mods smods;
+ Slapi_Operation *op = NULL;
+ int operation_result;
+ Slapi_PBlock *pb= slapi_pblock_new();
+ char *mtnnodedn;
+
+ slapi_mods_init(&smods,2);
+
+ /* need path and file name of the replication plugin here */
+ slapi_mods_add_string(&smods, LDAP_MOD_ADD, "nsslapd-distribution-plugin", replpluginpath);
+ slapi_mods_add_string(&smods, LDAP_MOD_ADD, "nsslapd-distribution-funct", "repl_chain_on_update");
+
+ /* need DN of mapping tree node here */
+ mtnnodedn = slapi_get_mapping_tree_node_configdn(suffix);
+ slapi_modify_internal_set_pb(
+ pb,
+ mtnnodedn,
+ slapi_mods_get_ldapmods_byref(&smods), /* JCM cast */
+ NULL, /*Controls*/
+ NULL, /*uniqueid*/
+ repl_get_plugin_identity(PLUGIN_MULTIMASTER_REPLICATION),
+ 0);
+
+ slapi_modify_internal_pb(pb);
+ slapi_pblock_get(pb, SLAPI_PLUGIN_INTOP_RESULT, &operation_result);
+ slapi_ch_free_string(&mtnnodedn);
+ slapi_pblock_destroy(pb);
+ switch(operation_result)
+ {
+ case LDAP_SUCCESS:
+ /* OK, everything is fine. */
+ break;
+ default:
+ PR_ASSERT(0); /* JCMREPL FOR DEBUGGING */
+ }
+ slapi_mods_done(&smods);
+
+ return operation_result;
+}
+
+int
+repl_disable_chain_on_update(Slapi_DN *suffix)
+{
+ /* Submit a Modify operation to remove the distribution function from the mapping tree
+ node for the given suffix */
+ slapi_mods smods;
+ Slapi_Operation *op = NULL;
+ int operation_result;
+ Slapi_PBlock *pb= slapi_pblock_new();
+ char *mtnnodedn;
+
+ slapi_mods_init(&smods,2);
+
+ slapi_mods_add_modbvps(&smods, LDAP_MOD_DELETE, "nsslapd-distribution-plugin", NULL);
+ slapi_mods_add_modbvps(&smods, LDAP_MOD_DELETE, "nsslapd-distribution-funct", NULL);
+
+ /* need DN of mapping tree node here */
+ mtnnodedn = slapi_get_mapping_tree_node_configdn(suffix);
+ slapi_modify_internal_set_pb(
+ pb,
+ mtnnodedn,
+ slapi_mods_get_ldapmods_byref(&smods), /* JCM cast */
+ NULL, /*Controls*/
+ NULL, /*uniqueid*/
+ repl_get_plugin_identity(PLUGIN_MULTIMASTER_REPLICATION),
+ 0);
+
+ slapi_modify_internal_pb(pb);
+ slapi_pblock_get(pb, SLAPI_PLUGIN_INTOP_RESULT, &operation_result);
+ slapi_ch_free_string(&mtnnodedn);
+ slapi_pblock_destroy(pb);
+ switch(operation_result)
+ {
+ case LDAP_SUCCESS:
+ /* OK, everything is fine. */
+ break;
+ default:
+ PR_ASSERT(0); /* JCMREPL FOR DEBUGGING */
+ }
+ slapi_mods_done(&smods);
+
+ return operation_result;
+}
+
+static PRBool
+is_chain_on_update_setup(const Slapi_DN *replroot)
+{
+ /* Do an internal search of the mapping tree node to see if chain on update is setup
+ for this replica
+ - has two backends
+ - has a distribution function
+ - has a distribution plugin
+ - one of the backends is a ldbm database
+ - one of the backends is a chaining database
+ */
+ static char* attrs[] = { "nsslapd-backend",
+ "nsslapd-distribution-plugin", "nsslapd-distribution-funct",
+ NULL };
+ int operation_result;
+ Slapi_PBlock *pb= slapi_pblock_new();
+ char *mtnnodedn = slapi_get_mapping_tree_node_configdn(replroot);
+ PRBool retval = PR_FALSE;
+
+ slapi_search_internal_set_pb(
+ pb,
+ mtnnodedn,
+ LDAP_SCOPE_BASE,
+ "objectclass=*",
+ attrs, /*attrs*/
+ 0, /*attrsonly*/
+ NULL, /*Controls*/
+ NULL, /*uniqueid*/
+ repl_get_plugin_identity(PLUGIN_MULTIMASTER_REPLICATION),
+ 0);
+ slapi_search_internal_pb(pb);
+ slapi_pblock_get(pb, SLAPI_PLUGIN_INTOP_RESULT, &operation_result);
+ switch(operation_result)
+ {
+ case LDAP_SUCCESS:
+ {
+ Slapi_Entry **entries= NULL;
+ slapi_pblock_get(pb, SLAPI_PLUGIN_INTOP_SEARCH_ENTRIES, &entries);
+ if(entries!=NULL && entries[0]!=NULL)
+ {
+ Slapi_Entry *e = entries[0];
+
+ char **backends = slapi_entry_attr_get_charray(e, "nsslapd-backend");
+ char *plg = slapi_entry_attr_get_charptr(e, "nsslapd-distribution-plugin");
+ char *func = slapi_entry_attr_get_charptr(e, "nsslapd-distribution-funct");
+
+ if (backends && backends[0] && backends[1] && plg && func)
+ {
+ /* all the necessary attrs are present - check to see if we
+ have one chaining backend */
+ Slapi_Backend *be0 = slapi_be_select_by_instance_name(backends[0]);
+ Slapi_Backend *be1 = slapi_be_select_by_instance_name(backends[1]);
+ PRBool foundchain0 = slapi_be_is_flag_set(be0,SLAPI_BE_FLAG_REMOTE_DATA);
+ PRBool foundchain1 = slapi_be_is_flag_set(be1,SLAPI_BE_FLAG_REMOTE_DATA);
+ retval = (foundchain0 || foundchain1) &&
+ !(foundchain0 && foundchain1); /* 1 (but not both) backend is chaining */
+ }
+ slapi_ch_array_free(backends);
+ slapi_ch_free_string(&plg);
+ slapi_ch_free_string(&func);
+ }
+ else /* could not find mapping tree entry - assume not set up */
+ {
+ }
+ }
+ break;
+ default: /* search error - assume not set up */
+ break;
+ }
+ slapi_ch_free_string(&mtnnodedn);
+ slapi_free_search_results_internal(pb);
+ slapi_pblock_destroy(pb);
+
+ return retval;
+}
+
+void
+repl_set_repl_plugin_path(const char *path)
+{
+ replpluginpath = slapi_ch_strdup(path);
+}
diff --git a/ldap/servers/plugins/replication/tests/dnp_sim.c b/ldap/servers/plugins/replication/tests/dnp_sim.c
new file mode 100644
index 00000000..ff524988
--- /dev/null
+++ b/ldap/servers/plugins/replication/tests/dnp_sim.c
@@ -0,0 +1,1033 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+/* dnp_simulation.c - this file varifies the correctness of dnp algorithm
+ by generating random sequences of operations, applying
+ the algorithm and outputing the result
+
+ usage: dnp_sim [-h] [-n <number of simulations> ] [-v] [-f <output file>]
+ -h - print usage information.
+ -n <number of simulations> - how many simulations to perform; default - 1.
+ -v - verbose mode (prints full entry state after each operation execution)
+ -f <output file> - file where results are stored; by default results are
+ printed to the screen.
+ -o <op file> - file that contains operation sequence to execute; by default,
+ random sequence is generated.
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <errno.h>
+#include <memory.h>
+#include <string.h>
+#include <time.h>
+
+#define MAX_OPS 12 /* maximum number of operations in a simulation */
+#define MAX_VALS 10 /* maximum number of values is entry or dn */
+#define NOT_PRESENT -1
+
+/* data types */
+typedef struct value_state
+{
+ int value_index; /* value */
+ int presense_csn; /* last time at which we know the value was present */
+ int distinguished_csn; /* last time at which we know the value was distinguished */
+ int delete_csn; /* last attempt to delete this value */
+ int non_distinguished_csns [MAX_OPS];/* list of times at which value became non-distinguished */
+ int present; /* flag that tells whether the value iscurrently present */
+} Value_State;
+
+typedef struct entry_state
+{
+ int dn_index;
+ int dn_csn;
+ Value_State values[MAX_VALS]; /* values of the attribute */
+ int attr_delete_csn; /* last attempt to delete the entire attribute */
+} Entry_State;
+
+typedef enum
+{
+ OP_ADD_VALUE,
+ OP_RENAME_ENTRY,
+ OP_DELETE_VALUE,
+ OP_DELETE_ATTR,
+ OP_END
+} Operation_Type;
+
+typedef struct operation
+{
+ Operation_Type type; /* operation type */
+ int csn; /* operation type */
+ int value_index; /* value to add, remove or rename from */
+ int old_dn_index; /* new dn - rename only */
+}Operation;
+
+typedef struct simulator_params
+{
+ int runs; /* number of runs */
+ FILE *fout; /* output file */
+ int value_count; /* number of values */
+ int verbose; /* verbose mode */
+ Operation *ops; /* operation sequence to apply */
+ int op_count;
+}Simulator_Params;
+
+
+/* gloabl data */
+Simulator_Params sim;
+char *g_values[] =
+{
+ "v",
+ "u",
+ "w",
+ NULL
+};
+
+/* forward declarations */
+
+/* initialization */
+void process_cmd (int argc, char **argv);
+void set_default_sim_params ();
+int count_values ();
+void parse_operations_file (char *name);
+void parse_operation (char *line, int pos);
+int value2index (char *value);
+void print_usage ();
+
+/* simulation run */
+void run_simulation ();
+void generate_operations (Operation **ops, int *op_count);
+void generate_operation (Operation *op, int csn, int *last_dn_index);
+int* generate_operation_order (int op_count, int seq_num);
+void apply_operation_sequence (Operation *ops, int op_count, int *order, Entry_State *entry);
+void init_entry_state (Entry_State *entry);
+void init_value_state (Value_State *val, int seq_num);
+void apply_operation (Entry_State *entry, Operation *op);
+void free_operations (Operation **ops);
+int ** new_perm_table (int op_count);
+void free_perm_table (int ***perm_table, int op_count);
+int get_perm_count (int op_count);
+void generate_perm_table (int *elements, int element_count, int static_part,
+ int **perm_table);
+void apply_add_operation (Entry_State *entry, Operation *op);
+void apply_value_delete_operation (Entry_State *entry, Operation *op);
+void apply_attr_delete_operation (Entry_State *entry, Operation *op);
+void apply_rename_operation (Entry_State *entry, Operation *op);
+void make_value_distinguished (int op_csn, Entry_State *entry, int value_index);
+void make_value_non_distinguished (int op_csn, Entry_State *entry, int value_index);
+void purge_value_state (Value_State *value);
+void purge_non_distinguished_csns (Value_State *value);
+void resolve_value_state (Entry_State *entry, int value_index);
+int value_distinguished_at_delete (Value_State *value, int attr_delete_csn);
+int compare_entry_state (Entry_State *entry1, Entry_State *entry2, int run);
+
+/* data tracing */
+void dump_operations (Operation *ops, int op_count, int *order);
+void dump_operation (Operation *op);
+void dump_perm_table (int **perm_table, int op_count);
+void dump_entry_state (Entry_State *entry);
+void dump_value_state (Value_State *value);
+void dump_list (int *list);
+
+/* misc functions */
+int max_val (int a, int b);
+int is_list_empty (int *list);
+int min_list_val (int *list);
+int list_equal (int *list1, int *list2);
+
+int main (int argc, char **argv)
+{
+ int i;
+
+ process_cmd (argc, argv);
+
+ for (i = 0; i < sim.runs; i++)
+ {
+ fprintf (sim.fout, "*******running simulation #%d ...\n\n", i+1);
+ run_simulation ();
+ fprintf (sim.fout, "\n*******done with simulation #%d ...\n\n", i+1);
+ }
+
+ if (sim.fout != stdout)
+ fclose (sim.fout);
+
+ return 0;
+}
+
+void process_cmd (int argc, char **argv)
+{
+ int i;
+
+ set_default_sim_params ();
+
+ if (argc == 1)
+ {
+ return;
+ }
+
+ if (strcmp (argv[1], "-h") == 0) /* print help */
+ {
+ print_usage ();
+ exit (0);
+ }
+
+ i = 1;
+ while (i < argc)
+ {
+ if (strcmp (argv[i], "-v") == 0) /* verbose mode */
+ {
+ sim.verbose = 1;
+ i ++;
+ }
+ else if (strcmp (argv[i], "-n") == 0)
+ {
+ if (i < argc - 1)
+ {
+ int runs = atoi (argv[i + 1]);
+ if (runs > 0)
+ sim.runs = runs;
+ i+=2;
+ }
+ else
+ {
+ /* ONREPL print warning */
+ i++;
+ }
+ }
+ else if (strcmp (argv[i], "-f") == 0) /* output file */
+ {
+ if (i < argc - 1)
+ {
+ FILE *f = fopen (argv[i + 1], "w");
+ if (f == 0)
+ {
+ printf ("failed to open output file; error - %s, using stdout\n",
+ strerror(errno));
+ }
+ else
+ {
+ /* ONREPL print warning */
+ sim.fout = f;
+ }
+
+ i += 2;
+ }
+ else
+ i++;
+ }
+ else if (strcmp (argv[i], "-o") == 0) /* file with operation sequence */
+ {
+ if (i < argc - 1)
+ {
+ parse_operations_file (argv[i+1]);
+ i += 2;
+ }
+ else
+ {
+ /* ONREPL print warning */
+ i ++;
+ }
+ }
+ else /* unknown option */
+ {
+ printf ("unknown option - %s; ignored\n", argv[i]);
+ i ++;
+ }
+
+ }
+}
+
+void set_default_sim_params ()
+{
+ memset (&sim, 0, sizeof (sim));
+ sim.runs = 1;
+ sim.fout = stdout;
+ sim.value_count = count_values ();
+}
+
+/* file format: <op count>
+ add <value>
+ delete <value>
+ delete attribute
+ rename <value> to <value>
+ */
+void parse_operations_file (char *name)
+{
+ FILE *file = fopen (name, "r");
+ char line [256];
+ int i;
+
+ if (file == NULL)
+ {
+ printf ("failed to open operations file %s: error = %d\n", name, errno);
+ print_usage ();
+ exit (1);
+ }
+
+ i = 0;
+ while (fgets (line, sizeof (line), file))
+ {
+ if (i == 0)
+ {
+ /* read operation count */
+ sim.op_count = atoi (line);
+ if (sim.op_count < 1 || sim.op_count > MAX_OPS/2)
+ {
+ printf ("invalid operation count - %d; value must be between 1 and %d\n",
+ sim.op_count, MAX_OPS/2);
+ print_usage ();
+ exit (1);
+ }
+ else
+ {
+ sim.ops = (Operation*)malloc (sim.op_count * sizeof (Operation));
+ }
+ }
+ else
+ {
+ if (strlen (line) == 0) /* skip empty lines */
+ continue;
+ parse_operation (line, i);
+ }
+
+ i ++;
+ }
+
+}
+
+void parse_operation (char *line, int i)
+{
+ sim.ops [i - 1].csn = i;
+
+ if (line[strlen(line) - 1] == '\n')
+ line[strlen(line) - 1] = '\0';
+
+ if (strncmp (line, "add", 3) == 0)
+ {
+ sim.ops [i - 1].type = OP_ADD_VALUE;
+ sim.ops [i - 1].value_index = value2index (&line[4]);
+ }
+ else if (strncmp (line, "delete attribute", 16) == 0)
+ {
+ sim.ops [i - 1].type = OP_DELETE_ATTR;
+ }
+ else if (strncmp (line, "delete", 6) == 0)
+ {
+ sim.ops [i - 1].type = OP_DELETE_VALUE;
+ sim.ops [i - 1].value_index = value2index (&line[7]);
+ }
+ else if (strncmp (line, "rename", 6) == 0)
+ {
+ char *tok;
+ sim.ops [i - 1].type = OP_RENAME_ENTRY;
+ /* strtok() is not MT safe, but it is okay to call here because this is a program test */
+ tok = strtok (&line[7], " ");
+ sim.ops [i - 1].old_dn_index = value2index (tok);
+ /* skip to */
+ tok = strtok (NULL, " ");
+ tok = strtok (NULL, " ");
+ sim.ops [i - 1].value_index = value2index (tok);
+ }
+ else
+ {
+ /* invalid line */
+ printf ("invalid operation: %s\n", line);
+ exit (1);
+ }
+}
+
+int value2index (char *value)
+{
+ int i;
+
+ for (i = 0; i < sim.value_count; i++)
+ {
+ if (strcmp (g_values[i], value) == 0)
+ return i;
+ }
+
+ return -1;
+}
+
+void print_usage ()
+{
+ printf ("usage: dnp_sim [-h] [-n <number of simulations> ] [-v] [-f <output file>]\n"
+ "\t-h - print usage information\n"
+ "\t-n <number of simulations>; default - 1\n"
+ "\t-v - verbose mode\n"
+ "\t-f <output file> - by default, results are printed to the screen\n"
+ "\t-o <op file> - file that contains operation sequence to execute;\n"
+ "\tby default, random sequence is generated.\n");
+}
+
+int count_values ()
+{
+ int i;
+
+ for (i = 0; g_values[i]; i++);
+
+ return i;
+}
+
+void run_simulation ()
+{
+ int *order;
+ int i;
+ int perm_count;
+ Entry_State entry_first, entry_current;
+ int error = 0;
+
+ init_entry_state (&entry_first);
+ fprintf (sim.fout, "initial entry state :\n");
+ dump_entry_state (&entry_first);
+
+ if (sim.ops == NULL)
+ {
+ generate_operations (&sim.ops, &sim.op_count);
+ }
+ fprintf (sim.fout, "initial operation set:\n");
+ dump_operations (sim.ops, sim.op_count, NULL/* order */);
+
+ perm_count = get_perm_count (sim.op_count);
+ for (i = 0; i < perm_count; i++)
+ {
+ fprintf (sim.fout, "--------------------------------\n");
+ fprintf (sim.fout, "simulation run %d\n", i + 1);
+ fprintf (sim.fout, "--------------------------------\n");
+ order = generate_operation_order (sim.op_count, i);
+ if (i == 0)
+ apply_operation_sequence (sim.ops, sim.op_count, order, &entry_first);
+ else
+ {
+ apply_operation_sequence (sim.ops, sim.op_count, order, &entry_current);
+ error |= compare_entry_state (&entry_first, &entry_current, i + 1);
+ }
+ }
+
+ switch (error)
+ {
+ case 0: fprintf (sim.fout, "all runs left the entry in the same state\n");
+ break;
+ case 1: fprintf (sim.fout, "while value presence is consistent across all runs, "
+ "the exact state does not match\n");
+ break;
+ case 3: fprintf (sim.fout, "the runs left entries in an inconsistent state\n");
+ break;
+ }
+
+ free_operations (&sim.ops);
+}
+
+void generate_operations (Operation **ops, int *op_count)
+{
+ int i;
+ int last_dn_index = 0;
+
+ /* generate number operations in the sequence */
+ *op_count = slapi_rand () % (MAX_OPS / 2) + 1;
+ *ops = (Operation *)malloc (*op_count * sizeof (Operation));
+
+ for (i = 0; i < *op_count; i ++)
+ {
+ generate_operation (&((*ops)[i]), i + 1, &last_dn_index);
+ }
+}
+
+void generate_operation (Operation *op, int csn, int *last_dn_index)
+{
+ /* generate operation type */
+ op->type = slapi_rand () % OP_END;
+
+ /* generate value to which operation applies */
+ op->value_index = slapi_rand () % sim.value_count;
+
+ op->csn = csn;
+
+ /* generate new distinguished value */
+ if (op->type == OP_RENAME_ENTRY)
+ {
+ op->old_dn_index = *last_dn_index;
+ while (op->value_index == op->old_dn_index)
+ op->value_index = slapi_rand () % sim.value_count;
+ *last_dn_index = op->value_index;
+ }
+}
+
+int* generate_operation_order (int op_count, int seq_num)
+{
+ static int **perm_table = NULL;
+
+ /* first request - generate pemutation table */
+ if (seq_num == 0)
+ {
+ int elements [MAX_OPS];
+ int i;
+
+ if (perm_table)
+ free_perm_table (&perm_table, op_count);
+ perm_table = new_perm_table (op_count);
+
+ for (i = 0; i < op_count; i++)
+ elements [i] = i;
+
+ generate_perm_table (elements, op_count, 0 /* static part */,
+ perm_table);
+ /* dump_perm_table (perm_table, op_count);*/
+ }
+
+ return perm_table [seq_num];
+}
+
+void apply_operation_sequence (Operation *ops, int op_count, int *order, Entry_State *entry)
+{
+ int i;
+
+ init_entry_state (entry);
+
+ if (!sim.verbose)
+ {
+ if (!sim.verbose)
+ {
+ fprintf (sim.fout, "operation_sequence for this run:\n");
+ dump_operations (ops, op_count, order);
+ }
+ }
+
+ for (i = 0; i < op_count; i++)
+ {
+ apply_operation (entry, &(ops [order[i]]));
+ }
+
+ if (!sim.verbose)
+ {
+ fprintf (sim.fout, "final entry state :\n");
+ dump_entry_state (entry);
+ }
+
+}
+
+void init_entry_state (Entry_State *entry)
+{
+ int i;
+
+ memset (entry, 0, sizeof (*entry));
+ entry->attr_delete_csn = NOT_PRESENT;
+
+ for (i = 0; i < sim.value_count; i++)
+ {
+ init_value_state (&(entry->values[i]), i);
+ }
+}
+
+void init_value_state (Value_State *val, int seq_num)
+{
+ memset (val, 0, sizeof (*val));
+ val->value_index = seq_num;
+ val->present = 1;
+ val->delete_csn = NOT_PRESENT;
+ if (seq_num > 0) /* only first value is distinguished */
+ val->distinguished_csn = -1;
+}
+
+void apply_operation (Entry_State *entry, Operation *op)
+{
+ switch (op->type)
+ {
+ case OP_ADD_VALUE: apply_add_operation (entry, op);
+ break;
+
+ case OP_DELETE_VALUE: apply_value_delete_operation (entry, op);
+ break;
+
+ case OP_DELETE_ATTR: apply_attr_delete_operation (entry, op);
+ break;
+
+ case OP_RENAME_ENTRY: apply_rename_operation (entry, op);
+ break;
+ }
+
+ if (sim.verbose)
+ {
+ fprintf (sim.fout, "operation: ");
+ dump_operation (op);
+ fprintf (sim.fout, "\n");
+ dump_entry_state (entry);
+ }
+}
+
+void free_operations (Operation **ops)
+{
+ free (*ops);
+ *ops = NULL;
+}
+
+int **new_perm_table (int op_count)
+{
+ int i;
+ int **perm_table;
+ int perm_count = get_perm_count (op_count);
+
+ perm_table = (int**)malloc (perm_count * sizeof (int*));
+ for (i = 0; i < perm_count; i ++)
+ perm_table [i] = (int*) malloc (op_count * sizeof (int));
+
+ return perm_table;
+}
+
+void free_perm_table (int ***perm_table, int op_count)
+{
+ int i;
+ int perm_count = get_perm_count (op_count);
+
+ for (i = 0; i < perm_count; i ++)
+ free ((*perm_table)[i]);
+
+ free (*perm_table);
+ *perm_table = NULL;
+}
+
+void generate_perm_table (int *elements, int element_count, int static_part,
+ int **perm_table)
+{
+ int i;
+ int elements_copy [MAX_OPS];
+ int start_pos;
+
+ if (element_count - 1 == static_part)
+ {
+ memcpy (*perm_table, elements, element_count * sizeof (int));
+ return;
+ }
+
+ start_pos = 0;
+ for (i = 0; i < element_count - static_part; i ++)
+ {
+ memcpy (elements_copy, elements, element_count * sizeof (int));
+ elements_copy [static_part] = elements [static_part + i];
+ elements_copy [static_part + i] = elements [static_part];
+ generate_perm_table (elements_copy, element_count, static_part + 1,
+ &perm_table [start_pos]);
+ start_pos += get_perm_count (element_count - static_part - 1);
+ }
+}
+
+int get_perm_count (int op_count)
+{
+ int i;
+ int perm_count = 1;
+
+ for (i = 2; i <= op_count; i ++)
+ perm_count *= i;
+
+ return perm_count;
+}
+
+void apply_add_operation (Entry_State *entry, Operation *op)
+{
+ if (entry->values[op->value_index].presense_csn < op->csn)
+ {
+ entry->values[op->value_index].presense_csn = op->csn;
+ entry->values[op->value_index].present = 1;
+ resolve_value_state (entry, op->value_index);
+ }
+}
+
+void apply_value_delete_operation (Entry_State *entry, Operation *op)
+{
+ if (entry->values[op->value_index].delete_csn < op->csn)
+ {
+ entry->values[op->value_index].delete_csn = op->csn;
+ resolve_value_state (entry, op->value_index);
+ }
+}
+
+void apply_attr_delete_operation (Entry_State *entry, Operation *op)
+{
+ int i;
+
+ if (entry->attr_delete_csn < op->csn)
+ {
+ entry->attr_delete_csn = op->csn;
+
+ for (i = 0; i < sim.value_count; i++)
+ {
+ resolve_value_state (entry, i);
+ }
+ }
+}
+
+void apply_rename_operation (Entry_State *entry, Operation *op)
+{
+ if (entry->dn_csn < op->csn)
+ {
+ entry->dn_index = op->value_index;
+ entry->dn_csn = op->csn;
+ }
+
+ make_value_non_distinguished (op->csn, entry, op->old_dn_index);
+ make_value_distinguished (op->csn, entry, op->value_index);
+}
+
+void make_value_distinguished (int op_csn, Entry_State *entry, int value_index)
+{
+ Value_State *value = &(entry->values[value_index]);
+
+ if (value->distinguished_csn < op_csn)
+ {
+ value->distinguished_csn = op_csn;
+
+ if (value->presense_csn < op_csn)
+ {
+ value->present = 1;
+ value->presense_csn = op_csn;
+ }
+
+ resolve_value_state (entry, value_index);
+ }
+}
+
+void make_value_non_distinguished (int op_csn, Entry_State *entry, int value_index)
+{
+ int i = 0;
+ int index;
+ Value_State *value = &(entry->values[value_index]);
+
+ if (op_csn < value->distinguished_csn)
+ return;
+
+ /* insert into sorted list */
+ while (value->non_distinguished_csns[i] && value->non_distinguished_csns[i] < op_csn)
+ i++;
+
+ if (value->non_distinguished_csns[i] == 0)
+ value->non_distinguished_csns[i] = op_csn;
+ else
+ {
+ index = i;
+
+ while (value->non_distinguished_csns[i])
+ i++;
+
+ memcpy (&(value->non_distinguished_csns[index + 1]),
+ &(value->non_distinguished_csns[index]), (i - index) * sizeof (int));
+
+ value->non_distinguished_csns[index] = op_csn;
+ }
+
+ resolve_value_state (entry, value_index);
+}
+
+void purge_value_state (Value_State *value)
+{
+ /* value state information can be purged once a value was
+ readed/made distinguished because at that point we know that the value
+ existed/was distinguished */
+
+ purge_non_distinguished_csns (value);
+
+ if (value->delete_csn < max_val (value->distinguished_csn, value->presense_csn))
+ value->delete_csn = NOT_PRESENT;
+}
+
+void purge_non_distinguished_csns (Value_State *value)
+{
+ int i = 0;
+ int index;
+
+ while (value->non_distinguished_csns[i] &&
+ value->non_distinguished_csns[i] < value->distinguished_csn)
+ i ++;
+
+ if (i > 0)
+ {
+ index = i-1;
+ while (value->non_distinguished_csns[i])
+ i ++;
+
+ if (i > index + 1)
+ {
+ memcpy (value->non_distinguished_csns, &value->non_distinguished_csns[index+1],
+ (i - index - 1) * sizeof (int));
+ memset (&(value->non_distinguished_csns[index+1]), 0, sizeof (int) * (i - index - 1));
+ }
+ else
+ {
+ memset (value->non_distinguished_csns, 0, sizeof (int) * i);
+ }
+ }
+}
+
+int is_list_empty (int *list)
+{
+ return (list[0] == 0);
+}
+
+int min_list_val (int *list)
+{
+ return (list [0]);
+}
+
+void resolve_value_state (Entry_State *entry, int value_index)
+{
+ Value_State *value = &(entry->values[value_index]);
+
+ purge_value_state (value);
+
+ /* no deletes that effect the state */
+ if (max_val (value->delete_csn, entry->attr_delete_csn) <
+ max_val (value->distinguished_csn, value->presense_csn))
+ return;
+
+ if (value->present) /* check if it should be removed based on the current state */
+ {
+ if (!value_distinguished_at_delete (value, entry->attr_delete_csn))
+ {
+ /* note that we keep presence csn because we might have to restore
+ the value in the future */
+ value->present = 0;
+ }
+ }
+ else /* not present - check if it should be restored */
+ {
+ if (value_distinguished_at_delete (value, entry->attr_delete_csn))
+ {
+ value->present = 1;
+ }
+ }
+}
+
+/* Note we can't trim distinguished_csn (even during regular trimming)
+ because in some cases we would not be able to figure out whether
+ a value was distinguished or not at the time of deletion.
+
+ Example 1: Example2:
+ csn order operation csn order operation
+ 1 1 make V distinguished 1 1 make V distinguished
+ 3 3 delete V 2 2 make V non distinguished
+ 4 2 make V non-distinguished 3 4 delete V
+ 4 3 make V non distinguished (on another server)
+
+ if the csns up to 2 were triimed, when delete operation is received, the state
+ is exactly the same in both examples but in example one delete should not go
+ through while in example 2 it should
+
+ */
+int value_distinguished_at_delete (Value_State *value, int attr_delete_csn)
+{
+ if (value->distinguished_csn >= 0 &&
+ (is_list_empty (value->non_distinguished_csns) ||
+ min_list_val (value->non_distinguished_csns) >
+ max_val (value->delete_csn, attr_delete_csn)))
+ return 1;
+ else
+ return 0;
+}
+
+int compare_entry_state (Entry_State *entry1, Entry_State *entry2, int run)
+{
+ int j;
+ int error = 0;
+
+ /* first - quick check for present / not present */
+ for (j = 0; j < sim.value_count; j++)
+ {
+ if (entry1->values[j].present != entry2->values[j].present)
+ {
+ fprintf (sim.fout,
+ "value %s is %s present in the first run but %s present in the %d run\n",
+ g_values[j], entry1->values[j].present ? "" : "not",
+ entry2->values[j].present ? "" : "not", run);
+ error = 1;
+ }
+ }
+
+ if (error)
+ return 3;
+
+ /* compare value state */
+ error = 0;
+ if (entry1->attr_delete_csn != entry2->attr_delete_csn)
+ {
+ fprintf (sim.fout, "attribute delete csn is %d for run 1 "
+ "but is %d for run %d\n", entry1->attr_delete_csn,
+ entry2->attr_delete_csn, run);
+ error = 1;
+ }
+
+ for (j = 0; j < sim.value_count; j++)
+ {
+ if (entry1->values[j].presense_csn != entry2->values[j].presense_csn)
+ {
+ fprintf (sim.fout, "presence csn for value %s is %d in run 1 "
+ "but is %d in run %d\n", g_values[j], entry1->values[j].presense_csn,
+ entry2->values[j].presense_csn, run);
+ error = 1;
+ }
+
+ if (entry1->values[j].distinguished_csn != entry2->values[j].distinguished_csn)
+ {
+ fprintf (sim.fout, "distinguished csn for value %s is %d in run 1 "
+ "but is %d in run %d\n", g_values[j], entry1->values[j].distinguished_csn,
+ entry2->values[j].distinguished_csn, run);
+ error = 1;
+ }
+
+ if (entry1->values[j].delete_csn != entry2->values[j].delete_csn)
+ {
+ fprintf (sim.fout, "delete csn for value %s is %d in run 1 "
+ "but is %d in run %d\n", g_values[j], entry1->values[j].delete_csn,
+ entry2->values[j].delete_csn, run);
+ error = 1;
+ }
+
+ if (!list_equal (entry1->values[j].non_distinguished_csns,
+ entry2->values[j].non_distinguished_csns))
+ {
+ fprintf (sim.fout, "pending list mismatch for valye %s in runs 1 and %d\n",
+ g_values[j], run);
+ dump_list (entry1->values[j].non_distinguished_csns);
+ dump_list (entry2->values[j].non_distinguished_csns);
+ }
+ }
+
+ if (error != 0)
+ {
+ return 1;
+ }
+ else
+ return 0;
+}
+
+void dump_operations (Operation *ops, int op_count, int *order)
+{
+ int index;
+ int i;
+
+ for (i = 0; i < op_count; i ++)
+ {
+ if (order == NULL) /* current order */
+ index = i;
+ else
+ index = order [i];
+
+ dump_operation (&ops[index]);
+ }
+
+ fprintf (sim.fout, "\n");
+}
+
+void dump_operation (Operation *op)
+{
+ switch (op->type)
+ {
+ case OP_ADD_VALUE:
+ fprintf (sim.fout, "\t%d add value %s\n", op->csn,
+ g_values [op->value_index]);
+ break;
+ case OP_DELETE_VALUE:
+ fprintf (sim.fout, "\t%d delete value %s\n", op->csn,
+ g_values [op->value_index]);
+ break;
+ case OP_DELETE_ATTR:
+ fprintf (sim.fout, "\t%d delete attribute\n", op->csn);
+ break;
+ case OP_RENAME_ENTRY:
+ fprintf (sim.fout, "\t%d rename entry from %s to %s\n", op->csn,
+ g_values [op->old_dn_index], g_values [op->value_index]);
+ break;
+ }
+}
+
+void dump_perm_table (int **perm_table, int op_count)
+{
+ int i, j;
+ int perm_count = get_perm_count (op_count);
+
+ for (i = 0; i < op_count; i++)
+ {
+ for (j = 0; j < perm_count; j++)
+ {
+ fprintf (sim.fout, "%d ", perm_table [j][i]);
+ }
+
+ fprintf (sim.fout, "\n");
+ }
+}
+
+void dump_entry_state (Entry_State *entry)
+{
+ int i;
+ fprintf (sim.fout, "\tentry dn: %s; dn csn - %d\n",
+ g_values [entry->dn_index], entry->dn_csn);
+
+ if (sim.verbose)
+ fprintf (sim.fout, "\n");
+
+ for (i = 0; i < sim.value_count; i++)
+ {
+ dump_value_state (&(entry->values[i]));
+ }
+
+ fprintf (sim.fout, "\n");
+}
+
+void dump_value_state (Value_State *value)
+{
+ fprintf (sim.fout, "\tvalue %s is %s\n", g_values[value->value_index],
+ value->present ? "present" : "not present");
+
+ if (sim.verbose)
+ {
+ fprintf (sim.fout, "\t\tpresent csn: %d\n", value->presense_csn);
+ fprintf (sim.fout, "\t\tdistinguished csn: %d\n", value->distinguished_csn);
+ fprintf (sim.fout, "\t\tdelete value csn: %d\n", value->delete_csn);
+ fprintf (sim.fout, "\t\tnon distinguished csns: ");
+
+ dump_list (value->non_distinguished_csns);
+
+ fprintf (sim.fout, "\n");
+ }
+}
+
+void dump_list (int *list)
+{
+ int i = 0;
+
+ while (list[i])
+ {
+ fprintf (sim.fout, "%d ", list[i]);
+ i ++;
+ }
+
+ fprintf (sim.fout, "\n");
+}
+
+/* misc functions */
+int max_val (int a, int b)
+{
+ if (a >= b)
+ return a;
+ else
+ return b;
+}
+
+int list_equal (int *list1, int *list2)
+{
+ int i;
+
+ i = 0;
+ while (list1[i] && list2[i])
+ {
+ if (list1[i] != list2[i])
+ return 0;
+
+ i ++;
+ }
+
+ if (list1[i] != list2[i])
+ return 0;
+ else
+ return 1;
+}
diff --git a/ldap/servers/plugins/replication/tests/dnp_sim2.c b/ldap/servers/plugins/replication/tests/dnp_sim2.c
new file mode 100644
index 00000000..e1838aa6
--- /dev/null
+++ b/ldap/servers/plugins/replication/tests/dnp_sim2.c
@@ -0,0 +1,972 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+/* dnp_simulation.c - this file varifies the correctness of dnp algorithm
+ by generating random sequences of operations, applying
+ the algorithm and outputing the result
+
+ usage: dnp_sim [-h] [-n <number of simulations> ] [-v] [-f <output file>]
+ -h - print usage information.
+ -n <number of simulations> - how many simulations to perform; default - 1.
+ -v - verbose mode (prints full entry state after each operation execution)
+ -f <output file> - file where results are stored; by default results are
+ printed to the screen.
+ -o <op file> - file that contains operation sequence to execute; by default,
+ random sequence is generated.
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <errno.h>
+#include <memory.h>
+#include <string.h>
+#include <time.h>
+#include <windows.h>
+
+#define MAX_OPS 18 /* maximum number of operations in a simulation */
+#define MAX_VALS 10 /* maximum number of values is entry or dn */
+#define NOT_PRESENT -1
+
+/* data types */
+typedef struct value_state
+{
+ int value_index; /* value */
+ int presence_csn; /* last time at which we know the value was present */
+ int distinguished_index; /* index into dncsn list */
+ int delete_csn; /* last attempt to delete this value */
+ int present; /* flag that tells whether the value iscurrently present */
+} Value_State;
+
+typedef struct dn_csn
+{
+ int csn; /* dn csn */
+ int value_index; /* dn value */
+} Dn_Csn;
+
+typedef struct entry_state
+{
+ Dn_Csn dn_csns [MAX_VALS + 1]; /* list of dn csns for this entry */
+ int dn_csn_count; /* csn of the current dn */
+ Value_State values[MAX_VALS]; /* values of the attribute */
+ int attr_delete_csn; /* last attempt to delete the entire attribute */
+} Entry_State;
+
+typedef enum
+{
+ OP_ADD_VALUE,
+ OP_DELETE_VALUE,
+ OP_RENAME_ENTRY,
+ OP_DELETE_ATTR,
+ OP_END
+} Operation_Type;
+
+typedef struct operation
+{
+ Operation_Type type; /* operation type */
+ int csn; /* operation type */
+ int value_index; /* value to add, remove or rename from */
+}Operation;
+
+typedef struct simulator_params
+{
+ int runs; /* number of runs */
+ FILE *fout; /* output file */
+ int value_count; /* number of values */
+ int verbose; /* verbose mode */
+ Operation *ops; /* operation sequence to apply */
+ int op_count;
+}Simulator_Params;
+
+
+/* gloabl data */
+Simulator_Params sim;
+char *g_values[] =
+{
+ "v",
+ "u",
+ "w",
+ NULL
+};
+
+/* forward declarations */
+
+/* initialization */
+void process_cmd (int argc, char **argv);
+void set_default_sim_params ();
+int count_values ();
+void parse_operations_file (char *name);
+void parse_operation (char *line, int pos);
+int value2index (char *value);
+void print_usage ();
+
+/* simulation run */
+void run_simulation ();
+void generate_operations (Operation **ops, int *op_count);
+void generate_operation (Operation *op, int csn);
+int* generate_operation_order (int op_count, int seq_num);
+void apply_operation_sequence (Operation *ops, int op_count, int *order, Entry_State *entry);
+void init_entry_state (Entry_State *entry);
+void init_value_state (Value_State *val, int seq_num);
+void apply_operation (Entry_State *entry, Operation *op);
+void free_operations (Operation **ops);
+int ** new_perm_table (int op_count);
+void free_perm_table (int ***perm_table, int op_count);
+int get_perm_count (int op_count);
+void generate_perm_table (int *elements, int element_count, int static_part,
+ int **perm_table);
+void apply_add_operation (Entry_State *entry, Operation *op);
+void apply_value_delete_operation (Entry_State *entry, Operation *op);
+void apply_attr_delete_operation (Entry_State *entry, Operation *op);
+void apply_rename_operation (Entry_State *entry, Operation *op);
+void purge_value_state (Entry_State *entry, int index);
+void resolve_value_state (Entry_State *entry, int value_index);
+int value_distinguished_at_delete (Entry_State *entry, int value_index);
+int compare_entry_state (Entry_State *entry1, Entry_State *entry2, int run);
+
+/* dnc_csn handling */
+int dn_csn_add (Entry_State *entry, int value_index, int csn);
+int get_value_dn_csn (Entry_State *entry, int value_index);
+
+/* data tracing */
+void dump_operations (Operation *ops, int op_count, int *order);
+void dump_operation (Operation *op);
+void dump_perm_table (int **perm_table, int op_count);
+void dump_entry_state (Entry_State *entry);
+void dump_value_state (Value_State *value);
+void dump_dn_csn_list (Entry_State *entry);
+
+/* misc functions */
+int max_val (int a, int b);
+
+int main (int argc, char **argv)
+{
+ int i;
+
+ process_cmd (argc, argv);
+
+ for (i = 0; i < sim.runs; i++)
+ {
+ fprintf (sim.fout, "*******running simulation #%d ...\n\n", i+1);
+ run_simulation ();
+ fprintf (sim.fout, "\n*******done with simulation #%d ...\n\n", i+1);
+ }
+
+ if (sim.fout != stdout)
+ fclose (sim.fout);
+
+ return 0;
+}
+
+void process_cmd (int argc, char **argv)
+{
+ int i;
+
+ set_default_sim_params ();
+
+ if (argc == 1)
+ {
+ return;
+ }
+
+ if (strcmp (argv[1], "-h") == 0) /* print help */
+ {
+ print_usage ();
+ exit (0);
+ }
+
+ i = 1;
+ while (i < argc)
+ {
+ if (strcmp (argv[i], "-v") == 0) /* verbose mode */
+ {
+ sim.verbose = 1;
+ i ++;
+ }
+ else if (strcmp (argv[i], "-n") == 0)
+ {
+ if (i < argc - 1)
+ {
+ int runs = atoi (argv[i + 1]);
+ if (runs > 0)
+ sim.runs = runs;
+ i+=2;
+ }
+ else
+ {
+ /* ONREPL print warning */
+ i++;
+ }
+ }
+ else if (strcmp (argv[i], "-f") == 0) /* output file */
+ {
+ if (i < argc - 1)
+ {
+ FILE *f = fopen (argv[i + 1], "w");
+ if (f == 0)
+ {
+ printf ("failed to open output file; error - %s, using stdout\n",
+ strerror(errno));
+ }
+ else
+ {
+ /* ONREPL print warning */
+ sim.fout = f;
+ }
+
+ i += 2;
+ }
+ else
+ i++;
+ }
+ else if (strcmp (argv[i], "-o") == 0) /* file with operation sequence */
+ {
+ if (i < argc - 1)
+ {
+ parse_operations_file (argv[i+1]);
+ i += 2;
+ }
+ else
+ {
+ /* ONREPL print warning */
+ i ++;
+ }
+ }
+ else /* unknown option */
+ {
+ printf ("unknown option - %s; ignored\n", argv[i]);
+ i ++;
+ }
+
+ }
+}
+
+void set_default_sim_params ()
+{
+ memset (&sim, 0, sizeof (sim));
+ sim.runs = 1;
+ sim.fout = stdout;
+ sim.value_count = count_values ();
+}
+
+/* file format: <op count>
+ add <value>
+ delete <value>
+ delete attribute
+ rename to <value>
+
+ all spaces are significant
+ */
+void parse_operations_file (char *name)
+{
+ FILE *file = fopen (name, "r");
+ char line [256];
+ int i;
+
+ if (file == NULL)
+ {
+ printf ("failed to open operations file %s: error = %d\n", name, errno);
+ print_usage ();
+ exit (1);
+ }
+
+ i = 0;
+ while (fgets (line, sizeof (line), file))
+ {
+ if (i == 0)
+ {
+ /* read operation count */
+ sim.op_count = atoi (line);
+ if (sim.op_count < 1 || sim.op_count > MAX_OPS/2)
+ {
+ printf ("invalid operation count - %d; value must be between 1 and %d\n",
+ sim.op_count, MAX_OPS/2);
+ print_usage ();
+ exit (1);
+ }
+ else
+ {
+ sim.ops = (Operation*)malloc (sim.op_count * sizeof (Operation));
+ }
+ }
+ else
+ {
+ if (strlen (line) == 0) /* skip empty lines */
+ continue;
+ parse_operation (line, i);
+ }
+
+ i ++;
+ }
+}
+
+void parse_operation (char *line, int i)
+{
+ sim.ops [i - 1].csn = i;
+
+ if (line[strlen(line) - 1] == '\n')
+ line[strlen(line) - 1] = '\0';
+
+ if (strncmp (line, "add", 3) == 0)
+ {
+ sim.ops [i - 1].type = OP_ADD_VALUE;
+ sim.ops [i - 1].value_index = value2index (&line[4]);
+ }
+ else if (strncmp (line, "delete attribute", 16) == 0)
+ {
+ sim.ops [i - 1].type = OP_DELETE_ATTR;
+ }
+ else if (strncmp (line, "delete", 6) == 0)
+ {
+ sim.ops [i - 1].type = OP_DELETE_VALUE;
+ sim.ops [i - 1].value_index = value2index (&line[7]);
+ }
+ else if (strncmp (line, "rename to", 6) == 0)
+ {
+ sim.ops [i - 1].type = OP_RENAME_ENTRY;
+ sim.ops [i - 1].value_index = value2index (&line[10]);
+ }
+ else
+ {
+ /* invalid line */
+ printf ("invalid operation: %s\n", line);
+ exit (1);
+ }
+}
+
+int value2index (char *value)
+{
+ int i;
+
+ for (i = 0; i < sim.value_count; i++)
+ {
+ if (strcmp (g_values[i], value) == 0)
+ return i;
+ }
+
+ return -1;
+}
+
+void print_usage ()
+{
+ printf ("usage: dnp_sim [-h] [-n <number of simulations> ] [-v] [-f <output file>]\n"
+ "\t-h - print usage information\n"
+ "\t-n <number of simulations>; default - 1\n"
+ "\t-v - verbose mode\n"
+ "\t-f <output file> - by default, results are printed to the screen\n"
+ "\t-o <op file> - file that contains operation sequence to execute;\n"
+ "\tby default, random sequence is generated.\n");
+}
+
+int count_values ()
+{
+ int i;
+
+ for (i = 0; g_values[i]; i++);
+
+ return i;
+}
+
+void run_simulation ()
+{
+ int *order;
+ int i;
+ int perm_count;
+ Entry_State entry_first, entry_current;
+ int error = 0;
+
+ init_entry_state (&entry_first);
+ fprintf (sim.fout, "initial entry state :\n");
+ dump_entry_state (&entry_first);
+
+ if (sim.ops == NULL)
+ {
+ generate_operations (&sim.ops, &sim.op_count);
+ }
+ fprintf (sim.fout, "initial operation set:\n");
+ dump_operations (sim.ops, sim.op_count, NULL/* order */);
+
+ //DebugBreak ();
+
+ perm_count = get_perm_count (sim.op_count);
+ for (i = 0; i < perm_count; i++)
+ {
+ fprintf (sim.fout, "--------------------------------\n");
+ fprintf (sim.fout, "simulation run %d\n", i + 1);
+ fprintf (sim.fout, "--------------------------------\n");
+ order = generate_operation_order (sim.op_count, i);
+ if (i == 0)
+ apply_operation_sequence (sim.ops, sim.op_count, order, &entry_first);
+ else
+ {
+ apply_operation_sequence (sim.ops, sim.op_count, order, &entry_current);
+ error |= compare_entry_state (&entry_first, &entry_current, i + 1);
+ }
+ }
+
+ switch (error)
+ {
+ case 0: fprintf (sim.fout, "all runs left the entry in the same state\n");
+ break;
+ case 1: fprintf (sim.fout, "while value presence is consistent across all runs, "
+ "the exact state does not match\n");
+ break;
+ case 3: fprintf (sim.fout, "the runs left entries in an inconsistent state\n");
+ break;
+ }
+
+ free_operations (&sim.ops);
+}
+
+void generate_operations (Operation **ops, int *op_count)
+{
+ int i;
+
+ /* generate number operations in the sequence */
+ *op_count = slapi_rand () % (MAX_OPS / 2) + 1;
+ *ops = (Operation *)malloc (*op_count * sizeof (Operation));
+
+ for (i = 0; i < *op_count; i ++)
+ {
+ generate_operation (&((*ops)[i]), i + 1);
+ }
+}
+
+void generate_operation (Operation *op, int csn)
+{
+ /* generate operation type */
+ op->type = slapi_rand () % OP_END;
+
+ /* generate value to which operation applies */
+ op->value_index = slapi_rand () % sim.value_count;
+
+ op->csn = csn;
+}
+
+int* generate_operation_order (int op_count, int seq_num)
+{
+ static int **perm_table = NULL;
+
+ /* first request - generate pemutation table */
+ if (seq_num == 0)
+ {
+ int elements [MAX_OPS];
+ int i;
+
+ if (perm_table)
+ free_perm_table (&perm_table, op_count);
+ perm_table = new_perm_table (op_count);
+
+ for (i = 0; i < op_count; i++)
+ elements [i] = i;
+
+ generate_perm_table (elements, op_count, 0 /* static part */,
+ perm_table);
+ }
+
+ return perm_table [seq_num];
+}
+
+void apply_operation_sequence (Operation *ops, int op_count, int *order, Entry_State *entry)
+{
+ int i;
+
+ init_entry_state (entry);
+
+ if (!sim.verbose)
+ {
+ if (!sim.verbose)
+ {
+ fprintf (sim.fout, "operation_sequence for this run:\n");
+ dump_operations (ops, op_count, order);
+ }
+ }
+
+ for (i = 0; i < op_count; i++)
+ {
+ apply_operation (entry, &(ops [order[i]]));
+ }
+
+ if (!sim.verbose)
+ {
+ fprintf (sim.fout, "final entry state :\n");
+ dump_entry_state (entry);
+ }
+
+}
+
+void init_entry_state (Entry_State *entry)
+{
+ int i;
+
+ memset (entry, 0, sizeof (*entry));
+ entry->attr_delete_csn = NOT_PRESENT;
+ entry->dn_csn_count = 1;
+
+ for (i = 0; i < sim.value_count; i++)
+ {
+ init_value_state (&(entry->values[i]), i);
+ }
+}
+
+void init_value_state (Value_State *val, int seq_num)
+{
+ memset (val, 0, sizeof (*val));
+ val->value_index = seq_num;
+ val->present = 1;
+ val->delete_csn = NOT_PRESENT;
+ if (seq_num > 0) /* only first value is distinguished */
+ val->distinguished_index = -1;
+}
+
+void apply_operation (Entry_State *entry, Operation *op)
+{
+ switch (op->type)
+ {
+ case OP_ADD_VALUE: apply_add_operation (entry, op);
+ break;
+
+ case OP_DELETE_VALUE: apply_value_delete_operation (entry, op);
+ break;
+
+ case OP_DELETE_ATTR: apply_attr_delete_operation (entry, op);
+ break;
+
+ case OP_RENAME_ENTRY: apply_rename_operation (entry, op);
+ break;
+ }
+
+ if (sim.verbose)
+ {
+ fprintf (sim.fout, "operation: ");
+ dump_operation (op);
+ fprintf (sim.fout, "\n");
+ dump_entry_state (entry);
+ }
+}
+
+void free_operations (Operation **ops)
+{
+ free (*ops);
+ *ops = NULL;
+}
+
+int **new_perm_table (int op_count)
+{
+ int i;
+ int **perm_table;
+ int perm_count = get_perm_count (op_count);
+
+ perm_table = (int**)malloc (perm_count * sizeof (int*));
+ for (i = 0; i < perm_count; i ++)
+ perm_table [i] = (int*) malloc (op_count * sizeof (int));
+
+ return perm_table;
+}
+
+void free_perm_table (int ***perm_table, int op_count)
+{
+ int i;
+ int perm_count = get_perm_count (op_count);
+
+ for (i = 0; i < perm_count; i ++)
+ free ((*perm_table)[i]);
+
+ free (*perm_table);
+ *perm_table = NULL;
+}
+
+void generate_perm_table (int *elements, int element_count, int static_part,
+ int **perm_table)
+{
+ int i;
+ int elements_copy [MAX_OPS];
+ int start_pos;
+
+ if (element_count - 1 == static_part)
+ {
+ memcpy (*perm_table, elements, element_count * sizeof (int));
+ return;
+ }
+
+ start_pos = 0;
+ for (i = 0; i < element_count - static_part; i ++)
+ {
+ memcpy (elements_copy, elements, element_count * sizeof (int));
+ elements_copy [static_part] = elements [static_part + i];
+ elements_copy [static_part + i] = elements [static_part];
+ generate_perm_table (elements_copy, element_count, static_part + 1,
+ &perm_table [start_pos]);
+ start_pos += get_perm_count (element_count - static_part - 1);
+ }
+}
+
+int get_perm_count (int op_count)
+{
+ int i;
+ int perm_count = 1;
+
+ for (i = 2; i <= op_count; i ++)
+ perm_count *= i;
+
+ return perm_count;
+}
+
+void apply_add_operation (Entry_State *entry, Operation *op)
+{
+ if (entry->values[op->value_index].presence_csn < op->csn)
+ {
+ entry->values[op->value_index].presence_csn = op->csn;
+ resolve_value_state (entry, op->value_index);
+ }
+}
+
+void apply_value_delete_operation (Entry_State *entry, Operation *op)
+{
+ if (entry->values[op->value_index].delete_csn < op->csn)
+ {
+ entry->values[op->value_index].delete_csn = op->csn;
+ resolve_value_state (entry, op->value_index);
+ }
+}
+
+void apply_attr_delete_operation (Entry_State *entry, Operation *op)
+{
+ int i;
+
+ if (entry->attr_delete_csn < op->csn)
+ {
+ entry->attr_delete_csn = op->csn;
+
+ for (i = 0; i < sim.value_count; i++)
+ {
+ resolve_value_state (entry, i);
+ }
+ }
+}
+
+void apply_rename_operation (Entry_State *entry, Operation *op)
+{
+ int index;
+
+ if (entry->values[op->value_index].presence_csn == NOT_PRESENT)
+ entry->values[op->value_index].presence_csn = op->csn;
+
+ index = dn_csn_add (entry, op->value_index, op->csn);
+
+ if (index > 0)
+ resolve_value_state (entry, entry->dn_csns[index - 1].value_index);
+
+ resolve_value_state (entry, entry->dn_csns[index].value_index);
+}
+
+void purge_value_state (Entry_State *entry, int value_index)
+{
+ Value_State *value = &(entry->values[value_index]);
+ int value_distinguished_csn = get_value_dn_csn (entry, value_index);
+
+ if (value_distinguished_csn == -1 && value->presence_csn > value->delete_csn)
+ value->delete_csn = NOT_PRESENT;
+ else if (value->delete_csn < max_val (value_distinguished_csn, value->presence_csn))
+ value->delete_csn = NOT_PRESENT;
+}
+
+void resolve_value_state (Entry_State *entry, int value_index)
+{
+ Value_State *value = &(entry->values[value_index]);
+ int value_distinguished_csn = get_value_dn_csn (entry, value_index);
+
+ purge_value_state (entry, value_index);
+
+ /* no deletes that effect the state */
+ if (max_val (value->delete_csn, entry->attr_delete_csn) <
+ max_val (value_distinguished_csn, value->presence_csn))
+ {
+ value->present = 1;
+ return;
+ }
+
+ if (value->present) /* check if it should be removed based on the current state */
+ {
+ if (!value_distinguished_at_delete (entry, value_index))
+ {
+ value->present = 0;
+ }
+ }
+ else /* not present - check if it should be restored */
+ {
+ if (value_distinguished_at_delete (entry, value_index))
+ {
+ value->present = 1;
+ }
+ }
+}
+
+int value_distinguished_at_delete (Entry_State *entry, int value_index)
+{
+ Value_State *value = &(entry->values[value_index]);
+ int value_distinguished_csn = get_value_dn_csn (entry, value_index);
+ int delete_csn;
+ int i;
+
+ /* value has never been distinguished */
+ if (value_distinguished_csn == -1)
+ return 0;
+
+ delete_csn = max_val (entry->attr_delete_csn, value->delete_csn);
+
+ for (i = 0; i < entry->dn_csn_count; i++)
+ {
+ if (entry->dn_csns[i].csn > delete_csn)
+ break;
+ }
+
+ /* i is never equal to 0 because the csn of the first element is always
+ smaller than csn of any operation we can receive */
+ return (entry->dn_csns[i-1].value_index == value_index);
+}
+
+int compare_entry_state (Entry_State *entry1, Entry_State *entry2, int run)
+{
+ int i;
+ int error = 0;
+
+ /* first - quick check for present / not present */
+ for (i = 0; i < sim.value_count; i++)
+ {
+ if (entry1->values[i].present != entry2->values[i].present)
+ {
+ fprintf (sim.fout,
+ "value %s is %s present in the first run but %s present in the %d run\n",
+ g_values[i], entry1->values[i].present ? "" : "not",
+ entry2->values[i].present ? "" : "not", run);
+ error = 1;
+ }
+ }
+
+ if (error)
+ return 3;
+
+ /* compare dnc_csn list */
+ if (entry1->dn_csn_count != entry2->dn_csn_count)
+ {
+ fprintf (sim.fout, "dn_csn count is %d for run 1 and %d for run %d\n",
+ entry1->dn_csn_count, entry2->dn_csn_count, run);
+ error = 1;
+ }
+
+ for (i = 0; i < entry1->dn_csn_count; i++)
+ {
+ if (entry1->dn_csns [i].csn != entry2->dn_csns [i].csn ||
+ entry1->dn_csns [i].value_index != entry2->dn_csns [i].value_index)
+ {
+ fprintf (sim.fout,"elements %d of dn csn list are different:\n"
+ "\tfirst run: csn - %d, value - %s\n"
+ "\t%d run: csn - %d, value - %s\n", i,
+ entry1->dn_csns [i].csn,
+ g_values[entry1->dn_csns [i].value_index],
+ run, entry2->dn_csns [i].csn,
+ g_values[entry2->dn_csns [i].value_index]);
+
+ error = 1;
+ }
+ }
+
+ /* compare value state */
+ if (entry1->attr_delete_csn != entry2->attr_delete_csn)
+ {
+ fprintf (sim.fout, "attribute delete csn is %d for run 1 "
+ "but is %d for run %d\n", entry1->attr_delete_csn,
+ entry2->attr_delete_csn, run);
+ error = 1;
+ }
+
+ for (i = 0; i < sim.value_count; i++)
+ {
+ if (entry1->values[i].presence_csn != entry2->values[i].presence_csn)
+ {
+ fprintf (sim.fout, "presence csn for value %s is %d in run 1 "
+ "but is %d in run %d\n", g_values[i], entry1->values[i].presence_csn,
+ entry2->values[i].presence_csn, run);
+ error = 1;
+ }
+
+ if (entry1->values[i].distinguished_index != entry2->values[i].distinguished_index)
+ {
+ fprintf (sim.fout, "distinguished index for value %s is %d in run 1 "
+ "but is %d in run %d\n", g_values[i],
+ entry1->values[i].distinguished_index,
+ entry2->values[i].distinguished_index, run);
+ error = 1;
+ }
+
+ if (entry1->values[i].delete_csn != entry2->values[i].delete_csn)
+ {
+ fprintf (sim.fout, "delete csn for value %s is %d in run 1 "
+ "but is %d in run %d\n", g_values[i], entry1->values[i].delete_csn,
+ entry2->values[i].delete_csn, run);
+ error = 1;
+ }
+ }
+
+ if (error != 0)
+ {
+ return 1;
+ }
+ else
+ return 0;
+}
+
+int dn_csn_add (Entry_State *entry, int value_index, int csn)
+{
+ int i, j;
+ int distinguished_index;
+
+ for (i = 0; i < entry->dn_csn_count; i++)
+ {
+ if (entry->dn_csns[i].csn > csn)
+ break;
+ }
+
+ if (i < entry->dn_csn_count)
+ {
+ distinguished_index = i;
+ for (j = i; j < entry->dn_csn_count; j ++)
+ {
+ if (entry->dn_csns[j].value_index == value_index)
+ distinguished_index = j + 1;
+
+ if (entry->values [entry->dn_csns[j].value_index].distinguished_index == j)
+ entry->values [entry->dn_csns[j].value_index].distinguished_index ++;
+ }
+
+ memcpy (&(entry->dn_csns[i+1]), &(entry->dn_csns[i]),
+ (entry->dn_csn_count - i) * sizeof (Dn_Csn));
+ }
+ else
+ {
+ distinguished_index = entry->dn_csn_count;
+ }
+
+ entry->values[value_index].distinguished_index = distinguished_index;
+ entry->dn_csns[i].csn = csn;
+ entry->dn_csns[i].value_index = value_index;
+ entry->dn_csn_count ++;
+
+ return i;
+}
+
+int get_value_dn_csn (Entry_State *entry, int value_index)
+{
+ Value_State *value = &(entry->values [value_index]);
+
+ if (value->distinguished_index == -1)
+ return -1;
+ else
+ return entry->dn_csns [value->distinguished_index].csn;
+}
+
+void dump_operations (Operation *ops, int op_count, int *order)
+{
+ int index;
+ int i;
+
+ for (i = 0; i < op_count; i ++)
+ {
+ if (order == NULL) /* current order */
+ index = i;
+ else
+ index = order [i];
+
+ dump_operation (&ops[index]);
+ }
+
+ fprintf (sim.fout, "\n");
+}
+
+void dump_operation (Operation *op)
+{
+ switch (op->type)
+ {
+ case OP_ADD_VALUE:
+ fprintf (sim.fout, "\t%d add value %s\n", op->csn,
+ g_values [op->value_index]);
+ break;
+ case OP_DELETE_VALUE:
+ fprintf (sim.fout, "\t%d delete value %s\n", op->csn,
+ g_values [op->value_index]);
+ break;
+ case OP_DELETE_ATTR:
+ fprintf (sim.fout, "\t%d delete attribute\n", op->csn);
+ break;
+ case OP_RENAME_ENTRY:
+ fprintf (sim.fout, "\t%d rename entry to %s\n", op->csn,
+ g_values [op->value_index]);
+ break;
+ }
+}
+
+void dump_perm_table (int **perm_table, int op_count)
+{
+ int i, j;
+ int perm_count = get_perm_count (op_count);
+
+ for (i = 0; i < op_count; i++)
+ {
+ for (j = 0; j < perm_count; j++)
+ {
+ fprintf (sim.fout, "%d ", perm_table [j][i]);
+ }
+
+ fprintf (sim.fout, "\n");
+ }
+}
+
+void dump_entry_state (Entry_State *entry)
+{
+ int i;
+
+ dump_dn_csn_list (entry);
+
+ for (i = 0; i < sim.value_count; i++)
+ {
+ dump_value_state (&(entry->values[i]));
+ }
+
+ fprintf (sim.fout, "\n");
+}
+
+void dump_value_state (Value_State *value)
+{
+ fprintf (sim.fout, "\tvalue %s is %s\n", g_values[value->value_index],
+ value->present ? "present" : "not present");
+
+ if (sim.verbose)
+ {
+ fprintf (sim.fout, "\t\tpresent csn: %d\n", value->presence_csn);
+ fprintf (sim.fout, "\t\tdistinguished index: %d\n", value->distinguished_index);
+ fprintf (sim.fout, "\t\tdelete value csn: %d\n", value->delete_csn);
+ }
+}
+
+void dump_dn_csn_list (Entry_State *entry)
+{
+ int i;
+
+ fprintf (sim.fout, "\tdn csn list: \n");
+ for (i = 0; i < entry->dn_csn_count; i++)
+ {
+ fprintf (sim.fout, "\t\tvalue: %s, csn: %d\n",
+ g_values[entry->dn_csns[i].value_index], entry->dn_csns[i].csn);
+ }
+}
+
+/* misc functions */
+int max_val (int a, int b)
+{
+ if (a >= b)
+ return a;
+ else
+ return b;
+}
diff --git a/ldap/servers/plugins/replication/tests/dnp_sim3.c b/ldap/servers/plugins/replication/tests/dnp_sim3.c
new file mode 100644
index 00000000..d018597a
--- /dev/null
+++ b/ldap/servers/plugins/replication/tests/dnp_sim3.c
@@ -0,0 +1,1489 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+/* dnp_simulation.c - this file varifies the correctness of dnp algorithm
+ by generating random sequences of operations, applying
+ the algorithm and outputing the result
+
+ usage: dnp_sim [-h] [-n <number of simulations> ] [-v] [-f <output file>]
+ -h - print usage information.
+ -n <number of simulations> - how many simulations to perform; default - 1.
+ -v - verbose mode (prints full entry state after each operation execution)
+ -f <output file> - file where results are stored; by default results are
+ printed to the screen.
+ -o <op file> - file that contains operation sequence to execute; by default,
+ random sequence is generated.
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <errno.h>
+#include <memory.h>
+#include <string.h>
+#include <time.h>
+#include <windows.h>
+
+#define MAX_OPS 18 /* maximum number of operations in a simulation */
+#define MAX_VALS 10 /* maximum number of values is entry or dn */
+#define MAX_ATTR_NAME 16 /* max length of the attribute name */
+#define NOT_PRESENT -1
+#define SV_ATTR_NAME "sv_attr" /* name of the singlevalued attribute */
+#define MV_ATTR_NAME "mv_attr" /* name of the multivalued attribute */
+
+/* data types */
+
+/* value */
+typedef struct value_state
+{
+ int value_index; /* value */
+ int presence_csn; /* last time at which we know the value was present */
+ int delete_csn; /* last attempt to delete this value */
+ int present; /* flag that tells whether the value is present */
+} Value_State;
+
+/* shared attribute state */
+typedef struct attr_state
+{
+ int delete_csn; /* last deletion csn */
+ int present; /* flag that tells whether the attribute is present */
+}Attr_State;
+
+/* singlevalued attribute */
+typedef struct sv_attr_state
+{
+ Attr_State attr_state; /* shared attribute state */
+ Value_State current_value; /* current attribute value */
+ Value_State *pending_value; /* latest pending value */
+} SV_Attr_State;
+
+/* maltivalued attribute */
+typedef struct mv_attr_state
+{
+ Attr_State attr_state; /* shared attribute state */
+ Value_State values [MAX_VALS]; /* latest pending value */
+ int value_count; /* number of values in the array */
+} MV_Attr_State;
+
+/* node of dn_csn_list */
+typedef struct dn_csn
+{
+ int csn; /* dn csn */
+ int sv_attr; /* is this single valued or multivalued attr */
+ int value_index; /* dn value */
+} Dn_Csn;
+
+typedef struct entry_state
+{
+ Dn_Csn dn_csns [MAX_VALS + 1]; /* list of dn csns for this entry */
+ int dn_csn_count; /* csn of the current dn */
+ SV_Attr_State sv_attr; /* singlevalued attribute */
+ MV_Attr_State mv_attr; /* singlevalued attribute */
+} Entry_State;
+
+typedef enum
+{
+ OP_ADD_VALUE,
+ OP_DELETE_VALUE,
+ OP_RENAME_ENTRY,
+ OP_DELETE_ATTR,
+ OP_END
+} Operation_Type;
+
+typedef struct operation
+{
+ Operation_Type type; /* operation type */
+ int csn; /* operation csn */
+ int sv_attr; /* is this applied to singlevalued attribute */
+ int value_index; /* value to add, remove or rename from */
+ int delete_old_rdn; /* rename only */
+ int old_rdn_sv_attr; /* is oldrdn a singlevalued attribute */
+ int old_rdn_value_index; /* index of old_rdn */
+}Operation;
+
+typedef struct simulator_params
+{
+ int runs; /* number of runs */
+ FILE *fout; /* output file */
+ int value_count; /* number of values */
+ int verbose; /* verbose mode */
+ Operation *ops; /* operation sequence to apply */
+ int op_count;
+}Simulator_Params;
+
+
+/* gloabl data */
+Simulator_Params sim;
+char *g_values[] =
+{
+ "v",
+ "u",
+ "w",
+ NULL
+};
+
+/* forward declarations */
+
+/* initialization */
+void process_cmd (int argc, char **argv);
+void set_default_sim_params ();
+int count_values ();
+void parse_operations_file (char *name);
+void parse_operation (char *line, int pos);
+int value2index (char *value);
+void print_usage ();
+
+/* simulation run */
+void run_simulation ();
+void generate_operations (Operation **ops, int *op_count);
+void generate_operation (Operation *op, int csn);
+int* generate_operation_order (int op_count, int seq_num);
+void apply_operation_sequence (Operation *ops, int op_count, int *order, Entry_State *entry);
+void init_entry_state (Entry_State *entry);
+void init_sv_attr_state (SV_Attr_State *sv_attr);
+void init_mv_attr_state (MV_Attr_State *mv_attr);
+void init_value_state (Value_State *val, int seq_num);
+void free_operations (Operation **ops);
+int ** new_perm_table (int op_count);
+void free_perm_table (int ***perm_table, int op_count);
+int get_perm_count (int op_count);
+void generate_perm_table (int *elements, int element_count, int static_part,
+ int **perm_table);
+void apply_operation (Entry_State *entry, Operation *op);
+void apply_add_operation (Entry_State *entry, Operation *op);
+void apply_value_delete_operation (Entry_State *entry, Operation *op);
+void apply_attr_delete_operation (Entry_State *entry, Operation *op);
+void apply_rename_operation (Entry_State *entry, Operation *op);
+void resolve_mv_attr_state (Entry_State *entry, Value_State *value);
+void resolve_sv_attr_state (Entry_State *entry, Value_State *value);
+void purge_sv_attr_state (Entry_State *entry);
+void purge_mv_attr_state (Entry_State *entry, Value_State *value);
+int value_distinguished_at_csn (Entry_State *entry, int sv_attr, Value_State *value, int csn);
+
+/* state comparison */
+int compare_entry_state (Entry_State *entry1, Entry_State *entry2, int run);
+int compare_entry_state_quick (Entry_State *entry1, Entry_State *entry2, int run);
+int compare_sv_attr_state_quick (SV_Attr_State *sv_attr1, SV_Attr_State *sv_attr2, int run);
+int compare_mv_attr_state_quick (MV_Attr_State *mv_attr1, MV_Attr_State *mv_attr2, int run);
+int compare_sv_attr_state (SV_Attr_State *sv_attr1, SV_Attr_State *sv_attr2, int run);
+int compare_mv_attr_state (MV_Attr_State *mv_attr1, MV_Attr_State *mv_attr2, int run);
+int compare_value_state (Value_State *value1, Value_State *value2, int run);
+
+/* dnc_csn handling */
+int dn_csn_add (Entry_State *entry, int sv_attr, int value_index, int csn);
+
+/* data tracing */
+void dump_operations (Operation *ops, int op_count, int *order);
+void dump_operation (Operation *op);
+void dump_perm_table (int **perm_table, int op_count);
+void dump_entry_state (Entry_State *entry);
+void dump_sv_attr_state (SV_Attr_State *sv_attr);
+void dump_mv_attr_state (MV_Attr_State *mv_attr);
+void dump_value_state (Value_State *value, int sv_attr);
+void dump_dn_csn_list (Entry_State *entry);
+
+/* misc functions */
+int max_val (int a, int b);
+
+int main (int argc, char **argv)
+{
+ int i;
+
+ process_cmd (argc, argv);
+
+ for (i = 0; i < sim.runs; i++)
+ {
+ fprintf (sim.fout, "*******running simulation #%d ...\n\n", i+1);
+ run_simulation ();
+ fprintf (sim.fout, "\n*******done with simulation #%d ...\n\n", i+1);
+ }
+
+ if (sim.fout != stdout)
+ fclose (sim.fout);
+
+ return 0;
+}
+
+void process_cmd (int argc, char **argv)
+{
+ int i;
+
+ set_default_sim_params ();
+
+ if (argc == 1)
+ {
+ return;
+ }
+
+ if (strcmp (argv[1], "-h") == 0) /* print help */
+ {
+ print_usage ();
+ exit (0);
+ }
+
+ i = 1;
+ while (i < argc)
+ {
+ if (strcmp (argv[i], "-v") == 0) /* verbose mode */
+ {
+ sim.verbose = 1;
+ i ++;
+ }
+ else if (strcmp (argv[i], "-n") == 0)
+ {
+ if (i < argc - 1)
+ {
+ int runs = atoi (argv[i + 1]);
+ if (runs > 0)
+ sim.runs = runs;
+ i+=2;
+ }
+ else
+ {
+ /* ONREPL print warning */
+ i++;
+ }
+ }
+ else if (strcmp (argv[i], "-f") == 0) /* output file */
+ {
+ if (i < argc - 1)
+ {
+ FILE *f = fopen (argv[i + 1], "w");
+ if (f == 0)
+ {
+ printf ("failed to open output file; error - %s, using stdout\n",
+ strerror(errno));
+ }
+ else
+ {
+ /* ONREPL print warning */
+ sim.fout = f;
+ }
+
+ i += 2;
+ }
+ else
+ i++;
+ }
+ else if (strcmp (argv[i], "-o") == 0) /* file with operation sequence */
+ {
+ if (i < argc - 1)
+ {
+ parse_operations_file (argv[i+1]);
+ i += 2;
+ }
+ else
+ {
+ /* ONREPL print warning */
+ i ++;
+ }
+ }
+ else /* unknown option */
+ {
+ printf ("unknown option - %s; ignored\n", argv[i]);
+ i ++;
+ }
+
+ }
+}
+
+void set_default_sim_params ()
+{
+ memset (&sim, 0, sizeof (sim));
+ sim.runs = 1;
+ sim.fout = stdout;
+ sim.value_count = count_values ();
+}
+
+/* file format: <operation count>
+ add <attribute> <value>
+ delete <attribute>[ <value>]
+ rename to <attribute> <value>[ delete <attribute> <value>]
+
+ all spaces are significant
+ */
+void parse_operations_file (char *name)
+{
+ FILE *file = fopen (name, "r");
+ char line [256];
+ int i;
+
+ if (file == NULL)
+ {
+ printf ("failed to open operations file %s: error = %d\n", name, errno);
+ print_usage ();
+ exit (1);
+ }
+
+ i = 0;
+ while (fgets (line, sizeof (line), file))
+ {
+ if (i == 0)
+ {
+ /* read operation count */
+ sim.op_count = atoi (line);
+ if (sim.op_count < 1 || sim.op_count > MAX_OPS/2)
+ {
+ printf ("invalid operation count - %d; value must be between 1 and %d\n",
+ sim.op_count, MAX_OPS/2);
+ print_usage ();
+ exit (1);
+ }
+ else
+ {
+ sim.ops = (Operation*)malloc (sim.op_count * sizeof (Operation));
+ }
+ }
+ else
+ {
+ if (strlen (line) == 0) /* skip empty lines */
+ continue;
+ parse_operation (line, i);
+ }
+
+ i ++;
+ }
+}
+
+#define ADD_KEYWORD "add "
+#define DELETE_KEYWORD "delete "
+#define RENAME_KEYWORD "rename to "
+#define DELET_OLD_RDN_KEYWORD " delete "
+
+void parse_operation (char *line, int i)
+{
+ int rc = 0;
+ char *pos;
+ char buff [64];
+
+ sim.ops [i - 1].csn = i;
+
+ if (line[strlen(line) - 1] == '\n')
+ line[strlen(line) - 1] = '\0';
+
+ /* add <attribute> <value> */
+ if (strncmp (line, ADD_KEYWORD, strlen (ADD_KEYWORD)) == 0)
+ {
+ sim.ops [i - 1].type = OP_ADD_VALUE;
+ pos = strchr (&line[strlen (ADD_KEYWORD)], ' ');
+ if (pos == NULL)
+ {
+ rc = -1;
+ goto done;
+ }
+
+ memset (buff, 0, sizeof (buff));
+ strncpy (buff, &line[strlen (ADD_KEYWORD)], pos - &line[strlen (ADD_KEYWORD)]);
+ sim.ops [i - 1].sv_attr = strcmp (buff, MV_ATTR_NAME);
+ sim.ops [i - 1].value_index = value2index (pos + 1);
+ }
+ /* delete <attribute>[ <value>] */
+ else if (strncmp (line, DELETE_KEYWORD, strlen (DELETE_KEYWORD)) == 0)
+ {
+ pos = strchr (&line[strlen (DELETE_KEYWORD)], ' ');
+ if (pos == NULL) /* delete attribute version */
+ {
+ sim.ops [i - 1].type = OP_DELETE_ATTR;
+ sim.ops [i - 1].sv_attr = strcmp (&line[strlen (DELETE_KEYWORD)],
+ MV_ATTR_NAME);
+ }
+ else /* delete value version */
+ {
+ memset (buff, 0, sizeof (buff));
+ sim.ops [i - 1].type = OP_DELETE_VALUE;
+ strncpy (buff, &line[strlen (DELETE_KEYWORD)],
+ pos - &line[strlen (DELETE_KEYWORD)]);
+ sim.ops [i - 1].sv_attr = strcmp (buff, MV_ATTR_NAME);
+ sim.ops [i - 1].value_index = value2index (pos + 1);
+ }
+ }
+ /* rename to <attribute> <valued>[ delete <attribute> <value>] */
+ else if (strncmp (line, RENAME_KEYWORD, 10) == 0)
+ {
+ char *pos2;
+
+ sim.ops [i - 1].type = OP_RENAME_ENTRY;
+
+ pos = strchr (&line[strlen (RENAME_KEYWORD)], ' ');
+ if (pos == NULL)
+ {
+ rc = -1;
+ goto done;
+ }
+
+ memset (buff, 0, sizeof (buff));
+ strncpy (buff, &line[strlen (RENAME_KEYWORD)], pos - &line[strlen (RENAME_KEYWORD)]);
+ sim.ops [i - 1].sv_attr = strcmp (buff, MV_ATTR_NAME);
+
+ pos2 = strstr (pos + 1, DELET_OLD_RDN_KEYWORD);
+ if (pos2 == NULL) /* no delete old rdn part */
+ {
+ sim.ops [i - 1].value_index = value2index (pos + 1);
+ sim.ops [i - 1].delete_old_rdn = 0;
+ }
+ else
+ {
+ memset (buff, 0, sizeof (buff));
+ strncpy (buff, pos + 1, pos2 - pos - 1);
+ sim.ops [i - 1].value_index = value2index (buff);
+ pos2 += strlen (DELET_OLD_RDN_KEYWORD);
+ pos = strchr (pos2, ' ');
+ if (pos == NULL)
+ {
+ rc = -1;
+ goto done;
+ }
+
+ memset (buff, 0, sizeof (buff));
+ strncpy (buff, pos2, pos - pos2);
+ sim.ops [i - 1].delete_old_rdn = 1;
+ sim.ops [i - 1].old_rdn_sv_attr = strcmp (buff, MV_ATTR_NAME);
+ sim.ops [i - 1].old_rdn_value_index = value2index (pos + 1);
+ }
+ }
+ else
+ {
+ /* error */
+ rc = -1;
+ }
+
+done:
+ if (rc)
+ {
+ /* invalid line */
+ printf ("invalid operation: %s\n", line);
+ exit (1);
+ }
+}
+int value2index (char *value)
+{
+ int i;
+
+ for (i = 0; i < sim.value_count; i++)
+ {
+ if (strcmp (g_values[i], value) == 0)
+ return i;
+ }
+
+ return -1;
+}
+
+void print_usage ()
+{
+ printf ("usage: dnp_sim [-h] [-n <number of simulations> ] [-v] [-f <output file>]\n"
+ "\t-h - print usage information\n"
+ "\t-n <number of simulations>; default - 1\n"
+ "\t-v - verbose mode\n"
+ "\t-f <output file> - by default, results are printed to the screen\n"
+ "\t-o <op file> - file that contains operation sequence to execute;\n"
+ "\tby default, random sequence is generated.\n");
+}
+
+int count_values ()
+{
+ int i;
+
+ for (i = 0; g_values[i]; i++);
+
+ return i;
+}
+
+void run_simulation ()
+{
+ int *order;
+ int i;
+ int perm_count;
+ Entry_State entry_first, entry_current;
+ int error = 0;
+
+ init_entry_state (&entry_first);
+ fprintf (sim.fout, "initial entry state :\n");
+ dump_entry_state (&entry_first);
+
+ if (sim.ops == NULL)
+ {
+ generate_operations (&sim.ops, &sim.op_count);
+ }
+ fprintf (sim.fout, "initial operation set:\n");
+ dump_operations (sim.ops, sim.op_count, NULL/* order */);
+
+ perm_count = get_perm_count (sim.op_count);
+ for (i = 0; i < perm_count; i++)
+ {
+ fprintf (sim.fout, "--------------------------------\n");
+ fprintf (sim.fout, "simulation run %d\n", i + 1);
+ fprintf (sim.fout, "--------------------------------\n");
+ order = generate_operation_order (sim.op_count, i);
+ if (i == 0)
+ apply_operation_sequence (sim.ops, sim.op_count, order, &entry_first);
+ else
+ {
+ apply_operation_sequence (sim.ops, sim.op_count, order, &entry_current);
+ error |= compare_entry_state (&entry_first, &entry_current, i + 1);
+ }
+ }
+
+ switch (error)
+ {
+ case 0: fprintf (sim.fout, "all runs left the entry in the same state\n");
+ break;
+ case 1: fprintf (sim.fout, "while value presence is consistent across all runs, "
+ "the exact state does not match\n");
+ break;
+ case 3: fprintf (sim.fout, "the runs left entries in an inconsistent state\n");
+ break;
+ }
+
+ free_operations (&sim.ops);
+}
+
+void generate_operations (Operation **ops, int *op_count)
+{
+ int i;
+
+ /* generate number operations in the sequence */
+ *op_count = slapi_rand () % (MAX_OPS / 2) + 1;
+ *ops = (Operation *)malloc (*op_count * sizeof (Operation));
+
+ for (i = 0; i < *op_count; i ++)
+ {
+ generate_operation (&((*ops)[i]), i + 1);
+ }
+}
+
+void generate_operation (Operation *op, int csn)
+{
+ /* generate operation type */
+ op->type = slapi_rand () % OP_END;
+ op->csn = csn;
+
+ /* choose if the operation applies to the single value or
+ the multivalued attribute */
+ op->sv_attr = slapi_rand () % 2;
+
+ /* generate value to which operation applies */
+ op->value_index = slapi_rand () % sim.value_count;
+
+ if (op->type == OP_RENAME_ENTRY)
+ {
+ op->delete_old_rdn = slapi_rand () % 2;
+ if (op->delete_old_rdn)
+ {
+ op->old_rdn_sv_attr = slapi_rand () % 2;
+ op->old_rdn_value_index = slapi_rand () % sim.value_count;
+
+ while (op->old_rdn_sv_attr == op->sv_attr &&
+ op->old_rdn_value_index == op->value_index)
+ {
+ op->old_rdn_sv_attr = slapi_rand () % 2;
+ op->old_rdn_value_index = slapi_rand () % sim.value_count;
+ }
+ }
+ }
+}
+
+int* generate_operation_order (int op_count, int seq_num)
+{
+ static int **perm_table = NULL;
+
+ /* first request - generate pemutation table */
+ if (seq_num == 0)
+ {
+ int elements [MAX_OPS];
+ int i;
+
+ if (perm_table)
+ free_perm_table (&perm_table, op_count);
+ perm_table = new_perm_table (op_count);
+
+ for (i = 0; i < op_count; i++)
+ elements [i] = i;
+
+ generate_perm_table (elements, op_count, 0 /* static part */,
+ perm_table);
+ }
+
+ return perm_table [seq_num];
+}
+
+void apply_operation_sequence (Operation *ops, int op_count, int *order, Entry_State *entry)
+{
+ int i;
+
+ init_entry_state (entry);
+
+ if (!sim.verbose)
+ {
+ if (!sim.verbose)
+ {
+ fprintf (sim.fout, "operation_sequence for this run:\n");
+ dump_operations (ops, op_count, order);
+ }
+ }
+
+ for (i = 0; i < op_count; i++)
+ {
+ apply_operation (entry, &(ops [order[i]]));
+ }
+
+ if (!sim.verbose)
+ {
+ fprintf (sim.fout, "final entry state :\n");
+ dump_entry_state (entry);
+ }
+}
+
+void init_entry_state (Entry_State *entry)
+{
+ memset (entry, 0, sizeof (*entry));
+ entry->dn_csn_count = 1;
+
+ init_sv_attr_state (&entry->sv_attr);
+ init_mv_attr_state (&entry->mv_attr);
+}
+
+void init_sv_attr_state (SV_Attr_State *sv_attr)
+{
+ memset (sv_attr, 0, sizeof (*sv_attr));
+ sv_attr->attr_state.delete_csn = NOT_PRESENT;
+ sv_attr->attr_state.present = 1;
+ init_value_state (&sv_attr->current_value, 1);
+}
+
+void init_mv_attr_state (MV_Attr_State *mv_attr)
+{
+ int i;
+
+ memset (mv_attr, 0, sizeof (*mv_attr));
+ mv_attr->attr_state.delete_csn = NOT_PRESENT;
+ mv_attr->attr_state.present = 1;
+ mv_attr->value_count = sim.value_count;
+
+ for (i = 0; i < mv_attr->value_count; i++)
+ {
+ init_value_state (&(mv_attr->values[i]), i);
+ }
+}
+
+void init_value_state (Value_State *val, int seq_num)
+{
+ memset (val, 0, sizeof (*val));
+ val->value_index = seq_num;
+ val->present = 1;
+ val->delete_csn = NOT_PRESENT;
+}
+
+void apply_operation (Entry_State *entry, Operation *op)
+{
+ switch (op->type)
+ {
+ case OP_ADD_VALUE: apply_add_operation (entry, op);
+ break;
+
+ case OP_DELETE_VALUE: apply_value_delete_operation (entry, op);
+ break;
+
+ case OP_DELETE_ATTR: apply_attr_delete_operation (entry, op);
+ break;
+
+ case OP_RENAME_ENTRY: apply_rename_operation (entry, op);
+ break;
+ }
+
+ if (sim.verbose)
+ {
+ fprintf (sim.fout, "operation: ");
+ dump_operation (op);
+ fprintf (sim.fout, "\n");
+ dump_entry_state (entry);
+ }
+}
+
+void free_operations (Operation **ops)
+{
+ free (*ops);
+ *ops = NULL;
+}
+
+int **new_perm_table (int op_count)
+{
+ int i;
+ int **perm_table;
+ int perm_count = get_perm_count (op_count);
+
+ perm_table = (int**)malloc (perm_count * sizeof (int*));
+ for (i = 0; i < perm_count; i ++)
+ perm_table [i] = (int*) malloc (op_count * sizeof (int));
+
+ return perm_table;
+}
+
+void free_perm_table (int ***perm_table, int op_count)
+{
+ int i;
+ int perm_count = get_perm_count (op_count);
+
+ for (i = 0; i < perm_count; i ++)
+ free ((*perm_table)[i]);
+
+ free (*perm_table);
+ *perm_table = NULL;
+}
+
+void generate_perm_table (int *elements, int element_count, int static_part,
+ int **perm_table)
+{
+ int i;
+ int elements_copy [MAX_OPS];
+ int start_pos;
+
+ if (element_count - 1 == static_part)
+ {
+ memcpy (*perm_table, elements, element_count * sizeof (int));
+ return;
+ }
+
+ start_pos = 0;
+ for (i = 0; i < element_count - static_part; i ++)
+ {
+ memcpy (elements_copy, elements, element_count * sizeof (int));
+ elements_copy [static_part] = elements [static_part + i];
+ elements_copy [static_part + i] = elements [static_part];
+ generate_perm_table (elements_copy, element_count, static_part + 1,
+ &perm_table [start_pos]);
+ start_pos += get_perm_count (element_count - static_part - 1);
+ }
+}
+
+int get_perm_count (int op_count)
+{
+ int i;
+ int perm_count = 1;
+
+ for (i = 2; i <= op_count; i ++)
+ perm_count *= i;
+
+ return perm_count;
+}
+
+void apply_add_operation (Entry_State *entry, Operation *op)
+{
+ if (op->sv_attr)
+ {
+ Value_State *val;
+ Value_State temp_val;
+
+ if (op->value_index == entry->sv_attr.current_value.value_index)
+ {
+ val = &entry->sv_attr.current_value;
+ }
+ else if (entry->sv_attr.pending_value &&
+ op->value_index == entry->sv_attr.pending_value->value_index)
+ {
+ val = entry->sv_attr.pending_value;
+ }
+ else /* new value */
+ {
+ init_value_state (&temp_val, op->value_index);
+ val = &temp_val;
+ }
+
+ if (val->presence_csn < op->csn)
+ val->presence_csn = op->csn;
+
+ resolve_sv_attr_state (entry, val);
+ }
+ else
+ {
+ if (entry->mv_attr.values[op->value_index].presence_csn < op->csn)
+ {
+ entry->mv_attr.values[op->value_index].presence_csn = op->csn;
+ resolve_mv_attr_state (entry, &(entry->mv_attr.values[op->value_index]));
+ }
+ }
+}
+
+void apply_value_delete_operation (Entry_State *entry, Operation *op)
+{
+ if (op->sv_attr)
+ {
+ if (entry->sv_attr.attr_state.delete_csn < op->csn)
+ {
+ entry->sv_attr.attr_state.delete_csn = op->csn;
+ resolve_sv_attr_state (entry, NULL);
+ }
+ }
+ else /* mv attr */
+ {
+ if (entry->mv_attr.values[op->value_index].delete_csn < op->csn)
+ {
+ entry->mv_attr.values[op->value_index].delete_csn = op->csn;
+ resolve_mv_attr_state (entry, &(entry->mv_attr.values[op->value_index]));
+ }
+ }
+}
+
+void apply_attr_delete_operation (Entry_State *entry, Operation *op)
+{
+ int i;
+
+ if (op->sv_attr)
+ {
+ if (entry->sv_attr.attr_state.delete_csn < op->csn)
+ {
+ entry->sv_attr.attr_state.delete_csn = op->csn;
+ resolve_sv_attr_state (entry, NULL);
+ }
+ }
+ else /* mv attr */
+ {
+ if (entry->mv_attr.attr_state.delete_csn < op->csn)
+ {
+ entry->mv_attr.attr_state.delete_csn = op->csn;
+
+ for (i = 0; i < sim.value_count; i++)
+ {
+ resolve_mv_attr_state (entry, &(entry->mv_attr.values[i]));
+ }
+ }
+ }
+}
+
+void apply_rename_operation (Entry_State *entry, Operation *op)
+{
+ int index;
+ Operation del_op;
+
+ /* insert new dn into dn_csn_list */
+ index = dn_csn_add (entry, op->sv_attr, op->value_index, op->csn);
+
+ /* issue delete value operation for the old rdn */
+ if (op->delete_old_rdn)
+ {
+ del_op.type = OP_DELETE_VALUE;
+ del_op.csn = op->csn;
+ del_op.sv_attr = op->old_rdn_sv_attr;
+ del_op.value_index = op->old_rdn_value_index;
+
+ apply_value_delete_operation (entry, &del_op);
+ }
+
+ /* resolve state of the previous node in dn_csn_list */
+ if (index > 0)
+ {
+ if (entry->dn_csns[index-1].sv_attr)
+ {
+ if (entry->dn_csns[index-1].value_index ==
+ entry->sv_attr.current_value.value_index)
+ {
+ resolve_sv_attr_state (entry, &(entry->sv_attr.current_value));
+ }
+ else if (entry->sv_attr.pending_value &&
+ entry->dn_csns[index-1].value_index ==
+ entry->sv_attr.pending_value->value_index)
+ {
+ resolve_sv_attr_state (entry, entry->sv_attr.pending_value);
+ }
+ }
+ else
+ {
+ int i = entry->dn_csns[index-1].value_index;
+ resolve_mv_attr_state (entry, &(entry->mv_attr.values[i]));
+ }
+ }
+
+ /* resolve state of the new dn */
+ if (op->sv_attr)
+ {
+ Value_State *value;
+ Value_State temp_val;
+ if (op->value_index == entry->sv_attr.current_value.value_index)
+ {
+ value = &entry->sv_attr.current_value;
+ }
+ else if (entry->sv_attr.pending_value &&
+ op->value_index == entry->sv_attr.pending_value->value_index)
+ {
+ value = entry->sv_attr.pending_value;
+ }
+ else /* new value */
+ {
+ init_value_state (&temp_val, op->value_index);
+ value = &temp_val;
+ }
+
+ if (value->presence_csn == NOT_PRESENT || value->presence_csn < op->csn)
+ value->presence_csn = op->csn;
+ resolve_sv_attr_state (entry, value);
+ }
+ else
+ {
+ if (entry->mv_attr.values[op->value_index].presence_csn == NOT_PRESENT ||
+ entry->mv_attr.values[op->value_index].presence_csn < op->csn)
+ entry->mv_attr.values[op->value_index].presence_csn = op->csn;
+
+ resolve_mv_attr_state (entry, &(entry->mv_attr.values[op->value_index]));
+ }
+}
+
+void purge_mv_attr_state (Entry_State *entry, Value_State *value)
+{
+ if (value->presence_csn > value->delete_csn)
+ value->delete_csn = NOT_PRESENT;
+}
+
+void purge_sv_attr_state (Entry_State *entry)
+{
+ if (entry->sv_attr.attr_state.delete_csn != NOT_PRESENT)
+ {
+ if (entry->sv_attr.pending_value)
+ {
+ if (entry->sv_attr.attr_state.delete_csn <
+ entry->sv_attr.pending_value->presence_csn)
+ {
+ entry->sv_attr.attr_state.delete_csn = NOT_PRESENT;
+ }
+ }
+ else
+ {
+ if (entry->sv_attr.attr_state.delete_csn <
+ entry->sv_attr.current_value.presence_csn)
+ entry->sv_attr.attr_state.delete_csn = NOT_PRESENT;
+ }
+ }
+}
+
+void resolve_mv_attr_state (Entry_State *entry, Value_State *value)
+{
+ purge_mv_attr_state (entry, value);
+
+ /* no deletes that effect the state */
+ if (max_val (value->delete_csn, entry->mv_attr.attr_state.delete_csn) <
+ value->presence_csn)
+ {
+ value->present = 1;
+ return;
+ }
+
+ if (value->present) /* check if it should be removed based on the current state */
+ {
+ if (!value_distinguished_at_csn (entry, 0, value,
+ max (value->delete_csn, entry->mv_attr.attr_state.delete_csn)))
+ {
+ value->present = 0;
+ }
+ }
+ else /* not present - check if it should be restored */
+ {
+ if (value_distinguished_at_csn (entry, 0, value,
+ max (value->delete_csn, entry->mv_attr.attr_state.delete_csn)))
+ {
+ value->present = 1;
+ }
+ }
+
+ if (entry->mv_attr.attr_state.delete_csn == NOT_PRESENT)
+ {
+ entry->mv_attr.attr_state.present = 1;
+ }
+ else
+ {
+ int i;
+ int distinguished = 0;
+
+ for (i = 0; i < entry->mv_attr.value_count; i ++)
+ {
+ distinguished |= value_distinguished_at_csn (entry, 0,
+ &(entry->mv_attr.values[i]),
+ entry->mv_attr.attr_state.delete_csn);
+ }
+
+ entry->mv_attr.attr_state.present = distinguished;
+ }
+}
+
+void resolve_sv_attr_state (Entry_State *entry, Value_State *value)
+{
+ purge_sv_attr_state (entry);
+
+ if (value)
+ {
+ /* existing value is modified */
+ if (value == &(entry->sv_attr.current_value) ||
+ value == entry->sv_attr.pending_value)
+ {
+ /* check if current value should be replaced with the pending value */
+ if (entry->sv_attr.pending_value)
+ {
+ if (!value_distinguished_at_csn (entry, 1, &entry->sv_attr.current_value,
+ entry->sv_attr.current_value.presence_csn))
+ {
+ /* replace current value with the pending value */
+ memcpy (&entry->sv_attr.current_value, entry->sv_attr.pending_value,
+ sizeof (Value_State));
+ free (entry->sv_attr.pending_value);
+ entry->sv_attr.pending_value = NULL;
+ }
+ }
+ }
+ else /* addition of a new value */
+ {
+ /* new value is before the current value; note that, for new value,
+ presence_csn is the same as distinguished_csn */
+ if (value->presence_csn < entry->sv_attr.current_value.presence_csn)
+ {
+ /* if new value is distinguished, it should become current and the
+ current can become pending */
+ if (value_distinguished_at_csn (entry, 1, value,
+ entry->sv_attr.current_value.presence_csn))
+ {
+ if (entry->sv_attr.pending_value == NULL)
+ {
+ entry->sv_attr.pending_value = (Value_State*)
+ malloc (sizeof (Value_State));
+ memcpy (entry->sv_attr.pending_value, &entry->sv_attr.current_value,
+ sizeof (Value_State));
+ }
+
+ memcpy (&entry->sv_attr.current_value, value, sizeof (Value_State));
+ }
+ }
+ else /* new value is after the current value */
+ {
+ /* if current value is not distinguished, new value should
+ become distinguished */
+ if (!value_distinguished_at_csn (entry, 1, &entry->sv_attr.current_value,
+ value->presence_csn))
+ {
+ memcpy (&entry->sv_attr.current_value, value, sizeof (Value_State));
+ }
+ else /* current value is distinguished - check if new value should replace
+ the pending value */
+ { if (entry->sv_attr.pending_value)
+ {
+ if (value->presence_csn > entry->sv_attr.pending_value->presence_csn)
+ {
+ memcpy (entry->sv_attr.pending_value, value, sizeof (Value_State));
+ }
+ }
+ else
+ {
+ entry->sv_attr.pending_value = (Value_State*)malloc (sizeof (Value_State));
+ memcpy (entry->sv_attr.pending_value, value, sizeof (Value_State));
+ }
+ }
+ }
+ }
+ }
+
+ /* update the attribute state */
+ purge_sv_attr_state (entry);
+
+ /* set attribute state */
+ if (entry->sv_attr.attr_state.delete_csn != NOT_PRESENT &&
+ !value_distinguished_at_csn (entry, 1, &entry->sv_attr.current_value,
+ entry->sv_attr.attr_state.delete_csn))
+ {
+ entry->sv_attr.attr_state.present = 0;
+ }
+ else
+ {
+ entry->sv_attr.attr_state.present = 1;
+ }
+}
+
+int value_distinguished_at_csn (Entry_State *entry, int sv_attr, Value_State *value, int csn)
+{
+ int i;
+
+ for (i = 0; i < entry->dn_csn_count; i++)
+ {
+ if (entry->dn_csns[i].csn > csn)
+ break;
+ }
+
+ /* i is never equal to 0 because the csn of the first element is always
+ smaller than csn of any operation we can receive */
+ return (entry->dn_csns[i-1].value_index == value->value_index &&
+ entry->dn_csns[i-1].sv_attr == sv_attr);
+}
+
+int compare_entry_state (Entry_State *entry1, Entry_State *entry2, int run)
+{
+ int i;
+ int error = 0;
+
+ error = compare_entry_state_quick (entry1, entry2, run);
+
+ if (error)
+ return 3;
+
+ /* compare dnc_csn list */
+ if (entry1->dn_csn_count != entry2->dn_csn_count)
+ {
+ fprintf (sim.fout, "dn_csn count is %d for run 1 and %d for run %d\n",
+ entry1->dn_csn_count, entry2->dn_csn_count, run);
+ error = 1;
+ }
+
+ for (i = 0; i < entry1->dn_csn_count; i++)
+ {
+ if (entry1->dn_csns [i].csn != entry2->dn_csns [i].csn ||
+ entry1->dn_csns [i].sv_attr != entry2->dn_csns [i].sv_attr ||
+ entry1->dn_csns [i].value_index != entry2->dn_csns [i].value_index)
+ {
+ fprintf (sim.fout,"elements %d of dn csn list are different:\n"
+ "\tfirst run: csn - %d, attr - %s, value - %s\n"
+ "\t%d run: csn - %d, attr - %s value - %s\n", i,
+ entry1->dn_csns [i].csn,
+ entry1->dn_csns [i].sv_attr ? SV_ATTR_NAME : MV_ATTR_NAME,
+ g_values[entry1->dn_csns [i].value_index],
+ run, entry2->dn_csns [i].csn,
+ entry2->dn_csns [i].sv_attr ? SV_ATTR_NAME : MV_ATTR_NAME,
+ g_values[entry2->dn_csns [i].value_index]);
+
+ error = 1;
+ }
+ }
+
+ error |= compare_sv_attr_state (&entry1->sv_attr, &entry2->sv_attr, run);
+
+ error |= compare_mv_attr_state (&entry1->mv_attr, &entry2->mv_attr, run);
+
+ if (error != 0)
+ {
+ return 1;
+ }
+ else
+ return 0;
+}
+
+/* just compare if the same attributes and values are present */
+int compare_entry_state_quick (Entry_State *entry1, Entry_State *entry2, int run)
+{
+ int error;
+
+ error = compare_sv_attr_state_quick (&entry1->sv_attr, &entry2->sv_attr, run);
+
+ error |= compare_mv_attr_state_quick (&entry1->mv_attr, &entry2->mv_attr, run);
+
+ return error;
+}
+
+int compare_sv_attr_state_quick (SV_Attr_State *sv_attr1, SV_Attr_State *sv_attr2, int run)
+{
+ int error = 0;
+ if (sv_attr1->attr_state.present != sv_attr2->attr_state.present)
+ {
+ fprintf (sim.fout, "singlevalued attribute is %s present in the first run "
+ "but is %s present in the %d run\n",
+ sv_attr1->attr_state.present ? "" : "not",
+ sv_attr2->attr_state.present ? "" : "not", run);
+ return 1;
+ }
+
+ if (sv_attr1->attr_state.present &&
+ sv_attr1->current_value.value_index != sv_attr2->current_value.value_index)
+ {
+ fprintf (sim.fout, "different values for singlevalued attribute: %s for the \n"
+ "first run and %s for the %d run\n",
+ g_values [sv_attr1->current_value.value_index],
+ g_values [sv_attr2->current_value.value_index], run);
+ return 1;
+ }
+
+ return 0;
+}
+
+int compare_mv_attr_state_quick (MV_Attr_State *mv_attr1, MV_Attr_State *mv_attr2, int run)
+{
+ int i;
+ int error = 0;
+
+ if (mv_attr1->attr_state.present != mv_attr2->attr_state.present)
+ {
+ fprintf (sim.fout, "multivalued attribute is %s present in the first run "
+ "but is %s present in the %d run\n",
+ mv_attr1->attr_state.present ? "" : "not",
+ mv_attr2->attr_state.present ? "" : "not", run);
+ return 1;
+ }
+
+ /* value count does not change during the iteration, so we don't have
+ to check if the count is the same for both attributes */
+ for (i = 0; i < mv_attr1->value_count; i++)
+ {
+ if (mv_attr1->values[i].present != mv_attr2->values[i].present)
+ {
+ fprintf (sim.fout, "value %s is %s present in the multivalued attribute\n"
+ "in the first run but %s present in the %d run\n",
+ g_values[i], mv_attr1->values[i].present ? "" : "not",
+ mv_attr2->values[i].present ? "" : "not", run);
+ error = 1;
+ }
+ }
+
+ return error;
+}
+
+int compare_sv_attr_state (SV_Attr_State *sv_attr1, SV_Attr_State *sv_attr2, int run)
+{
+ int error = 0;
+
+ if (sv_attr1->attr_state.delete_csn != sv_attr2->attr_state.delete_csn)
+ {
+ fprintf (sim.fout, "singlevalued attribute deletion csn is %d for run 1 "
+ "but is %d for run %d\n", sv_attr1->attr_state.delete_csn,
+ sv_attr2->attr_state.delete_csn, run);
+ error = 1;
+ }
+
+ error |= compare_value_state (&sv_attr1->current_value, &sv_attr2->current_value, run);
+
+ if ((sv_attr1->pending_value && !sv_attr1->pending_value) ||
+ (!sv_attr1->pending_value && sv_attr1->pending_value))
+ {
+ fprintf (sim.fout, "pending value is %s present in the singlevalued attribute\n"
+ " in the first run but is %s in the %d run\n",
+ sv_attr1->pending_value ? "" : "not",
+ sv_attr2->pending_value ? "" : "not", run);
+
+ return 1;
+ }
+
+ if (sv_attr1->pending_value)
+ error |= compare_value_state (sv_attr1->pending_value, sv_attr2->pending_value, run);
+
+ return 0;
+}
+
+int compare_mv_attr_state (MV_Attr_State *mv_attr1, MV_Attr_State *mv_attr2, int run)
+{
+ int error = 0;
+ int i;
+
+ if (mv_attr1->attr_state.delete_csn != mv_attr2->attr_state.delete_csn)
+ {
+ fprintf (sim.fout, "multivalued attribute deletion csn is %d for run 1 "
+ "but is %d for run %d\n", mv_attr1->attr_state.delete_csn,
+ mv_attr2->attr_state.delete_csn, run);
+ error = 1;
+ }
+
+ for (i = 0; i < mv_attr1->value_count; i++)
+ {
+ error |= compare_value_state (&mv_attr1->values[i], &mv_attr2->values[i], run);
+ }
+
+ return error;
+}
+
+int compare_value_state (Value_State *value1, Value_State *value2, int run)
+{
+ int error = 0;
+
+ if (value1->presence_csn != value2->presence_csn)
+ {
+ fprintf (sim.fout, "multivalued attribute: presence csn for value %s is %d "
+ "in run 1 but is %d in run %d\n", g_values[value1->value_index],
+ value1->presence_csn, value2->presence_csn, run);
+ error = 1;
+ }
+
+ if (value1->delete_csn != value2->delete_csn)
+ {
+ fprintf (sim.fout, "multivalued attribute: delete csn for value %s is %d in run 1 "
+ "but is %d in run %d\n", g_values[value1->value_index],
+ value1->delete_csn, value2->delete_csn, run);
+ error = 1;
+ }
+
+ return error;
+}
+
+int dn_csn_add (Entry_State *entry, int sv_attr, int value_index, int csn)
+{
+ int i;
+
+ for (i = 0; i < entry->dn_csn_count; i++)
+ {
+ if (entry->dn_csns[i].csn > csn)
+ break;
+ }
+
+ if (i < entry->dn_csn_count)
+ {
+ memcpy (&(entry->dn_csns[i+1]), &(entry->dn_csns[i]),
+ (entry->dn_csn_count - i) * sizeof (Dn_Csn));
+ }
+
+ entry->dn_csns[i].csn = csn;
+ entry->dn_csns[i].sv_attr = sv_attr;
+ entry->dn_csns[i].value_index = value_index;
+ entry->dn_csn_count ++;
+
+ return i;
+}
+
+void dump_operations (Operation *ops, int op_count, int *order)
+{
+ int index;
+ int i;
+
+ for (i = 0; i < op_count; i ++)
+ {
+ if (order == NULL) /* current order */
+ index = i;
+ else
+ index = order [i];
+
+ dump_operation (&ops[index]);
+ }
+
+ fprintf (sim.fout, "\n");
+}
+
+void dump_operation (Operation *op)
+{
+ switch (op->type)
+ {
+ case OP_ADD_VALUE:
+ fprintf (sim.fout, "\t%d add value %s to %s\n", op->csn,
+ g_values [op->value_index],
+ op->sv_attr ? SV_ATTR_NAME : MV_ATTR_NAME);
+ break;
+ case OP_DELETE_VALUE:
+ fprintf (sim.fout, "\t%d delete value %s from %s\n", op->csn,
+ g_values [op->value_index],
+ op->sv_attr ? SV_ATTR_NAME : MV_ATTR_NAME);
+ break;
+ case OP_DELETE_ATTR:
+ fprintf (sim.fout, "\t%d delete %s attribute\n", op->csn,
+ op->sv_attr ? SV_ATTR_NAME : MV_ATTR_NAME);
+ break;
+ case OP_RENAME_ENTRY:
+ fprintf (sim.fout, "\t%d rename entry to %s=%s", op->csn,
+ op->sv_attr ? SV_ATTR_NAME : MV_ATTR_NAME,
+ g_values [op->value_index]);
+ if (op->delete_old_rdn)
+ fprintf (sim.fout, " delete old rdn %s=%s\n",
+ op->old_rdn_sv_attr ? SV_ATTR_NAME : MV_ATTR_NAME,
+ g_values [op->old_rdn_value_index]);
+ else
+ fprintf (sim.fout, "\n");
+ break;
+ }
+}
+
+void dump_perm_table (int **perm_table, int op_count)
+{
+ int i, j;
+ int perm_count = get_perm_count (op_count);
+
+ for (i = 0; i < op_count; i++)
+ {
+ for (j = 0; j < perm_count; j++)
+ {
+ fprintf (sim.fout, "%d ", perm_table [j][i]);
+ }
+
+ fprintf (sim.fout, "\n");
+ }
+}
+
+void dump_entry_state (Entry_State *entry)
+{
+ dump_dn_csn_list (entry);
+
+ dump_sv_attr_state (&entry->sv_attr);
+ dump_mv_attr_state (&entry->mv_attr);
+
+ fprintf (sim.fout, "\n");
+}
+
+void dump_sv_attr_state (SV_Attr_State *sv_attr)
+{
+ fprintf (sim.fout, "\tattribute %s is %s present", SV_ATTR_NAME,
+ sv_attr->attr_state.present ? "" : "not");
+ if (sv_attr->attr_state.present)
+ {
+ fprintf (sim.fout, " and has the value of %s\n",
+ g_values[sv_attr->current_value.value_index]);
+ }
+ else
+ {
+ fprintf (sim.fout, "\n");
+ }
+
+ if (sim.verbose)
+ {
+ fprintf (sim.fout, "\t\tdeletion csn: %d\n", sv_attr->attr_state.delete_csn);
+ fprintf (sim.fout, "\t\tcurrent value: ");
+ dump_value_state (&sv_attr->current_value, 1/* for single valued attr */);
+ if (sv_attr->pending_value)
+ {
+ fprintf (sim.fout, "\t\tpending value: ");
+ dump_value_state (sv_attr->pending_value, 1/* for single valued attr */);
+ }
+ }
+}
+
+void dump_mv_attr_state (MV_Attr_State *mv_attr)
+{
+ int i;
+
+ fprintf (sim.fout, "\tattribute %s is %s present\n", MV_ATTR_NAME,
+ mv_attr->attr_state.present ? "" : "not");
+
+ if (sim.verbose)
+ {
+ fprintf (sim.fout, "\t\tdeletion csn: %d\n", mv_attr->attr_state.delete_csn);
+ }
+
+ for (i = 0; i < mv_attr->value_count; i++)
+ {
+ dump_value_state (&(mv_attr->values[i]), 0);
+ }
+}
+
+void dump_value_state (Value_State *value, int sv_attr)
+{
+ if (!sv_attr)
+ {
+ fprintf (sim.fout, "\tvalue %s is %s present\n", g_values[value->value_index],
+ value->present ? "" : "not");
+ }
+ else
+ {
+ fprintf (sim.fout, "%s\n", g_values[value->value_index]);
+ }
+
+ if (sim.verbose)
+ {
+ fprintf (sim.fout, "\t\t\tpresence csn: %d\n", value->presence_csn);
+ fprintf (sim.fout, "\t\t\tdeletion value csn: %d\n", value->delete_csn);
+ }
+}
+
+void dump_dn_csn_list (Entry_State *entry)
+{
+ int i;
+
+ fprintf (sim.fout, "\tdn csn list: \n");
+ for (i = 0; i < entry->dn_csn_count; i++)
+ {
+ fprintf (sim.fout, "\t\t %s=%s, csn: %d\n",
+ entry->dn_csns[i].sv_attr ? SV_ATTR_NAME : MV_ATTR_NAME,
+ g_values[entry->dn_csns[i].value_index], entry->dn_csns[i].csn);
+ }
+}
+
+/* misc functions */
+int max_val (int a, int b)
+{
+ if (a >= b)
+ return a;
+ else
+ return b;
+}
diff --git a/ldap/servers/plugins/replication/tests/makesim b/ldap/servers/plugins/replication/tests/makesim
new file mode 100755
index 00000000..0cedd6e1
--- /dev/null
+++ b/ldap/servers/plugins/replication/tests/makesim
@@ -0,0 +1,58 @@
+# BEGIN COPYRIGHT BLOCK
+# Copyright 2001 Sun Microsystems, Inc.
+# Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+# All rights reserved.
+# END COPYRIGHT BLOCK
+#
+# gnu makefile for LDAP Server tools.
+#
+
+MCOM_ROOT = ../../../../../..
+LDAP_SRC = ../../../..
+
+NOSTDCLEAN=true # don't let nsconfig.mk define target clean
+NOSTDSTRIP=true # don't let nsconfig.mk define target strip
+
+OBJDEST = $(OBJDIR)/lib/replication-plugin
+BINDIR = $(OBJDIR)/bin
+
+include $(MCOM_ROOT)/netsite/nsdefs.mk
+include $(MCOM_ROOT)/netsite/nsconfig.mk
+include $(LDAP_SRC)/nsldap.mk
+
+LDFLAGS += $(EXLDFLAGS)
+
+ifeq ($(ARCH), WINNT)
+SUBSYSTEM=console
+endif
+
+DEPLIBS=
+
+EXTRA_LIBS_DEP =
+
+EXTRA_LIBS =
+
+ifeq ($(ARCH), WINNT)
+EXTRA_LIBS += user32.lib
+endif
+
+DNP_SIM = $(addsuffix $(EXE_SUFFIX), \
+ $(addprefix $(BINDIR)/, dnp_sim))
+
+
+all: $(OBJDEST) $(BINDIR) $(DNP_SIM)
+
+$(DNP_SIM): $(OBJDEST)/dnp_sim3.o $(EXTRA_LIBS_DEP)
+ $(LINK_EXE) $(OBJDEST)/dnp_sim3.o \
+ $(EXTRA_LIBS) $<
+
+
+$(OBJDEST):
+ $(MKDIR) $(OBJDEST)
+
+$(BINDIR):
+ $(MKDIR) $(BINDIR)
+
+clean:
+ -$(RM) $(ALL_OBJS)
+ -$(RM) $(BINS)
diff --git a/ldap/servers/plugins/replication/urp.c b/ldap/servers/plugins/replication/urp.c
new file mode 100644
index 00000000..a4dc86f9
--- /dev/null
+++ b/ldap/servers/plugins/replication/urp.c
@@ -0,0 +1,1282 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+
+/*
+ * urp.c - Update Resolution Procedures
+ */
+
+#include "slapi-plugin.h"
+#include "repl.h"
+#include "repl5.h"
+#include "urp.h"
+
+extern int slapi_log_urp;
+
+static int urp_add_resolve_parententry (Slapi_PBlock *pb, char *sessionid, Slapi_Entry *entry, Slapi_Entry *parententry, CSN *opcsn);
+static int urp_annotate_dn (char *sessionid, Slapi_Entry *entry, CSN *opcsn, const char *optype);
+static int urp_naming_conflict_removal (Slapi_PBlock *pb, char *sessionid, CSN *opcsn, const char *optype);
+static int mod_namingconflict_attr (const char *uniqueid, const char*entrydn, const char *conflictdn, CSN *opcsn);
+static int del_replconflict_attr (Slapi_Entry *entry, CSN *opcsn, int opflags);
+static char *get_dn_plus_uniqueid(char *sessionid,const char *olddn,const char *uniqueid);
+static char *get_rdn_plus_uniqueid(char *sessionid,const char *olddn,const char *uniqueid);
+static void set_pblock_dn (Slapi_PBlock* pb,int pblock_parameter,char *newdn);
+static int is_suffix_entry (Slapi_PBlock *pb, Slapi_Entry *entry, Slapi_DN **parenddn);
+
+/*
+ * Return 0 for OK, -1 for Error.
+ */
+int
+urp_modify_operation( Slapi_PBlock *pb )
+{
+ Slapi_Entry *modifyentry= NULL;
+ int op_result= 0;
+ int rc= 0; /* OK */
+
+ if ( slapi_op_abandoned(pb) )
+ {
+ return rc;
+ }
+
+ slapi_pblock_get( pb, SLAPI_MODIFY_EXISTING_ENTRY, &modifyentry );
+
+ if(modifyentry!=NULL)
+ {
+ /*
+ * The entry to be modified exists.
+ * - the entry could be a tombstone... but that's OK.
+ * - the entry could be glue... that may not be OK. JCMREPL
+ */
+ rc= 0; /* OK, Modify the entry */
+ PROFILE_POINT; /* Modify Conflict; Entry Exists; Apply Modification */
+ }
+ else
+ {
+ /*
+ * The entry to be modified could not be found.
+ */
+ op_result= LDAP_NO_SUCH_OBJECT;
+ slapi_pblock_set(pb, SLAPI_RESULT_CODE, &op_result);
+ rc= -1; /* Must discard this Modification */
+ PROFILE_POINT; /* Modify Conflict; Entry Does Not Exist; Discard Modification */
+ }
+ return rc;
+}
+
+/*
+ * Return 0 for OK,
+ * -1 for Ignore or Error depending on SLAPI_RESULT_CODE,
+ * >0 for action code
+ * Action Code Bit 0: Fetch existing entry.
+ * Action Code Bit 1: Fetch parent entry.
+ * The function is called as a be pre-op on consumers.
+ */
+int
+urp_add_operation( Slapi_PBlock *pb )
+{
+ Slapi_Entry *existing_uniqueid_entry;
+ Slapi_Entry *existing_dn_entry;
+ Slapi_Entry *addentry;
+ const char *adduniqueid;
+ CSN *opcsn;
+ const char *basedn;
+ char sessionid[REPL_SESSION_ID_SIZE];
+ int r;
+ int op_result= 0;
+ int rc= 0; /* OK */
+
+ if ( slapi_op_abandoned(pb) )
+ {
+ return rc;
+ }
+
+ slapi_pblock_get( pb, SLAPI_ADD_EXISTING_UNIQUEID_ENTRY, &existing_uniqueid_entry );
+ if (existing_uniqueid_entry!=NULL)
+ {
+ /*
+ * An entry with this uniqueid already exists.
+ * - It could be a replay of the same Add, or
+ * - It could be a UUID generation collision, or
+ */
+ op_result = LDAP_SUCCESS;
+ slapi_pblock_set(pb, SLAPI_RESULT_CODE, &op_result);
+ rc= -1; /* Ignore this Operation */
+ PROFILE_POINT; /* Add Conflict; UniqueID Exists; Ignore */
+ goto bailout;
+ }
+
+ get_repl_session_id (pb, sessionid, &opcsn);
+ slapi_pblock_get( pb, SLAPI_ADD_ENTRY, &addentry );
+ slapi_pblock_get( pb, SLAPI_ADD_EXISTING_DN_ENTRY, &existing_dn_entry );
+ if (existing_dn_entry==NULL) /* The target DN does not exist */
+ {
+ /* Check for parent entry... this could be an orphan. */
+ Slapi_Entry *parententry;
+ slapi_pblock_get( pb, SLAPI_ADD_PARENT_ENTRY, &parententry );
+ rc = urp_add_resolve_parententry (pb, sessionid, addentry, parententry, opcsn);
+ PROFILE_POINT; /* Add Entry */
+ goto bailout;
+ }
+
+ /*
+ * Naming conflict: an entry with the target DN already exists.
+ * Compare the DistinguishedNameCSN of the existing entry
+ * and the OperationCSN. The smaller CSN wins. The loser changes
+ * its RDN to uniqueid+baserdn, and adds operational attribute
+ * ATTR_NSDS5_REPLCONFLIC.
+ */
+ basedn = slapi_entry_get_ndn (addentry);
+ adduniqueid = slapi_entry_get_uniqueid (addentry);
+ r = csn_compare (entry_get_dncsn(existing_dn_entry), opcsn);
+ if (r<0)
+ {
+ /* Entry to be added is a loser */
+ char *newdn= get_dn_plus_uniqueid (sessionid, basedn, adduniqueid);
+ if(newdn==NULL)
+ {
+ op_result= LDAP_OPERATIONS_ERROR;
+ slapi_pblock_set(pb, SLAPI_RESULT_CODE, &op_result);
+ rc= -1; /* Abort this Operation */
+ PROFILE_POINT; /* Add Conflict; Entry Exists; Unique ID already in RDN - Abort this update. */
+ }
+ else
+ {
+ /* Add the nsds5ReplConflict attribute in the mods */
+ Slapi_Attr *attr = NULL;
+ Slapi_Value **vals = NULL;
+ Slapi_RDN *rdn;
+ char buf[BUFSIZ];
+
+ sprintf(buf, "%s %s", REASON_ANNOTATE_DN, basedn);
+ if (slapi_entry_attr_find (addentry, ATTR_NSDS5_REPLCONFLICT, &attr) == 0)
+ {
+ /* ATTR_NSDS5_REPLCONFLICT exists */
+ slapi_log_error (SLAPI_LOG_FATAL, sessionid, "New entry has nsds5ReplConflict already\n");
+ vals = attr_get_present_values (attr); /* this returns a pointer to the contents */
+ }
+ if ( vals == NULL || *vals == NULL )
+ {
+ /* Add new attribute */
+ slapi_entry_add_string (addentry, ATTR_NSDS5_REPLCONFLICT, buf);
+ }
+ else
+ {
+ /*
+ * Replace old attribute. We don't worry about the index
+ * change here since the entry is yet to be added.
+ */
+ slapi_value_set_string (*vals, buf);
+ }
+ slapi_entry_set_dn (addentry,slapi_ch_strdup(newdn));
+ set_pblock_dn(pb,SLAPI_ADD_TARGET,newdn); /* consumes newdn */
+
+ rdn = slapi_rdn_new_sdn ( slapi_entry_get_sdn_const(addentry) );
+ slapi_log_error (slapi_log_urp, sessionid,
+ "Naming conflict ADD. Add %s instead\n", slapi_rdn_get_rdn(rdn) );
+ slapi_rdn_free(&rdn);
+
+ rc= slapi_setbit_int(rc,SLAPI_RTN_BIT_FETCH_EXISTING_DN_ENTRY);
+ PROFILE_POINT; /* Add Conflict; Entry Exists; Rename Operation Entry */
+ }
+ }
+ else if(r>0)
+ {
+ /* Existing entry is a loser */
+ if (!urp_annotate_dn(sessionid, existing_dn_entry, opcsn, "ADD"))
+ {
+ op_result= LDAP_OPERATIONS_ERROR;
+ slapi_pblock_set(pb, SLAPI_RESULT_CODE, &op_result);
+ rc= -1; /* Ignore this Operation */
+ }
+ else
+ {
+ /* The backend add code should now search for the existing entry again. */
+ rc= slapi_setbit_int(rc,SLAPI_RTN_BIT_FETCH_EXISTING_DN_ENTRY);
+ rc= slapi_setbit_int(rc,SLAPI_RTN_BIT_FETCH_PARENT_ENTRY);
+ }
+ PROFILE_POINT; /* Add Conflict; Entry Exists; Rename Existing Entry */
+ }
+ else /* r==0 */
+ {
+ /* The CSN of the Operation and the Entry DN are the same.
+ * This could only happen if:
+ * a) There are two replicas with the same ReplicaID.
+ * b) We've seen the Operation before.
+ * Let's go with (b) and ignore the little bastard.
+ */
+ op_result= LDAP_SUCCESS;
+ slapi_pblock_set(pb, SLAPI_RESULT_CODE, &op_result);
+ rc= -1; /* Ignore this Operation */
+ PROFILE_POINT; /* Add Conflict; Entry Exists; Same CSN */
+ }
+
+bailout:
+ return rc;
+}
+
+/*
+ * Return 0 for OK, -1 for Error, >0 for action code
+ * Action Code Bit 0: Fetch existing entry.
+ * Action Code Bit 1: Fetch parent entry.
+ */
+int
+urp_modrdn_operation( Slapi_PBlock *pb )
+{
+ slapi_operation_parameters *op_params = NULL;
+ Slapi_Entry *parent_entry;
+ Slapi_Entry *new_parent_entry;
+ Slapi_DN *newsuperior = NULL;
+ char *newsuperiordn;
+ Slapi_DN *parentdn = NULL;
+ Slapi_Entry *target_entry;
+ Slapi_Entry *existing_entry;
+ const CSN *target_entry_dncsn;
+ CSN *opcsn= NULL;
+ char *op_uniqueid = NULL;
+ const char *existing_uniqueid = NULL;
+ const char *target_dn;
+ const char *existing_dn;
+ char *newrdn;
+ char sessionid[REPL_SESSION_ID_SIZE];
+ int r;
+ int op_result= 0;
+ int rc= 0; /* OK */
+ int del_old_replconflict_attr = 0;
+
+ if ( slapi_op_abandoned(pb) )
+ {
+ return rc;
+ }
+
+ slapi_pblock_get (pb, SLAPI_MODRDN_TARGET_ENTRY, &target_entry);
+ if(target_entry==NULL)
+ {
+ /* An entry can't be found for the Unique Identifier */
+ op_result= LDAP_NO_SUCH_OBJECT;
+ slapi_pblock_set(pb, SLAPI_RESULT_CODE, &op_result);
+ rc= -1; /* No entry to modrdn */
+ PROFILE_POINT; /* ModRDN Conflict; Entry does not Exist; Discard ModRDN */
+ goto bailout;
+ }
+
+ get_repl_session_id (pb, sessionid, &opcsn);
+ target_entry_dncsn = entry_get_dncsn (target_entry);
+ if ( csn_compare (target_entry_dncsn, opcsn) >= 0 )
+ {
+ /*
+ * The Operation CSN is not newer than the DN CSN.
+ * Either we're beaten by another ModRDN or we've applied the op.
+ */
+ op_result= LDAP_SUCCESS;
+ slapi_pblock_set(pb, SLAPI_RESULT_CODE, &op_result);
+ rc= -1; /* Ignore the modrdn */
+ PROFILE_POINT; /* ModRDN Conflict; Entry with Target DN Exists; OPCSN is not newer. */
+ goto bailout;
+ }
+
+ /* The DN CSN is older than the Operation CSN. Apply the operation */
+ target_dn = slapi_entry_get_dn_const ( target_entry);
+ slapi_pblock_get(pb, SLAPI_MODRDN_NEWRDN, &newrdn);
+ slapi_pblock_get(pb, SLAPI_TARGET_UNIQUEID, &op_uniqueid);
+ slapi_pblock_get(pb, SLAPI_MODRDN_PARENT_ENTRY, &parent_entry);
+ slapi_pblock_get(pb, SLAPI_MODRDN_NEWPARENT_ENTRY, &new_parent_entry);
+ slapi_pblock_get(pb, SLAPI_MODRDN_NEWSUPERIOR, &newsuperiordn);
+
+ if ( is_tombstone_entry (target_entry) )
+ {
+ /*
+ * It is a non-trivial task to rename a tombstone.
+ * This op has been ignored so far by
+ * setting SLAPI_RESULT_CODE to LDAP_NO_SUCH_OBJECT
+ * and rc to -1.
+ */
+
+ /* Turn the tombstone to glue before rename it */
+ /*
+ op_result = tombstone_to_glue (pb, sessionid, target_entry,
+ slapi_entry_get_sdn (target_entry), "renameTombstone", opcsn);
+ */
+ op_result = LDAP_NO_SUCH_OBJECT;
+ slapi_pblock_set(pb, SLAPI_RESULT_CODE, &op_result);
+ if (op_result == 0)
+ {
+ /*
+ * Remember to turn this entry back to tombstone in post op.
+ * We'll just borrow an obsolete pblock type here.
+ */
+ slapi_pblock_set (pb, SLAPI_URP_TOMBSTONE_UNIQUEID, strdup(op_uniqueid));
+ rc= slapi_setbit_int(rc,SLAPI_RTN_BIT_FETCH_TARGET_ENTRY);
+ rc = 0;
+ }
+ else
+ {
+ rc = -1;
+ }
+ PROFILE_POINT; /* ModRDN Conflict; Entry with Target DN Exists; OPCSN is not newer. */
+ goto bailout;
+ }
+
+ slapi_pblock_get(pb, SLAPI_MODRDN_EXISTING_ENTRY, &existing_entry);
+ if(existing_entry!=NULL)
+ {
+ /*
+ * An entry with the target DN already exists.
+ * The smaller dncsn wins. The loser changes its RDN to
+ * uniqueid+baserdn, and adds operational attribute
+ * ATTR_NSDS5_REPLCONFLIC
+ */
+
+ existing_uniqueid = slapi_entry_get_uniqueid (existing_entry);
+ existing_dn = slapi_entry_get_dn_const ( existing_entry);
+
+ /*
+ * Dismiss the operation if the existing entry is the same as the target one.
+ */
+ if (strcmp(op_uniqueid, existing_uniqueid) == 0) {
+ op_result= LDAP_SUCCESS;
+ slapi_pblock_set(pb, SLAPI_RESULT_CODE, &op_result);
+ rc = -1; /* Ignore the op */
+ PROFILE_POINT; /* ModRDN Replay */
+ goto bailout;
+ }
+
+ r= csn_compare ( entry_get_dncsn (existing_entry), opcsn);
+ if (r == 0)
+ {
+ /*
+ * The CSN of the Operation and the Entry DN are the same
+ * but the uniqueids are not.
+ * There might be two replicas with the same ReplicaID.
+ */
+ slapi_log_error(SLAPI_LOG_FATAL, sessionid,
+ "Duplicated CSN for different uniqueids [%s][%s]",
+ existing_uniqueid, op_uniqueid);
+ op_result= LDAP_OPERATIONS_ERROR;
+ slapi_pblock_set(pb, SLAPI_RESULT_CODE, &op_result);
+ rc= -1; /* Abort */
+ PROFILE_POINT; /* ModRDN Conflict; Duplicated CSN for Different Entries */
+ goto bailout;
+ }
+
+ if(r<0)
+ {
+ /* The target entry is a loser */
+
+ char *newrdn_with_uniqueid;
+ newrdn_with_uniqueid= get_rdn_plus_uniqueid (sessionid, newrdn, op_uniqueid);
+ if(newrdn_with_uniqueid==NULL)
+ {
+ op_result= LDAP_OPERATIONS_ERROR;
+ slapi_pblock_set(pb, SLAPI_RESULT_CODE, &op_result);
+ rc= -1; /* Ignore this Operation */
+ PROFILE_POINT; /* ModRDN Conflict; Entry with Target DN Exists;
+ Unique ID already in RDN - Change to Lost and Found entry */
+ goto bailout;
+ }
+ mod_namingconflict_attr (op_uniqueid, target_dn, existing_dn, opcsn);
+ set_pblock_dn (pb, SLAPI_MODRDN_NEWRDN, newrdn_with_uniqueid);
+ slapi_log_error(slapi_log_urp, sessionid,
+ "Naming conflict MODRDN. Rename target entry to %s\n",
+ newrdn_with_uniqueid );
+
+ rc= slapi_setbit_int(rc,SLAPI_RTN_BIT_FETCH_EXISTING_DN_ENTRY);
+ PROFILE_POINT; /* ModRDN Conflict; Entry with Target DN Exists; Rename Operation Entry */
+ goto bailout;
+ }
+
+ if ( r>0 )
+ {
+ /* The existing entry is a loser */
+
+ int resolve = urp_annotate_dn (sessionid, existing_entry, opcsn, "MODRDN");
+ if(!resolve)
+ {
+ op_result= LDAP_OPERATIONS_ERROR;
+ slapi_pblock_set(pb, SLAPI_RESULT_CODE, &op_result);
+ rc= -1; /* Abort this Operation */
+ goto bailout;
+ }
+ rc= slapi_setbit_int(rc,SLAPI_RTN_BIT_FETCH_EXISTING_DN_ENTRY);
+ rc= slapi_setbit_int(rc,SLAPI_RTN_BIT_FETCH_NEWPARENT_ENTRY);
+ if (LDAP_NO_SUCH_OBJECT == resolve) {
+ /* This means that existing_dn_entry did not really exist!!!
+ * This indicates that a get_copy_of_entry -> dn2entry returned
+ * an entry (existing_dn_entry) that was already removed from the ldbm.
+ * This is bad, because it indicates a dn cache or DB corruption.
+ * However, as far as the conflict is concerned, this error is harmless:
+ * if the existing_dn_entry did not exist in the first place, there was no
+ * conflict!! Return 0 for success to break the ldbm_back_modrdn loop
+ * and get out of this inexistent conflict resolution ASAP.
+ */
+ rc = 0;
+ }
+ /* Set flag to remove possible old naming conflict */
+ del_old_replconflict_attr = 1;
+ PROFILE_POINT; /* ModRDN Conflict; Entry with Target DN Exists; Rename Entry with Target DN */
+ goto bailout;
+ }
+ }
+ else
+ {
+ /*
+ * No entry with the target DN exists.
+ */
+
+ /* Set flag to remove possible old naming conflict */
+ del_old_replconflict_attr = 1;
+
+ if(new_parent_entry!=NULL)
+ {
+ /* The new superior entry exists */
+ rc= 0; /* OK, Apply the ModRDN */
+ PROFILE_POINT; /* ModRDN Conflict; OK */
+ goto bailout;
+ }
+
+ /* The new superior entry doesn't exist */
+
+ slapi_pblock_get(pb, SLAPI_MODRDN_NEWSUPERIOR, &newsuperiordn);
+ if(newsuperiordn == NULL)
+ {
+ /* (new_parent_entry==NULL && newsuperiordn==NULL)
+ * This is ok - SLAPI_MODRDN_NEWPARENT_ENTRY will
+ * only be set if SLAPI_MODRDN_NEWSUPERIOR was
+ * suplied by the client. If it wasn't, we're just
+ * changing the RDN of the entry. In that case,
+ * if the entry exists, its parent won't change
+ * when it's renamed, and therefore we can assume
+ * its parent exists.
+ */
+ rc=0;
+ PROFILE_POINT; /* ModRDN OK */
+ goto bailout;
+ }
+
+ newsuperior= slapi_sdn_new_dn_byval(newsuperiordn);
+
+ if((0 == slapi_sdn_compare (slapi_entry_get_sdn(parent_entry), newsuperior)) ||
+ is_suffix_dn (pb, newsuperior, &parentdn) )
+ {
+ /*
+ * The new superior is the same as the current one, or
+ * this entry is a suffix whose parent can be absent.
+ */
+ rc= 0; /* OK, Move the entry */
+ PROFILE_POINT; /* ModRDN Conflict; Absent Target Parent; Create Suffix Entry */
+ goto bailout;
+ }
+
+ /*
+ * This entry is not a suffix entry, so the parent entry should exist.
+ * (This shouldn't happen in a ds5 server)
+ */
+ slapi_pblock_get ( pb, SLAPI_OPERATION_PARAMETERS, &op_params );
+ op_result = create_glue_entry (pb, sessionid, newsuperior,
+ op_params->p.p_modrdn.modrdn_newsuperior_address.uniqueid, opcsn);
+ if (LDAP_SUCCESS != op_result)
+ {
+ /*
+ * FATAL ERROR
+ * We should probably just abort the rename
+ * this will cause replication divergence requiring
+ * admin intercession
+ */
+ slapi_log_error( SLAPI_LOG_FATAL, sessionid,
+ "Parent %s couldn't be found, nor recreated as a glue entry\n", newsuperiordn );
+ op_result= LDAP_OPERATIONS_ERROR;
+ slapi_pblock_set(pb, SLAPI_RESULT_CODE, &op_result);
+ rc = -1;
+ PROFILE_POINT;
+ goto bailout;
+ }
+
+ /* The backend add code should now search for the parent again. */
+ rc= slapi_setbit_int(rc,SLAPI_RTN_BIT_FETCH_NEWPARENT_ENTRY);
+ PROFILE_POINT; /* ModRDN Conflict; Absent Target Parent - Change to Lost and Found entry */
+ goto bailout;
+ }
+
+bailout:
+ if ( del_old_replconflict_attr && rc == 0 )
+ {
+ del_replconflict_attr (target_entry, opcsn, 0);
+ }
+ if ( parentdn )
+ slapi_sdn_free(&parentdn);
+ if ( newsuperior )
+ slapi_sdn_free(&newsuperior);
+ return rc;
+}
+
+/*
+ * Return 0 for OK, -1 for Error
+ */
+int
+urp_delete_operation( Slapi_PBlock *pb )
+{
+ Slapi_Entry *deleteentry;
+ CSN *opcsn= NULL;
+ char sessionid[REPL_SESSION_ID_SIZE];
+ int op_result= 0;
+ int rc= 0; /* OK */
+
+ if ( slapi_op_abandoned(pb) )
+ {
+ return rc;
+ }
+
+ slapi_pblock_get(pb, SLAPI_DELETE_EXISTING_ENTRY, &deleteentry);
+
+ if(deleteentry==NULL) /* uniqueid can't be found */
+ {
+ op_result= LDAP_NO_SUCH_OBJECT;
+ slapi_pblock_set(pb, SLAPI_RESULT_CODE, &op_result);
+ rc= -1; /* Don't apply the Delete */
+ PROFILE_POINT; /* Delete Operation; Entry not exist. */
+ }
+ else if(is_tombstone_entry(deleteentry))
+ {
+ /* The entry is already a Tombstone, ignore this delete. */
+ op_result= LDAP_SUCCESS;
+ slapi_pblock_set(pb, SLAPI_RESULT_CODE, &op_result);
+ rc = -1; /* Don't apply the Delete */
+ PROFILE_POINT; /* Delete Operation; Already a Tombstone. */
+ }
+ else /* The entry to be deleted exists and is not a tombstone */
+ {
+ get_repl_session_id (pb, sessionid, &opcsn);
+
+ /* Check if the entry has children. */
+ if(!slapi_entry_has_children(deleteentry))
+ {
+ /* Remove possible conflict attributes */
+ del_replconflict_attr (deleteentry, opcsn, 0);
+ rc= 0; /* OK, to delete the entry */
+ PROFILE_POINT; /* Delete Operation; OK. */
+ }
+ else
+ {
+ /* Turn this entry into a glue_absent_parent entry */
+ entry_to_glue(sessionid, deleteentry, REASON_RESURRECT_ENTRY, opcsn);
+
+ /* Turn the Delete into a No-Op */
+ op_result= LDAP_SUCCESS;
+ slapi_pblock_set(pb, SLAPI_RESULT_CODE, &op_result);
+ rc = -1; /* Don't apply the Delete */
+ PROFILE_POINT; /* Delete Operation; Entry has children. */
+ }
+ }
+ return rc;
+}
+
+int urp_post_modrdn_operation (Slapi_PBlock *pb)
+{
+ CSN *opcsn;
+ char sessionid[REPL_SESSION_ID_SIZE];
+ char *tombstone_uniqueid;
+ Slapi_Entry *postentry;
+ Slapi_Operation *op;
+
+ /*
+ * Do not abandon the post op - the processed CSN needs to be
+ * committed to keep the consistency between the changelog
+ * and the backend DB.
+ * if ( slapi_op_abandoned(pb) ) return 0;
+ */
+
+ slapi_pblock_get (pb, SLAPI_URP_TOMBSTONE_UNIQUEID, &tombstone_uniqueid );
+ if (tombstone_uniqueid == NULL)
+ {
+ /*
+ * The entry is not resurrected from tombstone. Hence
+ * we need to check if any naming conflict with its
+ * old dn can be resolved.
+ */
+ slapi_pblock_get( pb, SLAPI_OPERATION, &op);
+ if (!operation_is_flag_set(op, OP_FLAG_REPL_FIXUP))
+ {
+ get_repl_session_id (pb, sessionid, &opcsn);
+ urp_naming_conflict_removal (pb, sessionid, opcsn, "MODRDN");
+ }
+ }
+ else
+ {
+ /*
+ * The entry was a resurrected tombstone.
+ * This could happen when we applied a rename
+ * to a tombstone to avoid server divergence. Now
+ * it's time to put the entry back to tombstone.
+ */
+ slapi_pblock_get ( pb, SLAPI_ENTRY_POST_OP, &postentry );
+ if (postentry && strcmp(tombstone_uniqueid, slapi_entry_get_uniqueid(postentry)) == 0)
+ {
+ entry_to_tombstone (pb, postentry);
+ }
+ slapi_ch_free ((void**)&tombstone_uniqueid);
+ slapi_pblock_set (pb, SLAPI_URP_TOMBSTONE_UNIQUEID, NULL);
+ }
+
+ return 0;
+}
+
+/*
+ * Conflict removal
+ */
+int
+urp_post_delete_operation( Slapi_PBlock *pb )
+{
+ Slapi_Operation *op;
+ Slapi_Entry *entry;
+ CSN *opcsn;
+ char sessionid[REPL_SESSION_ID_SIZE];
+ int op_result;
+
+ /*
+ * Do not abandon the post op - the processed CSN needs to be
+ * committed to keep the consistency between the changelog
+ * and the backend DB
+ * if ( slapi_op_abandoned(pb) ) return 0;
+ */
+
+ get_repl_session_id (pb, sessionid, &opcsn);
+
+ /*
+ * Conflict removal from the parent entry:
+ * If the parent is glue and has no more children,
+ * turn the parent to tombstone
+ */
+ slapi_pblock_get ( pb, SLAPI_DELETE_GLUE_PARENT_ENTRY, &entry );
+ if ( entry != NULL )
+ {
+ op_result = entry_to_tombstone ( pb, entry );
+ if ( op_result == LDAP_SUCCESS )
+ {
+ slapi_log_error ( slapi_log_urp, sessionid,
+ "Tombstoned glue entry %s since it has no more children\n",
+ slapi_entry_get_dn_const (entry) );
+ }
+ }
+
+ slapi_pblock_get( pb, SLAPI_OPERATION, &op);
+ if (!operation_is_flag_set(op, OP_FLAG_REPL_FIXUP))
+ {
+ /*
+ * Conflict removal from the peers of the old dn
+ */
+ urp_naming_conflict_removal (pb, sessionid, opcsn, "DEL");
+ }
+
+ return 0;
+}
+
+int
+urp_fixup_add_entry (Slapi_Entry *e, const char *target_uniqueid, const char *parentuniqueid, CSN *opcsn, int opflags)
+{
+ Slapi_PBlock *newpb;
+ Slapi_Operation *op;
+ int op_result;
+
+ newpb = slapi_pblock_new ();
+
+ /*
+ * Mark this operation as replicated, so that the front end
+ * doesn't add extra attributes.
+ */
+ slapi_add_entry_internal_set_pb (
+ newpb,
+ e,
+ NULL, /*Controls*/
+ repl_get_plugin_identity ( PLUGIN_MULTIMASTER_REPLICATION ),
+ OP_FLAG_REPLICATED | OP_FLAG_REPL_FIXUP | opflags);
+ if (target_uniqueid)
+ {
+ slapi_pblock_set( newpb, SLAPI_TARGET_UNIQUEID, (void*)target_uniqueid);
+ }
+ if (parentuniqueid)
+ {
+ struct slapi_operation_parameters *op_params;
+ slapi_pblock_get( newpb, SLAPI_OPERATION_PARAMETERS, &op_params );
+ op_params->p.p_add.parentuniqueid = (char*)parentuniqueid; /* Consumes parentuniqueid */
+ }
+ slapi_pblock_get ( newpb, SLAPI_OPERATION, &op );
+ operation_set_csn ( op, opcsn );
+
+ slapi_add_internal_pb ( newpb );
+ slapi_pblock_get ( newpb, SLAPI_PLUGIN_INTOP_RESULT, &op_result );
+ slapi_pblock_destroy ( newpb );
+
+ return op_result;
+}
+
+int
+urp_fixup_rename_entry (Slapi_Entry *entry, const char *newrdn, int opflags)
+{
+ Slapi_PBlock *newpb;
+ Slapi_Operation *op;
+ CSN *opcsn;
+ int op_result;
+
+ newpb = slapi_pblock_new();
+
+ /*
+ * Must mark this operation as replicated,
+ * so that the frontend doesn't add extra attributes.
+ */
+ slapi_rename_internal_set_pb (
+ newpb,
+ slapi_entry_get_dn_const (entry),
+ newrdn, /*NewRDN*/
+ NULL, /*NewSuperior*/
+ 0, /* !Delete Old RDNS */
+ NULL, /*Controls*/
+ slapi_entry_get_uniqueid (entry), /*uniqueid*/
+ repl_get_plugin_identity(PLUGIN_MULTIMASTER_REPLICATION),
+ OP_FLAG_REPLICATED | OP_FLAG_REPL_FIXUP | opflags);
+
+ /* set operation csn to the entry's dncsn */
+ opcsn = (CSN *)entry_get_dncsn (entry);
+ slapi_pblock_get (newpb, SLAPI_OPERATION, &op);
+ operation_set_csn (op, opcsn);
+
+ slapi_modrdn_internal_pb(newpb);
+ slapi_pblock_get(newpb, SLAPI_PLUGIN_INTOP_RESULT, &op_result);
+
+ slapi_pblock_destroy(newpb);
+ return op_result;
+}
+
+int
+urp_fixup_delete_entry (const char *uniqueid, const char *dn, CSN *opcsn, int opflags)
+{
+ Slapi_PBlock *newpb;
+ Slapi_Operation *op;
+ int op_result;
+
+ newpb = slapi_pblock_new ();
+
+ /*
+ * Mark this operation as replicated, so that the front end
+ * doesn't add extra attributes.
+ */
+ slapi_delete_internal_set_pb (
+ newpb,
+ dn,
+ NULL, /*Controls*/
+ uniqueid, /*uniqueid*/
+ repl_get_plugin_identity ( PLUGIN_MULTIMASTER_REPLICATION ),
+ OP_FLAG_REPLICATED | OP_FLAG_REPL_FIXUP | opflags );
+ slapi_pblock_get ( newpb, SLAPI_OPERATION, &op );
+ operation_set_csn ( op, opcsn );
+
+ slapi_delete_internal_pb ( newpb );
+ slapi_pblock_get ( newpb, SLAPI_PLUGIN_INTOP_RESULT, &op_result );
+ slapi_pblock_destroy ( newpb );
+
+ return op_result;
+}
+
+int
+urp_fixup_modify_entry (const char *uniqueid, const char *dn, CSN *opcsn, Slapi_Mods *smods, int opflags)
+{
+ Slapi_PBlock *newpb;
+ Slapi_Operation *op;
+ int op_result;
+
+ newpb = slapi_pblock_new();
+
+ slapi_modify_internal_set_pb (
+ newpb,
+ dn,
+ slapi_mods_get_ldapmods_byref (smods),
+ NULL, /* Controls */
+ uniqueid,
+ repl_get_plugin_identity (PLUGIN_MULTIMASTER_REPLICATION),
+ OP_FLAG_REPLICATED | OP_FLAG_REPL_FIXUP | opflags);
+
+ /* set operation csn */
+ slapi_pblock_get (newpb, SLAPI_OPERATION, &op);
+ operation_set_csn (op, opcsn);
+
+ /* do modify */
+ slapi_modify_internal_pb (newpb);
+ slapi_pblock_get (newpb, SLAPI_PLUGIN_INTOP_RESULT, &op_result);
+ slapi_pblock_destroy(newpb);
+
+ return op_result;
+}
+
+static int
+urp_add_resolve_parententry (Slapi_PBlock *pb, char *sessionid, Slapi_Entry *entry, Slapi_Entry *parententry, CSN *opcsn)
+{
+ Slapi_DN *parentdn = NULL;
+ Slapi_RDN *add_rdn = NULL;
+ char *newdn = NULL;
+ int ldap_rc;
+ int rc = 0;
+
+ if( is_suffix_entry (pb, entry, &parentdn) )
+ {
+ /* It's OK for the suffix entry's parent to be absent */
+ rc= 0;
+ PROFILE_POINT; /* Add Conflict; Suffix Entry */
+ goto bailout;
+ }
+
+ /* The entry is not a suffix. */
+ if(parententry==NULL) /* The parent entry was not found. */
+ {
+ /* Create a glue entry to stand in for the absent parent */
+ slapi_operation_parameters *op_params;
+ slapi_pblock_get( pb, SLAPI_OPERATION_PARAMETERS, &op_params );
+ ldap_rc = create_glue_entry (pb, sessionid, parentdn, op_params->p.p_add.parentuniqueid, opcsn);
+ if ( LDAP_SUCCESS == ldap_rc )
+ {
+ /* The backend code should now search for the parent again. */
+ rc= slapi_setbit_int(rc,SLAPI_RTN_BIT_FETCH_EXISTING_DN_ENTRY);
+ rc= slapi_setbit_int(rc,SLAPI_RTN_BIT_FETCH_PARENT_ENTRY);
+ PROFILE_POINT; /* Add Conflict; Orphaned Entry; Glue Parent */
+ }
+ else
+ {
+ /*
+ * Error. The parent can't be created as a glue entry.
+ * This will cause replication divergence and will
+ * require admin intercession
+ */
+ ldap_rc= LDAP_OPERATIONS_ERROR;
+ slapi_pblock_set(pb, SLAPI_RESULT_CODE, &ldap_rc);
+ rc= -1; /* Abort this Operation */
+ PROFILE_POINT; /* Add Conflict; Orphaned Entry; Impossible to create parent; Refuse Change. */
+ }
+ goto bailout;
+ }
+
+ if(is_tombstone_entry(parententry)) /* The parent is a tombstone */
+ {
+ /* The parent entry must be resurected from the dead. */
+ ldap_rc = tombstone_to_glue (pb, sessionid, parententry, parentdn, REASON_RESURRECT_ENTRY, opcsn);
+ if ( ldap_rc != LDAP_SUCCESS )
+ {
+ ldap_rc= LDAP_OPERATIONS_ERROR;
+ slapi_pblock_set(pb, SLAPI_RESULT_CODE, &ldap_rc);
+ rc = -1; /* Abort the operation */
+ }
+ else
+ {
+ /* The backend add code should now search for the parent again. */
+ rc= slapi_setbit_int(rc,SLAPI_RTN_BIT_FETCH_EXISTING_DN_ENTRY);
+ rc= slapi_setbit_int(rc,SLAPI_RTN_BIT_FETCH_PARENT_ENTRY);
+ }
+ PROFILE_POINT; /* Add Conflict; Orphaned Entry; Parent Was Tombstone */
+ goto bailout;
+ }
+
+ /* The parent is healthy */
+ /* Now we need to check that the parent has the correct DN */
+ if (slapi_sdn_isparent(slapi_entry_get_sdn(parententry), slapi_entry_get_sdn(entry)))
+ {
+ rc= 0; /* OK, Add the entry */
+ PROFILE_POINT; /* Add Conflict; Parent Exists */
+ goto bailout;
+ }
+
+ /*
+ * Parent entry doesn't have a DN parent to the entry.
+ * This can happen if parententry was renamed due to
+ * conflict and the child entry was created before
+ * replication occured. See defect 530942.
+ * We need to rename the entry to be child of its parent.
+ */
+ add_rdn = slapi_rdn_new_dn(slapi_entry_get_dn_const (entry));
+ newdn = slapi_dn_plus_rdn(slapi_entry_get_dn_const (parententry), slapi_rdn_get_rdn(add_rdn));
+ slapi_entry_set_dn ( entry,slapi_ch_strdup(newdn));
+ set_pblock_dn (pb,SLAPI_ADD_TARGET,newdn); /* consumes newdn */
+ slapi_log_error ( slapi_log_urp, sessionid,
+ "Parent was renamed. Renamed the child to %s\n", newdn );
+ rc= slapi_setbit_int(rc,SLAPI_RTN_BIT_FETCH_EXISTING_DN_ENTRY);
+ PROFILE_POINT; /* Add Conflict; Parent Renamed; Rename Operation Entry */
+
+bailout:
+ if (parentdn)
+ slapi_sdn_free(&parentdn);
+ return rc;
+}
+
+/*
+ * urp_annotate_dn:
+ * Returns 0 on failure
+ * Returns > 0 on success (1 on general conflict resolution success, LDAP_NO_SUCH_OBJECT on no-conflict success)
+ *
+ * Use this function to annotate an existing entry only. To annotate
+ * a new entry (the operation entry) see urp_add_operation.
+ */
+static int
+urp_annotate_dn (char *sessionid, Slapi_Entry *entry, CSN *opcsn, const char *optype)
+{
+ int rc = 0; /* Fail */
+ int op_result;
+ char *newrdn;
+ const char *uniqueid;
+ const char *basedn;
+ char ebuf[BUFSIZ];
+
+ uniqueid = slapi_entry_get_uniqueid (entry);
+ basedn = slapi_entry_get_ndn (entry);
+ newrdn = get_rdn_plus_uniqueid ( sessionid, basedn, uniqueid );
+ if(newrdn!=NULL)
+ {
+ mod_namingconflict_attr (uniqueid, basedn, basedn, opcsn);
+ op_result = urp_fixup_rename_entry ( entry, newrdn, 0 );
+ switch(op_result)
+ {
+ case LDAP_SUCCESS:
+ slapi_log_error(slapi_log_urp, sessionid,
+ "Naming conflict %s. Renamed existing entry to %s\n",
+ optype, escape_string (newrdn, ebuf));
+ rc = 1;
+ break;
+ case LDAP_NO_SUCH_OBJECT:
+ /* This means that entry did not really exist!!!
+ * This is clearly indicating that there is a
+ * get_copy_of_entry -> dn2entry returned
+ * an entry (entry) that was already removed
+ * from the ldbm database...
+ * This is bad, because it clearly indicates
+ * some kind of db or cache corruption. We need to print
+ * this fact clearly in the errors log to try
+ * to solve this corruption one day.
+ * However, as far as the conflict is concerned,
+ * this error is completely harmless:
+ * if thew entry did not exist in the first place,
+ * there was never a room
+ * for a conflict!! After fix for 558293, this
+ * state can't be reproduced anymore (5-Oct-01)
+ */
+ slapi_log_error( SLAPI_LOG_FATAL, sessionid,
+ "Entry %s exists in cache but not in DB\n",
+ escape_string (basedn, ebuf) );
+ rc = LDAP_NO_SUCH_OBJECT;
+ break;
+ default:
+ slapi_log_error( slapi_log_urp, sessionid,
+ "Failed to annotate %s, err=%d\n", newrdn, op_result);
+ }
+ slapi_ch_free ( (void**)&newrdn );
+ }
+ return rc;
+}
+
+/*
+ * An URP Naming Collision helper function. Retreives a list of entries
+ * that have the given dn excluding the unique id of the entry. Any
+ * entries returned will be entries that have been added with the same
+ * dn, but caused a naming conflict when replicated. The URP to fix
+ * this constraint violation is to append the unique id of the entry
+ * to its RDN.
+ */
+static Slapi_Entry *
+urp_get_min_naming_conflict_entry ( Slapi_PBlock *pb, char *sessionid, CSN *opcsn )
+{
+ Slapi_PBlock *newpb = NULL;
+ LDAPControl **server_ctrls = NULL;
+ Slapi_Entry **entries = NULL;
+ Slapi_Entry *min_naming_conflict_entry = NULL;
+ const CSN *min_csn = NULL;
+ char *filter = NULL;
+ char *parent_dn = NULL;
+ char *basedn;
+ int i = 0;
+ int min_i = -1;
+ int op_result = LDAP_SUCCESS;
+
+ slapi_pblock_get (pb, SLAPI_URP_NAMING_COLLISION_DN, &basedn);
+ if (NULL == basedn || strncmp (basedn, SLAPI_ATTR_UNIQUEID, strlen(SLAPI_ATTR_UNIQUEID)) == 0)
+ return NULL;
+
+ slapi_log_error ( SLAPI_LOG_REPL, sessionid,
+ "Enter urp_get_min_naming_conflict_entry for %s\n", basedn);
+
+ filter = slapi_ch_malloc(50 + strlen(basedn));
+ sprintf(filter, "(%s=%s %s)", ATTR_NSDS5_REPLCONFLICT, REASON_ANNOTATE_DN, basedn);
+
+ /* server_ctrls will be freed when newpb is destroyed */
+ server_ctrls = (LDAPControl **)slapi_ch_calloc (2, sizeof (LDAPControl *));
+ server_ctrls[0] = create_managedsait_control();
+ server_ctrls[1] = NULL;
+
+ newpb = slapi_pblock_new();
+ parent_dn = slapi_dn_parent (basedn);
+ slapi_search_internal_set_pb(newpb,
+ parent_dn, /* Base DN */
+ LDAP_SCOPE_ONELEVEL,
+ filter,
+ NULL, /* Attrs */
+ 0, /* AttrOnly */
+ server_ctrls, /* Controls */
+ NULL, /* UniqueID */
+ repl_get_plugin_identity(PLUGIN_MULTIMASTER_REPLICATION),
+ 0);
+ slapi_search_internal_pb(newpb);
+ slapi_pblock_get(newpb, SLAPI_PLUGIN_INTOP_RESULT, &op_result);
+ slapi_pblock_get(newpb, SLAPI_PLUGIN_INTOP_SEARCH_ENTRIES, &entries);
+ if ( (op_result != LDAP_SUCCESS) || (entries == NULL) )
+ {
+ /* Log a message */
+ goto done;
+ }
+ /* For all entries, get the one with the smallest dn csn */
+ for (i = 0; NULL != entries[i]; i++)
+ {
+ const CSN *dncsn;
+ dncsn = entry_get_dncsn(entries[i]);
+ if ((dncsn != opcsn) &&
+ ((min_csn == NULL) || (csn_compare(dncsn, min_csn) < 0)) &&
+ !is_tombstone_entry (entries[i]))
+ {
+ min_csn = dncsn;
+ min_i = i;
+ }
+ /*
+ * If there are too many conflicts, the current urp code has no
+ * guarantee for all servers to converge anyway, because the
+ * urp and the backend can't be done in one transaction due
+ * to either performance or the deadlock problem.
+ * Don't sacrifice the performance too much for impossible.
+ */
+ if (min_csn && i > 5)
+ {
+ break;
+ }
+ }
+
+ if (min_csn != NULL) {
+ /* Found one entry */
+ min_naming_conflict_entry = slapi_entry_dup(entries[min_i]);
+ }
+
+done:
+ slapi_ch_free((void **)&parent_dn);
+ slapi_ch_free((void **)&filter);
+ slapi_free_search_results_internal(newpb);
+ slapi_pblock_destroy(newpb);
+ newpb = NULL;
+
+ slapi_log_error ( SLAPI_LOG_REPL, sessionid,
+ "Leave urp_get_min_naming_conflict_entry (found %d entries)\n", i);
+
+ return min_naming_conflict_entry;
+}
+
+/*
+ * If an entry is deleted or renamed, a new winner may be
+ * chosen from its naming competitors.
+ * The entry with the smallest dncsn restores its original DN.
+ */
+static int
+urp_naming_conflict_removal ( Slapi_PBlock *pb, char *sessionid, CSN *opcsn, const char *optype )
+{
+ Slapi_Entry *min_naming_conflict_entry;
+ Slapi_RDN *oldrdn, *newrdn;
+ const char *oldrdnstr, *newrdnstr;
+ int op_result;
+
+ /*
+ * Backend op has set SLAPI_URP_NAMING_COLLISION_DN to the basedn.
+ */
+ min_naming_conflict_entry = urp_get_min_naming_conflict_entry (pb, sessionid, opcsn);
+ if (min_naming_conflict_entry == NULL)
+ {
+ return 0;
+ }
+
+ /* Step 1: Restore the entry's original DN */
+
+ oldrdn = slapi_rdn_new_sdn ( slapi_entry_get_sdn (min_naming_conflict_entry) );
+ oldrdnstr = slapi_rdn_get_rdn ( oldrdn );
+
+ /* newrdnstr is the old rdn of the entry minus the nsuniqueid part */
+ newrdn = slapi_rdn_new_rdn ( oldrdn );
+ slapi_rdn_remove_attr (newrdn, SLAPI_ATTR_UNIQUEID );
+ newrdnstr = slapi_rdn_get_rdn ( newrdn );
+
+ /*
+ * Set OP_FLAG_ACTION_INVOKE_FOR_REPLOP since this operation
+ * is done after DB lock was released. The backend modrdn
+ * will acquire the DB lock if it sees this flag.
+ */
+ op_result = urp_fixup_rename_entry (min_naming_conflict_entry, newrdnstr, OP_FLAG_ACTION_INVOKE_FOR_REPLOP);
+ if ( op_result != LDAP_SUCCESS )
+ {
+ slapi_log_error (slapi_log_urp, sessionid,
+ "Failed to restore RDN of %s, err=%d\n", oldrdnstr, op_result);
+ goto bailout;
+ }
+ slapi_log_error (slapi_log_urp, sessionid,
+ "Naming conflict removed by %s. RDN of %s was restored\n", optype, oldrdnstr);
+
+ /* Step2: Remove ATTR_NSDS5_REPLCONFLICT from the winning entry */
+ /*
+ * A fixup op will not invoke urp_modrdn_operation(). Even it does,
+ * urp_modrdn_operation() will do nothing because of the same CSN.
+ */
+ op_result = del_replconflict_attr (min_naming_conflict_entry, opcsn, OP_FLAG_ACTION_INVOKE_FOR_REPLOP);
+ if (op_result != LDAP_SUCCESS) {
+ slapi_log_error(SLAPI_LOG_REPL, sessionid,
+ "Failed to remove nsds5ReplConflict for %s, err=%d\n",
+ newrdnstr, op_result);
+ }
+
+bailout:
+ slapi_entry_free (min_naming_conflict_entry);
+ slapi_rdn_free(&oldrdn);
+ slapi_rdn_free(&newrdn);
+ return op_result;
+}
+
+/* The returned value is either null or "uniqueid=<uniqueid>+<basedn>" */
+static char *
+get_dn_plus_uniqueid(char *sessionid, const char *olddn, const char *uniqueid)
+{
+ Slapi_DN *sdn= slapi_sdn_new_dn_byval(olddn);
+ Slapi_RDN *rdn= slapi_rdn_new();
+ char *newdn;
+
+ PR_ASSERT(uniqueid!=NULL);
+
+ /* Check if the RDN already contains the Unique ID */
+ slapi_sdn_get_rdn(sdn,rdn);
+ if(slapi_rdn_contains(rdn,SLAPI_ATTR_UNIQUEID,uniqueid,strlen(uniqueid)))
+ {
+ /* The Unique ID is already in the RDN.
+ * This is a highly improbable collision.
+ * It suggests that a duplicate UUID was generated.
+ * This will cause replication divergence and will
+ * require admin intercession
+ */
+ slapi_log_error(SLAPI_LOG_FATAL, sessionid,
+ "Annotated DN %s has naming conflict\n", olddn );
+ newdn= NULL;
+ }
+ else
+ {
+ slapi_rdn_add(rdn,SLAPI_ATTR_UNIQUEID,uniqueid);
+ slapi_sdn_set_rdn(sdn, rdn);
+ newdn= slapi_ch_strdup(slapi_sdn_get_dn(sdn));
+ }
+ slapi_sdn_free(&sdn);
+ slapi_rdn_free(&rdn);
+ return newdn;
+}
+
+static char *
+get_rdn_plus_uniqueid(char *sessionid, const char *olddn, const char *uniqueid)
+{
+ char *newrdn;
+ /* Check if the RDN already contains the Unique ID */
+ Slapi_DN *sdn= slapi_sdn_new_dn_byval(olddn);
+ Slapi_RDN *rdn= slapi_rdn_new();
+ slapi_sdn_get_rdn(sdn,rdn);
+ PR_ASSERT(uniqueid!=NULL);
+ if(slapi_rdn_contains(rdn,SLAPI_ATTR_UNIQUEID,uniqueid,strlen(uniqueid)))
+ {
+ /* The Unique ID is already in the RDN.
+ * This is a highly improbable collision.
+ * It suggests that a duplicate UUID was generated.
+ * This will cause replication divergence and will
+ * require admin intercession
+ */
+ slapi_log_error(SLAPI_LOG_FATAL, sessionid,
+ "Annotated DN %s has naming conflict\n", olddn );
+ newrdn= NULL;
+ }
+ else
+ {
+ slapi_rdn_add(rdn,SLAPI_ATTR_UNIQUEID,uniqueid);
+ newrdn= slapi_ch_strdup(slapi_rdn_get_rdn(rdn));
+ }
+ slapi_sdn_free(&sdn);
+ slapi_rdn_free(&rdn);
+ return newrdn;
+}
+
+static void
+set_pblock_dn (Slapi_PBlock* pb,int pblock_parameter,char *newdn)
+{
+ char *olddn;
+ slapi_pblock_get( pb, pblock_parameter, &olddn );
+ slapi_ch_free((void**)&olddn);
+ slapi_pblock_set( pb, pblock_parameter, newdn );
+}
+
+static int
+is_suffix_entry ( Slapi_PBlock *pb, Slapi_Entry *entry, Slapi_DN **parentdn )
+{
+ return is_suffix_dn ( pb, slapi_entry_get_sdn(entry), parentdn );
+}
+
+int
+is_suffix_dn ( Slapi_PBlock *pb, const Slapi_DN *dn, Slapi_DN **parentdn )
+{
+ Slapi_Backend *backend;
+ int rc;
+
+ *parentdn = slapi_sdn_new();
+ slapi_pblock_get( pb, SLAPI_BACKEND, &backend );
+ slapi_sdn_get_backend_parent (dn, *parentdn, backend);
+
+ /* A suffix entry doesn't have parent dn */
+ rc = slapi_sdn_isempty (*parentdn) ? 1 : 0;
+
+ return rc;
+}
+
+static int
+mod_namingconflict_attr (const char *uniqueid, const char *entrydn, const char *conflictdn, CSN *opcsn)
+{
+ Slapi_Mods smods;
+ char buf[BUFSIZ];
+ int op_result;
+
+ sprintf (buf, "%s %s", REASON_ANNOTATE_DN, conflictdn);
+ slapi_mods_init (&smods, 2);
+ if ( strncmp (entrydn, SLAPI_ATTR_UNIQUEID, strlen(SLAPI_ATTR_UNIQUEID)) != 0 )
+ {
+ slapi_mods_add (&smods, LDAP_MOD_ADD, ATTR_NSDS5_REPLCONFLICT, strlen(buf), buf);
+ }
+ else
+ {
+ /*
+ * If the existing entry is already a naming conflict loser,
+ * the following replace operation should result in the
+ * replace of the ATTR_NSDS5_REPLCONFLICT index as well.
+ */
+ slapi_mods_add (&smods, LDAP_MOD_REPLACE, ATTR_NSDS5_REPLCONFLICT, strlen(buf), buf);
+ }
+ op_result = urp_fixup_modify_entry (uniqueid, entrydn, opcsn, &smods, 0);
+ slapi_mods_done (&smods);
+ return op_result;
+}
+
+static int
+del_replconflict_attr (Slapi_Entry *entry, CSN *opcsn, int opflags)
+{
+ Slapi_Attr *attr;
+ int op_result = 0;
+
+ if (slapi_entry_attr_find (entry, ATTR_NSDS5_REPLCONFLICT, &attr) == 0)
+ {
+ Slapi_Mods smods;
+ const char *uniqueid;
+ const char *entrydn;
+
+ uniqueid = slapi_entry_get_uniqueid (entry);
+ entrydn = slapi_entry_get_dn_const (entry);
+ slapi_mods_init (&smods, 2);
+ slapi_mods_add (&smods, LDAP_MOD_DELETE, ATTR_NSDS5_REPLCONFLICT, 0, NULL);
+ op_result = urp_fixup_modify_entry (uniqueid, entrydn, opcsn, &smods, opflags);
+ slapi_mods_done (&smods);
+ }
+ return op_result;
+}
diff --git a/ldap/servers/plugins/replication/urp.h b/ldap/servers/plugins/replication/urp.h
new file mode 100644
index 00000000..9db477bd
--- /dev/null
+++ b/ldap/servers/plugins/replication/urp.h
@@ -0,0 +1,45 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+/*
+ */
+
+#define REASON_ANNOTATE_DN "namingConflict"
+#define REASON_RESURRECT_ENTRY "deletedEntryHasChildren"
+
+/*
+ * urp.c
+ */
+int urp_modify_operation( Slapi_PBlock *pb );
+int urp_add_operation( Slapi_PBlock *pb );
+int urp_delete_operation( Slapi_PBlock *pb );
+int urp_post_delete_operation( Slapi_PBlock *pb );
+int urp_modrdn_operation( Slapi_PBlock *pb );
+int urp_post_modrdn_operation( Slapi_PBlock *pb );
+
+/* urp internal ops */
+int urp_fixup_add_entry (Slapi_Entry *e, const char *target_uniqueid, const char *parentuniqueid, CSN *opcsn, int opflags);
+int urp_fixup_delete_entry (const char *uniqueid, const char *dn, CSN *opcsn, int opflags);
+int urp_fixup_rename_entry (Slapi_Entry *entry, const char *newrdn, int opflags);
+int urp_fixup_modify_entry (const char *uniqueid, const char *dn, CSN *opcsn, Slapi_Mods *smods, int opflags);
+
+int is_suffix_dn (Slapi_PBlock *pb, const Slapi_DN *dn, Slapi_DN **parenddn);
+
+/*
+ * urp_glue.c
+ */
+int is_glue_entry(const Slapi_Entry* entry);
+int create_glue_entry ( Slapi_PBlock *pb, char *sessionid, Slapi_DN *dn, const char *uniqueid, CSN *opcsn );
+int entry_to_glue(char *sessionid, const Slapi_Entry* entry, const char *reason, CSN *opcsn);
+int glue_to_entry (Slapi_PBlock *pb, Slapi_Entry *entry );
+PRBool get_glue_csn(const Slapi_Entry *entry, const CSN **gluecsn);
+
+/*
+ * urp_tombstone.c
+ */
+int is_tombstone_entry(const Slapi_Entry* entry);
+int tombstone_to_glue(Slapi_PBlock *pb, const char *sessionid, Slapi_Entry *entry, const Slapi_DN *parentdn, const char *reason, CSN *opcsn);
+int entry_to_tombstone ( Slapi_PBlock *pb, Slapi_Entry *entry );
+PRBool get_tombstone_csn(const Slapi_Entry *entry, const CSN **delcsn);
diff --git a/ldap/servers/plugins/replication/urp_glue.c b/ldap/servers/plugins/replication/urp_glue.c
new file mode 100644
index 00000000..dcb2f72d
--- /dev/null
+++ b/ldap/servers/plugins/replication/urp_glue.c
@@ -0,0 +1,235 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+
+/*
+ * urp_glue.c - Update Resolution Procedures - Glue
+ */
+
+#include "slapi-plugin.h"
+#include "repl5.h"
+#include "urp.h"
+
+
+#define RDNBUFSIZE 2048
+extern int slapi_log_urp;
+
+/*
+ * Check if the entry is glue.
+ */
+int
+is_glue_entry(const Slapi_Entry* entry)
+{
+ /* JCMREPL - Is there a more efficient way to do this? */
+ return slapi_entry_attr_hasvalue(entry, SLAPI_ATTR_OBJECTCLASS, "glue");
+}
+
+/* returns PR_TRUE if the entry is a glue entry, PR_FALSE otherwise
+ sets the gluecsn if it is a glue entry - gluecsn may (but should not) be NULL */
+PRBool
+get_glue_csn(const Slapi_Entry *entry, const CSN **gluecsn)
+{
+ PRBool isglue = PR_FALSE;
+ Slapi_Attr *oc_attr = NULL;
+
+ /* cast away const - entry */
+ if (entry_attr_find_wsi((Slapi_Entry*)entry, SLAPI_ATTR_OBJECTCLASS, &oc_attr) == ATTRIBUTE_PRESENT)
+ {
+ Slapi_Value *glue_value = NULL;
+ struct berval v;
+ v.bv_val = "glue";
+ v.bv_len = strlen(v.bv_val);
+ if (attr_value_find_wsi(oc_attr, &v, &glue_value) == VALUE_PRESENT)
+ {
+ isglue = PR_TRUE;
+ *gluecsn = value_get_csn(glue_value, CSN_TYPE_VALUE_UPDATED);
+ }
+ }
+
+ return isglue;
+}
+
+/*
+ * Submit a Modify operation to turn the Entry into Glue.
+ */
+int
+entry_to_glue(char *sessionid, const Slapi_Entry* entry, const char *reason, CSN *opcsn)
+{
+ int op_result = 0;
+ const char *dn;
+ char ebuf[BUFSIZ];
+ slapi_mods smods;
+ Slapi_Attr *attr;
+
+ dn = slapi_entry_get_dn_const (entry);
+ slapi_mods_init(&smods, 4);
+ /*
+ richm: sometimes the entry is already a glue entry (how did that happen?)
+ OR
+ the entry is already objectclass extensibleObject or already has the
+ conflict attribute and/or value
+ */
+ if (!slapi_entry_attr_hasvalue(entry, SLAPI_ATTR_OBJECTCLASS, "glue"))
+ {
+ slapi_mods_add_string( &smods, LDAP_MOD_ADD, SLAPI_ATTR_OBJECTCLASS, "glue" );
+
+ if (!slapi_entry_attr_hasvalue(entry, SLAPI_ATTR_OBJECTCLASS, "extensibleobject"))
+ slapi_mods_add_string( &smods, LDAP_MOD_ADD, SLAPI_ATTR_OBJECTCLASS, "extensibleobject" );
+ }
+ else
+ {
+ slapi_log_error(SLAPI_LOG_REPL, repl_plugin_name,
+ "%s: Target entry %s is already a glue entry reason %s\n",
+ sessionid, escape_string(dn, ebuf), reason);
+ }
+
+ if (slapi_entry_attr_find (entry, ATTR_NSDS5_REPLCONFLICT, &attr) == 0)
+ {
+ slapi_mods_add_string( &smods, LDAP_MOD_REPLACE, ATTR_NSDS5_REPLCONFLICT, reason);
+ }
+ else
+ {
+ slapi_mods_add_string( &smods, LDAP_MOD_ADD, ATTR_NSDS5_REPLCONFLICT, reason);
+ }
+
+ if (slapi_mods_get_num_mods(&smods) > 0)
+ {
+ op_result = urp_fixup_modify_entry (NULL, dn, opcsn, &smods, 0);
+ if (op_result == LDAP_SUCCESS)
+ {
+ slapi_log_error (slapi_log_urp, repl_plugin_name,
+ "%s: Turned the entry %s to glue, reason %s\n",
+ sessionid, escape_string(dn, ebuf), reason);
+ }
+ }
+
+ slapi_mods_done(&smods);
+ return op_result;
+}
+
+static const char *glue_entry =
+ "dn: %s\n"
+ "%s"
+ "objectclass: top\n"
+ "objectclass: extensibleObject\n" /* JCMREPL - To avoid schema checking. */
+ "objectclass: glue\n"
+ "nsuniqueid: %s\n"
+ "%s: %s\n"; /* Add why it's been created */
+
+static int
+do_create_glue_entry(const Slapi_RDN *rdn, const Slapi_DN *superiordn, const char *uniqueid, const char *reason, CSN *opcsn)
+{
+ int op_result= LDAP_OPERATIONS_ERROR;
+ int rdnval_index = 0;
+ int rdntype_len, rdnval_len, rdnpair_len, rdnstr_len, alloc_len;
+ Slapi_Entry *e;
+ Slapi_DN *sdn = NULL;
+ Slapi_RDN *newrdn = slapi_rdn_new_rdn(rdn);
+ char *estr, *rdnstr, *rdntype, *rdnval, *rdnpair;
+ sdn = slapi_sdn_new_dn_byval(slapi_sdn_get_ndn(superiordn));
+ slapi_sdn_add_rdn(sdn,rdn);
+
+
+ /* must take care of multi-valued rdn: split rdn into different lines introducing
+ * '\n' between each type/value pair.
+ */
+ alloc_len = RDNBUFSIZE;
+ rdnstr = slapi_ch_malloc(alloc_len);
+ rdnpair = rdnstr;
+ *rdnpair = '\0'; /* so that strlen(rdnstr) may return 0 the first time it's called */
+ while ((rdnval_index = slapi_rdn_get_next(newrdn, rdnval_index, &rdntype, &rdnval)) != -1) {
+ rdntype_len = strlen(rdntype);
+ rdnval_len = strlen(rdnval);
+ rdnpair_len = LDIF_SIZE_NEEDED(rdntype_len, rdnval_len);
+ rdnstr_len = strlen(rdnstr);
+ if ((rdnstr_len + rdnpair_len + 1) > alloc_len) {
+ alloc_len += (rdnpair_len + 1);
+ rdnstr = slapi_ch_realloc(rdnstr, alloc_len);
+ rdnpair = &rdnstr[rdnstr_len];
+ }
+ ldif_put_type_and_value_with_options(&rdnpair, rdntype,
+ rdnval, rdnval_len, LDIF_OPT_NOWRAP);
+ *rdnpair = '\0';
+ }
+ estr= slapi_ch_malloc(strlen(glue_entry) + slapi_sdn_get_ndn_len(sdn) +
+ strlen(rdnstr) + strlen(uniqueid) +
+ strlen(ATTR_NSDS5_REPLCONFLICT) + strlen(reason) + 1);
+ sprintf(estr, glue_entry, slapi_sdn_get_ndn(sdn), rdnstr, uniqueid,
+ ATTR_NSDS5_REPLCONFLICT, reason);
+ slapi_ch_free((void**)&rdnstr);
+ slapi_rdn_done(newrdn);
+ slapi_ch_free((void**)&newrdn);
+ e = slapi_str2entry( estr, 0 );
+ PR_ASSERT(e!=NULL);
+ if ( e!=NULL )
+ {
+ slapi_entry_set_uniqueid (e, slapi_ch_strdup(uniqueid));
+ op_result = urp_fixup_add_entry (e, NULL, NULL, opcsn, 0);
+ slapi_ch_free ( (void **) &estr ); /* XXXggood - this leaks if e == NULL */
+ }
+ slapi_sdn_free(&sdn);
+ return op_result;
+}
+
+int
+create_glue_entry ( Slapi_PBlock *pb, char *sessionid, Slapi_DN *dn, const char *uniqueid, CSN *opcsn )
+{
+ int op_result;
+ const char *dnstr;
+
+ if ( slapi_sdn_get_dn (dn) )
+ dnstr = slapi_sdn_get_dn (dn);
+ else
+ dnstr = "";
+
+ if ( NULL == uniqueid )
+ {
+ op_result = LDAP_OPERATIONS_ERROR;
+ slapi_log_error (SLAPI_LOG_FATAL, repl_plugin_name,
+ "%s: Can't create glue %s, uniqueid=NULL\n", sessionid, dnstr);
+ }
+ else
+ {
+ Slapi_Backend *backend;
+ Slapi_DN *superiordn = slapi_sdn_new();
+ Slapi_RDN *rdn= slapi_rdn_new();
+ int done= 0;
+
+ slapi_pblock_get( pb, SLAPI_BACKEND, &backend );
+ slapi_sdn_get_backend_parent ( dn, superiordn, backend );
+ slapi_sdn_get_rdn ( dn, rdn );
+
+ while(!done)
+ {
+ op_result= do_create_glue_entry(rdn, superiordn, uniqueid, "missingEntry", opcsn);
+ switch(op_result)
+ {
+ case LDAP_SUCCESS:
+ slapi_log_error ( SLAPI_LOG_FATAL, repl_plugin_name,
+ "%s: Created glue entry %s uniqueid=%s reason missingEntry\n",
+ sessionid, dnstr, uniqueid);
+ done= 1;
+ break;
+ case LDAP_NO_SUCH_OBJECT:
+ /* The parent is missing */
+ {
+ /* JCMREPL - Create the parent ... recursion?... but what's the uniqueid? */
+ PR_ASSERT(0); /* JCMREPL */
+ }
+ default:
+ slapi_log_error ( SLAPI_LOG_FATAL, repl_plugin_name,
+ "%s: Can't created glue entry %s uniqueid=%s, error %d\n",
+ sessionid, dnstr, uniqueid, op_result);
+ break;
+ }
+ /* JCMREPL - Could get trapped in this loop forever! */
+ }
+
+ slapi_rdn_free ( &rdn );
+ slapi_sdn_free ( &superiordn );
+ }
+
+ return op_result;
+}
diff --git a/ldap/servers/plugins/replication/urp_tombstone.c b/ldap/servers/plugins/replication/urp_tombstone.c
new file mode 100644
index 00000000..3b24b928
--- /dev/null
+++ b/ldap/servers/plugins/replication/urp_tombstone.c
@@ -0,0 +1,210 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+
+/*
+ * urp_tombstone.c - Update Resolution Procedures - Tombstones
+ */
+
+#include "slapi-plugin.h"
+#include "repl5.h"
+#include "urp.h"
+
+extern int slapi_log_urp;
+
+/*
+ * Check if the entry is a tombstone.
+ */
+int
+is_tombstone_entry(const Slapi_Entry* entry)
+{
+ int flag;
+
+ /* LP: This doesn't work very well with entries that we tombstone ourself */
+ flag = slapi_entry_flag_is_set (entry, SLAPI_ENTRY_FLAG_TOMBSTONE);
+ if (flag == 0)
+ {
+ /* This is slow */
+ flag = slapi_entry_attr_hasvalue(entry, SLAPI_ATTR_OBJECTCLASS, SLAPI_ATTR_VALUE_TOMBSTONE);
+ }
+ return flag;
+}
+
+PRBool
+get_tombstone_csn(const Slapi_Entry *entry, const CSN **delcsn)
+{
+ PRBool ists = PR_FALSE;
+ if (is_tombstone_entry(entry)) {
+ ists = PR_TRUE;
+ *delcsn = _get_deletion_csn((Slapi_Entry *)entry); /* cast away const */
+ }
+
+ return ists;
+}
+
+static int
+tombstone_to_glue_resolve_parent (
+ Slapi_PBlock *pb,
+ const char *sessionid,
+ const Slapi_DN *parentdn,
+ const char *parentuniqueid,
+ CSN *opcsn)
+{
+ /* Let's have a look at the parent of this entry... */
+ if(!slapi_sdn_isempty(parentdn) && parentuniqueid!=NULL)
+ {
+ int op_result;
+ Slapi_PBlock *newpb= slapi_pblock_new();
+ slapi_search_internal_set_pb(
+ newpb,
+ slapi_sdn_get_dn(parentdn), /* JCM - This DN just identifies the backend to be searched. */
+ LDAP_SCOPE_BASE,
+ "objectclass=*",
+ NULL, /*attrs*/
+ 0, /*attrsonly*/
+ NULL, /*Controls*/
+ parentuniqueid, /*uniqueid*/
+ repl_get_plugin_identity(PLUGIN_MULTIMASTER_REPLICATION),
+ 0);
+ slapi_search_internal_pb(newpb);
+ slapi_pblock_get(newpb, SLAPI_PLUGIN_INTOP_RESULT, &op_result);
+ switch(op_result)
+ {
+ case LDAP_SUCCESS:
+ {
+ Slapi_Entry **entries= NULL;
+ /* OK, the tombstone entry parent exists. Is it also a tombstone? */
+ slapi_pblock_get(newpb, SLAPI_PLUGIN_INTOP_SEARCH_ENTRIES, &entries);
+ if(entries!=NULL && entries[0]!=NULL)
+ {
+ if(is_tombstone_entry(entries[0]))
+ {
+ tombstone_to_glue (pb, sessionid, entries[0], parentdn, REASON_RESURRECT_ENTRY, opcsn);
+ }
+ }
+ else
+ {
+ /* JCM - Couldn't find the entry! */
+ }
+ }
+ break;
+ default:
+ /* So, the tombstone entry had a parent... but it's gone. */
+ /* That's probably a bad thing. */
+ break;
+ }
+ slapi_free_search_results_internal (newpb);
+ slapi_pblock_destroy(newpb);
+ }
+ return 0;
+}
+
+/*
+ * Convert a tombstone into a glue entry.
+ */
+int
+tombstone_to_glue (
+ Slapi_PBlock *pb,
+ const char *sessionid,
+ Slapi_Entry *tombstoneentry,
+ const Slapi_DN *tombstonedn,
+ const char *reason,
+ CSN *opcsn)
+{
+ Slapi_DN *parentdn;
+ char *parentuniqueid;
+ const char *tombstoneuniqueid;
+ Slapi_Entry *addingentry;
+ const char *addingdn;
+ int op_result;
+
+ /* JCMREPL
+ * Nothing logged to the 5.0 Change Log
+ * Add is logged to the 4.0 Change Log - Core server Add code
+ * must attach the entry to the Operation
+ */
+
+
+ /* Resurrect the parent entry first */
+
+ /* JCM - This DN calculation is odd. It could resolve to NULL
+ * which won't help us identify the correct backend to search.
+ */
+ is_suffix_dn (pb, tombstonedn, &parentdn);
+ parentuniqueid= slapi_entry_attr_get_charptr (tombstoneentry,
+ SLAPI_ATTR_VALUE_PARENT_UNIQUEID); /* Allocated */
+ tombstone_to_glue_resolve_parent (pb, sessionid, parentdn, parentuniqueid, opcsn);
+ slapi_sdn_free(&parentdn);
+
+ /* Submit an Add operation to turn the tombstone entry into glue. */
+ /*
+ * The tombstone is stored with an invalid DN, we must fix this.
+ */
+ addingentry = slapi_entry_dup(tombstoneentry);
+ addingdn = slapi_sdn_get_dn(tombstonedn);
+ slapi_entry_set_dn(addingentry,slapi_ch_strdup(addingdn)); /* consumes DN */
+
+ if (!slapi_entry_attr_hasvalue(addingentry, ATTR_NSDS5_REPLCONFLICT, reason))
+ {
+ /* Add the reason of turning it to glue - The backend code will use it*/
+ slapi_entry_add_string(addingentry, ATTR_NSDS5_REPLCONFLICT, reason);
+ }
+ tombstoneuniqueid= slapi_entry_get_uniqueid(tombstoneentry);
+ op_result = urp_fixup_add_entry (addingentry, tombstoneuniqueid, parentuniqueid, opcsn, OP_FLAG_RESURECT_ENTRY);
+ if (op_result == LDAP_SUCCESS)
+ {
+ slapi_log_error (slapi_log_urp, repl_plugin_name,
+ "%s: Resurrected tombstone %s to glue reason '%s'\n", sessionid, addingdn, reason);
+ }
+ else
+ {
+ slapi_log_error (SLAPI_LOG_FATAL, repl_plugin_name,
+ "%s: Can't resurrect tombstone %s to glue reason '%s', error=%d\n",
+ sessionid, addingdn, reason, op_result);
+ }
+ slapi_entry_free (addingentry);
+ return op_result;
+}
+
+int
+entry_to_tombstone ( Slapi_PBlock *pb, Slapi_Entry *entry )
+{
+ Slapi_Operation *op;
+ Slapi_Mods smods;
+ CSN *opcsn;
+ const char *uniqueid;
+ int op_result = LDAP_SUCCESS;
+
+ slapi_pblock_get ( pb, SLAPI_OPERATION, &op );
+ opcsn = operation_get_csn ( op );
+ uniqueid = slapi_entry_get_uniqueid ( entry );
+
+
+ slapi_mods_init ( &smods, 2 );
+ /* Remove objectclass=glue */
+ slapi_mods_add ( &smods, LDAP_MOD_DELETE, SLAPI_ATTR_OBJECTCLASS, strlen("glue"), "glue");
+ /* Remove any URP conflict since a tombstone shouldn't
+ * be retrieved later for conflict removal.
+ */
+ slapi_mods_add ( &smods, LDAP_MOD_DELETE, ATTR_NSDS5_REPLCONFLICT, 0, NULL );
+
+ op_result = urp_fixup_modify_entry (uniqueid, slapi_entry_get_dn_const (entry), opcsn, &smods, 0);
+ slapi_mods_done ( &smods );
+
+ /*
+ * Delete the entry.
+ */
+ if ( op_result == LDAP_SUCCESS )
+ {
+ /*
+ * Using internal delete operation since it would go
+ * through the urp operations and trigger the recursive
+ * fixup if applicable.
+ */
+ op_result = urp_fixup_delete_entry (uniqueid, slapi_entry_get_dn_const (entry), opcsn, 0);
+ }
+
+ return op_result;
+}
diff --git a/ldap/servers/plugins/retrocl/Makefile b/ldap/servers/plugins/retrocl/Makefile
new file mode 100644
index 00000000..c81f0dfe
--- /dev/null
+++ b/ldap/servers/plugins/retrocl/Makefile
@@ -0,0 +1,135 @@
+#
+# BEGIN COPYRIGHT BLOCK
+# Copyright 2001 Sun Microsystems, Inc.
+# Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+# All rights reserved.
+# END COPYRIGHT BLOCK
+#
+#
+# GNU Makefile for Directory Server "Retrocl" plugin
+
+LDAP_SRC = ../../..
+MCOM_ROOT = ../../../../..
+
+NOSTDCLEAN=true # don't let nsconfig.mk define target clean
+NOSTDSTRIP=true # don't let nsconfig.mk define target strip
+NSPR20=true # probably should be defined somewhere else (not sure where)
+
+OBJDEST = $(OBJDIR)/lib/retrocl-plugin
+BINDIR = $(LDAP_SERVER_RELDIR)
+LIBDIR = $(LIB_RELDIR)
+
+include $(MCOM_ROOT)/ldapserver/nsdefs.mk
+include $(MCOM_ROOT)/ldapserver/nsconfig.mk
+include $(LDAP_SRC)/nsldap.mk
+include $(MCOM_ROOT)/ldapserver/ns_usedb.mk
+INCLUDES+=-I$(DB_INCLUDE)
+
+ifeq ($(ARCH), WINNT)
+DEF_FILE:=./retrocl.def
+endif
+
+CFLAGS += $(SLCFLAGS) -DSLAPD_LOGGING
+
+ifeq ($(ARCH), WINNT)
+CFLAGS += /WX
+endif
+
+ifdef TEST_CL5
+CFLAGS += -DTEST_CL5
+endif
+
+INCLUDES += -I$(LDAP_SRC)/servers/slapd -I$(DB_INCLUDE)
+
+ifeq ($(ARCH), WINNT)
+SUBSYSTEM=console
+endif
+
+LOCAL_OBJS= \
+ retrocl.o \
+ retrocl_po.o \
+ retrocl_rootdse.o \
+ retrocl_cn.o \
+ retrocl_trim.o \
+ retrocl_create.o \
+
+
+
+LIBRETROCL_OBJS = $(addprefix $(OBJDEST)/, $(LOCAL_OBJS))
+
+ifeq ($(ARCH), WINNT)
+RETROCL_DLL_OBJ = $(addprefix $(OBJDEST)/, dllmain.o)
+endif
+
+LIBRETROCL= $(addprefix $(LIBDIR)/, $(RETROCL_DLL).$(DLL_SUFFIX))
+
+LT_OBJS = $(addprefix $(OBJDEST)/, linktest.o)
+
+#EXTRA_LIBS_DEP = $(LDAPSDK_DEP) \
+# $(LDAP_LIBLDIF_DEP) \
+# $(LDAP_SLIBLCACHE_DEP) $(DB_LIB_DEP) $(LIBSLAPD_DEP) \
+# $(LDAP_COMMON_LIBS_DEP)
+
+#EXTRA_LIBS = $(LIBACCESS) $(LDAP_SDK_LIBSSLDAP_LIB) $(ADMINUTIL_LINK) \
+# $(LDAP_SDK_LIBLDAP_DLL) $(LDAP_SLIBLCACHE) $(DB_LIB) \
+# $(PLATFORM_SPECIFIC_EXTRA_LIBRARY) $(LIBSLAPD) $(LDAP_LIBLITEKEY) \
+# $(NLSLINK) $(ALIBS) \
+# $(LDAP_SDK_LIBSSLDAP_LIB) $(LDAP_SDK_LIBLDAP_DLL) \
+# $(LIBSECURITYLINK) $(NSPRLINK) $(DBMLINK) \
+# $(THREADSLIB) $(LDAP_COMMON_LIBS) $(NSPRLINK) $(SVRCORELINK)
+
+ifeq ($(ARCH), WINNT)
+EXTRA_LIBS_DEP += $(LIBSLAPD_DEP)
+EXTRA_LIBS_DEP += $(LDAPSDK_DEP) $(DB_LIB_DEP) $(NSPR_DEP)
+EXTRA_LIBS += $(LIBSLAPD) $(LDAP_SDK_LIBLDAP_DLL) $(DB_LIB)
+endif
+
+ifeq ($(ARCH), AIX)
+EXTRA_LIBS_DEP += $(LIBSLAPD_DEP)
+EXTRA_LIBS_DEP += $(LDAPSDK_DEP) $(DB_LIB_DEP) $(NSPR_DEP)
+EXTRA_LIBS += $(LIBSLAPD) $(LDAP_SDK_LIBLDAP_DLL) $(DB_LIB)
+endif
+
+ifeq ($(ARCH), HPUX)
+EXTRA_LIBS_DEP += $(LIBSLAPD_DEP) $(LDAPSDK_DEP) $(NSPR_DEP) $(SECURITY_DEP)
+EXTRA_LIBS += $(DYN_NSHTTPD) $(ADMINUTIL_LINK) $(LDAPLINK) $(SECURITYLINK) $(NSPRLINK) $(ICULINK)
+endif
+
+ifeq ($(ARCH), WINNT)
+DLL_LDFLAGS += -def:"./retrocl.def"
+endif # WINNT
+
+ifeq ($(ARCH), AIX)
+EXTRA_LIBS += $(DLL_EXTRA_LIBS)
+LD=ld
+endif
+
+clientSDK:
+
+all: $(OBJDEST) $(LIBDIR) $(LIBRETROCL)
+
+linktest: $(LIBRETROCL) $(LT_OBJS)
+ $(LINK_EXE_NOLIBSOBJS) -o linktest $(LT_OBJS) $(LIBRETROCL) -Rlib -Rlib/../bin/slapd/lib -Llib -Llib/../bin/slapd/lib -lslapd $(EXTRA_LIBS) $(NSPRLINK)
+
+
+$(LIBRETROCL): $(LIBRETROCL_OBJS) $(RETROCL_DLL_OBJ) $(DEF_FILE)
+ $(LINK_DLL) $(LIBRETROCL_OBJS) $(RETROCL_DLL_OBJ) $(PLATFORMLIBS) $(EXTRA_LIBS) $(LDAP_LIBLDIF) $(NSPRLINK)
+
+tests: $(TEST_PROGS)
+
+veryclean: clean
+
+clean:
+ $(RM) $(LIBRETROCL_OBJS)
+ifeq ($(ARCH), WINNT)
+ $(RM) $(RETROCL_DLL_OBJ)
+endif
+ $(RM) $(LIBRETROCL)
+
+$(OBJDEST):
+ $(MKDIR) $(OBJDEST)
+
+#
+# header file dependencies (incomplete)
+#
+$(LIBRETROCL_OBJS):
diff --git a/ldap/servers/plugins/retrocl/dllmain.c b/ldap/servers/plugins/retrocl/dllmain.c
new file mode 100644
index 00000000..276b1fa9
--- /dev/null
+++ b/ldap/servers/plugins/retrocl/dllmain.c
@@ -0,0 +1,90 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+ /*
+ * Microsoft Windows specifics for LIBRETROCL DLL
+ */
+#include "ldap.h"
+
+
+#ifdef _WIN32
+/* Lifted from Q125688
+ * How to Port a 16-bit DLL to a Win32 DLL
+ * on the MSVC 4.0 CD
+ */
+BOOL WINAPI DllMain (HANDLE hModule, DWORD fdwReason, LPVOID lpReserved)
+{
+
+ switch (fdwReason)
+ {
+ case DLL_PROCESS_ATTACH:
+ /* Code from LibMain inserted here. Return TRUE to keep the
+ DLL loaded or return FALSE to fail loading the DLL.
+
+ You may have to modify the code in your original LibMain to
+ account for the fact that it may be called more than once.
+ You will get one DLL_PROCESS_ATTACH for each process that
+ loads the DLL. This is different from LibMain which gets
+ called only once when the DLL is loaded. The only time this
+ is critical is when you are using shared data sections.
+ If you are using shared data sections for statically
+ allocated data, you will need to be careful to initialize it
+ only once. Check your code carefully.
+
+ Certain one-time initializations may now need to be done for
+ each process that attaches. You may also not need code from
+ your original LibMain because the operating system may now
+ be doing it for you.
+ */
+ /*
+ * 16 bit code calls UnlockData()
+ * which is mapped to UnlockSegment in windows.h
+ * in 32 bit world UnlockData is not defined anywhere
+ * UnlockSegment is mapped to GlobalUnfix in winbase.h
+ * and the docs for both UnlockSegment and GlobalUnfix say
+ * ".. function is oboslete. Segments have no meaning
+ * in the 32-bit environment". So we do nothing here.
+ */
+
+ break;
+
+ case DLL_THREAD_ATTACH:
+ /* Called each time a thread is created in a process that has
+ already loaded (attached to) this DLL. Does not get called
+ for each thread that exists in the process before it loaded
+ the DLL.
+
+ Do thread-specific initialization here.
+ */
+ break;
+
+ case DLL_THREAD_DETACH:
+ /* Same as above, but called when a thread in the process
+ exits.
+
+ Do thread-specific cleanup here.
+ */
+ break;
+
+ case DLL_PROCESS_DETACH:
+ /* Code from _WEP inserted here. This code may (like the
+ LibMain) not be necessary. Check to make certain that the
+ operating system is not doing it for you.
+ */
+
+ break;
+ }
+ /* The return value is only used for DLL_PROCESS_ATTACH; all other
+ conditions are ignored. */
+ return TRUE; /* successful DLL_PROCESS_ATTACH */
+}
+#else
+int CALLBACK
+LibMain( HINSTANCE hinst, WORD wDataSeg, WORD cbHeapSize, LPSTR lpszCmdLine )
+{
+ /*UnlockData( 0 );*/
+ return( 1 );
+}
+#endif
diff --git a/ldap/servers/plugins/retrocl/linktest.c b/ldap/servers/plugins/retrocl/linktest.c
new file mode 100644
index 00000000..22812c57
--- /dev/null
+++ b/ldap/servers/plugins/retrocl/linktest.c
@@ -0,0 +1,16 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+
+/* This is a test program. Not linked into the shared library */
+
+#include "retrocl.h"
+
+int main(int a,char **b)
+{
+ int r;
+
+ r = retrocl_plugin_init(NULL);
+}
diff --git a/ldap/servers/plugins/retrocl/retrocl.c b/ldap/servers/plugins/retrocl/retrocl.c
new file mode 100644
index 00000000..e0d3e325
--- /dev/null
+++ b/ldap/servers/plugins/retrocl/retrocl.c
@@ -0,0 +1,341 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+
+/*
+ * Requires that create_instance.c have added a plugin entry similar to:
+
+dn: cn=Retrocl Plugin,cn=plugins,cn=config
+objectclass: top
+objectclass: nsSlapdPlugin
+objectclass: extensibleObject
+cn: RetroCL Plugin
+nsslapd-pluginpath: /export2/servers/Hydra-supplier/lib/retrocl-plugin.so
+nsslapd-plugininitfunc: retrocl_plugin_init
+nsslapd-plugintype: object
+nsslapd-pluginenabled: on
+nsslapd-plugin-depends-on-type: database
+nsslapd-pluginid: retrocl
+nsslapd-pluginversion: 5.0b2
+nsslapd-pluginvendor: Sun Microsystems, Inc.
+nsslapd-plugindescription: Retrocl Plugin
+
+ *
+ */
+
+#include "retrocl.h"
+
+#ifdef _WIN32
+int *module_ldap_debug = 0;
+
+void plugin_init_debug_level(int *level_ptr)
+{
+ module_ldap_debug = level_ptr;
+}
+#endif
+
+void* g_plg_identity [PLUGIN_MAX];
+
+Slapi_Backend *retrocl_be_changelog = NULL;
+
+/* ----------------------------- Retrocl Plugin */
+
+static Slapi_PluginDesc retrocldesc = {"retrocl", PLUGIN_MAGIC_VENDOR_STR, PRODUCTTEXT, "Retrocl Plugin"};
+static Slapi_PluginDesc retroclpostopdesc = {"retrocl-postop", PLUGIN_MAGIC_VENDOR_STR, PRODUCTTEXT, "retrocl post-operation plugin"};
+static Slapi_PluginDesc retroclinternalpostopdesc = {"retrocl-internalpostop", PLUGIN_MAGIC_VENDOR_STR, PRODUCTTEXT, "retrocl internal post-operation plugin"};
+static Slapi_PluginDesc retroclbepostopdesc = {"retrocl-bepostop", PLUGIN_MAGIC_VENDOR_STR, PRODUCTTEXT, "Retrocl bepost-operation plugin"};
+
+
+/*
+ * Function: retrocl_*
+ *
+ * Returns: LDAP_
+ *
+ * Arguments: Pb of operation
+ *
+ * Description: wrappers around retrocl_postob registered as callback
+ *
+ */
+
+int retrocl_postop_add (Slapi_PBlock *pb) { return retrocl_postob(pb,OP_ADD);}
+int retrocl_postop_delete (Slapi_PBlock *pb) { return retrocl_postob(pb,OP_DELETE);}
+int retrocl_postop_modify (Slapi_PBlock *pb) { return retrocl_postob(pb,OP_MODIFY);}
+int retrocl_postop_modrdn (Slapi_PBlock *pb) { return retrocl_postob(pb,OP_MODRDN);}
+
+/*
+ * Function: retrocl_postop_init
+ *
+ * Returns: 0/-1
+ *
+ * Arguments: Pb
+ *
+ * Description: callback function
+ *
+ */
+
+int
+retrocl_postop_init( Slapi_PBlock *pb )
+{
+ int rc= 0; /* OK */
+
+ if( slapi_pblock_set( pb, SLAPI_PLUGIN_VERSION, SLAPI_PLUGIN_VERSION_01 ) != 0 ||
+ slapi_pblock_set( pb, SLAPI_PLUGIN_DESCRIPTION, (void *)&retroclpostopdesc ) != 0 ||
+ slapi_pblock_set( pb, SLAPI_PLUGIN_POST_ADD_FN, (void *) retrocl_postop_add ) != 0 ||
+ slapi_pblock_set( pb, SLAPI_PLUGIN_POST_DELETE_FN, (void *) retrocl_postop_delete ) != 0 ||
+ slapi_pblock_set( pb, SLAPI_PLUGIN_POST_MODIFY_FN, (void *) retrocl_postop_modify ) != 0 ||
+ slapi_pblock_set( pb, SLAPI_PLUGIN_POST_MODRDN_FN, (void *) retrocl_postop_modrdn ) != 0 )
+ {
+ slapi_log_error( SLAPI_LOG_PLUGIN, RETROCL_PLUGIN_NAME, "retrocl_postop_init failed\n" );
+ rc= -1;
+ }
+
+ return rc;
+}
+
+/*
+ * Function: retrocl_internalpostop_init
+ *
+ * Returns: 0/-1
+ *
+ * Arguments: Pb
+ *
+ * Description: callback function
+ *
+ */
+
+int
+retrocl_internalpostop_init( Slapi_PBlock *pb )
+{
+ int rc= 0; /* OK */
+
+ if( slapi_pblock_set( pb, SLAPI_PLUGIN_VERSION, SLAPI_PLUGIN_VERSION_01 ) != 0 ||
+ slapi_pblock_set( pb, SLAPI_PLUGIN_DESCRIPTION, (void *)&retroclinternalpostopdesc ) != 0 ||
+ slapi_pblock_set( pb, SLAPI_PLUGIN_INTERNAL_POST_ADD_FN, (void *) retrocl_postop_add ) != 0 ||
+ slapi_pblock_set( pb, SLAPI_PLUGIN_INTERNAL_POST_DELETE_FN, (void *) retrocl_postop_delete ) != 0 ||
+ slapi_pblock_set( pb, SLAPI_PLUGIN_INTERNAL_POST_MODIFY_FN, (void *) retrocl_postop_modify ) != 0 ||
+ slapi_pblock_set( pb, SLAPI_PLUGIN_INTERNAL_POST_MODRDN_FN, (void *) retrocl_postop_modrdn ) != 0 )
+ {
+ slapi_log_error( SLAPI_LOG_PLUGIN, RETROCL_PLUGIN_NAME, "retrocl_internalpostop_init failed\n" );
+ rc= -1;
+ }
+
+ return rc;
+}
+
+/*
+ * Function: retrocl_rootdse_init
+ *
+ * Returns: LDAP_SUCCESS
+ *
+ * Arguments: none
+ *
+ * Description: The FE DSE *must* be initialised before we get here.
+ *
+ */
+static int retrocl_rootdse_init(void)
+{
+
+ int return_value= LDAP_SUCCESS;
+
+ slapi_config_register_callback(SLAPI_OPERATION_SEARCH,DSE_FLAG_PREOP,"",
+ LDAP_SCOPE_BASE,"(objectclass=*)",
+ retrocl_rootdse_search,NULL);
+ return return_value;
+}
+
+/*
+ * Function: retrocl_select_backend
+ *
+ * Returns: LDAP_
+ *
+ * Arguments: none
+ *
+ * Description: simulates an add of the changelog to see if it exists. If not,
+ * creates it. Then reads the changenumbers. This function should be called
+ * exactly once at startup.
+ *
+ */
+
+static int retrocl_select_backend(void)
+{
+ int err;
+ Slapi_PBlock *pb;
+ Slapi_Backend *be = NULL;
+ Slapi_Entry *referral = NULL;
+ Slapi_Operation *op = NULL;
+ char errbuf[1024];
+
+ pb = slapi_pblock_new();
+
+ slapi_pblock_set (pb, SLAPI_PLUGIN_IDENTITY, g_plg_identity[PLUGIN_RETROCL]);
+
+ /* This is a simulated operation; no actual add is performed */
+ op = operation_new(OP_FLAG_INTERNAL);
+ operation_set_type(op,SLAPI_OPERATION_ADD); /* Ensure be not readonly */
+
+ operation_set_target_spec_str(op,RETROCL_CHANGELOG_DN);
+
+ slapi_pblock_set(pb,SLAPI_OPERATION, op);
+
+ err = slapi_mapping_tree_select(pb,&be,&referral,errbuf);
+ slapi_entry_free(referral);
+
+ operation_free(&op,NULL);
+
+ if (err != LDAP_SUCCESS || be == NULL || be == defbackend_get_backend()) {
+ LDAPDebug(LDAP_DEBUG_TRACE,"Mapping tree select failed (%d) %s.\n",
+ err,errbuf,0);
+
+ /* could not find the backend for cn=changelog, either because
+ * it doesn't exist
+ * mapping tree not registered.
+ */
+ err = retrocl_create_config();
+
+ if (err != LDAP_SUCCESS) return err;
+ } else {
+ retrocl_be_changelog = be;
+ }
+
+ retrocl_create_cle();
+
+ return retrocl_get_changenumbers();
+}
+
+/*
+ * Function: retrocl_get_config_str
+ *
+ * Returns: malloc'ed string which must be freed.
+ *
+ * Arguments: attribute type name
+ *
+ * Description: reads a single-valued string attr from the plugins' own DSE.
+ * This is called twice: to obtain the trim max age during startup, and to
+ * obtain the change log directory. No callback is registered; you cannot
+ * change the trim max age without restarting the server.
+ *
+ */
+
+char *retrocl_get_config_str(const char *attrt)
+{
+ Slapi_Entry **entries;
+ Slapi_PBlock *pb = NULL;
+ char *ma;
+ int rc = 0;
+ char *dn;
+
+ dn = RETROCL_PLUGIN_DN;
+
+ pb = slapi_pblock_new();
+
+ slapi_search_internal_set_pb (pb, dn, LDAP_SCOPE_BASE, "objectclass=*", NULL, 0, NULL,
+ NULL, g_plg_identity[PLUGIN_RETROCL] , 0);
+ slapi_search_internal_pb (pb);
+ slapi_pblock_get(pb, SLAPI_PLUGIN_INTOP_RESULT, &rc);
+ if (rc != 0) {
+ slapi_pblock_destroy(pb);
+ return NULL;
+ }
+ slapi_pblock_get(pb, SLAPI_PLUGIN_INTOP_SEARCH_ENTRIES, &entries);
+
+ ma = slapi_entry_attr_get_charptr(entries[0],attrt);
+ slapi_free_search_results_internal(pb);
+ slapi_pblock_destroy(pb);
+
+ return ma;
+}
+
+/*
+ * Function: retrocl_start
+ *
+ * Returns: 0 on success
+ *
+ * Arguments: Pb
+ *
+ * Description:
+ *
+ */
+
+static int retrocl_start (Slapi_PBlock *pb)
+{
+ static int retrocl_started = 0;
+ int rc = 0;
+
+ if (!retrocl_started) {
+ retrocl_rootdse_init();
+
+ rc = retrocl_select_backend();
+
+ if (rc == 0) {
+ retrocl_init_trimming();
+ } else {
+ LDAPDebug(LDAP_DEBUG_TRACE,"Couldnt find backend, not trimming retro changelog (%d).\n",rc,0,0);
+ }
+ }
+
+ retrocl_started = 1;
+ return rc;
+}
+
+/*
+ * Function: retrocl_stop
+ *
+ * Returns: 0
+ *
+ * Arguments: Pb
+ *
+ * Description: called when the server is shutting down
+ *
+ */
+
+static int retrocl_stop (Slapi_PBlock *pb)
+{
+ int rc = 0;
+
+ retrocl_stop_trimming();
+ retrocl_be_changelog = NULL;
+ retrocl_forget_changenumbers();
+
+ return rc;
+}
+
+/*
+ * Function: retrocl_plugin_init
+ *
+ * Returns: 0 on successs
+ *
+ * Arguments: Pb
+ *
+ * Description: main entry point for retrocl
+ *
+ */
+
+int
+retrocl_plugin_init(Slapi_PBlock *pb)
+{
+ static int legacy_initialised= 0;
+ int rc = 0;
+ void *identity = NULL;
+
+ slapi_pblock_get (pb, SLAPI_PLUGIN_IDENTITY, &identity);
+ PR_ASSERT (identity);
+ g_plg_identity[PLUGIN_RETROCL] = identity;
+
+ if (!legacy_initialised) {
+ rc= slapi_pblock_set( pb, SLAPI_PLUGIN_VERSION, SLAPI_PLUGIN_VERSION_01 );
+ rc= slapi_pblock_set( pb, SLAPI_PLUGIN_DESCRIPTION, (void *)&retrocldesc );
+ rc= slapi_pblock_set( pb, SLAPI_PLUGIN_START_FN, (void *) retrocl_start );
+ rc= slapi_pblock_set( pb, SLAPI_PLUGIN_CLOSE_FN, (void *) retrocl_stop );
+
+ rc= slapi_register_plugin("postoperation", 1 /* Enabled */, "retrocl_postop_init", retrocl_postop_init, "Retrocl postoperation plugin", NULL, identity);
+ rc= slapi_register_plugin("internalpostoperation", 1 /* Enabled */, "retrocl_internalpostop_init", retrocl_internalpostop_init, "Retrocl internal postoperation plugin", NULL, identity);
+ }
+
+ legacy_initialised = 1;
+ return rc;
+}
+
+
+
diff --git a/ldap/servers/plugins/retrocl/retrocl.def b/ldap/servers/plugins/retrocl/retrocl.def
new file mode 100644
index 00000000..ce730c44
--- /dev/null
+++ b/ldap/servers/plugins/retrocl/retrocl.def
@@ -0,0 +1,15 @@
+; BEGIN COPYRIGHT BLOCK
+; Copyright 2001 Sun Microsystems, Inc.
+; Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+; All rights reserved.
+; END COPYRIGHT BLOCK
+;
+;
+
+DESCRIPTION 'Netscape Directory Server 7.0 Retro-Changelog Plugin'
+;CODE SHARED READ EXECUTE
+;DATA SHARED READ WRITE
+EXPORTS
+ plugin_init_debug_level @1
+ retrocl_plugin_init @2
+
diff --git a/ldap/servers/plugins/retrocl/retrocl.h b/ldap/servers/plugins/retrocl/retrocl.h
new file mode 100644
index 00000000..f96af64c
--- /dev/null
+++ b/ldap/servers/plugins/retrocl/retrocl.h
@@ -0,0 +1,123 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+
+#ifndef _H_RETROCL
+#define _H_RETROCL 1
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "slapi-private.h"
+#include "slapi-plugin.h"
+/* #include "portable.h" */
+#include "dirver.h"
+#include "ldaplog.h"
+#include "ldif.h"
+#include "slap.h"
+#include <dirlite_strings.h>
+
+/* max len of a long (2^64), represented as a string, including null byte */
+#define CNUMSTR_LEN 21
+typedef unsigned long changeNumber;
+
+typedef struct _cnum_result_t {
+ int crt_nentries; /* number of entries returned from search */
+ int crt_err; /* err returned from backend */
+ Slapi_Entry *crt_entry; /* The entry returned from the backend */
+} cnum_result_t;
+
+typedef struct _cnumRet {
+ changeNumber cr_cnum;
+ char *cr_time;
+ int cr_lderr;
+} cnumRet;
+
+/* Operation types */
+#define OP_MODIFY 1
+#define OP_ADD 2
+#define OP_DELETE 3
+#define OP_MODRDN 4
+
+/*
+ * How often the changelog trimming thread runs. This is the minimum trim age.
+ */
+#define CHANGELOGDB_TRIM_INTERVAL 300*1000 /* 5 minutes */
+
+#define RETROCL_DLL_DEFAULT_THREAD_STACKSIZE 131072L
+#define RETROCL_BE_CACHEMEMSIZE "2097152"
+#define RETROCL_BE_CACHESIZE "-1"
+#define RETROCL_PLUGIN_NAME "DSRetroclPlugin"
+
+/* was originally changelogmaximumage */
+#define CONFIG_CHANGELOG_MAXAGE_ATTRIBUTE "nsslapd-changelogmaxage"
+#define CONFIG_CHANGELOG_DIRECTORY_ATTRIBUTE "nsslapd-changelogdir"
+
+#define RETROCL_CHANGELOG_DN "cn=changelog"
+#define RETROCL_MAPPINGTREE_DN "cn=\"cn=changelog\",cn=mapping tree,cn=config"
+#define RETROCL_PLUGIN_DN "cn=Retro Changelog Plugin,cn=plugins,cn=config"
+#define RETROCL_LDBM_DN "cn=changelog,cn=ldbm database,cn=plugins,cn=config"
+#define RETROCL_INDEX_DN "cn=changenumber,cn=index,cn=changelog,cn=ldbm database,cn=plugins,cn=config"
+
+/* Allow anonymous access to the changelog base only, but not to the
+ * entries in the changelog.
+ */
+#define RETROCL_ACL "(target =\"ldap:///cn=changelog\")(targetattr != \"aci\")(version 3.0; acl \"changelog base\"; allow( read,search, compare ) userdn =\"ldap:///anyone\";)"
+
+enum {
+ PLUGIN_RETROCL,
+ PLUGIN_MAX
+};
+
+extern void* g_plg_identity [PLUGIN_MAX];
+extern Slapi_Backend *retrocl_be_changelog;
+
+extern const char *attr_changenumber;
+extern const char *attr_targetdn;
+extern const char *attr_changetype;
+extern const char *attr_newrdn;
+extern const char *attr_newsuperior;
+extern const char *attr_deleteoldrdn;
+extern const char *attr_changes;
+extern const char *attr_changetime;
+extern const char *attr_objectclass;
+
+extern PRLock *retrocl_internal_lock;
+
+/* Functions */
+
+/* from repl_shared.h: not sure where defined */
+unsigned long strntoul( char *from, size_t len, int base );
+
+extern int retrocl_plugin_init(Slapi_PBlock *pb);
+
+extern int retrocl_bepostop_delete (Slapi_PBlock *pb);
+extern int retrocl_postop_add (Slapi_PBlock *pb);
+extern int retrocl_postop_delete (Slapi_PBlock *pb);
+extern int retrocl_postop_modify (Slapi_PBlock *pb);
+extern int retrocl_postop_modrdn (Slapi_PBlock *pb);
+extern int retrocl_postob(Slapi_PBlock *,int);
+
+extern int retrocl_rootdse_search (Slapi_PBlock *pb, Slapi_Entry* e, Slapi_Entry* entryAfter, int *returncode, char *returntext, void *arg);
+
+extern void retrocl_create_cle(void);
+extern int retrocl_create_config(void);
+
+extern changeNumber retrocl_get_first_changenumber(void);
+extern void retrocl_set_first_changenumber(changeNumber cn);
+extern changeNumber retrocl_get_last_changenumber(void);
+extern void retrocl_commit_changenumber(void);
+extern void retrocl_release_changenumber(void);
+extern changeNumber retrocl_assign_changenumber(void);
+extern int retrocl_get_changenumbers(void);
+extern void retrocl_forget_changenumbers(void);
+extern time_t retrocl_getchangetime( int type, int *err );
+
+extern void retrocl_init_trimming(void);
+extern void retrocl_stop_trimming(void);
+extern char *retrocl_get_config_str(const char *attrt);
+
+#endif /* _H_RETROCL */
diff --git a/ldap/servers/plugins/retrocl/retrocl.txt b/ldap/servers/plugins/retrocl/retrocl.txt
new file mode 100644
index 00000000..e82368e8
--- /dev/null
+++ b/ldap/servers/plugins/retrocl/retrocl.txt
@@ -0,0 +1,107 @@
+#
+# BEGIN COPYRIGHT BLOCK
+# Copyright 2001 Sun Microsystems, Inc.
+# Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+# All rights reserved.
+# END COPYRIGHT BLOCK
+#
+
+Changelog user documentation
+Last Updated October 6, 2000
+
+1. Introduction
+
+This document describes a how DS 6.0 provides a change log broadly
+compatible with the Internet Draft draft-good-ldap-changelog-01.txt.
+
+When enabled, the change log appears in the DIT below cn=changelog. It
+consists of a single level of entries, each of class changeLogEntry. This
+object class allows the following attributes:
+ - changeNumber. This attribute is always present and contains a single
+ value, an integer which is unique for each change. The value for later
+ changes is larger than those of any change which is already present.
+ - targetDN. This attribute contains the distinguished name of the entry
+ which was added, modified or deleted. In the case of a ModifyDN operation,
+ the targetDN attribute contains the DN of the entry before it was renamed
+ or moved.
+ - changeType. This attribute contains one of the following values: "add",
+ "delete", "modify" or "modrdn".
+ - changes. This attribute contains the changes made to the entry, in LDIF
+ format, for a add or modify change.
+ - newRDN. This attribute contains the new RDN of the entry, for a modifyDN
+ change.
+ - deleteOldRDN. This attribute contains whether the old RDN of the entry
+ was deleted, for a modifyDN change.
+ - newSuperior. This attribute contains the newSuperior field of the entry,
+ for a modifyDN change.
+
+The change log is implemented in an LDBM database.
+
+2. Configuration
+
+To enable the change log, the following steps should be performed. First,
+change the nsslapd-pluginenabled attribute of the DSE cn=Retrocl Plugin,
+cn=plugins,cn=config to "on" instead of "off", Then start or restart the
+server. The server will automatically create the change log database.
+
+3. Trimming
+
+The entries in the change log may be automatically removed if they are older
+than a specified period of time. This is done by setting the
+changelogmaximumage attribute in the change log plugin DSE cn=Retrocl Plugin,
+cn=plugins,cn=config and restarting the server. If this attribute is not
+present, then changed are not trimmed.
+
+The changelogmaximumage attribute is single-valued, and its value consists of
+two parts: a number and a time units code. The time units codes are:
+ - 's' for seconds,
+ - 'm' for minutes,
+ - 'h' for hours,
+ - 'd' for days,
+ - 'w' for weeks.
+
+For example,
+
+changelogmaximumage: 2d
+
+The minimum value is 5 minutes.
+
+4. Access Control
+
+When the changelog backend is created, the default access control is to allow
+anonymous read, search and compare to the changelog base entry, cn=changelog,
+by anyone. No access is granted, except implicitly to the Directory Manager,
+to any of the entries in the change log.
+
+Read access to the entries in the change log should not be granted to anonymous
+users, as the changes attribute could contain modifications to sensitive
+attribute values (such as passwords). Only authenticated services should be
+allowed to access this information.
+
+5. Protocol interaction
+
+All search and compare operations are supported on the change log database.
+Search operations whose filter is of the form
+(&(changenumber>=X)(changeNumber<=Y) are optimized.
+
+Add or modify operations should not be performed on change log entries in the
+change log database. Change log entries can be deleted if desired. The
+change log base entry, cn=changelog, can be modified if desired, to vary the
+access control policy of the change log database.
+
+6. Caveats
+
+The change log does not currently record changes which are internally
+constructed to resolve conflicts during multi-master replication. As a
+result, the change log should not be used in deployments which use multi-master
+replication with more than two masters or suppliers for a database.
+
+==
+
+root dse firstchangenumber and lastchangenumber
+
+changelogdir attribute
+
+test chaining be
+if changelog db deleted - what happens?
+cannot change trim max age without restarting the server
diff --git a/ldap/servers/plugins/retrocl/retrocl_cn.c b/ldap/servers/plugins/retrocl/retrocl_cn.c
new file mode 100644
index 00000000..3623f4b3
--- /dev/null
+++ b/ldap/servers/plugins/retrocl/retrocl_cn.c
@@ -0,0 +1,391 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+
+#include "retrocl.h"
+
+static changeNumber retrocl_internal_cn = 0;
+static changeNumber retrocl_first_cn = 0;
+PRLock *retrocl_internal_lock = NULL;
+
+/*
+ * Function: a2changeNumber
+ *
+ * Returns: changeNumber (long)
+ *
+ * Arguments: string
+ *
+ * Description: parses the string to a changenumber. changenumbers are
+ * positive integers.
+ *
+ */
+
+static changeNumber a2changeNumber (const char *p)
+{
+ changeNumber c;
+
+ c = strntoul((char *)p,strlen(p),10);
+ return c;
+}
+
+/*
+ * Function: handle_cnum_entry
+ * Arguments: op - pointer to Operation struct for this operation
+ * e - pointer to returned entry.
+ * Returns: nothing
+ * Description: Search result handler for retrocl_getchangenum(). Sets the
+ * op->o_handler_data to point to a structure which contains
+ * the changenumber retrieved and an error code.
+ */
+static int
+handle_cnum_entry( Slapi_Entry *e, void *callback_data )
+{
+ cnumRet *cr = (cnumRet *)callback_data;
+ Slapi_Value *sval=NULL;
+ const struct berval *value;
+
+ cr->cr_cnum = 0UL;
+ cr->cr_time = NULL;
+
+ if ( NULL != e ) {
+ Slapi_Attr *chattr = NULL;
+ sval = NULL;
+ value = NULL;
+ if ( slapi_entry_attr_find( e, attr_changenumber, &chattr ) == 0 ) {
+ slapi_attr_first_value( chattr,&sval );
+ if ( NULL != sval ) {
+ value = slapi_value_get_berval ( sval );
+ if( NULL != value && NULL != value->bv_val &&
+ '\0' != value->bv_val[0]) {
+ cr->cr_cnum = a2changeNumber( value->bv_val );
+ }
+ }
+ }
+ chattr = NULL;
+ sval = NULL;
+ value = NULL;
+
+ chattr = NULL;
+ sval = NULL;
+ value = NULL;
+ if ( slapi_entry_attr_find( e, attr_changetime, &chattr ) == 0 ) {
+ slapi_attr_first_value( chattr,&sval );
+ if ( NULL != sval) {
+ value = slapi_value_get_berval ( sval );
+ if (NULL != value && NULL != value->bv_val &&
+ '\0' != value->bv_val[0]) {
+ cr->cr_time = slapi_ch_strdup( value->bv_val );
+ }
+ }
+ }
+ }
+ return 0;
+}
+
+
+/*
+ * Function: handle_cnum_result
+ * Arguments: err - error code returned from search
+ * callback_data - private data for callback
+ * Returns: nothing
+ * Description: result handler for retrocl_getchangenum(). Sets the cr_lderr
+ * field of the cnumRet struct to the error returned
+ * from the backend.
+ */
+static void
+handle_cnum_result( int err, void *callback_data )
+{
+ cnumRet *cr = (cnumRet *)callback_data;
+ cr->cr_lderr = err;
+}
+
+/*
+ * Function: retrocl_get_changenumbers
+ *
+ * Returns: 0/-1
+ *
+ * Arguments: none
+ *
+ * Description: reads the first and last entry in the changelog to obtain
+ * the starting and ending change numbers.
+ *
+ */
+
+int retrocl_get_changenumbers(void)
+{
+ cnumRet cr;
+
+ if (retrocl_internal_lock == NULL) {
+ retrocl_internal_lock = PR_NewLock();
+
+ if (retrocl_internal_lock == NULL) return -1;
+ }
+
+ if (retrocl_be_changelog == NULL) return -1;
+
+ cr.cr_cnum = 0;
+ cr.cr_time = 0;
+
+ slapi_seq_callback(RETROCL_CHANGELOG_DN,SLAPI_SEQ_FIRST,
+ (char *)attr_changenumber, /* cast away const */
+ NULL,NULL,0,&cr,NULL,handle_cnum_result,
+ handle_cnum_entry, NULL);
+
+ retrocl_first_cn = cr.cr_cnum;
+
+ slapi_ch_free(( void **) &cr.cr_time );
+
+ slapi_seq_callback(RETROCL_CHANGELOG_DN,SLAPI_SEQ_LAST,
+ (char *)attr_changenumber, /* cast away const */
+ NULL,NULL,0,&cr,NULL,handle_cnum_result,
+ handle_cnum_entry, NULL);
+
+ retrocl_internal_cn = cr.cr_cnum;
+
+ slapi_log_error(SLAPI_LOG_PLUGIN,"retrocl","Got changenumbers %d and %d\n",
+ retrocl_first_cn,
+ retrocl_internal_cn);
+
+ slapi_ch_free(( void **) &cr.cr_time );
+
+ return 0;
+}
+
+/*
+ * Function: retrocl_getchangetime
+ * Arguments: type - one of SLAPI_SEQ_FIRST, SLAPI_SEQ_LAST
+ * Returns: The time of the requested change record. If the return value is
+ * NO_TIME, the changelog could not be read.
+ * If err is non-NULL, the memory it points to is set the the
+ * error code returned from the backend. If "type" is not valid,
+ * *err is set to -1.
+ * Description: Get the first or last changenumber stored in the changelog,
+ * depending on the value of argument "type".
+ */
+time_t retrocl_getchangetime( int type, int *err )
+{
+ cnumRet cr;
+ time_t ret;
+
+ if ( type != SLAPI_SEQ_FIRST && type != SLAPI_SEQ_LAST ) {
+ if ( err != NULL ) {
+ *err = -1;
+ }
+ return NO_TIME;
+ }
+ memset( &cr, '\0', sizeof( cnumRet ));
+ slapi_seq_callback( RETROCL_CHANGELOG_DN, type,
+ (char *)attr_changenumber, /* cast away const */
+ NULL,
+ NULL, 0, &cr, NULL,
+ handle_cnum_result, handle_cnum_entry, NULL );
+
+ if ( err != NULL ) {
+ *err = cr.cr_lderr;
+ }
+
+ if ( NULL == cr.cr_time ) {
+ ret = NO_TIME;
+ } else {
+ ret = parse_localTime( cr.cr_time );
+ }
+ slapi_ch_free(( void **) &cr.cr_time );
+ return ret;
+}
+
+/*
+ * Function: retrocl_forget_changenumbers
+ *
+ * Returns: none
+ *
+ * Arguments: none
+ *
+ * Description: used only when the server is shutting down
+ *
+ */
+
+void retrocl_forget_changenumbers(void)
+{
+ if (retrocl_internal_lock == NULL) return;
+
+ PR_Lock(retrocl_internal_lock);
+ retrocl_first_cn = 0;
+ retrocl_internal_cn = 0;
+ PR_Unlock(retrocl_internal_lock);
+}
+
+/*
+ * Function: retrocl_get_first_changenumber
+ *
+ * Returns: changeNumber
+ *
+ * Arguments: none
+ *
+ * Description: used in root DSE
+ *
+ */
+
+changeNumber retrocl_get_first_changenumber(void)
+{
+ changeNumber cn;
+ PR_Lock(retrocl_internal_lock);
+ cn = retrocl_first_cn;
+ PR_Unlock(retrocl_internal_lock);
+ return cn;
+}
+
+/*
+ * Function: retrocl_set_first_changenumber
+ *
+ * Returns: none
+ *
+ * Arguments: changenumber
+ *
+ * Description: used in changelog trimming
+ *
+ */
+
+void retrocl_set_first_changenumber(changeNumber cn)
+{
+ PR_Lock(retrocl_internal_lock);
+ retrocl_first_cn = cn;
+ PR_Unlock(retrocl_internal_lock);
+}
+
+
+/*
+ * Function: retrocl_get_last_changenumber
+ *
+ * Returns:
+ *
+ * Arguments:
+ *
+ * Description: used in root DSE
+ *
+ */
+
+changeNumber retrocl_get_last_changenumber(void)
+{
+ changeNumber cn;
+ PR_Lock(retrocl_internal_lock);
+ cn = retrocl_internal_cn;
+ PR_Unlock(retrocl_internal_lock);
+ return cn;
+}
+
+/*
+ * Function: retrocl_commit_changenumber
+ *
+ * Returns: none
+ *
+ * Arguments: none, lock must be held
+ *
+ * Description: NOTE! MUST BE PRECEEDED BY retrocl_assign_changenumber
+ *
+ */
+
+void retrocl_commit_changenumber(void)
+{
+ if ( retrocl_first_cn == 0) {
+ retrocl_first_cn = retrocl_internal_cn;
+ }
+}
+
+/*
+ * Function: retrocl_release_changenumber
+ *
+ * Returns: none
+ *
+ * Arguments: none, lock must be held
+ *
+ * Description: NOTE! MUST BE PRECEEDED BY retrocl_assign_changenumber
+ *
+ */
+
+void retrocl_release_changenumber(void)
+{
+ retrocl_internal_cn--;
+}
+
+/*
+ * Function: retrocl_update_lastchangenumber
+ *
+ * Returns: 0/-1
+ *
+ * Arguments: none
+ *
+ * Description: reads the last entry in the changelog to obtain
+ * the last change number.
+ *
+ */
+
+int retrocl_update_lastchangenumber(void)
+{
+ cnumRet cr;
+
+ if (retrocl_internal_lock == NULL) {
+ retrocl_internal_lock = PR_NewLock();
+
+ if (retrocl_internal_lock == NULL) return -1;
+ }
+
+ if (retrocl_be_changelog == NULL) return -1;
+
+ cr.cr_cnum = 0;
+ cr.cr_time = 0;
+ slapi_seq_callback(RETROCL_CHANGELOG_DN,SLAPI_SEQ_LAST,
+ (char *)attr_changenumber, /* cast away const */
+ NULL,NULL,0,&cr,NULL,handle_cnum_result,
+ handle_cnum_entry, NULL);
+
+
+ retrocl_internal_cn = cr.cr_cnum;
+ slapi_log_error(SLAPI_LOG_PLUGIN,"retrocl","Refetched last changenumber = %d \n",
+ retrocl_internal_cn);
+
+ slapi_ch_free(( void **) &cr.cr_time );
+
+ return 0;
+}
+
+
+
+/*
+ * Function: retrocl_assign_changenumber
+ *
+ * Returns: change number, 0 on error
+ *
+ * Arguments: none. Lock must be held.
+ *
+ * Description: NOTE! MUST BE FOLLOWED BY retrocl_commit_changenumber or
+ * retrocl_release_changenumber
+ *
+ */
+
+changeNumber retrocl_assign_changenumber(void)
+{
+ changeNumber cn;
+
+ if (retrocl_internal_lock == NULL) return 0;
+
+ /* Before we assign the changenumber; we should check for the
+ * validity of the internal assignment of retrocl_internal_cn
+ * we had from the startup */
+
+ if(retrocl_internal_cn <= retrocl_first_cn){
+ /* the numbers have become out of sync - retrocl_get_changenumbers
+ * gets called only once during startup and it may have had a problem
+ * getting the last changenumber.
+ * If there was any problem then update the lastchangenumber from the changelog db.
+ * This function is being called by only the thread that is actually writing
+ * to the changelog.
+ */
+ retrocl_update_lastchangenumber();
+ }
+
+ retrocl_internal_cn++;
+ cn = retrocl_internal_cn;
+ return cn;
+}
diff --git a/ldap/servers/plugins/retrocl/retrocl_create.c b/ldap/servers/plugins/retrocl/retrocl_create.c
new file mode 100644
index 00000000..fbda8e36
--- /dev/null
+++ b/ldap/servers/plugins/retrocl/retrocl_create.c
@@ -0,0 +1,317 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+
+#include "retrocl.h"
+
+/*
+The changelog is created by
+ - changing the node in the dse tree which represents the changelog plugin
+ to enabled on,
+ - shutting down the server,
+ - starting the server.
+ */
+
+/******************************/
+
+/*
+ * Function: retrocl_create_be
+ *
+ * Returns: LDAP_
+ *
+ * Arguments: location in file system to put changelog, or NULL for default
+ *
+ * Description:
+ * add an entry of class nsBackendInstance below cn=ldbm,cn=plugins,cn=config
+ *
+ */
+
+static int retrocl_create_be(const char *bedir)
+{
+ Slapi_PBlock *pb = NULL;
+ Slapi_Entry *e;
+ struct berval *vals[2];
+ struct berval val;
+ int rc;
+
+ vals[0] = &val;
+ vals[1] = NULL;
+
+ e = slapi_entry_alloc();
+ slapi_entry_set_dn(e,slapi_ch_strdup(RETROCL_LDBM_DN));
+
+ /* Set the objectclass attribute */
+ val.bv_val = "top";
+ val.bv_len = 3;
+ slapi_entry_add_values( e, "objectclass", vals );
+
+ /* Set the objectclass attribute */
+ val.bv_val = "extensibleObject";
+ val.bv_len = strlen(val.bv_val);
+ slapi_entry_add_values( e, "objectclass", vals );
+
+ /* Set the objectclass attribute */
+ val.bv_val = "nsBackendInstance";
+ val.bv_len = strlen(val.bv_val);
+ slapi_entry_add_values( e, "objectclass", vals );
+
+ val.bv_val = "changelog";
+ val.bv_len = strlen(val.bv_val);
+ slapi_entry_add_values( e, "cn", vals );
+
+ val.bv_val = RETROCL_BE_CACHESIZE;
+ val.bv_len = strlen(val.bv_val);
+ slapi_entry_add_values( e, "nsslapd-cachesize", vals );
+
+ val.bv_val = RETROCL_CHANGELOG_DN;
+ val.bv_len = strlen(val.bv_val);
+ slapi_entry_add_values( e, "nsslapd-suffix", vals );
+
+ val.bv_val = RETROCL_BE_CACHEMEMSIZE;
+ val.bv_len = strlen(val.bv_val);
+ slapi_entry_add_values( e, "nsslapd-cachememsize", vals );
+
+ val.bv_val = "off";
+ val.bv_len = strlen(val.bv_val);
+ slapi_entry_add_values( e, "nsslapd-readonly", vals );
+
+ if (bedir) {
+ val.bv_val = (char *)bedir; /* cast const */
+ val.bv_len = strlen(val.bv_val);
+ slapi_entry_add_values( e, "nsslapd-directory", vals );
+ }
+
+ pb = slapi_pblock_new ();
+ slapi_add_entry_internal_set_pb( pb, e, NULL /* controls */,
+ g_plg_identity[PLUGIN_RETROCL],
+ 0 /* actions */ );
+ slapi_add_internal_pb (pb);
+ slapi_pblock_get( pb, SLAPI_PLUGIN_INTOP_RESULT, &rc );
+ slapi_pblock_destroy(pb);
+
+ if (rc == 0) {
+ slapi_log_error (SLAPI_LOG_PLUGIN, RETROCL_PLUGIN_NAME,
+ "created changelog database node\n");
+ } else if (rc == LDAP_ALREADY_EXISTS) {
+ slapi_log_error (SLAPI_LOG_PLUGIN, RETROCL_PLUGIN_NAME,
+ "changelog database node already existed\n");
+ } else {
+ slapi_log_error( SLAPI_LOG_FATAL, RETROCL_PLUGIN_NAME, "Changelog LDBM backend could not be created (%d)\n", rc);
+ return rc;
+ }
+
+
+ /* we need the changenumber indexed */
+ e = slapi_entry_alloc();
+ slapi_entry_set_dn(e,slapi_ch_strdup(RETROCL_INDEX_DN));
+
+ /* Set the objectclass attribute */
+ val.bv_val = "top";
+ val.bv_len = 3;
+ slapi_entry_add_values( e, "objectclass", vals );
+
+ /* Set the objectclass attribute */
+ val.bv_val = "nsIndex";
+ val.bv_len = strlen(val.bv_val);
+ slapi_entry_add_values( e, "objectclass", vals );
+
+ val.bv_val = "changenumber";
+ val.bv_len = strlen(val.bv_val);
+ slapi_entry_add_values( e, "cn", vals );
+
+ val.bv_val = "false";
+ val.bv_len = strlen(val.bv_val);
+ slapi_entry_add_values( e, "nssystemindex", vals );
+
+ val.bv_val = "eq";
+ val.bv_len = strlen(val.bv_val);
+ slapi_entry_add_values( e, "nsindextype", vals );
+
+ pb = slapi_pblock_new ();
+ slapi_add_entry_internal_set_pb( pb, e, NULL /* controls */,
+ g_plg_identity[PLUGIN_RETROCL],
+ 0 /* actions */ );
+ slapi_add_internal_pb (pb);
+ slapi_pblock_get( pb, SLAPI_PLUGIN_INTOP_RESULT, &rc );
+ slapi_pblock_destroy(pb);
+
+ if (rc == 0) {
+ slapi_log_error (SLAPI_LOG_PLUGIN, RETROCL_PLUGIN_NAME,
+ "created changenumber index node\n");
+ } else if (rc == LDAP_ALREADY_EXISTS) {
+ slapi_log_error (SLAPI_LOG_PLUGIN, RETROCL_PLUGIN_NAME,
+ "changelog index node already existed\n");
+ } else {
+ slapi_log_error( SLAPI_LOG_FATAL, RETROCL_PLUGIN_NAME, "Changelog LDBM backend changenumber index could not be created (%d)\n", rc);
+ return rc;
+ }
+
+ return rc;
+}
+
+/*
+ * Function: retrocl_create_config
+ *
+ * Returns: LDAP_
+ *
+ * Arguments: none
+ *
+ * Description:
+ * This function is called if there was no mapping tree node or backend for
+ * cn=changelog.
+ */
+int retrocl_create_config(void)
+{
+ Slapi_PBlock *pb = NULL;
+ Slapi_Entry *e;
+ struct berval *vals[2];
+ struct berval val;
+ int rc;
+
+ vals[0] = &val;
+ vals[1] = NULL;
+
+ /* Assume the mapping tree node is missing. It doesn't hurt to
+ * attempt to add it if it already exists. You will see a warning
+ * in the errors file when the referenced backend does not exist.
+ */
+ e = slapi_entry_alloc();
+ slapi_entry_set_dn(e,slapi_ch_strdup(RETROCL_MAPPINGTREE_DN));
+
+ /* Set the objectclass attribute */
+ val.bv_val = "top";
+ val.bv_len = 3;
+ slapi_entry_add_values( e, "objectclass", vals );
+
+
+ /* Set the objectclass attribute */
+ val.bv_val = "extensibleObject";
+ val.bv_len = strlen(val.bv_val);
+ slapi_entry_add_values( e, "objectclass", vals );
+
+ /* Set the objectclass attribute */
+ val.bv_val = "nsMappingTree";
+ val.bv_len = strlen(val.bv_val);
+ slapi_entry_add_values( e, "objectclass", vals );
+
+ val.bv_val = "backend";
+ val.bv_len = strlen(val.bv_val);
+ slapi_entry_add_values( e, "nsslapd-state", vals );
+
+ val.bv_val = RETROCL_CHANGELOG_DN;
+ val.bv_len = strlen(val.bv_val);
+ slapi_entry_add_values( e, "cn", vals );
+
+ val.bv_val = "changelog";
+ val.bv_len = strlen(val.bv_val);
+ slapi_entry_add_values( e, "nsslapd-backend", vals );
+
+ pb = slapi_pblock_new ();
+ slapi_add_entry_internal_set_pb( pb, e, NULL /* controls */,
+ g_plg_identity[PLUGIN_RETROCL],
+ 0 /* actions */ );
+ slapi_add_internal_pb (pb);
+ slapi_pblock_get( pb, SLAPI_PLUGIN_INTOP_RESULT, &rc );
+ slapi_pblock_destroy(pb);
+
+ if (rc == 0) {
+ slapi_log_error (SLAPI_LOG_PLUGIN, RETROCL_PLUGIN_NAME,
+ "created changelog mapping tree node\n");
+ } else if (rc == LDAP_ALREADY_EXISTS) {
+ slapi_log_error (SLAPI_LOG_PLUGIN, RETROCL_PLUGIN_NAME,
+ "changelog mapping tree node already existed\n");
+ } else {
+ slapi_log_error( SLAPI_LOG_FATAL, RETROCL_PLUGIN_NAME, "cn=\"cn=changelog\",cn=mapping tree,cn=config could not be created (%d)\n", rc);
+ return rc;
+ }
+
+ retrocl_be_changelog = slapi_be_select_by_instance_name("changelog");
+
+ if (retrocl_be_changelog == NULL) {
+ /* This is not the nsslapd-changelogdir from cn=changelog4,cn=config */
+ char *bedir;
+
+ bedir = retrocl_get_config_str(CONFIG_CHANGELOG_DIRECTORY_ATTRIBUTE);
+
+ if (bedir == NULL) {
+ /* none specified */
+ }
+
+ rc = retrocl_create_be(bedir);
+ slapi_ch_free ((void **)&bedir);
+ if (rc != LDAP_SUCCESS && rc != LDAP_ALREADY_EXISTS) {
+ return rc;
+ }
+
+ retrocl_be_changelog = slapi_be_select_by_instance_name("changelog");
+ }
+
+ return LDAP_SUCCESS;
+}
+
+/******************************/
+
+/* Function: retrocl_create_cle
+ *
+ * Arguments: none
+ * Returns: nothing
+ * Description: Attempts to create the cn=changelog entry which might already
+ * exist.
+ */
+
+void retrocl_create_cle (void)
+{
+ Slapi_PBlock *pb = NULL;
+ Slapi_Entry *e;
+ int rc;
+ struct berval *vals[2];
+ struct berval val;
+
+ vals[0] = &val;
+ vals[1] = NULL;
+
+ e = slapi_entry_alloc();
+ slapi_entry_set_dn(e,slapi_ch_strdup(RETROCL_CHANGELOG_DN));
+
+ /* Set the objectclass attribute */
+ val.bv_val = "top";
+ val.bv_len = strlen(val.bv_val);
+ slapi_entry_add_values( e, "objectclass", vals );
+
+
+ /* Set the objectclass attribute */
+ val.bv_val = "nsContainer";
+ val.bv_len = strlen(val.bv_val);
+ slapi_entry_add_values( e, "objectclass", vals );
+
+
+ /* Set the objectclass attribute */
+ val.bv_val = "changelog";
+ val.bv_len = strlen(val.bv_val);
+ slapi_entry_add_values( e, "cn", vals );
+
+ val.bv_val = RETROCL_ACL;
+ val.bv_len = strlen(val.bv_val);
+ slapi_entry_add_values( e, "aci", vals );
+
+ pb = slapi_pblock_new ();
+ slapi_add_entry_internal_set_pb( pb, e, NULL /* controls */,
+ g_plg_identity[PLUGIN_RETROCL],
+ 0 /* actions */ );
+ slapi_add_internal_pb (pb);
+ slapi_pblock_get( pb, SLAPI_PLUGIN_INTOP_RESULT, &rc );
+ slapi_pblock_destroy(pb);
+
+ if (rc == 0) {
+ slapi_log_error (SLAPI_LOG_PLUGIN, RETROCL_PLUGIN_NAME,
+ "created cn=changelog\n");
+ } else if (rc == LDAP_ALREADY_EXISTS) {
+ slapi_log_error (SLAPI_LOG_PLUGIN, RETROCL_PLUGIN_NAME,
+ "cn=changelog already existed\n");
+ } else {
+ slapi_log_error( SLAPI_LOG_FATAL, RETROCL_PLUGIN_NAME, "cn=changelog could not be created (%d)\n", rc);
+ }
+}
+
diff --git a/ldap/servers/plugins/retrocl/retrocl_po.c b/ldap/servers/plugins/retrocl/retrocl_po.c
new file mode 100644
index 00000000..96512a51
--- /dev/null
+++ b/ldap/servers/plugins/retrocl/retrocl_po.c
@@ -0,0 +1,529 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+
+#include "retrocl.h"
+
+static int
+entry2reple( Slapi_Entry *e, Slapi_Entry *oe );
+
+static int
+mods2reple( Slapi_Entry *e, LDAPMod **ldm );
+
+static int
+modrdn2reple( Slapi_Entry *e, const char *newrdn, int deloldrdn,
+ LDAPMod **ldm, const char *newsup );
+
+/******************************/
+
+const char *attr_changenumber = "changenumber";
+const char *attr_targetdn = "targetdn";
+const char *attr_changetype = "changetype";
+const char *attr_newrdn = "newrdn";
+const char *attr_deleteoldrdn = "deleteoldrdn";
+const char *attr_changes = "changes";
+const char *attr_newsuperior = "newsuperior";
+const char *attr_changetime = "changetime";
+const char *attr_objectclass = "objectclass";
+
+/*
+ * Function: make_changes_string
+ *
+ * Returns:
+ *
+ * Arguments:
+ *
+ * Description:
+ * loop through the LDAPMod struct and construct the changes attribute/
+ *
+ */
+
+static lenstr *make_changes_string(LDAPMod **ldm, const char **includeattrs)
+{
+ lenstr *l;
+ int i, j, len;
+ int skip;
+
+ l = lenstr_new();
+
+ for ( i = 0; ldm[ i ] != NULL; i++ ) {
+ /* If a list of explicit attributes was given, only add those */
+ if ( NULL != includeattrs ) {
+ skip = 1;
+ for ( j = 0; includeattrs[ j ] != NULL; j++ ) {
+ if ( strcasecmp( includeattrs[ j ], ldm[ i ]->mod_type ) == 0 ) {
+ skip = 0;
+ break;
+ }
+ }
+ if ( skip ) {
+ continue;
+ }
+ }
+ switch ( ldm[ i ]->mod_op & ~LDAP_MOD_BVALUES ) {
+ case LDAP_MOD_ADD:
+ addlenstr( l, "add: " );
+ addlenstr( l, ldm[ i ]->mod_type );
+ addlenstr( l, "\n" );
+ break;
+ case LDAP_MOD_DELETE:
+ addlenstr( l, "delete: " );
+ addlenstr( l, ldm[ i ]->mod_type );
+ addlenstr( l, "\n" );
+ break;
+ case LDAP_MOD_REPLACE:
+ addlenstr( l, "replace: " );
+ addlenstr( l, ldm[ i ]->mod_type );
+ addlenstr( l, "\n" );
+ break;
+ }
+ for ( j = 0; ldm[ i ]->mod_bvalues != NULL &&
+ ldm[ i ]->mod_bvalues[ j ] != NULL; j++ ) {
+ char *buf = NULL;
+ char *bufp = NULL;
+
+ len = strlen( ldm[ i ]->mod_type );
+ len = LDIF_SIZE_NEEDED( len,
+ ldm[ i ]->mod_bvalues[ j ]->bv_len ) + 1;
+ buf = slapi_ch_malloc( len );
+ bufp = buf;
+ ldif_put_type_and_value( &bufp, ldm[ i ]->mod_type,
+ ldm[ i ]->mod_bvalues[ j ]->bv_val,
+ ldm[ i ]->mod_bvalues[ j ]->bv_len );
+ *bufp = '\0';
+
+ addlenstr( l, buf );
+
+ free( buf );
+ }
+ addlenstr( l, "-\n" );
+ }
+ return l;
+}
+
+/*
+ * Function: write_replog_db
+ * Arguments: be - backend to which this change is being applied
+ * optype - type of LDAP operation being logged
+ * dn - distinguished name of entry being changed
+ * log_m - pointer to the actual change operation on a modify
+ * flag - only used by modrdn operations - value of deleteoldrdn
+ * curtime - the current time
+ * Returns: nothing
+ * Description: Given a change, construct an entry which is to be added to the
+ * changelog database.
+ */
+static void
+write_replog_db(
+ int optype,
+ char *dn,
+ LDAPMod **log_m,
+ int flag,
+ time_t curtime,
+ Slapi_Entry *log_e,
+ const char *newrdn,
+ LDAPMod **modrdn_mods,
+ const char *newsuperior
+)
+{
+ char *pat, *edn;
+ struct berval *vals[ 2 ];
+ struct berval val;
+ Slapi_Entry *e;
+ char chnobuf[ 20 ];
+ int err;
+ Slapi_PBlock *pb = NULL;
+ changeNumber changenum;
+
+ PR_Lock(retrocl_internal_lock);
+ changenum = retrocl_assign_changenumber();
+
+ PR_ASSERT( changenum > 0UL );
+ slapi_log_error( SLAPI_LOG_PLUGIN, RETROCL_PLUGIN_NAME,
+ "write_replog_db: write change record %d for dn: \"%s\"\n",
+ changenum, ( dn == NULL ) ? "NULL" : dn );
+
+ /* Construct the dn of this change record */
+ pat = "%s=%lu,%s";
+ edn = slapi_ch_malloc( strlen( pat ) + strlen( RETROCL_CHANGELOG_DN) + 20 );
+ sprintf( edn, pat, attr_changenumber, changenum, RETROCL_CHANGELOG_DN);
+
+ /*
+ * Create the entry struct, and fill in fields common to all types
+ * of change records.
+ */
+ vals[ 0 ] = &val;
+ vals[ 1 ] = NULL;
+
+ e = slapi_entry_alloc();
+ slapi_entry_set_dn( e, slapi_ch_strdup( edn ));
+
+ /* Set the objectclass attribute */
+ val.bv_val = "top";
+ val.bv_len = 3;
+ slapi_entry_add_values( e, "objectclass", vals );
+
+ val.bv_val = "changelogentry";
+ val.bv_len = 14;
+ slapi_entry_add_values( e, "objectclass", vals );
+
+ /* Set the changeNumber attribute */
+ sprintf( chnobuf, "%lu", changenum );
+ val.bv_val = chnobuf;
+ val.bv_len = strlen( chnobuf );
+ slapi_entry_add_values( e, attr_changenumber, vals );
+
+ /* Set the targetentrydn attribute */
+ val.bv_val = dn;
+ val.bv_len = strlen( dn );
+ slapi_entry_add_values( e, attr_targetdn, vals );
+
+ /* Set the changeTime attribute */
+ val.bv_val = format_genTime (curtime);
+ val.bv_len = strlen( val.bv_val );
+ slapi_entry_add_values( e, attr_changetime, vals );
+ free( val.bv_val );
+
+ /*
+ * Finish constructing the entry. How to do it depends on the type
+ * of modification being logged.
+ */
+ err = 0;
+ switch ( optype ) {
+ case OP_ADD:
+ if ( entry2reple( e, log_e ) != 0 ) {
+ err = 1;
+ }
+ break;
+
+ case OP_MODIFY:
+ if ( mods2reple( e, log_m ) != 0 ) {
+ err = 1;
+ }
+ break;
+
+ case OP_MODRDN:
+ if ( modrdn2reple( e, newrdn, flag, modrdn_mods, newsuperior ) != 0 ) {
+ err = 1;
+ }
+ break;
+
+ case OP_DELETE:
+ /* Set the changetype attribute */
+ val.bv_val = "delete";
+ val.bv_len = 6;
+ slapi_entry_add_values( e, attr_changetype, vals );
+ break;
+ default:
+ slapi_log_error( SLAPI_LOG_FATAL, RETROCL_PLUGIN_NAME, "replog: Unknown LDAP operation type "
+ "%d.\n", optype );
+ err = 1;
+ }
+
+ /* Call the repl backend to add this entry */
+ if ( 0 == err ) {
+ int rc;
+
+ pb = slapi_pblock_new ();
+ slapi_add_entry_internal_set_pb( pb, e, NULL /* controls */,
+ g_plg_identity[PLUGIN_RETROCL],
+ 0 /* actions */ );
+ slapi_add_internal_pb (pb);
+ slapi_pblock_get( pb, SLAPI_PLUGIN_INTOP_RESULT, &rc );
+ slapi_pblock_destroy(pb);
+ if ( 0 != rc ) {
+ slapi_log_error( SLAPI_LOG_FATAL, RETROCL_PLUGIN_NAME,
+ "replog: an error occured while adding change "
+ "number %d, dn = %s: %s. \n",
+ changenum, edn, ldap_err2string( rc ));
+ retrocl_release_changenumber();
+ } else {
+ /* Tell the change numbering system this one's committed to disk */
+ retrocl_commit_changenumber( );
+ }
+ } else {
+ slapi_log_error( SLAPI_LOG_FATAL, RETROCL_PLUGIN_NAME,
+ "An error occurred while constructing "
+ "change record number %ld.\n", changenum );
+ retrocl_release_changenumber();
+ }
+ PR_Unlock(retrocl_internal_lock);
+ if ( NULL != edn ) {
+ slapi_ch_free((void **) &edn);
+ }
+
+}
+
+
+/*
+ * Function: entry2reple
+ * Arguments: e - a partially-constructed Slapi_Entry structure
+ * oe - the original entry (an entry being added by a client).
+ * Returns: 0 on success.
+ * Description: Given an Slapi_Entry struct, construct a changelog entry which will be
+ * added to the replication database. It is assumed that e points
+ * to an entry obtained from slapi_entry_alloc().
+ */
+static int
+entry2reple( Slapi_Entry *e, Slapi_Entry *oe )
+{
+ char *p, *estr;
+ struct berval *vals[ 2 ];
+ struct berval val;
+ int len;
+
+ vals[ 0 ] = &val;
+ vals[ 1 ] = NULL;
+
+ /* Set the changetype attribute */
+ val.bv_val = "add";
+ val.bv_len = 3;
+ slapi_entry_add_values( e, attr_changetype, vals );
+
+ estr = slapi_entry2str( oe, &len );
+ p = estr;
+ /* Skip over the dn: line */
+ while (( p = strchr( p, '\n' )) != NULL ) {
+ p++;
+ if ( !ldap_utf8isspace( p )) {
+ break;
+ }
+ }
+ val.bv_val = p;
+ val.bv_len = len - ( p - estr ); /* length + terminating \0 */
+ slapi_entry_add_values( e, attr_changes, vals );
+ free( estr );
+ return 0;
+}
+
+/*
+ * Function: mods2reple
+ * Arguments: e - a partially-constructed Slapi_Entry structure
+ * ldm - an array of pointers to LDAPMod structures describing the
+ * change applied.
+ * Returns: 0 on success.
+ * Description: Given a pointer to an LDAPMod struct and a dn, construct
+ * a new entry which will be added to the replication database.
+ * It is assumed that e points to an entry obtained from
+ * slapi_entry_alloc().
+ */
+static int
+mods2reple( Slapi_Entry *e, LDAPMod **ldm )
+{
+ struct berval val;
+ struct berval *vals[ 2 ];
+ lenstr *l;
+
+ vals[ 0 ] = &val;
+ vals[ 1 ] = NULL;
+
+ /* Set the changetype attribute */
+ val.bv_val = "modify";
+ val.bv_len = 6;
+ slapi_entry_add_values( e, "changetype", vals );
+
+ if (NULL != ldm) {
+ l = make_changes_string( ldm, NULL );
+ if ( NULL != l ) {
+ val.bv_val = l->ls_buf;
+ val.bv_len = l->ls_len + 1; /* string + terminating \0 */
+ slapi_entry_add_values( e, attr_changes, vals );
+ lenstr_free( &l );
+ }
+ }
+ return 0;
+}
+
+
+/*
+ * Function: modrdn2reple
+ * Arguments: e - a partially-constructed Slapi_Entry structure
+ * newrdn - the new relative distinguished name for the entry
+ * deloldrdn - the "deleteoldrdn" flag provided by the client
+ * ldm - any modifications applied as a side-effect of the modrdn
+ * Returns: 0 on success
+ * Description: Given a dn, a new rdn, and a deleteoldrdn flag, construct
+ * a new entry which will be added to the replication database reflecting a
+ * completed modrdn operation. The entry has the same form as above.
+ * It is assumed that e points to an entry obtained from slapi_entry_alloc().
+ */
+static int
+modrdn2reple(
+ Slapi_Entry *e,
+ const char *newrdn,
+ int deloldrdn,
+ LDAPMod **ldm,
+ const char *newsuperior
+)
+{
+ struct berval val;
+ struct berval *vals[ 2 ];
+ lenstr *l;
+ static const char *lastmodattrs[] = {"modifiersname", "modifytimestamp",
+ "creatorsname", "createtimestamp",
+ NULL };
+
+ vals[ 0 ] = &val;
+ vals[ 1 ] = NULL;
+
+ val.bv_val = "modrdn";
+ val.bv_len = 6;
+ slapi_entry_add_values( e, attr_changetype, vals );
+
+ if (newrdn) {
+ val.bv_val = (char *)newrdn; /* cast away const */
+ val.bv_len = strlen( newrdn );
+ slapi_entry_add_values( e, attr_newrdn, vals );
+ }
+
+ if ( deloldrdn == 0 ) {
+ val.bv_val = "FALSE";
+ val.bv_len = 5;
+ } else {
+ val.bv_val = "TRUE";
+ val.bv_len = 4;
+ }
+ slapi_entry_add_values( e, attr_deleteoldrdn, vals );
+
+ if (newsuperior) {
+ val.bv_val = (char *)newsuperior; /* cast away const */
+ val.bv_len = strlen(newsuperior);
+ slapi_entry_add_values(e, attr_newsuperior,vals);
+ }
+
+ if (NULL != ldm) {
+ l = make_changes_string( ldm, lastmodattrs );
+ if ( NULL != l ) {
+ val.bv_val = l->ls_buf;
+ val.bv_len = l->ls_len + 1; /* string + terminating \0 */
+ slapi_entry_add_values( e, attr_changes, vals );
+ lenstr_free( &l );
+ }
+ }
+
+ return 0;
+}
+
+/*
+ * Function: retrocl_postob
+ *
+ * Returns: 0 on success
+ *
+ * Arguments: pblock, optype (add, del, modify etc)
+ *
+ * Description: called from retrocl.c op-specific plugins.
+ *
+ * Please be aware that operation threads may be scheduled out between their
+ * being performed inside of the LDBM database and the changelog plugin
+ * running. For example, suppose MA and MB are two modify operations on the
+ * same entry. MA may be performed on the LDBM database, and then block
+ * before the changelog runs. MB then runs against the LDBM database and then
+ * is written to the changelog. MA starts running. In the changelog, MB will
+ * appear to have been performed before MA, but in the LDBM database the
+ * opposite will have occured.
+ *
+ *
+ */
+
+int retrocl_postob (Slapi_PBlock *pb,int optype)
+{
+ char *dn;
+ LDAPMod **log_m = NULL;
+ int flag = 0;
+ Slapi_Entry *te = NULL;
+ Slapi_Operation *op = NULL;
+ LDAPMod **modrdn_mods = NULL;
+ char *newrdn = NULL;
+ char *newsuperior = NULL;
+ Slapi_Backend *be = NULL;
+ time_t curtime;
+ int rc;
+
+ /*
+ * Check to see if the change was made to the replication backend db.
+ * If so, don't try to log it to the db (otherwise, we'd get in a loop).
+ */
+
+ (void)slapi_pblock_get( pb, SLAPI_BACKEND, &be );
+
+ if (slapi_be_logchanges(be) == 0) {
+ LDAPDebug(LDAP_DEBUG_TRACE,"not applying change if not logging\n",
+ 0,0,0);
+ return 0;
+ }
+
+ if (retrocl_be_changelog == NULL || be == retrocl_be_changelog) {
+ LDAPDebug(LDAP_DEBUG_TRACE,"not applying change if no/cl be\n",0,0,0);
+ return 0;
+ }
+
+ slapi_pblock_get(pb, SLAPI_RESULT_CODE, &rc);
+
+ if (rc != LDAP_SUCCESS) {
+ LDAPDebug(LDAP_DEBUG_TRACE,"not applying change if op failed %d\n",rc,
+ 0,0);
+ return 0;
+ }
+
+ if (slapi_op_abandoned(pb)) {
+ LDAPDebug(LDAP_DEBUG_PLUGIN,"not applying change if op abandoned\n",
+ 0,0,0);
+ return 0;
+ }
+
+ curtime = current_time();
+
+ (void)slapi_pblock_get( pb, SLAPI_ORIGINAL_TARGET_DN, &dn );
+
+ /* change number could be retrieved from Operation extension stored in
+ * the pblock, or else it needs to be made dynamically. */
+
+ /* get the operation extension and retrieve the change number */
+ slapi_pblock_get( pb, SLAPI_OPERATION, &op );
+
+ if (op == NULL) {
+ LDAPDebug(LDAP_DEBUG_TRACE,"not applying change if no op\n",0,0,0);
+ return 0;
+ }
+
+ if (operation_is_flag_set(op, OP_FLAG_TOMBSTONE_ENTRY)){
+ LDAPDebug(LDAP_DEBUG_TRACE,"not applying change for nsTombstone entries\n",0,0,0);
+ return 0;
+ }
+
+ switch ( optype ) {
+ case OP_MODIFY:
+ (void)slapi_pblock_get( pb, SLAPI_MODIFY_MODS, &log_m );
+ break;
+ case OP_ADD:
+ /*
+ * For adds, we want the unnormalized dn, so we can preserve
+ * spacing, case, when replicating it.
+ */
+ (void)slapi_pblock_get( pb, SLAPI_ADD_ENTRY, &te );
+ if ( NULL != te ) {
+ dn = slapi_entry_get_dn( te );
+ }
+ break;
+ case OP_DELETE:
+ break;
+ case OP_MODRDN:
+ (void)slapi_pblock_get( pb, SLAPI_MODRDN_NEWRDN, &newrdn );
+ (void)slapi_pblock_get( pb, SLAPI_MODRDN_DELOLDRDN, &flag );
+ (void)slapi_pblock_get( pb, SLAPI_MODIFY_MODS, &modrdn_mods );
+ (void)slapi_pblock_get( pb, SLAPI_MODRDN_NEWSUPERIOR, &newsuperior);
+ break;
+ }
+
+
+ /* check if we should log change to retro changelog, and
+ * if so, do it here */
+ write_replog_db( optype, dn, log_m, flag, curtime, te,
+ newrdn, modrdn_mods, newsuperior );
+
+ return 0;
+}
+
+
diff --git a/ldap/servers/plugins/retrocl/retrocl_rootdse.c b/ldap/servers/plugins/retrocl/retrocl_rootdse.c
new file mode 100644
index 00000000..602858d3
--- /dev/null
+++ b/ldap/servers/plugins/retrocl/retrocl_rootdse.c
@@ -0,0 +1,64 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+
+#include "retrocl.h"
+
+
+
+/*
+ * Function: retrocl_rootdse_search
+ *
+ * Returns: SLAPI_DSE_CALLBACK_OK always
+ *
+ * Arguments: See plugin API
+ *
+ * Description: callback function plugged into base object search of root DSE.
+ * Adds changelog, firstchangenumber and lastchangenumber attributes.
+ *
+ */
+
+int
+retrocl_rootdse_search(Slapi_PBlock *pb, Slapi_Entry* e, Slapi_Entry* entryAfter, int *returncode, char *returntext, void *arg)
+{
+
+ struct berval val;
+ struct berval *vals[2];
+ vals[0] = &val;
+ vals[1] = NULL;
+
+ /* Changelog information */
+ if ( retrocl_be_changelog != NULL )
+ {
+ char buf[BUFSIZ];
+ changeNumber cnum;
+
+ /* Changelog suffix */
+ val.bv_val = RETROCL_CHANGELOG_DN;
+ if ( val.bv_val != NULL )
+ {
+ val.bv_len = strlen( val.bv_val );
+ slapi_entry_attr_replace( e, "changelog", vals );
+ }
+
+ /* First change number contained in log */
+ cnum = retrocl_get_first_changenumber();
+ sprintf( buf, "%lu", cnum );
+ val.bv_val = buf;
+ val.bv_len = strlen( val.bv_val );
+ slapi_entry_attr_replace( e, "firstchangenumber", vals );
+
+ /* Last change number contained in log */
+ cnum = retrocl_get_last_changenumber();
+ sprintf( buf, "%lu", cnum );
+ val.bv_val = buf;
+ val.bv_len = strlen( val.bv_val );
+ slapi_entry_attr_replace( e, "lastchangenumber", vals );
+ }
+
+ return SLAPI_DSE_CALLBACK_OK;
+}
+
+
diff --git a/ldap/servers/plugins/retrocl/retrocl_trim.c b/ldap/servers/plugins/retrocl/retrocl_trim.c
new file mode 100644
index 00000000..5c413097
--- /dev/null
+++ b/ldap/servers/plugins/retrocl/retrocl_trim.c
@@ -0,0 +1,505 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+
+
+#include "retrocl.h"
+
+typedef struct _trim_status {
+ time_t ts_c_max_age; /* Constraint - max age of a changelog entry */
+ time_t ts_s_last_trim; /* Status - last time we trimmed */
+ int ts_s_initialized; /* Status - non-zero if initialized */
+ int ts_s_trimming; /* non-zero if trimming in progress */
+ PRLock *ts_s_trim_mutex; /* protects ts_s_trimming */
+} trim_status;
+static trim_status ts = {0L, 0L, 0, 0, NULL};
+
+/*
+ * All standard changeLogEntry attributes (initialized in get_cleattrs)
+ */
+static const char *cleattrs[ 10 ] = { NULL, NULL, NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL };
+
+static int retrocl_trimming = 0;
+static Slapi_Eq_Context retrocl_trim_ctx = NULL;
+
+/*
+ * Function: get_cleattrs
+ *
+ * Returns: an array of pointers to attribute names.
+ *
+ * Arguments: None.
+ *
+ * Description: Initializes, if necessary, and returns an array of char *s
+ * with attribute names used for retrieving changeLogEntry
+ * entries from the directory.
+ */
+static const char **get_cleattrs(void)
+{
+ if ( cleattrs[ 0 ] == NULL ) {
+ cleattrs[ 0 ] = attr_objectclass;
+ cleattrs[ 1 ] = attr_changenumber;
+ cleattrs[ 2 ] = attr_targetdn;
+ cleattrs[ 3 ] = attr_changetype;
+ cleattrs[ 4 ] = attr_newrdn;
+ cleattrs[ 5 ] = attr_deleteoldrdn;
+ cleattrs[ 6 ] = attr_changes;
+ cleattrs[ 7 ] = attr_newsuperior;
+ cleattrs[ 8 ] = attr_changetime;
+ cleattrs[ 9 ] = NULL;
+ }
+ return cleattrs;
+}
+
+/*
+ * Function: delete_changerecord
+ *
+ * Returns: LDAP_ error code
+ *
+ * Arguments: the number of the change to delete
+ *
+ * Description:
+ *
+ */
+
+static int
+delete_changerecord( changeNumber cnum )
+{
+ Slapi_PBlock *pb;
+ char *dnbuf;
+ int delrc;
+
+ dnbuf = slapi_ch_malloc( strlen( attr_changenumber ) + 20 +
+ strlen(RETROCL_CHANGELOG_DN));
+ /* Delete the record */
+ sprintf( dnbuf, "%s=%ld, %s", attr_changenumber, cnum,
+ RETROCL_CHANGELOG_DN);
+ pb = slapi_pblock_new ();
+ slapi_delete_internal_set_pb ( pb, dnbuf, NULL /*controls*/, NULL /* uniqueid */,
+ g_plg_identity[PLUGIN_RETROCL], 0 /* actions */ );
+ slapi_delete_internal_pb (pb);
+ slapi_pblock_get( pb, SLAPI_PLUGIN_INTOP_RESULT, &delrc );
+ slapi_pblock_destroy( pb );
+
+ if ( delrc != LDAP_SUCCESS ) {
+ slapi_log_error( SLAPI_LOG_FATAL, RETROCL_PLUGIN_NAME, "delete_changerecord: could not delete "
+ "change record %d\n", cnum );
+ } else {
+ slapi_log_error( SLAPI_LOG_PLUGIN, RETROCL_PLUGIN_NAME,
+ "delete_changerecord: deleted changelog entry \"%s\"\n", dnbuf);
+ }
+ slapi_ch_free((void **) &dnbuf );
+ return delrc;
+}
+
+/*
+ * Function: handle_getchangerecord_result
+ * Arguments: op - pointer to Operation struct for this operation
+ * err - error code returned from search
+ * Returns: nothing
+ * Description: result handler for get_changerecord(). Sets the crt_err
+ * field of the cnum_result_t struct to the error returned
+ * from the backend.
+ */
+static void
+handle_getchangerecord_result( int err, void *callback_data )
+{
+ cnum_result_t *crt = callback_data;
+
+ if ( crt == NULL ) {
+ slapi_log_error( SLAPI_LOG_FATAL, RETROCL_PLUGIN_NAME,
+ "handle_getchangerecord_result: callback_data NULL\n" );
+ } else {
+ crt->crt_err = err;
+ }
+}
+
+/*
+ * Function: handle_getchangerecord_search
+ * Arguments: op - pointer to Operation struct for this operation
+ * e - entry returned by backend
+ * Returns: 0 in all cases
+ * Description: Search result operation handler for get_changerecord().
+ * Sets fields in the cnum_result_t struct pointed to by
+ * op->o_handler_data.
+ */
+static int
+handle_getchangerecord_search( Slapi_Entry *e, void *callback_data)
+{
+ cnum_result_t *crt = callback_data;
+
+ if ( crt == NULL ) {
+ slapi_log_error( SLAPI_LOG_FATAL, RETROCL_PLUGIN_NAME,
+ "handle_getchangerecord_search: op->o_handler_data NULL\n" );
+ } else if ( crt->crt_nentries > 0 ) {
+ /* only return the first entry, I guess */
+ slapi_log_error( SLAPI_LOG_FATAL, RETROCL_PLUGIN_NAME,
+ "handle_getchangerecord_search: multiple entries returned\n" );
+ } else {
+ crt->crt_nentries++;
+ crt->crt_entry = e;
+ }
+
+ return 0;
+}
+
+
+/*
+ * Function: get_changerecord
+ * Arguments: cnum - number of change record to retrieve
+ * Returns: Pointer to an entry structure. The caller must free the entry.
+ * If "err" is non-NULL, an error code is returned in the memory
+ * location it points to.
+ * Description: Retrieve the change record entry whose number is "cnum".
+ */
+static Slapi_Entry *get_changerecord( changeNumber cnum, int *err )
+{
+ cnum_result_t crt, *crtp = &crt;
+ char fstr[ 16 + CNUMSTR_LEN + 2 ];
+ Slapi_PBlock *pb;
+
+ if ( cnum == 0UL ) {
+ if ( err != NULL ) {
+ *err = LDAP_PARAM_ERROR;
+ }
+ return NULL;
+ }
+ crtp->crt_nentries = crtp->crt_err = 0; crtp->crt_entry = NULL;
+ sprintf( fstr, "%s=%ld", attr_changenumber, cnum );
+
+ pb = slapi_pblock_new ();
+ slapi_search_internal_set_pb (pb, RETROCL_CHANGELOG_DN,
+ LDAP_SCOPE_SUBTREE, fstr,
+ (char **)get_cleattrs(), /* cast const */
+ 0 /* attrsonly */,
+ NULL /* controls */, NULL /* uniqueid */,
+ g_plg_identity[PLUGIN_RETROCL],
+ 0 /* actions */);
+
+ slapi_search_internal_callback_pb (pb, crtp,
+ handle_getchangerecord_result,
+ handle_getchangerecord_search, NULL );
+ if ( err != NULL ) {
+ *err = crtp->crt_err;
+ }
+
+ slapi_pblock_destroy (pb);
+
+ return( crtp->crt_entry );
+}
+
+/*
+ * Function: trim_changelog
+ *
+ * Arguments: none
+ *
+ * Returns: 0 on success, -1 on failure
+ *
+ * Description: Trims the changelog, according to the constraints
+ * described by the ts structure.
+ */
+static int trim_changelog(void)
+{
+ int rc = 0, ldrc, done;
+ time_t now;
+ changeNumber first_in_log = 0, last_in_log = 0;
+ Slapi_Entry *e = NULL;
+ int num_deleted = 0;
+ int me,lt;
+
+
+ now = current_time();
+
+ PR_Lock( ts.ts_s_trim_mutex );
+ me = ts.ts_c_max_age;
+ lt = ts.ts_s_last_trim;
+ PR_Unlock( ts.ts_s_trim_mutex );
+
+ if ( now - lt >= (CHANGELOGDB_TRIM_INTERVAL / 1000) ) {
+
+ /*
+ * Trim the changelog. Read sequentially through all the
+ * entries, deleting any which do not meet the criteria
+ * described in the ts structure.
+ */
+ done = 0;
+
+ while ( !done && retrocl_trimming == 1 ) {
+ int did_delete;
+ Slapi_Attr *attr;
+
+ did_delete = 0;
+ first_in_log = retrocl_get_first_changenumber();
+ if ( 0UL == first_in_log ) {
+ slapi_log_error( SLAPI_LOG_PLUGIN, RETROCL_PLUGIN_NAME,
+ "trim_changelog: no changelog records "
+ "to trim\n" );
+ /* Bail out - we can't do any useful work */
+ break;
+ }
+
+ last_in_log = retrocl_get_last_changenumber();
+ if ( last_in_log == first_in_log ) {
+ /* Always leave at least one entry in the change log */
+ break;
+ }
+ if ( me > 0L ) {
+ e = get_changerecord( first_in_log, &ldrc );
+ if ( NULL != e ) {
+ Slapi_Value *sval = NULL;
+ const struct berval *val = NULL;
+ rc = slapi_entry_attr_find( e, attr_changetime, &attr );
+ /* Bug 624442: Logic checking for lack of timestamp was
+ reversed. */
+ if ( 0 != rc || slapi_attr_first_value( attr,&sval ) == -1 ||
+ (val = slapi_value_get_berval ( sval )) == NULL ||
+ NULL == val->bv_val ) {
+ /* What to do if there's no timestamp? Just delete it. */
+ retrocl_set_first_changenumber( first_in_log + 1 );
+ ldrc = delete_changerecord( first_in_log );
+ num_deleted++;
+ did_delete = 1;
+ } else {
+ time_t change_time = parse_localTime( val->bv_val );
+ if ( change_time + me < now ) {
+ retrocl_set_first_changenumber( first_in_log + 1 );
+ ldrc = delete_changerecord( first_in_log );
+ num_deleted++;
+ did_delete = 1;
+ }
+ /* slapi_entry_free( e ); */ /* XXXggood should we be freeing this? */
+ }
+ }
+ }
+ if ( !did_delete ) {
+ done = 1;
+ }
+ }
+ } else {
+ LDAPDebug(LDAP_DEBUG_PLUGIN, "not yet time to trim: %d < (%d+%d)\n",
+ now,lt,(CHANGELOGDB_TRIM_INTERVAL/1000));
+ }
+ PR_Lock( ts.ts_s_trim_mutex );
+ ts.ts_s_trimming = 0;
+ ts.ts_s_last_trim = now;
+ PR_Unlock( ts.ts_s_trim_mutex );
+ if ( num_deleted > 0 ) {
+ slapi_log_error( SLAPI_LOG_PLUGIN, RETROCL_PLUGIN_NAME,
+ "trim_changelog: removed %d change records\n",
+ num_deleted );
+ }
+ return rc;
+}
+
+static int retrocl_active_threads;
+
+/*
+ * Function: changelog_trim_thread_fn
+ *
+ * Returns: nothing
+ *
+ * Arguments: none
+ *
+ * Description: the thread creation callback. retrocl_active_threads is
+ * provided for debugging purposes.
+ *
+ */
+
+static void
+changelog_trim_thread_fn( void *arg )
+{
+ PR_AtomicIncrement(&retrocl_active_threads);
+ trim_changelog();
+ PR_AtomicDecrement(&retrocl_active_threads);
+}
+
+
+
+/*
+ * Function: retrocl_housekeeping
+ * Arguments: cur_time - the current time
+ * Returns: nothing
+ * Description: Determines if it is time to trim the changelog database,
+ * and if so, determines if the changelog database needs to
+ * be trimmed. If so, a thread is started which will trim
+ * the database.
+ */
+
+void retrocl_housekeeping ( time_t cur_time, void *noarg )
+{
+ static time_t thread_start_time;
+ int ldrc;
+
+ if (retrocl_be_changelog == NULL) {
+ LDAPDebug(LDAP_DEBUG_TRACE,"not housekeeping if no cl be\n",0,0,0);
+ return;
+ }
+
+ if ( !ts.ts_s_initialized ) {
+ slapi_log_error( SLAPI_LOG_FATAL, RETROCL_PLUGIN_NAME, "changelog_housekeeping called before "
+ "trimming constraints set\n" );
+ return;
+ }
+
+ PR_Lock( ts.ts_s_trim_mutex );
+ if ( !ts.ts_s_trimming ) {
+ int must_trim = 0;
+ /* See if we need to trim */
+ /* Has enough time elapsed since our last check? */
+ if ( cur_time - ts.ts_s_last_trim >= (ts.ts_c_max_age) ) {
+ /* Is the first entry too old? */
+ time_t first_time;
+ /*
+ * good we could avoid going to the database to retrieve
+ * this time information if we cached the last value we'd read.
+ * But a client might have deleted it over protocol.
+ */
+ first_time = retrocl_getchangetime( SLAPI_SEQ_FIRST, &ldrc );
+ LDAPDebug(LDAP_DEBUG_PLUGIN,
+ "cltrim: ldrc=%d, first_time=%d, cur_time=%d\n",
+ ldrc,first_time,cur_time);
+ if ( LDAP_SUCCESS == ldrc && first_time > (time_t) 0L &&
+ first_time + ts.ts_c_max_age < cur_time ) {
+ must_trim = 1;
+ }
+ }
+ if ( must_trim ) {
+ LDAPDebug(LDAP_DEBUG_TRACE,"changelog about to create thread\n",0,0,0);
+ /* Start a thread to trim the changelog */
+ thread_start_time = cur_time;
+ ts.ts_s_trimming = 1;
+ if ( PR_CreateThread( PR_USER_THREAD,
+ changelog_trim_thread_fn, NULL,
+ PR_PRIORITY_NORMAL, PR_GLOBAL_THREAD, PR_UNJOINABLE_THREAD,
+ RETROCL_DLL_DEFAULT_THREAD_STACKSIZE ) == NULL ) {
+ slapi_log_error( SLAPI_LOG_FATAL, RETROCL_PLUGIN_NAME, "unable to create changelog trimming thread\n" );
+ }
+ } else {
+ LDAPDebug(LDAP_DEBUG_PLUGIN,
+ "changelog does not need to be trimmed\n",0,0,0);
+ }
+ }
+ PR_Unlock( ts.ts_s_trim_mutex );
+}
+
+
+/*
+ * Function: age_str2time
+ *
+ * Returns: time_t
+ *
+ * Arguments: string representation of age (digits and unit s,m,h,d or w)
+ *
+ * Description:
+ * convert time from string like 1h (1 hour) to corresponding time in seconds
+ *
+ */
+
+static time_t
+age_str2time (const char *age)
+{
+ char *maxage;
+ char unit;
+ time_t ageval;
+
+ if (age == NULL || age[0] == '\0' || strcmp (age, "0") == 0) {
+ return 0;
+ }
+
+ maxage = slapi_ch_strdup ( age );
+ unit = maxage[ strlen( maxage ) - 1 ];
+ maxage[ strlen( maxage ) - 1 ] = '\0';
+ ageval = strntoul( maxage, strlen( maxage ), 10 );
+ if ( maxage) {
+ slapi_ch_free ( (void **) &maxage );
+ }
+ switch ( unit ) {
+ case 's':
+ break;
+ case 'm':
+ ageval *= 60;
+ break;
+ case 'h':
+ ageval *= ( 60 * 60 );
+ break;
+ case 'd':
+ ageval *= ( 24 * 60 * 60 );
+ break;
+ case 'w':
+ ageval *= ( 7 * 24 * 60 * 60 );
+ break;
+ default:
+ slapi_log_error( SLAPI_LOG_PLUGIN, "retrocl",
+ "age_str2time: unknown unit \"%c\" "
+ "for maxiumum changelog age\n", unit );
+ ageval = -1;
+ }
+
+ return ageval;
+}
+
+/*
+ * Function: retrocl_init_trimming
+ *
+ * Returns: none, exits on fatal error
+ *
+ * Arguments: none
+ *
+ * Description: called during startup
+ *
+ */
+
+void retrocl_init_trimming (void)
+{
+ const char *cl_maxage;
+ time_t ageval;
+
+ cl_maxage = retrocl_get_config_str(CONFIG_CHANGELOG_MAXAGE_ATTRIBUTE);
+
+ if (cl_maxage == NULL) {
+ LDAPDebug(LDAP_DEBUG_TRACE,"No maxage, not trimming retro changelog.\n",0,0,0);
+ return;
+ }
+ ageval = age_str2time (cl_maxage);
+ slapi_ch_free ((void **)&cl_maxage);
+
+ ts.ts_c_max_age = ageval;
+ ts.ts_s_last_trim = (time_t) 0L;
+ ts.ts_s_trimming = 0;
+ if (( ts.ts_s_trim_mutex = PR_NewLock()) == NULL ) {
+ slapi_log_error( SLAPI_LOG_FATAL, RETROCL_PLUGIN_NAME, "set_changelog_trim_constraints: "
+ "cannot create new lock.\n" );
+ exit( 1 );
+ }
+ ts.ts_s_initialized = 1;
+ retrocl_trimming = 1;
+
+ retrocl_trim_ctx = slapi_eq_repeat(retrocl_housekeeping,
+ NULL,(time_t)0,
+ CHANGELOGDB_TRIM_INTERVAL);
+
+}
+
+/*
+ * Function: retrocl_stop_trimming
+ *
+ * Returns: none
+ *
+ * Arguments: none
+ *
+ * Description: called when server is shutting down to ensure trimming stops
+ * eventually.
+ *
+ */
+
+void retrocl_stop_trimming(void)
+{
+ retrocl_trimming = 0;
+ if (retrocl_trim_ctx) {
+ slapi_eq_cancel(retrocl_trim_ctx);
+ retrocl_trim_ctx = NULL;
+ }
+}
+
diff --git a/ldap/servers/plugins/rever/Makefile b/ldap/servers/plugins/rever/Makefile
new file mode 100644
index 00000000..76203a9c
--- /dev/null
+++ b/ldap/servers/plugins/rever/Makefile
@@ -0,0 +1,111 @@
+#
+# BEGIN COPYRIGHT BLOCK
+# Copyright 2001 Sun Microsystems, Inc.
+# Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+# All rights reserved.
+# END COPYRIGHT BLOCK
+#
+#
+# GNU Makefile for Directory Server password_storaged-plugin.so password storage scheme plugins
+#
+
+LDAP_SRC = ../../..
+MCOM_ROOT = ../../../../..
+
+NOSTDCLEAN=true # don't let nsconfig.mk define target clean
+NOSTDSTRIP=true # don't let nsconfig.mk define target strip
+NSPR20=true # probably should be defined somewhere else (not sure where)
+
+OBJDEST = $(OBJDIR)/lib/libdes
+LIBDIR = $(LIB_RELDIR)
+
+include $(MCOM_ROOT)/ldapserver/nsconfig.mk
+include $(MCOM_ROOT)/ldapserver/ns_usepurify.mk
+include $(LDAP_SRC)/nsldap.mk
+
+ifeq ($(ARCH), WINNT)
+DEF_FILE:=./libdes.def
+endif
+
+CFLAGS+=$(SLCFLAGS)
+
+INCLUDES += -I$(LDAP_SRC)/servers/slapd
+INCLUDES += -I$(MCOM_ROOT)/ldapserver/ldap/include
+
+REVER_OBJS= \
+ rever.o des.o
+
+OBJS = $(addprefix $(OBJDEST)/, $(REVER_OBJS))
+
+ifeq ($(ARCH), WINNT)
+LIBREVER_DLL_OBJ = $(addprefix $(OBJDEST)/, dllmain.o)
+endif
+
+REVER_DLL = des-plugin
+LIBREVER = $(addprefix $(LIBDIR)/, $(REVER_DLL).$(DLL_SUFFIX))
+
+ifeq ($(ARCH), WINNT)
+EXTRA_LIBS_DEP += \
+ $(LIBSLAPD_DEP) \
+ $(LDAP_LIBUTIL_DEP) \
+ $(LDAP_COMMON_LIBS_DEP)
+EXTRA_LIBS_DEP += \
+ $(LDAPSDK_DEP) \
+ $(SECURITY_DEP)
+EXTRA_LIBS += \
+ $(LIBSLAPD) \
+ $(LDAP_SDK_LIBLDAP_DLL) \
+ $(LIBUTIL) \
+ $(NSPRLINK) \
+ $(LDAP_COMMON_LIBS) \
+ $(SECURITYLINK)
+endif
+ifeq ($(ARCH), AIX)
+EXTRA_LIBS_DEP += \
+ $(LIBSLAPD_DEP) \
+ $(LDAP_LIBUTIL_DEP) \
+ $(LDAP_COMMON_LIBS_DEP)
+EXTRA_LIBS_DEP += \
+ $(LDAPSDK_DEP) \
+ $(SECURITY_DEP)
+EXTRA_LIBS += \
+ $(LIBSLAPDLINK) \
+ $(LDAP_SDK_LIBLDAP_DLL) \
+ $(LIBUTIL) \
+ $(NSPRLINK) \
+ $(LDAP_COMMON_LIBS) \
+ $(SECURITYLINK)
+endif
+
+ifeq ($(ARCH), HPUX)
+EXTRA_LIBS_DEP += $(LIBSLAPD_DEP) $(LDAPSDK_DEP) $(NSPR_DEP) $(SECURITY_DEP)
+EXTRA_LIBS += $(DYN_NSHTTPD) $(ADMINUTIL_LINK) $(LDAPLINK) $(SECURITYLINK) $(NSPRLINK) $(ICULINK)
+endif
+
+ifeq ($(ARCH), WINNT)
+DLL_LDFLAGS += -def:"./libdes.def"
+CFLAGS+= /WX
+endif # WINNT
+
+ifeq ($(ARCH), AIX)
+LD=ld
+endif
+
+clientSDK:
+
+all: $(OBJDEST) $(LIBDIR) $(LIBREVER)
+
+$(LIBREVER): $(OBJS) $(LIBREVER_DLL_OBJ) $(DEF_FILE)
+ $(LINK_DLL) $(LIBREVER_DLL_OBJ) $(EXTRA_LIBS)
+
+veryclean: clean
+
+clean:
+ $(RM) $(OBJS)
+ifeq ($(ARCH), WINNT)
+ $(RM) $(LIBREVER_DLL_OBJ)
+endif
+ $(RM) $(LIBREVER)
+
+$(OBJDEST):
+ $(MKDIR) $(OBJDEST)
diff --git a/ldap/servers/plugins/rever/des.c b/ldap/servers/plugins/rever/des.c
new file mode 100644
index 00000000..4b70c91d
--- /dev/null
+++ b/ldap/servers/plugins/rever/des.c
@@ -0,0 +1,465 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+/* from /usr/project/iplanet/ws/ds5.ke/ns/svrcore/pkcs7/tstarchive.c */
+
+#include <string.h>
+#include <stdio.h>
+
+#include <ldap.h>
+#include <nspr.h>
+#include <nss.h>
+#include <secmod.h>
+/*
+#include <secasn1.h>
+#include <secpkcs7.h>
+*/
+#include <key.h>
+#include <certdb.h>
+#include <cert.h>
+#include <svrcore.h>
+#include <secmodt.h>
+#include <prtypes.h>
+#include <seccomon.h>
+#include <pk11func.h>
+
+#include "rever.h"
+#include <slap.h>
+#include "slapi-plugin.h"
+#include <uuid.h>
+
+
+struct pk11MechItem
+{
+ CK_MECHANISM_TYPE type;
+ const char *mechName;
+};
+static const struct pk11MechItem mymech = { CKM_DES_CBC, "DES CBC encryption" };
+
+
+static Slapi_Mutex *mylock = NULL;
+
+struct pk11ContextStore
+{
+ PK11SlotInfo *slot;
+ const struct pk11MechItem *mech;
+
+ PK11SymKey *key;
+ SECItem *params;
+
+ int length;
+ unsigned char *crypt;
+};
+
+static int encode_path(char *inPlain, char **outCipher, char *path);
+static int decode_path(char *inCipher, char **outPlain, char *path);
+static SVRCOREError genKey(struct pk11ContextStore **out, const char *token, char *path);
+static SVRCOREError cryptPassword(struct pk11ContextStore *store, char * clear, unsigned char **out);
+static SVRCOREError decryptPassword(struct pk11ContextStore *store, unsigned char *cipher, char **out, int len);
+static void freeDes(struct pk11ContextStore *out);
+
+static int init = 0;
+
+void
+init_des_plugin()
+{
+ mylock = slapi_new_mutex();
+}
+
+int
+encode(char *inPlain, char **outCipher)
+{
+ return encode_path(inPlain, outCipher, NULL);
+}
+
+static int
+encode_path(char *inPlain, char **outCipher, char *path)
+{
+ struct pk11ContextStore *context = NULL;
+ int err;
+
+ unsigned char *cipher = NULL;
+ char *tmp = NULL;
+ char *base = NULL;
+
+
+ *outCipher = NULL;
+ err = 1;
+
+ if ( genKey(&context, tokDes, path) == SVRCORE_Success )
+ {
+ /* Try an encryption */
+ if ( cryptPassword(context, inPlain, &cipher) == SVRCORE_Success )
+ {
+ base = BTOA_DataToAscii(cipher, context->length);
+ if ( base != NULL )
+ {
+ tmp = slapi_ch_malloc( 3 + strlen(REVER_SCHEME_NAME) + strlen(base));
+ if ( tmp != NULL )
+ {
+ sprintf( tmp, "%c%s%c%s", PWD_HASH_PREFIX_START, REVER_SCHEME_NAME, PWD_HASH_PREFIX_END, base);
+ *outCipher = tmp;
+ tmp = NULL;
+ err = 0;
+ }
+ PORT_Free(base);
+ }
+ }
+ }
+
+ freeDes(context);
+ slapi_ch_free((void **) &context);
+ return(err);
+}
+
+int
+decode(char *inCipher, char **outPlain)
+{
+ return decode_path(inCipher, outPlain, NULL);
+}
+
+
+static int
+decode_path(char *inCipher, char **outPlain, char *path)
+{
+ struct pk11ContextStore *context = NULL;
+ char *plain= NULL;
+ int err;
+
+ unsigned char *base = NULL;
+ int len = 0;
+
+
+ *outPlain = NULL;
+ err = 1;
+
+ if ( genKey(&context, tokDes, path) == SVRCORE_Success )
+ {
+ /* it seems that there is memory leak in that function: bug 400170 */
+
+ base = ATOB_AsciiToData(inCipher, (unsigned int*)&len);
+ if ( base != NULL )
+ {
+ if ( decryptPassword(context, base, &plain, len) == SVRCORE_Success )
+ {
+ *outPlain = plain;
+ err = 0;
+ }
+ }
+ }
+
+ PORT_Free(base);
+ freeDes(context);
+ slapi_ch_free((void **) &context);
+ return(err);
+}
+
+static void freeDes(struct pk11ContextStore *out)
+{
+ if (out)
+ {
+ if (out->slot)
+ slapd_pk11_freeSlot(out->slot);
+ if (out->key)
+ slapd_pk11_freeSymKey(out->key);
+ if (out->params)
+ SECITEM_FreeItem(out->params,PR_TRUE);
+ if (out->crypt)
+ free(out->crypt);
+ }
+}
+
+static SVRCOREError genKey(struct pk11ContextStore **out, const char *token, char *path)
+{
+ SVRCOREError err = SVRCORE_Success;
+ struct pk11ContextStore *store = NULL;
+ SECItem *pwitem = NULL;
+ SECItem *result = NULL;
+ SECAlgorithmID *algid = NULL;
+ SECOidTag algoid;
+ SECItem *salt = NULL;
+ CK_MECHANISM pbeMech;
+ CK_MECHANISM cryptoMech;
+
+ char *instancedir = NULL;
+ char *iv = NULL;
+
+ store = (struct pk11ContextStore*)slapi_ch_malloc(sizeof(*store));
+ if (store == NULL)
+ {
+ return (err = SVRCORE_NoMemory_Error);
+ }
+ *out = store;
+
+ /* Low-level init */
+ store->slot = NULL;
+ store->key = NULL;
+ store->params = NULL;
+ store->crypt = NULL;
+
+ /* Use the tokenName to find a PKCS11 slot */
+ store->slot = slapd_pk11_findSlotByName((char *)token);
+ if (store->slot == NULL)
+ {
+ return (err = SVRCORE_NoSuchToken_Error);
+ }
+
+ /* Generate a key and parameters to do the encryption */
+ store->mech = &mymech;
+
+ /* Generate a unique id, used as salt for the key generation */
+ if ( path == NULL )
+ {
+ instancedir = config_get_instancedir();
+ if ( instancedir == NULL )
+ {
+ return (err = SVRCORE_System_Error);
+ }
+ }
+ else
+ {
+ instancedir = slapi_ch_strdup(path);
+ }
+ if ( slapi_uniqueIDGenerateFromNameString (&iv, NULL, instancedir, strlen(instancedir)) != UID_SUCCESS )
+ {
+ slapi_ch_free((void**)&instancedir);
+ return (err = SVRCORE_System_Error);
+ }
+ slapi_ch_free((void**)&instancedir);
+
+ pwitem = (SECItem *) PORT_Alloc(sizeof(SECItem));
+ if (pwitem == NULL)
+ {
+ return (err = SVRCORE_NoMemory_Error);
+ }
+ pwitem->type = siBuffer;
+ pwitem->data = (unsigned char *)PORT_Alloc(strlen(iv)+1);
+ if (pwitem->data == NULL)
+ {
+ return (err = SVRCORE_NoMemory_Error);
+ }
+ strcpy((char*)pwitem->data, iv);
+ pwitem->len = strlen(iv) + 1;
+
+ algoid = SEC_OID_PKCS5_PBE_WITH_MD2_AND_DES_CBC;
+
+ salt = (SECItem *) PORT_Alloc(sizeof(SECItem));
+ if (salt == NULL)
+ {
+ return (err = SVRCORE_NoMemory_Error);
+ }
+ salt->type = siBuffer;
+ salt->data = (unsigned char *)PORT_Alloc(strlen(iv)+1);
+ if ( salt->data == NULL )
+ {
+ return (err = SVRCORE_NoMemory_Error);
+ }
+ strcpy((char*)salt->data, iv);
+ salt->len = strlen(iv) + 1;
+ slapi_ch_free((void**)&iv);
+
+ algid = slapd_pk11_createPBEAlgorithmID(algoid, 2, salt);
+
+ slapi_lock_mutex(mylock);
+ store->key = slapd_pk11_pbeKeyGen(store->slot, algid, pwitem, 0, 0);
+ if (store->key == 0)
+ {
+ slapi_unlock_mutex(mylock);
+ return (err = SVRCORE_System_Error);
+ }
+
+ slapi_unlock_mutex(mylock);
+ pbeMech.mechanism = slapd_pk11_algtagToMechanism(algoid);
+ result = slapd_pk11_paramFromAlgid(algid);
+ secoid_destroyAlgorithmID(algid, PR_TRUE);
+ pbeMech.pParameter = result->data;
+ pbeMech.ulParameterLen = result->len;
+ if(slapd_pk11_mapPBEMechanismToCryptoMechanism(&pbeMech, &cryptoMech, pwitem,
+ PR_FALSE) != CKR_OK)
+ {
+ SECITEM_FreeItem(result, PR_TRUE);
+ return (err = SVRCORE_System_Error);
+ }
+ SECITEM_FreeItem(result, PR_TRUE);
+ SECITEM_FreeItem(pwitem, PR_TRUE);
+ SECITEM_FreeItem(salt, PR_TRUE);
+ store->params = (SECItem *) PORT_Alloc(sizeof(SECItem));
+ if (store->params == NULL)
+ {
+ return (err = SVRCORE_System_Error);
+ }
+ store->params->type = store->mech->type;
+ store->params->data = (unsigned char *)PORT_Alloc(cryptoMech.ulParameterLen);
+ if (store->params->data == NULL)
+ {
+ return (err = SVRCORE_System_Error);
+ }
+ memcpy(store->params->data, (unsigned char *)cryptoMech.pParameter, cryptoMech.ulParameterLen);
+ store->params->len = cryptoMech.ulParameterLen;
+ PORT_Free(cryptoMech.pParameter);
+ return (err);
+}
+
+static SVRCOREError decryptPassword(struct pk11ContextStore *store, unsigned char *cipher, char **out, int len)
+{
+ SVRCOREError err = SVRCORE_Success;
+ unsigned char *plain = NULL;
+ unsigned char *cipher_with_padding = NULL;
+ SECStatus rv;
+ PK11Context *ctx = 0;
+ int outLen = 0;
+ int blocksize = 0;
+
+ blocksize = slapd_pk11_getBlockSize(store->mech->type, 0);
+ store->length = len;
+
+ /* store->length is the max. length of the returned clear text -
+ must be >= length of crypted bytes - also must be a multiple
+ of blocksize */
+ if (blocksize != 0)
+ {
+ store->length += blocksize - (store->length % blocksize);
+ }
+
+ /* plain will hold the returned clear text */
+ plain = (unsigned char *)slapi_ch_calloc(sizeof(unsigned char),
+ store->length+1);
+ if (!plain)
+ {
+ return (err = SVRCORE_NoMemory_Error);
+ }
+
+ /* create a buffer holding the original cipher bytes, padded with
+ zeros to a multiple of blocksize - do not need +1 since buffer is not
+ a string */
+ cipher_with_padding = (unsigned char *)slapi_ch_calloc(sizeof(unsigned char),
+ store->length);
+ if (!cipher_with_padding)
+ {
+ return (err = SVRCORE_NoMemory_Error);
+ }
+ memcpy(cipher_with_padding, cipher, len);
+
+ ctx = slapd_pk11_createContextBySymKey(store->mech->type, CKA_DECRYPT,
+ store->key, store->params);
+ if (!ctx)
+ {
+ return (err = SVRCORE_System_Error);
+ }
+
+ /* warning - there is a purify UMR in the NSS des code - you may see it when the
+ password is not a multiple of 8 bytes long */
+ rv = slapd_pk11_cipherOp(ctx, plain, &outLen, store->length,
+ cipher_with_padding, store->length);
+ if (rv)
+ {
+ err = SVRCORE_System_Error;
+ }
+
+ rv = slapd_pk11_finalize(ctx);
+ /* we must do the finalize, but we only want to set the err return
+ code if it is not already set */
+ if (rv && (SVRCORE_Success == err))
+ err = SVRCORE_System_Error;
+
+ if (err == SVRCORE_Success)
+ *out = (char *)plain;
+
+ slapi_ch_free((void **)&cipher_with_padding);
+ /* We should free the PK11Context... Something like : */
+ slapd_pk11_destroyContext(ctx, PR_TRUE);
+ return err;
+}
+
+static SVRCOREError cryptPassword(struct pk11ContextStore *store, char * clear, unsigned char **out)
+{
+ SVRCOREError err = SVRCORE_Success;
+ SECStatus rv;
+ PK11Context *ctx = 0;
+ int outLen = 0;
+ int blocksize = 0;
+ unsigned char *clear_with_padding = NULL; /* clear with padding up to blocksize */
+
+ blocksize = slapd_pk11_getBlockSize(store->mech->type, 0);
+ store->length = strlen(clear);
+
+ /* the size of the clear text buffer passed to the des encryption functions
+ must be a multiple of blocksize (usually 8 bytes) - we allocate a buffer
+ of this size, copy the clear text password into it, and pad the rest with
+ zeros */
+ if (blocksize != 0)
+ {
+ store->length += blocksize - (store->length % blocksize);
+ }
+
+ /* store->crypt will hold the crypted password - it must be >= clear length */
+ store->crypt = (unsigned char *)slapi_ch_calloc(sizeof(unsigned char),
+ store->length+1);
+ if (!store->crypt)
+ {
+ return (err = SVRCORE_NoMemory_Error);
+ }
+
+ /* create a buffer big enough to hold the clear text password and padding */
+ clear_with_padding = (unsigned char *)slapi_ch_calloc(sizeof(unsigned char),
+ store->length+1);
+ if (!clear_with_padding)
+ {
+ return (err = SVRCORE_NoMemory_Error);
+ }
+ /* copy the clear text password into the buffer - the calloc insures the
+ remainder is zero padded */
+ strcpy((char *)clear_with_padding, clear);
+
+ ctx = slapd_pk11_createContextBySymKey(store->mech->type, CKA_ENCRYPT,
+ store->key, store->params);
+ if (!ctx)
+ {
+ return (err = SVRCORE_System_Error);
+ }
+
+ rv = slapd_pk11_cipherOp(ctx, store->crypt, &outLen, store->length,
+ clear_with_padding, store->length);
+ if (rv)
+ {
+ err = SVRCORE_System_Error;
+ }
+
+ rv = slapd_pk11_finalize(ctx);
+ /* we must do the finalize, but we only want to set the err return
+ code if it is not already set */
+ if (rv && (SVRCORE_Success == err))
+ err = SVRCORE_System_Error;
+
+ if (err == SVRCORE_Success)
+ *out = store->crypt;
+
+ slapi_ch_free((void **)&clear_with_padding);
+ /* We should free the PK11Context... Something like : */
+ slapd_pk11_destroyContext(ctx, PR_TRUE);
+ return err;
+}
+
+char *
+migrateCredentials(char *oldpath, char *newpath, char *oldcred)
+{
+ char *plain = NULL;
+ char *cipher = NULL;
+
+ init_des_plugin();
+
+ slapd_pk11_configurePKCS11(NULL, NULL, tokDes, ptokDes, NULL, NULL, NULL, NULL, 0, 0 );
+ NSS_NoDB_Init(NULL);
+
+ if ( decode_path(oldcred, &plain, oldpath) == 0 )
+ {
+ if ( encode_path(plain, &cipher, newpath) != 0 )
+ return(NULL);
+ else
+ return(cipher);
+ }
+ else
+ return(NULL);
+}
diff --git a/ldap/servers/plugins/rever/dllmain.c b/ldap/servers/plugins/rever/dllmain.c
new file mode 100644
index 00000000..0bb85815
--- /dev/null
+++ b/ldap/servers/plugins/rever/dllmain.c
@@ -0,0 +1,91 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+
+ /*
+ * Microsoft Windows specifics for LIBPWDSTORAGE DLL
+ */
+#include "rever.h"
+
+
+#ifdef _WIN32
+/* Lifted from Q125688
+ * How to Port a 16-bit DLL to a Win32 DLL
+ * on the MSVC 4.0 CD
+ */
+BOOL WINAPI DllMain (HANDLE hModule, DWORD fdwReason, LPVOID lpReserved)
+{
+
+ switch (fdwReason)
+ {
+ case DLL_PROCESS_ATTACH:
+ /* Code from LibMain inserted here. Return TRUE to keep the
+ DLL loaded or return FALSE to fail loading the DLL.
+
+ You may have to modify the code in your original LibMain to
+ account for the fact that it may be called more than once.
+ You will get one DLL_PROCESS_ATTACH for each process that
+ loads the DLL. This is different from LibMain which gets
+ called only once when the DLL is loaded. The only time this
+ is critical is when you are using shared data sections.
+ If you are using shared data sections for statically
+ allocated data, you will need to be careful to initialize it
+ only once. Check your code carefully.
+
+ Certain one-time initializations may now need to be done for
+ each process that attaches. You may also not need code from
+ your original LibMain because the operating system may now
+ be doing it for you.
+ */
+ /*
+ * 16 bit code calls UnlockData()
+ * which is mapped to UnlockSegment in windows.h
+ * in 32 bit world UnlockData is not defined anywhere
+ * UnlockSegment is mapped to GlobalUnfix in winbase.h
+ * and the docs for both UnlockSegment and GlobalUnfix say
+ * ".. function is oboslete. Segments have no meaning
+ * in the 32-bit environment". So we do nothing here.
+ */
+
+ break;
+
+ case DLL_THREAD_ATTACH:
+ /* Called each time a thread is created in a process that has
+ already loaded (attached to) this DLL. Does not get called
+ for each thread that exists in the process before it loaded
+ the DLL.
+
+ Do thread-specific initialization here.
+ */
+ break;
+
+ case DLL_THREAD_DETACH:
+ /* Same as above, but called when a thread in the process
+ exits.
+
+ Do thread-specific cleanup here.
+ */
+ break;
+
+ case DLL_PROCESS_DETACH:
+ /* Code from _WEP inserted here. This code may (like the
+ LibMain) not be necessary. Check to make certain that the
+ operating system is not doing it for you.
+ */
+
+ break;
+ }
+ /* The return value is only used for DLL_PROCESS_ATTACH; all other
+ conditions are ignored. */
+ return TRUE; /* successful DLL_PROCESS_ATTACH */
+}
+#else
+int CALLBACK
+LibMain( HINSTANCE hinst, WORD wDataSeg, WORD cbHeapSize, LPSTR lpszCmdLine )
+{
+ /*UnlockData( 0 );*/
+ return( 1 );
+}
+#endif
diff --git a/ldap/servers/plugins/rever/libdes.def b/ldap/servers/plugins/rever/libdes.def
new file mode 100644
index 00000000..048af6f4
--- /dev/null
+++ b/ldap/servers/plugins/rever/libdes.def
@@ -0,0 +1,13 @@
+; BEGIN COPYRIGHT BLOCK
+; Copyright 2001 Sun Microsystems, Inc.
+; Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+; All rights reserved.
+; END COPYRIGHT BLOCK
+;
+DESCRIPTION 'Directory Server 6.2.1 Local Credentials Reversible Encryption Plugin'
+EXPORTS
+ des_cmp @2
+ des_enc @3
+ des_dec @4
+ des_init @5
+ migrateCredentials @6
diff --git a/ldap/servers/plugins/rever/rever.c b/ldap/servers/plugins/rever/rever.c
new file mode 100644
index 00000000..10767944
--- /dev/null
+++ b/ldap/servers/plugins/rever/rever.c
@@ -0,0 +1,77 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+#include <stdio.h>
+#include <string.h>
+#include <sys/types.h>
+#include "dirver.h"
+
+#include "rever.h"
+
+static Slapi_PluginDesc pdesc = { "des-storage-scheme", PLUGIN_MAGIC_VENDOR_STR, PRODUCTTEXT, "DES storage scheme plugin" };
+
+static char *plugin_name = "ReverStoragePlugin";
+
+int
+des_cmp( char *userpwd, char *dbpwd )
+{
+ char *cipher = NULL;
+
+ if ( encode(userpwd, &cipher) != 0 )
+ return 1;
+ else
+ return( strcmp(cipher, dbpwd) );
+}
+
+char *
+des_enc( char *pwd )
+{
+ char *cipher = NULL;
+
+ if ( encode(pwd, &cipher) != 0 )
+ return(NULL);
+ else
+ return( cipher );
+}
+
+char *
+des_dec( char *pwd )
+{
+ char *plain = NULL;
+
+ if ( decode(pwd, &plain) != 0 )
+ return(NULL);
+ else
+ return( plain );
+}
+
+int
+des_init( Slapi_PBlock *pb )
+{
+ int rc;
+ char *name;
+
+ slapi_log_error( SLAPI_LOG_PLUGIN, plugin_name, "=> des_init\n" );
+
+ rc = slapi_pblock_set( pb, SLAPI_PLUGIN_VERSION,
+ (void *) SLAPI_PLUGIN_VERSION_01 );
+ rc |= slapi_pblock_set( pb, SLAPI_PLUGIN_DESCRIPTION,
+ (void *)&pdesc );
+ rc |= slapi_pblock_set( pb, SLAPI_PLUGIN_PWD_STORAGE_SCHEME_ENC_FN,
+ (void *) des_enc);
+ rc |= slapi_pblock_set( pb, SLAPI_PLUGIN_PWD_STORAGE_SCHEME_CMP_FN,
+ (void *) des_cmp );
+ rc |= slapi_pblock_set( pb, SLAPI_PLUGIN_PWD_STORAGE_SCHEME_DEC_FN,
+ (void *) des_dec );
+ name = slapi_ch_strdup(REVER_SCHEME_NAME);
+ rc |= slapi_pblock_set( pb, SLAPI_PLUGIN_PWD_STORAGE_SCHEME_NAME,
+ name );
+
+ init_des_plugin();
+
+ slapi_log_error( SLAPI_LOG_PLUGIN, plugin_name, "<= des_init %d\n\n", rc );
+
+ return( rc );
+}
diff --git a/ldap/servers/plugins/rever/rever.h b/ldap/servers/plugins/rever/rever.h
new file mode 100644
index 00000000..0992aea7
--- /dev/null
+++ b/ldap/servers/plugins/rever/rever.h
@@ -0,0 +1,34 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+#ifndef _REVER_H
+#define _REVER_H
+
+#include "slapi-plugin.h"
+#include "nspr.h"
+#include "base64.h"
+#include "slap.h"
+#include "ldaplog.h"
+
+#include <dirlite_strings.h> /* PLUGIN_MAGIC_VENDOR_STR */
+
+#define REVER_SCHEME_NAME "DES"
+#define PWD_HASH_PREFIX_START '{'
+#define PWD_HASH_PREFIX_END '}'
+
+
+int rever_cmp( char *userpwd, char *dbpwd );
+char *rever_enc( char *pwd );
+char *rever_dec( char *pwd );
+int rever_init( Slapi_PBlock *pb );
+void init_des_plugin();
+
+int encode(char *inPlain, char ** outCipher);
+int decode(char *inCipher, char **outPlain);
+
+char *migrateCredentials(char *oldpath, char *newpath, char *oldcred);
+typedef char *(*migrate_fn_type)(char *, char *, char *);
+
+#endif
diff --git a/ldap/servers/plugins/roles/Makefile b/ldap/servers/plugins/roles/Makefile
new file mode 100644
index 00000000..2e936fad
--- /dev/null
+++ b/ldap/servers/plugins/roles/Makefile
@@ -0,0 +1,95 @@
+#
+# BEGIN COPYRIGHT BLOCK
+# Copyright 2001 Sun Microsystems, Inc.
+# Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+# All rights reserved.
+# END COPYRIGHT BLOCK
+#
+LDAP_SRC = ../../..
+MCOM_ROOT = ../../../../..
+
+NOSTDCLEAN=true # don't let nsconfig.mk define target clean
+NOSTDSTRIP=true # don't let nsconfig.mk define target strip
+NSPR20=true # probably should be defined somewhere else (not sure where)
+
+OBJDEST = $(OBJDIR)/lib/libroles
+LIBDIR = $(LIB_RELDIR)
+ifndef INSTDIR
+INSTDIR = c:/netscape/server4/
+endif
+
+include $(MCOM_ROOT)/ldapserver/nsdefs.mk
+include $(MCOM_ROOT)/ldapserver/nsconfig.mk
+include $(LDAP_SRC)/nsldap.mk
+
+ifeq ($(ARCH), WINNT)
+DEF_FILE:=./roles.def
+endif
+
+ROLES_OBJS = roles_plugin.o roles_cache.o
+OBJS = $(addprefix $(OBJDEST)/, $(ROLES_OBJS))
+
+ROLES_DLL = roles-plugin
+
+INCLUDES += -I../../slapd -I../../../include
+CFLAGS+=$(SLCFLAGS) -DSLAPD_LOGGING
+
+# DBDB this is clearly all nonsense: the libraries this thing links with should not depend on the platform.
+# However, for now I make this AIX-specific change and leave the NT-specifc stuff in place (I think it came
+# from the makefile I copied to make this one. After build 3, fix this.
+
+ifeq ($(ARCH), WINNT)
+EXTRA_LIBS_DEP += $(LIBSLAPD)
+EXTRA_LIBS += $(NSPRLINK) $(LIBSLAPD) $(LDAP_LIBAVL)
+endif
+
+ifeq ($(ARCH), HPUX)
+EXTRA_LIBS_DEP += $(LIBSLAPD_DEP) $(LDAPSDK_DEP) $(NSPR_DEP) $(SECURITY_DEP)
+EXTRA_LIBS += $(DYN_NSHTTPD) $(ADMINUTIL_LINK) $(LDAPLINK) $(SECURITYLINK) $(NSPRLINK) $(ICULINK)
+endif
+
+ifeq ($(ARCH), WINNT)
+ROLES_DLL_OBJ = $(addprefix $(OBJDEST)/, dllmain.o)
+endif
+
+ifeq ($(ARCH), AIX)
+LD=ld
+EXTRA_LIBS += $(NSPRLINK) $(LIBSLAPD) $(LDAP_LIBAVL)
+endif
+
+ROLES= $(addprefix $(LIBDIR)/, $(ROLES_DLL).$(DLL_SUFFIX))
+
+clientSDK:
+
+all: $(OBJDEST) $(LIBDIR) $(ROLES)
+
+ifeq ($(ARCH), WINNT)
+$(ROLES): $(OBJS) $(ROLES_DLL_OBJ) $(DEF_FILE)
+ $(LINK_DLL) $(ROLES_DLL_OBJ) $(EXTRA_LIBS) /DEF:$(DEF_FILE)
+else
+$(ROLES): $(OBJS) $(ROLES_DLL_OBJ)
+ $(LINK_DLL) $(ROLES_DLL_OBJ) $(EXTRA_LIBS)
+endif
+
+
+veryclean: clean
+
+clean:
+ $(RM) $(OBJS)
+ifeq ($(ARCH), WINNT)
+ $(RM) $(ROLES_DLL_OBJ)
+endif
+ $(RM) $(ROLES)
+
+$(OBJDEST):
+ $(MKDIR) $(OBJDEST)
+
+$(LIBDIR):
+ $(MKDIR) $(LIBDIR)
+
+# Target to push the built binary to an installed server
+#ROLES_PUSH = $(addprefix $(INSTDIR)lib/, $(notdir $(ROLES)))
+#push: $(ROLES_PUSH)
+
+#$(ROLES_PUSH): $(ROLES)
+# cp $(ROLES) $(ROLES_PUSH)
diff --git a/ldap/servers/plugins/roles/dllmain.c b/ldap/servers/plugins/roles/dllmain.c
new file mode 100644
index 00000000..fabf8677
--- /dev/null
+++ b/ldap/servers/plugins/roles/dllmain.c
@@ -0,0 +1,96 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+/*
+ * Microsoft Windows specifics for BACK-LDBM DLL
+ */
+#include "ldap.h"
+#include "lber.h"
+
+
+#ifdef _WIN32
+/* Lifted from Q125688
+ * How to Port a 16-bit DLL to a Win32 DLL
+ * on the MSVC 4.0 CD
+ */
+BOOL WINAPI DllMain (HANDLE hModule, DWORD fdwReason, LPVOID lpReserved)
+{
+ WSADATA wsadata;
+
+ switch (fdwReason)
+ {
+ case DLL_PROCESS_ATTACH:
+ /* Code from LibMain inserted here. Return TRUE to keep the
+ DLL loaded or return FALSE to fail loading the DLL.
+
+ You may have to modify the code in your original LibMain to
+ account for the fact that it may be called more than once.
+ You will get one DLL_PROCESS_ATTACH for each process that
+ loads the DLL. This is different from LibMain which gets
+ called only once when the DLL is loaded. The only time this
+ is critical is when you are using shared data sections.
+ If you are using shared data sections for statically
+ allocated data, you will need to be careful to initialize it
+ only once. Check your code carefully.
+
+ Certain one-time initializations may now need to be done for
+ each process that attaches. You may also not need code from
+ your original LibMain because the operating system may now
+ be doing it for you.
+ */
+ /*
+ * 16 bit code calls UnlockData()
+ * which is mapped to UnlockSegment in windows.h
+ * in 32 bit world UnlockData is not defined anywhere
+ * UnlockSegment is mapped to GlobalUnfix in winbase.h
+ * and the docs for both UnlockSegment and GlobalUnfix say
+ * ".. function is oboslete. Segments have no meaning
+ * in the 32-bit environment". So we do nothing here.
+ */
+
+ if( errno = WSAStartup(0x0101, &wsadata ) != 0 )
+ return FALSE;
+
+ break;
+
+ case DLL_THREAD_ATTACH:
+ /* Called each time a thread is created in a process that has
+ already loaded (attached to) this DLL. Does not get called
+ for each thread that exists in the process before it loaded
+ the DLL.
+
+ Do thread-specific initialization here.
+ */
+ break;
+
+ case DLL_THREAD_DETACH:
+ /* Same as above, but called when a thread in the process
+ exits.
+
+ Do thread-specific cleanup here.
+ */
+ break;
+
+ case DLL_PROCESS_DETACH:
+ /* Code from _WEP inserted here. This code may (like the
+ LibMain) not be necessary. Check to make certain that the
+ operating system is not doing it for you.
+ */
+ WSACleanup();
+
+ break;
+ }
+ /* The return value is only used for DLL_PROCESS_ATTACH; all other
+ conditions are ignored. */
+ return TRUE; // successful DLL_PROCESS_ATTACH
+}
+#else
+int CALLBACK
+LibMain( HINSTANCE hinst, WORD wDataSeg, WORD cbHeapSize, LPSTR lpszCmdLine )
+{
+ /*UnlockData( 0 );*/
+ return( 1 );
+}
+#endif
diff --git a/ldap/servers/plugins/roles/roles.def b/ldap/servers/plugins/roles/roles.def
new file mode 100644
index 00000000..0ae9d85f
--- /dev/null
+++ b/ldap/servers/plugins/roles/roles.def
@@ -0,0 +1,10 @@
+; BEGIN COPYRIGHT BLOCK
+; Copyright 2001 Sun Microsystems, Inc.
+; Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+; All rights reserved.
+; END COPYRIGHT BLOCK
+;
+DESCRIPTION 'Netscape Directory Server 7.0 Roles Plugin'
+EXPORTS
+ roles_init @2
+ plugin_init_debug_level @3
diff --git a/ldap/servers/plugins/roles/roles_cache.c b/ldap/servers/plugins/roles/roles_cache.c
new file mode 100644
index 00000000..f4dc31c2
--- /dev/null
+++ b/ldap/servers/plugins/roles/roles_cache.c
@@ -0,0 +1,2061 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+#include <stdio.h>
+#include <string.h>
+#include <sys/types.h>
+#include "portable.h"
+#include "slapi-plugin.h"
+#include <dirlite_strings.h> /* PLUGIN_MAGIC_VENDOR_STR */
+#include "dirver.h"
+
+/* This is naughty ... */
+#include "slapi-private.h"
+
+/* include NSPR header files */
+#include "slap.h"
+#include "prthread.h"
+#include "prlock.h"
+#include "prerror.h"
+#include "prcvar.h"
+#include "prio.h"
+#include "avl.h"
+#include "vattr_spi.h"
+#include "roles_cache.h"
+#include "views.h"
+
+#ifdef SOLARIS
+#include <tnf/probe.h>
+#else
+#define TNF_PROBE_0(a,b,c)
+#endif
+
+#define MAX_NESTED_ROLES 30
+
+static char *allUserAttributes[] = {
+ LDAP_ALL_USER_ATTRS,
+ NULL
+};
+
+/* views scoping */
+static void **views_api;
+
+/* Service provider handler */
+static vattr_sp_handle *vattr_handle = NULL;
+
+/* List of nested roles */
+typedef struct _role_object_nested {
+ Slapi_DN *dn; /* value of attribute nsroledn in a nested role definition */
+} role_object_nested;
+
+/* Role object structure */
+typedef struct _role_object {
+ Slapi_DN *dn; /* dn of a role entry */
+ int type; /* ROLE_TYPE_MANAGED|ROLE_TYPE_FILTERED|ROLE_TYPE_NESTED */
+ Slapi_Filter *filter; /* if ROLE_TYPE_FILTERED */
+ Avlnode *avl_tree; /* if ROLE_TYPE_NESTED: tree of nested DNs (avl_data is a role_object_nested struct) */
+} role_object;
+
+/* Structure containing the roles definitions for a given suffix */
+typedef struct _roles_cache_def {
+
+ /* Suffix DN*/
+ Slapi_DN *suffix_dn;
+
+ /* Module level thread control */
+ PRThread *roles_tid;
+ int keeprunning;
+
+ Slapi_Mutex *cache_lock;
+ Slapi_Mutex *stop_lock;
+
+ Slapi_Mutex *change_lock;
+ Slapi_CondVar *something_changed;
+
+ Slapi_Mutex *create_lock;
+ Slapi_CondVar *suffix_created;
+ int is_ready;
+
+ /* Root of the avl tree containing all the roles definitions
+ NB: avl_data field is of type role_object
+ */
+ Avlnode *avl_tree;
+
+ /* Next roles suffix definitions */
+ struct _roles_cache_def *next;
+
+ /* Info passed from the server when an notification is sent to the plugin */
+ char *notified_dn;
+ Slapi_Entry *notified_entry;
+ int notified_operation;
+
+} roles_cache_def;
+
+
+/* Global list containing all the roles definitions per suffix */
+static roles_cache_def *roles_list = NULL;
+
+static PRRWLock *global_lock = NULL;
+
+/* Structure holding the nsrole values */
+typedef struct _roles_cache_build_result
+{
+ Slapi_ValueSet **nsrole_values; /* nsrole computed values */
+ Slapi_Entry *requested_entry; /* entry to get nsrole from */
+ int has_value; /* flag to determine if a new value has been added to the result */
+ int need_value; /* flag to determine if we need the result */
+} roles_cache_build_result;
+
+/* Structure used to check if is_entry_member_of is part of a role defined in its suffix */
+typedef struct _roles_cache_search_in_nested
+{
+ Slapi_Entry *is_entry_member_of;
+ int present; /* flag to know if the entry is part of a role */
+ int hint; /* to check the depth of the nested */
+} roles_cache_search_in_nested;
+
+/* Structure used to handle roles searches */
+typedef struct _roles_cache_search_roles
+{
+ roles_cache_def *suffix_def;
+ int rc; /* to check the depth of the nested */
+} roles_cache_search_roles;
+
+static roles_cache_def* roles_cache_create_suffix(Slapi_DN *sdn);
+static int roles_cache_add_roles_from_suffix(Slapi_DN *suffix_dn, roles_cache_def *suffix_def);
+static void roles_cache_wait_on_change(void * arg);
+static void roles_cache_trigger_update_suffix(void *handle, char *be_name, int old_be_state, int new_be_state);
+static void roles_cache_trigger_update_role(char *dn, Slapi_Entry *role_entry, Slapi_DN *be_dn, int operation);
+static int roles_cache_update(roles_cache_def *suffix_to_update);
+static int roles_get_roles_from_entry(Slapi_DN * suffix, Slapi_PBlock **int_search_pb);
+static int roles_cache_create_role_under(roles_cache_def** roles_cache_suffix, Slapi_Entry *entry);
+static int roles_cache_create_object_from_entry(Slapi_Entry *role_entry, role_object **result, int hint);
+static int roles_cache_determine_class(Slapi_Entry *role_entry);
+static int roles_cache_node_cmp( caddr_t d1, caddr_t d2 );
+static int roles_cache_insert_object(Avlnode **tree, role_object *object);
+static int roles_cache_node_nested_cmp( caddr_t d1, caddr_t d2 );
+static int roles_cache_insert_object_nested(Avlnode **tree, role_object_nested *object);
+static int roles_cache_object_nested_from_dn(Slapi_DN *role_dn, role_object_nested **result);
+static int roles_cache_build_nsrole( caddr_t data, caddr_t arg );
+static int roles_cache_find_node( caddr_t d1, caddr_t d2 );
+static int roles_cache_find_roles_in_suffix(Slapi_DN *target_entry_dn, roles_cache_def **list_of_roles);
+static int roles_is_entry_member_of_object(caddr_t data, caddr_t arg );
+static int roles_check_managed(Slapi_Entry *entry_to_check, role_object *role, int *present);
+static int roles_check_filtered(Slapi_Entry *entry_to_check, role_object *role, int *present);
+static int roles_check_nested(caddr_t data, caddr_t arg);
+static int roles_is_inscope(Slapi_Entry *entry_to_check, Slapi_DN *role_dn);
+static void berval_set_string(struct berval *bv, const char* string);
+static void roles_cache_role_def_delete(roles_cache_def *role_def);
+static void roles_cache_role_def_free(roles_cache_def *role_def);
+static void roles_cache_role_object_free(role_object *this_role);
+static void roles_cache_role_object_nested_free(role_object_nested *this_role);
+static int roles_cache_dump( caddr_t data, caddr_t arg );
+static int roles_cache_add_entry_cb(Slapi_Entry* e, void *callback_data);
+static void roles_cache_result_cb( int rc, void *callback_data);
+static Slapi_DN* roles_cache_get_top_suffix(Slapi_DN *suffix);
+
+/* ============== FUNCTIONS ================ */
+
+/* roles_cache_init
+ ----------------
+ create the cache for all the existing suffixes
+ starts up the threads which wait for changes
+ also registers vattr callbacks
+
+ return 0 if OK
+ return -1 otherwise
+*/
+int roles_cache_init()
+{
+ int rc = 0;
+ void *node = NULL;
+ Slapi_DN *sdn = NULL;
+ roles_cache_def *new_suffix = NULL;
+
+ slapi_log_error(SLAPI_LOG_PLUGIN,
+ ROLES_PLUGIN_SUBSYSTEM, "--> roles_cache_init\n");
+
+ if ( global_lock == NULL )
+ {
+ global_lock = PR_NewRWLock(0,"roles_cache");
+ }
+
+ /* grab the views interface */
+ if(slapi_apib_get_interface(Views_v1_0_GUID, &views_api))
+ {
+ /* lets be tolerant if views is disabled */
+ views_api = 0;
+ }
+
+ /* For each top suffix, get the roles definitions defined below it */
+ PR_RWLock_Wlock(global_lock);
+
+ sdn = slapi_get_first_suffix(&node, 0);
+ while (sdn)
+ {
+
+ if ( (new_suffix = roles_cache_create_suffix(sdn)) == NULL )
+ {
+ PR_DestroyRWLock(global_lock);
+ global_lock = NULL;
+ return(-1);
+ }
+
+ if ( roles_cache_add_roles_from_suffix(sdn, new_suffix) != 0 )
+ {
+ /* No roles in that suffix, stop the thread and remove it ? */
+ }
+ sdn = slapi_get_next_suffix(&node, 0);
+ }
+ PR_RWLock_Unlock(global_lock);
+
+ /* to expose roles_check to ACL plugin */
+ slapi_register_role_check(roles_check);
+
+ /* Register a callback on backends creation|modification|deletion,
+ so that we update the corresponding cache */
+ slapi_register_backend_state_change(NULL, roles_cache_trigger_update_suffix);
+
+ if ( slapi_vattrspi_register((vattr_sp_handle **)&vattr_handle,
+ roles_sp_get_value,
+ roles_sp_compare_value,
+ roles_sp_list_types) )
+ {
+ slapi_log_error( SLAPI_LOG_FATAL, ROLES_PLUGIN_SUBSYSTEM,
+ "roles_cache_init: slapi_vattrspi_register failed\n");
+
+ PR_DestroyRWLock(global_lock);
+ global_lock = NULL;
+ return(-1);
+ }
+ else if ( slapi_vattrspi_regattr(vattr_handle,NSROLEATTR,"", NULL) )
+ {
+ slapi_log_error( SLAPI_LOG_FATAL, ROLES_PLUGIN_SUBSYSTEM,
+ "roles_cache_init: slapi_vattrspi_regattr failed\n");
+ free(vattr_handle);
+ PR_DestroyRWLock(global_lock);
+ global_lock = NULL;
+ return(-1);
+ }
+
+ slapi_log_error( SLAPI_LOG_PLUGIN, ROLES_PLUGIN_SUBSYSTEM, "<-- roles_cache_init\n");
+ return rc;
+}
+
+/* roles_cache_create_suffix
+ -------------------------
+ Create a new entry in the global list
+ return a pointer on the suffix stucture: OK
+ return NULL: fail
+ */
+static roles_cache_def *roles_cache_create_suffix(Slapi_DN *sdn)
+{
+ roles_cache_def *current_suffix = NULL;
+ roles_cache_def *new_suffix = NULL;
+
+ slapi_log_error( SLAPI_LOG_PLUGIN, ROLES_PLUGIN_SUBSYSTEM, "--> roles_cache_create_suffix\n");
+
+ /* Allocate a new suffix block */
+ new_suffix = (roles_cache_def*)slapi_ch_calloc(1, sizeof(roles_cache_def));
+ if ( new_suffix == NULL )
+ {
+ slapi_log_error(SLAPI_LOG_FATAL,
+ ROLES_PLUGIN_SUBSYSTEM,
+ "roles_cache_create_suffix: Unable to allocate memory, cannot create role cache\n");
+ return(NULL);
+ }
+
+ new_suffix->cache_lock = slapi_new_mutex();
+ new_suffix->change_lock = slapi_new_mutex();
+ new_suffix->stop_lock = slapi_new_mutex();
+ new_suffix->create_lock = slapi_new_mutex();
+ if ( new_suffix->stop_lock == NULL ||
+ new_suffix->change_lock == NULL ||
+ new_suffix->cache_lock == NULL ||
+ new_suffix->create_lock == NULL )
+ {
+ slapi_log_error( SLAPI_LOG_FATAL, ROLES_PLUGIN_SUBSYSTEM,
+ "roles_cache_create_suffix: Lock creation failed\n");
+ roles_cache_role_def_free(new_suffix);
+ return(NULL);
+ }
+
+ new_suffix->something_changed = slapi_new_condvar(new_suffix->change_lock);
+ if ( new_suffix->something_changed == NULL)
+ {
+ slapi_log_error( SLAPI_LOG_FATAL, ROLES_PLUGIN_SUBSYSTEM,
+ "roles_cache_create_suffix: Lock creation failed\n");
+ roles_cache_role_def_free(new_suffix);
+ return(NULL);
+ }
+
+ new_suffix->suffix_created = slapi_new_condvar(new_suffix->create_lock);
+ if ( new_suffix->suffix_created == NULL)
+ {
+ slapi_log_error( SLAPI_LOG_FATAL, ROLES_PLUGIN_SUBSYSTEM,
+ "roles_cache_create_suffix: Lock creation failed\n");
+ roles_cache_role_def_free(new_suffix);
+ return(NULL);
+ }
+
+ new_suffix->keeprunning = 1;
+
+ new_suffix->suffix_dn = slapi_sdn_dup(sdn);
+
+ /* those 3 items are used to give back info to the thread when
+ it is awakened */
+ new_suffix->notified_dn = NULL;
+ new_suffix->notified_entry = NULL;
+ new_suffix->notified_operation = 0;
+
+ /* Create the global list */
+ if ( roles_list == NULL )
+ {
+ roles_list = new_suffix;
+ }
+ else
+ {
+ current_suffix = roles_list;
+ while ( current_suffix != NULL )
+ {
+ if ( current_suffix->next == NULL )
+ {
+ current_suffix->next = new_suffix;
+ break;
+ }
+ else
+ {
+ current_suffix = current_suffix->next;
+ }
+ }
+ }
+
+ /* to prevent deadlock */
+ new_suffix->is_ready = 0;
+ if ( (new_suffix->roles_tid = PR_CreateThread (PR_USER_THREAD,
+ roles_cache_wait_on_change,
+ (void*)new_suffix,
+ PR_PRIORITY_NORMAL,
+ PR_GLOBAL_THREAD,
+ PR_UNJOINABLE_THREAD,
+ SLAPD_DEFAULT_THREAD_STACKSIZE)) == NULL )
+ {
+ slapi_log_error( SLAPI_LOG_FATAL, ROLES_PLUGIN_SUBSYSTEM,
+ "roles_cache_create_suffix: PR_CreateThread failed\n");
+ roles_cache_role_def_delete(new_suffix);
+ return(NULL);
+ }
+
+ slapi_lock_mutex(new_suffix->create_lock);
+ if (new_suffix->is_ready != 1)
+ {
+ slapi_wait_condvar(new_suffix->suffix_created, NULL);
+ }
+ slapi_unlock_mutex(new_suffix->create_lock);
+
+ slapi_log_error( SLAPI_LOG_PLUGIN, ROLES_PLUGIN_SUBSYSTEM, "<-- roles_cache_create_suffix\n");
+ return(new_suffix);
+}
+
+/* roles_cache_wait_on_change
+ --------------------------
+ Sit around waiting on a notification that something has
+ changed, then fires off the updates
+ */
+static void roles_cache_wait_on_change(void * arg)
+{
+ roles_cache_def *roles_def = (roles_cache_def*)arg;
+
+ slapi_log_error( SLAPI_LOG_PLUGIN, ROLES_PLUGIN_SUBSYSTEM, "--> roles_cache_wait_on_change\n");
+
+ slapi_lock_mutex(roles_def->stop_lock);
+ slapi_lock_mutex(roles_def->change_lock);
+
+ while ( roles_def->keeprunning)
+ {
+ slapi_unlock_mutex(roles_def->change_lock);
+ slapi_lock_mutex(roles_def->change_lock);
+
+ /* means that the thread corresponding to that suffix is ready to receive notifications
+ from the server */
+ slapi_lock_mutex(roles_def->create_lock);
+ if ( roles_def->is_ready == 0 )
+ {
+ slapi_notify_condvar( roles_def->suffix_created, 1 );
+ roles_def->is_ready = 1;
+ }
+ slapi_unlock_mutex(roles_def->create_lock);
+
+ /* XXX In case the BE containing this role_def signaled
+ a shut down between the unlock/lock above should
+ test roles_def->keeprunning before
+ going to sleep.
+ */
+ slapi_wait_condvar(roles_def->something_changed, NULL);
+
+ slapi_log_error( SLAPI_LOG_PLUGIN, ROLES_PLUGIN_SUBSYSTEM, "roles_cache_wait_on_change \n");
+
+ if ( roles_def->keeprunning )
+ {
+ roles_cache_update(roles_def);
+ }
+ }
+
+ /* shut down the cache */
+ slapi_unlock_mutex(roles_def->change_lock);
+ slapi_unlock_mutex(roles_def->stop_lock);
+
+ roles_cache_role_def_free(roles_def);
+
+ slapi_log_error( SLAPI_LOG_PLUGIN, ROLES_PLUGIN_SUBSYSTEM, "<-- roles_cache_wait_on_change thread exit\n");
+}
+
+/* roles_cache_trigger_update_suffix
+ --------------------------------
+ This is called when a backend changes state (created|modified|deleted)
+ We simply signal to update the associated role cache in this case
+ */
+static void roles_cache_trigger_update_suffix(void *handle, char *be_name, int old_be_state, int new_be_state)
+{
+ roles_cache_def *current_role = roles_list;
+ const Slapi_DN *be_suffix_dn = NULL;
+ Slapi_DN *top_suffix_dn = NULL;
+ Slapi_Backend *backend = NULL;
+ int found = 0;
+
+ PR_RWLock_Wlock(global_lock);
+
+ if ( (new_be_state == SLAPI_BE_STATE_DELETE) || (new_be_state == SLAPI_BE_STATE_OFFLINE) )
+ {
+ /* Invalidate and rebuild the whole cache */
+ roles_cache_def *current_role = NULL;
+ roles_cache_def *next_role = NULL;
+ Slapi_DN *sdn = NULL;
+ void *node = NULL;
+ roles_cache_def *new_suffix = NULL;
+
+ /* Go through all the roles list and trigger the associated structure */
+ current_role = roles_list;
+ while ( current_role )
+ {
+ slapi_lock_mutex(current_role->change_lock);
+ current_role->keeprunning = 0;
+ next_role = current_role->next;
+ slapi_notify_condvar(current_role->something_changed, 1 );
+ slapi_unlock_mutex(current_role->change_lock);
+
+ current_role = next_role;
+ }
+
+ /* rebuild a new one */
+ roles_list = NULL;
+
+ sdn = slapi_get_first_suffix(&node, 0);
+ while (sdn)
+ {
+
+ if ( (new_suffix = roles_cache_create_suffix(sdn)) == NULL )
+ {
+ PR_RWLock_Unlock(global_lock);
+ return;
+ }
+
+ roles_cache_add_roles_from_suffix(sdn, new_suffix);
+ sdn = slapi_get_next_suffix(&node, 0);
+ }
+ PR_RWLock_Unlock(global_lock);
+ return;
+ }
+
+ /* Backend back on line or new one created*/
+ backend = slapi_be_select_by_instance_name(be_name);
+ if ( backend != NULL )
+ {
+ be_suffix_dn = slapi_be_getsuffix(backend, 0);
+ top_suffix_dn = roles_cache_get_top_suffix((Slapi_DN *)be_suffix_dn);
+ }
+
+ while ( (current_role != NULL) && !found && (top_suffix_dn != NULL) )
+ {
+ /* The backend already exists (back online): so invalidate "old roles definitions" */
+ if ( slapi_sdn_compare(current_role->suffix_dn, top_suffix_dn) == 0 )
+ {
+ roles_cache_role_def_delete(current_role);
+ found = 1;
+ }
+ else
+ {
+ current_role = current_role->next;
+ }
+ }
+
+ if ( top_suffix_dn != NULL )
+ {
+ /* Add the new definitions in the cache */
+ roles_cache_def *new_suffix = roles_cache_create_suffix(top_suffix_dn);
+
+ if ( new_suffix != NULL )
+ {
+ roles_cache_add_roles_from_suffix(top_suffix_dn, new_suffix);
+ }
+ slapi_sdn_free(&top_suffix_dn);
+ }
+
+ PR_RWLock_Unlock(global_lock);
+}
+
+/* roles_cache_trigger_update_role
+ --------------------------------
+ Call when an entry containing a role definition has been added, modified
+ or deleted
+ */
+static void roles_cache_trigger_update_role(char *dn, Slapi_Entry *roles_entry, Slapi_DN *be_dn, int operation)
+{
+ int found = 0;
+ roles_cache_def *current_role = NULL;
+
+ PR_RWLock_Wlock(global_lock);
+
+ current_role = roles_list;
+
+ slapi_log_error( SLAPI_LOG_PLUGIN,
+ ROLES_PLUGIN_SUBSYSTEM, "--> roles_cache_trigger_update_role: %x \n", roles_list);
+
+ /* Go through all the roles list and trigger the associated structure */
+
+ /* be_dn is already the top suffix for that dn */
+ while ( (current_role != NULL) && !found )
+ {
+ if ( slapi_sdn_compare(current_role->suffix_dn, be_dn) == 0 )
+ {
+ found = 1;
+ }
+ else
+ {
+ current_role = current_role->next;
+ }
+ }
+
+ if ( found )
+ {
+ slapi_lock_mutex(current_role->change_lock);
+
+ slapi_entry_free (current_role->notified_entry);
+ current_role->notified_entry = roles_entry;
+ slapi_ch_free ((void**)&(current_role->notified_dn));
+ current_role->notified_dn = dn;
+ current_role->notified_operation = operation;
+
+ roles_cache_update(current_role);
+
+ slapi_unlock_mutex(current_role->change_lock);
+ }
+
+ PR_RWLock_Unlock(global_lock);
+
+ slapi_log_error( SLAPI_LOG_PLUGIN, ROLES_PLUGIN_SUBSYSTEM, "<-- roles_cache_trigger_update_role: %x \n", roles_list);
+}
+
+/* roles_cache_update
+ ------------------
+ Update the cache associated to a suffix
+ Return 0: ok
+ Return -1: fail
+ */
+static int roles_cache_update(roles_cache_def *suffix_to_update)
+{
+ int rc = 0;
+ int operation;
+ Slapi_Entry *entry = NULL;
+ Slapi_DN *dn = NULL;
+ role_object *to_delete = NULL;
+
+ slapi_log_error( SLAPI_LOG_PLUGIN, ROLES_PLUGIN_SUBSYSTEM, "--> roles_cache_update \n");
+
+ slapi_lock_mutex(suffix_to_update->cache_lock);
+
+ operation = suffix_to_update->notified_operation;
+ entry = suffix_to_update->notified_entry;
+ dn = slapi_sdn_new();
+ slapi_sdn_set_dn_byval(dn, suffix_to_update->notified_dn);
+
+ if ( (entry != NULL) && (dn != NULL) )
+ {
+ if ( (operation == SLAPI_OPERATION_MODIFY) ||
+ (operation == SLAPI_OPERATION_DELETE) )
+ {
+ /* delete it */
+ int dummy;
+
+ to_delete = (role_object *)avl_delete(&(suffix_to_update->avl_tree), dn, roles_cache_find_node, &dummy);
+ roles_cache_role_object_free(to_delete);
+ to_delete = NULL;
+ if ( slapi_is_loglevel_set(SLAPI_LOG_PLUGIN) )
+ {
+ avl_apply(suffix_to_update->avl_tree, (IFP)roles_cache_dump, &rc, -1, AVL_INORDER);
+ }
+
+ }
+ if ( (operation == SLAPI_OPERATION_MODIFY) ||
+ (operation ==SLAPI_OPERATION_ADD) )
+ {
+ rc = roles_cache_create_role_under(&suffix_to_update,entry);
+ }
+ if ( entry != NULL )
+ {
+ slapi_entry_free(entry);
+ }
+ suffix_to_update->notified_entry = NULL;
+
+ }
+ slapi_unlock_mutex(suffix_to_update->cache_lock);
+
+ if ( dn != NULL )
+ {
+ slapi_sdn_free(&dn);
+ }
+
+ slapi_log_error( SLAPI_LOG_PLUGIN, ROLES_PLUGIN_SUBSYSTEM, "<-- roles_cache_update \n");
+ return(rc);
+}
+
+/* roles_cache_stop
+ ----------------
+
+ XXX the stop_lock of a roles_cache_def
+ doesn't seem to serve any useful purpose...
+
+ */
+void roles_cache_stop()
+{
+ roles_cache_def *current_role = NULL;
+ roles_cache_def *next_role = NULL;
+
+ slapi_log_error( SLAPI_LOG_PLUGIN, ROLES_PLUGIN_SUBSYSTEM, "--> roles_cache_stop\n");
+
+ /* Go through all the roles list and trigger the associated structure */
+ PR_RWLock_Wlock(global_lock);
+ current_role = roles_list;
+ while ( current_role )
+ {
+ slapi_lock_mutex(current_role->change_lock);
+ current_role->keeprunning = 0;
+ next_role = current_role->next;
+ slapi_notify_condvar(current_role->something_changed, 1 );
+ slapi_unlock_mutex(current_role->change_lock);
+
+ current_role = next_role;
+ }
+ PR_RWLock_Unlock(global_lock);
+
+ slapi_log_error( SLAPI_LOG_PLUGIN, ROLES_PLUGIN_SUBSYSTEM, "<-- roles_cache_stop\n");
+}
+
+/* roles_cache_is_role_entry
+ -------------------------
+ Check if the entry is a role
+ return -1: error in processing
+ return 0: entry is not a role
+ return 1: entry is a role
+*/
+static int roles_cache_is_role_entry(struct slapi_entry *entry)
+{
+ Slapi_Attr *pObjclasses = NULL;
+ Slapi_Value *val = NULL;
+ char *pObj = NULL;
+ int index = 0;
+
+ int nsroledefinition = 0;
+ int nsrolesimpleOrComplex = 0;
+ int nsroletype = 0;
+
+ if ( entry == NULL )
+ {
+ return(0);
+ }
+
+ if ( slapi_entry_attr_find(entry, "objectclass", &pObjclasses) )
+ {
+ slapi_log_error( SLAPI_LOG_FATAL,
+ ROLES_PLUGIN_SUBSYSTEM,
+ "roles_cache_is_role_entry: failed to get objectclass from %s\n",slapi_entry_get_dn_const(entry));
+ return(-1);
+ }
+
+ /* Check out the object classes to see if this was a nsroledefinition */
+
+ val = 0;
+ index = slapi_attr_first_value( pObjclasses, &val );
+ while(val)
+ {
+ const char *p;
+ int len = 0;
+
+ pObj = (char*)slapi_value_get_string(val);
+
+ for ( p = pObj, len = 0;
+ (*p != '\0') && (*p != ' ');
+ p++, len++ )
+ {
+ ; /* NULL */
+ }
+
+ if ( !strncasecmp(pObj, (char*)"nsroledefinition", len) )
+ {
+ nsroledefinition = 1;
+ }
+ if ( !strncasecmp(pObj, (char*)"nssimpleroledefinition", len) ||
+ !strncasecmp(pObj, (char*)"nscomplexroledefinition", len) )
+ {
+ nsrolesimpleOrComplex = 1;
+ }
+ if( !strncasecmp(pObj, (char*)"nsmanagedroledefinition", len) ||
+ !strncasecmp(pObj, (char*)"nsfilteredroledefinition", len) ||
+ !strncasecmp(pObj, (char*)"nsnestedroledefinition", len)
+ )
+ {
+ nsroletype = 1;
+ }
+ index = slapi_attr_next_value( pObjclasses, index, &val );
+ }
+ if ( (nsroledefinition == 0) ||
+ (nsrolesimpleOrComplex == 0) ||
+ (nsroletype == 0) )
+ {
+ return(0);
+ }
+ return(1);
+}
+
+/* roles_cache_change_notify
+ -------------------------
+ determines if the change effects the cache and if so
+ signals a rebuild
+ -- called when modify|modrdn|add|delete operation is performed --
+ -- called from a postoperation on an entry
+ XXX this implies that the client may have already received his LDAP response,
+ but that there will be a delay before he sees the effect in the roles cache.
+ Should we be doing this processing in a BE_POST_MODIFY postop
+ which is called _before_ the response goes to the client ?
+*/
+void roles_cache_change_notify(Slapi_PBlock *pb)
+{
+ char *dn = NULL;
+ struct slapi_entry *e = NULL;
+ struct slapi_entry *pre = NULL;
+ struct slapi_entry *entry = NULL;
+ Slapi_Backend *be = NULL;
+ Slapi_Operation *pb_operation = NULL;
+ int operation;
+ int do_update = 0;
+ int rc = -1;
+
+ slapi_log_error(SLAPI_LOG_PLUGIN,
+ ROLES_PLUGIN_SUBSYSTEM,
+ "--> roles_cache_change_notify\n");
+
+ /* if the current operation has failed, don't even try the post operation */
+ slapi_pblock_get( pb, SLAPI_PLUGIN_OPRETURN, &rc );
+ if ( rc != LDAP_SUCCESS )
+ {
+ return;
+ }
+
+ /* Don't update local cache when remote entries are updated */
+ slapi_pblock_get( pb, SLAPI_BACKEND, &be );
+ if ( ( be!=NULL ) &&
+ ( slapi_be_is_flag_set(be,SLAPI_BE_FLAG_REMOTE_DATA)) )
+ {
+ return;
+ }
+
+ slapi_pblock_get(pb, SLAPI_TARGET_DN, &dn);
+ if( dn == NULL )
+ {
+ return;
+ }
+
+ slapi_pblock_get (pb, SLAPI_OPERATION, &pb_operation);
+ operation = operation_get_type(pb_operation);
+
+ switch (operation)
+ {
+ case SLAPI_OPERATION_DELETE:
+
+ slapi_pblock_get(pb, SLAPI_ENTRY_PRE_OP, &e);
+ if( e == NULL )
+ {
+ return;
+ }
+ break;
+
+ case SLAPI_OPERATION_ADD:
+ slapi_pblock_get(pb, SLAPI_ENTRY_POST_OP, &e);
+ if ( e == NULL )
+ {
+ return;
+ }
+ break;
+
+ case SLAPI_OPERATION_MODIFY:
+ case SLAPI_OPERATION_MODRDN:
+ /* those operations are treated the same way and modify is a deletion followed by an addition.
+ the only point to take care is that dn is the olddn */
+ operation = SLAPI_OPERATION_MODIFY;
+ slapi_pblock_get(pb, SLAPI_ENTRY_PRE_OP, &pre);
+ if( pre == NULL )
+ {
+ return;
+ }
+ slapi_pblock_get(pb, SLAPI_ENTRY_POST_OP, &e);
+ if ( e == NULL )
+ {
+ return;
+ }
+ break;
+ default:
+ slapi_log_error( SLAPI_LOG_FATAL,
+ ROLES_PLUGIN_SUBSYSTEM,
+ "roles_cache_change_notify: unknown operation %d\n",operation);
+ return;
+ }
+
+ if ( operation != SLAPI_OPERATION_MODIFY )
+ {
+ if ( roles_cache_is_role_entry(e) != 1 )
+ {
+ slapi_log_error( SLAPI_LOG_PLUGIN, ROLES_PLUGIN_SUBSYSTEM, "<-- roles_cache_change_notify: not a role entry\n");
+ return;
+ }
+ entry = slapi_entry_dup(e);
+ do_update = 1;
+ }
+ else
+ {
+ int is_pre_role = roles_cache_is_role_entry(pre);
+ int is_post_role = roles_cache_is_role_entry(e);
+ if ( (is_pre_role==1) && (is_post_role==1) ) /* role definition has changed */
+ {
+ entry = slapi_entry_dup(e);
+ do_update = 1;
+ }
+ else if ( is_pre_role == 1 ) /* entry is no more a role */
+ {
+ operation = SLAPI_OPERATION_DELETE;
+ do_update = 1;
+ }
+ else if ( is_post_role == 1 ) /* entry is now a role */
+ {
+ operation = SLAPI_OPERATION_ADD;
+ entry = slapi_entry_dup(e);
+ do_update = 1;
+ }
+ else
+ {
+ slapi_log_error( SLAPI_LOG_PLUGIN, ROLES_PLUGIN_SUBSYSTEM, "<-- roles_cache_change_notify: not a role entry\n");
+ return;
+ }
+ }
+
+ if ( do_update )
+ {
+#ifdef moretrace
+if ( e != NULL )
+{
+ Slapi_Attr *attr = NULL;
+ int rc;
+
+ /* Get the list of nested roles */
+ rc = slapi_entry_attr_find(e,ROLE_NESTED_ATTR_NAME,&attr);
+
+ if ( (rc == 0) && attr)
+ {
+ /* Recurse to get the definition objects for them */
+ Slapi_Value **va = attr_get_present_values(attr);
+ int i = 0;
+ char *string = NULL;
+
+ for ( i = 0; va[i] != NULL; i++ )
+ {
+ string = (char*)slapi_value_get_string(va[i]);
+ slapi_log_error( SLAPI_LOG_PLUGIN, ROLES_PLUGIN_SUBSYSTEM, "roles_cache_change_notify:%s\n",string);
+ }
+ }
+}
+#endif
+ Slapi_DN *top_suffix = roles_cache_get_top_suffix(*(be->be_suffix));
+
+ if ( top_suffix != NULL )
+ {
+ roles_cache_trigger_update_role( slapi_ch_strdup(dn), entry,
+ top_suffix,
+ operation);
+
+ slapi_sdn_free(&top_suffix);
+ }
+
+ }
+
+ slapi_log_error( SLAPI_LOG_PLUGIN, ROLES_PLUGIN_SUBSYSTEM, "<-- roles_cache_change_notify\n");
+
+}
+
+/* roles_cache_get_top_suffix
+ -------------------------
+ The returned Slapi_DN must be freed with slapi_sdn_free().
+*/
+static Slapi_DN* roles_cache_get_top_suffix(Slapi_DN *suffix)
+{
+ Slapi_DN *current_suffix = NULL;
+ Slapi_DN parent_suffix;
+
+ slapi_log_error( SLAPI_LOG_PLUGIN, ROLES_PLUGIN_SUBSYSTEM, "--> roles_cache_get_top_suffix\n");
+
+ if ( suffix == NULL )
+ {
+ return(NULL);
+ }
+ current_suffix = slapi_sdn_new();
+ slapi_sdn_init(&parent_suffix);
+
+ /* we must get the top suffix for that DN */
+ slapi_sdn_copy(suffix,current_suffix);
+ while ( !slapi_sdn_isempty(current_suffix) )
+ {
+ if ( slapi_is_root_suffix(current_suffix) != 1 )
+ {
+ slapi_sdn_get_parent(current_suffix,&parent_suffix);
+ slapi_sdn_copy(&parent_suffix, current_suffix);
+ }
+ else
+ {
+ slapi_sdn_done(&parent_suffix);
+ return(current_suffix);
+ }
+ }
+ /* we should not return that way ... */
+ slapi_log_error( SLAPI_LOG_PLUGIN, ROLES_PLUGIN_SUBSYSTEM, "<-- roles_cache_get_top_suffix\n");
+ slapi_sdn_done(&parent_suffix);
+ slapi_sdn_free(&current_suffix);
+ return(NULL);
+}
+
+/* roles_cache_add_roles_from_suffix
+ -------------------------------
+ Get the roles entries under the suffix
+ return 0: OK
+ return -1: this suffix has no role defined
+ */
+static int roles_cache_add_roles_from_suffix(Slapi_DN *suffix_dn, roles_cache_def *suffix_def)
+{
+ /* Search subtree-level under this entry */
+ int rc = -1;
+ roles_cache_search_roles info;
+ Slapi_PBlock *int_search_pb = NULL;
+
+ slapi_log_error( SLAPI_LOG_PLUGIN, ROLES_PLUGIN_SUBSYSTEM, "--> roles_get_roles_from_entry\n");
+
+ info.suffix_def = suffix_def;
+ info.rc = LDAP_NO_SUCH_OBJECT;
+
+ /* Get the roles definitions of the given suffix_dn */
+ int_search_pb = slapi_pblock_new ();
+ slapi_search_internal_set_pb(int_search_pb,
+ (char*)slapi_sdn_get_ndn(suffix_dn),
+ LDAP_SCOPE_SUBTREE,
+ ROLE_DEFINITION_FILTER,
+ allUserAttributes,
+ 0 /* attrsonly */,
+ NULL /* controls */,
+ NULL /* uniqueid */,
+ roles_get_plugin_identity(),
+ SLAPI_OP_FLAG_NEVER_CHAIN /* actions : get local roles only */ );
+
+ slapi_search_internal_callback_pb(int_search_pb,
+ &info /* callback_data */,
+ roles_cache_result_cb,
+ roles_cache_add_entry_cb,
+ NULL /* referral_callback */);
+
+ slapi_pblock_destroy (int_search_pb);
+ int_search_pb = NULL;
+
+ if ( info.rc == LDAP_SUCCESS )
+ {
+ rc = 0;
+ }
+
+ slapi_log_error( SLAPI_LOG_PLUGIN, ROLES_PLUGIN_SUBSYSTEM, "<-- roles_get_roles_from_entry\n");
+
+ return(rc);
+}
+
+/* roles_cache_add_entry_cb
+ -----------------------
+*/
+static int roles_cache_add_entry_cb(Slapi_Entry* e, void *callback_data)
+{
+ roles_cache_search_roles *info=(roles_cache_search_roles *)callback_data;
+
+ roles_cache_def *suffix = info->suffix_def;
+
+ roles_cache_create_role_under(&suffix, e);
+ return(0);
+}
+
+/* roles_cache_result_cb
+ -----------------------
+*/
+static void roles_cache_result_cb( int rc, void *callback_data) {
+ roles_cache_search_roles *info=(roles_cache_search_roles *)callback_data;
+
+ info->rc = rc;
+}
+
+
+/* roles_cache_create_role_under
+ ----------------------------
+ Create the avl tree of roles definitions defined in the scope
+ of the suffix
+ Return 0: OK
+ Return -1: fail
+*/
+static int roles_cache_create_role_under(roles_cache_def** roles_cache_suffix, Slapi_Entry *entry)
+{
+ int rc;
+ role_object *new_role = NULL;
+
+ slapi_log_error(SLAPI_LOG_PLUGIN,
+ ROLES_PLUGIN_SUBSYSTEM, "--> roles_cache_create_role_under: %s - %x\n",
+ slapi_sdn_get_dn((*roles_cache_suffix)->suffix_dn),
+ (*roles_cache_suffix)->avl_tree);
+
+ rc = roles_cache_create_object_from_entry(entry,&new_role,0);
+ slapi_log_error(SLAPI_LOG_PLUGIN,
+ ROLES_PLUGIN_SUBSYSTEM,
+ "roles_cache_create_role_under: create node for entry %s - rc: %d SUFFIX: %x\n",
+ slapi_entry_get_dn_const(entry), rc, (*roles_cache_suffix)->avl_tree);
+
+ if ( (rc == 0) && new_role)
+ {
+ /* Add to the tree where avl_data is a role_object struct */
+ rc = roles_cache_insert_object(&((*roles_cache_suffix)->avl_tree),new_role);
+ slapi_log_error(SLAPI_LOG_PLUGIN,
+ ROLES_PLUGIN_SUBSYSTEM, "roles_cache_create_role_under:%s in tree %x rc: %d\n",
+ (char*)slapi_sdn_get_ndn(new_role->dn),
+ (*roles_cache_suffix)->avl_tree, rc);
+ }
+ return(rc);
+}
+
+
+/* roles_cache_create_object_from_entry
+ ------------------------------------
+ Create a node role_object from the information contained in role_entry
+ Return 0
+ Return ENOMEM: fail
+ Return SLAPI_ROLE_DEFINITION_ERROR: fail
+ Return SLAPI_ROLE_ERROR_NO_FILTER_SPECIFIED: fail
+ Return SLAPI_ROLE_ERROR_FILTER_BAD: fail
+*/
+static int roles_cache_create_object_from_entry(Slapi_Entry *role_entry, role_object **result, int hint)
+{
+ int rc = 0;
+ int type = 0;
+ role_object *this_role = NULL;
+
+ slapi_log_error(SLAPI_LOG_PLUGIN, ROLES_PLUGIN_SUBSYSTEM,
+ "--> roles_cache_create_object_from_entry\n");
+
+ *result = NULL;
+
+ /* Do not allow circular dependencies */
+ if ( hint > MAX_NESTED_ROLES )
+ {
+ char *ndn = NULL;
+
+ ndn = slapi_entry_get_ndn( role_entry );
+ slapi_log_error(
+ SLAPI_LOG_FATAL,
+ ROLES_PLUGIN_SUBSYSTEM,
+ "Maximum roles nesting exceeded (%d), not retrieving roles from entry %s--probable circular definition\n",
+ MAX_NESTED_ROLES,
+ ndn);
+
+ return (0);
+ }
+
+ /* Create the role cache definition */
+ this_role = (role_object*)slapi_ch_calloc(1, sizeof(role_object));
+ if (this_role == NULL )
+ {
+ return ENOMEM;
+ }
+
+ /* Check the entry is OK */
+ /* Determine role type and assign to structure */
+ /* We determine the role type by reading the objectclass */
+ if ( roles_cache_is_role_entry(role_entry) == 0 )
+ {
+ /* Bad type */
+ slapi_ch_free((void**)&this_role);
+ return SLAPI_ROLE_DEFINITION_ERROR;
+ }
+
+ type = roles_cache_determine_class(role_entry);
+
+ if (type != 0)
+ {
+ this_role->type = type;
+ }
+ else
+ {
+ /* Bad type */
+ slapi_ch_free((void**)&this_role);
+ return SLAPI_ROLE_DEFINITION_ERROR;
+ }
+
+ this_role->dn = slapi_sdn_new();
+ slapi_sdn_copy(slapi_entry_get_sdn(role_entry),this_role->dn);
+
+ /* Depending upon role type, pull out the remaining information we need */
+ switch (this_role->type)
+ {
+ case ROLE_TYPE_MANAGED:
+
+ /* Nothing further needed */
+ break;
+
+ case ROLE_TYPE_FILTERED:
+ {
+
+ Slapi_Filter *filter = NULL;
+ char *filter_attr_value = NULL;
+
+ /* Get the filter and retrieve the filter attribute */
+ filter_attr_value = slapi_entry_attr_get_charptr(role_entry,ROLE_FILTER_ATTR_NAME);
+ if ( filter_attr_value == NULL )
+ {
+ /* Means probably no attribute or no value there */
+ slapi_ch_free((void**)&this_role);
+ return SLAPI_ROLE_ERROR_NO_FILTER_SPECIFIED;
+ }
+
+ /* Turn it into a slapi filter object */
+ filter = slapi_str2filter(filter_attr_value);
+ slapi_ch_free((void**)&filter_attr_value);
+
+ if ( filter == NULL )
+ {
+ /* An error has occured */
+ slapi_ch_free((void**)&this_role);
+ return SLAPI_ROLE_ERROR_FILTER_BAD;
+ }
+ /* Store on the object */
+ this_role->filter = filter;
+
+ break;
+ }
+
+ case ROLE_TYPE_NESTED:
+ {
+ Slapi_Attr *attr = NULL;
+
+ /* Get the list of nested roles */
+ rc = slapi_entry_attr_find(role_entry,ROLE_NESTED_ATTR_NAME,&attr);
+
+ if ( (rc == 0) && attr)
+ {
+ /* Recurse to get the definition objects for them */
+ Slapi_Value **va = attr_get_present_values(attr);
+ int i = 0;
+ char *string = NULL;
+ Slapi_DN nested_role_dn;
+ role_object_nested *nested_role_object = NULL;
+
+ for ( i = 0; va[i] != NULL; i++ )
+ {
+ string = (char*)slapi_value_get_string(va[i]);
+
+ /* Make a DN from the string */
+ slapi_sdn_init_dn_byref(&nested_role_dn,string);
+
+ slapi_log_error(SLAPI_LOG_PLUGIN,
+ ROLES_PLUGIN_SUBSYSTEM, "roles_cache_create_object_from_entry: dn %s, nested %s\n",
+ (char*)slapi_sdn_get_ndn(this_role->dn),string);
+
+ /* Make a role object nested from the DN */
+ rc = roles_cache_object_nested_from_dn(&nested_role_dn,&nested_role_object);
+
+ /* Insert it into the nested list */
+ if ( (rc == 0) && nested_role_object)
+ {
+ /* Add to the tree where avl_data is a role_object_nested struct */
+ rc = roles_cache_insert_object_nested(&(this_role->avl_tree),nested_role_object);
+ }
+ slapi_sdn_done(&nested_role_dn);
+ }
+ }
+
+ break;
+ }
+
+ default:
+ slapi_log_error(SLAPI_LOG_FATAL,
+ ROLES_PLUGIN_SUBSYSTEM, "wrong role type\n");
+ }
+
+ if ( rc == 0 )
+ {
+ *result = this_role;
+ }
+
+ slapi_log_error(SLAPI_LOG_PLUGIN, ROLES_PLUGIN_SUBSYSTEM,
+ "<-- roles_cache_create_object_from_entry\n");
+
+
+ return rc;
+}
+
+/* roles_cache_determine_class:
+ ----------------------------
+ Determine the type of role depending on the objectclass
+ Return the type of the role
+ */
+static int roles_cache_determine_class(Slapi_Entry *role_entry)
+{
+ /* Examine the entry's objectclass attribute */
+ int found_managed = 0;
+ int found_filtered = 0;
+ int found_nested = 0;
+ Slapi_Attr *attr= NULL;
+ struct berval bv = {0};
+ int rc = 0;
+ int type = 0;
+
+ slapi_log_error(SLAPI_LOG_PLUGIN, ROLES_PLUGIN_SUBSYSTEM,
+ "--> roles_cache_determine_class\n");
+
+ rc = slapi_entry_attr_find(role_entry,"objectclass",&attr);
+ if ( rc != 0 )
+ {
+ /* No objectclass, definitely an error */
+ return 0;
+ }
+
+ berval_set_string(&bv,ROLE_OBJECTCLASS_MANAGED);
+ rc = slapi_attr_value_find(attr,&bv);
+ if ( rc == 0 )
+ {
+ found_managed = 1;
+ type = ROLE_TYPE_MANAGED;
+ }
+
+ berval_set_string(&bv,ROLE_OBJECTCLASS_FILTERED);
+ rc = slapi_attr_value_find(attr,&bv);
+ if ( rc == 0 )
+ {
+ found_filtered = 1;
+ type = ROLE_TYPE_FILTERED;
+ }
+
+ berval_set_string(&bv,ROLE_OBJECTCLASS_NESTED);
+ rc = slapi_attr_value_find(attr,&bv);
+ if ( rc == 0 )
+ {
+ found_nested = 1;
+ type = ROLE_TYPE_NESTED;
+ }
+
+ if ( (found_managed + found_nested + found_filtered) > 1 )
+ {
+ /* Means some goofball configured a role definition which is trying to be more than one different type. error. */
+ return 0;
+ }
+
+ if ( (found_managed + found_nested + found_filtered) == 0)
+ {
+ /* Means this entry isn't any of the role types we handle. error. */
+ return 0;
+ }
+
+ slapi_log_error(SLAPI_LOG_PLUGIN, ROLES_PLUGIN_SUBSYSTEM,
+ "<-- roles_cache_determine_class\n");
+
+ /* Return the appropriate type ordinal */
+ return type;
+}
+
+/* roles_cache_node_cmp:
+ ---------------------
+ Comparison function to add a new node in the avl tree (avl_data is of type role_object)
+ */
+static int roles_cache_node_cmp( caddr_t d1, caddr_t d2 )
+{
+ role_object *role_to_insert = (role_object*)d1;
+ role_object *current_role = (role_object*)d2;
+
+ /* role_to_insert and current_role are never NULL in that context */
+
+ slapi_log_error(SLAPI_LOG_PLUGIN, ROLES_PLUGIN_SUBSYSTEM,
+ "roles_cache_node_cmp\n");
+
+ return (slapi_sdn_compare((Slapi_DN *)role_to_insert->dn, (Slapi_DN *)current_role->dn));
+}
+
+/* roles_cache_insert_object:
+ --------------------------
+ Insert a new node in the avl tree of a specific suffix
+ */
+static int roles_cache_insert_object(Avlnode **tree, role_object *object)
+{
+
+ slapi_log_error(SLAPI_LOG_PLUGIN,
+ ROLES_PLUGIN_SUBSYSTEM, "roles_cache_insert_object: %s in tree %x\n",
+ (char*)slapi_sdn_get_ndn(object->dn),
+ *tree);
+ return (avl_insert(tree, (caddr_t)object, roles_cache_node_cmp, avl_dup_error));
+}
+
+/* roles_cache_node_nested_cmp:
+ ----------------------------
+ Comparison function to add a new node in the avl tree
+ */
+static int roles_cache_node_nested_cmp( caddr_t d1, caddr_t d2 )
+{
+ role_object_nested *role_to_insert = (role_object_nested*)d1;
+ role_object_nested *current_role = (role_object_nested*)d2;
+
+ /* role_to_insert and current_role are never NULL in that context */
+
+ slapi_log_error(SLAPI_LOG_PLUGIN, ROLES_PLUGIN_SUBSYSTEM,
+ "roles_cache_node_nested_cmp\n");
+
+ return slapi_sdn_compare(role_to_insert->dn, current_role->dn);
+}
+
+/* roles_cache_insert_object_nested:
+ ---------------------------------
+ Insert a new node in the avl tree of a specific suffix
+ */
+static int roles_cache_insert_object_nested(Avlnode **tree, role_object_nested *object)
+{
+ slapi_log_error(SLAPI_LOG_PLUGIN,
+ ROLES_PLUGIN_SUBSYSTEM, "roles_cache_insert_object_nested: %s in tree %x: \n",
+ (char*)slapi_sdn_get_ndn(object->dn), *tree);
+
+ return (avl_insert(tree, (caddr_t)object, roles_cache_node_nested_cmp, avl_dup_error));
+}
+
+/* roles_cache_object_nested_from_dn
+ ----------------------------------
+ Get the role associated to an entry DN
+ Return 0: OK
+ Return ENOMEM: fail
+ */
+static int roles_cache_object_nested_from_dn(Slapi_DN *role_dn, role_object_nested **result)
+{
+ role_object_nested *nested_role = NULL;
+
+ slapi_log_error(SLAPI_LOG_PLUGIN, ROLES_PLUGIN_SUBSYSTEM,
+ "--> roles_cache_object_nested_from_dn\n");
+
+ *result = NULL;
+
+ /* Create the role cache definition */
+ nested_role = (role_object_nested*)slapi_ch_calloc(1, sizeof(role_object_nested));
+ if (nested_role == NULL )
+ {
+ return ENOMEM;
+ }
+
+ nested_role->dn = slapi_sdn_new();
+ slapi_sdn_copy(role_dn,nested_role->dn);
+ *result = nested_role;
+
+ slapi_log_error(SLAPI_LOG_PLUGIN, ROLES_PLUGIN_SUBSYSTEM,
+ "<-- roles_cache_object_nested_from_dn\n");
+ return 0;
+}
+
+/* roles_cache_listroles
+ --------------------
+ Lists all the roles an entry posesses
+ return_values = 0 means that we don't need the nsrole values
+ return_values = 1 means that we need the nsrole values
+ Return 0: the entry has nsrole
+ Return -1: the entry has no nsrole
+ */
+int roles_cache_listroles(Slapi_Entry *entry, int return_values, Slapi_ValueSet **valueset_out)
+{
+ roles_cache_def *roles_cache = NULL;
+ role_object *this_role = NULL;
+ int rc = 0;
+ Avlnode * tree = NULL;
+ roles_cache_build_result arg;
+ Slapi_Backend *backend = NULL;
+
+ slapi_log_error(SLAPI_LOG_PLUGIN,
+ ROLES_PLUGIN_SUBSYSTEM, "--> roles_cache_listroles\n");
+
+ backend = slapi_mapping_tree_find_backend_for_sdn(slapi_entry_get_sdn(entry));
+ if ( (backend != NULL) && slapi_be_is_flag_set(backend,SLAPI_BE_FLAG_REMOTE_DATA) )
+ {
+ /* the entry is not local, so don't return anything */
+ return (-1);
+ }
+
+ if ( return_values )
+ {
+ *valueset_out = (Slapi_ValueSet*)slapi_ch_calloc(1,sizeof(Slapi_ValueSet));
+ slapi_valueset_init(*valueset_out);
+ }
+
+ /* First get a list of all the in-scope roles */
+ /* XXX really need a mutex for this read operation ? */
+ PR_RWLock_Rlock(global_lock);
+
+ rc = roles_cache_find_roles_in_suffix( slapi_entry_get_sdn(entry),&roles_cache);
+
+ PR_RWLock_Unlock(global_lock);
+
+ /* Traverse the tree checking if the entry has any of the roles */
+ if ( roles_cache != NULL )
+ {
+ if ( roles_cache->avl_tree )
+ {
+ arg.nsrole_values = valueset_out;
+ arg.need_value = return_values;
+ arg.requested_entry = entry;
+ arg.has_value = 0;
+
+ /* XXX really need a mutex for this read operation ? */
+ slapi_lock_mutex(roles_cache->cache_lock);
+
+ avl_apply(roles_cache->avl_tree, (IFP)roles_cache_build_nsrole, &arg, -1, AVL_INORDER);
+
+ slapi_unlock_mutex(roles_cache->cache_lock);
+
+ if( !arg.has_value )
+ {
+ if ( return_values )
+ {
+ slapi_valueset_free(*valueset_out);
+ *valueset_out = NULL;
+ }
+ rc = -1;
+ }
+ /* Free the list (we already did that) */
+ }
+ else
+ {
+ if ( return_values )
+ {
+ slapi_valueset_free(*valueset_out);
+ *valueset_out = NULL;
+ }
+ rc = -1;
+ }
+ }
+ else
+ {
+ /* no roles associated */
+ rc = -1;
+ }
+ slapi_log_error(SLAPI_LOG_PLUGIN,
+ ROLES_PLUGIN_SUBSYSTEM, "<-- roles_cache_listroles\n");
+ return rc;
+}
+
+/* roles_cache_build_nsrole
+ ------------------------
+ Traverse the tree containing roles definitions for a suffix and for each
+ one of them, check wether the entry is a member of it or not
+ For ones which check out positive, we add their DN to the value
+ always return 0 to allow to trverse all the tree
+ */
+static int roles_cache_build_nsrole( caddr_t data, caddr_t arg )
+{
+ Slapi_Value *value = NULL;
+ roles_cache_build_result *result = (roles_cache_build_result*)arg;
+ role_object *this_role = (role_object*)data;
+ roles_cache_search_in_nested get_nsrole;
+ /* Return a value different from the stop flag to be able
+ to go through all the tree */
+ int rc = 0;
+
+ slapi_log_error(SLAPI_LOG_PLUGIN,
+ ROLES_PLUGIN_SUBSYSTEM, "--> roles_cache_build_nsrole: role %s\n",
+ (char*) slapi_sdn_get_ndn(this_role->dn));
+
+ value = slapi_value_new_string("");
+
+ get_nsrole.is_entry_member_of = result->requested_entry;
+ get_nsrole.present = 0;
+ get_nsrole.hint = 0;
+
+ roles_is_entry_member_of_object((caddr_t)this_role, (caddr_t)&get_nsrole);
+
+ /* If so, add its DN to the attribute */
+ if (get_nsrole.present)
+ {
+ result->has_value = 1;
+ if ( result->need_value )
+ {
+ slapi_value_set_string(value,(char*) slapi_sdn_get_ndn(this_role->dn));
+ slapi_valueset_add_value(*(result->nsrole_values),value);
+ }
+ else
+ {
+ /* we don't need the value but we already know there is one nsrole.
+ stop the traversal
+ */
+ rc = -1;
+ }
+ }
+
+ slapi_value_free(&value);
+
+ slapi_log_error(SLAPI_LOG_PLUGIN,
+ ROLES_PLUGIN_SUBSYSTEM, "<-- roles_cache_build_nsrole\n");
+
+ return rc;
+}
+
+
+/* roles_check
+ -----------
+ Checks if an entry has a presented role, assuming that we've already verified
+that
+ the role both exists and is in scope
+ return 0: no processing error
+ return -1: error
+ */
+int roles_check(Slapi_Entry *entry_to_check, Slapi_DN *role_dn, int *present)
+{
+ roles_cache_def *roles_cache = NULL;
+ role_object *this_role = NULL;
+ roles_cache_search_in_nested get_nsrole;
+
+ int rc = 0;
+
+ slapi_log_error(SLAPI_LOG_PLUGIN,
+ ROLES_PLUGIN_SUBSYSTEM, "--> roles_check\n");
+
+ *present = 0;
+
+ PR_RWLock_Rlock(global_lock);
+
+ if ( roles_cache_find_roles_in_suffix(slapi_entry_get_sdn(entry_to_check),
+ &roles_cache) != 0 )
+ {
+ PR_RWLock_Unlock(global_lock);
+ return -1;
+ }
+ PR_RWLock_Unlock(global_lock);
+
+ this_role = (role_object *)avl_find(roles_cache->avl_tree, role_dn, (IFP)roles_cache_find_node);
+
+ /* MAB: For some reason the assumption made by this function (the role exists and is in scope)
+ * does not seem to be true... this_role might be NULL after the avl_find call (is the avl_tree
+ * broken? Anyway, this is crashing the 5.1 server on 29-Aug-01, so I am applying the following patch
+ * to avoid the crash inside roles_is_entry_member_of_object */
+ /* Begin patch */
+ if (!this_role) {
+ /* Assume that the entry is not member of the role (*present=0) and leave... */
+ return rc;
+ }
+ /* End patch */
+
+ get_nsrole.is_entry_member_of = entry_to_check;
+ get_nsrole.present = 0;
+ get_nsrole.hint = 0;
+
+ roles_is_entry_member_of_object((caddr_t)this_role, (caddr_t)&get_nsrole);
+ *present = get_nsrole.present;
+
+ slapi_log_error(SLAPI_LOG_PLUGIN,
+ ROLES_PLUGIN_SUBSYSTEM, "<-- roles_check\n");
+
+ return rc;
+}
+
+/* roles_cache_find_node:
+ ---------------------
+ Comparison function to add a new node in the avl tree
+ */
+static int roles_cache_find_node( caddr_t d1, caddr_t d2 )
+{
+ Slapi_DN *data = (Slapi_DN *)d1;
+ role_object *role= (role_object *)d2;
+
+ /* role is not NULL in that context */
+
+ slapi_log_error(SLAPI_LOG_PLUGIN,
+ ROLES_PLUGIN_SUBSYSTEM, "roles_cache_find_node: %s %s\n",
+ slapi_sdn_get_dn(data), slapi_sdn_get_dn(role->dn));
+
+ return (slapi_sdn_compare(data, (Slapi_DN *)role->dn));
+}
+
+/* roles_cache_find_roles_in_suffix
+ -------------------------------
+ Find all the roles in scope to an entry
+ */
+static int roles_cache_find_roles_in_suffix(Slapi_DN *target_entry_dn, roles_cache_def **list_of_roles)
+{
+ int rc = -1;
+ Slapi_Backend *backend = NULL;
+
+ slapi_log_error(SLAPI_LOG_PLUGIN,
+ ROLES_PLUGIN_SUBSYSTEM, "--> roles_cache_find_roles_in_suffix\n");
+
+ *list_of_roles = NULL;
+ backend = slapi_mapping_tree_find_backend_for_sdn(target_entry_dn);
+ if ( (backend != NULL) && !slapi_be_is_flag_set(backend,SLAPI_BE_FLAG_REMOTE_DATA) )
+ {
+ Slapi_DN *suffix = roles_cache_get_top_suffix(*(backend->be_suffix));
+ roles_cache_def *current_role = roles_list;
+
+ /* Go through all the roles list and trigger the associated structure */
+ while ( (current_role != NULL) && (suffix != NULL) )
+ {
+ if ( slapi_sdn_compare(current_role->suffix_dn, suffix) == 0 )
+ {
+ *list_of_roles = current_role;
+ /* OK, we have found one */
+ slapi_sdn_free(&suffix);
+ return 0;
+ }
+ else
+ {
+ current_role = current_role->next;
+ }
+ }
+ if ( suffix != NULL )
+ {
+ slapi_sdn_free(&suffix);
+ }
+ /* If we got out that way, means that we didn't have find
+ roles definitions for that suffix */
+ return rc;
+ }
+
+ slapi_log_error(SLAPI_LOG_PLUGIN,
+ ROLES_PLUGIN_SUBSYSTEM, "<-- roles_cache_find_roles_in_suffix\n");
+ return rc;
+}
+
+/* roles_is_entry_member_of_object
+ --------------------------------
+ Check if the entry is part of a role defined in its suffix
+ return 0: ok
+ return 1: fail
+ -> to check the presence, see present
+ */
+static int roles_is_entry_member_of_object(caddr_t data, caddr_t argument )
+{
+ int rc = -1;
+
+ roles_cache_search_in_nested *get_nsrole = (roles_cache_search_in_nested*)argument;
+ role_object *this_role = (role_object*)data;
+
+ Slapi_Entry *entry_to_check = get_nsrole->is_entry_member_of;
+
+ slapi_log_error(SLAPI_LOG_PLUGIN,
+ ROLES_PLUGIN_SUBSYSTEM, "--> roles_is_entry_member_of_object\n");
+
+ if (!roles_is_inscope(entry_to_check, this_role->dn))
+ {
+ slapi_log_error(SLAPI_LOG_PLUGIN,
+ ROLES_PLUGIN_SUBSYSTEM, "roles_is_entry_member_of_object-> entry not in scope of role\n");
+ return rc;
+ }
+
+ if ( this_role != NULL )
+ {
+ /* Determine the role type */
+ switch (this_role->type)
+ {
+ case ROLE_TYPE_MANAGED:
+ rc = roles_check_managed(entry_to_check,this_role,&get_nsrole->present);
+ break;
+ case ROLE_TYPE_FILTERED:
+ rc = roles_check_filtered(entry_to_check,this_role,&get_nsrole->present);
+ break;
+ case ROLE_TYPE_NESTED:
+ {
+ /* Go through the tree of the nested DNs */
+ get_nsrole->hint++;
+ avl_apply(this_role->avl_tree, (IFP)roles_check_nested, get_nsrole, 0, AVL_INORDER);
+ get_nsrole->hint--;
+
+ /* kexcoff?? */
+ rc = get_nsrole->present;
+ break;
+ }
+ default:
+ slapi_log_error(SLAPI_LOG_FATAL,
+ ROLES_PLUGIN_SUBSYSTEM, "roles_is_entry_member_of_object-> invalid role type\n");
+ }
+ }
+
+ slapi_log_error(SLAPI_LOG_PLUGIN,
+ ROLES_PLUGIN_SUBSYSTEM, "<-- roles_is_entry_member_of_object\n");
+ return rc;
+}
+
+/* roles_check_managed
+ -------------------------
+ Check a managed role: we just need to check the content of the entry's nsRoleDN attribute
+ against the role DN
+ return 0: ok
+ return 1: fail
+ -> to check the presence, see present
+ */
+static int roles_check_managed(Slapi_Entry *entry_to_check, role_object *role, int *present)
+{
+ int rc = 0;
+ Slapi_Attr *attr = NULL;
+
+ slapi_log_error(SLAPI_LOG_PLUGIN,
+ ROLES_PLUGIN_SUBSYSTEM, "--> roles_check_managed\n");
+ /* Get the attribute */
+ rc = slapi_entry_attr_find(entry_to_check,ROLE_MANAGED_ATTR_NAME,&attr);
+
+ if ( rc == 0)
+ {
+ struct berval bv = {0};
+ char *dn_string = NULL;
+
+ /* Check content against the presented DN */
+ /* We assume that this function handles normalization and so on */
+ dn_string = (char*) slapi_sdn_get_ndn(role->dn);
+ berval_set_string(&bv,dn_string);
+ rc = slapi_attr_value_find(attr,&bv);
+ if ( rc == 0 )
+ {
+ *present = 1;
+ }
+ }
+ slapi_log_error(SLAPI_LOG_PLUGIN,
+ ROLES_PLUGIN_SUBSYSTEM,
+ "<-- roles_check_managed: entry %s role %s present %d\n",
+ slapi_entry_get_dn_const(entry_to_check),(char*)slapi_sdn_get_ndn(role->dn),*present);
+ return rc;
+}
+
+/* roles_check_filtered
+ --------------------------
+ Check a filtered role: call slapi_filter_test here on the entry
+ and the filter from the role object
+ return 0: ok
+ return 1: fail
+ -> to check the presence, see present
+ */
+static int roles_check_filtered(Slapi_Entry *entry_to_check, role_object *role, int *present)
+{
+ int rc = 0;
+
+ slapi_log_error(SLAPI_LOG_PLUGIN,
+ ROLES_PLUGIN_SUBSYSTEM, "--> roles_check_filtered\n");
+ rc = slapi_filter_test_simple(entry_to_check,role->filter);
+ if ( rc == 0 )
+ {
+ *present = 1;
+ }
+ slapi_log_error(SLAPI_LOG_PLUGIN,
+ ROLES_PLUGIN_SUBSYSTEM,
+ "<-- roles_check_filtered: entry %s role %s present %d\n",
+ slapi_entry_get_dn_const(entry_to_check),(char*)slapi_sdn_get_ndn(role->dn),*present);
+ return rc;
+}
+
+
+/* roles_check_nested
+ ------------------------
+ Check a nested role
+ return 0: ok
+ return -1: fail
+ -> to check the presence, see present
+ */
+static int roles_check_nested(caddr_t data, caddr_t arg)
+{
+ roles_cache_search_in_nested *get_nsrole = (roles_cache_search_in_nested*)arg;
+ int rc = -1;
+ role_object_nested *current_nested_role = (role_object_nested*)data;
+
+
+ /* do not allow circular dependencies, the cheap and easy way */
+ if( get_nsrole->hint > MAX_NESTED_ROLES)
+ {
+ char *ndn = NULL;
+
+ ndn = slapi_entry_get_ndn( get_nsrole->is_entry_member_of );
+ slapi_log_error(SLAPI_LOG_FATAL,
+ ROLES_PLUGIN_SUBSYSTEM,
+ "Maximum roles nesting exceeded (max %d current %d), not checking roles in entry %s--probable circular definition\n",
+ MAX_NESTED_ROLES,
+ get_nsrole->hint,
+ ndn);
+
+ /* Stop traversal value */
+ return 0;
+ }
+
+ /* Here we traverse the list of nested roles, calling the appropriate
+ evaluation function for those in turn */
+
+ if (current_nested_role)
+ {
+ roles_cache_def *roles_cache = NULL;
+ role_object *this_role = NULL;
+
+ slapi_log_error(SLAPI_LOG_PLUGIN,
+ ROLES_PLUGIN_SUBSYSTEM,
+ "-->roles_check_nested: entry %s role %s present %d\n",
+ slapi_entry_get_dn_const(get_nsrole->is_entry_member_of),
+ (char*)slapi_sdn_get_ndn(current_nested_role->dn),
+ get_nsrole->present);
+
+ if ( roles_cache_find_roles_in_suffix(current_nested_role->dn,
+ &roles_cache) != 0 )
+ {
+ return rc;
+ }
+
+ if ( slapi_is_loglevel_set(SLAPI_LOG_PLUGIN) )
+ {
+ avl_apply(roles_cache->avl_tree, (IFP)roles_cache_dump, &rc, -1, AVL_INORDER);
+ }
+
+ this_role = (role_object *)avl_find(roles_cache->avl_tree,
+ current_nested_role->dn,
+ (IFP)roles_cache_find_node);
+
+ if ( this_role == NULL )
+ {
+ /* the nested role doesn't exist */
+ slapi_log_error(SLAPI_LOG_FATAL,
+ ROLES_PLUGIN_SUBSYSTEM,
+ "The nested role %s doesn't exist\n",
+ (char*)slapi_sdn_get_ndn(current_nested_role->dn));
+ return rc;
+ }
+ /* get the role_object data associated to that dn */
+ if ( roles_is_inscope(get_nsrole->is_entry_member_of, this_role->dn) )
+ {
+ /* The list of nested roles is contained in the role definition */
+ roles_is_entry_member_of_object((caddr_t)this_role, (caddr_t)get_nsrole);
+ if ( get_nsrole->present == 1 )
+ {
+ return 0;
+ }
+ }
+ }
+ slapi_log_error(SLAPI_LOG_PLUGIN,
+ ROLES_PLUGIN_SUBSYSTEM, "<-- roles_check_nested\n");
+ return rc;
+}
+
+/* roles_is_inscope
+ ----------------------
+ Tells us if a presented role is in scope with respect to the presented entry
+ */
+static int roles_is_inscope(Slapi_Entry *entry_to_check, Slapi_DN *role_dn)
+{
+ int rc;
+
+ Slapi_DN role_parent;
+
+ slapi_log_error(SLAPI_LOG_PLUGIN,
+ ROLES_PLUGIN_SUBSYSTEM, "--> roles_is_inscope\n");
+
+ slapi_sdn_init(&role_parent);
+ slapi_sdn_get_parent(role_dn,&role_parent);
+
+ rc = slapi_sdn_scope_test(slapi_entry_get_sdn( entry_to_check ),
+ &role_parent,
+ LDAP_SCOPE_SUBTREE);
+ /* we need to check whether the entry would be returned by a view in scope */
+ if(!rc && views_api)
+ {
+ rc = views_entry_exists(views_api, (char*)slapi_sdn_get_ndn(&role_parent), entry_to_check);
+ }
+
+ slapi_sdn_done(&role_parent);
+
+
+ slapi_log_error(SLAPI_LOG_PLUGIN,
+ ROLES_PLUGIN_SUBSYSTEM, "<-- roles_is_inscope: entry %s role %s result %d\n",
+ slapi_entry_get_dn_const(entry_to_check),(char*)slapi_sdn_get_ndn(role_dn), rc);
+
+ return (rc);
+}
+
+static void berval_set_string(struct berval *bv, const char* string)
+{
+ bv->bv_len= strlen(string);
+ bv->bv_val= (void*)string; /* We cast away the const, but we're not going to change anything
+*/
+}
+
+/* roles_cache_role_def_delete
+ ----------------------------
+*/
+static void roles_cache_role_def_delete(roles_cache_def *role_def)
+{
+ roles_cache_def *current = roles_list;
+ roles_cache_def *previous = NULL;
+
+ slapi_log_error(SLAPI_LOG_PLUGIN,
+ ROLES_PLUGIN_SUBSYSTEM, "--> roles_cache_role_def_delete\n");
+
+ while ( current!= NULL )
+ {
+ if ( slapi_sdn_compare(current->suffix_dn, role_def->suffix_dn) == 0 )
+ {
+ if ( previous== NULL )
+ {
+ roles_list = current->next;
+ }
+ else
+ {
+ previous->next = current->next;
+ }
+ slapi_lock_mutex(role_def->change_lock);
+ role_def->keeprunning = 0;
+ slapi_notify_condvar(role_def->something_changed, 1 );
+ slapi_unlock_mutex(role_def->change_lock);
+ break;
+ }
+ else
+ {
+ previous = current;
+ current = current->next;
+ }
+ }
+ slapi_log_error(SLAPI_LOG_PLUGIN,
+ ROLES_PLUGIN_SUBSYSTEM, "<-- roles_cache_role_def_delete\n");
+}
+
+/* roles_cache_role_def_free
+ ----------------------------
+*/
+static void roles_cache_role_def_free(roles_cache_def *role_def)
+{
+ roles_cache_def *next_def = NULL;
+
+ slapi_log_error(SLAPI_LOG_PLUGIN,
+ ROLES_PLUGIN_SUBSYSTEM, "--> roles_cache_role_def_free\n");
+ if ( role_def == NULL )
+ {
+ return;
+ }
+
+ slapi_lock_mutex(role_def->stop_lock);
+
+ avl_free(role_def->avl_tree, (IFP)roles_cache_role_object_free);
+ slapi_sdn_free(&(role_def->suffix_dn));
+ slapi_destroy_mutex(role_def->cache_lock);
+ role_def->cache_lock = NULL;
+ slapi_destroy_mutex(role_def->change_lock);
+ role_def->change_lock = NULL;
+ slapi_destroy_condvar(role_def->something_changed);
+ slapi_destroy_mutex(role_def->create_lock);
+ role_def->create_lock = NULL;
+ slapi_destroy_condvar(role_def->suffix_created);
+
+ slapi_ch_free((void**)&role_def->notified_dn);
+ if ( role_def->notified_entry != NULL )
+ {
+ slapi_entry_free(role_def->notified_entry);
+ }
+
+ slapi_unlock_mutex(role_def->stop_lock);
+ slapi_destroy_mutex(role_def->stop_lock);
+ role_def->stop_lock = NULL;
+
+ slapi_ch_free((void**)&role_def);
+
+ slapi_log_error(SLAPI_LOG_PLUGIN,
+ ROLES_PLUGIN_SUBSYSTEM, "<-- roles_cache_role_def_free\n");
+}
+
+/* roles_cache_role_object_free
+ ----------------------------
+*/
+static void roles_cache_role_object_free(role_object *this_role)
+{
+ slapi_log_error(SLAPI_LOG_PLUGIN,
+ ROLES_PLUGIN_SUBSYSTEM, "--> roles_cache_role_object_free\n");
+
+ if ( this_role == NULL )
+ {
+ return;
+ }
+
+ switch (this_role->type)
+ {
+ case ROLE_TYPE_MANAGED:
+ /* Nothing further needed */
+ break;
+ case ROLE_TYPE_FILTERED:
+ /* Free the filter */
+ if (this_role->filter)
+ {
+ slapi_filter_free(this_role->filter,1);
+ this_role->filter = NULL;
+ }
+ break;
+ case ROLE_TYPE_NESTED:
+ /* Free the list of nested roles */
+ {
+ avl_free(this_role->avl_tree, roles_cache_role_object_nested_free);
+ }
+ break;
+ }
+
+ slapi_sdn_free(&this_role->dn);
+
+ /* Free the object */
+ slapi_ch_free((void**)&this_role);
+
+ slapi_log_error(SLAPI_LOG_PLUGIN,
+ ROLES_PLUGIN_SUBSYSTEM, "<-- roles_cache_role_object_free\n");
+}
+
+/* roles_cache_role_object_nested_free
+ ------------------------------------
+*/
+static void roles_cache_role_object_nested_free(role_object_nested *this_role)
+{
+ slapi_log_error(SLAPI_LOG_PLUGIN,
+ ROLES_PLUGIN_SUBSYSTEM, "--> roles_cache_role_object_nested_free\n");
+
+ if ( this_role == NULL )
+ {
+ return;
+ }
+
+ slapi_sdn_free(&this_role->dn);
+
+ /* Free the object */
+ slapi_ch_free((void**)&this_role);
+
+ slapi_log_error(SLAPI_LOG_PLUGIN,
+ ROLES_PLUGIN_SUBSYSTEM, "<-- roles_cache_role_object_nested_free\n");
+}
+
+static int roles_cache_dump( caddr_t data, caddr_t arg )
+{
+ role_object *this_role = (role_object*)data;
+
+ slapi_log_error(SLAPI_LOG_PLUGIN,
+ ROLES_PLUGIN_SUBSYSTEM, "roles_cache_dump: %x - %s - %x\n",
+ this_role, (char*)slapi_sdn_get_ndn(this_role->dn), this_role->avl_tree);
+
+ return 0;
+}
diff --git a/ldap/servers/plugins/roles/roles_cache.h b/ldap/servers/plugins/roles/roles_cache.h
new file mode 100644
index 00000000..fbe6c641
--- /dev/null
+++ b/ldap/servers/plugins/roles/roles_cache.h
@@ -0,0 +1,52 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+
+#if !defined( _ROLES_CACHE_H )
+
+#define SLAPD_ROLES_INTERFACE "roles-slapd"
+#define ROLES_PLUGIN_SUBSYSTEM "roles-plugin"
+#define NSROLEATTR "nsRole"
+
+#define ROLE_DEFINITION_FILTER "(&(objectclass=nsRoleDefinition)(objectclass=ldapsubentry))"
+#define OBJ_FILTER "(|(objectclass=*)(objectclass=ldapsubentry))"
+
+#define ROLE_TYPE_MANAGED 1
+#define ROLE_TYPE_FILTERED 2
+#define ROLE_TYPE_NESTED 3
+
+#define ROLE_OBJECTCLASS_MANAGED "nsManagedRoleDefinition"
+#define ROLE_OBJECTCLASS_FILTERED "nsFilteredRoleDefinition"
+#define ROLE_OBJECTCLASS_NESTED "nsNestedRoleDefinition"
+
+#define ROLE_FILTER_ATTR_NAME "nsRoleFilter"
+#define ROLE_MANAGED_ATTR_NAME "nsRoleDN"
+#define ROLE_NESTED_ATTR_NAME "nsRoleDN"
+
+#define SLAPI_ROLE_ERROR_NO_FILTER_SPECIFIED -1
+#define SLAPI_ROLE_ERROR_FILTER_BAD -2
+#define SLAPI_ROLE_DEFINITION_DOESNT_EXIST -3
+#define SLAPI_ROLE_DEFINITION_ERROR -4
+#define SLAPI_ROLE_DEFINITION_ALREADY_EXIST -5
+
+/* From roles_cache.c */
+int roles_cache_init();
+void roles_cache_stop();
+void roles_cache_change_notify(Slapi_PBlock *pb);
+int roles_cache_listroles(Slapi_Entry *entry, int return_value, Slapi_ValueSet **valueset_out);
+
+int roles_check(Slapi_Entry *entry_to_check, Slapi_DN *role_dn, int *present);
+
+/* From roles_plugin.c */
+int roles_init( Slapi_PBlock *pb );
+int roles_sp_get_value(vattr_sp_handle *handle, vattr_context *c, Slapi_Entry *e, char *type, Slapi_ValueSet** results,int *type_name_disposition, char** actual_type_name, int flags, int *free_flags, void *hint);
+
+int roles_sp_compare_value(vattr_sp_handle *handle, vattr_context *c, Slapi_Entry *e, char *type, Slapi_Value *test_this, int* result,int flags, void *hint);
+
+int roles_sp_list_types(vattr_sp_handle *handle,Slapi_Entry *e,vattr_type_list_context *type_context,int flags);
+
+void * roles_get_plugin_identity();
+
+#endif /* _ROLES_CACHE_H */
diff --git a/ldap/servers/plugins/roles/roles_plugin.c b/ldap/servers/plugins/roles/roles_plugin.c
new file mode 100644
index 00000000..d1ce5903
--- /dev/null
+++ b/ldap/servers/plugins/roles/roles_plugin.c
@@ -0,0 +1,254 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+
+/*
+ Code to implement server roles features
+*/
+
+#include "slap.h"
+
+#include "vattr_spi.h"
+
+#include "roles_cache.h"
+#include "statechange.h"
+
+
+#ifdef SOURCEFILE
+#undef SOURCEFILE
+#endif
+#define SOURCEFILE "roles_plugin.c"
+static char *sourcefile = SOURCEFILE;
+
+#define STATECHANGE_ROLES_ID "Roles"
+#define STATECHANGE_ROLES_CONFG_FILTER "objectclass=nsRoleDefinition"
+#define STATECHANGE_ROLES_ENTRY_FILTER "objectclass=*"
+
+#define ROLES_PLUGIN_SUBSYSTEM "roles-plugin" /* for logging */
+static void * roles_plugin_identity = NULL;
+
+static Slapi_PluginDesc pdesc = { "roles",
+ PLUGIN_MAGIC_VENDOR_STR, PRODUCTTEXT, "roles plugin" };
+
+#ifdef _WIN32
+int *module_ldap_debug = 0;
+
+void plugin_init_debug_level(int *level_ptr)
+{
+ module_ldap_debug = level_ptr;
+}
+#endif
+
+static int roles_start( Slapi_PBlock *pb );
+static int roles_post_op( Slapi_PBlock *pb );
+static int roles_close( Slapi_PBlock *pb );
+static void roles_set_plugin_identity(void * identity);
+
+/* roles_init
+ ----------
+ Initialization of the plugin
+ */
+int roles_init( Slapi_PBlock *pb )
+{
+ int rc = 0;
+ void *plugin_identity = NULL;
+
+ slapi_log_error( SLAPI_LOG_PLUGIN, ROLES_PLUGIN_SUBSYSTEM,
+ "=> roles_init\n" );
+
+ slapi_pblock_get (pb, SLAPI_PLUGIN_IDENTITY, &plugin_identity);
+ PR_ASSERT (plugin_identity);
+ roles_set_plugin_identity(plugin_identity);
+
+ if ( slapi_pblock_set( pb, SLAPI_PLUGIN_VERSION,
+ (void *)SLAPI_PLUGIN_VERSION_01 ) != 0 ||
+ slapi_pblock_set( pb, SLAPI_PLUGIN_DESCRIPTION,
+ (void *)&pdesc ) != 0 ||
+ slapi_pblock_set( pb, SLAPI_PLUGIN_START_FN,
+ (void *)roles_start ) != 0 ||
+ slapi_pblock_set(pb, SLAPI_PLUGIN_POST_MODIFY_FN,
+ (void *) roles_post_op ) != 0 ||
+ slapi_pblock_set(pb, SLAPI_PLUGIN_POST_MODRDN_FN,
+ (void *) roles_post_op ) != 0 ||
+ slapi_pblock_set(pb, SLAPI_PLUGIN_POST_ADD_FN,
+ (void *) roles_post_op ) != 0 ||
+ slapi_pblock_set(pb, SLAPI_PLUGIN_POST_DELETE_FN,
+ (void *) roles_post_op ) != 0 ||
+ slapi_pblock_set(pb, SLAPI_PLUGIN_CLOSE_FN,
+ (void *) roles_close ) != 0 )
+ {
+ slapi_log_error( SLAPI_LOG_FATAL, ROLES_PLUGIN_SUBSYSTEM,
+ "roles_init failed\n" );
+ rc = -1;
+ }
+
+ slapi_log_error( SLAPI_LOG_PLUGIN, ROLES_PLUGIN_SUBSYSTEM,
+ "<= roles_init %d\n", rc );
+ return rc;
+}
+
+/* roles_start
+ -----------
+ kexcoff: cache build at init or at startup ?
+ */
+static int roles_start( Slapi_PBlock *pb )
+{
+ int rc = 0;
+ void **statechange_api;
+
+ slapi_log_error( SLAPI_LOG_PLUGIN, ROLES_PLUGIN_SUBSYSTEM,
+ "=> roles_start\n" );
+
+ roles_cache_init();
+
+ /* from Pete Rowley for vcache
+ * PLUGIN DEPENDENCY ON STATECHANGE PLUGIN
+ *
+ * register objectclasses which indicate a
+ * role configuration entry, and therefore
+ * a globally significant change for the vcache
+ */
+
+ if(!slapi_apib_get_interface(StateChange_v1_0_GUID, &statechange_api))
+ {
+ statechange_register(statechange_api, STATECHANGE_ROLES_ID, NULL, STATECHANGE_ROLES_CONFG_FILTER, &vattr_global_invalidate, (notify_callback) statechange_vattr_cache_invalidator_callback(statechange_api));
+ }
+
+ slapi_log_error( SLAPI_LOG_PLUGIN, ROLES_PLUGIN_SUBSYSTEM,
+ "<= roles_start %d\n", rc );
+ return rc;
+}
+
+/* roles_close
+ -----------
+ kexcoff: ??
+ */
+static int roles_close( Slapi_PBlock *pb )
+{
+ int rc = 0;
+
+ slapi_log_error( SLAPI_LOG_PLUGIN, ROLES_PLUGIN_SUBSYSTEM,
+ "=> roles_close\n" );
+
+ roles_cache_stop();
+
+ slapi_log_error( SLAPI_LOG_PLUGIN, ROLES_PLUGIN_SUBSYSTEM,
+ "<= roles_close %d\n", rc );
+ return rc;
+}
+
+/* roles_sp_get_value
+ ------------------
+ Enumerate the values of the role attribute.
+ We do this by first locating all the roles which are in scope
+ Then we iterate over the in-scope roles calling Slapi_Role_Check().
+ For those which pass the check, we add their DN to the attribute's value set.
+*/
+int roles_sp_get_value(vattr_sp_handle *handle,
+ vattr_context *c,
+ Slapi_Entry *e,
+ char *type,
+ Slapi_ValueSet** results,
+ int *type_name_disposition,
+ char** actual_type_name,
+ int flags,
+ int *free_flags,
+ void *hint)
+{
+ int rc = -1;
+
+ rc = roles_cache_listroles(e, 1, results);
+ if (rc == 0)
+ {
+ *free_flags = SLAPI_VIRTUALATTRS_RETURNED_COPIES;
+ *actual_type_name = strdup(NSROLEATTR);
+
+ if (type_name_disposition)
+ {
+ *type_name_disposition = SLAPI_VIRTUALATTRS_TYPE_NAME_MATCHED_EXACTLY_OR_ALIAS;
+ }
+ }
+
+ /* Need to check the return code here because the caller
+ doesn't understand roles return codes */
+
+ return rc;
+}
+
+
+/* roles_sp_compare_value
+ ----------------------
+ Compare the value of the role attribute with a presented value.
+ Return true or false to the client.
+ */
+
+int roles_sp_compare_value(vattr_sp_handle *handle, vattr_context *c, Slapi_Entry *e, char *type, Slapi_Value *test_this, int* result,int flags, void *hint)
+{
+ int rc = 0;
+ Slapi_DN the_dn;
+
+ /* Extract the role's DN from the value passed in */
+ slapi_sdn_init_dn_byref(&the_dn,slapi_value_get_string(test_this));
+
+ return (roles_check(e,&the_dn,result));
+}
+
+int roles_sp_list_types(vattr_sp_handle *handle,Slapi_Entry *e,vattr_type_list_context *type_context,int flags)
+{
+ static char* test_type_name = NSROLEATTR;
+ int ret =0;
+
+ if ( 0 == ( flags & SLAPI_VIRTUALATTRS_LIST_OPERATIONAL_ATTRS )) {
+ /*
+ * Operational attributes were NOT requested. Since the only
+ * attribute type we service is nsRole which IS operational,
+ * there is nothing for us to do in this case.
+ */
+ return 0;
+ }
+
+ ret = roles_cache_listroles(e, 0, NULL);
+ if(ret == 0)
+ {
+ vattr_type_thang thang = {0};
+ thang.type_name = test_type_name;
+ thang.type_flags = SLAPI_ATTR_FLAG_OPATTR;
+ slapi_vattrspi_add_type(type_context,&thang,SLAPI_VIRTUALATTRS_REQUEST_POINTERS);
+ }
+ return 0;
+}
+
+/* What do we do on shutdown ? */
+int roles_sp_cleanup()
+{
+ return 0;
+}
+
+/* roles_post_op
+ -----------
+ Catch all for all post operations that change entries
+ in some way - this simply notifies the cache of a
+ change - the cache decides if action is necessary
+*/
+static int roles_post_op( Slapi_PBlock *pb )
+{
+ slapi_log_error( SLAPI_LOG_PLUGIN, ROLES_PLUGIN_SUBSYSTEM, "--> roles_post_op\n");
+
+ roles_cache_change_notify(pb);
+
+ slapi_log_error( SLAPI_LOG_PLUGIN, ROLES_PLUGIN_SUBSYSTEM, "<-- roles_post_op\n");
+ return 0; /* always succeed */
+}
+
+static void roles_set_plugin_identity(void * identity)
+{
+ roles_plugin_identity=identity;
+}
+
+void * roles_get_plugin_identity()
+{
+ return roles_plugin_identity;
+}
+
diff --git a/ldap/servers/plugins/shared/Makefile b/ldap/servers/plugins/shared/Makefile
new file mode 100644
index 00000000..847735ee
--- /dev/null
+++ b/ldap/servers/plugins/shared/Makefile
@@ -0,0 +1,56 @@
+#
+# BEGIN COPYRIGHT BLOCK
+# Copyright 2001 Sun Microsystems, Inc.
+# Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+# All rights reserved.
+# END COPYRIGHT BLOCK
+#
+#
+# GNU Makefile for shared components for Directory Server plugins
+#
+#
+
+LDAP_SRC = ../../..
+MCOM_ROOT = ../../../../..
+
+SHARED=shared
+
+NOSTDCLEAN=true # don't let nsconfig.mk define target clean
+NOSTDSTRIP=true # don't let nsconfig.mk define target strip
+#NSPR20=true # probably should be defined somewhere else (not sure where)
+
+OBJDEST = $(OBJDIR)/lib/$(SHARED)
+LIBDIR = $(LDAP_LIBDIR)
+
+include $(MCOM_ROOT)/ldapserver/nsdefs.mk
+include $(MCOM_ROOT)/ldapserver/nsconfig.mk
+include $(LDAP_SRC)/nsldap.mk
+
+CFLAGS+=$(SLCFLAGS)
+
+INCLUDES += -I$(LDAP_SRC)/servers/slapd
+
+LOCAL_OBJS= utils.o
+
+OBJS = $(addprefix $(OBJDEST)/, $(LOCAL_OBJS))
+
+ifeq ($(ARCH), WINNT)
+EXTRA_LIBS_DEP += $(LIBSLAPD_DEP)
+EXTRA_LIBS += $(LIBSLAPD)
+endif
+
+all: $(OBJDEST) $(OBJS)
+
+veryclean: clean
+
+clean:
+ $(RM) $(OBJS)
+
+$(OBJDEST):
+ $(MKDIR) $(OBJDEST)
+
+#
+# header file dependencies (incomplete)
+#
+$(OBJS): plugin-utils.h
+
diff --git a/ldap/servers/plugins/shared/plugin-utils.h b/ldap/servers/plugins/shared/plugin-utils.h
new file mode 100644
index 00000000..31c956f4
--- /dev/null
+++ b/ldap/servers/plugins/shared/plugin-utils.h
@@ -0,0 +1,77 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+/***********************************************************************
+**
+** NAME
+** plugin-utils.h
+**
+** DESCRIPTION
+**
+**
+** AUTHOR
+** <rweltman@netscape.com>
+**
+***********************************************************************/
+
+#ifndef _PLUGIN_UTILS_H_
+#define _PLUGIN_UTILS_H_
+
+/***********************************************************************
+** Includes
+***********************************************************************/
+
+#include <slapi-plugin.h>
+/*
+ * slapi-plugin-compat4.h is needed because we use the following deprecated
+ * functions:
+ *
+ * slapi_search_internal()
+ * slapi_modify_internal()
+ */
+#include "slapi-plugin-compat4.h"
+#include <dirlite_strings.h>
+#include <stdio.h>
+#include <string.h>
+#ifdef _WINDOWS
+#undef strcasecmp
+#define strcasecmp strcmpi
+#endif
+#include "dirver.h"
+
+#ifdef LDAP_DEBUG
+#ifndef DEBUG
+#define DEBUG
+#endif
+#endif
+
+#define BEGIN do {
+#define END } while(0);
+
+int initCounterLock();
+int op_error(int internal_error);
+Slapi_PBlock *readPblockAndEntry( const char *baseDN, const char *filter,
+ char *attrs[] );
+int entryHasObjectClass(Slapi_PBlock *pb, Slapi_Entry *e,
+ const char *objectClass);
+Slapi_PBlock *dnHasObjectClass( const char *baseDN, const char *objectClass );
+Slapi_PBlock *dnHasAttribute( const char *baseDN, const char *attrName );
+int setCounter( Slapi_Entry *e, const char *attrName, int value );
+int updateCounter( Slapi_Entry *e, const char *attrName, int increment );
+int updateCounterByDN( const char *dn, const char *attrName, int increment );
+
+typedef struct DNLink {
+ char *dn;
+ void *data;
+ struct DNLink *next;
+} DNLink;
+
+DNLink *cacheInit( void );
+DNLink *cacheAdd( DNLink *root, char *dn, void *data );
+char *cacheRemove( DNLink *root, char *dn );
+int cacheDelete( DNLink *root, char *dn );
+DNLink *cacheFind( DNLink *root, char *dn );
+
+#endif /* _PLUGIN_UTILS_H_ */
diff --git a/ldap/servers/plugins/shared/utils.c b/ldap/servers/plugins/shared/utils.c
new file mode 100644
index 00000000..d5044b17
--- /dev/null
+++ b/ldap/servers/plugins/shared/utils.c
@@ -0,0 +1,467 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+/***********************************************************************
+** NAME
+** utils.c
+**
+** DESCRIPTION
+**
+**
+** AUTHOR
+** <rweltman@netscape.com>
+**
+***********************************************************************/
+
+
+/***********************************************************************
+** Includes
+***********************************************************************/
+
+#include "plugin-utils.h"
+
+static char *plugin_name = "utils";
+
+/*
+ * Lock for updating a counter (global for all counters)
+ */
+static Slapi_Mutex *counter_lock = NULL;
+
+/* ------------------------------------------------------------ */
+/*
+ * op_error - Record (and report) an operational error.
+ */
+int
+op_error(int internal_error) {
+ slapi_log_error(SLAPI_LOG_PLUGIN, plugin_name,
+ "Internal error: %d\n", internal_error);
+
+ return LDAP_OPERATIONS_ERROR;
+}
+
+int initCounterLock() {
+ if ( NULL == counter_lock ) {
+ if ( !(counter_lock = slapi_new_mutex()) ) {
+ return 200;
+ }
+ }
+ return 0;
+}
+
+/* ------------------------------------------------------------ */
+/*
+ * readPblockAndEntry - search for and read an entry
+ * Return:
+ * A pblock containing the entry, or NULL
+ */
+Slapi_PBlock *
+readPblockAndEntry( const char *baseDN, const char *filter,
+ char *attrs[] ) {
+ int result = 0;
+ Slapi_PBlock *spb = NULL;
+
+ BEGIN
+ int sres;
+
+ /* Perform the search - the new pblock needs to be freed */
+ spb = slapi_search_internal((char *)baseDN, LDAP_SCOPE_BASE,
+ (char *)filter, NULL, attrs, 0);
+ if ( !spb ) {
+ result = op_error(20);
+ break;
+ }
+
+ if ( slapi_pblock_get( spb, SLAPI_PLUGIN_INTOP_RESULT, &sres ) ) {
+ result = op_error(21);
+ break;
+ } else if (sres) {
+ result = op_error(22);
+ break;
+ }
+ END
+
+ return spb;
+}
+
+/* ------------------------------------------------------------ */
+/*
+ * hasObjectClass - read an entry and check if it has a
+ * particular object class value
+ * Return:
+ * 1 - the entry contains the object class value
+ * 0 - the entry doesn't contain the object class value
+ */
+int
+entryHasObjectClass(Slapi_PBlock *pb, Slapi_Entry *e,
+ const char *objectClass) {
+ Slapi_Attr *attr;
+ Slapi_Value *v;
+ const struct berval *bv;
+ int vhint;
+
+ if ( slapi_entry_attr_find(e, "objectclass", &attr) ) {
+ return 0; /* no objectclass values! */
+ }
+
+ /*
+ * Check each of the object class values in turn.
+ */
+ for ( vhint = slapi_attr_first_value( attr, &v );
+ vhint != -1;
+ vhint = slapi_attr_next_value( attr, vhint, &v )) {
+ bv = slapi_value_get_berval(v);
+ if ( NULL != bv && NULL != bv->bv_val &&
+ !strcasecmp(bv->bv_val, objectClass) ) {
+ return 1;
+ }
+ }
+ return 0;
+}
+
+/* ------------------------------------------------------------ */
+/*
+ * dnHasObjectClass - read an entry if it has a particular object class
+ * Return:
+ * A pblock containing the entry, or NULL
+ */
+Slapi_PBlock *
+dnHasObjectClass( const char *baseDN, const char *objectClass ) {
+ int result = 0;
+ Slapi_PBlock *spb = NULL;
+
+ BEGIN
+ Slapi_Entry **entries;
+ char filter[1024];
+ char *attrs[2];
+
+ /* Perform the search - the new pblock needs to be freed */
+ attrs[0] = "objectclass";
+ attrs[1] = NULL;
+ sprintf( filter, "objectclass=%s", objectClass );
+ if ( !(spb = readPblockAndEntry( baseDN, filter, attrs) ) ) {
+ break;
+ }
+
+ if ( slapi_pblock_get(spb, SLAPI_PLUGIN_INTOP_SEARCH_ENTRIES,
+ &entries) ) {
+ result = op_error(23);
+ break;
+ }
+ /*
+ * Can only be one entry returned on a base search; just check
+ * the first one
+ */
+ if ( !*entries ) {
+ /* Clean up */
+ slapi_free_search_results_internal(spb);
+ slapi_pblock_destroy(spb);
+ spb = NULL;
+ }
+ END
+
+ return spb;
+}
+
+/* ------------------------------------------------------------ */
+/*
+ * dnHasAttribute - read an entry if it has a particular attribute
+ * Return:
+ * The entry, or NULL
+ */
+Slapi_PBlock *
+dnHasAttribute( const char *baseDN, const char *attrName ) {
+ int result = 0;
+ Slapi_PBlock *spb = NULL;
+
+ BEGIN
+ int sres;
+ Slapi_Entry **entries;
+ char filter[1024];
+ char *attrs[2];
+
+ /* Perform the search - the new pblock needs to be freed */
+ attrs[0] = (char *)attrName;
+ attrs[1] = NULL;
+ sprintf( filter, "%s=*", attrName );
+ spb = slapi_search_internal((char *)baseDN, LDAP_SCOPE_BASE,
+ filter, NULL, attrs, 0);
+ if ( !spb ) {
+ result = op_error(20);
+ break;
+ }
+
+ if ( slapi_pblock_get( spb, SLAPI_PLUGIN_INTOP_RESULT, &sres ) ) {
+ result = op_error(21);
+ break;
+ } else if (sres) {
+ result = op_error(22);
+ break;
+ }
+
+ if ( slapi_pblock_get(spb, SLAPI_PLUGIN_INTOP_SEARCH_ENTRIES,
+ &entries) ) {
+ result = op_error(23);
+ break;
+ }
+ /*
+ * Can only be one entry returned on a base search; just check
+ * the first one
+ */
+ if ( !*entries ) {
+ /* Clean up */
+ slapi_free_search_results_internal(spb);
+ slapi_pblock_destroy(spb);
+ spb = NULL;
+ }
+ END
+
+ return spb;
+}
+
+/* ------------------------------------------------------------ */
+/*
+ * setCounter - set the value of a counter
+ *
+ * Return:
+ * LDAP_SUCCESS - updated the attribute
+ * other - failure to update the count
+ */
+int
+setCounter( Slapi_Entry *e, const char *attrName, int value ) {
+ int result = LDAP_SUCCESS;
+ Slapi_PBlock *modifySpb = NULL;
+ char strValue[16];
+ char *strValues[2] = { NULL };
+ LDAPMod mod;
+ LDAPMod *mods[2];
+ int res;
+
+ BEGIN
+ /* Store the updated value */
+ strValues[0] = strValue;
+ sprintf( strValue, "%d", value );
+ mod.mod_op = LDAP_MOD_REPLACE;
+ mod.mod_type = (char *)attrName;
+ mod.mod_values = strValues;
+ mods[0] = &mod;
+ mods[1] = NULL;
+ modifySpb = slapi_modify_internal( slapi_entry_get_dn(e), mods,
+ NULL, 1 );
+ /* Check if the operation succeeded */
+ if ( slapi_pblock_get( modifySpb, SLAPI_PLUGIN_INTOP_RESULT,
+ &res ) ) {
+ result = op_error(33);
+ break;
+ } else if (res) {
+ result = op_error(34);
+ break;
+ }
+ slapi_pblock_destroy(modifySpb);
+ END
+ return result;
+}
+
+/* ------------------------------------------------------------ */
+/*
+ * updateCounter - read and increment/decrement the value of a counter
+ *
+ * Return:
+ * LDAP_SUCCESS - updated the attribute
+ * other - failure to update the count
+ */
+int
+updateCounter( Slapi_Entry *e, const char *attrName, int increment ) {
+ int result = LDAP_SUCCESS;
+ Slapi_PBlock *modifySpb = NULL;
+ Slapi_Attr *attr;
+ int value = 0;
+ char strValue[16];
+ char *strValues[2] = { NULL };
+ LDAPMod mod;
+ LDAPMod *mods[2];
+ int res;
+
+ BEGIN
+ /* Lock the entry */
+ slapi_lock_mutex(counter_lock);
+ /* Get the count attribute */
+ if ( slapi_entry_attr_find(e, (char *)attrName, &attr) ) {
+ /* No count yet; that's OK */
+ } else {
+ /* Get the first value for the attribute */
+ Slapi_Value *v = NULL;
+ const struct berval *bv = NULL;
+
+ if ( -1 == slapi_attr_first_value( attr, &v ) || NULL == v ||
+ NULL == ( bv = slapi_value_get_berval(v)) ||
+ NULL == bv->bv_val ) {
+ /* No values yet; that's OK, too */
+ } else {
+ value = atoi( bv->bv_val );
+ }
+ }
+ /* Add the increment */
+ value += increment;
+ if ( value < 0 ) {
+ value = 0;
+ }
+ /* Store the updated value */
+ strValues[0] = strValue;
+ sprintf( strValue, "%d", value );
+ mod.mod_op = LDAP_MOD_REPLACE;
+ mod.mod_type = (char *)attrName;
+ mod.mod_values = strValues;
+ mods[0] = &mod;
+ mods[1] = NULL;
+ modifySpb = slapi_modify_internal( slapi_entry_get_dn(e), mods,
+ NULL, 1 );
+ /* Check if the operation succeeded */
+ if ( slapi_pblock_get( modifySpb, SLAPI_PLUGIN_INTOP_RESULT,
+ &res ) ) {
+ result = op_error(33);
+ break;
+ } else if (res) {
+ result = op_error(34);
+ break;
+ }
+ slapi_pblock_destroy(modifySpb);
+ /* Unlock the entry */
+ slapi_unlock_mutex(counter_lock);
+#ifdef DEBUG
+ slapi_log_error(SLAPI_LOG_PLUGIN, plugin_name,
+ "adjusted %s in %s by %d to %d\n",
+ attrName, slapi_entry_get_dn(e), increment, value);
+#endif
+
+ END
+ return result;
+}
+
+/* ------------------------------------------------------------ */
+/*
+ * updateCounterByDN - read and increment/decrement the value of a counter
+ *
+ * Return:
+ * LDAP_SUCCESS - updated the attribute
+ * other - failure to update the count
+ */
+int
+updateCounterByDN( const char *dn, const char *attrName, int increment ) {
+ int result = LDAP_SUCCESS;
+ Slapi_PBlock *spb = NULL;
+ Slapi_Entry **entries;
+
+ BEGIN
+ char *attrs[2];
+ int sres;
+
+ /* Perform the search - the new pblock needs to be freed */
+ attrs[0] = (char *)attrName;
+ attrs[1] = NULL;
+ spb = slapi_search_internal((char *)dn, LDAP_SCOPE_BASE,
+ "objectclass=*", NULL, attrs, 0);
+ if ( !spb ) {
+ result = op_error(20);
+ break;
+ }
+
+ if ( slapi_pblock_get( spb, SLAPI_PLUGIN_INTOP_RESULT, &sres ) ) {
+ result = op_error(21);
+ break;
+ } else if (sres) {
+ result = op_error(22);
+ break;
+ }
+
+ if ( slapi_pblock_get(spb, SLAPI_PLUGIN_INTOP_SEARCH_ENTRIES,
+ &entries) ) {
+ result = op_error(23);
+ break;
+ }
+ END
+ if ( 0 == result ) {
+ result = updateCounter( *entries, attrName, increment );
+ }
+ if ( NULL != spb ) {
+ /* Clean up */
+ slapi_free_search_results_internal(spb);
+ slapi_pblock_destroy(spb);
+ }
+ return result;
+}
+
+/*
+ * Lock for accessing a cache (global for all caches)
+ */
+static Slapi_Mutex *cache_lock = NULL;
+
+DNLink *cacheInit() {
+ DNLink *root;
+ slapi_lock_mutex(cache_lock);
+ root = (DNLink *)malloc( sizeof(DNLink) );
+ root->next = NULL;
+ root->data = NULL;
+ root->dn = (char *)malloc(1);
+ root->dn[0] = 0;
+ slapi_unlock_mutex(cache_lock);
+ return root;
+}
+
+DNLink *cacheAdd( DNLink *root, char *dn, void *data ) {
+ if ( NULL == root ) {
+ return NULL;
+ }
+ slapi_lock_mutex(cache_lock);
+ for( ; root->next; root = root->next ) {
+ }
+ root->next = (DNLink *)malloc( sizeof(DNLink) );
+ root = root->next;
+ root->dn = dn;
+ root->data = data;
+ root->next = NULL;
+ slapi_unlock_mutex(cache_lock);
+ return root;
+}
+
+char *cacheRemove( DNLink *root, char *dn ) {
+ char *found = NULL;
+ DNLink *current = root;
+ DNLink *prev = NULL;
+ if ( NULL == root ) {
+ return NULL;
+ }
+ slapi_lock_mutex(cache_lock);
+ for( ; current; prev = current, current = current->next ) {
+ if ( !strcmp( current->dn, dn ) ) {
+ found = current->dn;
+ prev->next = current->next;
+ slapi_ch_free( (void **)&current );
+ break;
+ }
+ }
+ slapi_unlock_mutex(cache_lock);
+ return found;
+}
+
+int cacheDelete( DNLink *root, char *dn ) {
+ char *found = cacheRemove( root, dn );
+ if ( found ) {
+ slapi_ch_free( (void **)&found );
+ return 0;
+ } else {
+ return 1;
+ }
+}
+
+DNLink *cacheFind( DNLink *root, char *dn ) {
+ if ( NULL == root ) {
+ return NULL;
+ }
+ slapi_lock_mutex(cache_lock);
+ for( ; root && strcmp(dn, root->dn); root = root->next ) {
+ }
+ slapi_unlock_mutex(cache_lock);
+ return root;
+}
diff --git a/ldap/servers/plugins/statechange/Makefile b/ldap/servers/plugins/statechange/Makefile
new file mode 100644
index 00000000..bc04a3f0
--- /dev/null
+++ b/ldap/servers/plugins/statechange/Makefile
@@ -0,0 +1,78 @@
+#
+# BEGIN COPYRIGHT BLOCK
+# Copyright 2001 Sun Microsystems, Inc.
+# Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+# All rights reserved.
+# END COPYRIGHT BLOCK
+#
+LDAP_SRC = ../../..
+MCOM_ROOT = ../../../../..
+
+NOSTDCLEAN=true # don't let nsconfig.mk define target clean
+NOSTDSTRIP=true # don't let nsconfig.mk define target strip
+NSPR20=true # probably should be defined somewhere else (not sure where)
+
+OBJDEST = $(OBJDIR)/lib/libstatechange
+LIBDIR = $(LIB_RELDIR)
+
+include $(MCOM_ROOT)/ldapserver/nsdefs.mk
+include $(MCOM_ROOT)/ldapserver/nsconfig.mk
+include $(LDAP_SRC)/nsldap.mk
+
+ifeq ($(ARCH), WINNT)
+DEF_FILE:=./statechange.def
+endif
+
+STATECHANGE_OBJS = statechange.o
+OBJS = $(addprefix $(OBJDEST)/, $(STATECHANGE_OBJS))
+
+STATECHANGE_DLL = statechange-plugin
+
+INCLUDES += -I../../slapd -I../../../include
+CFLAGS+=$(SLCFLAGS) -DSLAPD_LOGGING
+
+ifeq ($(ARCH), WINNT)
+EXTRA_LIBS_DEP += $(LIBSLAPD)
+EXTRA_LIBS += $(NSPRLINK) $(LIBSLAPD)
+STATECHANGE_DLL_OBJ = $(addprefix $(OBJDEST)/, dllmain.o)
+endif
+
+ifeq ($(ARCH), HPUX)
+EXTRA_LIBS_DEP += $(LIBSLAPD_DEP) $(LDAPSDK_DEP) $(NSPR_DEP) $(SECURITY_DEP)
+EXTRA_LIBS += $(DYN_NSHTTPD) $(ADMINUTIL_LINK) $(LDAPLINK) $(SECURITYLINK) $(NSPRLINK) $(ICULINK)
+endif
+
+ifeq ($(ARCH), AIX)
+LD=ld
+EXTRA_LIBS += $(LIBSLAPD)
+endif
+
+STATECHANGE= $(addprefix $(LIBDIR)/, $(STATECHANGE_DLL).$(DLL_SUFFIX))
+
+clientSDK:
+
+all: $(OBJDEST) $(LIBDIR) $(STATECHANGE)
+
+ifeq ($(ARCH), WINNT)
+$(STATECHANGE): $(OBJS) $(STATECHANGE_DLL_OBJ) $(DEF_FILE)
+ $(LINK_DLL) $(STATECHANGE_DLL_OBJ) $(EXTRA_LIBS) /DEF:$(DEF_FILE)
+else
+$(STATECHANGE): $(OBJS) $(STATECHANGE_DLL_OBJ)
+ $(LINK_DLL) $(STATECHANGE_DLL_OBJ) $(EXTRA_LIBS)
+endif
+
+
+veryclean: clean
+
+clean:
+ $(RM) $(OBJS)
+ifeq ($(ARCH), WINNT)
+ $(RM) $(STATECHANGE_DLL_OBJ)
+endif
+ $(RM) $(STATECHANGE)
+
+$(OBJDEST):
+ $(MKDIR) $(OBJDEST)
+
+$(LIBDIR):
+ $(MKDIR) $(LIBDIR)
diff --git a/ldap/servers/plugins/statechange/dllmain.c b/ldap/servers/plugins/statechange/dllmain.c
new file mode 100644
index 00000000..fabf8677
--- /dev/null
+++ b/ldap/servers/plugins/statechange/dllmain.c
@@ -0,0 +1,96 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+/*
+ * Microsoft Windows specifics for BACK-LDBM DLL
+ */
+#include "ldap.h"
+#include "lber.h"
+
+
+#ifdef _WIN32
+/* Lifted from Q125688
+ * How to Port a 16-bit DLL to a Win32 DLL
+ * on the MSVC 4.0 CD
+ */
+BOOL WINAPI DllMain (HANDLE hModule, DWORD fdwReason, LPVOID lpReserved)
+{
+ WSADATA wsadata;
+
+ switch (fdwReason)
+ {
+ case DLL_PROCESS_ATTACH:
+ /* Code from LibMain inserted here. Return TRUE to keep the
+ DLL loaded or return FALSE to fail loading the DLL.
+
+ You may have to modify the code in your original LibMain to
+ account for the fact that it may be called more than once.
+ You will get one DLL_PROCESS_ATTACH for each process that
+ loads the DLL. This is different from LibMain which gets
+ called only once when the DLL is loaded. The only time this
+ is critical is when you are using shared data sections.
+ If you are using shared data sections for statically
+ allocated data, you will need to be careful to initialize it
+ only once. Check your code carefully.
+
+ Certain one-time initializations may now need to be done for
+ each process that attaches. You may also not need code from
+ your original LibMain because the operating system may now
+ be doing it for you.
+ */
+ /*
+ * 16 bit code calls UnlockData()
+ * which is mapped to UnlockSegment in windows.h
+ * in 32 bit world UnlockData is not defined anywhere
+ * UnlockSegment is mapped to GlobalUnfix in winbase.h
+ * and the docs for both UnlockSegment and GlobalUnfix say
+ * ".. function is oboslete. Segments have no meaning
+ * in the 32-bit environment". So we do nothing here.
+ */
+
+ if( errno = WSAStartup(0x0101, &wsadata ) != 0 )
+ return FALSE;
+
+ break;
+
+ case DLL_THREAD_ATTACH:
+ /* Called each time a thread is created in a process that has
+ already loaded (attached to) this DLL. Does not get called
+ for each thread that exists in the process before it loaded
+ the DLL.
+
+ Do thread-specific initialization here.
+ */
+ break;
+
+ case DLL_THREAD_DETACH:
+ /* Same as above, but called when a thread in the process
+ exits.
+
+ Do thread-specific cleanup here.
+ */
+ break;
+
+ case DLL_PROCESS_DETACH:
+ /* Code from _WEP inserted here. This code may (like the
+ LibMain) not be necessary. Check to make certain that the
+ operating system is not doing it for you.
+ */
+ WSACleanup();
+
+ break;
+ }
+ /* The return value is only used for DLL_PROCESS_ATTACH; all other
+ conditions are ignored. */
+ return TRUE; // successful DLL_PROCESS_ATTACH
+}
+#else
+int CALLBACK
+LibMain( HINSTANCE hinst, WORD wDataSeg, WORD cbHeapSize, LPSTR lpszCmdLine )
+{
+ /*UnlockData( 0 );*/
+ return( 1 );
+}
+#endif
diff --git a/ldap/servers/plugins/statechange/statechange.c b/ldap/servers/plugins/statechange/statechange.c
new file mode 100644
index 00000000..c0942b41
--- /dev/null
+++ b/ldap/servers/plugins/statechange/statechange.c
@@ -0,0 +1,450 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+/* plugin which provides a callback mechanism for state changes in the DS */
+
+#include <stdio.h>
+#include <string.h>
+#include "portable.h"
+#include "slapi-plugin.h"
+#include <dirlite_strings.h> /* PLUGIN_MAGIC_VENDOR_STR */
+#include "dirver.h"
+#include "statechange.h"
+
+/* get file mode flags for unix */
+#ifndef _WIN32
+#include <sys/stat.h>
+#endif
+
+/* the circular list of systems to notify */
+typedef struct _statechange_notify
+{
+ char *caller_id;
+ char *dn;
+ char *filter;
+ Slapi_Filter *realfilter;
+ notify_callback func;
+ void *caller_data;
+ struct _statechange_notify *next;
+ struct _statechange_notify *prev;
+} SCNotify;
+
+static SCNotify *head; /* a place to start in the list */
+
+#define SCN_PLUGIN_SUBSYSTEM "statechange-plugin" /* used for logging */
+
+static void *api[5];
+static Slapi_Mutex *buffer_lock = 0;
+
+/* other function prototypes */
+int statechange_init( Slapi_PBlock *pb );
+static int statechange_start( Slapi_PBlock *pb );
+static int statechange_close( Slapi_PBlock *pb );
+static int statechange_post_op( Slapi_PBlock *pb, int modtype );
+static int statechange_mod_post_op( Slapi_PBlock *pb );
+static int statechange_modrdn_post_op( Slapi_PBlock *pb );
+static int statechange_add_post_op( Slapi_PBlock *pb );
+static int statechange_delete_post_op( Slapi_PBlock *pb );
+static int _statechange_register(char *caller_id, char *dn, char *filter, void *caller_data, notify_callback func);
+static void *_statechange_unregister(char *dn, char *filter, notify_callback func);
+static void _statechange_unregister_all(char *caller_id, caller_data_free_callback);
+static void _statechange_vattr_cache_invalidator_callback(Slapi_Entry *e, char *dn, int modtype, Slapi_PBlock *pb, void *caller_data);
+static SCNotify *statechange_find_notify(char *dn, char *filter, notify_callback func);
+
+
+static Slapi_PluginDesc pdesc = { "statechange", PLUGIN_MAGIC_VENDOR_STR, PRODUCTTEXT,
+ "state change notification service plugin" };
+
+
+#ifdef _WIN32
+int *module_ldap_debug = 0;
+
+void plugin_init_debug_level(int *level_ptr)
+{
+ module_ldap_debug = level_ptr;
+}
+#endif
+
+
+/*
+ statechange_init
+ --------
+ adds our callbacks to the list
+*/
+int statechange_init( Slapi_PBlock *pb )
+{
+ int ret = 0;
+
+ slapi_log_error( SLAPI_LOG_TRACE, SCN_PLUGIN_SUBSYSTEM, "--> statechange_init\n");
+
+ head = 0;
+
+ if ( slapi_pblock_set( pb, SLAPI_PLUGIN_VERSION,
+ SLAPI_PLUGIN_VERSION_01 ) != 0 ||
+ slapi_pblock_set(pb, SLAPI_PLUGIN_START_FN,
+ (void *) statechange_start ) != 0 ||
+ slapi_pblock_set(pb, SLAPI_PLUGIN_POST_MODIFY_FN,
+ (void *) statechange_mod_post_op ) != 0 ||
+ slapi_pblock_set(pb, SLAPI_PLUGIN_POST_MODRDN_FN,
+ (void *) statechange_modrdn_post_op ) != 0 ||
+ slapi_pblock_set(pb, SLAPI_PLUGIN_POST_ADD_FN,
+ (void *) statechange_add_post_op ) != 0 ||
+ slapi_pblock_set(pb, SLAPI_PLUGIN_POST_DELETE_FN,
+ (void *) statechange_delete_post_op ) != 0 ||
+ slapi_pblock_set(pb, SLAPI_PLUGIN_CLOSE_FN,
+ (void *) statechange_close ) != 0 ||
+ slapi_pblock_set( pb, SLAPI_PLUGIN_DESCRIPTION,
+ (void *)&pdesc ) != 0 )
+ {
+ slapi_log_error( SLAPI_LOG_FATAL, SCN_PLUGIN_SUBSYSTEM,
+ "statechange_init: failed to register plugin\n" );
+ ret = -1;
+ }
+
+ slapi_log_error( SLAPI_LOG_TRACE, SCN_PLUGIN_SUBSYSTEM, "<-- statechange_init\n");
+ return ret;
+}
+
+
+/*
+ statechange_start
+ ---------
+ This function publishes the interface for this plugin
+*/
+static int statechange_start( Slapi_PBlock *pb )
+{
+ int ret = 0;
+
+ slapi_log_error( SLAPI_LOG_TRACE, SCN_PLUGIN_SUBSYSTEM, "--> statechange_start\n");
+
+ api[0] = 0; /* reserved for api broker use, must be zero */
+ api[1] = (void *)_statechange_register;
+ api[2] = (void *)_statechange_unregister;
+ api[3] = (void *)_statechange_unregister_all;
+ api[4] = (void *)_statechange_vattr_cache_invalidator_callback;
+
+ if(0 == (buffer_lock = slapi_new_mutex())) /* we never free this mutex */
+ {
+ /* badness */
+ slapi_log_error( SLAPI_LOG_FATAL, SCN_PLUGIN_SUBSYSTEM, "statechange: failed to create lock\n");
+ ret = -1;
+ }
+ else
+ {
+ if( slapi_apib_register(StateChange_v1_0_GUID, api) )
+ {
+ slapi_log_error( SLAPI_LOG_FATAL, SCN_PLUGIN_SUBSYSTEM, "statechange: failed to publish state change interface\n");
+ ret = -1;
+ }
+ }
+
+ head = 0;
+
+ slapi_log_error( SLAPI_LOG_TRACE, SCN_PLUGIN_SUBSYSTEM, "<-- statechange_start\n");
+ return ret;
+}
+
+/*
+ statechange_close
+ ---------
+ unregisters the interface for this plugin
+*/
+static int statechange_close( Slapi_PBlock *pb )
+{
+ slapi_log_error( SLAPI_LOG_TRACE, SCN_PLUGIN_SUBSYSTEM, "--> statechange_close\n");
+
+ slapi_apib_unregister(StateChange_v1_0_GUID);
+
+ slapi_log_error( SLAPI_LOG_TRACE, SCN_PLUGIN_SUBSYSTEM, "<-- statechange_close\n");
+
+ return 0;
+}
+
+
+static int statechange_mod_post_op( Slapi_PBlock *pb )
+{
+ return statechange_post_op(pb, LDAP_CHANGETYPE_MODIFY);
+}
+
+static int statechange_modrdn_post_op( Slapi_PBlock *pb )
+{
+ return statechange_post_op(pb, LDAP_CHANGETYPE_MODDN);
+}
+
+static int statechange_add_post_op( Slapi_PBlock *pb )
+{
+ return statechange_post_op(pb, LDAP_CHANGETYPE_ADD);
+}
+
+static int statechange_delete_post_op( Slapi_PBlock *pb )
+{
+ return statechange_post_op(pb, LDAP_CHANGETYPE_DELETE);
+}
+
+
+/*
+ statechange_post_op
+ -----------
+ Catch all for all post operations that change entries
+ in some way - evaluate the change against the notification
+ entries and fire off the relevant callbacks - it is called
+ from the real postop functions which supply it with the
+ postop type
+*/
+static int statechange_post_op( Slapi_PBlock *pb, int modtype )
+{
+ SCNotify *notify = head;
+ int execute;
+ char *dn = NULL;
+ struct slapi_entry *e_before = NULL;
+ struct slapi_entry *e_after = NULL;
+
+ if(head == 0)
+ return 0;
+
+ slapi_log_error( SLAPI_LOG_TRACE, SCN_PLUGIN_SUBSYSTEM, "--> statechange_post_op\n");
+
+ /* evaluate this operation against the notification entries */
+
+ slapi_lock_mutex(buffer_lock);
+ if(head)
+ {
+ if(slapi_pblock_get( pb, SLAPI_TARGET_DN, &dn ))
+ {
+ slapi_log_error( SLAPI_LOG_FATAL, SCN_PLUGIN_SUBSYSTEM, "statechange_post_op: failed to get dn of changed entry");
+ goto bail;
+ }
+
+ slapi_dn_normalize( dn );
+
+ slapi_pblock_get( pb, SLAPI_ENTRY_PRE_OP, &e_before );
+ slapi_pblock_get( pb, SLAPI_ENTRY_POST_OP, &e_after );
+
+ do
+ {
+ execute = 0;
+
+ /* first dn */
+ if(notify && notify->dn)
+ {
+ if(0 != slapi_dn_issuffix(dn, notify->dn))
+ execute = 1;
+ }
+ else
+ /* note, if supplied null for everything in the entry *all* ops match */
+ if(notify)
+ execute = 1;
+
+ if(execute && notify->filter)
+ {
+ /* next the filter */
+ int filter_test = 0;
+
+ /* need to test entry both before and after op */
+ if(e_before && !slapi_filter_test_simple( e_before, notify->realfilter))
+ filter_test = 1;
+
+ if(!filter_test && e_after && !slapi_filter_test_simple( e_after, notify->realfilter))
+ filter_test = 1;
+
+ if(!filter_test)
+ execute = 0;
+ }
+
+ if(execute)
+ {
+ if(e_after)
+ (notify->func)(e_after, dn, modtype, pb, notify->caller_data);
+ else
+ (notify->func)(e_before, dn, modtype, pb, notify->caller_data);
+ }
+
+ notify = notify->next;
+ }
+ while(notify != head);
+ }
+bail:
+ slapi_unlock_mutex(buffer_lock);
+
+ slapi_log_error( SLAPI_LOG_TRACE, SCN_PLUGIN_SUBSYSTEM, "<-- statechange_post_op\n");
+ return 0; /* always succeed */
+}
+
+
+static int _statechange_register(char *caller_id, char *dn, char *filter, void *caller_data, notify_callback func)
+{
+ int ret = -1;
+ SCNotify *item;
+
+ /* simple - we don't check for duplicates */
+
+ item = (SCNotify*)slapi_ch_malloc(sizeof(SCNotify));
+ if(item)
+ {
+ char *writable_filter = slapi_ch_strdup(filter);
+ item->caller_id = slapi_ch_strdup(caller_id);
+ if(dn)
+ {
+ item->dn = slapi_ch_strdup(dn);
+ slapi_dn_normalize( item->dn );
+ }
+ else
+ item->dn = 0;
+ item->filter = slapi_ch_strdup(filter);
+ item->caller_data = caller_data;
+ item->realfilter = slapi_str2filter(writable_filter);
+ item->func = func;
+
+ slapi_lock_mutex(buffer_lock);
+ if(head == NULL)
+ {
+ head = item;
+ head->next = head;
+ head->prev = head;
+ }
+ else
+ {
+ item->next = head;
+ item->prev = head->prev;
+ head->prev = item;
+ item->prev->next = item;
+ }
+ slapi_unlock_mutex(buffer_lock);
+ slapi_ch_free_string(&writable_filter);
+
+ ret = 0;
+ }
+
+ return ret;
+}
+
+static void *_statechange_unregister(char *dn, char *filter, notify_callback thefunc)
+{
+ void *ret = NULL;
+ SCNotify *func = NULL;
+
+ if(buffer_lock == 0)
+ return ret;
+
+ slapi_lock_mutex(buffer_lock);
+
+ if(func = statechange_find_notify(dn, filter, thefunc))
+ {
+ func->prev->next = func->next;
+ func->next->prev = func->prev;
+
+ if(func == head)
+ {
+ head = func->next;
+ }
+
+ if(func == head) /* must be the last item, turn off the lights */
+ head = 0;
+
+ slapi_ch_free_string(&func->caller_id);
+ slapi_ch_free_string(&func->dn);
+ slapi_ch_free_string(&func->filter);
+ slapi_filter_free( func->realfilter, 1 );
+ ret = func->caller_data;
+ slapi_ch_free((void **)&func);
+ }
+
+ slapi_unlock_mutex(buffer_lock);
+
+ return ret;
+}
+
+static void _statechange_unregister_all(char *caller_id, caller_data_free_callback callback)
+{
+ SCNotify *notify = head;
+ SCNotify *start_notify = head;
+
+ if(buffer_lock == 0)
+ return;
+
+ slapi_lock_mutex(buffer_lock);
+
+
+ if(notify)
+ {
+ do
+ {
+ SCNotify *notify_next = notify->next;
+
+ if( slapi_utf8casecmp((unsigned char *)caller_id, (unsigned char *)notify->caller_id) )
+ {
+ notify->prev->next = notify->next;
+ notify->next->prev = notify->prev;
+
+ if(notify == head)
+ {
+ head = notify->next;
+ start_notify = notify->prev;
+ }
+
+ if(notify == head) /* must be the last item, turn off the lights */
+ head = 0;
+
+ if(callback)
+ callback(notify->caller_data);
+ slapi_ch_free_string(&notify->caller_id);
+ slapi_ch_free_string(&notify->dn);
+ slapi_ch_free_string(&notify->filter);
+ slapi_filter_free( notify->realfilter, 1 );
+ slapi_ch_free((void **)&notify);
+ }
+
+ notify = notify_next;
+ }
+ while(notify != start_notify && notify != NULL);
+ }
+
+ slapi_unlock_mutex(buffer_lock);
+}
+
+/* this func needs looking at to make work */
+static SCNotify *statechange_find_notify(char *dn, char *filter, notify_callback func)
+{
+ SCNotify *notify = head;
+ SCNotify *start_notify = head;
+
+ if(notify)
+ {
+ do
+ {
+ if( !slapi_utf8casecmp((unsigned char *)dn, (unsigned char *)notify->dn) &&
+ !slapi_utf8casecmp((unsigned char *)filter, (unsigned char *)notify->filter) && func == notify->func)
+ {
+ return notify;
+ }
+
+ notify = notify->next;
+ }
+ while(notify != start_notify);
+ }
+
+ return 0;
+}
+
+/* intended for use by vattr service providers
+ * to deal with significant vattr state changes
+ */
+static void _statechange_vattr_cache_invalidator_callback(Slapi_Entry *e, char *dn, int modtype, Slapi_PBlock *pb, void *caller_data)
+{
+ /* simply get the significance data and act */
+ switch(*(int*)caller_data)
+ {
+ case STATECHANGE_VATTR_ENTRY_INVALIDATE:
+ if(e)
+ slapi_entry_vattrcache_watermark_invalidate(e);
+ break;
+
+ case STATECHANGE_VATTR_GLOBAL_INVALIDATE:
+ default:
+ slapi_entrycache_vattrcache_watermark_invalidate();
+ break;
+ }
+}
+
diff --git a/ldap/servers/plugins/statechange/statechange.def b/ldap/servers/plugins/statechange/statechange.def
new file mode 100644
index 00000000..ed6dca6b
--- /dev/null
+++ b/ldap/servers/plugins/statechange/statechange.def
@@ -0,0 +1,10 @@
+; BEGIN COPYRIGHT BLOCK
+; Copyright 2001 Sun Microsystems, Inc.
+; Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+; All rights reserved.
+; END COPYRIGHT BLOCK
+;
+DESCRIPTION 'Netscape Directory Server 7.0 State Change Plugin'
+EXPORTS
+ statechange_init @2
+ plugin_init_debug_level @3
diff --git a/ldap/servers/plugins/syntaxes/Makefile b/ldap/servers/plugins/syntaxes/Makefile
new file mode 100644
index 00000000..edc9bd9c
--- /dev/null
+++ b/ldap/servers/plugins/syntaxes/Makefile
@@ -0,0 +1,87 @@
+#
+# BEGIN COPYRIGHT BLOCK
+# Copyright 2001 Sun Microsystems, Inc.
+# Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+# All rights reserved.
+# END COPYRIGHT BLOCK
+#
+#
+# GNU Makefile for Directory Server syntax-plugin.so syntax plugins
+#
+
+LDAP_SRC = ../../..
+MCOM_ROOT = ../../../../..
+
+NOSTDCLEAN=true # don't let nsconfig.mk define target clean
+NOSTDSTRIP=true # don't let nsconfig.mk define target strip
+NSPR20=true # probably should be defined somewhere else (not sure where)
+
+OBJDEST = $(OBJDIR)/lib/libsyntax
+LIBDIR = $(LIB_RELDIR)
+
+include $(MCOM_ROOT)/ldapserver/nsconfig.mk
+include $(LDAP_SRC)/nsldap.mk
+
+ifeq ($(ARCH), WINNT)
+DEF_FILE:=./libsyntax.def
+endif
+
+CFLAGS+=$(SLCFLAGS)
+
+INCLUDES += -I$(LDAP_SRC)/servers/slapd
+
+SYNTAX_OBJS= phonetic.o string.o cis.o sicis.o ces.o bin.o tel.o dn.o int.o \
+ value.o debug.o
+
+OBJS = $(addprefix $(OBJDEST)/, $(SYNTAX_OBJS))
+
+ifeq ($(ARCH), WINNT)
+LIBSYNTAX_DLL_OBJ = $(addprefix $(OBJDEST)/, dllmain.o)
+endif
+
+LIBSYNTAX= $(addprefix $(LIBDIR)/, $(SYNTAX_DLL).$(DLL_SUFFIX))
+
+ifeq ($(ARCH), WINNT)
+EXTRA_LIBS_DEP += $(LIBSLAPD_DEP) $(LDAP_LIBUTIL_DEP)
+EXTRA_LIBS_DEP += $(LDAPSDK_DEP)
+EXTRA_LIBS += $(LIBSLAPD) $(LDAP_SDK_LIBLDAP_DLL)
+endif
+ifeq ($(ARCH), AIX)
+EXTRA_LIBS_DEP += $(LIBSLAPD_DEP) $(LDAP_LIBUTIL_DEP)
+EXTRA_LIBS_DEP += $(LDAPSDK_DEP)
+EXTRA_LIBS += $(LIBSLAPD) $(LDAP_SDK_LIBLDAP_DLL)
+endif
+
+ifeq ($(ARCH), WINNT)
+DLL_LDFLAGS += -def:"./libsyntax.def"
+CFLAGS+= /WX
+endif # WINNT
+
+ifeq ($(ARCH), HPUX)
+EXTRA_LIBS_DEP += $(LIBSLAPD_DEP) $(LDAPSDK_DEP) $(NSPR_DEP) $(SECURITY_DEP)
+EXTRA_LIBS += $(DYN_NSHTTPD) $(ADMINUTIL_LINK) $(LDAPLINK) $(SECURITYLINK) $(NSPRLINK) $(ICULINK)
+endif
+
+ifeq ($(ARCH), AIX)
+EXTRA_LIBS += $(DLL_EXTRA_LIBS)
+LD=ld
+endif
+
+clientSDK:
+
+all: $(OBJDEST) $(LIBDIR) $(LIBSYNTAX)
+
+$(LIBSYNTAX): $(OBJS) $(LIBSYNTAX_DLL_OBJ) $(DEF_FILE)
+ $(LINK_DLL) $(LIBSYNTAX_DLL_OBJ) $(EXTRA_LIBS)
+
+veryclean: clean
+
+clean:
+ $(RM) $(OBJS)
+ifeq ($(ARCH), WINNT)
+ $(RM) $(LIBSYNTAX_DLL_OBJ)
+endif
+ $(RM) $(LIBSYNTAX)
+
+$(OBJDEST):
+ $(MKDIR) $(OBJDEST)
diff --git a/ldap/servers/plugins/syntaxes/bin.c b/ldap/servers/plugins/syntaxes/bin.c
new file mode 100644
index 00000000..da06e55e
--- /dev/null
+++ b/ldap/servers/plugins/syntaxes/bin.c
@@ -0,0 +1,201 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+/* bin.c - bin syntax routines */
+
+/*
+ * This file actually implements two syntax plugins: OctetString and Binary.
+ * We treat them identically for now. XXXmcs: check if that is correct.
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <sys/types.h>
+#include "syntax.h"
+
+static int bin_filter_ava( Slapi_PBlock *pb, struct berval *bvfilter,
+ Slapi_Value **bvals, int ftype, Slapi_Value **retVal );
+static int bin_values2keys( Slapi_PBlock *pb, Slapi_Value **bvals,
+ Slapi_Value ***ivals, int ftype );
+static int bin_assertion2keys_ava( Slapi_PBlock *pb, Slapi_Value *bval,
+ Slapi_Value ***ivals, int ftype );
+
+/*
+ * Attribute syntaxes. We treat all of these the same for now, even though
+ * the specifications (e.g., RFC 2252) impose various constraints on the
+ * the format for each of these.
+ *
+ * Note: the first name is the official one from RFC 2252.
+ */
+static char *bin_names[] = { "Binary", "bin", BINARY_SYNTAX_OID, 0 };
+
+static char *octetstring_names[] = { "OctetString", OCTETSTRING_SYNTAX_OID, 0 };
+
+static char *jpeg_names[] = { "JPEG", JPEG_SYNTAX_OID, 0 };
+
+
+static Slapi_PluginDesc bin_pdesc = {
+ "bin-syntax", PLUGIN_MAGIC_VENDOR_STR, PRODUCTTEXT,
+ "binary attribute syntax plugin"
+};
+
+static Slapi_PluginDesc octetstring_pdesc = {
+ "octetstring-syntax", PLUGIN_MAGIC_VENDOR_STR, PRODUCTTEXT,
+ "octet string attribute syntax plugin"
+};
+
+static Slapi_PluginDesc jpeg_pdesc = {
+ "jpeg-syntax", PLUGIN_MAGIC_VENDOR_STR, PRODUCTTEXT,
+ "JPEG attribute syntax plugin"
+};
+
+
+/*
+ * register_bin_like_plugin(): register all items for a bin-like plugin.
+ */
+static int
+register_bin_like_plugin( Slapi_PBlock *pb, Slapi_PluginDesc *pdescp,
+ char **names, char *oid )
+{
+ int rc;
+
+ rc = slapi_pblock_set( pb, SLAPI_PLUGIN_VERSION,
+ (void *) SLAPI_PLUGIN_VERSION_01 );
+ rc |= slapi_pblock_set( pb, SLAPI_PLUGIN_DESCRIPTION,
+ (void *)pdescp );
+ rc |= slapi_pblock_set( pb, SLAPI_PLUGIN_SYNTAX_FILTER_AVA,
+ (void *) bin_filter_ava );
+ rc |= slapi_pblock_set( pb, SLAPI_PLUGIN_SYNTAX_VALUES2KEYS,
+ (void *) bin_values2keys );
+ rc |= slapi_pblock_set( pb, SLAPI_PLUGIN_SYNTAX_ASSERTION2KEYS_AVA,
+ (void *) bin_assertion2keys_ava );
+ rc |= slapi_pblock_set( pb, SLAPI_PLUGIN_SYNTAX_NAMES,
+ (void *) names );
+ rc |= slapi_pblock_set( pb, SLAPI_PLUGIN_SYNTAX_OID,
+ (void *) oid );
+
+ return( rc );
+}
+
+
+int
+bin_init( Slapi_PBlock *pb )
+{
+ int rc;
+
+ LDAPDebug( LDAP_DEBUG_PLUGIN, "=> bin_init\n", 0, 0, 0 );
+ rc = register_bin_like_plugin( pb, &bin_pdesc, bin_names,
+ BINARY_SYNTAX_OID );
+ LDAPDebug( LDAP_DEBUG_PLUGIN, "<= bin_init %d\n", rc, 0, 0 );
+ return( rc );
+}
+
+
+int
+octetstring_init( Slapi_PBlock *pb )
+{
+ int rc;
+
+ LDAPDebug( LDAP_DEBUG_PLUGIN, "=> octetstring_init\n", 0, 0, 0 );
+ rc = register_bin_like_plugin( pb, &octetstring_pdesc, octetstring_names,
+ OCTETSTRING_SYNTAX_OID );
+ LDAPDebug( LDAP_DEBUG_PLUGIN, "<= octetstring_init %d\n", rc, 0, 0 );
+ return( rc );
+}
+
+
+int
+jpeg_init( Slapi_PBlock *pb )
+{
+ int rc;
+
+ LDAPDebug( LDAP_DEBUG_PLUGIN, "=> jpeg_init\n", 0, 0, 0 );
+ rc = register_bin_like_plugin( pb, &jpeg_pdesc, jpeg_names,
+ JPEG_SYNTAX_OID );
+ LDAPDebug( LDAP_DEBUG_PLUGIN, "<= jpeg_init %d\n", rc, 0, 0 );
+ return( rc );
+}
+
+
+static int
+bin_filter_ava( Slapi_PBlock *pb, struct berval *bvfilter,
+ Slapi_Value **bvals, int ftype, Slapi_Value **retVal )
+{
+ int i;
+
+ for ( i = 0; bvals[i] != NULL; i++ ) {
+ if ( slapi_value_get_length(bvals[i]) == bvfilter->bv_len &&
+ 0 == memcmp( slapi_value_get_string(bvals[i]), bvfilter->bv_val, bvfilter->bv_len ))
+ {
+ if(retVal!=NULL)
+ {
+ *retVal= bvals[i];
+ }
+ return( 0 );
+ }
+ }
+ if(retVal!=NULL)
+ {
+ *retVal= NULL;
+ }
+ return( -1 );
+}
+
+static int
+bin_values2keys( Slapi_PBlock *pb, Slapi_Value **bvals,
+ Slapi_Value ***ivals, int ftype )
+{
+ int i;
+
+ if ( ftype != LDAP_FILTER_EQUALITY ) {
+ return( LDAP_PROTOCOL_ERROR );
+ }
+
+ for ( i = 0; bvals[i] != NULL; i++ ) {
+ /* NULL */
+ }
+ (*ivals) = (Slapi_Value **) slapi_ch_malloc(( i + 1 ) *
+ sizeof(Slapi_Value *) );
+
+ for ( i = 0; bvals[i] != NULL; i++ )
+ {
+ (*ivals)[i] = slapi_value_dup(bvals[i]);
+ }
+ (*ivals)[i] = NULL;
+
+ return( 0 );
+}
+
+static int
+bin_assertion2keys_ava( Slapi_PBlock *pb, Slapi_Value *bval,
+ Slapi_Value ***ivals, int ftype )
+{
+ Slapi_Value *tmpval=NULL;
+ size_t len;
+
+ if (( ftype != LDAP_FILTER_EQUALITY ) &&
+ ( ftype != LDAP_FILTER_EQUALITY_FAST))
+ {
+ return( LDAP_PROTOCOL_ERROR );
+ }
+ if(ftype == LDAP_FILTER_EQUALITY_FAST) {
+ /* With the fast option, we are trying to avoid creating and freeing
+ * a bunch of structures - we just do one malloc here - see
+ * ava_candidates in filterentry.c
+ */
+ len=slapi_value_get_length(bval);
+ tmpval=(*ivals)[0];
+ if (len > tmpval->bv.bv_len) {
+ tmpval->bv.bv_val=(char *)slapi_ch_malloc(len);
+ }
+ tmpval->bv.bv_len=len;
+ memcpy(tmpval->bv.bv_val,slapi_value_get_string(bval),len);
+ } else {
+ (*ivals) = (Slapi_Value **) slapi_ch_malloc( 2 * sizeof(Slapi_Value *) );
+ (*ivals)[0] = slapi_value_dup( bval );
+ (*ivals)[1] = NULL;
+ }
+ return( 0 );
+}
diff --git a/ldap/servers/plugins/syntaxes/ces.c b/ldap/servers/plugins/syntaxes/ces.c
new file mode 100644
index 00000000..075da03b
--- /dev/null
+++ b/ldap/servers/plugins/syntaxes/ces.c
@@ -0,0 +1,168 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+/* ces.c - caseexactstring syntax routines */
+
+#include <stdio.h>
+#include <string.h>
+#include <sys/types.h>
+#include "syntax.h"
+
+static int ces_filter_ava( Slapi_PBlock *pb, struct berval *bvfilter,
+ Slapi_Value **bvals, int ftype, Slapi_Value **retVal );
+static int ces_filter_sub( Slapi_PBlock *pb, char *initial, char **any,
+ char *final, Slapi_Value **bvals );
+static int ces_values2keys( Slapi_PBlock *pb, Slapi_Value **val,
+ Slapi_Value ***ivals, int ftype );
+static int ces_assertion2keys_ava( Slapi_PBlock *pb, Slapi_Value *val,
+ Slapi_Value ***ivals, int ftype );
+static int ces_assertion2keys_sub( Slapi_PBlock *pb, char *initial, char **any,
+ char *final, Slapi_Value ***ivals );
+static int ces_compare(struct berval *v1, struct berval *v2);
+
+/* the first name is the official one from RFC 2252 */
+static char *ia5_names[] = { "IA5String", "ces", "caseexactstring",
+ IA5STRING_SYNTAX_OID, 0 };
+
+/* the first name is the official one from RFC 2252 */
+static char *uri_names[] = { "URI", "1.3.6.1.4.1.4401.1.1.1",0};
+
+static Slapi_PluginDesc ia5_pdesc = { "ces-syntax", PLUGIN_MAGIC_VENDOR_STR,
+ PRODUCTTEXT, "caseExactString attribute syntax plugin" };
+
+static Slapi_PluginDesc uri_pdesc = { "uri-syntax", PLUGIN_MAGIC_VENDOR_STR,
+ PRODUCTTEXT, "uri attribute syntax plugin" };
+
+
+/*
+ * register_ces_like_plugin(): register all items for a cis-like plugin.
+ */
+static int
+register_ces_like_plugin( Slapi_PBlock *pb, Slapi_PluginDesc *pdescp,
+ char **names, char *oid )
+{
+ int rc, flags;
+
+ rc = slapi_pblock_set( pb, SLAPI_PLUGIN_VERSION,
+ (void *) SLAPI_PLUGIN_VERSION_01 );
+ rc |= slapi_pblock_set( pb, SLAPI_PLUGIN_DESCRIPTION,
+ (void *) pdescp );
+ rc |= slapi_pblock_set( pb, SLAPI_PLUGIN_SYNTAX_FILTER_AVA,
+ (void *) ces_filter_ava );
+ rc |= slapi_pblock_set( pb, SLAPI_PLUGIN_SYNTAX_FILTER_SUB,
+ (void *) ces_filter_sub );
+ rc |= slapi_pblock_set( pb, SLAPI_PLUGIN_SYNTAX_VALUES2KEYS,
+ (void *) ces_values2keys );
+ rc |= slapi_pblock_set( pb, SLAPI_PLUGIN_SYNTAX_ASSERTION2KEYS_AVA,
+ (void *) ces_assertion2keys_ava );
+ rc |= slapi_pblock_set( pb, SLAPI_PLUGIN_SYNTAX_ASSERTION2KEYS_SUB,
+ (void *) ces_assertion2keys_sub );
+ flags = SLAPI_PLUGIN_SYNTAX_FLAG_ORDERING;
+ rc |= slapi_pblock_set( pb, SLAPI_PLUGIN_SYNTAX_FLAGS,
+ (void *) &flags );
+ rc |= slapi_pblock_set( pb, SLAPI_PLUGIN_SYNTAX_NAMES,
+ (void *) names );
+ rc |= slapi_pblock_set( pb, SLAPI_PLUGIN_SYNTAX_OID,
+ (void *) oid );
+ rc |= slapi_pblock_set( pb, SLAPI_PLUGIN_SYNTAX_COMPARE,
+ (void *) ces_compare );
+
+ return( rc );
+}
+
+int
+ces_init( Slapi_PBlock *pb )
+{
+ int rc;
+
+ LDAPDebug( LDAP_DEBUG_PLUGIN, "=> ces_init\n", 0, 0, 0 );
+
+ rc = register_ces_like_plugin(pb,&ia5_pdesc,ia5_names,IA5STRING_SYNTAX_OID);
+
+ LDAPDebug( LDAP_DEBUG_PLUGIN, "<= ces_init %d\n", rc, 0, 0 );
+ return( rc );
+}
+
+int
+uri_init( Slapi_PBlock *pb )
+{
+ int rc;
+
+ LDAPDebug( LDAP_DEBUG_PLUGIN, "=> uri_init\n", 0, 0, 0 );
+
+ rc = register_ces_like_plugin(pb,&uri_pdesc,uri_names,
+ "1.3.6.1.4.1.4401.1.1.1");
+
+ LDAPDebug( LDAP_DEBUG_PLUGIN, "<= uri_init %d\n", rc, 0, 0 );
+ return( rc );
+}
+
+static int
+ces_filter_ava(
+ Slapi_PBlock *pb,
+ struct berval *bvfilter,
+ Slapi_Value **bvals,
+ int ftype,
+ Slapi_Value **retVal
+)
+{
+ return( string_filter_ava( bvfilter, bvals, SYNTAX_CES, ftype,
+ retVal) );
+}
+
+static int
+ces_filter_sub(
+ Slapi_PBlock *pb,
+ char *initial,
+ char **any,
+ char *final,
+ Slapi_Value **bvals
+)
+{
+ return( string_filter_sub( pb, initial, any, final, bvals, SYNTAX_CES ) );
+}
+
+static int
+ces_values2keys(
+ Slapi_PBlock *pb,
+ Slapi_Value **vals,
+ Slapi_Value ***ivals,
+ int ftype
+)
+{
+ return( string_values2keys( pb, vals, ivals, SYNTAX_CES, ftype ) );
+}
+
+static int
+ces_assertion2keys_ava(
+ Slapi_PBlock *pb,
+ Slapi_Value *val,
+ Slapi_Value ***ivals,
+ int ftype
+)
+{
+ return(string_assertion2keys_ava( pb, val, ivals, SYNTAX_CES, ftype ));
+}
+
+static int
+ces_assertion2keys_sub(
+ Slapi_PBlock *pb,
+ char *initial,
+ char **any,
+ char *final,
+ Slapi_Value ***ivals
+)
+{
+ return( string_assertion2keys_sub( pb, initial, any, final, ivals,
+ SYNTAX_CES ) );
+}
+
+static int ces_compare(
+ struct berval *v1,
+ struct berval *v2
+)
+{
+ return value_cmp(v1,v2,SYNTAX_CES,3 /* Normalise both values */);
+}
diff --git a/ldap/servers/plugins/syntaxes/cis.c b/ldap/servers/plugins/syntaxes/cis.c
new file mode 100644
index 00000000..e2047fbc
--- /dev/null
+++ b/ldap/servers/plugins/syntaxes/cis.c
@@ -0,0 +1,298 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+/* cis.c - caseignorestring syntax routines */
+
+/*
+ * This file actually implements three syntax plugins:
+ * DirectoryString
+ * Boolean
+ * GeneralizedTime
+ *
+ * We treat them identically for now. XXXmcs: we could do some validation on
+ * Boolean and GeneralizedTime values (someday, maybe).
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <sys/types.h>
+#include "syntax.h"
+
+static int cis_filter_ava( Slapi_PBlock *pb, struct berval *bvfilter,
+ Slapi_Value **bvals, int ftype, Slapi_Value **retVal );
+static int cis_filter_sub( Slapi_PBlock *pb, char *initial, char **any,
+ char *final, Slapi_Value **bvals );
+static int cis_values2keys( Slapi_PBlock *pb, Slapi_Value **val,
+ Slapi_Value ***ivals, int ftype );
+static int cis_assertion2keys_ava( Slapi_PBlock *pb, Slapi_Value *val,
+ Slapi_Value ***ivals, int ftype );
+static int cis_assertion2keys_sub( Slapi_PBlock *pb, char *initial, char **any,
+ char *final, Slapi_Value ***ivals );
+static int cis_compare(struct berval *v1, struct berval *v2);
+
+/*
+ * Attribute syntaxes. We treat all of these the same for now, even though
+ * the specifications (e.g., RFC 2252) impose various constraints on the
+ * the format for each of these.
+ *
+ * Note: the first name is the official one from RFC 2252.
+ */
+static char *dirstring_names[] = { "DirectoryString", "cis",
+ "caseignorestring", DIRSTRING_SYNTAX_OID, 0 };
+
+static char *boolean_names[] = { "Boolean", BOOLEAN_SYNTAX_OID, 0 };
+
+static char *time_names[] = { "GeneralizedTime", "time",
+ GENERALIZEDTIME_SYNTAX_OID, 0 };
+
+static char *country_names[] = { "Country String",
+ COUNTRYSTRING_SYNTAX_OID, 0};
+
+static char *postal_names[] = { "Postal Address",
+ POSTALADDRESS_SYNTAX_OID, 0};
+
+static char *oid_names[] = { "OID",
+ OID_SYNTAX_OID, 0};
+
+
+/*
+ TBD (XXX)
+
+ "1.3.6.1.4.1.1466.115.121.1.16 \"DIT Content Rule Description
+\" "
+ "1.3.6.1.4.1.1466.115.121.1.17 \"DIT Structure Rule Descripti
+on\" "
+ "1.3.6.1.4.1.1466.115.121.1.20 \"DSE Type\" "
+ "1.3.6.1.4.1.1466.115.121.1.30 \"Matching Rule Description\"
+"
+ "1.3.6.1.4.1.1466.115.121.1.31 \"Matching Rule Use Descriptio
+n\" "
+ "1.3.6.1.4.1.1466.115.121.1.35 \"Name Form Description\" "
+
+ "1.3.6.1.4.1.1466.115.121.1.44 \"Printable String\" "
+ "1.3.6.1.4.1.1466.115.121.1.45 \"Subtree Specification\" "
+ "1.3.6.1.4.1.1466.115.121.1.54 \"LDAP Syntax Description\" "
+ "1.3.6.1.4.1.1466.115.121.1.55 \"Modify Rights\" "
+ "1.3.6.1.4.1.1466.115.121.1.56 \"LDAP Schema Description\" "
+ "1.3.6.1.4.1.1466.115.121.1.25 \"Guide\" "
+ "1.3.6.1.4.1.1466.115.121.1.52 \"Telex Number\" "
+ "1.3.6.1.4.1.1466.115.121.1.51 \"Teletex Terminal Identifier\
+" "
+ "1.3.6.1.4.1.1466.115.121.1.14 \"Delivery Method\" "
+ "1.3.6.1.4.1.1466.115.121.1.43 \"Presentation Address\" "
+ "1.3.6.1.4.1.1466.115.121.1.21 \"Enhanced Guide\" "
+ "1.3.6.1.4.1.1466.115.121.1.34 \"Name and Optional UID\" "
+ "1.2.840.113556.1.4.905 \"CaseIgnoreString\" "
+ "1.3.6.1.1.1.0.0 \"nisNetgroupTripleSyntax\" "
+ "1.3.6.1.1.1.0.1 \"bootParameterSyntax\" ");
+ */
+
+
+static Slapi_PluginDesc dirstring_pdesc = { "directorystring-syntax",
+ PLUGIN_MAGIC_VENDOR_STR, PRODUCTTEXT,
+ "DirectoryString attribute syntax plugin" };
+
+static Slapi_PluginDesc boolean_pdesc = { "boolean-syntax",
+ PLUGIN_MAGIC_VENDOR_STR, PRODUCTTEXT,
+ "Boolean attribute syntax plugin" };
+
+static Slapi_PluginDesc time_pdesc = { "time-syntax",
+ PLUGIN_MAGIC_VENDOR_STR, PRODUCTTEXT,
+ "GeneralizedTime attribute syntax plugin" };
+
+static Slapi_PluginDesc country_pdesc = { "countrystring-syntax",
+ PLUGIN_MAGIC_VENDOR_STR, PRODUCTTEXT,
+ "Country String attribute syntax plugin" };
+
+static Slapi_PluginDesc postal_pdesc = { "postaladdress-syntax",
+ PLUGIN_MAGIC_VENDOR_STR, PRODUCTTEXT,
+ "Postal Address attribute syntax plugin" };
+
+static Slapi_PluginDesc oid_pdesc = { "oid-syntax",
+ PLUGIN_MAGIC_VENDOR_STR, PRODUCTTEXT,
+ "OID attribute syntax plugin" };
+
+
+/*
+ * register_cis_like_plugin(): register all items for a cis-like plugin.
+ */
+static int
+register_cis_like_plugin( Slapi_PBlock *pb, Slapi_PluginDesc *pdescp,
+ char **names, char *oid )
+{
+ int rc, flags;
+
+ rc = slapi_pblock_set( pb, SLAPI_PLUGIN_VERSION,
+ (void *) SLAPI_PLUGIN_VERSION_01 );
+ rc |= slapi_pblock_set( pb, SLAPI_PLUGIN_DESCRIPTION,
+ (void *) pdescp );
+ rc |= slapi_pblock_set( pb, SLAPI_PLUGIN_SYNTAX_FILTER_AVA,
+ (void *) cis_filter_ava );
+ rc |= slapi_pblock_set( pb, SLAPI_PLUGIN_SYNTAX_FILTER_SUB,
+ (void *) cis_filter_sub );
+ rc |= slapi_pblock_set( pb, SLAPI_PLUGIN_SYNTAX_VALUES2KEYS,
+ (void *) cis_values2keys );
+ rc |= slapi_pblock_set( pb, SLAPI_PLUGIN_SYNTAX_ASSERTION2KEYS_AVA,
+ (void *) cis_assertion2keys_ava );
+ rc |= slapi_pblock_set( pb, SLAPI_PLUGIN_SYNTAX_ASSERTION2KEYS_SUB,
+ (void *) cis_assertion2keys_sub );
+ flags = SLAPI_PLUGIN_SYNTAX_FLAG_ORDERING;
+ rc |= slapi_pblock_set( pb, SLAPI_PLUGIN_SYNTAX_FLAGS,
+ (void *) &flags );
+ rc |= slapi_pblock_set( pb, SLAPI_PLUGIN_SYNTAX_NAMES,
+ (void *) names );
+ rc |= slapi_pblock_set( pb, SLAPI_PLUGIN_SYNTAX_OID,
+ (void *) oid );
+ rc |= slapi_pblock_set( pb, SLAPI_PLUGIN_SYNTAX_COMPARE,
+ (void *) cis_compare );
+
+ return( rc );
+}
+
+
+int
+cis_init( Slapi_PBlock *pb )
+{
+ int rc;
+
+ LDAPDebug( LDAP_DEBUG_PLUGIN, "=> cis_init\n", 0, 0, 0 );
+ rc = register_cis_like_plugin( pb, &dirstring_pdesc, dirstring_names,
+ DIRSTRING_SYNTAX_OID );
+ LDAPDebug( LDAP_DEBUG_PLUGIN, "<= cis_init %d\n", rc, 0, 0 );
+ return( rc );
+}
+
+
+int
+boolean_init( Slapi_PBlock *pb )
+{
+ int rc;
+
+ LDAPDebug( LDAP_DEBUG_PLUGIN, "=> boolean_init\n", 0, 0, 0 );
+ rc = register_cis_like_plugin( pb, &boolean_pdesc, boolean_names,
+ BOOLEAN_SYNTAX_OID );
+ LDAPDebug( LDAP_DEBUG_PLUGIN, "<= boolean_init %d\n", rc, 0, 0 );
+ return( rc );
+}
+
+
+int
+time_init( Slapi_PBlock *pb )
+{
+ int rc;
+
+ LDAPDebug( LDAP_DEBUG_PLUGIN, "=> time_init\n", 0, 0, 0 );
+ rc = register_cis_like_plugin( pb, &time_pdesc, time_names,
+ GENERALIZEDTIME_SYNTAX_OID );
+ LDAPDebug( LDAP_DEBUG_PLUGIN, "<= time_init %d\n", rc, 0, 0 );
+ return( rc );
+}
+
+int
+country_init( Slapi_PBlock *pb )
+{
+ int rc;
+
+ LDAPDebug( LDAP_DEBUG_PLUGIN, "=> country_init\n", 0, 0, 0 );
+ rc = register_cis_like_plugin( pb, &country_pdesc, country_names,
+ COUNTRYSTRING_SYNTAX_OID );
+ LDAPDebug( LDAP_DEBUG_PLUGIN, "<= country_init %d\n", rc, 0, 0 );
+ return( rc );
+}
+
+int
+postal_init( Slapi_PBlock *pb )
+{
+ int rc;
+
+ LDAPDebug( LDAP_DEBUG_PLUGIN, "=> postal_init\n", 0, 0, 0 );
+ rc = register_cis_like_plugin( pb, &postal_pdesc, postal_names,
+ POSTALADDRESS_SYNTAX_OID );
+ LDAPDebug( LDAP_DEBUG_PLUGIN, "<= postal_init %d\n", rc, 0, 0 );
+ return( rc );
+}
+
+
+int
+oid_init( Slapi_PBlock *pb )
+{
+ int rc;
+
+ LDAPDebug( LDAP_DEBUG_PLUGIN, "=> oid_init\n", 0, 0, 0 );
+ rc = register_cis_like_plugin( pb, &oid_pdesc, oid_names, OID_SYNTAX_OID );
+ LDAPDebug( LDAP_DEBUG_PLUGIN, "<= oid_init %d\n", rc, 0, 0 );
+ return( rc );
+}
+
+
+
+static int
+cis_filter_ava(
+ Slapi_PBlock *pb,
+ struct berval *bvfilter,
+ Slapi_Value **bvals,
+ int ftype,
+ Slapi_Value **retVal
+)
+{
+ return( string_filter_ava( bvfilter, bvals, SYNTAX_CIS, ftype,
+ retVal ) );
+}
+
+
+static int
+cis_filter_sub(
+ Slapi_PBlock *pb,
+ char *initial,
+ char **any,
+ char *final,
+ Slapi_Value **bvals
+)
+{
+ return( string_filter_sub( pb, initial, any, final, bvals, SYNTAX_CIS ) );
+}
+
+static int
+cis_values2keys(
+ Slapi_PBlock *pb,
+ Slapi_Value **vals,
+ Slapi_Value ***ivals,
+ int ftype
+)
+{
+ return( string_values2keys( pb, vals, ivals, SYNTAX_CIS, ftype ) );
+}
+
+static int
+cis_assertion2keys_ava(
+ Slapi_PBlock *pb,
+ Slapi_Value *val,
+ Slapi_Value ***ivals,
+ int ftype
+)
+{
+ return(string_assertion2keys_ava( pb, val, ivals, SYNTAX_CIS, ftype ));
+}
+
+static int
+cis_assertion2keys_sub(
+ Slapi_PBlock *pb,
+ char *initial,
+ char **any,
+ char *final,
+ Slapi_Value ***ivals
+)
+{
+ return( string_assertion2keys_sub( pb, initial, any, final, ivals,
+ SYNTAX_CIS ) );
+}
+
+static int cis_compare(
+ struct berval *v1,
+ struct berval *v2
+)
+{
+ return value_cmp(v1,v2,SYNTAX_CIS,3 /* Normalise both values */);
+}
diff --git a/ldap/servers/plugins/syntaxes/debug.c b/ldap/servers/plugins/syntaxes/debug.c
new file mode 100644
index 00000000..e4d69202
--- /dev/null
+++ b/ldap/servers/plugins/syntaxes/debug.c
@@ -0,0 +1,20 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+/* debug.c - syntax debug stuff */
+
+#include <stdio.h>
+#include <string.h>
+#include <sys/types.h>
+#include "syntax.h"
+
+#ifdef _WIN32
+int *module_ldap_debug = 0;
+
+void plugin_init_debug_level(int *level_ptr)
+{
+ module_ldap_debug = level_ptr;
+}
+#endif
diff --git a/ldap/servers/plugins/syntaxes/dllmain.c b/ldap/servers/plugins/syntaxes/dllmain.c
new file mode 100644
index 00000000..1f7aa331
--- /dev/null
+++ b/ldap/servers/plugins/syntaxes/dllmain.c
@@ -0,0 +1,133 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+/*
+ * Microsoft Windows specifics for syntax-plugin DLL
+ */
+#include "ldap.h"
+#include "syntax.h"
+
+
+#ifdef _WIN32
+/* Lifted from Q125688
+ * How to Port a 16-bit DLL to a Win32 DLL
+ * on the MSVC 4.0 CD
+ */
+BOOL WINAPI DllMain (HANDLE hModule, DWORD fdwReason, LPVOID lpReserved)
+{
+ WSADATA wsadata;
+
+ switch (fdwReason)
+ {
+ case DLL_PROCESS_ATTACH:
+ /* Code from LibMain inserted here. Return TRUE to keep the
+ DLL loaded or return FALSE to fail loading the DLL.
+
+ You may have to modify the code in your original LibMain to
+ account for the fact that it may be called more than once.
+ You will get one DLL_PROCESS_ATTACH for each process that
+ loads the DLL. This is different from LibMain which gets
+ called only once when the DLL is loaded. The only time this
+ is critical is when you are using shared data sections.
+ If you are using shared data sections for statically
+ allocated data, you will need to be careful to initialize it
+ only once. Check your code carefully.
+
+ Certain one-time initializations may now need to be done for
+ each process that attaches. You may also not need code from
+ your original LibMain because the operating system may now
+ be doing it for you.
+ */
+ /*
+ * 16 bit code calls UnlockData()
+ * which is mapped to UnlockSegment in windows.h
+ * in 32 bit world UnlockData is not defined anywhere
+ * UnlockSegment is mapped to GlobalUnfix in winbase.h
+ * and the docs for both UnlockSegment and GlobalUnfix say
+ * ".. function is oboslete. Segments have no meaning
+ * in the 32-bit environment". So we do nothing here.
+ */
+
+ if( errno = WSAStartup(0x0101, &wsadata ) != 0 )
+ return FALSE;
+
+ break;
+
+ case DLL_THREAD_ATTACH:
+ /* Called each time a thread is created in a process that has
+ already loaded (attached to) this DLL. Does not get called
+ for each thread that exists in the process before it loaded
+ the DLL.
+
+ Do thread-specific initialization here.
+ */
+ break;
+
+ case DLL_THREAD_DETACH:
+ /* Same as above, but called when a thread in the process
+ exits.
+
+ Do thread-specific cleanup here.
+ */
+ break;
+
+ case DLL_PROCESS_DETACH:
+ /* Code from _WEP inserted here. This code may (like the
+ LibMain) not be necessary. Check to make certain that the
+ operating system is not doing it for you.
+ */
+ WSACleanup();
+
+ break;
+ }
+ /* The return value is only used for DLL_PROCESS_ATTACH; all other
+ conditions are ignored. */
+ return TRUE; // successful DLL_PROCESS_ATTACH
+}
+#else
+int CALLBACK
+LibMain( HINSTANCE hinst, WORD wDataSeg, WORD cbHeapSize, LPSTR lpszCmdLine )
+{
+ /*UnlockData( 0 );*/
+ return( 1 );
+}
+#endif
+
+#ifdef LDAP_DEBUG
+#ifndef _WIN32
+#include <stdarg.h>
+#include <stdio.h>
+
+void LDAPDebug( int level, char* fmt, ... )
+{
+ static char debugBuf[1024];
+
+ if (module_ldap_debug && (*module_ldap_debug & level))
+ {
+ va_list ap;
+ va_start (ap, fmt);
+ _snprintf (debugBuf, sizeof(debugBuf), fmt, ap);
+ va_end (ap);
+
+ OutputDebugString (debugBuf);
+ }
+}
+#endif
+#endif
+
+#ifndef _WIN32
+
+/* The 16-bit version of the RTL does not implement perror() */
+
+#include <stdio.h>
+
+void perror( const char *msg )
+{
+ char buf[128];
+ wsprintf( buf, "%s: error %d\n", msg, WSAGetLastError()) ;
+ OutputDebugString( buf );
+}
+
+#endif
diff --git a/ldap/servers/plugins/syntaxes/dn.c b/ldap/servers/plugins/syntaxes/dn.c
new file mode 100644
index 00000000..e6a90df0
--- /dev/null
+++ b/ldap/servers/plugins/syntaxes/dn.c
@@ -0,0 +1,98 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+/* dn.c - dn syntax routines */
+
+#include <stdio.h>
+#include <string.h>
+#include <sys/types.h>
+#include "syntax.h"
+
+static int dn_filter_ava( Slapi_PBlock *pb, struct berval *bvfilter,
+ Slapi_Value **bvals, int ftype, Slapi_Value **retVal );
+static int dn_filter_sub( Slapi_PBlock *pb, char *initial, char **any,
+ char *final, Slapi_Value **bvals );
+static int dn_values2keys( Slapi_PBlock *pb, Slapi_Value **vals,
+ Slapi_Value ***ivals, int ftype );
+static int dn_assertion2keys_ava( Slapi_PBlock *pb, Slapi_Value *val,
+ Slapi_Value ***ivals, int ftype );
+static int dn_assertion2keys_sub( Slapi_PBlock *pb, char *initial, char **any,
+ char *final, Slapi_Value ***ivals );
+
+/* the first name is the official one from RFC 2252 */
+static char *names[] = { "DN", DN_SYNTAX_OID, 0 };
+
+static Slapi_PluginDesc pdesc = { "dn-syntax", PLUGIN_MAGIC_VENDOR_STR,
+ PRODUCTTEXT, "distinguished name attribute syntax plugin" };
+
+int
+dn_init( Slapi_PBlock *pb )
+{
+ int rc;
+
+ LDAPDebug( LDAP_DEBUG_PLUGIN, "=> dn_init\n", 0, 0, 0 );
+
+ rc = slapi_pblock_set( pb, SLAPI_PLUGIN_VERSION,
+ (void *) SLAPI_PLUGIN_VERSION_01 );
+ rc |= slapi_pblock_set( pb, SLAPI_PLUGIN_DESCRIPTION,
+ (void *)&pdesc );
+ rc |= slapi_pblock_set( pb, SLAPI_PLUGIN_SYNTAX_FILTER_AVA,
+ (void *) dn_filter_ava );
+ rc |= slapi_pblock_set( pb, SLAPI_PLUGIN_SYNTAX_FILTER_SUB,
+ (void *) dn_filter_sub );
+ rc |= slapi_pblock_set( pb, SLAPI_PLUGIN_SYNTAX_VALUES2KEYS,
+ (void *) dn_values2keys );
+ rc |= slapi_pblock_set( pb, SLAPI_PLUGIN_SYNTAX_ASSERTION2KEYS_AVA,
+ (void *) dn_assertion2keys_ava );
+ rc |= slapi_pblock_set( pb, SLAPI_PLUGIN_SYNTAX_ASSERTION2KEYS_SUB,
+ (void *) dn_assertion2keys_sub );
+ rc |= slapi_pblock_set( pb, SLAPI_PLUGIN_SYNTAX_NAMES,
+ (void *) names );
+ rc |= slapi_pblock_set( pb, SLAPI_PLUGIN_SYNTAX_OID,
+ (void *) DN_SYNTAX_OID );
+
+ LDAPDebug( LDAP_DEBUG_PLUGIN, "<= dn_init %d\n", rc, 0, 0 );
+ return( rc );
+}
+
+static int
+dn_filter_ava( Slapi_PBlock *pb, struct berval *bvfilter,
+ Slapi_Value **bvals, int ftype, Slapi_Value **retVal )
+{
+ return( string_filter_ava( bvfilter, bvals, SYNTAX_CIS | SYNTAX_DN,
+ ftype, retVal ) );
+}
+
+static int
+dn_filter_sub( Slapi_PBlock *pb, char *initial, char **any, char *final,
+ Slapi_Value **bvals )
+{
+ return( string_filter_sub( pb, initial, any, final, bvals,
+ SYNTAX_CIS | SYNTAX_DN ) );
+}
+
+static int
+dn_values2keys( Slapi_PBlock *pb, Slapi_Value **vals, Slapi_Value ***ivals,
+ int ftype )
+{
+ return( string_values2keys( pb, vals, ivals, SYNTAX_CIS | SYNTAX_DN,
+ ftype ) );
+}
+
+static int
+dn_assertion2keys_ava( Slapi_PBlock *pb, Slapi_Value *val,
+ Slapi_Value ***ivals, int ftype )
+{
+ return( string_assertion2keys_ava( pb, val, ivals,
+ SYNTAX_CIS | SYNTAX_DN, ftype ) );
+}
+
+static int
+dn_assertion2keys_sub( Slapi_PBlock *pb, char *initial, char **any, char *final,
+ Slapi_Value ***ivals )
+{
+ return( string_assertion2keys_sub( pb, initial, any, final, ivals,
+ SYNTAX_CIS | SYNTAX_DN ) );
+}
diff --git a/ldap/servers/plugins/syntaxes/int.c b/ldap/servers/plugins/syntaxes/int.c
new file mode 100644
index 00000000..f81167f2
--- /dev/null
+++ b/ldap/servers/plugins/syntaxes/int.c
@@ -0,0 +1,188 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+/* int.c - integer syntax routines */
+
+#include <stdio.h>
+#include <string.h>
+#include <sys/types.h>
+#include "syntax.h"
+
+static int int_filter_ava( Slapi_PBlock *pb, struct berval *bvfilter,
+ Slapi_Value **bvals, int ftype, Slapi_Value **retVal );
+static int int_values2keys( Slapi_PBlock *pb, Slapi_Value **val,
+ Slapi_Value ***ivals );
+static int int_assertion2keys( Slapi_PBlock *pb, Slapi_Value *val,
+ Slapi_Value ***ivals, int ftype );
+static int int_compare(struct berval *v1, struct berval *v2);
+static long int_to_canonical( long num );
+
+/* the first name is the official one from RFC 2252 */
+static char *names[] = { "INTEGER", "int", INTEGER_SYNTAX_OID, 0 };
+
+static Slapi_PluginDesc pdesc = { "int-syntax", PLUGIN_MAGIC_VENDOR_STR,
+ PRODUCTTEXT, "integer attribute syntax plugin" };
+
+int
+int_init( Slapi_PBlock *pb )
+{
+ int rc, flags;
+
+ LDAPDebug( LDAP_DEBUG_PLUGIN, "=> int_init\n", 0, 0, 0 );
+
+ rc = slapi_pblock_set( pb, SLAPI_PLUGIN_VERSION,
+ (void *) SLAPI_PLUGIN_VERSION_01 );
+ rc |= slapi_pblock_set( pb, SLAPI_PLUGIN_DESCRIPTION,
+ (void *)&pdesc );
+ rc |= slapi_pblock_set( pb, SLAPI_PLUGIN_SYNTAX_FILTER_AVA,
+ (void *) int_filter_ava );
+ rc |= slapi_pblock_set( pb, SLAPI_PLUGIN_SYNTAX_VALUES2KEYS,
+ (void *) int_values2keys );
+ rc |= slapi_pblock_set( pb, SLAPI_PLUGIN_SYNTAX_ASSERTION2KEYS_AVA,
+ (void *) int_assertion2keys );
+ flags = SLAPI_PLUGIN_SYNTAX_FLAG_ORDERING;
+ rc |= slapi_pblock_set( pb, SLAPI_PLUGIN_SYNTAX_FLAGS,
+ (void *) &flags );
+ rc |= slapi_pblock_set( pb, SLAPI_PLUGIN_SYNTAX_NAMES,
+ (void *) names );
+ rc |= slapi_pblock_set( pb, SLAPI_PLUGIN_SYNTAX_OID,
+ (void *) INTEGER_SYNTAX_OID );
+ rc |= slapi_pblock_set( pb, SLAPI_PLUGIN_SYNTAX_COMPARE,
+ (void *) int_compare );
+
+ LDAPDebug( LDAP_DEBUG_PLUGIN, "<= int_init %d\n", rc, 0, 0 );
+ return( rc );
+}
+
+static int
+int_filter_ava( Slapi_PBlock *pb, struct berval *bvfilter,
+ Slapi_Value **bvals, int ftype, Slapi_Value **retVal )
+{
+ int i, rc;
+ long flong, elong;
+
+ if ( ftype == LDAP_FILTER_APPROX ) {
+ return( LDAP_PROTOCOL_ERROR );
+ }
+ if(retVal) {
+ *retVal=NULL;
+ }
+ flong = atol( bvfilter->bv_val );
+ for ( i = 0; bvals[i] != NULL; i++ ) {
+ elong = atol ( slapi_value_get_string(bvals[i]) );
+ rc = elong - flong;
+ switch ( ftype ) {
+ case LDAP_FILTER_GE:
+ if ( rc >= 0 ) {
+ if(retVal) {
+ *retVal = bvals[i];
+ }
+ return( 0 );
+ }
+ break;
+ case LDAP_FILTER_LE:
+ if ( rc <= 0 ) {
+ if(retVal) {
+ *retVal = bvals[i];
+ }
+ return( 0 );
+ }
+ break;
+ case LDAP_FILTER_EQUALITY:
+ if ( rc == 0 ) {
+ if(retVal) {
+ *retVal = bvals[i];
+ }
+ return( 0 );
+ }
+ break;
+ }
+ }
+
+ return( -1 );
+}
+
+static int
+int_values2keys( Slapi_PBlock *pb, Slapi_Value **vals, Slapi_Value ***ivals )
+{
+ long num;
+ int i;
+
+ for ( i = 0; vals[i] != NULL; i++ ) {
+ /* NULL */
+ }
+
+ *ivals = (Slapi_Value **) slapi_ch_malloc(( i + 1 ) * sizeof(Slapi_Value *) );
+
+ for ( i = 0; vals[i] != NULL; i++ )
+ {
+ num = atol( slapi_value_get_string(vals[i]) );
+ num = int_to_canonical( num );
+ (*ivals)[i] = slapi_value_new();
+ slapi_value_set((*ivals)[i],&num,sizeof(long));
+ }
+ (*ivals)[i] = NULL;
+
+ return( 0 );
+}
+
+static int
+int_assertion2keys( Slapi_PBlock *pb, Slapi_Value *val, Slapi_Value ***ivals, int ftype )
+{
+ long num;
+ size_t len;
+ unsigned char *b;
+ Slapi_Value *tmpval=NULL;
+
+ num = atol( slapi_value_get_string(val) );
+ num = int_to_canonical( num );
+ /* similar to string.c to optimize equality path: avoid malloc/free */
+ if(ftype == LDAP_FILTER_EQUALITY_FAST) {
+ len=sizeof(long);
+ tmpval=(*ivals)[0];
+ if ( len > tmpval->bv.bv_len) {
+ tmpval->bv.bv_val=(char *)slapi_ch_malloc(len);
+ }
+ tmpval->bv.bv_len=len;
+ b = (unsigned char *)&num;
+ memcpy(tmpval->bv.bv_val,b,len);
+ } else {
+ *ivals = (Slapi_Value **) slapi_ch_malloc( 2 * sizeof(Slapi_Value *) );
+ (*ivals)[0] = (Slapi_Value *) slapi_ch_malloc( sizeof(Slapi_Value) );
+ /* XXXSD initialize memory */
+ memset((*ivals)[0],0,sizeof(Slapi_Value));
+ slapi_value_set((*ivals)[0],&num,sizeof(long));
+ (*ivals)[1] = NULL;
+ }
+ return( 0 );
+}
+
+static int int_compare(
+ struct berval *v1,
+ struct berval *v2
+)
+{
+ long value1 = atol(v1->bv_val);
+ long value2 = atol(v2->bv_val);
+
+ if (value1 == value2) {
+ return 0;
+ }
+ return ( ((value1 - value2) > 0) ? 1 : -1);
+}
+
+static long
+int_to_canonical( long num )
+{
+ long ret = 0L;
+ unsigned char *b = (unsigned char *)&ret;
+
+ b[0] = (unsigned char)(num >> 24);
+ b[1] = (unsigned char)(num >> 16);
+ b[2] = (unsigned char)(num >> 8);
+ b[3] = (unsigned char)num;
+
+ return ret;
+}
diff --git a/ldap/servers/plugins/syntaxes/libsyntax.def b/ldap/servers/plugins/syntaxes/libsyntax.def
new file mode 100644
index 00000000..30cb6b40
--- /dev/null
+++ b/ldap/servers/plugins/syntaxes/libsyntax.def
@@ -0,0 +1,24 @@
+; BEGIN COPYRIGHT BLOCK
+; Copyright 2001 Sun Microsystems, Inc.
+; Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+; All rights reserved.
+; END COPYRIGHT BLOCK
+;
+DESCRIPTION 'Directory Server 7 syntaxes Plugin'
+EXPORTS
+ cis_init @2
+ ces_init @3
+ tel_init @4
+ dn_init @5
+ bin_init @6
+ int_init @7
+ plugin_init_debug_level @8
+ octetstring_init @9
+ boolean_init @10
+ time_init @11
+ uri_init @12
+ country_init @13
+ postal_init @14
+ jpeg_init @15
+ oid_init @16
+ sicis_init @17
diff --git a/ldap/servers/plugins/syntaxes/phonetic.c b/ldap/servers/plugins/syntaxes/phonetic.c
new file mode 100644
index 00000000..1c1c8ba5
--- /dev/null
+++ b/ldap/servers/plugins/syntaxes/phonetic.c
@@ -0,0 +1,461 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+/* phonetic.c - routines to do phonetic matching */
+
+#include <stdio.h>
+#include <string.h>
+#include <ctype.h>
+#include <sys/types.h>
+#include "syntax.h"
+#include "portable.h"
+
+#if !defined(METAPHONE) && !defined(SOUNDEX)
+#define METAPHONE
+#endif
+
+#define iswordbreak(s) \
+(isascii(*(s)) \
+? (isspace(*(s)) || \
+ ispunct(*(s)) || \
+ isdigit(*(s)) || \
+ *(s) == '\0') \
+: utf8iswordbreak(s))
+
+static int
+utf8iswordbreak( const char* s )
+{
+ switch( LDAP_UTF8GETCC( s )) {
+ case 0x00A0: /* non-breaking space */
+ case 0x3000: /* ideographic space */
+ case 0xFEFF: /* zero-width non-breaking space */
+ return 1;
+ default: break;
+ }
+ return 0;
+}
+
+char *
+first_word( char *s )
+{
+ if ( s == NULL ) {
+ return( NULL );
+ }
+
+ while ( iswordbreak( s ) ) {
+ if ( *s == '\0' ) {
+ return( NULL );
+ } else {
+ LDAP_UTF8INC( s );
+ }
+ }
+
+ return( s );
+}
+
+char *
+next_word( char *s )
+{
+ if ( s == NULL ) {
+ return( NULL );
+ }
+
+ while ( ! iswordbreak( s ) ) {
+ LDAP_UTF8INC( s );
+ }
+
+ while ( iswordbreak( s ) ) {
+ if ( *s == '\0' ) {
+ return( NULL );
+ } else {
+ LDAP_UTF8INC( s );
+ }
+ }
+
+ return( s );
+}
+
+char *
+word_dup( char *w )
+{
+ char *s, *ret;
+ char save;
+
+ for ( s = w; !iswordbreak( s ); LDAP_UTF8INC( s ))
+ ; /* NULL */
+ save = *s;
+ *s = '\0';
+ ret = slapi_ch_strdup( w );
+ *s = save;
+
+ return( ret );
+}
+
+#ifndef MAXPHONEMELEN
+#define MAXPHONEMELEN 4
+#endif
+
+#if defined(SOUNDEX)
+
+/* lifted from isode-8.0 */
+char *
+phonetic( char *s )
+{
+ char code, adjacent, ch;
+ char *p;
+ char **c;
+ int i, cmax;
+ char phoneme[MAXPHONEMELEN + 1];
+
+ p = s;
+ if ( p == NULL || *p == '\0' ) {
+ return( NULL );
+ }
+
+ adjacent = '0';
+ phoneme[0] = TOUPPER(*p);
+
+ phoneme[1] = '\0';
+ for ( i = 0; i < 99 && (! iswordbreak(p)); LDAP_UTF8INC( p )) {
+ ch = TOUPPER (*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( slapi_ch_strdup( phoneme ) );
+}
+
+#else
+#if defined(METAPHONE)
+
+/*
+ * 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 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[42]; /* 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
+ */
+ n = ntrans + 4; n_end = ntrans + 35;
+ while (!iswordbreak( Word ) && n < n_end) {
+ if (isascii(*Word)) {
+ if (isalpha(*Word)) {
+ *n++ = TOUPPER(*Word);
+ }
+ ++Word;
+ } else {
+ auto const size_t len = LDAP_UTF8COPY(n, Word);
+ n += len; Word += len;
+ }
+ }
+ Metaph = buf;
+ *Metaph = '\0';
+ if (n == ntrans + 4) {
+ return( slapi_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 if (!isascii(*n)) {
+ *Metaph++ = *n;
+ } 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( slapi_ch_strdup( buf ) );
+}
+
+#endif /* METAPHONE */
+#endif /* !SOUNDEX */
diff --git a/ldap/servers/plugins/syntaxes/sicis.c b/ldap/servers/plugins/syntaxes/sicis.c
new file mode 100644
index 00000000..4bd6623d
--- /dev/null
+++ b/ldap/servers/plugins/syntaxes/sicis.c
@@ -0,0 +1,139 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2002 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+
+/*
+ * sicis.c - space insensitive string syntax routines.
+ * these strings are also case insensitive.
+ */
+#include <stdio.h>
+#include <string.h>
+#include <sys/types.h>
+#include "syntax.h"
+
+static int sicis_filter_ava( Slapi_PBlock *pb, struct berval *bvfilter,
+ Slapi_Value **bvals, int ftype, Slapi_Value **retVal );
+static int sicis_filter_sub( Slapi_PBlock *pb, char *initial, char **any,
+ char *final, Slapi_Value **bvals );
+static int sicis_values2keys( Slapi_PBlock *pb, Slapi_Value **val,
+ Slapi_Value ***ivals, int ftype );
+static int sicis_assertion2keys_ava( Slapi_PBlock *pb, Slapi_Value *val,
+ Slapi_Value ***ivals, int ftype );
+static int sicis_assertion2keys_sub( Slapi_PBlock *pb, char *initial,
+ char **any, char *final, Slapi_Value ***ivals );
+static int sicis_compare(struct berval *v1, struct berval *v2);
+
+/* the first name is the official one from RFC 2252 */
+static char *names[] = { "SpaceInsensitiveString",
+ SPACE_INSENSITIVE_STRING_SYNTAX_OID, 0 };
+
+static Slapi_PluginDesc pdesc = { "spaceinsensitivestring-syntax",
+ PLUGIN_MAGIC_VENDOR_STR, PRODUCTTEXT,
+ "space insensitive string attribute syntax plugin" };
+
+int
+sicis_init( Slapi_PBlock *pb )
+{
+ int rc, flags;
+
+ LDAPDebug( LDAP_DEBUG_PLUGIN, "=> sicis_init\n", 0, 0, 0 );
+
+ rc = slapi_pblock_set( pb, SLAPI_PLUGIN_VERSION,
+ (void *) SLAPI_PLUGIN_VERSION_01 );
+ rc |= slapi_pblock_set( pb, SLAPI_PLUGIN_DESCRIPTION,
+ (void *)&pdesc );
+ rc |= slapi_pblock_set( pb, SLAPI_PLUGIN_SYNTAX_FILTER_AVA,
+ (void *) sicis_filter_ava );
+ rc |= slapi_pblock_set( pb, SLAPI_PLUGIN_SYNTAX_FILTER_SUB,
+ (void *) sicis_filter_sub );
+ rc |= slapi_pblock_set( pb, SLAPI_PLUGIN_SYNTAX_VALUES2KEYS,
+ (void *) sicis_values2keys );
+ rc |= slapi_pblock_set( pb, SLAPI_PLUGIN_SYNTAX_ASSERTION2KEYS_AVA,
+ (void *) sicis_assertion2keys_ava );
+ rc |= slapi_pblock_set( pb, SLAPI_PLUGIN_SYNTAX_ASSERTION2KEYS_SUB,
+ (void *) sicis_assertion2keys_sub );
+ flags = SLAPI_PLUGIN_SYNTAX_FLAG_ORDERING;
+ rc |= slapi_pblock_set( pb, SLAPI_PLUGIN_SYNTAX_FLAGS,
+ (void *) &flags );
+ rc |= slapi_pblock_set( pb, SLAPI_PLUGIN_SYNTAX_NAMES,
+ (void *) names );
+ rc |= slapi_pblock_set( pb, SLAPI_PLUGIN_SYNTAX_OID,
+ (void *) SPACE_INSENSITIVE_STRING_SYNTAX_OID );
+ rc |= slapi_pblock_set( pb, SLAPI_PLUGIN_SYNTAX_COMPARE,
+ (void *) sicis_compare );
+
+ LDAPDebug( LDAP_DEBUG_PLUGIN, "<= sicis_init %d\n", rc, 0, 0 );
+ return( rc );
+}
+
+static int
+sicis_filter_ava(
+ Slapi_PBlock *pb,
+ struct berval *bvfilter,
+ Slapi_Value **bvals,
+ int ftype,
+ Slapi_Value **retVal
+)
+{
+ return( string_filter_ava( bvfilter, bvals, SYNTAX_SI | SYNTAX_CIS,
+ ftype, retVal ) );
+}
+
+
+static int
+sicis_filter_sub(
+ Slapi_PBlock *pb,
+ char *initial,
+ char **any,
+ char *final,
+ Slapi_Value **bvals
+)
+{
+ return( string_filter_sub( pb, initial, any, final, bvals, SYNTAX_SI | SYNTAX_CIS ) );
+}
+
+static int
+sicis_values2keys(
+ Slapi_PBlock *pb,
+ Slapi_Value **vals,
+ Slapi_Value ***ivals,
+ int ftype
+)
+{
+ return( string_values2keys( pb, vals, ivals, SYNTAX_SI | SYNTAX_CIS,
+ ftype ) );
+}
+
+static int
+sicis_assertion2keys_ava(
+ Slapi_PBlock *pb,
+ Slapi_Value *val,
+ Slapi_Value ***ivals,
+ int ftype
+)
+{
+ return(string_assertion2keys_ava( pb, val, ivals,
+ SYNTAX_SI | SYNTAX_CIS, ftype ));
+}
+
+static int
+sicis_assertion2keys_sub(
+ Slapi_PBlock *pb,
+ char *initial,
+ char **any,
+ char *final,
+ Slapi_Value ***ivals
+)
+{
+ return( string_assertion2keys_sub( pb, initial, any, final, ivals,
+ SYNTAX_SI | SYNTAX_CIS ) );
+}
+
+static int sicis_compare(
+ struct berval *v1,
+ struct berval *v2
+)
+{
+ return value_cmp(v1, v2, SYNTAX_SI|SYNTAX_CIS, 3 /* Normalise both values */);
+}
diff --git a/ldap/servers/plugins/syntaxes/string.c b/ldap/servers/plugins/syntaxes/string.c
new file mode 100644
index 00000000..d06a15f5
--- /dev/null
+++ b/ldap/servers/plugins/syntaxes/string.c
@@ -0,0 +1,612 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+/* string.c - common string syntax routines */
+
+#include <stdio.h>
+#include <string.h>
+#include <sys/types.h>
+#include "syntax.h"
+#if defined(IRIX)
+#include <unistd.h>
+#endif
+#if defined( MACOS ) || defined( DOS ) || defined( _WIN32 ) || defined( NEED_BSDREGEX )
+#include "regex.h"
+#endif
+
+static int string_filter_approx( struct berval *bvfilter,
+ Slapi_Value **bvals, Slapi_Value **retVal );
+static void substring_comp_keys( Slapi_Value ***ivals, int *nsubs, char *str,
+ int prepost, int syntax );
+
+int
+string_filter_ava( struct berval *bvfilter, Slapi_Value **bvals, int syntax,
+ int ftype, Slapi_Value **retVal )
+{
+ int i, rc;
+ struct berval bvfilter_norm;
+
+ if(retVal) {
+ *retVal = NULL;
+ }
+ if ( ftype == LDAP_FILTER_APPROX ) {
+ return( string_filter_approx( bvfilter, bvals, retVal ) );
+ }
+
+ bvfilter_norm.bv_val = slapi_ch_malloc( bvfilter->bv_len + 1 );
+ SAFEMEMCPY( bvfilter_norm.bv_val, bvfilter->bv_val, bvfilter->bv_len );
+ bvfilter_norm.bv_val[bvfilter->bv_len] = '\0';
+ value_normalize( bvfilter_norm.bv_val, syntax, 1 /* trim leading blanks */ );
+
+ for ( i = 0; bvals[i] != NULL; i++ ) {
+ rc = value_cmp( (struct berval*)slapi_value_get_berval(bvals[i]), &bvfilter_norm, syntax, 1/* Normalise the first value only */ );
+ switch ( ftype ) {
+ case LDAP_FILTER_GE:
+ if ( rc >= 0 ) {
+ if(retVal) {
+ *retVal = bvals[i];
+ }
+ slapi_ch_free ((void**)&bvfilter_norm.bv_val);
+ return( 0 );
+ }
+ break;
+ case LDAP_FILTER_LE:
+ if ( rc <= 0 ) {
+ if(retVal) {
+ *retVal = bvals[i];
+ }
+ slapi_ch_free ((void**)&bvfilter_norm.bv_val);
+ return( 0 );
+ }
+ break;
+ case LDAP_FILTER_EQUALITY:
+ if ( rc == 0 ) {
+ if(retVal) {
+ *retVal = bvals[i];
+ }
+ slapi_ch_free ((void**)&bvfilter_norm.bv_val);
+ return( 0 );
+ }
+ break;
+ }
+ }
+
+ slapi_ch_free ((void**)&bvfilter_norm.bv_val);
+ return( -1 );
+}
+
+static int
+string_filter_approx( struct berval *bvfilter, Slapi_Value **bvals,
+ Slapi_Value **retVal)
+{
+ int i, rc;
+ int ava_wordcount;
+ char *w1, *w2, *c1, *c2;
+
+ /*
+ * try to match words in each filter value in order
+ * in the attribute value.
+ * XXX should do this once for the filter and save it XXX
+ */
+ rc = -1;
+ if(retVal) {
+ *retVal = NULL;
+ }
+ for ( i = 0; bvals[i] != NULL; i++ ) {
+ w2 = (char*)slapi_value_get_string(bvals[i]); /* JCM cast */
+ ava_wordcount = 0;
+ /* for each word in the filter value */
+ for ( w1 = first_word( bvfilter->bv_val ); w1 != NULL;
+ w1 = next_word( w1 ) ) {
+ ++ava_wordcount;
+ if ( (c1 = phonetic( w1 )) == NULL ) {
+ break;
+ }
+
+ /*
+ * for each word in the attribute value from
+ * where we left off...
+ */
+ for ( w2 = first_word( w2 ); w2 != NULL;
+ w2 = next_word( w2 ) ) {
+ c2 = phonetic( w2 );
+ rc = strcmp( c1, c2 );
+ slapi_ch_free((void**)&c2 );
+ if ( rc == 0 ) {
+ if(retVal) {
+ *retVal = bvals[i];
+ }
+ break;
+ }
+ }
+ slapi_ch_free((void**)&c1 );
+
+ /*
+ * if we stopped because we ran out of words
+ * before making a match, go on to the next
+ * value. otherwise try to keep matching
+ * words in this value from where we left off.
+ */
+ if ( w2 == NULL ) {
+ break;
+ } else {
+ w2 = next_word( w2 );
+ }
+ }
+ /*
+ * if we stopped because we ran out of words and
+ * we found at leasy one word, we have a match.
+ */
+ if ( w1 == NULL && ava_wordcount > 0 ) {
+ rc = 0;
+ break;
+ }
+ }
+ LDAPDebug( LDAP_DEBUG_TRACE, "<= string_filter_approx %d\n",
+ rc, 0, 0 );
+
+ return( rc );
+}
+
+int
+string_filter_sub( Slapi_PBlock *pb, char *initial, char **any, char *final,
+ Slapi_Value **bvals, int syntax )
+{
+ int i, j, rc;
+ char *p, *end, *realval, *tmpbuf;
+ size_t tmpbufsize;
+ char pat[BUFSIZ];
+ char buf[BUFSIZ];
+ char ebuf[BUFSIZ];
+
+ LDAPDebug( LDAP_DEBUG_FILTER, "=> string_filter_sub\n",
+ 0, 0, 0 );
+
+ /*
+ * construct a regular expression corresponding to the
+ * filter and let regex do the work for each value
+ * XXX should do this once and save it somewhere XXX
+ */
+ pat[0] = '\0';
+ p = pat;
+ end = pat + sizeof(pat) - 2; /* leave room for null */
+ if ( initial != NULL ) {
+ value_normalize( initial, syntax, 1 /* trim leading blanks */ );
+ strcpy( p, "^" );
+ p = strchr( p, '\0' );
+ /* 2 * in case every char is special */
+ if ( p + 2 * strlen( initial ) > end ) {
+ LDAPDebug( LDAP_DEBUG_ANY, "not enough pattern space\n",
+ 0, 0, 0 );
+ return( -1 );
+ }
+ filter_strcpy_special( p, initial );
+ p = strchr( p, '\0' );
+ }
+ if ( any != NULL ) {
+ for ( i = 0; any[i] != NULL; i++ ) {
+ value_normalize( any[i], syntax, 0 /* DO NOT trim leading blanks */ );
+ /* ".*" + value */
+ if ( p + 2 * strlen( any[i] ) + 2 > end ) {
+ LDAPDebug( LDAP_DEBUG_ANY,
+ "not enough pattern space\n", 0, 0, 0 );
+ return( -1 );
+ }
+ strcpy( p, ".*" );
+ p = strchr( p, '\0' );
+ filter_strcpy_special( p, any[i] );
+ p = strchr( p, '\0' );
+ }
+ }
+ if ( final != NULL ) {
+ value_normalize( final, syntax, 0 /* DO NOT trim leading blanks */ );
+ /* ".*" + value */
+ if ( p + 2 * strlen( final ) + 2 > end ) {
+ LDAPDebug( LDAP_DEBUG_ANY, "not enough pattern space\n",
+ 0, 0, 0 );
+ return( -1 );
+ }
+ strcpy( p, ".*" );
+ p = strchr( p, '\0' );
+ filter_strcpy_special( p, final );
+ p = strchr( p, '\0' );
+ strcpy( p, "$" );
+ }
+
+ /* compile the regex */
+ slapd_re_lock();
+ if ( (p = slapd_re_comp( pat )) != 0 ) {
+ LDAPDebug( LDAP_DEBUG_ANY, "re_comp (%s) failed (%s)\n",
+ pat, p, 0 );
+ slapd_re_unlock();
+ return( -1 );
+ } else {
+ LDAPDebug( LDAP_DEBUG_TRACE, "re_comp (%s)\n",
+ escape_string( pat, ebuf ), 0, 0 );
+ }
+
+ /*
+ * test the regex against each value
+ */
+ rc = -1;
+ tmpbuf = NULL;
+ tmpbufsize = 0;
+ for ( j = 0; bvals[j] != NULL; j++ ) {
+ int tmprc;
+ size_t len;
+ const struct berval *bvp = slapi_value_get_berval(bvals[j]);
+
+ len = bvp->bv_len;
+ if ( len < sizeof(buf) ) {
+ strcpy( buf, bvp->bv_val );
+ realval = buf;
+ } else if ( len < tmpbufsize ) {
+ strcpy( buf, bvp->bv_val );
+ realval = tmpbuf;
+ } else {
+ tmpbuf = (char *) slapi_ch_realloc( tmpbuf, len + 1 );
+ strcpy( tmpbuf, bvp->bv_val );
+ realval = tmpbuf;
+ }
+ value_normalize( realval, syntax, 1 /* trim leading blanks */ );
+
+ tmprc = slapd_re_exec( realval );
+
+ LDAPDebug( LDAP_DEBUG_TRACE, "re_exec (%s) %i\n",
+ escape_string( realval, ebuf ), tmprc, 0 );
+ if ( tmprc != 0 ) {
+ rc = 0;
+ break;
+ }
+ }
+ slapd_re_unlock();
+ if ( tmpbuf != NULL ) {
+ slapi_ch_free((void**)&tmpbuf );
+ }
+
+ LDAPDebug( LDAP_DEBUG_FILTER, "<= string_filter_sub %d\n",
+ rc, 0, 0 );
+ return( rc );
+}
+
+int
+string_values2keys( Slapi_PBlock *pb, Slapi_Value **bvals,
+ Slapi_Value ***ivals, int syntax, int ftype )
+{
+ int nsubs, numbvals, i, n, j;
+ Slapi_Value **nbvals;
+ char *w, *c, *p;
+ char buf[SUBLEN+1];
+
+ switch ( ftype ) {
+ case LDAP_FILTER_EQUALITY:
+ /* allocate a new array for the normalized values */
+ for ( numbvals = 0; bvals[numbvals] != NULL; numbvals++ ) {
+ /* NULL */
+ }
+ nbvals = (Slapi_Value **) slapi_ch_malloc( (numbvals+1) * sizeof(Slapi_Value *));
+
+ for ( i = 0; i < numbvals; i++ )
+ {
+ c = slapi_ch_strdup(slapi_value_get_string(bvals[i]));
+ value_normalize( c, syntax, 1 /* trim leading blanks */ );
+ nbvals[i] = slapi_value_new_string_passin(c);
+ }
+ nbvals[i] = NULL;
+ *ivals = nbvals;
+ break;
+
+ case LDAP_FILTER_APPROX:
+ /* XXX should not do this twice! XXX */
+ /* get an upper bound on the number of ivals */
+ numbvals = 0;
+ for ( i = 0; bvals[i] != NULL; i++ ) {
+ for ( w = first_word( (char*)slapi_value_get_string(bvals[i]) ); w != NULL;
+ w = next_word( w ) ) {
+ numbvals++;
+ }
+ }
+ nbvals = (Slapi_Value **) slapi_ch_malloc( (numbvals + 1) * sizeof(Slapi_Value *) );
+
+ n = 0;
+ for ( i = 0; bvals[i] != NULL; i++ ) {
+ for ( w = first_word( (char*)slapi_value_get_string(bvals[i]) ); w != NULL;
+ w = next_word( w ) ) {
+ if ( (c = phonetic( w )) != NULL ) {
+ nbvals[n] = slapi_value_new_string_passin(c);
+ n++;
+ }
+ }
+ }
+ nbvals[n] = NULL;
+
+ if ( n == 0 ) {
+ slapi_ch_free((void**)ivals );
+ return( 0 );
+ }
+ *ivals = nbvals;
+ break;
+
+ case LDAP_FILTER_SUBSTRINGS:
+ {
+ /* XXX should remove duplicates! XXX */
+ Slapi_Value *bvdup;
+ const struct berval *bvp;
+ nsubs = 0;
+ for ( i = 0; bvals[i] != NULL; i++ ) {
+ /*
+ * Note: this calculation may err on the high side,
+ * because value_normalize(), which is called below
+ * before we actually create the substring keys, may
+ * reduce the length of the value in some cases. For
+ * example, spaces are removed when space insensitive
+ * strings are normalized. But it's okay for nsubs to
+ * be too big. Since the ivals array is NULL terminated,
+ * the only downside is that we allocate more space than
+ * we really need.
+ */
+ nsubs += slapi_value_get_length(bvals[i]) - SUBLEN + 3;
+ }
+ *ivals = (Slapi_Value **) slapi_ch_malloc( (nsubs + 1) * sizeof(Slapi_Value *) );
+
+ buf[SUBLEN] = '\0';
+ n = 0;
+
+ bvdup= slapi_value_new();
+ for ( i = 0; bvals[i] != NULL; i++ )
+ {
+ c = slapi_ch_strdup(slapi_value_get_string(bvals[i]));
+ value_normalize( c, syntax, 1 /* trim leading blanks */ );
+ slapi_value_set_string_passin(bvdup, c);
+
+ bvp = slapi_value_get_berval(bvdup);
+
+ /* leading */
+ if ( bvp->bv_len > SUBLEN - 2 ) {
+ buf[0] = '^';
+ for ( j = 0; j < SUBLEN - 1; j++ ) {
+ buf[j + 1] = bvp->bv_val[j];
+ }
+ (*ivals)[n] = slapi_value_new_string(buf);
+ n++;
+ }
+
+ /* any */
+ for ( p = bvp->bv_val;
+ p < (bvp->bv_val + bvp->bv_len - SUBLEN + 1);
+ p++ ) {
+ for ( j = 0; j < SUBLEN; j++ ) {
+ buf[j] = p[j];
+ }
+ buf[SUBLEN] = '\0';
+ (*ivals)[n] = slapi_value_new_string(buf);
+ n++;
+ }
+
+ /* trailing */
+ if ( bvp->bv_len > SUBLEN - 2 ) {
+ p = bvp->bv_val + bvp->bv_len - SUBLEN + 1;
+ for ( j = 0; j < SUBLEN - 1; j++ ) {
+ buf[j] = p[j];
+ }
+ buf[SUBLEN - 1] = '$';
+ (*ivals)[n] = slapi_value_new_string(buf);
+ n++;
+ }
+ }
+ slapi_value_free(&bvdup);
+ (*ivals)[n] = NULL;
+ }
+ break;
+ }
+
+ return( 0 );
+}
+
+
+/* we've added code to make our equality filter processing faster */
+
+int
+string_assertion2keys_ava(
+ Slapi_PBlock *pb,
+ Slapi_Value *val,
+ Slapi_Value ***ivals,
+ int syntax,
+ int ftype
+)
+{
+ int i, numbvals;
+ size_t len;
+ char *w, *c;
+ Slapi_Value *tmpval=NULL;
+
+ switch ( ftype ) {
+ case LDAP_FILTER_EQUALITY_FAST:
+ /* this code is trying to avoid multiple malloc/frees */
+ len=slapi_value_get_length(val);
+ tmpval=(*ivals)[0];
+ if (len >= tmpval->bv.bv_len) {
+ tmpval->bv.bv_val=(char *)slapi_ch_malloc(len+1);
+ }
+ memcpy(tmpval->bv.bv_val,slapi_value_get_string(val),len);
+ tmpval->bv.bv_val[len]='\0';
+ value_normalize(tmpval->bv.bv_val, syntax, 1 /* trim leading blanks */ );
+ tmpval->bv.bv_len=strlen(tmpval->bv.bv_val);
+ break;
+ case LDAP_FILTER_EQUALITY:
+ (*ivals) = (Slapi_Value **) slapi_ch_malloc( 2 * sizeof(Slapi_Value *) );
+ (*ivals)[0] = slapi_value_dup( val );
+ value_normalize( (*ivals)[0]->bv.bv_val, syntax, 1 /* trim leading blanks */ );
+ (*ivals)[0]->bv.bv_len = strlen( (*ivals)[0]->bv.bv_val );
+ (*ivals)[1] = NULL;
+ break;
+
+ case LDAP_FILTER_APPROX:
+ /* XXX should not do this twice! XXX */
+ /* get an upper bound on the number of ivals */
+ numbvals = 0;
+ for ( w = first_word( (char*)slapi_value_get_string(val) ); w != NULL;
+ w = next_word( w ) ) {
+ numbvals++;
+ }
+ (*ivals) = (Slapi_Value **) slapi_ch_malloc( (numbvals + 1) *
+ sizeof(Slapi_Value *) );
+
+ i = 0;
+ for ( w = first_word( (char*)slapi_value_get_string(val) ); w != NULL;
+ w = next_word( w ) ) {
+ if ( (c = phonetic( w )) != NULL ) {
+ (*ivals)[i] = slapi_value_new_string_passin(c);
+ i++;
+ }
+ }
+ (*ivals)[i] = NULL;
+
+ if ( i == 0 ) {
+ slapi_ch_free((void**)ivals );
+ return( 0 );
+ }
+ break;
+ default:
+ LDAPDebug( LDAP_DEBUG_ANY,
+ "string_assertion2keys_ava: unknown ftype 0x%x\n",
+ ftype, 0, 0 );
+ break;
+ }
+
+ return( 0 );
+}
+
+int
+string_assertion2keys_sub(
+ Slapi_PBlock *pb,
+ char *initial,
+ char **any,
+ char *final,
+ Slapi_Value ***ivals,
+ int syntax
+)
+{
+ int nsubs, i, len;
+
+ *ivals = NULL;
+
+ /*
+ * First figure out how many keys we will return. The answer is based
+ * on the length of each assertion value. Since normalization may
+ * reduce the length (such as when spaces are removed from space
+ * insensitive strings), we call value_normalize() before checking
+ * the length.
+ */
+ nsubs = 0;
+ if ( initial != NULL ) {
+ value_normalize( initial, syntax, 0 /* do not trim leading blanks */ );
+ if ( strlen( initial ) > SUBLEN - 2 ) {
+ nsubs += strlen( initial ) - SUBLEN + 2;
+ } else {
+ initial = NULL; /* save some work later */
+ }
+ }
+ for ( i = 0; any != NULL && any[i] != NULL; i++ ) {
+ value_normalize( any[i], syntax, 0 /* do not trim leading blanks */ );
+ len = strlen( any[i] );
+ if ( len >= SUBLEN ) {
+ nsubs += len - SUBLEN + 1;
+ }
+ }
+ if ( final != NULL ) {
+ value_normalize( final, syntax, 0 /* do not trim leading blanks */ );
+ if ( strlen( final ) > SUBLEN - 2 ) {
+ nsubs += strlen( final ) - SUBLEN + 2;
+ } else {
+ final = NULL; /* save some work later */
+ }
+ }
+ if ( nsubs == 0 ) { /* no keys to return */
+ return( 0 );
+ }
+
+ /*
+ * Next, allocated the ivals array and fill it in with the actual
+ * keys. *ivals is a NULL terminated array of Slapi_Value pointers.
+ */
+
+ *ivals = (Slapi_Value **) slapi_ch_malloc( (nsubs + 1) * sizeof(Slapi_Value *) );
+
+ nsubs = 0;
+ if ( initial != NULL ) {
+ substring_comp_keys( ivals, &nsubs, initial, '^', syntax );
+ }
+ for ( i = 0; any != NULL && any[i] != NULL; i++ ) {
+ if ( strlen( any[i] ) < SUBLEN ) {
+ continue;
+ }
+ substring_comp_keys( ivals, &nsubs, any[i], 0, syntax );
+ }
+ if ( final != NULL ) {
+ substring_comp_keys( ivals, &nsubs, final, '$', syntax );
+ }
+ (*ivals)[nsubs] = NULL;
+
+ return( 0 );
+}
+
+static void
+substring_comp_keys(
+ Slapi_Value ***ivals,
+ int *nsubs,
+ char *str,
+ int prepost,
+ int syntax
+)
+{
+ int i, len;
+ char *p;
+ char buf[SUBLEN + 1];
+
+ LDAPDebug( LDAP_DEBUG_TRACE, "=> substring_comp_keys (%s) %d\n",
+ str, prepost, 0 );
+
+ len = strlen( str );
+
+ /* prepend ^ for initial substring */
+ if ( prepost == '^' )
+ {
+ buf[0] = '^';
+ for ( i = 0; i < SUBLEN - 1; i++ )
+ {
+ buf[i + 1] = str[i];
+ }
+ buf[SUBLEN] = '\0';
+ (*ivals)[*nsubs] = slapi_value_new_string(buf);
+ (*nsubs)++;
+ }
+
+ for ( p = str; p < (str + len - SUBLEN + 1); p++ )
+ {
+ for ( i = 0; i < SUBLEN; i++ )
+ {
+ buf[i] = p[i];
+ }
+ buf[SUBLEN] = '\0';
+ (*ivals)[*nsubs] = slapi_value_new_string(buf);
+ (*nsubs)++;
+ }
+
+ if ( prepost == '$' )
+ {
+ p = str + len - SUBLEN + 1;
+ for ( i = 0; i < SUBLEN - 1; i++ )
+ {
+ buf[i] = p[i];
+ }
+ buf[SUBLEN - 1] = '$';
+ buf[SUBLEN] = '\0';
+ (*ivals)[*nsubs] = slapi_value_new_string(buf);
+ (*nsubs)++;
+ }
+
+ LDAPDebug( LDAP_DEBUG_TRACE, "<= substring_comp_keys\n", 0, 0, 0 );
+}
diff --git a/ldap/servers/plugins/syntaxes/syntax.h b/ldap/servers/plugins/syntaxes/syntax.h
new file mode 100644
index 00000000..d6d883c9
--- /dev/null
+++ b/ldap/servers/plugins/syntaxes/syntax.h
@@ -0,0 +1,42 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+/* syntax.h - string syntax definitions */
+
+#ifndef _LIBSYNTAX_H_
+#define _LIBSYNTAX_H_
+
+#define SLAPD_LOGGING 1
+
+#include "slap.h"
+#include "slapi-plugin.h"
+#include <dirlite_strings.h> /* PLUGIN_MAGIC_VENDOR_STR */
+
+#define SYNTAX_CIS 1
+#define SYNTAX_CES 2
+#define SYNTAX_TEL 4 /* telephone number: used with SYNTAX_CIS */
+#define SYNTAX_DN 8 /* distinguished name: used with SYNTAX_CIS */
+#define SYNTAX_SI 16 /* space insensitive: used with SYNTAX_CIS */
+
+#define SUBLEN 3
+
+#ifndef MIN
+#define MIN( a, b ) (a < b ? a : b )
+#endif
+
+int string_filter_sub( Slapi_PBlock *pb, char *initial, char **any, char *final,Slapi_Value **bvals, int syntax );
+int string_filter_ava( struct berval *bvfilter, Slapi_Value **bvals, int syntax,int ftype, Slapi_Value **retVal );
+int string_values2keys( Slapi_PBlock *pb, Slapi_Value **bvals,Slapi_Value ***ivals, int syntax, int ftype );
+int string_assertion2keys_ava(Slapi_PBlock *pb,Slapi_Value *val,Slapi_Value ***ivals,int syntax,int ftype );
+int string_assertion2keys_sub(Slapi_PBlock *pb,char *initial,char **any,char *final,Slapi_Value ***ivals,int syntax);
+int value_cmp(struct berval *v1,struct berval *v2,int syntax,int normalize);
+void value_normalize(char *s,int syntax,int trim_leading_blanks);
+
+char *first_word( char *s );
+char *next_word( char *s );
+char *phonetic( char *s );
+
+
+#endif
diff --git a/ldap/servers/plugins/syntaxes/tel.c b/ldap/servers/plugins/syntaxes/tel.c
new file mode 100644
index 00000000..c2c80d1a
--- /dev/null
+++ b/ldap/servers/plugins/syntaxes/tel.c
@@ -0,0 +1,135 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+/* tel.c - telephonenumber syntax routines */
+
+#include <stdio.h>
+#include <string.h>
+#include <sys/types.h>
+#include "syntax.h"
+
+static int tel_filter_ava( Slapi_PBlock *pb, struct berval *bvfilter,
+ Slapi_Value **bvals, int ftype, Slapi_Value **retVal );
+static int tel_filter_sub( Slapi_PBlock *pb, char *initial, char **any,
+ char *final, Slapi_Value **bvals );
+static int tel_values2keys( Slapi_PBlock *pb, Slapi_Value **val,
+ Slapi_Value ***ivals, int ftype );
+static int tel_assertion2keys_ava( Slapi_PBlock *pb, Slapi_Value *val,
+ Slapi_Value ***ivals, int ftype );
+static int tel_assertion2keys_sub( Slapi_PBlock *pb, char *initial, char **any,
+ char *final, Slapi_Value ***ivals );
+static int tel_compare(struct berval *v1, struct berval *v2);
+
+/* the first name is the official one from RFC 2252 */
+static char *names[] = { "TelephoneNumber", "tel", TELEPHONE_SYNTAX_OID, 0 };
+
+static Slapi_PluginDesc pdesc = { "tele-syntax", PLUGIN_MAGIC_VENDOR_STR, PRODUCTTEXT,
+ "telephoneNumber attribute syntax plugin" };
+
+int
+tel_init( Slapi_PBlock *pb )
+{
+ int rc, flags;
+
+ LDAPDebug( LDAP_DEBUG_PLUGIN, "=> tel_init\n", 0, 0, 0 );
+
+ rc = slapi_pblock_set( pb, SLAPI_PLUGIN_VERSION,
+ (void *) SLAPI_PLUGIN_VERSION_01 );
+ rc |= slapi_pblock_set( pb, SLAPI_PLUGIN_DESCRIPTION,
+ (void *)&pdesc );
+ rc |= slapi_pblock_set( pb, SLAPI_PLUGIN_SYNTAX_FILTER_AVA,
+ (void *) tel_filter_ava );
+ rc |= slapi_pblock_set( pb, SLAPI_PLUGIN_SYNTAX_FILTER_SUB,
+ (void *) tel_filter_sub );
+ rc |= slapi_pblock_set( pb, SLAPI_PLUGIN_SYNTAX_VALUES2KEYS,
+ (void *) tel_values2keys );
+ rc |= slapi_pblock_set( pb, SLAPI_PLUGIN_SYNTAX_ASSERTION2KEYS_AVA,
+ (void *) tel_assertion2keys_ava );
+ rc |= slapi_pblock_set( pb, SLAPI_PLUGIN_SYNTAX_ASSERTION2KEYS_SUB,
+ (void *) tel_assertion2keys_sub );
+ flags = SLAPI_PLUGIN_SYNTAX_FLAG_ORDERING;
+ rc |= slapi_pblock_set( pb, SLAPI_PLUGIN_SYNTAX_FLAGS,
+ (void *) &flags );
+ rc |= slapi_pblock_set( pb, SLAPI_PLUGIN_SYNTAX_NAMES,
+ (void *) names );
+ rc |= slapi_pblock_set( pb, SLAPI_PLUGIN_SYNTAX_OID,
+ (void *) TELEPHONE_SYNTAX_OID );
+ rc |= slapi_pblock_set( pb, SLAPI_PLUGIN_SYNTAX_COMPARE,
+ (void *) tel_compare );
+
+ LDAPDebug( LDAP_DEBUG_PLUGIN, "<= tel_init %d\n", rc, 0, 0 );
+ return( rc );
+}
+
+static int
+tel_filter_ava(
+ Slapi_PBlock *pb,
+ struct berval *bvfilter,
+ Slapi_Value **bvals,
+ int ftype,
+ Slapi_Value **retVal
+)
+{
+ return( string_filter_ava( bvfilter, bvals, SYNTAX_TEL | SYNTAX_CIS,
+ ftype, retVal ) );
+}
+
+
+static int
+tel_filter_sub(
+ Slapi_PBlock *pb,
+ char *initial,
+ char **any,
+ char *final,
+ Slapi_Value **bvals
+)
+{
+ return( string_filter_sub( pb, initial, any, final, bvals, SYNTAX_TEL | SYNTAX_CIS ) );
+}
+
+static int
+tel_values2keys(
+ Slapi_PBlock *pb,
+ Slapi_Value **vals,
+ Slapi_Value ***ivals,
+ int ftype
+)
+{
+ return( string_values2keys( pb, vals, ivals, SYNTAX_TEL | SYNTAX_CIS,
+ ftype ) );
+}
+
+static int
+tel_assertion2keys_ava(
+ Slapi_PBlock *pb,
+ Slapi_Value *val,
+ Slapi_Value ***ivals,
+ int ftype
+)
+{
+ return(string_assertion2keys_ava( pb, val, ivals,
+ SYNTAX_TEL | SYNTAX_CIS, ftype ));
+}
+
+static int
+tel_assertion2keys_sub(
+ Slapi_PBlock *pb,
+ char *initial,
+ char **any,
+ char *final,
+ Slapi_Value ***ivals
+)
+{
+ return( string_assertion2keys_sub( pb, initial, any, final, ivals,
+ SYNTAX_TEL | SYNTAX_CIS ) );
+}
+
+static int tel_compare(
+ struct berval *v1,
+ struct berval *v2
+)
+{
+ return value_cmp(v1, v2, SYNTAX_TEL|SYNTAX_CIS, 3 /* Normalise both values */);
+}
diff --git a/ldap/servers/plugins/syntaxes/value.c b/ldap/servers/plugins/syntaxes/value.c
new file mode 100644
index 00000000..be496091
--- /dev/null
+++ b/ldap/servers/plugins/syntaxes/value.c
@@ -0,0 +1,209 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+/* value.c - routines for dealing with values */
+
+#include <stdio.h>
+#include <string.h>
+#include <sys/types.h>
+#include "syntax.h"
+
+/*
+ * Do not use the SDK ldap_utf8isspace directly until it is faster
+ * than this one.
+ */
+static int
+utf8isspace_fast( char* s )
+{
+ register unsigned char c = *(unsigned char*)s;
+ if (0x80 & c) return(ldap_utf8isspace(s));
+ switch (c) {
+ case 0x09:
+ case 0x0A:
+ case 0x0B:
+ case 0x0C:
+ case 0x0D:
+ case 0x20:
+ return 1;
+ default: break;
+ }
+ return 0;
+}
+
+/*
+** This function is used to normalizes search filter components,
+** and attribute values.
+**
+** jcm: I added the trim_spaces flag since this function
+** was incorrectly modifying search filter components. A search
+** of the form "cn=a* b*" (note the space) would be wrongly
+** normalized into "cn=a*b*", because this function is called
+** once for "a" and once for " b".
+*/
+void
+value_normalize(
+ char *s,
+ int syntax,
+ int trim_spaces
+)
+{
+ char *d;
+ int prevspace, curspace;
+
+ if ( ! (syntax & SYNTAX_CIS) && ! (syntax & SYNTAX_CES) ) {
+ return;
+ }
+
+ if ( syntax & SYNTAX_DN ) {
+ (void) slapi_dn_normalize_case( s );
+ return;
+ }
+
+ d = s;
+ if (trim_spaces) {
+ /* strip leading blanks */
+ while (utf8isspace_fast(s)) {
+ LDAP_UTF8INC(s);
+ }
+ }
+ /* handle value of all spaces - turn into single space */
+ /* unless space insensitive syntax - turn into zero length string */
+ if ( *s == '\0' && s != d ) {
+ if ( ! (syntax & SYNTAX_SI)) {
+ *d++ = ' ';
+ }
+ *d = '\0';
+ return;
+ }
+ prevspace = 0;
+ while ( *s ) {
+ curspace = utf8isspace_fast(s);
+
+ /* ignore spaces and '-' in telephone numbers */
+ if ( (syntax & SYNTAX_TEL) && (curspace || *s == '-') ) {
+ LDAP_UTF8INC(s);
+ continue;
+ }
+
+ /* ignore all spaces if this is a space insensitive value */
+ if ( (syntax & SYNTAX_SI) && curspace ) {
+ LDAP_UTF8INC(s);
+ continue;
+ }
+
+ /* compress multiple blanks */
+ if ( prevspace && curspace ) {
+ LDAP_UTF8INC(s);
+ continue;
+ }
+ prevspace = curspace;
+ if ( syntax & SYNTAX_CIS ) {
+ int ssz, dsz;
+ slapi_utf8ToLower((unsigned char*)s, (unsigned char *)d, &ssz, &dsz);
+ s += ssz;
+ d += dsz;
+ } else {
+ char *np;
+ int sz;
+
+ np = ldap_utf8next(s);
+ if (np == NULL || np == s) break;
+ sz = np - s;
+ memcpy(d,s,sz);
+ d += sz;
+ s += sz;
+ }
+ }
+ *d = '\0';
+ /* strip trailing blanks */
+ if (prevspace && trim_spaces) {
+ char *nd;
+
+ nd = ldap_utf8prev(d);
+ while (nd && utf8isspace_fast(nd)) {
+ d = nd;
+ nd = ldap_utf8prev(d);
+ *d = '\0';
+ }
+ }
+}
+
+int
+value_cmp(
+ struct berval *v1,
+ struct berval *v2,
+ int syntax,
+ int normalize
+)
+{
+ int rc;
+ struct berval bvcopy1;
+ struct berval bvcopy2;
+ char little_buffer[64];
+ size_t buffer_space = sizeof(little_buffer);
+ int buffer_offset = 0;
+ int free_v1 = 0;
+ int free_v2 = 0;
+
+ /* This code used to call malloc up to four times in the copying
+ * of attributes to be normalized. Now we attempt to keep everything
+ * on the stack and only malloc if the data is big
+ */
+ if ( normalize & 1 ) {
+ /* Do we have space in the little buffer ? */
+ if (v1->bv_len < buffer_space) {
+ bvcopy1.bv_len = v1->bv_len;
+ SAFEMEMCPY(&little_buffer[buffer_offset],v1->bv_val,v1->bv_len);
+ bvcopy1.bv_val = &little_buffer[buffer_offset];
+ bvcopy1.bv_val[v1->bv_len] = '\0';
+ v1 = &bvcopy1;
+ buffer_space-= v1->bv_len+1;
+ buffer_offset+= v1->bv_len+1;
+ } else {
+ v1 = ber_bvdup( v1 );
+ free_v1 = 1;
+ }
+ value_normalize( v1->bv_val, syntax, 1 /* trim leading blanks */ );
+ }
+ if ( normalize & 2 ) {
+ /* Do we have space in the little buffer ? */
+ if (v2->bv_len < buffer_space) {
+ bvcopy2.bv_len = v2->bv_len;
+ SAFEMEMCPY(&little_buffer[buffer_offset],v2->bv_val,v2->bv_len);
+ bvcopy2.bv_val = &little_buffer[buffer_offset];
+ bvcopy2.bv_val[v2->bv_len] = '\0';
+ v2 = &bvcopy2;
+ buffer_space-= v2->bv_len+1;
+ buffer_offset+= v2->bv_len+1;
+ } else {
+ v2 = ber_bvdup( v2 );
+ free_v2 = 1;
+ }
+ value_normalize( v2->bv_val, syntax, 1 /* trim leading blanks */ );
+ }
+
+ switch ( syntax ) {
+ case SYNTAX_CIS:
+ case (SYNTAX_CIS | SYNTAX_TEL):
+ case (SYNTAX_CIS | SYNTAX_DN):
+ case (SYNTAX_CIS | SYNTAX_SI):
+ rc = slapi_utf8casecmp( (unsigned char *)v1->bv_val,
+ (unsigned char *)v2->bv_val );
+ break;
+
+ case SYNTAX_CES:
+ rc = strcmp( v1->bv_val, v2->bv_val );
+ break;
+ }
+
+ if ( (normalize & 1) && free_v1) {
+ ber_bvfree( v1 );
+ }
+ if ( (normalize & 2) && free_v2) {
+ ber_bvfree( v2 );
+ }
+
+ return( rc );
+}
diff --git a/ldap/servers/plugins/uiduniq/7bit.c b/ldap/servers/plugins/uiduniq/7bit.c
new file mode 100644
index 00000000..535dbe9c
--- /dev/null
+++ b/ldap/servers/plugins/uiduniq/7bit.c
@@ -0,0 +1,722 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+/*
+ * 7bit.c
+ *
+ * Implements a directory server pre-operation plugin to test
+ * attributes for 7 bit clean within a defined subtree in the
+ * directory.
+ *
+ */
+#include <stdio.h>
+#include <slapi-plugin.h>
+#include <dirlite_strings.h> /* PLUGIN_MAGIC_VENDOR_STR */
+#include <string.h>
+#include "dirver.h"
+
+/* DBDB this should be pulled from a common header file */
+#ifdef _WIN32
+#ifndef strcasecmp
+#define strcasecmp(x,y) strcmpi(x,y)
+#endif
+#endif
+
+#if defined( LDAP_DEBUG ) && !defined( DEBUG )
+#define DEBUG
+#endif
+
+/*
+ * ISSUES:
+ * How should this plugin handle ACL issues? It seems wrong to reject
+ * adds and modifies because there is already a conflicting UID, when
+ * the request would have failed because of an ACL check anyway.
+ *
+ * This code currently defines a maximum filter string size of 512. Is
+ * this large enough?
+ *
+ * This code currently does not quote the value portion of the filter as
+ * it is created. This is a bug.
+ */
+
+/* */
+#define BEGIN do {
+#define END } while(0);
+
+/*
+ * Slapi plugin descriptor
+ */
+static char *plugin_name = "NS7bitAttr";
+static Slapi_PluginDesc
+pluginDesc = { "NS7bitAttr", PLUGIN_MAGIC_VENDOR_STR, PRODUCTTEXT,
+ "Enforce 7-bit clean attribute values" };
+
+
+/*
+ * More information about constraint failure
+ */
+static char *moreInfo =
+ "The value is not 7-bit clean: ";
+
+/* ------------------------------------------------------------ */
+/*
+ * op_error - Record (and report) an operational error.
+ */
+static int
+op_error(int internal_error)
+{
+ slapi_log_error(SLAPI_LOG_PLUGIN, plugin_name,
+ "Internal error: %d\n", internal_error);
+
+ return LDAP_OPERATIONS_ERROR;
+}
+
+static void
+issue_error(Slapi_PBlock *pb, int result, char *type, char *value)
+{
+ char *moreinfop;
+ int sz;
+
+ slapi_log_error(SLAPI_LOG_PLUGIN, plugin_name,
+ "%s result %d\n", type, result);
+
+ if (value == NULL) {
+ value = "unknown";
+ }
+ sz = strlen(moreInfo) + strlen(value) + 1;
+ moreinfop = (char *)slapi_ch_malloc(sz);
+ sprintf(moreinfop, "%s%s", moreInfo, value);
+
+ /* Send failure to the client */
+ slapi_send_ldap_result(pb, result, 0, moreinfop, 0, 0);
+ slapi_ch_free((void **)&moreinfop);
+
+ return;
+}
+
+
+/*
+ * Check 'value' for 7-bit cleanliness.
+ */
+static int
+bit_check_one_berval(const struct berval *value, char **violated)
+{
+ int result;
+ char *ch;
+ int i;
+
+#ifdef DEBUG
+ slapi_log_error(SLAPI_LOG_PLUGIN, plugin_name, "7-bit checking begin\n");
+#endif
+
+ result = LDAP_SUCCESS;
+ /* If no value, can't possibly be a conflict */
+ if ( (struct berval *)NULL == value )
+ return result;
+
+ for(i=0, ch=value->bv_val; ch && i < (int)(value->bv_len) ;
+ ch++, i++)
+ {
+
+ if (( 0x80 & *ch ) != 0 )
+ {
+ result = LDAP_CONSTRAINT_VIOLATION;
+ *violated = value->bv_val;
+ break;
+ }
+ }
+
+ return result;
+}
+
+
+/*
+ * Check a set of values for 7-bit cleanliness.
+ *
+ * If 'attr' is NULL, the values are taken from 'values'.
+ * If 'attr' is non-NULL, the values are taken from 'attr'.
+ */
+static int
+bit_check(Slapi_Attr *attr, struct berval **values, char **violated)
+{
+ int result = LDAP_SUCCESS;
+ *violated = NULL;
+
+ /* If no values, can't possibly be a conflict */
+ if ( (Slapi_Attr *)NULL == attr && (struct berval **)NULL == values )
+ return result;
+
+ if ( (Slapi_Attr *)NULL != attr )
+ {
+ Slapi_Value *v = NULL;
+ int vhint = -1;
+
+ for ( vhint = slapi_attr_first_value( attr, &v );
+ vhint != -1 && LDAP_SUCCESS == result;
+ vhint = slapi_attr_next_value( attr, vhint, &v ))
+ {
+ result = bit_check_one_berval(slapi_value_get_berval(v), violated);
+ }
+ }
+ else
+ {
+ for (;*values != NULL && LDAP_SUCCESS == result; values++)
+ {
+ result = bit_check_one_berval(*values, violated);
+ }
+ }
+
+#ifdef DEBUG
+ slapi_log_error(SLAPI_LOG_PLUGIN, plugin_name,
+ "7 bit check result = %d\n", result);
+#endif
+
+ return result;
+}
+
+
+/* ------------------------------------------------------------ */
+/*
+ * preop_add - pre-operation plug-in for add
+ */
+static int
+preop_add(Slapi_PBlock *pb)
+{
+ int result;
+ char *violated;
+
+#ifdef DEBUG
+ slapi_log_error(SLAPI_LOG_PLUGIN, plugin_name, "ADD begin\n");
+#endif
+
+ result = LDAP_SUCCESS;
+
+ /*
+ * Do constraint check on the added entry. Set result.
+ */
+ BEGIN
+ int err;
+ int argc;
+ char **argv;
+ char **attrName;
+ char *dn;
+ Slapi_Entry *e;
+ Slapi_Attr *attr;
+ char **firstSubtree;
+ char **subtreeDN;
+ int subtreeCnt;
+ int is_replicated_operation;
+
+ /*
+ * Get the arguments
+ */
+ err = slapi_pblock_get(pb, SLAPI_PLUGIN_ARGC, &argc);
+ if (err) { result = op_error(53); break; }
+
+ err = slapi_pblock_get(pb, SLAPI_PLUGIN_ARGV, &argv);
+ if (err) { result = op_error(54); break; }
+
+ /*
+ * If this is a replication update, just be a noop.
+ */
+ err = slapi_pblock_get(pb, SLAPI_IS_REPLICATED_OPERATION, &is_replicated_operation);
+ if (err) { result = op_error(56); break; }
+ if (is_replicated_operation)
+ {
+ break;
+ }
+
+ /*
+ * Get the target DN for this add operation
+ */
+ err = slapi_pblock_get(pb, SLAPI_ADD_TARGET, &dn);
+ if (err) { result = op_error(50); break; }
+
+#ifdef DEBUG
+ slapi_log_error(SLAPI_LOG_PLUGIN, plugin_name, "ADD target=%s\n", dn);
+#endif
+
+ /*
+ * Get the entry data for this add. Check whether it
+ * contains a value for the unique attribute
+ */
+ err = slapi_pblock_get(pb, SLAPI_ADD_ENTRY, &e);
+ if (err) { result = op_error(51); break; }
+
+ for ( firstSubtree = argv; strcmp(*firstSubtree, ",") != 0;
+ firstSubtree++, argc--) {}
+ firstSubtree++;
+ argc--;
+
+ for (attrName = argv; strcmp(*attrName, ",") != 0; attrName++ )
+ {
+ /*
+ * if the attribute is userpassword, check unhashed#user#password
+ * instead. "userpassword" is encoded; it will always pass the 7bit
+ * check.
+ */
+ char *attr_name;
+ if ( strcasecmp(*attrName, "userpassword") == 0 )
+ {
+ attr_name = "unhashed#user#password";
+ } else {
+ attr_name = *attrName;
+ }
+ err = slapi_entry_attr_find(e, attr_name, &attr);
+ if (err) continue; /* break;*/ /* no 7-bit attribute */
+
+ /*
+ * For each DN in the managed list, do 7-bit checking if
+ * the target DN is a subnode in the tree.
+ */
+ for( subtreeDN=firstSubtree, subtreeCnt=argc ;subtreeCnt > 0;
+ subtreeCnt--,subtreeDN++)
+ {
+ /*
+ * issuffix determines whether the target is under the
+ * subtree *subtreeDN
+ */
+ if (slapi_dn_issuffix(dn, *subtreeDN))
+ {
+#ifdef DEBUG
+ slapi_log_error(SLAPI_LOG_PLUGIN, plugin_name,
+ "ADD subtree=%s\n", *subtreeDN);
+#endif
+
+ /*
+ * Check if the value is 7-bit clean
+ */
+ result = bit_check(attr, NULL, &violated);
+ if (result) break;
+ }
+ }
+ /* don't have to go on if there is a value not 7-bit clean */
+ if (result) break;
+ }
+ END
+
+ if (result) {
+ issue_error(pb, result, "ADD", violated);
+ }
+
+ return (result==LDAP_SUCCESS)?0:-1;
+}
+
+static void
+addMod(LDAPMod ***modary, int *capacity, int *nmods, LDAPMod *toadd)
+{
+ if (*nmods == *capacity) {
+ *capacity += 4;
+ if (*modary) {
+ *modary = (LDAPMod **)slapi_ch_realloc((char *)*modary, *capacity * sizeof(LDAPMod *));
+ } else {
+ *modary = (LDAPMod **)slapi_ch_malloc(*capacity * sizeof(LDAPMod *));
+ }
+ }
+ *modary[*nmods] = toadd;
+ (*nmods)++;
+}
+
+/* ------------------------------------------------------------ */
+/*
+ * preop_modify - pre-operation plug-in for modify
+ */
+static int
+preop_modify(Slapi_PBlock *pb)
+{
+ int result;
+ char *violated;
+ LDAPMod **checkmods = NULL; /* holds mods to check */
+ int checkmodsCapacity = 0; /* max capacity of checkmods */
+
+#ifdef DEBUG
+ slapi_log_error(SLAPI_LOG_PLUGIN, plugin_name,
+ "MODIFY begin\n");
+#endif
+
+ result = LDAP_SUCCESS;
+
+ BEGIN
+ int err;
+ int argc;
+ char **argv;
+ char **attrName;
+ LDAPMod **mods;
+ LDAPMod **firstMods;
+ LDAPMod *mod;
+ char *target;
+ char **firstSubtree;
+ char **subtreeDN;
+ int subtreeCnt;
+ int is_replicated_operation;
+
+ /*
+ * Get the arguments
+ */
+ err = slapi_pblock_get(pb, SLAPI_PLUGIN_ARGC, &argc);
+ if (err) { result = op_error(13); break; }
+
+ err = slapi_pblock_get(pb, SLAPI_PLUGIN_ARGV, &argv);
+ if (err) { result = op_error(14); break; }
+
+ /*
+ * If this is a replication update, just be a noop.
+ */
+ err = slapi_pblock_get(pb, SLAPI_IS_REPLICATED_OPERATION, &is_replicated_operation);
+ if (err) { result = op_error(16); break; }
+ if (is_replicated_operation)
+ {
+ break;
+ }
+
+ err = slapi_pblock_get(pb, SLAPI_MODIFY_MODS, &firstMods);
+ if (err) { result = op_error(10); break; }
+
+ /* Get the target DN */
+ err = slapi_pblock_get(pb, SLAPI_MODIFY_TARGET, &target);
+ if (err) { result = op_error(11); break; }
+
+ /*
+ * Look for managed trees that include the target
+ * Arguments before "," are the 7-bit clean attribute names. Arguemnts
+ * after "," are subtreeDN's.
+ */
+ for ( firstSubtree = argv; strcmp(*firstSubtree, ",") != 0;
+ firstSubtree++, argc--) {}
+ firstSubtree++;
+ argc--;
+
+ for (attrName = argv; strcmp(*attrName, ",") != 0; attrName++ )
+ {
+ int modcount = 0;
+ int ii = 0;
+
+ /*
+ * if the attribute is userpassword, check unhashed#user#password
+ * instead. "userpassword" is encoded; it will always pass the 7bit
+ * check.
+ */
+ char *attr_name;
+ if ( strcasecmp(*attrName, "userpassword") == 0 )
+ {
+ attr_name = "unhashed#user#password";
+ } else {
+ attr_name = *attrName;
+ }
+
+ /* There may be more than one mod that matches e.g.
+ changetype: modify
+ delete: uid
+ uid: balster1950
+ -
+ add: uid
+ uid: scottg
+
+ So, we need to first find all mods that contain the attribute
+ which are add or replace ops and are bvalue encoded
+ */
+ /* find out how many mods meet this criteria */
+ for(mods=firstMods;*mods;mods++)
+ {
+ mod = *mods;
+ if ((slapi_attr_type_cmp(mod->mod_type, attr_name, 1) == 0) && /* mod contains target attr */
+ (mod->mod_op & LDAP_MOD_BVALUES) && /* mod is bval encoded (not string val) */
+ (mod->mod_bvalues && mod->mod_bvalues[0]) && /* mod actually contains some values */
+ (((mod->mod_op & ~LDAP_MOD_BVALUES) == LDAP_MOD_ADD) || /* mod is add */
+ (mod->mod_op & LDAP_MOD_REPLACE))) /* mod is replace */
+ {
+ addMod(&checkmods, &checkmodsCapacity, &modcount, mod);
+ }
+ }
+ if (modcount == 0) {
+ continue; /* no mods to check, go to next attr */
+ }
+
+ /*
+ * stop checking at first mod that fails the check
+ */
+ for (ii = 0; (result == 0) && (ii < modcount); ++ii)
+ {
+ mod = checkmods[ii];
+ /*
+ * For each DN in the managed list, do 7-bit checking if
+ * the target DN is a subnode in the tree.
+ */
+ for( subtreeDN=firstSubtree, subtreeCnt=argc ;subtreeCnt > 0;
+ subtreeCnt--,subtreeDN++)
+ {
+ /*
+ * issuffix determines whether the target is under the
+ * subtree *subtreeDN
+ */
+ if (slapi_dn_issuffix(target, *subtreeDN))
+ {
+#ifdef DEBUG
+ slapi_log_error(SLAPI_LOG_PLUGIN, plugin_name,
+ "MODIFY subtree=%s\n", *subtreeDN);
+#endif
+ /*
+ * Check if the value is 7-bit clean
+ */
+ result = bit_check(NULL, mod->mod_bvalues, &violated);
+ if (result) break;
+ }
+ }
+ }
+ /* don't have to go on if there is a value not 7-bit clean */
+ if (result) break;
+ }
+ END
+
+ slapi_ch_free((void **)&checkmods);
+ if (result) {
+ issue_error(pb, result, "MODIFY", violated);
+ }
+
+ return (result==LDAP_SUCCESS)?0:-1;
+}
+
+/* ------------------------------------------------------------ */
+/*
+ * preop_modrdn - Pre-operation call for modify RDN
+ *
+ * Check that the new RDN does not include attributes that
+ * cause a constraint violation
+ */
+static int
+preop_modrdn(Slapi_PBlock *pb)
+{
+ int result;
+ Slapi_Entry *e;
+ char *violated;
+
+#ifdef DEBUG
+ slapi_log_error(SLAPI_LOG_PLUGIN, plugin_name,
+ "MODRDN begin\n");
+#endif
+
+ /* Init */
+ result = LDAP_SUCCESS;
+ e = 0;
+
+ BEGIN
+ int err;
+ int argc;
+ char **argv;
+ char **attrName;
+ char *target;
+ char *superior;
+ char *rdn;
+ Slapi_Attr *attr;
+ char **firstSubtree;
+ char **subtreeDN;
+ int subtreeCnt;
+ int is_replicated_operation;
+
+ /*
+ * Get the arguments
+ */
+ err = slapi_pblock_get(pb, SLAPI_PLUGIN_ARGC, &argc);
+ if (err) { result = op_error(30); break; }
+
+ err = slapi_pblock_get(pb, SLAPI_PLUGIN_ARGV, &argv);
+ if (err) { result = op_error(31); break; }
+
+ /*
+ * If this is a replication update, just be a noop.
+ */
+ err = slapi_pblock_get(pb, SLAPI_IS_REPLICATED_OPERATION, &is_replicated_operation);
+ if (err) { result = op_error(16); break; }
+ if (is_replicated_operation)
+ {
+ break;
+ }
+
+ /* Get the DN of the entry being renamed */
+ err = slapi_pblock_get(pb, SLAPI_MODRDN_TARGET, &target);
+ if (err) { result = op_error(22); break; }
+
+ /* Get superior value - unimplemented in 3.0 DS */
+ err = slapi_pblock_get(pb, SLAPI_MODRDN_NEWSUPERIOR, &superior);
+ if (err) { result = op_error(20); break; }
+
+ /*
+ * No superior means the entry is just renamed at
+ * its current level in the tree. Use the target DN for
+ * determining which managed tree this belongs to
+ */
+ if (!superior) superior = target;
+
+ /* Get the new RDN - this has the attribute values */
+ err = slapi_pblock_get(pb, SLAPI_MODRDN_NEWRDN, &rdn);
+ if (err) { result = op_error(33); break; }
+
+#ifdef DEBUG
+ slapi_log_error(SLAPI_LOG_PLUGIN, plugin_name,
+ "MODRDN newrdn=%s\n", rdn);
+#endif
+
+ /*
+ * Parse the RDN into attributes by creating a "dummy" entry
+ * and setting the attributes from the RDN.
+ *
+ * The new entry must be freed.
+ */
+ e = slapi_entry_alloc();
+ if (!e) { result = op_error(32); break; }
+
+ /* NOTE: strdup on the rdn, since it will be freed when
+ * the entry is freed */
+
+ slapi_entry_set_dn(e, slapi_ch_strdup(rdn));
+
+ err = slapi_entry_add_rdn_values(e);
+ if (err)
+ {
+ slapi_log_error(SLAPI_LOG_PLUGIN, plugin_name,
+ "MODRDN bad rdn value=%s\n", rdn);
+ break; /* Bad DN */
+ }
+
+ /*
+ * arguments before "," are the 7-bit clean attribute names. Arguemnts
+ * after "," are subtreeDN's.
+ */
+ for ( firstSubtree = argv; strcmp(*firstSubtree, ",") != 0;
+ firstSubtree++, argc--) {}
+ firstSubtree++;
+ argc--;
+
+ /*
+ * Find out if the node is being moved into one of
+ * the managed subtrees
+ */
+ for (attrName = argv; strcmp(*attrName, ",") != 0; attrName++ )
+ {
+ /*
+ * If the attribut type is userpassword, do not replace it by
+ * unhashed#user#password because unhashed#user#password does not exist
+ * in this case.
+ */
+ /*
+ * Find any 7-bit attribute data in the new RDN
+ */
+ err = slapi_entry_attr_find(e, *attrName, &attr);
+ if (err) continue; /* break;*/ /* no 7-bit attribute */
+
+ /*
+ * For each DN in the managed list, do 7-bit checking if
+ * the target DN is a subnode in the tree.
+ */
+ for( subtreeDN=firstSubtree, subtreeCnt=argc ;subtreeCnt > 0;
+ subtreeCnt--,subtreeDN++)
+ {
+ /*
+ * issuffix determines whether the target is under the
+ * subtree *subtreeDN
+ */
+ if (slapi_dn_issuffix(superior, *subtreeDN))
+ {
+#ifdef DEBUG
+ slapi_log_error(SLAPI_LOG_PLUGIN, plugin_name,
+ "MODRDN subtree=%s\n", *subtreeDN);
+#endif
+
+ /*
+ * Check if the value is 7-bit clean
+ */
+ result = bit_check(attr, NULL, &violated);
+ if (result) break;
+ }
+ }
+ /* don't have to go on if there is a value not 7-bit clean */
+ if (result) break;
+ }
+ END
+
+ /* Clean-up */
+ if (e) slapi_entry_free(e);
+
+ if (result) {
+ issue_error(pb, result, "MODRDN", violated);
+ }
+
+ return (result==LDAP_SUCCESS)?0:-1;
+}
+
+/* ------------------------------------------------------------ */
+/*
+ * Initialize the plugin
+ *
+ */
+int
+NS7bitAttr_Init(Slapi_PBlock *pb)
+{
+ int err = 0;
+
+ BEGIN
+ int err;
+ int argc;
+ char **argv;
+
+ /* Declare plugin version */
+ err = slapi_pblock_set(pb, SLAPI_PLUGIN_VERSION,
+ SLAPI_PLUGIN_VERSION_01);
+ if (err) break;
+
+ /*
+ * Get and normalize arguments
+ */
+ err = slapi_pblock_get(pb, SLAPI_PLUGIN_ARGC, &argc);
+ if (err) break;
+
+ err = slapi_pblock_get(pb, SLAPI_PLUGIN_ARGV, &argv);
+ if (err) break;
+
+ /*
+ * Arguments before "," are the 7-bit attribute names. Arguments after
+ * "," are the subtree DN's.
+ */
+ if (argc < 1) { err = -1; break; }
+ for(;strcmp(*argv, ",") != 0 && argc > 0; argc--, argv++)
+ {};
+ if (argc == 0) { err = -1; break; }
+ argv++; argc--;
+
+ for(;argc > 0;argc--, argv++)
+ slapi_dn_normalize_case(*argv);
+
+ /* Provide descriptive information */
+ err = slapi_pblock_set(pb, SLAPI_PLUGIN_DESCRIPTION,
+ (void*)&pluginDesc);
+ if (err) break;
+
+ /* Register functions */
+ err = slapi_pblock_set(pb, SLAPI_PLUGIN_PRE_ADD_FN,
+ (void*)preop_add);
+ if (err) break;
+
+ err = slapi_pblock_set(pb, SLAPI_PLUGIN_PRE_MODIFY_FN,
+ (void*)preop_modify);
+ if (err) break;
+
+ err = slapi_pblock_set(pb, SLAPI_PLUGIN_PRE_MODRDN_FN,
+ (void*)preop_modrdn);
+ if (err) break;
+
+ END
+
+ if (err) {
+ slapi_log_error(SLAPI_LOG_PLUGIN, "NS7bitAttr_Init",
+ "Error: %d\n", err);
+ err = -1;
+ }
+ else
+ slapi_log_error(SLAPI_LOG_PLUGIN, "NS7bitAttr_Init",
+ "plugin loaded\n");
+
+ return err;
+}
+
diff --git a/ldap/servers/plugins/uiduniq/Makefile b/ldap/servers/plugins/uiduniq/Makefile
new file mode 100644
index 00000000..79b95995
--- /dev/null
+++ b/ldap/servers/plugins/uiduniq/Makefile
@@ -0,0 +1,99 @@
+#
+# BEGIN COPYRIGHT BLOCK
+# Copyright 2001 Sun Microsystems, Inc.
+# Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+# All rights reserved.
+# END COPYRIGHT BLOCK
+#
+#
+# GNU Makefile for Directory Server "Pass Through Authentication" plugin
+#
+#
+
+LDAP_SRC = ../../..
+MCOM_ROOT = ../../../../..
+
+NOSTDCLEAN=true # don't let nsconfig.mk define target clean
+NOSTDSTRIP=true # don't let nsconfig.mk define target strip
+NSPR20=true # probably should be defined somewhere else (not sure where)
+
+OBJDEST = $(OBJDIR)/lib/libuidunique
+LIBDIR = $(LIB_RELDIR)
+SHAREDLIB = $(OBJDIR)/lib/shared/utils.o
+
+include $(MCOM_ROOT)/ldapserver/nsconfig.mk
+include $(LDAP_SRC)/nsldap.mk
+
+ifeq ($(ARCH), WINNT)
+DEF_FILE:=./libuiduniq.def
+endif
+
+CFLAGS+=$(SLCFLAGS)
+
+INCLUDES += -I$(LDAP_SRC)/servers/slapd -I../shared
+
+LOCAL_OBJS= uid.o 7bit.o
+
+SHAREDDIR= ../shared
+
+OBJS = $(addprefix $(OBJDEST)/, $(LOCAL_OBJS))
+
+ifeq ($(ARCH), WINNT)
+#LIBUIDUNIQUE_DLL_OBJ = $(addprefix $(OBJDEST)/, uid.o 7bit.o)
+endif
+
+LIBUIDUNIQUE= $(addprefix $(LIBDIR)/, $(UID_DLL).$(DLL_SUFFIX))
+
+ifeq ($(ARCH), WINNT)
+EXTRA_LIBS_DEP += $(LIBSLAPD_DEP)
+EXTRA_LIBS_DEP += $(LDAPSDK_DEP)
+EXTRA_LIBS += $(LIBSLAPD) $(LDAP_SDK_LIBLDAP_DLL)
+endif
+
+
+
+ifeq ($(ARCH), WINNT)
+DLL_LDFLAGS += -def:"./libuiduniq.def"
+endif # WINNT
+
+ifeq ($(ARCH), AIX)
+EXTRA_LIBS_DEP += $(LIBSLAPD_DEP)
+EXTRA_LIBS_DEP += $(LDAPSDK_DEP)
+EXTRA_LIBS += $(LIBSLAPD) $(LDAP_SDK_LIBLDAP_DLL)
+EXTRA_LIBS += $(DLL_EXTRA_LIBS)
+LD=ld
+endif
+
+ifeq ($(ARCH), HPUX)
+EXTRA_LIBS_DEP += $(LIBSLAPD_DEP) $(LDAPSDK_DEP) $(NSPR_DEP) $(SECURITY_DEP)
+EXTRA_LIBS += $(DYN_NSHTTPD) $(ADMINUTIL_LINK) $(LDAPLINK) $(SECURITYLINK) $(NSPRLINK) $(ICULINK)
+endif
+
+EXTRA_LIBS += $(SHAREDLIB)
+
+clientSDK:
+
+all: $(OBJDEST) $(LIBDIR) $(LIBUIDUNIQUE)
+
+$(LIBUIDUNIQUE): $(OBJS) $(LIBUIDUNIQUE_DLL_OBJ) $(DEF_FILE)
+# $(LINK_DLL) $(LIBUIDUNIQUE_DLL_OBJ) $(PLATFORMLIBS) $(EXTRA_LIBS)
+ $(LINK_DLL) $(PLATFORMLIBS) $(EXTRA_LIBS)
+
+veryclean: clean
+
+clean:
+ $(RM) $(OBJS)
+ifeq ($(ARCH), WINNT)
+ $(RM) $(LIBUIDUNIQUE_DLL_OBJ)
+endif
+ $(RM) $(LIBUIDUNIQUE)
+
+$(OBJDEST):
+ $(MKDIR) $(OBJDEST)
+
+#
+# header file dependencies (incomplete)
+#
+$(OBJS): $(LDAP_SRC)/servers/slapd/slapi-plugin.h \
+ ../shared/plugin-utils.h
+
diff --git a/ldap/servers/plugins/uiduniq/UID-Notes b/ldap/servers/plugins/uiduniq/UID-Notes
new file mode 100644
index 00000000..3d3617ff
--- /dev/null
+++ b/ldap/servers/plugins/uiduniq/UID-Notes
@@ -0,0 +1,93 @@
+#
+# BEGIN COPYRIGHT BLOCK
+# Copyright 2001 Sun Microsystems, Inc.
+# Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+# All rights reserved.
+# END COPYRIGHT BLOCK
+#
+Unique UID Checking Plugin
+--------------------------
+
+Terry Hayes, April 16, 1998
+
+
+GOALS
+
+The Unique UID Checking Plugin supports the management of user entries in the
+directory by enforcing the constraints on the value of an attribute within a
+portion of the directory. This provides a central point for enforcing this
+constraint, which allows changes from any source to be checked (DSGW, Kingpin,
+LDAP utilities, or user application).
+
+CONFIGURATION
+
+The software operates as a preoperation plugin to the directory server. An
+entry must be added to the slapd.conf file for the server that declares the
+plugin and provides arguments required for its operation.
+
+The plugin is declared as follows (line split for clarity):
+
+ plugin preoperation "uid uniqueness" /home/thayes/testdir/lib/uid-plugin.so
+ uidunique_init <attribute_name> <subtree_dn> ...
+
+The first 5 values are the standard plugin declaration. The uidunique_init
+function registers preoperation callbacks for the add, modify and modRDN
+directory operations.
+
+The next argument ("attribute_name") specifies the name of the entry attribute
+to check for uniqueness. This attribute must be unique within each of the
+subtrees listed in the remainder of the arguments.
+
+For example:
+
+ plugin preoperation "uid uniqueness" /home/thayes/testdir/lib/uid-plugin.so
+ uidunique_init uid o=mcom.com
+
+This line specifies "uid" as the unique attribute, and lists a single subtree
+to be checked. This line is typical of an initial installation (see below).
+
+A more complex case:
+
+ plugin preoperation "uid uniqueness" /home/thayes/testdir/lib/uid-plugin.so
+ uidunique_init uid o=Coke o=Pepsi
+ plugin preoperation "uid uniqueness" /home/thayes/testdir/lib/uid-plugin.so
+ uidunique_init mail "o=Dr. Pepper"
+
+This configuration specifies a total of three subtrees to check. Two use the
+(standard) "uid" attribute as a unique value. The other specifies "mail"
+as the unique attribute.
+
+INSTALLATION
+
+The standard installation of the directory server will configure this plugin
+to check the "uid" attribute on the default suffix.
+
+OPERATION
+
+The plugin responds to the following LDAP operations:
+
+ + add
+ + modify
+ + modRDN
+
+For all operations, the plugin forces the LDAP operation to return
+CONSTRAINT_VIOLATION if the operation would result in two entries with
+the same unique attribute value.
+
+For an "add" operation that includes the unique attribute, the plugin checks
+that no other entry has the same value.
+
+For a "modify" operation, the operation will fail if the new value of the
+attribute exists in any entry OTHER than the target of the modify. If the
+value already exists, but is in the node being changed, the operation
+succeeds. For example, if a modify operation replaces a 'uid' attribute
+with the same set of values, the plugin will find the "new" values already
+exist. However since it is in the entry being modified, the operation is
+allowed to complete.
+
+For modRDN, the same checking as for "modify" is performed.
+
+ModRDN is coded to handle reparenting, but since the LDAP protocol to support
+this operation is not present, it cannot be exercised and has not been
+tested.
+
diff --git a/ldap/servers/plugins/uiduniq/libuiduniq.def b/ldap/servers/plugins/uiduniq/libuiduniq.def
new file mode 100644
index 00000000..52bbfcca
--- /dev/null
+++ b/ldap/servers/plugins/uiduniq/libuiduniq.def
@@ -0,0 +1,15 @@
+; BEGIN COPYRIGHT BLOCK
+; Copyright 2001 Sun Microsystems, Inc.
+; Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+; All rights reserved.
+; END COPYRIGHT BLOCK
+;
+;
+;
+DESCRIPTION 'Netscape Directory Server 7 Unique Attribute Checking Plugin'
+;CODE SHARED READ EXECUTE
+;DATA SHARED READ WRITE
+EXPORTS
+ uidunique_init @1
+ NSUniqueAttr_Init @2
+ NS7bitAttr_Init @3
diff --git a/ldap/servers/plugins/uiduniq/uid.c b/ldap/servers/plugins/uiduniq/uid.c
new file mode 100644
index 00000000..f32e63ac
--- /dev/null
+++ b/ldap/servers/plugins/uiduniq/uid.c
@@ -0,0 +1,1073 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+/*
+ * uid.c
+ *
+ * Implements a directory server pre-operation plugin to test
+ * attributes for uniqueness within a defined subtree in the
+ * directory.
+ *
+ * Called uid.c since the original purpose of the plugin was to
+ * check the uid attribute in user entries.
+ */
+#include <slapi-plugin.h>
+#include <portable.h>
+#include <dirlite_strings.h> /* PLUGIN_MAGIC_VENDOR_STR */
+#include <string.h>
+#include "dirver.h"
+#include "plugin-utils.h"
+
+#if defined( LDAP_DEBUG ) && !defined( DEBUG )
+#define DEBUG
+#endif
+
+#define UNTAGGED_PARAMETER 12
+
+/* Quoting routine - this should be in a library somewhere (slapi?) */
+int ldap_quote_filter_value(
+ char *value, int len,
+ char *out, int maxLen,
+ int *outLen);
+
+
+static int search_one_berval(const char *baseDN, const char *attrName,
+ const struct berval *value, const char *target);
+
+/*
+ * ISSUES:
+ * How should this plugin handle ACL issues? It seems wrong to reject
+ * adds and modifies because there is already a conflicting UID, when
+ * the request would have failed because of an ACL check anyway.
+ *
+ * This code currently defines a maximum filter string size of 512. Is
+ * this large enough?
+ *
+ * This code currently does not quote the value portion of the filter as
+ * it is created. This is a bug.
+ */
+
+/* */
+#define BEGIN do {
+#define END } while(0);
+
+/*
+ * Slapi plugin descriptor
+ */
+static char *plugin_name = "NSUniqueAttr";
+static Slapi_PluginDesc
+pluginDesc = {
+ "NSUniqueAttr", PLUGIN_MAGIC_VENDOR_STR, PRODUCTTEXT,
+ "Enforce unique attribute values"
+};
+static void* plugin_identity = NULL;
+
+
+/*
+ * More information about constraint failure
+ */
+static char *moreInfo =
+ "Another entry with the same attribute value already exists";
+
+static void
+freePblock( Slapi_PBlock *spb ) {
+ if ( spb )
+ {
+ slapi_free_search_results_internal( spb );
+ slapi_pblock_destroy( spb );
+ }
+}
+
+/* ------------------------------------------------------------ */
+/*
+ * op_error - Record (and report) an operational error.
+ * name changed to uid_op_error so as not to conflict with the external function
+ * of the same name thereby preventing compiler warnings.
+ */
+static int
+uid_op_error(int internal_error)
+{
+ slapi_log_error(
+ SLAPI_LOG_PLUGIN,
+ plugin_name,
+ "Internal error: %d\n",
+ internal_error);
+
+ return LDAP_OPERATIONS_ERROR;
+}
+
+/* ------------------------------------------------------------ */
+/*
+ * Create an LDAP search filter from the attribute
+ * name and value supplied.
+ */
+
+static char *
+create_filter(const char *attribute, const struct berval *value)
+{
+ char *filter;
+ char *fp;
+ char *max;
+ int attrLen;
+ int valueLen;
+ int filterLen;
+
+ /* Compute the length of the required buffer */
+ attrLen = strlen(attribute);
+
+ if (ldap_quote_filter_value(value->bv_val,
+ value->bv_len, 0, 0, &valueLen))
+ return 0;
+
+ filterLen = attrLen + 1 + valueLen + 1;
+
+ /* Allocate the buffer */
+ filter = slapi_ch_malloc(filterLen);
+ fp = filter;
+ max = &filter[filterLen];
+
+ /* Place attribute name in filter */
+ strcpy(fp, attribute);
+ fp += attrLen;
+
+ /* Place comparison operator */
+ *fp++ = '=';
+
+ /* Place value in filter */
+ if (ldap_quote_filter_value(value->bv_val, value->bv_len,
+ fp, max-fp, &valueLen)) { slapi_ch_free((void**)&filter); return 0; }
+ fp += valueLen;
+
+ /* Terminate */
+ *fp = 0;
+
+ return filter;
+}
+
+/* ------------------------------------------------------------ */
+/*
+ * search - search a subtree for entries with a named attribute matching
+ * the list of values. An entry matching the 'target' DN is
+ * not considered in the search.
+ *
+ * If 'attr' is NULL, the values are taken from 'values'.
+ * If 'attr' is non-NULL, the values are taken from 'attr'.
+ *
+ * Return:
+ * LDAP_SUCCESS - no matches, or the attribute matches the
+ * target dn.
+ * LDAP_CONSTRAINT_VIOLATION - an entry was found that already
+ * contains the attribute value.
+ * LDAP_OPERATIONS_ERROR - a server failure.
+ */
+static int
+search(const char *baseDN, const char *attrName, Slapi_Attr *attr,
+ struct berval **values, const char *target)
+{
+ int result;
+
+#ifdef DEBUG
+ slapi_log_error(SLAPI_LOG_PLUGIN, plugin_name,
+ "SEARCH baseDN=%s attr=%s target=%s\n", baseDN, attrName,
+ target?target:"None");
+#endif
+
+ result = LDAP_SUCCESS;
+
+ /* If no values, can't possibly be a conflict */
+ if ( (Slapi_Attr *)NULL == attr && (struct berval **)NULL == values )
+ return result;
+
+ /*
+ * Perform the search for each value provided
+ *
+ * Another possibility would be to search for all the values at once.
+ * However, this is more complex (for filter creation) and unique
+ * attributes values are probably only changed one at a time anyway.
+ */
+ if ( (Slapi_Attr *)NULL != attr )
+ {
+ Slapi_Value *v = NULL;
+ int vhint = -1;
+
+ for ( vhint = slapi_attr_first_value( attr, &v );
+ vhint != -1 && LDAP_SUCCESS == result;
+ vhint = slapi_attr_next_value( attr, vhint, &v ))
+ {
+ result = search_one_berval(baseDN,attrName,
+ slapi_value_get_berval(v),target);
+ }
+ }
+ else
+ {
+ for (;*values != NULL && LDAP_SUCCESS == result; values++)
+ {
+ result = search_one_berval(baseDN,attrName,*values,target);
+ }
+ }
+
+#ifdef DEBUG
+ slapi_log_error(SLAPI_LOG_PLUGIN, plugin_name,
+ "SEARCH result = %d\n", result);
+#endif
+
+ return( result );
+}
+
+
+static int
+search_one_berval(const char *baseDN, const char *attrName,
+ const struct berval *value, const char *target)
+{
+ int result;
+ char *filter;
+ Slapi_PBlock *spb;
+
+ result = LDAP_SUCCESS;
+
+ /* If no value, can't possibly be a conflict */
+ if ( (struct berval *)NULL == value )
+ return result;
+
+ filter = 0;
+ spb = 0;
+
+ BEGIN
+ int err;
+ int sres;
+ Slapi_Entry **entries;
+ static char *attrs[] = { "1.1", 0 };
+
+ /* Create the filter - this needs to be freed */
+ filter = create_filter(attrName, value);
+
+#ifdef DEBUG
+ slapi_log_error(SLAPI_LOG_PLUGIN, plugin_name,
+ "SEARCH filter=%s\n", filter);
+#endif
+
+ /* Perform the search using the new internal API */
+ spb = slapi_pblock_new();
+ if (!spb) { result = uid_op_error(2); break; }
+
+ slapi_search_internal_set_pb(spb, baseDN, LDAP_SCOPE_SUBTREE,
+ filter, attrs, 0 /* attrs only */, NULL, NULL, plugin_identity, 0 /* actions */);
+ slapi_search_internal_pb(spb);
+
+ err = slapi_pblock_get(spb, SLAPI_PLUGIN_INTOP_RESULT, &sres);
+ if (err) { result = uid_op_error(3); break; }
+
+ /* Allow search to report that there is nothing in the subtree */
+ if (sres == LDAP_NO_SUCH_OBJECT) break;
+
+ /* Other errors are bad */
+ if (sres) { result = uid_op_error(3); break; }
+
+ err = slapi_pblock_get(spb, SLAPI_PLUGIN_INTOP_SEARCH_ENTRIES,
+ &entries);
+ if (err) { result = uid_op_error(4); break; }
+
+ /*
+ * Look at entries returned. Any entry found must be the
+ * target entry or the constraint fails.
+ */
+ for(;*entries;entries++)
+ {
+ char *dn = slapi_entry_get_dn(*entries);
+
+ /*
+ * DNs are returned in the original value used to insert
+ * the entry. This must be "normalized" for comparison.
+ *
+ * This normalization is done "in-place" (modifying the value
+ * in the entry). This is OK, since this is the only user
+ * of this copy of the entry.
+ */
+ slapi_dn_normalize_case(dn);
+
+#ifdef DEBUG
+ slapi_log_error(SLAPI_LOG_PLUGIN, plugin_name,
+ "SEARCH entry dn=%s\n", dn);
+#endif
+
+ /*
+ * It is a Constraint Violation if any entry is found, unless
+ * the entry is the target entry (if any).
+ */
+ if (!target || strcmp(dn, target) != 0)
+ {
+ result = LDAP_CONSTRAINT_VIOLATION;
+ break;
+ }
+ }
+
+#ifdef DEBUG
+ slapi_log_error(SLAPI_LOG_PLUGIN, plugin_name,
+ "SEARCH complete result=%d\n", result);
+#endif
+ END
+
+ /* Clean-up */
+ if (spb) {
+ slapi_free_search_results_internal(spb);
+ slapi_pblock_destroy(spb);
+ }
+
+ slapi_ch_free((void**)&filter);
+
+ return result;
+}
+
+/* ------------------------------------------------------------ */
+/*
+ * searchAllSubtrees - search all subtrees in argv for entries
+ * with a named attribute matching the list of values, by
+ * calling search for each one.
+ *
+ * If 'attr' is NULL, the values are taken from 'values'.
+ * If 'attr' is non-NULL, the values are taken from 'attr'.
+ *
+ * Return:
+ * LDAP_SUCCESS - no matches, or the attribute matches the
+ * target dn.
+ * LDAP_CONSTRAINT_VIOLATION - an entry was found that already
+ * contains the attribute value.
+ * LDAP_OPERATIONS_ERROR - a server failure.
+ */
+static int
+searchAllSubtrees(int argc, char *argv[], const char *attrName,
+ Slapi_Attr *attr, struct berval **values, const char *dn)
+{
+ int result = LDAP_SUCCESS;
+
+ /*
+ * For each DN in the managed list, do uniqueness checking if
+ * the target DN is a subnode in the tree.
+ */
+ for(;argc > 0;argc--,argv++)
+ {
+ result = search(*argv, attrName, attr, values, dn);
+ if (result) break;
+ }
+ return result;
+}
+
+/* ------------------------------------------------------------ */
+/*
+ * getArguments - parse invocation parameters
+ * Return:
+ * 0 - success
+ * >0 - error parsing parameters
+ */
+static int
+getArguments(Slapi_PBlock *pb, char **attrName, char **markerObjectClass,
+ char **requiredObjectClass)
+{
+ int argc;
+ char **argv;
+
+ /*
+ * Get the arguments
+ */
+ if (slapi_pblock_get(pb, SLAPI_PLUGIN_ARGC, &argc))
+ {
+ return uid_op_error(10);
+ }
+
+ if (slapi_pblock_get(pb, SLAPI_PLUGIN_ARGV, &argv))
+ {
+ return uid_op_error(11);
+ }
+
+ /*
+ * Required arguments: attribute and markerObjectClass
+ * Optional argument: requiredObjectClass
+ */
+ for(;argc > 0;argc--,argv++)
+ {
+ char *param = *argv;
+ char *delimiter = strchr(param, '=');
+ if (NULL == delimiter)
+ {
+ /* Old style untagged parameter */
+ *attrName = *argv;
+ return UNTAGGED_PARAMETER;
+ }
+ if (strncasecmp(param, "attribute", delimiter-param) == 0)
+ {
+ /* It's OK to set a pointer here, because ultimately it points
+ * inside the argv array of the pblock, which will be staying
+ * arround.
+ */
+ *attrName = delimiter+1;
+ } else if (strncasecmp(param, "markerobjectclass", delimiter-param) == 0)
+ {
+ *markerObjectClass = delimiter+1;
+ } else if (strncasecmp(param, "requiredobjectclass", delimiter-param) == 0)
+ {
+ *requiredObjectClass = delimiter+1;
+ }
+ }
+ if (!*attrName || !*markerObjectClass)
+ {
+ return uid_op_error(13);
+ }
+
+ return 0;
+}
+
+/* ------------------------------------------------------------ */
+/*
+ * findSubtreeAndSearch - walk up the tree to find an entry with
+ * the marker object class; if found, call search from there and
+ * return the result it returns
+ *
+ * If 'attr' is NULL, the values are taken from 'values'.
+ * If 'attr' is non-NULL, the values are taken from 'attr'.
+ *
+ * Return:
+ * LDAP_SUCCESS - no matches, or the attribute matches the
+ * target dn.
+ * LDAP_CONSTRAINT_VIOLATION - an entry was found that already
+ * contains the attribute value.
+ * LDAP_OPERATIONS_ERROR - a server failure.
+ */
+static int
+findSubtreeAndSearch(char *parentDN, const char *attrName, Slapi_Attr *attr,
+ struct berval **values, const char *target, const char *markerObjectClass)
+{
+ int result = LDAP_SUCCESS;
+ Slapi_PBlock *spb = NULL;
+
+ while (NULL != (parentDN = slapi_dn_parent(parentDN)))
+ {
+ if (spb = dnHasObjectClass(parentDN, markerObjectClass))
+ {
+ freePblock(spb);
+ /*
+ * Do the search. There is no entry that is allowed
+ * to have the attribute already.
+ */
+ result = search(parentDN, attrName, attr, values, target);
+ break;
+ }
+ }
+ return result;
+}
+
+
+/* ------------------------------------------------------------ */
+/*
+ * preop_add - pre-operation plug-in for add
+ */
+static int
+preop_add(Slapi_PBlock *pb)
+{
+ int result;
+
+#ifdef DEBUG
+ slapi_log_error(SLAPI_LOG_PLUGIN, plugin_name, "ADD begin\n");
+#endif
+
+ result = LDAP_SUCCESS;
+
+ /*
+ * Do constraint check on the added entry. Set result.
+ */
+
+ BEGIN
+ int err;
+ char *attrName = NULL;
+ char *markerObjectClass = NULL;
+ char *requiredObjectClass = NULL;
+ char *dn;
+ int isupdatedn;
+ Slapi_Entry *e;
+ Slapi_Attr *attr;
+ int argc;
+ char **argv = NULL;
+
+ /*
+ * If this is a replication update, just be a noop.
+ */
+ err = slapi_pblock_get(pb, SLAPI_IS_REPLICATED_OPERATION, &isupdatedn);
+ if (err) { result = uid_op_error(50); break; }
+ if (isupdatedn)
+ {
+ break;
+ }
+
+ /*
+ * Get the arguments
+ */
+ result = getArguments(pb, &attrName, &markerObjectClass,
+ &requiredObjectClass);
+ if (UNTAGGED_PARAMETER == result)
+ {
+ slapi_log_error(SLAPI_LOG_PLUGIN, plugin_name,
+ "ADD parameter untagged: %s\n", attrName);
+ result = LDAP_SUCCESS;
+ /* Statically defined subtrees to monitor */
+ err = slapi_pblock_get(pb, SLAPI_PLUGIN_ARGC, &argc);
+ if (err) { result = uid_op_error(53); break; }
+
+ err = slapi_pblock_get(pb, SLAPI_PLUGIN_ARGV, &argv);
+ if (err) { result = uid_op_error(54); break; }
+ argc--; argv++; /* First argument was attribute name */
+ } else if (0 != result)
+ {
+ break;
+ }
+
+ /*
+ * Get the target DN for this add operation
+ */
+ err = slapi_pblock_get(pb, SLAPI_ADD_TARGET, &dn);
+ if (err) { result = uid_op_error(51); break; }
+
+#ifdef DEBUG
+ slapi_log_error(SLAPI_LOG_PLUGIN, plugin_name, "ADD target=%s\n", dn);
+#endif
+
+ /*
+ * Get the entry data for this add. Check whether it
+ * contains a value for the unique attribute
+ */
+ err = slapi_pblock_get(pb, SLAPI_ADD_ENTRY, &e);
+ if (err) { result = uid_op_error(52); break; }
+
+ err = slapi_entry_attr_find(e, attrName, &attr);
+ if (err) break; /* no unique attribute */
+
+ /*
+ * Check if it contains the required object class
+ */
+ if (NULL != requiredObjectClass)
+ {
+ if (!entryHasObjectClass(pb, e, requiredObjectClass))
+ {
+ /* No, so we don't have to do anything */
+ break;
+ }
+ }
+
+ /*
+ * Passed all the requirements - this is an operation we
+ * need to enforce uniqueness on. Now find all parent entries
+ * with the marker object class, and do a search for each one.
+ */
+ if (NULL != markerObjectClass)
+ {
+ /* Subtree defined by location of marker object class */
+ result = findSubtreeAndSearch(dn, attrName, attr, NULL,
+ dn, markerObjectClass);
+ } else
+ {
+ /* Subtrees listed on invocation line */
+ result = searchAllSubtrees(argc, argv, attrName, attr, NULL, dn);
+ }
+ END
+
+ if (result)
+ {
+ slapi_log_error(SLAPI_LOG_PLUGIN, plugin_name,
+ "ADD result %d\n", result);
+
+ /* Send failure to the client */
+ slapi_send_ldap_result(pb, result, 0, moreInfo, 0, 0);
+ }
+
+ return (result==LDAP_SUCCESS)?0:-1;
+}
+
+static void
+addMod(LDAPMod ***modary, int *capacity, int *nmods, LDAPMod *toadd)
+{
+ if (*nmods == *capacity) {
+ *capacity += 4;
+ if (*modary) {
+ *modary = (LDAPMod **)slapi_ch_realloc((char *)*modary, *capacity * sizeof(LDAPMod *));
+ } else {
+ *modary = (LDAPMod **)slapi_ch_malloc(*capacity * sizeof(LDAPMod *));
+ }
+ }
+ *modary[*nmods] = toadd;
+ (*nmods)++;
+}
+
+/* ------------------------------------------------------------ */
+/*
+ * preop_modify - pre-operation plug-in for modify
+ */
+static int
+preop_modify(Slapi_PBlock *pb)
+{
+
+ int result = LDAP_SUCCESS;
+ Slapi_PBlock *spb = NULL;
+ LDAPMod **checkmods = NULL;
+ int checkmodsCapacity = 0;
+
+#ifdef DEBUG
+ slapi_log_error(SLAPI_LOG_PLUGIN, plugin_name,
+ "MODIFY begin\n");
+#endif
+
+ BEGIN
+ int err;
+ char *attrName;
+ char *markerObjectClass=NULL;
+ char *requiredObjectClass=NULL;
+ LDAPMod **mods;
+ int modcount = 0;
+ int ii;
+ LDAPMod *mod;
+ char *dn;
+ int isupdatedn;
+ int argc;
+ char **argv = NULL;
+
+ /*
+ * If this is a replication update, just be a noop.
+ */
+ err = slapi_pblock_get(pb, SLAPI_IS_REPLICATED_OPERATION, &isupdatedn);
+ if (err) { result = uid_op_error(60); break; }
+ if (isupdatedn)
+ {
+ break;
+ }
+
+ /*
+ * Get the arguments
+ */
+ result = getArguments(pb, &attrName, &markerObjectClass,
+ &requiredObjectClass);
+ if (UNTAGGED_PARAMETER == result)
+ {
+ result = LDAP_SUCCESS;
+ /* Statically defined subtrees to monitor */
+ err = slapi_pblock_get(pb, SLAPI_PLUGIN_ARGC, &argc);
+ if (err) { result = uid_op_error(53); break; }
+
+ err = slapi_pblock_get(pb, SLAPI_PLUGIN_ARGV, &argv);
+ if (err) { result = uid_op_error(54); break; }
+ argc--; /* First argument was attribute name */
+ argv++;
+ } else if (0 != result)
+ {
+ break;
+ }
+
+ err = slapi_pblock_get(pb, SLAPI_MODIFY_MODS, &mods);
+ if (err) { result = uid_op_error(61); break; }
+
+ /* There may be more than one mod that matches e.g.
+ changetype: modify
+ delete: uid
+ uid: balster1950
+ -
+ add: uid
+ uid: scottg
+
+ So, we need to first find all mods that contain the attribute
+ which are add or replace ops and are bvalue encoded
+ */
+ /* find out how many mods meet this criteria */
+ for(;*mods;mods++)
+ {
+ mod = *mods;
+ if ((slapi_attr_type_cmp(mod->mod_type, attrName, 1) == 0) && /* mod contains target attr */
+ (mod->mod_op & LDAP_MOD_BVALUES) && /* mod is bval encoded (not string val) */
+ (mod->mod_bvalues && mod->mod_bvalues[0]) && /* mod actually contains some values */
+ (((mod->mod_op & ~LDAP_MOD_BVALUES) == LDAP_MOD_ADD) || /* mod is add */
+ (mod->mod_op & LDAP_MOD_REPLACE))) /* mod is replace */
+ {
+ addMod(&checkmods, &checkmodsCapacity, &modcount, mod);
+ }
+ }
+ if (modcount == 0) {
+ break; /* no mods to check, we are done */
+ }
+
+ /* Get the target DN */
+ err = slapi_pblock_get(pb, SLAPI_MODIFY_TARGET, &dn);
+ if (err) { result = uid_op_error(11); break; }
+
+ if (requiredObjectClass &&
+ !(spb = dnHasObjectClass(dn, requiredObjectClass))) { break; }
+
+ /*
+ * Passed all the requirements - this is an operation we
+ * need to enforce uniqueness on. Now find all parent entries
+ * with the marker object class, and do a search for each one.
+ */
+ /*
+ * stop checking at first mod that fails the check
+ */
+ for (ii = 0; (result == 0) && (ii < modcount); ++ii)
+ {
+ mod = checkmods[ii];
+ if (NULL != markerObjectClass)
+ {
+ /* Subtree defined by location of marker object class */
+ result = findSubtreeAndSearch(dn, attrName, NULL,
+ mod->mod_bvalues, dn,
+ markerObjectClass);
+ } else
+ {
+ /* Subtrees listed on invocation line */
+ result = searchAllSubtrees(argc, argv, attrName, NULL,
+ mod->mod_bvalues, dn);
+ }
+ }
+ END
+
+ slapi_ch_free((void **)&checkmods);
+ freePblock(spb);
+ if (result)
+ {
+ slapi_log_error(SLAPI_LOG_PLUGIN, plugin_name,
+ "MODIFY result %d\n", result);
+
+ slapi_send_ldap_result(pb, result, 0, moreInfo, 0, 0);
+ }
+
+ return (result==LDAP_SUCCESS)?0:-1;
+
+}
+
+/* ------------------------------------------------------------ */
+/*
+ * preop_modrdn - Pre-operation call for modify RDN
+ *
+ * Check that the new RDN does not include attributes that
+ * cause a constraint violation
+ */
+static int
+preop_modrdn(Slapi_PBlock *pb)
+{
+ int result;
+ Slapi_Entry *e;
+
+#ifdef DEBUG
+ slapi_log_error(SLAPI_LOG_PLUGIN, plugin_name,
+ "MODRDN begin\n");
+#endif
+
+ /* Init */
+ result = LDAP_SUCCESS;
+ e = 0;
+
+ BEGIN
+ int err;
+ char *attrName;
+ char *markerObjectClass=NULL;
+ char *requiredObjectClass=NULL;
+ char *dn;
+ char *superior;
+ char *rdn;
+ int isupdatedn;
+ Slapi_Attr *attr;
+ int argc;
+ char **argv = NULL;
+
+ /*
+ * If this is a replication update, just be a noop.
+ */
+ err = slapi_pblock_get(pb, SLAPI_IS_REPLICATED_OPERATION, &isupdatedn);
+ if (err) { result = uid_op_error(30); break; }
+ if (isupdatedn)
+ {
+ break;
+ }
+
+ /*
+ * Get the arguments
+ */
+ result = getArguments(pb, &attrName, &markerObjectClass,
+ &requiredObjectClass);
+ if (UNTAGGED_PARAMETER == result)
+ {
+ result = LDAP_SUCCESS;
+ /* Statically defined subtrees to monitor */
+ err = slapi_pblock_get(pb, SLAPI_PLUGIN_ARGC, &argc);
+ if (err) { result = uid_op_error(53); break; }
+
+ err = slapi_pblock_get(pb, SLAPI_PLUGIN_ARGV, &argv);
+ if (err) { result = uid_op_error(54); break; }
+ argc--; /* First argument was attribute name */
+ argv++;
+ } else if (0 != result)
+ {
+ break;
+ }
+
+ /* Get the DN of the entry being renamed */
+ err = slapi_pblock_get(pb, SLAPI_MODRDN_TARGET, &dn);
+ if (err) { result = uid_op_error(31); break; }
+
+ /* Get superior value - unimplemented in 3.0/4.0/5.0 DS */
+ err = slapi_pblock_get(pb, SLAPI_MODRDN_NEWSUPERIOR, &superior);
+ if (err) { result = uid_op_error(32); break; }
+
+ /*
+ * No superior means the entry is just renamed at
+ * its current level in the tree. Use the target DN for
+ * determining which managed tree this belongs to
+ */
+ if (!superior) superior = dn;
+
+ /* Get the new RDN - this has the attribute values */
+ err = slapi_pblock_get(pb, SLAPI_MODRDN_NEWRDN, &rdn);
+ if (err) { result = uid_op_error(33); break; }
+#ifdef DEBUG
+ slapi_log_error(SLAPI_LOG_PLUGIN, plugin_name,
+ "MODRDN newrdn=%s\n", rdn);
+#endif
+
+ /*
+ * Parse the RDN into attributes by creating a "dummy" entry
+ * and setting the attributes from the RDN.
+ *
+ * The new entry must be freed.
+ */
+ e = slapi_entry_alloc();
+ if (!e) { result = uid_op_error(34); break; }
+
+ /* NOTE: strdup on the rdn, since it will be freed when
+ * the entry is freed */
+
+ slapi_entry_set_dn(e, slapi_ch_strdup(rdn));
+
+ err = slapi_entry_add_rdn_values(e);
+ if (err)
+ {
+ slapi_log_error(SLAPI_LOG_PLUGIN, plugin_name,
+ "MODRDN bad rdn value=%s\n", rdn);
+ break; /* Bad DN */
+ }
+
+ /*
+ * Find any unique attribute data in the new RDN
+ */
+ err = slapi_entry_attr_find(e, attrName, &attr);
+ if (err) break; /* no UID attribute */
+
+ /*
+ * Passed all the requirements - this is an operation we
+ * need to enforce uniqueness on. Now find all parent entries
+ * with the marker object class, and do a search for each one.
+ */
+ if (NULL != markerObjectClass)
+ {
+ /* Subtree defined by location of marker object class */
+ result = findSubtreeAndSearch(dn, attrName, attr, NULL, dn,
+ markerObjectClass);
+ } else
+ {
+ /* Subtrees listed on invocation line */
+ result = searchAllSubtrees(argc, argv, attrName, attr, NULL, dn);
+ }
+ END
+ /* Clean-up */
+ if (e) slapi_entry_free(e);
+
+ if (result)
+ {
+ slapi_log_error(SLAPI_LOG_PLUGIN, plugin_name,
+ "MODRDN result %d\n", result);
+
+ slapi_send_ldap_result(pb, result, 0, moreInfo, 0, 0);
+ }
+
+ return (result==LDAP_SUCCESS)?0:-1;
+
+}
+
+/* ------------------------------------------------------------ */
+/*
+ * Initialize the plugin
+ *
+ * uidunique_init (the old name) is deprecated
+ */
+int
+NSUniqueAttr_Init(Slapi_PBlock *pb)
+{
+ int err = 0;
+
+ BEGIN
+ int err;
+ int argc;
+ char **argv;
+
+ /* Declare plugin version */
+ err = slapi_pblock_set(pb, SLAPI_PLUGIN_VERSION,
+ SLAPI_PLUGIN_VERSION_01);
+ if (err) break;
+
+ /*
+ * Get plugin identity and store it for later use
+ * Used for internal operations
+ */
+
+ slapi_pblock_get (pb, SLAPI_PLUGIN_IDENTITY, &plugin_identity);
+ /* PR_ASSERT (plugin_identity); */
+
+ /*
+ * Get and normalize arguments
+ */
+ err = slapi_pblock_get(pb, SLAPI_PLUGIN_ARGC, &argc);
+ if (err) break;
+
+ err = slapi_pblock_get(pb, SLAPI_PLUGIN_ARGV, &argv);
+ if (err) break;
+
+ /* First argument is the unique attribute name */
+ if (argc < 1) { err = -1; break; }
+ argv++; argc--;
+
+ for(;argc > 0;argc--, argv++)
+ slapi_dn_normalize_case(*argv);
+
+ /* Provide descriptive information */
+ err = slapi_pblock_set(pb, SLAPI_PLUGIN_DESCRIPTION,
+ (void*)&pluginDesc);
+ if (err) break;
+
+ /* Register functions */
+ err = slapi_pblock_set(pb, SLAPI_PLUGIN_PRE_ADD_FN,
+ (void*)preop_add);
+ if (err) break;
+
+ err = slapi_pblock_set(pb, SLAPI_PLUGIN_PRE_MODIFY_FN,
+ (void*)preop_modify);
+ if (err) break;
+
+ err = slapi_pblock_set(pb, SLAPI_PLUGIN_PRE_MODRDN_FN,
+ (void*)preop_modrdn);
+ if (err) break;
+
+ END
+
+ if (err) {
+ slapi_log_error(SLAPI_LOG_PLUGIN, "NSUniqueAttr_Init",
+ "Error: %d\n", err);
+ err = -1;
+ }
+ else
+ slapi_log_error(SLAPI_LOG_PLUGIN, "NSUniqueAttr_Init",
+ "plugin loaded\n");
+
+ return err;
+}
+
+int
+uidunique_init(Slapi_PBlock *pb)
+{
+ return NSUniqueAttr_Init(pb);
+}
+
+
+/* ------------------------------------------------------------ */
+/*
+ * ldap_quote_filter_value
+ *
+ * Quote the filter value according to RFC 2254 (Dec 1997)
+ *
+ * value - a UTF8 string containing the value. It may contain
+ * any of the chars needing quotes ( '(' ')' '*' '/' and NUL ).
+ * len - the length of the UTF8 value
+ * out - a buffer to recieve the converted value. May be NULL, in
+ * which case, only the length of the output is computed (and placed in
+ * outLen).
+ * maxLen - the size of the output buffer. It is an error if this length
+ * is exceeded. Ignored if out is NULL.
+ * outLen - recieves the size of the output. If an error occurs, this
+ * result is not available.
+ *
+ * Returns
+ * 0 - success
+ * -1 - failure (usually a buffer overflow)
+ */
+int /* Error value */
+ldap_quote_filter_value(
+ char *value, int len,
+ char *out, int maxLen,
+ int *outLen)
+{
+ int err;
+ char *eValue;
+ int resLen;
+#ifdef SLAPI_SUPPORTS_V3_ESCAPING
+ static char hexchars[16] = "0123456789abcdef";
+#endif
+
+ err = 0;
+ eValue = &value[len];
+ resLen = 0;
+
+ /*
+ * Convert each character in the input string
+ */
+ while(value < eValue)
+ {
+ switch(*value)
+ {
+ case '(':
+ case ')':
+ case '*':
+ case '\\':
+#ifdef SLAPI_SUPPORTS_V3_ESCAPING
+ case 0:
+#endif
+ /* Handle characters needing special escape sequences */
+
+ /* Compute size of output */
+#ifdef SLAPI_SUPPORTS_V3_ESCAPING
+ resLen += 3;
+#else
+ resLen += 2;
+#endif
+
+ /* Generate output if requested */
+ if (out)
+ {
+ /* Check for overflow */
+ if (resLen > maxLen) { err = -1; break; }
+
+ *out++ = '\\';
+#ifdef SLAPI_SUPPORTS_V3_ESCAPING
+ *out++ = hexchars[(*value >> 4) & 0xF];
+ *out++ = hexchars[*value & 0xF];
+#else
+ *out++ = *value;
+#endif
+ }
+
+ break;
+
+ default:
+ /* Compute size of output */
+ resLen += 1;
+
+ /* Generate output if requested */
+ if (out)
+ {
+ if (resLen > maxLen) { err = -1; break; }
+ *out++ = *value;
+ }
+
+ break;
+ }
+
+ if (err) break;
+
+ value++;
+ }
+
+ if (!err) *outLen = resLen;
+
+ return err;
+}
diff --git a/ldap/servers/plugins/vattrsp_template/Makefile b/ldap/servers/plugins/vattrsp_template/Makefile
new file mode 100644
index 00000000..0eb9e072
--- /dev/null
+++ b/ldap/servers/plugins/vattrsp_template/Makefile
@@ -0,0 +1,79 @@
+#
+# BEGIN COPYRIGHT BLOCK
+# Copyright 2001 Sun Microsystems, Inc.
+# Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+# All rights reserved.
+# END COPYRIGHT BLOCK
+#
+LDAP_SRC = ../../..
+MCOM_ROOT = ../../../../..
+
+NOSTDCLEAN=true # don't let nsconfig.mk define target clean
+NOSTDSTRIP=true # don't let nsconfig.mk define target strip
+NSPR20=true # probably should be defined somewhere else (not sure where)
+
+OBJDEST = $(OBJDIR)/lib/libvattrsp
+LIBDIR = $(LIB_RELDIR)
+
+include $(MCOM_ROOT)/ldapserver/nsdefs.mk
+include $(MCOM_ROOT)/ldapserver/nsconfig.mk
+include $(LDAP_SRC)/nsldap.mk
+
+ifeq ($(ARCH), WINNT)
+DEF_FILE:=./vattrsp.def
+endif
+
+VATTRSP_OBJS = vattrsp.o
+OBJS = $(addprefix $(OBJDEST)/, $(VATTRSP_OBJS))
+
+VATTRSP_DLL = vattrsp-plugin
+
+INCLUDES += -I../../slapd -I../../../include
+CFLAGS+=$(SLCFLAGS) -DSLAPD_LOGGING
+
+ifeq ($(ARCH), WINNT)
+EXTRA_LIBS_DEP += $(LIBSLAPD) $(NSPR_DEP) $(LDAPSDK_DEP)
+EXTRA_LIBS += $(NSPRLINK) $(LIBSLAPD) $(LDAP_SDK_LIBLDAP_DLL)
+VATTRSP_DLL_OBJ = $(addprefix $(OBJDEST)/, dllmain.o)
+endif
+
+ifeq ($(ARCH), AIX)
+EXTRA_LIBS_DEP += $(LIBSLAPD) $(NSPR_DEP) $(LDAPSDK_DEP)
+EXTRA_LIBS += $(LIBSLAPDLINK) $(NSPRLINK) $(LDAP_SDK_LIBLDAP_DLL)
+LD=ld
+endif
+
+ifeq ($(ARCH), HPUX)
+EXTRA_LIBS_DEP += $(LIBSLAPD_DEP) $(LDAPSDK_DEP) $(NSPR_DEP) $(SECURITY_DEP)
+EXTRA_LIBS += $(DYN_NSHTTPD) $(ADMINUTIL_LINK) $(LDAPLINK) $(SECURITYLINK) $(NSPRLINK) $(ICULINK)
+endif
+
+VATTRSP= $(addprefix $(LIBDIR)/, $(VATTRSP_DLL).$(DLL_SUFFIX))
+
+clientSDK:
+
+all: $(OBJDEST) $(LIBDIR) $(VATTRSP)
+
+ifeq ($(ARCH), WINNT)
+$(VATTRSP): $(OBJS) $(VATTRSP_DLL_OBJ) $(DEF_FILE)
+ $(LINK_DLL) $(VATTRSP_DLL_OBJ) $(EXTRA_LIBS) /DEF:$(DEF_FILE)
+else
+$(VATTRSP): $(OBJS) $(VATTRSP_DLL_OBJ)
+ $(LINK_DLL) $(VATTRSP_DLL_OBJ) $(EXTRA_LIBS)
+endif
+
+
+veryclean: clean
+
+clean:
+ $(RM) $(OBJS)
+ifeq ($(ARCH), WINNT)
+ $(RM) $(VATTRSP_DLL_OBJ)
+endif
+ $(RM) $(VATTRSP)
+
+$(OBJDEST):
+ $(MKDIR) $(OBJDEST)
+
+$(LIBDIR):
+ $(MKDIR) $(LIBDIR)
diff --git a/ldap/servers/plugins/vattrsp_template/dllmain.c b/ldap/servers/plugins/vattrsp_template/dllmain.c
new file mode 100644
index 00000000..fabf8677
--- /dev/null
+++ b/ldap/servers/plugins/vattrsp_template/dllmain.c
@@ -0,0 +1,96 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+/*
+ * Microsoft Windows specifics for BACK-LDBM DLL
+ */
+#include "ldap.h"
+#include "lber.h"
+
+
+#ifdef _WIN32
+/* Lifted from Q125688
+ * How to Port a 16-bit DLL to a Win32 DLL
+ * on the MSVC 4.0 CD
+ */
+BOOL WINAPI DllMain (HANDLE hModule, DWORD fdwReason, LPVOID lpReserved)
+{
+ WSADATA wsadata;
+
+ switch (fdwReason)
+ {
+ case DLL_PROCESS_ATTACH:
+ /* Code from LibMain inserted here. Return TRUE to keep the
+ DLL loaded or return FALSE to fail loading the DLL.
+
+ You may have to modify the code in your original LibMain to
+ account for the fact that it may be called more than once.
+ You will get one DLL_PROCESS_ATTACH for each process that
+ loads the DLL. This is different from LibMain which gets
+ called only once when the DLL is loaded. The only time this
+ is critical is when you are using shared data sections.
+ If you are using shared data sections for statically
+ allocated data, you will need to be careful to initialize it
+ only once. Check your code carefully.
+
+ Certain one-time initializations may now need to be done for
+ each process that attaches. You may also not need code from
+ your original LibMain because the operating system may now
+ be doing it for you.
+ */
+ /*
+ * 16 bit code calls UnlockData()
+ * which is mapped to UnlockSegment in windows.h
+ * in 32 bit world UnlockData is not defined anywhere
+ * UnlockSegment is mapped to GlobalUnfix in winbase.h
+ * and the docs for both UnlockSegment and GlobalUnfix say
+ * ".. function is oboslete. Segments have no meaning
+ * in the 32-bit environment". So we do nothing here.
+ */
+
+ if( errno = WSAStartup(0x0101, &wsadata ) != 0 )
+ return FALSE;
+
+ break;
+
+ case DLL_THREAD_ATTACH:
+ /* Called each time a thread is created in a process that has
+ already loaded (attached to) this DLL. Does not get called
+ for each thread that exists in the process before it loaded
+ the DLL.
+
+ Do thread-specific initialization here.
+ */
+ break;
+
+ case DLL_THREAD_DETACH:
+ /* Same as above, but called when a thread in the process
+ exits.
+
+ Do thread-specific cleanup here.
+ */
+ break;
+
+ case DLL_PROCESS_DETACH:
+ /* Code from _WEP inserted here. This code may (like the
+ LibMain) not be necessary. Check to make certain that the
+ operating system is not doing it for you.
+ */
+ WSACleanup();
+
+ break;
+ }
+ /* The return value is only used for DLL_PROCESS_ATTACH; all other
+ conditions are ignored. */
+ return TRUE; // successful DLL_PROCESS_ATTACH
+}
+#else
+int CALLBACK
+LibMain( HINSTANCE hinst, WORD wDataSeg, WORD cbHeapSize, LPSTR lpszCmdLine )
+{
+ /*UnlockData( 0 );*/
+ return( 1 );
+}
+#endif
diff --git a/ldap/servers/plugins/vattrsp_template/vattrsp.c b/ldap/servers/plugins/vattrsp_template/vattrsp.c
new file mode 100644
index 00000000..52534b26
--- /dev/null
+++ b/ldap/servers/plugins/vattrsp_template/vattrsp.c
@@ -0,0 +1,397 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+#include <stdio.h>
+#include <string.h>
+#include "portable.h"
+#include "nspr.h"
+#include "slapi-plugin.h"
+#include "slapi-private.h"
+#include <dirlite_strings.h> /* PLUGIN_MAGIC_VENDOR_STR */
+#include "dirver.h"
+#include "vattr_spi.h"
+
+/* the global plugin handle */
+static volatile vattr_sp_handle *vattr_handle = NULL;
+
+/* get file mode flags for unix */
+#ifndef _WIN32
+#include <sys/stat.h>
+#endif
+
+
+#define VATTRSP_PLUGIN_SUBSYSTEM "vattrsp-template-plugin" /* used for logging */
+
+/* function prototypes */
+int vattrsp_init( Slapi_PBlock *pb );
+static int vattrsp_start( Slapi_PBlock *pb );
+static int vattrsp_close( Slapi_PBlock *pb );
+static int vattrsp_vattr_get(
+ vattr_sp_handle *handle,
+ vattr_context *c,
+ Slapi_Entry *e,
+ char *type,
+ Slapi_ValueSet** results,
+ int *type_name_disposition,
+ char** actual_type_name,
+ int flags,
+ int *free_flags,
+ void *hint
+ );
+static int vattrsp_vattr_compare(
+ vattr_sp_handle *handle,
+ vattr_context *c,
+ Slapi_Entry *e,
+ char *type,
+ Slapi_Value *test_this,
+ int* result,
+ int flags,
+ void *hint
+ );
+static int vattrsp_vattr_types(
+ vattr_sp_handle *handle,
+ Slapi_Entry *e,
+ vattr_type_list_context *type_context,
+ int flags
+ );
+
+
+static Slapi_PluginDesc pdesc = { "vattrsp", PLUGIN_MAGIC_VENDOR_STR, PRODUCTTEXT,
+ "class of service plugin" };
+
+static void * vattrsp_plugin_identity = NULL;
+
+
+#ifdef _WIN32
+int *module_ldap_debug = 0;
+
+void plugin_init_debug_level(int *level_ptr)
+{
+ module_ldap_debug = level_ptr;
+}
+#endif
+
+/*
+ * Plugin identity mgmt
+ * --------------------
+ * Used for internal search api's
+ */
+
+void vattrsp_set_plugin_identity(void * identity)
+{
+ vattrsp_plugin_identity=identity;
+}
+
+void * vattrsp_get_plugin_identity()
+{
+ return vattrsp_plugin_identity;
+}
+
+/*
+ * vattrsp_init
+ * --------
+ * adds our callbacks to the list
+ * this is called even if the plugin is disabled
+ * so do not do anything here which enables the
+ * plugin functionality - also no other plugin has been started yet
+ * so you cant use the functionality of other plugins here
+ *
+ * When does this get called?
+ * At server start up. This is the first function in
+ * the plugin to get called, and no guarantees are made
+ * about whether the init() function of other plugins
+ * have been called. It is really only safe to register
+ * the standard SLAPI_PLUGIN_* interfaces here.
+ *
+ * returns 0 on success
+*/
+int vattrsp_init( Slapi_PBlock *pb )
+{
+ int ret = 0;
+ void * plugin_identity=NULL;
+
+ slapi_log_error( SLAPI_LOG_TRACE, VATTRSP_PLUGIN_SUBSYSTEM,"--> vattrsp_init\n");
+
+ /*
+ * Store the plugin identity for later use.
+ * Used for internal operations
+ */
+
+ slapi_pblock_get (pb, SLAPI_PLUGIN_IDENTITY, &plugin_identity);
+ PR_ASSERT (plugin_identity);
+ vattrsp_set_plugin_identity(plugin_identity);
+
+ if ( slapi_pblock_set( pb, SLAPI_PLUGIN_VERSION,
+ SLAPI_PLUGIN_VERSION_01 ) != 0 ||
+ slapi_pblock_set(pb, SLAPI_PLUGIN_START_FN,
+ (void *) vattrsp_start ) != 0 ||
+ slapi_pblock_set(pb, SLAPI_PLUGIN_CLOSE_FN,
+ (void *) vattrsp_close ) != 0 ||
+ slapi_pblock_set( pb, SLAPI_PLUGIN_DESCRIPTION,
+ (void *)&pdesc ) != 0 )
+ {
+ slapi_log_error( SLAPI_LOG_FATAL, VATTRSP_PLUGIN_SUBSYSTEM,
+ "vattrsp_init: failed to register plugin\n" );
+ ret = -1;
+ }
+
+ slapi_log_error( SLAPI_LOG_TRACE, VATTRSP_PLUGIN_SUBSYSTEM,"<-- vattrsp_init\n");
+ return ret;
+}
+
+
+/*
+ * vattrsp_start
+ * ---------
+ * This is called after vattrsp_init, this is where plugin init starts.
+ * Dependencies on other plugins have been satisfied so
+ * feel free to use them e.g. statechange plugin to keep
+ * an eye on configuration changes
+ *
+ * pb should contain SLAPI_TARGET_DN, which is the dn
+ * of the entry representing this plugin, usually in
+ * cn=plugins, cn=config. Use this to get configuration
+ * specific to your plugin from the entry
+ *
+ * When does this get called?
+ * At server start up, after the vattrsp_init() function is
+ * called and when the start function of all plugins this one
+ * depends on have been called. It is safe to rely
+ * on dependencies from now on e.g. perform search operations
+ *
+ * returns 0 on success
+*/
+int vattrsp_start( Slapi_PBlock *pb )
+{
+ int ret = 0;
+
+ slapi_log_error( SLAPI_LOG_TRACE, VATTRSP_PLUGIN_SUBSYSTEM,"--> vattrsp_start\n");
+
+ /* register this vattr service provider with vattr subsystem */
+ if (slapi_vattrspi_register((vattr_sp_handle **)&vattr_handle,
+ vattrsp_vattr_get,
+ vattrsp_vattr_compare,
+ vattrsp_vattr_types) != 0)
+ {
+ slapi_log_error( SLAPI_LOG_FATAL, VATTRSP_PLUGIN_SUBSYSTEM,
+ "vattrsp_start: cannot register as service provider\n" );
+ ret = -1;
+ goto out;
+ }
+
+ /* register a vattr */
+ /* TODO: change dummyAttr to your attribute type,
+ * or write some configuration code which discovers
+ * the attributes to register
+ */
+ slapi_vattrspi_regattr((vattr_sp_handle *)vattr_handle, "dummyAttr", NULL, NULL);
+
+out:
+ slapi_log_error( SLAPI_LOG_TRACE, VATTRSP_PLUGIN_SUBSYSTEM,"<-- vattrsp_start\n");
+ return ret;
+}
+
+/*
+ * vattrsp_close
+ * ---------
+ * closes down the plugin
+ *
+ * When does this get called?
+ * On server shutdown
+ *
+ * returns 0 on success
+*/
+int vattrsp_close( Slapi_PBlock *pb )
+{
+ slapi_log_error( SLAPI_LOG_TRACE, VATTRSP_PLUGIN_SUBSYSTEM,"--> vattrsp_close\n");
+
+ /* clean up stuff here */
+
+ slapi_log_error( SLAPI_LOG_TRACE, VATTRSP_PLUGIN_SUBSYSTEM,"<-- vattrsp_close\n");
+
+ return 0;
+}
+
+
+/*
+ * vattrsp_vattr_get
+ * -----------------
+ * vattr subsystem is requesting the value(s) for "type"
+ *
+ * When does this get called?
+ * Whenever a client requests the attribute "type"
+ * this may be indirectly by a request for all
+ * attributes
+ *
+ * returns 0 on success
+ */
+int vattrsp_vattr_get(
+ vattr_sp_handle *handle, /* pass through to subsequent vattr calls */
+ vattr_context *c, /* opaque context, pass through to subsequent vattr calls */
+ Slapi_Entry *e, /* the entry that the values are for */
+ char *type, /* the type that the values are requested for */
+ Slapi_ValueSet** results, /* put the values here */
+ int *type_name_disposition, /* whether the type is a sub-type etc. */
+ char** actual_type_name, /* maybe you call this another type */
+ int flags, /* see slapi-plugin.h to support these flags */
+ int *free_flags, /* let vattr subsystem know if you supplied value copies it must free */
+ void *hint /* opaque hint, pass through to subsequent vattr calls */
+ )
+{
+ int ret = SLAPI_VIRTUALATTRS_NOT_FOUND;
+
+ slapi_log_error( SLAPI_LOG_TRACE, VATTRSP_PLUGIN_SUBSYSTEM,"--> vattrsp_vattr_get\n");
+
+ /* usual to schema check an attribute
+ * there may be sanity checks which can
+ * be done prior to this "relatively"
+ * slow function to ensure least work done
+ * to fail
+ */
+ if(!slapi_vattr_schema_check_type(e, type))
+ return ret;
+
+ /* TODO: do your thing, resolve the attribute */
+ /* some vattr sps may look after more than one
+ * attribute, this one does not, so no need to
+ * check against "type", we know its our "dummyAttr"
+ */
+ {
+ /* TODO: replace this with your resolver */
+ Slapi_Value *val = slapi_value_new_string("dummyValue");
+
+ *results = slapi_valueset_new();
+ slapi_valueset_add_value(*results, val);
+
+ ret = 0;
+ }
+
+ if(ret == 0)
+ {
+ /* TODO: set *free_flags
+ * if allocated memory for values
+ * use: SLAPI_VIRTUALATTRS_RETURNED_COPIES
+ *
+ * otherwise, if you can guarantee that
+ * this value will live beyond this operation
+ * use: SLAPI_VIRTUALATTRS_RETURNED_POINTERS
+ */
+ *free_flags = SLAPI_VIRTUALATTRS_RETURNED_COPIES;
+
+ /* say the type is the same as the one requested
+ * could be your vattr needs to switch types, say to
+ * make one type look like another or something, but
+ * that is unusual
+ */
+ *actual_type_name = slapi_ch_strdup(type);
+
+ /* TODO: set *type_name_disposition
+ * if the type matched exactly or it
+ * is an alias for the type then
+ * use: SLAPI_VIRTUALATTRS_TYPE_NAME_MATCHED_EXACTLY_OR_ALIAS
+ *
+ * otherwise, the actual_type_name is a subtype
+ * of type
+ * use: SLAPI_VIRTUALATTRS_TYPE_NAME_MATCHED_SUBTYPE
+ */
+ *type_name_disposition = SLAPI_VIRTUALATTRS_TYPE_NAME_MATCHED_EXACTLY_OR_ALIAS;
+ }
+
+ slapi_log_error( SLAPI_LOG_TRACE, VATTRSP_PLUGIN_SUBSYSTEM,"<-- vattrsp_cache_vattr_get\n");
+ return ret;
+}
+
+
+
+/*
+ * vattrsp_vattr_compare
+ * ---------------------
+ * vattr subsystem is requesting that the values are compared
+ *
+ * When does this get called?
+ * When an LDAP compare operation against this
+ * type is requested by the client
+ *
+ * returns 0 on success
+ */
+int vattrsp_vattr_compare(
+ vattr_sp_handle *handle, /* pass through to subsequent vattr calls */
+ vattr_context *c, /* opaque context, pass through to subsequent vattr calls */
+ Slapi_Entry *e, /* the entry that the values are for */
+ char *type, /* the type that the values belong to */
+ Slapi_Value *test_this, /* the value to compare against */
+ int* result, /* 1 for matched, 0 for not matched */
+ int flags, /* see slapi-plugin.h to support these flags */
+ void *hint /* opaque hint, pass through to subsequent vattr calls */
+ )
+{
+ int ret = SLAPI_VIRTUALATTRS_NOT_FOUND; /* assume failure */
+
+ slapi_log_error( SLAPI_LOG_TRACE, VATTRSP_PLUGIN_SUBSYSTEM,"--> vattrsp_vattr_compare\n");
+
+ /* TODO: do your thing, compare the attribute */
+
+ slapi_log_error( SLAPI_LOG_TRACE, VATTRSP_PLUGIN_SUBSYSTEM,"<-- vattrsp_vattr_compare\n");
+ return ret;
+}
+
+
+
+/*
+ * vattrsp_vattr_types
+ * -------------------
+ * vattr subsystem is requesting the types that you
+ * promise to supply values for if it calls
+ * vattrsp_vattr_get() with each type.
+ * Guesses are not good enough, the wrong answer
+ * effects what happens when all attributes
+ * are requested in the LDAP search operation.
+ * If you do not own up to an attribute,
+ * vattrsp_vattr_get() will never get called for it.
+ * If you say you will supply an attribute but
+ * vattrsp_vattr_get() does not supply a value
+ * then the attribute is returned, but *with* *no*
+ * *values*
+ *
+ * When does this get called?
+ * Only when all attributes are requested by the client
+ * and just prior to multiple calls to vattrsp_vattr_get()
+ *
+ * returns 0 on success
+ */
+int vattrsp_vattr_types(
+ vattr_sp_handle *handle, /* pass through to subsequent vattr calls */
+ Slapi_Entry *e, /* the entry that the types should have values for */
+ vattr_type_list_context *type_context, /* where we put the types */
+ int flags /* see slapi-plugin.h to support these flags */
+ )
+{
+ int ret = 0; /* assume success */
+ char *attr = "dummyAttr"; /* an attribute type that we will deliver */
+ int props = 0; /* properties of the attribute, make this SLAPI_ATTR_FLAG_OPATTR for operational attributes */
+
+ slapi_log_error( SLAPI_LOG_TRACE, VATTRSP_PLUGIN_SUBSYSTEM,"--> vattrsp_vattr_types\n");
+
+ /* TODO: for each type you will supply... */
+ if(ret)
+ {
+ /* ...do this */
+
+ /* entry contains this attr */
+ vattr_type_thang thang = {0};
+
+ thang.type_name = strcpy(thang.type_name,attr);
+ thang.type_flags = props;
+
+ /* add the type to the type context */
+ slapi_vattrspi_add_type(type_context,&thang,0);
+ }
+
+ slapi_log_error( SLAPI_LOG_TRACE, VATTRSP_PLUGIN_SUBSYSTEM,"<-- vattrsp_vattr_types\n");
+ return ret;
+}
+
+
+
diff --git a/ldap/servers/plugins/vattrsp_template/vattrsp.def b/ldap/servers/plugins/vattrsp_template/vattrsp.def
new file mode 100644
index 00000000..a0e2d8c0
--- /dev/null
+++ b/ldap/servers/plugins/vattrsp_template/vattrsp.def
@@ -0,0 +1,10 @@
+; BEGIN COPYRIGHT BLOCK
+; Copyright 2001 Sun Microsystems, Inc.
+; Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+; All rights reserved.
+; END COPYRIGHT BLOCK
+;
+DESCRIPTION 'Netscape Directory Server 6.2.1 Virtual Attribute Service Provider Template Plugin'
+EXPORTS
+ vattrsp_init @2
+ plugin_init_debug_level @3
diff --git a/ldap/servers/plugins/views/Makefile b/ldap/servers/plugins/views/Makefile
new file mode 100644
index 00000000..495c7d97
--- /dev/null
+++ b/ldap/servers/plugins/views/Makefile
@@ -0,0 +1,79 @@
+#
+# BEGIN COPYRIGHT BLOCK
+# Copyright 2001 Sun Microsystems, Inc.
+# Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+# All rights reserved.
+# END COPYRIGHT BLOCK
+#
+LDAP_SRC = ../../..
+MCOM_ROOT = ../../../../..
+
+NOSTDCLEAN=true # don't let nsconfig.mk define target clean
+NOSTDSTRIP=true # don't let nsconfig.mk define target strip
+NSPR20=true # probably should be defined somewhere else (not sure where)
+
+OBJDEST = $(OBJDIR)/lib/libviews
+LIBDIR = $(LIB_RELDIR)
+
+include $(MCOM_ROOT)/ldapserver/nsdefs.mk
+include $(MCOM_ROOT)/ldapserver/nsconfig.mk
+include $(LDAP_SRC)/nsldap.mk
+
+ifeq ($(ARCH), WINNT)
+DEF_FILE:=./views.def
+endif
+
+VIEWS_OBJS = views.o
+OBJS = $(addprefix $(OBJDEST)/, $(VIEWS_OBJS))
+
+VIEWS_DLL = views-plugin
+
+INCLUDES += -I../../slapd -I../../../include
+CFLAGS+=$(SLCFLAGS) -DSLAPD_LOGGING
+
+ifeq ($(ARCH), WINNT)
+EXTRA_LIBS_DEP += $(LIBSLAPD) $(NSPR_DEP) $(LDAPSDK_DEP)
+EXTRA_LIBS += $(NSPRLINK) $(LIBSLAPD) $(LDAP_SDK_LIBLDAP_DLL)
+VIEWS_DLL_OBJ = $(addprefix $(OBJDEST)/, dllmain.o)
+endif
+
+ifeq ($(ARCH), HPUX)
+EXTRA_LIBS_DEP += $(LIBSLAPD_DEP) $(LDAPSDK_DEP) $(NSPR_DEP) $(SECURITY_DEP)
+EXTRA_LIBS += $(DYN_NSHTTPD) $(ADMINUTIL_LINK) $(LDAPLINK) $(SECURITYLINK) $(NSPRLINK) $(ICULINK)
+endif
+
+ifeq ($(ARCH), AIX)
+LD=ld
+EXTRA_LIBS_DEP += $(LIBSLAPD) $(NSPR_DEP) $(LDAPSDK_DEP)
+EXTRA_LIBS += $(LIBSLAPDLINK) $(NSPRLINK) $(LDAP_SDK_LIBLDAP_DLL)
+endif
+
+VIEWS= $(addprefix $(LIBDIR)/, $(VIEWS_DLL).$(DLL_SUFFIX))
+
+clientSDK:
+
+all: $(OBJDEST) $(LIBDIR) $(VIEWS)
+
+ifeq ($(ARCH), WINNT)
+$(VIEWS): $(OBJS) $(VIEWS_DLL_OBJ) $(DEF_FILE)
+ $(LINK_DLL) $(VIEWS_DLL_OBJ) $(EXTRA_LIBS) /DEF:$(DEF_FILE)
+else
+$(VIEWS): $(OBJS) $(VIEWS_DLL_OBJ)
+ $(LINK_DLL) $(VIEWS_DLL_OBJ) $(EXTRA_LIBS)
+endif
+
+
+veryclean: clean
+
+clean:
+ $(RM) $(OBJS)
+ifeq ($(ARCH), WINNT)
+ $(RM) $(VIEWS_DLL_OBJ)
+endif
+ $(RM) $(VIEWS)
+
+$(OBJDEST):
+ $(MKDIR) $(OBJDEST)
+
+$(LIBDIR):
+ $(MKDIR) $(LIBDIR)
diff --git a/ldap/servers/plugins/views/dllmain.c b/ldap/servers/plugins/views/dllmain.c
new file mode 100644
index 00000000..fabf8677
--- /dev/null
+++ b/ldap/servers/plugins/views/dllmain.c
@@ -0,0 +1,96 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+/*
+ * Microsoft Windows specifics for BACK-LDBM DLL
+ */
+#include "ldap.h"
+#include "lber.h"
+
+
+#ifdef _WIN32
+/* Lifted from Q125688
+ * How to Port a 16-bit DLL to a Win32 DLL
+ * on the MSVC 4.0 CD
+ */
+BOOL WINAPI DllMain (HANDLE hModule, DWORD fdwReason, LPVOID lpReserved)
+{
+ WSADATA wsadata;
+
+ switch (fdwReason)
+ {
+ case DLL_PROCESS_ATTACH:
+ /* Code from LibMain inserted here. Return TRUE to keep the
+ DLL loaded or return FALSE to fail loading the DLL.
+
+ You may have to modify the code in your original LibMain to
+ account for the fact that it may be called more than once.
+ You will get one DLL_PROCESS_ATTACH for each process that
+ loads the DLL. This is different from LibMain which gets
+ called only once when the DLL is loaded. The only time this
+ is critical is when you are using shared data sections.
+ If you are using shared data sections for statically
+ allocated data, you will need to be careful to initialize it
+ only once. Check your code carefully.
+
+ Certain one-time initializations may now need to be done for
+ each process that attaches. You may also not need code from
+ your original LibMain because the operating system may now
+ be doing it for you.
+ */
+ /*
+ * 16 bit code calls UnlockData()
+ * which is mapped to UnlockSegment in windows.h
+ * in 32 bit world UnlockData is not defined anywhere
+ * UnlockSegment is mapped to GlobalUnfix in winbase.h
+ * and the docs for both UnlockSegment and GlobalUnfix say
+ * ".. function is oboslete. Segments have no meaning
+ * in the 32-bit environment". So we do nothing here.
+ */
+
+ if( errno = WSAStartup(0x0101, &wsadata ) != 0 )
+ return FALSE;
+
+ break;
+
+ case DLL_THREAD_ATTACH:
+ /* Called each time a thread is created in a process that has
+ already loaded (attached to) this DLL. Does not get called
+ for each thread that exists in the process before it loaded
+ the DLL.
+
+ Do thread-specific initialization here.
+ */
+ break;
+
+ case DLL_THREAD_DETACH:
+ /* Same as above, but called when a thread in the process
+ exits.
+
+ Do thread-specific cleanup here.
+ */
+ break;
+
+ case DLL_PROCESS_DETACH:
+ /* Code from _WEP inserted here. This code may (like the
+ LibMain) not be necessary. Check to make certain that the
+ operating system is not doing it for you.
+ */
+ WSACleanup();
+
+ break;
+ }
+ /* The return value is only used for DLL_PROCESS_ATTACH; all other
+ conditions are ignored. */
+ return TRUE; // successful DLL_PROCESS_ATTACH
+}
+#else
+int CALLBACK
+LibMain( HINSTANCE hinst, WORD wDataSeg, WORD cbHeapSize, LPSTR lpszCmdLine )
+{
+ /*UnlockData( 0 );*/
+ return( 1 );
+}
+#endif
diff --git a/ldap/servers/plugins/views/views.c b/ldap/servers/plugins/views/views.c
new file mode 100644
index 00000000..b052167c
--- /dev/null
+++ b/ldap/servers/plugins/views/views.c
@@ -0,0 +1,1779 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+/* plugin which implements directory server views */
+
+#include <stdio.h>
+#include <string.h>
+#include "portable.h"
+#include "slapi-plugin.h"
+#include <dirlite_strings.h> /* PLUGIN_MAGIC_VENDOR_STR */
+#include "dirver.h"
+#include "statechange.h"
+#include "views.h"
+
+#include "slapi-plugin-compat4.h"
+#include "slapi-private.h"
+
+
+#define VIEW_OBJECTCLASS "nsView"
+#define VIEW_FILTER_ATTR "nsViewFilter"
+#define STATECHANGE_VIEWS_ID "Views"
+#define STATECHANGE_VIEWS_CONFG_FILTER "objectclass=" VIEW_OBJECTCLASS
+
+/* get file mode flags for unix */
+#ifndef _WIN32
+#include <sys/stat.h>
+#endif
+
+#define VIEWS_PLUGIN_SUBSYSTEM "views-plugin" /* used for logging */
+
+/* cache data structs */
+
+struct _viewLinkedList
+{
+ void *pNext;
+ void *pPrev;
+};
+typedef struct _viewLinkedList viewLinkedList;
+
+#if defined(DEBUG)
+#define _VIEW_DEBUG_FILTERS /* Turning on hurts performance */
+#endif
+
+struct _viewEntry
+{
+ viewLinkedList list;
+ char *pDn;
+ char *viewfilter; /* the raw view */
+ Slapi_Filter *includeAncestorFiltersFilter; /* the filter with all ancestor filters */
+ Slapi_Filter *excludeAllButDescendentViewsFilter; /* for building the view of views */
+ Slapi_Filter *excludeChildFiltersFilter; /* NOT all children views, for one level searches */
+ Slapi_Filter *excludeGrandChildViewsFilter; /* view filter for one level searches */
+ Slapi_Filter *includeChildViewsFilter; /* view filter for subtree searches */
+#ifdef _VIEW_DEBUG_FILTERS
+ /* monitor the cached filters with these */
+ char includeAncestorFiltersFilter_str[1024]; /* the filter with all ancestor filters */
+ char excludeAllButDescendentViewsFilter_str[1024]; /* for building the view of views */
+ char excludeChildFiltersFilter_str[1024]; /* NOT all children views, for one level searches */
+ char excludeGrandChildViewsFilter_str[1024]; /* view filter for one level searches */
+ char includeChildViewsFilter_str[1024]; /* view filter for subtree searches */
+#endif
+ char *pSearch_base; /* the parent of the top most view */
+ void *pParent;
+ void **pChildren;
+ int child_count;
+ unsigned long entryid; /* unique identifier for this entry */
+ unsigned long parentid; /* unique identifier for the parent entry */
+};
+typedef struct _viewEntry viewEntry;
+
+struct _globalViewCache
+{
+ viewEntry *pCacheViews;
+ viewEntry **ppViewIndex;
+ int cache_built;
+ int view_count;
+ PRThread *currentUpdaterThread;
+};
+typedef struct _globalViewCache golbalViewCache;
+
+static golbalViewCache theCache;
+
+/* other function prototypes */
+int views_init( Slapi_PBlock *pb );
+static int views_start( Slapi_PBlock *pb );
+static int views_close( Slapi_PBlock *pb );
+static int views_cache_create();
+static void views_update_views_cache( Slapi_Entry *e, char *dn, int modtype, Slapi_PBlock *pb, void *caller_data );
+static int views_cache_build_view_list(viewEntry **pViews);
+static int views_cache_index();
+static int views_dn_views_cb (Slapi_Entry* e, void *callback_data);
+static int views_cache_add_dn_views(char *dn, viewEntry **pViews);
+static void views_cache_add_ll_entry(void** attrval, void *theVal);
+static void views_cache_discover_parent(viewEntry *pView);
+static void views_cache_discover_children(viewEntry *pView);
+static void views_cache_discover_view_scope(viewEntry *pView);
+static void views_cache_create_applied_filter(viewEntry *pView);
+static void views_cache_create_exclusion_filter(viewEntry *pView);
+static void views_cache_create_inclusion_filter(viewEntry *pView);
+Slapi_Filter *views_cache_create_descendent_filter(viewEntry *ancestor, PRBool useID);
+static int view_search_rewrite_callback(Slapi_PBlock *pb);
+static void views_cache_backend_state_change(void *handle, char *be_name, int old_be_state, int new_be_state);
+static void views_cache_act_on_change_thread(void *arg);
+static viewEntry *views_cache_find_view(char *view);
+
+/* our api broker published api */
+static void *api[3];
+static int _internal_api_views_entry_exists(char *view_dn, Slapi_Entry *e);
+static int _internal_api_views_entry_dn_exists(char *view_dn, char *e_dn);
+static int _internal_api_views_entry_exists_general(char *view_dn, Slapi_Entry *e, char *e_dn);
+
+
+static Slapi_PluginDesc pdesc = { "views", PLUGIN_MAGIC_VENDOR_STR, PRODUCTTEXT,
+ "virtual directory information tree views plugin" };
+
+static void * view_plugin_identity = NULL;
+
+static PRRWLock *g_views_cache_lock;
+
+#ifdef _WIN32
+int *module_ldap_debug = 0;
+
+void plugin_init_debug_level(int *level_ptr)
+{
+ module_ldap_debug = level_ptr;
+}
+#endif
+
+/*
+** Plugin identity mgmt
+*/
+
+void view_set_plugin_identity(void * identity)
+{
+ view_plugin_identity=identity;
+}
+
+void * view_get_plugin_identity()
+{
+ return view_plugin_identity;
+}
+
+/*
+ views_init
+ --------
+ adds our callbacks to the list
+*/
+int views_init( Slapi_PBlock *pb )
+{
+ int ret = 0;
+ void * plugin_identity=NULL;
+
+ slapi_log_error( SLAPI_LOG_TRACE, VIEWS_PLUGIN_SUBSYSTEM, "--> views_init\n");
+
+ /*
+ ** Store the plugin identity for later use.
+ ** Used for internal operations
+ */
+
+ slapi_pblock_get (pb, SLAPI_PLUGIN_IDENTITY, &plugin_identity);
+ view_set_plugin_identity(plugin_identity);
+
+ if ( slapi_pblock_set( pb, SLAPI_PLUGIN_VERSION,
+ SLAPI_PLUGIN_VERSION_01 ) != 0 ||
+ slapi_pblock_set(pb, SLAPI_PLUGIN_START_FN,
+ (void *) views_start ) != 0 ||
+ slapi_pblock_set(pb, SLAPI_PLUGIN_CLOSE_FN,
+ (void *) views_close ) != 0 ||
+ slapi_pblock_set( pb, SLAPI_PLUGIN_DESCRIPTION,
+ (void *)&pdesc ) != 0 )
+ {
+ slapi_log_error( SLAPI_LOG_FATAL, VIEWS_PLUGIN_SUBSYSTEM,
+ "views_init: failed to register plugin\n" );
+ ret = -1;
+ }
+
+ slapi_log_error( SLAPI_LOG_TRACE, VIEWS_PLUGIN_SUBSYSTEM, "<-- views_init\n");
+ return ret;
+}
+
+void views_read_lock()
+{
+ PR_RWLock_Rlock(g_views_cache_lock);
+}
+
+void views_write_lock()
+{
+ PR_RWLock_Wlock(g_views_cache_lock);
+}
+
+void views_unlock()
+{
+ PR_RWLock_Unlock(g_views_cache_lock);
+}
+
+/*
+ views_start
+ ---------
+ This function publishes the interface for this plugin
+*/
+static int views_start( Slapi_PBlock *pb )
+{
+ int ret = 0;
+ void **statechange_api;
+
+ slapi_log_error( SLAPI_LOG_TRACE, VIEWS_PLUGIN_SUBSYSTEM, "--> views_start\n");
+
+ theCache.cache_built = 0;
+ g_views_cache_lock = PR_NewRWLock(PR_RWLOCK_RANK_NONE, "views");
+
+ /* first register our backend state change func (we'll use func pointer as handle) */
+ slapi_register_backend_state_change((void *)views_cache_backend_state_change, views_cache_backend_state_change);
+
+ /* create the view cache */
+
+ views_cache_create();
+
+ /* register callbacks for filter and search rewriting */
+ slapi_compute_add_search_rewriter(view_search_rewrite_callback);
+
+ /* register for state changes to view configuration */
+ if(!slapi_apib_get_interface(StateChange_v1_0_GUID, &statechange_api))
+ {
+ statechange_register(statechange_api, STATECHANGE_VIEWS_ID, NULL, STATECHANGE_VIEWS_CONFG_FILTER, NULL, views_update_views_cache);
+ }
+
+ /* register our api so that other subsystems can be views aware */
+ api[0] = NULL; /* reserved for api broker use */
+ api[1] = (void *)_internal_api_views_entry_exists;
+ api[2] = (void *)_internal_api_views_entry_dn_exists;
+
+ if( slapi_apib_register(Views_v1_0_GUID, api) )
+ {
+ slapi_log_error( SLAPI_LOG_FATAL, VIEWS_PLUGIN_SUBSYSTEM, "views: failed to publish views interface\n");
+ ret = -1;
+ }
+
+ slapi_log_error( SLAPI_LOG_TRACE, VIEWS_PLUGIN_SUBSYSTEM, "<-- views_start\n");
+ return ret;
+}
+
+/* _internal_api_views_entry_exists()
+ * ----------------------------------
+ * externally published api to allow other subsystems to
+ * be views aware. Given a view and an entry, this function
+ * returns PR_TRUE if the entry would be returned by a subtree
+ * search on the view, PR_FALSE otherwise.
+ */
+static int _internal_api_views_entry_exists(char *view_dn, Slapi_Entry *e)
+{
+ return _internal_api_views_entry_exists_general(view_dn, e, NULL);
+}
+
+static int _internal_api_views_entry_dn_exists(char *view_dn, char *e_dn)
+{
+ return _internal_api_views_entry_exists_general(view_dn, NULL, e_dn);
+}
+
+static int _internal_api_views_entry_exists_general(char *view_dn, Slapi_Entry *e, char *e_dn)
+{
+ int ret = 0;
+ viewEntry *view;
+ char *dn;
+
+ /* there are two levels of scope for a view,
+ * from the parent of the view without a view filter
+ * and the parent of the top most view including a
+ * view filter - either match will do
+ */
+
+ /* find the view */
+ view = views_cache_find_view(view_dn);
+ if(0==view)
+ {
+ /* this is not the entry you are looking for */
+ goto bail;
+ }
+
+ /* normal scope - is the view an ancestor of the entry */
+ if(e_dn)
+ dn = e_dn;
+ else
+ dn = slapi_entry_get_ndn(e);
+
+ if(slapi_dn_issuffix(dn, view_dn))
+ {
+ /* this entry is physically contained in the view hiearchy */
+ ret = -1;
+ goto bail;
+ }
+
+ /* view scope - view hiearchy scope plus view filter */
+ if(slapi_dn_issuffix(dn, view->pSearch_base))
+ {
+ if(0==e)
+ {
+ Slapi_DN *sdn = slapi_sdn_new_dn_byref(dn);
+
+ slapi_search_internal_get_entry( sdn, NULL, &e , view_get_plugin_identity());
+
+ slapi_sdn_free(&sdn);
+ }
+
+ /* so far so good, apply filter */
+ if(0==slapi_filter_test_simple(e,view->includeAncestorFiltersFilter))
+ {
+ /* this entry would appear in the view */
+ ret = -1;
+ }
+ }
+
+bail:
+ return ret;
+}
+
+void views_cache_free()
+{
+ viewEntry *head = theCache.pCacheViews;
+ viewEntry *current;
+
+ slapi_log_error( SLAPI_LOG_TRACE, VIEWS_PLUGIN_SUBSYSTEM, "--> views_cache_free\n");
+
+ /* free the cache */
+ current = head;
+
+ while(current != NULL)
+ {
+ viewEntry *theView = current;
+ current = current->list.pNext;
+
+ /* free the view */
+ slapi_ch_free((void**)&theView->pDn);
+ slapi_ch_free((void**)&theView->viewfilter);
+ slapi_filter_free(theView->includeAncestorFiltersFilter,1);
+ slapi_filter_free(theView->excludeAllButDescendentViewsFilter,1);
+ slapi_filter_free(theView->excludeChildFiltersFilter,1);
+ slapi_filter_free(theView->excludeGrandChildViewsFilter,1);
+ slapi_filter_free(theView->includeChildViewsFilter,1);
+ slapi_ch_free((void**)&theView->pSearch_base);
+ slapi_ch_free((void**)&theView->pChildren);
+ slapi_ch_free((void**)&theView);
+ }
+
+ theCache.pCacheViews = NULL;
+
+ slapi_ch_free((void**)&theCache.ppViewIndex);
+
+ theCache.view_count = 0;
+
+ slapi_log_error( SLAPI_LOG_TRACE, VIEWS_PLUGIN_SUBSYSTEM, "<-- views_cache_free\n");
+}
+
+/*
+ views_close
+ ---------
+ unregisters the interface for this plugin
+*/
+static int views_close( Slapi_PBlock *pb )
+{
+ slapi_log_error( SLAPI_LOG_TRACE, VIEWS_PLUGIN_SUBSYSTEM, "--> views_close\n");
+
+ /* unregister backend state change notification */
+ slapi_unregister_backend_state_change((void *)views_cache_backend_state_change);
+
+ views_cache_free();
+
+ slapi_log_error( SLAPI_LOG_TRACE, VIEWS_PLUGIN_SUBSYSTEM, "<-- views_close\n");
+
+ return 0;
+}
+
+
+/*
+ views_cache_create
+ ---------------------
+ Walks the views in the DIT and populates the cache.
+*/
+static int views_cache_create()
+{
+ int ret = -1;
+ static int firstTime = 1;
+
+ slapi_log_error( SLAPI_LOG_TRACE, VIEWS_PLUGIN_SUBSYSTEM, "--> views_cache_create\n");
+
+
+ /* lock cache */
+ views_write_lock();
+
+ theCache.currentUpdaterThread = PR_GetCurrentThread(); /* to avoid deadlock */
+
+ if(theCache.pCacheViews)
+ {
+ /* need to get rid of the existing views */
+ views_cache_free();
+ }
+
+ /* grab the view entries */
+ ret = views_cache_build_view_list(&(theCache.pCacheViews));
+ if(!ret && theCache.pCacheViews)
+ {
+ viewEntry *head = theCache.pCacheViews;
+ viewEntry *current;
+
+ /* OK, we have a basic cache, now we need to
+ * fix up parent and children pointers
+ */
+ for(current = head; current != NULL; current = current->list.pNext)
+ {
+ views_cache_discover_parent(current);
+ views_cache_discover_children(current);
+ }
+
+ /* scope of views and cache search filters... */
+ for(current = head; current != NULL; current = current->list.pNext)
+ {
+ views_cache_discover_view_scope(current);
+ views_cache_create_applied_filter(current);
+ views_cache_create_exclusion_filter(current);
+ views_cache_create_inclusion_filter(current);
+ }
+
+ /* create the view index */
+ ret = views_cache_index();
+ if(ret != 0)
+ {
+ /* currently we cannot go on without the indexes */
+ slapi_log_error(SLAPI_LOG_FATAL, VIEWS_PLUGIN_SUBSYSTEM, "views_cache_create: failed to index cache\n");
+ }
+ else
+ theCache.cache_built = 1;
+ }
+ else
+ {
+ /* its ok to not have views to cache */
+ theCache.cache_built = 0;
+ ret = 0;
+ }
+
+ theCache.currentUpdaterThread = 0;
+
+ /* unlock cache */
+ views_unlock();
+
+ slapi_log_error( SLAPI_LOG_TRACE, VIEWS_PLUGIN_SUBSYSTEM, "<-- views_cache_create\n");
+ return ret;
+}
+
+/*
+ * views_cache_view_compare
+ * -----------------------
+ * compares the dns of two views - used for sorting the index
+ */
+int views_cache_view_compare(const void *e1, const void *e2)
+{
+ int ret;
+ Slapi_DN *dn1 = slapi_sdn_new_dn_byval((*(viewEntry**)e1)->pDn);
+ Slapi_DN *dn2 = slapi_sdn_new_dn_byval((*(viewEntry**)e2)->pDn);
+
+ ret = slapi_sdn_compare(dn1, dn2);
+
+ slapi_sdn_free(&dn1);
+ slapi_sdn_free(&dn2);
+
+ return ret;
+}
+
+/*
+ * views_cache_dn_compare
+ * -----------------------
+ * compares a dn with the dn of a view - used for searching the index
+ */
+int views_cache_dn_compare(const void *e1, const void *e2)
+{
+ int ret;
+ Slapi_DN *dn1 = slapi_sdn_new_dn_byval((char*)e1);
+ Slapi_DN *dn2 = slapi_sdn_new_dn_byval(((viewEntry*)e2)->pDn);
+
+ ret = slapi_sdn_compare(dn1, dn2);
+
+ slapi_sdn_free(&dn1);
+ slapi_sdn_free(&dn2);
+
+ return ret;
+}
+/*
+ * views_cache_index
+ * ----------------
+ * indexes the cache for fast look up of views
+ */
+static int views_cache_index()
+{
+ int ret = -1;
+ int i;
+ viewEntry *theView = theCache.pCacheViews;
+ viewEntry *current = 0;
+
+ if(theCache.ppViewIndex)
+ slapi_ch_free((void**)&theCache.ppViewIndex);
+
+ theCache.view_count = 0;
+
+ /* lets count the views */
+ for(current = theCache.pCacheViews; current != NULL; current = current->list.pNext)
+ theCache.view_count++;
+
+
+ theCache.ppViewIndex = (viewEntry**)calloc(theCache.view_count, sizeof(viewEntry*));
+ if(theCache.ppViewIndex)
+ {
+ /* copy over the views */
+ for(i=0; i<theCache.view_count; i++)
+ {
+ theCache.ppViewIndex[i] = theView;
+ theView = theView->list.pNext;
+ }
+
+ /* sort the views */
+ qsort(theCache.ppViewIndex, theCache.view_count, sizeof(viewEntry*), views_cache_view_compare);
+
+ ret = 0;
+ }
+
+ return ret;
+}
+
+
+/*
+ views_cache_view_index_bsearch - RECURSIVE
+ ----------------------------------------
+ performs a binary search on the cache view index
+ return -1 if key is not found
+*/
+viewEntry *views_cache_view_index_bsearch( const char *key, int lower, int upper )
+{
+ viewEntry *ret = 0;
+ int index = 0;
+ int compare_ret = 0;
+
+ if(upper >= lower)
+ {
+ if(upper != 0)
+ index = ((upper-lower)/2) + lower;
+ else
+ index = 0;
+
+ compare_ret = views_cache_dn_compare(key, theCache.ppViewIndex[index]);
+
+ if(!compare_ret)
+ {
+ ret = (theCache.ppViewIndex)[index];
+ }
+ else
+ {
+ /* seek elsewhere */
+ if(compare_ret < 0)
+ {
+ /* take the low road */
+ ret = views_cache_view_index_bsearch(key, lower, index-1);
+ }
+ else
+ {
+ /* go high */
+ ret = views_cache_view_index_bsearch(key, index+1, upper);
+ }
+ }
+ }
+
+ return ret;
+}
+
+
+/*
+ views_cache_find_view
+ -------------------
+ searches for a view, and if found returns it, null otherwise
+*/
+static viewEntry *views_cache_find_view(char *view)
+{
+ viewEntry *ret = 0; /* assume failure */
+
+ if(theCache.view_count != 1)
+ ret = views_cache_view_index_bsearch(view, 0, theCache.view_count-1);
+ else
+ {
+ /* only one view (that will fool our bsearch) lets check it here */
+ if(!slapi_utf8casecmp((unsigned char*)view, (unsigned char*)theCache.ppViewIndex[0]->pDn))
+ {
+ ret = theCache.ppViewIndex[0];
+ }
+ }
+
+ return ret;
+}
+
+
+/*
+ views_cache_discover_parent
+ ------------------------------
+ finds the parent of this view and caches it in view
+*/
+static void views_cache_discover_parent(viewEntry *pView)
+{
+ viewEntry *head = theCache.pCacheViews;
+ viewEntry *current;
+ int found = 0;
+
+ for(current = head; current != NULL && !found; current = current->list.pNext)
+ {
+ if(slapi_dn_isparent( current->pDn, pView->pDn ))
+ {
+ found = 1;
+ pView->pParent = current;
+ }
+ }
+
+ if(!found)
+ {
+ /* this is a top view */
+ pView->pParent = NULL;
+ }
+}
+
+
+/*
+ views_cache_discover_children
+ ------------------------------
+ finds the children of this view and caches them in view
+*/
+static void views_cache_discover_children(viewEntry *pView)
+{
+ viewEntry *head = theCache.pCacheViews;
+ viewEntry *current;
+ int child_count = 0;
+ int add_count = 0;
+
+ if(pView->pChildren)
+ {
+ slapi_ch_free((void**)&pView->pChildren);
+ pView->pChildren = NULL;
+ }
+
+ /* first lets count the children */
+ for(current = head; current != NULL; current = current->list.pNext)
+ {
+ if(slapi_dn_isparent(pView->pDn, current->pDn))
+ child_count++;
+ }
+
+ /* make the space for them */
+ pView->child_count = child_count;
+
+ pView->pChildren = calloc(child_count, sizeof(viewEntry*));
+
+ /* add them */
+ for(current = head; current != NULL; current = current->list.pNext)
+ {
+ if(slapi_dn_isparent(pView->pDn, current->pDn))
+ {
+ ((viewEntry**)pView->pChildren)[add_count] = current;
+ add_count++;
+ }
+ }
+}
+
+
+/*
+ views_cache_discover_view_scope
+ ------------------------------
+ finds the parent of the top most view and sets the scope of the view search
+*/
+
+static void views_cache_discover_view_scope(viewEntry *pView)
+{
+ viewEntry *current = pView;
+
+ if(pView->pSearch_base)
+ slapi_ch_free((void**)&pView->pSearch_base);
+
+ while(current != NULL)
+ {
+ if(current->pParent == NULL)
+ {
+ /* found top */
+ pView->pSearch_base = slapi_dn_parent(current->pDn);
+ }
+
+ current = current->pParent;
+ }
+
+}
+
+
+/*
+ views_cache_create_applied_filter
+ --------------------------------
+ builds the filters for:
+ char *includeAncestorFiltersFilter; the view with all ancestor views
+*/
+static void views_cache_create_applied_filter(viewEntry *pView)
+{
+ viewEntry *current = pView;
+ Slapi_Filter *pCurrentFilter = 0;
+ Slapi_Filter *pBuiltFilter = 0;
+ Slapi_Filter *pViewEntryExcludeFilter = 0;
+ char *buf = 0;
+ int len = 0;
+
+ if(pView->includeAncestorFiltersFilter)
+ {
+ /* release the current filter */
+ slapi_filter_free(pView->includeAncestorFiltersFilter, 1);
+ pView->includeAncestorFiltersFilter = 0;
+ }
+
+ /* create applied view filter (this view filter plus ancestors) */
+ while(current != NULL)
+ {
+ /* add this view filter to the built filter using AND */
+ char *buf;
+
+ if(!current->viewfilter)
+ {
+ current = current->pParent;
+ continue; /* skip this view */
+ }
+
+ buf = slapi_ch_strdup(current->viewfilter);
+
+ pCurrentFilter = slapi_str2filter( buf );
+ if(pBuiltFilter && pCurrentFilter)
+ pBuiltFilter = slapi_filter_join_ex( LDAP_FILTER_AND, pBuiltFilter, pCurrentFilter, 0 );
+ else
+ pBuiltFilter = pCurrentFilter;
+
+ slapi_ch_free((void **)&buf);
+
+ current = current->pParent;
+ }
+
+ /* filter for removing view entries from search */
+ pViewEntryExcludeFilter = slapi_str2filter( "(!(objectclass=" VIEW_OBJECTCLASS "))" );
+
+ if(pBuiltFilter)
+ pView->includeAncestorFiltersFilter = slapi_filter_join_ex( LDAP_FILTER_AND, pBuiltFilter, pViewEntryExcludeFilter, 0 );
+ else
+ pView->includeAncestorFiltersFilter = pViewEntryExcludeFilter;
+
+#ifdef _VIEW_DEBUG_FILTERS
+ slapi_filter_to_string(pView->includeAncestorFiltersFilter, pView->includeAncestorFiltersFilter_str, sizeof(pView->includeAncestorFiltersFilter_str));
+#endif
+}
+
+/* views_cache_create_exclusion_filter
+ * ----------------------------------
+ * makes a filter which is used for one level searches
+ * so that views show up correctly if the client filter
+ * allows: excludeGrandChildViewsFilter
+ *
+ * Also makes the filter which excludes entries which
+ * belong in descendent views: excludeChildFiltersFilter
+ */
+static void views_cache_create_exclusion_filter(viewEntry *pView)
+{
+ viewEntry *current = pView;
+ Slapi_Filter *pOrSubFilter = 0;
+ Slapi_Filter *excludeChildFiltersFilter = 0;
+ Slapi_Filter *pChildExcludeSubFilter = 0;
+ Slapi_Filter *pViewEntryExcludeFilter = 0;
+ int child_count = 0;
+ int len = 0;
+ char *buf = 0;
+ Slapi_RDN *rdn = 0;
+ char *str_rdn = 0;
+ Slapi_Filter *pCurrentFilter = 0;
+
+ /* create exclusion filter for one level searches
+ * this requires the rdns of the grandchildren of
+ * this view to be in a filter
+ */
+
+ if(pView->excludeGrandChildViewsFilter)
+ {
+ /* release the current filter */
+ slapi_filter_free(pView->excludeGrandChildViewsFilter, 1);
+ pView->excludeGrandChildViewsFilter = 0;
+ }
+
+ if(pView->excludeChildFiltersFilter)
+ {
+ /* release the current filter */
+ slapi_filter_free(pView->excludeChildFiltersFilter, 1);
+ pView->excludeChildFiltersFilter = 0;
+ }
+
+/* if(pView->child_count == 0)
+ {
+*/ /* this view has no children */
+/* pView->excludeGrandChildViewsFilter = 0;
+ pView->excludeChildFiltersFilter = 0;
+ return;
+ }
+
+
+ while(child_count < pView->child_count)
+ {
+ current = pView->pChildren[child_count];
+
+ if(current->child_count == 0)
+ {
+*/ /* no grandchildren here, skip */
+/* child_count++;
+ continue;
+ }
+*/
+ /* for each child we need to add its descendants */
+/* if(pOrSubFilter)
+ {
+ Slapi_Filter *pDescendents = views_cache_create_descendent_filter(current, TRUE);
+ if(pDescendents)
+ pOrSubFilter = slapi_filter_join_ex( LDAP_FILTER_OR, pOrSubFilter, pDescendents, 0 );
+ }
+ else
+ pOrSubFilter = views_cache_create_descendent_filter(current, TRUE);
+
+ child_count++;
+ }
+*/
+ buf=PR_smprintf("(parentid=%lu)", pView->entryid);
+ pView->excludeGrandChildViewsFilter = slapi_str2filter( buf );
+ PR_smprintf_free(buf);
+
+/* if(pOrSubFilter)
+ pView->excludeGrandChildViewsFilter = slapi_filter_join_ex( LDAP_FILTER_NOT, pOrSubFilter, NULL, 0 );*/
+
+ excludeChildFiltersFilter = views_cache_create_descendent_filter(pView, PR_FALSE);
+ if(excludeChildFiltersFilter)
+ pView->excludeChildFiltersFilter = slapi_filter_join_ex( LDAP_FILTER_NOT, excludeChildFiltersFilter, NULL, 0 );
+
+#ifdef _VIEW_DEBUG_FILTERS
+ slapi_filter_to_string(pView->excludeGrandChildViewsFilter, pView->excludeGrandChildViewsFilter_str, sizeof(pView->excludeGrandChildViewsFilter_str));
+ slapi_filter_to_string(pView->excludeChildFiltersFilter, pView->excludeChildFiltersFilter_str, sizeof(pView->excludeChildFiltersFilter_str));
+#endif
+}
+
+
+Slapi_Filter *views_cache_create_descendent_filter(viewEntry *ancestor, PRBool useEntryID)
+{
+ int child_count = 0;
+ Slapi_Filter *pOrSubFilter = 0;
+
+ while(child_count < ancestor->child_count)
+ {
+ Slapi_Filter *pDescendentSubFilter = 0;
+ Slapi_RDN *rdn = 0;
+ char *str_rdn = 0;
+ Slapi_Filter *pCurrentFilter = 0;
+ viewEntry *currentChild = ancestor->pChildren[child_count];
+ char *buf = 0;
+ int len = 0;
+
+ /* for each child we need to add its descendants
+ * we do this now before processing this view
+ * to try to help the filter code out by having
+ * the most significant filters first
+ */
+ pDescendentSubFilter = views_cache_create_descendent_filter(currentChild, useEntryID);
+ if(pDescendentSubFilter)
+ if(pOrSubFilter)
+ pOrSubFilter = slapi_filter_join_ex( LDAP_FILTER_OR, pOrSubFilter, pDescendentSubFilter, 0 );
+ else
+ pOrSubFilter = pDescendentSubFilter;
+
+ if(useEntryID)
+ {
+ /* we need the RDN of this child */
+/* rdn = slapi_rdn_new_dn(currentChild->pDn);
+ str_rdn = (char *)slapi_rdn_get_rdn(rdn);
+ len = strlen(str_rdn);
+
+ buf=PR_smprintf("(%s)", str_rdn);*/
+
+ /* uniquely identify this child */
+ buf=PR_smprintf("(parentid=%lu)", currentChild->entryid);
+ }
+ else
+ {
+ /* this is a filter based filter */
+ if(currentChild->viewfilter)
+ {
+ buf=PR_smprintf("%s",currentChild->viewfilter);
+ }
+ }
+
+ if(buf)
+ {
+ pCurrentFilter = slapi_str2filter( buf );
+ if(pOrSubFilter)
+ pOrSubFilter = slapi_filter_join_ex( LDAP_FILTER_OR, pOrSubFilter, pCurrentFilter, 0 );
+ else
+ pOrSubFilter = pCurrentFilter;
+
+ PR_smprintf_free(buf);
+ }
+
+
+ child_count++;
+ }
+
+ return pOrSubFilter;
+}
+
+
+/* views_cache_create_inclusion_filter
+ * ----------------------------------
+ * makes a filter which is used for subtree searches
+ * so that views show up correctly if the client filter
+ * allows
+ */
+static void views_cache_create_inclusion_filter(viewEntry *pView)
+{
+ viewEntry *head = theCache.pCacheViews;
+/* viewEntry *current; */
+/* Slapi_Filter *view_filter; */
+ char *view_filter_str;
+
+ if(pView->includeChildViewsFilter)
+ {
+ /* release the current filter */
+ slapi_filter_free(pView->includeChildViewsFilter, 1);
+ pView->includeChildViewsFilter = 0;
+ }
+#if 0
+ for(current = head; current != NULL; current = current->list.pNext)
+ {
+ Slapi_DN *viewDN;
+ Slapi_RDN *viewRDN;
+ char *viewRDNstr;
+ char *buf = 0;
+ Slapi_Filter *viewSubFilter;
+
+ /* if this is this a descendent, ignore it */
+ if(slapi_dn_issuffix(current->pDn,pView->pDn) && !(current == pView))
+ continue;
+
+ viewDN = slapi_sdn_new_dn_byref(current->pDn);
+ viewRDN = slapi_rdn_new();
+
+ slapi_sdn_get_rdn(viewDN,viewRDN);
+ viewRDNstr = (char *)slapi_rdn_get_rdn(viewRDN);
+
+ buf = calloc(1, strlen(viewRDNstr) + 11 ); /* 3 for filter */
+ sprintf(buf, "(%s)", viewRDNstr );
+ viewSubFilter = slapi_str2filter( buf );
+
+ if(pView->includeChildViewsFilter)
+ pView->includeChildViewsFilter = slapi_filter_join_ex( LDAP_FILTER_OR, pView->includeChildViewsFilter, viewSubFilter, 0 );
+ else
+ pView->includeChildViewsFilter = viewSubFilter;
+
+ slapi_ch_free((void **)&buf);
+ slapi_sdn_free(&viewDN);
+ slapi_rdn_free(&viewRDN);
+
+ child_count++;
+ }
+#endif
+
+ /* exclude all other view entries but decendents */
+/* pView->includeChildViewsFilter = slapi_filter_join_ex( LDAP_FILTER_NOT, pView->includeChildViewsFilter, NULL, 0 );
+*/
+ /* it seems reasonable to include entries which
+ * may not fit the view decription but which
+ * are actually *contained* in the view
+ * therefore we use parentids for the view
+ * filter
+ */
+
+
+ /* add decendents */
+ pView->includeChildViewsFilter = views_cache_create_descendent_filter(pView, PR_TRUE);
+
+ /* add this view */
+ view_filter_str = PR_smprintf("(parentid=%lu)", pView->entryid);
+
+ if(pView->includeChildViewsFilter)
+ {
+ pView->includeChildViewsFilter = slapi_filter_join_ex( LDAP_FILTER_OR, slapi_str2filter( view_filter_str ), pView->includeChildViewsFilter, PR_FALSE);
+ }
+ else
+ {
+ pView->includeChildViewsFilter = slapi_str2filter( view_filter_str );
+ }
+ PR_smprintf_free(view_filter_str);
+ view_filter_str = NULL;
+
+ /* and make sure the this applies only to views */
+
+/* if(pView->includeChildViewsFilter)
+ {*/
+/* Not necessary since we now use entryid in the filter,
+ so all will be views anyway, and the less sub-filters
+ the better
+ view_filter_str = strdup("(objectclass=" VIEW_OBJECTCLASS ")");
+ view_filter = slapi_str2filter( view_filter_str );
+*/
+ /* child views first because entryid indexed
+ * and makes evaluation faster when a bunch
+ * of indexed filter evaluations with only one
+ * target are evaluated first rather than an
+ * indexed filter which will provide many entries
+ * that may trigger an index evaluation short
+ * circuit. i.e. if one of the child filters is
+ * true then we have one entry, if not, then we
+ * have used indexes completely to determine that
+ * no entry matches and (objectclass=nsview) is never
+ * evaluated.
+ * I should imagine this will hold for all but the
+ * very deepest, widest view trees when subtree
+ * searches are performed from the top
+ */
+/* pView->includeChildViewsFilter = slapi_filter_join_ex( LDAP_FILTER_AND, pView->includeChildViewsFilter, view_filter, 0 );
+ }
+ else
+ {
+ view_filter_str = strdup("(objectclass=nsviewincludenone)"); *//* hackery to get the right result */
+/* pView->includeChildViewsFilter = slapi_str2filter( view_filter_str );
+ }
+*/
+#ifdef _VIEW_DEBUG_FILTERS
+ slapi_filter_to_string(pView->includeChildViewsFilter, pView->includeChildViewsFilter_str, sizeof(pView->includeChildViewsFilter_str));
+#endif
+}
+
+
+/*
+ views_cache_build_view_list
+ -------------------------------
+ builds the list of views by searching for them throughout the DIT
+*/
+static int views_cache_build_view_list(viewEntry **pViews)
+{
+ int ret = 0;
+ Slapi_PBlock *pSuffixSearch = 0;
+ Slapi_Entry **pSuffixList = 0;
+ Slapi_Attr *suffixAttr;
+ struct berval **suffixVals;
+ char *attrType = 0;
+ char *attrs[2];
+ int suffixIndex = 0;
+ int valIndex = 0;
+ int cos_def_available = 0;
+ static int firstTime = 1;
+
+ slapi_log_error(SLAPI_LOG_TRACE, VIEWS_PLUGIN_SUBSYSTEM, "--> views_cache_build_view_list\n");
+
+ /*
+ the views may be anywhere in the DIT,
+ so our first task is to find them.
+ */
+
+ attrs[0] = "namingcontexts";
+ attrs[1] = 0;
+
+ slapi_log_error(SLAPI_LOG_PLUGIN, VIEWS_PLUGIN_SUBSYSTEM, "views: Building view cache.\n");
+
+ pSuffixSearch = slapi_search_internal("",LDAP_SCOPE_BASE,"(objectclass=*)",NULL,attrs,0);
+ if(pSuffixSearch)
+ slapi_pblock_get( pSuffixSearch, SLAPI_PLUGIN_INTOP_RESULT, &ret);
+
+ if(pSuffixSearch && ret == LDAP_SUCCESS)
+ {
+ /* iterate through the suffixes and search for views */
+ slapi_pblock_get( pSuffixSearch, SLAPI_PLUGIN_INTOP_SEARCH_ENTRIES, &pSuffixList);
+ if(pSuffixList)
+ {
+ while(pSuffixList[suffixIndex])
+ {
+ if(!slapi_entry_first_attr(pSuffixList[suffixIndex], &suffixAttr))
+ {
+ do
+ {
+ attrType = 0;
+ slapi_attr_get_type(suffixAttr, &attrType);
+ if(attrType && !slapi_utf8casecmp((unsigned char*)attrType, (unsigned char*)"namingcontexts"))
+ {
+ if(!slapi_attr_get_bervals_copy(suffixAttr, &suffixVals))
+ {
+ valIndex = 0;
+
+ if(suffixVals)
+ {
+ while(suffixVals[valIndex])
+ {
+ /* here's a suffix, lets search it... */
+ if(suffixVals[valIndex]->bv_val)
+ views_cache_add_dn_views(suffixVals[valIndex]->bv_val ,pViews);
+
+ valIndex++;
+ }
+
+
+ ber_bvecfree( suffixVals );
+ suffixVals = NULL;
+ }
+ }
+ }
+
+ } while(!slapi_entry_next_attr(pSuffixList[suffixIndex], suffixAttr, &suffixAttr));
+ }
+ suffixIndex++;
+ }
+ }
+ }
+ else
+ {
+ slapi_log_error(SLAPI_LOG_PLUGIN, VIEWS_PLUGIN_SUBSYSTEM, "views_cache_build_view_list: failed to find suffixes\n");
+ ret = -1;
+ }
+
+ /* clean up */
+ if(pSuffixSearch)
+ {
+ slapi_free_search_results_internal(pSuffixSearch);
+ slapi_pblock_destroy(pSuffixSearch);
+ }
+
+
+ slapi_log_error(SLAPI_LOG_TRACE, VIEWS_PLUGIN_SUBSYSTEM, "<-- views_cache_build_view_list\n");
+ return ret;
+}
+
+/* struct to support search callback API */
+struct dn_views_info {
+ viewEntry **pViews;
+ int ret;
+};
+
+/* does same funcationality as views_add_dn_views except it is invoked via a callback */
+
+static int views_dn_views_cb (Slapi_Entry* e, void *callback_data) {
+ struct dn_views_info *info;
+ char *filter = 0;
+ char *pDn = 0;
+ struct berval **dnVals;
+ Slapi_Attr *dnAttr;
+ char *attrType = 0;
+ char *attrs[3];
+ viewEntry *pView;
+
+ attrs[0] = VIEW_FILTER_ATTR;
+ attrs[1] = "entryid";
+ attrs[2] = 0;
+ info=(struct dn_views_info *)callback_data;
+
+ info->ret = 0;
+
+ pDn = slapi_entry_get_ndn(e);
+
+ /* create the view */
+ pView = calloc(1, sizeof(viewEntry));
+ pView->pDn = slapi_ch_strdup(pDn);
+
+ if(!slapi_entry_first_attr(e, &dnAttr))
+ {
+ do
+ {
+ attrType = 0;
+
+
+ /* get the filter */
+ slapi_attr_get_type(dnAttr, &attrType);
+ if(attrType && !strcasecmp(attrType,VIEW_FILTER_ATTR))
+ {
+ if(!slapi_attr_get_bervals_copy(dnAttr, &dnVals))
+ {
+ /* add filter */
+ pView->viewfilter = slapi_ch_strdup(dnVals[0]->bv_val);
+ }
+
+ ber_bvecfree( dnVals );
+ dnVals = NULL;
+ }
+
+ if(attrType && !strcasecmp(attrType,"entryid"))
+ {
+ Slapi_Value *val = 0;
+
+ slapi_attr_first_value(dnAttr, &val);
+ pView->entryid = slapi_value_get_ulong(val);
+ }
+
+ if(attrType && !strcasecmp(attrType,"parentid"))
+ {
+ Slapi_Value *val = 0;
+
+ slapi_attr_first_value(dnAttr, &val);
+ pView->parentid = slapi_value_get_ulong(val);
+ }
+
+ } while(!slapi_entry_next_attr(e, dnAttr, &dnAttr));
+
+ }
+
+ /* add view to the cache */
+ views_cache_add_ll_entry((void**)info->pViews, (void *)pView);
+
+ return info->ret;
+}
+
+
+/*
+ views_cache_add_dn_views
+ -------------------------
+ takes a dn as argument and searches the dn for views,
+ adding any found to the view cache. Change to use search callback API
+*/
+
+#define DN_VIEW_FILTER "(objectclass=" VIEW_OBJECTCLASS ")"
+
+static int views_cache_add_dn_views(char *dn, viewEntry **pViews)
+{
+ Slapi_PBlock *pDnSearch = 0;
+ struct dn_views_info info;
+ pDnSearch = slapi_pblock_new();
+ if (pDnSearch) {
+ info.ret=-1;
+ info.pViews=pViews;
+ slapi_search_internal_set_pb(pDnSearch, dn, LDAP_SCOPE_SUBTREE,
+ DN_VIEW_FILTER,NULL,0,
+ NULL,NULL,view_get_plugin_identity(),0);
+ slapi_search_internal_callback_pb(pDnSearch,
+ &info /* callback_data */,
+ NULL/* result_callback */,
+ views_dn_views_cb,
+ NULL /* referral_callback */);
+ slapi_pblock_destroy (pDnSearch);
+ }
+ return info.ret;
+}
+
+/*
+ views_cache_add_ll_entry
+ ---------------------------------------------------
+ the element is added to the head of the linked list
+
+ *NOTE* this function assumes and *requires* that the structures
+ passed to it in "attrval" and "theVal" have a viewLinkedList
+ member, and it is the *first* member of the structure. This
+ is safe because this is a module level function, and all functions
+ which call this one are part of the same sub-system.
+*/
+static void views_cache_add_ll_entry(void** attrval, void *theVal)
+{
+ slapi_log_error(SLAPI_LOG_TRACE, VIEWS_PLUGIN_SUBSYSTEM, "--> views_cache_add_ll_entry\n");
+
+ if(*attrval)
+ {
+ /* push this to the start of the list (because its quick) */
+ ((viewLinkedList*)theVal)->pNext = *attrval;
+ ((viewLinkedList*)(*attrval))->pPrev = theVal;
+ *attrval = theVal;
+ }
+ else
+ {
+ /* new or end of list */
+ ((viewLinkedList*)theVal)->pNext = NULL;
+ ((viewLinkedList*)theVal)->pPrev = NULL;
+ *attrval = theVal;
+ }
+
+ slapi_log_error(SLAPI_LOG_TRACE, VIEWS_PLUGIN_SUBSYSTEM, "<-- views_cache_add_ll_entry\n");
+}
+
+
+/*
+ views_update_views_cache
+ -----------------------
+
+ update internal view cache after state change
+*/
+static void views_update_views_cache( Slapi_Entry *e, char *dn, int modtype, Slapi_PBlock *pb, void *caller_data )
+{
+ char *pDn;
+ viewEntry *theView;
+ viewEntry *current;
+ Slapi_Attr *attr;
+ struct berval val;
+ int build_cache = 0;
+
+ slapi_log_error( SLAPI_LOG_TRACE, VIEWS_PLUGIN_SUBSYSTEM, "--> views_update_views_cache\n");
+
+ views_write_lock();
+
+ if(!theCache.cache_built)
+ {
+ /* zarro views = no cache,
+ * this is probably an add op
+ * lets build the cache
+ */
+ build_cache = 1;
+ goto unlock_cache;
+ }
+
+ pDn = slapi_entry_get_ndn(e);
+ theView = views_cache_find_view(pDn);
+
+ switch(modtype)
+ {
+ case LDAP_CHANGETYPE_MODIFY:
+ /* if still a view and exists
+ * update string filter
+ * update the filters of all views
+ * if just became a view fall through to add op
+ * if stopped being a view fall through to delete op
+ */
+
+ /* determine what happenned - does the view exist currently? */
+ if(theView)
+ {
+ /* does it have the view objectclass? */
+ if(!slapi_entry_attr_find( e, "objectclass", &attr ))
+ {
+ val.bv_len = 8;
+ val.bv_val = VIEW_OBJECTCLASS;
+
+ if(!slapi_attr_value_find( attr, &val))
+ {
+ /* it is a view */
+ attr = 0;
+
+ /* has the filter changed? */
+ slapi_entry_attr_find( e, VIEW_FILTER_ATTR, &attr );
+
+ if(attr)
+ {
+ if(theView->viewfilter) /* NULL means a filter added */
+ {
+ /* we could translate the string filter into
+ * a real filter and compare against
+ * the view - that would tell us if the filter
+ * was substantively changed.
+ *
+ * But we're not gonna do that :)
+ */
+ val.bv_len = strlen(theView->viewfilter)+1;
+ val.bv_val = theView->viewfilter;
+
+ if(!slapi_attr_value_find( attr, &val))
+ {
+ /* filter unchanged */
+ break;
+ }
+ }
+ }
+ else
+ {
+ /* if no filter in view, then no change */
+ if(theView->viewfilter == 0)
+ break;
+ }
+
+ /* this was indeed a significant mod, add the new filter */
+ if(theView->viewfilter)
+ slapi_ch_free((void**)&theView->viewfilter);
+
+ if(attr)
+ {
+ Slapi_Value *v;
+ slapi_attr_first_value( attr, &v );
+ theView->viewfilter = slapi_ch_strdup(slapi_value_get_string(v));
+ }
+
+ /* update all filters */
+ for(current = theCache.pCacheViews; current != NULL; current = current->list.pNext)
+ {
+ views_cache_create_applied_filter(current);
+ views_cache_create_exclusion_filter(current);
+ views_cache_create_inclusion_filter(current);
+ }
+ }
+ else
+ {
+ /* this is a delete operation */
+ modtype = LDAP_CHANGETYPE_DELETE;
+ }
+ }
+ else
+ /* thats bad */
+ break;
+ }
+ else
+ {
+ /* this is an add operation */
+ modtype = LDAP_CHANGETYPE_ADD;
+ }
+
+ case LDAP_CHANGETYPE_DELETE:
+ /* remove view entry from list
+ * update children of parent
+ * update all child filters
+ * re-index
+ */
+ if(modtype == LDAP_CHANGETYPE_DELETE)
+ {
+
+ if(theCache.view_count-1)
+ {
+ /* detach view */
+ if(theView->list.pPrev)
+ ((viewEntry*)(theView->list.pPrev))->list.pNext = theView->list.pNext;
+
+ if(theView->list.pNext)
+ {
+ ((viewEntry*)(theView->list.pNext))->list.pPrev = theView->list.pPrev;
+
+ if(theView->list.pPrev == NULL) /* if this is the head */
+ theCache.pCacheViews = (viewEntry*)(theView->list.pNext);
+ }
+
+ /* update children */
+ if(theView->pParent)
+ views_cache_discover_children((viewEntry*)theView->pParent);
+
+ /* update filters */
+ for(current = theCache.pCacheViews; current != NULL; current = current->list.pNext)
+ {
+ views_cache_create_applied_filter(current);
+ views_cache_create_exclusion_filter(current);
+ views_cache_create_inclusion_filter(current);
+ }
+
+ /* reindex */
+ views_cache_index();
+ }
+ else
+ {
+ theCache.pCacheViews = NULL;
+ theCache.view_count = 0;
+ theCache.cache_built = 0;
+ }
+
+ /* free the view */
+ slapi_ch_free((void**)&theView->pDn);
+ slapi_ch_free((void**)&theView->viewfilter);
+ slapi_filter_free(theView->includeAncestorFiltersFilter,1);
+ slapi_filter_free(theView->excludeAllButDescendentViewsFilter,1);
+ slapi_filter_free(theView->excludeChildFiltersFilter,1);
+ slapi_filter_free(theView->excludeGrandChildViewsFilter,1);
+ slapi_filter_free(theView->includeChildViewsFilter,1);
+ slapi_ch_free((void**)&theView->pSearch_base);
+ slapi_ch_free((void**)&theView->pChildren);
+ slapi_ch_free((void**)&theView);
+
+ break;
+ }
+
+ case LDAP_CHANGETYPE_ADD:
+ /* create view entry
+ * add it to list
+ * update children of parent
+ * update all child filters
+ * re-index
+ */
+ if(modtype == LDAP_CHANGETYPE_ADD)
+ {
+ theView = calloc(1, sizeof(viewEntry));
+ theView->pDn = slapi_ch_strdup(pDn);
+
+ /* get the view filter, the entryid, and the parentid */
+ slapi_entry_attr_find( e, VIEW_FILTER_ATTR, &attr );
+
+ if(attr)
+ {
+ Slapi_Value *v;
+ slapi_attr_first_value( attr, &v );
+ theView->viewfilter = slapi_ch_strdup(slapi_value_get_string(v));
+ }
+ else
+ theView->viewfilter = NULL;
+
+ slapi_entry_attr_find( e, "entryid", &attr );
+
+ if(attr)
+ {
+ Slapi_Value *v;
+ slapi_attr_first_value( attr, &v );
+ theView->entryid = slapi_value_get_ulong(v);
+ }
+ else
+ theView->entryid = 0;
+
+ slapi_entry_attr_find( e, "parentid", &attr );
+
+ if(attr)
+ {
+ Slapi_Value *v;
+ slapi_attr_first_value( attr, &v );
+ theView->parentid = slapi_value_get_ulong(v);
+ }
+ else
+ theView->parentid = 0;
+
+ /* add view to the cache */
+ views_cache_add_ll_entry((void**)theCache.pCacheViews, (void *)theView);
+
+ views_cache_discover_parent(theView);
+ if(theView->pParent)
+ views_cache_discover_children((viewEntry*)theView->pParent);
+
+ /* update filters */
+ for(current = theCache.pCacheViews; current != NULL; current = current->list.pNext)
+ {
+ views_cache_discover_view_scope(current); /* if ns-view oc added, new view may be top */
+ views_cache_create_applied_filter(current);
+ views_cache_create_exclusion_filter(current);
+ views_cache_create_inclusion_filter(current);
+ }
+
+ /* reindex */
+ views_cache_index();
+ break;
+ }
+
+ case LDAP_CHANGETYPE_MODDN:
+ /* get old dn to find the view
+ * change dn
+ * update parents and children
+ * update all filters
+ * reindex
+ */
+
+ {
+ char *old_dn;
+ Slapi_Entry *old_entry;
+
+ slapi_pblock_get( pb, SLAPI_ENTRY_PRE_OP, &old_entry );
+ old_dn = slapi_entry_get_ndn(old_entry);
+
+ theView = views_cache_find_view(old_dn);
+ if(theView)
+ {
+ slapi_ch_free((void**)&theView->pDn);
+ theView->pDn = slapi_ch_strdup(pDn);
+
+ for(current = theCache.pCacheViews; current != NULL; current = current->list.pNext)
+ {
+ views_cache_discover_parent(current);
+ views_cache_discover_children(current);
+ }
+
+ for(current = theCache.pCacheViews; current != NULL; current = current->list.pNext)
+ {
+ views_cache_discover_view_scope(current);
+ views_cache_create_applied_filter(current);
+ views_cache_create_exclusion_filter(current);
+ views_cache_create_inclusion_filter(current);
+ }
+ }
+ /* reindex */
+ views_cache_index();
+ break;
+ }
+
+ default:
+ /* we don't care about this op */
+ break;
+ }
+
+unlock_cache:
+ views_unlock();
+
+ if(build_cache)
+ {
+ views_cache_create();
+ }
+
+ slapi_log_error( SLAPI_LOG_TRACE, VIEWS_PLUGIN_SUBSYSTEM, "<-- views_update_views_cache\n");
+}
+
+
+
+/*
+ * view_search_rewrite_callback
+ * ----------------------------
+ * this is the business end of the plugin
+ * this function is called from slapd
+ * rewrites the search to conform to the view
+ * Meaning of the return code :
+ * -1 : keep looking
+ * 0 : rewrote OK
+ * 1 : refuse to do this search
+ * 2 : operations error
+ */
+static int view_search_rewrite_callback(Slapi_PBlock *pb)
+{
+ int ret = -1;
+ char *base = 0;
+ Slapi_Filter *clientFilter = 0;
+ Slapi_Filter *includeAncestorFiltersFilter = 0; /* the view with all ancestor views */
+ Slapi_Filter *excludeChildFiltersFilter = 0; /* NOT all children views, for one level searches */
+ Slapi_Filter *excludeGrandChildViewsFilter = 0; /* view filter for one level searches */
+ Slapi_Filter *includeChildViewsFilter = 0; /* view filter for subtree searches */
+ Slapi_Filter *seeViewsFilter = 0; /* view filter to see views */
+ Slapi_Filter *outFilter = 0;
+ int scope = 0;
+ int set_scope = LDAP_SCOPE_SUBTREE;
+ viewEntry *theView = 0;
+
+#ifdef _VIEW_DEBUG_FILTERS
+ char outFilter_str[1024];
+ char clientFilter_str[1024];
+ char includeAncestorFiltersFilter_str[1024];
+ char excludeChildFiltersFilter_str[1024];
+ char excludeGrandChildViewsFilter_str[1024];
+ char includeChildViewsFilter_str[1024];
+#endif
+
+ /* if no cache, no views */
+ if(!theCache.cache_built)
+ goto end;
+
+ /* avoid locking if this thread is the updater */
+ if(theCache.currentUpdaterThread)
+ {
+ PRThread *thisThread = PR_GetCurrentThread();
+ if(thisThread == theCache.currentUpdaterThread)
+ goto end;
+ }
+
+ /* first, find out if this is a base search (we do nothing) */
+ slapi_pblock_get(pb, SLAPI_SEARCH_SCOPE, &scope);
+ if(scope == LDAP_SCOPE_BASE)
+ goto end;
+
+ /* if base of the search is a view */
+ slapi_pblock_get(pb, SLAPI_SEARCH_TARGET, &base);
+
+ /* Read lock the cache */
+ views_read_lock();
+
+ theView = views_cache_find_view(base);
+
+ /* if the view is disabled (we service subtree searches in this case) */
+ if(!theView || !theView->viewfilter && scope == LDAP_SCOPE_ONELEVEL)
+ {
+ /* unlock the cache */
+ views_unlock();
+ goto end;
+ }
+
+
+ /* this is a view search, and we are smokin' */
+
+ /* grab the view filters we are going to need now so we can release the cache lock */
+ if(scope == LDAP_SCOPE_ONELEVEL)
+ {
+ excludeChildFiltersFilter = slapi_filter_dup(theView->excludeChildFiltersFilter);
+ excludeGrandChildViewsFilter = slapi_filter_dup(theView->excludeGrandChildViewsFilter);
+
+#ifdef _VIEW_DEBUG_FILTERS
+ slapi_filter_to_string(excludeChildFiltersFilter, excludeChildFiltersFilter_str, sizeof(excludeChildFiltersFilter_str));
+ slapi_filter_to_string(excludeGrandChildViewsFilter, excludeGrandChildViewsFilter_str, sizeof(excludeGrandChildViewsFilter_str));
+#endif
+
+ }
+
+ includeChildViewsFilter = slapi_filter_dup(theView->includeChildViewsFilter);
+
+#ifdef _VIEW_DEBUG_FILTERS
+ slapi_filter_to_string(includeChildViewsFilter, includeChildViewsFilter_str, sizeof(includeChildViewsFilter_str));
+#endif
+
+ /* always used */
+ includeAncestorFiltersFilter = slapi_filter_dup(theView->includeAncestorFiltersFilter);
+
+#ifdef _VIEW_DEBUG_FILTERS
+ slapi_filter_to_string(includeAncestorFiltersFilter, includeAncestorFiltersFilter_str, sizeof(includeAncestorFiltersFilter_str));
+#endif
+
+ /* unlock the cache */
+ views_unlock();
+
+ /* rewrite search scope and base*/
+ slapi_pblock_set(pb, SLAPI_SEARCH_SCOPE, &set_scope);
+
+ base = slapi_ch_strdup(theView->pSearch_base);
+ slapi_pblock_set(pb, SLAPI_SEARCH_TARGET, base);
+
+ /* concatenate the filters */
+
+ /* grab the client filter - we need 2 copies */
+ slapi_pblock_get(pb, SLAPI_SEARCH_FILTER, &clientFilter);
+
+#ifdef _VIEW_DEBUG_FILTERS
+ slapi_filter_to_string(clientFilter, clientFilter_str, sizeof(clientFilter_str));
+#endif
+
+ /* client supplied filter AND inclusion filter - make sure we can see views */
+ if(scope == LDAP_SCOPE_ONELEVEL)
+ {
+ Slapi_Filter *clientSeeViewsFilter = 0; /* view filter to see views */
+
+ clientSeeViewsFilter = slapi_filter_dup(clientFilter);
+ if(excludeGrandChildViewsFilter)
+ seeViewsFilter = slapi_filter_join_ex( LDAP_FILTER_AND, excludeGrandChildViewsFilter, clientSeeViewsFilter, 0 );
+ else
+ seeViewsFilter = clientSeeViewsFilter;
+ }
+
+ /* this filter is to lock our view to the subtree at hand */
+ if(seeViewsFilter && includeChildViewsFilter)
+ seeViewsFilter = slapi_filter_join_ex( LDAP_FILTER_AND, includeChildViewsFilter, seeViewsFilter, 0 );
+ else
+ {
+ if(includeChildViewsFilter)
+ seeViewsFilter = includeChildViewsFilter;
+ }
+
+ /* create target filter */
+ if(includeAncestorFiltersFilter)
+ outFilter = slapi_filter_join_ex( LDAP_FILTER_AND, includeAncestorFiltersFilter, clientFilter, 0 );
+ else
+ outFilter = clientFilter;
+
+ if(scope == LDAP_SCOPE_ONELEVEL)
+ {
+ if(excludeChildFiltersFilter)
+ outFilter = slapi_filter_join_ex( LDAP_FILTER_AND, outFilter, excludeChildFiltersFilter, 0 );
+ }
+
+ if(seeViewsFilter)
+ outFilter = slapi_filter_join_ex( LDAP_FILTER_OR, outFilter, seeViewsFilter, 0 );
+
+#ifdef _VIEW_DEBUG_FILTERS
+ slapi_filter_to_string(outFilter, outFilter_str, sizeof(outFilter_str));
+#endif
+
+ /* make it happen */
+ slapi_pblock_set(pb, SLAPI_SEARCH_FILTER, outFilter);
+
+ ret = -2;
+
+end:
+ return ret;
+}
+
+/*
+ * views_cache_backend_state_change()
+ * --------------------------------
+ * This is called when a backend changes state
+ * We simply signal to rebuild the cache in this case
+ *
+ */
+static void views_cache_backend_state_change(void *handle, char *be_name,
+ int old_be_state, int new_be_state)
+{
+ /* we will create a thread to do this since
+ * calling views_cache_create() directly will
+ * hold up the op
+ */
+ if ((PR_CreateThread (PR_USER_THREAD,
+ views_cache_act_on_change_thread,
+ NULL,
+ PR_PRIORITY_NORMAL,
+ PR_GLOBAL_THREAD,
+ PR_UNJOINABLE_THREAD,
+ SLAPD_DEFAULT_THREAD_STACKSIZE)) == NULL )
+ {
+ slapi_log_error( SLAPI_LOG_FATAL, VIEWS_PLUGIN_SUBSYSTEM,
+ "views_cache_backend_state_change: PR_CreateThread failed\n" );
+ }
+}
+
+static void views_cache_act_on_change_thread(void *arg)
+{
+ views_cache_create();
+}
diff --git a/ldap/servers/plugins/views/views.def b/ldap/servers/plugins/views/views.def
new file mode 100644
index 00000000..26d28966
--- /dev/null
+++ b/ldap/servers/plugins/views/views.def
@@ -0,0 +1,10 @@
+; BEGIN COPYRIGHT BLOCK
+; Copyright 2001 Sun Microsystems, Inc.
+; Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+; All rights reserved.
+; END COPYRIGHT BLOCK
+;
+DESCRIPTION 'Netscape Directory Server 7.0 State Change Plugin'
+EXPORTS
+ views_init @2
+ plugin_init_debug_level @3
diff --git a/ldap/servers/slapd/Makefile b/ldap/servers/slapd/Makefile
new file mode 100644
index 00000000..6935ac30
--- /dev/null
+++ b/ldap/servers/slapd/Makefile
@@ -0,0 +1,278 @@
+#
+# BEGIN COPYRIGHT BLOCK
+# Copyright 2001 Sun Microsystems, Inc.
+# Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+# All rights reserved.
+# END COPYRIGHT BLOCK
+#
+#
+# GNU Makefile for Directory Server
+#
+
+LDAP_SRC = ../..
+MCOM_ROOT = ../../../..
+
+NOSTDCLEAN=true # don't let nsconfig.mk define target clean
+NOSTDSTRIP=true # don't let nsconfig.mk define target strip
+NSPR20=true # probably should be defined somewhere else (not sure where)
+
+OBJDEST = $(OBJDIR)/servers/obj
+BINDIR = $(LDAP_SERVER_RELDIR)
+ifndef INSTDIR
+INSTDIR = /netscape/server4/
+endif
+
+include $(MCOM_ROOT)/ldapserver/nsdefs.mk
+include $(MCOM_ROOT)/ldapserver/nsconfig.mk
+include $(LDAP_SRC)/nsldap.mk
+include $(MCOM_ROOT)/ldapserver/ns_usepurify.mk
+include $(MCOM_ROOT)/ldapserver/ns_usequantify.mk
+include $(LDAP_SRC)/nsdeps.mk
+
+
+ifdef HEAPAGENT
+CFLAGS+=-DPURIFYING
+LDAP_DONT_USE_SMARTHEAP=1
+endif
+
+ifdef USE_PURIFY
+CFLAGS+=-DPURIFYING
+LDAP_DONT_USE_SMARTHEAP=1
+endif
+
+ifdef USE_QUANTIFY
+CFLAGS+=-DQUANTIFYING
+LDAP_DONT_USE_SMARTHEAP=1
+endif
+
+# It looks like most of the latest versions of Unix that we ship on
+# have a good enough heap implementations that they don't need
+# SmartHeap. We still need it on NT, Solaris and HPUX.
+# Solaris 8 has mtmalloc but we still build on and support Solaris 2.6.
+# By contract HPUX must be aligned with Solaris.
+ifneq ($(ARCH), SOLARIS)
+ifneq ($(ARCH), WINNT)
+ifneq ($(ARCH), HPUX)
+LDAP_DONT_USE_SMARTHEAP=1
+endif
+endif
+endif
+
+# Don't use smartheap for debug builds
+ifeq ($(DEBUG), full)
+LDAP_DONT_USE_SMARTHEAP=1
+endif
+
+ifndef LDAP_DONT_USE_SMARTHEAP
+include $(MCOM_ROOT)/ldapserver/ns_usesh.mk
+INCLUDES+=-I$(SH_INCLUDE)
+else
+CFLAGS+=-DLDAP_DONT_USE_SMARTHEAP
+endif
+
+ifndef LDAP_USE_OLD_DB
+include $(MCOM_ROOT)/ldapserver/ns_usedb.mk
+_ldap_db_depend:=$(DB_LIB_DEP)
+INCLUDES+=-I$(DB_INCLUDE)
+endif
+
+INCLUDES += -I. -I$(ACLINC) -I$(MCOM_ROOT)/ldapserver/lib
+
+#ICONS = $(addprefix $(LDAP_SRC)/servers/slapd/ntwdog/, logo.ico key.ico)
+
+REGULAR_SLAPD_OBJS= abandon.o bind.o \
+ compare.o config.o connection.o daemon.o sasl_io.o \
+ detach.o globals.o house.o init.o \
+ monitor.o saslbind.o search.o strdup.o tempnam.o \
+ unbind.o extendop.o rootdse.o \
+ configdse.o pw_mgmt.o auth.o \
+ psearch.o conntable.o \
+ stubs.o protect_db.o fileio.o lite_entries.o \
+ getopt_ext.o start_tls_extop.o
+FEDSE_OBJ= fedse.o
+FEDSE_SRC= fedse.c
+SLAPD_OBJS= $(REGULAR_SLAPD_OBJS) $(FEDSE_OBJ)
+
+
+ifneq ($(ARCH), WINNT)
+SLAPD_OBJS += main.o
+endif
+
+ifeq ($(ARCH), WINNT)
+LDAP_COMMON_EXTRALIBSLIST=libsi18n
+LDAP_COMMON_EXTRALIBS = $(addsuffix .$(LIB_SUFFIX), \
+ $(addprefix $(LDAP_LIBDIR)/, $(LDAP_COMMON_EXTRALIBSLIST)))
+
+EXTRA_LIBS_DEP = \
+ $(LDAP_SDK_LIBLDAP_DLL_DEP) \
+ $(LDAP_SDK_LIBSSLDAP_LIB_DEP) $(LIBLDAPU_DEP) \
+ $(_ldap_db_depend) $(LDAP_COMMON_EXTRALIBS)
+
+EXTRA_LIBS += $(LIBSLAPD) $(LIBLDAPU) $(SVRCORELINK)\
+ $(LDAPLINK) $(SECURITYLINK) $(NSPRLINK) \
+ $(LDAP_COMMON_EXTRALIBS)
+
+# JCM - Warnings as Errors
+CFLAGS += /WX
+else
+LDFLAGS = $(SSLLIBFLAG)
+EXTRA_LIBS_DEP = $(SECURITY_DEP) $(NSPR_DEP) \
+ $(LDAP_LIBLDBM_DEP) $(LDAP_LIBAVL_DEP) $(LDAP_LIBLDIF_DEP) \
+ $(LDAPSDK_DEP) $(LIBLDAPU_DEP) \
+ $(_ldap_db_depend) \
+ $(SVRCORE_DEP)
+
+
+#IRIX linker needs LIBSEC first, couldn't find away that would make both IRIX and
+# solaris happy, hence the ifeq
+ifeq ($(ARCH), IRIX)
+# -llitekey is added; but it looks to me these two EXTRA_LIBS are identical...
+EXTRA_LIBS = $(LIBSLAPD) \
+ $(LIBLDAPU) $(LDAPLINK) \
+ $(SECURITYLINK) $(NSPRLINK) $(LDAP_LIBLDBM) \
+ $(DBMLINK) -lavl -lldif -llitekey \
+ $(ALIBS) $(DYNALIBS) $(THREADSLIB) $(SVRCORELINK)
+else
+EXTRA_LIBS = $(LIBSLAPD) \
+ $(LIBLDAPU) $(SECURITYLINK) $(LDAPLINK) \
+ $(NSPRLINK) $(LDAP_LIBLDBM) \
+ $(DBMLINK) -lavl -lldif -llitekey \
+ $(ALIBS) $(DYNALIBS) $(THREADSLIB) \
+ $(SVRCORELINK)
+endif
+
+endif
+
+EXTRA_LIBS_DEP+=$(LIBSLAPD_DEP)
+
+EXTRA_LIBS += $(SASL_LINK)
+
+ifeq ($(ARCH), Linux)
+EXTRA_LIBS += -lcrypt -lpthread
+endif
+
+# In order for debugging to work properly with shared libraries on HP/UX,
+# we need to link with end.o.
+ifeq ($(ARCH), HPUX)
+# need to add arch flags :maybe
+LDFLAGS+=$(ARCH_CFLAGS)
+# HPUX linker voodoo
+ifeq ($(DEBUG), full)
+ifeq ($(USE_64), 1)
+EXTRA_LIBS_TEMP:=$(EXTRA_LIBS)
+EXTRA_LIBS += /opt/langtools/lib/pa20_64/end.o
+else
+EXTRA_LIBS_TEMP:=$(EXTRA_LIBS)
+EXTRA_LIBS += /opt/langtools/lib/end.o
+endif #USE_64
+endif #DEBUG
+# Always put libpthread at the beginning of the library list, otherwise NSPR gets upset (very)
+EXTRA_LIBS_TEMP:=$(EXTRA_LIBS)
+EXTRA_LIBS = -lpthread $(EXTRA_LIBS_TEMP)
+endif #HPUX
+
+#Put SmartHeap at the beginning of the linker library list
+EXTRA_LIBS_TEMP:=$(EXTRA_LIBS)
+EXTRA_LIBS=$(SH_LIB) $(EXTRA_LIBS_TEMP)
+
+ifeq ($(ARCH), SOLARIS)
+ifeq ($(USE_64), 1)
+LDFLAGS+= -xarch=v9
+endif
+endif
+
+ifeq ($(ARCH), SOLARISx86)
+ SH_LIB = -lmtmalloc
+endif
+
+OBJS = $(addprefix $(OBJDEST)/, $(SLAPD_OBJS))
+
+STUB_OBJS = $(addprefix $(OBJDEST)/, stubrepl.o)
+
+ifeq ($(ARCH), WINNT)
+MAIN_OBJ = $(addprefix $(OBJDEST)/, main.o)
+SLAPD_RES = $(addprefix $(SVRCORE_LIBPATH)/, ntsvrcore.res)
+EXTRA_OBJS = $(MAIN_OBJ) $(SLAPD_RES)
+SUBSYSTEM=console
+endif
+
+ifeq ($(ARCH), WINNT)
+SLAPD = $(addprefix $(BINDIR)/, slapd.exe)
+else
+ifdef USE_PURIFY
+SLAPD = $(addprefix $(BINDIR)/, ns-slapd.pure)
+else
+ifdef USE_QUANTIFY
+SLAPD = $(addprefix $(BINDIR)/, ns-slapd.quantify)
+else
+SLAPD = $(addprefix $(BINDIR)/, ns-slapd)
+endif
+endif
+endif
+
+ifeq ($(ARCH), AIX)
+ifdef OLD_AIX_LINKING
+ CCC = svxlC_r
+endif
+
+#LDFLAGS += -bloadmap:$(BINDIR)/loadmap_slapd
+# setup the bmaxdata flag to use 5 segments (1.25 GB).
+# This is a trade-off that allows dbcachesize to be up to about 1GB.
+LDFLAGS += -bmaxdata:0x50000000
+EXTRA_LIBS += -L$(OBJDIR) $(EXE_EXTRA_LIBS)
+LINK_EXE = $(CCC) -bautoexp -brtl $(ALDFLAGS) $(LDFLAGS) \
+ $(RPATHFLAG_PREFIX)$(RPATHFLAG)$(RPATHFLAG_EXTRAS) \
+ -o $@ $(OBJS) $(EXTRA_LIBS)
+endif
+
+#ifeq ($(ARCH),OSF1)
+#LINK_EXE = $(CXX) $(ALDFLAGS) $(LDFLAGS) \
+# $(RPATHFLAG_PREFIX)$(RPATHFLAG)$(RPATHFLAG_EXTRAS) \
+# -o $@ $(OBJS) $(EXTRA_LIBS)
+#endif # OSF1
+
+# Special rule to compile a large source file on Win32:
+# Use the /Zm option to increase internal compiler heap size.
+ifeq ($(ARCH), WINNT)
+$(OBJDEST)/$(FEDSE_OBJ): $(FEDSE_SRC)
+ $(CC) -c /Zm250 $(CFLAGS) $(MCC_INCLUDE) $< -Fo$(OBJDEST)/$(FEDSE_OBJ) $(CBSCFLAGS)
+endif
+
+all: $(OBJDEST) $(BINDIR) $(BUILD_DEP) libslapd $(SLAPD) $(STUB_OBJS)
+
+static: $(OBJDEST) $(LIBSLAPD)
+
+clientSDK: static
+
+.PHONY: libslapd push
+
+libslapd $(LIBSLAPD_DEP):
+ $(MAKE) -f libmakefile $(MFLAGS) all
+
+#$(SLAPD_RES): $(LDAP_SRC)/libraries/libutil/ntslapd.rc \
+# $(DIRVER_H)
+# $(RSC) -fo $(SLAPD_RES) -i. -i $(OBJDIR)/include $<
+
+$(SLAPD): $(SH_LIB_DEP) $(OBJS) $(MAIN_OBJ) $(SLAPD_RES) $(EXTRA_LIBS_DEP)
+ $(QUANTIFY) $(PURIFY) $(PUREOPTS) $(LINK_EXE) $(EXTRA_OBJS) $(DB_LIB)
+
+veryclean: clean
+
+clean:
+ -$(RM) $(OBJS)
+ -$(RM) $(STUB_OBJS)
+ifeq ($(ARCH), WINNT)
+ -$(RM) $(MAIN_OBJ)
+# -$(RM) $(SLAPD_RES)
+endif
+ -$(RM) $(SLAPD)
+ $(MAKE) -f libmakefile clean
+
+# Target to push the built binary to an installed server
+SLAPD_PUSH = $(addprefix $(INSTDIR)/, bin/slapd/server/slapd.exe)
+push: $(SLAPD_PUSH)
+ $(MAKE) -f libmakefile $(MFLAGS) push
+
+$(SLAPD_PUSH): $(SLAPD)
+ cp $(SLAPD) $(SLAPD_PUSH)
+
diff --git a/ldap/servers/slapd/abandon.c b/ldap/servers/slapd/abandon.c
new file mode 100644
index 00000000..df099cda
--- /dev/null
+++ b/ldap/servers/slapd/abandon.c
@@ -0,0 +1,141 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+/* abandon.c - decode and handle an ldap abandon operation */
+
+/*
+ * 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 <stdio.h>
+#include <sys/types.h>
+#ifndef _WIN32
+#include <sys/socket.h>
+#endif
+#include "slap.h"
+
+void
+do_abandon( Slapi_PBlock *pb )
+{
+ int id, err, suppressed_by_plugin = 0;
+ long long_id;
+ Operation *o;
+ BerElement *ber = pb->pb_op->o_ber;
+
+ LDAPDebug( LDAP_DEBUG_TRACE, "do_abandon\n", 0, 0, 0 );
+
+ /*
+ * Parse the abandon request. It looks like this:
+ *
+ * AbandonRequest := MessageID
+ */
+
+ if ( ber_scanf( ber, "i", &long_id ) == LBER_ERROR ) {
+ LDAPDebug( LDAP_DEBUG_ANY,
+ "ber_scanf failed (op=Abandon; params=ID)\n",
+ 0, 0 ,0 );
+ return;
+ }
+ id=long_id;
+
+ slapi_pblock_set( pb, SLAPI_ABANDON_MSGID, &id );
+
+ /*
+ * in LDAPv3 there can be optional control extensions on
+ * the end of an LDAPMessage. we need to read them in and
+ * pass them to the backend.
+ */
+ if ( (err = get_ldapmessage_controls( pb, ber, NULL )) != 0 ) {
+ LDAPDebug( LDAP_DEBUG_ANY,
+ "get_ldapmessage_controls failed: %d (%s) (op=Abandon)\n",
+ err, ldap_err2string( err ), 0);
+ /* LDAP does not allow any response to an abandon */
+ return;
+ }
+
+ LDAPDebug( LDAP_DEBUG_ARGS, "do_abandon: id %d\n", id, 0 ,0 );
+
+ /*
+ * find the operation being abandoned and set the o_abandon
+ * flag. We don't allow the operation to abandon itself.
+ * It's up to the backend to periodically check this
+ * flag and abort the operation at a convenient time.
+ */
+
+ PR_Lock( pb->pb_conn->c_mutex );
+ for ( o = pb->pb_conn->c_ops; o != NULL; o = o->o_next ) {
+ if ( o->o_msgid == id && o != pb->pb_op)
+ break;
+ }
+
+ if ( o != NULL ) {
+ const Slapi_DN *ts = NULL;
+ /*
+ * call the pre-abandon plugins. if they succeed, call
+ * the backend abandon function. then call the post-abandon
+ * plugins.
+ */
+ /* ONREPL - plugins should be passed some information about abandoned operation */
+ /* target spec and abandoned operation type are used to decide which plugins
+ are applicable for the operation */
+ ts = operation_get_target_spec (o);
+ if (ts) {
+ operation_set_target_spec (pb->pb_op, ts);
+ } else {
+ LDAPDebug( LDAP_DEBUG_TRACE, "do_abandon: no target spec of abandoned operation\n", 0,0,0);
+ }
+
+ operation_set_abandoned_op (pb->pb_op, o->o_abandoned_op);
+ if ( plugin_call_plugins( pb, SLAPI_PLUGIN_PRE_ABANDON_FN )
+ == 0 ) {
+ int rc = 0;
+
+ if ( o->o_status != SLAPI_OP_STATUS_RESULT_SENT ) {
+ o->o_status = SLAPI_OP_STATUS_ABANDONED;
+ } else {
+ o = NULL; /* nothing was abandoned */
+ }
+
+ slapi_pblock_set( pb, SLAPI_PLUGIN_OPRETURN, &rc );
+ plugin_call_plugins( pb, SLAPI_PLUGIN_POST_ABANDON_FN );
+ } else {
+ suppressed_by_plugin = 1;
+ }
+ } else {
+ LDAPDebug( LDAP_DEBUG_TRACE, "do_abandon: op not found\n", 0, 0,
+ 0 );
+ }
+
+ if ( NULL == o ) {
+ slapi_log_access( LDAP_DEBUG_STATS, "conn=%d op=%d ABANDON"
+ " targetop=NOTFOUND msgid=%d\n",
+ pb->pb_conn->c_connid, pb->pb_op->o_opid, id );
+ } else if ( suppressed_by_plugin ) {
+ slapi_log_access( LDAP_DEBUG_STATS, "conn=%d op=%d ABANDON"
+ " targetop=SUPPRESSED-BY-PLUGIN msgid=%d\n",
+ pb->pb_conn->c_connid, pb->pb_op->o_opid, id );
+ } else {
+ slapi_log_access( LDAP_DEBUG_STATS, "conn=%d op=%d ABANDON"
+ " targetop=%d msgid=%d nentries=%d etime=%d\n",
+ pb->pb_conn->c_connid, pb->pb_op->o_opid, o->o_opid, id,
+ o->o_results.r.r_search.nentries, current_time() - o->o_time );
+
+ }
+
+ PR_Unlock( pb->pb_conn->c_mutex );
+ /*
+ * Wake up the persistent searches, so they
+ * can notice if they've been abandoned.
+ */
+ ps_wakeup_all();
+}
diff --git a/ldap/servers/slapd/add.c b/ldap/servers/slapd/add.c
new file mode 100644
index 00000000..3e58f672
--- /dev/null
+++ b/ldap/servers/slapd/add.c
@@ -0,0 +1,781 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+/*
+ * 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 <errno.h>
+#include <stdio.h>
+#include <string.h>
+#include <time.h>
+#include <sys/types.h>
+#ifndef _WIN32
+#include <sys/socket.h>
+#endif
+#include "slap.h"
+#include "pratom.h"
+#include "csngen.h"
+
+/* Forward declarations */
+static int add_internal_pb (Slapi_PBlock *pb);
+static void op_shared_add (Slapi_PBlock *pb);
+static void add_created_attrs(Operation *op, Slapi_Entry *e);
+static void handle_fast_add(Slapi_PBlock *pb, Slapi_Entry *entry);
+static void add_uniqueid (Slapi_Entry *e);
+static PRBool check_oc_subentry(Slapi_Entry *e, struct berval **vals, char *normtype);
+
+/* This function is called to process operation that come over external connections */
+void
+do_add( Slapi_PBlock *pb )
+{
+ Slapi_Operation *operation;
+ BerElement *ber;
+ char *last;
+ unsigned long len, tag;
+ Slapi_Entry *e = NULL;
+ int err;
+ int rc;
+ char ebuf[ BUFSIZ ];
+ PRBool searchsubentry=PR_TRUE;
+
+ LDAPDebug( LDAP_DEBUG_TRACE, "do_add\n", 0, 0, 0 );
+
+ slapi_pblock_get( pb, SLAPI_OPERATION, &operation);
+ ber = operation->o_ber;
+
+ /* count the add request */
+ PR_AtomicIncrement(g_get_global_snmp_vars()->ops_tbl.dsAddEntryOps);
+
+ /*
+ * 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 */
+ {
+ char *dn;
+ if ( ber_scanf( ber, "{a", &dn ) == LBER_ERROR ) {
+ LDAPDebug( LDAP_DEBUG_ANY,
+ "ber_scanf failed (op=Add; params=DN)\n", 0, 0, 0 );
+ op_shared_log_error_access (pb, "ADD", "???", "decoding error");
+ send_ldap_result( pb, LDAP_PROTOCOL_ERROR, NULL,
+ "decoding error", 0, NULL );
+ return;
+ }
+ e = slapi_entry_alloc();
+ slapi_entry_init(e,dn,NULL); /* Responsibility for DN is passed to the Entry. */
+ }
+ LDAPDebug( LDAP_DEBUG_ARGS, " do_add: dn (%s)\n", slapi_entry_get_dn_const(e), 0, 0 );
+
+ /* get the attrs */
+ for ( tag = ber_first_element( ber, &len, &last );
+ tag != LBER_DEFAULT && tag != LBER_END_OF_SEQORSET;
+ tag = ber_next_element( ber, &len, last ) ) {
+ char *type = NULL, *normtype = NULL;
+ struct berval **vals;
+ if ( ber_scanf( ber, "{a{V}}", &type, &vals ) == LBER_ERROR ) {
+ op_shared_log_error_access (pb, "ADD", slapi_sdn_get_dn (slapi_entry_get_sdn_const(e)), "decoding error");
+ send_ldap_result( pb, LDAP_PROTOCOL_ERROR, NULL,
+ "decoding error", 0, NULL );
+ goto free_and_return;
+ }
+
+ if ( vals == NULL ) {
+ LDAPDebug( LDAP_DEBUG_ANY, "no values for type %s\n", type, 0, 0 );
+ op_shared_log_error_access (pb, "ADD", slapi_sdn_get_dn (slapi_entry_get_sdn_const(e)), "null value");
+ send_ldap_result( pb, LDAP_PROTOCOL_ERROR, NULL, NULL,
+ 0, NULL );
+ free( type );
+ goto free_and_return;
+ }
+
+ normtype = slapi_attr_syntax_normalize(type);
+ if ( !normtype || !*normtype ) {
+ rc = LDAP_INVALID_SYNTAX;
+ PR_snprintf (ebuf, BUFSIZ, "invalid type '%s'", type);
+ ebuf[BUFSIZ-1] = '\0';
+ op_shared_log_error_access (pb, "ADD", slapi_sdn_get_dn (slapi_entry_get_sdn_const(e)), ebuf);
+ send_ldap_result( pb, rc, NULL, ebuf, 0, NULL );
+ free( type );
+ slapi_ch_free( (void**)&normtype );
+ ber_bvecfree( vals );
+ goto free_and_return;
+ }
+ free( type );
+
+ /* for now we just ignore attributes that client is not allowed
+ to modify so not to break existing clients */
+ if (op_shared_is_allowed_attr (normtype, pb->pb_conn->c_isreplication_session)){
+ if (( rc = slapi_entry_add_values( e, normtype, vals ))
+ != LDAP_SUCCESS ) {
+ slapi_log_access( LDAP_DEBUG_STATS,
+ "conn=%d op=%d ADD dn=\"%s\", add values for type %s failed\n",
+ pb->pb_conn->c_connid, operation->o_opid,
+ escape_string( slapi_entry_get_dn_const(e), ebuf ), normtype );
+ send_ldap_result( pb, rc, NULL, NULL, 0, NULL );
+
+ slapi_ch_free( (void**)&normtype );
+ ber_bvecfree( vals );
+ goto free_and_return;
+ }
+
+ /* if this is uniqueid attribute, set uniqueid field of the entry */
+ if (strcasecmp (normtype, SLAPI_ATTR_UNIQUEID) == 0)
+ {
+ e->e_uniqueid = slapi_ch_strdup (vals[0]->bv_val);
+ }
+ if(searchsubentry) searchsubentry=check_oc_subentry(e,vals,normtype);
+ }
+ slapi_ch_free( (void**)&normtype );
+ ber_bvecfree( vals );
+ }
+
+ if ( tag == LBER_DEFAULT ) {
+ op_shared_log_error_access (pb, "ADD", slapi_sdn_get_dn (slapi_entry_get_sdn_const(e)), "decoding error");
+ send_ldap_result( pb, LDAP_PROTOCOL_ERROR, NULL,
+ "decoding error", 0, NULL );
+ goto free_and_return;
+ }
+
+ /*
+ * in LDAPv3 there can be optional control extensions on
+ * the end of an LDAPMessage. we need to read them in and
+ * pass them to the backend.
+ */
+ if ( (err = get_ldapmessage_controls( pb, ber, NULL )) != 0 ) {
+ op_shared_log_error_access (pb, "ADD", slapi_sdn_get_dn (slapi_entry_get_sdn_const(e)),
+ "failed to decode LDAP controls");
+ send_ldap_result( pb, err, NULL, NULL, 0, NULL );
+ goto free_and_return;
+ }
+
+ slapi_pblock_set( pb, SLAPI_REQUESTOR_ISROOT, &operation->o_isroot );
+ slapi_pblock_set( pb, SLAPI_ADD_ENTRY, e );
+
+ if (pb->pb_conn->c_flags & CONN_FLAG_IMPORT) {
+ /* this add is actually part of a bulk import -- punt */
+ handle_fast_add(pb, e);
+ } else {
+ op_shared_add ( pb );
+ }
+
+ /* make sure that we don't free entry if it is successfully added */
+ e = NULL;
+
+free_and_return:;
+ if (e)
+ slapi_entry_free (e);
+
+}
+
+/* This function is used to issue internal add operation
+ This is an old style API. Its use is discoraged because it is not extendable and
+ because it does not allow to check whether plugin has right to access part of the
+ tree it is trying to modify. Use slapi_add_internal_pb instead */
+Slapi_PBlock *
+slapi_add_internal(const char *idn,
+ LDAPMod **iattrs,
+ LDAPControl **controls,
+ int dummy)
+{
+ Slapi_Entry *e;
+ Slapi_PBlock *result_pb = NULL;
+ int opresult= -1;
+
+ if(iattrs == NULL)
+ {
+ opresult = LDAP_PARAM_ERROR;
+ goto done;
+ }
+
+ opresult = slapi_mods2entry (&e, (char*)idn, iattrs);
+ if (opresult != LDAP_SUCCESS)
+ {
+ goto done;
+ }
+
+ result_pb= slapi_add_entry_internal(e, controls, dummy);
+
+done:
+ if(result_pb==NULL)
+ {
+ result_pb = slapi_pblock_new();
+ pblock_init(result_pb);
+ slapi_pblock_set(result_pb, SLAPI_PLUGIN_INTOP_RESULT, &opresult);
+ }
+
+ return result_pb;
+}
+
+/* This function is used to issue internal add operation
+ This is an old style API. Its use is discoraged because it is not extendable and
+ because it does not allow to check whether plugin has right to access part of the
+ tree it is trying to modify. Use slapi_add_internal_pb instead
+ Beware: The entry is consumed. */
+Slapi_PBlock *
+slapi_add_entry_internal(Slapi_Entry *e, LDAPControl **controls, int dummy)
+{
+ Slapi_PBlock pb;
+ Slapi_PBlock *result_pb = NULL;
+ int opresult;
+
+ pblock_init(&pb);
+
+ slapi_add_entry_internal_set_pb (&pb, e, controls, plugin_get_default_component_id(), 0);
+
+ add_internal_pb (&pb);
+
+ result_pb = slapi_pblock_new();
+ if (result_pb)
+ {
+ slapi_pblock_get(&pb, SLAPI_PLUGIN_INTOP_RESULT, &opresult);
+ slapi_pblock_set(result_pb, SLAPI_PLUGIN_INTOP_RESULT, &opresult);
+ }
+ pblock_done(&pb);
+
+ return result_pb;
+}
+
+/* This is new style API to issue internal add operation.
+ pblock should contain the following data (can be set via call to slapi_add_internal_set_pb):
+ SLAPI_TARGET_DN set to dn of the new entry
+ SLAPI_CONTROLS_ARG set to request controls if present
+ SLAPI_ADD_ENTRY set to Slapi_Entry to add
+ Beware: The entry is consumed. */
+int slapi_add_internal_pb (Slapi_PBlock *pb)
+{
+ if (pb == NULL)
+ return -1;
+
+ if (!allow_operation (pb))
+ {
+ slapi_send_ldap_result( pb, LDAP_UNWILLING_TO_PERFORM, NULL,
+ "This plugin is not configured to access operation target data", 0, NULL );
+ return 0;
+ }
+
+ return add_internal_pb (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_Entry *e;
+ int rc;
+
+ if (pb == NULL || dn == NULL || attrs == NULL)
+ {
+ slapi_log_error(SLAPI_LOG_PLUGIN, NULL, "slapi_add_internal_set_pb: invalid argument\n");
+ return LDAP_PARAM_ERROR;
+ }
+
+ rc = slapi_mods2entry (&e, dn, attrs);
+ if (rc == LDAP_SUCCESS)
+ {
+ slapi_add_entry_internal_set_pb (pb, e, controls, plugin_identity, operation_flags);
+ }
+
+ return rc;
+}
+
+
+/* Initialize a pblock for a call to slapi_add_internal_pb() */
+void slapi_add_entry_internal_set_pb (Slapi_PBlock *pb, Slapi_Entry *e, LDAPControl **controls,
+ Slapi_ComponentId *plugin_identity, int operation_flags)
+{
+ Operation *op;
+ PR_ASSERT (pb != NULL);
+ if (pb == NULL || e == NULL)
+ {
+ slapi_log_error(SLAPI_LOG_PLUGIN, NULL, "slapi_add_entry_internal_set_pb: invalid argument\n");
+ return;
+ }
+
+ op = internal_operation_new(SLAPI_OPERATION_ADD,operation_flags);
+ slapi_pblock_set(pb, SLAPI_OPERATION, op);
+ slapi_pblock_set(pb, SLAPI_ADD_ENTRY, e);
+ slapi_pblock_set(pb, SLAPI_CONTROLS_ARG, controls);
+ slapi_pblock_set(pb, SLAPI_PLUGIN_IDENTITY, plugin_identity);
+}
+
+/* Helper functions */
+
+static int add_internal_pb (Slapi_PBlock *pb)
+{
+ LDAPControl **controls;
+ Operation *op;
+ int opresult = 0;
+ Slapi_Entry *e;
+
+ PR_ASSERT (pb != NULL);
+
+ slapi_pblock_get(pb, SLAPI_ADD_ENTRY, &e);
+ slapi_pblock_get(pb, SLAPI_CONTROLS_ARG, &controls);
+
+ if (e == NULL)
+ {
+ opresult = LDAP_PARAM_ERROR;
+ slapi_pblock_set(pb, SLAPI_PLUGIN_INTOP_RESULT, &opresult);
+ return 0;
+ }
+
+ slapi_pblock_get(pb, SLAPI_OPERATION, &op);
+ op->o_handler_data = &opresult;
+ op->o_result_handler = internal_getresult_callback;
+
+ slapi_pblock_set(pb, SLAPI_REQCONTROLS, controls);
+
+ /* set parameters common to all internal operations */
+ set_common_params (pb);
+
+ /* set actions taken to process the operation */
+ set_config_params (pb);
+
+ /* perform the add operation */
+ op_shared_add (pb);
+
+ slapi_pblock_set(pb, SLAPI_PLUGIN_INTOP_RESULT, &opresult);
+
+ return 0;
+}
+
+/* Code shared between regular and internal add operation */
+static void op_shared_add (Slapi_PBlock *pb)
+{
+ Slapi_Operation *operation;
+ Slapi_Entry *e, *pse;
+ Slapi_Backend *be = NULL;
+ int err;
+ char ebuf[BUFSIZ];
+ int internal_op, repl_op, legacy_op, lastmod;
+ char *pwdtype = NULL;
+ Slapi_Value **unhashed_password_vals = NULL;
+ Slapi_Attr *attr = NULL;
+ Slapi_Entry *referral;
+ char errorbuf[BUFSIZ];
+ struct slapdplugin *p = NULL;
+
+ slapi_pblock_get (pb, SLAPI_OPERATION, &operation);
+ slapi_pblock_get (pb, SLAPI_ADD_ENTRY, &e);
+ slapi_pblock_get (pb, SLAPI_IS_REPLICATED_OPERATION, &repl_op);
+ slapi_pblock_get (pb, SLAPI_IS_LEGACY_REPLICATED_OPERATION, &legacy_op);
+ internal_op= operation_is_flag_set(operation, OP_FLAG_INTERNAL);
+
+ /* target spec is used to decide which plugins are applicable for the operation */
+ operation_set_target_spec (operation, slapi_entry_get_sdn (e));
+
+ if ((err = slapi_entry_add_rdn_values(e)) != LDAP_SUCCESS)
+ {
+ send_ldap_result(pb, err, NULL, "failed to add RDN values", 0, NULL);
+ goto done;
+ }
+
+
+ if (operation_is_flag_set(operation,OP_FLAG_ACTION_LOG_ACCESS))
+ {
+ if ( !internal_op )
+ {
+ slapi_log_access(LDAP_DEBUG_STATS, "conn=%d op=%d ADD dn=\"%s\"\n",
+ pb->pb_conn->c_connid,
+ operation->o_opid,
+ escape_string(slapi_entry_get_dn_const(e), ebuf));
+ }
+ else
+ {
+ slapi_log_access(LDAP_DEBUG_ARGS, "conn=%s op=%d ADD dn=\"%s\"\n",
+ LOG_INTERNAL_OP_CON_ID,
+ LOG_INTERNAL_OP_OP_ID,
+ escape_string(slapi_entry_get_dn_const(e), ebuf));
+ }
+ }
+
+ /*
+ * We could be serving multiple database backends. Select the
+ * appropriate one.
+ */
+ if ((err = slapi_mapping_tree_select(pb, &be, &referral, errorbuf)) != LDAP_SUCCESS) {
+ send_ldap_result(pb, err, NULL, errorbuf, 0, NULL);
+ be = NULL;
+ goto done;
+ }
+
+ if (referral)
+ {
+ int managedsait;
+
+ slapi_pblock_get(pb, SLAPI_MANAGEDSAIT, &managedsait);
+ if (managedsait)
+ {
+ send_ldap_result(pb, LDAP_UNWILLING_TO_PERFORM, NULL,
+ "cannot update referral", 0, NULL);
+ slapi_entry_free(referral);
+ goto done;
+ }
+
+ slapi_pblock_set(pb, SLAPI_TARGET_DN, (void*)slapi_sdn_get_ndn(operation_get_target_spec (operation)));
+ send_referrals_from_entry(pb,referral);
+ slapi_entry_free(referral);
+ goto done;
+ }
+
+ if (!slapi_be_is_flag_set(be,SLAPI_BE_FLAG_REMOTE_DATA)) {
+ /* look for user password attribute */
+ slapi_entry_attr_find(e, SLAPI_USERPWD_ATTR, &attr);
+ if (attr && !repl_op)
+ {
+ Slapi_Value **present_values;
+ present_values= attr_get_present_values(attr);
+
+ /* check password syntax */
+ if (check_pw_syntax(pb, slapi_entry_get_sdn_const(e), present_values, NULL, e, 0) == 0)
+ {
+ Slapi_Value **vals= NULL;
+ valuearray_add_valuearray(&unhashed_password_vals, present_values, 0);
+ valuearray_add_valuearray(&vals, present_values, 0);
+ pw_encodevals(vals);
+ add_password_attrs(pb, operation, e);
+ slapi_entry_attr_replace_sv(e, SLAPI_USERPWD_ATTR, vals);
+ valuearray_free(&vals);
+
+ /* Add the unhashed password pseudo-attribute to the entry */
+ pwdtype = slapi_attr_syntax_normalize(PSEUDO_ATTR_UNHASHEDUSERPASSWORD);
+ slapi_entry_add_values_sv(e, pwdtype, unhashed_password_vals);
+ } else {
+ /* error result is sent from check_pw_syntax */
+ goto done;
+ }
+ }
+
+ /* look for multiple backend local credentials or replication local credentials */
+ for ( p = get_plugin_list(PLUGIN_LIST_REVER_PWD_STORAGE_SCHEME); p != NULL;
+ p = p->plg_next )
+ {
+ char *L_attr = NULL;
+ int i=0;
+
+ /* Get the appropriate decoding function */
+ for ( L_attr = p->plg_argv[i]; i<p->plg_argc; L_attr = p->plg_argv[++i])
+ {
+ /* look for multiple backend local credentials or replication local credentials */
+ char *L_normalized = slapi_attr_syntax_normalize(L_attr);
+ slapi_entry_attr_find(e, L_normalized, &attr);
+ if (attr)
+ {
+ Slapi_Value **present_values = NULL;
+ Slapi_Value **vals = NULL;
+
+ present_values= attr_get_present_values(attr);
+
+ valuearray_add_valuearray(&vals, present_values, 0);
+ pw_rever_encode(vals, L_normalized);
+ slapi_entry_attr_replace_sv(e, L_normalized, vals);
+ valuearray_free(&vals);
+ }
+ if (L_normalized)
+ slapi_ch_free ((void**)&L_normalized);
+ }
+ }
+ }
+
+ slapi_pblock_set(pb, SLAPI_BACKEND, be);
+ /* we set local password policy ACI for non-replicated operations only */
+ if (!repl_op &&
+ !operation_is_flag_set(operation, OP_FLAG_REPL_FIXUP) &&
+ !operation_is_flag_set(operation, OP_FLAG_LEGACY_REPLICATION_DN) &&
+ !slapi_be_is_flag_set(be,SLAPI_BE_FLAG_REMOTE_DATA) &&
+ !slapi_be_private(be) &&
+ slapi_be_issuffix (be, slapi_entry_get_sdn_const(e)))
+ {
+ /* this is a suffix. update the pw aci */
+ slapdFrontendConfig_t *slapdFrontendConfig;
+ slapdFrontendConfig = getFrontendConfig();
+ pw_add_allowchange_aci(e, !slapdFrontendConfig->pw_policy.pw_change &&
+ !slapdFrontendConfig->pw_policy.pw_must_change);
+ }
+
+ /* can get lastmod only after backend is selected */
+ slapi_pblock_get(pb, SLAPI_BE_LASTMOD, &lastmod);
+ if (!repl_op && lastmod)
+ {
+ add_created_attrs(operation, e);
+ /* JCM - We could end up with an entry without a uniqueid...??? */
+ }
+
+ /* expand objectClass values to reflect the inheritance hierarchy */
+ if (!repl_op) {
+ slapi_schema_expand_objectclasses( e );
+ }
+
+
+ /* uniqueid needs to be generated for entries added during legacy replication */
+ if (legacy_op)
+ add_uniqueid (e);
+
+ /*
+ * call the pre-add plugins. if they succeed, call
+ * the backend add function. then call the post-add
+ * plugins.
+ */
+
+ slapi_pblock_set(pb, SLAPI_ADD_TARGET,
+ (char*)slapi_sdn_get_ndn(slapi_entry_get_sdn_const(e)));
+ if (plugin_call_plugins(pb, internal_op ? SLAPI_PLUGIN_INTERNAL_PRE_ADD_FN :
+ SLAPI_PLUGIN_PRE_ADD_FN) == 0)
+ {
+ int rc;
+ Slapi_Entry *ec;
+ char *add_target_dn;
+
+ slapi_pblock_set(pb, SLAPI_PLUGIN, be->be_database);
+ set_db_default_result_handlers(pb);
+
+ /* Remove the unhashed password pseudo-attribute
+ from the entry before duplicating the entry */
+
+ if (unhashed_password_vals)
+ {
+ slapi_entry_delete_values(e, pwdtype, NULL);
+ }
+
+ /* because be_add frees the entry */
+ ec = slapi_entry_dup(e);
+ add_target_dn= slapi_ch_strdup(slapi_sdn_get_ndn(slapi_entry_get_sdn_const(ec)));
+ slapi_pblock_set(pb, SLAPI_ADD_TARGET, add_target_dn);
+
+ if (be->be_add != NULL)
+ {
+ rc = (*be->be_add)(pb);
+ slapi_pblock_set(pb, SLAPI_ADD_ENTRY, ec);
+ if (rc == 0)
+ {
+ /* acl is not enabled for internal operations */
+ /* don't update aci store for remote acis */
+ if ((!internal_op) &&
+ (!slapi_be_is_flag_set(be,SLAPI_BE_FLAG_REMOTE_DATA)))
+ {
+ plugin_call_acl_mods_update (pb, SLAPI_OPERATION_ADD);
+ }
+
+ if (operation_is_flag_set(operation,OP_FLAG_ACTION_LOG_AUDIT))
+ {
+ write_audit_log_entry(pb); /* Record the operation in the audit log */
+ }
+
+ slapi_pblock_get(pb, SLAPI_ENTRY_POST_OP, &pse);
+ do_ps_service(pse, NULL, LDAP_CHANGETYPE_ADD, 0);
+
+ e = NULL; /* if be_add succeeded, then e is consumed. Must prevent e from being free'd. */
+ }
+ else
+ {
+ if (rc == SLAPI_FAIL_DISKFULL)
+ {
+ operation_out_of_disk_space();
+ goto done;
+ }
+ }
+ }
+ else
+ {
+ send_ldap_result(pb, LDAP_UNWILLING_TO_PERFORM, NULL,
+ "Function not implemented", 0, NULL);
+ }
+
+ /* Reattach the unhashed password pseudo-attribute
+ to the entry copy (ec), before calling the postop plugin */
+ if(unhashed_password_vals)
+ {
+ slapi_entry_add_values_sv(ec, pwdtype, unhashed_password_vals);
+ }
+
+ slapi_pblock_set(pb, SLAPI_PLUGIN_OPRETURN, &rc);
+ plugin_call_plugins(pb, internal_op ? SLAPI_PLUGIN_INTERNAL_POST_ADD_FN :
+ SLAPI_PLUGIN_POST_ADD_FN);
+ slapi_entry_free(ec);
+ slapi_pblock_get(pb, SLAPI_ADD_TARGET, &add_target_dn);
+ slapi_ch_free((void**)&add_target_dn);
+ }
+
+done:
+ if (be)
+ slapi_be_Unlock(be);
+ slapi_pblock_get(pb, SLAPI_ENTRY_POST_OP, &pse);
+ slapi_entry_free(pse);
+ slapi_ch_free((void **)&operation->o_params.p.p_add.parentuniqueid);
+ slapi_entry_free(e);
+ valuearray_free(&unhashed_password_vals);
+ slapi_ch_free((void**)&pwdtype);
+}
+
+static void
+add_created_attrs(Operation *op, Slapi_Entry *e)
+{
+ char buf[20];
+ struct berval bv;
+ struct berval *bvals[2];
+ time_t curtime;
+ struct tm ltm;
+
+ LDAPDebug(LDAP_DEBUG_TRACE, "add_created_attrs\n", 0, 0, 0);
+
+ bvals[0] = &bv;
+ bvals[1] = NULL;
+
+ if (slapi_sdn_isempty(&op->o_sdn)) {
+ bv.bv_val = "";
+ bv.bv_len = strlen(bv.bv_val);
+ } else {
+ bv.bv_val = (char*)slapi_sdn_get_dn(&op->o_sdn);
+ bv.bv_len = strlen(bv.bv_val);
+ }
+ slapi_entry_attr_replace(e, "creatorsname", bvals);
+ slapi_entry_attr_replace(e, "modifiersname", bvals);
+
+ curtime = current_time();
+#ifdef _WIN32
+{
+ struct tm *pt;
+ pt = gmtime(&curtime);
+ memcpy(&ltm, pt, sizeof(struct tm));
+}
+#else
+ gmtime_r(&curtime, &ltm);
+#endif
+ strftime(buf, sizeof(buf), "%Y%m%d%H%M%SZ", &ltm);
+
+ bv.bv_val = buf;
+ bv.bv_len = strlen(bv.bv_val);
+ slapi_entry_attr_replace(e, "createtimestamp", bvals);
+
+ bv.bv_val = buf;
+ bv.bv_len = strlen(bv.bv_val);
+ slapi_entry_attr_replace(e, "modifytimestamp", bvals);
+
+ add_uniqueid (e);
+}
+
+
+static void handle_fast_add(Slapi_PBlock *pb, Slapi_Entry *entry)
+{
+ Slapi_Backend *be;
+ Slapi_Operation *operation;
+ int ret;
+
+ be = pb->pb_conn->c_bi_backend;
+
+ if ((be == NULL) || (be->be_wire_import == NULL)) {
+ /* can this even happen? */
+ LDAPDebug(LDAP_DEBUG_ANY,
+ "handle_fast_add: backend not supported\n", 0, 0, 0);
+ send_ldap_result(pb, LDAP_NOT_SUPPORTED, NULL, NULL, 0, NULL);
+ return;
+ }
+
+ /* ensure that the RDN values are present as attribute values */
+ if ((ret = slapi_entry_add_rdn_values(entry)) != LDAP_SUCCESS) {
+ send_ldap_result(pb, ret, NULL, "failed to add RDN values", 0, NULL);
+ return;
+ }
+
+ /* schema check */
+ slapi_pblock_get(pb, SLAPI_OPERATION, &operation);
+ if (operation_is_flag_set(operation, OP_FLAG_ACTION_SCHEMA_CHECK) &&
+ (slapi_entry_schema_check(pb, entry) != 0)) {
+ char *errtext;
+ LDAPDebug(LDAP_DEBUG_TRACE, "entry failed schema check\n", 0, 0, 0);
+ slapi_pblock_get(pb, SLAPI_PB_RESULT_TEXT, &errtext);
+ send_ldap_result(pb, LDAP_OBJECT_CLASS_VIOLATION, NULL, errtext, 0, NULL);
+ slapi_entry_free(entry);
+ return;
+ }
+
+ /* Check if the entry being added is a Tombstone. Could be if we are
+ * doing a replica init. */
+ if (slapi_entry_attr_hasvalue(entry, SLAPI_ATTR_OBJECTCLASS,
+ SLAPI_ATTR_VALUE_TOMBSTONE)) {
+ entry->e_flags |= SLAPI_ENTRY_FLAG_TOMBSTONE;
+ }
+
+ slapi_pblock_set(pb, SLAPI_BACKEND, be);
+ slapi_pblock_set(pb, SLAPI_BULK_IMPORT_ENTRY, entry);
+ ret = SLAPI_BI_STATE_ADD;
+ slapi_pblock_set(pb, SLAPI_BULK_IMPORT_STATE, &ret);
+ ret = (*be->be_wire_import)(pb);
+ if (ret != 0) {
+ if (ret != LDAP_BUSY) {
+ LDAPDebug(LDAP_DEBUG_ANY,
+ "wire import: error during import (%d)\n",
+ ret, 0, 0);
+ } else {
+ LDAPDebug(LDAP_DEBUG_TRACE,
+ "wire import: asking client to wait before resuming (returning LDAP_BUSY)\n",
+ 0, 0, 0);
+ }
+ send_ldap_result(pb,
+ LDAP_BUSY == ret ? LDAP_BUSY : LDAP_OPERATIONS_ERROR,
+ NULL, NULL, 0, NULL);
+ slapi_entry_free(entry);
+
+ if (LDAP_BUSY != ret) {
+ /* turn off fast replica init -- import is now aborted */
+ pb->pb_conn->c_bi_backend = NULL;
+ pb->pb_conn->c_flags &= ~CONN_FLAG_IMPORT;
+ }
+ return;
+ }
+
+ send_ldap_result(pb, LDAP_SUCCESS, NULL, NULL, 0, NULL);
+ return;
+}
+
+static void
+add_uniqueid (Slapi_Entry *e)
+{
+ char *uniqueid;
+ int rc;
+
+ /* generate uniqueID for the entry */
+ rc = slapi_uniqueIDGenerateString (&uniqueid);
+ if (rc == UID_SUCCESS)
+ {
+ slapi_entry_set_uniqueid (e, uniqueid);
+ }
+ else
+ {
+ LDAPDebug(LDAP_DEBUG_ANY, "add_created_attrs: uniqueid generation failed for %s; error = %d\n",
+ slapi_entry_get_dn_const(e), rc, 0);
+ }
+}
+
+static PRBool
+check_oc_subentry(Slapi_Entry *e, struct berval **vals, char *normtype) {
+ int n;
+
+ PRBool subentry=PR_TRUE;
+ for(n=0; vals != NULL && vals[n] != NULL; n++) {
+ if((strcasecmp(normtype,"objectclass") == 0)
+ && (strncasecmp((const char *)vals[n]->bv_val,"ldapsubentry",vals[n]->bv_len) == 0)) {
+ e->e_flags |= SLAPI_ENTRY_LDAPSUBENTRY;
+ subentry=PR_FALSE;
+ break;
+ }
+ }
+ return subentry;
+}
diff --git a/ldap/servers/slapd/agtmmap.c b/ldap/servers/slapd/agtmmap.c
new file mode 100644
index 00000000..76abedeb
--- /dev/null
+++ b/ldap/servers/slapd/agtmmap.c
@@ -0,0 +1,330 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+/********************************************************************
+ *
+ * agtmmap.c: Memory Map interface for SNMP sub-agent for
+ * Netscape Directory Server stats (for UNIX environment).
+ *
+ * Revision History:
+ * 07/22/97 Created Steve Ross
+ *
+ *
+ **********************************************************************/
+
+
+#include "agtmmap.h"
+#ifndef _WIN32
+#include <sys/mman.h>
+#include <unistd.h>
+#else
+#define WIN32_LEAN_AND_MEAN
+#include <windows.h>
+#include <time.h>
+#include "nt/regparms.h"
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/stat.h>
+
+#ifndef _WIN32
+agt_mmap_context_t mmap_tbl [2] = { {AGT_MAP_UNINIT, -1, (caddr_t) -1},
+ {AGT_MAP_UNINIT, -1, (caddr_t) -1} };
+#else
+agt_mmap_context_t mmap_tbl[2] = { {AGT_MAP_UNINIT, NULL, (caddr_t) -1, NULL},
+ {AGT_MAP_UNINIT, NULL, (caddr_t) -1, NULL} };
+#endif /* ! _WIN32 */
+
+
+/****************************************************************************
+ *
+ * agt_mopen_stats () - open and Memory Map the stats file. agt_mclose_stats()
+ * must be called prior to invoking agt_mopen_stats() again.
+ * Inputs:
+ * statsfile -> Name of stats file including full path or NULL.
+ * If NULL, default (slapd.stats) is assumed.
+ * mode -> Must be one of O_RDONLY / O_RDWR.
+ * O_RDWR creates the file if it does not exist.
+ * Outputs:
+ * hdl -> Opaque handle to the mapped file. Should be passed
+ * Passed to a subsequent agt_mupdate_stats() or
+ * agt_mread_stats() or agt_mclose_stats() call.
+ * Return Values:
+ * Returns 0 on successfully doing the memmap or error codes
+ * as defined in <errno.h>, otherwise.
+ *
+ ****************************************************************************/
+
+int
+agt_mopen_stats (char * statsfile, int mode, int *hdl)
+{
+ caddr_t fp;
+ char *path;
+#ifndef _WIN32
+ int fd;
+ char *buf;
+ int err;
+ size_t sz;
+ struct stat fileinfo;
+#endif /* _WIN32 */
+
+ switch (mode)
+ {
+ case O_RDONLY:
+ if (mmap_tbl [0].maptype != AGT_MAP_UNINIT)
+ {
+ *hdl = 0;
+ return (EEXIST); /* We already mapped it once */
+ }
+ break;
+
+ case O_RDWR:
+ if (mmap_tbl [1].maptype != AGT_MAP_UNINIT)
+ {
+ *hdl = 1;
+ return (EEXIST); /* We already mapped it once */
+ }
+ break;
+
+ default:
+ return (EINVAL); /* Invalid (mode) parameter */
+
+ } /* end switch */
+
+
+ if (statsfile != NULL)
+ path = statsfile;
+ else
+ path = AGT_STATS_FILE;
+
+
+#ifndef _WIN32
+ switch (mode)
+ {
+ case O_RDONLY:
+ if ( (fd = open (path, O_RDONLY)) < 0 )
+ {
+ err = errno;
+#if (0)
+ fprintf (stderr, "returning errno =%d from %s(line: %d)\n", err, __FILE__, __LINE__);
+#endif
+ return (err);
+ }
+
+ fp = mmap (NULL, sizeof (struct agt_stats_t), PROT_READ, MAP_PRIVATE, fd, 0);
+
+ if (fp == (caddr_t) -1)
+ {
+ err = errno;
+ close (fd);
+#if (0)
+ fprintf (stderr, "returning errno =%d from %s(line: %d)\n", err, __FILE__, __LINE__);
+#endif
+ return (err);
+ }
+
+ mmap_tbl [0].maptype = AGT_MAP_READ;
+ mmap_tbl [0].fd = fd;
+ mmap_tbl [0].fp = fp;
+ *hdl = 0;
+#if (0)
+ fprintf (stderr, "%s@%d> opened fp = %d\n", __FILE__, __LINE__, fp);
+#endif
+ return (0);
+
+ case O_RDWR:
+ fd = open (path,
+ O_RDWR | O_CREAT,
+ S_IWUSR | S_IRUSR | S_IRGRP | S_IROTH);
+
+ if ( fd < 0 )
+ {
+ err = errno;
+#if (0)
+ fprintf (stderr, "returning errno =%d from %s(line: %d)\n", err, __FILE__, __LINE__);
+#endif
+ return (err);
+ }
+
+ fstat (fd, &fileinfo);
+
+ sz = sizeof (struct agt_stats_t);
+
+ if (fileinfo.st_size < sz)
+ {
+ /* Without this we will get segv when we try to read/write later */
+ buf = calloc (1, sz);
+ write (fd, buf, sz);
+ free (buf);
+ }
+
+ fp = mmap (NULL, sz, (PROT_READ | PROT_WRITE), MAP_SHARED, fd, 0);
+
+ if (fp == (caddr_t) -1)
+ {
+ err = errno;
+ close (fd);
+#if (0)
+ fprintf (stderr, "returning errno =%d from %s(line: %d)\n", err, __FILE__, __LINE__);
+#endif
+ return (err);
+ }
+
+ mmap_tbl [1].maptype = AGT_MAP_RDWR;
+ mmap_tbl [1].fd = fd;
+ mmap_tbl [1].fp = fp;
+ *hdl = 1;
+ return (0);
+
+ } /* end switch */
+#else
+
+ switch (mode) {
+ case O_RDONLY:
+ {
+ HANDLE hFile = NULL;
+ HANDLE hMapFile = NULL;
+
+ /* Open existing disk file for read */
+ hFile = CreateFile(path,
+ GENERIC_READ | GENERIC_WRITE,
+ FILE_SHARE_READ | FILE_SHARE_WRITE,
+ NULL,
+ OPEN_EXISTING,
+ FILE_ATTRIBUTE_NORMAL,
+ NULL);
+ if ( hFile == INVALID_HANDLE_VALUE || hFile == NULL ) return GetLastError();
+
+ /* Create mapped file handle for reading */
+ hMapFile = CreateFileMapping( hFile, NULL, PAGE_READONLY, 0,
+ sizeof(struct agt_stats_t),
+ NULL);
+ if ( hMapFile == NULL ) {
+ CloseHandle( hFile );
+ return GetLastError();
+ }
+
+ /* Create addr ptr to the start of the file */
+ fp = (caddr_t) MapViewOfFileEx( hMapFile, FILE_MAP_READ, 0, 0,
+ sizeof(struct agt_stats_t), NULL );
+ if ( fp == NULL ) {
+ CloseHandle( hMapFile );
+ CloseHandle( hFile );
+ return GetLastError();
+ }
+
+ /* Fill in info on this opaque handle */
+ mmap_tbl[0].maptype = AGT_MAP_READ;
+ mmap_tbl[0].fd = hFile;
+ mmap_tbl[0].fp = fp;
+ mmap_tbl[0].mfh = hMapFile;
+ *hdl = 0;
+ return 0;
+ }
+
+ case O_RDWR:
+ {
+
+ HANDLE hFile = NULL;
+ HANDLE hMapFile = NULL;
+
+ hFile = CreateFile( path,
+ GENERIC_WRITE | GENERIC_READ,
+ FILE_SHARE_READ | FILE_SHARE_WRITE,
+ NULL,
+ OPEN_ALWAYS,
+ FILE_ATTRIBUTE_NORMAL,
+ NULL );
+ if ( hFile == INVALID_HANDLE_VALUE || hFile == NULL ) return GetLastError();
+
+ /* Create mapped file handle for reading */
+ hMapFile = CreateFileMapping( hFile, NULL, PAGE_READWRITE, 0,
+ sizeof(struct agt_stats_t),
+ NULL );
+ if ( hMapFile == NULL ) {
+ CloseHandle( hFile );
+ return GetLastError();
+ }
+
+ /* Create addr ptr to the start of the file */
+ fp = (caddr_t) MapViewOfFileEx( hMapFile, FILE_MAP_ALL_ACCESS, 0, 0,
+ sizeof(struct agt_stats_t), NULL );
+ if ( fp == NULL ) {
+ CloseHandle( hMapFile );
+ CloseHandle( hFile );
+ return GetLastError();
+ }
+
+ mmap_tbl[1].maptype = AGT_MAP_RDWR;
+ mmap_tbl[1].fd = hFile;
+ mmap_tbl[1].fp = fp;
+ mmap_tbl[1].mfh = hMapFile;
+ *hdl = 1;
+ return 0;
+
+ }
+
+
+ }
+
+#endif /* !__WINNT__ */
+
+return 0;
+
+} /* agt_mopen_stats () */
+
+
+/****************************************************************************
+ *
+ * agt_mclose_stats () - Close the Memory Map'ed the stats file.
+ *
+ *
+ * Inputs:
+ * hdl -> Opaque handle to the mapped file. Should be have been
+ * returned by an earlier call to agt_mopen_stats().
+ *
+ * Outputs: <NONE>
+ *
+ * Return Values:
+ * Returns 0 on normal completion or error codes
+ * as defined in <errno.h>, otherwise.
+ *
+ ****************************************************************************/
+int
+agt_mclose_stats (int hdl)
+{
+ if ( (hdl > 1) || (hdl < 0) )
+ {
+ return (EINVAL); /* Inavlid handle */
+ }
+
+ if (mmap_tbl [hdl].maptype == AGT_MAP_UNINIT)
+ return (0);
+
+ if (mmap_tbl [hdl].fp > (caddr_t) 0)
+ {
+#ifndef _WIN32
+ munmap (mmap_tbl [hdl].fp, sizeof (struct agt_stats_t));
+ mmap_tbl [hdl].fp = (caddr_t) -1;
+ close (mmap_tbl [hdl].fd);
+ mmap_tbl [hdl].fd = -1;
+#else
+ BOOL bUnmapped;
+
+ bUnmapped = UnmapViewOfFile( mmap_tbl[hdl].fp );
+ if ( mmap_tbl[hdl].mfh ) CloseHandle( mmap_tbl[hdl].mfh );
+ if ( mmap_tbl[hdl].fd ) CloseHandle( mmap_tbl[hdl].fd );
+
+ mmap_tbl[hdl].fp = (caddr_t) -1;
+ mmap_tbl[hdl].mfh = NULL;
+ mmap_tbl[hdl].fd = NULL;
+#endif /* ! _WIN32 */
+ mmap_tbl [hdl].maptype = AGT_MAP_UNINIT;
+ return (0);
+ }
+
+ return EINVAL;
+} /* agt_mclose_stats () */
diff --git a/ldap/servers/slapd/agtmmap.h b/ldap/servers/slapd/agtmmap.h
new file mode 100644
index 00000000..7f7cbfc1
--- /dev/null
+++ b/ldap/servers/slapd/agtmmap.h
@@ -0,0 +1,191 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+
+/********************************************************************
+ *
+ * agtmmap.h: Memory Map interface for SNMP sub-agent for
+ * Netscape Directory Server stats (for UNIX environment).
+ *
+ * Revision History:
+ * 07/22/97 Created Steve Ross
+ *
+ *
+ *
+ **********************************************************************/
+
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <errno.h>
+#ifdef _WIN32
+#include <windows.h>
+#define caddr_t PCHAR
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
+#define NUM_SNMP_INT_TBL_ROWS 5
+#ifndef _WIN32
+extern int errno;
+#endif
+
+#if !defined(_MAX_PATH)
+#define _MAX_PATH 256
+#endif
+#define AGT_STATS_FILE "slapd.stats"
+#define AGT_MJR_VERSION 1
+#define AGT_MNR_VERSION 0
+
+
+typedef enum { AGT_MAP_UNINIT = 0, AGT_MAP_READ, AGT_MAP_RDWR } agt_mmap_type;
+
+#ifndef _WIN32
+typedef struct
+{
+ agt_mmap_type maptype;
+ int fd;
+ caddr_t fp;
+} agt_mmap_context_t;
+#else
+typedef struct
+{
+ agt_mmap_type maptype;
+ HANDLE fd;
+ caddr_t fp;
+ HANDLE mfh;
+} agt_mmap_context_t;
+#endif /* ! _WIN32 */
+
+struct hdr_stats_t{
+ /*
+ * Header
+ */
+ int hdrVersionMjr;
+ int hdrVersionMnr;
+ int restarted; /* 1/0 = Yes/No */
+ time_t startTime;
+ time_t updateTime;
+
+};
+
+struct ops_stats_t{
+ /*
+ * Ops Table attributes
+ */
+ int dsAnonymousBinds;
+ int dsUnAuthBinds;
+ int dsSimpleAuthBinds;
+ int dsStrongAuthBinds;
+ int dsBindSecurityErrors;
+ int dsInOps;
+ int dsReadOps;
+ int dsCompareOps;
+ int dsAddEntryOps;
+ int dsRemoveEntryOps;
+ int dsModifyEntryOps;
+ int dsModifyRDNOps;
+ int dsListOps;
+ int dsSearchOps;
+ int dsOneLevelSearchOps;
+ int dsWholeSubtreeSearchOps;
+ int dsReferrals;
+ int dsChainings;
+ int dsSecurityErrors;
+ int dsErrors;
+ int dsConnections; /* Number of currently connected clients */
+ int dsConnectionSeq; /* Monotonically increasing number bumped on each new conn est */
+ int dsBytesRecv; /* Count of bytes read from clients */
+ int dsBytesSent; /* Count of bytes sent to clients */
+ int dsEntriesReturned; /* Number of entries returned by the server */
+ int dsReferralsReturned; /* Number of entries returned by the server */
+};
+
+struct entries_stats_t
+{
+ /*
+ * Entries Table Attributes
+ */
+
+ int dsMasterEntries;
+ int dsCopyEntries;
+ int dsCacheEntries;
+ int dsCacheHits;
+ int dsSlaveHits;
+
+};
+struct int_stats_t
+{
+ /*
+ * Interaction Table Attributes
+ */
+
+ int dsIntIndex;
+ char dsName[100];
+ time_t dsTimeOfCreation;
+ time_t dsTimeOfLastAttempt;
+ time_t dsTimeOfLastSuccess;
+ int dsFailuresSinceLastSuccess;
+ int dsFailures;
+ int dsSuccesses;
+ char dsURL[100];
+
+};
+struct agt_stats_t
+{
+ struct hdr_stats_t hdr_stats;
+ struct ops_stats_t ops_stats;
+ struct entries_stats_t entries_stats;
+ struct int_stats_t int_stats[NUM_SNMP_INT_TBL_ROWS];
+
+} ;
+
+extern agt_mmap_context_t mmap_tbl[];
+
+/****************************************************************************
+ *
+ * agt_mopen_stats () - open and Memory Map the stats file. agt_mclose_stats()
+ * must be called prior to invoking agt_mopen_stats() again.
+ * Inputs:
+ * statsfile -> Name of stats file including full path or NULL.
+ * If NULL, default ($NETSITE_ROOT/daemonstats.ldap) is assumed.
+ * mode -> Must be one of O_RDONLY / O_RDWR.
+ * O_RDWR creates the file if it does not exist.
+ * Outputs:
+ * hdl -> Opaque handle to the mapped file. Should be
+ * passed to a subsequent agt_mupdate_stats() or
+ * agt_mread_stats() or agt_mclose_stats() call.
+ * Return Values:
+ * Returns 0 on successfully doing the memmap or error
+ * codes as defined in <errno.h>, otherwise.
+ *
+ ****************************************************************************/
+
+int agt_mopen_stats (char * statsfile, int mode, int *hdl);
+
+/****************************************************************************
+ *
+ * agt_mclose_stats () - Close the Memory Map'ed stats file.
+ *
+ *
+ * Inputs:
+ * hdl -> Opaque handle to the mapped file. Should have been
+ * returned by an earlier call to agt_mopen_stats().
+ *
+ * Outputs: <NONE>
+ *
+ * Return Values: Returns 0 on normal completion or error codes
+ * as defined in <errno.h>, otherwise.
+ *
+ ****************************************************************************/
+int agt_mclose_stats (int hdl);
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/ldap/servers/slapd/apibroker.c b/ldap/servers/slapd/apibroker.c
new file mode 100644
index 00000000..28068401
--- /dev/null
+++ b/ldap/servers/slapd/apibroker.c
@@ -0,0 +1,245 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+/* ABAPI Broker */
+/* Pete Rowley */
+
+#include "stdio.h"
+#include "slap.h"
+#include "prlock.h"
+#include "prerror.h"
+#include "prcvar.h"
+#include "prio.h"
+
+static Slapi_Mutex *buffer_lock = 0;
+
+/* circular api buffer */
+
+typedef struct _THEABAPI
+{
+ char *guid;
+ void **api;
+ struct _THEABAPI *next;
+ struct _THEABAPI *prev;
+} ABAPI;
+
+typedef struct _API_FEATURES
+{
+ int refcount;
+ slapi_apib_callback_on_zero callback_on_zero;
+ Slapi_Mutex *lock;
+} APIB_FEATURES;
+
+static ABAPI *head = NULL;
+
+static ABAPI **ABAPIBroker_FindInterface(char *guid);
+
+int slapi_apib_register(char *guid, void **api )
+{
+ int ret = -1;
+ ABAPI *item;
+
+ if(buffer_lock == 0)
+ {
+ if(0 == (buffer_lock = slapi_new_mutex())) /* we never free this mutex */
+ /* badness */
+ return -1;
+ }
+
+ /* simple - we don't check for duplicates */
+
+ item = (ABAPI*)slapi_ch_malloc(sizeof(ABAPI));
+ if(item)
+ {
+ item->guid = guid;
+ item->api = api;
+
+ slapi_lock_mutex(buffer_lock);
+ if(head == NULL)
+ {
+ head = item;
+ head->next = head;
+ head->prev = head;
+ }
+ else
+ {
+ item->next = head;
+ item->prev = head->prev;
+ head->prev = item;
+ item->prev->next = item;
+ }
+ slapi_unlock_mutex(buffer_lock);
+
+ ret = 0;
+ }
+
+ return ret;
+}
+
+int slapi_apib_unregister(char *guid)
+{
+ int ret = -1;
+ ABAPI **api;
+
+ if(buffer_lock == 0)
+ return ret;
+
+ if(buffer_lock == 0)
+ {
+ if(0 == (buffer_lock = slapi_new_mutex())) /* we never free this mutex */
+ /* badness */
+ return -1;
+ }
+
+ slapi_lock_mutex(buffer_lock);
+
+ if((api = ABAPIBroker_FindInterface(guid)) != NULL)
+ {
+ (*api)->prev->next = (*api)->next;
+ (*api)->next->prev = (*api)->prev;
+
+ if(*api == head)
+ {
+ head = (*api)->next;
+ }
+
+ if(*api == head) /* must be the last item, turn off the lights */
+ head = 0;
+
+ slapi_ch_free((void**)api);
+ *api = 0;
+ ret = 0;
+ }
+
+ slapi_unlock_mutex(buffer_lock);
+
+ return ret;
+}
+
+int slapi_apib_get_interface(char *guid, void ***api)
+{
+ int ret = -1;
+ ABAPI **theapi;
+
+ if(buffer_lock == 0)
+ return ret;
+
+ if(buffer_lock == 0)
+ {
+ if(0 == (buffer_lock = slapi_new_mutex())) /* we never free this mutex */
+ /* badness */
+ return -1;
+ }
+
+ slapi_lock_mutex(buffer_lock);
+
+ if((theapi = ABAPIBroker_FindInterface(guid)) != NULL)
+ {
+ *api = (*theapi)->api;
+ if((*api)[0])
+ {
+ slapi_apib_addref(*api);
+ }
+
+ ret = 0;
+ }
+
+ slapi_unlock_mutex(buffer_lock);
+
+ return ret;
+}
+
+int slapi_apib_make_reference_counted(void **api, slapi_apib_callback_on_zero callback_on_zero)
+{
+ int ret = -1;
+
+ if(api[0] == 0)
+ {
+ api[0] = slapi_ch_malloc(sizeof(APIB_FEATURES));
+ if(api[0])
+ {
+ ((APIB_FEATURES*)(api[0]))->lock = slapi_new_mutex();
+ if(((APIB_FEATURES*)(api[0]))->lock)
+ {
+ ((APIB_FEATURES*)(api[0]))->refcount = 0; /* the ref count */
+ ((APIB_FEATURES*)(api[0]))->callback_on_zero = callback_on_zero;
+ ret = 0;
+ }
+ else
+ slapi_ch_free(&(api[0]));
+ }
+ }
+
+ return ret;
+}
+
+int slapi_apib_addref(void **api)
+{
+ int ret;
+
+ slapi_lock_mutex(((APIB_FEATURES*)(api[0]))->lock);
+
+ ret = ++(((APIB_FEATURES*)(api[0]))->refcount);
+
+ slapi_unlock_mutex(((APIB_FEATURES*)(api[0]))->lock);
+
+ return ret;
+}
+
+int slapi_apib_release(void **api)
+{
+ APIB_FEATURES *features;
+ int ret;
+
+ slapi_lock_mutex(((APIB_FEATURES*)(api[0]))->lock);
+
+ ret = --(((APIB_FEATURES*)(api[0]))->refcount);
+
+ if(((APIB_FEATURES*)(api[0]))->refcount == 0 && ((APIB_FEATURES*)(api[0]))->callback_on_zero)
+ {
+ /* save our stuff for when it gets zapped */
+ features = (APIB_FEATURES*)api[0];
+
+ if(0==((APIB_FEATURES*)(api[0]))->callback_on_zero(api)) /* this should deregister the interface */
+ {
+ slapi_unlock_mutex(features->lock);
+ slapi_destroy_mutex(features->lock);
+ slapi_ch_free((void **)&features);
+ }
+ else
+ slapi_unlock_mutex(features->lock);
+ }
+ else
+ slapi_unlock_mutex(((APIB_FEATURES*)(api[0]))->lock);
+
+ return ret;
+}
+
+
+static ABAPI **ABAPIBroker_FindInterface(char *guid)
+{
+ static ABAPI *api = 0; /* simple gut feeling optimization for constant calls on same api */
+ ABAPI *start_api = api;
+
+ if(!api) {
+ start_api = api = head;
+ }
+
+ if(api)
+ {
+ do
+ {
+ if(0 == strcmp(guid, api->guid))
+ {
+ return &api;
+ }
+
+ api = api->next;
+ }
+ while(api != start_api);
+ }
+
+ return 0;
+}
diff --git a/ldap/servers/slapd/attr.c b/ldap/servers/slapd/attr.c
new file mode 100644
index 00000000..78b1b2a0
--- /dev/null
+++ b/ldap/servers/slapd/attr.c
@@ -0,0 +1,849 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+/* attr.c - routines for dealing with attributes */
+
+#include <stdio.h>
+#include <string.h>
+#include <ctype.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#ifndef _WIN32
+#include <sys/time.h>
+#include <sys/param.h>
+#include <fcntl.h>
+#include <sys/socket.h>
+#endif
+#include "slap.h"
+#undef DEBUG /* disable counters */
+#include <prcountr.h>
+
+
+static int counters_created= 0;
+PR_DEFINE_COUNTER(slapi_attr_counter_created);
+PR_DEFINE_COUNTER(slapi_attr_counter_deleted);
+PR_DEFINE_COUNTER(slapi_attr_counter_exist);
+
+/*
+ * structure used within AVL value trees.
+ */
+typedef struct slapi_attr_value_index {
+ int savi_index; /* index into a_vals[] */
+ struct berval *savi_normval; /* normalized value */
+} SlapiAttrValueIndex;
+
+/*
+ * Utility function used by slapi_attr_type_cmp to
+ * find the next component of an attribute type.
+ */
+static const char *
+next_comp( const char *s )
+{
+ while ( *s && *s != ';' ) {
+ s++;
+ }
+ if ( *s == '\0' ) {
+ return( NULL );
+ } else {
+ return( s + 1 );
+ }
+}
+
+/*
+ * Utility function used by slapi_attr_type_cmp to
+ * compare two components of an attribute type.
+ */
+static int
+comp_cmp( const char *s1, const char *s2 )
+{
+ while ( *s1 && *s1 != ';' && tolower( *s1 ) == tolower( *s2 ) ) {
+ s1++, s2++;
+ }
+ if ( *s1 != *s2 ) {
+ if ( (*s1 == '\0' || *s1 == ';') &&
+ (*s2 == '\0' || *s2 == ';') ) {
+ return( 0 );
+ } else {
+ return( 1 );
+ }
+ } else {
+ return( 0 );
+ }
+}
+
+int
+slapi_attr_type_cmp( const char *a1, const char *a2, int opt )
+{
+ int rc= 0;
+
+ switch ( opt ) {
+ case SLAPI_TYPE_CMP_EXACT: /* compare base name + options as given */
+ rc = strcmp( a1, a2 );
+ break;
+
+ case SLAPI_TYPE_CMP_BASE: /* ignore options on both names - compare base names only */
+ rc = comp_cmp( a1, a2 );
+ break;
+
+ case SLAPI_TYPE_CMP_SUBTYPE: /* ignore options on second name that are not in first name */
+ /*
+ * first, check that the base types match
+ */
+ if ( comp_cmp( a1, a2 ) != 0 ) {
+ rc = 1;
+ break;
+ }
+ /*
+ * next, for each component in a1, make sure there is a
+ * matching component in a2. the order must be the same,
+ * so we can keep looking where we left off each time in a2
+ */
+ rc = 0;
+ for ( a1 = next_comp( a1 ); a1 != NULL; a1 = next_comp( a1 ) ) {
+ for ( a2 = next_comp( a2 ); a2 != NULL;
+ a2 = next_comp( a2 ) ) {
+ if ( comp_cmp( a1, a2 ) == 0 ) {
+ break;
+ }
+ }
+ if ( a2 == NULL ) {
+ rc = 1;
+ break;
+ }
+ }
+ break;
+ }
+
+ return( rc );
+}
+
+
+
+
+/*
+ * Return 1 if the two types are equivalent -- either the same type name,
+ * or aliases for one another, including OIDs.
+ *
+ * Objective: don't allocate any memory!
+ */
+int
+slapi_attr_types_equivalent(const char *t1, const char *t2)
+{
+ int retval = 0;
+ struct asyntaxinfo *asi1, *asi2;
+
+ if (NULL == t1 || NULL == t2) {
+ return 0;
+ }
+
+ asi1 = attr_syntax_get_by_name(t1);
+ asi2 = attr_syntax_get_by_name(t2);
+ if (NULL != asi1) {
+ if (NULL != asi2) {
+ /* Both found - compare normalized names */
+ if (strcasecmp(asi1->asi_name, asi2->asi_name) == 0) {
+ retval = 1;
+ } else {
+ retval = 0;
+ }
+ } else {
+ /* One found, the other wasn't, so not equivalent */
+ retval = 0;
+ }
+ } else if (NULL != asi2) {
+ /* One found, the other wasn't, so not equivalent */
+ retval = 0;
+ } else {
+ /* Neither found - perform case-insensitive compare */
+ if (strcasecmp(t1, t2) == 0) {
+ retval = 1;
+ } else {
+ retval = 0;
+ }
+ }
+
+ attr_syntax_return( asi1 );
+ attr_syntax_return( asi2 );
+
+ return retval;
+}
+
+Slapi_Attr *
+slapi_attr_new()
+{
+ Slapi_Attr *a= (Slapi_Attr *)slapi_ch_calloc( 1, sizeof(Slapi_Attr));
+ if(!counters_created)
+ {
+ PR_CREATE_COUNTER(slapi_attr_counter_created,"Slapi_Attr","created","");
+ PR_CREATE_COUNTER(slapi_attr_counter_deleted,"Slapi_Attr","deleted","");
+ PR_CREATE_COUNTER(slapi_attr_counter_exist,"Slapi_Attr","exist","");
+ counters_created= 1;
+ }
+ PR_INCREMENT_COUNTER(slapi_attr_counter_created);
+ PR_INCREMENT_COUNTER(slapi_attr_counter_exist);
+ return a;
+}
+
+Slapi_Attr *
+slapi_attr_init(Slapi_Attr *a, const char *type)
+{
+ return slapi_attr_init_locking_optional(a, type, PR_TRUE, PR_TRUE);
+}
+
+Slapi_Attr *
+slapi_attr_init_locking_optional(Slapi_Attr *a, const char *type, PRBool use_lock, PRBool ref_count)
+{
+ PR_ASSERT(a!=NULL);
+
+ if(NULL != a)
+ {
+ struct asyntaxinfo *asi= NULL;
+ const char *basetype= NULL;
+ char *tmp= NULL;
+
+ if(type!=NULL)
+ {
+ char buf[SLAPD_TYPICAL_ATTRIBUTE_NAME_MAX_LENGTH];
+ basetype = buf;
+ tmp = slapi_attr_basetype(type, buf, sizeof(buf));
+ if(tmp != NULL)
+ {
+ basetype = tmp; /* basetype was malloc'd */
+ }
+ asi = attr_syntax_get_by_name_locking_optional(basetype, use_lock, ref_count);
+ }
+ if(NULL == asi)
+ {
+ a->a_type = attr_syntax_normalize_no_lookup( type );
+ /*
+ * no syntax for this type... return DirectoryString
+ * syntax. we accomplish this by looking up a well known
+ * attribute type that has that syntax.
+ */
+ asi = attr_syntax_get_by_name_locking_optional(
+ ATTR_WITH_DIRSTRING_SYNTAX, use_lock, ref_count);
+ }
+ else
+ {
+ char *attroptions = NULL;
+
+ if ( NULL != type ) {
+ attroptions = strchr( type, ';' );
+ }
+
+ if ( NULL == attroptions ) {
+ a->a_type = slapi_ch_strdup(asi->asi_name);
+ } else {
+ /*
+ * If the original type includes any attribute options,
+ * the a_type field is set to the type contained in the
+ * attribute syntax followed by a normalized copy of the
+ * options.
+ */
+ char *normalized_options;
+
+ normalized_options = attr_syntax_normalize_no_lookup( attroptions );
+ a->a_type = slapi_ch_malloc( strlen(asi->asi_name)
+ + strlen(normalized_options) +1 );
+ sprintf( a->a_type, "%s%s", asi->asi_name, normalized_options );
+ slapi_ch_free_string( &normalized_options );
+ }
+ }
+ if ( asi != NULL )
+ {
+ a->a_plugin = asi->asi_plugin;
+ a->a_flags = asi->asi_flags;
+ }
+ else
+ {
+ a->a_plugin = NULL; /* XXX - should be rare */
+ a->a_flags = 0; /* XXX - should be rare */
+ }
+
+ attr_syntax_return_locking_optional( asi, use_lock );
+
+ if (NULL != tmp)
+ {
+ slapi_ch_free_string(&tmp);
+ }
+ slapi_valueset_init(&a->a_present_values);
+ slapi_valueset_init(&a->a_deleted_values);
+ a->a_listtofree= NULL;
+ a->a_deletioncsn= NULL;
+ a->a_next= NULL;
+ }
+
+ return a;
+}
+
+Slapi_Attr *
+slapi_attr_dup(const Slapi_Attr *attr)
+{
+ Slapi_Attr *newattr= slapi_attr_new();
+ Slapi_Value **present_va= valueset_get_valuearray(&attr->a_present_values); /* JCM Mucking about inside the value set */
+ Slapi_Value **deleted_va= valueset_get_valuearray(&attr->a_deleted_values); /* JCM Mucking about inside the value set */
+ slapi_attr_init(newattr, attr->a_type);
+ valueset_add_valuearray( &newattr->a_present_values, present_va );
+ valueset_add_valuearray( &newattr->a_deleted_values, deleted_va );
+ newattr->a_deletioncsn= csn_dup(attr->a_deletioncsn);
+ return newattr;
+}
+
+void
+slapi_attr_free( Slapi_Attr **ppa )
+{
+ if(ppa!=NULL && *ppa!=NULL)
+ {
+ Slapi_Attr *a= *ppa;
+ attr_done(a);
+ slapi_ch_free( (void**)&a );
+ PR_INCREMENT_COUNTER(slapi_attr_counter_deleted);
+ PR_DECREMENT_COUNTER(slapi_attr_counter_exist);
+ }
+}
+
+void
+attr_done(Slapi_Attr *a)
+{
+ if(a!=NULL)
+ {
+ slapi_ch_free((void**)&a->a_type);
+ csn_free(&a->a_deletioncsn);
+ slapi_valueset_done(&a->a_present_values);
+ slapi_valueset_done(&a->a_deleted_values);
+ {
+ struct bervals2free *freelist;
+ struct bervals2free *tmp;
+ freelist = a->a_listtofree;
+ while(freelist) {
+ ber_bvecfree(freelist->bvals);
+ tmp=freelist;
+ freelist = freelist->next;
+ slapi_ch_free((void **)&tmp);
+ }
+ }
+ }
+}
+
+/*
+ * slapi_attr_basetype - extracts the attribute base type (without
+ * any attribute description options) from type. puts the result
+ * in buf if it can, otherwise, it malloc's and returns the base
+ * which should be free()'ed later.
+ */
+char *
+slapi_attr_basetype( const char *type, char *buf, size_t bufsize )
+{
+ unsigned int i;
+
+ i = 0;
+ while ( *type && *type != ';' && i < bufsize ) {
+ buf[i++] = *type++;
+ }
+ if ( i < bufsize ) {
+ buf[i] = '\0';
+ return( NULL );
+ } else {
+ int len;
+ char *tmp;
+
+ len = strlen( type );
+ tmp = slapi_ch_malloc( len + 1 );
+ slapi_attr_basetype( type, tmp, len + 1 );
+ return( tmp );
+ }
+}
+
+/*
+ * returns 0 if "v" is already a value within "a" and non-zero if not.
+ */
+int
+slapi_attr_value_find( const Slapi_Attr *a, const struct berval *v )
+{
+ struct ava ava;
+
+ if ( NULL == a ) {
+ return( -1 );
+ }
+
+ ava.ava_type = a->a_type;
+ ava.ava_value = *v;
+ return(plugin_call_syntax_filter_ava( a, LDAP_FILTER_EQUALITY, &ava ));
+}
+
+int
+slapi_attr_get_type( Slapi_Attr *a, char **type )
+{
+ *type = a->a_type;
+ return( 0 );
+}
+
+
+/*
+ * Fetch a copy of the values as an array of struct berval *'s.
+ * Returns 0 upon success and non-zero on failure.
+ * Free the array of values by calling ber_bvecfree().
+ */
+int
+slapi_attr_get_bervals_copy( Slapi_Attr *a, struct berval ***vals )
+{
+ int retVal= 0;
+
+ if ( NULL == vals )
+ {
+ return -1;
+ }
+
+ if(NULL==a || valueset_isempty(&a->a_present_values))
+ {
+ *vals = NULL;
+ }
+ else
+ {
+ Slapi_Value **va= valueset_get_valuearray(&a->a_present_values);
+ valuearray_get_bervalarray(va,vals);
+ }
+ return retVal;
+}
+
+
+/*
+ * JCM: BEWARE.. HIGHLY EVIL.. DO NOT USE THIS FUNCTION.
+ * XXXmcs: Why not? Because it is only provided as a not-quite-backwards
+ * compatible interface for older (pre-iDS 5.0) plugins. It works by
+ * making a copy of the attribute values (the pre-iDS 5.0 function with
+ * the same name returned a pointer into the Slapi_Attr structure -- no
+ * copying). Since older users of this interface did not have to free
+ * the values, this function arranges to free them WHEN THE Slapi_Attr
+ * IS DESTROYED. This is accomplished by adding a pointer to the newly
+ * allocated copy to a "to be freed" linked list inside the Slapi_Attr.
+ * But if the Slapi_Attr is not destroyed very often, and this function
+ * is called repeatedly, memory usage will grow without bound. Not good.
+ * The value copies are freed inside attr_done() which is called from
+ * slapi_attr_free() and a few other places.
+ *
+ * If you really want a copy of the values as a struct berval ** array,
+ * call slapi_attr_get_bervals_copy() and free it yourself by calling
+ * ber_bvecfree().
+ */
+int
+slapi_attr_get_values( Slapi_Attr *a, struct berval ***vals )
+{
+ int retVal = slapi_attr_get_bervals_copy( a, vals );
+
+ if ( 0 == retVal )
+ {
+ struct bervals2free *newfree;
+ newfree = (struct bervals2free *)slapi_ch_malloc(sizeof(struct bervals2free));
+ newfree->next = a->a_listtofree;
+ newfree->bvals = *vals;
+ a->a_listtofree = newfree;
+ }
+
+ return retVal;
+}
+
+
+/*
+ * Fetch a copy of the present valueset.
+ * Caller must free the valueset.
+ */
+int
+slapi_attr_get_valueset(const Slapi_Attr *a, Slapi_ValueSet **vs)
+{
+ int retVal= 0;
+ if(vs!=NULL)
+ {
+ *vs= valueset_dup(&a->a_present_values);
+ }
+ return retVal;
+}
+
+/*
+ * Careful... this returns a pointer to the contents!
+ */
+Slapi_Value **
+attr_get_present_values(const Slapi_Attr *a)
+{
+ return valueset_get_valuearray(&a->a_present_values);
+}
+
+int
+slapi_attr_get_flags( const Slapi_Attr *a, unsigned long *flags )
+{
+ *flags = a->a_flags;
+ return( 0 );
+}
+
+int
+slapi_attr_flag_is_set( const Slapi_Attr *a, unsigned long flag )
+{
+ return( a->a_flags & flag );
+}
+
+int
+slapi_attr_value_cmp( const Slapi_Attr *a, const struct berval *v1, const struct berval *v2 )
+{
+ int retVal;
+
+ if ( a->a_flags & SLAPI_ATTR_FLAG_CMP_BITBYBIT )
+ {
+ int cmplen = ( v1->bv_len <= v2->bv_len ? v1->bv_len : v2->bv_len );
+ retVal = memcmp(v1->bv_val, v2->bv_val, cmplen);
+ if ( retVal == 0 && v1->bv_len < v2->bv_len )
+ {
+ retVal = -1;
+ }
+ else if ( retVal == 0 && v1->bv_len > v2->bv_len )
+ {
+ retVal = 1;
+ }
+ }
+ else
+ {
+ Slapi_Attr a2;
+ struct ava ava;
+ Slapi_Value *cvals[2];
+ Slapi_Value tmpcval;
+
+ a2 = *a;
+ cvals[0] = &tmpcval;
+ cvals[0]->v_csnset = NULL;
+ cvals[0]->bv = *v1;
+ cvals[1] = NULL;
+ a2.a_present_values.va = cvals; /* JCM - PUKE */
+ ava.ava_type = a->a_type;
+ ava.ava_value = *v2;
+ retVal = plugin_call_syntax_filter_ava(&a2, LDAP_FILTER_EQUALITY, &ava);
+ }
+ return retVal;
+}
+
+/*
+ * Set the CSN of all the present values.
+ */
+int
+attr_set_csn( Slapi_Attr *a, const CSN *csn)
+{
+ PR_ASSERT(a!=NULL);
+ valueset_update_csn(&a->a_present_values, CSN_TYPE_VALUE_UPDATED, csn);
+ return 0;
+}
+
+int
+attr_set_deletion_csn( Slapi_Attr *a, const CSN *csn)
+{
+ PR_ASSERT(a!=NULL);
+ if(csn_compare(csn,a->a_deletioncsn)>0)
+ {
+ csn_free(&a->a_deletioncsn);
+ a->a_deletioncsn= csn_dup(csn);
+ }
+ return 0;
+}
+
+const CSN *
+attr_get_deletion_csn(const Slapi_Attr *a)
+{
+ PR_ASSERT(a!=NULL);
+ return a->a_deletioncsn;
+}
+
+int
+slapi_attr_first_value( Slapi_Attr *a, Slapi_Value **v )
+{
+ int rc;
+
+ if(NULL == a) {
+ rc = -1;
+ } else {
+ rc=slapi_valueset_first_value( &a->a_present_values, v );
+ }
+ return rc;
+}
+
+int
+slapi_attr_next_value( Slapi_Attr *a, int hint, Slapi_Value **v)
+{
+ int rc;
+
+ if(NULL == a) {
+ rc = -1;
+ } else {
+ rc=slapi_valueset_next_value( &a->a_present_values, hint, v );
+ }
+ return rc;
+}
+
+int
+slapi_attr_get_numvalues( const Slapi_Attr *a, int *numValues )
+{
+ if(NULL == a) {
+ *numValues = 0;
+ } else {
+ *numValues = slapi_valueset_count(&a->a_present_values);
+ }
+ return 0;
+}
+
+
+int
+attr_first_deleted_value( Slapi_Attr *a, Slapi_Value **v )
+{
+ return slapi_valueset_first_value( &a->a_deleted_values, v );
+}
+
+int
+attr_next_deleted_value( Slapi_Attr *a, int hint, Slapi_Value **v)
+{
+ return slapi_valueset_next_value( &a->a_deleted_values, hint, v );
+}
+
+/*
+ * Note: We are passing in the entry so that we may be able to "optimize"
+ * the csn related information and roll it up higher to the level of entry.
+ */
+void
+attr_purge_state_information(Slapi_Entry *entry, Slapi_Attr *attr, const CSN *csnUpTo)
+{
+ if(!valueset_isempty(&attr->a_deleted_values))
+ {
+ valueset_purge(&attr->a_deleted_values, csnUpTo);
+ }
+}
+
+/*
+ * Search an attribute for a value, it could be present, deleted, or not present.
+ */
+int
+attr_value_find_wsi(Slapi_Attr *a, const struct berval *bval, Slapi_Value **value)
+{
+ int retVal=0;
+ struct ava ava;
+
+ PR_ASSERT(a!=NULL);
+ PR_ASSERT(value!=NULL);
+
+ /*
+ * we will first search the present values, and then, if
+ * necessary, the deleted values.
+ */
+ ava.ava_type = a->a_type;
+ ava.ava_value = *bval;
+ retVal = plugin_call_syntax_filter_ava_sv(a, LDAP_FILTER_EQUALITY, &ava, value, 0 /* Present */);
+
+ if(retVal==0)
+ {
+ /* we found the value, so we don't search the deleted list */
+ retVal= VALUE_PRESENT;
+ }
+ else
+ {
+ retVal = plugin_call_syntax_filter_ava_sv(a, LDAP_FILTER_EQUALITY, &ava, value, 1 /* Deleted */);
+ if(retVal==0)
+ {
+ /* It was on the deleted value list */
+ retVal= VALUE_DELETED;
+ }
+ else
+ {
+ /* Couldn't find it */
+ retVal= VALUE_NOTFOUND;
+ }
+ }
+
+ return retVal;
+}
+
+int
+slapi_attr_add_value(Slapi_Attr *a, const Slapi_Value *v)
+{
+ slapi_valueset_add_value( &a->a_present_values, v);
+ return 0;
+}
+
+/* Make the valuset in SLapi_Attr be *vs--not a copy */
+int
+slapi_attr_set_valueset(Slapi_Attr *a, const Slapi_ValueSet *vs)
+{
+ slapi_valueset_set_valueset( &a->a_present_values, vs);
+ return 0;
+}
+
+int
+attr_add_deleted_value(Slapi_Attr *a, const Slapi_Value *v)
+{
+ slapi_valueset_add_value( &a->a_deleted_values, v);
+ return 0;
+}
+
+/*
+ * If we are adding or deleting SLAPD_MODUTIL_TREE_THRESHHOLD or more
+ * entries, we use an AVL tree to speed up searching for duplicates or
+ * values we are trying to delete. This threshhold is somewhat arbitrary;
+ * we should really take some measurements to determine an optimal number.
+ */
+#define SLAPD_MODUTIL_TREE_THRESHHOLD 5
+
+/*
+ * Add a value array to an attribute. If SLAPD_MODUTIL_TREE_THRESHHOLD or
+ * more values are being added, we build an AVL tree of any existing
+ * values and then update that in parallel with the existing values. This
+ * is done so that we do not waste a lot of CPU time searching for duplicate
+ * values. The AVL tree is created and destroyed all within this function.
+ *
+ * Returns
+ * LDAP_SUCCESS - OK
+ * LDAP_OPERATIONS_ERROR - Existing duplicates in attribute.
+ * LDAP_TYPE_OR_VALUE_EXISTS - Duplicate values.
+ */
+int
+attr_add_valuearray(Slapi_Attr *a, Slapi_Value **vals, const char *dn)
+{
+ int i = 0;
+ int duplicate_index = -1;
+ int was_present_null = 0;
+ int rc = LDAP_SUCCESS;
+
+ if (valuearray_isempty(vals)) {
+ /*
+ * No values to add (unexpected but acceptable).
+ */
+ return rc;
+ }
+
+ /*
+ * determine whether we should use an AVL tree of values or not
+ */
+ while ( i < SLAPD_MODUTIL_TREE_THRESHHOLD - 1 && vals[i] != NULL ) {
+ i++;
+ }
+
+ /*
+ * detect duplicate values
+ */
+ if ( i >= SLAPD_MODUTIL_TREE_THRESHHOLD - 1 ) {
+ /*
+ * Several values to add: use an AVL tree to detect duplicates.
+ */
+ LDAPDebug( LDAP_DEBUG_TRACE,
+ "slapi_entry_add_values: using an AVL tree to "
+ "detect duplicate values\n", 0, 0, 0 );
+
+ if (valueset_isempty(&a->a_present_values)) {
+ /* if the attribute contains no values yet, just check the
+ * input vals array for duplicates
+ */
+ Avlnode *vtree = NULL;
+ rc= valuetree_add_valuearray(a->a_type, a->a_plugin, vals, &vtree, &duplicate_index);
+ valuetree_free(&vtree);
+ was_present_null = 1;
+ } else {
+ /* the attr and vals both contain values, check intersection */
+ rc= valueset_intersectswith_valuearray(&a->a_present_values, a, vals, &duplicate_index);
+ }
+
+ } else if ( !valueset_isempty(&a->a_present_values) ) {
+ /*
+ * Small number of values to add: don't bother constructing
+ * an AVL tree, etc. since it probably isn't worth the time.
+ */
+ for ( i = 0; vals[i] != NULL; ++i ) {
+ if ( slapi_attr_value_find( a, slapi_value_get_berval(vals[i]) ) == 0 ) {
+ duplicate_index = i;
+ rc = LDAP_TYPE_OR_VALUE_EXISTS;
+ break;
+ }
+ }
+ }
+
+ /*
+ * add values if no duplicates detected
+ */
+ if(rc==LDAP_SUCCESS) {
+ valueset_add_valuearray( &a->a_present_values, vals );
+ }
+
+ /* In the case of duplicate value, rc == LDAP_TYPE_OR_VALUE_EXISTS or
+ * LDAP_OPERATIONS_ERROR
+ */
+ else if ( duplicate_index >= 0 ) {
+ char avdbuf[BUFSIZ];
+ char bvvalcopy[BUFSIZ];
+ char *duplicate_string = "null or non-ASCII";
+
+ i = 0;
+ while ( (unsigned int)i < vals[duplicate_index]->bv.bv_len &&
+ i < BUFSIZ - 1 &&
+ vals[duplicate_index]->bv.bv_val[i] &&
+ isascii ( vals[duplicate_index]->bv.bv_val[i] )) {
+ i++;
+ }
+
+ if ( i ) {
+ if ( vals[duplicate_index]->bv.bv_val[i] == 0 ) {
+ duplicate_string = vals[duplicate_index]->bv.bv_val;
+ }
+ else {
+ strncpy ( &bvvalcopy[0], vals[duplicate_index]->bv.bv_val, i );
+ bvvalcopy[i] = '\0';
+ duplicate_string = bvvalcopy;
+ }
+ }
+
+ slapi_log_error( SLAPI_LOG_FATAL, NULL, "add value \"%s\" to "
+ "attribute type \"%s\" in entry \"%s\" failed: %s\n",
+ duplicate_string,
+ a->a_type,
+ dn ? escape_string(dn,avdbuf) : "<null>",
+ (was_present_null ? "duplicate new value" : "value exists"));
+ }
+ return( rc );
+}
+
+/* quickly toss an attribute's values and replace them with new ones
+ * (used by attrlist_replace_fast)
+ */
+void attr_replace(Slapi_Attr *a, Slapi_Value **vals)
+{
+ valueset_replace(&a->a_present_values, vals);
+}
+
+int
+attr_check_onoff ( const char *attr_name, char *value, long minval, long maxval, char *errorbuf )
+{
+ int retVal = LDAP_SUCCESS;
+
+ if ( strcasecmp ( value, "on" ) != 0 &&
+ strcasecmp ( value, "off") != 0 &&
+ strcasecmp ( value, "1" ) != 0 &&
+ strcasecmp ( value, "0" ) != 0 &&
+ strcasecmp ( value, "true" ) != 0 &&
+ strcasecmp ( value, "false" ) != 0 ) {
+ PR_snprintf ( errorbuf, BUFSIZ,
+ "%s: invalid value \"%s\".", attr_name, value );
+ retVal = LDAP_CONSTRAINT_VIOLATION;
+ }
+
+ return retVal;
+}
+
+int
+attr_check_minmax ( const char *attr_name, char *value, long minval, long maxval, char *errorbuf )
+{
+ int retVal = LDAP_SUCCESS;
+ long val;
+
+ val = strtol(value, NULL, 0);
+ if ( (minval != -1 ? (val < minval ? 1 : 0) : 0) ||
+ (maxval != -1 ? (val > maxval ? 1 : 0) : 0) ) {
+ PR_snprintf ( errorbuf, BUFSIZ,
+ "%s: invalid value \"%s\".",
+ attr_name, value );
+ retVal = LDAP_CONSTRAINT_VIOLATION;
+ }
+
+ return retVal;
+}
diff --git a/ldap/servers/slapd/attrlist.c b/ldap/servers/slapd/attrlist.c
new file mode 100644
index 00000000..e57e473c
--- /dev/null
+++ b/ldap/servers/slapd/attrlist.c
@@ -0,0 +1,253 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+
+#include "slap.h"
+
+void
+attrlist_free(Slapi_Attr *alist)
+{
+ Slapi_Attr *a, *next;
+ for ( a = alist; a != NULL; a = next )
+ {
+ next = a->a_next;
+ slapi_attr_free( &a );
+ }
+}
+
+/*
+ * Search for the attribute.
+ * If not found then create it,
+ * and add it to the end of the list.
+ * Return 0 for found, 1 for created.
+ */
+int
+attrlist_find_or_create(Slapi_Attr **alist, const char *type, Slapi_Attr ***a)
+{
+ return attrlist_find_or_create_locking_optional(alist, type, a, PR_TRUE, PR_TRUE);
+}
+
+int
+attrlist_find_or_create_locking_optional(Slapi_Attr **alist, const char *type, Slapi_Attr ***a, PRBool use_lock, PRBool ref_count)
+{
+ int rc= 0; /* found */
+ if ( *a==NULL )
+ {
+ for ( *a = alist; **a != NULL; *a = &(**a)->a_next ) {
+ if ( strcasecmp( (**a)->a_type, type ) == 0 ) {
+ break;
+ }
+ }
+ }
+
+ if( **a==NULL )
+ {
+ **a = slapi_attr_new();
+ slapi_attr_init_locking_optional(**a, type, use_lock, ref_count);
+ rc= 1; /* created */
+ }
+ return rc;
+}
+
+/*
+ * attrlist_merge - merge the given type and value with the list of
+ * attributes in attrs.
+ */
+void
+attrlist_merge(Slapi_Attr **alist, const char *type, struct berval **vals)
+{
+ Slapi_Value **values= NULL;
+ valuearray_init_bervalarray(vals,&values); /* JCM SLOW FUNCTION */
+ attrlist_merge_valuearray(alist,type,values);
+ valuearray_free(&values);
+}
+
+
+/*
+ * attrlist_merge_valuearray - merge the given type and value with the list of
+ * attributes in attrs.
+ */
+void
+attrlist_merge_valuearray(Slapi_Attr **alist, const char *type, Slapi_Value **vals)
+{
+ Slapi_Attr **a= NULL;
+ attrlist_find_or_create(alist, type, &a);
+ valueset_add_valuearray( &(*a)->a_present_values, vals );
+}
+
+
+/*
+ * attrlist_find - find and return attribute type in list a
+ */
+
+Slapi_Attr *
+attrlist_find(Slapi_Attr *a, const char *type)
+{
+ for ( ; a != NULL; a = a->a_next ) {
+ if ( strcasecmp( a->a_type, type ) == 0 ) {
+ return( a );
+ }
+ }
+
+ return( NULL );
+}
+
+
+/*
+ * attrlist_count_subtypes
+ *
+ * Returns a count attributes which conform to type
+ * in the attr list a. This count includes all subtypes of
+ * type
+ */
+int
+attrlist_count_subtypes(Slapi_Attr *a, const char *type)
+{
+ int counter = 0;
+
+ for ( ; a != NULL; a = a->a_next ) {
+ if ( slapi_attr_type_cmp( type , a->a_type, SLAPI_TYPE_CMP_SUBTYPE) == 0 ) {
+ counter++;
+ }
+ }
+
+ return( counter );
+}
+
+/*
+ * attrlist_find_ex
+ *
+ * Finds the first subtype in the list which matches "type"
+ * starting at the beginning or hint depending on whether
+ * hint has a value
+ *
+ * It is intended that hint be zero when first called and then
+ * passed back in on subsequent calls until 0 is returned to mark
+ * the end of the filtered list
+ */
+Slapi_Attr *
+attrlist_find_ex(
+ Slapi_Attr *a,
+ const char *type,
+ int *type_name_disposition, /* pass null if you're not interested */
+ char** actual_type_name, /* pass null if you're not interested */
+ void **hint
+)
+{
+ Slapi_Attr **attr_cursor = (Slapi_Attr **)hint;
+
+ if (type_name_disposition) *type_name_disposition = 0;
+ if (actual_type_name) *actual_type_name = NULL;
+
+ if(*attr_cursor == NULL)
+ *attr_cursor = a; /* start at the beginning of the list */
+ else
+ *attr_cursor = (*attr_cursor)->a_next;
+
+ while(*attr_cursor != NULL) {
+
+ /* Determine whether the two types are related:*/
+ if ( slapi_attr_type_cmp( type , (*attr_cursor)->a_type, SLAPI_TYPE_CMP_SUBTYPE) == 0 ) {
+ /* We got a match. Now figure out if we matched because it was a subtype */
+ if (type_name_disposition) {
+ if ( 0 == slapi_attr_type_cmp( type , (*attr_cursor)->a_type, SLAPI_TYPE_CMP_EXACT) ) {
+ *type_name_disposition = SLAPI_VIRTUALATTRS_TYPE_NAME_MATCHED_EXACTLY_OR_ALIAS;
+ } else {
+ *type_name_disposition = SLAPI_VIRTUALATTRS_TYPE_NAME_MATCHED_SUBTYPE;
+ }
+ }
+
+ if (actual_type_name) {
+ *actual_type_name = (*attr_cursor)->a_type;
+ }
+
+ a = *attr_cursor; /* the attribute to return */
+ return( a );
+
+ }
+
+ *attr_cursor = (*attr_cursor)->a_next; /* no match, move cursor */
+ }
+
+ return( NULL );
+}
+
+/*
+ * attr_remove - remove the attribute from the list of attributes
+ */
+Slapi_Attr *
+attrlist_remove(Slapi_Attr **attrs, const char *type)
+{
+ Slapi_Attr **a;
+ Slapi_Attr *save= NULL;
+ for ( a = attrs; *a != NULL; a = &(*a)->a_next )
+ {
+ if ( strcasecmp( (*a)->a_type, type ) == 0 )
+ {
+ break;
+ }
+ }
+ if (*a != NULL)
+ {
+ save = *a;
+ *a = (*a)->a_next;
+ }
+ return save;
+}
+
+void
+attrlist_add(Slapi_Attr **attrs, Slapi_Attr *a)
+{
+ a->a_next= *attrs;
+ *attrs= a;
+}
+
+/*
+ * attrlist_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
+attrlist_delete(Slapi_Attr **attrs, const char *type)
+{
+ Slapi_Attr **a;
+ Slapi_Attr *save;
+
+ for ( a = attrs; *a != NULL; a = &(*a)->a_next ) {
+ if ( strcasecmp( (*a)->a_type, type ) == 0 ) {
+ break;
+ }
+ }
+
+ if ( *a == NULL ) {
+ return( 1 );
+ }
+
+ save = *a;
+ *a = (*a)->a_next;
+ slapi_attr_free( &save );
+
+ return( 0 );
+}
+
+/*
+ * attrlist_replace - replace the attribute value(s) with this value(s)
+ */
+void attrlist_replace(Slapi_Attr **alist, const char *type, struct berval **vals)
+{
+ Slapi_Attr **a = NULL;
+ Slapi_Value **values = NULL;
+
+ if (vals == NULL || vals[0] == NULL) {
+ (void)attrlist_delete(alist, type);
+ } else {
+ attrlist_find_or_create(alist, type, &a);
+ valuearray_init_bervalarray(vals, &values);
+ attr_replace(*a, values);
+ }
+}
+
diff --git a/ldap/servers/slapd/attrsyntax.c b/ldap/servers/slapd/attrsyntax.c
new file mode 100644
index 00000000..845e9666
--- /dev/null
+++ b/ldap/servers/slapd/attrsyntax.c
@@ -0,0 +1,906 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+/* attrsyntax.c */
+
+#include "slap.h"
+#include <plhash.h>
+
+/*
+ * Note: if both the oid2asi and name2asi locks are acquired at the
+ * same time, the old2asi one should be acquired first,
+ */
+
+/*
+ * This hashtable maps the oid to the struct asyntaxinfo for that oid.
+ */
+static PLHashTable *oid2asi = NULL;
+/* read/write lock to protect table */
+static PRRWLock *oid2asi_lock = NULL;
+
+/*
+ * This hashtable maps the name or alias of the attribute to the
+ * syntax info structure for that attribute. An attribute type has as
+ * many entries in the name2asi table as it has names and aliases, but
+ * all entries point to the same struct asyntaxinfo.
+ */
+static PLHashTable *name2asi = NULL;
+/* read/write lock to protect table */
+static PRRWLock *name2asi_lock = NULL;
+
+#define AS_LOCK_READ(l) PR_RWLock_Rlock(l)
+#define AS_LOCK_WRITE(l) PR_RWLock_Wlock(l)
+#define AS_UNLOCK_READ(l) PR_RWLock_Unlock(l)
+#define AS_UNLOCK_WRITE(l) PR_RWLock_Unlock(l)
+
+
+
+static void *attr_syntax_get_plugin_by_name_with_default( const char *type );
+static void attr_syntax_delete_no_lock( struct asyntaxinfo *asip,
+ PRBool remove_from_oid_table );
+static struct asyntaxinfo *attr_syntax_get_by_oid_locking_optional( const
+ char *oid, PRBool use_lock, PRBool ref_count);
+
+#ifdef ATTR_LDAP_DEBUG
+static void attr_syntax_print();
+#endif
+static int attr_syntax_init(void);
+
+void
+attr_syntax_read_lock(void)
+{
+ if (0 != attr_syntax_init()) return;
+
+ AS_LOCK_READ(oid2asi_lock);
+ AS_LOCK_READ(name2asi_lock);
+}
+
+void
+attr_syntax_unlock_read(void)
+{
+ if(name2asi_lock) AS_UNLOCK_READ(name2asi_lock);
+ if(oid2asi_lock) AS_UNLOCK_READ(oid2asi_lock);
+}
+
+
+
+#if 0
+static int
+check_oid( const char *oid ) {
+
+ int i = 0, length_oid = 0, rc = 0;
+
+ if ( oid == NULL) {
+ /* this is bad */
+ LDAPDebug (LDAP_DEBUG_ANY, "NULL passed to check_oid\n",0,0,0);
+ return 0;
+ }
+
+ length_oid = strlen(oid);
+ if (length_oid < 4) {
+ /* this is probably bad */
+ LDAPDebug (LDAP_DEBUG_ANY, "Bad oid %s passed to check_oid\n",oid,0,0);
+ return 0;
+ }
+
+ rc = strcasecmp(oid+(length_oid-4), "-oid");
+
+ if ( 0 == rc ) {
+ return 1;
+ }
+
+ /* If not, the OID must begin and end with a digit, and contain only
+ digits and dots */
+
+ if ( !isdigit(oid[0]) ||
+ !isdigit(oid[length_oid-1]) ) {
+ LDAPDebug (LDAP_DEBUG_ANY, "Non numeric oid %s passed to check_oid\n",oid,0,0);
+ return 0;
+ }
+
+ /* check to see that it contains only digits and dots */
+ for ( i = 0; i < length_oid; i++ ) {
+ if ( !isdigit(oid[i]) && oid[i] != '.' ){
+ LDAPDebug (LDAP_DEBUG_ANY, "Non numeric oid %s passed to check_oid\n",oid,0,0);
+ return 0;
+ }
+ }
+
+ /* The oid is OK if we're here */
+ return 1;
+
+
+}
+#endif
+
+#define NBUCKETS(ht) (1 << (PL_HASH_BITS - (ht)->shift))
+
+#if 0
+static int
+attr_syntax_check_oids()
+{
+ int ii = 0;
+ int nbad = 0;
+ AS_LOCK_READ(oid2asi_lock);
+ ii = NBUCKETS(oid2asi);
+ for (;ii;--ii) {
+ PLHashEntry *he = oid2asi->buckets[ii-1];
+ for (; he; he = he->next) {
+ if (!check_oid(he->key)) {
+ LDAPDebug(LDAP_DEBUG_ANY, "Error: bad oid %s in bucket %d\n",
+ he->key, ii-1, 0);
+ nbad++;
+ }
+ }
+ }
+
+ AS_UNLOCK_READ(oid2asi_lock);
+ return nbad;
+}
+#endif
+
+void
+attr_syntax_free( struct asyntaxinfo *a )
+{
+ PR_ASSERT( a->asi_refcnt == 0 );
+
+ cool_charray_free( a->asi_aliases );
+ slapi_ch_free( (void**)&a->asi_name );
+ slapi_ch_free( (void **)&a->asi_desc );
+ slapi_ch_free( (void **)&a->asi_oid );
+ slapi_ch_free( (void **)&a->asi_superior );
+ slapi_ch_free( (void **)&a->asi_mr_equality );
+ slapi_ch_free( (void **)&a->asi_mr_ordering );
+ slapi_ch_free( (void **)&a->asi_mr_substring );
+ cool_charray_free( a->asi_origin );
+ slapi_ch_free( (void **) &a );
+}
+
+static struct asyntaxinfo *
+attr_syntax_new()
+{
+ return (struct asyntaxinfo *)slapi_ch_calloc(1, sizeof(struct asyntaxinfo));
+}
+
+/*
+ * hashNocaseString - used for case insensitive hash lookups
+ */
+static PLHashNumber
+hashNocaseString(const void *key)
+{
+ PLHashNumber h = 0;
+ const unsigned char *s;
+
+ for (s = key; *s; s++)
+ h = (h >> 28) ^ (h << 4) ^ (tolower(*s));
+ return h;
+}
+
+/*
+ * hashNocaseCompare - used for case insensitive hash key comparisons
+ */
+static PRIntn
+hashNocaseCompare(const void *v1, const void *v2)
+{
+ return (strcasecmp((char *)v1, (char *)v2) == 0);
+}
+
+/*
+ * Given an OID, return the syntax info. If there is more than one
+ * attribute syntax with the same OID (i.e. aliases), the first one
+ * will be returned. This is usually the "canonical" one, but it may
+ * not be.
+ *
+ * Note: once the caller is finished using it, the structure returned must
+ * be returned by calling to attr_syntax_return().
+ */
+struct asyntaxinfo *
+attr_syntax_get_by_oid(const char *oid)
+{
+ return attr_syntax_get_by_oid_locking_optional( oid, PR_TRUE, PR_TRUE);
+}
+
+
+static struct asyntaxinfo *
+attr_syntax_get_by_oid_locking_optional( const char *oid, PRBool use_lock, PRBool ref_count )
+{
+ struct asyntaxinfo *asi = 0;
+ if (oid2asi)
+ {
+ if ( use_lock ) AS_LOCK_READ(oid2asi_lock);
+ asi = (struct asyntaxinfo *)PL_HashTableLookup_const(oid2asi, oid);
+ if (asi)
+ {
+ if(ref_count) PR_AtomicIncrement( &asi->asi_refcnt );
+ }
+ if ( use_lock ) AS_UNLOCK_READ(oid2asi_lock);
+ }
+
+ return asi;
+}
+
+/*
+ * Add the syntax info pointer to the look-up-by-oid table.
+ * The lock parameter is used by the initialization code. Normally, we want
+ * to acquire a write lock before we modify the table, but during
+ * initialization, we are running in single threaded mode, so we don't have
+ * to worry about resource contention.
+ */
+static void
+attr_syntax_add_by_oid(const char *oid, struct asyntaxinfo *a, int lock)
+{
+ if (0 != attr_syntax_init()) return;
+
+ if (lock)
+ AS_LOCK_WRITE(oid2asi_lock);
+
+ PL_HashTableAdd(oid2asi, oid, a);
+
+ if (lock)
+ AS_UNLOCK_WRITE(oid2asi_lock);
+}
+
+/*
+ * Return the syntax info given an attribute name. The name may be the
+ * "canonical" name, an alias, or an OID. The given name need not be
+ * normalized since the look up is done case insensitively.
+ *
+ * Note: once the caller is finished using it, the structure returned must
+ * be returned by calling to attr_syntax_return().
+ */
+struct asyntaxinfo *
+attr_syntax_get_by_name(const char *name)
+{
+ return attr_syntax_get_by_name_locking_optional(name, PR_TRUE, PR_TRUE);
+}
+
+
+struct asyntaxinfo *
+attr_syntax_get_by_name_locking_optional(const char *name, PRBool use_lock, PRBool ref_count)
+{
+ struct asyntaxinfo *asi = 0;
+ if (name2asi)
+ {
+ if ( use_lock ) AS_LOCK_READ(name2asi_lock);
+ asi = (struct asyntaxinfo *)PL_HashTableLookup_const(name2asi, name);
+ if ( NULL != asi ) {
+ if(ref_count) PR_AtomicIncrement( &asi->asi_refcnt );
+ }
+ if ( use_lock ) AS_UNLOCK_READ(name2asi_lock);
+ }
+ if (!asi) /* given name may be an OID */
+ asi = attr_syntax_get_by_oid_locking_optional(name, use_lock, ref_count);
+
+ return asi;
+}
+
+
+/*
+ * Give up a reference to an asi.
+ * If the asi has been marked for delete, free it. This would be a bit
+ * easier if we could upgrade a read lock to a write one... but NSPR does
+ * not support that paradigm.
+ */
+void
+attr_syntax_return( struct asyntaxinfo *asi )
+{
+ attr_syntax_return_locking_optional( asi, PR_TRUE );
+}
+
+void
+attr_syntax_return_locking_optional( struct asyntaxinfo *asi, PRBool use_lock )
+{
+ if ( NULL != asi ) {
+ if ( 0 == PR_AtomicDecrement( &asi->asi_refcnt ))
+ {
+ PRBool delete_it;
+
+ if(use_lock) AS_LOCK_READ(name2asi_lock);
+ delete_it = asi->asi_marked_for_delete;
+ if(use_lock) AS_UNLOCK_READ(name2asi_lock);
+
+ if ( delete_it )
+ {
+ AS_LOCK_WRITE(name2asi_lock); /* get a write lock */
+ if ( asi->asi_marked_for_delete ) /* one final check */
+ {
+ attr_syntax_free(asi);
+ }
+ AS_UNLOCK_WRITE(name2asi_lock);
+ }
+ }
+ }
+}
+
+/*
+ * Add the syntax info to the look-up-by-name table. The asi_name and
+ * elements of the asi_aliasses field of the syntax info are the keys.
+ * These need not be normalized since the look up table is case insensitive.
+ * The lock parameter is used by the initialization code. Normally, we want
+ * to acquire a write lock before we modify the table, but during
+ * initialization, we are running in single threaded mode, so we don't have
+ * to worry about resource contention.
+ */
+static void
+attr_syntax_add_by_name(struct asyntaxinfo *a, int lock)
+{
+ if (0 != attr_syntax_init()) return;
+
+ if (lock)
+ AS_LOCK_WRITE(name2asi_lock);
+
+ PL_HashTableAdd(name2asi, a->asi_name, a);
+ if ( a->asi_aliases != NULL ) {
+ int i;
+
+ for ( i = 0; a->asi_aliases[i] != NULL; ++i ) {
+ PL_HashTableAdd(name2asi, a->asi_aliases[i], a);
+ }
+ }
+
+ if (lock)
+ AS_UNLOCK_WRITE(name2asi_lock);
+}
+
+
+/*
+ * Delete the attribute syntax and all entries corresponding to aliases
+ * and oids.
+ */
+void
+attr_syntax_delete( struct asyntaxinfo *asi )
+{
+ PR_ASSERT( asi );
+
+ if (oid2asi && name2asi) {
+ AS_LOCK_WRITE(oid2asi_lock);
+ AS_LOCK_WRITE(name2asi_lock);
+
+ attr_syntax_delete_no_lock( asi, PR_TRUE );
+
+ AS_UNLOCK_WRITE(name2asi_lock);
+ AS_UNLOCK_WRITE(oid2asi_lock);
+ }
+}
+
+
+/*
+ * Dispose of a node. The caller is responsible for locking. See
+ * attr_syntax_delete() for an example.
+ */
+static void
+attr_syntax_delete_no_lock( struct asyntaxinfo *asi,
+ PRBool remove_from_oidtable )
+{
+ int i;
+
+ if (oid2asi && remove_from_oidtable ) {
+ PL_HashTableRemove(oid2asi, asi->asi_oid);
+ }
+
+ if(name2asi) {
+ PL_HashTableRemove(name2asi, asi->asi_name);
+ if ( asi->asi_aliases != NULL ) {
+ for ( i = 0; asi->asi_aliases[i] != NULL; ++i ) {
+ PL_HashTableRemove(name2asi, asi->asi_aliases[i]);
+ }
+ }
+ if ( asi->asi_refcnt > 0 ) {
+ asi->asi_marked_for_delete = PR_TRUE;
+ } else {
+ attr_syntax_free(asi);
+ }
+ }
+}
+
+
+/*
+ * Look up the attribute type in the syntaxes and return a copy of the
+ * normalised attribute type. If it's not there then return a normalised
+ * copy of what the caller gave us.
+ *
+ * Warning: The caller must free the returned string.
+ */
+
+
+
+char *
+slapi_attr_syntax_normalize( const char *s )
+{
+ struct asyntaxinfo *asi = NULL;
+ char *r;
+
+
+ if((asi=attr_syntax_get_by_name_locking_optional(s, PR_TRUE, PR_FALSE)) != NULL ) {
+ r = slapi_ch_strdup(asi->asi_name);
+ }
+ if ( NULL == asi ) {
+ r = attr_syntax_normalize_no_lookup( s );
+ }
+ return r;
+}
+
+
+/*
+ * attr_syntax_exists: return 1 if attr_name exists, 0 otherwise
+ *
+ */
+int
+attr_syntax_exists(const char *attr_name)
+{
+ struct asyntaxinfo *asi;
+
+ asi = attr_syntax_get_by_name(attr_name);
+ attr_syntax_return( asi );
+
+ if ( asi != NULL )
+ {
+ return 1;
+ }
+ return 0;
+}
+
+/* check syntax without incrementing refcount -- handles locking itself */
+
+static void *
+attr_syntax_get_plugin_by_name_with_default( const char *type )
+{
+ struct asyntaxinfo *asi;
+ void *plugin = NULL;
+
+ /*
+ * first we look for this attribute type explictly
+ */
+ if ( (asi = attr_syntax_get_by_name_locking_optional(type, PR_TRUE, PR_FALSE)) == NULL ) {
+ /*
+ * no syntax for this type... return DirectoryString
+ * syntax. we accomplish this by looking up a well known
+ * attribute type that has that syntax.
+ */
+ asi = attr_syntax_get_by_name_locking_optional(
+ ATTR_WITH_DIRSTRING_SYNTAX, PR_TRUE, PR_FALSE);
+ }
+ if ( NULL != asi ) {
+ plugin = asi->asi_plugin;
+ }
+ return( plugin );
+}
+
+
+static struct asyntaxinfo *
+attr_syntax_dup( struct asyntaxinfo *a )
+{
+ struct asyntaxinfo *newas = attr_syntax_new();
+
+ newas->asi_aliases = cool_charray_dup( a->asi_aliases );
+ newas->asi_name = slapi_ch_strdup( a->asi_name );
+ newas->asi_desc = slapi_ch_strdup( a->asi_desc );
+ newas->asi_superior = slapi_ch_strdup( a->asi_superior );
+ newas->asi_mr_equality = slapi_ch_strdup( a->asi_mr_equality );
+ newas->asi_mr_ordering = slapi_ch_strdup( a->asi_mr_ordering );
+ newas->asi_mr_substring = slapi_ch_strdup( a->asi_mr_substring );
+ newas->asi_origin = cool_charray_dup( a->asi_origin );
+ newas->asi_plugin = a->asi_plugin;
+ newas->asi_flags = a->asi_flags;
+ newas->asi_oid = slapi_ch_strdup( a->asi_oid);
+ newas->asi_syntaxlength = a->asi_syntaxlength;
+
+ return( newas );
+}
+
+
+/*
+ * Add a new attribute type to the schema.
+ *
+ * Returns an LDAP error code (LDAP_SUCCESS if all goes well).
+ */
+int
+attr_syntax_add( struct asyntaxinfo *asip )
+{
+ int i, rc = LDAP_SUCCESS;
+ int nolock = asip->asi_flags & SLAPI_ATTR_FLAG_NOLOCKING;
+ struct asyntaxinfo *oldas_from_oid = NULL, *oldas_from_name = NULL;
+ /* attr names may have subtypes in them, and we may not want this
+ if strip_subtypes is true, the ; and anything after it in the
+ attr name or alias will be stripped */
+ /*int strip_subtypes = 1;*/
+
+ /* make sure the oid is unique */
+ if ( NULL != ( oldas_from_oid = attr_syntax_get_by_oid_locking_optional(
+ asip->asi_oid, !nolock, PR_TRUE))) {
+ if ( 0 == (asip->asi_flags & SLAPI_ATTR_FLAG_OVERRIDE)) {
+ /* failure - OID is in use; no override flag */
+ rc = LDAP_TYPE_OR_VALUE_EXISTS;
+ goto cleanup_and_return;
+ }
+ }
+
+ /* make sure the primary name is unique OR, if override is allowed, that
+ * the primary name and OID point to the same schema definition.
+ */
+ if ( NULL != ( oldas_from_name = attr_syntax_get_by_name_locking_optional(
+ asip->asi_name, !nolock, PR_TRUE))) {
+ if ( 0 == (asip->asi_flags & SLAPI_ATTR_FLAG_OVERRIDE)
+ || ( oldas_from_oid != oldas_from_name )) {
+ /* failure; no override flag OR OID and name don't match */
+ rc = LDAP_TYPE_OR_VALUE_EXISTS;
+ goto cleanup_and_return;
+ }
+ attr_syntax_delete(oldas_from_name);
+ } else if ( NULL != oldas_from_oid ) {
+ /* failure - OID is in use but name does not exist */
+ rc = LDAP_TYPE_OR_VALUE_EXISTS;
+ goto cleanup_and_return;
+ }
+
+ if ( NULL != asip->asi_aliases ) {
+ /* make sure the aliases are unique */
+ for (i = 0; asip->asi_aliases[i] != NULL; ++i) {
+ struct asyntaxinfo *tmpasi;
+
+ if ( NULL != ( tmpasi =
+ attr_syntax_get_by_name_locking_optional(
+ asip->asi_aliases[i], !nolock,PR_TRUE))) {
+ if (asip->asi_flags & SLAPI_ATTR_FLAG_OVERRIDE) {
+ attr_syntax_delete(tmpasi);
+ } else {
+ /* failure - one of the aliases is already in use */
+ rc = LDAP_TYPE_OR_VALUE_EXISTS;
+ }
+
+ attr_syntax_return( tmpasi );
+ if ( LDAP_SUCCESS != rc ) {
+ goto cleanup_and_return;
+ }
+ }
+ }
+ }
+
+ /* the no lock flag is not worth keeping around */
+ asip->asi_flags &= ~SLAPI_ATTR_FLAG_NOLOCKING;
+ /* ditto for the override one */
+ asip->asi_flags &= ~SLAPI_ATTR_FLAG_OVERRIDE;
+
+ attr_syntax_add_by_oid( asip->asi_oid, asip, !nolock);
+ attr_syntax_add_by_name( asip, !nolock);
+
+cleanup_and_return:
+ attr_syntax_return( oldas_from_oid );
+ attr_syntax_return( oldas_from_name );
+ return rc;
+}
+
+
+/*
+ * Returns an LDAP result code.
+ */
+int
+attr_syntax_create(
+ const char *attr_oid,
+ char *const *attr_names,
+ int num_names,
+ const char *attr_desc,
+ const char *attr_superior,
+ const char *mr_equality,
+ const char *mr_ordering,
+ const char *mr_substring,
+ char *const *attr_origins,
+ const char *attr_syntax,
+ int syntaxlength,
+ unsigned long flags,
+ struct asyntaxinfo **asip
+)
+{
+ char *s;
+ struct asyntaxinfo a;
+
+ /* XXXmcs: had to cast away const in many places below */
+ memset(&a, 0, sizeof(a));
+ a.asi_name = slapi_ch_strdup(attr_names[0]);
+ if ( NULL != attr_names[1] ) {
+ a.asi_aliases = (char **)&attr_names[1]; /* all but the zero'th element */
+ }
+ a.asi_desc = (char*)attr_desc;
+ a.asi_oid = (char*)attr_oid;
+ a.asi_superior = (char*)attr_superior;
+ a.asi_mr_equality = (char*)mr_equality;
+ a.asi_mr_ordering = (char*)mr_ordering;
+ a.asi_mr_substring = (char*)mr_substring;
+ a.asi_origin = (char **)attr_origins;
+ a.asi_plugin = plugin_syntax_find( attr_syntax );
+ a.asi_syntaxlength = syntaxlength;
+ a.asi_flags = flags;
+
+ /*
+ * If the 'return exact case' option is on (the default), we store the
+ * first name (the canonical one) unchanged so that attribute names are
+ * returned exactly as they appear in the schema configuration files.
+ * But if 'return exact case' has been turned off, we convert the name
+ * to lowercase. In Netscape Directory Server 4.x and earlier versions,
+ * the default was to convert to lowercase.
+ */
+ if (!config_get_return_exact_case()) {
+ for (s = a.asi_name; *s; ++s) {
+ *s = TOLOWER(*s);
+ }
+ }
+
+ *asip = attr_syntax_dup(&a);
+ slapi_ch_free((void **)&a.asi_name);
+
+ return LDAP_SUCCESS;
+}
+
+
+/*
+ * slapi_attr_type2plugin - return the plugin handling the attribute type
+ * if type is unknown, we return the caseIgnoreString plugin used by the
+ * objectClass attribute type.
+ */
+
+int
+slapi_attr_type2plugin( const char *type, void **pi )
+{
+ char buf[SLAPD_TYPICAL_ATTRIBUTE_NAME_MAX_LENGTH];
+ char *tmp, *basetype;
+ int rc;
+
+ basetype = buf;
+ if ( (tmp = slapi_attr_basetype( type, buf, sizeof(buf) )) != NULL ) {
+ basetype = tmp;
+ }
+ rc = -1;
+ *pi = attr_syntax_get_plugin_by_name_with_default( basetype );
+ if ( NULL != *pi ) {
+ rc = 0;
+ }
+ if ( tmp != NULL ) {
+ free( tmp );
+ }
+
+ return( rc );
+}
+
+/* deprecated -- not MT safe (pointer into asi is returned!) */
+int
+slapi_attr_get_oid( const Slapi_Attr *a, char **oid )
+{
+ struct asyntaxinfo *asi = attr_syntax_get_by_name(a->a_type);
+ if (asi) {
+ *oid = asi->asi_oid;
+ attr_syntax_return(asi);
+ return( 0 );
+ } else {
+ *oid = NULL;
+ return( -1 );
+ }
+}
+
+
+/* The caller must dispose of oid by calling slapi_ch_free(). */
+int
+slapi_attr_get_oid_copy( const Slapi_Attr *a, char **oidp )
+{
+ struct asyntaxinfo *asi = attr_syntax_get_by_name(a->a_type);
+ if (asi) {
+ *oidp = slapi_ch_strdup( asi->asi_oid );
+ attr_syntax_return(asi);
+ return( 0 );
+ } else {
+ *oidp = NULL;
+ return( -1 );
+ }
+}
+
+#ifdef ATTR_LDAP_DEBUG
+
+PRIntn
+attr_syntax_printnode(PLHashEntry *he, PRIntn i, void *arg)
+{
+ char *alias = (char *)he->key;
+ struct asyntaxinfo *a = (struct asyntaxinfo *)he->value;
+ printf( " name: %s\n", a->asi_name );
+ printf( "\t flags : 0x%x\n", a->asi_flags );
+ printf( "\t alias : %s\n", alias );
+ printf( "\t desc : %s\n", a->asi_desc );
+ printf( "\t oid : %s\n", a->asi_oid );
+ printf( "\t superior : %s\n", a->asi_superior );
+ printf( "\t mr_equality : %s\n", a->asi_mr_equality );
+ printf( "\t mr_ordering : %s\n", a->asi_mr_ordering );
+ printf( "\t mr_substring: %s\n", a->asi_mr_substring );
+ if ( NULL != a->asi_origin ) {
+ for ( i = 0; NULL != a->asi_origin[i]; ++i ) {
+ printf( "\t origin : %s\n", a->asi_origin[i] );
+ }
+ }
+ printf( "\tplugin: %p\n", a->asi_plugin );
+ printf( "--------------\n" );
+
+ return HT_ENUMERATE_NEXT;
+}
+
+void
+attr_syntax_print()
+{
+ printf( "*** attr_syntax_print ***\n" );
+ PL_HashTableEnumerateEntries(name2asi, attr_syntax_printnode, 0);
+}
+
+#endif
+
+
+/* lowercase the attr name and chop trailing spaces */
+/* note that s may contain options also, e.g., userCertificate;binary */
+char *
+attr_syntax_normalize_no_lookup( const char *s )
+{
+ char *save, *tmps;
+
+ tmps = slapi_ch_strdup(s);
+ for ( save = tmps; (*tmps != '\0') && (*tmps != ' '); tmps++ )
+ {
+ *tmps = TOLOWER( *tmps );
+ }
+ *tmps = '\0';
+
+ return save;
+}
+
+struct enum_arg_wrapper {
+ AttrEnumFunc aef;
+ void *arg;
+};
+
+PRIntn
+attr_syntax_enumerate_internal(PLHashEntry *he, PRIntn i, void *arg)
+{
+ struct enum_arg_wrapper *eaw = (struct enum_arg_wrapper *)arg;
+ int rc;
+
+ rc = (eaw->aef)((struct asyntaxinfo *)he->value, eaw->arg);
+ if ( ATTR_SYNTAX_ENUM_STOP == rc ) {
+ rc = HT_ENUMERATE_STOP;
+ } else if ( ATTR_SYNTAX_ENUM_REMOVE == rc ) {
+ rc = HT_ENUMERATE_REMOVE;
+ } else {
+ rc = HT_ENUMERATE_NEXT;
+ }
+
+ return rc;
+}
+
+void
+attr_syntax_enumerate_attrs(AttrEnumFunc aef, void *arg, PRBool writelock )
+{
+ struct enum_arg_wrapper eaw;
+ eaw.aef = aef;
+ eaw.arg = arg;
+
+ if (!oid2asi)
+ return;
+
+ if ( writelock ) {
+ AS_LOCK_WRITE(oid2asi_lock);
+ AS_LOCK_WRITE(name2asi_lock);
+ } else {
+ AS_LOCK_READ(oid2asi_lock);
+ AS_LOCK_READ(name2asi_lock);
+ }
+
+ PL_HashTableEnumerateEntries(oid2asi, attr_syntax_enumerate_internal, &eaw);
+
+ if ( writelock ) {
+ AS_UNLOCK_WRITE(oid2asi_lock);
+ AS_UNLOCK_WRITE(name2asi_lock);
+ } else {
+ AS_UNLOCK_READ(oid2asi_lock);
+ AS_UNLOCK_READ(name2asi_lock);
+ }
+}
+
+
+struct attr_syntax_enum_flaginfo {
+ unsigned long asef_flag;
+};
+
+static int
+attr_syntax_clear_flag_callback(struct asyntaxinfo *asip, void *arg)
+{
+ struct attr_syntax_enum_flaginfo *fi;
+
+ PR_ASSERT( asip != NULL );
+ fi = (struct attr_syntax_enum_flaginfo *)arg;
+ PR_ASSERT( fi != NULL );
+
+ asip->asi_flags &= ~(fi->asef_flag);
+
+ return ATTR_SYNTAX_ENUM_NEXT;
+}
+
+
+static int
+attr_syntax_delete_if_not_flagged(struct asyntaxinfo *asip, void *arg)
+{
+ struct attr_syntax_enum_flaginfo *fi;
+
+ PR_ASSERT( asip != NULL );
+ fi = (struct attr_syntax_enum_flaginfo *)arg;
+ PR_ASSERT( fi != NULL );
+
+ if ( 0 == ( asip->asi_flags & fi->asef_flag )) {
+ attr_syntax_delete_no_lock( asip, PR_FALSE );
+ return ATTR_SYNTAX_ENUM_REMOVE;
+ } else {
+ return ATTR_SYNTAX_ENUM_NEXT;
+ }
+}
+
+
+/*
+ * Clear 'flag' within all attribute definitions.
+ */
+void
+attr_syntax_all_clear_flag( unsigned long flag )
+{
+ struct attr_syntax_enum_flaginfo fi;
+
+ memset( &fi, 0, sizeof(fi));
+ fi.asef_flag = flag;
+ attr_syntax_enumerate_attrs( attr_syntax_clear_flag_callback,
+ (void *)&fi, PR_TRUE );
+}
+
+
+/*
+ * Delete all attribute definitions that do not contain any bits of 'flag'
+ * in their flags.
+ */
+void
+attr_syntax_delete_all_not_flagged( unsigned long flag )
+{
+ struct attr_syntax_enum_flaginfo fi;
+
+ memset( &fi, 0, sizeof(fi));
+ fi.asef_flag = flag;
+ attr_syntax_enumerate_attrs( attr_syntax_delete_if_not_flagged,
+ (void *)&fi, PR_TRUE );
+}
+
+static int
+attr_syntax_init(void)
+{
+ if (!oid2asi)
+ {
+ oid2asi = PL_NewHashTable(2047, hashNocaseString,
+ hashNocaseCompare,
+ PL_CompareValues, 0, 0);
+ if ( NULL == ( oid2asi_lock = PR_NewRWLock( PR_RWLOCK_RANK_NONE,
+ "attrsyntax oid rwlock" ))) {
+ if(oid2asi) PL_HashTableDestroy(oid2asi);
+ oid2asi = NULL;
+
+ slapi_log_error( SLAPI_LOG_FATAL, "attr_syntax_init",
+ "PR_NewRWLock() for oid2asi lock failed\n" );
+ return 1;
+ }
+ }
+
+ if (!name2asi)
+ {
+ name2asi = PL_NewHashTable(2047, hashNocaseString,
+ hashNocaseCompare,
+ PL_CompareValues, 0, 0);
+ if ( NULL == ( name2asi_lock = PR_NewRWLock( PR_RWLOCK_RANK_NONE,
+ "attrsyntax name2asi rwlock"))) {
+ if(name2asi) PL_HashTableDestroy(name2asi);
+ name2asi = NULL;
+
+ slapi_log_error( SLAPI_LOG_FATAL, "attr_syntax_init",
+ "PR_NewRWLock() for oid2asi lock failed\n" );
+ return 1;
+ }
+ }
+ return 0;
+}
diff --git a/ldap/servers/slapd/auditlog.c b/ldap/servers/slapd/auditlog.c
new file mode 100644
index 00000000..e2f11afb
--- /dev/null
+++ b/ldap/servers/slapd/auditlog.c
@@ -0,0 +1,217 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+
+#include "slap.h"
+
+/*
+ * JCM - The audit log might be better implemented as a post-op plugin.
+ */
+
+#define ATTR_CHANGETYPE "changetype"
+#define ATTR_NEWRDN "newrdn"
+#define ATTR_DELETEOLDRDN "deleteoldrdn"
+#define ATTR_MODIFIERSNAME "modifiersname"
+char *attr_changetype = ATTR_CHANGETYPE;
+char *attr_newrdn = ATTR_NEWRDN;
+char *attr_deleteoldrdn = ATTR_DELETEOLDRDN;
+char *attr_modifiersname = ATTR_MODIFIERSNAME;
+
+/* Forward Declarations */
+static void write_audit_file( int optype, char *dn, void *change, int flag, time_t curtime );
+
+void
+write_audit_log_entry( Slapi_PBlock *pb )
+{
+ time_t curtime;
+ char *dn;
+ void *change;
+ int flag = 0;
+ int internal_op = 0;
+ Operation *op;
+
+ slapi_pblock_get( pb, SLAPI_OPERATION, &op );
+ internal_op = operation_is_flag_set(op, OP_FLAG_INTERNAL);
+ slapi_pblock_get( pb, SLAPI_TARGET_DN, &dn );
+ switch ( operation_get_type(op) )
+ {
+ case SLAPI_OPERATION_MODIFY:
+ slapi_pblock_get( pb, SLAPI_MODIFY_MODS, &change );
+ break;
+ case SLAPI_OPERATION_ADD:
+ {
+ /*
+ * For adds, we want the unnormalized dn, so we can preserve
+ * spacing, case, when replicating it.
+ */
+ Slapi_Entry *te = NULL;
+ slapi_pblock_get( pb, SLAPI_ADD_ENTRY, &change );
+ te = (Slapi_Entry *)change;
+ if ( NULL != te )
+ {
+ dn = slapi_entry_get_dn( te );
+ }
+ }
+ break;
+ case SLAPI_OPERATION_DELETE:
+ {
+ char * deleterDN = NULL;
+ slapi_pblock_get(pb, SLAPI_REQUESTOR_DN, &deleterDN);
+ change = deleterDN;
+ }
+ break;
+
+ case SLAPI_OPERATION_MODDN:
+ slapi_pblock_get( pb, SLAPI_MODRDN_NEWRDN, &change );
+ slapi_pblock_get( pb, SLAPI_MODRDN_DELOLDRDN, &flag );
+ break;
+ }
+ curtime = current_time();
+ write_audit_file( operation_get_type(op), dn, change, flag, curtime );
+}
+
+
+
+/*
+ * Function: write_audit_file
+ * Arguments:
+ * optype - type of LDAP operation being logged
+ * dn - distinguished name of entry being changed
+ * change - pointer to the actual change operation
+ * For a delete operation, may contain the modifier's DN.
+ * flag - only used by modrdn operations - value of deleteoldrdn flag
+ * curtime - the current time
+ * Returns: nothing
+ */
+static void
+write_audit_file(
+ int optype,
+ char *dn,
+ void *change,
+ int flag,
+ time_t curtime
+)
+{
+ LDAPMod **mods;
+ Slapi_Entry *e;
+ char *newrdn, *tmp, *tmpsave;
+ int len, i, j;
+ char *timestr;
+ lenstr *l;
+
+ l = lenstr_new();
+
+ addlenstr( l, "time: " );
+ timestr = format_localTime( curtime );
+ addlenstr( l, timestr );
+ slapi_ch_free((void **) &timestr );
+ addlenstr( l, "\n" );
+ addlenstr( l, "dn: " );
+ addlenstr( l, dn );
+ addlenstr( l, "\n" );
+
+ switch ( optype )
+ {
+ case SLAPI_OPERATION_MODIFY:
+ addlenstr( l, attr_changetype );
+ addlenstr( l, ": modify\n" );
+ mods = change;
+ for ( j = 0; mods[j] != NULL; j++ )
+ {
+ int operationtype= mods[j]->mod_op & ~LDAP_MOD_BVALUES;
+ switch ( operationtype )
+ {
+ case LDAP_MOD_ADD:
+ addlenstr( l, "add: " );
+ addlenstr( l, mods[j]->mod_type );
+ addlenstr( l, "\n" );
+ break;
+
+ case LDAP_MOD_DELETE:
+ addlenstr( l, "delete: " );
+ addlenstr( l, mods[j]->mod_type );
+ addlenstr( l, "\n" );
+ break;
+
+ case LDAP_MOD_REPLACE:
+ addlenstr( l, "replace: " );
+ addlenstr( l, mods[j]->mod_type );
+ addlenstr( l, "\n" );
+ break;
+
+ default:
+ operationtype= LDAP_MOD_IGNORE;
+ break;
+ }
+ if(operationtype!=LDAP_MOD_IGNORE)
+ {
+ for ( i = 0; mods[j]->mod_bvalues != NULL && mods[j]->mod_bvalues[i] != NULL; i++ )
+ {
+ char *buf, *bufp;
+ len = strlen( mods[j]->mod_type );
+ len = LDIF_SIZE_NEEDED( len, mods[j]->mod_bvalues[i]->bv_len ) + 1;
+ buf = slapi_ch_malloc( len );
+ bufp = buf;
+ ldif_put_type_and_value( &bufp, mods[j]->mod_type,
+ mods[j]->mod_bvalues[i]->bv_val,
+ mods[j]->mod_bvalues[i]->bv_len );
+ *bufp = '\0';
+ addlenstr( l, buf );
+ slapi_ch_free( (void**)&buf );
+ }
+ }
+ addlenstr( l, "-\n" );
+ }
+ break;
+
+ case SLAPI_OPERATION_ADD:
+ e = change;
+ addlenstr( l, attr_changetype );
+ addlenstr( l, ": add\n" );
+ tmp = slapi_entry2str( e, &len );
+ tmpsave = tmp;
+ while (( tmp = strchr( tmp, '\n' )) != NULL )
+ {
+ tmp++;
+ if ( !ldap_utf8isspace( tmp ))
+ {
+ break;
+ }
+ }
+ addlenstr( l, tmp );
+ slapi_ch_free((void**)&tmpsave );
+ break;
+
+ case SLAPI_OPERATION_DELETE:
+ tmp = change;
+ addlenstr( l, attr_changetype );
+ addlenstr( l, ": delete\n" );
+ if (tmp && tmp[0]) {
+ addlenstr( l, attr_modifiersname );
+ addlenstr( l, ": ");
+ addlenstr( l, tmp);
+ addlenstr( l, "\n");
+ }
+ break;
+
+ case SLAPI_OPERATION_MODDN:
+ newrdn = change;
+ addlenstr( l, attr_changetype );
+ addlenstr( l, ": modrdn\n" );
+ addlenstr( l, attr_newrdn );
+ addlenstr( l, ": " );
+ addlenstr( l, newrdn );
+ addlenstr( l, "\n" );
+ addlenstr( l, attr_deleteoldrdn );
+ addlenstr( l, ": " );
+ addlenstr( l, flag ? "1" : "0" );
+ addlenstr( l, "\n" );
+ }
+ addlenstr( l, "\n" );
+
+ slapd_log_audit_proc (l->ls_buf, l->ls_len);
+
+ lenstr_free( &l );
+}
diff --git a/ldap/servers/slapd/auth.c b/ldap/servers/slapd/auth.c
new file mode 100644
index 00000000..cfcb1c2e
--- /dev/null
+++ b/ldap/servers/slapd/auth.c
@@ -0,0 +1,506 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+#include <stdlib.h> /* getenv */
+#include <string.h> /* memcpy */
+#include <ldaputil/ldaputil.h> /* LDAPU_SUCCESS, ldapu_VTable_set */
+#include <ldaputil/init.h> /* ldaputil_init */
+#include <ldaputil/certmap.h> /* ldapu_cert_to_ldap_entry */
+#ifndef _WIN32
+#include <sys/param.h> /* MAXPATHLEN */
+#endif
+#include "slap.h" /* slapi_ch_malloc */
+#include "fe.h"
+
+char* client_auth_config_file = NULL;
+
+/* forward declarations */
+
+static void generate_id();
+static Slapi_ComponentId * auth_get_component_id();
+
+#define internal_ld NULL
+
+static int LDAP_CALL LDAP_CALLBACK
+slapu_msgfree( LDAP* ld, LDAPMessage* msg )
+{
+ Slapi_PBlock* pb = (Slapi_PBlock*)msg;
+ if (ld != internal_ld) {
+ return ldap_msgfree (msg);
+ }
+ if (pb) {
+ slapi_free_search_results_internal (pb);
+ slapi_pblock_destroy (pb);
+ }
+ return LDAP_SUCCESS;
+}
+
+static int LDAP_CALL LDAP_CALLBACK
+slapu_search_s( LDAP* ld, const char* baseDN, int scope, const char* filter,
+ char** attrs, int attrsonly, LDAPMessage** result )
+{
+ int err = LDAP_NO_SUCH_OBJECT;
+ Slapi_PBlock* pb;
+ LDAPControl **ctrls;
+
+ if (ld != internal_ld) {
+ return ldap_search_s (ld, baseDN, scope, filter, attrs, attrsonly, result);
+ }
+ LDAPDebug (LDAP_DEBUG_TRACE, "=> slapu_search_s (\"%s\", %i, %s)\n",
+ baseDN, scope, filter);
+ if (filter == NULL) filter = "objectclass=*";
+
+ /* use new internal search API */
+ pb=slapi_pblock_new();
+ /* we need to provide managedsait control to avoid returning continuation references */
+ ctrls = (LDAPControl **)slapi_ch_calloc (2, sizeof (LDAPControl *));
+ ctrls[0] = (LDAPControl*)slapi_ch_malloc (sizeof (LDAPControl));
+ ctrls[0]->ldctl_oid = slapi_ch_strdup (LDAP_CONTROL_MANAGEDSAIT);
+ ctrls[0]->ldctl_value.bv_val = NULL;
+ ctrls[0]->ldctl_value.bv_len = 0;
+ ctrls[0]->ldctl_iscritical = '\0';
+ slapi_search_internal_set_pb(pb, baseDN, scope, (char *)filter, attrs, attrsonly,
+ ctrls, NULL, auth_get_component_id(), 0 /* actions */);
+ slapi_search_internal_pb(pb);
+
+ if (pb != NULL) {
+ if (slapi_pblock_get (pb, SLAPI_PLUGIN_INTOP_RESULT, &err)) {
+ err = LDAP_LOCAL_ERROR;
+ }
+ if (err != LDAP_SUCCESS) {
+ slapu_msgfree (ld, (LDAPMessage*)pb);
+ pb = NULL;
+ if (scope == LDAP_SCOPE_SUBTREE) {
+ char ebuf[ BUFSIZ ], fbuf[ BUFSIZ ];
+ LDAPDebug (LDAP_DEBUG_ANY, "slapi_search_internal (\"%s\", subtree, %s) err %i\n",
+ escape_string( (char*)baseDN, ebuf ), escape_string( (char*)filter, fbuf ), err);
+ }
+ }
+ } else {
+ char ebuf[ BUFSIZ ], fbuf[ BUFSIZ ];
+ LDAPDebug (LDAP_DEBUG_ANY, "slapi_search_internal (\"%s\", %i, %s) NULL\n",
+ escape_string( (char*)baseDN, ebuf ), scope, escape_string( (char*)filter, fbuf ));
+ }
+ *result = (LDAPMessage*)pb;
+ LDAPDebug (LDAP_DEBUG_TRACE, "<= slapu_search_s %i\n", err, 0, 0);
+ return err;
+}
+
+static int LDAP_CALL LDAP_CALLBACK
+slapu_count_entries( LDAP* ld, LDAPMessage* msg )
+{
+ Slapi_Entry** entry = NULL;
+ int count = 0;
+ if (ld != internal_ld) {
+ return ldap_count_entries (ld, msg);
+ }
+ if (!slapi_pblock_get ((Slapi_PBlock*)msg, SLAPI_PLUGIN_INTOP_SEARCH_ENTRIES, &entry)
+ && entry) {
+ for (; *entry; ++entry) ++count;
+ }
+ return count;
+}
+
+/* slapu_search_s() returns a Slapi_PBlock*, but slapu_first_entry() and
+ * slapu_next_entry() return a Slapi_Entry** pointing into the same array
+ * as the PBlock. If one of the iteration (Slapi_Entry**) pointers was
+ * passed to slapu_msgfree(), havoc would ensue. ldaputil never does this.
+ * But ldap_msgfree() would support it (no?); so a plugin function might.
+ * Yet another way this doesn't support plugin functions.
+ */
+
+static LDAPMessage* LDAP_CALL LDAP_CALLBACK
+slapu_first_entry( LDAP* ld, LDAPMessage* msg )
+{
+ Slapi_Entry** entry = NULL;
+ if (ld != internal_ld) {
+ return ldap_first_entry (ld, msg);
+ }
+ if (!slapi_pblock_get ((Slapi_PBlock*)msg, SLAPI_PLUGIN_INTOP_SEARCH_ENTRIES, &entry)
+ && entry && *entry) {
+ return (LDAPMessage*)entry;
+ }
+ return NULL;
+}
+
+static LDAPMessage* LDAP_CALL LDAP_CALLBACK
+slapu_next_entry( LDAP* ld, LDAPMessage* msg )
+{
+ Slapi_Entry** entry = (Slapi_Entry**)msg;
+ if (ld != internal_ld) {
+ return ldap_next_entry (ld, msg);
+ }
+ if (entry && *entry && *++entry) {
+ return (LDAPMessage*)entry;
+ }
+ return NULL;
+}
+
+static char* LDAP_CALL LDAP_CALLBACK
+slapu_get_dn( LDAP* ld, LDAPMessage* entry )
+{
+ if (ld != internal_ld) {
+ return ldap_get_dn (ld, entry);
+ }
+ return slapi_ch_strdup (slapi_entry_get_dn (*(Slapi_Entry**)entry));
+}
+
+static void LDAP_CALL LDAP_CALLBACK
+slapu_memfree( LDAP* ld, void* dn )
+{
+ if (ld != internal_ld) {
+ ldap_memfree (dn);
+ } else {
+ free (dn);
+ }
+}
+
+static char*
+slapu_attr_get_desc( Slapi_Attr *attr )
+{
+ char* desc = NULL;
+ if (slapi_attr_get_type (attr, &desc) == LDAP_SUCCESS && desc) {
+ return slapi_ch_strdup (desc);
+ }
+ return NULL;
+}
+
+/* slapu_first_attribute and slapu_next_attribute use a Slapi_Attr*
+ * as an iterator. It is malloc'd by first() and free'd by ber_free().
+ */
+
+static char* LDAP_CALL LDAP_CALLBACK
+slapu_first_attribute( LDAP* ld, LDAPMessage* entry, BerElement** iter )
+{
+ if (ld != internal_ld) {
+ return ldap_first_attribute (ld, entry, iter);
+ } else {
+ Slapi_Attr** attr = (Slapi_Attr**) slapi_ch_malloc (sizeof(Slapi_Attr*));
+ *iter = (BerElement*) attr;
+ if (attr && slapi_entry_first_attr (*(Slapi_Entry**)entry, attr) == LDAP_SUCCESS) {
+ return slapu_attr_get_desc (*attr);
+ }
+ }
+ return NULL;
+}
+
+static char* LDAP_CALL LDAP_CALLBACK
+slapu_next_attribute( LDAP* ld, LDAPMessage* entry, BerElement* iter)
+{
+ Slapi_Attr** attr = (Slapi_Attr**)iter;
+ if (ld != internal_ld) {
+ return ldap_next_attribute (ld, entry, iter);
+ }
+ if (attr && slapi_entry_next_attr (*(Slapi_Entry**)entry, *attr, attr) == LDAP_SUCCESS) {
+ return slapu_attr_get_desc (*attr);
+ }
+ return NULL;
+}
+
+static void LDAP_CALL LDAP_CALLBACK
+slapu_ber_free( LDAP* ld, BerElement* iter, int freebuf )
+{
+ if (ld != internal_ld) {
+ ldap_ber_free (iter, freebuf);
+ } else {
+ free ((Slapi_Attr**)iter);
+ }
+}
+
+static struct berval** LDAP_CALL LDAP_CALLBACK
+slapu_get_values_len( LDAP *ld, LDAPMessage *entry, const char *desc )
+{
+ Slapi_Attr* attr = NULL;
+ if (ld != internal_ld) {
+ return ldap_get_values_len (ld, entry, desc);
+ }
+ if (slapi_entry_attr_find (*(Slapi_Entry**)entry, desc, &attr) == LDAP_SUCCESS
+ && attr) {
+ struct berval** values = NULL;
+ if ( slapi_attr_get_bervals_copy (attr, &values) == 0 ) {
+ return (values);
+ }
+ }
+ return NULL;
+}
+
+static void LDAP_CALL LDAP_CALLBACK
+slapu_value_free_len( LDAP* ld, struct berval **values )
+{
+ if (ld != internal_ld) {
+ ldap_value_free_len (values);
+ } else {
+ ber_bvecfree (values);
+ }
+}
+
+void
+client_auth_init ()
+{
+ char *instancedir;
+ int len = 0;
+ char *val = NULL;
+ char* filename;
+ char netsite_root[MAXPATHLEN];
+ int err;
+ if (client_auth_config_file == NULL) {
+ client_auth_config_file = "shared/config/certmap.conf";
+ }
+
+ /* calculate the server_root from instance dir */
+ instancedir = config_get_instancedir();
+ /* make sure path does not end in the path separator character */
+ len = strlen(instancedir);
+ if (instancedir[len-1] == '/' || instancedir[len-1] == '\\') {
+ instancedir[len-1] = '\0';
+ }
+
+ /* get the server root from the path */
+ val = strrchr(instancedir, '/');
+ if (!val) {
+ val = strrchr(instancedir, '\\');
+ }
+ if (val) {
+ val++;
+ *val = '\0';
+ }
+
+ strcpy(netsite_root, instancedir);
+ slapi_ch_free_string(&instancedir);
+ filename = PR_smprintf("%s%s", netsite_root, client_auth_config_file);
+
+ err = ldaputil_init (filename, "", netsite_root, "slapd", NULL);
+ if (err != LDAPU_SUCCESS) {
+ LDAPDebug (LDAP_DEBUG_TRACE, "ldaputil_init(%s,...) %i\n",
+ filename, err, 0);
+ } else {
+ LDAPUVTable_t vtable = {
+ NULL /* ssl_init */,
+ NULL /* set_option */,
+ NULL /* simple_bind_s */,
+ NULL /* unbind */,
+ slapu_search_s,
+ slapu_count_entries,
+ slapu_first_entry,
+ slapu_next_entry,
+ slapu_msgfree,
+ slapu_get_dn,
+ slapu_memfree,
+ slapu_first_attribute,
+ slapu_next_attribute,
+ slapu_ber_free,
+ NULL /* get_values */,
+ NULL /* value_free */,
+ slapu_get_values_len,
+ slapu_value_free_len};
+ ldapu_VTable_set (&vtable);
+ }
+ PR_smprintf_free (filename);
+ /* why do we define these strings if we never use them? */
+ if (ldapu_strings != NULL);
+
+ /* Generate a component id for cert-based authentication */
+ generate_id();
+}
+
+#include <ssl.h>
+#include "slapi-plugin.h" /* SLAPI_BERVAL_EQ */
+#include "slapi-private.h" /* COMPONENT_CERT_AUTH */
+
+static Slapi_ComponentId * auth_component_id=NULL;
+
+static void generate_id()
+{
+ if (auth_component_id == NULL ) {
+ auth_component_id=generate_componentid (NULL /* Not a plugin */ , COMPONENT_CERT_AUTH);
+ }
+}
+
+static Slapi_ComponentId * auth_get_component_id() {
+ return auth_component_id;
+}
+
+
+static char*
+subject_of (CERTCertificate* cert)
+{
+ char* dn = NULL;
+ if (cert != NULL) {
+ int err = ldapu_get_cert_subject_dn (cert, &dn);
+ if (err != LDAPU_SUCCESS) {
+ LDAPDebug (LDAP_DEBUG_ANY, "ldapu_get_cert_subject_dn(%p) %i (%s)\n",
+ (void*)cert, err, ldapu_err2string (err));
+ }
+ }
+ return dn;
+}
+
+static char*
+issuer_of (CERTCertificate* cert)
+{
+ char* dn = NULL;
+ if (cert != NULL) {
+ int err = ldapu_get_cert_issuer_dn (cert, &dn);
+ if (err != LDAPU_SUCCESS) {
+ LDAPDebug (LDAP_DEBUG_ANY, "ldapu_get_cert_issuer_dn(%p) %i (%s)\n",
+ (void*)cert, err, ldapu_err2string (err));
+ }
+ }
+ return dn;
+}
+
+/*
+ * Log a certificate that was rejected because the client didn't
+ * authenticate it.
+ *
+ * Note: handle_bad_certificate() is called via slapd_ssl_badCertHook().
+ * A Connection * is passed in client data. That connection must have its
+ * c_mutex locked.
+ */
+int
+handle_bad_certificate (void* clientData, PRFileDesc *prfd)
+{
+ char sbuf[ BUFSIZ ], ibuf[ BUFSIZ ];
+ Connection* conn = (Connection*) clientData;
+ CERTCertificate* clientCert = slapd_ssl_peerCertificate (prfd);
+
+ PRErrorCode errorCode = PR_GetError();
+ char* subject = subject_of (clientCert);
+ char* issuer = issuer_of (clientCert);
+ slapi_log_access( LDAP_DEBUG_STATS,
+ "conn=%d " SLAPI_COMPONENT_NAME_NSPR " error %i (%s); unauthenticated client %s; issuer %s\n",
+ conn->c_connid, errorCode, slapd_pr_strerror(errorCode),
+ subject ? escape_string( subject, sbuf ) : "NULL",
+ issuer ? escape_string( issuer, ibuf ) : "NULL" );
+ if (issuer) free (issuer);
+ if (subject) free (subject);
+ if (clientCert) CERT_DestroyCertificate (clientCert);
+ return -1; /* non-zero means reject this certificate */
+}
+
+
+/*
+ * Get an identity from the client's certificate (if any was sent).
+ *
+ * Note: handle_handshake_done() is called via slapd_ssl_handshakeCallback().
+ * A Connection * is passed in client data. That connection must have its
+ * c_mutex locked.
+ */
+void
+handle_handshake_done (PRFileDesc *prfd, void* clientData)
+{
+ Connection* conn = (Connection*) clientData;
+ CERTCertificate* clientCert = slapd_ssl_peerCertificate(prfd);
+
+ char* clientDN = NULL;
+ int keySize = 0;
+ char* cipher = NULL;
+ char* extraErrorMsg = "";
+ SSLChannelInfo channelInfo;
+ SSLCipherSuiteInfo cipherInfo;
+
+ if ( (slapd_ssl_getChannelInfo (prfd, &channelInfo, sizeof(channelInfo))) != SECSuccess ) {
+ PRErrorCode errorCode = PR_GetError();
+ slapi_log_access (LDAP_DEBUG_STATS,
+ "conn=%d SSL failed to obtain channel info; "
+ SLAPI_COMPONENT_NAME_NSPR " error %i (%s)\n",
+ conn->c_connid, errorCode, slapd_pr_strerror(errorCode));
+ return;
+ }
+ if ( (slapd_ssl_getCipherSuiteInfo (channelInfo.cipherSuite, &cipherInfo, sizeof(cipherInfo)) )
+ != SECSuccess) {
+ PRErrorCode errorCode = PR_GetError();
+ slapi_log_access (LDAP_DEBUG_STATS,
+ "conn=%d SSL failed to obtain cipher info; ",
+ SLAPI_COMPONENT_NAME_NSPR " error %i (%s)\n",
+ conn->c_connid, errorCode, slapd_pr_strerror(errorCode));
+ return;
+ }
+
+ keySize = cipherInfo.effectiveKeyBits;
+ cipher = slapi_ch_strdup(cipherInfo.symCipherName);
+
+ /* If inside an Start TLS operation, perform the privacy level discovery
+ * and if the security degree achieved after the handshake is not reckoned
+ * to be enough, close the SSL connection. */
+ if ( conn->c_flags & CONN_FLAG_START_TLS ) {
+ if ( cipherInfo.symKeyBits == 0 ) {
+ start_tls_graceful_closure( conn, NULL, 1 );
+ slapi_ch_free((void **)&cipher);
+ return ;
+ }
+ }
+
+ if (config_get_SSLclientAuth() == SLAPD_SSLCLIENTAUTH_OFF ) {
+ slapi_log_access (LDAP_DEBUG_STATS, "conn=%d SSL %i-bit %s\n",
+ conn->c_connid, keySize, cipher ? cipher : "NULL" );
+ slapi_ch_free((void **)&cipher);
+ return;
+ }
+ if (clientCert == NULL) {
+ slapi_log_access (LDAP_DEBUG_STATS, "conn=%d SSL %i-bit %s\n",
+ conn->c_connid, keySize, cipher ? cipher : "NULL" );
+ } else {
+ char* subject = subject_of (clientCert);
+ {
+ char* issuer = issuer_of (clientCert);
+ char sbuf[ BUFSIZ ], ibuf[ BUFSIZ ];
+ slapi_log_access( LDAP_DEBUG_STATS,
+ "conn=%d SSL %i-bit %s; client %s; issuer %s\n",
+ conn->c_connid, keySize, cipher ? cipher : "NULL",
+ subject ? escape_string( subject, sbuf ) : "NULL",
+ issuer ? escape_string( issuer, ibuf ) : "NULL");
+ if (issuer) free (issuer);
+ }
+ slapi_dn_normalize (subject);
+ {
+ LDAPMessage* chain = NULL;
+ char *basedn = config_get_basedn();
+ int err;
+
+ err = ldapu_cert_to_ldap_entry
+ (clientCert, internal_ld, basedn?basedn:""/*baseDN*/, &chain);
+ if (err == LDAPU_SUCCESS && chain) {
+ LDAPMessage* entry = slapu_first_entry (internal_ld, chain);
+ if (entry) {
+ clientDN = slapu_get_dn (internal_ld, entry);
+ if (clientDN) slapi_dn_normalize (clientDN);
+ } else {
+
+ extraErrorMsg = "no entry";
+ LDAPDebug (LDAP_DEBUG_TRACE, "<= ldapu_cert_to_ldap_entry() %s\n",
+ extraErrorMsg, 0, 0);
+ }
+ } else {
+ extraErrorMsg = ldapu_err2string(err);
+ LDAPDebug (LDAP_DEBUG_TRACE, "<= ldapu_cert_to_ldap_entry() %i (%s)%s\n",
+ err, extraErrorMsg, chain ? "" : " NULL");
+ }
+ slapi_ch_free((void**)&basedn);
+ slapu_msgfree (internal_ld, chain);
+ }
+ if (subject) free (subject);
+ }
+
+ if (clientDN != NULL) {
+ char ebuf[ BUFSIZ ];
+ slapi_log_access (LDAP_DEBUG_STATS, "conn=%d SSL client bound as %s\n",
+ conn->c_connid, escape_string( clientDN, ebuf ));
+ } else if (clientCert != NULL) {
+ slapi_log_access (LDAP_DEBUG_STATS,
+ "conn=%d SSL failed to map client certificate to LDAP DN (%s)\n",
+ conn->c_connid, extraErrorMsg );
+ }
+
+ /*
+ * Associate the new credentials with the connection. Note that
+ * clientDN and clientCert may be NULL.
+ */
+ bind_credentials_set( conn, SLAPD_AUTH_SSL, clientDN,
+ SLAPD_AUTH_SSL, clientDN, clientCert , NULL);
+
+ slapi_ch_free((void **)&cipher);
+ /* clientDN and clientCert will be freed later */
+}
diff --git a/ldap/servers/slapd/auth.h b/ldap/servers/slapd/auth.h
new file mode 100644
index 00000000..044b95e4
--- /dev/null
+++ b/ldap/servers/slapd/auth.h
@@ -0,0 +1,15 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+#ifndef _SLDAPD_AUTH_H_
+#define _SLDAPD_AUTH_H_
+
+#include <prio.h>
+
+void client_auth_init();
+void handle_handshake_done (PRFileDesc *prfd, void* clientData);
+int handle_bad_certificate (void* clientData, PRFileDesc *prfd);
+
+#endif
diff --git a/ldap/servers/slapd/ava.c b/ldap/servers/slapd/ava.c
new file mode 100644
index 00000000..9831315c
--- /dev/null
+++ b/ldap/servers/slapd/ava.c
@@ -0,0 +1,129 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+/* ava.c - routines for dealing with attribute value assertions */
+
+#include <stdio.h>
+#include <string.h>
+#include <sys/types.h>
+#ifndef _WIN32
+#include <sys/socket.h>
+#endif
+#include "slap.h"
+
+static void strcpy_special_undo();
+
+int
+get_ava(
+ BerElement *ber,
+ struct ava *ava
+)
+{
+ char *type;
+
+ if ( ber_scanf( ber, "{ao}", &type, &ava->ava_value )
+ == LBER_ERROR ) {
+ LDAPDebug( LDAP_DEBUG_ANY, " get_ava ber_scanf\n", 0, 0, 0 );
+ return( LDAP_PROTOCOL_ERROR );
+ }
+ ava->ava_type = slapi_attr_syntax_normalize(type);
+ free( type );
+
+ return( 0 );
+}
+
+void
+ava_done(
+ struct ava *ava
+)
+{
+ slapi_ch_free( (void**)&(ava->ava_type) );
+ slapi_ch_free( (void**)&(ava->ava_value.bv_val) );
+}
+
+int
+rdn2ava(
+ char *rdn,
+ struct ava *ava
+)
+{
+ char *s;
+
+ if ( (s = strchr( rdn, '=' )) == NULL ) {
+ return( -1 );
+ }
+ *s++ = '\0';
+
+ ava->ava_type = rdn;
+ strcpy_special_undo( s, s );
+ ava->ava_value.bv_val = s;
+ ava->ava_value.bv_len = strlen( s );
+
+ return( 0 );
+}
+
+/*
+** This function takes a quoted attribute value of the form "abc",
+** and strips off the enclosing quotes. It also deals with quoted
+** characters by removing the preceeding '\' character.
+**
+*/
+static void
+strcpy_special_undo( char *d, const char *s )
+{
+ const char *end = s + strlen(s);
+ for ( ; *s; s++ )
+ {
+ switch ( *s )
+ {
+ case '"':
+ break;
+ case '\\':
+ {
+ /*
+ * The '\' could be escaping a single character, ie \"
+ * or could be escaping a hex byte, ie \01
+ */
+ int singlecharacter= 1;
+ if ( s+2 < end )
+ {
+ int n = hexchar2int( s[1] );
+ if ( n >= 0 )
+ {
+ int n2 = hexchar2int( s[2] );
+ if ( n2 >= 0 )
+ {
+ singlecharacter= 0;
+ n = (n << 4) + n2;
+ if (n == 0)
+ {
+ /* don't change \00 */
+ *d++ = *++s;
+ *d++ = *++s;
+ }
+ else
+ {
+ /* change \xx to a single char */
+ ++s;
+ *(unsigned char*)(s+1) = n;
+ }
+ }
+ }
+ }
+ if(singlecharacter)
+ {
+ s++;
+ *d++ = *s;
+ }
+ break;
+ }
+ default:
+ *d++ = *s;
+ break;
+ }
+ }
+ *d = '\0';
+}
+
diff --git a/ldap/servers/slapd/back-ldbm/Makefile b/ldap/servers/slapd/back-ldbm/Makefile
new file mode 100644
index 00000000..c86ec0b3
--- /dev/null
+++ b/ldap/servers/slapd/back-ldbm/Makefile
@@ -0,0 +1,190 @@
+#
+# BEGIN COPYRIGHT BLOCK
+# Copyright 2001 Sun Microsystems, Inc.
+# Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+# All rights reserved.
+# END COPYRIGHT BLOCK
+#
+#
+# GNU Makefile for Directory Server libback-ldbm
+#
+LDAP_SRC = ../../..
+MCOM_ROOT = ../../../../..
+
+NOSTDCLEAN=true # don't let nsconfig.mk define target clean
+NOSTDSTRIP=true # don't let nsconfig.mk define target strip
+NSPR20=true # probably should be defined somewhere else (not sure where)
+
+OBJDEST = $(OBJDIR)/lib/libback-ldbm
+
+ifndef INSTDIR
+INSTDIR = /netscape/server4/
+endif
+
+include $(MCOM_ROOT)/ldapserver/nsconfig.mk
+include $(LDAP_SRC)/nsldap.mk
+ifndef LDAP_USE_OLD_DB
+include $(MCOM_ROOT)/ldapserver/ns_usedb.mk
+INCLUDES+=-I$(DB_INCLUDE)
+else
+CFLAGS+=-DLDAP_USE_DB185
+endif
+include $(LDAP_SRC)/nsdeps.mk
+
+CFLAGS+=$(SLCFLAGS)
+
+INCLUDES += -I$(LDAP_SRC)/servers/slapd
+
+LIBBACK_LDBM_OBJS= idl.o idl_shim.o idl_new.o idl_common.o cache.o dn2entry.o \
+ id2entry.o index.o haschildren.o nextid.o init.o \
+ filterindex.o close.o backentry.o monitor.o seq.o start.o \
+ rmdb.o ldif2ldbm.o dblayer.o findentry.o archive.o \
+ sort.o dbsize.o dbtest.o vlv.o vlv_key.o \
+ vlv_srch.o matchrule.o entrystore.o parents.o misc.o \
+ upgrade.o dbversion.o cleanup.o uniqueid2entry.o \
+ perfctrs.o instance.o import-threads.o import.o import-merge.o \
+ ldbm_config.o ldbm_instance_config.o ldbm_index_config.o ldbm_attrcrypt_config.o \
+ ldbm_attr.o \
+ ldbm_abandon.o \
+ ldbm_compare.o \
+ ldbm_add.o \
+ ldbm_search.o \
+ ldbm_modify.o \
+ ldbm_modrdn.o \
+ ldbm_delete.o \
+ ldbm_bind.o \
+ ldbm_unbind.o \
+ ancestorid.o \
+ ldbm_attrcrypt.o
+
+OBJS = $(addprefix $(OBJDEST)/, $(LIBBACK_LDBM_OBJS))
+
+ifeq ($(ARCH), WINNT)
+LIBBACK_LDBM_DLL_OBJ = $(addprefix $(OBJDEST)/, dllmain.o)
+endif
+
+LIBBACK_LDBM= $(addprefix $(LDAP_LIBBACK_LDBM_DLLDIR)/, $(LIBBACK_LDBM_DLL).$(DLL_SUFFIX))
+
+ifeq ($(ARCH), WINNT)
+EXTRA_LIBS_DEP += \
+ $(LDAPSDK_DEP) \
+ $(LDAP_LIBLDIF_DEP) \
+ $(LDAP_LIBAVL_DEP)
+EXTRA_LIBS += \
+ $(NSPRLINK) \
+ $(LDAP_SDK_LIBLDAP_DLL) \
+ $(LDAP_LIBLDIF) \
+ $(LDAP_LIBAVL)
+CFLAGS+= /WX
+endif
+
+ifeq ($(ARCH), AIX)
+EXTRA_LIBS_DEP += \
+ $(LDAP_SDK_LIBLDAP_DLL_DEP) \
+ $(LDAP_LIBLDIF_DEP) \
+ $(LDAP_LIBAVL_DEP)
+EXTRA_LIBS += \
+ $(NSPRLINK) \
+ $(LDAP_SDK_LIBLDAP_DLL) \
+ $(LDAP_LIBLDIF) \
+ $(LDAP_LIBAVL) \
+ $(DLL_EXTRA_LIBS)
+LD=ld
+endif
+
+ifeq ($(ARCH), SOLARIS)
+EXTRA_LIBS_DEP += \
+ $(LDAPSDK_DEP) \
+ $(LDAP_LIBLDIF_DEP) \
+ $(LDAP_LIBAVL_DEP)
+EXTRA_LIBS += \
+ $(LDAPLINK) $(SECURITYLINK) $(NSPRLINK) \
+ $(LDAP_LIBLDIF) \
+ $(LDAP_LIBAVL) \
+ $(DLL_EXTRA_LIBS) -lc
+LINK_DLL += -z defs
+endif
+
+ifeq ($(ARCH), HPUX)
+EXTRA_LIBS_DEP += \
+ $(LDAPSDK_DEP) \
+ $(LDAP_LIBLDIF_DEP) \
+ $(LDAP_LIBAVL_DEP)
+EXTRA_LIBS += \
+ $(LDAPLINK) $(SECURITYLINK) $(NSPRLINK) \
+ $(LDAP_LIBLDIF) \
+ $(LDAP_LIBAVL) \
+ $(DLL_EXTRA_LIBS) -lc
+endif
+
+ifeq ($(ARCH), WINNT)
+DLL_LDFLAGS += -def:"./libback-ldbm.def"
+IMPLIB= /IMPLIB:$(LDAP_LIBBACK_LDBM_LIBDIR)/$(LIBBACK_LDBM_DLL).lib
+MAPFILE= /MAP:$(LDAP_LIBBACK_LDBM_LIBDIR)/$(LIBBACK_LDBM_DLL).map
+endif # WINNT
+
+ifeq ($(ARCH), UnixWare)
+EXTRA_LIBS_DEP += $(LDAP_LIBAVL_DEP)
+EXTRA_LIBS += $(LDAP_LIBAVL)
+endif # UnixWare
+
+ifeq ($(ARCH), Linux)
+EXTRA_LIBS_DEP += $(LDAP_LIBLDBM_DEP) $(LDAP_LIBAVL_DEP) $(LDAP_LIBLDIF_DEP)
+EXTRA_LIBS += $(LDAP_LIBLDBM) $(LDAP_LIBAVL) $(LDAP_LIBLDIF)
+EXTRA_LIBS += $(DLL_EXTRA_LIBS)
+endif # Linux
+
+EXTRA_LIBS_DEP += $(DB_LIB_DEP)
+
+clientSDK:
+
+all: $(OBJDEST) $(LDAP_LIBBACK_LDBM_LIBDIR) $(LDAP_LIBBACK_LDBM_DLLDIR) \
+ $(BUILD_DEP) $(LIBBACK_LDBM)
+ifeq ($(ARCH), WINNT)
+ cd ntdbperfdll; $(MAKE) $(MFLAGS) all
+endif
+
+dummy:
+ -@echo LDAP_LIBDIR = $(LDAP_LIBDIR)
+ -@echo LDAP_LIBBACK_LDBM_LIBDIR = $(LDAP_LIBBACK_LDBM_LIBDIR)
+ -@echo LIB_RELDIR = $(LIB_RELDIR)
+ -@echo LDAP_LIBBACK_LDBM_DLLDIR = $(LDAP_LIBBACK_LDBM_DLLDIR)
+ -@echo LDAP_LIBBACK_LDBM = $(LDAP_LIBBACK_LDBM)
+ -@echo LIBBACK_LDBM = $(LIBBACK_LDBM)
+ abort
+
+$(LIBBACK_LDBM): $(OBJS) $(LIBBACK_LDBM_DLL_OBJ) $(EXTRA_LIBS_DEP) $(LIBSLAPD_DEP)
+ $(LINK_DLL) $(IMPLIB) $(MAPFILE) $(LIBBACK_LDBM_DLL_OBJ) $(EXTRA_LIBS) $(DB_LIB) $(LIBSLAPD)
+
+veryclean: clean
+
+clean:
+ $(RM) $(OBJS)
+ifeq ($(ARCH), WINNT)
+ $(RM) $(LIBBACK_LDBM_DLL_OBJ)
+endif
+ $(RM) $(LIBBACK_LDBM)
+
+$(OBJDEST) $(LIBBACK_LDBM_LIBDIR):
+ $(MKDIR) $@
+
+ifeq ($(ARCH), AIX)
+ifeq ($(DEBUG), optimize)
+
+# For some reason compiling ldif2ldbm.c with the -O flag on AIX causes
+# the new import code to hang. For now we will avoid the -O flag.
+
+TEMP_CFLAGS = $(subst -O,,$(CFLAGS))
+
+$(OBJDEST)/ldif2ldbm.o: ldif2ldbm.c
+ $(CC) -o $(OBJDEST)/ldif2ldbm.o -c $(TEMP_CFLAGS) $(MCC_INCLUDE) ldif2ldbm.c
+endif
+endif
+
+# Target to push the built binary to an installed server
+LDBM_PUSH = $(addprefix $(INSTDIR)/, lib/libback-ldbm.dll)
+push: $(LDBM_PUSH)
+
+$(LDBM_PUSH): $(LIBBACK_LDBM)
+ cp $(LIBBACK_LDBM) $(LDBM_PUSH)
+
diff --git a/ldap/servers/slapd/back-ldbm/ancestorid.c b/ldap/servers/slapd/back-ldbm/ancestorid.c
new file mode 100644
index 00000000..0ec76a17
--- /dev/null
+++ b/ldap/servers/slapd/back-ldbm/ancestorid.c
@@ -0,0 +1,747 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+
+#include "back-ldbm.h"
+
+static char *sourcefile = "ancestorid";
+
+/* Start of definitions for a simple cache using a hash table */
+
+typedef struct id2idl {
+ ID keyid;
+ IDList *idl;
+ struct id2idl *next;
+} id2idl;
+
+static void id2idl_free(id2idl **ididl);
+static int id2idl_same_key(const void *ididl, const void *k);
+
+typedef Hashtable id2idl_hash;
+
+#define id2idl_new_hash(size) new_hash(size,HASHLOC(id2idl,next),NULL,id2idl_same_key)
+#define id2idl_hash_lookup(ht,key,he) find_hash(ht,key,sizeof(ID),(void**)(he))
+#define id2idl_hash_add(ht,key,he,alt) add_hash(ht,key,sizeof(ID),he,(void**)(alt))
+#define id2idl_hash_remove(ht,key) remove_hash(ht,key,sizeof(ID))
+
+static void id2idl_hash_destroy(id2idl_hash *ht);
+
+/* End of definitions for a simple cache using a hash table */
+
+static int ldbm_parentid(backend *be, DB_TXN *txn, ID id, ID *ppid);
+static int check_cache(id2idl_hash *ht);
+static IDList *idl_union_allids(backend *be, struct attrinfo *ai, IDList *a, IDList *b);
+
+static int ldbm_get_nonleaf_ids(backend *be, DB_TXN *txn, IDList **idl)
+{
+ int ret = 0;
+ DB *db = NULL;
+ DBC *dbc = NULL;
+ DBT key = {0};
+ DBT data = {0};
+ struct attrinfo *ai = NULL;
+ IDList *nodes = NULL;
+ ID id;
+
+ /* Open the parentid index */
+ ainfo_get( be, "parentid", &ai );
+
+ /* Open the parentid index file */
+ ret = dblayer_get_index_file(be, ai, &db, DBOPEN_CREATE);
+ if (ret != 0) {
+ ldbm_nasty(sourcefile,13010,ret);
+ goto out;
+ }
+
+ /* Get a cursor so we can walk through the parentid */
+ ret = db->cursor(db,txn,&dbc,0);
+ if (ret != 0 ) {
+ ldbm_nasty(sourcefile,13020,ret);
+ goto out;
+ }
+
+ /* For each key which is an equality key */
+ do {
+ ret = dbc->c_get(dbc,&key,&data,DB_NEXT_NODUP);
+ if ((ret == 0) && (*(char*)key.data == EQ_PREFIX)) {
+ id = (ID) strtoul((char*)key.data+1, NULL, 10);
+ idl_insert(&nodes, id);
+ }
+ } while (ret == 0);
+
+ /* Check for success */
+ if (ret == DB_NOTFOUND) ret = 0;
+ if (ret != 0) ldbm_nasty(sourcefile,13030,ret);
+
+ out:
+ /* Close the cursor */
+ if (dbc != NULL) {
+ if (ret == 0) {
+ ret = dbc->c_close(dbc);
+ if (ret != 0) ldbm_nasty(sourcefile,13040,ret);
+ } else {
+ (void)dbc->c_close(dbc);
+ }
+ }
+
+ /* Release the parentid file */
+ if (db != NULL) {
+ dblayer_release_index_file( be, ai, db );
+ }
+
+ /* Return the idlist */
+ if (ret == 0) {
+ *idl = nodes;
+ LDAPDebug(LDAP_DEBUG_TRACE, "found %lu nodes for ancestorid\n",
+ (u_long)IDL_NIDS(nodes), 0, 0);
+ } else {
+ idl_free(nodes);
+ *idl = NULL;
+ }
+
+ return ret;
+}
+
+/*
+ * XXX: This function creates ancestorid index, which is a sort of hack.
+ * This function handles idl directly,
+ * which should have been implemented in the idl file(s).
+ * When the idl code would be updated in the future,
+ * this function may also get affected.
+ * (see also bug#: 605535)
+ *
+ * Construct the ancestorid index. Requirements:
+ * - The backend is read only.
+ * - The parentid index is accurate.
+ * - Non-leaf entries have IDs less than their descendants
+ * (guaranteed after a database import but not after a subtree move)
+ *
+ */
+int ldbm_ancestorid_create_index(backend *be)
+{
+ int ret = 0;
+ DB *db_pid = NULL;
+ DB *db_aid = NULL;
+ DBT key = {0};
+ DB_TXN *txn = NULL;
+ struct attrinfo *ai_pid = NULL;
+ struct attrinfo *ai_aid = NULL;
+ char keybuf[24];
+ IDList *nodes = NULL;
+ IDList *children = NULL, *descendants = NULL;
+ NIDS nids;
+ ID id, parentid;
+ id2idl_hash *ht = NULL;
+ id2idl *ididl;
+
+ /*
+ * We need to iterate depth-first through the non-leaf nodes
+ * in the tree amassing an idlist of descendant ids for each node.
+ * We would prefer to go through the parentid keys just once from
+ * highest id to lowest id but the btree ordering is by string
+ * rather than number. So we go through the parentid keys in btree
+ * order first of all to create an idlist of all the non-leaf nodes.
+ * Then we can use the idlist to iterate through parentid in the
+ * correct order.
+ */
+
+ LDAPDebug(LDAP_DEBUG_TRACE, "Creating ancestorid index\n", 0,0,0);
+
+ /* Get the non-leaf node IDs */
+ ret = ldbm_get_nonleaf_ids(be, txn, &nodes);
+ if (ret != 0) return ret;
+
+ /* Get the ancestorid index */
+ ainfo_get(be, "ancestorid", &ai_aid);
+
+ /* Prevent any other use of the index */
+ ai_aid->ai_indexmask |= INDEX_OFFLINE;
+
+ /* Open the ancestorid index file */
+ ret = dblayer_get_index_file(be, ai_aid, &db_aid, DBOPEN_CREATE);
+ if (ret != 0) {
+ ldbm_nasty(sourcefile,13050,ret);
+ goto out;
+ }
+
+ /* Maybe nothing to do */
+ if (nodes == NULL || nodes->b_nids == 0) {
+ LDAPDebug(LDAP_DEBUG_ANY, "Nothing to do to build ancestorid index\n",
+ 0, 0, 0);
+ goto out;
+ }
+
+ /* Create an ancestorid cache */
+ ht = id2idl_new_hash(nodes->b_nids);
+
+ /* Get the parentid index */
+ ainfo_get( be, "parentid", &ai_pid );
+
+ /* Open the parentid index file */
+ ret = dblayer_get_index_file(be, ai_pid, &db_pid, DBOPEN_CREATE);
+ if (ret != 0) {
+ ldbm_nasty(sourcefile,13060,ret);
+ goto out;
+ }
+
+ /* Initialize key DBT */
+ key.data = keybuf;
+ key.ulen = sizeof(keybuf);
+ key.flags = DB_DBT_USERMEM;
+
+ /* Iterate from highest to lowest ID */
+ nids = nodes->b_nids;
+ do {
+
+ nids--;
+ id = nodes->b_ids[nids];
+
+ /* Get immediate children from parentid index */
+ key.size = PR_snprintf(key.data, key.ulen, "%c%lu",
+ EQ_PREFIX, (u_long)id);
+ key.size++; /* include the null terminator */
+ ret = NEW_IDL_NO_ALLID;
+ children = idl_fetch(be, db_pid, &key, txn, ai_pid, &ret);
+ if (ret != 0) {
+ ldbm_nasty(sourcefile,13070,ret);
+ break;
+ }
+
+ /* Insert into ancestorid for this node */
+ if (id2idl_hash_lookup(ht, &id, &ididl)) {
+ descendants = idl_union_allids(be, ai_aid, ididl->idl, children);
+ idl_free(children);
+ if (id2idl_hash_remove(ht, &id) == 0) {
+ LDAPDebug(LDAP_DEBUG_ANY, "ancestorid hash_remove failed\n", 0,0,0);
+ } else {
+ id2idl_free(&ididl);
+ }
+ } else {
+ descendants = children;
+ }
+ ret = idl_store_block(be, db_aid, &key, descendants, txn, ai_aid);
+ if (ret != 0) break;
+
+ /* Get parentid for this entry */
+ ret = ldbm_parentid(be, txn, id, &parentid);
+ if (ret != 0) {
+ idl_free(descendants);
+ break;
+ }
+
+ /* A suffix entry does not have a parent */
+ if (parentid == NOID) {
+ idl_free(descendants);
+ continue;
+ }
+
+ /* Insert into ancestorid for this node's parent */
+ if (id2idl_hash_lookup(ht, &parentid, &ididl)) {
+ IDList *idl = idl_union_allids(be, ai_aid, ididl->idl, descendants);
+ idl_free(descendants);
+ idl_free(ididl->idl);
+ ididl->idl = idl;
+ } else {
+ ididl = (id2idl*)slapi_ch_calloc(1,sizeof(id2idl));
+ ididl->keyid = parentid;
+ ididl->idl = descendants;
+ if (id2idl_hash_add(ht, &parentid, ididl, NULL) == 0) {
+ LDAPDebug(LDAP_DEBUG_ANY, "ancestorid hash_add failed\n", 0,0,0);
+ }
+ }
+
+ } while (nids > 0);
+
+ if (ret != 0) {
+ goto out;
+ }
+
+ /* We're expecting the cache to be empty */
+ ret = check_cache(ht);
+
+ out:
+ if (ret == 0) {
+ LDAPDebug(LDAP_DEBUG_TRACE, "Created ancestorid index\n", 0,0,0);
+ } else {
+ LDAPDebug(LDAP_DEBUG_ANY, "Failed to create ancestorid index\n", 0,0,0);
+ }
+
+ /* Destroy the cache */
+ id2idl_hash_destroy(ht);
+
+ /* Free any leftover idlists */
+ idl_free(nodes);
+
+ /* Release the parentid file */
+ if (db_pid != NULL) {
+ dblayer_release_index_file( be, ai_pid, db_pid );
+ }
+
+ /* Release the ancestorid file */
+ if (db_aid != NULL) {
+ dblayer_release_index_file( be, ai_aid, db_aid );
+ }
+
+ /* Enable the index */
+ if (ret == 0) {
+ ai_aid->ai_indexmask &= ~INDEX_OFFLINE;
+ }
+
+ return ret;
+}
+
+/*
+ * Get parentid of an id by reading the operational attr from id2entry.
+ */
+static int ldbm_parentid(backend *be, DB_TXN *txn, ID id, ID *ppid)
+{
+ int ret = 0;
+ DB *db = NULL;
+ DBT key = {0};
+ DBT data = {0};
+ ID stored_id;
+ char *p;
+
+ /* Open the id2entry file */
+ ret = dblayer_get_id2entry(be, &db);
+ if (ret != 0) {
+ ldbm_nasty(sourcefile,13100,ret);
+ goto out;
+ }
+
+ /* Initialize key and data DBTs */
+ id_internal_to_stored(id, (char *)&stored_id);
+ key.data = (char *)&stored_id;
+ key.size = sizeof(stored_id);
+ key.flags = DB_DBT_USERMEM;
+ data.flags = DB_DBT_MALLOC;
+
+ /* Read id2entry */
+ ret = db->get(db, txn, &key, &data, 0);
+ if (ret != 0) {
+ ldbm_nasty(sourcefile,13110,ret);
+ goto out;
+ }
+
+ /* Extract the parentid value */
+#define PARENTID_STR "\nparentid:"
+ p = strstr(data.data, PARENTID_STR);
+ if (p == NULL) {
+ *ppid = NOID;
+ goto out;
+ }
+ *ppid = strtoul(p + strlen(PARENTID_STR), NULL, 10);
+
+ out:
+ /* Free the entry value */
+ if (data.data != NULL) free(data.data);
+
+ /* Release the id2entry file */
+ if (db != NULL) {
+ dblayer_release_id2entry(be, db);
+ }
+ return ret;
+}
+
+static void id2idl_free(id2idl **ididl)
+{
+ idl_free((*ididl)->idl);
+ slapi_ch_free((void**)ididl);
+}
+
+static int id2idl_same_key(const void *ididl, const void *k)
+{
+ return (((id2idl *)ididl)->keyid == *(ID *)k);
+}
+
+static int check_cache(id2idl_hash *ht)
+{
+ id2idl *e;
+ u_long i, found = 0;
+ int ret = 0;
+
+ if (ht == NULL) return 0;
+
+ for (i = 0; i < ht->size; i++) {
+ e = (id2idl *)ht->slot[i];
+ while (e) {
+ found++;
+ e = e->next;
+ }
+ }
+
+ if (found > 0) {
+ LDAPDebug(LDAP_DEBUG_ANY, "ERROR: parentid index is not complete (%lu extra keys in ancestorid cache)\n", found,0,0);
+ ret = -1;
+ }
+
+ return ret;
+}
+
+static void id2idl_hash_destroy(id2idl_hash *ht)
+{
+ u_long i;
+ id2idl *e, *next;
+
+ if (ht == NULL) return;
+
+ for (i = 0; i < ht->size; i++) {
+ e = (id2idl *)ht->slot[i];
+ while (e) {
+ next = e->next;
+ id2idl_free(&e);
+ e = next;
+ }
+ }
+ slapi_ch_free((void **)&ht);
+}
+
+/*
+ * idl_union_allids - return a union b
+ * takes attr index allids setting into account
+ */
+static IDList *idl_union_allids(backend *be, struct attrinfo *ai, IDList *a, IDList *b)
+{
+ if (!idl_get_idl_new()) {
+ if (a != NULL && b != NULL) {
+ if (ALLIDS( a ) || ALLIDS( b ) ||
+ (IDL_NIDS(a) + IDL_NIDS(b) > idl_get_allidslimit(ai))) {
+ return( idl_allids( be ) );
+ }
+ }
+ }
+ return idl_union(be, a, b);
+}
+
+static int ancestorid_addordel(
+ backend *be,
+ DB* db,
+ ID node_id,
+ ID id,
+ DB_TXN *txn,
+ struct attrinfo *ai,
+ int flags,
+ int *allids
+)
+{
+ DBT key = {0};
+ char keybuf[24];
+ int ret = 0;
+
+ /* Initialize key DBT */
+ key.data = keybuf;
+ key.ulen = sizeof(keybuf);
+ key.flags = DB_DBT_USERMEM;
+ key.size = PR_snprintf(key.data, key.ulen, "%c%lu",
+ EQ_PREFIX, (u_long)node_id);
+ key.size++; /* include the null terminator */
+
+ if (flags & BE_INDEX_ADD) {
+#if 1
+ LDAPDebug(LDAP_DEBUG_TRACE, "insert ancestorid %lu:%lu\n",
+ (u_long)node_id, (u_long)id, 0);
+#endif
+ ret = idl_insert_key(be, db, &key, id, txn, ai, allids);
+ } else {
+#if 1
+ LDAPDebug(LDAP_DEBUG_TRACE, "delete ancestorid %lu:%lu\n",
+ (u_long)node_id, (u_long)id, 0);
+#endif
+ ret = idl_delete_key(be, db, &key, id, txn, ai);
+ }
+
+ if (ret != 0) {
+ ldbm_nasty(sourcefile,13120,ret);
+ }
+
+ return ret;
+}
+
+/*
+ * Update ancestorid index inserting or deleting depending on flags.
+ * The entry ids to be indexed are given by id (a base object)
+ * and optionally subtree_idl (descendants of the base object).
+ * The ancestorid keys to be updated are derived from nodes
+ * in the tree from low up to high. Whether the low and high nodes
+ * themselves are updated is given by include_low and include_high.
+ */
+static int ldbm_ancestorid_index_update(
+ backend *be,
+ const Slapi_DN *low,
+ const Slapi_DN *high,
+ int include_low,
+ int include_high,
+ ID id,
+ IDList *subtree_idl,
+ int flags, /* BE_INDEX_ADD, BE_INDEX_DEL */
+ back_txn *txn
+)
+{
+ DB *db = NULL;
+ int allids = IDL_INSERT_NORMAL;
+ Slapi_DN dn = {0};
+ Slapi_DN nextdn = {0};
+ struct attrinfo *ai = NULL;
+ struct berval ndnv;
+ ID node_id, sub_id;
+ IDList *idl;
+ idl_iterator iter;
+ int err = 0, ret = 0;
+ DB_TXN *db_txn = txn != NULL ? txn->back_txn_txn : NULL;
+
+ /* Open the ancestorid index */
+ ainfo_get(be, "ancestorid", &ai);
+ ret = dblayer_get_index_file(be, ai, &db, DBOPEN_CREATE);
+ if (ret != 0) {
+ ldbm_nasty(sourcefile,13130,ret);
+ goto out;
+ }
+
+ slapi_sdn_copy(low, &dn);
+
+ if (include_low == 0) {
+ if (slapi_sdn_compare(&dn, high) == 0) {
+ goto out;
+ }
+ /* Get the next highest DN */
+ slapi_sdn_get_parent(&dn, &nextdn);
+ slapi_sdn_copy(&nextdn, &dn);
+ }
+
+ /* Iterate up through the tree */
+ do {
+ if (slapi_sdn_isempty(&dn)) {
+ break;
+ }
+
+ /* Have we reached the high node? */
+ if (include_high == 0 && slapi_sdn_compare(&dn, high) == 0) {
+ break;
+ }
+
+ /* Get the id for that DN */
+ ndnv.bv_val = (void*)slapi_sdn_get_ndn(&dn);
+ ndnv.bv_len = slapi_sdn_get_ndn_len(&dn);
+ err = 0;
+ idl = index_read(be, "entrydn", indextype_EQUALITY, &ndnv, txn, &err);
+ if (idl == NULL) {
+ if (err != 0 && err != DB_NOTFOUND) {
+ ldbm_nasty(sourcefile,13140,ret);
+ ret = err;
+ }
+ break;
+ }
+ node_id = idl_firstid(idl);
+ idl_free(idl);
+
+ /* Update ancestorid for the base entry */
+ ret = ancestorid_addordel(be, db, node_id, id, db_txn, ai, flags, &allids);
+ if (ret != 0) break;
+
+ /*
+ * If this node was already allids then all higher nodes must already
+ * be at allids since the higher nodes must have a greater number
+ * of descendants. Therefore no point continuing.
+ */
+ if (allids == IDL_INSERT_ALLIDS) break;
+
+ /* Update ancestorid for any subtree entries */
+ if (subtree_idl != NULL && ((flags & BE_INDEX_ADD) || (!ALLIDS(subtree_idl)))) {
+ iter = idl_iterator_init(subtree_idl);
+ while ((sub_id = idl_iterator_dereference_increment(&iter, subtree_idl)) != NOID) {
+ ret = ancestorid_addordel(be, db, node_id, sub_id, db_txn, ai, flags, &allids);
+ if (ret != 0) break;
+ }
+ if (ret != 0) break;
+ }
+
+ /* Have we reached the high node? */
+ if (slapi_sdn_compare(&dn, high) == 0) {
+ break;
+ }
+
+ /* Get the next highest DN */
+ slapi_sdn_get_parent(&dn, &nextdn);
+ slapi_sdn_copy(&nextdn, &dn);
+
+ } while (ret == 0);
+
+ out:
+ slapi_sdn_done(&dn);
+ slapi_sdn_done(&nextdn);
+
+ /* Release the ancestorid file */
+ if (db != NULL) {
+ dblayer_release_index_file(be, ai, db);
+ }
+
+ return ret;
+}
+
+/*
+ * Update the ancestorid index for a single entry.
+ * This function depends on the integrity of the entrydn index.
+ */
+int ldbm_ancestorid_index_entry(
+ backend *be,
+ struct backentry *e,
+ int flags, /* BE_INDEX_ADD, BE_INDEX_DEL */
+ back_txn *txn
+)
+{
+ int ret = 0;
+
+ ret = ldbm_ancestorid_index_update(be,
+ slapi_entry_get_sdn_const(e->ep_entry),
+ slapi_be_getsuffix(be, 0),
+ 0, 1, e->ep_id, NULL, flags, txn);
+
+ return ret;
+}
+
+/*
+ * Returns <0, 0, >0 according to whether right is a suffix of left,
+ * neither is a suffix of the other, or left is a suffix of right.
+ * If common is non-null then the common suffix of left and right
+ * is returned in *common.
+ */
+int slapi_sdn_suffix_cmp(
+ const Slapi_DN *left,
+ const Slapi_DN *right,
+ Slapi_DN *common
+)
+{
+ char **rdns1, **rdns2;
+ int count1, count2, i, ret = 0;
+ size_t len = 0;
+ char *p, *ndnstr;
+
+ rdns1 = ldap_explode_dn(slapi_sdn_get_ndn(left), 0);
+ rdns2 = ldap_explode_dn(slapi_sdn_get_ndn(right), 0);
+
+ for(count1 = 0; rdns1[count1]!=NULL; count1++){
+ }
+ count1--;
+
+ for(count2 = 0; rdns2[count2]!=NULL; count2++){
+ }
+ count2--;
+
+ while (count1 >= 0 && count2 >= 0) {
+ if (strcmp(rdns1[count1], rdns2[count2]) != 0) break;
+ count1--;
+ count2--;
+ }
+
+ count1++;
+ count2++;
+
+ if (count1 == 0 && count2 == 0) {
+ /* equal */
+ ret = 0;
+ } else if (count1 == 0) {
+ /* left is suffix of right */
+ ret = 1;
+ } else if (count2 == 0) {
+ /* right is suffix of left */
+ ret = -1;
+ } else {
+ /* common prefix (possibly root), not left nor right */
+ ret = 0;
+ }
+
+ /* if caller does not want the common prefix then we're done */
+ if (common == NULL) goto out;
+
+ /* figure out how much space we need */
+ for (i = count1; rdns1[i] != NULL; i++) {
+ len += strlen(rdns1[i]) + 1;
+ }
+
+ /* write the string */
+ p = ndnstr = slapi_ch_calloc(len+1,sizeof(char));
+ for (i = count1; rdns1[i] != NULL; i++) {
+ sprintf(p, "%s%s", (p != ndnstr) ? "," : "", rdns1[i]);
+ p += strlen(p);
+ }
+
+ /* return the DN */
+ slapi_sdn_set_dn_passin(common, ndnstr);
+
+ LDAPDebug(LDAP_DEBUG_TRACE, "common suffix <%s>\n",
+ slapi_sdn_get_dn(common), 0, 0);
+
+ out:
+ ldap_value_free(rdns1);
+ ldap_value_free(rdns2);
+
+ LDAPDebug(LDAP_DEBUG_TRACE, "slapi_sdn_suffix_cmp(<%s>, <%s>) => %d\n",
+ slapi_sdn_get_dn(left), slapi_sdn_get_dn(right), ret);
+
+ return ret;
+}
+
+int ldbm_ancestorid_move_subtree(
+ backend *be,
+ const Slapi_DN *olddn,
+ const Slapi_DN *newdn,
+ ID id,
+ IDList *subtree_idl,
+ back_txn *txn
+)
+{
+ int ret = 0;
+ Slapi_DN commondn = {0};
+
+ /* Determine the common ancestor */
+ (void)slapi_sdn_suffix_cmp(olddn, newdn, &commondn);
+
+ /* Delete from old ancestors */
+ ret = ldbm_ancestorid_index_update(be,
+ olddn,
+ &commondn,
+ 0,
+ 0,
+ id,
+ subtree_idl,
+ BE_INDEX_DEL,
+ txn);
+ if (ret != 0) goto out;
+
+ /* Add to new ancestors */
+ ret = ldbm_ancestorid_index_update(be,
+ newdn,
+ &commondn,
+ 0,
+ 0,
+ id,
+ subtree_idl,
+ BE_INDEX_ADD,
+ txn);
+
+ out:
+ slapi_sdn_done(&commondn);
+ return ret;
+}
+
+int ldbm_ancestorid_read(
+ backend *be,
+ back_txn *txn,
+ ID id,
+ IDList **idl
+)
+{
+ int ret = 0;
+ struct berval bv;
+ char keybuf[24];
+
+ bv.bv_val = keybuf;
+ bv.bv_len = PR_snprintf(keybuf, sizeof(keybuf), "%lu", (u_long)id);
+
+ *idl = index_read(be, "ancestorid", indextype_EQUALITY, &bv, txn, &ret);
+
+ return ret;
+}
+
diff --git a/ldap/servers/slapd/back-ldbm/archive.c b/ldap/servers/slapd/back-ldbm/archive.c
new file mode 100644
index 00000000..2daee2c5
--- /dev/null
+++ b/ldap/servers/slapd/back-ldbm/archive.c
@@ -0,0 +1,332 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+/* archive.c - ldap ldbm back-end archive and restore entry points */
+
+#include "back-ldbm.h"
+
+int ldbm_back_archive2ldbm( Slapi_PBlock *pb )
+{
+ struct ldbminfo *li;
+ char *directory = NULL;
+ int return_value = -1;
+ int task_flags = 0;
+ int run_from_cmdline = 0;
+ Slapi_Task *task;
+ int is_old_to_new = 0;
+
+ slapi_pblock_get( pb, SLAPI_PLUGIN_PRIVATE, &li );
+ slapi_pblock_get( pb, SLAPI_SEQ_VAL, &directory );
+ slapi_pblock_get( pb, SLAPI_BACKEND_TASK, &task );
+ slapi_pblock_get( pb, SLAPI_TASK_FLAGS, &task_flags );
+ li->li_flags = run_from_cmdline = (task_flags & TASK_RUNNING_FROM_COMMANDLINE);
+
+ /* check the current idl format vs backup DB version */
+ if (idl_get_idl_new())
+ {
+ char dbversion[LDBM_VERSION_MAXBUF];
+ char dataversion[LDBM_VERSION_MAXBUF];
+ int value = 0;
+
+ if (dbversion_read(li, directory, dbversion, dataversion) != 0)
+ {
+ LDAPDebug(LDAP_DEBUG_ANY, "Warning: Unable to read dbversion "
+ "file in %s\n", directory, 0, 0);
+ }
+ value = lookup_dbversion(dbversion, DBVERSION_TYPE);
+ if (value & DBVERSION_OLD_IDL)
+ {
+ is_old_to_new = 1;
+ }
+ }
+
+ /* No ldbm be's exist until we process the config information. */
+ if (run_from_cmdline) {
+ mapping_tree_init();
+ ldbm_config_load_dse_info(li);
+ } else {
+ ldbm_instance *inst;
+ Object *inst_obj, *inst_obj2;
+
+ /* task does not support restore old idl onto new idl server */
+ if (is_old_to_new)
+ {
+ LDAPDebug(LDAP_DEBUG_ANY,
+ "backup has old idl format; "
+ "to restore old formated backup onto the new server, "
+ "please use command line utility \"bak2db\" .\n",
+ 0, 0, 0);
+ if (task)
+ {
+ slapi_task_log_notice(task,
+ "backup has old idl format; "
+ "to restore old formated backup onto the new server, "
+ "please use command line utility \"bak2db\" .\n");
+ }
+ return -1;
+ }
+ /* server is up -- mark all backends busy */
+ for (inst_obj = objset_first_obj(li->li_instance_set); inst_obj;
+ inst_obj = objset_next_obj(li->li_instance_set, inst_obj)) {
+ inst = (ldbm_instance *)object_get_data(inst_obj);
+
+ /* check if an import/restore is already ongoing... */
+ if (instance_set_busy(inst) != 0) {
+ LDAPDebug(LDAP_DEBUG_ANY,
+ "ldbm: '%s' is already in the middle of "
+ "another task and cannot be disturbed.\n",
+ inst->inst_name, 0, 0);
+ if (task) {
+ slapi_task_log_notice(task,
+ "Backend '%s' is already in the middle of "
+ "another task and cannot be disturbed.\n",
+ inst->inst_name);
+ }
+
+ /* painfully, we have to clear the BUSY flags on the
+ * backends we'd already marked...
+ */
+ for (inst_obj2 = objset_first_obj(li->li_instance_set);
+ inst_obj2 && (inst_obj2 != inst_obj);
+ inst_obj2 = objset_next_obj(li->li_instance_set,
+ inst_obj2)) {
+ inst = (ldbm_instance *)object_get_data(inst_obj2);
+ instance_set_not_busy(inst);
+ }
+ object_release(inst_obj2);
+ object_release(inst_obj);
+ return -1;
+ }
+ }
+
+ /* now take down ALL BACKENDS */
+ for (inst_obj = objset_first_obj(li->li_instance_set); inst_obj;
+ inst_obj = objset_next_obj(li->li_instance_set, inst_obj)) {
+ inst = (ldbm_instance *)object_get_data(inst_obj);
+ LDAPDebug(LDAP_DEBUG_ANY, "Bringing %s offline...\n",
+ inst->inst_name, 0, 0);
+ if (task) {
+ slapi_task_log_notice(task, "Bringing %s offline...",
+ inst->inst_name);
+ }
+ slapi_mtn_be_disable(inst->inst_be);
+ cache_clear(&inst->inst_cache);
+ }
+ /* now we know nobody's using any of the backend instances, so we
+ * can shutdown the dblayer -- this closes all instances too.
+ * Use DBLAYER_RESTORE_MODE to prevent loss of perfctr memory.
+ */
+ dblayer_close(li, DBLAYER_RESTORE_MODE);
+ }
+
+ /* tell the database to restore */
+ return_value = dblayer_restore(li, directory, task);
+ if (0 != return_value) {
+ LDAPDebug( LDAP_DEBUG_ANY,
+ "archive2db: Failed to read backup file set. "
+ "Either the directory specified doesn't exist, "
+ "or it exists but doesn't contain a valid backup set, "
+ "or file permissions prevent the server reading "
+ "the backup set. error=%d (%s)\n",
+ return_value, dblayer_strerror(return_value), 0 );
+ if (task) {
+ slapi_task_log_notice(task, "Failed to read the backup file set "
+ "from %s", directory);
+ }
+ }
+
+ if (run_from_cmdline)
+ {
+ if (is_old_to_new)
+ {
+ /* does not exist */
+ char *p;
+ char c;
+ char *bakup_dir = NULL;
+ int skipinit = SLAPI_UPGRADEDB_SKIPINIT;
+
+ p = strrchr(directory, '/');
+ if (NULL == p)
+ {
+ p = strrchr(directory, '\\');
+ }
+
+ if (NULL == p) /* never happen, I guess */
+ {
+ directory = ".";
+ c = '/';
+ }
+ else
+ {
+ c = *p;
+ *p = '\0';
+ }
+ bakup_dir = (char *)slapi_ch_malloc(strlen(directory) +
+ sizeof("tmp") + 13);
+ sprintf(bakup_dir, "%s%ctmp_%010d", directory, c, time(0));
+ LDAPDebug( LDAP_DEBUG_ANY,
+ "archive2db: backup dir: %s\n", bakup_dir, 0, 0);
+ *p = c;
+
+ slapi_pblock_set( pb, SLAPI_SEQ_VAL, bakup_dir );
+ slapi_pblock_set( pb, SLAPI_SEQ_TYPE, &skipinit );
+ return_value = ldbm_back_upgradedb( pb );
+ }
+ }
+ else
+ {
+ ldbm_instance *inst;
+ Object *inst_obj;
+ int ret;
+
+ if (0 != return_value) {
+ /* error case (607331)
+ * just to go back to the previous state if possible */
+ dblayer_start(li, DBLAYER_NORMAL_MODE);
+ }
+ /* bring all backends back online */
+ for (inst_obj = objset_first_obj(li->li_instance_set); inst_obj;
+ inst_obj = objset_next_obj(li->li_instance_set, inst_obj)) {
+ inst = (ldbm_instance *)object_get_data(inst_obj);
+ ret = dblayer_instance_start(inst->inst_be, DBLAYER_NORMAL_MODE);
+ if (ret != 0) {
+ LDAPDebug(LDAP_DEBUG_ANY,
+ "archive2db: Unable to restart '%s'\n",
+ inst->inst_name, 0, 0);
+ if (task) {
+ slapi_task_log_notice(task, "Unable to restart '%s'",
+ inst->inst_name);
+ }
+ } else {
+ slapi_mtn_be_enable(inst->inst_be);
+ instance_set_not_busy(inst);
+ }
+ }
+ }
+
+ return return_value;
+}
+
+int ldbm_back_ldbm2archive( Slapi_PBlock *pb )
+{
+ struct ldbminfo *li;
+ char *directory = NULL;
+ int return_value = -1;
+ int task_flags = 0;
+ int run_from_cmdline = 0;
+ Slapi_Task *task;
+
+ slapi_pblock_get( pb, SLAPI_PLUGIN_PRIVATE, &li );
+ slapi_pblock_get( pb, SLAPI_SEQ_VAL, &directory );
+ slapi_pblock_get( pb, SLAPI_TASK_FLAGS, &task_flags );
+ li->li_flags = run_from_cmdline = (task_flags & TASK_RUNNING_FROM_COMMANDLINE);
+
+ slapi_pblock_get( pb, SLAPI_BACKEND_TASK, &task );
+
+ /* No ldbm be's exist until we process the config information. */
+ if (run_from_cmdline) {
+ mapping_tree_init();
+ ldbm_config_load_dse_info(li);
+ }
+ /* to avoid conflict w/ import, do this check for commandline, as well */
+ {
+ Object *inst_obj, *inst_obj2;
+ ldbm_instance *inst = NULL;
+
+ /* server is up -- mark all backends busy */
+ for (inst_obj = objset_first_obj(li->li_instance_set); inst_obj;
+ inst_obj = objset_next_obj(li->li_instance_set, inst_obj)) {
+ inst = (ldbm_instance *)object_get_data(inst_obj);
+
+ /* check if an import/restore is already ongoing... */
+ if (instance_set_busy(inst) != 0 || dblayer_in_import(inst) != 0) {
+ LDAPDebug(LDAP_DEBUG_ANY,
+ "ldbm: '%s' is already in the middle of "
+ "another task and cannot be disturbed.\n",
+ inst->inst_name, 0, 0);
+ if (task) {
+ slapi_task_log_notice(task,
+ "Backend '%s' is already in the middle of "
+ "another task and cannot be disturbed.\n",
+ inst->inst_name);
+ }
+
+ /* painfully, we have to clear the BUSY flags on the
+ * backends we'd already marked...
+ */
+ for (inst_obj2 = objset_first_obj(li->li_instance_set);
+ inst_obj2 && (inst_obj2 != inst_obj);
+ inst_obj2 = objset_next_obj(li->li_instance_set,
+ inst_obj2)) {
+ inst = (ldbm_instance *)object_get_data(inst_obj2);
+ instance_set_not_busy(inst);
+ }
+ object_release(inst_obj2);
+ object_release(inst_obj);
+ return -1;
+ }
+ }
+ }
+
+ if ( !directory || !*directory ) {
+ LDAPDebug( LDAP_DEBUG_ANY, "db2archive: no archive name\n",
+ 0, 0, 0 );
+ return( -1 );
+ }
+ if (0 != MKDIR(directory,SLAPD_DEFAULT_DIR_MODE) && EEXIST != errno) {
+ char *msg = dblayer_strerror(errno);
+
+ LDAPDebug(LDAP_DEBUG_ANY,
+ "db2archive: mkdir(%s) failed; errno %i (%s)\n",
+ directory, errno, msg ? msg : "unknown");
+ if (task) {
+ slapi_task_log_notice(task,
+ "mkdir(%s) failed; errno %i (%s)",
+ directory, errno, msg ? msg : "unknown");
+ }
+ }
+
+ /* start the database code up, do not attempt to perform recovery */
+ if (run_from_cmdline &&
+ 0 != dblayer_start(li,DBLAYER_ARCHIVE_MODE|DBLAYER_CMDLINE_MODE)) {
+ LDAPDebug(LDAP_DEBUG_ANY, "db2archive: Failed to init database\n",
+ 0, 0, 0);
+ if (task) {
+ slapi_task_log_notice(task, "Failed to init database");
+ }
+ return( -1 );
+ }
+
+ /* tell it to archive */
+ return_value = dblayer_backup(li, directory, task);
+
+ /* close the database down again */
+ if (run_from_cmdline &&
+ 0 != dblayer_close(li,DBLAYER_ARCHIVE_MODE|DBLAYER_CMDLINE_MODE)) {
+ LDAPDebug(LDAP_DEBUG_ANY, "db2archive: Failed to close database\n",
+ 0, 0, 0);
+ if (task) {
+ slapi_task_log_notice(task, "Failed to close database");
+ }
+
+ /* The backup succeeded, so a failed close is not really a
+ total error... */
+ /*return( -1 );*/
+ }
+
+ if (! run_from_cmdline) {
+ ldbm_instance *inst;
+ Object *inst_obj;
+
+ /* none of these backends are busy anymore */
+ for (inst_obj = objset_first_obj(li->li_instance_set); inst_obj;
+ inst_obj = objset_next_obj(li->li_instance_set, inst_obj)) {
+ inst = (ldbm_instance *)object_get_data(inst_obj);
+ instance_set_not_busy(inst);
+ }
+ }
+
+ return return_value;
+}
diff --git a/ldap/servers/slapd/back-ldbm/attrcrypt.h b/ldap/servers/slapd/back-ldbm/attrcrypt.h
new file mode 100644
index 00000000..b6ba50fb
--- /dev/null
+++ b/ldap/servers/slapd/back-ldbm/attrcrypt.h
@@ -0,0 +1,33 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Portions copyright 2004 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+/* Private tructures and #defines used in the attribute encryption code. */
+
+#ifndef _ATTRCRYPT_H_
+#define _ATTRCRYPT_H_
+
+/* structure which holds our stuff in the attrinfo objects */
+struct attrcrypt_private
+{
+ int attrcrypt_cipher;
+};
+
+typedef struct _attrcrypt_cipher_entry
+{
+ int cipher_number;
+ char *cipher_display_name;
+ CK_MECHANISM_TYPE cipher_mechanism;
+ CK_MECHANISM_TYPE wrap_mechanism;
+ CK_MECHANISM_TYPE key_gen_mechanism;
+ int key_size;
+ int iv_length;
+} attrcrypt_cipher_entry;
+
+extern attrcrypt_cipher_entry attrcrypt_cipher_list[];
+
+/* The ciphers we support (used in attrcrypt_cipher above) */
+#define ATTRCRYPT_CIPHER_AES 1
+#define ATTRCRYPT_CIPHER_DES3 2
+
+#endif /* _ATTRCRYPT_H_ */
diff --git a/ldap/servers/slapd/back-ldbm/back-ldbm.h b/ldap/servers/slapd/back-ldbm/back-ldbm.h
new file mode 100644
index 00000000..efe24ed7
--- /dev/null
+++ b/ldap/servers/slapd/back-ldbm/back-ldbm.h
@@ -0,0 +1,629 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+/* back-ldbm.h - ldap ldbm back-end header file */
+
+#ifndef _BACK_LDBM_H_
+#define _BACK_LDBM_H_
+
+#define SLAPD_LOGGING 1
+
+#if defined(irix) || defined(AIX) || defined(HPUX11) || defined(OS_solaris) || defined(linux)
+/* built-in 64-bit file I/O support */
+#define DB_USE_64LFS
+#endif
+
+/* needed by at least HPUX and Solaris, to define off64_t */
+#ifdef DB_USE_64LFS
+#define _LARGEFILE64_SOURCE
+#endif
+
+/* A bunch of random system headers taken from all the source files, no source file should #include
+ any system headers now */
+#include <stdio.h>
+#include <string.h>
+#include <sys/types.h>
+#include <errno.h>
+#include <sys/stat.h>
+#include <stdlib.h>
+#include "prio.h" /* for PR_OpenDir etc */
+#include "prlog.h" /* for PR_ASSERT */
+/* The following cruft is for ldif2db only */
+#ifndef XP_WIN32
+#include <unistd.h> /* write/close (ldbm2ldif_write) */
+#else
+#include <io.h> /* write/close (ldbm2ldif_write) */
+#endif
+#include <fcntl.h>
+#include <time.h>
+/* And this cruft is from nextid.c */
+#ifndef _WIN32
+#include <sys/param.h>
+#endif /* ! _WIN32 */
+#include <limits.h> /* Used in search.c (why?) */
+
+
+
+#ifndef _WIN32
+/* for MAXPATHLEN */
+#include <sys/param.h>
+#define MKDIR(path,mode) mkdir((path),(mode))
+#else
+/* for mkdir */
+#include <direct.h>
+#define MKDIR(path,mode) mkdir(path)
+#endif
+
+#ifdef HPUX11
+#define __BIT_TYPES_DEFINED__
+typedef unsigned char u_int8_t;
+typedef unsigned int u_int32_t;
+typedef unsigned short u_int16_t;
+#endif
+#include "db.h"
+
+#define dptr data
+#define dsize size
+
+#define ID2ENTRY "id2entry" /* main db file name: ID2ENTRY+LDBM_SUFFIX */
+
+#define LDBM_SUFFIX_OLD ".db3"
+#define LDBM_SUFFIX ".db4"
+
+#define MEGABYTE (1024 * 1024)
+#define GIGABYTE (1024 * MEGABYTE)
+
+
+/* include NSPR header files */
+#include "nspr.h"
+#include "plhash.h"
+
+#include "slap.h"
+#include "slapi-plugin.h"
+#include "slapi-private.h"
+#include "avl.h"
+#include "ldaplog.h"
+#include "portable.h"
+#include "proto-slap.h"
+
+/* We should only change the LDBM_VERSION when the format of the db files
+ * is changing in some (possibly incompatible) way -- so we can detect and
+ * treat older ldbm versions. Thus, f.e., DS4.1 will still use the same
+ * LDBM_VERSION as 4.0 and so on...
+ * Don't make the length of LDBM_VERSION longer than LDBM_VERSION_MAXBUF - 1
+ */
+#define LDBM_VERSION_MAXBUF 64
+#define LDBM_DATABASE_TYPE_NAME "ldbm database"
+/*
+ * While we support both new and old idl index,
+ * we distinguish them by the following 2 macros.
+ * When we drop the old idl code, we eliminate LDBM_VERSION_OLD.
+ * bug #604922
+ */
+/* To set new idl default, uncomment it. */
+#define USE_NEW_IDL 1
+
+#define LDBM_VERSION_BASE "Netscape-ldbm/"
+#define LDBM_VERSION "Netscape-ldbm/7.0" /* db42: new idl -> old */
+#define LDBM_VERSION_NEW "Netscape-ldbm/7.0_NEW" /* db42: new idl */
+ /* used only when
+ * USE_NEW_IDL is
+ * NOT defined
+ */
+#define LDBM_VERSION_OLD "Netscape-ldbm/7.0_CLASSIC" /* db42: old idl */
+ /* used only when
+ * USE_NEW_IDL is
+ * defined
+ */
+#define LDBM_VERSION_62 "Netscape-ldbm/6.2" /* db33: new idl */
+#define LDBM_VERSION_61 "Netscape-ldbm/6.1" /* db33: new idl */
+#define LDBM_VERSION_60 "Netscape-ldbm/6.0" /* db33: old idl */
+
+#define LDBM_VERSION_50 "Netscape-ldbm/5.0"
+#define LDBM_VERSION_40 "Netscape-ldbm/4.0"
+#define LDBM_VERSION_30 "Netscape-ldbm/3.0"
+#define LDBM_VERSION_31 "Netscape-ldbm/3.1"
+#define LDBM_FILENAME_SUFFIX ".db4"
+#define DBVERSION_FILENAME "DBVERSION"
+#define DEFAULT_CACHE_SIZE (size_t)10485760
+#define DEFAULT_CACHE_ENTRIES -1 /* no limit */
+#define DEFAULT_DBCACHE_SIZE 1000000
+#define DEFAULT_MODE 0600
+#define DEFAULT_ALLIDSTHRESHOLD 4000
+#define DEFAULT_LOOKTHROUGHLIMIT 5000
+#define DEFAULT_IDL_TUNE 1
+#define DEFAULT_SEARCH_TUNE 0
+#define DEFAULT_IMPORT_INDEX_BUFFER_SIZE 0
+#define SUBLEN 3
+#define LDBM_CACHE_RETRY_COUNT 1000 /* Number of times we re-try a cache operation */
+#define IDL_FETCH_RETRY_COUNT 5 /* Number of times we re-try idl_fetch if it returns deadlock */
+#define IMPORT_SUBCOUNT_HASHTABLE_SIZE 500 /* Number of buckets in hash used to accumulate subcount for broody parents */
+
+/* minimum max ids that a single index entry can map to in ldbm */
+#define SLAPD_LDBM_MIN_MAXIDS 4000
+
+/* clear the following flag to suppress "database files do not exist" warning */
+extern int ldbm_warn_if_no_db;
+
+/*
+ * there is a single index for each attribute. these prefixes insure
+ * that there is no collision among keys.
+ */
+#define EQ_PREFIX '=' /* prefix for equality keys */
+#define APPROX_PREFIX '~' /* prefix for approx keys */
+#define SUB_PREFIX '*' /* prefix for substring keys */
+#define CONT_PREFIX '\\' /* prefix for continuation keys */
+#define RULE_PREFIX ':' /* prefix for matchingRule keys */
+#define PRES_PREFIX '+'
+
+/* Values for "disposition" value in idl_insert_key() */
+#define IDL_INSERT_NORMAL 1
+#define IDL_INSERT_ALLIDS 2
+#define IDL_INSERT_NOW_ALLIDS 3
+
+#define DEFAULT_BLOCKSIZE 8192
+
+/*
+ * The candidate list size at which it is cheaper to apply the filter test
+ * to the whole list than to continue ANDing in IDLs.
+ */
+#define FILTER_TEST_THRESHOLD (NIDS)10
+
+/* flags to indicate what kind of startup the dblayer should do */
+#define DBLAYER_IMPORT_MODE 0x1
+#define DBLAYER_NORMAL_MODE 0x2
+#define DBLAYER_EXPORT_MODE 0x4
+#define DBLAYER_ARCHIVE_MODE 0x8
+#define DBLAYER_RESTORE_MODE 0x10
+#define DBLAYER_RESTORE_NO_RECOVERY_MODE 0x20
+#define DBLAYER_TEST_MODE 0x40
+#define DBLAYER_INDEX_MODE 0x80
+#define DBLAYER_CLEAN_RECOVER_MODE 0x100
+
+#define DBLAYER_CMDLINE_MODE 0x1000
+
+#define DBLAYER_RESTORE_MASK (DBLAYER_RESTORE_MODE|DBLAYER_RESTORE_NO_RECOVERY_MODE)
+
+
+/*
+ * the id used in the indexes to refer to an entry
+ */
+typedef u_int32_t ID;
+#define MAXID ((ID)-3)
+#define NOID ((ID)-2)
+#define ALLID ((ID)-1)
+
+/*
+ * effective only on idl_new_fetch
+ */
+#define NEW_IDL_NOOP 1 /* no need to fetch on new idl */
+#define NEW_IDL_NO_ALLID 2 /* force to return full idl (no allids) */
+#define NEW_IDL_DEFAULT 0
+
+/*
+ * if the id of any backend instance is above the threshold, then warning
+ * message will be logged about the need of rebuilding the database in question
+ */
+#define ID_WARNING_THRESHOLD (MAXID * 0.9)
+
+/*
+ * Use this to count and index into an array of ID.
+ */
+typedef u_int32_t NIDS;
+
+/*
+ * This structure represents an id block on disk and an id list
+ * in core.
+ *
+ * The fields have the following meanings:
+ *
+ * b_nmax maximum number of ids in this block. if this is == ALLIDSBLOCK,
+ * then this block represents all ids.
+ * b_nids current number of ids in use in this block. if this
+ * is == INDBLOCK, then this block is an indirect block
+ * containing a list of other blocks containing actual ids.
+ * the list is terminated by an id of NOID.
+ * b_ids a list of the actual ids themselves
+ */
+typedef struct block {
+ NIDS b_nmax; /* max number of ids in this list */
+#define ALLIDSBLOCK 0 /* == 0 => this is an allid block */
+ NIDS b_nids; /* current number of ids used */
+#define INDBLOCK 0 /* == 0 => this is an indirect blk */
+ ID b_ids[1]; /* the ids - actually bigger */
+} Block, IDList;
+
+#define ALLIDS( idl ) ((idl)->b_nmax == ALLIDSBLOCK)
+#define INDIRECT_BLOCK( idl ) ((idl)->b_nids == INDBLOCK)
+#define IDL_NIDS(idl) (idl ? (idl)->b_nids : (NIDS)0)
+
+typedef size_t idl_iterator;
+
+/* small hashtable implementation used in the entry cache -- the table
+ * stores little identical structs, and relies on using a (void *) inside
+ * the struct to store linkage information.
+ */
+typedef int (*HashTestFn)(const void *, const void *);
+typedef unsigned long (*HashFn)(const void *, size_t);
+typedef struct {
+ u_long offset; /* offset of linkage info in user struct */
+ u_long size; /* members in array below */
+ HashFn hashfn; /* compute a hash value on a key */
+ HashTestFn testfn; /* function to test if two entries are equal */
+ void * slot[1]; /* actually much bigger */
+} Hashtable;
+
+/* use this macro to find the offset of the linkage info into your structure
+ * (required for hashtable to work correctly)
+ * HASHLOC(struct mything, linkptr)
+ */
+#define HASHLOC(mem, node) (u_long)&(((mem *)0L)->node)
+
+struct backentry {
+ Slapi_Entry *ep_entry; /* real entry */
+ Slapi_Entry *ep_vlventry;
+ ID ep_id; /* entry id */
+ char ep_state; /* state in the cache */
+#define ENTRY_STATE_DELETED 0x1 /* entry is marked as deleted */
+#define ENTRY_STATE_CREATING 0x2 /* entry is being created; don't touch it */
+#define ENTRY_STATE_NOTINCACHE 0x4 /* cache_add failed; not in the cache */
+ int ep_refcnt; /* entry reference cnt */
+ void * ep_dn_link; /* linkage for the 3 hash */
+ void * ep_id_link; /* tables used for */
+ void * ep_uuid_link; /* looking up entries */
+ struct backentry *ep_lrunext; /* for the cache */
+ struct backentry *ep_lruprev; /* for the cache */
+ PRLock *ep_mutexp; /* protection for mods */
+ size_t size; /* for cache tracking */
+};
+
+/* for the in-core cache of entries */
+struct cache {
+ size_t c_maxsize; /* max size in bytes */
+ size_t c_cursize; /* size in bytes */
+ long c_maxentries; /* max entries allowed (-1: no limit) */
+ long c_curentries; /* current # entries in cache */
+ Hashtable *c_dntable;
+ Hashtable *c_idtable;
+#ifdef UUIDCACHE_ON
+ Hashtable *c_uuidtable;
+#endif
+ u_long c_hits; /* for analysis of hits/misses */
+ u_long c_tries;
+ struct backentry *c_lruhead; /* add entries here */
+ struct backentry *c_lrutail; /* remove entries here */
+ PRLock *c_mutex; /* lock for cache operations */
+ PRLock *c_emutexalloc_mutex;
+};
+
+/* various modules keep private data inside the attrinfo structure */
+typedef struct dblayer_private dblayer_private;
+typedef struct dblayer_private_env dblayer_private_env;
+typedef struct idl_private idl_private;
+typedef struct attrcrypt_private attrcrypt_private;
+
+
+/* for the cache of attribute information (which are indexed, etc.) */
+struct attrinfo {
+ char *ai_type; /* type name (cn, sn, ...) */
+ int ai_indexmask; /* how the attr is indexed */
+#define INDEX_PRESENCE 0x01
+#define INDEX_EQUALITY 0x02
+#define INDEX_APPROX 0x04
+#define INDEX_SUB 0x08
+#define INDEX_UNKNOWN 0x10
+#define INDEX_FROMINIT 0x20
+#define INDEX_RULES 0x40
+#define INDEX_VLV 0x80
+#define INDEX_ANY (INDEX_PRESENCE | INDEX_EQUALITY | INDEX_APPROX | INDEX_SUB | INDEX_RULES | INDEX_VLV)
+
+#define INDEX_OFFLINE 0x1000 /* index is being generated, or
+ * has been created but not indexed
+ * yet. */
+
+#define IS_INDEXED( a ) ( a & INDEX_ANY )
+ void *ai_plugin;
+ char **ai_index_rules; /* matching rule OIDs */
+ void *ai_dblayer; /* private data used by the dblayer code */
+ PRInt32 ai_dblayer_count; /* used by the dblayer code */
+ idl_private *ai_idl; /* private data used by the IDL code (eg locking the IDLs) */
+ attrcrypt_private *ai_attrcrypt; /* private data used by the attribute encryption code (eg is it enabled or not) */
+};
+
+#define MAXDBCACHE 20
+
+struct id_array {
+ int ida_next_index; /*The next index that is free*/
+ int ida_size; /*The size of this puppy*/
+ ID *ida_ids; /*The array of ids*/
+
+};
+typedef struct id_array Id_Array;
+
+struct _db_upgrade_info {
+ char* old_version_string;
+ int type;
+ int action;
+};
+typedef struct _db_upgrade_info db_upgrade_info;
+/* Values for dbversion_stuff->type */
+#define DBVERSION_COMPATIBLE 0x10
+#define DBVERSION_UPGRADABLE 0x20
+#define DBVERSION_SOL 0x40
+#define DBVERSION_OLD_IDL 0x1
+#define DBVERSION_NEW_IDL 0x2
+
+/* Values for dbversion_stuff->action + return value */
+#define DBVERSION_NO_UPGRADE 0x0
+#define DBVERSION_NEED_IDL_OLD2NEW 0x100
+#define DBVERSION_NEED_IDL_NEW2OLD 0x200
+#define DBVERSION_UPGRADE_3_4 0x400
+#define DBVERSION_NOT_SUPPORTED 0x800
+
+#define DBVERSION_TYPE 0x1
+#define DBVERSION_ACTION 0x2
+
+struct ldbminfo {
+ int li_mode;
+ int li_lookthroughlimit;
+ int li_allidsthreshold;
+ char *li_directory;
+ int li_reslimit_lookthrough_handle;
+ size_t li_dbcachesize;
+ int li_dbncache;
+ int li_import_cache_autosize; /* % of free memory to use
+ * for the import caches
+ * (-1=default, 80% on cmd import)
+ * (0 = off) -- overrides
+ * import cache size settings */
+ int li_cache_autosize; /* % of free memory to use
+ * for the combined caches
+ * (0 = off) -- overrides
+ * other cache size settings */
+ int li_cache_autosize_split; /* % of li_cache_autosize to
+ * use for the libdb cache.
+ * the rest is split up among
+ * the instance entry caches */
+ unsigned long li_cache_autosize_ec; /* new instances created while
+ * the server is up, should
+ * use this as the entry cache
+ * size (0 = autosize off) */
+ size_t li_import_cachesize; /* size of the mpool for
+ * imports */
+ PRLock *li_dbcache_mutex;
+ PRCondVar *li_dbcache_cv;
+ int li_shutdown; /* flag to tell any BE threads
+ * to end */
+ PRLock *li_shutdown_mutex; /* protect shutdown flag */
+ dblayer_private *li_dblayer_private; /* session ptr for databases */
+ int li_noparentcheck; /* check if parent exists on
+ * add */
+
+ /* the next 2 fields are for the params that don't get changed until
+ * the server is restarted (used by the admin console)
+ */
+ char *li_new_directory;
+ size_t li_new_dbcachesize;
+
+ int li_new_dbncache;
+
+ db_upgrade_info *upgrade_info;
+ int li_filter_bypass; /* bypass filter testing,
+ * when possible */
+ int li_filter_bypass_check; /* check that filter bypass
+ * is doing the right thing */
+ int li_use_vlv; /* use vlv indexes to short-
+ * circuit matches when
+ * possible */
+ void *li_identity; /* The ldbm plugin needs to keep
+ * track of its identity so it can
+ * perform internal ops. Its
+ * identity is given to it when
+ * its init function is called. */
+
+ Objset *li_instance_set; /* A set containing the ldbm
+ * instances. */
+
+ PRLock *li_config_mutex;
+
+ /* There are times when we need a pointer to the ldbm database
+ * plugin, so we will store a pointer to it here. Examples of
+ * when we need it are when we create a new instance and when
+ * we need the name of the plugin to do internal ops. */
+ struct slapdplugin *li_plugin;
+
+ /* factory extension markers for the Connection struct -- bulk import
+ * uses this to store state info on a Connection.
+ */
+ int li_bulk_import_object;
+ int li_bulk_import_handle;
+ /* maximum number of pass before merging the files during an import */
+ int li_maxpassbeforemerge;
+
+ /* charray of attributes to exclude from LDIF export */
+ char **li_attrs_to_exclude_from_export;
+
+ int li_flags;
+ int li_fat_lock; /* 608146 -- make this configurable, first */
+ int li_legacy_errcode; /* 615428 -- in case legacy err code is expected */
+};
+
+/* li_flags could store these bits defined in ../slap.h
+ * task flag (pb_task_flags) *
+ * #define TASK_RUNNING_AS_TASK 0x0
+ * #define TASK_RUNNING_FROM_COMMANDLINE 0x1
+ */
+/* allow conf w/o CONFIG_FLAG_ALLOW_RUNNING_CHANGE to be updated */
+#define LI_FORCE_MOD_CONFIG 0x10
+
+/* Structure used to hold stuff for the lifetime of an LDAP transaction */
+/* If we do clever stuff like LDAP transactions, we'll need a stack of TXN ID's */
+typedef struct back_txn back_txn;
+struct back_txn {
+ DB_TXN *back_txn_txn; /* Transaction ID for the database */
+};
+typedef void * back_txnid;
+
+#define RETRY_TIMES 50
+
+/* Structure used to communicate information about subordinatecount on import/upgrade */
+struct _import_subcount_stuff {
+ PLHashTable *hashtable;
+};
+typedef struct _import_subcount_stuff import_subcount_stuff;
+
+/* Handy structures for modify operations */
+
+struct _modify_context {
+ int new_entry_in_cache;
+ struct backentry *old_entry;
+ struct backentry *new_entry;
+ Slapi_Mods *smods;
+};
+typedef struct _modify_context modify_context;
+
+#define INSTANCE_DB_SUFFIX "-db"
+#define INSTANCE_CHANGELOG_SUFFIX "-changelog"
+
+
+/* This structure was moved here from dblayer.c because the ldbm_instance
+ * structure uses the dblayer_handle structure. */
+struct tag_dblayer_handle; typedef struct tag_dblayer_handle dblayer_handle;
+struct tag_dblayer_handle
+{
+ DB* dblayer_dbp;
+ PRLock *dblayer_lock; /* used when anyone wants exclusive access to a file */
+ dblayer_handle *dblayer_handle_next;
+ void **dblayer_handle_ai_backpointer; /* Voodo magic pointer to the place where we store a
+ pointer to this handle in the attrinfo structure */
+};
+
+/* This structure was moved here from perfctrs.c so the ldbm_instance structure
+ * could use it. */
+struct _perfctrs_private {
+#if defined(_WIN32)
+ /* Handle to the shared memory object */
+ HANDLE hMemory;
+ /* Handle to the update event */
+ HANDLE hEvent;
+#else
+ /* Nothing yet */
+#endif
+ /* Pointer to the shared memory */
+ void *memory;
+};
+typedef struct _perfctrs_private perfctrs_private;
+
+typedef struct _attrcrypt_state_private attrcrypt_state_private;
+
+/* flags for ldbm_instance */
+/* please lock inst_config_mutex before changing inst_flags */
+#define INST_FLAG_BUSY 0x0001 /* instance is doing an import or
+ * restore. */
+#define INST_FLAG_READONLY 0x0002 /* instance is truly readonly */
+
+/* Structure used to hold instance specific information. */
+typedef struct ldbm_instance {
+ char *inst_name; /* Name given for this instance. */
+ backend *inst_be; /* pointer back to the backend */
+ struct ldbminfo *inst_li; /* pointer back to global info */
+ int inst_flags; /* see above */
+
+ PRLock *inst_config_mutex;
+
+ PRInt32 *inst_ref_count; /* Keeps track of how many operations
+ * are currently using this instance */
+
+ char *inst_dir_name; /* The name of the directory in the db
+ * directory that holds the index files
+ * for this instance. Relative to the
+ * parent of the instance name dir */
+ char *inst_parent_dir_name; /* Absolute parent dir for this inst */
+
+ PRLock *inst_db_mutex; /* Used to synchronize modify operations
+ * on this instance. */
+
+ dblayer_handle *inst_handle_head; /* These are used to maintain a list */
+ dblayer_handle *inst_handle_tail; /* of open db handles for this instance */
+ PRLock *inst_handle_list_mutex;
+
+ DB *inst_id2entry; /* id2entry for this instance. */
+
+ perfctrs_private inst_perf_private; /* Private data for the performace
+ * counters specific to this instance */
+ attrcrypt_state_private *inst_attrcrypt_state_private;
+ int attrcrypt_configured; /* Are any attributes configured for encryption ? */
+
+ Avlnode *inst_attrs; /* Keeps track of what's indexed for
+ * this instance. */
+
+ struct cache inst_cache; /* The entry cache for this instance. */
+
+ PRLock *inst_nextid_mutex;
+ ID inst_nextid;
+
+ PRCondVar *inst_indexer_cv; /* indexer thread cond var */
+ PRThread *inst_indexer_tid; /* for the indexer thread */
+
+ long inst_cache_hits; /* used during imports to figure out when
+ * a pass should end. */
+ long inst_cache_misses;
+
+ char *inst_dataversion; /* The user data version tag. Used by
+ * replication. */
+ dblayer_private_env *import_env; /* use a different DB_ENV for imports */
+ int require_index; /* set to 1 to require an index be used
+ * in search */
+} ldbm_instance;
+
+/*
+ * This structure is passed through the PBlock from ldbm_back_search to
+ * ldbm_back_next_search_entry. It contains the candidate result set
+ * determined by ldbm_back_search, to be served up by ldbm_back_next_search_entry.
+ */
+typedef struct _back_search_result_set
+{
+ IDList* sr_candidates; /* the search results */
+ idl_iterator sr_current; /* the current position in the search results */
+ struct backentry* sr_entry; /* the last entry returned */
+ int sr_lookthroughcount; /* how many have we examined? */
+ int sr_lookthroughlimit; /* how many can we examine? */
+ int sr_virtuallistview; /* is this a VLV Search */
+ Slapi_Entry* sr_vlventry; /* a special VLV Entry for when the ACL check fails */
+ int sr_flags; /* Magic flags, defined below */
+} back_search_result_set;
+#define SR_FLAG_CAN_SKIP_FILTER_TEST 1 /* If set in sr_flags, means that we can safely skip the filter test */
+
+#include "proto-back-ldbm.h"
+#include "ldbm_config.h"
+
+/* flags used when adding/removing index items */
+#define BE_INDEX_ADD 1
+#define BE_INDEX_DEL 2
+#define BE_INDEX_PRESENCE 4 /* (w/DEL) remove the presence index */
+#define BE_INDEX_TOMBSTONE 8 /* Index entry as a tombstone */
+#define BE_INDEX_DONT_ENCRYPT 16 /* Disable any encryption if this flag is set */
+
+/* Name of attribute type used for binder-based look through limit */
+#define LDBM_LOOKTHROUGHLIMIT_AT "nsLookThroughLimit"
+
+/* OIDs for attribute types used internally */
+#define LDBM_ENTRYDN_OID "2.16.840.1.113730.3.1.602"
+#define LDBM_DNCOMP_OID "2.16.840.1.113730.3.1.603"
+#define LDBM_PARENTID_OID "2.16.840.1.113730.3.1.604"
+#define LDBM_ENTRYID_OID "2.16.840.1.113730.3.1.605"
+
+/* Name of psuedo attribute used to track default indexes */
+#define LDBM_PSEUDO_ATTR_DEFAULT ".default"
+
+/* for checking disk full errors. */
+#define LDBM_OS_ERR_IS_DISKFULL( err ) ((err)==ENOSPC || (err)==EFBIG)
+
+/* flag: open_flag for dblayer_get_index_file -> dblayer_open_file */
+#define DBOPEN_CREATE 0x1 /* oprinary mode: create a db file if needed */
+
+/* whether we call fat lock or not [608146] */
+#define SERIALLOCK(li) (li->li_fat_lock)
+#endif /* _back_ldbm_h_ */
diff --git a/ldap/servers/slapd/back-ldbm/backentry.c b/ldap/servers/slapd/back-ldbm/backentry.c
new file mode 100644
index 00000000..f38953d8
--- /dev/null
+++ b/ldap/servers/slapd/back-ldbm/backentry.c
@@ -0,0 +1,89 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+/* backentry.c - wrapper routines to deal with entries */
+
+#include "back-ldbm.h"
+
+void
+backentry_free( struct backentry **bep )
+{
+ struct backentry *ep;
+ if ( NULL == bep || NULL == *bep ) {
+ return;
+ }
+ ep = *bep;
+ if ( ep->ep_entry != NULL ) {
+ slapi_entry_free( ep->ep_entry );
+ }
+ if ( ep->ep_mutexp != NULL ) {
+ PR_DestroyLock( ep->ep_mutexp );
+ }
+ slapi_ch_free( (void**)&ep );
+ *bep = NULL;
+}
+
+struct backentry *
+backentry_alloc()
+{
+ struct backentry *ec;
+ ec = (struct backentry *) slapi_ch_calloc( 1, sizeof(struct backentry) ) ;
+ ec->ep_state = ENTRY_STATE_NOTINCACHE;
+#ifdef LDAP_CACHE_DEBUG
+ ec->debug_sig = 0x45454545;
+#endif
+ return ec;
+}
+
+void backentry_clear_entry( struct backentry *ep )
+{
+ if (ep)
+ {
+ ep->ep_entry = NULL;
+ }
+}
+
+struct backentry *
+backentry_init( Slapi_Entry *e )
+{
+ struct backentry *ep;
+
+ ep = (struct backentry *) slapi_ch_calloc( 1, sizeof(struct backentry) );
+ ep->ep_entry= e;
+ ep->ep_state = ENTRY_STATE_NOTINCACHE;
+#ifdef LDAP_CACHE_DEBUG
+ ep->debug_sig = 0x23232323;
+#endif
+
+ return( ep );
+}
+
+struct backentry *
+backentry_dup( struct backentry *e )
+{
+ struct backentry *ec;
+
+ ec = (struct backentry *) slapi_ch_calloc( 1, sizeof(struct backentry) );
+ ec->ep_id = e->ep_id;
+ ec->ep_entry = slapi_entry_dup( e->ep_entry );
+ ec->ep_state = ENTRY_STATE_NOTINCACHE;
+#ifdef LDAP_CACHE_DEBUG
+ ec->debug_sig = 0x12121212;
+#endif
+
+ return( ec );
+}
+
+char *
+backentry_get_ndn(const struct backentry *e)
+{
+ return (char *)slapi_sdn_get_ndn(slapi_entry_get_sdn_const(e->ep_entry));
+}
+
+const Slapi_DN *
+backentry_get_sdn(const struct backentry *e)
+{
+ return slapi_entry_get_sdn_const(e->ep_entry);
+}
diff --git a/ldap/servers/slapd/back-ldbm/cache.c b/ldap/servers/slapd/back-ldbm/cache.c
new file mode 100644
index 00000000..e55bddb2
--- /dev/null
+++ b/ldap/servers/slapd/back-ldbm/cache.c
@@ -0,0 +1,1195 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+/* cache.c - routines to maintain an in-core cache of entries */
+
+#include "back-ldbm.h"
+
+#ifdef DEBUG
+#define LDAP_CACHE_DEBUG
+/* #define LDAP_CACHE_DEBUG_LRU */ /* causes slowdown */
+#endif
+
+/* cache can't get any smaller than this (in bytes) */
+#define MINCACHESIZE (size_t)200000
+
+/* don't let hash be smaller than this # of slots */
+#define MINHASHSIZE 1024
+
+/*
+ * the cache 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
+ * by uniqueid
+ *
+ * these correspond to three different avl trees that are maintained.
+ * those avl trees are being destroyed as we speak.
+ */
+
+#ifdef LDAP_CACHE_DEBUG
+#define ASSERT(_x) do { \
+ if (!(_x)) { \
+ LDAPDebug(LDAP_DEBUG_ANY, "BAD CACHE ASSERTION at %s/%d: %s\n", \
+ __FILE__, __LINE__, #_x); \
+ *(char *)0L = 23; \
+ } \
+} while (0)
+#define LOG(_a, _x1, _x2, _x3) LDAPDebug(LDAP_DEBUG_CACHE, _a, _x1, _x2, _x3)
+#else
+#define ASSERT(_x) ;
+#define LOG(_a, _x1, _x2, _x3) ;
+#endif
+
+
+/***** tiny hashtable implementation *****/
+
+#define HASH_VALUE(_key, _keylen) \
+ ((ht->hashfn == NULL) ? (*(unsigned int *)(_key)) : \
+ ((*ht->hashfn)(_key, _keylen)))
+#define HASH_NEXT(ht, entry) (*(void **)((char *)(entry) + (ht)->offset))
+
+static int entry_same_id(const void *e, const void *k)
+{
+ return (((struct backentry *)e)->ep_id == *(ID *)k);
+}
+
+static unsigned long dn_hash(const void *key, size_t keylen)
+{
+ unsigned char *x = (unsigned char *)key;
+ ssize_t i;
+ unsigned long val = 0;
+
+ for (i = keylen-1; i >= 0; i--)
+ val += ((val << 5) + (*x++)) & 0xffffffff;
+ return val;
+}
+
+#ifdef UUIDCACHE_ON
+static unsigned long uuid_hash(const void *key, size_t keylen)
+{
+ unsigned char *x = (unsigned char *)key;
+ size_t i;
+ unsigned long val = 0;
+
+ for (i = 0; i < keylen; i++, x++) {
+ char c = (*x <= '9' ? (*x - '0') : (*x - 'A' + 10));
+ val = ((val << 4) ^ (val >> 28) ^ c) & 0xffffffff;
+ }
+ return val;
+}
+
+static int entry_same_uuid(const void *e, const void *k)
+{
+ struct backentry *be = (struct backentry *)e;
+ const char *uuid = slapi_entry_get_uniqueid(be->ep_entry);
+
+ return (strcmp(uuid, (char *)k) == 0);
+}
+#endif
+
+static int entry_same_dn(const void *e, const void *k)
+{
+ struct backentry *be = (struct backentry *)e;
+ const char *ndn = slapi_sdn_get_ndn(backentry_get_sdn(be));
+
+ return (strcmp(ndn, (char *)k) == 0);
+}
+
+Hashtable *new_hash(u_long size, u_long offset, HashFn hfn,
+ HashTestFn tfn)
+{
+ static u_long prime[] = { 3, 5, 7, 11, 13, 17, 19 };
+ Hashtable *ht;
+ int ok = 0, i;
+
+ if (size < MINHASHSIZE)
+ size = MINHASHSIZE;
+ /* move up to nearest relative prime (it's a statistical thing) */
+ size |= 1;
+ do {
+ ok = 1;
+ for (i = 0; i < (sizeof(prime) / sizeof(prime[0])); i++)
+ if (!(size % prime[i]))
+ ok = 0;
+ if (!ok)
+ size += 2;
+ } while (!ok);
+
+ ht = (Hashtable*)slapi_ch_calloc(1, sizeof(Hashtable) + size*sizeof(void *));
+ if (!ht)
+ return NULL;
+ ht->size = size;
+ ht->offset = offset;
+ ht->hashfn = hfn;
+ ht->testfn = tfn;
+ /* calloc zeroes out the slots automagically */
+ return ht;
+}
+
+/* adds an entry to the hash -- returns 1 on success, 0 if the key was
+ * already there (filled into 'alt' if 'alt' is not NULL)
+ */
+int add_hash(Hashtable *ht, void *key, size_t keylen, void *entry,
+ void **alt)
+{
+ u_long val, slot;
+ void *e;
+
+ val = HASH_VALUE(key, keylen);
+ slot = (val % ht->size);
+ /* first, check if this key is already in the table */
+ e = ht->slot[slot];
+ while (e) {
+ if ((*ht->testfn)(e, key)) {
+ /* ack! already in! */
+ if (alt)
+ *alt = e;
+ return 0;
+ }
+ e = HASH_NEXT(ht, e);
+ }
+ /* ok, it's not already there, so add it */
+ HASH_NEXT(ht, entry) = ht->slot[slot];
+ ht->slot[slot] = entry;
+ return 1;
+}
+
+/* returns 1 if the item was found, and puts a ptr to it in 'entry' */
+int find_hash(Hashtable *ht, const void *key, size_t keylen, void **entry)
+{
+ u_long val, slot;
+ void *e;
+
+ val = HASH_VALUE(key, keylen);
+ slot = (val % ht->size);
+ e = ht->slot[slot];
+ while (e) {
+ if ((*ht->testfn)(e, key)) {
+ *entry = e;
+ return 1;
+ }
+ e = HASH_NEXT(ht, e);
+ }
+ /* no go */
+ *entry = NULL;
+ return 0;
+}
+
+/* returns 1 if the item was found and removed */
+int remove_hash(Hashtable *ht, const void *key, size_t keylen)
+{
+ u_long val, slot;
+ void *e, *laste = NULL;
+
+ val = HASH_VALUE(key, keylen);
+ slot = (val % ht->size);
+ e = ht->slot[slot];
+ while (e) {
+ if ((*ht->testfn)(e, key)) {
+ /* remove this one */
+ if (laste)
+ HASH_NEXT(ht, laste) = HASH_NEXT(ht, e);
+ else
+ ht->slot[slot] = HASH_NEXT(ht, e);
+ HASH_NEXT(ht, e) = NULL;
+ return 1;
+ }
+ laste = e;
+ e = HASH_NEXT(ht, e);
+ }
+ /* nope */
+ return 0;
+}
+
+/* hashtable distribution stats --
+ * slots: # of slots in the hashtable
+ * total_entries: # of entries in the hashtable
+ * max_entries_per_slot: highest number of chained entries in a single slot
+ * slot_stats: if X is the number of entries in a given slot, then
+ * slot_stats[X] will hold the number of slots that held X entries
+ */
+static void hash_stats(Hashtable *ht, u_long *slots, int *total_entries,
+ int *max_entries_per_slot, int **slot_stats)
+{
+#define MAX_SLOT_STATS 50
+ u_long i;
+ int x;
+ void *e;
+
+ *slot_stats = (int *)slapi_ch_malloc(MAX_SLOT_STATS * sizeof(int));
+ for (i = 0; i < MAX_SLOT_STATS; i++)
+ (*slot_stats)[i] = 0;
+
+ *slots = ht->size;
+ *max_entries_per_slot = 0;
+ *total_entries = 0;
+ for (i = 0; i < ht->size; i++) {
+ e = ht->slot[i];
+ x = 0;
+ while (e) {
+ x++;
+ (*total_entries)++;
+ e = HASH_NEXT(ht, e);
+ }
+ if (x < MAX_SLOT_STATS)
+ (*slot_stats)[x]++;
+ if (x > *max_entries_per_slot)
+ *max_entries_per_slot = x;
+ }
+}
+
+
+/***** add/remove entries to/from the LRU list *****/
+
+#ifdef LDAP_CACHE_DEBUG_LRU
+/* for debugging -- painstakingly verify the lru list is ok -- if 'in' is
+ * true, then entry 'e' should be in the list right now; otherwise, it
+ * should NOT be in the list.
+ */
+static void lru_verify(struct cache *cache, struct backentry *e, int in)
+{
+ int is_in = 0;
+ int count = 0;
+ struct backentry *ep;
+
+ ep = cache->c_lruhead;
+ while (ep) {
+ count++;
+ if (ep == e) {
+ is_in = 1;
+ }
+ if (ep->ep_lruprev) {
+ ASSERT(ep->ep_lruprev->ep_lrunext == ep);
+ } else {
+ ASSERT(ep == cache->c_lruhead);
+ }
+ if (ep->ep_lrunext) {
+ ASSERT(ep->ep_lrunext->ep_lruprev == ep);
+ } else {
+ ASSERT(ep == cache->c_lrutail);
+ }
+
+ ep = ep->ep_lrunext;
+ }
+ ASSERT(is_in == in);
+}
+#endif
+
+/* assume lock is held */
+static void lru_detach(struct cache *cache, struct backentry *e)
+{
+#ifdef LDAP_CACHE_DEBUG_LRU
+ lru_verify(cache, e, 1);
+#endif
+ if (e->ep_lruprev)
+ {
+ e->ep_lruprev->ep_lrunext = NULL;
+ cache->c_lrutail = e->ep_lruprev;
+ }
+ else
+ {
+ cache->c_lruhead = NULL;
+ cache->c_lrutail = NULL;
+ }
+#ifdef LDAP_CACHE_DEBUG_LRU
+ lru_verify(cache, e, 0);
+#endif
+}
+
+/* assume lock is held */
+static void lru_delete(struct cache *cache, struct backentry *e)
+{
+#ifdef LDAP_CACHE_DEBUG_LRU
+ lru_verify(cache, e, 1);
+#endif
+ if (e->ep_lruprev)
+ e->ep_lruprev->ep_lrunext = e->ep_lrunext;
+ else
+ cache->c_lruhead = e->ep_lrunext;
+ if (e->ep_lrunext)
+ e->ep_lrunext->ep_lruprev = e->ep_lruprev;
+ else
+ cache->c_lrutail = e->ep_lruprev;
+#ifdef LDAP_CACHE_DEBUG_LRU
+ e->ep_lrunext = e->ep_lruprev = NULL;
+ lru_verify(cache, e, 0);
+#endif
+}
+
+/* assume lock is held */
+static void lru_add(struct cache *cache, struct backentry *e)
+{
+#ifdef LDAP_CACHE_DEBUG_LRU
+ lru_verify(cache, e, 0);
+#endif
+ e->ep_lruprev = NULL;
+ e->ep_lrunext = cache->c_lruhead;
+ cache->c_lruhead = e;
+ if (e->ep_lrunext)
+ e->ep_lrunext->ep_lruprev = e;
+ if (! cache->c_lrutail)
+ cache->c_lrutail = e;
+#ifdef LDAP_CACHE_DEBUG_LRU
+ lru_verify(cache, e, 1);
+#endif
+}
+
+
+/***** cache overhead *****/
+
+static int cache_remove_int(struct cache *cache, struct backentry *e);
+
+static void cache_make_hashes(struct cache *cache)
+{
+ u_long hashsize = (cache->c_maxentries > 0) ? cache->c_maxentries :
+ (cache->c_maxsize/512);
+
+ cache->c_dntable = new_hash(hashsize,
+ HASHLOC(struct backentry, ep_dn_link),
+ dn_hash, entry_same_dn);
+ cache->c_idtable = new_hash(hashsize,
+ HASHLOC(struct backentry, ep_id_link),
+ NULL, entry_same_id);
+#ifdef UUIDCACHE_ON
+ cache->c_uuidtable = new_hash(hashsize,
+ HASHLOC(struct backentry, ep_uuid_link),
+ uuid_hash, entry_same_uuid);
+#endif
+}
+
+/* initialize the cache */
+int cache_init(struct cache *cache, size_t maxsize, long maxentries)
+{
+ LDAPDebug(LDAP_DEBUG_TRACE, "=> cache_init\n", 0, 0, 0);
+ cache->c_maxsize = maxsize;
+ cache->c_maxentries = maxentries;
+ cache->c_cursize = cache->c_curentries = 0;
+ cache->c_hits = cache->c_tries = 0;
+ cache->c_lruhead = cache->c_lrutail = NULL;
+ cache_make_hashes(cache);
+
+ if (((cache->c_mutex = PR_NewLock()) == NULL) ||
+ ((cache->c_emutexalloc_mutex = PR_NewLock()) == NULL)) {
+ LDAPDebug(LDAP_DEBUG_ANY, "ldbm: cache_init: PR_NewLock failed\n",
+ 0, 0, 0);
+ return 0;
+ }
+ LDAPDebug(LDAP_DEBUG_TRACE, "<= cache_init\n", 0, 0, 0);
+ return 1;
+}
+
+#define CACHE_FULL(cache) \
+ (((cache)->c_cursize > (cache)->c_maxsize) || \
+ (((cache)->c_maxentries > 0) && \
+ ((cache)->c_curentries > cache->c_maxentries)))
+
+
+/* clear out the cache to make room for new entries
+ * you must be holding cache->c_mutex !!
+ * return a pointer on the list of entries that get kicked out
+ * of the cache.
+ * These entries should be freed outside of the cache->c_mutex
+ */
+static struct backentry * cache_flush(struct cache *cache)
+{
+ struct backentry *e = NULL;
+
+ LOG("=> cache_flush\n", 0, 0, 0);
+
+ /* all entries on the LRU list are guaranteed to have a refcnt = 0
+ * (iow, nobody's using them), so just delete from the tail down
+ * until the cache is a managable size again.
+ * (cache->c_mutex is locked when we enter this)
+ */
+ while ((cache->c_lrutail != NULL) && CACHE_FULL(cache)) {
+ if (e == NULL)
+ {
+ e = cache->c_lrutail;
+ }
+ else
+ {
+ e = e->ep_lruprev;
+ }
+ ASSERT(e->ep_refcnt == 0);
+ e->ep_refcnt++;
+ if (cache_remove_int(cache, e) < 0) {
+ LDAPDebug(LDAP_DEBUG_ANY, "cache flush: unable to delete entry\n",
+ 0, 0, 0);
+ break;
+ }
+ if(e == cache->c_lruhead) {
+ break;
+ }
+ }
+ if (e)
+ lru_detach(cache, e);
+ LOG("<= cache_flush (down to %lu entries, %lu bytes)\n", cache->c_curentries,
+ cache->c_cursize, 0);
+ return e;
+}
+
+/* remove everything from the cache */
+static void cache_clear_int(struct cache *cache)
+{
+ struct backentry *eflush = NULL;
+ struct backentry *eflushtemp = NULL;
+ size_t size = cache->c_maxsize;
+
+ cache->c_maxsize = 0;
+ eflush = cache_flush(cache);
+ while (eflush)
+ {
+ eflushtemp = eflush->ep_lrunext;
+ backentry_free(&eflush);
+ eflush = eflushtemp;
+ }
+ cache->c_maxsize = size;
+ if (cache->c_curentries > 0) {
+ LDAPDebug(LDAP_DEBUG_ANY, "somehow, there are still %ld entries "
+ "in the entry cache. :/\n", cache->c_curentries, 0, 0);
+ }
+}
+
+void cache_clear(struct cache *cache)
+{
+ PR_Lock(cache->c_mutex);
+ cache_clear_int(cache);
+ PR_Unlock(cache->c_mutex);
+}
+
+static void erase_cache(struct cache *cache)
+{
+ cache_clear_int(cache);
+ slapi_ch_free((void **)&cache->c_dntable);
+ slapi_ch_free((void **)&cache->c_idtable);
+#ifdef UUIDCACHE_ON
+ slapi_ch_free((void **)&cache->c_uuidtable);
+#endif
+}
+
+/* to be used on shutdown or when destroying a backend instance */
+void cache_destroy_please(struct cache *cache)
+{
+ erase_cache(cache);
+ PR_DestroyLock(cache->c_mutex);
+ PR_DestroyLock(cache->c_emutexalloc_mutex);
+}
+
+void cache_set_max_size(struct cache *cache, size_t bytes)
+{
+ struct backentry *eflush = NULL;
+ struct backentry *eflushtemp = NULL;
+
+ if (bytes < MINCACHESIZE) {
+ bytes = MINCACHESIZE;
+ LDAPDebug(LDAP_DEBUG_ANY,
+ "WARNING -- Minimum cache size is %lu -- rounding up\n",
+ MINCACHESIZE, 0, 0);
+ }
+ PR_Lock(cache->c_mutex);
+ cache->c_maxsize = bytes;
+ LOG("entry cache size set to %lu\n", bytes, 0, 0);
+ /* check for full cache, and clear out if necessary */
+ if (CACHE_FULL(cache))
+ eflush = cache_flush(cache);
+ while (eflush)
+ {
+ eflushtemp = eflush->ep_lrunext;
+ backentry_free(&eflush);
+ eflush = eflushtemp;
+ }
+ if (cache->c_curentries < 50) {
+ /* there's hardly anything left in the cache -- clear it out and
+ * resize the hashtables for efficiency.
+ */
+ erase_cache(cache);
+ cache_make_hashes(cache);
+ }
+ PR_Unlock(cache->c_mutex);
+ if (! dblayer_is_cachesize_sane(&bytes)) {
+ LDAPDebug(LDAP_DEBUG_ANY,
+ "WARNING -- Possible CONFIGURATION ERROR -- cachesize "
+ "(%lu) may be configured to use more than the available "
+ "physical memory.\n", bytes, 0, 0);
+ }
+}
+
+void cache_set_max_entries(struct cache *cache, long entries)
+{
+ struct backentry *eflush = NULL;
+ struct backentry *eflushtemp = NULL;
+
+ /* this is a dumb remnant of pre-5.0 servers, where the cache size
+ * was given in # entries instead of memory footprint. hopefully,
+ * we can eventually drop this.
+ */
+ PR_Lock(cache->c_mutex);
+ cache->c_maxentries = entries;
+ if (entries >= 0) {
+ LOG("entry cache entry-limit set to %lu\n", entries, 0, 0);
+ } else {
+ LOG("entry cache entry-limit turned off\n", 0, 0, 0);
+ }
+
+ /* check for full cache, and clear out if necessary */
+ if (CACHE_FULL(cache))
+ eflush = cache_flush(cache);
+ PR_Unlock(cache->c_mutex);
+ while (eflush)
+ {
+ eflushtemp = eflush->ep_lrunext;
+ backentry_free(&eflush);
+ eflush = eflushtemp;
+ }
+}
+
+size_t cache_get_max_size(struct cache *cache)
+{
+ size_t n;
+
+ PR_Lock(cache->c_mutex);
+ n = cache->c_maxsize;
+ PR_Unlock(cache->c_mutex);
+ return n;
+}
+
+long cache_get_max_entries(struct cache *cache)
+{
+ long n;
+
+ PR_Lock(cache->c_mutex);
+ n = cache->c_maxentries;
+ PR_Unlock(cache->c_mutex);
+ return n;
+}
+
+/* determine the general size of a cache entry */
+static size_t cache_entry_size(struct backentry *e)
+{
+ size_t size = 0;
+
+ if (e->ep_entry)
+ size += slapi_entry_size(e->ep_entry);
+ if (e->ep_vlventry)
+ size += slapi_entry_size(e->ep_vlventry);
+ /* cannot size ep_mutexp (PRLock) */
+ size += sizeof(struct backentry);
+ return size;
+}
+
+/* the monitor code wants to be able to safely fetch the cache stats --
+ * if it ever wants to pull out more info, we might want to change all
+ * these u_long *'s to a struct
+ */
+void cache_get_stats(struct cache *cache, u_long *hits, u_long *tries,
+ long *nentries, long *maxentries,
+ size_t *size, size_t *maxsize)
+{
+ PR_Lock(cache->c_mutex);
+ if (hits) *hits = cache->c_hits;
+ if (tries) *tries = cache->c_tries;
+ if (nentries) *nentries = cache->c_curentries;
+ if (maxentries) *maxentries = cache->c_maxentries;
+ if (size) *size = cache->c_cursize;
+ if (maxsize) *maxsize = cache->c_maxsize;
+ PR_Unlock(cache->c_mutex);
+}
+
+void cache_debug_hash(struct cache *cache, char **out)
+{
+ u_long slots;
+ int total_entries, max_entries_per_slot, *slot_stats;
+ int i, j;
+ Hashtable *ht;
+ char *name;
+
+ PR_Lock(cache->c_mutex);
+ *out = (char *)slapi_ch_malloc(1024);
+ **out = 0;
+
+ for (i = 0; i < 3; i++) {
+ if (i > 0)
+ sprintf(*out + strlen(*out), "; ");
+ switch(i) {
+ case 0:
+ ht = cache->c_dntable;
+ name = "dn";
+ break;
+ case 1:
+ ht = cache->c_idtable;
+ name = "id";
+ break;
+#ifdef UUIDCACHE_ON
+ case 2:
+ default:
+ ht = cache->c_uuidtable;
+ name = "uuid";
+ break;
+#endif
+ }
+ hash_stats(ht, &slots, &total_entries, &max_entries_per_slot,
+ &slot_stats);
+ sprintf(*out + strlen(*out), "%s hash: %lu slots, %d entries (%d max "
+ "entries per slot) -- ", name, slots, total_entries,
+ max_entries_per_slot);
+ for (j = 0; j <= max_entries_per_slot; j++)
+ sprintf(*out + strlen(*out), "%d[%d] ", j, slot_stats[j]);
+ slapi_ch_free((void **)&slot_stats);
+ }
+ PR_Unlock(cache->c_mutex);
+}
+
+
+/***** general-purpose cache stuff *****/
+
+/* remove an entry from the cache */
+/* you must be holding c_mutex !! */
+static int cache_remove_int(struct cache *cache, struct backentry *e)
+{
+ int ret = 1; /* assume not in cache */
+ const char *ndn;
+#ifdef UUIDCACHE_ON
+ const char *uuid;
+#endif
+
+ LOG("=> cache_remove (%s)\n", backentry_get_ndn(e), 0, 0);
+ if (e->ep_state & ENTRY_STATE_NOTINCACHE)
+ {
+ return ret;
+ }
+
+ /* remove from all hashtables -- this function may be called from places
+ * where the entry isn't in all the tables yet, so we don't care if any
+ * of these return errors.
+ */
+ ndn = slapi_sdn_get_ndn(backentry_get_sdn(e));
+ if (remove_hash(cache->c_dntable, (void *)ndn, strlen(ndn)))
+ {
+ ret = 0;
+ }
+ else
+ {
+ LOG("remove %s from dn hash failed\n", ndn, 0, 0);
+ }
+ if (remove_hash(cache->c_idtable, &(e->ep_id), sizeof(ID)))
+ {
+ ret = 0;
+ }
+ else
+ {
+ LOG("remove %d from id hash failed\n", e->ep_id, 0, 0);
+ }
+#ifdef UUIDCACHE_ON
+ uuid = slapi_entry_get_uniqueid(e->ep_entry);
+ if (remove_hash(cache->c_uuidtable, (void *)uuid, strlen(uuid)))
+ {
+ ret = 0;
+ }
+ else
+ {
+ LOG("remove %d from uuid hash failed\n", uuid, 0, 0);
+ }
+#endif
+ if (ret == 0) {
+ /* won't be on the LRU list since it has a refcount on it */
+ /* adjust cache size */
+ cache->c_cursize -= e->size;
+ cache->c_curentries--;
+ LOG("<= cache_remove (size %lu): cache now %lu entries, %lu bytes\n",
+ e->size, cache->c_curentries, cache->c_cursize);
+ }
+
+ /* mark for deletion (will be erased when refcount drops to zero) */
+ e->ep_state |= ENTRY_STATE_DELETED;
+ LOG("<= cache_remove: %d\n", ret, 0, 0);
+ return ret;
+}
+
+/* remove an entry from the cache.
+ * you must have a refcount on e (iow, fetched via cache_find_*). the
+ * entry is removed from the cache, but NOT freed! you are responsible
+ * for freeing the entry yourself when done with it, preferrably via
+ * cache_return (called AFTER cache_remove). some code still does this
+ * via backentry_free, which is okay, as long as you know you're the only
+ * thread holding a reference to the deleted entry.
+ * returns: 0 on success
+ * 1 if the entry wasn't in the cache at all (not even partially)
+ */
+int cache_remove(struct cache *cache, struct backentry *e)
+{
+ int ret;
+
+ PR_Lock(cache->c_mutex);
+ ASSERT(e->ep_refcnt > 0);
+ ret = cache_remove_int(cache, e);
+ PR_Unlock(cache->c_mutex);
+ return ret;
+}
+
+/* replace an entry in the cache.
+ * returns: 0 on success
+ * 1 if the entry wasn't in the cache
+ */
+int cache_replace(struct cache *cache, struct backentry *olde,
+ struct backentry *newe)
+{
+ int found;
+ const char *oldndn;
+ const char *newndn;
+#ifdef UUIDCACHE_ON
+ const char *olduuid;
+ const char *newuuid;
+#endif
+
+ LOG("=> cache_replace (%s) -> (%s)\n", backentry_get_ndn(olde),
+ backentry_get_ndn(newe), 0);
+
+ /* remove from all hashtables -- this function may be called from places
+ * where the entry isn't in all the tables yet, so we don't care if any
+ * of these return errors.
+ */
+ oldndn = slapi_sdn_get_ndn(backentry_get_sdn(olde));
+#ifdef UUIDCACHE_ON
+ olduuid = slapi_entry_get_uniqueid(olde->ep_entry);
+ newuuid = slapi_entry_get_uniqueid(newe->ep_entry);
+#endif
+ newndn = slapi_sdn_get_ndn(backentry_get_sdn(newe));
+ PR_Lock(cache->c_mutex);
+
+ /*
+ * First, remove the old entry from all the hashtables.
+ * If the old entry is in cache but not in at least one of the
+ * cache tables, operation error
+ */
+ if ( (olde->ep_state & ENTRY_STATE_NOTINCACHE) == 0 ) {
+
+ found = remove_hash(cache->c_dntable, (void *)oldndn, strlen(oldndn));
+ found &= remove_hash(cache->c_idtable, &(olde->ep_id), sizeof(ID));
+#ifdef UUIDCACHE_ON
+ found &= remove_hash(cache->c_uuidtable, (void *)olduuid, strlen(olduuid));
+#endif
+ if (!found) {
+ LOG("cache replace: cache index tables out of sync\n", 0, 0, 0);
+ PR_Unlock(cache->c_mutex);
+ return 1;
+ }
+ }
+ if (! entry_same_dn(newe, (void *)oldndn) &&
+ (newe->ep_state & ENTRY_STATE_NOTINCACHE) == 0) {
+ /* if we're doing a modrdn, the new entry can be in the dn table
+ * already, so we need to remove that too.
+ */
+ if (remove_hash(cache->c_dntable, (void *)newndn, strlen(newndn)))
+ {
+ cache->c_cursize -= newe->size;
+ cache->c_curentries--;
+ LOG("cache replace remove entry size %lu\n", newe->size, 0, 0);
+ }
+ }
+
+ /* now, add the new entry to the hashtables */
+ /* (probably don't need such extensive error handling, once this has been
+ * tested enough that we believe it works.)
+ */
+ if (!add_hash(cache->c_dntable, (void *)newndn, strlen(newndn), newe, NULL)) {
+ LOG("cache replace: can't add dn\n", 0, 0, 0);
+ PR_Unlock(cache->c_mutex);
+ return 1;
+ }
+ if (!add_hash(cache->c_idtable, &(newe->ep_id), sizeof(ID), newe, NULL)) {
+ LOG("cache replace: can't add id\n", 0, 0, 0);
+ remove_hash(cache->c_dntable, (void *)newndn, strlen(newndn));
+ PR_Unlock(cache->c_mutex);
+ return 1;
+ }
+#ifdef UUIDCACHE_ON
+ if (newuuid && !add_hash(cache->c_uuidtable, (void *)newuuid, strlen(newuuid),
+ newe, NULL)) {
+ LOG("cache replace: can't add uuid\n", 0, 0, 0);
+ remove_hash(cache->c_dntable, (void *)newndn, strlen(newndn));
+ remove_hash(cache->c_idtable, &(newe->ep_id), sizeof(ID));
+ PR_Unlock(cache->c_mutex);
+ return 1;
+ }
+#endif
+ /* adjust cache meta info */
+ newe->ep_refcnt = 1;
+ newe->size = cache_entry_size(newe);
+ cache->c_cursize += (newe->size - olde->size);
+ olde->ep_state = ENTRY_STATE_DELETED;
+ newe->ep_state = 0;
+ PR_Unlock(cache->c_mutex);
+ LOG("<= cache_replace OK, cache size now %lu cache count now %ld\n",
+ cache->c_cursize, cache->c_curentries, 0);
+ return 0;
+}
+
+/* call this when you're done with an entry that was fetched via one of
+ * the cache_find_* calls.
+ */
+void cache_return(struct cache *cache, struct backentry **bep)
+{
+ struct backentry *eflush = NULL;
+ struct backentry *eflushtemp = NULL;
+ struct backentry *e;
+ if (NULL == bep || NULL == *bep)
+ {
+ LOG("=> cache_return (null entry)\n", 0, 0, 0);
+ return;
+ }
+ e = *bep;
+ LOG("=> cache_return (%s) entry count: %d, entry in cache:%ld\n", backentry_get_ndn(e), e->ep_refcnt, cache->c_curentries);
+
+ PR_Lock(cache->c_mutex);
+ if (e->ep_state & ENTRY_STATE_NOTINCACHE)
+ {
+ backentry_free(bep);
+ }
+ else
+ {
+ ASSERT(e->ep_refcnt > 0);
+ if (! --e->ep_refcnt) {
+ if (e->ep_state & ENTRY_STATE_DELETED) {
+ backentry_free(bep);
+ } else {
+ lru_add(cache, e);
+ /* the cache might be overfull... */
+ if (CACHE_FULL(cache))
+ eflush = cache_flush(cache);
+ }
+ }
+ }
+ PR_Unlock(cache->c_mutex);
+ while (eflush)
+ {
+ eflushtemp = eflush->ep_lrunext;
+ backentry_free(&eflush);
+ eflush = eflushtemp;
+ }
+}
+
+
+/* lookup entry by DN (assume cache lock is held) */
+struct backentry *cache_find_dn(struct cache *cache, const char *dn, unsigned long ndnlen)
+{
+ struct backentry *e;
+
+ LOG("=> cache_find_dn (%s)\n", dn, 0, 0);
+
+ /*entry normalized by caller (dn2entry.c) */
+ PR_Lock(cache->c_mutex);
+ if (find_hash(cache->c_dntable, (void *)dn, ndnlen, (void **)&e)) {
+ /* need to check entry state */
+ if (e->ep_state != 0) {
+ /* entry is deleted or not fully created yet */
+ PR_Unlock(cache->c_mutex);
+ LOG("<= cache_find_dn (NOT FOUND)\n", 0, 0, 0);
+ return NULL;
+ }
+ if (e->ep_refcnt == 0)
+ lru_delete(cache, e);
+ e->ep_refcnt++;
+ cache->c_hits++;
+ }
+ cache->c_tries++;
+ PR_Unlock(cache->c_mutex);
+
+ LOG("<= cache_find_dn (%sFOUND)\n", e ? "" : "NOT ", 0, 0);
+ return e;
+}
+
+
+/* lookup an entry in the cache by its id# (you must return it later) */
+struct backentry *cache_find_id(struct cache *cache, ID id)
+{
+ struct backentry *e;
+
+ LOG("=> cache_find_id (%lu)\n", (u_long)id, 0, 0);
+
+ PR_Lock(cache->c_mutex);
+ if (find_hash(cache->c_idtable, &id, sizeof(ID), (void **)&e)) {
+ /* need to check entry state */
+ if (e->ep_state != 0) {
+ /* entry is deleted or not fully created yet */
+ PR_Unlock(cache->c_mutex);
+ LOG("<= cache_find_id (NOT FOUND)\n", 0, 0, 0);
+ return NULL;
+ }
+ if (e->ep_refcnt == 0)
+ lru_delete(cache, e);
+ e->ep_refcnt++;
+ cache->c_hits++;
+ }
+ cache->c_tries++;
+ PR_Unlock(cache->c_mutex);
+
+ LOG("<= cache_find_id (%sFOUND)\n", e ? "" : "NOT ", 0, 0);
+ return e;
+}
+
+#ifdef UUIDCACHE_ON
+/* lookup an entry in the cache by it's uuid (you must return it later) */
+struct backentry *cache_find_uuid(struct cache *cache, const char *uuid)
+{
+ struct backentry *e;
+
+ LOG("=> cache_find_uuid (%s)\n", uuid, 0, 0);
+
+ PR_Lock(cache->c_mutex);
+ if (find_hash(cache->c_uuidtable, uuid, strlen(uuid), (void **)&e)) {
+ /* need to check entry state */
+ if (e->ep_state != 0) {
+ /* entry is deleted or not fully created yet */
+ PR_Unlock(cache->c_mutex);
+ LOG("<= cache_find_uuid (NOT FOUND)\n", 0, 0, 0);
+ return NULL;
+ }
+ if (e->ep_refcnt == 0)
+ lru_delete(cache, e);
+ e->ep_refcnt++;
+ cache->c_hits++;
+ }
+ cache->c_tries++;
+ PR_Unlock(cache->c_mutex);
+
+ LOG("<= cache_find_uuid (%sFOUND)\n", e ? "" : "NOT ", 0, 0);
+ return e;
+}
+#endif
+
+/* add an entry to the cache */
+static int cache_add_int(struct cache *cache, struct backentry *e, int state,
+ struct backentry **alt)
+{
+ struct backentry *eflush = NULL;
+ struct backentry *eflushtemp = NULL;
+ const char *ndn = slapi_sdn_get_ndn(backentry_get_sdn(e));
+#ifdef UUIDCACHE_ON
+ const char *uuid = slapi_entry_get_uniqueid(e->ep_entry);
+#endif
+ struct backentry *my_alt;
+ int already_in = 0;
+
+ LOG("=> cache_add_int( \"%s\", %ld )\n", backentry_get_ndn(e),
+ e->ep_id, 0);
+
+ PR_Lock(cache->c_mutex);
+ if (! add_hash(cache->c_dntable, (void *)ndn, strlen(ndn), e,
+ (void **)&my_alt)) {
+ LOG("entry \"%s\" already in dn cache\n", backentry_get_ndn(e), 0, 0);
+ /* add_hash filled in 'my_alt' if necessary */
+ if (my_alt == e)
+ {
+ if ((e->ep_state & ENTRY_STATE_CREATING) && (state == 0))
+ {
+ /* attempting to "add" an entry that's already in the cache,
+ * and the old entry was a placeholder and the new one isn't?
+ * sounds like a confirmation of a previous add!
+ */
+ LOG("confirming a previous add\n", 0, 0, 0);
+ already_in = 1;
+ }
+ else
+ {
+ /* the entry already in the cache and either one of these:
+ * 1) ep_state: CREATING && state: CREATING
+ * ==> keep protecting the entry; increase the refcnt
+ * 2) ep_state: 0 && state: CREATING
+ * ==> change the state to CREATING (protect it);
+ * increase the refcnt
+ * 3) ep_state: 0 && state: 0
+ * ==> increase the refcnt
+ */
+ if (e->ep_refcnt == 0)
+ lru_delete(cache, e);
+ e->ep_refcnt++;
+ e->ep_state = state; /* might be CREATING */
+ /* returning 1 (entry already existed), but don't set to alt
+ * to prevent that the caller accidentally thinks the existing
+ * entry is not the same one the caller has and releases it.
+ */
+ PR_Unlock(cache->c_mutex);
+ return 1;
+ }
+ }
+ else
+ {
+ if (my_alt->ep_state & ENTRY_STATE_CREATING)
+ {
+ LOG("the entry is reserved\n", 0, 0, 0);
+ e->ep_state |= ENTRY_STATE_NOTINCACHE;
+ PR_Unlock(cache->c_mutex);
+ return -1;
+ }
+ else if (state != 0)
+ {
+ LOG("the entry already exists. cannot reserve it.\n", 0, 0, 0);
+ e->ep_state |= ENTRY_STATE_NOTINCACHE;
+ PR_Unlock(cache->c_mutex);
+ return -1;
+ }
+ else
+ {
+ if (alt) {
+ *alt = my_alt;
+ if ((*alt)->ep_refcnt == 0)
+ lru_delete(cache, *alt);
+ (*alt)->ep_refcnt++;
+ }
+ PR_Unlock(cache->c_mutex);
+ return 1;
+ }
+ }
+ }
+
+ /* creating an entry with ENTRY_STATE_CREATING just creates a stub
+ * which is only stored in the dn table (basically, reserving the dn) --
+ * doing an add later with state==0 will "confirm" the add
+ */
+ if (state == 0) {
+ /* neither of these should fail, or something is very wrong. */
+ if (! add_hash(cache->c_idtable, &(e->ep_id), sizeof(ID), e, NULL)) {
+ LOG("entry %s already in id cache!\n", backentry_get_ndn(e), 0, 0);
+ if (already_in) {
+ /* there's a bug in the implementatin of 'modify' and 'modrdn'
+ * that i'm working around here. basically they do a
+ * tentative add of the new (modified) entry, which places
+ * the new entry in the cache, indexed only by dn.
+ *
+ * later they call id2entry_add() on the new entry, which
+ * "adds" the new entry to the cache. unfortunately, that
+ * add will fail, since the old entry is still in the cache,
+ * and both the old and new entries have the same ID and UUID.
+ *
+ * i catch that here, and just return 0 for success, without
+ * messing with either entry. a later cache_replace() will
+ * remove the old entry and add the new one, and all will be
+ * fine (i think).
+ */
+ LOG("<= cache_add_int (ignoring)\n", 0, 0, 0);
+ PR_Unlock(cache->c_mutex);
+ return 0;
+ }
+ remove_hash(cache->c_dntable, (void *)ndn, strlen(ndn));
+ e->ep_state |= ENTRY_STATE_NOTINCACHE;
+ PR_Unlock(cache->c_mutex);
+ return -1;
+ }
+#ifdef UUIDCACHE_ON
+ if (uuid) {
+ /* (only insert entries with a uuid) */
+ if (! add_hash(cache->c_uuidtable, (void *)uuid, strlen(uuid), e,
+ NULL)) {
+ LOG("entry %s already in uuid cache!\n", backentry_get_ndn(e),
+ 0, 0);
+ remove_hash(cache->c_dntable, (void *)ndn, strlen(ndn));
+ remove_hash(cache->c_idtable, &(e->ep_id), sizeof(ID));
+ e->ep_state |= ENTRY_STATE_NOTINCACHE;
+ PR_Unlock(cache->c_mutex);
+ return -1;
+ }
+ }
+#endif
+ }
+
+ e->ep_state = state;
+
+ if (! already_in) {
+ e->ep_refcnt = 1;
+ e->size = cache_entry_size(e);
+
+ cache->c_cursize += e->size;
+ cache->c_curentries++;
+ /* don't add to lru since refcnt = 1 */
+ LOG("added entry of size %lu -> total now %lu out of max %lu\n",
+ e->size, cache->c_cursize, cache->c_maxsize);
+ if (cache->c_maxentries >= 0) {
+ LOG(" total entries %ld out of %ld\n",
+ cache->c_curentries, cache->c_maxentries, 0);
+ }
+ /* check for full cache, and clear out if necessary */
+ if (CACHE_FULL(cache))
+ eflush = cache_flush(cache);
+ }
+ PR_Unlock(cache->c_mutex);
+
+ while (eflush)
+ {
+ eflushtemp = eflush->ep_lrunext;
+ backentry_free(&eflush);
+ eflush = eflushtemp;
+ }
+ LOG("<= cache_add_int OK\n", 0, 0, 0);
+ return 0;
+}
+
+/* create an entry in the cache, and increase its refcount (you must
+ * return it when you're done).
+ * returns: 0 entry has been created & locked
+ * 1 entry already existed
+ * -1 something bad happened
+ *
+ * if 'alt' is not NULL, and the entry is found to already exist in the
+ * cache, a refcounted pointer to that entry will be placed in 'alt'.
+ * (this means code which suffered from race conditions between multiple
+ * entry modifiers can now work.)
+ */
+int cache_add(struct cache *cache, struct backentry *e,
+ struct backentry **alt)
+{
+ return cache_add_int(cache, e, 0, alt);
+}
+
+/* same as above, but add it tentatively: nobody else can use this entry
+ * from the cache until you later call cache_add.
+ */
+int cache_add_tentative(struct cache *cache, struct backentry *e,
+ struct backentry **alt)
+{
+ return cache_add_int(cache, e, ENTRY_STATE_CREATING, alt);
+}
+
+/* locks an entry so that it can be modified (you should have gotten the
+ * entry via cache_find_*).
+ * returns 0 on success, 1 if the entry is scheduled for deletion.
+ */
+int cache_lock_entry(struct cache *cache, struct backentry *e)
+{
+ LOG("=> cache_lock_entry (%s)\n", backentry_get_ndn(e), 0, 0);
+
+ if (! e->ep_mutexp) {
+ /* make sure only one thread does this */
+ PR_Lock(cache->c_emutexalloc_mutex);
+ if (! e->ep_mutexp)
+ e->ep_mutexp = PR_NewLock();
+ PR_Unlock(cache->c_emutexalloc_mutex);
+ }
+
+ /* wait on entry lock (done w/o holding the cache lock) */
+ PR_Lock(e->ep_mutexp);
+
+ /* make sure entry hasn't been deleted now */
+ PR_Lock(cache->c_mutex);
+ if (e->ep_state & (ENTRY_STATE_DELETED|ENTRY_STATE_NOTINCACHE)) {
+ PR_Unlock(cache->c_mutex);
+ PR_Unlock(e->ep_mutexp);
+ LOG("<= cache_lock_entry (DELETED)\n", 0, 0, 0);
+ return 1;
+ }
+ PR_Unlock(cache->c_mutex);
+
+ LOG("<= cache_lock_entry (FOUND)\n", 0, 0, 0);
+ return 0;
+}
+
+/* the opposite of above */
+void cache_unlock_entry(struct cache *cache, struct backentry *e)
+{
+ LOG("=> cache_unlock_entry\n", 0, 0, 0);
+ PR_Unlock(e->ep_mutexp);
+}
diff --git a/ldap/servers/slapd/back-ldbm/cleanup.c b/ldap/servers/slapd/back-ldbm/cleanup.c
new file mode 100644
index 00000000..ef539134
--- /dev/null
+++ b/ldap/servers/slapd/back-ldbm/cleanup.c
@@ -0,0 +1,51 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+/* cleanup.c - cleans up ldbm backend */
+
+#include "back-ldbm.h"
+
+int ldbm_back_cleanup( Slapi_PBlock *pb )
+{
+ struct ldbminfo *li;
+ Slapi_Backend *be;
+
+ LDAPDebug( LDAP_DEBUG_TRACE, "ldbm backend cleaning up\n", 0, 0, 0 );
+ slapi_pblock_get( pb, SLAPI_PLUGIN_PRIVATE, &li );
+ slapi_pblock_get( pb, SLAPI_BACKEND, &be );
+
+ if (be->be_state != BE_STATE_STOPPED &&
+ be->be_state != BE_STATE_DELETED)
+ {
+ LDAPDebug( LDAP_DEBUG_TRACE,
+ "ldbm_back_cleanup: warning - backend is in a wrong state - %d\n",
+ be->be_state, 0, 0 );
+ return 0;
+ }
+
+ PR_Lock (be->be_state_lock);
+
+ if (be->be_state != BE_STATE_STOPPED &&
+ be->be_state != BE_STATE_DELETED)
+ {
+ LDAPDebug( LDAP_DEBUG_TRACE,
+ "ldbm_back_cleanup: warning - backend is in a wrong state - %d\n",
+ be->be_state, 0, 0 );
+ PR_Unlock (be->be_state_lock);
+ return 0;
+ }
+
+ dblayer_terminate( li );
+
+/* JCM I tried adding this to tidy up memory on shutdown. */
+/* JCM But, the result was very messy. */
+/* JCM objset_delete(&li->li_instance_set); */
+
+ be->be_state = BE_STATE_CLEANED;
+
+ PR_Unlock (be->be_state_lock);
+
+ return 0;
+}
diff --git a/ldap/servers/slapd/back-ldbm/close.c b/ldap/servers/slapd/back-ldbm/close.c
new file mode 100644
index 00000000..ce2be3b9
--- /dev/null
+++ b/ldap/servers/slapd/back-ldbm/close.c
@@ -0,0 +1,52 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+/* close.c - close ldbm backend */
+
+#include "back-ldbm.h"
+
+int ldbm_back_close( Slapi_PBlock *pb )
+{
+ struct ldbminfo *li;
+
+ LDAPDebug( LDAP_DEBUG_TRACE, "ldbm backend syncing\n", 0, 0, 0 );
+ slapi_pblock_get( pb, SLAPI_PLUGIN_PRIVATE, &li );
+
+ /* Kill off any sleeping threads by setting this flag */
+ PR_Lock(li->li_shutdown_mutex);
+ li->li_shutdown = 1;
+ PR_Unlock(li->li_shutdown_mutex);
+
+ dblayer_flush( li ); /* just be doubly sure! */
+
+ /* close down all the ldbm instances */
+ dblayer_close( li, DBLAYER_NORMAL_MODE );
+
+ LDAPDebug( LDAP_DEBUG_TRACE, "ldbm backend done syncing\n", 0, 0, 0 );
+ return 0;
+}
+
+int ldbm_back_flush( Slapi_PBlock *pb )
+{
+ struct ldbminfo *li;
+
+ LDAPDebug( LDAP_DEBUG_TRACE, "ldbm backend flushing\n", 0, 0, 0 );
+ slapi_pblock_get( pb, SLAPI_PLUGIN_PRIVATE, &li );
+ dblayer_flush( li );
+ LDAPDebug( LDAP_DEBUG_TRACE, "ldbm backend done flushing\n", 0, 0, 0 );
+ return 0;
+}
+
+void ldbm_back_instance_set_destructor(void **arg)
+{
+ /*
+ Objset *instance_set = (Objset *) *arg;
+ */
+
+ /* This function is called when the instance set is destroyed.
+ * I can't really think of anything we should do here, but that
+ * may change in the future. */
+ LDAPDebug(LDAP_DEBUG_ANY, "Set of instances destroyed\n", 0, 0, 0);
+}
diff --git a/ldap/servers/slapd/back-ldbm/dblayer.c b/ldap/servers/slapd/back-ldbm/dblayer.c
new file mode 100644
index 00000000..c852b475
--- /dev/null
+++ b/ldap/servers/slapd/back-ldbm/dblayer.c
@@ -0,0 +1,5395 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2004 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+
+/*
+ Abstraction layer which sits between db2.0 and
+ higher layers in the directory server---typically
+ the back-end.
+ This module's purposes are 1) to hide messy stuff which
+ db2.0 needs, and with which we don't want to pollute the back-end
+ code. 2) Provide some degree of portability to other databases
+ if that becomes a requirement. Note that it is NOT POSSIBLE
+ to revert to db1.85 because the backend is now using features
+ from db2.0 which db1.85 does not have.
+ Also provides an emulation of the ldbm_ functions, for anyone
+ who is still calling those. The use of these functions is
+ deprecated. Only for backwards-compatibility.
+ Blame: dboreham
+*/
+
+/* Return code conventions:
+ Unless otherwise advertised, all the functions in this module
+ return an int which is zero if the operation was successful
+ and non-zero if it wasn't. If the return'ed value was > 0,
+ it can be interpreted as a system errno value. If it was < 0,
+ its meaning is defined in dblayer.h
+*/
+
+/*
+ Some information about how this stuff is to be used:
+
+ Call dblayer_init() near the beginning of the application's life.
+ This allocates some resources and allows the config line processing
+ stuff to work.
+ Call dblayer_start() when you're sure all config stuff has been seen.
+ This needs to be called before you can do anything else.
+ Call dblayer_close() when you're finished using the db and want to exit.
+ This closes and flushes all files opened by your application since calling
+ dblayer_start. If you do NOT call dblayer_close(), we assume that the
+ application crashed, and initiate recover next time you call dblayer_start().
+ Call dblayer_terminate() after close. This releases resources.
+
+ DB* handles are retrieved from dblayer via these functions:
+
+ dblayer_get_id2entry()
+ dblayer_get_index_file()
+
+ the caller must honour the protocol that these handles are released back
+ to dblayer when you're done using them, use thse functions to do this:
+
+ dblayer_release_id2entry()
+ dblayer_release_index_file()
+
+
+*/
+
+#include "back-ldbm.h"
+#include "dblayer.h"
+#include <prrwlock.h>
+
+#if 1000*DB_VERSION_MAJOR + 100*DB_VERSION_MINOR >= 4100
+#define DB_OPEN(oflags, db, txnid, file, database, type, flags, mode, rval) \
+{ \
+ if (((oflags) & DB_INIT_TXN) && ((oflags) & DB_INIT_LOG)) \
+ { \
+ (rval) = (db)->open((db), (txnid), (file), (database), (type), (flags)|DB_AUTO_COMMIT, (mode)); \
+ } \
+ else \
+ { \
+ (rval) = (db)->open((db), (txnid), (file), (database), (type), (flags), (mode)); \
+ } \
+}
+/* 608145: db4.1 and newer does not require exclusive lock for checkpointing
+ * and transactions */
+#define DB_CHECKPOINT_LOCK(use_lock, lock) ;
+#define DB_CHECKPOINT_UNLOCK(use_lock, lock) ;
+#else /* older then db 41 */
+#define DB_OPEN(oflags, db, txnid, file, database, type, flags, mode, rval) \
+ (rval) = (db)->open((db), (file), (database), (type), (flags), (mode))
+#define DB_CHECKPOINT_LOCK(use_lock, lock) if(use_lock) PR_RWLock_Wlock(lock);
+#define DB_CHECKPOINT_UNLOCK(use_lock, lock) if(use_lock) PR_RWLock_Unlock(lock);
+#endif
+
+#if 1000*DB_VERSION_MAJOR + 100*DB_VERSION_MINOR >= 4000
+#define DB_ENV_SET_REGION_INIT(env) (env)->set_flags((env), DB_REGION_INIT, 1)
+#define DB_ENV_SET_TAS_SPINS(env, tas_spins) \
+ (env)->set_tas_spins((env), (tas_spins))
+#define TXN_BEGIN(env, parent_txn, tid, flags) \
+ (env)->txn_begin((env), (parent_txn), (tid), (flags))
+#define TXN_COMMIT(txn, flags) (txn)->commit((txn), (flags))
+#define TXN_ABORT(txn) (txn)->abort(txn)
+#define TXN_CHECKPOINT(env, kbyte, min, flags) \
+ (env)->txn_checkpoint((env), (kbyte), (min), (flags))
+#define MEMP_STAT(env, gsp, fsp, flags, malloc) \
+ (env)->memp_stat((env), (gsp), (fsp), (flags))
+#define MEMP_TRICKLE(env, pct, nwrotep) \
+ (env)->memp_trickle((env), (pct), (nwrotep))
+#define LOG_ARCHIVE(env, listp, flags, malloc) \
+ (env)->log_archive((env), (listp), (flags))
+#define LOG_FLUSH(env, lsn) (env)->log_flush((env), (lsn))
+#define LOCK_DETECT(env, flags, atype, aborted) \
+ (env)->lock_detect((env), (flags), (atype), (aborted))
+
+#else /* older than db 4.0 */
+#define DB_ENV_SET_REGION_INIT(env) db_env_set_region_init(1)
+#define DB_ENV_SET_TAS_SPINS(env, tas_spins) \
+ db_env_set_tas_spins((tas_spins))
+#define TXN_BEGIN(env, parent_txn, tid, flags) \
+ txn_begin((env), (parent_txn), (tid), (flags))
+#define TXN_COMMIT(txn, flags) txn_commit((txn), (flags))
+#define TXN_ABORT(txn) txn_abort((txn))
+#define TXN_CHECKPOINT(env, kbyte, min, flags) \
+ txn_checkpoint((env), (kbyte), (min), (flags))
+#define MEMP_TRICKLE(env, pct, nwrotep) memp_trickle((env), (pct), (nwrotep))
+#define LOG_FLUSH(env, lsn) log_flush((env), (lsn))
+#define LOCK_DETECT(env, flags, atype, aborted) \
+ lock_detect((env), (flags), (atype), (aborted))
+
+#if 1000*DB_VERSION_MAJOR + 100*DB_VERSION_MINOR >= 3300
+#define MEMP_STAT(env, gsp, fsp, flags, malloc) memp_stat((env), (gsp), (fsp))
+#define LOG_ARCHIVE(env, listp, flags, malloc) \
+ log_archive((env), (listp), (flags))
+
+#else /* older than db 3.3 */
+#define MEMP_STAT(env, gsp, fsp, flags, malloc) \
+ memp_stat((env), (gsp), (fsp), (malloc))
+#define LOG_ARCHIVE(env, listp, flags, malloc) \
+ log_archive((env), (listp), (flags), (malloc))
+#endif
+#endif
+
+static int perf_threadmain(void *param);
+static int checkpoint_threadmain(void *param);
+static int trickle_threadmain(void *param);
+static int deadlock_threadmain(void *param);
+static int commit_good_database(dblayer_private *priv);
+static int read_metadata(struct ldbminfo *li);
+static int count_dbfiles_in_dir(char *directory, int *count, int recurse);
+static int dblayer_override_libdb_functions(DB_ENV *pEnv, dblayer_private *priv);
+static int dblayer_force_checkpoint(struct ldbminfo *li);
+static int log_flush_threadmain(void *param);
+static int dblayer_delete_transaction_logs(const char * log_dir);
+
+static int dblayer_start_log_flush_thread(dblayer_private *priv);
+static int dblayer_start_deadlock_thread(struct ldbminfo *li);
+static int dblayer_start_checkpoint_thread(struct ldbminfo *li);
+static int dblayer_start_trickle_thread(struct ldbminfo *li);
+static int dblayer_start_perf_thread(struct ldbminfo *li);
+static int trans_batch_count=1;
+static int trans_batch_limit=0;
+static PRBool log_flush_thread=PR_FALSE;
+static int dblayer_db_remove_ex(dblayer_private_env *env, char const path[], char const dbName[], PRBool use_lock);
+static char* last_four_chars(const char* s);
+
+#define MEGABYTE (1024 * 1024)
+#define GIGABYTE (1024 * MEGABYTE)
+
+/* this flag use if user remotely turned batching off */
+
+#define FLUSH_REMOTEOFF -1
+/* routine that allows batch value to be changed remotely:
+
+ 1. value = 0 turns batching off
+ 2. value = 1 makes behavior be like 5.0 but leaves batching on
+ 3. value > 1 changes batch value
+
+ 2 and 3 assume that nsslapd-db-transaction-batch-val is greater 0 at startup
+*/
+
+int
+dblayer_set_batch_transactions(void *arg, void *value, char *errorbuf, int phase, int apply) {
+ int val = (int) value;
+ int retval = LDAP_SUCCESS;
+
+ if (apply) {
+ if(phase == CONFIG_PHASE_STARTUP) {
+ trans_batch_limit=val;
+ } else if(trans_batch_limit != FLUSH_REMOTEOFF ) {
+ if((val == 0) && (log_flush_thread)) {
+ log_flush_thread=PR_FALSE;
+ trans_batch_limit = FLUSH_REMOTEOFF;
+ } else if(val > 0) {
+ trans_batch_limit=val;
+ }
+ }
+ }
+ return retval;
+}
+
+void *
+dblayer_get_batch_transactions(void *arg) {
+ return (void *)trans_batch_limit;
+}
+
+
+/*
+ Threading: dblayer isolates upper layers from threading considerations
+ Everything in dblayer is free-threaded. That is, you can have multiple
+ threads performing operations on a database and not worry about things.
+ Obviously, if you do something stupid, like move a cursor forward in
+ one thread, and backwards in another at the same time, you get what you
+ deserve. However, such a calling pattern will not crash your application !
+*/
+
+static int
+dblayer_txn_checkpoint(struct ldbminfo *li, struct dblayer_private_env *env,
+ PRBool use_lock, PRBool busy_skip)
+{
+ int ret = 0;
+ if (busy_skip && is_anyinstance_busy(li))
+ {
+ return ret;
+ }
+ DB_CHECKPOINT_LOCK(use_lock, env->dblayer_env_lock);
+ ret = TXN_CHECKPOINT(env->dblayer_DB_ENV, 0, 0, DB_FORCE);
+ DB_CHECKPOINT_UNLOCK(use_lock, env->dblayer_env_lock);
+ return ret;
+}
+
+static int _dblayer_check_version(dblayer_private *priv)
+{
+ int major, minor = 0;
+ char *string = 0;
+ int ret = 0;
+
+ string = db_version(&major,&minor,NULL);
+ if (major < DB_VERSION_MAJOR)
+ {
+ ret = -1;
+ }
+ else
+ {
+ ret = 0;
+ }
+ /* DB3X: always POST 24 :) */
+ priv->dblayer_lib_version = DBLAYER_LIB_VERSION_POST_24;
+ LDAPDebug(LDAP_DEBUG_TRACE,"version check: %s (%d.%d)\n", string, major, minor);
+ return ret;
+}
+
+
+/*
+ * return nsslapd-db-home-directory (dblayer_dbhome_directory), if exists.
+ * Otherwise, return nsslapd-directory (dblayer_home_directory).
+ *
+ * if dblayer_dbhome_directory exists, set 1 to dbhome.
+ */
+char *
+dblayer_get_home_dir(struct ldbminfo *li, int *dbhome)
+{
+ dblayer_private *priv = (dblayer_private*)li->li_dblayer_private;
+ char *home_dir = priv->dblayer_home_directory;
+ if (dbhome)
+ *dbhome = 0;
+
+ if (priv->dblayer_dbhome_directory && *(priv->dblayer_dbhome_directory))
+ {
+ if (dbhome)
+ *dbhome = 1;
+ home_dir = priv->dblayer_dbhome_directory;
+ }
+ if (NULL == home_dir)
+ {
+ LDAPDebug(LDAP_DEBUG_ANY,"Db home directory is not set. "
+ "Possibly %s (optinally %s) is missing in the config file.\n",
+ CONFIG_DIRECTORY, CONFIG_DB_HOME_DIRECTORY, 0);
+ }
+ return home_dir;
+}
+
+/* Helper function which deletes the persistent state of the database library
+ * IMHO this should be in inside libdb, but keith won't have it.
+ * Stop press---libdb now does delete these files on recovery, so we don't call this any more.
+ */
+static void dblayer_reset_env(struct ldbminfo *li)
+{
+ /* Remove the memory regions */
+ dblayer_private *priv = (dblayer_private*)li->li_dblayer_private;
+ DB_ENV *pEnv = priv->dblayer_env->dblayer_DB_ENV;
+ char *home_dir = dblayer_get_home_dir(li, NULL);
+ if (home_dir)
+ pEnv->remove(pEnv, home_dir, DB_FORCE);
+}
+
+/* Callback function for libdb to spit error info into our log */
+static void dblayer_log_print(const char* prefix, char *buffer)
+{
+ /* We ignore the prefix since we know who we are anyway */
+ LDAPDebug(LDAP_DEBUG_ANY,"libdb: %s\n", buffer, 0, 0);
+}
+
+void dblayer_remember_disk_filled(struct ldbminfo *li)
+{
+ dblayer_private *priv = NULL;
+
+ PR_ASSERT(NULL != li);
+ priv = li->li_dblayer_private;
+ PR_ASSERT(NULL != priv);
+
+ priv->dblayer_bad_stuff_happened = 1;
+
+}
+
+/* Function which calls libdb to override some system calls which
+ * the library makes. We call this before calling any other function
+ * in libdb.
+ * Several OS use this, either partially or completely.
+ * This will eventually change---we will simply pass to libdb
+ * the addresses of a bunch of NSPR functions, and everything
+ * will magically work on all platforms (Ha!)
+ */
+
+#ifdef DB_USE_64LFS
+/* What is going on here ?
+ * Well, some platforms now support an extended API for dealing with
+ * files larger than 2G. (This apparently comes from the LFS -- "Large
+ * File Summit"... Summit, indeed.) Anyway, we try to detect at runtime
+ * whether this machine has the extended API, and use it if it's present.
+ *
+ */
+
+
+/* helper function for open64 */
+static int dblayer_open_large(const char *path, int oflag, mode_t mode)
+{
+ int err;
+
+ err = open64(path, oflag, mode);
+ /* weird but necessary: */
+ if (err >= 0) errno = 0;
+ return err;
+}
+
+/* this is REALLY dumb. but nspr 19980529(x) doesn't support 64-bit files
+ * because of some weirdness we're doing at initialization (?), so we need
+ * to export some function that can open huge files, so that exporting
+ * can work right. when we fix the nspr problem (or get a more recent
+ * version of nspr that might magically work?), this should be blown away.
+ * (call mode_t an int because NT can't handle that in prototypes.)
+ * -robey, 28oct98
+ */
+int dblayer_open_huge_file(const char *path, int oflag, int mode)
+{
+ return dblayer_open_large(path, oflag, (mode_t)mode);
+}
+
+
+/* Helper function for large seeks, db2.4 */
+static int dblayer_seek24_large(int fd, size_t pgsize, db_pgno_t pageno,
+ u_long relative, int isrewind, int whence)
+{
+ off64_t offset = 0, ret;
+
+ offset = (off64_t)pgsize * pageno + relative;
+ if (isrewind) offset = -offset;
+ ret = lseek64(fd, offset, whence);
+
+ return (ret < 0) ? errno : 0;
+}
+
+/* helper function for large fstat -- this depends on 'struct stat64' having
+ * the following members:
+ * off64_t st_size;
+ * long st_blksize;
+ */
+static int dblayer_ioinfo_large(const char *path, int fd, u_int32_t *mbytesp,
+ u_int32_t *bytesp, u_int32_t *iosizep)
+{
+ struct stat64 sb;
+
+ if (fstat64(fd, &sb) < 0)
+ return (errno);
+
+ /* Return the size of the file. */
+ if (mbytesp)
+ *mbytesp = (u_int32_t) (sb.st_size / (off64_t) MEGABYTE);
+ if (bytesp)
+ *bytesp = (u_int32_t) (sb.st_size % (off64_t) MEGABYTE);
+
+ if (iosizep)
+ *iosizep = (u_int32_t)(sb.st_blksize);
+ return 0;
+}
+/* Helper function to tell if a file exists */
+/* On Solaris, if you use stat() on a file >4Gbytes, it fails with EOVERFLOW,
+ causing us to think that the file does not exist when it in fact does */
+static int dblayer_exists_large(char *path, int *isdirp)
+{
+ struct stat64 sb;
+
+ if (stat64(path, &sb) != 0)
+ return (errno);
+
+ if (isdirp != NULL)
+ *isdirp = S_ISDIR(sb.st_mode);
+
+ return (0);
+}
+
+#else /* DB_USE_64LFS */
+
+int dblayer_open_huge_file(const char *path, int oflag, int mode)
+{
+ return open(path, oflag, mode);
+}
+
+#endif /* DB_USE_64LFS */
+
+
+static int dblayer_override_libdb_functions(DB_ENV *pEnv, dblayer_private *priv)
+{
+#ifdef DB_USE_64LFS
+ int major = 0;
+ int minor = 0;
+
+ /* Find out whether we are talking to a 2.3 or 2.4+ libdb */
+ db_version(&major, &minor, NULL);
+
+#ifndef irix
+ /* irix doesn't have open64() */
+ db_env_set_func_open((int (*)(const char *, int, ...))dblayer_open_large);
+#endif /* !irix */
+ db_env_set_func_ioinfo(dblayer_ioinfo_large);
+ db_env_set_func_exists((int (*)())dblayer_exists_large);
+ db_env_set_func_seek((int (*)())dblayer_seek24_large);
+
+ LDAPDebug(LDAP_DEBUG_TRACE, "Enabled 64-bit files\n", 0, 0, 0);
+#endif /* DB_USE_64LFS */
+ return 0;
+}
+
+
+
+/* This function is called in the initialization code, before the
+ * config file is read in, so we can't do much here
+ */
+int dblayer_init(struct ldbminfo *li)
+{
+ /* Allocate memory we need, create mutexes etc. */
+ dblayer_private *priv = NULL;
+ int ret = 0;
+
+ PR_ASSERT(NULL != li);
+ if (NULL != li->li_dblayer_private)
+ {
+ return -1;
+ }
+
+ priv = (dblayer_private*) slapi_ch_calloc(1,sizeof(dblayer_private));
+ if (NULL == priv)
+ {
+ /* Memory allocation failed */
+ return -1;
+ }
+ li->li_dblayer_private = priv;
+
+ /* For now, we call this to get debug printout */
+ _dblayer_check_version(priv);
+
+ /* moved db_env_create to dblayer_start */
+ return ret;
+}
+
+int dblayer_terminate(struct ldbminfo *li)
+{
+ /* We assume that dblayer_close has been called already */
+ dblayer_private *priv = (dblayer_private*)li->li_dblayer_private;
+ Object *inst_obj;
+ ldbm_instance *inst;
+ int rval = 0;
+
+ if (NULL == priv) /* already terminated. nothing to do */
+ return rval;
+
+ /* clean up mutexes */
+ for (inst_obj = objset_first_obj(li->li_instance_set); inst_obj;
+ inst_obj = objset_next_obj(li->li_instance_set, inst_obj)) {
+ inst = (ldbm_instance *)object_get_data(inst_obj);
+ if (NULL != inst->inst_db_mutex) {
+ PR_DestroyLock(inst->inst_db_mutex);
+ }
+ if (NULL != inst->inst_handle_list_mutex) {
+ PR_DestroyLock(inst->inst_handle_list_mutex);
+ }
+ }
+
+ slapi_ch_free_string(&priv->dblayer_log_directory);
+ /* no need to release dblayer_home_directory,
+ * which is one of dblayer_data_directories */
+ charray_free(priv->dblayer_data_directories);
+ slapi_ch_free((void**)&priv);
+ li->li_dblayer_private = NULL;
+
+ return 0;
+}
+
+static void dblayer_select_ncache(size_t cachesize, int *ncachep)
+{
+ /* First thing, if the user asked to use a particular ncache,
+ * we let them, and don't override it here.
+ */
+ if (*ncachep) {
+ return;
+ }
+ /* If the user asked for a cache that's larger than 4G,
+ * we _must_ select an ncache >0 , such that each
+ * chunk is <4G. This is because DB won't accept a
+ * larger chunk.
+ */
+#if defined(__LP64__) || defined (_LP64)
+ if ( (sizeof(cachesize) > 4) && (cachesize > (4L * GIGABYTE))) {
+ *ncachep = (cachesize / (4L * GIGABYTE)) + 1;
+ LDAPDebug(LDAP_DEBUG_ANY,"Setting ncache to: %d to keep each chunk below 4Gbytes\n",
+ *ncachep, 0, 0);
+ }
+#endif
+ /* On Windows, we know that it's hard to allocate more than some
+ * maximum chunk. In that case
+ * we set ncache to a sensible value.
+ */
+#if defined(_WIN32)
+ {
+ size_t max_windows_chunk = (300 * MEGABYTE); /* This number was determined empirically on Win2k */
+ if (cachesize > max_windows_chunk) {
+ *ncachep = (cachesize / max_windows_chunk) + 1;
+ LDAPDebug(LDAP_DEBUG_ANY,"Setting ncache to: %d for Windows memory address space fragmentation\n",
+ *ncachep, 0, 0);
+ }
+ }
+#endif
+}
+
+/* This function is no longer called :
+ It attempts to find the maximum allocatable chunk size
+ by performing the actual allocations. However as a result
+ it allocates pretty much _all_ the available memory,
+ causing the actual cache allocation to fail later.
+ If there turns out to be a good safe way to determine
+ the maximum chunk size, then we should use that instead.
+ For now we just guess in dblayer_pick_ncache().
+ */
+static void dblayer_get_ncache(size_t cachesize, int *ncachep)
+{
+ int myncache;
+ int mymaxncache;
+ int found = 0;
+ char **head;
+
+ if (*ncachep <= 0) /* negative ncache is not allowed */
+ myncache = 1;
+ else
+ myncache = *ncachep;
+
+ mymaxncache = myncache + 20; /* should be reasonable */
+
+ head = (char **)slapi_ch_malloc(mymaxncache * sizeof(char *));
+ do {
+ int i;
+ int end;
+ size_t sz;
+ size_t firstsz;
+ size_t rest;
+
+ rest = cachesize % myncache;
+ sz = cachesize / myncache;
+ firstsz = sz + rest;
+ end = myncache;
+ for (i = 0; i < myncache; i++) {
+ if (i == 0)
+ head[i] = (char *)malloc(firstsz);
+ else
+ head[i] = (char *)malloc(sz);
+ if (NULL == head[i]) {
+ end = i;
+ myncache++;
+ goto cleanup;
+ }
+ }
+ found = 1;
+cleanup:
+ for (i = 0; i < end; i++) {
+ slapi_ch_free((void **)&head[i]);
+ }
+ if (myncache == mymaxncache) {
+ LDAPDebug(LDAP_DEBUG_ANY,
+ "WARNING: dbcachesize %lu too big\n", cachesize, 0, 0);
+ myncache = 0;
+ found = -1;
+ }
+ } while (0 == found);
+ *ncachep = myncache;
+ slapi_ch_free((void **)&head);
+ return;
+}
+
+static void dblayer_init_dbenv(DB_ENV *pEnv, dblayer_private *priv)
+{
+ size_t mysize;
+ int myncache = 1;
+
+ mysize = priv->dblayer_cachesize;
+ myncache = priv->dblayer_ncache;
+ dblayer_select_ncache(mysize, &myncache);
+ priv->dblayer_ncache = myncache;
+
+ pEnv->set_errpfx(pEnv, "ns-slapd");
+ pEnv->set_lg_max(pEnv, priv->dblayer_logfile_size);
+ pEnv->set_cachesize(pEnv, mysize / GIGABYTE, mysize % GIGABYTE, myncache);
+ pEnv->set_lk_max_locks(pEnv, priv->dblayer_lock_config);
+ pEnv->set_lk_max_objects(pEnv, priv->dblayer_lock_config);
+ pEnv->set_lk_max_lockers(pEnv, priv->dblayer_lock_config);
+ if (priv->dblayer_verbose) {
+ pEnv->set_verbose(pEnv, DB_VERB_CHKPOINT, 1); /* 1 means on */
+ pEnv->set_verbose(pEnv, DB_VERB_DEADLOCK, 1); /* 1 means on */
+ pEnv->set_verbose(pEnv, DB_VERB_RECOVERY, 1); /* 1 means on */
+ pEnv->set_verbose(pEnv, DB_VERB_WAITSFOR, 1); /* 1 means on */
+ }
+ if (priv->dblayer_debug) {
+ pEnv->set_errcall(pEnv, dblayer_log_print);
+ }
+
+ /* shm_key required for named_regions (DB_SYSTEM_MEM) */
+ pEnv->set_shm_key(pEnv, priv->dblayer_shm_key);
+
+ /* increase max number of active transactions */
+ pEnv->set_tx_max(pEnv, priv->dblayer_tx_max);
+
+#if 1000*DB_VERSION_MAJOR + 100*DB_VERSION_MINOR >= 3300
+ pEnv->set_alloc(pEnv, malloc, realloc, free);
+
+ /*
+ * The log region is used to store filenames and so needs to be
+ * increased in size from the default for a large number of files.
+ */
+ pEnv->set_lg_regionmax(pEnv, 1 * 1048576); /* 1 MB */
+#endif
+}
+
+/* returns system pagesize (in bytes) and the number of pages of physical
+ * RAM this machine has.
+ * as a bonus, if 'procpages' is non-NULL, it will be filled in with the
+ * approximate number of pages this process is using!
+ * on platforms that we haven't figured out how to do this yet, both fields
+ * are filled with zero and you're on your own.
+ *
+ * platforms supported so far:
+ * Solaris, Linux, Windows
+ */
+#ifdef OS_solaris
+#include <sys/procfs.h>
+#include <sys/resource.h>
+#endif
+#ifdef LINUX
+#include <linux/kernel.h>
+#include <linux/sys.h>
+#include <sys/sysinfo.h> /* undocumented (?) */
+#include <sys/resource.h>
+#endif
+#if defined ( hpux )
+#include <sys/pstat.h>
+#include <sys/resource.h>
+#endif
+
+#if !defined(_WIN32)
+static size_t dblayer_getvirtualmemsize()
+{
+ struct rlimit rl;
+
+ /* the maximum size of a process's total available memory, in bytes */
+ getrlimit(RLIMIT_AS, &rl);
+ return rl.rlim_cur;
+}
+#endif
+
+/* pages = number of pages of physical ram on the machine (corrected for 32-bit build on 64-bit machine).
+ * procpages = pages currently used by this process (or working set size, sometimes)
+ * availpages = some notion of the number of pages 'free'. Typically this number is not useful.
+ */
+void dblayer_sys_pages(size_t *pagesize, size_t *pages, size_t *procpages, size_t *availpages)
+{
+ *pagesize = *pages = *availpages = 0;
+ if (procpages)
+ *procpages = 0;
+
+#ifdef _WIN32
+ {
+ SYSTEM_INFO si;
+ MEMORYSTATUS ms;
+
+ GetSystemInfo(&si);
+ ms.dwLength = sizeof(ms);
+ GlobalMemoryStatus(&ms);
+ *pagesize = si.dwPageSize;
+ *pages = ms.dwTotalPhys / si.dwPageSize;
+ *availpages = ms.dwAvailVirtual / *pagesize;
+ if (procpages) {
+ DWORD minwss = 0, maxwss = 0;
+
+ GetProcessWorkingSetSize(GetCurrentProcess(), &minwss, &maxwss);
+ *procpages = (int)(maxwss / si.dwPageSize);
+ }
+ }
+#endif
+
+#ifdef OS_solaris
+ *pagesize = (int)sysconf(_SC_PAGESIZE);
+ *pages = (int)sysconf(_SC_PHYS_PAGES);
+ *availpages = dblayer_getvirtualmemsize() / *pagesize;
+ /* solaris has THE most annoying way to get this info */
+ if (procpages) {
+ struct prpsinfo psi;
+ char fn[40];
+ int fd;
+
+ sprintf(fn, "/proc/%d", getpid());
+ fd = open(fn, O_RDONLY);
+ if (fd >= 0) {
+ memset(&psi, 0, sizeof(psi));
+ if (ioctl(fd, PIOCPSINFO, (void *)&psi) == 0)
+ *procpages = psi.pr_size;
+ close(fd);
+ }
+ }
+#endif
+
+#ifdef LINUX
+ {
+ struct sysinfo si;
+ size_t pages_per_mem_unit = 0;
+ size_t mem_units_per_page = 0; /* We don't know if these units are really pages */
+
+ sysinfo(&si);
+ *pagesize = getpagesize();
+ if (si.mem_unit > *pagesize) {
+ pages_per_mem_unit = si.mem_unit / *pagesize;
+ *pages = si.totalram * pages_per_mem_unit;
+ } else {
+ mem_units_per_page = *pagesize / si.mem_unit;
+ *pages = si.totalram / mem_units_per_page;
+ }
+ *availpages = dblayer_getvirtualmemsize() / *pagesize;
+ /* okay i take that back, linux's method is more retarded here.
+ * hopefully linux doesn't have the FILE* problem that solaris does
+ * (where you can't use FILE if you have more than 256 fd's open)
+ */
+ if (procpages) {
+ FILE *f;
+ char fn[40], s[80];
+
+ sprintf(fn, "/proc/%d/status", getpid());
+ f = fopen(fn, "r");
+ while (! feof(f)) {
+ fgets(s, 79, f);
+ if (feof(f))
+ break;
+ if (strncmp(s, "VmSize:", 7) == 0) {
+ sscanf(s+7, "%d", procpages);
+ break;
+ }
+ }
+ fclose(f);
+ /* procpages is now in 1k chunks, not pages... */
+ *procpages /= (*pagesize / 1024);
+ }
+ }
+#endif
+
+#if defined ( hpux )
+ {
+ struct pst_static pst;
+ struct pst_dynamic pst_dyn;
+ int rval = pstat_getstatic(&pst, sizeof(pst), (size_t)1, 0);
+ if (rval < 0) /* pstat_getstatic failed */
+ return;
+ *pagesize = pst.page_size;
+ *pages = pst.physical_memory;
+ *availpages = dblayer_getvirtualmemsize() / *pagesize;
+ rval = pstat_getdynamic(&pst_dyn, sizeof(pst_dyn), (size_t)1, 0);
+ if (rval < 0) /* pstat_getdynamic failed */
+ return;
+ if (procpages)
+ {
+#define BURST (size_t)32 /* get BURST proc info at one time... */
+ struct pst_status psts[BURST];
+ int i, count;
+ int idx = 0; /* index within the context */
+ int mypid = getpid();
+
+ *procpages = 0;
+ /* loop until count == 0, will occur all have been returned */
+ while ((count = pstat_getproc(psts, sizeof(psts[0]), BURST, idx)) > 0) {
+ /* got count (max of BURST) this time. process them */
+ for (i = 0; i < count; i++) {
+ if (psts[i].pst_pid == mypid)
+ {
+ *procpages = (size_t)(psts[i].pst_dsize + psts[i].pst_tsize + psts[i].pst_ssize);
+ break;
+ }
+ }
+ if (i < count)
+ break;
+
+ /*
+ * now go back and do it again, using the next index after
+ * the current 'burst'
+ */
+ idx = psts[count-1].pst_idx + 1;
+ }
+ }
+ }
+#endif
+ /* If this is a 32-bit build, it might be running on a 64-bit machine,
+ * in which case, if the box has tons of ram, we can end up telling
+ * the auto cache code to use more memory than the process can address.
+ * so we cap the number returned here.
+ */
+#if defined(__LP64__) || defined (_LP64)
+#else
+ {
+ size_t one_gig_pages = GIGABYTE / *pagesize;
+ if (*pages > (2 * one_gig_pages) ) {
+ LDAPDebug(LDAP_DEBUG_TRACE,"More than 2Gbytes physical memory detected. Since this is a 32-bit process, truncating memory size used for auto cache calculations to 2Gbytes\n",
+ 0, 0, 0);
+ *pages = (2 * one_gig_pages);
+ }
+ }
+#endif
+}
+
+
+int dblayer_is_cachesize_sane(size_t *cachesize)
+{
+ size_t pages = 0, pagesize = 0, procpages = 0, availpages = 0;
+ int issane = 1;
+
+ dblayer_sys_pages(&pagesize, &pages, &procpages, &availpages);
+ if (!pagesize || !pages)
+ return 1; /* do nothing when we can't get the avail mem */
+ /* If the requested cache size is larger than the remaining pysical memory
+ * after the current working set size for this process has been subtracted,
+ * then we say that's insane and try to correct.
+ */
+ issane = (int)(*cachesize / pagesize) <= (pages - procpages);
+ if (!issane) {
+ *cachesize = (size_t)((pages - procpages) * pagesize);
+ }
+ /* We now compensate for DB's own compensation for metadata size
+ * They increase the actual cache size by 25%, but only for sizes
+ * less than 500Meg.
+ */
+ if (*cachesize < 500*MEGABYTE) {
+ *cachesize = (size_t)((double)*cachesize * (double)0.8);
+ }
+
+ return issane;
+}
+
+
+static void dblayer_dump_config_tracing(dblayer_private *priv)
+{
+ if (priv->dblayer_home_directory) {
+ LDAPDebug(LDAP_DEBUG_TRACE,"home_directory=%s\n",priv->dblayer_home_directory,0,0);
+ }
+ if (priv->dblayer_log_directory) {
+ LDAPDebug(LDAP_DEBUG_TRACE,"log_directory=%s\n",priv->dblayer_log_directory,0,0);
+ }
+ if (priv->dblayer_dbhome_directory) {
+ LDAPDebug(LDAP_DEBUG_TRACE,"dbhome_directory=%s\n",priv->dblayer_dbhome_directory,0,0);
+ }
+ LDAPDebug(LDAP_DEBUG_TRACE,"trickle_percentage=%d\n",priv->dblayer_trickle_percentage,0,0);
+ LDAPDebug(LDAP_DEBUG_TRACE,"page_size=%lu\n",priv->dblayer_page_size,0,0);
+ LDAPDebug(LDAP_DEBUG_TRACE,"index_page_size=%lu\n",priv->dblayer_index_page_size,0,0);
+ LDAPDebug(LDAP_DEBUG_TRACE,"cachesize=%lu\n",priv->dblayer_cachesize,0,0);
+ LDAPDebug(LDAP_DEBUG_TRACE,"previous_cachesize=%lu\n",priv->dblayer_previous_cachesize,0,0);
+ LDAPDebug(LDAP_DEBUG_TRACE,"ncache=%d\n",priv->dblayer_ncache,0,0);
+ LDAPDebug(LDAP_DEBUG_TRACE,"previous_ncache=%d\n",priv->dblayer_previous_ncache,0,0);
+ LDAPDebug(LDAP_DEBUG_TRACE,"recovery_required=%d\n",priv->dblayer_recovery_required,0,0);
+ LDAPDebug(LDAP_DEBUG_TRACE,"durable_transactions=%d\n",priv->dblayer_durable_transactions,0,0);
+ LDAPDebug(LDAP_DEBUG_TRACE,"checkpoint_interval=%d\n",priv->dblayer_checkpoint_interval,0,0);
+ LDAPDebug(LDAP_DEBUG_TRACE,"transaction_batch_val=%d\n",trans_batch_limit,0,0);
+ LDAPDebug(LDAP_DEBUG_TRACE,"circular_logging=%d\n",priv->dblayer_circular_logging,0,0);
+ LDAPDebug(LDAP_DEBUG_TRACE,"idl_divisor=%d\n",priv->dblayer_idl_divisor,0,0);
+ LDAPDebug(LDAP_DEBUG_TRACE,"logfile_size=%lu\n",priv->dblayer_logfile_size,0,0);
+ LDAPDebug(LDAP_DEBUG_TRACE,"logbuf_size=%lu\n",priv->dblayer_logbuf_size,0,0);
+ LDAPDebug(LDAP_DEBUG_TRACE,"file_mode=%d\n",priv->dblayer_file_mode,0,0);
+ LDAPDebug(LDAP_DEBUG_TRACE,"cache_config=%d\n",priv->dblayer_cache_config,0,0);
+ LDAPDebug(LDAP_DEBUG_TRACE,"lib_version=%d\n",priv->dblayer_lib_version,0,0);
+ LDAPDebug(LDAP_DEBUG_TRACE,"spin_count=%d\n",priv->dblayer_spin_count,0,0);
+ LDAPDebug(LDAP_DEBUG_TRACE,"named_regions=%d\n",priv->dblayer_named_regions,0,0);
+ LDAPDebug(LDAP_DEBUG_TRACE,"private mem=%d\n",priv->dblayer_private_mem,0,0);
+ LDAPDebug(LDAP_DEBUG_TRACE,"private import mem=%d\n",priv->dblayer_private_import_mem,0,0);
+ LDAPDebug(LDAP_DEBUG_TRACE,"shm_key=%ld\n",priv->dblayer_shm_key,0,0);
+ LDAPDebug(LDAP_DEBUG_TRACE,"lockdown=%d\n",priv->dblayer_lockdown,0,0);
+ LDAPDebug(LDAP_DEBUG_TRACE,"tx_max=%d\n",priv->dblayer_tx_max,0,0);
+}
+
+/* Check a given filesystem directory for access we need */
+#define DBLAYER_DIRECTORY_READ_ACCESS 1
+#define DBLAYER_DIRECTORY_WRITE_ACCESS 2
+#define DBLAYER_DIRECTORY_READWRITE_ACCESS 3
+static int dblayer_grok_directory(char *directory, int flags)
+{
+ /* First try to open the directory using NSPR */
+ /* If that fails, we can tell whether it's because it cannot be created or
+ * we don't have any permission to access it */
+ /* If that works, proceed to try to access files in the directory */
+ char filename[MAXPATHLEN];
+ PRDir *dirhandle = NULL;
+ PRDirEntry *direntry = NULL;
+ PRFileInfo info;
+
+ dirhandle = PR_OpenDir(directory);
+ if (NULL == dirhandle)
+ {
+ /* it does not exist or wrong file is there */
+ /* try delete and mkdir */
+ PR_Delete(directory);
+ return mkdir_p(directory, 0700);
+ }
+
+ while (NULL !=
+ (direntry = PR_ReadDir(dirhandle, PR_SKIP_DOT | PR_SKIP_DOT_DOT))) {
+ if (NULL == direntry->name)
+ {
+ break;
+ }
+ sprintf(filename,"%s/%s",directory,direntry->name);
+
+ /* Right now this is set up to only look at files here.
+ * With multiple instances of the backend the are now other directories
+ * in the db home directory. This function wasn't ment to deal with
+ * other directories, so we skip them. */
+ if (PR_GetFileInfo(filename, &info) == PR_SUCCESS &&
+ info.type == PR_FILE_DIRECTORY) {
+ /* go into it (instance dir) */
+ int retval = dblayer_grok_directory(filename, flags);
+ PR_CloseDir(dirhandle);
+ return retval;
+ }
+
+ /* If we are here, it means that the directory exists, that we can read
+ * from it, and that there is at least one file there */
+ /* We will try to open that file now if we were asked for read access */
+ if (flags) {
+ PRFileDesc *prfd;
+ PRIntn open_flags = 0;
+ char *access_string = NULL;
+
+ if (DBLAYER_DIRECTORY_READ_ACCESS & flags) {
+ open_flags = PR_RDONLY;
+ }
+ if (DBLAYER_DIRECTORY_WRITE_ACCESS & flags) {
+ open_flags = PR_RDWR;
+ }
+ /* Let's hope that on Solaris we get to open large files OK */
+ prfd = PR_Open(filename,open_flags,0);
+ if (NULL == prfd) {
+ if (DBLAYER_DIRECTORY_READ_ACCESS == flags) {
+ access_string = "read";
+ } else {
+ if (DBLAYER_DIRECTORY_READ_ACCESS & flags) {
+ access_string = "write";
+ } else {
+ access_string = "****";
+ }
+ }
+ /* If we're here, it means that we did not have the requested
+ * permission on this file */
+ LDAPDebug(LDAP_DEBUG_ANY,
+ "WARNING---no %s permission to file %s\n",
+ access_string,filename,0);
+ } else {
+ PR_Close(prfd); /* okay */
+ }
+ }
+ }
+ PR_CloseDir(dirhandle);
+ return 0;
+}
+
+static void
+dblayer_set_data_dir(dblayer_private *priv, struct dblayer_private_env *pEnv,
+ char **data_directories)
+{
+ char **dirp;
+
+ if (!(pEnv->dblayer_priv_flags & DBLAYER_PRIV_SET_DATA_DIR))
+ {
+ for (dirp = data_directories; dirp && *dirp; dirp++)
+ {
+ pEnv->dblayer_DB_ENV->set_data_dir(pEnv->dblayer_DB_ENV, *dirp);
+ }
+ pEnv->dblayer_priv_flags |= DBLAYER_PRIV_SET_DATA_DIR;
+ }
+}
+
+static int
+dblayer_inst_exists(ldbm_instance *inst, char *dbname)
+{
+ PRStatus prst;
+ char id2entry_file[MAXPATHLEN];
+ char *parent_dir = inst->inst_parent_dir_name;
+ char sep = get_sep(parent_dir);
+ char *dbnamep;
+ if (dbname)
+ dbnamep = dbname;
+ else
+ dbnamep = ID2ENTRY LDBM_FILENAME_SUFFIX;
+ sprintf(id2entry_file, "%s%c%s%c%s", parent_dir, sep, inst->inst_dir_name,
+ sep, dbnamep);
+ prst = PR_Access(id2entry_file, PR_ACCESS_EXISTS);
+ if (PR_SUCCESS == prst)
+ return 1;
+ return 0;
+}
+
+/*
+ * create a new DB_ENV and fill it with the goodies from dblayer_private
+ */
+#define INIT_MAX_DIRS 32
+static int
+dblayer_make_env(struct dblayer_private_env **env, struct ldbminfo *li)
+{
+ dblayer_private *priv = (dblayer_private*)li->li_dblayer_private;
+ struct dblayer_private_env *pEnv;
+ char *home_dir = NULL;
+ int ret;
+ int data_dirs = INIT_MAX_DIRS;
+ Object *inst_obj;
+ ldbm_instance *inst = NULL;
+
+ pEnv =
+ (struct dblayer_private_env *) PR_Calloc(1, sizeof(dblayer_private_env));
+
+ if ((ret = db_env_create(&pEnv->dblayer_DB_ENV, 0)) != 0) {
+ LDAPDebug(LDAP_DEBUG_ANY,
+ "ERROR -- Failed to create DB_ENV (returned: %d).\n",
+ ret, 0, 0);
+ }
+
+ DB_ENV_SET_REGION_INIT(pEnv->dblayer_DB_ENV);
+
+ /* Here we overide various system functions called by libdb */
+ ret = dblayer_override_libdb_functions(pEnv->dblayer_DB_ENV, priv);
+ if (ret != 0)
+ return ret;
+
+ if (priv->dblayer_spin_count != 0) {
+ DB_ENV_SET_TAS_SPINS(pEnv->dblayer_DB_ENV, priv->dblayer_spin_count);
+ }
+
+ dblayer_dump_config_tracing(priv);
+
+ /* set data dir to avoid having absolute paths in the transaction log */
+ priv->dblayer_data_directories = NULL;
+ for (inst_obj = objset_first_obj(li->li_instance_set);
+ inst_obj;
+ inst_obj = objset_next_obj(li->li_instance_set, inst_obj))
+ {
+ inst = (ldbm_instance *)object_get_data(inst_obj);
+ if (inst->inst_parent_dir_name)
+ {
+ if (!charray_utf8_inlist(priv->dblayer_data_directories,
+ inst->inst_parent_dir_name))
+ {
+ charray_add(&(priv->dblayer_data_directories),
+ inst->inst_parent_dir_name);
+ }
+ }
+ }
+ home_dir = dblayer_get_home_dir(li, NULL);
+ /* user specified db home */
+ if (!charray_utf8_inlist(priv->dblayer_data_directories, home_dir))
+ {
+ charray_add(&(priv->dblayer_data_directories), home_dir);
+ }
+
+ /* user specified log dir */
+ if (priv->dblayer_log_directory && *(priv->dblayer_log_directory)) {
+ pEnv->dblayer_DB_ENV->set_lg_dir(pEnv->dblayer_DB_ENV,
+ priv->dblayer_log_directory);
+ }
+
+ /* set up cache sizes */
+ dblayer_init_dbenv(pEnv->dblayer_DB_ENV, priv);
+
+ pEnv->dblayer_env_lock = PR_NewRWLock(PR_RWLOCK_RANK_NONE, "checkpointer");
+
+ if (pEnv->dblayer_env_lock) {
+ *env = pEnv;
+ } else {
+ LDAPDebug(LDAP_DEBUG_ANY,
+ "ERROR -- Failed to create RWLock (returned: %d).\n",
+ ret, 0, 0);
+ }
+
+ return ret;
+}
+
+/* generate an absolute path if the given instance dir is not. */
+char *
+dblayer_get_full_inst_dir(struct ldbminfo *li, ldbm_instance *inst,
+ char *buf, int buflen)
+{
+ char *parent_dir;
+ int mylen;
+
+ if (!inst)
+ return NULL;
+
+ if (inst->inst_parent_dir_name)
+ {
+ parent_dir = inst->inst_parent_dir_name;
+ if (inst->inst_parent_dir_name)
+ {
+ mylen = strlen(parent_dir) + strlen(inst->inst_dir_name) + 2;
+ }
+ else
+ {
+ mylen = strlen(parent_dir) + 1;
+ }
+ }
+ else
+ {
+ parent_dir = dblayer_get_home_dir(li, NULL);
+ mylen = strlen(parent_dir);
+ inst->inst_parent_dir_name = slapi_ch_strdup(parent_dir);
+ }
+
+
+ if (inst->inst_dir_name)
+ {
+ mylen += strlen(inst->inst_dir_name) + 2;
+ if (!buf || mylen > buflen)
+ buf = slapi_ch_malloc(mylen);
+ sprintf(buf, "%s%c%s",
+ parent_dir, get_sep(parent_dir), inst->inst_dir_name);
+ }
+ else if (inst->inst_name)
+ {
+ inst->inst_dir_name = slapi_ch_strdup(inst->inst_name);
+ mylen += strlen(inst->inst_dir_name) + 2;
+ if (!buf || mylen > buflen)
+ buf = slapi_ch_malloc(mylen);
+ sprintf(buf, "%s%c%s",
+ parent_dir, get_sep(parent_dir), inst->inst_dir_name);
+ }
+ else
+ {
+ mylen += 1;
+ if (!buf || mylen > buflen)
+ buf = slapi_ch_malloc(mylen);
+ sprintf(buf, "%s", parent_dir);
+ }
+ return buf;
+}
+
+#if defined( OS_solaris )
+#include <sys/types.h>
+#include <sys/statvfs.h>
+#endif
+
+#if defined( hpux )
+#undef f_type
+#include <sys/types.h>
+#include <sys/statvfs.h>
+#define f_type f_un.f_un_type
+#endif
+
+#if defined( linux )
+#undef f_type
+#include <sys/vfs.h>
+#define f_type f_un.f_un_type
+#endif
+
+static int
+no_diskspace(struct ldbminfo *li)
+{
+ int rval = 0;
+#if defined( OS_solaris ) || defined( hpux )
+ struct statvfs fsbuf;
+ if (statvfs(li->li_directory, &fsbuf) < 0)
+ {
+ LDAPDebug(LDAP_DEBUG_ANY,
+ "Cannot get file system info; file system corrupted?\n", 0, 0, 0);
+ rval = 1;
+ }
+ else
+ {
+ double fsiz = ((double)fsbuf.f_bavail) * fsbuf.f_frsize;
+ double expected_siz = ((double)li->li_dbcachesize) * 1.5; /* dbcache +
+ region files */
+ if (fsiz < expected_siz)
+ {
+ LDAPDebug(LDAP_DEBUG_ANY,
+ "No enough space left on device (%lu bytes); "
+ "at least %lu bytes space is needed for db region files\n",
+ fsiz, expected_siz, 0);
+ rval = 1;
+ }
+ }
+#endif
+#if defined( linux )
+ struct statfs fsbuf;
+ if (statfs(li->li_directory, &fsbuf) < 0)
+ {
+ LDAPDebug(LDAP_DEBUG_ANY,
+ "Cannot get file system info; file system corrupted?\n", 0, 0, 0);
+ rval = 1;
+ }
+ else
+ {
+ double fsiz = ((double)fsbuf.f_bavail) * fsbuf.f_bsize;
+ double expected_siz = ((double)li->li_dbcachesize) * 1.5; /* dbcache +
+ region files */
+ if (fsiz < expected_siz)
+ {
+ LDAPDebug(LDAP_DEBUG_ANY,
+ "No enough space left on device (%lu bytes); "
+ "at least %lu bytes space is needed for db region files\n",
+ fsiz, expected_siz, 0);
+ rval = 1;
+ }
+ }
+#endif
+ return rval;
+}
+
+/*
+ * This function is called after all the config options have been read in,
+ * so we can do real initialization work here.
+ */
+#define DBCONFLEN 3
+#define CATASTROPHIC (struct dblayer_private_env *)-1
+
+int dblayer_start(struct ldbminfo *li, int dbmode)
+{
+ /*
+ * So, here we open our DB_ENV session. We store it away for future use.
+ * We also check to see if we exited cleanly last time. If we didn't,
+ * we try to recover. If recovery fails, we're hosed.
+ * We also create the thread which handles checkpointing and logfile
+ * truncation here.
+ */
+ int return_value = -1;
+ dblayer_private *priv = NULL;
+ struct dblayer_private_env *pEnv = NULL;
+ char *region_dir = NULL; /* directory to place region files */
+ char *log_dir = NULL; /* directory to place txn log files */
+ int open_flags = 0;
+
+ PR_ASSERT(NULL != li);
+
+ priv = (dblayer_private*)li->li_dblayer_private;
+
+ if (NULL == priv) {
+ /* you didn't call init successfully */
+ return -1;
+ }
+
+ if (NULL != priv->dblayer_env) {
+ if (CATASTROPHIC == priv->dblayer_env) {
+ LDAPDebug(LDAP_DEBUG_ANY,
+ "Error: DB previously failed to start.\n", 0, 0, 0);
+ return -1;
+ } else {
+ LDAPDebug(LDAP_DEBUG_ANY,
+ "Warning: DB already started.\n", 0, 0, 0);
+ return 0;
+ }
+ }
+
+ /* DBDB we should pick these up in our config routine, and do away with
+ * the li_ one */
+ PR_Lock(li->li_config_mutex);
+ priv->dblayer_home_directory = li->li_directory; /* nsslapd-directory */
+ priv->dblayer_cachesize = li->li_dbcachesize;
+ priv->dblayer_file_mode = li->li_mode;
+ priv->dblayer_ncache = li->li_dbncache;
+ PR_Unlock(li->li_config_mutex);
+
+ /* use nsslapd-db-home-directory (dblayer_dbhome_directory), if set */
+ /* Otherwise, nsslapd-directory (dblayer_home_directory). */
+ region_dir = dblayer_get_home_dir(li, NULL);
+ if (!region_dir || !(*region_dir)) {
+ return -1;
+ }
+
+ /* Check here that the database directory both exists, and that we have
+ * the appropriate access to it */
+ return_value = dblayer_grok_directory(region_dir,
+ DBLAYER_DIRECTORY_READWRITE_ACCESS);
+ if (0 != return_value) {
+ LDAPDebug(LDAP_DEBUG_ANY,"Can't start because the database "
+ "directory \"%s\" either doesn't exist, or is not "
+ "accessible\n", region_dir, 0, 0);
+ return return_value;
+ }
+
+ log_dir = priv->dblayer_log_directory; /* nsslapd-db-logdirectory */
+ if (log_dir && *log_dir) {
+ return_value = dblayer_grok_directory(log_dir,
+ DBLAYER_DIRECTORY_READWRITE_ACCESS);
+ if (0 != return_value) {
+ LDAPDebug(LDAP_DEBUG_ANY,"Can't start because the log "
+ "directory \"%s\" either doesn't exist, or is not "
+ "accessible\n", log_dir, 0, 0);
+ return return_value;
+ }
+ }
+
+ /* Sanity check on cache size on platforms which allow us to figure out
+ * the available phys mem */
+ if (!dblayer_is_cachesize_sane(&(priv->dblayer_cachesize))) {
+ /* Oops---looks like the admin misconfigured, let's warn them */
+ LDAPDebug(LDAP_DEBUG_ANY,"WARNING---Likely CONFIGURATION ERROR---"
+ "dbcachesize is configured to use more than the available "
+ "physical memory, decreased to the largest available size (%lu bytes).\n",
+ priv->dblayer_cachesize, 0, 0);
+ li->li_dbcachesize = priv->dblayer_cachesize;
+ }
+
+ /* fill in DB_ENV stuff from the common configuration */
+ return_value = dblayer_make_env(&pEnv, li);
+ if (return_value != 0)
+ return return_value;
+
+ if ((DBLAYER_NORMAL_MODE|DBLAYER_CLEAN_RECOVER_MODE) & dbmode)
+ {
+ /* Now, we read our metadata */
+ return_value = read_metadata(li);
+ if (0 != return_value) {
+ /* The error message was output by read_metadata() */
+ return -1;
+ }
+ }
+
+ priv->dblayer_env = pEnv;
+
+ open_flags = DB_CREATE | DB_INIT_MPOOL | DB_THREAD;
+
+ if (priv->dblayer_enable_transactions) {
+ open_flags |= (DB_INIT_TXN | DB_INIT_LOG | DB_INIT_LOCK);
+ if (priv->dblayer_recovery_required) {
+ open_flags |= DB_RECOVER;
+ if (DBLAYER_RESTORE_MODE & dbmode) {
+ LDAPDebug(LDAP_DEBUG_ANY, "Recovering database after restore "
+ "from archive.\n", 0, 0, 0);
+ } else if (DBLAYER_CLEAN_RECOVER_MODE & dbmode) {
+ LDAPDebug(LDAP_DEBUG_ANY, "Clean up db environment and start "
+ "from archive.\n", 0, 0, 0);
+ } else {
+ LDAPDebug(LDAP_DEBUG_ANY, "Detected Disorderly Shutdown last "
+ "time Directory Server was running, recovering "
+ "database.\n", 0, 0, 0);
+ }
+ }
+ switch (dbmode&DBLAYER_RESTORE_MASK) {
+ case DBLAYER_RESTORE_MODE:
+ open_flags |= DB_RECOVER_FATAL;
+ open_flags &= ~DB_RECOVER; /* shouldn't set both */
+ if (!(dbmode & DBLAYER_CMDLINE_MODE))
+ dbmode = DBLAYER_NORMAL_MODE; /* to restart helper threads */
+ break;
+ case DBLAYER_RESTORE_NO_RECOVERY_MODE:
+ open_flags &= ~(DB_RECOVER | DB_RECOVER_FATAL);
+ if (!(dbmode & DBLAYER_CMDLINE_MODE))
+ dbmode = DBLAYER_NORMAL_MODE; /* to restart helper threads */
+ }
+ }
+
+ if (priv->dblayer_private_mem) {
+ LDAPDebug(LDAP_DEBUG_ANY,
+ "WARNING: Server is running with nsslapd-db-private-mem on; "
+ "No other process is allowed to access the database\n",
+ 0, 0, 0);
+ open_flags |= DB_PRIVATE;
+ }
+
+ if (priv->dblayer_named_regions) {
+ open_flags |= DB_SYSTEM_MEM;
+ }
+
+ if (priv->dblayer_lockdown) {
+ open_flags |= DB_LOCKDOWN;
+ }
+
+
+ /* Is the cache being re-sized ? (If we're just doing an archive or export,
+ * we don't care if the cache is being re-sized) */
+ if ( (priv->dblayer_previous_cachesize || priv->dblayer_previous_ncache) &&
+ ((priv->dblayer_cachesize != priv->dblayer_previous_cachesize) ||
+ (priv->dblayer_ncache != priv->dblayer_previous_ncache)) &&
+ !(dbmode & (DBLAYER_ARCHIVE_MODE|DBLAYER_EXPORT_MODE)) ) {
+ LDAPDebug(LDAP_DEBUG_ANY,
+ "I'm resizing my cache now...cache was %lu and is now %lu\n",
+ priv->dblayer_previous_cachesize, priv->dblayer_cachesize, 0);
+ dblayer_reset_env(li);
+ /*
+ * Once pEnv->remove (via dblayer_reset_env) has been called,
+ * the DB_ENV (pEnv) needs to be created again.
+ */
+ if ((return_value = dblayer_make_env(&pEnv, li)) != 0) {
+ LDAPDebug(LDAP_DEBUG_ANY,
+ "ERROR -- Failed to create DBENV (returned: %d).\n",
+ return_value, 0, 0);
+ }
+ priv->dblayer_env = pEnv;
+ }
+
+ /* transactions enabled and logbuf size greater than sleepycat's default */
+ if(priv->dblayer_enable_transactions && (priv->dblayer_logbuf_size > 0)) {
+ if(priv->dblayer_logbuf_size >= 32768) {
+ pEnv->dblayer_DB_ENV->set_lg_bsize(pEnv->dblayer_DB_ENV,priv->dblayer_logbuf_size);
+ } else {
+ LDAPDebug(LDAP_DEBUG_ANY, "using default value for log bufsize because configured value (%lu) is too small.\n",
+ priv->dblayer_logbuf_size, 0, 0);
+ }
+ }
+
+ /* check if there's enough disk space to start */
+ if (no_diskspace(li))
+ {
+ return ENOSPC;
+ }
+
+ dblayer_set_data_dir(priv, pEnv, priv->dblayer_data_directories);
+ /* If we're doing recovery, we MUST open the env single-threaded ! */
+ if ( (open_flags & DB_RECOVER) || (open_flags & DB_RECOVER_FATAL) ) {
+ /* Recover, then close, then open again */
+ int recover_flags = open_flags & ~DB_THREAD;
+
+ if (DBLAYER_CLEAN_RECOVER_MODE & dbmode) /* upgrade case */
+ {
+ DB_ENV *thisenv = pEnv->dblayer_DB_ENV;
+ return_value = thisenv->remove(thisenv, region_dir, DB_FORCE);
+ if (0 != return_value)
+ {
+ LDAPDebug(LDAP_DEBUG_ANY,
+ "dblayer_start: failed to remove old db env "
+ "in %s: %s\n", region_dir,
+ dblayer_strerror(return_value), 0);
+ return return_value;
+ }
+ dbmode = DBLAYER_NORMAL_MODE;
+
+ if ((return_value = dblayer_make_env(&pEnv, li)) != 0)
+ {
+ LDAPDebug(LDAP_DEBUG_ANY,
+ "ERROR -- Failed to create DBENV (returned: %d).\n",
+ return_value, 0, 0);
+ return return_value;
+ }
+ }
+
+ return_value = pEnv->dblayer_DB_ENV->open(
+ pEnv->dblayer_DB_ENV,
+ region_dir,
+ recover_flags,
+ priv->dblayer_file_mode
+ );
+ if (0 != return_value) {
+ if (return_value == ENOMEM) {
+ /*
+ * https://blackflag.mcom.com/show_bug.cgi?id=557319
+ * Crash ns-slapd while running scalab01 after restart slapd
+ */
+ LDAPDebug(LDAP_DEBUG_ANY,
+ "mmap in opening database environment (recovery mode) "
+ "failed trying to allocate %lu bytes. (OS err %d - %s)\n",
+ li->li_dbcachesize, return_value, dblayer_strerror(return_value));
+ priv->dblayer_env = CATASTROPHIC;
+ } else {
+ LDAPDebug(LDAP_DEBUG_ANY, "Database Recovery Process FAILED. "
+ "The database is not recoverable. err=%d: %s\n",
+ return_value, dblayer_strerror(return_value), 0);
+ LDAPDebug(LDAP_DEBUG_ANY,
+ "Please make sure there is enough disk space for "
+ "dbcache (%lu bytes) and db region files\n",
+ li->li_dbcachesize, 0, 0);
+ }
+ return return_value;
+ } else {
+ open_flags &= ~(DB_RECOVER | DB_RECOVER_FATAL);
+ pEnv->dblayer_DB_ENV->close(pEnv->dblayer_DB_ENV, 0);
+ if ((return_value = dblayer_make_env(&pEnv, li)) != 0) {
+ LDAPDebug(LDAP_DEBUG_ANY,
+ "ERROR -- Failed to create DBENV (returned: %d).\n",
+ return_value, 0, 0);
+ return return_value;
+ }
+ priv->dblayer_env = pEnv;
+ dblayer_set_data_dir(priv, pEnv, priv->dblayer_data_directories);
+ }
+ }
+
+ if ((!priv->dblayer_durable_transactions) ||
+ ((priv->dblayer_enable_transactions) && (trans_batch_limit > 0))){
+#if 1000*DB_VERSION_MAJOR + 100*DB_VERSION_MINOR >= 3200
+#if 1000*DB_VERSION_MAJOR + 100*DB_VERSION_MINOR >= 4100 /* db4.1 and newer */
+ pEnv->dblayer_DB_ENV->set_flags(pEnv->dblayer_DB_ENV, DB_TXN_WRITE_NOSYNC, 1);
+#else /* db3.3 */
+ pEnv->dblayer_DB_ENV->set_flags(pEnv->dblayer_DB_ENV, DB_TXN_NOSYNC, 1);
+#endif
+#else /* older */
+ open_flags |= DB_TXN_NOSYNC;
+#endif
+ }
+ if (!((DBLAYER_IMPORT_MODE|DBLAYER_INDEX_MODE) & dbmode))
+ {
+ pEnv->dblayer_openflags = open_flags;
+ return_value = pEnv->dblayer_DB_ENV->open(
+ pEnv->dblayer_DB_ENV,
+ region_dir,
+ open_flags,
+ priv->dblayer_file_mode
+ );
+
+
+ /* Now attempt to start up the checkpoint and deadlock threads */
+ if ( (DBLAYER_NORMAL_MODE & dbmode ) && (0 == return_value)) {
+ /* update the dbversion file */
+ dbversion_write(li, region_dir, NULL);
+
+ /* if dblayer_close then dblayer_start is called,
+ this flag is set */
+ priv->dblayer_stop_threads = 0;
+ if (0 != (return_value = dblayer_start_deadlock_thread(li))) {
+ return return_value;
+ }
+
+ if (0 != (return_value = dblayer_start_checkpoint_thread(li))) {
+ return return_value;
+ }
+
+ if (0 != (return_value = dblayer_start_log_flush_thread(priv))) {
+ return return_value;
+ }
+
+ if (0 != (return_value = dblayer_start_trickle_thread(li))) {
+ return return_value;
+ }
+
+ if (0 != (return_value = dblayer_start_perf_thread(li))) {
+ return return_value;
+ }
+
+ /* Now open the performance counters stuff */
+ perfctrs_init(li,&(priv->perf_private));
+ }
+ if (return_value != 0) {
+ if (return_value == ENOMEM) {
+ /*
+ * https://blackflag.mcom.com/show_bug.cgi?id=557319
+ * Crash ns-slapd while running scalab01 after restart slapd
+ */
+ LDAPDebug(LDAP_DEBUG_ANY,
+ "mmap in opening database environment "
+ "failed trying to allocate %d bytes. (OS err %lu - %s)\n",
+ li->li_dbcachesize, return_value, dblayer_strerror(return_value));
+ priv->dblayer_env = CATASTROPHIC;
+ } else {
+ LDAPDebug(LDAP_DEBUG_ANY,
+ "Opening database environment (%s) failed. err=%d: %s\n",
+ region_dir, return_value, dblayer_strerror(return_value));
+ }
+ }
+ return return_value;
+ }
+ return 0;
+}
+
+void
+autosize_import_cache(struct ldbminfo *li)
+{
+ /*
+ * default behavior for ldif2db import cache,
+ * nsslapd-import-cache-autosize==-1,
+ * autosize 50% mem to import cache
+ */
+ if (li->li_import_cache_autosize == -1) {
+ li->li_import_cache_autosize = 50;
+ }
+
+ /* sanity check */
+ if (li->li_import_cache_autosize > 100) {
+ LDAPDebug(LDAP_DEBUG_ANY,
+ "cache autosizing: bad setting, "
+ "import cache autosizing value should not be larger than 100(%).\n"
+ "set: 100(%).\n", NULL, NULL, NULL);
+ li->li_import_cache_autosize = 100;
+ }
+
+ /* autosizing importCache */
+ if (li->li_import_cache_autosize > 0) {
+ size_t pagesize, pages, procpages, availpages;
+
+ dblayer_sys_pages(&pagesize, &pages, &procpages, &availpages);
+ LDAPDebug(LDAP_DEBUG_ANY, "dblayer_instance_start: "
+ "pagesize: %d, pages: %d, procpages: %d\n",
+ pagesize, pages, procpages);
+ if (pagesize) {
+ char s[32]; /* big enough to hold %ld */
+ int import_pages;
+ int pages_limit = (200 * 1024) / (pagesize/1024);
+ import_pages = (li->li_import_cache_autosize * pages) / 125;
+ /* We don't want to go wild with memory when auto-sizing, cap the
+ * cache size at 200 Megs to try to avoid situations where we
+ * attempt to allocate more memory than there is free page pool for, or
+ * where there's some system limit on the size of process memory
+ */
+ if (import_pages > pages_limit) {
+ import_pages = pages_limit;
+ }
+ LDAPDebug(LDAP_DEBUG_ANY, "cache autosizing: import cache: %dk \n",
+ import_pages*(pagesize/1024), NULL, NULL);
+ LDAPDebug(LDAP_DEBUG_ANY,
+ "li_import_cache_autosize: %d, import_pages: %d, pagesize: %d\n",
+ li->li_import_cache_autosize, import_pages,
+ pagesize);
+
+ sprintf(s, "%lu", (unsigned long)(import_pages * pagesize));
+ ldbm_config_internal_set(li, CONFIG_IMPORT_CACHESIZE, s);
+ }
+ }
+}
+
+/* mode is one of
+ * DBLAYER_NORMAL_MODE,
+ * DBLAYER_INDEX_MODE,
+ * DBLAYER_IMPORT_MODE,
+ * DBLAYER_EXPORT_MODE
+ */
+int dblayer_instance_start(backend *be, int mode)
+{
+ struct ldbminfo *li = (struct ldbminfo *) be->be_database->plg_private;
+ ldbm_instance *inst = (ldbm_instance *) be->be_instance_info;
+ dblayer_private *priv;
+ struct dblayer_private_env *pEnv;
+ char inst_dir[MAXPATHLEN];
+ char *inst_dirp = NULL;
+ int return_value;
+
+ priv = (dblayer_private*)li->li_dblayer_private;
+ pEnv = priv->dblayer_env;
+ if (CATASTROPHIC == pEnv || NULL == pEnv) {
+ LDAPDebug(LDAP_DEBUG_ANY,
+ "instance %s: dbenv is not available (0x%x).\n",
+ inst?inst->inst_name:"unknown", pEnv, 0);
+ return -1;
+ }
+
+ if (NULL != inst->inst_id2entry) {
+ LDAPDebug(LDAP_DEBUG_ANY,
+ "Warning: DB instance \"%s\" already started.\n",
+ inst->inst_name, 0, 0);
+ return 0;
+ }
+
+ attrcrypt_init(inst);
+
+ /* Get the name of the directory that holds index files
+ * for this instance. */
+ if (dblayer_get_instance_data_dir(be) != 0) {
+ /* Problem getting the name of the directory that holds the
+ * index files for this instance. */
+ return -1;
+ }
+
+ inst_dirp = dblayer_get_full_inst_dir(li, inst, inst_dir, MAXPATHLEN);
+ return_value = dblayer_grok_directory(inst_dirp,
+ DBLAYER_DIRECTORY_READWRITE_ACCESS);
+ if (0 != return_value) {
+ LDAPDebug(LDAP_DEBUG_ANY,"Can't start because the database instance "
+ "directory \"%s\" either doesn't exist, "
+ "or the db files are not accessible\n",
+ inst_dirp, 0, 0);
+ goto errout;
+ }
+
+ if (mode & DBLAYER_NORMAL_MODE) {
+ /* In normal mode (not db2ldif, ldif2db, etc.) we need to deal with
+ * the dbversion file here. */
+
+ /* Read the dbversion file if there is one, and create it
+ * if it doesn't exist. */
+ if (dbversion_exists(li, inst_dirp)) {
+ char ldbmversion[LDBM_VERSION_MAXBUF];
+ char dataversion[LDBM_VERSION_MAXBUF];
+
+ if (dbversion_read(li, inst_dirp, ldbmversion, dataversion) != 0) {
+ LDAPDebug(LDAP_DEBUG_ANY, "Warning: Unable to read dbversion "
+ "file in %s\n", inst->inst_dir_name, 0, 0);
+ } else {
+ int rval = 0;
+#if defined(UPGRADEDB)
+ /* check the DBVERSION and reset idl-switch if needed (DS6.2) */
+ /* from the next major rel, we won't do this and just upgrade */
+ if (!(li->li_flags & LI_FORCE_MOD_CONFIG))
+ adjust_idl_switch(ldbmversion, li);
+#endif
+
+ /* check to make sure these instance was made with the correct
+ * version. */
+ rval = check_db_inst_version(inst);
+ if (rval & DBVERSION_NOT_SUPPORTED)
+ {
+ LDAPDebug(LDAP_DEBUG_ANY, "Instance %s does not have the "
+ "expected version\n", inst->inst_name, 0, 0);
+ PR_ASSERT(0);
+ return_value = -1;
+ goto errout;
+ }
+ if (rval & DBVERSION_NEED_IDL_OLD2NEW)
+ {
+ LDAPDebug(LDAP_DEBUG_ANY,
+ "Instance %s: idl-switch is new while db idl format is "
+ "old, modify nsslapd-idl-switch in dse.ldif to old\n",
+ inst->inst_name, 0, 0);
+ }
+ if (rval & DBVERSION_NEED_IDL_NEW2OLD)
+ {
+ LDAPDebug(LDAP_DEBUG_ANY,
+ "Instance %s: idl-switch is old while db idl format is "
+ "new, modify nsslapd-idl-switch in dse.ldif to new\n",
+ inst->inst_name, 0, 0);
+ }
+
+ /* record the dataversion */
+ if (dataversion[0] != '\0') {
+ inst->inst_dataversion = slapi_ch_strdup(dataversion);
+ }
+
+ rval = ldbm_upgrade(inst, rval);
+ if (0 != rval)
+ {
+ LDAPDebug(LDAP_DEBUG_ANY, "Upgrading instance %s failed\n",
+ inst->inst_name, 0, 0);
+ PR_ASSERT(0);
+ return_value = -1;
+ goto errout;
+ }
+ }
+ } else {
+ /* The dbversion file didn't exist, so we'll create one. */
+ dbversion_write(li, inst_dirp, NULL);
+ }
+ } /* on import we don't mess with the dbversion file except to write it
+ * when done with the import. */
+
+ /* Now attempt to open id2entry */
+ {
+ char *id2entry_file;
+ int open_flags = 0;
+ DB *dbp;
+ char *subname;
+ struct dblayer_private_env *mypEnv;
+
+ id2entry_file = slapi_ch_malloc(strlen(inst->inst_dir_name) +
+ strlen(ID2ENTRY LDBM_FILENAME_SUFFIX) + 2);
+ sprintf(id2entry_file, "%s/%s", inst->inst_dir_name,
+ ID2ENTRY LDBM_FILENAME_SUFFIX);
+
+ open_flags = DB_CREATE | DB_THREAD;
+
+ /* The subname argument allows applications to have
+ * subdatabases, i.e., multiple databases inside of a single
+ * physical file. This is useful when the logical databases
+ * are both numerous and reasonably small, in order to
+ * avoid creating a large number of underlying files.
+ */
+ subname = NULL;
+ mypEnv = NULL;
+ if (mode & (DBLAYER_IMPORT_MODE|DBLAYER_INDEX_MODE)) {
+ size_t cachesize;
+ char *data_directories[2] = {0, 0};
+ /* [605974] delete DB_PRIVATE:
+ * to make import visible to the other process */
+ int oflags = DB_CREATE | DB_INIT_MPOOL | DB_THREAD;
+ /*
+ * but nsslapd-db-private-import-mem should work with import,
+ * as well */
+ if (priv->dblayer_private_import_mem) {
+ LDAPDebug(LDAP_DEBUG_ANY,
+ "WARNING: Import is running with "
+ "nsslapd-db-private-import-mem on; "
+ "No other process is allowed to access the database\n",
+ 0, 0, 0);
+ oflags |= DB_PRIVATE;
+ }
+ PR_Lock(li->li_config_mutex);
+ if ((li->li_flags & TASK_RUNNING_FROM_COMMANDLINE) &&
+ (li->li_import_cache_autosize)) /* Autosizing importCache
+ * Need to re-eval every time
+ * to guarantee the memory is
+ * really available
+ * (just for command line I/F)
+ */
+ {
+ autosize_import_cache(li);
+ }
+ cachesize = li->li_import_cachesize;
+ PR_Unlock(li->li_config_mutex);
+
+ if (cachesize < 1048576) {
+ /* make it at least 1M */
+ cachesize = 1048576;
+ }
+ priv->dblayer_cachesize = cachesize;
+ /* We always auto-calculate ncache for the import region */
+ priv->dblayer_ncache = 0;
+
+ /* use our own env */
+ return_value = dblayer_make_env(&mypEnv, li);
+ if (return_value != 0) {
+ LDAPDebug(LDAP_DEBUG_ANY,
+ "Unable to create new DB_ENV for import/export! %d\n",
+ return_value, 0, 0);
+ goto out;
+ }
+ /* do not assume import cache size is under 1G */
+ mypEnv->dblayer_DB_ENV->set_cachesize(mypEnv->dblayer_DB_ENV,
+ cachesize/GIGABYTE,
+ cachesize%GIGABYTE,
+ priv->dblayer_ncache);
+ /* probably want to change this -- but for now, create the
+ * mpool files in the instance directory.
+ */
+ mypEnv->dblayer_openflags = oflags;
+ data_directories[0] = inst->inst_parent_dir_name;
+ dblayer_set_data_dir(priv, mypEnv, data_directories);
+ return_value = mypEnv->dblayer_DB_ENV->open(mypEnv->dblayer_DB_ENV,
+ inst_dirp,
+ oflags,
+ priv->dblayer_file_mode);
+ if (return_value != 0) {
+ LDAPDebug(LDAP_DEBUG_ANY,
+ "Unable to open new DB_ENV for import/export! %d\n",
+ return_value, 0, 0);
+ goto out;
+ }
+ inst->import_env = mypEnv;
+ } else {
+ mypEnv = pEnv;
+ }
+
+ inst->inst_id2entry = NULL;
+ return_value = db_create(&inst->inst_id2entry, mypEnv->dblayer_DB_ENV, 0);
+ if (0 != return_value) {
+ LDAPDebug(LDAP_DEBUG_ANY,
+ "Unable to create id2entry db file! %d\n",
+ return_value, 0, 0);
+ goto out;
+ }
+ dbp = inst->inst_id2entry;
+
+ return_value = dbp->set_pagesize(dbp,
+ (priv->dblayer_page_size == 0) ? DBLAYER_PAGESIZE :
+ priv->dblayer_page_size);
+ if (0 != return_value) {
+ LDAPDebug(LDAP_DEBUG_ANY,
+ "dbp->set_pagesize(%lu or %lu) failed %d\n",
+ priv->dblayer_page_size, DBLAYER_PAGESIZE,
+ return_value);
+ goto out;
+ }
+
+#if 1000*DB_VERSION_MAJOR + 100*DB_VERSION_MINOR < 3300
+ return_value = dbp->set_malloc(dbp, malloc);
+ if (0 != return_value) {
+ LDAPDebug(LDAP_DEBUG_ANY,
+ "dbp->set_malloc failed %d\n",
+ return_value, 0, 0);
+
+ goto out;
+ }
+#endif
+ if ((charray_get_index(priv->dblayer_data_directories,
+ inst->inst_parent_dir_name) != 0) &&
+ !dblayer_inst_exists(inst, NULL))
+ {
+ char *abs_id2entry_file = NULL;
+ int abs_len;
+ /* create a file with abs path, then try again */
+
+ abs_len = strlen(inst_dirp) +
+ strlen(ID2ENTRY LDBM_FILENAME_SUFFIX) + 2;
+ abs_id2entry_file = (char *)slapi_ch_malloc(abs_len);
+ sprintf(abs_id2entry_file, "%s%c%s", inst_dirp,
+ get_sep(inst_dirp), ID2ENTRY LDBM_FILENAME_SUFFIX);
+ DB_OPEN(mypEnv->dblayer_openflags,
+ dbp, NULL/* txnid */, abs_id2entry_file, subname, DB_BTREE,
+ open_flags, priv->dblayer_file_mode, return_value);
+ dbp->close(dbp, 0);
+ return_value = db_create(&inst->inst_id2entry,
+ mypEnv->dblayer_DB_ENV, 0);
+ if (0 != return_value)
+ goto out;
+ dbp = inst->inst_id2entry;
+ slapi_ch_free_string(&abs_id2entry_file);
+ }
+ DB_OPEN(mypEnv->dblayer_openflags,
+ dbp, NULL/* txnid */, id2entry_file, subname, DB_BTREE,
+ open_flags, priv->dblayer_file_mode, return_value);
+ if (0 != return_value) {
+ LDAPDebug(LDAP_DEBUG_ANY,
+ "dbp->open(\"%s\") failed: %s (%d)\n",
+ id2entry_file, dblayer_strerror(return_value),
+ return_value);
+ /* if it's a newly created backend instance,
+ * need to check the inst_parent_dir already exists and
+ * set as a data dir */
+ if (strstr(dblayer_strerror(return_value),
+ "No such file or directory"))
+ {
+ LDAPDebug(LDAP_DEBUG_ANY,
+ "Instance %s is not registered as a db data directory. "
+ "Please restart the server to create it.\n",
+ inst?inst->inst_name:"unknown", pEnv, 0);
+ }
+ else if (strstr(dblayer_strerror(return_value),
+ "Permission denied"))
+ {
+ LDAPDebug(LDAP_DEBUG_ANY,
+ "Instance directory %s may not be writable\n",
+ inst_dirp, 0, 0);
+ }
+
+ goto out;
+ }
+#if 1000*DB_VERSION_MAJOR + 100*DB_VERSION_MINOR == 4100
+ /* lower the buffer cache priority to avoid sleep in memp_alloc */
+ /* W/ DB_PRIORITY_LOW, the db buffer page priority is calculated as:
+ * priority = lru_count + pages / (-1)
+ * (by default, priority = lru_count)
+ * When upgraded to db4.2, this setting may not needed, hopefully.
+ * ask sleepycat [#8301]; blackflag #619964
+ */
+ dbp->set_cache_priority(dbp, DB_PRIORITY_LOW);
+#endif
+out:
+ slapi_ch_free((void**)&id2entry_file);
+ }
+
+ if (0 == return_value) {
+ /* get nextid from disk now */
+ get_ids_from_disk(be);
+ }
+
+ if (mode & DBLAYER_NORMAL_MODE) {
+ dbversion_write(li, inst_dirp, NULL);
+ }
+
+ /*
+ * check if nextid is valid: it only matters if the database is either
+ * being imported or is in normal mode
+ */
+ if (inst->inst_nextid > MAXID && !(mode & DBLAYER_EXPORT_MODE)) {
+ LDAPDebug(LDAP_DEBUG_ANY, "dblayer_instance_start fail: backend '%s' "
+ "has no IDs left. DATABASE MUST BE REBUILT.\n",
+ be->be_name, 0, 0);
+ return 1;
+ }
+
+ if (return_value != 0) {
+ LDAPDebug(LDAP_DEBUG_ANY, "dblayer_instance_start fail: %s (%d)\n",
+ dblayer_strerror(return_value), return_value, 0);
+ }
+errout:
+ if (inst_dirp != inst_dir)
+ slapi_ch_free_string(&inst_dirp);
+ return return_value;
+}
+
+
+/* This returns a DB* for the primary index.
+ * If the database library is non-reentrant, we lock it.
+ * the caller MUST call to unlock the db library once they're
+ * finished with the handle. Luckily, the back-end already has
+ * these semantics for the older dbcache stuff.
+ */
+/* Things have changed since the above comment was
+ * written. The database library is reentrant. */
+int dblayer_get_id2entry(backend *be, DB **ppDB)
+{
+ ldbm_instance *inst;
+
+ PR_ASSERT(NULL != be);
+
+ inst = (ldbm_instance *) be->be_instance_info;
+
+ *ppDB = inst->inst_id2entry;
+ return 0;
+}
+
+int dblayer_release_id2entry(backend *be, DB *pDB)
+{
+ return 0;
+}
+
+#ifdef UPGRADEDB
+/*
+ * dblayer_get_aux_id2entry:
+ * - create a dedicated db env and db handler for id2entry.
+ * - introduced for upgradedb not to share the env and db handler with
+ * other index files to support multiple passes and merge.
+ */
+int dblayer_get_aux_id2entry(backend *be, DB **ppDB, DB_ENV **ppEnv)
+{
+ ldbm_instance *inst;
+ struct dblayer_private_env *mypEnv = NULL;
+ DB *dbp = NULL;
+ int rval = 1;
+ struct ldbminfo *li;
+ dblayer_private *opriv;
+ dblayer_private *priv;
+ char *subname = NULL;
+ int envflags;
+ size_t cachesize;
+ PRFileInfo prfinfo;
+ PRStatus prst;
+ char *id2entry_file;
+ char inst_dir[MAXPATHLEN];
+ char *inst_dirp = NULL;
+ char *data_directories[2] = {0, 0};
+
+ PR_ASSERT(NULL != be);
+
+ *ppDB = NULL;
+ *ppEnv = NULL;
+ inst = (ldbm_instance *) be->be_instance_info;
+ if (NULL == inst)
+ {
+ LDAPDebug(LDAP_DEBUG_ANY,
+ "No instance/env: persistent id2entry is not available\n", 0, 0, 0);
+ goto done;
+ }
+
+ li = inst->inst_li;
+ if (NULL == li)
+ {
+ LDAPDebug(LDAP_DEBUG_ANY,
+ "No ldbm info: persistent id2entry is not available\n", 0, 0, 0);
+ goto done;
+ }
+
+ opriv = li->li_dblayer_private;
+ if (NULL == opriv)
+ {
+ LDAPDebug(LDAP_DEBUG_ANY,
+ "No dblayer info: persistent id2entry is not available\n", 0, 0, 0);
+ goto done;
+ }
+ priv = (dblayer_private *)slapi_ch_malloc(sizeof(dblayer_private));
+ memcpy(priv, opriv, sizeof(dblayer_private));
+ priv->dblayer_spin_count = 0;
+
+ inst_dirp = dblayer_get_full_inst_dir(li, inst, inst_dir, MAXPATHLEN);
+ priv->dblayer_home_directory =
+ slapi_ch_malloc(strlen(inst_dirp) + strlen("dbenv") + 2);
+ sprintf(priv->dblayer_home_directory, "%s/dbenv", inst_dirp);
+ priv->dblayer_log_directory = slapi_ch_strdup(priv->dblayer_home_directory);
+
+ prst = PR_GetFileInfo(inst_dirp, &prfinfo);
+ if (PR_FAILURE == prst || PR_FILE_DIRECTORY != prfinfo.type)
+ {
+ LDAPDebug(LDAP_DEBUG_ANY,
+ "No inst dir: persistent id2entry is not available\n", 0, 0, 0);
+ goto done;
+ }
+
+ prst = PR_GetFileInfo(priv->dblayer_home_directory, &prfinfo);
+ if (PR_SUCCESS == prst)
+ {
+ ldbm_delete_dirs(priv->dblayer_home_directory);
+ }
+ rval = mkdir_p(priv->dblayer_home_directory, 0700);
+ if (0 != rval)
+ {
+ LDAPDebug(LDAP_DEBUG_ANY,
+ "can't create env dir: persistent id2entry is not available\n", 0, 0, 0);
+ goto done;
+ }
+
+ /* use our own env */
+ rval = dblayer_make_env(&mypEnv, li);
+ if (rval != 0) {
+ LDAPDebug(LDAP_DEBUG_ANY,
+ "Unable to create new DB_ENV for import/export! %d\n", rval, 0, 0);
+ goto err;
+ }
+
+ envflags = DB_CREATE | DB_INIT_MPOOL | DB_PRIVATE;
+ cachesize = 1048576; /* 1M */
+
+ mypEnv->dblayer_DB_ENV->set_cachesize(mypEnv->dblayer_DB_ENV,
+ 0, cachesize, priv->dblayer_ncache);
+
+ /* probably want to change this -- but for now, create the
+ * mpool files in the instance directory.
+ */
+ mypEnv->dblayer_openflags = envflags;
+ data_directories[0] = inst->inst_parent_dir_name;
+ dblayer_set_data_dir(priv, mypEnv, data_directories);
+ rval = mypEnv->dblayer_DB_ENV->open(mypEnv->dblayer_DB_ENV,
+ priv->dblayer_home_directory, envflags, priv->dblayer_file_mode);
+ if (rval != 0)
+ {
+ LDAPDebug(LDAP_DEBUG_ANY,
+ "Unable to open new DB_ENV for upgradedb/reindex %d\n", rval, 0, 0);
+ goto err;
+ }
+ *ppEnv = mypEnv->dblayer_DB_ENV;
+
+ rval = db_create(&dbp, mypEnv->dblayer_DB_ENV, 0);
+ if (0 != rval) {
+ LDAPDebug(LDAP_DEBUG_ANY,
+ "Unable to create id2entry db handler! %d\n", rval, 0, 0);
+ goto err;
+ }
+
+ rval = dbp->set_pagesize(dbp, (priv->dblayer_page_size == 0) ?
+ DBLAYER_PAGESIZE : priv->dblayer_page_size);
+ if (0 != rval)
+ {
+ LDAPDebug(LDAP_DEBUG_ANY,
+ "dbp->set_pagesize(%lu or %lu) failed %d\n",
+ priv->dblayer_page_size, DBLAYER_PAGESIZE, rval);
+ goto err;
+ }
+
+ id2entry_file = slapi_ch_malloc(strlen(inst->inst_dir_name) +
+ strlen(ID2ENTRY LDBM_FILENAME_SUFFIX) + 2);
+ sprintf(id2entry_file, "%s/%s",
+ inst->inst_dir_name, ID2ENTRY LDBM_FILENAME_SUFFIX);
+
+ PR_ASSERT(dblayer_inst_exists(inst, NULL));
+ DB_OPEN(mypEnv->dblayer_openflags,
+ dbp, NULL/* txnid */, id2entry_file, subname, DB_BTREE,
+ 0, priv->dblayer_file_mode, rval);
+ if (0 != rval)
+ {
+ LDAPDebug(LDAP_DEBUG_ANY,
+ "dbp->open(\"%s\") failed: %s (%d)\n",
+ id2entry_file, dblayer_strerror(rval), rval);
+ if (strstr(dblayer_strerror(rval), "Permission denied"))
+ {
+ LDAPDebug(LDAP_DEBUG_ANY,
+ "Instance directory %s may not be writable\n", inst_dirp, 0, 0);
+ }
+ goto err;
+ }
+ *ppDB = dbp;
+ goto done;
+err:
+ if (*ppEnv)
+ (*ppEnv)->close(*ppEnv, 0);
+ if (priv->dblayer_home_directory)
+ ldbm_delete_dirs(priv->dblayer_home_directory);
+done:
+ slapi_ch_free_string(&id2entry_file);
+ slapi_ch_free_string(&priv->dblayer_home_directory);
+ slapi_ch_free_string(&priv->dblayer_log_directory);
+ /* Don't free priv->dblayer_data_directories since priv doesn't own the memory */
+ slapi_ch_free((void **)&priv);
+ slapi_ch_free((void **)&mypEnv);
+ if (inst_dirp != inst_dir)
+ slapi_ch_free_string(&inst_dirp);
+ return rval;
+}
+
+int dblayer_release_aux_id2entry(backend *be, DB *pDB, DB_ENV *pEnv)
+{
+ ldbm_instance *inst;
+ char *envdir = NULL;
+ char inst_dir[MAXPATHLEN];
+ char *inst_dirp = NULL;
+
+ inst = (ldbm_instance *) be->be_instance_info;
+ if (NULL == inst)
+ {
+ LDAPDebug(LDAP_DEBUG_ANY,
+ "No instance/env: persistent id2entry is not available\n", 0, 0, 0);
+ goto done;
+ }
+
+ inst_dirp = dblayer_get_full_inst_dir(inst->inst_li, inst,
+ inst_dir, MAXPATHLEN);
+ envdir = slapi_ch_malloc(strlen(inst_dirp) + strlen("dbenv") + 2);
+ sprintf(envdir, "%s/dbenv", inst_dirp);
+
+done:
+ if (pDB)
+ pDB->close(pDB, 0);
+ if (pEnv)
+ pEnv->close(pEnv, 0);
+ if (envdir)
+ ldbm_delete_dirs(envdir);
+ if (inst_dirp != inst_dir)
+ slapi_ch_free_string(&inst_dirp);
+ return 0;
+}
+#endif
+
+int dblayer_close_indexes(backend *be)
+{
+ ldbm_instance *inst;
+ DB *pDB = NULL;
+ dblayer_handle *handle = NULL;
+ dblayer_handle *next = NULL;
+ int return_value = 0;
+
+ PR_ASSERT(NULL != be);
+ inst = (ldbm_instance *) be->be_instance_info;
+ PR_ASSERT(NULL != inst);
+
+ for (handle = inst->inst_handle_head; handle != NULL; handle = next) {
+ /* Close it, and remove from the list */
+ pDB = handle->dblayer_dbp;
+ return_value |= pDB->close(pDB,0);
+ next = handle->dblayer_handle_next;
+ *(handle->dblayer_handle_ai_backpointer) = NULL;
+ slapi_ch_free((void**)&handle);
+ }
+
+ /* reset the list to make sure we don't use it again */
+ inst->inst_handle_tail = NULL;
+ inst->inst_handle_head = NULL;
+
+ return return_value;
+}
+
+int dblayer_instance_close(backend *be)
+{
+ DB *pDB = NULL;
+ int return_value = 0;
+ DB_ENV * env = 0;
+ ldbm_instance *inst = (ldbm_instance *)be->be_instance_info;
+
+ if (NULL == inst)
+ return -1;
+
+ return_value = dblayer_close_indexes(be);
+
+ /* Now close id2entry if it's open */
+ pDB = inst->inst_id2entry;
+ if (NULL != pDB) {
+ return_value |= pDB->close(pDB,0);
+ }
+ inst->inst_id2entry = NULL;
+
+ if (inst->import_env) {
+ /* ignore the value of env, close, because at this point, work is done with import env
+ by calling env.close, env and all the associated db handles will be closed, ignore,
+ if sleepycat complains, that db handles are open at env close time */
+ return_value |= inst->import_env->dblayer_DB_ENV->close(inst->import_env->dblayer_DB_ENV, 0);
+ return_value = db_env_create(&env, 0);
+ if (return_value == 0) {
+ char inst_dir[MAXPATHLEN];
+ char *inst_dirp = dblayer_get_full_inst_dir(inst->inst_li, inst,
+ inst_dir, MAXPATHLEN);
+ return_value = env->remove(env, inst_dirp, 0);
+ if (return_value == EBUSY) {
+ return_value = 0; /* something else is using the env so ignore */
+ }
+ if (inst_dirp != inst_dir)
+ slapi_ch_free_string(&inst_dirp);
+ }
+ PR_DestroyRWLock(inst->import_env->dblayer_env_lock);
+ PR_Free((void *)inst->import_env);
+ inst->import_env = NULL;
+ } else {
+ be->be_state = BE_STATE_STOPPED;
+ }
+
+ return return_value;
+}
+
+void dblayer_pre_close(struct ldbminfo *li)
+{
+ dblayer_private *priv = 0;
+
+ PR_ASSERT(NULL != li);
+ priv = (dblayer_private*)li->li_dblayer_private;
+
+ if (priv->dblayer_stop_threads) /* already stopped. do nothing... */
+ return;
+
+ /* Now stop the make sure they've stopped,
+ * the various threads we have running */
+ priv->dblayer_stop_threads = 1;
+ if (priv->dblayer_thread_count > 0) {
+ int sleep_counter = 0;
+ /* Print handy-dandy log message */
+ LDAPDebug(LDAP_DEBUG_ANY,"Waiting for %d database threads to stop\n",
+ priv->dblayer_thread_count, 0,0);
+ while(priv->dblayer_thread_count > 0) {
+ /* We have no alternative here but to wait for them to finish,
+ * since if any thread activity,
+ * such as a checkpoint, wasn't finished when we shut down,
+ * the database would need recovery on startup */
+ PRIntervalTime interval;
+
+ sleep_counter++;
+ if (0 == sleep_counter % 10) {
+ if (priv->dblayer_thread_count > 1) {
+ LDAPDebug(LDAP_DEBUG_ANY,
+ "Still waiting on %d database threads...\n",
+ priv->dblayer_thread_count,0,0);
+ } else {
+ LDAPDebug(LDAP_DEBUG_ANY,
+ "Still waiting one database thread...\n",0,0,0);
+ }
+ }
+
+ interval = PR_MillisecondsToInterval(DBLAYER_SLEEP_INTERVAL);
+ DS_Sleep(interval);
+ if (sleep_counter >= 100) {
+ LDAPDebug(LDAP_DEBUG_ANY,"Timeout; leave %d thread(s)...\n",
+ priv->dblayer_thread_count,0,0);
+ /*
+ * The guardian file should not
+ * get created under the timeout condition.
+ */
+ priv->dblayer_bad_stuff_happened = 1;
+ goto timeout_escape;
+ }
+ }
+ LDAPDebug(LDAP_DEBUG_ANY,"All database threads now stopped\n",0,0,0);
+ }
+timeout_escape:
+ return;
+}
+
+int dblayer_post_close(struct ldbminfo *li, int dbmode)
+{
+ dblayer_private *priv = 0;
+ int return_value = 0;
+ dblayer_private_env *pEnv;
+
+ PR_ASSERT(NULL != li);
+ priv = (dblayer_private*)li->li_dblayer_private;
+
+ /* We close all the files we ever opened, and call pEnv->close. */
+ if (NULL == priv->dblayer_env) /* db env is already closed. do nothing. */
+ return return_value;
+ /* Shutdown the performance counter stuff */
+ if (DBLAYER_NORMAL_MODE & dbmode) {
+ if (priv->perf_private) {
+ perfctrs_terminate(&priv->perf_private);
+ }
+ }
+
+ /* Now release the db environment */
+ pEnv = priv->dblayer_env;
+ return_value = pEnv->dblayer_DB_ENV->close(pEnv->dblayer_DB_ENV, 0);
+ PR_DestroyRWLock(priv->dblayer_env->dblayer_env_lock);
+ PR_Free((void *) priv->dblayer_env);
+
+ priv->dblayer_env = NULL; /* pEnv is now garbage */
+
+ if (return_value == 0) {
+ DB_ENV *env = 0;
+ return_value = db_env_create(&env, 0);
+ /* don't be tempted to use the
+ previously nulled out env handle
+ as Sleepycat 3.x is unhappy if
+ the env handle handed to remove
+ was used elsewhere. rwagner */
+ if (return_value == 0) {
+ char *home_dir = dblayer_get_home_dir(li, NULL);
+ if (home_dir)
+ /* DBDB do NOT remove the environment: bad, bad idea
+ * return_value = env->remove(env, home_dir, 0);
+ */
+ if (0 == return_value
+ && !((DBLAYER_ARCHIVE_MODE|DBLAYER_EXPORT_MODE) & dbmode)
+ && !priv->dblayer_bad_stuff_happened) {
+ /*
+ * If we got here, we have a good consistent database,
+ * so we write the guard file
+ */
+ commit_good_database(priv);
+ } else if (return_value == EBUSY) {
+ /* something else is using the env so ignore */
+ /* but let's not make a guardian file */
+ return_value = 0;
+ }
+ }
+ }
+
+ return return_value;
+}
+
+/*
+ * This function is called when the server is shutting down.
+ * This is not safe to call while other threads are calling into the open
+ * databases !!! So: DON'T !
+ */
+int dblayer_close(struct ldbminfo *li, int dbmode)
+{
+ backend *be = NULL;
+ ldbm_instance *inst;
+ Object *inst_obj;
+ int return_value = 0;
+
+ dblayer_pre_close(li);
+
+ /*
+ * dblayer_close_indexes and pDB->close used to be located above loop:
+ * while(priv->dblayer_thread_count > 0) in pre_close.
+ * This order fixes a bug: shutdown under the stress makes txn_checkpoint
+ * (checkpoint_thread) fail b/c the mpool might have been already closed.
+ */
+ for (inst_obj = objset_first_obj(li->li_instance_set); inst_obj;
+ inst_obj = objset_next_obj(li->li_instance_set, inst_obj)) {
+ inst = (ldbm_instance *)object_get_data(inst_obj);
+ be = inst->inst_be;
+ if (NULL != be->be_instance_info) {
+ return_value |= dblayer_instance_close(be);
+ }
+ }
+
+ if (return_value != 0) {
+ /* force recovery next startup if any close failed */
+ dblayer_private *priv;
+ PR_ASSERT(NULL != li);
+ priv = (dblayer_private*)li->li_dblayer_private;
+ PR_ASSERT(NULL != priv);
+ priv->dblayer_bad_stuff_happened = 1;
+ }
+
+ return_value |= dblayer_post_close(li, dbmode);
+
+ return return_value;
+}
+
+/*
+ * Called to tell us to flush any data not on disk to the disk
+ * for the transacted database, we interpret this as an instruction
+ * to write a checkpoint.
+ */
+int dblayer_flush(struct ldbminfo *li)
+{
+ return 0;
+}
+
+#if !defined(DB_DUPSORT)
+#define DB_DUPSORT 0
+#endif
+
+/* Routines for opening and closing random files in the DB_ENV.
+ Used by ldif2db merging code currently.
+
+ Return value:
+ Success: 0
+ Failure: -1
+ */
+int dblayer_open_file(backend *be, char* indexname, int open_flag, int index_flags, DB **ppDB)
+{
+ struct ldbminfo *li = (struct ldbminfo *) be->be_database->plg_private;
+ ldbm_instance *inst = (ldbm_instance *) be->be_instance_info;
+ int open_flags = 0;
+ char *file_name = NULL;
+ char *rel_path = NULL;
+ dblayer_private_env *pENV = 0;
+ dblayer_private *priv = NULL;
+ int len;
+ int return_value = 0;
+ DB *dbp = NULL;
+ char *subname = NULL;
+
+ PR_ASSERT(NULL != li);
+ priv = (dblayer_private*)li->li_dblayer_private;
+ PR_ASSERT(NULL != priv);
+
+ if (NULL == inst->inst_dir_name)
+ {
+ if (dblayer_get_instance_data_dir(be) != 0)
+ return -1;
+ }
+
+ if (NULL != inst->inst_parent_dir_name)
+ {
+ if (!charray_utf8_inlist(priv->dblayer_data_directories,
+ inst->inst_parent_dir_name) &&
+ !is_fullpath(inst->inst_dir_name))
+
+ {
+ LDAPDebug(LDAP_DEBUG_ANY,
+ "The instance path %s is not registered for db_data_dir, "
+ "although %s is a relative path.\n",
+ inst->inst_parent_dir_name, inst->inst_dir_name, 0);
+ return -1;
+ }
+ }
+ len = strlen(indexname) + strlen(LDBM_FILENAME_SUFFIX) + 1;
+ file_name = slapi_ch_malloc(len);
+ len += strlen(inst->inst_dir_name) + 1;
+ rel_path = slapi_ch_malloc(len);
+
+ pENV = priv->dblayer_env;
+ if (inst->import_env)
+ pENV = inst->import_env;
+
+ PR_ASSERT(NULL != pENV);
+ sprintf(file_name, "%s%s", indexname, LDBM_FILENAME_SUFFIX);
+ sprintf(rel_path, "%s/%s", inst->inst_dir_name, file_name);
+
+ open_flags = DB_THREAD;
+ if (open_flag & DBOPEN_CREATE)
+ open_flags |= DB_CREATE;
+
+ if (!ppDB)
+ goto out;
+ return_value = db_create(ppDB, pENV->dblayer_DB_ENV, 0);
+ if (0 != return_value)
+ goto out;
+
+ dbp = *ppDB;
+/* With the new idl design, the large 8Kbyte pages we use are not
+ optimal. The page pool churns very quickly as we add new IDs under a
+ sustained add load. Smaller pages stop this happening so much and
+ consequently make us spend less time flushing dirty pages on checkpoints.
+ But 8K is still a good page size for id2entry. So we now allow different
+ page sizes for the primary and secondary indices.
+ Filed as bug: 604654
+ */
+ if (idl_get_idl_new()) {
+ return_value = dbp->set_pagesize(
+ dbp,
+ (priv->dblayer_index_page_size == 0) ?
+ DBLAYER_INDEX_PAGESIZE : priv->dblayer_index_page_size
+ );
+ } else {
+ return_value = dbp->set_pagesize(
+ dbp,
+ (priv->dblayer_page_size == 0) ?
+ DBLAYER_PAGESIZE : priv->dblayer_page_size
+ );
+ }
+ if (0 != return_value)
+ goto out;
+
+#if 1000*DB_VERSION_MAJOR + 100*DB_VERSION_MINOR < 3300
+ return_value = dbp->set_malloc(dbp, malloc);
+ if (0 != return_value) {
+ goto out;
+ }
+#endif
+
+ if (idl_get_idl_new() && !(index_flags & INDEX_VLV)) {
+ return_value = dbp->set_flags(dbp, DB_DUP | DB_DUPSORT);
+ if (0 != return_value)
+ goto out;
+
+ return_value = dbp->set_dup_compare(
+ dbp,
+ idl_new_compare_dups
+ );
+ if (0 != return_value)
+ goto out;
+ }
+
+ if (index_flags & INDEX_VLV) {
+ /*
+ * Need index with record numbers for
+ * Virtual List View index
+ */
+ return_value = dbp->set_flags(dbp, DB_RECNUM);
+ if (0 != return_value)
+ goto out;
+ }
+
+ /* The subname argument allows applications to have
+ * subdatabases, i.e., multiple databases inside of a single
+ * physical file. This is useful when the logical databases
+ * are both numerous and reasonably small, in order to
+ * avoid creating a large number of underlying files.
+ */
+ if ((charray_get_index(priv->dblayer_data_directories,
+ inst->inst_parent_dir_name) != 0) &&
+ !dblayer_inst_exists(inst, file_name))
+ {
+ char inst_dir[MAXPATHLEN];
+ char *inst_dirp = NULL;
+ char *abs_file_name = NULL;
+ int abs_len;
+ /* create a file with abs path, then try again */
+
+ inst_dirp = dblayer_get_full_inst_dir(li, inst, inst_dir, MAXPATHLEN);
+ abs_len = strlen(inst_dirp) + strlen(file_name) + 2;
+ abs_file_name = (char *)slapi_ch_malloc(abs_len);
+ sprintf(abs_file_name, "%s%c%s",
+ inst_dirp, get_sep(inst_dirp), file_name);
+ DB_OPEN(pENV->dblayer_openflags,
+ dbp, NULL/* txnid */, abs_file_name, subname, DB_BTREE,
+ open_flags, priv->dblayer_file_mode, return_value);
+ dbp->close(dbp, 0);
+ return_value = db_create(ppDB, pENV->dblayer_DB_ENV, 0);
+ if (0 != return_value)
+ goto out;
+ dbp = *ppDB;
+
+ slapi_ch_free_string(&abs_file_name);
+ if (inst_dirp != inst_dir)
+ slapi_ch_free_string(&inst_dirp);
+ }
+ DB_OPEN(pENV->dblayer_openflags,
+ dbp, NULL, /* txnid */ rel_path, subname, DB_BTREE,
+ open_flags, priv->dblayer_file_mode, return_value);
+#if 1000*DB_VERSION_MAJOR + 100*DB_VERSION_MINOR == 4100
+ /* lower the buffer cache priority to avoid sleep in memp_alloc */
+ /* W/ DB_PRIORITY_LOW, the db buffer page priority is calculated as:
+ * priority = lru_count + pages / (-1)
+ * (by default, priority = lru_count)
+ * When upgraded to db4.2, this setting may not needed, hopefully.
+ * ask sleepycat [#8301]; blackflag #619964
+ */
+ dbp->set_cache_priority(dbp, DB_PRIORITY_LOW);
+#endif
+out:
+ slapi_ch_free((void**)&file_name);
+ slapi_ch_free((void**)&rel_path);
+ /* close the database handle to avoid handle leak */
+ if (dbp && (return_value != 0)) {
+ dblayer_close_file(dbp);
+ }
+ return return_value;
+}
+
+int dblayer_close_file(DB *db)
+{
+ return db->close(db,0);
+}
+
+/*
+ * OK, this is a tricky one. We store open DB* handles within an AVL
+ * structure used in other parts of the back-end. This is nasty, because
+ * that code has no idea we're doing this, and we don't have much control
+ * over what it does. But, the reason is that we want to get fast lookup
+ * of the index file pertaining to each particular attribute. Putting the
+ * DB* handles in the attribute info structures is an easy way to achieve this
+ * because we already lookup this structure as part of an index lookup.
+ */
+
+/*
+ * This function takes an attrinfo structure and returns a valid
+ * DB* handle for the index file corresponding to this attribute.
+ */
+
+/*
+ * If the db library is non-reentrant, we lock the mutex here,
+ * see comments above for id2entry for the details on this.
+ */
+
+/*
+ * The create flag determines if the index file should be created if it
+ * does not already exist.
+ */
+
+int dblayer_get_index_file(backend *be, struct attrinfo *a, DB** ppDB, int open_flags)
+{
+ /*
+ * We either already have a DB* handle in the attrinfo structure.
+ * in which case we simply return it to the caller, OR:
+ * we need to make one. We do this as follows:
+ * 1a) acquire the mutex that protects the handle list.
+ * 1b) check that the DB* is still null.
+ * 2) get the filename, and call libdb to open it
+ * 3) if successful, store the result in the attrinfo stucture
+ * 4) store the DB* in our own list so we can close it later.
+ * 5) release the mutex.
+ */
+ ldbm_instance *inst = (ldbm_instance *) be->be_instance_info;
+ int return_value = -1;
+ DB *pDB = NULL;
+ char *attribute_name = a->ai_type;
+
+ *ppDB = NULL;
+
+ /* it's like a semaphore -- when count > 0, any file handle that's in
+ * the attrinfo will remain valid from here on.
+ */
+ PR_AtomicIncrement(&a->ai_dblayer_count);
+
+ if (NULL != a->ai_dblayer) {
+ /* This means that the pointer is valid, so we should return it. */
+ *ppDB = ((dblayer_handle*)(a->ai_dblayer))->dblayer_dbp;
+ return 0;
+ }
+
+ /* attrinfo handle is NULL, at least for now -- grab the mutex and try
+ * again.
+ */
+ PR_Lock(inst->inst_handle_list_mutex);
+ if (NULL != a->ai_dblayer) {
+ /* another thread set the handle while we were waiting on the lock */
+ *ppDB = ((dblayer_handle*)(a->ai_dblayer))->dblayer_dbp;
+ PR_Unlock(inst->inst_handle_list_mutex);
+ return 0;
+ }
+
+ /* attrinfo handle is still blank, and we have the mutex: open the
+ * index file and stuff it in the attrinfo.
+ */
+ return_value = dblayer_open_file(be, attribute_name, open_flags,
+ a->ai_indexmask, &pDB);
+ if (0 == return_value) {
+ /* Opened it OK */
+ dblayer_handle *handle = (dblayer_handle *)
+ slapi_ch_calloc(1, sizeof(dblayer_handle));
+
+ if (NULL == handle) {
+ /* Memory allocation failed */
+ return_value = -1;
+ } else {
+ dblayer_handle *prev_handle = inst->inst_handle_tail;
+
+ PR_ASSERT(NULL != pDB);
+ /* Store the returned DB* in our own private list of
+ * open files */
+ if (NULL == prev_handle) {
+ /* List was empty */
+ inst->inst_handle_tail = handle;
+ inst->inst_handle_head = handle;
+ } else {
+ /* Chain the handle onto the last structure in the
+ * list */
+ inst->inst_handle_tail = handle;
+ prev_handle->dblayer_handle_next = handle;
+ }
+ /* Stash a pointer to our wrapper structure in the
+ * attrinfo structure */
+ handle->dblayer_dbp = pDB;
+ /* And, most importantly, return something to the caller!*/
+ *ppDB = pDB;
+ /* and save the hande in the attrinfo structure for
+ * next time */
+ a->ai_dblayer = handle;
+ /* don't need to update count -- we incr'd it already */
+ handle->dblayer_handle_ai_backpointer = &(a->ai_dblayer);
+ }
+ } else {
+ /* Did not open it OK ! */
+ /* Do nothing, because return value and fact that we didn't
+ * store a DB* in the attrinfo is enough
+ */
+ }
+ PR_Unlock(inst->inst_handle_list_mutex);
+
+ if (return_value != 0) {
+ /* some sort of error -- we didn't open a handle at all.
+ * decrement the refcount back to where it was.
+ */
+ PR_AtomicDecrement(&a->ai_dblayer_count);
+ }
+
+ return return_value;
+}
+
+/*
+ * Unlock the db lib mutex here if we need to.
+ */
+int dblayer_release_index_file(backend *be,struct attrinfo *a, DB* pDB)
+{
+ PR_AtomicDecrement(&a->ai_dblayer_count);
+ return 0;
+}
+
+/*
+ dblayer_db_remove assumptions:
+
+ No environment has the given database open.
+
+*/
+
+static int
+dblayer_db_remove_ex(dblayer_private_env *env, char const path[], char const dbName[], PRBool use_lock) {
+ DB_ENV * db_env = 0;
+ int rc;
+ DB *db;
+
+ if (env) {
+ if(use_lock) PR_RWLock_Wlock(env->dblayer_env_lock); /* We will be causing logging activity */
+ db_env = env->dblayer_DB_ENV;
+ }
+
+ rc = db_create(&db, db_env, 0); /* must use new handle to database */
+ if (0 != rc) {
+ LDAPDebug(LDAP_DEBUG_ANY, "db_remove: Failed to create db (%d) %s\n",
+ rc, dblayer_strerror(rc), 0);
+ goto done;
+ }
+ rc = db->remove(db, path, dbName, 0); /* kiss the db goodbye! */
+
+done:
+ if (env) {
+ if(use_lock) PR_RWLock_Unlock(env->dblayer_env_lock);
+ }
+
+ return rc;
+}
+
+
+int
+dblayer_db_remove(dblayer_private_env * env, char const path[], char const dbName[]) {
+ return(dblayer_db_remove_ex(env,path,dbName,PR_TRUE));
+}
+
+#define DBLAYER_CACHE_DELAY PR_MillisecondsToInterval(250)
+int dblayer_erase_index_file_ex(backend *be, struct attrinfo *a,
+ PRBool use_lock, int no_force_checkpoint)
+{
+ struct ldbminfo *li = (struct ldbminfo *) be->be_database->plg_private;
+ dblayer_private *priv = (dblayer_private*) li->li_dblayer_private;
+ struct dblayer_private_env *pEnv = priv->dblayer_env;
+ ldbm_instance *inst = (ldbm_instance *) be->be_instance_info;
+ dblayer_handle *handle;
+ char dbName[MAXPATHLEN];
+ char *dbNamep;
+ char *p;
+ int dbbasenamelen, dbnamelen;
+ int rc = 0;
+ DB *db = 0;
+
+ if (NULL == pEnv) /* index file does not exist */
+ return rc;
+
+ /* Added for bug 600401. Somehow the checkpoint thread deadlocked on
+ index file with this function, index file couldn't be removed on win2k.
+ Force a checkpoint here to break deadlock.
+ */
+ if (0 == no_force_checkpoint) {
+ dblayer_force_checkpoint(li);
+ }
+
+ if (dblayer_get_index_file(be, a, &db, DBOPEN_CREATE) == 0) {
+ /* first, remove the file handle for this index, if we have it open */
+ PR_Lock(inst->inst_handle_list_mutex);
+ if (a->ai_dblayer) {
+ /* there is a handle */
+ handle = (dblayer_handle *)a->ai_dblayer;
+
+ /* when we successfully called dblayer_get_index_file we bumped up
+ the reference count of how many threads are using the index. So we
+ must manually back off the count by one here.... rwagner */
+
+ dblayer_release_index_file(be, a, db);
+
+ while (a->ai_dblayer_count > 0) {
+ /* someone is using this index file */
+ /* ASSUMPTION: you have already set the INDEX_OFFLINE flag, because
+ * you intend to mess with this index. therefore no new requests
+ * for this indexfile should happen, so the dblayer_count should
+ * NEVER increase.
+ */
+ PR_ASSERT(a->ai_indexmask & INDEX_OFFLINE);
+ PR_Unlock(inst->inst_handle_list_mutex);
+ DS_Sleep(DBLAYER_CACHE_DELAY);
+ PR_Lock(inst->inst_handle_list_mutex);
+ }
+ dblayer_close_file(handle->dblayer_dbp);
+
+ /* remove handle from handle-list */
+ if (inst->inst_handle_head == handle) {
+ inst->inst_handle_head = handle->dblayer_handle_next;
+ if (inst->inst_handle_tail == handle) {
+ inst->inst_handle_tail = NULL;
+ }
+ } else {
+ dblayer_handle *hp;
+
+ for (hp = inst->inst_handle_head; hp; hp = hp->dblayer_handle_next) {
+ if (hp->dblayer_handle_next == handle) {
+ hp->dblayer_handle_next = handle->dblayer_handle_next;
+ if (inst->inst_handle_tail == handle) {
+ inst->inst_handle_tail = hp;
+ }
+ break;
+ }
+ }
+ }
+ dbNamep = dblayer_get_full_inst_dir(li, inst, dbName, MAXPATHLEN);
+ dbbasenamelen = strlen(dbNamep);
+ dbnamelen = dbbasenamelen + strlen(a->ai_type) + 6;
+ if (dbnamelen > MAXPATHLEN)
+ {
+ dbNamep = (char *)slapi_ch_realloc(dbNamep, dbnamelen);
+ }
+ p = dbNamep + dbbasenamelen;
+ sprintf(p, "%c%s%s", get_sep(dbNamep), a->ai_type, LDBM_FILENAME_SUFFIX);
+ rc = dblayer_db_remove_ex(pEnv, dbNamep, 0, use_lock);
+ a->ai_dblayer = NULL;
+ slapi_ch_free((void **)&handle);
+ if (dbNamep != dbName)
+ slapi_ch_free_string(&dbNamep);
+ } else {
+ /* no handle to close */
+ }
+ PR_Unlock(inst->inst_handle_list_mutex);
+
+ }
+
+ return rc;
+}
+
+int dblayer_erase_index_file_nolock(backend *be, struct attrinfo *a, int no_force_chkpt) {
+ return dblayer_erase_index_file_ex(be,a,PR_FALSE,no_force_chkpt);
+}
+
+int dblayer_erase_index_file(backend *be, struct attrinfo *a, int no_force_chkpt) {
+ return dblayer_erase_index_file_ex(be,a,PR_TRUE,no_force_chkpt);
+}
+
+
+/*
+ * Transaction stuff. The idea is that the caller doesn't need to
+ * know the transaction mechanism underneath (because the caller is
+ * typically a few calls up the stack from any DB stuff).
+ * Sadly, in slapd there was no handy structure associated with
+ * an LDAP operation, and passed around evberywhere, so we had
+ * to invent the back_txn structure.
+ * The lower levels of the back-end look into this structure, and
+ * take out the DB_TXN they need.
+ */
+int dblayer_txn_init(struct ldbminfo *li, back_txn *txn)
+{
+ PR_ASSERT(NULL != txn);
+
+ txn->back_txn_txn = NULL;
+ return 0;
+}
+
+
+int dblayer_txn_begin_ext(struct ldbminfo *li, back_txnid parent_txn, back_txn *txn, PRBool use_lock)
+{
+ int return_value = -1;
+ dblayer_private *priv = NULL;
+ PR_ASSERT(NULL != txn);
+ PR_ASSERT(NULL != li);
+ /*
+ * When server is shutting down, some components need to
+ * flush some data (e.g. replication to write ruv).
+ * So don't check shutdown signal unless we can't write.
+ */
+ if ( g_get_shutdown() == SLAPI_SHUTDOWN_DISKFULL ) {
+ return return_value;
+ }
+
+ priv = (dblayer_private*)li->li_dblayer_private;
+ PR_ASSERT(NULL != priv);
+
+ if (priv->dblayer_enable_transactions)
+ {
+ dblayer_private_env *pEnv = priv->dblayer_env;
+ if(use_lock) PR_RWLock_Rlock(pEnv->dblayer_env_lock);
+ return_value = TXN_BEGIN(pEnv->dblayer_DB_ENV,
+ (DB_TXN*)parent_txn,
+ &txn->back_txn_txn,
+ 0);
+ if (0 != return_value)
+ {
+ if(use_lock) PR_RWLock_Unlock(priv->dblayer_env->dblayer_env_lock);
+ txn->back_txn_txn = NULL;
+ }
+ } else
+ {
+ return_value = 0;
+ }
+ if (0 != return_value)
+ {
+ LDAPDebug(LDAP_DEBUG_ANY,
+ "Serious Error---Failed in dblayer_txn_begin, err=%d (%s)\n",
+ return_value, dblayer_strerror(return_value), 0);
+ }
+ return return_value;
+}
+
+int dblayer_read_txn_begin(struct ldbminfo *li, back_txnid parent_txn, back_txn *txn) {
+ return (dblayer_txn_begin_ext(li,parent_txn,txn,PR_FALSE));
+}
+
+int dblayer_txn_begin(struct ldbminfo *li, back_txnid parent_txn, back_txn *txn) {
+ return (dblayer_txn_begin_ext(li,parent_txn,txn,PR_TRUE));
+}
+
+
+int dblayer_txn_commit_ext(struct ldbminfo *li, back_txn *txn, PRBool use_lock)
+{
+ int return_value = -1;
+ dblayer_private *priv = NULL;
+ DB_TXN *db_txn;
+
+ PR_ASSERT(NULL != txn);
+ PR_ASSERT(NULL != li);
+
+ priv = (dblayer_private*)li->li_dblayer_private;
+ PR_ASSERT(NULL != priv);
+
+ db_txn = txn->back_txn_txn;
+ if (NULL != db_txn &&
+ 1 != priv->dblayer_stop_threads &&
+ priv->dblayer_env &&
+ priv->dblayer_enable_transactions)
+ {
+ return_value = TXN_COMMIT(db_txn, 0);
+ if ((priv->dblayer_durable_transactions) && use_lock ) {
+ if(trans_batch_limit > 0) {
+ if(trans_batch_count % trans_batch_limit) {
+ trans_batch_count++;
+ } else {
+ LOG_FLUSH(priv->dblayer_env->dblayer_DB_ENV,0);
+ trans_batch_count=1;
+ }
+ } else if(trans_batch_limit == FLUSH_REMOTEOFF) { /* user remotely turned batching off */
+ LOG_FLUSH(priv->dblayer_env->dblayer_DB_ENV,0);
+ }
+ }
+ if(use_lock) PR_RWLock_Unlock(priv->dblayer_env->dblayer_env_lock);
+ } else
+ {
+ return_value = 0;
+ }
+
+ if (0 != return_value)
+ {
+ LDAPDebug(LDAP_DEBUG_ANY,
+ "Serious Error---Failed in dblayer_txn_commit, err=%d (%s)\n",
+ return_value, dblayer_strerror(return_value), 0);
+ if (LDBM_OS_ERR_IS_DISKFULL(return_value)) {
+ operation_out_of_disk_space();
+ }
+ }
+ return return_value;
+}
+
+int dblayer_read_txn_commit(struct ldbminfo *li, back_txn *txn) {
+ return(dblayer_txn_commit_ext(li,txn,PR_FALSE));
+}
+
+int dblayer_txn_commit(struct ldbminfo *li, back_txn *txn) {
+ return(dblayer_txn_commit_ext(li,txn,PR_TRUE));
+}
+
+int dblayer_txn_abort_ext(struct ldbminfo *li, back_txn *txn, PRBool use_lock)
+{
+ int return_value = -1;
+ dblayer_private *priv = NULL;
+ DB_TXN *db_txn;
+
+ PR_ASSERT(NULL != txn);
+ PR_ASSERT(NULL != li);
+
+ priv = (dblayer_private*)li->li_dblayer_private;
+ PR_ASSERT(NULL != priv);
+
+ db_txn = txn->back_txn_txn;
+ if (NULL != db_txn &&
+ priv->dblayer_env &&
+ priv->dblayer_enable_transactions)
+ {
+ return_value = TXN_ABORT(db_txn);
+ if(use_lock) PR_RWLock_Unlock(priv->dblayer_env->dblayer_env_lock);
+ } else
+ {
+ return_value = 0;
+ }
+
+ if (0 != return_value)
+ {
+ LDAPDebug(LDAP_DEBUG_ANY,
+ "Serious Error---Failed in dblayer_txn_abort, err=%d (%s)\n",
+ return_value, dblayer_strerror(return_value), 0);
+ if (LDBM_OS_ERR_IS_DISKFULL(return_value)) {
+ operation_out_of_disk_space();
+ }
+ }
+ return return_value;
+}
+
+int dblayer_read_txn_abort(struct ldbminfo *li, back_txn *txn){
+ return(dblayer_txn_abort_ext(li,txn,PR_FALSE));
+}
+
+int dblayer_txn_abort(struct ldbminfo *li, back_txn *txn){
+ return(dblayer_txn_abort_ext(li,txn,PR_TRUE));
+}
+
+
+size_t dblayer_get_optimal_block_size(struct ldbminfo *li)
+{
+ dblayer_private *priv = NULL;
+ size_t page_size = 0;
+
+ PR_ASSERT(NULL != li);
+
+ priv = (dblayer_private*)li->li_dblayer_private;
+ PR_ASSERT(NULL != priv);
+
+ page_size = (priv->dblayer_page_size == 0) ? DBLAYER_PAGESIZE : priv->dblayer_page_size;
+ if (priv->dblayer_idl_divisor == 0)
+ {
+ return page_size - DB_EXTN_PAGE_HEADER_SIZE;
+ } else
+ {
+ return page_size / priv->dblayer_idl_divisor;
+ }
+}
+
+
+/*
+ * The dblock serializes writes to the database,
+ * which reduces deadlocking in the db code,
+ * which means that we run faster.
+ */
+void dblayer_lock_backend(backend *be)
+{
+ ldbm_instance *inst;
+
+ PR_ASSERT(NULL != be);
+ inst = (ldbm_instance *) be->be_instance_info;
+ PR_ASSERT(NULL != inst);
+
+ if (NULL != inst->inst_db_mutex) {
+ PR_Lock(inst->inst_db_mutex);
+ }
+}
+
+void dblayer_unlock_backend(backend *be)
+{
+ ldbm_instance *inst;
+
+ PR_ASSERT(NULL != be);
+ inst = (ldbm_instance *) be->be_instance_info;
+ PR_ASSERT(NULL != inst);
+
+ if (NULL != inst->inst_db_mutex) {
+ PR_Unlock(inst->inst_db_mutex);
+ }
+}
+
+
+/* code which implements checkpointing and log file truncation */
+
+/*
+ * create a thread for perf_threadmain
+ */
+static int
+dblayer_start_perf_thread(struct ldbminfo *li)
+{
+ int return_value = 0;
+ if (NULL == PR_CreateThread (PR_USER_THREAD,
+ (VFP) (void *) perf_threadmain, li,
+ PR_PRIORITY_NORMAL, PR_GLOBAL_THREAD,
+ PR_UNJOINABLE_THREAD,
+ SLAPD_DEFAULT_THREAD_STACKSIZE) )
+ {
+ PRErrorCode prerr = PR_GetError();
+ LDAPDebug(LDAP_DEBUG_ANY, "failed to create database perf thread, "
+ SLAPI_COMPONENT_NAME_NSPR " error %d (%s)\n",
+ prerr, slapd_pr_strerror(prerr), 0);
+ return_value = -1;
+ }
+ return return_value;
+}
+
+/* Performance thread */
+static int perf_threadmain(void *param)
+{
+ dblayer_private *priv = NULL;
+ struct ldbminfo *li = NULL;
+
+ PR_ASSERT(NULL != param);
+ li = (struct ldbminfo*)param;
+
+ priv = (dblayer_private*)li->li_dblayer_private;
+ PR_ASSERT(NULL != priv);
+ PR_AtomicIncrement(&(priv->dblayer_thread_count));
+ while (!priv->dblayer_stop_threads) {
+ /* sleep for a while, updating perf counters if we need to */
+ perfctrs_wait(1000,priv->perf_private,priv->dblayer_env->dblayer_DB_ENV);
+ }
+ PR_AtomicDecrement(&(priv->dblayer_thread_count));
+ return 0;
+}
+
+/*
+ * create a thread for deadlock_threadmain
+ */
+static int
+dblayer_start_deadlock_thread(struct ldbminfo *li)
+{
+ int return_value = 0;
+ if (NULL == PR_CreateThread (PR_USER_THREAD,
+ (VFP) (void *) deadlock_threadmain, li,
+ PR_PRIORITY_NORMAL, PR_GLOBAL_THREAD,
+ PR_UNJOINABLE_THREAD,
+ SLAPD_DEFAULT_THREAD_STACKSIZE) )
+ {
+ PRErrorCode prerr = PR_GetError();
+ LDAPDebug(LDAP_DEBUG_ANY, "failed to create database deadlock thread, "
+ SLAPI_COMPONENT_NAME_NSPR " error %d (%s)\n",
+ prerr, slapd_pr_strerror(prerr), 0);
+ return_value = -1;
+ }
+ return return_value;
+}
+
+/* checkpoint thread main function */
+
+static int deadlock_threadmain(void *param)
+{
+ int rval = -1;
+ dblayer_private *priv = NULL;
+ struct ldbminfo *li = NULL;
+ PRIntervalTime interval; /*NSPR timeout stuffy*/
+
+ PR_ASSERT(NULL != param);
+ li = (struct ldbminfo*)param;
+
+ priv = (dblayer_private*)li->li_dblayer_private;
+ PR_ASSERT(NULL != priv);
+
+ interval = PR_MillisecondsToInterval(100);
+ PR_AtomicIncrement(&(priv->dblayer_thread_count));
+ while (!priv->dblayer_stop_threads)
+ {
+ if (priv->dblayer_enable_transactions)
+ {
+ if (NULL != priv->dblayer_env->dblayer_DB_ENV->lk_handle) {
+ int aborted;
+ if ((rval = LOCK_DETECT(priv->dblayer_env->dblayer_DB_ENV,
+ 0,
+ DB_LOCK_YOUNGEST,
+ &aborted))
+ != 0) {
+ LDAPDebug(LDAP_DEBUG_ANY,
+ "Serious Error---Failed in deadlock detect (aborted at 0x%x), err=%d (%s)\n",
+ aborted, rval, dblayer_strerror(rval));
+ }
+ }
+ }
+ DS_Sleep(interval);
+ }
+ PR_AtomicDecrement(&(priv->dblayer_thread_count));
+ return 0;
+}
+
+#define checkpoint_debug_message(debug, fmt, a1, a2, a3) \
+ if (debug) { LDAPDebug(LDAP_DEBUG_ANY,fmt,a1,a2,a3); }
+
+/* this thread tries to do two things:
+ 1. catch a group of transactions that are pending allowing a worker thread
+ to work
+ 2. flush any left over transactions ( a single transaction for example)
+*/
+
+static int
+dblayer_start_log_flush_thread(dblayer_private *priv)
+{
+ int return_value = 0;
+
+ if ((priv->dblayer_durable_transactions) &&
+ (priv->dblayer_enable_transactions) && (trans_batch_limit > 0)) {
+ log_flush_thread=PR_TRUE;
+ if (NULL == PR_CreateThread (PR_USER_THREAD,
+ (VFP) (void *) log_flush_threadmain, priv,
+ PR_PRIORITY_NORMAL, PR_GLOBAL_THREAD,
+ PR_UNJOINABLE_THREAD,
+ SLAPD_DEFAULT_THREAD_STACKSIZE) ) {
+ PRErrorCode prerr = PR_GetError();
+ LDAPDebug(LDAP_DEBUG_ANY,
+ "failed to create database log flush thread, "
+ SLAPI_COMPONENT_NAME_NSPR " error %d (%s)\n",
+ prerr, slapd_pr_strerror(prerr), 0);
+ return_value = -1;
+ }
+ }
+ return return_value;
+}
+
+/* this thread tries to do two things:
+ 1. catch a group of transactions that are pending allowing a worker thread
+ to work
+ 2. flush any left over transactions ( a single transaction for example)
+*/
+
+static int log_flush_threadmain(void *param)
+{
+ dblayer_private *priv = NULL;
+ PRIntervalTime interval;
+
+
+ PR_ASSERT(NULL != param);
+ priv = (dblayer_private *) param;
+ PR_ASSERT(NULL != priv);
+ interval = PR_MillisecondsToInterval(300);
+ PR_AtomicIncrement(&(priv->dblayer_thread_count));
+ while ((!priv->dblayer_stop_threads) && (log_flush_thread))
+ {
+ if (priv->dblayer_enable_transactions)
+ {
+ DB_CHECKPOINT_LOCK(1, priv->dblayer_env->dblayer_env_lock);
+ if(trans_batch_limit > 0) {
+ if(trans_batch_count > 1) {
+ LOG_FLUSH(priv->dblayer_env->dblayer_DB_ENV,0);
+ trans_batch_count=1;
+ }
+ }
+ DB_CHECKPOINT_UNLOCK(1, priv->dblayer_env->dblayer_env_lock);
+ }
+ DS_Sleep(interval);
+ }
+ PR_AtomicDecrement(&(priv->dblayer_thread_count));
+ return 0;
+}
+
+/*
+ * create a thread for checkpoint_threadmain
+ */
+static int
+dblayer_start_checkpoint_thread(struct ldbminfo *li)
+{
+ int return_value = 0;
+ if (NULL == PR_CreateThread (PR_USER_THREAD,
+ (VFP) (void *) checkpoint_threadmain, li,
+ PR_PRIORITY_NORMAL, PR_GLOBAL_THREAD,
+ PR_UNJOINABLE_THREAD,
+ SLAPD_DEFAULT_THREAD_STACKSIZE) )
+ {
+ PRErrorCode prerr = PR_GetError();
+ LDAPDebug(LDAP_DEBUG_ANY,
+ "failed to create database checkpoint thread, "
+ SLAPI_COMPONENT_NAME_NSPR " error %d (%s)\n",
+ prerr, slapd_pr_strerror(prerr), 0);
+ return_value = -1;
+ }
+ return return_value;
+}
+
+static int checkpoint_threadmain(void *param)
+{
+ time_t time_of_last_checkpoint_completion = 0; /* seconds since epoch */
+ PRIntervalTime interval; /*NSPR timeout stuffy*/
+ int rval = -1;
+ dblayer_private *priv = NULL;
+ struct ldbminfo *li = NULL;
+ int debug_checkpointing = 0;
+ int checkpoint_interval;
+ char *home_dir = NULL;
+
+ PR_ASSERT(NULL != param);
+ li = (struct ldbminfo*)param;
+
+ home_dir = dblayer_get_home_dir(li, NULL);
+ if (NULL == home_dir)
+ {
+ LDAPDebug(LDAP_DEBUG_ANY,
+ "Checkpoint thread failed due to missing db home directory info\n",
+ 0, 0, 0);
+ return rval;
+ }
+
+ priv = (dblayer_private*)li->li_dblayer_private;
+ PR_ASSERT(NULL != priv);
+
+ /* work around a problem with newly created environments */
+ dblayer_force_checkpoint(li);
+
+ interval = PR_MillisecondsToInterval(DBLAYER_SLEEP_INTERVAL);
+ debug_checkpointing = priv->db_debug_checkpointing;
+ PR_AtomicIncrement(&(priv->dblayer_thread_count));
+ /* assumes dblayer_force_checkpoint worked */
+ time_of_last_checkpoint_completion = current_time();
+ while (!priv->dblayer_stop_threads)
+ {
+ /* sleep for a while */
+ /* why aren't we sleeping exactly the right amount of time ? */
+ /* answer---because the interval might be changed after the server
+ * starts up */
+ DS_Sleep(interval);
+
+ if (0 == priv->dblayer_enable_transactions)
+ continue;
+
+ PR_Lock(li->li_config_mutex);
+ checkpoint_interval = priv->dblayer_checkpoint_interval;
+ PR_Unlock(li->li_config_mutex);
+
+ /* Check to see if the checkpoint interval has elapsed */
+ if (current_time() - time_of_last_checkpoint_completion <
+ checkpoint_interval)
+ continue;
+
+ if (NULL == priv->dblayer_env->dblayer_DB_ENV->tx_handle)
+ continue;
+
+ /* now checkpoint */
+ checkpoint_debug_message(debug_checkpointing,
+ "Starting checkpoint\n", 0, 0, 0);
+ rval = dblayer_txn_checkpoint(li, priv->dblayer_env, PR_TRUE, PR_TRUE);
+#if 1000*DB_VERSION_MAJOR + 100*DB_VERSION_MINOR < 4100
+ if (DB_INCOMPLETE == rval)
+ {
+ checkpoint_debug_message(debug_checkpointing,
+ "Retrying checkpoint\n", 0, 0, 0);
+ } else
+#endif
+ {
+ checkpoint_debug_message(debug_checkpointing,
+ "Checkpoint Done\n", 0, 0, 0);
+ if (rval != 0) {
+ /* bad error */
+ LDAPDebug(LDAP_DEBUG_ANY,
+ "Serious Error---Failed to checkpoint database, "
+ "err=%d (%s)\n", rval, dblayer_strerror(rval), 0);
+ if (LDBM_OS_ERR_IS_DISKFULL(rval)) {
+ operation_out_of_disk_space();
+ goto diskfull_return;
+ }
+ } else {
+ time_of_last_checkpoint_completion = current_time();
+ }
+ }
+
+ checkpoint_debug_message(debug_checkpointing,
+ "Starting checkpoint\n", 0, 0, 0);
+ rval = dblayer_txn_checkpoint(li, priv->dblayer_env, PR_TRUE, PR_TRUE);
+#if 1000*DB_VERSION_MAJOR + 100*DB_VERSION_MINOR < 4100
+ if (DB_INCOMPLETE == rval)
+ {
+ checkpoint_debug_message(debug_checkpointing,
+ "Retrying checkpoint\n", 0, 0, 0);
+ } else
+#endif
+ {
+ checkpoint_debug_message(debug_checkpointing,
+ "Checkpoint Done\n", 0, 0, 0);
+ if (rval != 0) {
+ /* bad error */
+ LDAPDebug(LDAP_DEBUG_ANY,
+ "Serious Error---Failed to checkpoint database, "
+ "err=%d (%s)\n", rval, dblayer_strerror(rval), 0);
+ if (LDBM_OS_ERR_IS_DISKFULL(rval)) {
+ operation_out_of_disk_space();
+ goto diskfull_return;
+ }
+ } else {
+ time_of_last_checkpoint_completion = current_time();
+ }
+ }
+ {
+ char **list = NULL;
+ char **listp = NULL;
+ int return_value = -1;
+ char filename[MAXPATHLEN];
+ char *prefix = NULL;
+ struct dblayer_private_env *penv = priv->dblayer_env;
+ if ((NULL != priv->dblayer_log_directory) &&
+ (0 != strlen(priv->dblayer_log_directory)))
+ {
+ prefix = priv->dblayer_log_directory;
+ }
+ else
+ {
+ prefix = home_dir;
+ }
+ /* find out which log files don't contain active txns */
+ DB_CHECKPOINT_LOCK(PR_TRUE, penv->dblayer_env_lock);
+ return_value = LOG_ARCHIVE(penv->dblayer_DB_ENV, &list,
+ 0, malloc);
+ DB_CHECKPOINT_UNLOCK(PR_TRUE, penv->dblayer_env_lock);
+ checkpoint_debug_message(debug_checkpointing,
+ "Got list of logfiles not needed %d %p\n",
+ return_value,list, 0);
+ if (0 == return_value && NULL != list)
+ {
+ /* zap 'em ! */
+ for (listp = list; *listp != NULL; ++listp)
+ {
+ sprintf(filename,"%s/%s",prefix,*listp);
+ if (priv->dblayer_circular_logging) {
+ checkpoint_debug_message(debug_checkpointing,
+ "Deleting %s\n",filename, 0, 0);
+ unlink(filename);
+ } else {
+ char new_filename[MAXPATHLEN];
+ sprintf(new_filename,"%s/old.%s",
+ prefix,*listp);
+ checkpoint_debug_message(debug_checkpointing,
+ "Renaming %s\n",filename,0, 0);
+ rename(filename,new_filename);
+ }
+ }
+ slapi_ch_free((void**)&list);
+ }
+ }
+ }
+ dblayer_force_checkpoint(li);
+diskfull_return:
+ PR_AtomicDecrement(&(priv->dblayer_thread_count));
+ return 0;
+}
+
+/*
+ * create a thread for trickle_threadmain
+ */
+static int
+dblayer_start_trickle_thread(struct ldbminfo *li)
+{
+ int return_value = 0;
+ if (NULL == PR_CreateThread (PR_USER_THREAD,
+ (VFP) (void *) trickle_threadmain, li,
+ PR_PRIORITY_NORMAL, PR_GLOBAL_THREAD,
+ PR_UNJOINABLE_THREAD,
+ SLAPD_DEFAULT_THREAD_STACKSIZE) )
+ {
+ PRErrorCode prerr = PR_GetError();
+ LDAPDebug(LDAP_DEBUG_ANY, "failed to create database trickle thread, "
+ SLAPI_COMPONENT_NAME_NSPR " error %d (%s)\n",
+ prerr, slapd_pr_strerror(prerr), 0);
+ return_value = -1;
+ }
+ return return_value;
+}
+
+static int trickle_threadmain(void *param)
+{
+ PRIntervalTime interval; /*NSPR timeout stuffy*/
+ int rval = -1;
+ dblayer_private *priv = NULL;
+ struct ldbminfo *li = NULL;
+ int debug_checkpointing = 0;
+
+ PR_ASSERT(NULL != param);
+ li = (struct ldbminfo*)param;
+
+ priv = (dblayer_private*)li->li_dblayer_private;
+ PR_ASSERT(NULL != priv);
+
+ interval = PR_MillisecondsToInterval(DBLAYER_SLEEP_INTERVAL);
+ debug_checkpointing = priv->db_debug_checkpointing;
+ PR_AtomicIncrement(&(priv->dblayer_thread_count));
+ while (!priv->dblayer_stop_threads)
+ {
+ DS_Sleep(interval); /* 622855: wait for other threads fully started */
+ if (priv->dblayer_enable_transactions)
+ {
+ if ( (NULL != priv->dblayer_env->dblayer_DB_ENV->mp_handle) &&
+ (0 != priv->dblayer_trickle_percentage) )
+ {
+ int pages_written = 0;
+ if ((rval = MEMP_TRICKLE(
+ priv->dblayer_env->dblayer_DB_ENV,
+ priv->dblayer_trickle_percentage,
+ &pages_written)) != 0)
+ {
+ LDAPDebug(LDAP_DEBUG_ANY,"Serious Error---Failed to trickle, err=%d (%s)\n",rval,dblayer_strerror(rval), 0);
+ }
+ if (pages_written > 0)
+ {
+ checkpoint_debug_message(debug_checkpointing,"Trickle thread wrote %d pages\n",pages_written,0, 0);
+ }
+ }
+ }
+ }
+ PR_AtomicDecrement(&(priv->dblayer_thread_count));
+ return 0;
+}
+
+
+/* better atol -- it understands a trailing multiplier k/m/g
+ * for example, "32k" will be returned as 32768
+ * richm: added better error checking and support for 64 bit values.
+ * The err parameter is used by the caller to tell if there was an error
+ * during the a to i conversion - if 0, the value was successfully
+ * converted - if non-zero, there was some error (e.g. not a number)
+ */
+PRInt64 db_atol(char *str, int *err)
+{
+ PRInt64 mres1 = LL_INIT(0, 1);
+ PRInt64 mres2 = LL_INIT(0, 1);
+ PRInt64 mres3 = LL_INIT(0, 1);
+ PRInt64 onek = LL_INIT(0, 1024);
+ PRInt64 multiplier = LL_INIT(0, 1);
+ PRInt64 val = LL_INIT(0, 0);
+ PRInt64 result = LL_INIT(0, 0);
+ char x = 0;
+ int num = PR_sscanf(str, "%lld%c", &val, &x);
+ if (num < 1) { /* e.g. not a number */
+ if (err)
+ *err = 1;
+ return result; /* return 0 */
+ }
+
+ switch (x) {
+ case 'g':
+ case 'G':
+ LL_MUL(mres1, onek, multiplier);
+/* multiplier *= 1024;*/
+ case 'm':
+ case 'M':
+ LL_MUL(mres2, onek, mres1);
+/* multiplier *= 1024;*/
+ case 'k':
+ case 'K':
+ LL_MUL(mres3, onek, mres2);
+/* multiplier *= 1024;*/
+ }
+ LL_MUL(result, val, mres3);
+/* result = val * multiplier;*/
+ if (err)
+ *err = 0;
+ return result;
+}
+
+PRInt64 db_atoi(char *str, int *err)
+{
+ return db_atol(str, err);
+}
+
+unsigned long db_strtoul(const char *str, int *err)
+{
+ unsigned long val, result, multiplier = 1;
+ char *p;
+ errno = 0;
+
+ val = strtoul(str, &p, 10);
+ if (errno != 0) {
+ if (err) *err = errno;
+ return val;
+ }
+
+ switch (*p) {
+ case 'g':
+ case 'G':
+ multiplier *= 1024;
+ case 'm':
+ case 'M':
+ multiplier *= 1024;
+ case 'k':
+ case 'K':
+ multiplier *= 1024;
+ p++;
+ if (*p == 'b' || *p == 'B') p++;
+ if (err) {
+ /* extra chars? */
+ *err = (*p != '\0') ? EINVAL : 0;
+ }
+ break;
+ case '\0':
+ if (err) *err = 0;
+ break;
+ default:
+ if (err) *err = EINVAL;
+ return val;
+ }
+
+ result = val * multiplier;
+
+ return result;
+}
+
+/* functions called directly by the plugin interface from the front-end */
+
+/* Begin transaction */
+int dblayer_plugin_begin(Slapi_PBlock *pb)
+{
+ int return_value = -1;
+ struct ldbminfo *li = NULL;
+ back_txnid parent;
+ back_txn current;
+
+ slapi_pblock_get( pb, SLAPI_PLUGIN_PRIVATE, &li );
+ slapi_pblock_get( pb, SLAPI_PARENT_TXN, (void**)&parent );
+
+ /* call begin, and put the result in the txnid parameter */
+ return_value = dblayer_txn_begin(li,parent,&current);
+
+ if (0 == return_value)
+ {
+ slapi_pblock_set( pb, SLAPI_TXN, (void*)current.back_txn_txn );
+ }
+
+ return return_value;
+}
+
+/* Commit transaction */
+int dblayer_plugin_commit(Slapi_PBlock *pb)
+{
+ /* get the txnid and call commit */
+ int return_value = -1;
+ struct ldbminfo *li = NULL;
+ back_txn current;
+
+ slapi_pblock_get( pb, SLAPI_PLUGIN_PRIVATE, &li );
+ slapi_pblock_get( pb, SLAPI_TXN, (void**)&(current.back_txn_txn) );
+
+ /* call begin, and put the result in the txnid parameter */
+ return_value = dblayer_txn_commit(li,&current);
+
+ return return_value;
+}
+
+/* Abort Transaction */
+int dblayer_plugin_abort(Slapi_PBlock *pb)
+{
+ /* get the txnid and call abort */
+ return 0;
+}
+
+
+/* Helper function for monitor stuff */
+int dblayer_memp_stat(struct ldbminfo *li, DB_MPOOL_STAT **gsp,
+ DB_MPOOL_FSTAT ***fsp)
+{
+ dblayer_private *priv = NULL;
+ DB_ENV *env = NULL;
+
+ PR_ASSERT(NULL != li);
+
+ priv = (dblayer_private*)li->li_dblayer_private;
+ PR_ASSERT(NULL != priv);
+
+ env = priv->dblayer_env->dblayer_DB_ENV;
+ PR_ASSERT(NULL != env);
+
+ return MEMP_STAT(env, gsp, fsp, 0, malloc);
+}
+
+/* import wants this one */
+int dblayer_memp_stat_instance(ldbm_instance *inst, DB_MPOOL_STAT **gsp,
+ DB_MPOOL_FSTAT ***fsp)
+{
+ DB_ENV *env = NULL;
+ dblayer_private *priv = NULL;
+
+ PR_ASSERT(NULL != inst);
+
+ if (inst->import_env->dblayer_DB_ENV) {
+ env = inst->import_env->dblayer_DB_ENV;
+ } else {
+ priv = (dblayer_private *)inst->inst_li->li_dblayer_private;
+ PR_ASSERT(NULL != priv);
+ env = priv->dblayer_env->dblayer_DB_ENV;
+ }
+ PR_ASSERT(NULL != env);
+
+ return MEMP_STAT(env, gsp, fsp, 0, malloc);
+}
+
+/* Helper functions for recovery */
+
+#define DB_LINE_LENGTH 80
+
+static int commit_good_database(dblayer_private *priv)
+{
+ /* Write out the guard file */
+ char filename[MAXPATHLEN];
+ char line[DB_LINE_LENGTH * 2];
+ PRFileDesc *prfd;
+ int return_value = 0;
+ int num_bytes;
+
+ sprintf(filename,"%s/guardian",priv->dblayer_home_directory);
+
+ prfd = PR_Open(filename, PR_RDWR | PR_CREATE_FILE | PR_TRUNCATE,
+ priv->dblayer_file_mode );
+ if (NULL == prfd)
+ {
+ LDAPDebug( LDAP_DEBUG_ANY,"Fatal Error---Failed to write guardian file, database corruption possible" SLAPI_COMPONENT_NAME_NSPR " %d (%s)\n",
+ filename, PR_GetError(), slapd_pr_strerror(PR_GetError()) );
+ return -1;
+ }
+ sprintf(line,"cachesize:%lu\nncache:%d\nversion:%d\n",
+ priv->dblayer_cachesize, priv->dblayer_ncache, 3);
+ num_bytes = strlen(line);
+ return_value = slapi_write_buffer(prfd, line, num_bytes);
+ if (return_value != num_bytes)
+ {
+ goto error;
+ }
+ return_value = PR_Close(prfd);
+ if (PR_SUCCESS == return_value)
+ {
+ return 0;
+ } else
+ {
+ LDAPDebug( LDAP_DEBUG_ANY,"Fatal Error---Failed to write guardian file, database corruption possible\n", 0,0, 0 );
+ (void)PR_Delete(filename);
+ return -1;
+ }
+error:
+ (void)PR_Close(prfd);
+ (void)PR_Delete(filename);
+ return -1;
+}
+
+/* read the guardian file from db/ and possibly recover the database */
+static int read_metadata(struct ldbminfo *li)
+{
+ char filename[MAXPATHLEN];
+ char *buf;
+ char *thisline;
+ char *nextline;
+ char **dirp;
+ PRFileDesc *prfd;
+ PRFileInfo prfinfo;
+ int return_value = 0;
+ PRInt32 byte_count = 0;
+ char attribute[512];
+ char value[128], delimiter;
+ int number = 0;
+ dblayer_private *priv = (dblayer_private *)li->li_dblayer_private;
+
+ priv->dblayer_previous_cachesize = 0;
+ priv->dblayer_previous_ncache = 0;
+ /* Open the guard file and read stuff, then delete it */
+ sprintf(filename,"%s/guardian",priv->dblayer_home_directory);
+
+ memset(&prfinfo, '\0', sizeof(PRFileInfo));
+ (void)PR_GetFileInfo(filename, &prfinfo);
+
+ priv->dblayer_recovery_required = 0;
+ prfd = PR_Open(filename,PR_RDONLY,priv->dblayer_file_mode);
+ if (NULL == prfd || 0 == prfinfo.size) {
+ /* file empty or not present--means the database needs recovered */
+ int count = 0;
+ priv->dblayer_recovery_required = 0;
+ for (dirp = priv->dblayer_data_directories; dirp && *dirp; dirp++)
+ {
+ count_dbfiles_in_dir(*dirp, &count, 1 /* recurse */);
+ if (count > 0) {
+#if 0
+ char *home_dir;
+ /* This code used to check for a broken import by looking
+ * for a dbversion file. If it wasn't there, then an import
+ * failed. Now each instance has its own dbversion file.
+ * If this check is done at all, it ought to be done when
+ * bringing up individual backend instances.
+ */
+ /* While we're here, let's check for a broken import.
+ This would be indicated by the following conditions:
+ 1. db files in the directory.
+ 2. No guardian file.
+ 3. No DBVERSION file.
+ If we're here we have confitions 1 and 2,
+ so we should check for condition 3.
+ */
+ if (!dbversion_exists(li, home_dir)) {
+ LDAPDebug( LDAP_DEBUG_ANY,"Fatal Error---database is corrupt. Server can't start. Most likely cause is a previously aborted import. Either re-import or delete the database and re-start the server.\n", 0,0, 0 );
+ return -1;
+ } else {
+ priv->dblayer_recovery_required = 1;
+ }
+#endif
+ priv->dblayer_recovery_required = 1;
+ return 0;
+ }
+ }
+ return 0; /* no files found; no need to run recover start */
+ }
+ /* dblayer_recovery_required is initialized in dblayer_init;
+ * and might be set 1 in check_db_version;
+ * we don't want to override it
+ * priv->dblayer_recovery_required = 0; */
+ /* So, we opened the file, now let's read the cache size and version stuff
+ */
+ buf = slapi_ch_calloc(1, prfinfo.size + 1);
+ byte_count = slapi_read_buffer(prfd, buf, prfinfo.size);
+ if (byte_count < 0) {
+ /* something bad happened while reading */
+ priv->dblayer_recovery_required = 1;
+ } else {
+ buf[ byte_count ] = '\0';
+ thisline = buf;
+ while (1) {
+ /* Find the end of the line */
+ nextline = strchr( thisline, '\n' );
+ if (NULL != nextline) {
+ *nextline++ = '\0';
+ while ('\n' == *nextline) {
+ nextline++;
+ }
+ }
+ sscanf(thisline,"%[a-z]%c%s",attribute,&delimiter,value);
+ if (0 == strcmp("cachesize",attribute)) {
+ priv->dblayer_previous_cachesize = strtoul(value, NULL, 10);
+ } else if (0 == strcmp("ncache",attribute)) {
+ number = atoi(value);
+ priv->dblayer_previous_ncache = number;
+ } else if (0 == strcmp("version",attribute)) {
+ }
+ if (NULL == nextline || '\0' == *nextline) {
+ /* Nothing more to read */
+ break;
+ }
+ thisline = nextline;
+ }
+ }
+ slapi_ch_free((void **)&buf);
+ (void)PR_Close(prfd);
+ return_value = PR_Delete(filename); /* very important that this happen ! */
+ if (PR_SUCCESS != return_value) {
+ LDAPDebug(LDAP_DEBUG_ANY,
+ "Fatal Error---Failed to delete guardian file, "
+ "database corruption possible\n", 0, 0, 0 );
+ }
+ return return_value;
+}
+
+/* handy routine for checkpointing the db */
+static int dblayer_force_checkpoint(struct ldbminfo *li)
+{
+ int ret = 0, i;
+ dblayer_private *priv = (dblayer_private *)li->li_dblayer_private;
+ struct dblayer_private_env *pEnv;
+
+ if (NULL == priv){
+ /* already terminated. nothing to do */
+ return -1;
+ }
+
+ pEnv= priv->dblayer_env;
+
+
+ PR_ASSERT(pEnv != NULL);
+
+ if (priv->dblayer_enable_transactions) {
+
+ LDAPDebug(LDAP_DEBUG_TRACE, "Checkpointing database ...\n", 0, 0, 0);
+
+ /*
+ * DB workaround. Newly created environments do not know what the
+ * previous checkpoint LSN is. The default LSN of [0][0] would
+ * cause us to read all log files from very beginning during a
+ * later recovery. Taking two checkpoints solves the problem.
+ */
+
+ for (i = 0; i < 2; i++) {
+ ret = dblayer_txn_checkpoint(li, pEnv, PR_TRUE, PR_FALSE);
+ if (ret == 0) continue;
+#if 1000*DB_VERSION_MAJOR + 100*DB_VERSION_MINOR < 4100
+ if (ret != DB_INCOMPLETE)
+#endif
+ {
+ LDAPDebug(LDAP_DEBUG_ANY, "Checkpoint FAILED, error %s (%d)\n",
+ dblayer_strerror(ret), ret, 0);
+ break;
+ }
+#if 1000*DB_VERSION_MAJOR + 100*DB_VERSION_MINOR < 4100
+
+ LDAPDebug(LDAP_DEBUG_ANY, "Busy: retrying checkpoint\n", 0, 0, 0);
+
+ /* teletubbies: "again! again!" */
+ ret = dblayer_txn_checkpoint(li, pEnv, PR_TRUE, PR_FALSE);
+ if (ret == DB_INCOMPLETE) {
+ LDAPDebug(LDAP_DEBUG_ANY, "Busy: giving up on checkpoint\n", 0, 0, 0);
+ break;
+ } else if (ret != 0) {
+ LDAPDebug(LDAP_DEBUG_ANY, "Checkpoint FAILED, error %s (%d)\n",
+ dblayer_strerror(ret), ret, 0);
+ break;
+ }
+#endif
+ }
+ }
+
+ return ret;
+}
+
+
+static int _dblayer_delete_instance_dir(ldbm_instance *inst)
+{
+ PRDir *dirhandle = NULL;
+ PRDirEntry *direntry = NULL;
+ char filename[MAXPATHLEN];
+ struct ldbminfo *li = inst->inst_li;
+ dblayer_private *priv = NULL;
+ struct dblayer_private_env *pEnv = NULL;
+ char inst_dir[MAXPATHLEN];
+ char *inst_dirp = NULL;
+ int rval = 0;
+
+ if (NULL != li)
+ {
+ priv = (dblayer_private*)li->li_dblayer_private;
+ if (NULL != priv)
+ {
+ pEnv = priv->dblayer_env;
+ }
+ }
+
+ if (inst->inst_dir_name == NULL)
+ dblayer_get_instance_data_dir(inst->inst_be);
+
+ inst_dirp = dblayer_get_full_inst_dir(li, inst, inst_dir, MAXPATHLEN);
+ dirhandle = PR_OpenDir(inst_dirp);
+ if (! dirhandle) {
+ if ( PR_GetError() == PR_FILE_NOT_FOUND_ERROR ) {
+ /* the directory does not exist... that's not an error */
+ rval = 0;
+ goto done;
+ }
+ LDAPDebug(LDAP_DEBUG_ANY,
+ "_dblayer_delete_instance_dir: PR_OpenDir(%s) failed (%d): %s\n",
+ inst_dirp, PR_GetError(),slapd_pr_strerror(PR_GetError()));
+ rval = -1;
+ goto done;
+ }
+
+ /*
+ Note the use of PR_Delete here as opposed to using
+ sleepycat to "remove" the file. Reason: One should
+ not expect logging to be able to recover the wholesale
+ removal of a complete directory... a directory that includes
+ files outside the scope of sleepycat's logging. rwagner
+
+ ADDITIONAL COMMENT:
+ libdb41 is more strict on the transaction log control.
+ Even if checkpoint is forced before this delete function,
+ no log regarding the file deleted found in the log file,
+ following checkpoint repeatedly complains with these error messages:
+ libdb: <path>/mail.db4: cannot sync: No such file or directory
+ libdb: txn_checkpoint: failed to flush the buffer cache
+ No such file or directory
+ */
+
+ while (NULL != (direntry = PR_ReadDir(dirhandle, PR_SKIP_DOT |
+ PR_SKIP_DOT_DOT))) {
+ if (! direntry->name)
+ break;
+ sprintf(filename, "%s/%s", inst_dirp, direntry->name);
+ if (pEnv &&
+ strcmp(LDBM_FILENAME_SUFFIX , last_four_chars(direntry->name)) == 0)
+ {
+ rval = dblayer_db_remove_ex(pEnv, filename, 0, PR_TRUE);
+ }
+ else
+ {
+ rval = PR_Delete(filename);
+ }
+ }
+ PR_CloseDir(dirhandle);
+done:
+ /* remove the directory itself too */
+ if (0 == rval)
+ PR_RmDir(inst_dirp);
+ if (inst_dirp != inst_dir)
+ slapi_ch_free_string(&inst_dirp);
+ return rval;
+}
+
+/* delete the db3 files in a specific backend instance --
+ * this is probably only used for import.
+ * assumption: dblayer is open, but the instance has been closed.
+ */
+int dblayer_delete_instance_dir(backend *be)
+{
+ struct ldbminfo *li = (struct ldbminfo *)be->be_database->plg_private;
+ int ret = dblayer_force_checkpoint(li);
+
+ if (ret != 0) {
+ return ret;
+ } else {
+ ldbm_instance *inst = (ldbm_instance *)be->be_instance_info;
+ return _dblayer_delete_instance_dir(inst);
+ }
+}
+
+/* delete an entire db/ directory, including all instances under it!
+ * this is used mostly for restores.
+ * dblayer is assumed to be closed.
+ */
+int dblayer_delete_database(struct ldbminfo *li)
+{
+ dblayer_private *priv = NULL;
+ Object *inst_obj;
+ PRDir *dirhandle = NULL;
+ PRDirEntry *direntry = NULL;
+ char filename[MAXPATHLEN];
+ char *log_dir;
+ int ret;
+
+ PR_ASSERT(NULL != li);
+ priv = (dblayer_private *)li->li_dblayer_private;
+ PR_ASSERT(NULL != priv);
+
+ /* delete each instance */
+ for (inst_obj = objset_first_obj(li->li_instance_set); inst_obj;
+ inst_obj = objset_next_obj(li->li_instance_set, inst_obj)) {
+ ldbm_instance *inst = (ldbm_instance *)object_get_data(inst_obj);
+
+ if (inst->inst_be->be_instance_info != NULL) {
+ ret = _dblayer_delete_instance_dir(inst);
+ if (ret != 0)
+ {
+ LDAPDebug(LDAP_DEBUG_ANY,
+ "dblayer_delete_database: WARNING _dblayer_delete_instance_dir failed (%d)\n", ret, 0, 0);
+ return ret;
+ }
+ }
+ }
+
+ /* now smash everything else in the db/ dir */
+ dirhandle = PR_OpenDir(priv->dblayer_home_directory);
+ if (! dirhandle)
+ {
+ LDAPDebug(LDAP_DEBUG_ANY, "PR_OpenDir (%s) failed (%d): %s\n",
+ priv->dblayer_home_directory,
+ PR_GetError(),slapd_pr_strerror(PR_GetError()));
+ return -1;
+ }
+ while (NULL != (direntry = PR_ReadDir(dirhandle, PR_SKIP_DOT |
+ PR_SKIP_DOT_DOT))) {
+ if (! direntry->name)
+ break;
+ sprintf(filename, "%s/%s", priv->dblayer_home_directory,
+ direntry->name);
+ PR_Delete(filename);
+ }
+
+ PR_CloseDir(dirhandle);
+ /* remove transaction logs */
+ if ((NULL != priv->dblayer_log_directory) &&
+ (0 != strlen(priv->dblayer_log_directory) ))
+ {
+ log_dir = priv->dblayer_log_directory;
+ }
+ else
+ {
+ log_dir = dblayer_get_home_dir(li, NULL);
+ }
+ ret = dblayer_delete_transaction_logs(log_dir);
+ if(ret) {
+ LDAPDebug(LDAP_DEBUG_ANY,
+ "dblayer_delete_database: dblayer_delete_transaction_logs failed (%d)\n",
+ ret, 0, 0);
+ return -1;
+ }
+ return 0;
+}
+
+
+/*
+ * Return the size of the database (in kilobytes). XXXggood returning
+ * the size in units of kb is really a hack, and is done because we
+ * don't have NSPR support for 64-bit file offsets.
+ * Caveats:
+ * - We can still return incorrect results if an individual file is
+ * larger than fit in a PRUint32.
+ * - PR_GetFileInfo doesn't do any special processing for symlinks,
+ * nor does it inform us if the file is a symlink. Nice. So if
+ * a file in the db directory is a symlink, the size we return
+ * will probably be way too small.
+ */
+int dblayer_database_size(struct ldbminfo *li, unsigned int *size)
+{
+ dblayer_private *priv = NULL;
+ int return_value = 0;
+ char filename[MAXPATHLEN];
+ PRDir *dirhandle = NULL;
+ /*
+ * XXXggood - NSPR will only give us an unsigned 32-bit quantity for
+ * file sizes. This is bad. Files can be bigger than that these days.
+ */
+ unsigned int cumulative_size = 0;
+ unsigned int remainder = 0;
+ PRFileInfo info;
+
+ PR_ASSERT(NULL != li);
+ priv = (dblayer_private*)li->li_dblayer_private;
+ PR_ASSERT(NULL != priv);
+
+ dirhandle = PR_OpenDir(priv->dblayer_home_directory);
+ if (NULL != dirhandle)
+ {
+ PRDirEntry *direntry = NULL;
+ while (NULL != (direntry = PR_ReadDir(dirhandle, PR_SKIP_DOT | PR_SKIP_DOT_DOT))) {
+ if (NULL == direntry->name)
+ {
+ break;
+ }
+ sprintf(filename,"%s/%s",priv->dblayer_home_directory,direntry->name);
+ return_value = PR_GetFileInfo(filename, &info);
+ if (PR_SUCCESS == return_value)
+ {
+ cumulative_size += (info.size / 1024);
+ remainder += (info.size % 1024);
+ } else
+ {
+ cumulative_size = (PRUint32) 0;
+ return_value = -1;
+ break;
+ }
+ }
+ PR_CloseDir(dirhandle);
+ } else
+ {
+ return_value = -1;
+ }
+
+ *size = cumulative_size + (remainder / 1024);
+ return return_value;
+}
+
+static char* last_four_chars(const char* s)
+{
+ size_t l = strlen(s);
+ return ((char*)s + (l - 4));
+}
+
+static int count_dbfiles_in_dir(char *directory, int *count, int recurse)
+{
+ /* The new recurse argument was added to help with multiple backend
+ * instances. When recurse is true, this function will also look through
+ * the directories in the given directory for .db3 files. */
+ int return_value = 0;
+ PRDir *dirhandle = NULL;
+
+ if (!recurse) {
+ /* It is really the callers responsibility to set count to 0 before
+ * calling. However, if recurse isn't true, we can make sure it is
+ * set to 0. */
+ *count = 0;
+ }
+ dirhandle = PR_OpenDir(directory);
+ if (NULL != dirhandle) {
+ PRDirEntry *direntry = NULL;
+ char *direntry_name;
+ PRFileInfo info;
+
+ while (NULL != (direntry = PR_ReadDir(dirhandle, PR_SKIP_DOT | PR_SKIP_DOT_DOT))) {
+ if (NULL == direntry->name) {
+ break;
+ }
+ direntry_name = slapi_ch_malloc(strlen(directory) +
+ strlen(direntry->name) + 2);
+ sprintf(direntry_name, "%s/%s", directory, direntry->name);
+ if ((PR_GetFileInfo(direntry_name, &info) == PR_SUCCESS) &&
+ (PR_FILE_DIRECTORY == info.type) && recurse) {
+ /* Recurse into this directory but not any further. This is
+ * because each instance gets its own directory, but in those
+ * directories there should be only .db3 files. There should
+ * not be any more directories in an instance directory. */
+ count_dbfiles_in_dir(direntry_name, count, 0 /* don't recurse */);
+ }
+ slapi_ch_free((void**)&direntry_name);
+ if (strcmp( LDBM_FILENAME_SUFFIX , last_four_chars(direntry->name)) == 0) {
+ (*count)++;
+ }
+ }
+ PR_CloseDir(dirhandle);
+ } else {
+ return_value = -1;
+ }
+
+ return return_value;
+}
+
+/* And finally... Tubular Bells.
+ * Well, no, actually backup and restore...
+ */
+
+/* Backup works like this:
+ * the slapd executable is run like for ldif2ldbm and so on.
+ * this means that the front-end gets the back-end loaded, and then calls
+ * into the back-end backup entry point. This then gets us down to here.
+ *
+ * So, we need to copy the data files to the backup point.
+ * While we are doing that, we need to make sure that the logfile
+ * truncator in slapd doesn't delete our files. To do this we need
+ * some way to signal to it that it should cease its work, or we need
+ * to do something like start a long-lived transaction so that the
+ * log files look like they're needed.
+ *
+ * When we've copied the data files, we can then copy the log files
+ * too.
+ *
+ * Finally, we tell the log file truncator to go back about its business in peace
+ *
+ */
+
+int
+dblayer_copyfile(char *source, char *destination, int overwrite, int mode)
+{
+#if defined _WIN32
+ return (0 == CopyFile(source,destination,overwrite ? FALSE : TRUE));
+#else
+#ifdef DB_USE_64LFS
+#define OPEN_FUNCTION dblayer_open_large
+#else
+#define OPEN_FUNCTION open
+#endif
+ int source_fd = -1;
+ int dest_fd = -1;
+ char *buffer = NULL;
+ int return_value = -1;
+ int bytes_to_write = 0;
+
+ /* malloc the buffer */
+ buffer = slapi_ch_malloc(64*1024);
+ if (NULL == buffer)
+ {
+ goto error;
+ }
+ /* Open source file */
+ source_fd = OPEN_FUNCTION(source,O_RDONLY,0);
+ if (-1 == source_fd)
+ {
+ goto error;
+ }
+ /* Open destination file */
+ dest_fd = OPEN_FUNCTION(destination,O_CREAT | O_WRONLY, mode);
+ if (-1 == dest_fd)
+ {
+ goto error;
+ }
+ /* Loop round reading data and writing it */
+ while (1)
+ {
+ return_value = read(source_fd,buffer,64*1024);
+ if (return_value <= 0)
+ {
+ /* means error or EOF */
+ break;
+ }
+ bytes_to_write = return_value;
+ return_value = write(dest_fd,buffer,bytes_to_write);
+ if (return_value != bytes_to_write)
+ {
+ /* means error */
+ return_value = -1;
+ break;
+ }
+ }
+error:
+ if (source_fd != -1)
+ {
+ close(source_fd);
+ }
+ if (dest_fd != -1)
+ {
+ close(dest_fd);
+ }
+ slapi_ch_free((void**)&buffer);
+ return return_value;
+#endif
+}
+
+/*
+ * Copies all the .db# files in instance_dir to a directory with the same name
+ * in destination_dir. Both instance_dir and destination_dir are absolute
+ * paths.
+ * (#604921: added indexonly flag for the use in convindices
+ * -- backup/restore indices)
+ *
+ * If the argument restore is true,
+ * logging messages will be about "Restoring" files.
+ * If the argument restore is false,
+ * logging messages will be about "Backing up" files.
+ * The argument cnt is used to count the number of files that were copied.
+ *
+ * This function is used during db2bak and bak2db.
+ */
+int dblayer_copy_directory(struct ldbminfo *li,
+ Slapi_Task *task,
+ char *src_dir,
+ char *dest_dir,
+ int restore,
+ int *cnt,
+ int instance_dir_flag,
+ int indexonly)
+{
+ dblayer_private *priv = NULL;
+ char *new_src_dir = NULL;
+ char *new_dest_dir = NULL;
+ PRDir *dirhandle = NULL;
+ PRDirEntry *direntry = NULL;
+ size_t filename_length = 0;
+ size_t offset = 0;
+ char *compare_piece = NULL;
+ char *filename1;
+ char *filename2;
+ int return_value = -1;
+ char *relative_instance_name = NULL;
+ char *inst_dirp = NULL;
+ char inst_dir[MAXPATHLEN];
+ char sep;
+ ldbm_instance *inst;
+
+ if (!src_dir || '\0' == *src_dir || !dest_dir || '\0' == *dest_dir)
+ return return_value;
+
+ priv = (dblayer_private *) li->li_dblayer_private;
+
+ /* get the backend instance name */
+ sep = get_sep(src_dir);
+ if ((relative_instance_name = strrchr(src_dir, sep)) == NULL)
+ relative_instance_name = src_dir;
+ else
+ relative_instance_name++;
+
+ inst = ldbm_instance_find_by_name(li, relative_instance_name);
+ if (NULL == inst)
+ {
+ LDAPDebug(LDAP_DEBUG_ANY, "Backend instance \"%s\" does not exist; "
+ "Instance path %s could be invalid.\n",
+ relative_instance_name, src_dir, 0);
+ return return_value;
+ }
+
+ inst_dirp = dblayer_get_full_inst_dir(inst->inst_li, inst,
+ inst_dir, MAXPATHLEN);
+ if (is_fullpath(src_dir))
+ new_src_dir = src_dir;
+ else
+ {
+ int len = strlen(inst_dirp);
+ sep = get_sep(inst_dirp);
+ if (*(inst_dirp+len-1) == sep)
+ sep = '\0';
+ new_src_dir = (char *)slapi_ch_malloc(strlen(src_dir) + len + 2);
+ sprintf(new_src_dir, "%s%c%s", inst_dirp, sep, src_dir);
+ }
+
+ dirhandle = PR_OpenDir(new_src_dir);
+ if (NULL == dirhandle)
+ return return_value;
+
+ while (NULL != (direntry =
+ PR_ReadDir(dirhandle, PR_SKIP_DOT | PR_SKIP_DOT_DOT)))
+ {
+ if (NULL == direntry->name) {
+ /* NSPR doesn't behave like the docs say it should */
+ break;
+ }
+ if (indexonly &&
+ 0 == strcmp(direntry->name, ID2ENTRY LDBM_FILENAME_SUFFIX))
+ {
+ continue;
+ }
+
+ /* Look at the last three characters in the filename */
+ filename_length = strlen(direntry->name);
+ if (filename_length > 4) {
+ offset = filename_length - 4;
+ } else {
+ offset = 0;
+ }
+ compare_piece = (char *)direntry->name + offset;
+
+ if (0 == strcmp(compare_piece, LDBM_FILENAME_SUFFIX) || /* .db4 */
+ 0 == strcmp(compare_piece, LDBM_SUFFIX_OLD) || /* support .db3 */
+ 0 == strcmp(direntry->name, DBVERSION_FILENAME)) {
+ /* Found a database file. Copy it. */
+
+ if (NULL == new_dest_dir) {
+ /* Need to create the new directory where the files will be
+ * copied to. */
+ PRFileInfo info;
+ char *prefix = "";
+ char mysep = 0;
+
+ if (!is_fullpath(dest_dir))
+ {
+ prefix = dblayer_get_home_dir(li, NULL);
+ mysep = get_sep(prefix);
+ }
+
+ new_dest_dir = slapi_ch_malloc(strlen(dest_dir) +
+ strlen(relative_instance_name) +
+ strlen(prefix) + 3);
+ if (mysep)
+ sprintf(new_dest_dir, "%s%c%s%c%s",
+ prefix, mysep, dest_dir, mysep, relative_instance_name);
+ else
+ sprintf(new_dest_dir, "%s/%s",
+ dest_dir, relative_instance_name);
+ /* } */
+ if (PR_SUCCESS == PR_GetFileInfo(new_dest_dir, &info))
+ {
+ ldbm_delete_dirs(new_dest_dir);
+ }
+ if (mkdir_p(new_dest_dir, 0700) != PR_SUCCESS)
+ {
+ LDAPDebug(LDAP_DEBUG_ANY, "Can't create new directory %s, "
+ SLAPI_COMPONENT_NAME_NSPR " error %d (%s)\n",
+ new_dest_dir, PR_GetError(),
+ slapd_pr_strerror(PR_GetError()));
+ goto out;
+ }
+ }
+
+ filename1 = slapi_ch_malloc(strlen(new_src_dir) +
+ strlen(direntry->name) + 2);
+ sprintf(filename1, "%s/%s", new_src_dir, direntry->name);
+ filename2 = slapi_ch_malloc(strlen(new_dest_dir) +
+ strlen(direntry->name) + 2);
+ sprintf(filename2, "%s/%s", new_dest_dir, direntry->name);
+
+ if (restore) {
+ LDAPDebug(LDAP_DEBUG_ANY, "Restoring file %d (%s)\n",
+ *cnt, filename2, 0);
+ if (task) {
+ slapi_task_log_notice(task,
+ "Restoring file %d (%s)", *cnt, filename2);
+ slapi_task_log_status(task,
+ "Restoring file %d (%s)", *cnt, filename2);
+ }
+ } else {
+ LDAPDebug(LDAP_DEBUG_ANY, "Backing up file %d (%s)\n",
+ *cnt, filename2, 0);
+ if (task) {
+ slapi_task_log_notice(task,
+ "Backing up file %d (%s)", *cnt, filename2);
+ slapi_task_log_status(task,
+ "Backing up file %d (%s)", *cnt, filename2);
+ }
+ }
+
+ /* copy filename1 to filename2 */
+ return_value = dblayer_copyfile(filename1, filename2,
+ 0, priv->dblayer_file_mode);
+ slapi_ch_free((void**)&filename1);
+ slapi_ch_free((void**)&filename2);
+ if (0 > return_value)
+ break;
+
+ (*cnt)++;
+ }
+ }
+out:
+ PR_CloseDir(dirhandle);
+ slapi_ch_free((void**)&new_dest_dir);
+ if (new_src_dir != src_dir)
+ slapi_ch_free((void**)&new_src_dir);
+ return return_value;
+}
+
+
+
+/* Destination Directory is an absolute pathname */
+int dblayer_backup(struct ldbminfo *li, char *dest_dir, Slapi_Task *task)
+{
+ dblayer_private *priv = NULL;
+ char **listA = NULL, **listB = NULL, **listi, **listj, *prefix;
+ char *home_dir = NULL;
+ int return_value = 0;
+ char *pathname1;
+ char *pathname2;
+ back_txn txn;
+ int cnt = 1, ok = 0;
+ ldbm_instance *inst;
+ Object *inst_obj;
+
+ PR_ASSERT(NULL != li);
+ priv = (dblayer_private*)li->li_dblayer_private;
+ PR_ASSERT(NULL != priv);
+ home_dir = dblayer_get_home_dir(li, NULL);
+ if (NULL == home_dir)
+ {
+ LDAPDebug(LDAP_DEBUG_ANY,
+ "Backup failed due to missing db home directory info\n", 0, 0, 0);
+ return -1;
+ }
+
+ /*
+ * What are we doing here ?
+ * We want to copy into the backup directory:
+ * All the backend instance dir / database files;
+ * All the logfiles
+ * The version file
+ */
+
+ /* changed in may 1999 for political correctness.
+ * 1. take checkpoint
+ * 2. open transaction
+ * 3. get list of logfiles (A)
+ * 4. copy the db# files
+ * 5. get list of logfiles (B)
+ * 6. if !(A in B), goto 3
+ * (logfiles were flushed during our backup)
+ * 7. copy logfiles from list B
+ * 8. abort transaction
+ * 9. backup index config info
+ */
+
+ /* Order of checkpointing and txn creation reversed to work
+ * around DB problem. If we don't do it this way around DB
+ * thinks all old transaction logs are required for recovery
+ * when the DB environment has been newly created (such as
+ * after an import).
+ */
+
+ /* do a quick checkpoint */
+ dblayer_force_checkpoint(li);
+ dblayer_txn_init(li,&txn);
+ return_value=dblayer_txn_begin(li,NULL,&txn);
+ if (0 != return_value) {
+ LDAPDebug(LDAP_DEBUG_ANY,
+ "Backup failed due to transaction failure\n", 0, 0, 0);
+ return -1;
+ }
+
+ if ( g_get_shutdown() || c_get_shutdown() ) {
+ dblayer_txn_abort(li,&txn);
+ return -1;
+ }
+
+ /* repeat this until the logfile sets match... */
+ do {
+ /* get the list of logfiles currently existing */
+ if (priv->dblayer_enable_transactions) {
+ return_value = LOG_ARCHIVE(priv->dblayer_env->dblayer_DB_ENV,
+ &listA, DB_ARCH_LOG, malloc);
+ if ((return_value != 0) || (listA == NULL)) {
+ LDAPDebug(LDAP_DEBUG_ANY, "BAD: can't get list of logs\n",
+ 0, 0, 0);
+ dblayer_txn_abort(li,&txn);
+ return return_value;
+ }
+ } else {
+ ok=1;
+ }
+ if ( g_get_shutdown() || c_get_shutdown() ) {
+ dblayer_txn_abort(li,&txn);
+ return -1;
+ }
+
+ for (inst_obj = objset_first_obj(li->li_instance_set); inst_obj;
+ inst_obj = objset_next_obj(li->li_instance_set, inst_obj))
+ {
+ char inst_dir[MAXPATHLEN];
+ char *inst_dirp = NULL;
+ inst = (ldbm_instance *)object_get_data(inst_obj);
+ inst_dirp = dblayer_get_full_inst_dir(inst->inst_li, inst,
+ inst_dir, MAXPATHLEN);
+ return_value = dblayer_copy_directory(li, task, inst_dirp,
+ dest_dir, 0 /* backup */, &cnt, 0, 0);
+ if (return_value != 0) {
+ LDAPDebug(LDAP_DEBUG_ANY,
+ "ERROR: error copying directory (%s -> %s): err=%d\n",
+ inst_dirp, dest_dir, return_value);
+ if (task) {
+ slapi_task_log_notice(task,
+ "ERROR: error copying directory (%s -> %s): err=%d",
+ inst_dirp, dest_dir, return_value);
+ }
+ if (listA) {
+ free(listA);
+ }
+ dblayer_txn_abort(li,&txn);
+ if (inst_dirp != inst_dir)
+ slapi_ch_free_string(&inst_dirp);
+ return return_value;
+ }
+ if (inst_dirp != inst_dir)
+ slapi_ch_free_string(&inst_dirp);
+ }
+ if (priv->dblayer_enable_transactions) {
+ /* now, get the list of logfiles that still exist */
+ return_value = LOG_ARCHIVE(priv->dblayer_env->dblayer_DB_ENV,
+ &listB, DB_ARCH_LOG, malloc);
+ if ((return_value != 0) || (listB == NULL)) {
+ LDAPDebug(LDAP_DEBUG_ANY, "ERROR: can't get list of logs\n",
+ 0, 0, 0);
+ free(listA);
+ dblayer_txn_abort(li,&txn);
+ return return_value;
+ }
+
+ /* compare: make sure everything in list A is still in list B */
+ ok = 1;
+ for (listi = listA; *listi && ok; listi++) {
+ int found = 0;
+ for (listj = listB; *listj && !found; listj++) {
+ if (strcmp(*listi, *listj) == 0)
+ found = 1;
+ }
+ if (! found) {
+ ok = 0; /* missing log: start over */
+ LDAPDebug(LDAP_DEBUG_ANY, "WARNING: Log %s has been swiped "
+ "out from under me! (retrying)\n", *listi, 0, 0);
+ if (task) {
+ slapi_task_log_notice(task,
+ "WARNING: Log %s has been swiped out from under me! "
+ "(retrying)", *listi);
+ }
+ }
+ }
+
+ if ( g_get_shutdown() || c_get_shutdown() ) {
+ dblayer_txn_abort(li,&txn);
+ return -1;
+ }
+
+ if (ok) {
+ char **listptr;
+
+ prefix = NULL;
+ if ((NULL != priv->dblayer_log_directory) &&
+ (0 != strlen(priv->dblayer_log_directory))) {
+ prefix = priv->dblayer_log_directory;
+ } else {
+ prefix = home_dir;
+ }
+ /* log files have the same filename len(100 is a safety net:) */
+ pathname1 = (char *)slapi_ch_malloc(strlen(prefix) +
+ strlen(*listB) + 100);
+ pathname2 = (char *)slapi_ch_malloc(strlen(dest_dir) +
+ strlen(*listB) + 100);
+ /* We copy those over */
+ for (listptr = listB; (*listptr) && ok; ++listptr) {
+ sprintf(pathname1, "%s/%s", prefix, *listptr);
+ sprintf(pathname2, "%s/%s", dest_dir, *listptr);
+ LDAPDebug(LDAP_DEBUG_ANY, "Backing up file %d (%s)\n",
+ cnt, pathname2, 0);
+ if (task)
+ {
+ slapi_task_log_notice(task,
+ "Backing up file %d (%s)", cnt, pathname2);
+ slapi_task_log_status(task,
+ "Backing up file %d (%s)", cnt, pathname2);
+ }
+ return_value = dblayer_copyfile(pathname1, pathname2,
+ 0, priv->dblayer_file_mode);
+ if (0 > return_value) {
+ LDAPDebug(LDAP_DEBUG_ANY, "Error copying file '%s' "
+ "(err=%d) -- Starting over...\n",
+ pathname1, return_value, 0);
+ if (task) {
+ slapi_task_log_notice(task,
+ "Error copying file '%s' (err=%d) -- Starting "
+ "over...", pathname1, return_value);
+ }
+ ok = 0;
+ }
+ if ( g_get_shutdown() || c_get_shutdown() ) {
+ dblayer_txn_abort(li,&txn);
+ return -1;
+ }
+ cnt++;
+ }
+ slapi_ch_free((void **)&pathname1);
+ slapi_ch_free((void **)&pathname2);
+ }
+
+ if (listA) {
+ free(listA);
+ listA = NULL;
+ }
+ if (listB) {
+ free(listB);
+ listB = NULL;
+ }
+ }
+ } while (!ok);
+
+
+ if ( g_get_shutdown() || c_get_shutdown() ) {
+ dblayer_txn_abort(li,&txn);
+ return -1;
+ }
+
+ /* now copy the version file */
+ pathname1 = (char *)slapi_ch_malloc(strlen(home_dir) +
+ strlen(DBVERSION_FILENAME) + 2);
+ pathname2 = (char *)slapi_ch_malloc(strlen(dest_dir) +
+ strlen(DBVERSION_FILENAME) + 2);
+ sprintf(pathname1, "%s/%s", home_dir, DBVERSION_FILENAME);
+ sprintf(pathname2, "%s/%s", dest_dir, DBVERSION_FILENAME);
+ LDAPDebug(LDAP_DEBUG_ANY, "Backing up file %d (%s)\n",
+ cnt, pathname2, 0);
+ if (task) {
+ slapi_task_log_notice(task, "Backing up file %d (%s)", cnt, pathname2);
+ slapi_task_log_status(task, "Backing up file %d (%s)", cnt, pathname2);
+ }
+ return_value = dblayer_copyfile(pathname1,pathname2,0,priv->dblayer_file_mode);
+ slapi_ch_free((void **)&pathname1);
+ slapi_ch_free((void **)&pathname2);
+
+ /* Lastly we tell log file truncation to start again */
+
+ if (0 == return_value) /* if everything went well, backup the index conf */
+ return_value = dse_conf_backup(li, dest_dir);
+
+ return_value = dblayer_txn_abort(li,&txn);
+ return return_value;
+}
+
+
+/*
+ * Restore is pretty easy.
+ * We delete the current database.
+ * We then copy all the files over from the backup point.
+ * We then leave them there for the slapd process to pick up and do the recovery
+ * (which it will do as it sees no guard file).
+ */
+
+/* Helper function first */
+
+static int dblayer_is_logfilename(const char* path)
+{
+ int ret = 0;
+ /* Is the filename at least 4 characters long ? */
+ if (strlen(path) < 4)
+ {
+ return 0; /* Not a log file then */
+ }
+ /* Are the first 4 characters "log." ? */
+ ret = strncmp(path,"log.",4);
+ if (0 == ret)
+ {
+ /* Now, are the last 4 characters _not_ .db# ? */
+ const char *piece = path + (strlen(path) - 4);
+ ret = strcmp(piece,LDBM_FILENAME_SUFFIX);
+ if (0 != ret)
+ {
+ /* Is */
+ return 1;
+ }
+ }
+ return 0; /* Is not */
+}
+
+/* remove log.xxx from log directory*/
+static
+int dblayer_delete_transaction_logs(const char * log_dir)
+{
+ int rc=0;
+ char filename1[MAXPATHLEN];
+ PRDir *dirhandle = NULL;
+ dirhandle = PR_OpenDir(log_dir);
+ if (NULL != dirhandle) {
+ PRDirEntry *direntry = NULL;
+ int is_a_logfile = 0;
+ int pre=0;
+ PRFileInfo info ;
+
+ while (NULL != (direntry =
+ PR_ReadDir(dirhandle, PR_SKIP_DOT | PR_SKIP_DOT_DOT)))
+ {
+ if (NULL == direntry->name) {
+ /* NSPR doesn't behave like the docs say it should */
+ LDAPDebug(LDAP_DEBUG_ANY, "PR_ReadDir failed (%d): %s\n",
+ PR_GetError(),slapd_pr_strerror(PR_GetError()), 0);
+ break;
+ }
+ sprintf(filename1, "%s/%s", log_dir, direntry->name);
+ pre = PR_GetFileInfo(filename1, &info);
+ if (pre == PR_SUCCESS && PR_FILE_DIRECTORY == info.type) {
+ continue;
+ }
+ is_a_logfile = dblayer_is_logfilename(direntry->name);
+ if (is_a_logfile && (NULL != log_dir) && (0 != strlen(log_dir)) )
+ {
+ LDAPDebug(LDAP_DEBUG_ANY, "Deleting log file: (%s)\n",
+ filename1, 0, 0);
+ unlink(filename1);
+ }
+ }
+ PR_CloseDir(dirhandle);
+ }
+ else if (PR_FILE_NOT_FOUND_ERROR != PR_GetError())
+ {
+ LDAPDebug(LDAP_DEBUG_ANY,
+ "dblayer_delete_transaction_logs: PR_OpenDir(%s) failed (%d): %s\n",
+ log_dir, PR_GetError(),slapd_pr_strerror(PR_GetError()));
+ rc=1;
+ }
+ return rc;
+}
+
+const char *skip_list[] =
+{
+ ".ldif",
+ NULL
+};
+
+static int doskip(const char *filename)
+{
+ const char **p;
+ int len = strlen(filename);
+
+ for (p = skip_list; p && *p; p++)
+ {
+ int n = strlen(*p);
+ if (0 == strncmp(filename + len - n, *p, n))
+ return 1;
+ }
+ return 0;
+}
+
+/* Destination Directory is an absolute pathname */
+
+int dblayer_restore(struct ldbminfo *li, char *src_dir, Slapi_Task *task)
+{
+ dblayer_private *priv = NULL;
+ int return_value = 0;
+ int tmp_rval;
+ char filename1[MAXPATHLEN];
+ char filename2[MAXPATHLEN];
+ PRDir *dirhandle = NULL;
+ PRDirEntry *direntry = NULL;
+ PRFileInfo info;
+ ldbm_instance *inst;
+ int seen_logfiles = 0; /* Tells us if we restored any logfiles */
+ int is_a_logfile = 0;
+ int dbmode;
+ int action = 0;
+ char *home_dir = NULL;
+
+ PR_ASSERT(NULL != li);
+ priv = (dblayer_private*)li->li_dblayer_private;
+ PR_ASSERT(NULL != priv);
+
+ /* DBDB this is a hack, take out later */
+ PR_Lock(li->li_config_mutex);
+ priv->dblayer_home_directory = li->li_directory;
+ priv->dblayer_cachesize = li->li_dbcachesize;
+ priv->dblayer_ncache = li->li_dbncache;
+ priv->dblayer_file_mode = li->li_mode;
+ PR_Unlock(li->li_config_mutex);
+
+ home_dir = dblayer_get_home_dir(li, NULL);
+ if (NULL == home_dir)
+ {
+ LDAPDebug(LDAP_DEBUG_ANY,
+ "Restore failed due to missing db home directory info\n", 0, 0, 0);
+ return -1;
+ }
+
+ /* We find out if slapd is running */
+ /* If it is, we fail */
+ /* We check on the source staging area, no point in going further if it
+ * isn't there */
+ if (!dbversion_exists(li, src_dir)) {
+ LDAPDebug(LDAP_DEBUG_ANY, "restore: source directory %s does not "
+ "contain a complete backup\n", src_dir, 0, 0);
+
+
+ if (task) {
+ slapi_task_log_notice(task, "Source directory %s does not "
+ "contain a complete backup", src_dir );
+ }
+ }
+
+ /*
+ * Check if the target is a superset of the backup.
+ * If not don't restore any db at all, otherwise
+ * the target will be crippled.
+ */
+ dirhandle = PR_OpenDir(src_dir);
+ if (NULL != dirhandle)
+ {
+ while ((direntry = PR_ReadDir(dirhandle, PR_SKIP_DOT | PR_SKIP_DOT_DOT))
+ && direntry->name)
+ {
+ sprintf(filename1, "%s/%s", src_dir, direntry->name);
+ tmp_rval = PR_GetFileInfo(filename1, &info);
+ if (tmp_rval == PR_SUCCESS && PR_FILE_DIRECTORY == info.type) {
+ inst = ldbm_instance_find_by_name(li, (char *)direntry->name);
+ if ( inst == NULL)
+ {
+ LDAPDebug(LDAP_DEBUG_ANY,
+ "ERROR: target server has no %s configured\n", direntry->name, 0, 0);
+ if (task) {
+ slapi_task_log_notice(task,
+ "ERROR: target server has no %s configured\n", direntry->name);
+ }
+ PR_CloseDir(dirhandle);
+ return LDAP_UNWILLING_TO_PERFORM;
+ }
+ }
+ }
+ PR_CloseDir(dirhandle);
+ }
+
+ /* We delete the existing database */
+ return_value = dblayer_delete_database(li);
+ if (return_value) {
+ return return_value;
+ }
+
+ /* We copy the files over from the staging area */
+ /* We want to treat the logfiles specially: if there's
+ * a log file directory configured, copy the logfiles there
+ * rather than to the db dirctory */
+ if (0 == return_value) {
+ dirhandle = PR_OpenDir(src_dir);
+ if (NULL != dirhandle) {
+ char *restore_dir;
+ char *prefix = NULL;
+ int cnt = 1;
+
+
+ while (NULL != (direntry = PR_ReadDir(dirhandle, PR_SKIP_DOT | PR_SKIP_DOT_DOT))) {
+ if (NULL == direntry->name) {
+ /* NSPR doesn't behave like the docs say it should */
+ break;
+ }
+
+ /* Is this entry a directory? */
+ sprintf(filename1, "%s/%s", src_dir, direntry->name);
+ tmp_rval = PR_GetFileInfo(filename1, &info);
+ if (tmp_rval == PR_SUCCESS && PR_FILE_DIRECTORY == info.type) {
+ /* This is an instance directory. It contains the *.db#
+ * files for the backend instance.
+ * restore directory is supposed to be where the backend
+ * directory is located.
+ */
+
+ inst = ldbm_instance_find_by_name(li, (char *)direntry->name);
+ if (inst == NULL)
+ continue;
+
+ restore_dir = inst->inst_parent_dir_name;
+
+ if (dblayer_copy_directory(li, task, filename1,
+ restore_dir, 1 /* restore */, &cnt, 0, 0) == 0)
+ continue;
+ else
+ {
+ LDAPDebug(LDAP_DEBUG_ANY,
+ "restore: failed to copy directory %s\n",
+ filename1, 0, 0);
+ if (task) {
+ slapi_task_log_notice(task,
+ "Failed to copy directory %s", filename1);
+ }
+ break;
+ }
+ }
+
+ if (doskip(direntry->name))
+ continue;
+
+ /* Is this a log file ? */
+ /* Log files have names of the form "log.xxxxx" */
+ /* We detect these by looking for the prefix "log." and
+ * the lack of the ".db#" suffix */
+ is_a_logfile = dblayer_is_logfilename(direntry->name);
+ if (is_a_logfile) {
+ seen_logfiles = 1;
+ }
+ if (is_a_logfile && (NULL != priv->dblayer_log_directory) &&
+ (0 != strlen(priv->dblayer_log_directory)) ) {
+ prefix = priv->dblayer_log_directory;
+ } else {
+ prefix = home_dir;
+ }
+ mkdir_p(prefix, 0700);
+ sprintf(filename1, "%s/%s", src_dir, direntry->name);
+ sprintf(filename2, "%s/%s", prefix, direntry->name);
+ LDAPDebug(LDAP_DEBUG_ANY, "Restoring file %d (%s)\n",
+ cnt, filename2, 0);
+ if (task) {
+ slapi_task_log_notice(task, "Restoring file %d (%s)",
+ cnt, filename2);
+ slapi_task_log_status(task, "Restoring file %d (%s)",
+ cnt, filename2);
+ }
+ return_value = dblayer_copyfile(filename1, filename2, 0,
+ priv->dblayer_file_mode);
+ if (0 > return_value)
+ break;
+
+ cnt++;
+ }
+ PR_CloseDir(dirhandle);
+ }
+ }
+ /* We're done ! */
+
+#if defined(UPGRADEDB)
+ /* [605024] check the DBVERSION and reset idl-switch if needed */
+ if (dbversion_exists(li, home_dir))
+ {
+ char ldbmversion[LDBM_VERSION_MAXBUF];
+ char dataversion[LDBM_VERSION_MAXBUF];
+
+ if (dbversion_read(li, home_dir, ldbmversion, dataversion) != 0)
+ {
+ LDAPDebug(LDAP_DEBUG_ANY, "Warning: Unable to read dbversion "
+ "file in %s\n", home_dir, 0, 0);
+ }
+ else
+ {
+ adjust_idl_switch(ldbmversion, li);
+ }
+ }
+#endif
+
+ return_value = check_db_version(li, &action);
+ if (action & DBVERSION_UPGRADE_3_4)
+ {
+ dbmode = DBLAYER_CLEAN_RECOVER_MODE;/* upgrade: remove logs & recover */
+ }
+ else if (seen_logfiles)
+ {
+ dbmode = DBLAYER_RESTORE_MODE;
+ }
+ else
+ {
+ dbmode = DBLAYER_RESTORE_NO_RECOVERY_MODE;
+ }
+
+ /* now start the database code up, to prevent recovery next time the
+ * server starts;
+ * dse_conf_verify may need to have db started, as well. */
+ /* If no logfiles were stored, then fatal recovery isn't required */
+
+ if (li->li_flags & TASK_RUNNING_FROM_COMMANDLINE)
+ {
+ dbmode |= DBLAYER_CMDLINE_MODE;
+ }
+ else /* on-line mode */
+ {
+ allinstance_set_not_busy(li);
+ }
+
+ tmp_rval = dblayer_start(li, dbmode);
+ if (0 != tmp_rval) {
+ LDAPDebug(LDAP_DEBUG_ANY,
+ "dblayer_restore: Failed to init database\n", 0, 0, 0);
+ if (task) {
+ slapi_task_log_notice(task, "Failed to init database");
+ }
+ return tmp_rval;
+ }
+
+ if (0 == return_value) { /* only when the copyfile succeeded */
+ /* check the DSE_* files, if any */
+ tmp_rval = dse_conf_verify(li, src_dir);
+ if (0 != tmp_rval)
+ LDAPDebug(LDAP_DEBUG_ANY,
+ "Warning: Unable to verify the index configuration\n", 0, 0, 0);
+ }
+
+ if (li->li_flags & TASK_RUNNING_FROM_COMMANDLINE) {
+ /* command line: close the database down again */
+ tmp_rval = dblayer_close(li, dbmode);
+ if (0 != tmp_rval) {
+ LDAPDebug(LDAP_DEBUG_ANY,
+ "dblayer_restore: Failed to close database\n", 0, 0, 0);
+ }
+ } else {
+ allinstance_set_busy(li); /* on-line mode */
+ }
+
+ return_value = tmp_rval?tmp_rval:return_value;
+
+ return return_value;
+}
+
+
+static char *dblayer_make_friendly_instance_name(ldbm_instance *inst)
+{
+ char *name = slapi_ch_strdup(inst->inst_name);
+ int x;
+
+ if (name == NULL)
+ return NULL;
+ for (x = 0; name[x]; x++)
+ if (name[x] == ' ')
+ name[x] = '_';
+ return name;
+}
+
+/*
+ * inst_dir_name is a relative path (from 6.21)
+ * ==> txn log stores relative paths and becomes relocatable
+ * if full path is given, parent dir is inst_parent_dir_name;
+ * otherwise, inst_dir in home_dir
+ *
+ * Set an appropriate path to inst_dir_name, if not yet.
+ * Create the specified directory, if not exists.
+ */
+int dblayer_get_instance_data_dir(backend *be)
+{
+ struct ldbminfo *li = (struct ldbminfo *)be->be_database->plg_private;
+ ldbm_instance *inst = (ldbm_instance *)be->be_instance_info;
+ char *full_namep = NULL;
+ char full_name[MAXPATHLEN];
+ PRDir *db_dir = NULL;
+ int ret = -1;
+
+ /* if a specific directory name was specified for this particular
+ * instance use it othewise use the ldbm-wide one
+ */
+ full_namep = dblayer_get_full_inst_dir(inst->inst_li, inst,
+ full_name, MAXPATHLEN);
+ /* Does this directory already exist? */
+ if ((db_dir = PR_OpenDir(full_namep)) != NULL) {
+ /* yep. */
+ PR_CloseDir(db_dir);
+ ret = 0;
+ } else {
+ /* nope -- create it. */
+ ret = mkdir_p(full_namep, 0700);
+ }
+
+ if (full_name != full_namep)
+ slapi_ch_free_string(&full_namep);
+
+ return ret;
+}
+
+char *
+dblayer_strerror(int error)
+{
+ return db_strerror(error);
+}
+
+/* [605974] check a db region file's existence to know whether import is executed by other process or not */
+#define DB_REGION_PREFIX "__db."
+
+int
+dblayer_in_import(ldbm_instance *inst)
+{
+ PRDir *dirhandle = NULL;
+ PRDirEntry *direntry = NULL;
+ char inst_dir[MAXPATHLEN];
+ char *inst_dirp = NULL;
+ int rval = 0;
+
+ inst_dirp = dblayer_get_full_inst_dir(inst->inst_li, inst,
+ inst_dir, MAXPATHLEN);
+ dirhandle = PR_OpenDir(inst_dirp);
+
+ if (NULL == dirhandle)
+ goto done;
+
+ while (NULL != (direntry = PR_ReadDir(dirhandle, PR_SKIP_DOT | PR_SKIP_DOT_DOT)))
+ {
+ if (NULL == direntry->name)
+ {
+ break;
+ }
+ if (0 ==strncmp(direntry->name, DB_REGION_PREFIX, 5))
+ {
+ rval = 1;
+ break;
+ }
+ }
+ PR_CloseDir(dirhandle);
+done:
+ if (inst_dirp != inst_dir)
+ slapi_ch_free_string(&inst_dirp);
+ return rval;
+}
+
+/*
+ * to change the db extention (e.g., .db3 -> .db4)
+ */
+int dblayer_update_db_ext(ldbm_instance *inst, char *oldext, char *newext)
+{
+ struct attrinfo *a = NULL;
+ backend *be = NULL;
+ struct ldbminfo *li = NULL;
+ dblayer_private *priv = NULL;
+ DB *thisdb = NULL;
+ int rval = 0;
+ char *ofile = NULL;
+ char *nfile = NULL;
+ char inst_dir[MAXPATHLEN];
+ char *inst_dirp;
+
+ if (NULL == inst)
+ {
+ LDAPDebug(LDAP_DEBUG_ANY,
+ "update_db_ext: Null instance is passed\n", 0, 0, 0);
+ return -1; /* non zero */
+ }
+ be = inst->inst_be;
+ li = inst->inst_li;
+ priv = (dblayer_private*)li->li_dblayer_private;
+ inst_dirp = dblayer_get_full_inst_dir(li, inst, inst_dir, MAXPATHLEN);
+ for (a = (struct attrinfo *)avl_getfirst(inst->inst_attrs);
+ NULL != a;
+ a = (struct attrinfo *)avl_getnext())
+ {
+ PRFileInfo info;
+ ofile = slapi_ch_malloc(strlen(inst_dirp) +
+ strlen(a->ai_type) + strlen(oldext) + 2);
+ sprintf(ofile, "%s/%s%s", inst_dirp, a->ai_type, oldext);
+
+ if (PR_GetFileInfo(ofile, &info) != PR_SUCCESS)
+ {
+ slapi_ch_free_string(&ofile);
+ continue;
+ }
+
+ /* db->rename disable DB in it; we need to create for each */
+ rval = db_create(&thisdb, priv->dblayer_env->dblayer_DB_ENV, 0);
+ if (0 != rval)
+ {
+ LDAPDebug(LDAP_DEBUG_ANY, "db_create returned %d (%s)\n",
+ rval, dblayer_strerror(rval), 0);
+ goto done;
+ }
+ nfile = slapi_ch_malloc(strlen(inst_dirp) +
+ strlen(a->ai_type) + strlen(newext) + 2);
+ sprintf(nfile, "%s/%s%s", inst_dirp, a->ai_type, newext);
+ LDAPDebug(LDAP_DEBUG_TRACE, "update_db_ext: rename %s -> %s\n",
+ ofile, nfile, 0);
+
+ rval = thisdb->rename(thisdb, (const char *)ofile, NULL /* subdb */,
+ (const char *)nfile, 0);
+ if (0 != rval)
+ {
+ LDAPDebug(LDAP_DEBUG_ANY, "rename returned %d (%s)\n",
+ rval, dblayer_strerror(rval), 0);
+ LDAPDebug(LDAP_DEBUG_ANY,
+ "update_db_ext: index (%s) Failed to update index %s -> %s\n",
+ inst->inst_name, ofile, nfile);
+ goto done;
+ }
+ slapi_ch_free_string(&ofile);
+ slapi_ch_free_string(&nfile);
+ }
+
+ rval = db_create(&thisdb, priv->dblayer_env->dblayer_DB_ENV, 0);
+ if (0 != rval)
+ {
+ LDAPDebug(LDAP_DEBUG_ANY, "db_create returned %d (%s)\n",
+ rval, dblayer_strerror(rval), 0);
+ goto done;
+ }
+ ofile = slapi_ch_malloc(strlen(inst_dirp) +
+ strlen(ID2ENTRY) + strlen(oldext) + 2);
+ nfile = slapi_ch_malloc(strlen(inst_dirp) +
+ strlen(ID2ENTRY) + strlen(newext) + 2);
+ sprintf(ofile, "%s/%s%s", inst_dirp, ID2ENTRY, oldext);
+ sprintf(nfile, "%s/%s%s", inst_dirp, ID2ENTRY, newext);
+ LDAPDebug(LDAP_DEBUG_TRACE, "update_db_ext: rename %s -> %s\n",
+ ofile, nfile, 0);
+ rval = thisdb->rename(thisdb, (const char *)ofile, NULL /* subdb */,
+ (const char *)nfile, 0);
+ if (0 != rval)
+ {
+ LDAPDebug(LDAP_DEBUG_ANY, "rename returned %d (%s)\n",
+ rval, dblayer_strerror(rval), 0);
+ LDAPDebug(LDAP_DEBUG_ANY,
+ "update_db_ext: index (%s) Failed to update index %s -> %s\n",
+ inst->inst_name, ofile, nfile);
+ }
+done:
+ slapi_ch_free_string(&ofile);
+ slapi_ch_free_string(&nfile);
+ if (inst_dirp != inst_dir)
+ slapi_ch_free_string(&inst_dirp);
+
+ return rval;
+}
+
+/*
+ * delete the index files belonging to the instance
+ */
+int dblayer_delete_indices(ldbm_instance *inst)
+{
+ int rval = -1;
+ struct attrinfo *a = NULL;
+ int i;
+
+ if (NULL == inst)
+ {
+ LDAPDebug(LDAP_DEBUG_ANY,
+ "update_index_ext: Null instance is passed\n", 0, 0, 0);
+ return rval;
+ }
+ rval = 0;
+ for (a = (struct attrinfo *)avl_getfirst(inst->inst_attrs), i = 0;
+ NULL != a;
+ a = (struct attrinfo *)avl_getnext(), i++)
+ {
+ rval += dblayer_erase_index_file(inst->inst_be, a, i/* chkpt; 1st time only */);
+ }
+ return rval;
+}
+
+void dblayer_set_recovery_required(struct ldbminfo *li)
+{
+ if (NULL == li || NULL == li->li_dblayer_private)
+ {
+ LDAPDebug(LDAP_DEBUG_ANY,"set_recovery_required: no dblayer info\n",
+ 0, 0, 0);
+ return;
+ }
+ li->li_dblayer_private->dblayer_recovery_required = 1;
+}
diff --git a/ldap/servers/slapd/back-ldbm/dblayer.h b/ldap/servers/slapd/back-ldbm/dblayer.h
new file mode 100644
index 00000000..78274ec6
--- /dev/null
+++ b/ldap/servers/slapd/back-ldbm/dblayer.h
@@ -0,0 +1,140 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+/* Structures and #defines used in the dblayer. */
+
+#ifndef _DBLAYER_H_
+#define _DBLAYER_H_
+
+#ifdef DB_USE_64LFS
+#ifdef OS_solaris
+#include <dlfcn.h> /* needed for dlopen and dlsym */
+#endif /* solaris: dlopen */
+#ifdef OS_solaris
+#include <sys/mman.h> /* needed for mmap/mmap64 */
+#ifndef MAP_FAILED
+#define MAP_FAILED (-1)
+#endif
+#endif /* solaris: mmap */
+#endif /* DB_USE_64LFS */
+
+#define DBLAYER_PAGESIZE (size_t)8*1024
+#define DBLAYER_INDEX_PAGESIZE (size_t)8*1024 /* With the new idl design,
+ the large 8Kbyte pages we use are not optimal. The page pool churns very
+ quickly as we add new IDs under a sustained add load. Smaller pages stop
+ this happening so much and consequently make us spend less time flushing
+ dirty pages on checkpoints. But 8K is still a good page size for id2entry.
+ So we now allow different page sizes for the primary and secondary indices.
+ */
+
+/* Interval, in ms, that threads sleep when they are wanting to
+ * wait for a while withouth spinning. If this time is too long,
+ * the server takes too long to shut down. If this interval is too
+ * short, then CPU time gets burned by threads doing nothing.
+ * As CPU speed increases over time, we reduce this interval
+ * to allow the server to be more responsive to shutdown.
+ * (Why is this important ? : A: because the TET tests start up
+ * and shut down the server a gazillion times, so the server
+ * shut down delay has a significant impact on the overall test
+ * run time (which is very very very looooonnnnnggggg....).)
+*/
+#define DBLAYER_SLEEP_INTERVAL 250
+
+#define DB_EXTN_PAGE_HEADER_SIZE 64 /* DBDB this is a guess */
+
+#define DBLAYER_CACHE_FORCE_FILE 1
+
+#define DBLAYER_LIB_VERSION_PRE_24 1
+#define DBLAYER_LIB_VERSION_POST_24 2
+
+/* Define constants from DB2.4 when using DB2.3 header file */
+#ifndef DB_TSL_SPINS
+#define DB_TSL_SPINS 21 /* DB: initialize spin count. */
+#endif
+#ifndef DB_REGION_INIT
+#define DB_REGION_INIT 24 /* DB: page-fault regions in create. */
+#endif
+#ifndef DB_REGION_NAME
+#define DB_REGION_NAME 25 /* DB: named regions, no backing file. */
+#endif
+
+struct dblayer_private_env {
+ DB_ENV *dblayer_DB_ENV;
+ PRRWLock * dblayer_env_lock;
+ int dblayer_openflags;
+ int dblayer_priv_flags;
+};
+
+#define DBLAYER_PRIV_SET_DATA_DIR 0x1
+
+/* structure which holds our stuff */
+struct dblayer_private
+{
+ struct dblayer_private_env * dblayer_env;
+ char *dblayer_home_directory;
+ char *dblayer_log_directory;
+ char *dblayer_dbhome_directory; /* default path for relative inst paths */
+ char **dblayer_data_directories; /* passed to set_data_dir
+ * including dblayer_dbhome_directory */
+ char **dblayer_db_config;
+ int dblayer_ncache;
+ int dblayer_previous_ncache;
+ int dblayer_tx_max;
+ size_t dblayer_cachesize;
+ size_t dblayer_previous_cachesize; /* Cache size when we last shut down--
+ * used to determine if we delete
+ * the mpool */
+ int dblayer_recovery_required;
+ int dblayer_enable_transactions;
+ int dblayer_durable_transactions;
+ int dblayer_checkpoint_interval;
+ int dblayer_circular_logging;
+ size_t dblayer_page_size; /* db page size if configured,
+ * otherwise default to DBLAYER_PAGESIZE */
+ size_t dblayer_index_page_size; /* db index page size if configured,
+ * otherwise default to
+ * DBLAYER_INDEX_PAGESIZE */
+ int dblayer_idl_divisor; /* divide page size by this to get IDL
+ * size */
+ size_t dblayer_logfile_size; /* How large can one logfile be ? */
+ size_t dblayer_logbuf_size; /* how large log buffer can be */
+ int dblayer_file_mode; /* pmode for files we create */
+ int dblayer_verbose; /* Get libdb to exhale debugging info */
+ int dblayer_debug; /* Will libdb emit debugging info into
+ * our log ? */
+ int dblayer_trickle_percentage;
+ int dblayer_cache_config; /* Special cache configurations
+ * e.g. force file-based mpool */
+ int dblayer_lib_version;
+ int dblayer_spin_count; /* DB Mutex spin count, 0 == use default */
+ int dblayer_named_regions; /* Should the regions be named sections,
+ * or backed by files ? */
+ int dblayer_private_mem; /* private memory will be used for
+ * allocation of regions and mutexes */
+ int dblayer_private_import_mem; /* private memory will be used for
+ * allocation of regions and mutexes for
+ * import */
+ long dblayer_shm_key; /* base segment ID for named regions */
+ int db_debug_checkpointing; /* Enable debugging messages from
+ * checkpointing */
+ int dblayer_bad_stuff_happened; /* Means that something happened (e.g. out
+ * of disk space) such that the guardian
+ * file must not be written on shutdown */
+ perfctrs_private *perf_private; /* Private data for performance counters
+ * code */
+ int dblayer_stop_threads; /* Used to signal to threads that they
+ * should stop ASAP */
+ PRInt32 dblayer_thread_count; /* Tells us how many threads are running,
+ * used to figure out when they're all
+ * stopped */
+ int dblayer_lockdown; /* use DB_LOCKDOWN */
+ int dblayer_lock_config;
+};
+
+int dblayer_db_remove(dblayer_private_env * env, char const path[], char const dbName[]);
+
+int dblayer_delete_indices(ldbm_instance *inst);
+
+#endif /* _DBLAYER_H_ */
diff --git a/ldap/servers/slapd/back-ldbm/dbsize.c b/ldap/servers/slapd/back-ldbm/dbsize.c
new file mode 100644
index 00000000..891310fd
--- /dev/null
+++ b/ldap/servers/slapd/back-ldbm/dbsize.c
@@ -0,0 +1,25 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+/*
+ * dbsize.c - ldbm backend routine which returns the size (in bytes)
+ * that the database occupies on disk.
+ */
+
+#include "back-ldbm.h"
+
+int
+ldbm_db_size( Slapi_PBlock *pb )
+{
+ struct ldbminfo *li;
+ unsigned int size;
+ int rc;
+
+ slapi_pblock_get( pb, SLAPI_PLUGIN_PRIVATE, &li );
+ rc = dblayer_database_size(li, &size);
+ slapi_pblock_set( pb, SLAPI_DBSIZE, &size );
+
+ return rc;
+}
diff --git a/ldap/servers/slapd/back-ldbm/dbtest.c b/ldap/servers/slapd/back-ldbm/dbtest.c
new file mode 100644
index 00000000..e2ba3dd0
--- /dev/null
+++ b/ldap/servers/slapd/back-ldbm/dbtest.c
@@ -0,0 +1,312 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+/* dbtest.c - ldbm database test program */
+
+#include "back-ldbm.h"
+
+#define SLAPI_LDBM_DBTEST_OPT_DUMPDATA 0x0001
+#define SLAPI_LDBM_DBTEST_OPT_KEY_IS_BINARY 0x0002
+#define SLAPI_LDBM_DBTEST_OPT_DATA_IS_BINARY 0x0004
+#define SLAPI_LDBM_DBTEST_OPT_DATA_IS_IDLIST 0x0008
+#define SLAPI_LDBM_DBTEST_OPT_KEY_IS_ID 0x0010
+
+static void dbtest_help( void );
+static void dbtest_traverse( DB *db, char *filename, unsigned int options,
+ FILE *outfp );
+static void dbtest_print_idlist( char *keystr, void *p, u_int32_t size,
+ FILE *outfp );
+static void dbtest_bprint( char *data, int len, char *lineprefix,
+ FILE *outfp );
+
+int ldbm_back_db_test( Slapi_PBlock *pb )
+{
+ char buf[256], *instance_name;
+ backend *be;
+ struct ldbminfo *li;
+ ldbm_instance *inst;
+ struct attrinfo *ai;
+ DB *db;
+ int err, traversal_options;
+
+ slapi_pblock_get( pb, SLAPI_PLUGIN_PRIVATE, &li );
+
+ /* essential initialization */
+ mapping_tree_init();
+ ldbm_config_load_dse_info(li);
+ /* Turn off transactions */
+ ldbm_config_internal_set(li, CONFIG_DB_TRANSACTION_LOGGING, "off");
+
+ /* Find the instance */
+ slapi_pblock_get( pb, SLAPI_BACKEND_INSTANCE_NAME, &instance_name );
+ inst = ldbm_instance_find_by_name(li, instance_name);
+ if (NULL == inst) {
+ LDAPDebug(LDAP_DEBUG_ANY, "dbtest: unknown ldbm instance %s\n",
+ instance_name, 0, 0);
+ return -1;
+ }
+
+ /* store the be in the pb */
+ be = inst->inst_be;
+ slapi_pblock_set(pb, SLAPI_BACKEND, be);
+
+ /***** prepare & init libdb, dblayer, and dbinstance *****/
+ if (0 != dblayer_start(li, DBLAYER_TEST_MODE)) {
+ LDAPDebug( LDAP_DEBUG_ANY,
+ "dbtest: Failed to init database\n", 0, 0, 0 );
+ return( -1 );
+ }
+ if ( 0 != dblayer_instance_start(inst->inst_be, DBLAYER_NORMAL_MODE)) {
+ LDAPDebug( LDAP_DEBUG_ANY,
+ "dbtest: failed to start instance\n", 0, 0, 0 );
+ return( -1 );
+ }
+
+ /* display commands help test */
+ dbtest_help();
+
+ while ( 1 ) {
+ traversal_options = 0;
+ fputs( "dbtest: ", stdout );
+
+ if ( fgets( buf, sizeof(buf), stdin ) == NULL )
+ break;
+
+ switch ( buf[0] ) {
+ case 'i':
+ traversal_options |= SLAPI_LDBM_DBTEST_OPT_DATA_IS_IDLIST;
+ /*FALLTHRU*/
+
+ case 't':
+ traversal_options |= SLAPI_LDBM_DBTEST_OPT_DUMPDATA;
+ /*FALLTHRU*/
+
+ case 'T':
+ /* read the index to traverse */
+ fputs( " attr: ", stdout );
+ if ( fgets( buf, sizeof(buf), stdin ) == NULL ) {
+ exit( 0 );
+ }
+ buf[strlen( buf ) - 1] = '\0';
+ ai = NULL;
+ ainfo_get( be, buf, &ai );
+ if ( ai == NULL ) {
+ fprintf( stderr, "no index for %s\n", buf );
+ continue;
+ }
+
+ /* open the index file */
+ if ( (err = dblayer_get_index_file( be, ai, &db, 0 /* no create */ ))
+ != 0 ) {
+ fprintf( stderr, "could not get index for %s (error %d - %s)\n",
+ buf, err, slapd_system_strerror( err ));
+ continue;
+ }
+
+ /* traverse the file */
+ traversal_options |= SLAPI_LDBM_DBTEST_OPT_DATA_IS_BINARY;
+ dbtest_traverse( db, buf, traversal_options, stdout );
+
+ /* clean up */
+ dblayer_release_index_file( be, ai, db );
+ break;
+
+ case 'u':
+ traversal_options |= SLAPI_LDBM_DBTEST_OPT_DUMPDATA;
+ /*FALLTHRU*/
+
+ case 'U':
+ /* open the id2entry file */
+ if ( (err = dblayer_get_id2entry( be, &db )) != 0 ) {
+ fprintf( stderr, "could not get i2entry\n" );
+ continue;
+ }
+
+ /* traverse the file */
+ traversal_options |= SLAPI_LDBM_DBTEST_OPT_KEY_IS_ID;
+ dbtest_traverse( db, "id2entry", traversal_options, stdout );
+
+ /* clean up */
+ dblayer_release_id2entry( be, db );
+ break;
+
+ default:
+ dbtest_help();
+ break;
+ }
+ }
+
+ return( 0 );
+}
+
+
+static void
+dbtest_help()
+{
+ puts( LDBM_DATABASE_TYPE_NAME " test mode" );
+ puts( "\nindex key prefixes:" );
+ printf( " %c presence (sn=*)\n", PRES_PREFIX );
+ printf( " %c equality (sn=jensen)\n", EQ_PREFIX );
+ printf( " %c approximate (sn~=jensin)\n", APPROX_PREFIX );
+ printf( " %c substring (sn=jen*)\n", SUB_PREFIX );
+ printf( " %c matching rule (sn:1.2.3.4.5:=Jensen)\n", RULE_PREFIX );
+ printf( " %c continuation\n", CONT_PREFIX );
+
+ puts( "\ncommands: i => traverse index keys and ID list values" );
+ puts( " t => traverse index keys and values" );
+ puts( " T => traverse index keys" );
+ puts( " u => traverse id2entry keys and values" );
+ puts( " U => traverse id2entry keys" );
+#if 0
+ puts( " l<c> => lookup index" );
+ puts( " L<c> => lookup index (all)" );
+ puts( " t<c> => traverse index keys and values" );
+ puts( " T<c> => traverse index keys" );
+ puts( " x<c> => delete from index" );
+ puts( " e<c> => edit index entry" );
+ puts( " a<c> => add index entry" );
+ puts( " c<c> => create index" );
+ puts( " i<c> => insert ids into index" );
+ puts( " b => change default backend" );
+ puts( " B => print default backend" );
+ puts( " d<n> => set slapd_ldap_debug to n" );
+ puts( "where <c> is a char selecting the index:" );
+ puts( " c => id2children" );
+ puts( " d => dn2id" );
+ puts( " e => id2entry" );
+ puts( " f => arbitrary file" );
+ puts( " i => attribute index" );
+#endif /* 0 */
+}
+
+
+/*
+ * get a cursor and walk over the databasea
+ */
+static void
+dbtest_traverse( DB *db, char *filename, unsigned int options, FILE *outfp )
+{
+ DBC *dbc;
+ DBT key, data;
+
+ dbc = NULL;
+ if ( db->cursor( db, NULL, &dbc, 0 ) != 0 ) {
+ fprintf( stderr, "could not get cursor for %s\n", filename );
+ return;
+ }
+
+ memset( &key, 0, sizeof(key) );
+ memset( &data, 0, sizeof(data) );
+ key.flags = DB_DBT_MALLOC;
+ data.flags = DB_DBT_MALLOC;
+ while ( dbc->c_get( dbc, &key, &data, DB_NEXT ) == 0 ) {
+ if (( options & SLAPI_LDBM_DBTEST_OPT_KEY_IS_BINARY ) != 0 ) {
+ fputs( "\tkey: ", outfp );
+ dbtest_bprint( key.data, key.size, "\t ", outfp );
+ } else if (( options & SLAPI_LDBM_DBTEST_OPT_KEY_IS_ID ) != 0 ) {
+ fprintf( outfp, "\tkey: %ld\n",
+ (u_long)id_stored_to_internal( (char *)key.data ));
+ } else {
+ fprintf( outfp, "\tkey: %s\n", (char *)key.data );
+ }
+ if (( options & SLAPI_LDBM_DBTEST_OPT_DUMPDATA ) != 0 ) {
+ if (( options & SLAPI_LDBM_DBTEST_OPT_DATA_IS_IDLIST ) != 0 ) {
+ fputs( "\tdata: ", outfp );
+ dbtest_print_idlist( (char *)key.dptr, data.data, data.size,
+ outfp );
+ } else if (( options & SLAPI_LDBM_DBTEST_OPT_DATA_IS_BINARY ) != 0 ) {
+ fputs( "\tdata: ", outfp );
+ dbtest_bprint( data.data, data.size, "\t ", outfp );
+ } else {
+ fprintf( outfp, "\tdata: %s\n", (char *)data.data );
+ }
+ }
+ free( key.data );
+ free( data.data );
+ }
+ dbc->c_close(dbc);
+}
+
+static void
+dbtest_print_idlist( char *keystr, void *p, u_int32_t size, FILE *outfp )
+{
+ IDList *idl;
+ ID i;
+
+ idl = (IDList *)p;
+ if ( ALLIDS( idl )) {
+ fputs( "ALLIDS block\n", outfp );
+ } else if ( INDIRECT_BLOCK( idl )) {
+ fputs( "Indirect block)\n", outfp );
+ for ( i = 0; idl->b_ids[i] != NOID; ++i ) {
+ fprintf( outfp, "\t\tkey: %c%s%lu\n", CONT_PREFIX, keystr,
+ (u_long)idl->b_ids[i] );
+ }
+ } else {
+ const char *block_type;
+
+ if ( NULL != keystr && *keystr == CONT_PREFIX ) {
+ block_type = "Continued";
+ } else {
+ block_type = "Regular";
+ }
+ fprintf( outfp, "%s block (count=%lu, max=%lu)\n",
+ block_type, (u_long)idl->b_nids, (u_long)idl->b_nmax );
+ for ( i = 0; i < idl->b_nids; ++i ) {
+ fprintf( outfp, "\t\tid: %lu\n", (u_long)idl->b_ids[i] );
+ }
+ }
+}
+
+
+
+#define BPLEN 48
+
+static void
+dbtest_bprint( char *data, int len, char *lineprefix, FILE *outfp )
+{
+ static char hexdig[] = "0123456789abcdef";
+ char out[ BPLEN ], *curprefix;
+ int i = 0;
+
+ if ( NULL == lineprefix ) {
+ lineprefix = "";
+ }
+ curprefix = "";
+
+ memset( out, 0, BPLEN );
+ for ( ;; ) {
+ if ( len < 1 ) {
+ if ( i > 0 ) {
+ fprintf( outfp, "%s%s\n", curprefix, out );
+ }
+ break;
+ }
+
+#ifndef HEX
+ if ( isgraph( (unsigned char)*data )) {
+ out[ i ] = ' ';
+ out[ i+1 ] = *data;
+ } else {
+#endif
+ out[ i ] = hexdig[ ( *data & 0xf0 ) >> 4 ];
+ out[ i+1 ] = hexdig[ *data & 0x0f ];
+#ifndef HEX
+ }
+#endif
+ i += 2;
+ len--;
+ data++;
+
+ if ( i > BPLEN - 2 ) {
+ fprintf( outfp, "%s%s\n", curprefix, out );
+ curprefix = lineprefix;
+ memset( out, 0, BPLEN );
+ i = 0;
+ continue;
+ }
+ out[ i++ ] = ' ';
+ }
+}
diff --git a/ldap/servers/slapd/back-ldbm/dbversion.c b/ldap/servers/slapd/back-ldbm/dbversion.c
new file mode 100644
index 00000000..4c1a56da
--- /dev/null
+++ b/ldap/servers/slapd/back-ldbm/dbversion.c
@@ -0,0 +1,181 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+
+#include "back-ldbm.h"
+
+static void
+mk_dbversion_fullpath(struct ldbminfo *li, const char *directory, char *filename)
+{
+ if (li)
+ {
+ if (is_fullpath((char *)directory))
+ {
+ sprintf(filename, "%s/%s", directory, DBVERSION_FILENAME);
+ }
+ else
+ {
+ char *home_dir = dblayer_get_home_dir(li, NULL);
+ /* if relpath, nsslapd-dbhome_directory should be set */
+ sprintf(filename,"%s/%s/%s", home_dir,directory,DBVERSION_FILENAME);
+ }
+ }
+ else
+ {
+ sprintf(filename, "%s/%s", directory, DBVERSION_FILENAME);
+ }
+}
+
+/*
+ * Function: dbversion_write
+ *
+ * Returns: returns 0 on success, -1 on failure
+ *
+ * Description: This function writes the DB version file.
+ */
+int
+dbversion_write(struct ldbminfo *li, const char *directory,
+ const char *dataversion)
+{
+ char filename[ MAXPATHLEN*2 ];
+ PRFileDesc *prfd;
+ int rc = 0;
+
+ PR_ASSERT(is_fullpath((char *)directory));
+ mk_dbversion_fullpath(li, directory, filename);
+
+ /* Open the file */
+ if (( prfd = PR_Open( filename, PR_RDWR | PR_CREATE_FILE | PR_TRUNCATE,
+ SLAPD_DEFAULT_FILE_MODE )) == NULL )
+ {
+ LDAPDebug( LDAP_DEBUG_ANY, "Could not open file \"%s\" for writing "
+ SLAPI_COMPONENT_NAME_NSPR " %d (%s)\n",
+ filename, PR_GetError(), slapd_pr_strerror(PR_GetError()) );
+ rc= -1;
+ }
+ else
+ {
+ /* Write the file */
+ PRInt32 len;
+ char buf[ LDBM_VERSION_MAXBUF ];
+ /* recognize the difference between an old/new database regarding idl
+ * (406922) */
+ if (idl_get_idl_new())
+ {
+#if defined(USE_NEW_IDL)
+ sprintf( buf, "%s\n", LDBM_VERSION );
+#else
+ sprintf( buf, "%s\n", LDBM_VERSION_NEW );
+#endif
+ }
+ else
+ {
+#if defined(USE_NEW_IDL)
+ sprintf( buf, "%s\n", LDBM_VERSION_OLD );
+#else
+ sprintf( buf, "%s\n", LDBM_VERSION );
+#endif
+ }
+ len = strlen( buf );
+ if ( slapi_write_buffer( prfd, buf, len ) != len )
+ {
+ LDAPDebug( LDAP_DEBUG_ANY, "Could not write to file \"%s\"\n", filename, 0, 0 );
+ rc= -1;
+ }
+ if(rc==0 && dataversion!=NULL)
+ {
+ sprintf( buf, "%s\n", dataversion );
+ len = strlen( buf );
+ if ( slapi_write_buffer( prfd, buf, len ) != len )
+ {
+ LDAPDebug( LDAP_DEBUG_ANY, "Could not write to file \"%s\"\n", filename, 0, 0 );
+ rc= -1;
+ }
+ }
+ (void)PR_Close( prfd );
+ }
+ return rc;
+}
+
+/*
+ * Function: dbversion_read
+ *
+ * Returns: returns 0 on success, -1 on failure
+ *
+ * Description: This function reads the DB version file.
+ */
+int
+dbversion_read(struct ldbminfo *li, const char *directory,
+ char *ldbmversion, char *dataversion)
+{
+ char filename[ MAXPATHLEN*2 ];
+ PRFileDesc *prfd;
+ int rc = -1;
+ char * iter = NULL;
+
+ PR_ASSERT(is_fullpath((char *)directory));
+ mk_dbversion_fullpath(li, directory, filename);
+
+ ldbmversion[0]= '\0';
+ dataversion[0]= '\0';
+
+ /* Open the file */
+ if (( prfd = PR_Open( filename, PR_RDONLY, SLAPD_DEFAULT_FILE_MODE )) ==
+ NULL )
+ {
+ /* File missing... we are probably creating a new database. */
+ }
+ else
+ {
+ char buf[LDBM_VERSION_MAXBUF];
+ PRInt32 nr = slapi_read_buffer( prfd, buf,
+ (PRInt32)LDBM_VERSION_MAXBUF-1 );
+ if ( nr > 0 && nr != (PRInt32)LDBM_VERSION_MAXBUF-1 )
+ {
+ char *t;
+ buf[nr]= '\0';
+ t= ldap_utf8strtok_r(buf,"\n", &iter);
+ if(t!=NULL)
+ {
+ strcpy(ldbmversion,t);
+ t= ldap_utf8strtok_r(NULL,"\n", &iter);
+ if(t!=NULL && t[0]!='\0')
+ {
+ strcpy(dataversion,t);
+ }
+ }
+ }
+ (void)PR_Close( prfd );
+ rc= 0;
+ }
+ return rc;
+}
+
+
+/*
+ * Function: dbversion_exists
+ *
+ * Returns: 1 for exists, 0 for not.
+ *
+ * Description: This function checks if the DB version file exists.
+ */
+int
+dbversion_exists(struct ldbminfo *li, const char *directory)
+{
+ char filename[ MAXPATHLEN*2 ];
+ PRFileDesc *prfd;
+
+ PR_ASSERT(is_fullpath((char *)directory));
+ mk_dbversion_fullpath(li, directory, filename);
+
+ if (( prfd = PR_Open( filename, PR_RDONLY, SLAPD_DEFAULT_FILE_MODE )) ==
+ NULL )
+ {
+ return 0;
+ }
+ (void)PR_Close( prfd );
+ return 1;
+}
+
diff --git a/ldap/servers/slapd/back-ldbm/dllmain.c b/ldap/servers/slapd/back-ldbm/dllmain.c
new file mode 100644
index 00000000..d473ca49
--- /dev/null
+++ b/ldap/servers/slapd/back-ldbm/dllmain.c
@@ -0,0 +1,128 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+/*
+ * Microsoft Windows specifics for BACK-LDBM DLL
+ */
+#include "back-ldbm.h"
+
+
+#ifdef _WIN32
+/* Lifted from Q125688
+ * How to Port a 16-bit DLL to a Win32 DLL
+ * on the MSVC 4.0 CD
+ */
+BOOL WINAPI DllMain (HANDLE hModule, DWORD fdwReason, LPVOID lpReserved)
+{
+
+ switch (fdwReason)
+ {
+ case DLL_PROCESS_ATTACH:
+ /* Code from LibMain inserted here. Return TRUE to keep the
+ DLL loaded or return FALSE to fail loading the DLL.
+
+ You may have to modify the code in your original LibMain to
+ account for the fact that it may be called more than once.
+ You will get one DLL_PROCESS_ATTACH for each process that
+ loads the DLL. This is different from LibMain which gets
+ called only once when the DLL is loaded. The only time this
+ is critical is when you are using shared data sections.
+ If you are using shared data sections for statically
+ allocated data, you will need to be careful to initialize it
+ only once. Check your code carefully.
+
+ Certain one-time initializations may now need to be done for
+ each process that attaches. You may also not need code from
+ your original LibMain because the operating system may now
+ be doing it for you.
+ */
+ /*
+ * 16 bit code calls UnlockData()
+ * which is mapped to UnlockSegment in windows.h
+ * in 32 bit world UnlockData is not defined anywhere
+ * UnlockSegment is mapped to GlobalUnfix in winbase.h
+ * and the docs for both UnlockSegment and GlobalUnfix say
+ * ".. function is oboslete. Segments have no meaning
+ * in the 32-bit environment". So we do nothing here.
+ */
+
+
+ break;
+
+ case DLL_THREAD_ATTACH:
+ /* Called each time a thread is created in a process that has
+ already loaded (attached to) this DLL. Does not get called
+ for each thread that exists in the process before it loaded
+ the DLL.
+
+ Do thread-specific initialization here.
+ */
+ break;
+
+ case DLL_THREAD_DETACH:
+ /* Same as above, but called when a thread in the process
+ exits.
+
+ Do thread-specific cleanup here.
+ */
+ break;
+
+ case DLL_PROCESS_DETACH:
+ /* Code from _WEP inserted here. This code may (like the
+ LibMain) not be necessary. Check to make certain that the
+ operating system is not doing it for you.
+ */
+
+ break;
+ }
+ /* The return value is only used for DLL_PROCESS_ATTACH; all other
+ conditions are ignored. */
+ return TRUE; // successful DLL_PROCESS_ATTACH
+}
+#else
+int CALLBACK
+LibMain( HINSTANCE hinst, WORD wDataSeg, WORD cbHeapSize, LPSTR lpszCmdLine )
+{
+ /*UnlockData( 0 );*/
+ return( 1 );
+}
+#endif
+
+#ifdef LDAP_DEBUG
+#ifndef _WIN32
+#include <stdarg.h>
+#include <stdio.h>
+
+void LDAPDebug( int level, char* fmt, ... )
+{
+ static char debugBuf[1024];
+
+ if (slapd_ldap_debug & level)
+ {
+ va_list ap;
+ va_start (ap, fmt);
+ _snprintf (debugBuf, sizeof(debugBuf), fmt, ap);
+ va_end (ap);
+
+ OutputDebugString (debugBuf);
+ }
+}
+#endif
+#endif
+
+#ifndef _WIN32
+
+/* The 16-bit version of the RTL does not implement perror() */
+
+#include <stdio.h>
+
+void perror( const char *msg )
+{
+ char buf[128];
+ wsprintf( buf, "%s: error %d\n", msg, WSAGetLastError()) ;
+ OutputDebugString( buf );
+}
+
+#endif
diff --git a/ldap/servers/slapd/back-ldbm/dn2entry.c b/ldap/servers/slapd/back-ldbm/dn2entry.c
new file mode 100644
index 00000000..f34e7f77
--- /dev/null
+++ b/ldap/servers/slapd/back-ldbm/dn2entry.c
@@ -0,0 +1,230 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+/* dn2entry.c - given a dn return an entry */
+
+#include "back-ldbm.h"
+
+/*
+ * Fetch the entry for this DN.
+ *
+ * Retuns NULL of the entry doesn't exist
+ */
+struct backentry *
+dn2entry(
+ Slapi_Backend *be,
+ const Slapi_DN *sdn,
+ back_txn *txn,
+ int *err
+)
+{
+ ldbm_instance *inst;
+ struct berval ndnv;
+ struct backentry *e = NULL;
+
+ LDAPDebug( LDAP_DEBUG_TRACE, "=> dn2entry \"%s\"\n", slapi_sdn_get_dn(sdn), 0, 0 );
+
+ inst = (ldbm_instance *) be->be_instance_info;
+
+ *err = 0;
+ ndnv.bv_val = (void*)slapi_sdn_get_ndn(sdn); /* jcm - Had to cast away const */
+ ndnv.bv_len = slapi_sdn_get_ndn_len(sdn);
+
+ e = cache_find_dn(&inst->inst_cache, ndnv.bv_val, ndnv.bv_len);
+ if (e == NULL)
+ {
+ /* convert dn to entry id */
+ IDList *idl = NULL;
+ if ( (idl = index_read( be, "entrydn", indextype_EQUALITY, &ndnv, txn, err )) == NULL )
+ {
+ /* There's no entry with this DN. */
+ }
+ else
+ {
+ /* convert entry id to entry */
+ if ( (e = id2entry( be, idl_firstid( idl ), txn, err )) != NULL )
+ {
+ /* Means that we found the entry OK */
+ }
+ else
+ {
+ /* Hmm. The DN mapped onto an EntryID, but that didn't map onto an Entry. */
+ if ( *err != 0 && *err != DB_NOTFOUND )
+ {
+ /* JCM - Not sure if this is ever OK or not. */
+ }
+ else
+ {
+ /*
+ * this is pretty bad anyway. the dn was in the
+ * entrydn index, but we could not read the entry
+ * from the id2entry index. what should we do?
+ */
+ LDAPDebug( LDAP_DEBUG_ANY,
+ "dn2entry: the dn was in the entrydn index (id %lu), "
+ "but it did not exist in id2entry.\n",
+ (u_long)idl_firstid( idl ), 0, 0 );
+ }
+ }
+ slapi_ch_free((void**)&idl);
+ }
+ }
+
+ LDAPDebug( LDAP_DEBUG_TRACE, "<= dn2entry %p\n", e, 0, 0 );
+ return( e );
+}
+
+/*
+ * dn2entry_or_ancestor - look up dn in the cache/indexes and return the
+ * corresponding entry. If the entry is not found, this function returns NULL
+ * and sets ancestordn to the DN of highest entry in the tree matched.
+ *
+ * ancestordn should be initialized before calling this function.
+ *
+ * When the caller is finished with the entry returned, it should return it
+ * to the cache:
+ * e = dn2entry_or_ancestor( ... );
+ * if ( NULL != e ) {
+ * cache_return( &inst->inst_cache, &e );
+ * }
+ */
+struct backentry *
+dn2entry_or_ancestor(
+ Slapi_Backend *be,
+ const Slapi_DN *sdn,
+ Slapi_DN *ancestordn,
+ back_txn *txn,
+ int *err
+)
+{
+ struct backentry *e;
+
+ LDAPDebug( LDAP_DEBUG_TRACE, "=> dn2entry_or_ancestor \"%s\"\n", slapi_sdn_get_dn(sdn), 0, 0 );
+
+ /*
+ * Fetch the entry asked for.
+ */
+
+ e= dn2entry(be,sdn,txn,err);
+
+ if(e==NULL)
+ {
+ /*
+ * could not find the entry named. crawl back up the dn and
+ * stop at the first ancestor that does exist, or when we get
+ * to the suffix.
+ */
+ e= dn2ancestor(be,sdn,ancestordn,txn,err);
+ }
+
+ LDAPDebug( LDAP_DEBUG_TRACE, "<= dn2entry_or_ancestor %p\n", e, 0, 0 );
+ return( e );
+}
+
+/*
+ * Use the DN to fetch the parent of the entry.
+ * If the parent entry doesn't exist, keep working
+ * up the DN until we hit "" or an backend suffix.
+ *
+ * ancestordn should be initialized before calling this function.
+ *
+ * Returns NULL for no entry found.
+ *
+ * When the caller is finished with the entry returned, it should return it
+ * to the cache:
+ * e = dn2ancestor( ... );
+ * if ( NULL != e ) {
+ * cache_return( &inst->inst_cache, &e );
+ * }
+ */
+struct backentry *
+dn2ancestor(
+ Slapi_Backend *be,
+ const Slapi_DN *sdn,
+ Slapi_DN *ancestordn,
+ back_txn *txn,
+ int *err
+)
+{
+ struct backentry *e = NULL;
+
+ LDAPDebug( LDAP_DEBUG_TRACE, "=> dn2ancestor \"%s\"\n", slapi_sdn_get_dn(sdn), 0, 0 );
+
+ /* stop when we get to "", or a backend suffix point */
+ slapi_sdn_done(ancestordn); /* free any previous contents */
+ slapi_sdn_get_backend_parent(sdn,ancestordn,be);
+ if ( !slapi_sdn_isempty(ancestordn) )
+ {
+ Slapi_DN *newsdn = slapi_sdn_dup(ancestordn);
+ e = dn2entry_or_ancestor( be, newsdn, ancestordn, txn, err );
+ slapi_sdn_free(&newsdn);
+ }
+
+ LDAPDebug( LDAP_DEBUG_TRACE, "<= dn2ancestor %p\n", e, 0, 0 );
+ return( e );
+}
+
+/*
+ * Use uniqueid2entry or dn2entry to fetch an entry from the cache,
+ * make a copy of it, and stash it in the pblock.
+ */
+int
+get_copy_of_entry(Slapi_PBlock *pb, const entry_address *addr, back_txn *txn, int plock_parameter, int must_exist) /* JCM - Move somewhere more appropriate */
+{
+ int err= 0;
+ int rc= LDAP_SUCCESS;
+ backend *be;
+ struct backentry *entry;
+
+ slapi_pblock_get( pb, SLAPI_BACKEND, &be);
+
+ if( addr->uniqueid!=NULL)
+ {
+ entry = uniqueid2entry(be, addr->uniqueid, txn, &err );
+ }
+ else
+ {
+ Slapi_DN sdn;
+ slapi_sdn_init_dn_byref (&sdn, addr->dn); /* We assume that the DN is not normalized */
+ entry = dn2entry( be, &sdn, txn, &err );
+ slapi_sdn_done (&sdn);
+ }
+ if ( 0 != err && DB_NOTFOUND != err )
+ {
+ if(must_exist)
+ {
+ LDAPDebug( LDAP_DEBUG_ANY, "Operation error fetching %s (%s), error %d.\n",
+ addr->dn, (addr->uniqueid==NULL?"null":addr->uniqueid), err );
+ }
+ rc= LDAP_OPERATIONS_ERROR;
+ }
+ else
+ {
+ /* If an entry is found, copy it into the PBlock. */
+ if(entry!=NULL)
+ {
+ ldbm_instance *inst;
+ slapi_pblock_set( pb, plock_parameter, slapi_entry_dup(entry->ep_entry));
+ inst = (ldbm_instance *) be->be_instance_info;
+ cache_return( &inst->inst_cache, &entry );
+ }
+ }
+ /* JCMREPL - Free the backentry? */
+ return rc;
+}
+
+void
+done_with_pblock_entry(Slapi_PBlock *pb, int plock_parameter) /* JCM - Move somewhere more appropriate */
+{
+ Slapi_Entry *entry;
+ slapi_pblock_get( pb, plock_parameter, &entry);
+ if(entry!=NULL)
+ {
+ slapi_entry_free(entry);
+ entry= NULL;
+ slapi_pblock_set( pb, plock_parameter, entry);
+ }
+}
+
diff --git a/ldap/servers/slapd/back-ldbm/entrystore.c b/ldap/servers/slapd/back-ldbm/entrystore.c
new file mode 100644
index 00000000..46c011ae
--- /dev/null
+++ b/ldap/servers/slapd/back-ldbm/entrystore.c
@@ -0,0 +1,12 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+
+/* entrystore.c --- functions dealing with entries and their storage.
+ Put computed attributes, compression etc here */
+
+#include "back-ldbm.h"
+
+/* Nothing here yet */
diff --git a/ldap/servers/slapd/back-ldbm/filterindex.c b/ldap/servers/slapd/back-ldbm/filterindex.c
new file mode 100644
index 00000000..445f3195
--- /dev/null
+++ b/ldap/servers/slapd/back-ldbm/filterindex.c
@@ -0,0 +1,830 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+/* filterindex.c - generate the list of candidate entries from a filter */
+
+#include "back-ldbm.h"
+#include "../index_subsys.h"
+
+extern const char *indextype_PRESENCE;
+extern const char *indextype_EQUALITY;
+extern const char *indextype_APPROX;
+extern const char *indextype_SUB;
+
+static IDList *ava_candidates(Slapi_PBlock *pb, backend *be, Slapi_Filter *f, int ftype, Slapi_Filter *nextf, int range, int *err);
+static IDList *presence_candidates(Slapi_PBlock *pb, backend *be, Slapi_Filter *f, int *err);
+static IDList *extensible_candidates(backend *be, Slapi_Filter *f, int *err);
+static IDList *list_candidates(Slapi_PBlock *pb, backend *be, const char *base, Slapi_Filter *flist, int ftype, int *err);
+static IDList *substring_candidates(Slapi_PBlock *pb, backend *be, Slapi_Filter *f, int *err);
+static IDList * range_candidates(
+ Slapi_PBlock *pb,
+ backend *be,
+ char *type,
+ struct berval *low_val,
+ struct berval *high_val,
+ int *err
+);
+static IDList *
+keys2idl(
+ backend *be,
+ char *type,
+ const char *indextype,
+ Slapi_Value **ivals,
+ int *err
+);
+
+IDList *
+filter_candidates(
+ Slapi_PBlock *pb,
+ backend *be,
+ const char *base,
+ Slapi_Filter *f,
+ Slapi_Filter *nextf,
+ int range,
+ int *err
+)
+{
+ struct ldbminfo *li = (struct ldbminfo *) be->be_database->plg_private;
+ IDList *result;
+ int ftype;
+
+ LDAPDebug( LDAP_DEBUG_TRACE, "=> filter_candidates\n", 0, 0, 0 );
+
+ /* check if this is to be serviced by a virtual index */
+ if(INDEX_FILTER_EVALUTED == index_subsys_evaluate_filter(f, (Slapi_DN*)slapi_be_getsuffix(be, 0), (IndexEntryList**)&result))
+ {
+ LDAPDebug( LDAP_DEBUG_TRACE, "<= filter_candidates %lu (vattr)\n",
+ (u_long)IDL_NIDS(result), 0, 0 );
+ return result;
+ }
+
+ if (li->li_use_vlv) {
+ /* first, check to see if this particular filter node matches any
+ * vlv indexes we're keeping. if so, we can use that index
+ * instead.
+ */
+ result = vlv_find_index_by_filter(be, base, f);
+ if (result) {
+ LDAPDebug( LDAP_DEBUG_TRACE, "<= filter_candidates %lu (vlv)\n",
+ (u_long)IDL_NIDS(result), 0, 0 );
+ return result;
+ }
+ }
+
+ result = NULL;
+ switch ( (ftype = slapi_filter_get_choice( f )) ) {
+ case LDAP_FILTER_EQUALITY:
+ LDAPDebug( LDAP_DEBUG_FILTER, "\tEQUALITY\n", 0, 0, 0 );
+ result = ava_candidates( pb, be, f, LDAP_FILTER_EQUALITY, nextf, range, err );
+ break;
+
+ case LDAP_FILTER_SUBSTRINGS:
+ LDAPDebug( LDAP_DEBUG_FILTER, "\tSUBSTRINGS\n", 0, 0, 0 );
+ result = substring_candidates( pb, be, f, err );
+ break;
+
+ case LDAP_FILTER_GE:
+ LDAPDebug( LDAP_DEBUG_FILTER, "\tGE\n", 0, 0, 0 );
+ result = ava_candidates( pb, be, f, LDAP_FILTER_GE, nextf, range,
+ err );
+ break;
+
+ case LDAP_FILTER_LE:
+ LDAPDebug( LDAP_DEBUG_FILTER, "\tLE\n", 0, 0, 0 );
+ result = ava_candidates( pb, be, f, LDAP_FILTER_LE, nextf, range,
+ err );
+ break;
+
+ case LDAP_FILTER_PRESENT:
+ LDAPDebug( LDAP_DEBUG_FILTER, "\tPRESENT\n", 0, 0, 0 );
+ result = presence_candidates( pb, be, f, err );
+ break;
+
+ case LDAP_FILTER_APPROX:
+ LDAPDebug( LDAP_DEBUG_FILTER, "\tAPPROX\n", 0, 0, 0 );
+ result = ava_candidates( pb, be, f, LDAP_FILTER_APPROX, nextf,
+ range, err );
+ break;
+
+ case LDAP_FILTER_EXTENDED:
+ LDAPDebug( LDAP_DEBUG_FILTER, "\tEXTENSIBLE\n", 0, 0, 0 );
+ result = extensible_candidates( be, f, err );
+ break;
+
+ case LDAP_FILTER_AND:
+ LDAPDebug( LDAP_DEBUG_FILTER, "\tAND\n", 0, 0, 0 );
+ result = list_candidates( pb, be, base, f, LDAP_FILTER_AND, err );
+ break;
+
+ case LDAP_FILTER_OR:
+ LDAPDebug( LDAP_DEBUG_FILTER, "\tOR\n", 0, 0, 0 );
+ result = list_candidates( pb, be, base, f, LDAP_FILTER_OR, err );
+ break;
+
+ case LDAP_FILTER_NOT:
+ LDAPDebug( LDAP_DEBUG_FILTER, "\tNOT\n", 0, 0, 0 );
+ result = idl_allids( be );
+ break;
+
+ default:
+ LDAPDebug( LDAP_DEBUG_FILTER,
+ "filter_candidates: unknown type 0x%X\n",
+ ftype, 0, 0 );
+ break;
+ }
+
+ LDAPDebug( LDAP_DEBUG_TRACE, "<= filter_candidates %lu\n",
+ (u_long)IDL_NIDS(result), 0, 0 );
+ return( result );
+}
+
+static IDList *
+ava_candidates(
+ Slapi_PBlock *pb,
+ backend *be,
+ Slapi_Filter *f,
+ int ftype,
+ Slapi_Filter *nextf,
+ int range,
+ int *err
+)
+{
+ char *type, *indextype = NULL;
+ Slapi_Value sv;
+ struct berval *bval;
+ Slapi_Value **ivals;
+ IDList *idl;
+ void *pi;
+
+ LDAPDebug( LDAP_DEBUG_TRACE, "=> ava_candidates\n", 0, 0, 0 );
+
+ if ( slapi_filter_get_ava( f, &type, &bval ) != 0 ) {
+ LDAPDebug( LDAP_DEBUG_TRACE, " slapi_filter_get_ava failed\n",
+ 0, 0, 0 );
+ return( NULL );
+ }
+
+#ifdef LDAP_DEBUG
+ if ( LDAPDebugLevelIsSet( LDAP_DEBUG_TRACE )) {
+ char *op = NULL;
+ char buf[BUFSIZ];
+
+ switch ( ftype ) {
+ case LDAP_FILTER_GE:
+ op = ">=";
+ break;
+ case LDAP_FILTER_LE:
+ op = "<=";
+ break;
+ case LDAP_FILTER_EQUALITY:
+ op = "=";
+ break;
+ case LDAP_FILTER_APPROX:
+ op = "~=";
+ break;
+ }
+ LDAPDebug( LDAP_DEBUG_TRACE, " %s%s%s\n", type, op,
+ encode( bval, buf ) );
+ }
+#endif
+
+ switch ( ftype ) {
+ case LDAP_FILTER_GE:
+ idl = range_candidates(pb, be, type, bval, NULL, err);
+ LDAPDebug( LDAP_DEBUG_TRACE, "<= ava_candidates %lu\n",
+ (u_long)IDL_NIDS(idl), 0, 0 );
+ return( idl );
+ case LDAP_FILTER_LE:
+ idl = range_candidates(pb, be, type, NULL, bval, err);
+ LDAPDebug( LDAP_DEBUG_TRACE, "<= ava_candidates %lu\n",
+ (u_long)IDL_NIDS(idl), 0, 0 );
+ return( idl );
+ case LDAP_FILTER_EQUALITY:
+ indextype = (char*)indextype_EQUALITY;
+ break;
+ case LDAP_FILTER_APPROX:
+ indextype = (char*)indextype_APPROX;
+ break;
+ }
+
+ /*
+ * get the keys corresponding to this assertion value
+ */
+ if ( slapi_attr_type2plugin( type, &pi ) != 0 ) {
+ LDAPDebug( LDAP_DEBUG_TRACE, " slapi_filter_get_ava no plugin\n",
+ 0, 0, 0 );
+ return( NULL );
+ }
+
+ /* This code is result of performance anlysis; we are trying to
+ * optimize our equality filter processing -- mainly by limiting
+ * malloc/free calls.
+ *
+ * When the filter type is LDAP_FILTER_EQUALITY_FAST, the
+ * syntax_assertion2keys functions are passed a stack-based
+ * destination Slapi_Value array (ivals) that contains room
+ * for one key value with a fixed size buffer (also stack-based).
+ * If the buffer provided is not large enough, the
+ * syntax_assertion2keys function can alloc a new buffer (and
+ * reset ivals[0]->bv.bv_val) or alloc an entirely new ivals array.
+ */
+
+ if(ftype==LDAP_FILTER_EQUALITY) {
+ Slapi_Value tmp, *ptr[2], fake;
+ char buf[1024];
+
+ tmp.bv = *bval;
+ tmp.v_csnset=NULL;
+ fake.bv.bv_val=buf;
+ fake.bv.bv_len=sizeof(buf);
+ ptr[0]=&fake;
+ ptr[1]=NULL;
+ ivals=ptr;
+
+ slapi_call_syntax_assertion2keys_ava_sv( pi, &tmp, (Slapi_Value ***)&ivals, LDAP_FILTER_EQUALITY_FAST);
+ idl = keys2idl( be, type, indextype, ivals, err );
+
+ /* We don't use valuearray_free here since the valueset, berval
+ * and value was all allocated at once in one big chunk for
+ * performance reasons
+ */
+ if (fake.bv.bv_val != buf) {
+ slapi_ch_free((void**)&fake.bv.bv_val);
+ }
+
+ /* Some syntax_assertion2keys functions may allocate a whole new
+ * ivals array. Free it if so.
+ */
+ if (ivals != ptr) {
+ slapi_ch_free((void**)&ivals);
+ }
+ } else {
+ slapi_value_init_berval(&sv, bval);
+ ivals=NULL;
+ slapi_call_syntax_assertion2keys_ava_sv( pi, &sv, &ivals, ftype );
+ value_done(&sv);
+ if ( ivals == NULL || *ivals == NULL ) {
+ LDAPDebug( LDAP_DEBUG_TRACE,
+ "<= ava_candidates ALLIDS (no keys)\n", 0, 0, 0 );
+ return( idl_allids( be ) );
+ }
+ idl = keys2idl( be, type, indextype, ivals, err );
+ valuearray_free( &ivals );
+ LDAPDebug( LDAP_DEBUG_TRACE, "<= ava_candidates %lu\n",
+ (u_long)IDL_NIDS(idl), 0, 0 );
+ }
+ return( idl );
+}
+
+static IDList *
+presence_candidates(
+ Slapi_PBlock *pb,
+ backend *be,
+ Slapi_Filter *f,
+ int *err
+)
+{
+ char *type;
+ IDList *idl;
+
+ LDAPDebug( LDAP_DEBUG_TRACE, "=> presence_candidates\n", 0, 0, 0 );
+
+ if ( slapi_filter_get_type( f, &type ) != 0 ) {
+ LDAPDebug( LDAP_DEBUG_ANY, " slapi_filter_get_type failed\n",
+ 0, 0, 0 );
+ return( NULL );
+ }
+ idl = index_read( be, type, indextype_PRESENCE, NULL, NULL, err );
+
+ if (idl != NULL && ALLIDS(idl) && strcasecmp(type, "nscpentrydn") == 0) {
+ /* try the equality index instead */
+ LDAPDebug(LDAP_DEBUG_TRACE,
+ "fallback to eq index as pres index gave allids\n",
+ 0, 0, 0);
+ idl_free(idl);
+ idl = index_range_read(pb, be, type, indextype_EQUALITY,
+ SLAPI_OP_GREATER_OR_EQUAL,
+ NULL, NULL, 0, NULL, err);
+ }
+
+ LDAPDebug( LDAP_DEBUG_TRACE, "<= presence_candidates %lu\n",
+ (u_long)IDL_NIDS(idl), 0, 0 );
+ return( idl );
+}
+
+static IDList *
+extensible_candidates(
+ backend *be,
+ Slapi_Filter *f,
+ int *err
+)
+{
+ IDList* idl = NULL;
+ Slapi_PBlock* pb = slapi_pblock_new();
+ int mrOP = 0;
+ LDAPDebug (LDAP_DEBUG_TRACE, "=> extensible_candidates\n", 0, 0, 0);
+ if ( ! slapi_mr_filter_index (f, pb) && !slapi_pblock_get (pb, SLAPI_PLUGIN_MR_QUERY_OPERATOR, &mrOP))
+ {
+ switch (mrOP)
+ {
+ case SLAPI_OP_LESS:
+ case SLAPI_OP_LESS_OR_EQUAL:
+ case SLAPI_OP_EQUAL:
+ case SLAPI_OP_GREATER_OR_EQUAL:
+ case SLAPI_OP_GREATER:
+ {
+ IFP mrINDEX = NULL;
+ void* mrOBJECT = NULL;
+ struct berval** mrVALUES = NULL;
+ char* mrOID = NULL;
+ char* mrTYPE = NULL;
+
+ slapi_pblock_get (pb, SLAPI_PLUGIN_MR_INDEX_FN, &mrINDEX);
+ slapi_pblock_get (pb, SLAPI_PLUGIN_OBJECT, &mrOBJECT);
+ slapi_pblock_get (pb, SLAPI_PLUGIN_MR_VALUES, &mrVALUES);
+ slapi_pblock_get (pb, SLAPI_PLUGIN_MR_OID, &mrOID);
+ slapi_pblock_get (pb, SLAPI_PLUGIN_MR_TYPE, &mrTYPE);
+
+ if (mrVALUES != NULL && *mrVALUES != NULL)
+ {
+ /*
+ * Compute keys for each of the values, individually.
+ * Search the index, for the computed keys.
+ * Collect the resulting IDs in idl.
+ */
+ size_t n;
+ struct berval** val;
+ mrTYPE = slapi_attr_basetype (mrTYPE, NULL, 0);
+ for (n=0,val=mrVALUES; *val; ++n,++val)
+ {
+ struct berval** keys = NULL;
+ /* keys = mrINDEX (*val), conceptually. In detail: */
+ struct berval* bvec[2];
+ bvec[0] = *val;
+ bvec[1] = NULL;
+ if (slapi_pblock_set (pb, SLAPI_PLUGIN_OBJECT, mrOBJECT) ||
+ slapi_pblock_set (pb, SLAPI_PLUGIN_MR_VALUES, bvec) ||
+ mrINDEX (pb) ||
+ slapi_pblock_get (pb, SLAPI_PLUGIN_MR_KEYS, &keys))
+ {
+ /* something went wrong. bail. */
+ break;
+ }
+ else if (keys == NULL || keys[0] == NULL)
+ {
+ /* no keys */
+ idl_free (idl);
+ idl = idl_allids (be);
+ }
+ else
+ {
+ IDList* idl2= NULL;
+ struct berval** key;
+ for (key = keys; *key != NULL; ++key)
+ {
+ IDList* idl3 = (mrOP == SLAPI_OP_EQUAL) ?
+ index_read (be, mrTYPE, mrOID, *key, NULL, err) :
+ index_range_read (pb, be, mrTYPE, mrOID, mrOP, *key, NULL, 0, NULL, err);
+ if (idl2 == NULL)
+ {
+ /* first iteration */
+ idl2 = idl3;
+ }
+ else
+ {
+ IDList* tmp = idl_intersection (be, idl2, idl3);
+ idl_free (idl2);
+ idl_free (idl3);
+ idl2 = tmp;
+ }
+ if (idl2 == NULL) break; /* look no further */
+ }
+ if (idl == NULL)
+ {
+ idl = idl2;
+ }
+ else if (idl2 != NULL)
+ {
+ IDList* tmp = idl_union (be, idl, idl2);
+ idl_free (idl);
+ idl_free (idl2);
+ idl = tmp;
+ }
+ }
+ }
+ slapi_ch_free((void**)&mrTYPE);
+ goto return_idl; /* possibly no matches */
+ }
+ }
+ break;
+ default:
+ /* unsupported query operator */
+ break;
+ }
+ }
+ if (idl == NULL)
+ {
+ /* this filter isn't indexed */
+ idl = idl_allids (be); /* all entries are candidates */
+ }
+return_idl:
+ slapi_pblock_destroy (pb);
+ LDAPDebug (LDAP_DEBUG_TRACE, "<= extensible_candidates %lu\n",
+ (u_long)IDL_NIDS(idl), 0, 0);
+ return idl;
+}
+
+static int
+slapi_berval_reverse_cmp(const struct berval *a, const struct berval *b)
+{
+ return slapi_berval_cmp(b, a);
+}
+
+static IDList *
+range_candidates(
+ Slapi_PBlock *pb,
+ backend *be,
+ char *type,
+ struct berval *low_val,
+ struct berval *high_val,
+ int *err
+)
+{
+ IDList *idl;
+ struct berval *low = NULL, *high = NULL;
+ struct berval **lows = NULL, **highs = NULL;
+ void *pi;
+
+ LDAPDebug(LDAP_DEBUG_TRACE, "=> range_candidates attr=%s\n", type, 0, 0);
+
+ /*
+ * get the keys corresponding to the assertion values
+ */
+
+ if ( slapi_attr_type2plugin( type, &pi ) != 0 ) {
+ LDAPDebug( LDAP_DEBUG_TRACE, " slapi_filter_get_ava no plugin\n",
+ 0, 0, 0 );
+ return( NULL );
+ }
+
+ if (low_val != NULL) {
+ slapi_call_syntax_assertion2keys_ava(pi, low_val, &lows, LDAP_FILTER_EQUALITY);
+ if (lows == NULL || *lows == NULL) {
+ LDAPDebug( LDAP_DEBUG_TRACE,
+ "<= range_candidates ALLIDS (no keys)\n", 0, 0, 0 );
+ return( idl_allids( be ) );
+ }
+ low = attr_value_lowest(lows, slapi_berval_reverse_cmp);
+ }
+
+ if (high_val != NULL) {
+ slapi_call_syntax_assertion2keys_ava(pi, high_val, &highs, LDAP_FILTER_EQUALITY);
+ if (highs == NULL || *highs == NULL) {
+ LDAPDebug( LDAP_DEBUG_TRACE,
+ "<= range_candidates ALLIDS (no keys)\n", 0, 0, 0 );
+ if (lows) ber_bvecfree(lows);
+ return( idl_allids( be ) );
+ }
+ high = attr_value_lowest(highs, slapi_berval_cmp);
+ }
+
+ if (low == NULL) {
+ idl = index_range_read(pb, be, type, (char*)indextype_EQUALITY,
+ SLAPI_OP_LESS_OR_EQUAL,
+ high, NULL, 0, NULL, err);
+ } else if (high == NULL) {
+ idl = index_range_read(pb, be, type, (char*)indextype_EQUALITY,
+ SLAPI_OP_GREATER_OR_EQUAL,
+ low, NULL, 0, NULL, err);
+ } else {
+ idl = index_range_read(pb, be, type, (char*)indextype_EQUALITY,
+ SLAPI_OP_GREATER_OR_EQUAL,
+ low, high, 1, NULL, err);
+ }
+
+ if (lows) ber_bvecfree(lows);
+ if (highs) ber_bvecfree(highs);
+
+ LDAPDebug( LDAP_DEBUG_TRACE, "<= range_candidates %lu\n",
+ (u_long)IDL_NIDS(idl), 0, 0 );
+
+ return idl;
+}
+
+static IDList *
+list_candidates(
+ Slapi_PBlock *pb,
+ backend *be,
+ const char *base,
+ Slapi_Filter *flist,
+ int ftype,
+ int *err
+)
+{
+ IDList *idl, *tmp, *tmp2;
+ Slapi_Filter *f, *nextf, *f_head;
+ int range = 0;
+ int isnot;
+ int f_count = 0, le_count = 0, ge_count = 0, is_bounded_range = 1;
+ struct berval *low_val = NULL, *high_val = NULL;
+ char *t1;
+ Slapi_Filter *fpairs[2] = {NULL, NULL}; /* low, high */
+ char *tpairs[2] = {NULL, NULL};
+ struct berval *vpairs[2] = {NULL, NULL};
+ int is_and = 0;
+
+ LDAPDebug( LDAP_DEBUG_TRACE, "=> list_candidates 0x%x\n", ftype, 0, 0 );
+
+ /*
+ * Optimize bounded range queries such as (&(cn>=A)(cn<=B)).
+ * Could be better by matching pairs in a longer list
+ * but for now support only a single pair.
+ */
+ if (ftype != LDAP_FILTER_AND)
+ {
+ is_bounded_range = 0;
+ }
+ for ( f = slapi_filter_list_first( flist );
+ f != NULL && is_bounded_range;
+ f = slapi_filter_list_next( flist, f ) ) {
+ f_count++;
+ switch (slapi_filter_get_choice(f)) {
+ case LDAP_FILTER_GE:
+ if ( slapi_filter_get_ava(f, &t1, &low_val) != 0 ) {
+ is_bounded_range = 0;
+ continue;
+ }
+ ge_count++;
+ if (NULL == fpairs[0])
+ {
+ fpairs[0] = f;
+ tpairs[0] = slapi_ch_strdup(t1);
+ vpairs[0] = slapi_ch_bvdup(low_val);
+ }
+ else if (NULL != fpairs[1] &&
+ slapi_attr_type_cmp(tpairs[1], t1, SLAPI_TYPE_CMP_EXACT) != 0)
+ {
+ fpairs[0] = f;
+ slapi_ch_free_string(&tpairs[0]);
+ tpairs[0] = slapi_ch_strdup(t1);
+ slapi_ch_bvfree(&vpairs[0]);
+ vpairs[0] = slapi_ch_bvdup(low_val);
+ }
+ break;
+ case LDAP_FILTER_LE:
+ if ( slapi_filter_get_ava(f, &t1, &high_val) != 0 ) {
+ is_bounded_range = 0;
+ continue;
+ }
+ le_count++;
+ if (NULL == fpairs[1])
+ {
+ fpairs[1] = f;
+ tpairs[1] = slapi_ch_strdup(t1);
+ vpairs[1] = slapi_ch_bvdup(high_val);
+ }
+ else if (NULL != fpairs[0] &&
+ slapi_attr_type_cmp(tpairs[0], t1, SLAPI_TYPE_CMP_EXACT) != 0)
+ {
+ fpairs[1] = f;
+ slapi_ch_free_string(&tpairs[1]);
+ tpairs[1] = slapi_ch_strdup(t1);
+ slapi_ch_bvfree(&vpairs[1]);
+ vpairs[1] = slapi_ch_bvdup(high_val);
+ }
+ break;
+ default:
+ continue;
+ }
+ }
+ if (ftype == LDAP_FILTER_AND && f_count > 1)
+ {
+ is_and = 1;
+ }
+ slapi_pblock_set(pb, SLAPI_SEARCH_IS_AND, &is_and);
+ if (le_count != 1 || ge_count != 1 || f_count != 2)
+ {
+ is_bounded_range = 0;
+ }
+ if (NULL == fpairs[0] || NULL == fpairs[1])
+ {
+ fpairs[0] = fpairs[1] = NULL;
+ slapi_ch_free_string(&tpairs[0]);
+ slapi_ch_bvfree(&vpairs[0]);
+ slapi_ch_free_string(&tpairs[1]);
+ slapi_ch_bvfree(&vpairs[1]);
+ is_bounded_range = 0;
+ }
+ if (is_bounded_range) {
+ idl = range_candidates(pb, be, tpairs[0], vpairs[0], vpairs[1], err);
+ LDAPDebug( LDAP_DEBUG_TRACE, "<= list_candidates %lu\n",
+ (u_long)IDL_NIDS(idl), 0, 0 );
+ goto out;
+ }
+
+ idl = NULL;
+ nextf = NULL;
+ isnot = 0;
+ for ( f_head = f = slapi_filter_list_first( flist ); f != NULL;
+ f = slapi_filter_list_next( flist, f ) ) {
+
+ /* Look for NOT foo type filter elements where foo is simple equality */
+ isnot = (LDAP_FILTER_NOT == slapi_filter_get_choice( f )) &&
+ (LDAP_FILTER_AND == ftype &&
+ (LDAP_FILTER_EQUALITY == slapi_filter_get_choice(slapi_filter_list_first(f))));
+
+ if (isnot) {
+ /* if this is the first filter we have an allid search anyway, so bail */
+ if(f == f_head)
+ {
+ idl = idl_allids( be );
+ break;
+ }
+
+ /* Fetch the IDL for foo */
+ /* Later we'll remember to call idl_notin() */
+ LDAPDebug( LDAP_DEBUG_TRACE,"NOT filter\n", 0, 0, 0 );
+ tmp = ava_candidates( pb, be, slapi_filter_list_first(f), LDAP_FILTER_EQUALITY, nextf, range, err );
+ } else {
+ if (fpairs[0] == f)
+ {
+ continue;
+ }
+ else if (fpairs[1] == f)
+ {
+ tmp = range_candidates(pb, be, tpairs[0],
+ vpairs[0], vpairs[1], err);
+ if (tmp == NULL && ftype == LDAP_FILTER_AND)
+ {
+ LDAPDebug( LDAP_DEBUG_TRACE,
+ "<= list_candidates NULL\n", 0, 0, 0 );
+ idl_free( idl );
+ idl = NULL;
+ goto out;
+ }
+ }
+ /* Proceed as normal */
+ else if ( (tmp = filter_candidates( pb, be, base, f, nextf, range, err ))
+ == NULL && ftype == LDAP_FILTER_AND ) {
+ LDAPDebug( LDAP_DEBUG_TRACE,
+ "<= list_candidates NULL\n", 0, 0, 0 );
+ idl_free( idl );
+ idl = NULL;
+ goto out;
+ }
+ }
+
+ tmp2 = idl;
+ if ( idl == NULL ) {
+ idl = tmp;
+ if ( (ftype == LDAP_FILTER_AND) && ((idl == NULL) ||
+ (idl_length(idl) <= FILTER_TEST_THRESHOLD)))
+ break; /* We can exit the loop now, since the candidate list is small already */
+ } else if ( ftype == LDAP_FILTER_AND ) {
+ if (isnot) {
+ IDList *new_idl = NULL;
+ int notin_result = 0;
+ notin_result = idl_notin( be, idl, tmp, &new_idl );
+ if (notin_result) {
+ idl_free(idl);
+ idl = new_idl;
+ }
+ } else {
+ idl = idl_intersection(be, idl, tmp);
+ idl_free( tmp2 );
+ }
+ idl_free( tmp );
+ /* stop if the list has gotten too small */
+ if ((idl == NULL) ||
+ (idl_length(idl) <= FILTER_TEST_THRESHOLD))
+ break;
+ } else {
+ idl = idl_union( be, idl, tmp );
+ idl_free( tmp );
+ idl_free( tmp2 );
+ /* stop if we're already committed to an exhaustive
+ * search. :(
+ */
+ if (idl_is_allids(idl))
+ break;
+ }
+
+ }
+
+ LDAPDebug( LDAP_DEBUG_TRACE, "<= list_candidates %lu\n",
+ (u_long)IDL_NIDS(idl), 0, 0 );
+out:
+ is_and = 0;
+ slapi_pblock_set(pb, SLAPI_SEARCH_IS_AND, &is_and);
+ slapi_ch_free_string(&tpairs[0]);
+ slapi_ch_bvfree(&vpairs[0]);
+ slapi_ch_free_string(&tpairs[1]);
+ slapi_ch_bvfree(&vpairs[1]);
+ return( idl );
+}
+
+static IDList *
+substring_candidates(
+ Slapi_PBlock *pb,
+ backend *be,
+ Slapi_Filter *f,
+ int *err
+)
+{
+ char *type, *initial, *final;
+ char **any;
+ IDList *idl;
+ void *pi;
+ Slapi_Value **ivals;
+
+ LDAPDebug( LDAP_DEBUG_TRACE, "=> sub_candidates\n", 0, 0, 0 );
+
+ if (slapi_filter_get_subfilt( f, &type, &initial, &any, &final ) != 0) {
+ LDAPDebug( LDAP_DEBUG_ANY, " slapi_filter_get_subfilt fails\n",
+ 0, 0, 0 );
+ return( NULL );
+ }
+
+ /*
+ * get the index keys corresponding to the substring
+ * assertion values
+ */
+ if ( slapi_attr_type2plugin( type, &pi ) != 0 ) {
+ LDAPDebug( LDAP_DEBUG_TRACE, " sub_candidates no plugin\n",
+ 0, 0, 0 );
+ return( NULL );
+ }
+ slapi_call_syntax_assertion2keys_sub_sv( pi, initial, any, final, &ivals );
+ if ( ivals == NULL || *ivals == NULL ) {
+ LDAPDebug( LDAP_DEBUG_TRACE,
+ "<= sub_candidates ALLIDS (no keys)\n", 0, 0, 0 );
+ return( idl_allids( be ) );
+ }
+
+ /*
+ * look up each key in the index, ANDing the resulting
+ * IDLists together.
+ */
+ idl = keys2idl( be, type, indextype_SUB, ivals, err );
+ valuearray_free( &ivals );
+
+ LDAPDebug( LDAP_DEBUG_TRACE, "<= sub_candidates %lu\n",
+ (u_long)IDL_NIDS(idl), 0, 0 );
+ return( idl );
+}
+
+static IDList *
+keys2idl(
+ backend *be,
+ char *type,
+ const char *indextype,
+ Slapi_Value **ivals,
+ int *err
+)
+{
+ IDList *idl;
+ int i;
+
+ LDAPDebug( LDAP_DEBUG_TRACE, "=> keys2idl type %s indextype %s\n",
+ type, indextype, 0 );
+ idl = NULL;
+ for ( i = 0; ivals[i] != NULL; i++ ) {
+ IDList *idl2;
+
+ idl2 = index_read( be, type, indextype, slapi_value_get_berval(ivals[i]), NULL, err );
+
+#ifdef LDAP_DEBUG
+ /* XXX if ( slapd_ldap_debug & LDAP_DEBUG_TRACE ) { XXX */
+ {
+ char buf[BUFSIZ];
+
+ LDAPDebug( LDAP_DEBUG_TRACE,
+ " ival[%d] = \"%s\" => %lu IDs\n", i,
+ encode( slapi_value_get_berval(ivals[i]), buf ), (u_long)IDL_NIDS(idl2) );
+ }
+#endif
+ if ( idl2 == NULL ) {
+ idl_free( idl );
+ idl = NULL;
+ break;
+ }
+
+ if (idl == NULL) {
+ idl = idl2;
+ } else {
+ IDList *tmp;
+
+ tmp = idl;
+ idl = idl_intersection(be, idl, idl2);
+ idl_free( idl2 );
+ idl_free( tmp );
+ if ( idl == NULL ) {
+ break;
+ }
+ }
+ }
+
+ return( idl );
+}
diff --git a/ldap/servers/slapd/back-ldbm/findentry.c b/ldap/servers/slapd/back-ldbm/findentry.c
new file mode 100644
index 00000000..6565b279
--- /dev/null
+++ b/ldap/servers/slapd/back-ldbm/findentry.c
@@ -0,0 +1,284 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+/* findentry.c - find a database entry, obeying referrals (& aliases?) */
+
+#include "back-ldbm.h"
+
+int
+check_entry_for_referral(Slapi_PBlock *pb, Slapi_Entry *entry, char *matched, const char *callingfn) /* JCM - Move somewhere more appropriate */
+{
+ int rc=0, i=0, numValues=0;
+ Slapi_Attr *attr;
+
+ /* if the entry is a referral send the referral */
+ if ( slapi_entry_attr_find( entry, "ref", &attr ) == 0 )
+ {
+ Slapi_Value *val=NULL;
+ struct berval **refscopy=NULL;
+ struct berval **url=NULL;
+ slapi_attr_get_numvalues(attr, &numValues );
+ if(numValues > 0) {
+ url=(struct berval **) slapi_ch_malloc((numValues + 1) * sizeof(struct berval*));
+ }
+ for (i = slapi_attr_first_value(attr, &val); i != -1;
+ i = slapi_attr_next_value(attr, i, &val)) {
+ url[i]=(struct berval*)slapi_value_get_berval(val);
+ }
+ url[numValues]=NULL;
+ refscopy = ref_adjust( pb, url, slapi_entry_get_sdn(entry), 0 ); /* JCM - What's this PBlock* for? */
+ slapi_send_ldap_result( pb, LDAP_REFERRAL, matched, NULL, 0, refscopy );
+ LDAPDebug( LDAP_DEBUG_TRACE,
+ "<= %s sent referral to (%s) for (%s)\n",
+ callingfn,
+ refscopy ? refscopy[0]->bv_val : "",
+ slapi_entry_get_dn(entry));
+ if ( refscopy != NULL )
+ {
+ ber_bvecfree( refscopy );
+ }
+ if( url != NULL) {
+ slapi_ch_free( (void **)&url );
+ }
+ rc= 1;
+ }
+ return rc;
+}
+
+static struct backentry *
+find_entry_internal_dn(
+ Slapi_PBlock *pb,
+ backend *be,
+ const Slapi_DN *sdn,
+ int lock,
+ back_txn *txn,
+ int really_internal
+)
+{
+ struct backentry *e;
+ int managedsait = 0;
+ int err;
+ ldbm_instance *inst = (ldbm_instance *) be->be_instance_info;
+ size_t tries = 0;
+
+ /* get the managedsait ldap message control */
+ slapi_pblock_get( pb, SLAPI_MANAGEDSAIT, &managedsait );
+
+ while ( (tries < LDBM_CACHE_RETRY_COUNT) &&
+ (e = dn2entry( be, sdn, txn, &err )) != NULL )
+ {
+ /*
+ * we found the entry. if the managedsait control is set,
+ * we return the entry. if managedsait is not set, we check
+ * for the presence of a ref attribute, returning to the
+ * client a referral to the ref'ed entry if a ref is present,
+ * returning the entry to the caller if not.
+ */
+ if ( !managedsait && !really_internal) {
+ /* see if the entry is a referral */
+ if(check_entry_for_referral(pb, e->ep_entry, NULL, "find_entry_internal_dn"))
+ {
+ cache_return( &inst->inst_cache, &e );
+ return( NULL );
+ }
+ }
+
+ /*
+ * we'd like to return the entry. lock it if requested,
+ * retrying if necessary.
+ */
+
+ /* wait for entry modify lock */
+ if ( !lock || cache_lock_entry( &inst->inst_cache, e ) == 0 ) {
+ LDAPDebug( LDAP_DEBUG_TRACE,
+ "<= find_entry_internal_dn found (%s)\n", slapi_sdn_get_dn(sdn), 0, 0 );
+ return( e );
+ }
+ /*
+ * this entry has been deleted - see if it was actually
+ * replaced with a new copy, and try the whole thing again.
+ */
+ LDAPDebug( LDAP_DEBUG_ARGS,
+ " find_entry_internal_dn retrying (%s)\n", slapi_sdn_get_dn(sdn), 0, 0 );
+ cache_return( &inst->inst_cache, &e );
+ tries++;
+ }
+ if (tries >= LDBM_CACHE_RETRY_COUNT) {
+ LDAPDebug( LDAP_DEBUG_ANY,"find_entry_internal_dn retry count exceeded (%s)\n", slapi_sdn_get_dn(sdn), 0, 0 );
+ }
+ /*
+ * there is no such entry in this server. see how far we
+ * can match, and check if that entry contains a referral.
+ * if it does and managedsait is not set, we return the
+ * referral to the client. if it doesn't, or managedsait
+ * is set, we return no such object.
+ */
+ if (!really_internal) {
+ struct backentry *me;
+ Slapi_DN ancestordn= {0};
+ me= dn2ancestor(pb->pb_backend,sdn,&ancestordn,txn,&err);
+ if ( !managedsait && me != NULL ) {
+ /* if the entry is a referral send the referral */
+ if(check_entry_for_referral(pb, me->ep_entry, (char*)slapi_sdn_get_dn(&ancestordn), "find_entry_internal_dn"))
+ {
+ cache_return( &inst->inst_cache, &me );
+ slapi_sdn_done(&ancestordn);
+ return( NULL );
+ }
+ /* else fall through to no such object */
+ }
+
+ /* entry not found */
+ slapi_send_ldap_result( pb, ( 0 == err || DB_NOTFOUND == err ) ?
+ LDAP_NO_SUCH_OBJECT : LDAP_OPERATIONS_ERROR, (char*)slapi_sdn_get_dn(&ancestordn), NULL,
+ 0, NULL );
+ slapi_sdn_done(&ancestordn);
+ cache_return( &inst->inst_cache, &me );
+ }
+
+ LDAPDebug( LDAP_DEBUG_TRACE, "<= find_entry_internal_dn not found (%s)\n",
+ slapi_sdn_get_dn(sdn), 0, 0 );
+ return( NULL );
+}
+
+/* Note that this function does not issue any referals.
+ It should only be called in case of 5.0 replicated operation
+ which should not be referred.
+ */
+static struct backentry *
+find_entry_internal_uniqueid(
+ Slapi_PBlock *pb,
+ backend *be,
+ const char *uniqueid,
+ int lock,
+ back_txn *txn
+)
+{
+ ldbm_instance *inst = (ldbm_instance *) be->be_instance_info;
+ struct backentry *e;
+ int err;
+ size_t tries = 0;
+
+ while ( (tries < LDBM_CACHE_RETRY_COUNT) &&
+ (e = uniqueid2entry(be, uniqueid, txn, &err ))
+ != NULL ) {
+
+ /*
+ * we'd like to return the entry. lock it if requested,
+ * retrying if necessary.
+ */
+
+ /* wait for entry modify lock */
+ if ( !lock || cache_lock_entry( &inst->inst_cache, e ) == 0 ) {
+ LDAPDebug( LDAP_DEBUG_TRACE,
+ "<= find_entry_internal_uniqueid found; uniqueid = (%s)\n",
+ uniqueid, 0, 0 );
+ return( e );
+ }
+ /*
+ * this entry has been deleted - see if it was actually
+ * replaced with a new copy, and try the whole thing again.
+ */
+ LDAPDebug( LDAP_DEBUG_ARGS,
+ " find_entry_internal_uniqueid retrying; uniqueid = (%s)\n",
+ uniqueid, 0, 0 );
+ cache_return( &inst->inst_cache, &e );
+ tries++;
+ }
+ if (tries >= LDBM_CACHE_RETRY_COUNT) {
+ LDAPDebug( LDAP_DEBUG_ANY,
+ "find_entry_internal_uniqueid retry count exceeded; uniqueid = (%s)\n",
+ uniqueid , 0, 0 );
+ }
+
+ /* entry not found */
+ slapi_send_ldap_result( pb, ( 0 == err || DB_NOTFOUND == err ) ?
+ LDAP_NO_SUCH_OBJECT : LDAP_OPERATIONS_ERROR, NULL /* matched */, NULL,
+ 0, NULL );
+ LDAPDebug( LDAP_DEBUG_TRACE,
+ "<= find_entry_internal not found; uniqueid = (%s)\n",
+ uniqueid, 0, 0 );
+ return( NULL );
+}
+
+static struct backentry *
+find_entry_internal(
+ Slapi_PBlock *pb,
+ Slapi_Backend *be,
+ const entry_address *addr,
+ int lock,
+ back_txn *txn,
+ int really_internal
+)
+{
+ /* check if we should search based on uniqueid or dn */
+ if (addr->uniqueid!=NULL)
+ {
+ LDAPDebug( LDAP_DEBUG_TRACE, "=> find_entry_internal (uniqueid=%s) lock %d\n",
+ addr->uniqueid, lock, 0 );
+ return (find_entry_internal_uniqueid (pb, be, addr->uniqueid, lock, txn));
+ }
+ else
+ {
+ Slapi_DN sdn;
+ struct backentry *entry;
+
+ slapi_sdn_init_dn_ndn_byref (&sdn, addr->dn); /* normalized by front end */
+ LDAPDebug( LDAP_DEBUG_TRACE, "=> find_entry_internal (dn=%s) lock %d\n",
+ addr->dn, lock, 0 );
+ entry = find_entry_internal_dn (pb, be, &sdn, lock, txn, really_internal);
+ slapi_sdn_done (&sdn);
+ return entry;
+ }
+
+}
+
+struct backentry *
+find_entry(
+ Slapi_PBlock *pb,
+ Slapi_Backend *be,
+ const entry_address *addr,
+ back_txn *txn
+)
+{
+ return( find_entry_internal( pb, be, addr, 0/*!lock*/, txn, 0/*!really_internal*/ ) );
+}
+
+struct backentry *
+find_entry2modify(
+ Slapi_PBlock *pb,
+ Slapi_Backend *be,
+ const entry_address *addr,
+ back_txn *txn
+)
+{
+ return( find_entry_internal( pb, be, addr, 1/*lock*/, txn, 0/*!really_internal*/ ) );
+}
+
+/* New routines which do not do any referral stuff.
+ Call these if all you want to do is get pointer to an entry
+ and certainly do not want any side-effects relating to client ! */
+
+struct backentry *
+find_entry_only(
+ Slapi_PBlock *pb,
+ Slapi_Backend *be,
+ const entry_address *addr,
+ back_txn *txn
+)
+{
+ return( find_entry_internal( pb, be, addr, 0/*!lock*/, txn, 1/*really_internal*/ ) );
+}
+
+struct backentry *
+find_entry2modify_only(
+ Slapi_PBlock *pb,
+ Slapi_Backend *be,
+ const entry_address *addr,
+ back_txn *txn
+)
+{
+ return( find_entry_internal( pb, be, addr, 1/*lock*/, txn, 1/*really_internal*/ ) );
+}
diff --git a/ldap/servers/slapd/back-ldbm/haschildren.c b/ldap/servers/slapd/back-ldbm/haschildren.c
new file mode 100644
index 00000000..6e2fa9d9
--- /dev/null
+++ b/ldap/servers/slapd/back-ldbm/haschildren.c
@@ -0,0 +1,8 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+/* haschildren.c - tell if an entry has kids or not */
+
+
diff --git a/ldap/servers/slapd/back-ldbm/id2entry.c b/ldap/servers/slapd/back-ldbm/id2entry.c
new file mode 100644
index 00000000..7c5a00c2
--- /dev/null
+++ b/ldap/servers/slapd/back-ldbm/id2entry.c
@@ -0,0 +1,250 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+/* id2entry.c - routines to deal with the id2entry index */
+
+#include "back-ldbm.h"
+
+/*
+ * The caller MUST check for DB_LOCK_DEADLOCK and DB_RUNRECOVERY returned
+ */
+int
+id2entry_add_ext( backend *be, struct backentry *e, back_txn *txn, int encrypt )
+{
+ struct ldbminfo *li = (struct ldbminfo *) be->be_database->plg_private;
+ ldbm_instance *inst = (ldbm_instance *) be->be_instance_info;
+ DB *db = NULL;
+ DB_TXN *db_txn = NULL;
+ DBT data = {0};
+ DBT key = {0};
+ int len, rc;
+ char temp_id[sizeof(ID)];
+ struct backentry *encrypted_entry = NULL;
+
+ LDAPDebug( LDAP_DEBUG_TRACE, "=> id2entry_add( %lu, \"%s\" )\n",
+ (u_long)e->ep_id, backentry_get_ndn(e), 0 );
+
+ if ( (rc = dblayer_get_id2entry( be, &db )) != 0 ) {
+ LDAPDebug( LDAP_DEBUG_ANY, "Could not open/create id2entry\n",
+ 0, 0, 0 );
+ return( -1 );
+ }
+
+ id_internal_to_stored(e->ep_id,temp_id);
+
+ key.dptr = temp_id;
+ key.dsize = sizeof(temp_id);
+
+ /* Encrypt attributes in this entry if necessary */
+ if (encrypt) {
+ rc = attrcrypt_encrypt_entry(be, e, &encrypted_entry);
+ if (rc) {
+ LDAPDebug( LDAP_DEBUG_ANY, "attrcrypt_encrypt_entry failed in id2entry_add\n",
+ 0, 0, 0 );
+ return ( -1 );
+ }
+ }
+
+ {
+ Slapi_Entry *entry_to_use = encrypted_entry ? encrypted_entry->ep_entry : e->ep_entry;
+ data.dptr = slapi_entry2str_with_options( entry_to_use, &len, SLAPI_DUMP_STATEINFO | SLAPI_DUMP_UNIQUEID);
+ data.dsize = len + 1;
+ /* If we had an encrypted entry, we no longer need it */
+ if (encrypted_entry) {
+ backentry_free(&encrypted_entry);
+ }
+ }
+
+ if (NULL != txn) {
+ db_txn = txn->back_txn_txn;
+ }
+
+ /* call pre-entry-store plugin */
+ plugin_call_entrystore_plugins( (char **) &data.dptr, &data.dsize );
+
+ /* store it */
+ rc = db->put( db, db_txn, &key, &data, 0);
+ /* DBDB looks like we're freeing memory allocated by another DLL, which is bad */
+ free( data.dptr );
+
+ dblayer_release_id2entry( be, db );
+
+ if (0 == rc)
+ {
+ /* DBDB the fact that we don't check the return code here is
+ * indicitive that there may be a latent race condition lurking
+ * ---what happens if the entry is already in the cache by this point?
+ */
+ /*
+ * For ldbm_back_add and ldbm_back_modify, this entry had been already
+ * reserved as a tentative entry. So, it should be safe.
+ * For ldbm_back_modify, the original entry having the same dn/id
+ * should be in the cache. Thus, this entry e won't be put into the
+ * entry cache. It'll be added by cache_replace.
+ */
+ (void) cache_add( &inst->inst_cache, e, NULL );
+ }
+
+ LDAPDebug( LDAP_DEBUG_TRACE, "<= id2entry_add %d\n", rc, 0, 0 );
+ return( rc );
+}
+
+int
+id2entry_add( backend *be, struct backentry *e, back_txn *txn )
+{
+ return id2entry_add_ext(be,e,txn,1);
+}
+
+/*
+ * The caller MUST check for DB_LOCK_DEADLOCK and DB_RUNRECOVERY returned
+ */
+int
+id2entry_delete( backend *be, struct backentry *e, back_txn *txn )
+{
+ DB *db = NULL;
+ DB_TXN *db_txn = NULL;
+ DBT key = {0};
+ int rc;
+ char temp_id[sizeof(ID)];
+
+ LDAPDebug( LDAP_DEBUG_TRACE, "=> id2entry_delete( %lu, \"%s\" )\n",
+ (u_long)e->ep_id, backentry_get_ndn(e), 0 );
+
+ if ( (rc = dblayer_get_id2entry( be, &db )) != 0 ) {
+ LDAPDebug( LDAP_DEBUG_ANY, "Could not open/create id2entry\n",
+ 0, 0, 0 );
+ return( -1 );
+ }
+
+ id_internal_to_stored(e->ep_id,temp_id);
+
+ key.dptr = temp_id;
+ key.dsize = sizeof(temp_id);
+
+ if (NULL != txn) {
+ db_txn = txn->back_txn_txn;
+ }
+
+ rc = db->del( db,db_txn,&key,0 );
+ dblayer_release_id2entry( be, db );
+
+ LDAPDebug( LDAP_DEBUG_TRACE, "<= id2entry_delete %d\n", rc, 0, 0 );
+ return( rc );
+}
+
+struct backentry *
+id2entry( backend *be, ID id, back_txn *txn, int *err )
+{
+ struct ldbminfo *li = (struct ldbminfo *) be->be_database->plg_private;
+ ldbm_instance *inst = (ldbm_instance *) be->be_instance_info;
+ DB *db = NULL;
+ DB_TXN *db_txn = NULL;
+ DBT key = {0};
+ DBT data = {0};
+ struct backentry *e;
+ Slapi_Entry *ee;
+ char temp_id[sizeof(ID)];
+
+ LDAPDebug( LDAP_DEBUG_TRACE, "=> id2entry( %lu )\n", (u_long)id, 0, 0 );
+
+ if ( (e = cache_find_id( &inst->inst_cache, id )) != NULL ) {
+ LDAPDebug( LDAP_DEBUG_TRACE, "<= id2entry %p (cache)\n", e, 0,
+ 0 );
+ return( e );
+ }
+
+ if ( (*err = dblayer_get_id2entry( be, &db )) != 0 ) {
+ LDAPDebug( LDAP_DEBUG_ANY, "Could not open id2entry err %d\n",
+ *err, 0, 0 );
+ return( NULL );
+ }
+
+
+ id_internal_to_stored(id,temp_id);
+
+ key.data = temp_id;
+ key.size = sizeof(temp_id);
+
+ /* DBDB need to improve this, we're mallocing, freeing, all over the place here */
+ data.flags = DB_DBT_MALLOC;
+
+ if (NULL != txn) {
+ db_txn = txn->back_txn_txn;
+ }
+ do {
+ *err = db->get( db, db_txn, &key, &data, 0 );
+ if ( 0 != *err &&
+ DB_NOTFOUND != *err && DB_LOCK_DEADLOCK != *err )
+ {
+ LDAPDebug( LDAP_DEBUG_ANY, "id2entry error %d\n",
+ *err, 0, 0 );
+ }
+ }
+ while ( DB_LOCK_DEADLOCK == *err && txn == NULL );
+
+ if ( 0 != *err && DB_NOTFOUND != *err && DB_LOCK_DEADLOCK != *err )
+ {
+ LDAPDebug( LDAP_DEBUG_ANY, "id2entry get error %d\n",
+ *err, 0, 0 );
+ dblayer_release_id2entry( be, db );
+ return( NULL );
+ }
+
+ if ( data.dptr == NULL ) {
+ LDAPDebug( LDAP_DEBUG_TRACE, "<= id2entry( %lu ) not found\n",
+ (u_long)id, 0, 0 );
+ dblayer_release_id2entry( be, db );
+ return( NULL );
+ }
+
+ /* call post-entry plugin */
+ plugin_call_entryfetch_plugins( (char **) &data.dptr, &data.dsize );
+
+ if ( (ee = slapi_str2entry( data.dptr, 0 )) != NULL ) {
+ int retval = 0;
+ struct backentry *imposter = NULL;
+
+ PR_ASSERT(slapi_entry_get_uniqueid(ee) != NULL); /* All entries should have uniqueids */
+ e = backentry_init( ee ); /* ownership of the entry is passed into the backentry */
+ e->ep_id = id;
+
+ /* Decrypt any encrypted attributes in this entry, before adding it to the cache */
+ retval = attrcrypt_decrypt_entry(be, e);
+ if (retval) {
+ LDAPDebug( LDAP_DEBUG_ANY, "attrcrypt_decrypt_entry failed in id2entry\n",
+ 0, 0, 0 );
+ }
+
+ retval = cache_add( &inst->inst_cache, e, &imposter );
+ if (1 == retval) {
+ /* This means that someone else put the entry in the cache
+ while we weren't looking ! So, we need to use the pointer
+ returned and free the one we made earlier */
+ if (imposter)
+ {
+ backentry_free(&e);
+ e = imposter;
+ }
+ } else if (-1 == retval) {
+ /* the entry is in idtable but not in dntable, i.e., the entry
+ * could have been renamed */
+ LDAPDebug( LDAP_DEBUG_TRACE,
+ "id2entry: failed to put entry (id %lu, dn %s) into entry cache\n",
+ (u_long)id, backentry_get_ndn(e), 0 );
+ }
+ } else {
+ LDAPDebug( LDAP_DEBUG_ANY, "str2entry returned NULL for id %lu, string=\"%s\"\n", (u_long)id, (char*)data.data, 0);
+ e = NULL;
+ }
+
+ free( data.data );
+
+ dblayer_release_id2entry( be, db );
+
+ LDAPDebug( LDAP_DEBUG_TRACE, "<= id2entry( %lu ) %p (disk)\n", (u_long)id, e,
+ 0 );
+ return( e );
+}
+
diff --git a/ldap/servers/slapd/back-ldbm/idl.c b/ldap/servers/slapd/back-ldbm/idl.c
new file mode 100644
index 00000000..df97a979
--- /dev/null
+++ b/ldap/servers/slapd/back-ldbm/idl.c
@@ -0,0 +1,1599 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+/* idl.c - ldap id list handling routines */
+
+#include "back-ldbm.h"
+
+/*
+ * Disable idl locking since it causes unbreakable deadlock.
+ */
+#undef IDL_LOCKING_ENABLE
+
+static int idl_delete( IDList **idl, ID id ) ;
+static void make_cont_key( DBT *contkey, DBT *key, ID id );
+static int idl_insert_maxids( IDList **idl, ID id, int maxids );
+
+/* for the cache of open index files */
+struct idl_private {
+ int idl_maxids; /* Number of IDS in a block */
+ int idl_maxindirect; /* Number of blocks allowed */
+ size_t idl_allidslimit; /* Max number of IDs before it turns to allids */
+#ifdef IDL_LOCKING_ENABLE
+ PRRWLock *idl_rwlock;
+#endif
+};
+
+static int idl_tune = DEFAULT_IDL_TUNE; /* tuning parameters for IDL code */
+#define IDL_TUNE_BSEARCH 1 /* do a binary search when inserting into an IDL */
+#define IDL_TUNE_NOPAD 2 /* Don't pad IDLs with space at the end */
+
+void idl_old_set_tune(int val)
+{
+ idl_tune = val;
+}
+
+int idl_old_get_tune() {
+ return idl_tune;
+}
+
+size_t idl_old_get_allidslimit(struct attrinfo *a)
+{
+ idl_private *priv = NULL;
+
+ PR_ASSERT(NULL != a);
+ PR_ASSERT(NULL != a->ai_idl);
+
+ priv = a->ai_idl;
+
+ return priv->idl_allidslimit;
+}
+
+static void idl_init_maxids(struct ldbminfo *li,idl_private *priv)
+{
+ const size_t blksize = dblayer_get_optimal_block_size(li);
+
+ if (0 == li->li_allidsthreshold) {
+ li->li_allidsthreshold = DEFAULT_ALLIDSTHRESHOLD;
+ }
+ priv->idl_maxids = (blksize / sizeof(ID)) - 2;
+ priv->idl_maxindirect = (li->li_allidsthreshold / priv->idl_maxids) + 1;
+ priv->idl_allidslimit = (priv->idl_maxids * priv->idl_maxindirect);
+ LDAPDebug (LDAP_DEBUG_ARGS,
+ "idl_init_private: blksize %lu, maxids %i, maxindirect %i\n",
+ (unsigned long)blksize, priv->idl_maxids, priv->idl_maxindirect);
+}
+
+/* routine to initialize the private data used by the IDL code per-attribute */
+int idl_old_init_private(backend *be,struct attrinfo *a)
+{
+ idl_private *priv = NULL;
+
+ PR_ASSERT(NULL != a);
+ PR_ASSERT(NULL == a->ai_idl);
+
+ priv = (idl_private*) slapi_ch_malloc(sizeof(idl_private));
+ if (NULL == priv) {
+ return -1; /* Memory allocation failure */
+ }
+ {
+ priv->idl_maxids = 0;
+ priv->idl_maxindirect = 0;
+ }
+#ifdef IDL_LOCKING_ENABLE
+ priv->idl_rwlock = PR_NewRWLock(PR_RWLOCK_RANK_NONE, "idl lock");
+
+ if (NULL == priv->idl_rwlock) {
+ slapi_ch_free((void**)&priv);
+ return -1;
+ }
+#endif
+ a->ai_idl = (void*)priv;
+ return 0;
+}
+
+/* routine to release resources used by IDL private data structure */
+int idl_old_release_private(struct attrinfo *a)
+{
+ PR_ASSERT(NULL != a);
+ if (NULL != a->ai_idl)
+ {
+#ifdef IDL_LOCKING_ENABLE
+ idl_private *priv = a->ai_idl;
+ PR_ASSERT(NULL != priv->idl_rwlock);
+ PR_DestroyRWLock(priv->idl_rwlock);
+#endif
+ free( a->ai_idl );
+ }
+ return 0;
+}
+
+/* Locks one IDL so we can modify it knowing that
+ * nobody else is trying to do so at the same time
+ * also called by readers, since they need to be blocked
+ * when they read to avoid them seeing inconsistent data
+ * This is not really necessary for update operations
+ * today because they are already serialized by a lock
+ * at the backend level but is still necessary to
+ * stop concurrent access by one update thread and
+ * some other search threads
+ */
+
+#ifdef IDL_LOCKING_ENABLE
+static void idl_Wlock_list(idl_private *priv, DBT *key)
+{
+ PRRWLock *lock = NULL;
+
+ PR_ASSERT(NULL != priv);
+ lock = priv->idl_rwlock;
+ PR_ASSERT(NULL != lock);
+
+ PR_RWLock_Wlock(lock);
+}
+
+static void idl_Rlock_list(idl_private *priv, DBT *key)
+{
+ PRRWLock *lock = NULL;
+
+ PR_ASSERT(NULL != priv);
+ lock = priv->idl_rwlock;
+ PR_ASSERT(NULL != lock);
+
+ PR_RWLock_Rlock(lock);
+}
+
+static void idl_unlock_list(idl_private *priv, DBT *key)
+{
+ PRRWLock *lock = NULL;
+
+ PR_ASSERT(NULL != priv);
+ lock = priv->idl_rwlock;
+ PR_ASSERT(NULL != lock);
+
+ PR_RWLock_Unlock(lock);
+}
+#endif
+
+#ifndef IDL_LOCKING_ENABLE
+#define idl_Wlock_list(idl,dbt)
+#define idl_Rlock_list(idl,dbt)
+#define idl_unlock_list(idl,dbt)
+#endif
+
+/*
+ * idl_fetch_one - fetch a single IDList from the database and return a
+ * pointer to it.
+ *
+ * this routine always propagates errors other than DB_LOCK_DEADLOCK.
+ * for DB_LOCK_DEADLOCK, it propagates the error if called inside a
+ * transaction. if called not inside a transaction, it loops on
+ * DB_LOCK_DEADLOCK, retrying the fetch.
+ *
+ */
+static IDList *
+idl_fetch_one(
+ struct ldbminfo *li,
+ DB *db,
+ DBT *key,
+ DB_TXN *txn,
+ int *err
+)
+{
+ DBT data = {0};
+ IDList *idl = NULL;
+
+ /* LDAPDebug( LDAP_DEBUG_TRACE, "=> idl_fetch_one\n", 0, 0, 0 ); */
+
+ data.flags = DB_DBT_MALLOC;
+
+ do {
+ *err = db->get( db, txn, key, &data, 0 );
+ if ( 0 != *err && DB_NOTFOUND != *err && DB_LOCK_DEADLOCK != *err )
+ {
+ char *msg;
+ if ( EPERM == *err && *err != errno ) {
+ LDAPDebug( LDAP_DEBUG_ANY,
+ "idl_fetch_one(%s): Database failed to run, "
+ "There is either insufficient disk space or "
+ "insufficient memory available for database.\n",
+ ((char*)key->dptr)[ key->dsize - 1 ] ?
+ "" : (char*)key->dptr, 0, 0 );
+ } else {
+ LDAPDebug( LDAP_DEBUG_ANY,
+ "idl_fetch_one error %d %s\n",
+ *err, (msg = dblayer_strerror( *err )) ? msg : "", 0 );
+ }
+ }
+ }
+ while ( DB_LOCK_DEADLOCK == *err && NULL == txn );
+
+ if (0 == *err) {
+ idl = (IDList *) data.data;
+ }
+
+ return( idl );
+}
+
+IDList *
+idl_old_fetch(
+ backend *be,
+ DB *db,
+ DBT *key,
+ DB_TXN *txn,
+ struct attrinfo *a,
+ int *err
+)
+{
+ struct ldbminfo *li = (struct ldbminfo *) be->be_database->plg_private;
+ DBT k2 = {0};
+ IDList *idl;
+ IDList **tmp;
+ back_txn s_txn;
+ char *kstr;
+ int i;
+ unsigned long nids;
+
+ /* LDAPDebug( LDAP_DEBUG_TRACE, "=> idl_fetch\n", 0, 0, 0 ); */
+ if ( (idl = idl_fetch_one( li, db, key, txn, err )) == NULL ) {
+ return( NULL );
+ }
+
+ /* regular block */
+ if ( ! INDIRECT_BLOCK( idl ) ) {
+ /* make sure we have the current value of highest id */
+ if ( ALLIDS(idl) ) {
+ idl_free( idl );
+ idl = idl_allids( be );
+ }
+ return( idl );
+ }
+ idl_free( idl );
+
+ /* Taking a transaction is expensive; so we try and optimize for the common case by not
+ taking one above. If we have a indirect block; we need to take a transaction and re-read
+ the idl since they could have been changed by another thread after we read the first block
+ above */
+
+ dblayer_txn_init(li,&s_txn);
+ if (NULL != txn)
+ {
+ dblayer_read_txn_begin(li,txn,&s_txn);
+ }
+ if ( (idl = idl_fetch_one( li, db, key, s_txn.back_txn_txn, err )) == NULL ) {
+ dblayer_read_txn_commit(li,&s_txn);
+ return( NULL );
+ }
+
+ /* regular block */
+ if ( ! INDIRECT_BLOCK( idl ) ) {
+ dblayer_read_txn_commit(li,&s_txn);
+ /* make sure we have the current value of highest id */
+ if ( ALLIDS(idl) ) {
+ idl_free( idl );
+ idl = idl_allids( be );
+ }
+ return( idl );
+ }
+ /*
+ * this is an indirect block which points to other blocks.
+ * we need to read in all the blocks it points to and construct
+ * a big id list containing all the ids, which we will return.
+ */
+
+ /* count the number of blocks & allocate space for pointers to them */
+ for ( i = 0; idl->b_ids[i] != NOID; i++ )
+ ; /* NULL */
+ tmp = (IDList **) slapi_ch_malloc( (i + 1) * sizeof(IDList *) );
+
+ /* read in all the blocks */
+ kstr = (char *) slapi_ch_malloc( key->dsize + 20 );
+ nids = 0;
+ for ( i = 0; idl->b_ids[i] != NOID; i++ ) {
+ ID thisID = idl->b_ids[i];
+ ID nextID = idl->b_ids[i+1];
+
+ sprintf( kstr, "%c%s%lu", CONT_PREFIX, (char *)key->dptr, (u_long)thisID );
+ k2.dptr = kstr;
+ k2.dsize = strlen( kstr ) + 1;
+
+ if ( (tmp[i] = idl_fetch_one( li, db, &k2, s_txn.back_txn_txn, err )) == NULL ) {
+ if(*err == DB_LOCK_DEADLOCK) {
+ dblayer_read_txn_abort(li,&s_txn);
+ } else {
+ dblayer_read_txn_commit(li,&s_txn);
+ }
+ slapi_ch_free((void**)&kstr );
+ slapi_ch_free((void**)&tmp );
+ return( NULL );
+ }
+
+ nids += tmp[i]->b_nids;
+
+ /* Check for inconsistencies: */
+ if ( tmp[i]->b_ids[0] != thisID ) {
+ LDAPDebug (LDAP_DEBUG_ANY, "idl_fetch_one(%s)->b_ids[0] == %lu\n",
+ k2.dptr, (u_long)tmp[i]->b_ids[0], 0);
+ }
+ if ( nextID != NOID ) {
+ if ( nextID <= thisID ) {
+ LDAPDebug (LDAP_DEBUG_ANY, "indirect block (%s) contains %lu, %lu\n",
+ key->dptr, (u_long)thisID, (u_long)nextID);
+ }
+ if ( nextID <= tmp[i]->b_ids[(tmp[i]->b_nids)-1] ) {
+ LDAPDebug (LDAP_DEBUG_ANY, "idl_fetch_one(%s)->b_ids[last] == %lu"
+ " >= %lu (next indirect ID)\n",
+ k2.dptr, (u_long)tmp[i]->b_ids[(tmp[i]->b_nids)-1], (u_long)nextID);
+ }
+ }
+ }
+ dblayer_read_txn_commit(li,&s_txn);
+ tmp[i] = NULL;
+ slapi_ch_free((void**)&kstr );
+ idl_free( idl );
+
+ /* allocate space for the big block */
+ idl = idl_alloc( nids );
+ idl->b_nids = nids;
+ nids = 0;
+
+ /* copy in all the ids from the component blocks */
+ for ( i = 0; tmp[i] != NULL; i++ ) {
+ if ( tmp[i] == NULL ) {
+ continue;
+ }
+
+ SAFEMEMCPY( (char *) &idl->b_ids[nids], (char *) tmp[i]->b_ids,
+ tmp[i]->b_nids * sizeof(ID) );
+ nids += tmp[i]->b_nids;
+
+ idl_free( tmp[i] );
+ }
+ slapi_ch_free((void**)&tmp );
+
+ LDAPDebug( LDAP_DEBUG_TRACE, "<= idl_fetch %lu ids (%lu max)\n", (u_long)idl->b_nids,
+ (u_long)idl->b_nmax, 0 );
+ return( idl );
+}
+
+static int
+idl_store(
+ backend *be,
+ DB *db,
+ DBT *key,
+ IDList *idl,
+ DB_TXN *txn
+)
+{
+ int rc;
+ DBT data = {0};
+
+ /* LDAPDebug( LDAP_DEBUG_TRACE, "=> idl_store\n", 0, 0, 0 ); */
+
+ data.dptr = (char *) idl;
+ data.dsize = (2 + idl->b_nmax) * sizeof(ID);
+
+ rc = db->put( db, txn, key, &data, 0 );
+ if ( 0 != rc ) {
+ char *msg;
+ if ( EPERM == rc && rc != errno ) {
+ LDAPDebug( LDAP_DEBUG_ANY,
+ "idl_store(%s): Database failed to run, "
+ "There is insufficient memory available for database.\n",
+ ((char*)key->dptr)[ key->dsize - 1 ] ? "" : (char*)key->dptr, 0, 0 );
+ } else {
+ if (LDBM_OS_ERR_IS_DISKFULL(rc)) {
+ operation_out_of_disk_space();
+ }
+ LDAPDebug( ((DB_LOCK_DEADLOCK == rc) ? LDAP_DEBUG_TRACE : LDAP_DEBUG_ANY),
+ "idl_store(%s) returns %d %s\n",
+ ((char*)key->dptr)[ key->dsize - 1 ] ? "" : (char*)key->dptr,
+ rc, (msg = dblayer_strerror( rc )) ? msg : "" );
+ if (rc == DB_RUNRECOVERY) {
+ LDAPDebug(LDAP_DEBUG_ANY, "%s\n", "Note: idl_store failures can be an indication of insufficient disk space.", 0, 0);
+ ldbm_nasty("idl_store",71,rc);
+ }
+ }
+ }
+
+ /* LDAPDebug( LDAP_DEBUG_TRACE, "<= idl_store %d\n", rc, 0, 0 ); */
+ return( rc );
+}
+
+static void
+idl_split_block(
+ IDList *b,
+ ID id,
+ IDList **n1,
+ IDList **n2
+)
+{
+ ID i;
+
+ /* find where to split the block */
+ for ( i = 0; i < b->b_nids && id > b->b_ids[i]; i++ )
+ ; /* NULL */
+
+ *n1 = idl_alloc( i == 0 ? 1 : i );
+ *n2 = idl_alloc( b->b_nids - i + (i == 0 ? 0 : 1));
+
+ /*
+ * everything before the id being inserted in the first block
+ * unless there is nothing, in which case the id being inserted
+ * goes there.
+ */
+ SAFEMEMCPY( (char *) &(*n1)->b_ids[0], (char *) &b->b_ids[0],
+ i * sizeof(ID) );
+ (*n1)->b_nids = (i == 0 ? 1 : i);
+
+ if ( i == 0 ) {
+ (*n1)->b_ids[0] = id;
+ } else {
+ (*n2)->b_ids[0] = id;
+ }
+
+ /* the id being inserted & everything after in the second block */
+ SAFEMEMCPY( (char *) &(*n2)->b_ids[i == 0 ? 0 : 1],
+ (char *) &b->b_ids[i], (b->b_nids - i) * sizeof(ID) );
+ (*n2)->b_nids = b->b_nids - i + (i == 0 ? 0 : 1);
+}
+
+/*
+ * idl_change_first - called when an indirect block's first key has
+ * changed, meaning it needs to be stored under a new key, and the
+ * header block pointing to it needs updating.
+ */
+
+static int
+idl_change_first(
+ backend *be,
+ DB *db,
+ DBT *hkey, /* header block key */
+ IDList *h, /* header block */
+ int pos, /* pos in h to update */
+ DBT *bkey, /* data block key */
+ IDList *b, /* data block */
+ DB_TXN *txn
+)
+{
+ int rc;
+ char *msg;
+
+ /* LDAPDebug( LDAP_DEBUG_TRACE, "=> idl_change_first\n", 0, 0, 0 ); */
+
+ /* delete old key block */
+ rc = db->del( db, txn, bkey, 0 );
+ if ( (rc != 0) && (DB_LOCK_DEADLOCK != rc) )
+ {
+ LDAPDebug( LDAP_DEBUG_ANY, "idl_change_first del (%s) err %d %s\n",
+ bkey->dptr, rc, (msg = dblayer_strerror( rc )) ? msg : "" );
+ if (rc == DB_RUNRECOVERY) {
+ ldbm_nasty("idl_store",72,rc);
+ }
+ return( rc );
+ }
+
+ /* write block with new key */
+ sprintf( bkey->dptr, "%c%s%lu", CONT_PREFIX, (char *)hkey->dptr, (u_long)b->b_ids[0] );
+ bkey->dsize = strlen( bkey->dptr ) + 1;
+ if ( (rc = idl_store( be, db, bkey, b, txn )) != 0 ) {
+ return( rc );
+ }
+
+ /* update + write indirect header block */
+ h->b_ids[pos] = b->b_ids[0];
+ if ( (rc = idl_store( be, db, hkey, h, txn )) != 0 ) {
+ return( rc );
+ }
+
+ return( 0 );
+}
+
+
+#define IDL_CHECK_FAILED(FORMAT, ARG1, ARG2) \
+do { \
+ char* fmt = slapi_ch_malloc (strlen(func) + strlen(note) + strlen(FORMAT) + 30); \
+ if (fmt != NULL) { \
+ sprintf (fmt, "%s(%%s,%lu) %s: %s\n", func, (u_long)id, note, FORMAT); \
+ LDAPDebug (LDAP_DEBUG_ANY, fmt, key->dptr, ARG1, ARG2); \
+ slapi_ch_free((void**)&fmt); \
+ } \
+} while(0)
+
+
+static void
+idl_check_indirect (IDList* idl, int i, IDList* tmp, IDList* tmp2,
+ char* func, char* note, DBT* key, ID id)
+ /* Check for inconsistencies; report any via LDAPDebug(LDAP_DEBUG_ANY).
+ The caller alleges that *idl is a header block, in which the
+ i'th item points to the indirect block *tmp, and either tmp2 == NULL
+ or *tmp2 is the indirect block to which the i+1'th item in *idl points.
+ The other parameters are merely output in each error message, like:
+ printf ("%s(%s,%lu) %s: ...", func, key->dptr, (u_long)id, note, ...)
+ */
+{
+ /* The implementation is optimized for no inconsistencies. */
+ const ID thisID = idl->b_ids[i];
+ const ID nextID = idl->b_ids[i+1];
+ const ID tmp0 = tmp->b_ids[0];
+ const ID tmpLast = tmp->b_ids[tmp->b_nids-1];
+
+ if (tmp0 != thisID) {
+ IDL_CHECK_FAILED ("tmp->b_ids[0] == %lu, not %lu\n",
+ (u_long)tmp0, (u_long)thisID);
+ }
+ if (tmp0 > tmpLast) {
+ IDL_CHECK_FAILED ("tmp->b_ids[0] == %lu > %lu [last]\n",
+ (u_long)tmp0, (u_long)tmpLast);
+ }
+ if (nextID == NOID) {
+ if (tmp2 != NULL) {
+ IDL_CHECK_FAILED ("idl->b_ids[%i+1] == NOID, but tmp2 != NULL\n", i, 0);
+ }
+ } else {
+ if (nextID <= thisID) {
+ IDL_CHECK_FAILED ("idl->b_ids contains %lu, %lu\n", (u_long)thisID, (u_long)nextID);
+ }
+ if (nextID <= tmpLast) {
+ IDL_CHECK_FAILED ("idl->b_ids[i+1] == %lu <= %lu (last of idl->b_ids[i])\n",
+ (u_long)nextID, (u_long)tmpLast);
+ }
+ if (tmp2 != NULL && tmp2->b_ids[0] != nextID) {
+ IDL_CHECK_FAILED ("tmp2->b_ids[0] == %lu, not %lu\n",
+ (u_long)tmp2->b_ids[0], (u_long)nextID);
+ }
+ }
+}
+
+
+int
+idl_old_insert_key(
+ backend *be,
+ DB *db,
+ DBT *key,
+ ID id,
+ DB_TXN *txn,
+ struct attrinfo *a,
+ int *disposition
+)
+{
+ struct ldbminfo *li = (struct ldbminfo *) be->be_database->plg_private;
+ int i, j, rc = 0;
+ char *msg;
+ IDList *idl, *tmp, *tmp2, *tmp3;
+ char *kstr;
+ DBT k2 = {0};
+ DBT k3 = {0};
+
+ if (NULL != disposition) {
+ *disposition = IDL_INSERT_NORMAL;
+ }
+
+ if (0 == a->ai_idl->idl_maxids) {
+ idl_init_maxids(li,a->ai_idl);
+ }
+
+ idl_Wlock_list(a->ai_idl,key);
+ if ( (idl = idl_fetch_one( li, db, key, txn, &rc )) == NULL ) {
+ if ( rc != 0 && rc != DB_NOTFOUND ) {
+ if ( rc != DB_LOCK_DEADLOCK )
+ {
+ LDAPDebug( LDAP_DEBUG_ANY, "idl_insert_key 0 BAD %d %s\n",
+ rc, (msg = dblayer_strerror( rc )) ? msg : "", 0 );
+ }
+ return( rc );
+ }
+ idl = idl_alloc( 1 );
+ idl->b_ids[idl->b_nids++] = id;
+ rc = idl_store( be, db, key, idl, txn );
+ if ( rc != 0 && rc != DB_LOCK_DEADLOCK )
+ {
+ LDAPDebug( LDAP_DEBUG_ANY, "idl_insert_key 1 BAD %d %s\n",
+ rc, (msg = dblayer_strerror( rc )) ? msg : "", 0 );
+ }
+
+ idl_free( idl );
+ idl_unlock_list(a->ai_idl,key);
+ return( rc );
+ }
+
+ /* regular block */
+ if ( ! INDIRECT_BLOCK( idl ) ) {
+ switch ( idl_insert_maxids( &idl, id, a->ai_idl->idl_maxids ) ) {
+ case 0: /* id inserted - store the updated block */
+ case 1:
+ rc = idl_store( be, db, key, idl, txn );
+ break;
+
+ case 2: /* id already there - nothing to do */
+ rc = 0;
+ /* Could be an ALLID block, let's check */
+ if (ALLIDS(idl)) {
+ if (NULL != disposition) {
+ *disposition = IDL_INSERT_ALLIDS;
+ }
+ }
+ break;
+
+ case 3: /* id not inserted - block must be split */
+ /* check threshold for marking this an all-id block */
+ if ( a->ai_idl->idl_maxindirect < 2 ) {
+ idl_free( idl );
+ idl = idl_allids( be );
+ rc = idl_store( be, db, key, idl, txn );
+ idl_free( idl );
+
+ idl_unlock_list(a->ai_idl,key);
+ if ( rc != 0 && rc != DB_LOCK_DEADLOCK)
+ {
+ LDAPDebug( LDAP_DEBUG_ANY, "idl_insert_key 2 BAD %d %s\n",
+ rc, (msg = dblayer_strerror( rc )) ? msg : "", 0 );
+ }
+ if (NULL != disposition) {
+ *disposition = IDL_INSERT_NOW_ALLIDS;
+ }
+ return( rc );
+ }
+
+ idl_split_block( idl, id, &tmp, &tmp2 );
+ idl_free( idl );
+
+ /* create the header indirect block */
+ idl = idl_alloc( 3 );
+ idl->b_nmax = 3;
+ idl->b_nids = INDBLOCK;
+ idl->b_ids[0] = tmp->b_ids[0];
+ idl->b_ids[1] = tmp2->b_ids[0];
+ idl->b_ids[2] = NOID;
+
+ /* store it */
+ rc = idl_store( be, db, key, idl, txn );
+ if ( rc != 0 ) {
+ idl_free( idl );
+ idl_free( tmp );
+ idl_free( tmp2 );
+ if ( rc != DB_LOCK_DEADLOCK )
+ {
+ LDAPDebug( LDAP_DEBUG_ANY, "idl_insert_key 3 BAD %d %s\n",
+ rc, (msg = dblayer_strerror( rc )) ? msg : "", 0 );
+ }
+ return( rc );
+ }
+
+ /* store the first id block */
+ kstr = (char *) slapi_ch_malloc( key->dsize + 20 );
+ sprintf( kstr, "%c%s%lu", CONT_PREFIX, (char *)key->dptr,
+ (u_long)tmp->b_ids[0] );
+ k2.dptr = kstr;
+ k2.dsize = strlen( kstr ) + 1;
+ rc = idl_store( be, db, &k2, tmp, txn );
+
+ /* store the second id block */
+ sprintf( kstr, "%c%s%lu", CONT_PREFIX, (char *)key->dptr,
+ (u_long)tmp2->b_ids[0] );
+ k2.dptr = kstr;
+ k2.dsize = strlen( kstr ) + 1;
+ rc = idl_store( be, db, &k2, tmp2, txn );
+ if ( rc != 0 ) {
+ idl_free( idl );
+ idl_free( tmp );
+ idl_free( tmp2 );
+ if ( rc != DB_LOCK_DEADLOCK )
+ {
+ LDAPDebug( LDAP_DEBUG_ANY, "idl_insert_key 4 BAD %d %s\n",
+ rc, (msg = dblayer_strerror( rc )) ? msg : "", 0 );
+ }
+ return( rc );
+ }
+ idl_check_indirect (idl, 0, tmp, tmp2,
+ "idl_insert_key", "split", key, id);
+
+ slapi_ch_free((void**)&kstr );
+ idl_free( tmp );
+ idl_free( tmp2 );
+ break;
+ }
+
+ idl_free( idl );
+ idl_unlock_list(a->ai_idl,key);
+ if ( rc != 0 && rc != DB_LOCK_DEADLOCK )
+ {
+ LDAPDebug( LDAP_DEBUG_ANY, "idl_insert_key 5 BAD %d %s\n",
+ rc, (msg = dblayer_strerror( rc )) ? msg : "", 0 );
+ }
+ return( rc );
+ }
+
+ /*
+ * this is an indirect block which points to other blocks.
+ * we need to read in the block into which the id should be
+ * inserted, then insert the id and store the block. we might
+ * have to split the block if it is full, which means we also
+ * need to write a new "header" block.
+ */
+
+ /* select the block to try inserting into */
+ for ( i = 0; idl->b_ids[i] != NOID && id > idl->b_ids[i]; i++ )
+ ; /* NULL */
+ if ( id == idl->b_ids[i] ) { /* already in a block */
+#ifdef _DEBUG_LARGE_BLOCKS
+ LDAPDebug( LDAP_DEBUG_ANY,
+ "id %lu for key (%s) is already in block %d\n",
+ (u_long)id, key.dptr, i);
+#endif
+ idl_unlock_list(a->ai_idl,key);
+ idl_free( idl );
+ return( 0 );
+ }
+ if ( i != 0 ) {
+ i--;
+ }
+
+ /* get the block */
+ kstr = (char *) slapi_ch_malloc( key->dsize + 20 );
+ sprintf( kstr, "%c%s%lu", CONT_PREFIX, (char *)key->dptr, (u_long)idl->b_ids[i] );
+ k2.dptr = kstr;
+ k2.dsize = strlen( kstr ) + 1;
+ if ( (tmp = idl_fetch_one( li, db, &k2, txn, &rc )) == NULL ) {
+ if ( rc != 0 ) {
+ if ( rc != DB_LOCK_DEADLOCK )
+ {
+ LDAPDebug( LDAP_DEBUG_ANY, "idl_insert_key 5.5 BAD %d %s\n",
+ rc, (msg = dblayer_strerror( rc )) ? msg : "", 0 );
+ }
+ return( rc );
+ }
+ LDAPDebug( LDAP_DEBUG_ANY,
+ "nonexistent continuation block (%s)\n", k2.dptr, 0, 0 );
+ idl_unlock_list(a->ai_idl,key);
+ idl_free( idl );
+ slapi_ch_free((void**)&kstr );
+ return( -1 );
+ }
+
+ /* insert the id */
+ switch ( idl_insert_maxids( &tmp, id, a->ai_idl->idl_maxids ) ) {
+ case 0: /* id inserted ok */
+ rc = idl_store( be, db, &k2, tmp, txn );
+ if (0 != rc) {
+ idl_check_indirect (idl, i, tmp, NULL,
+ "idl_insert_key", "indirect", key, id);
+ }
+ break;
+
+ case 1: /* id inserted - first id in block has changed */
+ /*
+ * key for this block has changed, so we have to
+ * write the block under the new key, delete the
+ * old key block + update and write the indirect
+ * header block.
+ */
+
+ rc = idl_change_first( be, db, key, idl, i, &k2, tmp, txn );
+ if ( rc != 0 ) {
+ break; /* return error in rc */
+ }
+ idl_check_indirect (idl, i, tmp, NULL,
+ "idl_insert_key", "indirect 1", key, id);
+ break;
+
+ case 2: /* id not inserted - already there */
+ idl_check_indirect (idl, i, tmp, NULL,
+ "idl_insert_key", "indirect no change", key, id);
+ break;
+
+ case 3: /* id not inserted - block is full */
+ /*
+ * first, see if we can shift ids down one, moving
+ * the last id in the current block to the next
+ * block, and then adding the id we are inserting to
+ * the current block. we'll need to split the block
+ * otherwise.
+ */
+
+ /* is there a next block? */
+ if ( idl->b_ids[i + 1] != NOID ) {
+ char *kstr3 = (char *) slapi_ch_malloc( key->dsize + 20 );
+ /* yes - read it in */
+ sprintf( kstr3, "%c%s%lu", CONT_PREFIX, (char *)key->dptr,
+ (u_long)idl->b_ids[i + 1] );
+ k3.dptr = kstr3;
+ k3.dsize = strlen( kstr3 ) + 1;
+ if ( (tmp2 = idl_fetch_one( li, db, &k3, txn, &rc ))
+ == NULL ) {
+ if ( rc != DB_LOCK_DEADLOCK )
+ {
+ LDAPDebug( LDAP_DEBUG_ANY,
+ "idl_fetch_one (%s) returns NULL\n",
+ k3.dptr, 0, 0 );
+ }
+ if (0 != rc) {
+ idl_check_indirect (idl, i, tmp, NULL,
+ "idl_insert_key", "indirect missing", key, id);
+ }
+ break;
+ }
+
+ /*
+ * insert the last key in the previous block in
+ * the next block. it should go at the beginning
+ * always, if it fits at all.
+ */
+ rc = idl_insert_maxids (&tmp2,
+ id > tmp->b_ids[tmp->b_nids-1] ?
+ id : tmp->b_ids[tmp->b_nids-1],
+ a->ai_idl->idl_maxids);
+ switch ( rc ) {
+ case 1: /* id inserted first in block */
+ rc = idl_change_first( be, db, key, idl,
+ i + 1, &k3, tmp2, txn );
+ if ( rc != 0 ) {
+ break; /* return error in rc */
+ }
+
+ if (id < tmp->b_ids[tmp->b_nids-1]) {
+ /*
+ * we inserted the last id in the previous
+ * block in this block. we need to "remove"
+ * it from the previous block and insert the
+ * new id. decrementing the b_nids count
+ * in the previous block has the effect
+ * of removing the last id.
+ */
+
+ /* remove last id in previous block */
+ tmp->b_nids--;
+
+ /* insert new id in previous block */
+ switch ( (rc = idl_insert_maxids( &tmp, id,
+ a->ai_idl->idl_maxids )) ) {
+ case 0: /* id inserted */
+ rc = idl_store( be, db, &k2, tmp, txn );
+ break;
+ case 1: /* first in block */
+ rc = idl_change_first( be, db, key, idl,
+ i, &k2, tmp, txn );
+ break;
+ case 2: /* already there - how? */
+ case 3: /* split block - how? */
+ LDAPDebug( LDAP_DEBUG_ANY,
+ "not expecting (%d) from idl_insert_maxids of %lu in (%s)\n",
+ rc, (u_long)id, k2.dptr );
+ LDAPDebug( LDAP_DEBUG_ANY,
+ "likely database corruption\n",
+ 0, 0, 0 );
+ rc = 0;
+ break;
+ }
+ }
+ if ( rc != 0 ) {
+ break; /* return error in rc */
+ }
+ idl_check_indirect (idl, i, tmp, tmp2,
+ "idl_insert_key", "overflow", key, id);
+
+ if ( k2.dptr != NULL ) {
+ free( k2.dptr );
+ }
+ if ( k3.dptr != NULL ) {
+ free( k3.dptr );
+ }
+ idl_free( tmp );
+ idl_free( tmp2 );
+ idl_free( idl );
+ idl_unlock_list(a->ai_idl,key);
+ return( rc );
+
+ case 0: /* id inserted not at start - how? */
+ case 2: /* id already there - how? */
+ /*
+ * if either of these cases happen, this
+ * index entry must have been corrupt when
+ * we started this insert. what can we do
+ * aside from log a warning?
+ */
+ LDAPDebug( LDAP_DEBUG_ANY,
+ "not expecting return %d from idl_insert_maxids of id %lu in block with key (%s)\n",
+ rc, (u_long)tmp->b_ids[tmp->b_nids-1], k3.dptr );
+ LDAPDebug( LDAP_DEBUG_ANY,
+ "likely database corruption\n", 0, 0, 0 );
+ /* FALL */
+ case 3: /* block is full */
+ /*
+ * if this case happens, we fall back to
+ * splitting the original block.
+ * This is not an error condition. So set
+ * rc = 0 to continue. Otherwise, it will break
+ * from the case statement and return rc=3,
+ * which is not correct.
+ */
+ rc = 0;
+ idl_free( tmp2 );
+ break;
+ }
+ if ( rc != 0 ) {
+ break; /* return error in rc */
+ }
+ }
+
+ /*
+ * must split the block, write both new blocks + update
+ * and write the indirect header block.
+ */
+
+ /* count how many indirect blocks */
+ for ( j = 0; idl->b_ids[j] != NOID; j++ )
+ ; /* NULL */
+
+ /* check it against all-id thresholed */
+ if ( j + 1 > a->ai_idl->idl_maxindirect ) {
+ /*
+ * we've passed the all-id threshold, meaning
+ * that this set of blocks should be replaced
+ * by a single "all-id" block. our job: delete
+ * all the indirect blocks, and replace the header
+ * block by an all-id block.
+ */
+
+ /* delete all indirect blocks */
+ for ( j = 0; idl->b_ids[j] != NOID; j++ ) {
+ sprintf( kstr, "%c%s%lu", CONT_PREFIX, (char *)key->dptr,
+ (u_long)idl->b_ids[j] );
+ k2.dptr = kstr;
+ k2.dsize = strlen( kstr ) + 1;
+
+ rc = db->del( db, txn, &k2, 0 );
+ if ( rc != 0 ) {
+ if (rc == DB_RUNRECOVERY) {
+ ldbm_nasty("",73,rc);
+ }
+ break;
+ }
+ }
+
+ /* store allid block in place of header block */
+ if ( 0 == rc ) {
+ idl_free( idl );
+ idl = idl_allids( be );
+ rc = idl_store( be, db, key, idl, txn );
+ if (NULL != disposition) {
+ *disposition = IDL_INSERT_NOW_ALLIDS;
+ }
+ }
+
+ if ( k2.dptr != NULL ) {
+ free( k2.dptr );
+ }
+ if ( k3.dptr != NULL ) {
+ free( k3.dptr );
+ }
+ idl_free( idl );
+ idl_free( tmp );
+ idl_unlock_list(a->ai_idl,key);
+ return( rc );
+ }
+
+ idl_split_block( tmp, id, &tmp2, &tmp3 );
+ idl_free( tmp );
+
+ /* create a new updated indirect header block */
+ tmp = idl_alloc( idl->b_nmax + 1 );
+ tmp->b_nids = INDBLOCK;
+ /* everything up to the split block */
+ SAFEMEMCPY( (char *) tmp->b_ids, (char *) idl->b_ids,
+ i * sizeof(ID) );
+ /* the two new blocks */
+ tmp->b_ids[i] = tmp2->b_ids[0];
+ tmp->b_ids[i + 1] = tmp3->b_ids[0];
+ /* everything after the split block */
+ SAFEMEMCPY( (char *) &tmp->b_ids[i + 2], (char *)
+ &idl->b_ids[i + 1], (idl->b_nmax - i - 1) * sizeof(ID) );
+
+ /* store the header block */
+ rc = idl_store( be, db, key, tmp, txn );
+ if ( rc != 0 ) {
+ idl_free( tmp2 );
+ idl_free( tmp3 );
+ break;
+ }
+
+ /* store the first id block */
+ sprintf( kstr, "%c%s%lu", CONT_PREFIX, (char *)key->dptr,
+ (u_long)tmp2->b_ids[0] );
+ k2.dptr = kstr;
+ k2.dsize = strlen( kstr ) + 1;
+ rc = idl_store( be, db, &k2, tmp2, txn );
+ if ( rc != 0 ) {
+ idl_free( tmp2 );
+ idl_free( tmp3 );
+ break;
+ }
+
+ /* store the second id block */
+ sprintf( kstr, "%c%s%lu", CONT_PREFIX, (char *)key->dptr,
+ (u_long)tmp3->b_ids[0] );
+ k2.dptr = kstr;
+ k2.dsize = strlen( kstr ) + 1;
+ rc = idl_store( be, db, &k2, tmp3, txn );
+ if ( rc != 0 ) {
+ idl_free( tmp2 );
+ idl_free( tmp3 );
+ break;
+ }
+
+ idl_check_indirect (tmp, i, tmp2, tmp3,
+ "idl_insert_key", "indirect split", key, id);
+ idl_free( tmp2 );
+ idl_free( tmp3 );
+ break;
+ }
+
+ if ( k2.dptr != NULL ) {
+ free( k2.dptr );
+ }
+ if ( k3.dptr != NULL ) {
+ free( k3.dptr );
+ }
+ idl_free( tmp );
+ idl_free( idl );
+ idl_unlock_list(a->ai_idl,key);
+ return( rc );
+}
+
+
+/* Store a complete IDL all in one go, there must not be an existing key with the same value */
+/* Routine used by merging import code */
+int idl_old_store_block(
+ backend *be,
+ DB *db,
+ DBT *key,
+ IDList *idl,
+ DB_TXN *txn,
+ struct attrinfo *a
+ )
+{
+ struct ldbminfo *li = (struct ldbminfo *) be->be_database->plg_private;
+ int ret = 0;
+ idl_private *priv = a->ai_idl;
+
+ if (0 == a->ai_idl->idl_maxids) {
+ idl_init_maxids(li,a->ai_idl);
+ }
+
+ /* First, is it an ALLIDS block ? */
+ if (ALLIDS(idl)) {
+ /* If so, we can store it as-is */
+ ret = idl_store(be,db,key,idl,txn);
+ } else {
+ /* Next, is it a block with so many IDs in it that it _should_ be an ALLIDS block ? */
+ if (idl->b_nids > (ID)li->li_allidsthreshold) {
+ /* If so, store an ALLIDS block */
+ IDList *all = idl_allids(be);
+ ret = idl_store(be,db,key,all,txn);
+ idl_free(all);
+ } else {
+ /* Then , is it a block which is smaller than the size at which it needs splitting ? */
+ if (idl->b_nids <= (ID)priv->idl_maxids) {
+ /* If so, store as-is */
+ ret = idl_store(be,db,key,idl,txn);
+ } else {
+ size_t number_of_ids = 0;
+ size_t max_ids_in_block = 0;
+ size_t number_of_cont_blks = 0;
+ size_t i = 0;
+ size_t number_of_ids_left = 0;
+ IDList *master_block = NULL;
+ size_t index = 0;
+ DBT cont_key = {0};
+
+ number_of_ids = idl->b_nids;
+ max_ids_in_block = priv->idl_maxids;
+ number_of_cont_blks = number_of_ids / max_ids_in_block;
+ if (0 != number_of_ids % max_ids_in_block) {
+ number_of_cont_blks++;
+ }
+ number_of_ids_left = number_of_ids;
+ /* Block needs splitting into continuation blocks */
+ /* We need to make up a master block and n continuation blocks */
+ /* Alloc master block */
+ master_block = idl_alloc(number_of_cont_blks + 1);
+ if (NULL == master_block) {
+ return -1;
+ }
+ master_block->b_nids = INDBLOCK;
+ master_block->b_ids[number_of_cont_blks] = NOID;
+ /* Iterate over ids making the continuation blocks */
+ for (i = 0 ; i < number_of_cont_blks; i++) {
+ IDList *this_cont_block = NULL;
+ size_t size_of_this_block = 0;
+ ID lead_id = NOID;
+ size_t j = 0;
+
+ lead_id = idl->b_ids[index];
+ if (number_of_ids_left >= max_ids_in_block) {
+ size_of_this_block = max_ids_in_block;
+ } else {
+ size_of_this_block = number_of_ids_left;
+ }
+ this_cont_block = idl_alloc(size_of_this_block);
+ if (NULL == this_cont_block) {
+ return -1;
+ }
+ this_cont_block->b_nids = size_of_this_block;
+ /* Copy over the ids to the cont block we're making */
+ for (j = 0; j < size_of_this_block; j++) {
+ this_cont_block->b_ids[j] = idl->b_ids[index + j];
+ }
+ /* Make the continuation key */
+ make_cont_key(&cont_key,key,lead_id);
+ /* Now store the continuation block */
+ ret = idl_store(be,db,&cont_key,this_cont_block,txn);
+ idl_free(this_cont_block);
+ free(cont_key.data);
+ if ( ret != 0 && ret != DB_LOCK_DEADLOCK )
+ {
+ LDAPDebug( LDAP_DEBUG_ANY, "idl_store_block(%s) 1 BAD %d %s\n",key->data, ret, dblayer_strerror( ret ));
+ return ret;
+ }
+ /* Put the lead ID number in the header block */
+ master_block->b_ids[i] = lead_id;
+
+ /* Make our loop invariants correct */
+ number_of_ids_left -= size_of_this_block;
+ index += size_of_this_block;
+ }
+ PR_ASSERT(0 == number_of_ids_left);
+ /* Now store the master block */
+ ret = idl_store(be,db,key,master_block,txn);
+ /* And free it */
+ idl_free(master_block);
+ }
+ }
+ }
+ return ret;
+}
+
+/*
+ * idl_insert - insert an id into an id list.
+ */
+void idl_insert(IDList **idl, ID id)
+{
+ ID i, j;
+ NIDS nids;
+
+ if ((*idl) == NULL) {
+ (*idl) = idl_alloc(1);
+ idl_append((*idl), id);
+ return;
+ }
+
+ if (ALLIDS(*idl)) {
+ return;
+ }
+
+ i = nids = (*idl)->b_nids;
+
+ if (nids > 0) {
+ /* optimize for a simple append */
+ if (id == (*idl)->b_ids[nids-1]) {
+ return;
+ } else if (id > (*idl)->b_ids[nids-1]) {
+ if (nids < (*idl)->b_nmax) {
+ (*idl)->b_ids[nids] = id;
+ (*idl)->b_nids++;
+ return;
+ }
+
+ i = nids;
+
+ } else if (id < (*idl)->b_ids[0]) {
+ /* prepend */
+ i = 0;
+ } else {
+ int lo = 0;
+ int hi = (*idl)->b_nids - 1;
+ int mid = 0;
+ ID *ids = (*idl)->b_ids;
+
+ if (0 != (*idl)->b_nids) {
+ while (lo <= hi) {
+ mid = (hi + lo) >> 1;
+ if (ids[mid] > id) {
+ hi = mid - 1;
+ } else {
+ if (ids[mid] < id) {
+ lo = mid + 1;
+ } else {
+ /* Found it ! */
+ return;
+ }
+ }
+ }
+ }
+ i = lo;
+ }
+ }
+
+ /* do we need to make room for it? */
+ if ( (*idl)->b_nids == (*idl)->b_nmax ) {
+ (*idl)->b_nmax *= 2;
+
+ (*idl) = (IDList *) slapi_ch_realloc( (char *) (*idl),
+ ((*idl)->b_nmax + 2) * sizeof(ID) );
+ }
+
+ /* make a slot for the new id */
+ for ( j = (*idl)->b_nids; j != i; j-- ) {
+ (*idl)->b_ids[j] = (*idl)->b_ids[j-1];
+ }
+
+ (*idl)->b_ids[i] = id;
+ (*idl)->b_nids++;
+
+ memset( (char *) &(*idl)->b_ids[(*idl)->b_nids], '\0',
+ ((*idl)->b_nmax - (*idl)->b_nids) * sizeof(ID) );
+
+ return;
+}
+
+/*
+ * idl_insert_maxids - insert an id into an id list.
+ * returns 0 id inserted
+ * 1 id inserted, first id in block has changed
+ * 2 id not inserted, already there
+ * 3 id not inserted, block must be split
+ */
+
+static int
+idl_insert_maxids( IDList **idl, ID id, int maxids )
+{
+ ID i, j;
+ NIDS nids;
+
+ if ( ALLIDS( *idl ) ) {
+ return( 2 ); /* already there */
+ }
+
+ nids = (*idl)->b_nids;
+
+ if (nids > 0) {
+ /* optimize for a simple append */
+ if (id == (*idl)->b_ids[nids-1]) {
+ return (2);
+ } else if (id > (*idl)->b_ids[nids-1]) {
+ if (nids < (*idl)->b_nmax) {
+ (*idl)->b_ids[nids] = id;
+ (*idl)->b_nids++;
+ return 0;
+ }
+
+ i = nids;
+
+ } else if (idl_tune & IDL_TUNE_BSEARCH) {
+ int lo = 0;
+ int hi = (*idl)->b_nids - 1;
+ int mid = 0;
+ ID *ids = (*idl)->b_ids;
+ if (0 != (*idl)->b_nids) {
+ while (lo <= hi) {
+ mid = (hi + lo) >> 1;
+ if (ids[mid] > id) {
+ hi = mid - 1;
+ } else {
+ if (ids[mid] < id) {
+ lo = mid + 1;
+ } else {
+ /* Found it ! */
+ return(2);
+ }
+ }
+ }
+ }
+ i = lo;
+ } else {
+ /* is it already there? linear search */
+ for ( i = 0; i < (*idl)->b_nids && id > (*idl)->b_ids[i]; i++ ) {
+ ; /* NULL */
+ }
+ if ( i < (*idl)->b_nids && (*idl)->b_ids[i] == id ) {
+ return( 2 ); /* already there */
+ }
+ }
+ }
+
+ /* do we need to make room for it? */
+ if ( (*idl)->b_nids == (*idl)->b_nmax ) {
+ /* make room or indicate block needs splitting */
+ if ( (*idl)->b_nmax == (ID) maxids ) {
+ return( 3 ); /* block needs splitting */
+ }
+
+ if (idl_tune & IDL_TUNE_NOPAD) {
+ (*idl)->b_nmax++;
+ } else {
+ (*idl)->b_nmax *= 2;
+ }
+ if ( (*idl)->b_nmax > (ID)maxids ) {
+ (*idl)->b_nmax = maxids;
+ }
+ *idl = (IDList *) slapi_ch_realloc( (char *) *idl,
+ ((*idl)->b_nmax + 2) * sizeof(ID) );
+ }
+
+ /* make a slot for the new id */
+ for ( j = (*idl)->b_nids; j != i; j-- ) {
+ (*idl)->b_ids[j] = (*idl)->b_ids[j-1];
+ }
+ (*idl)->b_ids[i] = id;
+ (*idl)->b_nids++;
+ (void) memset( (char *) &(*idl)->b_ids[(*idl)->b_nids], '\0',
+ ((*idl)->b_nmax - (*idl)->b_nids) * sizeof(ID) );
+
+ return( i == 0 ? 1 : 0 ); /* inserted - first id changed or not */
+}
+
+/*
+ * idl_delete_key - delete an id from the index entry identified by key
+ * returns 0 id was deleted
+ * -666 no such index entry or id in index entry
+ * other an error code from db
+ */
+
+int
+idl_old_delete_key(
+ backend *be,
+ DB *db,
+ DBT *key,
+ ID id,
+ DB_TXN *txn,
+ struct attrinfo *a
+)
+{
+ struct ldbminfo *li = (struct ldbminfo *) be->be_database->plg_private;
+ int i, j, rc;
+ char *msg;
+ IDList *idl, *didl;
+ DBT contkey = {0};
+
+ LDAPDebug( LDAP_DEBUG_TRACE, "=> idl_delete_key(%s,%lu)\n",
+ key->dptr, (u_long)id, 0 );
+
+ idl_Wlock_list(a->ai_idl,key);
+
+ if ( (idl = idl_fetch_one( li, db, key, txn, &rc )) == NULL ) {
+ idl_unlock_list(a->ai_idl,key);
+ if ( rc != 0 && rc != DB_NOTFOUND && rc != DB_LOCK_DEADLOCK )
+ {
+ LDAPDebug( LDAP_DEBUG_ANY, "idl_delete_key(%s) 0 BAD %d %s\n",
+ key->dptr, rc, (msg = dblayer_strerror( rc )) ? msg : "" );
+ }
+ if ( 0 == rc || DB_NOTFOUND == rc ) rc = -666;
+ LDAPDebug( LDAP_DEBUG_TRACE, "<= idl_delete_key(%s,%lu) %d !idl_fetch_one\n",
+ key->dptr, (u_long)id, rc );
+ return rc;
+ }
+
+ /* regular block */
+ if ( ! INDIRECT_BLOCK( idl ) ) {
+ switch ( idl_delete( &idl, id ) ) {
+ case 0: /* id deleted, store the updated block */
+ case 1: /* first id changed - ok in direct block */
+ rc = idl_store( be, db, key, idl, txn );
+ if ( rc != 0 && rc != DB_LOCK_DEADLOCK )
+ {
+ LDAPDebug( LDAP_DEBUG_ANY, "idl_delete_key(%s) 1 BAD %d %s\n",
+ key->dptr, rc, (msg = dblayer_strerror( rc )) ? msg : "" );
+ }
+ break;
+
+ case 2: /* id deleted, block empty - delete it */
+ rc = db->del( db, txn, key, 0 );
+ if ( rc != 0 && rc != DB_LOCK_DEADLOCK )
+ {
+ LDAPDebug( LDAP_DEBUG_ANY, "idl_delete_key(%s) 2 BAD %d %s\n",
+ key->dptr, rc, (msg = dblayer_strerror( rc )) ? msg : "" );
+ if (rc == DB_RUNRECOVERY) {
+ ldbm_nasty("",74,rc);
+ }
+
+ }
+ break;
+
+ case 3: /* not there - previously deleted */
+ case 4: /* all ids block */
+ rc = 0;
+ break;
+
+ default:
+ LDAPDebug( LDAP_DEBUG_ANY, "idl_delete_key(%s) 3 BAD idl_delete\n",
+ key->dptr, 0, 0 );
+ break;
+ }
+
+ idl_free( idl );
+ idl_unlock_list(a->ai_idl,key);
+ LDAPDebug( LDAP_DEBUG_TRACE, "<= idl_delete_key(%s,%lu) %d (not indirect)\n",
+ key->dptr, (u_long)id, rc );
+ return( rc );
+ }
+
+ /*
+ * this is an indirect block that points to other blocks. we
+ * need to read the block containing the id to delete, delete
+ * the id, and store the changed block. if the first id in the
+ * block changes, or the block becomes empty, we need to rewrite
+ * the header block too.
+ */
+
+ /* select the block the id is in */
+ for ( i = 0; idl->b_ids[i] != NOID && id > idl->b_ids[i]; i++ ) {
+ ; /* NULL */
+ }
+ /* id smaller than smallest id - not there */
+ if ( i == 0 && id < idl->b_ids[i] ) {
+ idl_free( idl );
+ idl_unlock_list(a->ai_idl,key);
+ LDAPDebug( LDAP_DEBUG_TRACE, "<= idl_delete_key(%s,%lu) -666 (id not found)\n",
+ key->dptr, (u_long)id, 0 );
+ return( -666 );
+ }
+ if ( id != idl->b_ids[i] ) {
+ i--;
+ }
+
+ /* get the block to delete from */
+ make_cont_key( &contkey, key, idl->b_ids[i] );
+ if ( (didl = idl_fetch_one( li, db, &contkey, txn, &rc )) == NULL ) {
+ idl_free( idl );
+ idl_unlock_list(a->ai_idl,key);
+ if ( rc != DB_LOCK_DEADLOCK )
+ {
+ LDAPDebug( LDAP_DEBUG_ANY, "idl_delete_key(%s) 5 BAD %d %s\n",
+ contkey.dptr, rc, (msg = dblayer_strerror( rc )) ? msg : "" );
+ }
+ LDAPDebug( LDAP_DEBUG_TRACE, "<= idl_delete_key(%s,%lu) %d idl_fetch_one(contkey)\n",
+ contkey.dptr, (u_long)id, rc );
+ if ( contkey.dptr != NULL ) {
+ free( contkey.dptr );
+ }
+ return( rc );
+ }
+
+ rc = 0;
+ switch ( idl_delete( &didl, id ) ) {
+ case 0: /* id deleted - rewrite block */
+ if ( (rc = idl_store( be, db, &contkey, didl, txn )) != 0 ) {
+ if ( rc != DB_LOCK_DEADLOCK )
+ {
+ LDAPDebug( LDAP_DEBUG_ANY, "idl_delete_key(%s) BAD %d %s\n",
+ contkey.dptr, rc, (msg = dblayer_strerror( rc )) ? msg : "" );
+ }
+ }
+ if (0 != rc) {
+ idl_check_indirect( idl, i, didl, NULL, "idl_delete_key", "0", key, id );
+ }
+ break;
+
+ case 1: /* id deleted, first id changed, - write hdr, block */
+ rc = idl_change_first( be, db, key, idl, i, &contkey, didl, txn );
+ if ( rc != 0 && rc != DB_LOCK_DEADLOCK )
+ {
+ LDAPDebug( LDAP_DEBUG_ANY, "idl_delete_key(%s) 7 BAD %d %s\n",
+ contkey.dptr, rc, (msg = dblayer_strerror( rc )) ? msg : "" );
+ }
+ if (0 != rc) {
+ idl_check_indirect( idl, i, didl, NULL, "idl_delete_key", "1", key, id );
+ }
+ break;
+
+ case 2: /* id deleted, block empty - write hdr, del block */
+ for ( j = i; idl->b_ids[j] != NOID; j++ ) {
+ idl->b_ids[j] = idl->b_ids[j+1];
+ }
+ if ( idl->b_ids[0] != NOID ) { /* Write the header, first: */
+ rc = idl_store( be, db, key, idl, txn );
+ if ( rc != 0 && rc != DB_LOCK_DEADLOCK )
+ {
+ LDAPDebug( LDAP_DEBUG_ANY, "idl_delete_key: idl_store(%s) BAD %d %s\n",
+ key->dptr, rc, (msg = dblayer_strerror( rc )) ? msg : "" );
+ }
+ } else { /* This index is entirely empty. Delete the header: */
+ rc = db->del( db, txn, key, 0 );
+ if ( rc != 0 && rc != DB_LOCK_DEADLOCK )
+ {
+ LDAPDebug( LDAP_DEBUG_ANY, "idl_delete_key: db->del(%s) BAD %d %s\n",
+ key->dptr, rc, (msg = dblayer_strerror( rc )) ? msg : "" );
+ if (rc == DB_RUNRECOVERY) {
+ ldbm_nasty("",75,rc);
+ }
+
+ }
+ }
+ if ( rc == 0 ) { /* Delete the indirect block: */
+ rc = db->del( db, txn, &contkey, 0 );
+ if ( rc != 0 && rc != DB_LOCK_DEADLOCK )
+ {
+ LDAPDebug( LDAP_DEBUG_ANY, "idl_delete_key: db->del(%s) BAD %d %s\n",
+ contkey.dptr, rc, (msg = dblayer_strerror( rc )) ? msg : "" );
+ if (rc == DB_RUNRECOVERY) {
+ ldbm_nasty("",76,rc);
+ }
+
+ }
+ }
+ break;
+
+ case 3: /* id not found - previously deleted */
+ rc = 0;
+ idl_check_indirect( idl, i, didl, NULL, "idl_delete_key", "3", key, id );
+ break;
+ case 4: /* all ids block - should not happen */
+ LDAPDebug( LDAP_DEBUG_ANY, "idl_delete_key: cont block (%s) is allids\n",
+ contkey.dptr, 0, 0 );
+ rc = 0;
+ break;
+ }
+ idl_free( idl );
+ idl_free( didl );
+ if ( contkey.dptr != NULL ) {
+ free( contkey.dptr );
+ }
+ idl_unlock_list(a->ai_idl,key);
+ if ( rc != 0 && rc != DB_LOCK_DEADLOCK )
+ {
+ LDAPDebug( LDAP_DEBUG_ANY, "idl_delete_key(%s) 9 BAD %d %s\n",
+ key->dptr, rc, (msg = dblayer_strerror( rc )) ? msg : "" );
+ }
+ LDAPDebug( LDAP_DEBUG_TRACE, "<= idl_delete_key(%s,%lu) %d (indirect)\n",
+ key->dptr, (u_long)id, rc );
+ return( rc );
+}
+
+/*
+ * idl_delete - delete an id from an id list.
+ * returns 0 id deleted
+ * 1 id deleted, first id in block has changed
+ * 2 id deleted, block is empty
+ * 3 id not there
+ * 4 cannot delete from allids block
+ */
+
+static int
+idl_delete( IDList **idl, ID id )
+{
+ ID i, delpos;
+
+ if ( ALLIDS( *idl ) ) {
+ return( 4 ); /* cannot delete from allids block */
+ }
+
+ /* find the id to delete */
+ for ( i = 0; i < (*idl)->b_nids && id > (*idl)->b_ids[i]; i++ ) {
+ ; /* NULL */
+ }
+ if ( i == (*idl)->b_nids || (*idl)->b_ids[i] != id ) {
+ return( 3 ); /* id not there */
+ }
+
+ if ( --((*idl)->b_nids) == 0 ) {
+ return( 2 ); /* id deleted, block empty */
+ }
+
+ /* delete it */
+ delpos = i;
+ for ( ; i < (*idl)->b_nids; i++ ) {
+ (*idl)->b_ids[i] = (*idl)->b_ids[i+1];
+ }
+
+ return( delpos == 0 ? 1 : 0 ); /* first id changed : id deleted */
+}
+
+
+static void
+make_cont_key( DBT *contkey, DBT *key, ID id )
+{
+ contkey->dptr = (char *) slapi_ch_malloc( key->dsize + 20 );
+ sprintf( contkey->dptr, "%c%s%lu", CONT_PREFIX, (char *)key->dptr, (u_long)id );
+ contkey->dsize = strlen( contkey->dptr ) + 1;
+}
diff --git a/ldap/servers/slapd/back-ldbm/idl_common.c b/ldap/servers/slapd/back-ldbm/idl_common.c
new file mode 100644
index 00000000..2cea183e
--- /dev/null
+++ b/ldap/servers/slapd/back-ldbm/idl_common.c
@@ -0,0 +1,402 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+/* Common IDL code, used in both old and new indexing schemes */
+
+#include "back-ldbm.h"
+
+size_t idl_sizeof(IDList *idl)
+{
+ return (2 + idl->b_nmax) * sizeof(ID);
+}
+
+NIDS idl_length(IDList *idl)
+{
+ return (idl->b_nmax == ALLIDSBLOCK) ? UINT_MAX : idl->b_nids;
+}
+
+int idl_is_allids(IDList *idl)
+{
+ return (idl->b_nmax == ALLIDSBLOCK);
+}
+
+IDList *
+idl_alloc( NIDS nids )
+{
+ IDList *new;
+
+ /* nmax + nids + space for the ids */
+ new = (IDList *) slapi_ch_calloc( (2 + nids), sizeof(ID) );
+ new->b_nmax = nids;
+ new->b_nids = 0;
+
+ return( new );
+}
+
+IDList *
+idl_allids( backend *be )
+{
+ IDList *idl;
+
+ idl = idl_alloc( 0 );
+ idl->b_nmax = ALLIDSBLOCK;
+ idl->b_nids = next_id_get( be );
+
+ return( idl );
+}
+
+void
+idl_free( IDList *idl ) /* JCM - pass in ** */
+{
+ if ( idl == NULL ) {
+ return;
+ }
+
+ slapi_ch_free((void**)&idl );
+}
+
+
+/*
+ * idl_append - append an id to an id list.
+ *
+ * Warning: The ID List must be maintained in order.
+ * Use idl_insert if the id may not
+ *
+ * returns
+ * 0 - appended
+ * 1 - already in there
+ * 2 - not enough room
+ */
+
+int
+idl_append( IDList *idl, ID id)
+{
+ if ( ALLIDS( idl ) || ( (idl->b_nids) && (idl->b_ids[idl->b_nids - 1] == id)) ) {
+ return( 1 ); /* already there */
+ }
+
+ if ( idl->b_nids == idl->b_nmax ) {
+ return( 2 ); /* not enough room */
+ }
+
+ idl->b_ids[idl->b_nids] = id;
+ idl->b_nids++;
+
+ return( 0 );
+}
+
+static IDList *
+idl_dup( IDList *idl )
+{
+ IDList *new;
+
+ if ( idl == NULL ) {
+ return( NULL );
+ }
+
+ new = idl_alloc( idl->b_nmax );
+ SAFEMEMCPY( (char *) new, (char *) idl, (idl->b_nmax + 2)
+ * sizeof(ID) );
+
+ return( new );
+}
+
+static IDList *
+idl_min( IDList *a, IDList *b )
+{
+ return( a->b_nids > b->b_nids ? b : a );
+}
+
+/*
+ * idl_intersection - return a intersection b
+ */
+
+IDList *
+idl_intersection(
+ backend *be,
+ IDList *a,
+ IDList *b
+)
+{
+ NIDS ai, bi, ni;
+ IDList *n;
+
+ if ( a == NULL || b == NULL ) {
+ return( NULL );
+ }
+ if ( ALLIDS( a ) ) {
+ slapi_be_set_flag(be, SLAPI_BE_FLAG_DONT_BYPASS_FILTERTEST);
+ return( idl_dup( b ) );
+ }
+ if ( ALLIDS( b ) ) {
+ slapi_be_set_flag(be, SLAPI_BE_FLAG_DONT_BYPASS_FILTERTEST);
+ return( idl_dup( a ) );
+ }
+
+ n = idl_dup( idl_min( a, b ) );
+
+ for ( ni = 0, ai = 0, bi = 0; ai < a->b_nids; ai++ ) {
+ for ( ; bi < b->b_nids && b->b_ids[bi] < a->b_ids[ai]; bi++ )
+ ; /* NULL */
+
+ if ( bi == b->b_nids ) {
+ break;
+ }
+
+ if ( b->b_ids[bi] == a->b_ids[ai] ) {
+ n->b_ids[ni++] = a->b_ids[ai];
+ }
+ }
+
+ if ( ni == 0 ) {
+ idl_free( n );
+ return( NULL );
+ }
+ n->b_nids = ni;
+
+ return( n );
+}
+
+/*
+ * idl_union - return a union b
+ */
+
+IDList *
+idl_union(
+ backend *be,
+ IDList *a,
+ IDList *b
+)
+{
+ NIDS ai, bi, ni;
+ IDList *n;
+
+ if ( a == NULL ) {
+ return( idl_dup( b ) );
+ }
+ if ( b == NULL ) {
+ return( idl_dup( a ) );
+ }
+ if ( ALLIDS( a ) || ALLIDS( b ) ) {
+ return( idl_allids( be ) );
+ }
+
+ if ( b->b_nids < a->b_nids ) {
+ n = a;
+ a = b;
+ b = n;
+ }
+
+ n = idl_alloc( a->b_nids + b->b_nids );
+
+ for ( ni = 0, ai = 0, bi = 0; ai < a->b_nids && bi < b->b_nids; ) {
+ if ( a->b_ids[ai] < b->b_ids[bi] ) {
+ n->b_ids[ni++] = a->b_ids[ai++];
+ } else if ( b->b_ids[bi] < a->b_ids[ai] ) {
+ n->b_ids[ni++] = b->b_ids[bi++];
+ } else {
+ n->b_ids[ni++] = a->b_ids[ai];
+ ai++, bi++;
+ }
+ }
+
+ for ( ; ai < a->b_nids; ai++ ) {
+ n->b_ids[ni++] = a->b_ids[ai];
+ }
+ for ( ; bi < b->b_nids; bi++ ) {
+ n->b_ids[ni++] = b->b_ids[bi];
+ }
+ n->b_nids = ni;
+
+ return( n );
+}
+
+/*
+ * idl_notin - return a intersection ~b (or a minus b)
+ * DB --- changed the interface of this function (no code called it),
+ * such that it can modify IDL a in place (it'll always be the same
+ * or smaller than the a passed in if not allids).
+ * If a new list is generated, it's returned in new_result and the function
+ * returns 1. Otherwise the result remains in a, and the function returns 0.
+ * The intention is to optimize for the interesting case in filterindex.c
+ * where we are computing foo AND NOT bar, and both foo and bar are not allids.
+ */
+
+int
+idl_notin(
+ backend *be,
+ IDList *a,
+ IDList *b,
+ IDList **new_result
+)
+{
+ NIDS ni, ai, bi;
+ IDList *n;
+ *new_result = NULL;
+
+ if ( a == NULL ) {
+ return( 0 );
+ }
+ if ( b == NULL || ALLIDS( b ) ) {
+ *new_result = idl_dup( a );
+ return( 1 );
+ }
+
+ if ( ALLIDS( a ) ) { /* Not convinced that this code is really worth it */
+ /* It's trying to do allids notin b, where maxid is smaller than some size */
+ n = idl_alloc( SLAPD_LDBM_MIN_MAXIDS );
+ ni = 0;
+
+ for ( ai = 1, bi = 0; ai < a->b_nids && ni < n->b_nmax &&
+ bi < b->b_nmax; ai++ ) {
+ if ( b->b_ids[bi] == ai ) {
+ bi++;
+ } else {
+ n->b_ids[ni++] = ai;
+ }
+ }
+
+ for ( ; ai < a->b_nids && ni < n->b_nmax; ai++ ) {
+ n->b_ids[ni++] = ai;
+ }
+
+ if ( ni == n->b_nmax ) {
+ idl_free( n );
+ *new_result = idl_allids( be );
+ } else {
+ n->b_nids = ni;
+ *new_result = n;
+ }
+ return( 1 );
+ }
+
+ /* This is the case we're interested in, we want to detect where a and b don't overlap */
+ {
+ size_t ahii, aloi, bhii, bloi;
+ size_t ahi, alo, bhi, blo;
+ int aloblo, ahiblo, alobhi, ahibhi;
+
+ aloi = bloi = 0;
+ ahii = a->b_nids - 1;
+ bhii = b->b_nids - 1;
+
+ ahi = a->b_ids[ahii];
+ alo = a->b_ids[aloi];
+ bhi = b->b_ids[bhii];
+ blo = b->b_ids[bloi];
+ /* if the ranges don't overlap, we're done, current a is the result */
+ aloblo = alo < blo;
+ ahiblo = ahi < blo;
+ alobhi = ahi > bhi;
+ ahibhi = alo > bhi;
+ if ( (aloblo & ahiblo) || (alobhi & ahibhi) ) {
+ return 0;
+ } else {
+ /* Do what we did before */
+ n = idl_dup( a );
+
+ ni = 0;
+ for ( ai = 0, bi = 0; ai < a->b_nids; ai++ ) {
+ for ( ; bi < b->b_nids && b->b_ids[bi] < a->b_ids[ai];
+ bi++ ) {
+ ; /* NULL */
+ }
+
+ if ( bi == b->b_nids ) {
+ break;
+ }
+
+ if ( b->b_ids[bi] != a->b_ids[ai] ) {
+ n->b_ids[ni++] = a->b_ids[ai];
+ }
+ }
+
+ for ( ; ai < a->b_nids; ai++ ) {
+ n->b_ids[ni++] = a->b_ids[ai];
+ }
+ n->b_nids = ni;
+
+ *new_result = n;
+ return( 1 );
+ }
+ }
+}
+
+ID
+idl_firstid( IDList *idl )
+{
+ if ( idl == NULL || idl->b_nids == 0 ) {
+ return( NOID );
+ }
+
+ if ( ALLIDS( idl ) ) {
+ return( idl->b_nids == 1 ? NOID : 1 );
+ }
+
+ return( idl->b_ids[0] );
+}
+
+ID
+idl_nextid( IDList *idl, ID id )
+{
+ NIDS i;
+
+ if ( ALLIDS( idl ) ) {
+ return( ++id < idl->b_nids ? id : NOID );
+ }
+
+ for ( i = 0; i < idl->b_nids && idl->b_ids[i] < id; i++ ) {
+ ; /* NULL */
+ }
+ i++;
+
+ if ( i >= idl->b_nids ) {
+ return( NOID );
+ } else {
+ return( idl->b_ids[i] );
+ }
+}
+
+/* Make an ID list iterator */
+idl_iterator idl_iterator_init(const IDList *idl)
+{
+ return (idl_iterator) 0;
+}
+
+idl_iterator idl_iterator_increment(idl_iterator *i)
+{
+ size_t t = (size_t) *i;
+ t += 1;
+ *i = (idl_iterator) t;
+ return *i;
+}
+
+idl_iterator idl_iterator_decrement(idl_iterator *i)
+{
+ size_t t = (size_t) *i;
+ t -= 1;
+ *i = (idl_iterator) t;
+ return *i;
+}
+
+ID idl_iterator_dereference(idl_iterator i, const IDList *idl)
+{
+ if ( (NULL == idl) || (i >= idl->b_nids)) {
+ return NOID;
+ }
+ if (ALLIDS(idl)) {
+ return (ID) i + 1;
+ } else {
+ return idl->b_ids[i];
+ }
+}
+
+ID idl_iterator_dereference_increment(idl_iterator *i, const IDList *idl)
+{
+ ID t = idl_iterator_dereference(*i,idl);
+ idl_iterator_increment(i);
+ return t;
+}
+
diff --git a/ldap/servers/slapd/back-ldbm/idl_new.c b/ldap/servers/slapd/back-ldbm/idl_new.c
new file mode 100644
index 00000000..d2038261
--- /dev/null
+++ b/ldap/servers/slapd/back-ldbm/idl_new.c
@@ -0,0 +1,671 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+
+/* New IDL code for new indexing scheme */
+
+/* Note to future editors:
+ This file is now full of redundant code
+ (the DB_ALLIDS_ON_WRITE==true and DB_USE_BULK_FETCH==false code).
+ It should be stripped out at the beginning of a
+ major release cycle.
+ */
+
+#include "back-ldbm.h"
+
+static char* filename = "idl_new.c";
+
+/* Bulk fetch feature first in DB 3.3 */
+#if 1000*DB_VERSION_MAJOR + 100*DB_VERSION_MINOR >= 3300
+#define DB_USE_BULK_FETCH 1
+#define BULK_FETCH_BUFFER_SIZE (8*1024)
+#else
+#undef DB_USE_BULK_FETCH
+#endif
+
+/* We used to implement allids for inserts, but that's a bad idea.
+ Why ? Because:
+ 1) Allids results in hard to understand query behavior.
+ 2) The get() calls needed to check for allids on insert cost performance.
+ 3) Tests show that there is no significant performance benefit to having allids on writes,
+ either for updates or searches.
+ Set this to revert to that code */
+/* #undef DB_ALLIDS_ON_WRITE */
+/* We still enforce allids threshold on reads, to save time and space fetching vast id lists */
+#define DB_ALLIDS_ON_READ 1
+
+#if !defined(DB_NEXT_DUP)
+#define DB_NEXT_DUP 0
+#endif
+#if !defined(DB_GET_BOTH)
+#define DB_GET_BOTH 0
+#endif
+
+/* Structure used to hide private idl-specific data in the attrinfo object */
+struct idl_private {
+ size_t idl_allidslimit;
+ int dummy;
+};
+
+static int idl_tune = DEFAULT_IDL_TUNE; /* tuning parameters for IDL code */
+/* Currently none for new IDL code */
+
+#if defined(DB_ALLIDS_ON_WRITE)
+static int idl_new_store_allids(backend *be, DB *db, DBT *key, DB_TXN *txn);
+#endif
+
+void idl_new_set_tune(int val)
+{
+ idl_tune = val;
+}
+
+int idl_new_get_tune() {
+ return idl_tune;
+}
+
+/* Append an ID to an IDL, realloc-ing the space if needs be */
+/* ID presented is not to be already in the IDL. */
+static int
+idl_append_extend( IDList **orig_idl, ID id)
+{
+ IDList *idl = *orig_idl;
+
+ if (idl == NULL) {
+ idl = idl_alloc(1);
+ idl_append(idl, id);
+
+ *orig_idl = idl;
+ return 0;
+ }
+
+ if ( idl->b_nids == idl->b_nmax ) {
+ size_t x = 0;
+ /* No more room, need to extend */
+ /* Allocate new IDL with twice the space of this one */
+ IDList *idl_new = NULL;
+ idl_new = idl_alloc(idl->b_nmax * 2);
+ if (NULL == idl_new) {
+ return ENOMEM;
+ }
+ /* copy over the existing contents */
+ idl_new->b_nids = idl->b_nids;
+ for (x = 0; x < idl->b_nids;x++) {
+ idl_new->b_ids[x] = idl->b_ids[x];
+ }
+ idl_free(idl);
+ idl = idl_new;
+ }
+
+ idl->b_ids[idl->b_nids] = id;
+ idl->b_nids++;
+ *orig_idl = idl;
+
+ return 0;
+}
+
+size_t idl_new_get_allidslimit(struct attrinfo *a)
+{
+ idl_private *priv = NULL;
+
+ PR_ASSERT(NULL != a);
+ PR_ASSERT(NULL != a->ai_idl);
+
+ priv = a->ai_idl;
+
+ return priv->idl_allidslimit;
+}
+
+/* routine to initialize the private data used by the IDL code per-attribute */
+int idl_new_init_private(backend *be,struct attrinfo *a)
+{
+ idl_private *priv = NULL;
+ struct ldbminfo *li = (struct ldbminfo *)be->be_database->plg_private;
+
+ PR_ASSERT(NULL != a);
+ PR_ASSERT(NULL == a->ai_idl);
+
+ priv = (idl_private*) slapi_ch_calloc(sizeof(idl_private),1);
+ if (NULL == priv) {
+ return -1; /* Memory allocation failure */
+ }
+ priv->idl_allidslimit = li->li_allidsthreshold;
+ /* Initialize the structure */
+ a->ai_idl = (void*)priv;
+ return 0;
+}
+
+/* routine to release resources used by IDL private data structure */
+int idl_new_release_private(struct attrinfo *a)
+{
+ PR_ASSERT(NULL != a);
+ if (NULL != a->ai_idl)
+ {
+ slapi_ch_free((void**)&(a->ai_idl) );
+ }
+ return 0;
+}
+
+IDList * idl_new_fetch(
+ backend *be,
+ DB* db,
+ DBT *inkey,
+ DB_TXN *txn,
+ struct attrinfo *a,
+ int *flag_err
+)
+{
+ int ret = 0;
+ DBC *cursor = NULL;
+ IDList *idl = NULL;
+ DBT key = {0};
+ DBT data = {0};
+ ID id = 0;
+ size_t count = 0;
+#ifdef DB_USE_BULK_FETCH
+ /* beware that a large buffer on the stack might cause a stack overflow on some platforms */
+ char buffer[BULK_FETCH_BUFFER_SIZE];
+ void *ptr;
+ DBT dataret = {0};
+#endif
+
+ if (NEW_IDL_NOOP == *flag_err)
+ {
+ *flag_err = 0;
+ return NULL;
+ }
+
+ /* Make a cursor */
+ ret = db->cursor(db,txn,&cursor,0);
+ if (0 != ret) {
+ ldbm_nasty(filename,1,ret);
+ cursor = NULL;
+ goto error;
+ }
+#ifdef DB_USE_BULK_FETCH
+ data.ulen = sizeof(buffer);
+ data.size = sizeof(buffer);
+ data.data = buffer;
+ data.flags = DB_DBT_USERMEM;
+#else
+ data.ulen = sizeof(id);
+ data.size = sizeof(id);
+ data.data = &id;
+ data.flags = DB_DBT_USERMEM;
+#endif
+
+ /*
+ * We're not expecting the key to change in value
+ * so we can just use the input key as a buffer.
+ * This avoids memory management of the key.
+ */
+ key.ulen = inkey->size;
+ key.size = inkey->size;
+ key.data = inkey->data;
+ key.flags = DB_DBT_USERMEM;
+
+ /* Position cursor at the first matching key */
+#ifdef DB_USE_BULK_FETCH
+ ret = cursor->c_get(cursor,&key,&data,DB_SET|DB_MULTIPLE);
+#else
+ ret = cursor->c_get(cursor,&key,&data,DB_SET);
+#endif
+ if (0 != ret) {
+ if (DB_NOTFOUND == ret) {
+ ret = 0;
+ } else {
+#ifdef DB_USE_BULK_FETCH
+ if (ret == ENOMEM) {
+ LDAPDebug(LDAP_DEBUG_ANY, "database index is corrupt; "
+ "data item for key %s is too large for our buffer "
+ "(need=%d actual=%d)\n",
+ key.data, data.size, data.ulen);
+ }
+#endif
+ ldbm_nasty(filename,2,ret);
+ }
+ goto error; /* Not found is OK, return NULL IDL */
+ }
+
+ /* Iterate over the duplicates, amassing them into an IDL */
+#ifdef DB_USE_BULK_FETCH
+ for (;;) {
+
+ DB_MULTIPLE_INIT(ptr, &data);
+
+ for (;;) {
+ DB_MULTIPLE_NEXT(ptr, &data, dataret.data, dataret.size);
+ if (dataret.data == NULL) break;
+ if (ptr == NULL) break;
+
+ if (dataret.size != sizeof(ID)) {
+ LDAPDebug(LDAP_DEBUG_ANY, "database index is corrupt; "
+ "key %s has a data item with the wrong size (%d)\n",
+ key.data, dataret.size, 0);
+ goto error;
+ }
+ memcpy(&id, dataret.data, sizeof(ID));
+
+ /* we got another ID, add it to our IDL */
+ idl_append_extend(&idl, id);
+
+ count++;
+ }
+
+ LDAPDebug(LDAP_DEBUG_TRACE, "bulk fetch buffer nids=%d\n", count, 0, 0);
+
+ ret = cursor->c_get(cursor,&key,&data,DB_NEXT_DUP|DB_MULTIPLE);
+ if (0 != ret) {
+ break;
+ }
+#if defined(DB_ALLIDS_ON_READ)
+ /* enforce the allids read limit */
+ if (NEW_IDL_NO_ALLID != *flag_err &&
+ NULL != a && count > idl_new_get_allidslimit(a)) {
+ idl->b_nids = 1;
+ idl->b_ids[0] = ALLID;
+ ret = DB_NOTFOUND; /* fool the code below into thinking that we finished the dups */
+ break;
+ }
+#endif
+ }
+#else
+ for (;;) {
+ ret = cursor->c_get(cursor,&key,&data,DB_NEXT_DUP);
+ count++;
+ if (0 != ret) {
+ break;
+ }
+ /* we got another ID, add it to our IDL */
+ idl_append_extend(&idl, id);
+#if defined(DB_ALLIDS_ON_READ)
+ /* enforce the allids read limit */
+ if (count > idl_new_get_allidslimit(a)) {
+ idl->b_nids = 1;
+ idl->b_ids[0] = ALLID;
+ ret = DB_NOTFOUND; /* fool the code below into thinking that we finished the dups */
+ break;
+ }
+#endif
+ }
+#endif
+
+ if (ret != DB_NOTFOUND) {
+ idl_free(idl); idl = NULL;
+ ldbm_nasty(filename,59,ret);
+ goto error;
+ }
+
+ ret = 0;
+
+ /* check for allids value */
+ if (idl != NULL && idl->b_nids == 1 && idl->b_ids[0] == ALLID) {
+ idl_free(idl);
+ idl = idl_allids(be);
+ LDAPDebug(LDAP_DEBUG_TRACE, "idl_new_fetch %s returns allids\n",
+ key.data, 0, 0);
+ } else {
+ LDAPDebug(LDAP_DEBUG_TRACE, "idl_new_fetch %s returns nids=%lu\n",
+ key.data, (u_long)IDL_NIDS(idl), 0);
+ }
+
+error:
+ /* Close the cursor */
+ if (NULL != cursor) {
+ if (0 != cursor->c_close(cursor)) {
+ ldbm_nasty(filename,3,ret);
+ }
+ }
+ *flag_err = ret;
+ return idl;
+}
+
+int idl_new_insert_key(
+ backend *be,
+ DB* db,
+ DBT *key,
+ ID id,
+ DB_TXN *txn,
+ struct attrinfo *a,
+ int *disposition
+)
+{
+ int ret = 0;
+ DBT data = {0};
+
+#if defined(DB_ALLIDS_ON_WRITE)
+ DBC *cursor = NULL;
+ db_recno_t count;
+ ID tmpid = 0;
+ /* Make a cursor */
+ ret = db->cursor(db,txn,&cursor,0);
+ if (0 != ret) {
+ ldbm_nasty(filename,58,ret);
+ cursor = NULL;
+ goto error;
+ }
+ data.ulen = sizeof(id);
+ data.size = sizeof(id);
+ data.flags = DB_DBT_USERMEM;
+ data.data = &tmpid;
+ ret = cursor->c_get(cursor,key,&data,DB_SET);
+ if (0 == ret) {
+ if (tmpid == ALLID) {
+ if (NULL != disposition) {
+ *disposition = IDL_INSERT_ALLIDS;
+ }
+ goto error; /* allid: don't bother inserting any more */
+ }
+ } else if (DB_NOTFOUND != ret) {
+ ldbm_nasty(filename,12,ret);
+ goto error;
+ }
+ if (NULL != disposition) {
+ *disposition = IDL_INSERT_NORMAL;
+ }
+
+ data.data = &id;
+
+ /* insert it */
+ ret = cursor->c_put(cursor, key, &data, DB_NODUPDATA);
+ if (0 != ret) {
+ if (DB_KEYEXIST == ret) {
+ /* this is okay */
+ ret = 0;
+ } else {
+ ldbm_nasty(filename,50,ret);
+ }
+ } else {
+ /* check for allidslimit exceeded in database */
+ if (cursor->c_count(cursor, &count, 0) != 0) {
+ LDAPDebug(LDAP_DEBUG_ANY, "could not obtain count for key %s\n",
+ key->data, 0, 0);
+ goto error;
+ }
+ if ((size_t)count > idl_new_get_allidslimit(a)) {
+ LDAPDebug(LDAP_DEBUG_TRACE, "allidslimit exceeded for key %s\n",
+ key->data, 0, 0);
+ cursor->c_close(cursor);
+ cursor = NULL;
+ if ((ret = idl_new_store_allids(be, db, key, txn)) == 0) {
+ if (NULL != disposition) {
+ *disposition = IDL_INSERT_NOW_ALLIDS;
+ }
+ }
+ }
+error:
+ /* Close the cursor */
+ if (NULL != cursor) {
+ if (0 != cursor->c_close(cursor)) {
+ ldbm_nasty(filename,56,ret);
+ }
+ }
+#else
+ data.ulen = sizeof(id);
+ data.size = sizeof(id);
+ data.flags = DB_DBT_USERMEM;
+ data.data = &id;
+
+ if (NULL != disposition) {
+ *disposition = IDL_INSERT_NORMAL;
+ }
+
+ ret = db->put(db, txn, key, &data, DB_NODUPDATA);
+ if (0 != ret) {
+ if (DB_KEYEXIST == ret) {
+ /* this is okay */
+ ret = 0;
+ } else {
+ ldbm_nasty(filename,50,ret);
+ }
+ }
+#endif
+
+
+ return ret;
+}
+
+int idl_new_delete_key(
+ backend *be,
+ DB *db,
+ DBT *key,
+ ID id,
+ DB_TXN *txn,
+ struct attrinfo *a
+)
+{
+ int ret = 0;
+ DBC *cursor = NULL;
+ DBT data = {0};
+ ID tmpid = 0;
+
+ /* Make a cursor */
+ ret = db->cursor(db,txn,&cursor,0);
+ if (0 != ret) {
+ ldbm_nasty(filename,21,ret);
+ cursor = NULL;
+ goto error;
+ }
+ data.ulen = sizeof(id);
+ data.size = sizeof(id);
+ data.flags = DB_DBT_USERMEM;
+ data.data = &tmpid;
+ ret = cursor->c_get(cursor,key,&data,DB_SET);
+ if (0 == ret) {
+ if (tmpid == ALLID) {
+ goto error; /* allid: never delete it */
+ }
+ } else if (DB_NOTFOUND != ret) {
+ ldbm_nasty(filename,22,ret);
+ goto error;
+ }
+
+ /* Position cursor at the key, value pair */
+ data.data = &id;
+ ret = cursor->c_get(cursor,key,&data,DB_GET_BOTH);
+ if (0 != ret) {
+ if (DB_NOTFOUND == ret) {
+ ret = 0; /* Not Found is OK, return immediately */
+ } else {
+ ldbm_nasty(filename,23,ret);
+ }
+ goto error;
+ }
+ /* We found it, so delete it */
+ ret = cursor->c_del(cursor,0);
+error:
+ /* Close the cursor */
+ if (NULL != cursor) {
+ if (0 != cursor->c_close(cursor)) {
+ ldbm_nasty(filename,24,ret);
+ }
+ }
+ return ret;
+}
+
+#if defined(DB_ALLIDS_ON_WRITE)
+static int idl_new_store_allids(backend *be, DB *db, DBT *key, DB_TXN *txn)
+{
+ int ret = 0;
+ DBC *cursor = NULL;
+ DBT data = {0};
+ ID id = 0;
+
+ /* Make a cursor */
+ ret = db->cursor(db,txn,&cursor,0);
+ if (0 != ret) {
+ ldbm_nasty(filename,31,ret);
+ cursor = NULL;
+ goto error;
+ }
+ data.ulen = sizeof(ID);
+ data.size = sizeof(ID);
+ data.data = &id;
+ data.flags = DB_DBT_USERMEM;
+
+ /* Position cursor at the key */
+ ret = cursor->c_get(cursor,key,&data,DB_SET);
+ if (ret == 0) {
+ /* We found it, so delete all duplicates */
+ ret = cursor->c_del(cursor,0);
+ while (0 == ret) {
+ ret = cursor->c_get(cursor,key,&data,DB_NEXT_DUP);
+ if (0 != ret) {
+ break;
+ }
+ ret = cursor->c_del(cursor,0);
+ }
+ if (0 != ret && DB_NOTFOUND != ret) {
+ ldbm_nasty(filename,54,ret);
+ goto error;
+ } else {
+ ret = 0;
+ }
+ } else {
+ if (DB_NOTFOUND == ret) {
+ ret = 0; /* Not Found is OK */
+ } else {
+ ldbm_nasty(filename,32,ret);
+ goto error;
+ }
+ }
+
+ /* store the ALLID value */
+ id = ALLID;
+ ret = cursor->c_put(cursor, key, &data, DB_NODUPDATA);
+ if (0 != ret) {
+ ldbm_nasty(filename,53,ret);
+ goto error;
+ }
+
+ LDAPDebug(LDAP_DEBUG_TRACE, "key %s has been set to allids\n",
+ key->data, 0, 0);
+
+error:
+ /* Close the cursor */
+ if (NULL != cursor) {
+ if (0 != cursor->c_close(cursor)) {
+ ldbm_nasty(filename,33,ret);
+ }
+ }
+ return ret;
+ /* If this function is called in "no-allids" mode, then it's a bug */
+ ldbm_nasty(filename,63,0);
+ return -1;
+}
+#endif
+
+int idl_new_store_block(
+ backend *be,
+ DB *db,
+ DBT *key,
+ IDList *idl,
+ DB_TXN *txn,
+ struct attrinfo *a
+)
+{
+ int ret = 0;
+ DBC *cursor = NULL;
+ DBT data = {0};
+ ID id = 0;
+ size_t x = 0;
+#if defined(DB_ALLIDS_ON_WRITE)
+ db_recno_t count;
+#endif
+
+ if (NULL == idl)
+ {
+ return ret;
+ }
+
+ /*
+ * Really we need an extra entry point to the DB here, which
+ * inserts a list of duplicate keys. In the meantime, we'll
+ * just do it by brute force.
+ */
+
+#if defined(DB_ALLIDS_ON_WRITE)
+ /* allids check on input idl */
+ if (ALLIDS(idl) || (idl->b_nids > (ID)idl_new_get_allidslimit(a))) {
+ return idl_new_store_allids(be, db, key, txn);
+ }
+#endif
+
+ /* Make a cursor */
+ ret = db->cursor(db,txn,&cursor,0);
+ if (0 != ret) {
+ ldbm_nasty(filename,41,ret);
+ cursor = NULL;
+ goto error;
+ }
+
+ /* initialize data DBT */
+ data.data = &id;
+ data.ulen = sizeof(id);
+ data.size = sizeof(id);
+ data.flags = DB_DBT_USERMEM;
+
+ /* Position cursor at the key, value pair */
+ ret = cursor->c_get(cursor,key,&data,DB_GET_BOTH);
+ if (ret == DB_NOTFOUND) {
+ ret = 0;
+ } else if (ret != 0) {
+ ldbm_nasty(filename,47,ret);
+ goto error;
+ }
+
+ /* Iterate over the IDs in the idl */
+ for (x = 0; x < idl->b_nids; x++) {
+ /* insert an id */
+ id = idl->b_ids[x];
+ ret = cursor->c_put(cursor, key, &data, DB_NODUPDATA);
+ if (0 != ret) {
+ if (DB_KEYEXIST == ret) {
+ ret = 0; /* exist is okay */
+ } else {
+ ldbm_nasty(filename,48,ret);
+ goto error;
+ }
+ }
+ }
+#if defined(DB_ALLIDS_ON_WRITE)
+ /* check for allidslimit exceeded in database */
+ if (cursor->c_count(cursor, &count, 0) != 0) {
+ LDAPDebug(LDAP_DEBUG_ANY, "could not obtain count for key %s\n",
+ key->data, 0, 0);
+ goto error;
+ }
+ if ((size_t)count > idl_new_get_allidslimit(a)) {
+ LDAPDebug(LDAP_DEBUG_TRACE, "allidslimit exceeded for key %s\n",
+ key->data, 0, 0);
+ cursor->c_close(cursor);
+ cursor = NULL;
+ ret = idl_new_store_allids(be, db, key, txn);
+ }
+#endif
+
+error:
+ /* Close the cursor */
+ if (NULL != cursor) {
+ if (0 != cursor->c_close(cursor)) {
+ ldbm_nasty(filename,49,ret);
+ }
+ }
+ return ret;
+}
+
+/* idl_new_compare_dups: comparing ID, pass to libdb for callback */
+int idl_new_compare_dups(
+#if 1000*DB_VERSION_MAJOR + 100*DB_VERSION_MINOR >= 3200
+ DB *db,
+#endif
+ const DBT *a,
+ const DBT *b
+)
+{
+ ID a_copy, b_copy;
+ memmove(&a_copy, a->data, sizeof(ID));
+ memmove(&b_copy, b->data, sizeof(ID));
+ return a_copy - b_copy;
+}
diff --git a/ldap/servers/slapd/back-ldbm/idl_shim.c b/ldap/servers/slapd/back-ldbm/idl_shim.c
new file mode 100644
index 00000000..2332ce1b
--- /dev/null
+++ b/ldap/servers/slapd/back-ldbm/idl_shim.c
@@ -0,0 +1,124 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+/* Shim which forwards IDL calls to the appropriate implementation */
+
+#include "back-ldbm.h"
+
+static int idl_new = 0; /* non-zero if we're doing new IDL style */
+
+
+void idl_old_set_tune(int val);
+int idl_old_get_tune();
+int idl_old_init_private(backend *be, struct attrinfo *a);
+int idl_old_release_private(struct attrinfo *a);
+size_t idl_old_get_allidslimit(struct attrinfo *a);
+IDList * idl_old_fetch( backend *be, DB* db, DBT *key, DB_TXN *txn, struct attrinfo *a, int *err );
+int idl_old_insert_key( backend *be, DB* db, DBT *key, ID id, DB_TXN *txn, struct attrinfo *a,int *disposition );
+int idl_old_delete_key( backend *be, DB *db, DBT *key, ID id, DB_TXN *txn, struct attrinfo *a );
+int idl_old_store_block( backend *be,DB *db,DBT *key,IDList *idl,DB_TXN *txn,struct attrinfo *a);
+
+
+void idl_new_set_tune(int val);
+int idl_new_get_tune();
+int idl_new_init_private(backend *be, struct attrinfo *a);
+int idl_new_release_private(struct attrinfo *a);
+size_t idl_new_get_allidslimit(struct attrinfo *a);
+IDList * idl_new_fetch( backend *be, DB* db, DBT *key, DB_TXN *txn, struct attrinfo *a, int *err );
+int idl_new_insert_key( backend *be, DB* db, DBT *key, ID id, DB_TXN *txn, struct attrinfo *a,int *disposition );
+int idl_new_delete_key( backend *be, DB *db, DBT *key, ID id, DB_TXN *txn, struct attrinfo *a );
+int idl_new_store_block( backend *be,DB *db,DBT *key,IDList *idl,DB_TXN *txn,struct attrinfo *a);
+
+int idl_get_idl_new()
+{
+ return idl_new;
+}
+
+void idl_set_tune(int val)
+{
+ /* Catch idl_tune requests to use new idl code */
+ if (4096 == val) {
+ idl_new = 1;
+ } else {
+ idl_new = 0;
+ }
+ if (idl_new) {
+ idl_new_set_tune(val);
+ } else {
+ idl_old_set_tune(val);
+ }
+}
+
+int idl_get_tune()
+{
+ if (idl_new) {
+ return idl_new_get_tune();
+ } else {
+ return idl_old_get_tune();
+ }
+}
+
+int idl_init_private(backend *be, struct attrinfo *a)
+{
+ if (idl_new) {
+ return idl_new_init_private(be,a);
+ } else {
+ return idl_old_init_private(be,a);
+ }
+}
+
+int idl_release_private(struct attrinfo *a)
+{
+ if (idl_new) {
+ return idl_new_release_private(a);
+ } else {
+ return idl_old_release_private(a);
+ }
+}
+
+size_t idl_get_allidslimit(struct attrinfo *a)
+{
+ if (idl_new) {
+ return idl_new_get_allidslimit(a);
+ } else {
+ return idl_old_get_allidslimit(a);
+ }
+}
+
+IDList * idl_fetch( backend *be, DB* db, DBT *key, DB_TXN *txn, struct attrinfo *a, int *err )
+{
+ if (idl_new) {
+ return idl_new_fetch(be,db,key,txn,a,err);
+ } else {
+ return idl_old_fetch(be,db,key,txn,a,err);
+ }
+}
+
+int idl_insert_key( backend *be, DB* db, DBT *key, ID id, DB_TXN *txn, struct attrinfo *a,int *disposition )
+{
+ if (idl_new) {
+ return idl_new_insert_key(be,db,key,id,txn,a,disposition);
+ } else {
+ return idl_old_insert_key(be,db,key,id,txn,a,disposition);
+ }
+}
+
+int idl_delete_key(backend *be, DB *db, DBT *key, ID id, DB_TXN *txn, struct attrinfo *a )
+{
+ if (idl_new) {
+ return idl_new_delete_key(be,db,key,id,txn,a);
+ } else {
+ return idl_old_delete_key(be,db,key,id,txn,a);
+ }
+}
+
+int idl_store_block(backend *be,DB *db,DBT *key,IDList *idl,DB_TXN *txn,struct attrinfo *a)
+{
+ if (idl_new) {
+ return idl_new_store_block(be,db,key,idl,txn,a);
+ } else {
+ return idl_old_store_block(be,db,key,idl,txn,a);
+ }
+}
diff --git a/ldap/servers/slapd/back-ldbm/idlapi.h b/ldap/servers/slapd/back-ldbm/idlapi.h
new file mode 100644
index 00000000..39fe9c15
--- /dev/null
+++ b/ldap/servers/slapd/back-ldbm/idlapi.h
@@ -0,0 +1,30 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+
+#ifndef _IDL_API_H_
+#define _IDL_API_H_
+
+/* mechanics */
+
+typedef IDList *(*api_idl_alloc)( NIDS nids );
+typedef void (*api_idl_insert)(IDList **idl, ID id);
+
+/* API ID for slapi_apib_get_interface */
+
+#define IDL_v1_0_GUID "ec228d97-971d-4b9e-91b5-4f90e1841f24"
+
+/* API */
+
+/* the api broker reserves api[0] for its use */
+
+#define IDList_alloc(api, nids) \
+ ((api_idl_alloc*)(api))[1](nids)
+
+#define IDList_insert(api, idl, id) \
+ ((api_idl_insert*)(api))[2](idl, id)
+
+
+#endif /*_IDL_API_H_*/
diff --git a/ldap/servers/slapd/back-ldbm/import-merge.c b/ldap/servers/slapd/back-ldbm/import-merge.c
new file mode 100644
index 00000000..b50aaaec
--- /dev/null
+++ b/ldap/servers/slapd/back-ldbm/import-merge.c
@@ -0,0 +1,680 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+/*
+ * this is a bunch of routines for merging groups of db files together --
+ * currently it's only used for imports (when we import into several small
+ * db sets for speed, then merge them).
+ */
+
+#include "back-ldbm.h"
+#include "import.h"
+
+struct _import_merge_thang
+{
+ int type;
+#define IMPORT_MERGE_THANG_IDL 1 /* Values for type */
+#define IMPORT_MERGE_THANG_VLV 2
+ union {
+ IDList *idl; /* if type == IMPORT_MERGE_THANG_IDL */
+ DBT vlv_data; /* if type == IMPORT_MERGE_THANG_VLV */
+ } payload;
+};
+typedef struct _import_merge_thang import_merge_thang;
+
+struct _import_merge_queue_entry
+{
+ int *file_referenced_list;
+ import_merge_thang thang;
+ DBT key;
+ struct _import_merge_queue_entry *next;
+};
+typedef struct _import_merge_queue_entry import_merge_queue_entry;
+
+static int import_merge_get_next_thang(backend *be, DBC *cursor, DB *db, import_merge_thang *thang, DBT *key, int type)
+{
+ int ret = 0;
+ DBT value = {0};
+
+ value.flags = DB_DBT_MALLOC;
+ key->flags = DB_DBT_MALLOC;
+
+ thang->type = type;
+ if (IMPORT_MERGE_THANG_IDL == type) {
+ /* IDL case */
+ around:
+ ret = cursor->c_get(cursor, key, &value, DB_NEXT_NODUP);
+ if (0 == ret) {
+ /* Check that we've not reached the beginning of continuation
+ * blocks */
+ if (CONT_PREFIX != ((char*)key->data)[0]) {
+ /* If not, read the IDL using idl_fetch() */
+ key->flags = DB_DBT_REALLOC;
+ ret = NEW_IDL_NO_ALLID;
+ thang->payload.idl = idl_fetch(be, db, key, NULL, NULL, &ret);
+ PR_ASSERT(NULL != thang->payload.idl);
+ } else {
+ free(value.data);
+ free(key->data);
+ key->flags = DB_DBT_MALLOC;
+ goto around; /* Just skip these */
+ }
+ free(value.data);
+ } else {
+ if (DB_NOTFOUND == ret) {
+ /* This means that we're at the end of the file */
+ ret = EOF;
+ }
+ }
+ } else {
+ /* VLV case */
+ ret = cursor->c_get(cursor,key,&value,DB_NEXT);
+ if (0 == ret) {
+ thang->payload.vlv_data = value;
+ thang->payload.vlv_data.flags = 0;
+ key->flags = 0;
+ } else {
+ if (DB_NOTFOUND == ret) {
+ /* This means that we're at the end of the file */
+ ret = EOF;
+ }
+ }
+ }
+
+ return ret;
+}
+
+static import_merge_queue_entry *import_merge_make_new_queue_entry(import_merge_thang *thang, DBT *key, int fileno, int passes)
+{
+ /* Make a new entry */
+ import_merge_queue_entry *new_entry = (import_merge_queue_entry *)slapi_ch_calloc(1, sizeof(import_merge_queue_entry));
+
+ if (NULL == new_entry) {
+ return NULL;
+ }
+ new_entry->key = *key;
+ new_entry->thang = *thang;
+ new_entry->file_referenced_list =
+ (int *)slapi_ch_calloc(passes, sizeof(fileno));
+
+ if (NULL == new_entry->file_referenced_list) {
+ return NULL;
+ }
+ (new_entry->file_referenced_list)[fileno] = 1;
+ return new_entry;
+}
+
+/* Put an IDL onto the priority queue */
+static int import_merge_insert_input_queue(backend *be, import_merge_queue_entry **queue,int fileno, DBT *key, import_merge_thang *thang,int passes)
+{
+ /* Walk the list, looking for a key value which is greater than or equal
+ * to the presented key */
+ /* If an equal key is found, compute the union of the IDLs and store that
+ * back in the queue entry */
+ /* If a key greater than is found, or no key greater than is found, insert
+ * a new queue entry */
+ import_merge_queue_entry *current_entry = NULL;
+ import_merge_queue_entry *previous_entry = NULL;
+
+ PR_ASSERT(NULL != thang);
+ if (NULL == *queue) {
+ /* Queue was empty--- put ourselves at the head */
+ *queue = import_merge_make_new_queue_entry(thang,key,fileno,passes);
+ if (NULL == *queue) {
+ return -1;
+ }
+ } else {
+ for (current_entry = *queue; current_entry != NULL;
+ current_entry = current_entry->next) {
+ int cmp = strcmp(key->data,current_entry->key.data);
+
+ if (0 == cmp) {
+ if (IMPORT_MERGE_THANG_IDL == thang->type) { /* IDL case */
+ IDList *idl = thang->payload.idl;
+ /* Equal --- merge into the stored IDL, add file ID
+ * to the list */
+ IDList *new_idl =
+ idl_union(be, current_entry->thang.payload.idl, idl);
+
+ idl_free(current_entry->thang.payload.idl);
+ idl_free(idl);
+ current_entry->thang.payload.idl = new_idl;
+ /* Add this file id into the entry's referenced list */
+ (current_entry->file_referenced_list)[fileno] = 1;
+ /* Because we merged the entries, we no longer need the
+ * key, so free it */
+ free(key->data);
+ goto done;
+ } else {
+ /* VLV case, we can see exact keys, this is not a bug ! */
+ /* We want to ensure that they key read most recently is
+ * put later in the queue than any others though */
+ }
+ } else {
+ if (cmp < 0) {
+ /* We compare smaller than the stored key, so we should
+ * insert ourselves before this entry */
+ break;
+ } else {
+ /* We compare greater than this entry, so we should keep
+ * going */ ;
+ }
+ }
+ previous_entry = current_entry;
+ }
+
+ /* Now insert */
+ {
+ import_merge_queue_entry *new_entry =
+ import_merge_make_new_queue_entry(thang, key, fileno, passes);
+
+ if (NULL == new_entry) {
+ return -1;
+ }
+
+ /* If not, then we must need to insert ourselves after the last
+ * entry */
+ new_entry->next = current_entry;
+ if (NULL == previous_entry) {
+ *queue = new_entry;
+ } else {
+ previous_entry->next = new_entry;
+ }
+ }
+ }
+
+done:
+ return 0;
+}
+
+static int import_merge_remove_input_queue(backend *be, import_merge_queue_entry **queue, import_merge_thang *thang,DBT *key,DBC **input_cursors, DB **input_files,int passes)
+{
+ import_merge_queue_entry *head = NULL;
+ int file_referenced = 0;
+ int i = 0;
+ int ret = 0;
+
+ PR_ASSERT(NULL != queue);
+ head = *queue;
+ if (head == NULL) {
+ /* Means we've exhausted the queue---we're done */
+ return EOF;
+ }
+ /* Remove the head of the queue */
+ *queue = head->next;
+ /* Get the IDL */
+ *thang = head->thang;
+ *key = head->key;
+ PR_ASSERT(NULL != thang);
+ /* Walk the list of referenced files, reading in the next IDL from each
+ * one to the queue */
+ for (i = 0 ; i < passes; i++) {
+ import_merge_thang new_thang = {0};
+ DBT new_key = {0};
+
+ file_referenced = (head->file_referenced_list)[i];
+ if (file_referenced) {
+ ret = import_merge_get_next_thang(be, input_cursors[i],
+ input_files[i], &new_thang, &new_key, thang->type);
+ if (0 != ret) {
+ if (EOF == ret) {
+ /* Means that we walked off the end of the list,
+ * do nothing */
+ ret = 0;
+ } else {
+ /* Some other error */
+ break;
+ }
+ } else {
+ /* This function is responsible for any freeing needed */
+ import_merge_insert_input_queue(be, queue, i, &new_key,
+ &new_thang, passes);
+ }
+ }
+ }
+ slapi_ch_free( (void**)&(head->file_referenced_list));
+ slapi_ch_free( (void**)&head);
+
+ return ret;
+}
+
+static int import_merge_open_input_cursors(DB**files, int passes, DBC ***cursors)
+{
+ int i = 0;
+ int ret = 0;
+ *cursors = (DBC**)slapi_ch_calloc(passes,sizeof(DBC*));
+ if (NULL == *cursors) {
+ return -1;
+ }
+
+ for (i = 0; i < passes; i++) {
+ DB *pDB = files[i];
+ DBC *pDBC = NULL;
+ if (NULL != pDB) {
+ /* Try to open a cursor onto the file */
+ ret = pDB->cursor(pDB,NULL,&pDBC,0);
+ if (0 != ret) {
+ break;
+ } else {
+ (*cursors)[i] = pDBC;
+ }
+ }
+ }
+
+ return ret;
+}
+
+static int import_count_merge_input_files(ldbm_instance *inst,
+ char *indexname, int passes, int *number_found, int *pass_number)
+{
+ int i = 0;
+ int found_one = 0;
+
+ *number_found = 0;
+ *pass_number = 0;
+
+ for (i = 0; i < passes; i++) {
+ int fd;
+ char *filename = NULL;
+ size_t filename_length = strlen(inst->inst_dir_name) + 1 +
+ strlen(indexname) + 10 ;
+
+ filename = slapi_ch_malloc(filename_length);
+ if (NULL == filename) {
+ return -1;
+ }
+ sprintf(filename, "%s/%s.%d%s", inst->inst_dir_name, indexname, i+1,
+ LDBM_FILENAME_SUFFIX);
+ fd = dblayer_open_huge_file(filename, O_RDONLY, 0);
+ slapi_ch_free( (void**)&filename);
+ if (fd >= 0) {
+ close(fd);
+ if (found_one == 0) {
+ *pass_number = i+1;
+ }
+ found_one = 1;
+ (*number_found)++;
+ } else {
+ ; /* Not finding a file is OK */
+ }
+ }
+
+ return 0;
+}
+
+static int import_open_merge_input_files(backend *be, char *indexname,
+ int passes, DB ***input_files, int *number_found, int *pass_number)
+{
+ int i = 0;
+ int ret = 0;
+ int found_one = 0;
+
+ *number_found = 0;
+ *pass_number = 0;
+ *input_files = (DB**)slapi_ch_calloc(passes,sizeof(DB*));
+ if (NULL == *input_files) {
+ /* Memory allocation error */
+ return -1;
+ }
+ for (i = 0; i < passes; i++) {
+ DB *pDB = NULL;
+ char *filename = NULL;
+ size_t filename_length = strlen(indexname) + 10 ;
+
+ filename = slapi_ch_malloc(filename_length);
+ if (NULL == filename) {
+ return -1;
+ }
+ sprintf(filename,"%s.%d", indexname, i+1);
+
+ if (vlv_isvlv(filename)) {
+ ret = dblayer_open_file(be, filename, 0, INDEX_VLV, &pDB);
+ } else {
+ ret = dblayer_open_file(be, filename, 0, 0, &pDB);
+ }
+
+ slapi_ch_free( (void**)&filename);
+ if (0 == ret) {
+ if (found_one == 0) {
+ *pass_number = i+1;
+ }
+ found_one = 1;
+ (*number_found)++;
+ (*input_files)[i] = pDB;
+ } else {
+ if (ENOENT == ret) {
+ ret = 0; /* Not finding a file is OK */
+ } else {
+ break;
+ }
+ }
+ }
+
+ return ret;
+}
+
+/* Performs the n-way merge on one file */
+static int import_merge_one_file(ImportWorkerInfo *worker, int passes,
+ int *key_count)
+{
+ ldbm_instance *inst = worker->job->inst;
+ backend *be = inst->inst_be;
+ DB *output_file = NULL;
+ int ret = 0;
+ int preclose_ret = 0;
+ int number_found = 0;
+ int pass_number = 0;
+
+ PR_ASSERT(NULL != inst);
+
+ /* Try to open all the input files.
+ If we can't open file a file, we assume that is
+ because there was no data in it. */
+ ret = import_count_merge_input_files(inst, worker->index_info->name,
+ passes, &number_found, &pass_number);
+ if (0 != ret) {
+ goto error;
+ }
+ /* If there were no input files, then we're finished ! */
+ if (0 == number_found) {
+ ret = 0;
+ goto error;
+ }
+ /* Special-case where there's only one input file---just rename it */
+ if (1 == number_found) {
+ char *newname = NULL;
+ char *oldname = NULL;
+
+ ret = import_make_merge_filenames(inst->inst_dir_name,
+ worker->index_info->name, pass_number, &oldname, &newname);
+ if (0 != ret) {
+ import_log_notice(worker->job, "Failed making filename in merge");
+ goto error;
+ }
+ ret = PR_Rename(newname,oldname);
+ if (0 != ret) {
+ PRErrorCode prerr = PR_GetError();
+ import_log_notice(worker->job, "Failed to rename file \"%s\" to \"%s\" "
+ "in merge, " SLAPI_COMPONENT_NAME_NSPR " error %d (%s)",
+ oldname, newname, prerr, slapd_pr_strerror(prerr));
+ slapi_ch_free( (void**)&newname);
+ slapi_ch_free( (void**)&oldname);
+ goto error;
+ }
+ slapi_ch_free( (void**)&newname);
+ slapi_ch_free( (void**)&oldname);
+ *key_count = -1;
+ } else {
+ /* We really need to merge */
+ import_merge_queue_entry *merge_queue = NULL;
+ DB **input_files = NULL;
+ DBC **input_cursors = NULL;
+ DBT key = {0};
+ import_merge_thang thang = {0};
+ int i = 0;
+ int not_finished = 1;
+ int vlv_index = (INDEX_VLV == worker->index_info->ai->ai_indexmask);
+
+#if 0
+ /* Close and re-open regions, bugs otherwise */
+ ret = dblayer_close(inst->inst_li, DBLAYER_IMPORT_MODE);
+ if (0 != ret) {
+ if (ENOSPC == ret) {
+ import_log_notice(worker->job, "FAILED: NO DISK SPACE LEFT");
+ } else {
+ import_log_notice(worker->job, "MERGE FAIL 8 %d", ret);
+ }
+ return ret;
+ }
+ ret = dblayer_start(inst->inst_li, DBLAYER_IMPORT_MODE);
+ if (0 != ret) {
+ import_log_notice(worker->job, "MERGE FAIL 9");
+ return ret;
+ }
+ ret = dblayer_instance_start(be, DBLAYER_IMPORT_MODE);
+ if (0 != ret) {
+ import_log_notice(worker->job, "MERGE FAIL 9A");
+ return ret;
+ }
+#else
+ /* we have reason to believe that it's okay to leave the region files
+ * open in db3.x, since they track which files are opened and closed.
+ * if we had to close the region files, we'd have to take down the
+ * whole backend and defeat the purpose of an online import ---
+ * baaad medicine.
+ */
+ ret = dblayer_instance_close(be);
+ if (0 != ret) {
+ import_log_notice(worker->job, "MERGE FAIL 8i %d\n", ret);
+ return ret;
+ }
+ ret = dblayer_instance_start(be, DBLAYER_IMPORT_MODE);
+ if (0 != ret) {
+ import_log_notice(worker->job, "MERGE FAIL 8j %d\n", ret);
+ return ret;
+ }
+#endif
+
+ ret = import_open_merge_input_files(be, worker->index_info->name,
+ passes, &input_files, &number_found, &pass_number);
+ if (0 != ret) {
+ import_log_notice(worker->job, "MERGE FAIL 10");
+ return ret;
+ }
+
+ ret = dblayer_open_file(be, worker->index_info->name, 1,
+ vlv_index ? INDEX_VLV : 0, &output_file);
+ if (0 != ret) {
+ import_log_notice(worker->job, "Failed to open output file for "
+ "index %s in merge", worker->index_info->name);
+ goto error;
+ }
+
+ /* OK, so we now have input and output files open and can proceed to
+ * merge */
+ /* We want to pre-fill the input IDL queue */
+ /* Open cursors onto the input files */
+ ret = import_merge_open_input_cursors(input_files, passes,
+ &input_cursors);
+ if (0 != ret) {
+ import_log_notice(worker->job, "MERGE FAIL 2 %s %d",
+ worker->index_info->name, ret);
+ goto error;
+ }
+
+ /* Now read from the first location in each file and insert into the
+ * queue */
+ for (i = 0; i < passes; i++) if (input_files[i]) {
+ import_merge_thang prime_thang = {0};
+
+ /* Read an IDL from the file */
+ ret = import_merge_get_next_thang(be, input_cursors[i],
+ input_files[i], &prime_thang, &key,
+ vlv_index ? IMPORT_MERGE_THANG_VLV : IMPORT_MERGE_THANG_IDL);
+ if (0 != ret) {
+ import_log_notice(worker->job, "MERGE FAIL 1 %s %d",
+ worker->index_info->name, ret);
+ goto error;
+ }
+ /* Put it on the queue */
+ ret = import_merge_insert_input_queue(be, &merge_queue, i,& key,
+ &prime_thang, passes);
+ if (0 != ret) {
+ import_log_notice(worker->job, "MERGE FAIL 0 %s",
+ worker->index_info->name);
+ goto error;
+ }
+ }
+
+ /* We now have a pre-filled queue, so we may now proceed to remove the
+ head entry and write it to the output file, and repeat this process
+ until we've finished reading all the input data */
+ while (not_finished && (0 == ret) ) {
+ ret = import_merge_remove_input_queue(be, &merge_queue, &thang,
+ &key, input_cursors, input_files, passes);
+ if (0 != ret) {
+ /* Have we finished cleanly ? */
+ if (EOF == ret) {
+ not_finished = 0;
+ } else {
+ import_log_notice(worker->job, "MERGE FAIL 3 %s, %d",
+ worker->index_info->name, ret);
+ }
+ } else {
+ /* Write it out */
+ (*key_count)++;
+ if (vlv_index) {
+ /* Write the vlv index */
+ ret = output_file->put(output_file, NULL, &key,
+ &(thang.payload.vlv_data),0);
+ free(thang.payload.vlv_data.data);
+ thang.payload.vlv_data.data = NULL;
+ } else {
+ /* Write the IDL index */
+ ret = idl_store_block(be, output_file, &key,
+ thang.payload.idl, NULL, worker->index_info->ai);
+ /* Free the key we got back from the queue */
+ idl_free(thang.payload.idl);
+ thang.payload.idl = NULL;
+ }
+ free(key.data);
+ key.data = NULL;
+ if (0 != ret) {
+ /* Failed to write--- most obvious cause being out of
+ disk space, let's make sure that we at least print a
+ sensible error message right here. The caller should
+ really handle this properly, but we're always bad at
+ this. */
+ if (ret == DB_RUNRECOVERY || ret == ENOSPC) {
+ import_log_notice(worker->job, "OUT OF SPACE ON DISK, "
+ "failed writing index file %s",
+ worker->index_info->name);
+ } else {
+ import_log_notice(worker->job, "Failed to write "
+ "index file %s, errno=%d (%s)\n",
+ worker->index_info->name, errno,
+ dblayer_strerror(errno));
+ }
+ }
+ }
+ }
+ preclose_ret = ret;
+ /* Now close the files */
+ dblayer_close_file(output_file);
+ /* Close the cursors */
+ /* Close and delete the files */
+ for (i = 0; i < passes; i++) {
+ DBC *cursor = input_cursors[i];
+ DB *db = input_files[i];
+ if (NULL != db) {
+ PR_ASSERT(NULL != cursor);
+ ret = cursor->c_close(cursor);
+ if (0 != ret) {
+ import_log_notice(worker->job, "MERGE FAIL 4");
+ }
+ ret = dblayer_close_file(db);
+ if (0 != ret) {
+ import_log_notice(worker->job, "MERGE FAIL 5");
+ }
+ /* Now make the filename and delete the file */
+ {
+ char *newname = NULL;
+ char *oldname = NULL;
+ ret = import_make_merge_filenames(inst->inst_dir_name,
+ worker->index_info->name, i+1, &oldname, &newname);
+ if (0 != ret) {
+ import_log_notice(worker->job, "MERGE FAIL 6");
+ } else {
+ ret = PR_Delete(newname);
+ if (0 != ret) {
+ import_log_notice(worker->job, "MERGE FAIL 7");
+ }
+ slapi_ch_free( (void**)&newname);
+ slapi_ch_free( (void**)&oldname);
+ }
+ }
+ }
+ }
+ if (preclose_ret != 0) ret = preclose_ret;
+ slapi_ch_free( (void**)&input_files);
+ slapi_ch_free( (void**)&input_cursors);
+ }
+ if (EOF == ret) {
+ ret = 0;
+ }
+
+error:
+ return ret;
+}
+
+/********** the real deal here: **********/
+
+/* Our mission here is as follows:
+ * for each index job except entrydn and id2entry:
+ * open all the pass files
+ * open a new output file
+ * iterate cursors over all of the input files picking each distinct
+ * key and combining the input IDLs into a merged IDL. Put that
+ * IDL to the output file.
+ */
+int import_mega_merge(ImportJob *job)
+{
+ ImportWorkerInfo *current_worker = NULL;
+ int ret = 0;
+ time_t beginning = 0;
+ time_t end = 0;
+ int passes = job->current_pass;
+
+ if (1 == job->number_indexers) {
+ import_log_notice(job, "Beginning %d-way merge of one file...", passes,
+ job->number_indexers);
+ } else {
+ import_log_notice(job, "Beginning %d-way merge of up to %lu files...",
+ passes, job->number_indexers);
+ }
+
+ time(&beginning);
+ /* Iterate over the files */
+ for (current_worker = job->worker_list;
+ (ret == 0) && (current_worker != NULL);
+ current_worker = current_worker->next) {
+ /* We need to ignore the primary index */
+ if ((current_worker->work_type != FOREMAN) &&
+ (current_worker->work_type != PRODUCER)) {
+ time_t file_beginning = 0;
+ time_t file_end = 0;
+ int key_count = 0;
+
+ time(&file_beginning);
+ ret = import_merge_one_file(current_worker,passes,&key_count);
+ time(&file_end);
+ if (key_count == 0) {
+ import_log_notice(job, "No files to merge for \"%s\".",
+ current_worker->index_info->name);
+ } else {
+ if (-1 == key_count) {
+ import_log_notice(job, "Merged \"%s\": Simple merge - "
+ "file renamed.",
+ current_worker->index_info->name);
+ } else {
+ import_log_notice(job, "Merged \"%s\": %d keys merged "
+ "in %ld seconds.",
+ current_worker->index_info->name,
+ key_count, file_end-file_beginning);
+ }
+ }
+ }
+ }
+
+ time(&end);
+ if (0 == ret) {
+ int seconds_to_merge = end - beginning;
+
+ import_log_notice(job, "Merging completed in %d seconds.",
+ seconds_to_merge);
+ }
+
+ return ret;
+}
diff --git a/ldap/servers/slapd/back-ldbm/import-threads.c b/ldap/servers/slapd/back-ldbm/import-threads.c
new file mode 100644
index 00000000..413eaca6
--- /dev/null
+++ b/ldap/servers/slapd/back-ldbm/import-threads.c
@@ -0,0 +1,1992 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+/*
+ * the threads that make up an import:
+ * producer (1)
+ * foreman (1)
+ * worker (N: 1 for each index)
+ *
+ * a wire import (aka "fast replica" import) won't have a producer thread.
+ */
+
+#include "back-ldbm.h"
+#include "vlv_srch.h"
+#include "import.h"
+#ifdef XP_WIN32
+#define STDIN_FILENO 0
+#endif
+
+
+static struct backentry *import_make_backentry(Slapi_Entry *e, ID id)
+{
+ struct backentry *ep = backentry_alloc();
+
+ if (NULL != ep) {
+ ep->ep_entry = e;
+ ep->ep_id = id;
+ }
+ return ep;
+}
+
+static void import_decref_entry(struct backentry *ep)
+{
+ PR_AtomicDecrement(&(ep->ep_refcnt));
+ PR_ASSERT(ep->ep_refcnt >= 0);
+}
+
+/* generate uniqueid if requested */
+static void import_generate_uniqueid(ImportJob *job, Slapi_Entry *e)
+{
+ const char *uniqueid = slapi_entry_get_uniqueid(e);
+ int rc;
+
+ if (!uniqueid && (job->uuid_gen_type != SLAPI_UNIQUEID_GENERATE_NONE)) {
+ char *newuniqueid;
+
+ /* generate id based on dn */
+ if (job->uuid_gen_type == SLAPI_UNIQUEID_GENERATE_NAME_BASED) {
+ char *dn = slapi_entry_get_dn(e);
+
+ rc = slapi_uniqueIDGenerateFromNameString(&newuniqueid,
+ job->uuid_namespace, dn, strlen(dn));
+ } else {
+ /* time based */
+ rc = slapi_uniqueIDGenerateString(&newuniqueid);
+ }
+
+ if (rc == UID_SUCCESS) {
+ slapi_entry_set_uniqueid (e, newuniqueid);
+ } else {
+ char ebuf[BUFSIZ];
+ LDAPDebug( LDAP_DEBUG_ANY,
+ "import_generate_uniqueid: failed to generate "
+ "uniqueid for %s; error=%d.\n",
+ escape_string(slapi_entry_get_dn_const(e), ebuf), rc, 0 );
+ }
+ }
+}
+
+
+/********** BETTER LDIF PARSER **********/
+
+
+/* like the function in libldif, except this one doesn't need to use
+ * FILE (which breaks on various platforms for >4G files or large numbers
+ * of open files)
+ */
+#define LDIF_BUFFER_SIZE 8192
+
+typedef struct {
+ char *b; /* buffer */
+ size_t size; /* how full the buffer is */
+ size_t offset; /* where the current entry starts */
+} ldif_context;
+
+static void import_init_ldif(ldif_context *c)
+{
+ c->size = c->offset = 0;
+ c->b = NULL;
+}
+
+static void import_free_ldif(ldif_context *c)
+{
+ if (c->b)
+ FREE(c->b);
+ import_init_ldif(c);
+}
+
+static char *import_get_entry(ldif_context *c, int fd, int *lineno)
+{
+ int ret;
+ int done = 0, got_lf = 0;
+ size_t bufSize = 0, bufOffset = 0, i;
+ char *buf = NULL;
+
+ while (!done) {
+
+ /* If there's no data in the buffer, get some */
+ if ((c->size == 0) || (c->offset == c->size)) {
+ /* Do we even have a buffer ? */
+ if (! c->b) {
+ c->b = slapi_ch_malloc(LDIF_BUFFER_SIZE);
+ if (! c->b)
+ return NULL;
+ }
+ ret = read(fd, c->b, LDIF_BUFFER_SIZE);
+ if (ret < 0) {
+ /* Must be error */
+ goto error;
+ } else if (ret == 0) {
+ /* eof */
+ if (buf) {
+ /* last entry */
+ buf[bufOffset] = 0;
+ return buf;
+ }
+ return NULL;
+ } else {
+ /* read completed OK */
+ c->size = ret;
+ c->offset = 0;
+ }
+ }
+
+ /* skip blank lines at start of entry */
+ if (bufOffset == 0) {
+ size_t n;
+ char *p;
+
+ for (n = c->offset, p = c->b + n; n < c->size; n++, p++) {
+ if (!(*p == '\r' || *p == '\n' || *p == ' '|| *p == '\t'))
+ break;
+ }
+ c->offset = n;
+ if (c->offset == c->size) continue;
+ }
+
+ i = c->offset;
+ while (!done && (i < c->size)) {
+ /* scan forward in the buffer, looking for the end of the entry */
+ while ((i < c->size) && (c->b[i] != '\n'))
+ i++;
+
+ if ((i < c->size) && (c->b[i] == '\n')) {
+ if (got_lf && ((i == 0) || ((i == 1) && (c->b[0] == '\r')))) {
+ /* saw an lf at the end of the last buffer */
+ i++, (*lineno)++;
+ done = 1;
+ got_lf = 0;
+ break;
+ }
+ got_lf = 0;
+ (*lineno)++;
+ /* is this the end? (need another linefeed) */
+ if (++i < c->size) {
+ if (c->b[i] == '\n') {
+ /* gotcha! */
+ i++, (*lineno)++;
+ done = 1;
+ } else if (c->b[i] == '\r') {
+ if (++i < c->size) {
+ if (c->b[i] == '\n') {
+ /* gotcha! (nt) */
+ i++, (*lineno)++;
+ done = 1;
+ }
+ } else {
+ got_lf = 1;
+ }
+ }
+ } else {
+ /* lf at the very end of the buffer */
+ got_lf = 1;
+ }
+ }
+ }
+
+ /* copy what we did so far into the output buffer */
+ /* (first, make sure the output buffer is large enough) */
+ if (bufSize - bufOffset < i - c->offset + 1) {
+ char *newbuf = NULL;
+ size_t newsize = (buf ? bufSize*2 : LDIF_BUFFER_SIZE);
+
+ newbuf = slapi_ch_malloc(newsize);
+ if (! newbuf)
+ goto error;
+ /* copy over the old data (if there was any) */
+ if (buf) {
+ memmove(newbuf, buf, bufOffset);
+ slapi_ch_free((void **)&buf);
+ }
+ buf = newbuf;
+ bufSize = newsize;
+ }
+ memmove(buf + bufOffset, c->b + c->offset, i - c->offset);
+ bufOffset += (i - c->offset);
+ c->offset = i;
+ }
+
+ /* add terminating NUL char */
+ buf[bufOffset] = 0;
+ return buf;
+
+error:
+ if (buf)
+ slapi_ch_free((void **)&buf);
+ return NULL;
+}
+
+
+/********** THREADS **********/
+
+/*
+ * Description:
+ * 1) return the ldif version #
+ * 2) replace "version: 1" with "#ersion: 1"
+ * to pretend like a comment for the str2entry
+ */
+static int
+import_get_version(char *str)
+{
+ char *s;
+ char *type;
+ char *valuecharptr;
+ char *mystr, *ms;
+ int offset;
+ int valuelen;
+ int my_version = 0;
+ int retmalloc = 0;
+
+ if ((s = strstr(str, "version:")) == NULL)
+ return 0;
+
+ offset = s - str;
+ mystr = ms = slapi_ch_strdup(str);
+ while ( (s = ldif_getline( &ms )) != NULL ) {
+ char *errmsg = NULL;
+ if ( (retmalloc = ldif_parse_line( s, &type, &valuecharptr, &valuelen, &errmsg )) >= 0 ) {
+ if (!strcasecmp(type, "version")) {
+ my_version = atoi(valuecharptr);
+ *(str + offset) = '#';
+ /* the memory below was not allocated by the slapi_ch_ functions */
+ if (errmsg) slapi_ch_free((void **) &errmsg);
+ if (retmalloc) slapi_ch_free((void **) &valuecharptr);
+ break;
+ }
+ } else if ( errmsg != NULL ) {
+ LDAPDebug( LDAP_DEBUG_PARSE, "%s", errmsg, 0, 0 );
+ }
+ /* the memory below was not allocated by the slapi_ch_ functions */
+ if (errmsg) slapi_ch_free((void **) &errmsg);
+ if (retmalloc) slapi_ch_free((void **) &valuecharptr);
+ }
+
+ slapi_ch_free((void **)&mystr);
+ return my_version;
+}
+
+/* producer thread:
+ * read through the given file list, parsing entries (str2entry), assigning
+ * them IDs and queueing them on the entry FIFO. other threads will do
+ * the indexing.
+ */
+void import_producer(void *param)
+{
+ ImportWorkerInfo *info = (ImportWorkerInfo *)param;
+ ImportJob *job = info->job;
+ ID id = job->first_ID, id_filestart = id;
+ Slapi_Entry *e = NULL;
+ struct backentry *ep = NULL, *old_ep = NULL;
+ ldbm_instance *inst = job->inst;
+ PRIntervalTime sleeptime;
+ char *estr = NULL;
+ int str2entry_flags =
+ SLAPI_STR2ENTRY_TOMBSTONE_CHECK |
+ SLAPI_STR2ENTRY_REMOVEDUPVALS |
+ SLAPI_STR2ENTRY_EXPAND_OBJECTCLASSES |
+ SLAPI_STR2ENTRY_ADDRDNVALS |
+ SLAPI_STR2ENTRY_NOT_WELL_FORMED_LDIF;
+ int finished = 0;
+ int detected_eof = 0;
+ int fd, curr_file, curr_lineno;
+ char *curr_filename = NULL;
+ int idx;
+ ldif_context c;
+ int my_version = 0;
+ size_t newesize = 0;
+
+ PR_ASSERT(info != NULL);
+ PR_ASSERT(inst != NULL);
+
+ if ( job->flags & FLAG_ABORT ) {
+ goto error;
+ }
+
+ sleeptime = PR_MillisecondsToInterval(import_sleep_time);
+
+ /* pause until we're told to run */
+ while ((info->command == PAUSE) && !(job->flags & FLAG_ABORT)) {
+ info->state = WAITING;
+ DS_Sleep(sleeptime);
+ }
+ info->state = RUNNING;
+ import_init_ldif(&c);
+
+ /* jumpstart by opening the first file */
+ curr_file = 0;
+ fd = -1;
+ detected_eof = finished = 0;
+
+ /* we loop around reading the input files and processing each entry
+ * as we read it.
+ */
+ while (! finished) {
+ Slapi_Attr *attr = NULL;
+ int flags = 0;
+ int prev_lineno = 0;
+ int lines_in_entry = 0;
+
+ if (job->flags & FLAG_ABORT) {
+ goto error;
+ }
+
+ /* move on to next file? */
+ if (detected_eof) {
+ /* check if the file can still be read, whine if so... */
+ if (read(fd, (void *)&idx, 1) > 0) {
+ import_log_notice(job, "WARNING: Unexpected end of file found "
+ "at line %d of file \"%s\"", curr_lineno,
+ curr_filename);
+ }
+
+ if (fd == STDIN_FILENO) {
+ import_log_notice(job, "Finished scanning file stdin (%lu "
+ "entries)", (u_long)(id-id_filestart));
+ } else {
+ import_log_notice(job, "Finished scanning file \"%s\" (%lu "
+ "entries)", curr_filename, (u_long)(id-id_filestart));
+ }
+ close(fd);
+ fd = -1;
+ detected_eof = 0;
+ id_filestart = id;
+ curr_file++;
+ if (job->task) {
+ job->task->task_progress++;
+ slapi_task_status_changed(job->task);
+ }
+ if (job->input_filenames[curr_file] == NULL) {
+ /* done! */
+ finished = 1;
+ break;
+ }
+ }
+
+ /* separate from above, because this is also triggered when we
+ * start (to open the first file)
+ */
+ if (fd < 0) {
+ curr_lineno = 0;
+ curr_filename = job->input_filenames[curr_file];
+ if (strcmp(curr_filename, "-") == 0) {
+ fd = STDIN_FILENO;
+ } else {
+ int o_flag = O_RDONLY;
+#ifdef XP_WIN32
+ /* 613041 Somehow the windows low level io lose "\n"
+ at a very particular situation using O_TEXT mode read.
+ I think it is a windows bug for O_TEXT mode read.
+ Use O_BINARY instead, which honestly returns chars
+ without any translation.
+ */
+ o_flag |= O_BINARY;
+#endif
+ fd = dblayer_open_huge_file(curr_filename, o_flag, 0);
+ }
+ if (fd < 0) {
+ import_log_notice(job, "Could not open LDIF file \"%s\"",
+ curr_filename);
+ goto error;
+ }
+ if (fd == STDIN_FILENO) {
+ import_log_notice(job, "Processing file stdin");
+ } else {
+ import_log_notice(job, "Processing file \"%s\"", curr_filename);
+ }
+ }
+ if (job->flags & FLAG_ABORT) {
+ goto error;
+ }
+
+
+ while ((info->command == PAUSE) && !(job->flags & FLAG_ABORT)){
+ info->state = WAITING;
+ DS_Sleep(sleeptime);
+ }
+ info->state = RUNNING;
+
+ prev_lineno = curr_lineno;
+ estr = import_get_entry(&c, fd, &curr_lineno);
+
+ lines_in_entry = curr_lineno - prev_lineno;
+ if (!estr) {
+ /* error reading entry, or end of file */
+ detected_eof = 1;
+ continue;
+ }
+
+ if (0 == my_version && strstr(estr, "version:")) {
+ my_version = import_get_version(estr);
+ str2entry_flags |= SLAPI_STR2ENTRY_INCLUDE_VERSION_STR;
+ }
+
+ /* If there are more than so many lines in the entry, we tell
+ * str2entry to optimize for a large entry.
+ */
+ if (lines_in_entry > STR2ENTRY_ATTRIBUTE_PRESENCE_CHECK_THRESHOLD) {
+ flags = str2entry_flags | SLAPI_STR2ENTRY_BIGENTRY;
+ } else {
+ flags = str2entry_flags;
+ }
+ e = slapi_str2entry(estr, flags);
+ FREE(estr);
+ if (! e) {
+ if (!(str2entry_flags & SLAPI_STR2ENTRY_INCLUDE_VERSION_STR))
+ import_log_notice(job, "WARNING: skipping bad LDIF entry "
+ "ending line %d of file \"%s\"", curr_lineno,
+ curr_filename);
+ continue;
+ }
+ if (0 == my_version) {
+ /* after the first entry version string won't be given */
+ my_version = -1;
+ }
+
+ if (! import_entry_belongs_here(e, inst->inst_be)) {
+ /* silently skip */
+ if (e) {
+ job->not_here_skipped++;
+ slapi_entry_free(e);
+ }
+ continue;
+ }
+
+ if (slapi_entry_schema_check(NULL, e) != 0) {
+ char ebuf[BUFSIZ];
+ import_log_notice(job, "WARNING: skipping entry \"%s\" which "
+ "violates schema, ending line %d of file "
+ "\"%s\"", escape_string(slapi_entry_get_dn(e), ebuf),
+ curr_lineno, curr_filename);
+ if (e)
+ slapi_entry_free(e);
+ job->skipped++;
+ continue;
+ }
+
+ /* generate uniqueid if necessary */
+ import_generate_uniqueid(job, e);
+
+ ep = import_make_backentry(e, id);
+ if (!ep)
+ goto error;
+
+ /* check for include/exclude subtree lists */
+ if (! ldbm_back_ok_to_dump(backentry_get_ndn(ep),
+ job->include_subtrees,
+ job->exclude_subtrees)) {
+ backentry_free(&ep);
+ continue;
+ }
+
+ /* not sure what this does, but it looked like it could be
+ * simplified. if it's broken, it's my fault. -robey
+ */
+ if (slapi_entry_attr_find(ep->ep_entry, "userpassword", &attr) == 0) {
+ Slapi_Value **va = attr_get_present_values(attr);
+
+ pw_encodevals( (Slapi_Value **)va ); /* jcm - cast away const */
+ }
+
+ if (job->flags & FLAG_ABORT) {
+ goto error;
+ }
+
+
+ /* Now we have this new entry, all decoded
+ * Next thing we need to do is:
+ * (1) see if the appropriate fifo location contains an
+ * entry which had been processed by the indexers.
+ * If so, proceed.
+ * If not, spin waiting for it to become free.
+ * (2) free the old entry and store the new one there.
+ * (3) Update the job progress indicators so the indexers
+ * can use the new entry.
+ */
+ idx = id % job->fifo.size;
+ old_ep = job->fifo.item[idx].entry;
+ if (old_ep) {
+ /* for the slot to be recycled, it needs to be already absorbed
+ * by the foreman (id >= ready_ID), and all the workers need to
+ * be finished with it (refcount = 0).
+ */
+ while (((old_ep->ep_refcnt > 0) ||
+ (old_ep->ep_id >= job->ready_ID))
+ && (info->command != ABORT) && !(job->flags & FLAG_ABORT)) {
+ info->state = WAITING;
+ DS_Sleep(sleeptime);
+ }
+ if (job->flags & FLAG_ABORT){
+ goto error;
+ }
+ info->state = RUNNING;
+ PR_ASSERT(old_ep == job->fifo.item[idx].entry);
+ job->fifo.item[idx].entry = NULL;
+ if (job->fifo.c_bsize > job->fifo.item[idx].esize)
+ job->fifo.c_bsize -= job->fifo.item[idx].esize;
+ else
+ job->fifo.c_bsize = 0;
+ backentry_free(&old_ep);
+ }
+
+ newesize = (slapi_entry_size(ep->ep_entry) + sizeof(struct backentry));
+ if (newesize > job->fifo.bsize) { /* entry too big */
+ char ebuf[BUFSIZ];
+ import_log_notice(job, "WARNING: skipping entry \"%s\" "
+ "ending line %d of file \"%s\"",
+ escape_string(slapi_entry_get_dn(e), ebuf),
+ curr_lineno, curr_filename);
+ import_log_notice(job, "REASON: entry too large (%d bytes) for "
+ "the buffer size (%d bytes)", newesize, job->fifo.bsize);
+ backentry_free(&ep);
+ job->skipped++;
+ continue;
+ }
+ /* Now check if fifo has enough space for the new entry */
+ if ((job->fifo.c_bsize + newesize) > job->fifo.bsize) {
+ import_wait_for_space_in_fifo( job, newesize );
+ }
+
+ /* We have enough space */
+ job->fifo.item[idx].filename = curr_filename;
+ job->fifo.item[idx].line = curr_lineno;
+ job->fifo.item[idx].entry = ep;
+ job->fifo.item[idx].bad = 0;
+ job->fifo.item[idx].esize = newesize;
+
+ /* Add the entry size to total fifo size */
+ job->fifo.c_bsize += ep->ep_entry? job->fifo.item[idx].esize : 0;
+
+ /* Update the job to show our progress */
+ job->lead_ID = id;
+ if ((id - info->first_ID) <= job->fifo.size) {
+ job->trailing_ID = info->first_ID;
+ } else {
+ job->trailing_ID = id - job->fifo.size;
+ }
+
+ /* Update our progress meter too */
+ info->last_ID_processed = id;
+ id++;
+ if (job->flags & FLAG_ABORT){
+ goto error;
+ }
+ if (info->command == STOP) {
+ if (fd >= 0)
+ close(fd);
+ finished = 1;
+ }
+ }
+
+ import_free_ldif(&c);
+ info->state = FINISHED;
+ return;
+
+error:
+ info->state = ABORTED;
+}
+
+#if defined(UPGRADEDB)
+/* producer thread for re-indexing:
+ * read id2entry, parsing entries (str2entry) (needed???), assigning
+ * them IDs (again, needed???) and queueing them on the entry FIFO.
+ * other threads will do the indexing -- same as in import.
+ */
+void index_producer(void *param)
+{
+ ImportWorkerInfo *info = (ImportWorkerInfo *)param;
+ ImportJob *job = info->job;
+ ID id = job->first_ID;
+ Slapi_Entry *e = NULL;
+ struct backentry *ep = NULL, *old_ep = NULL;
+ ldbm_instance *inst = job->inst;
+ PRIntervalTime sleeptime;
+ int finished = 0;
+ int idx;
+
+ /* vars for Berkeley DB */
+ DB_ENV *env = NULL;
+ DB *db = NULL;
+ DBC *dbc = NULL;
+ DBT key = {0};
+ DBT data = {0};
+ int db_rval = -1;
+ backend *be = inst->inst_be;
+ int isfirst = 1;
+ int curr_entry = 0;
+ size_t newesize = 0;
+
+ PR_ASSERT(info != NULL);
+ PR_ASSERT(inst != NULL);
+ PR_ASSERT(be != NULL);
+
+ if ( job->flags & FLAG_ABORT )
+ goto error;
+
+ sleeptime = PR_MillisecondsToInterval(import_sleep_time);
+
+ /* pause until we're told to run */
+ while ((info->command == PAUSE) && !(job->flags & FLAG_ABORT)) {
+ info->state = WAITING;
+ DS_Sleep(sleeptime);
+ }
+ info->state = RUNNING;
+
+ /* open id2entry with dedicated db env and db handler */
+ if ( dblayer_get_aux_id2entry( be, &db, &env ) != 0 || db == NULL ||
+ env == NULL) {
+ LDAPDebug( LDAP_DEBUG_ANY, "Could not open id2entry\n", 0, 0, 0 );
+ goto error;
+ }
+
+ /* get a cursor to we can walk over the table */
+ db_rval = db->cursor(db, NULL, &dbc, 0);
+ if ( 0 != db_rval ) {
+ LDAPDebug( LDAP_DEBUG_ANY,
+ "Failed to get cursor for reindexing\n", 0, 0, 0 );
+ dblayer_release_id2entry(be, db);
+ goto error;
+ }
+
+ /* we loop around reading the input files and processing each entry
+ * as we read it.
+ */
+ finished = 0;
+ while (!finished) {
+ Slapi_Attr *attr = NULL;
+ ID temp_id;
+
+ if (job->flags & FLAG_ABORT) {
+ goto error;
+ }
+ while ((info->command == PAUSE) && !(job->flags & FLAG_ABORT)){
+ info->state = WAITING;
+ DS_Sleep(sleeptime);
+ }
+ info->state = RUNNING;
+
+ key.flags = DB_DBT_MALLOC;
+ data.flags = DB_DBT_MALLOC;
+ if (isfirst)
+ {
+ db_rval = dbc->c_get(dbc, &key, &data, DB_FIRST);
+ isfirst = 0;
+ }
+ else
+ {
+ db_rval = dbc->c_get(dbc, &key, &data, DB_NEXT);
+ }
+
+ if (0 != db_rval) {
+ if (DB_NOTFOUND != db_rval) {
+ LDAPDebug(LDAP_DEBUG_ANY, "%s: Failed to read database, "
+ "errno=%d (%s)\n", inst->inst_name, db_rval,
+ dblayer_strerror(db_rval));
+ if (job->task) {
+ slapi_task_log_notice(job->task,
+ "%s: Failed to read database, err %d (%s)",
+ inst->inst_name, db_rval,
+ dblayer_strerror(db_rval));
+ }
+ }
+ break;
+ }
+ curr_entry++;
+ temp_id = id_stored_to_internal((char *)key.data);
+ free(key.data);
+
+ /* call post-entry plugin */
+ plugin_call_entryfetch_plugins((char **) &data.dptr, &data.dsize);
+ e = slapi_str2entry(data.data, 0);
+ if ( NULL == e ) {
+ if (job->task) {
+ slapi_task_log_notice(job->task,
+ "%s: WARNING: skipping badly formatted entry (id %lu)",
+ inst->inst_name, (u_long)temp_id);
+ }
+ LDAPDebug(LDAP_DEBUG_ANY,
+ "%s: WARNING: skipping badly formatted entry (id %lu)\n",
+ inst->inst_name, (u_long)temp_id, 0);
+ continue;
+ }
+ free(data.data);
+
+ /* generate uniqueid if necessary */
+ import_generate_uniqueid(job, e);
+
+ ep = import_make_backentry(e, temp_id);
+ if (!ep)
+ goto error;
+
+ /* not sure what this does, but it looked like it could be
+ * simplified. if it's broken, it's my fault. -robey
+ */
+ if (slapi_entry_attr_find(ep->ep_entry, "userpassword", &attr) == 0) {
+ Slapi_Value **va = attr_get_present_values(attr);
+
+ pw_encodevals( (Slapi_Value **)va ); /* jcm - cast away const */
+ }
+
+ if (job->flags & FLAG_ABORT)
+ goto error;
+
+ /* Now we have this new entry, all decoded
+ * Next thing we need to do is:
+ * (1) see if the appropriate fifo location contains an
+ * entry which had been processed by the indexers.
+ * If so, proceed.
+ * If not, spin waiting for it to become free.
+ * (2) free the old entry and store the new one there.
+ * (3) Update the job progress indicators so the indexers
+ * can use the new entry.
+ */
+ idx = id % job->fifo.size;
+ old_ep = job->fifo.item[idx].entry;
+ if (old_ep) {
+ /* for the slot to be recycled, it needs to be already absorbed
+ * by the foreman (id >= ready_ID), and all the workers need to
+ * be finished with it (refcount = 0).
+ */
+ while (((old_ep->ep_refcnt > 0) ||
+ (old_ep->ep_id >= job->ready_ID))
+ && (info->command != ABORT) && !(job->flags & FLAG_ABORT)) {
+ info->state = WAITING;
+ DS_Sleep(sleeptime);
+ }
+ if (job->flags & FLAG_ABORT)
+ goto error;
+
+ info->state = RUNNING;
+ PR_ASSERT(old_ep == job->fifo.item[idx].entry);
+ job->fifo.item[idx].entry = NULL;
+ if (job->fifo.c_bsize > job->fifo.item[idx].esize)
+ job->fifo.c_bsize -= job->fifo.item[idx].esize;
+ else
+ job->fifo.c_bsize = 0;
+ backentry_free(&old_ep);
+ }
+
+ newesize = (slapi_entry_size(ep->ep_entry) + sizeof(struct backentry));
+ if (newesize > job->fifo.bsize) { /* entry too big */
+ char ebuf[BUFSIZ];
+ import_log_notice(job, "WARNING: skipping entry \"%s\"",
+ escape_string(slapi_entry_get_dn(e), ebuf));
+ import_log_notice(job, "REASON: entry too large (%d bytes) for "
+ "the buffer size (%d bytes)", newesize, job->fifo.bsize);
+ backentry_free(&ep);
+ job->skipped++;
+ continue;
+ }
+ /* Now check if fifo has enough space for the new entry */
+ if ((job->fifo.c_bsize + newesize) > job->fifo.bsize) {
+ import_wait_for_space_in_fifo( job, newesize );
+ }
+
+ /* We have enough space */
+ job->fifo.item[idx].filename = ID2ENTRY LDBM_FILENAME_SUFFIX;
+ job->fifo.item[idx].line = curr_entry;
+ job->fifo.item[idx].entry = ep;
+ job->fifo.item[idx].bad = 0;
+ job->fifo.item[idx].esize = newesize;
+
+ /* Add the entry size to total fifo size */
+ job->fifo.c_bsize += ep->ep_entry? job->fifo.item[idx].esize : 0;
+
+ /* Update the job to show our progress */
+ job->lead_ID = id;
+ if ((id - info->first_ID) <= job->fifo.size) {
+ job->trailing_ID = info->first_ID;
+ } else {
+ job->trailing_ID = id - job->fifo.size;
+ }
+
+ /* Update our progress meter too */
+ info->last_ID_processed = id;
+ id++;
+ if (job->flags & FLAG_ABORT)
+ goto error;
+ if (info->command == STOP)
+ {
+ finished = 1;
+ }
+ }
+
+ dbc->c_close(dbc);
+ dblayer_release_aux_id2entry( be, db, env );
+ info->state = FINISHED;
+ return;
+
+error:
+ dbc->c_close(dbc);
+ dblayer_release_aux_id2entry( be, db, env );
+ info->state = ABORTED;
+}
+#endif
+
+static void
+import_wait_for_space_in_fifo(ImportJob *job, size_t new_esize)
+{
+ struct backentry *temp_ep = NULL;
+ size_t i;
+ int slot_found;
+ PRIntervalTime sleeptime;
+
+ sleeptime = PR_MillisecondsToInterval(import_sleep_time);
+
+ /* Now check if fifo has enough space for the new entry */
+ while ((job->fifo.c_bsize + new_esize) > job->fifo.bsize) {
+ for ( i = 0, slot_found = 0 ; i < job->fifo.size ; i++ ) {
+ temp_ep = job->fifo.item[i].entry;
+ if (temp_ep) {
+ if (temp_ep->ep_refcnt == 0 && temp_ep->ep_id < job->ready_ID) {
+ job->fifo.item[i].entry = NULL;
+ if (job->fifo.c_bsize > job->fifo.item[i].esize)
+ job->fifo.c_bsize -= job->fifo.item[i].esize;
+ else
+ job->fifo.c_bsize = 0;
+ backentry_free(&temp_ep);
+ slot_found = 1;
+ }
+ }
+ }
+ if ( slot_found == 0 )
+ DS_Sleep(sleeptime);
+ }
+}
+
+/* helper function for the foreman: */
+static int foreman_do_parentid(ImportJob *job, struct backentry *entry,
+ struct attrinfo *parentid_ai)
+{
+ backend *be = job->inst->inst_be;
+ Slapi_Value **svals = NULL;
+ Slapi_Attr *attr = NULL;
+ int idl_disposition = 0;
+ int ret = 0;
+
+ if (slapi_entry_attr_find(entry->ep_entry, "parentid", &attr) == 0) {
+ svals = attr_get_present_values(attr);
+ ret = index_addordel_values_ext_sv(be, "parentid", svals, NULL, entry->ep_id,
+ BE_INDEX_ADD, NULL, &idl_disposition, NULL);
+ if (idl_disposition != IDL_INSERT_NORMAL) {
+ char *attr_value = slapi_value_get_berval(svals[0])->bv_val;
+ ID parent_id = atol(attr_value);
+
+ if (idl_disposition == IDL_INSERT_NOW_ALLIDS) {
+ import_subcount_mother_init(job->mothers, parent_id,
+ idl_get_allidslimit(parentid_ai)+1);
+ } else if (idl_disposition == IDL_INSERT_ALLIDS) {
+ import_subcount_mother_count(job->mothers, parent_id);
+ }
+ }
+ if (ret != 0) {
+ import_log_notice(job, "ERROR: Can't update parentid index "
+ "(error %d)", ret);
+ return ret;
+ }
+ }
+
+ return 0;
+}
+
+/* helper function for the foreman: */
+static int foreman_do_entrydn(ImportJob *job, FifoItem *fi)
+{
+ backend *be = job->inst->inst_be;
+ struct berval bv;
+ int err = 0, ret = 0;
+ IDList *IDL;
+
+ /* insert into the entrydn index */
+ bv.bv_val = (void*)backentry_get_ndn(fi->entry); /* jcm - Had to cast away const */
+ bv.bv_len = strlen(backentry_get_ndn(fi->entry));
+
+ /* We need to check here whether the DN is already present in
+ * the entrydn index. If it is then the input ldif
+ * contained a duplicate entry, which it isn't allowed to */
+ /* Due to popular demand, we only warn on this, given the
+ * tendency for customers to want to import dirty data */
+ /* So, we do an index read first */
+ err = 0;
+ IDL = index_read(be, "entrydn", indextype_EQUALITY, &bv, NULL, &err);
+
+ /* Did this work ? */
+ if (NULL != IDL) {
+ /* IMPOSTER ! Get thee hence... */
+ import_log_notice(job, "WARNING: Skipping duplicate entry "
+ "\"%s\" found at line %d of file \"%s\"",
+ slapi_entry_get_dn(fi->entry->ep_entry),
+ fi->line, fi->filename);
+ idl_free(IDL);
+ /* skip this one */
+ fi->bad = 1;
+ job->skipped++;
+ return -1; /* skip to next entry */
+ }
+ if ((ret = index_addordel_string(be, "entrydn",
+ bv.bv_val,
+ fi->entry->ep_id,
+ BE_INDEX_ADD, NULL)) != 0) {
+ import_log_notice(job, "Error writing entrydn index "
+ "(error %d: %s)",
+ ret, dblayer_strerror(ret));
+ return ret;
+ }
+ return 0;
+}
+
+/* foreman thread:
+ * i go through the FIFO just like the other worker threads, but i'm
+ * responsible for the interrelated indexes: entrydn, id2entry, and the
+ * operational attributes (plus the parentid index).
+ */
+void import_foreman(void *param)
+{
+ ImportWorkerInfo *info = (ImportWorkerInfo *)param;
+ ImportJob *job = info->job;
+ ldbm_instance *inst = job->inst;
+ backend *be = inst->inst_be;
+ PRIntervalTime sleeptime;
+ int finished = 0;
+ ID id = info->first_ID;
+ int ret = 0;
+ struct attrinfo *parentid_ai;
+ Slapi_PBlock *pb = slapi_pblock_new();
+
+ PR_ASSERT(info != NULL);
+ PR_ASSERT(inst != NULL);
+
+ if (job->flags & FLAG_ABORT) {
+ goto error;
+ }
+
+ /* the pblock is used only by add_op_attrs */
+ slapi_pblock_set(pb, SLAPI_BACKEND, be);
+ sleeptime = PR_MillisecondsToInterval(import_sleep_time);
+ info->state = RUNNING;
+
+ ainfo_get(be, "parentid", &parentid_ai);
+
+ while (! finished) {
+ FifoItem *fi = NULL;
+ int parent_status = 0;
+
+
+ if (job->flags & FLAG_ABORT) {
+ goto error;
+ }
+
+ while ( ((info->command == PAUSE) || (id > job->lead_ID)) &&
+ (info->command != STOP) && (info->command != ABORT) && !(job->flags & FLAG_ABORT)) {
+ /* Check to see if we've been told to stop */
+ info->state = WAITING;
+ DS_Sleep(sleeptime);
+ }
+ if (info->command == STOP) {
+ finished = 1;
+ continue;
+ }
+ if (job->flags & FLAG_ABORT) {
+ goto error;
+ }
+
+ info->state = RUNNING;
+
+ /* Read that entry from the cache */
+ fi = import_fifo_fetch(job, id, 0);
+ if (! fi) {
+ import_log_notice(job, "ERROR: foreman fifo error");
+ goto error;
+ }
+
+ /* first, fill in any operational attributes */
+ /* add_op_attrs wants a pblock for some reason. */
+ if (add_op_attrs(pb, inst->inst_li, fi->entry, &parent_status) != 0) {
+ import_log_notice(job, "ERROR: Could not add op attrs to "
+ "entry ending at line %d of file \"%s\"",
+ fi->line, fi->filename);
+ goto error;
+ }
+
+ if (! slapi_entry_flag_is_set(fi->entry->ep_entry,
+ SLAPI_ENTRY_FLAG_TOMBSTONE)) {
+ /*
+ * Only check for a parent and add to the entry2dn index if
+ * the entry is not a tombstone.
+ */
+ if (job->flags & FLAG_ABORT) {
+ goto error;
+ }
+
+ if (parent_status == IMPORT_ADD_OP_ATTRS_NO_PARENT) {
+ /* If this entry is a suffix entry, this is not a problem */
+ /* However, if it is not, this is an error---it means that
+ * someone tried to import an entry before importing its parent
+ * we reject the entry but carry on since we've not stored
+ * anything related to this entry.
+ */
+ if (! slapi_be_issuffix(inst->inst_be, backentry_get_sdn(fi->entry))) {
+ import_log_notice(job, "WARNING: Skipping entry \"%s\" "
+ "which has no parent, ending at line %d "
+ "of file \"%s\"",
+ slapi_entry_get_dn(fi->entry->ep_entry),
+ fi->line, fi->filename);
+ /* skip this one */
+ fi->bad = 1;
+ job->skipped++;
+ goto cont; /* below */
+ }
+ }
+ if (job->flags & FLAG_ABORT) {
+ goto error;
+ }
+
+
+ /* insert into the entrydn index */
+ ret = foreman_do_entrydn(job, fi);
+ if (ret == -1)
+ goto cont; /* skip entry */
+ if (ret != 0)
+ goto error;
+ }
+
+ if (job->flags & FLAG_ABORT) {
+ goto error;
+ }
+
+#if defined (UPGRADEDB)
+ if (!(job->flags & FLAG_REINDEXING))/* reindex reads data from id2entry */
+#endif
+ {
+ /* insert into the id2entry index
+ * (that isn't really an index -- it's the storehouse of the entries
+ * themselves.)
+ */
+ if ((ret = id2entry_add_ext(be, fi->entry, NULL, job->encrypt)) != 0) {
+ /* DB_RUNRECOVERY usually occurs if disk fills */
+ if (LDBM_OS_ERR_IS_DISKFULL(ret)) {
+ import_log_notice(job, "ERROR: OUT OF SPACE ON DISK or FILE TOO LARGE -- "
+ "Could not store the entry ending at line "
+ "%d of file \"%s\"",
+ fi->line, fi->filename);
+ } else if (ret == DB_RUNRECOVERY) {
+ import_log_notice(job, "FATAL ERROR: (LARGEFILE SUPPORT NOT ENABLED? OUT OF SPACE ON DISK?) -- "
+ "Could not store the entry ending at line "
+ "%d of file \"%s\"",
+ fi->line, fi->filename);
+ } else {
+ import_log_notice(job, "ERROR: Could not store the entry "
+ "ending at line %d of file \"%s\" -- "
+ "error %d", fi->line, fi->filename, ret);
+ }
+ goto error;
+ }
+ }
+
+ if (job->flags & FLAG_ABORT) {
+ goto error;
+ }
+
+ if (! slapi_entry_flag_is_set(fi->entry->ep_entry,
+ SLAPI_ENTRY_FLAG_TOMBSTONE)) {
+ /* parentid index
+ * (we have to do this here, because the parentID is dependent on
+ * looking up by entrydn.)
+ * Only add to the parent index if the entry is not a tombstone.
+ */
+ ret = foreman_do_parentid(job, fi->entry, parentid_ai);
+ if (ret != 0)
+ goto error;
+
+ /* Lastly, before we're finished with the entry, pass it to the
+ vlv code to see whether it's within the scope a VLV index. */
+ vlv_grok_new_import_entry(fi->entry, be);
+ }
+ if (job->flags & FLAG_ABORT) {
+ goto error;
+ }
+
+
+ /* Remove the entry from the cache (caused by id2entry_add) */
+#if defined (UPGRADEDB)
+ if (!(job->flags & FLAG_REINDEXING))/* reindex reads data from id2entry */
+#endif
+ cache_remove(&inst->inst_cache, fi->entry);
+ fi->entry->ep_refcnt = job->number_indexers;
+
+ cont:
+ if (job->flags & FLAG_ABORT) {
+ goto error;
+ }
+
+ job->ready_ID = id;
+ info->last_ID_processed = id;
+ id++;
+
+ if (job->flags & FLAG_ABORT){
+ goto error;
+ }
+ }
+
+ slapi_pblock_destroy(pb);
+ info->state = FINISHED;
+ return;
+
+error:
+ slapi_pblock_destroy(pb);
+ info->state = ABORTED;
+}
+
+
+/* worker thread:
+ * given an attribute, this worker plows through the entry FIFO, building
+ * up the attribute index.
+ */
+void import_worker(void *param)
+{
+ ImportWorkerInfo *info = (ImportWorkerInfo *)param;
+ ImportJob *job = info->job;
+ ldbm_instance *inst = job->inst;
+ backend *be = inst->inst_be;
+ PRIntervalTime sleeptime;
+ int finished = 0;
+ ID id = info->first_ID;
+ int ret = 0;
+ int idl_disposition = 0;
+ struct vlvIndex* vlv_index = NULL;
+ void *substring_key_buffer = NULL;
+ FifoItem *fi;
+ int is_objectclass_attribute;
+ int is_nsuniqueid_attribute;
+ void *attrlist_cursor;
+
+ PR_ASSERT(NULL != info);
+ PR_ASSERT(NULL != inst);
+
+ if (job->flags & FLAG_ABORT) {
+ goto error;
+ }
+
+ if (INDEX_VLV == info->index_info->ai->ai_indexmask) {
+ vlv_index = vlv_find_indexname(info->index_info->name, be);
+ if (NULL == vlv_index) {
+ goto error;
+ }
+ }
+
+ /*
+ * If the entry is a Tombstone, then we only add it to the nsuniqeid index
+ * and the idlist for (objectclass=tombstone). These two flags are just
+ * handy for working out what to do in this case.
+ */
+ is_objectclass_attribute =
+ (strcasecmp(info->index_info->name, "objectclass") == 0);
+ is_nsuniqueid_attribute =
+ (strcasecmp(info->index_info->name, SLAPI_ATTR_UNIQUEID) == 0);
+
+ if (1 != idl_get_idl_new()) {
+ /* Is there substring indexing going on here ? */
+ if ( (INDEX_SUB & info->index_info->ai->ai_indexmask) &&
+ (info->index_buffer_size > 0) ) {
+ /* Then make a key buffer thing */
+ ret = index_buffer_init(info->index_buffer_size, 0,
+ &substring_key_buffer);
+ if (0 != ret) {
+ import_log_notice(job, "IMPORT FAIL 1 (error %d)", ret);
+ }
+ }
+ }
+
+ sleeptime = PR_MillisecondsToInterval(import_sleep_time);
+ info->state = RUNNING;
+ info->last_ID_processed = id-1;
+
+ while (! finished) {
+ struct backentry *ep = NULL;
+ Slapi_Value **svals = NULL;
+ Slapi_Attr *attr = NULL;
+
+ if (job->flags & FLAG_ABORT) {
+ goto error;
+ }
+
+ /* entry can be NULL if it turned out to be bogus */
+ while (!finished && !ep) {
+ /* This worker thread must wait if the command flag is "PAUSE" or
+ * the entry corresponds to the current entry treated by the foreman
+ * thread, and the state is neither STOP nor ABORT
+ */
+ while (((info->command == PAUSE) || (id > job->ready_ID)) &&
+ (info->command != STOP) && (info->command != ABORT) && !(job->flags & FLAG_ABORT)) {
+ /* Check to see if we've been told to stop */
+ info->state = WAITING;
+ DS_Sleep(sleeptime);
+ }
+
+ if (info->command == STOP) {
+ finished = 1;
+ continue;
+ }
+ if (job->flags & FLAG_ABORT) {
+ goto error;
+ }
+
+ info->state = RUNNING;
+
+ /* Read that entry from the cache */
+ fi = import_fifo_fetch(job, id, 1);
+ ep = fi ? fi->entry : NULL;
+ if (!ep) {
+ /* skipping an entry that turned out to be bad */
+ info->last_ID_processed = id;
+ id++;
+ }
+ }
+ if (finished)
+ continue;
+
+ if (! slapi_entry_flag_is_set(fi->entry->ep_entry,
+ SLAPI_ENTRY_FLAG_TOMBSTONE)) {
+ /* This is not a tombstone entry. */
+ /* Is this a VLV index ? */
+
+ if (job->flags & FLAG_ABORT) {
+ goto error;
+ }
+
+ if (INDEX_VLV == info->index_info->ai->ai_indexmask) {
+ /* Yes, call VLV code -- needs pblock to find backend */
+ Slapi_PBlock *pb = slapi_pblock_new();
+
+ PR_ASSERT(NULL != vlv_index);
+ slapi_pblock_set(pb, SLAPI_BACKEND, be);
+ vlv_update_index(vlv_index, NULL, inst->inst_li, pb, NULL, ep);
+ slapi_pblock_destroy(pb);
+ } else {
+ /* No, process regular index */
+ /* Look for the attribute we're indexing and its subtypes */
+ /* For each attr write to the index */
+ attrlist_cursor = NULL;
+ while ((attr = attrlist_find_ex(ep->ep_entry->e_attrs,
+ info->index_info->name,
+ NULL,
+ NULL,
+ &attrlist_cursor)) != NULL) {
+
+ if (job->flags & FLAG_ABORT) {
+ goto error;
+ }
+ if(valueset_isempty(&(attr->a_present_values))) continue;
+ svals = attr_get_present_values(attr);
+ ret = index_addordel_values_ext_sv(be, info->index_info->name,
+ svals, NULL, ep->ep_id, BE_INDEX_ADD | (job->encrypt ? 0 : BE_INDEX_DONT_ENCRYPT), NULL, &idl_disposition,
+ substring_key_buffer);
+
+ if (0 != ret) {
+ /* Something went wrong, eg disk filled up */
+ goto error;
+ }
+ }
+ }
+ } else {
+ /* This is a Tombstone entry... we only add it to the nsuniqeid
+ * index and the idlist for (objectclass=nstombstone).
+ */
+ if (job->flags & FLAG_ABORT) {
+ goto error;
+ }
+ if (is_nsuniqueid_attribute) {
+ ret = index_addordel_string(be, SLAPI_ATTR_UNIQUEID,
+ slapi_entry_get_uniqueid(ep->ep_entry), ep->ep_id,
+ BE_INDEX_ADD, NULL);
+ if (0 != ret) {
+ /* Something went wrong, eg disk filled up */
+ goto error;
+ }
+ }
+ if (is_objectclass_attribute) {
+ ret = index_addordel_string(be, SLAPI_ATTR_OBJECTCLASS,
+ SLAPI_ATTR_VALUE_TOMBSTONE, ep->ep_id, BE_INDEX_ADD, NULL);
+ if (0 != ret) {
+ /* Something went wrong, eg disk filled up */
+ goto error;
+ }
+ }
+ }
+ import_decref_entry(ep);
+ info->last_ID_processed = id;
+ id++;
+
+ if (job->flags & FLAG_ABORT) {
+ goto error;
+ }
+ }
+
+ if (job->flags & FLAG_ABORT) {
+ goto error;
+ }
+
+
+ /* If we were buffering index keys, now flush them */
+ if (substring_key_buffer) {
+ ret = index_buffer_flush(substring_key_buffer,
+ inst->inst_be, NULL,
+ info->index_info->ai);
+ if (0 != ret) {
+ goto error;
+ }
+ index_buffer_terminate(substring_key_buffer);
+ }
+ info->state = FINISHED;
+ return;
+
+error:
+ if (ret == DB_RUNRECOVERY) {
+ LDAPDebug(LDAP_DEBUG_ANY,"cannot import; database recovery needed\n",
+ 0,0,0);
+ } else if (ret == DB_LOCK_DEADLOCK) {
+ /* can this occur? */
+ }
+
+ info->state = ABORTED;
+}
+
+
+
+/*
+ * import entries to a backend, over the wire -- entries will arrive
+ * asynchronously, so this method has no "producer" thread. instead, the
+ * front-end drops new entries in as they arrive.
+ *
+ * this is sometimes called "fast replica initialization".
+ *
+ * some of this code is duplicated from ldif2ldbm, but i don't think we
+ * can avoid it.
+ */
+static int bulk_import_start(Slapi_PBlock *pb)
+{
+ struct ldbminfo *li = NULL;
+ ImportJob *job = NULL;
+ backend *be = NULL;
+ PRThread *thread = NULL;
+ int ret = 0;
+
+ job = CALLOC(ImportJob);
+ if (job == NULL) {
+ LDAPDebug(LDAP_DEBUG_ANY, "not enough memory to do import job\n",
+ 0, 0, 0);
+ return -1;
+ }
+
+ slapi_pblock_get(pb, SLAPI_BACKEND, &be);
+ PR_ASSERT(be != NULL);
+ li = (struct ldbminfo *)(be->be_database->plg_private);
+ job->inst = (ldbm_instance *)be->be_instance_info;
+
+ /* check if an import/restore is already ongoing... */
+ PR_Lock(job->inst->inst_config_mutex);
+ if (job->inst->inst_flags & INST_FLAG_BUSY) {
+ PR_Unlock(job->inst->inst_config_mutex);
+ LDAPDebug(LDAP_DEBUG_ANY, "ldbm: '%s' is already in the middle of "
+ "another task and cannot be disturbed.\n",
+ job->inst->inst_name, 0, 0);
+ FREE(job);
+ return SLAPI_BI_ERR_BUSY;
+ }
+ job->inst->inst_flags |= INST_FLAG_BUSY;
+ PR_Unlock(job->inst->inst_config_mutex);
+
+ /* take backend offline */
+ slapi_mtn_be_disable(be);
+
+ /* get uniqueid info */
+ slapi_pblock_get(pb, SLAPI_LDIF2DB_GENERATE_UNIQUEID, &job->uuid_gen_type);
+ if (job->uuid_gen_type == SLAPI_UNIQUEID_GENERATE_NAME_BASED) {
+ char *namespaceid;
+
+ slapi_pblock_get(pb, SLAPI_LDIF2DB_NAMESPACEID, &namespaceid);
+ job->uuid_namespace = slapi_ch_strdup(namespaceid);
+ }
+
+ job->flags = 0; /* don't use files */
+ job->flags |= FLAG_INDEX_ATTRS;
+ job->flags |= FLAG_ONLINE;
+ job->starting_ID = 1;
+ job->first_ID = 1;
+
+ job->mothers = CALLOC(import_subcount_stuff);
+ /* how much space should we allocate to index buffering? */
+ job->job_index_buffer_size = import_get_index_buffer_size();
+ if (job->job_index_buffer_size == 0) {
+ /* 10% of the allocated cache size + one meg */
+ job->job_index_buffer_size = (job->inst->inst_li->li_dbcachesize/10) +
+ (1024*1024);
+ }
+ import_subcount_stuff_init(job->mothers);
+ job->wire_lock = PR_NewLock();
+ job->wire_cv = PR_NewCondVar(job->wire_lock);
+
+ /* COPIED from ldif2ldbm.c : */
+
+ /* shutdown this instance of the db */
+ cache_clear(&job->inst->inst_cache);
+ dblayer_instance_close(be);
+
+ /* Delete old database files */
+ dblayer_delete_instance_dir(be);
+ /* it's okay to fail -- it might already be gone */
+
+ /* dblayer_instance_start will init the id2entry index. */
+ /* it also (finally) fills in inst_dir_name */
+ ret = dblayer_instance_start(be, DBLAYER_IMPORT_MODE);
+ if (ret != 0)
+ goto fail;
+
+ /* END OF COPIED SECTION */
+
+ PR_Lock(job->wire_lock);
+ vlv_init(job->inst);
+
+ /* create thread for import_main, so we can return */
+ thread = PR_CreateThread(PR_USER_THREAD, import_main, (void *)job,
+ PR_PRIORITY_NORMAL, PR_GLOBAL_THREAD,
+ PR_JOINABLE_THREAD,
+ SLAPD_DEFAULT_THREAD_STACKSIZE);
+ if (thread == NULL) {
+ PRErrorCode prerr = PR_GetError();
+ LDAPDebug(LDAP_DEBUG_ANY, "unable to spawn import thread, "
+ SLAPI_COMPONENT_NAME_NSPR " error %d (%s)\n",
+ prerr, slapd_pr_strerror(prerr), 0);
+ PR_Unlock(job->wire_lock);
+ ret = -2;
+ goto fail;
+ }
+
+ job->main_thread = thread;
+ slapi_set_object_extension(li->li_bulk_import_object, pb->pb_conn,
+ li->li_bulk_import_handle, job);
+
+ /* wait for the import_main to signal that it's ready for entries */
+ /* (don't want to send the success code back to the LDAP client until
+ * we're ready for the adds to start rolling in)
+ */
+ PR_WaitCondVar(job->wire_cv, PR_INTERVAL_NO_TIMEOUT);
+ PR_Unlock(job->wire_lock);
+
+ return 0;
+
+fail:
+ PR_Lock(job->inst->inst_config_mutex);
+ job->inst->inst_flags &= ~INST_FLAG_BUSY;
+ PR_Unlock(job->inst->inst_config_mutex);
+ import_free_job(job);
+ FREE(job);
+ return ret;
+}
+
+/* returns 0 on success, or < 0 on error
+ *
+ * on error, the import process is aborted -- so if this returns an error,
+ * don't try to queue any more entries or you'll be sorry.
+ *
+ * flag_block in used to know if this thread should block when
+ * the fifo is full or return an error LDAP_BUSY
+ * Typically, import done on from the GUI or the command line will
+ * block while online import as used by the replication total update
+ * will not block
+ */
+static int bulk_import_queue(ImportJob *job, Slapi_Entry *entry, int flag_block)
+{
+ struct backentry *ep = NULL, *old_ep = NULL;
+ int idx;
+ ID id = job->lead_ID + 1;
+ Slapi_Attr *attr = NULL;
+ size_t newesize = 0;
+
+ PR_Lock(job->wire_lock);
+
+ /* generate uniqueid if necessary */
+ import_generate_uniqueid(job, entry);
+
+ /* make into backentry */
+ ep = import_make_backentry(entry, id);
+ if (!ep) {
+ import_abort_all(job, 1);
+ PR_Unlock(job->wire_lock);
+ return -1;
+ }
+
+ /* encode the password */
+ if (slapi_entry_attr_find(ep->ep_entry, "userpassword", &attr) == 0) {
+ Slapi_Value **va = attr_get_present_values(attr);
+
+ pw_encodevals( (Slapi_Value **)va ); /* jcm - had to cast away const */
+ }
+
+ /* Now we have this new entry, all decoded
+ * Next thing we need to do is:
+ * (1) see if the appropriate fifo location contains an
+ * entry which had been processed by the indexers.
+ * If so, proceed.
+ * If not, spin waiting for it to become free.
+ * (2) free the old entry and store the new one there.
+ * (3) Update the job progress indicators so the indexers
+ * can use the new entry.
+ */
+ idx = id % job->fifo.size;
+ old_ep = job->fifo.item[idx].entry;
+ if (old_ep) {
+ while ((old_ep->ep_refcnt > 0) && !(job->flags & FLAG_ABORT))
+ {
+ if (flag_block)
+ DS_Sleep(PR_MillisecondsToInterval(import_sleep_time));
+ else
+ {
+ PR_Unlock(job->wire_lock);
+ return LDAP_BUSY;
+ }
+ }
+
+ /* the producer could be running thru the fifo while
+ * everyone else is cycling to a new pass...
+ * double-check that this entry is < ready_ID
+ */
+ while ((old_ep->ep_id >= job->ready_ID) && !(job->flags & FLAG_ABORT))
+ {
+ if (flag_block)
+ DS_Sleep(PR_MillisecondsToInterval(import_sleep_time));
+ else
+ {
+ PR_Unlock(job->wire_lock);
+ return LDAP_BUSY;
+ }
+ }
+
+ if (job->flags & FLAG_ABORT) {
+ PR_Unlock(job->wire_lock);
+ return -2;
+ }
+
+ PR_ASSERT(old_ep == job->fifo.item[idx].entry);
+ job->fifo.item[idx].entry = NULL;
+ if (job->fifo.c_bsize > job->fifo.item[idx].esize)
+ job->fifo.c_bsize -= job->fifo.item[idx].esize;
+ else
+ job->fifo.c_bsize = 0;
+ backentry_free(&old_ep);
+ }
+
+ newesize = (slapi_entry_size(ep->ep_entry) + sizeof(struct backentry));
+ if (newesize > job->fifo.bsize) { /* entry too big */
+ char ebuf[BUFSIZ];
+ import_log_notice(job, "WARNING: skipping entry \"%s\"",
+ escape_string(slapi_entry_get_dn(ep->ep_entry), ebuf));
+ import_log_notice(job, "REASON: entry too large (%d bytes) for "
+ "the buffer size (%d bytes)", newesize, job->fifo.bsize);
+ backentry_free(&ep);
+ PR_Unlock(job->wire_lock);
+ return -1;
+ }
+ /* Now check if fifo has enough space for the new entry */
+ if ((job->fifo.c_bsize + newesize) > job->fifo.bsize) {
+ import_wait_for_space_in_fifo( job, newesize );
+ }
+
+ /* We have enough space */
+ job->fifo.item[idx].filename = "(bulk import)";
+ job->fifo.item[idx].line = 0;
+ job->fifo.item[idx].entry = ep;
+ job->fifo.item[idx].bad = 0;
+ job->fifo.item[idx].esize = newesize;
+
+ /* Add the entry size to total fifo size */
+ job->fifo.c_bsize += ep->ep_entry? job->fifo.item[idx].esize : 0;
+
+ /* Update the job to show our progress */
+ job->lead_ID = id;
+ if ((id - job->starting_ID) <= job->fifo.size) {
+ job->trailing_ID = job->starting_ID;
+ } else {
+ job->trailing_ID = id - job->fifo.size;
+ }
+
+ PR_Unlock(job->wire_lock);
+ return 0;
+}
+
+void *factory_constructor(void *object, void *parent)
+{
+ return NULL;
+}
+
+void factory_destructor(void *extension, void *object, void *parent)
+{
+ ImportJob *job = (ImportJob *)extension;
+ PRThread *thread;
+
+ if (extension == NULL)
+ return;
+
+ /* connection was destroyed while we were still storing the extension --
+ * this is bad news and means we have a bulk import that needs to be
+ * aborted!
+ */
+ thread = job->main_thread;
+ LDAPDebug(LDAP_DEBUG_ANY, "ERROR bulk import abandoned\n",
+ 0, 0, 0);
+ import_abort_all(job, 1);
+ /* wait for import_main to finish... */
+ PR_JoinThread(thread);
+ /* extension object is free'd by import_main */
+ return;
+}
+
+/* plugin entry function for replica init */
+int ldbm_back_wire_import(Slapi_PBlock *pb)
+{
+ struct ldbminfo *li;
+ backend *be = NULL;
+ ImportJob *job;
+ PRThread *thread;
+ int state;
+
+ slapi_pblock_get(pb, SLAPI_BACKEND, &be);
+ PR_ASSERT(be != NULL);
+ li = (struct ldbminfo *)(be->be_database->plg_private);
+ slapi_pblock_get(pb, SLAPI_BULK_IMPORT_STATE, &state);
+ if (state == SLAPI_BI_STATE_START) {
+ /* starting a new import */
+ return bulk_import_start(pb);
+ }
+
+ PR_ASSERT(pb->pb_conn != NULL);
+ if (pb->pb_conn != NULL) {
+ job = (ImportJob *)slapi_get_object_extension(li->li_bulk_import_object, pb->pb_conn, li->li_bulk_import_handle);
+ }
+
+ if ((job == NULL) || (pb->pb_conn == NULL)) {
+ /* import might be aborting */
+ return -1;
+ }
+
+ if (state == SLAPI_BI_STATE_ADD) {
+ /* continuing previous import */
+ if (! import_entry_belongs_here(pb->pb_import_entry,
+ job->inst->inst_be)) {
+ /* silently skip */
+ return 0;
+ }
+ return bulk_import_queue(job, pb->pb_import_entry,
+ job->flags & FLAG_USE_FILES);
+ }
+
+ thread = job->main_thread;
+
+ if (state == SLAPI_BI_STATE_DONE) {
+ /* finished with an import */
+ job->flags |= FLAG_PRODUCER_DONE;
+ /* "job" struct may vanish at any moment after we set the DONE
+ * flag, so keep a copy of the thread id in 'thread' for safekeeping.
+ */
+ /* wait for import_main to finish... */
+ PR_JoinThread(thread);
+ slapi_set_object_extension(li->li_bulk_import_object, pb->pb_conn,
+ li->li_bulk_import_handle, NULL);
+ return 0;
+ }
+
+ /* ??? unknown state */
+ LDAPDebug(LDAP_DEBUG_ANY,
+ "ERROR: ldbm_back_wire_import: unknown state %d\n",
+ state, 0, 0);
+ return -1;
+}
+
+/*
+ * backup index configuration
+ * this function is called from dblayer_backup (ldbm2archive)
+ * [547427] index config must not change between backup and restore
+ */
+#define DSE_INDEX "dse_index.ldif"
+#define DSE_INSTANCE "dse_instance.ldif"
+#define DSE_INDEX_FILTER "(objectclass=nsIndex)"
+#define DSE_INSTANCE_FILTER "(objectclass=nsBackendInstance)"
+static int
+dse_conf_backup_core(struct ldbminfo *li, char *dest_dir, char *file_name, char *filter)
+{
+ Slapi_PBlock *srch_pb = NULL;
+ Slapi_Entry **entries = NULL;
+ Slapi_Entry **ep = NULL;
+ Slapi_Attr *attr = NULL;
+ char *attr_name;
+ char *filename = NULL;
+ PRFileDesc *prfd = NULL;
+ int rval = 0;
+ int dlen = 0;
+ PRInt32 prrval;
+ char tmpbuf[BUFSIZ];
+ char *tp = NULL;
+
+ dlen = strlen(dest_dir);
+ if (0 == dlen)
+ {
+ filename = file_name;
+ }
+ else
+ {
+ filename = (char *)slapi_ch_malloc(strlen(file_name) + dlen + 2);
+ sprintf(filename, "%s/%s", dest_dir, file_name);
+ }
+ LDAPDebug(LDAP_DEBUG_TRACE, "dse_conf_backup(%s): backup file %s\n",
+ filter, filename, 0);
+
+ /* Open the file to write */
+ if ((prfd = PR_Open(filename, PR_RDWR | PR_CREATE_FILE | PR_TRUNCATE,
+ SLAPD_DEFAULT_FILE_MODE)) == NULL)
+ {
+ LDAPDebug(LDAP_DEBUG_ANY,
+ "dse_conf_backup(%s): open %s failed: (%s)\n",
+ filter, filename, slapd_pr_strerror(PR_GetError()));
+ rval = -1;
+ goto out;
+ }
+
+ srch_pb = slapi_pblock_new();
+ slapi_search_internal_set_pb(srch_pb, li->li_plugin->plg_dn,
+ LDAP_SCOPE_SUBTREE, filter, NULL, 0, NULL, NULL, li->li_identity, 0);
+ slapi_search_internal_pb(srch_pb);
+ slapi_pblock_get(srch_pb, SLAPI_PLUGIN_INTOP_SEARCH_ENTRIES, &entries);
+ for (ep = entries; ep != NULL && *ep != NULL; ep++)
+ {
+ size_t l = strlen(slapi_entry_get_dn_const(*ep)) + 5 /* "dn: \n" */;
+ LDAPDebug(LDAP_DEBUG_TRACE, "\ndn: %s\n",
+ slapi_entry_get_dn_const(*ep), 0, 0);
+
+ if (l <= BUFSIZ)
+ tp = tmpbuf;
+ else
+ tp = (char *)slapi_ch_malloc(l); /* should be very rare ... */
+ sprintf(tp, "dn: %s\n", slapi_entry_get_dn_const(*ep));
+ prrval = PR_Write(prfd, tp, l);
+ if ((size_t)prrval != l)
+ {
+ LDAPDebug(LDAP_DEBUG_ANY,
+ "dse_conf_backup(%s): write %s failed: %d (%s)\n",
+ filter, PR_GetError(), slapd_pr_strerror(PR_GetError()));
+ rval = -1;
+ if (l > BUFSIZ)
+ slapi_ch_free_string(&tp);
+ goto out;
+ }
+ if (l > BUFSIZ)
+ slapi_ch_free_string(&tp);
+
+ for (slapi_entry_first_attr(*ep, &attr); attr;
+ slapi_entry_next_attr(*ep, attr, &attr))
+ {
+ int i;
+ Slapi_Value *sval = NULL;
+ const struct berval *attr_val;
+ int attr_name_len;
+
+ slapi_attr_get_type(attr, &attr_name);
+ /* numsubordinates should not be backed up */
+ if (!strcasecmp("numsubordinates", attr_name))
+ continue;
+ attr_name_len = strlen(attr_name);
+ for (i = slapi_attr_first_value(attr, &sval); i != -1;
+ i = slapi_attr_next_value(attr, i, &sval))
+ {
+ attr_val = slapi_value_get_berval(sval);
+ l = strlen(attr_val->bv_val) + attr_name_len + 3; /* : \n" */
+ LDAPDebug(LDAP_DEBUG_TRACE, "%s: %s\n", attr_name,
+ attr_val->bv_val, 0);
+ if (l <= BUFSIZ)
+ tp = tmpbuf;
+ else
+ tp = (char *)slapi_ch_malloc(l);
+ sprintf(tp, "%s: %s\n", attr_name, attr_val->bv_val);
+ prrval = PR_Write(prfd, tp, l);
+ if ((size_t)prrval != l)
+ {
+ LDAPDebug(LDAP_DEBUG_ANY,
+ "dse_conf_backup(%s): write %s failed: %d (%s)\n",
+ filter, PR_GetError(), slapd_pr_strerror(PR_GetError()));
+ rval = -1;
+ if (l > BUFSIZ)
+ slapi_ch_free_string(&tp);
+ goto out;
+ }
+ if (l > BUFSIZ)
+ slapi_ch_free_string(&tp);
+ }
+ }
+ if (ep+1 != NULL && *(ep+1) != NULL)
+ {
+ prrval = PR_Write(prfd, "\n", 1);
+ if ((int)prrval != 1)
+ {
+ LDAPDebug(LDAP_DEBUG_ANY,
+ "dse_conf_backup(%s): write %s failed: %d (%s)\n",
+ filter, PR_GetError(), slapd_pr_strerror(PR_GetError()));
+ rval = -1;
+ goto out;
+ }
+ }
+ }
+
+out:
+ slapi_free_search_results_internal(srch_pb);
+ if (srch_pb)
+ {
+ slapi_pblock_destroy(srch_pb);
+ }
+
+ if (0 != dlen)
+ {
+ slapi_ch_free_string(&filename);
+ }
+
+ if (prfd)
+ {
+ prrval = PR_Close(prfd);
+ if (PR_SUCCESS != prrval)
+ {
+ LDAPDebug( LDAP_DEBUG_ANY,
+ "Fatal Error---Failed to back up dse indexes %d (%s)\n",
+ PR_GetError(), slapd_pr_strerror(PR_GetError()), 0);
+ rval = -1;
+ }
+ }
+
+ return rval;
+}
+
+int
+dse_conf_backup(struct ldbminfo *li, char *dest_dir)
+{
+ int rval = 0;
+ rval = dse_conf_backup_core(li, dest_dir, DSE_INSTANCE, DSE_INSTANCE_FILTER);
+ rval += dse_conf_backup_core(li, dest_dir, DSE_INDEX, DSE_INDEX_FILTER);
+ return rval;
+}
+
+/*
+ * read the backed up index configuration
+ * adjust them if the current configuration is different from it.
+ * this function is called from dblayer_restore (archive2ldbm)
+ * these functions are placed here to borrow import_get_entry
+ * [547427] index config must not change between backup and restore
+ */
+int
+dse_conf_verify_core(struct ldbminfo *li, char *src_dir, char *file_name, char *filter, char *log_str)
+{
+ char *filename = NULL;
+ int rval = 0;
+ ldif_context c;
+ int fd = -1;
+ int curr_lineno = 0;
+ int finished = 0;
+ int backup_entry_len = 256;
+ Slapi_Entry **backup_entries = NULL;
+ Slapi_Entry **bep = NULL;
+ Slapi_Entry **curr_entries = NULL;
+ Slapi_PBlock srch_pb;
+
+ filename = (char *)slapi_ch_malloc(strlen(file_name) + strlen(src_dir) + 2);
+ sprintf(filename, "%s/%s", src_dir, file_name);
+
+ if (PR_SUCCESS != PR_Access(filename, PR_ACCESS_READ_OK))
+ {
+ LDAPDebug(LDAP_DEBUG_ANY,
+ "Warning: config backup file %s not found in backup\n",
+ file_name, 0, 0);
+ rval = 0;
+ goto out;
+ }
+
+ fd = dblayer_open_huge_file(filename, O_RDONLY, 0);
+ if (fd < 0)
+ {
+ LDAPDebug(LDAP_DEBUG_ANY,
+ "Warning: can't open config backup file: %s\n", filename, 0, 0);
+ rval = -1;
+ goto out;
+ }
+
+ import_init_ldif(&c);
+ bep = backup_entries = (Slapi_Entry **)slapi_ch_calloc(1,
+ backup_entry_len * sizeof(Slapi_Entry *));
+
+ while (!finished)
+ {
+ char *estr = NULL;
+ Slapi_Entry *e = NULL;
+ estr = import_get_entry(&c, fd, &curr_lineno);
+
+ if (!estr)
+ break;
+
+ e = slapi_str2entry(estr, 0);
+ slapi_ch_free_string(&estr);
+ if (!e) {
+ LDAPDebug(LDAP_DEBUG_ANY, "WARNING: skipping bad LDIF entry "
+ "ending line %d of file \"%s\"", curr_lineno, filename, 0);
+ continue;
+ }
+ if (bep - backup_entries >= backup_entry_len)
+ {
+ backup_entries = (Slapi_Entry **)slapi_ch_realloc((char *)backup_entries,
+ 2 * backup_entry_len * sizeof(Slapi_Entry *));
+ bep = backup_entries + backup_entry_len;
+ backup_entry_len *= 2;
+ }
+ *bep = e;
+ bep++;
+ }
+ // 623986: terminate the list if we reallocated backup_entries
+ if (backup_entry_len > 256)
+ *bep = NULL;
+
+ pblock_init(&srch_pb);
+ slapi_search_internal_set_pb(&srch_pb, li->li_plugin->plg_dn,
+ LDAP_SCOPE_SUBTREE, filter, NULL, 0, NULL, NULL, li->li_identity, 0);
+ slapi_search_internal_pb(&srch_pb);
+ slapi_pblock_get(&srch_pb, SLAPI_PLUGIN_INTOP_SEARCH_ENTRIES, &curr_entries);
+
+ if (0 != slapi_entries_diff(backup_entries, curr_entries, 1 /* test_all */,
+ log_str, 1 /* force_update */, li->li_identity))
+ {
+ LDAPDebug(LDAP_DEBUG_ANY, "WARNING!!: current %s is "
+ "different from backed up configuration; "
+ "The backup is restored.\n", log_str, 0, 0);
+ }
+
+ slapi_free_search_results_internal(&srch_pb);
+ pblock_done(&srch_pb);
+ import_free_ldif(&c);
+out:
+ for (bep = backup_entries; bep && *bep; bep++)
+ slapi_entry_free(*bep);
+ slapi_ch_free((void **)&backup_entries);
+
+ slapi_ch_free_string(&filename);
+
+ if (fd > 0)
+ close(fd);
+
+ return rval;
+}
+
+int
+dse_conf_verify(struct ldbminfo *li, char *src_dir)
+{
+ int rval;
+ rval = dse_conf_verify_core(li, src_dir, DSE_INSTANCE, DSE_INSTANCE_FILTER,
+ "Instance Config");
+ rval += dse_conf_verify_core(li, src_dir, DSE_INDEX, DSE_INDEX_FILTER,
+ "Index Config");
+ return rval;
+}
diff --git a/ldap/servers/slapd/back-ldbm/import.c b/ldap/servers/slapd/back-ldbm/import.c
new file mode 100644
index 00000000..47e05fa6
--- /dev/null
+++ b/ldap/servers/slapd/back-ldbm/import.c
@@ -0,0 +1,1465 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+/*
+ * the "new" ("deluxe") backend import code
+ *
+ * please make sure you use 4-space indentation on this file.
+ */
+
+
+#include "back-ldbm.h"
+#include "vlv_srch.h"
+#include "import.h"
+
+#define ERR_IMPORT_ABORTED -23
+
+
+/********** routines to manipulate the entry fifo **********/
+
+/* this is pretty bogus -- could be a HUGE amount of memory */
+/* Not anymore with the Import Queue Adaptative Algorithm (Regulation) */
+#define MAX_FIFO_SIZE 8000
+
+static int import_fifo_init(ImportJob *job)
+{
+ ldbm_instance *inst = job->inst;
+
+ /* Work out how big the entry fifo can be */
+ if (inst->inst_cache.c_maxentries > 0)
+ job->fifo.size = inst->inst_cache.c_maxentries;
+ else
+ job->fifo.size = inst->inst_cache.c_maxsize / 1024; /* guess */
+
+ /* byte limit that should be respected to avoid memory starvation */
+ /* conservative computing: multiply by .8 to allow for reasonable overflow */
+ job->fifo.bsize = (inst->inst_cache.c_maxsize/10) << 3;
+
+ job->fifo.c_bsize = 0;
+
+ if (job->fifo.size > MAX_FIFO_SIZE)
+ job->fifo.size = MAX_FIFO_SIZE;
+ /* has to be at least 1 or 2, and anything less than about 100 destroys
+ * the point of doing all this optimization in the first place. */
+ if (job->fifo.size < 100)
+ job->fifo.size = 100;
+
+ /* Get memory for the entry fifo */
+ /* This is used to keep a ref'ed pointer to the last <cachesize>
+ * processed entries */
+ PR_ASSERT(NULL == job->fifo.item);
+ job->fifo.item = (FifoItem *)slapi_ch_calloc(job->fifo.size,
+ sizeof(FifoItem));
+ if (NULL == job->fifo.item) {
+ /* Memory allocation error */
+ return -1;
+ }
+ return 0;
+}
+
+FifoItem *import_fifo_fetch(ImportJob *job, ID id, int worker)
+{
+ int idx = id % job->fifo.size;
+ FifoItem *fi;
+
+ if (job->fifo.item) {
+ fi = &(job->fifo.item[idx]);
+ } else {
+ return NULL;
+ }
+ if (fi->entry) {
+ if (id != fi->entry->ep_id)
+ fi = NULL;
+ else if (worker) {
+ if (fi->bad) return NULL;
+ PR_ASSERT(fi->entry->ep_refcnt > 0);
+ }
+ }
+ return fi;
+}
+
+static void import_fifo_destroy(ImportJob *job)
+{
+ /* Free any entries in the fifo first */
+ struct backentry *be = NULL;
+ size_t i = 0;
+
+ for (i = 0; i < job->fifo.size; i++) {
+ be = job->fifo.item[i].entry;
+ backentry_free(&be);
+ job->fifo.item[i].entry = NULL;
+ job->fifo.item[i].filename = NULL;
+ }
+ free(job->fifo.item);
+ job->fifo.item = NULL;
+}
+
+
+/********** logging stuff **********/
+
+#define LOG_BUFFER 256
+
+/* this changes the 'nsTaskStatus' value, which is transient (anything logged
+ * here wipes out any previous status)
+ */
+static void import_log_status_start(ImportJob *job)
+{
+ if (! job->task_status)
+ job->task_status = (char *)slapi_ch_malloc(10 * LOG_BUFFER);
+ if (! job->task_status)
+ return; /* out of memory? */
+
+ job->task_status[0] = 0;
+}
+
+static void import_log_status_add_line(ImportJob *job, char *format, ...)
+{
+ va_list ap;
+ int len = 0;
+
+ if (! job->task_status)
+ return;
+ len = strlen(job->task_status);
+ if (len + 5 > (10 * LOG_BUFFER))
+ return; /* no room */
+
+ if (job->task_status[0])
+ strcat(job->task_status, "\n");
+
+ va_start(ap, format);
+ PR_vsnprintf(job->task_status + len, (10 * LOG_BUFFER) - len, format, ap);
+ va_end(ap);
+}
+
+static void import_log_status_done(ImportJob *job)
+{
+ if (job->task) {
+ int len = 0;
+ len = strlen(job->task_status);
+ slapi_task_log_status(job->task, "%s", job->task_status);
+ }
+}
+
+/* this adds a line to the 'nsTaskLog' value, which is cumulative (anything
+ * logged here is added to the end)
+ */
+void import_log_notice(ImportJob *job, char *format, ...)
+{
+ va_list ap;
+ char buffer[LOG_BUFFER];
+
+ va_start(ap, format);
+ PR_vsnprintf(buffer, LOG_BUFFER, format, ap);
+ va_end(ap);
+
+ if (job->task) {
+ slapi_task_log_notice(job->task, "%s", buffer);
+ }
+ /* also save it in the logs for posterity */
+ LDAPDebug(LDAP_DEBUG_ANY, "import %s: %s\n", job->inst->inst_name,
+ buffer, 0);
+}
+
+static int import_task_destroy(Slapi_Task *task)
+{
+ ImportJob *job = (ImportJob *)task->task_private;
+
+ if (task->task_log) {
+ slapi_ch_free((void **)&task->task_log);
+ }
+
+ if (task->task_status) {
+ slapi_ch_free((void **)&task->task_status);
+ }
+
+
+ if (job && job->task_status) {
+ slapi_ch_free((void **)&job->task_status);
+ job->task_status = NULL;
+ }
+ FREE(job);
+ task->task_private = NULL;
+ return 0;
+}
+
+static int import_task_abort(Slapi_Task *task)
+{
+ ImportJob *job;
+
+ /* don't log anything from here, because we're still holding the
+ * DSE lock for modify...
+ */
+
+ if (task->task_state == SLAPI_TASK_FINISHED) {
+ /* too late */
+ return 0;
+ }
+
+ /*
+ * Race condition.
+ * If the import thread happens to finish right now we're in trouble
+ * because it will free the job.
+ */
+
+ job = (ImportJob *)task->task_private;
+
+ import_abort_all(job, 0);
+ while (task->task_state != SLAPI_TASK_FINISHED)
+ DS_Sleep(PR_MillisecondsToInterval(100));
+
+
+ return 0;
+}
+
+
+/********** helper functions for importing **********/
+
+
+/* Function used to gather a list of indexed attrs */
+static int import_attr_callback(void *node, void *param)
+{
+ ImportJob *job = (ImportJob *)param;
+ struct attrinfo *a = (struct attrinfo *)node;
+
+ /* OK, so we now have hold of the attribute structure and the job info,
+ * let's see what we have. Remember that although this function is called
+ * many times, all these calls are in the context of a single thread, so we
+ * don't need to worry about protecting the data in the job structure.
+ */
+
+ /* We need to specifically exclude the entrydn & parentid indexes because
+ * we build those in the foreman thread.
+ */
+ if (IS_INDEXED(a->ai_indexmask) &&
+ (strcasecmp(a->ai_type, "entrydn") != 0) &&
+ (strcasecmp(a->ai_type, "parentid") != 0) &&
+ (strcasecmp(a->ai_type, "ancestorid") != 0) &&
+ (strcasecmp(a->ai_type, numsubordinates) != 0)) {
+ /* Make an import_index_info structure, fill it in and insert into the
+ * job's list */
+ IndexInfo *info = CALLOC(IndexInfo);
+
+ if (NULL == info) {
+ /* Memory allocation error */
+ return -1;
+ }
+ info->name = slapi_ch_strdup(a->ai_type);
+ info->ai = a;
+ if (NULL == info->name) {
+ /* Memory allocation error */
+ free(info);
+ return -1;
+ }
+ info->next = job->index_list;
+ job->index_list = info;
+ job->number_indexers++;
+ }
+ return 0;
+}
+
+static void import_set_index_buffer_size(ImportJob *job)
+{
+ IndexInfo *current_index = NULL;
+ size_t substring_index_count = 0;
+ size_t proposed_size = 0;
+
+ /* Count the substring indexes we have */
+ for (current_index = job->index_list; current_index != NULL;
+ current_index = current_index->next) {
+ if (current_index->ai->ai_indexmask & INDEX_SUB) {
+ substring_index_count++;
+ }
+ }
+ if (substring_index_count > 0) {
+ /* Make proposed size such that if all substring indices were
+ * reasonably full, we'd hit the target space */
+ proposed_size = (job->job_index_buffer_size / substring_index_count) /
+ IMPORT_INDEX_BUFFER_SIZE_CONSTANT;
+ if (proposed_size > IMPORT_MAX_INDEX_BUFFER_SIZE) {
+ proposed_size = IMPORT_MAX_INDEX_BUFFER_SIZE;
+ }
+ if (proposed_size < IMPORT_MIN_INDEX_BUFFER_SIZE) {
+ proposed_size = 0;
+ }
+ }
+
+ job->job_index_buffer_suggestion = proposed_size;
+}
+
+static void import_free_thread_data(ImportJob *job)
+{
+ /* DBDB free the lists etc */
+ ImportWorkerInfo *worker = job->worker_list;
+
+ while (worker != NULL) {
+ ImportWorkerInfo *asabird = worker;
+ worker = worker->next;
+ if (asabird->work_type != PRODUCER)
+ slapi_ch_free( (void**)&asabird);
+ }
+}
+
+void import_free_job(ImportJob *job)
+{
+ /* DBDB free the lists etc */
+ IndexInfo *index = job->index_list;
+
+ import_free_thread_data(job);
+ while (index != NULL) {
+ IndexInfo *asabird = index;
+ index = index->next;
+ slapi_ch_free( (void**)&asabird->name);
+ slapi_ch_free( (void**)&asabird);
+ }
+ job->index_list = NULL;
+ if (NULL != job->mothers) {
+ import_subcount_stuff_term(job->mothers);
+ slapi_ch_free( (void**)&job->mothers);
+ }
+
+ ldbm_back_free_incl_excl(job->include_subtrees, job->exclude_subtrees);
+ charray_free(job->input_filenames);
+ if (job->fifo.size)
+ import_fifo_destroy(job);
+ if (NULL != job->uuid_namespace)
+ slapi_ch_free((void **)&job->uuid_namespace);
+ if (job->wire_lock)
+ PR_DestroyLock(job->wire_lock);
+ if (job->wire_cv)
+ PR_DestroyCondVar(job->wire_cv);
+ slapi_ch_free((void **)&job->task_status);
+}
+
+/* determine if we are the correct backend for this entry
+ * (in a distributed suffix, some entries may be for other backends).
+ * if the entry's dn actually matches one of the suffixes of the be, we
+ * automatically take it as a belonging one, for such entries must be
+ * present in EVERY backend independently of the distribution applied.
+ */
+int import_entry_belongs_here(Slapi_Entry *e, backend *be)
+{
+ Slapi_Backend *retbe;
+ Slapi_DN *sdn = slapi_entry_get_sdn(e);
+
+ if (slapi_be_issuffix(be, sdn))
+ return 1;
+
+ retbe = slapi_mapping_tree_find_backend_for_sdn(sdn);
+ return (retbe == be);
+}
+
+
+/********** starting threads and stuff **********/
+
+/* Solaris is weird---we need an LWP per thread but NSPR doesn't give us
+ * one unless we make this magic belshe-call */
+/* Fixed on Solaris 8; NSPR supports PR_GLOBAL_BOUND_THREAD */
+#define CREATE_THREAD PR_CreateThread
+
+static void import_init_worker_info(ImportWorkerInfo *info, ImportJob *job)
+{
+ info->command = PAUSE;
+ info->job = job;
+ info->first_ID = job->first_ID;
+ info->index_buffer_size = job->job_index_buffer_suggestion;
+}
+
+static int import_start_threads(ImportJob *job)
+{
+ IndexInfo *current_index = NULL;
+ ImportWorkerInfo *foreman = NULL, *worker = NULL;
+
+ foreman = CALLOC(ImportWorkerInfo);
+ if (!foreman)
+ goto error;
+
+ /* start the foreman */
+ import_init_worker_info(foreman, job);
+ foreman->work_type = FOREMAN;
+ if (! CREATE_THREAD(PR_USER_THREAD, (VFP)import_foreman, foreman,
+ PR_PRIORITY_NORMAL, PR_GLOBAL_BOUND_THREAD,
+ PR_UNJOINABLE_THREAD, SLAPD_DEFAULT_THREAD_STACKSIZE)) {
+ PRErrorCode prerr = PR_GetError();
+ LDAPDebug(LDAP_DEBUG_ANY, "unable to spawn import foreman thread, "
+ SLAPI_COMPONENT_NAME_NSPR " error %d (%s)\n",
+ prerr, slapd_pr_strerror(prerr), 0);
+ FREE(foreman);
+ goto error;
+ }
+
+ foreman->next = job->worker_list;
+ job->worker_list = foreman;
+
+ /* Start follower threads, if we are doing attribute indexing */
+ current_index = job->index_list;
+ if (job->flags & FLAG_INDEX_ATTRS) {
+ while (current_index) {
+ /* make a new thread info structure */
+ worker = CALLOC(ImportWorkerInfo);
+ if (! worker)
+ goto error;
+
+ /* fill it in */
+ import_init_worker_info(worker, job);
+ worker->index_info = current_index;
+ worker->work_type = WORKER;
+
+ /* Start the thread */
+ if (! CREATE_THREAD(PR_USER_THREAD, (VFP)import_worker, worker,
+ PR_PRIORITY_NORMAL, PR_GLOBAL_BOUND_THREAD,
+ PR_UNJOINABLE_THREAD,
+ SLAPD_DEFAULT_THREAD_STACKSIZE)) {
+ PRErrorCode prerr = PR_GetError();
+ LDAPDebug(LDAP_DEBUG_ANY, "unable to spawn import worker thread, "
+ SLAPI_COMPONENT_NAME_NSPR " error %d (%s)\n",
+ prerr, slapd_pr_strerror(prerr), 0);
+ FREE(worker);
+ goto error;
+ }
+
+ /* link it onto the job's thread list */
+ worker->next = job->worker_list;
+ job->worker_list = worker;
+ current_index = current_index->next;
+ }
+ }
+ return 0;
+
+error:
+ import_log_notice(job, "Import thread creation failed.");
+ import_log_notice(job, "Aborting all import threads...");
+ import_abort_all(job, 1);
+ import_log_notice(job, "Import threads aborted.");
+ return -1;
+}
+
+
+/********** monitoring the worker threads **********/
+
+static void import_clear_progress_history(ImportJob *job)
+{
+ int i = 0;
+
+ for (i = 0; i < IMPORT_JOB_PROG_HISTORY_SIZE /*- 1*/; i++) {
+ job->progress_history[i] = job->first_ID;
+ job->progress_times[i] = job->start_time;
+ }
+ /* reset libdb cache stats */
+ job->inst->inst_cache_hits = job->inst->inst_cache_misses = 0;
+}
+
+static double import_grok_db_stats(ldbm_instance *inst)
+{
+ DB_MPOOL_STAT *mpstat = NULL;
+ DB_MPOOL_FSTAT **mpfstat = NULL;
+ int return_value = -1;
+ double cache_hit_ratio = 0.0;
+
+ return_value = dblayer_memp_stat_instance(inst, &mpstat, &mpfstat);
+
+ if (0 == return_value) {
+ unsigned long current_cache_hits = mpstat->st_cache_hit;
+ unsigned long current_cache_misses = mpstat->st_cache_miss;
+
+ if (inst->inst_cache_hits) {
+ unsigned long hit_delta, miss_delta;
+
+ hit_delta = current_cache_hits - inst->inst_cache_hits;
+ miss_delta = current_cache_misses - inst->inst_cache_misses;
+ if (hit_delta != 0) {
+ cache_hit_ratio = (double)hit_delta /
+ (double)(hit_delta + miss_delta);
+ }
+ }
+ inst->inst_cache_misses = current_cache_misses;
+ inst->inst_cache_hits = current_cache_hits;
+
+ if (mpstat)
+ free(mpstat);
+ if (mpfstat) {
+#if 1000*DB_VERSION_MAJOR + 100*DB_VERSION_MINOR + DB_VERSION_PATCH <= 3204
+ /* In DB 3.2.4 and earlier, we need to free each element */
+ DB_MPOOL_FSTAT **tfsp;
+ for (tfsp = mpfstat; *tfsp; tfsp++)
+ free(*tfsp);
+#endif
+ free(mpfstat);
+ }
+ }
+ return cache_hit_ratio;
+}
+
+static char* import_decode_worker_state(int state)
+{
+ switch (state) {
+ case WAITING:
+ return "W";
+ case RUNNING:
+ return "R";
+ case FINISHED:
+ return "F";
+ case ABORTED:
+ return "A";
+ default:
+ return "?";
+ }
+}
+
+static void import_print_worker_status(ImportWorkerInfo *info)
+{
+ char *name = (info->work_type == PRODUCER ? "Producer" :
+ (info->work_type == FOREMAN ? "Foreman" :
+ info->index_info->name));
+
+ import_log_status_add_line(info->job,
+ "%-25s %s%10ld %7.1f", name,
+ import_decode_worker_state(info->state),
+ info->last_ID_processed, info->rate);
+}
+
+
+#define IMPORT_CHUNK_TEST_HOLDOFF_TIME (5*60) /* Seconds */
+
+/* Got to be lower than this: */
+#define IMPORT_CHUNK_TEST_CACHE_HIT_RATIO (0.99)
+/* Less than half as fast as we were doing: */
+#define IMPORT_CHUNK_TEST_SLOWDOWN_RATIO_A (0.5)
+/* A lot less fast than we were doing: */
+#define IMPORT_CHUNK_TEST_SLOWDOWN_RATIO_B (0.1)
+
+static int import_throw_in_towel(ImportJob *job, time_t current_time,
+ ID trailing_ID)
+{
+ static int number_of_times_here = 0;
+
+ /* secret -c option allows specific chunk size to be set... */
+ if (job->merge_chunk_size != 0) {
+ if ((0 != job->lead_ID) &&
+ (trailing_ID > job->first_ID) &&
+ (trailing_ID - job->first_ID > job->merge_chunk_size)) {
+ return 1;
+ }
+ return 0;
+ }
+
+ /* Check stats to decide whether we're getting bogged down and should
+ * terminate this pass.
+ */
+
+ /* Check #1 : are we more than 10 minutes into the chunk ? */
+ if (current_time - job->start_time > IMPORT_CHUNK_TEST_HOLDOFF_TIME) {
+ /* Check #2 : Have we slowed down considerably recently ? */
+ if ((job->recent_progress_rate / job->average_progress_rate) <
+ IMPORT_CHUNK_TEST_SLOWDOWN_RATIO_A) {
+ /* Check #3: Cache performing poorly---the puported reason
+ * for the slowdown */
+ if (job->cache_hit_ratio < IMPORT_CHUNK_TEST_CACHE_HIT_RATIO) {
+ /* We have a winner ! */
+ import_log_notice(job, "Decided to end this pass because "
+ "the progress rate has dropped below "
+ "the %.0f%% threshold.",
+ IMPORT_CHUNK_TEST_SLOWDOWN_RATIO_A*100.0);
+ return 1;
+ }
+ } else {
+ if ((job->recent_progress_rate / job->average_progress_rate) <
+ IMPORT_CHUNK_TEST_SLOWDOWN_RATIO_B) {
+ /* Alternative check: have we really, really slowed down,
+ * without the test for cache overflow? */
+ /* This is designed to catch the case where the cache has
+ * been misconfigured too large */
+ if (number_of_times_here > 10) {
+ /* Got to get here ten times at least */
+ import_log_notice(job, "Decided to end this pass "
+ "because the progress rate "
+ "plummeted below %.0f%%",
+ IMPORT_CHUNK_TEST_SLOWDOWN_RATIO_B*100.0);
+ return 1;
+ }
+ number_of_times_here++;
+ }
+ }
+ }
+
+ number_of_times_here = 0;
+ return 0;
+}
+
+static void import_push_progress_history(ImportJob *job, ID current_id,
+ time_t current_time)
+{
+ int i = 0;
+
+ for (i = 0; i < IMPORT_JOB_PROG_HISTORY_SIZE - 1; i++) {
+ job->progress_history[i] = job->progress_history[i+1];
+ job->progress_times[i] = job->progress_times[i+1];
+ }
+ job->progress_history[i] = current_id;
+ job->progress_times[i] = current_time;
+}
+
+static void import_calc_rate(ImportWorkerInfo *info, int time_interval)
+{
+ size_t ids = info->last_ID_processed - info->previous_ID_counted;
+ double rate = (double)ids / time_interval;
+
+ if ( (info->previous_ID_counted != 0) && (info->last_ID_processed != 0) ) {
+ info->rate = rate;
+ } else {
+ info->rate = 0;
+ }
+ info->previous_ID_counted = info->last_ID_processed;
+}
+
+/* find the rate (ids/time) of work from a worker thread between history
+ * marks A and B.
+ */
+#define HISTORY(N) (job->progress_history[N])
+#define TIMES(N) (job->progress_times[N])
+#define PROGRESS(A, B) ((HISTORY(B) > HISTORY(A)) ? \
+ ((double)(HISTORY(B) - HISTORY(A)) / \
+ (double)(TIMES(B) - TIMES(A))) : \
+ (double)0)
+
+static int import_monitor_threads(ImportJob *job, int *status)
+{
+ PRIntervalTime tenthsecond = PR_MillisecondsToInterval(100);
+ ImportWorkerInfo *current_worker = NULL;
+ ImportWorkerInfo *producer = NULL, *foreman = NULL;
+ int finished = 0;
+ int giveup = 0;
+ int count = 1; /* 1 to prevent premature status report */
+ int producer_done = 0;
+ const int display_interval = 200;
+ time_t time_now = 0;
+ time_t last_time = 0;
+ time_t time_interval = 0;
+
+
+ for (current_worker = job->worker_list; current_worker != NULL;
+ current_worker = current_worker->next) {
+ current_worker->command = RUN;
+ if (current_worker->work_type == PRODUCER)
+ producer = current_worker;
+ if (current_worker->work_type == FOREMAN)
+ foreman = current_worker;
+ }
+
+
+ if (job->flags & FLAG_USE_FILES)
+ PR_ASSERT(producer != NULL);
+ PR_ASSERT(foreman != NULL);
+
+ time(&last_time);
+ job->start_time = last_time;
+ import_clear_progress_history(job);
+
+ while (!finished) {
+ ID trailing_ID = NOID;
+
+ DS_Sleep(tenthsecond);
+ finished = 1;
+
+ /* First calculate the time interval since last reported */
+ if (0 == (count % display_interval)) {
+ time(&time_now);
+ time_interval = time_now - last_time;
+ last_time = time_now;
+ /* Now calculate our rate of progress overall for this chunk */
+ if (time_now != job->start_time) {
+ /* log a cute chart of the worker progress */
+ import_log_status_start(job);
+ import_log_status_add_line(job,
+ "Index status for import of %s:", job->inst->inst_name);
+ import_log_status_add_line(job,
+ "-------Index Task-------State---Entry----Rate-");
+
+ import_push_progress_history(job, foreman->last_ID_processed,
+ time_now);
+ job->average_progress_rate =
+ (double)(HISTORY(IMPORT_JOB_PROG_HISTORY_SIZE-1)+1 - foreman->first_ID) /
+ (double)(TIMES(IMPORT_JOB_PROG_HISTORY_SIZE-1) - job->start_time);
+ job->recent_progress_rate =
+ PROGRESS(0, IMPORT_JOB_PROG_HISTORY_SIZE-1);
+ job->cache_hit_ratio = import_grok_db_stats(job->inst);
+ }
+ }
+
+ for (current_worker = job->worker_list; current_worker != NULL;
+ current_worker = current_worker->next) {
+ /* Calculate the ID at which the slowest worker is currently
+ * processing */
+ if ((trailing_ID > current_worker->last_ID_processed) &&
+ (current_worker->work_type == WORKER)) {
+ trailing_ID = current_worker->last_ID_processed;
+ }
+ if (0 == (count % display_interval) && time_interval) {
+ import_calc_rate(current_worker, time_interval);
+ import_print_worker_status(current_worker);
+ }
+ if (current_worker->state != FINISHED) {
+ finished = 0;
+ }
+ if (current_worker->state == ABORTED) {
+ goto error_abort;
+ }
+ }
+
+ if ((0 == (count % display_interval)) &&
+ (job->start_time != time_now)) {
+ char buffer[256], *p = buffer;
+
+ import_log_status_done(job);
+ p += sprintf(p, "Processed %lu entries ", (u_long)job->ready_ID);
+ if (job->total_pass > 1)
+ p += sprintf(p, "(pass %d) ", job->total_pass);
+
+ p += sprintf(p, "-- average rate %.1f/sec, ",
+ job->average_progress_rate);
+ p += sprintf(p, "recent rate %.1f/sec, ",
+ job->recent_progress_rate);
+ p += sprintf(p, "hit ratio %.0f%%", job->cache_hit_ratio * 100.0);
+ import_log_notice(job, "%s", buffer);
+ }
+
+ /* Then let's see if it's time to complete this import pass */
+ if (!giveup) {
+ giveup = import_throw_in_towel(job, time_now, trailing_ID);
+ if (giveup) {
+ /* If so, signal the lead thread to stop */
+ import_log_notice(job, "Ending pass number %d ...",
+ job->total_pass);
+ foreman->command = STOP;
+ while (foreman->state != FINISHED) {
+ DS_Sleep(tenthsecond);
+ }
+ import_log_notice(job, "Foreman is done; waiting for "
+ "workers to finish...");
+ }
+ }
+
+ /* if the producer is finished, and the foreman has caught up... */
+ if (producer) {
+ producer_done = (producer->state == FINISHED);
+ } else {
+ producer_done = (job->flags & FLAG_PRODUCER_DONE);
+ }
+ if (producer_done && (job->lead_ID == job->ready_ID)) {
+ /* tell the foreman to stop if he's still working. */
+ if (foreman->state != FINISHED)
+ foreman->command = STOP;
+
+ /* if all the workers are caught up too, we're done */
+ if (trailing_ID == job->lead_ID)
+ break;
+ }
+
+ /* if the foreman is done (end of pass) and the worker threads
+ * have caught up...
+ */
+ if ((foreman->state == FINISHED) && (job->ready_ID == trailing_ID)) {
+ break;
+ }
+
+ count++;
+ }
+
+ import_log_notice(job, "Workers finished; cleaning up...");
+
+ /* Now tell all the workers to stop */
+ for (current_worker = job->worker_list; current_worker != NULL;
+ current_worker = current_worker->next) {
+ if (current_worker->work_type != PRODUCER)
+ current_worker->command = STOP;
+ }
+
+ /* Having done that, wait for them to say that they've stopped */
+ for (current_worker = job->worker_list; current_worker != NULL; ) {
+ if ((current_worker->state != FINISHED) &&
+ (current_worker->state != ABORTED) &&
+ (current_worker->work_type != PRODUCER)) {
+ DS_Sleep(tenthsecond); /* Only sleep if we hit a thread that is still not done */
+ continue;
+ } else {
+ current_worker = current_worker->next;
+ }
+ }
+ import_log_notice(job, "Workers cleaned up.");
+
+ /* If we're here and giveup is true, and the primary hadn't finished
+ * processing the input files, we need to return IMPORT_INCOMPLETE_PASS */
+ if (giveup && (job->input_filenames || (job->flags & FLAG_ONLINE) ||
+ (job->flags & FLAG_REINDEXING /* support multi-pass */))) {
+ if (producer_done && (job->ready_ID == job->lead_ID)) {
+ /* foreman caught up with the producer, and the producer is
+ * done.
+ */
+ *status = IMPORT_COMPLETE_PASS;
+ } else {
+ *status = IMPORT_INCOMPLETE_PASS;
+ }
+ } else {
+ *status = IMPORT_COMPLETE_PASS;
+ }
+ return 0;
+
+error_abort:
+ return ERR_IMPORT_ABORTED;
+}
+
+
+/********** running passes **********/
+
+static int import_run_pass(ImportJob *job, int *status)
+{
+ int ret = 0;
+
+ /* Start the threads running */
+ ret = import_start_threads(job);
+ if (ret != 0) {
+ import_log_notice(job, "Starting threads failed: %d\n", ret);
+ goto error;
+ }
+
+ /* Monitor the threads until we're done or fail */
+ ret = import_monitor_threads(job, status);
+ if (ret == ERR_IMPORT_ABORTED) {
+ goto error;
+ } else if (ret != 0) {
+ import_log_notice(job, "Thread monitoring aborted: %d\n", ret);
+ goto error;
+ }
+
+error:
+ return ret;
+}
+
+static void import_set_abort_flag_all(ImportJob *job, int wait_for_them)
+{
+
+ ImportWorkerInfo *worker;
+
+ /* tell all the worker threads to abort */
+ job->flags |= FLAG_ABORT;
+
+ /* setting of the flag in the job will be detected in the worker, foreman
+ * threads and if there are any threads which have a sleeptime 200 msecs
+ * = import_sleep_time; after that time, they will examine the condition
+ * (job->flags & FLAG_ABORT) which will unblock the thread to proceed to
+ * abort. Hence, we will sleep here for atleast 3 sec to make sure clean
+ * up occurs */
+ /* allow all the aborts to be processed */
+ DS_Sleep(PR_MillisecondsToInterval(3000));
+
+ if (wait_for_them) {
+ /* Having done that, wait for them to say that they've stopped */
+ for (worker = job->worker_list; worker != NULL; ) {
+ DS_Sleep(PR_MillisecondsToInterval(100));
+ if ((worker->state != FINISHED) &&
+ (worker->state != ABORTED)){
+ continue;
+ }
+ else{
+ worker = worker->next;
+ }
+ }
+ }
+
+}
+
+
+/* tell all the threads to abort */
+void import_abort_all(ImportJob *job, int wait_for_them)
+{
+ ImportWorkerInfo *worker;
+
+ /* tell all the worker threads to abort */
+ job->flags |= FLAG_ABORT;
+
+ for (worker = job->worker_list; worker; worker = worker->next)
+ worker->command = ABORT;
+
+ if (wait_for_them) {
+ /* Having done that, wait for them to say that they've stopped */
+ for (worker = job->worker_list; worker != NULL; ) {
+ DS_Sleep(PR_MillisecondsToInterval(100));
+ if ((worker->state != FINISHED) &&
+ (worker->state != ABORTED))
+ continue;
+ else
+ worker = worker->next;
+ }
+ }
+}
+
+/* Helper function to make up filenames */
+int import_make_merge_filenames(char *directory, char *indexname, int pass,
+ char **oldname, char **newname)
+{
+ /* Filenames look like this: attributename<LDBM_FILENAME_SUFFIX>
+ and need to be renamed to: attributename<LDBM_FILENAME_SUFFIX>.n
+ where n is the pass number.
+ */
+ size_t oldname_length = strlen(directory) + 1 + strlen(indexname) +
+ strlen(LDBM_FILENAME_SUFFIX) + 1 ;
+ /* Enough space for an 8-digit pass number */
+ size_t newname_length = oldname_length + 9;
+
+ *oldname = slapi_ch_malloc(oldname_length);
+ if (NULL == oldname)
+ return -1;
+ *newname = slapi_ch_malloc(newname_length);
+ if (NULL == newname)
+ return -1;
+ sprintf(*oldname, "%s/%s%s", directory, indexname, LDBM_FILENAME_SUFFIX);
+ sprintf(*newname, "%s/%s.%d%s", directory, indexname, pass,
+ LDBM_FILENAME_SUFFIX);
+ return 0;
+}
+
+/* Task here is as follows:
+ * First, if this is pass #1, check for the presence of a merge
+ * directory. If it is not present, create it.
+ * If it is present, delete all the files in it.
+ * Then, flush the dblayer and close files.
+ * Now create a numbered subdir of the merge directory for this pass.
+ * Next, move the index files, except entrydn, parentid and id2entry to
+ * the merge subdirectory. Important to move if we can, because
+ * that can be millions of times faster than a copy.
+ * Finally open the dblayer back up because the caller expects
+ * us to not muck with it.
+ */
+static int import_sweep_after_pass(ImportJob *job)
+{
+ backend *be = job->inst->inst_be;
+ int ret = 0;
+
+ import_log_notice(job, "Sweeping files for merging later...");
+
+ ret = dblayer_instance_close(be);
+
+ if (0 == ret) {
+ /* Walk the list of index jobs */
+ ImportWorkerInfo *current_worker = NULL;
+
+ for (current_worker = job->worker_list; current_worker != NULL;
+ current_worker = current_worker->next) {
+ /* Foreach job, rename the file to <filename>.n, where n is the
+ * pass number */
+ if ((current_worker->work_type != FOREMAN) &&
+ (current_worker->work_type != PRODUCER) &&
+ (strcasecmp(current_worker->index_info->name, "parentid") != 0)) {
+ char *newname = NULL;
+ char *oldname = NULL;
+
+ ret = import_make_merge_filenames(job->inst->inst_dir_name,
+ current_worker->index_info->name, job->current_pass,
+ &oldname, &newname);
+ if (0 != ret) {
+ break;
+ }
+ if (PR_Access(oldname, PR_ACCESS_EXISTS) == PR_SUCCESS) {
+ ret = PR_Rename(oldname, newname);
+ if (ret != PR_SUCCESS) {
+ PRErrorCode prerr = PR_GetError();
+ import_log_notice(job, "Failed to rename file \"%s\" to \"%s\", "
+ SLAPI_COMPONENT_NAME_NSPR " error %d (%s)",
+ oldname, newname, prerr, slapd_pr_strerror(prerr));
+ slapi_ch_free( (void**)&newname);
+ slapi_ch_free( (void**)&oldname);
+ break;
+ }
+ }
+ slapi_ch_free( (void**)&newname);
+ slapi_ch_free( (void**)&oldname);
+ }
+ }
+
+ ret = dblayer_instance_start(be, DBLAYER_IMPORT_MODE);
+ }
+
+ if (0 == ret) {
+ import_log_notice(job, "Sweep done.");
+ } else {
+ if (ENOSPC == ret) {
+ import_log_notice(job, "ERROR: NO DISK SPACE LEFT in sweep phase");
+ } else {
+ import_log_notice(job, "ERROR: Sweep phase error %d (%s)", ret,
+ dblayer_strerror(ret));
+ }
+ }
+
+ return ret;
+}
+
+/* when the import is done, this function is called to bring stuff back up.
+ * returns 0 on success; anything else is an error
+ */
+static int import_all_done(ImportJob *job, int ret)
+{
+ ldbm_instance *inst = job->inst;
+
+ /* Writing this file indicates to future server startups that
+ * the db is OK */
+ if (ret == 0) {
+ char inst_dir[MAXPATHLEN*2];
+ char *inst_dirp = NULL;
+ inst_dirp = dblayer_get_full_inst_dir(inst->inst_li, inst,
+ inst_dir, MAXPATHLEN*2);
+ ret = dbversion_write(inst->inst_li, inst_dirp, NULL);
+ if (inst_dirp != inst_dir)
+ slapi_ch_free_string(&inst_dirp);
+ }
+
+ if (job->task != NULL && 0 == job->task->task_refcount) {
+ /* exit code */
+ job->task->task_exitcode = ret;
+ job->task->task_state = SLAPI_TASK_FINISHED;
+ job->task->task_progress = job->task->task_work;
+ job->task->task_private = NULL;
+ slapi_task_status_changed(job->task);
+ }
+
+ if (job->flags & FLAG_ONLINE) {
+ /* start up the instance */
+ ret = dblayer_instance_start(job->inst->inst_be, DBLAYER_NORMAL_MODE);
+ if (ret != 0)
+ return ret;
+
+ /* bring backend online again */
+ slapi_mtn_be_enable(inst->inst_be);
+ }
+
+ return ret;
+}
+
+
+int import_main_offline(void *arg)
+{
+ ImportJob *job = (ImportJob *)arg;
+ ldbm_instance *inst = job->inst;
+ backend *be = inst->inst_be;
+ int ret = 0;
+ time_t beginning = 0;
+ time_t end = 0;
+ int finished = 0;
+ int status = 0;
+ int verbose = 1;
+ ImportWorkerInfo *producer = NULL;
+
+ if (job->task)
+ job->task->task_refcount++;
+
+ PR_ASSERT(inst != NULL);
+ time(&beginning);
+
+ /* Decide which indexes are needed */
+ if (job->flags & FLAG_INDEX_ATTRS) {
+ /* Here, we get an AVL tree which contains nodes for all attributes
+ * in the schema. Given this tree, we need to identify those nodes
+ * which are marked for indexing. */
+ avl_apply(job->inst->inst_attrs, (IFP)import_attr_callback,
+ (caddr_t)job, -1, AVL_INORDER);
+ vlv_getindices((IFP)import_attr_callback, (void *)job, be);
+ }
+
+ /* Determine how much index buffering space to allocate to each index */
+ import_set_index_buffer_size(job);
+
+ /* initialize the entry FIFO */
+ ret = import_fifo_init(job);
+ if (ret) {
+ if (! (job->flags & FLAG_USE_FILES)) {
+ PR_Lock(job->wire_lock);
+ PR_NotifyCondVar(job->wire_cv);
+ PR_Unlock(job->wire_lock);
+ }
+ goto error;
+ }
+
+ if (job->flags & FLAG_USE_FILES) {
+ /* importing from files: start up a producer thread to read the
+ * files and queue them
+ */
+ producer = CALLOC(ImportWorkerInfo);
+ if (! producer)
+ goto error;
+
+ /* start the producer */
+ import_init_worker_info(producer, job);
+ producer->work_type = PRODUCER;
+#if defined(UPGRADEDB)
+ if (job->flags & FLAG_REINDEXING)
+ {
+ if (! CREATE_THREAD(PR_USER_THREAD, (VFP)index_producer, producer,
+ PR_PRIORITY_NORMAL, PR_GLOBAL_BOUND_THREAD,
+ PR_UNJOINABLE_THREAD,
+ SLAPD_DEFAULT_THREAD_STACKSIZE)) {
+ PRErrorCode prerr = PR_GetError();
+ LDAPDebug(LDAP_DEBUG_ANY,
+ "unable to spawn index producer thread, "
+ SLAPI_COMPONENT_NAME_NSPR " error %d (%s)\n",
+ prerr, slapd_pr_strerror(prerr), 0);
+ goto error;
+ }
+ }
+ else
+#endif
+ {
+ import_log_notice(job, "Beginning import job...");
+ if (! CREATE_THREAD(PR_USER_THREAD, (VFP)import_producer, producer,
+ PR_PRIORITY_NORMAL, PR_GLOBAL_BOUND_THREAD,
+ PR_UNJOINABLE_THREAD,
+ SLAPD_DEFAULT_THREAD_STACKSIZE)) {
+ PRErrorCode prerr = PR_GetError();
+ LDAPDebug(LDAP_DEBUG_ANY,
+ "unable to spawn import producer thread, "
+ SLAPI_COMPONENT_NAME_NSPR " error %d (%s)\n",
+ prerr, slapd_pr_strerror(prerr), 0);
+ goto error;
+ }
+ }
+
+ if (0 == job->job_index_buffer_suggestion)
+ import_log_notice(job, "Index buffering is disabled.");
+ else
+ import_log_notice(job,
+ "Index buffering enabled with bucket size %lu",
+ job->job_index_buffer_suggestion);
+
+ job->worker_list = producer;
+ } else {
+ /* release the startup lock and let the entries start queueing up
+ * in for import */
+ PR_Lock(job->wire_lock);
+ PR_NotifyCondVar(job->wire_cv);
+ PR_Unlock(job->wire_lock);
+ }
+
+ /* Run as many passes as we need to complete the job or die honourably in
+ * the attempt */
+ while (! finished) {
+ job->current_pass++;
+ job->total_pass++;
+ ret = import_run_pass(job, &status);
+ /* The following could have happened:
+ * (a) Some error happened such that we're hosed.
+ * This is indicated by a non-zero return code.
+ * (b) We finished the complete file without needing a second pass
+ * This is indicated by a zero return code and a status of
+ * IMPORT_COMPLETE_PASS and current_pass == 1;
+ * (c) We completed a pass and need at least another one
+ * This is indicated by a zero return code and a status of
+ * IMPORT_INCOMPLETE_PASS
+ * (d) We just completed what turned out to be the last in a
+ * series of passes
+ * This is indicated by a zero return code and a status of
+ * IMPORT_COMPLETE_PASS and current_pass > 1
+ */
+ if (ret == ERR_IMPORT_ABORTED) {
+ /* at least one of the threads has aborted -- shut down ALL
+ * of the threads */
+ import_log_notice(job, "Aborting all import threads...");
+ /* this abort sets the abort flag on the threads and will block for
+ * the exit of all threads
+ */
+ import_set_abort_flag_all(job, 1);
+ import_log_notice(job, "Import threads aborted.");
+ goto error;
+ }
+
+ if (0 != ret) {
+ /* Some horrible fate has befallen the import */
+ import_log_notice(job, "Fatal pass error %d", ret);
+ goto error;
+ }
+
+ /* No error, but a number of possibilities */
+ if ( IMPORT_COMPLETE_PASS == status ) {
+ if (1 == job->current_pass) {
+ /* We're done !!!! */ ;
+ } else {
+ /* Save the files, then merge */
+ ret = import_sweep_after_pass(job);
+ if (0 != ret) {
+ goto error;
+ }
+ ret = import_mega_merge(job);
+ if (0 != ret) {
+ goto error;
+ }
+ }
+ finished = 1;
+ } else {
+ if (IMPORT_INCOMPLETE_PASS == status) {
+ /* Need to go round again */
+ /* Time to save the files we've built for later */
+ ret = import_sweep_after_pass(job);
+ if (0 != ret) {
+ goto error;
+ }
+ if ( (inst->inst_li->li_maxpassbeforemerge != 0) &&
+ (job->current_pass > inst->inst_li->li_maxpassbeforemerge) )
+ {
+ ret = import_mega_merge(job);
+ if (0 != ret) {
+ goto error;
+ }
+ job->current_pass = 1;
+ ret = import_sweep_after_pass(job);
+ if (0 != ret) {
+ goto error;
+ }
+ }
+
+ /* Fixup the first_ID value to reflect previous work */
+ job->first_ID = job->ready_ID + 1;
+ import_free_thread_data(job);
+ job->worker_list = producer;
+ import_log_notice(job, "Beginning pass number %d",
+ job->total_pass+1);
+ } else {
+ /* Bizarro-slapd */
+ goto error;
+ }
+ }
+ }
+
+ /* kill the producer now; we're done */
+ if (producer) {
+ import_log_notice(job, "Cleaning up producer thread...");
+ producer->command = STOP;
+ /* wait for the lead thread to stop */
+ while (producer->state != FINISHED) {
+ DS_Sleep(PR_MillisecondsToInterval(100));
+ }
+ }
+
+ /* Now do the numsubordinates attribute */
+ import_log_notice(job, "Indexing complete. Post-processing...");
+ /* [610066] reindexed db cannot be used in the following backup/restore */
+ if ( !(job->flags & FLAG_REINDEXING) &&
+ (ret = update_subordinatecounts(be, job->mothers, NULL)) != 0) {
+ import_log_notice(job, "Failed to update numsubordinates attributes");
+ goto error;
+ }
+
+ /* And the ancestorid index */
+ if ((ret = ldbm_ancestorid_create_index(be)) != 0) {
+ import_log_notice(job, "Failed to create ancestorid index");
+ goto error;
+ }
+
+ import_log_notice(job, "Flushing caches...");
+ if (0 != (ret = dblayer_flush(job->inst->inst_li)) ) {
+ import_log_notice(job, "Failed to flush database");
+ goto error;
+ }
+
+ /* New way to exit the routine: check the return code.
+ * If it's non-zero, delete the database files.
+ * Otherwise don't, but always close the database layer properly.
+ * Then return. This ensures that we can't make a half-good/half-bad
+ * Database. */
+
+error:
+ /* If we fail, the database is now in a mess, so we delete it */
+ import_log_notice(job, "Closing files...");
+ cache_clear(&job->inst->inst_cache);
+ if (0 != ret) {
+ dblayer_delete_instance_dir(be);
+ dblayer_instance_close(job->inst->inst_be);
+ } else {
+ if (0 != (ret = dblayer_instance_close(job->inst->inst_be)) ) {
+ import_log_notice(job, "Failed to close database");
+ }
+ }
+ if (!(job->flags & FLAG_ONLINE))
+ dblayer_close(job->inst->inst_li, DBLAYER_IMPORT_MODE);
+
+ time(&end);
+ if (verbose && (0 == ret)) {
+ int seconds_to_import = end - beginning;
+ size_t entries_processed = job->lead_ID - (job->starting_ID - 1);
+ double entries_per_second = (double) entries_processed /
+ (double) seconds_to_import;
+
+ if (job->not_here_skipped)
+ {
+ if (job->skipped)
+ import_log_notice(job, "Import complete. Processed %lu entries "
+ "(%d bad entries were skipped, "
+ "%d entries were skipped because they don't "
+ "belong to this database) in %d seconds. "
+ "(%.2f entries/sec)", entries_processed,
+ job->skipped, job->not_here_skipped,
+ seconds_to_import, entries_per_second);
+ else
+ import_log_notice(job, "Import complete. Processed %lu entries "
+ "(%d entries were skipped because they don't "
+ "belong to this database) "
+ "in %d seconds. (%.2f entries/sec)",
+ entries_processed, job->not_here_skipped,
+ seconds_to_import, entries_per_second);
+ }
+ else
+ {
+ if (job->skipped)
+ import_log_notice(job, "Import complete. Processed %lu entries "
+ "(%d were skipped) in %d seconds. "
+ "(%.2f entries/sec)", entries_processed,
+ job->skipped, seconds_to_import,
+ entries_per_second);
+ else
+ import_log_notice(job, "Import complete. Processed %lu entries "
+ "in %d seconds. (%.2f entries/sec)",
+ entries_processed, seconds_to_import,
+ entries_per_second);
+ }
+ }
+
+ if (0 != ret) {
+ import_log_notice(job, "Import failed.");
+ if (job->task != NULL) {
+ job->task->task_state = SLAPI_TASK_FINISHED;
+ job->task->task_exitcode = ret;
+ slapi_task_status_changed(job->task);
+ }
+ } else {
+ if (job->task)
+ job->task->task_refcount--;
+
+ import_all_done(job, ret);
+ }
+
+ /* This instance isn't busy anymore */
+ instance_set_not_busy(job->inst);
+
+ import_free_job(job);
+ if (producer)
+ FREE(producer);
+
+
+ return(ret);
+}
+
+/*
+ * to be called by online import using PR_CreateThread()
+ * offline import directly calls import_main_offline()
+ *
+ */
+void import_main(void *arg)
+{
+ import_main_offline(arg);
+}
+
+int ldbm_back_ldif2ldbm_deluxe(Slapi_PBlock *pb)
+{
+ backend *be = NULL;
+ int noattrindexes = 0;
+ ImportJob *job = NULL;
+ char **name_array = NULL;
+ int total_files, i;
+ PRThread *thread = NULL;
+
+ job = CALLOC(ImportJob);
+ if (job == NULL) {
+ LDAPDebug(LDAP_DEBUG_ANY, "not enough memory to do import job\n",
+ 0, 0, 0);
+ return -1;
+ }
+
+ slapi_pblock_get( pb, SLAPI_BACKEND, &be);
+ PR_ASSERT(NULL != be);
+ job->inst = (ldbm_instance *)be->be_instance_info;
+ slapi_pblock_get( pb, SLAPI_LDIF2DB_NOATTRINDEXES, &noattrindexes );
+ slapi_pblock_get( pb, SLAPI_LDIF2DB_FILE, &name_array );
+
+ /* the removedupvals field is blatantly overloaded here to mean
+ * the chunk size too. (chunk size = number of entries that should
+ * be imported before starting a new pass. usually for debugging.)
+ */
+ slapi_pblock_get(pb, SLAPI_LDIF2DB_REMOVEDUPVALS, &job->merge_chunk_size);
+ if (job->merge_chunk_size == 1)
+ job->merge_chunk_size = 0;
+ /* get list of specifically included and/or excluded subtrees from
+ * the front-end */
+ ldbm_back_fetch_incl_excl(pb, &job->include_subtrees,
+ &job->exclude_subtrees);
+ /* get cn=tasks info, if any */
+ slapi_pblock_get(pb, SLAPI_BACKEND_TASK, &job->task);
+ slapi_pblock_get(pb, SLAPI_LDIF2DB_ENCRYPT, &job->encrypt);
+ /* get uniqueid info */
+ slapi_pblock_get(pb, SLAPI_LDIF2DB_GENERATE_UNIQUEID, &job->uuid_gen_type);
+ if (job->uuid_gen_type == SLAPI_UNIQUEID_GENERATE_NAME_BASED) {
+ char *namespaceid;
+
+ slapi_pblock_get(pb, SLAPI_LDIF2DB_NAMESPACEID, &namespaceid);
+ job->uuid_namespace = slapi_ch_strdup(namespaceid);
+ }
+
+ job->flags = FLAG_USE_FILES;
+#if defined(UPGRADEDB)
+ if (NULL == name_array) /* no ldif file is given -> reindexing */
+ job->flags |= FLAG_REINDEXING;
+#endif
+ if (!noattrindexes)
+ job->flags |= FLAG_INDEX_ATTRS;
+ for (i = 0; name_array && name_array[i] != NULL; i++)
+ charray_add(&job->input_filenames, slapi_ch_strdup(name_array[i]));
+ job->starting_ID = 1;
+ job->first_ID = 1;
+ job->mothers = CALLOC(import_subcount_stuff);
+
+ /* how much space should we allocate to index buffering? */
+ job->job_index_buffer_size = import_get_index_buffer_size();
+ if (job->job_index_buffer_size == 0) {
+ /* 10% of the allocated cache size + one meg */
+ PR_Lock(job->inst->inst_li->li_config_mutex);
+ job->job_index_buffer_size = (job->inst->inst_li->li_import_cachesize/10) +
+ (1024*1024);
+ PR_Unlock(job->inst->inst_li->li_config_mutex);
+ }
+ import_subcount_stuff_init(job->mothers);
+
+ if (job->task != NULL) {
+ /* count files, use that to track "progress" in cn=tasks */
+ total_files = 0;
+ while (name_array && name_array[total_files] != NULL)
+ total_files++;
+ /* add 1 to account for post-import cleanup (which can take a
+ * significant amount of time)
+ */
+ if (0 == total_files) /* reindexing */
+ job->task->task_work = 2;
+ else
+ job->task->task_work = total_files + 1;
+ job->task->task_progress = 0;
+ job->task->task_state = SLAPI_TASK_RUNNING;
+ job->task->task_private = job;
+ job->task->destructor = import_task_destroy;
+ job->task->cancel = import_task_abort;
+ job->flags |= FLAG_ONLINE;
+
+ /* create thread for import_main, so we can return */
+ thread = PR_CreateThread(PR_USER_THREAD, import_main, (void *)job,
+ PR_PRIORITY_NORMAL, PR_GLOBAL_THREAD,
+ PR_UNJOINABLE_THREAD,
+ SLAPD_DEFAULT_THREAD_STACKSIZE);
+ if (thread == NULL) {
+ PRErrorCode prerr = PR_GetError();
+ LDAPDebug(LDAP_DEBUG_ANY, "unable to spawn import thread, "
+ SLAPI_COMPONENT_NAME_NSPR " error %d (%s)\n",
+ prerr, slapd_pr_strerror(prerr), 0);
+ import_free_job(job);
+ FREE(job);
+ return -2;
+ }
+ return 0;
+ }
+
+ /* old style -- do it all synchronously (THIS IS GOING AWAY SOON) */
+ return import_main_offline((void *)job);
+}
diff --git a/ldap/servers/slapd/back-ldbm/import.h b/ldap/servers/slapd/back-ldbm/import.h
new file mode 100644
index 00000000..866ef9f8
--- /dev/null
+++ b/ldap/servers/slapd/back-ldbm/import.h
@@ -0,0 +1,199 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+/*
+ * structures & constants used for the import code
+ */
+
+
+/* Number of lines in the entry above which we switch to
+ using a tree to check for attribute presence in str2entry().
+ */
+#define STR2ENTRY_ATTRIBUTE_PRESENCE_CHECK_THRESHOLD 100
+
+#define IMPORT_ADD_OP_ATTRS_OK 0
+#define IMPORT_ADD_OP_ATTRS_NO_PARENT 1
+
+#define IMPORT_COMPLETE_PASS 1
+#define IMPORT_INCOMPLETE_PASS 2
+
+/* Constants for index buffering */
+#define IMPORT_MAX_INDEX_BUFFER_SIZE 100
+#define IMPORT_MIN_INDEX_BUFFER_SIZE 5
+#define IMPORT_INDEX_BUFFER_SIZE_CONSTANT (20*20*20*sizeof(ID))
+
+static const int import_sleep_time = 200; /* in millisecs */
+
+extern char *numsubordinates;
+extern char *hassubordinates;
+
+typedef struct _import_worker_info ImportWorkerInfo;
+typedef struct _import_index_info IndexInfo;
+
+
+/* structure which describes an indexing job */
+struct _import_index_info
+{
+ char *name;
+ struct attrinfo *ai;
+ IndexInfo *next;
+};
+
+/* item on the entry FIFO */
+typedef struct {
+ struct backentry *entry;
+ char *filename; /* or NULL */
+ int line; /* filename/line are used to report errors */
+ int bad; /* foreman did not like the entry */
+ size_t esize; /* entry size */
+} FifoItem;
+
+typedef struct {
+ FifoItem *item;
+ size_t size; /* Queue size in entries (computed in import_fifo_init). */
+ size_t bsize; /* Queue limitation in max bytes */
+ size_t c_bsize; /* Current queue size in bytes */
+} Fifo;
+
+/* notes on the import gang:
+ * 1. producer: reads the file(s), performs str2entry() and assigns IDs.
+ * job->lead_ID is the last entry in the FIFO it's decoded. as it
+ * circles the FIFO, it pauses whenever it runs into an entry with a
+ * non-zero refcount, and waits for the worker threads to finish.
+ * 2. foreman: reads the FIFO (up to lead_ID), adding operational attrs,
+ * and creating the entrydn & id2entry indexes. job->ready_ID is the
+ * last entry in the FIFO it's finished with. (workers can't browse
+ * the entries it's working on because it's effectively modifying the
+ * entry.)
+ * 3. workers (one for each other index): read the FIFO (up to ready_ID),
+ * creating the index for a particular attribute.
+ */
+
+/* Structure holding stuff about the whole import job */
+#define IMPORT_JOB_PROG_HISTORY_SIZE 3
+typedef struct {
+ ldbm_instance *inst; /* db instance we're importing to */
+ Slapi_Task *task; /* cn=tasks entry ptr */
+ int flags; /* (see below) */
+ char **input_filenames; /* NULL-terminated list of charz pointers */
+ IndexInfo *index_list; /* A list of indexing jobs to do */
+ ImportWorkerInfo *worker_list; /* A list of threads to work on the
+ * indexes */
+ size_t number_indexers; /* count of the indexer threads (not including
+ * the primary) */
+ ID starting_ID; /* Import starts work at this ID */
+ ID first_ID; /* Import pass starts at this ID */
+ ID lead_ID; /* Highest ID available in the cache */
+ ID ready_ID; /* Highest ID the foreman is done with */
+ ID trailing_ID; /* Lowest ID still available in the cache */
+ int current_pass; /* un-merged pass number in a multi-pass import */
+ int total_pass; /* total pass number in a multi-pass import */
+ int skipped; /* # entries skipped because they were bad */
+ int not_here_skipped; /* # entries skipped because they belong
+ * to another backend */
+ size_t merge_chunk_size; /* Allows us to manually override the magic
+ * voodoo logic for deciding when to begin
+ * another pass */
+ int uuid_gen_type; /* kind of uuid to generate */
+ char *uuid_namespace; /* namespace for name-generated uuid */
+ import_subcount_stuff *mothers;
+ double average_progress_rate;
+ double recent_progress_rate;
+ double cache_hit_ratio;
+ time_t start_time;
+ ID progress_history[IMPORT_JOB_PROG_HISTORY_SIZE];
+ time_t progress_times[IMPORT_JOB_PROG_HISTORY_SIZE];
+ size_t job_index_buffer_size; /* Suggested size of index buffering
+ * for all indexes */
+ size_t job_index_buffer_suggestion; /* Suggested size of index buffering
+ * for one index */
+ char **include_subtrees; /* list of subtrees to import */
+ char **exclude_subtrees; /* list of subtrees to NOT import */
+ Fifo fifo; /* entry fifo for indexing */
+ char *task_status; /* transient state info for the end-user */
+ PRLock *wire_lock; /* lock for serializing wire imports */
+ PRCondVar *wire_cv; /* ... and ordering the startup */
+ PRThread *main_thread; /* for FRI: import_main() thread id */
+ int encrypt;
+} ImportJob;
+
+#define FLAG_INDEX_ATTRS 0x01 /* should we index the attributes? */
+#define FLAG_USE_FILES 0x02 /* import from files */
+#define FLAG_PRODUCER_DONE 0x04 /* frontend is done sending entries
+ * for replica initialization */
+#define FLAG_ABORT 0x08 /* import has been aborted */
+#define FLAG_ONLINE 0x10 /* bring backend online when done */
+#if defined(UPGRADEDB)
+#define FLAG_REINDEXING 0x20 /* read from id2entry and do indexing */
+#endif
+
+
+/* Structure holding stuff about a worker thread and what it's up to */
+struct _import_worker_info {
+ int work_type; /* What sort of work is this ? */
+ int command; /* Used to control the thread */
+ int state; /* Thread indicates its state here */
+ IndexInfo *index_info; /* info on what we're asked to do */
+ ID last_ID_processed;
+ ID previous_ID_counted; /* Used by the monitor to calculate progress
+ * rate */
+ double rate; /* Number of IDs processed per second */
+ ID first_ID; /* Tell the thread to start at this ID */
+ ImportJob *job;
+ ImportWorkerInfo *next;
+ size_t index_buffer_size; /* Size of index buffering for this index */
+};
+
+/* Values for work_type */
+#define WORKER 1
+#define FOREMAN 2
+#define PRODUCER 3
+
+/* Values for command */
+#define RUN 1
+#define PAUSE 2
+#define ABORT 3
+#define STOP 4
+
+/* Values for state */
+#define WAITING 1
+#define RUNNING 2
+#define FINISHED 3
+#define ABORTED 4
+
+/* this is just a convenience, because the slapi_ch_* calls are annoying */
+#define CALLOC(name) (name *)slapi_ch_calloc(1, sizeof(name))
+#define FREE(x) slapi_ch_free((void **)&(x))
+
+
+/* import.c */
+FifoItem *import_fifo_fetch(ImportJob *job, ID id, int worker);
+void import_free_job(ImportJob *job);
+void import_log_notice(ImportJob *job, char *format, ...);
+void import_abort_all(ImportJob *job, int wait_for_them);
+int import_entry_belongs_here(Slapi_Entry *e, backend *be);
+int import_make_merge_filenames(char *directory, char *indexname, int pass,
+ char **oldname, char **newname);
+void import_main(void *arg);
+int import_main_offline(void *arg);
+int ldbm_back_ldif2ldbm_deluxe(Slapi_PBlock *pb);
+
+/* import-merge.c */
+int import_mega_merge(ImportJob *job);
+
+/* ldif2ldbm.c */
+void reset_progress( void );
+void report_progress( int count, int done );
+int add_op_attrs(Slapi_PBlock *pb, struct ldbminfo *li, struct backentry *ep,
+ int *status);
+
+/* import-threads.c */
+void import_producer(void *param);
+#if defined(UPGRADEDB)
+void index_producer(void *param);
+#endif
+void import_foreman(void *param);
+void import_worker(void *param);
+static void import_wait_for_space_in_fifo(ImportJob *job, size_t new_esize);
diff --git a/ldap/servers/slapd/back-ldbm/index.c b/ldap/servers/slapd/back-ldbm/index.c
new file mode 100644
index 00000000..c742c5fe
--- /dev/null
+++ b/ldap/servers/slapd/back-ldbm/index.c
@@ -0,0 +1,1852 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+
+/* index.c - routines for dealing with attribute indexes */
+
+#include "back-ldbm.h"
+#if ( defined ( OSF1 ))
+#undef BUFSIZ
+#define BUFSIZ 1024
+#endif
+
+static const char *errmsg = "database index operation failed";
+
+static int is_indexed (const char* indextype, int indexmask, char** index_rules);
+static char* index2prefix (const char* indextype);
+static void free_prefix (char*);
+static Slapi_Value **
+valuearray_minus_valuearray(
+ void *plugin,
+ Slapi_Value **a,
+ Slapi_Value **b
+);
+static int index_addordel_values( backend *be, const char *type, struct berval **vals, struct berval **evals, ID id, int flags, back_txn *txn );
+static int index_addordel_values_ext( backend *be, const char *type, struct berval **vals, struct berval **evals, ID id, int flags, back_txn *txn,int *idl_disposition, void *buffer_handle );
+
+const char* indextype_PRESENCE = "pres";
+const char* indextype_EQUALITY = "eq";
+const char* indextype_APPROX = "approx";
+const char* indextype_SUB = "sub";
+
+static char prefix_PRESENCE[2] = {PRES_PREFIX, 0};
+static char prefix_EQUALITY[2] = {EQ_PREFIX, 0};
+static char prefix_APPROX [2] = {APPROX_PREFIX, 0};
+static char prefix_SUB [2] = {SUB_PREFIX, 0};
+
+/* Yes, prefix_PRESENCE and prefix_SUB are identical.
+ * It works because SUB is always followed by a key value,
+ * but PRESENCE never is. Too slick by half.
+ */
+
+
+/* Structures for index key buffering magic used by import code */
+struct _index_buffer_bin {
+ DBT key;
+ IDList *value;
+};
+typedef struct _index_buffer_bin index_buffer_bin;
+
+struct _index_buffer_handle {
+ int flags;
+ size_t buffer_size;
+ size_t idl_size;
+ size_t max_key_length;
+ index_buffer_bin *bins;
+ unsigned char high_key_byte_range;
+ unsigned char low_key_byte_range;
+ unsigned char special_byte_a;
+ unsigned char special_byte_b;
+ size_t byte_range;
+ /* Statistics */
+ int inserts;
+ int keys;
+};
+typedef struct _index_buffer_handle index_buffer_handle;
+#define INDEX_BUFFER_FLAG_SERIALIZE 1
+#define INDEX_BUFFER_FLAG_STATS 2
+
+/* Index buffering functions */
+
+static int
+index_buffer_init_internal(size_t idl_size,
+ unsigned char high_key_byte_range, unsigned char low_key_byte_range,
+ size_t max_key_length,unsigned char special_byte_a, unsigned char special_byte_b,
+ int flags,void **h)
+{
+ size_t bin_count = 0;
+ /* Allocate the handle */
+ index_buffer_bin *bins = NULL;
+ size_t i = 0;
+ size_t byte_range = 0;
+
+ index_buffer_handle *handle = (index_buffer_handle *) slapi_ch_calloc(1,sizeof(index_buffer_handle));
+ if (NULL == handle) {
+ return -1;
+ }
+ handle->idl_size = idl_size;
+ handle->flags = flags;
+ handle->high_key_byte_range = high_key_byte_range;
+ handle->low_key_byte_range = low_key_byte_range;
+ handle->special_byte_a = special_byte_a;
+ handle->special_byte_b = special_byte_b;
+ handle->max_key_length = max_key_length;
+ byte_range = (high_key_byte_range - low_key_byte_range) + 3 + 10;
+ handle->byte_range = byte_range;
+ /* Allocate the bins */
+ bin_count = 1;
+ for (i = 0 ; i < max_key_length - 2; i++) {
+ bin_count *= byte_range;
+ }
+ handle->buffer_size = bin_count;
+ bins = (index_buffer_bin *)slapi_ch_calloc(bin_count, sizeof(index_buffer_bin));
+ if (NULL == bins) {
+ return -1;
+ }
+ handle->bins = bins;
+ *h = (void*) handle;
+ return 0;
+}
+
+int index_buffer_init(size_t size,int flags,void **h)
+{
+ return index_buffer_init_internal(size,'z','a',5,'^','$',flags,h);
+}
+
+static int
+index_put_idl(index_buffer_bin *bin,backend *be, DB_TXN *txn,struct attrinfo *a)
+{
+ int ret = 0;
+ DB *db = NULL;
+ int need_to_freed_new_idl = 0;
+ IDList *old_idl = NULL;
+ IDList *new_idl = NULL;
+
+ if ( (ret = dblayer_get_index_file( be, a, &db, DBOPEN_CREATE )) != 0 ) {
+ return ret;
+ }
+ if (bin->key.data && bin->value) {
+ /* Need to read the IDL at the key, if present, and form the union with what we have */
+ ret = NEW_IDL_NOOP; /* this flag is for new idl only;
+ * but this func is called only from index_buffer,
+ * which is enabled only for old idl.
+ */
+ old_idl = idl_fetch(be,db,&bin->key,txn,a,&ret);
+ if ( (0 != ret) && (DB_NOTFOUND != ret)) {
+ goto error;
+ }
+ if ( (old_idl != NULL) && !ALLIDS(old_idl)) {
+ /* We need to merge in our block with what was there */
+ new_idl = idl_union(be,old_idl,bin->value);
+ need_to_freed_new_idl = 1;
+ } else {
+ /* Nothing there previously, we store just what we have */
+ new_idl = bin->value;
+ }
+ /* Then write back the result, but only if the existing idl wasn't ALLIDS */
+ if (!old_idl || (old_idl && !ALLIDS(old_idl))) {
+ ret = idl_store_block(be,db,&bin->key,new_idl,txn,a);
+ }
+ if (0 != ret) {
+ goto error;
+ }
+ slapi_ch_free((void**)&bin->key.data );
+ idl_free(bin->value);
+ /* If we're already at allids, store an allids block to prevent needless accumulation of blocks */
+ if (old_idl && ALLIDS(old_idl)) {
+ bin->value = idl_allids(be);
+ } else {
+ bin->value = NULL;
+ }
+ }
+error:
+ if (old_idl) {
+ idl_free(old_idl);
+ }
+ if (new_idl && need_to_freed_new_idl) {
+ idl_free(new_idl);
+ }
+ dblayer_release_index_file( be, a, db );
+ return ret;
+}
+
+/* The caller MUST check for DB_RUNRECOVERY being returned */
+
+int
+index_buffer_flush(void *h,backend *be, DB_TXN *txn,struct attrinfo *a)
+{
+ index_buffer_handle *handle = (index_buffer_handle *) h;
+ index_buffer_bin *bin = NULL;
+ int ret = 0;
+ size_t i = 0;
+ DB *db = NULL;
+
+ PR_ASSERT(h);
+
+ /* Note to the wary: here we do NOT create the index file up front */
+ /* This is becuase there may be no buffers to flush, and the goal is to
+ * never create the index file (merging gets confused by this, among other things */
+
+ /* Walk along the bins, writing them to the database */
+ for (i = 0; i < handle->buffer_size; i++) {
+ bin = &(handle->bins[i]);
+ if (bin->key.data && bin->value) {
+ if (NULL == db) {
+ if ( (ret = dblayer_get_index_file( be, a, &db, DBOPEN_CREATE )) != 0 ) {
+ return ret;
+ }
+ }
+ ret = index_put_idl(bin,be,txn,a);
+ if (0 != ret) {
+ goto error;
+ }
+ }
+ }
+error:
+ if (NULL != db) {
+ dblayer_release_index_file( be, a, db );
+ }
+ return ret;
+}
+
+int
+index_buffer_terminate(void *h)
+{
+ index_buffer_handle *handle = (index_buffer_handle *) h;
+ index_buffer_bin *bin = NULL;
+ size_t i = 0;
+
+ PR_ASSERT(h);
+ /* Free all the buffers */
+ /* First walk down the bins, freeing the IDLs and the bins they're in */
+ for (i = 0; i < handle->buffer_size; i++) {
+ bin = &(handle->bins[i]);
+ if (bin->value) {
+ idl_free(bin->value);
+ bin->value = NULL;
+ }
+ if (bin->key.data) {
+ free(bin->key.data);
+ }
+ }
+ free(handle->bins);
+ /* Now free the handle */
+ free(handle);
+ return 0;
+}
+
+/* This function returns -1 or -2 for local errors, and DB_ errors as well. */
+
+static int
+index_buffer_insert(void *h, DBT *key, ID id,backend *be, DB_TXN *txn,struct attrinfo *a)
+{
+ index_buffer_handle *handle = (index_buffer_handle *) h;
+ index_buffer_bin *bin = NULL;
+ size_t index = 0;
+ int idl_ret = 0;
+ unsigned char x = 0;
+ unsigned int i = 0;
+ int ret = 0;
+
+ PR_ASSERT(h);
+
+ /* Check key length for validity */
+ if (key->size > handle->max_key_length) {
+ return -2;
+ }
+ /* discard the first character, as long as its the substring prefix */
+ if ((unsigned char)((char*)key->data)[0] != SUB_PREFIX) {
+ return -2;
+ }
+ /* Compute the bin index from the key */
+ /* Walk along the key data, byte by byte */
+ for (i = 1; i < (key->size - 1); i++) {
+ /* foreach byte, normalize to the range we accept */
+ x = (unsigned char) ((char*)key->data)[i];
+ if ( (x == handle->special_byte_a) || (x == handle->special_byte_b) ) {
+ if (x == handle->special_byte_a) {
+ x = handle->high_key_byte_range + 1;
+ }
+ if (x == handle->special_byte_b) {
+ x = handle->high_key_byte_range + 2;
+ }
+ } else {
+ if ( x >= '0' && x <= '9' ) {
+ x = (x - '0') + handle->high_key_byte_range + 3;
+ } else {
+ if (x > handle->high_key_byte_range) {
+ return -2; /* Out of range */
+ }
+ if (x < handle->low_key_byte_range) {
+ return -2; /* Out of range */
+ }
+ }
+ }
+ x = x - handle->low_key_byte_range;
+ index *= handle->byte_range;
+ index += x;
+ }
+ /* Check that the last byte in the key is zero */
+ if (0 != (unsigned char)((char*)key->data)[i]) {
+ return -2;
+ }
+ PR_ASSERT(index < handle->buffer_size);
+ /* Get the bin */
+ bin = &(handle->bins[index]);
+ /* Is the key already there ? */
+retry:
+ if (!(bin->key).data) {
+ (bin->key).size = key->size;
+ (bin->key).data = malloc(key->size);
+ if (NULL == bin->key.data) {
+ return -1;
+ }
+ memcpy(bin->key.data,key->data,key->size);
+ /* Make the IDL */
+ bin->value = idl_alloc(handle->idl_size);
+ if (!bin->value) {
+ return -1;
+ }
+ }
+ idl_ret = idl_append(bin->value, id);
+ if (0 != idl_ret) {
+ if (1 == idl_ret) {
+ /* ID already present */
+ } else {
+ /* If we get to here, it means that we've overflowed our IDL */
+ /* So, we need to write it out to the DB and zero out the pointers */
+ ret = index_put_idl(bin,be,txn,a);
+ /* Now we need to append the ID we have at hand */
+ if (0 == ret) {
+ goto retry;
+ }
+ }
+ }
+ return ret;
+}
+
+/*
+ * Add or Delete an entry from the attribute indexes.
+ * 'flags' is either BE_INDEX_ADD or BE_INDEX_DEL
+ */
+int
+index_addordel_entry(
+ backend *be,
+ struct backentry *e,
+ int flags,
+ back_txn *txn
+)
+{
+ char *type;
+ Slapi_Value **svals;
+ int rc, result;
+ Slapi_Attr *attr;
+
+ LDAPDebug( LDAP_DEBUG_TRACE, "=> index_%s_entry( \"%s\", %lu )\n",
+ (flags & BE_INDEX_ADD) ? "add" : "del",
+ backentry_get_ndn(e), (u_long)e->ep_id );
+
+ /* if we are adding a tombstone entry (see ldbm_add.c) */
+ if ((flags & BE_INDEX_TOMBSTONE) && (flags & BE_INDEX_ADD))
+ {
+ Slapi_DN parent;
+ Slapi_DN *sdn = slapi_entry_get_sdn(e->ep_entry);
+ slapi_sdn_init(&parent);
+ slapi_sdn_get_parent(sdn, &parent);
+ /*
+ * Just index the "nstombstone" attribute value from the objectclass
+ * attribute, and the nsuniqueid attribute value, and the entrydn value of the deleted entry.
+ */
+ result = index_addordel_string(be, SLAPI_ATTR_OBJECTCLASS, SLAPI_ATTR_VALUE_TOMBSTONE, e->ep_id, flags, txn);
+ if ( result != 0 ) {
+ ldbm_nasty(errmsg, 1010, result);
+ return( result );
+ }
+ result = index_addordel_string(be, SLAPI_ATTR_UNIQUEID, slapi_entry_get_uniqueid(e->ep_entry), e->ep_id, flags, txn);
+ if ( result != 0 ) {
+ ldbm_nasty(errmsg, 1020, result);
+ return( result );
+ }
+ result = index_addordel_string(be, SLAPI_ATTR_NSCP_ENTRYDN, slapi_sdn_get_ndn(&parent), e->ep_id, flags, txn);
+ if ( result != 0 ) {
+ ldbm_nasty(errmsg, 1020, result);
+ return( result );
+ }
+ slapi_sdn_done(&parent);
+ }
+ else
+ {
+ /* add each attribute to the indexes */
+ rc = 0, result = 0;
+ for ( rc = slapi_entry_first_attr( e->ep_entry, &attr ); rc == 0;
+ rc = slapi_entry_next_attr( e->ep_entry, attr, &attr ) ) {
+ slapi_attr_get_type( attr, &type );
+ svals = attr_get_present_values(attr);
+ result = index_addordel_values_sv( be, type, svals, NULL, e->ep_id,
+ flags, txn );
+ if ( result != 0 ) {
+ ldbm_nasty(errmsg, 1030, result);
+ return( result );
+ }
+ }
+
+ /* update ancestorid index . . . */
+ /* . . . only if we are not deleting a tombstone entry - tombstone entries are not in the ancestor id index - see bug 603279 */
+ if (!((flags & BE_INDEX_TOMBSTONE) && (flags & BE_INDEX_DEL))) {
+ result = ldbm_ancestorid_index_entry(be, e, flags, txn);
+ if ( result != 0 ) {
+ return( result );
+ }
+ }
+ }
+
+ LDAPDebug( LDAP_DEBUG_TRACE, "<= index_%s_entry%s %d\n",
+ (flags & BE_INDEX_ADD) ? "add" : "del",
+ (flags & BE_INDEX_TOMBSTONE) ? " (tombstone)" : "", result );
+ return( result );
+}
+
+/*
+ * Add ID to attribute indexes for which Add/Replace/Delete modifications exist
+ * [olde is the OLD entry, before modifications]
+ * [newe is the NEW entry, after modifications]
+ * the old entry is used for REPLACE; the new for DELETE */
+int
+index_add_mods(
+ backend *be,
+ const LDAPMod **mods,
+ struct backentry *olde,
+ struct backentry *newe,
+ back_txn *txn
+)
+{
+ int rc = 0;
+ int i;
+ Slapi_Attr *attr;
+ ID id = olde->ep_id;
+ Slapi_Value **svals = NULL;
+
+ for ( i = 0; mods[i] != NULL; i++ ) {
+ switch ( mods[i]->mod_op & ~LDAP_MOD_BVALUES ) {
+ case LDAP_MOD_REPLACE:
+ /* We need to first remove the old values from the
+ * index. */
+ if ( slapi_entry_attr_find( olde->ep_entry, mods[i]->mod_type, &attr ) == 0 &&
+ (svals = attr_get_present_values(attr)) != NULL ) {
+ index_addordel_values_sv( be, mods[i]->mod_type,
+ svals, NULL, id,
+ BE_INDEX_DEL|BE_INDEX_PRESENCE,
+ txn );
+ }
+ case LDAP_MOD_ADD:
+ if ( mods[i]->mod_bvalues == NULL ) {
+ rc = 0;
+ } else {
+ Slapi_Value **mods_valueArray = NULL;
+ valuearray_init_bervalarray(mods[i]->mod_bvalues,
+ &mods_valueArray);
+ rc = index_addordel_values_sv( be,
+ mods[i]->mod_type,
+ mods_valueArray, NULL,
+ id, BE_INDEX_ADD, txn );
+ valuearray_free(&mods_valueArray);
+ }
+ break;
+
+ case LDAP_MOD_DELETE:
+ if ( (mods[i]->mod_bvalues == NULL) ||
+ (mods[i]->mod_bvalues[0] == NULL) ) {
+ rc = 0;
+ /* if no value are specified all the values will
+ * be suppressed -> remove the presence index
+ */
+ if ( slapi_entry_attr_find( olde->ep_entry, mods[i]->mod_type, &attr ) == 0 &&
+ (svals = attr_get_present_values(attr)) != NULL ) {
+ index_addordel_values_sv( be, mods[i]->mod_type,
+ svals, NULL, id, BE_INDEX_DEL|BE_INDEX_PRESENCE, txn);
+ }
+ } else {
+ /* determine if the presence key should be
+ * removed (are we removing the last value
+ * for this attribute?)
+ */
+ int flags = BE_INDEX_DEL;
+ Slapi_Value ** svals = NULL;
+ Slapi_Value **mods_valueArray = NULL;
+
+ valuearray_init_bervalarray(mods[i]->mod_bvalues,
+ &mods_valueArray);
+
+ if (slapi_entry_attr_find(newe->ep_entry,
+ mods[i]->mod_type, &attr) == 0) {
+ svals = attr_get_present_values(attr);
+ }
+
+ if (svals == NULL || svals[0] == NULL) {
+ flags |= BE_INDEX_PRESENCE;
+ }
+
+ rc = index_addordel_values_sv( be, mods[i]->mod_type,
+ mods_valueArray,
+ svals, id, flags, txn );
+ valuearray_free(&mods_valueArray);
+ }
+ rc = 0;
+ break;
+ }
+
+ if ( rc != 0 ) {
+ ldbm_nasty(errmsg, 1040, rc);
+ return( rc );
+ }
+ }
+
+ return( 0 );
+}
+
+
+/*
+ * Convert a 'struct berval' into a displayable ASCII string
+ */
+
+#define SPECIAL(c) (c < 32 || c > 126 || c == '\\' || c == '"')
+
+const char*
+encode (const struct berval* data, char buf[BUFSIZ])
+{
+ char* s;
+ char* last;
+ if (data == NULL || data->bv_len == 0) return "";
+ last = data->bv_val + data->bv_len - 1;
+ for (s = data->bv_val; s < last; ++s) {
+ if ( SPECIAL (*s)) {
+ char* first = data->bv_val;
+ char* bufNext = buf;
+ size_t bufSpace = BUFSIZ - 4;
+ while (1) {
+/* printf ("%lu bytes ASCII\n", (unsigned long)(s - first)); */
+ if (bufSpace < (size_t)(s - first)) s = first + bufSpace - 1;
+ if (s != first) {
+ memcpy (bufNext, first, s - first);
+ bufNext += (s - first);
+ bufSpace -= (s - first);
+ }
+ do {
+ *bufNext++ = '\\'; --bufSpace;
+ if (bufSpace < 2) {
+ memcpy (bufNext, "..", 2);
+ bufNext += 2;
+ goto bail;
+ }
+ if (*s == '\\' || *s == '"') {
+ *bufNext++ = *s; --bufSpace;
+ } else {
+ sprintf (bufNext, "%02x", (unsigned)*(unsigned char*)s);
+ bufNext += 2; bufSpace -= 2;
+ }
+ } while (++s <= last && SPECIAL (*s));
+ if (s > last) break;
+ first = s;
+ while ( ! SPECIAL (*s) && s <= last) ++s;
+ }
+ bail:
+ *bufNext = '\0';
+/* printf ("%lu chars in buffer\n", (unsigned long)(bufNext - buf)); */
+ return buf;
+ }
+ }
+/* printf ("%lu bytes, all ASCII\n", (unsigned long)(s - data->bv_val)); */
+ return data->bv_val;
+}
+
+static const char*
+encoded (DBT* d, char buf [BUFSIZ])
+{
+ struct berval data;
+ data.bv_len = d->dsize;
+ data.bv_val = d->dptr;
+ return encode (&data, buf);
+}
+
+IDList *
+index_read(
+ backend *be,
+ char *type,
+ const char *indextype,
+ const struct berval *val,
+ back_txn *txn,
+ int *err
+)
+{
+ return index_read_ext(be, type, indextype, val, txn, err, NULL);
+}
+
+/*
+ * Extended version of index_read.
+ * The unindexed flag can be used to distinguish between a
+ * return of allids due to the attr not being indexed or
+ * the value really being allids.
+ */
+IDList *
+index_read_ext(
+ backend *be,
+ char *type,
+ const char *indextype,
+ const struct berval *val,
+ back_txn *txn,
+ int *err,
+ int *unindexed
+)
+{
+ DB *db = NULL;
+ DB_TXN *db_txn = NULL;
+ DBT key = {0};
+ IDList *idl;
+ char *prefix;
+ char *tmpbuf = NULL;
+ char buf[BUFSIZ];
+ char typebuf[ SLAPD_TYPICAL_ATTRIBUTE_NAME_MAX_LENGTH ];
+ struct attrinfo *ai = NULL;
+ char *basetmp, *basetype;
+ int retry_count = 0;
+ struct berval *encrypted_val = NULL;
+
+ *err = 0;
+
+ if (unindexed != NULL) *unindexed = 0;
+ prefix = index2prefix( indextype );
+ LDAPDebug( LDAP_DEBUG_TRACE, "=> index_read( \"%s\" %s \"%s\" )\n",
+ type, prefix, encode (val, buf));
+
+ basetype = typebuf;
+ if ( (basetmp = slapi_attr_basetype( type, typebuf, sizeof(typebuf) ))
+ != NULL ) {
+ basetype = basetmp;
+ }
+
+ ainfo_get( be, basetype, &ai );
+ if (ai == NULL) {
+ free_prefix( prefix );
+ slapi_ch_free_string( &basetmp );
+ return NULL;
+ }
+
+ LDAPDebug( LDAP_DEBUG_ARGS, " indextype: \"%s\" indexmask: 0x%x\n",
+ indextype, ai->ai_indexmask, 0 );
+
+ if ( !is_indexed( indextype, ai->ai_indexmask, ai->ai_index_rules ) ) {
+ idl = idl_allids( be );
+ if (unindexed != NULL) *unindexed = 1;
+ LDAPDebug( LDAP_DEBUG_TRACE, "<= index_read %lu candidates "
+ "(allids - not indexed)\n", (u_long)IDL_NIDS(idl), 0, 0 );
+ free_prefix( prefix );
+ slapi_ch_free_string( &basetmp );
+ return( idl );
+ }
+ if ( (*err = dblayer_get_index_file( be, ai, &db, DBOPEN_CREATE )) != 0 ) {
+ LDAPDebug( LDAP_DEBUG_TRACE,
+ "<= index_read NULL (index file open for attr %s)\n",
+ basetype, 0, 0 );
+ free_prefix (prefix);
+ slapi_ch_free_string( &basetmp );
+ return( NULL );
+ }
+ slapi_ch_free_string( &basetmp );
+
+ if ( val != NULL ) {
+ size_t plen, vlen;
+ char *realbuf;
+ int ret = 0;
+
+ /* If necessary, encrypt this index key */
+ ret = attrcrypt_encrypt_index_key(be, ai, val, &encrypted_val);
+ if (ret) {
+ LDAPDebug( LDAP_DEBUG_ANY,
+ "index_read failed to encrypt index key for %s\n",
+ basetype, 0, 0 );
+ }
+ if (encrypted_val) {
+ val = encrypted_val;
+ }
+ plen = strlen( prefix );
+ vlen = val->bv_len;
+ realbuf = (plen + vlen < sizeof(buf)) ?
+ buf : (tmpbuf = slapi_ch_malloc( plen + vlen + 1 ));
+ memcpy( realbuf, prefix, plen );
+ memcpy( realbuf+plen, val->bv_val, vlen );
+ realbuf[plen+vlen] = '\0';
+ key.data = realbuf;
+ key.size = key.ulen = plen + vlen + 1;
+ key.flags = DB_DBT_USERMEM;
+ } else {
+ key.data = prefix;
+ key.size = key.ulen = strlen( prefix ) + 1; /* include 0 terminator */
+ key.flags = DB_DBT_USERMEM;
+ }
+ if (NULL != txn) {
+ db_txn = txn->back_txn_txn;
+ }
+ for (retry_count = 0; retry_count < IDL_FETCH_RETRY_COUNT; retry_count++) {
+ *err = NEW_IDL_DEFAULT;
+ idl = idl_fetch( be, db, &key, db_txn, ai, err );
+ if(*err == DB_LOCK_DEADLOCK) {
+ ldbm_nasty("index read retrying transaction", 1045, *err);
+ continue;
+ } else {
+ break;
+ }
+ }
+ if(retry_count == IDL_FETCH_RETRY_COUNT) {
+ ldbm_nasty("index_read retry count exceeded",1046,*err);
+ } else if ( *err != 0 && *err != DB_NOTFOUND ) {
+ ldbm_nasty(errmsg, 1050, *err);
+ }
+ slapi_ch_free_string(&tmpbuf);
+
+ dblayer_release_index_file( be, ai, db );
+
+ free_prefix (prefix);
+
+ if (encrypted_val) {
+ ber_bvfree(encrypted_val);
+ }
+
+ LDAPDebug( LDAP_DEBUG_TRACE, "<= index_read %lu candidates\n",
+ (u_long)IDL_NIDS(idl), 0, 0 );
+ return( idl );
+}
+
+static int
+DBTcmp (DBT* L, DBT* R)
+{
+ struct berval Lv;
+ struct berval Rv;
+ Lv.bv_val = L->dptr; Lv.bv_len = L->dsize;
+ Rv.bv_val = R->dptr; Rv.bv_len = R->dsize;
+ return slapi_berval_cmp (&Lv, &Rv);
+}
+
+#define DBT_EQ(L,R) ((L)->dsize == (R)->dsize &&\
+ ! memcmp ((L)->dptr, (R)->dptr, (L)->dsize))
+
+
+#define DBT_FREE_PAYLOAD(d) if ((d).data) {free((d).data);(d).data=NULL;}
+
+/* Steps to the next key without keeping a cursor open */
+/* Returns the new key value in the DBT */
+static int index_range_next_key(DB *db,DBT *key,DB_TXN *db_txn)
+{
+ DBC *cursor = NULL;
+ DBT data = {0};
+ int ret = 0;
+ void *saved_key = key->data;
+
+ /* Make cursor */
+retry:
+ ret = db->cursor(db,db_txn,&cursor, 0);
+ if (0 != ret) {
+ return ret;
+ }
+ /* Seek to the last key */
+ data.flags = DB_DBT_MALLOC;
+ ret = cursor->c_get(cursor,key,&data,DB_SET); /* data allocated here, we don't need it */
+ DBT_FREE_PAYLOAD(data);
+ if (DB_NOTFOUND == ret) {
+ void *old_key_buffer = key->data;
+ /* If this happens, it means that we tried to seek to a key which has just been deleted */
+ /* So, we seek to the nearest one instead */
+ ret = cursor->c_get(cursor,key,&data,DB_SET_RANGE);
+ /* a new key and data are allocated here, need to free them both */
+ if (old_key_buffer != key->data) {
+ DBT_FREE_PAYLOAD(*key);
+ }
+ DBT_FREE_PAYLOAD(data);
+ }
+ if (0 != ret) {
+ if (DB_LOCK_DEADLOCK == ret)
+ {
+ /* Deadlock detected, retry the operation */
+ cursor->c_close(cursor);
+ cursor = NULL;
+ key->data = saved_key;
+ goto retry;
+ } else
+ {
+ goto error;
+ }
+ }
+ /* Seek to the next one
+ * [612498] NODUP is needed for new idl to get the next non-duplicated key
+ * No effect on old idl since there's no dup there (i.e., DB_NEXT == DB_NEXT_NODUP)
+ */
+ ret = cursor->c_get(cursor,key,&data,DB_NEXT_NODUP); /* new key and data are allocated, we only need the key */
+ DBT_FREE_PAYLOAD(data);
+ if (DB_LOCK_DEADLOCK == ret)
+ {
+ /* Deadlock detected, retry the operation */
+ cursor->c_close(cursor);
+ cursor = NULL;
+ key->data = saved_key;
+ goto retry;
+ }
+error:
+ /* Close the cursor */
+ cursor->c_close(cursor);
+ if (saved_key) { /* Need to free the original key passed in */
+ if (saved_key == key->data) {
+ /* Means that we never allocated a new key */
+ ;
+ } else {
+ free(saved_key);
+ }
+ }
+ return ret;
+}
+
+IDList *
+index_range_read(
+ Slapi_PBlock *pb,
+ backend *be,
+ char *type,
+ const char *indextype,
+ int operator,
+ struct berval *val,
+ struct berval *nextval,
+ int range,
+ back_txn *txn,
+ int *err
+)
+{
+ struct ldbminfo *li = (struct ldbminfo *) be->be_database->plg_private;
+ DB *db;
+ DB_TXN *db_txn = NULL;
+ DBC *dbc = NULL;
+ DBT lowerkey = {0};
+ DBT upperkey = {0};
+ DBT cur_key = {0};
+ DBT data = {0} ;
+ IDList *idl= NULL;
+ char *prefix;
+ char *realbuf, *nextrealbuf;
+ size_t reallen, nextreallen;
+ size_t plen;
+ ID i;
+ struct attrinfo *ai = NULL;
+ int lookthrough_limit = -1; /* default no limit */
+ int retry_count = 0;
+ int is_and = 0;
+ int sizelimit = 0;
+
+ *err = 0;
+ plen = strlen( prefix = index2prefix( indextype ));
+ slapi_pblock_get(pb, SLAPI_SEARCH_IS_AND, &is_and);
+ if (!is_and)
+ {
+ slapi_pblock_get(pb, SLAPI_SEARCH_SIZELIMIT, &sizelimit);
+ }
+
+ /*
+ * Determine the lookthrough_limit from the PBlock.
+ * No limit if there is no PBlock supplied or if there is no
+ * search result set and the requestor is root.
+ */
+ if (pb != NULL) {
+ back_search_result_set *sr = NULL;
+
+ slapi_pblock_get( pb, SLAPI_SEARCH_RESULT_SET, &sr );
+ if (sr != NULL) {
+ /* the normal case */
+ lookthrough_limit = sr->sr_lookthroughlimit;
+ } else {
+ int isroot = 0;
+ slapi_pblock_get( pb, SLAPI_REQUESTOR_ISROOT, &isroot );
+ if (!isroot) {
+ lookthrough_limit = li->li_lookthroughlimit;
+ }
+ }
+ }
+
+ LDAPDebug(LDAP_DEBUG_TRACE, "index_range_read lookthrough_limit=%d\n",
+ lookthrough_limit, 0, 0);
+
+ switch( operator ) {
+ case SLAPI_OP_LESS:
+ case SLAPI_OP_LESS_OR_EQUAL:
+ case SLAPI_OP_GREATER_OR_EQUAL:
+ case SLAPI_OP_GREATER:
+ break;
+ default:
+ LDAPDebug( LDAP_DEBUG_ANY,
+ "<= index_range_read(%s,%s) NULL (operator %i)\n",
+ type, prefix, operator );
+ return( NULL );
+ }
+ ainfo_get( be, type, &ai );
+ if (ai == NULL) return NULL;
+ LDAPDebug( LDAP_DEBUG_ARGS, " indextype: \"%s\" indexmask: 0x%x\n",
+ indextype, ai->ai_indexmask, 0 );
+ if ( !is_indexed( indextype, ai->ai_indexmask, ai->ai_index_rules )) {
+ idl = idl_allids( be );
+ LDAPDebug( LDAP_DEBUG_TRACE,
+ "<= index_range_read(%s,%s) %lu candidates (allids)\n",
+ type, prefix, (u_long)IDL_NIDS(idl) );
+ return( idl );
+ }
+ if ( (*err = dblayer_get_index_file( be, ai, &db, DBOPEN_CREATE )) != 0 ) {
+ LDAPDebug( LDAP_DEBUG_ANY,
+ "<= index_range_read(%s,%s) NULL (could not open index file)\n",
+ type, prefix, 0 );
+ return( NULL ); /* why not allids? */
+ }
+ if (NULL != txn) {
+ db_txn = txn->back_txn_txn;
+ }
+ /* get a cursor so we can walk over the table */
+ *err = db->cursor(db,db_txn,&dbc,0);
+ if (0 != *err ) {
+ ldbm_nasty(errmsg, 1060, *err);
+ LDAPDebug( LDAP_DEBUG_ANY,
+ "<= index_range_read(%s,%s) NULL: db->cursor() == %i\n",
+ type, prefix, *err );
+ dblayer_release_index_file( be, ai, db );
+ return( NULL ); /* why not allids? */
+ }
+
+ /* set up the starting and ending keys for a range search */
+ if ( val != NULL ) { /* compute a key from val */
+ const size_t vlen = val->bv_len;
+ reallen = plen + vlen + 1;
+ realbuf = slapi_ch_malloc( reallen );
+ memcpy( realbuf, prefix, plen );
+ memcpy( realbuf+plen, val->bv_val, vlen );
+ realbuf[plen+vlen] = '\0';
+ } else {
+ reallen = plen + 1; /* include 0 terminator */
+ realbuf = slapi_ch_strdup(prefix);
+ }
+ if (range != 1) {
+ char *tmpbuf = NULL;
+ /* this is a search with only one boundary value */
+ switch( operator ) {
+ case SLAPI_OP_LESS:
+ case SLAPI_OP_LESS_OR_EQUAL:
+ lowerkey.dptr = slapi_ch_strdup(prefix);
+ lowerkey.dsize = plen;
+ upperkey.dptr = realbuf;
+ upperkey.dsize = reallen;
+ break;
+ case SLAPI_OP_GREATER_OR_EQUAL:
+ case SLAPI_OP_GREATER:
+ lowerkey.dptr = realbuf;
+ lowerkey.dsize = reallen;
+ /* upperkey = a value slightly greater than prefix */
+ tmpbuf = slapi_ch_malloc (plen + 1);
+ memcpy (tmpbuf, prefix, plen + 1);
+ ++(tmpbuf[plen-1]);
+ upperkey.dptr = tmpbuf;
+ upperkey.dsize = plen;
+ tmpbuf = NULL;
+ /* ... but not greater than the last key in the index */
+ cur_key.flags = DB_DBT_MALLOC;
+ data.flags = DB_DBT_MALLOC;
+ *err = dbc->c_get(dbc,&cur_key,&data,DB_LAST); /* key and data allocated here, need to free them */
+ DBT_FREE_PAYLOAD(data);
+ /* Note that cur_key needs to get freed somewhere below */
+ if (0 != *err) {
+ if (DB_NOTFOUND == *err) {
+ /* There are no keys in the index so we should return no candidates. */
+ *err = 0;
+ idl = NULL;
+ slapi_ch_free( (void**)&realbuf);
+ dbc->c_close(dbc);
+ goto error;
+ } else {
+ ldbm_nasty(errmsg, 1070, *err);
+ LDAPDebug( LDAP_DEBUG_ANY,
+ "index_range_read(%s,%s) seek to end of index file err %i\n",
+ type, prefix, *err );
+ }
+ } else if (DBTcmp (&upperkey, &cur_key) > 0) {
+ tmpbuf = slapi_ch_realloc (tmpbuf, cur_key.dsize);
+ memcpy (tmpbuf, cur_key.dptr, cur_key.dsize);
+ DBT_FREE_PAYLOAD(upperkey);
+ upperkey.dptr = tmpbuf;
+ upperkey.dsize = cur_key.dsize;
+ }
+ break;
+ }
+ } else {
+ /* this is a search with two boundary values (starting and ending) */
+ if ( nextval != NULL ) { /* compute a key from nextval */
+ const size_t vlen = nextval->bv_len;
+ nextreallen = plen + vlen + 1;
+ nextrealbuf = slapi_ch_malloc( plen + vlen + 1 );
+ memcpy( nextrealbuf, prefix, plen );
+ memcpy( nextrealbuf+plen, nextval->bv_val, vlen );
+ nextrealbuf[plen+vlen] = '\0';
+ } else {
+ nextreallen = plen + 1; /* include 0 terminator */
+ nextrealbuf = slapi_ch_strdup(prefix);
+ }
+ /* set up the starting and ending keys for search */
+ switch( operator ) {
+ case SLAPI_OP_LESS:
+ case SLAPI_OP_LESS_OR_EQUAL:
+ lowerkey.dptr = nextrealbuf;
+ lowerkey.dsize = nextreallen;
+ upperkey.dptr = realbuf;
+ upperkey.dsize = reallen;
+ break;
+ case SLAPI_OP_GREATER_OR_EQUAL:
+ case SLAPI_OP_GREATER:
+ lowerkey.dptr = realbuf;
+ lowerkey.dsize = reallen;
+ upperkey.dptr = nextrealbuf;
+ upperkey.dsize = nextreallen;
+ break;
+ }
+ }
+ /* if (LDAP_DEBUG_FILTER) {
+ char encbuf [BUFSIZ];
+ LDAPDebug( LDAP_DEBUG_FILTER, " lowerkey=%s(%li bytes)\n",
+ encoded (&lowerkey, encbuf), (long)lowerkey.dsize, 0 );
+ LDAPDebug( LDAP_DEBUG_FILTER, " upperkey=%s(%li bytes)\n",
+ encoded (&upperkey, encbuf), (long)upperkey.dsize, 0 );
+ } */
+ data.flags = DB_DBT_MALLOC;
+ lowerkey.flags = DB_DBT_MALLOC;
+ {
+ void *old_lower_key_data = lowerkey.data;
+ *err = dbc->c_get(dbc,&lowerkey,&data,DB_SET_RANGE); /* lowerkey, if allocated and needs freed */
+ DBT_FREE_PAYLOAD(data);
+ if (old_lower_key_data != lowerkey.data) {
+ free(old_lower_key_data);
+ }
+ }
+ /* If the seek above fails due to DB_NOTFOUND, this means that there are no keys
+ which are >= the target key. This means that we should return no candidates */
+ if (0 != *err) {
+ /* Free the key we just read above */
+ DBT_FREE_PAYLOAD(lowerkey);
+ if (DB_NOTFOUND == *err) {
+ *err = 0;
+ idl = NULL;
+ } else {
+ idl = idl_allids( be );
+ ldbm_nasty(errmsg, 1080, *err);
+ LDAPDebug( LDAP_DEBUG_ANY,
+ "<= index_range_read(%s,%s) allids (seek to lower key in index file err %i)\n",
+ type, prefix, *err );
+ }
+ dbc->c_close(dbc);
+ goto error;
+ }
+ /* We now close the cursor, since we're about to iterate over many keys */
+ *err = dbc->c_close(dbc);
+
+ /* step through the indexed db to retrive IDs within the search range */
+ DBT_FREE_PAYLOAD(cur_key);
+ cur_key.data = lowerkey.data;
+ cur_key.size = lowerkey.size;
+ lowerkey.data = NULL; /* Don't need this any more, since the memory will be freed from cur_key */
+ if (operator == SLAPI_OP_GREATER) {
+ *err = index_range_next_key(db,&cur_key,db_txn);
+ }
+ while (*err == 0 &&
+ (operator == SLAPI_OP_LESS) ?
+ DBTcmp(&cur_key, &upperkey) < 0 :
+ DBTcmp(&cur_key, &upperkey) <= 0) {
+ /* exit the loop when we either run off the end of the table,
+ * fail to read a key, or read a key that's out of range.
+ */
+ IDList *tmp, *tmp2;
+ /*
+ char encbuf [BUFSIZ];
+ LDAPDebug( LDAP_DEBUG_FILTER, " cur_key=%s(%li bytes)\n",
+ encoded (&cur_key, encbuf), (long)cur_key.dsize, 0 );
+ */
+ /* Check to see if we've already looked too hard */
+ if (idl != NULL && lookthrough_limit != -1 && idl->b_nids > (ID)lookthrough_limit) {
+ if (NULL != idl) {
+ idl_free(idl);
+ }
+ idl = idl_allids( be );
+ LDAPDebug(LDAP_DEBUG_TRACE, "index_range_read lookthrough_limit exceeded\n",
+ 0, 0, 0);
+ break;
+ }
+ if (idl != NULL && sizelimit > 0 && idl->b_nids > (ID)sizelimit)
+ {
+ LDAPDebug(LDAP_DEBUG_TRACE, "index_range_read sizelimit exceeded\n",
+ 0, 0, 0);
+ break;
+ }
+
+ /* Check to see if the operation has been abandoned (also happens
+ * when the connection is closed by the client).
+ */
+ if ( slapi_op_abandoned( pb )) {
+ if (NULL != idl) {
+ idl_free(idl);
+ idl = NULL;
+ }
+ LDAPDebug(LDAP_DEBUG_TRACE,
+ "index_range_read - operation abandoned\n", 0, 0, 0);
+ break; /* clean up happens outside the while() loop */
+ }
+
+ /* the cur_key DBT already has the first entry in it when we enter the loop */
+ /* so we process the entry then step to the next one */
+ cur_key.flags = 0;
+ for (retry_count = 0; retry_count < IDL_FETCH_RETRY_COUNT; retry_count++) {
+ *err = NEW_IDL_DEFAULT;
+ tmp = idl_fetch( be, db, &cur_key, NULL, ai, err );
+ if(*err == DB_LOCK_DEADLOCK) {
+ ldbm_nasty("index_range_read retrying transaction", 1090, *err);
+ continue;
+ } else {
+ break;
+ }
+ }
+ if(retry_count == IDL_FETCH_RETRY_COUNT) {
+ ldbm_nasty("index_range_read retry count exceeded",1095,*err);
+ }
+ tmp2 = idl_union( be, idl, tmp );
+ idl_free( idl );
+ idl_free( tmp );
+ idl = tmp2;
+ if (ALLIDS(idl)) {
+ LDAPDebug(LDAP_DEBUG_TRACE, "index_range_read hit an allids value\n",
+ 0, 0, 0);
+ break;
+ }
+ if (DBT_EQ (&cur_key, &upperkey)) { /* this is the last key */
+ break;
+ /* Another c_get would return the same key, with no error. */
+ }
+ data.flags = DB_DBT_MALLOC;
+ cur_key.flags = DB_DBT_MALLOC;
+ *err = index_range_next_key(db,&cur_key,db_txn);
+ /* *err = dbc->c_get(dbc,&cur_key,&data,DB_NEXT); */
+ if (*err == DB_NOTFOUND) {
+ *err = 0;
+ break;
+ }
+ }
+ if (*err) LDAPDebug( LDAP_DEBUG_FILTER, " dbc->c_get(...DB_NEXT) == %i\n", *err, 0, 0);
+#ifdef LDAP_DEBUG
+ /* this is for debugging only */
+ if (idl != NULL)
+ {
+ if (ALLIDS(idl)) {
+ LDAPDebug( LDAP_DEBUG_FILTER,
+ " idl=ALLIDS\n", 0, 0, 0 );
+ } else {
+ LDAPDebug( LDAP_DEBUG_FILTER,
+ " idl->b_nids=%d\n", idl->b_nids, 0, 0 );
+ LDAPDebug( LDAP_DEBUG_FILTER,
+ " idl->b_nmax=%d\n", idl->b_nmax, 0, 0 );
+
+ for ( i= 0; i< idl->b_nids; i++)
+ {
+ LDAPDebug( LDAP_DEBUG_FILTER,
+ " idl->b_ids[%d]=%d\n", i, idl->b_ids[i], 0);
+ }
+ }
+ }
+#endif
+error:
+ DBT_FREE_PAYLOAD(cur_key);
+ DBT_FREE_PAYLOAD(upperkey);
+
+ dblayer_release_index_file( be, ai, db );
+
+ LDAPDebug( LDAP_DEBUG_TRACE, "<= index_range_read(%s,%s) %lu candidates\n",
+ type, prefix, (u_long)IDL_NIDS(idl) );
+ return( idl );
+}
+
+/* DBDB: this function is never actually called */
+#if 0
+static int
+addordel_values(
+ backend *be,
+ DB *db,
+ char *type,
+ const char *indextype,
+ struct berval **vals,
+ ID id,
+ int flags, /* BE_INDEX_ADD, etc */
+ back_txn *txn,
+ struct attrinfo *a,
+ int *idl_disposition,
+ void *buffer_handle
+)
+{
+ int rc = 0;
+ int i = 0;
+ DBT key = {0};
+ DB_TXN *db_txn = NULL;
+ size_t plen, vlen, len;
+ char *tmpbuf = NULL;
+ size_t tmpbuflen = 0;
+ char *realbuf;
+ char *prefix;
+
+ LDAPDebug( LDAP_DEBUG_TRACE, "=> %s_values\n",
+ (flags & BE_INDEX_ADD) ? "add" : "del", 0, 0);
+
+ prefix = index2prefix( indextype );
+ if ( vals == NULL ) {
+ key.dptr = prefix;
+ key.dsize = strlen( prefix ) + 1; /* include null terminator */
+ key.flags = DB_DBT_MALLOC;
+ if (NULL != txn) {
+ db_txn = txn->back_txn_txn;
+ }
+
+ if (flags & BE_INDEX_ADD) {
+ rc = idl_insert_key( be, db, &key, id, db_txn, a, idl_disposition );
+ } else {
+ rc = idl_delete_key( be, db, &key, id, db_txn, a );
+ /* check for no such key/id - ok in some cases */
+ if ( rc == DB_NOTFOUND || rc == -666 ) {
+ rc = 0;
+ }
+ }
+
+ if ( rc != 0)
+ {
+ ldbm_nasty(errmsg, 1090, rc);
+ }
+ free_prefix (prefix);
+ if (NULL != key.dptr && prefix != key.dptr)
+ slapi_ch_free( (void**)&key.dptr );
+ LDAPDebug( LDAP_DEBUG_TRACE, "<= %s_values %d\n",
+ (flags & BE_INDEX_ADD) ? "add" : "del", rc, 0 );
+ return( rc );
+ }
+
+ plen = strlen( prefix );
+ for ( i = 0; vals[i] != NULL; i++ ) {
+ vlen = vals[i]->bv_len;
+ len = plen + vlen;
+
+ if ( len < tmpbuflen ) {
+ realbuf = tmpbuf;
+ } else {
+ tmpbuf = slapi_ch_realloc( tmpbuf, len + 1 );
+ tmpbuflen = len + 1;
+ realbuf = tmpbuf;
+ }
+
+ memcpy( realbuf, prefix, plen );
+ memcpy( realbuf+plen, vals[i]->bv_val, vlen );
+ realbuf[len] = '\0';
+ key.dptr = realbuf;
+ key.size = plen + vlen + 1;
+ /* should be okay to use USERMEM here because we know what
+ * the key is and it should never return a different value
+ * than the one we pass in.
+ */
+ key.flags = DB_DBT_USERMEM;
+ key.ulen = tmpbuflen;
+#ifdef LDAP_DEBUG
+ /* XXX if ( slapd_ldap_debug & LDAP_DEBUG_TRACE ) XXX */
+ {
+ char encbuf[BUFSIZ];
+
+ LDAPDebug (LDAP_DEBUG_TRACE, " %s_value(\"%s\")\n",
+ (flags & BE_INDEX_ADD) ? "add" : "del",
+ encoded (&key, encbuf), 0);
+ }
+#endif
+
+ if (NULL != txn) {
+ db_txn = txn->back_txn_txn;
+ }
+
+ if ( flags & BE_INDEX_ADD ) {
+ if (buffer_handle) {
+ rc = index_buffer_insert(buffer_handle,&key,id,be,db_txn,a);
+ if (rc == -2) {
+ rc = idl_insert_key( be, db, &key, id, db_txn, a, idl_disposition );
+ }
+ } else {
+ rc = idl_insert_key( be, db, &key, id, db_txn, a, idl_disposition );
+ }
+ } else {
+ rc = idl_delete_key( be, db, &key, id, db_txn, a );
+ /* check for no such key/id - ok in some cases */
+ if ( rc == DB_NOTFOUND || rc == -666 ) {
+ rc = 0;
+ }
+ }
+ if ( rc != 0 ) {
+ ldbm_nasty(errmsg, 1100, rc);
+ break;
+ }
+ if ( NULL != key.dptr && realbuf != key.dptr) { /* realloc'ed */
+ tmpbuf = key.dptr;
+ tmpbuflen = key.size;
+ }
+ }
+ free_prefix (prefix);
+ if ( tmpbuf != NULL ) {
+ slapi_ch_free( (void**)&tmpbuf );
+ }
+
+ if ( rc != 0 )
+ {
+ ldbm_nasty(errmsg, 1110, rc);
+ }
+ LDAPDebug( LDAP_DEBUG_TRACE, "<= %s_values %d\n",
+ (flags & BE_INDEX_ADD) ? "add" : "del", rc, 0 );
+ return( rc );
+}
+#endif
+
+static int
+addordel_values_sv(
+ backend *be,
+ DB *db,
+ char *type,
+ const char *indextype,
+ Slapi_Value **vals,
+ ID id,
+ int flags, /* BE_INDEX_ADD, etc */
+ back_txn *txn,
+ struct attrinfo *a,
+ int *idl_disposition,
+ void *buffer_handle
+)
+{
+ int rc = 0;
+ int i = 0;
+ DBT key = {0};
+ DB_TXN *db_txn = NULL;
+ size_t plen, vlen, len;
+ char *tmpbuf = NULL;
+ size_t tmpbuflen = 0;
+ char *realbuf;
+ char *prefix;
+ const struct berval *bvp;
+ struct berval *encrypted_bvp = NULL;
+
+ LDAPDebug( LDAP_DEBUG_TRACE, "=> %s_values\n",
+ (flags & BE_INDEX_ADD) ? "add" : "del", 0, 0);
+
+ prefix = index2prefix( indextype );
+ if ( vals == NULL ) {
+ key.dptr = prefix;
+ key.dsize = strlen( prefix ) + 1; /* include null terminator */
+ key.flags = DB_DBT_MALLOC;
+ if (NULL != txn) {
+ db_txn = txn->back_txn_txn;
+ }
+
+ if (flags & BE_INDEX_ADD) {
+ rc = idl_insert_key( be, db, &key, id, db_txn, a, idl_disposition );
+ } else {
+ rc = idl_delete_key( be, db, &key, id, db_txn, a );
+ /* check for no such key/id - ok in some cases */
+ if ( rc == DB_NOTFOUND || rc == -666 ) {
+ rc = 0;
+ }
+ }
+
+ if ( rc != 0 )
+ {
+ ldbm_nasty(errmsg, 1120, rc);
+ }
+ free_prefix (prefix);
+ if (NULL != key.dptr && prefix != key.dptr)
+ slapi_ch_free( (void**)&key.dptr );
+ LDAPDebug( LDAP_DEBUG_TRACE, "<= %s_values %d\n",
+ (flags & BE_INDEX_ADD) ? "add" : "del", rc, 0 );
+ return( rc );
+ }
+
+ plen = strlen( prefix );
+ for ( i = 0; vals[i] != NULL; i++ ) {
+ bvp = slapi_value_get_berval(vals[i]);
+
+ /* Encrypt the index key if necessary */
+ {
+ if (a->ai_attrcrypt && (0 == (flags & BE_INDEX_DONT_ENCRYPT)))
+ {
+ rc = attrcrypt_encrypt_index_key(be,a,bvp,&encrypted_bvp);
+ if (rc)
+ {
+ LDAPDebug (LDAP_DEBUG_ANY, "Failed to encrypt index key for %s\n", a->ai_type ,0,0);
+ } else {
+ bvp = encrypted_bvp;
+ }
+ }
+ }
+
+ vlen = bvp->bv_len;
+ len = plen + vlen;
+
+ if ( len < tmpbuflen ) {
+ realbuf = tmpbuf;
+ } else {
+ tmpbuf = slapi_ch_realloc( tmpbuf, len + 1 );
+ tmpbuflen = len + 1;
+ realbuf = tmpbuf;
+ }
+
+ memcpy( realbuf, prefix, plen );
+ memcpy( realbuf+plen, bvp->bv_val, vlen );
+ realbuf[len] = '\0';
+ key.dptr = realbuf;
+ key.size = plen + vlen + 1;
+ /* Free the encrypted berval if necessary */
+ if (encrypted_bvp)
+ {
+ ber_bvfree(encrypted_bvp);
+ encrypted_bvp = NULL;
+ }
+ /* should be okay to use USERMEM here because we know what
+ * the key is and it should never return a different value
+ * than the one we pass in.
+ */
+ key.flags = DB_DBT_USERMEM;
+ key.ulen = tmpbuflen;
+#ifdef LDAP_DEBUG
+ /* XXX if ( slapd_ldap_debug & LDAP_DEBUG_TRACE ) XXX */
+ {
+ char encbuf[BUFSIZ];
+
+ LDAPDebug (LDAP_DEBUG_TRACE, " %s_value(\"%s\")\n",
+ (flags & BE_INDEX_ADD) ? "add" : "del",
+ encoded (&key, encbuf), 0);
+ }
+#endif
+
+ if (NULL != txn) {
+ db_txn = txn->back_txn_txn;
+ }
+
+ if ( flags & BE_INDEX_ADD ) {
+ if (buffer_handle) {
+ rc = index_buffer_insert(buffer_handle,&key,id,be,db_txn,a);
+ if (rc == -2) {
+ rc = idl_insert_key( be, db, &key, id, db_txn, a, idl_disposition );
+ }
+ } else {
+ rc = idl_insert_key( be, db, &key, id, db_txn, a, idl_disposition );
+ }
+ } else {
+ rc = idl_delete_key( be, db, &key, id, db_txn, a );
+ /* check for no such key/id - ok in some cases */
+ if ( rc == DB_NOTFOUND || rc == -666 ) {
+ rc = 0;
+ }
+ }
+ if ( rc != 0 ) {
+ ldbm_nasty(errmsg, 1130, rc);
+ break;
+ }
+ if ( NULL != key.dptr && realbuf != key.dptr) { /* realloc'ed */
+ tmpbuf = key.dptr;
+ tmpbuflen = key.size;
+ }
+ }
+ free_prefix (prefix);
+ if ( tmpbuf != NULL ) {
+ slapi_ch_free( (void**)&tmpbuf );
+ }
+
+ if ( rc != 0 )
+ {
+ ldbm_nasty(errmsg, 1140, rc);
+ }
+ LDAPDebug( LDAP_DEBUG_TRACE, "<= %s_values %d\n",
+ (flags & BE_INDEX_ADD) ? "add" : "del", rc, 0 );
+ return( rc );
+}
+
+int
+index_addordel_string(backend *be, const char *type, const char *s, ID id, int flags, back_txn *txn)
+{
+ Slapi_Value *svp[2];
+ Slapi_Value sv;
+
+ memset(&sv,0,sizeof(Slapi_Value));
+ sv.bv.bv_len= strlen(s);
+ sv.bv.bv_val= (void*)s;
+ svp[0] = &sv;
+ svp[1] = NULL;
+ return index_addordel_values_ext_sv(be,type,svp,NULL,id,flags,txn,NULL,NULL);
+}
+
+int
+index_addordel_values_sv(
+ backend *be,
+ const char *type,
+ Slapi_Value **vals,
+ Slapi_Value **evals, /* existing values */
+ ID id,
+ int flags,
+ back_txn *txn
+)
+{
+ return index_addordel_values_ext_sv(be,type,vals,evals,
+ id,flags,txn,NULL,NULL);
+}
+
+int
+index_addordel_values_ext_sv(
+ backend *be,
+ const char *type,
+ Slapi_Value **vals,
+ Slapi_Value **evals,
+ ID id,
+ int flags,
+ back_txn *txn,
+ int *idl_disposition,
+ void *buffer_handle
+)
+{
+ DB *db;
+ struct attrinfo *ai = NULL;
+ int err = -1;
+ Slapi_Value **ivals;
+ char buf[SLAPD_TYPICAL_ATTRIBUTE_NAME_MAX_LENGTH];
+ char *basetmp, *basetype;
+
+ LDAPDebug( LDAP_DEBUG_TRACE,
+ "=> index_addordel_values( \"%s\", %lu )\n", type, (u_long)id, 0 );
+
+ basetype = buf;
+ if ( (basetmp = slapi_attr_basetype( type, buf, sizeof(buf) ))
+ != NULL ) {
+ basetype = basetmp;
+ }
+
+ ainfo_get( be, basetype, &ai );
+ if ( ai == NULL || ai->ai_indexmask == 0
+ || ai->ai_indexmask == INDEX_OFFLINE ) {
+ slapi_ch_free_string( &basetmp );
+ return( 0 );
+ }
+ LDAPDebug( LDAP_DEBUG_ARGS, " index_addordel_values indexmask 0x%x\n",
+ ai->ai_indexmask, 0, 0 );
+ if ( (err = dblayer_get_index_file( be, ai, &db, DBOPEN_CREATE )) != 0 ) {
+ LDAPDebug( LDAP_DEBUG_ANY,
+ "<= index_read NULL (could not open index attr %s)\n",
+ basetype, 0, 0 );
+ slapi_ch_free_string( &basetmp );
+ if ( err != 0 ) {
+ ldbm_nasty(errmsg, 1210, err);
+ }
+ goto bad;
+ }
+
+ /*
+ * presence index entry
+ */
+ if (( ai->ai_indexmask & INDEX_PRESENCE ) &&
+ (flags & (BE_INDEX_ADD|BE_INDEX_PRESENCE))) {
+ /* on delete, only remove the presence index if the
+ * BE_INDEX_PRESENCE flag is set.
+ */
+ err = addordel_values_sv( be, db, basetype, indextype_PRESENCE,
+ NULL, id, flags, txn, ai, idl_disposition, NULL );
+ if ( err != 0 ) {
+ ldbm_nasty(errmsg, 1220, err);
+ goto bad;
+ }
+ }
+
+ /*
+ * equality index entry
+ */
+ if ( ai->ai_indexmask & INDEX_EQUALITY ) {
+ slapi_call_syntax_values2keys_sv( ai->ai_plugin, vals, &ivals,
+ LDAP_FILTER_EQUALITY );
+
+ err = addordel_values_sv( be, db, basetype, indextype_EQUALITY,
+ ivals != NULL ? ivals : vals, id, flags, txn, ai, idl_disposition, NULL );
+ if ( ivals != NULL ) {
+ valuearray_free( &ivals );
+ }
+ if ( err != 0 ) {
+ ldbm_nasty(errmsg, 1230, err);
+ goto bad;
+ }
+ }
+
+ /*
+ * approximate index entry
+ */
+ if ( ai->ai_indexmask & INDEX_APPROX ) {
+ slapi_call_syntax_values2keys_sv( ai->ai_plugin, vals, &ivals,
+ LDAP_FILTER_APPROX );
+
+ if ( ivals != NULL ) {
+ err = addordel_values_sv( be, db, basetype,
+ indextype_APPROX, ivals, id, flags, txn, ai, idl_disposition, NULL );
+ valuearray_free( &ivals );
+ if ( err != 0 ) {
+ ldbm_nasty(errmsg, 1240, err);
+ goto bad;
+ }
+ }
+ }
+
+ /*
+ * substrings index entry
+ */
+ if ( ai->ai_indexmask & INDEX_SUB ) {
+ Slapi_Value **esubvals = NULL;
+ Slapi_Value **substresult = NULL;
+ Slapi_Value **origvals = NULL;
+ slapi_call_syntax_values2keys_sv( ai->ai_plugin, vals, &ivals,
+ LDAP_FILTER_SUBSTRINGS );
+
+ origvals = ivals;
+ /* delete only: if the attribute has multiple values,
+ * figure out the substrings that should remain
+ * by slapi_call_syntax_values2keys,
+ * then get rid of them from the being deleted values
+ */
+ if ( evals != NULL ) {
+ slapi_call_syntax_values2keys_sv( ai->ai_plugin, evals, &esubvals,
+ LDAP_FILTER_SUBSTRINGS );
+ substresult = valuearray_minus_valuearray( ai->ai_plugin, ivals, esubvals );
+ ivals = substresult;
+ valuearray_free( &esubvals );
+ }
+ if ( ivals != NULL ) {
+ err = addordel_values_sv( be, db, basetype, indextype_SUB,
+ ivals, id, flags, txn, ai, idl_disposition, buffer_handle );
+ if ( ivals != origvals )
+ valuearray_free( &origvals );
+ valuearray_free( &ivals );
+ if ( err != 0 ) {
+ ldbm_nasty(errmsg, 1250, err);
+ goto bad;
+ }
+
+ ivals = NULL;
+ }
+ }
+
+ /*
+ * matching rule index entries
+ */
+ if ( ai->ai_indexmask & INDEX_RULES )
+ {
+ Slapi_PBlock* pb = slapi_pblock_new();
+ char** oid = ai->ai_index_rules;
+ for (; *oid != NULL; ++oid)
+ {
+ if(create_matchrule_indexer(&pb,*oid,basetype)==0)
+ {
+ char* officialOID = NULL;
+ if (!slapi_pblock_get (pb, SLAPI_PLUGIN_MR_OID, &officialOID) && officialOID != NULL)
+ {
+ Slapi_Value** keys = NULL;
+ matchrule_values_to_keys_sv(pb,vals,&keys);
+ if(keys != NULL && keys[0] != NULL)
+ {
+ /* we've computed keys */
+ err = addordel_values_sv (be, db, basetype, officialOID, keys, id, flags, txn, ai, idl_disposition, NULL);
+ if ( err != 0 )
+ {
+ ldbm_nasty(errmsg, 1260, err);
+ goto bad;
+ }
+ }
+ /*
+ * It would improve speed to save the indexer, for future use.
+ * But, for simplicity, we destroy it now:
+ */
+ destroy_matchrule_indexer(pb);
+ }
+ }
+ }
+ slapi_pblock_destroy (pb);
+ }
+
+ dblayer_release_index_file( be, ai, db );
+ if ( basetmp != NULL ) {
+ slapi_ch_free( (void**)&basetmp );
+ }
+
+ LDAPDebug (LDAP_DEBUG_TRACE, "<= index_addordel_values\n", 0, 0, 0 );
+ return( 0 );
+
+ bad:
+ dblayer_release_index_file(be, ai, db);
+ return err;
+}
+
+int
+index_delete_values(
+ struct ldbminfo *li,
+ char *type,
+ struct berval **vals,
+ ID id
+)
+{
+ return -1;
+}
+
+static int
+is_indexed (const char* indextype, int indexmask, char** index_rules)
+{
+ int indexed;
+ if (indextype == indextype_PRESENCE) indexed = INDEX_PRESENCE & indexmask;
+ else if (indextype == indextype_EQUALITY) indexed = INDEX_EQUALITY & indexmask;
+ else if (indextype == indextype_APPROX) indexed = INDEX_APPROX & indexmask;
+ else if (indextype == indextype_SUB) indexed = INDEX_SUB & indexmask;
+ else { /* matching rule */
+ indexed = 0;
+ if (INDEX_RULES & indexmask) {
+ char** rule;
+ for (rule = index_rules; *rule; ++rule) {
+ if ( ! strcmp( *rule, indextype )) {
+ indexed = INDEX_RULES;
+ break;
+ }
+ }
+ }
+ }
+
+ /* if index is currently being generated, pretend it doesn't exist */
+ if (indexmask & INDEX_OFFLINE)
+ indexed = 0;
+
+ return indexed;
+}
+
+static char*
+index2prefix (const char* indextype)
+{
+ char* prefix;
+ if ( indextype == indextype_PRESENCE ) prefix = prefix_PRESENCE;
+ else if ( indextype == indextype_EQUALITY ) prefix = prefix_EQUALITY;
+ else if ( indextype == indextype_APPROX ) prefix = prefix_APPROX;
+ else if ( indextype == indextype_SUB ) prefix = prefix_SUB;
+ else { /* indextype is a matching rule name */
+ const size_t len = strlen (indextype);
+ char* p = slapi_ch_malloc (len + 3);
+ p[0] = RULE_PREFIX;
+ memcpy( p+1, indextype, len );
+ p[len+1] = ':';
+ p[len+2] = '\0';
+ prefix = p;
+ }
+ return( prefix );
+}
+
+static void
+free_prefix (char* prefix)
+{
+ if (prefix == NULL ||
+ prefix == prefix_PRESENCE ||
+ prefix == prefix_EQUALITY ||
+ prefix == prefix_APPROX ||
+ prefix == prefix_SUB) {
+ /* do nothing */
+ } else {
+ slapi_ch_free( (void**)&prefix);
+ }
+}
+
+/* helper stuff for valuearray_minus_valuearray */
+
+typedef struct {
+ value_compare_fn_type cmp_fn;
+ Slapi_Value *data;
+} SVSORT;
+
+static int
+svsort_cmp(const void *x, const void *y)
+{
+ return ((SVSORT*)x)->cmp_fn(slapi_value_get_berval(((SVSORT*)x)->data),
+ slapi_value_get_berval(((SVSORT*)y)->data));
+}
+
+static int
+bvals_strcasecmp(const struct berval *a, const struct berval *b)
+{
+ return strcasecmp(a->bv_val, b->bv_val);
+}
+
+/* a - b = c */
+/* the returned array of Slapi_Value needs to be freed. */
+static Slapi_Value **
+valuearray_minus_valuearray(
+ void *plugin,
+ Slapi_Value **a,
+ Slapi_Value **b
+)
+{
+ int rc, i, j, k, acnt, bcnt;
+ SVSORT *atmp = NULL, *btmp = NULL;
+ Slapi_Value **c;
+ value_compare_fn_type cmp_fn;
+
+ /* get berval comparison function */
+ plugin_call_syntax_get_compare_fn(plugin, &cmp_fn);
+ if (cmp_fn == NULL) {
+ cmp_fn = (value_compare_fn_type)bvals_strcasecmp;
+ }
+
+ /* determine length of a */
+ for (acnt = 0; a[acnt] != NULL; acnt++);
+
+ /* determine length of b */
+ for (bcnt = 0; b[bcnt] != NULL; bcnt++);
+
+ /* allocate return array as big as a */
+ c = (Slapi_Value**)calloc(acnt+1, sizeof(Slapi_Value*));
+ if (acnt == 0) return c;
+
+ /* sort a */
+ atmp = (SVSORT*) slapi_ch_malloc(acnt*sizeof(SVSORT));
+ for (i = 0; i < acnt; i++) {
+ atmp[i].cmp_fn = cmp_fn;
+ atmp[i].data = a[i];
+ }
+ qsort((void*)atmp, acnt, (size_t)sizeof(SVSORT), svsort_cmp);
+
+ /* sort b */
+ if (bcnt > 0) {
+ btmp = (SVSORT*) slapi_ch_malloc(bcnt*sizeof(SVSORT));
+ for (i = 0; i < bcnt; i++) {
+ btmp[i].cmp_fn = cmp_fn;
+ btmp[i].data = b[i];
+ }
+ qsort((void*)btmp, bcnt, (size_t)sizeof(SVSORT), svsort_cmp);
+ }
+
+ /* lock step through a and b */
+ for (i = 0, j = 0, k = 0; i < acnt && j < bcnt; ) {
+ rc = svsort_cmp(&atmp[i], &btmp[j]);
+ if (rc == 0) {
+ i++;
+ } else if (rc < 0) {
+ c[k++] = slapi_value_new_value(atmp[i++].data);
+ } else {
+ j++;
+ }
+ }
+
+ /* copy what's left from a */
+ while (i < acnt) {
+ c[k++] = slapi_value_new_value(atmp[i++].data);
+ }
+
+ /* clean up */
+ slapi_ch_free((void**)&atmp);
+ if (btmp) slapi_ch_free((void**)&btmp);
+
+ return c;
+}
diff --git a/ldap/servers/slapd/back-ldbm/init.c b/ldap/servers/slapd/back-ldbm/init.c
new file mode 100644
index 00000000..a740ce76
--- /dev/null
+++ b/ldap/servers/slapd/back-ldbm/init.c
@@ -0,0 +1,255 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+/* init.c - initialize ldbm backend */
+
+#include "back-ldbm.h"
+#include "../slapi-plugin.h"
+#include "idlapi.h"
+
+static void *IDL_api[3];
+
+static Slapi_PluginDesc pdesc = { "ldbm-backend", PLUGIN_MAGIC_VENDOR_STR,
+ PRODUCTTEXT, "high-performance LDAP backend database plugin" };
+
+static int add_ldbm_internal_attr_syntax( const char *name, const char *oid,
+ const char *syntax, const char *mr_equality, unsigned long extraflags );
+
+#ifdef _WIN32
+int *module_ldap_debug = 0;
+
+void
+plugin_init_debug_level(int *level_ptr)
+{
+ module_ldap_debug = level_ptr;
+}
+#endif
+
+int
+ldbm_back_init( Slapi_PBlock *pb )
+{
+ struct ldbminfo *li;
+ int rc;
+ struct slapdplugin *p;
+ static int interface_published = 0;
+
+ LDAPDebug( LDAP_DEBUG_TRACE, "=> ldbm_back_init\n", 0, 0, 0 );
+
+ slapi_pblock_get(pb, SLAPI_PLUGIN, &p);
+
+ /* allocate backend-specific stuff */
+ li = (struct ldbminfo *) slapi_ch_calloc( 1, sizeof(struct ldbminfo) );
+
+ /* Record the identity of the ldbm plugin. The plugin
+ * identity is used during internal ops. */
+ slapi_pblock_get(pb, SLAPI_PLUGIN_IDENTITY, &(li->li_identity));
+
+ /* keep a pointer back to the plugin */
+ li->li_plugin = p;
+
+ /* set shutdown flag to zero.*/
+ li->li_shutdown = 0;
+
+ /* Initialize the set of instances. */
+ li->li_instance_set = objset_new(&ldbm_back_instance_set_destructor);
+
+ /* initialize dblayer */
+ if (dblayer_init(li)) {
+ LDAPDebug( LDAP_DEBUG_ANY, "ldbm_back_init: dblayer_init failed\n",0, 0, 0 );
+ return (-1);
+ }
+
+ /* Fill in the fields of the ldbminfo and the dblayer_private
+ * structures with some default values */
+ ldbm_config_setup_default(li);
+
+ /* ask the factory to give us space in the Connection object
+ * (only bulk import uses this)
+ */
+ if (slapi_register_object_extension(p->plg_name, SLAPI_EXT_CONNECTION,
+ factory_constructor, factory_destructor,
+ &li->li_bulk_import_object, &li->li_bulk_import_handle) != 0) {
+ LDAPDebug(LDAP_DEBUG_ANY, "ldbm_back_init: "
+ "slapi_register_object_extension failed.\n", 0, 0, 0);
+ return (-1);
+ }
+
+ /* add some private attributes */
+ rc = add_ldbm_internal_attr_syntax( "entrydn",
+ LDBM_ENTRYDN_OID, DN_SYNTAX_OID, DNMATCH_NAME,
+ SLAPI_ATTR_FLAG_SINGLE );
+
+ rc = add_ldbm_internal_attr_syntax( "dncomp",
+ LDBM_DNCOMP_OID, DN_SYNTAX_OID, DNMATCH_NAME,
+ 0 );
+
+ rc = add_ldbm_internal_attr_syntax( "parentid",
+ LDBM_PARENTID_OID, DIRSTRING_SYNTAX_OID, CASEIGNOREMATCH_NAME,
+ SLAPI_ATTR_FLAG_SINGLE );
+
+ rc = add_ldbm_internal_attr_syntax( "entryid",
+ LDBM_ENTRYID_OID, DIRSTRING_SYNTAX_OID, CASEIGNOREMATCH_NAME,
+ SLAPI_ATTR_FLAG_SINGLE );
+
+ /* set plugin private pointer and initialize locks, etc. */
+ rc = slapi_pblock_set( pb, SLAPI_PLUGIN_PRIVATE, (void *) li );
+
+ if ((li->li_dbcache_mutex = PR_NewLock()) == NULL ) {
+ LDAPDebug( LDAP_DEBUG_ANY, "ldbm_back_init: PR_NewLock failed\n",
+ 0, 0, 0 );
+ return(-1);
+ }
+
+ if ((li->li_shutdown_mutex = PR_NewLock()) == NULL ) {
+ LDAPDebug( LDAP_DEBUG_ANY, "ldbm_back_init: PR_NewLock failed\n",
+ 0, 0, 0 );
+ return(-1);
+ }
+
+ if ((li->li_config_mutex = PR_NewLock()) == NULL ) {
+ LDAPDebug( LDAP_DEBUG_ANY, "ldbm_back_init: PR_NewLock failed\n",
+ 0, 0, 0 );
+ return(-1);
+ }
+
+ if ((li->li_dbcache_cv = PR_NewCondVar( li->li_dbcache_mutex )) == NULL ) {
+ LDAPDebug( LDAP_DEBUG_ANY, "ldbm_back_init: PR_NewCondVar failed\n", 0, 0, 0 );
+ exit(-1);
+ }
+
+ /* set all of the necessary database plugin callback functions */
+ rc |= slapi_pblock_set( pb, SLAPI_PLUGIN_VERSION,
+ (void *) SLAPI_PLUGIN_VERSION_03 );
+ rc |= slapi_pblock_set( pb, SLAPI_PLUGIN_DESCRIPTION,
+ (void *)&pdesc );
+ rc |= slapi_pblock_set( pb, SLAPI_PLUGIN_DB_BIND_FN,
+ (void *) ldbm_back_bind );
+ rc |= slapi_pblock_set( pb, SLAPI_PLUGIN_DB_UNBIND_FN,
+ (void *) ldbm_back_unbind );
+ rc |= slapi_pblock_set( pb, SLAPI_PLUGIN_DB_SEARCH_FN,
+ (void *) ldbm_back_search );
+ rc |= slapi_pblock_set( pb, SLAPI_PLUGIN_DB_NEXT_SEARCH_ENTRY_FN,
+ (void *) ldbm_back_next_search_entry );
+ rc |= slapi_pblock_set( pb, SLAPI_PLUGIN_DB_NEXT_SEARCH_ENTRY_EXT_FN,
+ (void *) ldbm_back_next_search_entry_ext );
+ rc |= slapi_pblock_set( pb, SLAPI_PLUGIN_DB_ENTRY_RELEASE_FN,
+ (void *) ldbm_back_entry_release );
+ rc |= slapi_pblock_set( pb, SLAPI_PLUGIN_DB_COMPARE_FN,
+ (void *) ldbm_back_compare );
+ rc |= slapi_pblock_set( pb, SLAPI_PLUGIN_DB_MODIFY_FN,
+ (void *) ldbm_back_modify );
+ rc |= slapi_pblock_set( pb, SLAPI_PLUGIN_DB_MODRDN_FN,
+ (void *) ldbm_back_modrdn );
+ rc |= slapi_pblock_set( pb, SLAPI_PLUGIN_DB_ADD_FN,
+ (void *) ldbm_back_add );
+ rc |= slapi_pblock_set( pb, SLAPI_PLUGIN_DB_DELETE_FN,
+ (void *) ldbm_back_delete );
+ rc |= slapi_pblock_set( pb, SLAPI_PLUGIN_DB_ABANDON_FN,
+ (void *) ldbm_back_abandon );
+ rc |= slapi_pblock_set( pb, SLAPI_PLUGIN_CLOSE_FN,
+ (void *) ldbm_back_close );
+ rc |= slapi_pblock_set( pb, SLAPI_PLUGIN_CLEANUP_FN,
+ (void *) ldbm_back_cleanup );
+ rc |= slapi_pblock_set( pb, SLAPI_PLUGIN_DB_FLUSH_FN,
+ (void *) ldbm_back_flush );
+ rc |= slapi_pblock_set( pb, SLAPI_PLUGIN_START_FN,
+ (void *) ldbm_back_start );
+ rc |= slapi_pblock_set( pb, SLAPI_PLUGIN_DB_SEQ_FN,
+ (void *) ldbm_back_seq );
+ rc |= slapi_pblock_set( pb, SLAPI_PLUGIN_DB_RMDB_FN,
+ (void *) ldbm_back_rmdb );
+ rc |= slapi_pblock_set( pb, SLAPI_PLUGIN_DB_LDIF2DB_FN,
+ (void *) ldbm_back_ldif2ldbm );
+ rc |= slapi_pblock_set( pb, SLAPI_PLUGIN_DB_DB2LDIF_FN,
+ (void *) ldbm_back_ldbm2ldif );
+ rc |= slapi_pblock_set( pb, SLAPI_PLUGIN_DB_DB2INDEX_FN,
+ (void *) ldbm_back_ldbm2index );
+ rc |= slapi_pblock_set( pb, SLAPI_PLUGIN_DB_ARCHIVE2DB_FN,
+ (void *) ldbm_back_archive2ldbm );
+ rc |= slapi_pblock_set( pb, SLAPI_PLUGIN_DB_DB2ARCHIVE_FN,
+ (void *) ldbm_back_ldbm2archive );
+#if defined(UPGRADEDB)
+ rc |= slapi_pblock_set( pb, SLAPI_PLUGIN_DB_UPGRADEDB_FN,
+ (void *) ldbm_back_upgradedb );
+#endif
+ rc |= slapi_pblock_set( pb, SLAPI_PLUGIN_DB_BEGIN_FN,
+ (void *) dblayer_plugin_begin );
+ rc |= slapi_pblock_set( pb, SLAPI_PLUGIN_DB_COMMIT_FN,
+ (void *) dblayer_plugin_commit );
+ rc |= slapi_pblock_set( pb, SLAPI_PLUGIN_DB_ABORT_FN,
+ (void *) dblayer_plugin_abort );
+ rc |= slapi_pblock_set( pb, SLAPI_PLUGIN_DB_SIZE_FN,
+ (void *) ldbm_db_size );
+ rc |= slapi_pblock_set( pb, SLAPI_PLUGIN_DB_TEST_FN,
+ (void *) ldbm_back_db_test );
+ rc |= slapi_pblock_set( pb, SLAPI_PLUGIN_DB_INIT_INSTANCE_FN,
+ (void *) ldbm_back_init ); /* register itself so that the secon instance
+ can be initialized */
+ rc |= slapi_pblock_set( pb, SLAPI_PLUGIN_DB_WIRE_IMPORT_FN,
+ (void *) ldbm_back_wire_import);
+
+ if ( rc != 0 ) {
+ LDAPDebug( LDAP_DEBUG_ANY, "ldbm_back_init failed\n", 0, 0, 0 );
+ return( -1 );
+ }
+
+ /* register the IDL interface with the API broker */
+ if(!interface_published)
+ {
+ IDL_api[0] = 0;
+ IDL_api[1] = (void *)idl_alloc;
+ IDL_api[2] = (void *)idl_insert;
+
+ if( slapi_apib_register(IDL_v1_0_GUID, IDL_api) )
+ {
+ LDAPDebug( LDAP_DEBUG_ANY, "ldbm_back_init: failed to publish IDL interface\n", 0, 0, 0);
+ return( -1 );
+ }
+
+ interface_published = 1;
+ }
+
+ LDAPDebug( LDAP_DEBUG_TRACE, "<= ldbm_back_init\n", 0, 0, 0 );
+
+ return( 0 );
+}
+
+
+/*
+ * Add an attribute syntax using some default flags, etc.
+ * Returns an LDAP error code (LDAP_SUCCESS if all goes well)
+ */
+static int
+add_ldbm_internal_attr_syntax( const char *name, const char *oid,
+ const char *syntax, const char *mr_equality, unsigned long extraflags )
+{
+ int rc = LDAP_SUCCESS;
+ struct asyntaxinfo *asip;
+ char *names[2];
+ char *origins[2];
+ unsigned long std_flags = SLAPI_ATTR_FLAG_STD_ATTR | SLAPI_ATTR_FLAG_OPATTR
+ | SLAPI_ATTR_FLAG_NOUSERMOD;
+
+ names[0] = (char *)name;
+ names[1] = NULL;
+
+ origins[0] = SLAPD_VERSION_STR;
+ origins[1] = NULL;
+
+ rc = attr_syntax_create( oid, names, 1,
+ "Netscape defined attribute type",
+ NULL, /* superior */
+ mr_equality, NULL, NULL, /* matching rules */
+ origins, syntax,
+ SLAPI_SYNTAXLENGTH_NONE,
+ std_flags | extraflags,
+ &asip );
+
+ if ( rc == LDAP_SUCCESS ) {
+ rc = attr_syntax_add( asip );
+ }
+
+ return rc;
+}
diff --git a/ldap/servers/slapd/back-ldbm/instance.c b/ldap/servers/slapd/back-ldbm/instance.c
new file mode 100644
index 00000000..aa672000
--- /dev/null
+++ b/ldap/servers/slapd/back-ldbm/instance.c
@@ -0,0 +1,353 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+#include "back-ldbm.h"
+
+/* Forward declarations */
+static void ldbm_instance_destructor(void **arg);
+
+
+
+/* Creates and initializes a new ldbm_instance structure.
+ * Also sets up some default indexes for the new instance.
+ */
+int ldbm_instance_create(backend *be, char *name)
+{
+ struct ldbminfo *li = (struct ldbminfo *) be->be_database->plg_private;
+ ldbm_instance *inst;
+
+ /* Allocate storage for the ldbm_instance structure. Information specific
+ * to this instance of the ldbm backend will be held here. */
+ inst = (ldbm_instance *) slapi_ch_calloc(1, sizeof(ldbm_instance));
+
+ /* Record the name of this instance. */
+ inst->inst_name = strdup(name);
+
+ /* initialize the entry cache */
+ if (! cache_init(&(inst->inst_cache), DEFAULT_CACHE_SIZE,
+ DEFAULT_CACHE_ENTRIES)) {
+ LDAPDebug(LDAP_DEBUG_ANY, "ldbm_instance_create: cache_init failed\n",
+ 0, 0, 0);
+ return -1;
+ }
+
+ /* Lock for the list of open db handles */
+ inst->inst_handle_list_mutex = PR_NewLock();
+ if (NULL == inst->inst_handle_list_mutex) {
+ LDAPDebug(LDAP_DEBUG_ANY, "ldbm_instance_create: PR_NewLock failed\n",
+ 0, 0, 0);
+ return -1;
+ }
+
+ /* Lock used to synchronize modify operations. */
+ inst->inst_db_mutex = PR_NewLock();
+ if (NULL == inst->inst_db_mutex) {
+ LDAPDebug(LDAP_DEBUG_ANY, "ldbm_instance_create: PR_NewLock failed\n",
+ 0, 0, 0);
+ return -1;
+ }
+
+ if ((inst->inst_config_mutex = PR_NewLock()) == NULL) {
+ LDAPDebug(LDAP_DEBUG_ANY, "ldbm_instance_create: PR_NewLock failed\n",
+ 0, 0, 0);
+ return -1;
+ }
+
+ if ((inst->inst_nextid_mutex = PR_NewLock()) == NULL) {
+ LDAPDebug(LDAP_DEBUG_ANY, "ldbm_instance_create: PR_NewLock failed\n",
+ 0, 0, 0);
+ return -1;
+ }
+
+ if ((inst->inst_indexer_cv = PR_NewCondVar(inst->inst_nextid_mutex)) == NULL) {
+ LDAPDebug(LDAP_DEBUG_ANY, "ldbm_instance_create: PR_NewCondVar failed\n", 0, 0, 0 );
+ return -1;
+ }
+
+ inst->inst_be = be;
+ inst->inst_li = li;
+ be->be_instance_info = inst;
+
+ /* Initialize the fields with some default values. */
+ ldbm_instance_config_setup_default(inst);
+
+ /* Add this new instance to the the set of instances */
+ {
+ Object *instance_obj;
+
+ instance_obj = object_new((void *) inst, &ldbm_instance_destructor);
+ objset_add_obj(li->li_instance_set, instance_obj);
+ object_release(instance_obj);
+ }
+
+ return 0;
+}
+
+/* create the default indexes separately
+ * (because when we're creating a new backend while the server is running,
+ * the DSE needs to be pre-seeded first.)
+ */
+int ldbm_instance_create_default_indexes(backend *be)
+{
+ char *argv[ 9 ];
+ ldbm_instance *inst = (ldbm_instance *)be->be_instance_info;
+ /* write the dse file only on the final index */
+ int flags = LDBM_INSTANCE_CONFIG_DONT_WRITE;
+
+ /*
+ * Always index entrydn, parentid, objectclass, subordinatecount
+ * copiedFrom, and aci,
+ * since they are used by some searches, replication and the
+ * ACL routines.
+ */
+
+ argv[ 0 ] = "entrydn";
+ argv[ 1 ] = "eq";
+ argv[ 2 ] = NULL;
+ ldbm_instance_config_add_index_entry(inst, 2, argv, flags);
+
+ argv[ 0 ] = "parentid";
+ argv[ 1 ] = "eq";
+ argv[ 2 ] = NULL;
+ ldbm_instance_config_add_index_entry(inst, 2, argv, flags);
+
+ argv[ 0 ] = "objectclass";
+ argv[ 1 ] = "eq";
+ argv[ 2 ] = NULL;
+ ldbm_instance_config_add_index_entry(inst, 2, argv, flags);
+
+ argv[ 0 ] = "aci";
+ argv[ 1 ] = "pres";
+ argv[ 2 ] = NULL;
+ ldbm_instance_config_add_index_entry(inst, 2, argv, flags);
+
+#if 0 /* don't need copiedfrom */
+ argv[ 0 ] = "copiedfrom";
+ argv[ 1 ] = "pres";
+ argv[ 2 ] = NULL;
+ ldbm_instance_config_add_index_entry(inst, 2, argv, flags);
+#endif
+
+ argv[ 0 ] = "numsubordinates";
+ argv[ 1 ] = "pres";
+ argv[ 2 ] = NULL;
+ ldbm_instance_config_add_index_entry(inst, 2, argv, flags);
+
+ argv[ 0 ] = SLAPI_ATTR_UNIQUEID;
+ argv[ 1 ] = "eq";
+ argv[ 2 ] = NULL;
+ ldbm_instance_config_add_index_entry(inst, 2, argv, flags);
+
+ /* For MMR, we need this attribute (to replace use of dncomp in delete). */
+ argv[ 0 ] = ATTR_NSDS5_REPLCONFLICT;
+ argv[ 1 ] = "eq";
+ argv[ 2 ] = NULL;
+ ldbm_instance_config_add_index_entry(inst, 2, argv, flags);
+
+ /* write the dse file only on the final index */
+ argv[ 0 ] = SLAPI_ATTR_NSCP_ENTRYDN;
+ argv[ 1 ] = "eq";
+ argv[ 2 ] = NULL;
+ ldbm_instance_config_add_index_entry(inst, 2, argv, 0);
+
+ argv[ 0 ] = LDBM_PSEUDO_ATTR_DEFAULT;
+ argv[ 1 ] = "none";
+ argv[ 2 ] = NULL;
+ /* ldbm_instance_config_add_index_entry(inst, 2, argv); */
+ attr_index_config( be, "ldbm index init", 0, 2, argv, 1 );
+
+ /*
+ * ancestorid is special, there is actually no such attr type
+ * but we still want to use the attr index file APIs.
+ */
+ argv[ 0 ] = "ancestorid";
+ argv[ 1 ] = "eq";
+ argv[ 2 ] = NULL;
+ attr_index_config( be, "ldbm index init", 0, 2, argv, 1 );
+
+ return 0;
+}
+
+
+/* Starts a backend instance */
+int
+ldbm_instance_start(backend *be)
+{
+ int rc;
+ PR_Lock (be->be_state_lock);
+
+ if (be->be_state != BE_STATE_STOPPED &&
+ be->be_state != BE_STATE_DELETED) {
+ LDAPDebug( LDAP_DEBUG_TRACE,
+ "ldbm_instance_start: warning - backend is in a wrong state - %d\n",
+ be->be_state, 0, 0 );
+ PR_Unlock (be->be_state_lock);
+ return 0;
+ }
+
+ rc = dblayer_instance_start(be, DBLAYER_NORMAL_MODE);
+ be->be_state = BE_STATE_STARTED;
+
+ PR_Unlock (be->be_state_lock);
+
+ return rc;
+}
+
+
+/* Stops a backend instance */
+int
+ldbm_instance_stop(backend *be)
+{
+ int rc;
+ ldbm_instance *inst = (ldbm_instance *)be->be_instance_info;
+
+ PR_Lock (be->be_state_lock);
+
+ if (be->be_state != BE_STATE_STARTED) {
+ LDAPDebug( LDAP_DEBUG_ANY,
+ "ldbm_back_close: warning - backend %s is in the wrong state - %d\n",
+ inst ? inst->inst_name : "", be->be_state, 0 );
+ PR_Unlock (be->be_state_lock);
+ return 0;
+ }
+
+ rc = dblayer_instance_close(be);
+
+ be->be_state = BE_STATE_STOPPED;
+ PR_Unlock (be->be_state_lock);
+
+ cache_destroy_please(&inst->inst_cache);
+
+ return rc;
+}
+
+
+/* Walks down the set of instances, starting each one. */
+int
+ldbm_instance_startall(struct ldbminfo *li)
+{
+ Object *inst_obj;
+ ldbm_instance *inst;
+ int rc = 0;
+
+ inst_obj = objset_first_obj(li->li_instance_set);
+ while (inst_obj != NULL) {
+ int rc1;
+ inst = (ldbm_instance *) object_get_data(inst_obj);
+ rc1 = ldbm_instance_start(inst->inst_be);
+ if (rc1 != 0) {
+ rc = rc1;
+ } else {
+ vlv_init(inst);
+ }
+ inst_obj = objset_next_obj(li->li_instance_set, inst_obj);
+ }
+
+ return rc;
+}
+
+
+/* Walks down the set of instances, stopping each one. */
+int ldbm_instance_stopall(struct ldbminfo *li)
+{
+ Object *inst_obj;
+ ldbm_instance *inst;
+
+ inst_obj = objset_first_obj(li->li_instance_set);
+ while (inst_obj != NULL) {
+ inst = (ldbm_instance *) object_get_data(inst_obj);
+ ldbm_instance_stop(inst->inst_be);
+ inst_obj = objset_next_obj(li->li_instance_set, inst_obj);
+ }
+
+ return 0;
+}
+
+
+/* Walks down the set of instance, looking for one
+ * with the given name. Returns a pointer to the
+ * instance if found, and NULL if not found. The
+ * string compare on the instance name is NOT case
+ * sensitive.
+ */
+/* Currently this function doesn't bump
+ * the ref count of the instance returned.
+ */
+ldbm_instance *
+ldbm_instance_find_by_name(struct ldbminfo *li, char *name)
+{
+ Object *inst_obj;
+ ldbm_instance *inst;
+
+ inst_obj = objset_first_obj(li->li_instance_set);
+ while (inst_obj != NULL) {
+ inst = (ldbm_instance *) object_get_data(inst_obj);
+ if (!strcasecmp(inst->inst_name, name)) {
+ /* Currently we release the object here. There is no
+ * function for callers of this function to call to
+ * release the object.
+ */
+ object_release(inst_obj);
+ return inst;
+ }
+ inst_obj = objset_next_obj(li->li_instance_set, inst_obj);
+ }
+ return NULL;
+}
+
+
+/* Called when all references to the instance are gone. */
+/* (ie, only when an instance is being deleted) */
+static void
+ldbm_instance_destructor(void **arg)
+{
+ ldbm_instance *inst = (ldbm_instance *) *arg;
+
+ LDAPDebug(LDAP_DEBUG_ANY, "Destructor for instance %s called\n",
+ inst->inst_name, 0, 0);
+
+ slapi_ch_free((void **)&inst->inst_name);
+ PR_DestroyLock(inst->inst_config_mutex);
+ slapi_ch_free((void **)&inst->inst_dir_name);
+ PR_DestroyLock(inst->inst_db_mutex);
+ PR_DestroyLock(inst->inst_handle_list_mutex);
+ PR_DestroyLock(inst->inst_nextid_mutex);
+ PR_DestroyCondVar(inst->inst_indexer_cv);
+ attrinfo_deletetree(inst);
+ if (inst->inst_dataversion) {
+ slapi_ch_free((void **)&inst->inst_dataversion);
+ }
+ /* cache has already been destroyed */
+
+ slapi_ch_free((void **)&inst);
+}
+
+
+static int
+ldbm_instance_comparator(Object *object, const void *name)
+{
+ void *data = object_get_data(object);
+ return (data == name) ? 0 : 1;
+}
+
+
+/* find the instance in the objset and remove it */
+int
+ldbm_instance_destroy(ldbm_instance *inst)
+{
+ Object *object = NULL;
+ struct ldbminfo *li = inst->inst_li;
+
+ object = objset_find(li->li_instance_set, ldbm_instance_comparator, inst);
+ if (object == NULL) {
+ return -1;
+ }
+ /* decref from objset_find */
+ object_release(object);
+
+ /* now remove from the instance set */
+ objset_remove_obj(li->li_instance_set, object);
+ return 0;
+}
diff --git a/ldap/servers/slapd/back-ldbm/ldbm_abandon.c b/ldap/servers/slapd/back-ldbm/ldbm_abandon.c
new file mode 100644
index 00000000..6dd8c087
--- /dev/null
+++ b/ldap/servers/slapd/back-ldbm/ldbm_abandon.c
@@ -0,0 +1,14 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+/* abandon.c - ldbm backend abandon routine */
+
+#include "back-ldbm.h"
+
+int ldbm_back_abandon(Slapi_PBlock *pb)
+{
+ /* DBDB need to implement this */
+ return 0;
+}
diff --git a/ldap/servers/slapd/back-ldbm/ldbm_add.c b/ldap/servers/slapd/back-ldbm/ldbm_add.c
new file mode 100644
index 00000000..1c4b8541
--- /dev/null
+++ b/ldap/servers/slapd/back-ldbm/ldbm_add.c
@@ -0,0 +1,880 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+
+/* add.c - ldap ldbm back-end add routine */
+
+#include "back-ldbm.h"
+
+extern char *numsubordinates;
+extern char *hassubordinates;
+
+static void delete_update_entry_dn_operational_attributes(struct backentry *ep);
+
+/* in order to find the parent, we must have either the parent dn or uniqueid
+ This function will return true if either are set, or false otherwise */
+static int
+have_parent_address(const Slapi_DN *parentsdn, const char *parentuniqueid)
+{
+ if (parentuniqueid && parentuniqueid[0]) {
+ return 1; /* have parent uniqueid */
+ }
+
+ if (parentsdn && !slapi_sdn_isempty(parentsdn)) {
+ return 1; /* have parent dn */
+ }
+
+ return 0; /* have no address */
+}
+
+int
+ldbm_back_add( Slapi_PBlock *pb )
+{
+ backend *be;
+ struct ldbminfo *li;
+ ldbm_instance *inst;
+ char *dn = NULL;
+ Slapi_Entry *e;
+ struct backentry *tombstoneentry = NULL;
+ struct backentry *addingentry = NULL;
+ struct backentry *parententry = NULL;
+ ID pid;
+ int isroot;
+ char *errbuf= NULL;
+ back_txn txn;
+ back_txnid parent_txn;
+ int retval = -1;
+ char *msg;
+ int managedsait;
+ int ldap_result_code = LDAP_SUCCESS;
+ char *ldap_result_message= NULL;
+ char *ldap_result_matcheddn= NULL;
+ int retry_count = 0;
+ int disk_full = 0;
+ modify_context parent_modify_c = {0};
+ int parent_found = 0;
+ int rc;
+ int addingentry_id_assigned= 0;
+ int addingentry_in_cache= 0;
+ int tombstone_in_cache= 0;
+ Slapi_DN sdn;
+ Slapi_DN parentsdn;
+ Slapi_Operation *operation;
+ int dblock_acquired= 0;
+ int is_replicated_operation= 0;
+ int is_resurect_operation= 0;
+ int is_tombstone_operation= 0;
+ int is_fixup_operation= 0;
+ CSN *opcsn = NULL;
+ entry_address addr;
+
+ slapi_pblock_get( pb, SLAPI_PLUGIN_PRIVATE, &li );
+ slapi_pblock_get( pb, SLAPI_ADD_ENTRY, &e );
+ slapi_pblock_get( pb, SLAPI_REQUESTOR_ISROOT, &isroot );
+ slapi_pblock_get( pb, SLAPI_MANAGEDSAIT, &managedsait );
+ slapi_pblock_get( pb, SLAPI_PARENT_TXN, (void**)&parent_txn );
+ slapi_pblock_get( pb, SLAPI_OPERATION, &operation );
+ slapi_pblock_get( pb, SLAPI_IS_REPLICATED_OPERATION, &is_replicated_operation );
+ slapi_pblock_get( pb, SLAPI_BACKEND, &be);
+
+ is_resurect_operation= operation_is_flag_set(operation,OP_FLAG_RESURECT_ENTRY);
+ is_tombstone_operation= operation_is_flag_set(operation,OP_FLAG_TOMBSTONE_ENTRY);
+ is_fixup_operation = operation_is_flag_set(operation,OP_FLAG_REPL_FIXUP);
+
+ inst = (ldbm_instance *) be->be_instance_info;
+
+ slapi_sdn_init(&sdn);
+ slapi_sdn_init(&parentsdn);
+
+ /* Get rid of ldbm backend attributes that you are not allowed to specify yourself */
+ slapi_entry_delete_values( e, hassubordinates, NULL );
+ slapi_entry_delete_values( e, numsubordinates, NULL );
+
+ dblayer_txn_init(li,&txn);
+
+ /* The dblock serializes writes to the database,
+ * which reduces deadlocking in the db code,
+ * which means that we run faster.
+ *
+ * But, this lock is re-enterant for the fixup
+ * operations that the URP code in the Replication
+ * plugin generates.
+ */
+ if(SERIALLOCK(li) && !is_fixup_operation)
+ {
+ dblayer_lock_backend(be);
+ dblock_acquired= 1;
+ }
+
+ rc= 0;
+
+ /*
+ * We are about to pass the last abandon test, so from now on we are
+ * committed to finish this operation. Set status to "will complete"
+ * before we make our last abandon check to avoid race conditions in
+ * the code that processes abandon operations.
+ */
+ if (operation) {
+ operation->o_status = SLAPI_OP_STATUS_WILL_COMPLETE;
+ }
+ if ( slapi_op_abandoned( pb ) ) {
+ goto error_return;
+ }
+
+
+ if (!is_tombstone_operation && !is_resurect_operation)
+ {
+ rc= slapi_setbit_int(rc,SLAPI_RTN_BIT_FETCH_EXISTING_DN_ENTRY);
+ }
+ rc= slapi_setbit_int(rc,SLAPI_RTN_BIT_FETCH_EXISTING_UNIQUEID_ENTRY);
+ rc= slapi_setbit_int(rc,SLAPI_RTN_BIT_FETCH_PARENT_ENTRY);
+ while(rc!=0)
+ {
+ /* JCM - copying entries can be expensive... should optimize */
+ /*
+ * Some present state information is passed through the PBlock to the
+ * backend pre-op plugin. To ensure a consistent snapshot of this state
+ * we wrap the reading of the entry with the dblock.
+ */
+ if(slapi_isbitset_int(rc,SLAPI_RTN_BIT_FETCH_EXISTING_UNIQUEID_ENTRY))
+ {
+ /* Check if an entry with the intended uniqueid already exists. */
+ done_with_pblock_entry(pb,SLAPI_ADD_EXISTING_UNIQUEID_ENTRY); /* Could be through this multiple times */
+ addr.dn = NULL;
+ addr.uniqueid = (char*)slapi_entry_get_uniqueid(e); /* jcm - cast away const */
+ ldap_result_code= get_copy_of_entry(pb, &addr, &txn, SLAPI_ADD_EXISTING_UNIQUEID_ENTRY, !is_replicated_operation);
+ }
+ if(slapi_isbitset_int(rc,SLAPI_RTN_BIT_FETCH_EXISTING_DN_ENTRY))
+ {
+ slapi_pblock_get( pb, SLAPI_ADD_TARGET, &dn );
+ slapi_sdn_set_dn_byref(&sdn, dn);
+ slapi_sdn_get_backend_parent(&sdn,&parentsdn,pb->pb_backend);
+ /* Check if an entry with the intended DN already exists. */
+ done_with_pblock_entry(pb,SLAPI_ADD_EXISTING_DN_ENTRY); /* Could be through this multiple times */
+ addr.dn = dn;
+ addr.uniqueid = NULL;
+ ldap_result_code= get_copy_of_entry(pb, &addr, &txn, SLAPI_ADD_EXISTING_DN_ENTRY, !is_replicated_operation);
+ }
+ /* if we can find the parent by dn or uniqueid, and the operation has requested the parent
+ then get it */
+ if(have_parent_address(&parentsdn, operation->o_params.p.p_add.parentuniqueid) &&
+ slapi_isbitset_int(rc,SLAPI_RTN_BIT_FETCH_PARENT_ENTRY))
+ {
+ done_with_pblock_entry(pb,SLAPI_ADD_PARENT_ENTRY); /* Could be through this multiple times */
+ addr.dn = (char*)slapi_sdn_get_ndn (&parentsdn);
+ addr.uniqueid = operation->o_params.p.p_add.parentuniqueid;
+ ldap_result_code= get_copy_of_entry(pb, &addr, &txn, SLAPI_ADD_PARENT_ENTRY, !is_replicated_operation);
+ /* need to set parentsdn or parentuniqueid if either is not set? */
+ }
+
+ /* Call the Backend Pre Add plugins */
+ slapi_pblock_set(pb, SLAPI_RESULT_CODE, &ldap_result_code);
+ rc= plugin_call_plugins(pb, SLAPI_PLUGIN_BE_PRE_ADD_FN);
+ if(rc==-1)
+ {
+ /*
+ * Plugin indicated some kind of failure,
+ * or that this Operation became a No-Op.
+ */
+ slapi_pblock_get(pb, SLAPI_RESULT_CODE, &ldap_result_code);
+ goto error_return;
+ }
+ /*
+ * (rc!=-1 && rc!= 0) means that the plugin changed things, so we go around
+ * the loop once again to get the new present state.
+ */
+ /* JCMREPL - Warning: A Plugin could cause an infinite loop by always returning a result code that requires some action. */
+ }
+
+ /*
+ * Originally (in the U-M LDAP 3.3 code), there was a comment near this
+ * code about a race condition. The race was that a 2nd entry could be
+ * added between the time when we check for an already existing entry
+ * and the cache_add_entry_lock() call below. A race condition no
+ * longer exists, because now we keep the parent entry locked for
+ * the duration of the old race condition's window of opportunity.
+ */
+
+ /*
+ * Fetch the parent entry and acquire the cache lock.
+ */
+ if(have_parent_address(&parentsdn, operation->o_params.p.p_add.parentuniqueid))
+ {
+ addr.dn = (char*)slapi_sdn_get_ndn (&parentsdn);
+ addr.uniqueid = operation->o_params.p.p_add.parentuniqueid;
+ parententry = find_entry2modify_only(pb,be,&addr,&txn);
+ if (parententry && parententry->ep_entry) {
+ if (!operation->o_params.p.p_add.parentuniqueid){
+ /* Set the parentuniqueid now */
+ operation->o_params.p.p_add.parentuniqueid = slapi_ch_strdup(slapi_entry_get_uniqueid(parententry->ep_entry));
+ }
+ if (slapi_sdn_isempty(&parentsdn)) {
+ /* Set the parentsdn now */
+ slapi_sdn_set_dn_byval(&parentsdn, slapi_entry_get_dn_const(parententry->ep_entry));
+ }
+ }
+ modify_init(&parent_modify_c,parententry);
+ }
+
+ /* Check if the entry we have been asked to add already exists */
+ {
+ Slapi_Entry *entry;
+ slapi_pblock_get( pb, SLAPI_ADD_EXISTING_DN_ENTRY, &entry);
+ if ( entry != NULL )
+ {
+ /* The entry already exists */
+ ldap_result_code= LDAP_ALREADY_EXISTS;
+ goto error_return;
+ }
+ else
+ {
+ /*
+ * did not find the entry - this is good, since we're
+ * trying to add it, but we have to check whether the
+ * entry we did match has a referral we should return
+ * instead. we do this only if managedsait is not on.
+ */
+ if ( !managedsait && !is_tombstone_operation )
+ {
+ int err= 0;
+ Slapi_DN ancestordn= {0};
+ struct backentry *ancestorentry;
+ ancestorentry= dn2ancestor(pb->pb_backend,&sdn,&ancestordn,&txn,&err);
+ slapi_sdn_done(&ancestordn);
+ if ( ancestorentry != NULL )
+ {
+ int sentreferral= check_entry_for_referral(pb, ancestorentry->ep_entry, backentry_get_ndn(ancestorentry), "ldbm_back_add");
+ cache_return( &inst->inst_cache, &ancestorentry );
+ if(sentreferral)
+ {
+ ldap_result_code= -1; /* The result was sent by check_entry_for_referral */
+ goto error_return;
+ }
+ }
+ }
+ }
+ }
+
+
+ if ((operation_is_flag_set(operation,OP_FLAG_ACTION_SCHEMA_CHECK)) && slapi_entry_schema_check(pb, e) != 0)
+ {
+ LDAPDebug(LDAP_DEBUG_TRACE, "entry failed schema check\n", 0, 0, 0);
+ ldap_result_code = LDAP_OBJECT_CLASS_VIOLATION;
+ slapi_pblock_get(pb, SLAPI_PB_RESULT_TEXT, &ldap_result_message);
+ goto error_return;
+ }
+
+ opcsn = operation_get_csn (operation);
+ if(is_resurect_operation)
+ {
+ char *reason = NULL;
+ /*
+ * When we resurect a tombstone we must use its UniqueID
+ * to find the tombstone entry and lock it down in the cache.
+ */
+ addr.dn = NULL;
+ addr.uniqueid = (char *)slapi_entry_get_uniqueid(e); /* jcm - cast away const */
+ tombstoneentry = find_entry2modify( pb, be, &addr, NULL );
+ if ( tombstoneentry==NULL )
+ {
+ ldap_result_code= -1;
+ goto error_return; /* error result sent by find_entry2modify() */
+ }
+ tombstone_in_cache = 1;
+
+ addingentry = backentry_dup( tombstoneentry );
+ if ( addingentry==NULL )
+ {
+ ldap_result_code= LDAP_OPERATIONS_ERROR;
+ goto error_return;
+ }
+ /*
+ * To resurect a tombstone we must fix its DN and remove the
+ * parent UniqueID that we stashed in there.
+ *
+ * The entry comes back to life as a Glue entry, so we add the
+ * magic objectclass.
+ */
+ slapi_pblock_get( pb, SLAPI_ADD_TARGET, &dn );
+ slapi_sdn_set_dn_byref(&sdn, dn);
+ slapi_entry_set_dn(addingentry->ep_entry, slapi_ch_strdup(dn)); /* The DN is passed into the entry. */
+ /* LPREPL: the DN is normalized...Somehow who should get a not normalized one */
+ addingentry->ep_id = slapi_entry_attr_get_ulong(addingentry->ep_entry,"entryid");
+ slapi_entry_attr_delete(addingentry->ep_entry, SLAPI_ATTR_VALUE_PARENT_UNIQUEID);
+ slapi_entry_delete_string(addingentry->ep_entry, SLAPI_ATTR_OBJECTCLASS, SLAPI_ATTR_VALUE_TOMBSTONE);
+ /* Now also remove the nscpEntryDN */
+ if (slapi_entry_attr_delete(addingentry->ep_entry, SLAPI_ATTR_NSCP_ENTRYDN) != 0){
+ LDAPDebug(LDAP_DEBUG_REPL, "Resurrection of %s - Couldn't remove %s\n", dn, SLAPI_ATTR_NSCP_ENTRYDN, 0);
+ }
+
+ /* And copy the reason from e */
+ reason = slapi_entry_attr_get_charptr(e, "nsds5ReplConflict");
+ if (reason) {
+ if (!slapi_entry_attr_hasvalue(addingentry->ep_entry, "nsds5ReplConflict", reason)) {
+ slapi_entry_add_string(addingentry->ep_entry, "nsds5ReplConflict", reason);
+ LDAPDebug(LDAP_DEBUG_REPL, "Resurrection of %s - Added Conflict reason %s\n", dn, reason, 0);
+ }
+ slapi_ch_free((void **)&reason);
+ }
+ /* Clear the Tombstone Flag in the entry */
+ slapi_entry_clear_flag(addingentry->ep_entry, SLAPI_ENTRY_FLAG_TOMBSTONE);
+
+ /* make sure the objectclass
+ - does not contain any duplicate values
+ - has CSNs for the new values we added
+ */
+ {
+ Slapi_Attr *sa = NULL;
+ Slapi_Value sv;
+ const struct berval *svbv = NULL;
+
+ /* add the extensibleobject objectclass with csn if not present */
+ slapi_entry_attr_find(addingentry->ep_entry, SLAPI_ATTR_OBJECTCLASS, &sa);
+ slapi_value_init_string(&sv, "extensibleobject");
+ svbv = slapi_value_get_berval(&sv);
+ if (slapi_attr_value_find(sa, svbv)) { /* not found, so add it */
+ if (opcsn) {
+ value_update_csn(&sv, CSN_TYPE_VALUE_UPDATED, opcsn);
+ }
+ slapi_attr_add_value(sa, &sv);
+ }
+ value_done(&sv);
+
+ /* add the glue objectclass with csn if not present */
+ slapi_value_init_string(&sv, "glue");
+ svbv = slapi_value_get_berval(&sv);
+ if (slapi_attr_value_find(sa, svbv)) { /* not found, so add it */
+ if (opcsn) {
+ value_update_csn(&sv, CSN_TYPE_VALUE_UPDATED, opcsn);
+ }
+ slapi_attr_add_value(sa, &sv);
+ }
+ value_done(&sv);
+ }
+ }
+ else
+ {
+ /*
+ * Try to add the entry to the cache, assign it a new entryid
+ * and mark it locked. This should only fail if the entry
+ * already exists.
+ */
+ /*
+ * next_id will add this id to the list of ids that are pending
+ * id2entry indexing.
+ */
+ addingentry = backentry_init( e );
+ if ( ( addingentry->ep_id = next_id( be ) ) >= MAXID ) {
+ LDAPDebug( LDAP_DEBUG_ANY,
+ "add: maximum ID reached, cannot add entry to "
+ "backend '%s'", be->be_name, 0, 0 );
+ ldap_result_code = LDAP_OPERATIONS_ERROR;
+ goto error_return;
+ }
+ addingentry_id_assigned= 1;
+
+ if (!is_fixup_operation)
+ {
+ if ( opcsn == NULL && operation->o_csngen_handler )
+ {
+ /*
+ * Current op is a user request. Opcsn will be assigned
+ * if the dn is in an updatable replica.
+ */
+ opcsn = entry_assign_operation_csn ( pb, e, parententry ? parententry->ep_entry : NULL );
+ }
+ if ( opcsn != NULL )
+ {
+ entry_set_csn (e, opcsn);
+ entry_add_dncsn (e, opcsn);
+ entry_add_rdn_csn (e, opcsn);
+ entry_set_maxcsn (e, opcsn);
+ }
+ }
+
+ if (is_tombstone_operation)
+ {
+ /* Directly add the entry as a tombstone */
+ /*
+ * 1) If the entry has an existing DN, change it to be
+ * "nsuniqueid=<uniqueid>, <old dn>"
+ * 2) Add the objectclass value "tombstone" and arrange for only
+ * that value to be indexed.
+ * 3) If the parent entry was found, set the nsparentuniqueid
+ * attribute to be the unique id of that parent.
+ */
+ char *untombstoned_dn = slapi_entry_get_dn(e);
+ if (NULL == untombstoned_dn)
+ {
+ untombstoned_dn = "";
+ }
+ slapi_entry_set_dn(addingentry->ep_entry, compute_entry_tombstone_dn(untombstoned_dn, addr.uniqueid));
+ /* Work around pb with slapi_entry_add_string (defect 522327) doesn't check duplicate values */
+ if (!slapi_entry_attr_hasvalue(addingentry->ep_entry, SLAPI_ATTR_OBJECTCLASS, SLAPI_ATTR_VALUE_TOMBSTONE)) {
+ slapi_entry_add_string(addingentry->ep_entry, SLAPI_ATTR_OBJECTCLASS, SLAPI_ATTR_VALUE_TOMBSTONE);
+ slapi_entry_set_flag(addingentry->ep_entry, SLAPI_ENTRY_FLAG_TOMBSTONE);
+ }
+ if (NULL != operation->o_params.p.p_add.parentuniqueid)
+ {
+ slapi_entry_add_string(addingentry->ep_entry, SLAPI_ATTR_VALUE_PARENT_UNIQUEID, operation->o_params.p.p_add.parentuniqueid);
+ }
+ }
+ if ( cache_add_tentative( &inst->inst_cache, addingentry, NULL )!= 0 )
+ {
+ LDAPDebug( LDAP_DEBUG_CACHE, "cache_add_tentative concurrency detected\n", 0, 0, 0 );
+ ldap_result_code= LDAP_ALREADY_EXISTS;
+ goto error_return;
+ }
+ addingentry_in_cache= 1;
+ }
+
+ /*
+ * Get the parent dn and see if the corresponding entry exists.
+ * If the parent does not exist, only allow the "root" user to
+ * add the entry.
+ */
+ if ( !slapi_sdn_isempty(&parentsdn) )
+ {
+ /* This is getting the parent */
+ if (NULL == parententry)
+ {
+ /* Here means that we didn't find the parent */
+ int err = 0;
+ Slapi_DN ancestordn= {0};
+ struct backentry *ancestorentry;
+
+ LDAPDebug( LDAP_DEBUG_TRACE,
+ "parent does not exist, pdn = %s\n",
+ slapi_sdn_get_dn(&parentsdn), 0, 0 );
+
+ ancestorentry = dn2ancestor(be, &parentsdn, &ancestordn, &txn, &err );
+ cache_return( &inst->inst_cache, &ancestorentry );
+
+ ldap_result_code= LDAP_NO_SUCH_OBJECT;
+ ldap_result_matcheddn= slapi_ch_strdup((char *)slapi_sdn_get_dn(&ancestordn)); /* jcm - cast away const. */
+ slapi_sdn_done(&ancestordn);
+ goto error_return;
+ }
+ ldap_result_code = plugin_call_acl_plugin (pb, e, NULL, NULL, SLAPI_ACL_ADD,
+ ACLPLUGIN_ACCESS_DEFAULT, &errbuf );
+ if ( ldap_result_code != LDAP_SUCCESS )
+ {
+ LDAPDebug( LDAP_DEBUG_TRACE, "no access to parent\n", 0, 0, 0 );
+ ldap_result_message= errbuf;
+ goto error_return;
+ }
+ pid = parententry->ep_id;
+ }
+ else
+ { /* no parent */
+ if ( !isroot && !is_replicated_operation)
+ {
+ LDAPDebug( LDAP_DEBUG_TRACE, "no parent & not root\n",
+ 0, 0, 0 );
+ ldap_result_code= LDAP_INSUFFICIENT_ACCESS;
+ goto error_return;
+ }
+ parententry = NULL;
+ pid = 0;
+ }
+
+ if(is_resurect_operation)
+ {
+ /*
+ * add the entrydn operational attributes
+ */
+ add_update_entrydn_operational_attributes(addingentry);
+ }
+ else if (is_tombstone_operation)
+ {
+ /* Remove the entrydn operational attributes */
+ delete_update_entry_dn_operational_attributes(addingentry);
+ }
+ else
+ {
+ /*
+ * add the parentid, entryid and entrydn operational attributes
+ */
+ add_update_entry_operational_attributes(addingentry, pid);
+ }
+
+ /*
+ * Before we add the entry, find out if the syntax of the aci
+ * aci attribute values are correct or not. We don't want to
+ * the entry if the syntax is incorrect.
+ */
+ if ( plugin_call_acl_verify_syntax (pb, addingentry->ep_entry, &errbuf) != 0 ) {
+ LDAPDebug( LDAP_DEBUG_TRACE, "ACL syntax error\n", 0,0,0);
+ ldap_result_code= LDAP_INVALID_SYNTAX;
+ ldap_result_message= errbuf;
+ goto error_return;
+ }
+
+ /* Having decided that we're really going to do the operation, let's modify
+ the in-memory state of the parent to reflect the new child (update
+ subordinate count specifically */
+ if (NULL != parententry)
+ {
+ retval = parent_update_on_childchange(&parent_modify_c,1,NULL); /* 1==add */\
+ /* The modify context now contains info needed later */
+ if (0 != retval) {
+ ldap_result_code= LDAP_OPERATIONS_ERROR;
+ goto error_return;
+ }
+ parent_found = 1;
+ parententry = NULL;
+ }
+ /*
+ * So, we believe that no code up till here actually added anything
+ * to persistent store. From now on, we're transacted
+ */
+
+ for (retry_count = 0; retry_count < RETRY_TIMES; retry_count++) {
+ if (retry_count > 0) {
+ dblayer_txn_abort(li,&txn);
+ /* We're re-trying */
+ LDAPDebug( LDAP_DEBUG_TRACE, "Add Retrying Transaction\n", 0, 0, 0 );
+#ifndef LDBM_NO_BACKOFF_DELAY
+ {
+ PRIntervalTime interval;
+ interval = PR_MillisecondsToInterval(slapi_rand() % 100);
+ DS_Sleep(interval);
+ }
+#endif
+ }
+ retval = dblayer_txn_begin(li,parent_txn,&txn);
+ if (0 != retval) {
+ if (LDBM_OS_ERR_IS_DISKFULL(retval)) {
+ disk_full = 1;
+ ldap_result_code= LDAP_OPERATIONS_ERROR;
+ goto diskfull_return;
+ }
+ ldap_result_code= LDAP_OPERATIONS_ERROR;
+ goto error_return;
+ }
+ retval = id2entry_add( be, addingentry, &txn );
+ if (DB_LOCK_DEADLOCK == retval)
+ {
+ LDAPDebug( LDAP_DEBUG_ARGS, "add 1 DEADLOCK\n", 0, 0, 0 );
+ /* Retry txn */
+ continue;
+ }
+ if (retval != 0) {
+ LDAPDebug( LDAP_DEBUG_TRACE, "id2entry_add failed, err=%d %s\n",
+ retval, (msg = dblayer_strerror( retval )) ? msg : "", 0 );
+ if (LDBM_OS_ERR_IS_DISKFULL(retval)) {
+ disk_full = 1;
+ ldap_result_code= LDAP_OPERATIONS_ERROR;
+ goto diskfull_return;
+ }
+ ldap_result_code= LDAP_OPERATIONS_ERROR;
+ goto error_return;
+ }
+ if(is_resurect_operation)
+ {
+ retval = index_addordel_string(be,SLAPI_ATTR_OBJECTCLASS,SLAPI_ATTR_VALUE_TOMBSTONE,addingentry->ep_id,BE_INDEX_DEL,&txn);
+ if (DB_LOCK_DEADLOCK == retval) {
+ LDAPDebug( LDAP_DEBUG_ARGS, "add 2 DB_LOCK_DEADLOCK\n", 0, 0, 0 );
+ /* Retry txn */
+ continue;
+ }
+ if (0 != retval) {
+ LDAPDebug( LDAP_DEBUG_TRACE, "add 1 BAD, err=%d %s\n",
+ retval, (msg = dblayer_strerror( retval )) ? msg : "", 0 );
+ if (LDBM_OS_ERR_IS_DISKFULL(retval)) {
+ disk_full = 1;
+ ldap_result_code= LDAP_OPERATIONS_ERROR;
+ goto diskfull_return;
+ }
+ ldap_result_code= LDAP_OPERATIONS_ERROR;
+ goto error_return;
+ }
+ retval = index_addordel_string(be,SLAPI_ATTR_UNIQUEID,slapi_entry_get_uniqueid(addingentry->ep_entry),addingentry->ep_id,BE_INDEX_DEL,&txn);
+ if (DB_LOCK_DEADLOCK == retval) {
+ LDAPDebug( LDAP_DEBUG_ARGS, "add 3 DB_LOCK_DEADLOCK\n", 0, 0, 0 );
+ /* Retry txn */
+ continue;
+ }
+ if (0 != retval) {
+ LDAPDebug( LDAP_DEBUG_TRACE, "add 2 BAD, err=%d %s\n",
+ retval, (msg = dblayer_strerror( retval )) ? msg : "", 0 );
+ if (LDBM_OS_ERR_IS_DISKFULL(retval)) {
+ disk_full = 1;
+ ldap_result_code= LDAP_OPERATIONS_ERROR;
+ goto diskfull_return;
+ }
+ ldap_result_code= LDAP_OPERATIONS_ERROR;
+ goto error_return;
+ }
+ retval = index_addordel_string(be,SLAPI_ATTR_NSCP_ENTRYDN,slapi_sdn_get_ndn(&sdn),addingentry->ep_id,BE_INDEX_DEL,&txn);
+ if (DB_LOCK_DEADLOCK == retval) {
+ LDAPDebug( LDAP_DEBUG_ARGS, "add 4 DB_LOCK_DEADLOCK\n", 0, 0, 0 );
+ /* Retry txn */
+ continue;
+ }
+ if (0 != retval) {
+ LDAPDebug( LDAP_DEBUG_TRACE, "add 3 BAD, err=%d %s\n",
+ retval, (msg = dblayer_strerror( retval )) ? msg : "", 0 );
+ if (LDBM_OS_ERR_IS_DISKFULL(retval)) {
+ disk_full = 1;
+ ldap_result_code= LDAP_OPERATIONS_ERROR;
+ goto diskfull_return;
+ }
+ ldap_result_code= LDAP_OPERATIONS_ERROR;
+ goto error_return;
+ }
+ }
+ if (is_tombstone_operation)
+ {
+ retval = index_addordel_entry( be, addingentry, BE_INDEX_ADD | BE_INDEX_TOMBSTONE, &txn );
+ }
+ else
+ {
+ retval = index_addordel_entry( be, addingentry, BE_INDEX_ADD, &txn );
+ }
+ if (DB_LOCK_DEADLOCK == retval)
+ {
+ LDAPDebug( LDAP_DEBUG_ARGS, "add 5 DEADLOCK\n", 0, 0, 0 );
+ /* retry txn */
+ continue;
+ }
+ if (retval != 0) {
+ LDAPDebug( LDAP_DEBUG_ANY, "add: attempt to index %lu failed\n",
+ (u_long)addingentry->ep_id, 0, 0 );
+ if (LDBM_OS_ERR_IS_DISKFULL(retval)) {
+ disk_full = 1;
+ ldap_result_code= LDAP_OPERATIONS_ERROR;
+ goto diskfull_return;
+ }
+ ldap_result_code= LDAP_OPERATIONS_ERROR;
+ goto error_return;
+ }
+ if (parent_found) {
+ /* Push out the db modifications from the parent entry */
+ retval = modify_update_all(be,pb,&parent_modify_c,&txn);
+ if (DB_LOCK_DEADLOCK == retval)
+ {
+ LDAPDebug( LDAP_DEBUG_ARGS, "add 4 DEADLOCK\n", 0, 0, 0 );
+ /* Retry txn */
+ continue;
+ }
+ if (0 != retval) {
+ LDAPDebug( LDAP_DEBUG_TRACE, "add 1 BAD, err=%d %s\n",
+ retval, (msg = dblayer_strerror( retval )) ? msg : "", 0 );
+ if (LDBM_OS_ERR_IS_DISKFULL(retval)) {
+ disk_full = 1;
+ ldap_result_code= LDAP_OPERATIONS_ERROR;
+ goto diskfull_return;
+ }
+ ldap_result_code= LDAP_OPERATIONS_ERROR;
+ goto error_return;
+ }
+ }
+ /*
+ * Update the Virtual List View indexes
+ */
+ retval= vlv_update_all_indexes(&txn, be, pb, NULL, addingentry);
+ if (DB_LOCK_DEADLOCK == retval)
+ {
+ LDAPDebug( LDAP_DEBUG_ARGS, "add DEADLOCK vlv_update_index\n", 0, 0, 0 );
+ /* Retry txn */
+ continue;
+ }
+ if (0 != retval) {
+ LDAPDebug( LDAP_DEBUG_TRACE, "vlv_update_index failed, err=%d %s\n",
+ retval, (msg = dblayer_strerror( retval )) ? msg : "", 0 );
+ if (LDBM_OS_ERR_IS_DISKFULL(retval)) {
+ disk_full = 1;
+ ldap_result_code= LDAP_OPERATIONS_ERROR;
+ goto diskfull_return;
+ }
+ ldap_result_code= LDAP_OPERATIONS_ERROR;
+ goto error_return;
+ }
+ if (retval == 0 ) {
+ break;
+ }
+
+ }
+ if (retry_count == RETRY_TIMES) {
+ /* Failed */
+ LDAPDebug( LDAP_DEBUG_ANY, "Retry count exceeded in add\n", 0, 0, 0 );
+ ldap_result_code= LDAP_OPERATIONS_ERROR;
+ goto error_return;
+ }
+
+ /*
+ * At this point, everything's cool, and the only thing which
+ * can go wrong is a transaction commit failure.
+ */
+ slapi_pblock_set( pb, SLAPI_ENTRY_PRE_OP, NULL );
+ slapi_pblock_set( pb, SLAPI_ENTRY_POST_OP, slapi_entry_dup( addingentry->ep_entry ));
+
+ if(is_resurect_operation)
+ {
+ /*
+ * We can now switch the tombstone entry with the real entry.
+ */
+ if (cache_replace( &inst->inst_cache, tombstoneentry, addingentry ) != 0 )
+ {
+ /* This happens if the dn of addingentry already exists */
+ ldap_result_code= LDAP_ALREADY_EXISTS;
+ cache_unlock_entry( &inst->inst_cache, tombstoneentry );
+ goto error_return;
+ }
+ /*
+ * The tombstone was locked down in the cache... we can
+ * get rid of the entry in the cache now.
+ */
+ cache_unlock_entry( &inst->inst_cache, tombstoneentry );
+ cache_return( &inst->inst_cache, &tombstoneentry );
+ tombstone_in_cache = 0; /* deleted */
+ }
+ if (parent_found)
+ {
+ /* switch the parent entry copy into play */
+ modify_switch_entries( &parent_modify_c,be);
+ }
+
+ retval = dblayer_txn_commit(li,&txn);
+ if (0 != retval)
+ {
+ if (LDBM_OS_ERR_IS_DISKFULL(retval)) {
+ disk_full = 1;
+ ldap_result_code= LDAP_OPERATIONS_ERROR;
+ goto diskfull_return;
+ }
+ ldap_result_code= LDAP_OPERATIONS_ERROR;
+ goto error_return;
+ }
+
+ rc= 0;
+ goto common_return;
+
+error_return:
+ if ( addingentry_id_assigned )
+ {
+ next_id_return( be, addingentry->ep_id );
+ }
+ if ( NULL != addingentry )
+ {
+ if ( addingentry_in_cache )
+ {
+ cache_remove(&inst->inst_cache, addingentry);
+ }
+ backentry_clear_entry(addingentry); /* e is released in the frontend */
+ backentry_free( &addingentry ); /* release the backend wrapper, here */
+ }
+ if(tombstone_in_cache)
+ {
+ cache_return(&inst->inst_cache, &tombstoneentry);
+ }
+
+ if (rc == DB_RUNRECOVERY) {
+ dblayer_remember_disk_filled(li);
+ ldbm_nasty("Add",80,rc);
+ disk_full = 1;
+ }
+
+ /* It is specifically OK to make this call even when no transaction was in progress */
+ dblayer_txn_abort(li,&txn); /* abort crashes in case disk full */
+diskfull_return:
+
+ if (disk_full)
+ rc= return_on_disk_full(li);
+ else
+ rc= SLAPI_FAIL_GENERAL;
+
+common_return:
+
+ if (addingentry_in_cache)
+ {
+ cache_return( &inst->inst_cache, &addingentry );
+ }
+ /* JCMREPL - The bepostop is called even if the operation fails. */
+ plugin_call_plugins (pb, SLAPI_PLUGIN_BE_POST_ADD_FN);
+
+ modify_term(&parent_modify_c,be);
+ done_with_pblock_entry(pb,SLAPI_ADD_EXISTING_DN_ENTRY);
+ done_with_pblock_entry(pb,SLAPI_ADD_EXISTING_UNIQUEID_ENTRY);
+ done_with_pblock_entry(pb,SLAPI_ADD_PARENT_ENTRY);
+ if(dblock_acquired)
+ {
+ dblayer_unlock_backend(be);
+ }
+ if(ldap_result_code!=-1)
+ {
+ slapi_send_ldap_result( pb, ldap_result_code, ldap_result_matcheddn, ldap_result_message, 0, NULL );
+ }
+ slapi_sdn_done(&sdn);
+ slapi_sdn_done(&parentsdn);
+ slapi_ch_free( (void**)&ldap_result_matcheddn );
+ slapi_ch_free( (void**)&errbuf );
+ return rc;
+}
+
+/*
+ * add the parentid, entryid and entrydn, operational attributes.
+ *
+ * Note: This is called from the ldif2ldbm code.
+ */
+void
+add_update_entry_operational_attributes(struct backentry *ep, ID pid)
+{
+ struct berval bv;
+ struct berval *bvp[2];
+ char buf[40]; /* Enough for an EntryID */
+
+ bvp[0] = &bv;
+ bvp[1] = NULL;
+
+ /* parentid */
+ /* If the pid is 0, then the entry does not have a parent. It
+ * may be the case that the entry is a suffix. In any case,
+ * the parentid attribute should only be added if the entry
+ * has a parent. */
+ if (pid != 0) {
+ sprintf( buf, "%lu", (u_long)pid );
+ bv.bv_val = buf;
+ bv.bv_len = strlen( buf );
+ entry_replace_values( ep->ep_entry, "parentid", bvp );
+ }
+
+ /* entryid */
+ sprintf( buf, "%lu", (u_long)ep->ep_id );
+ bv.bv_val = buf;
+ bv.bv_len = strlen( buf );
+ entry_replace_values( ep->ep_entry, "entryid", bvp );
+
+ /* entrydn */
+ add_update_entrydn_operational_attributes(ep);
+}
+
+/*
+ * add the entrydn operational attribute.
+ */
+void
+add_update_entrydn_operational_attributes(struct backentry *ep)
+{
+ struct berval bv;
+ struct berval *bvp[2];
+
+ /* entrydn */
+ bvp[0] = &bv;
+ bvp[1] = NULL;
+ bv.bv_val = (void*)backentry_get_ndn(ep);
+ bv.bv_len = strlen( bv.bv_val );
+ entry_replace_values( ep->ep_entry, "entrydn", bvp );
+}
+
+/*
+ * delete the entrydn operational attributes
+ */
+static void
+delete_update_entry_dn_operational_attributes(struct backentry *ep)
+{
+ slapi_entry_attr_delete( ep->ep_entry, "entrydn");
+}
+
diff --git a/ldap/servers/slapd/back-ldbm/ldbm_attr.c b/ldap/servers/slapd/back-ldbm/ldbm_attr.c
new file mode 100644
index 00000000..246a8d7d
--- /dev/null
+++ b/ldap/servers/slapd/back-ldbm/ldbm_attr.c
@@ -0,0 +1,635 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+/* attr.c - backend routines for dealing with attributes */
+
+#include "back-ldbm.h"
+
+extern char **str2charray();
+
+struct attrinfo *
+attrinfo_new()
+{
+ struct attrinfo *p= (struct attrinfo *)slapi_ch_calloc(1, sizeof(struct attrinfo));
+ p->ai_type= 0;
+ p->ai_indexmask= 0;
+ p->ai_plugin= NULL;
+ p->ai_index_rules= NULL;
+ p->ai_dblayer= NULL;
+ p->ai_dblayer_count = 0;
+ p->ai_idl= NULL;
+ return p;
+}
+
+void
+attrinfo_delete(struct attrinfo **pp)
+{
+ if(pp!=NULL && *pp!=NULL)
+ {
+ idl_release_private(*pp);
+ slapi_ch_free((void**)&((*pp)->ai_type));
+ slapi_ch_free((void**)(*pp)->ai_index_rules);
+ slapi_ch_free((void**)pp);
+ *pp= NULL;
+ }
+}
+
+static int
+attrinfo_internal_delete( caddr_t data, caddr_t arg )
+{
+ struct attrinfo *n = (struct attrinfo *)data;
+ attrinfo_delete(&n);
+ return 0;
+}
+
+void
+attrinfo_deletetree(ldbm_instance *inst)
+{
+ avl_free( inst->inst_attrs, attrinfo_internal_delete );
+}
+
+
+static int
+ainfo_type_cmp(
+ char *type,
+ struct attrinfo *a
+)
+{
+ return( strcasecmp( type, a->ai_type ) );
+}
+
+static int
+ainfo_cmp(
+ struct attrinfo *a,
+ struct attrinfo *b
+)
+{
+ return( strcasecmp( a->ai_type, b->ai_type ) );
+}
+
+/*
+ * Called when a duplicate "index" line is encountered.
+ *
+ * returns 1 => original from init code, indexmask updated
+ * 2 => original not from init code, warn the user
+ *
+ * Hard coded to return a 1 always...
+ *
+ */
+
+static int
+ainfo_dup(
+ struct attrinfo *a,
+ struct attrinfo *b
+)
+{
+ /* merge duplicate indexing information */
+ if (b->ai_indexmask == 0 || b->ai_indexmask == INDEX_OFFLINE) {
+ a->ai_indexmask = INDEX_OFFLINE; /* turns off all indexes */
+ charray_free ( a->ai_index_rules );
+ a->ai_index_rules = NULL;
+ }
+ a->ai_indexmask |= b->ai_indexmask;
+ if ( b->ai_indexmask & INDEX_RULES ) {
+ charray_merge( &a->ai_index_rules, b->ai_index_rules, 1 );
+ }
+
+ return( 1 );
+}
+
+void
+ainfo_get(
+ backend *be,
+ char *type,
+ struct attrinfo **at
+)
+{
+ ldbm_instance *inst = (ldbm_instance *) be->be_instance_info;
+ if ( (*at = (struct attrinfo *) avl_find( inst->inst_attrs, type,
+ ainfo_type_cmp )) == NULL ) {
+ if ( (*at = (struct attrinfo *) avl_find( inst->inst_attrs,
+ LDBM_PSEUDO_ATTR_DEFAULT, ainfo_type_cmp )) == NULL ) {
+ return;
+ }
+ }
+}
+
+void
+attr_index_config(
+ backend *be,
+ char *fname,
+ int lineno,
+ int argc,
+ char **argv,
+ int init
+)
+{
+ ldbm_instance *inst = (ldbm_instance *) be->be_instance_info;
+ int i, j;
+ char **attrs;
+ char **indexes = NULL;
+ char **index_rules = NULL;
+ struct attrinfo *a;
+ int return_value = -1;
+
+ attrs = str2charray( argv[0], "," );
+ if ( argc > 1 ) {
+ indexes = str2charray( argv[1], "," );
+ if ( argc > 2 ) {
+ index_rules = str2charray( argv[2], "," );
+ }
+ }
+ for ( i = 0; attrs[i] != NULL; i++ ) {
+ a = attrinfo_new();
+ a->ai_type = slapi_attr_basetype( attrs[i], NULL, 0 );
+ slapi_attr_type2plugin( a->ai_type, &a->ai_plugin );
+ if ( argc == 1 ) {
+ a->ai_indexmask = (INDEX_PRESENCE | INDEX_EQUALITY |
+ INDEX_APPROX | INDEX_SUB);
+ } else {
+ a->ai_indexmask = 0;
+ for ( j = 0; indexes[j] != NULL; j++ ) {
+ if ( strncasecmp( indexes[j], "pres", 4 )
+ == 0 ) {
+ a->ai_indexmask |= INDEX_PRESENCE;
+ } else if ( strncasecmp( indexes[j], "eq", 2 )
+ == 0 ) {
+ a->ai_indexmask |= INDEX_EQUALITY;
+ } else if ( strncasecmp( indexes[j], "approx",
+ 6 ) == 0 ) {
+ a->ai_indexmask |= INDEX_APPROX;
+ } else if ( strncasecmp( indexes[j], "sub", 3 )
+ == 0 ) {
+ a->ai_indexmask |= INDEX_SUB;
+ } else if ( strncasecmp( indexes[j], "none", 4 )
+ == 0 ) {
+ if ( a->ai_indexmask != 0 ) {
+ LDAPDebug(LDAP_DEBUG_ANY,
+ "%s: line %d: index type \"none\" cannot be combined with other types\n",
+ fname, lineno, 0);
+ }
+ a->ai_indexmask = INDEX_OFFLINE; /* note that the index isn't available */
+ } else {
+ LDAPDebug(LDAP_DEBUG_ANY,
+ "%s: line %d: unknown index type \"%s\" (ignored)\n",
+ fname, lineno, indexes[j]);
+ LDAPDebug(LDAP_DEBUG_ANY,
+ "valid index types are \"pres\", \"eq\", \"approx\", or \"sub\"\n",
+ 0, 0, 0);
+ }
+ }
+
+ /* compute a->ai_index_rules: */
+ j = 0;
+ if (index_rules != NULL) for (; index_rules[j] != NULL; ++j);
+ if (j > 0) { /* there are some candidates */
+ char** official_rules = (char**)
+ slapi_ch_malloc ((j + 1) * sizeof (char*));
+ size_t k = 0;
+ for (j = 0; index_rules[j] != NULL; ++j) {
+ /* Check that index_rules[j] is an official OID */
+ char* officialOID = NULL;
+ IFP mrINDEX = NULL;
+ Slapi_PBlock* pb = slapi_pblock_new();
+ if (!slapi_pblock_set (pb, SLAPI_PLUGIN_MR_OID, index_rules[j]) &&
+ !slapi_pblock_set (pb, SLAPI_PLUGIN_MR_TYPE, a->ai_type) &&
+ !slapi_mr_indexer_create (pb) &&
+ !slapi_pblock_get (pb, SLAPI_PLUGIN_MR_INDEX_FN, &mrINDEX) &&
+ mrINDEX != NULL &&
+ !slapi_pblock_get (pb, SLAPI_PLUGIN_MR_OID, &officialOID) &&
+ officialOID != NULL) {
+ if (!strcasecmp (index_rules[j], officialOID)) {
+ official_rules[k++] = slapi_ch_strdup (officialOID);
+ } else {
+ char* preamble = slapi_ch_malloc (strlen (fname) + 30);
+ sprintf (preamble, "%s: line %d", fname, lineno);
+ LDAPDebug (LDAP_DEBUG_ANY, "%s: use \"%s\" instead of \"%s\" (ignored)\n",
+ preamble, officialOID, index_rules[j] );
+ slapi_ch_free((void**)&preamble);
+ }
+ } else {
+ LDAPDebug (LDAP_DEBUG_ANY, "%s: line %d: "
+ "unknown index rule \"%s\" (ignored)\n",
+ fname, lineno, index_rules[j] );
+ }
+ {/* It would improve speed to save the indexer, for future use.
+ But, for simplicity, we destroy it now: */
+ IFP mrDESTROY = NULL;
+ if (!slapi_pblock_get (pb, SLAPI_PLUGIN_DESTROY_FN, &mrDESTROY) &&
+ mrDESTROY != NULL) {
+ mrDESTROY (pb);
+ }
+ }
+ slapi_pblock_destroy (pb);
+ }
+ official_rules[k] = NULL;
+ if (k > 0) {
+ a->ai_index_rules = official_rules;
+ a->ai_indexmask |= INDEX_RULES;
+ } else {
+ slapi_ch_free((void**)&official_rules);
+ }
+ }
+ }
+#if 0 /* seems to not matter -- INDEX_FROMINIT is checked nowhere else */
+ if ( init ) {
+ a->ai_indexmask |= INDEX_FROMINIT;
+ a->ai_indexmask &= ~INDEX_OFFLINE;
+ }
+#endif
+
+ /* initialize the IDL code's private data */
+ return_value = idl_init_private(be, a);
+ if (0 != return_value) {
+ /* fatal error, exit */
+ LDAPDebug(LDAP_DEBUG_ANY,"%s: line %d:Fatal Error: Failed to initialize attribute structure\n",
+ fname, lineno, 0);
+ exit( 1 );
+ }
+
+ if ( avl_insert( &inst->inst_attrs, a, ainfo_cmp, ainfo_dup ) != 0 ) {
+ /* duplicate - existing version updated */
+ attrinfo_delete(&a);
+ }
+ }
+ charray_free( attrs );
+ if ( indexes != NULL ) {
+ charray_free( indexes );
+ }
+ if ( index_rules != NULL ) {
+ charray_free( index_rules );
+ }
+}
+
+/*
+ * Function that creates a new attrinfo structure and
+ * inserts it into the avl tree. This is used by code
+ * that wants to store attribute-level configuration data
+ * e.g. attribute encryption, but where the attr_info
+ * structure doesn't exist because the attribute in question
+ * is not indexed.
+ */
+void
+attr_create_empty(backend *be,char *type,struct attrinfo **ai)
+{
+ ldbm_instance *inst = (ldbm_instance *) be->be_instance_info;
+ struct attrinfo *a = attrinfo_new();
+ a->ai_type = slapi_ch_strdup(type);
+ if ( avl_insert( &inst->inst_attrs, a, ainfo_cmp, ainfo_dup ) != 0 ) {
+ /* duplicate - existing version updated */
+ attrinfo_delete(&a);
+ ainfo_get(be,type,&a);
+ }
+ *ai = a;
+}
+
+/* Code for computed attributes */
+extern char* hassubordinates;
+extern char* numsubordinates;
+
+static int
+ldbm_compute_evaluator(computed_attr_context *c,char* type,Slapi_Entry *e,slapi_compute_output_t outputfn)
+{
+ int rc = 0;
+
+ if ( strcasecmp (type, numsubordinates ) == 0)
+ {
+ Slapi_Attr *read_attr = NULL;
+ /* Check to see whether this attribute is already present in the entry */
+ if (0 != slapi_entry_attr_find( e, numsubordinates, &read_attr ))
+ {
+ /* If not, we return it as zero */
+ Slapi_Attr our_attr;
+ slapi_attr_init(&our_attr, numsubordinates);
+ our_attr.a_flags = SLAPI_ATTR_FLAG_OPATTR;
+ valueset_add_string(&our_attr.a_present_values,"0",CSN_TYPE_UNKNOWN,NULL);
+ rc = (*outputfn) (c, &our_attr, e);
+ attr_done(&our_attr);
+ return (rc);
+ }
+ }
+ if ( strcasecmp (type, hassubordinates ) == 0)
+ {
+ Slapi_Attr *read_attr = NULL;
+ Slapi_Attr our_attr;
+ slapi_attr_init(&our_attr, hassubordinates);
+ our_attr.a_flags = SLAPI_ATTR_FLAG_OPATTR;
+ /* This attribute is always computed */
+ /* Check to see whether the subordinate count attribute is already present in the entry */
+ rc = slapi_entry_attr_find( e, numsubordinates, &read_attr );
+ if ( (0 != rc) || slapi_entry_attr_hasvalue(e,numsubordinates,"0") ) {
+ /* If not, or present and zero, we return FALSE, otherwise TRUE */
+ valueset_add_string(&our_attr.a_present_values,"FALSE",CSN_TYPE_UNKNOWN,NULL);
+ } else {
+ valueset_add_string(&our_attr.a_present_values,"TRUE",CSN_TYPE_UNKNOWN,NULL);
+ }
+ rc = (*outputfn) (c, &our_attr, e);
+ attr_done(&our_attr);
+ return (rc);
+ }
+
+ return -1; /* I see no ships */
+}
+
+/*
+ * string_find(): case sensitive search for the substring str2 within str1.
+ */
+static
+char * string_find (
+ const char * str1,
+ const char * str2
+ )
+{
+ char *cp = (char *) str1;
+ char *s1, *s2;
+
+ if ( !*str2 )
+ return((char *)str1);
+
+ while (*cp)
+ {
+ s1 = cp;
+ s2 = (char *) str2;
+
+ while ( *s1 && *s2 && !(*s1-*s2) )
+ s1++, s2++;
+
+ if (!*s2)
+ return(cp);
+
+ cp++;
+ }
+
+ return(NULL);
+
+}
+
+/* What are we doing ?
+ The back-end can't search properly for the hasSubordinates and
+ numSubordinates attributes. The reason being that they're not
+ always stored on entries, so filter test fails to do the correct thing.
+ However, it is possible to rewrite a given search to one
+ which will work, given that numSubordinates is present when non-zero,
+ and we maintain a presence index for numSubordinates.
+ */
+/* Searches we rewrite here :
+ substrings of the form
+ (hassubordinates=TRUE) to (&(numsubordinates=*)(numsubordinates>=1)) [indexed]
+ (hassubordinates=FALSE) to (&(objectclass=*)(!(numsubordinates=*))) [not indexed]
+ (hassubordinates=*) to (objectclass=*) [not indexed]
+ (numsubordinates=*) to (objectclass=*) [not indexed]
+ (numsubordinates=x) to (&(numsubordinates=*)(numsubordinates=x)) [indexed]
+ (numsubordinates>=x) to (&(numsubordinates=*)(numsubordinates>=x)) [indexed where X > 0]
+ (numsubordinates<=x) to (&(numsubordinates=*)(numsubordinates<=x)) [indexed]
+
+ anything else involving numsubordinates and hassubordinates we flag as unwilling to perform
+
+*/
+
+/* Before calling this function, you must free all the parts
+ which will be overwritten, this function dosn't know
+ how to do that */
+static int replace_filter(Slapi_Filter *f, char *s)
+{
+ Slapi_Filter *newf = NULL;
+ Slapi_Filter *temp = NULL;
+/* LP: Fix for defect 515161. Crash on AIX
+ * slapi_str2filter is a nasty function that mangle whatever gets passed in.
+ * AIX crashes on altering the literal string.
+ * So we need to allocate the string and then free it.
+ */
+ char *buf = slapi_ch_strdup(s);
+
+ newf = slapi_str2filter(buf);
+ slapi_ch_free((void **)&buf);
+
+ if (NULL == newf) {
+ return -1;
+ }
+
+ /* Now take the parts of newf and put them in f */
+ /* An easy way to do this is to preserve the "next" ptr */
+ temp = f->f_next;
+ *f = *newf;
+ f->f_next = temp;
+ /* Free the new filter husk */
+ slapi_ch_free((void**)&newf);
+ return 0;
+}
+
+static void find_our_friends(char *s, int *has, int *num)
+{
+ *has = (0 == strcasecmp(s,"hassubordinates"));
+ if (!(*has)) {
+ *num = (0 == strcasecmp(s,"numsubordinates"));
+ }
+}
+
+/* Free the parts of a filter we're about to overwrite */
+void free_the_filter_bits(Slapi_Filter *f)
+{
+ /* We need to free: */
+ switch ( f->f_choice ) {
+ case LDAP_FILTER_EQUALITY:
+ case LDAP_FILTER_GE:
+ case LDAP_FILTER_LE:
+ case LDAP_FILTER_APPROX:
+ ava_done( &f->f_ava );
+ break;
+
+ case LDAP_FILTER_PRESENT:
+ if ( f->f_type != NULL ) {
+ slapi_ch_free( (void**)&(f->f_type) );
+ }
+ break;
+
+ default:
+ break;
+ }
+}
+
+static int grok_and_rewrite_filter(Slapi_Filter *f)
+{
+ Slapi_Filter *p = NULL;
+ int has = 0;
+ int num = 0;
+ char *rhs = NULL;
+ struct berval rhs_berval;
+
+ switch ( f->f_choice ) {
+ case LDAP_FILTER_EQUALITY:
+ /* Does this involve either of our target attributes ? */
+ find_our_friends(f->f_ava.ava_type,&has,&num);
+ if (has || num) {
+ rhs = f->f_ava.ava_value.bv_val;
+ if (has) {
+ if (0 == strcasecmp(rhs,"TRUE")) {
+ free_the_filter_bits(f);
+ replace_filter(f,"(&(numsubordinates=*)(numsubordinates>=1))");
+ } else if (0 == strcasecmp(rhs, "FALSE")) {
+ free_the_filter_bits(f);
+ replace_filter(f,"(&(objectclass=*)(!(numsubordinates=*)))");
+ } else {
+ return 1; /* Filter we can't rewrite */
+ }
+ }
+ if (num) {
+ int rhs_number = 0;
+
+ rhs_number = atoi(rhs);
+ if (rhs_number > 0) {
+
+ char * theType=f->f_ava.ava_type;
+ rhs_berval = f->f_ava.ava_value;
+ replace_filter(f,"(&(numsubordinates=*)(numsubordinates=x))");
+ /* Now fixup the resulting filter so that x = rhs */
+ slapi_ch_free((void**)&(f->f_and->f_next->f_ava.ava_value.bv_val));
+ /*free type also */
+ slapi_ch_free((void**)&theType);
+
+ f->f_and->f_next->f_ava.ava_value = rhs_berval;
+ } else {
+ if (rhs_number == 0) {
+ /* This is the same as hassubordinates=FALSE */
+ free_the_filter_bits(f);
+ replace_filter(f,"(&(objectclass=*)(!(numsubordinates=*)))");
+ } else {
+ return 1;
+ }
+ }
+ }
+ return 0;
+ }
+ break;
+
+ case LDAP_FILTER_GE:
+ find_our_friends(f->f_ava.ava_type,&has,&num);
+ if (has) {
+ return 1; /* Makes little sense for this attribute */
+ }
+ if (num) {
+ int rhs_num = 0;
+ rhs = f->f_ava.ava_value.bv_val;
+ /* is the value zero ? */
+ rhs_num = atoi(rhs);
+ if (0 == rhs) {
+ /* If so, rewrite to same as numsubordinates=* */
+ free_the_filter_bits(f);
+ replace_filter(f,"(objectclass=*)");
+ } else {
+ /* Rewrite to present and GE the rhs */
+ char * theType=f->f_ava.ava_type;
+ rhs_berval = f->f_ava.ava_value;
+
+ replace_filter(f,"(&(numsubordinates=*)(numsubordinates>=x))");
+ /* Now fixup the resulting filter so that x = rhs */
+ slapi_ch_free((void**)&(f->f_and->f_next->f_ava.ava_value.bv_val));
+ /*free type also */
+ slapi_ch_free((void**)&theType);
+
+ f->f_and->f_next->f_ava.ava_value = rhs_berval;
+ }
+ return 0;
+ }
+ break;
+
+ case LDAP_FILTER_LE:
+ find_our_friends(f->f_ava.ava_type,&has,&num);
+ if (has) {
+ return 1; /* Makes little sense for this attribute */
+ }
+ if (num) {
+ /* One could imagine doing this one, but it's quite hard */
+ return 1;
+ }
+ break;
+
+ case LDAP_FILTER_APPROX:
+ find_our_friends(f->f_ava.ava_type,&has,&num);
+ if (has || num) {
+ /* Not allowed */
+ return 1;
+ }
+ break;
+
+ case LDAP_FILTER_SUBSTRINGS:
+ find_our_friends(f->f_sub_type,&has,&num);
+ if (has || num) {
+ /* Not allowed */
+ return 1;
+ }
+ break;
+
+ case LDAP_FILTER_PRESENT:
+ find_our_friends(f->f_type,&has,&num);
+ if (has || num) {
+ /* we rewrite this search to (objectclass=*) */
+ slapi_ch_free((void**)&(f->f_type));
+ f->f_type = slapi_ch_strdup("objectclass");
+ return 0;
+ } /* We already weeded out the special search we use use in the console */
+ break;
+ case LDAP_FILTER_AND:
+ case LDAP_FILTER_OR:
+ case LDAP_FILTER_NOT:
+ for ( p = f->f_list; p != NULL; p = p->f_next ) {
+ grok_and_rewrite_filter( p );
+ }
+ break;
+
+ default:
+ return -1; /* Bad, might be an extended filter or something */
+ }
+ return -1;
+}
+
+static int
+ldbm_compute_rewriter(Slapi_PBlock *pb)
+{
+ int rc = -1;
+ char *fstr= NULL;
+
+ /*
+ * We need to look at the filter and see whether it might contain
+ * numSubordinates or hasSubordinates. We want to do a quick check
+ * before we look thoroughly.
+ */
+ slapi_pblock_get( pb, SLAPI_SEARCH_STRFILTER, &fstr );
+
+ if ( NULL != fstr ) {
+ char *lc_fstr = (char *)slapi_utf8StrToLower( (unsigned char *)fstr );
+
+ if (string_find(lc_fstr,"subordinates")) {
+ Slapi_Filter *f = NULL;
+ /* Look for special filters we want to leave alone */
+ if (0 == strcmp(lc_fstr, "(&(numsubordinates=*)(numsubordinates>=1))" )) {
+ ; /* Do nothing, this one works OK */
+ } else {
+ /* So let's grok the filter in detail and try to rewrite it */
+ slapi_pblock_get( pb, SLAPI_SEARCH_FILTER, &f );
+ rc = grok_and_rewrite_filter(f);
+ if (0 == rc) {
+ /* he rewrote it ! fixup the string version */
+ /* slapi_pblock_set( pb, SLAPI_SEARCH_STRFILTER, newfstr ); */
+ }
+ }
+ }
+
+ slapi_ch_free_string( &lc_fstr );
+ }
+ return rc;
+}
+
+
+int ldbm_compute_init()
+{
+ int ret = 0;
+ ret = slapi_compute_add_evaluator(ldbm_compute_evaluator);
+ if (0 == ret) {
+ ret = slapi_compute_add_search_rewriter(ldbm_compute_rewriter);
+ }
+ return ret;
+}
+
diff --git a/ldap/servers/slapd/back-ldbm/ldbm_attrcrypt.c b/ldap/servers/slapd/back-ldbm/ldbm_attrcrypt.c
new file mode 100644
index 00000000..c114fdd6
--- /dev/null
+++ b/ldap/servers/slapd/back-ldbm/ldbm_attrcrypt.c
@@ -0,0 +1,870 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 1999, 2001-2004 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+/* This file handles attribute encryption.
+ */
+
+
+#include "back-ldbm.h"
+#include "attrcrypt.h"
+#include "pk11func.h"
+#include "keyhi.h"
+#include "nss.h"
+
+/*
+ * Todo:
+ * Remember to free the private structures in the attrinfos, so avoid a leak.
+ */
+
+attrcrypt_cipher_entry attrcrypt_cipher_list[] = { {ATTRCRYPT_CIPHER_AES, "AES", CKM_AES_CBC_PAD, CKM_AES_CBC_PAD, CKM_AES_CBC_PAD, 128/8, 16} ,
+ {ATTRCRYPT_CIPHER_DES3 , "3DES" , CKM_DES3_CBC_PAD, CKM_DES3_CBC_PAD, CKM_DES3_CBC_PAD, 112/8, 8},
+ {0} };
+
+#define KEY_ATTRIBUTE_NAME "nsSymmetricKey"
+
+/*
+ * We maintain one of these structures per cipher that we handle
+ */
+
+typedef struct _attrcrypt_cipher_state {
+ char *cipher_display_name;
+ PRLock *cipher_lock;
+ PK11SlotInfo *slot;
+ PK11SymKey *key;
+ attrcrypt_cipher_entry *ace;
+} attrcrypt_cipher_state;
+
+struct _attrcrypt_state_private {
+ attrcrypt_cipher_state *acs_array[1];
+};
+
+static int attrcrypt_wrap_key(attrcrypt_cipher_state *acs, PK11SymKey *symmetric_key, SECKEYPublicKey *public_key, SECItem *wrapped_symmetric_key);
+static int attrcrypt_unwrap_key(attrcrypt_cipher_state *acs, SECKEYPrivateKey *private_key, SECItem *wrapped_symmetric_key, PK11SymKey **unwrapped_symmetric_key);
+
+/*
+ * Copied from front-end because it's private to plugins
+ */
+
+static int
+local_valuearray_count( Slapi_Value **va)
+{
+ int i=0;
+ if(va!=NULL)
+ {
+ while(NULL != va[i]) i++;
+ }
+ return(i);
+}
+
+/*
+ * Helper functions for key management
+ */
+
+static Slapi_Entry *
+getConfigEntry( const char *dn, Slapi_Entry **e2 ) {
+ Slapi_DN sdn;
+
+ slapi_sdn_init_dn_byref( &sdn, dn );
+ slapi_search_internal_get_entry( &sdn, NULL, e2,
+ plugin_get_default_component_id());
+ slapi_sdn_done( &sdn );
+ return *e2;
+}
+
+/**
+ * Free an entry
+ */
+static void
+freeConfigEntry( Slapi_Entry ** e ) {
+ if ( (e != NULL) && (*e != NULL) ) {
+ slapi_entry_free( *e );
+ *e = NULL;
+ }
+}
+
+static int
+attrcrypt_get_ssl_cert_name(char **cert_name)
+{
+ char *config_entry_dn = "cn=RSA,cn=encryption,cn=config";
+ Slapi_Entry *config_entry = NULL;
+
+ *cert_name = NULL;
+ getConfigEntry(config_entry_dn, &config_entry);
+ if (NULL == config_entry) {
+ return -1;
+ }
+ *cert_name = slapi_entry_attr_get_charptr( config_entry, "nssslpersonalityssl" );
+ freeConfigEntry(&config_entry);
+ return 0;
+}
+
+/* Retrieve a symmetric key from dse.ldif for a specified cipher */
+static int
+attrcrypt_keymgmt_get_key(ldbm_instance *li, attrcrypt_cipher_state *acs, SECKEYPrivateKey *private_key, PK11SymKey **key_from_store)
+{
+ int ret = 0;
+ Slapi_Entry *entry = NULL;
+ char *dn_template = "cn=%s,cn=encrypted attribute keys,cn=%s,cn=ldbm database,cn=plugins,cn=config";
+ char *instance_name = li->inst_name;
+ size_t dn_string_length = 0;
+ char *dn_string = NULL;
+ Slapi_Attr *keyattr = NULL;
+
+ LDAPDebug(LDAP_DEBUG_TRACE,"-> attrcrypt_keymgmt_get_key\n", 0, 0, 0);
+ dn_string_length = strlen(dn_template) + strlen(instance_name) + strlen(acs->ace->cipher_display_name);
+ dn_string = slapi_ch_malloc(dn_string_length);
+ sprintf(dn_string, dn_template, acs->ace->cipher_display_name, instance_name);
+ /* Fetch the entry */
+ getConfigEntry(dn_string, &entry);
+ /* Did we find the entry ? */
+ if (NULL != entry) {
+ SECItem key_to_unwrap = {0};
+ /* If so then look for the attribute that contains the key */
+ slapi_entry_attr_find(entry, KEY_ATTRIBUTE_NAME, &keyattr);
+ if (keyattr != NULL) {
+ Slapi_Value *v = NULL;
+ slapi_valueset_first_value( &keyattr->a_present_values, &v);
+ key_to_unwrap.len = slapi_value_get_length(v);
+ key_to_unwrap.data = (void*)slapi_value_get_string(v);
+ }
+ /* Unwrap it */
+ ret = attrcrypt_unwrap_key(acs, private_key, &key_to_unwrap, key_from_store);
+ if (entry) {
+ freeConfigEntry(&entry);
+ }
+ } else {
+ ret = -2; /* Means: we didn't find the entry (which happens if the key has never been generated) */
+ }
+ LDAPDebug(LDAP_DEBUG_TRACE,"<- attrcrypt_keymgmt_get_key\n", 0, 0, 0);
+ return ret;
+}
+
+/* Store a symmetric key for a given cipher in dse.ldif */
+static int
+attrcrypt_keymgmt_store_key(ldbm_instance *li, attrcrypt_cipher_state *acs, SECKEYPublicKey *public_key, PK11SymKey *key_to_store)
+{
+ int ret = 0;
+ SECItem wrapped_symmetric_key = {0};
+ LDAPDebug(LDAP_DEBUG_TRACE,"-> attrcrypt_keymgmt_store_key\n", 0, 0, 0);
+ /* Wrap the key and then store it in the right place in dse.ldif */
+ ret = attrcrypt_wrap_key(acs, key_to_store, public_key, &wrapped_symmetric_key);
+ if (!ret) {
+ /* Make the entry to store */
+ Slapi_Entry *e = NULL;
+ Slapi_PBlock *pb = slapi_pblock_new();
+ Slapi_Value *key_value = NULL;
+ struct berval key_as_berval = {0};
+ int rc = 0;
+ char *entry_template =
+ "dn: cn=%s,cn=encrypted attribute keys,cn=%s,cn=ldbm database,cn=plugins,cn=config\n"
+ "objectclass:top\n"
+ "objectclass:extensibleObject\n"
+ "cn:%s\n";
+ char *instance_name = li->inst_name;
+ char *entry_string = NULL;
+ size_t entry_string_length = strlen(entry_template) + strlen(instance_name) + (strlen(acs->ace->cipher_display_name)*2);
+ entry_string = slapi_ch_malloc(entry_string_length);
+ sprintf(entry_string, entry_template,acs->ace->cipher_display_name,instance_name,acs->ace->cipher_display_name);
+ e = slapi_str2entry(entry_string, 0);
+ /* Add the key as a binary attribute */
+ key_as_berval.bv_val = wrapped_symmetric_key.data;
+ key_as_berval.bv_len = wrapped_symmetric_key.len;
+ key_value = slapi_value_new_berval(&key_as_berval);
+ slapi_entry_add_value(e, KEY_ATTRIBUTE_NAME, key_value);
+ slapi_value_free(&key_value);
+ /* Store the entry */
+ slapi_add_entry_internal_set_pb(pb, e, NULL, li->inst_li->li_identity, 0);
+ if ((rc = slapi_add_internal_pb(pb)) != LDAP_SUCCESS) {
+ LDAPDebug(LDAP_DEBUG_ANY, "attrcrypt_keymgmt_store_key: failed to add config key entries to the DSE: %d\n", rc, 0, 0);
+ }
+ if (entry_string) {
+ slapi_ch_free((void**)&entry_string);
+ }
+ if (pb) {
+ slapi_pblock_destroy(pb);
+ }
+ }
+ LDAPDebug(LDAP_DEBUG_TRACE,"<- attrcrypt_keymgmt_store_key\n", 0, 0, 0);
+ return ret;
+}
+
+/*
+ * Helper functions for key generation and wrapping
+ */
+
+/* Wrap a key with the server's public assymetric key for storage */
+static int
+attrcrypt_wrap_key(attrcrypt_cipher_state *acs, PK11SymKey *symmetric_key, SECKEYPublicKey *public_key, SECItem *wrapped_symmetric_key)
+{
+ int ret = 0;
+ SECStatus s = 0;
+ CK_MECHANISM_TYPE wrap_mechanism = CKM_RSA_PKCS;
+ SECKEYPublicKey *wrapping_key = public_key;
+ wrapped_symmetric_key->len = slapd_SECKEY_PublicKeyStrength(public_key);
+ wrapped_symmetric_key->data = slapi_ch_malloc(wrapped_symmetric_key->len);
+ LDAPDebug(LDAP_DEBUG_TRACE,"-> attrcrypt_wrap_key\n", 0, 0, 0);
+ s = slapd_pk11_PubWrapSymKey(wrap_mechanism, wrapping_key, symmetric_key, wrapped_symmetric_key);
+ if (SECSuccess != s) {
+ ret = -1;
+ LDAPDebug(LDAP_DEBUG_ANY,"attrcrypt_wrap_key: failed to wrap key for cipher %s\n", acs->ace->cipher_display_name, 0, 0);
+ }
+ LDAPDebug(LDAP_DEBUG_TRACE,"<- attrcrypt_wrap_key\n", 0, 0, 0);
+ return ret;
+}
+
+/* Unwrap a key previously wrapped with the server's private key */
+static int
+attrcrypt_unwrap_key(attrcrypt_cipher_state *acs, SECKEYPrivateKey *private_key, SECItem *wrapped_symmetric_key, PK11SymKey **unwrapped_symmetric_key)
+{
+ int ret = 0;
+ CK_MECHANISM_TYPE wrap_mechanism = acs->ace->wrap_mechanism;
+ SECKEYPrivateKey *unwrapping_key = private_key;
+ LDAPDebug(LDAP_DEBUG_TRACE,"-> attrcrypt_unwrap_key\n", 0, 0, 0);
+ *unwrapped_symmetric_key = slapd_pk11_PubUnwrapSymKey(unwrapping_key, wrapped_symmetric_key, wrap_mechanism, CKA_UNWRAP, 0);
+ if (NULL == *unwrapped_symmetric_key) {
+ ret = -1;
+ LDAPDebug(LDAP_DEBUG_ANY,"attrcrypt_unwrap_key: failed to unwrap key for cipher %s\n", acs->ace->cipher_display_name, 0, 0);
+ }
+ LDAPDebug(LDAP_DEBUG_TRACE,"<- attrcrypt_unwrap_key\n", 0, 0, 0);
+ return ret;
+}
+
+/* Generate a random key for a specified cipher */
+static int
+attrcrypt_generate_key(attrcrypt_cipher_state *acs,PK11SymKey **symmetric_key)
+{
+ int ret = -1;
+ PK11SymKey *new_symmetric_key = NULL;
+ *symmetric_key = NULL;
+ LDAPDebug(LDAP_DEBUG_TRACE,"-> attrcrypt_generate_key\n", 0, 0, 0);
+ new_symmetric_key = slapd_pk11_KeyGen(acs->slot, acs->ace->key_gen_mechanism, NULL, acs->ace->key_size, NULL);
+ if (new_symmetric_key) {
+ *symmetric_key = new_symmetric_key;
+ ret = 0;
+ }
+ LDAPDebug(LDAP_DEBUG_TRACE,"<- attrcrypt_generate_key\n", 0, 0, 0);
+ return ret;
+}
+
+static int
+attrcrypt_fetch_public_key(SECKEYPublicKey **public_key)
+{
+ int ret = 0;
+ CERTCertificate *cert = NULL;
+ SECKEYPublicKey *key = NULL;
+ PRErrorCode errorCode = 0;
+ char *default_cert_name = "server-cert";
+ char *cert_name = NULL;
+ LDAPDebug(LDAP_DEBUG_TRACE,"-> attrcrypt_fetch_public_key\n", 0, 0, 0);
+ *public_key = NULL;
+ /* Try to grok the server cert name from the SSL config */
+ ret = attrcrypt_get_ssl_cert_name(&cert_name);
+ if (ret) {
+ cert_name = default_cert_name;
+ }
+ /* We assume that the server core pin stuff is already enabled, via the SSL initialization done in the front-end */
+ cert = slapd_pk11_findCertFromNickname(cert_name, NULL);
+ if (cert == NULL) {
+ errorCode = PR_GetError();
+ LDAPDebug(LDAP_DEBUG_ANY,"Can't find certificate %s in attrcrypt_fetch_public_key: %d - %s\n", cert_name, errorCode, slapd_pr_strerror(errorCode));
+ }
+ if( cert != NULL ) {
+ key = slapd_CERT_ExtractPublicKey(cert);
+ }
+ if (key == NULL) {
+ errorCode = PR_GetError();
+ LDAPDebug(LDAP_DEBUG_ANY,"Can't get private key from cert %s in attrcrypt_fetch_public_key: %d - %s\n", cert_name, errorCode, slapd_pr_strerror(errorCode));
+ ret = -1;
+ }
+ if (cert) {
+ slapd_pk11_CERT_DestroyCertificate(cert);
+ }
+ if (key) {
+ *public_key = key;
+ }else {
+ ret = -1;
+ }
+ LDAPDebug(LDAP_DEBUG_TRACE,"<- attrcrypt_fetch_public_key\n", 0, 0, 0);
+ return ret;
+}
+
+static int
+attrcrypt_fetch_private_key(SECKEYPrivateKey **private_key)
+{
+ int ret = 0;
+ CERTCertificate *cert = NULL;
+ SECKEYPrivateKey *key = NULL;
+ PRErrorCode errorCode = 0;
+ char *default_cert_name = "server-cert";
+ char *cert_name = NULL;
+ LDAPDebug(LDAP_DEBUG_TRACE,"-> attrcrypt_fetch_private_key\n", 0, 0, 0);
+ *private_key = NULL;
+ /* Try to grok the server cert name from the SSL config */
+ ret = attrcrypt_get_ssl_cert_name(&cert_name);
+ if (ret) {
+ cert_name = default_cert_name;
+ }
+ /* We assume that the server core pin stuff is already enabled, via the SSL initialization done in the front-end */
+ cert = slapd_pk11_findCertFromNickname(cert_name, NULL);
+ if (cert == NULL) {
+ errorCode = PR_GetError();
+ LDAPDebug(LDAP_DEBUG_ANY,"Can't find certificate %s in attrcrypt_fetch_private_key: %d - %s\n", cert_name, errorCode, slapd_pr_strerror(errorCode));
+ }
+ if( cert != NULL ) {
+ key = slapd_pk11_findKeyByAnyCert(cert, NULL);
+ }
+ if (key == NULL) {
+ errorCode = PR_GetError();
+ LDAPDebug(LDAP_DEBUG_ANY,"Can't get private key from cert %s in attrcrypt_fetch_private_key: %d - %s\n", cert_name, errorCode, slapd_pr_strerror(errorCode));
+ ret = -1;
+ }
+ if (cert) {
+ slapd_pk11_CERT_DestroyCertificate(cert);
+ }
+ if (key) {
+ *private_key = key;
+ } else {
+ ret = -1;
+ }
+ LDAPDebug(LDAP_DEBUG_TRACE,"<- attrcrypt_fetch_private_key\n", 0, 0, 0);
+ return ret;
+}
+
+/*
+ CKM_AES_CBC_PAD
+ CKM_DES3_CBC_PAD
+ */
+
+/* Initialize the structure for a single cipher */
+static int
+attrcrypt_cipher_init(ldbm_instance *li, attrcrypt_cipher_entry *ace, SECKEYPrivateKey *private_key, SECKEYPublicKey *public_key, attrcrypt_cipher_state *acs)
+{
+ int ret = 0;
+ PK11SymKey *symmetric_key = NULL;
+ LDAPDebug(LDAP_DEBUG_TRACE,"-> attrcrypt_cipher_init\n", 0, 0, 0);
+ acs->cipher_lock = PR_NewLock();
+ /* Fill in some basic stuff */
+ acs->ace = ace;
+ acs->cipher_display_name = ace->cipher_display_name;
+ if (NULL == acs->cipher_lock) {
+ LDAPDebug(LDAP_DEBUG_ANY,"Failed to create cipher lock in attrcrypt_cipher_init\n", 0, 0, 0);
+ }
+ acs->slot = slapd_pk11_GetInternalKeySlot();
+ if (NULL == acs->slot) {
+ LDAPDebug(LDAP_DEBUG_ANY,"Failed to create a slot for cipher %s in attrcrypt_cipher_entry\n", acs->cipher_display_name, 0, 0);
+ goto error;
+ }
+ /* Try to get the symmetric key for this cipher */
+ ret = attrcrypt_keymgmt_get_key(li,acs,private_key,&symmetric_key);
+ if (ret) {
+ if (-2 == ret) {
+ LDAPDebug(LDAP_DEBUG_ANY,"No symmetric key found for cipher %s in backend %s, attempting to create one...\n", acs->cipher_display_name, li->inst_name, 0);
+ ret = attrcrypt_generate_key(acs,&symmetric_key);
+ if (ret) {
+ LDAPDebug(LDAP_DEBUG_ANY,"Failed to generate key for %s in attrcrypt_cipher_init\n", acs->cipher_display_name, 0, 0);
+ }
+ if (symmetric_key) {
+ ret = attrcrypt_keymgmt_store_key(li,acs,public_key,symmetric_key);
+ if (ret) {
+ LDAPDebug(LDAP_DEBUG_ANY,"Failed to store key for cipher %s in attrcrypt_cipher_init\n", acs->cipher_display_name, 0, 0);
+ } else {
+ LDAPDebug(LDAP_DEBUG_ANY,"Key for cipher %s successfully generated and stored\n", acs->cipher_display_name, 0, 0);
+ }
+ }
+
+ } else {
+ LDAPDebug(LDAP_DEBUG_ANY,"Failed to retrieve key for cipher %s in attrcrypt_cipher_init\n", acs->cipher_display_name, 0, 0);
+ }
+ }
+ if (symmetric_key) {
+ /* we loaded the symmetric key, store it in the acs */
+ acs->key = symmetric_key;
+ }
+error:
+ LDAPDebug(LDAP_DEBUG_TRACE,"<- attrcrypt_cipher_init\n", 0, 0, 0);
+ return ret;
+}
+
+static void
+attrcrypt_acs_list_add(ldbm_instance *li,attrcrypt_cipher_state *acs)
+{
+ /* Realloc the existing list and add to the end */
+ attrcrypt_cipher_state **current = NULL;
+ size_t list_size = 0;
+ /* Is the list already there ? */
+ if (NULL == li->inst_attrcrypt_state_private) {
+ /* If not, add it */
+ li->inst_attrcrypt_state_private = (attrcrypt_state_private *) slapi_ch_calloc(sizeof(attrcrypt_cipher_state *), 2); /* 2 == The pointer and a NULL terminator */
+ } else {
+ /* Otherwise re-size it */
+ for (current = &(li->inst_attrcrypt_state_private->acs_array[0]); *current; current++) {
+ list_size++;
+ }
+ li->inst_attrcrypt_state_private = (attrcrypt_state_private *) slapi_ch_realloc((char*)li->inst_attrcrypt_state_private,sizeof(attrcrypt_cipher_state *) * (list_size + 2));
+ li->inst_attrcrypt_state_private->acs_array[list_size + 1] = NULL;
+ }
+ li->inst_attrcrypt_state_private->acs_array[list_size] = acs;
+}
+
+int
+attrcrypt_init(ldbm_instance *li)
+{
+ int ret = 0;
+ attrcrypt_cipher_entry *ace = NULL;
+ SECKEYPrivateKey *private_key = NULL;
+ SECKEYPublicKey *public_key = NULL;
+ LDAPDebug(LDAP_DEBUG_TRACE,"-> attrcrypt_init\n", 0, 0, 0);
+ if (slapd_security_library_is_initialized()) {
+ li->inst_attrcrypt_state_private = NULL;
+ /* Get the server's private key, which is used to unwrap the stored symmetric keys */
+ ret = attrcrypt_fetch_private_key(&private_key);
+ if (!ret) {
+ ret = attrcrypt_fetch_public_key(&public_key);
+ if (!ret) {
+ for (ace = attrcrypt_cipher_list; ace && ace->cipher_number && !ret; ace++) {
+ /* Make a state object for this cipher */
+ attrcrypt_cipher_state *acs = (attrcrypt_cipher_state *) slapi_ch_calloc(sizeof(attrcrypt_cipher_state),1);
+ ret = attrcrypt_cipher_init(li, ace, private_key, public_key, acs);
+ if (ret) {
+ LDAPDebug(LDAP_DEBUG_ANY,"Failed to initialize cipher %s in attrcrypt_init\n", ace->cipher_display_name, 0, 0);
+ } else {
+ /* Since we succeeded, add the acs to the backend instance list */
+ attrcrypt_acs_list_add(li,acs);
+ LDAPDebug(LDAP_DEBUG_TRACE,"Initialized cipher %s in attrcrypt_init\n", ace->cipher_display_name, 0, 0);
+ }
+
+ }
+ }
+ }
+ } else {
+ if (li->attrcrypt_configured) {
+ LDAPDebug(LDAP_DEBUG_ANY,"Warning: encryption is configured in backend %s, but because SSL is not enabled, database encryption is not available and the configuration will be overridden.\n", li->inst_name, 0, 0);
+ }
+ }
+ LDAPDebug(LDAP_DEBUG_TRACE,"<- attrcrypt_init : %d\n", ret, 0, 0);
+ return ret;
+}
+
+/*
+ * Called by the config code when a new attribute is added,
+ * to make sure that we already have the runtime state and key
+ * stored for that cipher. If not, we attmept to make it.
+ * If this function succeeds, then its ok to go on to use the
+ * cipher.
+ */
+int attrcrypt_check_enable_cipher(attrcrypt_cipher_entry *ace)
+{
+ int ret = 0;
+ LDAPDebug(LDAP_DEBUG_TRACE,"-> attrcrypt_check_enable_cipher\n", 0, 0, 0);
+ LDAPDebug(LDAP_DEBUG_TRACE,"<- attrcrypt_check_enable_cipher\n", 0, 0, 0);
+ return ret;
+}
+
+int
+attrcrypt_cleanup(attrcrypt_cipher_state *acs)
+{
+ LDAPDebug(LDAP_DEBUG_TRACE,"-> attrcrypt_cleanup\n", 0, 0, 0);
+ if (acs->key) {
+ slapd_pk11_FreeSymKey(acs->key);
+ }
+ if (acs->slot) {
+ slapd_pk11_FreeSlot(acs->slot);
+ }
+ LDAPDebug(LDAP_DEBUG_TRACE,"<- attrcrypt_cleanup\n", 0, 0, 0);
+ return 0;
+}
+
+static attrcrypt_cipher_state *
+attrcrypt_get_acs(backend *be, attrcrypt_private *priv)
+{
+ /* Walk the list of acs objects looking for the one for our cipher */
+ int cipher = priv->attrcrypt_cipher;
+ ldbm_instance *li = (ldbm_instance *) be->be_instance_info;
+ attrcrypt_state_private* iasp = li->inst_attrcrypt_state_private;
+ if (iasp) {
+ attrcrypt_cipher_state **current = &(iasp->acs_array[0]);
+ while (current) {
+ if ((*current)->ace->cipher_number == cipher) {
+ return *current;
+ }
+ current++;
+ }
+ }
+ return NULL;
+}
+
+#if defined(DEBUG_ATTRCRYPT)
+static void log_bytes(char* format_string, unsigned char *bytes, size_t length)
+{
+ size_t max_length = 20;
+ size_t truncated_length = (length > max_length) ? max_length : length;
+ size_t x = 0;
+ char *print_buffer = NULL;
+ char *print_ptr = NULL;
+
+ print_buffer = (char*)slapi_ch_malloc((truncated_length * 3) + 1);
+ print_ptr = print_buffer;
+
+ for (x = 0; x < truncated_length; x++) {
+ print_ptr += sprintf(print_ptr, "%02x ", bytes[x]);
+ }
+
+ LDAPDebug(LDAP_DEBUG_ANY,format_string, print_buffer, length, 0);
+
+ slapi_ch_free((void**)&print_buffer);
+}
+#endif
+
+/* Either encipher or decipher an attribute value */
+static int
+attrcrypt_crypto_op(attrcrypt_private *priv, backend *be, struct attrinfo *ai, char *in_data, size_t in_size, char **out_data, size_t *out_size, int encrypt)
+{
+ int ret = 0;
+ SECStatus secret = 0;
+ PK11Context* sec_context = NULL;
+ SECItem iv_item = {0};
+ SECItem *security_parameter = NULL;
+ int output_buffer_length = 0;
+ int output_buffer_size1 = 0;
+ int output_buffer_size2 = 0;
+ unsigned char *output_buffer = NULL;
+ attrcrypt_cipher_state *acs = NULL;
+
+ LDAPDebug(LDAP_DEBUG_TRACE,"-> attrcrypt_crypto_op\n", 0, 0, 0);
+ acs = attrcrypt_get_acs(be,ai->ai_attrcrypt);
+ if (NULL == acs) {
+ /* This happens if SSL/NSS has not been enabled */
+ return -1;
+ }
+#if defined(DEBUG_ATTRCRYPT)
+ if (encrypt) {
+ LDAPDebug(LDAP_DEBUG_ANY,"attrcrypt_crypto_op encrypt '%s' (%d)\n", in_data, in_size, 0);
+ } else {
+ log_bytes("attrcrypt_crypto_op decrypt '%s' (%d)\n", in_data, in_size);
+ }
+#endif
+ /* Allocate the output buffer */
+ output_buffer_length = in_size + 16;
+ output_buffer = slapi_ch_malloc(output_buffer_length);
+ /* Now call NSS to do the cipher op */
+ iv_item.data = "aaaaaaaaaaaaaaaa"; /* ptr to an array of IV bytes */
+ iv_item.len = acs->ace->iv_length; /* length of the array of IV bytes */
+ security_parameter = slapd_pk11_ParamFromIV(acs->ace->cipher_mechanism, &iv_item);
+ if (NULL == security_parameter) {
+ int errorCode = PR_GetError();
+ LDAPDebug(LDAP_DEBUG_ANY,"attrcrypt_crypto_op failed to make IV for cipher %s : %d - %s\n", acs->ace->cipher_display_name, errorCode, slapd_pr_strerror(errorCode));
+ goto error;
+ }
+ sec_context = slapd_pk11_createContextBySymKey(acs->ace->cipher_mechanism, (encrypt ? CKA_ENCRYPT : CKA_DECRYPT), acs->key, security_parameter);
+ if (NULL == sec_context) {
+ int errorCode = PR_GetError();
+ LDAPDebug(LDAP_DEBUG_ANY,"attrcrypt_crypto_op failed on cipher %s : %d - %s\n", acs->ace->cipher_display_name, errorCode, slapd_pr_strerror(errorCode));
+ goto error;
+ }
+ secret = slapd_pk11_cipherOp(sec_context, output_buffer, &output_buffer_size1, output_buffer_length, in_data, in_size);
+ if (SECSuccess != secret) {
+ int errorCode = PR_GetError();
+ LDAPDebug(LDAP_DEBUG_ANY,"attrcrypt_crypto_op failed on cipher %s : %d - %s\n", acs->ace->cipher_display_name, errorCode, slapd_pr_strerror(errorCode));
+ goto error;
+ }
+#if defined(DEBUG_ATTRCRYPT)
+ LDAPDebug(LDAP_DEBUG_ANY,"slapd_pk11_cipherOp %d\n", output_buffer_size1, 0, 0);
+#endif
+ secret = slapd_pk11_DigestFinal(sec_context, output_buffer + output_buffer_size1, &output_buffer_size2, output_buffer_length - output_buffer_size1);
+ if (SECSuccess != secret) {
+ int errorCode = PR_GetError();
+ LDAPDebug(LDAP_DEBUG_ANY,"attrcrypt_crypto_op digest final failed on cipher %s : %d - %s\n", acs->ace->cipher_display_name, errorCode, slapd_pr_strerror(errorCode));
+ goto error;
+ } else {
+#if defined(DEBUG_ATTRCRYPT)
+ if (encrypt) {
+ log_bytes("slapd_pk11_DigestFinal '%s' (%d)\n", output_buffer, output_buffer_size1 + output_buffer_size2);
+ } else {
+ LDAPDebug(LDAP_DEBUG_ANY,"slapd_pk11_DigestFinal '%s', %d\n", output_buffer, output_buffer_size2, 0);
+ }
+#endif
+ *out_size = output_buffer_size1 + output_buffer_size2;
+ *out_data = output_buffer;
+ }
+error:
+ if (sec_context) {
+ slapd_pk11_DestroyContext(sec_context, PR_TRUE);
+ }
+ if (security_parameter) {
+ slapd_SECITEM_FreeItem(security_parameter, PR_TRUE);
+ }
+ LDAPDebug(LDAP_DEBUG_TRACE,"<- attrcrypt_crypto_op\n", 0, 0, 0);
+ return ret;
+}
+
+static int
+attrcrypt_crypto_op_value(attrcrypt_private *priv, backend *be, struct attrinfo *ai, Slapi_Value *invalue, Slapi_Value **outvalue, int encrypt)
+{
+ int ret = 0;
+ char *in_data = NULL;
+ size_t in_size = 0;
+ char *out_data = NULL;
+ size_t out_size = 0;
+ struct berval *bval = NULL;
+
+ LDAPDebug(LDAP_DEBUG_TRACE,"-> attrcrypt_crypto_op_value\n", 0, 0, 0);
+
+ bval = (struct berval *) slapi_value_get_berval(invalue);
+ in_data = bval->bv_val;
+ in_size = bval->bv_len;
+
+ ret = attrcrypt_crypto_op(priv,be,ai,in_data,in_size,&out_data,&out_size,encrypt);
+
+ if (0 == ret) {
+ struct berval outbervalue = {0};
+ outbervalue.bv_len = out_size;
+ outbervalue.bv_val = out_data;
+ /* This call makes a copy of the payload data, so we need to free the original data after making the call */
+ *outvalue = slapi_value_new_berval(&outbervalue);
+ slapi_ch_free((void**)&out_data);
+ }
+
+ LDAPDebug(LDAP_DEBUG_TRACE,"<- attrcrypt_crypto_op_value: %d\n", ret, 0, 0);
+ return ret;
+}
+
+int
+attrcrypt_crypto_op_value_replace(attrcrypt_private *priv, backend *be, struct attrinfo *ai, Slapi_Value *inoutvalue, int encrypt)
+{
+ int ret = 0;
+ char *in_data = NULL;
+ size_t in_size = 0;
+ char *out_data = NULL;
+ size_t out_size = 0;
+ struct berval *bval = NULL;
+
+ LDAPDebug(LDAP_DEBUG_TRACE,"-> attrcrypt_crypto_op_value_replace\n", 0, 0, 0);
+
+ bval = (struct berval *) slapi_value_get_berval(inoutvalue);
+ in_data = bval->bv_val;
+ in_size = bval->bv_len;
+
+ ret = attrcrypt_crypto_op(priv,be,ai,in_data,in_size,&out_data,&out_size,encrypt);
+
+ if (0 == ret) {
+ struct berval outbervalue = {0};
+ outbervalue.bv_len = out_size;
+ outbervalue.bv_val = out_data;
+ /* This takes a copy of the payload, so we need to free it now */
+ slapi_value_set_berval(inoutvalue,&outbervalue);
+ slapi_ch_free((void**)&out_data);
+ }
+
+ LDAPDebug(LDAP_DEBUG_TRACE,"<- attrcrypt_crypto_op_value_replace: %d\n", ret, 0, 0);
+ return ret;
+}
+
+static int
+attrcrypt_crypto_op_values(attrcrypt_private *priv, backend *be, struct attrinfo *ai, Slapi_Value **invalues, Slapi_Value ***outvalues, int encrypt)
+{
+ int ret = 0;
+ int i = 0;
+ Slapi_Value **encrypted_values = NULL;
+
+ LDAPDebug(LDAP_DEBUG_TRACE,"-> attrcrypt_crypto_op_values\n", 0, 0, 0);
+ encrypted_values = (Slapi_Value **) slapi_ch_calloc(sizeof(Slapi_Value *),local_valuearray_count(invalues) + 1);
+ for ( i = 0; (invalues[i] != NULL) && (ret == 0); i++ ) {
+ Slapi_Value *encrypted_value = NULL;
+
+ ret = attrcrypt_crypto_op_value(priv,be,ai,invalues[i],&encrypted_value,encrypt);
+ if (0 == ret) {
+ encrypted_values[i] = encrypted_value;
+ }
+ }
+ *outvalues = encrypted_values;
+ LDAPDebug(LDAP_DEBUG_TRACE,"<- attrcrypt_crypto_op_values: %d\n", ret, 0, 0);
+ return ret;
+}
+
+static int
+attrcrypt_crypto_op_values_replace(attrcrypt_private *priv, backend *be, struct attrinfo *ai, Slapi_Value **invalues, int encrypt)
+{
+ int ret = 0;
+ int i = 0;
+
+ LDAPDebug(LDAP_DEBUG_TRACE,"-> attrcrypt_crypto_op_values_replace\n", 0, 0, 0);
+ for ( i = 0; (invalues[i] != NULL) && (ret == 0); i++ ) {
+
+ ret = attrcrypt_crypto_op_value_replace(priv,be,ai,invalues[i],encrypt);
+ if (ret) {
+ break;
+ }
+ }
+ LDAPDebug(LDAP_DEBUG_TRACE,"<- attrcrypt_crypto_op_values_replace\n", 0, 0, 0);
+ return ret;
+}
+
+/* Modifies the entry in-place to decrypt any encrypted attributes */
+int
+attrcrypt_decrypt_entry(backend *be, struct backentry *e)
+{
+ int ret = 0;
+ int rc = 0;
+ Slapi_Attr *attr = NULL;
+ char *type = NULL;
+
+ LDAPDebug(LDAP_DEBUG_TRACE,"-> attrcrypt_decrypt_entry\n", 0, 0, 0);
+ /* Scan through the entry's attributes, looking to see if any are configured for crypto */
+ for ( rc = slapi_entry_first_attr( e->ep_entry, &attr ); rc == 0 && attr ; rc = slapi_entry_next_attr( e->ep_entry, attr, &attr )) {
+
+ struct attrinfo *ai = NULL;
+ Slapi_Value *value = NULL;
+ int i = 0;
+
+ slapi_attr_get_type( attr, &type );
+ ainfo_get(be, type, &ai);
+
+ if (ai && ai->ai_attrcrypt) {
+ i = slapi_attr_first_value(attr,&value);
+ while (NULL != value && i != -1)
+ {
+ /* Now decrypt the attribute values in place on the original entry */
+ ret = attrcrypt_crypto_op_value_replace(ai->ai_attrcrypt,be,ai,value,0);
+ if (ret) {
+ LDAPDebug(LDAP_DEBUG_ANY,"attrcrypt_decrypt_entry: FAILING because decryption operation failed\n", 0, 0, 0);
+ return ret;
+ }
+ i = slapi_attr_next_value(attr,i,&value);
+ }
+ /* Now do the same thing with deleted values */
+ i = attr_first_deleted_value(attr,&value);
+ while (NULL != value && i != -1)
+ {
+ /* Now decrypt the attribute values in place on the original entry */
+ ret = attrcrypt_crypto_op_value_replace(ai->ai_attrcrypt,be,ai,value,0);
+ if (ret) {
+ LDAPDebug(LDAP_DEBUG_ANY,"attrcrypt_decrypt_entry: FAILING because decryption operation failed\n", 0, 0, 0);
+ return ret;
+ }
+ i = attr_next_deleted_value(attr,i,&value);
+ }
+ }
+ }
+ LDAPDebug(LDAP_DEBUG_TRACE,"<- attrcrypt_decrypt_entry\n", 0, 0, 0);
+ return ret;
+}
+
+/* Encrypts attributes on this entry in-place (only changes the attribute data, nothing else)
+ */
+int
+attrcrypt_encrypt_entry_inplace(backend *be, const struct backentry *inout)
+{
+ int ret = 0;
+ int rc = 0;
+ char *type = NULL;
+ Slapi_Attr *attr = NULL;
+ Slapi_Value **svals = NULL;
+
+ LDAPDebug(LDAP_DEBUG_TRACE,"-> attrcrypt_encrypt_entry_inplace\n", 0, 0, 0);
+ /* Scan the entry's attributes looking for any that are configured for encryption */
+ for ( rc = slapi_entry_first_attr( inout->ep_entry, &attr ); rc == 0;
+ rc = slapi_entry_next_attr( inout->ep_entry, attr, &attr ) ) {
+
+ struct attrinfo *ai = NULL;
+
+ slapi_attr_get_type( attr, &type );
+
+ ainfo_get(be, type, &ai);
+
+ if (ai && ai->ai_attrcrypt) {
+ svals = attr_get_present_values(attr);
+ if (svals) {
+ /* Now encrypt the attribute values in place on the new entry */
+ ret = attrcrypt_crypto_op_values_replace(ai->ai_attrcrypt,be,ai,svals,1);
+ }
+ }
+ }
+ LDAPDebug(LDAP_DEBUG_TRACE,"<- attrcrypt_encrypt_entry_inplace\n", 0, 0, 0);
+ return ret;
+}
+
+/* Makes a copy of the entry that has all necessary attributes encrypted
+ * as a performance optimization, if there are no attributes configured
+ * for encryption in the entry, then no copy is returned.
+ */
+int
+attrcrypt_encrypt_entry(backend *be, const struct backentry *in, struct backentry **out)
+{
+ int ret = 0;
+ int rc = 0;
+ struct backentry *new_entry = NULL;
+ char *type = NULL;
+ Slapi_Attr *attr = NULL;
+ Slapi_Value **svals = NULL;
+ Slapi_Value **new_vals = NULL;
+
+ LDAPDebug(LDAP_DEBUG_TRACE,"-> attrcrypt_encrypt_entry\n", 0, 0, 0);
+ *out = NULL;
+ /* Scan the entry's attributes looking for any that are configured for encryption */
+ for ( rc = slapi_entry_first_attr( in->ep_entry, &attr ); rc == 0;
+ rc = slapi_entry_next_attr( in->ep_entry, attr, &attr ) ) {
+
+ struct attrinfo *ai = NULL;
+
+ slapi_attr_get_type( attr, &type );
+
+ ainfo_get(be, type, &ai);
+
+ if (ai && ai->ai_attrcrypt) {
+ svals = attr_get_present_values(attr);
+ if (svals) {
+ /* If we find one, did we make the new entry yet ? */
+ if (NULL == new_entry) {
+ /* If not then make it now as a copy of the old entry */
+ new_entry = backentry_dup((struct backentry *)in);
+ }
+ /* Now encrypt the attribute values in place on the new entry */
+ ret = attrcrypt_crypto_op_values(ai->ai_attrcrypt,be,ai,svals,&new_vals,1);
+ if (ret) {
+ LDAPDebug(LDAP_DEBUG_ANY,"Error: attrcrypt_crypto_op_values failed in attrcrypt_encrypt_entry\n", 0, 0, 0);
+ break;
+ }
+ /* DBDB does this call free the old value memory ? */
+ slapi_entry_attr_replace_sv(new_entry->ep_entry, type, new_vals);
+ }
+ }
+ }
+ *out = new_entry;
+ LDAPDebug(LDAP_DEBUG_TRACE,"<- attrcrypt_encrypt_entry\n", 0, 0, 0);
+ return ret;
+}
+
+/*
+ * Encrypt an index key. There is never any need to decrypt index keys since
+ * we only ever look them up using plain text.
+ */
+int
+attrcrypt_encrypt_index_key(backend *be, struct attrinfo *ai, const struct berval *in, struct berval **out)
+{
+ int ret = 0;
+ char *in_data = in->bv_val;
+ size_t in_size = in->bv_len;
+ char *out_data = NULL;
+ size_t out_size = 0;
+ struct berval *out_berval = NULL;
+
+ if (ai->ai_attrcrypt) {
+ LDAPDebug(LDAP_DEBUG_TRACE,"-> attrcrypt_encrypt_index_key\n", 0, 0, 0);
+ ret = attrcrypt_crypto_op(ai->ai_attrcrypt,be,ai, in_data,in_size,&out_data,&out_size, 1);
+ if (0 == ret) {
+ out_berval = (struct berval *)ber_alloc();
+ if (NULL == out_berval) {
+ return ENOMEM;
+ }
+ out_berval->bv_len = out_size;
+ /* Because we're making a new berval, we copy the payload pointer in */
+ /* It's now the responsibility of our caller to free that data */
+ out_berval->bv_val = out_data;
+ *out = out_berval;
+ }
+ LDAPDebug(LDAP_DEBUG_TRACE,"<- attrcrypt_encrypt_index_key\n", 0, 0, 0);
+ }
+ return ret;
+}
+
diff --git a/ldap/servers/slapd/back-ldbm/ldbm_attrcrypt_config.c b/ldap/servers/slapd/back-ldbm/ldbm_attrcrypt_config.c
new file mode 100644
index 00000000..7ec93554
--- /dev/null
+++ b/ldap/servers/slapd/back-ldbm/ldbm_attrcrypt_config.c
@@ -0,0 +1,298 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2004 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+/* This file handles configuration information that is specific
+ * to ldbm instance attribute encryption configuration.
+ */
+
+/* DBDB I left in the Sun copyright statement because some of the code
+ * in this file is derived from an older file : ldbm_index_config.c
+ */
+
+#include "back-ldbm.h"
+#include "attrcrypt.h"
+
+/* Forward declarations for the callbacks */
+int ldbm_instance_attrcrypt_config_add_callback(Slapi_PBlock *pb, Slapi_Entry* entryBefore, Slapi_Entry* e, int *returncode, char *returntext, void *arg);
+int ldbm_instance_attrcrypt_config_delete_callback(Slapi_PBlock *pb, Slapi_Entry* entryBefore, Slapi_Entry* e, int *returncode, char *returntext, void *arg);
+
+/*
+
+Config entries look like this:
+
+dn: cn=<attributeName>, cn=encrypted attributes, cn=databaseName, cn=ldbm database, cn=plugins, cn=config
+objectclass: top
+objectclass: nsAttributeEncryption
+cn: <attributeName>
+nsEncryptionAlgorithm: <cipherName>
+
+*/
+
+static int
+ldbm_attrcrypt_parse_cipher(char* cipher_display_name)
+{
+ attrcrypt_cipher_entry *ce = attrcrypt_cipher_list;
+ while (ce->cipher_number) {
+ if (0 == strcmp(ce->cipher_display_name,cipher_display_name)) {
+ return ce->cipher_number;
+ }
+ ce++;
+ }
+ return 0;
+}
+
+static int
+ldbm_attrcrypt_parse_entry(ldbm_instance *inst, Slapi_Entry *e,
+ char **attribute_name,
+ int *cipher)
+{
+ Slapi_Attr *attr;
+ const struct berval *attrValue;
+ Slapi_Value *sval;
+
+ *cipher = 0;
+ *attribute_name = NULL;
+
+ /* Get the name of the attribute to index which will be the value
+ * of the cn attribute. */
+ if (slapi_entry_attr_find(e, "cn", &attr) != 0) {
+ LDAPDebug(LDAP_DEBUG_ANY, "Warning: malformed attribute encryption entry %s\n",
+ slapi_entry_get_dn(e), 0, 0);
+ return LDAP_OPERATIONS_ERROR;
+ }
+
+ slapi_attr_first_value(attr, &sval);
+ attrValue = slapi_value_get_berval(sval);
+ *attribute_name = slapi_ch_strdup(attrValue->bv_val);
+
+ /* Get the list of index types from the entry. */
+ if (0 == slapi_entry_attr_find(e, "nsEncryptionAlgorithm", &attr)) {
+ slapi_attr_first_value(attr, &sval);
+ if (sval) {
+ attrValue = slapi_value_get_berval(sval);
+ *cipher = ldbm_attrcrypt_parse_cipher(attrValue->bv_val);
+ if (0 == *cipher)
+ LDAPDebug(LDAP_DEBUG_ANY, "Warning: attempt to configure unrecognized cipher %s in encrypted attribute config entry %s\n",
+ attrValue->bv_val, slapi_entry_get_dn(e), 0);
+ }
+ }
+ return LDAP_SUCCESS;
+}
+
+static void
+ldbm_instance_attrcrypt_enable(struct attrinfo *ai, int cipher)
+{
+ attrcrypt_private *priv = NULL;
+ if (NULL == ai->ai_attrcrypt) {
+ /* No existing private structure, allocate one */
+ ai->ai_attrcrypt = (attrcrypt_private*) slapi_ch_calloc(1, sizeof(attrcrypt_private));
+ }
+ priv = ai->ai_attrcrypt;
+ priv->attrcrypt_cipher = cipher;
+}
+
+static void
+ldbm_instance_attrcrypt_disable(struct attrinfo *ai)
+{
+ if (NULL != ai->ai_attrcrypt) {
+ /* Don't free the structure here, because other threads might be
+ * concurrently referencing it.
+ */
+ ai->ai_attrcrypt = 0;
+ }
+}
+
+/*
+ * Config DSE callback for attribute encryption entry add.
+ */
+int
+ldbm_instance_attrcrypt_config_add_callback(Slapi_PBlock *pb, Slapi_Entry* e, Slapi_Entry* eAfter, int *returncode, char *returntext, void *arg)
+{
+ ldbm_instance *inst = (ldbm_instance *) arg;
+ char *attribute_name = NULL;
+ int cipher = 0;
+ int ret = 0;
+
+ returntext[0] = '\0';
+
+ /* For add, we parse the entry, then check the attribute exists,
+ * then check that indexing config does not preclude us encrypting it,
+ * and finally we set the private structure in the attrinfo for the attribute.
+ */
+
+ *returncode = ldbm_attrcrypt_parse_entry(inst, e, &attribute_name , &cipher);
+
+ if (*returncode == LDAP_SUCCESS) {
+
+ struct attrinfo *ai = NULL;
+
+ /* If the cipher was invalid, return unwilling to perform */
+ if (0 == cipher) {
+ returntext = "invalid cipher";
+ *returncode = LDAP_UNWILLING_TO_PERFORM;
+ ret = SLAPI_DSE_CALLBACK_ERROR;
+ } else {
+
+ ainfo_get(inst->inst_be, attribute_name, &ai);
+ /* If we couldn't find a non-default attrinfo, then that means
+ * that no indexing or encryption has yet been defined for this attribute
+ * therefore , create a new attrinfo structure now.
+ */
+ if ((ai == NULL) || (0 == strcmp(LDBM_PSEUDO_ATTR_DEFAULT, ai->ai_type) )) {
+ /* If this attribute doesn't exist in the schema, then we DO NOT fail
+ * (this is because entensible objects and disabled schema checking allow
+ * non-schema attributes to exist.
+ */
+ /* Make a new attrinfo object */
+ attr_create_empty(inst->inst_be,attribute_name,&ai);
+ }
+ if (ai) {
+ ldbm_instance_attrcrypt_enable(ai, cipher);
+ /* Remember that we have some encryption enabled, so we can be intelligent about warning when SSL is not enabled */
+ inst->attrcrypt_configured = 1;
+ } else {
+ LDAPDebug(LDAP_DEBUG_ANY, "Warning: attempt to encryption on a non-existent attribute: %s\n",
+ attribute_name, 0, 0);
+ returntext = "attribute does not exist";
+ *returncode = LDAP_UNWILLING_TO_PERFORM;
+ ret = SLAPI_DSE_CALLBACK_ERROR;
+ }
+ ret = SLAPI_DSE_CALLBACK_OK;
+ }
+
+ } else {
+ ret = SLAPI_DSE_CALLBACK_ERROR;
+ }
+ if (attribute_name) {
+ slapi_ch_free(&attribute_name);
+ }
+ return ret;
+}
+
+/*
+ * Temp callback that gets called for each attribute encryption entry when a new
+ * instance is starting up.
+ */
+int
+ldbm_attrcrypt_init_entry_callback(Slapi_PBlock *pb, Slapi_Entry* e, Slapi_Entry* entryAfter, int *returncode, char *returntext, void *arg)
+{
+ return ldbm_instance_attrcrypt_config_add_callback(pb,e,entryAfter,returncode,returntext,arg);
+}
+
+/*
+ * Config DSE callback for attribute encryption deletes.
+ */
+int
+ldbm_instance_attrcrypt_config_delete_callback(Slapi_PBlock *pb, Slapi_Entry* e, Slapi_Entry* entryAfter, int *returncode, char *returntext, void *arg)
+{
+ ldbm_instance *inst = (ldbm_instance *) arg;
+ char *attribute_name = NULL;
+ int cipher = 0;
+ int ret = SLAPI_DSE_CALLBACK_ERROR;
+
+ returntext[0] = '\0';
+
+ /* For add, we parse the entry, then check the attribute exists,
+ * then check that indexing config does not preclude us encrypting it,
+ * and finally we set the private structure in the attrinfo for the attribute.
+ */
+
+ *returncode = ldbm_attrcrypt_parse_entry(inst, e, &attribute_name , &cipher);
+
+ if (*returncode == LDAP_SUCCESS) {
+
+ struct attrinfo *ai = NULL;
+
+ ainfo_get(inst->inst_be, attribute_name, &ai);
+ if (ai == NULL && (0 == strcmp(LDBM_PSEUDO_ATTR_DEFAULT, ai->ai_type)) ) {
+ LDAPDebug(LDAP_DEBUG_ANY, "Warning: attempt to delete encryption for non-existant attribute: %s\n",
+ attribute_name, 0, 0);
+ } else {
+ ldbm_instance_attrcrypt_disable(ai);
+ ret = SLAPI_DSE_CALLBACK_OK;
+ }
+ }
+ if (attribute_name) {
+ slapi_ch_free((void **)&attribute_name);
+ }
+ return ret;
+}
+
+/*
+ * Config DSE callback for index entry changes.
+ *
+ * this function is huge!
+ */
+int
+ldbm_instance_attrcrypt_config_modify_callback(Slapi_PBlock *pb, Slapi_Entry *e,
+ Slapi_Entry *entryAfter, int *returncode, char *returntext, void *arg)
+{
+ ldbm_instance *inst = (ldbm_instance *)arg;
+ Slapi_Attr *attr;
+ Slapi_Value *sval;
+ const struct berval *attrValue;
+ struct attrinfo *ainfo = NULL;
+ LDAPMod **mods;
+ int i = 0;
+ int j = 0;
+
+ returntext[0] = '\0';
+ *returncode = LDAP_SUCCESS;
+ slapi_pblock_get(pb, SLAPI_MODIFY_MODS, &mods);
+
+ slapi_entry_attr_find(e, "cn", &attr);
+ slapi_attr_first_value(attr, &sval);
+ attrValue = slapi_value_get_berval(sval);
+ ainfo_get(inst->inst_be, attrValue->bv_val, &ainfo);
+ if (NULL == ainfo) {
+ return SLAPI_DSE_CALLBACK_ERROR;
+ }
+
+ for (i = 0; mods[i] != NULL; i++) {
+
+ char *config_attr = (char *)mods[i]->mod_type;
+
+ /* There are basically three cases in the modify:
+ * 1. The attribute was added
+ * 2. The attribute was deleted
+ * 3. The attribute was modified (deleted and added).
+ * Now, of these three, only #3 is legal.
+ * This is because the attribute is mandatory and single-valued in the schema.
+ * We handle this as follows: an add will always replace what's there (if anything).
+ * a delete will remove what's there as long as it matches what's being deleted.
+ * this is to avoid ordering problems with the adds and deletes.
+ */
+
+ if (strcasecmp(config_attr, "nsEncryptionAlgorithm") == 0) {
+
+ if ((mods[i]->mod_op & ~LDAP_MOD_BVALUES) == LDAP_MOD_ADD) {
+
+ for (j = 0; mods[i]->mod_bvalues[j] != NULL; j++) {
+ int cipher = ldbm_attrcrypt_parse_cipher(mods[i]->mod_bvalues[j]->bv_val);
+ if (0 == cipher) {
+ /* Tried to configure an invalid cipher */
+ }
+ ldbm_instance_attrcrypt_enable(ainfo,cipher);
+ }
+ continue;
+ }
+ if ((mods[i]->mod_op & ~LDAP_MOD_BVALUES) == LDAP_MOD_DELETE) {
+ if ((mods[i]->mod_bvalues == NULL) ||
+ (mods[i]->mod_bvalues[0] == NULL)) {
+ /* Not legal */
+ return SLAPI_DSE_CALLBACK_ERROR;
+ } else {
+ for (j = 0; mods[i]->mod_bvalues[j] != NULL; j++) {
+ /* Code before here should ensure that we only ever delete something that was already here */
+ ldbm_instance_attrcrypt_disable(ainfo);
+ }
+ }
+ continue;
+ }
+ }
+ }
+ return SLAPI_DSE_CALLBACK_OK;
+}
+
diff --git a/ldap/servers/slapd/back-ldbm/ldbm_bind.c b/ldap/servers/slapd/back-ldbm/ldbm_bind.c
new file mode 100644
index 00000000..de70c601
--- /dev/null
+++ b/ldap/servers/slapd/back-ldbm/ldbm_bind.c
@@ -0,0 +1,245 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+/* bind.c - ldbm backend bind and unbind routines */
+
+#include "back-ldbm.h"
+
+#if defined( XP_WIN32 )
+
+typedef enum LDAPWAEnum {
+ LDAPWA_NoDomainAttr = -3,
+ LDAPWA_InvalidCredentials = -2,
+ LDAPWA_Failure = -1,
+ LDAPWA_Success= 0
+} LDAPWAStatus;
+
+int
+GetDomainUsername(
+ char *pszNTuserdomainid,
+ char *pszNTDomain,
+ char *pszNTUsername
+)
+{
+ char *pszAttr, *pDomain, *pUsername;
+
+ if( !pszNTuserdomainid )
+ return( 1 );
+
+ // Split the specially constructed attribute.
+ pszAttr = slapi_ch_strdup( pszNTuserdomainid );
+
+ pDomain = pszAttr;
+
+ pUsername = strchr( pszAttr, ':' );
+ if( pUsername == NULL )
+ return( 1 );
+
+ // Set the end of the NT Domain name,
+ // and the start of the NT username.
+ *pUsername = (char)NULL;
+ pUsername++;
+
+ strcpy( pszNTDomain, pDomain);
+ strcpy( pszNTUsername, pUsername);
+
+ slapi_ch_free( (void**)&pszAttr );
+
+ return( 0 );
+}
+
+/* Attempt Windows NT Authentication, using the password from the client app,
+ with the NT Domain and NT username, both stored in the entry.
+ If successful, the ldap_bind() is completed successsfully. */
+
+LDAPWAStatus
+WindowsAuthentication(
+ struct backentry *e,
+ struct berval *cred
+)
+{
+ Slapi_Attr *a;
+ Slapi_Value *sval = NULL;
+ int iStatus;
+ char szNTDomain[MAX_PATH], szNTUsername[MAX_PATH];
+ HANDLE hToken = NULL;
+ BOOL bLogonStatus = FALSE;
+ int i= -1;
+
+ /* Get the NT Domain and username - if the entry has such an attribute */
+ if( !e || !e->ep_entry ||
+ slapi_entry_attr_find( e->ep_entry, "ntuserdomainid", &a ) != 0)
+ {
+ return( LDAPWA_NoDomainAttr );
+ }
+
+ i= slapi_attr_first_value( a, &sval );
+ if(sval==NULL)
+ {
+ return( LDAPWA_NoDomainAttr );
+ }
+
+ while(i != -1)
+ {
+ const struct berval *val = slapi_value_get_berval(sval);
+ char * colon = NULL;
+
+ if (!val->bv_val || (strlen(val->bv_val) > (MAX_PATH<<1))) {
+ LDAPDebug( LDAP_DEBUG_TRACE,
+ "WindowsAuthentication => validation FAILED for \"%s\" on NT Domain : "
+ "ntuserdomainid attr value too long\n",
+ val->bv_val, 0, 0);
+ i= slapi_attr_next_value(a, i, &sval);
+ continue;
+ }
+ colon = strchr( val->bv_val, ':' );
+ if (!colon || ((colon - val->bv_val)/sizeof(char) > MAX_PATH)) {
+ if (!colon) {
+ LDAPDebug( LDAP_DEBUG_TRACE,
+ "WindowsAuthentication => validation FAILED for \"%s\" on NT Domain : "
+ "a colon is missing in ntuserdomainid attr value\n",
+ val->bv_val, 0, 0);
+ }
+ else {
+ LDAPDebug( LDAP_DEBUG_TRACE,
+ "WindowsAuthentication => validation FAILED for \"%s\" on NT Domain : "
+ "domain in ntuserdomainid attr value too long\n",
+ val->bv_val, 0, 0);
+ }
+ i= slapi_attr_next_value(a, i, &sval);
+ continue;
+ }
+
+ if(( iStatus = GetDomainUsername( val->bv_val,
+ szNTDomain,
+ szNTUsername )) != 0)
+ {
+ i= slapi_attr_next_value(a, i, &sval);
+ continue;
+ }
+
+#if !defined( LOGON32_LOGON_NETWORK )
+/* This is specified in the WIn32 LogonUser() documentation, but not defined
+ in the Visual C++ 4.2 include file winbase.h. A search of the lastest version
+ of this file at www.microsoft.com finds that LOGON32_LOGON_NETWORK == 3.
+ */
+#define LOGON32_LOGON_NETWORK 3
+#endif
+ /* Now do the Logon attempt */
+ bLogonStatus = LogonUser( szNTUsername, // string that specifies the user name
+ szNTDomain, // string that specifies the domain or server
+ cred->bv_val, // string that specifies the password
+ LOGON32_LOGON_NETWORK, // the type of logon operation,
+ LOGON32_PROVIDER_DEFAULT, // specifies the logon provider
+ &hToken ); // pointer to variable to receive token handle
+ if( bLogonStatus && hToken )
+ CloseHandle( hToken );
+
+ if( bLogonStatus )
+ {
+ // Successful validation
+ LDAPDebug( LDAP_DEBUG_TRACE,
+ "WindowsAuthentication => validated \"%s\" on NT Domain \"%s\"\n",
+ szNTUsername, szNTDomain, 0 );
+ return( LDAPWA_Success );
+ }
+ else
+ {
+ LDAPDebug( LDAP_DEBUG_TRACE,
+ "WindowsAuthentication => validation FAILED for \"%s\" on NT Domain \"%s\", reason %d\n",
+ szNTUsername, szNTDomain, GetLastError() );
+ return( LDAPWA_InvalidCredentials );
+ }
+ i= slapi_attr_next_value(a, i, &sval);
+ }
+
+
+ return( LDAPWA_Failure );
+
+}
+#endif
+
+int
+ldbm_back_bind( Slapi_PBlock *pb )
+{
+ backend *be;
+ ldbm_instance *inst;
+ int method;
+ struct berval *cred;
+ struct ldbminfo *li;
+ struct backentry *e;
+ Slapi_Attr *attr;
+ Slapi_Value **bvals;
+ entry_address *addr;
+
+ /* get parameters */
+ slapi_pblock_get( pb, SLAPI_BACKEND, &be );
+ slapi_pblock_get( pb, SLAPI_PLUGIN_PRIVATE, &li );
+ slapi_pblock_get( pb, SLAPI_TARGET_ADDRESS, &addr );
+ slapi_pblock_get( pb, SLAPI_BIND_METHOD, &method );
+ slapi_pblock_get( pb, SLAPI_BIND_CREDENTIALS, &cred );
+
+ inst = (ldbm_instance *) be->be_instance_info;
+
+ /* always allow noauth simple binds (front end will send the result) */
+ if ( method == LDAP_AUTH_SIMPLE && cred->bv_len == 0 ) {
+ return( SLAPI_BIND_ANONYMOUS );
+ }
+
+ /*
+ * find the target entry. find_entry() takes care of referrals
+ * and sending errors if the entry does not exist.
+ */
+ if (( e = find_entry( pb, be, addr, NULL /* no txn */ )) == NULL ) {
+ return( SLAPI_BIND_FAIL );
+ }
+
+ switch ( method ) {
+ case LDAP_AUTH_SIMPLE:
+ {
+ Slapi_Value cv;
+ if ( slapi_entry_attr_find( e->ep_entry, "userpassword", &attr ) != 0 ) {
+#if defined( XP_WIN32 )
+ if( WindowsAuthentication( e, cred ) == LDAPWA_Success ) {
+ break;
+ }
+#endif
+ slapi_send_ldap_result( pb, LDAP_INAPPROPRIATE_AUTH, NULL,
+ NULL, 0, NULL );
+ cache_return( &inst->inst_cache, &e );
+ return( SLAPI_BIND_FAIL );
+ }
+ bvals= attr_get_present_values(attr);
+ slapi_value_init_berval(&cv,cred);
+ if ( slapi_pw_find_sv( bvals, &cv ) != 0 ) {
+#if defined( XP_WIN32 )
+ /* One last try - attempt Windows authentication,
+ if the user has a Windows account. */
+ if( WindowsAuthentication( e, cred ) == LDAPWA_Success ) {
+ break;
+ }
+#endif
+ slapi_send_ldap_result( pb, LDAP_INVALID_CREDENTIALS, NULL,
+ NULL, 0, NULL );
+ cache_return( &inst->inst_cache, &e );
+ value_done(&cv);
+ return( SLAPI_BIND_FAIL );
+ }
+ value_done(&cv);
+ }
+ break;
+
+ default:
+ slapi_send_ldap_result( pb, LDAP_STRONG_AUTH_NOT_SUPPORTED, NULL,
+ "auth method not supported", 0, NULL );
+ cache_return( &inst->inst_cache, &e );
+ return( SLAPI_BIND_FAIL );
+ }
+
+ cache_return( &inst->inst_cache, &e );
+
+ /* success: front end will send result */
+ return( SLAPI_BIND_SUCCESS );
+}
diff --git a/ldap/servers/slapd/back-ldbm/ldbm_compare.c b/ldap/servers/slapd/back-ldbm/ldbm_compare.c
new file mode 100644
index 00000000..a63f6009
--- /dev/null
+++ b/ldap/servers/slapd/back-ldbm/ldbm_compare.c
@@ -0,0 +1,77 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+/* compare.c - ldbm backend compare routine */
+
+#include "back-ldbm.h"
+
+int
+ldbm_back_compare( Slapi_PBlock *pb )
+{
+ backend *be;
+ ldbm_instance *inst;
+ struct ldbminfo *li;
+ struct backentry *e;
+ int err;
+ char *type;
+ struct berval *bval;
+ entry_address *addr;
+ Slapi_Value compare_value;
+ int result;
+ int ret = 0;
+ Slapi_DN *namespace_dn;
+
+
+ slapi_pblock_get( pb, SLAPI_BACKEND, &be );
+ slapi_pblock_get( pb, SLAPI_PLUGIN_PRIVATE, &li );
+ slapi_pblock_get( pb, SLAPI_TARGET_ADDRESS, &addr);
+ slapi_pblock_get( pb, SLAPI_COMPARE_TYPE, &type );
+ slapi_pblock_get( pb, SLAPI_COMPARE_VALUE, &bval );
+
+ inst = (ldbm_instance *) be->be_instance_info;
+ /* get the namespace dn */
+ namespace_dn = (Slapi_DN*)slapi_be_getsuffix(be, 0);
+
+ if ( (e = find_entry( pb, be, addr, NULL )) == NULL ) {
+ return( -1 ); /* error result sent by find_entry() */
+ }
+
+ err = slapi_access_allowed (pb, e->ep_entry, type, bval, SLAPI_ACL_COMPARE);
+ if ( err != LDAP_SUCCESS ) {
+ slapi_send_ldap_result( pb, err, NULL, NULL, 0, NULL );
+ ret = 1;
+ } else {
+
+ slapi_value_init_berval(&compare_value,bval);
+
+ err = slapi_vattr_namespace_value_compare(e->ep_entry,namespace_dn,type,&compare_value,&result,0);
+
+ if (0 != err) {
+ /* Was the attribute not found ? */
+ if (SLAPI_VIRTUALATTRS_NOT_FOUND == err) {
+ slapi_send_ldap_result( pb, LDAP_NO_SUCH_ATTRIBUTE, NULL, NULL,0, NULL );
+ ret = 1;
+ } else {
+ /* Some other problem, call it an operations error */
+ slapi_send_ldap_result( pb, LDAP_OPERATIONS_ERROR, NULL, NULL,0, NULL );
+ ret = -1;
+ }
+ } else {
+ /* Interpret the result */
+ if (result) {
+ /* Compare true */
+ slapi_send_ldap_result( pb, LDAP_COMPARE_TRUE, NULL, NULL, 0, NULL );
+ } else {
+ /* Compare false */
+ slapi_send_ldap_result( pb, LDAP_COMPARE_FALSE, NULL, NULL, 0, NULL );
+ }
+ ret = 0;
+ }
+ value_done(&compare_value);
+ }
+
+ cache_return( &inst->inst_cache, &e );
+ return( ret );
+}
diff --git a/ldap/servers/slapd/back-ldbm/ldbm_config.c b/ldap/servers/slapd/back-ldbm/ldbm_config.c
new file mode 100644
index 00000000..59e197f3
--- /dev/null
+++ b/ldap/servers/slapd/back-ldbm/ldbm_config.c
@@ -0,0 +1,1730 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+/* ldbm_config.c - Handles configuration information that is global to all ldbm instances. */
+
+#include "back-ldbm.h"
+#include "dblayer.h"
+
+/* Forward declarations */
+static int parse_ldbm_config_entry(struct ldbminfo *li, Slapi_Entry *e, config_info *config_array);
+
+/* Forward callback declarations */
+int ldbm_config_search_entry_callback(Slapi_PBlock *pb, Slapi_Entry* e, Slapi_Entry* entryAfter, int *returncode, char *returntext, void *arg);
+int ldbm_config_modify_entry_callback(Slapi_PBlock *pb, Slapi_Entry* e, Slapi_Entry* entryAfter, int *returncode, char *returntext, void *arg);
+
+static char *ldbm_skeleton_entries[] =
+{
+ "dn:cn=config, cn=%s, cn=plugins, cn=config\n"
+ "objectclass:top\n"
+ "objectclass:extensibleObject\n"
+ "cn:config\n",
+
+ "dn:cn=monitor, cn=%s, cn=plugins, cn=config\n"
+ "objectclass:top\n"
+ "objectclass:extensibleObject\n"
+ "cn:monitor\n",
+
+ "dn:cn=database, cn=monitor, cn=%s, cn=plugins, cn=config\n"
+ "objectclass:top\n"
+ "objectclass:extensibleObject\n"
+ "cn:database\n",
+
+ ""
+};
+
+/* Used to add an array of entries, like the one above and
+ * ldbm_instance_skeleton_entries in ldbm_instance_config.c, to the dse.
+ * Returns 0 on success.
+ */
+int ldbm_config_add_dse_entries(struct ldbminfo *li, char **entries, char *string1, char *string2, char *string3, int flags)
+{
+ int x;
+ Slapi_Entry *e;
+ Slapi_PBlock *util_pb = NULL;
+ int rc;
+ char entry_string[512];
+ int dont_write_file = 0;
+
+ if (flags & LDBM_INSTANCE_CONFIG_DONT_WRITE) {
+ dont_write_file = 1;
+ }
+
+ for(x = 0; strlen(entries[x]) > 0; x++) {
+ util_pb = slapi_pblock_new();
+ sprintf(entry_string, entries[x], string1, string2, string3);
+ e = slapi_str2entry(entry_string, 0);
+ slapi_add_entry_internal_set_pb(util_pb, e, NULL, li->li_identity, 0);
+ slapi_pblock_set(util_pb, SLAPI_DSE_DONT_WRITE_WHEN_ADDING,
+ &dont_write_file);
+ if ((rc = slapi_add_internal_pb(util_pb)) != LDAP_SUCCESS) {
+ LDAPDebug(LDAP_DEBUG_ANY, "Unable to add config entries to the DSE: %d\n", rc, 0, 0);
+ }
+ slapi_pblock_destroy(util_pb);
+ }
+
+ return 0;
+}
+
+/* used to add a single entry, special case of above */
+int ldbm_config_add_dse_entry(struct ldbminfo *li, char *entry, int flags)
+{
+ char *entries[] = { "%s", "" };
+
+ return ldbm_config_add_dse_entries(li, entries, entry, NULL, NULL, flags);
+}
+
+/* Finds an entry in a config_info array with the given name. Returns
+ * the entry on success and NULL when not found.
+ */
+config_info *get_config_info(config_info *config_array, char *attr_name)
+{
+ int x;
+
+ for(x = 0; config_array[x].config_name != NULL; x++) {
+ if (!strcasecmp(config_array[x].config_name, attr_name)) {
+ return &(config_array[x]);
+ }
+ }
+ return NULL;
+}
+
+/*------------------------------------------------------------------------
+ * Get and set functions for ldbm and dblayer variables
+ *----------------------------------------------------------------------*/
+static void *ldbm_config_lookthroughlimit_get(void *arg)
+{
+ struct ldbminfo *li = (struct ldbminfo *) arg;
+
+ return (void *) (li->li_lookthroughlimit);
+}
+
+static int ldbm_config_lookthroughlimit_set(void *arg, void *value, char *errorbuf, int phase, int apply)
+{
+ struct ldbminfo *li = (struct ldbminfo *) arg;
+ int retval = LDAP_SUCCESS;
+ int val = (int) value;
+
+ /* Do whatever we can to make sure the data is ok. */
+
+ if (apply) {
+ li->li_lookthroughlimit = val;
+ }
+
+ return retval;
+}
+
+static void *ldbm_config_mode_get(void *arg)
+{
+ struct ldbminfo *li = (struct ldbminfo *) arg;
+
+ return (void *) (li->li_mode);
+}
+
+static int ldbm_config_mode_set(void *arg, void *value, char *errorbuf, int phase, int apply)
+{
+ struct ldbminfo *li = (struct ldbminfo *) arg;
+ int retval = LDAP_SUCCESS;
+ int val = (int) value;
+
+ /* Do whatever we can to make sure the data is ok. */
+
+ if (apply) {
+ li->li_mode = val;
+ }
+
+ return retval;
+}
+
+static void *ldbm_config_allidsthreshold_get(void *arg)
+{
+ struct ldbminfo *li = (struct ldbminfo *) arg;
+
+ return (void *) (li->li_allidsthreshold);
+}
+
+static int ldbm_config_allidsthreshold_set(void *arg, void *value, char *errorbuf, int phase, int apply)
+{
+ struct ldbminfo *li = (struct ldbminfo *) arg;
+ int retval = LDAP_SUCCESS;
+ int val = (int) value;
+
+ /* Do whatever we can to make sure the data is ok. */
+
+ /* Catch attempts to configure a stupidly low allidsthreshold */
+ if (val < 100) {
+ val = 100;
+ }
+
+ if (apply) {
+ li->li_allidsthreshold = val;
+ }
+
+ return retval;
+}
+
+static void *ldbm_config_directory_get(void *arg)
+{
+ struct ldbminfo *li = (struct ldbminfo *) arg;
+
+ /* Remember get functions of type string need to return
+ * alloced memory. */
+ return (void *) slapi_ch_strdup(li->li_new_directory);
+}
+
+static int ldbm_config_directory_set(void *arg, void *value, char *errorbuf, int phase, int apply)
+{
+ struct ldbminfo *li = (struct ldbminfo *) arg;
+ int retval = LDAP_SUCCESS;
+ char *val = (char *) value;
+ char tmpbuf[BUFSIZ];
+
+ errorbuf[0] = '\0';
+
+ if (!apply) {
+ /* we should really do some error checking here. */
+ return retval;
+ }
+
+ if (CONFIG_PHASE_RUNNING == phase) {
+ slapi_ch_free((void **) &(li->li_new_directory));
+ li->li_new_directory = slapi_ch_strdup(val);
+ LDAPDebug(LDAP_DEBUG_ANY, "New db directory location will not take affect until the server is restarted\n", 0, 0, 0);
+ } else {
+ if (!strcmp(val, "get default")) {
+ /* Generate the default db directory name. The default db directory
+ * should be the instance directory with a '/db' thrown on the end.
+ * We need to read cn=config to get the instance dir. */
+ /* We use this funky "get default" string for the caller to
+ * tell us that it has no idea what the db directory should
+ * be. This code figures it out be reading cn=config. */
+
+ Slapi_PBlock *search_pb;
+ Slapi_Entry **entries = NULL;
+ Slapi_Attr *attr = NULL;
+ Slapi_Value *v = NULL;
+ const char *s = NULL;
+ int res;
+
+ search_pb = slapi_pblock_new();
+ slapi_search_internal_set_pb(search_pb, "cn=config", LDAP_SCOPE_BASE,
+ "objectclass=*", NULL, 0, NULL, NULL, li->li_identity, 0);
+ slapi_search_internal_pb(search_pb);
+ slapi_pblock_get(search_pb, SLAPI_PLUGIN_INTOP_RESULT, &res);
+
+ if (res != LDAP_SUCCESS) {
+ LDAPDebug(LDAP_DEBUG_ANY, "ERROR: ldbm plugin unable to read cn=config\n",
+ 0, 0, 0);
+ goto done;
+ }
+
+ slapi_pblock_get(search_pb, SLAPI_PLUGIN_INTOP_SEARCH_ENTRIES, &entries);
+ if (NULL == entries) {
+ LDAPDebug(LDAP_DEBUG_ANY, "ERROR: ldbm plugin unable to read cn=config\n",
+ 0, 0, 0);
+ res = LDAP_OPERATIONS_ERROR;
+ goto done;
+ }
+
+ res = slapi_entry_attr_find(entries[0], "nsslapd-instancedir", &attr);
+ if (res != 0 || attr == NULL) {
+ LDAPDebug(LDAP_DEBUG_ANY,
+ "ERROR: ldbm plugin unable to read attribute nsslapd-instancedir from cn=config\n",
+ 0, 0, 0);
+ res = LDAP_OPERATIONS_ERROR;
+ goto done;
+ }
+
+ if ( slapi_attr_first_value(attr,&v) != 0
+ || ( NULL == v )
+ || ( NULL == ( s = slapi_value_get_string( v )))) {
+ LDAPDebug(LDAP_DEBUG_ANY,
+ "ERROR: ldbm plugin unable to read attribute nsslapd-instancedir from cn=config\n",
+ 0, 0, 0);
+ res = LDAP_OPERATIONS_ERROR;
+ goto done;
+ }
+
+done:
+ slapi_pblock_destroy(search_pb);
+ if (res != LDAP_SUCCESS) {
+ return res;
+ }
+ sprintf(tmpbuf, "%s/db", s );
+ val = tmpbuf;
+ }
+ slapi_ch_free((void **) &(li->li_new_directory));
+ slapi_ch_free((void **) &(li->li_directory));
+ li->li_new_directory = slapi_ch_strdup(val);
+ li->li_directory = slapi_ch_strdup(val);
+ }
+
+ return retval;
+}
+
+static void *ldbm_config_dbcachesize_get(void *arg)
+{
+ struct ldbminfo *li = (struct ldbminfo *) arg;
+
+ return (void *) (li->li_new_dbcachesize);
+}
+
+static int ldbm_config_dbcachesize_set(void *arg, void *value, char *errorbuf, int phase, int apply)
+{
+ struct ldbminfo *li = (struct ldbminfo *) arg;
+ int retval = LDAP_SUCCESS;
+ size_t val = (size_t) value;
+
+ if (apply) {
+ /* Stop the user configuring a stupidly small cache */
+ /* min: 8KB (page size) * def thrd cnts (threadnumber==20). */
+#define DBDEFMINSIZ 500000
+ if (val < DBDEFMINSIZ) {
+ LDAPDebug( LDAP_DEBUG_ANY,"WARNING: cache too small, increasing to %dK bytes\n", DBDEFMINSIZ/1000, 0, 0);
+ val = DBDEFMINSIZ;
+ }
+
+ if (CONFIG_PHASE_RUNNING == phase) {
+ li->li_new_dbcachesize = val;
+ LDAPDebug(LDAP_DEBUG_ANY, "New db cache size will not take affect until the server is restarted\n", 0, 0, 0);
+ } else {
+ li->li_new_dbcachesize = val;
+ li->li_dbcachesize = val;
+ }
+
+ }
+
+ return retval;
+}
+
+static void *ldbm_config_maxpassbeforemerge_get(void *arg)
+{
+ struct ldbminfo *li = (struct ldbminfo *) arg;
+
+ return (void *) (li->li_maxpassbeforemerge);
+}
+
+static int ldbm_config_maxpassbeforemerge_set(void *arg, void *value, char *errorbuf, int phase, int apply)
+{
+ struct ldbminfo *li = (struct ldbminfo *) arg;
+ int retval = LDAP_SUCCESS;
+ int val = (int) value;
+
+ if (apply) {
+ if (val < 0) {
+ LDAPDebug( LDAP_DEBUG_ANY,"WARNING: maxpassbeforemerge will not take negative value\n", 0, 0, 0);
+ val = 100;
+ }
+
+ li->li_maxpassbeforemerge = val;
+ }
+
+ return retval;
+}
+
+
+static void *ldbm_config_dbncache_get(void *arg)
+{
+ struct ldbminfo *li = (struct ldbminfo *) arg;
+
+ return (void *) (li->li_new_dbncache);
+}
+
+static int ldbm_config_dbncache_set(void *arg, void *value, char *errorbuf, int phase, int apply)
+{
+ struct ldbminfo *li = (struct ldbminfo *) arg;
+ int retval = LDAP_SUCCESS;
+ int val = (int) value;
+
+ if (apply) {
+ if (val < 0) {
+ LDAPDebug( LDAP_DEBUG_ANY,"WARNING: ncache will not take negative value\n", 0, 0, 0);
+ val = 0;
+ }
+
+ if (CONFIG_PHASE_RUNNING == phase) {
+ li->li_new_dbncache = val;
+ LDAPDebug(LDAP_DEBUG_ANY, "New db ncache will not take affect until the server is restarted\n", 0, 0, 0);
+ } else {
+ li->li_new_dbncache = val;
+ li->li_dbncache = val;
+ }
+
+ }
+
+ return retval;
+}
+
+static void *ldbm_config_db_logdirectory_get(void *arg)
+{
+ struct ldbminfo *li = (struct ldbminfo *) arg;
+
+ /* Remember get functions of type string need to return
+ * alloced memory. */
+ /* if dblayer_log_directory is set to a string different from ""
+ * then it has been set, return this variable
+ * otherwise it is set to default, use the instance home directory
+ */
+ if (strlen(li->li_dblayer_private->dblayer_log_directory) > 0)
+ return (void *) slapi_ch_strdup(li->li_dblayer_private->dblayer_log_directory);
+ else
+ return (void *) slapi_ch_strdup(li->li_new_directory);
+
+}
+
+static int ldbm_config_db_logdirectory_set(void *arg, void *value, char *errorbuf, int phase, int apply)
+{
+ struct ldbminfo *li = (struct ldbminfo *) arg;
+ int retval = LDAP_SUCCESS;
+ char *val = (char *) value;
+
+ if (apply) {
+ slapi_ch_free((void **) &(li->li_dblayer_private->dblayer_log_directory));
+ li->li_dblayer_private->dblayer_log_directory = slapi_ch_strdup(val);
+ }
+
+ return retval;
+}
+
+static void *ldbm_config_db_durable_transactions_get(void *arg)
+{
+ struct ldbminfo *li = (struct ldbminfo *) arg;
+
+ return (void *) li->li_dblayer_private->dblayer_durable_transactions;
+}
+
+static int ldbm_config_db_durable_transactions_set(void *arg, void *value, char *errorbuf, int phase, int apply)
+{
+ struct ldbminfo *li = (struct ldbminfo *) arg;
+ int retval = LDAP_SUCCESS;
+ int val = (int) value;
+
+ if (apply) {
+ li->li_dblayer_private->dblayer_durable_transactions = val;
+ }
+
+ return retval;
+}
+
+static void *ldbm_config_db_lockdown_get(void *arg)
+{
+ struct ldbminfo *li = (struct ldbminfo *) arg;
+
+ return (void *) li->li_dblayer_private->dblayer_lockdown;
+}
+
+static int ldbm_config_db_lockdown_set(
+ void *arg,
+ void *value,
+ char *errorbuf,
+ int phase,
+ int apply
+)
+{
+ struct ldbminfo *li = (struct ldbminfo *) arg;
+ int retval = LDAP_SUCCESS;
+ int val = (int) value;
+
+ if (apply) {
+ li->li_dblayer_private->dblayer_lockdown = val;
+ }
+
+ return retval;
+}
+
+static void *ldbm_config_db_circular_logging_get(void *arg)
+{
+ struct ldbminfo *li = (struct ldbminfo *) arg;
+
+ return (void *) li->li_dblayer_private->dblayer_circular_logging;
+}
+
+static int ldbm_config_db_circular_logging_set(void *arg, void *value, char *errorbuf, int phase, int apply)
+{
+ struct ldbminfo *li = (struct ldbminfo *) arg;
+ int retval = LDAP_SUCCESS;
+ int val = (int) value;
+
+ if (apply) {
+ li->li_dblayer_private->dblayer_circular_logging = val;
+ }
+
+ return retval;
+}
+
+static void *ldbm_config_db_transaction_logging_get(void *arg)
+{
+ struct ldbminfo *li = (struct ldbminfo *) arg;
+
+ return (void *) li->li_dblayer_private->dblayer_enable_transactions;
+}
+
+static int ldbm_config_db_transaction_logging_set(void *arg, void *value, char *errorbuf, int phase, int apply)
+{
+ struct ldbminfo *li = (struct ldbminfo *) arg;
+ int retval = LDAP_SUCCESS;
+ int val = (int) value;
+
+ if (apply) {
+ li->li_dblayer_private->dblayer_enable_transactions = val;
+ }
+
+ return retval;
+}
+
+static void *ldbm_config_db_logbuf_size_get(void *arg)
+{
+ struct ldbminfo *li = (struct ldbminfo *) arg;
+
+ return (void *) li->li_dblayer_private->dblayer_logbuf_size;
+}
+
+static int ldbm_config_db_logbuf_size_set(void *arg, void *value, char *errorbuf, int phase, int apply)
+{
+ struct ldbminfo *li = (struct ldbminfo *) arg;
+ int retval = LDAP_SUCCESS;
+ size_t val = (size_t) value;
+
+ if (apply) {
+ li->li_dblayer_private->dblayer_logbuf_size = val;
+ }
+
+ return retval;
+}
+
+static void *ldbm_config_db_checkpoint_interval_get(void *arg)
+{
+ struct ldbminfo *li = (struct ldbminfo *) arg;
+
+ return (void *) li->li_dblayer_private->dblayer_checkpoint_interval;
+}
+
+static int ldbm_config_db_checkpoint_interval_set(void *arg, void *value, char *errorbuf, int phase, int apply)
+{
+ struct ldbminfo *li = (struct ldbminfo *) arg;
+ int retval = LDAP_SUCCESS;
+ int val = (int) value;
+
+ if (apply) {
+ li->li_dblayer_private->dblayer_checkpoint_interval = val;
+ }
+
+ return retval;
+}
+
+static void *ldbm_config_db_page_size_get(void *arg)
+{
+ struct ldbminfo *li = (struct ldbminfo *) arg;
+
+ return (void *) li->li_dblayer_private->dblayer_page_size;
+}
+
+static int ldbm_config_db_page_size_set(void *arg, void *value, char *errorbuf, int phase, int apply)
+{
+ struct ldbminfo *li = (struct ldbminfo *) arg;
+ int retval = LDAP_SUCCESS;
+ size_t val = (size_t) value;
+
+ if (apply) {
+ li->li_dblayer_private->dblayer_page_size = val;
+ }
+
+ return retval;
+}
+
+static void *ldbm_config_db_index_page_size_get(void *arg)
+{
+ struct ldbminfo *li = (struct ldbminfo *) arg;
+
+ return (void *) li->li_dblayer_private->dblayer_index_page_size;
+}
+
+static int ldbm_config_db_index_page_size_set(void *arg, void *value, char *errorbuf, int phase, int apply)
+{
+ struct ldbminfo *li = (struct ldbminfo *) arg;
+ int retval = LDAP_SUCCESS;
+ size_t val = (size_t) value;
+
+ if (apply) {
+ li->li_dblayer_private->dblayer_index_page_size = val;
+ }
+
+ return retval;
+}
+
+static void *ldbm_config_db_idl_divisor_get(void *arg)
+{
+ struct ldbminfo *li = (struct ldbminfo *) arg;
+
+ return (void *) li->li_dblayer_private->dblayer_idl_divisor;
+}
+
+static int ldbm_config_db_idl_divisor_set(void *arg, void *value, char *errorbuf, int phase, int apply)
+{
+ struct ldbminfo *li = (struct ldbminfo *) arg;
+ int retval = LDAP_SUCCESS;
+ int val = (int) value;
+
+ if (apply) {
+ li->li_dblayer_private->dblayer_idl_divisor = val;
+ }
+
+ return retval;
+}
+
+static void *ldbm_config_db_logfile_size_get(void *arg)
+{
+ struct ldbminfo *li = (struct ldbminfo *) arg;
+
+ return (void *) li->li_dblayer_private->dblayer_logfile_size;
+}
+
+static int ldbm_config_db_logfile_size_set(void *arg, void *value, char *errorbuf, int phase, int apply)
+{
+ struct ldbminfo *li = (struct ldbminfo *) arg;
+ int retval = LDAP_SUCCESS;
+ size_t val = (size_t) value;
+
+ if (apply) {
+ li->li_dblayer_private->dblayer_logfile_size = val;
+ }
+
+ return retval;
+}
+
+static void *ldbm_config_db_spin_count_get(void *arg)
+{
+ struct ldbminfo *li = (struct ldbminfo *) arg;
+
+ return (void *) li->li_dblayer_private->dblayer_spin_count;
+}
+
+static int ldbm_config_db_spin_count_set(void *arg, void *value, char *errorbuf, int phase, int apply)
+{
+ struct ldbminfo *li = (struct ldbminfo *) arg;
+ int retval = LDAP_SUCCESS;
+ int val = (int) value;
+
+ if (apply) {
+ li->li_dblayer_private->dblayer_spin_count = val;
+ }
+
+ return retval;
+}
+
+static void *ldbm_config_db_trickle_percentage_get(void *arg)
+{
+ struct ldbminfo *li = (struct ldbminfo *) arg;
+
+ return (void *) li->li_dblayer_private->dblayer_trickle_percentage;
+}
+
+static int ldbm_config_db_trickle_percentage_set(void *arg, void *value, char *errorbuf, int phase, int apply)
+{
+ struct ldbminfo *li = (struct ldbminfo *) arg;
+ int retval = LDAP_SUCCESS;
+ int val = (int) value;
+
+ if (val < 0 || val > 100) {
+ sprintf(errorbuf, "Error: Invalid value for %s (%d). Must be between 0 and 100\n", CONFIG_DB_TRICKLE_PERCENTAGE, val);
+ LDAPDebug(LDAP_DEBUG_ANY, "%s", errorbuf, 0, 0);
+ return LDAP_UNWILLING_TO_PERFORM;
+ }
+
+ if (apply) {
+ li->li_dblayer_private->dblayer_trickle_percentage = val;
+ }
+
+ return retval;
+}
+
+static void *ldbm_config_db_verbose_get(void *arg)
+{
+ struct ldbminfo *li = (struct ldbminfo *) arg;
+
+ return (void *) li->li_dblayer_private->dblayer_verbose;
+}
+
+static int ldbm_config_db_verbose_set(void *arg, void *value, char *errorbuf, int phase, int apply)
+{
+ struct ldbminfo *li = (struct ldbminfo *) arg;
+ int retval = LDAP_SUCCESS;
+ int val = (int) value;
+
+ if (apply) {
+ li->li_dblayer_private->dblayer_verbose = val;
+ }
+
+ return retval;
+}
+
+static void *ldbm_config_db_debug_get(void *arg)
+{
+ struct ldbminfo *li = (struct ldbminfo *) arg;
+
+ return (void *) li->li_dblayer_private->dblayer_debug;
+}
+
+static int ldbm_config_db_debug_set(void *arg, void *value, char *errorbuf, int phase, int apply)
+{
+ struct ldbminfo *li = (struct ldbminfo *) arg;
+ int retval = LDAP_SUCCESS;
+ int val = (int) value;
+
+ if (apply) {
+ li->li_dblayer_private->dblayer_debug = val;
+ }
+
+ return retval;
+}
+
+static void *ldbm_config_db_named_regions_get(void *arg)
+{
+ struct ldbminfo *li = (struct ldbminfo *) arg;
+
+ return (void *) li->li_dblayer_private->dblayer_named_regions;
+}
+
+static int ldbm_config_db_named_regions_set(void *arg, void *value, char *errorbuf, int phase, int apply)
+{
+ struct ldbminfo *li = (struct ldbminfo *) arg;
+ int retval = LDAP_SUCCESS;
+ int val = (int) value;
+
+ if (apply) {
+ li->li_dblayer_private->dblayer_named_regions = val;
+ }
+
+ return retval;
+}
+
+static void *ldbm_config_db_private_mem_get(void *arg)
+{
+ struct ldbminfo *li = (struct ldbminfo *) arg;
+
+ return (void *) li->li_dblayer_private->dblayer_private_mem;
+}
+
+static int ldbm_config_db_private_mem_set(void *arg, void *value, char *errorbuf, int phase, int apply)
+{
+ struct ldbminfo *li = (struct ldbminfo *) arg;
+ int retval = LDAP_SUCCESS;
+ int val = (int) value;
+
+ if (apply) {
+ li->li_dblayer_private->dblayer_private_mem = val;
+ }
+
+ return retval;
+}
+
+static void *ldbm_config_db_private_import_mem_get(void *arg)
+{
+ struct ldbminfo *li = (struct ldbminfo *) arg;
+
+ return (void *) li->li_dblayer_private->dblayer_private_import_mem;
+}
+
+static int ldbm_config_db_private_import_mem_set(void *arg, void *value, char *errorbuf, int phase, int apply)
+{
+ struct ldbminfo *li = (struct ldbminfo *) arg;
+ int retval = LDAP_SUCCESS;
+ int val = (int) value;
+
+ if (apply) {
+ li->li_dblayer_private->dblayer_private_import_mem = val;
+ }
+
+ return retval;
+}
+
+static void *ldbm_config_db_shm_key_get(void *arg)
+{
+ struct ldbminfo *li = (struct ldbminfo *) arg;
+
+ return (void *) li->li_dblayer_private->dblayer_shm_key;
+}
+
+static int ldbm_config_db_shm_key_set(
+ void *arg,
+ void *value,
+ char *errorbuf,
+ int phase,
+ int apply
+)
+{
+ struct ldbminfo *li = (struct ldbminfo *) arg;
+ int retval = LDAP_SUCCESS;
+ int val = (int) value;
+
+ if (apply) {
+ li->li_dblayer_private->dblayer_shm_key = val;
+ }
+
+ return retval;
+}
+
+static void *ldbm_config_db_lock_get(void *arg)
+{
+ struct ldbminfo *li = (struct ldbminfo *) arg;
+
+ return (void *) li->li_dblayer_private->dblayer_lock_config;
+}
+
+
+static int ldbm_config_db_lock_set(void *arg, void *value, char *errorbuf, int phase, int apply)
+{
+ struct ldbminfo *li = (struct ldbminfo *) arg;
+ int retval = LDAP_SUCCESS;
+ size_t val = (size_t) value;
+
+ if (apply) {
+ if (CONFIG_PHASE_RUNNING == phase) {
+ li->li_dblayer_private->dblayer_lock_config = val;
+ LDAPDebug(LDAP_DEBUG_ANY, "New db cache size will not take affect until the server is restarted\n", 0, 0, 0);
+ } else {
+ li->li_dblayer_private->dblayer_lock_config = val;
+ }
+
+ }
+
+ return retval;
+}
+static void *ldbm_config_db_cache_get(void *arg)
+{
+ struct ldbminfo *li = (struct ldbminfo *) arg;
+
+ return (void *) li->li_dblayer_private->dblayer_cache_config;
+}
+
+static int ldbm_config_db_cache_set(void *arg, void *value, char *errorbuf, int phase, int apply)
+{
+ struct ldbminfo *li = (struct ldbminfo *) arg;
+ int retval = LDAP_SUCCESS;
+ int val = (int) value;
+
+ if (apply) {
+ li->li_dblayer_private->dblayer_cache_config = val;
+ }
+
+ return retval;
+}
+
+static void *ldbm_config_db_debug_checkpointing_get(void *arg)
+{
+ struct ldbminfo *li = (struct ldbminfo *) arg;
+
+ return (void *) li->li_dblayer_private->db_debug_checkpointing;
+}
+
+static int ldbm_config_db_debug_checkpointing_set(void *arg, void *value, char *errorbuf, int phase, int apply)
+{
+ struct ldbminfo *li = (struct ldbminfo *) arg;
+ int retval = LDAP_SUCCESS;
+ int val = (int) value;
+
+ if (apply) {
+ li->li_dblayer_private->db_debug_checkpointing = val;
+ }
+
+ return retval;
+}
+
+static void *ldbm_config_db_home_directory_get(void *arg)
+{
+ struct ldbminfo *li = (struct ldbminfo *) arg;
+
+ /* Remember get functions of type string need to return
+ * alloced memory. */
+ return (void *) slapi_ch_strdup(li->li_dblayer_private->dblayer_dbhome_directory);
+}
+
+static int ldbm_config_db_home_directory_set(void *arg, void *value, char *errorbuf, int phase, int apply)
+{
+ struct ldbminfo *li = (struct ldbminfo *) arg;
+ int retval = LDAP_SUCCESS;
+ char *val = (char *) value;
+
+ if (apply) {
+ slapi_ch_free((void **) &(li->li_dblayer_private->dblayer_dbhome_directory));
+ li->li_dblayer_private->dblayer_dbhome_directory = slapi_ch_strdup(val);
+ }
+
+ return retval;
+}
+
+static void *ldbm_config_import_cache_autosize_get(void *arg)
+{
+ struct ldbminfo *li = (struct ldbminfo *)arg;
+
+ return (void *)(li->li_import_cache_autosize);
+}
+
+static int ldbm_config_import_cache_autosize_set(void *arg, void *value, char *errorbuf,
+ int phase, int apply)
+{
+ struct ldbminfo *li = (struct ldbminfo *)arg;
+
+ if (apply)
+ li->li_import_cache_autosize = (int)value;
+ return LDAP_SUCCESS;
+}
+
+static void *ldbm_config_cache_autosize_get(void *arg)
+{
+ struct ldbminfo *li = (struct ldbminfo *)arg;
+
+ return (void *)(li->li_cache_autosize);
+}
+
+static int ldbm_config_cache_autosize_set(void *arg, void *value, char *errorbuf,
+ int phase, int apply)
+{
+ struct ldbminfo *li = (struct ldbminfo *)arg;
+
+ if (apply)
+ li->li_cache_autosize = (int)value;
+ return LDAP_SUCCESS;
+}
+
+static void *ldbm_config_cache_autosize_split_get(void *arg)
+{
+ struct ldbminfo *li = (struct ldbminfo *)arg;
+
+ return (void *)(li->li_cache_autosize_split);
+}
+
+static int ldbm_config_cache_autosize_split_set(void *arg, void *value, char *errorbuf,
+ int phase, int apply)
+{
+ struct ldbminfo *li = (struct ldbminfo *)arg;
+
+ if (apply)
+ li->li_cache_autosize_split = (int)value;
+ return LDAP_SUCCESS;
+}
+
+static void *ldbm_config_import_cachesize_get(void *arg)
+{
+ struct ldbminfo *li = (struct ldbminfo *)arg;
+
+ return (void *)(li->li_import_cachesize);
+}
+
+static int ldbm_config_import_cachesize_set(void *arg, void *value, char *errorbuf,
+ int phase, int apply)
+{
+ struct ldbminfo *li = (struct ldbminfo *)arg;
+
+ if (apply)
+ li->li_import_cachesize = (size_t)value;
+ return LDAP_SUCCESS;
+}
+
+static void *ldbm_config_index_buffer_size_get(void *arg)
+{
+ return (void *)import_get_index_buffer_size();
+}
+
+static int ldbm_config_index_buffer_size_set(void *arg, void *value, char *errorbuf,
+ int phase, int apply)
+{
+ if (apply)
+ import_configure_index_buffer_size((size_t)value);
+ return LDAP_SUCCESS;
+}
+
+static void *ldbm_config_idl_get_idl_new(void *arg)
+{
+ if (idl_get_idl_new())
+ return slapi_ch_strdup("new");
+ else
+ return slapi_ch_strdup("old");
+}
+
+static int ldbm_config_idl_set_tune(void *arg, void *value, char *errorbuf,
+ int phase, int apply)
+{
+ if (!strcasecmp("new", value))
+ idl_set_tune(4096);
+ else
+ idl_set_tune(0);
+ return LDAP_SUCCESS;
+}
+
+static void *ldbm_config_serial_lock_get(void *arg)
+{
+ struct ldbminfo *li = (struct ldbminfo *) arg;
+
+ return (void *) li->li_fat_lock;
+}
+
+static int ldbm_config_serial_lock_set(void *arg, void *value, char *errorbuf,
+ int phase, int apply)
+{
+ struct ldbminfo *li = (struct ldbminfo *) arg;
+
+ if (apply) {
+ li->li_fat_lock = (int) value;
+ }
+
+ return LDAP_SUCCESS;
+}
+
+static void *ldbm_config_legacy_errcode_get(void *arg)
+{
+ struct ldbminfo *li = (struct ldbminfo *) arg;
+
+ return (void *) li->li_legacy_errcode;
+}
+
+static int ldbm_config_legacy_errcode_set(void *arg, void *value, char *errorbuf,
+ int phase, int apply)
+{
+ struct ldbminfo *li = (struct ldbminfo *) arg;
+
+ if (apply) {
+ li->li_legacy_errcode = (int) value;
+ }
+
+ return LDAP_SUCCESS;
+}
+
+static int
+ldbm_config_set_bypass_filter_test(void *arg, void *value, char *errorbuf,
+ int phase, int apply)
+{
+ struct ldbminfo *li = (struct ldbminfo *)arg;
+
+ if (apply) {
+ char *myvalue = (char *)value;
+
+ if (0 == strcasecmp(myvalue, "on")) {
+ li->li_filter_bypass = 1;
+ li->li_filter_bypass_check = 0;
+ } else if (0 == strcasecmp(myvalue, "verify")) {
+ li->li_filter_bypass = 1;
+ li->li_filter_bypass_check = 1;
+ } else {
+ li->li_filter_bypass = 0;
+ li->li_filter_bypass_check = 0;
+ }
+ }
+ return LDAP_SUCCESS;
+}
+
+static void *ldbm_config_get_bypass_filter_test(void *arg)
+{
+ struct ldbminfo *li = (struct ldbminfo *)arg;
+ char *retstr = NULL;
+
+ if (li->li_filter_bypass) {
+ if (li->li_filter_bypass_check) {
+ /* meaningful only if is bypass filter test called */
+ retstr = slapi_ch_strdup("verify");
+ } else {
+ retstr = slapi_ch_strdup("on");
+ }
+ } else {
+ retstr = slapi_ch_strdup("off");
+ }
+ return (void *)retstr;
+}
+
+static int ldbm_config_set_use_vlv_index(void *arg, void *value, char *errorbuf, int phase, int apply)
+{
+ struct ldbminfo *li = (struct ldbminfo *) arg;
+ int val = (int) value;
+
+ if (apply) {
+ int setval = 0;
+ if (val) {
+ li->li_use_vlv = 1;
+ } else {
+ li->li_use_vlv = 0;
+ }
+ }
+ return LDAP_SUCCESS;
+}
+
+static void *ldbm_config_get_use_vlv_index(void *arg)
+{
+ struct ldbminfo *li = (struct ldbminfo *) arg;
+
+ return (void *) li->li_use_vlv;
+}
+
+static int
+ldbm_config_exclude_from_export_set( void *arg, void *value, char *errorbuf,
+ int phase, int apply)
+{
+ struct ldbminfo *li = (struct ldbminfo *)arg;
+
+ if ( apply ) {
+ if ( NULL != li->li_attrs_to_exclude_from_export ) {
+ charray_free( li->li_attrs_to_exclude_from_export );
+ li->li_attrs_to_exclude_from_export = NULL;
+ }
+
+ if ( NULL != value ) {
+ char *dupvalue = slapi_ch_strdup( value );
+ li->li_attrs_to_exclude_from_export = str2charray( dupvalue, " " );
+ slapi_ch_free((void**)&dupvalue);
+ }
+ }
+
+ return LDAP_SUCCESS;
+}
+
+static void *
+ldbm_config_exclude_from_export_get( void *arg )
+{
+ struct ldbminfo *li = (struct ldbminfo *)arg;
+ char *p, *retstr = NULL;
+ size_t len = 0;
+
+ if ( NULL != li->li_attrs_to_exclude_from_export &&
+ NULL != li->li_attrs_to_exclude_from_export[0] ) {
+ int i;
+
+ for ( i = 0; li->li_attrs_to_exclude_from_export[i] != NULL; ++i ) {
+ len += strlen( li->li_attrs_to_exclude_from_export[i] ) + 1;
+ }
+ p = retstr = slapi_ch_malloc( len );
+ for ( i = 0; li->li_attrs_to_exclude_from_export[i] != NULL; ++i ) {
+ if ( i > 0 ) {
+ *p++ = ' ';
+ }
+ strcpy( p, li->li_attrs_to_exclude_from_export[i] );
+ p += strlen( p );
+ }
+ *p = '\0';
+ } else {
+ retstr = slapi_ch_strdup( "" );
+ }
+
+ return (void *)retstr;
+}
+
+static void *ldbm_config_db_tx_max_get(void *arg)
+{
+ struct ldbminfo *li = (struct ldbminfo *) arg;
+
+ return (void *) li->li_dblayer_private->dblayer_tx_max;
+}
+
+static int ldbm_config_db_tx_max_set(
+ void *arg,
+ void *value,
+ char *errorbuf,
+ int phase,
+ int apply
+)
+{
+ struct ldbminfo *li = (struct ldbminfo *) arg;
+ int retval = LDAP_SUCCESS;
+ int val = (int) value;
+
+ if (apply) {
+ li->li_dblayer_private->dblayer_tx_max = val;
+ }
+
+ return retval;
+}
+
+
+/*------------------------------------------------------------------------
+ * Configuration array for ldbm and dblayer variables
+ *----------------------------------------------------------------------*/
+static config_info ldbm_config[] = {
+ {CONFIG_LOOKTHROUGHLIMIT, CONFIG_TYPE_INT, "5000", &ldbm_config_lookthroughlimit_get, &ldbm_config_lookthroughlimit_set, CONFIG_FLAG_ALWAYS_SHOW|CONFIG_FLAG_ALLOW_RUNNING_CHANGE},
+ {CONFIG_MODE, CONFIG_TYPE_INT_OCTAL, "0600", &ldbm_config_mode_get, &ldbm_config_mode_set, CONFIG_FLAG_ALWAYS_SHOW|CONFIG_FLAG_ALLOW_RUNNING_CHANGE},
+ {CONFIG_IDLISTSCANLIMIT, CONFIG_TYPE_INT, "4000", &ldbm_config_allidsthreshold_get, &ldbm_config_allidsthreshold_set, CONFIG_FLAG_ALWAYS_SHOW},
+ {CONFIG_DIRECTORY, CONFIG_TYPE_STRING, "", &ldbm_config_directory_get, &ldbm_config_directory_set, CONFIG_FLAG_ALWAYS_SHOW|CONFIG_FLAG_ALLOW_RUNNING_CHANGE},
+ {CONFIG_DBCACHESIZE, CONFIG_TYPE_SIZE_T, "10000000", &ldbm_config_dbcachesize_get, &ldbm_config_dbcachesize_set, CONFIG_FLAG_ALWAYS_SHOW|CONFIG_FLAG_ALLOW_RUNNING_CHANGE},
+ {CONFIG_DBNCACHE, CONFIG_TYPE_INT, "0", &ldbm_config_dbncache_get, &ldbm_config_dbncache_set, CONFIG_FLAG_ALLOW_RUNNING_CHANGE},
+ {CONFIG_MAXPASSBEFOREMERGE, CONFIG_TYPE_INT, "100", &ldbm_config_maxpassbeforemerge_get, &ldbm_config_maxpassbeforemerge_set, 0},
+
+ /* dblayer config attributes */
+ {CONFIG_DB_LOGDIRECTORY, CONFIG_TYPE_STRING, "", &ldbm_config_db_logdirectory_get, &ldbm_config_db_logdirectory_set, CONFIG_FLAG_ALWAYS_SHOW},
+ {CONFIG_DB_DURABLE_TRANSACTIONS, CONFIG_TYPE_ONOFF, "on", &ldbm_config_db_durable_transactions_get, &ldbm_config_db_durable_transactions_set, CONFIG_FLAG_ALWAYS_SHOW},
+ {CONFIG_DB_CIRCULAR_LOGGING, CONFIG_TYPE_ONOFF, "on", &ldbm_config_db_circular_logging_get, &ldbm_config_db_circular_logging_set, 0},
+ {CONFIG_DB_TRANSACTION_LOGGING, CONFIG_TYPE_ONOFF, "on", &ldbm_config_db_transaction_logging_get, &ldbm_config_db_transaction_logging_set, CONFIG_FLAG_ALWAYS_SHOW},
+ {CONFIG_DB_CHECKPOINT_INTERVAL, CONFIG_TYPE_INT, "60", &ldbm_config_db_checkpoint_interval_get, &ldbm_config_db_checkpoint_interval_set, CONFIG_FLAG_ALWAYS_SHOW|CONFIG_FLAG_ALLOW_RUNNING_CHANGE},
+ {CONFIG_DB_TRANSACTION_BATCH, CONFIG_TYPE_INT, "0", &dblayer_get_batch_transactions, &dblayer_set_batch_transactions, CONFIG_FLAG_ALWAYS_SHOW|CONFIG_FLAG_ALLOW_RUNNING_CHANGE},
+ {CONFIG_DB_LOGBUF_SIZE, CONFIG_TYPE_SIZE_T, "0", &ldbm_config_db_logbuf_size_get, &ldbm_config_db_logbuf_size_set, CONFIG_FLAG_ALWAYS_SHOW},
+ {CONFIG_DB_PAGE_SIZE, CONFIG_TYPE_SIZE_T, "0", &ldbm_config_db_page_size_get, &ldbm_config_db_page_size_set, 0},
+ {CONFIG_DB_INDEX_PAGE_SIZE, CONFIG_TYPE_SIZE_T, "0", &ldbm_config_db_index_page_size_get, &ldbm_config_db_index_page_size_set, 0},
+ {CONFIG_DB_IDL_DIVISOR, CONFIG_TYPE_INT, "0", &ldbm_config_db_idl_divisor_get, &ldbm_config_db_idl_divisor_set, 0},
+ {CONFIG_DB_LOGFILE_SIZE, CONFIG_TYPE_SIZE_T, "0", &ldbm_config_db_logfile_size_get, &ldbm_config_db_logfile_size_set, 0},
+ {CONFIG_DB_TRICKLE_PERCENTAGE, CONFIG_TYPE_INT, "5", &ldbm_config_db_trickle_percentage_get, &ldbm_config_db_trickle_percentage_set, 0},
+ {CONFIG_DB_SPIN_COUNT, CONFIG_TYPE_INT, "0", &ldbm_config_db_spin_count_get, &ldbm_config_db_spin_count_set, 0},
+ {CONFIG_DB_VERBOSE, CONFIG_TYPE_ONOFF, "off", &ldbm_config_db_verbose_get, &ldbm_config_db_verbose_set, 0},
+ {CONFIG_DB_DEBUG, CONFIG_TYPE_ONOFF, "on", &ldbm_config_db_debug_get, &ldbm_config_db_debug_set, 0},
+ {CONFIG_DB_NAMED_REGIONS, CONFIG_TYPE_ONOFF, "off", &ldbm_config_db_named_regions_get, &ldbm_config_db_named_regions_set, 0},
+ {CONFIG_DB_LOCK, CONFIG_TYPE_INT, "10000", &ldbm_config_db_lock_get, &ldbm_config_db_lock_set, 0},
+ {CONFIG_DB_PRIVATE_MEM, CONFIG_TYPE_ONOFF, "off", &ldbm_config_db_private_mem_get, &ldbm_config_db_private_mem_set, 0},
+ {CONFIG_DB_PRIVATE_IMPORT_MEM, CONFIG_TYPE_ONOFF, "on", &ldbm_config_db_private_import_mem_get, &ldbm_config_db_private_import_mem_set, CONFIG_FLAG_ALWAYS_SHOW|CONFIG_FLAG_ALLOW_RUNNING_CHANGE},
+ {CONFIG_DB_SHM_KEY, CONFIG_TYPE_LONG, "389389", &ldbm_config_db_shm_key_get, &ldbm_config_db_shm_key_set, 0},
+ {CONFIG_DB_CACHE, CONFIG_TYPE_INT, "0", &ldbm_config_db_cache_get, &ldbm_config_db_cache_set, 0},
+ {CONFIG_DB_DEBUG_CHECKPOINTING, CONFIG_TYPE_ONOFF, "off", &ldbm_config_db_debug_checkpointing_get, &ldbm_config_db_debug_checkpointing_set, 0},
+ {CONFIG_DB_HOME_DIRECTORY, CONFIG_TYPE_STRING, "", &ldbm_config_db_home_directory_get, &ldbm_config_db_home_directory_set, 0},
+ {CONFIG_IMPORT_CACHE_AUTOSIZE, CONFIG_TYPE_INT, "-1", &ldbm_config_import_cache_autosize_get, &ldbm_config_import_cache_autosize_set, CONFIG_FLAG_ALWAYS_SHOW|CONFIG_FLAG_ALLOW_RUNNING_CHANGE},
+ {CONFIG_CACHE_AUTOSIZE, CONFIG_TYPE_INT, "0", &ldbm_config_cache_autosize_get, &ldbm_config_cache_autosize_set, 0},
+ {CONFIG_CACHE_AUTOSIZE_SPLIT, CONFIG_TYPE_INT, "50", &ldbm_config_cache_autosize_split_get, &ldbm_config_cache_autosize_split_set, 0},
+ {CONFIG_IMPORT_CACHESIZE, CONFIG_TYPE_SIZE_T, "20000000", &ldbm_config_import_cachesize_get, &ldbm_config_import_cachesize_set, CONFIG_FLAG_ALWAYS_SHOW|CONFIG_FLAG_ALLOW_RUNNING_CHANGE},
+#if defined(USE_NEW_IDL)
+ {CONFIG_IDL_SWITCH, CONFIG_TYPE_STRING, "new", &ldbm_config_idl_get_idl_new, &ldbm_config_idl_set_tune, CONFIG_FLAG_ALWAYS_SHOW},
+#else
+ {CONFIG_IDL_SWITCH, CONFIG_TYPE_STRING, "old", &ldbm_config_idl_get_idl_new, &ldbm_config_idl_set_tune, CONFIG_FLAG_ALWAYS_SHOW},
+#endif
+ {CONFIG_BYPASS_FILTER_TEST, CONFIG_TYPE_STRING, "on", &ldbm_config_get_bypass_filter_test, &ldbm_config_set_bypass_filter_test, CONFIG_FLAG_ALWAYS_SHOW|CONFIG_FLAG_ALLOW_RUNNING_CHANGE},
+ {CONFIG_USE_VLV_INDEX, CONFIG_TYPE_ONOFF, "on", &ldbm_config_get_use_vlv_index, &ldbm_config_set_use_vlv_index, CONFIG_FLAG_ALWAYS_SHOW|CONFIG_FLAG_ALLOW_RUNNING_CHANGE},
+ {CONFIG_DB_LOCKDOWN, CONFIG_TYPE_ONOFF, "off", &ldbm_config_db_lockdown_get, &ldbm_config_db_lockdown_set, 0},
+ {CONFIG_INDEX_BUFFER_SIZE, CONFIG_TYPE_INT, "0", &ldbm_config_index_buffer_size_get, &ldbm_config_index_buffer_size_set, 0},
+ {CONFIG_EXCLUDE_FROM_EXPORT, CONFIG_TYPE_STRING,
+ CONFIG_EXCLUDE_FROM_EXPORT_DEFAULT_VALUE,
+ &ldbm_config_exclude_from_export_get,
+ &ldbm_config_exclude_from_export_set, CONFIG_FLAG_ALWAYS_SHOW},
+ {CONFIG_DB_TX_MAX, CONFIG_TYPE_INT, "200", &ldbm_config_db_tx_max_get, &ldbm_config_db_tx_max_set, 0},
+ {CONFIG_SERIAL_LOCK, CONFIG_TYPE_ONOFF, "on", &ldbm_config_serial_lock_get, &ldbm_config_serial_lock_set, CONFIG_FLAG_ALWAYS_SHOW|CONFIG_FLAG_ALLOW_RUNNING_CHANGE},
+ {CONFIG_USE_LEGACY_ERRORCODE, CONFIG_TYPE_ONOFF, "off", &ldbm_config_legacy_errcode_get, &ldbm_config_legacy_errcode_set, 0},
+ {NULL, 0, NULL, NULL, NULL, 0}
+};
+
+void ldbm_config_setup_default(struct ldbminfo *li)
+{
+ config_info *config;
+ char err_buf[BUFSIZ];
+
+ for (config = ldbm_config; config->config_name != NULL; config++) {
+ ldbm_config_set((void *)li, config->config_name, ldbm_config, NULL /* use default */, err_buf, CONFIG_PHASE_INITIALIZATION, 1 /* apply */);
+ }
+}
+
+void
+ldbm_config_read_instance_entries(struct ldbminfo *li, const char *backend_type)
+{
+ Slapi_PBlock *tmp_pb;
+ char basedn[BUFSIZ];
+ Slapi_Entry **entries = NULL;
+
+ /* Construct the base dn of the subtree that holds the instance entries. */
+ sprintf(basedn, "cn=%s, cn=plugins, cn=config", backend_type);
+
+ /* Do a search of the subtree containing the instance entries */
+ tmp_pb = slapi_pblock_new();
+ slapi_search_internal_set_pb(tmp_pb, basedn, LDAP_SCOPE_SUBTREE, "(objectclass=nsBackendInstance)", NULL, 0, NULL, NULL, li->li_identity, 0);
+ slapi_search_internal_pb (tmp_pb);
+ slapi_pblock_get(tmp_pb, SLAPI_PLUGIN_INTOP_SEARCH_ENTRIES, &entries);
+ if (entries!=NULL) {
+ int i;
+ for (i=0; entries[i]!=NULL; i++) {
+ ldbm_instance_add_instance_entry_callback(NULL, entries[i], NULL, NULL, NULL, li);
+ }
+ }
+
+ slapi_free_search_results_internal(tmp_pb);
+ slapi_pblock_destroy(tmp_pb);
+}
+
+/* Reads in any config information held in the dse for the ldbm plugin.
+ * Creates dse entries used to configure the ldbm plugin and dblayer
+ * if they don't already exist. Registers dse callback functions to
+ * maintain those dse entries. Returns 0 on success.
+ */
+int ldbm_config_load_dse_info(struct ldbminfo *li)
+{
+ Slapi_PBlock *search_pb;
+ Slapi_Entry **entries = NULL;
+ int res;
+ char dn[BUFSIZ];
+
+ /* We try to read the entry
+ * cn=config, cn=ldbm database, cn=plugins, cn=config. If the entry is
+ * there, then we process the config information it stores.
+ */
+ sprintf(dn, "cn=config, cn=%s, cn=plugins, cn=config",
+ li->li_plugin->plg_name);
+ search_pb = slapi_pblock_new();
+ slapi_search_internal_set_pb(search_pb, dn, LDAP_SCOPE_BASE,
+ "objectclass=*", NULL, 0, NULL, NULL, li->li_identity, 0);
+ slapi_search_internal_pb (search_pb);
+ slapi_pblock_get(search_pb, SLAPI_PLUGIN_INTOP_RESULT, &res);
+
+ if (LDAP_NO_SUCH_OBJECT == res) {
+ /* Add skeleten dse entries for the ldbm plugin */
+ ldbm_config_add_dse_entries(li, ldbm_skeleton_entries,
+ li->li_plugin->plg_name, NULL, NULL, 0);
+ } else if (res != LDAP_SUCCESS) {
+ LDAPDebug(LDAP_DEBUG_ANY, "Error accessing the ldbm config DSE\n",
+ 0, 0, 0);
+ return 1;
+ } else {
+ /* Need to parse the configuration information for the ldbm
+ * plugin that is held in the DSE. */
+ slapi_pblock_get(search_pb, SLAPI_PLUGIN_INTOP_SEARCH_ENTRIES,
+ &entries);
+ if (NULL == entries || entries[0] == NULL) {
+ LDAPDebug(LDAP_DEBUG_ANY, "Error accessing the ldbm config DSE\n",
+ 0, 0, 0);
+ return 1;
+ }
+ parse_ldbm_config_entry(li, entries[0], ldbm_config);
+ }
+
+ if (search_pb) {
+ slapi_free_search_results_internal(search_pb);
+ slapi_pblock_destroy(search_pb);
+ }
+
+ /* Find all the instance entries and create a Slapi_Backend and an
+ * ldbm_instance for each */
+ ldbm_config_read_instance_entries(li, li->li_plugin->plg_name);
+
+ /* setup the dse callback functions for the ldbm backend config entry */
+ sprintf(dn, "cn=config, cn=%s, cn=plugins, cn=config",
+ li->li_plugin->plg_name);
+ slapi_config_register_callback(SLAPI_OPERATION_SEARCH, DSE_FLAG_PREOP, dn,
+ LDAP_SCOPE_BASE, "(objectclass=*)", ldbm_config_search_entry_callback,
+ (void *) li);
+ slapi_config_register_callback(SLAPI_OPERATION_MODIFY, DSE_FLAG_PREOP, dn,
+ LDAP_SCOPE_BASE, "(objectclass=*)", ldbm_config_modify_entry_callback,
+ (void *) li);
+ slapi_config_register_callback(DSE_OPERATION_WRITE, DSE_FLAG_PREOP, dn,
+ LDAP_SCOPE_BASE, "(objectclass=*)", ldbm_config_search_entry_callback,
+ (void *) li);
+
+ /* setup the dse callback functions for the ldbm backend monitor entry */
+ sprintf(dn, "cn=monitor, cn=%s, cn=plugins, cn=config",
+ li->li_plugin->plg_name);
+ slapi_config_register_callback(SLAPI_OPERATION_SEARCH, DSE_FLAG_PREOP, dn,
+ LDAP_SCOPE_BASE, "(objectclass=*)", ldbm_back_monitor_search,
+ (void *)li);
+
+ /* And the ldbm backend database monitor entry */
+ sprintf(dn, "cn=database, cn=monitor, cn=%s, cn=plugins, cn=config",
+ li->li_plugin->plg_name);
+ slapi_config_register_callback(SLAPI_OPERATION_SEARCH, DSE_FLAG_PREOP, dn,
+ LDAP_SCOPE_BASE, "(objectclass=*)", ldbm_back_dbmonitor_search,
+ (void *)li);
+
+ /* setup the dse callback functions for the ldbm backend instance
+ * entries */
+ sprintf(dn, "cn=%s, cn=plugins, cn=config", li->li_plugin->plg_name);
+ slapi_config_register_callback(SLAPI_OPERATION_ADD, DSE_FLAG_PREOP, dn,
+ LDAP_SCOPE_SUBTREE, "(objectclass=nsBackendInstance)",
+ ldbm_instance_add_instance_entry_callback, (void *) li);
+ slapi_config_register_callback(SLAPI_OPERATION_ADD, DSE_FLAG_POSTOP, dn,
+ LDAP_SCOPE_SUBTREE, "(objectclass=nsBackendInstance)",
+ ldbm_instance_postadd_instance_entry_callback, (void *) li);
+ slapi_config_register_callback(SLAPI_OPERATION_DELETE, DSE_FLAG_PREOP, dn,
+ LDAP_SCOPE_SUBTREE, "(objectclass=nsBackendInstance)",
+ ldbm_instance_delete_instance_entry_callback, (void *) li);
+ slapi_config_register_callback(SLAPI_OPERATION_DELETE, DSE_FLAG_POSTOP, dn,
+ LDAP_SCOPE_SUBTREE, "(objectclass=nsBackendInstance)",
+ ldbm_instance_post_delete_instance_entry_callback, (void *) li);
+
+ return 0;
+}
+
+
+/* Utility function used in creating config entries. Using the
+ * config_info, this function gets info and formats in the correct
+ * way.
+ */
+void ldbm_config_get(void *arg, config_info *config, char *buf)
+{
+ char *tmp_string;
+
+ if (config == NULL) {
+ buf[0] = '\0';
+ }
+
+ switch(config->config_type) {
+ case CONFIG_TYPE_INT:
+ sprintf(buf, "%d", (int) config->config_get_fn(arg));
+ break;
+ case CONFIG_TYPE_INT_OCTAL:
+ sprintf(buf, "%o", (int) config->config_get_fn(arg));
+ break;
+ case CONFIG_TYPE_LONG:
+ sprintf(buf, "%ld", (long) config->config_get_fn(arg));
+ break;
+ case CONFIG_TYPE_SIZE_T:
+ sprintf(buf, "%lu", (size_t) config->config_get_fn(arg));
+ break;
+ case CONFIG_TYPE_STRING:
+ /* Remember the get function for strings returns memory
+ * that must be freed. */
+ tmp_string = (char *) config->config_get_fn(arg);
+ sprintf(buf, "%s", (char *) tmp_string);
+ slapi_ch_free((void **)&tmp_string);
+ break;
+ case CONFIG_TYPE_ONOFF:
+ if ((int) config->config_get_fn(arg)) {
+ sprintf(buf, "on");
+ } else {
+ sprintf(buf, "off");
+ }
+ break;
+ }
+}
+
+/*
+ * Returns:
+ * SLAPI_DSE_CALLBACK_ERROR on failure
+ * SLAPI_DSE_CALLBACK_OK on success
+ */
+int ldbm_config_search_entry_callback(Slapi_PBlock *pb, Slapi_Entry *e, Slapi_Entry *entryAfter, int *returncode, char *returntext, void *arg)
+{
+ char buf[BUFSIZ];
+ struct berval *vals[2];
+ struct berval val;
+ struct ldbminfo *li= (struct ldbminfo *) arg;
+ config_info *config;
+
+ vals[0] = &val;
+ vals[1] = NULL;
+
+ returntext[0] = '\0';
+
+ PR_Lock(li->li_config_mutex);
+
+ for(config = ldbm_config; config->config_name != NULL; config++) {
+ /* Go through the ldbm_config table and fill in the entry. */
+
+ if (!(config->config_flags & (CONFIG_FLAG_ALWAYS_SHOW | CONFIG_FLAG_PREVIOUSLY_SET))) {
+ /* This config option shouldn't be shown */
+ continue;
+ }
+
+ ldbm_config_get((void *) li, config, buf);
+
+ val.bv_val = buf;
+ val.bv_len = strlen(buf);
+ slapi_entry_attr_replace(e, config->config_name, vals);
+ }
+
+ PR_Unlock(li->li_config_mutex);
+
+ *returncode = LDAP_SUCCESS;
+ return SLAPI_DSE_CALLBACK_OK;
+}
+
+
+int ldbm_config_ignored_attr(char *attr_name)
+{
+ /* These are the names of attributes that are in the
+ * config entries but are not config attributes. */
+ if (!strcasecmp("objectclass", attr_name) ||
+ !strcasecmp("cn", attr_name) ||
+ !strcasecmp("creatorsname", attr_name) ||
+ !strcasecmp("modifiersname", attr_name) ||
+ !strcasecmp("createtimestamp", attr_name) ||
+ !strcasecmp("numsubordinates", attr_name) ||
+ !strcasecmp("modifytimestamp", attr_name)) {
+ return 1;
+ } else {
+ return 0;
+ }
+}
+
+/* Returns LDAP_SUCCESS on success */
+int ldbm_config_set(void *arg, char *attr_name, config_info *config_array, struct berval *bval, char *err_buf, int phase, int apply_mod)
+{
+ config_info *config;
+ int use_default;
+ int int_val;
+ long long_val;
+ size_t sz_val;
+ PRInt64 llval;
+ int maxint = (int)(((unsigned int)~0)>>1);
+ int minint = ~maxint;
+ PRInt64 llmaxint;
+ PRInt64 llminint;
+ int err = 0;
+ char *str_val;
+ int retval = 0;
+
+ LL_I2L(llmaxint, maxint);
+ LL_I2L(llminint, minint);
+
+ config = get_config_info(config_array, attr_name);
+ if (NULL == config) {
+ LDAPDebug(LDAP_DEBUG_CONFIG, "Unknown config attribute %s\n", attr_name, 0, 0);
+ sprintf(err_buf, "Unknown config attribute %s\n", attr_name);
+ return LDAP_SUCCESS; /* Ignore unknown attributes */
+ }
+
+ /* Some config attrs can't be changed while the server is running. */
+ if (phase == CONFIG_PHASE_RUNNING &&
+ !(config->config_flags & CONFIG_FLAG_ALLOW_RUNNING_CHANGE)) {
+ sprintf(err_buf, "%s can't be modified while the server is running.\n", attr_name);
+ LDAPDebug(LDAP_DEBUG_ANY, "%s", err_buf, 0, 0);
+ return LDAP_UNWILLING_TO_PERFORM;
+ }
+
+ /* If the config phase is initialization or if bval is NULL, we will use
+ * the default value for the attribute. */
+ if (CONFIG_PHASE_INITIALIZATION == phase || NULL == bval) {
+ use_default = 1;
+ } else {
+ use_default = 0;
+
+ /* Since we are setting the value for the config attribute, we
+ * need to turn on the CONFIG_FLAG_PREVIOUSLY_SET flag to make
+ * sure this attribute is shown. */
+ config->config_flags |= CONFIG_FLAG_PREVIOUSLY_SET;
+ }
+
+ switch(config->config_type) {
+ case CONFIG_TYPE_INT:
+ if (use_default) {
+ str_val = config->config_default_value;
+ } else {
+ str_val = bval->bv_val;
+ }
+ /* get the value as a 64 bit value */
+ llval = db_atoi(str_val, &err);
+ /* check for parsing error (e.g. not a number) */
+ if (err) {
+ sprintf(err_buf, "Error: value %s for attr %s is not a number\n",
+ str_val, attr_name);
+ LDAPDebug(LDAP_DEBUG_ANY, "%s", err_buf, 0, 0);
+ return LDAP_UNWILLING_TO_PERFORM;
+ /* check for overflow */
+ } else if (LL_CMP(llval, >, llmaxint)) {
+ sprintf(err_buf, "Error: value %s for attr %s is greater than the maximum %d\n",
+ str_val, attr_name, maxint);
+ LDAPDebug(LDAP_DEBUG_ANY, "%s", err_buf, 0, 0);
+ return LDAP_UNWILLING_TO_PERFORM;
+ /* check for underflow */
+ } else if (LL_CMP(llval, <, llminint)) {
+ sprintf(err_buf, "Error: value %s for attr %s is less than the minimum %d\n",
+ str_val, attr_name, minint);
+ LDAPDebug(LDAP_DEBUG_ANY, "%s", err_buf, 0, 0);
+ return LDAP_UNWILLING_TO_PERFORM;
+ }
+ /* convert 64 bit value to 32 bit value */
+ LL_L2I(int_val, llval);
+ retval = config->config_set_fn(arg, (void *) int_val, err_buf, phase, apply_mod);
+ break;
+ case CONFIG_TYPE_INT_OCTAL:
+ if (use_default) {
+ int_val = (int) strtol(config->config_default_value, NULL, 8);
+ } else {
+ int_val = (int) strtol((char *)bval->bv_val, NULL, 8);
+ }
+ retval = config->config_set_fn(arg, (void *) int_val, err_buf, phase, apply_mod);
+ break;
+ case CONFIG_TYPE_LONG:
+ if (use_default) {
+ str_val = config->config_default_value;
+ } else {
+ str_val = bval->bv_val;
+ }
+ /* get the value as a 64 bit value */
+ llval = db_atoi(str_val, &err);
+ /* check for parsing error (e.g. not a number) */
+ if (err) {
+ sprintf(err_buf, "Error: value %s for attr %s is not a number\n",
+ str_val, attr_name);
+ LDAPDebug(LDAP_DEBUG_ANY, "%s", err_buf, 0, 0);
+ return LDAP_UNWILLING_TO_PERFORM;
+ /* check for overflow */
+ } else if (LL_CMP(llval, >, llmaxint)) {
+ sprintf(err_buf, "Error: value %s for attr %s is greater than the maximum %d\n",
+ str_val, attr_name, maxint);
+ LDAPDebug(LDAP_DEBUG_ANY, "%s", err_buf, 0, 0);
+ return LDAP_UNWILLING_TO_PERFORM;
+ /* check for underflow */
+ } else if (LL_CMP(llval, <, llminint)) {
+ sprintf(err_buf, "Error: value %s for attr %s is less than the minimum %d\n",
+ str_val, attr_name, minint);
+ LDAPDebug(LDAP_DEBUG_ANY, "%s", err_buf, 0, 0);
+ return LDAP_UNWILLING_TO_PERFORM;
+ }
+ /* convert 64 bit value to 32 bit value */
+ LL_L2I(long_val, llval);
+ retval = config->config_set_fn(arg, (void *) long_val, err_buf, phase, apply_mod);
+ break;
+ case CONFIG_TYPE_SIZE_T:
+ if (use_default) {
+ str_val = config->config_default_value;
+ } else {
+ str_val = bval->bv_val;
+ }
+
+ /* get the value as a size_t value */
+ sz_val = db_strtoul(str_val, &err);
+
+ /* check for parsing error (e.g. not a number) */
+ if (err == EINVAL) {
+ sprintf(err_buf, "Error: value %s for attr %s is not a number\n",
+ str_val, attr_name);
+ LDAPDebug(LDAP_DEBUG_ANY, "%s", err_buf, 0, 0);
+ return LDAP_UNWILLING_TO_PERFORM;
+ /* check for overflow */
+ } else if (err == ERANGE) {
+ sprintf(err_buf, "Error: value %s for attr %s is outside the range of representable values\n",
+ str_val, attr_name);
+ LDAPDebug(LDAP_DEBUG_ANY, "%s", err_buf, 0, 0);
+ return LDAP_UNWILLING_TO_PERFORM;
+ }
+ retval = config->config_set_fn(arg, (void *) sz_val, err_buf, phase, apply_mod);
+ break;
+ case CONFIG_TYPE_STRING:
+ if (use_default) {
+ retval = config->config_set_fn(arg, config->config_default_value, err_buf, phase, apply_mod);
+ } else {
+ retval = config->config_set_fn(arg, bval->bv_val, err_buf, phase, apply_mod);
+ }
+ break;
+ case CONFIG_TYPE_ONOFF:
+ if (use_default) {
+ int_val = !strcasecmp(config->config_default_value, "on");
+ } else {
+ int_val = !strcasecmp((char *) bval->bv_val, "on");
+ }
+ retval = config->config_set_fn(arg, (void *) int_val, err_buf, phase, apply_mod);
+ break;
+ }
+
+ return retval;
+}
+
+
+static int parse_ldbm_config_entry(struct ldbminfo *li, Slapi_Entry *e, config_info *config_array)
+{
+ Slapi_Attr *attr = NULL;
+
+ for (slapi_entry_first_attr(e, &attr); attr; slapi_entry_next_attr(e, attr, &attr)) {
+ char *attr_name = NULL;
+ Slapi_Value *sval = NULL;
+ struct berval *bval;
+ char err_buf[BUFSIZ];
+
+ slapi_attr_get_type(attr, &attr_name);
+
+ /* There are some attributes that we don't care about, like objectclass. */
+ if (ldbm_config_ignored_attr(attr_name)) {
+ continue;
+ }
+
+ slapi_attr_first_value(attr, &sval);
+ bval = (struct berval *) slapi_value_get_berval(sval);
+
+ if (ldbm_config_set(li, attr_name, config_array, bval, err_buf, CONFIG_PHASE_STARTUP, 1 /* apply */) != LDAP_SUCCESS) {
+ LDAPDebug(LDAP_DEBUG_ANY, "Error with config attribute %s : %s\n", attr_name, err_buf, 0);
+ return 1;
+ }
+ }
+ return 0;
+}
+
+/*
+ * Returns:
+ * SLAPI_DSE_CALLBACK_ERROR on failure
+ * SLAPI_DSE_CALLBACK_OK on success
+ */
+int ldbm_config_modify_entry_callback(Slapi_PBlock *pb, Slapi_Entry* entryBefore, Slapi_Entry* e, int *returncode, char *returntext, void *arg)
+{
+ int i;
+ char *attr_name;
+ LDAPMod **mods;
+ int rc = LDAP_SUCCESS;
+ int apply_mod = 0;
+ struct ldbminfo *li= (struct ldbminfo *) arg;
+
+ /* This lock is probably way too conservative, but we don't expect much
+ * contention for it. */
+ PR_Lock(li->li_config_mutex);
+
+ slapi_pblock_get( pb, SLAPI_MODIFY_MODS, &mods );
+
+ returntext[0] = '\0';
+
+ /*
+ * First pass: set apply mods to 0 so only input validation will be done;
+ * 2nd pass: set apply mods to 1 to apply changes to internal storage
+ */
+ for ( apply_mod = 0; apply_mod <= 1 && LDAP_SUCCESS == rc; apply_mod++ ) {
+ for (i = 0; mods[i] && LDAP_SUCCESS == rc; i++) {
+ attr_name = mods[i]->mod_type;
+
+ /* There are some attributes that we don't care about, like modifiersname. */
+ if (ldbm_config_ignored_attr(attr_name)) {
+ continue;
+ }
+
+ if ((mods[i]->mod_op & LDAP_MOD_DELETE) ||
+ ((mods[i]->mod_op & ~LDAP_MOD_BVALUES) == LDAP_MOD_ADD)) {
+ rc= LDAP_UNWILLING_TO_PERFORM;
+ sprintf(returntext, "%s attributes is not allowed",
+ (mods[i]->mod_op & LDAP_MOD_DELETE) ? "Deleting" : "Adding");
+ } else if (mods[i]->mod_op & LDAP_MOD_REPLACE) {
+ /* This assumes there is only one bval for this mod. */
+ rc = ldbm_config_set((void *) li, attr_name, ldbm_config,
+ ( mods[i]->mod_bvalues == NULL ) ? NULL
+ : mods[i]->mod_bvalues[0], returntext,
+ ((li->li_flags&LI_FORCE_MOD_CONFIG)?
+ CONFIG_PHASE_INTERNAL:CONFIG_PHASE_RUNNING),
+ apply_mod);
+ }
+ }
+ }
+
+ PR_Unlock(li->li_config_mutex);
+
+ *returncode= rc;
+ if(LDAP_SUCCESS == rc) {
+ return SLAPI_DSE_CALLBACK_OK;
+ }
+ else {
+ return SLAPI_DSE_CALLBACK_ERROR;
+ }
+}
+
+
+/* This function is used to set config attributes. It can be used as a
+ * shortcut to doing an internal modify operation on the config DSE.
+ */
+void ldbm_config_internal_set(struct ldbminfo *li, char *attrname, char *value)
+{
+ char err_buf[BUFSIZ];
+ struct berval bval;
+
+ bval.bv_val = value;
+ bval.bv_len = strlen(value);
+
+ if (ldbm_config_set((void *) li, attrname, ldbm_config, &bval,
+ err_buf, CONFIG_PHASE_INTERNAL, 1 /* apply */) != LDAP_SUCCESS) {
+ LDAPDebug(LDAP_DEBUG_ANY,
+ "Internal Error: Error setting instance config attr %s to %s: %s\n",
+ attrname, value, err_buf);
+ exit(1);
+ }
+}
+
+/*
+ * replace_ldbm_config_value:
+ * - update an ldbm database config value
+ */
+void replace_ldbm_config_value(char *conftype, char *val, struct ldbminfo *li)
+{
+ Slapi_PBlock pb;
+ Slapi_Mods smods;
+
+ pblock_init(&pb);
+ slapi_mods_init(&smods, 1);
+ slapi_mods_add(&smods, LDAP_MOD_REPLACE, conftype, strlen(val), val);
+ slapi_modify_internal_set_pb(&pb,
+ "cn=config,cn=ldbm database,cn=plugins,cn=config",
+ slapi_mods_get_ldapmods_byref(&smods),
+ NULL, NULL, li->li_identity, 0);
+ slapi_modify_internal_pb(&pb);
+ pblock_done(&pb);
+}
diff --git a/ldap/servers/slapd/back-ldbm/ldbm_config.h b/ldap/servers/slapd/back-ldbm/ldbm_config.h
new file mode 100644
index 00000000..a26a73ed
--- /dev/null
+++ b/ldap/servers/slapd/back-ldbm/ldbm_config.h
@@ -0,0 +1,133 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+#ifndef _LDBM_CONFIG_H_
+#define _LDBM_CONFIG_H_
+
+struct config_info;
+typedef struct config_info config_info;
+
+typedef int config_set_fn_t(void *arg, void *value, char *errorbuf, int phase, int apply);
+typedef void *config_get_fn_t(void *arg);
+ /* The value for these is passed around as a
+ * void *, the actual value should be gotten
+ * by casting the void * as shown below. */
+#define CONFIG_TYPE_ONOFF 1 /* val = (int) value */
+#define CONFIG_TYPE_STRING 2 /* val = (char *) value - The get functions
+ * for this type must return alloced memory
+ * that should be freed by the caller. */
+#define CONFIG_TYPE_INT 3 /* val = (int) value */
+#define CONFIG_TYPE_LONG 4 /* val = (long) value */
+#define CONFIG_TYPE_INT_OCTAL 5 /* Same as CONFIG_TYPE_INT, but shown in
+ * octal */
+#define CONFIG_TYPE_SIZE_T 6 /* val = (size_t) value */
+
+/* How changes to some config attributes are handled depends on what
+ * "phase" the server is in. Initialization, reading the config
+ * information at startup, or actually running. */
+#define CONFIG_PHASE_INITIALIZATION 1
+#define CONFIG_PHASE_STARTUP 2
+#define CONFIG_PHASE_RUNNING 3
+#define CONFIG_PHASE_INTERNAL 4
+
+#define CONFIG_FLAG_PREVIOUSLY_SET 1
+#define CONFIG_FLAG_ALWAYS_SHOW 2
+#define CONFIG_FLAG_ALLOW_RUNNING_CHANGE 4
+
+struct config_info {
+ char *config_name;
+ int config_type;
+ char *config_default_value;
+ config_get_fn_t *config_get_fn;
+ config_set_fn_t *config_set_fn;
+ int config_flags;
+};
+
+#define CONFIG_INSTANCE "nsslapd-instance"
+#define CONFIG_LOOKTHROUGHLIMIT "nsslapd-lookthroughlimit"
+#define CONFIG_IDLISTSCANLIMIT "nsslapd-idlistscanlimit"
+#define CONFIG_DIRECTORY "nsslapd-directory"
+#define CONFIG_MODE "nsslapd-mode"
+#define CONFIG_DBCACHESIZE "nsslapd-dbcachesize"
+#define CONFIG_DBNCACHE "nsslapd-dbncache"
+#define CONFIG_MAXPASSBEFOREMERGE "nsslapd-maxpassbeforemerge"
+#define CONFIG_IMPORT_CACHE_AUTOSIZE "nsslapd-import-cache-autosize"
+#define CONFIG_CACHE_AUTOSIZE "nsslapd-cache-autosize"
+#define CONFIG_CACHE_AUTOSIZE_SPLIT "nsslapd-cache-autosize-split"
+#define CONFIG_IMPORT_CACHESIZE "nsslapd-import-cachesize"
+#define CONFIG_INDEX_BUFFER_SIZE "nsslapd-index-buffer-size"
+#define CONFIG_EXCLUDE_FROM_EXPORT "nsslapd-exclude-from-export"
+#define CONFIG_EXCLUDE_FROM_EXPORT_DEFAULT_VALUE \
+ "entrydn entryid dncomp parentid numSubordinates"
+
+/* dblayer config options - These are hidden from the user
+ * and can't be updated on the fly. */
+#define CONFIG_DB_LOGDIRECTORY "nsslapd-db-logdirectory"
+#define CONFIG_DB_DURABLE_TRANSACTIONS "nsslapd-db-durable-transaction"
+#define CONFIG_DB_CIRCULAR_LOGGING "nsslapd-db-circular-logging"
+#define CONFIG_DB_TRANSACTION_LOGGING "nsslapd-db-transaction-logging"
+#define CONFIG_DB_CHECKPOINT_INTERVAL "nsslapd-db-checkpoint-interval"
+#define CONFIG_DB_TRANSACTION_BATCH "nsslapd-db-transaction-batch-val"
+#define CONFIG_DB_LOGBUF_SIZE "nsslapd-db-logbuf-size"
+#define CONFIG_DB_PAGE_SIZE "nsslapd-db-page-size"
+#define CONFIG_DB_INDEX_PAGE_SIZE "nsslapd-db-index-page-size" /* With the new
+ idl design, the large 8Kbyte pages we use are not
+ optimal. The page pool churns very quickly as we add new IDs under a
+ sustained add load. Smaller pages stop this happening so much and
+ consequently make us spend less time flushing dirty pages on checkpoints.
+ But 8K is still a good page size for id2entry. So we now allow different
+ page sizes for the primary and secondary indices. */
+#define CONFIG_DB_IDL_DIVISOR "nsslapd-db-idl-divisor"
+#define CONFIG_DB_LOGFILE_SIZE "nsslapd-db-logfile-size"
+#define CONFIG_DB_TRICKLE_PERCENTAGE "nsslapd-db-trickle-percentage"
+#define CONFIG_DB_SPIN_COUNT "nsslapd-db-spin-count"
+#define CONFIG_DB_VERBOSE "nsslapd-db-verbose"
+#define CONFIG_DB_DEBUG "nsslapd-db-debug"
+#define CONFIG_DB_LOCK "nsslapd-db-locks"
+#define CONFIG_DB_NAMED_REGIONS "nsslapd-db-named-regions"
+#define CONFIG_DB_PRIVATE_MEM "nsslapd-db-private-mem"
+#define CONFIG_DB_PRIVATE_IMPORT_MEM "nsslapd-db-private-import-mem"
+#define CONFIG_DB_SHM_KEY "nsslapd-db-shm-key"
+#define CONFIG_DB_CACHE "nsslapd-db-cache"
+#define CONFIG_DB_DEBUG_CHECKPOINTING "nsslapd-db-debug-checkpointing"
+#define CONFIG_DB_HOME_DIRECTORY "nsslapd-db-home-directory"
+#define CONFIG_DB_LOCKDOWN "nsslapd-db-lockdown"
+#define CONFIG_DB_TX_MAX "nsslapd-db-tx-max"
+
+#define CONFIG_IDL_SWITCH "nsslapd-idl-switch"
+#define CONFIG_BYPASS_FILTER_TEST "nsslapd-search-bypass-filter-test"
+#define CONFIG_USE_VLV_INDEX "nsslapd-search-use-vlv-index"
+#define CONFIG_SERIAL_LOCK "nsslapd-serial-lock"
+
+/* instance config options */
+#define CONFIG_INSTANCE_CACHESIZE "nsslapd-cachesize"
+#define CONFIG_INSTANCE_CACHEMEMSIZE "nsslapd-cachememsize"
+#define CONFIG_INSTANCE_SUFFIX "nsslapd-suffix"
+#define CONFIG_INSTANCE_READONLY "nsslapd-readonly"
+#define CONFIG_INSTANCE_DIR "nsslapd-directory"
+
+#define CONFIG_INSTANCE_REQUIRE_INDEX "nsslapd-require-index"
+
+#define CONFIG_USE_LEGACY_ERRORCODE "nsslapd-do-not-use-vlv-error"
+
+#define LDBM_INSTANCE_CONFIG_DONT_WRITE 1
+
+/* Some fuctions in ldbm_config.c used by ldbm_instance_config.c */
+int ldbm_config_add_dse_entries(struct ldbminfo *li, char **entries, char *string1, char *string2, char *string3, int flags);
+int ldbm_config_add_dse_entry(struct ldbminfo *li, char *entry, int flags);
+void ldbm_config_get(void *arg, config_info *config, char *buf);
+int ldbm_config_set(void *arg, char *attr_name, config_info *config_array, struct berval *bval, char *err_buf, int phase, int apply_mod);
+int ldbm_config_ignored_attr(char *attr_name);
+
+/* Functions in ldbm_instance_config.c used in ldbm_config.c */
+int ldbm_instance_config_load_dse_info(ldbm_instance *inst);
+int ldbm_instance_config_add_index_entry(ldbm_instance *inst, int argc,
+ char **argv, int flags);
+int
+ldbm_instance_index_config_enable_index(ldbm_instance *inst, Slapi_Entry* e);
+int ldbm_instance_create_default_user_indexes(ldbm_instance *inst);
+
+
+#endif /* _LDBM_CONFIG_H_ */
diff --git a/ldap/servers/slapd/back-ldbm/ldbm_delete.c b/ldap/servers/slapd/back-ldbm/ldbm_delete.c
new file mode 100644
index 00000000..55d8f162
--- /dev/null
+++ b/ldap/servers/slapd/back-ldbm/ldbm_delete.c
@@ -0,0 +1,633 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+
+/* delete.c - ldbm backend delete routine */
+
+#include "back-ldbm.h"
+
+int
+ldbm_back_delete( Slapi_PBlock *pb )
+{
+ backend *be;
+ ldbm_instance *inst;
+ struct ldbminfo *li = NULL;
+ struct backentry *e = NULL;
+ struct backentry *tombstone = NULL;
+ char *dn = NULL;
+ back_txn txn;
+ back_txnid parent_txn;
+ int retval = -1;
+ char *msg;
+ char *errbuf = NULL;
+ int retry_count = 0;
+ int disk_full = 0;
+ int parent_found = 0;
+ modify_context parent_modify_c = {0};
+ int rc;
+ int ldap_result_code= LDAP_SUCCESS;
+ char *ldap_result_message= NULL;
+ Slapi_DN sdn;
+ char *e_uniqueid = NULL;
+ Slapi_DN *nscpEntrySDN = NULL;
+ int dblock_acquired= 0;
+ Slapi_Operation *operation;
+ CSN *opcsn = NULL;
+ int is_fixup_operation = 0;
+ int is_replicated_operation= 0;
+ int is_tombstone_entry = 0; /* True if the current entry is alreday a tombstone */
+ int delete_tombstone_entry = 0; /* We must remove the given tombstone entry from the DB */
+ int create_tombstone_entry = 0; /* We perform a "regular" LDAP delete but since we use */
+ /* replication, we must create a new tombstone entry */
+ int tombstone_in_cache = 0;
+ entry_address *addr;
+ int addordel_flags = 0; /* passed to index_addordel */
+
+ slapi_pblock_get( pb, SLAPI_BACKEND, &be);
+ slapi_pblock_get( pb, SLAPI_PLUGIN_PRIVATE, &li );
+ slapi_pblock_get( pb, SLAPI_DELETE_TARGET, &dn );
+ slapi_pblock_get( pb, SLAPI_TARGET_ADDRESS, &addr);
+ slapi_pblock_get( pb, SLAPI_PARENT_TXN, (void**)&parent_txn );
+ slapi_pblock_get( pb, SLAPI_OPERATION, &operation );
+ slapi_pblock_get( pb, SLAPI_IS_REPLICATED_OPERATION, &is_replicated_operation );
+
+ if (pb->pb_conn)
+ {
+ slapi_log_error (SLAPI_LOG_TRACE, "ldbm_back_delete", "enter conn=%d op=%d\n", pb->pb_conn->c_connid, operation->o_opid);
+ }
+
+ is_fixup_operation = operation_is_flag_set(operation,OP_FLAG_REPL_FIXUP);
+ delete_tombstone_entry = operation_is_flag_set(operation, OP_FLAG_TOMBSTONE_ENTRY);
+
+ inst = (ldbm_instance *) be->be_instance_info;
+
+ slapi_sdn_init_dn_byref(&sdn,dn);
+
+ dblayer_txn_init(li,&txn);
+
+ /* The dblock serializes writes to the database,
+ * which reduces deadlocking in the db code,
+ * which means that we run faster.
+ *
+ * But, this lock is re-enterant for the fixup
+ * operations that the URP code in the Replication
+ * plugin generates.
+ */
+ if(SERIALLOCK(li) && !operation_is_flag_set(operation,OP_FLAG_REPL_FIXUP))
+ {
+ dblayer_lock_backend(be);
+ dblock_acquired= 1;
+ }
+
+ /*
+ * We are about to pass the last abandon test, so from now on we are
+ * committed to finish this operation. Set status to "will complete"
+ * before we make our last abandon check to avoid race conditions in
+ * the code that processes abandon operations.
+ */
+ if (operation) {
+ operation->o_status = SLAPI_OP_STATUS_WILL_COMPLETE;
+ }
+ if ( slapi_op_abandoned( pb ) ) {
+ goto error_return;
+ }
+
+ /* Don't call pre-op for Tombstone entries */
+ if (!delete_tombstone_entry)
+ {
+ /*
+ * Some present state information is passed through the PBlock to the
+ * backend pre-op plugin. To ensure a consistent snapshot of this state
+ * we wrap the reading of the entry with the dblock.
+ */
+ ldap_result_code= get_copy_of_entry(pb, addr, &txn, SLAPI_DELETE_EXISTING_ENTRY, !is_replicated_operation);
+ slapi_pblock_set(pb, SLAPI_RESULT_CODE, &ldap_result_code);
+ if(plugin_call_plugins(pb, SLAPI_PLUGIN_BE_PRE_DELETE_FN)==-1)
+ {
+ /*
+ * Plugin indicated some kind of failure,
+ * or that this Operation became a No-Op.
+ */
+ slapi_pblock_get(pb, SLAPI_RESULT_CODE, &ldap_result_code);
+ goto error_return;
+ }
+ }
+
+
+ /* find and lock the entry we are about to modify */
+ if ( (e = find_entry2modify( pb, be, addr, NULL )) == NULL )
+ {
+ ldap_result_code= -1;
+ goto error_return; /* error result sent by find_entry2modify() */
+ }
+
+ if ( slapi_entry_has_children( e->ep_entry ) )
+ {
+ ldap_result_code= LDAP_NOT_ALLOWED_ON_NONLEAF;
+ goto error_return;
+ }
+
+ /*
+ * Sanity check to avoid to delete a non-tombstone or to tombstone again
+ * a tombstone entry. This should not happen (see bug 561003).
+ */
+ is_tombstone_entry = slapi_entry_flag_is_set(e->ep_entry, SLAPI_ENTRY_FLAG_TOMBSTONE);
+ if (delete_tombstone_entry) {
+ PR_ASSERT(is_tombstone_entry);
+ if (!is_tombstone_entry) {
+ slapi_log_error(SLAPI_LOG_FATAL, "ldbm_back_delete",
+ "Attempt to delete a non-tombstone entry %s\n", dn);
+ delete_tombstone_entry = 0;
+ }
+ } else {
+ PR_ASSERT(!is_tombstone_entry);
+ if (is_tombstone_entry) {
+ slapi_log_error(SLAPI_LOG_FATAL, "ldbm_back_delete",
+ "Attempt to Tombstone again a tombstone entry %s\n", dn);
+ delete_tombstone_entry = 1;
+ }
+ }
+
+ /*
+ * If a CSN is set, we need to tombstone the entry,
+ * rather than deleting it outright.
+ */
+ opcsn = operation_get_csn (operation);
+ if (!delete_tombstone_entry)
+ {
+ if (opcsn == NULL && !is_fixup_operation && operation->o_csngen_handler)
+ {
+ /*
+ * Current op is a user request. Opcsn will be assigned
+ * by entry_assign_operation_csn() if the dn is in an
+ * updatable replica.
+ */
+ opcsn = entry_assign_operation_csn ( pb, e->ep_entry, NULL );
+ }
+ if (opcsn != NULL)
+ {
+ if (!is_fixup_operation)
+ {
+ entry_set_maxcsn (e->ep_entry, opcsn);
+ }
+ /*
+ * We are dealing with replication and if we haven't been called to
+ * remove a tombstone, then it's because we want to create a new one.
+ */
+ if ( slapi_operation_get_replica_attr (pb, operation, "nsds5ReplicaTombstonePurgeInterval", &create_tombstone_entry) == 0)
+ {
+ create_tombstone_entry = (create_tombstone_entry < 0) ? 0 : 1;
+ }
+ }
+ }
+
+#if DEBUG
+ slapi_log_error(SLAPI_LOG_REPL, "ldbm_back_delete",
+ "entry: %s - flags: delete %d is_tombstone_entry %d create %d \n",
+ dn, delete_tombstone_entry, is_tombstone_entry, create_tombstone_entry);
+#endif
+
+ /* Save away a copy of the entry, before modifications */
+ slapi_pblock_set( pb, SLAPI_ENTRY_PRE_OP, slapi_entry_dup( e->ep_entry ));
+
+ /* JCMACL - Shouldn't the access check be before the has children check...
+ * otherwise we're revealing the fact that an entry exists and has children */
+ ldap_result_code = plugin_call_acl_plugin (pb, e->ep_entry, NULL, NULL, SLAPI_ACL_DELETE,
+ ACLPLUGIN_ACCESS_DEFAULT, &errbuf );
+ if ( ldap_result_code != LDAP_SUCCESS )
+ {
+ ldap_result_message= errbuf;
+ goto error_return;
+ }
+
+ /*
+ * Get the entry's parent. We do this here because index_read
+ * seems to deadlock the database when dblayer_txn_begin is
+ * called.
+ */
+ if (!delete_tombstone_entry)
+ {
+ Slapi_DN parentsdn;
+
+ slapi_sdn_init(&parentsdn);
+ slapi_sdn_get_backend_parent(&sdn,&parentsdn,pb->pb_backend);
+ if ( !slapi_sdn_isempty(&parentsdn) )
+ {
+ struct backentry *parent = NULL;
+ entry_address parent_addr;
+
+ parent_addr.dn = (char*)slapi_sdn_get_ndn (&parentsdn);
+ parent_addr.uniqueid = NULL;
+ parent = find_entry2modify_only(pb,be,&parent_addr,&txn);
+ if (NULL != parent) {
+ int isglue;
+ size_t haschildren = 0;
+
+ /* Unfortunately findentry doesn't tell us whether it just didn't find the entry, or if
+ there was an error, so we have to assume that the parent wasn't found */
+ parent_found = 1;
+
+ /* Modify the parent in memory */
+ modify_init(&parent_modify_c,parent);
+ retval = parent_update_on_childchange(&parent_modify_c,2,&haschildren); /* 2==delete */\
+ /* The modify context now contains info needed later */
+ if (0 != retval) {
+ ldap_result_code= LDAP_OPERATIONS_ERROR;
+ goto error_return;
+ }
+
+ /*
+ * Replication urp_post_delete will delete the parent entry
+ * if it is a glue entry without any more children.
+ * Those urp condition checkings are done here to
+ * save unnecessary entry dup.
+ */
+ isglue = slapi_entry_attr_hasvalue (parent_modify_c.new_entry->ep_entry,
+ SLAPI_ATTR_OBJECTCLASS, "glue");
+ if ( opcsn && parent_modify_c.new_entry && !haschildren && isglue)
+ {
+ slapi_pblock_set ( pb, SLAPI_DELETE_GLUE_PARENT_ENTRY,
+ slapi_entry_dup (parent_modify_c.new_entry->ep_entry) );
+ }
+ }
+ }
+ slapi_sdn_done(&parentsdn);
+ }
+
+ if(create_tombstone_entry)
+ {
+ /*
+ * The entry is not removed from the disk when we tombstone an
+ * entry. We change the DN, add objectclass=tombstone, and record
+ * the UniqueID of the parent entry.
+ */
+ const char *childuniqueid= slapi_entry_get_uniqueid(e->ep_entry);
+ const char *parentuniqueid= NULL;
+ char *tombstone_dn = compute_entry_tombstone_dn(slapi_entry_get_dn(e->ep_entry),
+ childuniqueid);
+ Slapi_Value *tomb_value;
+
+ nscpEntrySDN = slapi_entry_get_sdn(e->ep_entry);
+
+ /* Copy the entry unique_id for URP conflict checking */
+ e_uniqueid = slapi_ch_strdup(childuniqueid);
+
+ if(parent_modify_c.old_entry!=NULL)
+ {
+ /* The suffix entry has no parent */
+ parentuniqueid= slapi_entry_get_uniqueid(parent_modify_c.old_entry->ep_entry);
+ }
+ tombstone = backentry_dup( e );
+ slapi_entry_set_dn(tombstone->ep_entry,tombstone_dn); /* Consumes DN */
+ /* Set tombstone flag on ep_entry */
+ slapi_entry_set_flag(tombstone->ep_entry, SLAPI_ENTRY_FLAG_TOMBSTONE);
+
+ if(parentuniqueid!=NULL)
+ {
+ /* The suffix entry has no parent */
+ slapi_entry_add_string(tombstone->ep_entry, SLAPI_ATTR_VALUE_PARENT_UNIQUEID, parentuniqueid);
+ }
+ if(nscpEntrySDN!=NULL)
+ {
+ slapi_entry_add_string(tombstone->ep_entry, SLAPI_ATTR_NSCP_ENTRYDN, slapi_sdn_get_ndn(nscpEntrySDN));
+ }
+ tomb_value = slapi_value_new_string(SLAPI_ATTR_VALUE_TOMBSTONE);
+ value_update_csn(tomb_value, CSN_TYPE_VALUE_UPDATED,
+ operation_get_csn(operation));
+ slapi_entry_add_value(tombstone->ep_entry, SLAPI_ATTR_OBJECTCLASS, tomb_value);
+ slapi_value_free(&tomb_value);
+
+ /* XXXggood above used to be: slapi_entry_add_string(tombstone->ep_entry, SLAPI_ATTR_OBJECTCLASS, SLAPI_ATTR_VALUE_TOMBSTONE); */
+ /* JCMREPL - Add a description of what's going on? */
+ }
+
+ /*
+ * So, we believe that no code up till here actually added anything
+ * to the persistent store. From now on, we're transacted
+ */
+
+ for (retry_count = 0; retry_count < RETRY_TIMES; retry_count++) {
+ if (retry_count > 0) {
+ dblayer_txn_abort(li,&txn);
+ /* We're re-trying */
+ LDAPDebug( LDAP_DEBUG_TRACE, "Delete Retrying Transaction\n", 0, 0, 0 );
+#ifndef LDBM_NO_BACKOFF_DELAY
+ {
+ PRIntervalTime interval;
+ interval = PR_MillisecondsToInterval(slapi_rand() % 100);
+ DS_Sleep(interval);
+ }
+#endif
+ }
+ retval = dblayer_txn_begin(li,parent_txn,&txn);
+ if (0 != retval) {
+ if (LDBM_OS_ERR_IS_DISKFULL(retval)) disk_full = 1;
+ ldap_result_code= LDAP_OPERATIONS_ERROR;
+ goto error_return;
+ }
+ if(create_tombstone_entry)
+ {
+ /*
+ * The entry is not removed from the disk when we tombstone an
+ * entry. We change the DN, add objectclass=tombstone, and record
+ * the UniqueID of the parent entry.
+ */
+ retval = id2entry_add( be, tombstone, &txn );
+ if (DB_LOCK_DEADLOCK == retval) {
+ LDAPDebug( LDAP_DEBUG_ARGS, "delete 1 DB_LOCK_DEADLOCK\n", 0, 0, 0 );
+ /* Abort and re-try */
+ continue;
+ }
+ if (0 != retval) {
+ LDAPDebug( LDAP_DEBUG_ANY, "id2entry_add failed, err=%d %s\n",
+ retval, (msg = dblayer_strerror( retval )) ? msg : "", 0 );
+ if (LDBM_OS_ERR_IS_DISKFULL(retval)) disk_full = 1;
+ ldap_result_code= LDAP_OPERATIONS_ERROR;
+ goto error_return;
+ }
+ tombstone_in_cache = 1;
+ }
+ else
+ {
+ /* delete the entry from disk */
+ retval = id2entry_delete( be, e, &txn );
+ if (DB_LOCK_DEADLOCK == retval)
+ {
+ LDAPDebug( LDAP_DEBUG_ARGS, "delete 2 DEADLOCK\n", 0, 0, 0 );
+ /* Retry txn */
+ continue;
+ }
+ if (retval != 0 ) {
+ if (retval == DB_RUNRECOVERY ||
+ LDBM_OS_ERR_IS_DISKFULL(retval)) {
+ disk_full = 1;
+ }
+ ldap_result_code= LDAP_OPERATIONS_ERROR;
+ goto error_return;
+ }
+ }
+ /* delete from attribute indexes */
+ addordel_flags = BE_INDEX_DEL|BE_INDEX_PRESENCE;
+ if (delete_tombstone_entry)
+ {
+ addordel_flags |= BE_INDEX_TOMBSTONE; /* tell index code we are deleting a tombstone */
+ }
+ retval = index_addordel_entry( be, e, addordel_flags, &txn );
+ if (DB_LOCK_DEADLOCK == retval)
+ {
+ LDAPDebug( LDAP_DEBUG_ARGS, "delete 1 DEADLOCK\n", 0, 0, 0 );
+ /* Retry txn */
+ continue;
+ }
+ if (retval != 0) {
+ LDAPDebug( LDAP_DEBUG_TRACE, "index_del_entry failed\n", 0, 0, 0 );
+ ldap_result_code= LDAP_OPERATIONS_ERROR;
+ goto error_return;
+ }
+ if(create_tombstone_entry)
+ {
+ /*
+ * The tombstone entry is removed from all attribute indexes
+ * above, but we want it to remain in the nsUniqueID and nscpEntryDN indexes
+ * and for objectclass=tombstone.
+ */
+ retval = index_addordel_string(be,SLAPI_ATTR_OBJECTCLASS,SLAPI_ATTR_VALUE_TOMBSTONE,tombstone->ep_id,BE_INDEX_ADD,&txn);
+ if (DB_LOCK_DEADLOCK == retval) {
+ LDAPDebug( LDAP_DEBUG_ARGS, "delete 4 DB_LOCK_DEADLOCK\n", 0, 0, 0 );
+ /* Retry txn */
+ continue;
+ }
+ if (0 != retval) {
+ LDAPDebug( LDAP_DEBUG_TRACE, "delete 1 BAD, err=%d %s\n",
+ retval, (msg = dblayer_strerror( retval )) ? msg : "", 0 );
+ if (LDBM_OS_ERR_IS_DISKFULL(retval)) disk_full = 1;
+ ldap_result_code= LDAP_OPERATIONS_ERROR;
+ goto error_return;
+ }
+ retval = index_addordel_string(be,SLAPI_ATTR_UNIQUEID,slapi_entry_get_uniqueid(tombstone->ep_entry),tombstone->ep_id,BE_INDEX_ADD,&txn);
+ if (DB_LOCK_DEADLOCK == retval) {
+ LDAPDebug( LDAP_DEBUG_ARGS, "delete 5 DB_LOCK_DEADLOCK\n", 0, 0, 0 );
+ /* Retry txn */
+ continue;
+ }
+ if (0 != retval) {
+ LDAPDebug( LDAP_DEBUG_TRACE, "delete 2 BAD, err=%d %s\n",
+ retval, (msg = dblayer_strerror( retval )) ? msg : "", 0 );
+ if (LDBM_OS_ERR_IS_DISKFULL(retval)) disk_full = 1;
+ ldap_result_code= LDAP_OPERATIONS_ERROR;
+ goto error_return;
+ }
+ retval = index_addordel_string(be,SLAPI_ATTR_NSCP_ENTRYDN, slapi_sdn_get_ndn(nscpEntrySDN),tombstone->ep_id,BE_INDEX_ADD,&txn);
+ if (DB_LOCK_DEADLOCK == retval) {
+ LDAPDebug( LDAP_DEBUG_ARGS, "delete 6 DB_LOCK_DEADLOCK\n", 0, 0, 0 );
+ /* Retry txn */
+ continue;
+ }
+ if (0 != retval) {
+ LDAPDebug( LDAP_DEBUG_TRACE, "delete 3 BAD, err=%d %s\n",
+ retval, (msg = dblayer_strerror( retval )) ? msg : "", 0 );
+ if (LDBM_OS_ERR_IS_DISKFULL(retval)) disk_full = 1;
+ ldap_result_code= LDAP_OPERATIONS_ERROR;
+ goto error_return;
+ }
+ } else if (delete_tombstone_entry)
+ {
+ /*
+ * We need to remove the Tombstone entry from the remaining indexes:
+ * objectclass=nsTombstone, nsUniqueID, nscpEntryDN
+ */
+ char *nscpedn = NULL;
+
+ retval = index_addordel_string(be,SLAPI_ATTR_OBJECTCLASS,SLAPI_ATTR_VALUE_TOMBSTONE,e->ep_id,BE_INDEX_DEL,&txn);
+ if (DB_LOCK_DEADLOCK == retval) {
+ LDAPDebug( LDAP_DEBUG_ARGS, "delete 4 DB_LOCK_DEADLOCK\n", 0, 0, 0 );
+ /* Retry txn */
+ continue;
+ }
+ if (0 != retval) {
+ LDAPDebug( LDAP_DEBUG_TRACE, "delete 1 BAD, err=%d %s\n",
+ retval, (msg = dblayer_strerror( retval )) ? msg : "", 0 );
+ if (LDBM_OS_ERR_IS_DISKFULL(retval)) disk_full = 1;
+ ldap_result_code= LDAP_OPERATIONS_ERROR;
+ goto error_return;
+ }
+ retval = index_addordel_string(be,SLAPI_ATTR_UNIQUEID,slapi_entry_get_uniqueid(e->ep_entry),e->ep_id,BE_INDEX_DEL,&txn);
+ if (DB_LOCK_DEADLOCK == retval) {
+ LDAPDebug( LDAP_DEBUG_ARGS, "delete 5 DB_LOCK_DEADLOCK\n", 0, 0, 0 );
+ /* Retry txn */
+ continue;
+ }
+ if (0 != retval) {
+ LDAPDebug( LDAP_DEBUG_TRACE, "delete 2 BAD, err=%d %s\n",
+ retval, (msg = dblayer_strerror( retval )) ? msg : "", 0 );
+ if (LDBM_OS_ERR_IS_DISKFULL(retval)) disk_full = 1;
+ ldap_result_code= LDAP_OPERATIONS_ERROR;
+ goto error_return;
+ }
+
+ nscpedn = slapi_entry_attr_get_charptr(e->ep_entry, SLAPI_ATTR_NSCP_ENTRYDN);
+ if (nscpedn) {
+ retval = index_addordel_string(be,SLAPI_ATTR_NSCP_ENTRYDN, nscpedn, e->ep_id,BE_INDEX_DEL,&txn);
+ slapi_ch_free((void **)&nscpedn);
+ if (DB_LOCK_DEADLOCK == retval) {
+ LDAPDebug( LDAP_DEBUG_ARGS, "delete 6 DB_LOCK_DEADLOCK\n", 0, 0, 0 );
+ /* Retry txn */
+ continue;
+ }
+ if (0 != retval) {
+ LDAPDebug( LDAP_DEBUG_TRACE, "delete 3 BAD, err=%d %s\n",
+ retval, (msg = dblayer_strerror( retval )) ? msg : "", 0 );
+ if (LDBM_OS_ERR_IS_DISKFULL(retval)) disk_full = 1;
+ ldap_result_code= LDAP_OPERATIONS_ERROR;
+ goto error_return;
+ }
+ }
+ }
+
+ if (parent_found) {
+ /* Push out the db modifications from the parent entry */
+ retval = modify_update_all(be,pb,&parent_modify_c,&txn);
+ if (DB_LOCK_DEADLOCK == retval)
+ {
+ LDAPDebug( LDAP_DEBUG_ARGS, "del 4 DEADLOCK\n", 0, 0, 0 );
+ /* Retry txn */
+ continue;
+ }
+ if (0 != retval) {
+ LDAPDebug( LDAP_DEBUG_TRACE, "delete 3 BAD, err=%d %s\n",
+ retval, (msg = dblayer_strerror( retval )) ? msg : "", 0 );
+ if (LDBM_OS_ERR_IS_DISKFULL(retval)) disk_full = 1;
+ ldap_result_code= LDAP_OPERATIONS_ERROR;
+ goto error_return;
+ }
+ }
+ /*
+ * first check if searchentry needs to be removed
+ * Remove the entry from the Virtual List View indexes.
+ *
+ */
+ if(!delete_tombstone_entry &&
+ !vlv_delete_search_entry(pb,e->ep_entry,inst)) {
+ retval = vlv_update_all_indexes(&txn, be, pb, e, NULL);
+ }
+
+ if (DB_LOCK_DEADLOCK == retval)
+ {
+ LDAPDebug( LDAP_DEBUG_ARGS, "delete DEADLOCK vlv_update_index\n", 0, 0, 0 );
+ /* Retry txn */
+ continue;
+ }
+ if (retval != 0 ) {
+ if (LDBM_OS_ERR_IS_DISKFULL(retval)) disk_full = 1;
+ ldap_result_code= LDAP_OPERATIONS_ERROR;
+ goto error_return;
+ }
+ if (retval == 0 ) {
+ break;
+ }
+ }
+ if (retry_count == RETRY_TIMES) {
+ /* Failed */
+ LDAPDebug( LDAP_DEBUG_ANY, "Retry count exceeded in delete\n", 0, 0, 0 );
+ ldap_result_code= LDAP_OPERATIONS_ERROR;
+ goto error_return;
+ }
+
+ retval = dblayer_txn_commit(li,&txn);
+ if (0 != retval)
+ {
+ if (LDBM_OS_ERR_IS_DISKFULL(retval)) disk_full = 1;
+ ldap_result_code= LDAP_OPERATIONS_ERROR;
+ goto error_return;
+ }
+
+ /* delete from cache and clean up */
+ cache_remove(&inst->inst_cache, e);
+ cache_unlock_entry( &inst->inst_cache, e );
+ cache_return( &inst->inst_cache, &e );
+ if (parent_found)
+ {
+ /* Replace the old parent entry with the newly modified one */
+ modify_switch_entries( &parent_modify_c,be);
+ }
+
+
+ rc= 0;
+ goto common_return;
+
+error_return:
+ if (e!=NULL) {
+ cache_unlock_entry( &inst->inst_cache, e );
+ cache_return( &inst->inst_cache, &e );
+ }
+ if (tombstone_in_cache)
+ {
+ cache_remove( &inst->inst_cache, tombstone );
+ }
+ else
+ {
+ backentry_free( &tombstone );
+ }
+
+ if (retval == DB_RUNRECOVERY) {
+ dblayer_remember_disk_filled(li);
+ ldbm_nasty("Delete",79,retval);
+ disk_full = 1;
+ }
+
+ if (disk_full) {
+ rc= return_on_disk_full(li);
+ goto diskfull_return;
+ }
+ else
+ rc= SLAPI_FAIL_GENERAL;
+
+ /* It is specifically OK to make this call even when no transaction was in progress */
+ dblayer_txn_abort(li,&txn); /* abort crashes in case disk full */
+
+common_return:
+ if (tombstone_in_cache)
+ {
+ cache_return( &inst->inst_cache, &tombstone );
+ }
+
+ /*
+ * The bepostop is called even if the operation fails,
+ * but not if the operation is purging tombstones.
+ */
+ if (!delete_tombstone_entry) {
+ plugin_call_plugins (pb, SLAPI_PLUGIN_BE_POST_DELETE_FN);
+ }
+
+diskfull_return:
+ if(ldap_result_code!=-1)
+ {
+ slapi_send_ldap_result( pb, ldap_result_code, NULL, ldap_result_message, 0, NULL );
+ }
+ modify_term(&parent_modify_c,be);
+ if(dblock_acquired)
+ {
+ dblayer_unlock_backend(be);
+ }
+ if (rc == 0 && opcsn && !is_fixup_operation && !delete_tombstone_entry)
+ {
+ /* URP Naming Collision
+ * When an entry is deleted by a replicated delete operation
+ * we must check for entries that have had a naming collision
+ * with this entry. Now that this name has been given up, one
+ * of those entries can take over the name.
+ */
+ slapi_pblock_set(pb, SLAPI_URP_NAMING_COLLISION_DN, slapi_ch_strdup (dn));
+ }
+ done_with_pblock_entry(pb, SLAPI_DELETE_EXISTING_ENTRY);
+ slapi_ch_free((void**)&errbuf);
+ slapi_sdn_done(&sdn);
+ slapi_ch_free_string(&e_uniqueid);
+ if (pb->pb_conn)
+ {
+ slapi_log_error (SLAPI_LOG_TRACE, "ldbm_back_delete", "leave conn=%d op=%d\n", pb->pb_conn->c_connid, operation->o_opid);
+ }
+ return rc;
+}
diff --git a/ldap/servers/slapd/back-ldbm/ldbm_index_config.c b/ldap/servers/slapd/back-ldbm/ldbm_index_config.c
new file mode 100644
index 00000000..bd604cdb
--- /dev/null
+++ b/ldap/servers/slapd/back-ldbm/ldbm_index_config.c
@@ -0,0 +1,698 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+/* This file handles configuration information that is specific
+ * to ldbm instance indexes.
+ */
+
+#include "back-ldbm.h"
+#include "dblayer.h"
+
+/* Forward declarations for the callbacks */
+int ldbm_instance_index_config_add_callback(Slapi_PBlock *pb, Slapi_Entry* entryBefore, Slapi_Entry* e, int *returncode, char *returntext, void *arg);
+int ldbm_instance_index_config_delete_callback(Slapi_PBlock *pb, Slapi_Entry* entryBefore, Slapi_Entry* e, int *returncode, char *returntext, void *arg);
+
+
+
+
+
+/* attrinfo2ConfIndexes: converts attrinfo into "pres,eq,sub,approx"
+ * as seen in index entries within dse.ldif
+ */
+static char *attrinfo2ConfIndexes (struct attrinfo *pai)
+{
+ char buffer[128];
+
+ buffer[0] = '\0';
+ if (!(IS_INDEXED( pai->ai_indexmask ))) { /* skip if no index */
+ strcat (buffer, "none");
+ }
+
+ if (pai->ai_indexmask & INDEX_PRESENCE) {
+ if (strlen (buffer)) {
+ strcat (buffer, ",");
+ }
+ strcat (buffer, "pres");
+ }
+ if (pai->ai_indexmask & INDEX_EQUALITY) {
+ if (strlen (buffer)) {
+ strcat (buffer, ",");
+ }
+ strcat (buffer, "eq");
+ }
+ if (pai->ai_indexmask & INDEX_APPROX) {
+ if (strlen(buffer)) {
+ strcat (buffer, ",");
+ }
+ strcat (buffer, "approx");
+ }
+ if (pai->ai_indexmask & INDEX_SUB) {
+ if (strlen (buffer)) {
+ strcat (buffer, ",");
+ }
+ strcat (buffer, "sub");
+ }
+
+ return (slapi_ch_strdup (buffer) );
+}
+
+
+/* attrinfo2ConfMatchingRules: converts attrinfo into matching rule oids, as
+ * seen in index entries within dse.ldif
+ */
+static char *attrinfo2ConfMatchingRules (struct attrinfo *pai)
+{
+ int i;
+ char buffer[1024];
+
+ buffer[0] = '\0';
+
+ if (pai->ai_index_rules) {
+ strcat (buffer, "\t");
+ for (i = 0; pai->ai_index_rules[i]; i++) {
+ strcat (buffer, pai->ai_index_rules[i]);
+ if (pai->ai_index_rules[i+1]) {
+ strcat (buffer, ",");
+ }
+ }
+ }
+ return (slapi_ch_strdup (buffer) );
+}
+
+
+/* used by the two callbacks below, to parse an index entry into something
+ * awkward that we can pass to attr_index_config().
+ */
+#define MAX_TMPBUF 256
+#define ZCAT_SAFE(_buf, _x1, _x2) do { \
+ if (strlen(_buf) + strlen(_x1) + strlen(_x2) + 2 < MAX_TMPBUF) { \
+ strcat(_buf, _x1); \
+ strcat(_buf, _x2); \
+ } \
+} while (0)
+static int ldbm_index_parse_entry(ldbm_instance *inst, Slapi_Entry *e,
+ const char *trace_string,
+ char **index_name)
+{
+ char *arglist[] = { NULL, NULL, NULL, NULL };
+ int argc = 0, i;
+ Slapi_Attr *attr;
+ const struct berval *attrValue;
+ Slapi_Value *sval;
+ char tmpBuf[MAX_TMPBUF];
+
+ /* Get the name of the attribute to index which will be the value
+ * of the cn attribute. */
+ if (slapi_entry_attr_find(e, "cn", &attr) != 0) {
+ LDAPDebug(LDAP_DEBUG_ANY, "Warning: malformed index entry %s\n",
+ slapi_entry_get_dn(e), 0, 0);
+ return LDAP_OPERATIONS_ERROR;
+ }
+
+ slapi_attr_first_value(attr, &sval);
+ attrValue = slapi_value_get_berval(sval);
+ arglist[argc++] = slapi_ch_strdup(attrValue->bv_val);
+ if (index_name != NULL) {
+ *index_name = slapi_ch_strdup(attrValue->bv_val);
+ }
+
+ /* Get the list of index types from the entry. */
+ if (0 == slapi_entry_attr_find(e, "nsIndexType", &attr)) {
+ for (i = slapi_attr_first_value(attr, &sval); i != -1;
+ i = slapi_attr_next_value(attr, i, &sval)) {
+ attrValue = slapi_value_get_berval(sval);
+ if (0 == i) {
+ tmpBuf[0] = 0;
+ ZCAT_SAFE(tmpBuf, "", attrValue->bv_val);
+ } else {
+ ZCAT_SAFE(tmpBuf, ",", attrValue->bv_val);
+ }
+ }
+ arglist[argc++] = slapi_ch_strdup(tmpBuf);
+ }
+
+ /* Get the list of matching rules from the entry. */
+ if (0 == slapi_entry_attr_find(e, "nsMatchingRule", &attr)) {
+ for (i = slapi_attr_first_value(attr, &sval); i != -1;
+ i = slapi_attr_next_value(attr, i, &sval)) {
+ attrValue = slapi_value_get_berval(sval);
+ if (0 == i) {
+ tmpBuf[0] = 0;
+ ZCAT_SAFE(tmpBuf, "", attrValue->bv_val);
+ } else {
+ ZCAT_SAFE(tmpBuf, ",", attrValue->bv_val);
+ }
+ }
+ arglist[argc++] = slapi_ch_strdup(tmpBuf);
+ }
+
+ arglist[argc] = NULL;
+ attr_index_config(inst->inst_be, (char *)trace_string, 0, argc, arglist, 0);
+ for (i = 0; i < argc; i++) {
+ slapi_ch_free((void **)&arglist[i]);
+ }
+ return LDAP_SUCCESS;
+}
+
+
+/*
+ * Temp callback that gets called for each index entry when a new
+ * instance is starting up.
+ */
+int
+ldbm_index_init_entry_callback(Slapi_PBlock *pb, Slapi_Entry* e, Slapi_Entry* entryAfter, int *returncode, char *returntext, void *arg)
+{
+ ldbm_instance *inst = (ldbm_instance *) arg;
+
+ returntext[0] = '\0';
+ *returncode = ldbm_index_parse_entry(inst, e, "from ldbm instance init",
+ NULL);
+ if (*returncode == LDAP_SUCCESS) {
+ return SLAPI_DSE_CALLBACK_OK;
+ } else {
+ sprintf(returntext, "Problem initializing index entry %s\n",
+ slapi_entry_get_dn(e));
+ return SLAPI_DSE_CALLBACK_ERROR;
+ }
+}
+
+/*
+ * Config DSE callback for index additions.
+ */
+int
+ldbm_instance_index_config_add_callback(Slapi_PBlock *pb, Slapi_Entry* e, Slapi_Entry* eAfter, int *returncode, char *returntext, void *arg)
+{
+ ldbm_instance *inst = (ldbm_instance *) arg;
+ char *index_name;
+
+ returntext[0] = '\0';
+ *returncode = ldbm_index_parse_entry(inst, e, "from DSE add", &index_name);
+ if (*returncode == LDAP_SUCCESS) {
+ struct attrinfo *ai = NULL;
+
+ /* if the index is a "system" index, we assume it's being added by
+ * by the server, and it's okay for the index to go online immediately.
+ * if not, we set the index "offline" so it won't actually be used
+ * until someone runs db2index on it.
+ */
+ if (! ldbm_attribute_always_indexed(index_name)) {
+ ainfo_get(inst->inst_be, index_name, &ai);
+ PR_ASSERT(ai != NULL);
+ ai->ai_indexmask |= INDEX_OFFLINE;
+ }
+ slapi_ch_free((void **)&index_name);
+ return SLAPI_DSE_CALLBACK_OK;
+ } else {
+ return SLAPI_DSE_CALLBACK_ERROR;
+ }
+}
+
+/*
+ * Config DSE callback for index deletes.
+ */
+int
+ldbm_instance_index_config_delete_callback(Slapi_PBlock *pb, Slapi_Entry* e, Slapi_Entry* entryAfter, int *returncode, char *returntext, void *arg)
+{
+ ldbm_instance *inst = (ldbm_instance *) arg;
+ char *arglist[4];
+ Slapi_Attr *attr;
+ Slapi_Value *sval;
+ const struct berval *attrValue;
+ int argc = 0;
+ int rc = SLAPI_DSE_CALLBACK_OK;
+ struct attrinfo *ainfo = NULL;
+
+ returntext[0] = '\0';
+ *returncode = LDAP_SUCCESS;
+
+ slapi_entry_attr_find(e, "cn", &attr);
+ slapi_attr_first_value(attr, &sval);
+ attrValue = slapi_value_get_berval(sval);
+
+ arglist[argc++] = slapi_ch_strdup(attrValue->bv_val);
+ arglist[argc++] = slapi_ch_strdup("none");
+ arglist[argc] = NULL;
+ attr_index_config(inst->inst_be, "From DSE delete", 0, argc, arglist, 0);
+ slapi_ch_free((void **)&arglist[0]);
+ slapi_ch_free((void **)&arglist[1]);
+
+ ainfo_get(inst->inst_be, attrValue->bv_val, &ainfo);
+
+ if (NULL == ainfo) {
+ *returncode = LDAP_UNAVAILABLE;
+ rc = SLAPI_DSE_CALLBACK_ERROR;
+ } else {
+ if (dblayer_erase_index_file(inst->inst_be, ainfo, 0 /* do chkpt */)) {
+ *returncode = LDAP_UNWILLING_TO_PERFORM;
+ rc = SLAPI_DSE_CALLBACK_ERROR;
+ }
+ }
+
+ return rc;
+}
+
+/*
+ * Config DSE callback for index entry changes.
+ *
+ * this function is huge!
+ */
+int
+ldbm_instance_index_config_modify_callback(Slapi_PBlock *pb, Slapi_Entry *e,
+ Slapi_Entry *entryAfter, int *returncode, char *returntext, void *arg)
+{
+ ldbm_instance *inst = (ldbm_instance *)arg;
+ Slapi_Attr *attr;
+ Slapi_Value *sval;
+ const struct berval *attrValue;
+ struct attrinfo *ainfo = NULL;
+ LDAPMod **mods;
+ char *arglist[4];
+ char *config_attr;
+ char *origIndexTypes, *origMatchingRules;
+ char **origIndexTypesArray = NULL;
+ char **origMatchingRulesArray = NULL;
+ char **addIndexTypesArray = NULL;
+ char **addMatchingRulesArray = NULL;
+ char **deleteIndexTypesArray = NULL;
+ char **deleteMatchingRulesArray = NULL;
+ int i, j;
+ int dodeletes = 0;
+ char tmpBuf[MAX_TMPBUF];
+
+ returntext[0] = '\0';
+ *returncode = LDAP_SUCCESS;
+ slapi_pblock_get(pb, SLAPI_MODIFY_MODS, &mods);
+
+ slapi_entry_attr_find(e, "cn", &attr);
+ slapi_attr_first_value(attr, &sval);
+ attrValue = slapi_value_get_berval(sval);
+ ainfo_get(inst->inst_be, attrValue->bv_val, &ainfo);
+ if (NULL == ainfo) {
+ return SLAPI_DSE_CALLBACK_ERROR;
+ }
+
+ origIndexTypes = attrinfo2ConfIndexes(ainfo);
+ origMatchingRules = attrinfo2ConfMatchingRules(ainfo);
+ origIndexTypesArray = str2charray(origIndexTypes, ",");
+ origMatchingRulesArray = str2charray(origMatchingRules, ",");
+
+ for (i = 0; mods[i] != NULL; i++) {
+ config_attr = (char *)mods[i]->mod_type;
+
+ if (strcasecmp(config_attr, "nsIndexType") == 0) {
+ if ((mods[i]->mod_op & ~LDAP_MOD_BVALUES) == LDAP_MOD_ADD) {
+ for (j = 0; mods[i]->mod_bvalues[j] != NULL; j++) {
+ charray_add(&addIndexTypesArray,
+ slapi_ch_strdup(mods[i]->mod_bvalues[j]->bv_val));
+ }
+ continue;
+ }
+ if ((mods[i]->mod_op & ~LDAP_MOD_BVALUES) == LDAP_MOD_DELETE) {
+ if ((mods[i]->mod_bvalues == NULL) ||
+ (mods[i]->mod_bvalues[0] == NULL)) {
+ if (deleteIndexTypesArray) {
+ charray_free(deleteIndexTypesArray);
+ }
+ deleteIndexTypesArray = charray_dup(origIndexTypesArray);
+ } else {
+ for (j = 0; mods[i]->mod_bvalues[j] != NULL; j++) {
+ charray_add(&deleteIndexTypesArray,
+ slapi_ch_strdup(mods[i]->mod_bvalues[j]->bv_val));
+ }
+ }
+ continue;
+ }
+ }
+ if (strcasecmp(config_attr, "nsMatchingRule") == 0) {
+ if ((mods[i]->mod_op & ~LDAP_MOD_BVALUES) == LDAP_MOD_ADD) {
+ for (j = 0; mods[i]->mod_bvalues[j] != NULL; j++) {
+ charray_add(&addMatchingRulesArray,
+ slapi_ch_strdup(mods[i]->mod_bvalues[j]->bv_val));
+ }
+ continue;
+ }
+ if ((mods[i]->mod_op & ~LDAP_MOD_BVALUES) == LDAP_MOD_DELETE) {
+ if ((mods[i]->mod_bvalues == NULL) ||
+ (mods[i]->mod_bvalues[0] == NULL)) {
+ if (deleteMatchingRulesArray) {
+ charray_free(deleteMatchingRulesArray);
+ }
+ deleteMatchingRulesArray = charray_dup(origMatchingRulesArray);
+ } else {
+ for (j = 0; mods[i]->mod_bvalues[j] != NULL; j++) {
+ charray_add(&deleteMatchingRulesArray,
+ slapi_ch_strdup(mods[i]->mod_bvalues[j]->bv_val));
+ }
+ }
+ continue;
+ }
+ }
+ }
+
+ /* create the new set of index types */
+ if (deleteIndexTypesArray) {
+ for (i = 0; origIndexTypesArray[i] != NULL; i++) {
+ if (charray_inlist(deleteIndexTypesArray,
+ origIndexTypesArray[i])) {
+ slapi_ch_free((void **)&(origIndexTypesArray[i]));
+ dodeletes = 1;
+ if (origIndexTypesArray[i+1] != NULL) {
+ for (j = i+1; origIndexTypesArray[j] != NULL; j++) {
+ origIndexTypesArray[j-1] = origIndexTypesArray[j];
+ }
+ origIndexTypesArray[j-1] = NULL;
+ i--;
+ }
+ }
+ }
+ }
+
+ if (addIndexTypesArray) {
+ for (i = 0; addIndexTypesArray[i] != NULL; i++) {
+ if (!charray_inlist(origIndexTypesArray, addIndexTypesArray[i])) {
+ charray_add(&origIndexTypesArray,
+ slapi_ch_strdup(addIndexTypesArray[i]));
+ }
+ }
+ }
+
+ if (deleteMatchingRulesArray) {
+ for (i = 0; origMatchingRulesArray[i] != NULL; i++) {
+ if (charray_inlist(deleteMatchingRulesArray,
+ origMatchingRulesArray[i])) {
+ slapi_ch_free((void **)&(origMatchingRulesArray[i]));
+ dodeletes = 1;
+ if (origMatchingRulesArray[i+1] != NULL) {
+ for (j = i+1; origMatchingRulesArray[j] != NULL; j++) {
+ origMatchingRulesArray[j-1] = origMatchingRulesArray[j];
+ }
+ origMatchingRulesArray[j-1] = NULL;
+ i--;
+ }
+ }
+ }
+ }
+
+ if (addMatchingRulesArray) {
+ for (i = 0; addMatchingRulesArray[i] != NULL; i++) {
+ if (!charray_inlist(origMatchingRulesArray,
+ addMatchingRulesArray[i])) {
+ charray_add(&origMatchingRulesArray,
+ slapi_ch_strdup(addMatchingRulesArray[i]));
+ }
+ }
+ }
+
+ if (dodeletes) {
+ i = 0;
+ arglist[i++] = slapi_ch_strdup(attrValue->bv_val);
+ arglist[i++] = slapi_ch_strdup("none");
+ arglist[i] = NULL;
+ attr_index_config(inst->inst_be, "from DSE modify", 0, i, arglist, 0);
+
+ /* Free args */
+ slapi_ch_free((void **)&arglist[0]);
+ slapi_ch_free((void **)&arglist[1]);
+ }
+
+ i = 0;
+ arglist[i++] = slapi_ch_strdup(attrValue->bv_val);
+ if (origIndexTypesArray && origIndexTypesArray[0]) {
+ tmpBuf[0] = 0;
+ ZCAT_SAFE(tmpBuf, "", origIndexTypesArray[0]);
+ for (j = 1; origIndexTypesArray[j] != NULL; j++) {
+ ZCAT_SAFE(tmpBuf, ",", origIndexTypesArray[j]);
+ }
+ arglist[i++] = slapi_ch_strdup(tmpBuf);
+ } else {
+ arglist[i++] = slapi_ch_strdup("none");
+ }
+
+ if (origMatchingRulesArray && origMatchingRulesArray[0]) {
+ tmpBuf[0] = 0;
+ ZCAT_SAFE(tmpBuf, "", origMatchingRulesArray[0]);
+ for (j = 1; origMatchingRulesArray[j] != NULL; j++) {
+ ZCAT_SAFE(tmpBuf, ",", origMatchingRulesArray[j]);
+ }
+ arglist[i++] = slapi_ch_strdup(tmpBuf);
+ }
+
+ arglist[i] = NULL;
+ attr_index_config(inst->inst_be, "from DSE modify", 0, i, arglist, 0);
+
+ /* Free args */
+ for (i=0; arglist[i]; i++) {
+ slapi_ch_free((void **)&arglist[i]);
+ }
+
+ if(origIndexTypesArray) {
+ charray_free(origIndexTypesArray);
+ }
+ if(origMatchingRulesArray) {
+ charray_free(origMatchingRulesArray);
+ }
+ if(addIndexTypesArray) {
+ charray_free(addIndexTypesArray);
+ }
+ if(deleteIndexTypesArray) {
+ charray_free(deleteIndexTypesArray);
+ }
+ if(addMatchingRulesArray) {
+ charray_free(addMatchingRulesArray);
+ }
+ if(deleteMatchingRulesArray) {
+ charray_free(deleteMatchingRulesArray);
+ }
+ if (origIndexTypes) {
+ slapi_ch_free ((void **)&origIndexTypes);
+ }
+ if (origMatchingRules) {
+ slapi_ch_free ((void **)&origMatchingRules);
+ }
+
+ return SLAPI_DSE_CALLBACK_OK;
+}
+
+/* add index entries to the per-instance DSE (used only from instance.c) */
+int ldbm_instance_config_add_index_entry(
+ ldbm_instance *inst,
+ int argc,
+ char **argv,
+ int flags
+)
+{
+ char **attrs = NULL;
+ char **indexes = NULL;
+ char **matchingRules = NULL;
+ char eBuf[BUFSIZ];
+ int i = 0;
+ int j = 0;
+ char *basetype = NULL;
+ char tmpAttrsStr[256];
+ char tmpIndexesStr[256];
+ char tmpMatchingRulesStr[1024];
+ struct ldbminfo *li = inst->inst_li;
+
+ if ((argc < 2) || (NULL == argv) || (NULL == argv[0]) ||
+ (NULL == argv[1])) {
+ return(-1);
+ }
+
+ strcpy(tmpAttrsStr,argv[0]);
+ attrs = str2charray( tmpAttrsStr, "," );
+ strcpy(tmpIndexesStr,argv[1]);
+ indexes = str2charray( tmpIndexesStr, ",");
+
+ if(argc > 2) {
+ strcpy(tmpMatchingRulesStr,argv[2]);
+ matchingRules = str2charray( tmpMatchingRulesStr, ",");
+ }
+
+ for(i=0; attrs[i] !=NULL; i++)
+ {
+ if('\0' == attrs[i][0]) continue;
+ basetype = slapi_attr_basetype(attrs[i], NULL, 0);
+ sprintf(eBuf,
+ "dn: cn=%s, cn=index, cn=%s, cn=%s, cn=plugins, cn=config\n"
+ "objectclass:top\n"
+ "objectclass:nsIndex\n"
+ "cn:%s\n"
+ "nsSystemIndex:%s\n",
+ basetype, inst->inst_name, li->li_plugin->plg_name,
+ basetype,
+ (ldbm_attribute_always_indexed(basetype)?"true":"false"));
+ for(j=0; indexes[j] != NULL; j++)
+ {
+ strcat(eBuf, "nsIndexType:");
+ strcat(eBuf,indexes[j]);
+ strcat(eBuf,"\n");
+ }
+ if((argc>2)&&(argv[2]))
+ {
+ for(j=0; matchingRules[j] != NULL; j++)
+ {
+ strcat(eBuf,"nsMatchingRule:");
+ strcat(eBuf,matchingRules[j]);
+ strcat(eBuf,"\n");
+ }
+ }
+
+ ldbm_config_add_dse_entry(li, eBuf, flags);
+
+ slapi_ch_free((void**)&basetype);
+ }
+
+ if(NULL != attrs) {
+ charray_free(attrs);
+ }
+ if(NULL != indexes) {
+ charray_free(indexes);
+ }
+ if(NULL != matchingRules) {
+ charray_free(matchingRules);
+ }
+ return (0);
+}
+
+int
+ldbm_instance_index_config_enable_index(ldbm_instance *inst, Slapi_Entry* e)
+{
+ char *index_name;
+ int rc;
+
+ rc=ldbm_index_parse_entry(inst, e, "from DSE add", &index_name);
+ if (rc == LDAP_SUCCESS) {
+ struct attrinfo *ai = NULL;
+
+ /* Assume the caller knows if it is OK to go online immediatly */
+
+ ainfo_get(inst->inst_be, index_name, &ai);
+ PR_ASSERT(ai != NULL);
+ ai->ai_indexmask &= ~INDEX_OFFLINE;
+ slapi_ch_free((void **)&index_name);
+ }
+ return rc;
+}
+
+
+/*
+** create the default user-defined indexes
+*/
+
+int ldbm_instance_create_default_user_indexes(ldbm_instance *inst)
+{
+
+ /*
+ ** Search for user-defined default indexes and add them
+ ** to the backend instance beeing created.
+ */
+
+ Slapi_PBlock *aPb;
+ Slapi_Entry **entries = NULL;
+ Slapi_Attr *attr;
+ Slapi_Value *sval = NULL;
+ const struct berval *attrValue;
+ char *argv[ 8 ];
+ char basedn[BUFSIZ];
+ char tmpBuf[MAX_TMPBUF];
+ char tmpBuf2[MAX_TMPBUF];
+ int argc;
+
+ struct ldbminfo *li;
+
+ /* write the dse file only on the final index */
+ int flags = LDBM_INSTANCE_CONFIG_DONT_WRITE;
+
+ if (NULL == inst) {
+ LDAPDebug(LDAP_DEBUG_ANY,
+ "Warning: can't initialize default user indexes (invalid instance).\n", 0,0,0);
+ return -1;
+ }
+
+ li = inst->inst_li;
+ strcpy(tmpBuf,"");
+
+ /* Construct the base dn of the subtree that holds the default user indexes. */
+ sprintf(basedn, "cn=default indexes, cn=config, cn=%s, cn=plugins, cn=config",
+ li->li_plugin->plg_name);
+
+ /* Do a search of the subtree containing the index entries */
+ aPb = slapi_pblock_new();
+ slapi_search_internal_set_pb(aPb, basedn, LDAP_SCOPE_SUBTREE,
+ "(objectclass=nsIndex)", NULL, 0 , NULL, NULL, li->li_identity, 0);
+ slapi_search_internal_pb (aPb);
+ slapi_pblock_get(aPb, SLAPI_PLUGIN_INTOP_SEARCH_ENTRIES, &entries);
+ if (entries!=NULL) {
+ int i,j;
+ for (i=0; entries[i]!=NULL; i++) {
+
+ /* Get the name of the attribute to index which will be the value
+ * of the cn attribute. */
+
+ if (slapi_entry_attr_find(entries[i], "cn", &attr) != 0) {
+ LDAPDebug(LDAP_DEBUG_ANY,"Warning: malformed index entry %s. Index ignored.\n",
+ slapi_entry_get_dn(entries[i]), 0, 0);
+ continue;
+ }
+ slapi_attr_first_value(attr, &sval);
+ attrValue = slapi_value_get_berval(sval);
+ argv[0] = attrValue->bv_val;
+ argc=1;
+
+ /* Get the list of index types from the entry. */
+
+ if (0 == slapi_entry_attr_find(entries[i], "nsIndexType", &attr)) {
+ for (j = slapi_attr_first_value(attr, &sval); j != -1;
+ j = slapi_attr_next_value(attr, j, &sval)) {
+ attrValue = slapi_value_get_berval(sval);
+ if (0 == j) {
+ tmpBuf[0] = 0;
+ ZCAT_SAFE(tmpBuf, "", attrValue->bv_val);
+ } else {
+ ZCAT_SAFE(tmpBuf, ",", attrValue->bv_val);
+ }
+ }
+ argv[argc]=tmpBuf;
+ argc++;
+ }
+
+ /* Get the list of matching rules from the entry. */
+
+ if (0 == slapi_entry_attr_find(entries[i], "nsMatchingRule", &attr)) {
+ for (j = slapi_attr_first_value(attr, &sval); j != -1;
+ j = slapi_attr_next_value(attr, j, &sval)) {
+ attrValue = slapi_value_get_berval(sval);
+ if (0 == j) {
+ tmpBuf2[0] = 0;
+ ZCAT_SAFE(tmpBuf2, "", attrValue->bv_val);
+ } else {
+ ZCAT_SAFE(tmpBuf2, ",", attrValue->bv_val);
+ }
+ }
+ argv[argc]=tmpBuf2;
+ argc++;
+ }
+
+ argv[argc]=NULL;
+
+ /* Create the index entry in the backend */
+
+ if (entries[i+1] == NULL) {
+ /* write the dse file only on the final index */
+ flags = 0;
+ }
+
+ ldbm_instance_config_add_index_entry(inst, argc, argv, flags);
+
+ /* put the index online */
+
+ ldbm_instance_index_config_enable_index(inst, entries[i]);
+ }
+ }
+
+ slapi_free_search_results_internal(aPb);
+ slapi_pblock_destroy(aPb);
+ return 0;
+}
diff --git a/ldap/servers/slapd/back-ldbm/ldbm_instance_config.c b/ldap/servers/slapd/back-ldbm/ldbm_instance_config.c
new file mode 100644
index 00000000..a5819d6f
--- /dev/null
+++ b/ldap/servers/slapd/back-ldbm/ldbm_instance_config.c
@@ -0,0 +1,997 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+/* This file handles configuration information that is specific
+ * to ldbm instances.
+ */
+
+#include "back-ldbm.h"
+#include "dblayer.h"
+
+/* Forward declarations for the callbacks */
+int ldbm_instance_search_config_entry_callback(Slapi_PBlock *pb, Slapi_Entry *e, Slapi_Entry *entryAfter, int *returncode, char *returntext, void *arg);
+int ldbm_instance_modify_config_entry_callback(Slapi_PBlock *pb, Slapi_Entry* entryBefore, Slapi_Entry* e, int *returncode, char *returntext, void *arg);
+
+static char *ldbm_instance_attrcrypt_filter = "(objectclass=nsAttributeEncryption)";
+
+/* dse entries add for a new ldbm instance */
+static char *ldbm_instance_skeleton_entries[] =
+{
+ "dn:cn=monitor, cn=%s, cn=%s, cn=plugins, cn=config\n"
+ "objectclass:top\n"
+ "objectclass:extensibleObject\n"
+ "cn:monitor\n",
+
+ "dn:cn=index, cn=%s, cn=%s, cn=plugins, cn=config\n"
+ "objectclass:top\n"
+ "objectclass:extensibleObject\n"
+ "cn:index\n",
+
+ "dn:cn=encrypted attributes, cn=%s, cn=%s, cn=plugins, cn=config\n"
+ "objectclass:top\n"
+ "objectclass:extensibleObject\n"
+ "cn:encrypted attributes\n",
+
+ "dn:cn=encrypted attribute keys, cn=%s, cn=%s, cn=plugins, cn=config\n"
+ "objectclass:top\n"
+ "objectclass:extensibleObject\n"
+ "cn:encrypted attribute keys\n",
+
+ ""
+};
+
+
+/*------------------------------------------------------------------------
+ * Get and set functions for ldbm instance variables
+ *----------------------------------------------------------------------*/
+static void *
+ldbm_instance_config_cachesize_get(void *arg)
+{
+ ldbm_instance *inst = (ldbm_instance *) arg;
+
+ return (void *) cache_get_max_entries(&(inst->inst_cache));
+}
+
+static int
+ldbm_instance_config_cachesize_set(void *arg, void *value, char *errorbuf, int phase, int apply)
+{
+ ldbm_instance *inst = (ldbm_instance *) arg;
+ int retval = LDAP_SUCCESS;
+ long val = (long) value;
+
+ /* Do whatever we can to make sure the data is ok. */
+
+ if (apply) {
+ cache_set_max_entries(&(inst->inst_cache), val);
+ }
+
+ return retval;
+}
+
+static void *
+ldbm_instance_config_cachememsize_get(void *arg)
+{
+ ldbm_instance *inst = (ldbm_instance *) arg;
+
+ return (void *) cache_get_max_size(&(inst->inst_cache));
+}
+
+static int
+ldbm_instance_config_cachememsize_set(void *arg, void *value, char *errorbuf, int phase, int apply)
+{
+ ldbm_instance *inst = (ldbm_instance *) arg;
+ int retval = LDAP_SUCCESS;
+ size_t val = (size_t) value;
+
+ /* Do whatever we can to make sure the data is ok. */
+
+ if (apply) {
+ cache_set_max_size(&(inst->inst_cache), val);
+ }
+
+ return retval;
+}
+
+static void *
+ldbm_instance_config_readonly_get(void *arg)
+{
+ ldbm_instance *inst = (ldbm_instance *)arg;
+
+ return (void *)inst->inst_be->be_readonly;
+}
+
+static void *
+ldbm_instance_config_instance_dir_get(void *arg)
+{
+ ldbm_instance *inst = (ldbm_instance *)arg;
+
+ if (inst->inst_dir_name == NULL)
+ return slapi_ch_strdup("");
+ else if (inst->inst_parent_dir_name)
+ {
+ int len = strlen(inst->inst_parent_dir_name) +
+ strlen(inst->inst_dir_name) + 2;
+ char *full_inst_dir = (char *)slapi_ch_malloc(len);
+ sprintf(full_inst_dir, "%s%c%s",
+ inst->inst_parent_dir_name, get_sep(inst->inst_parent_dir_name),
+ inst->inst_dir_name);
+ return full_inst_dir;
+ }
+ else
+ return slapi_ch_strdup(inst->inst_dir_name);
+}
+
+static void *
+ldbm_instance_config_require_index_get(void *arg)
+{
+ ldbm_instance *inst = (ldbm_instance *)arg;
+
+ return (void *)inst->require_index;
+}
+
+static int
+ldbm_instance_config_instance_dir_set(void *arg, void *value, char *errorbuf, int phase, int apply)
+{
+ ldbm_instance *inst = (ldbm_instance *)arg;
+
+ if (!apply) {
+ return LDAP_SUCCESS;
+ }
+
+ if ((value == NULL) || (strlen(value) == 0))
+ {
+ inst->inst_dir_name = NULL;
+ inst->inst_parent_dir_name = NULL;
+ }
+ else
+ {
+ char *dir = (char *)value;
+ if (is_fullpath(dir))
+ {
+ char sep = get_sep(dir);
+ char *p = strrchr(dir, sep);
+ if (NULL == p) /* never happens, tho */
+ {
+ inst->inst_parent_dir_name = NULL;
+ inst->inst_dir_name = slapi_ch_strdup(dir);
+ }
+ else
+ {
+ *p = '\0';
+ inst->inst_parent_dir_name = slapi_ch_strdup(dir);
+ inst->inst_dir_name = slapi_ch_strdup(p+1);
+ *p = sep;
+ }
+ }
+ else
+ {
+ inst->inst_parent_dir_name = NULL;
+ inst->inst_dir_name = slapi_ch_strdup(dir);
+ }
+ }
+ return LDAP_SUCCESS;
+}
+
+static int
+ldbm_instance_config_readonly_set(void *arg, void *value, char *errorbuf, int phase, int apply)
+{
+ ldbm_instance *inst = (ldbm_instance *)arg;
+
+ if (!apply) {
+ return LDAP_SUCCESS;
+ }
+
+ if (CONFIG_PHASE_RUNNING == phase) {
+ /* if the instance is busy, we'll save the user's readonly settings
+ * but won't change them until the instance is un-busy again.
+ */
+ if (! (inst->inst_flags & INST_FLAG_BUSY)) {
+ slapi_mtn_be_set_readonly(inst->inst_be, (int)value);
+ }
+ if ((int)value) {
+ inst->inst_flags |= INST_FLAG_READONLY;
+ } else {
+ inst->inst_flags &= ~INST_FLAG_READONLY;
+ }
+ } else {
+ slapi_be_set_readonly(inst->inst_be, (int)value);
+ }
+
+ return LDAP_SUCCESS;
+}
+
+static int
+ldbm_instance_config_require_index_set(void *arg, void *value, char *errorbuf, int phase, int apply)
+{
+ ldbm_instance *inst = (ldbm_instance *)arg;
+
+ if (!apply) {
+ return LDAP_SUCCESS;
+ }
+
+ inst->require_index = (int)value;
+
+ return LDAP_SUCCESS;
+}
+
+
+/*------------------------------------------------------------------------
+ * ldbm instance configuration array
+ *----------------------------------------------------------------------*/
+static config_info ldbm_instance_config[] = {
+ {CONFIG_INSTANCE_CACHESIZE, CONFIG_TYPE_LONG, "-1", &ldbm_instance_config_cachesize_get, &ldbm_instance_config_cachesize_set, CONFIG_FLAG_ALWAYS_SHOW|CONFIG_FLAG_ALLOW_RUNNING_CHANGE},
+ {CONFIG_INSTANCE_CACHEMEMSIZE, CONFIG_TYPE_SIZE_T, "10485760", &ldbm_instance_config_cachememsize_get, &ldbm_instance_config_cachememsize_set, CONFIG_FLAG_ALWAYS_SHOW|CONFIG_FLAG_ALLOW_RUNNING_CHANGE},
+ {CONFIG_INSTANCE_READONLY, CONFIG_TYPE_ONOFF, "off", &ldbm_instance_config_readonly_get, &ldbm_instance_config_readonly_set, CONFIG_FLAG_ALWAYS_SHOW|CONFIG_FLAG_ALLOW_RUNNING_CHANGE},
+ {CONFIG_INSTANCE_REQUIRE_INDEX, CONFIG_TYPE_ONOFF, "off", &ldbm_instance_config_require_index_get, &ldbm_instance_config_require_index_set, CONFIG_FLAG_ALWAYS_SHOW|CONFIG_FLAG_ALLOW_RUNNING_CHANGE},
+ {CONFIG_INSTANCE_DIR, CONFIG_TYPE_STRING, NULL, &ldbm_instance_config_instance_dir_get, &ldbm_instance_config_instance_dir_set, CONFIG_FLAG_ALWAYS_SHOW},
+ {NULL, 0, NULL, NULL, NULL, 0}
+};
+
+void
+ldbm_instance_config_setup_default(ldbm_instance *inst)
+{
+ config_info *config;
+ char err_buf[BUFSIZ];
+
+ for (config = ldbm_instance_config; config->config_name != NULL; config++) {
+ ldbm_config_set((void *)inst, config->config_name, ldbm_instance_config, NULL /* use default */, err_buf, CONFIG_PHASE_INITIALIZATION, 1 /* apply */);
+ }
+}
+
+static int
+parse_ldbm_instance_entry(Slapi_Entry *e, char **instance_name)
+{
+ Slapi_Attr *attr = NULL;
+
+ for (slapi_entry_first_attr(e, &attr); attr;
+ slapi_entry_next_attr(e, attr, &attr)) {
+ char *attr_name = NULL;
+
+ slapi_attr_get_type(attr, &attr_name);
+ if (strcasecmp(attr_name, "cn") == 0) {
+ Slapi_Value *sval = NULL;
+ struct berval *bval;
+ slapi_attr_first_value(attr, &sval);
+ bval = (struct berval *) slapi_value_get_berval(sval);
+ *instance_name = slapi_ch_strdup((char *)bval->bv_val);
+ }
+ }
+ return 0;
+}
+
+/* When a new instance is started, we need to read the dse to
+ * find out what indexes should be maintained. This function
+ * does that. Returns 0 on success. */
+static int
+read_instance_index_entries(ldbm_instance *inst)
+{
+ Slapi_PBlock *tmp_pb;
+ int scope = LDAP_SCOPE_SUBTREE;
+ char basedn[BUFSIZ];
+ const char *searchfilter = "(objectclass=nsIndex)";
+
+ /* Construct the base dn of the subtree that holds the index entries
+ * for this instance. */
+ sprintf(basedn, "cn=index, cn=%s, cn=%s, cn=plugins, cn=config",
+ inst->inst_name, inst->inst_li->li_plugin->plg_name);
+
+ /* Set up a tmp callback that will handle the init for each index entry */
+ slapi_config_register_callback(SLAPI_OPERATION_SEARCH, DSE_FLAG_PREOP,
+ basedn, scope, searchfilter, ldbm_index_init_entry_callback,
+ (void *) inst);
+
+ /* Do a search of the subtree containing the index entries */
+ tmp_pb = slapi_pblock_new();
+ slapi_search_internal_set_pb(tmp_pb, basedn, LDAP_SCOPE_SUBTREE,
+ searchfilter, NULL, 0, NULL, NULL, inst->inst_li->li_identity, 0);
+ slapi_search_internal_pb (tmp_pb);
+
+ /* Remove the tmp callback */
+ slapi_config_remove_callback(SLAPI_OPERATION_SEARCH, DSE_FLAG_PREOP,
+ basedn, scope, searchfilter, ldbm_index_init_entry_callback);
+ slapi_free_search_results_internal(tmp_pb);
+ slapi_pblock_destroy(tmp_pb);
+
+ return 0;
+}
+
+/* When a new instance is started, we need to read the dse to
+ * find out what attributes should be encrypted. This function
+ * does that. Returns 0 on success. */
+static int
+read_instance_attrcrypt_entries(ldbm_instance *inst)
+{
+ Slapi_PBlock *tmp_pb;
+ int scope = LDAP_SCOPE_SUBTREE;
+ char basedn[BUFSIZ];
+ const char *searchfilter = ldbm_instance_attrcrypt_filter;
+
+ /* Construct the base dn of the subtree that holds the index entries
+ * for this instance. */
+ sprintf(basedn, "cn=encrypted attributes, cn=%s, cn=%s, cn=plugins, cn=config",
+ inst->inst_name, inst->inst_li->li_plugin->plg_name);
+
+ /* Set up a tmp callback that will handle the init for each index entry */
+ slapi_config_register_callback(SLAPI_OPERATION_SEARCH, DSE_FLAG_PREOP,
+ basedn, scope, searchfilter, ldbm_attrcrypt_init_entry_callback,
+ (void *) inst);
+
+ /* Do a search of the subtree containing the index entries */
+ tmp_pb = slapi_pblock_new();
+ slapi_search_internal_set_pb(tmp_pb, basedn, LDAP_SCOPE_SUBTREE,
+ searchfilter, NULL, 0, NULL, NULL, inst->inst_li->li_identity, 0);
+ slapi_search_internal_pb (tmp_pb);
+
+ /* Remove the tmp callback */
+ slapi_config_remove_callback(SLAPI_OPERATION_SEARCH, DSE_FLAG_PREOP,
+ basedn, scope, searchfilter, ldbm_attrcrypt_init_entry_callback);
+ slapi_free_search_results_internal(tmp_pb);
+ slapi_pblock_destroy(tmp_pb);
+
+ return 0;
+}
+
+/* Handles the parsing of the config entry for an ldbm instance. Returns 0
+ * on success. */
+static int
+parse_ldbm_instance_config_entry(ldbm_instance *inst, Slapi_Entry *e, config_info *config_array)
+{
+ Slapi_Attr *attr = NULL;
+
+ for (slapi_entry_first_attr(e, &attr); attr;
+ slapi_entry_next_attr(e, attr, &attr)) {
+ char *attr_name = NULL;
+ Slapi_Value *sval = NULL;
+ struct berval *bval;
+ char err_buf[BUFSIZ];
+
+ slapi_attr_get_type(attr, &attr_name);
+
+ /* There are some attributes that we don't care about,
+ * like objectclass. */
+ if (ldbm_config_ignored_attr(attr_name)) {
+ continue;
+ }
+
+ /* We have to handle suffix attributes a little differently */
+ if (strcasecmp(attr_name, CONFIG_INSTANCE_SUFFIX) == 0) {
+ Slapi_DN suffix;
+
+ slapi_attr_first_value(attr, &sval);
+ bval = (struct berval *) slapi_value_get_berval(sval);
+ slapi_sdn_init_dn_byref(&suffix, bval->bv_val);
+ if (!slapi_be_issuffix(inst->inst_be, &suffix)) {
+ be_addsuffix(inst->inst_be, &suffix);
+ }
+ slapi_sdn_done(&suffix);
+ continue;
+ }
+
+ /* We are assuming that each of these attributes are to have
+ * only one value. If they have more than one value, like
+ * the nsslapd-suffix attribute, then they need to be
+ * handled differently. */
+ slapi_attr_first_value(attr, &sval);
+ bval = (struct berval *) slapi_value_get_berval(sval);
+
+ if (ldbm_config_set((void *) inst, attr_name, config_array, bval,
+ err_buf, CONFIG_PHASE_STARTUP, 1 /* apply */) != LDAP_SUCCESS) {
+ LDAPDebug(LDAP_DEBUG_ANY, "Error with config attribute %s : %s\n",
+ attr_name, err_buf, 0);
+ return 1;
+ }
+ }
+
+ /* Read the index entries */
+ read_instance_index_entries(inst);
+ /* Read the attribute encryption entries */
+ read_instance_attrcrypt_entries(inst);
+
+ return 0;
+}
+
+/* general-purpose callback to deny an operation */
+static int ldbm_instance_deny_config(Slapi_PBlock *pb, Slapi_Entry *e,
+ Slapi_Entry *entryAfter, int *returncode, char *returntext, void *arg)
+{
+ *returncode = LDAP_UNWILLING_TO_PERFORM;
+ return SLAPI_DSE_CALLBACK_ERROR;
+}
+
+/* Reads in any config information held in the dse for the given
+ * entry. Creates dse entries used to configure the given instance
+ * if they don't already exist. Registers dse callback functions to
+ * maintain those dse entries. Returns 0 on success. */
+int
+ldbm_instance_config_load_dse_info(ldbm_instance *inst)
+{
+ struct ldbminfo *li = inst->inst_li;
+ Slapi_PBlock *search_pb;
+ Slapi_Entry **entries = NULL;
+ int res;
+ char dn[BUFSIZ];
+
+ /* We try to read the entry
+ * cn=instance_name, cn=ldbm database, cn=plugins, cn=config. If the
+ * entry is there, then we process the config information it stores.
+ */
+ sprintf(dn, "cn=%s, cn=%s, cn=plugins, cn=config",
+ inst->inst_name, li->li_plugin->plg_name);
+ search_pb = slapi_pblock_new();
+ slapi_search_internal_set_pb(search_pb, dn, LDAP_SCOPE_BASE,
+ "objectclass=*", NULL, 0, NULL, NULL,
+ li->li_identity, 0);
+ slapi_search_internal_pb (search_pb);
+ slapi_pblock_get(search_pb, SLAPI_PLUGIN_INTOP_RESULT, &res);
+
+ if (res != LDAP_SUCCESS) {
+ LDAPDebug(LDAP_DEBUG_ANY, "Error accessing the config DSE\n", 0, 0, 0);
+ return 1;
+ } else {
+ /* Need to parse the configuration information for the ldbm
+ * plugin that is held in the DSE. */
+ slapi_pblock_get(search_pb, SLAPI_PLUGIN_INTOP_SEARCH_ENTRIES,
+ &entries);
+ if ((!entries) || (!entries[0])) {
+ LDAPDebug(LDAP_DEBUG_ANY, "Error accessing the config DSE\n",
+ 0, 0, 0);
+ return 1;
+ }
+ parse_ldbm_instance_config_entry(inst, entries[0],
+ ldbm_instance_config);
+ }
+
+ if (search_pb)
+ {
+ slapi_free_search_results_internal(search_pb);
+ slapi_pblock_destroy(search_pb);
+ }
+
+ /* now check for cn=monitor -- if not present, add default child entries */
+ search_pb = slapi_pblock_new();
+ sprintf(dn, "cn=monitor, cn=%s, cn=%s, cn=plugins, cn=config",
+ inst->inst_name, li->li_plugin->plg_name);
+ slapi_search_internal_set_pb(search_pb, dn, LDAP_SCOPE_BASE,
+ "objectclass=*", NULL, 0, NULL, NULL,
+ li->li_identity, 0);
+ slapi_search_internal_pb(search_pb);
+ slapi_pblock_get(search_pb, SLAPI_PLUGIN_INTOP_RESULT, &res);
+
+ if (res == LDAP_NO_SUCH_OBJECT) {
+ /* Add skeleton dse entries for this instance */
+ ldbm_config_add_dse_entries(li, ldbm_instance_skeleton_entries,
+ inst->inst_name, li->li_plugin->plg_name,
+ inst->inst_name, 0);
+ }
+
+ if (search_pb) {
+ slapi_free_search_results_internal(search_pb);
+ slapi_pblock_destroy(search_pb);
+ }
+
+ /* setup the dse callback functions for the ldbm instance config entry */
+ sprintf(dn, "cn=%s, cn=%s, cn=plugins, cn=config",
+ inst->inst_name, li->li_plugin->plg_name);
+ slapi_config_register_callback(SLAPI_OPERATION_SEARCH, DSE_FLAG_PREOP, dn,
+ LDAP_SCOPE_BASE, "(objectclass=*)",
+ ldbm_instance_search_config_entry_callback, (void *) inst);
+ slapi_config_register_callback(SLAPI_OPERATION_MODIFY, DSE_FLAG_PREOP, dn,
+ LDAP_SCOPE_BASE, "(objectclass=*)",
+ ldbm_instance_modify_config_entry_callback, (void *) inst);
+ slapi_config_register_callback(DSE_OPERATION_WRITE, DSE_FLAG_PREOP, dn,
+ LDAP_SCOPE_BASE, "(objectclass=*)",
+ ldbm_instance_search_config_entry_callback, (void *) inst);
+ slapi_config_register_callback(SLAPI_OPERATION_ADD, DSE_FLAG_PREOP, dn,
+ LDAP_SCOPE_BASE, "(objectclass=*)",
+ ldbm_instance_deny_config, (void *)inst);
+ /* delete is handled by a callback set in ldbm_config.c */
+
+ /* don't forget the monitor! */
+ sprintf(dn, "cn=monitor, cn=%s, cn=%s, cn=plugins, cn=config",
+ inst->inst_name, li->li_plugin->plg_name);
+ /* make callback on search; deny add/modify/delete */
+ slapi_config_register_callback(SLAPI_OPERATION_SEARCH, DSE_FLAG_PREOP, dn,
+ LDAP_SCOPE_BASE, "(objectclass=*)", ldbm_back_monitor_instance_search,
+ (void *)inst);
+ slapi_config_register_callback(SLAPI_OPERATION_ADD, DSE_FLAG_PREOP, dn,
+ LDAP_SCOPE_SUBTREE, "(objectclass=*)", ldbm_instance_deny_config,
+ (void *)inst);
+ slapi_config_register_callback(SLAPI_OPERATION_MODIFY, DSE_FLAG_PREOP, dn,
+ LDAP_SCOPE_BASE, "(objectclass=*)", ldbm_instance_deny_config,
+ (void *)inst);
+ /* delete is okay */
+
+ /* Callbacks to handle indexes */
+ sprintf(dn, "cn=index, cn=%s, cn=%s, cn=plugins, cn=config",
+ inst->inst_name, li->li_plugin->plg_name);
+ slapi_config_register_callback(SLAPI_OPERATION_ADD, DSE_FLAG_PREOP, dn,
+ LDAP_SCOPE_SUBTREE, "(objectclass=nsIndex)",
+ ldbm_instance_index_config_add_callback, (void *) inst);
+ slapi_config_register_callback(SLAPI_OPERATION_DELETE, DSE_FLAG_PREOP, dn,
+ LDAP_SCOPE_SUBTREE, "(objectclass=nsIndex)",
+ ldbm_instance_index_config_delete_callback, (void *) inst);
+ slapi_config_register_callback(SLAPI_OPERATION_MODIFY, DSE_FLAG_PREOP, dn,
+ LDAP_SCOPE_SUBTREE, "(objectclass=nsIndex)",
+ ldbm_instance_index_config_modify_callback, (void *) inst);
+
+ /* Callbacks to handle attribute encryption */
+ sprintf(dn, "cn=encrypted attributes, cn=%s, cn=%s, cn=plugins, cn=config",
+ inst->inst_name, li->li_plugin->plg_name);
+ slapi_config_register_callback(SLAPI_OPERATION_ADD, DSE_FLAG_PREOP, dn,
+ LDAP_SCOPE_SUBTREE, ldbm_instance_attrcrypt_filter,
+ ldbm_instance_attrcrypt_config_add_callback, (void *) inst);
+ slapi_config_register_callback(SLAPI_OPERATION_DELETE, DSE_FLAG_PREOP, dn,
+ LDAP_SCOPE_SUBTREE, ldbm_instance_attrcrypt_filter,
+ ldbm_instance_attrcrypt_config_delete_callback, (void *) inst);
+ slapi_config_register_callback(SLAPI_OPERATION_MODIFY, DSE_FLAG_PREOP, dn,
+ LDAP_SCOPE_SUBTREE, ldbm_instance_attrcrypt_filter,
+ ldbm_instance_attrcrypt_config_modify_callback, (void *) inst);
+
+ return 0;
+}
+
+/*
+ * Config. DSE callback for instance entry searches.
+ */
+int
+ldbm_instance_search_config_entry_callback(Slapi_PBlock *pb, Slapi_Entry *e, Slapi_Entry *entryAfter, int *returncode, char *returntext, void *arg)
+{
+ char buf[BUFSIZ];
+ struct berval *vals[2];
+ struct berval val;
+ ldbm_instance *inst = (ldbm_instance *) arg;
+ config_info *config;
+ int x;
+ const Slapi_DN *suffix;
+
+ vals[0] = &val;
+ vals[1] = NULL;
+
+ returntext[0] = '\0';
+
+ /* show the suffixes */
+ attrlist_delete(&e->e_attrs, CONFIG_INSTANCE_SUFFIX);
+ x = 0;
+ do {
+ suffix = slapi_be_getsuffix(inst->inst_be, x);
+ if (suffix != NULL) {
+ val.bv_val = (char *) slapi_sdn_get_dn(suffix);
+ val.bv_len = strlen (val.bv_val);
+ attrlist_merge( &e->e_attrs, CONFIG_INSTANCE_SUFFIX, vals );
+ }
+ x++;
+ } while(suffix!=NULL);
+
+ PR_Lock(inst->inst_config_mutex);
+
+ for(config = ldbm_instance_config; config->config_name != NULL; config++) {
+ /* Go through the ldbm_config table and fill in the entry. */
+
+ if (!(config->config_flags & (CONFIG_FLAG_ALWAYS_SHOW | CONFIG_FLAG_PREVIOUSLY_SET))) {
+ /* This config option shouldn't be shown */
+ continue;
+ }
+
+ ldbm_config_get((void *) inst, config, buf);
+
+ val.bv_val = buf;
+ val.bv_len = strlen(buf);
+ slapi_entry_attr_replace(e, config->config_name, vals);
+ }
+
+ PR_Unlock(inst->inst_config_mutex);
+
+ *returncode = LDAP_SUCCESS;
+ return SLAPI_DSE_CALLBACK_OK;
+}
+
+/* This function is used by the instance modify callback to add a new
+ * suffix. It return LDAP_SUCCESS on success.
+ */
+int
+add_suffix(ldbm_instance *inst, struct berval **bvals, int apply_mod, char *returntext)
+{
+ Slapi_DN suffix;
+ int x;
+
+ returntext[0] = '\0';
+ for (x = 0; bvals[x]; x++) {
+ slapi_sdn_init_dn_byref(&suffix, bvals[x]->bv_val);
+ if (!slapi_be_issuffix(inst->inst_be, &suffix) && apply_mod) {
+ be_addsuffix(inst->inst_be, &suffix);
+ }
+ slapi_sdn_done(&suffix);
+ }
+
+ return LDAP_SUCCESS;
+}
+
+/*
+ * Config. DSE callback for instance entry modifies.
+ */
+int
+ldbm_instance_modify_config_entry_callback(Slapi_PBlock *pb, Slapi_Entry* entryBefore, Slapi_Entry* e, int *returncode, char *returntext, void *arg)
+{
+ int i;
+ char *attr_name;
+ LDAPMod **mods;
+ int rc = LDAP_SUCCESS;
+ int apply_mod = 0;
+ ldbm_instance *inst = (ldbm_instance *) arg;
+
+ /* This lock is probably way too conservative, but we don't expect much
+ * contention for it. */
+ PR_Lock(inst->inst_config_mutex);
+
+ slapi_pblock_get( pb, SLAPI_MODIFY_MODS, &mods );
+
+ returntext[0] = '\0';
+
+ /*
+ * First pass: set apply mods to 0 so only input validation will be done;
+ * 2nd pass: set apply mods to 1 to apply changes to internal storage
+ */
+ for ( apply_mod = 0; apply_mod <= 1 && LDAP_SUCCESS == rc; apply_mod++ ) {
+ for (i = 0; mods[i] && LDAP_SUCCESS == rc; i++) {
+ attr_name = mods[i]->mod_type;
+
+ if (strcasecmp(attr_name, CONFIG_INSTANCE_SUFFIX) == 0) {
+ /* naughty naughty, we don't allow this */
+ rc = LDAP_UNWILLING_TO_PERFORM;
+ if (returntext) {
+ sprintf(returntext,
+ "Can't change the root suffix of a backend");
+ }
+ LDAPDebug(LDAP_DEBUG_ANY,
+ "ldbm: modify attempted to change the root suffix "
+ "of a backend (which is not allowed)\n",
+ 0, 0, 0);
+ continue;
+ }
+
+ /* There are some attributes that we don't care about, like
+ * modifiersname. */
+ if (ldbm_config_ignored_attr(attr_name)) {
+ continue;
+ }
+
+ if ((mods[i]->mod_op & LDAP_MOD_DELETE) ||
+ (mods[i]->mod_op & LDAP_MOD_ADD)) {
+ rc= LDAP_UNWILLING_TO_PERFORM;
+ sprintf(returntext, "%s attributes is not allowed",
+ (mods[i]->mod_op & LDAP_MOD_DELETE) ?
+ "Deleting" : "Adding");
+ } else if (mods[i]->mod_op & LDAP_MOD_REPLACE) {
+ /* This assumes there is only one bval for this mod. */
+ rc = ldbm_config_set((void *) inst, attr_name,
+ ldbm_instance_config, mods[i]->mod_bvalues[0], returntext,
+ CONFIG_PHASE_RUNNING, apply_mod);
+ }
+ }
+ }
+
+ PR_Unlock(inst->inst_config_mutex);
+
+ *returncode = rc;
+ if (LDAP_SUCCESS == rc) {
+ return SLAPI_DSE_CALLBACK_OK;
+ } else {
+ return SLAPI_DSE_CALLBACK_ERROR;
+ }
+}
+
+/* This function is used to set instance config attributes. It can be used as a
+ * shortcut to doing an internal modify operation on the config DSE.
+ */
+void
+ldbm_instance_config_internal_set(ldbm_instance *inst, char *attrname, char *value)
+{
+ char err_buf[BUFSIZ];
+ struct berval bval;
+
+ bval.bv_val = value;
+ bval.bv_len = strlen(value);
+
+ if (ldbm_config_set((void *) inst, attrname, ldbm_instance_config, &bval,
+ err_buf, CONFIG_PHASE_INTERNAL, 1 /* apply */) != LDAP_SUCCESS) {
+ LDAPDebug(LDAP_DEBUG_ANY,
+ "Internal Error: Error setting instance config attr %s to %s: %s\n",
+ attrname, value, err_buf);
+ exit(1);
+ }
+}
+
+
+
+static int ldbm_instance_generate(struct ldbminfo *li, char *instance_name,
+ Slapi_Backend **ret_be)
+{
+ Slapi_Backend *new_be = NULL;
+ int rc = 0;
+
+ /* Create a new instance, process config info for it,
+ * and then call slapi_be_new and create a new backend here
+ */
+ new_be = slapi_be_new(LDBM_DATABASE_TYPE_NAME /* type */, instance_name,
+ 0 /* public */, 1 /* do log changes */);
+ new_be->be_database = li->li_plugin;
+ ldbm_instance_create(new_be, instance_name);
+
+ ldbm_instance_config_load_dse_info(new_be->be_instance_info);
+ rc = ldbm_instance_create_default_indexes(new_be);
+
+ if (ret_be != NULL) {
+ *ret_be = new_be;
+ }
+
+ return rc;
+}
+
+int
+ldbm_instance_postadd_instance_entry_callback(Slapi_PBlock *pb, Slapi_Entry* entryBefore, Slapi_Entry* entryAfter, int *returncode, char *returntext, void *arg)
+{
+ backend *be = NULL;
+ struct ldbm_instance *inst;
+ char *instance_name;
+ struct ldbminfo *li = (struct ldbminfo *)arg;
+ int rval = 0;
+
+ parse_ldbm_instance_entry(entryBefore, &instance_name);
+ ldbm_instance_generate(li, instance_name, &be);
+
+ inst = ldbm_instance_find_by_name(li, instance_name);
+
+ /* Add default indexes */
+ ldbm_instance_create_default_user_indexes(inst);
+
+ /* Initialize and register callbacks for VLV indexes */
+ vlv_init(inst);
+
+ /* this is an ACTUAL ADD being done while the server is running!
+ * start up the appropriate backend...
+ */
+ rval = ldbm_instance_start(be);
+ if (0 != rval)
+ {
+ LDAPDebug(LDAP_DEBUG_ANY,
+ "ldbm_instance_postadd_instance_entry_callback: "
+ "ldbm_instnace_start (%s) failed (%d)\n",
+ instance_name, rval, 0);
+ }
+
+ slapi_ch_free((void **)&instance_name);
+
+ /* instance must be fully ready before we call this */
+ slapi_mtn_be_started(be);
+
+ return SLAPI_DSE_CALLBACK_OK;
+}
+
+int
+ldbm_instance_add_instance_entry_callback(Slapi_PBlock *pb, Slapi_Entry* entryBefore, Slapi_Entry* entryAfter, int *returncode, char *returntext, void *arg)
+{
+ char *instance_name;
+ struct ldbm_instance *inst= NULL;
+ struct ldbminfo *li= (struct ldbminfo *) arg;
+ int rc = 0;
+
+ parse_ldbm_instance_entry(entryBefore, &instance_name);
+
+ /* Make sure we don't create two instances with the same name. */
+ inst = ldbm_instance_find_by_name(li, instance_name);
+ if (inst != NULL) {
+ LDAPDebug(LDAP_DEBUG_ANY, "WARNING: ldbm instance %s already exists\n",
+ instance_name, 0, 0);
+ if (returntext != NULL)
+ sprintf(returntext, "An ldbm instance with the name %s already exists\n",
+ instance_name);
+ if (returncode != NULL)
+ *returncode = LDAP_UNWILLING_TO_PERFORM;
+ slapi_ch_free((void **)&instance_name);
+ return SLAPI_DSE_CALLBACK_ERROR;
+ }
+
+ if (pb == NULL) {
+ /* called during startup -- do the rest now */
+ rc = ldbm_instance_generate(li, instance_name, NULL);
+ }
+ /* if called during a normal ADD operation, the postadd callback
+ * will do the rest.
+ */
+
+ slapi_ch_free((void **)&instance_name);
+ return (rc == 0) ? SLAPI_DSE_CALLBACK_OK : SLAPI_DSE_CALLBACK_ERROR;
+}
+
+
+
+
+/* unregister the DSE callbacks on a backend -- this needs to be done when
+ * deleting a backend, so that adding the same backend later won't cause
+ * these expired callbacks to be called.
+ */
+static void ldbm_instance_unregister_callbacks(ldbm_instance *inst)
+{
+ struct ldbminfo *li = inst->inst_li;
+ char dn[BUFSIZ];
+
+ /* tear down callbacks for the instance config entry */
+ sprintf(dn, "cn=%s, cn=%s, cn=plugins, cn=config",
+ inst->inst_name, li->li_plugin->plg_name);
+ slapi_config_remove_callback(SLAPI_OPERATION_SEARCH, DSE_FLAG_PREOP, dn,
+ LDAP_SCOPE_BASE, "(objectclass=*)",
+ ldbm_instance_search_config_entry_callback);
+ slapi_config_remove_callback(SLAPI_OPERATION_MODIFY, DSE_FLAG_PREOP, dn,
+ LDAP_SCOPE_BASE, "(objectclass=*)",
+ ldbm_instance_modify_config_entry_callback);
+ slapi_config_remove_callback(DSE_OPERATION_WRITE, DSE_FLAG_PREOP, dn,
+ LDAP_SCOPE_BASE, "(objectclass=*)",
+ ldbm_instance_search_config_entry_callback);
+ slapi_config_remove_callback(SLAPI_OPERATION_ADD, DSE_FLAG_PREOP, dn,
+ LDAP_SCOPE_BASE, "(objectclass=*)",
+ ldbm_instance_deny_config);
+
+ /* now the cn=monitor entry */
+ sprintf(dn, "cn=monitor, cn=%s, cn=%s, cn=plugins, cn=config",
+ inst->inst_name, li->li_plugin->plg_name);
+ slapi_config_remove_callback(SLAPI_OPERATION_SEARCH, DSE_FLAG_PREOP, dn,
+ LDAP_SCOPE_BASE, "(objectclass=*)", ldbm_back_monitor_instance_search);
+ slapi_config_remove_callback(SLAPI_OPERATION_ADD, DSE_FLAG_PREOP, dn,
+ LDAP_SCOPE_SUBTREE, "(objectclass=*)", ldbm_instance_deny_config);
+ slapi_config_remove_callback(SLAPI_OPERATION_MODIFY, DSE_FLAG_PREOP, dn,
+ LDAP_SCOPE_BASE, "(objectclass=*)", ldbm_instance_deny_config);
+
+ /* now the cn=index entries */
+ sprintf(dn, "cn=index, cn=%s, cn=%s, cn=plugins, cn=config",
+ inst->inst_name, li->li_plugin->plg_name);
+ slapi_config_remove_callback(SLAPI_OPERATION_ADD, DSE_FLAG_PREOP, dn,
+ LDAP_SCOPE_SUBTREE, "(objectclass=nsIndex)",
+ ldbm_instance_index_config_add_callback);
+ slapi_config_remove_callback(SLAPI_OPERATION_DELETE, DSE_FLAG_PREOP, dn,
+ LDAP_SCOPE_SUBTREE, "(objectclass=nsIndex)",
+ ldbm_instance_index_config_delete_callback);
+ slapi_config_remove_callback(SLAPI_OPERATION_MODIFY, DSE_FLAG_PREOP, dn,
+ LDAP_SCOPE_SUBTREE, "(objectclass=nsIndex)",
+ ldbm_instance_index_config_modify_callback);
+
+ /* now the cn=encrypted attributes entries */
+ sprintf(dn, "cn=encrypted attributes, cn=%s, cn=%s, cn=plugins, cn=config",
+ inst->inst_name, li->li_plugin->plg_name);
+ slapi_config_remove_callback(SLAPI_OPERATION_ADD, DSE_FLAG_PREOP, dn,
+ LDAP_SCOPE_SUBTREE, ldbm_instance_attrcrypt_filter,
+ ldbm_instance_attrcrypt_config_add_callback);
+ slapi_config_remove_callback(SLAPI_OPERATION_DELETE, DSE_FLAG_PREOP, dn,
+ LDAP_SCOPE_SUBTREE, ldbm_instance_attrcrypt_filter,
+ ldbm_instance_attrcrypt_config_delete_callback);
+ slapi_config_remove_callback(SLAPI_OPERATION_MODIFY, DSE_FLAG_PREOP, dn,
+ LDAP_SCOPE_SUBTREE, ldbm_instance_attrcrypt_filter,
+ ldbm_instance_attrcrypt_config_modify_callback);
+
+ vlv_remove_callbacks(inst);
+}
+
+
+int
+ldbm_instance_post_delete_instance_entry_callback(Slapi_PBlock *pb, Slapi_Entry* entryBefore, Slapi_Entry* entryAfter, int *returncode, char *returntext, void *arg)
+{
+ char *instance_name;
+ struct ldbminfo *li = (struct ldbminfo *)arg;
+ struct ldbm_instance *inst = NULL;
+
+ parse_ldbm_instance_entry(entryBefore, &instance_name);
+ inst = ldbm_instance_find_by_name(li, instance_name);
+
+ if (inst == NULL) {
+ LDAPDebug(LDAP_DEBUG_ANY, "ldbm: instance '%s' does not exist! (2)\n",
+ instance_name, 0, 0);
+ if (returntext) {
+ sprintf(returntext, "No ldbm instance exists with the name '%s' (2)\n",
+ instance_name);
+ }
+ if (returncode) {
+ *returncode = LDAP_UNWILLING_TO_PERFORM;
+ }
+ slapi_ch_free((void **)&instance_name);
+ return SLAPI_DSE_CALLBACK_ERROR;
+ }
+
+ LDAPDebug(LDAP_DEBUG_ANY, "ldbm: removing '%s'.\n", instance_name, 0, 0);
+
+ {
+ struct ldbminfo *li = (struct ldbminfo *) inst->inst_be->be_database->plg_private;
+ dblayer_private *priv = (dblayer_private*) li->li_dblayer_private;
+ struct dblayer_private_env *pEnv = priv->dblayer_env;
+ if(pEnv) {
+ PRDir *dirhandle = NULL;
+ char dbName[MAXPATHLEN*2];
+ char *dbNamep = NULL;
+ char *p;
+ int dbbasenamelen, dbnamelen;
+ int rc;
+ if (inst->inst_dir_name == NULL){
+ dblayer_get_instance_data_dir(inst->inst_be);
+ }
+ dirhandle = PR_OpenDir(inst->inst_dir_name);
+ /* the db dir instance may have been removed already */
+ if (dirhandle){
+ dbNamep = dblayer_get_full_inst_dir(li, inst,
+ dbName, MAXPATHLEN*2);
+ dbbasenamelen = strlen(dbNamep);
+ dbnamelen = dbbasenamelen + 14; /* "/id2entry.db#" + '\0' */
+ if (dbnamelen > MAXPATHLEN*2)
+ {
+ dbNamep = (char *)slapi_ch_realloc(dbNamep, dbnamelen);
+ }
+ p = dbNamep + dbbasenamelen;
+ sprintf(p, "%c%s%s", get_sep(dbNamep),
+ "id2entry", LDBM_FILENAME_SUFFIX);
+ rc = dblayer_db_remove(pEnv, dbName, 0);
+ PR_ASSERT(rc == 0);
+ if (dbNamep != dbName)
+ slapi_ch_free_string(&dbNamep);
+ PR_CloseDir(dirhandle);
+ } /* non-null dirhandle */
+ } /* non-null pEnv */
+ }
+
+ ldbm_instance_unregister_callbacks(inst);
+ slapi_be_free(&inst->inst_be);
+ ldbm_instance_destroy(inst);
+ slapi_ch_free((void **)&instance_name);
+
+ return SLAPI_DSE_CALLBACK_OK;
+}
+
+int
+ldbm_instance_delete_instance_entry_callback(Slapi_PBlock *pb, Slapi_Entry* entryBefore, Slapi_Entry* entryAfter, int *returncode, char *returntext, void *arg)
+{
+ char *instance_name;
+ struct ldbminfo *li = (struct ldbminfo *)arg;
+ struct ldbm_instance *inst = NULL;
+
+ parse_ldbm_instance_entry(entryBefore, &instance_name);
+ inst = ldbm_instance_find_by_name(li, instance_name);
+ if (inst == NULL) {
+ LDAPDebug(LDAP_DEBUG_ANY, "ldbm: instance '%s' does not exist!\n",
+ instance_name, 0, 0);
+ if (returntext) {
+ sprintf(returntext, "No ldbm instance exists with the name '%s'\n",
+ instance_name);
+ }
+ if (returncode) {
+ *returncode = LDAP_UNWILLING_TO_PERFORM;
+ }
+ slapi_ch_free((void **)&instance_name);
+ return SLAPI_DSE_CALLBACK_ERROR;
+ }
+
+ /* check if some online task is happening */
+ if (instance_set_busy(inst) != 0) {
+ LDAPDebug(LDAP_DEBUG_ANY, "ldbm: '%s' is in the middle of a task. "
+ "Cancel the task or wait for it to finish, "
+ "then try again.\n", instance_name, 0, 0);
+ if (returntext) {
+ sprintf(returntext, "ldbm instance '%s' is in the middle of a "
+ "task. Cancel the task or wait for it to finish, "
+ "then try again.\n", instance_name);
+ }
+ if (returncode) {
+ *returncode = LDAP_UNWILLING_TO_PERFORM;
+ }
+ slapi_ch_free((void **)&instance_name);
+ return SLAPI_DSE_CALLBACK_ERROR;
+ }
+
+ /* okay, we're gonna delete this database instance. take it offline. */
+ LDAPDebug(LDAP_DEBUG_ANY, "ldbm: Bringing %s offline...\n",
+ instance_name, 0, 0);
+ slapi_mtn_be_stopping(inst->inst_be);
+ dblayer_instance_close(inst->inst_be);
+ cache_destroy_please(&inst->inst_cache);
+ slapi_ch_free((void **)&instance_name);
+
+ return SLAPI_DSE_CALLBACK_OK;
+}
+
diff --git a/ldap/servers/slapd/back-ldbm/ldbm_modify.c b/ldap/servers/slapd/back-ldbm/ldbm_modify.c
new file mode 100644
index 00000000..1df2e6fd
--- /dev/null
+++ b/ldap/servers/slapd/back-ldbm/ldbm_modify.c
@@ -0,0 +1,567 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+/* modify.c - ldbm backend modify routine */
+
+#include "back-ldbm.h"
+
+extern char *numsubordinates;
+extern char *hassubordinates;
+
+static void remove_illegal_mods(LDAPMod **mods);
+static int mods_have_effect (Slapi_Entry *entry, Slapi_Mods *smods);
+
+/* Modify context structure constructor, sans allocation */
+void modify_init(modify_context *mc,struct backentry *old_entry)
+{
+ /* Store the old entry */
+ PR_ASSERT(NULL == mc->old_entry);
+ PR_ASSERT(NULL == mc->new_entry);
+
+ mc->old_entry = old_entry;
+ mc->new_entry_in_cache = 0;
+}
+
+int modify_apply_mods(modify_context *mc, Slapi_Mods *smods)
+{
+ int ret = 0;
+ /* Make a copy of the entry */
+ PR_ASSERT(mc->old_entry != NULL);
+ PR_ASSERT(mc->new_entry == NULL);
+ mc->new_entry = backentry_dup(mc->old_entry);
+ PR_ASSERT(smods!=NULL);
+ if ( mods_have_effect (mc->new_entry->ep_entry, smods) ) {
+ ret = entry_apply_mods( mc->new_entry->ep_entry, slapi_mods_get_ldapmods_byref(smods));
+ }
+ mc->smods= smods;
+ return ret;
+}
+
+/* Modify context structure destructor */
+int modify_term(modify_context *mc,struct backend *be)
+{
+ ldbm_instance *inst = (ldbm_instance *) be->be_instance_info;
+
+ slapi_mods_free(&mc->smods);
+ /* Unlock and return entries */
+ if (NULL != mc->old_entry) {
+ cache_unlock_entry(&inst->inst_cache, mc->old_entry);
+ cache_return( &(inst->inst_cache), &(mc->old_entry) );
+ mc->old_entry= NULL;
+ }
+ if (mc->new_entry_in_cache) {
+ cache_return( &(inst->inst_cache), &(mc->new_entry) );
+ } else {
+ backentry_free(&(mc->new_entry));
+ }
+ mc->new_entry= NULL;
+ return 0;
+}
+
+/* Modify context structure member to switch entries in the cache */
+int modify_switch_entries(modify_context *mc,backend *be)
+{
+ ldbm_instance *inst = (ldbm_instance *) be->be_instance_info;
+ int ret = 0;
+ if (mc->old_entry!=NULL && mc->new_entry!=NULL) {
+ ret = cache_replace(&(inst->inst_cache), mc->old_entry, mc->new_entry);
+ if (ret == 0) mc->new_entry_in_cache = 1;
+ }
+ return ret;
+}
+
+/* This routine does that part of a modify operation which involves
+ updating the on-disk data: updates idices, id2entry.
+ Copes properly with DB_LOCK_DEADLOCK. The caller must be able to cope with
+ DB_LOCK_DEADLOCK returned.
+ The caller is presumed to proceed as follows:
+ Find the entry you want to modify;
+ Lock it for modify;
+ Make a copy of it; (call backentry_dup() )
+ Apply modifications to the copy in memory (call entry_apply_mods() )
+ begin transaction;
+ Do any other mods to on-disk data you want
+ Call this routine;
+ Commit transaction;
+ You pass it environment data: struct ldbminfo, pb (not sure why, but the vlv code seems to need it)
+ the copy of the entry before modfication, the entry after modification;
+ an LDAPMods array containing the modifications performed
+*/
+int modify_update_all(backend *be, Slapi_PBlock *pb,
+ modify_context *mc,
+ back_txn *txn)
+{
+ static char *function_name = "modify_update_all";
+ int retval = 0;
+
+ /*
+ * Update the ID to Entry index.
+ * Note that id2entry_add replaces the entry, so the Entry ID stays the same.
+ */
+ retval = id2entry_add( be, mc->new_entry, txn );
+ if ( 0 != retval ) {
+ if (DB_LOCK_DEADLOCK != retval)
+ {
+ ldbm_nasty(function_name,66,retval);
+ }
+ goto error;
+ }
+ retval = index_add_mods( be, (const LDAPMod **)slapi_mods_get_ldapmods_byref(mc->smods), mc->old_entry, mc->new_entry, txn );
+ if ( 0 != retval ) {
+ if (DB_LOCK_DEADLOCK != retval)
+ {
+ ldbm_nasty(function_name,65,retval);
+ }
+ goto error;
+ }
+ /*
+ * Remove the old entry from the Virtual List View indexes.
+ * Add the new entry to the Virtual List View indexes.
+ * Because the VLV code calls slapi_filter_test(), which requires a pb (why?),
+ * we allow the caller sans pb to get everything except vlv indexing.
+ */
+ if (NULL != pb) {
+ retval= vlv_update_all_indexes(txn, be, pb, mc->old_entry, mc->new_entry);
+ if ( 0 != retval ) {
+ if (DB_LOCK_DEADLOCK != retval)
+ {
+ ldbm_nasty(function_name,64,retval);
+ }
+ goto error;
+ }
+ }
+error:
+ return retval;
+}
+
+int
+ldbm_back_modify( Slapi_PBlock *pb )
+{
+ backend *be;
+ ldbm_instance *inst;
+ struct ldbminfo *li;
+ struct backentry *e, *ec = NULL;
+ Slapi_Entry *postentry = NULL;
+ LDAPMod **mods;
+ back_txn txn;
+ back_txnid parent_txn;
+ int retval = -1;
+ char *msg;
+ char *errbuf = NULL;
+ int retry_count = 0;
+ int disk_full = 0;
+ int ldap_result_code= LDAP_SUCCESS;
+ char *ldap_result_message= NULL;
+ int rc = 0;
+ Slapi_Operation *operation;
+ int dblock_acquired= 0;
+ entry_address *addr;
+ int change_entry = 0;
+ int ec_in_cache = 0;
+ int is_fixup_operation= 0;
+ CSN *opcsn = NULL;
+ int repl_op;
+
+ slapi_pblock_get( pb, SLAPI_BACKEND, &be);
+ slapi_pblock_get( pb, SLAPI_PLUGIN_PRIVATE, &li );
+ slapi_pblock_get( pb, SLAPI_TARGET_ADDRESS, &addr );
+ slapi_pblock_get( pb, SLAPI_MODIFY_MODS, &mods );
+ slapi_pblock_get( pb, SLAPI_PARENT_TXN, (void**)&parent_txn );
+ slapi_pblock_get( pb, SLAPI_OPERATION, &operation );
+ slapi_pblock_get (pb, SLAPI_IS_REPLICATED_OPERATION, &repl_op);
+ is_fixup_operation = operation_is_flag_set(operation,OP_FLAG_REPL_FIXUP);
+ inst = (ldbm_instance *) be->be_instance_info;
+
+ dblayer_txn_init(li,&txn);
+
+ /* The dblock serializes writes to the database,
+ * which reduces deadlocking in the db code,
+ * which means that we run faster.
+ *
+ * But, this lock is re-enterant for the fixup
+ * operations that the URP code in the Replication
+ * plugin generates.
+ */
+ if(SERIALLOCK(li) && !operation_is_flag_set(operation,OP_FLAG_REPL_FIXUP))
+ {
+ dblayer_lock_backend(be);
+ dblock_acquired= 1;
+ }
+
+ /* find and lock the entry we are about to modify */
+ if ( (e = find_entry2modify( pb, be, addr, NULL )) == NULL ) {
+ ldap_result_code= -1;
+ goto error_return; /* error result sent by find_entry2modify() */
+ }
+
+ if ( !is_fixup_operation )
+ {
+ opcsn = operation_get_csn (operation);
+ if (NULL == opcsn && operation->o_csngen_handler)
+ {
+ /*
+ * Current op is a user request. Opcsn will be assigned
+ * if the dn is in an updatable replica.
+ */
+ opcsn = entry_assign_operation_csn ( pb, e->ep_entry, NULL );
+ }
+ if (opcsn)
+ {
+ entry_set_maxcsn (e->ep_entry, opcsn);
+ }
+ }
+
+ /* Save away a copy of the entry, before modifications */
+ slapi_pblock_set( pb, SLAPI_ENTRY_PRE_OP, slapi_entry_dup( e->ep_entry ));
+
+ if ( (ldap_result_code = plugin_call_acl_mods_access( pb, e->ep_entry, mods, &errbuf)) != LDAP_SUCCESS ) {
+ ldap_result_message= errbuf;
+ goto error_return;
+ }
+
+ /* create a copy of the entry and apply the changes to it */
+ if ( (ec = backentry_dup( e )) == NULL ) {
+ ldap_result_code= LDAP_OPERATIONS_ERROR;
+ goto error_return;
+ }
+
+ remove_illegal_mods(mods);
+
+ /* ec is the entry that our bepreop should get to mess with */
+ slapi_pblock_set( pb, SLAPI_MODIFY_EXISTING_ENTRY, ec->ep_entry );
+ slapi_pblock_set(pb, SLAPI_RESULT_CODE, &ldap_result_code);
+ plugin_call_plugins(pb, SLAPI_PLUGIN_BE_PRE_MODIFY_FN);
+ slapi_pblock_get(pb, SLAPI_RESULT_CODE, &ldap_result_code);
+ /* The Plugin may have messed about with some of the PBlock parameters... ie. mods */
+ slapi_pblock_get( pb, SLAPI_MODIFY_MODS, &mods );
+
+ {
+ Slapi_Mods smods;
+ CSN *csn = operation_get_csn(operation);
+ slapi_mods_init_byref(&smods,mods);
+ if ( (change_entry = mods_have_effect (ec->ep_entry, &smods)) ) {
+ ldap_result_code = entry_apply_mods_wsi(ec->ep_entry, &smods, csn, operation_is_flag_set(operation,OP_FLAG_REPLICATED));
+ /*
+ * XXXmcs: it would be nice to get back an error message from
+ * the above call so we could pass it along to the client, e.g.,
+ * "duplicate value for attribute givenName."
+ */
+ } else {
+ /* If the entry was not actually changed, we still need to
+ * set the SLAPI_ENTRY_POST_OP field in the pblock (post-op
+ * plugins expect that field to be present for all modify
+ * operations that return LDAP_SUCCESS).
+ */
+ postentry = slapi_entry_dup( e->ep_entry );
+ slapi_pblock_set ( pb, SLAPI_ENTRY_POST_OP, postentry );
+ postentry = NULL; /* avoid removal/free in error_return code */
+ }
+ slapi_mods_done(&smods);
+ if ( !change_entry || ldap_result_code != 0 ) {
+ /* change_entry == 0 is not an error, but we need to free lock etc */
+ goto error_return;
+ }
+ }
+
+ /*
+ * If we are not handling a replicated operation, AND if the
+ * objectClass attribute type was modified in any way, expand
+ * the objectClass values to reflect the inheritance hierarchy.
+ * [blackflag 624152]: repl_op covers both regular and legacy replication
+ */
+ if(!repl_op)
+ {
+ int i;
+
+ for ( i = 0; mods[i] != NULL; ++i ) {
+ if ( 0 == strcasecmp( SLAPI_ATTR_OBJECTCLASS, mods[i]->mod_type )) {
+ slapi_schema_expand_objectclasses( ec->ep_entry );
+ break;
+ }
+ }
+ }
+
+ /*
+ * We are about to pass the last abandon test, so from now on we are
+ * committed to finish this operation. Set status to "will complete"
+ * before we make our last abandon check to avoid race conditions in
+ * the code that processes abandon operations.
+ */
+ if (operation) {
+ operation->o_status = SLAPI_OP_STATUS_WILL_COMPLETE;
+ }
+ if ( slapi_op_abandoned( pb ) ) {
+ goto error_return;
+ }
+
+ /* check that the entry still obeys the schema */
+ if ( (operation_is_flag_set(operation,OP_FLAG_ACTION_SCHEMA_CHECK)) &&
+ slapi_entry_schema_check( pb, ec->ep_entry ) != 0 ) {
+ ldap_result_code= LDAP_OBJECT_CLASS_VIOLATION;
+ slapi_pblock_get(pb, SLAPI_PB_RESULT_TEXT, &ldap_result_message);
+ goto error_return;
+ }
+
+ /*
+ * make sure the entry contains all values in the RDN.
+ * if not, the modification must have removed them.
+ */
+ if ( ! slapi_entry_rdn_values_present( ec->ep_entry ) ) {
+ ldap_result_code= LDAP_NOT_ALLOWED_ON_RDN;
+ goto error_return;
+ }
+
+ for (retry_count = 0; retry_count < RETRY_TIMES; retry_count++) {
+
+ if (retry_count > 0) {
+ dblayer_txn_abort(li,&txn);
+ LDAPDebug( LDAP_DEBUG_TRACE, "Modify Retrying Transaction\n", 0, 0, 0 );
+#ifndef LDBM_NO_BACKOFF_DELAY
+ {
+ PRIntervalTime interval;
+ interval = PR_MillisecondsToInterval(slapi_rand() % 100);
+ DS_Sleep(interval);
+ }
+#endif
+ }
+
+ /* Nothing above here modifies persistent store, everything after here is subject to the transaction */
+ retval = dblayer_txn_begin(li,parent_txn,&txn);
+
+ if (0 != retval) {
+ if (LDBM_OS_ERR_IS_DISKFULL(retval)) disk_full = 1;
+ ldap_result_code= LDAP_OPERATIONS_ERROR;
+ goto error_return;
+ }
+
+ /*
+ * Update the ID to Entry index.
+ * Note that id2entry_add replaces the entry, so the Entry ID stays the same.
+ */
+ retval = id2entry_add( be, ec, &txn );
+ if (DB_LOCK_DEADLOCK == retval)
+ {
+ /* Abort and re-try */
+ continue;
+ }
+ if (0 != retval) {
+ LDAPDebug( LDAP_DEBUG_ANY, "id2entry_add failed, err=%d %s\n",
+ retval, (msg = dblayer_strerror( retval )) ? msg : "", 0 );
+ if (LDBM_OS_ERR_IS_DISKFULL(retval)) disk_full = 1;
+ ldap_result_code= LDAP_OPERATIONS_ERROR;
+ goto error_return;
+ }
+ ec_in_cache = 1;
+ retval = index_add_mods( be, (const LDAPMod**)mods, e, ec, &txn );
+ if (DB_LOCK_DEADLOCK == retval)
+ {
+ /* Abort and re-try */
+ continue;
+ }
+ if (0 != retval) {
+ LDAPDebug( LDAP_DEBUG_ANY, "index_add_mods failed, err=%d %s\n",
+ retval, (msg = dblayer_strerror( retval )) ? msg : "", 0 );
+ if (LDBM_OS_ERR_IS_DISKFULL(retval)) disk_full = 1;
+ ldap_result_code= LDAP_OPERATIONS_ERROR;
+ goto error_return;
+ }
+ /*
+ * Remove the old entry from the Virtual List View indexes.
+ * Add the new entry to the Virtual List View indexes.
+ */
+ retval= vlv_update_all_indexes(&txn, be, pb, e, ec);
+ if (DB_LOCK_DEADLOCK == retval)
+ {
+ /* Abort and re-try */
+ continue;
+ }
+ if (0 != retval) {
+ LDAPDebug( LDAP_DEBUG_ANY, "vlv_update_index failed, err=%d %s\n",
+ retval, (msg = dblayer_strerror( retval )) ? msg : "", 0 );
+ if (LDBM_OS_ERR_IS_DISKFULL(retval)) disk_full = 1;
+ ldap_result_code= LDAP_OPERATIONS_ERROR;
+ goto error_return;
+ }
+
+ if (0 == retval) {
+ break;
+ }
+ }
+ if (retry_count == RETRY_TIMES) {
+ LDAPDebug( LDAP_DEBUG_ANY, "Retry count exceeded in modify\n", 0, 0, 0 );
+ ldap_result_code= LDAP_OPERATIONS_ERROR;
+ goto error_return;
+ }
+
+ if (cache_replace( &inst->inst_cache, e, ec ) != 0 ) {
+ ldap_result_code= LDAP_OPERATIONS_ERROR;
+ goto error_return;
+ }
+
+ postentry = slapi_entry_dup( ec->ep_entry );
+ slapi_pblock_set( pb, SLAPI_ENTRY_POST_OP, postentry );
+
+ /* invalidate virtual cache */
+ ec->ep_entry->e_virtual_watermark = 0;
+
+ /* we must return both e (which has been deleted) and new entry ec */
+ cache_unlock_entry( &inst->inst_cache, e );
+ cache_return( &inst->inst_cache, &e );
+ /*
+ * LP Fix of crash when the commit will fail:
+ * If the commit fail, the common error path will
+ * try to unlock the entry again and crash (PR_ASSERT
+ * in debug mode.
+ * By just setting e to NULL, we avoid this. It's OK since
+ * we don't use e after that in the normal case.
+ */
+ e = NULL;
+
+ retval = dblayer_txn_commit(li,&txn);
+ if (0 != retval) {
+ if (LDBM_OS_ERR_IS_DISKFULL(retval)) disk_full = 1;
+ ldap_result_code= LDAP_OPERATIONS_ERROR;
+ goto error_return;
+ }
+
+ rc= 0;
+ goto common_return;
+
+error_return:
+ if (ec_in_cache)
+ {
+ cache_remove( &inst->inst_cache, ec );
+ }
+ else
+ {
+ backentry_free(&ec);
+ }
+ if ( postentry != NULL )
+ {
+ slapi_entry_free( postentry );
+ postentry = NULL;
+ slapi_pblock_set( pb, SLAPI_ENTRY_POST_OP, NULL );
+ }
+
+ if (e!=NULL) {
+ cache_unlock_entry( &inst->inst_cache, e);
+ cache_return( &inst->inst_cache, &e);
+ }
+
+ if (retval == DB_RUNRECOVERY) {
+ dblayer_remember_disk_filled(li);
+ ldbm_nasty("Modify",81,retval);
+ disk_full = 1;
+ }
+
+ if (disk_full)
+ rc= return_on_disk_full(li);
+ else if (ldap_result_code != LDAP_SUCCESS) {
+ /* It is specifically OK to make this call even when no transaction was in progress */
+ dblayer_txn_abort(li,&txn); /* abort crashes in case disk full */
+ rc= SLAPI_FAIL_GENERAL;
+ }
+
+
+common_return:
+
+ if (ec_in_cache)
+ {
+ cache_return( &inst->inst_cache, &ec );
+ }
+ /* JCMREPL - The bepostop is called even if the operation fails. */
+ if (!disk_full)
+ plugin_call_plugins (pb, SLAPI_PLUGIN_BE_POST_MODIFY_FN);
+
+ if(dblock_acquired)
+ {
+ dblayer_unlock_backend(be);
+ }
+ if(ldap_result_code!=-1)
+ {
+ slapi_send_ldap_result( pb, ldap_result_code, NULL, ldap_result_message, 0, NULL );
+ }
+
+ slapi_ch_free( (void**)&errbuf);
+ return rc;
+}
+
+/* Function removes mods which are not allowed over-the-wire */
+static void
+remove_illegal_mods(LDAPMod **mods)
+{
+ int i, j;
+ LDAPMod *tmp;
+
+ /* remove any attempts by the user to modify these attrs */
+ for ( i = 0; mods[i] != NULL; i++ ) {
+ if ( strcasecmp( mods[i]->mod_type, numsubordinates ) == 0
+ || strcasecmp( mods[i]->mod_type, hassubordinates ) == 0 )
+ {
+ tmp = mods[i];
+ for ( j = i; mods[j] != NULL; j++ ) {
+ mods[j] = mods[j + 1];
+ }
+ slapi_ch_free( (void**)&(tmp->mod_type) );
+ if ( tmp->mod_bvalues != NULL ) {
+ ber_bvecfree( tmp->mod_bvalues );
+ }
+ slapi_ch_free( (void**)&tmp );
+ i--;
+ }
+ }
+}
+
+/* A mod has no effect if it is trying to replace a non-existing
+ * attribute with null value
+ */
+static int
+mods_have_effect (Slapi_Entry *entry, Slapi_Mods *smods)
+{
+ LDAPMod *mod;
+ Slapi_Attr *attr;
+ int have_effect = 1;
+ int j;
+
+ /* Mods have effect if there is at least a non-replace mod or
+ * a non-null-value mod.
+ */
+ for ( j = 0; j < smods->num_mods - 1; j++ ) {
+ if ( (mod = smods->mods[j]) != NULL ) {
+ if ( (mod->mod_op & LDAP_MOD_REPLACE) == 0 ||
+ mod->mod_vals.modv_bvals &&
+ strcasecmp (mod->mod_type, "modifiersname") &&
+ strcasecmp (mod->mod_type, "modifytime") ) {
+ goto done;
+ }
+ }
+ }
+
+ if ( entry && entry->e_sdn.dn ) {
+ for ( j = 0; j < smods->num_mods - 1; j++ ) {
+ if ( (mod = smods->mods[j]) != NULL &&
+ strcasecmp (mod->mod_type, "modifiersname") &&
+ strcasecmp (mod->mod_type, "modifytime") ) {
+ for ( attr = entry->e_attrs; attr; attr = attr->a_next ) {
+ /* Mods have effect if at least a null-value-mod is
+ * to actually remove an existing attribute
+ */
+ if ( strcasecmp ( mod->mod_type, attr->a_type ) == 0 ) {
+ goto done;
+ }
+ }
+ have_effect = 0;
+ }
+ }
+
+ }
+
+done:
+
+ /* Return true would let the flow continue along the old path before
+ * this function was added
+ */
+ return have_effect;
+}
diff --git a/ldap/servers/slapd/back-ldbm/ldbm_modrdn.c b/ldap/servers/slapd/back-ldbm/ldbm_modrdn.c
new file mode 100644
index 00000000..8658886a
--- /dev/null
+++ b/ldap/servers/slapd/back-ldbm/ldbm_modrdn.c
@@ -0,0 +1,1403 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+/* modrdn.c - ldbm backend modrdn routine */
+
+#include "back-ldbm.h"
+
+static const char *moddn_get_newdn(Slapi_PBlock *pb, Slapi_DN *dn_olddn, Slapi_DN *dn_newrdn, Slapi_DN *dn_newsuperiordn);
+static void moddn_unlock_and_return_entries(backend *be,struct backentry **targetentry, struct backentry **existingentry);
+static int moddn_newrdn_mods(Slapi_PBlock *pb, const char *olddn, struct backentry *ec, Slapi_Mods *smods, Slapi_Mods *smods_wsi, int is_repl_op);
+static IDList *moddn_get_children(back_txn *ptxn, Slapi_PBlock *pb, backend *be, struct backentry *parententry, Slapi_DN *parentdn, struct backentry ***child_entries, struct backentry ***child_entry_copies);
+static int moddn_rename_children(back_txn *ptxn, Slapi_PBlock *pb, backend *be, IDList *children, Slapi_DN *dn_parentdn, Slapi_DN *dn_newsuperiordn, struct backentry *child_entries[], struct backentry *child_entry_copies[]);
+static int modrdn_rename_entry_update_indexes(back_txn *ptxn, Slapi_PBlock *pb, struct ldbminfo *li, struct backentry *e, struct backentry *ec, Slapi_Mods *smods1, Slapi_Mods *smods2, Slapi_Mods *smods3);
+static void mods_remove_nsuniqueid(Slapi_Mods *smods);
+
+int
+ldbm_back_modrdn( Slapi_PBlock *pb )
+{
+ backend *be;
+ ldbm_instance *inst;
+ struct ldbminfo *li;
+ struct backentry *e= NULL;
+ struct backentry *ec= NULL;
+ int ec_in_cache= 0;
+ back_txn txn;
+ back_txnid parent_txn;
+ int retval = -1;
+ char *msg;
+ Slapi_Entry *postentry = NULL;
+ char *errbuf = NULL;
+ int disk_full = 0;
+ int retry_count = 0;
+ int ldap_result_code= LDAP_SUCCESS;
+ char *ldap_result_message= NULL;
+ char *ldap_result_matcheddn= NULL;
+ struct backentry *parententry= NULL;
+ struct backentry *newparententry= NULL;
+ struct backentry *existingentry= NULL;
+ modify_context parent_modify_context = {0};
+ modify_context newparent_modify_context = {0};
+ IDList *children= NULL;
+ struct backentry **child_entries= NULL;
+ struct backentry **child_entry_copies= NULL;
+ Slapi_DN dn_olddn;
+ Slapi_DN dn_newdn;
+ Slapi_DN dn_newrdn;
+ Slapi_DN dn_newsuperiordn;
+ Slapi_DN dn_parentdn;
+ int rc;
+ int isroot;
+ LDAPMod **mods;
+ Slapi_Mods smods_operation_wsi = {0};
+ Slapi_Mods smods_generated = {0};
+ Slapi_Mods smods_generated_wsi = {0};
+ Slapi_Operation *operation;
+ int dblock_acquired= 0;
+ int is_replicated_operation= 0;
+ int is_fixup_operation = 0;
+ entry_address new_addr;
+ entry_address *old_addr;
+ entry_address oldparent_addr;
+ entry_address *newsuperior_addr;
+ char *dn;
+ char ebuf[BUFSIZ];
+ CSN *opcsn = NULL;
+
+ slapi_sdn_init(&dn_newdn);
+ slapi_sdn_init(&dn_parentdn);
+
+ slapi_pblock_get( pb, SLAPI_MODRDN_TARGET, &dn );
+ slapi_pblock_get( pb, SLAPI_BACKEND, &be);
+ slapi_pblock_get( pb, SLAPI_PLUGIN_PRIVATE, &li );
+ slapi_pblock_get( pb, SLAPI_PARENT_TXN, (void**)&parent_txn );
+ slapi_pblock_get( pb, SLAPI_REQUESTOR_ISROOT, &isroot );
+ slapi_pblock_get( pb, SLAPI_OPERATION, &operation );
+ slapi_pblock_get( pb, SLAPI_IS_REPLICATED_OPERATION, &is_replicated_operation );
+ is_fixup_operation = operation_is_flag_set(operation,OP_FLAG_REPL_FIXUP);
+
+ if (pb->pb_conn)
+ {
+ slapi_log_error (SLAPI_LOG_TRACE, "ldbm_back_modrdn", "enter conn=%d op=%d\n", pb->pb_conn->c_connid, operation->o_opid);
+ }
+
+ inst = (ldbm_instance *) be->be_instance_info;
+ {
+ char *newrdn, *newsuperiordn;
+ slapi_pblock_get( pb, SLAPI_MODRDN_NEWRDN, &newrdn );
+ slapi_pblock_get( pb, SLAPI_MODRDN_NEWSUPERIOR, &newsuperiordn );
+ slapi_sdn_init_dn_byref(&dn_olddn,dn);
+ slapi_sdn_init_dn_byref(&dn_newrdn,newrdn);
+ slapi_sdn_init_dn_byref(&dn_newsuperiordn,newsuperiordn);
+ slapi_sdn_get_parent(&dn_olddn,&dn_parentdn);
+ }
+
+ /* if old and new superior are equals, newsuperior should not be set
+ * Here we have to reset newsuperiordn in order to save processing and
+ * avoid later deadlock when trying to fetch twice the same entry
+ */
+ if (slapi_sdn_compare(&dn_newsuperiordn, &dn_parentdn) == 0)
+ {
+ slapi_sdn_done(&dn_newsuperiordn);
+ slapi_sdn_init_dn_byref(&dn_newsuperiordn,NULL);
+ }
+
+ /* Replicated Operations are allowed to change the superior */
+ if ( !is_replicated_operation && !slapi_sdn_isempty(&dn_newsuperiordn))
+ {
+ slapi_send_ldap_result( pb, LDAP_UNWILLING_TO_PERFORM, NULL,
+ "server does not support moving of entries", 0, NULL );
+ return( -1 );
+ }
+
+ dblayer_txn_init(li,&txn);
+
+ /* The dblock serializes writes to the database,
+ * which reduces deadlocking in the db code,
+ * which means that we run faster.
+ *
+ * But, this lock is re-enterant for the fixup
+ * operations that the URP code in the Replication
+ * plugin generates.
+ *
+ * Also some URP post-op operations are called after
+ * the backend has committed the change and released
+ * the dblock. Acquire the dblock again for them
+ * if OP_FLAG_ACTION_INVOKE_FOR_REPLOP is set.
+ */
+ if(SERIALLOCK(li) && (!operation_is_flag_set(operation,OP_FLAG_REPL_FIXUP) || operation_is_flag_set(operation,OP_FLAG_ACTION_INVOKE_FOR_REPLOP)))
+ {
+ dblayer_lock_backend(be);
+ dblock_acquired= 1;
+ }
+
+ /* Work out what the new name of the entry will be */
+ {
+ const char *newdn= moddn_get_newdn(pb,&dn_olddn,&dn_newrdn,&dn_newsuperiordn);
+ slapi_sdn_set_dn_passin(&dn_newdn,newdn);
+ }
+
+ rc= 0;
+ rc= slapi_setbit_int(rc,SLAPI_RTN_BIT_FETCH_EXISTING_DN_ENTRY);
+ rc= slapi_setbit_int(rc,SLAPI_RTN_BIT_FETCH_PARENT_ENTRY);
+ rc= slapi_setbit_int(rc,SLAPI_RTN_BIT_FETCH_NEWPARENT_ENTRY);
+ rc= slapi_setbit_int(rc,SLAPI_RTN_BIT_FETCH_TARGET_ENTRY);
+ while(rc!=0)
+ {
+ /* JCM - copying entries can be expensive... should optimize */
+ /*
+ * Some present state information is passed through the PBlock to the
+ * backend pre-op plugin. To ensure a consistent snapshot of this state
+ * we wrap the reading of the entry with the dblock.
+ */
+ if(slapi_isbitset_int(rc,SLAPI_RTN_BIT_FETCH_EXISTING_DN_ENTRY))
+ {
+ const char *newdn = NULL;
+ char * newrdn = NULL;
+
+ /* see if an entry with the new name already exists */
+ done_with_pblock_entry(pb,SLAPI_MODRDN_EXISTING_ENTRY); /* Could be through this multiple times */
+ slapi_sdn_done(&dn_newrdn);
+ slapi_pblock_get(pb, SLAPI_MODRDN_NEWRDN, &newrdn);
+ slapi_sdn_init_dn_byref(&dn_newrdn,newrdn);
+ newdn= moddn_get_newdn(pb,&dn_olddn,&dn_newrdn,&dn_newsuperiordn);
+ slapi_sdn_set_dn_passin(&dn_newdn,newdn);
+ new_addr.dn = (char*)slapi_sdn_get_ndn (&dn_newdn);
+ new_addr.uniqueid = NULL;
+ ldap_result_code= get_copy_of_entry(pb, &new_addr, &txn, SLAPI_MODRDN_EXISTING_ENTRY, 0);
+ }
+ if(slapi_isbitset_int(rc,SLAPI_RTN_BIT_FETCH_PARENT_ENTRY))
+ {
+ /* find and lock the old parent entry */
+ done_with_pblock_entry(pb,SLAPI_MODRDN_PARENT_ENTRY); /* Could be through this multiple times */
+ oldparent_addr.dn = (char*)slapi_sdn_get_ndn (&dn_parentdn);
+ oldparent_addr.uniqueid = NULL;
+ ldap_result_code= get_copy_of_entry(pb, &oldparent_addr, &txn, SLAPI_MODRDN_PARENT_ENTRY, !is_replicated_operation);
+ }
+ if(slapi_sdn_get_ndn(&dn_newsuperiordn)!=NULL && slapi_isbitset_int(rc,SLAPI_RTN_BIT_FETCH_NEWPARENT_ENTRY))
+ {
+ /* JCM - Could check that this really is a new superior, and not the same old one. Compare parentdn & newsuperior */
+ /* find and lock the new parent entry */
+ done_with_pblock_entry(pb,SLAPI_MODRDN_NEWPARENT_ENTRY); /* Could be through this multiple times */
+ /* JCMREPL - If this is a replicated operation then should fetch new superior with uniqueid */
+ slapi_pblock_get (pb, SLAPI_MODRDN_NEWSUPERIOR_ADDRESS, &newsuperior_addr);
+ ldap_result_code= get_copy_of_entry(pb, newsuperior_addr, &txn, SLAPI_MODRDN_NEWPARENT_ENTRY, !is_replicated_operation);
+ }
+ if(slapi_isbitset_int(rc,SLAPI_RTN_BIT_FETCH_TARGET_ENTRY))
+ {
+ /* find and lock the entry we are about to modify */
+ done_with_pblock_entry(pb,SLAPI_MODRDN_TARGET_ENTRY); /* Could be through this multiple times */
+ slapi_pblock_get (pb, SLAPI_TARGET_ADDRESS, &old_addr);
+ ldap_result_code= get_copy_of_entry(pb, old_addr, &txn, SLAPI_MODRDN_TARGET_ENTRY, !is_replicated_operation);
+ if(ldap_result_code==LDAP_OPERATIONS_ERROR)
+ {
+ /* JCM - Usually the call to find_entry2modify would generate the result code. */
+ /* JCM !!! */
+ goto error_return;
+ }
+ }
+ /* Call the Backend Pre ModRDN plugins */
+ slapi_pblock_set(pb, SLAPI_RESULT_CODE, &ldap_result_code);
+ rc= plugin_call_plugins(pb, SLAPI_PLUGIN_BE_PRE_MODRDN_FN);
+ if(rc==-1)
+ {
+ /*
+ * Plugin indicated some kind of failure,
+ * or that this Operation became a No-Op.
+ */
+ slapi_pblock_get(pb, SLAPI_RESULT_CODE, &ldap_result_code);
+ goto error_return;
+ }
+ /*
+ * (rc!=-1) means that the plugin changed things, so we go around
+ * the loop once again to get the new present state.
+ */
+ /* JCMREPL - Warning: A Plugin could cause an infinite loop by always returning a result code that requires some action. */
+ }
+
+ /* find and lock the entry we are about to modify */
+ /* JCMREPL - Argh, what happens about the stinking referrals? */
+ slapi_pblock_get (pb, SLAPI_TARGET_ADDRESS, &old_addr);
+ e = find_entry2modify( pb, be, old_addr, NULL );
+ if ( e == NULL )
+ {
+ ldap_result_code= -1;
+ goto error_return; /* error result sent by find_entry2modify() */
+ }
+
+ /* Check that an entry with the same DN doesn't already exist. */
+ {
+ Slapi_Entry *entry;
+ slapi_pblock_get( pb, SLAPI_MODRDN_EXISTING_ENTRY, &entry);
+ if(entry!=NULL)
+ {
+ ldap_result_code= LDAP_ALREADY_EXISTS;
+ goto error_return;
+ }
+ }
+
+ /* Fetch and lock the parent of the entry that is moving */
+ oldparent_addr.dn = (char*)slapi_sdn_get_ndn (&dn_parentdn);
+ oldparent_addr.uniqueid = NULL;
+ parententry = find_entry2modify_only( pb, be, &oldparent_addr, NULL );
+ modify_init(&parent_modify_context,parententry);
+
+ /* Fetch and lock the new parent of the entry that is moving */
+ if(slapi_sdn_get_ndn(&dn_newsuperiordn)!=NULL)
+ {
+ slapi_pblock_get (pb, SLAPI_MODRDN_NEWSUPERIOR_ADDRESS, &newsuperior_addr);
+ newparententry = find_entry2modify_only( pb, be, newsuperior_addr, NULL);
+ modify_init(&newparent_modify_context,newparententry);
+ }
+
+ opcsn = operation_get_csn (operation);
+ if (!is_fixup_operation)
+ {
+ if ( opcsn == NULL && operation->o_csngen_handler)
+ {
+ /*
+ * Current op is a user request. Opcsn will be assigned
+ * if the dn is in an updatable replica.
+ */
+ opcsn = entry_assign_operation_csn ( pb, e->ep_entry, parententry ? parententry->ep_entry : NULL );
+ }
+ if ( opcsn != NULL )
+ {
+ entry_set_maxcsn (e->ep_entry, opcsn);
+ }
+ }
+
+ /*
+ * Now that we have the old entry, we reset the old DN and recompute
+ * the new DN. Why? Because earlier when we computed the new DN, we did
+ * not have the old entry, so we used the DN that was presented as the
+ * target DN in the ModRDN operation itself, and we would prefer to
+ * preserve the case and spacing that are in the actual entry's DN
+ * instead. Otherwise, a ModRDN operation will potentially change an
+ * entry's entire DN (at least with respect to case and spacing).
+ */
+ slapi_sdn_copy( slapi_entry_get_sdn_const( e->ep_entry ), &dn_olddn );
+ if (newparententry != NULL) {
+ /* don't forget we also want to preserve case of new superior */
+ slapi_sdn_copy(slapi_entry_get_sdn_const(newparententry->ep_entry), &dn_newsuperiordn);
+ }
+ slapi_sdn_set_dn_passin(&dn_newdn,
+ moddn_get_newdn(pb, &dn_olddn, &dn_newrdn, &dn_newsuperiordn));
+
+ /* Check that we're allowed to add an entry below the new superior */
+ if ( newparententry == NULL )
+ {
+ /* There may not be a new parent because we don't intend there to be one. */
+ if(slapi_sdn_get_ndn(&dn_newsuperiordn)!=NULL)
+ {
+ /* If the new entry is to be a suffix, and we're root, then it's OK that the new parent doesn't exist */
+ if(!(slapi_dn_isbesuffix(pb,slapi_sdn_get_ndn(&dn_newdn)) && isroot))
+ {
+ /* Here means that we didn't find the parent */
+ int err = 0;
+ Slapi_DN ancestordn = {0};
+ struct backentry *ancestorentry;
+ ancestorentry= dn2ancestor(be,&dn_newdn,&ancestordn,&txn,&err);
+ cache_return( &inst->inst_cache, &ancestorentry );
+ ldap_result_matcheddn= slapi_ch_strdup((char *) slapi_sdn_get_dn(&ancestordn));
+ ldap_result_code= LDAP_NO_SUCH_OBJECT;
+ LDAPDebug( LDAP_DEBUG_TRACE, "New superior does not exist matched %s, newsuperior = %s\n",
+ ldap_result_matcheddn == NULL ? "NULL" : ldap_result_matcheddn, slapi_sdn_get_ndn(&dn_newsuperiordn), 0 );
+ slapi_sdn_done(&ancestordn);
+ goto error_return;
+ }
+ }
+ }
+ else
+ {
+ ldap_result_code= plugin_call_acl_plugin (pb, newparententry->ep_entry, NULL, NULL, SLAPI_ACL_ADD, ACLPLUGIN_ACCESS_DEFAULT, &errbuf );
+ if ( ldap_result_code != LDAP_SUCCESS )
+ {
+ ldap_result_message= errbuf;
+ LDAPDebug( LDAP_DEBUG_TRACE, "No access to new superior.\n", 0, 0, 0 );
+ goto error_return;
+ }
+ }
+
+ /* Check that the target entry has a parent */
+ if ( parententry == NULL )
+ {
+ /* If the entry a suffix, and we're root, then it's OK that the parent doesn't exist */
+ if(!(slapi_dn_isbesuffix(pb,slapi_sdn_get_ndn(&dn_olddn)) && isroot))
+ {
+ /* Here means that we didn't find the parent */
+ ldap_result_matcheddn = slapi_ch_strdup((char *) slapi_entry_get_dn(parententry->ep_entry));
+ ldap_result_code= LDAP_NO_SUCH_OBJECT;
+ LDAPDebug( LDAP_DEBUG_TRACE, "Parent does not exist matched %s, parentdn = %s\n",
+ ldap_result_matcheddn == NULL ? "NULL" : ldap_result_matcheddn, slapi_sdn_get_ndn(&dn_parentdn), 0 );
+ goto error_return;
+ }
+ }
+
+ /* Replicated Operations are allowed to rename entries with children */
+ if ( !is_replicated_operation && slapi_entry_has_children( e->ep_entry ))
+ {
+ ldap_result_code = LDAP_NOT_ALLOWED_ON_NONLEAF;
+ goto error_return;
+ }
+
+
+ /*
+ * JCM - All the child entries must be locked in the cache, so the size of
+ * subtree that can be renamed is limited by the cache size.
+ */
+
+ /* Save away a copy of the entry, before modifications */
+ slapi_pblock_set( pb, SLAPI_ENTRY_PRE_OP, slapi_entry_dup( e->ep_entry ));
+
+ /* create a copy of the entry and apply the changes to it */
+ if ( (ec = backentry_dup( e )) == NULL )
+ {
+ ldap_result_code= LDAP_OPERATIONS_ERROR;
+ goto error_return;
+ }
+
+ /* JCMACL - Should be performed before the child check. */
+ /* JCMACL - Why is the check performed against the copy, rather than the existing entry? */
+ /*ldap_result_code = plugin_call_acl_plugin (pb, ec->ep_entry, NULL , NULL , SLAPI_ACL_WRITE, ACLPLUGIN_ACCESS_DEFAULT, &errbuf );*/
+ ldap_result_code = plugin_call_acl_plugin (pb, ec->ep_entry,
+ NULL /*attr*/, NULL /*value*/, SLAPI_ACL_WRITE,
+ ACLPLUGIN_ACCESS_MODRDN, &errbuf );
+ if ( ldap_result_code != LDAP_SUCCESS )
+ {
+ goto error_return;
+ }
+
+ slapi_entry_set_sdn( ec->ep_entry, &dn_newdn );
+
+ /* create it in the cache - prevents others from creating it */
+ if ( cache_add_tentative( &inst->inst_cache, ec, NULL ) != 0 ) {
+ /* somebody must've created it between dn2entry() and here */
+ /* JCMREPL - Hmm... we can't permit this to happen...? */
+ ldap_result_code= LDAP_ALREADY_EXISTS;
+ goto error_return;
+ }
+ ec_in_cache= 1;
+
+ /* Build the list of modifications required to the existing entry */
+ {
+ slapi_mods_init(&smods_generated,4);
+ slapi_mods_init(&smods_generated_wsi,4);
+ ldap_result_code = moddn_newrdn_mods(pb, slapi_sdn_get_ndn(&dn_olddn), ec, &smods_generated, &smods_generated_wsi,
+ is_replicated_operation);
+ if (ldap_result_code != LDAP_SUCCESS) {
+ if (ldap_result_code == LDAP_UNWILLING_TO_PERFORM)
+ ldap_result_message = "Modification of old rdn attribute type not allowed.";
+ goto error_return;
+ }
+ /*
+ * Remove the old entrydn index entry, and add the new one.
+ */
+ slapi_mods_add( &smods_generated, LDAP_MOD_DELETE, "entrydn", strlen(backentry_get_ndn(e)), backentry_get_ndn(e));
+ slapi_mods_add( &smods_generated, LDAP_MOD_REPLACE, "entrydn", strlen(backentry_get_ndn(ec)), backentry_get_ndn(ec));
+
+ /*
+ * Update parentid if we have a new superior.
+ */
+ if(slapi_sdn_get_dn(&dn_newsuperiordn)!=NULL) {
+ char buf[40]; /* Enough for an ID */
+
+ if (parententry != NULL) {
+ sprintf( buf, "%lu", (u_long)parententry->ep_id );
+ slapi_mods_add_string(&smods_generated, LDAP_MOD_DELETE, "parentid", buf);
+ }
+ if (newparententry != NULL) {
+ sprintf( buf, "%lu", (u_long)newparententry->ep_id );
+ slapi_mods_add_string(&smods_generated, LDAP_MOD_REPLACE, "parentid", buf);
+ }
+ }
+ }
+
+ slapi_pblock_get( pb, SLAPI_MODIFY_MODS, &mods );
+ slapi_mods_init_byref(&smods_operation_wsi,mods);
+
+ /*
+ * We are about to pass the last abandon test, so from now on we are
+ * committed to finish this operation. Set status to "will complete"
+ * before we make our last abandon check to avoid race conditions in
+ * the code that processes abandon operations.
+ */
+ if (operation) {
+ operation->o_status = SLAPI_OP_STATUS_WILL_COMPLETE;
+ }
+ if ( slapi_op_abandoned( pb ) ) {
+ goto error_return;
+ }
+
+ /*
+ * First, we apply the generated mods that do not involve any state information.
+ */
+ if ( entry_apply_mods( ec->ep_entry, slapi_mods_get_ldapmods_byref(&smods_generated) ) != 0 )
+ {
+ ldap_result_code= LDAP_OPERATIONS_ERROR;
+ LDAPDebug( LDAP_DEBUG_TRACE, "ldbm_modrdn: entry_apply_mods failed for entry %s\n",
+ escape_string(slapi_entry_get_dn_const(ec->ep_entry), ebuf), 0, 0);
+ goto error_return;
+ }
+
+ /*
+ * Now we apply the generated mods that do involve state information.
+ */
+ if (slapi_mods_get_num_mods(&smods_generated_wsi)>0)
+ {
+ if (entry_apply_mods_wsi(ec->ep_entry, &smods_generated_wsi, operation_get_csn(operation), is_replicated_operation)!=0)
+ {
+ ldap_result_code= LDAP_OPERATIONS_ERROR;
+ LDAPDebug( LDAP_DEBUG_TRACE, "ldbm_modrdn: entry_apply_mods_wsi failed for entry %s\n",
+ escape_string(slapi_entry_get_dn_const(ec->ep_entry), ebuf), 0, 0);
+ goto error_return;
+ }
+ }
+
+ /*
+ * Now we apply the operation mods that do involve state information.
+ * (Operational attributes).
+ * The following block looks redundent to the one above. But it may
+ * be necessary - check the comment for version 1.3.16.22.2.76 of
+ * this file and compare that version with its previous one.
+ */
+ if (slapi_mods_get_num_mods(&smods_operation_wsi)>0)
+ {
+ if (entry_apply_mods_wsi(ec->ep_entry, &smods_operation_wsi, operation_get_csn(operation), is_replicated_operation)!=0)
+ {
+ ldap_result_code= LDAP_OPERATIONS_ERROR;
+ LDAPDebug( LDAP_DEBUG_TRACE, "ldbm_modrdn: entry_apply_mods_wsi (operational attributes) failed for entry %s\n",
+ escape_string(slapi_entry_get_dn_const(ec->ep_entry), ebuf), 0, 0);
+ goto error_return;
+ }
+ }
+ /* check that the entry still obeys the schema */
+ if ( slapi_entry_schema_check( pb, ec->ep_entry ) != 0 ) {
+ ldap_result_code = LDAP_OBJECT_CLASS_VIOLATION;
+ slapi_pblock_get(pb, SLAPI_PB_RESULT_TEXT, &ldap_result_message);
+ goto error_return;
+ }
+
+ /*
+ * Update the DN CSN of the entry.
+ */
+ entry_add_dncsn(ec->ep_entry,operation_get_csn(operation));
+ entry_add_rdn_csn(ec->ep_entry,operation_get_csn(operation));
+
+ /*
+ * If the entry has a new superior then the subordinate count
+ * of the parents must be updated.
+ */
+ if(slapi_sdn_get_dn(&dn_newsuperiordn)!=NULL)
+ {
+ /*
+ * Update the subordinate count of the parents to reflect the moved child.
+ */
+ if ( parententry!=NULL )
+ {
+ retval = parent_update_on_childchange(&parent_modify_context,2,NULL); /* 2==delete */
+ /* The parent modify context now contains info needed later */
+ if (0 != retval)
+ {
+ goto error_return;
+ }
+ }
+ if ( newparententry!=NULL )
+ {
+ retval = parent_update_on_childchange(&newparent_modify_context,1,NULL); /* 1==add */
+ /* The newparent modify context now contains info needed later */
+ if (0 != retval)
+ {
+ goto error_return;
+ }
+ }
+ }
+
+ /*
+ * If the entry has children then we're going to have to rename them all.
+ */
+ if (slapi_entry_has_children( e->ep_entry ))
+ {
+ /* JCM - This is where the subtree lock will appear */
+ children= moddn_get_children(&txn, pb, be, e, &dn_olddn, &child_entries, &child_entry_copies);
+ /* JCM - Shouldn't we perform an access control check on all the children. */
+ /* JCMREPL - But, the replication client has total rights over its subtree, so no access check needed. */
+ /* JCM - A subtree move could break ACIs, static groups, and dynamic groups. */
+ }
+
+ /*
+ * So, we believe that no code up till here actually added anything
+ * to persistent store. From now on, we're transacted
+ */
+ for (retry_count = 0; retry_count < RETRY_TIMES; retry_count++)
+ {
+ if (retry_count > 0)
+ {
+ dblayer_txn_abort(li,&txn);
+ /* We're re-trying */
+ LDAPDebug( LDAP_DEBUG_TRACE, "Modrdn Retrying Transaction\n", 0, 0, 0 );
+ }
+ retval = dblayer_txn_begin(li,parent_txn,&txn);
+ if (0 != retval) {
+ ldap_result_code= LDAP_OPERATIONS_ERROR;
+ if (LDBM_OS_ERR_IS_DISKFULL(retval)) disk_full = 1;
+ goto error_return;
+ }
+
+ /*
+ * Update the indexes for the entry.
+ */
+ retval = modrdn_rename_entry_update_indexes(&txn, pb, li, e, ec, &smods_generated, &smods_generated_wsi, &smods_operation_wsi);
+ if (DB_LOCK_DEADLOCK == retval)
+ {
+ /* Retry txn */
+ continue;
+ }
+ if (retval != 0 )
+ {
+ LDAPDebug( LDAP_DEBUG_TRACE, "modrdn_rename_entry_update_indexes failed, err=%d %s\n",
+ retval, (msg = dblayer_strerror( retval )) ? msg : "", 0 );
+ if (LDBM_OS_ERR_IS_DISKFULL(retval)) disk_full = 1;
+ ldap_result_code= LDAP_OPERATIONS_ERROR;
+ }
+
+ /*
+ * add new name to index
+ */
+ {
+ char **rdns;
+ int i;
+ if ( (rdns = ldap_explode_rdn( slapi_sdn_get_dn(&dn_newrdn), 0 )) != NULL )
+ {
+ for ( i = 0; rdns[i] != NULL; i++ )
+ {
+ char *type;
+ Slapi_Value *svp[2];
+ Slapi_Value sv;
+ memset(&sv,0,sizeof(Slapi_Value));
+ if ( slapi_rdn2typeval( rdns[i], &type, &sv.bv ) != 0 )
+ {
+ char ebuf[ BUFSIZ ];
+ LDAPDebug( LDAP_DEBUG_ANY, "modrdn: rdn2typeval (%s) failed\n",
+ escape_string( rdns[i], ebuf ), 0, 0 );
+ goto error_return;
+ }
+ svp[0] = &sv;
+ svp[1] = NULL;
+ retval = index_addordel_values_sv( be, type, svp, NULL, ec->ep_id, BE_INDEX_ADD, &txn );
+ if (DB_LOCK_DEADLOCK == retval)
+ {
+ /* Retry txn */
+ continue;
+ }
+ if (retval != 0 )
+ {
+ LDAPDebug( LDAP_DEBUG_ANY, "modrdn: could not add new value to index, err=%d %s\n",
+ retval, (msg = dblayer_strerror( retval )) ? msg : "", 0 );
+ if (LDBM_OS_ERR_IS_DISKFULL(retval)) disk_full = 1;
+ }
+ }
+ ldap_value_free( rdns );
+ if (DB_LOCK_DEADLOCK == retval)
+ {
+ /* Retry txn */
+ goto error_return;
+ }
+ }
+ }
+ if (slapi_sdn_get_dn(&dn_newsuperiordn)!=NULL)
+ {
+ /* Push out the db modifications from the parent entry */
+ retval = modify_update_all(be, pb, &parent_modify_context, &txn);
+ if (DB_LOCK_DEADLOCK == retval)
+ {
+ /* Retry txn */
+ continue;
+ }
+ if (0 != retval)
+ {
+ LDAPDebug( LDAP_DEBUG_TRACE, "moddn: could not update parent, err=%d %s\n", retval, (msg = dblayer_strerror( retval )) ? msg : "", 0 );
+ if (LDBM_OS_ERR_IS_DISKFULL(retval)) disk_full = 1;
+ }
+ /* Push out the db modifications from the new parent entry */
+ if(retval==0)
+ {
+ retval = modify_update_all(be, pb, &newparent_modify_context, &txn);
+ if (DB_LOCK_DEADLOCK == retval)
+ {
+ /* Retry txn */
+ continue;
+ }
+ if (0 != retval)
+ {
+ LDAPDebug( LDAP_DEBUG_TRACE, "moddn: could not update parent, err=%d %s\n", retval, (msg = dblayer_strerror( retval )) ? msg : "", 0 );
+ if (LDBM_OS_ERR_IS_DISKFULL(retval)) disk_full = 1;
+ }
+ }
+ }
+
+ /*
+ * Update ancestorid index.
+ */
+ if (slapi_sdn_get_dn(&dn_newsuperiordn)!=NULL) {
+ retval = ldbm_ancestorid_move_subtree(be, &dn_olddn, &dn_newdn, e->ep_id, children, &txn);
+ if (retval != 0) {
+ if (retval == DB_LOCK_DEADLOCK) continue;
+ if (retval == DB_RUNRECOVERY || LDBM_OS_ERR_IS_DISKFULL(retval))
+ disk_full = 1;
+ goto error_return;
+ }
+ }
+
+ /*
+ * If the entry has children, then rename them all.
+ */
+ if (children!=NULL)
+ {
+ retval= moddn_rename_children( &txn, pb, be, children, &dn_olddn, &dn_newdn, child_entries, child_entry_copies);
+ }
+ if (DB_LOCK_DEADLOCK == retval)
+ {
+ /* Retry txn */
+ continue;
+ }
+ if (retval != 0)
+ {
+ if (retval == DB_RUNRECOVERY || LDBM_OS_ERR_IS_DISKFULL(retval))
+ disk_full = 1;
+ goto error_return;
+ }
+
+ break; /* retval==0, Done, Terminate the loop */
+ }
+ if (retry_count == RETRY_TIMES)
+ {
+ /* Failed */
+ LDAPDebug( LDAP_DEBUG_ANY, "Retry count exceeded in modrdn\n", 0, 0, 0 );
+ ldap_result_code= LDAP_OPERATIONS_ERROR;
+ goto error_return;
+ }
+
+ postentry = slapi_entry_dup( ec->ep_entry );
+
+ if(parententry!=NULL)
+ {
+ modify_switch_entries( &parent_modify_context,be);
+ }
+ if(newparententry!=NULL)
+ {
+ modify_switch_entries( &newparent_modify_context,be);
+ }
+
+ retval = dblayer_txn_commit(li,&txn);
+ if (0 != retval)
+ {
+ if (LDBM_OS_ERR_IS_DISKFULL(retval)) disk_full = 1;
+ ldap_result_code= LDAP_OPERATIONS_ERROR;
+ goto error_return;
+ }
+
+ if(children!=NULL)
+ {
+ int i=0;
+ for (; child_entries[i]!=NULL; i++) {
+ cache_unlock_entry( &inst->inst_cache, child_entries[i]) ;
+ cache_return( &inst->inst_cache, &(child_entries[i]) );
+ cache_return( &inst->inst_cache, &(child_entry_copies[i]) );
+ }
+ }
+
+ retval= 0;
+#if 0 /* this new entry in the cache can be used for future; don't remove it */
+ /* remove from cache so that memory can be freed by cache_return */
+ if (ec_in_cache) {
+ cache_remove(&inst->inst_cache, ec);
+ }
+#endif
+ goto common_return;
+
+error_return:
+ /* result already sent above - just free stuff */
+ if ( NULL != postentry )
+ {
+ slapi_entry_free( postentry );
+ postentry= NULL;
+ }
+ if( ec!=NULL ) {
+ if (ec_in_cache) {
+ cache_remove(&inst->inst_cache, ec);
+ } else {
+ backentry_free( &ec );
+ }
+ }
+ if(children!=NULL)
+ {
+ int i=0;
+ for(;child_entries[i]!=NULL;i++) {
+ cache_unlock_entry(&inst->inst_cache, child_entries[i]);
+ cache_return(&inst->inst_cache, &(child_entries[i]));
+ if (child_entry_copies[i] != NULL) {
+ cache_remove(&inst->inst_cache, child_entry_copies[i]);
+ cache_return( &inst->inst_cache, &(child_entry_copies[i]) );
+ }
+ }
+ }
+
+
+ if (retval == DB_RUNRECOVERY) {
+ dblayer_remember_disk_filled(li);
+ ldbm_nasty("ModifyDN",82,retval);
+ disk_full = 1;
+ }
+
+ if (disk_full)
+ {
+ retval = return_on_disk_full(li);
+ }
+ else
+ {
+ /* It is specifically OK to make this call even when no transaction was in progress */
+ dblayer_txn_abort(li,&txn); /* abort crashes in case disk full */
+ retval= SLAPI_FAIL_GENERAL;
+ }
+
+common_return:
+
+ /* Free up the resource we don't need any more */
+ if(ec_in_cache) {
+ cache_return( &inst->inst_cache, &ec );
+ }
+
+ /*
+ * The bepostop is called even if the operation fails.
+ */
+ plugin_call_plugins (pb, SLAPI_PLUGIN_BE_POST_MODRDN_FN);
+
+ if (ldap_result_code!=-1)
+ {
+ slapi_send_ldap_result( pb, ldap_result_code, ldap_result_matcheddn,
+ ldap_result_message, 0,NULL );
+ }
+ slapi_mods_done(&smods_operation_wsi);
+ slapi_mods_done(&smods_generated);
+ slapi_mods_done(&smods_generated_wsi);
+ moddn_unlock_and_return_entries(be,&e,&existingentry);
+ slapi_ch_free((void**)&child_entries);
+ slapi_ch_free((void**)&child_entry_copies);
+ slapi_ch_free((void**)&ldap_result_matcheddn);
+ idl_free(children);
+ slapi_sdn_done(&dn_olddn);
+ slapi_sdn_done(&dn_newdn);
+ slapi_sdn_done(&dn_newrdn);
+ slapi_sdn_done(&dn_newsuperiordn);
+ slapi_sdn_done(&dn_parentdn);
+ modify_term(&parent_modify_context,be);
+ modify_term(&newparent_modify_context,be);
+ done_with_pblock_entry(pb,SLAPI_MODRDN_EXISTING_ENTRY);
+ done_with_pblock_entry(pb,SLAPI_MODRDN_PARENT_ENTRY);
+ done_with_pblock_entry(pb,SLAPI_MODRDN_NEWPARENT_ENTRY);
+ done_with_pblock_entry(pb,SLAPI_MODRDN_TARGET_ENTRY);
+ if(dblock_acquired)
+ {
+ dblayer_unlock_backend(be);
+ }
+ slapi_ch_free((void**)&errbuf);
+ if (retval == 0 && opcsn != NULL && !is_fixup_operation)
+ {
+ slapi_pblock_set(pb, SLAPI_URP_NAMING_COLLISION_DN, slapi_ch_strdup (dn));
+ }
+ slapi_pblock_set( pb, SLAPI_ENTRY_POST_OP, postentry );
+ if (pb->pb_conn)
+ {
+ slapi_log_error (SLAPI_LOG_TRACE, "ldbm_back_modrdn", "leave conn=%d op=%d\n", pb->pb_conn->c_connid, operation->o_opid);
+ }
+ return retval;
+}
+
+/*
+ * Work out what the new DN of the entry will be.
+ */
+static const char *
+moddn_get_newdn(Slapi_PBlock *pb, Slapi_DN *dn_olddn, Slapi_DN *dn_newrdn, Slapi_DN *dn_newsuperiordn)
+{
+ char *newdn;
+ const char *newrdn= slapi_sdn_get_dn(dn_newrdn);
+ const char *newsuperiordn= slapi_sdn_get_dn(dn_newsuperiordn);
+
+ if( newsuperiordn!=NULL)
+ {
+ /* construct the new dn */
+ if(slapi_dn_isroot(newsuperiordn))
+ {
+ newdn= slapi_ch_strdup(newrdn);
+ }
+ else
+ {
+ newdn= slapi_dn_plus_rdn(newsuperiordn, newrdn); /* JCM - Use Slapi_RDN */
+ }
+ }
+ else
+ {
+ /* construct the new dn */
+ char *pdn;
+ const char *dn= slapi_sdn_get_dn(dn_olddn);
+ pdn = slapi_dn_beparent( pb, dn );
+ if ( pdn != NULL )
+ {
+ newdn= slapi_dn_plus_rdn(pdn, newrdn); /* JCM - Use Slapi_RDN */
+ }
+ else
+ {
+ newdn= slapi_ch_strdup(newrdn);
+ }
+ slapi_ch_free( (void**)&pdn );
+ }
+ return newdn;
+}
+
+/*
+ * Return the entries to the cache.
+ */
+static void
+moddn_unlock_and_return_entries(
+ backend *be,
+ struct backentry **targetentry,
+ struct backentry **existingentry)
+ {
+ ldbm_instance *inst = (ldbm_instance *) be->be_instance_info;
+
+ /* Something bad happened so we should give back all the entries */
+ if ( *targetentry!=NULL ) {
+ cache_unlock_entry(&inst->inst_cache, *targetentry);
+ cache_return( &inst->inst_cache, targetentry );
+ *targetentry= NULL;
+ }
+ if ( *existingentry!=NULL ) {
+ cache_return( &inst->inst_cache, existingentry );
+ *existingentry= NULL;
+ }
+ }
+
+
+/*
+ * JCM - There was a problem with multi-valued RDNs where
+ * JCM - there was an intersection of the two sets RDN Components
+ * JCM - and the deleteoldrdn flag was set. A value was deleted
+ * JCM - but not re-added because the value is found to already
+ * JCM - exist.
+ *
+ * This function returns 1 if it is necessary to add an RDN value
+ * to the entry. This is necessary if either:
+ * 1 the attribute or the value is not present in the entry, or
+ * 2 the attribute is present, deleteoldrdn is set, and the RDN value
+ * is in the deleted list.
+ *
+ * For example, suppose you rename cn=a to cn=a+sn=b. The cn=a value
+ * is removed from the entry and then readded.
+ */
+
+static int
+moddn_rdn_add_needed (
+ struct backentry *ec,
+ char *type,
+ struct berval *bvp,
+ int deleteoldrdn,
+ Slapi_Mods *smods_wsi
+)
+{
+ Slapi_Attr *attr;
+ LDAPMod *mod;
+
+ if (slapi_entry_attr_find(ec->ep_entry, type, &attr) != 0 ||
+ slapi_attr_value_find( attr, bvp ) != 0 )
+ {
+ return 1;
+ }
+
+ if (deleteoldrdn == 0) return 0;
+
+ /* in a multi-valued RDN, the RDN value might have been already
+ * put on the smods_wsi list to be deleted, yet might still be
+ * in the target RDN.
+ */
+
+ for (mod = slapi_mods_get_first_mod(smods_wsi);
+ mod != NULL;
+ mod = slapi_mods_get_next_mod(smods_wsi)) {
+ if (((mod->mod_op & ~LDAP_MOD_BVALUES) == LDAP_MOD_DELETE) &&
+ (strcasecmp(mod->mod_type, type) == 0) &&
+ (mod->mod_bvalues != NULL) &&
+ (slapi_attr_value_cmp(attr, *mod->mod_bvalues, bvp) == 0)) {
+ return 1;
+ }
+ }
+
+ return 0;
+}
+
+/*
+ * Build the list of modifications to apply to the Existing Entry
+ * With State Information:
+ * - delete old rdn values from the entry if deleteoldrdn is set
+ * - add new rdn values to the entry
+ * Without State Information
+ * - No changes
+ */
+static int
+moddn_newrdn_mods(Slapi_PBlock *pb, const char *olddn, struct backentry *ec, Slapi_Mods *smods, Slapi_Mods *smods_wsi, int is_repl_op)
+{
+ char ebuf[BUFSIZ];
+ char **rdns = NULL;
+ char **dns = NULL;
+ int deleteoldrdn;
+ char *type = NULL;
+ char *dn = NULL;
+ char *newrdn = NULL;
+ int i;
+ struct berval *bvps[2];
+ struct berval bv;
+
+ bvps[0] = &bv;
+ bvps[1] = NULL;
+
+ /* slapi_pblock_get( pb, SLAPI_MODRDN_TARGET, &dn ); */
+ slapi_pblock_get( pb, SLAPI_MODRDN_NEWRDN, &newrdn );
+ slapi_pblock_get( pb, SLAPI_MODRDN_DELOLDRDN, &deleteoldrdn );
+
+
+ /*
+ * This loop removes the old RDN of the existing entry.
+ */
+ if (deleteoldrdn) {
+ int baddn = 0; /* set to true if could not parse dn */
+ int badrdn = 0; /* set to true if could not parse rdn */
+ dn = slapi_ch_strdup(olddn);
+ dns = ldap_explode_dn( dn, 0 );
+ if ( dns != NULL )
+ {
+ rdns = ldap_explode_rdn( dns[0], 0 );
+ if ( rdns != NULL )
+ {
+ for ( i = 0; rdns[i] != NULL; i++ )
+ {
+ /* delete from entry attributes */
+ if ( deleteoldrdn && slapi_rdn2typeval( rdns[i], &type, &bv ) == 0 )
+ {
+ /* check if user is allowed to modify the specified attribute */
+ /*
+ * It would be better to do this check in the front end
+ * end inside op_shared_rename(), but unfortunately we
+ * don't have access to the target entry there.
+ */
+ if (!op_shared_is_allowed_attr (type, is_repl_op))
+ {
+ ldap_value_free( rdns );
+ ldap_value_free( dns );
+ slapi_ch_free_string(&dn);
+ return LDAP_UNWILLING_TO_PERFORM;
+ }
+ if (strcasecmp (type, SLAPI_ATTR_UNIQUEID) != 0)
+ slapi_mods_add_modbvps( smods_wsi, LDAP_MOD_DELETE, type, bvps );
+ }
+ }
+ ldap_value_free( rdns );
+ }
+ else
+ {
+ badrdn = 1;
+ }
+ ldap_value_free( dns );
+ }
+ else
+ {
+ baddn = 1;
+ }
+ slapi_ch_free_string(&dn);
+
+ if ( baddn || badrdn )
+ {
+ LDAPDebug( LDAP_DEBUG_TRACE, "moddn_newrdn_mods failed: olddn=%s baddn=%d badrdn=%d\n",
+ escape_string(olddn, ebuf), baddn, badrdn);
+ return LDAP_OPERATIONS_ERROR;
+ }
+ }
+ /*
+ * add new RDN values to the entry (non-normalized)
+ */
+ rdns = ldap_explode_rdn( newrdn, 0 );
+ if ( rdns != NULL )
+ {
+ for ( i = 0; rdns[i] != NULL; i++ )
+ {
+ if ( slapi_rdn2typeval( rdns[i], &type, &bv ) != 0) {
+ continue;
+ }
+
+ /* add to entry if it's not already there or if was
+ * already deleted
+ */
+ if (moddn_rdn_add_needed(ec, type, &bv,
+ deleteoldrdn,
+ smods_wsi) == 1) {
+ slapi_mods_add_modbvps( smods_wsi, LDAP_MOD_ADD, type, bvps );
+ }
+ }
+ ldap_value_free( rdns );
+ }
+ else
+ {
+ LDAPDebug( LDAP_DEBUG_TRACE, "moddn_newrdn_mods failed: could not parse new rdn %s\n",
+ escape_string(newrdn, ebuf), 0, 0);
+ return LDAP_OPERATIONS_ERROR;
+ }
+
+ return LDAP_SUCCESS;
+}
+
+static void
+mods_remove_nsuniqueid(Slapi_Mods *smods)
+{
+ int i;
+
+ LDAPMod **mods = slapi_mods_get_ldapmods_byref(smods);
+ for ( i = 0; mods[i] != NULL; i++ ) {
+ if (!strcasecmp(mods[i]->mod_type, SLAPI_ATTR_UNIQUEID)) {
+ mods[i]->mod_op = LDAP_MOD_IGNORE;
+ }
+ }
+}
+
+
+/*
+ * Update the indexes to reflect the DN change made.
+ * e is the entry before, ec the entry after.
+ * mods contains the list of attribute change made.
+ */
+static int
+modrdn_rename_entry_update_indexes(back_txn *ptxn, Slapi_PBlock *pb, struct ldbminfo *li, struct backentry *e, struct backentry *ec, Slapi_Mods *smods1, Slapi_Mods *smods2, Slapi_Mods *smods3)
+{
+ backend *be;
+ ldbm_instance *inst;
+ int retval= 0;
+ char *msg;
+
+ slapi_pblock_get( pb, SLAPI_BACKEND, &be);
+ inst = (ldbm_instance *) be->be_instance_info;
+
+ /*
+ * Update the ID to Entry index.
+ * Note that id2entry_add replaces the entry, so the Entry ID stays the same.
+ */
+ retval = id2entry_add( be, ec, ptxn );
+ if (DB_LOCK_DEADLOCK == retval)
+ {
+ /* Retry txn */
+ goto error_return;
+ }
+ if (retval != 0)
+ {
+ LDAPDebug( LDAP_DEBUG_ANY, "id2entry_add failed, err=%d %s\n", retval, (msg = dblayer_strerror( retval )) ? msg : "", 0 );
+ goto error_return;
+ }
+ if(smods1!=NULL && slapi_mods_get_num_mods(smods1)>0)
+ {
+ /*
+ * update the indexes: lastmod, rdn, etc.
+ */
+ retval = index_add_mods( be, (const LDAPMod **)slapi_mods_get_ldapmods_byref(smods1), e, ec, ptxn );
+ if (DB_LOCK_DEADLOCK == retval)
+ {
+ /* Retry txn */
+ goto error_return;
+ }
+ if (retval != 0)
+ {
+ LDAPDebug( LDAP_DEBUG_TRACE, "index_add_mods 1 failed, err=%d %s\n", retval, (msg = dblayer_strerror( retval )) ? msg : "", 0 );
+ goto error_return;
+ }
+ }
+ if(smods2!=NULL && slapi_mods_get_num_mods(smods2)>0)
+ {
+ /*
+ * smods2 contains the state generated mods. One of them might be the removal of a "nsuniqueid" rdn component
+ * previously gnerated through a conflict resolution. We need to make sure we don't remove the index for "nsuniqueid"
+ * so let's get it out from the mods before calling index_add_mods...
+ */
+ mods_remove_nsuniqueid(smods2);
+ /*
+ * update the indexes: lastmod, rdn, etc.
+ */
+ retval = index_add_mods( be, (const LDAPMod **)slapi_mods_get_ldapmods_byref(smods2), e, ec, ptxn );
+ if (DB_LOCK_DEADLOCK == retval)
+ {
+ /* Retry txn */
+ goto error_return;
+ }
+ if (retval != 0)
+ {
+ LDAPDebug( LDAP_DEBUG_TRACE, "index_add_mods 2 failed, err=%d %s\n", retval, (msg = dblayer_strerror( retval )) ? msg : "", 0 );
+ goto error_return;
+ }
+ }
+ if(smods3!=NULL && slapi_mods_get_num_mods(smods3)>0)
+ {
+ /*
+ * update the indexes: lastmod, rdn, etc.
+ */
+ retval = index_add_mods( be, (const LDAPMod **)slapi_mods_get_ldapmods_byref(smods3), e, ec, ptxn );
+ if (DB_LOCK_DEADLOCK == retval)
+ {
+ /* Retry txn */
+ goto error_return;
+ }
+ if (retval != 0)
+ {
+ LDAPDebug( LDAP_DEBUG_TRACE, "index_add_mods 3 failed, err=%d %s\n", retval, (msg = dblayer_strerror( retval )) ? msg : "", 0 );
+ goto error_return;
+ }
+ }
+ /*
+ * Remove the old entry from the Virtual List View indexes.
+ * Add the new entry to the Virtual List View indexes.
+ */
+ retval= vlv_update_all_indexes(ptxn, be, pb, e, ec);
+ if (DB_LOCK_DEADLOCK == retval)
+ {
+ /* Abort and re-try */
+ goto error_return;
+ }
+ if (retval != 0)
+ {
+ LDAPDebug( LDAP_DEBUG_TRACE, "vlv_update_all_indexes failed, err=%d %s\n", retval, (msg = dblayer_strerror( retval )) ? msg : "", 0 );
+ goto error_return;
+ }
+ if (cache_replace( &inst->inst_cache, e, ec ) != 0 ) {
+ retval= -1;
+ goto error_return;
+ }
+error_return:
+ return retval;
+}
+
+
+/*
+ */
+static int
+moddn_rename_child_entry(
+ back_txn *ptxn,
+ Slapi_PBlock *pb,
+ struct ldbminfo *li,
+ struct backentry *e,
+ struct backentry *ec,
+ int parentdncomps,
+ char **newsuperiordns,
+ int newsuperiordncomps,
+ CSN *opcsn)
+{
+ /*
+ * Construct the new DN for the entry by taking the old DN
+ * excluding the old parent entry DN, and adding the new
+ * superior entry DN.
+ *
+ * ldap_explode_dn is probably a bit slow, but it knows about
+ * DN escaping which is pretty complicated, and we wouldn't
+ * want to reimplement that here.
+ *
+ * JCM - This was written before Slapi_RDN... so this could be made much neater.
+ */
+ int retval;
+ char *olddn;
+ char *newdn;
+ char **olddns;
+ int olddncomps= 0;
+ int need= 1; /* For the '\0' */
+ int i;
+
+ olddn = slapi_entry_get_dn(ec->ep_entry);
+ olddns = ldap_explode_dn( olddn, 0 );
+ for(;olddns[olddncomps]!=NULL;olddncomps++);
+ for(i=0;i<olddncomps-parentdncomps;i++)
+ {
+ need+= strlen(olddns[i]) + 2; /* For the ", " */
+ }
+ for(i=0;i<newsuperiordncomps;i++)
+ {
+ need+= strlen(newsuperiordns[i]) + 2; /* For the ", " */
+ }
+ need--; /* We don't have a comma on the end of the last component */
+ newdn= slapi_ch_malloc(need);
+ newdn[0]= '\0';
+ for(i=0;i<olddncomps-parentdncomps;i++)
+ {
+ strcat(newdn,olddns[i]);
+ strcat(newdn,", ");
+ }
+ for(i=0;i<newsuperiordncomps;i++)
+ {
+ strcat(newdn,newsuperiordns[i]);
+ if(i<newsuperiordncomps-1)
+ {
+ /* We don't have a comma on the end of the last component */
+ strcat(newdn,", ");
+ }
+ }
+ ldap_value_free( olddns );
+ slapi_entry_set_dn( ec->ep_entry, newdn );
+ add_update_entrydn_operational_attributes (ec);
+
+ /*
+ * Update the DN CSN of the entry.
+ */
+ {
+ entry_add_dncsn(e->ep_entry, opcsn);
+ entry_add_rdn_csn(e->ep_entry, opcsn);
+ entry_set_maxcsn(e->ep_entry, opcsn);
+ }
+ {
+ Slapi_Mods smods;
+ slapi_mods_init(&smods, 2);
+ slapi_mods_add( &smods, LDAP_MOD_DELETE, "entrydn", strlen( backentry_get_ndn(e) ), backentry_get_ndn(e) );
+ slapi_mods_add( &smods, LDAP_MOD_REPLACE, "entrydn", strlen( backentry_get_ndn(ec) ), backentry_get_ndn(ec) );
+ /*
+ * Update all the indexes.
+ */
+ retval= modrdn_rename_entry_update_indexes(ptxn, pb, li, e, ec, &smods, NULL, NULL); /* JCMREPL - Should the children get updated modifiersname and lastmodifiedtime? */
+ slapi_mods_done(&smods);
+ }
+ return retval;
+}
+
+/*
+ * Rename all the children of an entry who's name has changed.
+ */
+static int
+moddn_rename_children(
+ back_txn *ptxn,
+ Slapi_PBlock *pb,
+ backend *be,
+ IDList *children,
+ Slapi_DN *dn_parentdn,
+ Slapi_DN *dn_newsuperiordn,
+ struct backentry *child_entries[],
+ struct backentry *child_entry_copies[])
+{
+ /* Iterate over the children list renaming every child */
+ struct ldbminfo *li = (struct ldbminfo *) be->be_database->plg_private;
+ Slapi_Operation *operation;
+ CSN *opcsn;
+ int retval= 0, i;
+ char **newsuperiordns;
+ int newsuperiordncomps= 0;
+ int parentdncomps= 0;
+
+ /*
+ * Break down the parent entry dn into its components.
+ */
+ {
+ char **parentdns;
+ parentdns = ldap_explode_dn( slapi_sdn_get_dn(dn_parentdn), 0 );
+ for(;parentdns[parentdncomps]!=NULL;parentdncomps++);
+ ldap_value_free( parentdns );
+ }
+
+ /*
+ * Break down the new superior entry dn into its components.
+ */
+ newsuperiordns = ldap_explode_dn( slapi_sdn_get_dn(dn_newsuperiordn), 0 );
+ for(;newsuperiordns[newsuperiordncomps]!=NULL;newsuperiordncomps++);
+
+ /*
+ * Iterate over the child entries renaming them.
+ */
+ slapi_pblock_get( pb, SLAPI_OPERATION, &operation );
+ opcsn = operation_get_csn (operation);
+ for (i = 0; retval == 0 && child_entries[i] != NULL; i++) {
+ retval= moddn_rename_child_entry(ptxn, pb, li, child_entries[i], child_entry_copies[i], parentdncomps, newsuperiordns, newsuperiordncomps, opcsn );
+ }
+ if (retval != 0) {
+ while (child_entries[i] != NULL) {
+ backentry_free(&(child_entry_copies[i]));
+ i++;
+ }
+ }
+ ldap_value_free( newsuperiordns );
+ return retval;
+}
+
+
+/*
+ * Get an IDList of all the children of an entry.
+ */
+static IDList *
+moddn_get_children(back_txn *ptxn, Slapi_PBlock *pb, backend *be, struct backentry *parententry, Slapi_DN *dn_parentdn, struct backentry ***child_entries, struct backentry ***child_entry_copies)
+{
+ ldbm_instance *inst = (ldbm_instance *) be->be_instance_info;
+ int err= 0;
+ IDList *candidates;
+ IDList *result_idl = NULL;
+ char filterstr[20];
+ Slapi_Filter *filter;
+ NIDS nids;
+ int entrynumber= 0;
+ ID id;
+ idl_iterator sr_current; /* the current position in the search results */
+ struct backentry *e= NULL;
+
+ /* Fetch a candidate list of all the entries below the entry being moved */
+ strcpy( filterstr, "objectclass=*" );
+ filter = slapi_str2filter( filterstr );
+ candidates= subtree_candidates(pb, be, slapi_sdn_get_ndn(dn_parentdn), parententry, filter, 1 /* ManageDSAIT */, NULL /* allids_before_scopingp */, &err);
+ slapi_filter_free(filter,1);
+
+ if (candidates!=NULL)
+ {
+ sr_current = idl_iterator_init(candidates);
+ result_idl= idl_alloc(candidates->b_nids);
+ do
+ {
+ id = idl_iterator_dereference_increment(&sr_current, candidates);
+ if ( id!=NOID )
+ {
+ int err= 0;
+ e = id2entry( be, id, NULL, &err );
+ if (e!=NULL)
+ {
+ /* The subtree search will have included the parent entry in the result set */
+ if (e!=parententry)
+ {
+ /* Check that the candidate entry is really below the base. */
+ if(slapi_dn_issuffix( backentry_get_ndn(e), slapi_sdn_get_ndn(dn_parentdn)))
+ {
+ idl_append(result_idl,id);
+ }
+ }
+ cache_return(&inst->inst_cache, &e);
+ }
+ }
+ } while (id!=NOID);
+ idl_free(candidates);
+ }
+
+ nids = result_idl ? result_idl->b_nids : 0;
+
+ *child_entries= (struct backentry**)slapi_ch_calloc(sizeof(struct backentry*),nids+1);
+ *child_entry_copies= (struct backentry**)slapi_ch_calloc(sizeof(struct backentry*),nids+1);
+
+ sr_current = idl_iterator_init(result_idl);
+ do {
+ id = idl_iterator_dereference_increment(&sr_current, result_idl);
+ if ( id!=NOID ) {
+ e= cache_find_id( &inst->inst_cache, id );
+ if ( e != NULL ) {
+ cache_lock_entry(&inst->inst_cache, e);
+ (*child_entries)[entrynumber]= e;
+ (*child_entry_copies)[entrynumber]= backentry_dup(e);
+ entrynumber++;
+ }
+ }
+ } while (id!=NOID);
+
+ return result_idl;
+}
diff --git a/ldap/servers/slapd/back-ldbm/ldbm_search.c b/ldap/servers/slapd/back-ldbm/ldbm_search.c
new file mode 100644
index 00000000..ee33011d
--- /dev/null
+++ b/ldap/servers/slapd/back-ldbm/ldbm_search.c
@@ -0,0 +1,1345 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+/* search.c - ldbm backend search function */
+/* view with ts=4 */
+
+#include "back-ldbm.h"
+#include "vlv_srch.h"
+
+/* prototypes */
+static int build_candidate_list( Slapi_PBlock *pb, backend *be,
+ struct backentry *e, const char * base, int scope,
+ int *lookup_returned_allidsp, IDList** candidates);
+static IDList *base_candidates( Slapi_PBlock *pb, struct backentry *e );
+static IDList *onelevel_candidates( Slapi_PBlock *pb, backend *be, const char *base, struct backentry *e, Slapi_Filter *filter, int managedsait, int *lookup_returned_allidsp, int *err );
+static back_search_result_set* new_search_result_set(IDList* idl,int vlv, int lookthroughlimit);
+static void delete_search_result_set( back_search_result_set **sr );
+static int can_skip_filter_test( Slapi_PBlock *pb, struct slapi_filter *f,
+ int scope, IDList *idl );
+
+/* This is for performance testing, allows us to disable ACL checking altogether */
+#if defined(DISABLE_ACL_CHECK)
+#define ACL_CHECK_FLAG 0
+#else
+#define ACL_CHECK_FLAG 1
+#endif
+
+#define ISLEGACY(be) (be?(be->be_instance_info?(((ldbm_instance *)be->be_instance_info)->inst_li?(((ldbm_instance *)be->be_instance_info)->inst_li->li_legacy_errcode):0):0):0)
+
+static int
+compute_lookthrough_limit( Slapi_PBlock *pb, struct ldbminfo *li )
+{
+ Slapi_Connection *conn = NULL;
+ int limit;
+
+ slapi_pblock_get( pb, SLAPI_CONNECTION, &conn);
+
+ if ( slapi_reslimit_get_integer_limit( conn,
+ li->li_reslimit_lookthrough_handle, &limit )
+ != SLAPI_RESLIMIT_STATUS_SUCCESS ) {
+ /*
+ * no limit associated with binder/connection or some other error
+ * occurred. use the default.
+ */
+ int isroot = 0;
+
+ slapi_pblock_get( pb, SLAPI_REQUESTOR_ISROOT, &isroot );
+ if (isroot) {
+ limit = -1;
+ } else {
+ PR_Lock(li->li_config_mutex);
+ limit = li->li_lookthroughlimit;
+ PR_Unlock(li->li_config_mutex);
+ }
+ }
+
+ return( limit );
+}
+
+/* don't free the berval, just clean it */
+static void
+berval_done(struct berval *val)
+{
+ slapi_ch_free_string(&val->bv_val);
+}
+
+/*
+ * We call this function as we exit ldbm_back_search
+ */
+int ldbm_back_search_cleanup(Slapi_PBlock *pb, struct ldbminfo *li, sort_spec_thing *sort_control, int ldap_result, char* ldap_result_description, int function_result, Slapi_DN *sdn, struct vlv_request *vlv_request_control)
+{
+ if(sort_control!=NULL)
+ {
+ sort_spec_free(sort_control);
+ }
+ if(ldap_result>=LDAP_SUCCESS)
+ {
+ slapi_send_ldap_result( pb, ldap_result, NULL, ldap_result_description, 0, NULL );
+ }
+ {
+ /* hack hack --- code to free the result set if we don't need it */
+ /* We get it and check to see if the structure was ever used */
+ back_search_result_set *sr = NULL;
+ slapi_pblock_get( pb, SLAPI_SEARCH_RESULT_SET, &sr );
+ if ( (NULL != sr) && (function_result != 0) ) {
+ delete_search_result_set(&sr);
+ }
+ }
+ slapi_sdn_done(sdn);
+ if (vlv_request_control)
+ {
+ berval_done(&vlv_request_control->value);
+ }
+ return function_result;
+}
+
+/*
+ * Return values from ldbm_back_search are:
+ *
+ * 0: Success. A result set is in the pblock. No results have been
+ * sent to the client.
+ * 1: Success. The result has already been sent to the client.
+ * -1: An error occurred, and results have been sent to the client.
+ * -2: Disk Full. Abandon ship!
+ */
+int
+ldbm_back_search( Slapi_PBlock *pb )
+{
+ /* Search stuff */
+ backend *be;
+ ldbm_instance *inst;
+ struct ldbminfo *li;
+ struct backentry *e;
+ IDList *candidates= NULL;
+ char *base;
+ Slapi_DN basesdn;
+ int scope;
+ LDAPControl **controls = NULL;
+ Slapi_Operation *operation;
+ entry_address *addr;
+
+ /* SORT control stuff */
+ int sort = 0;
+ int vlv = 0;
+ struct berval *sort_spec = NULL;
+ int is_sorting_critical = 0;
+ int is_sorting_critical_orig = 0;
+ sort_spec_thing *sort_control = NULL;
+
+ /* VLV control stuff */
+ int virtual_list_view = 0;
+ struct berval *vlv_spec = NULL;
+ int is_vlv_critical = 0;
+ struct vlv_request vlv_request_control;
+ back_search_result_set *sr = NULL;
+
+ /* Fix for bugid #394184, SD, 20 Jul 00 */
+ int tmp_err = -1; /* must be lower than LDAP_SUCCESS */
+ char * tmp_desc = NULL;
+ /* end Fix for defect #394184 */
+
+ int lookup_returned_allids = 0;
+ int backend_count = 1;
+ static int print_once = 1;
+
+ slapi_pblock_get( pb, SLAPI_BACKEND, &be );
+ slapi_pblock_get( pb, SLAPI_OPERATION, &operation);
+ slapi_pblock_get( pb, SLAPI_PLUGIN_PRIVATE, &li );
+ slapi_pblock_get( pb, SLAPI_SEARCH_TARGET, &base );
+ slapi_pblock_get( pb, SLAPI_TARGET_ADDRESS, &addr);
+ slapi_pblock_get( pb, SLAPI_SEARCH_SCOPE, &scope );
+ slapi_pblock_get( pb, SLAPI_REQCONTROLS, &controls );
+ slapi_pblock_get( pb, SLAPI_BACKEND_COUNT, &backend_count );
+
+ inst = (ldbm_instance *) be->be_instance_info;
+
+ slapi_sdn_init_dn_ndn_byref(&basesdn,base); /* normalized by front end*/
+ /* Initialize the result set structure here because we need to use it during search processing */
+ /* Beware that if we exit this routine sideways, we might leak this structure */
+ sr = new_search_result_set( NULL, 0,
+ compute_lookthrough_limit( pb, li ));
+ slapi_pblock_set( pb, SLAPI_SEARCH_RESULT_SET, sr );
+
+ /* clear this out so we can free it later */
+ memset(&vlv_request_control, 0, sizeof(vlv_request_control));
+ if ( NULL != controls )
+ {
+ /* Are we being asked to sort the results ? */
+ sort = slapi_control_present( controls, LDAP_CONTROL_SORTREQUEST, &sort_spec, &is_sorting_critical_orig );
+ if(sort)
+ {
+ int r= parse_sort_spec(sort_spec, &sort_control);
+ if(r!=0)
+ {
+ /* Badly formed SORT control */
+ return ldbm_back_search_cleanup(pb, li, sort_control, LDAP_PROTOCOL_ERROR, "Sort Control", SLAPI_FAIL_GENERAL, &basesdn, NULL);
+ }
+ }
+ is_sorting_critical = is_sorting_critical_orig;
+
+ /* Are we to provide a virtual view of the list? */
+ if ((vlv = slapi_control_present( controls, LDAP_CONTROL_VLVREQUEST, &vlv_spec, &is_vlv_critical)))
+ {
+ if(sort)
+ {
+ int r = vlv_parse_request_control( be, vlv_spec, &vlv_request_control );
+ if(r!=LDAP_SUCCESS)
+ {
+ /* Badly formed VLV control */
+ return ldbm_back_search_cleanup(pb, li, sort_control, r, "VLV Control", SLAPI_FAIL_GENERAL, &basesdn, &vlv_request_control);
+ }
+ {
+ /* Access Control Check to see if the client is allowed to use the VLV Control. */
+ Slapi_Entry *feature;
+ char dn[128];
+ char *dummyAttr = "dummy#attr";
+ char *dummyAttrs[2] = { NULL, NULL };
+
+ dummyAttrs[0] = dummyAttr;
+
+ sprintf(dn,"dn: oid=%s,cn=features,cn=config",LDAP_CONTROL_VLVREQUEST);
+ feature= slapi_str2entry(dn,0);
+ r= plugin_call_acl_plugin (pb, feature, dummyAttrs, NULL, SLAPI_ACL_READ, ACLPLUGIN_ACCESS_DEFAULT, NULL);
+ slapi_entry_free(feature);
+ if(r!=LDAP_SUCCESS)
+ {
+ /* Client isn't allowed to do this. */
+ return ldbm_back_search_cleanup(pb, li, sort_control, r, "VLV Control", SLAPI_FAIL_GENERAL, &basesdn, &vlv_request_control);
+ }
+ }
+ /*
+ * Sorting must always be critical for VLV; Force it be so.
+ */
+ is_sorting_critical= 1;
+ virtual_list_view= 1;
+ }
+ else
+ {
+ /* Can't have a VLV control without a SORT control */
+ return ldbm_back_search_cleanup(pb, li, sort_control, LDAP_SORT_CONTROL_MISSING, "VLV Control", SLAPI_FAIL_GENERAL, &basesdn, &vlv_request_control);
+ }
+ }
+ }
+ if ((virtual_list_view || sort) && backend_count > 0)
+ {
+ char *ctrlstr = NULL;
+ struct vlv_response vlv_response = {0};
+ if (virtual_list_view)
+ {
+ if (sort)
+ {
+ ctrlstr = "The VLV and sort controls cannot be processed";
+ }
+ else
+ {
+ ctrlstr = "The VLV control cannot be processed";
+ }
+ }
+ else
+ {
+ if (sort)
+ {
+ ctrlstr = "The sort control cannot be processed";
+ }
+ }
+
+ PR_ASSERT(NULL != ctrlstr);
+
+ if (print_once)
+ {
+ LDAPDebug(LDAP_DEBUG_ANY,
+ "ERROR: %s "
+ "when more than one backend is involved. "
+ "VLV indexes that will never be used should be removed.\n",
+ ctrlstr, 0, 0);
+ print_once = 0;
+ }
+
+ /* 402380: mapping tree must refuse VLV and SORT control
+ * when several backends are impacted by a search */
+ if (0 != is_vlv_critical)
+ {
+ vlv_response.result = LDAP_UNWILLING_TO_PERFORM;
+ vlv_make_response_control(pb, &vlv_response);
+ if (sort)
+ {
+ make_sort_response_control(pb, LDAP_UNWILLING_TO_PERFORM, NULL);
+ }
+ if (ISLEGACY(be))
+ {
+ return ldbm_back_search_cleanup(pb, li, sort_control,
+ LDAP_UNWILLING_TO_PERFORM, ctrlstr,
+ SLAPI_FAIL_GENERAL, &basesdn, &vlv_request_control);
+ }
+ else
+ {
+ return ldbm_back_search_cleanup(pb, li, sort_control,
+ LDAP_VIRTUAL_LIST_VIEW_ERROR, ctrlstr,
+ SLAPI_FAIL_GENERAL, &basesdn, &vlv_request_control);
+ }
+ }
+ else
+ {
+ if (0 != is_sorting_critical_orig)
+ {
+ if (virtual_list_view)
+ {
+ vlv_response.result = LDAP_UNWILLING_TO_PERFORM;
+ vlv_make_response_control(pb, &vlv_response);
+ }
+ make_sort_response_control(pb, LDAP_UNWILLING_TO_PERFORM, NULL);
+ return ldbm_back_search_cleanup(pb, li, sort_control,
+ LDAP_UNAVAILABLE_CRITICAL_EXTENSION, ctrlstr,
+ SLAPI_FAIL_GENERAL, &basesdn, &vlv_request_control);
+ }
+ else /* vlv and sorting are not critical, so ignore the control */
+ {
+ if (virtual_list_view)
+ {
+ vlv_response.result = LDAP_UNWILLING_TO_PERFORM;
+ vlv_make_response_control(pb, &vlv_response);
+ }
+ if (sort)
+ {
+ make_sort_response_control(pb, LDAP_UNWILLING_TO_PERFORM, NULL);
+ }
+ sort = 0;
+ virtual_list_view = 0;
+ }
+ }
+ }
+
+ /*
+ * Get the base object for the search.
+ * The entry "" will never be contained in the database,
+ * so treat it as a special case.
+ */
+ if ( *base == '\0' )
+ {
+ e = NULL;
+ }
+ else
+ {
+ if ( ( e = find_entry( pb, be, addr, NULL )) == NULL )
+ {
+ /* error or referral sent by find_entry */
+ return ldbm_back_search_cleanup(pb, li, sort_control, -1, NULL, 1, &basesdn, &vlv_request_control);
+ }
+ }
+
+ /*
+ * If this is a persistent search then the client is only
+ * interested in entries that change, so we skip building
+ * a candidate list.
+ */
+ if (operation_is_flag_set( operation, OP_FLAG_PS_CHANGESONLY ))
+ {
+ candidates = NULL;
+ }
+ else
+ {
+ time_t time_up= 0;
+ int lookthrough_limit = 0;
+ struct vlv_response vlv_response_control;
+ int abandoned= 0;
+ int vlv_rc;
+ /*
+ * Build a list of IDs for this entry and scope
+ */
+ if ((NULL != controls) && (sort)) {
+ switch (vlv_search_build_candidate_list(pb, &basesdn, &vlv_rc, sort_control, (vlv ? &vlv_request_control : NULL), &candidates, &vlv_response_control)) {
+ case VLV_ACCESS_DENIED:
+ return ldbm_back_search_cleanup(pb, li, sort_control, vlv_rc, "VLV Control", SLAPI_FAIL_GENERAL, &basesdn, &vlv_request_control);
+
+ case VLV_BLD_LIST_FAILED:
+ return ldbm_back_search_cleanup(pb, li, sort_control, vlv_response_control.result, NULL, SLAPI_FAIL_GENERAL, &basesdn, &vlv_request_control);
+
+ case LDAP_SUCCESS:
+ /* Log to the access log the particulars of this sort request */
+ /* Log message looks like this: SORT <key list useful for input to ldapsearch> <#candidates> | <unsortable> */
+ sort_log_access(pb,sort_control,NULL);
+ /* Since a pre-computed index was found for the VLV Search then
+ * the candidate list now contains exactly what should be returned.
+ * There's no need to sort or trim the candidate list.
+ *
+ * However, the client will be expecting a Sort Response control
+ */
+ if (LDAP_SUCCESS != make_sort_response_control( pb, 0, NULL ) )
+ {
+ return ldbm_back_search_cleanup(pb, li, sort_control, LDAP_OPERATIONS_ERROR, "Sort Response Control", SLAPI_FAIL_GENERAL, &basesdn, &vlv_request_control);
+ }
+ }
+ }
+ if(candidates==NULL)
+ {
+ int rc = build_candidate_list(pb, be, e, base, scope,
+ &lookup_returned_allids, &candidates);
+ if (rc)
+ {
+ /* Error result sent by build_candidate_list */
+ return ldbm_back_search_cleanup(pb, li, sort_control, -1, NULL, rc, &basesdn, &vlv_request_control);
+ }
+ /*
+ * If we're sorting then we must check what administrative
+ * limits should be imposed. Work out at what time to give
+ * up, and how many entries we should sift through.
+ */
+ if (sort && (NULL != candidates))
+ {
+ time_t optime = 0;
+ time_t tlimit = 0;
+
+ slapi_pblock_get( pb, SLAPI_SEARCH_TIMELIMIT, &tlimit );
+ slapi_pblock_get( pb, SLAPI_OPINITIATED_TIME, &optime );
+ /*
+ * (tlimit==-1) means no time limit
+ */
+ time_up = ( tlimit==-1 ? -1 : optime + tlimit);
+
+ lookthrough_limit = compute_lookthrough_limit( pb, li );
+ }
+
+ /*
+ * If we're presenting a virtual list view, then apply the
+ * search filter before sorting.
+ */
+ if (virtual_list_view && (NULL != candidates))
+ {
+ int r= 0;
+ IDList *idl= NULL;
+ Slapi_Filter *filter= NULL;
+ slapi_pblock_get( pb, SLAPI_SEARCH_FILTER, &filter );
+ r= vlv_filter_candidates(be, pb, candidates, &basesdn, scope, filter, &idl, lookthrough_limit, time_up);
+ if(r==0)
+ {
+ idl_free(candidates);
+ candidates= idl;
+ }
+ else
+ {
+ return ldbm_back_search_cleanup(pb, li, sort_control, r, NULL, -1, &basesdn, &vlv_request_control);
+ }
+ }
+ /*
+ * Client wants the server to sort the results.
+ */
+ if (sort && (NULL != candidates))
+ {
+ /* Before we haste off to sort the candidates, we need to
+ * prepare some information for the purpose of imposing the
+ * administrative limits.
+ * We figure out the time when the time limit will be up.
+ * We can't use the size limit because we might be sorting
+ * a candidate list larger than the result set.
+ * But, we can use the lookthrough limit---we count each
+ * time we access an entry as one look and act accordingly.
+ */
+
+ char *sort_error_type = NULL;
+ int sort_return_value = 0;
+
+ /* Log to the access log the particulars of this sort request */
+ /* Log message looks like this: SORT <key list useful for input to ldapsearch> <#candidates> | <unsortable> */
+ sort_log_access(pb,sort_control,candidates);
+ sort_return_value = sort_candidates( be, lookthrough_limit, time_up, pb, candidates, sort_control, &sort_error_type );
+ /* Fix for bugid # 394184, SD, 20 Jul 00 */
+ /* replace the hard coded return value by the appropriate LDAP error code */
+ switch (sort_return_value) {
+ case LDAP_SUCCESS: /* Everything OK */
+ vlv_response_control.result= LDAP_SUCCESS;
+ break;
+ case LDAP_PROTOCOL_ERROR: /* A protocol error */
+ return ldbm_back_search_cleanup(pb, li, sort_control, LDAP_PROTOCOL_ERROR, "Sort Control", -1, &basesdn, &vlv_request_control);
+ case LDAP_UNWILLING_TO_PERFORM: /* Too hard */
+ case LDAP_OPERATIONS_ERROR: /* Operation error */
+ case LDAP_TIMELIMIT_EXCEEDED: /* Timeout */
+ vlv_response_control.result= LDAP_TIMELIMIT_EXCEEDED;
+ break;
+ case LDAP_ADMINLIMIT_EXCEEDED: /* Admin limit exceeded */
+ vlv_response_control.result= LDAP_ADMINLIMIT_EXCEEDED;
+ break;
+ case LDAP_OTHER: /* Abandoned */
+ abandoned= 1; /* So that we don't return a result code */
+ is_sorting_critical= 1; /* In order to have the results discarded */
+ break;
+ default: /* Should never get here */
+ break;
+ }
+ /* End fix for bug # 394184 */
+ /*
+ * If the sort control was marked as critical, and there was an error in sorting,
+ * don't return any entries, and return unavailableCriticalExtension in the
+ * searchResultDone message.
+ */
+ /* Fix for bugid #394184, SD, 05 Jul 00 */
+ /* we were not actually returning unavailableCriticalExtension;
+ now fixed (hopefully !) */
+ if (is_sorting_critical && (0 != sort_return_value))
+ {
+ idl_free(candidates);
+ candidates = idl_alloc(0);
+ tmp_err = LDAP_UNAVAILABLE_CRITICAL_EXTENSION;
+ tmp_desc = "Sort Response Control";
+ }
+ /* end Fix for bugid #394184 */
+ /* Generate the control returned to the client to indicate sort result */
+ if (LDAP_SUCCESS != make_sort_response_control( pb, sort_return_value, sort_error_type ) )
+ {
+ return ldbm_back_search_cleanup(pb, li, sort_control, (abandoned?-1:LDAP_PROTOCOL_ERROR), "Sort Response Control", -1, &basesdn, &vlv_request_control);
+ }
+ }
+ /*
+ * If we're presenting a virtual list view, then the candidate list
+ * must be trimmed down to just the range of entries requested.
+ */
+ if (virtual_list_view)
+ {
+ if (NULL != candidates && candidates->b_nids>0)
+ {
+ IDList *idl= NULL;
+ vlv_response_control.result= vlv_trim_candidates(be, candidates, sort_control, &vlv_request_control, &idl, &vlv_response_control);
+ if(vlv_response_control.result==0)
+ {
+ idl_free(candidates);
+ candidates= idl;
+ }
+ else
+ {
+ return ldbm_back_search_cleanup(pb, li, sort_control, vlv_response_control.result, NULL, -1, &basesdn, &vlv_request_control);
+ }
+ }
+ else
+ {
+ vlv_response_control.targetPosition= 0;
+ vlv_response_control.contentCount= 0;
+ vlv_response_control.result= LDAP_SUCCESS;
+ }
+ }
+ }
+ if (virtual_list_view)
+ {
+ if(LDAP_SUCCESS != vlv_make_response_control( pb, &vlv_response_control ))
+ {
+ return ldbm_back_search_cleanup(pb, li, sort_control, (abandoned?-1:LDAP_PROTOCOL_ERROR), "VLV Response Control", -1, &basesdn, &vlv_request_control);
+ }
+ /* Log the VLV operation */
+ vlv_print_access_log(pb,&vlv_request_control,&vlv_response_control);
+ }
+ }
+
+ cache_return( &inst->inst_cache, &e );
+
+ /*
+ * if the candidate list is an allids list, arrange for access log
+ * to record that fact.
+ */
+ if ( NULL != candidates && ALLIDS( candidates )) {
+ unsigned int opnote = SLAPI_OP_NOTE_UNINDEXED;
+ int ri = 0;
+
+ /*
+ * Return error if nsslapd-require-index is set and
+ * this is not an internal operation.
+ * We hope the plugins know what they are doing!
+ */
+ if (!operation_is_flag_set(operation, OP_FLAG_INTERNAL)) {
+
+ PR_Lock(inst->inst_config_mutex);
+ ri = inst->require_index;
+ PR_Unlock(inst->inst_config_mutex);
+
+ if (ri) {
+ idl_free(candidates);
+ candidates = idl_alloc(0);
+ tmp_err = LDAP_UNWILLING_TO_PERFORM;
+ tmp_desc = "Search is not indexed";
+ }
+ }
+
+ slapi_pblock_set( pb, SLAPI_OPERATION_NOTES, &opnote );
+ }
+
+ sr->sr_candidates = candidates;
+ sr->sr_virtuallistview = virtual_list_view;
+
+ /* check to see if we can skip the filter test */
+ if ( li->li_filter_bypass && NULL != candidates && !virtual_list_view
+ && !lookup_returned_allids ) {
+ Slapi_Filter *filter= NULL;
+
+ slapi_pblock_get( pb, SLAPI_SEARCH_FILTER, &filter );
+ if ( can_skip_filter_test( pb, filter, scope, candidates)) {
+ sr->sr_flags |= SR_FLAG_CAN_SKIP_FILTER_TEST;
+ }
+ }
+
+ /* Fix for bugid #394184, SD, 05 Jul 00 */
+ /* tmp_err == -1: no error */
+ return ldbm_back_search_cleanup(pb, li, sort_control, tmp_err, tmp_desc, (tmp_err == -1 ? 0 : -1), &basesdn, &vlv_request_control);
+ /* end Fix for bugid #394184 */
+}
+
+/*
+ * Build a candidate list for this backentry and scope.
+ * Could be a BASE, ONELEVEL, or SUBTREE search.
+ *
+ * Returns:
+ * 0 - success
+ * <0 - fail
+ *
+ */
+static int
+build_candidate_list( Slapi_PBlock *pb, backend *be, struct backentry *e,
+ const char * base, int scope, int *lookup_returned_allidsp,
+ IDList** candidates)
+{
+ struct ldbminfo *li = (struct ldbminfo *) be->be_database->plg_private;
+ int managedsait= 0;
+ Slapi_Filter *filter= NULL;
+ int err= 0;
+ int r= 0;
+
+ slapi_pblock_get( pb, SLAPI_SEARCH_FILTER, &filter );
+ slapi_pblock_get( pb, SLAPI_MANAGEDSAIT, &managedsait );
+
+ switch ( scope ) {
+ case LDAP_SCOPE_BASE:
+ *candidates = base_candidates( pb, e );
+ break;
+
+ case LDAP_SCOPE_ONELEVEL:
+ *candidates = onelevel_candidates( pb, be, base, e, filter, managedsait,
+ lookup_returned_allidsp, &err );
+ break;
+
+ case LDAP_SCOPE_SUBTREE:
+ *candidates = subtree_candidates(pb, be, base, e, filter, managedsait,
+ lookup_returned_allidsp, &err);
+ break;
+
+ default:
+ slapi_send_ldap_result( pb, LDAP_PROTOCOL_ERROR, NULL, "Bad scope", 0, NULL );
+ r = SLAPI_FAIL_GENERAL;
+ }
+ if ( 0 != err && DB_NOTFOUND != err ) {
+ LDAPDebug( LDAP_DEBUG_ANY, "database error %d\n", err, 0, 0 );
+ slapi_send_ldap_result( pb, LDAP_OPERATIONS_ERROR, NULL, NULL,
+ 0, NULL );
+ if (LDBM_OS_ERR_IS_DISKFULL(err)) r = return_on_disk_full(li);
+ else r = SLAPI_FAIL_GENERAL;
+ }
+
+ /*
+ * If requested, set a flag to indicate whether the indexed
+ * lookup returned an ALLIDs block. Note that this is taken care of
+ * above already for subtree searches.
+ */
+ if ( NULL != lookup_returned_allidsp ) {
+ if ( 0 == err ) {
+ if ( !(*lookup_returned_allidsp) && LDAP_SCOPE_SUBTREE != scope ) {
+ *lookup_returned_allidsp =
+ ( NULL != *candidates && ALLIDS( *candidates ));
+ }
+ } else {
+ *lookup_returned_allidsp = 0;
+ }
+ }
+
+ LDAPDebug(LDAP_DEBUG_TRACE, "candidate list has %lu ids\n",
+ *candidates ? (*candidates)->b_nids : 0L, 0, 0);
+
+ return r;
+}
+
+/*
+ * Build a candidate list for a BASE scope search.
+ */
+static IDList *
+base_candidates(Slapi_PBlock *pb, struct backentry *e)
+{
+ IDList *idl= idl_alloc( 1 );
+ idl_append( idl, NULL == e ? 0 : e->ep_id );
+ return( idl );
+}
+
+/*
+ * Modify the filter to include entries of the referral objectclass
+ *
+ * make (|(originalfilter)(objectclass=referral))
+ *
+ * "focref, forr" are temporary filters which the caller must free
+ * non-recursively when done with the returned filter.
+ */
+static Slapi_Filter*
+create_referral_filter(Slapi_Filter* filter, Slapi_Filter** focref, Slapi_Filter** forr)
+{
+ char *buf = slapi_ch_strdup( "objectclass=referral" );
+
+ *focref = slapi_str2filter( buf );
+ *forr = slapi_filter_join( LDAP_FILTER_OR, filter, *focref );
+
+ slapi_ch_free((void **)&buf);
+ return *forr;
+}
+
+/*
+ * Modify the filter to be a one level search.
+ *
+ * (&(parentid=idofbase)(|(originalfilter)(objectclass=referral)))
+ *
+ * "fid2kids, focref, fand, forr" are temporary filters which the
+ * caller must free'd non-recursively when done with the returned filter.
+ *
+ * This function is exported for the VLV code to use.
+ */
+Slapi_Filter*
+create_onelevel_filter(Slapi_Filter* filter, const struct backentry *baseEntry, int managedsait, Slapi_Filter** fid2kids, Slapi_Filter** focref, Slapi_Filter** fand, Slapi_Filter** forr)
+{
+ Slapi_Filter *ftop= filter;
+ char buf[40];
+
+ if ( !managedsait )
+ {
+ ftop= create_referral_filter(filter, focref, forr);
+ }
+
+ sprintf( buf, "parentid=%lu", (u_long)(baseEntry != NULL ? baseEntry->ep_id : 0) );
+ *fid2kids = slapi_str2filter( buf );
+ *fand = slapi_filter_join( LDAP_FILTER_AND, ftop, *fid2kids );
+
+ return *fand;
+}
+
+/*
+ * Build a candidate list for a ONELEVEL scope search.
+ */
+static IDList *
+onelevel_candidates(
+ Slapi_PBlock *pb,
+ backend *be,
+ const char *base,
+ struct backentry *e,
+ Slapi_Filter *filter,
+ int managedsait,
+ int *lookup_returned_allidsp,
+ int *err
+)
+{
+ Slapi_Filter *fid2kids= NULL;
+ Slapi_Filter *focref= NULL;
+ Slapi_Filter *fand= NULL;
+ Slapi_Filter *forr= NULL;
+ Slapi_Filter *ftop= NULL;
+ IDList *candidates;
+
+ /*
+ * modify the filter to be something like this:
+ *
+ * (&(parentid=idofbase)(|(originalfilter)(objectclass=referral)))
+ */
+
+ ftop= create_onelevel_filter(filter, e, managedsait, &fid2kids, &focref, &fand, &forr);
+
+ /* from here, it's just like subtree_candidates */
+ candidates = filter_candidates( pb, be, base, ftop, NULL, 0, err );
+
+ *lookup_returned_allidsp = slapi_be_is_flag_set(be, SLAPI_BE_FLAG_DONT_BYPASS_FILTERTEST);
+
+ /* free up just the filter stuff we allocated above */
+ slapi_filter_free( fid2kids, 0 );
+ slapi_filter_free( fand, 0 );
+ slapi_filter_free( forr, 0 );
+ slapi_filter_free( focref, 0 );
+
+ return( candidates );
+}
+
+
+#define GRABSIZE2 50
+#define BUF_ALLOC_CAT( cpyfunc, s ) { \
+ int len = 2 * strlen( s ); \
+ while ( bmax - bcur < len + 1 ) { \
+ bmax += GRABSIZE2; \
+ buf = slapi_ch_realloc( buf, bmax ); \
+ } \
+ cpyfunc( buf + bcur, s ); \
+ bcur += strlen( buf + bcur ); \
+}
+
+/*
+ * We need to modify the filter to be something like this:
+ *
+ * (|(originalfilter)(objectclass=referral))
+ *
+ * the "objectclass=referral" part is used to select referrals to return.
+ * it is only included if the managedsait service control is not set.
+ *
+ * This function is exported for the VLV code to use.
+ */
+Slapi_Filter*
+create_subtree_filter(Slapi_Filter* filter, int managedsait, Slapi_Filter** focref, Slapi_Filter** forr)
+{
+ Slapi_Filter *ftop= filter;
+
+ if ( !managedsait )
+ {
+ ftop= create_referral_filter(filter, focref, forr);
+ }
+
+ return ftop;
+}
+
+
+static int
+nscpentrydn_check_filter(Slapi_Filter *f)
+{
+ if (!f || (f->f_choice != LDAP_FILTER_AND))
+ return 0; /* Not nscpEntryDN filter */
+
+ if ( 0 == strcasecmp ( f->f_and->f_avtype, SLAPI_ATTR_NSCP_ENTRYDN)) {
+ return 1; /* Contains a nscpEntryDN filter */
+ } else if ( 0 == strcasecmp ( f->f_and->f_next->f_avtype, SLAPI_ATTR_NSCP_ENTRYDN)) {
+ return 1;
+ }
+ return 0; /* Not nscpEntryDN filter */
+}
+
+
+/*
+ * Build a candidate list for a SUBTREE scope search.
+ */
+IDList *
+subtree_candidates(
+ Slapi_PBlock *pb,
+ backend *be,
+ const char *base,
+ const struct backentry *e,
+ Slapi_Filter *filter,
+ int managedsait,
+ int *allids_before_scopingp,
+ int *err
+)
+{
+ Slapi_Filter *focref= NULL;
+ Slapi_Filter *forr= NULL;
+ Slapi_Filter *ftop= NULL;
+ IDList *candidates;
+ PRBool has_tombstone_filter;
+ int isroot = 0;
+
+ /* make (|(originalfilter)(objectclass=referral)) */
+ ftop= create_subtree_filter(filter, managedsait, &focref, &forr);
+
+ /* Fetch a candidate list for the original filter */
+ candidates = filter_candidates( pb, be, base, ftop, NULL, 0, err );
+ slapi_filter_free( forr, 0 );
+ slapi_filter_free( focref, 0 );
+
+ /* set 'allids before scoping' flag */
+ if ( NULL != allids_before_scopingp ) {
+ *allids_before_scopingp = ( NULL != candidates && ALLIDS( candidates ));
+ }
+
+ has_tombstone_filter = (filter->f_flags & SLAPI_FILTER_TOMBSTONE);
+ slapi_pblock_get( pb, SLAPI_REQUESTOR_ISROOT, &isroot );
+
+ /*
+ * Apply the DN components if the candidate list is greater than
+ * our threshold, and if the filter is not "(objectclass=nstombstone)",
+ * since tombstone entries are not indexed in the ancestorid index.
+ */
+ if(candidates!=NULL && ( idl_length(candidates)>FILTER_TEST_THRESHOLD) && !has_tombstone_filter)
+ {
+ IDList *tmp = candidates, *descendants = NULL;
+
+ *err = ldbm_ancestorid_read(be, NULL, e->ep_id, &descendants);
+ idl_insert(&descendants, e->ep_id);
+ candidates = idl_intersection(be, candidates, descendants);
+ idl_free(tmp);
+ idl_free(descendants);
+ }
+ /*
+ * If the search is initiated by the Directory Manager,
+ * and the filter includes objectclass=nsTombstone,
+ * then we union the candidate list with all the tombstone
+ * entries in this backend instance.
+ */
+ if (has_tombstone_filter && isroot && !nscpentrydn_check_filter(filter))
+ {
+ IDList *idl;
+ IDList *tmp= candidates;
+ struct slapi_filter f = {0};
+ f.f_choice = LDAP_FILTER_EQUALITY;
+ f.f_avtype = "objectclass";
+ f.f_avvalue.bv_val = SLAPI_ATTR_VALUE_TOMBSTONE;
+ f.f_avvalue.bv_len = strlen(SLAPI_ATTR_VALUE_TOMBSTONE);
+ f.f_next= NULL;
+ idl = filter_candidates( pb, be, NULL, &f, NULL, 0, err );
+
+ /*
+ * If that gave allids then try (nscpentrydn=*) instead.
+ * The nscpentrydn equality index contains all the tombstones
+ * and can be used to resolve a presence filter without
+ * hitting allids.
+ */
+ if (idl && ALLIDS(idl)) {
+ idl_free(idl);
+ f.f_choice = LDAP_FILTER_PRESENT;
+ f.f_avtype = SLAPI_ATTR_NSCP_ENTRYDN;
+ idl = filter_candidates( pb, be, NULL, &f, NULL, 0, err );
+ }
+
+ candidates = idl_union( be, idl, tmp );
+ idl_free( idl );
+ idl_free( tmp );
+ }
+
+ return( candidates );
+}
+
+static int grok_filter(struct slapi_filter *f);
+#if 0
+/* Helper for grok_filter() */
+static int
+grok_filter_list(struct slapi_filter *flist)
+{
+ struct slapi_filter *f;
+
+ /* Scan the clauses of the AND filter, if any of them fails the grok, then we fail */
+ for ( f = flist; f != NULL; f = f->f_next ) {
+ if ( !grok_filter(f) ) {
+ return( 0 );
+ }
+ }
+ return( 1 );
+}
+#endif
+
+/* Helper function for can_skip_filter_test() */
+static int grok_filter(struct slapi_filter *f)
+{
+ switch ( f->f_choice ) {
+ case LDAP_FILTER_EQUALITY:
+ return 1; /* If there's an ID list and an equality filter, we can skip the filter test */
+ case LDAP_FILTER_SUBSTRINGS:
+ return 0;
+
+ case LDAP_FILTER_GE:
+ return 1;
+
+ case LDAP_FILTER_LE:
+ return 1;
+
+ case LDAP_FILTER_PRESENT:
+ return 1; /* If there's an ID list, and a presence filter, we can skip the filter test */
+
+ case LDAP_FILTER_APPROX:
+ return 0;
+
+ case LDAP_FILTER_EXTENDED:
+ return 0;
+
+ case LDAP_FILTER_AND:
+ return 0; /* Unless we check to see whether the presence and equality branches
+ of the search filter were all indexed, we get things wrong here,
+ so let's punt for now */
+ /* return grok_filter_list(f->f_and); AND clauses are potentially OK */
+
+ case LDAP_FILTER_OR:
+ return 0;
+
+ case LDAP_FILTER_NOT:
+ return 0;
+
+ default:
+ return 0;
+ }
+}
+
+/* Routine which says whether or not the indices produced a "correct" answer */
+static int
+can_skip_filter_test(
+ Slapi_PBlock *pb,
+ struct slapi_filter *f,
+ int scope,
+ IDList *idl
+)
+{
+ /* Is the ID list ALLIDS ? */
+ if ( ALLIDS(idl)) {
+ /* If so, then can't optimize */
+ return 0;
+ }
+
+ /* Is this a base scope search? */
+ if ( scope == LDAP_SCOPE_BASE ) {
+ /*
+ * If so, then we can't optimize. Why not? Because we only consult
+ * the entrydn index in producing our 1 candidate, and that means
+ * we have not used the filter to produce the candidate list.
+ */
+ return 0;
+ }
+
+ /* Grok the filter and tell me if it has only equality components in it */
+ return grok_filter(f);
+}
+
+
+
+/*
+ * Return the next entry in the result set. The entry is returned
+ * in the pblock.
+ * Returns 0 normally. If -1 is returned, it means that some
+ * exceptional condition, e.g. timelimit exceeded has occurred,
+ * and this routine has sent a result to the client. If zero
+ * is returned and no entry is available in the PBlock, then
+ * we've iterated through all the entries.
+ */
+int
+ldbm_back_next_search_entry( Slapi_PBlock *pb )
+{
+ return ldbm_back_next_search_entry_ext( pb, 0 );
+}
+
+int
+ldbm_back_next_search_entry_ext( Slapi_PBlock *pb, int use_extension )
+{
+ backend *be;
+ ldbm_instance *inst;
+ struct ldbminfo *li;
+ int scope;
+ int managedsait;
+ Slapi_Attr *attr;
+ Slapi_Filter *filter;
+ char *base;
+ back_search_result_set *sr;
+ ID id;
+ struct backentry *e;
+ int nentries;
+ time_t curtime, stoptime, optime;
+ int tlimit, llimit, slimit, isroot;
+ struct berval **urls = NULL;
+ int err;
+ Slapi_DN basesdn;
+ char *target_uniqueid;
+
+ slapi_pblock_get( pb, SLAPI_BACKEND, &be );
+ slapi_pblock_get( pb, SLAPI_PLUGIN_PRIVATE, &li );
+ slapi_pblock_get( pb, SLAPI_SEARCH_SCOPE, &scope );
+ slapi_pblock_get( pb, SLAPI_MANAGEDSAIT, &managedsait );
+ slapi_pblock_get( pb, SLAPI_SEARCH_FILTER, &filter );
+ slapi_pblock_get( pb, SLAPI_SEARCH_TARGET, &base );
+ slapi_pblock_get( pb, SLAPI_NENTRIES, &nentries );
+ slapi_pblock_get( pb, SLAPI_SEARCH_SIZELIMIT, &slimit );
+ slapi_pblock_get( pb, SLAPI_SEARCH_TIMELIMIT, &tlimit );
+ slapi_pblock_get( pb, SLAPI_OPINITIATED_TIME, &optime );
+ slapi_pblock_get( pb, SLAPI_REQUESTOR_ISROOT, &isroot );
+ slapi_pblock_get( pb, SLAPI_SEARCH_REFERRALS, &urls );
+ slapi_pblock_get( pb, SLAPI_SEARCH_RESULT_SET, &sr );
+ slapi_pblock_get( pb, SLAPI_TARGET_UNIQUEID, &target_uniqueid );
+
+ inst = (ldbm_instance *) be->be_instance_info;
+
+ slapi_sdn_init_dn_ndn_byref(&basesdn,base); /* normalized by front end */
+ /* Return to the cache the entry we handed out last time */
+ /* If we are using the extension, the front end will tell
+ * us when to do this so we don't do it now */
+ if ( !use_extension )
+ {
+ cache_return( &inst->inst_cache, &(sr->sr_entry) );
+ }
+
+ if(sr->sr_vlventry != NULL && !use_extension )
+ {
+ /* This empty entry was handed out last time because the ACL check failed on a VLV Search. */
+ /* The empty entry has a pointer to the cache entry dn... make sure we don't free the dn */
+ /* which belongs to the cache entry. */
+ slapi_entry_free( sr->sr_vlventry );
+ sr->sr_vlventry = NULL;
+ }
+
+ stoptime = optime + tlimit;
+ llimit = sr->sr_lookthroughlimit;
+
+ /* Find the next candidate entry and return it. */
+ while ( 1 )
+ {
+
+ /* check for abandon */
+ if ( slapi_op_abandoned( pb ))
+ {
+ delete_search_result_set( &sr );
+ if ( use_extension ) {
+ slapi_pblock_set( pb, SLAPI_SEARCH_RESULT_ENTRY_EXT, NULL );
+ }
+ slapi_pblock_set( pb, SLAPI_SEARCH_RESULT_ENTRY, NULL );
+ slapi_sdn_done(&basesdn);
+ return -1;
+ }
+
+ /* check time limit */
+ curtime = current_time();
+ if ( tlimit != -1 && curtime > stoptime )
+ {
+ slapi_send_ldap_result( pb, LDAP_TIMELIMIT_EXCEEDED, NULL, NULL, nentries, urls );
+ delete_search_result_set( &sr );
+ if ( use_extension ) {
+ slapi_pblock_set( pb, SLAPI_SEARCH_RESULT_ENTRY_EXT, NULL );
+ }
+ slapi_pblock_set( pb, SLAPI_SEARCH_RESULT_ENTRY, NULL );
+ slapi_sdn_done(&basesdn);
+ return -1;
+ }
+
+ /* check lookthrough limit */
+ if ( llimit != -1 && sr->sr_lookthroughcount >= llimit )
+ {
+ slapi_send_ldap_result( pb, LDAP_ADMINLIMIT_EXCEEDED, NULL, NULL, nentries, urls );
+ delete_search_result_set( &sr );
+ if ( use_extension ) {
+ slapi_pblock_set( pb, SLAPI_SEARCH_RESULT_ENTRY_EXT, NULL );
+ }
+ slapi_pblock_set( pb, SLAPI_SEARCH_RESULT_ENTRY, NULL );
+ slapi_sdn_done(&basesdn);
+ return -1;
+ }
+
+ /* get the entry */
+ id = idl_iterator_dereference_increment(&(sr->sr_current), sr->sr_candidates);
+ if ( id == NOID )
+ {
+ /* No more entries */
+ delete_search_result_set( &sr );
+ if ( use_extension ) {
+ slapi_pblock_set( pb, SLAPI_SEARCH_RESULT_ENTRY_EXT, NULL );
+ }
+ slapi_pblock_set( pb, SLAPI_SEARCH_RESULT_ENTRY, NULL );
+ slapi_sdn_done(&basesdn);
+ return 0;
+ }
+
+ ++sr->sr_lookthroughcount; /* checked above */
+
+ /* get the entry */
+ if ( (e = id2entry( be, id, NULL, &err )) == NULL )
+ {
+ if ( err != 0 && err != DB_NOTFOUND )
+ {
+ LDAPDebug( LDAP_DEBUG_ANY, "next_search_entry db err %d\n", err, 0, 0 );
+ if (LDBM_OS_ERR_IS_DISKFULL(err))
+ {
+ /* disk full in the middle of returning search results
+ * is gonna be traumatic. unavoidable.
+ */
+ slapi_send_ldap_result(pb, LDAP_OPERATIONS_ERROR, NULL, NULL, 0, NULL);
+ slapi_sdn_done(&basesdn);
+ return return_on_disk_full(li);
+ }
+ }
+ LDAPDebug( LDAP_DEBUG_ARGS, "candidate %lu not found\n", (u_long)id, 0, 0 );
+ if ( err == DB_NOTFOUND )
+ {
+ /* Since we didn't really look at this entry, we should
+ * decrement the lookthrough counter (it was just incremented).
+ * If we didn't do this, it would be possible to go over the
+ * lookthrough limit when there are fewer entries in the database
+ * than the lookthrough limit. This could happen on an ALLIDS
+ * search after adding a bunch of entries and then deleting
+ * them. */
+ --sr->sr_lookthroughcount;
+ }
+ continue;
+ }
+ e->ep_vlventry = NULL;
+ sr->sr_entry = e;
+
+ /*
+ * If it's a referral, return it without checking the
+ * filter explicitly here since it's only a candidate anyway. Do
+ * check the scope though.
+ */
+ if ( !managedsait && slapi_entry_attr_find( e->ep_entry, "ref", &attr ) == 0)
+ {
+ Slapi_Value **refs= attr_get_present_values(attr);
+ if ( refs == NULL || refs[0] == NULL )
+ {
+ char ebuf[ BUFSIZ ];
+ LDAPDebug( LDAP_DEBUG_ANY, "null ref in (%s)\n", escape_string( backentry_get_ndn(e), ebuf ), 0, 0 );
+ }
+ else if ( slapi_sdn_scope_test( backentry_get_sdn(e), &basesdn, scope ))
+ {
+ if ( use_extension ) {
+ slapi_pblock_set( pb, SLAPI_SEARCH_RESULT_ENTRY_EXT, e );
+ }
+ slapi_pblock_set( pb, SLAPI_SEARCH_RESULT_ENTRY, e->ep_entry );
+ slapi_sdn_done(&basesdn);
+ return 0;
+ }
+ }
+ else
+ {
+ /*
+ * As per slapi_filter_test:
+ * 0 filter matched
+ * -1 filter did not match
+ * >0 an ldap error code
+ */
+ int filter_test = -1;
+
+ if((slapi_entry_flag_is_set(e->ep_entry,SLAPI_ENTRY_LDAPSUBENTRY)
+ && !filter_flag_is_set(filter,SLAPI_FILTER_LDAPSUBENTRY)) ||
+ (slapi_entry_flag_is_set(e->ep_entry,SLAPI_ENTRY_FLAG_TOMBSTONE)
+ && (!isroot || !filter_flag_is_set(filter, SLAPI_FILTER_TOMBSTONE))))
+ {
+ /* If the entry is an LDAP subentry and filter don't filter subentries OR
+ * the entry is a TombStone and filter don't filter Tombstone
+ * don't return the entry
+ */
+ /* ugaston - we don't want to mistake this filter failure with the one below due to ACL,
+ * because whereas the former should be read as 'no entry must be returned', the latter
+ * might still lead to return an empty entry. */
+ filter_test=-1;
+ }
+ else
+ {
+ /* it's a regular entry, check if it matches the filter, and passes the ACL check */
+ if ( 0 != ( sr->sr_flags & SR_FLAG_CAN_SKIP_FILTER_TEST )) {
+ /* Since we do access control checking in the filter test (?Why?) we need to check access now */
+ LDAPDebug( LDAP_DEBUG_FILTER, "Bypassing filter test\n", 0, 0, 0 );
+ if ( ACL_CHECK_FLAG ) {
+ filter_test = slapi_vattr_filter_test_ext( pb, e->ep_entry, filter, ACL_CHECK_FLAG, 1 /* Only perform access checking, thank you */);
+ } else {
+ filter_test = 0;
+ }
+ if (li->li_filter_bypass_check) {
+ int ft_rc;
+
+ LDAPDebug( LDAP_DEBUG_FILTER, "Checking bypass\n", 0, 0, 0 );
+ ft_rc = slapi_vattr_filter_test( pb, e->ep_entry, filter,
+ ACL_CHECK_FLAG );
+ if (filter_test != ft_rc) {
+ /* Oops ! This means that we thought we could bypass the filter test, but noooo... */
+ char ebuf[ BUFSIZ ];
+ LDAPDebug( LDAP_DEBUG_ANY, "Filter bypass ERROR on entry %s\n", escape_string( backentry_get_ndn(e), ebuf ), 0, 0 );
+ filter_test = ft_rc; /* Fix the error */
+ }
+ }
+ } else {
+ /* Old-style case---we need to do a filter test */
+ filter_test = slapi_vattr_filter_test( pb, e->ep_entry, filter, ACL_CHECK_FLAG);
+ }
+ }
+ if ( (filter_test == 0) || (sr->sr_virtuallistview && (filter_test != -1)) )
+ /* ugaston - if filter failed due to subentries or tombstones (filter_test=-1),
+ * just forget about it, since we don't want to return anything at all. */
+ {
+ if ( slapi_uniqueIDCompareString(target_uniqueid, e->ep_entry->e_uniqueid) ||
+ slapi_sdn_scope_test( backentry_get_sdn(e), &basesdn, scope ))
+ {
+ /* check size limit */
+ if ( slimit >= 0 )
+ {
+ if ( --slimit < 0 ) {
+ cache_return( &inst->inst_cache, &e );
+ delete_search_result_set( &sr );
+ slapi_send_ldap_result( pb, LDAP_SIZELIMIT_EXCEEDED, NULL, NULL, nentries, urls );
+ slapi_sdn_done(&basesdn);
+ return -1;
+ }
+ slapi_pblock_set( pb, SLAPI_SEARCH_SIZELIMIT, &slimit );
+ }
+ if ( (filter_test != 0) && sr->sr_virtuallistview)
+ {
+ /* Slapi Filter Test failed.
+ * Must be that the ACL check failed.
+ * Send back an empty entry.
+ */
+ sr->sr_vlventry = slapi_entry_alloc();
+ slapi_entry_init(sr->sr_vlventry,slapi_ch_strdup(slapi_entry_get_dn_const(e->ep_entry)),NULL);
+ e->ep_vlventry = sr->sr_vlventry;
+ if ( use_extension ) {
+ slapi_pblock_set( pb, SLAPI_SEARCH_RESULT_ENTRY_EXT, e );
+ }
+ slapi_pblock_set( pb, SLAPI_SEARCH_RESULT_ENTRY, sr->sr_vlventry );
+ } else {
+ if ( use_extension ) {
+ slapi_pblock_set( pb, SLAPI_SEARCH_RESULT_ENTRY_EXT, e );
+ }
+ slapi_pblock_set( pb, SLAPI_SEARCH_RESULT_ENTRY, e->ep_entry );
+ }
+ slapi_sdn_done(&basesdn);
+ return 0;
+ }
+ else
+ {
+ cache_return ( &inst->inst_cache, &(sr->sr_entry) );
+ }
+ }
+ else
+ {
+ /* Failed the filter test, and this isn't a VLV Search */
+ cache_return( &inst->inst_cache, &(sr->sr_entry) );
+ }
+ }
+ }
+ /*NOTREACHED*/
+ slapi_sdn_done(&basesdn);
+}
+
+
+static back_search_result_set*
+new_search_result_set(IDList *idl, int vlv, int lookthroughlimit)
+{
+ back_search_result_set *p= (back_search_result_set *)slapi_ch_malloc( sizeof( back_search_result_set ));
+ p->sr_candidates = idl;
+ p->sr_current = idl_iterator_init(idl);
+ p->sr_entry = NULL;
+ p->sr_lookthroughcount = 0;
+ p->sr_lookthroughlimit = lookthroughlimit;
+ p->sr_virtuallistview= vlv;
+ p->sr_vlventry = NULL;
+ p->sr_flags = 0;
+ return p;
+}
+
+static void
+delete_search_result_set( back_search_result_set **sr )
+{
+ if ( NULL == sr || NULL == *sr)
+ {
+ return;
+ }
+ if ( NULL != (*sr)->sr_candidates )
+ {
+ idl_free( (*sr)->sr_candidates );
+ }
+ slapi_ch_free( (void**)sr );
+}
+
+
+int
+ldbm_back_entry_release( Slapi_PBlock *pb, void *backend_info_ptr ) {
+ backend *be;
+ ldbm_instance *inst;
+
+ if ( backend_info_ptr == NULL )
+ return 1;
+
+ slapi_pblock_get( pb, SLAPI_BACKEND, &be );
+ inst = (ldbm_instance *) be->be_instance_info;
+
+ cache_return( &inst->inst_cache, (struct backentry **)&backend_info_ptr );
+
+ if( ((struct backentry *) backend_info_ptr)->ep_vlventry != NULL )
+ {
+ /* This entry was created during a vlv search whose acl check failed. It needs to be
+ * freed here */
+ slapi_entry_free( ((struct backentry *) backend_info_ptr)->ep_vlventry );
+ ((struct backentry *) backend_info_ptr)->ep_vlventry = NULL;
+ }
+ return 0;
+}
diff --git a/ldap/servers/slapd/back-ldbm/ldbm_unbind.c b/ldap/servers/slapd/back-ldbm/ldbm_unbind.c
new file mode 100644
index 00000000..9d3e80fa
--- /dev/null
+++ b/ldap/servers/slapd/back-ldbm/ldbm_unbind.c
@@ -0,0 +1,14 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+/* unbind.c - handle an ldap unbind operation */
+
+#include "back-ldbm.h"
+
+int
+ldbm_back_unbind( Slapi_PBlock *pb )
+{
+ return( 0 );
+}
diff --git a/ldap/servers/slapd/back-ldbm/ldif2ldbm.c b/ldap/servers/slapd/back-ldbm/ldif2ldbm.c
new file mode 100644
index 00000000..1930cb51
--- /dev/null
+++ b/ldap/servers/slapd/back-ldbm/ldif2ldbm.c
@@ -0,0 +1,2440 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2004 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+/* ldif2ldbm.c
+ *
+ * common functions for import (old and new) and export
+ * the export code (db2ldif)
+ * code for db2index (is this still in use?)
+ */
+
+#include "back-ldbm.h"
+#include "vlv_srch.h"
+#include "dblayer.h"
+#include "import.h"
+
+static char *sourcefile = "ldif2ldbm.c";
+
+
+static int db2index_add_indexed_attr(backend *be, char *attrString);
+
+static int ldbm_exclude_attr_from_export( struct ldbminfo *li,
+ const char *attr, int dump_uniqueid );
+
+
+/********** common routines for classic/deluxe import code **********/
+
+static size_t import_config_index_buffer_size = DEFAULT_IMPORT_INDEX_BUFFER_SIZE;
+
+void import_configure_index_buffer_size(size_t size)
+{
+ import_config_index_buffer_size = size;
+}
+
+size_t import_get_index_buffer_size() {
+ return import_config_index_buffer_size;
+}
+
+static PRIntn import_subcount_hash_compare_keys(const void *v1, const void *v2)
+{
+ return( ((ID)v1 == (ID)v2 ) ? 1 : 0);
+}
+
+static PRIntn import_subcount_hash_compare_values(const void *v1, const void *v2)
+{
+ return( ((size_t)v1 == (size_t)v2 ) ? 1 : 0);
+}
+
+static PLHashNumber import_subcount_hash_fn(const void *id)
+{
+ return (PLHashNumber) id;
+}
+
+void import_subcount_stuff_init(import_subcount_stuff *stuff)
+{
+ stuff->hashtable = PL_NewHashTable(IMPORT_SUBCOUNT_HASHTABLE_SIZE,
+ import_subcount_hash_fn, import_subcount_hash_compare_keys,
+ import_subcount_hash_compare_values, NULL, NULL);
+}
+
+void import_subcount_stuff_term(import_subcount_stuff *stuff)
+{
+ if ( stuff != NULL && stuff->hashtable != NULL ) {
+ PL_HashTableDestroy(stuff->hashtable);
+ }
+}
+
+/* fetch include/exclude DNs from the pblock and normalize them --
+ * returns true if there are any include/exclude DNs
+ * [used by both ldif2db and db2ldif]
+ */
+int ldbm_back_fetch_incl_excl(Slapi_PBlock *pb, char ***include,
+ char ***exclude)
+{
+ char **pb_incl, **pb_excl;
+ char subtreeDn[BUFSIZ];
+ char *normSubtreeDn;
+ int i;
+
+ slapi_pblock_get(pb, SLAPI_LDIF2DB_INCLUDE, &pb_incl);
+ slapi_pblock_get(pb, SLAPI_LDIF2DB_EXCLUDE, &pb_excl);
+ *include = *exclude = NULL;
+
+ /* normalize */
+ if (pb_excl) {
+ for (i = 0; pb_excl[i]; i++) {
+ strcpy(subtreeDn, pb_excl[i]);
+ normSubtreeDn = slapi_dn_normalize_case(subtreeDn);
+ charray_add(exclude, slapi_ch_strdup(normSubtreeDn));
+ }
+ }
+ if (pb_incl) {
+ for (i = 0; pb_incl[i]; i++) {
+ strcpy(subtreeDn, pb_incl[i]);
+ normSubtreeDn = slapi_dn_normalize_case(subtreeDn);
+ charray_add(include, slapi_ch_strdup(normSubtreeDn));
+ }
+ }
+ return (pb_incl || pb_excl);
+}
+
+void ldbm_back_free_incl_excl(char **include, char **exclude)
+{
+ if (include) {
+ charray_free(include);
+ }
+ if (exclude) {
+ charray_free(exclude);
+ }
+}
+
+/* check if a DN is in the include list but NOT the exclude list
+ * [used by both ldif2db and db2ldif]
+ */
+int ldbm_back_ok_to_dump(const char *dn, char **include, char **exclude)
+{
+ int i = 0;
+
+ if (!(include || exclude))
+ return(1);
+
+ if (exclude) {
+ i = 0;
+ while (exclude[i]) {
+ if (slapi_dn_issuffix(dn,exclude[i]))
+ return(0);
+ i++;
+ }
+ }
+
+ if (include) {
+ i = 0;
+ while (include[i]) {
+ if (slapi_dn_issuffix(dn,include[i]))
+ return(1);
+ i++;
+ }
+ /* not in include... bye. */
+ return(0);
+ }
+
+ return(1);
+}
+
+
+/*
+ * add_op_attrs - add the parentid, entryid, dncomp,
+ * and entrydn operational attributes to an entry.
+ * Also---new improved washes whiter than white version
+ * now removes any bogus operational attributes you're not
+ * allowed to specify yourself on entries.
+ * Currenty the list of these is: numSubordinates, hasSubordinates
+ */
+int add_op_attrs(Slapi_PBlock *pb, struct ldbminfo *li, struct backentry *ep,
+ int *status)
+{
+ backend *be;
+ const char *pdn;
+ ID pid = 0;
+
+ slapi_pblock_get(pb, SLAPI_BACKEND, &be);
+
+ /*
+ * add the parentid and entryid operational attributes
+ */
+
+ if (NULL != status) {
+ *status = IMPORT_ADD_OP_ATTRS_OK;
+ }
+
+ /* parentid */
+ if ( (pdn = slapi_dn_parent( backentry_get_ndn(ep))) != NULL ) {
+ struct berval bv;
+ IDList *idl;
+ int err = 0;
+
+ /*
+ * read the entrydn index to get the id of the parent
+ * If this entry's parent is not present in the index,
+ * we'll get a DB_NOTFOUND error here.
+ * In olden times, we just ignored this, but now...
+ * we see this as meaning that the entry is either a
+ * suffix entry, or its erroneous. So, we signal this to the
+ * caller via the status parameter.
+ */
+ bv.bv_val = (char *)pdn;
+ bv.bv_len = strlen(pdn);
+ if ( (idl = index_read( be, "entrydn", indextype_EQUALITY, &bv, NULL,
+ &err )) != NULL ) {
+ pid = idl_firstid( idl );
+ idl_free( idl );
+ } else if ( 0 != err ) {
+ if (DB_NOTFOUND != err ) {
+ LDAPDebug( LDAP_DEBUG_ANY, "database error %d\n", err, 0, 0 );
+ slapi_ch_free( (void**)&pdn );
+ return( -1 );
+ } else {
+ if (NULL != status) {
+ *status = IMPORT_ADD_OP_ATTRS_NO_PARENT;
+ }
+ }
+ }
+ slapi_ch_free( (void**)&pdn );
+ } else {
+ if (NULL != status) {
+ *status = IMPORT_ADD_OP_ATTRS_NO_PARENT;
+ }
+ }
+
+ /* Get rid of attributes you're not allowed to specify yourself */
+ slapi_entry_delete_values( ep->ep_entry, hassubordinates, NULL );
+ slapi_entry_delete_values( ep->ep_entry, numsubordinates, NULL );
+
+ /* Add the entryid, parentid and entrydn operational attributes */
+ /* Note: This function is provided by the Add code */
+ add_update_entry_operational_attributes(ep, pid);
+
+ return( 0 );
+}
+
+/********** functions for maintaining the subordinate count **********/
+
+/* Update subordinate count in a hint list, given the parent's ID */
+int import_subcount_mother_init(import_subcount_stuff *mothers, ID parent_id,
+ size_t count)
+{
+ PR_ASSERT(NULL == PL_HashTableLookup(mothers->hashtable,(void*)parent_id));
+ PL_HashTableAdd(mothers->hashtable,(void*)parent_id,(void*)count);
+ return 0;
+}
+
+/* Look for a subordinate count in a hint list, given the parent's ID */
+static int import_subcount_mothers_lookup(import_subcount_stuff *mothers,
+ ID parent_id, size_t *count)
+{
+ size_t stored_count = 0;
+
+ *count = 0;
+ /* Lookup hash table for ID */
+ stored_count = (size_t)PL_HashTableLookup(mothers->hashtable,
+ (void*)parent_id);
+ /* If present, return the count found */
+ if (0 != stored_count) {
+ *count = stored_count;
+ return 0;
+ }
+ return -1;
+}
+
+/* Update subordinate count in a hint list, given the parent's ID */
+int import_subcount_mother_count(import_subcount_stuff *mothers, ID parent_id)
+{
+ size_t stored_count = 0;
+
+ /* Lookup the hash table for the target ID */
+ stored_count = (size_t)PL_HashTableLookup(mothers->hashtable,
+ (void*)parent_id);
+ PR_ASSERT(0 != stored_count);
+ /* Increment the count */
+ stored_count++;
+ PL_HashTableAdd(mothers->hashtable, (void*)parent_id, (void*)stored_count);
+ return 0;
+}
+
+static int import_update_entry_subcount(backend *be, ID parentid,
+ size_t sub_count)
+{
+ ldbm_instance *inst = (ldbm_instance *) be->be_instance_info;
+ int ret = 0;
+ modify_context mc = {0};
+ char value_buffer[20]; /* enough digits for 2^64 children */
+ struct backentry *e = NULL;
+ int isreplace = 0;
+
+ /* Get hold of the parent */
+ e = id2entry(be,parentid,NULL,&ret);
+ if ( (NULL == e) || (0 != ret)) {
+ ldbm_nasty(sourcefile,5,ret);
+ return (0 == ret) ? -1 : ret;
+ }
+ /* Lock it (not really required since we're single-threaded here, but
+ * let's do it so we can reuse the modify routines) */
+ cache_lock_entry( &inst->inst_cache, e );
+ modify_init(&mc,e);
+ sprintf(value_buffer,"%lu",sub_count);
+ /* attr numsubordinates could already exist in the entry,
+ let's check whether it's already there or not */
+ isreplace = (attrlist_find(e->ep_entry->e_attrs, numsubordinates) != NULL);
+ {
+ int op = isreplace ? LDAP_MOD_REPLACE : LDAP_MOD_ADD;
+ Slapi_Mods *smods= slapi_mods_new();
+
+ slapi_mods_add(smods, op | LDAP_MOD_BVALUES, numsubordinates,
+ strlen(value_buffer), value_buffer);
+ ret = modify_apply_mods(&mc,smods); /* smods passed in */
+ }
+ if (0 == ret || LDAP_TYPE_OR_VALUE_EXISTS == ret) {
+ /* This will correctly index subordinatecount: */
+ ret = modify_update_all(be,NULL,&mc,NULL);
+ if (0 == ret) {
+ modify_switch_entries( &mc,be);
+ }
+ }
+ modify_term(&mc,be);
+ return ret;
+}
+
+struct _import_subcount_trawl_info {
+ struct _import_subcount_trawl_info *next;
+ ID id;
+ size_t sub_count;
+};
+typedef struct _import_subcount_trawl_info import_subcount_trawl_info;
+
+static void import_subcount_trawl_add(import_subcount_trawl_info **list, ID id)
+{
+ import_subcount_trawl_info *new_info = CALLOC(import_subcount_trawl_info);
+
+ new_info->next = *list;
+ new_info->id = id;
+ *list = new_info;
+}
+
+static int import_subcount_trawl(backend *be, import_subcount_trawl_info *trawl_list)
+{
+ ldbm_instance *inst = (ldbm_instance *) be->be_instance_info;
+ ID id = 1;
+ int ret = 0;
+ import_subcount_trawl_info *current = NULL;
+ char value_buffer[20]; /* enough digits for 2^64 children */
+
+ /* OK, we do */
+ /* We open id2entry and iterate through it */
+ /* Foreach entry, we check to see if its parentID matches any of the
+ * values in the trawl list . If so, we bump the sub count for that
+ * parent in the list.
+ */
+ while (1) {
+ struct backentry *e = NULL;
+
+ /* Get the next entry */
+ e = id2entry(be,id,NULL,&ret);
+ if ( (NULL == e) || (0 != ret)) {
+ if (DB_NOTFOUND == ret) {
+ break;
+ } else {
+ ldbm_nasty(sourcefile,8,ret);
+ return ret;
+ }
+ }
+ for (current = trawl_list; current != NULL; current = current->next) {
+ sprintf(value_buffer,"%lu",(u_long)current->id);
+ if (slapi_entry_attr_hasvalue(e->ep_entry,"parentid",value_buffer)) {
+ /* If this entry's parent ID matches one we're trawling for,
+ * bump its count */
+ current->sub_count++;
+ }
+ }
+ /* Free the entry */
+ cache_remove(&inst->inst_cache, e);
+ cache_return(&inst->inst_cache, &e);
+ id++;
+ }
+ /* Now update the parent entries from the list */
+ for (current = trawl_list; current != NULL; current = current->next) {
+ /* Update the parent entry with the correctly counted subcount */
+ ret = import_update_entry_subcount(be,current->id,current->sub_count);
+ if (0 != ret) {
+ ldbm_nasty(sourcefile,10,ret);
+ break;
+ }
+ }
+ return ret;
+}
+
+/*
+ * Function: update_subordinatecounts
+ *
+ * Returns: Nothing
+ *
+ */
+int update_subordinatecounts(backend *be, import_subcount_stuff *mothers,
+ DB_TXN *txn)
+{
+ int ret = 0;
+ DB *db = NULL;
+ DBC *dbc = NULL;
+ struct attrinfo *ai = NULL;
+ DBT key = {0};
+ DBT data = {0};
+ import_subcount_trawl_info *trawl_list = NULL;
+
+ /* Open the parentid index */
+ ainfo_get( be, "parentid", &ai );
+
+ /* Open the parentid index file */
+ if ( (ret = dblayer_get_index_file( be, ai, &db, DBOPEN_CREATE )) != 0 ) {
+ ldbm_nasty(sourcefile,67,ret);
+ return(ret);
+ }
+
+ /* Get a cursor so we can walk through the parentid */
+ ret = db->cursor(db,txn,&dbc,0);
+ if (ret != 0 ) {
+ ldbm_nasty(sourcefile,68,ret);
+ dblayer_release_index_file( be, ai, db );
+ return ret;
+ }
+
+ /* Walk along the index */
+ while (1) {
+ size_t sub_count = 0;
+ int found_count = 1;
+ ID parentid = 0;
+
+ /* Foreach key which is an equality key : */
+ data.flags = DB_DBT_MALLOC;
+ key.flags = DB_DBT_MALLOC;
+ ret = dbc->c_get(dbc,&key,&data,DB_NEXT_NODUP);
+ if (NULL != data.data) {
+ free(data.data);
+ data.data = NULL;
+ }
+ if (0 != ret) {
+ if (ret != DB_NOTFOUND) {
+ ldbm_nasty(sourcefile,62,ret);
+ }
+ if (NULL != key.data) {
+ free(key.data);
+ key.data = NULL;
+ }
+ break;
+ }
+ if (*(char*)key.data == EQ_PREFIX) {
+ char *idptr = NULL;
+
+ /* construct the parent's ID from the key */
+ /* Look for the ID in the hint list supplied by the caller */
+ /* If its there, we know the answer already */
+ idptr = (((char *) key.data) + 1);
+ parentid = (ID) atol(idptr);
+ PR_ASSERT(0 != parentid);
+ ret = import_subcount_mothers_lookup(mothers,parentid,&sub_count);
+ if (0 != ret) {
+ IDList *idl = NULL;
+
+ /* If it's not, we need to compute it ourselves: */
+ /* Load the IDL matching the key */
+ key.flags = DB_DBT_REALLOC;
+ ret = NEW_IDL_NO_ALLID;
+ idl = idl_fetch(be,db,&key,NULL,NULL,&ret);
+ if ( (NULL == idl) || (0 != ret)) {
+ ldbm_nasty(sourcefile,4,ret);
+ dblayer_release_index_file( be, ai, db );
+ return (0 == ret) ? -1 : ret;
+ }
+ /* The number of IDs in the IDL tells us the number of
+ * subordinates for the entry */
+ /* Except, the number might be above the allidsthreshold,
+ * in which case */
+ if (ALLIDS(idl)) {
+ /* We add this ID to the list for which to trawl */
+ import_subcount_trawl_add(&trawl_list,parentid);
+ found_count = 0;
+ } else {
+ /* We get the count from the IDL */
+ sub_count = idl->b_nids;
+ }
+ idl_free(idl);
+ }
+ /* Did we get the count ? */
+ if (found_count) {
+ PR_ASSERT(0 != sub_count);
+ /* If so, update the parent now */
+ import_update_entry_subcount(be,parentid,sub_count);
+ }
+ }
+ if (NULL != key.data) {
+ free(key.data);
+ key.data = NULL;
+ }
+ }
+
+ ret = dbc->c_close(dbc);
+ if (0 != ret) {
+ ldbm_nasty(sourcefile,6,ret);
+ }
+ dblayer_release_index_file( be, ai, db );
+
+ /* Now see if we need to go trawling through id2entry for the info
+ * we need */
+ if (NULL != trawl_list) {
+ ret = import_subcount_trawl(be,trawl_list);
+ if (0 != ret) {
+ ldbm_nasty(sourcefile,7,ret);
+ }
+ }
+ return(ret);
+}
+
+
+/********** ldif2db entry point **********/
+
+/*
+ Some notes about this stuff:
+
+ The front-end does call our init routine before calling us here.
+ So, we get the regular chance to parse the config file etc.
+ However, it does _NOT_ call our start routine, so we need to
+ do whatever work that did and which we need for this work , here.
+ Furthermore, the front-end simply exits after calling us, so we need
+ to do any cleanup work here also.
+ */
+
+/*
+ * ldbm_back_ldif2ldbm - backend routine to convert an ldif file to
+ * a database.
+ */
+int ldbm_back_ldif2ldbm( Slapi_PBlock *pb )
+{
+ struct ldbminfo *li;
+ ldbm_instance *inst = NULL;
+ char *instance_name;
+ int ret, task_flags;
+
+ slapi_pblock_get( pb, SLAPI_PLUGIN_PRIVATE, &li );
+ slapi_pblock_get( pb, SLAPI_BACKEND_INSTANCE_NAME, &instance_name );
+
+ /* BEGIN complex dependencies of various initializations. */
+ /* hopefully this will go away once import is not run standalone... */
+
+ slapi_pblock_get(pb, SLAPI_TASK_FLAGS, &task_flags);
+ if (task_flags & TASK_RUNNING_FROM_COMMANDLINE) {
+ li->li_flags |= TASK_RUNNING_FROM_COMMANDLINE;
+ ldbm_config_load_dse_info(li);
+ autosize_import_cache(li);
+ }
+
+ /* Find the instance that the ldif2db will be done on. */
+ inst = ldbm_instance_find_by_name(li, instance_name);
+ if (NULL == inst) {
+ LDAPDebug(LDAP_DEBUG_ANY, "Unknown ldbm instance %s\n", instance_name,
+ 0, 0);
+ return -1;
+ }
+
+ /* check if an import/restore is already ongoing... */
+ if (instance_set_busy(inst) != 0) {
+ LDAPDebug(LDAP_DEBUG_ANY, "ldbm: '%s' is already in the middle of "
+ "another task and cannot be disturbed.\n",
+ inst->inst_name, 0, 0);
+ return -1;
+ }
+
+ /***** prepare & init libdb and dblayer *****/
+
+ if (! (task_flags & TASK_RUNNING_FROM_COMMANDLINE)) {
+ /* shutdown this instance of the db */
+ LDAPDebug(LDAP_DEBUG_ANY, "Bringing %s offline...\n",
+ instance_name, 0, 0);
+ slapi_mtn_be_disable(inst->inst_be);
+
+ cache_clear(&inst->inst_cache);
+ dblayer_instance_close(inst->inst_be);
+ dblayer_delete_indices(inst);
+ } else {
+ /* from the command line, libdb needs to be started up */
+ ldbm_config_internal_set(li, CONFIG_DB_TRANSACTION_LOGGING, "off");
+
+ if (0 != (ret = dblayer_start(li, DBLAYER_IMPORT_MODE)) ) {
+ if (LDBM_OS_ERR_IS_DISKFULL(ret)) {
+ LDAPDebug(LDAP_DEBUG_ANY, "ERROR: Failed to init database. "
+ "There is either insufficient disk space or "
+ "insufficient memory available to initialize the "
+ "database.\n", 0, 0, 0);
+ LDAPDebug(LDAP_DEBUG_ANY,"Please check that\n"
+ "1) disks are not full,\n"
+ "2) no file exceeds the file size limit,\n"
+ "3) the configured dbcachesize is not too large for the available memory on this machine.\n",
+ 0, 0, 0);
+ } else {
+ LDAPDebug(LDAP_DEBUG_ANY, "ERROR: Failed to init database "
+ "(error %d: %s)\n", ret, dblayer_strerror(ret), 0);
+ }
+ goto fail;
+ }
+ }
+
+ /* Delete old database files */
+ dblayer_delete_instance_dir(inst->inst_be);
+ /* it's okay to fail -- the directory might have already been deleted */
+
+ /* dblayer_instance_start will init the id2entry index. */
+ /* it also (finally) fills in inst_dir_name */
+ ret = dblayer_instance_start(inst->inst_be, DBLAYER_IMPORT_MODE);
+ if (ret != 0) {
+ goto fail;
+ }
+
+ vlv_init(inst);
+
+ /***** done init libdb and dblayer *****/
+
+ /* always use "new" import code now */
+ slapi_pblock_set(pb, SLAPI_BACKEND, inst->inst_be);
+ return ldbm_back_ldif2ldbm_deluxe(pb);
+
+fail:
+ /* DON'T enable the backend -- leave it offline */
+ instance_set_not_busy(inst);
+ return ret;
+}
+
+
+/********** db2ldif, db2index **********/
+
+
+/* fetch an IDL for the series of subtree specs */
+/* (used for db2ldif) */
+static IDList *ldbm_fetch_subtrees(backend *be, char **include, int *err)
+{
+ int i;
+ ID id;
+ IDList *idltotal = NULL, *idltmp;
+ back_txn *txn = NULL;
+ struct berval bv;
+
+ /* for each subtree spec... */
+ for (i = 0; include[i]; i++) {
+ IDList *idl = NULL;
+
+ /*
+ * First map the suffix to its entry ID.
+ * Note that the suffix is already normalized.
+ */
+ bv.bv_val = include[i];
+ bv.bv_len = strlen(include[i]);
+ idl = index_read(be, "entrydn", indextype_EQUALITY, &bv, txn, err);
+ if (idl == NULL) {
+ LDAPDebug(LDAP_DEBUG_ANY, "warning: entrydn not indexed on '%s'\n",
+ include[i], 0, 0);
+ continue;
+ }
+ id = idl_firstid(idl);
+ idl_free(idl);
+ idl = NULL;
+
+ /*
+ * Now get all the descendants of that suffix.
+ */
+ *err = ldbm_ancestorid_read(be, txn, id, &idl);
+ if (idl == NULL) {
+ LDAPDebug(LDAP_DEBUG_ANY, "warning: ancestorid not indexed on %lu\n",
+ id, 0, 0);
+ continue;
+ }
+
+ /* Insert the suffix itself */
+ idl_insert(&idl, id);
+
+ /* Merge the idlists */
+ if (! idltotal) {
+ idltotal = idl;
+ } else if (idl) {
+ idltmp = idl_union(be, idltotal, idl);
+ idl_free(idltotal);
+ idl_free(idl);
+ idltotal = idltmp;
+ }
+ }
+
+ return idltotal;
+}
+
+#define FD_STDOUT 1
+
+
+/*
+ * ldbm_back_ldbm2ldif - backend routine to convert database to an
+ * ldif file.
+ * (reunified at last)
+ */
+int
+ldbm_back_ldbm2ldif( Slapi_PBlock *pb )
+{
+ backend *be;
+ struct ldbminfo *li = NULL;
+ DB *db = NULL;
+ DBC *dbc = NULL;
+ struct backentry *ep;
+ DBT key = {0};
+ DBT data = {0};
+ char *type, *fname = NULL;
+ int len, printkey, rc, ok_index;
+ int return_value = 0;
+ int nowrap = 0;
+ int nobase64 = 0;
+ NIDS idindex = 0;
+ ID temp_id;
+ char **exclude_suffix = NULL;
+ char **include_suffix = NULL;
+ int decrypt = 0;
+ int dump_replica = 0;
+ int dump_uniqueid = 1;
+ int fd;
+ IDList *idl = NULL; /* optimization for -s include lists */
+ int cnt = 0, lastcnt = 0;
+ int options = 0;
+ int keepgoing = 1;
+ int isfirst = 1;
+ int appendmode = 0;
+ int appendmode_1 = 0;
+ int noversion = 0;
+ ID lastid;
+ int task_flags;
+ Slapi_Task *task;
+ int run_from_cmdline = 0;
+ char *instance_name;
+ ldbm_instance *inst;
+ int str2entry_options= 0;
+ int retry;
+ int we_start_the_backends = 0;
+ int server_running;
+
+ LDAPDebug( LDAP_DEBUG_TRACE, "=> ldbm_back_ldbm2ldif\n", 0, 0, 0 );
+
+ slapi_pblock_get( pb, SLAPI_PLUGIN_PRIVATE, &li );
+ slapi_pblock_get( pb, SLAPI_TASK_FLAGS, &task_flags );
+ slapi_pblock_get( pb, SLAPI_DB2LDIF_DECRYPT, &decrypt );
+ slapi_pblock_get( pb, SLAPI_DB2LDIF_SERVER_RUNNING, &server_running );
+ run_from_cmdline = (task_flags & TASK_RUNNING_FROM_COMMANDLINE);
+
+ dump_replica = pb->pb_ldif_dump_replica;
+ if (run_from_cmdline) {
+ li->li_flags |= TASK_RUNNING_FROM_COMMANDLINE;
+ if (!dump_replica) {
+ we_start_the_backends = 1;
+ }
+ }
+
+ if (we_start_the_backends) {
+ /* No ldbm be's exist until we process the config information. */
+
+ /*
+ * Note that we should only call this once. If we're
+ * dumping several backends then it gets called multiple
+ * times and we get warnings in the error log like this:
+ * WARNING: ldbm instance NetscapeRoot already exists
+ */
+ ldbm_config_load_dse_info(li);
+ }
+
+ if (run_from_cmdline && li->li_dblayer_private->dblayer_private_mem
+ && server_running)
+ {
+ LDAPDebug(LDAP_DEBUG_ANY,
+ "Cannot export the database while the server is running and "
+ "nsslapd-db-private-mem option is used, "
+ "please use ldif2db.pl\n", 0, 0, 0);
+ return_value = -1;
+ goto bye;
+ }
+
+ if (run_from_cmdline) {
+
+ /* Now that we have processed the config information, we look for
+ * the be that should do the db2ldif. */
+ slapi_pblock_get(pb, SLAPI_BACKEND_INSTANCE_NAME, &instance_name);
+ inst = ldbm_instance_find_by_name(li, instance_name);
+ if (NULL == inst) {
+ LDAPDebug(LDAP_DEBUG_ANY, "Unknown ldbm instance %s\n",
+ instance_name, 0, 0);
+ return_value = -1;
+ goto bye;
+ }
+ /* [605974] command db2ldif should not be able to run when on-line
+ * import is running */
+ if (dblayer_in_import(inst)) {
+ LDAPDebug(LDAP_DEBUG_ANY, "instance %s is busy\n",
+ instance_name, 0, 0);
+ return_value = -1;
+ goto bye;
+ }
+
+ /* store the be in the pb */
+ be = inst->inst_be;
+ slapi_pblock_set(pb, SLAPI_BACKEND, be);
+ } else {
+ slapi_pblock_get(pb, SLAPI_BACKEND, &be);
+ inst = (ldbm_instance *)be->be_instance_info;
+
+ if (NULL == inst) {
+ LDAPDebug(LDAP_DEBUG_ANY, "Unknown ldbm instance\n", 0, 0, 0);
+ return_value = -1;
+ goto bye;
+ }
+
+ /* check if an import/restore is already ongoing... */
+ if (instance_set_busy(inst) != 0) {
+ LDAPDebug(LDAP_DEBUG_ANY, "ldbm: '%s' is already in the middle"
+ " of another task and cannot be disturbed.\n",
+ inst->inst_name, 0, 0);
+ return_value = -1;
+ goto bye;
+ }
+ }
+
+ slapi_pblock_get( pb, SLAPI_BACKEND_TASK, &task );
+
+ ldbm_back_fetch_incl_excl(pb, &include_suffix, &exclude_suffix);
+
+ str2entry_options= (dump_replica?0:SLAPI_STR2ENTRY_TOMBSTONE_CHECK);
+
+ slapi_pblock_get( pb, SLAPI_DB2LDIF_FILE, &fname );
+ slapi_pblock_get( pb, SLAPI_DB2LDIF_PRINTKEY, &printkey );
+ slapi_pblock_get( pb, SLAPI_DB2LDIF_DUMP_UNIQUEID, &dump_uniqueid );
+
+ /* tsk, overloading printkey. shame on me. */
+ ok_index = !(printkey & EXPORT_ID2ENTRY_ONLY);
+ printkey &= ~EXPORT_ID2ENTRY_ONLY;
+
+ nobase64 = (printkey & EXPORT_MINIMAL_ENCODING);
+ printkey &= ~EXPORT_MINIMAL_ENCODING;
+ nowrap = (printkey & EXPORT_NOWRAP);
+ printkey &= ~EXPORT_NOWRAP;
+ appendmode = (printkey & EXPORT_APPENDMODE);
+ printkey &= ~EXPORT_APPENDMODE;
+ appendmode_1 = (printkey & EXPORT_APPENDMODE_1);
+ printkey &= ~EXPORT_APPENDMODE_1;
+ noversion = (printkey & EXPORT_NOVERSION);
+ printkey &= ~EXPORT_NOVERSION;
+
+ /* decide whether to dump uniqueid */
+ if (dump_uniqueid)
+ options |= SLAPI_DUMP_UNIQUEID;
+ if (nowrap)
+ options |= SLAPI_DUMP_NOWRAP;
+ if (nobase64)
+ options |= SLAPI_DUMP_MINIMAL_ENCODING;
+ if (dump_replica)
+ options |= SLAPI_DUMP_STATEINFO;
+
+ if (fname == NULL) {
+ LDAPDebug(LDAP_DEBUG_ANY, "db2ldif: no LDIF filename supplied\n",
+ 0, 0, 0);
+ return_value = -1;
+ goto bye;
+ }
+
+ if (strcmp(fname, "-")) { /* not '-' */
+ if (appendmode) {
+ if (appendmode_1) {
+ fd = dblayer_open_huge_file(fname, O_WRONLY|O_CREAT|O_TRUNC,
+ SLAPD_DEFAULT_FILE_MODE);
+ } else {
+ fd = dblayer_open_huge_file(fname, O_WRONLY|O_CREAT|O_APPEND,
+ SLAPD_DEFAULT_FILE_MODE);
+ }
+ } else {
+ /* open it */
+ fd = dblayer_open_huge_file(fname, O_WRONLY|O_CREAT|O_TRUNC,
+ SLAPD_DEFAULT_FILE_MODE);
+ }
+ if (fd < 0) {
+ LDAPDebug(LDAP_DEBUG_ANY, "db2ldif: can't open %s: %d (%s)\n",
+ fname, errno, dblayer_strerror(errno));
+ return_value = -1;
+ goto bye;
+ }
+ } else { /* '-' */
+ fd = FD_STDOUT;
+ }
+
+ if ( we_start_the_backends ) {
+ if (0 != dblayer_start(li,DBLAYER_EXPORT_MODE)) {
+ LDAPDebug( LDAP_DEBUG_ANY, "db2ldif: Failed to init database\n",
+ 0, 0, 0 );
+ return_value = -1;
+ goto bye;
+ }
+ /* dblayer_instance_start will init the id2entry index. */
+ if (0 != dblayer_instance_start(be, DBLAYER_EXPORT_MODE)) {
+ LDAPDebug(LDAP_DEBUG_ANY, "db2ldif: Failed to init instance\n",
+ 0, 0, 0);
+ return_value = -1;
+ goto bye;
+ }
+ }
+
+ /* idl manipulation requires nextid to be init'd now */
+ if (include_suffix && ok_index)
+ get_ids_from_disk(be);
+
+ if ((( dblayer_get_id2entry( be, &db )) != 0) || (db == NULL)) {
+ LDAPDebug( LDAP_DEBUG_ANY, "Could not open/create id2entry\n",
+ 0, 0, 0 );
+ ldbm_back_free_incl_excl(include_suffix, exclude_suffix);
+ return_value = -1;
+ goto bye;
+ }
+
+ /* if an include_suffix was given (and we're pretty sure the
+ * entrydn and ancestorid indexes are valid), we try to
+ * assemble an id-list of candidates instead of plowing thru
+ * the whole database. this is a big performance improvement
+ * when exporting config info (which is usually on the order
+ * of 100 entries) from a database that may be on the order of
+ * GIGS in size.
+ */
+ {
+ /* Here, we assume that the table is ordered in EID-order,
+ * which it is !
+ */
+ /* get a cursor to we can walk over the table */
+ return_value = db->cursor(db,NULL,&dbc,0);
+ if (0 != return_value ) {
+ LDAPDebug( LDAP_DEBUG_ANY,
+ "Failed to get cursor for db2ldif\n",
+ 0, 0, 0 );
+ ldbm_back_free_incl_excl(include_suffix, exclude_suffix);
+ return_value = -1;
+ goto bye;
+ }
+ key.flags = DB_DBT_MALLOC;
+ data.flags = DB_DBT_MALLOC;
+ return_value = dbc->c_get(dbc,&key,&data,DB_LAST);
+ if (0 != return_value) {
+ keepgoing = 0;
+ } else {
+ lastid = id_stored_to_internal((char *)key.data);
+ free( key.data );
+ free( data.data );
+ isfirst = 1;
+ }
+ }
+ if (include_suffix && ok_index && !dump_replica) {
+ int err;
+
+ idl = ldbm_fetch_subtrees(be, include_suffix, &err);
+ if (! idl) {
+ /* most likely, indexes are bad. */
+ LDAPDebug(LDAP_DEBUG_ANY,
+ "Failed to fetch subtree lists (error %d) %s\n",
+ err, dblayer_strerror(err), 0);
+ LDAPDebug(LDAP_DEBUG_ANY,
+ "Possibly the entrydn or ancestorid index is corrupted or "
+ "does not exist.\n", 0, 0, 0);
+ LDAPDebug(LDAP_DEBUG_ANY,
+ "Attempting direct unindexed export instead.\n",
+ 0, 0, 0);
+ ok_index = 0;
+ idl = NULL;
+ } else if (ALLIDS(idl)) {
+ /* allids list is no help at all -- revert to trawling
+ * the whole list. */
+ ok_index = 0;
+ idl_free(idl);
+ idl = NULL;
+ }
+ idindex = 0;
+ }
+
+ /* When user has specifically asked not to print the version
+ * or when this is not the first backend that is append into
+ * this file : don't print the version
+ */
+ if ((!noversion) && ((!appendmode) || (appendmode_1))) {
+ char vstr[64];
+ int myversion = 1; /* XXX: ldif version;
+ * needs to be modified when version
+ * control begins.
+ */
+
+ sprintf(vstr, "version: %d\n\n", myversion);
+ write(fd, vstr, strlen(vstr));
+ }
+
+ while ( keepgoing ) {
+ Slapi_Attr *this_attr, *next_attr;
+
+ /*
+ * All database operations in a transactional environment,
+ * including non-transactional reads can receive a return of
+ * DB_LOCK_DEADLOCK. Which operation gets aborted depends
+ * on the deadlock detection policy, but can include
+ * non-transactional reads (in which case the single
+ * operation should just be retried).
+ */
+
+ if (idl) {
+ /* exporting from an ID list */
+ if (idindex >= idl->b_nids)
+ break;
+ id_internal_to_stored(idl->b_ids[idindex], (char *)&temp_id);
+ key.data = (char *)&temp_id;
+ key.size = sizeof(temp_id);
+ data.flags = DB_DBT_MALLOC;
+
+ for (retry = 0; retry < RETRY_TIMES; retry++) {
+ return_value = db->get(db, NULL, &key, &data, 0);
+ if (return_value != DB_LOCK_DEADLOCK) break;
+ }
+ if (return_value) {
+ LDAPDebug(LDAP_DEBUG_ANY, "db2ldif: failed to read "
+ "entry %lu, err %d\n", (u_long)idl->b_ids[idindex],
+ return_value, 0);
+ return_value = -1;
+ break;
+ }
+ /* back to internal format: */
+ temp_id = idl->b_ids[idindex];
+ idindex++;
+ } else {
+ /* follow the cursor */
+ key.flags = DB_DBT_MALLOC;
+ data.flags = DB_DBT_MALLOC;
+ if (isfirst) {
+ for (retry = 0; retry < RETRY_TIMES; retry++) {
+ return_value = dbc->c_get(dbc,&key,&data,DB_FIRST);
+ if (return_value != DB_LOCK_DEADLOCK) break;
+ }
+ isfirst = 0;
+ } else {
+ for (retry = 0; retry < RETRY_TIMES; retry++) {
+ return_value = dbc->c_get(dbc,&key,&data,DB_NEXT);
+ if (return_value != DB_LOCK_DEADLOCK) break;
+ }
+ }
+
+ if (0 != return_value)
+ break;
+
+ /* back to internal format */
+ temp_id = id_stored_to_internal((char *)key.data);
+ free(key.data);
+ }
+
+ /* call post-entry plugin */
+ plugin_call_entryfetch_plugins( (char **) &data.dptr, &data.dsize );
+
+ ep = backentry_alloc();
+ ep->ep_entry = slapi_str2entry( data.data, str2entry_options );
+ free(data.data);
+
+ if ( (ep->ep_entry) != NULL ) {
+ ep->ep_id = temp_id;
+ cnt++;
+ } else {
+ LDAPDebug( LDAP_DEBUG_ANY,
+ "skipping badly formatted entry with id %lu\n",
+ (u_long)temp_id, 0, 0 );
+ backentry_free( &ep );
+ continue;
+ }
+ if (!ldbm_back_ok_to_dump(backentry_get_ndn(ep), include_suffix,
+ exclude_suffix)) {
+ backentry_free( &ep );
+ continue;
+ }
+ if(!dump_replica && slapi_entry_flag_is_set(ep->ep_entry, SLAPI_ENTRY_FLAG_TOMBSTONE))
+ {
+ /* We only dump the tombstones if the user needs to create a replica from the ldif */
+ backentry_free( &ep );
+ continue;
+ }
+
+
+ /* do not output attributes that are in the "exclude" list */
+ /* Also, decrypt any encrypted attributes, if we're asked to */
+ rc = slapi_entry_first_attr( ep->ep_entry, &this_attr );
+ while (0 == rc) {
+ rc = slapi_entry_next_attr( ep->ep_entry,
+ this_attr, &next_attr );
+ slapi_attr_get_type( this_attr, &type );
+ if ( ldbm_exclude_attr_from_export( li, type, dump_uniqueid )) {
+ slapi_entry_delete_values( ep->ep_entry, type, NULL );
+ }
+ this_attr = next_attr;
+ }
+ if (decrypt) {
+ /* Decrypt in place */
+ rc = attrcrypt_decrypt_entry(be, ep);
+ if (rc) {
+ LDAPDebug(LDAP_DEBUG_ANY,"Failed to decrypt entry%s\n", ep->ep_entry->e_sdn , 0, 0);
+ }
+ }
+
+ data.data = slapi_entry2str_with_options( ep->ep_entry, &len, options );
+ data.size = len + 1;
+
+ if ( printkey & EXPORT_PRINTKEY ) {
+ char idstr[32];
+
+ sprintf(idstr, "# entry-id: %lu\n", (u_long)ep->ep_id);
+ write(fd, idstr, strlen(idstr));
+ }
+ write(fd, data.data, len);
+ write(fd, "\n", 1);
+ if (cnt % 1000 == 0) {
+ int percent;
+
+ if (idl) {
+ percent = (idindex*100 / idl->b_nids);
+ } else {
+ percent = (ep->ep_id*100 / lastid);
+ }
+ if (task != NULL) {
+ slapi_task_log_status(task,
+ "%s: Processed %d entries (%d%%).",
+ inst->inst_name, cnt, percent);
+ slapi_task_log_notice(task,
+ "%s: Processed %d entries (%d%%).",
+ inst->inst_name, cnt, percent);
+ }
+ LDAPDebug(LDAP_DEBUG_ANY,
+ "export %s: Processed %d entries (%d%%).\n",
+ inst->inst_name, cnt, percent);
+ lastcnt = cnt;
+ }
+
+ backentry_free( &ep );
+ free( data.data );
+ }
+ /* DB_NOTFOUND -> successful end */
+ if (return_value == DB_NOTFOUND)
+ return_value = 0;
+
+ /* done cycling thru entries to write */
+ if (lastcnt != cnt) {
+ if (task) {
+ slapi_task_log_status(task,
+ "%s: Processed %d entries (100%%).",
+ inst->inst_name, cnt);
+ slapi_task_log_notice(task,
+ "%s: Processed %d entries (100%%).",
+ inst->inst_name, cnt);
+ }
+ LDAPDebug(LDAP_DEBUG_ANY,
+ "export %s: Processed %d entries (100%%).\n",
+ inst->inst_name, cnt, 0);
+ }
+
+ if (idl) {
+ idl_free(idl);
+ }
+ if (dbc) {
+ dbc->c_close(dbc);
+ }
+
+ dblayer_release_id2entry( be, db );
+ ldbm_back_free_incl_excl(include_suffix, exclude_suffix);
+
+ if (fd != FD_STDOUT) {
+ close(fd);
+ }
+
+ LDAPDebug( LDAP_DEBUG_TRACE, "<= ldbm_back_ldbm2ldif\n", 0, 0, 0 );
+
+ if (we_start_the_backends && 0 != dblayer_flush(li)) {
+ LDAPDebug( LDAP_DEBUG_ANY, "db2ldif: Failed to flush database\n",
+ 0, 0, 0 );
+ }
+
+ if (we_start_the_backends) {
+ if (0 != dblayer_close(li,DBLAYER_EXPORT_MODE)) {
+ LDAPDebug( LDAP_DEBUG_ANY,
+ "db2ldif: Failed to close database\n",
+ 0, 0, 0 );
+ }
+ } else if (run_from_cmdline && dump_replica) {
+ /*
+ * It should not be necessary to close the dblayer here.
+ * However it masks complex thread timing issues that
+ * prevent a correct shutdown of the plugins. Closing the
+ * dblayer here means we cannot dump multiple replicas
+ * using -r, but the server doesn't allow that either.
+ */
+
+ /*
+ * Use DBLAYER_NORMAL_MODE to match the value that was provided
+ * to dblayer_start() and ensure creation of the guardian file.
+ */
+ if (0 != dblayer_close(li,DBLAYER_NORMAL_MODE)) {
+ LDAPDebug( LDAP_DEBUG_ANY,
+ "db2ldif: Failed to close database\n",
+ 0, 0, 0 );
+ }
+ }
+
+ if (!run_from_cmdline) {
+ instance_set_not_busy(inst);
+ }
+
+bye:
+ if (inst != NULL) {
+ PR_Lock(inst->inst_config_mutex);
+ inst->inst_flags &= ~INST_FLAG_BUSY;
+ PR_Unlock(inst->inst_config_mutex);
+ }
+
+ return( return_value );
+}
+
+
+static void ldbm2index_bad_vlv(Slapi_Task *task, ldbm_instance *inst,
+ char *index)
+{
+ char *text = vlv_getindexnames(inst->inst_be);
+
+ if (task) {
+ slapi_task_log_status(task, "%s: Unknown VLV index '%s'",
+ inst->inst_name, index);
+ slapi_task_log_notice(task, "%s: Unknown VLV index '%s'",
+ inst->inst_name, index);
+ slapi_task_log_notice(task, "%s: Known VLV indexes are: %s",
+ inst->inst_name, text);
+ }
+ LDAPDebug(LDAP_DEBUG_ANY,
+ "ldbm2index: Unknown VLV Index named '%s'\n", index, 0, 0);
+ LDAPDebug(LDAP_DEBUG_ANY,
+ "ldbm2index: Known VLV Indexes are: %s\n", text, 0, 0);
+ slapi_ch_free((void**)&text);
+}
+
+/*
+ * ldbm_back_ldbm2index - backend routine to create a new index from an
+ * existing database
+ */
+int
+ldbm_back_ldbm2index(Slapi_PBlock *pb)
+{
+ char *instance_name;
+ struct ldbminfo *li;
+ int task_flags, run_from_cmdline;
+ ldbm_instance *inst;
+ backend *be;
+ DB *db = NULL;
+ DBC *dbc = NULL;
+ char **indexAttrs = NULL;
+ struct vlvIndex **pvlv= NULL;
+ DBT key = {0};
+ DBT data = {0};
+ IDList *idl = NULL; /* optimization for vlv index creation */
+ int numvlv = 0;
+ int return_value = -1;
+ ID temp_id;
+ int i, j;
+ ID lastid;
+ struct backentry *ep;
+ char *type;
+ NIDS idindex = 0;
+ int count = 0;
+ Slapi_Attr *attr;
+ Slapi_Task *task;
+ int ret = 0;
+ int isfirst = 1;
+ int index_aid = 0; /* index ancestorid */
+
+ LDAPDebug( LDAP_DEBUG_TRACE, "=> ldbm_back_ldbm2index\n", 0, 0, 0 );
+
+ slapi_pblock_get(pb, SLAPI_BACKEND_INSTANCE_NAME, &instance_name);
+ slapi_pblock_get(pb, SLAPI_PLUGIN_PRIVATE, &li);
+ slapi_pblock_get(pb, SLAPI_TASK_FLAGS, &task_flags);
+ run_from_cmdline = (task_flags & TASK_RUNNING_FROM_COMMANDLINE);
+ slapi_pblock_get(pb, SLAPI_BACKEND_TASK, &task);
+
+ if (run_from_cmdline) {
+ /* No ldbm backend exists until we process the config info. */
+ li->li_flags |= TASK_RUNNING_FROM_COMMANDLINE;
+ ldbm_config_load_dse_info(li);
+ }
+
+ inst = ldbm_instance_find_by_name(li, instance_name);
+ if (NULL == inst) {
+ if (task) {
+ slapi_task_log_notice(task, "Unknown ldbm instance %s",
+ instance_name, 0, 0);
+ }
+ LDAPDebug(LDAP_DEBUG_ANY, "Unknown ldbm instance %s\n",
+ instance_name, 0, 0);
+ return -1;
+ }
+ be = inst->inst_be;
+ slapi_pblock_set(pb, SLAPI_BACKEND, be);
+
+ /* would love to be able to turn off transactions here, but i don't
+ * think it's in the cards...
+ */
+ if (run_from_cmdline) {
+ /* Turn off transactions */
+ ldbm_config_internal_set(li, CONFIG_DB_TRANSACTION_LOGGING, "off");
+
+ if (0 != dblayer_start(li,DBLAYER_INDEX_MODE)) {
+ LDAPDebug( LDAP_DEBUG_ANY,
+ "ldbm2index: Failed to init database\n", 0, 0, 0 );
+ return( -1 );
+ }
+
+ /* dblayer_instance_start will init the id2entry index. */
+ if (0 != dblayer_instance_start(be, DBLAYER_INDEX_MODE)) {
+ LDAPDebug(LDAP_DEBUG_ANY, "db2ldif: Failed to init instance\n",
+ 0, 0, 0);
+ return -1;
+ }
+
+ /* Initialise the Virtual List View code */
+ vlv_init(inst);
+ }
+
+ /* make sure no other tasks are going, and set the backend readonly */
+ if (instance_set_busy_and_readonly(inst) != 0) {
+ LDAPDebug(LDAP_DEBUG_ANY, "ldbm: '%s' is already in the middle of "
+ "another task and cannot be disturbed.\n",
+ inst->inst_name, 0, 0);
+ return -1;
+ }
+
+ if ((( dblayer_get_id2entry( be, &db )) != 0 ) || (db == NULL)) {
+ LDAPDebug( LDAP_DEBUG_ANY, "Could not open/create id2entry\n",
+ 0, 0, 0 );
+ instance_set_not_busy(inst);
+ return( -1 );
+ }
+
+ /* get a cursor to we can walk over the table */
+ return_value = db->cursor(db, NULL, &dbc, 0);
+ if (0 != return_value ) {
+ LDAPDebug( LDAP_DEBUG_ANY,
+ "Failed to get cursor for ldbm2index\n", 0, 0, 0 );
+ dblayer_release_id2entry(be, db);
+ instance_set_not_busy(inst);
+ return( -1 );
+ }
+
+ /* ask for the last id so we can give cute percentages */
+ key.flags = DB_DBT_MALLOC;
+ data.flags = DB_DBT_MALLOC;
+ return_value = dbc->c_get(dbc, &key, &data, DB_LAST);
+ if (return_value == DB_NOTFOUND) {
+ lastid = 0;
+ isfirst = 0; /* neither a first nor a last */
+ } else if (return_value == 0) {
+ lastid = id_stored_to_internal((char *)key.data);
+ free(key.data);
+ free(data.data);
+ isfirst = 1;
+ } else {
+ LDAPDebug(LDAP_DEBUG_ANY,
+ "Failed to seek within id2entry (BAD %d)\n",
+ return_value, 0 ,0);
+ dbc->c_close(dbc);
+ dblayer_release_id2entry(be, db);
+ instance_set_not_busy(inst);
+ return( -1 );
+ }
+
+ /* Work out which indexes we should build */
+ /* explanation: for archaic reasons, the list of indexes is passed to
+ * ldif2index as a string list, where each string either starts with a
+ * 't' (normal index) or a 'T' (vlv index).
+ * example: "tcn" (normal index cn)
+ */
+ {
+ char **attrs = NULL;
+ struct vlvIndex *p = NULL;
+ struct attrinfo *ai = NULL;
+
+ slapi_pblock_get(pb, SLAPI_DB2INDEX_ATTRS, &attrs);
+ for (i = 0; attrs[i] != NULL; i++) {
+ switch(attrs[i][0]) {
+ case 't': /* attribute type to index */
+ db2index_add_indexed_attr(be, attrs[i]);
+ ainfo_get(be, attrs[i]+1, &ai);
+ /* the ai was added above, if it didn't already exist */
+ PR_ASSERT(ai != NULL);
+ if (strcasecmp(attrs[i]+1, "ancestorid") == 0) {
+ if (task) {
+ slapi_task_log_notice(task, "%s: Indexing ancestorid",
+ inst->inst_name);
+ }
+ LDAPDebug(LDAP_DEBUG_ANY, "%s: Indexing ancestorid\n",
+ inst->inst_name, 0, 0);
+ index_aid = 1;
+ } else {
+ charray_add(&indexAttrs, attrs[i]+1);
+ ai->ai_indexmask |= INDEX_OFFLINE;
+ if (task) {
+ slapi_task_log_notice(task, "%s: Indexing attribute: %s",
+ inst->inst_name, attrs[i]+1);
+ }
+ LDAPDebug(LDAP_DEBUG_ANY, "%s: Indexing attribute: %s\n",
+ inst->inst_name, attrs[i]+1, 0);
+ }
+ dblayer_erase_index_file(be, ai, i/* chkpt; 1st time only */);
+ break;
+ case 'T': /* VLV Search to index */
+ p = vlv_find_searchname((attrs[i])+1, be);
+ if (p == NULL) {
+ ldbm2index_bad_vlv(task, inst, attrs[i]+1);
+ ret = -1;
+ goto out;
+ } else {
+ vlvIndex_go_offline(p, be);
+ if (pvlv == NULL) {
+ pvlv = (struct vlvIndex **)slapi_ch_calloc(1,
+ sizeof(struct vlvIndex *));
+ } else {
+ pvlv = (struct vlvIndex **)slapi_ch_realloc((char*)pvlv,
+ (numvlv+1)*sizeof(struct vlvIndex *));
+ }
+ pvlv[numvlv] = p;
+ numvlv++;
+ /* Get rid of the index if it already exists */
+ PR_Delete(vlvIndex_filename(p));
+ if (task) {
+ slapi_task_log_notice(task, "%s: Indexing VLV: %s",
+ inst->inst_name, attrs[i]+1);
+ }
+ LDAPDebug(LDAP_DEBUG_ANY, "%s: Indexing VLV: %s\n",
+ inst->inst_name, attrs[i]+1, 0);
+ }
+ break;
+ }
+ }
+ }
+
+ /* if we're only doing vlv indexes, we can accomplish this with an
+ * idl composed from the ancestorid list, instead of traversing the
+ * entire database.
+ */
+ if (!indexAttrs && !index_aid && pvlv) {
+ int i, err;
+ char **suffix_list = NULL;
+
+ /* create suffix list */
+ for (i = 0; i < numvlv; i++) {
+ char *s = slapi_ch_strdup(slapi_sdn_get_dn(vlvIndex_getBase(pvlv[i])));
+
+ s = slapi_dn_normalize_case(s);
+ charray_add(&suffix_list, s);
+ }
+ idl = ldbm_fetch_subtrees(be, suffix_list, &err);
+ charray_free(suffix_list);
+ if (! idl) {
+ LDAPDebug(LDAP_DEBUG_ANY,
+ "%s: WARNING: Failed to fetch subtree lists: (%d) %s\n",
+ inst->inst_name, err, dblayer_strerror(err));
+ LDAPDebug(LDAP_DEBUG_ANY,
+ "%s: Possibly the entrydn or ancestorid index is "
+ "corrupted or does not exist.\n", inst->inst_name, 0, 0);
+ LDAPDebug(LDAP_DEBUG_ANY,
+ "%s: Attempting brute-force method instead.\n",
+ inst->inst_name, 0, 0);
+ if (task) {
+ slapi_task_log_notice(task,
+ "%s: WARNING: Failed to fetch subtree lists (err %d) -- "
+ "attempting brute-force method instead.",
+ inst->inst_name, err);
+ }
+ } else if (ALLIDS(idl)) {
+ /* that's no help. */
+ idl_free(idl);
+ idl = NULL;
+ }
+ }
+
+ if (idl) {
+ /* don't need that cursor, we have a shopping list. */
+ dbc->c_close(dbc);
+ idindex = 0;
+ }
+
+ /* Bug 603120: slapd dumps core while indexing and deleting the db at the
+ * same time. Now added the lock for the indexing code too.
+ */
+ vlv_acquire_lock(be);
+ while (1) {
+ if (idl) {
+ if (idindex >= idl->b_nids)
+ break;
+ id_internal_to_stored(idl->b_ids[idindex], (char *)&temp_id);
+ key.data = (char *)&temp_id;
+ key.size = sizeof(temp_id);
+ data.flags = DB_DBT_MALLOC;
+
+ return_value = db->get(db, NULL, &key, &data, 0);
+ if (return_value) {
+ LDAPDebug(LDAP_DEBUG_ANY, "%s: Failed "
+ "to read database, errno=%d (%s)\n",
+ inst->inst_name, return_value,
+ dblayer_strerror(return_value));
+ if (task) {
+ slapi_task_log_notice(task,
+ "%s: Failed to read database, err %d (%s)",
+ inst->inst_name, return_value,
+ dblayer_strerror(return_value));
+ }
+ break;
+ }
+ /* back to internal format: */
+ temp_id = idl->b_ids[idindex];
+ idindex++;
+ } else {
+ key.flags = DB_DBT_MALLOC;
+ data.flags = DB_DBT_MALLOC;
+ if (isfirst) {
+ return_value = dbc->c_get(dbc, &key, &data, DB_FIRST);
+ isfirst = 0;
+ } else{
+ return_value = dbc->c_get(dbc, &key, &data, DB_NEXT);
+ }
+
+ if (0 != return_value) {
+ if (DB_NOTFOUND == return_value) {
+ break;
+ } else {
+ LDAPDebug(LDAP_DEBUG_ANY, "%s: Failed to read database, "
+ "errno=%d (%s)\n", inst->inst_name, return_value,
+ dblayer_strerror(return_value));
+ if (task) {
+ slapi_task_log_notice(task,
+ "%s: Failed to read database, err %d (%s)",
+ inst->inst_name, return_value,
+ dblayer_strerror(return_value));
+ }
+ break;
+ }
+ }
+ temp_id = id_stored_to_internal((char *)key.data);
+ free(key.data);
+ }
+
+ /* call post-entry plugin */
+ plugin_call_entryfetch_plugins( (char **) &data.dptr, &data.dsize );
+
+ ep = backentry_alloc();
+ ep->ep_entry = slapi_str2entry( data.data, 0 );
+ free(data.data);
+
+ if ( ep->ep_entry != NULL ) {
+ ep->ep_id = temp_id;
+ } else {
+ if (task) {
+ slapi_task_log_notice(task,
+ "%s: WARNING: skipping badly formatted entry (id %lu)",
+ inst->inst_name, (u_long)temp_id);
+ }
+ LDAPDebug(LDAP_DEBUG_ANY,
+ "%s: WARNING: skipping badly formatted entry (id %lu)\n",
+ inst->inst_name, (u_long)temp_id, 0);
+ backentry_free( &ep );
+ continue;
+ }
+
+ if ( add_op_attrs( pb, li, ep, NULL ) != 0 ) {
+ if (task) {
+ slapi_task_log_notice(task,
+ "%s: ERROR: Could not add op attrs to entry (id %lu)",
+ inst->inst_name, (u_long)ep->ep_id);
+ }
+ LDAPDebug(LDAP_DEBUG_ANY,
+ "%s: ERROR: Could not add op attrs to entry (id %lu)\n",
+ inst->inst_name, (u_long)ep->ep_id, 0);
+ backentry_free( &ep );
+ ret = -1;
+ goto out;
+ }
+
+ /*
+ * Update the attribute indexes
+ */
+ if (indexAttrs != NULL) {
+ for (i = slapi_entry_first_attr(ep->ep_entry, &attr); i == 0;
+ i = slapi_entry_next_attr(ep->ep_entry, attr, &attr)) {
+ Slapi_Value **svals;
+ int rc = 0;
+
+ slapi_attr_get_type( attr, &type );
+ for ( j = 0; indexAttrs[j] != NULL; j++ ) {
+ if (slapi_attr_type_cmp(indexAttrs[j], type,
+ SLAPI_TYPE_CMP_SUBTYPE) == 0 ) {
+ back_txn txn;
+ svals = attr_get_present_values(attr);
+
+ if (run_from_cmdline)
+ {
+ txn.back_txn_txn = NULL;
+ }
+ else
+ {
+ rc = dblayer_txn_begin(li, NULL, &txn);
+ if (0 != rc) {
+ LDAPDebug(LDAP_DEBUG_ANY,
+ "%s: ERROR: failed to begin txn for update "
+ "index '%s'\n",
+ inst->inst_name, indexAttrs[j], 0);
+ LDAPDebug(LDAP_DEBUG_ANY,
+ "%s: Error %d: %s\n", inst->inst_name, rc,
+ dblayer_strerror(rc));
+ if (task) {
+ slapi_task_log_notice(task,
+ "%s: ERROR: failed to begin txn for "
+ "update index '%s' (err %d: %s)",
+ inst->inst_name, indexAttrs[j], rc,
+ dblayer_strerror(rc));
+ }
+ ret = -2;
+ goto out;
+ }
+ }
+ rc = index_addordel_values_sv(
+ be, indexAttrs[j], svals,
+ NULL, ep->ep_id, BE_INDEX_ADD, &txn);
+ if (rc != 0) {
+ LDAPDebug(LDAP_DEBUG_ANY,
+ "%s: ERROR: failed to update index '%s'\n",
+ inst->inst_name, indexAttrs[j], 0);
+ LDAPDebug(LDAP_DEBUG_ANY,
+ "%s: Error %d: %s\n", inst->inst_name, rc,
+ dblayer_strerror(rc));
+ if (task) {
+ slapi_task_log_notice(task,
+ "%s: ERROR: failed to update index '%s' "
+ "(err %d: %s)", inst->inst_name,
+ indexAttrs[j], rc, dblayer_strerror(rc));
+ }
+ if (!run_from_cmdline)
+ dblayer_txn_abort(li, &txn);
+ ret = -2;
+ goto out;
+ }
+ if (!run_from_cmdline)
+ {
+ rc = dblayer_txn_commit(li, &txn);
+ if (0 != rc) {
+ LDAPDebug(LDAP_DEBUG_ANY,
+ "%s: ERROR: failed to commit txn for "
+ "update index '%s'\n",
+ inst->inst_name, indexAttrs[j], 0);
+ LDAPDebug(LDAP_DEBUG_ANY,
+ "%s: Error %d: %s\n", inst->inst_name, rc,
+ dblayer_strerror(rc));
+ if (task) {
+ slapi_task_log_notice(task,
+ "%s: ERROR: failed to commit txn for "
+ "update index '%s' "
+ "(err %d: %s)", inst->inst_name,
+ indexAttrs[j], rc, dblayer_strerror(rc));
+ }
+ ret = -2;
+ goto out;
+ }
+ }
+ }
+ }
+ }
+ }
+
+ /*
+ * Update the Virtual List View indexes
+ */
+ for ( j = 0; j<numvlv; j++ ) {
+ back_txn txn;
+ int rc = 0;
+ if (run_from_cmdline)
+ {
+ txn.back_txn_txn = NULL;
+ }
+ else
+ if (!run_from_cmdline)
+ {
+ rc = dblayer_txn_begin(li, NULL, &txn);
+ if (0 != rc) {
+ LDAPDebug(LDAP_DEBUG_ANY,
+ "%s: ERROR: failed to begin txn for update index '%s'\n",
+ inst->inst_name, indexAttrs[j], 0);
+ LDAPDebug(LDAP_DEBUG_ANY,
+ "%s: Error %d: %s\n", inst->inst_name, rc,
+ dblayer_strerror(rc));
+ if (task) {
+ slapi_task_log_notice(task,
+ "%s: ERROR: failed to begin txn for update index '%s' "
+ "(err %d: %s)", inst->inst_name,
+ indexAttrs[j], rc, dblayer_strerror(rc));
+ }
+ ret = -2;
+ goto out;
+ }
+ }
+ vlv_update_index(pvlv[j], &txn, li, pb, NULL, ep);
+ if (!run_from_cmdline)
+ {
+ rc = dblayer_txn_commit(li, &txn);
+ if (0 != rc) {
+ LDAPDebug(LDAP_DEBUG_ANY,
+ "%s: ERROR: failed to commit txn for update index '%s'\n",
+ inst->inst_name, indexAttrs[j], 0);
+ LDAPDebug(LDAP_DEBUG_ANY,
+ "%s: Error %d: %s\n", inst->inst_name, rc,
+ dblayer_strerror(rc));
+ if (task) {
+ slapi_task_log_notice(task,
+ "%s: ERROR: failed to commit txn for update index '%s' "
+ "(err %d: %s)", inst->inst_name,
+ indexAttrs[j], rc, dblayer_strerror(rc));
+ }
+ ret = -2;
+ goto out;
+ }
+ }
+ }
+
+ /*
+ * Update the ancestorid index
+ */
+ if (index_aid) {
+ int rc;
+
+ rc = ldbm_ancestorid_index_entry(be, ep, BE_INDEX_ADD, NULL);
+ if (rc != 0) {
+ LDAPDebug(LDAP_DEBUG_ANY,
+ "%s: ERROR: failed to update index 'ancestorid'\n",
+ inst->inst_name, 0, 0);
+ LDAPDebug(LDAP_DEBUG_ANY,
+ "%s: Error %d: %s\n", inst->inst_name, rc,
+ dblayer_strerror(rc));
+ if (task) {
+ slapi_task_log_notice(task,
+ "%s: ERROR: failed to update index 'ancestorid' "
+ "(err %d: %s)", inst->inst_name,
+ rc, dblayer_strerror(rc));
+ }
+ ret = -2;
+ goto out;
+ }
+ }
+
+ count++;
+ if ((count % 1000) == 0) {
+ int percent;
+
+ if (idl) {
+ percent = (idindex*100 / (idl->b_nids ? idl->b_nids : 1));
+ } else {
+ percent = (ep->ep_id*100 / (lastid ? lastid : 1));
+ }
+ if (task) {
+ task->task_progress = (idl ? idindex : ep->ep_id);
+ task->task_work = (idl ? idl->b_nids : lastid);
+ slapi_task_status_changed(task);
+ slapi_task_log_status(task, "%s: Indexed %d entries (%d%%).",
+ inst->inst_name, count, percent);
+ slapi_task_log_notice(task, "%s: Indexed %d entries (%d%%).",
+ inst->inst_name, count, percent);
+ }
+ LDAPDebug(LDAP_DEBUG_ANY, "%s: Indexed %d entries (%d%%).\n",
+ inst->inst_name, count, percent);
+ }
+
+ backentry_free( &ep );
+ }
+ vlv_release_lock(be);
+
+ /* if we got here, we finished successfully */
+
+ /* activate all the indexes we added */
+ for (i = 0; indexAttrs && indexAttrs[i]; i++) {
+ struct attrinfo *ai = NULL;
+
+ ainfo_get(be, indexAttrs[i], &ai);
+ PR_ASSERT(ai != NULL);
+ ai->ai_indexmask &= ~INDEX_OFFLINE;
+ }
+ for (i = 0; i < numvlv; i++) {
+ vlvIndex_go_online(pvlv[i], be);
+ }
+
+ if (task) {
+ slapi_task_log_status(task, "%s: Finished indexing.",
+ inst->inst_name);
+ slapi_task_log_notice(task, "%s: Finished indexing.",
+ inst->inst_name);
+ }
+ LDAPDebug(LDAP_DEBUG_ANY, "%s: Finished indexing.\n",
+ inst->inst_name, 0, 0);
+
+out:
+ if (idl) {
+ idl_free(idl);
+ } else {
+ dbc->c_close(dbc);
+ }
+ dblayer_release_id2entry( be, db );
+
+ instance_set_not_busy(inst);
+
+ LDAPDebug( LDAP_DEBUG_TRACE, "<= ldbm_back_ldbm2index\n", 0, 0, 0 );
+
+ if (run_from_cmdline) {
+ if (0 != dblayer_flush(li)) {
+ LDAPDebug(LDAP_DEBUG_ANY,
+ "%s: Failed to flush database\n", inst->inst_name, 0, 0);
+ }
+ dblayer_instance_close(be);
+ if (0 != dblayer_close(li,DBLAYER_INDEX_MODE)) {
+ LDAPDebug(LDAP_DEBUG_ANY,
+ "%s: Failed to close database\n", inst->inst_name, 0, 0);
+ }
+ }
+
+ if (indexAttrs) {
+ slapi_ch_free((void **)&indexAttrs);
+ }
+
+ return (ret);
+}
+
+/*
+ * The db2index mode of slapd accepts commandline specification of
+ * an attribute to be indexed and the types of indexes to be created.
+ * The format is:
+ * (ns-)slapd db2index -tattributeName[:indextypes[:matchingrules]]
+ * where indextypes and matchingrules(OIDs) are comma separated lists
+ * e.g.,
+ * -tuid:eq,pres
+ * -tuid:sub:2.1.15.17.blah
+ */
+static int
+db2index_add_indexed_attr(backend *be, char *attrString)
+{
+ char *iptr = NULL;
+ char *mptr = NULL;
+ char *nsslapd_index_value[4];
+ int argc = 0;
+ int i;
+
+ if (NULL == (iptr = strchr(attrString, ':'))) {
+ return(0);
+ }
+ iptr[0] = '\0';
+ iptr++;
+
+ nsslapd_index_value[argc++] = slapi_ch_strdup(attrString+1);
+
+ if (NULL != (mptr = strchr(iptr, ':'))) {
+ mptr[0] = '\0';
+ mptr++;
+ }
+ nsslapd_index_value[argc++] = slapi_ch_strdup(iptr);
+ if (NULL != mptr) {
+ nsslapd_index_value[argc++] = slapi_ch_strdup(mptr);
+ }
+ nsslapd_index_value[argc] = NULL;
+ attr_index_config(be, "from db2index()", 0, argc, nsslapd_index_value, 0);
+
+ for ( i=0; i<argc; i++ ) {
+ slapi_ch_free((void **)&nsslapd_index_value[i]);
+ }
+ return(0);
+}
+
+
+/*
+ * Determine if the given normalized 'attr' is to be excluded from LDIF
+ * exports.
+ *
+ * Returns a non-zero value if:
+ * 1) The 'attr' is in the configured list of attribute types that
+ * are to be excluded.
+ * OR 2) dump_uniqueid is non-zero and 'attr' is the unique ID attribute.
+ *
+ * Return 0 if the attribute is not to be excluded.
+ */
+static int
+ldbm_exclude_attr_from_export( struct ldbminfo *li , const char *attr,
+ int dump_uniqueid )
+
+{
+ int i, rc = 0;
+
+ if ( !dump_uniqueid && 0 == strcasecmp( SLAPI_ATTR_UNIQUEID, attr )) {
+ rc = 1; /* exclude */
+
+ } else if ( NULL != li && NULL != li->li_attrs_to_exclude_from_export ) {
+ for ( i = 0; li->li_attrs_to_exclude_from_export[i] != NULL; ++i ) {
+ if ( 0 == strcasecmp( li->li_attrs_to_exclude_from_export[i],
+ attr )) {
+ rc = 1; /* exclude */
+ break;
+ }
+ }
+ }
+
+ return( rc );
+}
+
+#if defined(UPGRADEDB)
+/*
+ * ldbm_back_upgradedb -
+ *
+ * functions to convert idl from the old format to the new one
+ * (604921) Support a database uprev process any time post-install
+ */
+
+void upgradedb_core(Slapi_PBlock *pb, ldbm_instance *inst);
+int upgradedb_copy_logfiles(struct ldbminfo *li, char *destination_dir, int restore, int *cnt);
+int upgradedb_delete_indices_4cmd(ldbm_instance *inst);
+void normalize_dir(char *dir);
+
+/*
+ * ldbm_back_upgradedb -
+ * check the DB version and if it's old idl'ed index,
+ * then reindex using new idl.
+ *
+ * standalone only -- not allowed to run while DS is up.
+ */
+int ldbm_back_upgradedb(Slapi_PBlock *pb)
+{
+ struct ldbminfo *li;
+ Object *inst_obj = NULL;
+ ldbm_instance *inst = NULL;
+ int run_from_cmdline = 0;
+ int task_flags = 0;
+ int server_running = 0;
+ int rval = 0;
+ int backup_rval = 0;
+ char *dest_dir = NULL;
+ char *orig_dest_dir = NULL;
+ char *home_dir = NULL;
+ int up_flags;
+ int i;
+ Slapi_Task *task;
+ char inst_dir[MAXPATHLEN];
+ char *inst_dirp = NULL;
+
+ slapi_pblock_get(pb, SLAPI_SEQ_TYPE, &up_flags);
+ slapi_log_error(SLAPI_LOG_TRACE, "upgrade DB", "Reindexing all...\n");
+ slapi_pblock_get(pb, SLAPI_TASK_FLAGS, &task_flags);
+ slapi_pblock_get(pb, SLAPI_BACKEND_TASK, &task);
+ slapi_pblock_get(pb, SLAPI_DB2LDIF_SERVER_RUNNING, &server_running);
+
+ run_from_cmdline = (task_flags & TASK_RUNNING_FROM_COMMANDLINE);
+ slapi_pblock_get(pb, SLAPI_PLUGIN_PRIVATE, &li);
+ if (run_from_cmdline)
+ {
+ if (!(up_flags & SLAPI_UPGRADEDB_SKIPINIT))
+ {
+ ldbm_config_load_dse_info(li);
+ }
+ autosize_import_cache(li);
+ }
+ else
+ {
+ Object *inst_obj, *inst_obj2;
+ ldbm_instance *inst = NULL;
+
+ /* server is up -- mark all backends busy */
+ slapi_log_error(SLAPI_LOG_TRACE, "upgrade DB",
+ "server is up -- marking all LDBM backends busy\n");
+ for (inst_obj = objset_first_obj(li->li_instance_set); inst_obj;
+ inst_obj = objset_next_obj(li->li_instance_set, inst_obj))
+ {
+ inst = (ldbm_instance *)object_get_data(inst_obj);
+ /* check if an import/restore is already ongoing... */
+ /* BUSY flag is cleared at the end of import_main (join thread);
+ it should not cleared in this thread [610347] */
+ if (instance_set_busy(inst) != 0)
+ {
+ slapi_log_error(SLAPI_LOG_FATAL, "upgrade DB",
+ "ldbm: '%s' is already in the middle of "
+ "another task and cannot be disturbed.\n",
+ inst->inst_name);
+ if (task)
+ {
+ slapi_task_log_notice(task,
+ "Backend '%s' is already in the middle of "
+ "another task and cannot be disturbed.\n",
+ inst->inst_name);
+ }
+
+ /* painfully, we have to clear the BUSY flags on the
+ * backends we'd already marked...
+ */
+ for (inst_obj2 = objset_first_obj(li->li_instance_set);
+ inst_obj2 && (inst_obj2 != inst_obj);
+ inst_obj2 = objset_next_obj(li->li_instance_set, inst_obj2))
+ {
+ inst = (ldbm_instance *)object_get_data(inst_obj2);
+ instance_set_not_busy(inst);
+ }
+ object_release(inst_obj2);
+ object_release(inst_obj);
+ return -1;
+ }
+ }
+ }
+
+ inst_obj = objset_first_obj(li->li_instance_set);
+ if (inst_obj)
+ {
+ inst = (ldbm_instance *)object_get_data(inst_obj);
+ if (!(up_flags & SLAPI_UPGRADEDB_FORCE))
+ { /* upgrade idl to new */
+ li->li_flags |= LI_FORCE_MOD_CONFIG;
+ /* set new idl */
+ ldbm_config_internal_set(li, CONFIG_IDL_SWITCH, "new");
+ /* First check the dbversion */
+ rval = check_db_inst_version(inst);
+ if (!(DBVERSION_NEED_IDL_OLD2NEW & rval))
+ {
+ slapi_log_error(SLAPI_LOG_FATAL, "upgrade DB",
+ "Index version is up-to-date\n");
+ return 0;
+ }
+ }
+ }
+ else
+ {
+ slapi_log_error(SLAPI_LOG_FATAL,
+ "upgrade DB", "No instance to be upgraded\n");
+ return -1;
+ }
+
+ /* we are going to go forward */
+ /*
+ * First, backup index files and checkpoint log files
+ * since the server is not up and running, we can just copy them.
+ */
+ slapi_pblock_get( pb, SLAPI_SEQ_VAL, &dest_dir );
+ if (NULL == dest_dir)
+ {
+ slapi_log_error(SLAPI_LOG_FATAL, "upgrade DB",
+ "Backup directory is not specified.\n");
+ return -1;
+ }
+
+ {
+ int cnt = 0;
+ PRFileInfo info;
+
+ orig_dest_dir = dest_dir;
+ normalize_dir(dest_dir);
+ /* clean up the backup dir first, then create it */
+ rval = PR_GetFileInfo(dest_dir, &info);
+ if (PR_SUCCESS == rval)
+ {
+ if (PR_FILE_DIRECTORY == info.type) /* directory exists */
+ {
+ time_t tm = time(0); /* long */
+
+ char *tmpname = (char *)slapi_ch_malloc(strlen(dest_dir) + 32);
+ sprintf(tmpname, "%s/%d", dest_dir, tm);
+ dest_dir = tmpname;
+ }
+ else /* not a directory */
+ PR_Delete(dest_dir);
+ }
+
+ if (mkdir_p(dest_dir, 0700) < 0)
+ goto fail0;
+
+ while (1)
+ {
+ inst_dirp = dblayer_get_full_inst_dir(inst->inst_li, inst,
+ inst_dir, MAXPATHLEN);
+ backup_rval = dblayer_copy_directory(li, NULL /* task */,
+ inst_dirp, dest_dir, 0/*backup*/,
+ &cnt, 0, 1);
+ if (inst_dirp != inst_dir)
+ slapi_ch_free_string(&inst_dirp);
+ if (backup_rval < 0)
+ {
+ slapi_log_error(SLAPI_LOG_FATAL, "upgrade DB",
+ "Warning: Failed to backup index files (instance %s).\n",
+ inst_dirp);
+ goto fail1;
+ }
+
+ /* delete index files to be reindexed */
+ if (run_from_cmdline)
+ {
+ if (0 != upgradedb_delete_indices_4cmd(inst))
+ {
+ slapi_log_error(SLAPI_LOG_FATAL, "upgrade DB",
+ "Can't clean up indices in %s\n", inst->inst_dir_name);
+ goto fail1;
+ }
+ }
+ else
+ {
+ if (0 != dblayer_delete_indices(inst))
+ {
+ slapi_log_error(SLAPI_LOG_FATAL, "upgrade DB",
+ "Can't clean up indices in %s\n", inst->inst_dir_name);
+ goto fail1;
+ }
+ }
+
+ inst_obj = objset_next_obj(li->li_instance_set, inst_obj);
+ if (NULL == inst_obj)
+ break;
+ inst = (ldbm_instance *)object_get_data(inst_obj);
+ }
+
+ /* copy checkpoint logs */
+ backup_rval += upgradedb_copy_logfiles(li, dest_dir, 0, &cnt);
+ }
+
+ if (run_from_cmdline)
+ ldbm_config_internal_set(li, CONFIG_DB_TRANSACTION_LOGGING, "off");
+
+ inst_obj = objset_first_obj(li->li_instance_set);
+ for (i = 0; NULL != inst_obj; i++)
+ {
+ if (run_from_cmdline)
+ {
+ /* need to call dblayer_start for each instance,
+ since dblayer_close is called in upgradedb_core =>
+ ldbm_back_ldif2ldbm_deluxe */
+ if (0 != dblayer_start(li, DBLAYER_IMPORT_MODE))
+ {
+ slapi_log_error(SLAPI_LOG_FATAL, "upgrade DB",
+ "upgradedb: Failed to init database\n");
+ goto fail1;
+ }
+ }
+
+ inst = (ldbm_instance *)object_get_data(inst_obj);
+ slapi_pblock_set(pb, SLAPI_BACKEND, inst->inst_be);
+ slapi_pblock_set(pb, SLAPI_BACKEND_INSTANCE_NAME, inst->inst_name);
+ upgradedb_core(pb, inst);
+ inst_obj = objset_next_obj(li->li_instance_set, inst_obj);
+ }
+
+ /* upgrade idl to new; otherwise no need to modify idl-switch */
+ if (!(up_flags & SLAPI_UPGRADEDB_FORCE))
+ {
+ replace_ldbm_config_value(CONFIG_IDL_SWITCH, "new", li);
+ }
+
+ home_dir = dblayer_get_home_dir(li, NULL);
+
+ /* write db version files */
+ dbversion_write(li, home_dir, NULL);
+
+ inst_obj = objset_first_obj(li->li_instance_set);
+ while (NULL != inst_obj)
+ {
+ char *inst_dirp = NULL;
+ inst_dirp = dblayer_get_full_inst_dir(li, inst, inst_dir, MAXPATHLEN);
+ inst = (ldbm_instance *)object_get_data(inst_obj);
+ dbversion_write(li, inst_dirp, NULL);
+ inst_obj = objset_next_obj(li->li_instance_set, inst_obj);
+ if (inst_dirp != inst_dir)
+ slapi_ch_free_string(&inst_dirp);
+ }
+
+ /* close the database down again */
+ if (run_from_cmdline)
+ {
+ if (0 != dblayer_flush(li))
+ {
+ slapi_log_error(SLAPI_LOG_FATAL, "upgrade DB",
+ "Failed to flush database\n");
+ }
+ if (0 != dblayer_close(li,DBLAYER_IMPORT_MODE))
+ {
+ slapi_log_error(SLAPI_LOG_FATAL, "upgrade DB",
+ "Failed to close database\n");
+ goto fail1;
+ }
+ }
+
+ /* delete backup */
+ if (NULL != dest_dir)
+ ldbm_delete_dirs(dest_dir);
+
+ if (dest_dir != orig_dest_dir)
+ slapi_ch_free_string(&dest_dir);
+
+ return 0;
+
+fail1:
+ if (0 != dblayer_flush(li))
+ slapi_log_error(SLAPI_LOG_FATAL, "upgrade DB",
+ "Failed to flush database\n");
+
+ /* Ugly! (we started dblayer with DBLAYER_IMPORT_MODE)
+ * We just want not to generate a guardian file...
+ */
+ if (0 != dblayer_close(li,DBLAYER_ARCHIVE_MODE))
+ slapi_log_error(SLAPI_LOG_FATAL, "upgrade DB",
+ "Failed to close database\n");
+
+ /* restore from the backup, if possible */
+ if (NULL != dest_dir)
+ {
+ if (0 == backup_rval) /* only when the backup succeeded... */
+ {
+ int cnt = 0;
+
+ inst_obj = objset_first_obj(li->li_instance_set);
+ while (NULL != inst_obj)
+ {
+ inst = (ldbm_instance *)object_get_data(inst_obj);
+
+ inst_dirp = dblayer_get_full_inst_dir(inst->inst_li, inst,
+ inst_dir, MAXPATHLEN);
+ backup_rval = dblayer_copy_directory(li, NULL /* task */,
+ inst->inst_dir_name,
+ dest_dir, 1/*restore*/,
+ &cnt, 0, 1);
+ if (inst_dirp != inst_dir)
+ slapi_ch_free_string(&inst_dirp);
+ if (backup_rval < 0)
+ slapi_log_error(SLAPI_LOG_FATAL, "upgrade DB",
+ "Failed to restore index files (instance %s).\n",
+ inst->inst_name);
+
+ inst_obj = objset_next_obj(li->li_instance_set, inst_obj);
+ }
+
+ backup_rval = upgradedb_copy_logfiles(li, dest_dir, 1, &cnt);
+ if (backup_rval < 0)
+ slapi_log_error(SLAPI_LOG_FATAL, "upgrade DB",
+ "Failed to restore log files.\n");
+ }
+
+ /* anyway clean up the backup dir */
+ ldbm_delete_dirs(dest_dir);
+ }
+
+fail0:
+ if (dest_dir != orig_dest_dir)
+ slapi_ch_free_string(&dest_dir);
+
+ return rval;
+}
+
+void normalize_dir(char *dir)
+{
+ int l = strlen(dir);
+ if ('/' == dir[l-1] || '\\' == dir[l-1])
+ {
+ dir[l-1] = '\0';
+ }
+}
+
+#define LOG "log."
+#define LOGLEN 4
+int upgradedb_copy_logfiles(struct ldbminfo *li, char *destination_dir,
+ int restore, int *cnt)
+{
+ PRDir *dirhandle = NULL;
+ PRDirEntry *direntry = NULL;
+ char *src;
+ char *dest;
+ int srclen;
+ int destlen;
+ int rval = 0;
+ int len0 = 0;
+ int len1 = 0;
+ char *from = NULL;
+ char *to = NULL;
+
+ *cnt = 0;
+ if (restore)
+ {
+ src = destination_dir;
+ dest = li->li_directory;
+ }
+ else
+ {
+ src = li->li_directory;
+ dest = destination_dir;
+ }
+ srclen = strlen(src);
+ destlen = strlen(dest);
+
+ /* Open the instance dir so we can look what's in it. */
+ dirhandle = PR_OpenDir(src);
+ if (NULL == dirhandle)
+ return -1;
+
+ while (NULL != (direntry =
+ PR_ReadDir(dirhandle, PR_SKIP_DOT | PR_SKIP_DOT_DOT)))
+ {
+ if (NULL == direntry->name)
+ break;
+
+ if (0 == strncmp(direntry->name, LOG, 4))
+ {
+ int filelen = strlen(direntry->name);
+ char *p, *endp;
+ int fromlen, tolen;
+ int notalog = 0;
+
+ endp = (char *)direntry->name + filelen;
+ for (p = (char *)direntry->name + LOGLEN; p < endp; p++)
+ {
+ if (!isdigit(*p))
+ {
+ notalog = 1;
+ break;
+ }
+ }
+ if (notalog)
+ continue; /* go to next file */
+
+ fromlen = srclen + filelen + 2;
+ if (len0 < fromlen)
+ {
+ slapi_ch_free_string(&from);
+ from = slapi_ch_calloc(1, fromlen);
+ len0 = fromlen;
+ }
+ sprintf(from, "%s/%s", src, direntry->name);
+ tolen = destlen + filelen + 2;
+ if (len1 < tolen)
+ {
+ slapi_ch_free_string(&to);
+ to = slapi_ch_calloc(1, tolen);
+ len1 = tolen;
+ }
+ sprintf(to, "%s/%s", dest, direntry->name);
+ if (NULL == from || NULL == to)
+ break;
+ rval = dblayer_copyfile(from, to, 1, DEFAULT_MODE);
+ if (rval < 0)
+ break;
+ cnt++;
+ }
+ }
+ slapi_ch_free_string(&from);
+ slapi_ch_free_string(&to);
+ PR_CloseDir(dirhandle);
+
+ return rval;
+}
+
+int upgradedb_delete_indices_4cmd(ldbm_instance *inst)
+{
+ PRDir *dirhandle = NULL;
+ PRDirEntry *direntry = NULL;
+ int rval = 0;
+ char fullpath[MAXPATHLEN];
+ char *fullpathp = fullpath;
+ char inst_dir[MAXPATHLEN];
+ char *inst_dirp = dblayer_get_full_inst_dir(inst->inst_li, inst,
+ inst_dir, MAXPATHLEN);
+
+ slapi_log_error(SLAPI_LOG_TRACE, "upgrade DB",
+ "upgradedb_delete_indices_4cmd: %s\n");
+ dirhandle = PR_OpenDir(inst_dirp);
+ if (!dirhandle)
+ {
+ slapi_log_error(SLAPI_LOG_FATAL, "upgrade DB",
+ "upgradedb_delete_indices_4cmd: PR_OpenDir failed\n");
+ if (inst_dirp != inst_dir)
+ slapi_ch_free_string(&inst_dirp);
+ return -1;
+ }
+
+ while (NULL != (direntry =
+ PR_ReadDir(dirhandle, PR_SKIP_DOT | PR_SKIP_DOT_DOT)))
+ {
+ PRFileInfo info;
+ int len;
+
+ if (! direntry->name)
+ break;
+
+ if (0 == strcmp(direntry->name, ID2ENTRY LDBM_FILENAME_SUFFIX))
+ continue;
+
+ len = strlen(inst_dirp) + strlen(direntry->name) + 2;
+ if (len > MAXPATHLEN)
+ {
+ fullpathp = (char *)slapi_ch_malloc(len);
+ }
+ sprintf(fullpathp, "%s/%s", inst_dirp, direntry->name);
+ rval = PR_GetFileInfo(fullpathp, &info);
+ if (PR_SUCCESS == rval && PR_FILE_DIRECTORY != info.type)
+ {
+ PR_Delete(fullpathp);
+ slapi_log_error(SLAPI_LOG_TRACE, "upgrade DB",
+ "upgradedb_delete_indices_4cmd: %s deleted\n", fullpath);
+ }
+ if (fullpathp != fullpath)
+ slapi_ch_free_string(&fullpathp);
+ }
+ PR_CloseDir(dirhandle);
+ if (inst_dirp != inst_dir)
+ slapi_ch_free_string(&inst_dirp);
+ return rval;
+}
+
+/*
+ * upgradedb_core
+ */
+void upgradedb_core(Slapi_PBlock *pb, ldbm_instance *inst)
+{
+ backend *be = NULL;
+ int task_flags = 0;
+ int run_from_cmdline = 0;
+
+ slapi_pblock_get(pb, SLAPI_TASK_FLAGS, &task_flags);
+ run_from_cmdline = (task_flags & TASK_RUNNING_FROM_COMMANDLINE);
+
+ be = inst->inst_be;
+ slapi_log_error(SLAPI_LOG_FATAL, "upgrade DB",
+ "%s: Start upgradedb.\n", inst->inst_name);
+
+ if (!run_from_cmdline)
+ {
+ /* shutdown this instance of the db */
+ slapi_log_error(SLAPI_LOG_TRACE, "upgrade DB",
+ "Bringing %s offline...\n", inst->inst_name);
+ slapi_mtn_be_disable(inst->inst_be);
+
+ cache_clear(&inst->inst_cache);
+ dblayer_instance_close(be);
+ }
+
+ /* dblayer_instance_start will init the id2entry index. */
+ if (0 != dblayer_instance_start(be, DBLAYER_IMPORT_MODE))
+ {
+ slapi_log_error(SLAPI_LOG_FATAL, "upgrade DB",
+ "upgradedb: Failed to init instance %s\n", inst->inst_name);
+ return;
+ }
+
+ if (run_from_cmdline)
+ vlv_init(inst); /* Initialise the Virtual List View code */
+
+ ldbm_back_ldif2ldbm_deluxe(pb);
+}
+
+#endif /* UPGRADEDB */
diff --git a/ldap/servers/slapd/back-ldbm/libback-ldbm.def b/ldap/servers/slapd/back-ldbm/libback-ldbm.def
new file mode 100644
index 00000000..0967d9c5
--- /dev/null
+++ b/ldap/servers/slapd/back-ldbm/libback-ldbm.def
@@ -0,0 +1,13 @@
+; BEGIN COPYRIGHT BLOCK
+; Copyright 2001 Sun Microsystems, Inc.
+; Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+; All rights reserved.
+; END COPYRIGHT BLOCK
+;
+DESCRIPTION 'Directory Server 2.0 DB Backend Plugin'
+EXPORTS
+ ldbm_back_init @2
+ plugin_init_debug_level @3
+; ldbm_back_changelog_init @4
+
+
diff --git a/ldap/servers/slapd/back-ldbm/matchrule.c b/ldap/servers/slapd/back-ldbm/matchrule.c
new file mode 100644
index 00000000..0d7197ab
--- /dev/null
+++ b/ldap/servers/slapd/back-ldbm/matchrule.c
@@ -0,0 +1,126 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+/* matchrule.c */
+
+
+#include "back-ldbm.h"
+
+/* NPCTE fix for bug # 394184, SD, 20 Jul 00 */
+/* replace the hard coded return value by the appropriate LDAP error code */
+/*
+ * Returns: 0 -- OK now is: LDAP_SUCCESS (fix for bug #394184)
+ * -1 -- protocol error now is: LDAP_PROTOCOL_ERROR
+ * -3 -- operation error now is: LDAP_OPERATIONS_ERROR
+ */
+int
+create_matchrule_indexer(Slapi_PBlock **pb,char* matchrule,char* type)
+{
+ IFP mrINDEX = NULL;
+ int return_value = LDAP_SUCCESS;
+ unsigned int sort_indicator = SLAPI_PLUGIN_MR_USAGE_SORT;
+
+ if(pb==NULL)
+ {
+ return LDAP_OPERATIONS_ERROR;
+ }
+
+ if(*pb==NULL)
+ {
+ *pb = slapi_pblock_new();
+ }
+ if(*pb==NULL)
+ {
+ /* Memory allocation faliure */
+ /* Operations error to the calling routine */
+ return LDAP_OPERATIONS_ERROR;
+ }
+
+ /* If these fail, it's an operations error */
+ return_value |= slapi_pblock_set (*pb, SLAPI_PLUGIN_MR_OID, matchrule);
+ return_value |= slapi_pblock_set (*pb, SLAPI_PLUGIN_MR_TYPE, type);
+ return_value |= slapi_pblock_set (*pb, SLAPI_PLUGIN_MR_USAGE, (void*)&sort_indicator);
+ if (0 != return_value)
+ {
+ return LDAP_OPERATIONS_ERROR;
+ }
+
+ /* If this fails, could be operations error, or that OID is not supported */
+ return_value = slapi_mr_indexer_create (*pb);
+ if (0 != return_value)
+ {
+ return LDAP_PROTOCOL_ERROR;
+ }
+
+ /* If these fail, ops error */
+ return_value = slapi_pblock_get (*pb, SLAPI_PLUGIN_MR_INDEX_FN, &mrINDEX);
+
+ if ( (0 != return_value) || (mrINDEX == NULL) )
+ {
+ return LDAP_OPERATIONS_ERROR;
+ }
+ else
+ {
+ return LDAP_SUCCESS;
+ }
+}
+/* End NPCTE fix for bug # 394184 */
+
+int
+destroy_matchrule_indexer(Slapi_PBlock *pb)
+{
+ IFP mrDESTROY = NULL;
+ if (!slapi_pblock_get (pb, SLAPI_PLUGIN_DESTROY_FN, &mrDESTROY))
+ {
+ if (mrDESTROY != NULL)
+ {
+ mrDESTROY (pb);
+ }
+ }
+ return 0;
+}
+
+
+/*
+ * This routine returns pointer to memory which is owned by the plugin, so don't
+ * free it. Gets freed by the next call to this routine, or when the indexer
+ * is destroyed
+ */
+int
+matchrule_values_to_keys(Slapi_PBlock *pb,struct berval **input_values,struct berval ***output_values)
+{
+ IFP mrINDEX = NULL;
+
+ slapi_pblock_get (pb, SLAPI_PLUGIN_MR_INDEX_FN, &mrINDEX);
+ slapi_pblock_set (pb, SLAPI_PLUGIN_MR_VALUES, input_values);
+ mrINDEX (pb);
+ slapi_pblock_get (pb, SLAPI_PLUGIN_MR_KEYS, output_values);
+ return 0;
+}
+
+/*
+ * This routine returns pointer to memory which is owned by the plugin, so don't
+ * free it. Gets freed by the next call to this routine, or when the indexer
+ * is destroyed
+ */
+int
+matchrule_values_to_keys_sv(Slapi_PBlock *pb,Slapi_Value **input_values,Slapi_Value ***output_values)
+{
+ IFP mrINDEX = NULL;
+ struct berval **bvi, **bvo;
+
+ valuearray_get_bervalarray(input_values, &bvi);
+
+ slapi_pblock_get (pb, SLAPI_PLUGIN_MR_INDEX_FN, &mrINDEX);
+ slapi_pblock_set (pb, SLAPI_PLUGIN_MR_VALUES, bvi);
+ mrINDEX (pb);
+ slapi_pblock_get (pb, SLAPI_PLUGIN_MR_KEYS, &bvo);
+
+ slapi_pblock_set (pb, SLAPI_PLUGIN_MR_VALUES, NULL);
+ ber_bvecfree(bvi);
+
+ valuearray_init_bervalarray(bvo, output_values);
+ return 0;
+}
diff --git a/ldap/servers/slapd/back-ldbm/misc.c b/ldap/servers/slapd/back-ldbm/misc.c
new file mode 100644
index 00000000..b2a8d6de
--- /dev/null
+++ b/ldap/servers/slapd/back-ldbm/misc.c
@@ -0,0 +1,356 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+/* misc.c - backend misc routines */
+
+#include "back-ldbm.h"
+
+/* Takes a return code supposed to be errno or from lidb
+ which we don't expect to see and prints a handy log message */
+void ldbm_nasty(const char* str, int c, int err)
+{
+ char *msg = NULL;
+ char buffer[200];
+ if (err == DB_LOCK_DEADLOCK) {
+ sprintf(buffer,"%s WARNING %d",str,c);
+ LDAPDebug(LDAP_DEBUG_TRACE,"%s, err=%d %s\n",
+ buffer,err,(msg = dblayer_strerror( err )) ? msg : "");
+ } else if (err == DB_RUNRECOVERY) {
+ LDAPDebug(LDAP_DEBUG_ANY,"FATAL ERROR at %s (%d); server stopping as database recovery needed.\n", str,c,0);
+ exit(1);
+ } else {
+ sprintf(buffer,"%s BAD %d",str,c);
+ LDAPDebug(LDAP_DEBUG_ANY,"%s, err=%d %s\n",
+ buffer,err,(msg = dblayer_strerror( err )) ? msg : "");
+ }
+}
+
+/* Put a message in the access log, complete with connection ID and operation ID */
+void ldbm_log_access_message(Slapi_PBlock *pblock,char *string)
+{
+ int ret = 0;
+ int connection_id = 0;
+ int operation_id = 0;
+ Operation *operation = NULL; /* DBDB this is sneaky---opid should be covered by the API directly */
+
+ ret = slapi_pblock_get(pblock,SLAPI_OPERATION,&operation);
+ if (0 != ret) {
+ return;
+ }
+ ret = slapi_pblock_get(pblock,SLAPI_CONN_ID,&connection_id);
+ if (0 != ret) {
+ return;
+ }
+ operation_id = operation->o_opid;
+ slapi_log_access( LDAP_DEBUG_STATS, "conn=%d op=%d %s\n",connection_id, operation_id,string);
+}
+
+int return_on_disk_full(struct ldbminfo *li)
+{
+ dblayer_remember_disk_filled(li);
+ return SLAPI_FAIL_DISKFULL;
+}
+
+
+/* System Indexes */
+
+static const char *systemIndexes[] = {
+ "entrydn",
+ "parentid",
+ "objectclass",
+ "aci",
+ "numsubordinates",
+ SLAPI_ATTR_UNIQUEID,
+ SLAPI_ATTR_NSCP_ENTRYDN,
+ ATTR_NSDS5_REPLCONFLICT,
+ NULL
+};
+
+int
+ldbm_attribute_always_indexed(const char *attrtype)
+{
+ int r= 0;
+ if(NULL != attrtype)
+ {
+ int i=0;
+ while (!r && systemIndexes[i] != NULL)
+ {
+ if(!strcasecmp(attrtype,systemIndexes[i]))
+ {
+ r= 1;
+ }
+ i++;
+ }
+ }
+ return(r);
+}
+
+
+
+/*
+ * Given an entry dn and a uniqueid, compute the
+ * DN of the entry's tombstone. Returns a pointer
+ * to an allocated block of memory.
+ */
+char *
+compute_entry_tombstone_dn(const char *entrydn, const char *uniqueid)
+{
+ const char *tombstone_dn_pattern = "%s=%s, %s";
+ char *tombstone_dn;
+
+ PR_ASSERT(NULL != entrydn);
+ PR_ASSERT(NULL != uniqueid);
+
+ tombstone_dn = slapi_ch_malloc(strlen(SLAPI_ATTR_UNIQUEID) +
+ strlen(tombstone_dn_pattern) +
+ strlen(uniqueid) +
+ strlen(entrydn) + 1);
+ sprintf(tombstone_dn, tombstone_dn_pattern,
+ SLAPI_ATTR_UNIQUEID,
+ uniqueid,
+ entrydn);
+ return tombstone_dn;
+}
+
+
+/* mark a backend instance "busy"
+ * returns 0 on success, -1 if the instance is ALREADY busy
+ */
+int instance_set_busy(ldbm_instance *inst)
+{
+ PR_Lock(inst->inst_config_mutex);
+ if (inst->inst_flags & INST_FLAG_BUSY) {
+ PR_Unlock(inst->inst_config_mutex);
+ return -1;
+ }
+
+ inst->inst_flags |= INST_FLAG_BUSY;
+ PR_Unlock(inst->inst_config_mutex);
+ return 0;
+}
+
+int instance_set_busy_and_readonly(ldbm_instance *inst)
+{
+ PR_Lock(inst->inst_config_mutex);
+ if (inst->inst_flags & INST_FLAG_BUSY) {
+ PR_Unlock(inst->inst_config_mutex);
+ return -1;
+ }
+
+ inst->inst_flags |= INST_FLAG_BUSY;
+
+ /* save old readonly state */
+ if (slapi_be_get_readonly(inst->inst_be)) {
+ inst->inst_flags |= INST_FLAG_READONLY;
+ } else {
+ inst->inst_flags &= ~INST_FLAG_READONLY;
+ }
+ slapi_mtn_be_set_readonly(inst->inst_be, 1);
+
+ PR_Unlock(inst->inst_config_mutex);
+ return 0;
+}
+
+/* mark a backend instance to be not "busy" anymore */
+void instance_set_not_busy(ldbm_instance *inst)
+{
+ int readonly;
+
+ PR_Lock(inst->inst_config_mutex);
+ inst->inst_flags &= ~INST_FLAG_BUSY;
+ /* set backend readonly flag to match instance flags again
+ * (sometimes the instance changes the readonly status when it's busy)
+ */
+ readonly = (inst->inst_flags & INST_FLAG_READONLY ? 1 : 0);
+ slapi_mtn_be_set_readonly(inst->inst_be, readonly);
+ PR_Unlock(inst->inst_config_mutex);
+}
+
+void
+allinstance_set_not_busy(struct ldbminfo *li)
+{
+ ldbm_instance *inst;
+ Object *inst_obj;
+
+ /* server is up -- mark all backends busy */
+ for (inst_obj = objset_first_obj(li->li_instance_set); inst_obj;
+ inst_obj = objset_next_obj(li->li_instance_set, inst_obj)) {
+ inst = (ldbm_instance *)object_get_data(inst_obj);
+ instance_set_not_busy(inst);
+ }
+ if (inst_obj)
+ object_release(inst_obj);
+}
+
+void
+allinstance_set_busy(struct ldbminfo *li)
+{
+ ldbm_instance *inst;
+ Object *inst_obj;
+
+ /* server is up -- mark all backends busy */
+ for (inst_obj = objset_first_obj(li->li_instance_set); inst_obj;
+ inst_obj = objset_next_obj(li->li_instance_set, inst_obj)) {
+ inst = (ldbm_instance *)object_get_data(inst_obj);
+ instance_set_busy(inst);
+ }
+ if (inst_obj)
+ object_release(inst_obj);
+}
+
+int
+is_anyinstance_busy(struct ldbminfo *li)
+{
+ ldbm_instance *inst;
+ Object *inst_obj;
+ int rval = 0;
+
+ /* server is up -- mark all backends busy */
+ for (inst_obj = objset_first_obj(li->li_instance_set); inst_obj;
+ inst_obj = objset_next_obj(li->li_instance_set, inst_obj)) {
+ inst = (ldbm_instance *)object_get_data(inst_obj);
+ PR_Lock(inst->inst_config_mutex);
+ rval = inst->inst_flags & INST_FLAG_BUSY;
+ PR_Unlock(inst->inst_config_mutex);
+ if (0 != rval) {
+ break;
+ }
+ }
+ if (inst_obj)
+ object_release(inst_obj);
+ return rval;
+}
+
+/*
+ * delete the given file/directory and its sub files/directories
+ */
+int
+ldbm_delete_dirs(char *path)
+{
+ PRDir *dirhandle = NULL;
+ PRDirEntry *direntry = NULL;
+ char fullpath[MAXPATHLEN];
+ int rval = 0;
+ PRFileInfo info;
+
+ dirhandle = PR_OpenDir(path);
+ if (! dirhandle)
+ {
+ PR_Delete(path);
+ return 0;
+ }
+
+ while (NULL != (direntry =
+ PR_ReadDir(dirhandle, PR_SKIP_DOT | PR_SKIP_DOT_DOT)))
+ {
+ if (! direntry->name)
+ break;
+
+ sprintf(fullpath, "%s/%s", path, direntry->name);
+ rval = PR_GetFileInfo(fullpath, &info);
+ if (PR_SUCCESS == rval)
+ {
+ if (PR_FILE_DIRECTORY == info.type)
+ rval += ldbm_delete_dirs(fullpath);
+ }
+ if (PR_FILE_DIRECTORY != info.type)
+ PR_Delete(fullpath);
+ }
+ PR_CloseDir(dirhandle);
+ /* remove the directory itself too */
+ rval += PR_RmDir(path);
+ return rval;
+}
+
+char
+get_sep(char *path)
+{
+ if (NULL == path)
+ return '/'; /* default */
+ if (NULL != strchr(path, '/'))
+ return '/';
+ if (NULL != strchr(path, '\\'))
+ return '\\';
+ return '/'; /* default */
+}
+
+/* mkdir -p */
+int
+mkdir_p(char *dir, unsigned int mode)
+{
+ PRFileInfo info;
+ int rval;
+ char sep = get_sep(dir);
+
+ rval = PR_GetFileInfo(dir, &info);
+ if (PR_SUCCESS == rval)
+ {
+ if (PR_FILE_DIRECTORY != info.type) /* not a directory */
+ {
+ PR_Delete(dir);
+ if (PR_SUCCESS != PR_MkDir(dir, mode))
+ {
+ LDAPDebug(LDAP_DEBUG_ANY, "mkdir_p %s: error %d (%s)\n",
+ dir, PR_GetError(),slapd_pr_strerror(PR_GetError()));
+ return -1;
+ }
+ }
+ return 0;
+ }
+ else
+ {
+ /* does not exist */
+ char *p, *e;
+ char c[2] = {0, 0};
+ int len = strlen(dir);
+ rval = 0;
+
+ e = dir + len - 1;
+ if (*e == sep)
+ {
+ c[1] = *e;
+ *e = '\0';
+ }
+
+ c[0] = '/';
+ p = strrchr(dir, sep);
+ if (NULL != p)
+ {
+ *p = '\0';
+ rval = mkdir_p(dir, mode);
+ *p = c[0];
+ }
+ if (c[1])
+ *e = c[1];
+ if (0 != rval)
+ return rval;
+ if (PR_SUCCESS != PR_MkDir(dir, mode))
+ {
+ LDAPDebug(LDAP_DEBUG_ANY, "mkdir_p %s: error %d (%s)\n",
+ dir, PR_GetError(),slapd_pr_strerror(PR_GetError()));
+ return -1;
+ }
+ return 0;
+ }
+}
+
+int
+is_fullpath(char *path)
+{
+ int len;
+ if (NULL == path || '\0' == *path)
+ return 0;
+
+ if ('/' == *path || '\\' == *path)
+ return 1;
+
+ len = strlen(path);
+ if (len > 2)
+ {
+ if (':' == path[1] && ('/' == path[2] || '\\' == path[2])) /* Windows */
+ return 1;
+ }
+ return 0;
+}
diff --git a/ldap/servers/slapd/back-ldbm/monitor.c b/ldap/servers/slapd/back-ldbm/monitor.c
new file mode 100644
index 00000000..1c5a2960
--- /dev/null
+++ b/ldap/servers/slapd/back-ldbm/monitor.c
@@ -0,0 +1,274 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+/* monitor.c - ldbm backend monitor function */
+
+#include "back-ldbm.h"
+#include "dblayer.h" /* XXXmcs: not sure this is good to do... */
+#include <sys/stat.h>
+
+
+#define MSET(_attr) do { \
+ val.bv_val = buf; \
+ val.bv_len = strlen(buf); \
+ attrlist_replace(&e->e_attrs, (_attr), vals); \
+} while (0)
+
+#define MSETF(_attr, _x) do { \
+ char tmp_atype[37]; \
+ sprintf(tmp_atype, _attr, _x); \
+ MSET(tmp_atype); \
+} while (0)
+
+
+/* DSE callback to monitor stats for a particular instance */
+int ldbm_back_monitor_instance_search(Slapi_PBlock *pb, Slapi_Entry *e,
+ Slapi_Entry *entryAfter, int *returncode, char *returntext, void *arg)
+{
+ ldbm_instance *inst = (ldbm_instance *)arg;
+ struct ldbminfo *li = NULL;
+ struct berval val;
+ struct berval *vals[2];
+ char buf[BUFSIZ];
+ u_long hits, tries;
+ long nentries,maxentries;
+ size_t size,maxsize;
+/* NPCTE fix for bugid 544365, esc 0. <P.R> <04-Jul-2001> */
+ struct stat astat;
+/* end of NPCTE fix for bugid 544365 */
+ DB_MPOOL_FSTAT **mpfstat = NULL;
+ int i,j;
+
+ /* Get the LDBM Info structure for the ldbm backend */
+ if (inst->inst_be->be_database == NULL) {
+ *returncode= LDAP_OPERATIONS_ERROR;
+ return SLAPI_DSE_CALLBACK_ERROR;
+ }
+
+ li = (struct ldbminfo *)inst->inst_be->be_database->plg_private;
+ if (li == NULL) {
+ *returncode= LDAP_OPERATIONS_ERROR;
+ return SLAPI_DSE_CALLBACK_ERROR;
+ }
+
+ if (inst->inst_be->be_state != BE_STATE_STARTED)
+ {
+ *returncode = LDAP_SUCCESS;
+ return SLAPI_DSE_CALLBACK_OK;
+ }
+
+ vals[0] = &val;
+ vals[1] = NULL;
+
+ /* database name */
+ sprintf(buf, "%s", li->li_plugin->plg_name);
+ MSET("database");
+
+ /* read-only status */
+ sprintf( buf, "%d", inst->inst_be->be_readonly );
+ MSET("readOnly");
+
+ /* fetch cache statistics */
+ cache_get_stats(&(inst->inst_cache), &hits, &tries,
+ &nentries, &maxentries, &size, &maxsize);
+ sprintf(buf, "%lu", hits);
+ MSET("entryCacheHits");
+ sprintf(buf, "%lu", tries);
+ MSET("entryCacheTries");
+ sprintf(buf, "%lu", (unsigned long)(100.0*(double)hits / (double)(tries > 0 ? tries : 1)));
+ MSET("entryCacheHitRatio");
+ sprintf(buf, "%lu", size);
+ MSET("currentEntryCacheSize");
+ sprintf(buf, "%lu", maxsize);
+ MSET("maxEntryCacheSize");
+ sprintf(buf, "%ld", nentries);
+ MSET("currentEntryCacheCount");
+ sprintf(buf, "%ld", maxentries);
+ MSET("maxEntryCacheCount");
+
+#ifdef DEBUG
+ {
+ /* debugging for hash statistics */
+ char *x;
+ cache_debug_hash(&(inst->inst_cache), &x);
+ val.bv_val = x;
+ val.bv_len = strlen(x);
+ attrlist_replace(&e->e_attrs, "entrycache-hashtables", vals);
+ slapi_ch_free((void **)&x);
+ }
+#endif
+
+ if (dblayer_memp_stat(li, NULL, &mpfstat) != 0) {
+ *returncode = LDAP_OPERATIONS_ERROR;
+ return SLAPI_DSE_CALLBACK_ERROR;
+ }
+
+ for (i = 0;(mpfstat[i] && (mpfstat[i]->file_name != NULL)); i++) {
+#ifdef _WIN32
+ int fpos = 0;
+#endif
+ char *absolute_pathname = NULL;
+ size_t absolute_pathname_size = 0;
+
+ /* only print out stats on files used by this instance */
+ if (strlen(mpfstat[i]->file_name) < strlen(inst->inst_dir_name))
+ continue;
+ if (strncmp(mpfstat[i]->file_name, inst->inst_dir_name,
+ strlen(inst->inst_dir_name)) != 0)
+ continue;
+
+ /* Since the filenames are now relative, we need to construct an absolute version
+ * for the purpose of stat() etc below...
+ */
+ if (absolute_pathname) {
+ slapi_ch_free(&absolute_pathname);
+ }
+ absolute_pathname_size = strlen(inst->inst_parent_dir_name) + strlen(mpfstat[i]->file_name) + 2;
+ absolute_pathname = slapi_ch_malloc(absolute_pathname_size);
+ sprintf(absolute_pathname, "%s%c%s" , inst->inst_parent_dir_name, get_sep(inst->inst_parent_dir_name), mpfstat[i]->file_name );
+
+/* NPCTE fix for bugid 544365, esc 0. <P.R> <04-Jul-2001> */
+ /* Hide statistic of deleted files (mainly indexes) */
+ if (stat(absolute_pathname,&astat))
+ continue;
+ /* If the file has been re-created after been deleted
+ * We should show only statistics for the last instance
+ * Since SleepyCat returns the statistic of the last open file first,
+ * we should only display the first statistic record for a given file
+ */
+ for (j=0;j<i;j++)
+ if (!strcmp(mpfstat[i]->file_name,mpfstat[j]->file_name))
+ break;
+ if (j<i)
+ continue;
+/* end of NPCTE fix for bugid 544365 */
+
+ /* Get each file's stats */
+ sprintf(buf, "%s", mpfstat[i]->file_name);
+#ifdef _WIN32
+ /*
+ * For NT, switch the last
+ * backslash to a foward
+ * slash. - RJP
+ */
+ for (fpos = strlen(buf); fpos >= 0; fpos--) {
+ if (buf[fpos] == '\\') {
+ buf[fpos] = '/';
+ break;
+ }
+ }
+#endif
+ MSETF("dbFilename-%d", i);
+
+ sprintf(buf, "%u", mpfstat[i]->st_cache_hit);
+ MSETF("dbFileCacheHit-%d", i);
+ sprintf(buf, "%u", mpfstat[i]->st_cache_miss);
+ MSETF("dbFileCacheMiss-%d", i);
+ sprintf(buf, "%u", mpfstat[i]->st_page_in);
+ MSETF("dbFilePageIn-%d", i);
+ sprintf(buf, "%u", mpfstat[i]->st_page_out);
+ MSETF("dbFilePageOut-%d", i);
+
+ if (absolute_pathname) {
+ slapi_ch_free(&absolute_pathname);
+ }
+
+ }
+
+#if 1000*DB_VERSION_MAJOR + 100*DB_VERSION_MINOR + DB_VERSION_PATCH <= 3204
+ /* In DB 3.2.4 and earlier, we need to free each element */
+ for (i = 0; mpfstat[i]; i++)
+ free(mpfstat[i]);
+#endif
+ free(mpfstat);
+
+ *returncode = LDAP_SUCCESS;
+ return SLAPI_DSE_CALLBACK_OK;
+
+}
+
+
+/* monitor global ldbm stats */
+int ldbm_back_monitor_search(Slapi_PBlock *pb, Slapi_Entry *e,
+ Slapi_Entry *entryAfter, int *returncode, char *returntext, void *arg)
+{
+ struct ldbminfo *li = (struct ldbminfo *)arg;
+ struct berval val;
+ struct berval *vals[2];
+ char buf[BUFSIZ];
+ DB_MPOOL_STAT *mpstat = NULL;
+ DB_MPOOL_FSTAT **mpfstat = NULL;
+ u_int32_t cache_tries;
+
+ vals[0] = &val;
+ vals[1] = NULL;
+
+ /* database name */
+ sprintf(buf, "%s", li->li_plugin->plg_name);
+ MSET("database");
+
+ /* we have to ask for file stats in order to get correct global stats */
+ if (dblayer_memp_stat(li, &mpstat, &mpfstat) != 0) {
+ *returncode = LDAP_OPERATIONS_ERROR;
+ return SLAPI_DSE_CALLBACK_ERROR;
+ }
+
+ /* cache hits*/
+ sprintf(buf, "%u", mpstat->st_cache_hit);
+ MSET("dbCacheHits");
+
+ /* cache tries*/
+ cache_tries = (mpstat->st_cache_miss + mpstat->st_cache_hit);
+ sprintf(buf, "%u", cache_tries);
+ MSET("dbCacheTries");
+
+ /* cache hit ratio*/
+ sprintf(buf, "%lu", (unsigned long)(100.0 * (double)mpstat->st_cache_hit / (double)(cache_tries > 0 ? cache_tries : 1) ));
+ MSET("dbCacheHitRatio");
+
+ sprintf(buf, "%u", mpstat->st_page_in);
+ MSET("dbCachePageIn");
+ sprintf(buf, "%u", mpstat->st_page_out);
+ MSET("dbCachePageOut");
+ sprintf(buf, "%u", mpstat->st_ro_evict);
+ MSET("dbCacheROEvict");
+ sprintf(buf, "%u", mpstat->st_rw_evict);
+ MSET("dbCacheRWEvict");
+
+ free(mpstat);
+
+ if (mpfstat) {
+#if 1000*DB_VERSION_MAJOR + 100*DB_VERSION_MINOR + DB_VERSION_PATCH <= 3204
+ /* In DB 3.2.4 and earlier, we need to free each element */
+ int i;
+ for (i = 0; mpfstat[i]; i++)
+ free(mpfstat[i]);
+#endif
+ free(mpfstat);
+ }
+
+ *returncode = LDAP_SUCCESS;
+ return SLAPI_DSE_CALLBACK_OK;
+}
+
+
+/* monitor global ldbm database stats */
+int
+ldbm_back_dbmonitor_search(Slapi_PBlock *pb, Slapi_Entry *e,
+ Slapi_Entry *entryAfter, int *returncode, char *returntext, void *arg)
+{
+ dblayer_private *dbpriv = NULL;
+ struct ldbminfo *li = NULL;
+
+ PR_ASSERT(NULL != arg);
+ li = (struct ldbminfo*)arg;
+ dbpriv = (dblayer_private*)li->li_dblayer_private;
+ PR_ASSERT(NULL != dbpriv);
+
+ perfctrs_as_entry( e, dbpriv->perf_private, dbpriv->dblayer_env->dblayer_DB_ENV);
+
+ *returncode = LDAP_SUCCESS;
+ return SLAPI_DSE_CALLBACK_OK;
+}
diff --git a/ldap/servers/slapd/back-ldbm/nextid.c b/ldap/servers/slapd/back-ldbm/nextid.c
new file mode 100644
index 00000000..12773768
--- /dev/null
+++ b/ldap/servers/slapd/back-ldbm/nextid.c
@@ -0,0 +1,204 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+/* id.c - keep track of the next id to be given out */
+
+#include "back-ldbm.h"
+
+ID
+next_id(backend *be)
+{
+ ldbm_instance *inst = (ldbm_instance *) be->be_instance_info;
+ ID id;
+
+ /*Lock*/
+ PR_Lock( inst->inst_nextid_mutex );
+
+ /*Test if nextid hasn't been initialized. */
+ if (inst->inst_nextid < 1) {
+ LDAPDebug( LDAP_DEBUG_ANY,
+ "ldbm backend instance: nextid not initialized... exiting.\n", 0,0,0);
+ exit(1);
+ }
+
+ /*Increment the in-memory nextid*/
+ inst->inst_nextid++;
+
+ id = inst->inst_nextid - 1;
+
+ /*unlock*/
+ PR_Unlock( inst->inst_nextid_mutex );
+
+ /* if ID is above the threshold, the database may need rebuilding soon */
+ if (id >= ID_WARNING_THRESHOLD) {
+ if ( id >= MAXID ) {
+ LDAPDebug( LDAP_DEBUG_ANY,
+ "ldbm backend instance: FATAL ERROR: backend '%s' has no"
+ "IDs left. DATABASE MUST BE REBUILT.\n", be->be_name, 0,
+ 0);
+ id = MAXID;
+ } else {
+ LDAPDebug( LDAP_DEBUG_ANY,
+ "ldbm backend instance: WARNING: backend '%s' may run out "
+ "of IDs. Please, rebuild database.\n", be->be_name, 0, 0);
+ }
+ }
+ return( id );
+}
+
+void
+next_id_return( backend *be, ID id )
+{
+ ldbm_instance *inst = (ldbm_instance *) be->be_instance_info;
+
+ /*Lock*/
+ PR_Lock( inst->inst_nextid_mutex );
+
+ /*Test if nextid hasn't been initialized. */
+ if (inst->inst_nextid < 1) {
+ LDAPDebug( LDAP_DEBUG_ANY,
+ "ldbm backend instance: nextid not initialized... exiting\n", 0,0,0);
+ exit(1);
+ }
+
+ if ( id != inst->inst_nextid - 1 ) {
+ PR_Unlock( inst->inst_nextid_mutex );
+ return;
+ }
+
+ /*decrement the in-memory version*/
+ inst->inst_nextid--;
+
+ /*unlock this bad boy*/
+ PR_Unlock( inst->inst_nextid_mutex );
+}
+
+ID
+next_id_get( backend *be )
+{
+ ldbm_instance *inst = (ldbm_instance *) be->be_instance_info;
+ ID id;
+
+ /*lock*/
+ PR_Lock( inst->inst_nextid_mutex );
+
+ /*Test if nextid hasn't been initialized.*/
+ if (inst->inst_nextid < 1) {
+ LDAPDebug( LDAP_DEBUG_ANY,
+ "ldbm backend instance: nextid not initialized... exiting\n", 0,0,0);
+ exit(1);
+ }
+
+ id = inst->inst_nextid;
+ PR_Unlock( inst->inst_nextid_mutex );
+
+ return( id );
+}
+
+/*
+ * Function: get_ids_from_disk
+ *
+ * Returns: squat
+ *
+ * Description: Opend the id2entry file and obtains the largest
+ * ID in use, and sets li->li_nextid. If no IDs
+ * could be read from id2entry, li->li_nextid
+ * is set to 1.
+ */
+void
+get_ids_from_disk(backend *be)
+{
+ ldbm_instance *inst = (ldbm_instance *) be->be_instance_info;
+ DB *id2entrydb; /*the id2entry database*/
+ int return_value = -1;
+
+ /*For the nextid, we go directly to the id2entry database,
+ and grab the max ID*/
+
+ /*Get a copy of the id2entry database*/
+ if ( (return_value = dblayer_get_id2entry( be, &id2entrydb )) != 0 ) {
+ id2entrydb = NULL;
+ }
+
+ /* lock the nextid mutex*/
+ PR_Lock( inst->inst_nextid_mutex );
+
+ /*
+ * If there is no id2entry database, then we can assume that there
+ * are no entries, and that nextid should be 1
+ */
+ if (id2entrydb == NULL) {
+ inst->inst_nextid = 1;
+
+ /* unlock */
+ PR_Unlock( inst->inst_nextid_mutex );
+ return;
+
+ } else {
+
+ /*Get the last key*/
+ DBC *dbc = NULL;
+ DBT key = {0}; /*For the nextid*/
+ DBT Value = {0};
+ Value.flags = DB_DBT_MALLOC;
+ key.flags = DB_DBT_MALLOC;
+ return_value = id2entrydb->cursor(id2entrydb,NULL,&dbc,0);
+ if (0 == return_value) {
+ return_value = dbc->c_get(dbc,&key,&Value,DB_LAST);
+ if (0 == return_value) {
+ inst->inst_nextid = id_stored_to_internal(key.dptr) + 1;
+ }
+ if (NULL != key.data) {
+ free(key.data);
+ }
+ if (NULL != Value.data) {
+ free(Value.data);
+ }
+ dbc->c_close(dbc);
+ }
+ if ( (key.dptr == NULL) || (0 != return_value) ) {
+ inst->inst_nextid = 1;
+
+ /*close the cache*/
+ dblayer_release_id2entry( be, id2entrydb );
+
+ /* unlock */
+ PR_Unlock( inst->inst_nextid_mutex );
+ return;
+ }
+
+ }
+
+ /*close the cache*/
+ dblayer_release_id2entry( be, id2entrydb );
+
+ /* unlock */
+ PR_Unlock( inst->inst_nextid_mutex );
+}
+
+
+/* routines to turn an internal machine-representation ID into the one we store (big-endian) */
+
+void id_internal_to_stored(ID i,char *b)
+{
+ if ( sizeof(ID) > 4 ) {
+ memset (b+4, 0, sizeof(ID)-4);
+ }
+
+ b[0] = (char)(i >> 24);
+ b[1] = (char)(i >> 16);
+ b[2] = (char)(i >> 8);
+ b[3] = (char)i;
+}
+
+ID id_stored_to_internal(char* b)
+{
+ ID i;
+ i = (ID)b[3] & 0x000000ff;
+ i |= (((ID)b[2]) << 8) & 0x0000ff00;
+ i |= (((ID)b[1]) << 16) & 0x00ff0000;
+ i |= ((ID)b[0]) << 24;
+ return i;
+}
diff --git a/ldap/servers/slapd/back-ldbm/parents.c b/ldap/servers/slapd/back-ldbm/parents.c
new file mode 100644
index 00000000..80fec7ac
--- /dev/null
+++ b/ldap/servers/slapd/back-ldbm/parents.c
@@ -0,0 +1,105 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+/* parents.c - where the adults live */
+
+#include "back-ldbm.h"
+
+char *numsubordinates = "numsubordinates";
+char *hassubordinates = "hassubordinates";
+
+/* Routine where any in-memory modification of a parent entry happens on some state-change in
+ one of its children. vaid op values are: 1 == child entry newly added, 2 == child entry about to be
+ deleted; 3 == child modified, in which case childmods points to the modifications. The child entry
+ passed is in the state which reflects the mods having been appied. op ==3 HAS NOT BEEN IMPLEMENTED YET
+ The routine is allowed to modify the parent entry, and to return a set of LDAPMods reflecting
+ the changes it made. The LDAPMods array must be freed by the called by calling ldap_free_mods(p,1)
+
+ */
+int parent_update_on_childchange(modify_context *mc,int op, size_t *new_sub_count )
+{
+ int ret = 0;
+ int mod_op = 0;
+ Slapi_Attr *read_attr = NULL;
+ size_t current_sub_count = 0;
+ int already_present = 0;
+
+ if (new_sub_count)
+ *new_sub_count = 0;
+
+ /* Check nobody is trying to use op == 3, it's not implemented yet */
+ PR_ASSERT( (op == 1) || (op == 2));
+
+ /* We want to invent a mods set to be passed to modify_apply_mods() */
+
+ /* For now, we're only interested in subordinatecount.
+ We first examine the present value for the attribute.
+ If it isn't present and we're adding, we assign value 1 to the attribute and add it.
+ If it is present, we increment or decrement depending upon whether we're adding or deleting.
+ If the value after decrementing is zero, we remove it.
+ */
+
+ /* Get the present value of the subcount attr, or 0 if not present */
+ ret = slapi_entry_attr_find(mc->old_entry->ep_entry,numsubordinates,&read_attr);
+ if (0 == ret) {
+ /* decode the value */
+ Slapi_Value *sval;
+ slapi_attr_first_value( read_attr, &sval );
+ if (sval!=NULL) {
+ const struct berval *bval = slapi_value_get_berval(sval);
+ if(NULL != bval) {
+ already_present = 1;
+ current_sub_count = atol(bval->bv_val);
+ }
+ }
+ }
+ /* are we adding ? */
+ if ( (1 == op) && !already_present) {
+ /* If so, and the parent entry does not already have a subcount attribute, we need to add it */
+ mod_op = LDAP_MOD_ADD;
+ } else {
+ if (2 == op) {
+ if (!already_present) {
+ /* This means that something is wrong---deleting a child but no subcount present on parent */
+ LDAPDebug( LDAP_DEBUG_ANY, "numsubordinates assertion failure\n", 0, 0, 0 );
+ return -1;
+ } else {
+ if (current_sub_count == 1) {
+ mod_op = LDAP_MOD_DELETE;
+ } else {
+ mod_op = LDAP_MOD_REPLACE;
+ }
+ }
+ } else {
+ mod_op = LDAP_MOD_REPLACE;
+ }
+ }
+
+ /* Mow compute the new value */
+ if (1 == op) {
+ current_sub_count++;
+ } else {
+ current_sub_count--;
+ }
+
+ {
+ Slapi_Mods *smods= slapi_mods_new();
+ if (mod_op == LDAP_MOD_DELETE)
+ {
+ slapi_mods_add(smods, mod_op | LDAP_MOD_BVALUES, numsubordinates, 0, NULL);
+ }
+ else
+ {
+ char value_buffer[20]; /* enough digits for 2^64 children */
+ sprintf(value_buffer,"%lu", current_sub_count);
+ slapi_mods_add(smods, mod_op | LDAP_MOD_BVALUES, numsubordinates, strlen(value_buffer), value_buffer);
+ }
+ ret = modify_apply_mods(mc,smods); /* smods passed in */
+ }
+
+ if (new_sub_count)
+ *new_sub_count = current_sub_count;
+ return ret;
+}
diff --git a/ldap/servers/slapd/back-ldbm/perfctrs.c b/ldap/servers/slapd/back-ldbm/perfctrs.c
new file mode 100644
index 00000000..0457e170
--- /dev/null
+++ b/ldap/servers/slapd/back-ldbm/perfctrs.c
@@ -0,0 +1,444 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+/* Database performance counters stuff */
+#include "back-ldbm.h"
+
+#include "perfctrs.h"
+
+#if 1000*DB_VERSION_MAJOR + 100*DB_VERSION_MINOR >= 4000
+#define TXN_STAT(env, statp, flags, malloc) \
+ (env)->txn_stat((env), (statp), (flags))
+#define MEMP_STAT(env, gsp, fsp, flags, malloc) \
+ (env)->memp_stat((env), (gsp), (fsp), (flags))
+#define LOG_STAT(env, spp, flags, malloc) (env)->log_stat((env), (spp), (flags))
+#define LOCK_STAT(env, statp, flags, malloc) \
+ (env)->lock_stat((env), (statp), (flags))
+
+#else /* older than db 4.0 */
+#if 1000*DB_VERSION_MAJOR + 100*DB_VERSION_MINOR >= 3300
+#define TXN_STAT(env, statp, flags, malloc) txn_stat((env), (statp))
+#define MEMP_STAT(env, gsp, fsp, flags, malloc) memp_stat((env), (gsp), (fsp))
+#define LOG_STAT(env, spp, flags, malloc) log_stat((env), (spp))
+#define LOCK_STAT(env, statp, flags, malloc) lock_stat((env), (statp))
+
+#else /* older than db 3.3 */
+#define TXN_STAT(env, statp, flags, malloc) txn_stat((env), (statp), (malloc))
+#define MEMP_STAT(env, gsp, fsp, flags, malloc)
+ memp_stat((env), (gsp), (fsp), (malloc))
+#define LOG_STAT(env, spp, flags, malloc) log_stat((env), (spp), (malloc))
+#define LOCK_STAT(env, statp, flags, malloc) lock_stat((env), (statp), (malloc))
+#endif
+#endif
+
+static void perfctrs_update(perfctrs_private *priv, DB_ENV *db_env);
+static void perfctr_add_to_entry( Slapi_Entry *e, char *type,
+ PRUint32 countervalue );
+
+/*
+ * Win32 specific code (to support the Windows NT/2000 Performance Monitor).
+ */
+#if defined(_WIN32)
+static
+char * string_concatenate(char *a, char* b)
+{
+ size_t string_length = 0;
+ char *string = NULL;
+
+ string_length = strlen(a) + strlen(b) + 1;
+ string = malloc(string_length);
+ if (NULL == string) {
+ return string;
+ }
+ sprintf(string,"%s%s",a,b);
+ return string;
+}
+
+static void init_shared_memory(perfctrs_private *priv)
+{
+ performance_counters *perf = (performance_counters*)priv->memory;
+ if (NULL != perf) {
+ memset(perf,sizeof(performance_counters),0);
+ }
+}
+
+static int open_event(char *name, perfctrs_private *priv)
+{
+ HANDLE hEvent = INVALID_HANDLE_VALUE;
+
+ hEvent = OpenEvent(EVENT_ALL_ACCESS,FALSE,name);
+ if (NULL == hEvent) {
+ hEvent = CreateEvent(NULL,FALSE,FALSE,name);
+ if (NULL == hEvent) {
+ LDAPDebug(LDAP_DEBUG_ANY,"BAD EV 1, err=%d\n",GetLastError(),0,0);
+ return -1;
+ }
+ }
+ priv->hEvent = hEvent;
+ return 0;
+}
+
+static int open_shared_memory(char *name, perfctrs_private *priv)
+{
+ HANDLE hMapping = INVALID_HANDLE_VALUE;
+ void *pMemory = NULL;
+ /* We fear a bug in NT where it fails to attach to an existing region on calling CreateFileMapping, so let's call OpenFileMapping first */
+ hMapping = OpenFileMapping(FILE_MAP_ALL_ACCESS,FALSE,name);
+ if (NULL == hMapping) {
+ hMapping = CreateFileMapping((HANDLE)0xFFFFFFFF,NULL,PAGE_READWRITE,0,sizeof(performance_counters),name);
+ if (NULL == hMapping) {
+ LDAPDebug(LDAP_DEBUG_ANY,"BAD MAP 1, err=%d\n",GetLastError(),0,0);
+ return -1;
+ }
+ }
+ /* If we got to here, we have the mapping object open */
+ pMemory = MapViewOfFile(hMapping,FILE_MAP_ALL_ACCESS,0,0,0);
+ if (NULL == pMemory) {
+ LDAPDebug(LDAP_DEBUG_ANY,"BAD MAP 2, err=%d\n",GetLastError(),0,0);
+ return -1;
+ }
+ priv->memory = pMemory;
+ priv->hMemory = hMapping;
+ return 0;
+}
+#endif
+
+/* Init perf ctrs */
+void perfctrs_init(struct ldbminfo *li, perfctrs_private **ret_priv)
+{
+ perfctrs_private *priv = NULL;
+
+#if defined(_WIN32)
+ /* XXX What's my instance name ? */
+
+ /*
+ * We have a single DB environment for all backend databases.
+ * Therefore the instance name can be the server instance name.
+ * To match the db perf ctr DLL the instance name should be the
+ * name of a key defined in the registry under:
+ * HKEY_LOCAL_MACHINE\SOFTWARE\Netscape\Directory\5
+ * i.e. slapd-servername
+ */
+
+ char *string = NULL;
+ char *instance_name = li->li_plugin->plg_name; /* XXX does not identify server instance */
+#endif
+
+ *ret_priv = NULL;
+
+#if defined(_WIN32)
+ /*
+ * On Windows, the performance counters reside in shared memory.
+ */
+ if (NULL == instance_name) {
+ return;
+ }
+ /* Invent the name for the shared memory region */
+ string = string_concatenate(instance_name,PERFCTRS_REGION_SUFFIX);
+ if (NULL == string) {
+ return;
+ }
+#endif
+
+ /*
+ * We need the perfctrs_private area on all platforms.
+ */
+ priv = calloc(1,sizeof(perfctrs_private));
+ if (NULL == priv) {
+ return;
+ }
+
+#if defined(_WIN32)
+ /* Try to open the shared memory region */
+ open_shared_memory(string,priv);
+ free(string);
+ /* Invent the name for the update mutex */
+ string = string_concatenate(instance_name,PERFCTRS_MUTEX_SUFFIX);
+ if (NULL == string) {
+ return;
+ }
+ open_event(string,priv);
+ free(string);
+ init_shared_memory(priv);
+
+#else
+ /*
+ * On other platforms, the performance counters reside in regular memory.
+ */
+ if ( NULL == ( priv->memory = calloc( 1, sizeof( performance_counters )))) {
+ return;
+ }
+#endif
+
+ *ret_priv = priv;
+}
+
+/* Terminate perf ctrs */
+void perfctrs_terminate(perfctrs_private **priv)
+{
+#if defined(_WIN32)
+ if (NULL != (*priv)->memory) {
+ UnmapViewOfFile((*priv)->memory);
+ }
+ if (NULL != (*priv)->hMemory) {
+ CloseHandle((*priv)->hMemory);
+ }
+ if (NULL != (*priv)->hEvent) {
+ CloseHandle((*priv)->hEvent);
+ }
+#else
+ if (NULL != (*priv)->memory) {
+ free((*priv)->memory);
+ }
+#endif
+
+ free( (*priv) );
+ (*priv) = NULL;
+}
+
+/* Wait while checking for perfctr update requests */
+void perfctrs_wait(size_t milliseconds,perfctrs_private *priv,DB_ENV *db_env)
+{
+#if defined(_WIN32)
+ if (NULL != priv) {
+ DWORD ret = 0;
+ if (NULL != priv->hEvent) {
+ /* Sleep waiting on the perfctrs update event */
+ ret = WaitForSingleObject(priv->hEvent,milliseconds);
+ /* If we didn't time out, update the perfctrs */
+ if (ret == WAIT_OBJECT_0) {
+ perfctrs_update(priv,db_env);
+ }
+ } else {
+ Sleep(milliseconds);
+ }
+ }
+#else
+ /* Just sleep */
+ PRIntervalTime interval; /*NSPR timeout stuffy*/
+ interval = PR_MillisecondsToInterval(milliseconds);
+ DS_Sleep(interval);
+#endif
+}
+
+/* Update perfctrs */
+static
+void perfctrs_update(perfctrs_private *priv, DB_ENV *db_env)
+{
+ int ret = 0;
+ performance_counters *perf;
+ if (NULL == priv) {
+ return;
+ }
+ if (NULL == db_env) {
+ return;
+ }
+ perf = (performance_counters*)priv->memory;
+ if (NULL == perf) {
+ return;
+ }
+ /* Call libdb to get the various stats */
+ if (NULL != db_env->lg_handle)
+ {
+ DB_LOG_STAT *logstat = NULL;
+ ret = LOG_STAT(db_env,&logstat,0,malloc);
+ if (0 == ret) {
+ perf->log_region_wait_rate = logstat->st_region_wait;
+ perf->log_write_rate = 1024*1024*logstat->st_w_mbytes + logstat->st_w_bytes;
+ perf->log_bytes_since_checkpoint = 1024*1024*logstat->st_wc_mbytes + logstat->st_wc_bytes;
+ }
+ free(logstat);
+ }
+ if (NULL != db_env->tx_handle)
+ {
+ DB_TXN_STAT *txnstat = NULL;
+ ret = TXN_STAT(db_env, &txnstat, 0, malloc);
+ if (0 == ret) {
+ perf->active_txns = txnstat->st_nactive;
+ perf->commit_rate = txnstat->st_ncommits;
+ perf->abort_rate = txnstat->st_naborts;
+ perf->txn_region_wait_rate = txnstat->st_region_wait;
+ }
+ if (txnstat)
+ free(txnstat);
+ }
+ if (NULL != db_env->lk_handle)
+ {
+ DB_LOCK_STAT *lockstat = NULL;
+ ret = LOCK_STAT(db_env,&lockstat,0,malloc);
+ if (0 == ret) {
+ perf->lock_region_wait_rate = lockstat->st_region_wait;
+ perf->deadlock_rate = lockstat->st_ndeadlocks;
+ perf->configured_locks = lockstat->st_maxlocks;
+ perf->current_locks = lockstat->st_nlocks;
+ perf->max_locks = lockstat->st_maxnlocks;
+ perf->lockers = lockstat->st_nlockers;
+ perf->lock_conflicts = lockstat->st_nconflicts;
+ perf->lock_request_rate = lockstat->st_nrequests;
+ perf->current_lock_objects = lockstat->st_nobjects;
+ perf->max_lock_objects = lockstat->st_maxnobjects;
+ }
+ free(lockstat);
+ }
+ if (NULL != db_env->mp_handle)
+ {
+ DB_MPOOL_STAT *mpstat = NULL;
+ ret = MEMP_STAT(db_env,&mpstat,NULL,0,malloc);
+ if (0 == ret) {
+#define ONEG 1073741824
+ perf->cache_size_bytes = mpstat->st_gbytes * ONEG + mpstat->st_bytes;
+ perf->page_access_rate = mpstat->st_cache_hit + mpstat->st_cache_miss;
+ perf->cache_hit = mpstat->st_cache_hit;
+ perf->cache_try = mpstat->st_cache_hit + mpstat->st_cache_miss;
+ perf->page_create_rate = mpstat->st_page_create;
+ perf->page_read_rate = mpstat->st_page_in;
+ perf->page_write_rate = mpstat->st_page_out;
+ perf->page_ro_evict_rate = mpstat->st_ro_evict;
+ perf->page_rw_evict_rate = mpstat->st_rw_evict;
+ perf->hash_buckets = mpstat->st_hash_buckets;
+ perf->hash_search_rate = mpstat->st_hash_searches;
+ perf->longest_chain_length = mpstat->st_hash_longest;
+ perf->hash_elements_examine_rate = mpstat->st_hash_examined;
+ perf->pages_in_use = mpstat->st_page_dirty + mpstat->st_page_clean;
+ perf->dirty_pages = mpstat->st_page_dirty;
+ perf->clean_pages = mpstat->st_page_clean;
+ perf->page_trickle_rate = mpstat->st_page_trickle;
+ perf->cache_region_wait_rate = mpstat->st_region_wait;
+ free(mpstat);
+ }
+ }
+ /* Place the stats in the shared memory region */
+ /* Bump the sequence number */
+ perf->sequence_number++;
+}
+
+
+
+/*
+ * Define a map (array of structures) which is used to retrieve performance
+ * counters from the performance_counters structure and map them to an
+ * LDAP attribute type.
+ */
+
+#define SLAPI_LDBM_PERFCTR_AT_PREFIX "nsslapd-db-"
+typedef struct slapi_ldbm_perfctr_at_map {
+ char *pam_type; /* name of LDAP attribute type */
+ size_t pam_offset; /* offset into performance_counters struct */
+} SlapiLDBMPerfctrATMap;
+
+static SlapiLDBMPerfctrATMap perfctr_at_map[] = {
+ { SLAPI_LDBM_PERFCTR_AT_PREFIX "abort-rate",
+ offsetof( performance_counters, abort_rate ) },
+ { SLAPI_LDBM_PERFCTR_AT_PREFIX "active-txns",
+ offsetof( performance_counters, active_txns ) },
+ { SLAPI_LDBM_PERFCTR_AT_PREFIX "cache-hit",
+ offsetof( performance_counters, cache_hit ) },
+ { SLAPI_LDBM_PERFCTR_AT_PREFIX "cache-try",
+ offsetof( performance_counters, cache_try ) },
+ { SLAPI_LDBM_PERFCTR_AT_PREFIX "cache-region-wait-rate",
+ offsetof( performance_counters, cache_region_wait_rate ) },
+ { SLAPI_LDBM_PERFCTR_AT_PREFIX "cache-size-bytes",
+ offsetof( performance_counters, cache_size_bytes ) },
+ { SLAPI_LDBM_PERFCTR_AT_PREFIX "clean-pages",
+ offsetof( performance_counters, clean_pages ) },
+ { SLAPI_LDBM_PERFCTR_AT_PREFIX "commit-rate",
+ offsetof( performance_counters, commit_rate ) },
+ { SLAPI_LDBM_PERFCTR_AT_PREFIX "deadlock-rate",
+ offsetof( performance_counters, deadlock_rate ) },
+ { SLAPI_LDBM_PERFCTR_AT_PREFIX "dirty-pages",
+ offsetof( performance_counters, dirty_pages ) },
+ { SLAPI_LDBM_PERFCTR_AT_PREFIX "hash-buckets",
+ offsetof( performance_counters, hash_buckets ) },
+ { SLAPI_LDBM_PERFCTR_AT_PREFIX "hash-elements-examine-rate",
+ offsetof( performance_counters, hash_elements_examine_rate ) },
+ { SLAPI_LDBM_PERFCTR_AT_PREFIX "hash-search-rate",
+ offsetof( performance_counters, hash_search_rate ) },
+ { SLAPI_LDBM_PERFCTR_AT_PREFIX "lock-conflicts",
+ offsetof( performance_counters, lock_conflicts ) },
+ { SLAPI_LDBM_PERFCTR_AT_PREFIX "lock-region-wait-rate",
+ offsetof( performance_counters, lock_region_wait_rate ) },
+ { SLAPI_LDBM_PERFCTR_AT_PREFIX "lock-request-rate",
+ offsetof( performance_counters, lock_request_rate ) },
+ { SLAPI_LDBM_PERFCTR_AT_PREFIX "lockers",
+ offsetof( performance_counters, lockers ) },
+ { SLAPI_LDBM_PERFCTR_AT_PREFIX "configured-locks",
+ offsetof( performance_counters, configured_locks ) },
+ { SLAPI_LDBM_PERFCTR_AT_PREFIX "current-locks",
+ offsetof( performance_counters, current_locks ) },
+ { SLAPI_LDBM_PERFCTR_AT_PREFIX "max-locks",
+ offsetof( performance_counters, max_locks ) },
+ { SLAPI_LDBM_PERFCTR_AT_PREFIX "current-lock-objects",
+ offsetof( performance_counters, current_lock_objects ) },
+ { SLAPI_LDBM_PERFCTR_AT_PREFIX "max-lock-objects",
+ offsetof( performance_counters, max_lock_objects ) },
+ { SLAPI_LDBM_PERFCTR_AT_PREFIX "log-bytes-since-checkpoint",
+ offsetof( performance_counters, log_bytes_since_checkpoint ) },
+ { SLAPI_LDBM_PERFCTR_AT_PREFIX "log-region-wait-rate",
+ offsetof( performance_counters, log_region_wait_rate ) },
+ { SLAPI_LDBM_PERFCTR_AT_PREFIX "log-write-rate",
+ offsetof( performance_counters, log_write_rate ) },
+ { SLAPI_LDBM_PERFCTR_AT_PREFIX "longest-chain-length",
+ offsetof( performance_counters, longest_chain_length ) },
+ { SLAPI_LDBM_PERFCTR_AT_PREFIX "objects-locked",
+ offsetof( performance_counters, page_access_rate ) },
+ { SLAPI_LDBM_PERFCTR_AT_PREFIX "page-create-rate",
+ offsetof( performance_counters, page_create_rate ) },
+ { SLAPI_LDBM_PERFCTR_AT_PREFIX "page-read-rate",
+ offsetof( performance_counters, page_read_rate ) },
+ { SLAPI_LDBM_PERFCTR_AT_PREFIX "page-ro-evict-rate",
+ offsetof( performance_counters, page_ro_evict_rate ) },
+ { SLAPI_LDBM_PERFCTR_AT_PREFIX "page-rw-evict-rate",
+ offsetof( performance_counters, page_rw_evict_rate ) },
+ { SLAPI_LDBM_PERFCTR_AT_PREFIX "page-trickle-rate",
+ offsetof( performance_counters, page_trickle_rate ) },
+ { SLAPI_LDBM_PERFCTR_AT_PREFIX "page-write-rate",
+ offsetof( performance_counters, page_write_rate ) },
+ { SLAPI_LDBM_PERFCTR_AT_PREFIX "pages-in-use",
+ offsetof( performance_counters, pages_in_use ) },
+ { SLAPI_LDBM_PERFCTR_AT_PREFIX "txn-region-wait-rate",
+ offsetof( performance_counters, txn_region_wait_rate ) },
+};
+#define SLAPI_LDBM_PERFCTR_AT_MAP_COUNT \
+ (sizeof(perfctr_at_map) / sizeof(SlapiLDBMPerfctrATMap))
+
+
+/*
+ * Set attributes and values in entry `e' based on performance counter
+ * information (from `priv').
+ */
+void
+perfctrs_as_entry( Slapi_Entry *e, perfctrs_private *priv, DB_ENV *db_env )
+{
+ performance_counters *perf;
+ int i;
+
+ if (priv == NULL) return;
+
+ perf = (performance_counters*)priv->memory;
+
+ /*
+ * First, update the values so they are current.
+ */
+ perfctrs_update( priv, db_env );
+
+ /*
+ * Then convert all the counters to attribute values.
+ */
+ for ( i = 0; i < SLAPI_LDBM_PERFCTR_AT_MAP_COUNT; ++i ) {
+ perfctr_add_to_entry( e, perfctr_at_map[i].pam_type,
+ *((PRUint32 *)((char *)perf + perfctr_at_map[i].pam_offset)));
+ }
+}
+
+
+static void
+perfctr_add_to_entry( Slapi_Entry *e, char *type, PRUint32 countervalue )
+{
+ /*
+ * XXXmcs: the following line assumes that long's are 32 bits or larger,
+ * which we assume in other places too I am sure.
+ */
+ slapi_entry_attr_set_ulong( e, type, (unsigned long)countervalue );
+}
diff --git a/ldap/servers/slapd/back-ldbm/perfctrs.h b/ldap/servers/slapd/back-ldbm/perfctrs.h
new file mode 100644
index 00000000..8aed8d55
--- /dev/null
+++ b/ldap/servers/slapd/back-ldbm/perfctrs.h
@@ -0,0 +1,51 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+/* Structure definition for performance data */
+/* This stuff goes in shared memory, so make sure the packing is consistent */
+
+struct _performance_counters {
+ PRUint32 sequence_number;
+ PRUint32 lock_region_wait_rate;
+ PRUint32 deadlock_rate;
+ PRUint32 configured_locks;
+ PRUint32 current_locks;
+ PRUint32 max_locks;
+ PRUint32 lockers;
+ PRUint32 current_lock_objects;
+ PRUint32 max_lock_objects;
+ PRUint32 lock_conflicts;
+ PRUint32 lock_request_rate;
+ PRUint32 log_region_wait_rate;
+ PRUint32 log_write_rate;
+ PRUint32 log_bytes_since_checkpoint;
+ PRUint32 cache_size_bytes;
+ PRUint32 page_access_rate;
+ PRUint32 cache_hit;
+ PRUint32 cache_try;
+ PRUint32 page_create_rate;
+ PRUint32 page_read_rate;
+ PRUint32 page_write_rate;
+ PRUint32 page_ro_evict_rate;
+ PRUint32 page_rw_evict_rate;
+ PRUint32 hash_buckets;
+ PRUint32 hash_search_rate;
+ PRUint32 longest_chain_length;
+ PRUint32 hash_elements_examine_rate;
+ PRUint32 pages_in_use;
+ PRUint32 dirty_pages;
+ PRUint32 clean_pages;
+ PRUint32 page_trickle_rate;
+ PRUint32 cache_region_wait_rate;
+ PRUint32 active_txns;
+ PRUint32 commit_rate;
+ PRUint32 abort_rate;
+ PRUint32 txn_region_wait_rate;
+};
+typedef struct _performance_counters performance_counters;
+
+#define PERFCTRS_REGION_SUFFIX "-sm"
+#define PERFCTRS_MUTEX_SUFFIX "-mx"
+
diff --git a/ldap/servers/slapd/back-ldbm/proto-back-ldbm.h b/ldap/servers/slapd/back-ldbm/proto-back-ldbm.h
new file mode 100644
index 00000000..4e8ed4df
--- /dev/null
+++ b/ldap/servers/slapd/back-ldbm/proto-back-ldbm.h
@@ -0,0 +1,582 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2004 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+#ifndef _PROTO_BACK_LDBM
+#define _PROTO_BACK_LDBM
+
+/*
+ * attr.c
+ */
+struct attrinfo * attrinfo_new();
+void attrinfo_delete(struct attrinfo **pp);
+void ainfo_get( backend *be, char *type, struct attrinfo **at );
+void attr_masks( backend *be, char *type, int *indexmask,
+ int *syntaxmask );
+void attr_masks_ex( backend *be, char *type, int *indexmask,
+ int *syntaxmask, struct attrinfo **at );
+void attr_index_config( backend *be, char *fname, int lineno,
+ int argc, char **argv, int init );
+int ldbm_compute_init();
+void attrinfo_deletetree(ldbm_instance *inst);
+void attr_create_empty(backend *be,char *type,struct attrinfo **ai);
+
+/*
+ * cache.c
+ */
+int cache_init(struct cache *cache, size_t maxsize, long maxentries);
+void cache_clear(struct cache *cache);
+void cache_destroy_please(struct cache *cache);
+void cache_set_max_size(struct cache *cache, size_t bytes);
+void cache_set_max_entries(struct cache *cache, long entries);
+size_t cache_get_max_size(struct cache *cache);
+long cache_get_max_entries(struct cache *cache);
+void cache_get_stats(struct cache *cache, u_long *hits, u_long *tries,
+ long *entries,long *maxentries,
+ size_t *size, size_t *maxsize);
+void cache_debug_hash(struct cache *cache, char **out);
+int cache_remove(struct cache *cache, struct backentry *e);
+void cache_return(struct cache *cache, struct backentry **bep);
+struct backentry *cache_find_dn(struct cache *cache, const char *dn, unsigned long ndnlen);
+struct backentry *cache_find_id(struct cache *cache, ID id);
+struct backentry *cache_find_uuid(struct cache *cache, const char *uuid);
+int cache_add(struct cache *cache, struct backentry *e,
+ struct backentry **alt);
+int cache_add_tentative(struct cache *cache, struct backentry *e,
+ struct backentry **alt);
+int cache_lock_entry(struct cache *cache, struct backentry *e);
+void cache_unlock_entry(struct cache *cache, struct backentry *e);
+int cache_replace(struct cache *cache, struct backentry *olde,
+ struct backentry *newe);
+
+Hashtable *new_hash(u_long size, u_long offset, HashFn hfn,
+ HashTestFn tfn);
+int add_hash(Hashtable *ht, void *key, size_t keylen, void *entry,
+ void **alt);
+int find_hash(Hashtable *ht, const void *key, size_t keylen, void **entry);
+int remove_hash(Hashtable *ht, const void *key, size_t keylen);
+
+/*
+ * dblayer.c
+ */
+int dblayer_init(struct ldbminfo *li);
+int dblayer_terminate(struct ldbminfo *li);
+int dblayer_start(struct ldbminfo *li, int dbmode);
+int dblayer_flush(struct ldbminfo *li );
+int dblayer_close(struct ldbminfo *li, int dbmode );
+void dblayer_pre_close(struct ldbminfo *li);
+int dblayer_post_close(struct ldbminfo *li, int dbmode );
+int dblayer_instance_close(backend *be);
+int dblayer_get_index_file(backend *be,struct attrinfo *a, DB** ppDB, int create);
+int dblayer_release_index_file(backend *be,struct attrinfo *a, DB* pDB);
+int dblayer_erase_index_file(backend *be, struct attrinfo *a, int no_force_chkpt);
+int dblayer_erase_index_file_nolock(backend *be, struct attrinfo *a, int no_force_chkpt);
+int dblayer_get_id2entry(backend *be, DB **ppDB);
+int dblayer_release_id2entry(backend *be, DB *pDB);
+int dblayer_get_aux_id2entry(backend *be, DB **ppDB, DB_ENV **ppEnv);
+int dblayer_release_aux_id2entry(backend *be, DB *pDB, DB_ENV *pEnv);
+int dblayer_txn_init(struct ldbminfo *li, back_txn *txn);
+int dblayer_txn_begin(struct ldbminfo *li,back_txnid parent_txn, back_txn *txn);
+int dblayer_txn_commit(struct ldbminfo *li, back_txn *txn);
+int dblayer_txn_abort(struct ldbminfo *li, back_txn *txn);
+int dblayer_read_txn_abort(struct ldbminfo *li, back_txn *txn);
+int dblayer_read_txn_begin(struct ldbminfo *li,back_txnid parent_txn, back_txn *txn);
+int dblayer_read_txn_commit(struct ldbminfo *li, back_txn *txn);
+size_t dblayer_get_optimal_block_size(struct ldbminfo *li);
+void dblayer_unlock_backend(backend *be);
+void dblayer_lock_backend(backend *be);
+int dblayer_plugin_begin(Slapi_PBlock *pb);
+int dblayer_plugin_commit(Slapi_PBlock *pb);
+int dblayer_plugin_abort(Slapi_PBlock *pb);
+int dblayer_memp_stat(struct ldbminfo *li, DB_MPOOL_STAT **gsp,DB_MPOOL_FSTAT ***fsp);
+int dblayer_memp_stat_instance(ldbm_instance *inst, DB_MPOOL_STAT **gsp, DB_MPOOL_FSTAT ***fsp);
+int dblayer_backup(struct ldbminfo *li, char *destination_directory,
+ Slapi_Task *task);
+int dblayer_restore(struct ldbminfo *li, char* source_directory, Slapi_Task *task);
+int dblayer_copy_directory(struct ldbminfo *li, Slapi_Task *task,
+ char *instance_dir, char *destination_dir,
+ int restore, int *cnt, int instance_dir_flag,
+ int indexonly);
+int dblayer_copyfile(char* source, char * destination, int overwrite, int mode);
+int dblayer_delete_instance_dir(backend *be);
+int dblayer_delete_database(struct ldbminfo *li);
+int dblayer_database_size(struct ldbminfo *li, unsigned int *size);
+int dblayer_terminate(struct ldbminfo *li);
+int dblayer_close_indexes(backend *be);
+int dblayer_open_file(backend *be, char* indexname, int create, int index_flags, DB **ppDB);
+int dblayer_close_file(DB *db);
+void dblayer_sys_pages(size_t *pagesize, size_t *pages, size_t *procpages, size_t *availpages);
+int dblayer_is_cachesize_sane(size_t *cachesize);
+void dblayer_remember_disk_filled(struct ldbminfo *li);
+int dblayer_open_huge_file(const char *path, int oflag, int mode);
+int dblayer_instance_start(backend *be, int normal_mode);
+int dblayer_make_new_instance_data_dir(backend *be);
+int dblayer_get_instance_data_dir(backend *be);
+char *dblayer_strerror(int error);
+PRInt64 db_atol(char *str, int *err);
+PRInt64 db_atoi(char *str, int *err);
+unsigned long db_strtoul(const char *str, int *err);
+int dblayer_set_batch_transactions(void *arg, void *value, char *errorbuf, int phase, int apply);
+void *dblayer_get_batch_transactions(void *arg);
+int dblayer_in_import(ldbm_instance *inst);
+
+int dblayer_update_db_ext(ldbm_instance *inst, char *oldext, char *newext);
+void dblayer_set_recovery_required(struct ldbminfo *li);
+
+char *dblayer_get_home_dir(struct ldbminfo *li, int *dbhome);
+char *dblayer_get_full_inst_dir(struct ldbminfo *li, ldbm_instance *inst,
+ char *buf, int buflen);
+void autosize_import_cache(struct ldbminfo *li);
+
+
+/*
+ * dn2entry.c
+ */
+struct backentry *dn2entry(Slapi_Backend *be, const Slapi_DN *sdn, back_txn *txn, int *err);
+struct backentry *dn2entry_or_ancestor(Slapi_Backend *be, const Slapi_DN *sdn, Slapi_DN *ancestor, back_txn *txn, int *err);
+struct backentry *dn2ancestor(Slapi_Backend *be,const Slapi_DN *sdn,Slapi_DN *ancestordn,back_txn *txn,int *err);
+int get_copy_of_entry(Slapi_PBlock *pb, const entry_address *addr, back_txn *txn, int plock_parameter, int must_exist);
+void done_with_pblock_entry(Slapi_PBlock *pb, int plock_parameter);
+
+/*
+ * uniqueid2entry.c
+ */
+struct backentry * uniqueid2entry(backend *be, const char *uniqueid,
+ back_txn *txn, int *err);
+
+/*
+ * filterindex.c
+ */
+IDList * filter_candidates( Slapi_PBlock *pb, backend *be, const char *base, Slapi_Filter *f, Slapi_Filter *nextf, int range, int *err );
+
+/*
+ * findentry.c
+ */
+struct backentry * find_entry2modify( Slapi_PBlock *pb, Slapi_Backend *be, const entry_address *addr, back_txn *txn );
+struct backentry * find_entry( Slapi_PBlock *pb, Slapi_Backend *be, const entry_address *addr, back_txn *txn );
+struct backentry * find_entry2modify_only( Slapi_PBlock *pb, Slapi_Backend *be, const entry_address *addr, back_txn *txn);
+struct backentry * find_entry_only( Slapi_PBlock *pb, Slapi_Backend *be, const entry_address *addr, back_txn *txn);
+int check_entry_for_referral(Slapi_PBlock *pb, Slapi_Entry *entry, char *matched, const char *callingfn);
+
+/*
+ * haschildren.c
+ */
+int has_children( struct ldbminfo *li, struct backentry *p, back_txn *txn, int *err );
+
+/*
+ * id2entry.c
+ */
+int id2entry_add( backend *be, struct backentry *e, back_txn *txn );
+int id2entry_add_ext( backend *be, struct backentry *e, back_txn *txn, int encrypt );
+int id2entry_delete( backend *be, struct backentry *e, back_txn *txn );
+struct backentry * id2entry( backend *be, ID id, back_txn *txn, int *err );
+
+/*
+ * idl.c
+ */
+IDList * idl_alloc( NIDS nids );
+void idl_free( IDList *idl );
+NIDS idl_length(IDList *idl);
+int idl_is_allids(IDList *idl);
+int idl_append( IDList *idl, ID id);
+void idl_insert(IDList **idl, ID id);
+IDList * idl_allids( backend *be );
+IDList * idl_fetch( backend *be, DB* db, DBT *key, DB_TXN *txn, struct attrinfo *a, int *err );
+int idl_insert_key( backend *be, DB* db, DBT *key, ID id, DB_TXN *txn, struct attrinfo *a,int *disposition );
+int idl_delete_key( backend *be, DB *db, DBT *key, ID id, DB_TXN *txn, struct attrinfo *a );
+IDList * idl_intersection( backend *be, IDList *a, IDList *b );
+IDList * idl_union( backend *be, IDList *a, IDList *b );
+int idl_notin( backend *be, IDList *a, IDList *b , IDList **new_result);
+ID idl_firstid( IDList *idl );
+ID idl_nextid( IDList *idl, ID id );
+int idl_init_private(backend *be, struct attrinfo *a);
+int idl_release_private(struct attrinfo *a);
+
+idl_iterator idl_iterator_init(const IDList *idl);
+idl_iterator idl_iterator_increment(idl_iterator *i);
+idl_iterator idl_iterator_decrement(idl_iterator *i);
+ID idl_iterator_dereference(idl_iterator i, const IDList *idl);
+ID idl_iterator_dereference_increment(idl_iterator *i, const IDList *idl);
+size_t idl_sizeof(IDList *idl);
+int idl_store_block(backend *be,DB *db,DBT *key,IDList *idl,DB_TXN *txn,struct attrinfo *a);
+void idl_set_tune(int val);
+int idl_get_tune();
+size_t idl_get_allidslimit(struct attrinfo *a);
+int idl_get_idl_new();
+int idl_new_compare_dups(
+#if 1000*DB_VERSION_MAJOR + 100*DB_VERSION_MINOR >= 3200
+ DB *db,
+#endif
+ const DBT *a,
+ const DBT *b
+);
+
+/*
+ * index.c
+ */
+int index_addordel_entry( backend *be, struct backentry *e, int flags, back_txn *txn );
+int index_add_mods( backend *be, const LDAPMod**mods, struct backentry *olde, struct backentry *newe, back_txn *txn );
+int index_addordel_string(backend *be, const char *type, const char *s, ID id, int flags, back_txn *txn);
+int index_addordel_values_sv( backend *be, const char *type, Slapi_Value **vals, Slapi_Value **evals, ID id, int flags, back_txn *txn );
+int index_addordel_values_ext_sv( backend *be, const char *type, Slapi_Value **vals, Slapi_Value **evals, ID id, int flags, back_txn *txn,int *idl_disposition, void *buffer_handle );
+int id_array_init(Id_Array *new_guy, int size);
+
+IDList* index_read( backend *be, char *type, const char* indextype, const struct berval* val, back_txn *txn, int *err );
+IDList* index_read_ext( backend *be, char *type, const char* indextype, const struct berval* val, back_txn *txn, int *err, int *unindexed );
+IDList* index_range_read( Slapi_PBlock *pb, backend *be, char *type, const char* indextype, int ftype, struct berval* val, struct berval* nextval, int range, back_txn *txn, int *err );
+const char *encode( const struct berval* data, char buf[BUFSIZ] );
+
+extern const char* indextype_PRESENCE;
+extern const char* indextype_EQUALITY;
+extern const char* indextype_APPROX;
+extern const char* indextype_SUB;
+
+int index_buffer_init(size_t size,int flags,void **h);
+int index_buffer_flush(void *h,backend *be, DB_TXN *txn,struct attrinfo *a);
+int index_buffer_terminate(void *h);
+
+/*
+ * instance.c
+ */
+int ldbm_instance_create(backend *be, char *name);
+int ldbm_instance_create_default_indexes(backend *be);
+int ldbm_instance_start(backend *be);
+int ldbm_instance_stop(backend *be);
+int ldbm_instance_startall(struct ldbminfo *li);
+int ldbm_instance_stopall(struct ldbminfo *li);
+ldbm_instance *ldbm_instance_find_by_name(struct ldbminfo *li, char *name);
+int ldbm_instance_destroy(ldbm_instance *inst);
+
+/*
+ * ldif2ldbm.c
+ */
+int import_subcount_mother_init(import_subcount_stuff *mothers,ID parent_id, size_t count);
+int import_subcount_mother_count(import_subcount_stuff *mothers,ID parent_id);
+void import_subcount_stuff_init(import_subcount_stuff *stuff);
+void import_subcount_stuff_term(import_subcount_stuff *stuff);
+int update_subordinatecounts(backend *be,import_subcount_stuff *mothers, DB_TXN *txn);
+void import_configure_index_buffer_size(size_t size);
+size_t import_get_index_buffer_size();
+int ldbm_back_fetch_incl_excl(Slapi_PBlock *pb, char ***include,
+ char ***exclude);
+void ldbm_back_free_incl_excl(char **include, char **exclude);
+int ldbm_back_ok_to_dump(const char *dn, char **include, char **exclude);
+int ldbm_back_wire_import(Slapi_PBlock *pb);
+void *factory_constructor(void *object, void *parent);
+void factory_destructor(void *extension, void *object, void *parent);
+
+/*
+ * modify.c
+ */
+int modify_update_all(backend *be, Slapi_PBlock *pb,modify_context *mc,back_txn *txn);
+void modify_init(modify_context *mc,struct backentry *old_entry);
+int modify_apply_mods(modify_context *mc, Slapi_Mods *smods);
+int modify_term(modify_context *mc,backend *be);
+int modify_switch_entries(modify_context *mc,backend *be);
+
+/*
+ * add.c
+ */
+void add_update_entry_operational_attributes(struct backentry *ep, ID pid);
+void add_update_entrydn_operational_attributes(struct backentry *ep);
+
+/*
+ * misc.c
+ */
+void ldbm_nasty(const char* str, int c, int err);
+void ldbm_log_access_message(Slapi_PBlock *pblock,char *string);
+int return_on_disk_full(struct ldbminfo *li);
+int ldbm_attribute_always_indexed(const char *attrtype);
+void ldbm_destroy_instance_name(struct ldbminfo *li);
+char *compute_entry_tombstone_dn(const char *entrydn, const char *uniqueid);
+int instance_set_busy(ldbm_instance *inst);
+int instance_set_busy_and_readonly(ldbm_instance *inst);
+void instance_set_not_busy(ldbm_instance *inst);
+void allinstance_set_busy(struct ldbminfo *li);
+void allinstance_set_not_busy(struct ldbminfo *li);
+int is_anyinstance_busy(struct ldbminfo *li);
+int ldbm_delete_dirs(char *path);
+int mkdir_p(char *dir, unsigned int mode);
+int is_fullpath(char *path);
+char get_sep(char *path);
+
+/*
+ * nextid.c
+ */
+ID next_id( backend *be );
+void next_id_return( backend *be, ID id );
+ID next_id_get( backend *be );
+void id_internal_to_stored(ID,char*);
+ID id_stored_to_internal(char*);
+#if 0
+int write_dbversion( ldbm_instance *inst );
+#endif
+void get_ids_from_disk(backend *be);
+void get_both_ids( struct ldbminfo *li, ID *nextid, ID *nextid2index );
+
+/*
+ * backentry.c
+ */
+struct backentry *backentry_init( Slapi_Entry *e );
+struct backentry *backentry_alloc();
+void backentry_free( struct backentry **bep );
+struct backentry *backentry_dup( struct backentry * );
+void backentry_clear_entry( struct backentry * );
+char *backentry_get_ndn(const struct backentry *e);
+const Slapi_DN *backentry_get_sdn(const struct backentry *e);
+
+/*
+ * parents.c
+ */
+int parent_update_on_childchange(modify_context *mc,int op, size_t *numofchildren);
+
+/*
+ * perfctrs.c
+ */
+void perfctrs_wait(size_t milliseconds,perfctrs_private *priv,DB_ENV *db_env);
+void perfctrs_init(struct ldbminfo *li,perfctrs_private **priv);
+void perfctrs_terminate(perfctrs_private **priv);
+void perfctrs_as_entry( Slapi_Entry *e, perfctrs_private *priv, DB_ENV *db_env );
+
+/*
+ * rmdb.c
+ */
+int ldbm_back_rmdb( Slapi_PBlock *pb );
+
+/*
+ * sort.c
+ */
+
+/*
+ * Definitions for sort spec object
+ */
+struct sort_spec_thing
+{
+ char *type;
+ char *matchrule; /* Matching rule string */
+ int order; /* 0 == ascending, 1 == decending */
+ struct sort_spec_thing *next; /* Link to the next one */
+ Slapi_PBlock *mr_pb; /* For matchrule indexing */
+ value_compare_fn_type compare_fn; /* For non-matchrule indexing */
+};
+typedef struct sort_spec_thing sort_spec_thing;
+typedef struct sort_spec_thing sort_spec;
+
+void sort_spec_free(sort_spec *s);
+int sort_candidates(backend *be, int lookthrough_limit, time_t time_up, Slapi_PBlock *pb, IDList *candidates, sort_spec_thing *sort_spec, char **sort_error_type) ;
+int make_sort_response_control ( Slapi_PBlock *pb, int code, char *error_type);
+int parse_sort_spec(struct berval *sort_spec_ber, sort_spec **ps);
+struct berval* attr_value_lowest(struct berval **values, value_compare_fn_type compare_fn);
+int sort_attr_compare(struct berval ** value_a, struct berval ** value_b, value_compare_fn_type compare_fn);
+void sort_log_access(Slapi_PBlock *pb,sort_spec_thing *s,IDList *candidates);
+
+/*
+ * dbsize.c
+ */
+int ldbm_db_size( Slapi_PBlock *pb );
+
+/*
+ * external functions
+ */
+int ldbm_back_bind( Slapi_PBlock *pb );
+int ldbm_back_unbind( Slapi_PBlock *pb );
+int ldbm_back_search( Slapi_PBlock *pb );
+int ldbm_back_compare( Slapi_PBlock *pb );
+int ldbm_back_modify( Slapi_PBlock *pb );
+int ldbm_back_modrdn( Slapi_PBlock *pb );
+int ldbm_back_add( Slapi_PBlock *pb );
+int ldbm_back_delete( Slapi_PBlock *pb );
+int ldbm_back_abandon( Slapi_PBlock *pb );
+int ldbm_back_config( Slapi_PBlock *pb );
+int ldbm_back_close( Slapi_PBlock *pb );
+int ldbm_back_cleanup( Slapi_PBlock *pb );
+void ldbm_back_instance_set_destructor(void **arg);
+int ldbm_back_flush( Slapi_PBlock *pb );
+int ldbm_back_start( Slapi_PBlock *pb );
+int ldbm_back_seq( Slapi_PBlock *pb );
+int ldbm_back_ldif2ldbm( Slapi_PBlock *pb );
+int ldbm_back_ldbm2ldif( Slapi_PBlock *pb );
+int ldbm_back_ldbm2ldifalt( Slapi_PBlock *pb );
+int ldbm_back_ldbm2index( Slapi_PBlock *pb );
+int ldbm_back_archive2ldbm( Slapi_PBlock *pb );
+int ldbm_back_ldbm2archive( Slapi_PBlock *pb );
+#if defined(UPGRADEDB)
+int ldbm_back_upgradedb( Slapi_PBlock *pb );
+#endif
+int ldbm_back_next_search_entry( Slapi_PBlock *pb );
+int ldbm_back_next_search_entry_ext( Slapi_PBlock *pb, int use_extension );
+int ldbm_back_db_test( Slapi_PBlock *pb );
+int ldbm_back_entry_release( Slapi_PBlock *pb, void *backend_info_ptr );
+int ldbm_back_init( Slapi_PBlock *pb );
+
+/*
+ * monitor.c
+ */
+
+int ldbm_back_monitor_search(Slapi_PBlock *pb, Slapi_Entry* e,
+ Slapi_Entry* entryAfter, int *returncode, char *returntext, void *arg);
+int ldbm_back_monitor_instance_search(Slapi_PBlock *pb, Slapi_Entry *e,
+ Slapi_Entry *entryAfter, int *returncode, char *returntext, void *arg);
+int ldbm_back_dbmonitor_search(Slapi_PBlock *pb, Slapi_Entry *e,
+ Slapi_Entry *entryAfter, int *returncode, char *returntext, void *arg);
+
+/*
+ * vlv.c
+ */
+struct vlv_request
+{
+ unsigned long beforeCount;
+ unsigned long afterCount;
+ unsigned long tag;
+ unsigned long index;
+ unsigned long contentCount;
+ struct berval value;
+};
+
+struct vlv_response
+{
+ unsigned long targetPosition;
+ unsigned long contentCount;
+ unsigned long result;
+};
+
+int vlv_init(ldbm_instance *inst);
+int vlv_remove_callbacks(ldbm_instance *inst);
+const Slapi_Entry **vlv_get_search_entries();
+struct vlvIndex* vlv_find_searchname(const char * name, backend *be);
+struct vlvIndex* vlv_find_indexname(const char * name, backend *be);
+char *vlv_getindexnames();
+int vlv_search_build_candidate_list(Slapi_PBlock *pb, const Slapi_DN *base, int *rc, const sort_spec* sort_control,
+ const struct vlv_request *vlv_request_control, IDList** candidates, struct vlv_response *vlv_response_control);
+int vlv_update_index(struct vlvIndex* p, back_txn *txn, struct ldbminfo *li, Slapi_PBlock *pb, struct backentry* oldEntry, struct backentry* newEntry);
+int vlv_update_all_indexes(back_txn *txn, backend *be, Slapi_PBlock *pb, struct backentry* oldEntry, struct backentry* newEntry);
+int vlv_filter_candidates(backend *be, Slapi_PBlock *pb, const IDList *candidates, const Slapi_DN *base, int scope, Slapi_Filter *filter, IDList** filteredCandidates,int lookthrough_limit, time_t time_up);
+int vlv_trim_candidates(backend *be, const IDList *candidates, const sort_spec* sort_control, const struct vlv_request *vlv_request_control, IDList** filteredCandidates,struct vlv_response *pResponse);
+int vlv_parse_request_control(backend *be, struct berval *vlv_spec_ber, struct vlv_request* vlvp);
+int vlv_make_response_control(Slapi_PBlock *pb, const struct vlv_response* vlvp);
+void vlv_getindices(IFP callback_fn,void *param, backend *be);
+void vlv_print_access_log(Slapi_PBlock *pb,struct vlv_request* vlvi, struct vlv_response *vlvo);
+void vlv_grok_new_import_entry(const struct backentry *e, backend *be);
+IDList *vlv_find_index_by_filter(struct backend *be, const char *base,
+ Slapi_Filter *f);
+int vlv_delete_search_entry(Slapi_PBlock *pb, Slapi_Entry* e, ldbm_instance *inst);
+void vlv_acquire_lock(backend *be);
+void vlv_release_lock(backend *be);
+int vlv_isvlv(char *filename);
+
+/*
+ * Indexfile.c
+ */
+int indexfile_delete_all_keys(backend *be,char* type,back_txn *txn);
+int indexfile_primary_modifyall(backend *be, LDAPMod **mods_to_perform,char **indexes_to_update,back_txn *txn);
+
+/*
+ * bedse.c
+ */
+#if 0
+int bedse_init();
+int bedse_search(Slapi_PBlock *pb);
+struct dse_callback *bedse_register_callback(int operation, const Slapi_DN *base, int scope, const char *filter, int (*fn)(Slapi_PBlock *,Slapi_Entry *,Slapi_Entry *,int*,char*,void *), void *fn_arg);
+void bedse_remove_callback(int operation, const Slapi_DN *base, int scope, const char *filter, int (*fn)(Slapi_PBlock *,Slapi_Entry *,Slapi_Entry *,int*,char*,void *));
+int bedse_add_index_entry(int argc, char **argv);
+#endif
+
+/*
+ * search.c
+ */
+Slapi_Filter* create_onelevel_filter(Slapi_Filter* filter, const struct backentry *e, int managedsait, Slapi_Filter** fid2kids, Slapi_Filter** focref, Slapi_Filter** fand, Slapi_Filter** forr);
+Slapi_Filter* create_subtree_filter(Slapi_Filter* filter, int managedsait, Slapi_Filter** focref, Slapi_Filter** forr);
+IDList* subtree_candidates(Slapi_PBlock *pb, backend *be, const char *base, const struct backentry *e, Slapi_Filter *filter, int managedsait, int *allids_before_scopingp, int *err);
+void search_set_tune(struct ldbminfo *li,int val);
+int search_get_tune(struct ldbminfo *li);
+
+/*
+ * matchrule.c
+ */
+int create_matchrule_indexer(Slapi_PBlock **pb,char* matchrule,char* type);
+int destroy_matchrule_indexer(Slapi_PBlock *pb);
+int matchrule_values_to_keys(Slapi_PBlock *pb,struct berval **input_values,struct berval ***output_values);
+int matchrule_values_to_keys_sv(Slapi_PBlock *pb,Slapi_Value **input_values, Slapi_Value ***output_values);
+
+/*
+ * upgrade.c
+ */
+int check_db_version(struct ldbminfo *li, int *action);
+int check_db_inst_version(ldbm_instance *inst);
+#if defined(UPGRADEDB)
+int adjust_idl_switch(char *ldbmversion, struct ldbminfo *li);
+#endif
+int ldbm_upgrade(ldbm_instance *inst, int action);
+int lookup_dbversion(char *dbversion, int flag);
+
+
+/*
+ * init.c
+ */
+int ldbm_attribute_always_indexed(const char *attrtype);
+
+/*
+ * dbversion.c
+ */
+int dbversion_write(struct ldbminfo *li, const char *dir, const char *dversion);
+int dbversion_read(struct ldbminfo *li, const char *directory,
+ char *ldbmversion, char *dataversion);
+int dbversion_exists(struct ldbminfo *li, const char *directory);
+
+/*
+ * config_ldbm.c
+ */
+int ldbm_config_load_dse_info(struct ldbminfo *li);
+void ldbm_config_setup_default(struct ldbminfo *li);
+void ldbm_config_internal_set(struct ldbminfo *li, char *attrname, char *value);
+void ldbm_instance_config_internal_set(ldbm_instance *inst, char *attrname, char *value);
+void ldbm_instance_config_setup_default(ldbm_instance *inst);
+int ldbm_instance_postadd_instance_entry_callback(Slapi_PBlock *pb, Slapi_Entry* entryBefore, Slapi_Entry* entryAfter, int *returncode, char *returntext, void *arg);
+int ldbm_instance_add_instance_entry_callback(Slapi_PBlock *pb, Slapi_Entry* entryBefore, Slapi_Entry* entryAfter, int *returncode, char *returntext, void *arg);
+int ldbm_instance_delete_instance_entry_callback(Slapi_PBlock *pb, Slapi_Entry* entryBefore, Slapi_Entry* entryAfter, int *returncode, char *returntext, void *arg);
+int ldbm_instance_post_delete_instance_entry_callback(Slapi_PBlock *pb, Slapi_Entry* entryBefore, Slapi_Entry* entryAfter, int *returncode, char *returntext, void *arg);
+/* Index config functions */
+int ldbm_index_init_entry_callback(Slapi_PBlock *pb, Slapi_Entry* e, Slapi_Entry* entryAfter, int *returncode, char *returntext, void *arg);
+int ldbm_instance_index_config_add_callback(Slapi_PBlock *pb, Slapi_Entry* entryBefore, Slapi_Entry* e, int *returncode, char *returntext, void *arg);
+int ldbm_instance_index_config_delete_callback(Slapi_PBlock *pb, Slapi_Entry* entryBefore, Slapi_Entry* e, int *returncode, char *returntext, void *arg);
+int ldbm_instance_index_config_modify_callback(Slapi_PBlock *pb, Slapi_Entry *e, Slapi_Entry *entryAfter, int *returncode, char *returntext, void *arg);
+/* Attribute Encryption config functions */
+int ldbm_attrcrypt_init_entry_callback(Slapi_PBlock *pb, Slapi_Entry* e, Slapi_Entry* entryAfter, int *returncode, char *returntext, void *arg);
+int ldbm_instance_attrcrypt_config_add_callback(Slapi_PBlock *pb, Slapi_Entry* entryBefore, Slapi_Entry* e, int *returncode, char *returntext, void *arg);
+int ldbm_instance_attrcrypt_config_delete_callback(Slapi_PBlock *pb, Slapi_Entry* entryBefore, Slapi_Entry* e, int *returncode, char *returntext, void *arg);
+int ldbm_instance_attrcrypt_config_modify_callback(Slapi_PBlock *pb, Slapi_Entry *e, Slapi_Entry *entryAfter, int *returncode, char *returntext, void *arg);
+
+void replace_ldbm_config_value(char *conftype, char *val, struct ldbminfo *li);
+
+/*
+ * ancestorid.c
+ */
+int ldbm_ancestorid_create_index(backend *be);
+int ldbm_ancestorid_index_entry(backend *be, struct backentry *e, int flags, back_txn *txn);
+int ldbm_ancestorid_read(backend *be, back_txn *txn, ID id, IDList **idl);
+int ldbm_ancestorid_move_subtree(
+ backend *be,
+ const Slapi_DN *olddn,
+ const Slapi_DN *newdn,
+ ID id,
+ IDList *subtree_idl,
+ back_txn *txn
+);
+
+#endif
+
+/*
+ * import-threads.c
+ */
+int dse_conf_backup(struct ldbminfo *li, char *destination_directory);
+int dse_conf_verify(struct ldbminfo *li, char *src_dir);
+
+/*
+ * ldbm_attrcrypt.c
+ */
+int attrcrypt_decrypt_entry(backend *be, struct backentry *e);
+int attrcrypt_encrypt_entry_inplace(backend *be, const struct backentry *inout);
+int attrcrypt_encrypt_entry(backend *be, const struct backentry *in, struct backentry **out);
+int attrcrypt_encrypt_index_key(backend *be, struct attrinfo *ai, const struct berval *in, struct berval **out);
+int attrcrypt_init(ldbm_instance *li);
diff --git a/ldap/servers/slapd/back-ldbm/rmdb.c b/ldap/servers/slapd/back-ldbm/rmdb.c
new file mode 100644
index 00000000..d4b760bd
--- /dev/null
+++ b/ldap/servers/slapd/back-ldbm/rmdb.c
@@ -0,0 +1,54 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+/*
+ * rmdb.c - ldbm backend routine which deletes an entire database.
+ * This routine is not exposed in the public SLAPI interface. It
+ * is called by the replication subsystem when then changelog must
+ * be erased.
+ */
+
+#include "back-ldbm.h"
+
+int
+ldbm_back_rmdb( Slapi_PBlock *pb )
+{
+ struct ldbminfo *li = NULL;
+ /* char *directory = NULL;*/
+ int return_value = -1;
+ Slapi_Backend *be;
+
+ slapi_pblock_get( pb, SLAPI_BACKEND, &be );
+
+ if (be->be_state != BE_STATE_STOPPED)
+ {
+ LDAPDebug( LDAP_DEBUG_TRACE,
+ "ldbm_back_cleanup: warning - backend is in a wrong state - %d\n",
+ be->be_state, 0, 0 );
+ return 0;
+ }
+
+ PR_Lock (be->be_state_lock);
+
+ if (be->be_state != BE_STATE_STOPPED)
+ {
+ LDAPDebug( LDAP_DEBUG_TRACE,
+ "ldbm_back_cleanup: warning - backend is in a wrong state - %d\n",
+ be->be_state, 0, 0 );
+ PR_Unlock (be->be_state_lock);
+ return 0;
+ }
+
+ slapi_pblock_get( pb, SLAPI_PLUGIN_PRIVATE, &li );
+/* slapi_pblock_get( pb, SLAPI_SEQ_VAL, &directory );*/
+ return_value = dblayer_delete_database( li );
+
+ if (return_value == 0)
+ be->be_state = BE_STATE_DELETED;
+
+ PR_Unlock (be->be_state_lock);
+
+ return return_value;
+}
diff --git a/ldap/servers/slapd/back-ldbm/seq.c b/ldap/servers/slapd/back-ldbm/seq.c
new file mode 100644
index 00000000..6a61fd2e
--- /dev/null
+++ b/ldap/servers/slapd/back-ldbm/seq.c
@@ -0,0 +1,262 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+/* seq.c - ldbm backend sequential access function */
+
+#include "back-ldbm.h"
+
+#define SEQ_LITTLE_BUFFER_SIZE 100
+
+/*
+ * Access the database sequentially.
+ * There are 4 ways to call this routine. In each case, the equality index
+ * for "attrname" is consulted:
+ * 1) If the SLAPI_SEQ_TYPE parameter is SLAPI_SEQ_FIRST, then this routine
+ * will find the smallest key greater than or equal to the SLAPI_SEQ_VAL
+ * parameter, and return all entries that key's IDList. If SLAPI_SEQ_VAL
+ * is NULL, then the smallest key is retrieved and the associaated
+ * entries are returned.
+ * 2) If the SLAPI_SEQ_TYPE parameter is SLAPI_SEQ_NEXT, then this routine
+ * will find the smallest key strictly greater than the SLAPI_SEQ_VAL
+ * parameter, and return all entries that key's IDList.
+ * 3) If the SLAPI_SEQ_TYPE parameter is SLAPI_SEQ_PREV, then this routine
+ * will find the greatest key strictly less than the SLAPI_SEQ_VAL
+ * parameter, and return all entries that key's IDList.
+ * 4) If the SLAPI_SEQ_TYPE parameter is SLAPI_SEQ_LAST, then this routine
+ * will find the largest equality key in the index and return all entries
+ * which match that key. The SLAPI_SEQ_VAL parameter is ignored.
+ */
+int
+ldbm_back_seq( Slapi_PBlock *pb )
+{
+ backend *be;
+ ldbm_instance *inst;
+ struct ldbminfo *li;
+ IDList *idl = NULL;
+ int err = LDAP_SUCCESS;
+ DB *db;
+ DBC *dbc = NULL;
+ int type;
+ char *attrname, *val;
+ int isroot;
+ struct attrinfo *ai = NULL;
+ int return_value = -1;
+ int nentries = 0;
+ int retry_count=0;
+
+ /* Decode arguments */
+ slapi_pblock_get( pb, SLAPI_BACKEND, &be);
+ slapi_pblock_get( pb, SLAPI_PLUGIN_PRIVATE, &li );
+ slapi_pblock_get( pb, SLAPI_SEQ_TYPE, &type );
+ slapi_pblock_get( pb, SLAPI_SEQ_ATTRNAME, &attrname );
+ slapi_pblock_get( pb, SLAPI_SEQ_VAL, &val );
+ slapi_pblock_get( pb, SLAPI_REQUESTOR_ISROOT, &isroot );
+
+ inst = (ldbm_instance *) be->be_instance_info;
+
+ /* Validate arguments */
+ if ( type != SLAPI_SEQ_FIRST &&
+ type != SLAPI_SEQ_LAST &&
+ type != SLAPI_SEQ_NEXT &&
+ type != SLAPI_SEQ_PREV )
+ {
+ slapi_send_ldap_result( pb, LDAP_PROTOCOL_ERROR, NULL,
+ "Bad seq access type", 0, NULL );
+ return( -1 );
+ }
+
+ /* get a database */
+
+ ainfo_get( be, attrname, &ai );
+ LDAPDebug( LDAP_DEBUG_ARGS,
+ " seq: indextype: %s indexmask: 0x%x seek type: %d\n",
+ ai->ai_type, ai->ai_indexmask, type );
+ if ( ! (INDEX_EQUALITY & ai->ai_indexmask) ) {
+ LDAPDebug( LDAP_DEBUG_TRACE,
+ "seq: caller specified un-indexed attribute %s\n",
+ attrname ? attrname : "", 0, 0 );
+ slapi_send_ldap_result( pb, LDAP_UNWILLING_TO_PERFORM, NULL,
+ "Unindexed seq access type", 0, NULL );
+ return -1;
+ }
+
+ if ( (return_value = dblayer_get_index_file( be, ai, &db, DBOPEN_CREATE )) != 0 ) {
+ LDAPDebug( LDAP_DEBUG_ANY,
+ "<= ldbm_back_seq NULL (could not open index file for attribute %s)\n",
+ attrname, 0, 0 );
+ slapi_send_ldap_result( pb, LDAP_OPERATIONS_ERROR, NULL, NULL, 0, NULL );
+ return -1;
+ }
+
+ /* First, get a database cursor */
+
+ return_value = db->cursor(db,NULL,&dbc,0);
+
+ if (0 == return_value)
+ {
+ DBT data = {0};
+ DBT key = {0};
+ char little_buffer[SEQ_LITTLE_BUFFER_SIZE];
+ char *big_buffer = NULL;
+ char keystring = EQ_PREFIX;
+
+ /* Set data */
+ data.flags = DB_DBT_MALLOC;
+
+ /* Set up key */
+ key.flags = DB_DBT_MALLOC;
+ if (NULL == val)
+ {
+ /* this means, goto the first equality key */
+ /* seek to key >= "=" */
+ key.data = &keystring;
+ key.size = 1;
+ }
+ else
+ {
+ size_t key_length = strlen(val) + 2;
+ if (key_length <= SEQ_LITTLE_BUFFER_SIZE) {
+ key.data = &little_buffer;
+ } else {
+ big_buffer = slapi_ch_malloc(key_length);
+ if (NULL == big_buffer) {
+ /* memory allocation failure */
+ dblayer_release_index_file( be, ai, db );
+ return -1;
+ }
+ key.data = big_buffer;
+ }
+ key.size = sprintf(key.data,"%c%s",EQ_PREFIX,val);
+ }
+
+ /* decide which type of operation we're being asked to do and do the db bit */
+ /* The c_get call always mallocs memory for data.data */
+ /* The c_get call mallocs memory for key.data, except for DB_SET */
+ /* after this, we leave data containing the retrieved IDL, or NULL if we didn't get it */
+
+ switch (type) {
+ case SLAPI_SEQ_FIRST:
+ /* if (NULL == val) goto the first equality key ( seek to key >= "=" ) */
+ /* else goto the first equality key >= val ( seek to key >= "=val" )*/
+ return_value = dbc->c_get(dbc,&key,&data,DB_SET_RANGE);
+ break;
+ case SLAPI_SEQ_NEXT:
+ /* seek to the indicated =value, then seek to the next entry, */
+ return_value = dbc->c_get(dbc,&key,&data,DB_SET);
+ if (0 == return_value)
+ {
+ free(data.data);
+ return_value = dbc->c_get(dbc,&key,&data,DB_NEXT);
+ }
+ else
+ {
+ /* DB_SET doesn't allocate key data. Make sure we don't try to free it... */
+ key.data= NULL;
+ }
+ break;
+ case SLAPI_SEQ_PREV:
+ /* seek to the indicated =value, then seek to the previous entry, */
+ return_value = dbc->c_get(dbc,&key,&data,DB_SET);
+ if (0 == return_value )
+ {
+ free(data.data);
+ return_value = dbc->c_get(dbc,&key,&data,DB_PREV);
+ }
+ else
+ {
+ /* DB_SET doesn't allocate key data. Make sure we don't try to free it... */
+ key.data= NULL;
+ }
+ break;
+ case SLAPI_SEQ_LAST:
+ /* seek to the first possible key after all the equality keys (">"), then seek back one */
+ {
+ keystring = EQ_PREFIX + 1;
+ key.data = &keystring;
+ key.size = 1;
+ return_value = dbc->c_get(dbc,&key,&data,DB_SET_RANGE);
+ if (0 == return_value || DB_NOTFOUND == return_value)
+ {
+ free(data.data);
+ return_value = dbc->c_get(dbc,&key,&data,DB_PREV);
+ }
+ }
+ break;
+ default:
+ PR_ASSERT(0);
+ }
+
+ dbc->c_close(dbc);
+
+ if (0 == return_value && key.data!=NULL)
+ {
+
+ /* Now check that the key we eventually settled on was an equality key ! */
+ if (*((char*)key.data) == EQ_PREFIX)
+ {
+ /* Retrieve the idlist for this key */
+ key.flags = 0;
+ for (retry_count = 0; retry_count < IDL_FETCH_RETRY_COUNT; retry_count++) {
+ err = NEW_IDL_DEFAULT;
+ idl = idl_fetch( be, db, &key, NULL, ai, &err );
+ if(err == DB_LOCK_DEADLOCK) {
+ ldbm_nasty("ldbm_back_seq deadlock retry", 1600, err);
+ continue;
+ } else {
+ break;
+ }
+ }
+ }
+ }
+ if(retry_count == IDL_FETCH_RETRY_COUNT) {
+ ldbm_nasty("ldbm_back_seq retry count exceeded",1645,err);
+ } else if ( err != 0 && err != DB_NOTFOUND ) {
+ ldbm_nasty("ldbm_back_seq database error", 1650, err);
+ }
+ free( data.data );
+ if ( key.data != little_buffer && key.data != &keystring ) {
+ free( key.data );
+ }
+ free( big_buffer );
+ }
+
+ /* null idlist means there were no matching keys */
+ if ( idl != NULL )
+ {
+ /*
+ * Step through the IDlist. For each ID, get the entry
+ * and send it.
+ */
+ ID id;
+ struct backentry *e;
+ for ( id = idl_firstid( idl ); id != NOID;
+ id = idl_nextid( idl, id ))
+ {
+ if (( e = id2entry( be, id, NULL, &err )) == NULL )
+ {
+ if ( err != LDAP_SUCCESS )
+ {
+ LDAPDebug( LDAP_DEBUG_ANY, "seq id2entry err %d\n", err, 0, 0 );
+ }
+ LDAPDebug( LDAP_DEBUG_ARGS,
+ "ldbm_back_seq: candidate %lu not found\n",
+ (u_long)id, 0, 0 );
+ continue;
+ }
+ if ( slapi_send_ldap_search_entry( pb, e->ep_entry, NULL, NULL, 0 ) == 0 )
+ {
+ nentries++;
+ }
+ cache_return( &inst->inst_cache, &e );
+ }
+ idl_free( idl );
+ }
+
+ dblayer_release_index_file( be, ai, db );
+
+ slapi_send_ldap_result( pb, LDAP_SUCCESS == err ? LDAP_SUCCESS : LDAP_OPERATIONS_ERROR, NULL, NULL, nentries, NULL );
+
+ return 0;
+}
diff --git a/ldap/servers/slapd/back-ldbm/sort.c b/ldap/servers/slapd/back-ldbm/sort.c
new file mode 100644
index 00000000..4a25e068
--- /dev/null
+++ b/ldap/servers/slapd/back-ldbm/sort.c
@@ -0,0 +1,1031 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+/* Code to implement result sorting */
+
+#include "back-ldbm.h"
+
+#define CHECK_INTERVAL 10 /* The frequency whith which we'll check the admin limits */
+
+/* Structure to carry the things we need down the call stack */
+struct baggage_carrier {
+ backend *be; /* For id2entry */
+ Slapi_PBlock *pb; /* For slapi_op_abandoned */
+ time_t stoptime; /* For timelimit policing */
+ int lookthrough_limit;
+ int check_counter; /* Used to avoid checking every 100ns */
+};
+typedef struct baggage_carrier baggage_carrier;
+
+static int slapd_qsort (baggage_carrier *bc,IDList *list,sort_spec *s);
+static int print_out_sort_spec(char* buffer,sort_spec *s,int *size);
+
+static void sort_spec_thing_free(sort_spec_thing *s)
+{
+ if (NULL != s->type) {
+ slapi_ch_free((void **)&s->type);
+ }
+ if (NULL != s->matchrule) {
+ slapi_ch_free( (void**)&s->matchrule);
+ }
+ if (NULL != s->mr_pb) {
+ destroy_matchrule_indexer(s->mr_pb);
+ slapi_pblock_destroy (s->mr_pb);
+ }
+ slapi_ch_free( (void**)&s);
+}
+
+static sort_spec_thing *sort_spec_thing_allocate()
+{
+ return (sort_spec_thing *) slapi_ch_calloc(1,sizeof (sort_spec_thing));
+}
+
+void sort_spec_free(sort_spec *s)
+{
+ /* Walk down the list freeing */
+ sort_spec_thing *t = (sort_spec_thing*)s;
+ sort_spec_thing *p = NULL;
+ do {
+ p = t->next;
+ sort_spec_thing_free(t);
+ t = p;
+ } while (p);
+}
+
+static sort_spec_thing * sort_spec_thing_new(char *type, char* matchrule, int reverse)
+{
+ sort_spec_thing *s = sort_spec_thing_allocate();
+ if (NULL == s) {
+ return s;
+ }
+ s->type = type;
+ s->matchrule = matchrule;
+ s->order = reverse;
+ return s;
+}
+
+void sort_log_access(Slapi_PBlock *pb,sort_spec_thing *s,IDList *candidates)
+{
+#define SORT_LOG_BSZ 64
+#define SORT_LOG_PAD 22 /* space for the number of candidates */
+ char stack_buffer[SORT_LOG_BSZ + SORT_LOG_PAD];
+ char *buffer = NULL;
+ int ret = 0;
+ int size = SORT_LOG_BSZ + SORT_LOG_PAD;
+ char *prefix = "SORT ";
+ int prefix_size = strlen(prefix);
+
+ buffer = stack_buffer;
+ size -= sprintf(buffer,"%s",prefix);
+ ret = print_out_sort_spec(buffer+prefix_size,s,&size);
+ if (0 != ret) {
+ /* It wouldn't fit in the buffer */
+ buffer = slapi_ch_malloc(prefix_size + size + SORT_LOG_PAD);
+ sprintf(buffer,"%s",prefix);
+ ret = print_out_sort_spec(buffer+prefix_size,s,&size);
+ }
+ if (candidates) {
+ if (ALLIDS(candidates)) {
+ sprintf(buffer+size+prefix_size,"(*)");
+ } else {
+ sprintf(buffer+size+prefix_size,"(%lu)",(u_long)candidates->b_nids);
+ }
+ }
+ /* Now output it */
+ ldbm_log_access_message(pb,buffer);
+ if (buffer != stack_buffer) {
+ slapi_ch_free( (void**)&buffer);
+ }
+}
+
+/* Fix for bug # 394184, SD, 20 Jul 00 */
+/* replace the hard coded return value by the appropriate LDAP error code */
+/* also removed an useless if (0 == return_value) {} statement */
+/* Given a candidate list and a list of sort order specifications, sort this, or cop out */
+/* Returns: 0 -- sorted OK now is: LDAP_SUCCESS (fix for bug #394184)
+ * -1 -- protocol error now is: LDAP_PROTOCOL_ERROR
+ * -2 -- too hard to sort these now is: LDAP_UNWILLING_TO_PERFORM
+ * -3 -- operation error now is: LDAP_OPERATIONS_ERROR
+ * -4 -- timeout now is: LDAP_TIMELIMIT_EXCEEDED
+ * -5 -- admin limit exceeded now is: LDAP_ADMINLIMIT_EXCEEDED
+ * -6 -- abandoned now is: LDAP_OTHER
+ */
+/*
+ * So here's the plan:
+ * Plan A: We do a regular quicksort on the entries.
+ * Plan B: Through some hint given us from on high, we
+ * determine that the entries are _already_
+ * sorted as requested, thus we do nothing !
+ * Plan C: We determine that sorting these suckers is
+ * far too hard for us to even try, so we refuse.
+ */
+int sort_candidates(backend *be,int lookthrough_limit,time_t time_up, Slapi_PBlock *pb,
+ IDList *candidates, sort_spec_thing *s, char **sort_error_type)
+{
+ int return_value = LDAP_SUCCESS;
+ baggage_carrier bc = {0};
+ sort_spec_thing *this_s = NULL;
+
+ /* We refuse to sort a non-existent IDlist */
+ if (NULL == candidates) {
+ return LDAP_UNWILLING_TO_PERFORM;
+ }
+ /* we refuse to sort a candidate list which is vast */
+ if (ALLIDS(candidates)) {
+ LDAPDebug( LDAP_DEBUG_TRACE, "Asked to sort ALLIDS candidate list, refusing\n",0, 0, 0 );
+ return LDAP_UNWILLING_TO_PERFORM;
+ }
+
+ /* Iterate over the sort types */
+ for (this_s = s; this_s; this_s=this_s->next) {
+ if (NULL == this_s->matchrule) {
+ void *pi;
+ int return_value = 0;
+ return_value = slapi_attr_type2plugin( this_s->type, &pi );
+ if (0 == return_value) {
+ return_value = plugin_call_syntax_get_compare_fn( pi, &(this_s->compare_fn) );
+ }
+ if (return_value != 0 ) {
+ LDAPDebug( LDAP_DEBUG_TRACE, "Attempting to sort a non-ordered attribute (%s)\n",this_s->type, 0, 0 );
+ /* DBDB we should set the error type here */
+ return_value = LDAP_UNWILLING_TO_PERFORM;
+ *sort_error_type = this_s->type;
+ return return_value;
+ }
+ } else {
+ /* Need to---find the matching rule plugin,
+ * tell it it needs to do ordering for this OID
+ * see whether it agrees---if not signal error to client
+ * Then later use it for generating ordering keys.
+ * finally, free it up
+ */
+ return_value = create_matchrule_indexer(&this_s->mr_pb,this_s->matchrule,this_s->type);
+ if (LDAP_SUCCESS != return_value) {
+ *sort_error_type = this_s->type;
+ return return_value;
+ }
+ this_s->compare_fn = slapi_berval_cmp;
+ }
+ }
+
+ bc.be = be;
+ bc.pb = pb;
+ bc.stoptime = time_up;
+ bc.lookthrough_limit = lookthrough_limit;
+ bc.check_counter = 1;
+
+ return_value = slapd_qsort(&bc,candidates,s);
+ LDAPDebug( LDAP_DEBUG_TRACE, "<= Sorting done\n",0, 0, 0 );
+
+ return return_value;
+}
+/* End fix for bug # 394184 */
+
+/* Fix for bug # 394184, SD, 20 Jul 00 */
+/* fix and cleanup (switch(code) {} removed) */
+/* arg 'code' has now the correct sortResult value */
+int
+make_sort_response_control ( Slapi_PBlock *pb, int code, char *error_type) {
+
+ LDAPControl new_ctrl = {0};
+ BerElement *ber= NULL;
+ struct berval *bvp = NULL;
+ int rc = -1;
+ int control_code = code;
+
+ /*
+ 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] AttributeType OPTIONAL }
+
+ */
+
+ if ( ( ber = ber_alloc()) == NULL ) {
+ return -1;
+ }
+
+ if (( rc = ber_printf( ber, "{e", control_code )) != -1 ) {
+ if ( rc != -1 && NULL != error_type ) {
+ rc = ber_printf( ber, "s", error_type );
+ }
+ if ( rc != -1 ) {
+ rc = ber_printf( ber, "}" );
+ }
+ }
+ if ( rc != -1 ) {
+ rc = ber_flatten( ber, &bvp );
+ }
+
+ ber_free( ber, 1 );
+
+ if ( rc == -1 ) {
+ return rc;
+ }
+
+ new_ctrl.ldctl_oid = LDAP_CONTROL_SORTRESPONSE;
+ new_ctrl.ldctl_value = *bvp;
+ new_ctrl.ldctl_iscritical = 1;
+
+ if ( slapi_pblock_set( pb, SLAPI_ADD_RESCONTROL, &new_ctrl ) != 0 ) {
+ ber_bvfree(bvp);
+ return( -1 );
+ }
+
+ ber_bvfree(bvp);
+ return( LDAP_SUCCESS );
+}
+/* End fix for bug #394184 */
+
+static int term_tag(unsigned long tag)
+{
+ return ( (LBER_END_OF_SEQORSET == tag) || (LBER_ERROR == tag) );
+}
+
+/* hacky function to convert a sort spec to a string
+ you specify a buffer and a size. If the thing won't fit, it returns
+ non-zero, and the size needed. Pass NULL buffer to just get the size */
+static int print_out_sort_spec(char* buffer,sort_spec *s,int *size)
+{
+ /* Walk down the list printing */
+ sort_spec_thing *t = (sort_spec_thing*)s;
+ sort_spec_thing *p = NULL;
+ int buffer_size = 0;
+ int input_size = 0;
+
+ if (NULL != size) {
+ input_size = *size;
+ }
+ do {
+ p = t->next;
+
+ buffer_size += strlen(t->type);
+ if (t->order) {
+ buffer_size += 1; /* For the '-' */
+ }
+ if (NULL != t->matchrule) {
+ /* space for matchrule + semicolon */
+ buffer_size += strlen(t->matchrule) + 1;
+ }
+ buffer_size += 1; /* for the space */
+ if ( (NULL != buffer) && (buffer_size <= input_size) ) {
+ /* write into the buffer */
+ buffer += sprintf(buffer,"%s%s%s%s ",
+ t->order ? "-" : "",
+ t->type,
+ ( NULL == t->matchrule ) ? "" : ";",
+ ( NULL == t->matchrule ) ? "" : t->matchrule);
+ }
+
+ t = p;
+ } while (p);
+ if (NULL != size) {
+ *size = buffer_size;
+ }
+ if (buffer_size <= input_size) {
+ return 0;
+ } else {
+ return 1;
+ }
+}
+
+int parse_sort_spec(struct berval *sort_spec_ber, sort_spec **ps)
+{
+ /* So here we call ber_scanf to get the sort spec */
+ /* This control looks like this :
+ SortKeyList ::= SEQUENCE OF SEQUENCE {
+ attributeType AttributeType,
+ orderingRule [0] MatchingRuleId OPTIONAL,
+ reverseOrder [1] BOOLEAN DEFAULT FALSE }
+ */
+ BerElement *ber = NULL;
+ sort_spec_thing *listhead = NULL;
+ unsigned long tag = 0;
+ unsigned long len = 0;
+ char *last = NULL;
+ sort_spec_thing *listpointer = NULL;
+ char *type = NULL;
+ char *matchrule = NULL;
+ int rc = LDAP_SUCCESS;
+
+ ber = ber_init(sort_spec_ber);
+ if(ber==NULL)
+ {
+ return -1;
+ }
+
+ /* Work our way along the BER, one sort spec at a time */
+ for ( tag = ber_first_element( ber, &len, &last ); !term_tag(tag); tag = ber_next_element( ber, &len, last )) {
+ /* we're now pointing at the beginning of a sequence of type, matching rule and reverse indicator */
+
+ char *inner_last = NULL;
+ char *rtype = NULL;
+ int reverse = 0;
+ unsigned long next_tag = 0;
+ sort_spec_thing *s = NULL;
+ unsigned long return_value;
+
+ next_tag = ber_first_element( ber, &len, &inner_last );
+
+ /* The type is not optional */
+
+ return_value = ber_scanf(ber,"a",&rtype);
+ if (LBER_ERROR == return_value) {
+ rc = LDAP_PROTOCOL_ERROR;
+ goto err;
+ }
+ /* normalize */
+ type = slapi_attr_syntax_normalize(rtype);
+ free(rtype);
+
+ /* Now look for the next tag. */
+
+ next_tag = ber_next_element(ber,&len, inner_last);
+
+ /* Are we done ? */
+ if ( !term_tag(next_tag) ) {
+ /* Is it the matching rule ? */
+ if (LDAP_TAG_SK_MATCHRULE == next_tag) {
+ /* If so, get it */
+ ber_scanf(ber,"a",&matchrule);
+ /* That can be followed by a reverse indicator */
+ next_tag = ber_next_element(ber,&len, inner_last);
+ if (LDAP_TAG_SK_REVERSE == next_tag) {
+ /* Get the reverse sort indicator here */
+ ber_scanf(ber,"b",&reverse);
+ /* The protocol police say--"You must have other than your default value" */
+ if (0 == reverse) {
+ /* Protocol error */
+ rc = LDAP_PROTOCOL_ERROR;
+ goto err;
+ }
+ } else {
+ /* Perhaps we're done now ? */
+ if (LBER_END_OF_SEQORSET != next_tag) {
+ /* Protocol error---we got a matching rule, but followed by something other
+ * than reverse or end of sequence.
+ */
+ rc = LDAP_PROTOCOL_ERROR;
+ goto err;
+ }
+ }
+ } else {
+ /* Is it the reverse indicator ? */
+ if (LDAP_TAG_SK_REVERSE == next_tag) {
+ /* If so, get it */
+ ber_scanf(ber,"b",&reverse);
+ } else {
+ /* Protocol error---tag which isn't either of the legal ones came first */
+ rc = LDAP_PROTOCOL_ERROR;
+ goto err;
+ }
+ }
+ }
+
+ s = sort_spec_thing_new(type,matchrule,reverse);
+ if (NULL == s) {
+ /* Memory allocation failed */
+ rc = LDAP_OPERATIONS_ERROR;
+ goto err;
+ }
+ type = matchrule = NULL;
+ if (NULL != listpointer) {
+ listpointer->next = s;
+ }
+ listpointer = s;
+ if (NULL == listhead) {
+ listhead = s;
+ }
+
+ }
+
+ if (NULL == listhead) { /* LP - defect #559792 - don't return null listhead */
+ *ps = NULL;
+ rc = LDAP_PROTOCOL_ERROR;
+ goto err;
+ }
+
+ /* the ber encoding is no longer needed */
+ ber_free(ber,1);
+
+ *ps = (sort_spec *)listhead;
+
+
+ return LDAP_SUCCESS;
+
+ err:
+ if (listhead) sort_spec_free((sort_spec*) listhead);
+ slapi_ch_free((void**)&type);
+ slapi_ch_free((void**)&matchrule);
+ ber_free(ber,1);
+
+ return rc;
+}
+
+#if 0
+static int attr_value_compare(struct berval *value_a, struct berval *value_b)
+{
+ /* return value_cmp(value_a,value_b,syntax,3); */
+ return strcasecmp(value_a->bv_val, value_b->bv_val);
+}
+#endif
+
+struct berval* attr_value_lowest(struct berval **values, value_compare_fn_type compare_fn)
+{
+ /* We iterate through the values, storing our last best guess as to the lowest */
+ struct berval *lowest_so_far = values[0];
+ struct berval *this_one = NULL;
+
+ for (this_one = *values; this_one; this_one = *values++) {
+ if (compare_fn(lowest_so_far,this_one) > 0) {
+ lowest_so_far = this_one;
+ }
+ }
+ return lowest_so_far;
+}
+
+int sort_attr_compare(struct berval ** value_a, struct berval ** value_b, value_compare_fn_type compare_fn)
+{
+ /* So, the thing we need to do here is to look out for multi-valued
+ * attributes. When we get one of those, we need to look through all the
+ * values to find the lowest one (per X.511 edict). We then use that one to
+ * compare against the other. We should really put some logic in here to
+ * prevent us partying on an attribute with thousands of values for a long time.
+ */
+ struct berval *compare_value_a = NULL;
+ struct berval *compare_value_b = NULL;
+
+ compare_value_a = attr_value_lowest(value_a, compare_fn);
+ compare_value_b = attr_value_lowest(value_b, compare_fn);
+
+ return compare_fn(compare_value_a,compare_value_b);
+
+}
+
+
+#if 0
+/* USE THE _SV VERSION NOW */
+
+/* Comparison routine, called by qsort.
+ * The job here is to return the correct value
+ * for the operation a < b
+ * Returns:
+ * <0 when a < b
+ * 0 when a == b
+ * >0 when a > b
+ */
+static int compare_entries(ID *id_a, ID *id_b, sort_spec *s,baggage_carrier *bc, int *error)
+{
+ /* We get passed the IDs, but need to fetch the entries in order to
+ * perform the comparison .
+ */
+ struct backentry *a = NULL;
+ struct backentry *b = NULL;
+ int result = 0;
+ sort_spec_thing *this_one = NULL;
+ int return_value = -1;
+ backend *be = bc->be;
+ ldbm_instance *inst = (ldbm_instance *) be->be_instance_info;
+ int err;
+
+ *error = 1;
+ a = id2entry(be,*id_a,NULL,&err);
+ if (NULL == a) {
+ if (0 != err ) {
+ LDAPDebug(LDAP_DEBUG_ANY,"compare_entries db err %d\n",err,0,0);
+ }
+ /* Were up a creek without paddle here */
+ /* Best to log error and set some flag */
+ return 0;
+ }
+ b = id2entry(be,*id_b,NULL,&err);
+ if (NULL == b) {
+ if (0 != err ) {
+ LDAPDebug(LDAP_DEBUG_ANY,"compare_entries db err %d\n",err,0,0);
+ }
+ return 0;
+ }
+ /* OK, now we have the entries, so we work our way down the attribute list comparing as we go */
+ for (this_one = (sort_spec_thing*)s; this_one ; this_one = this_one->next) {
+
+ char *type = this_one->type;
+ int order = this_one->order;
+ Slapi_Attr *attr_a = NULL;
+ Slapi_Attr *attr_b = NULL;
+ struct berval **value_a = NULL;
+ struct berval **value_b = NULL;
+
+ /* Get the two attribute values from the entries */
+ return_value = slapi_entry_attr_find(a->ep_entry,type,&attr_a);
+ return_value = slapi_entry_attr_find(b->ep_entry,type,&attr_b);
+ /* What do we do if one or more of the entries lacks this attribute ? */
+ /* if one lacks the attribute */
+ if (NULL == attr_a) {
+ /* then if the other does too, they're equal */
+ if (NULL == attr_b) {
+ result = 0;
+ continue;
+ } else
+ {
+ /* If one has the attribute, and the other
+ * doesn't, the missing attribute is the
+ * LARGER one. (bug #108154) -robey
+ */
+ result = 1;
+ break;
+ }
+ }
+ if (NULL == attr_b) {
+ result = -1;
+ break;
+ }
+ /* Somewhere in here, we need to go sideways for match rule case
+ * we need to call the match rule plugin to get the attribute values
+ * converted into ordering keys. Then we proceed as usual to use those,
+ * but ensuring that we don't leak memory anywhere. This works as follows:
+ * the code assumes that the attrs are references into the entry, so
+ * doesn't try to free them. We need to note at the right place that
+ * we're on the matchrule path, and accordingly free the keys---this turns out
+ * to be when we free the indexer */
+ if (NULL == s->matchrule) {
+ /* Non-match rule case */
+ /* xxxPINAKI
+ needs modification
+
+ value_a = attr_a->a_vals;
+ value_b = attr_b->a_vals;
+ */
+ } else {
+ /* Match rule case */
+ struct berval **actual_value_b = NULL;
+ struct berval **temp_value = NULL;
+
+ /* xxxPINAKI
+ needs modification
+ struct berval **actual_value_a = NULL;
+
+ actual_value_a = attr_a->a_vals;
+ actual_value_b = attr_b->a_vals;
+ matchrule_values_to_keys(s->mr_pb,actual_value_a,&temp_value);
+ */
+ /* Now copy it, so the second call doesn't crap on it */
+ value_a = slapi_ch_bvecdup(temp_value); /* Really, we'd prefer to not call the chXXX variant...*/
+ matchrule_values_to_keys(s->mr_pb,actual_value_b,&value_b);
+ }
+ /* Compare them */
+ if (!order) {
+ result = sort_attr_compare(value_a, value_b, s->compare_fn);
+ } else {
+ /* If reverse, invert the sense of the comparison */
+ result = sort_attr_compare(value_b, value_a, s->compare_fn);
+ }
+ /* Time to free up the attribute allocated above */
+ if (NULL != s->matchrule) {
+ ber_bvecfree(value_a);
+ }
+ /* Are they equal ? */
+ if (0 != result) {
+ /* If not, we're done */
+ break;
+ }
+ /* If so, proceed to the next attribute for comparison */
+ }
+ cache_return(&inst->inst_cache,&a);
+ cache_return(&inst->inst_cache,&b);
+ *error = 0;
+ return result;
+}
+#endif
+
+/* Comparison routine, called by qsort.
+ * The job here is to return the correct value
+ * for the operation a < b
+ * Returns:
+ * <0 when a < b
+ * 0 when a == b
+ * >0 when a > b
+ */
+static int compare_entries_sv(ID *id_a, ID *id_b, sort_spec *s,baggage_carrier *bc, int *error)
+{
+ /* We get passed the IDs, but need to fetch the entries in order to
+ * perform the comparison .
+ */
+ struct backentry *a = NULL;
+ struct backentry *b = NULL;
+ int result = 0;
+ sort_spec_thing *this_one = NULL;
+ int return_value = -1;
+ backend *be = bc->be;
+ ldbm_instance *inst = (ldbm_instance *) be->be_instance_info;
+ int err;
+
+ *error = 1;
+ a = id2entry(be,*id_a,NULL,&err);
+ if (NULL == a) {
+ if (0 != err ) {
+ LDAPDebug(LDAP_DEBUG_ANY,"compare_entries db err %d\n",err,0,0);
+ }
+ /* Were up a creek without paddle here */
+ /* Best to log error and set some flag */
+ return 0;
+ }
+ b = id2entry(be,*id_b,NULL,&err);
+ if (NULL == b) {
+ if (0 != err ) {
+ LDAPDebug(LDAP_DEBUG_ANY,"compare_entries db err %d\n",err,0,0);
+ }
+ return 0;
+ }
+ /* OK, now we have the entries, so we work our way down the attribute list comparing as we go */
+ for (this_one = (sort_spec_thing*)s; this_one ; this_one = this_one->next) {
+
+ char *type = this_one->type;
+ int order = this_one->order;
+ Slapi_Attr *attr_a = NULL;
+ Slapi_Attr *attr_b = NULL;
+ struct berval **value_a = NULL;
+ struct berval **value_b = NULL;
+
+ /* Get the two attribute values from the entries */
+ return_value = slapi_entry_attr_find(a->ep_entry,type,&attr_a);
+ return_value = slapi_entry_attr_find(b->ep_entry,type,&attr_b);
+ /* What do we do if one or more of the entries lacks this attribute ? */
+ /* if one lacks the attribute */
+ if (NULL == attr_a) {
+ /* then if the other does too, they're equal */
+ if (NULL == attr_b) {
+ result = 0;
+ continue;
+ } else
+ {
+ /* If one has the attribute, and the other
+ * doesn't, the missing attribute is the
+ * LARGER one. (bug #108154) -robey
+ */
+ result = 1;
+ break;
+ }
+ }
+ if (NULL == attr_b) {
+ result = -1;
+ break;
+ }
+ /* Somewhere in here, we need to go sideways for match rule case
+ * we need to call the match rule plugin to get the attribute values
+ * converted into ordering keys. Then we proceed as usual to use those,
+ * but ensuring that we don't leak memory anywhere. This works as follows:
+ * the code assumes that the attrs are references into the entry, so
+ * doesn't try to free them. We need to note at the right place that
+ * we're on the matchrule path, and accordingly free the keys---this turns out
+ * to be when we free the indexer */
+ if (NULL == s->matchrule) {
+ /* Non-match rule case */
+ valuearray_get_bervalarray(valueset_get_valuearray(&attr_a->a_present_values),&value_a);
+ valuearray_get_bervalarray(valueset_get_valuearray(&attr_b->a_present_values),&value_b);
+ } else {
+ /* Match rule case */
+ struct berval **actual_value_a = NULL;
+ struct berval **actual_value_b = NULL;
+ struct berval **temp_value = NULL;
+
+ valuearray_get_bervalarray(valueset_get_valuearray(&attr_a->a_present_values),&actual_value_a);
+ valuearray_get_bervalarray(valueset_get_valuearray(&attr_b->a_present_values),&actual_value_b);
+ matchrule_values_to_keys(s->mr_pb,actual_value_a,&temp_value);
+ /* Now copy it, so the second call doesn't crap on it */
+ value_a = slapi_ch_bvecdup(temp_value); /* Really, we'd prefer to not call the chXXX variant...*/
+ matchrule_values_to_keys(s->mr_pb,actual_value_b,&value_b);
+ if (actual_value_a) ber_bvecfree(actual_value_a);
+ if (actual_value_b) ber_bvecfree(actual_value_b);
+ }
+ /* Compare them */
+ if (!order) {
+ result = sort_attr_compare(value_a, value_b, s->compare_fn);
+ } else {
+ /* If reverse, invert the sense of the comparison */
+ result = sort_attr_compare(value_b, value_a, s->compare_fn);
+ }
+ /* Time to free up the attributes allocated above */
+ if (NULL != s->matchrule) {
+ ber_bvecfree(value_a);
+ } else {
+ ber_bvecfree(value_a);
+ ber_bvecfree(value_b);
+ }
+ /* Are they equal ? */
+ if (0 != result) {
+ /* If not, we're done */
+ break;
+ }
+ /* If so, proceed to the next attribute for comparison */
+ }
+ cache_return(&inst->inst_cache,&a);
+ cache_return(&inst->inst_cache,&b);
+ *error = 0;
+ return result;
+}
+
+/* Fix for bug # 394184, SD, 20 Jul 00 */
+/* replace the hard coded return value by the appropriate LDAP error code */
+/*
+ * Returns:
+ * 0: Everything OK now is: LDAP_SUCCESS (fix for bug #394184)
+ * -1: A protocol error now is: LDAP_PROTOCOL_ERROR
+ * -2: Too hard now is: LDAP_UNWILLING_TO_PERFORM
+ * -3: Operation error now is: LDAP_OPERATIONS_ERROR
+ * -4: Timeout now is: LDAP_TIMELIMIT_EXCEEDED
+ * -5: Admin limit exceeded now is: LDAP_ADMINLIMIT_EXCEEDED
+ * -6: Abandoned now is: LDAP_OTHER
+ */
+static int sort_nazi(baggage_carrier *bc)
+{
+ time_t curtime = 0;
+ /* check for abandon */
+ if ( slapi_op_abandoned( bc->pb)) {
+ return LDAP_OTHER;
+ }
+
+ /* Check to see if our journey is really necessary */
+
+ if (0 == ((bc->check_counter)++ % CHECK_INTERVAL) ) {
+
+ /* check time limit */
+ curtime = current_time();
+ if ( bc->stoptime != -1 && curtime > bc->stoptime ) {
+ return LDAP_TIMELIMIT_EXCEEDED;
+ }
+
+ /* Fix for bugid #394184, SD, 05 Jul 00 */
+ /* not sure this is the appropriate place to do this;
+ since the entries are swaped in slapd_qsort, some of them are most
+ probably counted more than once */
+ /* hence commenting out the following test and moving it into slapd_qsort */
+ /* check lookthrough limit */
+ /* if ( bc->lookthrough_limit != -1 && (bc->lookthrough_limit -= CHECK_INTERVAL) < 0 ) {
+ return LDAP_ADMINLIMIT_EXCEEDED;
+ } */
+ /* end for bugid #394184 */
+
+ }
+ return LDAP_SUCCESS;
+}
+/* End fix for bug # 394184 */
+
+/* prototypes for local routines */
+static void shortsort(baggage_carrier *bc,ID *lo, ID *hi,sort_spec *s );
+static void swap (ID *a,ID *b);
+
+/* this parameter defines the cutoff between using quick sort and
+ insertion sort for arrays; arrays with lengths shorter or equal to the
+ below value use insertion sort */
+
+#define CUTOFF 8 /* testing shows that this is good value */
+
+
+/* Fix for bug # 394184, SD, 20 Jul 00 */
+/* replace the hard coded return value by the appropriate LDAP error code */
+/* Our qsort needs to police the client timeout and lookthrough limit ?
+ * It knows how to compare entries, so we don't bother with all the void * stuff.
+ */
+/*
+ * Returns:
+ * 0: Everything OK now is: LDAP_SUCCESS (fix for bug #394184)
+ * -1: A protocol error now is: LDAP_PROTOCOL_ERROR
+ * -2: Too hard now is: LDAP_UNWILLING_TO_PERFORM
+ * -3: Operation error now is: LDAP_OPERATIONS_ERROR
+ * -4: Timeout now is: LDAP_TIMELIMIT_EXCEEDED
+ * -5: Admin limit exceeded now is: LDAP_ADMINLIMIT_EXCEEDED
+ * -6: Abandoned now is: LDAP_OTHER
+ */
+static int slapd_qsort(baggage_carrier *bc,IDList *list, sort_spec *s)
+{
+ ID *lo, *hi; /* ends of sub-array currently sorting */
+ ID *mid; /* points to middle of subarray */
+ ID *loguy, *higuy; /* traveling pointers for partition step */
+ NIDS size; /* size of the sub-array */
+ ID *lostk[30], *histk[30];
+ int stkptr; /* stack for saving sub-array to be processed */
+ NIDS num = list->b_nids;
+ int return_value = LDAP_SUCCESS;
+ int error = 0;
+
+ /* Note: the number of stack entries required is no more than
+ 1 + log2(size), so 30 is sufficient for any array */
+ if (num < 2 )
+ return LDAP_SUCCESS; /* nothing to do */
+
+ stkptr = 0; /* initialize stack */
+
+ lo = &(list->b_ids[0]);
+ hi = &(list->b_ids[num-1]); /* initialize limits */
+
+ /* Fix for bugid #394184, SD, 20 Jul 00 */
+ if ( bc->lookthrough_limit != -1 && ( bc->lookthrough_limit <= (int) list->b_nids) ) {
+ return LDAP_ADMINLIMIT_EXCEEDED;
+ }
+ /* end Fix for bugid #394184 */
+
+ /* this entry point is for pseudo-recursion calling: setting
+ lo and hi and jumping to here is like recursion, but stkptr is
+ prserved, locals aren't, so we preserve stuff on the stack */
+recurse:
+
+ size = (hi - lo) + 1; /* number of el's to sort */
+
+ /* below a certain size, it is faster to use a O(n^2) sorting method */
+ if (size <= CUTOFF) {
+ shortsort(bc,lo, hi, s );
+ }
+ else {
+ /* First we pick a partititioning element. The efficiency of the
+ algorithm demands that we find one that is approximately the
+ median of the values, but also that we select one fast. Using
+ the first one produces bad performace if the array is already
+ sorted, so we use the middle one, which would require a very
+ wierdly arranged array for worst case performance. Testing shows
+ that a median-of-three algorithm does not, in general, increase
+ performance. */
+
+ mid = lo + (size / 2); /* find middle element */
+ swap(mid, lo); /* swap it to beginning of array */
+
+ /* We now wish to partition the array into three pieces, one
+ consisiting of elements <= partition element, one of elements
+ equal to the parition element, and one of element >= to it. This
+ is done below; comments indicate conditions established at every
+ step. */
+
+ loguy = lo;
+ higuy = hi + 1;
+
+ /* Note that higuy decreases and loguy increases on every iteration,
+ so loop must terminate. */
+ for (;;) {
+ /* lo <= loguy < hi, lo < higuy <= hi + 1,
+ A[i] <= A[lo] for lo <= i <= loguy,
+ A[i] >= A[lo] for higuy <= i <= hi */
+
+ do {
+ loguy ++;
+ } while (loguy <= hi && compare_entries_sv(loguy, lo, s, bc, &error) <= 0);
+
+ /* lo < loguy <= hi+1, A[i] <= A[lo] for lo <= i < loguy,
+ either loguy > hi or A[loguy] > A[lo] */
+
+ do {
+ higuy --;
+ } while (higuy > lo && compare_entries_sv(higuy, lo, s, bc, &error) >= 0);
+
+ /* lo-1 <= higuy <= hi, A[i] >= A[lo] for higuy < i <= hi,
+ either higuy <= lo or A[higuy] < A[lo] */
+
+ if (higuy < loguy)
+ break;
+
+ /* if loguy > hi or higuy <= lo, then we would have exited, so
+ A[loguy] > A[lo], A[higuy] < A[lo],
+ loguy < hi, highy > lo */
+
+ swap(loguy, higuy);
+
+ /* Check admin and time limits here on the sort */
+ if ( LDAP_SUCCESS != (return_value = sort_nazi(bc)) )
+ {
+ return return_value;
+ }
+
+ /* A[loguy] < A[lo], A[higuy] > A[lo]; so condition at top
+ of loop is re-established */
+ }
+
+ /* A[i] >= A[lo] for higuy < i <= hi,
+ A[i] <= A[lo] for lo <= i < loguy,
+ higuy < loguy, lo <= higuy <= hi
+ implying:
+ A[i] >= A[lo] for loguy <= i <= hi,
+ A[i] <= A[lo] for lo <= i <= higuy,
+ A[i] = A[lo] for higuy < i < loguy */
+
+ swap(lo, higuy); /* put partition element in place */
+
+ /* OK, now we have the following:
+ A[i] >= A[higuy] for loguy <= i <= hi,
+ A[i] <= A[higuy] for lo <= i < higuy
+ A[i] = A[lo] for higuy <= i < loguy */
+
+ /* We've finished the partition, now we want to sort the subarrays
+ [lo, higuy-1] and [loguy, hi].
+ We do the smaller one first to minimize stack usage.
+ We only sort arrays of length 2 or more.*/
+
+ if ( higuy - 1 - lo >= hi - loguy ) {
+ if (lo + 1 < higuy) {
+ lostk[stkptr] = lo;
+ histk[stkptr] = higuy - 1;
+ ++stkptr;
+ } /* save big recursion for later */
+
+ if (loguy < hi) {
+ lo = loguy;
+ goto recurse; /* do small recursion */
+ }
+ }
+ else {
+ if (loguy < hi) {
+ lostk[stkptr] = loguy;
+ histk[stkptr] = hi;
+ ++stkptr; /* save big recursion for later */
+ }
+
+ if (lo + 1 < higuy) {
+ hi = higuy - 1;
+ goto recurse; /* do small recursion */
+ }
+ }
+ }
+
+ /* We have sorted the array, except for any pending sorts on the stack.
+ Check if there are any, and do them. */
+
+ --stkptr;
+ if (stkptr >= 0) {
+ lo = lostk[stkptr];
+ hi = histk[stkptr];
+ goto recurse; /* pop subarray from stack */
+ }
+ else
+ return LDAP_SUCCESS; /* all subarrays done */
+}
+/* End fix for bug # 394184 */
+
+
+static void shortsort (
+ baggage_carrier *bc,
+ ID *lo,
+ ID *hi,
+ sort_spec *s
+ )
+{
+ ID *p, *max;
+ int error = 0;
+
+ /* Note: in assertions below, i and j are alway inside original bound of
+ array to sort. */
+
+ while (hi > lo) {
+ /* A[i] <= A[j] for i <= j, j > hi */
+ max = lo;
+ for (p = lo+1; p <= hi; p++) {
+ /* A[i] <= A[max] for lo <= i < p */
+ if (compare_entries_sv(p,max,s,bc,&error) > 0) {
+ max = p;
+ }
+ /* A[i] <= A[max] for lo <= i <= p */
+ }
+
+ /* A[i] <= A[max] for lo <= i <= hi */
+
+ swap(max, hi);
+
+ /* A[i] <= A[hi] for i <= hi, so A[i] <= A[j] for i <= j, j >= hi */
+
+ hi--;
+
+ /* A[i] <= A[j] for i <= j, j > hi, loop top condition established */
+ }
+ /* A[i] <= A[j] for i <= j, j > lo, which implies A[i] <= A[j] for i < j,
+ so array is sorted */
+}
+
+static void swap (ID *a,ID *b)
+{
+ ID tmp;
+
+ if ( a != b ) {
+ tmp = *a;
+ *a = *b;
+ *b = tmp;
+ }
+}
+
+
diff --git a/ldap/servers/slapd/back-ldbm/start.c b/ldap/servers/slapd/back-ldbm/start.c
new file mode 100644
index 00000000..f87b7112
--- /dev/null
+++ b/ldap/servers/slapd/back-ldbm/start.c
@@ -0,0 +1,177 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+/*
+ * start.c
+ */
+
+#include "back-ldbm.h"
+
+/*
+ * Start the LDBM plugin, and all its instances.
+ */
+int
+ldbm_back_start( Slapi_PBlock *pb )
+{
+ struct ldbminfo *li;
+ static int initialized = 0;
+ char *home_dir;
+ int action;
+ int retval;
+
+ LDAPDebug( LDAP_DEBUG_TRACE, "ldbm backend starting\n", 0, 0, 0 );
+
+ slapi_pblock_get( pb, SLAPI_PLUGIN_PRIVATE, &li );
+
+ /* parse the config file here */
+ ldbm_config_load_dse_info(li);
+
+ /* register with the binder-based resource limit subsystem so that */
+ /* lookthroughlimit can be supported on a per-connection basis. */
+ if ( slapi_reslimit_register( SLAPI_RESLIMIT_TYPE_INT,
+ LDBM_LOOKTHROUGHLIMIT_AT, &li->li_reslimit_lookthrough_handle )
+ != SLAPI_RESLIMIT_STATUS_SUCCESS ) {
+ LDAPDebug( LDAP_DEBUG_ANY, "start: Resource limit registration failed\n",
+ 0, 0, 0 );
+ return SLAPI_FAIL_GENERAL;
+ }
+
+ /* If the db directory hasn't been set yet, we need to set it to
+ * the default. */
+ if ('\0' == li->li_directory[0]) {
+ /* "get default" is a special string that tells the config
+ * routines to figure out the default db directory by
+ * reading cn=config. */
+ ldbm_config_internal_set(li, CONFIG_DIRECTORY, "get default");
+ }
+
+ /* sanity check the autosizing values,
+ no value or sum of values larger than 100.
+ */
+ if ( (li->li_cache_autosize > 100) ||
+ (li->li_cache_autosize_split > 100) ||
+ (li->li_import_cache_autosize > 100) ||
+ ((li->li_cache_autosize > 0) && (li->li_import_cache_autosize > 0) &&
+ (li->li_cache_autosize + li->li_import_cache_autosize > 100)) )
+ {
+ LDAPDebug( LDAP_DEBUG_ANY, "cache autosizing: bad settings, "
+ "value or sum of values can not larger than 100.\n", 0, 0, 0 );
+ } else
+ /* if cache autosize was selected, select the cache sizes now */
+ if ((li->li_cache_autosize > 0) || (li->li_import_cache_autosize > 0)) {
+ size_t pagesize, pages, procpages, availpages;
+
+ dblayer_sys_pages(&pagesize, &pages, &procpages, &availpages);
+ if (pagesize) {
+ char s[32]; /* big enough to hold %ld */
+ unsigned long cache_size_to_configure = 0;
+ int zone_pages, db_pages, entry_pages, import_pages;
+ Object *inst_obj;
+ ldbm_instance *inst;
+ /* autosizing dbCache and entryCache */
+ if (li->li_cache_autosize) {
+ zone_pages = (li->li_cache_autosize * pages) / 100;
+ /* now split it according to user prefs */
+ db_pages = (li->li_cache_autosize_split * zone_pages) / 100;
+ /* fudge an extra instance into our calculations... */
+ entry_pages = (zone_pages - db_pages) /
+ (objset_size(li->li_instance_set) + 1);
+ LDAPDebug(LDAP_DEBUG_ANY, "cache autosizing. found %dk physical memory\n",
+ pages*(pagesize/1024), 0, 0);
+ LDAPDebug(LDAP_DEBUG_ANY, "cache autosizing: db cache: %dk, "
+ "each entry cache (%d total): %dk\n",
+ db_pages*(pagesize/1024), objset_size(li->li_instance_set),
+ entry_pages*(pagesize/1024));
+
+ /* libdb allocates 1.25x the amount we tell it to, but only for values < 500Meg */
+ if (cache_size_to_configure < (500 * MEGABYTE)) {
+ cache_size_to_configure = (unsigned long)((db_pages * pagesize) / 1.25);
+ } else {
+ cache_size_to_configure = (unsigned long)(db_pages * pagesize);
+ }
+ sprintf(s, "%lu", cache_size_to_configure);
+ ldbm_config_internal_set(li, CONFIG_DBCACHESIZE, s);
+ li->li_cache_autosize_ec = (unsigned long)entry_pages * pagesize;
+
+ for (inst_obj = objset_first_obj(li->li_instance_set); inst_obj;
+ inst_obj = objset_next_obj(li->li_instance_set, inst_obj)) {
+ inst = (ldbm_instance *)object_get_data(inst_obj);
+ cache_set_max_entries(&(inst->inst_cache), -1);
+ cache_set_max_size(&(inst->inst_cache), li->li_cache_autosize_ec);
+ }
+ }
+ /* autosizing importCache */
+ if (li->li_import_cache_autosize) {
+ /* For some reason, -1 means 50 ... */
+ if (li->li_import_cache_autosize == -1) {
+ li->li_import_cache_autosize = 50;
+ }
+ import_pages = (li->li_import_cache_autosize * pages) / 100;
+ LDAPDebug(LDAP_DEBUG_ANY, "cache autosizing: import cache: %dk \n",
+ import_pages*(pagesize/1024), NULL, NULL);
+
+ sprintf(s, "%lu", (unsigned long)(import_pages * pagesize));
+ ldbm_config_internal_set(li, CONFIG_IMPORT_CACHESIZE, s);
+ }
+ }
+ }
+
+ retval = check_db_version(li, &action);
+ if (0 != retval)
+ {
+ LDAPDebug( LDAP_DEBUG_ANY, "start: db version is not supported\n",
+ 0, 0, 0);
+ return SLAPI_FAIL_GENERAL;
+ }
+
+ if (action & DBVERSION_UPGRADE_3_4)
+ {
+ retval = dblayer_start(li,DBLAYER_CLEAN_RECOVER_MODE);
+ }
+ else
+ {
+ retval = dblayer_start(li,DBLAYER_NORMAL_MODE);
+ }
+ if (0 != retval) {
+ char *msg;
+ LDAPDebug( LDAP_DEBUG_ANY, "start: Failed to init database, err=%d %s\n",
+ retval, (msg = dblayer_strerror( retval )) ? msg : "", 0 );
+ if (LDBM_OS_ERR_IS_DISKFULL(retval)) return return_on_disk_full(li);
+ else return SLAPI_FAIL_GENERAL;
+ }
+
+ /* Walk down the instance list, starting all the instances. */
+ retval = ldbm_instance_startall(li);
+ if (0 != retval) {
+ char *msg;
+ LDAPDebug( LDAP_DEBUG_ANY, "start: Failed to start databases, err=%d %s\n",
+ retval, (msg = dblayer_strerror( retval )) ? msg : "", 0 );
+ if (LDBM_OS_ERR_IS_DISKFULL(retval)) return return_on_disk_full(li);
+ else return SLAPI_FAIL_GENERAL;
+ }
+
+ /* write DBVERSION file if one does not exist */
+ home_dir = dblayer_get_home_dir(li, NULL);
+ if (!dbversion_exists(li, home_dir))
+ {
+ dbversion_write (li, home_dir, NULL);
+ }
+
+
+ /* this function is called every time new db is initialized */
+ /* currently it is called the 2nd time when changelog db is */
+ /* dynamically created. Code below should only be called once */
+ if (!initialized)
+ {
+ ldbm_compute_init();
+
+ initialized = 1;
+ }
+
+ LDAPDebug( LDAP_DEBUG_TRACE, "ldbm backend done starting\n", 0, 0, 0 );
+
+ return( 0 );
+
+}
diff --git a/ldap/servers/slapd/back-ldbm/tools/index_dump/Makefile b/ldap/servers/slapd/back-ldbm/tools/index_dump/Makefile
new file mode 100644
index 00000000..7b41ae90
--- /dev/null
+++ b/ldap/servers/slapd/back-ldbm/tools/index_dump/Makefile
@@ -0,0 +1,40 @@
+#
+# BEGIN COPYRIGHT BLOCK
+# Copyright 2001 Sun Microsystems, Inc.
+# Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+# All rights reserved.
+# END COPYRIGHT BLOCK
+#
+#
+# GNU Makefile for Directory Server libback-ldbm
+#
+
+LDAP_SRC = ../../../../..
+MCOM_ROOT = ../../../../../../..
+
+OBJDEST = $(OBJDIR)/lib/libback-ldbm
+LIBDIR = $(LDAP_LIBDIR)
+
+include $(MCOM_ROOT)/netsite/nsdefs.mk
+include $(MCOM_ROOT)/netsite/nsconfig.mk
+include $(LDAP_SRC)/nsldap.mk
+include $(MCOM_ROOT)/netsite/ns_usedb.mk
+
+INCLUDES += -I$(LDAP_SRC)/servers/slapd
+
+INDEX_DUMP_OBJS= index_dump.o
+
+OBJS = $(addprefix $(OBJDEST)/, $(INDEX_DUMP_OBJS))
+
+all: $(OBJDEST) $(LIBDIR) $(SLIBBACK_LDBM) $(LIBBACK_LDBM)
+
+veryclean: clean
+
+clean:
+
+$(OBJDEST):
+ $(MKDIR) $(OBJDEST)
+
+$(BINDIR):
+ $(MKDIR) $(LIBDIR)
+
diff --git a/ldap/servers/slapd/back-ldbm/tools/index_dump/index_dump.c b/ldap/servers/slapd/back-ldbm/tools/index_dump/index_dump.c
new file mode 100644
index 00000000..66998e49
--- /dev/null
+++ b/ldap/servers/slapd/back-ldbm/tools/index_dump/index_dump.c
@@ -0,0 +1,206 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+
+void configure __P((char *));
+DB_ENV *db_init __P((char *));
+void pheader __P((DB *, int));
+void usage __P((void));
+
+const char
+ *progname = "db_dump"; /* Program name. */
+
+int
+main(argc, argv)
+ int argc;
+ char *argv[];
+{
+ extern char *optarg;
+ extern int optind;
+ DB *dbp;
+ DBC *dbcp;
+ DBT key, data;
+ DB_ENV *dbenv;
+ int ch, checkprint, dflag;
+ char *home;
+
+ home = NULL;
+ checkprint = dflag = 0;
+ while ((ch = getopt(argc, argv, "df:h:p")) != EOF)
+ switch (ch) {
+ case 'd':
+ dflag = 1;
+ break;
+ case 'f':
+ if (freopen(optarg, "w", stdout) == NULL)
+ err(1, "%s", optarg);
+ break;
+ case 'h':
+ home = optarg;
+ break;
+ case 'p':
+ checkprint = 1;
+ break;
+ case '?':
+ default:
+ usage();
+ }
+ argc -= optind;
+ argv += optind;
+
+ if (argc != 1)
+ usage();
+
+ if (dflag) {
+ if (home != NULL)
+ errx(1,
+ "the -d and -h options may not both be specified");
+ if (checkprint)
+ errx(1,
+ "the -d and -p options may not both be specified");
+ }
+ /* Initialize the environment. */
+ dbenv = dflag ? NULL : db_init(home);
+
+ /* Open the DB file. */
+ if ((errno =
+ db_open(argv[0], DB_UNKNOWN, DB_RDONLY, 0, dbenv, NULL, &dbp)) != 0)
+ err(1, "%s", argv[0]);
+
+ /* DB dump. */
+ if (dflag) {
+ (void)__db_dump(dbp, NULL, 1);
+ if ((errno = dbp->close(dbp, 0)) != 0)
+ err(1, "close");
+ exit (0);
+ }
+
+ /* Get a cursor and step through the database. */
+ if ((errno = dbp->cursor(dbp, NULL, &dbcp)) != 0) {
+ (void)dbp->close(dbp, 0);
+ err(1, "cursor");
+ }
+
+ /* Print out the header. */
+ pheader(dbp, checkprint);
+
+ /* Print out the key/data pairs. */
+ memset(&key, 0, sizeof(key));
+ memset(&data, 0, sizeof(data));
+ while ((errno = dbcp->c_get(dbcp, &key, &data, DB_NEXT)) == 0) {
+ if (dbp->type != DB_RECNO &&
+ (errno = __db_prdbt(&key, checkprint, stdout)) != 0)
+ break;
+ if ((errno = __db_prdbt(&data, checkprint, stdout)) != 0)
+ break;
+ }
+
+ if (errno != DB_NOTFOUND)
+ err(1, "cursor get");
+
+ if ((errno = dbp->close(dbp, 0)) != 0)
+ err(1, "close");
+ return (0);
+}
+
+/*
+ * db_init --
+ * Initialize the environment.
+ */
+DB_ENV *
+db_init(home)
+ char *home;
+{
+ DB_ENV *dbenv;
+
+ if ((dbenv = (DB_ENV *)calloc(sizeof(DB_ENV), 1)) == NULL) {
+ errno = ENOMEM;
+ err(1, NULL);
+ }
+ dbenv->db_errfile = stderr;
+ dbenv->db_errpfx = progname;
+
+ if ((errno =
+ db_appinit(home, NULL, dbenv, DB_CREATE | DB_USE_ENVIRON)) != 0)
+ err(1, "db_appinit");
+ return (dbenv);
+}
+
+/*
+ * pheader --
+ * Write out the header information.
+ */
+void
+pheader(dbp, pflag)
+ DB *dbp;
+ int pflag;
+{
+ DB_BTREE_STAT *btsp;
+ HTAB *hashp;
+ HASHHDR *hdr;
+ db_pgno_t pgno;
+
+ printf("format=%s\n", pflag ? "print" : "bytevalue");
+ switch (dbp->type) {
+ case DB_BTREE:
+ printf("type=btree\n");
+ if ((errno = dbp->stat(dbp, &btsp, NULL, 0)) != 0)
+ err(1, "dbp->stat");
+ if (F_ISSET(dbp, DB_BT_RECNUM))
+ printf("recnum=1\n");
+ if (btsp->bt_maxkey != 0)
+ printf("bt_maxkey=%lu\n", (u_long)btsp->bt_maxkey);
+ if (btsp->bt_minkey != 0)
+ printf("bt_minkey=%lu\n", (u_long)btsp->bt_minkey);
+ break;
+ case DB_HASH:
+ printf("type=hash\n");
+ hashp = dbp->internal;
+ pgno = PGNO_METADATA;
+ if (memp_fget(dbp->mpf, &pgno, 0, &hdr) == 0) {
+ if (hdr->ffactor != 0)
+ printf("h_ffactor=%lu\n", (u_long)hdr->ffactor);
+ if (hdr->nelem != 0)
+ printf("h_nelem=%lu\n", (u_long)hdr->nelem);
+ (void)memp_fput(dbp->mpf, hdr, 0);
+ }
+ break;
+ case DB_RECNO:
+ printf("type=recno\n");
+ if (F_ISSET(dbp, DB_RE_RENUMBER))
+ printf("renumber=1\n");
+ if (F_ISSET(dbp, DB_RE_FIXEDLEN))
+ printf("re_len=%lu\n", (u_long)btsp->bt_re_len);
+ if (F_ISSET(dbp, DB_RE_PAD))
+ printf("re_pad=%#x\n", btsp->bt_re_pad);
+ break;
+ case DB_UNKNOWN:
+ abort();
+ /* NOTREACHED */
+ }
+
+ if (F_ISSET(dbp, DB_AM_DUP))
+ printf("duplicates=1\n");
+
+ if (dbp->dbenv->db_lorder != 0)
+ printf("db_lorder=%lu\n", (u_long)dbp->dbenv->db_lorder);
+
+ if (!F_ISSET(dbp, DB_AM_PGDEF))
+ printf("db_pagesize=%lu\n", (u_long)dbp->pgsize);
+
+ printf("HEADER=END\n");
+}
+
+/*
+ * usage --
+ * Display the usage message.
+ */
+void
+usage()
+{
+ (void)fprintf(stderr,
+ "usage: db_dump [-dp] [-f file] [-h home] db_file\n");
+ exit(1);
+}
diff --git a/ldap/servers/slapd/back-ldbm/uniqueid2entry.c b/ldap/servers/slapd/back-ldbm/uniqueid2entry.c
new file mode 100644
index 00000000..31f53898
--- /dev/null
+++ b/ldap/servers/slapd/back-ldbm/uniqueid2entry.c
@@ -0,0 +1,74 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+/* uniqueid2entry.c - given a dn return an entry */
+
+#include "back-ldbm.h"
+
+/*
+ * uniqueid2entry - look up uniqueid in the cache/indexes and return the
+ * corresponding entry.
+ */
+
+struct backentry *
+uniqueid2entry(
+ backend *be,
+ const char *uniqueid,
+ back_txn *txn,
+ int *err
+)
+{
+#ifdef UUIDCACHE_ON
+ ldbm_instance *inst = (ldbm_instance *) be->be_instance_info;
+#endif
+ struct berval idv;
+ IDList *idl = NULL;
+ struct backentry *e = NULL;
+
+ LDAPDebug( LDAP_DEBUG_TRACE, "=> uniqueid2entry \"%s\"\n", uniqueid,
+ 0, 0 );
+#ifdef UUIDCACHE_ON
+ e = cache_find_uuid(&inst->inst_cache, uniqueid);
+#endif
+ if (e == NULL) {
+ /* convert dn to entry id */
+ *err = 0;
+ idv.bv_val = (void*)uniqueid;
+ idv.bv_len = strlen( idv.bv_val );
+
+ if ( (idl = index_read( be, SLAPI_ATTR_UNIQUEID, indextype_EQUALITY, &idv, txn,
+ err )) == NULL ) {
+ if ( *err != 0 && *err != DB_NOTFOUND ) {
+ goto ext;
+ }
+ } else {
+ /* convert entry id to entry */
+ if ( (e = id2entry( be, idl_firstid( idl ), txn, err ))
+ != NULL ) {
+ goto ext;
+ } else {
+ if ( *err != 0 && *err != DB_NOTFOUND ) {
+ goto ext;
+ }
+ /*
+ * this is pretty bad anyway. the dn was in the
+ * SLAPI_ATTR_UNIQUEID index, but we could not
+ * read the entry from the id2entry index.
+ * what should we do?
+ */
+ }
+ }
+ } else {
+ goto ext;
+ }
+
+ext:
+ if (NULL != idl) {
+ slapi_ch_free( (void**)&idl);
+ }
+
+ LDAPDebug( LDAP_DEBUG_TRACE, "<= uniqueid2entry %p\n", e, 0, 0 );
+ return( e );
+}
diff --git a/ldap/servers/slapd/back-ldbm/upgrade.c b/ldap/servers/slapd/back-ldbm/upgrade.c
new file mode 100644
index 00000000..1c0bfb9d
--- /dev/null
+++ b/ldap/servers/slapd/back-ldbm/upgrade.c
@@ -0,0 +1,352 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+
+/* upgrade.c --- upgrade from a previous version of the database */
+
+#include "back-ldbm.h"
+
+#if 0
+static char* filename = "upgrade.c";
+#endif
+/*
+ * ldbm_compat_versions holds DBVERSION strings for all versions of the
+ * database with which we are (upwards) compatible. If check_db_version
+ * encounters a database with a version that is not listed in this array,
+ * we display a warning message.
+ */
+
+db_upgrade_info ldbm_version_suss[] = {
+#if defined(USE_NEW_IDL)
+ {LDBM_VERSION,DBVERSION_NEW_IDL,DBVERSION_NO_UPGRADE},
+ {LDBM_VERSION_OLD,DBVERSION_OLD_IDL,DBVERSION_NO_UPGRADE},
+#else
+ /* default: old idl (DS6.2) */
+ {LDBM_VERSION_NEW,DBVERSION_NEW_IDL,DBVERSION_NO_UPGRADE},
+ {LDBM_VERSION,DBVERSION_OLD_IDL,DBVERSION_NO_UPGRADE},
+#endif
+ {LDBM_VERSION_61,DBVERSION_NEW_IDL,DBVERSION_UPGRADE_3_4},
+ {LDBM_VERSION_60,DBVERSION_OLD_IDL,DBVERSION_UPGRADE_3_4},
+ {NULL,0,0}
+};
+
+
+/* clear the following flag to suppress "database files do not exist" warning
+int ldbm_warn_if_no_db = 0;
+*/
+/* global LDBM version in the db home */
+
+int
+lookup_dbversion(char *dbversion, int flag)
+{
+ int i, matched = 0;
+ int rval = 0;
+
+ for ( i = 0; ldbm_version_suss[i].old_version_string != NULL; ++i )
+ {
+ if ( strcmp( dbversion, ldbm_version_suss[i].old_version_string ) == 0 )
+ {
+ matched = 1;
+ break;
+ }
+ }
+ if ( matched )
+ {
+ if ( flag & DBVERSION_TYPE )
+ {
+ rval |= ldbm_version_suss[i].type;
+ }
+ if ( flag & DBVERSION_ACTION )
+ {
+ rval |= ldbm_version_suss[i].action;
+ }
+ }
+ return rval;
+}
+
+/*
+ * this function reads the db/DBVERSION file and check
+ * 1) if the db version is supported, and
+ * 2) if the db version requires some migration operation
+ *
+ * return: 0: supported
+ * DBVERSION_NOT_SUPPORTED: not supported
+ *
+ * action: 0: nothing is needed
+ * DBVERSION_UPGRADE_3_4: db3->db4 uprev is needed
+ */
+int
+check_db_version( struct ldbminfo *li, int *action )
+{
+ int value = 0;
+ char ldbmversion[BUFSIZ];
+ char dataversion[BUFSIZ];
+
+ *action = 0;
+ dbversion_read(li, li->li_directory,ldbmversion,dataversion);
+ if (0 == strlen(ldbmversion))
+ return 0;
+
+ value = lookup_dbversion( ldbmversion, DBVERSION_TYPE | DBVERSION_ACTION);
+ if ( !value )
+ {
+ LDAPDebug( LDAP_DEBUG_ANY,
+ "ERROR: Database version mismatch (expecting "
+ "'%s' but found '%s' in directory %s)\n",
+ LDBM_VERSION, ldbmversion, li->li_directory );
+ /*
+ * A non-zero return here will cause slapd to exit during startup.
+ */
+ return DBVERSION_NOT_SUPPORTED;
+ }
+ if ( value & DBVERSION_UPGRADE_3_4 )
+ {
+ dblayer_set_recovery_required(li);
+ *action = DBVERSION_UPGRADE_3_4;
+ }
+ return 0;
+}
+
+/*
+ * this function reads the db/<inst>/DBVERSION file and check
+ * 1) if the db version is supported, and
+ * 2) if the db version matches the idl configuration
+ * (nsslapd-idl-switch: new|old)
+ * note that old idl will disappear from the next major update (6.5? 7.0?)
+ *
+ * return: 0: supported and the version matched
+ * DBVERSION_NEED_IDL_OLD2NEW: old->new uprev is needed
+ * (used in convindices)
+ * DBVERSION_NEED_IDL_NEW2OLD: old db is found, for the new idl config
+ * DBVERSION_NOT_SUPPORTED: not supported
+ *
+ * DBVERSION_UPGRADE_3_4: db3->db4 uprev is needed
+ */
+int
+check_db_inst_version( ldbm_instance *inst )
+{
+ int value = 0;
+ char ldbmversion[BUFSIZ];
+ char dataversion[BUFSIZ];
+ int rval = 0;
+ char inst_dir[MAXPATHLEN*2];
+ char *inst_dirp = NULL;
+
+ inst_dirp =
+ dblayer_get_full_inst_dir(inst->inst_li, inst, inst_dir, MAXPATHLEN*2);
+
+ dbversion_read(inst->inst_li, inst_dirp,ldbmversion,dataversion);
+ if (0 == strlen(ldbmversion))
+ return rval;
+
+ value = lookup_dbversion( ldbmversion, DBVERSION_TYPE | DBVERSION_ACTION);
+ if ( !value )
+ {
+ LDAPDebug( LDAP_DEBUG_ANY,
+ "ERROR: Database version mismatch (expecting "
+ "'%s' but found '%s' in directory %s)\n",
+ LDBM_VERSION, ldbmversion, inst->inst_dir_name );
+ /*
+ * A non-zero return here will cause slapd to exit during startup.
+ */
+ return DBVERSION_NOT_SUPPORTED;
+ }
+
+ /* recognize the difference between an old/new database regarding idl
+ * (406922) */
+ if (idl_get_idl_new() && !(value & DBVERSION_NEW_IDL) )
+ {
+ rval |= DBVERSION_NEED_IDL_OLD2NEW;
+ }
+ else if (!idl_get_idl_new() && !(value & DBVERSION_OLD_IDL) )
+ {
+ rval |= DBVERSION_NEED_IDL_NEW2OLD;
+ }
+ if ( value & DBVERSION_UPGRADE_3_4 )
+ {
+ rval |= DBVERSION_UPGRADE_3_4;
+ }
+ if (inst_dirp != inst_dir)
+ slapi_ch_free_string(&inst_dirp);
+ return rval;
+}
+
+#if defined(UPGRADEDB)
+/*
+ * adjust_idl_switch
+ * if the current nsslapd-idl-switch is different from ldbmversion,
+ * update the value of nsslapd-idl-switch (in LDBM_CONFIG_ENTRY)
+ */
+int
+adjust_idl_switch(char *ldbmversion, struct ldbminfo *li)
+{
+ int rval = 0;
+
+ li->li_flags |= LI_FORCE_MOD_CONFIG;
+#if defined(USE_NEW_IDL)
+ if ((0 == strcmp(ldbmversion, LDBM_VERSION)) ||
+ (0 == strcmp(ldbmversion, LDBM_VERSION_61))) /* db: new idl */
+#else
+ if ((0 == strcmp(ldbmversion, LDBM_VERSION_NEW)) ||
+ (0 == strcmp(ldbmversion, LDBM_VERSION_61))) /* db: new idl */
+#endif
+ {
+ if (!idl_get_idl_new()) /* config: old idl */
+ {
+ replace_ldbm_config_value(CONFIG_IDL_SWITCH, "new", li);
+ LDAPDebug(LDAP_DEBUG_ANY,
+ "Warning: Dbversion %s does not meet nsslapd-idl-switch: \"old\"; "
+ "nsslapd-idl-switch is updated to \"new\"\n",
+
+ ldbmversion, 0, 0);
+ }
+ }
+#if defined(USE_NEW_IDL)
+ else if ((0 == strcmp(ldbmversion, LDBM_VERSION_OLD)) ||
+ (0 == strcmp(ldbmversion, LDBM_VERSION_60))) /* db: old */
+#else
+ else if ((0 == strcmp(ldbmversion, LDBM_VERSION)) || /* ds6.2: old */
+ (0 == strcmp(ldbmversion, LDBM_VERSION_60))) /* db: old */
+#endif
+ {
+ if (idl_get_idl_new()) /* config: new */
+ {
+ replace_ldbm_config_value(CONFIG_IDL_SWITCH, "old", li);
+ LDAPDebug(LDAP_DEBUG_ANY,
+ "Warning: Dbversion %s does not meet nsslapd-idl-switch: \"new\"; "
+ "nsslapd-idl-switch is updated to \"old\"\n",
+ ldbmversion, 0, 0);
+ }
+ }
+ else
+ {
+ LDAPDebug(LDAP_DEBUG_ANY,
+ "Warning: Dbversion %s is not supported\n",
+ ldbmversion, 0, 0);
+ rval = 1;
+ }
+
+ /* ldbminfo is a common resource; should clean up when the job is done */
+ li->li_flags &= ~LI_FORCE_MOD_CONFIG;
+ return rval;
+}
+#endif
+
+/* Do the work to upgrade a database if needed */
+/* When we're called, the database files have been opened, and any
+recovery needed has been performed. */
+int ldbm_upgrade(ldbm_instance *inst, int action)
+{
+ int rval = 0;
+
+ if (0 == action)
+ {
+ return rval;
+ }
+
+ if (action & DBVERSION_UPGRADE_3_4) /* upgrade from db3 to db4 */
+ {
+ /* basically, db4 supports db3.
+ * so, what we need to do is rename XXX.db3 to XXX.db4 */
+
+ int rval = dblayer_update_db_ext(inst, LDBM_SUFFIX_OLD, LDBM_SUFFIX);
+ if (0 == rval)
+ {
+#if defined(USE_NEW_IDL)
+ if (idl_get_idl_new())
+ {
+ LDAPDebug(LDAP_DEBUG_ANY,
+ "ldbm_upgrade: Upgrading instance %s to %s%s is successfully done.\n",
+ inst->inst_name, LDBM_VERSION_BASE, PRODUCTTEXT);
+ }
+ else
+ {
+ LDAPDebug(LDAP_DEBUG_ANY,
+ "ldbm_upgrade: Upgrading instance %s to %s%s is successfully done.\n",
+ inst->inst_name, LDBM_VERSION_OLD, 0);
+ }
+#else
+ LDAPDebug(LDAP_DEBUG_ANY,
+ "ldbm_upgrade: Upgrading instance %s to %s%s is successfully done.\n",
+ inst->inst_name, LDBM_VERSION_BASE, PRODUCTTEXT);
+#endif
+ }
+ else
+ {
+ /* recovery effort ... */
+ dblayer_update_db_ext(inst, LDBM_SUFFIX, LDBM_SUFFIX_OLD);
+ return rval;
+ }
+ }
+
+ return 0; /* Means that the database is new */
+}
+
+/* Here's the upgrade process :
+ Delete all the keys from the parentid index
+ Scan the id2entry file:
+ Remove any hassubordinates attribute present
+ Update the parentid index, maintaining a hash of high-count parents
+ Scan the newly created parentid index updating the subordinatecount attributes.
+
+ Most of the functionality is implemented in the import code.
+ */
+#if 0
+static int upgrade_db_3x_40(backend *be)
+{
+ struct ldbminfo *li = (struct ldbminfo *) be->be_database->plg_private;
+ int ret = 0;
+ back_txn txn;
+
+ static char* indexes_modified[] = {"parentid", "numsubordinates", NULL};
+
+ LDAPDebug( LDAP_DEBUG_ANY, "WARNING: Detected a database older than this server, upgrading data...\n",0,0,0);
+
+ dblayer_txn_init(li,&txn);
+ ret = dblayer_txn_begin(li,NULL,&txn);
+ if (0 != ret) {
+ ldbm_nasty(filename,69,ret);
+ goto error;
+ }
+ ret = indexfile_delete_all_keys(be,"parentid",&txn);
+ if (0 != ret) {
+ ldbm_nasty(filename,70,ret);
+ goto error;
+ }
+
+ {
+ Slapi_Mods smods;
+ slapi_mods_init(&smods,1);
+ /* Mods are to remove the hassubordinates attribute */
+ slapi_mods_add(&smods, LDAP_MOD_DELETE, "hassubordinates", 0, NULL);
+ /* This function takes care of generating the subordinatecount attribute and indexing it */
+ ret = indexfile_primary_modifyall(be,slapi_mods_get_ldapmods_byref(&smods),indexes_modified,&txn);
+ slapi_mods_done(&smods);
+ }
+
+ if (0 != ret) {
+ ldbm_nasty(filename,61,ret);
+ }
+
+error:
+ if (0 != ret ) {
+ dblayer_txn_abort(li,&txn);
+ } else {
+ ret = dblayer_txn_commit(li,&txn);
+ if (0 != ret) {
+ ldbm_nasty(filename,60,ret);
+ } else {
+ /* Now update DBVERSION file */
+ }
+ }
+ if (0 == ret) {
+ LDAPDebug( LDAP_DEBUG_ANY, "...upgrade complete.\n",0,0,0);
+ } else {
+ LDAPDebug( LDAP_DEBUG_ANY, "ERROR: Attempt to upgrade the older database FAILED.\n",0,0,0);
+ }
+ return ret;
+}
+
+#endif
diff --git a/ldap/servers/slapd/back-ldbm/vlv.c b/ldap/servers/slapd/back-ldbm/vlv.c
new file mode 100644
index 00000000..8397ef0e
--- /dev/null
+++ b/ldap/servers/slapd/back-ldbm/vlv.c
@@ -0,0 +1,1947 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+/* vlv.c */
+
+
+/*
+ * References to on-line documentation here.
+ *
+ * http://BLUES/users/dboreham/publish/Design_Documentation/RFCs/draft-ietf-asid-ldapv3-virtuallistview-01.html
+ * http://warp.mcom.com/server/directory-server/clientsdk/hammerhead/design/virtuallistview.html
+ * ftp://ftp.ietf.org/internet-drafts/draft-ietf-ldapext-ldapv3-vlv-00.txt
+ * http://rocknroll/users/merrells/publish/vlvimplementation.html
+ */
+
+
+#include "back-ldbm.h"
+#include "vlv_srch.h"
+#include "vlv_key.h"
+
+static PRUint32 vlv_trim_candidates_byindex(PRUint32 length, const struct vlv_request *vlv_request_control);
+static PRUint32 vlv_trim_candidates_byvalue(backend *be, const IDList *candidates, const sort_spec* sort_control, const struct vlv_request *vlv_request_control);
+static int vlv_build_candidate_list( backend *be, struct vlvIndex* p, const struct vlv_request *vlv_request_control, IDList** candidates, struct vlv_response *vlv_response_control);
+
+/* New mutex for vlv locking
+PRRWLock * vlvSearchList_lock=NULL;
+static struct vlvSearch *vlvSearchList= NULL;
+*/
+
+#define ISLEGACY(be) (be?(be->be_instance_info?(((ldbm_instance *)be->be_instance_info)->inst_li?(((ldbm_instance *)be->be_instance_info)->inst_li->li_legacy_errcode):0):0):0)
+
+/* Callback to add a new VLV Search specification. Added write lock.*/
+
+int vlv_AddSearchEntry(Slapi_PBlock *pb, Slapi_Entry* entryBefore, Slapi_Entry* entryAfter, int *returncode, char *returntext, void *arg)
+{
+ ldbm_instance *inst = (ldbm_instance *)arg;
+ struct vlvSearch* newVlvSearch= vlvSearch_new();
+ backend *be = inst->inst_be;
+
+ vlvSearch_init(newVlvSearch, pb, entryBefore, inst);
+ PR_RWLock_Wlock(be->vlvSearchList_lock);
+ vlvSearch_addtolist(newVlvSearch, (struct vlvSearch **)&be->vlvSearchList);
+ PR_RWLock_Unlock(be->vlvSearchList_lock);
+ return SLAPI_DSE_CALLBACK_OK;
+}
+
+/* Callback to add a new VLV Index specification. Added write lock.*/
+
+int vlv_AddIndexEntry(Slapi_PBlock *pb, Slapi_Entry* entryBefore, Slapi_Entry* entryAfter, int *returncode, char *returntext, void *arg)
+{
+ struct vlvSearch *parent;
+ backend *be= ((ldbm_instance*)arg)->inst_be;
+ Slapi_DN parentdn;
+
+ slapi_sdn_init(&parentdn);
+ slapi_sdn_get_parent(slapi_entry_get_sdn(entryBefore),&parentdn);
+ {
+ PR_RWLock_Wlock(be->vlvSearchList_lock);
+ parent= vlvSearch_finddn((struct vlvSearch *)be->vlvSearchList, &parentdn);
+ if(parent!=NULL)
+ {
+ struct vlvIndex* newVlvIndex= vlvIndex_new();
+ newVlvIndex->vlv_be=be;
+ vlvIndex_init(newVlvIndex, be, parent, entryBefore);
+ vlvSearch_addIndex(parent, newVlvIndex);
+ }
+ PR_RWLock_Unlock(be->vlvSearchList_lock);
+ }
+ slapi_sdn_done(&parentdn);
+ return SLAPI_DSE_CALLBACK_OK;
+}
+
+/* Callback to delete a VLV Index specification. Added write lock.*/
+
+int vlv_DeleteSearchEntry(Slapi_PBlock *pb, Slapi_Entry* entryBefore, Slapi_Entry* entryAfter, int *returncode, char *returntext, void *arg)
+{
+ struct vlvSearch* p=NULL;
+ backend *be= ((ldbm_instance*)arg)->inst_be;
+
+ PR_RWLock_Wlock(be->vlvSearchList_lock);
+ p = vlvSearch_finddn((struct vlvSearch *)be->vlvSearchList, slapi_entry_get_sdn(entryBefore));
+ if(p!=NULL)
+ {
+ LDAPDebug( LDAP_DEBUG_ANY, "Deleted Virtual List View Search (%s).\n", p->vlv_name, 0, 0);
+ vlvSearch_removefromlist((struct vlvSearch **)&be->vlvSearchList,p->vlv_dn);
+ vlvSearch_delete(&p);
+ }
+ PR_RWLock_Unlock(be->vlvSearchList_lock);
+ return SLAPI_DSE_CALLBACK_OK;
+}
+
+
+/* Stub Callback to delete a VLV Index specification.*/
+
+int vlv_DeleteIndexEntry(Slapi_PBlock *pb, Slapi_Entry* entryBefore, Slapi_Entry* entryAfter, int *returncode, char *returntext, void *arg)
+{
+ LDAPDebug( LDAP_DEBUG_ANY, "Deleted Virtual List View Index.\n", 0, 0, 0);
+ return SLAPI_DSE_CALLBACK_OK;
+}
+
+
+/* Callback to modify a VLV Search specification. Added read lock.*/
+
+int vlv_ModifySearchEntry(Slapi_PBlock *pb, Slapi_Entry* entryBefore, Slapi_Entry* entryAfter, int *returncode, char *returntext, void *arg)
+{
+ struct vlvSearch* p=NULL;
+ backend *be= ((ldbm_instance*)arg)->inst_be;
+
+ PR_RWLock_Rlock(be->vlvSearchList_lock);
+ p= vlvSearch_finddn((struct vlvSearch *)be->vlvSearchList, slapi_entry_get_sdn(entryBefore));
+ if(p!=NULL)
+ {
+ LDAPDebug( LDAP_DEBUG_ANY, "Modified Virtual List View Search (%s), which will be enabled when the database is rebuilt.\n", p->vlv_name, 0, 0);
+ }
+ PR_RWLock_Unlock(be->vlvSearchList_lock);
+ return SLAPI_DSE_CALLBACK_DO_NOT_APPLY;
+}
+
+
+/* Stub callback to modify a VLV Index specification. */
+
+int vlv_ModifyIndexEntry(Slapi_PBlock *pb, Slapi_Entry* entryBefore, Slapi_Entry* entryAfter, int *returncode, char *returntext, void *arg)
+{
+ LDAPDebug( LDAP_DEBUG_ANY, "Modified Virtual List View Index.\n", 0, 0, 0);
+ return SLAPI_DSE_CALLBACK_DO_NOT_APPLY;
+}
+
+
+/* Callback to rename a VLV Search specification. Added read lock.*/
+
+int vlv_ModifyRDNSearchEntry(Slapi_PBlock *pb, Slapi_Entry* entryBefore, Slapi_Entry* entryAfter, int *returncode, char *returntext, void *arg)
+{
+ struct vlvSearch* p=NULL;
+ backend *be= ((ldbm_instance*)arg)->inst_be;
+
+ PR_RWLock_Rlock(be->vlvSearchList_lock);
+ p= vlvSearch_finddn((struct vlvSearch *)be->vlvSearchList, slapi_entry_get_sdn(entryBefore));
+ if(p!=NULL)
+ {
+ LDAPDebug( LDAP_DEBUG_ANY, "Modified Virtual List View Search (%s), which will be enabled when the database is rebuilt.\n", p->vlv_name, 0, 0);
+ }
+ PR_RWLock_Unlock(be->vlvSearchList_lock);
+ return SLAPI_DSE_CALLBACK_DO_NOT_APPLY;
+}
+
+
+/* Stub callback to modify a VLV Index specification. */
+
+int vlv_ModifyRDNIndexEntry(Slapi_PBlock *pb, Slapi_Entry* entryBefore, Slapi_Entry* entryAfter, int *returncode, char *returntext, void *arg)
+{
+ LDAPDebug( LDAP_DEBUG_ANY, "Modified Virtual List View Index.\n", 0, 0, 0);
+ return SLAPI_DSE_CALLBACK_DO_NOT_APPLY;
+}
+
+/* Something may have just read a VLV Entry. */
+
+int vlv_SearchIndexEntry(Slapi_PBlock *pb, Slapi_Entry* entryBefore, Slapi_Entry* entryAfter, int *returncode, char *returntext, void *arg)
+{
+ char *name= slapi_entry_attr_get_charptr(entryBefore,type_vlvName);
+ backend *be= ((ldbm_instance*)arg)->inst_be;
+ if (name!=NULL)
+ {
+ struct vlvIndex* p= vlv_find_searchname(name, be); /* lock list */
+ slapi_ch_free((void **) &name);
+ if(p!=NULL)
+ {
+ if(vlvIndex_enabled(p))
+ {
+ slapi_entry_attr_set_charptr(entryBefore, type_vlvEnabled, "1");
+ }
+ else
+ {
+ slapi_entry_attr_set_charptr(entryBefore, type_vlvEnabled, "0");
+ }
+ slapi_entry_attr_set_ulong(entryBefore, type_vlvUses, p->vlv_uses);
+ }
+ }
+ return SLAPI_DSE_CALLBACK_OK;
+}
+
+/* Handle results of a search for objectclass "vlvIndex". Called by vlv_init at inittime -- no need to lock*/
+
+static int
+vlv_init_index_entry(Slapi_PBlock *pb, Slapi_Entry* entryBefore, Slapi_Entry* entryAfter, int *returncode, char *returntext, void *arg)
+{
+ struct vlvIndex* newVlvIndex;
+ struct vlvSearch* pSearch;
+ Slapi_Backend *be= ((ldbm_instance*)arg)->inst_be;
+ char ebuf[BUFSIZ];
+
+ if(be!=NULL)
+ {
+ Slapi_DN parentdn;
+
+ slapi_sdn_init(&parentdn);
+ newVlvIndex= vlvIndex_new();
+ slapi_sdn_get_parent(slapi_entry_get_sdn(entryBefore),&parentdn);
+ pSearch= vlvSearch_finddn((struct vlvSearch *)be->vlvSearchList, &parentdn);
+ if (pSearch == NULL) {
+ LDAPDebug( LDAP_DEBUG_ANY, "Parent doesn't exist for entry %s.\n",
+ escape_string(slapi_entry_get_dn(entryBefore), ebuf), 0, 0);
+ }
+ else {
+ vlvIndex_init(newVlvIndex, be, pSearch, entryBefore);
+ vlvSearch_addIndex(pSearch, newVlvIndex);
+ }
+ slapi_sdn_done(&parentdn);
+ }
+ return SLAPI_DSE_CALLBACK_OK;
+}
+
+/* Handle results of a search for objectclass "vlvSearch". Called by vlv_init at inittime -- no need to lock*/
+
+static int
+vlv_init_search_entry(Slapi_PBlock *pb, Slapi_Entry* entryBefore, Slapi_Entry* entryAfter, int *returncode, char *returntext, void *arg)
+{
+ struct vlvSearch* newVlvSearch= vlvSearch_new();
+ ldbm_instance *inst = (ldbm_instance*)arg;
+ backend *be= inst->inst_be;
+
+ vlvSearch_init(newVlvSearch, pb, entryBefore, inst);
+ vlvSearch_addtolist(newVlvSearch, (struct vlvSearch **)&be->vlvSearchList);
+ return SLAPI_DSE_CALLBACK_OK;
+}
+
+/* Look at a new entry, and the set of VLV searches, and see whether
+there are any which have deferred initialization and which can now
+be initialized given the new entry. Added write lock. */
+
+
+void vlv_grok_new_import_entry(const struct backentry *e, backend *be)
+{
+ struct vlvSearch* p = NULL;
+ static int seen_them_all = 0;
+ int any_not_done = 0;
+
+
+ PR_RWLock_Wlock(be->vlvSearchList_lock);
+ if (seen_them_all) {
+ PR_RWLock_Unlock(be->vlvSearchList_lock);
+ return;
+ }
+ p=(struct vlvSearch *)be->vlvSearchList;
+
+ /* Walk the list of searches */
+ for(;p!=NULL;p= p->vlv_next)
+ /* is this one not initialized ? */
+ if (0 == p->vlv_initialized) {
+ any_not_done = 1;
+ /* Is its base the entry we have here ? */
+ if (0 == slapi_sdn_compare(backentry_get_sdn(e),p->vlv_base) ) {
+ /* Then initialize it */
+ vlvSearch_reinit(p,e);
+ }
+ }
+ if (!any_not_done) {
+ seen_them_all = 1;
+ }
+ PR_RWLock_Unlock(be->vlvSearchList_lock);
+}
+
+/*
+ * Search for the VLV entries which describe the pre-computed indexes we
+ * support. Register administartion DSE callback functions.
+ * This is exported to the backend initialisation routine.
+ * 'inst' may be NULL for non-slapd initialization...
+ */
+int
+vlv_init(ldbm_instance *inst)
+{
+ /* The FE DSE *must* be initialised before we get here */
+ int return_value= LDAP_SUCCESS;
+ int scope= LDAP_SCOPE_SUBTREE;
+ char *basedn, buf[512];
+ const char *searchfilter = "(objectclass=vlvsearch)";
+ const char *indexfilter = "(objectclass=vlvindex)";
+ backend *be= inst->inst_be;
+
+ /* Initialize lock first time through */
+ if(be->vlvSearchList_lock == NULL) {
+ char *rwlockname = (char *)slapi_ch_malloc(sizeof("vlvSearchList") +
+ strlen(inst->inst_name) + 2);
+ sprintf(rwlockname, "vlvSearchList_%s", inst->inst_name);
+ be->vlvSearchList_lock = PR_NewRWLock(PR_RWLOCK_RANK_NONE, rwlockname);
+ slapi_ch_free((void**)&rwlockname);
+ }
+ if (NULL != (struct vlvSearch *)be->vlvSearchList)
+ {
+ struct vlvSearch *t = NULL;
+ struct vlvSearch *nt = NULL;
+ PR_RWLock_Wlock(be->vlvSearchList_lock);
+ for (t = (struct vlvSearch *)be->vlvSearchList; NULL != t; )
+ {
+ nt = t->vlv_next;
+ vlvSearch_delete(&t);
+ t = nt;
+ }
+ be->vlvSearchList = NULL;
+ PR_RWLock_Unlock(be->vlvSearchList_lock);
+ }
+ if (inst == NULL) {
+ basedn = NULL;
+ } else {
+ sprintf(buf, "cn=%s,cn=%s,cn=plugins,cn=config",
+ inst->inst_name, inst->inst_li->li_plugin->plg_name);
+ basedn = buf;
+ }
+
+ /* Find the VLV Search Entries */
+ {
+ Slapi_PBlock *tmp_pb;
+ slapi_config_register_callback(SLAPI_OPERATION_SEARCH,DSE_FLAG_PREOP,basedn,scope,searchfilter,vlv_init_search_entry,(void *)inst);
+ tmp_pb= slapi_search_internal(basedn, scope, searchfilter, NULL, NULL, 0);
+ slapi_config_remove_callback(SLAPI_OPERATION_SEARCH,DSE_FLAG_PREOP,basedn,scope,searchfilter,vlv_init_search_entry);
+ slapi_free_search_results_internal(tmp_pb);
+ slapi_pblock_destroy(tmp_pb);
+ }
+
+ /* Find the VLV Index Entries */
+ {
+ Slapi_PBlock *tmp_pb;
+ slapi_config_register_callback(SLAPI_OPERATION_SEARCH,DSE_FLAG_PREOP,basedn,scope,indexfilter,vlv_init_index_entry,(void*)inst);
+ tmp_pb= slapi_search_internal(basedn, scope, indexfilter, NULL, NULL, 0);
+ slapi_config_remove_callback(SLAPI_OPERATION_SEARCH,DSE_FLAG_PREOP,basedn,scope,indexfilter,vlv_init_index_entry);
+ slapi_free_search_results_internal(tmp_pb);
+ slapi_pblock_destroy(tmp_pb);
+ }
+
+ /* Only need to register these callbacks for SLAPD mode... */
+ if(basedn!=NULL)
+ {
+ slapi_config_register_callback(SLAPI_OPERATION_SEARCH,DSE_FLAG_PREOP,basedn,scope,indexfilter,vlv_SearchIndexEntry,(void*)inst);
+ slapi_config_register_callback(SLAPI_OPERATION_ADD,DSE_FLAG_PREOP,basedn,scope,searchfilter,vlv_AddSearchEntry,(void*)inst);
+ slapi_config_register_callback(SLAPI_OPERATION_ADD,DSE_FLAG_PREOP,basedn,scope,indexfilter,vlv_AddIndexEntry,(void*)inst);
+ slapi_config_register_callback(SLAPI_OPERATION_MODIFY,DSE_FLAG_PREOP,basedn,scope,searchfilter,vlv_ModifySearchEntry,(void*)inst);
+ slapi_config_register_callback(SLAPI_OPERATION_MODIFY,DSE_FLAG_PREOP,basedn,scope,indexfilter,vlv_ModifyIndexEntry,(void*)inst);
+ slapi_config_register_callback(SLAPI_OPERATION_DELETE,DSE_FLAG_PREOP,basedn,scope,searchfilter,vlv_DeleteSearchEntry,(void*)inst);
+ slapi_config_register_callback(SLAPI_OPERATION_DELETE,DSE_FLAG_PREOP,basedn,scope,indexfilter,vlv_DeleteIndexEntry,(void*)inst);
+ slapi_config_register_callback(SLAPI_OPERATION_MODRDN,DSE_FLAG_PREOP,basedn,scope,searchfilter,vlv_ModifyRDNSearchEntry,(void*)inst);
+ slapi_config_register_callback(SLAPI_OPERATION_MODRDN,DSE_FLAG_PREOP,basedn,scope,indexfilter,vlv_ModifyRDNIndexEntry,(void*)inst);
+ }
+
+ return return_value;
+}
+
+/* Removes callbacks from above when instance is removed. */
+
+int
+vlv_remove_callbacks(ldbm_instance *inst) {
+
+ int return_value= LDAP_SUCCESS;
+ int scope= LDAP_SCOPE_SUBTREE;
+ char *basedn, buf[512];
+ const char *searchfilter = "(objectclass=vlvsearch)";
+ const char *indexfilter = "(objectclass=vlvindex)";
+
+ if (inst == NULL) {
+ basedn = NULL;
+ } else {
+ sprintf(buf, "cn=%s,cn=%s,cn=plugins,cn=config",
+ inst->inst_name, inst->inst_li->li_plugin->plg_name);
+ basedn = buf;
+ }
+ if(basedn!=NULL)
+ {
+ slapi_config_remove_callback(SLAPI_OPERATION_SEARCH,DSE_FLAG_PREOP,basedn,scope,indexfilter,vlv_SearchIndexEntry);
+ slapi_config_remove_callback(SLAPI_OPERATION_ADD,DSE_FLAG_PREOP,basedn,scope,searchfilter,vlv_AddSearchEntry);
+ slapi_config_remove_callback(SLAPI_OPERATION_ADD,DSE_FLAG_PREOP,basedn,scope,indexfilter,vlv_AddIndexEntry);
+ slapi_config_remove_callback(SLAPI_OPERATION_MODIFY,DSE_FLAG_PREOP,basedn,scope,searchfilter,vlv_ModifySearchEntry);
+ slapi_config_remove_callback(SLAPI_OPERATION_MODIFY,DSE_FLAG_PREOP,basedn,scope,indexfilter,vlv_ModifyIndexEntry);
+ slapi_config_remove_callback(SLAPI_OPERATION_DELETE,DSE_FLAG_PREOP,basedn,scope,searchfilter,vlv_DeleteSearchEntry);
+ slapi_config_remove_callback(SLAPI_OPERATION_DELETE,DSE_FLAG_PREOP,basedn,scope,indexfilter,vlv_DeleteIndexEntry);
+ slapi_config_remove_callback(SLAPI_OPERATION_MODRDN,DSE_FLAG_PREOP,basedn,scope,searchfilter,vlv_ModifyRDNSearchEntry);
+ slapi_config_remove_callback(SLAPI_OPERATION_MODRDN,DSE_FLAG_PREOP,basedn,scope,indexfilter,vlv_ModifyRDNIndexEntry);
+ }
+ return return_value;
+}
+
+/* Find an enabled index which matches this description. */
+
+static struct vlvIndex*
+vlv_find_search(backend *be, const Slapi_DN *base, int scope, const char *filter, const sort_spec* sort_control)
+{
+ return vlvSearch_findenabled(be,(struct vlvSearch *)be->vlvSearchList,base,scope,filter,sort_control);
+}
+
+
+/* Find a search which matches this name. Added read lock. */
+
+struct vlvIndex*
+vlv_find_searchname(const char * name, backend *be)
+{
+ struct vlvIndex *p=NULL;
+
+ PR_RWLock_Rlock(be->vlvSearchList_lock);
+ p=vlvSearch_findname((struct vlvSearch *)be->vlvSearchList,name);
+ PR_RWLock_Unlock(be->vlvSearchList_lock);
+ return p;
+}
+
+/* Find a search which matches this indexname. Added to read lock */
+
+struct vlvIndex*
+vlv_find_indexname(const char * name, backend *be)
+{
+
+ struct vlvIndex *p=NULL;
+
+ PR_RWLock_Rlock(be->vlvSearchList_lock);
+ p=vlvSearch_findindexname((struct vlvSearch *)be->vlvSearchList,name);
+ PR_RWLock_Unlock(be->vlvSearchList_lock);
+ return p;
+}
+
+
+/* Get a list of known VLV Indexes. Added read lock */
+
+char *
+vlv_getindexnames(backend *be)
+{
+ char *n=NULL;
+
+ PR_RWLock_Rlock(be->vlvSearchList_lock);
+ n=vlvSearch_getnames((struct vlvSearch *)be->vlvSearchList);
+ PR_RWLock_Unlock(be->vlvSearchList_lock);
+ return n;
+}
+
+/* Return the list of VLV indices to the import code. Added read lock */
+
+void
+vlv_getindices(IFP callback_fn,void *param, backend *be)
+{
+ /* Traverse the list, calling the import code's callback function */
+ struct vlvSearch* ps = NULL;
+
+ PR_RWLock_Rlock(be->vlvSearchList_lock);
+ ps = (struct vlvSearch *)be->vlvSearchList;
+ for(;ps!=NULL;ps= ps->vlv_next)
+ {
+ struct vlvIndex* pi= ps->vlv_index;
+ for(;pi!=NULL;pi= pi->vlv_next)
+ {
+ callback_fn(pi->vlv_attrinfo,param);
+ }
+ }
+ PR_RWLock_Unlock(be->vlvSearchList_lock);
+}
+
+/*
+ * Create a key for the entry in the vlv index.
+ *
+ * The key is a composite of a value from each sorted attribute.
+ *
+ * If a sorted attribute has many values, then the key is built
+ * with the attribute value with the lowest value.
+ *
+ * The primary sorted attribute value is followed by a 0x00 to
+ * ensure that short attribute values appear before longer ones.
+ *
+ * Many entries may have the same attribute values, which would
+ * generate the same composite key, so we append the EntryID
+ * to ensure the uniqueness of the key.
+ *
+ * Always creates a key. Never returns NULL.
+ */
+static struct vlv_key *
+vlv_create_key(struct vlvIndex* p, struct backentry* e)
+{
+ struct berval val, *lowest_value = NULL;
+ unsigned char char_min = 0x00;
+ unsigned char char_max = 0xFF;
+ struct vlv_key *key= vlv_key_new();
+ if(p->vlv_sortkey!=NULL)
+ {
+ /* Foreach sorted attribute... */
+ int sortattr= 0;
+ while(p->vlv_sortkey[sortattr]!=NULL)
+ {
+ Slapi_Attr* attr= attrlist_find(e->ep_entry->e_attrs, p->vlv_sortkey[sortattr]->sk_attrtype);
+ {
+ /*
+ * If there's a matching rule associated with the sorted
+ * attribute then use the indexer to mangle the attr values.
+ * This ensures that the international characters will
+ * collate in the correct order.
+ */
+
+ /* xxxPINAKI */
+ /* need to free some stuff! */
+ Slapi_Value **cvalue = NULL;
+ struct berval **value = NULL;
+ int free_value= 0;
+ if (attr != NULL && !valueset_isempty(&attr->a_present_values))
+ {
+ /* Sorted attribute found. */
+ int totalattrs;
+ if (p->vlv_sortkey[sortattr]->sk_matchruleoid==NULL)
+ {
+ /* No matching rule. Syntax Plugin mangles value. */
+ Slapi_Value **va= valueset_get_valuearray(&attr->a_present_values);
+ slapi_call_syntax_values2keys_sv( p->vlv_syntax_plugin[sortattr], va, &cvalue, LDAP_FILTER_EQUALITY );
+ valuearray_get_bervalarray(cvalue,&value);
+
+ /* XXXSD need to free some more stuff */
+ {
+ int numval;
+ for (numval=0; cvalue&&cvalue[numval];numval++) {
+ slapi_value_free(&cvalue[numval]);
+ }
+ if (cvalue)
+ slapi_ch_free((void **)&cvalue);
+ }
+
+ free_value= 1;
+ }
+ else
+ {
+ /* Matching rule. Do the magic mangling. Plugin owns the memory. */
+ if(p->vlv_mrpb[sortattr]!=NULL)
+ {
+ /* xxxPINAKI */
+ struct berval **bval=NULL;
+ Slapi_Value **va= valueset_get_valuearray(&attr->a_present_values);
+ valuearray_get_bervalarray(va,&bval);
+ matchrule_values_to_keys(p->vlv_mrpb[sortattr],bval,&value);
+ }
+ }
+ for(totalattrs=0;value[totalattrs]!=NULL;totalattrs++) {}; /* Total Number of Attributes */
+ if(totalattrs==1)
+ {
+ lowest_value= value[0];
+ }
+ else
+ {
+ lowest_value = attr_value_lowest(value, slapi_berval_cmp);
+ }
+ } /* end of if (attr != NULL && ...) */
+ if(p->vlv_sortkey[sortattr]->sk_reverseorder)
+ {
+ /*
+ * This attribute is reverse sorted, so we must
+ * invert the attribute value so that the keys
+ * will be in the correct order.
+ */
+ unsigned int i;
+ char *attributeValue = NULL;
+ /* Bug 605477 : Don't malloc 0 bytes */
+ if (attr != NULL && lowest_value->bv_len != 0) {
+ attributeValue = (char*)slapi_ch_malloc(lowest_value->bv_len);
+ for(i=0;i<lowest_value->bv_len;i++)
+ {
+ attributeValue[i]= UCHAR_MAX - ((char*)lowest_value->bv_val)[i];
+ }
+ val.bv_len= lowest_value->bv_len;
+ val.bv_val= (void*)attributeValue;
+ } else {
+ /* Reverse Sort: We use an attribute value of 0x00 when
+ * there is no attribute value or attrbute is absent
+ */
+ val.bv_val= (void*)&char_min;
+ val.bv_len= 1;
+ }
+ vlv_key_addattr(key,&val);
+ slapi_ch_free((void**)&attributeValue);
+ }
+ else
+ {
+ /*
+ * This attribute is forward sorted, so add the
+ * attribute value to the end of all the keys.
+ */
+
+ /* If the forward-sorted attribute is absent or has no
+ * value, we need to use the value of 0xFF.
+ */
+ if (attr != NULL && lowest_value->bv_len > 0) {
+ vlv_key_addattr(key,lowest_value);
+ } else {
+ val.bv_val = (void*)&char_max;
+ val.bv_len = 1;
+ vlv_key_addattr(key,&val);
+ }
+ }
+ if(sortattr==0)
+ {
+ /*
+ * If this is the first attribute (the typedown attribute)
+ * then it should be followed by a zero. This is to ensure
+ * that shorter attribute values appear before longer ones.
+ */
+ char zero = 0;
+ val.bv_len= 1;
+ val.bv_val= (void*)&zero;
+ vlv_key_addattr(key,&val);
+ }
+ if(free_value)
+ {
+ ber_bvecfree(value);
+ }
+ }
+ sortattr++;
+ }
+ }
+ {
+ /* Append the EntryID to the key to ensure uniqueness */
+ val.bv_len= sizeof(e->ep_id);
+ val.bv_val= (void*)&e->ep_id;
+ vlv_key_addattr(key,&val);
+ }
+ return key;
+}
+
+/*
+ * Insert or Delete the entry to or from the index
+ */
+
+static int
+do_vlv_update_index(back_txn *txn, struct ldbminfo *li, Slapi_PBlock *pb, struct vlvIndex* pIndex, struct backentry* entry, int insert)
+{
+ backend *be;
+ int rc= 0;
+ DB *db = NULL;
+ DB_TXN *db_txn = NULL;
+ struct vlv_key *key = NULL;
+
+ slapi_pblock_get(pb, SLAPI_BACKEND, &be);
+
+ rc = dblayer_get_index_file(be, pIndex->vlv_attrinfo, &db, DBOPEN_CREATE);
+ if (rc != 0) {
+ if(rc != DB_LOCK_DEADLOCK)
+ LDAPDebug(LDAP_DEBUG_ANY, "VLV: can't get index file '%s' (err %d)\n",
+ pIndex->vlv_attrinfo->ai_type, rc, 0);
+ return rc;
+ }
+
+ key = vlv_create_key(pIndex,entry);
+ if (NULL != txn) {
+ db_txn = txn->back_txn_txn;
+ } else {
+ /* Very bad idea to do this outside of a transaction */
+ }
+
+ if (insert) {
+ DBT data = {0};
+ data.size = sizeof(entry->ep_id);
+ data.data = &entry->ep_id;
+ rc = db->put(db, db_txn, &key->key, &data, 0);
+ if (rc == 0) {
+ LDAPDebug(LDAP_DEBUG_TRACE,
+ "vlv_update_index: %s Insert %s ID=%lu\n",
+ pIndex->vlv_name, key->key.data, (u_long)entry->ep_id);
+ vlvIndex_increment_indexlength(pIndex, db, txn);
+ } else if (rc == DB_RUNRECOVERY) {
+ ldbm_nasty(pIndex->vlv_name,77,rc);
+ } else if(rc != DB_LOCK_DEADLOCK) {
+ /* jcm: This error is valid if the key already exists.
+ * Identical multi valued attr values could do this. */
+ LDAPDebug(LDAP_DEBUG_TRACE,
+ "vlv_update_index: %s Insert %s ID=%lu FAILED\n",
+ pIndex->vlv_name, key->key.data, (u_long)entry->ep_id);
+ }
+ } else {
+ LDAPDebug(LDAP_DEBUG_TRACE,
+ "vlv_update_index: %s Delete %s\n",
+ pIndex->vlv_name, key->key.data, 0);
+ rc = db->del(db, db_txn, &key->key, 0);
+ if (rc == 0) {
+ vlvIndex_decrement_indexlength(pIndex, db, txn);
+ } else if (rc == DB_RUNRECOVERY) {
+ ldbm_nasty(pIndex->vlv_name,78,rc);
+ } else if (rc != DB_LOCK_DEADLOCK) {
+ LDAPDebug(LDAP_DEBUG_TRACE,
+ "vlv_update_index: %s Delete %s FAILED\n",
+ pIndex->vlv_name, key->key.data, 0);
+ }
+ }
+
+ vlv_key_delete(&key);
+ dblayer_release_index_file(be, pIndex->vlv_attrinfo, db);
+ return rc;
+}
+
+/*
+ * Given an entry modification check if a VLV index needs to be updated.
+ */
+
+int
+vlv_update_index(struct vlvIndex* p, back_txn *txn, struct ldbminfo *li, Slapi_PBlock *pb, struct backentry* oldEntry, struct backentry* newEntry)
+{
+ int return_value=0;
+ /* Check if the old entry is in this VLV index */
+ if(oldEntry!=NULL)
+ {
+ if(slapi_sdn_scope_test(backentry_get_sdn(oldEntry),vlvIndex_getBase(p),vlvIndex_getScope(p)))
+ {
+ if(slapi_filter_test( pb, oldEntry->ep_entry, vlvIndex_getFilter(p), 0 /* No ACL Check */) == 0 )
+ {
+ /* Remove the entry from the index */
+ return_value=do_vlv_update_index(txn, li, pb, p, oldEntry, 0 /* Delete Key */);
+ }
+ }
+ }
+ /* Check if the new entry should be in the VLV index */
+ if(newEntry!=NULL)
+ {
+ if(slapi_sdn_scope_test(backentry_get_sdn(newEntry),vlvIndex_getBase(p),vlvIndex_getScope(p)))
+ {
+ if(slapi_filter_test( pb, newEntry->ep_entry, vlvIndex_getFilter(p), 0 /* No ACL Check */) == 0 )
+ {
+ /* Add the entry to the index */
+ return_value=do_vlv_update_index(txn, li, pb, p, newEntry, 1 /* Insert Key */);
+ }
+ }
+ }
+ return return_value;
+}
+
+/*
+ * Given an entry modification check if a VLV index needs to be updated.
+ *
+ * This is called for every modifying operation, so it must be very efficient.
+ *
+ * We need to know if we're adding, deleting, or modifying
+ * because we could be leaving and/or joining an index
+ *
+ * ADD: oldEntry==NULL && newEntry!=NULL
+ * DEL: oldEntry!=NULL && newEntry==NULL
+ * MOD: oldEntry!=NULL && newEntry!=NULL
+ *
+ * JCM: If only non-sorted attributes are changed, then the indexes don't need updating.
+ * JCM: Detecting this fact, given multi-valued atribibutes, might be tricky...
+ * Added write lock
+*/
+
+int
+vlv_update_all_indexes(back_txn *txn, backend *be, Slapi_PBlock *pb, struct backentry* oldEntry, struct backentry* newEntry)
+{
+ int return_value= LDAP_SUCCESS;
+ struct vlvSearch* ps=NULL;
+ struct ldbminfo *li = ((ldbm_instance *)be->be_instance_info)->inst_li;
+
+ PR_RWLock_Wlock(be->vlvSearchList_lock);
+ ps = (struct vlvSearch *)be->vlvSearchList;
+ for(;ps!=NULL;ps= ps->vlv_next)
+ {
+ struct vlvIndex* pi= ps->vlv_index;
+ for (return_value = LDAP_SUCCESS; return_value == LDAP_SUCCESS && pi!=NULL; pi=pi->vlv_next)
+ return_value=vlv_update_index(pi, txn, li, pb, oldEntry, newEntry);
+ }
+ PR_RWLock_Unlock(be->vlvSearchList_lock);
+ return return_value;
+}
+
+/*
+ * Determine the range of record numbers to return.
+ * Prevent an underrun, or overrun.
+ */
+ /* jcm: Should we make sure that start < stop */
+
+static void
+determine_result_range(const struct vlv_request *vlv_request_control, PRUint32 index, PRUint32 length, PRUint32* pstart, PRUint32 *pstop)
+{
+ if (vlv_request_control == NULL)
+ {
+ *pstart= 0;
+ if (0 == length) /* 609377: index size could be 0 */
+ {
+ *pstop= 0;
+ }
+ else
+ {
+ *pstop= length - 1;
+ }
+ }
+ else
+ {
+ /* Make sure we don't run off the start */
+ if(index < vlv_request_control->beforeCount)
+ {
+ *pstart= 0;
+ }
+ else
+ {
+ *pstart= index - vlv_request_control->beforeCount;
+ }
+ /* Make sure we don't run off the end */
+ if(ULONG_MAX - index > vlv_request_control->afterCount)
+ {
+ *pstop= index + vlv_request_control->afterCount;
+ }
+ else
+ {
+ *pstop= ULONG_MAX;
+ }
+ /* Client tried to index off the end */
+ if (0 == length) /* 609377: index size could be 0 */
+ {
+ *pstop= 0;
+ }
+ else if(*pstop > length - 1)
+ {
+ *pstop= length - 1;
+ }
+ }
+ LDAPDebug( LDAP_DEBUG_TRACE, "<= vlv_determine_result_range: Result Range %lu-%lu\n", *pstart, *pstop, 0 );
+}
+
+/*
+ * This is a utility function to pass the client
+ * supplied attribute value through the appropriate
+ * matching rule indexer.
+ *
+ * It allocates a berval vector which the caller
+ * must free.
+ */
+
+static struct berval **
+vlv_create_matching_rule_value( Slapi_PBlock* pb, struct berval *original_value)
+{
+ struct berval **value= NULL;
+ if(pb!=NULL)
+ {
+ struct berval **outvalue = NULL;
+ struct berval *invalue[2];
+ invalue[0]= original_value; /* jcm: cast away const */
+ invalue[1]= NULL;
+ /* The plugin owns the memory it returns in outvalue */
+ matchrule_values_to_keys(pb,invalue,&outvalue);
+ if(outvalue!=NULL)
+ {
+ value= slapi_ch_bvecdup(outvalue);
+ }
+ }
+ if(value==NULL)
+ {
+ struct berval *outvalue[2];
+ outvalue[0]= original_value; /* jcm: cast away const */
+ outvalue[1]= NULL;
+ value= slapi_ch_bvecdup(outvalue);
+ }
+ return value;
+}
+
+
+/*
+ * Find the record number in a VLV index for a given attribute value.
+ * The returned index is counted from zero.
+ */
+
+static PRUint32
+vlv_build_candidate_list_byvalue( struct vlvIndex* p, DBC *dbc, PRUint32 length, const struct vlv_request *vlv_request_control)
+{
+ PRUint32 si= 0; /* The Selected Index */
+ int err= 0;
+ DBT key= {0};
+ DBT data= {0};
+ /*
+ * If the primary sorted attribute has an associated
+ * matching rule, then we must mangle the typedown
+ * value.
+ */
+ struct berval **typedown_value= NULL;
+ struct berval *invalue[2];
+ invalue[0]= (struct berval *)&vlv_request_control->value; /* jcm: cast away const */
+ invalue[1]= NULL;
+ if (p->vlv_sortkey[0]->sk_matchruleoid==NULL)
+ {
+ slapi_call_syntax_values2keys(p->vlv_syntax_plugin[0],invalue,&typedown_value,LDAP_FILTER_EQUALITY); /* JCM SLOW FUNCTION */
+ }
+ else
+ {
+ typedown_value= vlv_create_matching_rule_value(p->vlv_mrpb[0],(struct berval *)&vlv_request_control->value); /* jcm: cast away const */
+ }
+ if(p->vlv_sortkey[0]->sk_reverseorder)
+ {
+ /*
+ * The primary attribute is reverse sorted, so we must
+ * invert the typedown value in order to match the key.
+ */
+ unsigned int i;
+ for(i=0;i<(*typedown_value)->bv_len;i++)
+ {
+ ((char*)(*typedown_value)->bv_val)[i]= UCHAR_MAX - ((char*)(*typedown_value)->bv_val)[i];
+ }
+ }
+
+ key.flags= DB_DBT_MALLOC;
+ key.size= typedown_value[0]->bv_len;
+ key.data= typedown_value[0]->bv_val;
+ data.flags= DB_DBT_MALLOC;
+ err= dbc->c_get(dbc,&key,&data,DB_SET_RANGE);
+ if(err==0)
+ {
+ free(data.data);
+ err= dbc->c_get(dbc,&key,&data,DB_GET_RECNO);
+ if(err==0)
+ {
+ si= *((db_recno_t*)data.data);
+ /* Records are numbered from one. */
+ si--;
+ free(data.data);
+ LDAPDebug( LDAP_DEBUG_TRACE, "<= vlv_build_candidate_list_byvalue: Found. Index=%lu\n",si,0,0);
+ }
+ else
+ {
+ /* Couldn't get the record number for the record we found. */
+ }
+ }
+ else
+ {
+ /* Couldn't find an entry which matches the value,
+ * so return the last entry
+ * (609377) when the index file is empty, there is no "last entry".
+ */
+ if (0 == length)
+ {
+ si = 0;
+ }
+ else
+ {
+ si = length - 1;
+ }
+ LDAPDebug( LDAP_DEBUG_TRACE, "<= vlv_build_candidate_list_byvalue: Not Found. Index=%lu\n",si,0,0);
+ }
+ ber_bvecfree((struct berval**)typedown_value);
+ return si;
+}
+
+static int
+vlv_idl_sort_cmp(const void *x, const void *y)
+{
+ return *(ID *)x - *(ID *)y;
+}
+
+/* build a candidate list (IDL) from a VLV index, given the starting index
+ * and the ending index (as an inclusive list).
+ * returns 0 on success, or an LDAP error code.
+ */
+int vlv_build_idl(PRUint32 start, PRUint32 stop, DB *db, DBC *dbc,
+ IDList **candidates, int dosort)
+{
+ IDList *idl = NULL;
+ int err;
+ PRUint32 recno;
+ DBT key = {0};
+ DBT data = {0};
+ ID id;
+
+ idl = idl_alloc(stop-start+1);
+ if (!idl) {
+ /* out of memory :( */
+ return LDAP_OPERATIONS_ERROR;
+ }
+ recno = start+1;
+ key.size = sizeof(recno);
+ key.data = &recno;
+ key.flags = DB_DBT_MALLOC;
+ data.ulen = sizeof(ID);
+ data.data = &id;
+ data.flags = DB_DBT_USERMEM; /* don't alloc */
+ err = dbc->c_get(dbc, &key, &data, DB_SET_RECNO);
+ while ((err == 0) && (recno <= stop+1)) {
+ if (key.data != &recno)
+ free(key.data);
+ idl_append(idl, *(ID *)data.data);
+ if (++recno <= stop+1) {
+ err = dbc->c_get(dbc, &key, &data, DB_NEXT);
+ }
+ }
+ if (err != 0) {
+ /* some db error...? */
+ LDAPDebug(LDAP_DEBUG_ANY, "vlv_build_idl: can't follow db cursor "
+ "(err %d)\n", err, 0, 0);
+ if (err == ENOMEM)
+ LDAPDebug(LDAP_DEBUG_ANY, " nomem: wants %d key, %d data\n",
+ key.size, data.size, 0);
+ return LDAP_OPERATIONS_ERROR;
+ }
+
+ /* success! */
+ if (idl) {
+ if (candidates)
+ {
+ if (dosort)
+ {
+ qsort((void *)&idl->b_ids[0], idl->b_nids,
+ (size_t)sizeof(ID), vlv_idl_sort_cmp);
+ }
+ *candidates = idl;
+ }
+ else
+ idl_free(idl); /* ??? */
+ }
+ return LDAP_SUCCESS;
+}
+
+
+/* This function does vlv_access, searching and building list all while holding read lock
+
+ 1. vlv_find_search fails, set:
+ unsigned int opnote = SLAPI_OP_NOTE_UNINDEXED;
+ slapi_pblock_set( pb, SLAPI_OPERATION_NOTES, &opnote );
+ return FIND_SEARCH FAILED
+
+ 2. vlvIndex_accessallowed fails
+ return VLV_LDBM_ACCESS_DENIED
+
+ 3. vlv_build_candidate_list fails:
+ return VLV_BLD_LIST_FAILED
+
+ 4. return LDAP_SUCCESS
+*/
+
+int
+vlv_search_build_candidate_list(Slapi_PBlock *pb, const Slapi_DN *base, int *vlv_rc, const sort_spec* sort_control,
+ const struct vlv_request *vlv_request_control,
+ IDList** candidates, struct vlv_response *vlv_response_control) {
+ struct vlvIndex* pi = NULL;
+ backend *be;
+ int scope, rc=LDAP_SUCCESS;
+ char *fstr;
+
+ slapi_pblock_get( pb, SLAPI_BACKEND, &be );
+ slapi_pblock_get( pb, SLAPI_SEARCH_SCOPE, &scope );
+ slapi_pblock_get( pb, SLAPI_SEARCH_STRFILTER, &fstr );
+ PR_RWLock_Rlock(be->vlvSearchList_lock);
+ if((pi=vlv_find_search(be, base, scope, fstr, sort_control)) == NULL) {
+ unsigned int opnote = SLAPI_OP_NOTE_UNINDEXED;
+ slapi_pblock_set( pb, SLAPI_OPERATION_NOTES, &opnote );
+ rc = VLV_FIND_SEARCH_FAILED;
+ } else if((*vlv_rc=vlvIndex_accessallowed(pi, pb)) != LDAP_SUCCESS) {
+ rc = VLV_ACCESS_DENIED;
+ } else if ((*vlv_rc=vlv_build_candidate_list(be,pi,vlv_request_control,candidates,vlv_response_control)) != LDAP_SUCCESS) {
+ rc = VLV_BLD_LIST_FAILED;
+ vlv_response_control->result=*vlv_rc;
+ }
+ PR_RWLock_Unlock(be->vlvSearchList_lock);
+ return rc;
+}
+
+/*
+ * Given the SORT and VLV controls return a candidate list from the
+ * pre-computed index file.
+ *
+ * Returns:
+ * success (0),
+ * operationsError (1),
+ * unwillingToPerform (53),
+ * timeLimitExceeded (3),
+ * adminLimitExceeded (11),
+ * indexRangeError (61),
+ * other (80)
+ */
+
+
+static int
+vlv_build_candidate_list( backend *be, struct vlvIndex* p, const struct vlv_request *vlv_request_control, IDList** candidates, struct vlv_response *vlv_response_control)
+{
+ int return_value = LDAP_SUCCESS;
+ DB *db = NULL;
+ DBC *dbc = NULL;
+ int rc, err;
+ PRUint32 si = 0; /* The Selected Index */
+ PRUint32 length;
+ int do_trim= 1;
+
+ LDAPDebug(LDAP_DEBUG_TRACE,
+ "=> vlv_build_candidate_list: %s %s Using VLV Index %s\n",
+ slapi_sdn_get_dn(vlvIndex_getBase(p)), p->vlv_search->vlv_filter,
+ vlvIndex_getName(p));
+ if (!vlvIndex_online(p)) {
+ return -1;
+ }
+ rc = dblayer_get_index_file(be, p->vlv_attrinfo, &db, 0);
+ if (rc != 0) {
+ /* shouldn't happen */
+ LDAPDebug(LDAP_DEBUG_ANY, "VLV: can't get index file '%s' (err %d)\n",
+ p->vlv_attrinfo->ai_type, rc, 0);
+ return -1;
+ }
+
+ err = db->cursor(db, 0 /* txn */, &dbc, 0);
+ if (err != 0) {
+ /* shouldn't happen */
+ LDAPDebug(LDAP_DEBUG_ANY, "VLV: couldn't get cursor (err %d)\n",
+ rc, 0, 0);
+ return -1;
+ }
+
+ length = vlvIndex_get_indexlength(p, db, 0 /* txn */);
+
+ /* Increment the usage counter */
+ vlvIndex_incrementUsage(p);
+
+ if (vlv_request_control)
+ {
+ switch(vlv_request_control->tag) {
+ case 0: /* byIndex */
+ si = vlv_trim_candidates_byindex(length, vlv_request_control);
+ break;
+ case 1: /* byValue */
+ si = vlv_build_candidate_list_byvalue(p, dbc, length,
+ vlv_request_control);
+ if (si==length) {
+ do_trim = 0;
+ *candidates = idl_alloc(0);
+ }
+ break;
+ default:
+ /* Some wierd tag value. Shouldn't ever happen */
+ if (ISLEGACY(be)) {
+ return_value = LDAP_OPERATIONS_ERROR;
+ } else {
+ return_value = LDAP_VIRTUAL_LIST_VIEW_ERROR;
+ }
+ break;
+ }
+
+ /* Tell the client what the real content count is.
+ * Client counts from 1. */
+ vlv_response_control->targetPosition = si + 1;
+ vlv_response_control->contentCount = length;
+ vlv_response_control->result = return_value;
+ }
+
+ if ((return_value == LDAP_SUCCESS) && do_trim) {
+ /* Work out the range of records to return */
+ PRUint32 start, stop;
+ determine_result_range(vlv_request_control, si, length, &start, &stop);
+
+ /* fetch the idl */
+ return_value = vlv_build_idl(start, stop, db, dbc, candidates, 0);
+ }
+ dbc->c_close(dbc);
+
+ dblayer_release_index_file( be, p->vlv_attrinfo, db );
+ return return_value;
+}
+
+/*
+ * Given a candidate list and a filter specification, filter the candidate list
+ *
+ * Returns:
+ * success (0),
+ * operationsError (1),
+ * unwillingToPerform (53),
+ * timeLimitExceeded (3),
+ * adminLimitExceeded (11),
+ * indexRangeError (61),
+ * other (80)
+ */
+int
+vlv_filter_candidates(backend *be, Slapi_PBlock *pb, const IDList *candidates, const Slapi_DN *base, int scope, Slapi_Filter *filter, IDList** filteredCandidates, int lookthrough_limit, time_t time_up)
+{
+ IDList* resultIdl= NULL;
+ int return_value = LDAP_SUCCESS;
+
+ /* Refuse to filter a non-existent IDlist */
+ if (NULL == candidates)
+ {
+ return LDAP_UNWILLING_TO_PERFORM;
+ }
+
+ LDAPDebug( LDAP_DEBUG_TRACE, "=> vlv_filter_candidates: Filtering %lu Candidates\n",(u_long)candidates->b_nids, 0, 0 );
+
+ if (0 == return_value && candidates->b_nids>0)
+ {
+ /* jcm: Could be an idlist function. create_filtered_idlist */
+ /* Iterate over the ID List applying the filter */
+ int lookedat= 0;
+ int done= 0;
+ int counter= 0;
+ ID id = NOID;
+ idl_iterator current = idl_iterator_init(candidates);
+ resultIdl= idl_alloc(candidates->b_nids);
+ do
+ {
+ id = idl_iterator_dereference_increment(&current, candidates);
+ if ( id != NOID )
+ {
+ int err= 0;
+ struct backentry *e= NULL;
+ e = id2entry( be, id, NULL, &err );
+ if ( e == NULL )
+ {
+ /*
+ * The ALLIDS ID List contains IDs for which there is no entry.
+ * This is because the entries have been deleted. An error in
+ * this case is ok.
+ */
+ if(!(ALLIDS(candidates) && err==DB_NOTFOUND))
+ {
+ LDAPDebug( LDAP_DEBUG_ANY, "vlv_filter_candidates: Candidate %lu not found err=%d\n", (u_long)id, err, 0 );
+ }
+ }
+ else
+ {
+ lookedat++;
+ if(slapi_sdn_scope_test(backentry_get_sdn(e),base,scope))
+ {
+ if ( slapi_filter_test( pb, e->ep_entry, filter, 0 /* No ACL Check */) == 0 )
+ {
+ /* The entry passed the filter test, add the id to the list */
+ LDAPDebug( LDAP_DEBUG_TRACE, "vlv_filter_candidates: Candidate %lu Passed Filter\n", (u_long)id, 0, 0 );
+ idl_append(resultIdl,id);
+ }
+ }
+ cache_return(&(((ldbm_instance *) be->be_instance_info)->inst_cache), &e);
+ }
+ }
+
+ done= slapi_op_abandoned(pb);
+
+ /* Check to see if our journey is really necessary */
+ if ( counter++ % 10 == 0 )
+ {
+ /* check time limit */
+ time_t curtime = current_time();
+ if ( time_up != -1 && curtime > time_up )
+ {
+ return_value= LDAP_TIMELIMIT_EXCEEDED;
+ done= 1;
+ }
+ /* check lookthrough limit */
+ if ( lookthrough_limit != -1 && lookedat>lookthrough_limit )
+ {
+ return_value= LDAP_ADMINLIMIT_EXCEEDED;
+ done= 1;
+ }
+ }
+ } while (!done && id!=NOID);
+ }
+ if(filteredCandidates!=NULL)
+ *filteredCandidates= resultIdl;
+ LDAPDebug( LDAP_DEBUG_TRACE, "<= vlv_filter_candidates: Filtering done\n",0, 0, 0 );
+
+ return return_value;
+}
+
+/*
+ * Given a candidate list and a virtual list view specification, trim the candidate list
+ *
+ * Returns:
+ * success (0),
+ * operationsError (1),
+ * unwillingToPerform (53),
+ * timeLimitExceeded (3),
+ * adminLimitExceeded (11),
+ * indexRangeError (61),
+ * other (80)
+ */
+int
+vlv_trim_candidates(backend *be, const IDList *candidates, const sort_spec* sort_control, const struct vlv_request *vlv_request_control, IDList** trimmedCandidates,struct vlv_response *vlv_response_control)
+{
+ IDList* resultIdl= NULL;
+ int return_value= LDAP_SUCCESS;
+ PRUint32 si= 0; /* The Selected Index */
+ int do_trim= 1;
+
+ /* Refuse to trim a non-existent IDlist */
+ if (NULL == candidates || candidates->b_nids==0)
+ {
+ return LDAP_UNWILLING_TO_PERFORM;
+ }
+
+ switch(vlv_request_control->tag)
+ {
+ case 0: /* byIndex */
+ si= vlv_trim_candidates_byindex(candidates->b_nids, vlv_request_control);
+ break;
+ case 1: /* byValue */
+ si= vlv_trim_candidates_byvalue(be, candidates, sort_control, vlv_request_control);
+ /* Don't bother sending results if the attribute value wasn't found */
+ if(si==candidates->b_nids)
+ {
+ do_trim= 0;
+ resultIdl= idl_alloc(0);
+ }
+ break;
+ default:
+ /* Some wierd tag value. Shouldn't ever happen */
+ if (ISLEGACY(be)) {
+ return_value = LDAP_OPERATIONS_ERROR;
+ } else {
+ return_value = LDAP_VIRTUAL_LIST_VIEW_ERROR;
+ }
+ break;
+ }
+
+ /* Tell the client what the real content count is. Clients count from 1 */
+ vlv_response_control->targetPosition= si + 1;
+ vlv_response_control->contentCount= candidates->b_nids;
+
+ if(return_value==LDAP_SUCCESS && do_trim)
+ {
+ /* Work out the range of records to return */
+ PRUint32 start, stop;
+ determine_result_range(vlv_request_control,si,candidates->b_nids,&start,&stop);
+ /* Build a new list containing the (start..stop) range */
+ /* JCM: Should really be a function in idlist.c to copy a range */
+ resultIdl= idl_alloc(stop-start+1);
+ {
+ PRUint32 cursor= 0;
+ for(cursor=start;cursor<=stop;cursor++)
+ {
+ LDAPDebug( LDAP_DEBUG_TRACE, "vlv_trim_candidates: Include ID %lu\n",(u_long)candidates->b_ids[cursor], 0, 0 );
+ idl_append(resultIdl,candidates->b_ids[cursor]);
+ }
+ }
+ }
+ LDAPDebug( LDAP_DEBUG_TRACE, "<= vlv_trim_candidates: Trimmed list contains %lu entries.\n",(u_long)resultIdl->b_nids, 0, 0 );
+ if(trimmedCandidates!=NULL)
+ *trimmedCandidates= resultIdl;
+ return return_value;
+}
+
+/*
+ * Work out the Selected Index given the length of the candidate list
+ * and the request control from the client.
+ *
+ * If the client sends Index==0 we behave as if I=1
+ * If the client sends Index==Size==1 we behave as if I=1, S=0
+ */
+static PRUint32
+vlv_trim_candidates_byindex(PRUint32 length, const struct vlv_request *vlv_request_control)
+{
+ PRUint32 si= 0; /* The Selected Index */
+ LDAPDebug( LDAP_DEBUG_TRACE, "=> vlv_trim_candidates_byindex: length=%lu index=%lu size=%lu\n",length, vlv_request_control->index, vlv_request_control->contentCount );
+ if(vlv_request_control->index==0)
+ {
+ /* Always select the first entry in the list */
+ si= 0;
+ }
+ else
+ {
+ if(vlv_request_control->contentCount==0)
+ {
+ /* The client has no idea what the content count might be. */
+ /* Can't scale the index, so use as is */
+ si= vlv_request_control->index;
+ if (0 == length) /* 609377: index size could be 0 */
+ {
+ if (si > 0)
+ {
+ si = length;
+ }
+ }
+ else if(si > length - 1)
+ {
+ si= length - 1;
+ }
+ }
+ else
+ {
+ if(vlv_request_control->index>=vlv_request_control->contentCount)
+ {
+ /* Always select the last entry in the list */
+ if (0 == length) /* 609377: index size could be 0 */
+ {
+ si = 0;
+ }
+ else
+ {
+ si= length-1;
+ }
+ }
+ else
+ {
+ /* The three components of this expression are (PRUint32) and may well have a value up to ULONG_MAX */
+ /* SelectedIndex = ActualContentCount * ( ClientIndex / ClientContentCount ) */
+ si= ((PRUint32)((double)length * (double)(vlv_request_control->index / (double)vlv_request_control->contentCount )));
+ }
+ }
+ }
+ LDAPDebug( LDAP_DEBUG_TRACE, "<= vlv_trim_candidates_byindex: Selected Index %lu\n",si, 0, 0 );
+ return si;
+}
+
+/*
+ * Iterate over the Candidate ID List looking for an entry >= the provided attribute value.
+ */
+static PRUint32
+vlv_trim_candidates_byvalue(backend *be, const IDList *candidates, const sort_spec* sort_control, const struct vlv_request *vlv_request_control)
+{
+ PRUint32 si= 0; /* The Selected Index */
+ PRUint32 low= 0;
+ PRUint32 high= candidates->b_nids-1;
+ PRUint32 current= 0;
+ ID id = NOID;
+ int found= 0;
+ struct berval **typedown_value;
+
+ /* For non-matchrule indexing */
+ value_compare_fn_type compare_fn= NULL;
+
+ /*
+ * If the primary sorted attribute has an associated
+ * matching rule, then we must mangle the typedown
+ * value.
+ */
+ if (sort_control->matchrule==NULL)
+ {
+ void *pi= NULL;
+ if(slapi_attr_type2plugin(sort_control->type, &pi)==0)
+ {
+ struct berval *invalue[2];
+ invalue[0]= (struct berval *)&vlv_request_control->value; /* jcm: cast away const */
+ invalue[1]= NULL;
+ slapi_call_syntax_values2keys(pi,invalue,&typedown_value,LDAP_FILTER_EQUALITY); /* JCM SLOW FUNCTION */
+ plugin_call_syntax_get_compare_fn( pi, &compare_fn );
+ if (compare_fn == NULL) {
+ LDAPDebug(LDAP_DEBUG_ANY, "vlv_trim_candidates_byvalue: "
+ "attempt to compare an unordered attribute",
+ 0, 0, 0);
+ compare_fn = slapi_berval_cmp;
+ }
+ }
+ }
+ else
+ {
+ typedown_value= vlv_create_matching_rule_value(sort_control->mr_pb,(struct berval *)&vlv_request_control->value);
+ compare_fn= slapi_berval_cmp;
+ }
+ /*
+ * Perform a binary search over the candidate list
+ */
+ do {
+ int err= 0;
+ struct backentry *e= NULL;
+ if(!sort_control->order)
+ {
+ current = (low + high)/2;
+ }
+ else
+ {
+ current = (1 + low + high)/2;
+ }
+ id= candidates->b_ids[current];
+ e = id2entry( be, id, NULL, &err );
+ if ( e == NULL )
+ {
+ LDAPDebug( LDAP_DEBUG_ANY, "vlv_trim_candidates_byvalue: Candidate ID %lu not found err=%d\n", (u_long)id, err, 0 );
+ }
+ else
+ {
+ /* Check if vlv_request_control->value is greater than or equal to the primary key. */
+ int match;
+ Slapi_Attr *attr;
+ if ( (NULL != compare_fn) && (slapi_entry_attr_find( e->ep_entry, sort_control->type, &attr ) == 0) )
+ {
+ /*
+ * If there's a matching rule associated with the primary
+ * attribute then use the indexer to mangle the attr values.
+ */
+ Slapi_Value **csn_value = valueset_get_valuearray(&attr->a_present_values);
+ struct berval **entry_value = /* xxxPINAKI needs modification attr->a_vals */NULL;
+ if(sort_control->mr_pb!=NULL)
+ {
+ struct berval **tmp_entry_value = NULL;
+
+ valuearray_get_bervalarray(csn_value,&tmp_entry_value);
+ /* Matching rule. Do the magic mangling. Plugin owns the memory. */
+ matchrule_values_to_keys(sort_control->mr_pb,/* xxxPINAKI needs modification attr->a_vals */tmp_entry_value,&entry_value);
+ }
+ else
+ {
+ valuearray_get_bervalarray(csn_value,&entry_value);
+ }
+ if(!sort_control->order)
+ {
+ match= sort_attr_compare(entry_value, (struct berval**)typedown_value, compare_fn);
+ }
+ else
+ {
+ match= sort_attr_compare((struct berval**)typedown_value, entry_value, compare_fn);
+ }
+ }
+ else
+ {
+ /*
+ * This attribute doesn't exist on this entry.
+ */
+ if(sort_control->order)
+ {
+ match= 1;
+ }
+ else
+ {
+ match= 0;
+ }
+ }
+ if(!sort_control->order)
+ {
+ if (match>=0)
+ {
+ high= current;
+ }
+ else
+ {
+ low= current+1;
+ }
+ }
+ else
+ {
+ if (match>=0)
+ {
+ high= current-1;
+ }
+ else
+ {
+ low= current;
+ }
+ }
+ if (low>=high)
+ {
+ found= 1;
+ si= high;
+ if(si==candidates->b_nids && !match)
+ {
+ /* Couldn't find an entry which matches the value, so return contentCount */
+ LDAPDebug( LDAP_DEBUG_TRACE, "<= vlv_trim_candidates_byvalue: Not Found. Index %lu\n",si, 0, 0 );
+ si= candidates->b_nids;
+ }
+ else
+ {
+ LDAPDebug( LDAP_DEBUG_TRACE, "<= vlv_trim_candidates_byvalue: Found. Index %lu\n",si, 0, 0 );
+ }
+ }
+ }
+ } while (!found);
+ ber_bvecfree((struct berval**)typedown_value);
+ return si;
+}
+
+/*
+ * Encode the VLV RESPONSE control.
+ *
+ * Create a virtual list view response control,
+ * and add it to the PBlock to be returned to the client.
+ *
+ * Returns:
+ * success ( 0 )
+ * operationsError (1),
+ */
+int
+vlv_make_response_control (Slapi_PBlock *pb, const struct vlv_response* vlvp)
+{
+ BerElement *ber= NULL;
+ struct berval *bvp = NULL;
+ int rc = -1;
+
+ /*
+ 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),
+ indexRangeError (61),
+ other (80) } }
+ */
+
+ if ( ( ber = ber_alloc()) == NULL )
+ {
+ return rc;
+ }
+
+ rc = ber_printf( ber, "{iie}", vlvp->targetPosition, vlvp->contentCount, vlvp->result );
+ if ( rc != -1 )
+ {
+ rc = ber_flatten( ber, &bvp );
+ }
+
+ ber_free( ber, 1 );
+
+ if ( rc != -1 )
+ {
+ LDAPControl new_ctrl = {0};
+ new_ctrl.ldctl_oid = LDAP_CONTROL_VLVRESPONSE;
+ new_ctrl.ldctl_value = *bvp;
+ new_ctrl.ldctl_iscritical = 1;
+ rc= slapi_pblock_set( pb, SLAPI_ADD_RESCONTROL, &new_ctrl );
+ ber_bvfree(bvp);
+ }
+
+ LDAPDebug( LDAP_DEBUG_TRACE, "<= vlv_make_response_control: Index=%lu Size=%lu Result=%lu\n", vlvp->targetPosition, vlvp->contentCount, vlvp->result );
+
+ return (rc==-1?LDAP_OPERATIONS_ERROR:LDAP_SUCCESS);
+}
+
+/*
+ * Generate a logging string for the vlv request and response
+ */
+void vlv_print_access_log(Slapi_PBlock *pb,struct vlv_request* vlvi, struct vlv_response *vlvo)
+{
+#define VLV_LOG_BS (21*6 + 4 + 5) /* space for 20-digit values for all parameters + 'VLV ' + status */
+ char stack_buffer[VLV_LOG_BS];
+ char *buffer = stack_buffer;
+ char *p;
+
+ if (vlvi->value.bv_len > 20) {
+ buffer = slapi_ch_malloc(VLV_LOG_BS + vlvi->value.bv_len);
+ }
+ p = buffer;
+ p+= sprintf(p,"VLV ");
+ if (0 == vlvi->tag) {
+ /* By Index case */
+ p+= sprintf(p,"%ld:%ld:%ld:%ld",
+ vlvi->beforeCount ,
+ vlvi->afterCount ,
+ vlvi->index ,
+ vlvi->contentCount
+ );
+ } else {
+ /* By value case */
+#define VLV_LOG_SS 32
+ char stack_string[VLV_LOG_SS];
+ char *string = stack_string;
+
+ if (vlvi->value.bv_len >= VLV_LOG_SS) {
+ string = slapi_ch_malloc(vlvi->value.bv_len+1);
+ }
+ strncpy(string,vlvi->value.bv_val,vlvi->value.bv_len);
+ string[vlvi->value.bv_len] = '\0';
+ p += sprintf(p,"%ld:%ld:%s",
+ vlvi->beforeCount ,
+ vlvi->afterCount ,
+ string
+ );
+ if (string != stack_string) {
+ slapi_ch_free( (void**)&string);
+ }
+ }
+ /* Now the response info */
+ p += sprintf(p," %ld:%ld (%ld)",
+ vlvo->targetPosition ,
+ vlvo->contentCount,
+ vlvo->result
+ );
+
+
+ ldbm_log_access_message(pb,buffer);
+
+ if (buffer != stack_buffer) {
+ slapi_ch_free( (void**)&buffer);
+ }
+}
+
+/*
+ * Decode the VLV REQUEST control.
+ *
+ * If the client sends Index==0 we behave as if I=1
+ *
+ * Returns:
+ * success (0),
+ * operationsError (1),
+ *
+ */
+int
+vlv_parse_request_control( backend *be, struct berval *vlv_spec_ber,struct vlv_request* vlvp)
+{
+ /* This control looks like this :
+
+ VirtualListViewRequest ::= SEQUENCE {
+ beforeCount INTEGER (0 .. maxInt),
+ afterCount INTEGER (0 .. maxInt),
+ CHOICE {
+ byIndex [0] SEQUENCE {
+ index INTEGER (0 .. maxInt),
+ contentCount INTEGER (0 .. maxInt) }
+ greaterThanOrEqual [1] assertionValue }
+ */
+ BerElement *ber = NULL;
+ int return_value = LDAP_SUCCESS;
+ PRUint32 rc= 0;
+ long long_beforeCount;
+ long long_afterCount;
+ long long_index;
+ long long_contentCount;
+
+ vlvp->value.bv_len = 0;
+ vlvp->value.bv_val = NULL;
+
+ ber = ber_init(vlv_spec_ber);
+ rc = ber_scanf(ber,"{ii",&long_beforeCount,&long_afterCount);
+ vlvp->beforeCount = long_beforeCount;
+ vlvp->afterCount = long_afterCount;
+ if (LBER_ERROR == rc)
+ {
+ return_value= LDAP_OPERATIONS_ERROR;
+ }
+ else
+ {
+ LDAPDebug( LDAP_DEBUG_TRACE, "vlv_parse_request_control: Before=%lu After=%lu\n", vlvp->beforeCount, vlvp->afterCount, 0 );
+ rc = ber_scanf(ber,"t",&vlvp->tag);
+ switch(vlvp->tag)
+ {
+ case LDAP_TAG_VLV_BY_INDEX:
+ /* byIndex */
+ vlvp->tag= 0;
+ rc = ber_scanf(ber,"{ii}}",&long_index,&long_contentCount);
+ vlvp->index = long_index;
+ vlvp->contentCount = long_contentCount;
+ if (LBER_ERROR == rc)
+ {
+ if (ISLEGACY(be)) {
+ return_value = LDAP_OPERATIONS_ERROR;
+ } else {
+ return_value = LDAP_VIRTUAL_LIST_VIEW_ERROR;
+ }
+ }
+ else
+ {
+ /* Client Counts from 1. */
+ if(vlvp->index!=0)
+ {
+ vlvp->index--;
+ }
+ LDAPDebug( LDAP_DEBUG_TRACE, "vlv_parse_request_control: Index=%lu Content=%lu\n", vlvp->index, vlvp->contentCount, 0 );
+ }
+ break;
+ case LDAP_TAG_VLV_BY_VALUE:
+ /* byValue */
+ vlvp->tag= 1;
+ rc = ber_scanf(ber,"o}",&vlvp->value);
+ if (LBER_ERROR == rc)
+ {
+ if (ISLEGACY(be)) {
+ return_value = LDAP_OPERATIONS_ERROR;
+ } else {
+ return_value = LDAP_VIRTUAL_LIST_VIEW_ERROR;
+ }
+ }
+ {
+ /* jcm: isn't there a utility fn to do this? */
+ char *p= slapi_ch_malloc(vlvp->value.bv_len+1);
+ strncpy(p,vlvp->value.bv_val,vlvp->value.bv_len);
+ p[vlvp->value.bv_len]= '\0';
+ LDAPDebug( LDAP_DEBUG_TRACE, "vlv_parse_request_control: Value=%s\n", p, 0, 0 );
+ slapi_ch_free( (void**)&p);
+ }
+ break;
+ default:
+ if (ISLEGACY(be)) {
+ return_value = LDAP_OPERATIONS_ERROR;
+ } else {
+ return_value = LDAP_VIRTUAL_LIST_VIEW_ERROR;
+ }
+ }
+ }
+
+ /* the ber encoding is no longer needed */
+ ber_free(ber,1);
+
+ return return_value;
+}
+
+/* given a slapi_filter, check if there's a vlv index that matches that
+ * filter. if so, return the IDL for that index (else return NULL).
+ * -- a vlv index will match ONLY if that vlv index is subtree-scope and
+ * has the same search base and search filter.
+ * added read lock */
+
+IDList *vlv_find_index_by_filter(struct backend *be, const char *base,
+ Slapi_Filter *f)
+{
+ struct vlvSearch *t = NULL;
+ struct vlvIndex *vi;
+ Slapi_DN base_sdn;
+ PRUint32 length;
+ int err;
+ DB *db = NULL;
+ DBC *dbc = NULL;
+ IDList *idl;
+ Slapi_Filter *vlv_f;
+
+ PR_RWLock_Rlock(be->vlvSearchList_lock);
+ slapi_sdn_init_dn_byref(&base_sdn, base);
+ for (t = (struct vlvSearch *)be->vlvSearchList; t; t = t->vlv_next) {
+ /* all vlv "filters" start with (|(xxx)(objectclass=referral)).
+ * we only care about the (xxx) part.
+ */
+ vlv_f = t->vlv_slapifilter->f_or;
+ if ((t->vlv_scope == LDAP_SCOPE_SUBTREE) &&
+ (slapi_sdn_compare(t->vlv_base, &base_sdn) == 0) &&
+ (slapi_filter_compare(vlv_f, f) == 0)) {
+ /* found match! */
+ slapi_sdn_done(&base_sdn);
+
+ /* is there an index that's ready? */
+ vi = t->vlv_index;
+ while (!vlvIndex_online(vi) && vi) {
+ vi = vi->vlv_next;
+ }
+ if (!vi) {
+ /* no match */
+ LDAPDebug(LDAP_DEBUG_TRACE, "vlv: no index online for %s\n",
+ t->vlv_filter, 0, 0);
+ PR_RWLock_Unlock(be->vlvSearchList_lock);
+ return NULL;
+ }
+
+ if (dblayer_get_index_file(be, vi->vlv_attrinfo, &db, 0) == 0) {
+ err = db->cursor(db, 0 /* txn */, &dbc, 0);
+ if (err == 0) {
+ length = vlvIndex_get_indexlength(vi, db, 0 /* txn */);
+ if (length == 0) /* 609377: index size could be 0 */
+ {
+ LDAPDebug(LDAP_DEBUG_TRACE, "vlv: index %s is empty\n",
+ t->vlv_filter, 0, 0);
+ idl = NULL;
+ }
+ else
+ {
+ err = vlv_build_idl(0, length-1, db, dbc, &idl, 1 /* dosort */);
+ }
+ dbc->c_close(dbc);
+ }
+ dblayer_release_index_file(be, vi->vlv_attrinfo, db);
+ if (err == 0) {
+ PR_RWLock_Unlock(be->vlvSearchList_lock);
+ return idl;
+ } else {
+ LDAPDebug(LDAP_DEBUG_ANY, "vlv find index: err %d\n",
+ err, 0, 0);
+ PR_RWLock_Unlock(be->vlvSearchList_lock);
+ return NULL;
+ }
+ }
+ }
+ }
+ PR_RWLock_Unlock(be->vlvSearchList_lock);
+ /* no match */
+ slapi_sdn_done(&base_sdn);
+ return NULL;
+}
+
+
+
+/* replace c with c2 in string -- probably exists somewhere but I can't find it slapi maybe? */
+
+static void replace_char(char *name, char c, char c2)
+{
+ int x;
+
+ for (x = 0; name[x] != '\0'; x++) {
+ if (c == name[x]) {
+ name[x] = c2;
+ }
+ }
+}
+
+/* similar to what the console GUI does */
+
+char *create_vlv_search_tag(const char* dn) {
+ char *tmp2=strdup(dn);
+
+ replace_char(tmp2,',',' ');
+ replace_char(tmp2,'"','-');
+ replace_char(tmp2,'+','_');
+ return tmp2;
+}
+
+/* Builds strings from Slapi_DN similar console GUI. Uses those dns to
+ delete vlvsearch's if they match. New write lock.
+ */
+
+#define LDBM_PLUGIN_ROOT ", cn=ldbm database, cn=plugins, cn=config"
+#define TAG "cn=by MCC "
+
+int vlv_delete_search_entry(Slapi_PBlock *pb, Slapi_Entry* e, ldbm_instance *inst)
+{
+ int rc=0;
+ Slapi_PBlock *tmppb;
+ Slapi_DN *newdn;
+ struct vlvSearch* p=NULL;
+ char *buf, *buf2, *tag1, *tag2;
+ const char *dn= slapi_sdn_get_dn(&e->e_sdn);
+ backend *be= inst->inst_be;
+
+ tag1=create_vlv_search_tag(dn);
+ buf=slapi_ch_malloc(strlen("cn=MCC ")+strlen(tag1)+strlen(", cn=")+strlen(inst->inst_name)+strlen(LDBM_PLUGIN_ROOT) + 1);
+ sprintf(buf,"%s%s%s%s%s","cn=MCC ",tag1,", cn=",inst->inst_name,LDBM_PLUGIN_ROOT);
+ newdn=slapi_sdn_new_dn_byval(buf);
+ PR_RWLock_Wlock(be->vlvSearchList_lock);
+ p = vlvSearch_finddn((struct vlvSearch *)be->vlvSearchList, newdn);
+ if(p!=NULL)
+ {
+ LDAPDebug( LDAP_DEBUG_ANY, "Deleted Virtual List View Search (%s).\n", p->vlv_name, 0, 0);
+ tag2=create_vlv_search_tag(dn);
+ buf2=slapi_ch_malloc(strlen(TAG)+strlen(tag2)+strlen(buf)+2);
+ sprintf(buf2,"%s%s,%s",TAG,tag2,buf);
+ vlvSearch_removefromlist((struct vlvSearch **)&be->vlvSearchList,p->vlv_dn);
+ /* This line release lock to prevent recursive deadlock caused by slapi_internal_delete calling vlvDeleteSearchEntry */
+ PR_RWLock_Unlock(be->vlvSearchList_lock);
+ vlvSearch_delete(&p);
+ tmppb = slapi_pblock_new();
+ slapi_delete_internal_set_pb(tmppb, buf2, NULL, NULL,
+ (void *)plugin_get_default_component_id(), 0);
+ slapi_delete_internal_pb(tmppb);
+ slapi_pblock_get (tmppb, SLAPI_PLUGIN_INTOP_RESULT, &rc);
+ if(rc != LDAP_SUCCESS) {
+ LDAPDebug(LDAP_DEBUG_ANY, "vlv_delete_search_entry:can't delete dse entry '%s'\n", buf2, 0, 0);
+ }
+ pblock_done(tmppb);
+ pblock_init(tmppb);
+ slapi_delete_internal_set_pb(tmppb, buf, NULL, NULL,
+ (void *)plugin_get_default_component_id(), 0);
+ slapi_delete_internal_pb(tmppb);
+ slapi_pblock_get (tmppb, SLAPI_PLUGIN_INTOP_RESULT, &rc);
+ if(rc != LDAP_SUCCESS) {
+ LDAPDebug(LDAP_DEBUG_ANY, "vlv_delete_search_entry:can't delete dse entry '%s'\n", buf, 0, 0);
+ }
+ slapi_pblock_destroy(tmppb);
+ slapi_ch_free((void **)&tag2);
+ slapi_ch_free((void **)&buf2);
+ } else {
+ PR_RWLock_Unlock(be->vlvSearchList_lock);
+ }
+ slapi_ch_free((void **)&tag1);
+ slapi_ch_free((void **)&buf);
+ slapi_sdn_free(&newdn);
+ return rc;
+}
+
+void
+vlv_acquire_lock(backend *be)
+{
+ LDAPDebug(LDAP_DEBUG_TRACE, "vlv_acquire_lock => trying to acquire the lock\n", 0, 0, 0);
+ PR_RWLock_Wlock(be->vlvSearchList_lock);
+}
+
+void
+vlv_release_lock(backend *be)
+{
+ LDAPDebug(LDAP_DEBUG_TRACE, "vlv_release_lock => trying to release the lock\n", 0, 0, 0);
+ PR_RWLock_Unlock(be->vlvSearchList_lock);
+}
diff --git a/ldap/servers/slapd/back-ldbm/vlv_key.c b/ldap/servers/slapd/back-ldbm/vlv_key.c
new file mode 100644
index 00000000..d80aaa34
--- /dev/null
+++ b/ldap/servers/slapd/back-ldbm/vlv_key.c
@@ -0,0 +1,69 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+/* vlv_key.c */
+
+
+#include "back-ldbm.h"
+#include "vlv_key.h"
+
+/*
+ * These functions manipulate keys for the virtual list view indexes.
+ * A key consists of a string of attribute values concatinated together,
+ * plus an entry DN to ensure uniqueness.
+ */
+
+struct vlv_key *
+vlv_key_new()
+{
+ struct vlv_key *p= (struct vlv_key*)slapi_ch_malloc(sizeof(struct vlv_key));
+ p->keymem= 64;
+ memset(&p->key,0,sizeof(DBT));
+ p->key.data= slapi_ch_malloc(p->keymem);
+ p->key.size= 0;
+ return p;
+}
+
+void
+vlv_key_delete(struct vlv_key **p)
+{
+ slapi_ch_free(&((*p)->key.data));
+ slapi_ch_free((void **)p);
+}
+
+#if 0
+static void
+vlv_key_copy(const struct vlv_key *p1,struct vlv_key *p2)
+{
+ p2->keymem= p1->keymem;
+ p2->key.data= slapi_ch_realloc(p2->key.data,p2->keymem);
+ strcpy(p2->key.data, p1->key.data);
+ p2->key.size= p1->key.size;
+}
+#endif
+
+/*
+ * Add an attribute value to the end of a composite key.
+ */
+void
+vlv_key_addattr(struct vlv_key *p,struct berval *val)
+{
+ /* If there isn't room then allocate some more memory */
+ unsigned int need = p->key.size + val->bv_len;
+ if(need > p->keymem)
+ {
+ p->keymem*= 2;
+ if(need > p->keymem)
+ {
+ p->keymem= need;
+ }
+ p->key.data= slapi_ch_realloc(p->key.data,p->keymem);
+ }
+ memcpy(((char*)p->key.data)+p->key.size, val->bv_val, val->bv_len);
+ p->key.size+= val->bv_len;
+}
+
+
+
diff --git a/ldap/servers/slapd/back-ldbm/vlv_key.h b/ldap/servers/slapd/back-ldbm/vlv_key.h
new file mode 100644
index 00000000..d7436629
--- /dev/null
+++ b/ldap/servers/slapd/back-ldbm/vlv_key.h
@@ -0,0 +1,22 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+/* vlv_key.h */
+
+
+#if !defined(__VLV_KEY_H)
+#define __VLV_KEY_H
+
+struct vlv_key
+{
+ PRUint32 keymem;
+ DBT key;
+};
+
+struct vlv_key *vlv_key_new();
+void vlv_key_delete(struct vlv_key **p);
+void vlv_key_addattr(struct vlv_key *p,struct berval *val);
+
+#endif
diff --git a/ldap/servers/slapd/back-ldbm/vlv_srch.c b/ldap/servers/slapd/back-ldbm/vlv_srch.c
new file mode 100644
index 00000000..0f72c418
--- /dev/null
+++ b/ldap/servers/slapd/back-ldbm/vlv_srch.c
@@ -0,0 +1,901 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+/* vlv_srch.c */
+
+
+#include "back-ldbm.h"
+#include "vlv_srch.h"
+
+/* Attributes for vlvSearch */
+char* const type_vlvName = "cn";
+char* const type_vlvBase = "vlvBase";
+char* const type_vlvScope = "vlvScope";
+char* const type_vlvFilter = "vlvFilter";
+
+/* Attributes for vlvIndex */
+char* const type_vlvSort = "vlvSort";
+char* const type_vlvFilename = "vlvFilename";
+char* const type_vlvEnabled = "vlvEnabled";
+char* const type_vlvUses = "vlvUses";
+
+static const char *file_prefix= "vlv#"; /* '#' used to avoid collision with real attributes */
+static const char *file_suffix= LDBM_FILENAME_SUFFIX;
+
+static int vlvIndex_createfilename(struct vlvIndex* pIndex, char **ppc);
+
+static int vlvIndex_equal(const struct vlvIndex* p1, const sort_spec* sort_control);
+static void vlvIndex_checkforindex(struct vlvIndex* p, backend *be);
+
+/*
+ * Create a new vlvSearch object
+ */
+struct vlvSearch*
+vlvSearch_new()
+{
+ struct vlvSearch* p = (struct vlvSearch*)slapi_ch_calloc(1,sizeof(struct vlvSearch));
+ if(p!=NULL)
+ {
+ p->vlv_e= NULL;
+ p->vlv_dn= NULL;
+ p->vlv_name= NULL;
+ p->vlv_base= NULL;
+ p->vlv_scope= LDAP_SCOPE_BASE;
+ p->vlv_filter= NULL;
+ p->vlv_slapifilter= NULL;
+ p->vlv_index= NULL;
+ p->vlv_next= NULL;
+ }
+ return p;
+}
+
+/*
+ * Trim spaces off the end of the string
+ */
+static void
+trimspaces(char *s)
+{
+ PRUint32 i= strlen(s) - 1;
+ while(i > 0 && isascii(s[i]) && isspace(s[i]))
+ {
+ s[i]= '\0';
+ i--;
+ }
+}
+
+/*
+ * Re-Initialise a vlvSearch object
+ */
+void
+vlvSearch_reinit(struct vlvSearch* p, const struct backentry *base)
+{
+ if (p->vlv_initialized) {
+ return; /* no work to do */
+ }
+ if (LDAP_SCOPE_ONELEVEL != p->vlv_scope) {
+ /* Only kind we re-init is onelevel searches */
+ return;
+ }
+ /* Now down to work */
+ if (NULL != p->vlv_slapifilter) {
+ slapi_filter_free(p->vlv_slapifilter,1);
+ }
+ p->vlv_slapifilter= slapi_str2filter( p->vlv_filter );
+ filter_normalize(p->vlv_slapifilter);
+ /* make (&(parentid=idofbase)(|(originalfilter)(objectclass=referral))) */
+ {
+ Slapi_Filter *fid2kids= NULL;
+ Slapi_Filter *focref= NULL;
+ Slapi_Filter *fand= NULL;
+ Slapi_Filter *forr= NULL;
+ p->vlv_slapifilter= create_onelevel_filter(p->vlv_slapifilter, base, 0 /* managedsait */, &fid2kids, &focref, &fand, &forr);
+ }
+}
+
+/*
+ * Initialise a vlvSearch object
+ */
+void
+vlvSearch_init(struct vlvSearch* p, Slapi_PBlock *pb, const Slapi_Entry *e, ldbm_instance *inst)
+{
+ /* VLV specification */
+ /* Need to copy the entry here because this one is in the cache,
+ * not forever ! */
+ p->vlv_e= slapi_entry_dup( e );
+ p->vlv_dn= slapi_sdn_dup(slapi_entry_get_sdn_const(e));
+ p->vlv_name= slapi_entry_attr_get_charptr(e,type_vlvName);
+ p->vlv_base= slapi_sdn_new_dn_passin(slapi_entry_attr_get_charptr(e,type_vlvBase));
+ p->vlv_scope= slapi_entry_attr_get_int(e,type_vlvScope);
+ p->vlv_filter= slapi_entry_attr_get_charptr(e,type_vlvFilter);
+ p->vlv_initialized = 1;
+
+ /* JCM: Should perform some validation and report errors to the error log */
+ /* JCM: Add brackets around the filter if none are there... */
+ trimspaces(p->vlv_name);
+ trimspaces(p->vlv_filter);
+
+ if(strlen(p->vlv_filter)>0)
+ {
+ /* Convert the textual filter, into a Slapi_Filter structure */
+ p->vlv_slapifilter= slapi_str2filter( p->vlv_filter );
+ filter_normalize(p->vlv_slapifilter);
+ }
+
+ /* JCM: Really should convert the slapifilter into a string and use that. */
+
+ /* Convert the filter based on the scope of the search */
+ switch(p->vlv_scope)
+ {
+ case LDAP_SCOPE_BASE:
+ /* Don't need to alter the filter */
+ break;
+ case LDAP_SCOPE_ONELEVEL:
+ {
+ /*
+ * Get the base object for the search.
+ * The entry "" will never be contained in the database,
+ * so treat it as a special case.
+ */
+ struct backentry *e= NULL;
+ if ( !slapi_sdn_isempty(p->vlv_base)) {
+ Slapi_Backend *oldbe = NULL;
+ entry_address addr;
+
+ /* switch context to the target backend */
+ slapi_pblock_get(pb, SLAPI_BACKEND, &oldbe);
+ slapi_pblock_set(pb, SLAPI_BACKEND, inst->inst_be);
+ slapi_pblock_set(pb, SLAPI_PLUGIN, inst->inst_be->be_database);
+
+ addr.dn = (char*)slapi_sdn_get_ndn (p->vlv_base);
+ addr.uniqueid = NULL;
+ e = find_entry( pb, inst->inst_be, &addr, NULL );
+ /* Check to see if the entry is absent. If it is, mark this search
+ * as not initialized */
+ if (NULL == e) {
+ p->vlv_initialized = 0;
+ /* We crash on anyhow, and rely on the fact that the filter
+ * we create is bogus to prevent chaos */
+ }
+
+ /* switch context back to the DSE backend */
+ slapi_pblock_set(pb, SLAPI_BACKEND, oldbe);
+ slapi_pblock_set(pb, SLAPI_PLUGIN, oldbe->be_database);
+ }
+
+ /* make (&(parentid=idofbase)(|(originalfilter)(objectclass=referral))) */
+ {
+ Slapi_Filter *fid2kids= NULL;
+ Slapi_Filter *focref= NULL;
+ Slapi_Filter *fand= NULL;
+ Slapi_Filter *forr= NULL;
+ p->vlv_slapifilter= create_onelevel_filter(p->vlv_slapifilter, e, 0 /* managedsait */, &fid2kids, &focref, &fand, &forr);
+ /* jcm: fid2kids, focref, fand, and forr get freed when we free p->vlv_slapifilter */
+ cache_return(&inst->inst_cache,&e);
+ }
+ }
+ break;
+ case LDAP_SCOPE_SUBTREE:
+ {
+ /* make (|(originalfilter)(objectclass=referral))) */
+ /* No need for scope-filter since we apply a scope test before the filter test */
+ Slapi_Filter *focref= NULL;
+ Slapi_Filter *forr= NULL;
+ p->vlv_slapifilter= create_subtree_filter(p->vlv_slapifilter, 0 /* managedsait */, &focref, &forr);
+ /* jcm: focref and forr get freed when we free p->vlv_slapifilter */
+ }
+ break;
+ }
+}
+
+/*
+ * Destroy an existing vlvSearch object
+ */
+void
+vlvSearch_delete(struct vlvSearch** ppvs)
+{
+ if(ppvs!=NULL && *ppvs!=NULL)
+ {
+ struct vlvIndex *pi, *ni;
+ slapi_sdn_free(&((*ppvs)->vlv_dn));
+ slapi_ch_free((void**)&((*ppvs)->vlv_name));
+ slapi_sdn_free(&((*ppvs)->vlv_base));
+ slapi_ch_free((void**)&((*ppvs)->vlv_filter));
+ slapi_filter_free((*ppvs)->vlv_slapifilter,1);
+ for(pi= (*ppvs)->vlv_index;pi!=NULL;)
+ {
+ ni= pi->vlv_next;
+ if(pi->vlv_be != NULL) {
+ vlvIndex_go_offline(pi,pi->vlv_be);
+ }
+ vlvIndex_delete(&pi);
+ pi= ni;
+ }
+ slapi_ch_free((void**)ppvs);
+ *ppvs= NULL;
+ }
+}
+
+/*
+ * Add a search to a list.
+ *
+ * We add it to the end of the list because there could
+ * be other threads traversing the list at this time.
+ */
+void
+vlvSearch_addtolist(struct vlvSearch* p, struct vlvSearch** pplist)
+{
+ if(pplist!=NULL && p!=NULL)
+ {
+ p->vlv_next= NULL;
+ if(*pplist==NULL)
+ {
+ *pplist= p;
+ }
+ else
+ {
+ struct vlvSearch* last= *pplist;
+ for(;last->vlv_next!=NULL;last=last->vlv_next);
+ last->vlv_next= p;
+ }
+ }
+}
+
+
+/*
+ * Compare two VLV Searches to see if they're the same, based on their VLV Search specification.
+ */
+static struct vlvIndex *
+vlvSearch_equal(const struct vlvSearch* p1, const Slapi_DN *base, int scope, const char *filter, const sort_spec* sort_control)
+{
+ struct vlvIndex *pi= NULL;
+ int r= (slapi_sdn_compare(p1->vlv_base,base)==0);
+ if(r) r= (p1->vlv_scope==scope);
+ if(r) r= (strcasecmp(p1->vlv_filter,filter)==0);
+ if(r)
+ {
+ pi= p1->vlv_index;
+ r= 0;
+ for(;!r && pi!=NULL;)
+ {
+ r= vlvIndex_equal(pi, sort_control);
+ if(!r)
+ {
+ pi= pi->vlv_next;
+ }
+ }
+ }
+ return pi;
+}
+
+/*
+ * Find an enabled VLV Search in a list which matches the
+ * description provided in "base, scope, filter, sort_control"
+ */
+struct vlvIndex*
+vlvSearch_findenabled(backend *be,struct vlvSearch* plist, const Slapi_DN *base, int scope, const char *filter, const sort_spec* sort_control)
+{
+ struct vlvSearch *t= plist;
+ struct vlvIndex *pi= NULL;
+ for(; (t!=NULL) && (pi == NULL); t= t->vlv_next)
+ {
+ pi= vlvSearch_equal(t,base,scope,filter,sort_control);
+ if(pi!=NULL)
+ {
+ if(!vlvIndex_enabled(pi))
+ {
+ /*
+ * A VLV Spec which matched the search criteria was found.
+ * But it hasn't been enabled yet. Check to see if the
+ * index is there. But, only check once every 60 seconds.
+ */
+ time_t curtime = current_time();
+ if(curtime>pi->vlv_lastchecked+60)
+ {
+ vlvIndex_checkforindex(pi, be);
+ pi->vlv_lastchecked= current_time();
+ }
+ }
+ if(!vlvIndex_enabled(pi))
+ {
+ pi= NULL;
+ }
+ }
+ }
+ return pi;
+}
+
+/*
+ * Find a VLV Search in a list which matches the name
+ */
+struct vlvIndex*
+vlvSearch_findname(const struct vlvSearch* plist, const char *name)
+{
+ const struct vlvSearch* t= plist;
+ for(; t!=NULL ; t= t->vlv_next)
+ {
+ struct vlvIndex *pi= t->vlv_index;
+ for(;pi!=NULL;pi= pi->vlv_next)
+ {
+ if(strcasecmp(pi->vlv_name,name)==0)
+ {
+ return pi;
+ }
+ }
+ }
+ return NULL;
+}
+
+/*
+ * Find a VLV Search in a list which matches the index name
+ */
+struct vlvIndex*
+vlvSearch_findindexname(const struct vlvSearch* plist, const char *name)
+{
+ const struct vlvSearch* t= plist;
+ for(; t!=NULL ; t= t->vlv_next)
+ {
+ struct vlvIndex *pi= t->vlv_index;
+ for(;pi!=NULL;pi= pi->vlv_next)
+ {
+ if(strcasecmp(pi->vlv_attrinfo->ai_type,name)==0)
+ {
+ return pi;
+ }
+ }
+ }
+ return NULL;
+}
+
+/*
+ * Get a list of VLV Index names.
+ * The returned pointer must be freed with slapi_ch_free
+ */
+char *
+vlvSearch_getnames(const struct vlvSearch* plist)
+{
+ /* Work out how long the string will be */
+ char *text;
+ int length= 5; /* enough to hold 'none' */
+ const struct vlvSearch* t= plist;
+ for(; t!=NULL ; t= t->vlv_next)
+ {
+ struct vlvIndex *pi= t->vlv_index;
+ for(;pi!=NULL;pi= pi->vlv_next)
+ {
+ length+= strlen(pi->vlv_name) + 4;
+ }
+ }
+ /* Build a comma delimited list of Index names */
+ text= slapi_ch_malloc(length);
+ if(length==5)
+ {
+ strcpy(text,"none");
+ }
+ else
+ {
+ text[0]= '\0';
+ t= plist;
+ for(; t!=NULL ; t= t->vlv_next)
+ {
+ struct vlvIndex *pi= t->vlv_index;
+ for(;pi!=NULL;pi= pi->vlv_next)
+ {
+ sprintf(text + strlen(text),"'%s', ",pi->vlv_name);
+ }
+ }
+ }
+ return text;
+}
+
+/*
+ * Find a VLV Search in a list, based on the DN.
+ */
+struct vlvSearch*
+vlvSearch_finddn(const struct vlvSearch* plist, const Slapi_DN *dn)
+{
+ const struct vlvSearch* curr= plist;
+ for(; curr!=NULL && slapi_sdn_compare(curr->vlv_dn,dn)!=0; curr= curr->vlv_next);
+ return (struct vlvSearch*)curr;
+}
+
+/*
+ * Remove a VLV Search from a list, based on the DN.
+ */
+void
+vlvSearch_removefromlist(struct vlvSearch** pplist, const Slapi_DN *dn)
+{
+ int done= 0;
+ struct vlvSearch* prev= NULL;
+ struct vlvSearch* curr= *pplist;
+ while(curr!=NULL && !done)
+ {
+ if(slapi_sdn_compare(curr->vlv_dn,dn)==0)
+ {
+ if(curr==*pplist)
+ {
+ *pplist= curr->vlv_next;
+ }
+ else
+ {
+ prev->vlv_next= curr->vlv_next;
+ }
+ done= 1;
+ }
+ else
+ {
+ prev= curr;
+ curr= curr->vlv_next;
+ }
+ }
+}
+
+/*
+ * Access Control Check to see if the client is allowed to use this VLV Search.
+ */
+int
+vlvSearch_accessallowed(struct vlvSearch *p, Slapi_PBlock *pb)
+{
+ char *attrs[2] = { NULL, NULL};
+
+ attrs[0] = type_vlvName;
+ return (plugin_call_acl_plugin ( pb, (Slapi_Entry*)p->vlv_e, attrs, NULL,
+ SLAPI_ACL_READ, ACLPLUGIN_ACCESS_READ_ON_VLV, NULL ) );
+}
+
+const Slapi_DN *vlvSearch_getBase(struct vlvSearch* p)
+{
+ return p->vlv_base;
+}
+
+int vlvSearch_getScope(struct vlvSearch* p)
+{
+ return p->vlv_scope;
+}
+
+Slapi_Filter *vlvSearch_getFilter(struct vlvSearch* p)
+{
+ return p->vlv_slapifilter;
+}
+
+int vlvSearch_isVlvSearchEntry(Slapi_Entry *e)
+{
+ return slapi_entry_attr_hasvalue(e, "objectclass", "vlvsearch");
+}
+
+void vlvSearch_addIndex(struct vlvSearch *pSearch, struct vlvIndex *pIndex)
+{
+ pIndex->vlv_next= NULL;
+ if(pSearch->vlv_index==NULL)
+ {
+ pSearch->vlv_index= pIndex;
+ }
+ else
+ {
+ struct vlvIndex* last= pSearch->vlv_index;
+ for(;last->vlv_next!=NULL;last=last->vlv_next);
+ last->vlv_next= pIndex;
+ }
+}
+
+/* ============================================================================================== */
+
+/*
+ * Create a new vlvIndex object
+ */
+struct vlvIndex*
+vlvIndex_new()
+{
+ struct vlvIndex* p = (struct vlvIndex*)slapi_ch_calloc(1,sizeof(struct vlvIndex));
+ if(p!=NULL)
+ {
+ p->vlv_sortspec= NULL;
+ p->vlv_attrinfo= attrinfo_new();
+ p->vlv_sortkey= NULL;
+ p->vlv_filename= NULL;
+ p->vlv_mrpb= NULL;
+ p->vlv_syntax_plugin= NULL;
+ p->vlv_indexlength_lock= PR_NewLock();
+ p->vlv_indexlength_cached= 0;
+ p->vlv_indexlength= 0;
+ p->vlv_online = 1;
+ p->vlv_enabled = 0;
+ p->vlv_lastchecked= 0;
+ p->vlv_uses= 0;
+ p->vlv_search= NULL;
+ p->vlv_next= NULL;
+ }
+ return p;
+}
+
+/*
+ * Destroy an existing vlvIndex object
+ */
+void
+vlvIndex_delete(struct vlvIndex** ppvs)
+{
+ if(ppvs!=NULL && *ppvs!=NULL)
+ {
+ slapi_ch_free((void**)&((*ppvs)->vlv_sortspec));
+ {
+ int n;
+ for(n=0;(*ppvs)->vlv_sortkey[n]!=NULL;n++)
+ {
+ if((*ppvs)->vlv_mrpb[n] != NULL) {
+ destroy_matchrule_indexer((*ppvs)->vlv_mrpb[n]);
+ slapi_pblock_destroy((*ppvs)->vlv_mrpb[n]);
+ }
+ }
+ }
+ ldap_free_sort_keylist((*ppvs)->vlv_sortkey);
+ attrinfo_delete(&((*ppvs)->vlv_attrinfo));
+ slapi_ch_free((void**)&((*ppvs)->vlv_mrpb));
+ slapi_ch_free((void**)&((*ppvs)->vlv_syntax_plugin));
+ PR_DestroyLock((*ppvs)->vlv_indexlength_lock);
+ slapi_ch_free((void**)ppvs);
+ *ppvs= NULL;
+ }
+}
+
+/*
+ * Initialise a vlvSearch object
+ */
+void
+vlvIndex_init(struct vlvIndex* p, backend *be, struct vlvSearch* pSearch, const Slapi_Entry *e)
+{
+ struct ldbminfo *li = (struct ldbminfo *) be->be_database->plg_private;
+ char *filename= NULL;
+
+ if (NULL == p)
+ return;
+
+ /* JCM: Should perform some validation and report errors to the error log */
+ /* JCM: Add brackets around the filter if none are there... */
+ p->vlv_sortspec= slapi_entry_attr_get_charptr(e,type_vlvSort);
+ trimspaces(p->vlv_sortspec);
+
+ p->vlv_name= slapi_entry_attr_get_charptr(e,type_vlvName);
+ trimspaces(p->vlv_name);
+
+ p->vlv_search= pSearch;
+
+ /* Convert the textual sort specification into a keylist structure */
+ ldap_create_sort_keylist(&(p->vlv_sortkey),p->vlv_sortspec);
+ {
+ /*
+ * For each sort attribute find the appropriate syntax plugin,
+ * and if it has a matching rule, create a matching rule indexer object.
+ */
+ int n;
+ for(n=0;p->vlv_sortkey[n]!=NULL;n++);
+ p->vlv_mrpb= (Slapi_PBlock**)slapi_ch_calloc(n+1,sizeof(Slapi_PBlock*));
+ p->vlv_syntax_plugin= (void **)(Slapi_PBlock**)slapi_ch_calloc(n+1,sizeof(Slapi_PBlock*));
+ for(n=0;p->vlv_sortkey[n]!=NULL;n++)
+ {
+ slapi_attr_type2plugin( p->vlv_sortkey[n]->sk_attrtype, &p->vlv_syntax_plugin[n] );
+ if(p->vlv_sortkey[n]->sk_matchruleoid!=NULL)
+ {
+ create_matchrule_indexer(&p->vlv_mrpb[n],p->vlv_sortkey[n]->sk_matchruleoid,p->vlv_sortkey[n]->sk_attrtype);
+ }
+
+ }
+
+ }
+
+ /* Create an index filename for the search */
+ if(vlvIndex_createfilename(p,&filename))
+ {
+ p->vlv_filename= slapi_ch_malloc(strlen(file_prefix) + strlen(filename) + strlen(file_suffix) + 1);
+ sprintf(p->vlv_filename,"%s%s%s",file_prefix,filename,file_suffix);
+
+ /* Create an attrinfo structure */
+ p->vlv_attrinfo->ai_type= slapi_ch_malloc(strlen(file_prefix) + strlen(filename) + 1);
+ sprintf(p->vlv_attrinfo->ai_type,"%s%s",file_prefix,filename);
+ p->vlv_attrinfo->ai_indexmask= INDEX_VLV;
+
+ /* Check if the index file actually exists */
+ if(li!=NULL)
+ {
+ vlvIndex_checkforindex(p, be);
+ }
+ p->vlv_lastchecked= current_time();
+ }
+ slapi_ch_free((void**)&filename);
+}
+
+/*
+ * Determine how many {key,data} pairs there are in the VLV Index.
+ * We only work out the length of the index once, then we cache
+ * it and maintain it.
+ */
+PRUint32
+vlvIndex_get_indexlength(struct vlvIndex* p, DB *db, back_txn *txn)
+{
+ if (NULL == p)
+ return 0;
+
+ if(!p->vlv_indexlength_cached)
+ {
+ DBC *dbc = NULL;
+ DB_TXN *db_txn = NULL;
+ int err= 0;
+ if (NULL != txn)
+ {
+ db_txn = txn->back_txn_txn;
+ }
+ err = db->cursor(db, db_txn, &dbc, 0);
+ if(err==0)
+ {
+ DBT key= {0};
+ DBT data= {0};
+ key.flags= DB_DBT_MALLOC;
+ data.flags= DB_DBT_MALLOC;
+ err= dbc->c_get(dbc,&key,&data,DB_LAST);
+ if(err==0)
+ {
+ free(key.data); key.data= NULL;
+ free(data.data); data.data= NULL;
+ err= dbc->c_get(dbc,&key,&data,DB_GET_RECNO);
+ if(err==0)
+ {
+ PR_Lock(p->vlv_indexlength_lock);
+ p->vlv_indexlength_cached= 1;
+ p->vlv_indexlength= *((db_recno_t*)data.data);
+ PR_Unlock(p->vlv_indexlength_lock);
+ free(data.data);
+ }
+ }
+ dbc->c_close(dbc);
+ }
+ else
+ {
+ /* couldn't get cursor??? */
+ }
+ }
+ return p->vlv_indexlength;
+}
+
+/*
+ * Increment the index length count.
+ * We keep track of the index length for efficiency.
+ */
+void
+vlvIndex_increment_indexlength(struct vlvIndex* p, DB *db, back_txn *txn)
+{
+ if (NULL == p)
+ return;
+
+ if(p->vlv_indexlength_cached)
+ {
+ PR_Lock(p->vlv_indexlength_lock);
+ p->vlv_indexlength++;
+ PR_Unlock(p->vlv_indexlength_lock);
+ }
+ else
+ {
+ p->vlv_indexlength= vlvIndex_get_indexlength(p, db, txn);
+ }
+}
+
+/*
+ * Decrement the index length count.
+ * We keep track of the index length for efficiency.
+ */
+void
+vlvIndex_decrement_indexlength(struct vlvIndex* p, DB *db, back_txn *txn)
+{
+ if (NULL == p)
+ return;
+
+ if(p->vlv_indexlength_cached)
+ {
+ /* jcm: Check for underflow? */
+ PR_Lock(p->vlv_indexlength_lock);
+ p->vlv_indexlength--;
+ PR_Unlock(p->vlv_indexlength_lock);
+ }
+ else
+ {
+ p->vlv_indexlength= vlvIndex_get_indexlength(p, db, txn);
+ }
+}
+
+/*
+ * Increment the usage counter
+ */
+void
+vlvIndex_incrementUsage(struct vlvIndex* p)
+{
+ if (NULL == p)
+ return;
+ p->vlv_uses++;
+}
+
+/*
+ * Get the filename of the index.
+ */
+const char *
+vlvIndex_filename(const struct vlvIndex* p)
+{
+ if (NULL == p)
+ return NULL;
+ return p->vlv_filename;
+}
+
+/*
+ * Check if the index is available.
+ */
+int vlvIndex_enabled(const struct vlvIndex* p)
+{
+ if (NULL == p)
+ return 0;
+ return p->vlv_enabled;
+}
+
+int vlvIndex_online(const struct vlvIndex *p)
+{
+ if (NULL == p)
+ return 0;
+ return p->vlv_online;
+}
+
+void vlvIndex_go_offline(struct vlvIndex *p, backend *be)
+{
+ if (NULL == p)
+ return;
+ p->vlv_online = 0;
+ p->vlv_enabled = 0;
+ p->vlv_indexlength = 0;
+ p->vlv_attrinfo->ai_indexmask |= INDEX_OFFLINE;
+ dblayer_erase_index_file_nolock(be, p->vlv_attrinfo, 1 /* chkpt if not busy */);
+}
+
+void vlvIndex_go_online(struct vlvIndex *p, backend *be)
+{
+ if (NULL == p)
+ return;
+ p->vlv_attrinfo->ai_indexmask &= ~INDEX_OFFLINE;
+ p->vlv_online = 1;
+ vlvIndex_checkforindex(p, be);
+}
+
+
+/*
+ * Access Control Check to see if the client is allowed to use this VLV Index.
+ */
+int
+vlvIndex_accessallowed(struct vlvIndex *p, Slapi_PBlock *pb)
+{
+ if (NULL == p)
+ return 0;
+ return vlvSearch_accessallowed(p->vlv_search, pb);
+}
+
+const Slapi_DN *vlvIndex_getBase(struct vlvIndex* p)
+{
+ if (NULL == p)
+ return NULL;
+ return vlvSearch_getBase(p->vlv_search);
+}
+
+int vlvIndex_getScope(struct vlvIndex* p)
+{
+ if (NULL == p)
+ return 0;
+ return vlvSearch_getScope(p->vlv_search);
+}
+
+Slapi_Filter *vlvIndex_getFilter(struct vlvIndex* p)
+{
+ if (NULL == p)
+ return NULL;
+ return vlvSearch_getFilter(p->vlv_search);
+}
+
+const char *vlvIndex_getName(struct vlvIndex* p)
+{
+ if (NULL == p)
+ return NULL;
+ return p->vlv_name;
+}
+
+/*
+ * JCM: Could also match reverse sense of index and use in reverse.
+ */
+static int
+vlvIndex_equal(const struct vlvIndex* p1, const sort_spec* sort_control)
+{
+ int r= 1;
+ const sort_spec *t1= sort_control;
+ LDAPsortkey *t2= p1->vlv_sortkey[0];
+ int n= 1;
+ for(;t1!=NULL && t2!=NULL && r;t1= t1->next,t2=p1->vlv_sortkey[n],n++)
+ {
+ r= (t1->order && t2->sk_reverseorder) || (!t1->order && !t2->sk_reverseorder);
+ if(r) r= (strcasecmp(t1->type, t2->sk_attrtype)==0);
+ if(r)
+ {
+ if(t1->matchrule==NULL && t2->sk_matchruleoid==NULL)
+ {
+ r= 1;
+ }
+ else if(t1->matchrule!=NULL && t2->sk_matchruleoid!=NULL)
+ {
+ r= (strcasecmp(t1->matchrule, t2->sk_matchruleoid)==0);
+ }
+ else
+ {
+ r= 0;
+ }
+ }
+ }
+ if(r) r= (t1==NULL && t2==NULL);
+ return r;
+}
+
+/*
+ * Check if the index file actually exists,
+ * and set vlv_enabled appropriately
+ */
+static void
+vlvIndex_checkforindex(struct vlvIndex* p, backend *be)
+{
+ DB *db = NULL;
+
+ /* if the vlv index is offline (being generated), don't even look */
+ if (! p->vlv_online)
+ return;
+
+ if (dblayer_get_index_file(be, p->vlv_attrinfo, &db, 0) == 0) {
+ p->vlv_enabled = 1;
+ dblayer_release_index_file( be, p->vlv_attrinfo, db );
+ } else {
+ p->vlv_enabled = 0;
+ }
+}
+
+int vlvIndex_isVlvIndexEntry(Slapi_Entry *e)
+{
+ return slapi_entry_attr_hasvalue(e, "objectclass", "vlvindex");
+}
+
+/*
+ * Create the filename for the index.
+ * Extract all the alphanumeric characters from the descriptive name.
+ * Convert to all lower case.
+ */
+static int
+vlvIndex_createfilename(struct vlvIndex* pIndex, char **ppc)
+{
+ int filenameValid= 1;
+ unsigned int i;
+ char *p, *filename;
+ filename= slapi_ch_malloc(strlen(pIndex->vlv_name) + 1);
+ p= filename;
+ for(i=0;i<strlen(pIndex->vlv_name);i++)
+ {
+ if(isalnum(pIndex->vlv_name[i]))
+ {
+ *p= TOLOWER( pIndex->vlv_name[i] );
+ p++;
+ }
+ }
+ *p= '\0';
+ if(strlen(filename)==0)
+ {
+ LDAPDebug( LDAP_DEBUG_ANY, "Couldn't generate valid filename from Virtual List View Index Name (%s). Need some alphabetical characters.\n", pIndex->vlv_name, 0, 0);
+ filenameValid= 0;
+ }
+ /* JCM: Check if this file clashes with another VLV Index filename */
+ *ppc= filename;
+ return filenameValid;
+}
+
+int
+vlv_isvlv(char *filename)
+{
+ if (0 == strncmp(filename, file_prefix, 4))
+ return 1;
+ return 0;
+}
diff --git a/ldap/servers/slapd/back-ldbm/vlv_srch.h b/ldap/servers/slapd/back-ldbm/vlv_srch.h
new file mode 100644
index 00000000..c892f6b4
--- /dev/null
+++ b/ldap/servers/slapd/back-ldbm/vlv_srch.h
@@ -0,0 +1,134 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+/* vlv_srch.h */
+
+
+#if !defined(__VLV_SRCH_H)
+#define __VLV_SRCH_H
+
+extern char* const type_vlvName;
+extern char* const type_vlvBase;
+extern char* const type_vlvScope;
+extern char* const type_vlvFilter;
+extern char* const type_vlvSort;
+extern char* const type_vlvFilename;
+extern char* const type_vlvEnabled;
+extern char* const type_vlvUses;
+
+/*
+ * This structure is the internal representation of a VLV Search.
+ */
+struct vlvSearch
+{
+ /* The VLV Search Specification Entry */
+ const Slapi_Entry *vlv_e;
+
+ /* Extracted from the VLV Search Specification entry */
+ Slapi_DN *vlv_dn;
+ char *vlv_name;
+ Slapi_DN *vlv_base;
+ int vlv_scope;
+ char *vlv_filter;
+ int vlv_initialized;
+
+ /* Derived from the VLV Entry */
+ Slapi_Filter *vlv_slapifilter;
+
+ /* List of Indexes for this Search */
+ struct vlvIndex* vlv_index;
+
+ /* The next VLV Search in the list */
+ struct vlvSearch* vlv_next;
+};
+
+struct vlvIndex
+{
+ char *vlv_name;
+ char *vlv_sortspec;
+
+ /* Derived from the VLV Entry */
+ LDAPsortkey **vlv_sortkey;
+
+ /* The Index filename */
+ char *vlv_filename;
+
+ /* Attribute Structure maps filename onto index */
+ struct attrinfo *vlv_attrinfo;
+
+ /* Syntax Plugin. One for each LDAPsortkey */
+ void **vlv_syntax_plugin;
+
+ /* Matching Rule PBlock. One for each LDAPsortkey */
+ Slapi_PBlock **vlv_mrpb;
+
+ /* Keep track of the index length */
+ PRLock *vlv_indexlength_lock;
+ int vlv_indexlength_cached;
+ db_recno_t vlv_indexlength;
+
+ int vlv_enabled; /* index file is there & ready */
+ int vlv_online; /* turned off when generating index */
+
+ /* The last time we checked to see if the index file was available */
+ time_t vlv_lastchecked;
+
+ /* The number of uses this search has received since start up */
+ PRUint32 vlv_uses;
+
+ struct backend* vlv_be; /* need backend to remove the index when done */
+
+ /* The parent Search Specification for this Index */
+ struct vlvSearch* vlv_search;
+
+ /* The next VLV Index in the list */
+ struct vlvIndex* vlv_next;
+};
+
+struct vlvSearch* vlvSearch_new();
+void vlvSearch_init(struct vlvSearch*, Slapi_PBlock *pb, const Slapi_Entry *e, ldbm_instance *inst);
+void vlvSearch_reinit(struct vlvSearch* p, const struct backentry *base);
+void vlvSearch_delete(struct vlvSearch** ppvs);
+void vlvSearch_addtolist(struct vlvSearch* p, struct vlvSearch** pplist);
+struct vlvSearch* vlvSearch_find(const struct vlvSearch* plist, const char *base, int scope, const char *filter, const char *sortspec);
+struct vlvIndex* vlvSearch_findenabled(backend *be,struct vlvSearch* plist, const Slapi_DN *base, int scope, const char *filter, const sort_spec* sort_control);
+struct vlvSearch* vlvSearch_finddn(const struct vlvSearch* plist, const Slapi_DN *dn);
+struct vlvIndex* vlvSearch_findname(const struct vlvSearch* plist, const char *name);
+struct vlvIndex* vlvSearch_findindexname(const struct vlvSearch* plist, const char *name);
+char *vlvSearch_getnames(const struct vlvSearch* plist);
+void vlvSearch_removefromlist(struct vlvSearch** pplist, const Slapi_DN *dn);
+int vlvSearch_accessallowed(struct vlvSearch *p, Slapi_PBlock *pb);
+const Slapi_DN *vlvSearch_getBase(struct vlvSearch* p);
+int vlvSearch_getScope(struct vlvSearch* p);
+Slapi_Filter *vlvSearch_getFilter(struct vlvSearch* p);
+int vlvSearch_isVlvSearchEntry(Slapi_Entry *e);
+void vlvSearch_addIndex(struct vlvSearch *pSearch, struct vlvIndex *pIndex);
+
+
+struct vlvIndex* vlvIndex_new();
+void vlvIndex_init(struct vlvIndex* p, backend *be, struct vlvSearch* pSearch, const Slapi_Entry *e);
+void vlvIndex_delete(struct vlvIndex** ppvs);
+PRUint32 vlvIndex_get_indexlength(struct vlvIndex* p, DB *db, back_txn *txn);
+void vlvIndex_increment_indexlength(struct vlvIndex* p, DB *db, back_txn *txn);
+void vlvIndex_decrement_indexlength(struct vlvIndex* p, DB *db, back_txn *txn);
+void vlvIndex_incrementUsage(struct vlvIndex* p);
+const char *vlvIndex_filename(const struct vlvIndex* p);
+int vlvIndex_enabled(const struct vlvIndex* p);
+int vlvIndex_online(const struct vlvIndex *p);
+void vlvIndex_go_offline(struct vlvIndex *p, backend *be);
+void vlvIndex_go_online(struct vlvIndex *p, backend *be);
+int vlvIndex_accessallowed(struct vlvIndex *p, Slapi_PBlock *pb);
+const Slapi_DN *vlvIndex_getBase(struct vlvIndex* p);
+int vlvIndex_getScope(struct vlvIndex* p);
+Slapi_Filter *vlvIndex_getFilter(struct vlvIndex* p);
+const char *vlvIndex_getName(struct vlvIndex* p);
+int vlvIndex_isVlvIndexEntry(Slapi_Entry *e);
+
+#define VLV_ACCESS_DENIED -1
+#define VLV_BLD_LIST_FAILED -2
+#define VLV_FIND_SEARCH_FAILED -3
+
+
+#endif
diff --git a/ldap/servers/slapd/back-ldif/Makefile b/ldap/servers/slapd/back-ldif/Makefile
new file mode 100644
index 00000000..1b7037cd
--- /dev/null
+++ b/ldap/servers/slapd/back-ldif/Makefile
@@ -0,0 +1,81 @@
+#
+# BEGIN COPYRIGHT BLOCK
+# Copyright 2001 Sun Microsystems, Inc.
+# Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+# All rights reserved.
+# END COPYRIGHT BLOCK
+#
+#
+# GNU Makefile for LDAP Back-ldif backend
+#
+
+LDAP_SRC = ../../..
+MCOM_ROOT = ../../../../..
+
+NOSTDCLEAN=true # don't let nsconfig.mk define target clean
+NOSTDSTRIP=true # don't let nsconfig.mk define target strip
+NSPR20=true # probably should be defined somewhere else (not sure where)
+
+OBJDEST = $(OBJDIR)/lib/libback-ldif
+LIBDIR = $(LDAP_LIBDIR)
+SERVER_OBJDEST = $(OBJDIR)/servers/obj
+
+include $(MCOM_ROOT)/ldapserver/nsdefs.mk
+include $(MCOM_ROOT)/ldapserver/nsconfig.mk
+include $(LDAP_SRC)/nsldap.mk
+
+BACKLDIF_OBJS = close.o delete.o modrdn.o unbind.o add.o \
+ compare.o init.o search.o bind.o config.o modify.o monitor.o \
+ start.o
+
+OBJS = $(addprefix $(OBJDEST)/, $(BACKLDIF_OBJS))
+
+SERVER_OBJS= ch_malloc.o entry.o result.o modutil.o
+
+EXTRA_OBJS = $(addprefix $(SERVER_OBJDEST)/, $(SERVER_OBJS))
+
+INCLUDES += -I..
+
+ifeq ($(ARCH), WINNT)
+BACKLDIF_DLL_OBJ = $(addprefix $(OBJDEST)/, dllmain.o)
+endif
+
+LDAP_BACKLDIF= $(addprefix $(LIBDIR)/, $(LIBBACK_LDIF_DLL).$(DLL_SUFFIX))
+
+ifeq ($(ARCH), WINNT)
+EXTRA_LIBS_DEP += $(LIBSECURITY) $(LIBNSPR) \
+ $(LDAP_COMMON_LIBS_DEP) $(LDAP_SDK_LIBS_DEP) \
+ $(LIBSLAPD_DEP) $(LIBLDAPU_DEP)
+
+EXTRA_LIBS += $(LIBSECURITY) $(LIBNSPR) \
+ $(LDAP_COMMON_LIBS) $(LDAP_SDK_LIBS) \
+ $(LIBSLAPD) $(THREADSLIB) $(LIBLDAPU)
+endif
+
+ifeq ($(ARCH), AIX)
+EXTRA_LIBS += $(DLL_EXTRA_LIBS)
+endif
+
+clientSDK:
+
+all: $(OBJDEST) $(LIBDIR) $(LDAP_BACKLDIF)
+
+$(LIBDIR):
+ $(MKDIR) $(LIBDIR)
+
+$(LDAP_BACKLDIF): $(OBJS) $(BACKLDIF_DLL_OBJ)
+ $(LINK_DLL) $(BACKLDIF_DLL_OBJ) $(EXTRA_LIBS)
+
+$(SERVER_OBJDEST)/ch_malloc.o: ../ch_malloc.c
+ $(CC) -c $(CFLAGS) $(MCC_INCLUDE) $< $(OFFLAG)$*.o
+
+clean:
+ $(RM) $(OBJS)
+ifeq ($(ARCH), WINNT)
+ $(RM) $(BACKLDIF_DLL_OBJ)
+endif
+ $(RM) $(LDAP_BACKLDIF)
+
+$(OBJDEST):
+ $(MKDIR) $(OBJDEST)
+
diff --git a/ldap/servers/slapd/back-ldif/add.c b/ldap/servers/slapd/back-ldif/add.c
new file mode 100644
index 00000000..cd0b81dc
--- /dev/null
+++ b/ldap/servers/slapd/back-ldif/add.c
@@ -0,0 +1,189 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+/*
+ * File: add.c
+ *
+ * Functions:
+ *
+ * ldif_back_add() - ldap ldif back-end add routine
+ * ldifentry_init() - takes an Entry and makes an ldif_Entry
+ *
+ */
+
+#include "back-ldif.h"
+ldif_Entry * ldifentry_init(Slapi_Entry *);
+
+/*
+ * Function: ldif_back_add
+ *
+ * Returns: returns 0 if good, -1 else.
+ *
+ * Description: For changetype: add, this function adds the entry
+ */
+int
+ldif_back_add( Slapi_PBlock *pb )
+{
+ LDIF *db; /*Stores the ldif database*/
+ char *dn = NULL, *parentdn = NULL;
+ Slapi_Entry *e; /*The new entry to add*/
+ ldif_Entry *new, *old; /*Used for various accounting purposes*/
+ ldif_Entry *prev; /*Used to add new ldif_Entry to db*/
+ ldif_Entry *tprev; /*Dummy pointer for traversing the list*/
+ char *errbuf = NULL;
+
+ prev = NULL;
+ tprev = NULL;
+
+ /*Turn on tracing to see this printed out*/
+ LDAPDebug( LDAP_DEBUG_TRACE, "=> ldif_back_add\n", 0, 0, 0 );
+
+ /*Get the database, the dn and the entry to add*/
+ if (slapi_pblock_get( pb, SLAPI_PLUGIN_PRIVATE, &db ) < 0 ||
+ slapi_pblock_get( pb, SLAPI_ADD_TARGET, &dn ) < 0 ||
+ slapi_pblock_get( pb, SLAPI_ADD_ENTRY, &e ) < 0){
+ slapi_send_ldap_result( pb, LDAP_OPERATIONS_ERROR, NULL, NULL, 0, NULL );
+ return(-1);
+ }
+
+ /*Check to make sure the entry passes the schema check*/
+ if ( slapi_entry_schema_check( pb, e ) != 0 ) {
+ LDAPDebug( LDAP_DEBUG_TRACE, "entry failed schema check\n", 0, 0, 0 );
+ slapi_send_ldap_result( pb, LDAP_OBJECT_CLASS_VIOLATION, NULL, NULL, 0, NULL );
+ return( -1 );
+ }
+
+ prev = NULL;
+
+ /*Lock the database*/
+ PR_Lock( db->ldif_lock );
+
+ /*
+ * Attempt to find this dn in db. If there is no such dn,
+ * ldif_find_entry should return NULL, and prev should point
+ * to the last element in the list.
+ */
+ if ((old = (ldif_Entry *)ldif_find_entry(pb, db, dn, &prev)) != NULL) {
+
+ /*
+ * If we've reached this code, there is an entry in db
+ * whose dn matches dn, so release the db lock,
+ * tell the user and return
+ */
+ PR_Unlock( db->ldif_lock );
+ slapi_send_ldap_result( pb, LDAP_ALREADY_EXISTS, NULL, NULL, 0, NULL );
+ return( -1 );
+ }
+
+
+ /*
+ * Get the parent dn and see if the corresponding entry exists.
+ * If the parent does not exist, only allow the "root" user to
+ * add the entry.
+ */
+ if ( (parentdn = slapi_dn_beparent( pb, dn )) != NULL ) {
+ int rc;
+ if ((old = (ldif_Entry *)ldif_find_entry( pb, db, parentdn, &tprev)) == NULL ) {
+ slapi_send_ldap_result( pb, LDAP_NO_SUCH_OBJECT, NULL, NULL, 0, NULL );
+ LDAPDebug( LDAP_DEBUG_TRACE, "parent does not exist\n", 0, 0, 0 );
+ goto error_return;
+ }
+ rc= slapi_access_allowed( pb, e, NULL, NULL, SLAPI_ACL_ADD );
+ if ( rc!=LDAP_SUCCESS ) {
+ LDAPDebug( LDAP_DEBUG_TRACE, "no access to parent\n", 0,0, 0 );
+ slapi_send_ldap_result( pb, rc, NULL, NULL, 0, NULL );
+ goto error_return;
+ }
+ } else { /* no parent */
+ int isroot;
+ slapi_pblock_get( pb, SLAPI_REQUESTOR_ISROOT, &isroot );
+ if ( !isroot ) {
+ LDAPDebug( LDAP_DEBUG_TRACE, "no parent & not root\n", 0, 0, 0 );
+ slapi_send_ldap_result( pb, LDAP_INSUFFICIENT_ACCESS, NULL, NULL, 0, NULL );
+ goto error_return;
+ }
+
+ }
+
+ /*
+ * Before we add the entry, find out if the syntax of the aci
+ * aci attribute values are correct or not. We don't want to add
+ * the entry if the syntax is incorrect.
+ */
+ if ( slapi_acl_verify_aci_syntax(pb, e, &errbuf) != 0 ) {
+ slapi_send_ldap_result( pb, LDAP_INVALID_SYNTAX, NULL, errbuf, 0, NULL );
+ if (errbuf) free(errbuf);
+ goto error_return;
+ }
+
+ /*Make a new element for the linked list*/
+ if ( (new = (ldif_Entry *)ldifentry_init( e )) == NULL){
+ slapi_send_ldap_result( pb, LDAP_OPERATIONS_ERROR, NULL, NULL, 0, NULL );
+ goto error_return;
+
+ }
+
+ /*Add the new element to the end of the list of entries in db*/
+ if ( update_db(pb, db, new, prev, LDIF_DB_ADD) != 0)
+ {
+ ldifentry_free( new );
+ slapi_send_ldap_result( pb, LDAP_OPERATIONS_ERROR, NULL, NULL, 0, NULL );
+ goto error_return;
+
+ }
+
+ /*We have been sucessful. Tell the user*/
+ slapi_send_ldap_result( pb, LDAP_SUCCESS, NULL, NULL, 0, NULL );
+
+ /*Release the database lock*/
+ PR_Unlock( db->ldif_lock );
+
+ /*Free the parentdn, and return*/
+ if ( parentdn != NULL ){
+ free( (void *)parentdn );
+ }
+ LDAPDebug( LDAP_DEBUG_TRACE, "<= ldif_back_add\n", 0, 0, 0 );
+ return( 0 );
+
+ error_return:;
+ if ( parentdn != NULL ){
+ free( (void *)parentdn );
+ }
+
+ PR_Unlock( db->ldif_lock );
+ return( -1 );
+}
+
+/*
+ * Function: ldifentry_init
+ *
+ * Returns: a pointer to an ldif_Entry, or NULL
+ *
+ * Description: Takes a pointer to an Entry, and sticks
+ * it into an ldif_Entry structure.
+ * Note, uses Malloc.
+ */
+ldif_Entry *
+ldifentry_init(Slapi_Entry *e)
+{
+ ldif_Entry *new;
+
+ /*Alloc a new ldif_entry*/
+ new = (ldif_Entry *) malloc(sizeof(ldif_Entry));
+
+ /*Did it work? if not, return NULL*/
+ if (new == NULL) {
+ return (NULL);
+ }
+ /*If it did work, then fill it*/
+ new->lde_e = e;
+ new->next = NULL;
+
+ /*Send it*/
+ return (new);
+}
+
+
+
diff --git a/ldap/servers/slapd/back-ldif/back-ldif.h b/ldap/servers/slapd/back-ldif/back-ldif.h
new file mode 100644
index 00000000..5875c985
--- /dev/null
+++ b/ldap/servers/slapd/back-ldif/back-ldif.h
@@ -0,0 +1,94 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+/*
+ * File: back-ldif.h
+ *
+ * Description: This header file contains the definitions
+ * for the data structures used in the ldif backend database
+ */
+
+#define SLAPD_LOGGING 1
+
+#include <stdio.h>
+#include <string.h>
+#include <sys/types.h>
+
+/* include NSPR header files */
+#include "prlock.h"
+
+#include "ldaplog.h"
+#include "portable.h"
+#include "dirver.h"
+#include "slap.h"
+
+#ifdef _WIN32
+#define WIN32_LEAN_AND_MEAN
+#include <windows.h>
+#else
+#include <sys/socket.h>
+#endif /* _WIN32 */
+
+/*Defines*/
+#define LDIF_DB_ADD 0
+#define LDIF_DB_DELETE 1
+#define LDIF_DB_REPLACE 2
+
+
+/*This structure basically allows the entries to be linked listed*/
+struct ldif_entry{
+ Slapi_Entry *lde_e; /*ptr to the Entry datatype, but you knew that*/
+ struct ldif_entry *next; /*ptr to the next list element.*/
+};
+typedef struct ldif_entry ldif_Entry;
+
+
+/*Holds the data from the ldif file*/
+struct ldif {
+ long ldif_n; /*The number of entries in the database*/
+ long ldif_tries; /*The number of accesses to the database*/
+ long ldif_hits; /*The number of succesful searches to the db*/
+ char *ldif_file; /*From where we read the ldif data*/
+ PRLock *ldif_lock; /*Write & read lock.(a simple locking model)*/
+ ldif_Entry *ldif_entries; /*The linked list of entries*/
+};
+typedef struct ldif LDIF;
+
+
+/*Prototypes*/
+ int ldif_back_modify( Slapi_PBlock * );
+ int update_db(Slapi_PBlock *, LDIF *, ldif_Entry *, ldif_Entry *, int);
+ int db2disk(Slapi_PBlock *, LDIF *);
+ void ldifentry_free(ldif_Entry *);
+ ldif_Entry *ldifentry_dup(ldif_Entry *);
+ ldif_Entry *ldif_find_entry(Slapi_PBlock *, LDIF *, char *, ldif_Entry **);
+ int apply_mods( Slapi_Entry *, LDAPMod ** );
+
+ int ldif_back_add( Slapi_PBlock *);
+ ldif_Entry *ldifentry_init(Slapi_Entry *);
+ int ldif_back_config( Slapi_PBlock *);
+ static char * ldif_read_one_record( FILE *);
+ int ldif_back_delete( Slapi_PBlock *);
+ int has_children(LDIF *, ldif_Entry *);
+ int ldif_back_init( Slapi_PBlock *);
+
+ int ldif_back_search( Slapi_PBlock * );
+
+ int ldif_back_modrdn( Slapi_PBlock * );
+ static int rdn2typeval(char *, char **, struct berval *);
+ void add_mod( LDAPMod ***, int, char *, struct berval ** );
+
+ int ldif_back_bind( Slapi_PBlock * );
+ int ldif_back_unbind( Slapi_PBlock * );
+
+ int ldif_back_start( Slapi_PBlock * );
+ void ldif_back_close( Slapi_PBlock * );
+ void ldif_back_flush( Slapi_PBlock * );
+ void ldif_free_db(LDIF *);
+
+ int ldif_back_compare( Slapi_PBlock * );
+
+ char * get_monitordn(Slapi_PBlock * );
+ int ldif_back_monitor_info( Slapi_PBlock *pb, LDIF *db);
diff --git a/ldap/servers/slapd/back-ldif/bind.c b/ldap/servers/slapd/back-ldif/bind.c
new file mode 100644
index 00000000..b95a589f
--- /dev/null
+++ b/ldap/servers/slapd/back-ldif/bind.c
@@ -0,0 +1,108 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+/*
+ * File: bind.c
+ *
+ * Functions:
+ *
+ * ldif_back_bind() - ldap ldif back-end bind routine
+ *
+ */
+
+#include "back-ldif.h"
+
+/*
+ * Function: ldif_back_bind
+ *
+ * Returns: returns 0|1 if good, -1 else.
+ *
+ * Description: performs an ldap bind.
+ */
+int
+ldif_back_bind( Slapi_PBlock *pb )
+{
+ char *dn; /*Storage for the dn*/
+ int method; /*Storage for the bind method*/
+ struct berval *cred; /*Storage for the bind credentials*/
+ struct berval **bvals;
+ LDIF *db; /*The database*/
+ ldif_Entry *e, *prev; /*Used for searching the db*/
+ int rc, syntax; /*Storage for error return values*/
+ Slapi_Attr *attr;
+
+ LDAPDebug( LDAP_DEBUG_TRACE, "=> ldif_back_bind\n", 0, 0, 0 );
+
+ prev = NULL;
+
+ /*Get the parameters*/
+ if (slapi_pblock_get( pb, SLAPI_PLUGIN_PRIVATE, &db ) < 0 ||
+ slapi_pblock_get( pb, SLAPI_BIND_TARGET, &dn ) < 0 ||
+ slapi_pblock_get( pb, SLAPI_BIND_METHOD, &method ) < 0 ||
+ slapi_pblock_get( pb, SLAPI_BIND_CREDENTIALS, &cred ) < 0){
+ slapi_send_ldap_result( pb, LDAP_OPERATIONS_ERROR, NULL, NULL, 0, NULL );
+ return(-1);
+ }
+
+ /*Lock the database*/
+ PR_Lock( db->ldif_lock );
+
+
+ /*Find the entry that the person is attempting to bind as*/
+ if ( (e = (ldif_Entry *)ldif_find_entry( pb, db, dn, &prev )) == NULL ) {
+
+ /* Allow noauth binds */
+ if ( method == LDAP_AUTH_SIMPLE && cred->bv_len == 0 ) {
+ rc = SLAPI_BIND_ANONYMOUS;
+ } else {
+ slapi_send_ldap_result( pb, LDAP_NO_SUCH_OBJECT, NULL, NULL, 0, NULL );
+ rc = SLAPI_BIND_FAIL;
+ }
+
+ /*Unlock the database*/
+ PR_Unlock( db->ldif_lock );
+
+ return( rc );
+ }
+
+ switch ( method ) {
+ case LDAP_AUTH_SIMPLE:
+ if ( cred->bv_len == 0 ) {
+ PR_Unlock( db->ldif_lock );
+ return( SLAPI_BIND_ANONYMOUS );
+ }
+
+ if ( slapi_entry_attr_find( e->lde_e, "userpassword", &attr ) != 0 ) {
+ slapi_send_ldap_result( pb, LDAP_INAPPROPRIATE_AUTH, NULL, NULL, 0, NULL );
+ PR_Unlock( db->ldif_lock );
+ return( SLAPI_BIND_FAIL );
+ }
+ /*
+ * XXXmcs: slapi_attr_get_values() is deprecated and should be avoided
+ * See XXXmcs comments in ../attr.c for detailed information.
+ */
+ slapi_attr_get_values( attr, &bvals );
+
+ if ( slapi_pw_find( bvals, cred ) != 0 ) {
+ slapi_send_ldap_result( pb, LDAP_INVALID_CREDENTIALS, NULL, NULL, 0, NULL );
+ PR_Unlock( db->ldif_lock );
+ return( SLAPI_BIND_FAIL );
+ }
+ break;
+
+ default:
+ slapi_send_ldap_result( pb, LDAP_STRONG_AUTH_NOT_SUPPORTED, NULL,
+ "auth method not supported", 0, NULL );
+ PR_Unlock( db->ldif_lock );
+ return( SLAPI_BIND_FAIL );
+ }
+
+ PR_Unlock( db->ldif_lock );
+
+ LDAPDebug( LDAP_DEBUG_TRACE, "<= ldif_back_bind\n", 0, 0, 0 );
+
+ /* success: front end will send result */
+ return( SLAPI_BIND_SUCCESS );
+}
diff --git a/ldap/servers/slapd/back-ldif/close.c b/ldap/servers/slapd/back-ldif/close.c
new file mode 100644
index 00000000..34048eca
--- /dev/null
+++ b/ldap/servers/slapd/back-ldif/close.c
@@ -0,0 +1,79 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+/*
+ * File: close.c
+ *
+ * Functions:
+ *
+ * ldif_back_close() - ldap ldif back-end close routine
+ *
+ */
+
+#include "back-ldif.h"
+
+/*
+ * Function: ldif_free_db
+ *
+ * Returns: void
+ *
+ * Description: frees up the ldif database
+ */
+void
+ldif_free_db(LDIF *db)
+{
+ ldif_Entry *cur; /*Used for walking down the list*/
+
+ /*If db is null, there is nothing to do*/
+ if (db == NULL) {
+ return;
+ }
+
+ /*Walk down the list, freeing up the ldif_entries*/
+ for (cur = db->ldif_entries; cur != NULL; cur = cur->next){
+ ldifentry_free(cur);
+ }
+
+ /*Free the ldif_file string, and then the db itself*/
+ free ((void *)db->ldif_file);
+ free((void *) db);
+}
+
+
+
+/*
+ * Function: ldif_back_close
+ *
+ * Returns: void
+ *
+ * Description: closes the ldif backend, frees up the database
+ */
+void
+ldif_back_close( Slapi_PBlock *pb )
+{
+ LDIF *db;
+
+ LDAPDebug( LDAP_DEBUG_TRACE, "ldbm backend syncing\n", 0, 0, 0 );
+ slapi_pblock_get( pb, SLAPI_PLUGIN_PRIVATE, &db );
+ ldif_free_db(db);
+ LDAPDebug( LDAP_DEBUG_TRACE, "ldbm backend done syncing\n", 0, 0, 0 );
+}
+
+/*
+ * Function: ldif_back_flush
+ *
+ * Returns: void
+ *
+ * Description: does nothing
+ */
+void
+ldif_back_flush( Slapi_PBlock *pb )
+{
+ LDAPDebug( LDAP_DEBUG_TRACE, "ldbm backend flushing\n", 0, 0, 0 );
+ LDAPDebug( LDAP_DEBUG_TRACE, "ldbm backend done flushing\n", 0, 0, 0 );
+ return;
+}
+
+
diff --git a/ldap/servers/slapd/back-ldif/compare.c b/ldap/servers/slapd/back-ldif/compare.c
new file mode 100644
index 00000000..bb0c3754
--- /dev/null
+++ b/ldap/servers/slapd/back-ldif/compare.c
@@ -0,0 +1,82 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+/*
+ * File: compare.c
+ *
+ * Functions:
+ *
+ * ldif_back_compare() - ldap ldif back-end compare routine
+ *
+ */
+
+#include "back-ldif.h"
+
+/*
+ * Function: ldif_back_compare
+ *
+ * Returns: -1, 0 or 1
+ *
+ * Description: compares entries in the ldif backend
+ */
+int
+ldif_back_compare( Slapi_PBlock *pb )
+{
+ LDIF *db; /*The Database*/
+ ldif_Entry *e, *prev; /*Used for searching the database*/
+ char *dn, *type; /*The dn and the type*/
+ struct berval *bval;
+ Slapi_Attr *attr;
+ int rc;
+
+ LDAPDebug( LDAP_DEBUG_TRACE, "=> ldif_back_compare\n", 0, 0, 0 );
+ prev = NULL;
+
+ if (slapi_pblock_get( pb, SLAPI_PLUGIN_PRIVATE, &db ) < 0 ||
+ slapi_pblock_get( pb, SLAPI_COMPARE_TARGET, &dn ) < 0 ||
+ slapi_pblock_get( pb, SLAPI_COMPARE_TYPE, &type ) < 0 ||
+ slapi_pblock_get( pb, SLAPI_COMPARE_VALUE, &bval ) < 0){
+ slapi_send_ldap_result( pb, LDAP_OPERATIONS_ERROR, NULL, NULL, 0, NULL );
+ return(-1);
+ }
+
+ /*Lock the database*/
+ PR_Lock( db->ldif_lock );
+
+
+ /*Find the entry for comparison*/
+ if ( (e = (ldif_Entry*) ldif_find_entry( pb, db, dn, &prev )) == NULL ) {
+ slapi_send_ldap_result( pb, LDAP_NO_SUCH_OBJECT, NULL, NULL, 0, NULL );
+ PR_Unlock( db->ldif_lock );
+ return( 1 );
+ }
+
+ /*Check the access*/
+ rc= slapi_access_allowed( pb, e->lde_e, type, bval, SLAPI_ACL_COMPARE );
+ if ( rc!=LDAP_SUCCESS ) {
+ slapi_send_ldap_result( pb, rc, NULL, NULL, 0, NULL );
+ PR_Unlock( db->ldif_lock );
+ return( 1 );
+ }
+
+ /*find the attribute*/
+ if ( slapi_entry_attr_find( e->lde_e, type, &attr ) != 0 ) {
+ slapi_send_ldap_result( pb, LDAP_NO_SUCH_ATTRIBUTE, NULL, NULL, 0, NULL );
+ PR_Unlock( db->ldif_lock );
+ return( 1 );
+ }
+
+ if ( slapi_attr_value_find( attr, bval ) == 0 ) {
+ slapi_send_ldap_result( pb, LDAP_COMPARE_TRUE, NULL, NULL, 0, NULL );
+ PR_Unlock( db->ldif_lock );
+ return( 0 );
+ }
+
+ slapi_send_ldap_result( pb, LDAP_COMPARE_FALSE, NULL, NULL, 0, NULL );
+ PR_Unlock( db->ldif_lock );
+ LDAPDebug( LDAP_DEBUG_TRACE, "<= ldif_back_compare\n", 0, 0, 0 );
+ return( 0 );
+}
+
diff --git a/ldap/servers/slapd/back-ldif/config.c b/ldap/servers/slapd/back-ldif/config.c
new file mode 100644
index 00000000..a62335a5
--- /dev/null
+++ b/ldap/servers/slapd/back-ldif/config.c
@@ -0,0 +1,203 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+/*
+ * File: config.c
+ *
+ * Functions:
+ *
+ * ldif_back_config() - Reads in and stores ldif file for ldif backend
+ * ldif_read_one_record() - Reads in one record from ldif file as a string
+ *
+ */
+
+#include "back-ldif.h"
+#define LDAPMOD_MAXLINE 4096
+#define safe_realloc( ptr, size ) ( ptr == NULL ? malloc( size ) : \
+ realloc( ptr, size ))
+static char *ldif_read_one_record();
+
+
+/*
+ * Function: ldif_back_config
+ *
+ * Returns: 0 if success, -1 if not
+ *
+ * Description: Reads the data in the ldif file specified in ldif.conf
+ * stores it in db
+ */
+int
+ldif_back_config( Slapi_PBlock *pb )
+{
+ LDIF *db; /*The ldif file will be read into this structure*/
+ char *fname; /*Config file name*/
+ int lineno, argc; /*Config file stuff*/
+ char **argv; /*More config file stuff*/
+ FILE *fp; /*Pointer to ldif file*/
+ char *buf; /*Tmp storage for ldif entries*/
+ int first; /*Boolean to determine if db is empty*/
+ ldif_Entry *cur; /*For db manipulation*/
+ ldif_Entry *new; /*For db manipulation*/
+ Slapi_Entry *tmp; /*Used for initialization purposes*/
+
+
+ LDAPDebug( LDAP_DEBUG_TRACE, "=> ldif_back_config\n", 0, 0, 0 );
+
+ /*
+ * Get the private_info structure you created in ldif_back_init().
+ * Also get the config file name, current line number, and arguments
+ * from the current line, broken out into an argv.
+ */
+ if (slapi_pblock_get( pb, SLAPI_PLUGIN_PRIVATE, &db ) < 0 ||
+ slapi_pblock_get( pb, SLAPI_CONFIG_FILENAME, &fname ) < 0 ||
+ slapi_pblock_get( pb, SLAPI_CONFIG_LINENO, &lineno ) < 0 ||
+ slapi_pblock_get( pb, SLAPI_CONFIG_ARGC, &argc ) < 0 ||
+ slapi_pblock_get( pb, SLAPI_CONFIG_ARGV, &argv ) < 0 ){
+ LDAPDebug(LDAP_DEBUG_ANY, "Ldif Backend: unable to get data from front end\n", 0, 0, 0);
+ return(-1);
+ }
+
+
+ /*
+ * Process the config info. For example, if the config file
+ * contains a line like this:
+ *
+ * file /path/to/ldif/file
+ *
+ * then argv[0] would be "file", and argv[1] would be the file
+ * name.
+ */
+
+ /*Check for the correct number of arguments*/
+ if (argc != 2){
+ LDAPDebug(LDAP_DEBUG_ANY, "Ldif Backend: Unable to configure; invalid ldif input file specification line format (file: %s, line: %d)\n",
+ fname, lineno, 0);
+ return(-1);
+ }
+
+ /*Check for the right format*/
+ if (strcmp(argv[0], "file") != 0){
+ LDAPDebug(LDAP_DEBUG_ANY, "Ldif Backend: unable to configure; invalid parameter \"%s\" in config file (file: %s, line: %d)\n",
+ argv[0], fname, lineno );
+ return(-1);
+ }
+
+ /*Now we fopen the file and grab up the contents*/
+ fp = fopen (argv[1], "r");
+ if (fp == NULL){
+ LDAPDebug(LDAP_DEBUG_ANY, "Ldif Backend: unable to read ldif file %s\n", argv[1], 0, 0);
+ fp = fopen (argv[1], "w");
+ if(fp == NULL){
+ LDAPDebug(LDAP_DEBUG_ANY, "Ldif Backend: unable to create ldif file %s\n", argv[1], 0, 0);
+ return -1;
+ }
+ }
+
+ first = 1;
+
+ /* Lock the database first, just to be safe*/
+ PR_Lock( db->ldif_lock );
+
+ /*Save the filename, for modifications to the file later*/
+ if ((db->ldif_file = strdup(argv[1])) == NULL){
+ LDAPDebug(LDAP_DEBUG_ANY, "Ldif Backend: out of memory\n", 0, 0, 0);
+ PR_Unlock( db->ldif_lock );
+ fclose(fp);
+ return(-1);
+ }
+
+ /*
+ * Loop through the entries in the file, and add them to the end
+ * of the linked list
+ */
+ while ((buf = ldif_read_one_record(fp)) != NULL){
+
+ /*Create a new element for the linked list of entries*/
+ tmp = (Slapi_Entry *) slapi_str2entry(buf,
+ SLAPI_STR2ENTRY_NOT_WELL_FORMED_LDIF);
+ new = (ldif_Entry *) ldifentry_init(tmp);
+ if (new == NULL){
+ LDAPDebug(LDAP_DEBUG_ANY, "Ldif Backend: unable to read in ldif file; out of memory\n",0 ,0 ,0 );
+ PR_Unlock( db->ldif_lock );
+ fclose(fp);
+ return(-1);
+ }
+
+ /*
+ * If this is the first entry we are adding,
+ * we have to make it the first element in the list
+ */
+ if (first){
+ db->ldif_entries = new;
+ first = 0;
+ } else{
+ cur->next = new;
+ }
+
+ /*Reset the pointer to the last element in the list*/
+ cur = new;
+
+ /*Increment the number of entries*/
+ db->ldif_n++;
+
+ /*Free the buffer*/
+ free ((void *) buf );
+
+ }
+
+ /*By now, the database should be read in*/
+ PR_Unlock( db->ldif_lock );
+ fclose(fp);
+
+ LDAPDebug( LDAP_DEBUG_TRACE, "<= ldif_back_config\n", 0, 0, 0 );
+ return( 0 );
+}
+
+
+
+/*
+ * Function: ldif_read_one_record
+ *
+ * Returns: a long string representing an ldif record
+ *
+ * Description: Returns a huge string comprised of 1 ldif record
+ * read from fp.
+ *
+ */
+static char *
+ldif_read_one_record( FILE *fp )
+{
+ int len, gotnothing;
+ char *buff, line[ LDAPMOD_MAXLINE ];
+ int lcur, lmax;
+
+ lcur = lmax = 0;
+ buff = NULL;
+ gotnothing = 1;
+
+ while ( fgets( line, sizeof(line), fp ) != NULL ) {
+ if ( (len = strlen( line )) < 2 ) {
+ if ( gotnothing ) {
+ continue;
+ } else {
+ break;
+ }
+ }
+ gotnothing = 0;
+ if ( lcur + len + 1 > lmax ) {
+ lmax = LDAPMOD_MAXLINE
+ * (( lcur + len + 1 ) / LDAPMOD_MAXLINE + 1 );
+ if (( buff = (char *)safe_realloc( buff, lmax )) == NULL ) {
+ perror( "safe_realloc" );
+ exit( LDAP_NO_MEMORY );
+ }
+ }
+ strcpy( buff + lcur, line );
+ lcur += len;
+ }
+
+ return( buff );
+}
+
diff --git a/ldap/servers/slapd/back-ldif/delete.c b/ldap/servers/slapd/back-ldif/delete.c
new file mode 100644
index 00000000..cf155ad4
--- /dev/null
+++ b/ldap/servers/slapd/back-ldif/delete.c
@@ -0,0 +1,136 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+/*
+ * File: delete.c
+ *
+ * Functions:
+ *
+ * ldif_back_delete() - ldap ldif back-end delete routine
+ * has_children() - determines if an entry has any children
+ *
+ */
+
+#include "back-ldif.h"
+
+/*
+ * Function: ldif_back_delete
+ *
+ * Returns: returns 0 if good, -1 else.
+ *
+ * Description: For changetype: delete, this function deletes the entry
+ */
+int
+ldif_back_delete( Slapi_PBlock *pb )
+{
+ LDIF *db; /*The database*/
+ ldif_Entry *bye, *prev; /*"bye" is the record to be deleted*/
+ char *dn; /*Storage for the dn*/
+ int rc;
+
+ LDAPDebug( LDAP_DEBUG_TRACE, "=> ldif_back_delete\n", 0, 0, 0 );
+
+ prev = NULL;
+
+ /*Get the database and the dn to delete*/
+ if (slapi_pblock_get( pb, SLAPI_PLUGIN_PRIVATE, &db ) < 0 ||
+ slapi_pblock_get( pb, SLAPI_DELETE_TARGET, &dn ) < 0){
+ slapi_send_ldap_result( pb, LDAP_OPERATIONS_ERROR, NULL, NULL, 0, NULL );
+ return(-1);
+ }
+
+ /*Lock the database*/
+ PR_Lock( db->ldif_lock );
+
+ /* Find the entry we're about to delete*/
+ bye = (ldif_Entry *) ldif_find_entry(pb, db, dn, &prev);
+ if (bye == NULL) {
+ slapi_send_ldap_result( pb, LDAP_NO_SUCH_OBJECT, NULL, NULL, 0, NULL );
+ LDAPDebug( LDAP_DEBUG_TRACE, "entry for delete does not exist\n", 0, 0, 0 );
+ PR_Unlock( db->ldif_lock );
+ return(-1);
+ }
+
+ /*Make sure that we are trying to delete a leaf.*/
+ if ( has_children( db, bye ) ) {
+ slapi_send_ldap_result( pb, LDAP_NOT_ALLOWED_ON_NONLEAF, NULL, NULL, 0, NULL );
+ PR_Unlock( db->ldif_lock );
+ return( -1 );
+ }
+
+ /*Check the access*/
+ rc= slapi_access_allowed( pb, bye->lde_e, "entry", NULL, SLAPI_ACL_DELETE );
+ if ( rc!=LDAP_SUCCESS) {
+ slapi_send_ldap_result( pb, rc, NULL, NULL, 0, NULL );
+ PR_Unlock( db->ldif_lock );
+ return( -1 );
+ }
+
+ /* Delete from disk and database */
+ if ( update_db(pb, db, NULL, prev, LDIF_DB_DELETE) != 0){
+ slapi_send_ldap_result( pb, LDAP_OPERATIONS_ERROR, NULL, NULL, 0, NULL );
+ PR_Unlock( db->ldif_lock );
+ return(-1);
+
+ }
+
+ /*Success*/
+ slapi_send_ldap_result( pb, LDAP_SUCCESS, NULL, NULL, 0, NULL );
+ PR_Unlock( db->ldif_lock );
+ LDAPDebug( LDAP_DEBUG_TRACE, "<= ldif_back_delete\n", 0, 0, 0 );
+ return( 0 );
+}
+
+/*
+ * Function: has_children
+ *
+ * Returns: returns 1 if the entry has kids, 0 else.
+ *
+ * Description: Determines if the entry has children
+ */
+int
+has_children(LDIF *db, ldif_Entry *p)
+{
+ char *parentdn; /*Basically the dn of p (copied)*/
+ char *childdn; /*Will be used to test if p has any children*/
+ ldif_Entry *cur; /*Used to walk down the list*/
+ int has_kid = 0; /*Flag to return*/
+
+ LDAPDebug( LDAP_DEBUG_TRACE, "=> has_children\n", 0, 0, 0);
+
+ /*If there is no p or db, then there can be no children*/
+ if (p == NULL || db == NULL){
+ return(0);
+ }
+
+ /*Get a copy of p's dn, and normalize it (squeeze any unneeded spaces out)*/
+ parentdn = strdup( slapi_entry_get_dn(p->lde_e) );
+ (void) slapi_dn_normalize( parentdn );
+
+ /*Walk down the list, seeing if each entry has p as a parent*/
+ for (cur = db->ldif_entries; cur != NULL; cur = cur->next){
+ childdn = strdup(slapi_entry_get_dn(cur->lde_e));
+ (void) slapi_dn_normalize(childdn);
+
+ /*Test to see if this childdn is a child of the parentdn*/
+ if (slapi_dn_issuffix( childdn, parentdn ) && strlen(childdn) > strlen(parentdn))
+ {
+ has_kid = 1;
+ free( (void *) childdn);
+ break;
+ }
+ free( (void *) childdn);
+
+ }
+
+ free( (void *) parentdn );
+
+ LDAPDebug( LDAP_DEBUG_TRACE, "<= has_children %d\n", has_kid, 0, 0);
+ return( has_kid );
+}
+
+
+
+
diff --git a/ldap/servers/slapd/back-ldif/dllmain.c b/ldap/servers/slapd/back-ldif/dllmain.c
new file mode 100644
index 00000000..69d0ceb5
--- /dev/null
+++ b/ldap/servers/slapd/back-ldif/dllmain.c
@@ -0,0 +1,132 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+/*
+ * Microsoft Windows specifics for LIBLDAP DLL
+ */
+#include "ldap.h"
+
+
+#ifdef _WIN32
+/* Lifted from Q125688
+ * How to Port a 16-bit DLL to a Win32 DLL
+ * on the MSVC 4.0 CD
+ */
+BOOL WINAPI DllMain (HANDLE hModule, DWORD fdwReason, LPVOID lpReserved)
+{
+ WSADATA wsadata;
+
+ switch (fdwReason)
+ {
+ case DLL_PROCESS_ATTACH:
+ /* Code from LibMain inserted here. Return TRUE to keep the
+ DLL loaded or return FALSE to fail loading the DLL.
+
+ You may have to modify the code in your original LibMain to
+ account for the fact that it may be called more than once.
+ You will get one DLL_PROCESS_ATTACH for each process that
+ loads the DLL. This is different from LibMain which gets
+ called only once when the DLL is loaded. The only time this
+ is critical is when you are using shared data sections.
+ If you are using shared data sections for statically
+ allocated data, you will need to be careful to initialize it
+ only once. Check your code carefully.
+
+ Certain one-time initializations may now need to be done for
+ each process that attaches. You may also not need code from
+ your original LibMain because the operating system may now
+ be doing it for you.
+ */
+ /*
+ * 16 bit code calls UnlockData()
+ * which is mapped to UnlockSegment in windows.h
+ * in 32 bit world UnlockData is not defined anywhere
+ * UnlockSegment is mapped to GlobalUnfix in winbase.h
+ * and the docs for both UnlockSegment and GlobalUnfix say
+ * ".. function is oboslete. Segments have no meaning
+ * in the 32-bit environment". So we do nothing here.
+ */
+
+ if( errno = WSAStartup(0x0101, &wsadata ) != 0 )
+ return FALSE;
+
+ break;
+
+ case DLL_THREAD_ATTACH:
+ /* Called each time a thread is created in a process that has
+ already loaded (attached to) this DLL. Does not get called
+ for each thread that exists in the process before it loaded
+ the DLL.
+
+ Do thread-specific initialization here.
+ */
+ break;
+
+ case DLL_THREAD_DETACH:
+ /* Same as above, but called when a thread in the process
+ exits.
+
+ Do thread-specific cleanup here.
+ */
+ break;
+
+ case DLL_PROCESS_DETACH:
+ /* Code from _WEP inserted here. This code may (like the
+ LibMain) not be necessary. Check to make certain that the
+ operating system is not doing it for you.
+ */
+ WSACleanup();
+
+ break;
+ }
+ /* The return value is only used for DLL_PROCESS_ATTACH; all other
+ conditions are ignored. */
+ return TRUE; // successful DLL_PROCESS_ATTACH
+}
+#else
+int CALLBACK
+LibMain( HINSTANCE hinst, WORD wDataSeg, WORD cbHeapSize, LPSTR lpszCmdLine )
+{
+ /*UnlockData( 0 );*/
+ return( 1 );
+}
+#endif
+
+#ifdef LDAP_DEBUG
+#ifndef _WIN32
+#include <stdarg.h>
+#include <stdio.h>
+
+void LDAPDebug( int level, char* fmt, ... )
+{
+ static char debugBuf[1024];
+
+ if (slapd_ldap_debug & level)
+ {
+ va_list ap;
+ va_start (ap, fmt);
+ _snprintf (debugBuf, sizeof(debugBuf), fmt, ap);
+ va_end (ap);
+
+ OutputDebugString (debugBuf);
+ }
+}
+#endif
+#endif
+
+#ifndef _WIN32
+
+/* The 16-bit version of the RTL does not implement perror() */
+
+#include <stdio.h>
+
+void perror( const char *msg )
+{
+ char buf[128];
+ wsprintf( buf, "%s: error %d\n", msg, WSAGetLastError()) ;
+ OutputDebugString( buf );
+}
+
+#endif
diff --git a/ldap/servers/slapd/back-ldif/init.c b/ldap/servers/slapd/back-ldif/init.c
new file mode 100644
index 00000000..8196d371
--- /dev/null
+++ b/ldap/servers/slapd/back-ldif/init.c
@@ -0,0 +1,114 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+/*
+ * File: init.c
+ *
+ * Functions:
+ *
+ * ldif_back_init() - ldap ldif back-end initialize routine
+ *
+ */
+
+#include "back-ldif.h"
+
+static Slapi_PluginDesc pdesc = { "ldif-backend", "Netscape", PRODUCTTEXT,
+ "LDIF backend database plugin" };
+
+#ifdef _WIN32
+int *module_ldap_debug = 0;
+
+void plugin_init_debug_level(int *level_ptr)
+{
+ module_ldap_debug = level_ptr;
+}
+#endif
+
+/*
+ * Function: ldif_back_init
+ *
+ * Returns: returns 0 if good, -1 else.
+ *
+ * Description: Allocates a database for filling by ldif_back_config
+ */
+int
+ldif_back_init( Slapi_PBlock *pb )
+{
+ LDIF *db; /*This will hold the ldif file in memory*/
+ int rc;
+
+ LDAPDebug( LDAP_DEBUG_TRACE, "=> ldif_back_init\n", 0, 0, 0 );
+
+ /*
+ * Allocate and initialize db with everything we
+ * need to keep track of in this backend. In ldif_back_config(),
+ * we will fill in db with things like the name
+ * of the ldif file containing the database, and any other
+ * options we allow people to set through the config file.
+ */
+
+ /*Allocate memory for our database and check if success*/
+ db = (LDIF *) malloc( sizeof(LDIF) );
+ if (db == NULL) {
+ LDAPDebug(LDAP_DEBUG_ANY, "Ldif Backend: unable to initialize; out of memory\n", 0, 0, 0);
+ return(-1);
+ }
+
+ /*Fill with initial values, including the mutex*/
+ db->ldif_n = 0;
+ db->ldif_entries = NULL;
+ db->ldif_tries = 0;
+ db->ldif_hits = 0;
+ db->ldif_file = NULL;
+ db->ldif_lock = PR_NewLock();
+ if (&db->ldif_lock == NULL) {
+ LDAPDebug(LDAP_DEBUG_ANY, "Ldif Backend: Lock creation failed\n", 0, 0, 0);
+ return(-1);
+ }
+
+
+ /*
+ * set SLAPI_PLUGIN_PRIVATE field in pb, so it's available
+ * later in ldif_back_config(), ldif_back_search(), etc.
+ */
+ rc = slapi_pblock_set( pb, SLAPI_PLUGIN_PRIVATE, (void *) db );
+ rc |= slapi_pblock_set( pb, SLAPI_PLUGIN_DESCRIPTION, (void *) &pdesc );
+ rc |= slapi_pblock_set( pb, SLAPI_PLUGIN_VERSION,
+ (void *) SLAPI_PLUGIN_VERSION_01 );
+ rc |= slapi_pblock_set( pb, SLAPI_PLUGIN_DB_BIND_FN,
+ (void *) ldif_back_bind );
+ rc |= slapi_pblock_set( pb, SLAPI_PLUGIN_DB_UNBIND_FN,
+ (void *) ldif_back_unbind );
+ rc |= slapi_pblock_set( pb, SLAPI_PLUGIN_DB_SEARCH_FN,
+ (void *) ldif_back_search );
+ rc |= slapi_pblock_set( pb, SLAPI_PLUGIN_DB_COMPARE_FN,
+ (void *) ldif_back_compare );
+ rc |= slapi_pblock_set( pb, SLAPI_PLUGIN_DB_MODIFY_FN,
+ (void *) ldif_back_modify );
+ rc |= slapi_pblock_set( pb, SLAPI_PLUGIN_DB_MODRDN_FN,
+ (void *) ldif_back_modrdn );
+ rc |= slapi_pblock_set( pb, SLAPI_PLUGIN_DB_ADD_FN,
+ (void *) ldif_back_add );
+ rc |= slapi_pblock_set( pb, SLAPI_PLUGIN_DB_DELETE_FN,
+ (void *) ldif_back_delete );
+ rc |= slapi_pblock_set( pb, SLAPI_PLUGIN_DB_CONFIG_FN,
+ (void *) ldif_back_config );
+ rc |= slapi_pblock_set( pb, SLAPI_PLUGIN_CLOSE_FN,
+ (void *) ldif_back_close );
+ rc |= slapi_pblock_set( pb, SLAPI_PLUGIN_DB_FLUSH_FN,
+ (void *) ldif_back_flush );
+ rc |= slapi_pblock_set( pb, SLAPI_PLUGIN_START_FN,
+ (void *) ldif_back_start );
+ if (rc != 0) {
+ LDAPDebug(LDAP_DEBUG_ANY, "Ldif Backend: unable to pass database information to front end\n",0 ,0 ,0);
+ return(-1);
+ }
+
+ LDAPDebug( LDAP_DEBUG_TRACE, "<= ldif_back_init\n", 0, 0, 0 );
+
+ return( 0 );
+}
+
+
diff --git a/ldap/servers/slapd/back-ldif/libback-ldif.def b/ldap/servers/slapd/back-ldif/libback-ldif.def
new file mode 100644
index 00000000..f4724f97
--- /dev/null
+++ b/ldap/servers/slapd/back-ldif/libback-ldif.def
@@ -0,0 +1,12 @@
+; BEGIN COPYRIGHT BLOCK
+; Copyright 2001 Sun Microsystems, Inc.
+; Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+; All rights reserved.
+; END COPYRIGHT BLOCK
+;
+DESCRIPTION 'Directory Server LDIF Backend Plugin'
+EXPORTS
+ ldif_back_init @2
+ plugin_init_debug_level @3
+
+
diff --git a/ldap/servers/slapd/back-ldif/modify.c b/ldap/servers/slapd/back-ldif/modify.c
new file mode 100644
index 00000000..53f0bb2e
--- /dev/null
+++ b/ldap/servers/slapd/back-ldif/modify.c
@@ -0,0 +1,557 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+/*
+ * File: modify.c
+ *
+ * Functions:
+ *
+ * ldif_back_modify() - ldif backend modify function
+ * update_db() - updates memory and disk db to reflect changes
+ * db2disk() - writes out ldif database to disk
+ * ldifentry_free() - frees an ldif_Entry
+ * ldifentry_dup() - copies an ldif_Entry
+ * ldif_find_entry() - searches an ldif DB for a particular dn
+ * apply_mods() - applies the modifications to an Entry
+ *
+ */
+
+#include "back-ldif.h"
+
+/*Prototypes*/
+void ldifentry_free(ldif_Entry *);
+ldif_Entry * ldifentry_dup(ldif_Entry *);
+int apply_mods( Slapi_Entry *, LDAPMod ** );
+ldif_Entry * ldif_find_entry(Slapi_PBlock *, LDIF *, char *, ldif_Entry **);
+int db2disk(Slapi_PBlock *, LDIF *);
+int update_db(Slapi_PBlock *, LDIF *, ldif_Entry *, ldif_Entry *, int );
+
+/*
+ * Function: ldif_back_modify
+ *
+ * Returns: returns 0 if good, -1 else.
+ *
+ * Description: For changetype: modify, this makes the changes
+ */
+int
+ldif_back_modify( Slapi_PBlock *pb )
+{
+ LDIF *db; /*The ldif file is stored here*/
+ ldif_Entry *entry, *entry2,*prev; /*For db manipulation*/
+ int err; /*House keeping stuff*/
+ LDAPMod **mods; /*Used to apply the modifications*/
+ char *dn; /*Storage for the dn*/
+ char *errbuf = NULL; /* To get error back */
+
+ LDAPDebug( LDAP_DEBUG_TRACE, "=> ldif_back_modify\n", 0, 0, 0 );
+ prev = NULL;
+
+ /*Get the database, the dn and the mods*/
+ if (slapi_pblock_get( pb, SLAPI_PLUGIN_PRIVATE, &db ) < 0 ||
+ slapi_pblock_get( pb, SLAPI_MODIFY_TARGET, &dn ) < 0 ||
+ slapi_pblock_get( pb, SLAPI_MODIFY_MODS, &mods ) < 0){
+ slapi_send_ldap_result( pb, LDAP_OPERATIONS_ERROR, NULL, NULL, 0, NULL );
+ return(-1);
+ }
+ /*Lock the database*/
+ PR_Lock( db->ldif_lock );
+
+ /*
+ * Find the entry we are about to modify.
+ * prev will point to the previous element in the list,
+ * NULL if there is no previous element.
+ */
+ if ( (entry = (ldif_Entry *)ldif_find_entry( pb, db, dn, &prev)) == NULL ) {
+ slapi_send_ldap_result( pb, LDAP_NO_SUCH_OBJECT, NULL, NULL, 0, NULL );
+ PR_Unlock( db->ldif_lock );
+ return( -1 );
+ }
+
+ /*Check acl, note that entry is not an Entry, but a ldif_Entry*/
+ if ( (err = slapi_acl_check_mods( pb, entry->lde_e, mods, &errbuf )) != LDAP_SUCCESS ) {
+ slapi_send_ldap_result( pb, err, NULL, errbuf, 0, NULL );
+ if (errbuf) free (errbuf);
+ PR_Unlock( db->ldif_lock );
+ goto error_return;
+ }
+
+ /* Create a copy of the entry and apply the changes to it */
+ if ( (entry2 = (ldif_Entry *) ldifentry_dup( entry )) == NULL ) {
+ slapi_send_ldap_result( pb, LDAP_OPERATIONS_ERROR, NULL, NULL, 0, NULL );
+ PR_Unlock( db->ldif_lock );
+ goto error_return;
+ }
+
+ /*Actually apply the modifications*/
+ if ( (err = apply_mods( entry2->lde_e, mods )) != 0 ) {
+ slapi_send_ldap_result( pb, err, NULL, NULL, 0, NULL );
+ PR_Unlock( db->ldif_lock );
+ goto error_return;
+ }
+
+ /* Check for abandon */
+ if ( slapi_op_abandoned( pb ) ) {
+ PR_Unlock( db->ldif_lock );
+ goto error_return;
+ }
+
+ /* Check that the entry still obeys the schema */
+ if ( slapi_entry_schema_check( pb, entry2->lde_e ) != 0 ) {
+ slapi_send_ldap_result( pb, LDAP_OBJECT_CLASS_VIOLATION, NULL, NULL, 0, NULL );
+ PR_Unlock( db->ldif_lock );
+ goto error_return;
+ }
+
+ /* Check for abandon again */
+ if ( slapi_op_abandoned( pb ) ) {
+ PR_Unlock( db->ldif_lock );
+ goto error_return;
+ }
+
+ /* Change the entry itself both on disk and in the cache */
+ if ( update_db(pb, db, entry2, prev, LDIF_DB_REPLACE) != 0) {
+ slapi_send_ldap_result( pb, LDAP_OPERATIONS_ERROR, NULL, NULL, 0, NULL );
+ PR_Unlock( db->ldif_lock );
+ goto error_return;
+ }
+
+ slapi_send_ldap_result( pb, LDAP_SUCCESS, NULL, NULL, 0, NULL );
+ PR_Unlock( db->ldif_lock );
+ LDAPDebug( LDAP_DEBUG_TRACE, "<= ldif_back_modify\n", 0, 0, 0 );
+ return( 0 );
+
+ error_return:;
+ if ( entry2 != NULL ) {
+ ldifentry_free( entry2 );
+ }
+
+ return( -1 );
+}
+
+/*
+ * Function: update_db
+ *
+ * Returns: returns 0 if good, -1 else.
+ *
+ * Description: Will update the database in memory, and on disk
+ * if prev == NULL, then the element to be deleted/replaced
+ * is the first in the list.
+ * mode = LDIF_DB_ADD | LDIF_DB_REPLACE | LDIF_DB_DELETE
+ * The database should be locked when this function is called.
+ * Note that on replaces and deletes, the old ldif_Entry's
+ * are freed.
+ */
+int
+update_db(Slapi_PBlock *pb, LDIF *db, ldif_Entry *new, ldif_Entry *prev, int mode)
+{
+ ldif_Entry *tmp; /*Used to free the removed/replaced entries*/
+ char *buf; /*Used to convert entries to strings for output to file*/
+ FILE *fp; /*File ptr to the ldif file*/
+ int len; /*Used by slapi_entry2str*/
+ int db_updated=0; /*Flag to designate if db in memory has been updated*/
+
+ /*Make sure that the database is not null. Everything else can be, though*/
+ if (db == NULL){
+ slapi_send_ldap_result( pb, LDAP_OPERATIONS_ERROR, NULL, NULL, 0, NULL );
+ return(-1);
+ }
+
+ /*
+ * If we are adding an entry, then prev should be pointing
+ * to the last element in the list, or null if the list is empty,
+ * and new should not be null.
+ */
+ if (mode == LDIF_DB_ADD) {
+
+ /*Make sure there is something to add*/
+ if ( new == NULL ) {
+ slapi_send_ldap_result( pb, LDAP_OPERATIONS_ERROR, NULL, NULL, 0, NULL );
+ return(-1);
+ }
+
+ /*If prev is null, then there had better be no entries in the list*/
+ if (prev == NULL){
+ if( db->ldif_entries != NULL) {
+ slapi_send_ldap_result( pb, LDAP_OPERATIONS_ERROR, NULL, NULL, 0, NULL );
+ return(-1);
+ }
+ /*There are no elements, so let's add the new one*/
+ db->ldif_entries = new;
+ db->ldif_n++;
+
+ /*Set a flag*/
+ db_updated = 1;
+
+ }
+ /*
+ * Last error case to test for is if prev is not null, and prev->next
+ * points to something. This means that we are not at the end of the list
+ */
+ if (prev != NULL) {
+ if (prev->next != NULL) {
+ slapi_send_ldap_result( pb, LDAP_OPERATIONS_ERROR, NULL, NULL, 0, NULL );
+ return(-1);
+ }
+
+ /*We're at the end of the list, so tack the new entry onto the end*/
+ prev->next = new;
+ db->ldif_n++;
+
+ db_updated = 1;
+
+ }
+
+ /*If the database has been updated in memory, update the disk*/
+ if (db_updated && db->ldif_file!=NULL) {
+
+ /*Update the disk by appending to the ldif file*/
+ fp = fopen(db->ldif_file, "a");
+ if (fp == NULL) {
+ slapi_send_ldap_result( pb, LDAP_OPERATIONS_ERROR, NULL, NULL, 0, NULL );
+ /*This is s pretty serious problem, so we exit*/
+ exit(-1);
+ }
+
+ /*Convert the entry to ldif format*/
+ buf = slapi_entry2str(new->lde_e, &len);
+ fprintf(fp, "%s\n", buf);
+ free ( (void *) buf);
+ fclose(fp);
+ return(0);
+ } else {
+ slapi_send_ldap_result( pb, LDAP_OPERATIONS_ERROR, NULL, NULL, 0, NULL );
+ fclose(fp);
+ return(-1);
+
+ }
+
+ } else if (mode == LDIF_DB_DELETE){
+
+ /*We're not deleting the first entry in the list*/
+ if (prev != NULL){
+ /*Make sure there is something to delete*/
+ if (prev->next == NULL) {
+ slapi_send_ldap_result( pb, LDAP_OPERATIONS_ERROR, NULL, NULL, 0, NULL );
+ return(-1);
+ }
+ tmp = prev->next;
+ prev->next = tmp->next;
+ db->ldif_n--;
+ ldifentry_free(tmp);
+
+ db_updated = 1;
+
+ } else { /*We are deleting the first entry in the list*/
+
+ /*Make sure there is something to delete*/
+ if (db->ldif_entries == NULL) {
+ slapi_send_ldap_result( pb, LDAP_OPERATIONS_ERROR, NULL, NULL, 0, NULL );
+ return(-1);
+ }
+
+ tmp = db->ldif_entries;
+ db->ldif_entries = tmp->next;
+ db->ldif_n--;
+
+ /*Free the entry, and set the flag*/
+ ldifentry_free(tmp);
+ db_updated = 1;
+ }
+
+ /*
+ * Update the disk by rewriting entire ldif file
+ * I know, I know, but simplicity is the key here.
+ */
+ if (db_updated) {
+ return(db2disk(pb, db));
+ } else {
+ slapi_send_ldap_result( pb, LDAP_OPERATIONS_ERROR, NULL, NULL, 0, NULL );
+ return(-1);
+
+ }
+
+ } else if (mode == LDIF_DB_REPLACE) {
+
+ /*Make sure there is something to replace with*/
+ if ( new == NULL ) {
+ slapi_send_ldap_result( pb, LDAP_OPERATIONS_ERROR, NULL, NULL, 0, NULL );
+ return(-1);
+ }
+
+
+ /*We're not replacing the first element in the list*/
+ if (prev != NULL){
+
+ /*Make sure there is something to replace*/
+ if (prev->next == NULL) {
+ slapi_send_ldap_result( pb, LDAP_OPERATIONS_ERROR, NULL, NULL, 0, NULL );
+ return(-1);
+ }
+
+ /*Splice out the old entry, and put in the new*/
+ tmp = prev->next;
+ prev->next = new;
+ new->next = tmp->next;
+
+ /*Free it*/
+ ldifentry_free(tmp);
+ db_updated = 1;
+ } else { /*We are replacing the first entry in the list*/
+
+ /*Make sure there is something to replace*/
+ if (db->ldif_entries == NULL) {
+ slapi_send_ldap_result( pb, LDAP_OPERATIONS_ERROR, NULL, NULL, 0, NULL );
+ return(-1);
+ }
+
+ /*Splice out the old entry, and put in the new*/
+ tmp = db->ldif_entries;
+ db->ldif_entries = new;
+ new->next = tmp->next;
+
+ /*Free it*/
+ ldifentry_free(tmp);
+ db_updated = 1;
+ }
+
+ /*Update the disk by rewriting entire ldif file*/
+ if (db_updated) {
+ return(db2disk(pb, db));
+ } else {
+ slapi_send_ldap_result( pb, LDAP_OPERATIONS_ERROR, NULL, NULL, 0, NULL );
+ return(-1);
+ }
+
+ }
+}
+
+/*
+ * Function: db2disk
+ *
+ * Returns: returns 0 if good, exits else
+ *
+ * Description: Takes an ldif database, db, and writes it out to disk
+ * if it can't open the file, there's trouble, so we exit
+ * because this function is usually called after the db
+ * in memory has been updated.
+ *
+ */
+int
+db2disk(Slapi_PBlock *pb, LDIF *db)
+{
+ ldif_Entry *cur; /*Used for walking down the list*/
+ char *buf; /*temp storage for Entry->ldif converter*/
+ FILE *fp; /*File pointer to ldif target file*/
+ int len; /*length returned by slapi_entry2str*/
+
+ /*Open the file*/
+ fp = fopen(db->ldif_file, "w");
+ if (fp == NULL) {
+ slapi_send_ldap_result( pb, LDAP_OPERATIONS_ERROR, NULL, NULL, 0, NULL );
+ /*This is s pretty serious problem, so we exit*/
+ exit(-1);
+ }
+
+ /*
+ * Walk down the list, converting each entry to a string,
+ * writing the string out to fp
+ */
+ for (cur = db->ldif_entries; cur != NULL; cur = cur->next){
+ buf = slapi_entry2str(cur->lde_e, &len);
+ fprintf(fp, "%s\n",buf);
+ free ( (void *) buf);
+ }
+
+ fclose(fp);
+ return(0);
+
+}
+
+
+/*
+ * Function: ldifentry_free
+ *
+ * Returns: void
+ *
+ * Description: Frees an ldif_Entry
+ */
+void
+ldifentry_free(ldif_Entry *e)
+{
+
+ /*Make sure that there is actually something to free*/
+ if (e == NULL){
+ return;
+ }
+
+ /*Free the entry*/
+ slapi_entry_free(e->lde_e);
+
+ /*Free the entire thing*/
+ free ((void *) e);
+}
+
+/*
+ * Function: ldifentry_dup
+ *
+ * Returns: a pointer to the new ldif_entry, or NULL
+ *
+ * Description: Copies and returns a pointer to a new
+ * ldif_Entry whose contents are a copy of e's contents
+ * Note: uses malloc
+ */
+ldif_Entry *
+ldifentry_dup(ldif_Entry *e)
+{
+ ldif_Entry *new;
+
+ /*Let's make sure that e is not null*/
+ if (e == NULL){
+ return(NULL);
+ }
+
+ /*Allocate a new ldif_entry, and return it if it is null*/
+ new = (ldif_Entry *) malloc( (sizeof(ldif_Entry)));
+ if (new == NULL) {
+ return(new);
+ }
+
+ /*Copy the Entry in e*/
+ new->lde_e = slapi_entry_dup(e->lde_e);
+ new->next = NULL;
+
+ return(new);
+
+
+}
+
+/*
+ * Function: ldif_find_entry
+ *
+ * Returns: A pointer to the matched ldif_Entry, or Null
+ *
+ * Description: Goes down the list of entries in db to find the entry
+ * matching dn. Returns a pointer to the entry,
+ * and sets prev to point to the entry before the match.
+ * If there is no match, prev points to the last
+ * entry in the list, and null is returned.
+ * If the first element matches, prev points to NULL
+ */
+ldif_Entry *
+ldif_find_entry(Slapi_PBlock *pb, LDIF *db, char *dn, ldif_Entry **prev)
+{
+ ldif_Entry *cur; /*Used for walking down the list*/
+ char *finddn, *targetdn; /*Copies of dns for searching */
+ int found_it = 0; /*A flag to denote a successful search*/
+
+ /*Set cur to the start of the list*/
+ cur =db->ldif_entries;
+
+ /*Increase the number of accesses*/
+ db->ldif_tries++;
+
+ /*Make a copy of the target dn, and normalize it*/
+ targetdn = strdup(dn);
+ (void) slapi_dn_normalize(targetdn);
+
+
+ /*Go down the list until we find the entry*/
+ while(cur != NULL) {
+ finddn = strdup(slapi_entry_get_dn(cur->lde_e));
+ (void) slapi_dn_normalize(finddn);
+
+
+ /*Test to see if we got the entry matching the dn*/
+ if (strcasecmp(targetdn, finddn) == 0)
+ {
+ found_it = 1;
+ free ((void *)finddn);
+ db->ldif_hits++;
+ break;
+ }
+
+ /*Udpate the pointers*/
+ *prev = cur;
+ cur = cur->next;
+ free ((void *)finddn);
+
+ }
+
+ free ((void *)targetdn);
+
+
+ /*
+ * If we didn't find a matching entry, we should
+ * return, and let the caller handle this (possible) error
+ */
+ if (!found_it){
+ return(NULL);
+ }
+
+ /*
+ * If the first entry matches, we have to set prev to null,
+ * so the caller knows.
+ */
+ if (*prev == cur){
+ *prev = NULL;
+ }
+
+ return( cur );
+}
+
+/*
+ * Function: apply_mods
+ *
+ * Returns: LDAP_SUCCESS if success
+ *
+ * Description: Applies the modifications specified in mods to e.
+ */
+int
+apply_mods( Slapi_Entry *e, LDAPMod **mods )
+{
+ int err, i, j;
+
+ LDAPDebug( LDAP_DEBUG_TRACE, "=> apply_mods\n", 0, 0, 0 );
+
+ err = LDAP_SUCCESS;
+ for ( j = 0; mods[j] != NULL; j++ ) {
+ switch ( mods[j]->mod_op & ~LDAP_MOD_BVALUES ) {
+ case LDAP_MOD_ADD:
+ LDAPDebug( LDAP_DEBUG_ARGS, " add: %s\n",
+ mods[j]->mod_type, 0, 0 );
+ err = slapi_entry_add_values( e, mods[j]->mod_type,
+ mods[j]->mod_bvalues );
+ break;
+
+ case LDAP_MOD_DELETE:
+ LDAPDebug( LDAP_DEBUG_ARGS, " delete: %s\n",
+ mods[j]->mod_type, 0, 0 );
+ err = slapi_entry_delete_values( e, mods[j]->mod_type,
+ mods[j]->mod_bvalues );
+ break;
+
+ case LDAP_MOD_REPLACE:
+ LDAPDebug( LDAP_DEBUG_ARGS, " replace: %s\n",
+ mods[j]->mod_type, 0, 0 );
+ err = entry_replace_values( e, mods[j]->mod_type,
+ mods[j]->mod_bvalues );
+ break;
+ }
+ for ( i = 0; mods[j]->mod_bvalues != NULL &&
+ mods[j]->mod_bvalues[i] != NULL; i++ ) {
+ LDAPDebug( LDAP_DEBUG_ARGS, " %s: %s\n",
+ mods[j]->mod_type, mods[j]->mod_bvalues[i]->bv_val,
+ 0 );
+ }
+ LDAPDebug( LDAP_DEBUG_ARGS, " -\n", 0, 0, 0 );
+
+ if ( err != LDAP_SUCCESS ) {
+ break;
+ }
+ }
+
+ LDAPDebug( LDAP_DEBUG_TRACE, "<= apply_mods %d\n", err, 0, 0 );
+ return( err );
+}
diff --git a/ldap/servers/slapd/back-ldif/modrdn.c b/ldap/servers/slapd/back-ldif/modrdn.c
new file mode 100644
index 00000000..d2dfcdb8
--- /dev/null
+++ b/ldap/servers/slapd/back-ldif/modrdn.c
@@ -0,0 +1,282 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+/*
+ * File: modrdn.c
+ *
+ * Functions:
+ *
+ * ldif_back_modrdn() - ldap ldif back-end modrdn routine
+ * rdn2typval() - rdn to typval converter
+ * ldif_add_mod() - Adds a modification to be performed.
+ *
+ */
+
+#include "back-ldif.h"
+int rdn2typval(char *, char **, struct berval *);
+void ldif_add_mod( LDAPMod ***, int, char *, struct berval ** );
+
+/*
+ * Function: ldif_back_modrdn
+ *
+ * Returns: returns 0 if good, -1 else.
+ *
+ * Description: For changetype: modrdn, this modifies the rdn of the entry
+ */
+int
+ldif_back_modrdn( Slapi_PBlock *pb )
+{
+ LDIF *db; /*ldif backend database*/
+ ldif_Entry *prev, *tprev, *entry, *entry2, *test;
+ char *pdn, *newdn; /*Used for dn manipulation*/
+ char *dn, *newrdn, *type; /*Used for dn manipulation*/
+ int i; /*A counter*/
+ char **rdns, **dns; /*Used for dn manipulation*/
+ int deleteoldrdn; /*Flag from user to delete old rdn*/
+ struct berval bv;
+ struct berval *bvps[2];
+ LDAPMod **mods; /*Holds the list of modifications*/
+ int rc;
+
+ LDAPDebug( LDAP_DEBUG_TRACE, "=> ldif_back_modrdn\n", 0, 0, 0 );
+
+ prev = NULL;
+
+ /*Get the information from the front end, including the database*/
+ if (slapi_pblock_get( pb, SLAPI_PLUGIN_PRIVATE, &db )< 0 ||
+ slapi_pblock_get( pb, SLAPI_MODRDN_TARGET, &dn ) < 0 ||
+ slapi_pblock_get( pb, SLAPI_MODRDN_NEWRDN, &newrdn ) < 0 ||
+ slapi_pblock_get( pb, SLAPI_MODRDN_DELOLDRDN, &deleteoldrdn ) <0){
+ slapi_send_ldap_result( pb, LDAP_OPERATIONS_ERROR, NULL, NULL, 0, NULL );
+ return(-1);
+
+ }
+
+ /*Lock the database*/
+ PR_Lock( db->ldif_lock );
+
+ /*
+ * Find the entry we are about to modify
+ * prev will point to the previous element in the list,
+ * NULL if there is no previous element
+ */
+ if ( (entry = (ldif_Entry *)ldif_find_entry( pb, db, dn, &prev)) == NULL ) {
+ slapi_send_ldap_result( pb, LDAP_NO_SUCH_OBJECT, NULL, NULL, 0, NULL );
+ PR_Unlock( db->ldif_lock );
+ return( -1 );
+ }
+
+ /*Make sure that we are trying to modify the rdn of a leaf.*/
+ if ( has_children( db, entry ) ) {
+ slapi_send_ldap_result( pb, LDAP_NOT_ALLOWED_ON_NONLEAF, NULL, NULL, 0, NULL );
+ PR_Unlock( db->ldif_lock );
+ return( -1 );
+ }
+
+
+ /* Create a copy of the entry and apply the changes to it */
+ if ( (entry2 = (ldif_Entry *)ldifentry_dup( entry )) == NULL ) {
+ slapi_send_ldap_result( pb, LDAP_OPERATIONS_ERROR, NULL, NULL, 0, NULL );
+ PR_Unlock( db->ldif_lock );
+ return( -1 );
+ }
+
+ /*Check the access*/
+ rc= slapi_access_allowed( pb, entry2->lde_e, NULL, NULL, SLAPI_ACL_WRITE );
+ if ( rc!=LDAP_SUCCESS ) {
+ slapi_send_ldap_result( pb, rc, NULL, NULL, 0, NULL );
+ ldifentry_free( entry2 );
+ PR_Unlock( db->ldif_lock );
+ return( -1 );
+ }
+
+ /* Construct the new dn */
+ if ( (pdn = slapi_dn_beparent( pb, dn )) != NULL ) {
+
+ /* parent + rdn + separator(s) + null */
+ newdn = (char *) malloc( strlen( pdn ) + strlen( newrdn ) + 3 );
+ if (newdn == NULL){
+ LDAPDebug( LDAP_DEBUG_ANY,"malloc failed", 0, 0, 0 );
+ exit(1);
+ }
+
+ strcpy( newdn, newrdn );
+ strcat( newdn, ", " );
+ strcat( newdn, pdn );
+ } else {
+ newdn = strdup( newrdn );
+ }
+ free( pdn );
+
+ /*Normalize the newdn, that is, squeeze out all unnecessary spaces*/
+ (void) slapi_dn_normalize( newdn );
+
+
+ /* Add the new dn to our working copy of the entry */
+ slapi_entry_set_dn( entry2->lde_e, newdn );
+
+
+ /* See if an entry with the new name already exists */
+ if ( (test = (ldif_Entry *)ldif_find_entry( pb, db, newdn, &tprev )) != NULL ) {
+ slapi_send_ldap_result( pb, LDAP_ALREADY_EXISTS, NULL, NULL, 0, NULL );
+
+ goto error_return;
+ }
+
+
+ /*
+ * Delete old rdn values from the entry if deleteoldrdn is set.
+ * Add new rdn values to the entry.
+ */
+ mods = NULL;
+ bvps[0] = &bv;
+ bvps[1] = NULL;
+ if ( (dns = ldap_explode_dn( dn, 0 )) != NULL ) {
+ if ( (rdns = ldap_explode_rdn( dns[0], 0 )) != NULL ) {
+ for ( i = 0; rdns[i] != NULL; i++ ) {
+
+ /* Delete from entry attributes */
+ if ( deleteoldrdn && rdn2typval( rdns[i], &type, &bv ) == 0 ) {
+ ldif_add_mod( &mods, LDAP_MOD_DELETE, type, bvps );
+ }
+ }
+ ldap_value_free( rdns );
+ }
+ ldap_value_free( dns );
+ }
+ if ( dns == NULL || rdns == NULL ) {
+ slapi_send_ldap_result( pb, LDAP_OPERATIONS_ERROR, NULL, NULL, 0, NULL );
+ goto error_return;
+ }
+ /* Add new rdn values to the entry */
+ if ( (rdns = ldap_explode_rdn( newrdn, 0 )) != NULL ) {
+ for ( i = 0; rdns[i] != NULL; i++ ) {
+ /* Add to entry */
+ if ( rdn2typval( rdns[i], &type, &bv ) == 0 ) {
+ ldif_add_mod( &mods, LDAP_MOD_ADD, type, bvps );
+ }
+ }
+ ldap_value_free( rdns );
+ } else {
+ slapi_send_ldap_result( pb, LDAP_OPERATIONS_ERROR, NULL, NULL, 0, NULL );
+ goto error_return;
+ }
+ bv.bv_val = newdn;
+ bv.bv_len = strlen( newdn );
+ ldif_add_mod( &mods, LDAP_MOD_REPLACE, "entrydn", bvps );
+
+ /* Check for abandon */
+ if ( slapi_op_abandoned( pb ) ) {
+ goto error_return;
+ }
+
+ /* Apply the mods we built above to the copy of the entry */
+ if ( apply_mods( entry2->lde_e, mods ) != 0 ) {
+ slapi_send_ldap_result( pb, LDAP_OPERATIONS_ERROR, NULL, NULL, 0, NULL );
+
+ goto error_return;
+ }
+
+ /* Update the database and the disk */
+ if ( update_db(pb, db, entry2, prev, LDIF_DB_REPLACE) != 0) {
+ slapi_send_ldap_result( pb, LDAP_OPERATIONS_ERROR, NULL, NULL, 0, NULL );
+
+ goto error_return;
+ }
+
+ /*Unlock the database, and tell the user the good news*/
+ PR_Unlock( db->ldif_lock );
+ slapi_send_ldap_result( pb, LDAP_SUCCESS, NULL, NULL, 0, NULL );
+ LDAPDebug( LDAP_DEBUG_TRACE, "<= ldif_back_modrdn\n", 0, 0, 0 );
+ return( 0 );
+
+error_return:;
+
+ /* Result already sent above - just free stuff */
+ PR_Unlock( db->ldif_lock );
+ ldifentry_free( entry2 );
+
+ return( -1 );
+}
+
+/*
+ * Function: rdn2typval
+ *
+ * Returns: returns 0 if good, -1 else.
+ *
+ * Description: converts an rdn to a typeval
+ */
+int
+rdn2typval(char *rdn, char **type, struct berval *bv)
+{
+ char *s;
+
+ if ( (s = strchr( rdn, '=' )) == NULL ) {
+ return( -1 );
+ }
+ *s++ = '\0';
+
+ *type = rdn;
+ bv->bv_val = s;
+ bv->bv_len = strlen( s );
+
+ return( 0 );
+}
+
+/*
+ * Function: ldif_add_mod
+ *
+ * Returns: void
+ *
+ * Description: Adds a modification (add, delete, etc) to the list
+ * of modifications that will eventually be made to some entry
+ */
+void
+ldif_add_mod( LDAPMod ***modlist, int modtype, char *type, struct berval **bvps )
+{
+ int i;
+
+ for ( i = 0; modlist[i] != NULL; i++ ) {
+ ; /* NULL */
+ }
+
+ *modlist = (LDAPMod **) realloc( (char *) *modlist,
+ (i + 2) * sizeof(LDAPMod *) );
+
+ if (*modlist == NULL){
+ LDAPDebug( LDAP_DEBUG_ANY, "realloc failed", 0, 0, 0 );
+ exit(1);
+ }
+ (*modlist)[i] = (LDAPMod *) malloc( sizeof(LDAPMod) );
+
+ if ((*modlist)[i] == NULL){
+ LDAPDebug( LDAP_DEBUG_ANY,"malloc failed", 0, 0, 0 );
+ exit(1);
+ }
+
+ (*modlist)[i]->mod_type = (char *) strdup( type );
+ if ((*modlist)[i]->mod_type == NULL){
+ LDAPDebug( LDAP_DEBUG_ANY,"strdup failed", 0, 0, 0 );
+ exit(1);
+ }
+
+
+ (*modlist)[i]->mod_op = modtype;
+ (*modlist)[i]->mod_bvalues = (struct berval **) malloc(2*sizeof(struct berval *));
+ if ((*modlist)[i]->mod_bvalues == NULL){
+ LDAPDebug( LDAP_DEBUG_ANY,"malloc failed",0, 0, 0 );
+ exit(1);
+ }
+ (*modlist)[i]->mod_bvalues[0] = ber_bvdup( bvps[0] );
+ (*modlist)[i]->mod_bvalues[1] = NULL;
+ (*modlist)[i+1] = NULL;
+}
+
+
+
+
+
+
+
diff --git a/ldap/servers/slapd/back-ldif/monitor.c b/ldap/servers/slapd/back-ldif/monitor.c
new file mode 100644
index 00000000..b4b721b0
--- /dev/null
+++ b/ldap/servers/slapd/back-ldif/monitor.c
@@ -0,0 +1,124 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+/*
+ * File: monitor.c
+ *
+ * Functions:
+ *
+ * ldif_back_monitor_info() - ldap ldif back-end initialize routine
+ *
+ * get_monitordn() - gets the monitor dn for this backend
+ *
+ */
+
+#include "back-ldif.h"
+
+extern char Versionstr[];
+
+
+/*
+ * Function: ldif_back_monitor_info
+ *
+ * Returns: returns 1
+ *
+ * Description: This function wraps up backend specific monitor information
+ * and returns it to the client as an entry. This function
+ * is usually called by ldif_back_search upon receipt of
+ * the monitor dn for this backend.
+ */
+int
+ldif_back_monitor_info( Slapi_PBlock *pb, LDIF *db)
+{
+ Slapi_Entry *e; /*Entry*/
+ char buf[BUFSIZ]; /*Buffer for getting the attrs*/
+ struct berval val; /*More attribute storage*/
+ struct berval *vals[2]; /*Even more*/
+ char *type; /*Database name (type) */
+
+ vals[0] = &val;
+ vals[1] = NULL;
+
+ /*Alloc the entry and set the monitordn*/
+ e = slapi_entry_alloc();
+ slapi_entry_set_dn(e, (char *) get_monitordn(pb));
+
+ /* Get the database name (be_type) */
+ slapi_pblock_get( pb, SLAPI_BE_TYPE, &type);
+ sprintf( buf, "%s", type );
+ val.bv_val = buf;
+ val.bv_len = strlen( buf );
+ slapi_entry_attr_merge( e, "database", vals );
+
+ /*Lock the database*/
+ PR_Lock( db->ldif_lock );
+
+ /*Get the number of database hits */
+ sprintf( buf, "%ld", db->ldif_hits);
+ val.bv_val = buf;
+ val.bv_len = strlen( buf );
+ slapi_entry_attr_merge( e, "entrycachehits", vals );
+
+ /*Get the number of database tries */
+ sprintf( buf, "%ld", db->ldif_tries);
+ val.bv_val = buf;
+ val.bv_len = strlen( buf );
+ slapi_entry_attr_merge( e, "entrycachetries", vals );
+
+ /*Get the current size of the entrycache (db) */
+ sprintf( buf, "%ld", db->ldif_n);
+ val.bv_val = buf;
+ val.bv_len = strlen( buf );
+ slapi_entry_attr_merge( e, "currententrycachesize", vals );
+
+
+ /*
+ * Get the maximum size of the entrycache (db)
+ * in this database, there is no max, so return the current size
+ */
+ val.bv_val = buf;
+ val.bv_len = strlen( buf );
+ slapi_entry_attr_merge( e, "maxentrycachesize", vals );
+
+ /* Release the lock*/
+ PR_Unlock( db->ldif_lock );
+
+ /*Send the results back to the client*/
+ slapi_send_ldap_search_entry( pb, e, NULL, NULL, 0 );
+ slapi_send_ldap_result( pb, LDAP_SUCCESS, NULL, NULL, 1, NULL );
+
+ slapi_entry_free( e );
+
+ return(1);
+
+
+}
+
+
+/*
+ * Function: get_monitordn
+ *
+ * Returns: returns ptr to string if success, NULL else
+ *
+ * Description: get_monitordn takes a pblock and extracts the
+ * monitor dn of this backend. The monitordn is a special
+ * signal to the backend to return backend specific monitor
+ * information (usually called by back_ldif_search()).
+ */
+char *
+get_monitordn(Slapi_PBlock *pb )
+{
+ char *mdn;
+
+ slapi_pblock_get( pb, SLAPI_BE_MONITORDN, &mdn );
+
+ if (mdn == NULL) {
+ return(NULL);
+
+ }
+
+ return(strdup(mdn));
+
+}
diff --git a/ldap/servers/slapd/back-ldif/search.c b/ldap/servers/slapd/back-ldif/search.c
new file mode 100644
index 00000000..1f9adbfb
--- /dev/null
+++ b/ldap/servers/slapd/back-ldif/search.c
@@ -0,0 +1,197 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+/*
+ * File: search.c
+ *
+ * Functions:
+ *
+ * ldif_back_search() - ldif backend search function
+ *
+ */
+
+#include "back-ldif.h"
+
+/*
+ * Function: ldif_back_search
+ *
+ * Returns: returns 0 if good, -1 else.
+ *
+ * Description: Searches the database for entries satisfying the
+ * user's criteria
+ */
+int
+ldif_back_search( Slapi_PBlock *pb )
+{
+ LDIF *db; /*The database*/
+ char *base; /*Base of the search*/
+ int scope; /*Scope of the search*/
+ int deref; /*Should we dereference aliases?*/
+ int slimit; /*Size limit of the search*/
+ int tlimit; /*Time limit of the search*/
+ Slapi_Filter *filter; /*The filter*/
+ time_t dummy=0; /*Used for time()*/
+ char **attrs; /*Attributes*/
+ int attrsonly; /*Should we just return the attributes found?*/
+ time_t optime; /*Time the operation started*/
+ int nentries; /*Number of entries found thus far*/
+ ldif_Entry *cur; /*Used for traversing the list of entries*/
+ int hitflag=0; /*Used to test if we found the entry in the db*/
+ char *freeme; /*Tmp storage for monitordn*/
+ time_t currtime; /*The current time*/
+
+ LDAPDebug( LDAP_DEBUG_TRACE, "=> ldif_back_search\n", 0, 0, 0 );
+
+ /*
+ * Get private information created in the init routine.
+ * Also get the parameters of the search operation. These come
+ * more or less directly from the client.
+ */
+ if (slapi_pblock_get( pb, SLAPI_PLUGIN_PRIVATE, &db ) < 0 ||
+ slapi_pblock_get( pb, SLAPI_SEARCH_TARGET, &base ) < 0 ||
+ slapi_pblock_get( pb, SLAPI_SEARCH_SCOPE, &scope ) < 0 ||
+ slapi_pblock_get( pb, SLAPI_SEARCH_DEREF, &deref ) < 0 ||
+ slapi_pblock_get( pb, SLAPI_SEARCH_SIZELIMIT, &slimit ) < 0 ||
+ slapi_pblock_get( pb, SLAPI_SEARCH_TIMELIMIT, &tlimit ) < 0 ||
+ slapi_pblock_get( pb, SLAPI_SEARCH_FILTER, &filter ) < 0 ||
+ slapi_pblock_get( pb, SLAPI_SEARCH_ATTRS, &attrs ) < 0 ||
+ slapi_pblock_get( pb, SLAPI_SEARCH_ATTRSONLY, &attrsonly ) <0 ||
+ slapi_pblock_get( pb, SLAPI_OPINITIATED_TIME, &optime ) < 0){
+ slapi_send_ldap_result( pb, LDAP_OPERATIONS_ERROR, NULL, NULL, 0, NULL );
+ return(-1);
+ }
+
+
+ /*
+ * If we get a search request for the backend monitor dn,
+ * call ldif_back_monitor_info(), which packages up the
+ * backend database analysis info and sends it back to the
+ * client
+ */
+ if ( scope == LDAP_SCOPE_BASE ) {
+
+ /*Get the backend's monitor dn*/
+ freeme = (char *) get_monitordn(pb);
+
+ if (freeme != NULL){
+
+ /*
+ * Compare the monitor dn with the base,
+ * if they match, call monitor_info, which
+ * will return all the relevant info to the client
+ */
+ if ( strcasecmp( base, freeme) == 0 ) {
+ ldif_back_monitor_info( pb, db );
+ free ((void *) freeme);
+ return(-1);
+ }
+ free ((void *) freeme);
+ }
+ }
+
+
+ /*
+ * First we lock the whole database (clumsy, inefficient and
+ * inelegant, but simple)
+ */
+ PR_Lock( db->ldif_lock );
+
+ /*Increase the number of accesses*/
+ db->ldif_tries++;
+
+ /*
+ * Look through each entry in the ldif file and see if it matches
+ * the filter and scope of the search. Do this by calling the
+ * slapi_filter_test() routine.
+ */
+ nentries = 0;
+ for (cur=db->ldif_entries; cur != NULL; cur = cur->next ) {
+
+ /*Make sure we're not exceeding our time limit...*/
+ currtime = time(&dummy);
+ if ((tlimit > 0) && ((currtime - optime) > tlimit)){
+ slapi_send_ldap_result( pb, LDAP_TIMELIMIT_EXCEEDED, NULL, NULL, nentries, NULL);
+
+ /*We "hit" the cache*/
+ if (hitflag)
+ {
+ db->ldif_hits++;
+ }
+
+ PR_Unlock( db->ldif_lock );
+ return(-1);
+ }
+
+ /*...or that we haven't been abandoned*/
+ if ( slapi_op_abandoned( pb ) ) {
+
+ /*We "hit" the cache*/
+ if (hitflag)
+ {
+ db->ldif_hits++;
+ }
+
+ PR_Unlock( db->ldif_lock );
+ return( -1 );
+ }
+
+ /*Test for exceedence of size limit*/
+ if ((slimit > -1) && (nentries >= slimit)){
+ slapi_send_ldap_result( pb, LDAP_SIZELIMIT_EXCEEDED, NULL, NULL, nentries, NULL);
+
+ /*We hit the "cache"*/
+ if (hitflag)
+ {
+ db->ldif_hits++;
+ }
+ PR_Unlock( db->ldif_lock );
+ return(-1);
+ }
+
+
+
+ /*Test if this entry matches the filter*/
+ if ( slapi_vattr_filter_test( pb, cur->lde_e, filter, 1 /* verify access */ ) == 0 ) {
+
+ /* Entry matches - send it */
+ hitflag = 1;
+
+ switch ( slapi_send_ldap_search_entry( pb, cur->lde_e, NULL, attrs,
+ attrsonly ) ) {
+ case 0: /* Entry sent ok */
+ nentries++;
+ break;
+ case 1: /* Entry not sent - because of acl, etc. */
+ break;
+ case -1:/* Connection closed */
+ /* Clean up and return */
+
+ /*We "hit" the cache*/
+ if (hitflag)
+ {
+ db->ldif_hits++;
+ }
+ PR_Unlock( db->ldif_lock );
+ return( -1 );
+ }
+
+
+ }
+ }
+
+ /*If we succeeded, we should update the ldif_hits entry of db*/
+ if (hitflag)
+ {
+ db->ldif_hits++;
+ }
+
+
+ /* Search is done, send LDAP_SUCCESS */
+ slapi_send_ldap_result( pb, LDAP_SUCCESS, NULL, NULL, nentries, NULL );
+ PR_Unlock( db->ldif_lock );
+ LDAPDebug( LDAP_DEBUG_TRACE, "<= ldif_back_search\n", 0, 0, 0 );
+ return( -1 );
+
+}
diff --git a/ldap/servers/slapd/back-ldif/start.c b/ldap/servers/slapd/back-ldif/start.c
new file mode 100644
index 00000000..4e22cb08
--- /dev/null
+++ b/ldap/servers/slapd/back-ldif/start.c
@@ -0,0 +1,31 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+/*
+ * File: start.c
+ *
+ * Functions:
+ *
+ * ldif_back_start() - ldap ldif back-end start routine
+ *
+ */
+#include "back-ldif.h"
+
+/*
+ * Function: ldif_back_start
+ *
+ * Returns: returns 0
+ *
+ * Description: After the config file is read, the backend start function is called.
+ * This allows the backend writer to start any threads or perform any
+ * operations that need to be done after the config file has been read in.
+ * The ldif backend requires no such operations to be performed.
+ *
+ */
+int
+ldif_back_start( Slapi_PBlock *pb )
+{
+ return( 0 );
+}
diff --git a/ldap/servers/slapd/back-ldif/unbind.c b/ldap/servers/slapd/back-ldif/unbind.c
new file mode 100644
index 00000000..77c45adb
--- /dev/null
+++ b/ldap/servers/slapd/back-ldif/unbind.c
@@ -0,0 +1,27 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+/*
+ * File: unbind.c
+ *
+ * Functions:
+ *
+ * ldif_back_unbind() - ldap ldif back-end unbind routine
+ *
+ */
+#include "back-ldif.h"
+
+/*
+ * Function: ldif_back_unbind
+ *
+ * Returns: returns 0
+ *
+ * Description: performs an ldap unbind.
+ */
+int
+ldif_back_unbind( Slapi_PBlock *pb )
+{
+ return( 0 );
+}
diff --git a/ldap/servers/slapd/backend.c b/ldap/servers/slapd/backend.c
new file mode 100644
index 00000000..f4ffcc87
--- /dev/null
+++ b/ldap/servers/slapd/backend.c
@@ -0,0 +1,505 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+/* backend.c - Slapi_Backend methods */
+
+#include "slap.h"
+
+void
+be_init( Slapi_Backend *be, const char *type, const char *name, int isprivate, int logchanges, int sizelimit, int timelimit )
+{
+ char text[128];
+ slapdFrontendConfig_t *fecfg;
+ be->be_suffix = NULL;
+ be->be_suffixlock= PR_NewLock();
+ be->be_suffixcount= 0;
+ /* e.g. dn: cn=config,cn=NetscapeRoot,cn=ldbm database,cn=plugins,cn=config */
+ sprintf(text, "cn=%s,cn=%s,cn=plugins,cn=config", name, type);
+ be->be_basedn= slapi_ch_strdup(slapi_dn_normalize(text));
+ sprintf(text, "cn=config,cn=%s,cn=%s,cn=plugins,cn=config", name, type);
+ be->be_configdn= slapi_ch_strdup(slapi_dn_normalize(text));
+ sprintf(text, "cn=monitor,cn=%s,cn=%s,cn=plugins,cn=config", name, type);
+ be->be_monitordn= slapi_ch_strdup(slapi_dn_normalize(text));
+ be->be_sizelimit = sizelimit;
+ be->be_timelimit = timelimit;
+ /* maximum group nesting level before giving up */
+ be->be_maxnestlevel = SLAPD_DEFAULT_GROUPNESTLEVEL;
+ be->be_noacl= 0;
+ be->be_flags=0;
+ if (( fecfg = getFrontendConfig()) != NULL )
+ {
+ if ( fecfg->backendconfig != NULL && fecfg->backendconfig[ 0 ] != NULL )
+ {
+ be->be_backendconfig = slapi_ch_strdup( fecfg->backendconfig[0] );
+ }
+ else
+ {
+ be->be_backendconfig= NULL;
+ }
+ be->be_readonly = fecfg->readonly;
+ }
+ else
+ {
+ be->be_readonly= 0;
+ be->be_backendconfig= NULL;
+ }
+ be->be_lastmod = LDAP_UNDEFINED;
+ be->be_type = slapi_ch_strdup(type);
+ be->be_include = NULL;
+ be->be_private = isprivate;
+ be->be_logchanges = logchanges;
+ be->be_database = NULL;
+ be->be_writeconfig = NULL;
+ be->be_delete_on_exit = 0;
+ be->be_state = BE_STATE_STOPPED;
+ be->be_state_lock = PR_NewLock();
+ be->be_name = slapi_ch_strdup(name);
+ be->be_mapped = 0;
+}
+
+void
+be_done(Slapi_Backend *be)
+{
+ int i;
+
+ for(i=0;i<be->be_suffixcount;i++)
+ {
+ slapi_sdn_free(&be->be_suffix[i]);
+ }
+ slapi_ch_free((void**)&be->be_suffix);
+ PR_DestroyLock(be->be_suffixlock);
+ slapi_ch_free((void **)&be->be_basedn);
+ slapi_ch_free((void **)&be->be_configdn);
+ slapi_ch_free((void **)&be->be_monitordn);
+ slapi_ch_free((void **)&be->be_type);
+ slapi_ch_free((void **)&be->be_backendconfig);
+ /* JCM char **be_include; ??? */
+ slapi_ch_free((void **)&be->be_name);
+ PR_DestroyLock(be->be_state_lock);
+}
+
+void
+slapi_be_delete_onexit (Slapi_Backend *be)
+{
+ be->be_delete_on_exit = 1;
+}
+
+void
+slapi_be_set_readonly(Slapi_Backend *be, int readonly)
+{
+ be->be_readonly = readonly;
+}
+
+int
+slapi_be_get_readonly(Slapi_Backend *be)
+{
+ return be->be_readonly;
+}
+
+/*
+ * Check if suffix, exactly matches a registered
+ * suffix of this backend.
+ */
+int
+slapi_be_issuffix( const Slapi_Backend *be, const Slapi_DN *suffix )
+{
+ int r= 0;
+ /* this backend is no longer valid */
+ if (be->be_state != BE_STATE_DELETED)
+ {
+ int i;
+ PR_Lock(be->be_suffixlock);
+ for ( i = 0; be->be_suffix != NULL && i<be->be_suffixcount; i++ )
+ {
+ if ( slapi_sdn_compare( be->be_suffix[i], suffix ) == 0)
+ {
+ r= 1;
+ break;
+ }
+ }
+ PR_Unlock(be->be_suffixlock);
+ }
+ return r;
+}
+
+void
+be_addsuffix(Slapi_Backend *be,const Slapi_DN *suffix)
+{
+ if (be->be_state != BE_STATE_DELETED)
+ {
+ PR_Lock(be->be_suffixlock);
+ if(be->be_suffix==NULL)
+ {
+ be->be_suffix= (Slapi_DN **)slapi_ch_malloc(sizeof(Slapi_DN *));
+ }
+ else
+ {
+ be->be_suffix= (Slapi_DN **)slapi_ch_realloc((char*)be->be_suffix,(be->be_suffixcount+1)*sizeof(Slapi_DN *));
+ }
+ be->be_suffix[be->be_suffixcount]= slapi_sdn_dup(suffix);
+ be->be_suffixcount++;
+ PR_Unlock(be->be_suffixlock);
+ }
+}
+
+void slapi_be_addsuffix(Slapi_Backend *be,const Slapi_DN *suffix)
+{
+ be_addsuffix(be,suffix);
+}
+
+/*
+ * The caller may use the returned pointer without holding the
+ * be_suffixlock since we never remove suffixes from the array.
+ * The Slapi_DN pointer will always be valid even though the array
+ * itself may be changing due to the addition of a suffix.
+ */
+const Slapi_DN *
+slapi_be_getsuffix(Slapi_Backend *be,int n)
+{
+ Slapi_DN *sdn = NULL;
+
+ if(NULL == be)
+ return NULL;
+
+ if(be->be_state != BE_STATE_DELETED) {
+ PR_Lock(be->be_suffixlock);
+ if (be->be_suffix !=NULL && n<be->be_suffixcount) {
+ sdn = be->be_suffix[n];
+ }
+ PR_Unlock(be->be_suffixlock);
+ }
+ return sdn;
+}
+
+const char *
+slapi_be_gettype(Slapi_Backend *be)
+{
+ const char *r= NULL;
+ if (be->be_state != BE_STATE_DELETED)
+ {
+ r= be->be_type;
+ }
+ return r;
+}
+
+Slapi_DN *
+be_getconfigdn(Slapi_Backend *be, Slapi_DN *dn)
+{
+ if (be->be_state == BE_STATE_DELETED)
+ {
+ slapi_sdn_set_ndn_byref(dn,NULL);
+ }
+ else
+ {
+ slapi_sdn_set_ndn_byref(dn,be->be_configdn);
+ }
+ return dn;
+}
+
+Slapi_DN *
+be_getmonitordn(Slapi_Backend *be, Slapi_DN *dn)
+{
+ if (be->be_state == BE_STATE_DELETED)
+ {
+ slapi_sdn_set_ndn_byref(dn,NULL);
+ }
+ else
+ {
+ slapi_sdn_set_ndn_byref(dn,be->be_monitordn);
+ }
+ return dn;
+}
+
+int
+be_writeconfig ( Slapi_Backend *be )
+{
+ Slapi_PBlock *newpb;
+
+ if (be->be_state == BE_STATE_DELETED || be->be_private ||
+ (be->be_writeconfig == NULL) ) {
+ return -1;
+ }
+ else {
+ newpb = slapi_pblock_new();
+ slapi_pblock_set ( newpb, SLAPI_PLUGIN, (void *) be->be_database );
+ slapi_pblock_set ( newpb, SLAPI_BACKEND, (void *) be );
+ (be->be_writeconfig)(newpb);
+ slapi_pblock_destroy ( newpb );
+ return 1;
+ }
+}
+
+/*
+ * Find out if changes made to entries in this backend
+ * should be recorded in the changelog.
+ */
+int
+slapi_be_logchanges(Slapi_Backend *be)
+{
+ if (be->be_state == BE_STATE_DELETED)
+ return 0;
+
+ return be->be_logchanges;
+}
+
+int
+slapi_be_private ( Slapi_Backend *be )
+{
+ if ( be!=NULL )
+ {
+ return (be->be_private);
+ }
+
+ return 0;
+}
+
+void *
+slapi_be_get_instance_info(Slapi_Backend * be)
+{
+ PR_ASSERT(NULL != be);
+ return be->be_instance_info;
+}
+
+void
+slapi_be_set_instance_info(Slapi_Backend * be, void * data)
+{
+ PR_ASSERT(NULL != be);
+ be->be_instance_info=data;
+}
+
+int
+slapi_be_getentrypoint(Slapi_Backend *be, int entrypoint, void **ret_fnptr, Slapi_PBlock *pb)
+{
+ PR_ASSERT(NULL != be);
+
+ /* this is something needed for most of the entry points */
+ if (pb)
+ {
+ slapi_pblock_set( pb, SLAPI_PLUGIN, be->be_database );
+ slapi_pblock_set( pb, SLAPI_BACKEND, be );
+ }
+
+ switch (entrypoint) {
+ case SLAPI_PLUGIN_DB_BIND_FN:
+ *ret_fnptr = (void*)be->be_bind;
+ break;
+ case SLAPI_PLUGIN_DB_UNBIND_FN:
+ *ret_fnptr = (void*)be->be_unbind;
+ break;
+ case SLAPI_PLUGIN_DB_SEARCH_FN:
+ *ret_fnptr = (void*)be->be_search;
+ break;
+ case SLAPI_PLUGIN_DB_COMPARE_FN:
+ *ret_fnptr = (void*)be->be_compare;
+ break;
+ case SLAPI_PLUGIN_DB_MODIFY_FN:
+ *ret_fnptr = (void*)be->be_modify;
+ break;
+ case SLAPI_PLUGIN_DB_MODRDN_FN:
+ *ret_fnptr = (void*)be->be_modrdn;
+ break;
+ case SLAPI_PLUGIN_DB_ADD_FN:
+ *ret_fnptr = (void*)be->be_add;
+ break;
+ case SLAPI_PLUGIN_DB_DELETE_FN:
+ *ret_fnptr = (void*)be->be_delete;
+ break;
+ case SLAPI_PLUGIN_DB_ABANDON_FN:
+ *ret_fnptr = (void*)be->be_abandon;
+ break;
+ case SLAPI_PLUGIN_DB_CONFIG_FN:
+ *ret_fnptr = (void*)be->be_config;
+ break;
+ case SLAPI_PLUGIN_CLOSE_FN:
+ *ret_fnptr = (void*)be->be_close;
+ break;
+ case SLAPI_PLUGIN_DB_FLUSH_FN:
+ *ret_fnptr = (void*)be->be_flush;
+ break;
+ case SLAPI_PLUGIN_START_FN:
+ *ret_fnptr = (void*)be->be_start;
+ break;
+ case SLAPI_PLUGIN_DB_RESULT_FN:
+ *ret_fnptr = (void*)be->be_result;
+ break;
+ case SLAPI_PLUGIN_DB_LDIF2DB_FN:
+ *ret_fnptr = (void*)be->be_ldif2db;
+ break;
+ case SLAPI_PLUGIN_DB_DB2LDIF_FN:
+ *ret_fnptr = (void*)be->be_db2ldif;
+ break;
+ case SLAPI_PLUGIN_DB_ARCHIVE2DB_FN:
+ *ret_fnptr = (void*)be->be_archive2db;
+ break;
+ case SLAPI_PLUGIN_DB_DB2ARCHIVE_FN:
+ *ret_fnptr = (void*)be->be_db2archive;
+ break;
+ case SLAPI_PLUGIN_DB_NEXT_SEARCH_ENTRY_FN:
+ *ret_fnptr = (void*)be->be_next_search_entry;
+ break;
+ case SLAPI_PLUGIN_DB_NEXT_SEARCH_ENTRY_EXT_FN:
+ *ret_fnptr = (void*)be->be_next_search_entry_ext;
+ break;
+ case SLAPI_PLUGIN_DB_ENTRY_RELEASE_FN:
+ *ret_fnptr = (void*)be->be_entry_release;
+ break;
+ case SLAPI_PLUGIN_DB_SIZE_FN:
+ *ret_fnptr = (void*)be->be_dbsize;
+ break;
+ case SLAPI_PLUGIN_DB_TEST_FN:
+ *ret_fnptr = (void*)be->be_dbtest;
+ break;
+ case SLAPI_PLUGIN_DB_RMDB_FN:
+ *ret_fnptr = (void*)be->be_rmdb;
+ break;
+ case SLAPI_PLUGIN_DB_INIT_INSTANCE_FN:
+ *ret_fnptr = (void*)be->be_init_instance;
+ break;
+ case SLAPI_PLUGIN_DB_SEQ_FN:
+ *ret_fnptr = (void*)be->be_seq;
+ break;
+ case SLAPI_PLUGIN_DB_DB2INDEX_FN:
+ *ret_fnptr = (void*)be->be_db2index;
+ break;
+ case SLAPI_PLUGIN_CLEANUP_FN:
+ *ret_fnptr = (void*)be->be_cleanup;
+ break;
+ default:
+ slapi_log_error(SLAPI_LOG_FATAL, NULL,
+ "slapi_be_getentrypoint: unknown entry point %d\n", entrypoint);
+ return -1;
+ }
+ return 0;
+}
+
+int
+slapi_be_setentrypoint(Slapi_Backend *be, int entrypoint, void *ret_fnptr, Slapi_PBlock *pb)
+{
+ PR_ASSERT(NULL != be);
+
+ /* this is something needed for most of the entry points */
+ if (pb)
+ {
+ be->be_database=pb->pb_plugin;
+ return 0;
+ }
+
+ switch (entrypoint) {
+ case SLAPI_PLUGIN_DB_BIND_FN:
+ be->be_bind=(IFP)ret_fnptr;
+ break;
+ case SLAPI_PLUGIN_DB_UNBIND_FN:
+ be->be_unbind=(IFP)ret_fnptr;
+ break;
+ case SLAPI_PLUGIN_DB_SEARCH_FN:
+ be->be_search=(IFP)ret_fnptr;
+ break;
+ case SLAPI_PLUGIN_DB_COMPARE_FN:
+ be->be_compare=(IFP)ret_fnptr;
+ break;
+ case SLAPI_PLUGIN_DB_MODIFY_FN:
+ be->be_modify=(IFP)ret_fnptr;
+ break;
+ case SLAPI_PLUGIN_DB_MODRDN_FN:
+ be->be_modrdn=(IFP)ret_fnptr;
+ break;
+ case SLAPI_PLUGIN_DB_ADD_FN:
+ be->be_add=(IFP)ret_fnptr;
+ break;
+ case SLAPI_PLUGIN_DB_DELETE_FN:
+ be->be_delete=(IFP)ret_fnptr;
+ break;
+ case SLAPI_PLUGIN_DB_ABANDON_FN:
+ be->be_abandon=(IFP)ret_fnptr;
+ break;
+ case SLAPI_PLUGIN_DB_CONFIG_FN:
+ be->be_config=(IFP)ret_fnptr;
+ break;
+ case SLAPI_PLUGIN_CLOSE_FN:
+ be->be_close=(IFP)ret_fnptr;
+ break;
+ case SLAPI_PLUGIN_DB_FLUSH_FN:
+ be->be_flush=(IFP)ret_fnptr;
+ break;
+ case SLAPI_PLUGIN_START_FN:
+ be->be_start=(IFP)ret_fnptr;
+ break;
+ case SLAPI_PLUGIN_DB_RESULT_FN:
+ be->be_result=(IFP)ret_fnptr;
+ break;
+ case SLAPI_PLUGIN_DB_LDIF2DB_FN:
+ be->be_ldif2db=(IFP)ret_fnptr;
+ break;
+ case SLAPI_PLUGIN_DB_DB2LDIF_FN:
+ be->be_db2ldif=(IFP) ret_fnptr;
+ break;
+ case SLAPI_PLUGIN_DB_ARCHIVE2DB_FN:
+ be->be_archive2db=(IFP) ret_fnptr;
+ break;
+ case SLAPI_PLUGIN_DB_DB2ARCHIVE_FN:
+ be->be_db2archive=(IFP) ret_fnptr;
+ break;
+ case SLAPI_PLUGIN_DB_NEXT_SEARCH_ENTRY_FN:
+ be->be_next_search_entry=(IFP) ret_fnptr;
+ break;
+ case SLAPI_PLUGIN_DB_NEXT_SEARCH_ENTRY_EXT_FN:
+ be->be_next_search_entry_ext=(IFP) ret_fnptr;
+ break;
+ case SLAPI_PLUGIN_DB_ENTRY_RELEASE_FN:
+ be->be_entry_release=(IFP) ret_fnptr;
+ break;
+ case SLAPI_PLUGIN_DB_SIZE_FN:
+ be->be_dbsize=(IFP) ret_fnptr;
+ break;
+ case SLAPI_PLUGIN_DB_TEST_FN:
+ be->be_dbtest=(IFP)ret_fnptr;
+ break;
+ case SLAPI_PLUGIN_DB_RMDB_FN:
+ be->be_rmdb=(IFP)ret_fnptr;
+ break;
+ case SLAPI_PLUGIN_DB_INIT_INSTANCE_FN:
+ be->be_init_instance=(IFP)ret_fnptr;
+ break;
+ case SLAPI_PLUGIN_DB_SEQ_FN:
+ be->be_seq=(IFP)ret_fnptr;
+ break;
+ case SLAPI_PLUGIN_DB_DB2INDEX_FN:
+ be->be_db2index=(IFP)ret_fnptr;
+ break;
+ case SLAPI_PLUGIN_CLEANUP_FN:
+ be->be_cleanup=(IFP)ret_fnptr;
+ break;
+ default:
+ slapi_log_error(SLAPI_LOG_FATAL, NULL,
+ "slapi_be_setentrypoint: unknown entry point %d\n", entrypoint);
+ return -1;
+ }
+ return 0;
+}
+
+int slapi_be_is_flag_set(Slapi_Backend * be, int flag)
+{
+ return be->be_flags & flag;
+}
+
+void slapi_be_set_flag(Slapi_Backend * be, int flag)
+{
+ be->be_flags|= flag;
+}
+
+char * slapi_be_get_name(Slapi_Backend * be)
+{
+ return be->be_name;
+}
+
+void be_set_sizelimit(Slapi_Backend * be, int sizelimit)
+{
+ be->be_sizelimit = sizelimit;
+}
+
+void be_set_timelimit(Slapi_Backend * be, int timelimit)
+{
+ be->be_timelimit = timelimit;
+}
diff --git a/ldap/servers/slapd/backend_manager.c b/ldap/servers/slapd/backend_manager.c
new file mode 100644
index 00000000..18a9e8a6
--- /dev/null
+++ b/ldap/servers/slapd/backend_manager.c
@@ -0,0 +1,770 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+/* backend_manager.c - routines for dealing with back-end databases */
+
+#include "slap.h"
+
+#define BACKEND_GRAB_SIZE 10
+
+/* JCM - searching the backend array is linear... */
+
+static int defsize = SLAPD_DEFAULT_SIZELIMIT;
+static int deftime = SLAPD_DEFAULT_TIMELIMIT;
+static int nbackends= 0;
+static Slapi_Backend **backends= NULL;
+static int maxbackends= 0;
+
+Slapi_Backend *
+slapi_be_new( const char *type, const char *name, int isprivate, int logchanges )
+{
+ Slapi_Backend *be;
+ int i;
+
+ /* should add some locking here to prevent concurrent access */
+ if ( nbackends == maxbackends )
+ {
+ int oldsize = maxbackends;
+ maxbackends += BACKEND_GRAB_SIZE;
+ backends = (Slapi_Backend **) slapi_ch_realloc( (char *) backends, maxbackends * sizeof(Slapi_Backend *) );
+ memset( &backends[oldsize], '\0', BACKEND_GRAB_SIZE * sizeof(Slapi_Backend *) );
+ }
+
+ for (i=0; ((i<maxbackends) && (backends[i])); i++)
+ ;
+
+ PR_ASSERT(i<maxbackends);
+
+ be = (Slapi_Backend *) slapi_ch_calloc(1, sizeof(Slapi_Backend));
+ be->be_lock = PR_NewRWLock(PR_RWLOCK_RANK_NONE, name );
+ be_init( be, type, name, isprivate, logchanges, defsize, deftime );
+
+ backends[i] = be;
+ nbackends++;
+ return( be );
+}
+
+void
+slapi_be_stopping (Slapi_Backend *be)
+{
+ int i;
+
+ PR_Lock (be->be_state_lock);
+ for (i=0; ((i<maxbackends) && backends[i] != be); i++)
+ ;
+
+ PR_ASSERT(i<maxbackends);
+
+ backends[i] = NULL;
+ be->be_state = BE_STATE_DELETED;
+ if (be->be_lock != NULL)
+ {
+ PR_DestroyRWLock(be->be_lock);
+ be->be_lock = NULL;
+ }
+
+ nbackends--;
+ PR_Unlock (be->be_state_lock);
+}
+
+
+void
+slapi_be_free(Slapi_Backend **be)
+{
+ be_done(*be);
+ slapi_ch_free((void**)be);
+ *be = NULL;
+}
+
+static int
+be_plgfn_unwillingtoperform(Slapi_PBlock *pb)
+{
+ send_ldap_result( pb, LDAP_UNWILLING_TO_PERFORM, NULL, "Operation on Directory Specific Entry not allowed", 0, NULL );
+ return -1;
+}
+
+/* JCM - Seems rather DSE specific... why's it here?... Should be in fedse.c... */
+
+Slapi_Backend *
+be_new_internal(struct dse *pdse, const char *type, const char *name)
+{
+ Slapi_Backend *be= slapi_be_new(type, name, 1 /* Private */, 0 /* Do Not Log Changes */);
+ be->be_database = (struct slapdplugin *) slapi_ch_calloc( 1, sizeof(struct slapdplugin) );
+ be->be_database->plg_private= (void*)pdse;
+ be->be_database->plg_bind= &dse_bind;
+ be->be_database->plg_unbind= &dse_unbind;
+ be->be_database->plg_search= &dse_search;
+ be->be_database->plg_next_search_entry= &dse_next_search_entry;
+ be->be_database->plg_compare= &be_plgfn_unwillingtoperform;
+ be->be_database->plg_modify= &dse_modify;
+ be->be_database->plg_modrdn= &be_plgfn_unwillingtoperform;
+ be->be_database->plg_add= &dse_add;
+ be->be_database->plg_delete= &dse_delete;
+ be->be_database->plg_abandon= &be_plgfn_unwillingtoperform;
+ be->be_database->plg_cleanup = dse_deletedse;
+ /* All the other function pointers default to NULL */
+ return be;
+}
+
+Slapi_Backend*
+slapi_get_first_backend (char **cookie)
+{
+ int i;
+
+ for (i = 0; i < maxbackends; i++)
+ {
+ if ( backends[i] && (backends[i]->be_state != BE_STATE_DELETED))
+ {
+ *cookie = (char*)slapi_ch_malloc (sizeof (int));
+ memcpy (*cookie, &i, sizeof (int));
+ return backends[i];
+ }
+ }
+
+ return NULL;
+}
+
+Slapi_Backend*
+slapi_get_next_backend (char *cookie)
+{
+ int i, last_be;
+ if (cookie == NULL)
+ {
+ LDAPDebug( LDAP_DEBUG_ARGS, "slapi_get_next_backend: NULL argument\n",
+ 0, 0, 0 );
+ return NULL;
+ }
+
+ last_be = *(int *)cookie;
+
+ if ( last_be < 0 || last_be >= maxbackends)
+ {
+ LDAPDebug( LDAP_DEBUG_ARGS, "slapi_get_next_backend: argument out of range\n",
+ 0, 0, 0 );
+ return NULL;
+ }
+
+ if (last_be == maxbackends - 1)
+ return NULL; /* done */
+
+ for (i = last_be + 1; i < maxbackends; i++)
+ {
+ if (backends[i] && (backends[i]->be_state != BE_STATE_DELETED))
+ {
+ memcpy (cookie, &i, sizeof (int));
+ return backends [i];
+ }
+ }
+
+ return NULL;
+}
+
+Slapi_Backend *
+g_get_user_backend( int n )
+{
+ int i, useri;
+
+ useri = 0;
+ for ( i = 0; i < maxbackends; i++ ) {
+ if ( (backends[i] == NULL) || (backends[i]->be_private == 1) ) {
+ continue;
+ }
+
+ if ( useri == n ) {
+ if (backends[i]->be_state != BE_STATE_DELETED)
+ return backends[i];
+ else
+ return NULL;
+ }
+ useri++;
+ }
+ return NULL;
+}
+
+void
+g_set_deftime(int val)
+{
+ deftime = val;
+}
+
+void
+g_set_defsize(int val)
+{
+ defsize = val;
+}
+
+int
+g_get_deftime()
+{
+ return deftime;
+}
+
+int
+g_get_defsize()
+{
+ return defsize;
+}
+
+int strtrimcasecmp(const char *s1, const char *s2)
+{
+ char * s1bis, *s2bis;
+ int len_s1 = 0, len_s2 = 0;
+
+ if ( ((s1 == NULL) && (s2 != NULL))
+ || ((s2 == NULL) && (s1 != NULL)) )
+ return 1;
+
+ if ((s1 == NULL) && (s2 == NULL))
+ return 0;
+
+ while (*s1 == ' ')
+ s1++;
+
+ while (*s2 == ' ')
+ s2++;
+
+ s1bis = (char *) s1;
+ while ((*s1bis != ' ') && (*s1bis != 0))
+ {
+ len_s1 ++;
+ s1bis ++;
+ }
+
+ s2bis = (char *) s2;
+ while ((*s2bis != ' ') && (*s2bis != 0))
+ {
+ len_s2 ++;
+ s2bis ++;
+ }
+
+ if (len_s2 != len_s1)
+ return 1;
+
+ return strncasecmp(s1, s2, len_s1);
+}
+/*
+ * Find the backend of the given type.
+ */
+Slapi_Backend *
+slapi_be_select_by_instance_name( const char *name )
+{
+ int i;
+ for ( i = 0; i < maxbackends; i++ )
+ {
+ if ( backends[i] && (backends[i]->be_state != BE_STATE_DELETED) &&
+ strtrimcasecmp( backends[i]->be_name, name ) == 0)
+ {
+ return backends[i];
+ }
+ }
+ return NULL;
+}
+
+/* void
+be_cleanupall()
+{
+ int i;
+ Slapi_PBlock pb;
+
+ for ( i = 0; i < maxbackends; i++ )
+ {
+ if ( backends[i] &&
+ backends[i]->be_cleanup != NULL &&
+ (backends[i]->be_state == BE_STATE_STOPPED ||
+ backends[i]->be_state == BE_STATE_DELETED))
+ {
+ slapi_pblock_set( &pb, SLAPI_PLUGIN, backends[i]->be_database );
+ slapi_pblock_set( &pb, SLAPI_BACKEND, backends[i] );
+
+ (*backends[i]->be_cleanup)( &pb );
+ }
+ }
+}*/
+
+void
+be_cleanupall()
+{
+ int i;
+ Slapi_PBlock pb;
+
+ for ( i = 0; i < maxbackends; i++ )
+ {
+ if (backends[i] &&
+ backends[i]->be_cleanup != NULL &&
+ (backends[i]->be_state == BE_STATE_STOPPED ||
+ backends[i]->be_state == BE_STATE_DELETED))
+ {
+ slapi_pblock_set( &pb, SLAPI_PLUGIN, backends[i]->be_database );
+ slapi_pblock_set( &pb, SLAPI_BACKEND, backends[i] );
+
+ (*backends[i]->be_cleanup)( &pb );
+ be_done(backends[i]);
+ slapi_ch_free((void **)&backends[i]);
+ }
+ }
+ slapi_ch_free((void**)&backends);
+}
+
+void
+be_flushall()
+{
+ int i;
+ Slapi_PBlock pb;
+
+ for ( i = 0; i < maxbackends; i++ )
+ {
+ if ( backends[i] &&
+ backends[i]->be_state == BE_STATE_STARTED &&
+ backends[i]->be_flush != NULL )
+ {
+ slapi_pblock_set( &pb, SLAPI_PLUGIN, backends[i]->be_database );
+ slapi_pblock_set( &pb, SLAPI_BACKEND, backends[i] );
+ (*backends[i]->be_flush)( &pb );
+ }
+ }
+}
+
+void
+be_unbindall(Connection *conn, Operation *op)
+{
+ int i;
+ Slapi_PBlock pb;
+
+ for ( i = 0; i < maxbackends; i++ )
+ {
+ if ( backends[i] && (backends[i]->be_unbind != NULL) )
+ {
+ pblock_init_common( &pb, backends[i], conn, op );
+
+ if ( plugin_call_plugins( &pb, SLAPI_PLUGIN_PRE_UNBIND_FN ) == 0 )
+ {
+ int rc;
+ slapi_pblock_set( &pb, SLAPI_PLUGIN, backends[i]->be_database );
+ if(backends[i]->be_state != BE_STATE_DELETED &&
+ backends[i]->be_unbind!=NULL)
+ {
+ rc = (*backends[i]->be_unbind)( &pb );
+ }
+ slapi_pblock_set( &pb, SLAPI_PLUGIN_OPRETURN, &rc );
+ (void) plugin_call_plugins( &pb, SLAPI_PLUGIN_POST_UNBIND_FN );
+ }
+ }
+ }
+}
+
+int
+be_nbackends_public()
+{
+ int i;
+ int n= 0;
+ for ( i = 0; i < maxbackends; i++ )
+ {
+ if ( backends[i] &&
+ (backends[i]->be_state != BE_STATE_DELETED) &&
+ (!backends[i]->be_private) )
+ {
+ n++;
+ }
+ }
+ return n;
+}
+
+/* backend instance management */
+/* JCM - These are hardcoded for the LDBM database */
+#define LDBM_CLASS_PREFIX "cn=ldbm database,cn=plugins,cn=config"
+#define LDBM_CONFIG_ENTRY "cn=config,cn=ldbm database,cn=plugins,cn=config"
+#define INSTANCE_ATTR "nsslapd-instance"
+#define SUFFIX_ATTR "nsslapd-suffix"
+#define CACHE_ATTR "nsslapd-cachememsize"
+
+/* add nsslapd-instance attribute to cn=config,cn=ldbm database,cn=plugins,cn=config
+ entry. This causes empty backend instance creation */
+/* JCM - Should be adding an instance entry, not an attr value */
+static int
+be_add_instance (const char *name, void *plugin_identity)
+{
+ Slapi_PBlock pb;
+ Slapi_Mods smods;
+ int rc;
+
+ PR_ASSERT (name && plugin_identity);
+
+ slapi_mods_init (&smods, 1);
+ slapi_mods_add(&smods, LDAP_MOD_ADD, INSTANCE_ATTR, strlen (name), name);
+
+ pblock_init (&pb);
+ slapi_modify_internal_set_pb (&pb, LDBM_CONFIG_ENTRY,
+ slapi_mods_get_ldapmods_byref(&smods), NULL,
+ NULL, plugin_identity, 0);
+ slapi_modify_internal_pb (&pb);
+ slapi_mods_done (&smods);
+
+ slapi_pblock_get (&pb, SLAPI_PLUGIN_INTOP_RESULT,&rc);
+ if (rc != LDAP_SUCCESS)
+ {
+ slapi_log_error(SLAPI_LOG_FATAL, NULL, "be_create_instance: "
+ "failed to modify ldbm configuration entry; LDAP error - %d\n", rc);
+ pblock_done(&pb);
+ return -1;
+ }
+
+ pblock_done(&pb);
+ return 0;
+}
+
+static char*
+be_get_instance_dn (const char *index_name, const char *name)
+{
+ int len;
+ char *dn;
+
+ PR_ASSERT (name);
+
+ len = strlen ("cn=config,") + strlen (name) +
+ strlen (LDBM_CLASS_PREFIX) + 4; /* 4 = "cn=" + ',' + '\0' */
+
+ if (index_name)
+ {
+ len += strlen (index_name) + strlen ("cn=index,") + 4; /* 4 = "cn=" + ',' */
+ }
+
+ dn = (char*)slapi_ch_malloc (len);
+ if (dn)
+ {
+ if (index_name)
+ {
+ sprintf (dn, "cn=%s,cn=index,cn=config,cn=%s,%s", index_name, name,
+ LDBM_CLASS_PREFIX);
+ }
+ else
+ {
+ sprintf (dn, "cn=config,cn=%s,%s", name, LDBM_CLASS_PREFIX);
+ }
+ }
+
+ return dn;
+}
+
+
+/* configure newly added backend by modifying instance's configuration entry:
+ cn=config,cn=<instance name>,cn=ldbm database,cn=plugins,cn=config.
+ Can configure backend root and cache size */
+static int
+be_configure_instance (const char *name, const char *root, int cache_size,
+ void *plugin_identity)
+{
+ Slapi_PBlock pb;
+ Slapi_Mods smods;
+ char value [128];
+ char *dn;
+ int rc;
+
+ PR_ASSERT (name && root && plugin_identity);
+
+ dn = be_get_instance_dn (NULL, name);
+
+ slapi_mods_init (&smods, 2);
+ slapi_mods_add(&smods, LDAP_MOD_ADD, SUFFIX_ATTR, strlen (root), root);
+ if (cache_size > 0)
+ {
+ sprintf (value, "%d", cache_size);
+ slapi_mods_add(&smods, LDAP_MOD_REPLACE, CACHE_ATTR, strlen (value), value);
+ }
+
+ pblock_init (&pb);
+ slapi_modify_internal_set_pb (&pb, dn, slapi_mods_get_ldapmods_byref(&smods),
+ NULL, NULL, plugin_identity, 0);
+ slapi_modify_internal_pb (&pb);
+
+ slapi_mods_done (&smods);
+ slapi_ch_free ((void**)&dn);
+
+ slapi_pblock_get (&pb, SLAPI_PLUGIN_INTOP_RESULT, &rc);
+ if (rc != LDAP_SUCCESS)
+ {
+ slapi_log_error(SLAPI_LOG_FATAL, NULL, "be_create_instance: "
+ "failed to update instance entry; LDAP error - %d\n", rc);
+ pblock_done(&pb);
+ return -1;
+ }
+
+ pblock_done(&pb);
+ return 0;
+}
+
+/* configure instance indexes by adding an index entry:
+ "cn=<attr name>,cn=index,cn=config,cn=<instance name>,
+ cn=ldbm database,cn=plugins,cn=config".*/
+static int
+be_configure_instance_indexes (const char *name, IndexConfig *indexes,
+ int index_count, void *plugin_identity)
+{
+ int rc;
+ Slapi_PBlock pb;
+ Slapi_Entry *e;
+ char *dn;
+ int i;
+ char *start, *end;
+ char index_type [16];
+
+ PR_ASSERT (name && indexes && index_count > 0 && plugin_identity);
+
+ for (i = 0; i < index_count; i++)
+ {
+ dn = be_get_instance_dn (indexes[i].attr_name, name);
+ e = slapi_entry_alloc ();
+ slapi_entry_init (e, dn, NULL);
+
+ /* add objectclases */
+ slapi_entry_add_string (e, "objectclass", "top");
+ slapi_entry_add_string (e, "objectclass", "nsIndex");
+ slapi_entry_add_string (e, "cn", indexes[i].attr_name);
+ slapi_entry_add_string (e, "nssystemindex", indexes[i].system ? "true" : "false");
+
+ start = indexes[i].index_type;
+ while ((end = strchr (start, ' ')) != NULL)
+ {
+ if ((end - start) >= 16)
+ continue;
+
+ strncpy (index_type, start, end - start);
+ slapi_entry_add_string (e, "nsindextype", index_type);
+ start = end + 1;
+ }
+
+ slapi_entry_add_string (e, "nsindextype", start);
+
+ pblock_init (&pb);
+ slapi_add_entry_internal_set_pb (&pb, e, NULL /* controls */, plugin_identity,
+ 0/* operation flags */);
+ slapi_add_internal_pb (&pb);
+
+ slapi_pblock_get (&pb, SLAPI_PLUGIN_INTOP_RESULT, &rc);
+ if (rc != LDAP_SUCCESS)
+ {
+ slapi_log_error(SLAPI_LOG_FATAL, NULL, "be_create_instance: "
+ "failed to update instance entry; LDAP error - %d\n", rc);
+ pblock_done(&pb);
+ return -1;
+ }
+ }
+
+ pblock_done(&pb);
+ return 0;
+}
+
+int
+be_create_instance (const char *type, const char *name, const char *root,
+ int cache_size, IndexConfig *indexes, int index_count,
+ void *plugin_identity)
+{
+ int rc;
+
+ if (type == NULL || strcasecmp (type, "ldbm") != 0)
+ {
+ slapi_log_error(SLAPI_LOG_FATAL, NULL, "be_create_instance: "
+ "invalid backend type: %s.\n", type ? type : "null");
+ return -1;
+ }
+
+ if (name == NULL)
+ {
+ slapi_log_error(SLAPI_LOG_FATAL, NULL, "be_create_instance: null instance name.\n");
+ return -1;
+ }
+
+ if (root == NULL)
+ {
+ slapi_log_error(SLAPI_LOG_FATAL, NULL, "be_create_instance: null root dn.\n");
+ return -1;
+ }
+
+ if (plugin_identity == NULL)
+ {
+ slapi_log_error(SLAPI_LOG_FATAL, NULL, "be_create_instance: null plugin identity.\n");
+ return -1;
+ }
+
+ rc = be_add_instance (name, plugin_identity);
+ if (rc != 0)
+ return rc;
+
+ rc = be_configure_instance (name, root, cache_size, plugin_identity);
+ if (rc != 0)
+ return rc;
+
+ if (index_count > 0)
+ rc = be_configure_instance_indexes (name, indexes, index_count, plugin_identity);
+
+ return rc;
+}
+
+int
+be_remove_instance (const char *type, const char *name, void *plugin_identity)
+{
+ int rc;
+ char *dn;
+ Slapi_PBlock pb;
+
+ if (type == NULL || strcasecmp (type, "ldbm") != 0)
+ {
+ slapi_log_error(SLAPI_LOG_FATAL, NULL, "be_remove_instance: "
+ "invalid backend type: %s.\n", type ? type : "null");
+ return -1;
+ }
+
+ if (name == NULL)
+ {
+ slapi_log_error(SLAPI_LOG_FATAL, NULL, "be_remove_instance: null instance name.\n");
+ return -1;
+ }
+
+ if (plugin_identity == NULL)
+ {
+ slapi_log_error(SLAPI_LOG_FATAL, NULL, "be_remove_instance: null plugin identity.\n");
+ return -1;
+ }
+
+ dn = be_get_instance_dn (NULL, name);
+
+ pblock_init (&pb);
+ slapi_delete_internal_set_pb (&pb, dn, NULL, NULL, plugin_identity, 0);
+ slapi_delete_internal_pb (&pb);
+
+ slapi_ch_free ((void**)&dn);
+
+ slapi_pblock_get (&pb, SLAPI_PLUGIN_INTOP_RESULT, &rc);
+ if (rc != LDAP_SUCCESS)
+ {
+ slapi_log_error(SLAPI_LOG_FATAL, NULL, "be_create_instance: "
+ "failed to update instance entry; LDAP error - %d\n", rc);
+ pblock_done(&pb);
+ return -1;
+ }
+
+ pblock_done(&pb);
+ return 0;
+}
+
+void
+slapi_be_Rlock(Slapi_Backend * be)
+{
+ PR_RWLock_Rlock(be->be_lock);
+}
+
+void
+slapi_be_Wlock(Slapi_Backend * be)
+{
+ PR_RWLock_Wlock(be->be_lock);
+}
+
+void
+slapi_be_Unlock(Slapi_Backend * be)
+{
+ PR_RWLock_Unlock(be->be_lock);
+}
+
+/*
+ * lookup instance names by suffix.
+ * if isexact == 0: returns instances including ones that associates with
+ * its sub suffixes.
+ * e.g., suffix: "o=<suffix>" is given, these are returned:
+ * suffixes: o=<suffix>, ou=<ou>,o=<suffix>, ...
+ * instances: inst of "o=<suffix>",
+ * inst of "ou=<ou>,o=<suffix>",
+ * ...
+ * if isexact != 0: returns an instance that associates with the given suffix
+ * e.g., suffix: "o=<suffix>" is given, these are returned:
+ * suffixes: "o=<suffix>"
+ * instances: inst of "o=<suffix>"
+ * Note: if suffixes
+ */
+int
+slapi_lookup_instance_name_by_suffix(char *suffix,
+ char ***suffixes, char ***instances, int isexact)
+{
+ Slapi_Backend *be = NULL;
+ char *cookie = NULL;
+ const char *thisdn;
+ int thisdnlen;
+ int suffixlen;
+ int maxinst = 1;
+ int i;
+ int rval = -1;
+
+ if (instances == NULL)
+ return rval;
+
+ rval = 0;
+ suffixlen = strlen(suffix);
+ cookie = NULL;
+ be = slapi_get_first_backend (&cookie);
+ while (be) {
+ if (NULL == be->be_suffix) {
+ be = (backend *)slapi_get_next_backend (cookie);
+ continue;
+ }
+ PR_Lock(be->be_suffixlock);
+ for (i = 0; be->be_suffix && i < be->be_suffixcount; i++) {
+ thisdn = slapi_sdn_get_ndn(be->be_suffix[i]);
+ thisdnlen = slapi_sdn_get_ndn_len(be->be_suffix[i]);
+ if (isexact?suffixlen!=thisdnlen:suffixlen>thisdnlen)
+ continue;
+ if (isexact?(!slapi_UTF8CASECMP(suffix, (char *)thisdn)):
+ (!slapi_UTF8CASECMP(suffix,
+ (char *)thisdn+thisdnlen-suffixlen))) {
+ charray_add(instances, slapi_ch_strdup(be->be_name));
+ if (suffixes)
+ charray_add(suffixes, slapi_ch_strdup(thisdn));
+ }
+ }
+ PR_Unlock(be->be_suffixlock);
+ be = (backend *)slapi_get_next_backend (cookie);
+ }
+
+ return rval;
+}
+
+/*
+ * lookup instance names by included suffixes and excluded suffixes.
+ *
+ * Get instance names associated with the given included suffixes
+ * as well as the excluded suffixes.
+ * Subtract the excluded instances from the included instance.
+ * Assign the result to instances.
+ */
+int
+slapi_lookup_instance_name_by_suffixes(char **included, char **excluded,
+ char ***instances)
+{
+ char **incl_instances, **excl_instances;
+ char **p;
+ int rval = -1;
+
+ if (instances == NULL)
+ return rval;
+
+ *instances = NULL;
+ incl_instances = NULL;
+ for (p = included; p && *p; p++) {
+ if (slapi_lookup_instance_name_by_suffix(*p, NULL, &incl_instances, 0)
+ < 0)
+ return rval;
+ }
+
+ excl_instances = NULL;
+ for (p = excluded; p && *p; p++) {
+ /* okay to be empty */
+ slapi_lookup_instance_name_by_suffix(*p, NULL, &excl_instances, 0);
+ }
+
+ rval = 0;
+ if (excl_instances) {
+ charray_subtract(incl_instances, excl_instances, NULL);
+ charray_free(excl_instances);
+ }
+ *instances = incl_instances;
+ return rval;
+}
diff --git a/ldap/servers/slapd/bind.c b/ldap/servers/slapd/bind.c
new file mode 100644
index 00000000..ab9ca89d
--- /dev/null
+++ b/ldap/servers/slapd/bind.c
@@ -0,0 +1,710 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+/* bind.c - decode an ldap bind operation and pass it to a backend db */
+
+/*
+ * 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 <stdio.h>
+#include <string.h>
+#include <sys/types.h>
+#ifndef _WIN32
+#include <sys/socket.h>
+#endif
+#include "slap.h"
+#include "fe.h"
+
+#include "pratom.h"
+#include <sasl.h>
+
+static void log_bind_access(
+ Slapi_PBlock *pb,
+ const char* dn,
+ int method,
+ int version,
+ const char *saslmech,
+ const char *msg
+);
+
+
+/*
+ * Function: is_root_dn_pw
+ *
+ * Returns: 1 if the password for the root dn is correct.
+ * 0 otherwise.
+ * dn must be normalized
+ *
+ */
+static int
+is_root_dn_pw( const char *dn, const Slapi_Value *cred )
+{
+ int rv= 0;
+ char *rootpw = config_get_rootpw();
+ if ( rootpw == NULL || !slapi_dn_isroot( dn ) )
+ {
+ rv = 0;
+ }
+ else
+ {
+ Slapi_Value rdnpwbv;
+ Slapi_Value *rdnpwvals[2];
+ slapi_value_init_string(&rdnpwbv,rootpw);
+ rdnpwvals[ 0 ] = &rdnpwbv;
+ rdnpwvals[ 1 ] = NULL;
+ rv = slapi_pw_find_sv( rdnpwvals, cred ) == 0;
+ value_done(&rdnpwbv);
+ }
+ slapi_ch_free( (void **) &rootpw );
+ return rv;
+}
+
+void
+do_bind( Slapi_PBlock *pb )
+{
+ BerElement *ber = pb->pb_op->o_ber;
+ int err, version = -1, method = -1, isroot;
+ long long_method = -1;
+ long ber_version = -1;
+ int auth_response_requested = 0;
+ int pw_response_requested = 0;
+ char *dn, *saslmech = NULL;
+ struct berval cred = {0};
+ Slapi_Backend *be = NULL;
+ unsigned long rc;
+ Slapi_DN sdn;
+ Slapi_Entry *referral;
+ char errorbuf[BUFSIZ];
+ char **supported, **pmech;
+ char authtypebuf[256]; /* >26 (strlen(SLAPD_AUTH_SASL)+SASL_MECHNAMEMAX+1) */
+ Slapi_Entry *bind_target_entry = NULL;
+
+ LDAPDebug( LDAP_DEBUG_TRACE, "do_bind\n", 0, 0, 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, -- not used
+ * krbv42dsa [2] OCTET STRING, -- not used
+ * sasl [3] SaslCredentials -- v3 only
+ * }
+ * }
+ *
+ * Saslcredentials ::= SEQUENCE {
+ * mechanism LDAPString,
+ * credentials OCTET STRING
+ * }
+ */
+
+ rc = ber_scanf( ber, "{iat", &ber_version, &dn, &long_method );
+ method = long_method;
+ version = ber_version;
+ if ( rc == LBER_ERROR ) {
+ LDAPDebug( LDAP_DEBUG_ANY,
+ "ber_scanf failed (op=Bind; params=Version,DN,Method)\n",
+ 0, 0, 0 );
+ log_bind_access (pb, "???", method, version, saslmech, "decoding error");
+ send_ldap_result( pb, LDAP_PROTOCOL_ERROR, NULL,
+ "decoding error", 0, NULL );
+ return;
+ }
+
+ slapi_sdn_init_dn_passin(&sdn,dn);
+
+ LDAPDebug( LDAP_DEBUG_TRACE, "BIND dn=\"%s\" method=%d version=%d\n",
+ dn, method, version );
+
+ /* target spec is used to decide which plugins are applicable for the operation */
+ operation_set_target_spec (pb->pb_op, &sdn);
+
+ switch ( method ) {
+ case LDAP_AUTH_SASL:
+ if ( version < LDAP_VERSION3 ) {
+ LDAPDebug( LDAP_DEBUG_ANY,
+ "got SASL credentials from LDAPv2 client\n",
+ 0, 0, 0 );
+ log_bind_access (pb, slapi_sdn_get_dn (&sdn), method, version, saslmech, "SASL credentials only in LDAPv3");
+ send_ldap_result( pb, LDAP_PROTOCOL_ERROR, NULL,
+ "SASL credentials only in LDAPv3", 0, NULL );
+ goto free_and_return;
+ }
+ /* Get the SASL mechanism */
+ rc = ber_scanf( ber, "{a", &saslmech );
+ /* Get the (optional) SASL credentials */
+ if ( rc != LBER_ERROR ) {
+ /* Credentials are optional in SASL bind */
+ unsigned long clen;
+ if (( ber_peek_tag( ber, &clen )) == LBER_OCTETSTRING ) {
+ rc = ber_scanf( ber, "o}}", &cred );
+ } else {
+ rc = ber_scanf( ber, "}}" );
+ }
+ }
+ break;
+ case LDAP_AUTH_KRBV41:
+ /* FALLTHROUGH */
+ case LDAP_AUTH_KRBV42:
+ if ( version >= LDAP_VERSION3 ) {
+ static char *kmsg =
+ "LDAPv2-style kerberos authentication received "
+ "on LDAPv3 connection.";
+ LDAPDebug( LDAP_DEBUG_ANY, kmsg, 0, 0, 0 );
+ log_bind_access (pb, slapi_sdn_get_dn (&sdn), method, version, saslmech, kmsg);
+ send_ldap_result( pb, LDAP_PROTOCOL_ERROR, NULL,
+ kmsg, 0, NULL );
+ goto free_and_return;
+ }
+ /* FALLTHROUGH */
+ case LDAP_AUTH_SIMPLE:
+ rc = ber_scanf( ber, "o}", &cred );
+ break;
+ default:
+ log_bind_access (pb, slapi_sdn_get_dn (&sdn), method, version, saslmech, "Unknown bind method");
+ send_ldap_result( pb, LDAP_PROTOCOL_ERROR, NULL,
+ "Unknown bind method", 0, NULL );
+ goto free_and_return;
+ }
+ if ( rc == LBER_ERROR ) {
+ LDAPDebug( LDAP_DEBUG_ANY,
+ "ber_scanf failed (op=Bind; params=Credentials)\n",
+ 0, 0, 0 );
+ log_bind_access (pb, slapi_sdn_get_dn (&sdn), method, version, saslmech, "decoding error");
+ send_ldap_result( pb, LDAP_PROTOCOL_ERROR, NULL,
+ "decoding error", 0, NULL );
+ goto free_and_return;
+ }
+
+ /*
+ * in LDAPv3 there can be optional control extensions on
+ * the end of an LDAPMessage. we need to read them in and
+ * pass them to the backend.
+ * We also check for the presence of an "Authentication Request
+ * Control" and set a flag so we know later whether we need to send
+ * an "Authentication Response Control" with Success responses.
+ */
+ {
+ LDAPControl **reqctrls;
+
+ if (( err = get_ldapmessage_controls( pb, ber, &reqctrls ))
+ != 0 ) {
+ log_bind_access (pb, slapi_sdn_get_dn (&sdn), method,
+ version, saslmech, "failed to parse LDAP controls");
+ send_ldap_result( pb, err, NULL, NULL, 0, NULL );
+ goto free_and_return;
+ }
+
+ auth_response_requested = slapi_control_present( reqctrls,
+ LDAP_CONTROL_AUTH_REQUEST, NULL, NULL );
+ slapi_pblock_get (pb, SLAPI_PWPOLICY, &pw_response_requested);
+ }
+
+ log_bind_access(pb, dn, method, version, saslmech, NULL);
+
+ /* According to RFC2251,
+ * "if the bind fails, the connection will be treated as anonymous".
+ */
+ PR_Lock( pb->pb_conn->c_mutex );
+ bind_credentials_clear( pb->pb_conn, PR_FALSE, /* conn is already locked */
+ PR_FALSE /* do not clear external creds. */ );
+ /* Clear the password policy flag that forbid operation
+ * other than Bind, Modify, Unbind :
+ * With a new bind, the flag should be reset so that the new
+ * bound user can work properly
+ */
+ pb->pb_conn->c_needpw = 0;
+ PR_Unlock( pb->pb_conn->c_mutex );
+
+ switch ( version ) {
+ case LDAP_VERSION2:
+ if (method == LDAP_AUTH_SIMPLE
+ && (dn == NULL || *dn == '\0') && cred.bv_len == 0
+ && pb->pb_conn->c_external_dn != NULL) {
+ /* Treat this like a SASL EXTERNAL Bind: */
+ method = LDAP_AUTH_SASL;
+ saslmech = slapi_ch_strdup (LDAP_SASL_EXTERNAL);
+ /* This enables a client to establish an identity by sending
+ * a certificate in the SSL handshake, and also use LDAPv2
+ * (by sending this type of Bind request).
+ */
+ }
+ break;
+ case LDAP_VERSION3:
+ break;
+ default:
+ LDAPDebug( LDAP_DEBUG_TRACE, "bind: unknown LDAP protocol version %d\n",
+ version, 0, 0 );
+ send_ldap_result( pb, LDAP_PROTOCOL_ERROR, NULL,
+ "version not supported", 0, NULL );
+ goto free_and_return;
+ }
+
+ LDAPDebug( LDAP_DEBUG_TRACE, "do_bind: version %d method 0x%x dn %s\n",
+ version, method, dn );
+ pb->pb_conn->c_ldapversion = version;
+
+ isroot = slapi_dn_isroot( slapi_sdn_get_ndn(&sdn) );
+ slapi_pblock_set( pb, SLAPI_REQUESTOR_ISROOT, &isroot );
+ slapi_pblock_set( pb, SLAPI_BIND_TARGET, (void*)slapi_sdn_get_ndn(&sdn) );
+ slapi_pblock_set( pb, SLAPI_BIND_METHOD, &method );
+ slapi_pblock_set( pb, SLAPI_BIND_SASLMECHANISM, saslmech );
+ slapi_pblock_set( pb, SLAPI_BIND_CREDENTIALS, &cred );
+
+ if (method != LDAP_AUTH_SASL) {
+ /*
+ * RFC2251: client may abort a sasl bind negotiation by sending
+ * an authentication choice other than sasl.
+ */
+ pb->pb_conn->c_flags &= ~CONN_FLAG_SASL_CONTINUE;
+ }
+
+ switch ( method ) {
+ case LDAP_AUTH_SASL:
+ /*
+ * All SASL auth methods are categorized as strong binds,
+ * although they are not necessarily stronger than simple.
+ */
+ PR_AtomicIncrement(g_get_global_snmp_vars()->ops_tbl.dsStrongAuthBinds);
+ if ( saslmech == NULL || *saslmech == '\0' ) {
+ send_ldap_result( pb, LDAP_AUTH_METHOD_NOT_SUPPORTED, NULL,
+ "SASL mechanism absent", 0, NULL );
+ goto free_and_return;
+ }
+
+ if (strlen(saslmech) > SASL_MECHNAMEMAX) {
+ send_ldap_result( pb, LDAP_AUTH_METHOD_NOT_SUPPORTED, NULL,
+ "SASL mechanism name is too long", 0, NULL );
+ goto free_and_return;
+ }
+
+ supported = slapi_get_supported_saslmechanisms_copy();
+ if ( (pmech = supported) != NULL ) while (1) {
+ if (*pmech == NULL) {
+ /* As we call the safe function, we receive a strdup'd saslmechanisms
+ charray. Therefore, we need to remove it instead of NULLing it */
+ charray_free(supported);
+ pmech = supported = NULL;
+ break;
+ }
+ if (!strcasecmp (saslmech, *pmech)) break;
+ ++pmech;
+ }
+ if (!pmech) {
+ /* now check the sasl library */
+ ids_sasl_check_bind(pb);
+ goto free_and_return;
+ }
+ else {
+ charray_free(supported); /* Avoid leaking */
+ }
+
+ if (!strcasecmp (saslmech, LDAP_SASL_EXTERNAL)) {
+ /*
+ * if this is not an SSL connection, fail and return an
+ * inappropriateAuth error.
+ */
+ if ( 0 == ( pb->pb_conn->c_flags & CONN_FLAG_SSL )) {
+ send_ldap_result( pb, LDAP_INAPPROPRIATE_AUTH, NULL,
+ "SASL EXTERNAL bind requires an SSL connection",
+ 0, NULL );
+ goto free_and_return;
+ }
+
+ /*
+ * if the client sent us a certificate but we could not map it
+ * to an LDAP DN, fail and return an invalidCredentials error.
+ */
+ if ( NULL != pb->pb_conn->c_client_cert &&
+ NULL == pb->pb_conn->c_external_dn ) {
+ send_ldap_result( pb, LDAP_INVALID_CREDENTIALS, NULL,
+ "client certificate mapping failed", 0, NULL );
+ goto free_and_return;
+ }
+
+ /*
+ * copy external credentials into connection structure
+ */
+ bind_credentials_set( pb->pb_conn,
+ pb->pb_conn->c_external_authtype,
+ pb->pb_conn->c_external_dn,
+ NULL, NULL, NULL , NULL);
+ if ( auth_response_requested ) {
+ add_auth_response_control( pb, pb->pb_conn->c_external_dn );
+ }
+ send_ldap_result( pb, LDAP_SUCCESS, NULL, NULL, 0, NULL );
+ goto free_and_return;
+ }
+ break;
+ case LDAP_AUTH_SIMPLE:
+ PR_AtomicIncrement(g_get_global_snmp_vars()->ops_tbl.dsSimpleAuthBinds);
+ /* accept null binds */
+ if (dn == NULL || *dn == '\0') {
+ PR_AtomicIncrement(g_get_global_snmp_vars()->ops_tbl.dsAnonymousBinds);
+ /* by definition its anonymous is also UnAuthenticated so increment
+ that counter */
+ PR_AtomicIncrement(g_get_global_snmp_vars()->ops_tbl.dsUnAuthBinds);
+
+ /* call preop plugins */
+ if (plugin_call_plugins( pb, SLAPI_PLUGIN_PRE_BIND_FN ) == 0){
+ if ( auth_response_requested ) {
+ add_auth_response_control( pb, "" );
+ }
+ send_ldap_result( pb, LDAP_SUCCESS, NULL, NULL, 0, NULL );
+
+ /* call postop plugins */
+ plugin_call_plugins( pb, SLAPI_PLUGIN_POST_BIND_FN );
+ }
+ goto free_and_return;
+ }
+ break;
+ default:
+ break;
+ }
+
+ /*
+ * handle binds as the manager here, pass others to the backend
+ */
+
+ if ( isroot && method == LDAP_AUTH_SIMPLE ) {
+ if ( cred.bv_len == 0 ) {
+ /* unauthenticated bind */
+ PR_AtomicIncrement(g_get_global_snmp_vars()->ops_tbl.dsUnAuthBinds);
+
+ } else {
+ /* a passwd was supplied -- check it */
+ Slapi_Value cv;
+ slapi_value_init_berval(&cv,&cred);
+
+ if ( is_root_dn_pw( slapi_sdn_get_ndn(&sdn), &cv )) {
+ /* right dn and passwd - authorize */
+ bind_credentials_set( pb->pb_conn, SLAPD_AUTH_SIMPLE,
+ slapi_ch_strdup( slapi_sdn_get_ndn(&sdn) ),
+ NULL, NULL, NULL , NULL);
+
+ /* right dn, wrong passwd - reject with invalid creds */
+ } else {
+ send_ldap_result( pb, LDAP_INVALID_CREDENTIALS, NULL,
+ NULL, 0, NULL );
+ /* increment BindSecurityErrorcount */
+ PR_AtomicIncrement(g_get_global_snmp_vars()->ops_tbl.dsBindSecurityErrors);
+ value_done(&cv);
+ goto free_and_return;
+ }
+ value_done(&cv);
+ }
+
+ /* call preop plugin */
+ if (plugin_call_plugins( pb, SLAPI_PLUGIN_PRE_BIND_FN ) == 0){
+ if ( auth_response_requested ) {
+ add_auth_response_control( pb,
+ ( cred.bv_len == 0 ) ? "" :
+ slapi_sdn_get_ndn(&sdn));
+ }
+ send_ldap_result( pb, LDAP_SUCCESS, NULL, NULL, 0, NULL );
+
+ /* call postop plugins */
+ plugin_call_plugins( pb, SLAPI_PLUGIN_POST_BIND_FN );
+ }
+ goto free_and_return;
+ }
+
+ /* We could be serving multiple database backends. Select the appropriate one */
+ if (slapi_mapping_tree_select(pb, &be, &referral, errorbuf) != LDAP_SUCCESS) {
+ send_nobackend_ldap_result( pb );
+ be = NULL;
+ goto free_and_return;
+ }
+
+ if (referral)
+ {
+ send_referrals_from_entry(pb,referral);
+ slapi_entry_free(referral);
+ goto free_and_return;
+ }
+
+ slapi_pblock_set( pb, SLAPI_BACKEND, be );
+
+ /* not root dn - pass to the backend */
+ if ( be->be_bind != NULL ) {
+
+ /*
+ * call the pre-bind plugins. if they succeed, call
+ * the backend bind function. then call the post-bind
+ * plugins.
+ */
+ if ( plugin_call_plugins( pb, SLAPI_PLUGIN_PRE_BIND_FN )
+ == 0 ) {
+ int rc = 0;
+
+ /*
+ * Is this account locked ?
+ * could be locked through the account inactivation
+ * or by the password policy
+ *
+ * rc=0: account not locked
+ * rc=1: account locked, can not bind, result has been sent
+ * rc!=0 and rc!=1: error. Result was not sent, lets be_bind
+ * deal with it.
+ *
+ */
+
+ /* get the entry now, so that we can give it to check_account_lock and reslimit_update_from_dn */
+ if (! slapi_be_is_flag_set(be, SLAPI_BE_FLAG_REMOTE_DATA)) {
+ bind_target_entry = get_entry(pb, slapi_sdn_get_ndn(&sdn));
+ rc = check_account_lock ( pb, bind_target_entry, pw_response_requested);
+ }
+
+ slapi_pblock_set( pb, SLAPI_PLUGIN, be->be_database );
+ set_db_default_result_handlers(pb);
+ if ( (rc != 1) && (((rc = (*be->be_bind)( pb ))
+ == SLAPI_BIND_SUCCESS ) || rc
+ == SLAPI_BIND_ANONYMOUS )) {
+ long t;
+ {
+ char* authtype = NULL;
+ switch ( method ) {
+ case LDAP_AUTH_SIMPLE:
+ if (cred.bv_len != 0) {
+ authtype = SLAPD_AUTH_SIMPLE;
+ }
+ break;
+ case LDAP_AUTH_SASL:
+ /* authtype = SLAPD_AUTH_SASL && saslmech: */
+ sprintf(authtypebuf, "%s%s", SLAPD_AUTH_SASL, saslmech);
+ authtype = authtypebuf;
+ break;
+ default: /* ??? */
+ break;
+ }
+
+ if ( rc == SLAPI_BIND_SUCCESS ) {
+ bind_credentials_set( pb->pb_conn,
+ authtype, slapi_ch_strdup(
+ slapi_sdn_get_ndn(&sdn)),
+ NULL, NULL, NULL, bind_target_entry );
+ if ( auth_response_requested ) {
+ add_auth_response_control( pb,
+ slapi_sdn_get_ndn(&sdn));
+ }
+ } else { /* anonymous */
+ if ( auth_response_requested ) {
+ add_auth_response_control( pb,
+ "" );
+ }
+ }
+ }
+
+ if ( rc != SLAPI_BIND_ANONYMOUS &&
+ ! slapi_be_is_flag_set(be,
+ SLAPI_BE_FLAG_REMOTE_DATA)) {
+ /* check if need new password before sending
+ the bind success result */
+ switch ( need_new_pw (pb, &t, bind_target_entry, pw_response_requested )) {
+
+ case 1:
+ (void)add_pwd_control ( pb,
+ LDAP_CONTROL_PWEXPIRED, 0);
+ break;
+
+ case 2:
+ (void)add_pwd_control ( pb,
+ LDAP_CONTROL_PWEXPIRING, t);
+ break;
+ case -1:
+ goto free_and_return;
+ default:
+ break;
+ }
+ } /* end if */
+ }else{
+
+ if(cred.bv_len == 0) {
+ /* its an UnAuthenticated Bind, DN specified but no pw */
+ PR_AtomicIncrement(g_get_global_snmp_vars()->ops_tbl.dsUnAuthBinds);
+ }else{
+ /* password must have been invalid */
+ /* increment BindSecurityError count */
+ PR_AtomicIncrement(g_get_global_snmp_vars()->ops_tbl.dsBindSecurityErrors);
+ }
+
+ }
+
+ /*
+ * if rc != SLAPI_BIND_SUCCESS and != SLAPI_BIND_ANONYMOUS,
+ * the result has already been sent by the backend. otherwise,
+ * we assume it is success and send it here to avoid a race
+ * condition where the client could be told by the
+ * backend that the bind succeeded before we set the
+ * c_dn field in the connection structure here in
+ * the front end.
+ */
+ if ( rc == SLAPI_BIND_SUCCESS || rc == SLAPI_BIND_ANONYMOUS) {
+ send_ldap_result( pb, LDAP_SUCCESS, NULL, NULL,
+ 0, NULL );
+ }
+
+ slapi_pblock_set( pb, SLAPI_PLUGIN_OPRETURN, &rc );
+ plugin_call_plugins( pb, SLAPI_PLUGIN_POST_BIND_FN );
+ }
+ } else {
+ send_ldap_result( pb, LDAP_UNWILLING_TO_PERFORM, NULL,
+ "Function not implemented", 0, NULL );
+ }
+
+ free_and_return:;
+ if (be)
+ slapi_be_Unlock(be);
+ slapi_sdn_done(&sdn);
+ if ( saslmech != NULL ) {
+ free( saslmech );
+ }
+ if ( cred.bv_val != NULL ) {
+ free( cred.bv_val );
+ }
+ if ( bind_target_entry != NULL )
+ slapi_entry_free(bind_target_entry);
+}
+
+
+/*
+ * register all of the LDAPv3 SASL mechanisms we know about.
+ */
+void
+init_saslmechanisms( void )
+{
+ ids_sasl_init();
+ slapi_register_supported_saslmechanism( LDAP_SASL_EXTERNAL );
+}
+
+static void
+log_bind_access (
+ Slapi_PBlock *pb,
+ const char* dn,
+ int method,
+ int version,
+ const char *saslmech,
+ const char *msg
+)
+{
+ char ebuf[ BUFSIZ ];
+ const char *edn;
+
+ edn = escape_string( dn, ebuf );
+
+ if (method == LDAP_AUTH_SASL && saslmech && msg) {
+ slapi_log_access( LDAP_DEBUG_STATS,
+ "conn=%d op=%d BIND dn=\"%s\" "
+ "method=sasl version=%d mech=%s, %s\n",
+ pb->pb_conn->c_connid, pb->pb_op->o_opid, edn,
+ version, saslmech, msg );
+ } else if (method == LDAP_AUTH_SASL && saslmech) {
+ slapi_log_access( LDAP_DEBUG_STATS,
+ "conn=%d op=%d BIND dn=\"%s\" "
+ "method=sasl version=%d mech=%s\n",
+ pb->pb_conn->c_connid, pb->pb_op->o_opid, edn,
+ version, saslmech );
+ } else if (msg) {
+ slapi_log_access( LDAP_DEBUG_STATS,
+ "conn=%d op=%d BIND dn=\"%s\" "
+ "method=%d version=%d, %s\n",
+ pb->pb_conn->c_connid, pb->pb_op->o_opid, edn,
+ method, version, msg );
+ } else {
+ slapi_log_access( LDAP_DEBUG_STATS,
+ "conn=%d op=%d BIND dn=\"%s\" "
+ "method=%d version=%d\n",
+ pb->pb_conn->c_connid, pb->pb_op->o_opid, edn,
+ method, version );
+ }
+}
+
+
+void
+add_auth_response_control( Slapi_PBlock *pb, const char *binddn )
+{
+ LDAPControl arctrl;
+ char dnbuf_fixedsize[ 512 ], *dnbuf, *dnbuf_dynamic = NULL;
+ size_t dnlen;
+
+ if ( NULL == binddn ) {
+ binddn = "";
+ }
+ dnlen = strlen( binddn );
+
+ /*
+ * According to draft-weltman-ldapv3-auth-response-03.txt section
+ * 4 (Authentication Response Control):
+ *
+ * The controlType is "2.16.840.1.113730.3.4.15". If the bind request
+ * succeeded and resulted in an identity (not anonymous), the
+ * controlValue contains the authorization identity [AUTH] granted to
+ * the requestor. If the bind request resulted in anonymous
+ * authentication, the controlValue field is a string of zero length.
+ *
+ * [AUTH] is a reference to RFC 2829, which in section 9 defines
+ * authorization identity as:
+ *
+ *
+ * The authorization identity is a string in the UTF-8 character set,
+ * corresponding to the following ABNF [7]:
+ *
+ * ; Specific predefined authorization (authz) id schemes are
+ * ; defined below -- new schemes may be defined in the future.
+ *
+ * authzId = dnAuthzId / uAuthzId
+ *
+ * ; distinguished-name-based authz id.
+ * dnAuthzId = "dn:" dn
+ * dn = utf8string ; with syntax defined in RFC 2253
+ *
+ * ; unspecified userid, UTF-8 encoded.
+ * uAuthzId = "u:" userid
+ * userid = utf8string ; syntax unspecified
+ *
+ * A utf8string is defined to be the UTF-8 encoding of one or more ISO
+ * 10646 characters.
+ *
+ * We always map identities to DNs, so we always use the dnAuthzId form.
+ */
+ arctrl.ldctl_oid = LDAP_CONTROL_AUTH_RESPONSE;
+ arctrl.ldctl_iscritical = 0;
+
+ if ( dnlen == 0 ) { /* anonymous -- return zero length value */
+ arctrl.ldctl_value.bv_val = "";
+ arctrl.ldctl_value.bv_len = 0;
+ } else { /* mapped to a DN -- return "dn:<DN>" */
+ if ( 3 + dnlen < sizeof( dnbuf_fixedsize )) {
+ dnbuf = dnbuf_fixedsize;
+ } else {
+ dnbuf = dnbuf_dynamic = slapi_ch_malloc( 4 + dnlen );
+ }
+ strcpy( dnbuf, "dn:" );
+ strcpy( dnbuf + 3, binddn );
+ arctrl.ldctl_value.bv_val = dnbuf;
+ arctrl.ldctl_value.bv_len = 3 + dnlen;
+ }
+
+ if ( slapi_pblock_set( pb, SLAPI_ADD_RESCONTROL, &arctrl ) != 0 ) {
+ slapi_log_error( SLAPI_LOG_FATAL, "bind",
+ "unable to add authentication response control" );
+ }
+
+ if ( NULL != dnbuf_dynamic ) {
+ slapi_ch_free( (void **)&dnbuf_dynamic );
+ }
+}
diff --git a/ldap/servers/slapd/bitset.c b/ldap/servers/slapd/bitset.c
new file mode 100644
index 00000000..5bdc5291
--- /dev/null
+++ b/ldap/servers/slapd/bitset.c
@@ -0,0 +1,47 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+
+unsigned char
+slapi_setbit_uchar(unsigned char f,unsigned char bitnum)
+{
+ return (f | ((unsigned char)1 << bitnum));
+}
+
+unsigned char
+slapi_unsetbit_uchar(unsigned char f,unsigned char bitnum)
+{
+/* TEMPORARY WORKAROUND FOR x86 compiler problem on solaris
+ * return (f & (~((unsigned char)1 << bitnum)));
+ */
+ unsigned char t;
+ t = f & (~((unsigned char)1 << bitnum));
+ return(t);
+}
+
+int
+slapi_isbitset_uchar(unsigned char f,unsigned char bitnum)
+{
+ return (f & ((unsigned char)1 << bitnum));
+}
+
+
+unsigned int
+slapi_setbit_int(unsigned int f,unsigned int bitnum)
+{
+ return (f | ((unsigned int)1 << bitnum));
+}
+
+unsigned int
+slapi_unsetbit_int(unsigned int f,unsigned int bitnum)
+{
+ return (f & (~((unsigned int)1 << bitnum)));
+}
+
+int
+slapi_isbitset_int(unsigned int f,unsigned int bitnum)
+{
+ return (f & ((unsigned int)1 << bitnum));
+}
diff --git a/ldap/servers/slapd/bulk_import.c b/ldap/servers/slapd/bulk_import.c
new file mode 100644
index 00000000..ea531cb3
--- /dev/null
+++ b/ldap/servers/slapd/bulk_import.c
@@ -0,0 +1,149 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+/*
+ * 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.
+ */
+
+/* The functions in this file allow plugins to perform bulk import of data
+ comming over ldap connection. Note that the code will not work if
+ there is now active connection since import state is stored in the
+ connection extension */
+
+#include "slap.h"
+
+/* forward declarations */
+static int process_bulk_import_op (Slapi_PBlock *pb, int state, Slapi_Entry *e);
+
+/* This function initiates bulk import. The pblock must contain
+ SLAPI_LDIF2DB_GENERATE_UNIQUEID -- currently always set to TIME_BASED
+ SLAPI_CONNECTION -- connection over which bulk import is coming
+ SLAPI_BACKEND -- the backend being imported
+ or
+ SLAPI_TARGET_DN that contains root of the imported area.
+ The function returns LDAP_SUCCESS or LDAP error code
+*/
+
+int slapi_start_bulk_import (Slapi_PBlock *pb)
+{
+ return (process_bulk_import_op (pb, SLAPI_BI_STATE_START, NULL));
+}
+
+/* This function stops bulk import. The pblock must contain
+ SLAPI_CONNECTION -- connection over which bulk import is coming
+ SLAPI_BACKEND -- the backend being imported
+ or
+ SLAPI_TARGET_DN that contains root of the imported area.
+ The function returns LDAP_SUCCESS or LDAP error code
+*/
+int slapi_stop_bulk_import (Slapi_PBlock *pb)
+{
+ return (process_bulk_import_op (pb, SLAPI_BI_STATE_DONE, NULL));
+}
+
+/* This function adds an entry to the bulk import. The pblock must contain
+ SLAPI_CONNECTION -- connection over which bulk import is coming
+ SLAPI_BACKEND -- optional backend pointer; if missing computed based on entry dn
+ The function returns LDAP_SUCCESS or LDAP error code
+*/
+int slapi_import_entry (Slapi_PBlock *pb, Slapi_Entry *e)
+{
+ return (process_bulk_import_op (pb, SLAPI_BI_STATE_ADD, e));
+}
+
+static int
+process_bulk_import_op (Slapi_PBlock *pb, int state, Slapi_Entry *e)
+{
+ int rc;
+ Slapi_Backend *be = NULL;
+ char *dn = NULL;
+ Slapi_DN sdn;
+ const Slapi_DN *target_sdn = NULL;
+
+ if (pb == NULL)
+ {
+ slapi_log_error(SLAPI_LOG_FATAL, NULL, "process_bulk_import_op: NULL pblock\n");
+ return LDAP_OPERATIONS_ERROR;
+ }
+
+ if (state == SLAPI_BI_STATE_ADD && e == NULL)
+ {
+ slapi_log_error(SLAPI_LOG_FATAL, NULL, "process_bulk_import_op: NULL entry\n");
+ return LDAP_OPERATIONS_ERROR;
+ }
+
+ slapi_pblock_get (pb, SLAPI_BACKEND, &be);
+ if (be == NULL)
+ {
+ /* try to get dn to select backend */
+ if (e)
+ {
+ target_sdn = slapi_entry_get_sdn_const (e);
+ be = slapi_be_select (target_sdn);
+ }
+ else
+ {
+ slapi_sdn_init(&sdn);
+ slapi_pblock_get (pb, SLAPI_TARGET_DN, &dn);
+ if (dn)
+ {
+ slapi_sdn_init_dn_byref(&sdn, dn);
+ be = slapi_be_select (&sdn);
+ target_sdn = &sdn;
+ }
+ }
+
+ if (be)
+ {
+ if (state == SLAPI_BI_STATE_START && (!slapi_be_issuffix(be, target_sdn)))
+ {
+ slapi_log_error(SLAPI_LOG_FATAL, NULL,
+ "process_bulk_import_op: wrong backend suffix\n");
+ return LDAP_OPERATIONS_ERROR;
+ }
+ slapi_pblock_set (pb, SLAPI_BACKEND, be);
+ }
+ else
+ {
+ slapi_log_error(SLAPI_LOG_FATAL, NULL, "process_bulk_import_op: NULL backend\n");
+ return LDAP_OPERATIONS_ERROR;
+ }
+
+ if (NULL == e)
+ slapi_sdn_done (&sdn);
+ }
+
+ if (be->be_wire_import == NULL)
+ {
+ slapi_log_error(SLAPI_LOG_FATAL, NULL, "slapi_start_bulk_import: "
+ "bulk import is not supported by this (%s) backend\n",
+ be->be_type);
+ return LDAP_NOT_SUPPORTED;
+ }
+
+ /* set required parameters */
+ slapi_pblock_set (pb, SLAPI_BULK_IMPORT_STATE, &state);
+ if (e)
+ slapi_pblock_set (pb, SLAPI_BULK_IMPORT_ENTRY, e);
+
+ rc = be->be_wire_import (pb);
+ if (rc != 0)
+ {
+ if (rc != LDAP_BUSY)
+ slapi_log_error(SLAPI_LOG_FATAL, NULL, "slapi_start_bulk_import: "
+ "failed; error = %d\n", rc);
+ return (LDAP_BUSY == rc ? LDAP_BUSY : LDAP_OPERATIONS_ERROR);
+ }
+
+ return LDAP_SUCCESS;
+}
diff --git a/ldap/servers/slapd/ch_malloc.c b/ldap/servers/slapd/ch_malloc.c
new file mode 100644
index 00000000..c4d1e748
--- /dev/null
+++ b/ldap/servers/slapd/ch_malloc.c
@@ -0,0 +1,657 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+/* slapi_ch_malloc.c - malloc routines that test returns from malloc and friends */
+
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h> /* strdup */
+#include <sys/types.h>
+#ifndef _WIN32
+#include <sys/socket.h>
+#endif
+#undef DEBUG /* disable counters */
+#include <prcountr.h>
+#include "slap.h"
+
+static int counters_created= 0;
+PR_DEFINE_COUNTER(slapi_ch_counter_malloc);
+PR_DEFINE_COUNTER(slapi_ch_counter_calloc);
+PR_DEFINE_COUNTER(slapi_ch_counter_realloc);
+PR_DEFINE_COUNTER(slapi_ch_counter_strdup);
+PR_DEFINE_COUNTER(slapi_ch_counter_free);
+PR_DEFINE_COUNTER(slapi_ch_counter_created);
+PR_DEFINE_COUNTER(slapi_ch_counter_exist);
+
+#define OOM_PREALLOC_SIZE 65536
+static void *oom_emergency_area = NULL;
+static PRLock *oom_emergency_lock = NULL;
+
+#if defined(_WIN32)
+static int recording= 0;
+#endif
+
+#define SLAPD_MODULE "memory allocator"
+
+static const char* const oom_advice =
+ "\nThe server has probably allocated all available virtual memory. To solve\n"
+ "this problem, make more virtual memory available to your server, or reduce\n"
+ "one or more of the following server configuration settings:\n"
+ " nsslapd-cachesize (Database Settings - Maximum entries in cache)\n"
+ " nsslapd-cachememsize (Database Settings - Memory available for cache)\n"
+ " nsslapd-dbcachesize (LDBM Plug-in Settings - Maximum cache size)\n"
+ " nsslapd-import-cachesize (LDBM Plug-in Settings - Import cache size).\n"
+ "Can't recover; calling exit(1).\n";
+
+#if defined(_WIN32) && defined(DEBUG)
+static void add_memory_record(void *p,unsigned long size);
+static void remove_memory_record(void *p);
+static int memory_record_dump( caddr_t data, caddr_t arg );
+static int memory_record_delete( caddr_t data, caddr_t arg );
+#endif
+
+static void
+create_counters()
+{
+ PR_CREATE_COUNTER(slapi_ch_counter_malloc,"slapi_ch","malloc","");
+ PR_CREATE_COUNTER(slapi_ch_counter_calloc,"slapi_ch","calloc","");
+ PR_CREATE_COUNTER(slapi_ch_counter_realloc,"slapi_ch","realloc","");
+ PR_CREATE_COUNTER(slapi_ch_counter_strdup,"slapi_ch","strdup","");
+ PR_CREATE_COUNTER(slapi_ch_counter_free,"slapi_ch","free","");
+ PR_CREATE_COUNTER(slapi_ch_counter_created,"slapi_ch","created","");
+ PR_CREATE_COUNTER(slapi_ch_counter_exist,"slapi_ch","exist","");
+
+ /* ensure that we have space to allow for shutdown calls to malloc()
+ * from should we run out of memory.
+ */
+ if (oom_emergency_area == NULL) {
+ oom_emergency_area = malloc(OOM_PREALLOC_SIZE);
+ }
+ oom_emergency_lock = PR_NewLock();
+}
+
+/* called when we have just detected an out of memory condition, before
+ * we make any other library calls. Note that LDAPDebug() calls malloc,
+ * indirectly. By making 64KB free, we should be able to have a few
+ * mallocs' succeed before we shut down.
+ */
+void oom_occurred(void)
+{
+ int tmp_errno = errno; /* callers will need the error from malloc */
+ if (oom_emergency_lock == NULL) return;
+
+ PR_Lock(oom_emergency_lock);
+ if (oom_emergency_area) {
+ free(oom_emergency_area);
+ oom_emergency_area = NULL;
+ }
+ PR_Unlock(oom_emergency_lock);
+ errno = tmp_errno;
+}
+
+static void
+log_negative_alloc_msg( const char *op, const char *units, unsigned long size )
+{
+ slapi_log_error( SLAPI_LOG_FATAL, SLAPD_MODULE,
+ "cannot %s %lu %s;\n"
+ "trying to allocate 0 or a negative number of %s is not portable and\n"
+ "gives different results on different platforms.\n",
+ op, size, units, units );
+}
+
+char *
+slapi_ch_malloc(
+ unsigned long size
+)
+{
+ char *newmem;
+
+ if (size <= 0) {
+ log_negative_alloc_msg( "malloc", "bytes", size );
+ return 0;
+ }
+
+ if ( (newmem = (char *) malloc( size )) == NULL ) {
+ int oserr = errno;
+
+ oom_occurred();
+ slapi_log_error( SLAPI_LOG_FATAL, SLAPD_MODULE,
+ "malloc of %lu bytes failed; OS error %d (%s)%s\n",
+ size, oserr, slapd_system_strerror( oserr ), oom_advice );
+ exit( 1 );
+ }
+ if(!counters_created)
+ {
+ create_counters();
+ counters_created= 1;
+ }
+ PR_INCREMENT_COUNTER(slapi_ch_counter_malloc);
+ PR_INCREMENT_COUNTER(slapi_ch_counter_created);
+ PR_INCREMENT_COUNTER(slapi_ch_counter_exist);
+#if defined(_WIN32) && defined(DEBUG)
+ if(recording)
+ {
+ add_memory_record(newmem,size);
+ }
+#endif
+
+ return( newmem );
+}
+
+char *
+slapi_ch_realloc(
+ char *block,
+ unsigned long size
+)
+{
+ char *newmem;
+
+ if ( block == NULL ) {
+ return( slapi_ch_malloc( size ) );
+ }
+
+ if (size <= 0) {
+ log_negative_alloc_msg( "realloc", "bytes", size );
+ return block;
+ }
+
+ if ( (newmem = (char *) realloc( block, size )) == NULL ) {
+ int oserr = errno;
+
+ oom_occurred();
+ slapi_log_error( SLAPI_LOG_FATAL, SLAPD_MODULE,
+ "realloc of %lu bytes failed; OS error %d (%s)%s\n",
+ size, oserr, slapd_system_strerror( oserr ), oom_advice );
+ exit( 1 );
+ }
+ if(!counters_created)
+ {
+ create_counters();
+ counters_created= 1;
+ }
+ PR_INCREMENT_COUNTER(slapi_ch_counter_realloc);
+#if defined(_WIN32) && defined(DEBUG)
+ if(recording)
+ {
+ remove_memory_record(block);
+ add_memory_record(newmem,size);
+ }
+#endif
+
+ return( newmem );
+}
+
+char *
+slapi_ch_calloc(
+ unsigned long nelem,
+ unsigned long size
+)
+{
+ char *newmem;
+
+ if (size <= 0) {
+ log_negative_alloc_msg( "calloc", "bytes", size );
+ return 0;
+ }
+
+ if (nelem <= 0) {
+ log_negative_alloc_msg( "calloc", "elements", nelem );
+ return 0;
+ }
+
+ if ( (newmem = (char *) calloc( nelem, size )) == NULL ) {
+ int oserr = errno;
+
+ oom_occurred();
+ slapi_log_error( SLAPI_LOG_FATAL, SLAPD_MODULE,
+ "calloc of %lu elems of %lu bytes failed; OS error %d (%s)%s\n",
+ nelem, size, oserr, slapd_system_strerror( oserr ), oom_advice );
+ exit( 1 );
+ }
+ if(!counters_created)
+ {
+ create_counters();
+ counters_created= 1;
+ }
+ PR_INCREMENT_COUNTER(slapi_ch_counter_calloc);
+ PR_INCREMENT_COUNTER(slapi_ch_counter_created);
+ PR_INCREMENT_COUNTER(slapi_ch_counter_exist);
+#if defined(_WIN32) && defined(DEBUG)
+ if(recording)
+ {
+ add_memory_record(newmem,size);
+ }
+#endif
+ return( newmem );
+}
+
+char*
+slapi_ch_strdup ( const char* s1)
+{
+ char* newmem;
+
+ /* strdup pukes on NULL strings...bail out now */
+ if(NULL == s1)
+ return NULL;
+ newmem = strdup (s1);
+ if (newmem == NULL) {
+ int oserr = errno;
+ oom_occurred();
+
+ slapi_log_error( SLAPI_LOG_FATAL, SLAPD_MODULE,
+ "strdup of %lu characters failed; OS error %d (%s)%s\n",
+ (unsigned long)strlen(s1), oserr, slapd_system_strerror( oserr ),
+ oom_advice );
+ exit (1);
+ }
+ if(!counters_created)
+ {
+ create_counters();
+ counters_created= 1;
+ }
+ PR_INCREMENT_COUNTER(slapi_ch_counter_strdup);
+ PR_INCREMENT_COUNTER(slapi_ch_counter_created);
+ PR_INCREMENT_COUNTER(slapi_ch_counter_exist);
+#if defined(_WIN32) && defined(DEBUG)
+ if(recording)
+ {
+ add_memory_record(newmem,strlen(s1)+1);
+ }
+#endif
+ return newmem;
+}
+
+struct berval*
+slapi_ch_bvdup (const struct berval* v)
+{
+ struct berval* newberval = ber_bvdup ((struct berval *)v);
+ if (newberval == NULL) {
+ int oserr = errno;
+
+ oom_occurred();
+ slapi_log_error( SLAPI_LOG_FATAL, SLAPD_MODULE,
+ "ber_bvdup of %lu bytes failed; OS error %d (%s)%s\n",
+ (unsigned long)v->bv_len, oserr, slapd_system_strerror( oserr ),
+ oom_advice );
+ exit( 1 );
+ }
+ return newberval;
+}
+
+struct berval**
+slapi_ch_bvecdup (struct berval** v)
+{
+ struct berval** newberval = NULL;
+ if (v != NULL) {
+ size_t i = 0;
+ while (v[i] != NULL) ++i;
+ newberval = (struct berval**) slapi_ch_malloc ((i + 1) * sizeof (struct berval*));
+ newberval[i] = NULL;
+ while (i-- > 0) {
+ newberval[i] = slapi_ch_bvdup (v[i]);
+ }
+ }
+ return newberval;
+}
+
+/*
+ * Function: slapi_ch_free
+ *
+ * Returns: nothing
+ *
+ * Description: frees the pointer, and then sets it to NULL to
+ * prevent free-memory writes.
+ * Note: pass in the address of the pointer you want to free.
+ * Note: you can pass in null pointers, it's cool.
+ */
+void
+slapi_ch_free(void **ptr)
+{
+ if (ptr==NULL || *ptr == NULL){
+ return;
+ }
+
+#if defined(_WIN32) && defined(DEBUG)
+ if(recording)
+ {
+ remove_memory_record(*ptr);
+ }
+#endif
+ free (*ptr);
+ *ptr = NULL;
+ if(!counters_created)
+ {
+ create_counters();
+ counters_created= 1;
+ }
+ PR_INCREMENT_COUNTER(slapi_ch_counter_free);
+ PR_DECREMENT_COUNTER(slapi_ch_counter_exist);
+ return;
+}
+
+
+/* just like slapi_ch_free, takes the address of the struct berval pointer */
+void
+slapi_ch_bvfree(struct berval** v)
+{
+ if (v == NULL || *v == NULL)
+ return;
+
+ slapi_ch_free((void **)&((*v)->bv_val));
+ slapi_ch_free((void **)v);
+
+ return;
+}
+
+/* just like slapi_ch_free, but the argument is the address of a string
+ This helps with compile time error checking
+*/
+void
+slapi_ch_free_string(char **s)
+{
+ slapi_ch_free((void **)s);
+}
+
+/* ========================= NT Specific Leak Checking Code ================================== */
+
+#if defined(_WIN32) && defined(DEBUG)
+#define STOP_TRAVERSAL -2
+#define MR_CALL_STACK 16
+#define MR_DUMP_AMOUNT 16
+static Avlnode *mr_tree= NULL;
+static PRLock *mr_tree_lock= NULL;
+#endif
+
+void
+slapi_ch_start_recording()
+{
+#if defined(_WIN32) && defined(DEBUG)
+ if(mr_tree_lock==NULL)
+ {
+ mr_tree_lock = PR_NewLock();
+ }
+ PR_Lock( mr_tree_lock );
+ recording= 1;
+ PR_Unlock( mr_tree_lock );
+#endif
+}
+
+void
+slapi_ch_stop_recording()
+{
+#if defined(_WIN32) && defined(DEBUG)
+ PR_Lock( mr_tree_lock );
+ recording= 0;
+ avl_apply( mr_tree, memory_record_dump, NULL, STOP_TRAVERSAL, AVL_INORDER );
+ avl_free( mr_tree, memory_record_delete );
+ mr_tree= NULL;
+ PR_Unlock( mr_tree_lock );
+#endif
+}
+
+#if defined(_WIN32) && defined(DEBUG)
+
+struct memory_record
+{
+ void *p;
+ unsigned long size;
+ DWORD ra[MR_CALL_STACK];
+};
+
+
+static void
+mr_to_hex_dump(char* dest, void *addr, int size, int MaxBytes)
+{
+ int i;
+ for (i=0; i<MaxBytes; i++)
+ {
+ if(i<size)
+ {
+ wsprintf(dest+i*2, "%02x", ((unsigned char*)addr)[i]);
+ }
+ else
+ {
+ strcpy(dest+i*2, " ");
+ }
+ }
+}
+
+static void
+mr_to_char_dump(char* dest, void *addr, int size, int MaxBytes)
+{
+ int i;
+ char *c= (char*)addr;
+ for(i=0;i<MaxBytes;i++)
+ {
+ if(i<size)
+ {
+ *(dest+i)= (isprint(*c)?*c:'.');
+ c++;
+ }
+ else
+ {
+ *(dest+i)= ' ';
+ }
+ }
+ *(dest+i)= '\0';
+}
+
+
+/*
+ * Check that the address is (probably) valid
+ */
+static int ValidateBP(UINT bp)
+{
+ return !(IsBadReadPtr((void*)bp, 4) || IsBadWritePtr((void*)bp, 4));
+}
+
+/*
+ * Check that the address is (probably) valid
+ */
+static int ValidateIP(UINT ip)
+{
+ return !IsBadReadPtr((void*)ip, 4);
+}
+
+static int
+memory_record_delete( caddr_t data, caddr_t arg )
+{
+ struct memory_record *mr = (struct memory_record *)data;
+ free(mr);
+ return 0;
+}
+
+static int
+memory_record_duplicate_disallow( caddr_t d1, caddr_t d2 )
+{
+ return -1;
+}
+
+static int
+memory_record_compare( caddr_t d1, caddr_t d2 )
+{
+ struct memory_record *mr1 = (struct memory_record *)d1;
+ struct memory_record *mr2 = (struct memory_record *)d2;
+ return (mr1->p==mr2->p);
+}
+
+static void
+grab_stack(DWORD *ra,int framestograb,int framestoskip)
+{
+ int framelookingat = 0;
+ int framestoring = 0;
+ DWORD _bp = 0;
+
+ /* for every function the frame layout is:
+ * ---------
+ * |ret add|
+ * ---------
+ * |old bp | <- new bp
+ * ---------
+ */
+
+ __asm mov _bp, ebp;
+
+ if(framestoskip==0)
+ {
+ ra[framestoring]= _bp;
+ framestoring++;
+ }
+ while (framelookingat < framestograb+framestoskip-1)
+ {
+ DWORD returnAddress = *(((DWORD*)_bp)+1);
+ _bp = *((DWORD*)_bp);
+ if (!ValidateBP(_bp)) break;
+ if (!ValidateIP(returnAddress)) break;
+ if(framelookingat>=framestoskip)
+ {
+ ra[framestoring]= returnAddress;
+ framestoring++;
+ }
+ framelookingat++;
+ }
+ ra[framestoring]= 0;
+}
+
+static void
+add_memory_record(void *p,unsigned long size)
+{
+ struct memory_record *mr= (struct memory_record *)malloc(sizeof(struct memory_record));
+ mr->p= p;
+ mr->size= size;
+ grab_stack(mr->ra,MR_CALL_STACK,1);
+ PR_Lock( mr_tree_lock );
+ avl_insert( &mr_tree, mr, memory_record_compare, memory_record_duplicate_disallow );
+ PR_Unlock( mr_tree_lock );
+}
+
+static void
+remove_memory_record(void *p)
+{
+ struct memory_record *mr = NULL;
+ struct memory_record search;
+ PR_Lock( mr_tree_lock );
+ search.p= p;
+ mr = (struct memory_record *)avl_find( mr_tree, &search, memory_record_compare );
+ if(mr!=NULL)
+ {
+ avl_delete( &mr_tree, mr, memory_record_compare );
+ }
+ PR_Unlock( mr_tree_lock );
+}
+
+#include <imagehlp.h>
+#pragma comment(lib, "imagehlp")
+
+static BOOL SymInitialized= FALSE;
+static HANDLE s_hProcess= NULL;
+
+BOOL InitialiseImageHelp()
+{
+ if (!SymInitialized)
+ {
+ /*
+ * searchpath= <instancedir>\bin\slapd\server;<instancedir>\lib
+ */
+ char *searchpath= NULL;
+ char *id= config_get_instancedir();
+ if(id!=NULL)
+ {
+ char *p= id;
+ while(p!=NULL)
+ {
+ p= strchr(id,'/');
+ if(p!=NULL) *p='\\';
+ }
+ p= strrchr(id,'\\');
+ if(p!=NULL)
+ {
+ *p= '\0';
+ searchpath= slapi_ch_malloc(100+strlen(p)*2);
+ strcpy(searchpath,id);
+ strcat(searchpath,"\\bin\\slapd\\server;");
+ strcat(searchpath,id);
+ strcat(searchpath,"\\lib");
+ }
+ }
+ s_hProcess = GetCurrentProcess();
+ SymInitialized = SymInitialize(s_hProcess, searchpath, TRUE);
+ slapi_ch_free((void**)&id);
+ slapi_ch_free((void**)&searchpath);
+ if (SymInitialized)
+ {
+ SymSetOptions(SYMOPT_DEFERRED_LOADS);
+ }
+ }
+ return SymInitialized;
+}
+
+BOOL AddressToName(DWORD Addr, LPTSTR Str, int Max)
+{
+ DWORD base;
+ if (!InitialiseImageHelp())
+ return FALSE;
+ base = SymGetModuleBase(s_hProcess, Addr);
+ if (base)
+ {
+ struct
+ {
+ IMAGEHLP_SYMBOL ihs;
+ char NameBuf[256];
+ } SymInfo;
+ DWORD Displacement = 0;
+ SymInfo.ihs.SizeOfStruct = sizeof(SymInfo);
+ SymInfo.ihs.MaxNameLength = sizeof(SymInfo.NameBuf);
+ if (SymGetSymFromAddr(s_hProcess, Addr, &Displacement, &SymInfo.ihs))
+ {
+ if (Displacement)
+ _snprintf(Str, Max-1, "%s+%x", SymInfo.ihs.Name, Displacement);
+ else
+ _snprintf(Str, Max-1, "%s", SymInfo.ihs.Name);
+ return TRUE;
+ }
+ else
+ {
+ _snprintf(Str, Max, "SymGetSymFromAddr failed (%d)", GetLastError());
+ }
+ }
+ else
+ {
+ _snprintf(Str, Max, "SymGetModuleBase failed (%d)", GetLastError());
+ }
+ return FALSE;
+}
+
+static int
+memory_record_dump( caddr_t data, caddr_t arg )
+{
+ int frame= 0;
+ char b1[MR_DUMP_AMOUNT*2+1];
+ char b2[MR_DUMP_AMOUNT+1];
+ char b3[128];
+ int size= 0;
+ struct memory_record *mr = (struct memory_record *)data;
+ if(!IsBadReadPtr(mr->p, MR_DUMP_AMOUNT))
+ {
+ size= MR_DUMP_AMOUNT;
+ }
+ mr_to_hex_dump(b1, mr->p, size, MR_DUMP_AMOUNT);
+ mr_to_char_dump(b2, mr->p, size, MR_DUMP_AMOUNT);
+ sprintf(b3,"%p %ld %s %s",mr->p,mr->size,b1,b2);
+ LDAPDebug( LDAP_DEBUG_ANY, "%s\n",b3,0,0);
+ while(mr->ra[frame]!=0)
+ {
+ char fn[100];
+ AddressToName(mr->ra[frame], fn, 100);
+ LDAPDebug( LDAP_DEBUG_ANY, "%d %p %s\n",frame,mr->ra[frame],fn);
+ frame++;
+ }
+ return 0;
+}
+
+#endif
+
+
diff --git a/ldap/servers/slapd/charray.c b/ldap/servers/slapd/charray.c
new file mode 100644
index 00000000..77c264ca
--- /dev/null
+++ b/ldap/servers/slapd/charray.c
@@ -0,0 +1,354 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+/* charray.c - routines for dealing with char * arrays */
+
+#include <stdio.h>
+#include <string.h>
+#include <sys/types.h>
+#ifndef _WIN32
+#include <sys/socket.h>
+#endif
+#include "slap.h"
+
+void
+charray_add(
+ char ***a,
+ char *s
+)
+{
+ int n;
+
+ if ( *a == NULL ) {
+ *a = (char **) slapi_ch_malloc( 2 * sizeof(char *) );
+ n = 0;
+ } else {
+ for ( n = 0; *a != NULL && (*a)[n] != NULL; n++ ) {
+ ; /* NULL */
+ }
+
+ *a = (char **) slapi_ch_realloc( (char *) *a,
+ (n + 2) * sizeof(char *) );
+ }
+
+ /* At this point, *a may be different from the value it had when this
+ * function is called. Furthermore, *a[n] may contain an arbitrary
+ * value, such as a pointer to the middle of a unallocated area.
+ */
+
+#ifdef TEST_BELLATON
+ (*a)[n+1] = NULL;
+ (*a)[n] = s;
+#endif
+
+ /* Putting code back so that thread conflict can be made visible */
+
+ (*a)[n++] = s;
+ (*a)[n] = NULL;
+
+}
+
+void
+charray_merge(
+ char ***a,
+ char **s,
+ int copy_strs
+)
+{
+ int i, n, nn;
+
+ if ( (s == NULL) || (s[0] == NULL) )
+ return;
+
+ for ( n = 0; *a != NULL && (*a)[n] != NULL; n++ ) {
+ ; /* NULL */
+ }
+ for ( nn = 0; s[nn] != NULL; nn++ ) {
+ ; /* NULL */
+ }
+
+ *a = (char **) slapi_ch_realloc( (char *) *a, (n + nn + 1) * sizeof(char *) );
+
+ for ( i = 0; i < nn; i++ ) {
+ if ( copy_strs ) {
+ (*a)[n + i] = slapi_ch_strdup( s[i] );
+ } else {
+ (*a)[n + i] = s[i];
+ }
+ }
+ (*a)[n + nn] = NULL;
+}
+
+/* Routines which don't pound on malloc. Don't interchange the arrays with the
+ * regular calls---they can end up freeing non-heap memory, which is wrong */
+
+void
+cool_charray_free( char **array )
+{
+ slapi_ch_free((void**)&array);
+}
+
+/* Like strcpy, but returns a pointer to the next byte after the last one written to */
+static char *strcpy_len(char *dest, char *source)
+{
+ if('\0' == (*source)) {
+ return(dest);
+ }
+ do {
+ *dest++ = *source++;
+ } while (*source);
+ return dest;
+}
+
+char **
+cool_charray_dup( char **a )
+{
+ int i,size, num_strings;
+ char **newa;
+ char *p;
+
+ if ( a == NULL ) {
+ return( NULL );
+ }
+
+ for ( i = 0; a[i] != NULL; i++ )
+ ; /* NULL */
+
+ num_strings = i;
+ size = (i + 1) * sizeof(char *);
+
+ for ( i = 0; a[i] != NULL; i++ ) {
+ size += strlen( a[i] ) + 1;
+ }
+
+ newa = (char **) slapi_ch_malloc( size );
+
+ p = (char *) &(newa[num_strings + 1]);
+
+ for ( i = 0; a[i] != NULL; i++ ) {
+ newa[i] = p;
+ p = strcpy_len(p, a[i] );
+ *p++ = '\0';
+ }
+ newa[i] = NULL;
+
+ return( newa );
+}
+
+void
+charray_free( char **array )
+{
+ char **a;
+
+ if ( array == NULL ) {
+ return;
+ }
+
+ for ( a = array; *a != NULL; a++ )
+ {
+ char *tmp= *a;
+ slapi_ch_free((void**)&tmp);
+ }
+ slapi_ch_free( (void**)&array );
+}
+
+/*
+ * charray_free version for plugins: there is a need for plugins to free
+ * the ch_arrays returned by functions like:
+ * slapi_get_supported_extended_ops_copy
+ * slapi_get_supported_saslmechanisms_copy
+ * slapi_get_supported_controls_copy
+ */
+void
+slapi_ch_array_free( char **array )
+{
+ charray_free (array);
+}
+
+
+/* case insensitive search */
+int
+charray_inlist(
+ char **a,
+ 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 );
+}
+
+/* case insensitive search covering non-ascii */
+int
+charray_utf8_inlist(
+ char **a,
+ char *s
+)
+{
+ int i;
+
+ if ( a == NULL ) {
+ return( 0 );
+ }
+
+ for ( i = 0; a[i] != NULL; i++ ) {
+ if (!slapi_UTF8CASECMP(a[i], s)) {
+ return( 1 );
+ }
+ }
+
+ return( 0 );
+}
+
+char **
+charray_dup( char **a )
+{
+ int i;
+ char **newa;
+
+ if ( a == NULL ) {
+ return( NULL );
+ }
+
+ for ( i = 0; a[i] != NULL; i++ )
+ ; /* NULL */
+
+ newa = (char **) slapi_ch_malloc( (i + 1) * sizeof(char *) );
+
+ for ( i = 0; a[i] != NULL; i++ ) {
+ newa[i] = slapi_ch_strdup( a[i] );
+ }
+ newa[i] = NULL;
+
+ return( newa );
+}
+
+char **
+str2charray( char *str, char *brkstr )
+{
+ char **res;
+ char *s;
+ int i;
+ char * iter = NULL;
+
+ i = 1;
+ for ( s = str; *s; s++ ) {
+ if ( strchr( brkstr, *s ) != NULL ) {
+ i++;
+ }
+ }
+
+ res = (char **) slapi_ch_malloc( (i + 1) * sizeof(char *) );
+ i = 0;
+ for ( s = ldap_utf8strtok_r( str, brkstr , &iter); s != NULL;
+ s = ldap_utf8strtok_r( NULL, brkstr , &iter) ) {
+ res[i++] = slapi_ch_strdup( s );
+ }
+ res[i] = NULL;
+
+ return( res );
+}
+
+void
+charray_print( char **a )
+{
+ int i;
+
+ printf( "charray_print:\n");
+ for ( i = 0; a!= NULL && a[i] != NULL; i++ ) {
+ printf( "\t%s\n", a[i]);
+ }
+}
+
+/*
+ * Remove the char string from the array of char strings.
+ * Performs a case *insensitive* comparison!
+ * Just shunts the strings down to cover the deleted string.
+ * Doesn't free up the unused memory.
+ * Returns 1 if the entry found and removed, 0 if not.
+ */
+int
+charray_remove(
+ char **a,
+ const char *s
+)
+{
+ int i;
+ int found= 0;
+ for ( i=0; a!= NULL && a[i] != NULL; i++ )
+ {
+ if ( !found && strcasecmp (a[i],s) == 0 )
+ {
+ found= 1;
+ }
+ if (found)
+ {
+ a[i]= a[i+1];
+ }
+ }
+ return found;
+}
+
+/*
+ * if c == NULL, a = a - b
+ * if c != NULL, *c = a - b
+ */
+#define SUBTRACT_DEL (char *)(-1)
+void
+charray_subtract(char **a, char **b, char ***c)
+{
+ char **bp, **cp, **tmp;
+ char **p;
+
+ if (c)
+ tmp = *c = cool_charray_dup(a);
+ else
+ tmp = a;
+
+ for (cp = tmp; cp && *cp; cp++) {
+ for (bp = b; bp && *bp; bp++) {
+ if (!slapi_UTF8CASECMP(*cp, *bp)) {
+ slapi_ch_free((void **)&*cp);
+ *cp = SUBTRACT_DEL;
+ break;
+ }
+ }
+ }
+
+ for (cp = tmp; cp && *cp; cp++) {
+ if (*cp == SUBTRACT_DEL) {
+ for (p = cp+1; *p && *p == (char *)SUBTRACT_DEL; p++)
+ ;
+ *cp = *p;
+ if (*p == NULL)
+ break;
+ else
+ *p = SUBTRACT_DEL;
+ }
+ }
+}
+
+int
+charray_get_index(char **array, char *s)
+{
+ int i;
+
+ for (i = 0; array && array[i]; i++)
+ {
+ if (!slapi_UTF8CASECMP(array[i], s))
+ return i;
+ }
+ return -1;
+}
diff --git a/ldap/servers/slapd/compare.c b/ldap/servers/slapd/compare.c
new file mode 100644
index 00000000..8030871b
--- /dev/null
+++ b/ldap/servers/slapd/compare.c
@@ -0,0 +1,153 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+/*
+ * 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 <stdio.h>
+#include <sys/types.h>
+#ifndef _WIN32
+#include <sys/socket.h>
+#endif
+#include "slap.h"
+#include "pratom.h"
+
+
+void
+do_compare( Slapi_PBlock *pb )
+{
+ BerElement *ber = pb->pb_op->o_ber;
+ char *dn;
+ struct ava ava;
+ Slapi_Backend *be = NULL;
+ int err;
+ char ebuf[ BUFSIZ ];
+ Slapi_DN sdn;
+ Slapi_Entry *referral;
+ char errorbuf[BUFSIZ];
+
+ LDAPDebug( LDAP_DEBUG_TRACE, "do_compare\n", 0, 0, 0 );
+
+ /* count the compare request */
+ PR_AtomicIncrement(g_get_global_snmp_vars()->ops_tbl.dsCompareOps);
+
+ /*
+ * Parse the compare request. It looks like this:
+ *
+ * CompareRequest := [APPLICATION 14] SEQUENCE {
+ * entry DistinguishedName,
+ * ava SEQUENCE {
+ * type AttributeType,
+ * value AttributeValue
+ * }
+ * }
+ */
+
+
+ if ( ber_scanf( ber, "{a{ao}}", &dn, &ava.ava_type,
+ &ava.ava_value ) == LBER_ERROR ) {
+ LDAPDebug( LDAP_DEBUG_ANY,
+ "ber_scanf failed (op=Compare; params=DN,Type,Value)\n",
+ 0, 0, 0 );
+ send_ldap_result( pb, LDAP_PROTOCOL_ERROR, NULL, NULL, 0,
+ NULL );
+ return;
+ }
+ /*
+ * in LDAPv3 there can be optional control extensions on
+ * the end of an LDAPMessage. we need to read them in and
+ * pass them to the backend.
+ */
+ if ( (err = get_ldapmessage_controls( pb, ber, NULL )) != 0 ) {
+ send_ldap_result( pb, err, NULL, NULL, 0, NULL );
+ goto free_and_return;
+ }
+ slapi_sdn_init_dn_passin(&sdn,dn);
+
+ /* target spec is used to decide which plugins are applicable for the operation */
+ operation_set_target_spec (pb->pb_op, &sdn);
+
+ LDAPDebug( LDAP_DEBUG_ARGS, "do_compare: dn (%s) attr (%s)\n",
+ dn, ava.ava_type, 0 );
+
+ slapi_log_access( LDAP_DEBUG_STATS,
+ "conn=%d op=%d CMP dn=\"%s\" attr=\"%s\"\n",
+ pb->pb_conn->c_connid, pb->pb_op->o_opid,
+ escape_string( dn, ebuf ), ava.ava_type );
+
+ /*
+ * We could be serving multiple database backends. Select the
+ * appropriate one.
+ */
+ if ((err = slapi_mapping_tree_select(pb, &be, &referral, errorbuf)) != LDAP_SUCCESS) {
+ send_ldap_result(pb, err, NULL, errorbuf, 0, NULL);
+ be = NULL;
+ goto free_and_return;
+ }
+
+ if (referral)
+ {
+ int managedsait;
+
+ slapi_pblock_get(pb, SLAPI_MANAGEDSAIT, &managedsait);
+ if (managedsait)
+ {
+ send_ldap_result(pb, LDAP_UNWILLING_TO_PERFORM, NULL,
+ "cannot compare referral", 0, NULL);
+ slapi_entry_free(referral);
+ goto free_and_return;
+ }
+
+ send_referrals_from_entry(pb,referral);
+ slapi_entry_free(referral);
+ goto free_and_return;
+ }
+
+ if ( be->be_compare != NULL ) {
+ int isroot;
+
+ slapi_pblock_set( pb, SLAPI_BACKEND, be );
+ isroot = pb->pb_op->o_isroot;
+
+ slapi_pblock_set( pb, SLAPI_REQUESTOR_ISROOT, &isroot );
+ slapi_pblock_set( pb, SLAPI_COMPARE_TARGET, (void*)slapi_sdn_get_ndn(&sdn) );
+ slapi_pblock_set( pb, SLAPI_COMPARE_TYPE, ava.ava_type);
+ slapi_pblock_set( pb, SLAPI_COMPARE_VALUE, &ava.ava_value );
+ /*
+ * call the pre-compare plugins. if they succeed, call
+ * the backend compare function. then call the
+ * post-compare plugins.
+ */
+ if ( plugin_call_plugins( pb,
+ SLAPI_PLUGIN_PRE_COMPARE_FN ) == 0 ) {
+ int rc;
+
+ slapi_pblock_set( pb, SLAPI_PLUGIN, be->be_database );
+ set_db_default_result_handlers(pb);
+ rc = (*be->be_compare)( pb );
+
+ slapi_pblock_set( pb, SLAPI_PLUGIN_OPRETURN, &rc );
+ plugin_call_plugins( pb, SLAPI_PLUGIN_POST_COMPARE_FN );
+ }
+ } else {
+ send_ldap_result( pb, LDAP_UNWILLING_TO_PERFORM, NULL,
+ "Function not implemented", 0, NULL );
+ }
+
+free_and_return:;
+ if (be)
+ slapi_be_Unlock(be);
+ slapi_sdn_done(&sdn);
+ ava_done( &ava );
+}
diff --git a/ldap/servers/slapd/computed.c b/ldap/servers/slapd/computed.c
new file mode 100644
index 00000000..638c8159
--- /dev/null
+++ b/ldap/servers/slapd/computed.c
@@ -0,0 +1,208 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+
+/* Handles computed attributes for entries as they're returned to the client */
+
+#include "slap.h"
+
+
+/* Structure used to pass the context needed for completing a computed attribute operation */
+struct _computed_attr_context {
+ BerElement *ber;
+ int attrsonly;
+ char *requested_type;
+ Slapi_PBlock *pb;
+};
+
+
+struct _compute_evaluator {
+ struct _compute_evaluator *next;
+ slapi_compute_callback_t function;
+};
+typedef struct _compute_evaluator compute_evaluator;
+
+static compute_evaluator *compute_evaluators = NULL;
+static PRRWLock *compute_evaluators_lock = NULL;
+
+static int
+compute_stock_evaluator(computed_attr_context *c,char* type,Slapi_Entry *e,slapi_compute_output_t outputfn);
+
+struct _compute_rewriter {
+ struct _compute_rewriter *next;
+ slapi_search_rewrite_callback_t function;
+};
+typedef struct _compute_rewriter compute_rewriter;
+
+static compute_rewriter *compute_rewriters = NULL;
+static PRRWLock *compute_rewriters_lock = NULL;
+
+/* Function called by evaluators to have the value output */
+static int
+compute_output_callback(computed_attr_context *c,Slapi_Attr *a , Slapi_Entry *e)
+{
+ return encode_attr (c->pb, c->ber, e, a, c->attrsonly, c->requested_type);
+}
+
+static int
+compute_call_evaluators(computed_attr_context *c,slapi_compute_output_t outfn,char *type,Slapi_Entry *e)
+{
+ int rc = -1;
+ compute_evaluator *current = NULL;
+ /* Walk along the list (locked) calling the evaluator functions util one says yes, an error happens, or we finish */
+ PR_RWLock_Rlock(compute_evaluators_lock);
+ for (current = compute_evaluators; (current != NULL) && (-1 == rc); current = current->next) {
+ rc = (*(current->function))(c,type,e,outfn);
+ }
+ PR_RWLock_Unlock(compute_evaluators_lock);
+ return rc;
+}
+
+/* Returns : -1 if no attribute matched the requested type */
+/* 0 if one matched and it was processed without error */
+/* >0 if an error happened */
+int
+compute_attribute(char *type, Slapi_PBlock *pb,BerElement *ber,Slapi_Entry *e,int attrsonly,char *requested_type)
+{
+ computed_attr_context context;
+
+ context.ber = ber;
+ context.attrsonly = attrsonly;
+ context.requested_type = requested_type;
+ context.pb = pb;
+
+ return compute_call_evaluators(&context,compute_output_callback,type,e);
+}
+
+static int
+compute_stock_evaluator(computed_attr_context *c,char* type,Slapi_Entry *e,slapi_compute_output_t outputfn)
+{
+ int rc= -1;
+ static char* subschemasubentry = "subschemasubentry";
+
+ if ( strcasecmp (type, subschemasubentry ) == 0)
+ {
+ Slapi_Attr our_attr;
+ slapi_attr_init(&our_attr, subschemasubentry);
+ our_attr.a_flags = SLAPI_ATTR_FLAG_OPATTR;
+ valueset_add_string(&our_attr.a_present_values,SLAPD_SCHEMA_DN,CSN_TYPE_UNKNOWN,NULL);
+ rc = (*outputfn) (c, &our_attr, e);
+ attr_done(&our_attr);
+ return (rc);
+ }
+ return rc; /* I see no ships */
+}
+
+int slapi_compute_add_evaluator(slapi_compute_callback_t function)
+{
+ int rc = 0;
+ compute_evaluator *new_eval = NULL;
+ PR_ASSERT(NULL != function);
+ PR_ASSERT(NULL != compute_evaluators_lock);
+ PR_RWLock_Wlock(compute_evaluators_lock);
+ new_eval = calloc(1,sizeof (compute_evaluator));
+ if (NULL == new_eval) {
+ rc = ENOMEM;
+ } else {
+ new_eval->next = compute_evaluators;
+ new_eval->function = function;
+ compute_evaluators = new_eval;
+ }
+ PR_RWLock_Unlock(compute_evaluators_lock);
+ return rc;
+}
+
+/* Call this on server startup, before the first LDAP operation is serviced */
+int compute_init()
+{
+ /* Initialize the lock */
+ compute_evaluators_lock = PR_NewRWLock( PR_RWLOCK_RANK_NONE, "compute_attr_lock" );
+ if (NULL == compute_evaluators_lock) {
+ /* Out of resources */
+ return ENOMEM;
+ }
+ compute_rewriters_lock = PR_NewRWLock( PR_RWLOCK_RANK_NONE, "compute_rewriters_lock" );
+ if (NULL == compute_rewriters_lock) {
+ /* Out of resources */
+ return ENOMEM;
+ }
+ /* Now add the stock evaluators to the list */
+ return slapi_compute_add_evaluator(compute_stock_evaluator);
+}
+
+/* Call this on server shutdown, after the last LDAP operation has
+terminated */
+int compute_terminate()
+{
+ /* Free the list */
+ if (NULL != compute_evaluators_lock) {
+ compute_evaluator *current = compute_evaluators;
+ PR_RWLock_Wlock(compute_evaluators_lock);
+ while (current != NULL) {
+ compute_evaluator *asabird = current;
+ current = current->next;
+ free(asabird);
+ }
+ PR_RWLock_Unlock(compute_evaluators_lock);
+ /* Free the lock */
+ PR_DestroyRWLock(compute_evaluators_lock);
+ }
+ if (NULL != compute_rewriters_lock) {
+ compute_rewriter *current = compute_rewriters;
+ PR_RWLock_Wlock(compute_rewriters_lock);
+ while (current != NULL) {
+ compute_rewriter *asabird = current;
+ current = current->next;
+ free(asabird);
+ }
+ PR_RWLock_Unlock(compute_rewriters_lock);
+ PR_DestroyRWLock(compute_rewriters_lock);
+ }
+ return 0;
+}
+
+/* Functions dealing with re-writing of search filters */
+
+int slapi_compute_add_search_rewriter(slapi_search_rewrite_callback_t function)
+{
+ int rc = 0;
+ compute_rewriter *new_rewriter = NULL;
+ PR_ASSERT(NULL != function);
+ PR_ASSERT(NULL != compute_rewriters_lock);
+ new_rewriter = calloc(1,sizeof (compute_rewriter));
+ if (NULL == new_rewriter) {
+ rc = ENOMEM;
+ } else {
+ PR_RWLock_Wlock(compute_rewriters_lock);
+ new_rewriter->next = compute_rewriters;
+ new_rewriter->function = function;
+ compute_rewriters = new_rewriter;
+ PR_RWLock_Unlock(compute_rewriters_lock);
+ }
+ return rc;
+}
+
+int compute_rewrite_search_filter(Slapi_PBlock *pb)
+{
+ /* Iterate through the listed rewriters until one says it matched */
+ int rc = -1;
+ compute_rewriter *current = NULL;
+ /* Walk along the list (locked) calling the evaluator functions util one says yes, an error happens, or we finish */
+ PR_RWLock_Rlock(compute_rewriters_lock);
+ for (current = compute_rewriters; (current != NULL) && (-1 == rc); current = current->next) {
+ rc = (*(current->function))(pb);
+ /* Meaning of the return code :
+ -1 : keep looking
+ 0 : rewrote OK
+ 1 : refuse to do this search
+ 2 : operations error
+ */
+ }
+ PR_RWLock_Unlock(compute_rewriters_lock);
+ return rc;
+
+}
+
+
diff --git a/ldap/servers/slapd/config.c b/ldap/servers/slapd/config.c
new file mode 100644
index 00000000..2e3170a4
--- /dev/null
+++ b/ldap/servers/slapd/config.c
@@ -0,0 +1,459 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+/* config.c - configuration file handling routines */
+
+#include <stdio.h>
+#include <string.h>
+#include <sys/types.h>
+#include <errno.h>
+#include <stdlib.h>
+#ifdef _WIN32
+#include <direct.h> /* for getcwd */
+#else
+#include <sys/socket.h>
+#include <sys/param.h>
+#include <unistd.h>
+#include <pwd.h>
+#endif
+#include "slap.h"
+#include "pw.h"
+#include <sys/stat.h>
+#include <prio.h>
+
+#define MAXARGS 1000
+
+
+extern int should_detach;
+extern Slapi_PBlock *repl_pb;
+
+
+extern char* slapd_SSL3ciphers;
+
+#ifndef _WIN32
+extern char *localuser;
+#endif
+
+char* rel2abspath( char * );
+
+/*
+ See if the given entry has an attribute with the given name and the
+ given value; if value is NULL, just test for the presence of the given
+ attribute; if value is an empty string (i.e. value[0] == 0),
+ the first value in the attribute will be copied into the given buffer
+ and returned
+*/
+static int
+entry_has_attr_and_value(Slapi_Entry *e, const char *attrname,
+ char *value, size_t valuebufsize )
+{
+ int retval = 0;
+ Slapi_Attr *attr = 0;
+ if (!e || !attrname)
+ return retval;
+
+ /* see if the entry has the specified attribute name */
+ if (!slapi_entry_attr_find(e, attrname, &attr) && attr)
+ {
+ /* if value is not null, see if the attribute has that
+ value */
+ if (!value)
+ {
+ retval = 1;
+ }
+ else
+ {
+ Slapi_Value *v = 0;
+ int index = 0;
+ for (index = slapi_attr_first_value(attr, &v);
+ v && (index != -1);
+ index = slapi_attr_next_value(attr, index, &v))
+ {
+ const char *s = slapi_value_get_string(v);
+ if (!s)
+ continue;
+
+ if (!*value)
+ {
+ size_t len = strlen(s);
+
+ if ( len < valuebufsize )
+ {
+ strcpy(value, s);
+ retval = 1;
+ }
+ else
+ {
+ slapi_log_error( SLAPI_LOG_FATAL, "bootstrap config",
+ "Ignoring extremely large value for"
+ " configuration attribute %s"
+ " (length=%d, value=%40.40s...)\n",
+ attrname, len, s );
+ retval = 0; /* value is too large: ignore it */
+ }
+ break;
+ }
+ else if (!strcasecmp(s, value))
+ {
+ retval = 1;
+ break;
+ }
+ }
+ }
+ }
+
+ return retval;
+}
+
+
+/*
+ Extract just the configuration information we need for bootstrapping
+ purposes
+ 1) set up error logging
+ 2) disable syntax checking
+ 3) load the syntax plugins
+ etc.
+*/
+int
+slapd_bootstrap_config(const char *configdir)
+{
+ char configfile[MAXPATHLEN+1];
+ PRFileInfo prfinfo;
+ int rc = 0; /* Fail */
+ int done = 0;
+ PRInt32 nr = 0;
+ PRFileDesc *prfd = 0;
+ char *buf = 0;
+ char *lastp = 0;
+ char *entrystr = 0;
+
+ PR_snprintf(configfile, sizeof(configfile), "%s/%s", configdir,
+ CONFIG_FILENAME);
+ if ( (rc = PR_GetFileInfo( configfile, &prfinfo )) != PR_SUCCESS )
+ {
+ /* the "real" file does not exist; see if there is a tmpfile */
+ char tmpfile[MAXPATHLEN+1];
+ PR_snprintf(tmpfile, sizeof(tmpfile), "%s/%s.tmp", configdir,
+ CONFIG_FILENAME);
+ if ( PR_GetFileInfo( tmpfile, &prfinfo ) == PR_SUCCESS ) {
+ rc = PR_Rename(tmpfile, configfile);
+ if (rc == PR_SUCCESS) {
+ slapi_log_error(SLAPI_LOG_FATAL, "config",
+ "The configuration file %s was restored from backup %s\n",
+ configfile, tmpfile);
+ rc = 1;
+ } else {
+ slapi_log_error(SLAPI_LOG_FATAL, "config",
+ "The configuration file %s was not restored from backup %s, error %d\n",
+ configfile, tmpfile, rc);
+ rc = 0;
+ }
+ } else {
+ rc = 0; /* fail */
+ }
+ }
+ if ( (rc = PR_GetFileInfo( configfile, &prfinfo )) != PR_SUCCESS )
+ {
+ PRErrorCode prerr = PR_GetError();
+ slapi_log_error(SLAPI_LOG_FATAL, "config", "The given config file %s could not be accessed, " SLAPI_COMPONENT_NAME_NSPR " error %d (%s)\n",
+ configfile, prerr, slapd_pr_strerror(prerr));
+ rc = 0; /* Fail */
+ }
+ else if (( prfd = PR_Open( configfile, PR_RDONLY,
+ SLAPD_DEFAULT_FILE_MODE )) == NULL )
+ {
+ PRErrorCode prerr = PR_GetError();
+ slapi_log_error(SLAPI_LOG_FATAL, "config", "The given config file %s could not be opened for reading, " SLAPI_COMPONENT_NAME_NSPR " error %d (%s)\n",
+ configfile, prerr, slapd_pr_strerror(prerr));
+ rc = 0; /* Fail */
+ }
+ else
+ {
+ /* read the entire file into core */
+ buf = slapi_ch_malloc( prfinfo.size + 1 );
+ if (( nr = slapi_read_buffer( prfd, buf, prfinfo.size )) < 0 )
+ {
+ slapi_log_error(SLAPI_LOG_FATAL, "config", "Could only read %d of %d bytes from config file %s\n",
+ nr, prfinfo.size, configfile);
+ rc = 0; /* Fail */
+ done= 1;
+ }
+
+ (void)PR_Close(prfd);
+ buf[ nr ] = '\0';
+
+ if(!done)
+ {
+ char errorlog[MAXPATHLEN+1];
+ char loglevel[BUFSIZ];
+ char maxdescriptors[BUFSIZ];
+ char val[BUFSIZ];
+ char logenabled[BUFSIZ];
+ char schemacheck[BUFSIZ];
+ Slapi_DN plug_dn;
+
+ errorlog[0] = loglevel[0] = maxdescriptors[0] = '\0';
+ val[0] = logenabled[0] = schemacheck[0] = '\0';
+
+ /* Convert LDIF to entry structures */
+ slapi_sdn_init_dn_byref(&plug_dn, PLUGIN_BASE_DN);
+ while ((entrystr = dse_read_next_entry(buf, &lastp)) != NULL)
+ {
+ char errorbuf[BUFSIZ];
+ /*
+ * XXXmcs: it would be better to also pass
+ * SLAPI_STR2ENTRY_REMOVEDUPVALS in the flags, but
+ * duplicate value checking requires that the syntax
+ * and schema subsystems be initialized... and they
+ * are not yet.
+ */
+ Slapi_Entry *e = slapi_str2entry(entrystr,
+ SLAPI_STR2ENTRY_NOT_WELL_FORMED_LDIF);
+ if (e == NULL)
+ {
+ LDAPDebug(LDAP_DEBUG_ANY, "The entry [%s] in the configfile %s was empty or could not be parsed\n",
+ entrystr, configfile, 0);
+ continue;
+ }
+
+ /* increase file descriptors */
+#if !defined(_WIN32) && !defined(AIX)
+ if (!maxdescriptors[0] &&
+ entry_has_attr_and_value(e, CONFIG_MAXDESCRIPTORS_ATTRIBUTE,
+ maxdescriptors, sizeof(maxdescriptors)))
+ {
+ if (config_set_maxdescriptors(
+ CONFIG_MAXDESCRIPTORS_ATTRIBUTE,
+ maxdescriptors, errorbuf, CONFIG_APPLY)
+ != LDAP_SUCCESS)
+ {
+ LDAPDebug(LDAP_DEBUG_ANY, "%s: %s: %s\n", configfile,
+ CONFIG_MAXDESCRIPTORS_ATTRIBUTE, errorbuf);
+ }
+ }
+#endif /* !defined(_WIN32) && !defined(AIX) */
+
+ /* see if we need to enable error logging */
+ if (!logenabled[0] &&
+ entry_has_attr_and_value(e,
+ CONFIG_ERRORLOG_LOGGING_ENABLED_ATTRIBUTE,
+ logenabled, sizeof(logenabled)))
+ {
+ if (log_set_logging(
+ CONFIG_ERRORLOG_LOGGING_ENABLED_ATTRIBUTE,
+ logenabled, SLAPD_ERROR_LOG, errorbuf, CONFIG_APPLY)
+ != LDAP_SUCCESS)
+ {
+ LDAPDebug(LDAP_DEBUG_ANY, "%s: %s: %s\n", configfile,
+ CONFIG_ERRORLOG_LOGGING_ENABLED_ATTRIBUTE, errorbuf);
+ }
+ }
+
+ /* set the log file name */
+ if (!errorlog[0] &&
+ entry_has_attr_and_value(e, CONFIG_ERRORLOG_ATTRIBUTE,
+ errorlog, sizeof(errorlog)))
+ {
+ if (config_set_errorlog(CONFIG_ERRORLOG_ATTRIBUTE,
+ errorlog, errorbuf, CONFIG_APPLY) != LDAP_SUCCESS)
+ {
+ LDAPDebug(LDAP_DEBUG_ANY, "%s: %s: %s. \n", configfile,
+ CONFIG_ERRORLOG_ATTRIBUTE, errorbuf);
+ }
+ }
+
+ /* set the error log level */
+ if (!loglevel[0] &&
+ entry_has_attr_and_value(e, CONFIG_LOGLEVEL_ATTRIBUTE,
+ loglevel, sizeof(loglevel)))
+ {
+ if (should_detach || !config_get_errorlog_level())
+ { /* -d wasn't on command line */
+ if (config_set_errorlog_level(CONFIG_LOGLEVEL_ATTRIBUTE,
+ loglevel, errorbuf, CONFIG_APPLY) != LDAP_SUCCESS)
+ {
+ LDAPDebug(LDAP_DEBUG_ANY, "%s: %s: %s. \n", configfile,
+ CONFIG_LOGLEVEL_ATTRIBUTE, errorbuf);
+ }
+ }
+ else
+ {
+ LDAPDebug(LDAP_DEBUG_ANY,
+ "%s: ignoring %s (since -d %d was given on "
+ "the command line)\n",
+ CONFIG_LOGLEVEL_ATTRIBUTE, loglevel,
+ config_get_errorlog_level());
+ }
+ }
+
+ /* see if the entry is a child of the plugin base dn */
+ if (slapi_sdn_isparent(&plug_dn,
+ slapi_entry_get_sdn_const(e)))
+ {
+ if (entry_has_attr_and_value(e, "objectclass",
+ "nsSlapdPlugin", 0) &&
+ (entry_has_attr_and_value(e, ATTR_PLUGIN_TYPE,
+ "syntax", 0) ||
+ entry_has_attr_and_value(e, ATTR_PLUGIN_TYPE,
+ "matchingrule", 0)))
+ {
+ /* add the syntax/matching scheme rule plugin */
+ if (plugin_setup(e, 0, 0, 1))
+ {
+ LDAPDebug(LDAP_DEBUG_ANY, "The plugin entry [%s] in the configfile %s was invalid\n", slapi_entry_get_dn(e), configfile, 0);
+ rc = 0;
+ goto bail;
+ }
+ }
+ }
+ /* see if the entry is a grand child of the plugin base dn */
+ if (slapi_sdn_isgrandparent(&plug_dn,
+ slapi_entry_get_sdn_const(e)))
+ {
+ if (entry_has_attr_and_value(e, "objectclass",
+ "nsSlapdPlugin", 0) &&
+ ( entry_has_attr_and_value(e, ATTR_PLUGIN_TYPE,
+ "pwdstoragescheme", 0) ||
+ entry_has_attr_and_value(e, ATTR_PLUGIN_TYPE,
+ "reverpwdstoragescheme", 0) ) )
+ {
+ /* add the pwd storage scheme rule plugin */
+ if (plugin_setup(e, 0, 0, 1))
+ {
+ LDAPDebug(LDAP_DEBUG_ANY, "The plugin entry [%s] in the configfile %s was invalid\n", slapi_entry_get_dn(e), configfile, 0);
+ rc = 0;
+ goto bail;
+ }
+ }
+ }
+
+ /* see if we need to disable schema checking */
+ if (!schemacheck[0] &&
+ entry_has_attr_and_value(e, CONFIG_SCHEMACHECK_ATTRIBUTE,
+ schemacheck, sizeof(schemacheck)))
+ {
+ if (config_set_schemacheck(CONFIG_SCHEMACHECK_ATTRIBUTE,
+ schemacheck, errorbuf, CONFIG_APPLY)
+ != LDAP_SUCCESS)
+ {
+ LDAPDebug(LDAP_DEBUG_ANY, "%s: %s: %s\n", configfile,
+ CONFIG_SCHEMACHECK_ATTRIBUTE, errorbuf);
+ }
+ }
+
+ /* see if we need to expect quoted schema values */
+ if (entry_has_attr_and_value(e, CONFIG_ENQUOTE_SUP_OC_ATTRIBUTE,
+ val, sizeof(val)))
+ {
+ if (config_set_enquote_sup_oc(
+ CONFIG_ENQUOTE_SUP_OC_ATTRIBUTE, val, errorbuf,
+ CONFIG_APPLY) != LDAP_SUCCESS)
+ {
+ LDAPDebug(LDAP_DEBUG_ANY, "%s: %s: %s\n", configfile,
+ CONFIG_ENQUOTE_SUP_OC_ATTRIBUTE, errorbuf);
+ }
+ val[0] = 0;
+ }
+
+ /* see if we need to maintain case in AT and OC names */
+ if (entry_has_attr_and_value(e,
+ CONFIG_RETURN_EXACT_CASE_ATTRIBUTE, val, sizeof(val)))
+ {
+ if (config_set_return_exact_case(
+ CONFIG_RETURN_EXACT_CASE_ATTRIBUTE, val,
+ errorbuf, CONFIG_APPLY) != LDAP_SUCCESS)
+ {
+ LDAPDebug(LDAP_DEBUG_ANY, "%s: %s: %s\n", configfile,
+ CONFIG_RETURN_EXACT_CASE_ATTRIBUTE, errorbuf);
+ }
+ val[0] = 0;
+ }
+
+ /* see if we should allow attr. name exceptions, e.g. '_'s */
+ if (entry_has_attr_and_value(e,
+ CONFIG_ATTRIBUTE_NAME_EXCEPTION_ATTRIBUTE,
+ val, sizeof(val)))
+ {
+ if (config_set_attrname_exceptions(
+ CONFIG_ATTRIBUTE_NAME_EXCEPTION_ATTRIBUTE, val,
+ errorbuf, CONFIG_APPLY) != LDAP_SUCCESS)
+ {
+ LDAPDebug(LDAP_DEBUG_ANY, "%s: %s: %s\n", configfile,
+ CONFIG_ATTRIBUTE_NAME_EXCEPTION_ATTRIBUTE,
+ errorbuf);
+ }
+ val[0] = 0;
+ }
+
+ /* see if we need to maintain schema compatibility with 4.x */
+ if (entry_has_attr_and_value(e,
+ CONFIG_DS4_COMPATIBLE_SCHEMA_ATTRIBUTE, val, sizeof(val)))
+ {
+ if (config_set_ds4_compatible_schema(
+ CONFIG_DS4_COMPATIBLE_SCHEMA_ATTRIBUTE, val,
+ errorbuf, CONFIG_APPLY) != LDAP_SUCCESS)
+ {
+ LDAPDebug(LDAP_DEBUG_ANY, "%s: %s: %s\n", configfile,
+ CONFIG_DS4_COMPATIBLE_SCHEMA_ATTRIBUTE,
+ errorbuf);
+ }
+ val[0] = 0;
+ }
+
+ /* see if we need to allow trailing spaces in OC and AT names */
+ if (entry_has_attr_and_value(e,
+ CONFIG_SCHEMA_IGNORE_TRAILING_SPACES, val, sizeof(val)))
+ {
+ if (config_set_schema_ignore_trailing_spaces(
+ CONFIG_SCHEMA_IGNORE_TRAILING_SPACES, val,
+ errorbuf, CONFIG_APPLY) != LDAP_SUCCESS)
+ {
+ LDAPDebug(LDAP_DEBUG_ANY, "%s: %s: %s\n", configfile,
+ CONFIG_SCHEMA_IGNORE_TRAILING_SPACES,
+ errorbuf);
+ }
+ val[0] = 0;
+ }
+
+ /* rfc1274-rewrite */
+ if (entry_has_attr_and_value(e,
+ CONFIG_REWRITE_RFC1274_ATTRIBUTE,
+ val, sizeof(val))) {
+ if (config_set_rewrite_rfc1274(
+ CONFIG_REWRITE_RFC1274_ATTRIBUTE, val,
+ errorbuf, CONFIG_APPLY) != LDAP_SUCCESS) {
+ LDAPDebug(LDAP_DEBUG_ANY, "%s: %s: %s\n",
+ configfile,
+ CONFIG_REWRITE_RFC1274_ATTRIBUTE,
+ errorbuf);
+ }
+ }
+
+ if (e)
+ slapi_entry_free(e);
+ }
+
+ /* kexcoff: initialize rootpwstoragescheme and pw_storagescheme
+ * if not explicilty set in the config file
+ */
+ if ( config_set_storagescheme() ) { /* default scheme plugin not loaded */
+ slapi_log_error(SLAPI_LOG_FATAL, "startup",
+ "The default password storage scheme SSHA could not be read or was not found in the file %s. It is mandatory.\n",
+ configfile);
+ exit (1);
+ }
+ else {
+ slapi_sdn_done(&plug_dn);
+ rc= 1; /* OK */
+ }
+ }
+
+ slapi_ch_free((void **)&buf);
+ }
+
+bail:
+ return rc;
+}
+
diff --git a/ldap/servers/slapd/configdse.c b/ldap/servers/slapd/configdse.c
new file mode 100644
index 00000000..b2fba44e
--- /dev/null
+++ b/ldap/servers/slapd/configdse.c
@@ -0,0 +1,515 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+/* configdse.c - routines to manage the config DSE */
+
+#include <stdio.h>
+#include <string.h>
+#include <sys/types.h>
+#include <errno.h>
+#ifndef _WIN32
+#include <sys/socket.h>
+#include <sys/param.h>
+#endif
+#include "log.h"
+#include "slap.h"
+#include "pw.h"
+
+static int check_all_maxdiskspace_and_mlogsize(Slapi_PBlock *pb, LDAPMod **mod, char *returntext);
+static void get_log_max_size( LDAPMod *mod,
+ char *maxdiskspace_str,
+ char *mlogsize_str,
+ int *maxdiskspace,
+ int *mlogsize);
+
+/* List of attributes which require server restart to take effect */
+static const char *requires_restart[] = {
+ "cn=config:nsslapd-port",
+ "cn=config:nsslapd-secureport",
+ "cn=config:nsslapd-workingdir",
+ "cn=config:nsslapd-plugin",
+ "cn=config:nsslapd-sslclientauth",
+ "cn=config:nsslapd-changelogdir",
+ "cn=config:nsslapd-changelogsuffix",
+ "cn=config:nsslapd-changelogmaxentries",
+ "cn=config:nsslapd-changelogmaxage",
+ "cn=config:nsslapd-db-locks",
+#if !defined(_WIN32) && !defined(AIX)
+ "cn=config:nsslapd-maxdescriptors",
+#endif
+ "cn=config:" CONFIG_RETURN_EXACT_CASE_ATTRIBUTE,
+ "cn=config:" CONFIG_SCHEMA_IGNORE_TRAILING_SPACES,
+ "cn=config,cn=ldbm:nsslapd-idlistscanlimit",
+ "cn=config,cn=ldbm:nsslapd-parentcheck",
+ "cn=config,cn=ldbm:nsslapd-dbcachesize",
+ "cn=config,cn=ldbm:nsslapd-dbncache",
+ "cn=config,cn=ldbm:nsslapd-cachesize",
+ "cn=config,cn=ldbm:nsslapd-plugin",
+ "cn=encryption,cn=config:nssslsessiontimeout",
+ "cn=encryption,cn=config:nssslclientauth",
+ "cn=encryption,cn=config:nsssl2",
+ "cn=encryption,cn=config:nsssl3" };
+
+static int
+isASyntaxOrMrPluginOrPss(Slapi_Entry *e)
+{
+ char *ptype = slapi_entry_attr_get_charptr(e, ATTR_PLUGIN_TYPE);
+ int retval = (ptype && !strcasecmp(ptype, "syntax"));
+ if (!retval)
+ retval = (ptype && !strcasecmp(ptype, "matchingrule"));
+ if (!retval)
+ retval = (ptype && !strcasecmp(ptype, "pwdstoragescheme"));
+ if (!retval)
+ retval = (ptype && !strcasecmp(ptype, "reverpwdstoragescheme"));
+ slapi_ch_free((void**)&ptype);
+ return retval;
+}
+
+/* these attr types are ignored for the purposes of configuration search/modify */
+static int
+ignore_attr_type(const char *attr_type)
+{
+ if ( !attr_type ||
+ (strcasecmp (attr_type, "cn") == 0) ||
+ (strcasecmp (attr_type, "aci") == 0) ||
+ (strcasecmp (attr_type, "objectclass") == 0) ||
+ (strcasecmp (attr_type, "numsubordinates") == 0) ||
+ (strcasecmp (attr_type, "modifytimestamp") == 0) ||
+ (strcasecmp (attr_type, "modifiersname") == 0)) {
+ return 1;
+ }
+
+ return 0;
+}
+
+int
+read_config_dse (Slapi_PBlock *pb, Slapi_Entry* e, Slapi_Entry* entryAfter, int *returncode, char *returntext, void *arg)
+{
+ struct berval *vals[2];
+ struct berval val;
+ Slapi_Backend *be;
+ slapdFrontendConfig_t *slapdFrontendConfig;
+ struct slapdplugin *pPlugin;
+ char *cookie;
+ int i;
+
+ slapdFrontendConfig = getFrontendConfig();
+
+ vals[0] = &val;
+ vals[1] = NULL;
+
+ /*
+ * We can skip using the config accessor functions here because we're holding
+ * the read lock explicitly
+ */
+ CFG_LOCK_READ(slapdFrontendConfig);
+
+ /* show backend config */
+ attrlist_delete ( &e->e_attrs, "nsslapd-backendconfig");
+ for ( i = 0;
+ slapdFrontendConfig->backendconfig &&
+ slapdFrontendConfig->backendconfig[i];
+ i++) {
+ val.bv_val = slapdFrontendConfig->backendconfig[i];
+ val.bv_len = strlen ( val.bv_val );
+ attrlist_merge ( &e->e_attrs, "nsslapd-backendconfig", vals);
+ }
+
+ CFG_UNLOCK_READ(slapdFrontendConfig);
+
+ /* show other config entries */
+ attrlist_delete ( &e->e_attrs, "nsslapd-backendconfig");
+ cookie = NULL;
+ be = slapi_get_first_backend (&cookie);
+ while ( be )
+ {
+ if(!be->be_private)
+ {
+ Slapi_DN dn;
+ slapi_sdn_init(&dn);
+ be_getconfigdn(be,&dn);
+ val.bv_val = (char*)slapi_sdn_get_ndn(&dn);
+ val.bv_len = strlen (val.bv_val);
+ attrlist_merge ( &e->e_attrs, "nsslapd-backendconfig", vals);
+ slapi_sdn_done(&dn);
+ }
+
+ be = slapi_get_next_backend (cookie);
+ }
+
+ slapi_ch_free ((void **)&cookie);
+
+ /* show be_type */
+ attrlist_delete( &e->e_attrs, "nsslapd-betype");
+ cookie = NULL;
+ be = slapi_get_first_backend(&cookie);
+ while ( be ) {
+ if( !be->be_private )
+ {
+ val.bv_val = be->be_type;
+ val.bv_len = strlen (be->be_type);
+ attrlist_replace( &e->e_attrs, "nsslapd-betype", vals );
+ }
+
+ be = slapi_get_next_backend(cookie);
+ }
+
+ slapi_ch_free ( (void **) &cookie);
+
+ /* show private suffixes */
+ attrlist_delete ( &e->e_attrs, "nsslapd-privatenamespaces");
+ cookie = NULL;
+ be = slapi_get_first_backend(&cookie);
+ while ( be )
+ {
+ if(be->be_private)
+ {
+ int n= 0;
+ const Slapi_DN *base= NULL;
+ do {
+ base= slapi_be_getsuffix(be,n);
+ if(base!=NULL)
+ {
+ val.bv_val = (void*)slapi_sdn_get_dn(base); /* jcm: had to cast away const */
+ val.bv_len = strlen (val.bv_val);
+ attrlist_merge ( &e->e_attrs, "nsslapd-privatenamespaces", vals);
+ }
+ n++;
+ } while (base!=NULL);
+ }
+
+ be = slapi_get_next_backend(cookie);
+ }
+
+ slapi_ch_free ((void **) &cookie);
+
+ /* show syntax plugins */
+ attrlist_delete ( &e->e_attrs, CONFIG_PLUGIN_ATTRIBUTE );
+ for ( pPlugin = slapi_get_global_syntax_plugins(); pPlugin != NULL;
+ pPlugin = pPlugin->plg_next ) {
+ val.bv_val = pPlugin->plg_dn;
+ val.bv_len = strlen ( val.bv_val );
+ attrlist_merge ( &e->e_attrs, CONFIG_PLUGIN_ATTRIBUTE, vals );
+ }
+
+ /* show matching rule plugins */
+ for ( pPlugin = slapi_get_global_mr_plugins(); pPlugin != NULL;
+ pPlugin = pPlugin->plg_next ) {
+ val.bv_val = pPlugin->plg_dn;
+ val.bv_len = strlen ( val.bv_val );
+ attrlist_merge ( &e->e_attrs, CONFIG_PLUGIN_ATTRIBUTE, vals );
+ }
+
+ /* show requiresrestart */
+ attrlist_delete( &e->e_attrs, "nsslapd-requiresrestart");
+ for ( i = 0; i < sizeof(requires_restart)/sizeof(requires_restart[0]); i++ ) {
+ val.bv_val = (char *)requires_restart[i];
+ val.bv_len = strlen (val.bv_val);
+ attrlist_merge ( &e->e_attrs, "nsslapd-requiresrestart", vals);
+ }
+
+ /* show the rest of the configuration parameters */
+ *returncode= config_set_entry(e);
+
+ return SLAPI_DSE_CALLBACK_OK;
+}
+
+int
+load_config_dse(Slapi_PBlock *pb, Slapi_Entry* e, Slapi_Entry* ignored, int *returncode, char *returntext, void *arg)
+{
+ int retval = LDAP_SUCCESS;
+ Slapi_Attr *attr = 0;
+
+ for (slapi_entry_first_attr(e, &attr); (retval == LDAP_SUCCESS) && attr;
+ slapi_entry_next_attr(e, attr, &attr))
+ {
+ char *attr_name = 0;
+ struct berval **values = 0;
+ int nvals = 0;
+
+ slapi_attr_get_type(attr, &attr_name);
+ if (ignore_attr_type(attr_name))
+ continue;
+
+ slapi_attr_get_numvalues(attr, &nvals);
+
+ /* convert the values into an array of bervals */
+ if (nvals)
+ {
+ Slapi_Value *v = 0;
+ int index = 0;
+
+ values = (struct berval **)slapi_ch_malloc((nvals+1) *
+ sizeof(struct berval *));
+ values[nvals] = 0;
+ for (index = slapi_attr_first_value(attr, &v);
+ v && (index != -1);
+ index = slapi_attr_next_value(attr, index, &v))
+ {
+ values[index] = (struct berval *)slapi_value_get_berval(v);
+ }
+ }
+
+ if (attr_name)
+ {
+ retval = config_set(attr_name, values, returntext, 1 /* force apply */);
+ if ((retval != LDAP_SUCCESS) &&
+ slapi_attr_flag_is_set(attr, SLAPI_ATTR_FLAG_OPATTR))
+ retval = LDAP_SUCCESS; /* ignore attempts to modify operational attrs */
+ }
+
+ if (values)
+ {
+ /* slapi_value_get_berval returns the actual memory owned by the
+ slapi attr, so we cannot free it */
+ slapi_ch_free((void **)&values);
+ }
+ }
+
+ *returncode = retval;
+ return (retval == LDAP_SUCCESS) ? SLAPI_DSE_CALLBACK_OK
+ : SLAPI_DSE_CALLBACK_ERROR;
+}
+
+int
+load_plugin_entry(Slapi_PBlock *pb, Slapi_Entry* e, Slapi_Entry* ignored, int *returncode, char *returntext, void *arg)
+{
+ int retval = LDAP_SUCCESS;
+
+ if (isASyntaxOrMrPluginOrPss(e))
+ {
+ /*
+ * syntax/matching/passwd storage scheme rule plugins are loaded
+ * at bootstrap time, so no need to load them here. BUT -- the
+ * descriptive information that is registered by the plugin is
+ * thrown away during bootstrap time, so we set it here.
+ */
+ (void)plugin_add_descriptive_attributes( e, NULL );
+
+ } else {
+ /*
+ * Process plugins that were not loaded during bootstrap.
+ */
+ retval = plugin_setup(e, 0, 0, 1);
+
+ /*
+ * well this damn well sucks, but this function is used as a callback
+ * and to ensure we do not continue if a plugin fails to load or init
+ * properly we must exit here.
+ */
+ if(retval)
+ {
+ char dnbuf[ BUFSIZ ];
+
+ slapi_log_error( SLAPI_LOG_FATAL, NULL,
+ "Unable to load plugin \"%s\"\n",
+ escape_string( slapi_entry_get_dn_const( e ), dnbuf ));
+ exit(1);
+ }
+ }
+
+ *returncode = retval;
+ return (retval == LDAP_SUCCESS) ? SLAPI_DSE_CALLBACK_OK
+ : SLAPI_DSE_CALLBACK_ERROR;
+}
+
+int
+modify_config_dse(Slapi_PBlock *pb, Slapi_Entry* entryBefore, Slapi_Entry* e, int *returncode, char *returntext, void *arg)
+{
+ char *config_attr;
+ LDAPMod **mods;
+ int rc = LDAP_SUCCESS;
+ int apply_mods = 0;
+ char *pwd = 0;
+ int checked_all_maxdiskspace_and_mlogsize = 0;
+
+ slapi_pblock_get( pb, SLAPI_MODIFY_MODS, &mods );
+
+ returntext[0] = '\0';
+
+ /*
+ * First pass: set apply mods to 0 so only input validation will be done;
+ * 2nd pass: set apply mods to 1 to apply changes to internal storage
+ */
+ for ( apply_mods = 0; apply_mods <= 1; apply_mods++ ) {
+ int i = 0;
+ for (i = 0; (mods[i] && (LDAP_SUCCESS == rc)); i++) {
+ if ((mods[i]->mod_op & LDAP_MOD_DELETE) ||
+ (mods[i]->mod_op & LDAP_MOD_ADD)) {
+ rc= LDAP_UNWILLING_TO_PERFORM;
+ sprintf(returntext, "%s attributes is not allowed",
+ (mods[i]->mod_op & LDAP_MOD_DELETE) ? "Deleting" : "Adding");
+ } else if (mods[i]->mod_op & LDAP_MOD_REPLACE) {
+ /* send all aci modifications to the backend */
+ config_attr = (char *)mods[i]->mod_type;
+ if (ignore_attr_type(config_attr))
+ continue;
+
+ if ( (checked_all_maxdiskspace_and_mlogsize == 0 ) &&
+ ((strcasecmp( mods[i]->mod_type, CONFIG_ERRORLOG_MAXLOGDISKSPACE_ATTRIBUTE) == 0) ||
+ (strcasecmp( mods[i]->mod_type, CONFIG_ERRORLOG_MAXLOGSIZE_ATTRIBUTE) == 0) ||
+ (strcasecmp( mods[i]->mod_type, CONFIG_ACCESSLOG_MAXLOGDISKSPACE_ATTRIBUTE) == 0) ||
+ (strcasecmp( mods[i]->mod_type, CONFIG_ACCESSLOG_MAXLOGSIZE_ATTRIBUTE) == 0) ||
+ (strcasecmp( mods[i]->mod_type, CONFIG_AUDITLOG_MAXLOGDISKSPACE_ATTRIBUTE) == 0) ||
+ (strcasecmp( mods[i]->mod_type, CONFIG_AUDITLOG_MAXLOGSIZE_ATTRIBUTE) == 0)) )
+ {
+ checked_all_maxdiskspace_and_mlogsize = 1;
+ if ( (rc=check_all_maxdiskspace_and_mlogsize(pb, mods, returntext)) != LDAP_SUCCESS )
+ {
+ goto finish_and_return;
+ }
+ }
+
+ rc = config_set(config_attr, mods[i]->mod_bvalues, returntext,
+ apply_mods);
+ }
+ }
+ }
+
+finish_and_return:
+ /*
+ * The DSE code will be writing the resultant entry value to the
+ * dse.ldif file. We *must*not* write plain passwords into here.
+ */
+ slapi_entry_attr_delete( e, CONFIG_ROOTPW_ATTRIBUTE );
+ /* if the password has been set, it will be hashed */
+ if ((pwd = config_get_rootpw()) != NULL) {
+ slapi_entry_attr_set_charptr(e, CONFIG_ROOTPW_ATTRIBUTE, pwd);
+ slapi_ch_free((void**)&pwd);
+ }
+
+ *returncode= rc;
+ if(LDAP_SUCCESS == rc) {
+ return(SLAPI_DSE_CALLBACK_OK); /* success -- apply the mods. */
+ }
+ else {
+ return(SLAPI_DSE_CALLBACK_ERROR); /* failure -- reject the mods. */
+ }
+}
+
+int
+postop_modify_config_dse(Slapi_PBlock *pb, Slapi_Entry* entryBefore, Slapi_Entry* e, int *returncode, char *returntext, void *arg)
+{
+ static int num_requires_restart = sizeof(requires_restart)/sizeof(char*);
+ LDAPMod **mods;
+ int i, j;
+ char *p;
+
+ slapi_pblock_get( pb, SLAPI_MODIFY_MODS, &mods );
+ returntext[0] = '\0';
+
+ for (i = 0; mods[i]; i++) {
+ if (mods[i]->mod_op & LDAP_MOD_REPLACE ) {
+ /* Check if the server needs to be restarted */
+ for (j = 0; j < num_requires_restart; j++)
+ {
+ p = strchr (requires_restart[j], ':');
+ if (p == NULL)
+ continue;
+ while ( *(++p) == ' ' || *p == '\t' );
+ if ( strcasecmp (p, mods[i]->mod_type) == 0 ) {
+ sprintf(returntext,
+ "The change of %s will not take effect "
+ "until the server is restarted", mods[i]->mod_type);
+ slapi_log_error (SLAPI_LOG_FATAL, NULL, "%s\n", returntext);
+ break;
+ }
+ }
+ if (j < num_requires_restart) {
+ /* That's enough, don't check remaining mods any more */
+ break;
+ }
+ }
+ }
+
+ *returncode = LDAP_SUCCESS;
+ return *returncode;
+}
+
+static int
+check_all_maxdiskspace_and_mlogsize(Slapi_PBlock *pb, LDAPMod **mods, char *returntext)
+{
+ int i = 0;
+ int rc = LDAP_SUCCESS;
+ int errormaxdiskspace = -1;
+ int errormlogsize = -1;
+ int accessmaxdiskspace = -1;
+ int accessmlogsize = -1;
+ int auditmaxdiskspace = -1;
+ int auditmlogsize = -1;
+
+ for (i = 0; mods[i] ; i++)
+ {
+ get_log_max_size(mods[i],
+ CONFIG_ERRORLOG_MAXLOGDISKSPACE_ATTRIBUTE,
+ CONFIG_ERRORLOG_MAXLOGSIZE_ATTRIBUTE,
+ &errormaxdiskspace,
+ &errormlogsize);
+
+ get_log_max_size(mods[i],
+ CONFIG_ACCESSLOG_MAXLOGDISKSPACE_ATTRIBUTE,
+ CONFIG_ACCESSLOG_MAXLOGSIZE_ATTRIBUTE,
+ &accessmaxdiskspace,
+ &accessmlogsize);
+
+ get_log_max_size(mods[i],
+ CONFIG_AUDITLOG_MAXLOGDISKSPACE_ATTRIBUTE,
+ CONFIG_AUDITLOG_MAXLOGSIZE_ATTRIBUTE,
+ &auditmaxdiskspace,
+ &auditmlogsize);
+ }
+
+ if ( (rc=check_log_max_size(
+ CONFIG_ERRORLOG_MAXLOGDISKSPACE_ATTRIBUTE,
+ CONFIG_ERRORLOG_MAXLOGSIZE_ATTRIBUTE,
+ errormaxdiskspace,
+ errormlogsize,
+ returntext,
+ SLAPD_ERROR_LOG)) != LDAP_SUCCESS )
+ {
+ return rc;
+ }
+ if ( (rc=check_log_max_size(
+ CONFIG_ACCESSLOG_MAXLOGDISKSPACE_ATTRIBUTE,
+ CONFIG_ACCESSLOG_MAXLOGSIZE_ATTRIBUTE,
+ accessmaxdiskspace,
+ accessmlogsize,
+ returntext,
+ SLAPD_ACCESS_LOG)) != LDAP_SUCCESS )
+ {
+ return rc;
+ }
+ if ( (rc=check_log_max_size(
+ CONFIG_AUDITLOG_MAXLOGDISKSPACE_ATTRIBUTE,
+ CONFIG_AUDITLOG_MAXLOGSIZE_ATTRIBUTE,
+ auditmaxdiskspace,
+ auditmlogsize,
+ returntext,
+ SLAPD_AUDIT_LOG)) != LDAP_SUCCESS )
+ {
+ return rc;
+ }
+ return rc;
+}
+
+static void
+get_log_max_size( LDAPMod *mod,
+ char *maxdiskspace_str,
+ char *mlogsize_str,
+ int *maxdiskspace,
+ int *mlogsize)
+{
+ if ( mod->mod_bvalues != NULL &&
+ (strcasecmp( mod->mod_type, maxdiskspace_str ) == 0) )
+ {
+ *maxdiskspace = atoi((char *) mod->mod_bvalues[0]->bv_val);
+ }
+
+ if ( mod->mod_bvalues != NULL &&
+ (strcasecmp( mod->mod_type, mlogsize_str ) == 0) )
+ {
+ *mlogsize = atoi((char *) mod->mod_bvalues[0]->bv_val);
+ }
+}
diff --git a/ldap/servers/slapd/connection.c b/ldap/servers/slapd/connection.c
new file mode 100644
index 00000000..4cf7869b
--- /dev/null
+++ b/ldap/servers/slapd/connection.c
@@ -0,0 +1,2485 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+#include <stdio.h>
+#include <string.h>
+#include <sys/types.h>
+#ifndef _WIN32
+#include <sys/time.h>
+#include <sys/socket.h>
+#include <stdlib.h>
+#endif
+#define TCPLEN_T int
+#include <signal.h>
+#include "slap.h"
+#include "prcvar.h"
+#include "prlog.h" /* for PR_ASSERT */
+#include "fe.h"
+#include <sasl.h>
+#if defined(LINUX)
+#include <netinet/tcp.h> /* for TCP_CORK */
+#endif
+
+
+static void connection_threadmain( void );
+static void add_pb( Slapi_PBlock * );
+static Slapi_PBlock *get_pb( void );
+static void connection_add_operation(Connection* conn, Operation *op);
+static void connection_free_private_buffer(Connection *conn);
+static void op_copy_identity(Connection *conn, Operation *op);
+static int is_ber_too_big(const Connection *conn,unsigned long ber_len);
+static void log_ber_too_big_error(const Connection *conn,
+ unsigned long ber_len, unsigned long maxbersize);
+
+/*
+ * We maintain a global work queue of Slapi_PBlock's that have not yet
+ * been handed off to an operation thread.
+ */
+struct Slapi_PBlock_q
+{
+ Slapi_PBlock *pb;
+ struct Slapi_PBlock_q *next_pb;
+ int pb_fd;
+};
+
+static struct Slapi_PBlock_q *first_pb= NULL; /* global work queue head */
+static struct Slapi_PBlock_q *last_pb= NULL; /* global work queue tail */
+static PRLock *pb_q_lock=NULL; /* protects first_pb & last_pb */
+
+static PRCondVar *op_thread_cv; /* used by operation threads to wait for work */
+static PRLock *op_thread_lock; /* associated with op_thread_cv */
+static int op_shutdown= 0; /* if non-zero, server is shutting down */
+
+#define LDAP_SOCKET_IO_BUFFER_SIZE 512 /* Size of the buffer we give to the I/O system for reads */
+
+
+/*
+ * We really are done with this connection. Get rid of everything.
+ *
+ * Note: this function should be called with conn->c_mutex already locked
+ * or at a time when multiple threads are not in play that might touch the
+ * connection structure.
+ */
+void
+connection_done(Connection *conn)
+{
+ connection_cleanup(conn);
+ /* free the private content, the buffer has been freed by above connection_cleanup */
+ slapi_ch_free((void**)&conn->c_private);
+ if (NULL != conn->c_sb)
+ {
+ ber_sockbuf_free(conn->c_sb);
+ }
+ if (NULL != conn->c_mutex)
+ {
+ PR_DestroyLock(conn->c_mutex);
+ }
+ if (NULL != conn->c_pdumutex)
+ {
+ PR_DestroyLock(conn->c_pdumutex);
+ }
+}
+
+/*
+ * We're going to be making use of this connection again.
+ * So, get rid of everything we can't make use of.
+ *
+ * Note: this function should be called with conn->c_mutex already locked
+ * or at a time when multiple threads are not in play that might touch the
+ * connection structure.
+ */
+void
+connection_cleanup(Connection *conn)
+{
+ bind_credentials_clear( conn, PR_FALSE /* do not lock conn */,
+ PR_TRUE /* clear external creds. */ );
+ slapi_ch_free((void**)&conn->c_authtype);
+
+ /* Call the plugin extension destructors */
+ factory_destroy_extension(connection_type,conn,NULL/*Parent*/,&(conn->c_extension));
+ /*
+ * We hang onto these, since we can reuse them.
+ * Sockbuf *c_sb;
+ * PRLock *c_mutex;
+ * PRLock *c_pdumutex;
+ * Conn_private *c_private;
+ */
+
+#ifdef _WIN32
+ if (conn->c_prfd && (conn->c_flags & CONN_FLAG_SSL))
+ {
+ LDAPDebug( LDAP_DEBUG_CONNS,
+ "conn=%d fd=%d closed now\n",
+ conn->c_connid, conn->c_sd,0);
+ PR_Close(conn->c_prfd);
+ }
+ else if (conn->c_sd)
+ {
+ LDAPDebug( LDAP_DEBUG_CONNS,
+ "conn=%d fd=%d closed now\n",
+ conn->c_connid, conn->c_sd,0);
+ closesocket(conn->c_sd);
+ }
+#else
+ if (conn->c_prfd)
+ {
+ PR_Close(conn->c_prfd);
+ }
+#endif
+
+ conn->c_sd= SLAPD_INVALID_SOCKET;
+ conn->c_ldapversion= 0;
+
+ conn->c_isreplication_session = 0;
+ slapi_ch_free((void**)&conn->cin_addr );
+ slapi_ch_free((void**)&conn->cin_destaddr );
+ if ( conn->c_domain != NULL )
+ {
+ ber_bvecfree( conn->c_domain );
+ conn->c_domain = NULL;
+ }
+ /* conn->c_ops= NULL; */
+ conn->c_gettingber= 0;
+ conn->c_currentber= NULL;
+ conn->c_starttime= 0;
+ conn->c_connid= 0;
+ conn->c_opsinitiated= 0;
+ conn->c_opscompleted= 0;
+ conn->c_threadnumber= 0;
+ conn->c_refcnt= 0;
+ conn->c_idlesince= 0;
+ conn->c_flags= 0;
+ conn->c_needpw= 0;
+ conn->c_prfd= NULL;
+ /* c_ci stays as it is */
+ conn->c_fdi= SLAPD_INVALID_SOCKET_INDEX;
+ conn->c_next= NULL;
+ conn->c_prev= NULL;
+ conn->c_extension= NULL;
+ /* remove any SASL I/O from the connection */
+ sasl_io_cleanup(conn);
+ sasl_dispose((sasl_conn_t**)&conn->c_sasl_conn);
+
+ /* free the connection socket buffer */
+ connection_free_private_buffer(conn);
+}
+
+/*
+ * Callers of connection_reset() must hold the conn->c_mutex lock.
+ */
+void
+connection_reset(Connection* conn, int ns, PRNetAddr * from, int fromLen, int is_SSL)
+{
+ char * pTmp = is_SSL ? "SSL " : "";
+ TCPLEN_T l_fromLen = (TCPLEN_T)fromLen;
+ TCPLEN_T addrlen, destaddrlen;
+ struct sockaddr_in addr, destaddr;
+ char *str_ip, *str_destip, buf_ip[ 256 ], buf_destip[ 256 ];
+ char *str_unknown = "unknown";
+ int in_referral_mode = config_check_referral_mode();
+
+ LDAPDebug( LDAP_DEBUG_CONNS, "new %sconnection on %d\n", pTmp, conn->c_sd, 0 );
+
+ /* bump our count of connections and update SNMP stats */
+ PR_Lock( num_conns_mutex );
+ conn->c_connid = num_conns++;
+ PR_Unlock( num_conns_mutex );
+
+ if (! in_referral_mode) {
+ PR_AtomicIncrement(g_get_global_snmp_vars()->ops_tbl.dsConnectionSeq);
+ PR_AtomicIncrement(g_get_global_snmp_vars()->ops_tbl.dsConnections);
+ }
+
+ /* get peer address (IP address of this client) */
+ addrlen = sizeof( addr );
+ memset( &addr, 0, addrlen );
+
+ if ( ((from->ipv6.ip.pr_s6_addr32[0] != 0) ||
+ (from->ipv6.ip.pr_s6_addr32[1] != 0) ||
+ (from->ipv6.ip.pr_s6_addr32[2] != 0) ||
+ (from->ipv6.ip.pr_s6_addr32[3] != 0)) ||
+ ((conn->c_prfd != NULL) && (PR_GetPeerName( conn->c_prfd, from ) == 0)) ) {
+ conn->cin_addr = (PRNetAddr *) slapi_ch_malloc( sizeof( PRNetAddr ) );
+ memcpy( conn->cin_addr, from, sizeof( PRNetAddr ) );
+
+ if ( PR_IsNetAddrType( conn->cin_addr, PR_IpAddrV4Mapped ) ) {
+ PRNetAddr v4addr;
+ memset( &v4addr, 0, sizeof( v4addr ) );
+ v4addr.inet.family = PR_AF_INET;
+ v4addr.inet.ip = conn->cin_addr->ipv6.ip.pr_s6_addr32[3];
+ PR_NetAddrToString( &v4addr, buf_ip, sizeof( buf_ip ) );
+ } else {
+ PR_NetAddrToString( conn->cin_addr, buf_ip, sizeof( buf_ip ) );
+ }
+ buf_ip[ sizeof( buf_ip ) - 1 ] = '\0';
+ str_ip = buf_ip;
+
+ } else if ( (conn->c_prfd == NULL) &&
+ (getpeername( conn->c_sd, (struct sockaddr*)&addr, &addrlen ) == 0) ) {
+ conn->cin_addr = (PRNetAddr *)slapi_ch_malloc( sizeof( PRNetAddr ) );
+
+ if ( PR_SetNetAddr(PR_IpAddrNull, PR_AF_INET6, addr.sin_port, conn->cin_addr)
+ != PR_SUCCESS ) {
+ int oserr = PR_GetError();
+ LDAPDebug( LDAP_DEBUG_ANY, "PR_SetNetAddr() failed, "
+ SLAPI_COMPONENT_NAME_NSPR " error %d (%s)\n",
+ oserr, slapd_pr_strerror(oserr), 0 );
+ } else {
+ PR_ConvertIPv4AddrToIPv6(addr.sin_addr.s_addr, &(conn->cin_addr->ipv6.ip));
+ }
+
+ /* copy string equivalent of address into a buffer to use for
+ * logging since each call to inet_ntoa() returns a pointer to a
+ * single thread-specific buffer (which prevents us from calling
+ * inet_ntoa() twice in one call to slapi_log_access()).
+ */
+ str_ip = inet_ntoa( addr.sin_addr );
+ strncpy( buf_ip, str_ip, sizeof( buf_ip ) - 1 );
+ buf_ip[ sizeof( buf_ip ) - 1 ] = '\0';
+ str_ip = buf_ip;
+
+ } else {
+ str_ip = str_unknown;
+ }
+
+
+ /*
+ * get destination address (server IP address this client connected to)
+ */
+ destaddrlen = sizeof( destaddr );
+ memset( &destaddr, 0, destaddrlen );
+
+
+ if ( conn->c_prfd != NULL ) {
+ conn->cin_destaddr = (PRNetAddr *) slapi_ch_malloc( sizeof( PRNetAddr ) );
+ if (PR_GetSockName( conn->c_prfd, conn->cin_destaddr ) == 0) {
+ if ( PR_IsNetAddrType( conn->cin_destaddr, PR_IpAddrV4Mapped ) ) {
+ PRNetAddr v4destaddr;
+ memset( &v4destaddr, 0, sizeof( v4destaddr ) );
+ v4destaddr.inet.family = PR_AF_INET;
+ v4destaddr.inet.ip = conn->cin_destaddr->ipv6.ip.pr_s6_addr32[3];
+ PR_NetAddrToString( &v4destaddr, buf_destip, sizeof( buf_destip ) );
+ } else {
+ PR_NetAddrToString( conn->cin_destaddr, buf_destip, sizeof( buf_destip ) );
+ }
+ buf_destip[ sizeof( buf_destip ) - 1 ] = '\0';
+ str_destip = buf_destip;
+ } else {
+ str_destip = str_unknown;
+ }
+ } else if ( (conn->c_prfd == NULL) &&
+ (getsockname( conn->c_sd, (struct sockaddr*)&destaddr, &destaddrlen ) == 0) ) {
+ conn->cin_destaddr = (PRNetAddr *)slapi_ch_malloc( sizeof( PRNetAddr ) );
+
+ if ( PR_SetNetAddr(PR_IpAddrNull, PR_AF_INET6, destaddr.sin_port, conn->cin_destaddr)
+ != PR_SUCCESS ) {
+ int oserr = PR_GetError();
+ LDAPDebug( LDAP_DEBUG_ANY, "PR_SetNetAddr() failed, "
+ SLAPI_COMPONENT_NAME_NSPR " error %d (%s)\n",
+ oserr, slapd_pr_strerror(oserr), 0 );
+ } else {
+ PR_ConvertIPv4AddrToIPv6(destaddr.sin_addr.s_addr, &(conn->cin_destaddr->ipv6.ip));
+ }
+
+ /* copy string equivalent of address into a buffer to use for
+ * logging since each call to inet_ntoa() returns a pointer to a
+ * single thread-specific buffer (which prevents us from calling
+ * inet_ntoa() twice in one call to slapi_log_access()).
+ */
+ str_destip = inet_ntoa( destaddr.sin_addr );
+ strncpy( buf_destip, str_destip, sizeof( buf_destip ) - 1 );
+ buf_destip[ sizeof( buf_destip ) - 1 ] = '\0';
+ str_destip = buf_destip;
+
+ } else {
+ str_destip = str_unknown;
+ }
+
+
+ if ( !in_referral_mode ) {
+ /* create a sasl connection */
+ ids_sasl_server_new(conn);
+ }
+
+ /* log useful stuff to our access log */
+ slapi_log_access( LDAP_DEBUG_STATS,
+ "conn=%d fd=%d slot=%d %sconnection from %s to %s\n",
+ conn->c_connid, conn->c_sd, ns, pTmp, str_ip, str_destip );
+
+ /* initialize the remaining connection fields */
+ conn->c_ldapversion = LDAP_VERSION3;
+ conn->c_starttime = current_time();
+ conn->c_idlesince = conn->c_starttime;
+ conn->c_flags = is_SSL ? CONN_FLAG_SSL : 0;
+ conn->c_authtype = slapi_ch_strdup(SLAPD_AUTH_NONE);
+}
+
+/* Create a pool of threads for handling the operations */
+void
+init_op_threads()
+{
+ int i;
+ PRErrorCode errorCode;
+ int max_threads = config_get_threadnumber();
+ /* Initialize the locks and cv */
+
+ if ((pb_q_lock = PR_NewLock()) == NULL ) {
+ errorCode = PR_GetError();
+ LDAPDebug( LDAP_DEBUG_ANY,
+ "init_op_threads: PR_NewLock failed, " SLAPI_COMPONENT_NAME_NSPR " error %d (%s)\n",
+ errorCode, slapd_pr_strerror(errorCode), 0 );
+ exit(-1);
+ }
+
+ if ((op_thread_lock = PR_NewLock()) == NULL ) {
+ errorCode = PR_GetError();
+ LDAPDebug( LDAP_DEBUG_ANY,
+ "init_op_threads: PR_NewLock failed, " SLAPI_COMPONENT_NAME_NSPR " error %d (%s)\n",
+ errorCode, slapd_pr_strerror(errorCode), 0 );
+ exit(-1);
+ }
+
+ if ((op_thread_cv = PR_NewCondVar( op_thread_lock )) == NULL) {
+ errorCode = PR_GetError();
+ LDAPDebug( LDAP_DEBUG_ANY, "init_op_threads: PR_NewCondVar failed, " SLAPI_COMPONENT_NAME_NSPR " error %d (%s)\n",
+ errorCode, slapd_pr_strerror(errorCode), 0 );
+ exit(-1);
+ }
+
+ /* start the operation threads */
+ for (i=0; i < max_threads; i++) {
+ PR_SetConcurrency(4);
+ if (PR_CreateThread (PR_USER_THREAD,
+ (VFP) (void *) connection_threadmain, NULL,
+ PR_PRIORITY_NORMAL, PR_GLOBAL_THREAD,
+ PR_UNJOINABLE_THREAD,
+ SLAPD_DEFAULT_THREAD_STACKSIZE
+ ) == NULL ) {
+ int prerr = PR_GetError();
+ LDAPDebug( LDAP_DEBUG_ANY, "PR_CreateThread failed, " SLAPI_COMPONENT_NAME_NSPR " error %d (%s)\n",
+ prerr, slapd_pr_strerror( prerr ), 0 );
+ } else {
+ PR_AtomicIncrement(&active_threads);
+ }
+ }
+}
+
+static void
+referral_mode_reply(Slapi_PBlock *pb)
+{
+ struct slapdplugin *plugin;
+ plugin = (struct slapdplugin *) slapi_ch_calloc(1, sizeof(struct slapdplugin));
+ if (plugin!=NULL)
+ {
+ struct berval *urls[2], url;
+ char *refer;
+ refer = config_get_referral_mode();
+ pb->pb_plugin = plugin;
+ set_db_default_result_handlers(pb);
+ urls[0] = &url;
+ urls[1] = NULL;
+ url.bv_val = refer;
+ url.bv_len = refer ? strlen(refer) : 0;
+ slapi_send_ldap_result(pb, LDAP_REFERRAL, NULL, NULL, 0, urls);
+ slapi_ch_free((void **)&plugin);
+ slapi_ch_free((void **)&refer);
+ }
+}
+
+static int
+connection_need_new_password(const Connection *conn, const Operation *op, Slapi_PBlock *pb)
+{
+ int r= 0;
+ /*
+ * add tag != LDAP_REQ_SEARCH to allow admin server 3.5 to do
+ * searches when the user needs to reset
+ * the pw the first time logon.
+ * LP: 22 Dec 2000: Removing LDAP_REQ_SEARCH. It's very unlikely that AS 3.5 will
+ * be used to manage DS5.0
+ */
+
+ if ( conn->c_needpw && op->o_tag != LDAP_REQ_MODIFY &&
+ op->o_tag != LDAP_REQ_BIND && op->o_tag != LDAP_REQ_UNBIND &&
+ op->o_tag != LDAP_REQ_ABANDON )
+ {
+ add_pwd_control ( pb, LDAP_CONTROL_PWEXPIRED, 0);
+ slapi_log_access( LDAP_DEBUG_STATS, "conn=%d op=%d %s\n",
+ pb->pb_conn->c_connid, pb->pb_op->o_opid,
+ "need new password" );
+ send_ldap_result( pb, LDAP_UNWILLING_TO_PERFORM,
+ NULL, NULL, 0, NULL );
+ r= 1;
+ }
+ return r;
+}
+
+
+static void
+connection_dispatch_operation(Connection *conn, Operation *op, Slapi_PBlock *pb)
+{
+ /* Copy the Connection DN into the operation struct */
+ op_copy_identity( conn, op );
+
+ /* process the operation */
+
+ switch ( op->o_tag ) {
+ case LDAP_REQ_BIND:
+ operation_set_type(op,SLAPI_OPERATION_BIND);
+ do_bind( pb );
+ break;
+
+ case LDAP_REQ_UNBIND:
+ operation_set_type(op,SLAPI_OPERATION_UNBIND);
+ do_unbind( pb );
+ break;
+
+ case LDAP_REQ_ADD:
+ operation_set_type(op,SLAPI_OPERATION_ADD);
+ do_add( pb );
+ break;
+
+ case LDAP_REQ_DELETE:
+ operation_set_type(op,SLAPI_OPERATION_DELETE);
+ do_delete( pb );
+ break;
+
+ case LDAP_REQ_MODRDN:
+ operation_set_type(op,SLAPI_OPERATION_MODRDN);
+ do_modrdn( pb );
+ break;
+
+ case LDAP_REQ_MODIFY:
+ operation_set_type(op,SLAPI_OPERATION_MODIFY);
+ do_modify( pb );
+ break;
+
+ case LDAP_REQ_COMPARE:
+ operation_set_type(op,SLAPI_OPERATION_COMPARE);
+ do_compare( pb );
+ break;
+
+ case LDAP_REQ_SEARCH:
+ operation_set_type(op,SLAPI_OPERATION_SEARCH);
+
+
+ /* On Linux we can use TCP_CORK to get us 5-10% speed benefit when one entry is returned */
+ /* Nagle needs to be turned _off_, the default is off on linux, in daemon.c */
+#if defined(LINUX)
+ {
+ int i = 1;
+ int ret = 0;
+ /* Set TCP_CORK here */
+ ret = setsockopt(conn->c_sd,IPPROTO_TCP,TCP_CORK,&i,sizeof(i));
+ if (ret < 0) {
+ LDAPDebug(LDAP_DEBUG_ANY, "Failed to set TCP_CORK on connection %d\n",conn->c_connid, 0, 0);
+ }
+#endif
+
+ do_search( pb );
+
+#if defined(LINUX)
+ /* Clear TCP_CORK to flush any unsent data */
+ i = 0;
+ ret = setsockopt(conn->c_sd,IPPROTO_TCP,TCP_CORK,&i,sizeof(i));
+ if (ret < 0) {
+ LDAPDebug(LDAP_DEBUG_ANY, "Failed to clear TCP_CORK on connection %d\n",conn->c_connid, 0, 0);
+ }
+ }
+#endif
+ break;
+
+ /* for some strange reason, the console is using this old obsolete
+ * value for ABANDON so we have to support it until the console
+ * get fixed
+ * otherwise the console has VERY BAD performances when a fair amount
+ * of entries are created in the DIT
+ */
+ case LDAP_REQ_ABANDON_30:
+ case LDAP_REQ_ABANDON:
+ operation_set_type(op,SLAPI_OPERATION_ABANDON);
+ do_abandon( pb );
+ break;
+
+ case LDAP_REQ_EXTENDED:
+ operation_set_type(op,SLAPI_OPERATION_EXTENDED);
+ do_extended( pb );
+ break;
+
+ default:
+ LDAPDebug( LDAP_DEBUG_ANY,
+ "ignoring unknown LDAP request (conn=%d, tag=0x%lx)\n",
+ conn->c_connid, op->o_tag, 0 );
+ break;
+ }
+}
+
+/* this function should be called under c_mutex */
+int connection_release_nolock (Connection *conn)
+{
+ if (conn->c_refcnt <= 0)
+ {
+ slapi_log_error(SLAPI_LOG_FATAL, "connection",
+ "conn=%d fd=%d Attempt to release connection that is not aquired\n",
+ conn->c_connid, conn->c_sd);
+ PR_ASSERT (PR_FALSE);
+ return -1;
+ }
+ else
+ {
+ conn->c_refcnt--;
+
+ return 0;
+ }
+}
+
+/* this function should be called under c_mutex */
+int connection_acquire_nolock (Connection *conn)
+{
+ /* connection in the closing state can't be acquired */
+ if (conn->c_flags & CONN_FLAG_CLOSING)
+ {
+ /* This may happen while other threads are still working on this connection */
+ slapi_log_error(SLAPI_LOG_FATAL, "connection",
+ "conn=%d fd=%d Attempt to acquire connection in the closing state\n",
+ conn->c_connid, conn->c_sd);
+ return -1;
+ }
+ else
+ {
+ conn->c_refcnt++;
+ return 0;
+ }
+}
+
+/* returns non-0 if connection can be reused and 0 otherwise */
+int connection_is_free (Connection *conn)
+{
+ int rc;
+
+ PR_Lock(conn->c_mutex);
+ rc = conn->c_sd == SLAPD_INVALID_SOCKET && conn->c_refcnt == 0 &&
+ !(conn->c_flags & CONN_FLAG_CLOSING);
+ PR_Unlock(conn->c_mutex);
+
+ return rc;
+}
+
+int connection_is_active_nolock (Connection *conn)
+{
+ return (conn->c_sd != SLAPD_INVALID_SOCKET) &&
+ !(conn->c_flags & CONN_FLAG_CLOSING);
+}
+
+/* returns non-0 if this is an active connection meaning it is in use
+ and not in the closing mode */
+
+#if defined LDAP_IOCP
+/*
+ * IO Completion ports are currently only available on NT.
+ */
+
+typedef enum {read_data, write_data, new_connection} work_type;
+static int wait_on_new_work(Connection **ppConn, work_type *type);
+static int issue_new_read(Connection *conn);
+static int finished_chomping(Connection *conn);
+static int read_the_data(Connection *op, int *process_op);
+static int is_new_operation(Connection *conn);
+static int process_operation(Connection *conn, Operation *op);
+static int connection_operation_new(Connection *conn, Operation **ppOp);
+Operation *get_current_op(Connection *conn);
+static int handle_read_data(Connection *conn,Operation **op,
+ int * connection_referenced);
+
+static void inc_op_count(Connection* conn)
+{
+ PR_AtomicIncrement(&conn->c_opscompleted);
+ PR_AtomicIncrement(&ops_completed);
+}
+
+static int connection_increment_reference(Connection *conn)
+{
+ int rc = 0;
+ PR_Lock( conn->c_mutex );
+ rc = connection_acquire_nolock (conn);
+ PR_Unlock( conn->c_mutex );
+ return rc;
+}
+
+static void connection_decrement_reference(Connection *conn)
+{
+ PR_Lock( conn->c_mutex );
+ connection_release_nolock (conn);
+ PR_Unlock( conn->c_mutex );
+}
+
+static void
+connection_threadmain()
+{
+ /*
+ * OK, so this is the thread main routine for the thread pool.
+ * This is the general idea : wait on the i/o completion port.
+ * then get some data. There are three cases here:
+ * 1) This is the first piece of data read for a new LDAP op.
+ * 2) This is a subsequent, but not final, piece of data read in the current LDAP op on this connection
+ * 3) This is the last piece of the current LDAP op on the current connection.
+ * Note that these cases are NOT exclusive ! In particular, all three can occur for the same read.
+ * based on detecting these cases, we end up doing one or more of the following things:
+ * a) Create new structures for a new op.
+ * b) Read data into the BER buffer for the op.
+ * c) Press on to service the operation request (note that the results are currently written
+ * synchronously.
+ * We always queue a new read on the socket too.
+ * (Note, we need to make sure we don't issue the new read operation until we've copied
+ * the data from the existing one. Otherwise we'd open ourselves to getting OOO data.)
+ *
+ * The intention is that this code will be clean enough to be used for the UNIX build,
+ * once we fake up I/O completion ports with select and another thread.
+ */
+
+ Connection *conn = NULL;
+ Operation *op = NULL;
+ int return_value = -1;
+ int abandon_connection = 0;
+ work_type command = 0;
+ int connection_referenced = 0;
+
+ /* Don't ask me, and I will tell you no lies */
+#if defined( OSF1 ) || defined( hpux ) || defined( LINUX )
+ /* Arrange to ignore SIGPIPE signals. */
+ SIGNAL( SIGPIPE, SIG_IGN );
+#endif
+
+ while (1) {
+
+ abandon_connection = 1; /* we start off assuming that we'll fail somewhere */
+ conn = NULL; /* just make sure we don't step on an old connection by mistake */
+ op = NULL; /* Same goes for the operation */
+
+ return_value = wait_on_new_work(&conn,&command);
+ if( op_shutdown )
+ break;
+ if (0 == return_value) {
+ connection_referenced = 0; /* No outstanding ref count on connection if wait for work returned OK */
+ switch (command) {
+ case read_data:
+ return_value = handle_read_data(conn,&op,&connection_referenced);
+ if (0 == return_value)
+ {
+ abandon_connection = 0;
+ }
+ break;
+ case write_data:
+ /* NYI, but we need to go and find the state for the connection, find the operation
+ * which queued the write, and then get whatever data we need to write, then write it ! */
+ break;
+ case new_connection:
+ /* NYI, but this would consist of the same stuff which is currently in daemon.c.
+ * On NT, we'd use AcceptEx() */
+ break;
+ default:
+ break;
+ }
+ finished_chomping(conn);
+ } else {
+ PR_SetError(PR_IO_ERROR, return_value);
+ connection_referenced = 1; /* There is an outstanding refcnt on the conn, so we get to close the right one ! */
+ }
+
+ /* If anything went wrong with the connection above, such that we need to
+ * disconnect it, we'll know here and shoot it in the foot.
+ */
+ if ( (NULL != conn) && abandon_connection) {
+ disconnect_server(conn, conn->c_connid, op ? op->o_opid : -1, SLAPD_DISCONNECT_ABORT, 0 );
+ if (connection_referenced) {
+ connection_decrement_reference(conn);
+ }
+ }
+ }
+ PR_AtomicDecrement(&active_threads);
+}
+
+static int handle_read_data(Connection *conn,Operation **op,
+ int * connection_referenced)
+{
+ int return_value = 0;
+ int process_op = 0; /* Do we or do we not process a complete operation now ? */
+
+ if (is_new_operation(conn)) {
+ return_value = connection_operation_new(conn,op);
+ } else {
+ *op = get_current_op(conn);
+ }
+
+ /* if connection is closing */
+ if (return_value != 0) {
+ LDAPDebug(LDAP_DEBUG_CONNS,
+ "handle_read_data returns as conn %d closing, fd=%d\n",
+ conn->c_connid,conn->c_sd,0);
+ return return_value;
+ }
+
+ return_value = read_the_data(conn,&process_op);
+
+ if (0 == return_value) {
+ if (0 != process_op)
+ return_value = process_operation(conn,*op);
+ }
+ else
+ *connection_referenced = 1;
+
+ return return_value;
+}
+
+/* Function which does the work involved in servicing an LDAP operation. */
+static int process_operation(Connection *conn, Operation *op)
+{
+ Slapi_PBlock *pb = NULL;
+ unsigned long len, tag;
+ long msgid;
+ int return_value = 0;
+ int destroy_content = 1;
+
+
+ pb = (Slapi_PBlock *) slapi_ch_calloc( 1, sizeof(Slapi_PBlock) );
+ pb->pb_conn = conn;
+ pb->pb_op = op;
+ /* destroy operation content when done */
+ slapi_pblock_set (pb, SLAPI_DESTROY_CONTENT, &destroy_content);
+
+ if (! config_check_referral_mode()) {
+ PR_AtomicIncrement(&ops_initiated);
+ PR_AtomicIncrement(g_get_global_snmp_vars()->ops_tbl.dsInOps);
+ }
+
+ if ( (tag = ber_get_int( op->o_ber, &msgid ))
+ != LDAP_TAG_MSGID ) {
+ /* log, close and send error */
+ LDAPDebug( LDAP_DEBUG_ANY,
+ "conn=%d unable to read tag for incoming request\n", conn->c_connid, 0, 0 );
+ return_value = -1;
+ goto done;
+ }
+ op->o_msgid = msgid;
+
+ tag = ber_peek_tag( op->o_ber, &len );
+ switch ( tag ) {
+ case LBER_ERROR:
+ case LDAP_TAG_LDAPDN: /* optional username, for CLDAP */
+ /* log, close and send error */
+ LDAPDebug( LDAP_DEBUG_ANY,
+ "conn=%d ber_peek_tag returns 0x%lx\n", conn->c_connid, tag, 0 );
+ return_value = -1;
+ goto done;
+ default:
+ break;
+ }
+ op->o_tag = tag;
+
+ /* are we in referral-only mode? */
+ if (config_check_referral_mode() && tag != LDAP_REQ_UNBIND)
+ {
+ referral_mode_reply(pb);
+ goto done;
+ }
+
+ /* check if new password is required */
+ if(connection_need_new_password(conn, op, pb))
+ {
+ goto done;
+ }
+
+ /* if this is a bulk import, only "add" and "import done (extop)" are
+ * allowed */
+ if (conn->c_flags & CONN_FLAG_IMPORT) {
+ if ((tag != LDAP_REQ_ADD) && (tag != LDAP_REQ_EXTENDED)) {
+ /* no cookie for you. */
+ LDAPDebug(LDAP_DEBUG_ANY, "Attempted operation %d from "
+ "within bulk import\n", tag, 0, 0);
+ slapi_send_ldap_result(pb, LDAP_PROTOCOL_ERROR, NULL, NULL,
+ 0, NULL);
+ return_value = -1;
+ goto done;
+ }
+ }
+
+ /*
+ * Call the do_<operation> function to process this request.
+ */
+ connection_dispatch_operation(conn, op, pb);
+
+done:
+
+ /* If we're here, it means that we successfully completed an operation , so bump the counts */
+ inc_op_count(conn);
+
+ if ( !( pb->pb_op->o_flags & OP_FLAG_PS )) {
+ /*
+ * If not a persistent search, remove the operation
+ * from this connection's list.
+ */
+ PR_Lock( conn->c_mutex );
+ connection_remove_operation( conn, op );
+ PR_Unlock( conn->c_mutex );
+
+ /* destroying the pblock will cause destruction of the operation
+ * so this must happen before releasing the connection
+ */
+ slapi_pblock_destroy( pb );
+
+ PR_Lock( conn->c_mutex );
+ if (connection_release_nolock (conn) != 0)
+ {
+ return_value = -1;
+ }
+ PR_Unlock( conn->c_mutex );
+
+ }
+ return return_value;
+}
+
+/* Helper functions for the code above: */
+
+
+struct Conn_private {
+ /* First the platform-dependent part */
+#ifdef _WIN32
+ OVERLAPPED c_overlapped;
+ DWORD c_buffer_size;
+ char *c_buffer;
+ DWORD c_number_of_async_bytes_read;
+ DWORD c_buffer_offset;
+#else
+#endif
+ /* Now the platform independent part */
+ Operation *c_current_op;
+ int c_flags;
+};
+
+static void connection_free_private_buffer(Connection *conn)
+{
+#ifdef _WIN32
+ if (NULL != conn->c_private) {
+ slapi_ch_free( (void**)&conn->c_private->c_buffer);
+ }
+#else
+#endif
+}
+
+#define FLAG_CONN_HAD_SOME 1 /* Set when we've read the first piece of data already, means we don't need to allocate a new op */
+#define FLAG_CONN_COMPLETE 2 /* Set when we've read all of an LDAP operation request, means we can proceed to process it */
+
+
+/* Little helper functions */
+
+Operation *get_current_op(Connection *conn)
+{
+ Operation *return_op = conn->c_private->c_current_op;
+ PR_ASSERT(NULL != return_op);
+ return return_op;
+}
+
+static int is_new_operation(Connection *conn)
+{
+ if (0 == conn->c_private->c_flags) {
+ return 1;
+ } else {
+ return 0;
+ }
+}
+
+/* Called when a new operation comes in on a connection */
+static int connection_operation_new(Connection *conn, Operation **ppOp)
+{
+ /* we need to make a new operation structure and chain it onto the connection */
+ Operation *temp_op = NULL;
+ int rc;
+
+ PR_Lock( conn->c_mutex );
+ if (connection_is_active_nolock(conn) == 0) {
+ LDAPDebug(LDAP_DEBUG_CONNS,
+ "not creating a new operation when conn %d closing\n",
+ conn->c_connid,0,0);
+ PR_Unlock( conn->c_mutex );
+ return -1;
+ }
+ temp_op = operation_new( plugin_build_operation_action_bitmap( 0,
+ plugin_get_server_plg() ));
+ connection_add_operation( conn, temp_op);
+ rc = connection_acquire_nolock (conn);
+ PR_Unlock( conn->c_mutex );
+ /* Stash the op pointer in the connection structure for later use */
+ PR_ASSERT(NULL == conn->c_private->c_current_op);
+ conn->c_private->c_current_op = temp_op;
+ *ppOp = temp_op;
+ return rc;
+}
+
+/* Call this to tell the select thread to put us back into the read-ready signal set */
+static int add_to_select_set(Connection *conn)
+{
+ conn->c_gettingber = 0;
+ signal_listner();
+ return 0;
+}
+
+static int remove_from_select_set(Connection *conn)
+{
+ conn->c_gettingber = 1;
+ return 0;
+}
+
+/* Helper functions from here on are platform-dependent */
+/* First the NT ones */
+
+#ifdef _WIN32
+
+static HANDLE completion_port = INVALID_HANDLE_VALUE;
+#define COMPKEY_DIE ((DWORD) -1L) /* used to kill off workers */
+
+static int push_back_data(Connection *conn, size_t offset, size_t length);
+
+/* Called when we've read from the completion queue, so there's data
+ * waiting for us to pickup. We're told: the number of bytes read, the
+ * address of the buffer, the state of this connection (new op, middle of op).
+ */
+static int read_the_data(Connection *conn, int *process_op)
+{
+ Conn_private *priv = conn->c_private;
+ Operation *op = NULL;
+ DWORD Bytes_Read = 0;
+ char *Buffer = NULL;
+ int tag = 0;
+ int return_value = -1;
+ unsigned long ber_len = 0;
+ unsigned long Bytes_Scanned = 0;
+
+ op = priv->c_current_op;
+ Bytes_Read = priv->c_number_of_async_bytes_read;
+ Buffer = priv->c_buffer + priv->c_buffer_offset;
+
+ PR_ASSERT(NULL != op->o_ber);
+
+ /* Is this an SSL connection ? */
+ if (0 == (conn->c_flags & CONN_FLAG_SSL)) {
+ /* Not SSL */
+
+ if (! config_check_referral_mode()) {
+ /* Update stats */
+ PR_Lock( op_thread_lock );
+ (*(g_get_global_snmp_vars()->ops_tbl.dsBytesRecv)) += Bytes_Read;
+ PR_Unlock( op_thread_lock );
+ }
+
+ /* We need to read the data into the BER buffer */
+ /* This can return a tag pr LBER_DEFAULT, indicating some error condition */
+ tag = ber_get_next_buffer_ext( Buffer, Bytes_Read, &ber_len, op->o_ber, &Bytes_Scanned, conn->c_sb );
+ if(LBER_DEFAULT == tag)
+ {
+ if (0 == Bytes_Scanned)
+ {
+ /* Means we encountered an error---eg the client sent us pure crap---
+ a bunch of bytes which we took to be a tag, length, then we ran off the
+ end of the buffer. The next time we get here, we'll be returned LBER_DEFAULT
+ This means that everything we've seen up till now is useless because it wasn't
+ an LDAP message.
+ So, we toss it away ! */
+ if (errno == EMSGSIZE) {
+ log_ber_too_big_error(conn, ber_len, 0);
+ }
+ PR_Lock( conn->c_mutex );
+ connection_remove_operation( conn, op );
+ operation_free(&op, conn);
+ priv->c_current_op = NULL;
+ PR_Unlock( conn->c_mutex );
+ return -1; /* Abandon Connection */
+ }
+ }
+ if (is_ber_too_big(conn,ber_len))
+ {
+ PR_Lock( conn->c_mutex );
+ connection_remove_operation( conn, op );
+ operation_free(&op, conn);
+ priv->c_current_op = NULL;
+ PR_Unlock( conn->c_mutex );
+ return -1; /* Abandon Connection */
+ }
+
+ /* We set the flag to indicate that we'er in the middle of an op */
+ priv->c_flags |= FLAG_CONN_HAD_SOME;
+
+ /* Then we decide whether this is the last read for the current op */
+ /* and set the flag accordingly */
+ if (LBER_DEFAULT != tag) { /* we received a complete message */
+ if (LDAP_TAG_MESSAGE == tag) { /* looks like an LDAP message */
+ /* It's time to process this operation */
+ *process_op = 1;
+ priv->c_current_op = NULL;
+ priv->c_flags = 0;
+ } else {
+ /*
+ * We received a non-LDAP message. Log and close connection.
+ */
+ LDAPDebug( LDAP_DEBUG_ANY,
+ "conn=%d received a non-LDAP message"
+ " (tag 0x%lx, expected 0x%lx)\n",
+ conn->c_connid, tag, LDAP_TAG_MESSAGE );
+ PR_Lock( conn->c_mutex );
+ connection_remove_operation( conn, op );
+ operation_free(&op, conn);
+ priv->c_current_op = NULL;
+ PR_Unlock( conn->c_mutex );
+ return -1; /* Abandon Connection */
+ }
+ }
+
+ /* Finally, mark whether there's the beginning of another operation remaining in the buffer */
+ /* If there is, queue up another I/O completion request on the port to get it handled OK */
+ /* If not, issue a new read on the socket. */
+ if (Bytes_Scanned != Bytes_Read) {
+ if (connection_increment_reference(conn) == -1) {
+ LDAPDebug(LDAP_DEBUG_CONNS,
+ "could not acquire lock in issue_new_read as conn %d closing fd=%d\n",
+ conn->c_connid,conn->c_sd,0);
+ /* XXX how to handle this error? */
+ /* MAB: 25 Jan 01: let's try like this and pray this won't leak... */
+ /* GB : this should be OK because an error here
+ * means some other thread decided to close the
+ * connection, which mean a fatal error happened
+ * in that case just forget about the remaining
+ * data and return
+ */
+ return (0);
+ }
+ if ((return_value = push_back_data(conn,priv->c_overlapped.Offset + Bytes_Scanned,
+ Bytes_Read-Bytes_Scanned)) == -1) {
+ /* MAB: 25 jan 01 we need to decrement the conn refcnt before leaving... Otherwise,
+ * this thread will unbalance the ref_cnt inc and dec for this connection
+ * and the result is that the connection is never closed and instead is kept
+ * forever an never released -> this was causing a fd starvation on NT
+ */
+ connection_decrement_reference(conn);
+ LDAPDebug(LDAP_DEBUG_CONNS,
+ "push_back_data failed: closing conn %d fd=%d\n",
+ conn->c_connid,conn->c_sd,0);
+ }
+ } else {
+ priv->c_overlapped.Offset = 0;
+ return_value = issue_new_read(conn);
+ }
+ } else {
+ /* SSL */
+ if ( (tag = ber_get_next( conn->c_sb, &ber_len, op->o_ber ))
+ != LDAP_TAG_MESSAGE ) {
+ return( -1 );
+ }
+ if(is_ber_too_big(conn,ber_len))
+ {
+ return( -1 );
+ }
+ /* Put this connection back into the read-ready signal state */
+ /* priv->c_flags |= FLAG_CONN_COMPLETE; Redundant now */
+ /* It's time to process this operation */
+ *process_op = 1;
+ priv->c_current_op = NULL;
+ priv->c_flags = 0;
+ return_value = 0;
+ add_to_select_set(conn);
+ }
+
+ return return_value;
+}
+
+int push_back_data(Connection *conn, size_t offset, size_t length)
+{
+ /* Use PostQueuedCompletionStatus() to push the data back up the pipe */
+ BOOL return_bool = FALSE;
+
+ conn->c_private->c_overlapped.Offset = offset;
+ return_bool = PostQueuedCompletionStatus(completion_port,length,(DWORD)conn,&conn->c_private->c_overlapped);
+
+ if (return_bool) {
+ return 0;
+ } else {
+ return -1;
+ }
+}
+
+/* This function issues a new read operation on the connection.
+ * Called once we've finished reading everything from the buffer.
+ * VMS crusties will notice the similarity to $QIO.
+ */
+int issue_new_read(Connection *conn)
+{
+ BOOL return_bool = FALSE;
+ HANDLE socket = INVALID_HANDLE_VALUE;
+ void **buffer = NULL;
+ DWORD bytes_read = 0;
+ DWORD buffer_size = 0;
+ OVERLAPPED *overlapped = NULL;
+
+ PR_ASSERT(NULL != conn);
+ socket = (HANDLE)conn->c_sd;
+ PR_ASSERT(NULL != socket);
+
+ /* here we make sure that we have a buffer allocated */
+ buffer = &conn->c_private->c_buffer;
+ if (NULL == *buffer) {
+ *buffer = (void*)slapi_ch_malloc(LDAP_SOCKET_IO_BUFFER_SIZE);
+ if (NULL == *buffer) {
+ /* memory allocation failure */
+ return -1;
+ }
+ conn->c_private->c_buffer_size = LDAP_SOCKET_IO_BUFFER_SIZE;
+ }
+
+ buffer_size = conn->c_private->c_buffer_size;
+ overlapped = &conn->c_private->c_overlapped;
+
+ if (connection_increment_reference(conn) == -1) {
+ LDAPDebug(LDAP_DEBUG_CONNS,
+ "could not acquire lock in issue_new_read as conn %d closing fd=%d\n",
+ conn->c_connid,conn->c_sd,0);
+ /* This means that the connection is closing */
+ return -1;
+ }
+ return_bool = ReadFile(socket,*buffer,buffer_size,&bytes_read,overlapped);
+ if ( !return_bool && ERROR_IO_PENDING != GetLastError( ) ) {
+ /* This means that the connection is shot for some reason */
+ connection_decrement_reference(conn);
+ return -1;
+ } else {
+ /* Our work is done, i/o read now queued */
+ return 0;
+ }
+}
+
+static int wait_on_new_work(Connection **ppConn, work_type *type)
+{
+ /* Here, we wait on the I/O completion port for new data */
+ /* because we're not sure whether the completion port has been created yet,
+ * we wait 'till it has been.
+ */
+ Connection *temp_conn = NULL;
+ DWORD Bytes_Received = 0;
+ OVERLAPPED *pOverlapped = NULL;
+ BOOL return_bool = FALSE;
+
+ *type = read_data;
+
+ while ( (INVALID_HANDLE_VALUE == completion_port) && (!op_shutdown) ) {
+ Sleep(100);
+ }
+ while (1) {
+ if (op_shutdown) {
+ return EINTR;
+ }
+ return_bool = GetQueuedCompletionStatus(completion_port,&Bytes_Received,(DWORD*)&temp_conn,&pOverlapped,INFINITE);
+ if ((unsigned long)temp_conn == COMPKEY_DIE ) {
+ continue; /* kill this worker */
+ }
+ if (TRUE == return_bool) {
+ /* we successfully completed the I/O operation */
+ /* set the connection pointer the caller gave us to the one from the port */
+ PR_ASSERT(NULL != pOverlapped);
+ PR_ASSERT(NULL != temp_conn);
+ *ppConn = temp_conn;
+ /* store the # bytes read in the connection structure */
+ (*ppConn)->c_private->c_number_of_async_bytes_read = Bytes_Received;
+ (*ppConn)->c_private->c_buffer_offset = (*ppConn)->c_private->c_overlapped.Offset;
+ if( Bytes_Received == 0 )
+ {
+ /* 0 bytes received from a completed overlapped I/O
+ operation means the socket's been closed. */
+ break;
+ }
+ (*ppConn)->c_idlesince = current_time();
+ /* If we exit here, everything is OK */
+ connection_decrement_reference(temp_conn);
+ return 0;
+ }
+ if ( (FALSE == return_bool) && (NULL == pOverlapped) ) {
+ /* we timed out */
+ /* slapi_log_error( SLAPI_LOG_FATAL, "connection",
+ "GetQueuedCompletionStatus call timed out\n");*/
+ continue;
+ }
+ if ( (FALSE == return_bool) && (NULL != pOverlapped)) {
+ /* signifies some sort of i/o error, most likely an abortive close */
+ /* slapi_log_error( SLAPI_LOG_FATAL, "connection",
+ "GetQueuedCompletionStatus call failed; error - %ld\n", GetLastError());*/
+ if (NULL != temp_conn) {
+ /* If we were told the connection, return it--otherwise we can't tell which connection to close */
+ *ppConn = temp_conn;
+ }
+ break;
+ }
+ }
+ return EPIPE; /* we failed to read for some reason */
+}
+
+int connection_new_private(Connection *conn)
+{
+ /* first add to the completion port */
+ DWORD threads = 10; /* DBDB hackhack */
+ HANDLE socket = INVALID_HANDLE_VALUE;
+ HANDLE return_port = NULL;
+ Conn_private *priv = NULL;
+ int return_value = -1;
+
+ PR_ASSERT(NULL != conn);
+
+ socket = (HANDLE) conn->c_sd;
+
+ /* make the private data if it isn't already there */
+
+ if (NULL == conn->c_private) {
+ Conn_private *new_private = (Conn_private *)slapi_ch_malloc(sizeof(Conn_private));
+ if (NULL == new_private) {
+ /* memory allocation failed */
+ return -1;
+ }
+ conn->c_private = new_private;
+ ZeroMemory(conn->c_private,sizeof(Conn_private));
+ }
+ priv = conn->c_private;
+ /* Make sure the private structure is cleared */
+ /* Note: you must modify this code if the contents
+ * of the structure are changed---we can't simply
+ * zero the structure because we want to preserve the
+ * buffer. IMPORTANT---here we reuse the I/O buffer
+ * from before. This is deliberate, to avoid mallocing again */
+ ZeroMemory(&(priv->c_overlapped),sizeof(OVERLAPPED));
+ priv->c_number_of_async_bytes_read = 0;
+ priv->c_buffer_offset = 0;
+ priv->c_flags = 0;
+ priv->c_current_op = NULL;
+
+
+ if (INVALID_HANDLE_VALUE == completion_port) {
+ /* completion port not yet setup, we need to make it */
+ completion_port = CreateIoCompletionPort(INVALID_HANDLE_VALUE,NULL,0,0);
+ if (NULL == completion_port) {
+ LDAPDebug(LDAP_DEBUG_ANY,"Failed to create master I/O completion port\n",0,0,0);
+ return -1;
+ }
+ }
+ /* If the connection is SSL, don't do the right thing */
+ if (0 == (conn->c_flags & CONN_FLAG_SSL)) {
+ return_port = CreateIoCompletionPort(socket,completion_port,(DWORD)conn,0);
+ if (NULL == return_port) {
+ LDAPDebug(LDAP_DEBUG_ANY,"Failed to associate socket with I/O completion port, fd=%d,GetLastError = %d\n",socket,GetLastError(),0);
+ return -1;
+ }
+ /* Now queue the initial read on this connection */
+ return_value = issue_new_read(conn);
+ } else {
+ return_value = 0;
+ }
+
+ return return_value;
+}
+
+/* If all is well, this only gets called for SSL connections */
+int connection_activity(Connection *conn)
+{
+ /* First check that this really is an SSL connection */
+ if (0 == (conn->c_flags & CONN_FLAG_SSL)) {
+ return -1;
+ }
+ /* Now, the plan here is to push something up the IOCP pipe */
+ /* We need to fake something up so that the code which pulls
+ * it off the queue does the right thing. Here's what we do:
+ * We just call PostQueuedCompletionStatus like normal.
+ * The connection is marked as SSL, and it is this that the
+ * reading code notices. Simple !
+ */
+ /* Also, we need to participate in the signaling protocol to the select thread */
+ remove_from_select_set(conn);
+ /* We hold the lock already, increment the reference count, which will
+ be decremented in wait_for_new_work(). */
+ if (connection_acquire_nolock (conn) == -1) {
+ LDAPDebug(LDAP_DEBUG_CONNS,
+ "could not acquire lock in connection_activity as conn %d closing fd=%d\n",
+ conn->c_connid,conn->c_sd,0);
+ /* XXX how to handle this error? */
+ /* MAB: 25 Jan 01: let's return on error and pray this won't leak */
+ return (-1);
+ }
+ return push_back_data(conn, 0, 1);
+}
+
+static int finished_chomping(Connection *conn)
+{
+ /* On NT we don't need to do anything here */
+ return 0;
+}
+
+#else /* WIN32/UNIX */
+
+/*
+ * This is where the UNIX Helper functions would be if IO
+ * Completion Ports were supported on UNIX.
+ */
+
+#endif /* WIN32/UNIX */
+
+#else /* LDAP_IOCP */
+
+/*
+ * IO Completion Ports are not available on this platform.
+ */
+
+static int counter= 0; /* JCM Dumb Name */
+
+/* The connection private structure for UNIX turbo mode */
+struct Conn_private
+{
+ int turbo_flag; /* set if we are currently in turbo mode */
+ int previous_op_count; /* the operation counter value last time we sampled it, used to compute operation rate */
+ int operation_rate; /* rate (ops/sample period) at which this connection has been processing operations */
+ time_t previous_count_check_time; /* The wall clock time we last sampled the operation count */
+ size_t c_buffer_size; /* size of the socket read buffer */
+ char *c_buffer; /* pointer to the socket read buffer */
+ size_t c_buffer_bytes; /* number of bytes currently stored in the buffer */
+ size_t c_buffer_offset; /* offset to the location of new data in the buffer */
+};
+
+int
+connection_new_private(Connection *conn)
+{
+ if (NULL == conn->c_private) {
+ Conn_private *new_private = (Conn_private *)slapi_ch_calloc(1,sizeof(Conn_private));
+ if (NULL == new_private) {
+ /* memory allocation failed */
+ return -1;
+ }
+ conn->c_private = new_private;
+ }
+
+ /* The c_buffer is supposed to be NULL here, cleaned by connection_cleanup,
+ double check to avoid memory leak */
+ if (NULL == conn->c_private->c_buffer) {
+ conn->c_private->c_buffer = (char*)slapi_ch_malloc(LDAP_SOCKET_IO_BUFFER_SIZE);
+ if (NULL == conn->c_private->c_buffer) {
+ /* memory allocation failure */
+ return -1;
+ }
+ conn->c_private->c_buffer_size = LDAP_SOCKET_IO_BUFFER_SIZE;
+ }
+
+ /*
+ * Clear the private structure, preserving the buffer and length in
+ * case we are reusing the buffer.
+ */
+ {
+ char *c_buffer = conn->c_private->c_buffer;
+ size_t c_buffer_size = conn->c_private->c_buffer_size;;
+
+ memset( conn->c_private, 0, sizeof(Conn_private));
+ conn->c_private->c_buffer = c_buffer;
+ conn->c_private->c_buffer_size = c_buffer_size;
+ }
+
+ return 0;
+}
+
+static void
+connection_free_private_buffer(Connection *conn)
+{
+ if (NULL != conn->c_private) {
+ slapi_ch_free((void*)&(conn->c_private->c_buffer));
+ }
+}
+
+/*
+ * Turbo Mode:
+ * Turbo Connection Mode is designed to more efficiently
+ * serve a small number of highly active connections performing
+ * mainly search operations. It is only used on UNIX---completion
+ * ports on NT make it unnecessary.
+ * A connection can be in turbo mode, or not in turbo mode.
+ * For non-turbo mode, the code path is the same as was before:
+ * worker threads wait on a condition variable for work.
+ * When they awake they consult the operation queue for
+ * something to do, read the operation from the connection's socket,
+ * perform the operation and go back to waiting on the condition variable.
+ * In Turbo Mode, a worker thread becomes associated with a connection.
+ * It then waits not on the condition variable, but directly on read ready
+ * state on the connection's socket. When new data arrives, it decodes
+ * the operation and executes it, and then goes back to read another
+ * operation from the same socket, or block waiting on new data.
+ * The read is done non-blocking, wait in poll with a timeout.
+ *
+ * There is a mechanism to ensure that only the most active
+ * connections are in turbo mode at any time. If this were not
+ * the case we could starve out some client operation requests
+ * due to waiting on I/O in many turbo threads at the same time.
+ *
+ * Each worker thread periodically (every 10 seconds) examines
+ * the activity level for the connection it is processing.
+ * This applies regardless of whether the connection is
+ * currently in turbo mode or not. Activity is measured as
+ * the number of operations initiated since the last check was done.
+ * The N connections with the highest activity level are allowed
+ * to enter turbo mode. If the current connection is in the top N,
+ * then we decide to enter turbo mode. If the current connection
+ * is no longer in the top N, then we leave turbo mode.
+ * The decision to enter or leave turbo mode is taken under
+ * the connection mutex, preventing race conditions where
+ * more than one thread can change the turbo state of a connection
+ * concurrently.
+ */
+
+
+/* Connection status values returned by
+ connection_wait_for_new_pb(), connection_read_operation(), etc. */
+
+#define CONN_FOUND_WORK_TO_DO 0
+#define CONN_SHUTDOWN 1
+#define CONN_NOWORK 2
+#define CONN_DONE 3
+#define CONN_TIMEDOUT 4
+
+#define CONN_TURBO_TIMEOUT_INTERVAL 1000 /* milliseconds */
+#define CONN_TURBO_CHECK_INTERVAL 5 /* seconds */
+#define CONN_TURBO_PERCENTILE 50 /* proportion of threads allowed to be in turbo mode */
+#define CONN_TURBO_HYSTERESIS 0 /* avoid flip flopping in and out of turbo mode */
+
+int connection_wait_for_new_pb(Slapi_PBlock **ppb, PRIntervalTime interval)
+{
+ int ret = CONN_FOUND_WORK_TO_DO;
+
+ PR_Lock( op_thread_lock );
+
+ /* While there is no operation to do... */
+ while( counter < 1) {
+ /* Check if we should shutdown. */
+ if (op_shutdown) {
+ PR_Unlock( op_thread_lock );
+ return CONN_SHUTDOWN;
+ }
+ PR_WaitCondVar( op_thread_cv, interval);
+ }
+
+ /* There is some work to do. */
+
+ counter--;
+ PR_Unlock( op_thread_lock );
+
+ /* Get the next operation from the work queue. */
+
+ *ppb = get_pb();
+ if (*ppb == NULL) {
+ LDAPDebug( LDAP_DEBUG_ANY, "pb is null \n", 0, 0, 0 );
+ PR_Lock( op_thread_lock );
+ counter++;
+ PR_Unlock( op_thread_lock );
+ ret = CONN_NOWORK;
+ }
+ return ret;
+}
+
+void connection_make_new_pb(Slapi_PBlock **ppb, Connection *conn)
+{
+ /* In the classic case, the pb is made in connection_activity() and then
+ queued. get_pb() dequeues it. So we can just make it ourselves here */
+
+ /* *ppb = (Slapi_PBlock *) slapi_ch_calloc( 1, sizeof(Slapi_PBlock) ); */
+ *ppb = slapi_pblock_new();
+ (*ppb)->pb_conn = conn;
+ (*ppb)->pb_op = operation_new( plugin_build_operation_action_bitmap( 0,
+ plugin_get_server_plg() ));
+ connection_add_operation( conn, (*ppb)->pb_op );
+}
+
+
+/*
+ * Utility function called by connection_read_operation(). This is a
+ * small wrapper on top of libldap's ber_get_next_buffer_ext().
+ */
+static int
+get_next_from_buffer( void *buffer, size_t buffer_size, unsigned long *lenp,
+ unsigned long *tagp, BerElement *ber, Connection *conn )
+{
+ PRErrorCode err = 0;
+ PRInt32 syserr = 0;
+ unsigned long bytes_scanned = 0;
+
+ *lenp = 0;
+ *tagp = ber_get_next_buffer_ext( buffer, buffer_size, lenp, ber,
+ &bytes_scanned, conn->c_sb );
+ if (LBER_DEFAULT == *tagp && 0 == bytes_scanned) {
+ if (errno == EMSGSIZE) {
+ log_ber_too_big_error(conn, *lenp, 0);
+ err = SLAPD_DISCONNECT_BER_TOO_BIG;
+ } else {
+ syserr = errno;
+ }
+ /* Bad stuff happened, like the client sent us some junk */
+ LDAPDebug( LDAP_DEBUG_CONNS,
+ "ber_get_next failed for connection %d\n", conn->c_connid, 0, 0 );
+ /* reset private buffer */
+ conn->c_private->c_buffer_bytes = conn->c_private->c_buffer_offset = 0;
+
+ /* drop connection */
+ disconnect_server( conn, conn->c_connid, -1, err, syserr );
+ return -1;
+ }
+
+ /* success, or need to wait for more data */
+ conn->c_private->c_buffer_offset += bytes_scanned;
+ return 0;
+}
+
+/* Either read read data into the connection buffer, or fail with err set */
+static int
+connection_read_ldap_data(Connection *conn, PRInt32 *err)
+{
+ int ret = 0;
+ /* Is SASL encryption enabled on this connection ? */
+ if (conn->c_sasl_io) {
+ /* If so, call the SASL I/O layer */
+ ret = sasl_recv_connection(conn,conn->c_private->c_buffer, conn->c_private->c_buffer_size,err);
+ } else
+ {
+ /* Otherwise, just call PRRecv() */
+ ret = PR_Recv(conn->c_prfd,conn->c_private->c_buffer,conn->c_private->c_buffer_size,0,PR_INTERVAL_NO_WAIT);
+ if (ret < 0) {
+ *err = PR_GetError();
+ }
+ }
+ return ret;
+}
+
+/* Upon returning from this function, we have either:
+ 1. Read a PDU successfully.
+ 2. Detected some error condition with the connection which requires closing it.
+ 3. In Turbo mode, we Timed out without seeing any data.
+
+ We also handle the case where we read ahead beyond the current PDU
+ by buffering the data and setting the 'remaining_data' flag.
+
+ */
+int connection_read_operation(Connection *conn, Operation *op, unsigned long *tag, int *remaining_data)
+{
+ unsigned long len = 0;
+ int ret = 0;
+ int waits_done = 0;
+ long msgid;
+ int new_operation = 1; /* Are we doing the first I/O read for a new operation ? */
+ char *buffer = conn->c_private->c_buffer;
+ PRErrorCode err = 0;
+ PRInt32 syserr = 0;
+
+ /*
+ * if the socket is still valid, get the ber element
+ * waiting for us on this connection. timeout is handled
+ * in the low-level [secure_]read_function.
+ */
+ if ( (conn->c_sd == SLAPD_INVALID_SOCKET) ||
+ (conn->c_flags & CONN_FLAG_CLOSING) ) {
+ return CONN_DONE;
+ }
+
+ /* See if we should enable SASL I/O for this connection */
+ if (conn->c_enable_sasl_io) {
+ ret = sasl_io_setup(conn);
+ if (ret) {
+ LDAPDebug( LDAP_DEBUG_ANY,
+ "conn=%d unable to enable SASL I/O\n", conn->c_connid, 0, 0 );
+ disconnect_server( conn, conn->c_connid, -1, SLAPD_DISCONNECT_BAD_BER_TAG, EPROTO );
+ return CONN_DONE;
+ }
+ }
+
+ *tag = LBER_DEFAULT;
+ /* First check to see if we have buffered data from "before" */
+ if (conn->c_private->c_buffer_bytes - conn->c_private->c_buffer_offset) {
+ /* If so, use that data first */
+ if ( 0 != get_next_from_buffer( buffer
+ + conn->c_private->c_buffer_offset,
+ conn->c_private->c_buffer_bytes
+ - conn->c_private->c_buffer_offset,
+ &len, tag, op->o_ber, conn )) {
+ return CONN_DONE;
+ }
+ new_operation = 0;
+ }
+ /* If we still haven't seen a complete PDU, read from the network */
+ while (*tag == LBER_DEFAULT) {
+ int ioblocktimeout_waits = config_get_ioblocktimeout() / CONN_TURBO_TIMEOUT_INTERVAL;
+ /* We should never get here with data remaining in the buffer */
+ PR_ASSERT( !new_operation || 0 == (conn->c_private->c_buffer_bytes - conn->c_private->c_buffer_offset) );
+ /* We make a non-blocking read call */
+ /* ret = PR_Recv(conn->c_prfd,conn->c_private->c_buffer,conn->c_private->c_buffer_size,0,PR_INTERVAL_NO_WAIT); */
+ ret = connection_read_ldap_data(conn,&err);
+ if (ret <= 0) {
+ if (0 == ret) {
+ /* Connection is closed */
+ PR_Lock( conn->c_mutex );
+ disconnect_server_nomutex( conn, conn->c_connid, -1, SLAPD_DISCONNECT_BAD_BER_TAG, 0 );
+ conn->c_gettingber = 0;
+ PR_Unlock( conn->c_mutex );
+ signal_listner();
+ return CONN_DONE;
+ }
+ /* err = PR_GetError(); */
+ /* If we would block, we need to poll for a while */
+ if ( SLAPD_PR_WOULD_BLOCK_ERROR( err ) ) {
+ struct PRPollDesc pr_pd;
+ PRIntervalTime timeout = PR_MillisecondsToInterval(CONN_TURBO_TIMEOUT_INTERVAL);
+ pr_pd.fd = (PRFileDesc *)conn->c_prfd;
+ pr_pd.in_flags = PR_POLL_READ;
+ pr_pd.out_flags = 0;
+ ret = PR_Poll(&pr_pd, 1, timeout);
+ waits_done++;
+ /* Did we time out ? */
+ if (0 == ret) {
+ /* We timed out, should the server shutdown ? */
+ if (op_shutdown) {
+ return CONN_SHUTDOWN;
+ }
+ /* We timed out, is this the first read in a PDU ? */
+ if (new_operation) {
+ /* If so, we return */
+ return CONN_TIMEDOUT;
+ } else {
+ /* Otherwise we loop, unless we exceeded the ioblock timeout */
+ if (waits_done > ioblocktimeout_waits) {
+ LDAPDebug( LDAP_DEBUG_CONNS,"ioblock timeout expired on connection %d\n", conn->c_connid, 0, 0 );
+ disconnect_server( conn, conn->c_connid, -1,
+ SLAPD_DISCONNECT_IO_TIMEOUT, 0 );
+ return CONN_DONE;
+ } else {
+
+ /* The turbo mode may cause threads starvation.
+ Do a yield here to reduce the starving.
+ */
+ PR_Sleep(PR_INTERVAL_NO_WAIT);
+
+ continue;
+ }
+ }
+ }
+ if (-1 == ret) {
+ /* PR_Poll call failed */
+ err = PR_GetError();
+ syserr = PR_GetOSError();
+ LDAPDebug( LDAP_DEBUG_ANY,
+ "PR_Poll for connection %d returns %d (%s)\n", conn->c_connid, err, slapd_pr_strerror( err ) );
+ /* If this happens we should close the connection */
+ disconnect_server( conn, conn->c_connid, -1, err, syserr );
+ return CONN_DONE;
+ }
+ } else {
+ /* Some other error, typically meaning bad stuff */
+ syserr = PR_GetOSError();
+ LDAPDebug( LDAP_DEBUG_CONNS,
+ "PR_Recv for connection %d returns %d (%s)\n", conn->c_connid, err, slapd_pr_strerror( err ) );
+ /* If this happens we should close the connection */
+ disconnect_server( conn, conn->c_connid, -1, err, syserr );
+ return CONN_DONE;
+ }
+ } else {
+ /* We read some data off the network, do something with it */
+ conn->c_private->c_buffer_bytes = ret;
+ conn->c_private->c_buffer_offset = 0;
+
+ if ( get_next_from_buffer( buffer,
+ conn->c_private->c_buffer_bytes
+ - conn->c_private->c_buffer_offset,
+ &len, tag, op->o_ber, conn ) != 0 ) {
+ return CONN_DONE;
+ }
+
+ new_operation = 0;
+ ret = 0;
+ waits_done = 0; /* got some data: reset counter */
+ }
+ }
+ /* If there is remaining buffered data, set the flag to tell the caller */
+ if (conn->c_private->c_buffer_bytes - conn->c_private->c_buffer_offset) {
+ *remaining_data = 1;
+ }
+
+ if ( *tag != LDAP_TAG_MESSAGE ) {
+ /*
+ * We received a non-LDAP message. Log and close connection.
+ */
+ LDAPDebug( LDAP_DEBUG_ANY,
+ "conn=%d received a non-LDAP message (tag 0x%lx, expected 0x%lx)\n",
+ conn->c_connid, *tag, LDAP_TAG_MESSAGE );
+ disconnect_server( conn, conn->c_connid, -1,
+ SLAPD_DISCONNECT_BAD_BER_TAG, EPROTO );
+ return CONN_DONE;
+ }
+
+ if ( (*tag = ber_get_int( op->o_ber, &msgid ))
+ != LDAP_TAG_MSGID ) {
+ /* log, close and send error */
+ LDAPDebug( LDAP_DEBUG_ANY,
+ "conn=%d unable to read tag for incoming request\n", conn->c_connid, 0, 0 );
+ disconnect_server( conn, conn->c_connid, -1, SLAPD_DISCONNECT_BAD_BER_TAG, EPROTO );
+ return CONN_DONE;
+ }
+ if(is_ber_too_big(conn,len))
+ {
+ disconnect_server( conn, conn->c_connid, -1, SLAPD_DISCONNECT_BER_TOO_BIG, 0 );
+ return CONN_DONE;
+ }
+ op->o_msgid = msgid;
+
+ *tag = ber_peek_tag( op->o_ber, &len );
+ switch ( *tag ) {
+ case LBER_ERROR:
+ case LDAP_TAG_LDAPDN: /* optional username, for CLDAP */
+ /* log, close and send error */
+ LDAPDebug( LDAP_DEBUG_ANY,
+ "conn=%d ber_peek_tag returns 0x%lx\n", conn->c_connid, *tag, 0 );
+ disconnect_server( conn, conn->c_connid, -1, SLAPD_DISCONNECT_BER_PEEK, EPROTO );
+ return CONN_DONE;
+ default:
+ break;
+ }
+ op->o_tag = *tag;
+ return ret;
+}
+
+void connection_make_readable(Connection *conn)
+{
+ PR_Lock( conn->c_mutex );
+ conn->c_gettingber = 0;
+ PR_Unlock( conn->c_mutex );
+ signal_listner();
+}
+
+/*
+ * Figure out the operation completion rate for this connection
+ */
+void connection_check_activity_level(Connection *conn)
+{
+ int current_count = 0;
+ int delta_count = 0;
+ PR_Lock( conn->c_mutex );
+ /* get the current op count */
+ current_count = conn->c_opscompleted;
+ /* compare to the previous op count */
+ delta_count = current_count - conn->c_private->previous_op_count;
+ /* delta is the rate, store that */
+ conn->c_private->operation_rate = delta_count;
+ /* store current count in the previous count slot */
+ conn->c_private->previous_op_count = current_count;
+ /* update the last checked time */
+ conn->c_private->previous_count_check_time = current_time();
+ PR_Unlock( conn->c_mutex );
+ LDAPDebug(LDAP_DEBUG_CONNS,"conn %d activity level = %d\n",conn->c_connid,delta_count,0);
+}
+
+typedef struct table_iterate_info_struct {
+ int connection_count;
+ int rank_count;
+ int our_rate;
+} table_iterate_info;
+
+int table_iterate_function(Connection *conn, void *arg)
+{
+ int ret = 0;
+ table_iterate_info *pinfo = (table_iterate_info*)arg;
+ pinfo->connection_count++;
+ if (conn->c_private->operation_rate > pinfo->our_rate) {
+ pinfo->rank_count++;
+ }
+ return ret;
+}
+
+/*
+ * Scan the list of active connections, evaluate our relative rank
+ * for connection activity.
+ */
+void connection_find_our_rank(Connection *conn,int *connection_count, int *our_rank)
+{
+ int ret = 0;
+ table_iterate_info info = {0};
+ info.our_rate = conn->c_private->operation_rate;
+ ret = connection_table_iterate_active_connections(the_connection_table, &info, &table_iterate_function);
+ *connection_count = info.connection_count;
+ *our_rank = info.rank_count;
+}
+
+/*
+ * Evaluate the turbo policy for this connection
+ */
+void connection_enter_leave_turbo(Connection *conn, int *new_turbo_flag)
+{
+ int current_mode = 0;
+ int new_mode = 0;
+ int connection_count = 0;
+ int our_rank = 0;
+ int threshold_rank = 0;
+ PR_Lock(conn->c_mutex);
+ /* We can already be in turbo mode, or not */
+ current_mode = conn->c_private->turbo_flag;
+ if(conn->c_private->operation_rate == 0) {
+ /* The connection is ranked by the passed activities. If some other connection have more activity,
+ increase rank by one. The highest rank is least activity, good candidates to move out of turbo mode.
+ However, if no activity on all the connections, then every connection gets 0 rank, so none move out.
+ No bother to do so much calcuation, short-cut to non-turbo mode if no activities in passed interval */
+ new_mode = 0;
+ } else {
+ connection_find_our_rank(conn,&connection_count, &our_rank);
+ LDAPDebug(LDAP_DEBUG_CONNS,"conn %d turbo rank = %d out of %d conns\n",conn->c_connid,our_rank,connection_count);
+ threshold_rank = (int)((double)active_threads * ((double)CONN_TURBO_PERCENTILE / 100.0) );
+
+ /* adjust threshold_rank according number of connections,
+ less turbo threads as more connections,
+ one measure to reduce thread startvation.
+ */
+ if (connection_count > threshold_rank) {
+ threshold_rank -= (connection_count - threshold_rank) / 5;
+ }
+
+ if (current_mode) {
+ /* We're currently in turbo mode */
+ /* Policy says that we stay in turbo mode provided
+ connection activity is still high.
+ */
+ if (our_rank - CONN_TURBO_HYSTERESIS < threshold_rank) {
+ /* Stay in turbo mode */
+ new_mode = 1;
+ } else {
+ /* Exit turbo mode */
+ new_mode = 0;
+ }
+ } else {
+ /* We're currently not in turbo mode */
+ /* Policy says that we go into turbo mode if
+ recent connection activity is high.
+ */
+ if (our_rank + CONN_TURBO_HYSTERESIS < threshold_rank) {
+ /* Enter turbo mode */
+ new_mode = 1;
+ } else {
+ /* Stay out of turbo mode */
+ new_mode = 0;
+ }
+ }
+ }
+ conn->c_private->turbo_flag = new_mode;
+ PR_Unlock(conn->c_mutex);
+ if (current_mode != new_mode) {
+ if (current_mode) {
+ LDAPDebug(LDAP_DEBUG_CONNS,"conn %d leaving turbo mode\n",conn->c_connid,0,0);
+ } else {
+ LDAPDebug(LDAP_DEBUG_CONNS,"conn %d entering turbo mode\n",conn->c_connid,0,0);
+ }
+ }
+ *new_turbo_flag = new_mode;
+}
+
+static void
+connection_threadmain()
+{
+ Slapi_PBlock *pb = NULL;
+ PRIntervalTime interval = PR_SecondsToInterval(10);
+ Connection *conn = NULL;
+ Operation *op;
+ unsigned long tag = 0;
+ int need_wakeup;
+ int thread_turbo_flag = 0;
+ int ret = 0;
+ int more_data = 0;
+
+#if defined( OSF1 ) || defined( hpux )
+ /* Arrange to ignore SIGPIPE signals. */
+ SIGNAL( SIGPIPE, SIG_IGN );
+#endif
+
+ while (1) {
+ int is_timedout = 0;
+
+ if( op_shutdown ) {
+ LDAPDebug( LDAP_DEBUG_TRACE,
+ "op_thread received shutdown signal\n", 0, 0, 0 );
+ PR_AtomicDecrement(&active_threads);
+ return;
+ }
+
+ if (!thread_turbo_flag && (NULL == pb) && !more_data) {
+ /* If more data is left from the previous connection_read_operation,
+ we should finish the op now. Client might be thinking it's
+ done sending the request and wait for the response forever.
+ [blackflag 624234] */
+ ret = connection_wait_for_new_pb(&pb,interval);
+ switch (ret) {
+ case CONN_NOWORK:
+ continue;
+ case CONN_SHUTDOWN:
+ LDAPDebug( LDAP_DEBUG_TRACE,
+ "op_thread received shutdown signal\n", 0, 0, 0 );
+ PR_AtomicDecrement(&active_threads);
+ return;
+ case CONN_FOUND_WORK_TO_DO:
+ default:
+ break;
+ }
+ } else if (NULL == pb) {
+
+ /* The turbo mode may cause threads starvation.
+ Do a yield here to reduce the starving
+ */
+ PR_Sleep(PR_INTERVAL_NO_WAIT);
+
+ PR_Lock(conn->c_mutex);
+ /* Make our own pb in turbo mode */
+ connection_make_new_pb(&pb,conn);
+ PR_Unlock(conn->c_mutex);
+ if (! config_check_referral_mode()) {
+ PR_AtomicIncrement(&ops_initiated);
+ PR_AtomicIncrement(g_get_global_snmp_vars()->ops_tbl.dsInOps);
+ }
+ }
+ /* Once we're here we have a pb */
+ conn = pb->pb_conn;
+ op = pb->pb_op;
+
+ more_data = 0;
+ ret = connection_read_operation(conn,op,&tag,&more_data);
+
+#define DB_PERF_TURBO 1
+#if defined(DB_PERF_TURBO)
+ /* If it's been a while since we last did it ... */
+ if (current_time() - conn->c_private->previous_count_check_time > CONN_TURBO_CHECK_INTERVAL) {
+ int new_turbo_flag = 0;
+ /* Check the connection's activity level */
+ connection_check_activity_level(conn);
+ /* And if appropriate, change into or out of turbo mode */
+ connection_enter_leave_turbo(conn,&new_turbo_flag);
+ thread_turbo_flag = new_turbo_flag;
+ }
+
+ /* turn off turbo mode immediately if any pb waiting in global queue */
+ if (thread_turbo_flag && (counter > 0)) {
+ thread_turbo_flag = 0;
+ LDAPDebug(LDAP_DEBUG_CONNS,"conn %d leaving turbo mode\n",conn->c_connid,0,0);
+ }
+#endif
+
+ switch (ret) {
+ case CONN_DONE:
+ /* This means that the connection was closed, so clear turbo mode */
+ /*FALLTHROUGH*/
+ case CONN_TIMEDOUT:
+ thread_turbo_flag = 0;
+ is_timedout = 1;
+ /* note:
+ * should call connection_make_readable after the op is removed
+ * connection_make_readable(conn);
+ */
+ goto done;
+ case CONN_SHUTDOWN:
+ LDAPDebug( LDAP_DEBUG_TRACE,
+ "op_thread received shutdown signal\n", 0, 0, 0 );
+ PR_AtomicDecrement(&active_threads);
+ return;
+ default:
+ break;
+ }
+
+ /*
+ * Do not put the connection back to the read ready poll list
+ * if the operation is unbind. Unbind will close the socket.
+ * Similarly, if we are in turbo mode, don't send the socket
+ * back to the poll set.
+ * more_data: [blackflag 624234]
+ */
+ if (tag != LDAP_REQ_UNBIND && (!thread_turbo_flag) && !more_data) {
+ connection_make_readable(conn);
+ }
+
+ /* are we in referral-only mode? */
+ if (config_check_referral_mode() && tag != LDAP_REQ_UNBIND) {
+ referral_mode_reply(pb);
+ goto done;
+ }
+
+ /* check if new password is required */
+ if(connection_need_new_password(conn, op, pb)) {
+ goto done;
+ }
+
+ /* if this is a bulk import, only "add" and "import done"
+ * are allowed */
+ if (conn->c_flags & CONN_FLAG_IMPORT) {
+ if ((tag != LDAP_REQ_ADD) && (tag != LDAP_REQ_EXTENDED)) {
+ /* no cookie for you. */
+ LDAPDebug(LDAP_DEBUG_ANY, "Attempted operation %d "
+ "from within bulk import\n",
+ tag, 0, 0);
+ slapi_send_ldap_result(pb, LDAP_PROTOCOL_ERROR, NULL,
+ NULL, 0, NULL);
+ goto done;
+ }
+ }
+
+ /*
+ * Call the do_<operation> function to process this request.
+ */
+ connection_dispatch_operation(conn, op, pb);
+
+done:
+ /*
+ * done with this operation. delete it from the op
+ * queue for this connection, delete the number of
+ * threads devoted to this connection, and see if
+ * there's more work to do right now on this conn.
+ */
+
+ /* number of ops on this connection */
+ PR_AtomicIncrement(&conn->c_opscompleted);
+ /* total number of ops for the server */
+ PR_AtomicIncrement(&ops_completed);
+ /* If this op isn't a persistent search, remove it */
+ if ( !( pb->pb_op->o_flags & OP_FLAG_PS )) {
+ /* delete from connection operation queue & decr refcnt */
+ PR_Lock( conn->c_mutex );
+ connection_remove_operation( conn, op );
+ /* destroying the pblock will cause destruction of the operation
+ * so this must happend before releasing the connection
+ */
+ slapi_pblock_destroy( pb );
+
+ /* If we're in turbo mode, we keep our reference to the connection
+ alive */
+ if (!thread_turbo_flag && !more_data) {
+ connection_release_nolock (conn);
+ }
+ PR_Unlock( conn->c_mutex );
+ }
+ if (1 == is_timedout && !more_data)
+ connection_make_readable(conn);
+ pb = NULL;
+
+ if (!thread_turbo_flag && !more_data) { /* Don't do this in turbo mode */
+ PR_Lock( conn->c_mutex );
+ /* if the threadnumber of now below the maximum, wakeup
+ * the listener thread so that we start polling on this
+ * connection again
+ */
+ /* DBDB I think this code is bogus -- we already signaled the listener above here */
+ if (conn->c_threadnumber == config_get_maxthreadsperconn())
+ need_wakeup = 1;
+ else
+ need_wakeup = 0;
+ conn->c_threadnumber--;
+ PR_Unlock( conn->c_mutex );
+
+ if (need_wakeup)
+ signal_listner();
+ }
+
+
+ } /* while (1) */
+}
+
+/* thread need to hold conn->c_mutex before calling this function */
+int
+connection_activity(Connection *conn)
+{
+ Slapi_PBlock *pb;
+
+ connection_make_new_pb(&pb, conn);
+
+ /* Add pb to the end of the work queue. */
+ add_pb( pb );
+
+ /* Check if exceed the max thread per connection. If so, increment
+ c_pbwait. Otherwise increment the counter and notify the cond. var.
+ there is work to do. */
+
+ if (connection_acquire_nolock (conn) == -1) {
+ LDAPDebug(LDAP_DEBUG_CONNS,
+ "could not acquire lock in connection_activity as conn %d closing fd=%d\n",
+ conn->c_connid,conn->c_sd,0);
+ /* XXX how to handle this error? */
+ /* MAB: 25 Jan 01: let's return on error and pray this won't leak */
+ return (-1);
+ }
+ conn->c_gettingber = 1;
+ conn->c_threadnumber++;
+ PR_Lock( op_thread_lock );
+ counter++;
+ PR_NotifyCondVar( op_thread_cv );
+ PR_Unlock( op_thread_lock );
+
+ if (! config_check_referral_mode()) {
+ PR_AtomicIncrement(&ops_initiated);
+ PR_AtomicIncrement(g_get_global_snmp_vars()->ops_tbl.dsInOps);
+ }
+ return 0;
+}
+
+/* add_pb(): will add a pb to the end of the global work queue. The work queue
+ is implemented as a singal link list. */
+
+static void
+add_pb( Slapi_PBlock *pb)
+{
+
+ struct Slapi_PBlock_q *new_pb=NULL;
+
+ LDAPDebug( LDAP_DEBUG_TRACE, "add_pb \n", 0, 0, 0 );
+
+ new_pb = (struct Slapi_PBlock_q *) slapi_ch_malloc ( sizeof( struct Slapi_PBlock_q ));
+ new_pb->pb = pb;
+ new_pb->next_pb =NULL;
+
+ PR_Lock( pb_q_lock );
+ if (last_pb == NULL) {
+ last_pb = new_pb;
+ first_pb = new_pb;
+ }
+ else {
+ last_pb->next_pb = new_pb;
+ last_pb = new_pb;
+ }
+ PR_Unlock( pb_q_lock );
+}
+
+/* get_pb(): will get a pb from the begining of the work queue, return NULL if
+ the queue is empty.*/
+
+static Slapi_PBlock *
+get_pb()
+{
+
+ struct Slapi_PBlock_q *tmp = NULL;
+ Slapi_PBlock *pb;
+
+ LDAPDebug( LDAP_DEBUG_TRACE, "get_pb \n", 0, 0, 0 );
+ PR_Lock( pb_q_lock );
+ if (first_pb == NULL) {
+ PR_Unlock( pb_q_lock );
+ LDAPDebug( LDAP_DEBUG_ANY, "get_pb: the work queue is empty.\n",
+ 0, 0, 0 );
+ return NULL;
+ }
+
+ tmp = first_pb;
+ if ( first_pb == last_pb ) {
+ last_pb = NULL;
+ }
+ first_pb = tmp->next_pb;
+ PR_Unlock( pb_q_lock );
+
+ pb = tmp->pb;
+ /* Free the memory used by the pb found. */
+ free ((char *) tmp);
+
+ return (pb);
+}
+#endif /* LDAP_IOCP */
+
+
+/* Helper functions common to both varieties of connection code: */
+
+/* op_thread_cleanup() : This function is called by daemon thread when it gets
+ the slapd_shutdown signal. It will set op_shutdown to 1 and notify
+ all thread waiting on op_thread_cv to terminate. */
+
+void
+op_thread_cleanup()
+{
+#ifdef _WIN32
+ int i;
+ PRIntervalTime interval;
+ int max_threads = config_get_threadnumber();
+ interval = PR_SecondsToInterval(3);
+#endif
+ LDAPDebug( LDAP_DEBUG_ANY,
+ "slapd shutting down - signaling operation threads\n", 0, 0, 0);
+
+ PR_Lock( op_thread_lock );
+ op_shutdown = 1;
+ PR_NotifyAllCondVar ( op_thread_cv );
+ PR_Unlock( op_thread_lock );
+#ifdef _WIN32
+ LDAPDebug( LDAP_DEBUG_ANY,
+ "slapd shutting down - waiting for %d threads to terminate\n",
+ active_threads, 0, 0 );
+ /* kill off each worker waiting on GetQueuedCompletionStatus */
+ for ( i = 0; i < max_threads; ++ i )
+ {
+ PostQueuedCompletionStatus( completion_port, 0, COMPKEY_DIE ,0);
+ }
+ /* don't sleep: there's no reason to do so here DS_Sleep(interval); */ /* sleep 3 seconds */
+#endif
+}
+
+static void
+connection_add_operation(Connection* conn,Operation* op)
+{
+ Operation **olist= &conn->c_ops;
+ int id= conn->c_opsinitiated++;
+ int connid= conn->c_connid;
+ Operation **tmp;
+
+ /* slapi_ch_stop_recording(); */
+
+ for ( tmp = olist; *tmp != NULL; tmp = &(*tmp)->o_next )
+ ; /* NULL */
+
+ *tmp= op;
+ op->o_opid = id;
+ op->o_connid = connid;
+ /* Call the plugin extension constructors */
+ op->o_extension = factory_create_extension(get_operation_object_type(),op,conn);
+}
+
+/*
+ * Find an Operation on the Connection, and zap it in the butt.
+ * Call this function with conn->c_mutex locked.
+ */
+void
+connection_remove_operation( Connection *conn, Operation *op )
+{
+ Operation **olist= &conn->c_ops;
+ Operation **tmp;
+
+ for ( tmp = olist; *tmp != NULL && *tmp != op; tmp = &(*tmp)->o_next )
+ ; /* NULL */
+
+ if ( *tmp == NULL )
+ {
+ LDAPDebug( LDAP_DEBUG_ANY, "connection_remove_operation: can't find op %d for conn %d\n",
+ op->o_msgid, conn->c_connid, 0 );
+ }
+ else
+ {
+ *tmp = (*tmp)->o_next;
+ }
+}
+
+
+/*
+ * Return a non-zero value if any operations are pending on conn.
+ * Operation op2ignore is ignored (okay to pass NULL). Typically, op2ignore
+ * is the caller's op (because the caller wants to check if all other
+ * ops are done).
+ * If test_resultsent is non-zero, operations that have already sent
+ * a result to the client are ignored.
+ * Call this function with conn->c_mutex locked.
+ */
+int
+connection_operations_pending( Connection *conn, Operation *op2ignore,
+ int test_resultsent )
+{
+ Operation *op;
+
+ PR_ASSERT( conn != NULL );
+
+ for ( op = conn->c_ops; op != NULL; op = op->o_next ) {
+ if ( op == op2ignore ) {
+ continue;
+ }
+ if ( !test_resultsent || op->o_status != SLAPI_OP_STATUS_RESULT_SENT ) {
+ break;
+ }
+ }
+
+ return( op != NULL );
+}
+
+
+/* Copy the authorization identity from the connection struct into the
+ * operation struct. We do this late, because an operation might start
+ * before authentication is complete, at least on an SSL connection.
+ * We want each operation to get its authorization identity after the
+ * SSL software has had its chance to finish the SSL handshake;
+ * that is, after the first few bytes of the request are received.
+ * In particular, we want the first request from an LDAPS client
+ * to have an authorization identity derived from the initial SSL
+ * handshake.
+ */
+static void
+op_copy_identity(Connection *conn, Operation *op)
+{
+ size_t dnlen;
+ size_t typelen;
+
+ PR_Lock( conn->c_mutex );
+ dnlen= conn->c_dn ? strlen (conn->c_dn) : 0;
+ typelen= conn->c_authtype ? strlen (conn->c_authtype) : 0;
+
+ slapi_sdn_done(&op->o_sdn);
+ slapi_ch_free((void **) &(op->o_authtype));
+ if (dnlen <= 0 && typelen <= 0) {
+ op->o_authtype = NULL;
+ } else {
+ char* id = slapi_ch_malloc (typelen + 1);
+ if (typelen <= 0)
+ id[dnlen+1] = '\0';
+ else
+ memcpy (id, conn->c_authtype, typelen + 1);
+ slapi_sdn_set_dn_byval(&op->o_sdn,conn->c_dn);
+ op->o_authtype = id;
+ }
+ /* XXX We should also copy c_client_cert into *op here; it's
+ * part of the authorization identity. The operation's copy
+ * (not c_client_cert) should be used for access control.
+ */
+
+ /* copy isroot flag as well so root DN privileges are preserved */
+ op->o_isroot = conn->c_isroot;
+ PR_Unlock( conn->c_mutex );
+}
+
+
+static int
+is_ber_too_big(const Connection *conn,unsigned long ber_len)
+{
+ unsigned long maxbersize= config_get_maxbersize();
+ if(ber_len>maxbersize)
+ {
+ log_ber_too_big_error(conn, ber_len, maxbersize);
+ return 1;
+ }
+ return 0;
+}
+
+
+/*
+ * Pass 0 for maxbersize if you do not have it handy. It is also OK to pass
+ * 0 for ber_len, in which case a slightly less informative message is
+ * logged.
+ */
+static void
+log_ber_too_big_error(const Connection *conn, unsigned long ber_len,
+ unsigned long maxbersize)
+{
+ if (0 == maxbersize) {
+ maxbersize= config_get_maxbersize();
+ }
+ if (0 == ber_len) {
+ slapi_log_error( SLAPI_LOG_FATAL, "connection",
+ "conn=%d fd=%d Incoming BER Element was too long, max allowable"
+ " is %ld bytes. Change the nsslapd-maxbersize attribute in"
+ " cn=config to increase.\n",
+ conn->c_connid, conn->c_sd, maxbersize );
+ } else {
+ slapi_log_error( SLAPI_LOG_FATAL, "connection",
+ "conn=%d fd=%d Incoming BER Element was %ld bytes, max allowable"
+ " is %ld bytes. Change the nsslapd-maxbersize attribute in"
+ " cn=config to increase.\n",
+ conn->c_connid, conn->c_sd, ber_len, maxbersize );
+ }
+}
+
+
+void
+disconnect_server( Connection *conn, int opconnid, int opid, PRErrorCode reason, PRInt32 error )
+{
+ PR_Lock( conn->c_mutex );
+ disconnect_server_nomutex( conn, opconnid, opid, reason, error );
+ PR_Unlock( conn->c_mutex );
+}
+
+static ps_wakeup_all_fn_ptr ps_wakeup_all_fn = NULL;
+
+/*
+ * disconnect_server - close a connection. takes the connection to close,
+ * the connid associated with the operation generating the close (so we
+ * don't accidentally close a connection that's not ours), and the opid
+ * of the operation generating the close (for logging purposes).
+ */
+
+void
+disconnect_server_nomutex( Connection *conn, int opconnid, int opid, PRErrorCode reason, PRInt32 error )
+{
+ if ( ( conn->c_sd != SLAPD_INVALID_SOCKET &&
+ conn->c_connid == opconnid ) && !(conn->c_flags & CONN_FLAG_CLOSING) ) {
+
+ /*
+ * PR_Close must be called before anything else is done because
+ * of NSPR problem on NT which requires that the socket on which
+ * I/O timed out is closed before any other I/O operation is
+ * attempted by the thread.
+ * WARNING : As of today the current code does not fulfill the
+ * requirements above.
+ */
+
+ /* Mark that the socket should be closed on this connection.
+ * We don't want to actually close the socket here, because
+ * the listener thread could be PR_Polling over it right now.
+ * The last thread to stop using the connection will do the closing.
+ */
+ conn->c_flags |= CONN_FLAG_CLOSING;
+ g_decrement_current_conn_count();
+
+ /*
+ * Print the error captured above.
+ */
+ if (error && (EPIPE != error) ) {
+ slapi_log_access( LDAP_DEBUG_STATS,
+ "conn=%d op=%d fd=%d closed error %d (%s) - %s\n",
+ conn->c_connid, opid, conn->c_sd, error,
+ slapd_system_strerror(error),
+ slapd_pr_strerror(reason));
+ } else {
+ slapi_log_access( LDAP_DEBUG_STATS,
+ "conn=%d op=%d fd=%d closed - %s\n",
+ conn->c_connid, opid, conn->c_sd,
+ slapd_pr_strerror(reason));
+ }
+
+ if (! config_check_referral_mode()) {
+ PR_AtomicDecrement(g_get_global_snmp_vars()->ops_tbl.dsConnections);
+ }
+
+ conn->c_gettingber = 0;
+ connection_abandon_operations( conn );
+
+ if (! config_check_referral_mode()) {
+ /*
+ * If any of the outstanding operations on this
+ * connection were persistent searches, then
+ * ding all the persistent searches to get them
+ * to notice that their operations have been abandoned.
+ */
+ int found_ps = 0;
+ Operation *o;
+
+ for ( o = conn->c_ops; !found_ps && o != NULL; o = o->o_next ) {
+ if ( o->o_flags & OP_FLAG_PS ) {
+ found_ps = 1;
+ }
+ }
+ if ( found_ps ) {
+ if ( NULL == ps_wakeup_all_fn ) {
+ if ( get_entry_point( ENTRY_POINT_PS_WAKEUP_ALL,
+ (caddr_t *)(&ps_wakeup_all_fn )) == 0 ) {
+ (ps_wakeup_all_fn)();
+ }
+ } else {
+ (ps_wakeup_all_fn)();
+ }
+ }
+ }
+ }
+}
+
+void
+connection_abandon_operations( Connection *c )
+{
+ Operation *op;
+ for ( op = c->c_ops; op != NULL; op = op->o_next )
+ {
+ /* abandon the operation only if it is not yet
+ completed (i.e., no result has been sent yet to
+ the client */
+ if ( op->o_status != SLAPI_OP_STATUS_RESULT_SENT ) {
+ op->o_status = SLAPI_OP_STATUS_ABANDONED;
+ }
+ }
+}
diff --git a/ldap/servers/slapd/conntable.c b/ldap/servers/slapd/conntable.c
new file mode 100644
index 00000000..8297971c
--- /dev/null
+++ b/ldap/servers/slapd/conntable.c
@@ -0,0 +1,515 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+/* Connection Table */
+
+#include "fe.h"
+
+Connection_Table *
+connection_table_new(int table_size)
+{
+ Connection_Table *ct;
+ int i = 0;
+
+
+ ct= (Connection_Table*)slapi_ch_calloc( 1, sizeof(Connection_Table) );
+ ct->size= table_size;
+ ct->c = (Connection *)slapi_ch_calloc( 1, table_size * sizeof(Connection) );
+ ct->fd = (struct POLL_STRUCT *)slapi_ch_calloc(1, table_size * sizeof(struct POLL_STRUCT));
+ ct->table_mutex = PR_NewLock();
+
+ /* We rely on the fact that we called calloc, which zeros the block, so we don't
+ * init any structure element unless a zero value is troublesome later
+ */
+ for ( i = 0; i < table_size; i++ )
+ {
+ int invalid_socket;
+ unsigned long maxbersize= config_get_maxbersize();
+ /* DBDB---move this out of here once everything works */
+ ct->c[i].c_sb = ber_sockbuf_alloc();
+ invalid_socket = SLAPD_INVALID_SOCKET;
+ ber_sockbuf_set_option( ct->c[i].c_sb, LBER_SOCKBUF_OPT_DESC, &invalid_socket );
+ ct->c[i].c_sd = SLAPD_INVALID_SOCKET;
+ ber_sockbuf_set_option( ct->c[i].c_sb, LBER_SOCKBUF_OPT_NO_READ_AHEAD, LBER_OPT_ON );
+ ber_sockbuf_set_option( ct->c[i].c_sb, LBER_SOCKBUF_OPT_MAX_INCOMING_SIZE, &maxbersize );
+#ifndef _WIN32
+ /* all connections start out invalid */
+ ct->fd[i].fd = SLAPD_INVALID_SOCKET;
+#endif
+ /* The connection table has a double linked list running through it.
+ * This is used to find out which connections should be looked at
+ * in the poll loop. Slot 0 in the table is always the head of
+ * the linked list. Each slot has a c_next and c_prev which are
+ * pointers back into the array of connection slots. */
+ ct->c[i].c_next = NULL;
+ ct->c[i].c_prev = NULL;
+ ct->c[i].c_ci= i;
+ ct->c[i].c_fdi= SLAPD_INVALID_SOCKET_INDEX;
+ }
+ return ct;
+}
+
+void connection_table_free(Connection_Table *ct)
+{
+ int i;
+ for (i = 0; i < ct->size; i++)
+ {
+ /* Free the contents of the connection structure */
+ Connection *c= &(ct->c[i]);
+ connection_done(c);
+ }
+ slapi_ch_free((void**)&ct->c);
+ slapi_ch_free((void**)&ct->fd);
+ PR_DestroyLock(ct->table_mutex);
+ slapi_ch_free((void**)&ct);
+}
+
+
+#ifdef _WIN32
+/*
+ * This function looks up connection by nspr fd. It is
+ * slow because it iterrates through the entire connection
+ * array. Currently, it is only used on NT in secure_read_function
+ * to handle I/O timeout on SSL socket we should almost never
+ * happen.
+ */
+Connection*
+connection_table_get_connection_from_fd(Connection_Table *ct,PRFileDesc *prfd)
+{
+ int i;
+ for (i = 0; i < ct->size; i++)
+ {
+ if (ct->c[i].c_prfd == prfd)
+ {
+ return (&(ct->c[i]));
+ }
+ }
+
+ return NULL;
+}
+#endif
+
+void
+connection_table_abandon_all_operations(Connection_Table *ct)
+{
+ int i;
+ for ( i = 0; i < ct->size; i++ )
+ {
+ if ( ct->c[i].c_mutex != NULL )
+ {
+ PR_Lock( ct->c[i].c_mutex );
+ connection_abandon_operations( &ct->c[i] );
+ PR_Unlock( ct->c[i].c_mutex );
+ }
+ }
+}
+
+/* Given a file descriptor for a socket, this function will return
+ * a slot in the connection table to use.
+ *
+ * Note: this function is only called from the slapd_daemon (listener)
+ * thread, which means it will never be called by two threads at
+ * the same time.
+ *
+ * Returns a Connection on success
+ * Returns NULL on failure
+ */
+Connection *
+connection_table_get_connection(Connection_Table *ct, int sd)
+{
+ Connection *c= NULL;
+ int index, count;
+
+ index = sd % ct->size;
+ for( count = 0; count < ct->size; count++, index = (index + 1) % ct->size)
+ {
+ /* Do not use slot 0, slot 0 is head of the list of active connections */
+ if ( index == 0 )
+ {
+ continue;
+ }
+ else if( ct->c[index].c_mutex == NULL )
+ {
+ break;
+ }
+
+ if( connection_is_free (&(ct->c[index])))
+ {
+ break;
+ }
+ }
+
+ if ( count < ct->size )
+ {
+ /* Found an available Connection */
+ c= &(ct->c[index]);
+ PR_ASSERT(c->c_next==NULL);
+ PR_ASSERT(c->c_prev==NULL);
+ PR_ASSERT(c->c_extension==NULL);
+ if ( c->c_mutex == NULL )
+ {
+ PR_Lock( ct->table_mutex );
+ c->c_mutex = PR_NewLock();
+ c->c_pdumutex = PR_NewLock();
+ PR_Unlock( ct->table_mutex );
+ if ( c->c_mutex == NULL || c->c_pdumutex == NULL )
+ {
+ c->c_mutex = NULL;
+ c->c_pdumutex = NULL;
+ LDAPDebug( LDAP_DEBUG_ANY,"PR_NewLock failed\n",0, 0, 0 );
+ c= NULL;
+ }
+ }
+ /* Let's make sure there's no cruft left on there from the last time this connection was used. */
+ /* Note: no need to lock c->c_mutex because this function is only
+ * called by one thread (the slapd_daemon thread), and if we got this
+ * far then `c' is not being used by any operation threads, etc.
+ */
+ connection_cleanup(c);
+ }
+ else
+ {
+ /* couldn't find a Connection */
+ LDAPDebug( LDAP_DEBUG_CONNS, "max open connections reached\n", 0, 0, 0);
+ }
+ return c;
+}
+
+/* active connection iteration functions */
+
+Connection*
+connection_table_get_first_active_connection (Connection_Table *ct)
+{
+ return ct->c[0].c_next;
+}
+
+Connection*
+connection_table_get_next_active_connection (Connection_Table *ct, Connection *c)
+{
+ return c->c_next;
+}
+
+int connection_table_iterate_active_connections(Connection_Table *ct, void* arg, Connection_Table_Iterate_Function f)
+{
+ /* Locking in this area seems rather undeveloped, I think because typically only one
+ * thread accesses the connection table (daemon thread). But now we allow other threads
+ * to iterate over the table. So we use the "new mutex mutex" to lock the table.
+ */
+ Connection *current_conn = NULL;
+ int ret = 0;
+ PR_Lock(ct->table_mutex);
+ current_conn = connection_table_get_first_active_connection (ct);
+ while (current_conn) {
+ ret = f(current_conn, arg);
+ if (ret) {
+ break;
+ }
+ current_conn = connection_table_get_next_active_connection (ct, current_conn);
+ }
+ PR_Unlock(ct->table_mutex);
+ return ret;
+}
+
+static void
+connection_table_dump_active_connection (Connection *c)
+{
+ slapi_log_error(SLAPI_LOG_FATAL, "connection", "conn=%p fd=%d refcnt=%d c_flags=%d\n"
+ "mutex=%p next=%p prev=%p\n\n", c, c->c_sd, c->c_refcnt, c->c_flags,
+ c->c_mutex, c->c_next, c->c_prev);
+}
+
+static void
+connection_table_dump_active_connections (Connection_Table *ct)
+{
+ Connection* c;
+
+ PR_Lock(ct->table_mutex);
+ slapi_log_error(SLAPI_LOG_FATAL, "connection", "********** BEGIN DUMP ************\n");
+ c = connection_table_get_first_active_connection (ct);
+ while (c)
+ {
+ connection_table_dump_active_connection (c);
+ c = connection_table_get_next_active_connection (ct, c);
+ }
+
+ slapi_log_error(SLAPI_LOG_FATAL, "connection", "********** END DUMP ************\n");
+ PR_Unlock(ct->table_mutex);
+}
+
+/*
+ * There's a double linked list of active connections running through the array
+ * of connections. This function removes a connection (by index) from that
+ * list. This list is used to find the connections that should be used in the
+ * poll call.
+ */
+void
+connection_table_move_connection_out_of_active_list(Connection_Table *ct,Connection *c)
+{
+ /* we always have previous element because list contains a dummy header */;
+ PR_ASSERT (c->c_prev);
+
+ /* slapi_log_error(SLAPI_LOG_FATAL, "connection", "Moving connection out of active list\n");
+ connection_table_dump_active_connection (c);*/
+
+ /*
+ * Note: the connection will NOT be moved off the active list if any other threads still hold
+ * a reference to the connection (that is, its reference count must be 1 or less).
+ */
+ if(c->c_refcnt > 1) return;
+
+ /* We need to lock here because we're modifying the linked list */
+ PR_Lock(ct->table_mutex);
+
+ c->c_prev->c_next = c->c_next;
+
+ if (c->c_next)
+ {
+ c->c_next->c_prev = c->c_prev;
+ }
+
+ connection_release_nolock (c);
+
+ connection_cleanup (c);
+
+ PR_Unlock(ct->table_mutex);
+
+ /* connection_table_dump_active_connections (ct); */
+}
+
+/*
+ * There's a double linked list of active connections running through the array
+ * of connections. This function adds a connection (by index) to the head of
+ * that list. This list is used to find the connections that should be used in the
+ * poll call.
+ */
+void
+connection_table_move_connection_on_to_active_list(Connection_Table *ct,Connection *c)
+{
+ PR_ASSERT(c->c_next==NULL);
+ PR_ASSERT(c->c_prev==NULL);
+
+ PR_Lock(ct->table_mutex);
+
+ connection_acquire_nolock (c);
+
+ /* slapi_log_error(SLAPI_LOG_FATAL, "connection", "Moving connection into active list\n");
+ connection_table_dump_active_connection (c);*/
+
+ c->c_next = ct->c[0].c_next;
+ if ( c->c_next != NULL )
+ {
+ c->c_next->c_prev = c;
+ }
+ c->c_prev = &(ct->c[0]);
+ ct->c[0].c_next = c;
+
+ PR_Unlock(ct->table_mutex);
+
+ /* connection_table_dump_active_connections (ct); */
+}
+
+/*
+ * Replace the following attributes within the entry 'e' with
+ * information about the connection table:
+ * connection // multivalued; one value for each active connection
+ * currentconnections // single valued; an integer count
+ * totalconnections // single valued; an integer count
+ * dtablesize // single valued; an integer size
+ * readwaiters // single valued; an integer count
+ */
+void
+connection_table_as_entry(Connection_Table *ct, Slapi_Entry *e)
+{
+ char buf[BUFSIZ];
+ struct berval val;
+ struct berval *vals[2];
+ int i, nconns, nreadwaiters;
+ struct tm utm;
+
+ vals[0] = &val;
+ vals[1] = NULL;
+
+ attrlist_delete( &e->e_attrs, "connection");
+ nconns = 0;
+ nreadwaiters = 0;
+ for ( i = 0; i < (ct!=NULL?ct->size:0); i++ )
+ {
+ PR_Lock( ct->table_mutex );
+ if ( (ct->c[i].c_mutex == NULL) || (ct->c[i].c_mutex == (PRLock*)-1) )
+ {
+ PR_Unlock( ct->table_mutex );
+ continue;
+ }
+ /* Can't take c_mutex if holding table_mutex; temporarily unlock */
+ PR_Unlock( ct->table_mutex );
+
+ PR_Lock( ct->c[i].c_mutex );
+ if ( ct->c[i].c_sd != SLAPD_INVALID_SOCKET )
+ {
+ char buf2[20];
+ int lendn = ct->c[i].c_dn ? strlen(ct->c[i].c_dn) : 6; /* "NULLDN" */
+ char *bufptr = &buf[0];
+ char *newbuf = NULL;
+
+ nconns++;
+ if ( ct->c[i].c_gettingber )
+ {
+ nreadwaiters++;
+ }
+
+#ifdef _WIN32
+ {
+ struct tm *pt;
+ pt = gmtime( &ct->c[i].c_starttime );
+ memcpy(&utm, pt, sizeof(struct tm) );
+ }
+#else
+ gmtime_r( &ct->c[i].c_starttime, &utm );
+#endif
+ strftime( buf2, sizeof(buf2), "%Y%m%d%H%M%SZ", &utm );
+
+ if (lendn > (BUFSIZ - 46)) {
+ /*
+ * 46 = 4 for the "i" couter + 20 for buf2 +
+ * 10 for c_opsinitiated + 10 for c_opscompleted +
+ * 1 for c_gettingber + 1
+ */
+ newbuf = (char *) slapi_ch_malloc(lendn + 46);
+ bufptr = newbuf;
+ }
+
+ sprintf( bufptr, "%d:%s:%d:%d:%s%s:%s", i,
+ buf2,
+ ct->c[i].c_opsinitiated,
+ ct->c[i].c_opscompleted,
+ ct->c[i].c_gettingber ? "r" : "",
+ "",
+ ct->c[i].c_dn ? ct->c[i].c_dn : "NULLDN" );
+ val.bv_val = bufptr;
+ val.bv_len = strlen( bufptr );
+ attrlist_merge( &e->e_attrs, "connection", vals );
+ if (newbuf) {
+ slapi_ch_free((void **) &newbuf);
+ }
+ }
+ PR_Unlock( ct->c[i].c_mutex );
+ }
+
+ sprintf( buf, "%d", nconns );
+ val.bv_val = buf;
+ val.bv_len = strlen( buf );
+ attrlist_replace( &e->e_attrs, "currentconnections", vals );
+
+ PR_Lock( num_conns_mutex );
+ sprintf( buf, "%d", num_conns );
+ PR_Unlock( num_conns_mutex );
+ val.bv_val = buf;
+ val.bv_len = strlen( buf );
+ attrlist_replace( &e->e_attrs, "totalconnections", vals );
+
+ sprintf( buf, "%d", (ct!=NULL?ct->size:0) );
+ val.bv_val = buf;
+ val.bv_len = strlen( buf );
+ attrlist_replace( &e->e_attrs, "dtablesize", vals );
+
+ sprintf( buf, "%d", nreadwaiters );
+ val.bv_val = buf;
+ val.bv_len = strlen( buf );
+ attrlist_replace( &e->e_attrs, "readwaiters", vals );
+}
+
+void
+connection_table_dump_activity_to_errors_log(Connection_Table *ct)
+{
+ int i;
+ for ( i = 0; i < ct->size; i++ )
+ {
+ Connection *c= &(ct->c[i]);
+ if ( c->c_mutex != NULL )
+ {
+ /* Find the connection we are referring to */
+ int j= c->c_fdi;
+ PR_Lock( c->c_mutex );
+ if ( c->c_sd != SLAPD_INVALID_SOCKET && c->c_prfd == ct->fd[j].fd )
+ {
+ int r = ct->fd[j].out_flags & SLAPD_POLL_FLAGS;
+ if ( r )
+ {
+ LDAPDebug( LDAP_DEBUG_CONNS,"activity on %d%s\n", i, r ? "r" : "",0 );
+ }
+ }
+ PR_Unlock( c->c_mutex );
+ }
+ }
+}
+
+#if 0
+void dump_op_list(FILE *file, Operation *op);
+
+void
+connection_table_dump(Connection_Table *ct)
+{
+ FILE *file;
+
+ file = fopen("/tmp/slapd.conn", "a+");
+ if (file != NULL)
+ {
+ int i;
+ fprintf(file, "=============pid=%d==================\n", getpid());
+ for ( i = 0; i < ct->size; i++ )
+ {
+ if ( (ct->c[i].c_sd == SLAPD_INVALID_SOCKET) && (ct->c[i].c_connid == 0) )
+ {
+ continue;
+ }
+ fprintf(file, "c[%d].c_dn=0x%x\n", i, ct->c[i].c_dn);
+ dump_op_list(file, ct->c[i].c_ops);
+ fprintf(file, "c[%d].c_sb.sb_sd=%d\n", i, ct->c[i].c_sd);
+ fprintf(file, "c[%d].c_connid=%d\n", i, ct->c[i].c_connid);
+ fprintf(file, "c[%d].c_opsinitiated=%d\n", i, ct->c[i].c_opsinitiated);
+ fprintf(file, "c[%d].c_opscompleted=%d\n", i, ct->c[i].c_opscompleted);
+ }
+ fclose(file);
+ }
+}
+
+static const char *
+op_status2str( int o_status )
+{
+ const char *s = "unknown";
+
+ switch( o_status ) {
+ case SLAPI_OP_STATUS_PROCESSING:
+ s = "processing";
+ break;
+ case SLAPI_OP_STATUS_ABANDONED:
+ s = "abandoned";
+ break;
+ case SLAPI_OP_STATUS_WILL_COMPLETE:
+ s = "will_complete";
+ break;
+ case SLAPI_OP_STATUS_RESULT_SENT:
+ s = "result_sent";
+ break;
+ }
+
+ return s;
+}
+
+void
+dump_op_list(FILE *file, Operation *op)
+{
+ Operation *tmp;
+
+ for ( tmp = op; tmp != NULL; tmp = tmp->o_next )
+ {
+ fprintf(file,
+ "(o_msgid=%d, o_tag=%d, o_sdn=0x%x, o_opid=%d, o_connid=%d, o_status=%s)\n",
+ tmp->o_msgid, tmp->o_tag, slapi_sdn_get_dn(&tmp->o_sdn), tmp->o_connid,
+ *tmp->o_tid, op_status2str( tmp->o_status ));
+ }
+}
+#endif /* 0 */
+
diff --git a/ldap/servers/slapd/control.c b/ldap/servers/slapd/control.c
new file mode 100644
index 00000000..012d3d4c
--- /dev/null
+++ b/ldap/servers/slapd/control.c
@@ -0,0 +1,592 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+/* control.c - routines for dealing with LDAPMessage controls */
+
+#include <stdio.h>
+#include "slap.h"
+
+
+/*
+ * static variables used to track information about supported controls.
+ * supported_controls is a NULL-terminated array of OIDs.
+ * supported_controls_ops is an array of bitmaps that hold SLAPI_OPERATION_*
+ * flags that specify the operation(s) for which a control is supported.
+ * The elements in the supported_controls_ops array align with the ones
+ * in the supported_controls array.
+ */
+static char **supported_controls = NULL;
+static unsigned long *supported_controls_ops = NULL;
+static int supported_controls_count = 0;
+static PRRWLock *supported_controls_lock = NULL;
+
+/*
+ * Register all of the LDAPv3 controls we know about "out of the box."
+ */
+void
+init_controls( void )
+{
+ supported_controls_lock = PR_NewRWLock(PR_RWLOCK_RANK_NONE,
+ "supported controls rwlock");
+ if (NULL == supported_controls_lock) {
+ /* Out of resources */
+ slapi_log_error(SLAPI_LOG_FATAL, "startup",
+ "init_controls: failed to create lock.\n");
+ exit (1);
+ }
+
+ slapi_register_supported_control( LDAP_CONTROL_MANAGEDSAIT,
+ SLAPI_OPERATION_SEARCH | SLAPI_OPERATION_COMPARE
+ | SLAPI_OPERATION_ADD | SLAPI_OPERATION_DELETE
+ | SLAPI_OPERATION_MODIFY | SLAPI_OPERATION_MODDN );
+ slapi_register_supported_control( LDAP_CONTROL_PERSISTENTSEARCH,
+ SLAPI_OPERATION_SEARCH );
+ slapi_register_supported_control( LDAP_CONTROL_PWEXPIRED,
+ SLAPI_OPERATION_NONE );
+ slapi_register_supported_control( LDAP_CONTROL_PWEXPIRING,
+ SLAPI_OPERATION_NONE );
+ slapi_register_supported_control( LDAP_CONTROL_SORTREQUEST,
+ SLAPI_OPERATION_SEARCH );
+ slapi_register_supported_control( LDAP_CONTROL_VLVREQUEST,
+ SLAPI_OPERATION_SEARCH );
+ slapi_register_supported_control( LDAP_CONTROL_AUTH_REQUEST,
+ SLAPI_OPERATION_BIND );
+ slapi_register_supported_control( LDAP_CONTROL_AUTH_RESPONSE,
+ SLAPI_OPERATION_NONE );
+ slapi_register_supported_control( LDAP_CONTROL_REAL_ATTRS_ONLY,
+ SLAPI_OPERATION_SEARCH );
+ slapi_register_supported_control( LDAP_CONTROL_VIRT_ATTRS_ONLY,
+ SLAPI_OPERATION_SEARCH );
+ slapi_register_supported_control( LDAP_X_CONTROL_PWPOLICY_REQUEST,
+ SLAPI_OPERATION_SEARCH | SLAPI_OPERATION_COMPARE
+ | SLAPI_OPERATION_ADD | SLAPI_OPERATION_DELETE
+ | SLAPI_OPERATION_MODIFY | SLAPI_OPERATION_MODDN );
+ slapi_register_supported_control( LDAP_X_CONTROL_PWPOLICY_RESPONSE,
+ SLAPI_OPERATION_SEARCH | SLAPI_OPERATION_COMPARE
+ | SLAPI_OPERATION_ADD | SLAPI_OPERATION_DELETE
+ | SLAPI_OPERATION_MODIFY | SLAPI_OPERATION_MODDN );
+ slapi_register_supported_control( LDAP_CONTROL_GET_EFFECTIVE_RIGHTS,
+ SLAPI_OPERATION_SEARCH );
+}
+
+
+/*
+ * register a supported control so it can be returned as part of the root DSE.
+ */
+void
+slapi_register_supported_control( char *controloid, unsigned long controlops )
+{
+ if ( controloid != NULL ) {
+ PR_RWLock_Wlock(supported_controls_lock);
+ ++supported_controls_count;
+ charray_add( &supported_controls,
+ slapi_ch_strdup( controloid ));
+ supported_controls_ops = (unsigned long *)slapi_ch_realloc(
+ (char *)supported_controls_ops,
+ supported_controls_count * sizeof( unsigned long ));
+ supported_controls_ops[ supported_controls_count - 1 ] =
+ controlops;
+ PR_RWLock_Unlock(supported_controls_lock);
+ }
+}
+
+
+/*
+ * retrieve supported controls OID and/or operations arrays.
+ * return 0 if successful and -1 if not.
+ * This function is not MTSafe and should be deprecated.
+ * slapi_get_supported_controls_copy should be used instead.
+ */
+int
+slapi_get_supported_controls( char ***ctrloidsp, unsigned long **ctrlopsp )
+{
+ if ( ctrloidsp != NULL ) {
+ *ctrloidsp = supported_controls;
+ }
+ if ( ctrlopsp != NULL ) {
+ *ctrlopsp = supported_controls_ops;
+ }
+
+ return( 0 );
+}
+
+
+static
+unsigned long *supported_controls_ops_dup(unsigned long *ctrlops)
+{
+ int i;
+ unsigned long *dup_ops = (unsigned long *)slapi_ch_calloc(
+ supported_controls_count + 1, sizeof( unsigned long ));
+ if (NULL != dup_ops) {
+ for (i=0; i < supported_controls_count; i++)
+ dup_ops[i] = supported_controls_ops[i];
+ }
+ return dup_ops;
+}
+
+
+int slapi_get_supported_controls_copy( char ***ctrloidsp, unsigned long **ctrlopsp )
+{
+ PR_RWLock_Rlock(supported_controls_lock);
+ if ( ctrloidsp != NULL ) {
+ *ctrloidsp = charray_dup(supported_controls);
+ }
+ if ( ctrlopsp != NULL ) {
+ *ctrlopsp = supported_controls_ops_dup(supported_controls_ops);
+ }
+ PR_RWLock_Unlock(supported_controls_lock);
+ return (0);
+}
+
+
+int
+get_ldapmessage_controls(
+ Slapi_PBlock *pb,
+ BerElement *ber,
+ LDAPControl ***controlsp /* can be NULL if no need to return */
+)
+{
+ LDAPControl **ctrls, *new;
+ unsigned long tag, len;
+ int rc, maxcontrols, curcontrols;
+ char *last;
+ int managedsait, pwpolicy_ctrl;
+
+ /*
+ * Each LDAPMessage can have a set of controls appended
+ * to it. Controls are used to extend the functionality
+ * of an LDAP operation (e.g., add an attribute size limit
+ * to the search operation). These controls look like this:
+ *
+ * Controls ::= SEQUENCE OF Control
+ *
+ * Control ::= SEQUENCE {
+ * controlType LDAPOID,
+ * criticality BOOLEAN DEFAULT FALSE,
+ * controlValue OCTET STRING
+ * }
+ */
+
+ LDAPDebug( LDAP_DEBUG_TRACE, "=> get_ldapmessage_controls\n", 0, 0, 0 );
+
+ ctrls = NULL;
+ slapi_pblock_set( pb, SLAPI_REQCONTROLS, ctrls );
+ if ( controlsp != NULL ) {
+ *controlsp = NULL;
+ }
+ rc = LDAP_PROTOCOL_ERROR; /* most popular error we may return */
+
+ /*
+ * check to see if controls were included
+ */
+ if ( ber_get_option( ber, LBER_OPT_REMAINING_BYTES, &len ) != 0 ) {
+ LDAPDebug( LDAP_DEBUG_TRACE,
+ "<= get_ldapmessage_controls LDAP_OPERATIONS_ERROR\n",
+ 0, 0, 0 );
+ return( LDAP_OPERATIONS_ERROR ); /* unexpected error */
+ }
+ if ( len == 0 ) {
+ LDAPDebug( LDAP_DEBUG_TRACE,
+ "<= get_ldapmessage_controls no controls\n", 0, 0, 0 );
+ return( LDAP_SUCCESS ); /* no controls */
+ }
+ if (( tag = ber_peek_tag( ber, &len )) != LDAP_TAG_CONTROLS ) {
+ if ( tag == LBER_ERROR ) {
+ LDAPDebug( LDAP_DEBUG_TRACE,
+ "<= get_ldapmessage_controls LDAP_PROTOCOL_ERROR\n",
+ 0, 0, 0 );
+ return( LDAP_PROTOCOL_ERROR ); /* decoding error */
+ }
+ /*
+ * We found something other than controls. This should never
+ * happen in LDAPv3, but we don't treat this is a hard error --
+ * we just ignore the extra stuff.
+ */
+ LDAPDebug( LDAP_DEBUG_TRACE,
+ "<= get_ldapmessage_controls ignoring unrecognized data in request (tag 0x%x)\n",
+ tag, 0, 0 );
+ return( LDAP_SUCCESS );
+ }
+
+ /*
+ * A sequence of controls is present. If connection is not LDAPv3
+ * or better, return a protocol error. Otherwise, parse the controls.
+ */
+ if ( pb != NULL && pb->pb_conn != NULL
+ && pb->pb_conn->c_ldapversion < LDAP_VERSION3 ) {
+ slapi_log_error( SLAPI_LOG_FATAL, "connection",
+ "received control(s) on an LDAPv%d connection\n",
+ pb->pb_conn->c_ldapversion );
+ return( LDAP_PROTOCOL_ERROR );
+ }
+
+ maxcontrols = curcontrols = 0;
+ for ( tag = ber_first_element( ber, &len, &last );
+ tag != LBER_ERROR && tag != LBER_END_OF_SEQORSET;
+ tag = ber_next_element( ber, &len, last ) ) {
+ if ( curcontrols >= maxcontrols - 1 ) {
+#define CONTROL_GRABSIZE 6
+ maxcontrols += CONTROL_GRABSIZE;
+ ctrls = (LDAPControl **) slapi_ch_realloc( (char *)ctrls,
+ maxcontrols * sizeof(LDAPControl *) );
+ }
+ new = (LDAPControl *) slapi_ch_calloc( 1, sizeof(LDAPControl) );
+ ctrls[curcontrols++] = new;
+ ctrls[curcontrols] = NULL;
+
+ if ( ber_scanf( ber, "{a", &new->ldctl_oid ) == LBER_ERROR ) {
+ goto free_and_return;
+ }
+
+ /* the criticality is optional */
+ if ( ber_peek_tag( ber, &len ) == LBER_BOOLEAN ) {
+ if ( ber_scanf( ber, "b", &new->ldctl_iscritical )
+ == LBER_ERROR ) {
+ goto free_and_return;
+ }
+ } else {
+ /* absent is synonomous with FALSE */
+ new->ldctl_iscritical = 0;
+ }
+
+ /*
+ * return an appropriate error if this control is marked
+ * critical and either:
+ * a) we do not support it at all OR
+ * b) it is not supported for this operation
+ */
+ if ( new->ldctl_iscritical ) {
+ int i;
+
+ PR_RWLock_Rlock(supported_controls_lock);
+ for ( i = 0; supported_controls != NULL
+ && supported_controls[i] != NULL; ++i ) {
+ if ( strcmp( supported_controls[i],
+ new->ldctl_oid ) == 0 ) {
+ break;
+ }
+ }
+
+ if ( supported_controls == NULL ||
+ supported_controls[i] == NULL ||
+ ( 0 == ( supported_controls_ops[i] &
+ operation_get_type(pb->pb_op) ))) {
+ rc = LDAP_UNAVAILABLE_CRITICAL_EXTENSION;
+ PR_RWLock_Unlock(supported_controls_lock);
+ goto free_and_return;
+ }
+ PR_RWLock_Unlock(supported_controls_lock);
+ }
+
+ /* the control value is optional */
+ if ( ber_peek_tag( ber, &len ) == LBER_OCTETSTRING ) {
+ if ( ber_scanf( ber, "o", &new->ldctl_value )
+ == LBER_ERROR ) {
+ goto free_and_return;
+ }
+ } else {
+ (new->ldctl_value).bv_val = NULL;
+ (new->ldctl_value).bv_len = 0;
+ }
+ }
+
+ if ( tag == LBER_ERROR ) {
+ goto free_and_return;
+ }
+
+ slapi_pblock_set( pb, SLAPI_REQCONTROLS, ctrls );
+ managedsait = slapi_control_present( ctrls,
+ LDAP_CONTROL_MANAGEDSAIT, NULL, NULL );
+ slapi_pblock_set( pb, SLAPI_MANAGEDSAIT, &managedsait );
+ pwpolicy_ctrl = slapi_control_present( ctrls,
+ LDAP_X_CONTROL_PWPOLICY_REQUEST, NULL, NULL );
+ slapi_pblock_set( pb, SLAPI_PWPOLICY, &pwpolicy_ctrl );
+
+ if ( controlsp != NULL ) {
+ *controlsp = ctrls;
+ }
+
+#ifdef SLAPD_ECHO_CONTROL
+ /*
+ * XXXmcs: Start of hack: if a control with OID "1.1" was sent by
+ * the client, echo all controls back to the client unchanged. Note
+ * that this is just a hack to test control handling in libldap and
+ * should be removed once we support all interesting controls.
+ */
+ if ( slapi_control_present( ctrls, "1.1", NULL, NULL )) {
+ int i;
+
+ for ( i = 0; ctrls[i] != NULL; ++i ) {
+ slapi_pblock_set( pb, SLAPI_ADD_RESCONTROL,
+ (void *)ctrls[i] );
+ }
+ }
+#endif /* SLAPD_ECHO_CONTROL */
+
+ LDAPDebug( LDAP_DEBUG_TRACE,
+ "<= get_ldapmessage_controls %d controls\n", curcontrols, 0, 0 );
+ return( LDAP_SUCCESS );
+
+free_and_return:;
+ ldap_controls_free( ctrls );
+ LDAPDebug( LDAP_DEBUG_TRACE,
+ "<= get_ldapmessage_controls %i\n", rc, 0, 0 );
+ return( rc );
+}
+
+
+int
+slapi_control_present( LDAPControl **controls, char *oid, struct berval **val, int *iscritical )
+{
+ int i;
+
+ LDAPDebug( LDAP_DEBUG_TRACE,
+ "=> slapi_control_present (looking for %s)\n", oid, 0, 0 );
+
+ if ( val != NULL ) {
+ *val = NULL;
+ }
+
+ if ( controls == NULL ) {
+ LDAPDebug( LDAP_DEBUG_TRACE,
+ "<= slapi_control_present 0 (NO CONTROLS)\n", 0, 0, 0 );
+ return( 0 );
+ }
+
+ for ( i = 0; controls[i] != NULL; i++ ) {
+ if ( strcmp( controls[i]->ldctl_oid, oid ) == 0 ) {
+ if ( val != NULL ) {
+ if (NULL != val) {
+ *val = &controls[i]->ldctl_value;
+ }
+ if (NULL != iscritical) {
+ *iscritical = (int) controls[i]->ldctl_iscritical;
+ }
+ }
+ LDAPDebug( LDAP_DEBUG_TRACE,
+ "<= slapi_control_present 1 (FOUND)\n", 0, 0, 0 );
+ return( 1 );
+ }
+ }
+
+ LDAPDebug( LDAP_DEBUG_TRACE,
+ "<= slapi_control_present 0 (NOT FOUND)\n", 0, 0, 0 );
+ return( 0 );
+}
+
+
+/*
+ * Write sequence of controls in "ctrls" to "ber".
+ * Return zero on success and -1 if an error occurs.
+ */
+int
+write_controls( BerElement *ber, LDAPControl **ctrls )
+{
+ int i;
+ unsigned long rc;
+
+ rc= ber_start_seq( ber, LDAP_TAG_CONTROLS );
+ if ( rc == LBER_ERROR ) {
+ return( -1 );
+ }
+
+ /* if the criticality is false, it should be absent from the encoding */
+ for ( i = 0; ctrls[ i ] != NULL; ++i ) {
+ if ( ctrls[ i ]->ldctl_value.bv_val == 0 ) {
+ if ( ctrls[ i ]->ldctl_iscritical ) {
+ rc = ber_printf( ber, "{sb}", ctrls[ i ]->ldctl_oid,
+ ctrls[ i ]->ldctl_iscritical );
+ } else {
+ rc = ber_printf( ber, "{s}", ctrls[ i ]->ldctl_oid );
+ }
+ } else {
+ if ( ctrls[ i ]->ldctl_iscritical ) {
+ rc = ber_printf( ber, "{sbo}", ctrls[ i ]->ldctl_oid,
+ ctrls[ i ]->ldctl_iscritical,
+ ctrls[ i ]->ldctl_value.bv_val,
+ ctrls[ i ]->ldctl_value.bv_len );
+ } else {
+ rc = ber_printf( ber, "{so}", ctrls[ i ]->ldctl_oid,
+ ctrls[ i ]->ldctl_value.bv_val,
+ ctrls[ i ]->ldctl_value.bv_len );
+ }
+ }
+ if ( rc == LBER_ERROR ) {
+ return( -1 );
+ }
+ }
+
+ rc= ber_put_seq( ber );
+ if ( rc == LBER_ERROR ) {
+ return( -1 );
+ }
+
+ return( 0 );
+}
+
+
+/*
+ * duplicate "newctrl" and add it to the array of controls "*ctrlsp"
+ * note that *ctrlsp may be reset and that it is okay to pass NULL for it.
+ */
+void
+add_control( LDAPControl ***ctrlsp, LDAPControl *newctrl )
+{
+ int count;
+
+ if ( *ctrlsp == NULL ) {
+ count = 0;
+ } else {
+ for ( count = 0; (*ctrlsp)[count] != NULL; ++count ) {
+ ;
+ }
+ }
+
+ *ctrlsp = (LDAPControl **)slapi_ch_realloc( (char *)*ctrlsp,
+ ( count + 2 ) * sizeof(LDAPControl *));
+
+ (*ctrlsp)[ count ] = slapi_dup_control( newctrl );
+ (*ctrlsp)[ ++count ] = NULL;
+}
+
+
+/*
+ * return a malloc'd copy of "ctrl"
+ */
+LDAPControl *
+slapi_dup_control( LDAPControl *ctrl )
+{
+ LDAPControl *rctrl;
+
+ rctrl = (LDAPControl *)slapi_ch_malloc( sizeof( LDAPControl ));
+
+ rctrl->ldctl_oid = slapi_ch_strdup( ctrl->ldctl_oid );
+ rctrl->ldctl_iscritical = ctrl->ldctl_iscritical;
+
+ if ( ctrl->ldctl_value.bv_val == NULL ) { /* no value */
+ rctrl->ldctl_value.bv_len = 0;
+ rctrl->ldctl_value.bv_val = NULL;
+ } else if ( ctrl->ldctl_value.bv_len <= 0 ) { /* zero length value */
+ rctrl->ldctl_value.bv_len = 0;
+ rctrl->ldctl_value.bv_val = slapi_ch_malloc( 1 );
+ rctrl->ldctl_value.bv_val[0] = '\0';
+ } else { /* value with content */
+ rctrl->ldctl_value.bv_len = ctrl->ldctl_value.bv_len;
+ rctrl->ldctl_value.bv_val =
+ slapi_ch_malloc( ctrl->ldctl_value.bv_len );
+ memcpy( rctrl->ldctl_value.bv_val, ctrl->ldctl_value.bv_val,
+ ctrl->ldctl_value.bv_len );
+ }
+
+ return( rctrl );
+}
+
+
+int
+slapi_build_control( char *oid, BerElement *ber,
+ char iscritical, LDAPControl **ctrlp )
+{
+ int rc = 0;
+ int return_value = LDAP_SUCCESS;
+ struct berval *bvp = NULL;
+
+ PR_ASSERT( NULL != oid && NULL != ctrlp );
+
+ if ( NULL == ber ) {
+ bvp = NULL;
+ } else {
+ /* allocate struct berval with contents of the BER encoding */
+ rc = ber_flatten( ber, &bvp );
+ if ( -1 == rc ) {
+ return_value = LDAP_NO_MEMORY;
+ goto loser;
+ }
+ }
+
+ /* allocate the new control structure */
+ *ctrlp = (LDAPControl *)slapi_ch_calloc( 1, sizeof(LDAPControl));
+
+ /* fill in the fields of this new control */
+ (*ctrlp)->ldctl_iscritical = iscritical;
+ (*ctrlp)->ldctl_oid = slapi_ch_strdup( oid );
+ if ( NULL == bvp ) {
+ (*ctrlp)->ldctl_value.bv_len = 0;
+ (*ctrlp)->ldctl_value.bv_val = NULL;
+ } else {
+ (*ctrlp)->ldctl_value = *bvp; /* struct copy */
+ ldap_memfree(bvp); /* free container, but not contents */
+ bvp = NULL;
+ }
+
+loser:
+ return return_value;
+}
+
+
+#if 0
+/*
+ * rbyrne: This is version of the above using the slapi_build_control_from_berval()
+ * I'll enable this afterwards.
+ * Build an allocated LDAPv3 control from a BerElement.
+ * Returns an LDAP error code.
+ */
+int
+slapi_build_control( char *oid, BerElement *ber,
+ char iscritical, LDAPControl **ctrlp )
+{
+ int rc = 0;
+ int return_value = LDAP_SUCCESS;
+ struct berval *bvp = NULL;
+
+ PR_ASSERT( NULL != oid && NULL != ctrlp );
+
+ if ( NULL == ber ) {
+ bvp = NULL;
+ } else {
+ /* allocate struct berval with contents of the BER encoding */
+ rc = ber_flatten( ber, &bvp );
+ if ( -1 == rc ) {
+ return_value = LDAP_NO_MEMORY;
+ goto loser;
+ }
+ }
+
+ return_value = slapi_build_control_from_berval( oid, bvp, iscritical,
+ ctrlp);
+ if ( bvp != NULL ) {
+ ldap_memfree(bvp); /* free container, but not contents */
+ bvp = NULL;
+ }
+
+loser:
+ return return_value;
+}
+#endif
+
+/*
+ * Build an allocated LDAPv3 control from a berval. Returns an LDAP error code.
+ */
+int
+slapi_build_control_from_berval( char *oid, struct berval *bvp,
+ char iscritical, LDAPControl **ctrlp )
+{
+ int return_value = LDAP_SUCCESS;
+
+ /* allocate the new control structure */
+ *ctrlp = (LDAPControl *)slapi_ch_calloc( 1, sizeof(LDAPControl));
+
+ /* fill in the fields of this new control */
+ (*ctrlp)->ldctl_iscritical = iscritical;
+ (*ctrlp)->ldctl_oid = slapi_ch_strdup( oid );
+ if ( NULL == bvp ) {
+ (*ctrlp)->ldctl_value.bv_len = 0;
+ (*ctrlp)->ldctl_value.bv_val = NULL;
+ } else {
+ (*ctrlp)->ldctl_value = *bvp; /* struct copy */
+ }
+
+ return return_value;
+}
+
diff --git a/ldap/servers/slapd/counters.c b/ldap/servers/slapd/counters.c
new file mode 100644
index 00000000..f9875790
--- /dev/null
+++ b/ldap/servers/slapd/counters.c
@@ -0,0 +1,151 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+
+#include <prcountr.h>
+#include "slap.h"
+
+#if defined(DEBUG)
+struct counter
+{
+ const char *qname;
+ const char *rname;
+ const char *description;
+ PRUint32 before_counter;
+ PRUint32 after_counter;
+};
+
+static int num_counters= 0;
+static struct counter *counters= NULL;
+
+static int
+count_counters()
+{
+ int i= 0;
+ PR_DEFINE_COUNTER(qh);
+ PR_INIT_COUNTER_HANDLE(qh,NULL);
+ PR_FIND_NEXT_COUNTER_QNAME(qh,qh);
+ while(qh!=NULL)
+ {
+ PR_DEFINE_COUNTER(rh);
+ PR_INIT_COUNTER_HANDLE(rh,NULL);
+ PR_FIND_NEXT_COUNTER_RNAME(rh,rh,qh);
+ while(rh!=NULL)
+ {
+ i++;
+ PR_FIND_NEXT_COUNTER_RNAME(rh,rh,qh);
+ }
+ PR_FIND_NEXT_COUNTER_QNAME(qh,qh);
+ }
+ return i;
+}
+
+static int
+do_fetch_counters()
+{
+ int i= 0;
+ PR_DEFINE_COUNTER(qh);
+ PR_INIT_COUNTER_HANDLE(qh,NULL);
+ PR_FIND_NEXT_COUNTER_QNAME(qh,qh);
+ while(qh!=NULL)
+ {
+ PR_DEFINE_COUNTER(rh);
+ PR_INIT_COUNTER_HANDLE(rh,NULL);
+ PR_FIND_NEXT_COUNTER_RNAME(rh,rh,qh);
+ while(rh!=NULL)
+ {
+ if(i<num_counters)
+ {
+ counters[i].before_counter= counters[i].after_counter;
+ PR_GET_COUNTER_NAME_FROM_HANDLE(rh,&counters[i].qname,&counters[i].rname,&counters[i].description);
+ PR_GET_COUNTER(counters[i].after_counter,rh);
+ }
+ i++;
+ PR_FIND_NEXT_COUNTER_RNAME(rh,rh,qh);
+ }
+ PR_FIND_NEXT_COUNTER_QNAME(qh,qh);
+ }
+ return i;
+}
+
+static void
+fetch_counters()
+{
+ int i;
+ if(counters==NULL)
+ {
+ num_counters= count_counters();
+ counters= (struct counter*)calloc(num_counters,sizeof(struct counter));
+ }
+ i= do_fetch_counters();
+ if(i>num_counters)
+ {
+ free(counters);
+ counters= NULL;
+ num_counters= i;
+ counters= (struct counter*)calloc(num_counters,sizeof(struct counter));
+ do_fetch_counters();
+ }
+}
+
+static size_t
+counter_size(struct counter *counter)
+{
+ size_t r= 0;
+ r+= (counter->qname?strlen(counter->qname):0);
+ r+= (counter->rname?strlen(counter->rname):0);
+ r+= (counter->description?strlen(counter->description):0);
+ return r;
+}
+
+static void
+counter_dump(char *name, char *value, int i)
+{
+ PRUint32 diff_counter;
+ sprintf(name,"%s_%s_%s",counters[i].qname,counters[i].rname,counters[i].description);
+ diff_counter= counters[i].after_counter-counters[i].before_counter;
+ sprintf(value,"%d -> %d (%s%d)",
+ counters[i].before_counter,
+ counters[i].after_counter,
+ (diff_counter>0?"+":""),
+ diff_counter);
+}
+#endif
+
+void
+counters_as_entry(Slapi_Entry* e)
+{
+#if defined(DEBUG)
+ int i;
+ fetch_counters();
+ for(i=0;i<num_counters;i++)
+ {
+ char value[40];
+ char *type= (char*)malloc(counter_size(&counters[i])+4);
+ counter_dump(type,value,i);
+ slapi_entry_attr_set_charptr( e, type, value);
+ free(type);
+ }
+#endif
+}
+
+
+void
+counters_to_errors_log(const char *text)
+{
+#if defined(DEBUG)
+ int i;
+ fetch_counters();
+ LDAPDebug( LDAP_DEBUG_ANY, "Counter Dump - %s\n",text, 0, 0);
+ for(i=0;i<num_counters;i++)
+ {
+ char value[40];
+ char *type= (char*)malloc(counter_size(&counters[i])+4);
+ counter_dump(type,value,i);
+ LDAPDebug( LDAP_DEBUG_ANY, "%s %s\n",type, value, 0);
+ free(type);
+ }
+#endif
+}
diff --git a/ldap/servers/slapd/csn.c b/ldap/servers/slapd/csn.c
new file mode 100644
index 00000000..30243d88
--- /dev/null
+++ b/ldap/servers/slapd/csn.c
@@ -0,0 +1,383 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+
+/*
+ * csn.c - CSN
+ */
+
+#include <string.h>
+#include "slap.h"
+#include <prcountr.h>
+
+#define _CSN_TSTAMP_STRSIZE_STR "8"
+#define _CSN_SEQNUM_STRSIZE_STR "4"
+#define _CSN_REPLID_STRSIZE_STR "4"
+#define _CSN_SUBSEQNUM_STRSIZE_STR "4"
+
+#define _CSN_TSTAMP_SCANSTR "%"_CSN_TSTAMP_STRSIZE_STR"lx"
+#define _CSN_SEQNUM_SCANSTR "%"_CSN_SEQNUM_STRSIZE_STR"hx"
+#define _CSN_REPLID_SCANSTR "%"_CSN_REPLID_STRSIZE_STR"hx"
+#define _CSN_SUBSEQNUM_SCANSTR "%"_CSN_SUBSEQNUM_STRSIZE_STR"hx"
+
+#define _CSN_TSORDER_SPRINTSTR "%08x%04x%04x%04x"
+
+#define _CSN_TSORDER_TSTAMP_OFFSET 0
+#define _CSN_TSORDER_SEQNUM_OFFSET 8
+#define _CSN_TSORDER_REPLID_OFFSET 12
+#define _CSN_TSORDER_SUBSEQNUM_OFFSET 16
+
+static PRBool _csnIsValidString(const char *csnStr);
+
+/*
+ * Debugging counters.
+ */
+static int counters_created= 0;
+PR_DEFINE_COUNTER(slapi_csn_counter_created);
+PR_DEFINE_COUNTER(slapi_csn_counter_deleted);
+PR_DEFINE_COUNTER(slapi_csn_counter_exist);
+
+/*
+ * **************************************************************************
+ * CSN Functions
+ * **************************************************************************
+ */
+
+static void
+csn_create_counters()
+{
+ PR_CREATE_COUNTER(slapi_csn_counter_created,"Slapi_CSN","created","");
+ PR_CREATE_COUNTER(slapi_csn_counter_deleted,"Slapi_CSN","deleted","");
+ PR_CREATE_COUNTER(slapi_csn_counter_exist,"Slapi_CSN","exist","");
+ counters_created= 1;
+}
+
+CSN *csn_new()
+{
+ if(!counters_created)
+ {
+ csn_create_counters();
+ }
+ PR_INCREMENT_COUNTER(slapi_csn_counter_created);
+ PR_INCREMENT_COUNTER(slapi_csn_counter_exist);
+ return (CSN*)slapi_ch_calloc(sizeof(CSN),1);
+}
+
+CSN *csn_new_by_string(const char *s)
+{
+ CSN *newcsn= NULL;
+ if(s!=NULL)
+ {
+ if(_csnIsValidString(s))
+ {
+ newcsn= csn_new();
+ csn_init_by_string(newcsn,s);
+ }
+ }
+ return newcsn;
+}
+
+void csn_init(CSN *csn)
+{
+ if(csn!=NULL)
+ {
+ memset(csn,0,sizeof(CSN));
+ }
+}
+
+void csn_init_by_csn(CSN *csn1,const CSN *csn2)
+{
+ if(csn2!=NULL)
+ {
+ memcpy(csn1,csn2,sizeof(CSN));
+ }
+ else
+ {
+ csn_init(csn1);
+ }
+}
+
+void csn_init_by_string(CSN *csn, const char *s)
+{
+ time_t csnTime= 0;
+ PRUint16 csnSeqNum= 0;
+ ReplicaId rid= 0;
+ PRUint16 csnSubSeqNum= 0;
+
+ if(_csnIsValidString(s))
+ {
+ /* JCM - char2hex faster */
+ sscanf((s+_CSN_TSORDER_TSTAMP_OFFSET), _CSN_TSTAMP_SCANSTR, &csnTime); /* JCM - scanf is very slow */
+ sscanf((s+_CSN_TSORDER_SEQNUM_OFFSET), _CSN_SEQNUM_SCANSTR, &csnSeqNum);/* JCM - scanf is very slow */
+ sscanf((s+_CSN_TSORDER_REPLID_OFFSET), _CSN_REPLID_SCANSTR, &rid);/* JCM - scanf is very slow */
+ sscanf((s+_CSN_TSORDER_SUBSEQNUM_OFFSET), _CSN_SUBSEQNUM_SCANSTR, &csnSubSeqNum);/* JCM - scanf is very slow */
+ csn->tstamp= csnTime;
+ csn->seqnum= csnSeqNum;
+ csn->rid= rid;
+ csn->subseqnum= csnSubSeqNum;
+ }
+}
+
+CSN *csn_dup(const CSN *csn)
+{
+ CSN *newcsn= NULL;
+ if(csn!=NULL)
+ {
+ newcsn= csn_new();
+ csn_init_by_csn(newcsn,csn);
+ }
+ return newcsn;
+}
+
+void csn_done(CSN *csn)
+{
+}
+
+void csn_free(CSN **csn)
+{
+ if(csn!=NULL && *csn!=NULL)
+ {
+ if(!counters_created)
+ {
+ csn_create_counters();
+ }
+ PR_INCREMENT_COUNTER(slapi_csn_counter_deleted);
+ PR_DECREMENT_COUNTER(slapi_csn_counter_exist);
+ slapi_ch_free((void **)csn);
+ }
+ return;
+}
+
+void csn_set_replicaid(CSN *csn, ReplicaId rid)
+{
+ csn->rid= rid;
+}
+
+void csn_set_time(CSN *csn, time_t csntime)
+{
+ csn->tstamp= csntime;
+}
+
+void csn_set_seqnum(CSN *csn, PRUint16 seqnum)
+{
+ csn->seqnum= seqnum;
+}
+
+ReplicaId csn_get_replicaid(const CSN *csn)
+{
+ return csn->rid;
+}
+
+PRUint16 csn_get_seqnum(const CSN *csn)
+{
+ return csn->seqnum;
+}
+
+time_t csn_get_time(const CSN *csn)
+{
+ if(csn==NULL)
+ {
+ return 0;
+ }
+ else
+ {
+ return csn->tstamp;
+ }
+}
+
+
+/*
+ * WARNING: ss must point to memory at least CSN_STRSIZE bytes long,
+ * WARNING: or be NULL, which means this function will allocate the
+ * WARNING: memory, which must be free'd by the caller.
+ */
+char *
+csn_as_string(const CSN *csn, PRBool replicaIdOrder, char *ss)
+{
+ char *s= ss;
+ if(s==NULL)
+ {
+ s= slapi_ch_malloc(CSN_STRSIZE);
+ }
+ if(csn==NULL)
+ {
+ s[0]= '\0';
+ }
+ else
+ {
+ /* JCM - hex2char would be quicker */
+ sprintf(s,"%08lx%04x%04x%04x",
+ csn->tstamp,csn->seqnum,csn->rid, csn->subseqnum);
+ }
+ return s;
+}
+
+
+/*
+ * WARNING: ss must point to memory at least (7+CSN_STRSIZE) bytes long,
+ * WARNING: or be NULL, which means this function will allocate the
+ * WARNING: memory, which must be free'd by the caller.
+ */
+char *
+csn_as_attr_option_string(CSNType t,const CSN *csn,char *ss)
+{
+ char *s= ss;
+ if(csn!=NULL)
+ {
+ if(s==NULL)
+ {
+ s= slapi_ch_malloc(8+CSN_STRSIZE);
+ }
+ s[0]= ';';
+ switch(t)
+ {
+ case CSN_TYPE_UNKNOWN:
+ s[1]= 'x';
+ s[2]= '1';
+ break;
+ case CSN_TYPE_NONE:
+ s[1]= 'x';
+ s[2]= '2';
+ break;
+ case CSN_TYPE_ATTRIBUTE_DELETED:
+ s[1]= 'a';
+ s[2]= 'd';
+ break;
+ case CSN_TYPE_VALUE_UPDATED:
+ s[1]= 'v';
+ s[2]= 'u';
+ break;
+ case CSN_TYPE_VALUE_DELETED:
+ s[1]= 'v';
+ s[2]= 'd';
+ break;
+ case CSN_TYPE_VALUE_DISTINGUISHED:
+ s[1]= 'm';
+ s[2]= 'd';
+ break;
+ }
+ s[3]= 'c';
+ s[4]= 's';
+ s[5]= 'n';
+ s[6]= '-';
+ csn_as_string(csn,PR_FALSE,s+7);
+ }
+ return s;
+}
+
+int
+csn_compare(const CSN *csn1, const CSN *csn2)
+{
+ PRInt32 retVal;
+ if(csn1!=NULL && csn2!=NULL)
+ {
+ /* csns can't be compared via memcmp (previuos version of the code)
+ because, on NT, bytes are reversed */
+ if (csn1->tstamp < csn2->tstamp)
+ retVal = -1;
+ else if (csn1->tstamp > csn2->tstamp)
+ retVal = 1;
+ else
+ {
+ if (csn1->seqnum < csn2->seqnum)
+ retVal = -1;
+ else if (csn1->seqnum > csn2->seqnum)
+ retVal = 1;
+ else
+ {
+ if (csn1->rid < csn2->rid)
+ retVal = -1;
+ else if (csn1->rid > csn2->rid)
+ retVal = 1;
+ else
+ {
+ if (csn1->subseqnum < csn2->subseqnum)
+ retVal = -1;
+ else if (csn1->subseqnum > csn2->subseqnum)
+ retVal = 1;
+ else
+ retVal = 0;
+ }
+ }
+ }
+
+ }
+ else if(csn1!=NULL && csn2==NULL)
+ {
+ retVal= 1; /* csn1>csn2 */
+ }
+ else if (csn1==NULL && csn2!=NULL)
+ {
+ retVal= -1; /* csn1<csn2 */
+ }
+ else /* (csn1==NULL && csn2==NULL) */
+ {
+ retVal= 0; /* The same */
+ }
+
+ return(retVal);
+}
+
+time_t csn_time_difference(const CSN *csn1, const CSN *csn2)
+{
+ return csn_get_time(csn1) - csn_get_time(csn2);
+}
+
+const CSN *
+csn_max(const CSN *csn1,const CSN *csn2)
+{
+ if(csn_compare(csn1, csn2)>0)
+ {
+ return csn1;
+ }
+ else
+ {
+ return csn2;
+ }
+}
+
+int csn_increment_subsequence (CSN *csn)
+{
+ if (csn == NULL)
+ {
+ return -1;
+ }
+ else if (csn->subseqnum == 0xFFFFFFFF)
+ {
+ slapi_log_error(SLAPI_LOG_FATAL, NULL,
+ "csn_increment_subsequence: subsequence overflow\n");
+
+ return -1;
+ }
+ else
+ {
+ csn->subseqnum ++;
+ return 0;
+ }
+}
+
+/*
+ * sizeof(vucsn-011111111222233334444)
+ * Does not include the trailing '\0'
+ */
+size_t
+csn_string_size()
+{
+ return LDIF_CSNPREFIX_MAXLENGTH + _CSN_VALIDCSN_STRLEN;
+}
+
+static PRBool
+_csnIsValidString(const char *s)
+{
+ if(NULL == s) {
+ return(PR_FALSE);
+ }
+ if(strlen(s) < _CSN_VALIDCSN_STRLEN) {
+ return(PR_FALSE);
+ }
+
+ /* some more checks on validity of tstamp portion? */
+ return(PR_TRUE);
+}
diff --git a/ldap/servers/slapd/csngen.c b/ldap/servers/slapd/csngen.c
new file mode 100644
index 00000000..16fe2afa
--- /dev/null
+++ b/ldap/servers/slapd/csngen.c
@@ -0,0 +1,762 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+
+/*
+ * csngen.c - CSN Generator
+ */
+
+#ifdef _WIN32
+#define _WIN32_WINNT 0x0400
+#include <windows.h>
+#endif
+
+#include <string.h>
+#include "prcountr.h"
+#include "slap.h"
+
+#define CSN_MAX_SEQNUM 0xffff /* largest sequence number */
+#define CSN_MAX_TIME_ADJUST 24*60*60 /* maximum allowed time adjustment (in seconds) = 1 day */
+#define ATTR_CSN_GENERATOR_STATE "nsState" /* attribute that stores csn state information */
+#define STATE_FORMAT "%8x%8x%8x%4hx%4hx"
+#define STATE_LENGTH 32
+#define MAX_VAL(x,y) ((x)>(y)?(x):(y))
+
+/*
+ * **************************************************************************
+ * data structures
+ * **************************************************************************
+ */
+
+/* callback node */
+typedef struct callback_node
+{
+ GenCSNFn gen_fn; /* function to be called when new csn is generated */
+ void *gen_arg; /* argument to pass to gen_fn function */
+ AbortCSNFn abort_fn; /* function to be called when csn is aborted */
+ void *abort_arg; /* argument to pass to abort_fn function */
+} callback_node;
+
+typedef struct callback_list
+{
+ PRRWLock *lock;
+ DataList *list; /* list of callback_node structures */
+} callback_list;
+
+/* persistently stored generator's state */
+typedef struct csngen_state
+{
+ ReplicaId rid; /* replica id of the replicated area to which it is attached */
+ time_t sampled_time; /* time last obtained from time() */
+ time_t local_offset; /* offset due to the local clock being set back */
+ time_t remote_offset; /* offset due to clock difference with remote systems */
+ PRUint16 seq_num; /* used to allow to generate multiple csns within a second */
+}csngen_state;
+
+/* data maintained for each generator */
+struct csngen
+{
+ csngen_state state; /* persistent state of the generator */
+ callback_list callbacks; /* list of callbacks registered with the generator */
+ PRRWLock *lock; /* concurrency control */
+};
+
+/*
+ * **************************************************************************
+ * global data
+ * **************************************************************************
+ */
+
+static time_t g_sampled_time; /* time obtained from time() call */
+
+/*
+ * **************************************************************************
+ * forward declarations of helper functions
+ * **************************************************************************
+ */
+
+static int _csngen_parse_state (CSNGen *gen, Slapi_Attr *state);
+static int _csngen_init_callbacks (CSNGen *gen);
+static void _csngen_call_callbacks (const CSNGen *gen, const CSN *csn, PRBool abort);
+static int _csngen_cmp_callbacks (const void *el1, const void *el2);
+static void _csngen_free_callbacks (CSNGen *gen);
+static int _csngen_adjust_local_time (CSNGen *gen, time_t cur_time);
+
+/*
+ * **************************************************************************
+ * forward declarations of tester functions
+ * **************************************************************************
+ */
+
+static int _csngen_start_test_threads (CSNGen *gen);
+static void _csngen_stop_test_threads ();
+static void _csngen_gen_tester_main (void *data);
+static void _csngen_local_tester_main (void *data);
+static void _csngen_remote_tester_main (void *data);
+
+/*
+ * **************************************************************************
+ * API
+ * **************************************************************************
+ */
+CSNGen*
+csngen_new (ReplicaId rid, Slapi_Attr *state)
+{
+ int rc = CSN_SUCCESS;
+ CSNGen *gen = NULL;
+
+ gen = (CSNGen*)slapi_ch_calloc (1, sizeof (CSNGen));
+ if (gen == NULL)
+ {
+ slapi_log_error (SLAPI_LOG_FATAL, NULL, "csngen_new: memory allocation failed\n");
+ return NULL;
+ }
+
+ /* create lock to control the access to the state information */
+ gen->lock = PR_NewRWLock(PR_RWLOCK_RANK_NONE, "state_lock");
+ if (gen->lock == NULL)
+ {
+ slapi_log_error (SLAPI_LOG_FATAL, NULL, "csngen_new: failed to create lock\n");
+ rc = CSN_NSPR_ERROR;
+ goto done;
+ }
+
+ /* initialize callback list */
+ _csngen_init_callbacks (gen);
+
+ gen->state.rid = rid;
+
+ if (state)
+ {
+ rc = _csngen_parse_state (gen, state);
+ if (rc != CSN_SUCCESS)
+ {
+ goto done;
+ }
+ }
+ else
+ {
+ /* new generator */
+ gen->state.sampled_time = current_time ();
+ gen->state.local_offset = 0;
+ gen->state.remote_offset = 0;
+ gen->state.seq_num = 0;
+ }
+
+done:
+ if (rc != CSN_SUCCESS)
+ {
+ if (gen)
+ {
+ csngen_free (&gen);
+ }
+
+ return NULL;
+ }
+
+ return gen;
+}
+
+void
+csngen_free (CSNGen **gen)
+{
+ if (gen == NULL || *gen == NULL)
+ return;
+
+ _csngen_free_callbacks (*gen);
+
+ if ((*gen)->lock)
+ PR_DestroyRWLock ((*gen)->lock);
+}
+
+int
+csngen_new_csn (CSNGen *gen, CSN **csn, PRBool notify)
+{
+ int rc = CSN_SUCCESS;
+ time_t cur_time;
+ int delta;
+
+ if (gen == NULL || csn == NULL)
+ {
+ slapi_log_error (SLAPI_LOG_FATAL, NULL, "csngen_new_csn: invalid argument\n");
+ return CSN_INVALID_PARAMETER;
+ }
+
+ *csn = csn_new ();
+ if (*csn == NULL)
+ {
+ slapi_log_error (SLAPI_LOG_FATAL, NULL, "csngen_new_csn: memory allocation failed\n");
+ return CSN_MEMORY_ERROR;
+ }
+
+ PR_RWLock_Wlock (gen->lock);
+
+ if (g_sampled_time == 0)
+ csngen_update_time ();
+
+ cur_time = g_sampled_time;
+
+ /* check if the time should be adjusted */
+ delta = cur_time - gen->state.sampled_time;
+ if (delta > 0)
+ {
+ rc = _csngen_adjust_local_time (gen, cur_time);
+ if (rc != CSN_SUCCESS)
+ {
+ PR_RWLock_Unlock (gen->lock);
+ return rc;
+ }
+ }
+ else if (delta < -300) {
+ /*
+ * The maxseqnum could support up to 65535 CSNs per second.
+ * That means that we could avoid duplicated CSN's for
+ * delta up to 300 secs if update rate is 200/sec (usually
+ * the max rate is below 20/sec).
+ * Beyond 300 secs, we advance gen->state.sampled_time by
+ * one sec to recycle seqnum.
+ */
+ slapi_log_error (SLAPI_LOG_FATAL, "csngen_new_csn", "Warning: too much time skew (%d secs). Current seqnum=%0x\n", delta, gen->state.seq_num );
+ rc = _csngen_adjust_local_time (gen, gen->state.sampled_time+1);
+ if (rc != CSN_SUCCESS)
+ {
+ PR_RWLock_Unlock (gen->lock);
+ return rc;
+ }
+
+ }
+
+ if (gen->state.seq_num == CSN_MAX_SEQNUM)
+ {
+ slapi_log_error (SLAPI_LOG_FATAL, NULL, "csngen_new_csn: sequence rollover; "
+ "local offset updated.\n");
+ gen->state.local_offset ++;
+ gen->state.seq_num = 0;
+ }
+
+ (*csn)->tstamp = gen->state.sampled_time + gen->state.local_offset +
+ gen->state.remote_offset;
+ (*csn)->seqnum = gen->state.seq_num ++;
+ (*csn)->rid = gen->state.rid;
+ (*csn)->subseqnum = 0;
+
+ /* The lock is intentionally unlocked before callbacks are called.
+ This is to prevent deadlocks. The callback management code has
+ its own lock */
+ PR_RWLock_Unlock (gen->lock);
+
+ /* notify modules that registered interest in csn generation */
+ if (notify)
+ {
+ _csngen_call_callbacks (gen, *csn, 0);
+ }
+
+ return rc;
+}
+
+/* this function should be called for csns generated with non-zero notify
+ that were unused because the corresponding operation was aborted.
+ The function calls "abort" functions registered through
+ csngen_register_callbacks call */
+void csngen_abort_csn (CSNGen *gen, const CSN *csn)
+{
+ _csngen_call_callbacks (gen, csn, 1);
+}
+
+/* this function should be called when a remote CSN for the same part of
+ the dit becomes known to the server (for instance, as part of RUV during
+ replication session. In response, the generator would adjust its notion
+ of time so that it does not generate smaller csns */
+int csngen_adjust_time (CSNGen *gen, const CSN* csn)
+{
+ time_t remote_time, remote_offset;
+ PRUint16 remote_seqnum;
+
+ if (gen == NULL || csn == NULL)
+ return CSN_INVALID_PARAMETER;
+
+ remote_time = csn_get_time (csn);
+ remote_seqnum = csn_get_seqnum (csn);
+
+ PR_RWLock_Wlock (gen->lock);
+
+ if (remote_seqnum > gen->state.seq_num )
+ {
+ if (remote_seqnum < CSN_MAX_SEQNUM)
+ {
+ gen->state.seq_num = remote_seqnum + 1;
+ }
+ else
+ {
+ remote_time++;
+ }
+ }
+
+ if (remote_time >= gen->state.sampled_time)
+ {
+ remote_offset = remote_time - gen->state.sampled_time;
+ if (remote_offset > gen->state.remote_offset)
+ {
+ if (remote_offset <= CSN_MAX_TIME_ADJUST)
+ {
+ gen->state.remote_offset = remote_offset;
+ }
+ else /* remote_offset > CSN_MAX_TIME_ADJUST */
+ {
+ slapi_log_error (SLAPI_LOG_FATAL, NULL, "csngen_adjust_time: "
+ "adjustment limit exceeded; value - %d, limit - %d\n",
+ remote_offset, CSN_MAX_TIME_ADJUST);
+ PR_RWLock_Unlock (gen->lock);
+ return CSN_LIMIT_EXCEEDED;
+ }
+ }
+ }
+
+ PR_RWLock_Unlock (gen->lock);
+
+ return CSN_SUCCESS;
+}
+
+/* returns PR_TRUE if the csn was generated by this generator and
+ PR_FALSE otherwise. */
+PRBool csngen_is_local_csn(const CSNGen *gen, const CSN *csn)
+{
+ return (gen && csn && gen->state.rid == csn_get_replicaid(csn));
+}
+
+/* returns current state of the generator so that it can be saved in the DIT */
+int csngen_get_state (const CSNGen *gen, Slapi_Mod *state)
+{
+ struct berval bval;
+
+ if (gen == NULL || state == NULL)
+ return CSN_INVALID_PARAMETER;
+
+ PR_RWLock_Rlock (gen->lock);
+
+ slapi_mod_init (state, 1);
+ slapi_mod_set_type (state, ATTR_CSN_GENERATOR_STATE);
+ slapi_mod_set_operation (state, LDAP_MOD_REPLACE | LDAP_MOD_BVALUES);
+ bval.bv_val = (char*)&gen->state;
+ bval.bv_len = sizeof (gen->state);
+ slapi_mod_add_value(state, &bval);
+
+ PR_RWLock_Unlock (gen->lock);
+
+ return CSN_SUCCESS;
+}
+
+/* registers callbacks to be called when csn is created or aborted */
+void* csngen_register_callbacks(CSNGen *gen, GenCSNFn genFn, void *genArg,
+ AbortCSNFn abortFn, void *abortArg)
+{
+ callback_node *node;
+ if (gen == NULL || (genFn == NULL && abortFn == NULL))
+ return NULL;
+
+ node = (callback_node *)slapi_ch_malloc (sizeof (callback_node));
+ node->gen_fn = genFn;
+ node->gen_arg = genArg;
+ node->abort_fn = abortFn;
+ node->abort_arg = abortArg;
+
+ PR_RWLock_Wlock (gen->callbacks.lock);
+ dl_add (gen->callbacks.list, node);
+ PR_RWLock_Unlock (gen->callbacks.lock);
+
+ return node;
+}
+
+/* unregisters callbacks registered via call to csngenRegisterCallbacks */
+void csngen_unregister_callbacks(CSNGen *gen, void *cookie)
+{
+ if (gen && cookie)
+ {
+ PR_RWLock_Wlock (gen->callbacks.lock);
+ dl_delete (gen->callbacks.list, cookie, _csngen_cmp_callbacks, slapi_ch_free);
+ PR_RWLock_Unlock (gen->callbacks.lock);
+ }
+}
+
+/* this functions is periodically called from daemon.c to
+ update time used by all generators */
+void csngen_update_time ()
+{
+ g_sampled_time = current_time ();
+}
+
+/* debugging function */
+void csngen_dump_state (const CSNGen *gen)
+{
+ if (gen)
+ {
+ PR_RWLock_Rlock (gen->lock);
+ slapi_log_error(SLAPI_LOG_FATAL, NULL, "CSN generator's state:\n");
+ slapi_log_error(SLAPI_LOG_FATAL, NULL, "\treplica id: %d\n", gen->state.rid);
+ slapi_log_error(SLAPI_LOG_FATAL, NULL, "\tsampled time: %d\n", gen->state.sampled_time);
+ slapi_log_error(SLAPI_LOG_FATAL, NULL, "\tlocal offset: %d\n", gen->state.local_offset);
+ slapi_log_error(SLAPI_LOG_FATAL, NULL, "\tremote offset: %d\n", gen->state.remote_offset);
+ slapi_log_error(SLAPI_LOG_FATAL, NULL, "\tsequence number: %d\n", gen->state.seq_num);
+ PR_RWLock_Unlock (gen->lock);
+ }
+}
+
+#define TEST_TIME 600 /* 10 minutes */
+/* This function tests csn generator. It verifies that csn's are generated in
+ monotnically increasing order in the face of local and remote time skews */
+void csngen_test ()
+{
+ int rc;
+ CSNGen *gen = csngen_new (255, NULL);
+
+ slapi_log_error(SLAPI_LOG_FATAL, NULL, "staring csn generator test ...");
+ csngen_dump_state (gen);
+
+ rc = _csngen_start_test_threads(gen);
+ if (rc == 0)
+ {
+ DS_Sleep(PR_SecondsToInterval(TEST_TIME));
+ }
+
+ _csngen_stop_test_threads(gen);
+ csngen_dump_state (gen);
+ slapi_log_error(SLAPI_LOG_FATAL, NULL, "csn generator test is complete...");
+}
+
+/*
+ * **************************************************************************
+ * Helper functions
+ * **************************************************************************
+ */
+static int
+_csngen_parse_state (CSNGen *gen, Slapi_Attr *state)
+{
+ int rc;
+ Slapi_Value *val;
+ const struct berval *bval;
+ ReplicaId rid = gen->state.rid;
+
+ PR_ASSERT (gen && state);
+
+ rc = slapi_attr_first_value(state, &val);
+ if (rc != 0)
+ {
+ slapi_log_error (SLAPI_LOG_FATAL, NULL, "_csngen_parse_state: invalid state format\n");
+ return CSN_INVALID_FORMAT;
+ }
+
+ bval = slapi_value_get_berval(val);
+ memcpy (&gen->state, bval->bv_val, bval->bv_len);
+
+ /* replicaid does not match */
+ if (rid != gen->state.rid)
+ {
+ slapi_log_error (SLAPI_LOG_FATAL, NULL, "_csngen_parse_state: replica id"
+ " mismatch; current id - %d, replica id in the state - %d\n",
+ rid, gen->state.rid);
+ return CSN_INVALID_FORMAT;
+ }
+
+ return CSN_SUCCESS;
+}
+
+static int
+_csngen_init_callbacks (CSNGen *gen)
+{
+ /* create a lock to control access to the callback list */
+ gen->callbacks.lock = PR_NewRWLock(PR_RWLOCK_RANK_NONE, "callback_lock");
+ if (gen->callbacks.lock == NULL)
+ {
+ return CSN_NSPR_ERROR;
+ }
+
+ gen->callbacks.list = dl_new ();
+ dl_init (gen->callbacks.list, 0);
+
+ return CSN_SUCCESS;
+}
+
+static void
+_csngen_free_callbacks (CSNGen *gen)
+{
+ PR_ASSERT (gen);
+
+ if (gen->callbacks.list)
+ {
+ dl_cleanup (gen->callbacks.list, slapi_ch_free);
+ dl_free (&(gen->callbacks.list));
+ }
+
+ if (gen->callbacks.lock)
+ PR_DestroyRWLock (gen->callbacks.lock);
+}
+
+static void
+_csngen_call_callbacks (const CSNGen *gen, const CSN *csn, PRBool abort)
+{
+ int cookie;
+ callback_node* node;
+
+ PR_ASSERT (gen && csn);
+
+ PR_RWLock_Rlock (gen->callbacks.lock);
+ node = (callback_node*)dl_get_first (gen->callbacks.list, &cookie);
+ while (node)
+ {
+ if (abort)
+ {
+ if (node->abort_fn)
+ node->abort_fn (csn, node->abort_arg);
+ }
+ else
+ {
+ if (node->gen_fn)
+ node->gen_fn (csn, node->gen_arg);
+ }
+ node = (callback_node*)dl_get_next (gen->callbacks.list, &cookie);
+ }
+
+ PR_RWLock_Unlock (gen->callbacks.lock);
+}
+
+/* el1 is just a pointer to the callback_node */
+static int
+_csngen_cmp_callbacks (const void *el1, const void *el2)
+{
+ if (el1 == el2)
+ return 0;
+
+ if (el1 < el2)
+ return -1;
+ else
+ return 1;
+}
+
+static int
+_csngen_adjust_local_time (CSNGen *gen, time_t cur_time)
+{
+ time_t time_diff = cur_time - gen->state.sampled_time;
+
+ if (time_diff > 0)
+ {
+ gen->state.sampled_time = cur_time;
+ if (time_diff > gen->state.local_offset)
+ gen->state.local_offset = 0;
+ else
+ gen->state.local_offset = gen->state.local_offset - time_diff;
+
+ gen->state.seq_num = 0;
+
+ return CSN_SUCCESS;
+ }
+ else /* time was turend back */
+ {
+ if (abs (time_diff) > CSN_MAX_TIME_ADJUST)
+ {
+ slapi_log_error (SLAPI_LOG_FATAL, NULL, "_csngen_adjust_local_time: "
+ "adjustment limit exceeded; value - %d, limit - %d\n",
+ abs (time_diff), CSN_MAX_TIME_ADJUST);
+ return CSN_LIMIT_EXCEEDED;
+ }
+
+ gen->state.sampled_time = cur_time;
+ gen->state.local_offset = MAX_VAL (gen->state.local_offset, abs (time_diff));
+ gen->state.seq_num = 0;
+
+ return CSN_SUCCESS;
+ }
+}
+
+/*
+ * **************************************************************************
+ * test code
+ * **************************************************************************
+ */
+
+/*
+ * The defult thread stacksize for nspr21 is 64k. For OSF, we require
+ * a larger stacksize as actual storage allocation is higher i.e
+ * pointers are allocated 8 bytes but lower 4 bytes are used.
+ * The value 0 means use the default stacksize.
+ */
+#if defined (OSF1)
+#define DEFAULT_THREAD_STACKSIZE 131072L
+#else
+#define DEFAULT_THREAD_STACKSIZE 0
+#endif
+
+#define GEN_TREAD_COUNT 20
+int s_thread_count;
+int s_must_exit;
+
+static int
+_csngen_start_test_threads(CSNGen *gen)
+{
+ int i;
+
+ PR_ASSERT (gen);
+
+ s_thread_count = 0;
+ s_must_exit = 0;
+
+ /* create threads that generate csns */
+ for(i=0; i< GEN_TREAD_COUNT; i++)
+ {
+ if (PR_CreateThread(PR_USER_THREAD, _csngen_gen_tester_main, gen,
+ PR_PRIORITY_NORMAL, PR_GLOBAL_THREAD, PR_UNJOINABLE_THREAD,
+ DEFAULT_THREAD_STACKSIZE) == NULL)
+ {
+ PRErrorCode prerr = PR_GetError();
+ slapi_log_error(SLAPI_LOG_FATAL, NULL,
+ "failed to create a CSN generator thread number %d; " SLAPI_COMPONENT_NAME_NSPR " error %d (%s)\n",
+ i, prerr, slapd_pr_strerror(prerr));
+ return -1;
+ }
+
+ s_thread_count ++;
+ }
+
+ /* create a thread that modifies remote time */
+ if (PR_CreateThread(PR_USER_THREAD, _csngen_remote_tester_main, (void *)gen,
+ PR_PRIORITY_NORMAL, PR_GLOBAL_THREAD, PR_UNJOINABLE_THREAD,
+ DEFAULT_THREAD_STACKSIZE) == NULL)
+ {
+ PRErrorCode prerr = PR_GetError();
+ slapi_log_error(SLAPI_LOG_FATAL, NULL,
+ "failed to create the remote CSN tester thread; " SLAPI_COMPONENT_NAME_NSPR " error %d (%s)\n",
+ prerr, slapd_pr_strerror(prerr));
+ return -1;
+ }
+
+ s_thread_count ++;
+
+ /* create a thread that modifies local time */
+ if (PR_CreateThread(PR_USER_THREAD, _csngen_local_tester_main, (void *)gen,
+ PR_PRIORITY_NORMAL, PR_GLOBAL_THREAD, PR_UNJOINABLE_THREAD,
+ DEFAULT_THREAD_STACKSIZE) == NULL)
+
+ {
+ PRErrorCode prerr = PR_GetError();
+ slapi_log_error(SLAPI_LOG_FATAL, NULL,
+ "failed to create the local CSN tester thread; " SLAPI_COMPONENT_NAME_NSPR " error %d (%s)\n",
+ prerr, slapd_pr_strerror(prerr));
+ return -1;
+ }
+
+ s_thread_count ++;
+
+
+ return 0;
+}
+
+static void _csngen_stop_test_threads ()
+{
+ s_must_exit = 1;
+
+ while (s_thread_count > 0)
+ {
+ /* sleep for 30 seconds */
+ DS_Sleep (PR_SecondsToInterval(20));
+ }
+}
+
+/* periodically generate a csn and dump it to the error log */
+static void
+_csngen_gen_tester_main (void *data)
+{
+ CSNGen *gen = (CSNGen*)data;
+ CSN *csn;
+ char buff [CSN_STRSIZE];
+ int rc;
+
+ PR_ASSERT (gen);
+
+ while (!s_must_exit)
+ {
+ rc = csngen_new_csn (gen, &csn, PR_FALSE);
+ if (rc != CSN_SUCCESS)
+ {
+ slapi_log_error (SLAPI_LOG_FATAL, NULL,
+ "failed to generate csn; csn error - %d\n", rc);
+ }
+ else
+ {
+ slapi_log_error (SLAPI_LOG_FATAL, NULL, "generate csn %s\n",
+ csn_as_string(csn, PR_FALSE, buff));
+ }
+
+ /* sleep for 30 seconds */
+ DS_Sleep (PR_SecondsToInterval(10));
+ }
+
+ PR_AtomicDecrement (&s_thread_count);
+}
+
+/* simulate clock skew with remote servers that causes
+ generator to advance its remote offset */
+static void
+_csngen_remote_tester_main (void *data)
+{
+ CSNGen *gen = (CSNGen*)data;
+ CSN *csn;
+ time_t csn_time;
+ int rc;
+
+ PR_ASSERT (gen);
+
+ while (!s_must_exit)
+ {
+ rc = csngen_new_csn (gen, &csn, PR_FALSE);
+ if (rc != CSN_SUCCESS)
+ {
+ slapi_log_error (SLAPI_LOG_FATAL, NULL,
+ "failed to generate csn; csn error - %d\n", rc);
+ }
+ else
+ {
+ csn_time = csn_get_time(csn);
+ csn_set_time (csn, csn_time + slapi_rand () % 100);
+
+ rc = csngen_adjust_time (gen, csn);
+ if (rc != CSN_SUCCESS)
+ {
+ slapi_log_error (SLAPI_LOG_FATAL, NULL,
+ "failed to adjust generator's time; csn error - %d\n", rc);
+ }
+
+ csngen_dump_state (gen);
+
+ }
+
+ /* sleep for 30 seconds */
+ DS_Sleep (PR_SecondsToInterval(60));
+ }
+
+ PR_AtomicDecrement (&s_thread_count);
+}
+
+/* simulate local clock being set back */
+static void
+_csngen_local_tester_main (void *data)
+{
+ CSNGen *gen = (CSNGen*)data;
+
+ PR_ASSERT (gen);
+
+
+ while (!s_must_exit)
+ {
+ /* sleep for 30 seconds */
+ DS_Sleep (PR_SecondsToInterval(60));
+
+ g_sampled_time -= slapi_rand () % 100;
+
+ csngen_dump_state (gen);
+ }
+
+ PR_AtomicDecrement (&s_thread_count);
+}
+
+
diff --git a/ldap/servers/slapd/csngen.h b/ldap/servers/slapd/csngen.h
new file mode 100644
index 00000000..14021c41
--- /dev/null
+++ b/ldap/servers/slapd/csngen.h
@@ -0,0 +1,27 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+
+#ifndef _REPL_CSNGEN_H
+#define _REPL_CSNGEN_H
+
+#include <stdio.h>
+#include <time.h>
+#include "slapi-private.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
+/* the "exported" function declarations can be found in slapi-private.h */
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
+
diff --git a/ldap/servers/slapd/csnset.c b/ldap/servers/slapd/csnset.c
new file mode 100644
index 00000000..700cbc82
--- /dev/null
+++ b/ldap/servers/slapd/csnset.c
@@ -0,0 +1,360 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+
+#include "slap.h"
+#include "slapi-private.h"
+
+static const CSNSet *csnset_get_csnset_node_from_csn(const CSNSet *csnset, const CSN *csn);
+static const CSNSet *csnset_get_csnset_node_from_type(const CSNSet *csnset, CSNType t);
+static CSNSet *csnset_get_previous_csnset_node(CSNSet *csnset, const CSN *csn);
+
+/*
+ * The CSN is always added to the end of the list.
+ */
+void
+csnset_add_csn(CSNSet **csnset, CSNType t, const CSN *csn)
+{
+ if(csn!=NULL)
+ {
+ CSNSet *newcsn= (CSNSet*)slapi_ch_malloc(sizeof(CSNSet));
+ newcsn->type= t;
+ csn_init_by_csn(&newcsn->csn,csn);
+ newcsn->next= NULL;
+ {
+ CSNSet **p= csnset;
+ CSNSet *n= *csnset;
+ while(n!=NULL)
+ {
+ p= &(n->next);
+ n= n->next;
+ }
+ *p= newcsn;
+ }
+ }
+}
+
+/*
+ * The CSN is inserted into the list at the appropriate point..
+ */
+void
+csnset_insert_csn(CSNSet **csnset, CSNType t, const CSN *csn)
+{
+ if((csn!=NULL) && (*csnset==NULL))
+ {
+ csnset_add_csn(csnset, t, csn);
+ }
+ else if(csn!=NULL)
+ {
+ CSNSet *newcsn= (CSNSet*)slapi_ch_malloc(sizeof(CSNSet));
+ CSNSet *f= csnset_get_previous_csnset_node(*csnset, csn);
+ newcsn->type= t;
+ csn_init_by_csn(&newcsn->csn,csn);
+ if(f==NULL)
+ {
+ /* adding to the list head */
+ newcsn->next= *csnset;
+ *csnset= newcsn;
+ }
+ else
+ {
+ newcsn->next= f->next;
+ f->next= newcsn;
+ }
+ }
+}
+
+/*
+ * Find the CSN of the given type and update it.
+ */
+void
+csnset_update_csn(CSNSet **csnset, CSNType t, const CSN *csn)
+{
+ const CSNSet *f= csnset_get_csnset_node_from_type(*csnset, t);
+ if(f==NULL)
+ {
+ csnset_add_csn(csnset,t,csn);
+ }
+ else
+ {
+ if (csn_compare(csn, (CSN*)(&f->csn)) > 0)
+ {
+ csn_init_by_csn((CSN*)(&f->csn),csn);
+ }
+ }
+}
+
+/*
+ * Check if the set CSN of CSNs contains a given CSN.
+ */
+int
+csnset_contains(const CSNSet *csnset, const CSN *csn)
+{
+ const CSNSet *f= csnset_get_csnset_node_from_csn(csnset, csn);
+ return(f!=NULL);
+}
+
+/*
+ * Remove the first CSN of the given type.
+ */
+void
+csnset_remove_csn(CSNSet **csnset, CSNType t)
+{
+ CSNSet **p= csnset;
+ CSNSet *n= *csnset;
+ while(n!=NULL)
+ {
+ if(n->type==t)
+ {
+ *p= n->next;
+ slapi_ch_free((void**)&n);
+ }
+ else
+ {
+ p= &n->next;
+ n= n->next;
+ }
+ }
+}
+
+void
+csnset_free(CSNSet **csnset)
+{
+ csnset_purge(csnset, NULL);
+}
+
+/*
+ * Get the first CSN of the given type.
+ */
+const CSN *
+csnset_get_csn_of_type(const CSNSet *csnset, CSNType t)
+{
+ const CSN *csn= NULL;
+ const CSNSet *f= csnset_get_csnset_node_from_type(csnset, t);
+ if(f!=NULL)
+ {
+ csn= &f->csn;
+ }
+ return csn;
+}
+
+const CSN *
+csnset_get_previous_csn(const CSNSet *csnset, const CSN *csn)
+{
+ const CSN *prevcsn= NULL;
+ CSNSet *f= csnset_get_previous_csnset_node((CSNSet*)csnset, csn);
+ if(f!=NULL)
+ {
+ prevcsn= &f->csn;
+ }
+ return prevcsn;
+}
+
+const CSN *
+csnset_get_last_csn(const CSNSet *csnset)
+{
+ const CSN *csn= NULL;
+ const CSNSet *n= csnset;
+ while(n!=NULL)
+ {
+ if(n->next==NULL)
+ {
+ csn= &n->csn;
+ }
+ n= n->next;
+ }
+ return csn;
+}
+
+void*
+csnset_get_first_csn (const CSNSet *csnset, CSN **csn, CSNType *t)
+{
+ if (csnset)
+ {
+ *csn = (CSN*)&csnset->csn;
+ *t = csnset->type;
+ return (void*)csnset;
+ }
+ else
+ return NULL;
+}
+
+void*
+csnset_get_next_csn (const CSNSet *csnset, void *cookie, CSN **csn, CSNType *t)
+{
+ CSNSet *node;
+
+ if (csnset && cookie)
+ {
+ node = ((CSNSet*)cookie)->next;
+ if (node)
+ {
+ *csn = (CSN*)&node->csn;
+ *t = node->type;
+ return node;
+ }
+ else
+ return NULL;
+ }
+ else
+ return NULL;
+}
+
+static CSNSet *
+csnset_get_previous_csnset_node(CSNSet *csnset, const CSN *csn)
+{
+ CSNSet *f= NULL;
+ CSNSet *p= NULL;
+ CSNSet *n= csnset;
+ while(n!=NULL)
+ {
+ if(csn_compare(&n->csn, csn)>0)
+ {
+ f= p;
+ n= NULL;
+ }
+ else
+ {
+ p= n;
+ n= n->next;
+ if(n==NULL)
+ {
+ /* Got to the end of the list... */
+ f= p;
+ }
+ }
+ }
+ return f;
+}
+
+static const CSNSet *
+csnset_get_csnset_node_from_csn(const CSNSet *csnset, const CSN *csn)
+{
+ const CSNSet *f= NULL;
+ const CSNSet *n= csnset;
+ while(n!=NULL)
+ {
+ if(csn_compare(&n->csn, csn)==0)
+ {
+ f= n;
+ n= NULL;
+ }
+ else
+ {
+ n= n->next;
+ }
+ }
+ return f;
+}
+
+static const CSNSet *
+csnset_get_csnset_node_from_type(const CSNSet *csnset, CSNType t)
+{
+ const CSNSet *f= NULL;
+ const CSNSet *n= csnset;
+ while(n!=NULL)
+ {
+ if(n->type==t)
+ {
+ f= n;
+ n= NULL;
+ }
+ else
+ {
+ n= n->next;
+ }
+ }
+ return f;
+}
+
+/*
+ * Remove any CSNs older than csnUpTo. If csnUpTo is NULL,
+ * remove all CSNs.
+ */
+void
+csnset_purge(CSNSet **csnset, const CSN *csnUpTo)
+{
+ if (csnset != NULL)
+ {
+ CSNSet *n = *csnset, *nprev = NULL, *nnext;
+ while (n != NULL)
+ {
+ if (NULL == csnUpTo || (csn_compare(&n->csn, csnUpTo) < 0))
+ {
+ nnext = n->next;
+ if (*csnset == n)
+ {
+ /* Deletion of head */
+ *csnset = nnext;
+ }
+ else if (nprev)
+ {
+ /* nprev was not purged, but n will be */
+ nprev->next = nnext;
+ }
+ slapi_ch_free((void**)&n);
+ n = nnext;
+ }
+ else
+ {
+ nprev = n;
+ n = n->next;
+ }
+ }
+ }
+}
+
+size_t
+csnset_string_size(CSNSet *csnset)
+{
+ size_t s= 0;
+ CSNSet *n= csnset;
+ while(n!=NULL)
+ {
+ /* sizeof(;vucsn-011111111222233334444) */
+ s+= 1 + LDIF_CSNPREFIX_MAXLENGTH + _CSN_VALIDCSN_STRLEN;
+ n= n->next;
+ }
+ return s;
+}
+
+size_t
+csnset_size(CSNSet *csnset)
+{
+ size_t s= 0;
+ CSNSet *n= csnset;
+ while(n!=NULL)
+ {
+ s+= sizeof(CSNSet);
+ n= n->next;
+ }
+ return s;
+}
+
+CSNSet *
+csnset_dup(const CSNSet *csnset)
+{
+ CSNSet *newcsnset= NULL;
+ const CSNSet *n= csnset;
+ while(n!=NULL)
+ {
+ csnset_add_csn(&newcsnset,n->type,&n->csn);
+ n= n->next;
+ }
+ return newcsnset;
+}
+
+void
+csnset_as_string(const CSNSet *csnset,char *s)
+{
+ const CSNSet *n= csnset;
+ while(n!=NULL)
+ {
+ csn_as_attr_option_string(n->type,&n->csn,s);
+ /* sizeof(;vucsn-011111111222233334444) */
+ s+= 1 + LDIF_CSNPREFIX_MAXLENGTH + _CSN_VALIDCSN_STRLEN;
+ n= n->next;
+ }
+}
diff --git a/ldap/servers/slapd/daemon.c b/ldap/servers/slapd/daemon.c
new file mode 100644
index 00000000..db79288f
--- /dev/null
+++ b/ldap/servers/slapd/daemon.c
@@ -0,0 +1,2694 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+#include <string.h>
+#include <sys/types.h>
+#ifdef _WIN32
+#include <windows.h>
+#include <process.h> /* for getpid */
+#include "proto-ntutil.h"
+#include "ntslapdmessages.h"
+#else
+#include <unistd.h>
+#include <sys/socket.h>
+#include <errno.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <sys/types.h>
+#include <netinet/tcp.h>
+#include <netdb.h>
+#include <sys/time.h>
+#include <sys/wait.h>
+#include <pthread.h>
+#endif
+#include <time.h>
+#include <signal.h>
+#if defined(IRIX6_2) || defined(IRIX6_3)
+#include <sys/param.h>
+#endif
+#if defined(_AIX)
+#include <sys/select.h>
+#include <sys/param.h>
+#endif
+#include <fcntl.h>
+#define TCPLEN_T int
+#if !defined( _WIN32 )
+#ifdef NEED_FILIO
+#include <sys/filio.h>
+#else /* NEED_FILIO */
+#include <sys/ioctl.h>
+#endif /* NEED_FILIO */
+#endif /* !defined( _WIN32 ) */
+/* for some reason, linux tty stuff defines CTIME */
+#ifdef LINUX
+#undef CTIME
+#endif
+#include "slap.h"
+#include "slapi-plugin.h"
+
+#include "snmp_collator.h"
+#include <private/pprio.h>
+
+#if defined( NET_SSL )
+#include <ssl.h>
+#endif /* defined(NET_SSL) */
+
+#include "fe.h"
+
+/*
+ * Define the backlog number for use in listen() call.
+ * We use the same definition as in ldapserver/include/base/systems.h
+ */
+#ifndef DAEMON_LISTEN_SIZE
+#define DAEMON_LISTEN_SIZE 128
+#endif
+
+#if defined (LDAP_IOCP)
+#define SLAPD_WAKEUP_TIMER 250
+#else
+#define SLAPD_WAKEUP_TIMER 250
+#endif
+
+int slapd_wakeup_timer = SLAPD_WAKEUP_TIMER; /* time in ms to wakeup */
+#ifdef notdef /* GGOODREPL */
+/*
+ * time in secs to do housekeeping:
+ * this must be greater than slapd_wakeup_timer
+ */
+short slapd_housekeeping_timer = 10;
+#endif /* notdef GGOODREPL */
+
+/* Do we support timeout on socket send() ? */
+int have_send_timeouts = 0;
+
+PRFileDesc* signalpipe[2];
+static int writesignalpipe = SLAPD_INVALID_SOCKET;
+static int readsignalpipe = SLAPD_INVALID_SOCKET;
+
+#define FDS_SIGNAL_PIPE 0
+#define FDS_N_TCPS 1
+#define FDS_S_TCPS 2
+
+static int get_configured_connection_table_size();
+#ifdef RESOLVER_NEEDS_LOW_FILE_DESCRIPTORS
+static void get_loopback_by_addr( void );
+#endif
+
+#ifdef XP_WIN32
+static int createlistensocket(unsigned short port, const PRNetAddr *listenaddr);
+#endif
+static PRFileDesc *createprlistensocket(unsigned short port,
+ const PRNetAddr *listenaddr, int secure);
+static const char *netaddr2string(const PRNetAddr *addr, char *addrbuf,
+ size_t addrbuflen);
+static void set_shutdown (int);
+static void setup_pr_read_pds(Connection_Table *ct, PRFileDesc *n_tcps, PRFileDesc *s_tcps, PRIntn *num_to_read);
+
+#ifdef HPUX10
+static void* catch_signals();
+#endif
+
+#if defined( _WIN32 )
+HANDLE hServDoneEvent = NULL;
+#endif
+
+static int createsignalpipe( void );
+
+
+#if defined( _WIN32 )
+/* Set an event to hook the NT Service termination */
+void *slapd_service_exit_wait()
+{
+#if defined( PURIFYING )
+
+#include <sys/types.h>
+#include <sys/stat.h>
+
+ char module[_MAX_FNAME];
+ char exit_file_name[_MAX_FNAME];
+ char drive[_MAX_DRIVE];
+ char dir[_MAX_DIR];
+ char fname[_MAX_FNAME];
+ char ext[_MAX_EXT];
+ struct stat statbuf;
+
+ memset( module, 0, sizeof( module ) );
+ memset( exit_file_name, 0, sizeof( exit_file_name ) );
+
+ GetModuleFileName(GetModuleHandle( NULL ), module, sizeof( module ) );
+
+ _splitpath( module, drive, dir, fname, ext );
+
+ sprintf( exit_file_name, "%s%s%s", drive, dir, "exitnow.txt" );
+
+ LDAPDebug( LDAP_DEBUG_ANY, "PURIFYING - Create %s to terminate the process.\n", exit_file_name, 0, 0 );
+
+ while ( TRUE )
+ {
+ if( stat( exit_file_name, &statbuf ) < 0)
+ {
+ Sleep( 5000 ); /* 5 Seconds */
+ continue;
+ }
+ LDAPDebug( LDAP_DEBUG_ANY, "slapd shutting down immediately, "
+ "\"%s\" exists - don't forget to delete it\n", exit_file_name, 0, 0 );
+ g_set_shutdown( SLAPI_SHUTDOWN_SIGNAL );
+ return NULL;
+ }
+
+#else /* PURIFYING */
+
+ DWORD dwWait;
+ char szDoneEvent[256];
+
+ sprintf(szDoneEvent, "NS_%s", pszServerName);
+
+ hServDoneEvent = CreateEvent( NULL, // default security attributes (LocalSystem)
+ TRUE, // manual reset event
+ FALSE, // not-signalled
+ szDoneEvent );// named after the service itself.
+
+ /* Wait indefinitely until hServDoneEvent is signaled. */
+ dwWait = WaitForSingleObject( hServDoneEvent, // event object
+ INFINITE ); // wait indefinitely
+
+ /* The termination event has been signalled, log this occurrence, and signal to exit. */
+ ReportSlapdEvent( EVENTLOG_INFORMATION_TYPE, MSG_SERVER_SHUTDOWN_STARTING, 0, NULL );
+
+ g_set_shutdown( SLAPI_SHUTDOWN_SIGNAL );
+ return NULL;
+#endif /* PURIFYING */
+}
+#endif /* _WIN32 */
+
+static char *
+get_pid_file()
+{
+ return(pid_file);
+}
+
+static int daemon_configure_send_timeout(int s,size_t timeout /* Miliseconds*/)
+{
+ /* Currently this function is only good for NT, and expects the s argument to be a SOCKET */
+#if defined(_WIN32)
+ return setsockopt(
+ s,
+ SOL_SOCKET,
+ SO_SNDTIMEO,
+ (char*) &timeout,
+ sizeof(timeout)
+ );
+#else
+ return 0;
+#endif
+}
+
+#if defined (_WIN32)
+/* This function is a workaround for accept problem on NT.
+ Accept call fires on NT during syn scan even though the connection is not
+ open. This causes a resource leak. For more details, see bug 391414.
+ Experimentally, we determined that, in case of syn scan, the local
+ address is set to 0. This in undocumented and my change in the future
+
+ The function returns 0 if this is normal connection
+ 1 if this is syn_scan connection
+ -1 in case of any other error
+ */
+static int
+syn_scan (int sock)
+{
+ int rc;
+ struct sockaddr_in addr;
+ int size = sizeof (addr);
+
+ if (sock == SLAPD_INVALID_SOCKET)
+ return -1;
+
+ rc = getsockname (sock, (struct sockaddr*)&addr, &size);
+ if (rc != 0)
+ return -1;
+ else if (addr.sin_addr.s_addr == 0)
+ return 1;
+ else
+ return 0;
+}
+
+#endif
+
+static int
+accept_and_configure(int s, PRFileDesc *pr_acceptfd, PRNetAddr *pr_netaddr,
+ int addrlen, int secure, PRFileDesc **pr_clonefd)
+{
+ int ns = 0;
+ int ioblock_timeout = config_get_ioblocktimeout();
+ int enable_nagle = config_get_nagle();
+
+ PRIntervalTime pr_timeout = PR_MillisecondsToInterval(slapd_wakeup_timer);
+
+#if !defined( XP_WIN32 )
+ (*pr_clonefd) = PR_Accept(pr_acceptfd, pr_netaddr, pr_timeout);
+ if( !(*pr_clonefd) ) {
+ PRErrorCode prerr = PR_GetError();
+ LDAPDebug( LDAP_DEBUG_ANY, "PR_Accept() failed, "
+ SLAPI_COMPONENT_NAME_NSPR " error %d (%s)\n",
+ prerr, slapd_pr_strerror(prerr), 0 );
+ return(SLAPD_INVALID_SOCKET);
+ }
+
+ ns = configure_pr_socket( pr_clonefd, secure );
+
+#else
+ if( secure ) {
+ (*pr_clonefd) = PR_Accept(pr_acceptfd, pr_netaddr, pr_timeout);
+ if( !(*pr_clonefd) ) {
+ PRErrorCode prerr = PR_GetError();
+ LDAPDebug( LDAP_DEBUG_ANY, "PR_Accept() failed, "
+ SLAPI_COMPONENT_NAME_NSPR " error %d (%s)\n",
+ prerr, slapd_pr_strerror(prerr), 0 );
+
+ /* Bug 613324: Call PR_NT_CancelIo if an error occurs */
+ if( (prerr == PR_IO_TIMEOUT_ERROR ) ||
+ (prerr == PR_PENDING_INTERRUPT_ERROR) ) {
+ if( (PR_NT_CancelIo( pr_acceptfd )) != PR_SUCCESS) {
+ prerr = PR_GetError();
+ LDAPDebug( LDAP_DEBUG_ANY,
+ "PR_NT_CancelIo() failed, "
+ SLAPI_COMPONENT_NAME_NSPR
+ " error %d (%s)\n",
+ prerr, slapd_pr_strerror(prerr), 0 );
+ }
+ }
+ return(SLAPD_INVALID_SOCKET);
+ }
+
+ ns = configure_pr_socket( pr_clonefd, secure );
+
+ } else {
+ struct sockaddr *addr;
+
+ addr = (struct sockaddr *) slapi_ch_malloc( sizeof(struct sockaddr) );
+ ns = accept (s, addr, (TCPLEN_T *)&addrlen);
+
+ if (ns == SLAPD_INVALID_SOCKET) {
+ int oserr = errno;
+
+ LDAPDebug( LDAP_DEBUG_ANY,
+ "accept(%d) failed errno %d (%s)\n",
+ s, oserr, slapd_system_strerror(oserr));
+ }
+
+ else if (syn_scan (ns))
+ {
+ /* this is a work around for accept problem with SYN scan on NT.
+ See bug 391414 for more details */
+ LDAPDebug(LDAP_DEBUG_ANY, "syn-scan request is received - ignored\n", 0, 0, 0);
+ closesocket (ns);
+ ns = SLAPD_INVALID_SOCKET;
+ }
+
+ if ( PR_SetNetAddr(PR_IpAddrNull, PR_AF_INET6, ((struct sockaddr_in *)addr)->sin_port, pr_netaddr)
+ != PR_SUCCESS ) {
+ int oserr = PR_GetError();
+ LDAPDebug( LDAP_DEBUG_ANY, "PR_SetNetAddr() failed, "
+ SLAPI_COMPONENT_NAME_NSPR " error %d (%s)\n",
+ oserr, slapd_pr_strerror(oserr), 0 );
+ } else {
+ PR_ConvertIPv4AddrToIPv6(((struct sockaddr_in *)addr)->sin_addr.s_addr, &(pr_netaddr->ipv6.ip));
+ }
+
+ (*pr_clonefd) = NULL;
+
+ slapi_ch_free( (void **)&addr );
+ configure_ns_socket( &ns );
+ }
+#endif
+
+ return ns;
+}
+
+/*
+ * This is the shiny new re-born daemon function, without all the hair
+ */
+#ifdef _WIN32
+static void setup_read_fds(Connection_Table *ct, fd_set *readfds, int n_tcps, int s_tcps );
+static void handle_read_ready(Connection_Table *ct, fd_set *readfds);
+static void set_timeval_ms(struct timeval *t, int ms);
+#endif
+/* GGOODREPL static void handle_timeout( void ); */
+static void handle_pr_read_ready(Connection_Table *ct, PRIntn num_poll);
+static int handle_new_connection(Connection_Table *ct, int tcps, PRFileDesc *pr_acceptfd, int secure );
+#ifdef _WIN32
+static void unfurl_banners(Connection_Table *ct,daemon_ports_t *ports, int n_tcps, PRFileDesc *s_tcps);
+#else
+static void unfurl_banners(Connection_Table *ct,daemon_ports_t *ports, PRFileDesc *n_tcps, PRFileDesc *s_tcps);
+#endif
+static int write_pid_file();
+static int init_shutdown_detect();
+#ifdef _WIN32
+static int clear_signal(fd_set *readfdset);
+#else
+static int clear_signal(struct POLL_STRUCT *fds);
+#endif
+
+/* Globals which are used to store the sockets between
+ * calls to daemon_pre_setuid_init() and the daemon thread
+ * creation. */
+
+int daemon_pre_setuid_init(daemon_ports_t *ports)
+{
+ int rc = 0;
+
+ if (0 != ports->n_port) {
+#if defined( XP_WIN32 )
+ ports->n_socket = createlistensocket((unsigned short)ports->n_port,
+ &ports->n_listenaddr);
+#else
+ ports->n_socket = createprlistensocket(ports->n_port,
+ &ports->n_listenaddr, 0);
+#endif
+ }
+
+ if ( config_get_security() && (0 != ports->s_port) ) {
+ ports->s_socket = createprlistensocket((unsigned short)ports->s_port,
+ &ports->s_listenaddr, 1);
+#ifdef XP_WIN32
+ ports->s_socket_native = PR_FileDesc2NativeHandle(ports->s_socket);
+#endif
+ /* check if ports->s_socket != -1 ? */
+ rc = slapd_ssl_init2 ( &ports->s_socket, 0 );
+ } else {
+ ports->s_socket = SLAPD_INVALID_SOCKET;
+#ifdef XP_WIN32
+ ports->s_socket_native = SLAPD_INVALID_SOCKET;
+#endif
+ }
+
+ return( rc );
+}
+
+
+/* Decide whether we're running on a platform which supports send with timeouts */
+static void detect_timeout_support()
+{
+ /* Currently we know that NT4.0 or higher DOES support timeouts */
+#if defined _WIN32
+ /* Get the OS revision */
+ OSVERSIONINFO ver;
+ ver.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
+ GetVersionEx(&ver);
+ if (ver.dwPlatformId == VER_PLATFORM_WIN32_NT && ver.dwMajorVersion >= 4) {
+ have_send_timeouts = 1;
+ }
+#else
+ /* Some UNIXen do, but for now I don't feel confident which , and whether timeouts really work there */
+#endif
+}
+
+
+/*
+ * The time_shutdown static variable is used to signal the time thread
+ * to shutdown. We used to shut down the time thread when g_get_shutdown()
+ * returned a non-zero value, but that caused the clock to stop, so to speak,
+ * and all error log entries to have the same timestamp once the shutdown
+ * process began.
+ */
+static int time_shutdown = 0;
+
+void *
+time_thread(void *nothing)
+{
+ PRIntervalTime interval;
+
+ interval = PR_SecondsToInterval(1);
+
+ while(!time_shutdown) {
+ poll_current_time();
+ csngen_update_time ();
+ DS_Sleep(interval);
+ }
+
+ /*NOTREACHED*/
+ return(NULL);
+}
+
+
+void slapd_daemon( daemon_ports_t *ports )
+{
+ /* We are passed a pair of ports---one for regular connections, the
+ * other for SSL connections.
+ */
+ /* Previously there was a ton of code #defined on NET_SSL.
+ * This looked horrible, so now I'm doing it this way:
+ * If you want me to do SSL, pass me something in the ssl port number.
+ * If you don't, pass me zero.
+ */
+
+#if defined( XP_WIN32 )
+ int n_tcps = 0;
+ int s_tcps_native = 0;
+#else
+ PRFileDesc *n_tcps = NULL;
+ PRFileDesc *tcps = 0;
+#endif
+ PRFileDesc *s_tcps = NULL;
+ PRIntn num_poll = 0;
+ PRIntervalTime pr_timeout = PR_MillisecondsToInterval(slapd_wakeup_timer);
+ PRThread *time_thread_p;
+ int threads;
+ int in_referral_mode = config_check_referral_mode();
+
+ int connection_table_size = get_configured_connection_table_size();
+ the_connection_table= connection_table_new(connection_table_size);
+
+#ifdef RESOLVER_NEEDS_LOW_FILE_DESCRIPTORS
+ /*
+ * Some DNS resolver implementations, such as the one built into
+ * Solaris <= 8, need to use one or more low numbered file
+ * descriptors internally (probably because they use a deficient
+ * implementation of stdio). So we make a call now that uses the
+ * resolver so it has an opportunity to grab whatever low file
+ * descriptors it needs (before we use up all of the low numbered
+ * ones for incoming client connections and so on).
+ */
+ get_loopback_by_addr();
+#endif
+
+ /* Retrieve the sockets from their hiding place */
+ n_tcps = ports->n_socket;
+ s_tcps = ports->s_socket;
+#ifdef XP_WIN32
+ s_tcps_native = ports->s_socket_native;
+#endif
+
+ createsignalpipe();
+
+ init_shutdown_detect();
+
+#if defined( XP_WIN32 )
+ if ( (n_tcps == SLAPD_INVALID_SOCKET) &&
+#else
+ if ( (n_tcps == NULL) &&
+#endif
+ (s_tcps == NULL) ) { /* nothing to do */
+ LDAPDebug( LDAP_DEBUG_ANY,
+ "no port to listen on\n", 0, 0, 0 );
+ exit( 1 );
+ }
+
+ unfurl_banners(the_connection_table,ports,n_tcps,s_tcps);
+ init_op_threads ();
+ detect_timeout_support();
+
+ /* Start the time thread */
+ time_thread_p = PR_CreateThread(PR_SYSTEM_THREAD,
+ (VFP) (void *) time_thread, NULL,
+ PR_PRIORITY_NORMAL, PR_GLOBAL_THREAD,
+ PR_JOINABLE_THREAD,
+ SLAPD_DEFAULT_THREAD_STACKSIZE);
+ if ( NULL == time_thread_p ) {
+ PRErrorCode errorCode = PR_GetError();
+ LDAPDebug(LDAP_DEBUG_ANY, "Unable to create time thread - Shutting Down ("
+ SLAPI_COMPONENT_NAME_NSPR " error %d - %s)\n",
+ errorCode, slapd_pr_strerror(errorCode), 0);
+ g_set_shutdown( SLAPI_SHUTDOWN_EXIT );
+ }
+
+ /* We are now ready to accept imcoming connections */
+#if defined( XP_WIN32 )
+ if ( n_tcps != SLAPD_INVALID_SOCKET
+ && listen( n_tcps, DAEMON_LISTEN_SIZE ) == -1 ) {
+ int oserr = errno;
+ char addrbuf[ 256 ];
+
+ slapi_log_error(SLAPI_LOG_FATAL, "slapd_daemon",
+ "listen() on %s port %d failed: OS error %d (%s)\n",
+ netaddr2string(&ports->n_listenaddr, addrbuf, sizeof(addrbuf)),
+ ports->n_port, oserr, slapd_system_strerror( oserr ) );
+ g_set_shutdown( SLAPI_SHUTDOWN_EXIT );
+ }
+#else
+ if ( n_tcps != NULL
+ && PR_Listen( n_tcps, DAEMON_LISTEN_SIZE ) == PR_FAILURE) {
+ PRErrorCode prerr = PR_GetError();
+ char addrbuf[ 256 ];
+
+ slapi_log_error(SLAPI_LOG_FATAL, "slapd_daemon",
+ "PR_Listen() on %s port %d failed: %s error %d (%s)\n",
+ netaddr2string(&ports->n_listenaddr, addrbuf, sizeof(addrbuf)),
+ ports->n_port, SLAPI_COMPONENT_NAME_NSPR, prerr,
+ slapd_pr_strerror( prerr ));
+ g_set_shutdown( SLAPI_SHUTDOWN_EXIT );
+ }
+#endif
+
+ if ( s_tcps != NULL
+ && PR_Listen( s_tcps, DAEMON_LISTEN_SIZE ) == PR_FAILURE ) {
+ PRErrorCode prerr = PR_GetError();
+ char addrbuf[ 256 ];
+
+ slapi_log_error(SLAPI_LOG_FATAL, "slapd_daemon",
+ "PR_Listen() on %s port %d failed: %s error %d (%s)\n",
+ netaddr2string(&ports->s_listenaddr, addrbuf, sizeof(addrbuf)),
+ ports->s_port, SLAPI_COMPONENT_NAME_NSPR, prerr,
+ slapd_pr_strerror( prerr ));
+ g_set_shutdown( SLAPI_SHUTDOWN_EXIT );
+ }
+
+ /* Now we write the pid file, indicating that the server is finally and listening for connections */
+ write_pid_file();
+
+ /* The meat of the operation is in a loop on a call to select */
+ while(!g_get_shutdown())
+ {
+#ifdef _WIN32
+ fd_set readfds;
+ struct timeval wakeup_timer;
+ int oserr;
+#endif
+ int select_return = 0;
+ int secure = 0; /* is a new connection an SSL one ? */
+#ifndef _WIN32
+ PRErrorCode prerr;
+#endif
+
+#ifdef _WIN32
+ set_timeval_ms(&wakeup_timer, slapd_wakeup_timer);
+ setup_read_fds(the_connection_table,&readfds,n_tcps, s_tcps_native);
+ /* This select needs to timeout to give the server a chance to test for shutdown */
+ select_return = select(connection_table_size, &readfds, NULL, 0, &wakeup_timer);
+#else
+ setup_pr_read_pds(the_connection_table,n_tcps,s_tcps,&num_poll);
+ select_return = POLL_FN(the_connection_table->fd, num_poll, pr_timeout);
+#endif
+ switch (select_return) {
+ case 0: /* Timeout */
+ /* GGOODREPL handle_timeout(); */
+ break;
+ case -1: /* Error */
+#ifdef _WIN32
+ oserr = errno;
+
+ LDAPDebug( LDAP_DEBUG_TRACE,
+ "select failed errno %d (%s)\n", oserr,
+ slapd_system_strerror(oserr), 0 );
+#else
+ prerr = PR_GetError();
+ LDAPDebug( LDAP_DEBUG_TRACE, "PR_Poll() failed, "
+ SLAPI_COMPONENT_NAME_NSPR " error %d (%s)\n",
+ prerr, slapd_system_strerror(prerr), 0 );
+#endif
+ break;
+ default: /* either a new connection or some new data ready */
+ /* Figure out if we are dealing with one of the listen sockets */
+#ifdef _WIN32
+ /* If so, then handle a new connection */
+ if ( n_tcps != SLAPD_INVALID_SOCKET && FD_ISSET( n_tcps,&readfds ) ) {
+ handle_new_connection(the_connection_table,n_tcps,NULL,0);
+ }
+ /* If so, then handle a new connection */
+ if ( s_tcps != SLAPD_INVALID_SOCKET && FD_ISSET( s_tcps_native,&readfds ) ) {
+ handle_new_connection(the_connection_table,SLAPD_INVALID_SOCKET,s_tcps,1);
+ }
+ /* handle new data ready */
+ handle_read_ready(the_connection_table,&readfds);
+ clear_signal(&readfds);
+#else
+ tcps = NULL;
+ /* info for n_tcps is always in fd[FDS_N_TCPS] and info for s_tcps is always
+ * in fd[FDS_S_TCPS] */
+ if( n_tcps != NULL &&
+ the_connection_table->fd[FDS_N_TCPS].out_flags & SLAPD_POLL_FLAGS ) {
+ tcps = n_tcps;
+ } else if ( s_tcps != NULL &&
+ the_connection_table->fd[FDS_S_TCPS].out_flags & SLAPD_POLL_FLAGS ) {
+ tcps = s_tcps;
+ secure = 1;
+ }
+ /* If so, then handle a new connection */
+ if ( tcps != NULL ) {
+ handle_new_connection(the_connection_table,SLAPD_INVALID_SOCKET,tcps,secure);
+ }
+ /* handle new data ready */
+ handle_pr_read_ready(the_connection_table, connection_table_size);
+ clear_signal(the_connection_table->fd);
+#endif
+ break;
+ }
+
+ }
+ /* We get here when the server is shutting down */
+ /* Do what we have to do before death */
+
+ connection_table_abandon_all_operations(the_connection_table); /* abandon all operations in progress */
+
+ if ( ! in_referral_mode ) {
+ ps_stop_psearch_system(); /* stop any persistent searches */
+ }
+
+#ifdef _WIN32
+ if ( n_tcps != SLAPD_INVALID_SOCKET ) {
+ closesocket( n_tcps );
+#else
+ if ( n_tcps != NULL ) {
+ PR_Close( n_tcps );
+#endif
+ }
+ if ( s_tcps != NULL ) {
+ PR_Close( s_tcps );
+ }
+
+ /* Might compete with housecleaning thread, but so far so good */
+ be_flushall();
+ op_thread_cleanup();
+ housekeeping_stop(); /* Run this after op_thread_cleanup() logged sth */
+
+#ifndef _WIN32
+ if ( active_threads > 0 ) {
+ LDAPDebug( LDAP_DEBUG_ANY,
+ "slapd shutting down - waiting for %d thread%s to terminate\n",
+ active_threads, ( active_threads > 1 ) ? "s" : "", 0 );
+ }
+#endif
+
+ threads = active_threads;
+ while ( active_threads > 0 ) {
+ PRPollDesc xpd;
+ char x;
+ int spe = 0;
+
+ /* try to read from the signal pipe, in case threads are
+ * blocked on it. */
+ xpd.fd = signalpipe[0];
+ xpd.in_flags = PR_POLL_READ;
+ xpd.out_flags = 0;
+ spe = PR_Poll(&xpd, 1, PR_INTERVAL_NO_WAIT);
+ if (spe > 0) {
+ spe = PR_Read(signalpipe[0], &x, 1);
+ if (spe < 0) {
+ PRErrorCode prerr = PR_GetError();
+ LDAPDebug( LDAP_DEBUG_ANY, "listener could not clear signal pipe, "
+ SLAPI_COMPONENT_NAME_NSPR " error %d (%s)\n",
+ prerr, slapd_system_strerror(prerr), 0 );
+ break;
+ }
+ } else if (spe == -1) {
+ PRErrorCode prerr = PR_GetError();
+ LDAPDebug( LDAP_DEBUG_ANY, "PR_Poll() failed, "
+ SLAPI_COMPONENT_NAME_NSPR " error %d (%s)\n",
+ prerr, slapd_system_strerror(prerr), 0 );
+ break;
+ } else {
+ /* no data */
+ }
+ DS_Sleep(PR_INTERVAL_NO_WAIT);
+ if ( threads != active_threads ) {
+ LDAPDebug( LDAP_DEBUG_TRACE,
+ "slapd shutting down - waiting for %d threads to terminate\n",
+ active_threads, 0, 0 );
+ threads = active_threads;
+ }
+ }
+
+ LDAPDebug( LDAP_DEBUG_ANY,
+ "slapd shutting down - closing down internal subsystems and plugins\n",
+ 0, 0, 0 );
+
+ log_access_flush();
+
+ /* let backends do whatever cleanup they need to do */
+ LDAPDebug( LDAP_DEBUG_TRACE,"slapd shutting down - waiting for backends to close down\n", 0, 0,0 );
+
+ eq_stop();
+ if ( ! in_referral_mode ) {
+ task_shutdown();
+ uniqueIDGenCleanup ();
+ }
+
+ plugin_closeall( 1 /* Close Backends */, 1 /* Close Gloabls */);
+
+ if ( ! in_referral_mode ) {
+ /* Close SNMP collator after the plugins closed...
+ * Replication plugin still performs internal ops that
+ * may try to increment snmp stats.
+ * Fix for defect 523780
+ */
+ snmp_collator_stop();
+ mapping_tree_free ();
+ }
+
+ be_cleanupall ();
+ LDAPDebug( LDAP_DEBUG_TRACE, "slapd shutting down - backends closed down\n",
+ 0, 0, 0 );
+ referrals_free();
+
+ connection_table_free(the_connection_table);
+ the_connection_table= NULL;
+
+ /* tell the time thread to shutdown and then wait for it */
+ time_shutdown = 1;
+ PR_JoinThread( time_thread_p );
+
+#ifdef _WIN32
+ WSACleanup();
+#endif
+}
+
+int signal_listner()
+{
+ /* Replaces previous macro---called to bump the thread out of select */
+#if defined( _WIN32 )
+ if ( PR_Write( signalpipe[1], "", 1) != 1 ) {
+ /* this now means that the pipe is full
+ * this is not a problem just go-on
+ */
+ LDAPDebug( LDAP_DEBUG_CONNS,
+ "listener could not write to signal pipe %d\n",
+ errno, 0, 0 );
+ }
+
+#else
+ if ( write( writesignalpipe, "", 1) != 1 ) {
+ /* this now means that the pipe is full
+ * this is not a problem just go-on
+ */
+ LDAPDebug( LDAP_DEBUG_CONNS,
+ "listener could not write to signal pipe %d\n",
+ errno, 0, 0 );
+ }
+#endif
+ return( 0 );
+}
+
+#ifdef _WIN32
+static int clear_signal(fd_set *readfdset)
+#else
+static int clear_signal(struct POLL_STRUCT *fds)
+#endif
+{
+#ifdef _WIN32
+ if ( FD_ISSET(readsignalpipe, readfdset)) {
+#else
+ if ( fds[FDS_SIGNAL_PIPE].out_flags & SLAPD_POLL_FLAGS ) {
+#endif
+ char buf[200];
+
+ LDAPDebug( LDAP_DEBUG_CONNS,
+ "listener got signaled\n",
+ 0, 0, 0 );
+#ifdef _WIN32
+ if ( PR_Read( signalpipe[0], buf, 20 ) < 1 ) {
+#else
+ if ( read( readsignalpipe, buf, 200 ) < 1 ) {
+#endif
+ LDAPDebug( LDAP_DEBUG_ANY,
+ "listener could not clear signal pipe\n",
+ 0, 0, 0 );
+ }
+ }
+ return 0;
+}
+
+#ifdef _WIN32
+static void set_timeval_ms(struct timeval *t, int ms)
+{
+ t->tv_sec = ms/1000;
+ t->tv_usec = (ms % 1000)*1000;
+}
+#endif
+
+#ifdef _WIN32
+static void setup_read_fds(Connection_Table *ct, fd_set *readfds, int n_tcps, int s_tcps)
+{
+ Connection *c= NULL;
+ Connection *next= NULL;
+ int accept_new_connections;
+ static int last_accept_new_connections = -1;
+ slapdFrontendConfig_t *slapdFrontendConfig = getFrontendConfig();
+
+ LBER_SOCKET socketdesc = SLAPD_INVALID_SOCKET;
+
+ FD_ZERO( readfds );
+
+ accept_new_connections = ((ct->size - g_get_current_conn_count())
+ > slapdFrontendConfig->reservedescriptors);
+ if ( ! accept_new_connections ) {
+ if ( last_accept_new_connections ) {
+ LDAPDebug( LDAP_DEBUG_ANY, "Not listening for new "
+ "connections - too many fds open\n", 0, 0, 0 );
+ }
+ } else {
+ if ( ! last_accept_new_connections &&
+ last_accept_new_connections != -1 ) {
+ LDAPDebug( LDAP_DEBUG_ANY, "Listening for new "
+ "connections again\n", 0, 0, 0 );
+ }
+ }
+ last_accept_new_connections = accept_new_connections;
+ if (n_tcps != SLAPD_INVALID_SOCKET && accept_new_connections) {
+ FD_SET( n_tcps, readfds );
+ LDAPDebug( LDAP_DEBUG_HOUSE,
+ "listening for connections on %d\n", n_tcps, 0, 0 );
+ }
+ if (s_tcps != SLAPD_INVALID_SOCKET && accept_new_connections) {
+ FD_SET( s_tcps, readfds );
+ LDAPDebug( LDAP_DEBUG_HOUSE,
+ "listening for connections on %d\n", s_tcps, 0, 0 );
+ }
+
+ if ((s_tcps != SLAPD_INVALID_SOCKET)
+ && (readsignalpipe != SLAPD_INVALID_SOCKET)) {
+ FD_SET( readsignalpipe, readfds );
+ }
+
+ /* Walk down the list of active connections to find
+ * out which connections we should poll over. If a connection
+ * is no longer in use, we should remove it from the linked
+ * list. */
+ c= connection_table_get_first_active_connection (ct);
+ while (c)
+ {
+ next = connection_table_get_next_active_connection (ct, c);
+ if ( c->c_mutex == NULL )
+ {
+ connection_table_move_connection_out_of_active_list(ct,c);
+ }
+ else
+ {
+ PR_Lock( c->c_mutex );
+ if ( c->c_flags & CONN_FLAG_CLOSING )
+ {
+ /* A worker thread has marked that this connection
+ * should be closed by calling disconnect_server.
+ * move this connection out of the active list
+ * the last thread to use the connection will close it
+ */
+ connection_table_move_connection_out_of_active_list(ct,c);
+ }
+ else if ( c->c_sd == SLAPD_INVALID_SOCKET )
+ {
+ connection_table_move_connection_out_of_active_list(ct,c);
+ }
+ else
+ {
+#if defined(LDAP_IOCP) /* When we have IO completion ports, we don't want to do this */
+ if ( !c->c_gettingber && (c->c_flags & CONN_FLAG_SSL) )
+#else
+ if ( !c->c_gettingber )
+#endif
+ {
+ FD_SET( c->c_sd, readfds );
+ }
+ }
+ PR_Unlock( c->c_mutex );
+ }
+ c = next;
+ }
+}
+#endif /* _WIN32 */
+
+static int first_time_setup_pr_read_pds = 1;
+static void
+setup_pr_read_pds(Connection_Table *ct, PRFileDesc *n_tcps, PRFileDesc *s_tcps, PRIntn *num_to_read)
+{
+ Connection *c= NULL;
+ Connection *next= NULL;
+ LBER_SOCKET socketdesc = SLAPD_INVALID_SOCKET;
+ int accept_new_connections;
+ static int last_accept_new_connections = -1;
+ PRIntn count = 0;
+ slapdFrontendConfig_t *slapdFrontendConfig = getFrontendConfig();
+ int max_threads_per_conn = config_get_maxthreadsperconn();
+
+ accept_new_connections = ((ct->size - g_get_current_conn_count())
+ > slapdFrontendConfig->reservedescriptors);
+ if ( ! accept_new_connections ) {
+ if ( last_accept_new_connections ) {
+ LDAPDebug( LDAP_DEBUG_ANY, "Not listening for new "
+ "connections - too many fds open\n", 0, 0, 0 );
+ /* reinitialize n_tcps and s_tcps to the pds */
+ first_time_setup_pr_read_pds = 1;
+ }
+ } else {
+ if ( ! last_accept_new_connections &&
+ last_accept_new_connections != -1 ) {
+ LDAPDebug( LDAP_DEBUG_ANY, "Listening for new "
+ "connections again\n", 0, 0, 0 );
+ /* reinitialize n_tcps and s_tcps to the pds */
+ first_time_setup_pr_read_pds = 1;
+ }
+ }
+ last_accept_new_connections = accept_new_connections;
+
+
+ /* initialize the mapping from connection table entries to fds entries */
+ if (first_time_setup_pr_read_pds)
+ {
+ int i;
+ for (i = 0; i < ct->size; i++)
+ {
+ ct->c[i].c_fdi = SLAPD_INVALID_SOCKET_INDEX;
+ }
+
+ /* The fds entry for n_tcps is always FDS_N_TCPS */
+ if (n_tcps != NULL && accept_new_connections)
+ {
+ ct->fd[FDS_N_TCPS].fd = n_tcps;
+ ct->fd[FDS_N_TCPS].in_flags = SLAPD_POLL_FLAGS;
+ ct->fd[FDS_N_TCPS].out_flags = 0;
+ LDAPDebug( LDAP_DEBUG_HOUSE,
+ "listening for connections on %d\n", socketdesc, 0, 0 );
+ } else {
+ ct->fd[FDS_N_TCPS].fd = NULL;
+ }
+
+ /* The fds entry for s_tcps is always FDS_S_TCPS */
+ if (s_tcps != NULL && accept_new_connections)
+ {
+ ct->fd[FDS_S_TCPS].fd = s_tcps;
+ ct->fd[FDS_S_TCPS].in_flags = SLAPD_POLL_FLAGS;
+ ct->fd[FDS_S_TCPS].out_flags = 0;
+ LDAPDebug( LDAP_DEBUG_HOUSE,
+ "listening for SSL connections on %d\n", socketdesc, 0, 0 );
+ } else {
+ ct->fd[FDS_S_TCPS].fd = NULL;
+ }
+
+#if !defined(_WIN32)
+ /* The fds entry for the signalpipe is always FDS_SIGNAL_PIPE */
+ ct->fd[FDS_SIGNAL_PIPE].fd = signalpipe[0];
+ ct->fd[FDS_SIGNAL_PIPE].in_flags = SLAPD_POLL_FLAGS;
+ ct->fd[FDS_SIGNAL_PIPE].out_flags = 0;
+#else
+ ct->fd[FDS_SIGNAL_PIPE].fd = NULL;
+#endif
+ first_time_setup_pr_read_pds = 0;
+ }
+
+ /* count is the number of entries we've place in the fds array.
+ * we always put n_tcps in slot FDS_N_TCPS, s_tcps in slot
+ * FDS_S_TCPS and the signal pipe in slot FDS_SIGNAL_PIPE
+ * so we now set count to 3 */
+ count = 3;
+
+ /* Walk down the list of active connections to find
+ * out which connections we should poll over. If a connection
+ * is no longer in use, we should remove it from the linked
+ * list. */
+ c = connection_table_get_first_active_connection (ct);
+ while (c)
+ {
+ next = connection_table_get_next_active_connection (ct, c);
+ if ( c->c_mutex == NULL )
+ {
+ connection_table_move_connection_out_of_active_list(ct,c);
+ }
+ else
+ {
+ PR_Lock( c->c_mutex );
+ if (c->c_flags & CONN_FLAG_CLOSING)
+ {
+ /* A worker thread has marked that this connection
+ * should be closed by calling disconnect_server.
+ * move this connection out of the active list
+ * the last thread to use the connection will close it
+ */
+ connection_table_move_connection_out_of_active_list(ct,c);
+ }
+ else if ( c->c_sd == SLAPD_INVALID_SOCKET )
+ {
+ connection_table_move_connection_out_of_active_list(ct,c);
+ }
+ else if ( c->c_prfd != NULL)
+ {
+ if ((!c->c_gettingber)
+ && (c->c_threadnumber < max_threads_per_conn))
+ {
+ ct->fd[count].fd = c->c_prfd;
+ ct->fd[count].in_flags = SLAPD_POLL_FLAGS;
+ /* slot i of the connection table is mapped to slot
+ * count of the fds array */
+ c->c_fdi = count;
+ count++;
+ }
+ else
+ {
+ c->c_fdi = SLAPD_INVALID_SOCKET_INDEX;
+ }
+ }
+ PR_Unlock( c->c_mutex );
+ }
+ c = next;
+ }
+
+ if( num_to_read )
+ (*num_to_read) = count;
+
+}
+
+#ifdef notdef /* GGOODREPL */
+static void
+handle_timeout( void )
+{
+ static time_t prevtime = 0;
+ static time_t housekeeping_fire_time = 0;
+ time_t curtime = current_time();
+
+ if (0 == prevtime) {
+ prevtime = time (&housekeeping_fire_time);
+ }
+
+ if ( difftime(curtime, prevtime) >=
+ slapd_housekeeping_timer ) {
+ int num_active_threads;
+
+ snmp_collator_update();
+
+ prevtime = curtime;
+ num_active_threads = active_threads;
+ if ( (num_active_threads == 0) ||
+ (difftime(curtime, housekeeping_fire_time) >=
+ slapd_housekeeping_timer*3) ) {
+ housekeeping_fire_time = curtime;
+ housekeeping_start(curtime);
+ }
+ }
+
+}
+#endif /* notdef */
+
+
+static int idletimeout_reslimit_handle = -1;
+
+/*
+ * Register the idletimeout with the binder-based resource limits
+ * subsystem. A SLAPI_RESLIMIT_STATUS_... code is returned.
+ */
+int
+daemon_register_reslimits( void )
+{
+ return( slapi_reslimit_register( SLAPI_RESLIMIT_TYPE_INT, "nsIdleTimeout",
+ &idletimeout_reslimit_handle ));
+}
+
+
+/*
+ * Compute the idle timeout for the connection.
+ *
+ * Note: this function must always be called with conn->c_mutex locked.
+ */
+static int
+compute_idletimeout( slapdFrontendConfig_t *fecfg, Connection *conn )
+{
+ int idletimeout;
+
+ if ( slapi_reslimit_get_integer_limit( conn, idletimeout_reslimit_handle,
+ &idletimeout ) != SLAPI_RESLIMIT_STATUS_SUCCESS ) {
+ /*
+ * no limit associated with binder/connection or some other error
+ * occurred. use the default idle timeout.
+ */
+ if ( conn->c_isroot ) {
+ idletimeout = 0; /* no limit for Directory Manager */
+ } else {
+ idletimeout = fecfg->idletimeout;
+ }
+ }
+
+ return( idletimeout );
+}
+
+
+#ifdef _WIN32
+static void
+handle_read_ready(Connection_Table *ct, fd_set *readfds)
+{
+ Connection *c= NULL;
+ time_t curtime = current_time();
+ slapdFrontendConfig_t *slapdFrontendConfig = getFrontendConfig();
+ int idletimeout;
+
+#ifdef LDAP_DEBUG
+ if ( slapd_ldap_debug & LDAP_DEBUG_CONNS )
+ {
+ connection_table_dump_activity_to_errors_log(ct);
+ }
+#endif /* LDAP_DEBUG */
+
+
+ /* Instead of going through the whole connection table to see which
+ * connections we can read from, we'll only check the slots in the
+ * linked list */
+ c = connection_table_get_first_active_connection (ct);
+ while ( c!=NULL )
+ {
+ if ( c->c_mutex != NULL )
+ {
+ PR_Lock( c->c_mutex );
+ if (connection_is_active_nolock (c) && c->c_gettingber == 0 )
+ {
+ /* read activity */
+ short readready= ( FD_ISSET( c->c_sd, readfds ) );
+
+ /* read activity */
+ if ( readready )
+ {
+ LDAPDebug( LDAP_DEBUG_CONNS, "read activity on %d\n", c->c_ci, 0, 0 );
+ c->c_idlesince = curtime;
+
+ /* This is where the work happens ! */
+ connection_activity( c );
+
+ /* idle timeout */
+ }
+ else if (( idletimeout = compute_idletimeout(
+ slapdFrontendConfig, c )) > 0 &&
+ (curtime - c->c_idlesince) >= idletimeout &&
+ NULL == c->c_ops )
+ {
+ disconnect_server_nomutex( c, c->c_connid, -1,
+ SLAPD_DISCONNECT_IDLE_TIMEOUT, EAGAIN );
+ }
+ }
+ PR_Unlock( c->c_mutex );
+ }
+ c = connection_table_get_next_active_connection (ct, c);
+ }
+}
+#endif /* _WIN32 */
+
+
+static void
+handle_pr_read_ready(Connection_Table *ct, PRIntn num_poll)
+{
+ Connection *c;
+ time_t curtime = current_time();
+ slapdFrontendConfig_t *slapdFrontendConfig = getFrontendConfig();
+ int idletimeout;
+#if defined( XP_WIN32 )
+ int i;
+#endif
+
+#if LDAP_DEBUG
+ if ( slapd_ldap_debug & LDAP_DEBUG_CONNS )
+ {
+ connection_table_dump_activity_to_errors_log(ct);
+ }
+#endif /* LDAP_DEBUG */
+
+#if defined( XP_WIN32 )
+ /*
+ * WIN32: this function is only called for SSL connections and
+ * num_poll indicates exactly how many PR fds we polled on.
+ */
+ for ( i = 0; i < num_poll; i++ )
+ {
+ short readready;
+ readready = (ct->fd[i].out_flags & SLAPD_POLL_FLAGS);
+
+ /* Find the connection we are referring to */
+ for ( c = connection_table_get_first_active_connection (ct); c != NULL;
+ c = connection_table_get_next_active_connection (ct, c) )
+ {
+ if ( c->c_mutex != NULL )
+ {
+ PR_Lock( c->c_mutex );
+ if ( c->c_prfd == ct->fd[i].fd )
+ {
+ break; /* c_mutex is still locked! */
+ }
+ PR_Unlock( c->c_mutex );
+ }
+ }
+
+ if ( c == NULL )
+ { /* connection not found! */
+ LDAPDebug( LDAP_DEBUG_CONNS, "handle_pr_read_ready: "
+ "connection not found for poll slot %d\n", i,0,0 );
+ }
+ else
+ {
+ /* c_mutex is still locked... check for activity and errors */
+ if ( !readready && ct->fd[i].out_flags && c->c_prfd == ct->fd[i].fd )
+ {
+ /* some error occured */
+ LDAPDebug( LDAP_DEBUG_CONNS,
+ "poll says connection on sd %d is bad "
+ "(closing)\n", c->c_sd, 0, 0 );
+ disconnect_server_nomutex( c, c->c_connid, -1, SLAPD_DISCONNECT_POLL, EPIPE );
+ }
+ else if ( readready && c->c_prfd == ct->fd[i].fd )
+ {
+ /* read activity */
+ LDAPDebug( LDAP_DEBUG_CONNS,
+ "read activity on %d\n", i, 0, 0 );
+ c->c_idlesince = curtime;
+
+ /* This is where the work happens ! */
+ connection_activity( c );
+ }
+ else if (( idletimeout = compute_idletimeout( slapdFrontendConfig,
+ c )) > 0 &&
+ c->c_prfd == ct->fd[i].fd &&
+ (curtime - c->c_idlesince) >= idletimeout &&
+ NULL == c->c_ops )
+ {
+ /* idle timeout */
+ disconnect_server_nomutex( c, c->c_connid, -1,
+ SLAPD_DISCONNECT_IDLE_TIMEOUT, EAGAIN );
+ }
+
+ PR_Unlock( c->c_mutex );
+ }
+ }
+#else
+
+ /*
+ * non-WIN32: this function is called for all connections, so we
+ * traverse the entire active connection list to find any errors,
+ * activity, etc.
+ */
+ for ( c = connection_table_get_first_active_connection (ct); c != NULL;
+ c = connection_table_get_next_active_connection (ct, c) )
+ {
+ if ( c->c_mutex != NULL )
+ {
+ PR_Lock( c->c_mutex );
+ if ( connection_is_active_nolock (c) && c->c_gettingber == 0 )
+ {
+ PRInt16 out_flags;
+ short readready;
+
+ if (c->c_fdi != SLAPD_INVALID_SOCKET_INDEX)
+ {
+ out_flags = ct->fd[c->c_fdi].out_flags;
+ }
+ else
+ {
+ out_flags = 0;
+ }
+
+ readready = ( out_flags & SLAPD_POLL_FLAGS );
+
+ if ( !readready && out_flags )
+ {
+ /* some error occured */
+ LDAPDebug( LDAP_DEBUG_CONNS,
+ "POLL_FN() says connection on sd %d is bad "
+ "(closing)\n", c->c_sd, 0, 0 );
+ disconnect_server_nomutex( c, c->c_connid, -1,
+ SLAPD_DISCONNECT_POLL, EPIPE );
+ }
+ else if ( readready )
+ {
+ /* read activity */
+ LDAPDebug( LDAP_DEBUG_CONNS,
+ "read activity on %d\n", c->c_ci, 0, 0 );
+ c->c_idlesince = curtime;
+
+ /* This is where the work happens ! */
+ /* MAB: 25 jan 01, error handling added */
+ if ((connection_activity( c )) == -1) {
+ /* This might happen as a result of
+ * trying to acquire a closing connection
+ */
+ LDAPDebug (LDAP_DEBUG_ANY,
+ "connection_activity: abandoning conn %d as fd=%d is already closing\n",
+ c->c_connid,c->c_sd,0);
+ /* The call disconnect_server should do nothing,
+ * as the connection c should be already set to CLOSING */
+ disconnect_server_nomutex( c, c->c_connid, -1,
+ SLAPD_DISCONNECT_POLL, EPIPE );
+ }
+ }
+ else if (( idletimeout = compute_idletimeout(
+ slapdFrontendConfig, c )) > 0 &&
+ (curtime - c->c_idlesince) >= idletimeout &&
+ NULL == c->c_ops )
+ {
+ /* idle timeout */
+ disconnect_server_nomutex( c, c->c_connid, -1,
+ SLAPD_DISCONNECT_IDLE_TIMEOUT, EAGAIN );
+ }
+ }
+ PR_Unlock( c->c_mutex );
+ }
+ }
+#endif
+}
+
+/*
+ * wrapper functions required so we can implement ioblock_timeout and
+ * avoid blocking forever.
+ */
+
+#define SLAPD_POLLIN 0
+#define SLAPD_POLLOUT 1
+
+/* Return 1 if the given handle is ready for input or output,
+ * or if it becomes ready within g_ioblock_timeout [msec].
+ * Return -1 if handle is not ready and g_ioblock_timeout > 0,
+ * or something goes seriously wrong. Otherwise, return 0.
+ * If -1 is returned, PR_GetError() explains why.
+ * Revision: handle changed to void * to allow 64bit support
+ */
+static int
+slapd_poll( void *handle, int output, int secure )
+{
+ int rc;
+ int ioblock_timeout = config_get_ioblocktimeout();
+
+#if defined( XP_WIN32 )
+ if( !secure ) {
+ fd_set handle_set;
+ struct timeval timeout;
+ int windows_handle = (int) handle;
+
+ memset (&timeout, 0, sizeof(timeout));
+ if (ioblock_timeout > 0) {
+ timeout.tv_sec = ioblock_timeout / 1000;
+ timeout.tv_usec = (ioblock_timeout % 1000) * 1000;
+ }
+ FD_ZERO(&handle_set);
+ FD_SET(windows_handle, &handle_set);
+ rc = output ? select(FD_SETSIZE, NULL, &handle_set, NULL, &timeout)
+ : select(FD_SETSIZE, &handle_set, NULL, NULL, &timeout);
+ } else {
+ struct POLL_STRUCT pr_pd;
+ PRIntervalTime timeout = PR_MillisecondsToInterval( ioblock_timeout );
+
+ if (timeout < 0) timeout = 0;
+ pr_pd.fd = (PRFileDesc *)handle;
+ pr_pd.in_flags = output ? PR_POLL_WRITE : PR_POLL_READ;
+ pr_pd.out_flags = 0;
+ rc = POLL_FN(&pr_pd, 1, timeout);
+ }
+#else
+ struct POLL_STRUCT pr_pd;
+ PRIntervalTime timeout = PR_MillisecondsToInterval(ioblock_timeout);
+
+ if (timeout < 0) timeout = 0;
+ pr_pd.fd = (PRFileDesc *)handle;
+ pr_pd.in_flags = output ? PR_POLL_WRITE : PR_POLL_READ;
+ pr_pd.out_flags = 0;
+ rc = POLL_FN(&pr_pd, 1, timeout);
+#endif
+
+ if (rc < 0) {
+#if defined( XP_WIN32 )
+ if( !secure ) {
+ int oserr = errno;
+
+ LDAPDebug(LDAP_DEBUG_CONNS, "slapd_poll(%d) error %d (%s)\n",
+ handle, oserr, slapd_system_strerror(oserr));
+ if ( SLAPD_SYSTEM_WOULD_BLOCK_ERROR(oserr)) {
+ rc = 0; /* try again */
+ }
+ } else {
+ PRErrorCode prerr = PR_GetError();
+ LDAPDebug(LDAP_DEBUG_CONNS, "slapd_poll(%d) "
+ SLAPI_COMPONENT_NAME_NSPR " error %d (%s)\n",
+ handle, prerr, slapd_pr_strerror(prerr));
+ if ( prerr == PR_PENDING_INTERRUPT_ERROR ||
+ SLAPD_PR_WOULD_BLOCK_ERROR(prerr)) {
+ rc = 0; /* try again */
+ }
+ }
+#else
+ PRErrorCode prerr = PR_GetError();
+ LDAPDebug(LDAP_DEBUG_ANY, "slapd_poll(%d) "
+ SLAPI_COMPONENT_NAME_NSPR " error %d (%s)\n",
+ handle, prerr, slapd_pr_strerror(prerr));
+ if ( prerr == PR_PENDING_INTERRUPT_ERROR ||
+ SLAPD_PR_WOULD_BLOCK_ERROR(prerr)) {
+ rc = 0; /* try again */
+ }
+#endif
+
+ } else if (rc == 0 && ioblock_timeout > 0) {
+ PRIntn ihandle;
+#if !defined( XP_WIN32 )
+ ihandle = PR_FileDesc2NativeHandle((PRFileDesc *)handle);
+#else
+ if( secure )
+ ihandle = PR_FileDesc2NativeHandle((PRFileDesc *)handle);
+ else
+ ihandle = (PRIntn)handle;
+#endif
+ LDAPDebug(LDAP_DEBUG_ANY, "slapd_poll(%d) timed out\n",
+ ihandle, 0, 0);
+#if defined( XP_WIN32 )
+ /*
+ * Bug 624303 - This connection will be cleaned up soon.
+ * During cleanup (see connection_cleanup()), SSL3_SendAlert()
+ * will be called by PR_Close(), and its default wTimeout
+ * in sslSocket associated with the handle
+ * is no time out (I gave up after waited for 30 minutes).
+ * It was during this closing period that server won't
+ * response to new connection requests.
+ * PR_Send() null is a hack here to change the default wTimeout
+ * (see ssl_Send()) to one second which affects PR_Close()
+ * only in the current scenario.
+ */
+ if( secure ) {
+ PR_Send ((PRFileDesc *)handle, NULL, 0, 0, PR_SecondsToInterval(1));
+ }
+#endif
+ PR_SetError(PR_IO_TIMEOUT_ERROR, EAGAIN); /* timeout */
+ rc = -1;
+ }
+ return rc;
+}
+
+/* The following 4 functions each read or write count bytes from or to
+ * a socket handle. If all goes well, they return the same count;
+ * otherwise they return -1 and PR_GetError() explains the problem.
+ * Revision: handle changed to struct lextiof_socket_private * and first
+ * argument which used to be handle is now ignored.
+ */
+int
+secure_read_function( int ignore, void *buffer, int count, struct lextiof_socket_private *handle )
+{
+ int gotbytes = 0;
+ int bytes;
+ int ioblock_timeout = config_get_ioblocktimeout();
+ PRIntervalTime pr_timeout = PR_MillisecondsToInterval(ioblock_timeout);
+
+ if (handle == SLAPD_INVALID_SOCKET) {
+ PR_SetError(PR_NOT_SOCKET_ERROR, EBADF);
+ } else {
+ while (1) {
+ bytes = PR_Recv( (PRFileDesc *)handle, (char *)buffer + gotbytes,
+ count - gotbytes, 0, pr_timeout );
+ if (bytes > 0) {
+ gotbytes += bytes;
+ } else if (bytes < 0) {
+ PRErrorCode prerr = PR_GetError();
+
+#ifdef _WIN32
+ /* we need to do this because on NT, once an I/O
+ times out on an NSPR socket, that socket must
+ be closed before any other I/O can happen in
+ this thread.
+ */
+ if (prerr == PR_IO_TIMEOUT_ERROR){
+ Connection *conn = connection_table_get_connection_from_fd(the_connection_table,(PRFileDesc *)handle);
+ if (conn == NULL)
+ return -1;
+
+ disconnect_server (conn, conn->c_connid, -1, SLAPD_DISCONNECT_NTSSL_TIMEOUT, 0);
+ /* Disconnect_server just tells the poll thread that the
+ * socket should be closed. We'll sleep 2 seconds here to
+ * to make sure that the poll thread has time to run
+ * and close this socket. */
+ DS_Sleep(PR_SecondsToInterval(2));
+
+ LDAPDebug(LDAP_DEBUG_CONNS, "SSL PR_Recv(%d) "
+ SLAPI_COMPONENT_NAME_NSPR " error %d (%s)\n",
+ handle, prerr, slapd_pr_strerror(prerr));
+
+ return -1;
+ }
+#endif
+
+ LDAPDebug(LDAP_DEBUG_CONNS,
+ "SSL PR_Recv(%d) error %d (%s)\n",
+ handle, prerr, slapd_pr_strerror(prerr));
+ if ( !SLAPD_PR_WOULD_BLOCK_ERROR(prerr) ) {
+ break; /* fatal error */
+ }
+ } else if (gotbytes < count) {
+ LDAPDebug(LDAP_DEBUG_CONNS,
+ "SSL PR_Recv(%d) 0 (EOF)\n", /* disconnected */
+ handle, 0, 0);
+ PR_SetError(PR_PIPE_ERROR, EPIPE);
+ break;
+ }
+ if (gotbytes == count) { /* success */
+ return count;
+ } else if (gotbytes > count) { /* too many bytes */
+ PR_SetError(PR_BUFFER_OVERFLOW_ERROR, EMSGSIZE);
+ break;
+ } else if (slapd_poll(handle, SLAPD_POLLIN, 1) < 0) { /* error */
+ break;
+ }
+ }
+ }
+ return -1;
+}
+
+
+/*
+ * Revision: handle changed to struct lextiof_socket_private * and first
+ * argument which used to be handle is now ignored.
+ */
+int
+secure_write_function( int ignore, const void *buffer, int count, struct lextiof_socket_private *handle )
+{
+ int sentbytes = 0;
+ int bytes;
+
+ if (handle == SLAPD_INVALID_SOCKET) {
+ PR_SetError(PR_NOT_SOCKET_ERROR, EBADF);
+ } else {
+ while (1) {
+ if (slapd_poll(handle, SLAPD_POLLOUT, 1) < 0) { /* error */
+ break;
+ }
+ bytes = PR_Write((PRFileDesc *)handle, (char *)buffer + sentbytes,
+ count - sentbytes);
+ if (bytes > 0) {
+ sentbytes += bytes;
+ } else if (bytes < 0) {
+ PRErrorCode prerr = PR_GetError();
+ LDAPDebug(LDAP_DEBUG_CONNS, "SSL PR_Write(%d) "
+ SLAPI_COMPONENT_NAME_NSPR " error %d (%s)\n",
+ handle, prerr, slapd_pr_strerror( prerr ));
+ if ( !SLAPD_PR_WOULD_BLOCK_ERROR(prerr)) {
+ break; /* fatal error */
+ }
+ } else if (sentbytes < count) {
+ LDAPDebug( LDAP_DEBUG_CONNS,
+ "SSL PR_Write(%d) 0\n", /* ??? */ handle, 0, 0);
+ PR_SetError(PR_PIPE_ERROR, EPIPE);
+ break;
+ }
+ if (sentbytes == count) { /* success */
+ return count;
+ } else if (sentbytes > count) { /* too many bytes */
+ PR_SetError(PR_BUFFER_OVERFLOW_ERROR, EMSGSIZE);
+ break;
+ }
+ }
+ }
+ return -1;
+}
+
+/* stub functions required because we need to call send/recv on NT,
+ * but the SDK requires functions with a read/write signature.
+ * Revision: handle changed to struct lextiof_socket_private * and first
+ * argument which used to be handle is now ignored.
+ */
+int
+read_function(int ignore, void *buffer, int count, struct lextiof_socket_private *handle )
+{
+ int gotbytes = 0;
+ int bytes;
+#if !defined( XP_WIN32 )
+ PRIntervalTime pr_timeout = PR_MillisecondsToInterval(1000);
+#endif
+
+ if (handle == SLAPD_INVALID_SOCKET) {
+ PR_SetError(PR_NOT_SOCKET_ERROR, EBADF);
+ } else {
+ while (1) {
+#if !defined( XP_WIN32 )
+ bytes = PR_Recv((PRFileDesc *)handle, (char *)buffer + gotbytes,
+ count - gotbytes, 0, pr_timeout);
+#else
+ bytes = recv((int)handle, (char *)buffer + gotbytes,
+ count - gotbytes, 0);
+#endif
+ if (bytes > 0) {
+ gotbytes += bytes;
+ } else if (bytes < 0) {
+#if !defined( XP_WIN32 )
+ PRErrorCode prerr = PR_GetError();
+
+ LDAPDebug(LDAP_DEBUG_CONNS, "PR_Recv(%d) "
+ SLAPI_COMPONENT_NAME_NSPR " error %d (%s)\n",
+ handle, prerr, slapd_pr_strerror( prerr ));
+ if ( !SLAPD_PR_WOULD_BLOCK_ERROR(prerr)) {
+#else
+ int oserr = errno;
+
+ LDAPDebug(LDAP_DEBUG_CONNS, "recv(%d) OS error %d (%s)\n",
+ handle, oserr, slapd_system_strerror(oserr));
+ if ( !SLAPD_SYSTEM_WOULD_BLOCK_ERROR(oserr)) {
+ PR_SetError(PR_UNKNOWN_ERROR, oserr);
+#endif
+ break; /* fatal error */
+ }
+ } else if (gotbytes < count) { /* disconnected */
+#if !defined( XP_WIN32 )
+ LDAPDebug(LDAP_DEBUG_CONNS, "PR_Recv(%d) 0 (EOF)\n",
+ handle, 0, 0);
+#else
+ LDAPDebug(LDAP_DEBUG_CONNS, "recv(%d) 0 (EOF)\n",
+ handle, 0, 0);
+#endif
+ PR_SetError(PR_PIPE_ERROR, EPIPE);
+ break;
+ }
+ if (gotbytes == count) { /* success */
+ return count;
+ } else if (gotbytes > count) { /* too many bytes */
+ PR_SetError(PR_BUFFER_OVERFLOW_ERROR, EMSGSIZE);
+ break;
+ }
+ /* we did not get the whole PDU
+ * call slapd_poll before starting a new read to get
+ * sure some new data have been received and
+ * thus avoid active looping in the while
+ */
+ if (slapd_poll(handle, SLAPD_POLLIN, 0) < 0) {
+ break;
+ }
+ }
+ }
+ return -1;
+}
+
+/*
+Slapd's old (3.x) network I/O code works something like this:
+ when I want to send some data to the client, I will call send().
+ That might block for a long time, resulting in thread pool starvation,
+ so let's not call it unless we're sure that data can be buffered
+ locally. The mechanism for achieving this is to call select()
+ (poll() on UNIX), on the target socket, passing a short timeout
+ (configurable via cn=config).
+
+ Now, this means that to send some data we're making two system
+ calls. Slowness results.
+
+ I did some research and found the following in the MSDN
+ that NT4.0 and beyond do support the configuration of a send timeout
+ on sockets, so this is code which makes use of that and saves the
+ call to select.
+*/
+
+/*Revision: handle changed from int to void * to allow 64bit support
+ *
+ */
+static int send_with_timeout(void *handle, const char * buffer, int count,int *bytes_sent)
+{
+ int ret = 0;
+#if defined( XP_WIN32 )
+ *bytes_sent = send((SOCKET)handle, buffer,count,0);
+#else
+ *bytes_sent = PR_Write((PRFileDesc *)handle,buffer,count);
+ if (*bytes_sent < 0)
+ {
+ PRErrorCode prerr = PR_GetError();
+ if (SLAPD_PR_WOULD_BLOCK_ERROR(prerr))
+ {
+ if ((ret = slapd_poll(handle, SLAPD_POLLOUT, 0)) < 0)
+ { /* error */
+ *bytes_sent = 0;
+ return ret;
+ }
+
+ }
+ }
+#endif
+
+ return ret;
+}
+
+int
+write_function(int ignore, const void *buffer, int count, struct lextiof_socket_private *handle)
+{
+ int sentbytes = 0;
+ int bytes;
+
+
+ if (handle == SLAPD_INVALID_SOCKET) {
+ PR_SetError(PR_NOT_SOCKET_ERROR, EBADF);
+ } else {
+ while (1) {
+
+ if (send_with_timeout(handle, (char *)buffer + sentbytes, count - sentbytes,&bytes) < 0) { /* error */
+ break;
+ }
+ if (bytes > 0) {
+ sentbytes += bytes;
+ } else if (bytes < 0) {
+#if !defined( XP_WIN32 )
+ PRErrorCode prerr = PR_GetError();
+
+ LDAPDebug(LDAP_DEBUG_CONNS, "PR_Write(%d) "
+ SLAPI_COMPONENT_NAME_NSPR " error %d (%s)\n",
+ handle, prerr, slapd_pr_strerror(prerr));
+ if ( !SLAPD_PR_WOULD_BLOCK_ERROR(prerr)) {
+#else
+ int oserr = errno; /* DBDB this is almost certainly wrong, should be a call to WSAGetLastError() */
+
+ LDAPDebug(LDAP_DEBUG_CONNS, "send(%d) error %d (%s)\n",
+ handle, oserr, slapd_system_strerror(oserr));
+ if ( !SLAPD_SYSTEM_WOULD_BLOCK_ERROR(oserr)) {
+ PR_SetError(PR_UNKNOWN_ERROR, oserr);
+#endif
+ break; /* fatal error */
+ }
+ } else if (sentbytes < count) {
+ LDAPDebug(LDAP_DEBUG_CONNS, "send(%d) 0\n", /* ??? */
+ handle, 0, 0);
+ PR_SetError(PR_PIPE_ERROR, EPIPE);
+ break;
+ }
+ if (sentbytes == count) { /* success */
+ return count;
+ } else if (sentbytes > count) { /* too many bytes */
+ PR_SetError(PR_BUFFER_OVERFLOW_ERROR, EMSGSIZE);
+ break;
+ }
+ }
+ }
+ return -1;
+}
+
+int connection_type = -1; /* The type number assigned by the Factory for 'Connection' */
+
+void
+daemon_register_connection()
+{
+ if(connection_type==-1)
+ {
+ /* The factory is given the name of the object type, in
+ * return for a type handle. Whenever the object is created
+ * or destroyed the factory is called with the handle so
+ * that it may call the constructors or destructors registered
+ * with it.
+ */
+ connection_type= factory_register_type(SLAPI_EXT_CONNECTION,offsetof(Connection,c_extension));
+ }
+}
+
+/* NOTE: this routine is not reentrant */
+static int
+handle_new_connection(Connection_Table *ct, int tcps, PRFileDesc *pr_acceptfd, int secure)
+{
+ int ns = 0;
+ Connection *conn = NULL;
+ /* struct sockaddr_in from;*/
+ PRNetAddr from;
+ PRFileDesc *pr_clonefd = NULL;
+
+ if ( (ns = accept_and_configure( tcps, pr_acceptfd, &from,
+ sizeof(from), secure, &pr_clonefd)) == SLAPD_INVALID_SOCKET ) {
+ return -1;
+ }
+
+ /* get a new Connection from the Connection Table */
+ conn= connection_table_get_connection(ct,ns);
+ if(conn==NULL)
+ {
+ PR_Close(pr_acceptfd);
+ return -1;
+ }
+ PR_Lock( conn->c_mutex );
+
+#if !defined( XP_WIN32 )
+ ber_sockbuf_set_option(conn->c_sb,LBER_SOCKBUF_OPT_DESC,&pr_clonefd);
+#else
+ if( !secure )
+ ber_sockbuf_set_option(conn->c_sb,LBER_SOCKBUF_OPT_DESC,&ns);
+ else
+ ber_sockbuf_set_option(conn->c_sb,LBER_SOCKBUF_OPT_DESC,&pr_clonefd);
+#endif
+
+ conn->c_sd = ns;
+ conn->c_prfd = pr_clonefd;
+ conn->c_flags &= ~CONN_FLAG_CLOSING;
+
+ /* Store the fact that this new connection is an SSL connection */
+ if (secure) {
+ conn->c_flags |= CONN_FLAG_SSL;
+ }
+
+#ifndef _WIN32
+ /*
+ * clear the "returned events" field in ns' slot within the poll fds
+ * array so that handle_read_ready() doesn't look at out_flags for an
+ * old connection by mistake and do something bad such as close the
+ * connection we just accepted.
+ */
+
+ /* Dont have to worry about this now because of our mapping from
+ * the connection table to the fds array. This new connection
+ * won't have a mapping. */
+ /* fds[ns].out_flags = 0; */
+#endif
+
+ if (secure) {
+ /*structure added to enable 64bit support changed from
+ *the commented code that follows each of the next two
+ *blocks of code
+ */
+ struct lber_x_ext_io_fns *func_pointers = malloc(LBER_X_EXTIO_FNS_SIZE);
+ func_pointers->lbextiofn_size = LBER_X_EXTIO_FNS_SIZE;
+ func_pointers->lbextiofn_read = secure_read_function;
+ func_pointers->lbextiofn_write = secure_write_function;
+ func_pointers->lbextiofn_writev = NULL;
+ func_pointers->lbextiofn_socket_arg = (struct lextiof_socket_private *) pr_clonefd;
+ ber_sockbuf_set_option( conn->c_sb,
+ LBER_SOCKBUF_OPT_EXT_IO_FNS, func_pointers);
+
+ /* changed here by Cheston
+ ber_sockbuf_set_option( conn->c_sb,
+ LBER_SOCKBUF_OPT_READ_FN, (void *)secure_read_function );
+ ber_sockbuf_set_option( conn->c_sb,
+ LBER_SOCKBUF_OPT_WRITE_FN, (void *)secure_write_function );
+ */
+ } else {
+ struct lber_x_ext_io_fns *func_pointers = malloc(LBER_X_EXTIO_FNS_SIZE);
+ func_pointers->lbextiofn_size = LBER_X_EXTIO_FNS_SIZE;
+ func_pointers->lbextiofn_read = read_function;
+ func_pointers->lbextiofn_write = write_function;
+ func_pointers->lbextiofn_writev = NULL;
+#ifdef _WIN32
+ func_pointers->lbextiofn_socket_arg = (struct lextiof_socket_private *) ns;
+#else
+ func_pointers->lbextiofn_socket_arg = (struct lextiof_socket_private *) pr_clonefd;
+#endif
+ ber_sockbuf_set_option( conn->c_sb,
+ LBER_SOCKBUF_OPT_EXT_IO_FNS, func_pointers);
+ /*
+ ber_sockbuf_set_option( conn->c_sb,
+ LBER_SOCKBUF_OPT_READ_FN, (void *)read_function );
+ ber_sockbuf_set_option( conn->c_sb,
+ LBER_SOCKBUF_OPT_WRITE_FN, (void *)write_function );
+ */
+ }
+
+#if defined(NET_SSL)
+ if( secure && config_get_SSLclientAuth() != SLAPD_SSLCLIENTAUTH_OFF ) {
+ /* Prepare to handle the client's certificate (if any): */
+ int rv;
+
+ rv = slapd_ssl_handshakeCallback (conn->c_prfd, (void*)handle_handshake_done, conn);
+
+ if (rv < 0) {
+ PRErrorCode prerr = PR_GetError();
+ LDAPDebug (LDAP_DEBUG_ANY, "SSL_HandshakeCallback() %d "
+ SLAPI_COMPONENT_NAME_NSPR " error %d (%s)\n",
+ rv, prerr, slapd_pr_strerror( prerr ));
+ }
+ rv = slapd_ssl_badCertHook (conn->c_prfd, (void*)handle_bad_certificate, conn);
+
+ if (rv < 0) {
+ PRErrorCode prerr = PR_GetError();
+ LDAPDebug (LDAP_DEBUG_ANY, "SSL_BadCertHook(%i) %i "
+ SLAPI_COMPONENT_NAME_NSPR " error %d\n",
+ conn->c_sd, rv, prerr);
+ }
+ }
+#endif
+
+ connection_reset(conn, ns, &from, sizeof(from), secure);
+
+ /* Call the plugin extension constructors */
+ conn->c_extension = factory_create_extension(connection_type,conn,NULL /* Parent */);
+
+
+ /* Add this connection slot to the doubly linked list of active connections. This
+ * list is used to find the connections that should be used in the poll call. This
+ * connection will be added directly after slot 0 which serves as the head of the list */
+ if ( conn != NULL && conn->c_next == NULL && conn->c_prev == NULL )
+ {
+ /* Now give the new connection to the connection code */
+ connection_table_move_connection_on_to_active_list(the_connection_table,conn);
+ }
+
+ PR_Unlock( conn->c_mutex );
+
+ connection_new_private(conn);
+
+ g_increment_current_conn_count();
+
+ return 0;
+}
+
+static int init_shutdown_detect()
+{
+
+#ifdef _WIN32
+ PRThread *service_exit_wait_tid;
+#else
+ /* First of all, we must reset the signal mask to get rid of any blockages
+ * the process may have inherited from its parent (such as the console), which
+ * might result in the process not delivering those blocked signals, and thus,
+ * misbehaving....
+ */
+ {
+ int rc;
+ sigset_t proc_mask;
+
+ LDAPDebug( LDAP_DEBUG_TRACE, "Reseting signal mask....\n", 0, 0, 0);
+ (void)sigemptyset( &proc_mask );
+ rc = pthread_sigmask( SIG_SETMASK, &proc_mask, NULL );
+ LDAPDebug( LDAP_DEBUG_TRACE, " %s \n",
+ rc ? "Failed to reset signal mask":"....Done (signal mask reset)!!", 0, 0 );
+ }
+#endif
+
+#ifdef _WIN32
+
+ /* Create a thread to wait on the Win32 event which will
+ be signalled by the watchdog when the Service is
+ being halted. */
+ service_exit_wait_tid = PR_CreateThread( PR_USER_THREAD,
+ (VFP) (void *) slapd_service_exit_wait, (void *) NULL,
+ PR_PRIORITY_NORMAL, PR_GLOBAL_THREAD, PR_UNJOINABLE_THREAD,
+ SLAPD_DEFAULT_THREAD_STACKSIZE);
+ if( service_exit_wait_tid == NULL ) {
+ LDAPDebug( LDAP_DEBUG_ANY,
+ "Error: PR_CreateThread(slapd_service_exit_wait) failed\n", 0, 0, 0 );
+ }
+#elif defined ( HPUX10 )
+ PR_CreateThread ( PR_USER_THREAD,
+ catch_signals,
+ NULL,
+ PR_PRIORITY_NORMAL,
+ PR_GLOBAL_THREAD,
+ PR_UNJOINABLE_THREAD,
+ SLAPD_DEFAULT_THREAD_STACKSIZE);
+#else
+#ifdef HPUX11
+ /* In the optimized builds for HPUX, the signal handler doesn't seem
+ * to get set correctly unless the primordial thread gets a chance
+ * to run before we make the call to SIGNAL. (At this point the
+ * the primordial thread has spawned the daemon thread which called
+ * this function.) The call to DS_Sleep will give the primordial
+ * thread a chance to run.
+ */
+ DS_Sleep(0);
+#endif
+ (void) SIGNAL( SIGPIPE, SIG_IGN );
+ (void) SIGNAL( SIGCHLD, slapd_wait4child );
+#ifndef LINUX
+ /* linux uses USR1/USR2 for thread synchronization, so we aren't
+ * allowed to mess with those.
+ */
+ (void) SIGNAL( SIGUSR1, slapd_do_nothing );
+ (void) SIGNAL( SIGUSR2, set_shutdown );
+#endif
+ (void) SIGNAL( SIGTERM, set_shutdown );
+ (void) SIGNAL( SIGHUP, set_shutdown );
+#endif /* _WIN32 */
+ return 0;
+}
+
+#if defined( XP_WIN32 )
+static void
+unfurl_banners(Connection_Table *ct,daemon_ports_t *ports, int n_tcps, PRFileDesc *s_tcps)
+#else
+static void
+unfurl_banners(Connection_Table *ct,daemon_ports_t *ports, PRFileDesc *n_tcps, PRFileDesc *s_tcps)
+#endif
+{
+ slapdFrontendConfig_t *slapdFrontendConfig = getFrontendConfig();
+ char addrbuf[ 256 ];
+
+ if ( ct->size <= slapdFrontendConfig->reservedescriptors ) {
+#ifdef _WIN32
+ LDAPDebug( LDAP_DEBUG_ANY,
+ "ERROR: Not enough descriptors to accept any connections. "
+ "This may be because the maxdescriptors configuration "
+ "directive is too small, or the reservedescriptors "
+ "configuration directive is too large. "
+ "Try increasing the number of descriptors available to "
+ "the slapd process. The current value is %d. %d "
+ "descriptors are currently reserved for internal "
+ "slapd use, so the total number of descriptors available "
+ "to the process must be greater than %d.\n",
+ ct->size, slapdFrontendConfig->reservedescriptors, slapdFrontendConfig->reservedescriptors );
+#else /* _WIN32 */
+ LDAPDebug( LDAP_DEBUG_ANY,
+ "ERROR: Not enough descriptors to accept any connections. "
+ "This may be because the maxdescriptors configuration "
+ "directive is too small, the hard limit on descriptors is "
+ "too small (see limit(1)), or the reservedescriptors "
+ "configuration directive is too large. "
+ "Try increasing the number of descriptors available to "
+ "the slapd process. The current value is %d. %d "
+ "descriptors are currently reserved for internal "
+ "slapd use, so the total number of descriptors available "
+ "to the process must be greater than %d.\n",
+ ct->size, slapdFrontendConfig->reservedescriptors, slapdFrontendConfig->reservedescriptors );
+#endif /* _WIN32 */
+ exit( 1 );
+ }
+
+ /*
+ * This final startup message gives a definite signal to the admin
+ * program that the server is up. It must contain the string
+ * "slapd started." because some of the administrative programs
+ * depend on this. See ldap/admin/lib/dsalib_updown.c.
+ */
+#if !defined( XP_WIN32 )
+ if ( n_tcps != NULL ) { /* standard LDAP */
+#else
+ if ( n_tcps != SLAPD_INVALID_SOCKET ) { /* standard LDAP */
+#endif
+
+ LDAPDebug( LDAP_DEBUG_ANY,
+ "slapd started. Listening on %s port %d for LDAP requests\n",
+ netaddr2string(&ports->n_listenaddr, addrbuf, sizeof(addrbuf)),
+ ports->n_port, 0 );
+ }
+
+ if ( s_tcps != NULL ) { /* LDAP over SSL; separate port */
+ LDAPDebug( LDAP_DEBUG_ANY,
+ "Listening on %s port %d for LDAPS requests\n",
+ netaddr2string(&ports->s_listenaddr, addrbuf, sizeof(addrbuf)),
+ ports->s_port, 0 );
+ }
+}
+
+#if defined( _WIN32 )
+/* On Windows, we signal the SCM when we're ready to accept connections */
+static int
+write_pid_file()
+{
+ if( SlapdIsAService() )
+ {
+ /* Initialization complete and successful. Set service to running */
+ LDAPServerStatus.dwCurrentState = SERVICE_RUNNING;
+ LDAPServerStatus.dwCheckPoint = 0;
+ LDAPServerStatus.dwWaitHint = 0;
+
+ if (!SetServiceStatus(hLDAPServerServiceStatus, &LDAPServerStatus)) {
+ ReportSlapdEvent(EVENTLOG_INFORMATION_TYPE, MSG_SERVER_START_FAILED, 1,
+ "Could not set Service status.");
+ exit(1);
+ }
+ }
+
+ ReportSlapdEvent(EVENTLOG_INFORMATION_TYPE, MSG_SERVER_STARTED, 0, NULL );
+ return 0;
+}
+#else /* WIN32 */
+/* On UNIX, we create a file with our PID in it */
+static int
+write_pid_file()
+{
+ FILE *fp = NULL;
+ /*
+ * The following section of code is closely coupled with the
+ * admin programs. Please do not make changes here without
+ * consulting the start/stop code for the admin code.
+ */
+ if ( (fp = fopen( get_pid_file(), "w" )) != NULL ) {
+ fprintf( fp, "%d\n", getpid() );
+ fclose( fp );
+ return 0;
+ } else
+ {
+ return -1;
+ }
+}
+#endif /* WIN32 */
+
+static void
+set_shutdown (int sig)
+{
+ /* don't log anything from a signal handler:
+ * you could be holding a lock when the signal was trapped. more
+ * specifically, you could be holding the logfile lock (and deadlock
+ * yourself).
+ */
+#if 0
+ LDAPDebug( LDAP_DEBUG_ANY, "slapd got shutdown signal\n", 0, 0, 0 );
+#endif
+ g_set_shutdown( SLAPI_SHUTDOWN_SIGNAL );
+#ifndef _WIN32
+#ifndef LINUX
+ /* don't mess with USR1/USR2 on linux, used by libpthread */
+ (void) SIGNAL( SIGUSR2, set_shutdown );
+#endif
+ (void) SIGNAL( SIGTERM, set_shutdown );
+ (void) SIGNAL( SIGHUP, set_shutdown );
+#endif
+}
+
+#ifndef LINUX
+void
+slapd_do_nothing (int sig)
+{
+ /* don't log anything from a signal handler:
+ * you could be holding a lock when the signal was trapped. more
+ * specifically, you could be holding the logfile lock (and deadlock
+ * yourself).
+ */
+#if 0
+ LDAPDebug( LDAP_DEBUG_TRACE, "slapd got SIGUSR1\n", 0, 0, 0 );
+#endif
+#ifndef _WIN32
+ (void) SIGNAL( SIGUSR1, slapd_do_nothing );
+#endif
+
+#if 0
+ /*
+ * Actually do a little more: dump the conn struct and
+ * send it to a tmp file
+ */
+ connection_table_dump(connection_table);
+#endif
+}
+#endif /* LINUX */
+
+#ifndef _WIN32
+void
+slapd_wait4child(int sig)
+{
+ WAITSTATUSTYPE status;
+
+ /* don't log anything from a signal handler:
+ * you could be holding a lock when the signal was trapped. more
+ * specifically, you could be holding the logfile lock (and deadlock
+ * yourself).
+ */
+#if 0
+ LDAPDebug( LDAP_DEBUG_ARGS, "listener: catching SIGCHLD\n", 0, 0, 0 );
+#endif
+#ifdef USE_WAITPID
+ while (waitpid ((pid_t) -1, 0, WAIT_FLAGS) > 0)
+#else /* USE_WAITPID */
+ while ( wait3( &status, WAIT_FLAGS, 0 ) > 0 )
+#endif /* USE_WAITPID */
+ ; /* NULL */
+
+ (void) SIGNAL( SIGCHLD, slapd_wait4child );
+}
+#endif
+
+#ifdef XP_WIN32
+static int
+createlistensocket(unsigned short port, const PRNetAddr *listenaddr)
+{
+ int tcps;
+ struct sockaddr_in addr;
+ char *logname = "createlistensocket";
+ char addrbuf[ 256 ];
+
+ if (!port) goto suppressed;
+
+ PR_ASSERT( listenaddr != NULL );
+
+ /* create TCP socket */
+ if ((tcps = socket(AF_INET, SOCK_STREAM, 0))
+ == SLAPD_INVALID_SOCKET) {
+ int oserr = errno;
+
+ slapi_log_error(SLAPI_LOG_FATAL, logname,
+ "socket() failed: OS error %d (%s)\n",
+ oserr, slapd_system_strerror( oserr ));
+ goto failed;
+ }
+
+ /* initialize listener address */
+ (void) memset( (void *) &addr, '\0', sizeof(addr) );
+ addr.sin_family = AF_INET;
+ addr.sin_port = htons( port );
+ if (listenaddr->raw.family == PR_AF_INET) {
+ addr.sin_addr.s_addr = listenaddr->inet.ip;
+ } else if (PR_IsNetAddrType(listenaddr,PR_IpAddrAny)) {
+ addr.sin_addr.s_addr = INADDR_ANY;
+ } else {
+ if (!PR_IsNetAddrType(listenaddr,PR_IpAddrV4Mapped)) {
+ /*
+ * When Win32 supports IPv6, we will be able to use IPv6
+ * addresses here. But not yet.
+ */
+ slapi_log_error(SLAPI_LOG_FATAL, logname,
+ "unable to listen on %s port %d (IPv6 addresses "
+ "are not supported on this platform)\n",
+ netaddr2string(listenaddr, addrbuf, sizeof(addrbuf)),
+ port );
+ goto failed;
+ }
+
+ addr.sin_addr.s_addr = listenaddr->ipv6.ip.pr_s6_addr32[3];
+ }
+
+ LDAPDebug( LDAP_DEBUG_TRACE, "%s - binding to %s:%d\n",
+ logname, inet_ntoa( addr.sin_addr ), port )
+
+ if ( bind( tcps, (struct sockaddr *) &addr, sizeof(addr) ) == -1 ) {
+ int oserr = errno;
+
+ slapi_log_error(SLAPI_LOG_FATAL, logname,
+ "bind() on %s port %d failed: OS error %d (%s)\n",
+ inet_ntoa( addr.sin_addr ), port, oserr,
+ slapd_system_strerror( oserr ));
+ goto failed;
+ }
+
+ return tcps;
+
+failed:
+ WSACleanup();
+ exit( 1 );
+suppressed:
+ return -1;
+} /* createlistensocket */
+#endif /* XP_WIN32 */
+
+
+static PRFileDesc *
+createprlistensocket(unsigned short port, const PRNetAddr *listenaddr,
+ int secure)
+{
+ PRFileDesc *sock;
+ PRNetAddr sa_server;
+ PRErrorCode prerr = 0;
+ PRSocketOptionData pr_socketoption;
+ char addrbuf[ 256 ];
+ char *logname = "createprlistensocket";
+
+ if (!port) goto suppressed;
+
+ PR_ASSERT( listenaddr != NULL );
+
+ /* create TCP socket */
+ if ((sock = PR_OpenTCPSocket(PR_AF_INET6)) == SLAPD_INVALID_SOCKET) {
+ prerr = PR_GetError();
+ slapi_log_error(SLAPI_LOG_FATAL, logname,
+ "PR_OpenTCPSocket(PR_AF_INET6) failed: %s error %d (%s)\n",
+ SLAPI_COMPONENT_NAME_NSPR, prerr, slapd_pr_strerror(prerr));
+ goto failed;
+ }
+
+ pr_socketoption.option = PR_SockOpt_Reuseaddr;
+ pr_socketoption.value.reuse_addr = 1;
+ if ( PR_SetSocketOption(sock, &pr_socketoption ) == PR_FAILURE) {
+ prerr = PR_GetError();
+ slapi_log_error(SLAPI_LOG_FATAL, logname,
+ "PR_SetSocketOption(PR_SockOpt_Reuseaddr) failed: %s error %d (%s)\n",
+ SLAPI_COMPONENT_NAME_NSPR, prerr, slapd_pr_strerror( prerr ));
+ goto failed;
+ }
+
+ /* set up listener address, including port */
+ memcpy(&sa_server, listenaddr, sizeof(sa_server));
+ if ( PR_SetNetAddr(PR_IpAddrNull, PR_AF_INET6, port, &sa_server)
+ != PR_SUCCESS ) {
+ prerr = PR_GetError();
+ slapi_log_error(SLAPI_LOG_FATAL, logname,
+ "PR_SetNetAddr() failed: %s error %d (%s)\n",
+ SLAPI_COMPONENT_NAME_NSPR,
+ prerr, slapd_pr_strerror(prerr));
+ goto failed;
+ }
+
+ if ( PR_Bind(sock, &sa_server) == PR_FAILURE) {
+ prerr = PR_GetError();
+ slapi_log_error(SLAPI_LOG_FATAL, logname,
+ "PR_Bind() on %s port %d failed: %s error %d (%s)\n",
+ netaddr2string(&sa_server, addrbuf, sizeof(addrbuf)), port,
+ SLAPI_COMPONENT_NAME_NSPR, prerr, slapd_pr_strerror(prerr));
+ goto failed;
+ }
+
+ return( sock );
+
+failed:
+#ifdef XP_WIN32
+ WSACleanup();
+#endif /* XP_WIN32 */
+ exit( 1 );
+
+suppressed:
+ return (PRFileDesc *)-1;
+} /* createprlistensocket */
+
+
+/*
+ * Initialize the *addr structure based on listenhost.
+ * Returns: 0 if successful and -1 if not (after logging an error message).
+ */
+int
+slapd_listenhost2addr(const char *listenhost, PRNetAddr *addr)
+{
+ char *logname = "slapd_listenhost2addr";
+ PRErrorCode prerr = 0;
+ PRHostEnt hent;
+ char hbuf[ PR_NETDB_BUF_SIZE ];
+
+ PR_ASSERT( addr != NULL );
+
+ if (NULL == listenhost) {
+ /* listen on all interfaces */
+ if ( PR_SUCCESS != PR_SetNetAddr(PR_IpAddrAny, PR_AF_INET6, 0, addr)) {
+ prerr = PR_GetError();
+ slapi_log_error( SLAPI_LOG_FATAL, logname,
+ "PR_SetNetAddr(PR_IpAddrAny) failed - %s error %d (%s)\n",
+ SLAPI_COMPONENT_NAME_NSPR, prerr, slapd_pr_strerror(prerr));
+ goto failed;
+ }
+ } else if (PR_SUCCESS == PR_StringToNetAddr(listenhost, addr)) {
+ if (PR_AF_INET == PR_NetAddrFamily(addr)) {
+ PRUint32 ipv4ip = addr->inet.ip;
+ memset(addr, 0, sizeof(PRNetAddr));
+ PR_ConvertIPv4AddrToIPv6(ipv4ip, &addr->ipv6.ip);
+ addr->ipv6.family = PR_AF_INET6;
+ }
+ } else if (PR_SUCCESS == PR_GetIPNodeByName(listenhost,
+ PR_AF_INET6, PR_AI_DEFAULT | PR_AI_ALL,
+ hbuf, sizeof(hbuf), &hent )) {
+ /* just use the first IP address returned */
+ if (PR_EnumerateHostEnt(0, &hent, 0, addr) < 0) {
+ slapi_log_error( SLAPI_LOG_FATAL, logname,
+ "PR_EnumerateHostEnt() failed - %s error %d (%s)\n",
+ SLAPI_COMPONENT_NAME_NSPR, prerr, slapd_pr_strerror(prerr));
+ goto failed;
+ }
+ } else { /* failure */
+ slapi_log_error( SLAPI_LOG_FATAL, logname,
+ "PR_GetIPNodeByName(%s) failed - %s error %d (%s)\n",
+ listenhost, SLAPI_COMPONENT_NAME_NSPR, prerr,
+ slapd_pr_strerror(prerr));
+ goto failed;
+ }
+
+ return( 0 );
+
+failed:
+ return( -1 );
+}
+
+
+/*
+ * Map addr to a string equivalent and place the result in addrbuf.
+ */
+static const char *
+netaddr2string(const PRNetAddr *addr, char *addrbuf, size_t addrbuflen)
+{
+ const char *retstr;
+
+ if (NULL == addr || PR_IsNetAddrType(addr, PR_IpAddrAny)) {
+ retstr = "All Interfaces";
+ } else if (PR_IsNetAddrType(addr, PR_IpAddrLoopback)) {
+ if ( addr->raw.family == PR_AF_INET6 &&
+ !PR_IsNetAddrType(addr, PR_IpAddrV4Mapped)) {
+ retstr = "IPv6 Loopback";
+ } else {
+ retstr = "Loopback";
+ }
+ } else if (PR_SUCCESS == PR_NetAddrToString( addr, addrbuf, addrbuflen)) {
+ if (0 == strncmp( addrbuf, "::ffff:", 7 )) {
+ /* IPv4 address mapped into IPv6 address space */
+ retstr = addrbuf + 7;
+ } else {
+ /* full blown IPv6 address */
+ retstr = addrbuf;
+ }
+ } else { /* punt */
+ retstr = "address conversion failed";
+ }
+
+ return(retstr);
+}
+
+
+static int
+createsignalpipe( void )
+{
+#if defined( _WIN32 )
+ if ( PR_NewTCPSocketPair(&signalpipe[0])) {
+ PRErrorCode prerr = PR_GetError();
+ LDAPDebug( LDAP_DEBUG_ANY, "PR_CreatePipe() failed, "
+ SLAPI_COMPONENT_NAME_NSPR " error %d (%s)\n",
+ prerr, slapd_pr_strerror(prerr), SLAPD_DEFAULT_THREAD_STACKSIZE );
+ return( -1 );
+ }
+ writesignalpipe = PR_FileDesc2NativeHandle(signalpipe[1]);
+ readsignalpipe = PR_FileDesc2NativeHandle(signalpipe[0]);
+#else
+ if ( PR_CreatePipe( &signalpipe[0], &signalpipe[1] ) != 0 ) {
+ PRErrorCode prerr = PR_GetError();
+ LDAPDebug( LDAP_DEBUG_ANY, "PR_CreatePipe() failed, "
+ SLAPI_COMPONENT_NAME_NSPR " error %d (%s)\n",
+ prerr, slapd_pr_strerror(prerr), SLAPD_DEFAULT_THREAD_STACKSIZE );
+ return( -1 );
+ }
+ writesignalpipe = PR_FileDesc2NativeHandle(signalpipe[1]);
+ readsignalpipe = PR_FileDesc2NativeHandle(signalpipe[0]);
+ fcntl(writesignalpipe, F_SETFD, O_NONBLOCK);
+ fcntl(readsignalpipe, F_SETFD, O_NONBLOCK);
+#endif
+
+ return( 0 );
+}
+
+
+#ifdef HPUX10
+#include <pthread.h> /* for sigwait */
+/*
+ * Set up a thread to catch signals
+ * SIGUSR1 (ignore), SIGCHLD (call slapd_wait4child),
+ * SIGUSR2 (set slapd_shutdown), SIGTERM (set slapd_shutdown),
+ * SIGHUP (set slapd_shutdown)
+ */
+static void *
+catch_signals()
+{
+ sigset_t caught_signals;
+ int sig;
+
+ sigemptyset( &caught_signals );
+
+ while ( !g_get_shutdown() ) {
+
+ /* Set the signals we're interested in catching */
+ sigaddset( &caught_signals, SIGUSR1 );
+ sigaddset( &caught_signals, SIGCHLD );
+ sigaddset( &caught_signals, SIGUSR2 );
+ sigaddset( &caught_signals, SIGTERM );
+ sigaddset( &caught_signals, SIGHUP );
+
+ (void)sigprocmask( SIG_BLOCK, &caught_signals, NULL );
+
+ if (( sig = sigwait( &caught_signals )) < 0 ) {
+ LDAPDebug( LDAP_DEBUG_ANY, "catch_signals: sigwait returned -1\n",
+ 0, 0, 0 );
+ continue;
+ } else {
+ LDAPDebug( LDAP_DEBUG_TRACE, "catch_signals: detected signal %d\n",
+ sig, 0, 0 );
+ switch ( sig ) {
+ case SIGUSR1:
+ continue; /* ignore SIGUSR1 */
+ case SIGUSR2: /* fallthrough */
+ case SIGTERM: /* fallthrough */
+ case SIGHUP:
+ g_set_shutdown( SLAPI_SHUTDOWN_SIGNAL );
+ return NULL;
+ case SIGCHLD:
+ slapd_wait4child( sig );
+ break;
+ default:
+ LDAPDebug( LDAP_DEBUG_ANY,
+ "catch_signals: unknown signal (%d) received\n",
+ sig, 0, 0 );
+ }
+ }
+ }
+}
+#endif /* HPUX */
+
+static int
+get_configured_connection_table_size()
+{
+ int size;
+ size = config_get_conntablesize();
+
+/*
+ * Cap the table size at nsslapd-maxdescriptors.
+ */
+#if !defined(_WIN32) && !defined(AIX)
+ {
+ int maxdesc = config_get_maxdescriptors();
+
+ if ( maxdesc >= 0 && size > maxdesc ) {
+ size = maxdesc;
+ }
+ }
+#endif
+
+ return size;
+}
+
+
+
+
+PRFileDesc * get_ssl_listener_fd()
+{
+ PRFileDesc * listener;
+
+ listener = the_connection_table->fd[FDS_S_TCPS].fd;
+
+ return listener;
+}
+
+
+
+int configure_pr_socket( PRFileDesc **pr_socket, int secure )
+{
+ int ns = 0;
+ int reservedescriptors = config_get_reservedescriptors();
+ int ioblock_timeout = config_get_ioblocktimeout();
+ int enable_nagle = config_get_nagle();
+
+ PRSocketOptionData pr_socketoption;
+
+#if defined(LINUX)
+ /* On Linux we use TCP_CORK so we must enable nagle */
+ enable_nagle = 1;
+#endif
+
+ ns = PR_FileDesc2NativeHandle( *pr_socket );
+
+#if !defined(_WIN32)
+ /*
+ * Some OS or third party libraries may require that low
+ * numbered file descriptors be available, e.g., the DNS resolver
+ * library on most operating systems. Therefore, we try to
+ * replace the file descriptor returned by accept() with a
+ * higher numbered one. If this fails, we log an error and
+ * continue (not considered a truly fatal error).
+ */
+ if ( reservedescriptors > 0 && ns < reservedescriptors ) {
+ int newfd = fcntl( ns, F_DUPFD, reservedescriptors );
+
+ if ( newfd > 0 ) {
+ PRFileDesc *nspr_layer_fd = PR_GetIdentitiesLayer( *pr_socket,
+ PR_NSPR_IO_LAYER );
+ if ( NULL == nspr_layer_fd ) {
+ slapi_log_error( SLAPI_LOG_FATAL, "configure_pr_socket",
+ "Unable to move socket file descriptor %d above %d:"
+ " PR_GetIdentitiesLayer( 0x%x, PR_NSPR_IO_LAYER )"
+ " failed\n", ns, reservedescriptors, *pr_socket );
+ close( newfd ); /* can't fix things up in NSPR -- close copy */
+ } else {
+ PR_ChangeFileDescNativeHandle( nspr_layer_fd, newfd );
+ close( ns ); /* dup succeeded -- close the original */
+ ns = newfd;
+ }
+ } else {
+ int oserr = errno;
+ slapi_log_error(SLAPI_LOG_FATAL, "configure_pr_socket",
+ "Unable to move socket file descriptor %d above %d:"
+ " OS error %d (%s)\n", ns, reservedescriptors, oserr,
+ slapd_system_strerror( oserr ) );
+ }
+ }
+#endif /* !_WIN32 */
+
+ if ( secure ) {
+
+ pr_socketoption.option = PR_SockOpt_Nonblocking;
+ pr_socketoption.value.non_blocking = 0;
+ if ( PR_SetSocketOption( *pr_socket, &pr_socketoption ) == PR_FAILURE ) {
+ PRErrorCode prerr = PR_GetError();
+ LDAPDebug( LDAP_DEBUG_ANY,
+ "PR_SetSocketOption(PR_SockOpt_Nonblocking) failed, "
+ SLAPI_COMPONENT_NAME_NSPR " error %d (%s)\n",
+ prerr, slapd_pr_strerror(prerr), 0 );
+ }
+ } else {
+ /* We always want to have non-blocking I/O */
+ pr_socketoption.option = PR_SockOpt_Nonblocking;
+ pr_socketoption.value.non_blocking = 1;
+ if ( PR_SetSocketOption( *pr_socket, &pr_socketoption ) == PR_FAILURE ) {
+ PRErrorCode prerr = PR_GetError();
+ LDAPDebug( LDAP_DEBUG_ANY,
+ "PR_SetSocketOption(PR_SockOpt_Nonblocking) failed, "
+ SLAPI_COMPONENT_NAME_NSPR " error %d (%s)\n",
+ prerr, slapd_pr_strerror(prerr), 0 );
+ }
+
+ if ( have_send_timeouts ) {
+ daemon_configure_send_timeout(ns,config_get_ioblocktimeout());
+ }
+
+ } /* else (secure) */
+
+
+ if ( !enable_nagle ) {
+
+ pr_socketoption.option = PR_SockOpt_NoDelay;
+ pr_socketoption.value.no_delay = 1;
+ if ( PR_SetSocketOption( *pr_socket, &pr_socketoption ) == PR_FAILURE) {
+ PRErrorCode prerr = PR_GetError();
+ LDAPDebug( LDAP_DEBUG_ANY,
+ "PR_SetSocketOption(PR_SockOpt_NoDelay) failed, "
+ SLAPI_COMPONENT_NAME_NSPR " error %d (%s)\n",
+ prerr, slapd_pr_strerror( prerr ), 0 );
+ }
+ } else {
+ pr_socketoption.option = PR_SockOpt_NoDelay;
+ pr_socketoption.value.no_delay = 0;
+ if ( PR_SetSocketOption( *pr_socket, &pr_socketoption ) == PR_FAILURE) {
+ PRErrorCode prerr = PR_GetError();
+ LDAPDebug( LDAP_DEBUG_ANY,
+ "PR_SetSocketOption(PR_SockOpt_NoDelay) failed, "
+ SLAPI_COMPONENT_NAME_NSPR " error %d (%s)\n",
+ prerr, slapd_pr_strerror( prerr ), 0 );
+ }
+ } /* else (!enable_nagle) */
+
+
+ return ns;
+
+}
+
+
+
+
+void configure_ns_socket( int * ns )
+{
+
+ int ioblock_timeout = config_get_ioblocktimeout();
+ int enable_nagle = config_get_nagle();
+ int on;
+
+#if defined(LINUX)
+ /* On Linux we use TCP_CORK so we must enable nagle */
+ enable_nagle = 1;
+#endif
+
+ if ( have_send_timeouts ) {
+ daemon_configure_send_timeout( *ns, config_get_ioblocktimeout() );
+ }
+
+
+ if ( !enable_nagle ) {
+ on = 1;
+ setsockopt( *ns, IPPROTO_TCP, TCP_NODELAY, (char * ) &on, sizeof(on) );
+ } else {
+ on = 0;
+ setsockopt( *ns, IPPROTO_TCP, TCP_NODELAY, (char * ) &on, sizeof(on) );
+ } /* else (!enable_nagle) */
+
+
+ return;
+
+}
+
+
+#ifdef RESOLVER_NEEDS_LOW_FILE_DESCRIPTORS
+/*
+ * A function that uses the DNS resolver in a simple way. This is only
+ * used to ensure that the DNS resolver has opened its files, etc.
+ * using low numbered file descriptors.
+ */
+static void
+get_loopback_by_addr( void )
+{
+#ifdef GETHOSTBYADDR_BUF_T
+ struct hostent hp;
+ GETHOSTBYADDR_BUF_T hbuf;
+#endif
+ unsigned long ipaddr;
+ struct in_addr ia;
+ int herrno, rc = 0;
+
+ memset( (char *)&hp, 0, sizeof(hp));
+ ipaddr = htonl( INADDR_LOOPBACK );
+ (void) GETHOSTBYADDR( (char *)&ipaddr, sizeof( ipaddr ),
+ AF_INET, &hp, hbuf, sizeof(hbuf), &herrno );
+}
+#endif /* RESOLVER_NEEDS_LOW_FILE_DESCRIPTORS */
diff --git a/ldap/servers/slapd/defbackend.c b/ldap/servers/slapd/defbackend.c
new file mode 100644
index 00000000..abd76d88
--- /dev/null
+++ b/ldap/servers/slapd/defbackend.c
@@ -0,0 +1,200 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+/*
+ * defbackend.c - implement a "backend of last resort" which is used only
+ * when a request's basedn does not match one of the suffixes of any of the
+ * configured backends.
+ *
+ */
+
+#include "slap.h"
+
+/*
+ * ---------------- Macros ---------------------------------------------------
+ */
+#define DEFBACKEND_TYPE "default"
+
+#define DEFBACKEND_OP_NOT_HANDLED 0
+#define DEFBACKEND_OP_HANDLED 1
+
+
+/*
+ * ---------------- Static Variables -----------------------------------------
+ */
+static struct slapdplugin defbackend_plugin;
+static Slapi_Backend *defbackend_backend = NULL;
+
+
+/*
+ * ---------------- Prototypes for Private Functions -------------------------
+ */
+static int defbackend_default( Slapi_PBlock *pb );
+static int defbackend_noop( Slapi_PBlock *pb );
+static int defbackend_abandon( Slapi_PBlock *pb );
+static int defbackend_bind( Slapi_PBlock *pb );
+static int defbackend_next_search_entry( Slapi_PBlock *pb );
+
+
+/*
+ * ---------------- Public Functions -----------------------------------------
+ */
+
+/*
+ * defbackend_init: instantiate the default backend
+ */
+void
+defbackend_init( void )
+{
+ int rc;
+ char *errmsg;
+ Slapi_PBlock pb;
+
+ LDAPDebug( LDAP_DEBUG_TRACE, "defbackend_init\n", 0, 0, 0 );
+
+ /*
+ * create a new backend
+ */
+ pblock_init( &pb );
+ defbackend_backend = slapi_be_new( DEFBACKEND_TYPE , DEFBACKEND_TYPE, 1 /* Private */, 0 /* Do Not Log Changes */ );
+ if (( rc = slapi_pblock_set( &pb, SLAPI_BACKEND, defbackend_backend ))
+ != 0 ) {
+ errmsg = "slapi_pblock_set SLAPI_BACKEND failed";
+ goto cleanup_and_return;
+ }
+
+ /*
+ * create a plugin structure for this backend since the
+ * slapi_pblock_set()/slapi_pblock_get() functions assume there is one.
+ */
+ memset( &defbackend_plugin, '\0', sizeof( struct slapdplugin ));
+ defbackend_plugin.plg_type = SLAPI_PLUGIN_DATABASE;
+ defbackend_backend->be_database = &defbackend_plugin;
+ if (( rc = slapi_pblock_set( &pb, SLAPI_PLUGIN, &defbackend_plugin ))
+ != 0 ) {
+ errmsg = "slapi_pblock_set SLAPI_PLUGIN failed";
+ goto cleanup_and_return;
+ }
+
+ /* default backend is managed as if it would */
+ /* contain remote data. */
+ slapi_be_set_flag(defbackend_backend,SLAPI_BE_FLAG_REMOTE_DATA);
+
+ /*
+ * install handler functions, etc.
+ */
+ errmsg = "slapi_pblock_set handlers failed";
+ rc = slapi_pblock_set( &pb, SLAPI_PLUGIN_VERSION,
+ (void *)SLAPI_PLUGIN_CURRENT_VERSION );
+ rc |= slapi_pblock_set( &pb, SLAPI_PLUGIN_DB_BIND_FN,
+ (void *)defbackend_bind );
+ rc |= slapi_pblock_set( &pb, SLAPI_PLUGIN_DB_UNBIND_FN,
+ (void *)defbackend_noop );
+ rc |= slapi_pblock_set( &pb, SLAPI_PLUGIN_DB_SEARCH_FN,
+ (void *)defbackend_default );
+ rc |= slapi_pblock_set( &pb, SLAPI_PLUGIN_DB_NEXT_SEARCH_ENTRY_FN,
+ (void *)defbackend_next_search_entry );
+ rc |= slapi_pblock_set( &pb, SLAPI_PLUGIN_DB_COMPARE_FN,
+ (void *)defbackend_default );
+ rc |= slapi_pblock_set( &pb, SLAPI_PLUGIN_DB_MODIFY_FN,
+ (void *)defbackend_default );
+ rc |= slapi_pblock_set( &pb, SLAPI_PLUGIN_DB_MODRDN_FN,
+ (void *)defbackend_default );
+ rc |= slapi_pblock_set( &pb, SLAPI_PLUGIN_DB_ADD_FN,
+ (void *)defbackend_default );
+ rc |= slapi_pblock_set( &pb, SLAPI_PLUGIN_DB_DELETE_FN,
+ (void *)defbackend_default );
+ rc |= slapi_pblock_set( &pb, SLAPI_PLUGIN_DB_ABANDON_FN,
+ (void *)defbackend_abandon );
+
+cleanup_and_return:
+ if ( rc != 0 ) {
+ LDAPDebug( LDAP_DEBUG_ANY, "defbackend_init: failed (%s)\n",
+ errmsg, 0, 0 );
+ exit( 1 );
+ }
+}
+
+
+/*
+ * defbackend_get_backend: return a pointer to the default backend.
+ * we never return NULL.
+ */
+Slapi_Backend *
+defbackend_get_backend( void )
+{
+ return( defbackend_backend );
+}
+
+
+/*
+ * ---------------- Private Functions ----------------------------------------
+ */
+
+static int
+defbackend_default( Slapi_PBlock *pb )
+{
+ LDAPDebug( LDAP_DEBUG_TRACE, "defbackend_default\n", 0, 0, 0 );
+
+ send_nobackend_ldap_result( pb );
+
+ return( DEFBACKEND_OP_HANDLED );
+}
+
+
+static int
+defbackend_noop( Slapi_PBlock *pb )
+{
+ LDAPDebug( LDAP_DEBUG_TRACE, "defbackend_noop\n", 0, 0, 0 );
+
+ return( DEFBACKEND_OP_HANDLED );
+}
+
+
+static int
+defbackend_abandon( Slapi_PBlock *pb )
+{
+ LDAPDebug( LDAP_DEBUG_TRACE, "defbackend_abandon\n", 0, 0, 0 );
+
+ /* nothing to do */
+ return( DEFBACKEND_OP_HANDLED );
+}
+
+
+static int
+defbackend_bind( Slapi_PBlock *pb )
+{
+ int rc, method;
+ struct berval *cred;
+
+ LDAPDebug( LDAP_DEBUG_TRACE, "defbackend_bind\n", 0, 0, 0 );
+
+ /*
+ * Accept simple binds that do not contain passwords (but do not
+ * update the bind DN field in the connection structure since we don't
+ * grant access based on these "NULL binds")
+ */
+ slapi_pblock_get( pb, SLAPI_BIND_METHOD, &method );
+ slapi_pblock_get( pb, SLAPI_BIND_CREDENTIALS, &cred );
+ if ( method == LDAP_AUTH_SIMPLE && cred->bv_len == 0 ) {
+ PR_AtomicIncrement(g_get_global_snmp_vars()->ops_tbl.dsAnonymousBinds);
+ rc = SLAPI_BIND_ANONYMOUS;
+ } else {
+ send_nobackend_ldap_result( pb );
+ rc = SLAPI_BIND_FAIL;
+ }
+
+ return( rc );
+}
+
+
+
+static int
+defbackend_next_search_entry( Slapi_PBlock *pb )
+{
+ LDAPDebug( LDAP_DEBUG_TRACE, "defbackend_next_search_entry\n", 0, 0, 0 );
+
+ return( 0 ); /* no entries and no error */
+}
diff --git a/ldap/servers/slapd/delete.c b/ldap/servers/slapd/delete.c
new file mode 100644
index 00000000..6e009709
--- /dev/null
+++ b/ldap/servers/slapd/delete.c
@@ -0,0 +1,324 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+/*
+ * 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 <stdio.h>
+#include <string.h>
+#include <sys/types.h>
+#ifndef _WIN32
+#include <sys/socket.h>
+#endif
+#include "slap.h"
+#include "pratom.h"
+
+/* Forward declarations */
+static int delete_internal_pb (Slapi_PBlock *pb);
+static void op_shared_delete (Slapi_PBlock *pb);
+
+/* This function is called to process operation that come over external connections */
+void
+do_delete( Slapi_PBlock *pb )
+{
+ Slapi_Operation *operation;
+ BerElement *ber;
+ char *dn;
+ int err;
+
+ LDAPDebug( LDAP_DEBUG_TRACE, "do_delete\n", 0, 0, 0 );
+
+ slapi_pblock_get( pb, SLAPI_OPERATION, &operation);
+ ber = operation->o_ber;
+
+ /* count the delete request */
+ PR_AtomicIncrement(g_get_global_snmp_vars()->ops_tbl.dsRemoveEntryOps);
+
+ /*
+ * Parse the delete request. It looks like this:
+ *
+ * DelRequest := DistinguishedName
+ */
+
+ if ( ber_scanf( pb->pb_op->o_ber, "a", &dn ) == LBER_ERROR ) {
+ LDAPDebug( LDAP_DEBUG_ANY,
+ "ber_scanf failed (op=Delete; params=DN)\n", 0, 0, 0 );
+ op_shared_log_error_access (pb, "DEL", "???", "decoding error");
+ send_ldap_result( pb, LDAP_PROTOCOL_ERROR, NULL, NULL, 0,
+ NULL );
+ return;
+ }
+
+ /*
+ * in LDAPv3 there can be optional control extensions on
+ * the end of an LDAPMessage. we need to read them in and
+ * pass them to the backend.
+ */
+ if ( (err = get_ldapmessage_controls( pb, ber, NULL )) != 0 ) {
+ op_shared_log_error_access (pb, "DEL", dn, "decoding error");
+ send_ldap_result( pb, err, NULL, NULL, 0, NULL );
+ goto free_and_return;
+ }
+
+ LDAPDebug( LDAP_DEBUG_ARGS, "do_delete: dn (%s)\n", dn, 0, 0 );
+
+ slapi_pblock_set( pb, SLAPI_REQUESTOR_ISROOT, &pb->pb_op->o_isroot );
+ slapi_pblock_set( pb, SLAPI_ORIGINAL_TARGET, dn);
+
+ op_shared_delete (pb);
+
+free_and_return:;
+ slapi_ch_free ((void**)&dn);
+}
+
+/* This function is used to issue internal delete operation
+ This is an old style API. Its use is discoraged because it is not extendable and
+ because it does not allow to check whether plugin has right to access part of the
+ tree it is trying to modify. Use slapi_delete_internal_pb instead */
+Slapi_PBlock *
+slapi_delete_internal(const char *idn, LDAPControl **controls, int dummy)
+{
+ Slapi_PBlock pb;
+ Slapi_PBlock *result_pb;
+ int opresult;
+
+ pblock_init (&pb);
+
+ slapi_delete_internal_set_pb (&pb, idn, controls, NULL, plugin_get_default_component_id(), 0);
+
+ delete_internal_pb (&pb);
+
+ result_pb = slapi_pblock_new();
+ if (result_pb)
+ {
+ slapi_pblock_get(&pb, SLAPI_PLUGIN_INTOP_RESULT, &opresult);
+ slapi_pblock_set(result_pb, SLAPI_PLUGIN_INTOP_RESULT, &opresult);
+ }
+ pblock_done(&pb);
+
+ return result_pb;
+}
+
+/* This is new style API to issue internal delete operation.
+ pblock should contain the following data (can be set via call to slapi_delete_internal_set_pb):
+ For uniqueid based operation:
+ SLAPI_TARGET_DN set to dn that allows to select right backend, can be stale
+ SLAPI_TARGET_UNIQUEID set to the uniqueid of the entry we are looking for
+ SLAPI_CONTROLS_ARG set to request controls if present
+
+ For dn based search:
+ SLAPI_TARGET_DN set to the entry dn
+ SLAPI_CONTROLS_ARG set to request controls if present
+ */
+int slapi_delete_internal_pb (Slapi_PBlock *pb)
+{
+ if (pb == NULL)
+ return -1;
+
+ if (!allow_operation (pb))
+ {
+ slapi_send_ldap_result( pb, LDAP_UNWILLING_TO_PERFORM, NULL,
+ "This plugin is not configured to access operation target data", 0, NULL );
+ return 0;
+ }
+
+ return delete_internal_pb (pb);
+}
+
+/* Initialize a pblock for a call to slapi_delete_internal_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)
+{
+ Operation *op;
+ PR_ASSERT (pb != NULL);
+ if (pb == NULL || dn == NULL)
+ {
+ slapi_log_error(SLAPI_LOG_FATAL, NULL,
+ "slapi_delete_internal_set_pb: NULL parameter\n");
+ return;
+ }
+
+ op = internal_operation_new(SLAPI_OPERATION_DELETE,operation_flags);
+ slapi_pblock_set(pb, SLAPI_OPERATION, op);
+ slapi_pblock_set(pb, SLAPI_ORIGINAL_TARGET, (void*)dn);
+ slapi_pblock_set(pb, SLAPI_CONTROLS_ARG, controls);
+ if (uniqueid)
+ {
+ slapi_pblock_set(pb, SLAPI_TARGET_UNIQUEID, (void*)uniqueid);
+ }
+ slapi_pblock_set(pb, SLAPI_PLUGIN_IDENTITY, plugin_identity);
+}
+
+/* Helper functions */
+
+static int delete_internal_pb (Slapi_PBlock *pb)
+{
+ LDAPControl **controls;
+ Operation *op;
+ int opresult = 0;
+
+ PR_ASSERT (pb != NULL);
+
+ slapi_pblock_get(pb, SLAPI_CONTROLS_ARG, &controls);
+
+ slapi_pblock_get(pb, SLAPI_OPERATION, &op);
+ op->o_handler_data = &opresult;
+ op->o_result_handler = internal_getresult_callback;
+
+ slapi_pblock_set(pb, SLAPI_OPERATION, op);
+ slapi_pblock_set(pb, SLAPI_REQCONTROLS, controls);
+
+ /* set parameters common for all internal operations */
+ set_common_params (pb);
+
+ /* set actions taken to process the operation */
+ set_config_params (pb);
+
+ /* perform delete operation */
+ op_shared_delete (pb);
+
+ slapi_pblock_set(pb, SLAPI_PLUGIN_INTOP_RESULT, &opresult);
+
+ return 0;
+}
+
+static void op_shared_delete (Slapi_PBlock *pb)
+{
+ char *dn;
+ Slapi_Backend *be = NULL;
+ char ebuf[ BUFSIZ ];
+ int internal_op;
+ Slapi_DN sdn;
+ Slapi_Operation *operation;
+ Slapi_Entry *referral;
+ Slapi_Entry *ecopy = NULL;
+ char errorbuf[BUFSIZ];
+ int err;
+
+ slapi_pblock_get(pb, SLAPI_ORIGINAL_TARGET, &dn);
+ slapi_pblock_get(pb, SLAPI_OPERATION, &operation);
+ internal_op= operation_is_flag_set(operation, OP_FLAG_INTERNAL);
+
+ slapi_sdn_init_dn_byref(&sdn,dn);
+ slapi_pblock_set(pb, SLAPI_DELETE_TARGET, (void*)slapi_sdn_get_ndn (&sdn));
+
+ /* target spec is used to decide which plugins are applicable for the operation */
+ operation_set_target_spec (operation, &sdn);
+
+ if (operation_is_flag_set(operation,OP_FLAG_ACTION_LOG_ACCESS))
+ {
+ if (!internal_op )
+ {
+ slapi_log_access(LDAP_DEBUG_STATS, "conn=%d op=%d DEL dn=\"%s\"\n",
+ pb->pb_conn->c_connid,
+ pb->pb_op->o_opid,
+ escape_string(dn, ebuf));
+ }
+ else
+ {
+ slapi_log_access(LDAP_DEBUG_ARGS, "conn=%s op=%d DEL dn=\"%s\"\n",
+ LOG_INTERNAL_OP_CON_ID,
+ LOG_INTERNAL_OP_OP_ID,
+ escape_string(dn, ebuf));
+ }
+ }
+
+ /*
+ * We could be serving multiple database backends. Select the
+ * appropriate one.
+ */
+ if ((err = slapi_mapping_tree_select(pb, &be, &referral, errorbuf)) != LDAP_SUCCESS) {
+ send_ldap_result(pb, err, NULL, errorbuf, 0, NULL);
+ be = NULL;
+ goto free_and_return;
+ }
+
+ if (referral)
+ {
+ int managedsait;
+
+ slapi_pblock_get(pb, SLAPI_MANAGEDSAIT, &managedsait);
+ if (managedsait)
+ {
+ send_ldap_result(pb, LDAP_UNWILLING_TO_PERFORM, NULL,
+ "cannot delete referral", 0, NULL);
+ slapi_entry_free(referral);
+ goto free_and_return;
+ }
+
+ send_referrals_from_entry(pb,referral);
+ slapi_entry_free(referral);
+ goto free_and_return;
+ }
+
+ slapi_pblock_set(pb, SLAPI_BACKEND, be);
+
+ /*
+ * call the pre-delete plugins. if they succeed, call
+ * the backend delete function. then call the
+ * post-delete plugins.
+ */
+ if (plugin_call_plugins(pb, internal_op ? SLAPI_PLUGIN_INTERNAL_PRE_DELETE_FN :
+ SLAPI_PLUGIN_PRE_DELETE_FN) == 0)
+ {
+ int rc;
+
+ slapi_pblock_set(pb, SLAPI_PLUGIN, be->be_database);
+ set_db_default_result_handlers(pb);
+ if (be->be_delete != NULL)
+ {
+ if ((rc = (*be->be_delete)(pb)) == 0)
+ {
+ /* we don't perform acl check for internal operations */
+ /* Dont update aci store for remote acis */
+ if ((!internal_op) &&
+ (!slapi_be_is_flag_set(be,SLAPI_BE_FLAG_REMOTE_DATA)))
+ plugin_call_acl_mods_update (pb, SLAPI_OPERATION_DELETE);
+
+ if (operation_is_flag_set(operation,OP_FLAG_ACTION_LOG_AUDIT))
+ write_audit_log_entry(pb); /* Record the operation in the audit log */
+
+ slapi_pblock_get(pb, SLAPI_ENTRY_PRE_OP, &ecopy);
+ do_ps_service(ecopy, NULL, LDAP_CHANGETYPE_DELETE, 0UL);
+ }
+ else
+ {
+ if (rc == SLAPI_FAIL_DISKFULL)
+ {
+ operation_out_of_disk_space();
+ goto free_and_return;
+ }
+ }
+ }
+
+ slapi_pblock_set(pb, SLAPI_PLUGIN_OPRETURN, &rc);
+ plugin_call_plugins(pb, internal_op ? SLAPI_PLUGIN_INTERNAL_POST_DELETE_FN :
+ SLAPI_PLUGIN_POST_DELETE_FN);
+ }
+
+free_and_return:
+ if (be)
+ slapi_be_Unlock(be);
+ slapi_pblock_get(pb, SLAPI_ENTRY_PRE_OP, &ecopy);
+ slapi_entry_free(ecopy);
+ slapi_pblock_get ( pb, SLAPI_DELETE_GLUE_PARENT_ENTRY, &ecopy );
+ if (ecopy)
+ {
+ slapi_entry_free (ecopy);
+ slapi_pblock_set (pb, SLAPI_DELETE_GLUE_PARENT_ENTRY, NULL);
+ }
+ slapi_pblock_get(pb, SLAPI_URP_NAMING_COLLISION_DN, &dn);
+ slapi_ch_free((void **)&dn);
+ slapi_sdn_done(&sdn);
+}
diff --git a/ldap/servers/slapd/detach.c b/ldap/servers/slapd/detach.c
new file mode 100644
index 00000000..8c86358d
--- /dev/null
+++ b/ldap/servers/slapd/detach.c
@@ -0,0 +1,244 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+/*
+ * 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.
+ */
+
+#include <stdio.h>
+#include <sys/types.h>
+#ifdef SVR4
+#include <sys/stat.h>
+#endif /* svr4 */
+#include <fcntl.h>
+#ifndef _WIN32
+#include <errno.h>
+#include <sys/file.h>
+#include <sys/ioctl.h>
+#include <sys/resource.h>
+#endif
+#include <signal.h>
+#ifdef LINUX
+#undef CTIME
+#endif
+#include "slap.h"
+#include "fe.h"
+
+#if defined(USE_SYSCONF) || defined(LINUX)
+#include <unistd.h>
+#endif /* USE_SYSCONF */
+
+void
+detach()
+{
+#ifndef _WIN32
+ int i, sd;
+ char *workingdir = 0;
+ char *errorlog = 0;
+ char *ptr = 0;
+ char errorbuf[BUFSIZ];
+ extern char *config_get_errorlog(void);
+#endif
+
+#ifndef _WIN32
+ if ( should_detach ) {
+ for ( i = 0; i < 5; i++ ) {
+#if defined( sunos5 ) && ( defined( THREAD_SUNOS5_LWP ) || defined( NSPR20 ))
+ switch ( fork1() ) {
+#else
+ switch ( fork() ) {
+#endif
+ case -1:
+ sleep( 5 );
+ continue;
+
+ case 0:
+ break;
+
+ default:
+ _exit( 0 );
+ }
+ break;
+ }
+
+ workingdir = config_get_workingdir();
+ if ( NULL == workingdir ) {
+ errorlog = config_get_errorlog();
+ if ( NULL == errorlog ) {
+ (void) chdir( "/" );
+ } else {
+ if ((ptr = strrchr(errorlog, '/')) ||
+ (ptr = strrchr(errorlog, '\\'))) {
+ *ptr = 0;
+ }
+ (void) chdir( errorlog );
+ config_set_workingdir(CONFIG_WORKINGDIR_ATTRIBUTE, errorlog, errorbuf, 1);
+ slapi_ch_free((void**)&errorlog);
+ }
+ } else {
+ /* calling config_set_workingdir to check for validity of directory, don't apply */
+ if (config_set_workingdir(CONFIG_WORKINGDIR_ATTRIBUTE, workingdir, errorbuf, 0) == LDAP_OPERATIONS_ERROR) {
+ exit(1);
+ }
+ (void) chdir( workingdir );
+ slapi_ch_free((void**)&workingdir);
+ }
+
+ if ( (sd = open( "/dev/null", O_RDWR )) == -1 ) {
+ perror( "/dev/null" );
+ exit( 1 );
+ }
+ (void) dup2( sd, 0 );
+ (void) dup2( sd, 1 );
+ (void) dup2( sd, 2 );
+ close( sd );
+
+#ifdef USE_SETSID
+ setsid();
+#else /* USE_SETSID */
+ if ( (sd = open( "/dev/tty", O_RDWR )) != -1 ) {
+ (void) ioctl( sd, TIOCNOTTY, NULL );
+ (void) close( sd );
+ }
+#endif /* USE_SETSID */
+
+ g_set_detached(1);
+ }
+
+ (void) SIGNAL( SIGPIPE, SIG_IGN );
+#endif /* _WIN32 */
+}
+
+
+#ifndef _WIN32
+/*
+ * close all open files except stdin/out/err
+ */
+void
+close_all_files()
+{
+ int i, nbits;
+
+#ifdef USE_SYSCONF
+ nbits = sysconf( _SC_OPEN_MAX );
+#else /* USE_SYSCONF */
+ nbits = getdtablesize();
+#endif /* USE_SYSCONF */
+
+ for ( i = 3; i < nbits; i++ ) {
+ close( i );
+ }
+}
+#endif /* !_WIN32 */
+
+/*
+ * There is no need to do anything on some platforms (NT) and not try to
+ * raise fds on AIX.
+ */
+
+static void raise_process_fd_limits(void)
+{
+#if !defined(_WIN32) && !defined(AIX)
+ struct rlimit rl, setrl;
+ RLIM_TYPE curlim;
+ slapdFrontendConfig_t *slapdFrontendConfig = getFrontendConfig();
+ if ( slapdFrontendConfig->maxdescriptors < 0 ) {
+ return;
+ }
+
+ /*
+ * Try to set our file descriptor limit. Our basic strategy is:
+ * 1) Try to set the soft limit and the hard limit if
+ * necessary to match our maxdescriptors value.
+ * 2) If that fails and our soft limit is less than our hard
+ * limit, we try to raise it to match the hard.
+ */
+ if ( getrlimit( RLIMIT_NOFILE, &rl ) != 0 ) {
+ int oserr = errno;
+
+ LDAPDebug( LDAP_DEBUG_ANY,
+ "getrlimit of descriptor limit failed - error %d (%s)\n",
+ oserr, slapd_system_strerror( oserr ), 0 );
+ return;
+ }
+
+ if ( rl.rlim_cur == slapdFrontendConfig->maxdescriptors ) { /* already correct */
+ return;
+ }
+ curlim = rl.rlim_cur;
+ setrl = rl; /* struct copy */
+ setrl.rlim_cur = slapdFrontendConfig->maxdescriptors;
+ /* don't lower the hard limit as it's irreversible */
+ if (setrl.rlim_cur > setrl.rlim_max) {
+ setrl.rlim_max = setrl.rlim_cur;
+ }
+ if ( setrlimit( RLIMIT_NOFILE, &setrl ) != 0 && curlim < rl.rlim_max ) {
+ setrl = rl; /* struct copy */
+ setrl.rlim_cur = setrl.rlim_max;
+ if ( setrlimit( RLIMIT_NOFILE, &setrl ) != 0 ) {
+ int oserr = errno;
+
+ LDAPDebug( LDAP_DEBUG_ANY, "setrlimit of descriptor "
+ "limit to %d failed - error %d (%s)\n",
+ setrl.rlim_cur, oserr,
+ slapd_system_strerror(oserr));
+ return;
+ }
+ }
+
+ (void)getrlimit( RLIMIT_NOFILE, &rl );
+ LDAPDebug( LDAP_DEBUG_TRACE, "descriptor limit changed from %d to %d\n",
+ curlim, rl.rlim_cur, 0 );
+#endif /* !_WIN32 && !AIX */
+}
+
+/*
+ * Try to raise relevant per-process limits
+ */
+void
+raise_process_limits()
+{
+#if !defined(_WIN32)
+ struct rlimit rl;
+
+ raise_process_fd_limits();
+
+#ifdef RLIMIT_DATA
+ if (getrlimit(RLIMIT_DATA,&rl) == 0) {
+ rl.rlim_cur = rl.rlim_max;
+ if (setrlimit(RLIMIT_DATA,&rl) != 0) {
+ LDAPDebug(LDAP_DEBUG_TRACE,"setrlimit(RLIMIT_DATA) failed %d\n",
+ errno,0,0);
+ }
+ } else {
+ LDAPDebug(LDAP_DEBUG_TRACE,"getrlimit(RLIMIT_DATA) failed %d\n",
+ errno,0,0);
+ }
+#endif
+
+#ifdef RLIMIT_VMEM
+ if (getrlimit(RLIMIT_VMEM,&rl) == 0) {
+ rl.rlim_cur = rl.rlim_max;
+ if (setrlimit(RLIMIT_VMEM,&rl) != 0) {
+ LDAPDebug(LDAP_DEBUG_TRACE,"setrlimit(RLIMIT_VMEM) failed %d\n",
+ errno,0,0);
+ }
+ } else {
+ LDAPDebug(LDAP_DEBUG_TRACE,"getrlimit(RLIMIT_VMEM) failed %d\n",
+ errno,0,0);
+ }
+#endif /* RLIMIT_VMEM */
+
+#endif /* !_WIN32 */
+}
+
diff --git a/ldap/servers/slapd/disconnect_error_strings.h b/ldap/servers/slapd/disconnect_error_strings.h
new file mode 100644
index 00000000..673d3528
--- /dev/null
+++ b/ldap/servers/slapd/disconnect_error_strings.h
@@ -0,0 +1,30 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+/* disconnect_error_strings.h
+ *
+ * Strings describing the errors used in logging the reason a connection
+ * was closed.
+ */
+#ifndef __DISCONNECT_ERROR_STRINGS_H_
+#define __DISCONNECT_ERROR_STRINGS_H_
+
+ER2( SLAPD_DISCONNECT_ABORT, "A1" )
+ER2( SLAPD_DISCONNECT_BAD_BER_TAG, "B1" )
+ER2( SLAPD_DISCONNECT_BER_TOO_BIG, "B2" )
+ER2( SLAPD_DISCONNECT_BER_PEEK, "B3" )
+ER2( SLAPD_DISCONNECT_BER_FLUSH, "B4" )
+ER2( SLAPD_DISCONNECT_IDLE_TIMEOUT, "T1" )
+ER2( SLAPD_DISCONNECT_REVENTS, "R1" )
+ER2( SLAPD_DISCONNECT_IO_TIMEOUT, "T2" )
+ER2( SLAPD_DISCONNECT_PLUGIN, "P1" )
+ER2( SLAPD_DISCONNECT_UNBIND, "U1" )
+ER2( SLAPD_DISCONNECT_POLL, "P2" )
+ER2( SLAPD_DISCONNECT_NTSSL_TIMEOUT,"T2" )
+ER2( SLAPD_DISCONNECT_SASL_FAIL,"S1" )
+
+
+#endif /* __DISCONNECT_ERROR_STRINGS_H_ */
+
diff --git a/ldap/servers/slapd/disconnect_errors.h b/ldap/servers/slapd/disconnect_errors.h
new file mode 100644
index 00000000..c992b874
--- /dev/null
+++ b/ldap/servers/slapd/disconnect_errors.h
@@ -0,0 +1,32 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+/* disconnect_errors.h
+ *
+ * Errors used in logging the reason a connection was closed.
+ */
+#ifndef __DISCONNECT_ERRORS_H_
+#define __DISCONNECT_ERRORS_H_
+
+#define SLAPD_DISCONNECT_ERROR_BASE -5000
+
+#define SLAPD_DISCONNECT_ABORT SLAPD_DISCONNECT_ERROR_BASE + 0
+#define SLAPD_DISCONNECT_BAD_BER_TAG SLAPD_DISCONNECT_ERROR_BASE + 1
+#define SLAPD_DISCONNECT_BER_TOO_BIG SLAPD_DISCONNECT_ERROR_BASE + 2
+#define SLAPD_DISCONNECT_BER_PEEK SLAPD_DISCONNECT_ERROR_BASE + 3
+#define SLAPD_DISCONNECT_BER_FLUSH SLAPD_DISCONNECT_ERROR_BASE + 4
+#define SLAPD_DISCONNECT_IDLE_TIMEOUT SLAPD_DISCONNECT_ERROR_BASE + 5
+#define SLAPD_DISCONNECT_REVENTS SLAPD_DISCONNECT_ERROR_BASE + 6
+#define SLAPD_DISCONNECT_IO_TIMEOUT SLAPD_DISCONNECT_ERROR_BASE + 7
+#define SLAPD_DISCONNECT_PLUGIN SLAPD_DISCONNECT_ERROR_BASE + 8
+#define SLAPD_DISCONNECT_UNBIND SLAPD_DISCONNECT_ERROR_BASE + 9
+#define SLAPD_DISCONNECT_POLL SLAPD_DISCONNECT_ERROR_BASE + 10
+#define SLAPD_DISCONNECT_NTSSL_TIMEOUT SLAPD_DISCONNECT_ERROR_BASE + 11
+#define SLAPD_DISCONNECT_SASL_FAIL SLAPD_DISCONNECT_ERROR_BASE + 12
+
+
+
+#endif /* __DISCONNECT_ERRORS_H_ */
+
diff --git a/ldap/servers/slapd/dl.c b/ldap/servers/slapd/dl.c
new file mode 100644
index 00000000..b4724b06
--- /dev/null
+++ b/ldap/servers/slapd/dl.c
@@ -0,0 +1,219 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+/* DataList access functions */
+#define INIT_ALLOC 8
+#define ALLOC_INCREMENT 4
+
+#include "slap.h"
+
+DataList* dl_new ()
+{
+ return (DataList*)slapi_ch_malloc (sizeof (DataList));
+}
+void dl_free (DataList **dl)
+{
+ slapi_ch_free ((void**) dl);
+}
+
+void dl_init (DataList *dl, int init_alloc)
+{
+ PR_ASSERT (dl);
+
+ memset (dl, 0, sizeof (*dl));
+
+ if (init_alloc <= 0)
+ dl->alloc_count = INIT_ALLOC;
+ else
+ dl->alloc_count = init_alloc;
+
+ dl->elements = (void**)slapi_ch_calloc (dl->alloc_count, sizeof (void*));
+}
+
+void dl_cleanup (DataList *dl, FREEFN freefn)
+{
+ PR_ASSERT (dl);
+
+ if (freefn && dl->elements)
+ {
+ int i;
+
+ for (i = 0; i < dl->element_count; i++)
+ {
+ freefn (&(dl->elements[i]));
+ }
+ }
+
+ if (dl->elements)
+ {
+ slapi_ch_free ((void**)&dl->elements);
+ }
+
+ memset (dl, 0, sizeof (*dl));
+}
+
+/* index == 1 : insert first */
+void dl_add_index (DataList *dl, void *element, int index)
+{
+ int i = 0;
+
+ PR_ASSERT (dl);
+ PR_ASSERT (element);
+
+ if (dl->element_count == dl->alloc_count)
+ {
+ dl->alloc_count += ALLOC_INCREMENT;
+ dl->elements = (void**)slapi_ch_realloc ((char*)dl->elements, dl->alloc_count * sizeof (void*));
+ }
+
+ dl->element_count ++;
+
+ for (i = dl->element_count-1; i >= index; i--)
+ {
+ dl->elements[i] = dl->elements[i-1];
+ }
+ if ( dl->element_count < index )
+ {
+ /* Means that we are adding the first element */
+ dl->elements[0] = element;
+ }
+ else
+ {
+ dl->elements[i] = element;
+ }
+}
+
+void dl_add (DataList *dl, void *element)
+{
+ PR_ASSERT (dl);
+ PR_ASSERT (element);
+
+ if (dl->element_count == dl->alloc_count)
+ {
+ dl->alloc_count += ALLOC_INCREMENT;
+ dl->elements = (void**)slapi_ch_realloc ((char*)dl->elements, dl->alloc_count * sizeof (void*));
+ }
+
+ dl->elements[dl->element_count] = element;
+ dl->element_count ++;
+}
+
+void *dl_get_first (const DataList *dl, int *cookie)
+{
+ PR_ASSERT (dl);
+ PR_ASSERT (cookie);
+
+ if (dl->element_count == 0)
+ return NULL;
+
+ *cookie = 1;
+ return dl->elements[0];
+}
+
+void *dl_get_next (const DataList *dl, int *cookie)
+{
+ PR_ASSERT (dl);
+ PR_ASSERT (cookie && *cookie > 0);
+
+ if (*cookie >= dl->element_count)
+ return NULL;
+
+ return dl->elements[(*cookie)++];
+}
+
+void *dl_replace(const DataList *dl, const void *elementOld, void *elementNew, CMPFN cmpfn, FREEFN freefn)
+{
+ int i;
+ void *save;
+
+ PR_ASSERT (dl);
+ PR_ASSERT (elementOld);
+ PR_ASSERT (elementNew);
+ PR_ASSERT (cmpfn);
+
+ for (i = 0; i < dl->element_count; i++)
+ {
+ if (cmpfn (dl->elements[i], elementOld) == 0)
+ {
+ /* if we have destructor - free the data; otherwise, return it to the client */
+ if (freefn)
+ {
+ freefn (&dl->elements[i]);
+ save = NULL;
+ }
+ else
+ save = dl->elements[i];
+
+ dl->elements[i] = elementNew;
+
+ return save;
+ }
+ }
+
+ return NULL;
+}
+
+void *dl_get (const DataList *dl, const void *element, CMPFN cmpfn)
+{
+ int i;
+
+ PR_ASSERT (dl);
+ PR_ASSERT (element);
+ PR_ASSERT (cmpfn);
+
+ for (i = 0; i < dl->element_count; i++)
+ {
+ if (cmpfn (dl->elements[i], element) == 0)
+ {
+ return dl->elements[i];
+ }
+ }
+
+ return NULL;
+}
+
+void *dl_delete (DataList *dl, const void *element, CMPFN cmpfn, FREEFN freefn)
+{
+ int i;
+ void *save;
+
+ PR_ASSERT (dl);
+ PR_ASSERT (element);
+ PR_ASSERT (cmpfn);
+
+ for (i = 0; i < dl->element_count; i++)
+ {
+ if (cmpfn (dl->elements[i], element) == 0)
+ {
+ /* if we have destructor - free the data; otherwise, return it to the client */
+ if (freefn)
+ {
+ freefn (&dl->elements[i]);
+ save = NULL;
+ }
+ else
+ save = dl->elements[i];
+
+ if (i != dl->element_count - 1)
+ {
+ memcpy (&dl->elements[i], &dl->elements[i+1], (dl->element_count - i - 1) * sizeof (void*));
+ }
+
+ dl->element_count --;
+
+ return save;
+ }
+ }
+
+ return NULL;
+}
+
+int dl_get_count (const DataList *dl)
+{
+ PR_ASSERT (dl);
+
+ return dl->element_count;
+}
+
diff --git a/ldap/servers/slapd/dn.c b/ldap/servers/slapd/dn.c
new file mode 100644
index 00000000..0a05a6a8
--- /dev/null
+++ b/ldap/servers/slapd/dn.c
@@ -0,0 +1,1469 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+/* dn.c - routines for dealing with distinguished names */
+
+#include <stdio.h>
+#include <ctype.h>
+#include <string.h>
+#include <sys/types.h>
+#ifndef _WIN32
+#include <sys/time.h>
+#include <sys/socket.h>
+#endif
+#include "slap.h"
+
+#undef SDN_DEBUG
+
+static void add_rdn_av( char *avstart, char *avend, int *rdn_av_countp,
+ struct berval **rdn_avsp, struct berval *avstack );
+static void reset_rdn_avs( struct berval **rdn_avsp, int *rdn_av_countp );
+static void sort_rdn_avs( struct berval *avs, int count );
+static int rdn_av_cmp( struct berval *av1, struct berval *av2 );
+static void rdn_av_swap( struct berval *av1, struct berval *av2 );
+
+
+int
+hexchar2int( char c )
+{
+ if ( '0' <= c && c <= '9' ) {
+ return( c - '0' );
+ }
+ if ( 'a' <= c && c <= 'f' ) {
+ return( c - 'a' + 10 );
+ }
+ if ( 'A' <= c && c <= 'F' ) {
+ return( c - 'A' + 10 );
+ }
+ return( -1 );
+}
+
+#define DNSEPARATOR(c) (c == ',' || c == ';')
+#define SEPARATOR(c) (c == ',' || c == ';' || c == '+')
+#define SPACE(c) (c == ' ' || c == '\n') /* XXX 518524 */
+#define NEEDSESCAPE(c) (c == '\\' || c == '"')
+#define B4TYPE 0
+#define INTYPE 1
+#define B4EQUAL 2
+#define B4VALUE 3
+#define INVALUE 4
+#define INQUOTEDVALUE 5
+#define B4SEPARATOR 6
+
+#define SLAPI_DNNORM_INITIAL_RDN_AVS 10
+#define SLAPI_DNNORM_SMALL_RDN_AV 512
+
+/*
+ * substr_dn_normalize - map a DN to a canonical form.
+ * The DN is read from *dn through *(end-1) and normalized in place.
+ * The new end is returned; that is, the canonical form is in
+ * *dn through *(the_return_value-1).
+ */
+
+/* The goals of this function are:
+ * 1. be compatible with previous implementations. Especially, enable
+ * a server running this code to find database index keys that were
+ * computed by Directory Server 3.0 with a prior version of this code.
+ * 2. Normalize in place; that is, avoid allocating memory to contain
+ * the canonical form.
+ * 3. eliminate insignificant differences; that is, any two DNs are
+ * not significantly different if and only if their canonical forms
+ * are identical (ignoring upper/lower case).
+ * 4. handle a DN in the syntax defined by RFC 2253.
+ * 5. handle a DN in the syntax defined by RFC 1779.
+ *
+ * Goals 3 through 5 are not entirely achieved by this implementation,
+ * because it can't be done without violating goal 1. Specifically,
+ * DNs like cn="a,b" and cn=a\,b are not mapped to the same canonical form,
+ * although they're not significantly different. Likewise for any pair
+ * of DNs that differ only in their choice of quoting convention.
+ * A previous version of this code changed all DNs to the most compact
+ * quoting convention, but that violated goal 1, since Directory Server
+ * 3.0 did not.
+ *
+ * Also, this implementation handles the \xx convention of RFC 2253 and
+ * consequently violates RFC 1779, according to which this type of quoting
+ * would be interpreted as a sequence of 2 numerals (not a single byte).
+ *
+ * Finally, if the DN contains any RDNs that are multivalued, we sort
+ * the values in the RDN(s) to help meet goal 3. Ordering is based on a
+ * case-insensitive comparison of the "attribute=value" pairs.
+ *
+ * This function does not support UTF-8 multi-byte encoding for attribute
+ * values, in particular it does not support UTF-8 whitespace. First the
+ * SPACE macro above is limited, but also its frequent use of '-1' indexing
+ * into a char[] may hit the middle of a multi-byte UTF-8 whitespace character
+ * encoding (518524).
+ */
+
+char *
+substr_dn_normalize( char *dn, char *end )
+{
+ /* \xx is changed to \c.
+ * \c is changed to c, unless this would change its meaning.
+ * All values that contain 2 or more separators are "enquoted";
+ * all other values are not enquoted.
+ */
+ char *value = NULL;
+ char *value_separator = NULL;
+ char *d = NULL;
+ char *s = NULL;
+ char *typestart = NULL;
+ int gotesc = 0;
+ int state = B4TYPE;
+ int rdn_av_count = 0;
+ struct berval *rdn_avs = NULL;
+ struct berval initial_rdn_av_stack[ SLAPI_DNNORM_INITIAL_RDN_AVS ];
+
+ for ( d = s = dn; s != end; s++ ) {
+ switch ( state ) {
+ case B4TYPE:
+ if ( ! SPACE( *s ) ) {
+ state = INTYPE;
+ typestart = d;
+ *d++ = *s;
+ }
+ break;
+ case INTYPE:
+ if ( *s == '=' ) {
+ state = B4VALUE;
+ *d++ = *s;
+ } else if ( SPACE( *s ) ) {
+ state = B4EQUAL;
+ } else {
+ *d++ = *s;
+ }
+ break;
+ case B4EQUAL:
+ if ( *s == '=' ) {
+ state = B4VALUE;
+ *d++ = *s;
+ } else if ( ! SPACE( *s ) ) {
+ /* not a valid dn - but what can we do here? */
+ *d++ = *s;
+ }
+ break;
+ case B4VALUE:
+ if ( *s == '"' || ! SPACE( *s ) ) {
+ value_separator = NULL;
+ value = d;
+ state = ( *s == '"' ) ? INQUOTEDVALUE : INVALUE;
+ *d++ = *s;
+ }
+ break;
+ case INVALUE:
+ if ( gotesc ) {
+ if ( SEPARATOR( *s ) ) {
+ if ( value_separator ) value_separator = dn;
+ else value_separator = d;
+ } else if ( ! NEEDSESCAPE( *s ) ) {
+ --d; /* eliminate the \ */
+ }
+ } else if ( SEPARATOR( *s ) ) {
+ while ( SPACE( *(d - 1) ) )
+ d--;
+ if ( value_separator == dn ) { /* 2 or more separators */
+ /* convert to quoted value: */
+ char *L = NULL; /* char after last seperator */
+ char *R; /* value character iterator */
+ int escape_skips = 0; /* number of escapes we have seen after the first */
+
+ for ( R = value; (R = strchr( R, '\\' )) && (R < d); L = ++R ) {
+ if ( SEPARATOR( R[1] )) {
+ if ( L == NULL ) {
+ /* executes once, at first escape, adds opening quote */
+ const size_t len = R - value;
+
+ /* make room for quote by covering escape */
+ if ( len > 0 ) {
+ memmove( value+1, value, len );
+ }
+
+ *value = '"'; /* opening quote */
+ value = R + 1; /* move passed what has been parsed */
+ } else {
+ const size_t len = R - L;
+ if ( len > 0 ) {
+ /* remove the seperator */
+ memmove( value, L, len );
+ value += len; /* move passed what has been parsed */
+ }
+ --d;
+ ++escape_skips;
+ }
+ }
+ }
+ memmove( value, L, d - L + escape_skips );
+ *d++ = '"'; /* closing quote */
+ }
+ state = B4TYPE;
+
+ /*
+ * Track and sort attribute values within
+ * multivalued RDNs.
+ */
+ if ( *s == '+' || rdn_av_count > 0 ) {
+ add_rdn_av( typestart, d, &rdn_av_count,
+ &rdn_avs, initial_rdn_av_stack );
+ }
+ if ( *s != '+' ) { /* at end of this RDN */
+ if ( rdn_av_count > 1 ) {
+ sort_rdn_avs( rdn_avs, rdn_av_count );
+ }
+ if ( rdn_av_count > 0 ) {
+ reset_rdn_avs( &rdn_avs, &rdn_av_count );
+ }
+ }
+
+ *d++ = (*s == '+') ? '+' : ',';
+ break;
+ }
+ *d++ = *s;
+ break;
+ case INQUOTEDVALUE:
+ if ( gotesc ) {
+ if ( ! NEEDSESCAPE( *s ) ) {
+ --d; /* eliminate the \ */
+ }
+ } else if ( *s == '"' ) {
+ state = B4SEPARATOR;
+ if ( value_separator == dn /* 2 or more separators */
+ || SPACE( value[1] ) || SPACE( d[-1] ) ) {
+ *d++ = *s;
+ } else {
+ /* convert to non-quoted value: */
+ if ( value_separator == NULL ) { /* no separators */
+ memmove ( value, value+1, (d-value)-1 );
+ --d;
+ } else { /* 1 separator */
+ memmove ( value, value+1, (value_separator-value)-1 );
+ *(value_separator - 1) = '\\';
+ }
+ }
+ break;
+ }
+ if ( SEPARATOR( *s )) {
+ if ( value_separator ) value_separator = dn;
+ else value_separator = d;
+ }
+ *d++ = *s;
+ break;
+ case B4SEPARATOR:
+ if ( SEPARATOR( *s ) ) {
+ state = B4TYPE;
+
+ /*
+ * Track and sort attribute values within
+ * multivalued RDNs.
+ */
+ if ( *s == '+' || rdn_av_count > 0 ) {
+ add_rdn_av( typestart, d, &rdn_av_count,
+ &rdn_avs, initial_rdn_av_stack );
+ }
+ if ( *s != '+' ) { /* at end of this RDN */
+ if ( rdn_av_count > 1 ) {
+ sort_rdn_avs( rdn_avs, rdn_av_count );
+ }
+ if ( rdn_av_count > 0 ) {
+ reset_rdn_avs( &rdn_avs, &rdn_av_count );
+ }
+ }
+
+ *d++ = (*s == '+') ? '+' : ',';
+ }
+ break;
+ default:
+ LDAPDebug( LDAP_DEBUG_ANY,
+ "slapi_dn_normalize - unknown state %d\n", state, 0, 0 );
+ break;
+ }
+ if ( *s != '\\' ) {
+ gotesc = 0;
+ } else {
+ gotesc = 1;
+ if ( s+2 < end ) {
+ int n = hexchar2int( s[1] );
+ if ( n >= 0 ) {
+ int n2 = hexchar2int( s[2] );
+ if ( n2 >= 0 ) {
+ n = (n << 4) + n2;
+ if (n == 0) { /* don't change \00 */
+ *d++ = *++s;
+ *d++ = *++s;
+ gotesc = 0;
+ } else { /* change \xx to a single char */
+ ++s;
+ *(unsigned char*)(s+1) = n;
+ }
+ }
+ }
+ }
+ }
+ }
+
+ /*
+ * Track and sort attribute values within multivalued RDNs.
+ */
+ if ( rdn_av_count > 0 ) {
+ add_rdn_av( typestart, d, &rdn_av_count,
+ &rdn_avs, initial_rdn_av_stack );
+ }
+ if ( rdn_av_count > 1 ) {
+ sort_rdn_avs( rdn_avs, rdn_av_count );
+ }
+ if ( rdn_av_count > 0 ) {
+ reset_rdn_avs( &rdn_avs, &rdn_av_count );
+ }
+
+ /* Trim trailing spaces */
+ while ( d != dn && *(d - 1) == ' ' ) d--; /* XXX 518524 */
+
+ return( d );
+}
+
+
+
+/*
+ * Append previous AV to the attribute value array if multivalued RDN.
+ * We use a stack based array at first and if we overflow that, we
+ * allocate a larger one from the heap, copy the stack based data in,
+ * and continue to grow the heap based one as needed.
+ */
+static void
+add_rdn_av( char *avstart, char *avend, int *rdn_av_countp,
+ struct berval **rdn_avsp, struct berval *avstack )
+{
+ if ( *rdn_av_countp == 0 ) {
+ *rdn_avsp = avstack;
+ } else if ( *rdn_av_countp == SLAPI_DNNORM_INITIAL_RDN_AVS ) {
+ struct berval *tmpavs;
+
+ tmpavs = (struct berval *)slapi_ch_calloc(
+ SLAPI_DNNORM_INITIAL_RDN_AVS * 2, sizeof( struct berval ));
+ memcpy( tmpavs, *rdn_avsp,
+ SLAPI_DNNORM_INITIAL_RDN_AVS * sizeof( struct berval ));
+ *rdn_avsp = tmpavs;
+ } else if (( *rdn_av_countp % SLAPI_DNNORM_INITIAL_RDN_AVS ) == 0 ) {
+ *rdn_avsp = (struct berval *)slapi_ch_realloc( (char *)*rdn_avsp,
+ (*rdn_av_countp + SLAPI_DNNORM_INITIAL_RDN_AVS)*sizeof(struct berval) );
+ }
+
+ /*
+ * Note: The bv_val's are just pointers into the dn itself. Also,
+ * we DO NOT zero-terminate the bv_val's. The sorting code in
+ * sort_rdn_avs() takes all of this into account.
+ */
+ (*rdn_avsp)[ *rdn_av_countp ].bv_val = avstart;
+ (*rdn_avsp)[ *rdn_av_countp ].bv_len = avend - avstart;
+ ++(*rdn_av_countp);
+}
+
+
+/*
+ * Reset RDN attribute value array, freeing memory if any was allocated.
+ */
+static void
+reset_rdn_avs( struct berval **rdn_avsp, int *rdn_av_countp )
+{
+ if ( *rdn_av_countp > SLAPI_DNNORM_INITIAL_RDN_AVS ) {
+ slapi_ch_free( (void **)rdn_avsp );
+ }
+ *rdn_avsp = NULL;
+ *rdn_av_countp = 0;
+}
+
+
+/*
+ * Perform an in-place, case-insensitive sort of RDN attribute=value pieces.
+ * This function is always called with more than one element in "avs".
+ *
+ * Note that this is used by the DN normalization code, so if any changes
+ * are made to the comparison function used for sorting customers will need
+ * to rebuild their database/index files.
+ *
+ * Also note that the bv_val's in the "avas" array are not zero-terminated.
+ */
+static void
+sort_rdn_avs( struct berval *avs, int count )
+{
+ int i, j, swaps;
+
+ /*
+ * Since we expect there to be a small number of AVs, we use a
+ * simple bubble sort. rdn_av_swap() only works correctly on
+ * adjacent values anyway.
+ */
+ for ( i = 0; i < count - 1; ++i ) {
+ swaps = 0;
+ for ( j = 0; j < count - 1; ++j ) {
+ if ( rdn_av_cmp( &avs[j], &avs[j+1] ) > 0 ) {
+ rdn_av_swap( &avs[j], &avs[j+1] );
+ ++swaps;
+ }
+ }
+ if ( swaps == 0 ) {
+ break; /* stop early if no swaps made during the last pass */
+ }
+ }
+}
+
+
+/*
+ * strcasecmp()-like function for RDN attribute values.
+ */
+static int
+rdn_av_cmp( struct berval *av1, struct berval *av2 )
+{
+ int rc;
+
+ rc = strncasecmp( av1->bv_val, av2->bv_val,
+ ( av1->bv_len < av2->bv_len ) ? av1->bv_len : av2->bv_len );
+
+ if ( rc == 0 ) {
+ return( av1->bv_len - av2->bv_len ); /* longer is greater */
+ } else {
+ return( rc );
+ }
+}
+
+
+/*
+ * Swap two adjacent attribute=value pieces within an (R)DN.
+ * Avoid allocating any heap memory for reasonably small AVs.
+ */
+static void
+rdn_av_swap( struct berval *av1, struct berval *av2 )
+{
+ char *buf1, *buf2;
+ char stackbuf1[ SLAPI_DNNORM_SMALL_RDN_AV ];
+ char stackbuf2[ SLAPI_DNNORM_SMALL_RDN_AV ];
+ int len1, len2;
+
+ /*
+ * Copy the two avs into temporary buffers. We use stack-based buffers
+ * if the avs are small and allocate buffers from the heap to hold
+ * large values.
+ */
+ if (( len1 = av1->bv_len ) <= SLAPI_DNNORM_SMALL_RDN_AV ) {
+ buf1 = stackbuf1;
+ } else {
+ buf1 = slapi_ch_malloc( len1 );
+ }
+ memcpy( buf1, av1->bv_val, len1 );
+
+ if (( len2 = av2->bv_len ) <= SLAPI_DNNORM_SMALL_RDN_AV ) {
+ buf2 = stackbuf2;
+ } else {
+ buf2 = slapi_ch_malloc( len2 );
+ }
+ memcpy( buf2, av2->bv_val, len2 );
+
+ /*
+ * Copy av2 over av1 and reset length of av1.
+ */
+ memcpy( av1->bv_val, buf2, av2->bv_len );
+ av1->bv_len = len2;
+
+ /*
+ * Add separator character (+) and copy av1 into place.
+ * Also reset av2 pointer and length.
+ */
+ av2->bv_val = av1->bv_val + len2;
+ *(av2->bv_val)++ = '+';
+ memcpy( av2->bv_val, buf1, len1 );
+ av2->bv_len = len1;
+
+ /*
+ * Clean up.
+ */
+ if ( len1 > SLAPI_DNNORM_SMALL_RDN_AV ) {
+ slapi_ch_free( (void **)&buf1 );
+ }
+ if ( len2 > SLAPI_DNNORM_SMALL_RDN_AV ) {
+ slapi_ch_free( (void **)&buf2 );
+ }
+}
+
+
+/*
+ * slapi_dn_normalize - put dn into a canonical format. the dn is
+ * normalized in place, as well as returned.
+ */
+
+char *
+slapi_dn_normalize( char *dn )
+{
+ /* LDAPDebug( LDAP_DEBUG_TRACE, "=> slapi_dn_normalize \"%s\"\n", dn, 0, 0 ); */
+ *(substr_dn_normalize( dn, dn + strlen( dn ))) = '\0';
+ /* LDAPDebug( LDAP_DEBUG_TRACE, "<= slapi_dn_normalize \"%s\"\n", dn, 0, 0 ); */
+ return dn;
+}
+
+/* Note that this routine normalizes to the end and doesn't null terminate */
+char *
+slapi_dn_normalize_to_end( char *dn , char *end)
+{
+ return ( substr_dn_normalize( dn, end ? end : dn + strlen( dn )) );
+}
+
+/*
+ * dn could contain UTF-8 multi-byte characters,
+ * which also need to be converted to the lower case.
+ */
+char *
+slapi_dn_ignore_case( char *dn )
+{
+ unsigned char *s, *d;
+ int ssz, dsz;
+ /* normalize case (including UTF-8 multi-byte chars) */
+ for ( s = d = (unsigned char *)dn; *s; s += ssz, d += dsz ) {
+ slapi_utf8ToLower( s, d, &ssz, &dsz );
+ }
+ *d = '\0'; /* utf8ToLower result may be shorter than the original */
+ return( dn );
+}
+
+/*
+ * slapi_dn_normalize_case - put dn into a canonical form suitable for storing
+ * in a hash database. this involves normalizing the case as well as
+ * the format. the dn is normalized in place as well as returned.
+ */
+
+char *
+slapi_dn_normalize_case( char *dn )
+{
+ /* normalize format */
+ slapi_dn_normalize( dn );
+
+ /* normalize case */
+ return( slapi_dn_ignore_case( dn ));
+}
+
+/*
+ * slapi_dn_beparent - return a copy of the dn of dn's parent,
+ * NULL if the DN is a suffix of the backend.
+ */
+char *
+slapi_dn_beparent(
+ Slapi_PBlock *pb,
+ const char *dn
+)
+{
+ char *r= NULL;
+ if ( dn != NULL && *dn != '\0')
+ {
+ if(!slapi_dn_isbesuffix( pb, dn ))
+ {
+ r= slapi_dn_parent( dn );
+ }
+ }
+ return r;
+}
+
+char*
+slapi_dn_parent( const char *dn )
+{
+ const char *s;
+ int inquote;
+
+ if ( dn == NULL || *dn == '\0' ) {
+ return( NULL );
+ }
+
+ /*
+ * An X.500-style distinguished name looks like this:
+ * foo=bar,sha=baz,...
+ */
+
+ inquote = 0;
+ for ( s = dn; *s; s++ ) {
+ if ( *s == '\\' ) {
+ if ( *(s + 1) )
+ s++;
+ continue;
+ }
+ if ( inquote ) {
+ if ( *s == '"' )
+ inquote = 0;
+ } else {
+ if ( *s == '"' )
+ inquote = 1;
+ else if ( DNSEPARATOR( *s ) )
+ return( slapi_ch_strdup( s + 1 ) );
+ }
+ }
+
+ return( NULL );
+}
+
+/*
+ * slapi_dn_issuffix - tells whether suffix is a suffix of dn. both dn
+ * and suffix must be normalized.
+ */
+int
+slapi_dn_issuffix(const char *dn, const char *suffix)
+{
+ int dnlen, suffixlen;
+
+ if ( dn==NULL || suffix==NULL)
+ {
+ return( 0 );
+ }
+
+ suffixlen = strlen( suffix );
+ dnlen = strlen( dn );
+
+ if ( suffixlen > dnlen )
+ {
+ return( 0 );
+ }
+
+ if ( suffixlen == 0 )
+ {
+ return ( 1 );
+ }
+
+ return( (slapi_utf8casecmp( (unsigned char *)(dn + dnlen - suffixlen),
+ (unsigned char *)suffix ) == 0)
+ && ( (dnlen == suffixlen) || DNSEPARATOR(dn[dnlen-suffixlen-1])) );
+}
+
+int
+slapi_dn_isbesuffix( Slapi_PBlock *pb, const char *dn )
+{
+ int r;
+ Slapi_DN sdn;
+ slapi_sdn_init_dn_byref(&sdn,dn);
+ r= slapi_be_issuffix( pb->pb_backend, &sdn );
+ slapi_sdn_done(&sdn);
+ return r;
+}
+
+/*
+ * slapi_dn_isparent - returns non-zero if parentdn is the parent of childdn,
+ * 0 otherwise
+ */
+int
+slapi_dn_isparent( const char *parentdn, const char *childdn )
+{
+ char *realparentdn, *copyparentdn;
+ int rc;
+
+ /* child is root - has no parent */
+ if ( childdn == NULL || *childdn == '\0' ) {
+ return( 0 );
+ }
+
+ /* construct the actual parent dn and normalize it */
+ if ( (realparentdn = slapi_dn_parent( childdn )) == NULL ) {
+ return( parentdn == NULL || *parentdn == '\0' );
+ }
+ slapi_dn_normalize( realparentdn );
+
+ /* normalize the purported parent dn */
+ copyparentdn = slapi_ch_strdup( (char *)parentdn );
+ slapi_dn_normalize( copyparentdn );
+
+ /* compare them */
+ rc = ! strcasecmp( realparentdn, copyparentdn );
+ slapi_ch_free( (void**)&copyparentdn );
+ slapi_ch_free( (void**)&realparentdn );
+
+ return( rc );
+}
+
+/*
+ * Function: slapi_dn_isroot
+ *
+ * Returns: 1 if "dn" is the root dn
+ * 0 otherwise.
+ * dn must be normalized
+ *
+ */
+int
+slapi_dn_isroot( const char *dn )
+{
+ int rc;
+ char *rootdn;
+
+ if ( NULL == dn ) {
+ return( 0 );
+ }
+ if ( NULL == (rootdn = config_get_rootdn())) {
+ return( 0 );
+ }
+
+ /* note: global root dn is normalized when read from config. file */
+ rc = (strcasecmp( rootdn, dn ) == 0);
+ slapi_ch_free ( (void **) &rootdn );
+ return( rc );
+}
+
+int
+slapi_is_rootdse( const char *dn )
+{
+ if ( NULL != dn )
+ {
+ if ( *dn == '\0' )
+ {
+ return 1;
+ }
+ }
+ return 0;
+}
+
+
+
+/*
+** This function takes a quoted attribute value of the form "abc",
+** and strips off the enclosing quotes. It also deals with quoted
+** characters by removing the preceeding '\' character.
+**
+*/
+static void
+strcpy_unescape_dnvalue( char *d, const char *s )
+{
+ const char *end = s + strlen(s);
+ for ( ; *s; s++ )
+ {
+ switch ( *s )
+ {
+ case '"':
+ break;
+ case '\\':
+ {
+ /*
+ * The '\' could be escaping a single character, ie \"
+ * or could be escaping a hex byte, ie \01
+ */
+ int singlecharacter= 1;
+ if ( s+2 < end )
+ {
+ int n = hexchar2int( s[1] );
+ if ( n >= 0 )
+ {
+ int n2 = hexchar2int( s[2] );
+ if ( n2 >= 0 )
+ {
+ singlecharacter= 0;
+ n = (n << 4) + n2;
+ if (n == 0)
+ {
+ /* don't change \00 */
+ *d++ = *++s;
+ *d++ = *++s;
+ }
+ else
+ {
+ /* change \xx to a single char */
+ ++s;
+ *(unsigned char*)(s+1) = n;
+ }
+ }
+ }
+ }
+ if(singlecharacter)
+ {
+ s++;
+ *d++ = *s;
+ }
+ break;
+ }
+ default:
+ *d++ = *s;
+ break;
+ }
+ }
+ *d = '\0';
+}
+
+
+
+int
+slapi_rdn2typeval(
+ char *rdn,
+ char **type,
+ struct berval *bv
+)
+{
+ char *s;
+
+ if ( (s = strchr( rdn, '=' )) == NULL ) {
+ return( -1 );
+ }
+ *s++ = '\0';
+
+ *type = rdn;
+
+ /* MAB 9 Oct 00 : explicit bug fix of 515715
+ implicit bug fix of 394800 (can't reproduce anymore)
+ When adding the rdn attribute in the entry, we need to remove
+ all special escaped characters included in the value itself,
+ i.e., strings like "\;" must be converted to ";" and so on... */
+ strcpy_unescape_dnvalue(s,s);
+
+ bv->bv_val = s;
+ bv->bv_len = strlen( s );
+
+ return( 0 );
+}
+
+/*
+ * Add an RDN to a DN, getting back the new DN.
+ */
+char *
+slapi_dn_plus_rdn(const char *dn, const char *rdn)
+{
+ /* rdn + separator + dn + null */
+ char *newdn = (char *) slapi_ch_malloc( strlen( dn ) + strlen( rdn ) + 2 );
+ strcpy( newdn, rdn );
+ strcat( newdn, "," );
+ strcat( newdn, dn );
+ return newdn;
+}
+
+/* ====== Slapi_DN functions ====== */
+
+#ifdef SDN_DEBUG
+#define SDN_DUMP(sdn,name) sdn_dump(sdn,name)
+static void sdn_dump( const Slapi_DN *sdn, const char *text);
+#else
+#define SDN_DUMP(sdn,name) ((void)0)
+#endif
+
+#ifndef SLAPI_DN_COUNTERS
+#undef DEBUG /* disable counters */
+#endif
+#include <prcountr.h>
+
+static int counters_created= 0;
+PR_DEFINE_COUNTER(slapi_sdn_counter_created);
+PR_DEFINE_COUNTER(slapi_sdn_counter_deleted);
+PR_DEFINE_COUNTER(slapi_sdn_counter_exist);
+PR_DEFINE_COUNTER(slapi_sdn_counter_dn_created);
+PR_DEFINE_COUNTER(slapi_sdn_counter_dn_deleted);
+PR_DEFINE_COUNTER(slapi_sdn_counter_dn_exist);
+PR_DEFINE_COUNTER(slapi_sdn_counter_ndn_created);
+PR_DEFINE_COUNTER(slapi_sdn_counter_ndn_deleted);
+PR_DEFINE_COUNTER(slapi_sdn_counter_ndn_exist);
+
+static void
+sdn_create_counters()
+{
+ PR_CREATE_COUNTER(slapi_sdn_counter_created,"Slapi_DN","created","");
+ PR_CREATE_COUNTER(slapi_sdn_counter_deleted,"Slapi_DN","deleted","");
+ PR_CREATE_COUNTER(slapi_sdn_counter_exist,"Slapi_DN","exist","");
+ PR_CREATE_COUNTER(slapi_sdn_counter_dn_created,"Slapi_DN","internal_dn_created","");
+ PR_CREATE_COUNTER(slapi_sdn_counter_dn_deleted,"Slapi_DN","internal_dn_deleted","");
+ PR_CREATE_COUNTER(slapi_sdn_counter_dn_exist,"Slapi_DN","internal_dn_exist","");
+ PR_CREATE_COUNTER(slapi_sdn_counter_ndn_created,"Slapi_DN","internal_ndn_created","");
+ PR_CREATE_COUNTER(slapi_sdn_counter_ndn_deleted,"Slapi_DN","internal_ndn_deleted","");
+ PR_CREATE_COUNTER(slapi_sdn_counter_ndn_exist,"Slapi_DN","internal_ndn_exist","");
+ counters_created= 1;
+}
+
+#define FLAG_ALLOCATED 0
+#define FLAG_DN 1
+#define FLAG_NDN 2
+
+Slapi_DN *
+slapi_sdn_new()
+{
+ Slapi_DN *sdn= (Slapi_DN *)slapi_ch_malloc(sizeof(Slapi_DN));
+ slapi_sdn_init(sdn);
+ sdn->flag= slapi_setbit_uchar(sdn->flag,FLAG_ALLOCATED);
+ SDN_DUMP( sdn, "slapi_sdn_new");
+ PR_INCREMENT_COUNTER(slapi_sdn_counter_created);
+ PR_INCREMENT_COUNTER(slapi_sdn_counter_exist);
+ return sdn;
+}
+
+Slapi_DN *
+slapi_sdn_init(Slapi_DN *sdn)
+{
+ sdn->flag= 0;
+ sdn->dn= NULL;
+ sdn->ndn= NULL;
+ sdn->ndn_len=0;
+ if(!counters_created)
+ {
+ sdn_create_counters();
+ }
+ return sdn;
+}
+
+Slapi_DN *
+slapi_sdn_init_dn_byref(Slapi_DN *sdn,const char *dn)
+{
+ slapi_sdn_init(sdn);
+ slapi_sdn_set_dn_byref(sdn,dn);
+ return sdn;
+}
+
+Slapi_DN *
+slapi_sdn_init_dn_byval(Slapi_DN *sdn,const char *dn)
+{
+ slapi_sdn_init(sdn);
+ slapi_sdn_set_dn_byval(sdn,dn);
+ return sdn;
+}
+
+Slapi_DN *
+slapi_sdn_init_dn_passin(Slapi_DN *sdn,const char *dn)
+{
+ slapi_sdn_init(sdn);
+ slapi_sdn_set_dn_passin(sdn,dn);
+ return sdn;
+}
+
+/* use when dn is normalized previously */
+Slapi_DN *
+slapi_sdn_init_dn_ndn_byref(Slapi_DN *sdn,const char *dn) {
+ slapi_sdn_init(sdn);
+ slapi_sdn_set_dn_byref(sdn,dn);
+ /* slapi_sdn_set_ndn_byref nulls out dn set in above statement */
+ sdn->flag= slapi_unsetbit_uchar(sdn->flag,FLAG_NDN);
+ sdn->ndn= dn;
+ if(dn == NULL) {
+ sdn->ndn_len=0;
+ } else {
+ sdn->ndn_len=strlen(dn);
+ }
+ return sdn;
+}
+
+Slapi_DN *
+slapi_sdn_init_ndn_byref(Slapi_DN *sdn,const char *dn)
+{
+ slapi_sdn_init(sdn);
+ slapi_sdn_set_ndn_byref(sdn,dn);
+ return sdn;
+}
+
+Slapi_DN *
+slapi_sdn_init_ndn_byval(Slapi_DN *sdn,const char *dn)
+{
+ slapi_sdn_init(sdn);
+ slapi_sdn_set_ndn_byval(sdn,dn);
+ return sdn;
+}
+
+Slapi_DN *
+slapi_sdn_new_dn_byval(const char *dn)
+{
+ Slapi_DN *sdn= slapi_sdn_new();
+ slapi_sdn_set_dn_byval(sdn,dn);
+ SDN_DUMP( sdn, "slapi_sdn_new_dn_byval");
+ return sdn;
+}
+
+Slapi_DN *
+slapi_sdn_new_ndn_byval(const char *ndn)
+{
+ Slapi_DN *sdn= slapi_sdn_new();
+ slapi_sdn_set_ndn_byval(sdn,ndn);
+ SDN_DUMP( sdn, "slapi_sdn_new_ndn_byval");
+ return sdn;
+}
+
+Slapi_DN *
+slapi_sdn_new_dn_byref(const char *dn)
+{
+ Slapi_DN *sdn= slapi_sdn_new();
+ slapi_sdn_set_dn_byref(sdn,dn);
+ SDN_DUMP( sdn, "slapi_sdn_new_dn_byref");
+ return sdn;
+}
+
+Slapi_DN *
+slapi_sdn_new_dn_passin(const char *dn)
+{
+ Slapi_DN *sdn= slapi_sdn_new();
+ slapi_sdn_set_dn_passin(sdn,dn);
+ SDN_DUMP( sdn, "slapi_sdn_new_dn_passin");
+ return sdn;
+}
+
+Slapi_DN *
+slapi_sdn_new_ndn_byref(const char *ndn)
+{
+ Slapi_DN *sdn= slapi_sdn_new();
+ slapi_sdn_set_ndn_byref(sdn,ndn);
+ SDN_DUMP( sdn, "slapi_sdn_new_ndn_byref");
+ return sdn;
+}
+
+Slapi_DN *
+slapi_sdn_set_dn_byval(Slapi_DN *sdn, const char *dn)
+{
+ slapi_sdn_done(sdn);
+ sdn->flag= slapi_setbit_uchar(sdn->flag,FLAG_DN);
+ if(dn!=NULL)
+ {
+ sdn->dn= slapi_ch_strdup(dn);
+ PR_INCREMENT_COUNTER(slapi_sdn_counter_dn_created);
+ PR_INCREMENT_COUNTER(slapi_sdn_counter_dn_exist);
+ }
+ return sdn;
+}
+
+Slapi_DN *
+slapi_sdn_set_dn_byref(Slapi_DN *sdn, const char *dn)
+{
+ slapi_sdn_done(sdn);
+ sdn->flag= slapi_unsetbit_uchar(sdn->flag,FLAG_DN);
+ sdn->dn= dn;
+ return sdn;
+}
+
+Slapi_DN *
+slapi_sdn_set_dn_passin(Slapi_DN *sdn, const char *dn)
+{
+ slapi_sdn_done(sdn);
+ sdn->flag= slapi_setbit_uchar(sdn->flag,FLAG_DN);
+ sdn->dn= dn;
+ if(dn!=NULL)
+ {
+ PR_INCREMENT_COUNTER(slapi_sdn_counter_dn_created);
+ PR_INCREMENT_COUNTER(slapi_sdn_counter_dn_exist);
+ }
+ return sdn;
+}
+
+Slapi_DN *
+slapi_sdn_set_ndn_byval(Slapi_DN *sdn, const char *ndn)
+{
+ slapi_sdn_done(sdn);
+ sdn->flag= slapi_setbit_uchar(sdn->flag,FLAG_NDN);
+ if(ndn!=NULL)
+ {
+ sdn->ndn= slapi_ch_strdup(ndn);
+ sdn->ndn_len=strlen(ndn);
+ PR_INCREMENT_COUNTER(slapi_sdn_counter_ndn_created);
+ PR_INCREMENT_COUNTER(slapi_sdn_counter_ndn_exist);
+ }
+ return sdn;
+}
+
+Slapi_DN *
+slapi_sdn_set_ndn_byref(Slapi_DN *sdn, const char *ndn)
+{
+ slapi_sdn_done(sdn);
+ sdn->flag= slapi_unsetbit_uchar(sdn->flag,FLAG_NDN);
+ sdn->ndn= ndn;
+ if(ndn == NULL) {
+ sdn->ndn_len=0;
+ } else {
+ sdn->ndn_len=strlen(ndn);
+ }
+ return sdn;
+}
+
+/*
+ * Set the RDN of the DN.
+ */
+Slapi_DN *
+slapi_sdn_set_rdn(Slapi_DN *sdn, const Slapi_RDN *rdn)
+{
+ const char *rawrdn= slapi_rdn_get_rdn(rdn);
+ if(slapi_sdn_isempty(sdn))
+ {
+ slapi_sdn_set_dn_byval(sdn,rawrdn);
+ }
+ else
+ {
+ /* NewDN= NewRDN + OldParent */
+ char *parentdn= slapi_dn_parent(sdn->dn);
+ char *newdn= slapi_ch_malloc(strlen(rawrdn)+1+strlen(parentdn)+1);
+ strcpy( newdn, rawrdn );
+ strcat( newdn, "," );
+ strcat( newdn, parentdn );
+ slapi_ch_free((void**)&parentdn);
+ slapi_sdn_set_dn_passin(sdn,newdn);
+ }
+ return sdn;
+}
+
+/*
+ * Add the RDN to the DN.
+ */
+Slapi_DN *
+slapi_sdn_add_rdn(Slapi_DN *sdn, const Slapi_RDN *rdn)
+{
+ const char *rawrdn= slapi_rdn_get_rdn(rdn);
+ if(slapi_sdn_isempty(sdn))
+ {
+ slapi_sdn_set_dn_byval(sdn,rawrdn);
+ }
+ else
+ {
+ /* NewDN= NewRDN + DN */
+ const char *dn= slapi_sdn_get_dn(sdn);
+ char *newdn= slapi_ch_malloc(strlen(rawrdn)+1+strlen(dn)+1);
+ strcpy( newdn, rawrdn );
+ strcat( newdn, "," );
+ strcat( newdn, dn );
+ slapi_sdn_set_dn_passin(sdn,newdn);
+ }
+ return sdn;
+}
+
+/*
+ * Set the parent of the DN.
+ */
+Slapi_DN *
+slapi_sdn_set_parent(Slapi_DN *sdn, const Slapi_DN *parentdn)
+{
+ if(slapi_sdn_isempty(sdn))
+ {
+ slapi_sdn_copy(parentdn, sdn);
+ }
+ else
+ {
+ /* NewDN= OldRDN + NewParent */
+ Slapi_RDN rdn;
+ const char *rawrdn;
+ slapi_rdn_init_dn(&rdn,sdn->dn);
+ rawrdn= slapi_rdn_get_rdn(&rdn);
+ if(slapi_sdn_isempty(parentdn))
+ {
+ slapi_sdn_set_dn_byval(sdn,rawrdn);
+ }
+ else
+ {
+ char *newdn;
+ newdn= slapi_ch_malloc(strlen(rawrdn)+1+strlen(parentdn->dn)+1);
+ strcpy( newdn, rawrdn );
+ strcat( newdn, "," );
+ strcat( newdn, parentdn->dn );
+ slapi_sdn_set_dn_passin(sdn,newdn);
+ }
+ slapi_rdn_done(&rdn);
+ }
+ return sdn;
+}
+
+void
+slapi_sdn_done(Slapi_DN *sdn)
+{
+ /* sdn_dump( sdn, "slapi_sdn_done"); */
+ if(sdn->dn!=NULL)
+ {
+ if(slapi_isbitset_uchar(sdn->flag,FLAG_DN))
+ {
+ slapi_ch_free((void**)&(sdn->dn));
+ sdn->flag= slapi_unsetbit_uchar(sdn->flag,FLAG_DN);
+ PR_INCREMENT_COUNTER(slapi_sdn_counter_dn_deleted);
+ PR_DECREMENT_COUNTER(slapi_sdn_counter_dn_exist);
+ }
+ else
+ {
+ sdn->dn= NULL;
+ }
+ }
+ if(sdn->ndn!=NULL)
+ {
+ if(slapi_isbitset_uchar(sdn->flag,FLAG_NDN))
+ {
+ slapi_ch_free((void**)&(sdn->ndn));
+ sdn->flag= slapi_unsetbit_uchar(sdn->flag,FLAG_NDN);
+ sdn->ndn_len=0;
+ PR_INCREMENT_COUNTER(slapi_sdn_counter_ndn_deleted);
+ PR_DECREMENT_COUNTER(slapi_sdn_counter_ndn_exist);
+ }
+ else
+ {
+ sdn->ndn= NULL;
+ sdn->ndn_len=0;
+ }
+ }
+}
+
+void
+slapi_sdn_free(Slapi_DN **sdn)
+{
+ if(sdn!=NULL && *sdn!=NULL)
+ {
+ SDN_DUMP( *sdn, "slapi_sdn_free");
+ slapi_sdn_done(*sdn);
+ if(slapi_isbitset_uchar((*sdn)->flag,FLAG_ALLOCATED))
+ {
+ slapi_ch_free((void**)sdn);
+ PR_INCREMENT_COUNTER(slapi_sdn_counter_deleted);
+ PR_DECREMENT_COUNTER(slapi_sdn_counter_exist);
+ }
+ }
+}
+
+const char *
+slapi_sdn_get_dn(const Slapi_DN *sdn)
+{
+ return (sdn->dn!=NULL ? sdn->dn : sdn->ndn);
+}
+
+const char *
+slapi_sdn_get_ndn(const Slapi_DN *sdn)
+{
+ if(sdn->ndn==NULL)
+ {
+ if(sdn->dn!=NULL)
+ {
+ char *p= slapi_ch_strdup(sdn->dn);
+ Slapi_DN *ncsdn= (Slapi_DN*)sdn; /* non-const Slapi_DN */
+ slapi_dn_normalize_case(p);
+ ncsdn->ndn= p;
+ ncsdn->ndn_len=strlen(p);
+ ncsdn->flag= slapi_setbit_uchar(sdn->flag,FLAG_NDN);
+ PR_INCREMENT_COUNTER(slapi_sdn_counter_ndn_created);
+ PR_INCREMENT_COUNTER(slapi_sdn_counter_ndn_exist);
+ }
+ }
+ return sdn->ndn;
+}
+
+void
+slapi_sdn_get_parent(const Slapi_DN *sdn,Slapi_DN *sdn_parent)
+{
+ const char *parentdn= slapi_dn_parent(slapi_sdn_get_dn(sdn));
+ slapi_sdn_set_dn_passin(sdn_parent,parentdn);
+ sdn_parent->flag= slapi_setbit_uchar(sdn_parent->flag,FLAG_DN);
+ PR_INCREMENT_COUNTER(slapi_sdn_counter_dn_created);
+ PR_INCREMENT_COUNTER(slapi_sdn_counter_dn_exist);
+}
+
+void
+slapi_sdn_get_backend_parent(const Slapi_DN *sdn,Slapi_DN *sdn_parent,const Slapi_Backend *backend)
+{
+ if(slapi_sdn_isempty(sdn) || slapi_be_issuffix( backend, sdn ))
+ {
+ slapi_sdn_done(sdn_parent);
+ }
+ else
+ {
+ slapi_sdn_get_parent(sdn,sdn_parent);
+ }
+}
+
+void
+slapi_sdn_get_rdn(const Slapi_DN *sdn,Slapi_RDN *rdn)
+{
+ slapi_rdn_set_dn(rdn,sdn->dn);
+}
+
+Slapi_DN *
+slapi_sdn_dup(const Slapi_DN *sdn)
+{
+ Slapi_DN *tmp;
+ SDN_DUMP( sdn, "slapi_sdn_dup");
+ tmp=slapi_sdn_new_dn_byval(slapi_sdn_get_dn(sdn));
+ /* can't use slapi_set_ndn_byval -- it nulls the dn */
+ tmp->flag= slapi_setbit_uchar(tmp->flag,FLAG_NDN);
+ if(sdn->ndn!=NULL)
+ {
+ tmp->ndn= slapi_ch_strdup(sdn->ndn);
+ tmp->ndn_len=sdn->ndn_len;
+ } else tmp->ndn=NULL;
+ return tmp;
+}
+
+void
+slapi_sdn_copy(const Slapi_DN *from, Slapi_DN *to)
+{
+ SDN_DUMP( from, "slapi_sdn_copy from");
+ SDN_DUMP( to, "slapi_sdn_copy to");
+ slapi_sdn_done(to);
+ slapi_sdn_set_dn_byval(to,slapi_sdn_get_dn(from));
+}
+
+int
+slapi_sdn_compare( const Slapi_DN *sdn1, const Slapi_DN *sdn2 )
+{
+ int rc;
+ const char *ndn1= slapi_sdn_get_ndn(sdn1);
+ const char *ndn2= slapi_sdn_get_ndn(sdn2);
+ if(ndn1==ndn2)
+ {
+ rc= 0;
+ }
+ else
+ {
+ if(ndn1==NULL)
+ {
+ rc= -1;
+ }
+ else
+ {
+ if(ndn2==NULL)
+ {
+ rc= 1;
+ }
+ else
+ {
+ rc= strcmp(ndn1,ndn2);
+ }
+ }
+ }
+ return rc;
+}
+
+int
+slapi_sdn_isempty( const Slapi_DN *sdn)
+{
+ const char *dn= slapi_sdn_get_dn(sdn);
+ return (dn==NULL || dn[0]=='\0');
+}
+
+int
+slapi_sdn_issuffix(const Slapi_DN *sdn, const Slapi_DN *suffixsdn)
+{
+ int rc;
+ const char *dn= slapi_sdn_get_ndn(sdn);
+ const char *suffixdn= slapi_sdn_get_ndn(suffixsdn);
+ if(dn!=NULL && suffixdn!=NULL)
+ {
+ int dnlen = slapi_sdn_get_ndn_len(sdn);
+ int suffixlen= slapi_sdn_get_ndn_len(suffixsdn);
+ if (dnlen<suffixlen)
+ {
+ rc= 0;
+ }
+ else
+ {
+ if ( suffixlen == 0 )
+ {
+ return ( 1 );
+ }
+
+ rc= ( (strcasecmp(suffixdn, dn+dnlen-suffixlen)==0)
+ && ( (dnlen == suffixlen)
+ || DNSEPARATOR(dn[dnlen-suffixlen-1])) );
+ }
+ }
+ else
+ {
+ rc= 0;
+ }
+ return rc;
+}
+
+/* normalizes sdn if it hasn't already been done */
+int
+slapi_sdn_get_ndn_len(const Slapi_DN *sdn)
+{
+ int r= 0;
+ const char *ndn=slapi_sdn_get_ndn(sdn);
+ if(sdn->ndn!=NULL)
+ {
+ r= sdn->ndn_len;
+ }
+ return r;
+}
+
+int
+slapi_sdn_isparent( const Slapi_DN *parent, const Slapi_DN *child )
+{
+ int rc= 0;
+
+ /* child is root - has no parent */
+ if ( !slapi_sdn_isempty(child) )
+ {
+ Slapi_DN childparent;
+ slapi_sdn_init(&childparent);
+ slapi_sdn_get_parent(child,&childparent);
+ rc= (slapi_sdn_compare(parent,&childparent)==0);
+ slapi_sdn_done(&childparent);
+ }
+ return( rc );
+}
+
+int
+slapi_sdn_isgrandparent( const Slapi_DN *parent, const Slapi_DN *child )
+{
+ int rc= 0;
+
+ /* child is root - has no parent */
+ if ( !slapi_sdn_isempty(child) )
+ {
+ Slapi_DN childparent;
+ slapi_sdn_init(&childparent);
+ slapi_sdn_get_parent(child,&childparent);
+ if ( !slapi_sdn_isempty(&childparent) )
+ {
+ Slapi_DN childchildparent;
+ slapi_sdn_init(&childchildparent);
+ slapi_sdn_get_parent(&childparent,&childchildparent);
+ rc= (slapi_sdn_compare(parent,&childchildparent)==0);
+ slapi_sdn_done(&childchildparent);
+ }
+ slapi_sdn_done(&childparent);
+ }
+ return( rc );
+}
+
+/*
+ * Return non-zero if "dn" matches the scoping criteria
+ * given by "base" and "scope".
+ */
+int
+slapi_sdn_scope_test( const Slapi_DN *dn, const Slapi_DN *base, int scope )
+{
+ int rc = 0;
+
+ switch ( scope ) {
+ case LDAP_SCOPE_BASE:
+ rc = ( slapi_sdn_compare( dn, base ) == 0 );
+ break;
+ case LDAP_SCOPE_ONELEVEL:
+ rc = ( slapi_sdn_isparent( base, dn ) != 0 );
+ break;
+ case LDAP_SCOPE_SUBTREE:
+ rc = ( slapi_sdn_issuffix( dn, base ) != 0 );
+ break;
+ }
+ return rc;
+}
+
+/*
+ * build the new dn of an entry for moddn operations
+ */
+char *
+slapi_moddn_get_newdn(Slapi_DN *dn_olddn, char *newrdn, char *newsuperiordn)
+{
+ char *newdn;
+
+ if( newsuperiordn!=NULL)
+ {
+ /* construct the new dn */
+ newdn= slapi_dn_plus_rdn(newsuperiordn, newrdn); /* JCM - Use Slapi_RDN */
+ }
+ else
+ {
+ /* construct the new dn */
+ char *pdn;
+ const char *dn= slapi_sdn_get_dn(dn_olddn);
+ pdn = slapi_dn_parent( dn );
+ if ( pdn != NULL )
+ {
+ newdn= slapi_dn_plus_rdn(pdn, newrdn); /* JCM - Use Slapi_RDN */
+ }
+ else
+ {
+ newdn= slapi_ch_strdup(newrdn);
+ }
+ slapi_ch_free( (void**)&pdn );
+ }
+ return newdn;
+}
+
+/* JCM slapi_sdn_get_first ? */
+/* JCM slapi_sdn_get_next ? */
+
+#ifdef SDN_DEBUG
+static void
+sdn_dump( const Slapi_DN *sdn, const char *text)
+{
+ LDAPDebug( LDAP_DEBUG_ANY, "SDN %s ptr=%lx dn=%s\n", text, sdn, (sdn->dn==NULL?"NULL":sdn->dn));
+}
+#endif
diff --git a/ldap/servers/slapd/dse.c b/ldap/servers/slapd/dse.c
new file mode 100644
index 00000000..31c23e3b
--- /dev/null
+++ b/ldap/servers/slapd/dse.c
@@ -0,0 +1,2304 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+/*
+ * dse.c - DSE (DSA-Specific Entry) persistent storage.
+ *
+ * The DSE store is an LDIF file contained in the file
+ * INSTANCEDIR/config/XXX.ldif, where INSTANCEDIR is
+ * the directory of the server instance, and XXX is
+ * dfined by the caller of dse_new.
+ *
+ * In core, the DSEs are stored in an AVL tree, keyed on
+ * DN. Whenever a modification is made to a DSE, the
+ * in-core entry is updated, then dse_write_file() is
+ * called to commit the changes to disk.
+ *
+ * This is designed for a small number of DSEs, say
+ * a maximum of 10 or 20. If large numbers of DSEs
+ * need to be stored, this approach of writing out
+ * the entire contents on every modification will
+ * be insufficient.
+ *
+ */
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <prio.h>
+#include <prcountr.h>
+#include "slap.h"
+
+#if !defined (_WIN32)
+#include <pwd.h>
+#endif /* _WIN32 */
+
+/* #define SLAPI_DSE_DEBUG */ /* define this to force trace log */
+ /* messages to always be logged */
+
+#ifdef SLAPI_DSE_DEBUG
+#define SLAPI_DSE_TRACELEVEL LDAP_DEBUG_ANY
+#else /* SLAPI_DSE_DEBUG */
+#define SLAPI_DSE_TRACELEVEL LDAP_DEBUG_TRACE
+#endif /* SLAPI_DSE_DEBUG */
+
+#define STOP_TRAVERSAL -2
+
+/* This is returned by dupentry_replace if the duplicate entry was found and
+ replaced. This is returned up through the avl_insert() in
+ dse_replace_entry(). Otherwise, if avl_insert() returns 0, the
+ entry was added i.e. a duplicate was not found.
+*/
+#define DSE_ENTRY_WAS_REPLACED -3
+/* This is returned by dupentry_merge if the duplicate entry was found and
+ merged. This is returned up through the avl_insert() in dse_add_entry_pb().
+ Otherwise, if avl_insert() returns 0, the
+ entry was added i.e. a duplicate was not found.
+*/
+#define DSE_ENTRY_WAS_MERGED -4
+
+/* some functions can be used either from within a lock or "standalone" */
+#define DSE_USE_LOCK 1
+#define DSE_NO_LOCK 0
+
+struct dse_callback
+{
+ int operation;
+ int flags;
+ Slapi_DN *base;
+ int scope;
+ char *filter; /* NULL means match all entries */
+ Slapi_Filter *slapifilter; /* NULL means match all entries */
+ int (*fn)(Slapi_PBlock *,Slapi_Entry *,Slapi_Entry *,int*,char*,void *);
+ void *fn_arg;
+ struct dse_callback *next;
+};
+
+struct dse
+{
+ char *dse_filename; /* these are the primary files which get read from */
+ char *dse_tmpfile; /* and written to when changes are made via LDAP */
+ char *dse_fileback; /* contain the latest info, just before a new change */
+ char *dse_filestartOK; /* contain the latest info with which the server has successfully started */
+ Avlnode *dse_tree;
+ struct dse_callback *dse_callback;
+ PRRWLock *dse_rwlock; /* a read-write lock to protect the whole dse backend */
+ char **dse_filelist; /* these are additional read only files used to */
+ /* initialize the dse */
+ int dse_is_updateable; /* if non-zero, this DSE can be written to */
+ int dse_readonly_error_reported; /* used to ensure that read-only errors are logged only once */
+};
+
+struct dse_node
+{
+ Slapi_Entry *entry;
+};
+
+/* search set stuff - used to pass search results to the frontend */
+typedef struct dse_search_set
+{
+ DataList dl;
+ int current_entry;
+} dse_search_set;
+
+static int dse_permission_to_write(struct dse* pdse, int loglevel);
+static int dse_write_file_nolock(struct dse* pdse);
+static int dse_apply_nolock(struct dse* pdse, IFP fp, caddr_t arg);
+static int dse_replace_entry( struct dse* pdse, Slapi_Entry *e, int write_file, int use_lock );
+static dse_search_set* dse_search_set_new ();
+static void dse_search_set_delete (dse_search_set *ss);
+static void dse_search_set_clean (dse_search_set *ss);
+static void dse_free_entry (void **data);
+static void dse_search_set_add_entry (dse_search_set *ss, Slapi_Entry *e);
+static Slapi_Entry* dse_search_set_get_next_entry (dse_search_set *ss);
+static int dse_add_entry_pb(struct dse* pdse, Slapi_Entry *e, Slapi_PBlock *pb);
+static struct dse_node *dse_find_node( struct dse* pdse, const Slapi_DN *dn );
+
+/*
+ richm: In almost all modes e.g. db2ldif, ldif2db, etc. we do not need/want
+ to write out the dse.ldif and ldbm.ldif files. The only mode which really
+ needs to write out the file is the regular server mode. The variable
+ dont_ever_write_dse_files tells dse_write_file_nolock whether or not to write
+ the .ldif file for the entry. The default is 1, which means never write the
+ file. The server, when it starts up in regular mode, must call
+ dse_unset_dont_ever_write_dse_files() to enable this file to be written
+*/
+static int dont_ever_write_dse_files = 1;
+
+/* Forward declarations */
+static int entry_dn_cmp( caddr_t d1, caddr_t d2 );
+static int dupentry_disallow( caddr_t d1, caddr_t d2 );
+static int dupentry_merge( caddr_t d1, caddr_t d2 );
+static int dse_write_entry( caddr_t data, caddr_t arg );
+static int ldif_record_end( char *p );
+static int dse_call_callback(struct dse* pdse, Slapi_PBlock *pb, int operation, int flags, Slapi_Entry *entryBefore, Slapi_Entry *entryAfter, int *returncode, char* returntext);
+
+/*
+ * Map a DN onto a dse_node.
+ * Returns NULL if not found.
+ * You must have a read or write lock on the dse_rwlock while
+ * using the returned node.
+ */
+static struct dse_node *
+dse_find_node( struct dse* pdse, const Slapi_DN *dn )
+{
+ struct dse_node *n = NULL;
+ if ( NULL != dn )
+ {
+ struct dse_node searchNode;
+ Slapi_Entry *fe= slapi_entry_alloc();
+ slapi_entry_init(fe, NULL, NULL);
+ slapi_entry_set_sdn(fe,dn);
+ searchNode.entry= fe;
+
+ n = (struct dse_node *)avl_find( pdse->dse_tree, &searchNode, entry_dn_cmp );
+
+ slapi_entry_free(fe);
+ }
+ return n;
+}
+
+static int counters_created= 0;
+PR_DEFINE_COUNTER(dse_entries_exist);
+
+/*
+ * Map a DN onto a real Entry.
+ * Returns NULL if not found.
+ */
+static Slapi_Entry *
+dse_get_entry_copy( struct dse* pdse, const Slapi_DN *dn, int use_lock )
+{
+ Slapi_Entry *e= NULL;
+ struct dse_node *n;
+
+ if (use_lock == DSE_USE_LOCK)
+ PR_RWLock_Rlock(pdse->dse_rwlock);
+
+ n = dse_find_node( pdse, dn );
+ if(n!=NULL)
+ {
+ e = slapi_entry_dup(n->entry);
+ }
+
+ if (use_lock == DSE_USE_LOCK)
+ PR_RWLock_Unlock(pdse->dse_rwlock);
+
+ return e;
+}
+
+static struct dse_callback *
+dse_callback_new(int operation, int flags, const Slapi_DN *base, int scope, const char *filter, dseCallbackFn fn, void *fn_arg)
+{
+ struct dse_callback *p= NULL;
+ p = (struct dse_callback *)slapi_ch_malloc(sizeof(struct dse_callback));
+ if (p!=NULL) {
+ p->operation= operation;
+ p->flags = flags;
+ p->base= slapi_sdn_dup(base);
+ p->scope= scope;
+ if ( NULL == filter ) {
+ p->filter= NULL;
+ p->slapifilter= NULL;
+ } else {
+ p->filter= slapi_ch_strdup(filter);
+ p->slapifilter= slapi_str2filter( p->filter );
+ filter_normalize(p->slapifilter);
+ }
+ p->fn= fn;
+ p->fn_arg= fn_arg;
+ p->next= NULL;
+ }
+ return p;
+}
+
+static void
+dse_callback_delete(struct dse_callback **pp)
+{
+ if (pp!=NULL) {
+ slapi_sdn_free(&((*pp)->base));
+ slapi_ch_free((void**)&((*pp)->filter));
+ slapi_filter_free((*pp)->slapifilter,1);
+ slapi_ch_free((void**)pp);
+ }
+}
+
+static void
+dse_callback_deletelist(struct dse_callback **pp)
+{
+ if(pp!=NULL)
+ {
+ struct dse_callback *p, *n;
+ for(p= *pp;p!=NULL;)
+ {
+ n= p->next;
+ dse_callback_delete(&p);
+ p= n;
+ }
+ }
+}
+
+/*
+ Makes a copy of the entry passed in, so it's const
+*/
+static struct dse_node *
+dse_node_new(const Slapi_Entry *entry)
+{
+ struct dse_node *p= NULL;
+ p= (struct dse_node *)slapi_ch_malloc(sizeof(struct dse_node));
+ if(p!=NULL)
+ {
+ p->entry= slapi_entry_dup(entry);
+ }
+ if(!counters_created)
+ {
+ PR_CREATE_COUNTER(dse_entries_exist,"DSE","entries","");
+ counters_created= 1;
+ }
+ PR_INCREMENT_COUNTER(dse_entries_exist);
+ return p;
+}
+
+static void
+dse_node_delete(struct dse_node **pp)
+{
+ slapi_entry_free((*pp)->entry);
+ slapi_ch_free((void **)&(*pp));
+ PR_DECREMENT_COUNTER(dse_entries_exist);
+}
+
+static void
+dse_callback_addtolist(struct dse_callback **pplist, struct dse_callback *p)
+{
+ if(pplist!=NULL)
+ {
+ p->next= NULL;
+ if(*pplist==NULL)
+ {
+ *pplist= p;
+ }
+ else
+ {
+ struct dse_callback *t= *pplist;
+ for(;t->next!=NULL;t= t->next);
+ t->next= p;
+ }
+ }
+}
+
+static void
+dse_callback_removefromlist(struct dse_callback **pplist, int operation, int flags, const Slapi_DN *base, int scope, const char *filter, dseCallbackFn fn)
+{
+ if (pplist != NULL) {
+ struct dse_callback *t= *pplist;
+ struct dse_callback *prev= NULL;
+ for(; t!=NULL; ) {
+ if ((t->operation == operation) && (t->flags == flags) &&
+ (t->fn == fn) && (scope == t->scope) &&
+ (slapi_sdn_compare(base,t->base) == 0) &&
+ (( NULL == filter && NULL == t->filter ) ||
+ (strcasecmp(filter, t->filter) == 0))) {
+ if (prev == NULL) {
+ *pplist= t->next;
+ } else {
+ prev->next= t->next;
+ }
+ dse_callback_delete(&t);
+ t= NULL;
+ } else {
+ prev= t;
+ t= t->next;
+ }
+ }
+ }
+}
+
+/*
+ * Create a new dse structure.
+ */
+struct dse *
+dse_new( char *filename, char *tmpfilename, char *backfilename, char *startokfilename, const char *configdir)
+{
+ struct dse *pdse= NULL;
+ const char *config_sub_dir = "config";
+ char *id = config_get_instancedir();
+ char *realconfigdir = NULL;
+
+ if (configdir!=NULL) {
+ realconfigdir = slapi_ch_malloc(strlen(configdir)+1);
+ strcpy(realconfigdir, configdir);
+ } else if (id!=NULL) {
+ realconfigdir = slapi_ch_malloc(strlen(id)+strlen(config_sub_dir)+3);
+ sprintf(realconfigdir, "%s/%s", id, config_sub_dir);
+ }
+ if(realconfigdir!=NULL)
+ {
+ pdse= (struct dse *)slapi_ch_calloc(1, sizeof(struct dse));
+ if(pdse!=NULL)
+ {
+ pdse->dse_rwlock = PR_NewRWLock(PR_RWLOCK_RANK_NONE,"dse lock");
+ /* Set the full path name for the config DSE entry */
+ if (!strstr(filename, realconfigdir))
+ {
+ pdse->dse_filename = slapi_ch_malloc( strlen( realconfigdir ) +
+ strlen( filename ) + 3 );
+ sprintf( pdse->dse_filename, "%s/%s", realconfigdir, filename );
+ }
+ else
+ pdse->dse_filename = slapi_ch_strdup(filename);
+
+ if (!strstr(tmpfilename, realconfigdir)) {
+ pdse->dse_tmpfile = slapi_ch_malloc( strlen( realconfigdir ) +
+ strlen( tmpfilename ) + 3 );
+ sprintf( pdse->dse_tmpfile, "%s/%s", realconfigdir, tmpfilename );
+ }
+ else
+ pdse->dse_tmpfile = slapi_ch_strdup(tmpfilename);
+
+ if ( backfilename != NULL )
+ {
+ if (!strstr(backfilename, realconfigdir)) {
+ pdse->dse_fileback = slapi_ch_malloc( strlen( realconfigdir ) +
+ strlen( backfilename ) + 3 );
+ sprintf( pdse->dse_fileback, "%s/%s", realconfigdir, backfilename );
+ }
+ else
+ pdse->dse_fileback = slapi_ch_strdup(backfilename);
+ }
+ else
+ pdse->dse_fileback = NULL;
+
+ if ( startokfilename != NULL )
+ {
+ if (!strstr(startokfilename, realconfigdir)) {
+ pdse->dse_filestartOK = slapi_ch_malloc( strlen( realconfigdir ) +
+ strlen( startokfilename ) + 3 );
+ sprintf( pdse->dse_filestartOK, "%s/%s", realconfigdir, startokfilename );
+ }
+ else
+ pdse->dse_filestartOK = slapi_ch_strdup(startokfilename);
+ }
+ else
+ pdse->dse_filestartOK = NULL;
+
+ pdse->dse_tree= NULL;
+ pdse->dse_callback= NULL;
+ pdse->dse_is_updateable = dse_permission_to_write(pdse,
+ SLAPI_LOG_TRACE);
+ }
+ slapi_ch_free( (void **) &realconfigdir );
+ }
+ slapi_ch_free( (void **) &id );
+ return pdse;
+}
+
+/*
+ * Create a new dse structure with a file list
+ */
+struct dse *
+dse_new_with_filelist(char *filename, char *tmpfilename, char *backfilename, char *startokfilename, const char *configdir,
+ char **filelist)
+{
+ struct dse *newdse = dse_new(filename, tmpfilename, backfilename, startokfilename, configdir);
+ newdse->dse_filelist = filelist;
+ return newdse;
+}
+
+static int
+dse_internal_delete_entry( caddr_t data, caddr_t arg )
+{
+ struct dse_node *n = (struct dse_node *)data;
+ dse_node_delete(&n);
+ return 0;
+}
+
+/*
+ * Get rid of a dse structure.
+ */
+int
+dse_deletedse(Slapi_PBlock *pb)
+{
+ struct dse *pdse = NULL;
+
+ slapi_pblock_get(pb, SLAPI_PLUGIN_PRIVATE, &pdse);
+
+ if (pdse)
+ {
+ int nentries = 0;
+ PR_RWLock_Wlock(pdse->dse_rwlock);
+ slapi_ch_free((void **)&(pdse->dse_filename));
+ slapi_ch_free((void **)&(pdse->dse_tmpfile));
+ slapi_ch_free((void **)&(pdse->dse_fileback));
+ slapi_ch_free((void **)&(pdse->dse_filestartOK));
+ dse_callback_deletelist(&pdse->dse_callback);
+ nentries = avl_free(pdse->dse_tree, dse_internal_delete_entry);
+ PR_RWLock_Unlock(pdse->dse_rwlock);
+ PR_DestroyRWLock(pdse->dse_rwlock);
+ slapi_ch_free((void **)&pdse);
+ LDAPDebug( SLAPI_DSE_TRACELEVEL, "Removed [%d] entries from the dse tree.\n",
+ nentries,0,0 );
+ }
+
+ /* data is freed, so make sure no one tries to use it */
+ slapi_pblock_set(pb, SLAPI_PLUGIN_PRIVATE, NULL);
+
+ return 0;
+}
+
+static char* subordinatecount = "numsubordinates";
+
+/*
+ * Get the number of subordinates for this entry.
+ */
+static size_t
+dse_numsubordinates(Slapi_Entry *entry)
+{
+ int ret= 0;
+ Slapi_Attr *read_attr = NULL;
+ size_t current_sub_count = 0;
+
+ /* Get the present value of the subcount attr, or 0 if not present */
+ ret = slapi_entry_attr_find(entry,subordinatecount,&read_attr);
+ if (0 == ret)
+ {
+ /* decode the value */
+ Slapi_Value *sval;
+ slapi_attr_first_value( read_attr,&sval );
+ if (sval!=NULL)
+ {
+ const struct berval *bval = slapi_value_get_berval( sval );
+ if( NULL != bval )
+ current_sub_count = atol(bval->bv_val);
+ }
+ }
+ return current_sub_count;
+}
+
+/*
+ * Update the numsubordinates count.
+ * mod_op is either an Add or Delete.
+ */
+static void
+dse_updateNumSubordinates(Slapi_Entry *entry, int op)
+{
+ int ret= 0;
+ int mod_op = 0;
+ Slapi_Attr *read_attr = NULL;
+ size_t current_sub_count = 0;
+ int already_present = 0;
+
+ /* For now, we're only interested in subordinatecount.
+ We first examine the present value for the attribute.
+ If it isn't present and we're adding, we assign value 1 to the attribute and add it.
+ If it is present, we increment or decrement depending upon whether we're adding or deleting.
+ If the value after decrementing is zero, we remove it.
+ */
+
+ /* Get the present value of the subcount attr, or 0 if not present */
+ ret = slapi_entry_attr_find(entry,subordinatecount,&read_attr);
+ if (0 == ret)
+ {
+ /* decode the value */
+ Slapi_Value *sval;
+ slapi_attr_first_value( read_attr,&sval );
+ if (sval!=NULL)
+ {
+ const struct berval *bval = slapi_value_get_berval( sval );
+ if (bval!=NULL)
+ {
+ already_present = 1;
+ current_sub_count = atol(bval->bv_val);
+ }
+ }
+ }
+
+ /* are we adding ? */
+ if ( (SLAPI_OPERATION_ADD == op) && !already_present)
+ {
+ /* If so, and the parent entry does not already have a subcount attribute, we need to add it */
+ mod_op = LDAP_MOD_ADD;
+ }
+ else
+ {
+ if (SLAPI_OPERATION_DELETE == op)
+ {
+ if (!already_present)
+ {
+ /* This means that something is wrong---deleting a child but no subcount present on parent */
+ slapi_log_error( SLAPI_LOG_FATAL, "dse",
+ "numsubordinates assertion failure\n" );
+ return;
+ }
+ else
+ {
+ if (current_sub_count == 1)
+ {
+ mod_op = LDAP_MOD_DELETE;
+ }
+ else
+ {
+ mod_op = LDAP_MOD_REPLACE;
+ }
+ }
+ }
+ else
+ {
+ mod_op = LDAP_MOD_REPLACE;
+ }
+ }
+
+ /* Now compute the new value */
+ if (SLAPI_OPERATION_ADD == op)
+ {
+ current_sub_count++;
+ }
+ else
+ {
+ current_sub_count--;
+ }
+ {
+ char value_buffer[20]; /* enough digits for 2^64 children */
+ struct berval *vals[2];
+ struct berval val;
+ vals[0] = &val;
+ vals[1] = NULL;
+ sprintf(value_buffer,"%u",current_sub_count);
+ val.bv_val = value_buffer;
+ val.bv_len = strlen (val.bv_val);
+ switch(mod_op)
+ {
+ case LDAP_MOD_ADD:
+ attrlist_merge( &entry->e_attrs, subordinatecount, vals);
+ break;
+ case LDAP_MOD_REPLACE:
+ attrlist_replace( &entry->e_attrs, subordinatecount, vals);
+ break;
+ case LDAP_MOD_DELETE:
+ attrlist_delete( &entry->e_attrs, subordinatecount);
+ break;
+ }
+ }
+}
+
+/* the write lock should always be acquired before calling this function */
+static void
+dse_updateNumSubOfParent(struct dse *pdse, const Slapi_DN *child, int op)
+{
+ Slapi_DN parent;
+ slapi_sdn_init(&parent);
+ slapi_sdn_get_parent(child, &parent);
+ if ( !slapi_sdn_isempty(&parent) )
+ {
+ /* no lock because caller should already have the write lock */
+ Slapi_Entry *parententry= dse_get_entry_copy( pdse, &parent, DSE_NO_LOCK );
+ if( parententry!=NULL )
+ {
+ /* Decrement the numsubordinate count of the parent entry */
+ dse_updateNumSubordinates(parententry, op);
+ /* no lock because caller should always have the write lock */
+ dse_replace_entry( pdse, parententry, 0, DSE_NO_LOCK );
+ slapi_entry_free(parententry);
+ }
+ }
+ slapi_sdn_done(&parent);
+}
+
+static int
+dse_read_one_file(struct dse *pdse, const char *filename, Slapi_PBlock *pb,
+ int primary_file )
+{
+ Slapi_Entry *e= NULL;
+ char *entrystr= NULL;
+ char *buf = NULL;
+ char *lastp = NULL;
+ int rc= 0; /* Fail */
+ PRInt32 remaining;
+ PRInt32 nr = 0;
+ PRFileInfo prfinfo;
+ PRFileDesc *prfd = 0;
+
+ if ( (NULL != pdse) && (NULL != filename) )
+ {
+ if ( (rc = PR_GetFileInfo( filename, &prfinfo )) != PR_SUCCESS )
+ {
+ /* the "real" file does not exist; see if there is a tmpfile */
+ if ( pdse->dse_tmpfile &&
+ PR_GetFileInfo( pdse->dse_tmpfile, &prfinfo ) == PR_SUCCESS ) {
+ rc = PR_Rename(pdse->dse_tmpfile, filename);
+ if (rc == PR_SUCCESS) {
+ slapi_log_error(SLAPI_LOG_FATAL, "dse",
+ "The configuration file %s was restored from backup %s\n",
+ filename, pdse->dse_tmpfile);
+ rc = 1;
+ } else {
+ slapi_log_error(SLAPI_LOG_FATAL, "dse",
+ "The configuration file %s was not restored from backup %s, error %d\n",
+ filename, pdse->dse_tmpfile, rc);
+ rc = 0;
+ }
+ } else {
+ rc = 0; /* fail */
+ }
+ }
+ if ( (rc = PR_GetFileInfo( filename, &prfinfo )) != PR_SUCCESS )
+ {
+ slapi_log_error(SLAPI_LOG_FATAL, "dse",
+ "The configuration file %s could not be accessed, error %d\n",
+ filename, rc);
+ rc = 0; /* Fail */
+ }
+ else if (( prfd = PR_Open( filename, PR_RDONLY, SLAPD_DEFAULT_FILE_MODE )) == NULL )
+ {
+ slapi_log_error(SLAPI_LOG_FATAL, "dse",
+ "The configuration file %s could not be read. "
+ SLAPI_COMPONENT_NAME_NSPR " %d (%s)\n",
+ filename,
+ PR_GetError(), slapd_pr_strerror(PR_GetError()));
+ rc = 0; /* Fail */
+ }
+ else
+ {
+ int done= 0;
+ /* read the entire file into core */
+ buf = slapi_ch_malloc( prfinfo.size + 1 );
+ remaining = prfinfo.size;
+ if (( nr = slapi_read_buffer( prfd, buf, prfinfo.size )) < 0 )
+ {
+ slapi_log_error(SLAPI_LOG_FATAL, "dse",
+ "Could only read %d of %d bytes from config file %s\n",
+ nr, prfinfo.size, filename);
+ rc = 0; /* Fail */
+ done= 1;
+ }
+
+ (void)PR_Close( prfd );
+ buf[ nr ] = '\0';
+
+ if(!done)
+ {
+ int dont_check_dups = 0;
+ int str2entry_flags = SLAPI_STR2ENTRY_EXPAND_OBJECTCLASSES |
+ SLAPI_STR2ENTRY_NOT_WELL_FORMED_LDIF ;
+
+ PR_ASSERT(pb);
+ slapi_pblock_get(pb, SLAPI_DSE_DONT_CHECK_DUPS, &dont_check_dups);
+ if ( !dont_check_dups ) {
+ str2entry_flags |= SLAPI_STR2ENTRY_REMOVEDUPVALS;
+ }
+
+ /* Convert LDIF to entry structures */
+ rc= 1; /* assume we will succeed */
+ while (( entrystr = dse_read_next_entry( buf, &lastp )) != NULL )
+ {
+ e = slapi_str2entry( entrystr, str2entry_flags );
+ if ( e != NULL )
+ {
+ int returncode = 0;
+ char returntext[SLAPI_DSE_RETURNTEXT_SIZE]= {0};
+
+ LDAPDebug(SLAPI_DSE_TRACELEVEL, "dse_read_one_file"
+ " processing entry \"%s\" in file %s%s\n",
+ slapi_entry_get_dn_const(e), filename,
+ primary_file ? " (primary file)" : "" );
+
+ /* remove the numsubordinates attr, which may be bogus */
+ slapi_entry_attr_delete(e, subordinatecount);
+
+ /* set the "primary file" flag if appropriate */
+ slapi_pblock_set( pb, SLAPI_DSE_IS_PRIMARY_FILE, &primary_file );
+ if(dse_call_callback(pdse, pb, DSE_OPERATION_READ,
+ DSE_FLAG_PREOP, e, NULL, &returncode,
+ returntext) == SLAPI_DSE_CALLBACK_OK)
+ {
+ /* this will free the entry if not added, so it is
+ definitely consumed by this call */
+ dse_add_entry_pb(pdse, e, pb);
+ }
+ else /* free entry if not used */
+ {
+ slapi_log_error(SLAPI_LOG_FATAL, "dse",
+ "The entry %s in file %s is invalid, error code %d (%s) - %s\n",
+ slapi_entry_get_dn_const(e),
+ filename, returncode,
+ ldap_err2string(returncode),
+ returntext);
+ slapi_entry_free(e);
+ rc = 0; /* failure */
+ }
+ } else {
+ slapi_log_error( SLAPI_LOG_FATAL, "dse",
+ "parsing dse entry [%s]\n", entrystr );
+ rc = 0; /* failure */
+ }
+ }
+ }
+ slapi_ch_free((void **)&buf);
+ }
+ }
+
+ return rc;
+}
+
+/*
+ * Read the file we were initialised with into memory.
+ * If not NULL call entry_filter_fn on each entry as it's read.
+ * The function is free to modify the entry before it's places
+ * into the AVL tree. True means add the entry. False means don't.
+ *
+ * Return 1 for OK, 0 for Fail.
+ */
+int
+dse_read_file(struct dse *pdse, Slapi_PBlock *pb)
+{
+ int rc= 1; /* Good */
+ int ii;
+ char **filelist = 0;
+ char *filename = 0;
+
+ filelist = charray_dup(pdse->dse_filelist);
+ filename = slapi_ch_strdup(pdse->dse_filename);
+
+ for (ii = 0; rc && filelist && filelist[ii]; ++ii)
+ {
+ if (strcasecmp(filename, filelist[ii])!=0)
+ {
+ rc = dse_read_one_file(pdse, filelist[ii], pb, 0 /* not primary */);
+ }
+ }
+
+ if (rc)
+ {
+ rc = dse_read_one_file(pdse, filename, pb, 1 /* primary file */);
+ }
+
+ charray_free(filelist);
+ slapi_ch_free((void **)&filename);
+
+ return rc;
+}
+
+/*
+ * Structure to carry context information whilst
+ * traversing the tree writing the entries to disk.
+ */
+typedef struct _fpw
+{
+ PRFileDesc *fpw_prfd;
+ int fpw_rc;
+ struct dse *fpw_pdse;
+} FPWrapper;
+
+
+static int
+dse_rw_permission_to_one_file(const char *name, int loglevel)
+{
+ PRErrorCode prerr = 0;
+ const char *accesstype = "";
+
+ if ( NULL == name ) {
+ return 1; /* file won't be used -- return "sufficient permission" */
+ }
+
+ if ( PR_Access( name, PR_ACCESS_EXISTS ) == PR_SUCCESS ) {
+ /* file exists: check for read and write permission */
+ if ( PR_Access( name, PR_ACCESS_WRITE_OK ) != PR_SUCCESS ) {
+ prerr = PR_GetError();
+ accesstype = "write";
+ } else if ( PR_Access( name, PR_ACCESS_READ_OK ) != PR_SUCCESS ) {
+ prerr = PR_GetError();
+ accesstype = "read";
+ }
+ } else {
+ /* file does not exist: make sure we can create it */
+ PRFileDesc *prfd;
+
+ prfd = PR_Open( name, PR_RDWR | PR_CREATE_FILE | PR_TRUNCATE,
+ SLAPD_DEFAULT_FILE_MODE );
+ if ( NULL == prfd ) {
+ prerr = PR_GetError();
+ accesstype = "create";
+ } else {
+ PR_Close( prfd );
+ PR_Delete( name );
+ }
+ }
+
+ if ( prerr != 0 ) {
+ slapi_log_error( loglevel, "dse", "Unable to %s \"%s\": "
+ SLAPI_COMPONENT_NAME_NSPR " error %d (%s)\n",
+ accesstype, name, prerr, slapd_pr_strerror(prerr));
+ return 0; /* insufficient permission */
+ } else {
+ return 1; /* sufficient permission */
+ }
+}
+
+
+/*
+ * Check that we have permission to write to all the files that
+ * dse_write_file_nolock() uses.
+ * Returns a non-zero value if sufficient permission and 0 if not.
+ */
+static int
+dse_permission_to_write(struct dse* pdse, int loglevel)
+{
+ int rc = 1; /* sufficient permission */
+
+ if ( NULL != pdse->dse_filename ) {
+ if ( !dse_rw_permission_to_one_file( pdse->dse_filename, loglevel ) ||
+ !dse_rw_permission_to_one_file( pdse->dse_fileback, loglevel ) ||
+ !dse_rw_permission_to_one_file( pdse->dse_tmpfile, loglevel )) {
+ rc = 0; /* insufficient permission */
+ }
+ }
+
+ return rc;
+}
+
+
+/*
+ * Check for read-only status and return an appropriate error to the
+ * LDAP client.
+ * Returns 0 if no error was returned and non-zero if one was.
+ */
+static int
+dse_check_for_readonly_error(Slapi_PBlock *pb, struct dse* pdse)
+{
+ int rc = 0; /* default: no error */
+
+ PR_RWLock_Rlock(pdse->dse_rwlock);
+
+ if ( !pdse->dse_is_updateable ) {
+ if ( !pdse->dse_readonly_error_reported ) {
+ if ( NULL != pdse->dse_filename ) {
+ slapi_log_error( SLAPI_LOG_FATAL, "dse",
+ "The DSE database stored in \"%s\" is not writeable\n",
+ pdse->dse_filename );
+ /* log the details too */
+ (void)dse_permission_to_write(pdse, SLAPI_LOG_FATAL);
+ }
+ pdse->dse_readonly_error_reported = 1;
+ }
+ rc = 1; /* return an error to the client */
+ }
+
+ PR_RWLock_Unlock(pdse->dse_rwlock);
+
+ if ( rc != 0 ) {
+ slapi_send_ldap_result( pb, LDAP_UNWILLING_TO_PERFORM, NULL,
+ "DSE database is read-only", 0, NULL );
+ }
+
+ return rc; /* no error */
+}
+
+
+/*
+ * Write the AVL tree of entries back to the LDIF file.
+ */
+static int
+dse_write_file_nolock(struct dse* pdse)
+{
+ FPWrapper fpw;
+ int rc = 0;
+
+ if (dont_ever_write_dse_files)
+ return rc;
+
+ fpw.fpw_rc = 0;
+ fpw.fpw_prfd = NULL;
+
+ if ( NULL != pdse->dse_filename )
+ {
+ if (( fpw.fpw_prfd = PR_Open( pdse->dse_tmpfile, PR_RDWR | PR_CREATE_FILE | PR_TRUNCATE, SLAPD_DEFAULT_FILE_MODE )) == NULL )
+ {
+ rc = PR_GetOSError();
+ slapi_log_error( SLAPI_LOG_FATAL, "dse", "Cannot open "
+ "temporary DSE file \"%s\" for update: OS error %d (%s)\n",
+ pdse->dse_tmpfile, rc, slapd_system_strerror( rc ));
+ }
+ else
+ {
+ fpw.fpw_pdse = pdse;
+ if ( avl_apply( pdse->dse_tree, dse_write_entry, &fpw, STOP_TRAVERSAL, AVL_INORDER ) == STOP_TRAVERSAL )
+ {
+ rc = fpw.fpw_rc;
+ slapi_log_error( SLAPI_LOG_FATAL, "dse", "Cannot write "
+ " temporary DSE file \"%s\": OS error %d (%s)\n",
+ pdse->dse_tmpfile, rc, slapd_system_strerror( rc ));
+ (void)PR_Close( fpw.fpw_prfd );
+ fpw.fpw_prfd = NULL;
+ }
+ else
+ {
+ (void)PR_Close( fpw.fpw_prfd );
+ fpw.fpw_prfd = NULL;
+ if ( pdse->dse_fileback != NULL )
+ {
+ rc = slapi_destructive_rename( pdse->dse_filename, pdse->dse_fileback );
+ if ( rc != 0 )
+ {
+ slapi_log_error( SLAPI_LOG_FATAL, "dse", "Cannot backup"
+ " DSE file \"%s\" to \"%s\": OS error %d (%s)\n",
+ pdse->dse_filename, pdse->dse_fileback,
+ rc, slapd_system_strerror( rc ));
+ }
+ }
+ rc = slapi_destructive_rename( pdse->dse_tmpfile, pdse->dse_filename );
+ if ( rc != 0 )
+ {
+ slapi_log_error( SLAPI_LOG_FATAL, "dse", "Cannot rename"
+ " temporary DSE file \"%s\" to \"%s\":"
+ " OS error %d (%s)\n",
+ pdse->dse_tmpfile, pdse->dse_filename,
+ rc, slapd_system_strerror( rc ));
+ }
+ }
+ }
+ if (fpw.fpw_prfd)
+ (void)PR_Close(fpw.fpw_prfd);
+ }
+
+ return rc;
+}
+
+/*
+ * Local function for writing an entry to a file.
+ * Called by the AVL code during traversal.
+ */
+static int
+dse_write_entry( caddr_t data, caddr_t arg )
+{
+ struct dse_node *n = (struct dse_node *)data;
+ FPWrapper *fpw = (FPWrapper *)arg;
+ char *s;
+ PRInt32 len;
+
+ if ( NULL != n && NULL != n->entry )
+ {
+ int returncode;
+ char returntext[SLAPI_DSE_RETURNTEXT_SIZE]= "";
+ /* need to make a duplicate here for two reasons:
+ 1) we don't want to hold on to the raw data in the node for any longer
+ than we have to; we will usually be inside the dse write lock, but . . .
+ 2) the write callback may modify the entry, so we want to pass it a
+ writeable copy rather than the raw avl tree data pointer
+ */
+ Slapi_Entry *ec = slapi_entry_dup(n->entry);
+ if(dse_call_callback(fpw->fpw_pdse, NULL, DSE_OPERATION_WRITE,
+ DSE_FLAG_PREOP, ec, NULL, &returncode, returntext)
+ == SLAPI_DSE_CALLBACK_OK)
+ {
+ /*
+ * 3-August-2000 mcs: We used to pass the SLAPI_DUMP_NOOPATTRS
+ * option to slapi_entry2str_with_options() so that operational
+ * attributes were NOT stored in the DSE LDIF files. But now
+ * we store all attribute types.
+ */
+ if (( s = slapi_entry2str_with_options( ec, &len, 0 )) != NULL )
+ {
+ if ( slapi_write_buffer( fpw->fpw_prfd, s, len ) != len )
+ {
+ fpw->fpw_rc = PR_GetOSError();;
+ slapi_ch_free((void **) &s);
+ return STOP_TRAVERSAL;
+ }
+ if ( slapi_write_buffer( fpw->fpw_prfd, "\n", 1 ) != 1 )
+ {
+ fpw->fpw_rc = PR_GetOSError();;
+ slapi_ch_free((void **) &s);
+ return STOP_TRAVERSAL;
+ }
+ slapi_ch_free((void **) &s);
+ }
+ }
+ slapi_entry_free(ec);
+ }
+ return 0;
+}
+
+static int
+dse_add_entry_pb(struct dse* pdse, Slapi_Entry *e, Slapi_PBlock *pb)
+{
+ int dont_write_file = 0, merge = 0; /* defaults */
+ int rc= 0;
+ struct dse_node *n = dse_node_new(e); /* copies e */
+ Slapi_Entry *schemacheckentry= NULL; /* to use for schema checking */
+
+ PR_ASSERT(pb);
+ slapi_pblock_get(pb, SLAPI_DSE_DONT_WRITE_WHEN_ADDING, &dont_write_file);
+ slapi_pblock_get(pb, SLAPI_DSE_MERGE_WHEN_ADDING, &merge);
+
+ /* keep write lock during both tree update and file write operations */
+ PR_RWLock_Wlock(pdse->dse_rwlock);
+ if (merge)
+ {
+ rc= avl_insert( &(pdse->dse_tree), n, entry_dn_cmp, dupentry_merge );
+ }
+ else
+ {
+ rc= avl_insert( &(pdse->dse_tree), n, entry_dn_cmp, dupentry_disallow );
+ }
+ if (-1 != rc) {
+ /* update num sub of parent with no lock; we already hold the write lock */
+ if (0 == rc) { /* entry was added, not merged; update numsub */
+ dse_updateNumSubOfParent(pdse, slapi_entry_get_sdn_const(e),
+ SLAPI_OPERATION_ADD);
+ } else { /* entry was merged, free temp unused data */
+ dse_node_delete(&n);
+ }
+ if (!dont_write_file) {
+ dse_write_file_nolock(pdse);
+ }
+ } else { /* duplicate entry ignored */
+ dse_node_delete(&n); /* This also deletes the contained entry */
+ }
+ PR_RWLock_Unlock(pdse->dse_rwlock);
+
+ if (rc == -1)
+ {
+ /* duplicate entry ignored */
+ schemacheckentry = dse_get_entry_copy( pdse,
+ slapi_entry_get_sdn_const(e),
+ DSE_USE_LOCK );
+ }
+ else /* entry added or merged */
+ {
+ /* entry was added or merged */
+ if (0 == rc) /* 0 return means entry was added, not merged */
+ {
+ /* save a search of the tree, since we added the entry, the
+ contents of e should be the same as what is in the tree */
+ schemacheckentry = slapi_entry_dup(e);
+ }
+ else /* merged */
+ {
+ /* schema check the new merged entry, so get it from the tree */
+ schemacheckentry = dse_get_entry_copy( pdse,
+ slapi_entry_get_sdn_const(e),
+ DSE_USE_LOCK );
+ }
+ }
+ if ( NULL != schemacheckentry )
+ {
+ /*
+ * Verify that the new or merged entry conforms to the schema.
+ * Errors are logged by slapi_entry_schema_check().
+ */
+ (void)slapi_entry_schema_check( pb, schemacheckentry );
+ slapi_entry_free(schemacheckentry);
+ }
+
+ /* callers expect e (SLAPI_ADD_ENTRY) to be freed or otherwise consumed */
+ slapi_entry_free(e);
+
+ return rc;
+}
+
+/*
+ * Local function for comparing two entries by DN. Store the entries
+ * so that when they are printed out, the child entries are below their
+ * ancestor entries
+ */
+static int
+entry_dn_cmp( caddr_t d1, caddr_t d2 )
+{
+ struct dse_node *n1 = (struct dse_node *)d1;
+ struct dse_node *n2 = (struct dse_node *)d2;
+ const Slapi_DN *dn1 = slapi_entry_get_sdn_const(n1->entry);
+ const Slapi_DN *dn2 = slapi_entry_get_sdn_const(n2->entry);
+ int retval = slapi_sdn_compare(dn1, dn2);
+
+ if (retval != 0)
+ {
+ if (slapi_sdn_issuffix(dn1, dn2))
+ {
+ retval = 1;
+ }
+ else if (slapi_sdn_issuffix(dn2, dn1))
+ {
+ retval = -1;
+ }
+ else
+ {
+ /* put fewer rdns before more rdns */
+ int rc = 0;
+ char **dnlist1 = ldap_explode_dn(slapi_sdn_get_ndn(dn1), 0);
+ char **dnlist2 = ldap_explode_dn(slapi_sdn_get_ndn(dn2), 0);
+ int len1 = 0;
+ int len2 = 0;
+ if (dnlist1)
+ for (len1 = 0; dnlist1[len1]; ++len1);
+ if (dnlist2)
+ for (len2 = 0; dnlist2[len2]; ++len2);
+
+ if (len1 == len2)
+ {
+ len1--;
+ for (; (rc == 0) && (len1 >= 0); --len1)
+ {
+ rc = strcmp(dnlist1[len1], dnlist2[len1]);
+ }
+ if (rc)
+ retval = rc;
+ }
+ else
+ retval = len1 - len2;
+
+ if (dnlist1)
+ ldap_value_free(dnlist1);
+ if (dnlist2)
+ ldap_value_free(dnlist2);
+ }
+ }
+ /* else entries are equal if dns are equal */
+
+ return retval;
+}
+
+
+static int
+dupentry_disallow( caddr_t d1, caddr_t d2 )
+{
+ return -1;
+}
+
+
+static int
+dupentry_replace( caddr_t d1, caddr_t d2 )
+{
+ /*
+ * Hack attack: since we don't have the address of the pointer
+ * in the avl node, we have to replace the e_dn and e_attrs
+ * members of the entry which is in the AVL tree with our
+ * new entry DN and attrs. We then point the "new" entry's
+ * e_dn and e_attrs pointers to point to the values we just
+ * replaced, on the assumption that the caller will be freeing
+ * these.
+ */
+ struct dse_node *n1 = (struct dse_node *)d1; /* OLD */
+ struct dse_node *n2 = (struct dse_node *)d2; /* NEW */
+ Slapi_Entry *e= n1->entry;
+ n1->entry= n2->entry;
+ n2->entry= e;
+ return DSE_ENTRY_WAS_REPLACED;
+}
+
+static int
+dupentry_merge( caddr_t d1, caddr_t d2 )
+{
+ struct dse_node *n1 = (struct dse_node *)d1; /* OLD */
+ struct dse_node *n2 = (struct dse_node *)d2; /* NEW */
+ Slapi_Entry *e1= n1->entry;
+ Slapi_Entry *e2= n2->entry;
+ int rc = 0;
+ Slapi_Attr *newattr = 0;
+
+ for (rc = slapi_entry_first_attr(e2, &newattr);
+ !rc && newattr;
+ rc = slapi_entry_next_attr(e2, newattr, &newattr)) {
+ char *type = 0;
+ slapi_attr_get_type(newattr, &type);
+ if (type) {
+ /* insure there are no duplicate values in e1 */
+ rc = slapi_entry_merge_values_sv(e1, type,
+ attr_get_present_values(newattr));
+ }
+ }
+
+ return DSE_ENTRY_WAS_MERGED;
+}
+
+/*
+ * Add an entry to the DSE without locking the DSE avl tree.
+ * Replaces the entry if it already exists.
+ *
+ * The given entry e is never consumed. It is the responsibility of the
+ * caller to free it when it is no longer needed.
+ *
+ * The write_file flag is used if we want to update the entry in memory
+ * but we do not want to write out the file. For example, if we update
+ * the numsubordinates in the entry, this is an operational attribute that
+ * we do not want saved to disk.
+ */
+static int
+dse_replace_entry( struct dse* pdse, Slapi_Entry *e, int write_file, int use_lock )
+{
+ int rc= -1;
+ if ( NULL != e )
+ {
+ struct dse_node *n= dse_node_new(e);
+ if (use_lock)
+ PR_RWLock_Wlock(pdse->dse_rwlock);
+ rc = avl_insert( &(pdse->dse_tree), n, entry_dn_cmp, dupentry_replace );
+ if (write_file)
+ dse_write_file_nolock(pdse);
+ /* If the entry was replaced i.e. not added as a new entry, we need to
+ free the old data, which is set in dupentry_replace */
+ if (DSE_ENTRY_WAS_REPLACED == rc) {
+ dse_node_delete(&n);
+ rc = 0; /* for return to caller */
+ }
+ if (use_lock)
+ PR_RWLock_Unlock(pdse->dse_rwlock);
+ }
+ return rc;
+}
+
+
+/*
+ * Return -1 if p does not point to a valid LDIF
+ * end-of-record delimiter (a NULL, two newlines, or two
+ * pairs of CRLF). Otherwise, return the length of
+ * the delimiter found.
+ */
+static int
+ldif_record_end( char *p )
+{
+ if ( NULL != p )
+ {
+ if ( '\0' == *p )
+ {
+ return 0;
+ }
+ else if ( '\n' == *p && '\n' == *( p + 1 ))
+ {
+ return 2;
+ }
+ else if ( '\r' == *p && '\n' == *( p + 1 ) && '\r' == *( p + 2 ) && '\n' == *( p + 3 ))
+ {
+ return 4;
+ }
+ }
+ return -1;
+}
+
+char *
+dse_read_next_entry( char *buf, char **lastp )
+{
+ char *p, *start;
+
+ if ( NULL == buf )
+ {
+ *lastp = NULL;
+ return NULL;
+ }
+ p = start = ( NULL == *lastp ) ? buf : *lastp;
+ /* Skip over any leading record delimiters */
+ while ( '\n' == *p || '\r' == *p )
+ {
+ p++;
+ }
+ if ( '\0' == *p )
+ {
+ *lastp = NULL;
+ return NULL;
+ }
+ while ( '\0' != *p )
+ {
+ int rc;
+ if (( rc = ldif_record_end( p )) >= 0 )
+ {
+ /* Found end of LDIF record */
+ *p = '\0';
+ p += rc;
+ break;
+ }
+ else
+ {
+ p++;
+ }
+ }
+ *lastp = p;
+ return start;
+}
+
+/*
+ * Apply the function to each entry. The caller is responsible for locking
+ * the rwlock in the dse for the appropriate type of operation e.g. for
+ * searching, a read lock, for modifying in place, a write lock
+ */
+static int
+dse_apply_nolock(struct dse* pdse,IFP fp,caddr_t arg)
+{
+ avl_apply( pdse->dse_tree, fp, arg, STOP_TRAVERSAL, AVL_INORDER );
+ return 1;
+}
+
+
+/*
+ * Remove the entry from the tree.
+ * Returns 1 if entry is removed and 0 if not.
+ */
+static int
+dse_delete_entry(struct dse* pdse, Slapi_PBlock *pb, const Slapi_Entry *e)
+{
+ int dont_write_file = 0;
+ struct dse_node *n= dse_node_new(e);
+ struct dse_node *deleted_node = NULL;
+
+ slapi_pblock_get(pb, SLAPI_DSE_DONT_WRITE_WHEN_ADDING, &dont_write_file);
+
+ /* keep write lock for both tree deleting and file writing */
+ PR_RWLock_Wlock(pdse->dse_rwlock);
+ if (deleted_node = (struct dse_node *)avl_delete(&pdse->dse_tree,
+ n, entry_dn_cmp))
+ dse_node_delete(&deleted_node);
+ dse_node_delete(&n);
+
+ if (!dont_write_file)
+ {
+ /* Decrement the numsubordinate count of the parent entry */
+ dse_updateNumSubOfParent(pdse, slapi_entry_get_sdn_const(e),
+ SLAPI_OPERATION_DELETE);
+ dse_write_file_nolock(pdse);
+ }
+ PR_RWLock_Unlock(pdse->dse_rwlock);
+
+ return 1;
+}
+
+
+/*
+ * Returns a SLAPI_BIND_xxx retun code.
+ */
+int
+dse_bind( Slapi_PBlock *pb ) /* JCM There should only be one exit point from this function! */
+{
+ char *dn; /* The bind DN */
+ int method; /* The bind method */
+ struct berval *cred; /* The bind credentials */
+ Slapi_Value **bvals;
+ struct dse* pdse;
+ Slapi_Attr *attr;
+ Slapi_DN sdn;
+ Slapi_Entry *ec= NULL;
+
+ /*Get the parameters*/
+ if (slapi_pblock_get( pb, SLAPI_PLUGIN_PRIVATE, &pdse ) < 0 ||
+ slapi_pblock_get( pb, SLAPI_BIND_TARGET, &dn ) < 0 ||
+ slapi_pblock_get( pb, SLAPI_BIND_METHOD, &method ) < 0 ||
+ slapi_pblock_get( pb, SLAPI_BIND_CREDENTIALS, &cred ) < 0){
+ slapi_send_ldap_result( pb, LDAP_OPERATIONS_ERROR, NULL, NULL, 0, NULL );
+ return SLAPI_BIND_FAIL;
+ }
+
+ /* always allow noauth simple binds */
+ if ( method == LDAP_AUTH_SIMPLE && cred->bv_len == 0 )
+ {
+ /*
+ * report success to client, but return
+ * SLAPI_BIND_FAIL so we don't
+ * authorize based on noauth credentials
+ */
+ slapi_send_ldap_result( pb, LDAP_SUCCESS, NULL, NULL, 0, NULL );
+ return( SLAPI_BIND_FAIL );
+ }
+
+ /* Find the entry that the person is attempting to bind as */
+ slapi_sdn_init_dn_byref(&sdn,dn);
+ ec = dse_get_entry_copy(pdse,&sdn,DSE_USE_LOCK);
+ if ( ec == NULL )
+ {
+ slapi_send_ldap_result( pb, LDAP_NO_SUCH_OBJECT, NULL, NULL, 0, NULL );
+ slapi_sdn_done(&sdn);
+ return( SLAPI_BIND_FAIL );
+ }
+
+ switch ( method )
+ {
+ case LDAP_AUTH_SIMPLE:
+ {
+ Slapi_Value cv;
+ if ( slapi_entry_attr_find( ec, "userpassword", &attr ) != 0 )
+ {
+ slapi_send_ldap_result( pb, LDAP_INAPPROPRIATE_AUTH, NULL, NULL, 0, NULL );
+ slapi_entry_free(ec);
+ slapi_sdn_done(&sdn);
+ return SLAPI_BIND_FAIL;
+ }
+ bvals= attr_get_present_values( attr );
+
+ slapi_value_init_berval(&cv,cred);
+ if ( slapi_pw_find_sv( bvals, &cv ) != 0 )
+ {
+ slapi_send_ldap_result( pb, LDAP_INVALID_CREDENTIALS, NULL, NULL, 0, NULL );
+ slapi_entry_free(ec);
+ slapi_sdn_done(&sdn);
+ value_done(&cv);
+ return SLAPI_BIND_FAIL;
+ }
+ value_done(&cv);
+ }
+ break;
+
+ default:
+ slapi_send_ldap_result( pb, LDAP_STRONG_AUTH_NOT_SUPPORTED, NULL, "auth method not supported", 0, NULL );
+ slapi_entry_free(ec);
+ slapi_sdn_done(&sdn);
+ return SLAPI_BIND_FAIL;
+ }
+ slapi_entry_free(ec);
+ slapi_sdn_done(&sdn);
+ /* success: front end will send result */
+ return SLAPI_BIND_SUCCESS;
+}
+
+int
+dse_unbind( Slapi_PBlock *pb )
+{
+ return 0;
+}
+
+/*
+ * This structure is simply to pass parameters to dse_search_filter_entry.
+ */
+struct magicSearchStuff
+{
+ Slapi_PBlock *pb;
+ struct dse *pdse;
+ int scope;
+ const Slapi_DN *basedn;
+ Slapi_Filter *filter;
+ int nentries;
+ char **attrs; /*Attributes*/
+ int attrsonly; /*Should we just return the attributes found?*/
+ dse_search_set *ss; /* for the temporary results - to pass to the dse search callbacks */
+};
+
+/*
+ * The function which is called on each node of the AVL tree.
+ */
+static int
+dse_search_filter_entry(caddr_t data, caddr_t arg)
+{
+ struct dse_node *n = (struct dse_node *)data;
+ struct magicSearchStuff *p= (struct magicSearchStuff *)arg;
+ if(slapi_sdn_scope_test( slapi_entry_get_sdn_const(n->entry), p->basedn, p->scope))
+ {
+ if(slapi_vattr_filter_test( p->pb, n->entry, p->filter, 1 /* verify access */ )==0)
+ {
+ Slapi_Entry *ec = slapi_entry_dup( n->entry );
+ p->nentries++;
+ if (!p->ss)
+ {
+ p->ss = dse_search_set_new();
+ }
+ dse_search_set_add_entry(p->ss, ec); /* consumes the entry */
+ } else {
+/*
+ slapd_log_error_proc("dse_search_filter_entry",
+ "filter test failed: dn %s did not match filter %d\n",
+ slapi_entry_get_dn_const(n->entry), p->filter->f_choice);
+*/
+ }
+ } else {
+/*
+ slapd_log_error_proc("dse_search_filter_entry",
+ "scope test failed: dn %s is not in scope %d of dn [%s]\n",
+ slapi_entry_get_dn_const(n->entry), p->scope,
+ slapi_sdn_get_dn(p->basedn));
+*/
+ }
+ return 0;
+}
+
+/*
+ * The function which kicks off the traversal of the AVL tree.
+ * Returns the number of entries returned.
+ */
+/* jcm: Not very efficient if there are many DSE entries. */
+/* jcm: It applies the filter to every node in the tree regardless */
+static int
+do_dse_search(struct dse* pdse, Slapi_PBlock *pb, int scope, const Slapi_DN *basedn, Slapi_Filter *filter, char **attrs, int attrsonly)
+{
+ struct magicSearchStuff stuff;
+ stuff.pb= pb;
+ stuff.pdse= pdse;
+ stuff.scope= scope;
+ stuff.basedn= basedn;
+ stuff.filter= filter;
+ stuff.nentries= 0;
+ stuff.attrs= attrs;
+ stuff.attrsonly= attrsonly;
+ stuff.ss = NULL;
+
+ /*
+ * If this is a persistent search and the client is only interested in
+ * entries that change, we skip looking through the DSE entries.
+ */
+ if ( pb->pb_op == NULL
+ || !operation_is_flag_set( pb->pb_op, OP_FLAG_PS_CHANGESONLY )) {
+ PR_RWLock_Rlock(pdse->dse_rwlock);
+ dse_apply_nolock(pdse,dse_search_filter_entry,(caddr_t)&stuff);
+ PR_RWLock_Unlock(pdse->dse_rwlock);
+ }
+
+ if (stuff.ss) /* something was found which matched our criteria */
+ {
+ Slapi_Entry *e = NULL;
+ for (e = dse_search_set_get_next_entry(stuff.ss);
+ e;
+ e = dse_search_set_get_next_entry(stuff.ss))
+ {
+ int returncode = 0;
+ char returntext[SLAPI_DSE_RETURNTEXT_SIZE]= "";
+
+ if(dse_call_callback(pdse, pb, SLAPI_OPERATION_SEARCH,
+ DSE_FLAG_PREOP, e, NULL, &returncode, returntext)
+ == SLAPI_DSE_CALLBACK_OK)
+ {
+ dse_search_set *ss = NULL;
+ slapi_pblock_get (pb, SLAPI_SEARCH_RESULT_SET, &ss);
+ /* if this is the first entry - allocate dse_search_set structure */
+ if (ss == NULL)
+ {
+ ss = dse_search_set_new ();
+ slapi_pblock_set (pb, SLAPI_SEARCH_RESULT_SET, ss);
+ }
+ /* make another reference to e (stuff.ss references it too)
+ the stuff.ss reference is removed by dse_search_set_clean()
+ below, leaving ss as the sole owner of the memory */
+ dse_search_set_add_entry(ss, e);
+ } else {
+ stuff.nentries--; /* rejected entry */
+ /* this leaves a freed pointer in stuff.ss, but that's ok because
+ it should never be referenced, and the reference is removed by
+ the call to dse_search_set_clean() below */
+ slapi_entry_free(e);
+ }
+ }
+ dse_search_set_clean(stuff.ss);
+ }
+
+ /* the pblock ss now contains the "real" search result set and the copies of
+ the entries allocated in dse_search_filter_entry; any entries rejected by
+ the search callback were freed above by the call to slapi_entry_free() */
+ return stuff.nentries;
+}
+
+/*
+ * -1 means something went wrong.
+ * 0 means everything went ok.
+ */
+int
+dse_search(Slapi_PBlock *pb) /* JCM There should only be one exit point from this function! */
+{
+ char *base; /*Base of the search*/
+ int scope; /*Scope of the search*/
+ Slapi_Filter *filter; /*The filter*/
+ char **attrs; /*Attributes*/
+ int attrsonly; /*Should we just return the attributes found?*/
+ /*int nentries= 0; Number of entries found thus far*/
+ struct dse* pdse;
+ int returncode= LDAP_SUCCESS;
+ int isrootdse= 0;
+ char returntext[SLAPI_DSE_RETURNTEXT_SIZE]= "";
+ Slapi_DN basesdn;
+
+ /*
+ * Get private information created in the init routine.
+ * Also get the parameters of the search operation. These come
+ * more or less directly from the client.
+ */
+ if (slapi_pblock_get( pb, SLAPI_PLUGIN_PRIVATE, &pdse ) < 0 ||
+ slapi_pblock_get( pb, SLAPI_SEARCH_TARGET, &base ) < 0 ||
+ slapi_pblock_get( pb, SLAPI_SEARCH_SCOPE, &scope ) < 0 ||
+ slapi_pblock_get( pb, SLAPI_SEARCH_FILTER, &filter ) < 0 ||
+ slapi_pblock_get( pb, SLAPI_SEARCH_ATTRS, &attrs ) < 0 ||
+ slapi_pblock_get( pb, SLAPI_SEARCH_ATTRSONLY, &attrsonly ) <0)
+ {
+ slapi_send_ldap_result( pb, LDAP_OPERATIONS_ERROR, NULL, NULL, 0, NULL );
+ return(-1);
+ }
+
+ slapi_sdn_init_dn_byref(&basesdn,base);
+
+ /*
+ * Sadly the root dse is still a special case. We must not allow
+ * acl checks on it, or allow onelevel or subtree searches on it.
+ */
+ isrootdse= slapi_sdn_isempty(&basesdn);
+
+ switch(scope)
+ {
+ case LDAP_SCOPE_BASE:
+ {
+ Slapi_Entry *baseentry= NULL;
+ baseentry = dse_get_entry_copy(pdse,&basesdn,DSE_USE_LOCK);
+ if ( baseentry == NULL )
+ {
+ slapi_send_ldap_result( pb, LDAP_NO_SUCH_OBJECT, NULL, NULL, 0, NULL );
+ slapi_log_error(SLAPI_LOG_PLUGIN,"dse_search", "node %s was not found\n",
+ slapi_sdn_get_dn(&basesdn));
+ slapi_sdn_done(&basesdn);
+ return -1;
+ }
+ /*
+ * We don't want to do an acl check for the root dse... because the acl
+ * code thinks it's a suffix of every target... so every acl applies to
+ * the root dse... which is wrong.
+ */
+ if(slapi_vattr_filter_test( pb, baseentry, filter, !isrootdse /* verify access */ )==0)
+ {
+ /* Callbacks modify a copy of the entry */
+ if(dse_call_callback(pdse, pb, SLAPI_OPERATION_SEARCH,
+ DSE_FLAG_PREOP, baseentry, NULL, &returncode, returntext)
+ == SLAPI_DSE_CALLBACK_OK)
+ {
+ dse_search_set *ss;
+ ss = dse_search_set_new ();
+ slapi_pblock_set (pb, SLAPI_SEARCH_RESULT_SET, ss);
+ dse_search_set_add_entry (ss, baseentry); /* consumes the entry */
+ baseentry= NULL;
+ }
+ }
+ slapi_entry_free(baseentry);
+ }
+ break;
+ case LDAP_SCOPE_ONELEVEL:
+ /* FALL THROUGH */
+ case LDAP_SCOPE_SUBTREE:
+ if(!isrootdse)
+ {
+ do_dse_search(pdse, pb, scope, &basesdn, filter, attrs, attrsonly);
+ }
+ break;
+ }
+
+ /* Search is done, send LDAP_SUCCESS */
+ slapi_sdn_done(&basesdn);
+ return 0;
+}
+
+
+/*
+ * -1 means something went wrong.
+ * 0 means everything went ok.
+ */
+
+static int
+dse_modify_return( int rv, Slapi_Entry *ec, Slapi_Entry *ecc )
+{
+ slapi_entry_free(ec);
+ slapi_entry_free(ecc);
+ return rv;
+}
+
+int
+dse_modify(Slapi_PBlock *pb) /* JCM There should only be one exit point from this function! */
+{
+ int err; /*House keeping stuff*/
+ LDAPMod **mods; /*Used to apply the modifications*/
+ char *dn; /*Storage for the dn*/
+ char *errbuf = NULL; /* To get error back */
+ struct dse* pdse;
+ Slapi_Entry *ec= NULL;
+ Slapi_Entry *ecc= NULL;
+ int returncode= LDAP_SUCCESS;
+ char returntext[SLAPI_DSE_RETURNTEXT_SIZE]= "";
+ Slapi_DN sdn;
+ int dont_write_file = 0; /* default */
+
+ PR_ASSERT(pb);
+ if (slapi_pblock_get( pb, SLAPI_PLUGIN_PRIVATE, &pdse ) < 0 ||
+ slapi_pblock_get( pb, SLAPI_MODIFY_TARGET, &dn ) < 0 ||
+ slapi_pblock_get( pb, SLAPI_MODIFY_MODS, &mods ) < 0 || (NULL == pdse))
+ {
+ slapi_send_ldap_result( pb, LDAP_OPERATIONS_ERROR, NULL, NULL, 0, NULL );
+ return( -1 );
+ }
+
+ slapi_pblock_get(pb, SLAPI_DSE_DONT_WRITE_WHEN_ADDING, &dont_write_file);
+ if ( !dont_write_file && dse_check_for_readonly_error(pb,pdse)) {
+ return( -1 );
+ }
+
+ slapi_sdn_init_dn_byref(&sdn,dn);
+
+ /* Find the entry we are about to modify. */
+ ec = dse_get_entry_copy(pdse,&sdn,DSE_USE_LOCK);
+ if ( ec == NULL )
+ {
+ slapi_send_ldap_result( pb, LDAP_NO_SUCH_OBJECT, NULL, NULL, 0, NULL );
+ slapi_sdn_done(&sdn);
+ return dse_modify_return( -1, ec, ecc );
+ }
+
+ /* Check acl */
+ err = plugin_call_acl_mods_access ( pb, ec, mods, &errbuf );
+ if ( err != LDAP_SUCCESS )
+ {
+ slapi_send_ldap_result( pb, err, NULL, errbuf, 0, NULL );
+ if (errbuf) slapi_ch_free ((void**)&errbuf);
+ slapi_sdn_done(&sdn);
+ return dse_modify_return( -1, ec, ecc );
+ }
+
+ /* Save away a copy of the entry, before modifications */
+ slapi_pblock_set( pb, SLAPI_ENTRY_PRE_OP, slapi_entry_dup( ec )); /* JCM - When does this get free'd? */
+ /* richm - it is freed in modify.c */
+
+ /* Modify a copy of the entry*/
+ ecc = slapi_entry_dup( ec );
+ err = entry_apply_mods( ecc, mods );
+
+ /* XXXmcs: should we expand objectclass values here?? */
+
+ switch(dse_call_callback(pdse, pb, SLAPI_OPERATION_MODIFY, DSE_FLAG_PREOP, ec, ecc, &returncode, returntext))
+ {
+ case SLAPI_DSE_CALLBACK_ERROR:
+ {
+ /* Error occured in the callback -- return error code from callback */
+ slapi_send_ldap_result( pb, returncode, NULL, returntext, 0, NULL );
+ slapi_sdn_done(&sdn);
+ return dse_modify_return( -1, ec, ecc );
+ }
+
+ case SLAPI_DSE_CALLBACK_DO_NOT_APPLY:
+ {
+ /* Callback says don't apply the changes -- return Success */
+ slapi_send_ldap_result( pb, LDAP_SUCCESS, NULL, NULL, 0, NULL );
+ slapi_sdn_done(&sdn);
+ return dse_modify_return( 0, ec, ecc );
+ }
+
+ case SLAPI_DSE_CALLBACK_OK:
+ {
+ /* The callback may alter the mods in the pblock. This happens
+ for example in the schema code. Since the schema attributes
+ are managed exclusively by the schema code, we should not
+ apply those mods. However, for reasons unknown to me, we
+ must in the general case call entry_apply_mods before calling
+ the modify callback above. In the case of schema, the schema
+ code will remove the schema attributes from the mods. So, we
+ reapply the mods to the entry for the attributes we manage in
+ the dse code (e.g. aci)
+ */
+ int reapply_mods = 0; /* default is to not reapply entry_apply_mods */
+ slapi_pblock_get(pb, SLAPI_DSE_REAPPLY_MODS, &reapply_mods);
+ /* Callback says apply the changes */
+ if ( reapply_mods )
+ {
+ LDAPMod **modsagain = NULL; /*Used to apply the modifications*/
+ slapi_pblock_get( pb, SLAPI_MODIFY_MODS, &modsagain );
+ if (NULL != modsagain)
+ {
+ /* the dse modify callback must have modified ecc back to it's
+ original state, before the earlier apply_mods, but without the
+ attributes it did not want us to apply mods to */
+ err = entry_apply_mods( ecc, modsagain );
+ }
+ }
+
+ if (err != 0)
+ {
+ /* entry_apply_mods() failed above, so return an error now */
+ slapi_send_ldap_result( pb, err, NULL, NULL, 0, NULL );
+ slapi_sdn_done(&sdn);
+ return dse_modify_return( -1, ec, ecc );
+ }
+ break;
+ }
+ }
+
+ /* We're applying the mods... check that the entry still obeys the schema */
+ if ( slapi_entry_schema_check( pb, ecc ) != 0 )
+ {
+ char *errtext;
+
+ slapi_pblock_get(pb, SLAPI_PB_RESULT_TEXT, &errtext);
+ slapi_send_ldap_result( pb, LDAP_OBJECT_CLASS_VIOLATION, NULL, errtext, 0, NULL );
+ slapi_sdn_done(&sdn);
+ return dse_modify_return( -1, ec, ecc );
+ }
+
+ /* Change the entry itself both on disk and in the AVL tree */
+ /* dse_replace_entry free's the existing entry. */
+ if (dse_replace_entry( pdse, ecc, !dont_write_file, DSE_USE_LOCK )!=0 )
+ {
+ slapi_send_ldap_result( pb, LDAP_OPERATIONS_ERROR, NULL, NULL, 0, NULL );
+ slapi_sdn_done(&sdn);
+ return dse_modify_return( -1, ec, ecc );
+ }
+ slapi_pblock_set( pb, SLAPI_ENTRY_POST_OP, slapi_entry_dup(ecc) ); /* JCM - When does this get free'd? */
+ /* richm - it is freed in modify.c */
+ dse_call_callback(pdse, pb, SLAPI_OPERATION_MODIFY, DSE_FLAG_POSTOP, ec, ecc, &returncode, returntext);
+
+ slapi_send_ldap_result( pb, returncode, NULL, returntext, 0, NULL );
+
+ slapi_sdn_done(&sdn);
+ return dse_modify_return(0, ec, ecc);
+}
+
+static int
+dse_add_return( int rv, Slapi_Entry *e)
+{
+ slapi_entry_free(e);
+ return rv;
+}
+
+/*
+ * -1 means something went wrong.
+ * 0 means everything went ok.
+ */
+int
+dse_add(Slapi_PBlock *pb) /* JCM There should only be one exit point from this function! */
+{
+ char *dn = NULL;
+ Slapi_Entry *e; /*The new entry to add*/
+ Slapi_Entry *e_copy = NULL; /* copy of added entry */
+ char *errbuf = NULL;
+ int rc = LDAP_SUCCESS;
+ int error = -1;
+ int dont_write_file = 0; /* default */
+ struct dse* pdse;
+ int returncode= LDAP_SUCCESS;
+ char returntext[SLAPI_DSE_RETURNTEXT_SIZE]= "";
+ Slapi_DN sdn;
+ Slapi_DN parent;
+
+ /*
+ * Get the database, the dn and the entry to add
+ */
+ if (slapi_pblock_get( pb, SLAPI_PLUGIN_PRIVATE, &pdse ) < 0 ||
+ slapi_pblock_get( pb, SLAPI_ADD_TARGET, &dn ) < 0 ||
+ slapi_pblock_get( pb, SLAPI_ADD_ENTRY, &e ) < 0 || (NULL == pdse))
+ {
+ slapi_send_ldap_result( pb, LDAP_OPERATIONS_ERROR, NULL, NULL, 0, NULL );
+ return error;
+ }
+
+ slapi_pblock_get(pb, SLAPI_DSE_DONT_WRITE_WHEN_ADDING, &dont_write_file);
+ if ( !dont_write_file && dse_check_for_readonly_error(pb,pdse)) {
+ return( error );
+ }
+
+ slapi_sdn_init_dn_byref(&sdn,dn);
+
+ /*
+ * Check to make sure the entry passes the schema check
+ */
+ if ( slapi_entry_schema_check( pb, e ) != 0 )
+ {
+ char *errtext;
+ LDAPDebug( SLAPI_DSE_TRACELEVEL,
+ "dse_add: entry failed schema check\n", 0, 0, 0 );
+ slapi_pblock_get(pb, SLAPI_PB_RESULT_TEXT, &errtext);
+ slapi_send_ldap_result( pb, LDAP_OBJECT_CLASS_VIOLATION, NULL, errtext, 0, NULL );
+ slapi_sdn_done(&sdn);
+ return error;
+ }
+
+ /*
+ * Attempt to find this dn.
+ */
+ {
+ Slapi_Entry *existingentry= dse_get_entry_copy( pdse, &sdn, DSE_USE_LOCK );
+ if(existingentry!=NULL)
+ {
+ /*
+ * If we've reached this code, there is an entry
+ * whose dn matches dn, so tell the user and return
+ */
+ slapi_send_ldap_result( pb, LDAP_ALREADY_EXISTS, NULL, NULL, 0, NULL );
+ slapi_sdn_done(&sdn);
+ slapi_entry_free(existingentry);
+ return dse_add_return(error, NULL);
+ }
+ }
+
+ /*
+ * Get the parent dn and see if the corresponding entry exists.
+ * If the parent does not exist, only allow the "root" user to
+ * add the entry.
+ */
+ slapi_sdn_init(&parent);
+ slapi_sdn_get_parent(&sdn,&parent);
+ if ( !slapi_sdn_isempty(&parent) )
+ {
+ Slapi_Entry *parententry= NULL;
+ parententry= dse_get_entry_copy( pdse, &parent, DSE_USE_LOCK );
+ if( parententry==NULL )
+ {
+ slapi_send_ldap_result( pb, LDAP_NO_SUCH_OBJECT, NULL, NULL, 0, NULL );
+ LDAPDebug( SLAPI_DSE_TRACELEVEL, "dse_add: parent does not exist\n", 0, 0, 0 );
+ slapi_sdn_done(&sdn);
+ slapi_sdn_done(&parent);
+ return dse_add_return(error, NULL);
+ }
+ rc= plugin_call_acl_plugin ( pb, parententry, NULL, NULL, SLAPI_ACL_ADD, ACLPLUGIN_ACCESS_DEFAULT, &errbuf );
+ slapi_entry_free(parententry);
+ if ( rc!=LDAP_SUCCESS )
+ {
+ LDAPDebug( SLAPI_DSE_TRACELEVEL, "dse_add: no access to parent\n", 0, 0, 0 );
+ slapi_send_ldap_result( pb, rc, NULL, NULL, 0, NULL );
+ slapi_ch_free((void**)&errbuf);
+ slapi_sdn_done(&sdn);
+ slapi_sdn_done(&parent);
+ return dse_add_return(rc, NULL);
+ }
+ }
+ else
+ {
+ /* no parent */
+ int isroot;
+ slapi_pblock_get( pb, SLAPI_REQUESTOR_ISROOT, &isroot );
+ if ( !isroot )
+ {
+ LDAPDebug( SLAPI_DSE_TRACELEVEL, "dse_add: no parent and not root\n", 0, 0, 0 );
+ slapi_send_ldap_result( pb, LDAP_INSUFFICIENT_ACCESS, NULL, NULL, 0, NULL );
+ slapi_sdn_done(&sdn);
+ slapi_sdn_done(&parent);
+ return dse_add_return(error, NULL);
+ }
+ }
+ slapi_sdn_done(&parent);
+
+ /*
+ * Before we add the entry, find out if the syntax of the aci
+ * aci attribute values are correct or not. We don't want to add
+ * the entry if the syntax is incorrect.
+ */
+ if ( plugin_call_acl_verify_syntax (pb, e, &errbuf) != 0 )
+ {
+ slapi_send_ldap_result( pb, LDAP_INVALID_SYNTAX, NULL, errbuf, 0, NULL );
+ slapi_ch_free((void**)&errbuf);
+ return dse_add_return(error, NULL);
+ }
+
+ if(dse_call_callback(pdse, pb, SLAPI_OPERATION_ADD, DSE_FLAG_PREOP, e,
+ NULL, &returncode, returntext)!=SLAPI_DSE_CALLBACK_OK)
+ {
+ slapi_send_ldap_result( pb, returncode, NULL, returntext, 0, NULL );
+ slapi_sdn_done(&sdn);
+ return dse_add_return(error, NULL);
+ }
+
+ /* make copy for postop fns because add_entry_pb consumes e */
+ e_copy = slapi_entry_dup(e);
+ if ( dse_add_entry_pb(pdse, e, pb) != 0)
+ {
+ slapi_send_ldap_result( pb, LDAP_OPERATIONS_ERROR, NULL, NULL, 0, NULL );
+ slapi_sdn_done(&sdn);
+ return dse_add_return(error, e_copy);
+ }
+
+ /* e has been consumed, so use the copy for the post ops */
+ slapi_pblock_set(pb, SLAPI_ADD_ENTRY, e_copy);
+
+ /* The postop must be called after the write lock is released. */
+ dse_call_callback(pdse, pb, SLAPI_OPERATION_ADD, DSE_FLAG_POSTOP, e_copy, NULL, &returncode, returntext);
+
+ /* We have been successful. Tell the user */
+ slapi_send_ldap_result( pb, returncode, NULL, NULL, 0, NULL );
+
+ slapi_pblock_set( pb, SLAPI_ENTRY_POST_OP, slapi_entry_dup( e_copy ));
+
+ /* entry has been freed, so make sure no one tries to use it later */
+ slapi_pblock_set(pb, SLAPI_ADD_ENTRY, NULL);
+
+ /* Free the dn, and return */
+ slapi_sdn_done(&sdn);
+
+ return dse_add_return(rc, e_copy);
+}
+
+/*
+ * -1 means something went wrong.
+ * 0 means everything went ok.
+ */
+
+static int
+dse_delete_return( int rv, Slapi_Entry *ec)
+{
+ slapi_entry_free(ec);
+ return rv;
+}
+
+int
+dse_delete(Slapi_PBlock *pb) /* JCM There should only be one exit point from this function! */
+{
+ char *dn = NULL;
+ int rc= -1;
+ int dont_write_file = 0; /* default */
+ struct dse* pdse = NULL;
+ int returncode= LDAP_SUCCESS;
+ char returntext[SLAPI_DSE_RETURNTEXT_SIZE]= "";
+ char *entry_str = "entry";
+ char *errbuf = NULL;
+ char *attrs[2] = { NULL, NULL };
+ Slapi_DN sdn;
+ Slapi_Entry *ec = NULL; /* copy of entry to delete */
+
+ /*
+ * Get the database and the dn
+ */
+ if (slapi_pblock_get( pb, SLAPI_PLUGIN_PRIVATE, &pdse ) < 0 ||
+ slapi_pblock_get( pb, SLAPI_DELETE_TARGET, &dn ) < 0 || (pdse == NULL))
+ {
+ slapi_send_ldap_result( pb, LDAP_OPERATIONS_ERROR, NULL, NULL, 0, NULL );
+ return rc;
+ }
+
+ slapi_pblock_get(pb, SLAPI_DSE_DONT_WRITE_WHEN_ADDING, &dont_write_file);
+ if ( !dont_write_file && dse_check_for_readonly_error(pb,pdse)) {
+ return( rc );
+ }
+
+ slapi_sdn_init_dn_byref(&sdn,dn);
+
+ ec= dse_get_entry_copy( pdse, &sdn, DSE_USE_LOCK );
+ if (ec == NULL)
+ {
+ slapi_send_ldap_result( pb, LDAP_NO_SUCH_OBJECT, NULL, NULL, 0, NULL );
+ slapi_sdn_done(&sdn);
+ return dse_delete_return( rc, ec );
+ }
+
+ /*
+ * Check if this node has any children.
+ */
+ if(dse_numsubordinates(ec)>0)
+ {
+ slapi_send_ldap_result( pb, LDAP_NOT_ALLOWED_ON_NONLEAF, NULL, NULL, 0, NULL );
+ slapi_sdn_done(&sdn);
+ return dse_delete_return( rc, ec );
+ }
+
+ /*
+ * Check the access
+ */
+ attrs[0] = entry_str;
+ attrs[1] = NULL;
+ returncode= plugin_call_acl_plugin ( pb, ec, attrs, NULL, SLAPI_ACL_DELETE, ACLPLUGIN_ACCESS_DEFAULT, &errbuf );
+ if ( returncode!=LDAP_SUCCESS)
+ {
+ slapi_send_ldap_result( pb, returncode, NULL, NULL, 0, NULL );
+ slapi_ch_free ( (void**)&errbuf );
+ slapi_sdn_done(&sdn);
+ return dse_delete_return( rc, ec );
+ }
+
+ if(dse_call_callback(pdse, pb, SLAPI_OPERATION_DELETE, DSE_FLAG_PREOP, ec, NULL, &returncode,returntext)==SLAPI_DSE_CALLBACK_OK)
+ {
+ if(dse_delete_entry(pdse, pb, ec)==0)
+ {
+ returncode= LDAP_OPERATIONS_ERROR;
+ }
+ }
+ else
+ {
+ slapi_send_ldap_result( pb, returncode, NULL, NULL, 0, NULL );
+ slapi_sdn_done(&sdn);
+ return dse_delete_return( rc, ec );
+ }
+
+ dse_call_callback(pdse, pb, SLAPI_OPERATION_DELETE, DSE_FLAG_POSTOP, ec, NULL, &returncode, returntext);
+
+ slapi_send_ldap_result( pb, returncode, NULL, returntext, 0, NULL );
+ slapi_pblock_set( pb, SLAPI_ENTRY_PRE_OP, slapi_entry_dup( ec ));
+ slapi_sdn_done(&sdn);
+ return dse_delete_return(0, ec);
+}
+
+struct dse_callback *
+dse_register_callback(struct dse* pdse, int operation, int flags, const Slapi_DN *base, int scope, const char *filter, dseCallbackFn fn, void *fn_arg)
+{
+ struct dse_callback *callback = dse_callback_new(operation, flags, base, scope, filter, fn, fn_arg);
+ dse_callback_addtolist(&pdse->dse_callback, callback);
+ return callback;
+}
+
+void
+dse_remove_callback(struct dse* pdse, int operation, int flags, const Slapi_DN *base, int scope, const char *filter, dseCallbackFn fn)
+{
+ dse_callback_removefromlist(&pdse->dse_callback, operation, flags, base, scope, filter, fn);
+}
+
+/*
+ * Return values:
+ * SLAPI_DSE_CALLBACK_ERROR -- Callback failed.
+ * SLAPI_DSE_CALLBACK_OK -- OK, do it.
+ * SLAPI_DSE_CALLBACK_DO_NOT_APPLY -- No error, but don't apply changes.
+ */
+static int
+dse_call_callback(struct dse* pdse, Slapi_PBlock *pb, int operation, int flags, Slapi_Entry *entryBefore, Slapi_Entry *entryAfter, int *returncode, char *returntext)
+{
+ /* ONREPL callbacks can potentially modify pblock parameters like backend
+ * which would cause problems during request processing. We need to save
+ * "important" fields before calls and restoring them afterwards */
+ int r = SLAPI_DSE_CALLBACK_OK;
+ if (pdse->dse_callback != NULL) {
+ struct dse_callback *p;
+ p=pdse->dse_callback;
+ while (p!=NULL) {
+ struct dse_callback *p_next = p->next;
+ if ((p->operation & operation) && (p->flags & flags)) {
+ if(slapi_sdn_scope_test(slapi_entry_get_sdn_const(entryBefore), p->base, p->scope))
+ {
+ if(NULL == p->slapifilter ||
+ slapi_vattr_filter_test(pb, entryBefore, p->slapifilter,
+ 0 /* !verify access */ )==0)
+ {
+ int result= (*p->fn)(pb, entryBefore,entryAfter,returncode,returntext,p->fn_arg);
+ if(result<r)
+ {
+ r= result;
+ }
+ }
+ }
+ }
+ p = p_next;
+ }
+ }
+ return r;
+}
+
+int
+slapi_config_register_callback(int operation, int flags, const char *base, int scope, const char *filter, dseCallbackFn fn, void *fn_arg)
+{
+ int rc= 0;
+ Slapi_Backend *be= slapi_be_select_by_instance_name(DSE_BACKEND);
+ if (be != NULL) {
+ struct dse* pdse= (struct dse*)be->be_database->plg_private;
+ if (pdse!=NULL) {
+ Slapi_DN dn;
+ slapi_sdn_init_dn_byref(&dn,base);
+ rc = (NULL != dse_register_callback(pdse, operation, flags, &dn, scope, filter, fn, fn_arg));
+ slapi_sdn_done(&dn);
+ }
+ }
+ return rc;
+}
+
+int
+slapi_config_remove_callback(int operation, int flags, const char *base, int scope, const char *filter, dseCallbackFn fn)
+{
+ int rc= 0;
+ Slapi_Backend *be= slapi_be_select_by_instance_name(DSE_BACKEND);
+ if(be != NULL) {
+ struct dse* pdse = (struct dse*)be->be_database->plg_private;
+ if (pdse != NULL) {
+ Slapi_DN dn;
+ slapi_sdn_init_dn_byref(&dn,base);
+ dse_remove_callback(pdse, operation, flags, &dn, scope, filter, fn);
+ slapi_sdn_done(&dn);
+ rc= 1;
+ }
+ }
+ return rc;
+}
+
+void
+dse_set_dont_ever_write_dse_files()
+{
+ dont_ever_write_dse_files = 1;
+}
+
+void
+dse_unset_dont_ever_write_dse_files()
+{
+ dont_ever_write_dse_files = 0;
+}
+
+static dse_search_set*
+dse_search_set_new ()
+{
+ dse_search_set *ss;
+
+ ss = (dse_search_set *)slapi_ch_malloc (sizeof (*ss));
+
+ if (ss)
+ {
+ dl_init (&ss->dl, 0);
+ ss->current_entry = -1;
+ }
+
+ return ss;
+}
+
+/* This is similar to delete, but it does not free the entries contained in the
+ search set. This is useful in do_dse_search when we copy the entries from
+ 1 search set to the other. */
+static void
+dse_search_set_clean(dse_search_set *ss)
+{
+ if (ss)
+ {
+ dl_cleanup(&ss->dl, NULL);
+ slapi_ch_free((void **)&ss);
+ }
+}
+
+static void
+dse_search_set_delete (dse_search_set *ss)
+{
+ if (ss)
+ {
+ dl_cleanup (&ss->dl, dse_free_entry);
+ slapi_ch_free ((void**)&ss);
+ }
+}
+
+static void
+dse_free_entry (void **data)
+{
+ Slapi_Entry **e;
+
+ if (data)
+ {
+ e = (Slapi_Entry **)data;
+ if (*e)
+ slapi_entry_free (*e);
+ }
+}
+
+static void
+dse_search_set_add_entry (dse_search_set *ss, Slapi_Entry *e)
+{
+ PR_ASSERT (ss && e);
+
+ dl_add (&ss->dl, e);
+}
+
+static Slapi_Entry*
+dse_search_set_get_next_entry (dse_search_set *ss)
+{
+ PR_ASSERT (ss);
+
+ if (ss->current_entry == -1)
+ return (dl_get_first (&ss->dl, &ss->current_entry));
+ else
+ return (dl_get_next (&ss->dl, &ss->current_entry));
+}
+
+int
+dse_next_search_entry (Slapi_PBlock *pb)
+{
+ dse_search_set *ss;
+ Slapi_Entry *e;
+
+ slapi_pblock_get(pb, SLAPI_SEARCH_RESULT_SET, &ss);
+
+ /* no entries to return */
+ if (ss == NULL)
+ {
+ slapi_pblock_set (pb, SLAPI_SEARCH_RESULT_ENTRY, NULL);
+ return 0;
+ }
+
+ e = dse_search_set_get_next_entry (ss);
+ slapi_pblock_set (pb, SLAPI_SEARCH_RESULT_ENTRY, e);
+
+ /* we reached the end of the list */
+ if (e == NULL)
+ {
+ dse_search_set_delete (ss);
+ slapi_pblock_set(pb, SLAPI_SEARCH_RESULT_SET, NULL);
+ }
+
+ return 0;
+}
diff --git a/ldap/servers/slapd/dynalib.c b/ldap/servers/slapd/dynalib.c
new file mode 100644
index 00000000..4839a17b
--- /dev/null
+++ b/ldap/servers/slapd/dynalib.c
@@ -0,0 +1,86 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+/* dynalib.c - dynamic library routines */
+
+#include <stdio.h>
+#include "prlink.h"
+#include "slap.h"
+#if defined(SOLARIS)
+#include <dlfcn.h> /* dlerror */
+#endif
+
+
+static struct dynalib {
+ char *dl_name;
+ PRLibrary *dl_handle;
+} **libs = NULL;
+
+static void symload_report_error( char *libpath, char *symbol, char *plugin,
+ int libopen );
+
+void *
+sym_load( char *libpath, char *symbol, char *plugin, int report_errors )
+{
+ int i;
+ void *handle;
+
+ for ( i = 0; libs != NULL && libs[i] != NULL; i++ ) {
+ if ( strcasecmp( libs[i]->dl_name, libpath ) == 0 ) {
+ handle = PR_FindSymbol( libs[i]->dl_handle, symbol );
+ if ( NULL == handle && report_errors ) {
+ symload_report_error( libpath, symbol, plugin, 1 /* lib open */ );
+ }
+ return handle;
+ }
+ }
+
+ if ( (handle = PR_LoadLibrary( libpath )) == NULL ) {
+ if ( report_errors ) {
+ symload_report_error( libpath, symbol, plugin, 0 /* lib not open */ );
+ }
+ return( NULL );
+ }
+
+ libs = (struct dynalib **) slapi_ch_realloc( (char *) libs, (i + 2) *
+ sizeof(struct dynalib) );
+ libs[i] = (struct dynalib *) slapi_ch_malloc( sizeof(struct dynalib) );
+ libs[i]->dl_name = slapi_ch_strdup( libpath );
+ libs[i]->dl_handle = handle;
+ libs[ i + 1 ] = NULL;
+
+ handle = PR_FindSymbol( libs[i]->dl_handle, symbol );
+ if ( NULL == handle && report_errors ) {
+ symload_report_error( libpath, symbol, plugin, 1 /* lib open */ );
+ }
+ return handle;
+}
+
+
+static void
+symload_report_error( char *libpath, char *symbol, char *plugin, int libopen )
+{
+ char *errtext = NULL;
+ PRInt32 errlen, err;
+
+ errlen = PR_GetErrorTextLength();
+ if ( errlen > 0 ) {
+ errtext = slapi_ch_malloc( errlen );
+ if (( err = PR_GetErrorText( errtext )) > 0 ) {
+ LDAPDebug( LDAP_DEBUG_ANY, SLAPI_COMPONENT_NAME_NSPR " error %d: %s\n",
+ PR_GetError(), errtext, 0 );
+ }
+ slapi_ch_free( (void **)&errtext );
+ }
+ if ( libopen ) {
+ LDAPDebug( LDAP_DEBUG_ANY,
+ "Could not load symbol \"%s\" from \"%s\" for plugin %s\n",
+ symbol, libpath, plugin );
+ } else {
+ LDAPDebug( LDAP_DEBUG_ANY,
+ "Could not open library \"%s\" for plugin %s\n",
+ libpath, plugin, 0 );
+ }
+}
diff --git a/ldap/servers/slapd/entry.c b/ldap/servers/slapd/entry.c
new file mode 100644
index 00000000..0d315053
--- /dev/null
+++ b/ldap/servers/slapd/entry.c
@@ -0,0 +1,3124 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+/* entry.c - routines for dealing with entries */
+
+#include <stdio.h>
+#include <string.h>
+#include <ctype.h>
+#include <sys/types.h>
+#ifndef _WIN32
+#include <sys/socket.h>
+#endif
+#undef DEBUG /* disable counters */
+#include <prcountr.h>
+#include "slap.h"
+
+
+#undef ENTRY_DEBUG
+
+#define DELETED_ATTR_STRING ";deletedattribute"
+#define DELETED_ATTR_STRSIZE 17 /* sizeof(";deletedattribute") */
+#define DELETED_VALUE_STRING ";deleted"
+#define DELETED_VALUE_STRSIZE 8 /* sizeof(";deleted") */
+
+/*
+ * An attribute name is of the form 'basename[;option]'.
+ * The state informaion is encoded in options. For example:
+ *
+ * telephonenumber;vucsn-011111111222233334444: 1 650 937 5739
+ *
+ * This function strips out the csn options, leaving behind a
+ * type with any non-csn options left intact.
+ */
+/*
+ * WARNING: s gets butchered... the base type remains.
+ */
+void
+str2entry_state_information_from_type(char *s,CSNSet **csnset,CSN **attributedeletioncsn,CSN **maxcsn,int *value_state,int *attr_state)
+{
+ char *p= strchr(s, ';');
+ *value_state= VALUE_PRESENT;
+ *attr_state= ATTRIBUTE_PRESENT;
+ while(p!=NULL)
+ {
+ if(p[3]=='c' && p[4]=='s' && p[5]=='n' && p[6]=='-')
+ {
+ CSNType t= CSN_TYPE_UNKNOWN;
+ if(p[1]=='x' && p[2]=='1')
+ {
+ t= CSN_TYPE_UNKNOWN;
+ }
+ if(p[1]=='x' && p[2]=='2')
+ {
+ t= CSN_TYPE_NONE;
+ }
+ if(p[1]=='a' && p[2]=='d')
+ {
+ t= CSN_TYPE_ATTRIBUTE_DELETED;
+ }
+ if(p[1]=='v' && p[2]=='u')
+ {
+ t= CSN_TYPE_VALUE_UPDATED;
+ }
+ if(p[1]=='v' && p[2]=='d')
+ {
+ t= CSN_TYPE_VALUE_DELETED;
+ }
+ if(p[1]=='m' && p[2]=='d')
+ {
+ t= CSN_TYPE_VALUE_DISTINGUISHED;
+ }
+ p[0]='\0';
+ if(t!=CSN_TYPE_ATTRIBUTE_DELETED)
+ {
+ CSN csn;
+ csn_init_by_string(&csn,p+7);
+ csnset_add_csn(csnset,t,&csn);
+ if ( *maxcsn == NULL )
+ {
+ *maxcsn = csn_dup ( &csn );
+ }
+ else if ( csn_compare (*maxcsn, &csn) < 0 )
+ {
+ csn_init_by_csn ( *maxcsn, &csn );
+ }
+ }
+ else
+ {
+ *attributedeletioncsn= csn_new_by_string(p+7);
+ if ( *maxcsn == NULL )
+ {
+ *maxcsn = csn_dup ( *attributedeletioncsn );
+ }
+ else if ( csn_compare (*maxcsn, *attributedeletioncsn) < 0 )
+ {
+ csn_init_by_csn ( *maxcsn, *attributedeletioncsn );
+ }
+ }
+ }
+ else if(strncmp(p+1,"deletedattribute", 16)==0)
+ {
+ p[0]='\0';
+ *attr_state= ATTRIBUTE_DELETED;
+ }
+ else if(strncmp(p+1,"deleted", 7)==0)
+ {
+ p[0]='\0';
+ *value_state= VALUE_DELETED;
+ }
+ p= strchr(p+1, ';');
+ }
+}
+
+
+static Slapi_Entry *
+str2entry_fast( char *s, int flags, int read_stateinfo )
+{
+ Slapi_Entry *e;
+ char *next, *ptype=NULL;
+ int nvals= 0;
+ int del_nvals= 0;
+ int retmalloc = 0;
+ unsigned long attr_val_cnt = 0;
+ CSN *attributedeletioncsn= NULL; /* Moved to this level so that the JCM csn_free call below gets useful */
+ CSNSet *valuecsnset= NULL; /* Moved to this level so that the JCM csn_free call below gets useful */
+ CSN *maxcsn = NULL;
+
+ /*
+ * In 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.
+ */
+
+ LDAPDebug( LDAP_DEBUG_TRACE, "=> str2entry_fast\n", 0, 0, 0 );
+
+ e = slapi_entry_alloc();
+ slapi_entry_init(e,NULL,NULL);
+
+ /* dn + attributes */
+ next = s;
+
+ /* get the read lock of name2asi for performance purpose.
+ It reduces read locking by per-entry lock, instead of per-attribute.
+ */
+ attr_syntax_read_lock();
+
+ while ( (s = ldif_getline( &next )) != NULL &&
+ attr_val_cnt < ENTRY_MAX_ATTRIBUTE_VALUE_COUNT )
+ {
+ Slapi_Attr **a;
+ char *valuecharptr=NULL;
+ int valuelen;
+ CSNType attributecsntype;
+ int value_state= VALUE_NOTFOUND;
+ int attr_state= ATTRIBUTE_NOTFOUND;
+ int maxvals;
+ int del_maxvals;
+ char *type;
+ char *errmsg = NULL;
+
+ if ( *s == '\n' || *s == '\0' ) {
+ break;
+ }
+
+ if ( (retmalloc = ldif_parse_line( s, &type, &valuecharptr, &valuelen, &errmsg )) < 0 ) {
+ if ( errmsg != NULL ) {
+ LDAPDebug( LDAP_DEBUG_PARSE, "%s", errmsg, 0, 0 );
+ /* the memory below was not allocated by the slapi_ch_ functions */
+ slapi_ch_free( (void**)&errmsg );
+ }
+ LDAPDebug( LDAP_DEBUG_TRACE,
+ "<= str2entry_fast NULL (parse_line)\n", 0, 0, 0 );
+ continue;
+ }
+
+ /* We don't use errmsg anywhere later. free it to avoid leaking... */
+ /* the memory below was not allocated by the slapi_ch_ functions */
+ slapi_ch_free( (void**)&errmsg );
+
+ /*
+ * Extract the attribute and value CSNs from the attribute type.
+ */
+ attributecsntype= CSN_TYPE_NONE;
+ csn_free(&attributedeletioncsn); /* JCM - Do this more efficiently */
+ csnset_free(&valuecsnset);
+ value_state= VALUE_NOTFOUND;
+ attr_state= ATTRIBUTE_NOTFOUND;
+ str2entry_state_information_from_type(type,&valuecsnset,&attributedeletioncsn,&maxcsn,&value_state,&attr_state);
+ if(!read_stateinfo)
+ {
+ /* We are not maintaining state information */
+ if(value_state==VALUE_DELETED || attr_state==ATTRIBUTE_DELETED)
+ {
+ /* ignore deleted values and attributes */
+ /* the memory below was not allocated by the slapi_ch_ functions */
+ if (retmalloc) slapi_ch_free((void **) &valuecharptr);
+ continue;
+ }
+ /* Ignore CSNs */
+ csn_free(&attributedeletioncsn);
+ csnset_free(&valuecsnset);
+ }
+ /*
+ * We cache some stuff as we go around the loop.
+ */
+ if((ptype==NULL)||(strcasecmp(type,ptype) != 0))
+ {
+ ptype=type;
+ nvals = 0;
+ maxvals = 0;
+ del_nvals = 0;
+ del_maxvals = 0;
+ a = NULL;
+ }
+
+ if ( strcasecmp( type, "dn" ) == 0 )
+ {
+ if ( slapi_entry_get_dn_const(e)!=NULL )
+ {
+ char ebuf[ BUFSIZ ];
+ LDAPDebug( LDAP_DEBUG_ANY,
+ "str2entry_fast: entry has multiple dns \"%s\" and \"%s\" (second ignored)\n",
+ escape_string( slapi_entry_get_dn_const(e), ebuf ),
+ escape_string( valuecharptr, ebuf ), 0 );
+ /* the memory below was not allocated by the slapi_ch_ functions */
+ if (retmalloc) slapi_ch_free((void **) &valuecharptr);
+ continue;
+ }
+ slapi_entry_set_dn(e,slapi_ch_strdup( valuecharptr ));
+ /* the memory below was not allocated by the slapi_ch_ functions */
+ if (retmalloc) slapi_ch_free((void **) &valuecharptr);
+ continue;
+ }
+
+ /* retrieve uniqueid */
+ if ( strcasecmp (type, SLAPI_ATTR_UNIQUEID) == 0 ){
+
+ if (e->e_uniqueid != NULL){
+ LDAPDebug (LDAP_DEBUG_ANY, "str2entry_fast: entry has multiple "
+ "uniqueids %s and %s (second ignored)\n",
+ e->e_uniqueid, valuecharptr, 0);
+ }else{
+ /* name2asi will be locked in slapi_entry_set_uniqueid */
+ attr_syntax_unlock_read();
+ slapi_entry_set_uniqueid (e, slapi_ch_strdup(valuecharptr));
+ attr_syntax_read_lock();
+ }
+ /* the memory below was not allocated by the slapi_ch_ functions */
+ if (retmalloc) slapi_ch_free((void **) &valuecharptr);
+ continue;
+ }
+
+ if (strcasecmp(type,"objectclass") == 0) {
+ if (strcasecmp(valuecharptr,"ldapsubentry") == 0)
+ e->e_flags |= SLAPI_ENTRY_LDAPSUBENTRY;
+ if (strcasecmp(valuecharptr, SLAPI_ATTR_VALUE_TOMBSTONE) == 0)
+ e->e_flags |= SLAPI_ENTRY_FLAG_TOMBSTONE;
+ }
+
+ {
+ Slapi_Value *value= value_new(NULL,CSN_TYPE_NONE,NULL);
+ slapi_value_set( value, valuecharptr, valuelen );
+ /* the memory below was not allocated by the slapi_ch_ functions */
+ if (retmalloc) slapi_ch_free((void **) &valuecharptr);
+ value->v_csnset= valuecsnset;
+ valuecsnset= NULL;
+ if(a==NULL)
+ {
+ switch(attr_state)
+ {
+ case ATTRIBUTE_PRESENT:
+ if(attrlist_find_or_create_locking_optional(&e->e_attrs, type, &a, PR_FALSE, PR_TRUE)==0 /* Found */)
+ {
+ LDAPDebug (LDAP_DEBUG_ANY, "str2entry_fast: Error. Non-contiguous attribute values for %s\n", type, 0, 0);
+ PR_ASSERT(0);
+ continue;
+ }
+ break;
+ case ATTRIBUTE_DELETED:
+ if(attrlist_find_or_create_locking_optional(&e->e_deleted_attrs, type, &a, PR_FALSE, PR_TRUE)==0 /* Found */)
+ {
+ LDAPDebug (LDAP_DEBUG_ANY, "str2entry_fast: Error. Non-contiguous deleted attribute values for %s\n", type, 0, 0);
+ PR_ASSERT(0);
+ continue;
+ }
+ break;
+ case ATTRIBUTE_NOTFOUND:
+ LDAPDebug (LDAP_DEBUG_ANY, "str2entry_fast: Error. Non-contiguous deleted attribute values for %s\n", type, 0, 0);
+ PR_ASSERT(0);
+ continue;
+ /* break; ??? */
+ }
+
+ }
+ {
+ const CSN *distinguishedcsn= csnset_get_csn_of_type(value->v_csnset,CSN_TYPE_VALUE_DISTINGUISHED);
+ if(distinguishedcsn!=NULL)
+ {
+ entry_add_dncsn_ext(e,distinguishedcsn, ENTRY_DNCSN_INCREASING);
+ }
+ }
+ if(value_state==VALUE_DELETED)
+ {
+ /* consumes the value */
+ valuearray_add_value_fast(
+ &(*a)->a_deleted_values.va, /* JCM .va is private */
+ value,
+ del_nvals,
+ &del_maxvals,
+ 0/*!Exact*/,
+ 1/*Passin*/ );
+ del_nvals++;
+ }
+ else
+ {
+ /* consumes the value */
+ valuearray_add_value_fast(
+ &(*a)->a_present_values.va, /* JCM .va is private */
+ value,
+ nvals,
+ &maxvals,
+ 0 /*!Exact*/,
+ 1 /*Passin*/ );
+ nvals++;
+ }
+ if(attributedeletioncsn!=NULL)
+ {
+ attr_set_deletion_csn(*a,attributedeletioncsn);
+ }
+ }
+ csn_free(&attributedeletioncsn);
+ csnset_free(&valuecsnset);
+ attr_val_cnt++;
+ }
+ if ( attr_val_cnt >= ENTRY_MAX_ATTRIBUTE_VALUE_COUNT )
+ {
+ LDAPDebug( LDAP_DEBUG_ANY,
+ "str2entry_fast: entry %s exceeded max attribute value cound %ld\n",
+ slapi_entry_get_dn_const(e)?slapi_entry_get_dn_const(e):"unkown",
+ attr_val_cnt, 0 );
+ }
+ if (read_stateinfo && maxcsn)
+ {
+ e->e_maxcsn = maxcsn;
+ }
+
+ /* release read lock of name2asi, per-entry lock */
+ attr_syntax_unlock_read();
+
+ /* check to make sure there was a dn: line */
+ if ( slapi_entry_get_dn_const(e)==NULL ) {
+ if (!(SLAPI_STR2ENTRY_INCLUDE_VERSION_STR & flags))
+ LDAPDebug( LDAP_DEBUG_ANY, "str2entry_fast: entry has no dn\n",
+ 0, 0, 0 );
+ slapi_entry_free( e );
+ return( NULL );
+ }
+
+ LDAPDebug( LDAP_DEBUG_TRACE, "<= str2entry_fast 0x%x\n",
+ e, 0, 0 );
+ return( e );
+}
+
+
+#define STR2ENTRY_SMALL_BUFFER_SIZE 64
+#define STR2ENTRY_INITIAL_BERVAL_ARRAY_SIZE 8
+#define STR2ENTRY_VALUE_DUPCHECK_THRESHOLD 5
+
+typedef struct _entry_attr_data {
+ int ead_attrarrayindex;
+ const char *ead_attrtypename;
+ char ead_allocated; /* non-zero if this struct needs to be freed */
+} entry_attr_data;
+
+/* Structure which stores a tree for the attributes on the entry rather than the linked list on a regular entry struture */
+typedef struct _entry_attrs {
+ Avlnode *ea_attrlist;
+ int ea_attrdatacount;
+ entry_attr_data ea_attrdata[ STR2ENTRY_SMALL_BUFFER_SIZE ];
+} entry_attrs;
+
+typedef struct _str2entry_attr {
+ char *sa_type;
+ int sa_state;
+ struct valuearrayfast sa_present_values;
+ struct valuearrayfast sa_deleted_values;
+ int sa_numdups;
+ struct slapdplugin *sa_pi;
+ value_compare_fn_type sa_comparefn;
+ Avlnode *sa_vtree;
+ CSN *sa_attributedeletioncsn;
+} str2entry_attr;
+
+static void
+entry_attr_init(str2entry_attr *sa, const char *type, int state)
+{
+ sa->sa_type= slapi_ch_strdup(type);
+ sa->sa_state= state;
+ valuearrayfast_init(&sa->sa_present_values,NULL);
+ valuearrayfast_init(&sa->sa_deleted_values,NULL);
+ sa->sa_numdups= 0;
+ sa->sa_pi= NULL;
+ sa->sa_comparefn = NULL;
+ sa->sa_vtree= NULL;
+ sa->sa_attributedeletioncsn= NULL;
+}
+
+/*
+ * Create a tree of attributes.
+ */
+static int
+entry_attrs_new(entry_attrs **pea)
+{
+ entry_attrs *tmp = (entry_attrs *)slapi_ch_calloc(1, sizeof(entry_attrs));
+ if (NULL == tmp) {
+ return -1;
+ } else {
+ *pea = tmp;
+ return 0;
+ }
+}
+
+/*
+ * Delete an attribute type tree node.
+ */
+static void
+attr_type_node_free( caddr_t data )
+{
+ entry_attr_data *ea = (entry_attr_data *)data;
+ if ( NULL != ea && ea->ead_allocated ) {
+ slapi_ch_free( (void **)&ea );
+ }
+}
+
+
+/*
+ * Delete a tree of attributes.
+ */
+static void
+entry_attrs_delete(entry_attrs **pea)
+{
+ if (NULL != *pea) {
+ /* Delete the AVL tree */
+ avl_free((*pea)->ea_attrlist, attr_type_node_free);
+ slapi_ch_free((void**)pea);
+ }
+}
+
+static int
+attr_type_node_cmp( caddr_t d1, caddr_t d2 )
+{
+ /*
+ * A simple strcasecmp() will do here because we do not care
+ * about subtypes, etc. The slapi_str2entry() function treats
+ * subtypes as distinct attribute types, because that is how
+ * they are stored within the Slapi_Entry structure.
+ */
+ entry_attr_data *ea1= (entry_attr_data *)d1;
+ entry_attr_data *ea2= (entry_attr_data *)d2;
+ PR_ASSERT( ea1 != NULL );
+ PR_ASSERT( ea1->ead_attrtypename != NULL );
+ PR_ASSERT( ea2 != NULL );
+ PR_ASSERT( ea2->ead_attrtypename != NULL );
+ return strcasecmp(ea1->ead_attrtypename,ea2->ead_attrtypename);
+}
+
+/*
+ * Adds a new attribute to the attribute tree.
+ */
+static void
+entry_attrs_add(entry_attrs *ea, const char *atname, int atarrayindex)
+{
+ entry_attr_data *ead;
+
+ if ( ea->ea_attrdatacount < STR2ENTRY_SMALL_BUFFER_SIZE ) {
+ ead = &(ea->ea_attrdata[ ea->ea_attrdatacount ]);
+ ead->ead_allocated = 0;
+ } else {
+ ead = (entry_attr_data *)slapi_ch_malloc( sizeof( entry_attr_data ));
+ ead->ead_allocated = 1;
+ }
+ ++ea->ea_attrdatacount;
+ ead->ead_attrarrayindex = atarrayindex;
+ ead->ead_attrtypename = atname; /* a reference, not a strdup! */
+
+ avl_insert( &(ea->ea_attrlist), ead, attr_type_node_cmp, avl_dup_error );
+}
+
+/*
+ * Checks for an attribute in the tree. Returns the attr array index or -1
+ * if not found;
+ */
+static int
+entry_attrs_find(entry_attrs *ea,char *type)
+{
+ entry_attr_data tmpead = {0};
+ entry_attr_data *foundead;
+
+ tmpead.ead_attrtypename = type;
+ foundead = (entry_attr_data *)avl_find( ea->ea_attrlist, &tmpead,
+ attr_type_node_cmp );
+ return ( NULL != foundead ) ? foundead->ead_attrarrayindex : -1;
+}
+
+/* What's going on here then ?
+ Well, originally duplicate value checking was done by taking each
+ new value and comparing in turn against all the previous values.
+ Needless to say this was costly when there were many values.
+ So, new code was written which built a binary tree of index keys
+ for the values, and the test was done against the tree.
+ Nothing wrong with this, it speeded up the case where there were
+ many values nicely.
+ Unfortunately, when there are few values, it proved to be a significent
+ performance sink.
+ So, now we check the old way up 'till there's 5 attribute values, then
+ switch to the tree-based scheme.
+
+ Note that duplicate values are only checked for and ignored
+ if flags contains SLAPI_STR2ENTRY_REMOVEDUPVALS.
+ */
+
+static Slapi_Entry *
+str2entry_dupcheck( char *s, int flags, int read_stateinfo )
+{
+ Slapi_Entry *e;
+ str2entry_attr stack_attrs[STR2ENTRY_SMALL_BUFFER_SIZE];
+ str2entry_attr *dyn_attrs = NULL;
+ str2entry_attr *attrs = stack_attrs;
+ str2entry_attr *prev_attr= NULL;
+ int nattrs;
+ int maxattrs = STR2ENTRY_SMALL_BUFFER_SIZE;
+ char *type;
+ str2entry_attr *sa;
+ int i, j;
+ char *next=NULL;
+ char *valuecharptr=NULL;
+ char *errmsg = NULL;
+ int retmalloc = 0;
+ int rc;
+ int fast_dup_check = 0;
+ entry_attrs *ea = NULL;
+ int tree_attr_checking = 0;
+ int big_entry_attr_presence_check = 0;
+ int check_for_duplicate_values =
+ ( 0 != ( flags & SLAPI_STR2ENTRY_REMOVEDUPVALS ));
+ Slapi_Value *value = 0;
+ CSN *maxcsn= NULL;
+
+ LDAPDebug( LDAP_DEBUG_TRACE, "=> str2entry_dupcheck\n", 0, 0, 0 );
+
+ e = slapi_entry_alloc();
+ slapi_entry_init(e,NULL,NULL);
+ next = s;
+ nattrs = 0;
+
+ if (flags & SLAPI_STR2ENTRY_BIGENTRY)
+ {
+ big_entry_attr_presence_check = 1;
+ }
+ while ( (s = ldif_getline( &next )) != NULL )
+ {
+ CSN *attributedeletioncsn= NULL;
+ CSNType attributecsntype;
+ CSNSet *valuecsnset= NULL;
+ int value_state= VALUE_NOTFOUND;
+ int attr_state= VALUE_NOTFOUND;
+ int valuelen;
+
+ if ( *s == '\n' || *s == '\0' ) {
+ break;
+ }
+
+ if ( (retmalloc = ldif_parse_line( s, &type, &valuecharptr, &valuelen, &errmsg )) < 0 ) {
+ if ( errmsg != NULL ) {
+ LDAPDebug( LDAP_DEBUG_PARSE, "%s", errmsg, 0, 0 );
+ /* the memory below was not allocated by the slapi_ch_ functions */
+ slapi_ch_free( (void**)&errmsg );
+ }
+ LDAPDebug( LDAP_DEBUG_TRACE,
+ "<= slapi_str2entry NULL (parse_line)\n", 0, 0, 0 );
+ continue;
+ }
+
+ /* We don't use errmsg anywhere later. free it to avoid leaking... */
+ /* the memory below was not allocated by the slapi_ch_ functions */
+ slapi_ch_free( (void**)&errmsg );
+
+ /*
+ * Extract the attribute and value CSNs from the attribute type.
+ */
+ attributecsntype= CSN_TYPE_UNKNOWN;
+ csn_free(&attributedeletioncsn);
+ csnset_free(&valuecsnset);
+ value_state= VALUE_NOTFOUND;
+ attr_state= VALUE_NOTFOUND;
+ str2entry_state_information_from_type(type,&valuecsnset,&attributedeletioncsn,&maxcsn,&value_state,&attr_state);
+ if(!read_stateinfo)
+ {
+ /* We are not maintaining state information */
+ if(value_state==VALUE_DELETED || attr_state==ATTRIBUTE_DELETED)
+ {
+ /* ignore deleted values and attributes */
+ /* the memory below was not allocated by the slapi_ch_ functions */
+ if (retmalloc) slapi_ch_free((void **) &valuecharptr);
+ continue;
+ }
+ /* Ignore CSNs */
+ csn_free(&attributedeletioncsn);
+ csnset_free(&valuecsnset);
+ }
+
+ if ( strcasecmp( type, "dn" ) == 0 ) {
+ if ( slapi_entry_get_dn_const(e)!=NULL ) {
+ char ebuf[ BUFSIZ ];
+ LDAPDebug( LDAP_DEBUG_ANY,
+ "slapi_str2entry: entry has multiple dns \"%s\" and \"%s\" (second ignored)\n",
+ escape_string( slapi_entry_get_dn_const(e), ebuf ),
+ escape_string( valuecharptr, ebuf ), 0 );
+ /* the memory below was not allocated by the slapi_ch_ functions */
+ if (retmalloc) slapi_ch_free((void **) &valuecharptr);
+ continue;
+ }
+ slapi_entry_set_dn(e,slapi_ch_strdup( valuecharptr ));
+ /* the memory below was not allocated by the slapi_ch_ functions */
+ if (retmalloc) slapi_ch_free((void **) &valuecharptr);
+ continue;
+ }
+
+ /* retrieve uniqueid */
+ if ( strcasecmp (type, SLAPI_ATTR_UNIQUEID) == 0 ){
+
+ if (e->e_uniqueid != NULL){
+ LDAPDebug (LDAP_DEBUG_ANY, "slapi_str2entry: entry has multiple "
+ "uniqueids %s and %s (second ignored)\n",
+ e->e_uniqueid, valuecharptr, 0);
+ }else{
+ slapi_entry_set_uniqueid (e, slapi_ch_strdup(valuecharptr));
+ }
+ /* the memory below was not allocated by the slapi_ch_ functions */
+ if (retmalloc) slapi_ch_free((void **) &valuecharptr);
+ continue;
+ }
+
+ if (strcasecmp(type,"objectclass") == 0) {
+ if (strcasecmp(valuecharptr,"ldapsubentry") == 0)
+ e->e_flags |= SLAPI_ENTRY_LDAPSUBENTRY;
+ if (strcasecmp(valuecharptr, SLAPI_ATTR_VALUE_TOMBSTONE) == 0)
+ e->e_flags |= SLAPI_ENTRY_FLAG_TOMBSTONE;
+ }
+
+ /* Here we have a quick look to see if this attribute is a new
+ value for the type we last processed or a new type.
+ If not, we look to see if we've seen this attribute type before.
+ */
+ if ( prev_attr!=NULL && strcasecmp( type, prev_attr->sa_type ) != 0 )
+ {
+ /* Different attribute type - find it, or alloc new */
+ prev_attr = NULL;
+ /* The linear check below can take a while, so we change to use a tree if there are many attrs */
+ if (!big_entry_attr_presence_check)
+ {
+ for ( i = 0; i < nattrs; i++ )
+ {
+ if (strcasecmp( type, attrs[i].sa_type ) == 0 )
+ {
+ prev_attr = &attrs[i];
+ break;
+ }
+ }
+ }
+ else
+ {
+ int prev_index;
+
+ /* Did we just switch checking mechanism ? */
+ if (!tree_attr_checking)
+ {
+ /* If so then put the exising attrs into the tree */
+ if (0 != entry_attrs_new(&ea))
+ {
+ /* Something very bad happened */
+ return NULL;
+ }
+ for ( i = 0; i < nattrs; i++ )
+ {
+ entry_attrs_add(ea,attrs[i].sa_type, i);
+ }
+ tree_attr_checking = 1;
+ }
+ prev_index = entry_attrs_find(ea,type);
+ if ( prev_index >= 0 ) {
+ prev_attr = &attrs[prev_index];
+ /* (prev_attr!=NULL) Means that we already had that one in the set */
+ }
+ }
+ }
+ if ( prev_attr==NULL )
+ {
+ /* Haven't seen this type yet */
+ fast_dup_check = 1;
+ if ( nattrs == maxattrs )
+ {
+ /* Out of space - reallocate */
+ maxattrs *= 2;
+ if ( nattrs == STR2ENTRY_SMALL_BUFFER_SIZE ) {
+ /* out of fixed space - switch to dynamic */
+ PR_ASSERT( dyn_attrs == NULL );
+ dyn_attrs = (str2entry_attr *)
+ slapi_ch_malloc( sizeof( str2entry_attr ) *
+ maxattrs );
+ memcpy( dyn_attrs, stack_attrs,
+ STR2ENTRY_SMALL_BUFFER_SIZE *
+ sizeof( str2entry_attr ));
+ attrs = dyn_attrs;
+ } else {
+ /* Need more dynamic space */
+ dyn_attrs = (str2entry_attr *)
+ slapi_ch_realloc( (char *) dyn_attrs,
+ sizeof( str2entry_attr ) * maxattrs );
+ attrs = dyn_attrs; /* realloc may change base pointer */
+ }
+ }
+
+ /* Record the new type in the array */
+ entry_attr_init(&attrs[nattrs], type, attr_state);
+
+ if ( check_for_duplicate_values )
+ {
+ if ( slapi_attr_type2plugin( type,(void **)&(attrs[nattrs].sa_pi) ) != 0 )
+ {
+ LDAPDebug( LDAP_DEBUG_ANY,
+ "<= slapi_str2entry NULL (slapi_attr_type2plugin)\n",
+ 0, 0, 0 );
+ slapi_entry_free( e ); e = NULL;
+ goto free_and_return;
+ }
+ /* Get the comparison function for later use */
+ plugin_call_syntax_get_compare_fn( attrs[nattrs].sa_pi, &(attrs[nattrs].sa_comparefn));
+ /*
+ * If the compare function wasn't available,
+ * we have to revert to AVL-tree-based dup checking,
+ * which uses index keys for comparisons
+ */
+ if (NULL == attrs[nattrs].sa_comparefn)
+ {
+ fast_dup_check = 0;
+ }
+ /*
+ * If we are maintaining the attribute tree,
+ * then add the new attribute to the tree.
+ */
+ if (big_entry_attr_presence_check && tree_attr_checking)
+ {
+ entry_attrs_add(ea,attrs[nattrs].sa_type, nattrs);
+ }
+ }
+ prev_attr = &attrs[nattrs];
+ nattrs++;
+ }
+
+ sa = prev_attr; /* For readability */
+ value= value_new(NULL,CSN_TYPE_NONE,NULL);
+ slapi_value_set( value, valuecharptr, valuelen );
+ /* the memory below was not allocated by the slapi_ch_ functions */
+ if (retmalloc) slapi_ch_free((void **) &valuecharptr);
+ value->v_csnset= valuecsnset;
+ valuecsnset= NULL;
+ {
+ const CSN *distinguishedcsn= csnset_get_csn_of_type(value->v_csnset,CSN_TYPE_VALUE_DISTINGUISHED);
+ if(distinguishedcsn!=NULL)
+ {
+ entry_add_dncsn(e,distinguishedcsn);
+ }
+ }
+
+ if(value_state==VALUE_DELETED)
+ {
+ /*
+ * for deleted values, we do not want to perform a dupcheck against
+ * existing values. Also, we do not want to add it to the
+ * avl tree (if one is being maintained)
+ *
+ */
+ rc = 0; /* Presume no duplicate */
+ }
+ else if ( !check_for_duplicate_values )
+ {
+ rc = LDAP_SUCCESS; /* presume no duplicate */
+ } else {
+ /* For value dup checking, we either use brute-force, if there's a small number */
+ /* Or a tree-based approach if there's a large number. */
+ /* The tree code is expensive, which is why we don't use it unless there's many attributes */
+ rc = 0; /* Presume no duplicate */
+ if (fast_dup_check)
+ {
+ /* Fast dup-checking */
+ /* Do we now have so many values that we should switch to tree-based checking ? */
+ if (sa->sa_present_values.num > STR2ENTRY_VALUE_DUPCHECK_THRESHOLD)
+ {
+ /* Make the tree from the existing attr values */
+ rc= valuetree_add_valuearray( sa->sa_type, sa->sa_pi, sa->sa_present_values.va, &sa->sa_vtree, NULL);
+ /* Check if the value already exists, in the tree. */
+ rc= valuetree_add_value( sa->sa_type, sa->sa_pi, value, &sa->sa_vtree);
+ fast_dup_check = 0;
+ }
+ else
+ {
+ /* JCM - need an efficient valuearray function to do this */
+ /* Brute-force check */
+ for ( j = 0; j < sa->sa_present_values.num; j++ )/* JCM innards */
+ {
+ if (0 == sa->sa_comparefn(slapi_value_get_berval(value),slapi_value_get_berval(sa->sa_present_values.va[j])))/* JCM innards */
+ {
+ /* Oops---this value matches one already present */
+ rc = LDAP_TYPE_OR_VALUE_EXISTS;
+ break;
+ }
+ }
+ }
+ }
+ else
+ {
+ /* Check if the value already exists, in the tree. */
+ rc = valuetree_add_value( sa->sa_type, sa->sa_pi, value, &sa->sa_vtree);
+ }
+ }
+
+ if ( rc==LDAP_SUCCESS )
+ {
+ if(value_state==VALUE_DELETED)
+ {
+ valuearrayfast_add_value_passin(&sa->sa_deleted_values,value);
+ value= NULL; /* value was consumed */
+ }
+ else
+ {
+ valuearrayfast_add_value_passin(&sa->sa_present_values,value);
+ value= NULL; /* value was consumed */
+ }
+ if(attributedeletioncsn!=NULL)
+ {
+ sa->sa_attributedeletioncsn= attributedeletioncsn;
+ attributedeletioncsn= NULL; /* csn was consumed */
+ }
+ }
+ else if (rc==LDAP_TYPE_OR_VALUE_EXISTS)
+ {
+ sa->sa_numdups++;
+ }
+ else
+ {
+ /* Failure adding to value tree */
+ LDAPDebug( LDAP_DEBUG_ANY, "slapi_str2entry: unexpected failure %d constructing value tree\n", rc, 0, 0 );
+ slapi_entry_free( e ); e = NULL;
+ goto free_and_return;
+ }
+
+ slapi_value_free(&value);
+ }
+
+ /* All done with parsing. Now create the entry. */
+ /* check to make sure there was a dn: line */
+ if ( slapi_entry_get_dn_const(e)==NULL )
+ {
+ if (!(SLAPI_STR2ENTRY_INCLUDE_VERSION_STR & flags))
+ LDAPDebug( LDAP_DEBUG_ANY, "slapi_str2entry: entry has no dn\n",
+ 0, 0, 0 );
+ slapi_entry_free( e ); e = NULL;
+ goto free_and_return;
+ }
+
+ /* get the read lock of name2asi for performance purpose.
+ It reduces read locking by per-entry lock, instead of per-attribute.
+ */
+ attr_syntax_read_lock();
+
+ /*
+ * For each unique attribute in the array,
+ * Create a Slapi_Attr and set it's present and deleted values.
+ */
+ for ( i = 0; i < nattrs; i++ )
+ {
+ sa = &attrs[i];
+ if ( sa->sa_numdups > 0 )
+ {
+ if ( sa->sa_numdups > 1 ) {
+ LDAPDebug( LDAP_DEBUG_ANY, "%d duplicate values for attribute "
+ "type %s detected in entry %s. Extra values ignored.\n",
+ sa->sa_numdups, sa->sa_type, slapi_entry_get_dn_const(e) );
+ } else {
+ LDAPDebug( LDAP_DEBUG_ANY, "Duplicate value for attribute "
+ "type %s detected in entry %s. Extra value ignored.\n",
+ sa->sa_type, slapi_entry_get_dn_const(e), 0 );
+ }
+ }
+ {
+ Slapi_Attr **alist= NULL;
+ if(sa->sa_state==ATTRIBUTE_DELETED)
+ {
+ if(read_stateinfo)
+ {
+ alist= &e->e_deleted_attrs;
+ }
+ else
+ {
+ /*
+ * if we are not maintaining state info,
+ * ignore the deleted attributes
+ */
+ }
+ }
+ else
+ {
+ alist= &e->e_attrs;
+ }
+ if(alist!=NULL)
+ {
+ int maxvals = 0;
+ Slapi_Attr **a= NULL;
+ attrlist_find_or_create_locking_optional(alist, sa->sa_type, &a, PR_FALSE, PR_TRUE);
+ valuearray_add_valuearray_fast( /* JCM should be calling a valueset function */
+ &(*a)->a_present_values.va, /* JCM .va is private */
+ sa->sa_present_values.va,
+ 0, /* Currently there are no present values on the attribute */
+ sa->sa_present_values.num,
+ &maxvals,
+ 1/*Exact*/,
+ 1/*Passin*/);
+ sa->sa_present_values.num= 0; /* The values have been consumed */
+ maxvals = 0;
+ valuearray_add_valuearray_fast( /* JCM should be calling a valueset function */
+ &(*a)->a_deleted_values.va, /* JCM .va is private */
+ sa->sa_deleted_values.va,
+ 0, /* Currently there are no deleted values on the attribute */
+ sa->sa_deleted_values.num,
+ &maxvals,
+ 1/*Exact*/,
+ 1/*Passin*/);
+ sa->sa_deleted_values.num= 0; /* The values have been consumed */
+ if(sa->sa_attributedeletioncsn!=NULL)
+ {
+ attr_set_deletion_csn(*a,sa->sa_attributedeletioncsn);
+ }
+ }
+ }
+ }
+
+ /* release read lock of name2asi, per-entry lock */
+ attr_syntax_unlock_read();
+
+
+ /* Add the RDN values, if asked, and if not already present */
+ if ( flags & SLAPI_STR2ENTRY_ADDRDNVALS ) {
+ if ( slapi_entry_add_rdn_values( e ) != LDAP_SUCCESS ) {
+ LDAPDebug( LDAP_DEBUG_TRACE,
+ "slapi_str2entry: entry has badly formatted dn\n",
+ 0, 0, 0 );
+ slapi_entry_free( e ); e = NULL;
+ goto free_and_return;
+ }
+ }
+
+ if (read_stateinfo)
+ {
+ e->e_maxcsn = maxcsn;
+ }
+
+free_and_return:
+ for ( i = 0; i < nattrs; i++ )
+ {
+ slapi_ch_free((void **) &(attrs[ i ].sa_type));
+ valuearrayfast_done(&attrs[ i ].sa_present_values);
+ valuearrayfast_done(&attrs[ i ].sa_deleted_values);
+ valuetree_free( &attrs[ i ].sa_vtree );
+ }
+ if (tree_attr_checking)
+ {
+ entry_attrs_delete(&ea);
+ }
+ slapi_ch_free((void **) &dyn_attrs );
+ if (value) slapi_value_free(&value);
+
+ LDAPDebug( LDAP_DEBUG_TRACE, "<= str2entry_dupcheck 0x%x \"%s\"\n",
+ e, slapi_sdn_get_dn (slapi_entry_get_sdn_const(e)), 0 );
+ return e;
+}
+
+/*
+ *
+ * Convert an entry in LDIF format into a
+ * Slapi_Entry structure. If we can assume that the
+ * LDIF is well-formed we call str2entry_fast(),
+ * which does no error checking.
+ * Otherwise we do not assume well-formed LDIF, and
+ * call str2entry_dupcheck(), which checks for
+ * duplicate attribute values and does not assume
+ * that values are all contiguous.
+ *
+ * Well-formed LDIF has the following characteristics:
+ * 1) There are no duplicate attribute values
+ * 2) The RDN is an attribute of the entry
+ * 3) All values for a given attribute type are
+ * contiguous.
+ */
+#define SLAPI_STRENTRY_FLAGS_HANDLED_IN_SLAPI_STR2ENTRY \
+ ( SLAPI_STR2ENTRY_IGNORE_STATE \
+ | SLAPI_STR2ENTRY_EXPAND_OBJECTCLASSES \
+ | SLAPI_STR2ENTRY_TOMBSTONE_CHECK \
+ )
+
+#define SLAPI_STRENTRY_FLAGS_HANDLED_BY_STR2ENTRY_FAST \
+ ( SLAPI_STR2ENTRY_INCLUDE_VERSION_STR \
+ | SLAPI_STRENTRY_FLAGS_HANDLED_IN_SLAPI_STR2ENTRY \
+ )
+
+
+Slapi_Entry *
+slapi_str2entry( char *s, int flags )
+{
+ Slapi_Entry *e;
+ int read_stateinfo= ~( flags & SLAPI_STR2ENTRY_IGNORE_STATE );
+
+ LDAPDebug( LDAP_DEBUG_ARGS,
+ "slapi_str2entry: flags=0x%x, entry=\"%.50s...\"\n",
+ flags, s, 0 );
+
+
+ /*
+ * If well-formed LDIF has not been provided OR if a flag that is
+ * not handled by str2entry_fast() has been passed in, call the
+ * slower but more forgiving str2entry_dupcheck() function.
+ */
+ if ( 0 != ( flags & SLAPI_STR2ENTRY_NOT_WELL_FORMED_LDIF ) ||
+ 0 != ( flags & ~SLAPI_STRENTRY_FLAGS_HANDLED_BY_STR2ENTRY_FAST ))
+ {
+ e= str2entry_dupcheck( s, flags, read_stateinfo );
+ }
+ else
+ {
+ e= str2entry_fast( s, flags, read_stateinfo );
+ }
+ if (!e)
+ return e; /* e == NULL */
+
+ if ( flags & SLAPI_STR2ENTRY_EXPAND_OBJECTCLASSES )
+ {
+ slapi_schema_expand_objectclasses( e );
+ }
+
+ if ( flags & SLAPI_STR2ENTRY_TOMBSTONE_CHECK )
+ {
+ /*
+ * Check if the entry is a tombstone.
+ */
+ if(slapi_entry_attr_hasvalue(e, SLAPI_ATTR_OBJECTCLASS, SLAPI_ATTR_VALUE_TOMBSTONE))
+ {
+ e->e_flags |= SLAPI_ENTRY_FLAG_TOMBSTONE;
+ }
+ }
+ return e;
+}
+
+static size_t
+entry2str_internal_size_value( const char *attrtype, const Slapi_Value *v, int entry2str_ctrl, int attribute_state, int value_state )
+{
+ size_t elen= 0;
+ if(attrtype!=NULL)
+ {
+ size_t attrtypelen= strlen(attrtype);
+ if(entry2str_ctrl & SLAPI_DUMP_STATEINFO)
+ {
+ attrtypelen+= csnset_string_size(v->v_csnset);
+ if (attribute_state==ATTRIBUTE_DELETED)
+ {
+ attrtypelen += DELETED_ATTR_STRSIZE;
+ }
+ if(value_state==VALUE_DELETED)
+ {
+ attrtypelen += DELETED_VALUE_STRSIZE;
+ }
+ }
+ elen = LDIF_SIZE_NEEDED(attrtypelen, slapi_value_get_berval(v)->bv_len);
+ }
+ return elen;
+}
+
+static size_t
+entry2str_internal_size_valueset( const char *attrtype, const Slapi_ValueSet *vs, int entry2str_ctrl, int attribute_state, int value_state )
+{
+ size_t elen= 0;
+ if(!valueset_isempty(vs))
+ {
+ int i;
+ Slapi_Value **va= valueset_get_valuearray(vs);
+ for (i = 0; va[i]; i++)
+ {
+ elen+= entry2str_internal_size_value(attrtype, va[i], entry2str_ctrl,
+ attribute_state, value_state );
+ }
+ }
+ return elen;
+}
+
+static size_t
+entry2str_internal_size_attrlist( const Slapi_Attr *attrlist, int entry2str_ctrl, int attribute_state )
+{
+ size_t elen= 0;
+ const Slapi_Attr *a;
+ for (a= attrlist; a; a = a->a_next)
+ {
+ /* skip operational attributes if not requested */
+ if ((entry2str_ctrl & SLAPI_DUMP_NOOPATTRS) &&
+ slapi_attr_flag_is_set(a, SLAPI_ATTR_FLAG_OPATTR))
+ continue;
+
+ /* Count the space required for the present and deleted values */
+ elen+= entry2str_internal_size_valueset(a->a_type, &a->a_present_values,
+ entry2str_ctrl, attribute_state,
+ VALUE_PRESENT);
+ if(entry2str_ctrl & SLAPI_DUMP_STATEINFO)
+ {
+ elen+= entry2str_internal_size_valueset(a->a_type, &a->a_deleted_values,
+ entry2str_ctrl, attribute_state,
+ VALUE_DELETED);
+ /* ";adcsn-" + a->a_deletioncsn */
+ if ( a->a_deletioncsn )
+ {
+ elen+= 1 + LDIF_CSNPREFIX_MAXLENGTH + CSN_STRSIZE;
+ }
+ }
+ }
+ return elen;
+}
+
+static void
+entry2str_internal_put_value( const char *attrtype, const CSN *attrcsn, CSNType attrcsntype, int attr_state, const Slapi_Value *v, int value_state, char **ecur, char **typebuf, size_t *typebuf_len, int entry2str_ctrl )
+{
+ const char *type;
+ unsigned long options = 0;
+ const struct berval *bvp;
+ if(entry2str_ctrl & SLAPI_DUMP_STATEINFO)
+ {
+ char *p;
+ size_t attrtypelen= strlen(attrtype);
+ size_t attrcsnlen= 0;
+ size_t valuecsnlen= 0;
+ size_t need= attrtypelen+1;
+ if(attrcsn!=NULL)
+ {
+ /* ; csntype csn */
+ attrcsnlen= 1 + csn_string_size();
+ need+= attrcsnlen;
+ }
+ if(v->v_csnset!=NULL)
+ {
+ /* +(; csntype csn) */
+ valuecsnlen= csnset_string_size(v->v_csnset);
+ need+= valuecsnlen;
+ }
+ if(attr_state==ATTRIBUTE_DELETED)
+ {
+ need+= DELETED_ATTR_STRSIZE;
+ }
+ if(value_state==VALUE_DELETED)
+ {
+ need+= DELETED_VALUE_STRSIZE; /* ;deleted */
+ }
+ if(*typebuf_len<need)
+ {
+ *typebuf= (char*)slapi_ch_realloc(*typebuf,need);
+ *typebuf_len= need;
+ }
+ p= *typebuf;
+ type= p;
+ strcpy(p,attrtype);
+ p+= attrtypelen;
+ if(attrcsn!=NULL)
+ {
+ csn_as_attr_option_string(attrcsntype,attrcsn,p);
+ p+= attrcsnlen;
+ }
+ if(v->v_csnset!=NULL)
+ {
+ csnset_as_string(v->v_csnset,p);
+ p+= valuecsnlen;
+ }
+ if(attr_state==ATTRIBUTE_DELETED)
+ {
+ strcpy(p,DELETED_ATTR_STRING);
+ p+= DELETED_ATTR_STRSIZE;
+ }
+ if(value_state==VALUE_DELETED)
+ {
+ strcpy(p,DELETED_VALUE_STRING);
+ }
+ }
+ else
+ {
+ type= attrtype;
+ }
+ if (entry2str_ctrl & SLAPI_DUMP_NOWRAP)
+ options |= LDIF_OPT_NOWRAP;
+ if (entry2str_ctrl & SLAPI_DUMP_MINIMAL_ENCODING)
+ options |= LDIF_OPT_MINIMAL_ENCODING;
+ bvp = slapi_value_get_berval(v);
+ ldif_put_type_and_value_with_options( ecur, (char*)type, bvp->bv_val, bvp->bv_len, options );
+}
+
+static void
+entry2str_internal_put_valueset( const char *attrtype, const CSN *attrcsn, CSNType attrcsntype, int attr_state, const Slapi_ValueSet *vs, int value_state, char **ecur, char **typebuf, size_t *typebuf_len, int entry2str_ctrl )
+{
+ if(!valueset_isempty(vs))
+ {
+ int i;
+ Slapi_Value **va= valueset_get_valuearray(vs);
+ for ( i = 0; va[i] != NULL; i++ )
+ {
+ /* Attach the attribute deletion csn on the first value */
+ if((entry2str_ctrl & SLAPI_DUMP_STATEINFO) && i==0)
+ {
+ entry2str_internal_put_value( attrtype, attrcsn, attrcsntype, attr_state, va[i], value_state, ecur, typebuf, typebuf_len, entry2str_ctrl );
+ }
+ else
+ {
+ entry2str_internal_put_value( attrtype, NULL, CSN_TYPE_UNKNOWN, attr_state, va[i], value_state, ecur, typebuf, typebuf_len, entry2str_ctrl );
+ }
+ }
+ }
+}
+
+static void
+entry2str_internal_put_attrlist( const Slapi_Attr *attrlist, int attr_state, int entry2str_ctrl, char **ecur, char **typebuf, size_t *typebuf_len)
+{
+ const Slapi_Attr *a;
+
+ /* Put the present attributes */
+ for (a= attrlist; a; a = a->a_next)
+ {
+ /* skip operational attributes if not requested */
+ if ((entry2str_ctrl & SLAPI_DUMP_NOOPATTRS) &&
+ slapi_attr_flag_is_set(a, SLAPI_ATTR_FLAG_OPATTR))
+ continue;
+
+ /* don't dump uniqueid if not asked */
+ if (!(strcasecmp(a->a_type, SLAPI_ATTR_UNIQUEID) == 0 &&
+ !(SLAPI_DUMP_UNIQUEID & entry2str_ctrl)))
+ {
+ /* Putting present attribute values */
+ /* put "<type>:[:] <value>" line for each value */
+ int present_values= !valueset_isempty(&a->a_present_values);
+ if(present_values)
+ {
+ entry2str_internal_put_valueset(a->a_type, a->a_deletioncsn, CSN_TYPE_ATTRIBUTE_DELETED, attr_state, &a->a_present_values, VALUE_PRESENT, ecur, typebuf, typebuf_len, entry2str_ctrl);
+ }
+ if(entry2str_ctrl & SLAPI_DUMP_STATEINFO)
+ {
+ /* Putting deleted attribute values */
+ if(present_values)
+ {
+ entry2str_internal_put_valueset(a->a_type, NULL, CSN_TYPE_NONE, attr_state, &a->a_deleted_values, VALUE_DELETED, ecur, typebuf, typebuf_len, entry2str_ctrl);
+ }
+ else
+ {
+ /* There were no present values on which to place the ADCSN, so we put it on the first deleted value. */
+ entry2str_internal_put_valueset(a->a_type, a->a_deletioncsn, CSN_TYPE_ATTRIBUTE_DELETED, attr_state, &a->a_deleted_values, VALUE_DELETED, ecur, typebuf, typebuf_len, entry2str_ctrl);
+ }
+ }
+ }
+ }
+}
+
+static char *
+entry2str_internal( Slapi_Entry *e, int *len, int entry2str_ctrl )
+{
+ char *ebuf;
+ char *ecur;
+ size_t elen = 0;
+ size_t typebuf_len= 64;
+ char *typebuf= (char *)slapi_ch_malloc(typebuf_len);
+ Slapi_Value dnvalue;
+
+ /*
+ * In string format, an entry looks like this:
+ * dn: <dn>\n
+ * [<attr>: <value>\n]*
+ */
+
+ ecur = ebuf = NULL;
+
+ value_init(&dnvalue,NULL,CSN_TYPE_NONE,NULL);
+
+ /* find length of buffer needed to hold this entry */
+ if (slapi_entry_get_dn_const(e)!=NULL)
+ {
+ slapi_value_set_string(&dnvalue,slapi_entry_get_dn_const(e));
+ elen+= entry2str_internal_size_value( "dn", &dnvalue, entry2str_ctrl,
+ ATTRIBUTE_PRESENT, VALUE_PRESENT );
+ }
+
+ /* Count the space required for the present attributes */
+ elen+= entry2str_internal_size_attrlist( e->e_attrs, entry2str_ctrl, ATTRIBUTE_PRESENT );
+
+ /* Count the space required for the deleted attributes */
+ if(entry2str_ctrl & SLAPI_DUMP_STATEINFO)
+ {
+ elen+= entry2str_internal_size_attrlist( e->e_deleted_attrs, entry2str_ctrl,
+ ATTRIBUTE_DELETED );
+ }
+
+ elen += 1;
+ ecur = ebuf = (char *)slapi_ch_malloc(elen);
+
+ /* put the dn */
+ if ( slapi_entry_get_dn_const(e)!=NULL)
+ {
+ /* put "dn: <dn>" */
+ entry2str_internal_put_value("dn", NULL, CSN_TYPE_NONE, ATTRIBUTE_PRESENT, &dnvalue, VALUE_PRESENT, &ecur, &typebuf, &typebuf_len, entry2str_ctrl);
+ }
+
+ /* Put the present attributes */
+ entry2str_internal_put_attrlist( e->e_attrs, ATTRIBUTE_PRESENT, entry2str_ctrl, &ecur, &typebuf, &typebuf_len );
+
+ /* Put the deleted attributes */
+ if(entry2str_ctrl & SLAPI_DUMP_STATEINFO)
+ {
+ entry2str_internal_put_attrlist( e->e_deleted_attrs, ATTRIBUTE_DELETED, entry2str_ctrl, &ecur, &typebuf, &typebuf_len );
+ }
+
+ *ecur = '\0';
+ if ( (size_t)(ecur - ebuf + 1) > elen )
+ {
+ slapi_log_error (SLAPI_LOG_FATAL, NULL,
+ "entry2str_internal: array boundary wrote: bufsize=%d wrote=%d\n",
+ elen, (ecur - ebuf + 1));
+ }
+
+ if ( NULL != len ) {
+ *len = ecur - ebuf;
+ }
+
+ slapi_ch_free((void**)&typebuf);
+ value_done(&dnvalue);
+
+ return ebuf;
+}
+
+char *
+slapi_entry2str( Slapi_Entry *e, int *len )
+{
+ return entry2str_internal(e, len, 0);
+}
+
+char *
+slapi_entry2str_dump_uniqueid( Slapi_Entry *e, int *len )
+{
+ return entry2str_internal(e, len, SLAPI_DUMP_UNIQUEID);
+}
+
+char *
+slapi_entry2str_no_opattrs( Slapi_Entry *e, int *len )
+{
+ return entry2str_internal(e, len, SLAPI_DUMP_NOOPATTRS);
+}
+
+char *
+slapi_entry2str_with_options( Slapi_Entry *e, int *len, int options )
+{
+ return entry2str_internal(e, len, options);
+}
+
+static int entry_type = -1; /* The type number assigned by the Factory for 'Entry' */
+
+int
+get_entry_object_type()
+{
+ if(entry_type==-1)
+ {
+ /* The factory is given the name of the object type, in
+ * return for a type handle. Whenever the object is created
+ * or destroyed the factory is called with the handle so
+ * that it may call the constructors or destructors registered
+ * with it.
+ */
+ entry_type= factory_register_type(SLAPI_EXT_ENTRY,offsetof(Slapi_Entry,e_extension));
+ }
+ return entry_type;
+}
+
+/* ====== Slapi_Entry functions ====== */
+
+#ifdef ENTRY_DEBUG
+static void entry_dump( const Slapi_Entry *e, const char *text);
+#define ENTRY_DUMP(e,name) entry_dump(e,name)
+#else
+#define ENTRY_DUMP(e,name) ((void)0)
+#endif
+
+
+static int counters_created= 0;
+PR_DEFINE_COUNTER(slapi_entry_counter_created);
+PR_DEFINE_COUNTER(slapi_entry_counter_deleted);
+PR_DEFINE_COUNTER(slapi_entry_counter_exist);
+
+Slapi_Entry *
+slapi_entry_alloc()
+{
+ Slapi_Entry *e= (Slapi_Entry *) slapi_ch_calloc( 1, sizeof(struct slapi_entry) );
+ slapi_sdn_init(&e->e_sdn);
+ e->e_extension = factory_create_extension(get_entry_object_type(),e,NULL);
+ if(!counters_created)
+ {
+ PR_CREATE_COUNTER(slapi_entry_counter_created,"Slapi_Entry","created","");
+ PR_CREATE_COUNTER(slapi_entry_counter_deleted,"Slapi_Entry","deleted","");
+ PR_CREATE_COUNTER(slapi_entry_counter_exist,"Slapi_Entry","exist","");
+ counters_created= 1;
+ }
+ PR_INCREMENT_COUNTER(slapi_entry_counter_created);
+ PR_INCREMENT_COUNTER(slapi_entry_counter_exist);
+ ENTRY_DUMP(e,"slapi_entry_alloc");
+ return e;
+}
+
+/*
+ * WARNING - The DN is passed in *not* copied.
+ */
+void
+slapi_entry_init(Slapi_Entry *e, char *dn, Slapi_Attr *a)
+{
+ slapi_sdn_set_dn_passin(slapi_entry_get_sdn(e), dn);
+ e->e_uniqueid= NULL;
+ e->e_attrs= a;
+ e->e_dncsnset= NULL;
+ e->e_maxcsn= NULL;
+ e->e_deleted_attrs= NULL;
+ e->e_virtual_attrs= NULL;
+ e->e_virtual_watermark= 0;
+ e->e_virtual_lock= PR_NewRWLock(PR_RWLOCK_RANK_NONE, "vattrValueCache");
+ e->e_flags= 0;
+}
+
+void
+slapi_entry_free( Slapi_Entry *e ) /* JCM - Should be ** so that we can NULL the ptr */
+{
+ if(e!=NULL)
+ {
+ ENTRY_DUMP(e,"slapi_entry_free");
+ factory_destroy_extension(get_entry_object_type(),e,NULL/*Parent*/,&(e->e_extension));
+ slapi_sdn_done(&e->e_sdn);
+ csnset_free(&e->e_dncsnset);
+ csn_free(&e->e_maxcsn);
+ slapi_ch_free((void **)&e->e_uniqueid);
+ attrlist_free(e->e_attrs);
+ attrlist_free(e->e_deleted_attrs);
+ attrlist_free(e->e_virtual_attrs);
+ if(e->e_virtual_lock)
+ PR_DestroyRWLock(e->e_virtual_lock);
+ slapi_ch_free((void**)&e);
+ PR_INCREMENT_COUNTER(slapi_entry_counter_deleted);
+ PR_DECREMENT_COUNTER(slapi_entry_counter_exist);
+ }
+}
+
+static size_t slapi_attrlist_size(Slapi_Attr *attrs)
+{
+ size_t size = 0;
+ Slapi_Attr *a;
+
+ for (a= attrs; a; a = a->a_next) {
+ if (a->a_type) size += strlen(a->a_type) + 1;
+ size += valueset_size(&a->a_present_values);
+ size += valueset_size(&a->a_deleted_values);
+ /* Don't bother with a_listtofree. This is only set
+ * by a call to slapi_attr_get_values, which should
+ * never be used on a cache entry since it can cause
+ * the entry to grow without bound.
+ */
+ if (a->a_deletioncsn) size += sizeof(CSN);
+ size += sizeof(Slapi_Attr);
+ }
+
+ return size;
+}
+
+static size_t slapi_dn_size(Slapi_DN *sdn)
+{
+ size_t size = 0;
+
+ if (sdn == NULL) return 0;
+
+ if (sdn->dn) size += strlen(sdn->dn) + 1;
+ if (sdn->ndn) size *= 2;
+
+ return size;
+}
+
+/* return the approximate size of an entry --
+ * useful for checking cache sizes, etc
+ */
+size_t
+slapi_entry_size(Slapi_Entry *e)
+{
+ u_long size = 0;
+
+ /* doesn't include memory used by e_extension */
+
+ if (e->e_uniqueid) size += strlen(e->e_uniqueid) + 1;
+ if (e->e_dncsnset) size += csnset_size(e->e_dncsnset);
+ if (e->e_maxcsn) size += sizeof( CSN );
+ size += slapi_dn_size(&e->e_sdn);
+ size += slapi_attrlist_size(e->e_attrs);
+ if (e->e_deleted_attrs) size += slapi_attrlist_size(e->e_deleted_attrs);
+ if (e->e_virtual_attrs) size += slapi_attrlist_size(e->e_virtual_attrs);
+ size += sizeof(Slapi_Entry);
+
+ return size;
+}
+
+
+/*
+ * return a complete copy of entry pointed to by "e"
+ * LPXXX: entry extensions are not duplicated
+ */
+Slapi_Entry *
+slapi_entry_dup( const Slapi_Entry *e )
+{
+ Slapi_Entry *ec;
+ Slapi_Attr *a;
+ Slapi_Attr *lastattr= NULL;
+
+ PR_ASSERT( NULL != e );
+
+ ec = slapi_entry_alloc();
+
+ /*
+ * init the new entry--some things (eg. locks in the entry) are not dup'ed
+ */
+ slapi_entry_init(ec,NULL,NULL);
+
+ slapi_sdn_copy(slapi_entry_get_sdn_const(e),&ec->e_sdn);
+
+ /* duplicate the dncsn also */
+ ec->e_dncsnset= csnset_dup(e->e_dncsnset);
+ ec->e_maxcsn= csn_dup(e->e_maxcsn);
+
+ /* don't use slapi_entry_set_uniqueid here because
+ it will cause uniqueid to be added twice to the
+ attribute list
+ */
+ if ( e->e_uniqueid != NULL )
+ {
+ ec->e_uniqueid = slapi_ch_strdup( e->e_uniqueid ); /* JCM - UniqueID Dup function? */
+ }
+
+ for ( a = e->e_attrs; a != NULL; a = a->a_next )
+ {
+ Slapi_Attr *newattr= slapi_attr_dup(a);
+ if(lastattr==NULL)
+ {
+ ec->e_attrs= newattr;
+ }
+ else
+ {
+ lastattr->a_next= newattr;
+ }
+ lastattr= newattr;
+ }
+ lastattr= NULL;
+ for ( a = e->e_deleted_attrs; a != NULL; a = a->a_next )
+ {
+ Slapi_Attr *newattr= slapi_attr_dup(a);
+ if(lastattr==NULL)
+ {
+ ec->e_deleted_attrs= newattr;
+ }
+ else
+ {
+ lastattr->a_next= newattr;
+ }
+ lastattr= newattr;
+ }
+
+ /* Copy flags as well */
+ ec->e_flags = e->e_flags;
+
+ ENTRY_DUMP(ec,"slapi_entry_dup");
+ return( ec );
+}
+
+#ifdef ENTRY_DEBUG
+static void
+entry_dump( const Slapi_Entry *e, const char *text)
+{
+ const char *dn= slapi_entry_get_dn_const(e);
+ LDAPDebug( LDAP_DEBUG_ANY, "Entry %s ptr=%lx dn=%s\n", text, e, (dn==NULL?"NULL":dn));
+}
+#endif
+
+char *
+slapi_entry_get_dn( Slapi_Entry *e )
+{
+ /* jcm - This is evil... we have to cast away the const. */
+ return (char*)(slapi_sdn_get_dn(slapi_entry_get_sdn_const(e)));
+}
+char *
+slapi_entry_get_ndn( Slapi_Entry *e )
+{
+ /* jcm - This is evil... we have to cast away the const. */
+ return (char*)(slapi_sdn_get_ndn(slapi_entry_get_sdn_const(e)));
+}
+
+const Slapi_DN *
+slapi_entry_get_sdn_const( const Slapi_Entry *e )
+{
+ return &e->e_sdn;
+}
+
+Slapi_DN *
+slapi_entry_get_sdn( Slapi_Entry *e )
+{
+ return &e->e_sdn;
+}
+
+const char *
+slapi_entry_get_dn_const( const Slapi_Entry *e )
+{
+ return (slapi_sdn_get_dn(slapi_entry_get_sdn_const(e)));
+}
+
+/*
+ * WARNING - The DN is passed in *not* copied.
+ */
+void
+slapi_entry_set_dn( Slapi_Entry *e, char *dn )
+{
+ slapi_sdn_set_dn_passin(slapi_entry_get_sdn(e),dn);
+}
+
+void
+slapi_entry_set_sdn( Slapi_Entry *e, const Slapi_DN *sdn )
+{
+ slapi_sdn_copy(sdn,slapi_entry_get_sdn(e));
+}
+
+const char *
+slapi_entry_get_uniqueid( const Slapi_Entry *e )
+{
+ return( e->e_uniqueid );
+}
+
+/*
+ * WARNING - The UniqueID is passed in *not* copied.
+ */
+void
+slapi_entry_set_uniqueid( Slapi_Entry *e, char *uniqueid )
+{
+ e->e_uniqueid = uniqueid;
+
+ /* also add it to the list of attributes - it makes things easier */
+ slapi_entry_attr_set_charptr ( e, SLAPI_ATTR_UNIQUEID, uniqueid );
+}
+
+int
+slapi_entry_first_attr( const Slapi_Entry *e, Slapi_Attr **a )
+{
+ return slapi_entry_next_attr( e, NULL, a);
+}
+
+int
+slapi_entry_next_attr( const Slapi_Entry *e, Slapi_Attr *prevattr, Slapi_Attr **a )
+{
+ int done= 0;
+ /*
+ * We skip over any attributes that have no present values.
+ * Our state information storage scheme can cause this, since
+ * we have to hang onto the deleted value state information.
+ * <jcm - actually we don't do this any more... so this skipping
+ * may now be redundant.>
+ */
+ while(!done)
+ {
+ if(prevattr==NULL)
+ {
+ *a = e->e_attrs;
+ }
+ else
+ {
+ *a = prevattr->a_next;
+ }
+ if(*a!=NULL)
+ {
+ done= !valueset_isempty(&((*a)->a_present_values));
+ }
+ else
+ {
+ done= 1;
+ }
+ if(!done)
+ {
+ prevattr= *a;
+ }
+ }
+ return( *a ? 0 : -1 );
+}
+
+int
+slapi_entry_attr_find( const Slapi_Entry *e, const char *type, Slapi_Attr **a )
+{
+ int r= -1;
+ *a = attrlist_find( e->e_attrs, type );
+ if (*a != NULL)
+ {
+ if(valueset_isempty(&((*a)->a_present_values)))
+ {
+ /*
+ * We ignore attributes that have no present values.
+ * Our state information storage scheme can cause this, since
+ * we have to hang onto the deleted value state information.
+ */
+ *a= NULL;
+ }
+ else
+ {
+ r= 0;
+ }
+ }
+ return r;
+}
+
+/* the following functions control virtual attribute cache invalidation */
+
+static PRInt32 g_virtual_watermark = -1; /* good enough to init */
+
+int slapi_entry_vattrcache_watermark_isvalid(const Slapi_Entry *e)
+{
+ return e->e_virtual_watermark == g_virtual_watermark;
+}
+
+void slapi_entry_vattrcache_watermark_set(Slapi_Entry *e)
+{
+ e->e_virtual_watermark = g_virtual_watermark;
+}
+
+void slapi_entry_vattrcache_watermark_invalidate(Slapi_Entry *e)
+{
+ e->e_virtual_watermark = 0;
+}
+
+void slapi_entrycache_vattrcache_watermark_invalidate()
+{
+ PR_AtomicIncrement(&g_virtual_watermark);
+ if (g_virtual_watermark == 0) {
+ PR_AtomicIncrement(&g_virtual_watermark);
+ }
+}
+
+/*
+ * slapi_entry_vattrcache_findAndTest()
+ *
+ * returns:
+ * SLAPI_ENTRY_VATTR_NOT_RESOLVED--not found in vattrcache; *rc set to -1.
+ * SLAPI_ENTRY_VATTR_RESOLVED_ABSENT--present in vattrcache but empty value:
+ * means tjhat vattr type is not present in
+ * that entry.
+ * SLAPI_ENTRY_VATTR_RESOLVED_EXISTS--found vattr in the cache, in which
+ * case *rc contains the result of testing
+ * the filter f of type filter_type
+ * on the value of type in e.
+ * rc==-1=>not a filter match
+ * rc==0=>a filter match
+ * rc>0=>an LDAP error code.
+ */
+
+int
+slapi_entry_vattrcache_findAndTest( const Slapi_Entry *e, const char *type,
+ Slapi_Filter *f,
+ filter_type_t filter_type,
+ int *rc )
+{
+ Slapi_Attr *tmp_attr = NULL;
+
+ int r= SLAPI_ENTRY_VATTR_NOT_RESOLVED; /* assume not resolved yet */
+ *rc = -1;
+
+ if( slapi_vattrcache_iscacheable(type) &&
+ slapi_entry_vattrcache_watermark_isvalid(e) && e->e_virtual_attrs)
+ {
+
+ if(e->e_virtual_lock == NULL) {
+ return r;
+ }
+
+ vattrcache_entry_READ_LOCK(e);
+ tmp_attr = attrlist_find( e->e_virtual_attrs, type );
+ if (tmp_attr != NULL)
+ {
+ if(valueset_isempty(&(tmp_attr->a_present_values)))
+ {
+ /*
+ * this is a vattr that has been
+ * cached already but does not exist
+ */
+ r= SLAPI_ENTRY_VATTR_RESOLVED_ABSENT; /* hard coded for prototype */
+ }
+ else
+ {
+ /*
+ * this is a cached vattr--test the filter on it.
+ *
+ */
+ r= SLAPI_ENTRY_VATTR_RESOLVED_EXISTS;
+ if ( filter_type == FILTER_TYPE_AVA ) {
+ *rc = plugin_call_syntax_filter_ava( tmp_attr,
+ f->f_choice,
+ &f->f_ava );
+ } else if ( filter_type == FILTER_TYPE_SUBSTRING) {
+ *rc = plugin_call_syntax_filter_sub( tmp_attr,
+ &f->f_sub);
+ } else if ( filter_type == FILTER_TYPE_PRES ) {
+ /* type is there, that's all we need to know. */
+ *rc = 0;
+ }
+ }
+ }
+ vattrcache_entry_READ_UNLOCK(e);
+ }
+
+ return r;
+}
+
+/*
+ * slapi_entry_vattrcache_find_values_and_type_ex()
+ *
+ * returns:
+ * SLAPI_ENTRY_VATTR_NOT_RESOLVED--not found in vattrcache.
+ * SLAPI_ENTRY_VATTR_RESOLVED_ABSENT--found in vattrcache but empty value
+ * ==>that vattr type is not present in the
+ * entry.
+ * SLAPI_ENTRY_VATTR_RESOLVED_EXISTS--found vattr in the vattr cache,
+ * in which case **results is a
+ * pointer to a duped Slapi_Valueset
+ * containing the values of type and
+ * **actual_type_name is the actual type
+ * name.
+*/
+
+int
+slapi_entry_vattrcache_find_values_and_type_ex( const Slapi_Entry *e,
+ const char *type,
+ Slapi_ValueSet ***results,
+ char ***actual_type_name)
+{
+ Slapi_Attr *tmp_attr = NULL;
+
+ int r= SLAPI_ENTRY_VATTR_NOT_RESOLVED; /* assume not resolved yet */
+
+ if( slapi_vattrcache_iscacheable(type) &&
+ slapi_entry_vattrcache_watermark_isvalid(e) && e->e_virtual_attrs)
+ {
+
+ if(e->e_virtual_lock == NULL) {
+ return r;
+ }
+
+ vattrcache_entry_READ_LOCK(e);
+ tmp_attr = attrlist_find( e->e_virtual_attrs, type );
+ if (tmp_attr != NULL)
+ {
+ if(valueset_isempty(&(tmp_attr->a_present_values)))
+ {
+ /*
+ * this is a vattr that has been
+ * cached already but does not exist
+ */
+ r= SLAPI_ENTRY_VATTR_RESOLVED_ABSENT; /* hard coded for prototype */
+ }
+ else
+ {
+ /*
+ * this is a cached vattr
+ * return a duped copy of the values and type
+ */
+ char *vattr_type=NULL;
+
+ r= SLAPI_ENTRY_VATTR_RESOLVED_EXISTS;
+ *results = (Slapi_ValueSet**)slapi_ch_calloc(1, sizeof(*results));
+ **results = valueset_dup(&(tmp_attr->a_present_values));
+
+ *actual_type_name =
+ (char**)slapi_ch_malloc(sizeof(*actual_type_name));
+ slapi_attr_get_type( tmp_attr, &vattr_type );
+ **actual_type_name = strdup(vattr_type);
+
+ }
+ }
+ vattrcache_entry_READ_UNLOCK(e);
+ }
+
+ return r;
+}
+
+/*
+ * Deprecated in favour of slapi_entry_vattrcache_find_values_and_type_ex()
+ * which meshes better with slapi_vattr_values_get_sp_ex().
+*/
+SLAPI_DEPRECATED int
+slapi_entry_vattrcache_find_values_and_type( const Slapi_Entry *e,
+ const char *type,
+ Slapi_ValueSet **results,
+ char **actual_type_name)
+{
+ Slapi_Attr *tmp_attr = NULL;
+
+ int r= SLAPI_ENTRY_VATTR_NOT_RESOLVED; /* assume not resolved yet */
+
+ if( slapi_vattrcache_iscacheable(type) &&
+ slapi_entry_vattrcache_watermark_isvalid(e) && e->e_virtual_attrs)
+ {
+
+ if(e->e_virtual_lock == NULL) {
+ return r;
+ }
+
+ vattrcache_entry_READ_LOCK(e);
+ tmp_attr = attrlist_find( e->e_virtual_attrs, type );
+ if (tmp_attr != NULL)
+ {
+ if(valueset_isempty(&(tmp_attr->a_present_values)))
+ {
+ /*
+ * this is a vattr that has been
+ * cached already but does not exist
+ */
+ r= SLAPI_ENTRY_VATTR_RESOLVED_ABSENT; /* hard coded for prototype */
+ }
+ else
+ {
+ /*
+ * this is a cached vattr
+ * return a duped copy of the values and type
+ */
+ char *vattr_type=NULL;
+
+ r= SLAPI_ENTRY_VATTR_RESOLVED_EXISTS;
+ *results = valueset_dup(&(tmp_attr->a_present_values));
+
+ slapi_attr_get_type( tmp_attr, &vattr_type );
+ *actual_type_name = strdup(vattr_type);
+
+ }
+ }
+ vattrcache_entry_READ_UNLOCK(e);
+ }
+
+ return r;
+}
+
+SLAPI_DEPRECATED int
+slapi_entry_attr_merge( Slapi_Entry *e, const char *type, struct berval **vals )
+{
+ Slapi_Value **values= NULL;
+ int rc=0;
+ valuearray_init_bervalarray(vals,&values); /* JCM SLOW FUNCTION */
+ rc = slapi_entry_attr_merge_sv(e, type, values);
+ valuearray_free(&values);
+ return(rc);
+}
+
+int
+slapi_entry_attr_merge_sv(Slapi_Entry *e, const char *type, Slapi_Value **vals )
+{
+ attrlist_merge_valuearray( &e->e_attrs, type, vals );
+ return 0;
+}
+
+/*
+ * Merge this valuset for type into e's vattrcache list.
+ * Creates the type if necessary.
+ * Dups valset.
+ * Only merge's in cacheable vattrs.
+*/
+
+int
+slapi_entry_vattrcache_merge_sv(Slapi_Entry *e, const char *type,
+ Slapi_ValueSet *valset)
+{
+ Slapi_Value **vals = NULL;
+
+ /* only attempt to merge if it's a cacheable attribute */
+ if ( slapi_vattrcache_iscacheable(type) ) {
+
+ if(e->e_virtual_lock == NULL) {
+ return 0;
+ }
+
+ vattrcache_entry_WRITE_LOCK(e);
+
+ if(!slapi_entry_vattrcache_watermark_isvalid(e) && e->e_virtual_attrs)
+ {
+ attrlist_free(e->e_virtual_attrs);
+ e->e_virtual_attrs = NULL;
+ }
+
+ if(valset)
+ vals = valueset_get_valuearray(valset);
+
+ /* dups the type (if necessary) and vals */
+ attrlist_merge_valuearray( &e->e_virtual_attrs, type, vals);
+ slapi_entry_vattrcache_watermark_set(e);
+
+ vattrcache_entry_WRITE_UNLOCK(e);
+
+ }
+
+ return 0;
+}
+
+int
+slapi_entry_attr_delete( Slapi_Entry *e, const char *type )
+{
+ return( attrlist_delete(&e->e_attrs, type) );
+}
+
+SLAPI_DEPRECATED int
+slapi_entry_attr_replace( Slapi_Entry *e, const char *type, struct berval **vals )
+{
+ slapi_entry_attr_delete(e, type);
+ slapi_entry_attr_merge(e, type, vals);
+ return 0;
+}
+
+int
+slapi_entry_attr_replace_sv( Slapi_Entry *e, const char *type, Slapi_Value **vals )
+{
+ slapi_entry_attr_delete(e, type);
+ slapi_entry_attr_merge_sv(e, type, vals);
+ return 0;
+}
+
+int
+slapi_entry_add_value (Slapi_Entry *e, const char *type, const Slapi_Value *value)
+{
+ Slapi_Attr **a= NULL;
+ attrlist_find_or_create(&e->e_attrs, type, &a);
+ if(value != (Slapi_Value *) NULL) {
+ slapi_valueset_add_value ( &(*a)->a_present_values, value);
+ }
+ return 0;
+}
+
+
+int
+slapi_entry_add_string(Slapi_Entry *e, const char *type, const char *value)
+{
+ Slapi_Attr **a= NULL;
+ attrlist_find_or_create(&e->e_attrs, type, &a);
+ valueset_add_string ( &(*a)->a_present_values, value, CSN_TYPE_UNKNOWN, NULL);
+ return 0;
+}
+
+int
+slapi_entry_delete_string(Slapi_Entry *e, const char *type, const char *value)
+{
+ Slapi_Attr *a= attrlist_find(e->e_attrs, type);
+ if (a != NULL)
+ valueset_remove_string(a,&a->a_present_values, value);
+ return 0;
+}
+
+/* caller must free with slapi_ch_array_free */
+char **
+slapi_entry_attr_get_charray( const Slapi_Entry* e, const char *type)
+{
+ char **parray = NULL;
+ Slapi_Attr* attr = NULL;
+ slapi_entry_attr_find(e, type, &attr);
+ if(attr!=NULL)
+ {
+ int hint;
+ Slapi_Value *v = NULL;
+ for (hint = slapi_attr_first_value(attr, &v);
+ hint != -1;
+ hint = slapi_attr_next_value(attr, hint, &v))
+ {
+ const struct berval *bvp = slapi_value_get_berval(v);
+ char *p = slapi_ch_malloc(bvp->bv_len + 1);
+ memcpy(p, bvp->bv_val, bvp->bv_len);
+ p[bvp->bv_len]= '\0';
+ charray_add(&parray, p);
+ }
+ }
+ return parray;
+}
+
+char *
+slapi_entry_attr_get_charptr( const Slapi_Entry* e, const char *type)
+{
+ char *p= NULL;
+ Slapi_Attr* attr;
+ slapi_entry_attr_find(e, type, &attr);
+ if(attr!=NULL)
+ {
+ Slapi_Value *v;
+ const struct berval *bvp;
+ slapi_valueset_first_value( &attr->a_present_values, &v);
+ bvp = slapi_value_get_berval(v);
+ p= slapi_ch_malloc(bvp->bv_len + 1);
+ memcpy(p, bvp->bv_val, bvp->bv_len);
+ p[bvp->bv_len]= '\0';
+ }
+ return p;
+}
+
+int
+slapi_entry_attr_get_int( const Slapi_Entry* e, const char *type)
+{
+ int r= 0;
+ Slapi_Attr* attr;
+ slapi_entry_attr_find(e, type, &attr);
+ if (attr!=NULL)
+ {
+ Slapi_Value *v;
+ slapi_valueset_first_value( &attr->a_present_values, &v);
+ r= slapi_value_get_int(v);
+ }
+ return r;
+}
+
+unsigned int
+slapi_entry_attr_get_uint( const Slapi_Entry* e, const char *type)
+{
+ unsigned int r= 0;
+ Slapi_Attr* attr;
+ slapi_entry_attr_find(e, type, &attr);
+ if (attr!=NULL)
+ {
+ Slapi_Value *v;
+ slapi_valueset_first_value( &attr->a_present_values, &v);
+ r= slapi_value_get_uint(v);
+ }
+ return r;
+}
+
+long
+slapi_entry_attr_get_long( const Slapi_Entry* e, const char *type)
+{
+ long r = 0;
+ Slapi_Attr* attr;
+ slapi_entry_attr_find(e, type, &attr);
+ if (attr!=NULL)
+ {
+ Slapi_Value *v;
+ slapi_valueset_first_value( &attr->a_present_values, &v);
+ r = slapi_value_get_long(v);
+ }
+ return r;
+}
+
+unsigned long
+slapi_entry_attr_get_ulong( const Slapi_Entry* e, const char *type)
+{
+ unsigned long r = 0;
+ Slapi_Attr* attr;
+ slapi_entry_attr_find(e, type, &attr);
+ if (attr!=NULL)
+ {
+ Slapi_Value *v;
+ slapi_valueset_first_value( &attr->a_present_values, &v);
+ r = slapi_value_get_ulong(v);
+ }
+ return r;
+}
+
+void
+slapi_entry_attr_set_charptr( Slapi_Entry* e, const char *type, const char *value)
+{
+ struct berval bv;
+ struct berval *bvals[2];
+ bvals[0] = &bv;
+ bvals[1] = NULL;
+ bv.bv_val = (char*)value;
+ bv.bv_len = strlen( value );
+ slapi_entry_attr_replace( e, type, bvals );
+}
+
+void
+slapi_entry_attr_set_int( Slapi_Entry* e, const char *type, int l)
+{
+ char value[16];
+ struct berval bv;
+ struct berval *bvals[2];
+ bvals[0] = &bv;
+ bvals[1] = NULL;
+ sprintf(value,"%d",l);
+ bv.bv_val = value;
+ bv.bv_len = strlen( value );
+ slapi_entry_attr_replace( e, type, bvals );
+}
+
+void
+slapi_entry_attr_set_uint( Slapi_Entry* e, const char *type, unsigned int l)
+{
+ char value[16];
+ struct berval bv;
+ struct berval *bvals[2];
+ bvals[0] = &bv;
+ bvals[1] = NULL;
+ sprintf(value,"%u",l);
+ bv.bv_val = value;
+ bv.bv_len = strlen( value );
+ slapi_entry_attr_replace( e, type, bvals );
+}
+
+void
+slapi_entry_attr_set_long( Slapi_Entry* e, const char *type, long l)
+{
+ char value[16];
+ struct berval bv;
+ struct berval *bvals[2];
+ bvals[0] = &bv;
+ bvals[1] = NULL;
+ sprintf(value,"%ld",l);
+ bv.bv_val = value;
+ bv.bv_len = strlen( value );
+ slapi_entry_attr_replace( e, type, bvals );
+}
+
+void
+slapi_entry_attr_set_ulong( Slapi_Entry* e, const char *type, unsigned long l)
+{
+ char value[16];
+ struct berval bv;
+ struct berval *bvals[2];
+ bvals[0] = &bv;
+ bvals[1] = NULL;
+ sprintf(value,"%lu",l);
+ bv.bv_val = value;
+ bv.bv_len = strlen( value );
+ slapi_entry_attr_replace( e, type, bvals );
+}
+
+/* JCM: The strcasecmp below should really be a bervalcmp
+ * deprecatred in favour of slapi_entry_attr_has_syntax_value
+ * which does respect the syntax of the attribute type.
+*/
+
+SLAPI_DEPRECATED int
+slapi_entry_attr_hasvalue(const Slapi_Entry *e, const char *type, const char *value) /* JCM - (const char *) => (struct berval *) */
+{
+ int r= 0;
+ Slapi_Attr *attr;
+ Slapi_Value *sval;
+ if(slapi_entry_attr_find(e, type, &attr)==0)
+ {
+ int i= slapi_attr_first_value( attr, &sval );
+ while(!r && i!=-1)
+ {
+ const struct berval *val= slapi_value_get_berval(sval);
+ r= (strcasecmp(val->bv_val,value)==0);
+ i= slapi_attr_next_value( attr, i, &sval );
+ }
+ }
+ return r;
+}
+
+
+/*
+ * Checks if e contains an attr type with a value
+ * of value.
+ * Unlike slapi_entry_attr_hasvalue(), it does teh comparison
+ * respecting the syntax of type.
+ *
+ * returns non-zero if type has value in e, zero otherwise.
+ *
+ *
+*/
+
+int
+slapi_entry_attr_has_syntax_value(const Slapi_Entry *e,
+ const char *type,
+ const Slapi_Value *value)
+{
+ int r= 0;
+ Slapi_Attr *attr;
+
+ if(slapi_entry_attr_find(e, type, &attr)==0)
+ {
+ const struct berval *bv = slapi_value_get_berval(value);
+
+ if ( bv != NULL) {
+ r = (slapi_attr_value_find(attr, bv) == 0);
+ }
+
+ }
+
+ return r;
+}
+
+
+int
+slapi_entry_rdn_values_present( const Slapi_Entry *e )
+{
+ char **dns, **rdns;
+ int i, rc;
+ Slapi_Attr *attr;
+ struct ava ava;
+ const char *dn = slapi_entry_get_dn_const(e);
+
+ if (slapi_is_rootdse(dn))
+ return 1; /* the root dse has no RDN, so it should default to TRUE */
+
+ /* JCM Use the Slapi_RDN code */
+ rc = 1;
+ if ( (dns = ldap_explode_dn( slapi_entry_get_dn_const(e), 0 )) != NULL )
+ {
+ if ( (rdns = ldap_explode_rdn( dns[0], 0 )) != NULL )
+ {
+ for ( i = 0; rdns[i] != NULL; i++ )
+ {
+ if ( rdn2ava( rdns[i], &ava ) == 0 )
+ {
+ char *type = slapi_attr_syntax_normalize( ava.ava_type );
+ if ( slapi_entry_attr_find( e, type, &attr ) != 0 )
+ {
+ rc = 0;
+ }
+
+ slapi_ch_free((void **)&type);
+
+ if ( 0 == rc ) { /* attribute not found */
+ break;
+ }
+
+ if ( slapi_attr_value_find( attr, &(ava.ava_value) ) != 0 )
+ {
+ rc = 0;
+ break; /* value not found */
+ }
+ }
+ }
+ ldap_value_free( rdns );
+ } else {
+ rc = 0; /* Failure: the RDN seems invalid */
+ }
+
+ ldap_value_free( dns );
+ }
+ else
+ {
+ rc = 0; /* failure: the RDN seems to be invalid */
+ }
+
+ return( rc );
+}
+
+int
+slapi_entry_add_rdn_values( Slapi_Entry *e )
+{
+ const char *dn;
+ char **dns, **rdns;
+ int i, rc = LDAP_SUCCESS;
+ Slapi_Value *foundVal;
+ Slapi_Attr *attr;
+
+ if ( NULL == e || (dn = slapi_entry_get_dn_const(e))==NULL ) {
+ return( LDAP_SUCCESS );
+ }
+
+ if (slapi_is_rootdse(dn)) {
+ return( LDAP_SUCCESS );
+ }
+
+ /* JCM Use the Slapi_RDN code */
+ /* make sure RDN values are also in the entry */
+ if ( (dns = ldap_explode_dn( dn, 0 )) == NULL ) {
+ return( LDAP_INVALID_DN_SYNTAX );
+ }
+ if ( (rdns = ldap_explode_rdn( dns[0], 0 )) == NULL ) {
+ ldap_value_free( dns );
+ return( LDAP_INVALID_DN_SYNTAX );
+ }
+ ldap_value_free( dns );
+ for ( i = 0; rdns[i] != NULL && rc == LDAP_SUCCESS; i++ ) {
+ struct ava ava;
+ char *type;
+
+ if ( rdn2ava( rdns[i], &ava ) != 0 ) {
+ ldap_value_free( rdns );
+ return( LDAP_INVALID_DN_SYNTAX );
+ }
+
+ foundVal = NULL;
+
+ type = slapi_attr_syntax_normalize( ava.ava_type );
+
+ if ( slapi_entry_attr_find( e, type, &attr ) == 0 ) {
+ rc = plugin_call_syntax_filter_ava_sv(attr, LDAP_FILTER_EQUALITY,
+ &ava, &foundVal, 0);
+
+ if (rc == 0 && foundVal != NULL) {
+ const struct berval *bv = slapi_value_get_berval(foundVal);
+
+ /*
+ * A subtlety to consider is that LDAP does not
+ * allow two values which compare the same for
+ * equality in an attribute at once.
+ */
+
+ if ((ava.ava_value.bv_len != bv->bv_len) ||
+ (memcmp(ava.ava_value.bv_val, bv->bv_val, bv->bv_len) != 0)) {
+ /* bytes not identical so reject */
+ char avdbuf[BUFSIZ];
+ LDAPDebug(LDAP_DEBUG_TRACE, "RDN value is not identical to entry value for type %s in entry %s\n",
+ type, dn ? escape_string(dn,avdbuf) : "<null>", 0 );
+#if 0
+ /*
+ * This would be the right thing to do except that
+ * it breaks our own clients.
+ */
+ rc = LDAP_TYPE_OR_VALUE_EXISTS;
+#endif
+ }
+ /* exact same ava already present in entry, that's OK */
+ }
+ }
+
+ if (foundVal == NULL) {
+ struct berval *vals[2];
+
+ vals[0] = &ava.ava_value;
+ vals[1] = NULL;
+ rc = slapi_entry_add_values( e, type, vals );
+ }
+
+ slapi_ch_free( (void **)&type );
+ }
+ ldap_value_free( rdns );
+
+ return( rc );
+}
+
+/*
+ * Function: slapi_entry_has_children
+ *
+ * Returns: 0 if "p" has no children, 1 if "p" has children.
+ *
+ * Description: We (RJP+DB) modified this code to take advantage
+ * of the subordinatecount operational attribute that
+ * each entry now has.
+ *
+ * Author/Modifier: RJP
+ */
+int
+slapi_entry_has_children(const Slapi_Entry *entry)
+{
+ Slapi_Attr *attr;
+
+ LDAPDebug( LDAP_DEBUG_TRACE, "=> slapi_has_children( %s )\n", slapi_entry_get_dn_const(entry), 0, 0);
+
+ /*If the subordinatecount exists, and it's nonzero, then return 1.*/
+ if (slapi_entry_attr_find( entry, "numsubordinates", &attr) == 0)
+ {
+ Slapi_Value *sval;
+ slapi_attr_first_value( attr, &sval );
+ if(sval!=NULL)
+ {
+ const struct berval *bval = slapi_value_get_berval( sval );
+ if(bval!=NULL)
+ {
+ /* The entry has the attribute, and it's non-zero */
+ if (strcmp(bval->bv_val, "0") != 0)
+ {
+ LDAPDebug( LDAP_DEBUG_TRACE, "<= slapi_has_children 1\n", 0, 0, 0 );
+ return(1);
+ }
+ }
+ }
+ }
+ LDAPDebug( LDAP_DEBUG_TRACE, "<= slapi_has_children 0\n", 0, 0, 0 );
+ return(0);
+}
+
+/*
+ * Apply a set of modifications to an entry
+ */
+int
+entry_apply_mods( Slapi_Entry *e, LDAPMod **mods )
+{
+ int err, j;
+
+ LDAPDebug( LDAP_DEBUG_TRACE, "=> entry_apply_mods\n", 0, 0, 0 );
+
+ err = LDAP_SUCCESS;
+ for ( j = 0; mods[j] != NULL; j++ )
+ {
+ err= entry_apply_mod( e, mods[j] );
+ if ( err != LDAP_SUCCESS ) {
+ break;
+ }
+ }
+
+ LDAPDebug( LDAP_DEBUG_TRACE, "<= entry_apply_mods %d\n", err, 0, 0 );
+ return( err );
+}
+
+/*
+ * Apply a modification to an entry
+ */
+int
+entry_apply_mod( Slapi_Entry *e, const LDAPMod *mod )
+{
+ int i;
+ int err = LDAP_SUCCESS;
+ PRBool sawsubentry=PR_FALSE;
+
+ for ( i = 0; mod->mod_bvalues != NULL && mod->mod_bvalues[i] != NULL; i++ ) {
+ if((strcasecmp(mod->mod_type,"objectclass") == 0)
+ && (strncasecmp((const char *)mod->mod_bvalues[i]->bv_val,"ldapsubentry",mod->mod_bvalues[i]->bv_len) == 0))
+ sawsubentry=PR_TRUE;
+ LDAPDebug( LDAP_DEBUG_ARGS, " %s: %s\n", mod->mod_type, mod->mod_bvalues[i]->bv_val, 0 );
+ }
+
+ switch ( mod->mod_op & ~LDAP_MOD_BVALUES )
+ {
+ case LDAP_MOD_ADD:
+ LDAPDebug( LDAP_DEBUG_ARGS, " add: %s\n", mod->mod_type, 0, 0 );
+ if(sawsubentry) e->e_flags |= SLAPI_ENTRY_LDAPSUBENTRY;
+ err = slapi_entry_add_values( e, mod->mod_type, mod->mod_bvalues );
+ break;
+
+ case LDAP_MOD_DELETE:
+ LDAPDebug( LDAP_DEBUG_ARGS, " delete: %s\n", mod->mod_type, 0, 0 );
+ if(sawsubentry) e->e_flags |= 0;
+ err = slapi_entry_delete_values( e, mod->mod_type, mod->mod_bvalues );
+ break;
+
+ case LDAP_MOD_REPLACE:
+ LDAPDebug( LDAP_DEBUG_ARGS, " replace: %s\n", mod->mod_type, 0, 0 );
+ err = entry_replace_values( e, mod->mod_type, mod->mod_bvalues );
+ break;
+ }
+ LDAPDebug( LDAP_DEBUG_ARGS, " -\n", 0, 0, 0 );
+
+ return( err );
+}
+
+
+/*
+ * Add an array of "vals" to entry "e".
+ */
+SLAPI_DEPRECATED int
+slapi_entry_add_values(
+ Slapi_Entry *e,
+ const char *type,
+ struct berval **vals
+)
+{
+ Slapi_Value **values= NULL;
+ int rc=0;
+ valuearray_init_bervalarray(vals,&values); /* JCM SLOW FUNCTION */
+ rc=slapi_entry_add_values_sv(e,type,values);
+ valuearray_free(&values);
+ return(rc);
+}
+
+/*
+ * Add an array of "vals" to entry "e".
+ */
+int
+slapi_entry_add_values_sv(Slapi_Entry *e,
+ const char *type,
+ Slapi_Value **vals)
+{
+ int rc= LDAP_SUCCESS;
+ if (valuearray_isempty(vals))
+ {
+ /*
+ * No values to add (unexpected but acceptable).
+ */
+ }
+ else
+ {
+ Slapi_Attr **a= NULL;
+ Slapi_Attr **alist= &e->e_attrs;
+ attrlist_find_or_create(alist, type, &a);
+ rc= attr_add_valuearray(*a,vals,slapi_entry_get_dn_const(e));
+ }
+ return( rc );
+}
+
+/*
+ * Add a value set of "vs" to entry "e".
+ *
+ * 0 is success anything else failure.
+ */
+
+int
+slapi_entry_add_valueset(Slapi_Entry *e, const char *type, Slapi_ValueSet *vs)
+{
+ Slapi_Value *v;
+
+ int i= slapi_valueset_first_value(vs,&v);
+ while(i!=-1) {
+
+ slapi_entry_add_value( e, type, v);
+ i= slapi_valueset_next_value(vs,i,&v);
+ }/* while */
+
+ return(0);
+}
+
+
+/*
+ * Delete an array of bervals from entry.
+ *
+ * Note that if this function fails, it leaves the values for "type" within
+ * "e" in an indeterminate state. The present value set may be truncated.
+ */
+SLAPI_DEPRECATED int
+slapi_entry_delete_values(
+ Slapi_Entry *e,
+ const char *type,
+ struct berval **vals
+)
+{
+ Slapi_Value **values= NULL;
+ int rc=0;
+ valuearray_init_bervalarray(vals,&values); /* JCM SLOW FUNCTION */
+ rc=slapi_entry_delete_values_sv(e,type,values);
+ valuearray_free(&values);
+ return(rc);
+}
+
+
+static int
+delete_values_sv_internal(
+ Slapi_Entry *e,
+ const char *type,
+ Slapi_Value **valuestodelete,
+ int flags
+)
+{
+ Slapi_Attr *a;
+ int retVal= LDAP_SUCCESS;
+
+ /* delete the entire attribute */
+ if ( valuestodelete == NULL || valuestodelete[0] == NULL ){
+ LDAPDebug( LDAP_DEBUG_ARGS, "removing entire attribute %s\n",
+ type, 0, 0 );
+ return( attrlist_delete( &e->e_attrs, type) ?
+ LDAP_NO_SUCH_ATTRIBUTE : LDAP_SUCCESS );
+ }
+
+ /* delete specific values - find the attribute first */
+ a= attrlist_find(e->e_attrs, type);
+ if ( a == NULL ) {
+ LDAPDebug( LDAP_DEBUG_ARGS, "could not find attribute %s\n",
+ type, 0, 0 );
+ return( LDAP_NO_SUCH_ATTRIBUTE );
+ }
+
+ {
+ retVal= valueset_remove_valuearray(&a->a_present_values, a, valuestodelete, flags, NULL);
+ if(retVal==LDAP_SUCCESS)
+ {
+ /*
+ * all values have been deleted -- remove entire attribute
+ */
+ if ( valueset_isempty(&a->a_present_values) )
+ {
+ attrlist_delete( &e->e_attrs, a->a_type );
+ }
+ }
+ else
+ {
+ /* Failed
+ * - Duplicate value
+ * - Value not found
+ * - Operations error
+ */
+ if ( retVal==LDAP_OPERATIONS_ERROR )
+ {
+ LDAPDebug( LDAP_DEBUG_ANY, "Possible existing duplicate "
+ "value for attribute type %s found in "
+ "entry %s\n", a->a_type, slapi_entry_get_dn_const(e), 0 );
+ }
+ }
+ }
+
+ return( retVal );
+}
+
+
+/*
+ * Delete an array of present values from an entry.
+ *
+ * Note that if this function fails, it leaves the values for "type" within
+ * "e" in an indeterminate state. The present value set may be truncated.
+ */
+int
+slapi_entry_delete_values_sv(
+ Slapi_Entry *e,
+ const char *type,
+ Slapi_Value **valuestodelete
+)
+{
+ return( delete_values_sv_internal( e, type, valuestodelete,
+ 0 /* Do Not Ignore Errors */ ));
+}
+
+
+int
+entry_replace_values(
+ Slapi_Entry *e,
+ const char *type,
+ struct berval **vals
+)
+{
+ attrlist_replace( &e->e_attrs, type, vals );
+ return 0;
+}
+
+int
+slapi_entry_flag_is_set( const Slapi_Entry *e, unsigned char flag )
+{
+ return( e->e_flags & flag );
+}
+
+void slapi_entry_set_flag( Slapi_Entry *e, unsigned char flag)
+{
+ e->e_flags |= flag;
+}
+
+void slapi_entry_clear_flag( Slapi_Entry *e, unsigned char flag)
+{
+ e->e_flags &= ~flag;
+}
+
+
+/*
+ * Add the missing values in `vals' to an entry.
+ *
+ * Note that if this function fails, it leaves the values for "type" within
+ * "e" in an indeterminate state. The present value set may be truncated.
+ */
+int
+slapi_entry_merge_values_sv(
+ Slapi_Entry *e,
+ const char *type,
+ Slapi_Value **vals
+)
+{
+ int rc;
+
+ rc = delete_values_sv_internal( e, type, vals, SLAPI_VALUE_FLAG_IGNOREERROR );
+
+ if ( rc == LDAP_SUCCESS || rc == LDAP_NO_SUCH_ATTRIBUTE ) {
+ rc = slapi_entry_attr_merge_sv( e, type, vals );
+ }
+
+ return( rc );
+}
+
+void
+send_referrals_from_entry(Slapi_PBlock *pb, Slapi_Entry *referral)
+{
+ Slapi_Value *val=NULL;
+ Slapi_Attr *attr=NULL;
+ int i=0, numValues=0;
+ struct berval **refscopy=NULL;
+ struct berval **url=NULL;
+
+ slapi_entry_attr_find( referral, "ref", &attr );
+ if(attr != NULL) {
+ slapi_attr_get_numvalues(attr, &numValues );
+ if(numValues > 0) {
+ url=(struct berval **) slapi_ch_malloc((numValues + 1) * sizeof(struct berval*));
+ }
+ for (i = slapi_attr_first_value(attr, &val); i != -1;
+ i = slapi_attr_next_value(attr, i, &val)) {
+ url[i]=(struct berval*)slapi_value_get_berval(val);
+ }
+ url[numValues]=NULL;
+ }
+ refscopy = ref_adjust(pb, url, slapi_entry_get_sdn(referral), 0);
+ send_ldap_result(pb, LDAP_REFERRAL,
+ slapi_entry_get_dn(referral), NULL, 0, refscopy );
+ if(url != NULL) {
+ slapi_ch_free( (void **)&url );
+ }
+ if ( refscopy != NULL ) {
+ ber_bvecfree( refscopy );
+ }
+}
+
+/*
+ * slapi_entry_diff: perform diff between entry e1 and e2
+ * and set mods to smods which updates e1 to e2.
+ * diff_ctrl: SLAPI_DUMP_NOOPATTRS => skip operational attributes
+ */
+void
+slapi_entry_diff(Slapi_Mods *smods, Slapi_Entry *e1, Slapi_Entry *e2, int diff_ctrl)
+{
+ Slapi_Attr *e1_attr = NULL;
+ Slapi_Attr *e2_attr = NULL;
+ char *e1_attr_name = NULL;
+ char *e2_attr_name = NULL;
+ int rval = 0;
+
+ slapi_mods_init(smods, 0);
+
+ for (slapi_entry_first_attr(e1, &e1_attr); e1_attr;
+ slapi_entry_next_attr(e1, e1_attr, &e1_attr))
+ {
+ /* skip operational attributes if not requested */
+ if ((diff_ctrl & SLAPI_DUMP_NOOPATTRS) &&
+ slapi_attr_flag_is_set(e1_attr, SLAPI_ATTR_FLAG_OPATTR))
+ continue;
+
+ slapi_attr_get_type(e1_attr, &e1_attr_name);
+ rval = slapi_entry_attr_find(e2, e1_attr_name, &e2_attr);
+ if (0 == rval)
+ {
+ int i;
+ Slapi_Value *e1_val;
+ /* attr e1_attr_names is shared with e2 */
+ /* XXX: not very efficient.
+ * needs to be rewritten for the schema w/ lots of attributes
+ */
+ for (i = slapi_attr_first_value(e1_attr, &e1_val); i != -1;
+ i = slapi_attr_next_value(e1_attr, i, &e1_val))
+ {
+ if (0 != slapi_attr_value_find(e2_attr,
+ slapi_value_get_berval(e1_val)))
+ {
+ /* attr-value e1_val not found in e2_attr; replace it */
+ /* XXX: does not support multi-value here */
+ LDAPDebug(LDAP_DEBUG_TRACE,
+ "slapi_entry_diff: attr-val of %s is not in e2; "
+ "replace it\n",
+ e1_attr_name, 0, 0);
+ slapi_mods_add(smods, LDAP_MOD_REPLACE, e1_attr_name,
+ e1_val->bv.bv_len, e1_val->bv.bv_val);
+ }
+ }
+ }
+ else
+ {
+ /* attr e1_attr_names not found in e2 */
+ LDAPDebug(LDAP_DEBUG_TRACE,
+ "slapi_entry_diff: attr %s is not in e2; add it\n",
+ e1_attr_name, 0, 0);
+ slapi_mods_add_mod_values(smods, LDAP_MOD_ADD,
+ e1_attr_name,
+ attr_get_present_values(e1_attr));
+ }
+ }
+
+ for (slapi_entry_first_attr(e2, &e2_attr); e2_attr;
+ slapi_entry_next_attr(e2, e2_attr, &e2_attr)) {
+ /* skip operational attributes if not requested */
+ if ((diff_ctrl & SLAPI_DUMP_NOOPATTRS) &&
+ slapi_attr_flag_is_set(e2_attr, SLAPI_ATTR_FLAG_OPATTR))
+ continue;
+
+ slapi_attr_get_type(e2_attr, &e2_attr_name);
+ rval = slapi_entry_attr_find(e1, e2_attr_name, &e1_attr);
+ if (0 != rval)
+ {
+ /* attr e2_attr_names not in e1 */
+ LDAPDebug(LDAP_DEBUG_TRACE,
+ "slapi_entry_diff: attr %s is not in e1; delete it\n",
+ e2_attr_name, 0, 0);
+ slapi_mods_add_mod_values(smods, LDAP_MOD_DELETE, e2_attr_name, NULL);
+ }
+ }
+
+ return;
+}
+
+static int
+entry_cmp_with_dn(const void *e1, const void *e2)
+{
+ return slapi_sdn_compare(slapi_entry_get_sdn_const(*(Slapi_Entry **)e1),
+ slapi_entry_get_sdn_const(*(Slapi_Entry **)e2));
+}
+
+/* delete the entry (and sub entries if any) specified with dn */
+static void
+delete_subtree(Slapi_PBlock *pb, const char *dn, void *plg_id)
+{
+ Slapi_PBlock mypb;
+ int ret = 0;
+ int opresult;
+
+ slapi_search_internal_set_pb(pb, dn, LDAP_SCOPE_SUBTREE, "(objectclass=*)",
+ NULL, 0, NULL, NULL, plg_id, 0);
+ slapi_search_internal_pb(pb);
+
+ slapi_pblock_get(pb, SLAPI_PLUGIN_INTOP_RESULT, &ret);
+ if (ret == LDAP_SUCCESS) {
+ Slapi_Entry **entries = NULL;
+ Slapi_Entry **ep = NULL;
+ Slapi_DN *rootDN = slapi_sdn_new_dn_byval(dn);
+ slapi_pblock_get(pb, SLAPI_PLUGIN_INTOP_SEARCH_ENTRIES, &entries);
+ for (ep = entries; ep && *ep; ep++) {
+ const Slapi_DN *sdn = slapi_entry_get_sdn_const(*ep);
+
+ if (slapi_sdn_compare(sdn, rootDN) == 0)
+ continue;
+ pblock_init(&mypb);
+ slapi_delete_internal_set_pb(&mypb, slapi_sdn_get_dn(sdn),
+ NULL, NULL, plg_id, 0);
+ slapi_delete_internal_pb(&mypb);
+ slapi_pblock_get(&mypb, SLAPI_PLUGIN_INTOP_RESULT, &opresult);
+ pblock_done(&mypb);
+ }
+ slapi_sdn_free(&rootDN);
+ }
+ pblock_done(pb);
+
+ pblock_init(pb);
+ slapi_delete_internal_set_pb(pb, dn, NULL, NULL, plg_id, 0);
+ slapi_delete_internal_pb(pb);
+ slapi_pblock_get(pb, SLAPI_PLUGIN_INTOP_RESULT, &opresult);
+ pblock_done(pb);
+}
+
+/*
+ * slapi_entries_diff: diff between entry array old_entries and curr_entries
+ * (testall == 0) => return immediately after the 1st diff
+ * (testall != 0) => scan all the entries
+ * (force_update == 0) => just print the diff info
+ * (force_update != 0) => force to go back to old
+ *
+ * return 0, if identical
+ * return 1, otherwise
+ */
+int
+slapi_entries_diff(Slapi_Entry **old_entries, Slapi_Entry **curr_entries,
+ int testall, const char *logging_prestr,
+ const int force_update, void *plg_id)
+{
+ char *my_logging_prestr = "";
+ Slapi_Entry **oep, **cep;
+ int rval = 0;
+ Slapi_PBlock pb;
+#ifdef ENTRY_DIFF_DEBUG
+ int i;
+#endif
+
+ for (oep = old_entries; oep != NULL && *oep != NULL; oep++)
+ ;
+
+ qsort(old_entries, oep - old_entries, sizeof(Slapi_Entry **),
+ entry_cmp_with_dn);
+
+#ifdef ENTRY_DIFF_DEBUG
+ LDAPDebug(LDAP_DEBUG_TRACE, "Old entries:\n", 0, 0, 0);
+ for (oep = old_entries, i = 0; oep != NULL && *oep != NULL; oep++, i++)
+ {
+ LDAPDebug(LDAP_DEBUG_TRACE, "%d: %s\n", i, slapi_entry_get_dn_const(*oep), 0);
+ }
+#endif
+
+ for (cep = curr_entries; cep != NULL && *cep != NULL; cep++)
+ ;
+
+ qsort(curr_entries, cep - curr_entries, sizeof(Slapi_Entry **),
+ entry_cmp_with_dn);
+
+#ifdef ENTRY_DIFF_DEBUG
+ LDAPDebug(LDAP_DEBUG_TRACE, "New entries:\n", 0, 0, 0);
+ for (cep = curr_entries, i = 0; cep != NULL && *cep != NULL; cep++, i++)
+ {
+ LDAPDebug(LDAP_DEBUG_TRACE, "%d: %s\n", i, slapi_entry_get_dn_const(*cep), 0);
+ }
+#endif
+
+ if (NULL != logging_prestr && '\0' != *logging_prestr)
+ {
+ my_logging_prestr = (char *)slapi_ch_malloc(strlen(logging_prestr) + 2);
+ sprintf(my_logging_prestr, "%s ", logging_prestr);
+ }
+
+ for (oep = old_entries; oep != NULL && *oep != NULL; )
+ {
+ for (cep = curr_entries; cep != NULL && *cep != NULL; )
+ {
+ int dncmp = slapi_sdn_compare(slapi_entry_get_sdn_const(*oep),
+ slapi_entry_get_sdn_const(*cep));
+ if (force_update)
+ {
+ pblock_init(&pb);
+ }
+
+ if (0 == dncmp)
+ {
+ Slapi_Mods *smods = slapi_mods_new();
+ LDAPMod *mod;
+ int isfirst = 1;
+
+ /* check the attr diff and do modify */
+ slapi_entry_diff(smods, *oep, *cep, SLAPI_DUMP_NOOPATTRS);
+
+ for (mod = slapi_mods_get_first_mod(smods);
+ mod != NULL;
+ mod = slapi_mods_get_next_mod(smods))
+ {
+ rval = 1;
+ if (isfirst)
+ {
+ LDAPDebug(LDAP_DEBUG_ANY, "%sEntry %s\n", my_logging_prestr,
+ slapi_entry_get_dn_const(*oep), 0);
+ isfirst = 0;
+ }
+
+ switch (mod->mod_op & ~LDAP_MOD_BVALUES)
+ {
+ case LDAP_MOD_DELETE:
+ LDAPDebug(LDAP_DEBUG_ANY,
+ " Del Attribute %s Value %s\n",
+ mod->mod_type, mod->mod_bvalues?
+ mod->mod_bvalues[0]->bv_val:"N/A", 0);
+ break;
+ case LDAP_MOD_ADD:
+ LDAPDebug(LDAP_DEBUG_ANY,
+ " Add Attribute %s Value %s\n",
+ mod->mod_type, mod->mod_bvalues[0]->bv_val, 0);
+ break;
+ case LDAP_MOD_REPLACE:
+ LDAPDebug(LDAP_DEBUG_ANY,
+ " Rep Attribute %s Value %s\n",
+ mod->mod_type, mod->mod_bvalues[0]->bv_val, 0);
+ break;
+ default:
+ LDAPDebug(LDAP_DEBUG_ANY,
+ " Unknown op %d Attribute %s\n",
+ mod->mod_op & ~LDAP_MOD_BVALUES,
+ mod->mod_type, 0);
+ break;
+ }
+
+ if (!testall)
+ {
+ slapi_mods_free(&smods);
+ goto out;
+ }
+ }
+ if (0 == isfirst && force_update && testall)
+ {
+ slapi_modify_internal_set_pb(&pb,
+ slapi_entry_get_dn_const(*oep),
+ slapi_mods_get_ldapmods_byref(smods),
+ NULL, NULL, plg_id, 0);
+
+ slapi_modify_internal_pb(&pb);
+ }
+
+ slapi_mods_free(&smods);
+ oep++; cep++;
+ }
+ else if (dncmp > 0) /* old_entries does not have cep */
+ {
+ rval = 1;
+
+ LDAPDebug(LDAP_DEBUG_ANY, "Del %sEntry %s\n",
+ my_logging_prestr, slapi_entry_get_dn_const(*cep), 0);
+
+ if (testall)
+ {
+ if (force_update)
+ delete_subtree(&pb, slapi_entry_get_dn_const(*cep), plg_id);
+ }
+ else
+ {
+ goto out;
+ }
+ cep++;
+ }
+ else /* if (dncmp < 0) curr_entries does not have oep */
+ {
+ rval = 1;
+ LDAPDebug(LDAP_DEBUG_ANY, "Add %sEntry %s\n",
+ my_logging_prestr, slapi_entry_get_dn_const(*oep), 0);
+ if (testall)
+ {
+ if (force_update)
+ {
+ LDAPMod **mods;
+ slapi_entry2mods(*oep, NULL, &mods);
+ slapi_add_internal_set_pb(&pb,
+ slapi_entry_get_dn_const(*oep), mods, NULL, plg_id, 0);
+ slapi_add_internal_pb(&pb);
+ freepmods(mods);
+ }
+ }
+ else
+ {
+ goto out;
+ }
+ oep++;
+ }
+ if (force_update)
+ {
+ pblock_done(&pb);
+ }
+ }
+ }
+out:
+ if (NULL != logging_prestr && '\0' != *logging_prestr)
+ slapi_ch_free_string(&my_logging_prestr);
+
+ return rval;
+}
diff --git a/ldap/servers/slapd/entrywsi.c b/ldap/servers/slapd/entrywsi.c
new file mode 100644
index 00000000..8a39b941
--- /dev/null
+++ b/ldap/servers/slapd/entrywsi.c
@@ -0,0 +1,1151 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+
+/* entrywsi.c - routines for dealing with entries... With State Information */
+
+#include "slap.h"
+#include "slapi-plugin.h"
+
+static void resolve_attribute_state(Slapi_Entry *e, Slapi_Attr *a, int attribute_state);
+
+static int
+entry_present_value_to_deleted_value(Slapi_Attr *a, Slapi_Value *v)
+{
+ Slapi_Value *r= valueset_remove_value(a, &a->a_present_values, v);
+ if(r!=NULL)
+ {
+ slapi_valueset_add_value_ext(&a->a_deleted_values, r, SLAPI_VALUE_FLAG_PASSIN);
+ }
+ return LDAP_SUCCESS;
+}
+
+static int
+entry_present_value_to_zapped_value(Slapi_Attr *a, Slapi_Value *v)
+{
+ if(v!=NULL)
+ {
+ Slapi_Value *r= valueset_remove_value(a, &a->a_present_values, v);
+ if(r!=NULL)
+ {
+ slapi_value_free(&r);
+ }
+ }
+ return LDAP_SUCCESS;
+}
+
+static int
+entry_deleted_value_to_present_value(Slapi_Attr *a, Slapi_Value *v)
+{
+ Slapi_Value *r= valueset_remove_value(a, &a->a_deleted_values, v);
+ if(r!=NULL)
+ {
+ slapi_valueset_add_value_ext(&a->a_present_values, r, SLAPI_VALUE_FLAG_PASSIN);
+ }
+ return LDAP_SUCCESS;
+}
+
+static int
+entry_deleted_value_to_zapped_value(Slapi_Attr *a, Slapi_Value *v)
+{
+ if(v!=NULL)
+ {
+ Slapi_Value *r= valueset_remove_value(a, &a->a_deleted_values, v);
+ if(r!=NULL)
+ {
+ slapi_value_free(&r);
+ }
+ }
+ return LDAP_SUCCESS;
+}
+
+static int
+entry_present_attribute_to_deleted_attribute(Slapi_Entry *e, Slapi_Attr *a)
+{
+ attrlist_remove(&e->e_attrs,a->a_type);
+ attrlist_add(&e->e_deleted_attrs,a);
+ return LDAP_SUCCESS;
+}
+
+static int
+entry_deleted_attribute_to_present_attribute(Slapi_Entry *e, Slapi_Attr *a)
+{
+ attrlist_remove(&e->e_deleted_attrs,a->a_type);
+ attrlist_add(&e->e_attrs,a);
+ return LDAP_SUCCESS;
+}
+
+/*
+ * Get the first deleted attribute.
+ *
+ * Return 0: Return the type and the CSN of the deleted attribute.
+ * Return -1: There are no deleted attributes.
+ */
+int
+entry_first_deleted_attribute( const Slapi_Entry *e, Slapi_Attr **a)
+{
+ *a= e->e_deleted_attrs;
+ return( *a ? 0 : -1 );
+}
+
+/*
+ * Get the next deleted attribute.
+ *
+ * Return 0: the type and the CSN of the deleted attribute.
+ * Return -1: no deleted attributes.
+ */
+int
+entry_next_deleted_attribute( const Slapi_Entry *e, Slapi_Attr **a)
+{
+ *a= (*a)->a_next;
+ return( *a ? 0 : -1 );
+}
+
+const CSN *
+entry_get_maxcsn ( const Slapi_Entry *entry )
+{
+ return entry->e_maxcsn;
+}
+
+void
+entry_set_maxcsn ( Slapi_Entry *entry, const CSN *csn )
+{
+ if ( NULL == entry->e_maxcsn )
+ {
+ entry->e_maxcsn = csn_dup ( csn );
+ }
+ else if ( csn_compare ( entry->e_maxcsn, csn ) < 0 )
+ {
+ csn_init_by_csn ( entry->e_maxcsn, csn );
+ }
+}
+
+/*
+ * Get the DN CSN of an entry.
+ */
+const CSN *
+entry_get_dncsn(const Slapi_Entry *entry)
+{
+ return csnset_get_last_csn(entry->e_dncsnset);
+}
+
+/*
+ * Get the DN CSN set of an entry.
+ */
+const CSNSet *
+entry_get_dncsnset(const Slapi_Entry *entry)
+{
+ return entry->e_dncsnset;
+}
+
+/*
+ * Add a DN CSN to an entry.
+ */
+int
+entry_add_dncsn(Slapi_Entry *entry, const CSN *csn)
+{
+ PR_ASSERT(entry!=NULL);
+ if(!csnset_contains(entry->e_dncsnset,csn))
+ {
+ csnset_add_csn(&entry->e_dncsnset, CSN_TYPE_VALUE_DISTINGUISHED, csn);
+ }
+ return 0;
+}
+
+/*
+ * Add a DN CSN to an entry, but uses flags to control the behavior
+ * Using the ENTRY_DNCSN_INCREASING flag makes sure the csnset is in
+ * order of increasing csn. csnset_insert_csn may not be very fast, so
+ * we may have to revisit this if it becomes a performance problem.
+ * In most cases, storing the csn unsorted is ok since the server
+ * usually makes sure the csn is already in order. However, when doing
+ * a str2entry, the order is not preserved unless we sort it.
+ */
+int
+entry_add_dncsn_ext(Slapi_Entry *entry, const CSN *csn, PRUint32 flags)
+{
+ PR_ASSERT(entry!=NULL);
+ if(!csnset_contains(entry->e_dncsnset,csn))
+ {
+ if (flags & ENTRY_DNCSN_INCREASING)
+ {
+ csnset_insert_csn(&entry->e_dncsnset, CSN_TYPE_VALUE_DISTINGUISHED, csn);
+ }
+ else
+ {
+ csnset_add_csn(&entry->e_dncsnset, CSN_TYPE_VALUE_DISTINGUISHED, csn);
+ }
+ }
+ return 0;
+}
+
+/*
+ * Set the CSN for all the present values on the entry.
+ * This is only intended to be used for new entries
+ * being added.
+ */
+int
+entry_set_csn(Slapi_Entry *entry, const CSN *csn)
+{
+ Slapi_Attr *a;
+
+ PR_ASSERT(entry!=NULL);
+
+ slapi_entry_first_attr( entry, &a );
+ while(a!=NULL)
+ {
+ /*
+ * JCM - it'd be more efficient if the str2entry code
+ * set a flag on the attribute structure.
+ */
+ if(strcasecmp(a->a_type, SLAPI_ATTR_UNIQUEID)!=0)
+ {
+ attr_set_csn(a,csn);
+ }
+ slapi_entry_next_attr( entry, a, &a );
+ }
+ return 0;
+}
+
+/*
+ * Set the Distinguished CSN for the RDN components of the entry.
+ */
+void
+entry_add_rdn_csn(Slapi_Entry *e, const CSN *csn)
+{
+ char *type;
+ char *value;
+ int index;
+ const Slapi_DN *dn= slapi_entry_get_sdn_const(e);
+ Slapi_RDN *rdn= slapi_rdn_new_sdn(dn);
+ index= slapi_rdn_get_first(rdn, &type, &value);
+ while(index!=-1)
+ {
+ Slapi_Attr *a= NULL;
+ Slapi_Value *v= NULL;
+ entry_attr_find_wsi(e, type, &a);
+ if(a!=NULL)
+ {
+ struct berval bv;
+ bv.bv_len= strlen(value);
+ bv.bv_val= (void*)value;
+ attr_value_find_wsi(a, &bv, &v);
+ }
+ if(v!=NULL)
+ {
+ value_update_csn(v,CSN_TYPE_VALUE_DISTINGUISHED,csn);
+ }
+ else
+ {
+ /* JCM RDN component isn't a present value - this is illegal. */
+ }
+ index= slapi_rdn_get_next(rdn, index, &type, &value);
+ }
+ slapi_rdn_free(&rdn);
+}
+
+CSN*
+entry_assign_operation_csn ( Slapi_PBlock *pb, Slapi_Entry *e, Slapi_Entry *parententry )
+{
+ Slapi_Operation *op;
+ const CSN *basecsn = NULL;
+ const CSN *parententry_dncsn = NULL;
+ CSN *opcsn = NULL;
+
+ slapi_pblock_get ( pb, SLAPI_OPERATION, &op );
+
+ /*
+ * The replication pre-op would have set op->o_csngen_handler for
+ * user requests that are against a replica.
+ */
+ if ( op->o_csngen_handler )
+ {
+ /*
+ * Sync up the CSN generator so that the new csn is greater
+ * than the entry's maxcsn and/or the parent's max dncsn.
+ */
+ if ( e )
+ {
+ basecsn = entry_get_maxcsn ( e );
+ }
+ if ( parententry )
+ {
+ parententry_dncsn = entry_get_dncsn ( parententry );
+ if ( csn_compare ( parententry_dncsn, basecsn ) > 0 )
+ {
+ basecsn = parententry_dncsn;
+ }
+ }
+ opcsn = op->o_csngen_handler ( pb, basecsn );
+
+ if (NULL != opcsn)
+ {
+ operation_set_csn (op, opcsn);
+ }
+ }
+
+ return opcsn;
+}
+
+/*
+ * Purge state information from the entry older than csnUpTo
+ *
+ * if csnUpTo is NULL, get rid of all the CSN related info.
+ * if csnUpTo is non-NULL, purge all info older than csnUpTo
+ */
+void
+entry_purge_state_information(Slapi_Entry *e, const CSN *csnUpTo)
+{
+ Slapi_Attr *a=NULL;
+
+ PR_ASSERT(e!=NULL);
+
+ for(a = e->e_attrs; NULL != a; a = a->a_next)
+ {
+ /*
+ * we are passing in the entry so that we may be able to "optimize"
+ * the csn related information and roll it up higher to the level
+ * of entry
+ */
+ attr_purge_state_information(e, a, csnUpTo);
+ }
+ for(a = e->e_deleted_attrs; NULL != a; a = a->a_next)
+ {
+ /*
+ * we are passing in the entry so that we may be able to "optimize"
+ * the csn related information and roll it up higher to the level
+ * of entry
+ */
+ attr_purge_state_information(e, a, csnUpTo);
+ }
+}
+
+/*
+ * Look for the attribute on the present and deleted attribute lists.
+ */
+int
+entry_attr_find_wsi(Slapi_Entry *e, const char *type, Slapi_Attr **a)
+{
+ int retVal= ATTRIBUTE_NOTFOUND;
+
+ PR_ASSERT(e!=NULL);
+ PR_ASSERT(type!=NULL);
+ PR_ASSERT(a!=NULL);
+
+ /* Look on the present attribute list */
+ *a= attrlist_find(e->e_attrs,type);
+ if(*a!=NULL)
+ {
+ /* The attribute is present */
+ retVal= ATTRIBUTE_PRESENT;
+ }
+ else
+ {
+ /* Maybe the attribue was deleted... */
+ *a= attrlist_find(e->e_deleted_attrs,type);
+ if(*a!=NULL)
+ {
+ /* The attribute is deleted */
+ retVal= ATTRIBUTE_DELETED;
+ }
+ else
+ {
+ /* The attribute was not found */
+ retVal= ATTRIBUTE_NOTFOUND;
+ }
+ }
+ return retVal;
+}
+
+/*
+ * Add the attribute to the deleted attribute list.
+ *
+ * Consumes the attribute.
+ */
+int
+entry_add_deleted_attribute_wsi(Slapi_Entry *e, Slapi_Attr *a)
+{
+ PR_ASSERT( e!=NULL );
+ PR_ASSERT( a!=NULL );
+ attrlist_add(&e->e_deleted_attrs, a);
+ return 0;
+}
+
+/*
+ * Add the attribute to the present attribute list.
+ *
+ * Consumes the attribute.
+ */
+int
+entry_add_present_attribute_wsi(Slapi_Entry *e, Slapi_Attr *a)
+{
+ PR_ASSERT( e!=NULL );
+ PR_ASSERT( a!=NULL );
+ attrlist_add(&e->e_attrs, a);
+ return 0;
+}
+
+/*
+ * Add a list of values to the attribute, whilst maintaining state information.
+ *
+ * Preserves LDAP Information Model constraints,
+ * returning an LDAP result code.
+ */
+static int
+entry_add_present_values_wsi(Slapi_Entry *e, const char *type, struct berval **bervals, const CSN *csn, int urp, long flags)
+{
+ int retVal= LDAP_SUCCESS;
+ Slapi_Value **valuestoadd = NULL;
+ valuearray_init_bervalarray(bervals,&valuestoadd); /* JCM SLOW FUNCTION */
+ if(!valuearray_isempty(valuestoadd))
+ {
+ Slapi_Attr *a= NULL;
+ long a_flags_orig;
+ int attr_state= entry_attr_find_wsi(e, type, &a);
+ if (ATTRIBUTE_NOTFOUND == attr_state)
+ {
+ /* Create a new attribute */
+ a = slapi_attr_new();
+ slapi_attr_init(a, type);
+ attrlist_add(&e->e_attrs, a);
+ }
+ a_flags_orig = a->a_flags;
+ a->a_flags |= flags;
+ if(urp)
+ {
+ /*
+ * Consolidate a->a_present_values and the pending values:
+ * Delete the pending values from a->a_present_values
+ * and transfer their csnsets to valuestoadd.
+ */
+ valueset_remove_valuearray (&a->a_present_values, a, valuestoadd,
+ SLAPI_VALUE_FLAG_IGNOREERROR |
+ SLAPI_VALUE_FLAG_PRESERVECSNSET, NULL);
+ /*
+ * Consolidate a->a_deleted_values and the pending values
+ * similarly.
+ */
+ valueset_remove_valuearray (&a->a_deleted_values, a, valuestoadd,
+ SLAPI_VALUE_FLAG_IGNOREERROR |
+ SLAPI_VALUE_FLAG_PRESERVECSNSET, NULL);
+
+ /* Append the pending values to a->a_present_values */
+ valuearray_update_csn (valuestoadd,CSN_TYPE_VALUE_UPDATED,csn);
+ valueset_add_valuearray_ext(&a->a_present_values, valuestoadd, SLAPI_VALUE_FLAG_PASSIN);
+ slapi_ch_free ( (void **)&valuestoadd );
+
+ /*
+ * Now delete non-RDN values from a->a_present_values; and
+ * restore possible RDN values from a->a_deleted_values
+ */
+ resolve_attribute_state(e, a, attr_state);
+ retVal= LDAP_SUCCESS;
+ }
+ else
+ {
+ Slapi_Value **deletedvalues= NULL;
+ switch(attr_state)
+ {
+ case ATTRIBUTE_PRESENT:
+ /* The attribute is already on the present list */
+ break;
+ case ATTRIBUTE_DELETED:
+ /* Move the deleted attribute onto the present list */
+ entry_deleted_attribute_to_present_attribute(e, a);
+ break;
+ case ATTRIBUTE_NOTFOUND:
+ /* No-op - attribute was initialized & added to entry above */
+ break;
+ }
+ /* Check if any of the values to be added are on the deleted list */
+ valueset_remove_valuearray(&a->a_deleted_values, a, valuestoadd, SLAPI_VALUE_FLAG_IGNOREERROR,&deletedvalues); /* JCM Check return code */
+ if(deletedvalues!=NULL && deletedvalues[0]!=NULL)
+ {
+ /* Some of the values to be added were on the deleted list */
+ Slapi_Value **v= NULL;
+ Slapi_ValueSet vs;
+ /* Add each deleted value to the present list */
+ valuearray_update_csn(deletedvalues,CSN_TYPE_VALUE_UPDATED,csn);
+ valueset_add_valuearray_ext(&a->a_present_values, deletedvalues, SLAPI_VALUE_FLAG_PASSIN);
+ /* Remove the deleted values from the values to add */
+ valueset_set_valuearray_passin(&vs,valuestoadd);
+ valueset_remove_valuearray(&vs, a, deletedvalues, SLAPI_VALUE_FLAG_IGNOREERROR, &v);
+ valuestoadd= valueset_get_valuearray(&vs);
+ valuearray_free(&v);
+ slapi_ch_free((void **)&deletedvalues);
+ }
+ valuearray_update_csn(valuestoadd,CSN_TYPE_VALUE_UPDATED,csn);
+ retVal= attr_add_valuearray(a, valuestoadd, slapi_entry_get_dn_const(e));
+ valuearray_free(&valuestoadd);
+ }
+ a->a_flags = a_flags_orig;
+ }
+ return(retVal);
+}
+
+/*
+ * Delete a list of values from an attribute, whilst maintaining state information.
+ *
+ * Preserves LDAP Information Model constraints,
+ * returning an LDAP result code.
+ */
+static int
+entry_delete_present_values_wsi(Slapi_Entry *e, const char *type, struct berval **vals, const CSN *csn, int urp, int mod_op)
+{
+ int retVal= LDAP_SUCCESS;
+ Slapi_Attr *a= NULL;
+ int attr_state= entry_attr_find_wsi(e, type, &a);
+ if(attr_state==ATTRIBUTE_PRESENT || (attr_state==ATTRIBUTE_DELETED && urp))
+ {
+ /* The attribute is on the present list, or the deleted list and we're doing URP */
+ if ( vals == NULL || vals[0] == NULL )
+ {
+ /* delete the entire attribute */
+ LDAPDebug( LDAP_DEBUG_ARGS, "removing entire attribute %s\n", type, 0, 0 );
+ attr_set_deletion_csn(a,csn);
+ if(urp)
+ {
+ resolve_attribute_state(e, a, attr_state); /* ABSOLVED */
+ }
+ else
+ {
+ if(!slapi_attr_flag_is_set(a,SLAPI_ATTR_FLAG_SINGLE))
+ {
+ /* We don't maintain a deleted value list for single valued attributes */
+ valueset_add_valueset(&a->a_deleted_values, &a->a_present_values); /* JCM Would be better to passin the valuestodelete */
+ }
+ slapi_valueset_done(&a->a_present_values);
+ entry_present_attribute_to_deleted_attribute(e, a);
+ }
+ retVal= LDAP_SUCCESS; /* This Operation always succeeds when the attribute is Present */
+ }
+ else
+ {
+ /* delete some specific values */
+ Slapi_Value **valuestodelete= NULL;
+ valuearray_init_bervalarray(vals,&valuestodelete); /* JCM SLOW FUNCTION */
+ if(urp)
+ {
+ Slapi_Value **valuesupdated= NULL;
+ valueset_update_csn_for_valuearray(&a->a_present_values, a, valuestodelete, CSN_TYPE_VALUE_DELETED, csn, &valuesupdated);
+ /* if we removed the last value, we need to mark the attribute as deleted
+ the resolve_attribute_state() code will "resurrect" the attribute if
+ there are present values with a later CSN - otherwise, even though
+ the value will be updated with a VDCSN which is later than the VUCSN,
+ the attribute will not be deleted */
+ if(slapi_attr_flag_is_set(a,SLAPI_ATTR_FLAG_SINGLE) && valuesupdated &&
+ *valuesupdated)
+ {
+ attr_set_deletion_csn(a,csn);
+ }
+ valuearray_free(&valuesupdated);
+ valueset_update_csn_for_valuearray(&a->a_deleted_values, a, valuestodelete, CSN_TYPE_VALUE_DELETED, csn, &valuesupdated);
+ valuearray_free(&valuesupdated);
+ valuearray_update_csn(valuestodelete,CSN_TYPE_VALUE_DELETED,csn);
+ valueset_add_valuearray_ext(&a->a_deleted_values, valuestodelete, SLAPI_VALUE_FLAG_PASSIN);
+ /* all the elements in valuestodelete are passed;
+ * should free valuestodelete only (don't call valuearray_free)
+ * [622023] */
+ slapi_ch_free((void **)&valuestodelete);
+ resolve_attribute_state(e, a, attr_state);
+ retVal= LDAP_SUCCESS;
+ }
+ else
+ {
+ Slapi_Value **deletedvalues= NULL;
+ retVal= valueset_remove_valuearray(&a->a_present_values, a, valuestodelete, 0 /* Do Not Ignore Errors */,&deletedvalues);
+ if(retVal==LDAP_SUCCESS && deletedvalues != NULL)
+ {
+ if(!slapi_attr_flag_is_set(a,SLAPI_ATTR_FLAG_SINGLE))
+ {
+ /* We don't maintain a deleted value list for single valued attributes */
+ /* Add each deleted value to the deleted set */
+ valuearray_update_csn(deletedvalues,CSN_TYPE_VALUE_DELETED,csn);
+ valueset_add_valuearray_ext(&a->a_deleted_values, deletedvalues, SLAPI_VALUE_FLAG_PASSIN);
+ slapi_ch_free((void **)&deletedvalues);
+ }
+ else {
+ valuearray_free(&deletedvalues);
+ }
+ if(valueset_isempty(&a->a_present_values))
+ {
+ /* There are no present values, so move the
+ * attribute to the deleted attribute list. */
+ entry_present_attribute_to_deleted_attribute(e, a);
+ }
+ }
+ else if (retVal != LDAP_SUCCESS)
+ {
+ /* Failed
+ * - Duplicate value
+ * - Value not found
+ * - Operations error
+ */
+ if ( retVal==LDAP_OPERATIONS_ERROR )
+ {
+ LDAPDebug( LDAP_DEBUG_ANY, "Possible existing duplicate "
+ "value for attribute type %s found in "
+ "entry %s\n", a->a_type, slapi_entry_get_dn_const(e), 0 );
+ }
+ }
+ valuearray_free(&valuestodelete);
+ }
+ }
+ }
+ else if (attr_state==ATTRIBUTE_DELETED)
+ {
+ retVal= LDAP_NO_SUCH_ATTRIBUTE;
+ }
+ else if (attr_state==ATTRIBUTE_NOTFOUND)
+ {
+ if (!urp)
+ {
+ /* Only warn if not urping */
+ LDAPDebug( LDAP_DEBUG_ARGS, "could not find attribute %s\n", type, 0, 0 );
+ }
+ retVal= LDAP_NO_SUCH_ATTRIBUTE;
+ if (LDAP_MOD_REPLACE == mod_op)
+ {
+ /* Create a new attribute and set the adcsn */
+ Slapi_Attr *a = slapi_attr_new();
+ slapi_attr_init(a, type);
+ /* According to URP there should be an adcsn.
+ * According to Tests, there should not
+ * since the attribute never existed
+ * Tests 26 and 27 of MMRepl state. */
+ if (urp)
+ attr_set_deletion_csn(a,csn);
+ attrlist_add(&e->e_attrs, a);
+ }
+ }
+ return( retVal );
+}
+
+/*
+ * Replace all the values of an attribute with a list of attribute values.
+ *
+ * Preserves LDAP Information Model constraints,
+ * returning an LDAP result code.
+ */
+static int
+entry_replace_present_values_wsi(Slapi_Entry *e, const char *type, struct berval **vals, const CSN *csn, int urp)
+{
+ /*
+ * Remove all existing values.
+ */
+ entry_delete_present_values_wsi(e, type, NULL /* Delete all values */, csn, urp, LDAP_MOD_REPLACE);
+
+ /*
+ * Add the new values. If there are no new values,
+ * slapi_entry_add_values() returns LDAP_SUCCESS and so the
+ * attribute remains deleted (which is the correct outcome).
+ */
+ return( entry_add_present_values_wsi( e, type, vals, csn, urp, SLAPI_ATTR_FLAG_CMP_BITBYBIT ));
+}
+
+/*
+ * Applies the modification to the entry whilst
+ * maintaining state information.
+ */
+int
+entry_apply_mod_wsi(Slapi_Entry *e, const LDAPMod *mod, const CSN *csn, int urp)
+{
+ int retVal= LDAP_SUCCESS;
+ int i;
+
+ switch ( mod->mod_op & ~LDAP_MOD_BVALUES )
+ {
+ case LDAP_MOD_ADD:
+ LDAPDebug( LDAP_DEBUG_ARGS, " add: %s\n", mod->mod_type, 0, 0 );
+ retVal = entry_add_present_values_wsi( e, mod->mod_type, mod->mod_bvalues, csn, urp, 0 );
+ break;
+
+ case LDAP_MOD_DELETE:
+ LDAPDebug( LDAP_DEBUG_ARGS, " delete: %s\n", mod->mod_type, 0, 0 );
+ retVal = entry_delete_present_values_wsi( e, mod->mod_type, mod->mod_bvalues, csn, urp, mod->mod_op );
+ break;
+
+ case LDAP_MOD_REPLACE:
+ LDAPDebug( LDAP_DEBUG_ARGS, " replace: %s\n", mod->mod_type, 0, 0 );
+ retVal = entry_replace_present_values_wsi( e, mod->mod_type, mod->mod_bvalues, csn, urp );
+ break;
+ }
+ for ( i = 0; mod->mod_bvalues != NULL && mod->mod_bvalues[i] != NULL; i++ )
+ {
+ LDAPDebug( LDAP_DEBUG_ARGS, " %s: %s\n", mod->mod_type, mod->mod_bvalues[i]->bv_val, 0 );
+ }
+ LDAPDebug( LDAP_DEBUG_ARGS, " -\n", 0, 0, 0 );
+
+ return retVal;
+}
+
+/*
+ * Applies the set of modifications to the entry whilst
+ * maintaining state information.
+ */
+int
+entry_apply_mods_wsi(Slapi_Entry *e, Slapi_Mods *smods, const CSN *csn, int urp)
+{
+ int retVal= LDAP_SUCCESS;
+ LDAPMod *mod;
+
+ LDAPDebug( LDAP_DEBUG_TRACE, "=> entry_apply_mods_wsi\n", 0, 0, 0 );
+
+ mod = slapi_mods_get_first_mod(smods);
+ while(NULL!=mod && retVal==LDAP_SUCCESS)
+ {
+ if(csn!=NULL)
+ {
+ retVal= entry_apply_mod_wsi(e, mod, csn, urp);
+ }
+ else
+ {
+ retVal= entry_apply_mod(e, mod);
+ }
+ mod = slapi_mods_get_next_mod(smods);
+ }
+
+ LDAPDebug( LDAP_DEBUG_TRACE, "<= entry_apply_mods_wsi %d\n", retVal, 0, 0 );
+
+ return retVal;
+}
+
+/*
+ * This code implements a computed attribute called 'nscpEntryWSI'.
+ * By specifically asking for this attribute the client will recieve
+ * an LDIF dump of the entry with all its state information.
+ *
+ * JCM - Security... Only for the Directory Manager.
+ */
+static const char *nscpEntryWSI= "nscpEntryWSI";
+/*
+ */
+static int
+entry_compute_nscpentrywsi(computed_attr_context *c,char* type,Slapi_Entry *e,slapi_compute_output_t outputfn)
+{
+ int rc = 0;
+
+ if ( strcasecmp(type, nscpEntryWSI ) == 0)
+ {
+ /* If not, we return it as zero */
+ char *es;
+ char *s;
+ char *p;
+ int len;
+ Slapi_Attr our_attr;
+ slapi_attr_init(&our_attr, nscpEntryWSI);
+ our_attr.a_flags = SLAPI_ATTR_FLAG_OPATTR;
+ es= slapi_entry2str_with_options(e, &len, SLAPI_DUMP_STATEINFO | SLAPI_DUMP_UNIQUEID | SLAPI_DUMP_NOWRAP);
+ s= es;
+ p= ldif_getline( &s );
+ while(p!=NULL)
+ {
+ Slapi_Value *v;
+ char *t, *d;
+ /* Strip out the Continuation Markers (JCM - I think that NOWRAP means we don't need to do this any more)*/
+ for ( t = p, d = p; *t; t++ )
+ {
+ if ( *t != 0x01 )
+ *d++ = *t;
+ }
+ *d = '\0';
+ v= slapi_value_new_string(p);
+ slapi_attr_add_value(&our_attr,v);
+ slapi_value_free(&v);
+ p= ldif_getline( &s );
+ }
+ slapi_ch_free((void**)&es);
+ rc = (*outputfn) (c, &our_attr, e);
+ attr_done(&our_attr);
+ return (rc);
+ }
+
+ return -1; /* I see no ships */
+}
+
+
+int
+entry_computed_attr_init()
+{
+ slapi_compute_add_evaluator(entry_compute_nscpentrywsi);
+ return 0;
+}
+
+static void
+purge_attribute_state_multi_valued(const Slapi_Attr *a, Slapi_Value *v)
+{
+ const CSN *vdcsn= value_get_csn(v,CSN_TYPE_VALUE_DELETED);
+ const CSN *vucsn= value_get_csn(v,CSN_TYPE_VALUE_UPDATED);
+ if(vdcsn && csn_compare(vdcsn,vucsn)<0)
+ {
+ value_remove_csn(v,CSN_TYPE_VALUE_DELETED);
+ }
+}
+
+/*
+ * utility function for value_distinguished_at_csn...
+ */
+static const CSN *
+vdac_sniff_value(Slapi_ValueSet *vs, const Slapi_Value *v, const CSN *csn, const CSN *most_recent_mdcsn)
+{
+ const CSN *mdcsn= value_get_csn(v,CSN_TYPE_VALUE_DISTINGUISHED);
+ if(mdcsn!=NULL)
+ {
+ /* This value was/is distinguished... */
+ if(csn_compare(csn,most_recent_mdcsn)<0)
+ {
+ /* ...and was distinguished before the point in time we're interested in... */
+ int r= csn_compare(mdcsn,most_recent_mdcsn);
+ if(r>0)
+ {
+ /* ...and is the most recent MDCSN we've seen thus far. */
+ slapi_valueset_done(vs);
+ slapi_valueset_add_value(vs,v);
+ most_recent_mdcsn= mdcsn;
+ }
+ else if(r==0)
+ {
+ /* ...and is as recent as the last most recent MDCSN we've seen thus far. */
+ /* Must have been a multi-valued RDN */
+ slapi_valueset_add_value(vs,v);
+ }
+ }
+ }
+ return most_recent_mdcsn;
+}
+
+/*
+ * utility function for value_distinguished_at_csn...
+ */
+static const CSN *
+vdac_sniff_attribute(Slapi_ValueSet *vs, Slapi_Attr *a, const CSN *csn, const CSN *most_recent_mdcsn)
+{
+ Slapi_Value *v;
+ int i= slapi_attr_first_value( a, &v );
+ while(i!=-1)
+ {
+ most_recent_mdcsn= vdac_sniff_value( vs, v, csn, most_recent_mdcsn );
+ i= slapi_attr_next_value( a, i, &v );
+ }
+ i= attr_first_deleted_value( a, &v );
+ while(i!=-1)
+ {
+ most_recent_mdcsn= vdac_sniff_value( vs, v, csn, most_recent_mdcsn );
+ i= attr_next_deleted_value( a, i, &v );
+ }
+ return most_recent_mdcsn;
+}
+
+/*
+ * utility function for value_distinguished_at_csn...
+ *
+ * Return the set of values that made up the RDN at or before the csn point.
+ */
+static const CSN *
+distinguished_values_at_csn(const Slapi_Entry *e, const CSN *csn, Slapi_ValueSet *vs)
+{
+ const CSN *most_recent_mdcsn= NULL;
+ Slapi_Attr *a;
+ int i= slapi_entry_first_attr( e, &a );
+ while(i!=-1)
+ {
+ most_recent_mdcsn= vdac_sniff_attribute( vs, a, csn, most_recent_mdcsn);
+ i= slapi_entry_next_attr( e, a, &a );
+ }
+ i= entry_first_deleted_attribute( e, &a );
+ while(i!=-1)
+ {
+ most_recent_mdcsn= vdac_sniff_attribute( vs, a, csn, most_recent_mdcsn);
+ i= entry_next_deleted_attribute( e, &a );
+ }
+ return most_recent_mdcsn;
+}
+
+/*
+ * Work out if the value was distinguished at time csn.
+ */
+static int
+value_distinguished_at_csn(const Slapi_Entry *e, const Slapi_Attr *original_attr, Slapi_Value *original_value, const CSN *csn)
+{
+ int r= 0;
+ const CSN *mdcsn= value_get_csn(original_value,CSN_TYPE_VALUE_DISTINGUISHED);
+ if(mdcsn!=NULL)
+ {
+ /*
+ * Oh bugger. This means that we have to work out what the RDN components
+ * were at this point in time. This is non-trivial since we must walk
+ * through all the present and deleted attributes and their present and
+ * deleted values. Slow :-(
+ */
+ Slapi_ValueSet *vs= slapi_valueset_new();
+ const CSN *most_recent_mdcsn= distinguished_values_at_csn(e, csn, vs);
+ /*
+ * We now know what the RDN components were at the point in time we're interested in.
+ * And the question we need to answer is :-
+ * 'Was the provided value one of those RDN components?'
+ */
+ if(most_recent_mdcsn!=NULL)
+ {
+ Slapi_Value *v;
+ int i= slapi_valueset_first_value( vs, &v );
+ while(i!=-1)
+ {
+ if(slapi_value_compare(original_attr, original_value, v)==0)
+ {
+ /* This value was distinguished at the time in question. */
+ r= 1;
+ i= -1;
+ }
+ else
+ {
+ i= slapi_valueset_next_value( vs, i, &v );
+ }
+ }
+ }
+ slapi_valueset_free(vs);
+ }
+ else
+ {
+ /* This value has never been distinguished */
+ r= 0;
+ }
+ return r;
+}
+
+static void
+resolve_attribute_state_multi_valued(Slapi_Entry *e, Slapi_Attr *a, int attribute_state)
+{
+ int i;
+ const CSN *adcsn= attr_get_deletion_csn(a);
+ Slapi_ValueSet *vs= valueset_dup(&a->a_present_values); /* JCM This is slow... but otherwise we end up iterating through a changing array */
+ Slapi_Value *v;
+
+ /* Loop over the present attribute values */
+ i= slapi_valueset_first_value( vs, &v );
+ while(v!=NULL)
+ {
+ const CSN *vdcsn;
+ const CSN *vucsn;
+ const CSN *deletedcsn;
+ /* This call ensures that the value does not contain a deletion_csn
+ * which is before the presence_csn or distinguished_csn of the value.
+ */
+ purge_attribute_state_multi_valued(a, v);
+ vdcsn= value_get_csn(v, CSN_TYPE_VALUE_DELETED);
+ vucsn= value_get_csn(v, CSN_TYPE_VALUE_UPDATED);
+ deletedcsn= csn_max(vdcsn, adcsn);
+ if(csn_compare(vucsn,deletedcsn)<0) /* check if the attribute or value was deleted after the value was last updated */
+ {
+ if(!value_distinguished_at_csn(e, a, v, deletedcsn))
+ {
+ entry_present_value_to_deleted_value(a,v);
+ }
+ }
+ i= slapi_valueset_next_value( vs, i, &v );
+ }
+ slapi_valueset_free(vs);
+
+ /* Loop over the deleted attribute values */
+ vs= valueset_dup(&a->a_deleted_values); /* JCM This is slow... but otherwise we end up iterating through a changing array */
+ i= slapi_valueset_first_value( vs, &v );
+ while(v!=NULL)
+ {
+ const CSN *vdcsn;
+ const CSN *vucsn;
+ const CSN *deletedcsn;
+ /* This call ensures that the value does not contain a deletion_csn which is before the presence_csn or distinguished_csn of the value. */
+ purge_attribute_state_multi_valued(a, v);
+ vdcsn= value_get_csn(v, CSN_TYPE_VALUE_DELETED);
+ vucsn= value_get_csn(v, CSN_TYPE_VALUE_UPDATED);
+ deletedcsn= csn_max(vdcsn, adcsn);
+ if((csn_compare(vucsn,deletedcsn)>=0) || /* check if the attribute or value was deleted after the value was last updated */
+ value_distinguished_at_csn(e, a, v, deletedcsn))
+ {
+ entry_deleted_value_to_present_value(a,v);
+ }
+ i= slapi_valueset_next_value( vs, i, &v );
+ }
+ slapi_valueset_free(vs);
+
+ if(valueset_isempty(&a->a_present_values))
+ {
+ if(attribute_state==ATTRIBUTE_PRESENT)
+ {
+ entry_present_attribute_to_deleted_attribute(e, a);
+ }
+ }
+ else
+ {
+ if(attribute_state==ATTRIBUTE_DELETED)
+ {
+ entry_deleted_attribute_to_present_attribute(e, a);
+ }
+ }
+}
+
+static void
+resolve_attribute_state_single_valued(Slapi_Entry *e, Slapi_Attr *a, int attribute_state)
+{
+ Slapi_Value *current_value= NULL;
+ Slapi_Value *pending_value= NULL;
+ Slapi_Value *new_value= NULL;
+ const CSN *current_value_vucsn;
+ const CSN *pending_value_vucsn;
+ const CSN *adcsn;
+ int i;
+
+ /*
+ * this call makes sure that the attribute does not have a pending_value
+ * or deletion_csn which is before the current_value.
+ */
+ i= slapi_attr_first_value(a,&current_value);
+ if(i!=-1)
+ {
+ slapi_attr_next_value(a,i,&new_value);
+ }
+ attr_first_deleted_value(a,&pending_value);
+
+ /* purge_attribute_state_single_valued */
+ adcsn= attr_get_deletion_csn(a);
+ current_value_vucsn= value_get_csn(current_value, CSN_TYPE_VALUE_UPDATED);
+ pending_value_vucsn= value_get_csn(pending_value, CSN_TYPE_VALUE_UPDATED);
+ if((pending_value!=NULL && (csn_compare(adcsn, pending_value_vucsn)<0)) ||
+ (pending_value==NULL && (csn_compare(adcsn, current_value_vucsn)<0)))
+ {
+ attr_set_deletion_csn(a,NULL);
+ adcsn= NULL;
+ }
+
+ if(new_value==NULL)
+ {
+ /* check if the pending value should become the current value */
+ if(pending_value!=NULL)
+ {
+ if(!value_distinguished_at_csn(e,a,current_value,pending_value_vucsn))
+ {
+ /* attribute.current_value = attribute.pending_value; */
+ /* attribute.pending_value = NULL; */
+ entry_present_value_to_zapped_value(a,current_value);
+ entry_deleted_value_to_present_value(a,pending_value);
+ current_value= pending_value;
+ pending_value= NULL;
+ current_value_vucsn= pending_value_vucsn;
+ pending_value_vucsn= NULL;
+ }
+ }
+ /* check if the current value should be deleted */
+ if(current_value!=NULL)
+ {
+ if(csn_compare(adcsn,current_value_vucsn)>0) /* check if the attribute was deleted after the value was last updated */
+ {
+ if(!value_distinguished_at_csn(e,a,current_value,current_value_vucsn))
+ {
+ entry_present_value_to_zapped_value(a,current_value);
+ current_value= NULL;
+ current_value_vucsn= NULL;
+ }
+ }
+ }
+ }
+ else /* addition of a new value */
+ {
+ const CSN *new_value_vucsn= value_get_csn(new_value,CSN_TYPE_VALUE_UPDATED);
+ if(csn_compare(new_value_vucsn,current_value_vucsn)<0)
+ {
+ /*
+ * if the new value was distinguished at the time the current value was added
+ * then the new value should become current
+ */
+ if(value_distinguished_at_csn(e,a,new_value,current_value_vucsn))
+ {
+ /* attribute.pending_value = attribute.current_value */
+ /* attribute.current_value = new_value */
+ if(pending_value==NULL)
+ {
+ entry_present_value_to_deleted_value(a,current_value);
+ }
+ else
+ {
+ entry_present_value_to_zapped_value(a,current_value);
+ }
+ pending_value= current_value;
+ current_value= new_value;
+ new_value= NULL;
+ pending_value_vucsn= current_value_vucsn;
+ current_value_vucsn= new_value_vucsn;
+ }
+ else
+ {
+ /* new_value= NULL */
+ entry_present_value_to_zapped_value(a, new_value);
+ new_value= NULL;
+ }
+ }
+ else /* new value is after the current value */
+ {
+ if(!value_distinguished_at_csn(e, a, current_value, new_value_vucsn))
+ {
+ /* attribute.current_value = new_value */
+ entry_present_value_to_zapped_value(a, current_value);
+ current_value= new_value;
+ new_value= NULL;
+ current_value_vucsn= new_value_vucsn;
+ }
+ else /* value is distinguished - check if we should replace the current pending value */
+ {
+ if(csn_compare(new_value_vucsn, pending_value_vucsn)>0)
+ {
+ /* attribute.pending_value = new_value */
+ entry_deleted_value_to_zapped_value(a,pending_value);
+ entry_present_value_to_deleted_value(a,new_value);
+ pending_value= new_value;
+ new_value= NULL;
+ pending_value_vucsn= new_value_vucsn;
+ }
+ }
+ }
+ }
+
+ /*
+ * This call ensures that the attribute does not have a pending_value
+ * or a deletion_csn that is earlier than the current_value.
+ */
+ /* purge_attribute_state_single_valued */
+ if((pending_value!=NULL && (csn_compare(adcsn, pending_value_vucsn)<0)) ||
+ (pending_value==NULL && (csn_compare(adcsn, current_value_vucsn)<0)))
+ {
+ attr_set_deletion_csn(a,NULL);
+ adcsn= NULL;
+ }
+
+ /* set attribute state */
+ if(current_value==NULL)
+ {
+ if(attribute_state==ATTRIBUTE_PRESENT)
+ {
+ entry_present_attribute_to_deleted_attribute(e, a);
+ }
+ }
+ else
+ {
+ if(attribute_state==ATTRIBUTE_DELETED)
+ {
+ entry_deleted_attribute_to_present_attribute(e, a);
+ }
+ }
+}
+
+static void
+resolve_attribute_state(Slapi_Entry *e, Slapi_Attr *a, int attribute_state)
+{
+ if(slapi_attr_flag_is_set(a,SLAPI_ATTR_FLAG_SINGLE))
+ {
+ resolve_attribute_state_single_valued(e,a,attribute_state);
+ }
+ else
+ {
+ resolve_attribute_state_multi_valued(e,a,attribute_state);
+ }
+}
diff --git a/ldap/servers/slapd/errormap.c b/ldap/servers/slapd/errormap.c
new file mode 100644
index 00000000..8e7f2151
--- /dev/null
+++ b/ldap/servers/slapd/errormap.c
@@ -0,0 +1,186 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+/*
+ * errormap.c - map NSPR and OS errors to strings
+ *
+ */
+
+#include "slap.h"
+
+#ifndef SYSERRLIST_IN_STDIO
+extern int sys_nerr;
+extern char *sys_errlist[];
+#endif
+
+/*
+ * function protoypes
+ */
+static const char *SECU_Strerror(PRErrorCode errNum);
+
+
+/*
+ * return the string equivalent of an NSPR error
+ */
+char *
+slapd_pr_strerror( const int prerrno )
+{
+ char *s;
+
+ if (( s = (char *)SECU_Strerror( (PRErrorCode)prerrno )) == NULL ) {
+ s = "unknown";
+ }
+
+ return( s );
+}
+
+
+/*
+ * return the string equivalent of a system error
+ */
+const char *
+slapd_system_strerror( const int syserrno )
+{
+ const char *s;
+ /* replaced
+ if ( syserrno > -1 && syserrno < sys_nerr ) {
+ s = sys_errlist[ syserrno ];
+ } else {
+ s = "unknown";
+ }
+ with s= strerror(syserrno)*/
+ s=strerror(syserrno);
+ return( s );
+}
+
+
+/*
+ * return the string equivalent of an NSPR error. If "prerrno" is not
+ * an NSPR error, assume it is a system error. Please use slapd_pr_strerror()
+ * or slapd_system_strerror() if you can since the concept behind this
+ * function is a bit of a kludge -- one should *really* know what kind of
+ * error code they have.
+ */
+const char *
+slapd_versatile_strerror( const PRErrorCode prerrno )
+{
+ const char *s;
+
+ if (( s = (const char *)SECU_Strerror( prerrno )) == NULL ) {
+ s = slapd_system_strerror( (const int)prerrno );
+ }
+
+ return( s );
+}
+
+
+/*
+ ****************************************************************************
+ * The code below this point was provided by Nelson Bolyard <nelsonb> of the
+ * Netscape Certificate Server team on 27-March-1998.
+ * Taken from the file ns/security/cmd/lib/secerror.c on NSS_1_BRANCH.
+ * Last updated from there: 24-July-1998 by Mark Smith <mcs>
+ *
+ * All of the Directory Server specific changes are enclosed inside
+ * #ifdef NS_DS.
+ ****************************************************************************
+ */
+#include "nspr.h"
+
+struct tuple_str {
+ PRErrorCode errNum;
+ const char * errString;
+};
+
+typedef struct tuple_str tuple_str;
+
+#define ER2(a,b) {a, b},
+#define ER3(a,b,c) {a, c},
+
+#include "secerr.h"
+#include "sslerr.h"
+
+static const tuple_str errStrings[] = {
+
+/* keep this list in ascending order of error numbers */
+#ifdef NS_DS
+#include "dberrstrs.h"
+#include "sslerrstrs.h"
+#include "secerrstrs.h"
+#include "prerrstrs.h"
+#include "disconnect_error_strings.h"
+#else /* NS_DS */
+#include "SSLerrs.h"
+#include "SECerrs.h"
+#include "NSPRerrs.h"
+#endif /* NS_DS */
+
+};
+
+static const PRInt32 numStrings = sizeof(errStrings) / sizeof(tuple_str);
+
+/* Returns a UTF-8 encoded constant error string for "errNum".
+ * Returns NULL of errNum is unknown.
+ */
+#ifdef NS_DS
+static
+#endif /* NS_DS */
+const char *
+SECU_Strerror(PRErrorCode errNum) {
+ PRInt32 low = 0;
+ PRInt32 high = numStrings - 1;
+ PRInt32 i;
+ PRErrorCode num;
+ static int initDone;
+
+ /* make sure table is in ascending order.
+ * binary search depends on it.
+ */
+ if (!initDone) {
+ PRErrorCode lastNum = errStrings[low].errNum;
+ for (i = low + 1; i <= high; ++i) {
+ num = errStrings[i].errNum;
+ if (num <= lastNum) {
+#ifdef NS_DS
+ LDAPDebug( LDAP_DEBUG_ANY,
+ "sequence error in error strings at item %d\n"
+ "error %d (%s)\n",
+ i, lastNum, errStrings[i-1].errString );
+ LDAPDebug( LDAP_DEBUG_ANY,
+ "should come after \n"
+ "error %d (%s)\n",
+ num, errStrings[i].errString, 0 );
+#else /* NS_DS */
+ fprintf(stderr,
+"sequence error in error strings at item %d\n"
+"error %d (%s)\n"
+"should come after \n"
+"error %d (%s)\n",
+ i, lastNum, errStrings[i-1].errString,
+ num, errStrings[i].errString);
+#endif /* NS_DS */
+ }
+ lastNum = num;
+ }
+ initDone = 1;
+ }
+
+ /* Do binary search of table. */
+ while (low + 1 < high) {
+ i = (low + high) / 2;
+ num = errStrings[i].errNum;
+ if (errNum == num)
+ return errStrings[i].errString;
+ if (errNum < num)
+ high = i;
+ else
+ low = i;
+ }
+ if (errNum == errStrings[low].errNum)
+ return errStrings[low].errString;
+ if (errNum == errStrings[high].errNum)
+ return errStrings[high].errString;
+ return NULL;
+}
diff --git a/ldap/servers/slapd/eventq.c b/ldap/servers/slapd/eventq.c
new file mode 100644
index 00000000..3acd027f
--- /dev/null
+++ b/ldap/servers/slapd/eventq.c
@@ -0,0 +1,482 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+
+/* ********************************************************
+eventq.c - Event queue/scheduling system.
+
+There are 3 publicly-accessible entry points:
+
+slapi_eq_once(): cause an event to happen exactly once
+slapi_eq_repeat(): cause an event to happen repeatedly
+slapi_eq_cancel(): cancel a pending event
+
+There is also an initialization point which must be
+called by the server to initialize the event queue system:
+eq_start(), and an entry point used to shut down the system:
+eq_stop().
+*********************************************************** */
+
+#include "slap.h"
+#include "prlock.h"
+#include "prcvar.h"
+#include "prinit.h"
+
+/*
+ * Private definition of slapi_eq_context. Only this
+ * module (eventq.c) should know about the layout of
+ * this structure.
+ */
+typedef struct _slapi_eq_context {
+ time_t ec_when;
+ time_t ec_interval;
+ slapi_eq_fn_t ec_fn;
+ void *ec_arg;
+ Slapi_Eq_Context ec_id;
+ struct _slapi_eq_context *ec_next;
+} slapi_eq_context;
+
+/*
+ * Definition of the event queue.
+ */
+typedef struct _event_queue {
+ PRLock *eq_lock;
+ PRCondVar *eq_cv;
+ slapi_eq_context *eq_queue;
+} event_queue;
+
+/*
+ * The event queue itself.
+ */
+static event_queue eqs = {0};
+static event_queue *eq = &eqs;
+
+/*
+ * Thread ID of the main thread loop
+ */
+static PRThread *eq_loop_tid = NULL;
+
+/*
+ * Flags used to control startup/shutdown of the event queue
+ */
+static int eq_running = 0;
+static int eq_stopped = 0;
+static int eq_initialized = 0;
+PRLock *ss_lock = NULL;
+PRCondVar *ss_cv = NULL;
+PRCallOnceType init_once = {0};
+
+/* Forward declarations */
+static slapi_eq_context *eq_new(slapi_eq_fn_t fn, void *arg,
+ time_t when, unsigned long interval);
+static void eq_enqueue(slapi_eq_context *newec);
+static slapi_eq_context *eq_dequeue(time_t now);
+static PRStatus eq_create(void);
+
+
+/* ******************************************************** */
+
+
+
+/*
+ * slapi_eq_once: cause an event to happen exactly once.
+ *
+ * Arguments:
+ * fn: the function to call
+ * arg: an argument to pass to the called function
+ * when: the time that the function should be called
+ * Returns:
+ * slapi_eq_context - a handle to an opaque object which
+ * the caller can use to refer to this particular scheduled
+ * event.
+ */
+Slapi_Eq_Context
+slapi_eq_once(slapi_eq_fn_t fn, void *arg, time_t when)
+{
+ slapi_eq_context *tmp;
+ PR_ASSERT(eq_initialized);
+ if (!eq_stopped) {
+
+ Slapi_Eq_Context id;
+
+ tmp = eq_new(fn, arg, when, 0UL);
+ id = tmp->ec_id;
+
+ eq_enqueue(tmp);
+
+ /* After this point, <tmp> may have */
+ /* been freed, depending on the thread */
+ /* scheduling. Too bad */
+
+ slapi_log_error(SLAPI_LOG_HOUSE, NULL,
+ "added one-time event id 0x%x at time %u\n",
+ id, when);
+ return(id);
+ }
+ return NULL; /* JCM - Not sure if this should be 0 or something else. */
+}
+
+
+
+
+/*
+ * slapi_eq_repeat: cause an event to happen repeatedly.
+ *
+ * Arguments:
+ * fn: the function to call
+ * arg: an argument to pass to the called function
+ * when: the time that the function should first be called
+ * interval: the amount of time (in milliseconds) between
+ * successive calls to the function
+ * Returns:
+ * slapi_eq_context - a handle to an opaque object which
+ * the caller can use to refer to this particular scheduled
+ */
+Slapi_Eq_Context
+slapi_eq_repeat(slapi_eq_fn_t fn, void *arg, time_t when, unsigned long interval)
+{
+ slapi_eq_context *tmp ;
+ PR_ASSERT(eq_initialized);
+ if (!eq_stopped) {
+ tmp = eq_new(fn, arg, when, interval);
+ eq_enqueue(tmp);
+ slapi_log_error(SLAPI_LOG_HOUSE, NULL,
+ "added repeating event id 0x%x at time %u, interval %u\n",
+ tmp->ec_id, when, interval);
+ return(tmp->ec_id);
+ }
+ return NULL; /* JCM - Not sure if this should be 0 or something else. */
+}
+
+
+
+/*
+ * slapi_eq_cancel: cancel a pending event.
+ * Arguments:
+ * ctx: the context of the event which should be de-scheduled
+ */
+int
+slapi_eq_cancel(Slapi_Eq_Context ctx)
+{
+ slapi_eq_context **p, *tmp = NULL;
+ int found = 0;
+
+ PR_ASSERT(eq_initialized);
+ if (!eq_stopped) {
+ PR_Lock(eq->eq_lock);
+ p = &(eq->eq_queue);
+ while (!found && *p != NULL) {
+ if ((*p)->ec_id == ctx) {
+ tmp = *p;
+ *p = (*p)->ec_next;
+ slapi_ch_free((void**)&tmp);
+ found = 1;
+ } else {
+ p = &((*p)->ec_next);
+ }
+ }
+ PR_Unlock(eq->eq_lock);
+ }
+ slapi_log_error(SLAPI_LOG_HOUSE, NULL,
+ "cancellation of event id 0x%x requested: %s\n",
+ ctx, found ? "cancellation succeeded" : "event not found");
+ return found;
+}
+
+
+
+
+/*
+ * Construct a new ec structure
+ */
+static slapi_eq_context *
+eq_new(slapi_eq_fn_t fn, void *arg, time_t when, unsigned long interval)
+{
+ slapi_eq_context *retptr = (slapi_eq_context *)slapi_ch_calloc(1, sizeof(slapi_eq_context));
+ time_t now;
+
+ retptr->ec_fn = fn;
+ retptr->ec_arg = arg;
+ now = current_time();
+ retptr->ec_when = when < now ? now : when;
+ retptr->ec_interval = interval == 0UL ? 0UL : (interval + 999) / 1000;
+ retptr->ec_id = (Slapi_Eq_Context)retptr;
+ return retptr;
+}
+
+
+
+
+/*
+ * Add a new event to the event queue.
+ */
+static void
+eq_enqueue(slapi_eq_context *newec)
+{
+ slapi_eq_context **p;
+
+ PR_ASSERT(NULL != newec);
+ PR_Lock(eq->eq_lock);
+ /* Insert <newec> in order (sorted by start time) in the list */
+ for (p = &(eq->eq_queue); *p != NULL; p = &((*p)->ec_next)) {
+ if ((*p)->ec_when > newec->ec_when) {
+ break;
+ }
+ }
+ if (NULL != *p) {
+ newec->ec_next = *p;
+ } else {
+ newec->ec_next = NULL;
+ }
+ *p = newec;
+ PR_NotifyCondVar(eq->eq_cv); /* wake up scheduler thread */
+ PR_Unlock(eq->eq_lock);
+}
+
+
+
+
+/*
+ * If there is an event in the queue scheduled at time
+ * <now> or before, dequeue it and return a pointer
+ * to it. Otherwise, return NULL.
+ */
+static slapi_eq_context *
+eq_dequeue(time_t now)
+{
+ slapi_eq_context *retptr = NULL;
+
+ PR_Lock(eq->eq_lock);
+ if (NULL != eq->eq_queue && eq->eq_queue->ec_when <= now) {
+ retptr = eq->eq_queue;
+ eq->eq_queue = retptr->ec_next;
+ }
+ PR_Unlock(eq->eq_lock);
+ return retptr;
+}
+
+
+
+/*
+ * Call all events which are due to run.
+ * Note that if we've missed a schedule
+ * opportunity, we don't try to catch up
+ * by calling the function repeatedly.
+ */
+static void
+eq_call_all()
+{
+ slapi_eq_context *p;
+
+ while ((p = eq_dequeue(current_time())) != NULL) {
+ /* Call the scheduled function */
+ p->ec_fn(p->ec_when, p->ec_arg);
+ slapi_log_error(SLAPI_LOG_HOUSE, NULL,
+ "Event id 0x%x called at %u (scheduled for %u)\n",
+ p->ec_id, current_time(), p->ec_when);
+ if (0UL != p->ec_interval) {
+ /* This is a repeating event. Requeue it. */
+ do {
+ p->ec_when += p->ec_interval;
+ } while (p->ec_when < current_time());
+ eq_enqueue(p);
+ } else {
+ slapi_ch_free((void **)&p);
+ }
+ }
+}
+
+
+
+
+/*
+ * The main event queue loop.
+ */
+#define WORK_AVAILABLE ((NULL != eq->eq_queue) && (eq->eq_queue->ec_when <= current_time()))
+
+static void
+eq_loop(void *arg)
+{
+ while (eq_running) {
+ PRIntervalTime timeout;
+ int until;
+ PR_Lock(eq->eq_lock);
+ while (!WORK_AVAILABLE) {
+ if (!eq_running) {
+ PR_Unlock(eq->eq_lock);
+ goto bye;
+ }
+ /* Compute new timeout */
+ if (NULL != eq->eq_queue) {
+ until = eq->eq_queue->ec_when - current_time();
+ timeout = PR_SecondsToInterval(until);
+ } else {
+ timeout = PR_INTERVAL_NO_TIMEOUT;
+ }
+ PR_WaitCondVar(eq->eq_cv, timeout);
+ }
+ /* There is some work to do */
+ PR_Unlock(eq->eq_lock);
+ eq_call_all();
+ }
+bye:
+ eq_stopped = 1;
+ PR_Lock(ss_lock);
+ PR_NotifyAllCondVar(ss_cv);
+ PR_Unlock(ss_lock);
+}
+
+
+
+/*
+ * Allocate and initialize the event queue structures.
+ */
+static PRStatus
+eq_create(void)
+{
+ PR_ASSERT(NULL == eq->eq_lock);
+ if ((eq->eq_lock = PR_NewLock()) == NULL) {
+ slapi_log_error(SLAPI_LOG_FATAL, NULL, "eq_start PR_NewLock failed\n");
+ exit(1);
+ }
+ if ((eq->eq_cv = PR_NewCondVar(eq->eq_lock)) == NULL) {
+ slapi_log_error(SLAPI_LOG_FATAL, NULL, "eq_start PR_NewCondVar failed\n");
+ exit(1);
+ }
+ if ((ss_lock = PR_NewLock()) == NULL) {
+ slapi_log_error(SLAPI_LOG_FATAL, NULL, "eq_start PR_NewLock failed\n");
+ exit(1);
+ }
+ if ((ss_cv = PR_NewCondVar(ss_lock)) == NULL) {
+ slapi_log_error(SLAPI_LOG_FATAL, NULL, "eq_start PR_NewCondVar failed\n");
+ exit(1);
+ }
+ eq->eq_queue = NULL;
+ eq_initialized = 1;
+ return PR_SUCCESS;
+}
+
+
+
+
+
+/*
+ * eq_start: start the event queue system.
+ *
+ * This should be called exactly once. It will start a
+ * thread which wakes up periodically and schedules events.
+ */
+void
+eq_start()
+{
+ PR_ASSERT(eq_initialized);
+ eq_running = 1;
+ if ((eq_loop_tid = PR_CreateThread(PR_USER_THREAD, (VFP)eq_loop,
+ NULL, PR_PRIORITY_NORMAL, PR_GLOBAL_THREAD, PR_JOINABLE_THREAD,
+ SLAPD_DEFAULT_THREAD_STACKSIZE)) == NULL) {
+ slapi_log_error(SLAPI_LOG_FATAL, NULL, "eq_init PR_CreateThread failed\n");
+ exit(1);
+ }
+ slapi_log_error(SLAPI_LOG_HOUSE, NULL, "event queue services have started\n");
+}
+
+
+
+/*
+ * eq_init: initialize the event queue system.
+ *
+ * This function should be called early in server startup.
+ * Once it has been called, the event queue will queue
+ * events, but will not fire any events. Once all of the
+ * server plugins have been started, the eq_start()
+ * function should be called, and events will then start
+ * to fire.
+ */
+void
+eq_init()
+{
+ if (!eq_initialized) {
+ PR_CallOnce(&init_once, eq_create);
+ }
+}
+
+
+
+/*
+ * eq_stop: shut down the event queue system.
+ * Does not return until event queue is fully
+ * shut down.
+ */
+void
+eq_stop()
+{
+ slapi_eq_context *p, *q;
+
+ if ( NULL == eq || NULL == eq->eq_lock ) { /* never started */
+ eq_stopped = 1;
+ return;
+ }
+
+ eq_stopped = 0;
+ eq_running = 0;
+ /*
+ * Signal the eq thread function to stop, and wait until
+ * it acknowledges by setting eq_stopped.
+ */
+ while (!eq_stopped) {
+ PR_Lock(eq->eq_lock);
+ PR_NotifyAllCondVar(eq->eq_cv);
+ PR_Unlock(eq->eq_lock);
+ PR_Lock(ss_lock);
+ PR_WaitCondVar(ss_cv, PR_MillisecondsToInterval(100));
+ PR_Unlock(ss_lock);
+ }
+ (void)PR_JoinThread(eq_loop_tid);
+ /*
+ * XXXggood we don't free the actual event queue data structures.
+ * This is intentional, to allow enqueueing/cancellation of events
+ * even after event queue services have shut down (these are no-ops).
+ * The downside is that the event queue can't be stopped and restarted
+ * easily.
+ */
+ PR_Lock(eq->eq_lock);
+ p = eq->eq_queue;
+ while (p != NULL) {
+ q = p->ec_next;
+ slapi_ch_free((void**)&p);
+ /* Some ec_arg could get leaked here in shutdown (e.g., replica_name)
+ * This can be fixed by specifying a flag when the context is queued.
+ * [After 6.2]
+ */
+ p = q;
+ }
+ PR_Unlock(eq->eq_lock);
+ slapi_log_error(SLAPI_LOG_HOUSE, NULL, "event queue services have shut down\n");
+}
+
+/*
+ * return arg (ec_arg) only if the context is in the event queue
+ */
+void *
+slapi_eq_get_arg ( Slapi_Eq_Context ctx )
+{
+ slapi_eq_context **p;
+
+ PR_ASSERT(eq_initialized);
+ if (!eq_stopped) {
+ PR_Lock(eq->eq_lock);
+ p = &(eq->eq_queue);
+ while (p && *p != NULL) {
+ if ((*p)->ec_id == ctx) {
+ PR_Unlock(eq->eq_lock);
+ return (*p)->ec_arg;
+ } else {
+ p = &((*p)->ec_next);
+ }
+ }
+ PR_Unlock(eq->eq_lock);
+ }
+ return NULL;
+}
diff --git a/ldap/servers/slapd/extendop.c b/ldap/servers/slapd/extendop.c
new file mode 100644
index 00000000..8044f2f3
--- /dev/null
+++ b/ldap/servers/slapd/extendop.c
@@ -0,0 +1,297 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+/* extendedop.c - handle an LDAPv3 extended operation */
+
+#include <stdio.h>
+#include "slap.h"
+
+static const char *extended_op_oid2string( const char *oid );
+
+
+/********** this stuff should probably be moved when it's done **********/
+
+static void extop_handle_import_start(Slapi_PBlock *pb, char *extoid,
+ struct berval *extval)
+{
+ char *suffix;
+ Slapi_DN *sdn = NULL;
+ Slapi_Backend *be = NULL;
+ struct berval bv;
+ int ret;
+
+ if (extval == NULL || extval->bv_val == NULL) {
+ LDAPDebug(LDAP_DEBUG_ANY,
+ "extop_handle_import_start: no data supplied\n", 0, 0, 0);
+ send_ldap_result(pb, LDAP_OPERATIONS_ERROR, NULL,
+ "no data supplied", 0, NULL);
+ return;
+ }
+ suffix = slapi_ch_malloc(extval->bv_len+1);
+ strncpy(suffix, extval->bv_val, extval->bv_len);
+ suffix[extval->bv_len] = 0;
+
+ sdn = slapi_sdn_new_dn_byval(suffix);
+ if (!sdn) {
+ LDAPDebug(LDAP_DEBUG_ANY,
+ "extop_handle_import_start: out of memory\n", 0, 0, 0);
+ send_ldap_result(pb, LDAP_OPERATIONS_ERROR, NULL, NULL, 0, NULL);
+ return;
+ }
+ /* be = slapi_be_select(sdn); */
+ be = slapi_mapping_tree_find_backend_for_sdn(sdn);
+ slapi_sdn_free(&sdn);
+ if (be == NULL || be == defbackend_get_backend()) {
+ /* might be instance name instead of suffix */
+ be = slapi_be_select_by_instance_name(suffix);
+ }
+ if (be == NULL || be == defbackend_get_backend()) {
+ LDAPDebug(LDAP_DEBUG_ANY,
+ "bulk import: invalid suffix or instance name '%s'\n",
+ suffix, 0, 0);
+ send_ldap_result(pb, LDAP_NO_SUCH_OBJECT, NULL,
+ "invalid suffix or instance name", 0, NULL);
+ goto out;
+ }
+
+ slapi_pblock_set(pb, SLAPI_BACKEND, be);
+ slapi_pblock_set( pb, SLAPI_REQUESTOR_ISROOT, &pb->pb_op->o_isroot );
+
+ {
+ /* Access Control Check to see if the client is
+ * allowed to use task import
+ */
+ char *dummyAttr = "dummy#attr";
+ char *dummyAttrs[2] = { NULL, NULL };
+ int rc = 0;
+ char dn[128];
+ Slapi_Entry *feature;
+
+ /* slapi_str2entry modify its dn parameter so we must copy
+ * this string each time we call it !
+ */
+ sprintf(dn, "dn: oid=%s,cn=features,cn=config",
+ EXTOP_BULK_IMPORT_START_OID);
+
+ dummyAttrs[0] = dummyAttr;
+ feature = slapi_str2entry(dn, 0);
+ rc = plugin_call_acl_plugin (pb, feature, dummyAttrs, NULL,
+ SLAPI_ACL_WRITE, ACLPLUGIN_ACCESS_DEFAULT, NULL);
+ slapi_entry_free(feature);
+ if (rc != LDAP_SUCCESS)
+ {
+ /* Client isn't allowed to do this. */
+ send_ldap_result(pb, rc, NULL, NULL, 0, NULL);
+ goto out;
+ }
+ }
+
+ if (be->be_wire_import == NULL) {
+ /* not supported by this backend */
+ LDAPDebug(LDAP_DEBUG_ANY,
+ "bulk import attempted on '%s' (not supported)\n",
+ suffix, 0, 0);
+ send_ldap_result(pb, LDAP_NOT_SUPPORTED, NULL, NULL, 0, NULL);
+ goto out;
+ }
+
+ ret = SLAPI_UNIQUEID_GENERATE_TIME_BASED;
+ slapi_pblock_set(pb, SLAPI_LDIF2DB_GENERATE_UNIQUEID, &ret);
+ ret = SLAPI_BI_STATE_START;
+ slapi_pblock_set(pb, SLAPI_BULK_IMPORT_STATE, &ret);
+ ret = (*be->be_wire_import)(pb);
+ if (ret != 0) {
+ LDAPDebug(LDAP_DEBUG_ANY,
+ "extop_handle_import_start: error starting import (%d)\n",
+ ret, 0, 0);
+ send_ldap_result(pb, LDAP_OPERATIONS_ERROR, NULL, NULL, 0, NULL);
+ goto out;
+ }
+
+ /* okay, the import is starting now -- save the backend in the
+ * connection block & mark this connection as belonging to a bulk import
+ */
+ PR_Lock(pb->pb_conn->c_mutex);
+ pb->pb_conn->c_flags |= CONN_FLAG_IMPORT;
+ pb->pb_conn->c_bi_backend = be;
+ PR_Unlock(pb->pb_conn->c_mutex);
+
+ slapi_pblock_set(pb, SLAPI_EXT_OP_RET_OID, EXTOP_BULK_IMPORT_START_OID);
+ bv.bv_val = NULL;
+ bv.bv_len = 0;
+ slapi_pblock_set(pb, SLAPI_EXT_OP_RET_VALUE, &bv);
+ send_ldap_result(pb, LDAP_SUCCESS, NULL, NULL, 0, NULL);
+ LDAPDebug(LDAP_DEBUG_ANY,
+ "Bulk import: begin import on '%s'.\n", suffix, 0, 0);
+
+out:
+ slapi_ch_free((void **)&suffix);
+ return;
+}
+
+static void extop_handle_import_done(Slapi_PBlock *pb, char *extoid,
+ struct berval *extval)
+{
+ Slapi_Backend *be;
+ struct berval bv;
+ int ret;
+
+ PR_Lock(pb->pb_conn->c_mutex);
+ pb->pb_conn->c_flags &= ~CONN_FLAG_IMPORT;
+ be = pb->pb_conn->c_bi_backend;
+ pb->pb_conn->c_bi_backend = NULL;
+ PR_Unlock(pb->pb_conn->c_mutex);
+
+ if ((be == NULL) || (be->be_wire_import == NULL)) {
+ /* can this even happen? */
+ LDAPDebug(LDAP_DEBUG_ANY,
+ "extop_handle_import_done: backend not supported\n",
+ 0, 0, 0);
+ send_ldap_result(pb, LDAP_NOT_SUPPORTED, NULL, NULL, 0, NULL);
+ return;
+ }
+
+ /* signal "done" to the backend */
+ slapi_pblock_set(pb, SLAPI_BACKEND, be);
+ slapi_pblock_set(pb, SLAPI_BULK_IMPORT_ENTRY, NULL);
+ ret = SLAPI_BI_STATE_DONE;
+ slapi_pblock_set(pb, SLAPI_BULK_IMPORT_STATE, &ret);
+ ret = (*be->be_wire_import)(pb);
+ if (ret != 0) {
+ LDAPDebug(LDAP_DEBUG_ANY,
+ "bulk import: error ending import (%d)\n",
+ ret, 0, 0);
+ send_ldap_result(pb, LDAP_OPERATIONS_ERROR, NULL, NULL, 0, NULL);
+ return;
+ }
+
+ /* more goofiness */
+ slapi_pblock_set(pb, SLAPI_EXT_OP_RET_OID, EXTOP_BULK_IMPORT_DONE_OID);
+ bv.bv_val = NULL;
+ bv.bv_len = 0;
+ slapi_pblock_set(pb, SLAPI_EXT_OP_RET_VALUE, &bv);
+ send_ldap_result(pb, LDAP_SUCCESS, NULL, NULL, 0, NULL);
+ LDAPDebug(LDAP_DEBUG_ANY,
+ "Bulk import completed successfully.\n", 0, 0, 0);
+ return;
+}
+
+
+void
+do_extended( Slapi_PBlock *pb )
+{
+ char *extoid = NULL, *errmsg;
+ struct berval extval = {0};
+ int lderr, rc;
+ unsigned long len, tag;
+ const char *name;
+
+ LDAPDebug( LDAP_DEBUG_TRACE, "do_extended\n", 0, 0, 0 );
+
+ /*
+ * Parse the extended request. It looks like this:
+ *
+ * ExtendedRequest := [APPLICATION 23] SEQUENCE {
+ * requestName [0] LDAPOID,
+ * requestValue [1] OCTET STRING OPTIONAL
+ * }
+ */
+
+ if ( ber_scanf( pb->pb_op->o_ber, "{a", &extoid )
+ == LBER_ERROR ) {
+ LDAPDebug( LDAP_DEBUG_ANY,
+ "ber_scanf failed (op=extended; params=OID)\n",
+ 0, 0, 0 );
+ op_shared_log_error_access (pb, "EXT", "???", "decoding error: fail to get extension OID");
+ send_ldap_result( pb, LDAP_PROTOCOL_ERROR, NULL, "decoding error", 0,
+ NULL );
+ goto free_and_return;
+ }
+ tag = ber_peek_tag(pb->pb_op->o_ber, &len);
+
+ if (tag == LDAP_TAG_EXOP_REQ_VALUE) {
+ if ( ber_scanf( pb->pb_op->o_ber, "o}", &extval ) == LBER_ERROR ) {
+ op_shared_log_error_access (pb, "EXT", "???", "decoding error: fail to get extension value");
+ send_ldap_result( pb, LDAP_PROTOCOL_ERROR, NULL, "decoding error", 0,
+ NULL );
+ goto free_and_return;
+ }
+ } else {
+ if ( ber_scanf( pb->pb_op->o_ber, "}") == LBER_ERROR ) {
+ op_shared_log_error_access (pb, "EXT", "???", "decoding error");
+ send_ldap_result( pb, LDAP_PROTOCOL_ERROR, NULL, "decoding error", 0,
+ NULL );
+ goto free_and_return;
+ }
+ }
+ if ( NULL == ( name = extended_op_oid2string( extoid ))) {
+ LDAPDebug( LDAP_DEBUG_ARGS, "do_extended: oid (%s)\n", extoid, 0, 0 );
+
+ slapi_log_access( LDAP_DEBUG_STATS, "conn=%d op=%d EXT oid=\"%s\"\n",
+ pb->pb_conn->c_connid, pb->pb_op->o_opid, extoid );
+ } else {
+ LDAPDebug( LDAP_DEBUG_ARGS, "do_extended: oid (%s-%s)\n",
+ extoid, name, 0 );
+
+ slapi_log_access( LDAP_DEBUG_STATS,
+ "conn=%d op=%d EXT oid=\"%s\" name=\"%s\"\n",
+ pb->pb_conn->c_connid, pb->pb_op->o_opid, extoid, name );
+ }
+
+ /* during a bulk import, only BULK_IMPORT_DONE is allowed!
+ * (and this is the only time it's allowed)
+ */
+ if (pb->pb_conn->c_flags & CONN_FLAG_IMPORT) {
+ if (strcmp(extoid, EXTOP_BULK_IMPORT_DONE_OID) != 0) {
+ send_ldap_result(pb, LDAP_PROTOCOL_ERROR, NULL, NULL, 0, NULL);
+ goto free_and_return;
+ }
+ extop_handle_import_done(pb, extoid, &extval);
+ goto free_and_return;
+ }
+
+ if (strcmp(extoid, EXTOP_BULK_IMPORT_START_OID) == 0) {
+ extop_handle_import_start(pb, extoid, &extval);
+ goto free_and_return;
+ }
+
+ slapi_pblock_set( pb, SLAPI_EXT_OP_REQ_OID, extoid );
+ slapi_pblock_set( pb, SLAPI_EXT_OP_REQ_VALUE, &extval );
+ rc = plugin_call_exop_plugins( pb, extoid );
+
+ if ( SLAPI_PLUGIN_EXTENDED_SENT_RESULT != rc ) {
+ if ( SLAPI_PLUGIN_EXTENDED_NOT_HANDLED == rc ) {
+ lderr = LDAP_PROTOCOL_ERROR; /* no plugin handled the op */
+ errmsg = "unsupported extended operation";
+ } else {
+ errmsg = NULL;
+ lderr = rc;
+ }
+ send_ldap_result( pb, lderr, NULL, errmsg, 0, NULL );
+ }
+free_and_return:
+ if (extoid)
+ slapi_ch_free((void **)&extoid);
+ if (extval.bv_val)
+ slapi_ch_free((void **)&extval.bv_val);
+ return;
+}
+
+
+static const char *
+extended_op_oid2string( const char *oid )
+{
+ const char *rval = NULL;
+
+ if ( 0 == strcmp(oid, EXTOP_BULK_IMPORT_START_OID)) {
+ rval = "Netscape Bulk Import Start";
+ } else if ( 0 == strcmp(oid, EXTOP_BULK_IMPORT_DONE_OID)) {
+ rval = "Netscape Bulk Import End";
+ } else {
+ rval = plugin_extended_op_oid2string( oid );
+ }
+
+ return( rval );
+}
diff --git a/ldap/servers/slapd/factory.c b/ldap/servers/slapd/factory.c
new file mode 100644
index 00000000..b4ef0585
--- /dev/null
+++ b/ldap/servers/slapd/factory.c
@@ -0,0 +1,458 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+
+#include "slap.h"
+
+/*
+ * This module provides a mechanism for extending core server objects.
+ * This functionality is provided to plugin writers so that they may
+ * efficiently pass state information between plugin calls. Typically
+ * a plugin might register both a pre-op and post-op call. It's very
+ * convenient for the plugin to associate it's private data with the
+ * operation object that's passed through the PBlock.
+ *
+ * --- An interface is made available to the core server.
+ *
+ * int factory_register_type(const char *name, size_t offset)
+ * void *factory_create_extension(int type,void *object,void *parent)
+ * void factory_destroy_extension(int type,void *object,void *parent,void **extension)
+ *
+ * An object that wishes to make itself available for extension must
+ * register with the Factory. It passes it's name, say 'Operation',
+ * and an offset into the structure of where the extension block
+ * is to be stored. In return a type handle is passed back, which is
+ * used in place of the name in the creation and destruction calls.
+ *
+ * When an object is created, which has registered as extensible, it
+ * must call the factory_create_extension with its type handle so that
+ * the extension block can be constructed. A pointer to the block is
+ * returned that *must* be stored in the object structure at the offset
+ * declared by the call to factory_register_type.
+ *
+ * When an extensible object is destroyed the extension block must also
+ * be destroyed. The factory_destroy_extension call is provided to
+ * tidy up and free any extenions created for this object.
+ *
+ * --- An interface is made available to the plugins.
+ *
+ * int slapi_register_object_extension(
+ * const char* objectname,
+ * slapi_extension_constructor_fnptr constructor,
+ * slapi_extension_destructor_fnptr destructor,
+ * int *objecttype,
+ * int *extensionhandle)
+ * void *slapi_get_object_extension(int objecttype,void *object,int extensionhandle)
+ *
+ * When the plugin is initialised it must register its object extensions.
+ * It must provide the name of the object to be extended, say 'Operation',
+ * and constructor and destructor functions. These functions are called
+ * when the object is constructed and destroyed. The extension functions
+ * would probably allocate some memory and initialise it for its
+ * own use. The registration function will fail if any objects have already
+ * been created. This is why the registration *must* happen during plugin
+ * initialisation. In return the plugin will receive two handles, one for
+ * the object type, and one for the object extension. These only have meaning
+ * for the slapi_get_object_extension function.
+ *
+ * A plugin retrieves a pointer to its own extension by calling slapi_get_
+ * object_extension with the object from which the extension is to be
+ * retrieved. The factory uses the objecttype to find the offset into the
+ * object of where the extension block is stored. The extension handle is
+ * then used to find the appropriate extension within the block.
+ *
+ * Currently (Oct 98) the only supported objects are Operation and Connection.
+ *
+ * This documentation is available here...
+ *
+ * http://warp/server/directory-server/hydra/replication/objext.html
+ */
+
+/* JCM: Could implement simple object leak detection here */
+
+
+/* ---------------------- Factory Extension ---------------------- */
+
+struct factory_extension
+{
+ const char *pluginname;
+ slapi_extension_constructor_fnptr constructor;
+ slapi_extension_destructor_fnptr destructor;
+};
+
+static struct factory_extension*
+new_factory_extension(
+ const char *pluginname,
+ slapi_extension_constructor_fnptr constructor,
+ slapi_extension_destructor_fnptr destructor)
+{
+ struct factory_extension* fe= (struct factory_extension*)slapi_ch_malloc(sizeof(struct factory_extension));
+ if(pluginname!=NULL)
+ {
+ fe->pluginname= slapi_ch_strdup(pluginname);
+ }
+ fe->constructor= constructor;
+ fe->destructor= destructor;
+ return fe;
+}
+
+static void
+delete_factory_extension(struct factory_extension **fe)
+{
+ slapi_ch_free( (void **) &((*fe)->pluginname) );
+ slapi_ch_free( (void **) fe);
+}
+
+/* ---------------------- Factory Type ---------------------- */
+
+#define MAX_EXTENSIONS 32
+
+struct factory_type
+{
+ char *name; /* The name of the object that can be extended */
+ int extension_count; /* The number of extensions registered for this object */
+ PRLock *extension_lock; /* Protect the array of extensions */
+ size_t extension_offset; /* The offset into the object where the extension pointer is */
+ long existence_count; /* Keep track of how many extensions blocks are in existence */
+ struct factory_extension *extensions[MAX_EXTENSIONS]; /* The extension registered for this object type */
+};
+
+static struct factory_type*
+new_factory_type(const char *name, size_t offset)
+{
+ struct factory_type* ft= (struct factory_type*)slapi_ch_malloc(sizeof(struct factory_type));
+ ft->name= slapi_ch_strdup(name);
+ ft->extension_lock = PR_NewLock();
+ ft->extension_count= 0;
+ ft->extension_offset= offset;
+ ft->existence_count= 0;
+ return ft;
+}
+
+static void
+delete_factory_type(struct factory_type **ft)
+{
+ slapi_ch_free( (void **) &((*ft)->name));
+ PR_DestroyLock((*ft)->extension_lock);
+ slapi_ch_free( (void **) ft);
+}
+
+static int
+factory_type_add_extension(struct factory_type *ft,struct factory_extension *fe)
+{
+ int extensionhandle= -1;
+ PR_Lock(ft->extension_lock);
+ if(ft->existence_count>0)
+ {
+ /* Can't register an extension if there are objects already with extension blocks */
+ LDAPDebug( LDAP_DEBUG_ANY, "ERROR: factory.c: Registration of %s extension by %s failed.\n", ft->name, fe->pluginname, 0);
+ LDAPDebug( LDAP_DEBUG_ANY, "ERROR: factory.c: %lu %s objects already in existence.\n", ft->existence_count, ft->name, 0);
+ }
+ else
+ {
+ if(ft->extension_count<MAX_EXTENSIONS)
+ {
+ extensionhandle= ft->extension_count;
+ ft->extensions[ft->extension_count]= fe;
+ ft->extension_count++;
+ }
+ else
+ {
+ LDAPDebug( LDAP_DEBUG_ANY, "ERROR: factory.c: Registration of %s extension by %s failed.\n", ft->name, fe->pluginname, 0);
+ LDAPDebug( LDAP_DEBUG_ANY, "ERROR: factory.c: %d extensions already registered. Max is %d\n", ft->extension_count, MAX_EXTENSIONS, 0);
+ }
+ }
+ PR_Unlock(ft->extension_lock);
+ return extensionhandle;
+}
+
+static void
+factory_type_increment_existence(struct factory_type *ft)
+{
+ ft->existence_count++;
+}
+
+static void
+factory_type_decrement_existence(struct factory_type *ft)
+{
+ ft->existence_count--;
+ if(ft->existence_count<0)
+ {
+ /* This just shouldn't happen */
+ LDAPDebug( LDAP_DEBUG_ANY, "ERROR: factory.c: %lu %s object extensions in existence.\n", ft->extension_count, ft->name, 0);
+ }
+}
+
+/* ---------------------- Factory Type Store ---------------------- */
+
+#define MAX_TYPES 16
+
+static PRLock *factory_type_store_lock;
+static struct factory_type* factory_type_store[MAX_TYPES];
+static int number_of_types= 0;
+
+static void
+factory_type_store_init()
+{
+ int i= 0;
+ factory_type_store_lock= PR_NewLock(); /* JCM - Should really free this at shutdown */
+ for(i=0;i<MAX_TYPES;i++)
+ {
+ factory_type_store[number_of_types]= NULL;
+ }
+}
+
+static int
+factory_type_store_add(struct factory_type* ft)
+{
+ int type= number_of_types;
+ factory_type_store[type]= ft;
+ number_of_types++;
+ return type;
+}
+
+static void
+factory_type_store_remove(struct factory_type *ft)
+{
+ int i;
+ int found_it = 0;
+
+ for (i = 0; i < number_of_types; i++)
+ {
+ if (!found_it)
+ {
+ if (factory_type_store[i] == ft)
+ {
+ found_it = 1;
+ }
+ }
+ else
+ {
+ factory_type_store[i-1] = factory_type_store[i];
+ }
+ }
+
+ if (found_it)
+ {
+ factory_type_store[i-1] = NULL;
+ number_of_types--;
+ }
+}
+
+static struct factory_type*
+factory_type_store_get_factory_type(int type)
+{
+ if(type>=0 && type<number_of_types)
+ {
+ return factory_type_store[type];
+ }
+ else
+ {
+ return NULL;
+ }
+}
+
+static int
+factory_type_store_name_to_type(const char* name)
+{
+ int i;
+ for(i=0;i<number_of_types;i++)
+ {
+ if(strcasecmp(factory_type_store[i]->name,name)==0)
+ {
+ return i;
+ }
+ }
+ return -1;
+}
+
+/* ---------------------- Core Server Functions ---------------------- */
+
+/*
+ * Function for core server usage.
+ * See documentation at head of file.
+ */
+int
+factory_register_type(const char *name, size_t offset)
+{
+ int type= 0;
+ if(number_of_types==0)
+ {
+ factory_type_store_init();
+ }
+ PR_Lock(factory_type_store_lock);
+ if(number_of_types<MAX_TYPES)
+ {
+ struct factory_type* ft= new_factory_type(name,offset);
+ type= factory_type_store_add(ft);
+ }
+ else
+ {
+ LDAPDebug( LDAP_DEBUG_ANY, "ERROR: factory.c: Registration of %s object failed.\n", name, 0, 0);
+ LDAPDebug( LDAP_DEBUG_ANY, "ERROR: factory.c: %d objects already registered. Max is %d\n", number_of_types, MAX_TYPES, 0);
+ type= -1;
+ }
+ PR_Unlock(factory_type_store_lock);
+ return type;
+}
+
+/*
+ * Function for core server usage.
+ * See documentation at head of file.
+ */
+void *
+factory_create_extension(int type,void *object,void *parent)
+{
+ int n;
+ void **extension= NULL;
+ struct factory_type* ft= factory_type_store_get_factory_type(type);
+
+ if(ft!=NULL)
+ {
+ PR_Lock(ft->extension_lock);
+ if((n = ft->extension_count)>0)
+ {
+ int i;
+ factory_type_increment_existence(ft);
+ PR_Unlock(ft->extension_lock);
+ extension= (void**)slapi_ch_malloc(n*sizeof(void*));
+ for(i=0;i<n;i++)
+ {
+ slapi_extension_constructor_fnptr constructor= ft->extensions[i]->constructor;
+ if(constructor!=NULL)
+ {
+ extension[i]= (*constructor)(object,parent);
+ }
+ }
+ }
+ else
+ {
+ /* No extensions registered. That's OK */
+ PR_Unlock(ft->extension_lock);
+ }
+ }
+ else
+ {
+ /* The type wasn't registered. Programming error? */
+ LDAPDebug( LDAP_DEBUG_ANY, "ERROR: factory.c: Object type handle %d not valid. Object not registered?\n", type, 0, 0);
+ }
+ return (void*)extension;
+}
+
+/*
+ * Function for core server usage.
+ * See documentation at head of file.
+ */
+void
+factory_destroy_extension(int type,void *object,void *parent,void **extension)
+{
+ if(extension!=NULL && *extension!=NULL)
+ {
+ struct factory_type* ft= factory_type_store_get_factory_type(type);
+ if(ft!=NULL)
+ {
+ int i,n;
+
+ PR_Lock(ft->extension_lock);
+ n=ft->extension_count;
+ factory_type_decrement_existence(ft);
+ PR_Unlock(ft->extension_lock);
+ for(i=0;i<n;i++)
+ {
+ slapi_extension_destructor_fnptr destructor= ft->extensions[i]->destructor;
+ if(destructor!=NULL)
+ {
+ void **extention_array= (void**)(*extension);
+ (*destructor)(extention_array[i],object,parent);
+ }
+ }
+ }
+ else
+ {
+ /* The type wasn't registered. Programming error? */
+ LDAPDebug( LDAP_DEBUG_ANY, "ERROR: factory.c: Object type handle %d not valid. Object not registered?\n", type, 0, 0);
+ }
+ slapi_ch_free(extension);
+ }
+}
+
+/* ---------------------- Slapi Functions ---------------------- */
+
+/*
+ * Function for plugin usage.
+ * See documentation at head of file.
+ */
+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= 0;
+ struct factory_extension* fe;
+ struct factory_type* ft;
+ fe= new_factory_extension(pluginname,constructor, destructor);
+ *objecttype= factory_type_store_name_to_type(objectname);
+ ft= factory_type_store_get_factory_type(*objecttype);
+ if(ft!=NULL)
+ {
+ *extensionhandle= factory_type_add_extension(ft,fe);
+ if(*extensionhandle==-1)
+ {
+ delete_factory_extension(&fe);
+ factory_type_store_remove(ft);
+ delete_factory_type(&ft);
+ }
+ }
+ else
+ {
+ LDAPDebug( LDAP_DEBUG_ANY, "ERROR: factory.c: Plugin %s failed to register extension for object %s.\n", pluginname, objectname, 0);
+ rc= -1;
+ }
+ return rc;
+}
+
+/*
+ * Function for plugin usage.
+ * See documentation at head of file.
+ */
+void *
+slapi_get_object_extension(int objecttype,void *object,int extensionhandle)
+{
+ void *object_extension= NULL;
+ struct factory_type* ft= factory_type_store_get_factory_type(objecttype);
+ if(ft!=NULL)
+ {
+ char *object_base= (char*)object;
+ void **o_extension= (void**)(object_base + ft->extension_offset);
+ void **extension_array= (void**)(*o_extension);
+ if ( extension_array != NULL ) {
+ object_extension= extension_array[extensionhandle];
+ }
+ }
+ return object_extension;
+}
+
+/*
+ * sometimes a plugin would like to change its extension, too.
+ */
+void
+slapi_set_object_extension(int objecttype, void *object, int extensionhandle,
+ void *extension)
+{
+ void *object_extension = NULL;
+ struct factory_type *ft = factory_type_store_get_factory_type(objecttype);
+ if (ft != NULL) {
+ char *object_base = (char *)object;
+ void **o_extension = (void **)(object_base + ft->extension_offset);
+ void **extension_array= (void**)(*o_extension);
+ if (extension_array != NULL) {
+ extension_array[extensionhandle] = extension;
+ }
+ }
+}
diff --git a/ldap/servers/slapd/fe.h b/ldap/servers/slapd/fe.h
new file mode 100644
index 00000000..f1e8dae6
--- /dev/null
+++ b/ldap/servers/slapd/fe.h
@@ -0,0 +1,159 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+#ifndef _SLAPD_FE_H_
+#define _SLAPD_FE_H_
+
+#include <prio.h>
+#include "slap.h"
+
+/*
+ * Global Variables...
+ */
+#ifdef LDAP_DEBUG
+#if defined( _WIN32 )
+#ifndef DONT_DECLARE_SLAPD_LDAP_DEBUG
+extern __declspec(dllimport) int slapd_ldap_debug;
+#endif /* DONT_DECLARE_SLAPD_LDAP_DEBUG */
+#endif
+#endif
+extern int active_threads;
+extern PRInt32 ops_initiated;
+extern PRInt32 ops_completed;
+extern PRLock *ops_mutex;
+extern PRThread *listener_tid;
+extern PRThread *listener_tid;
+extern int num_conns;
+extern PRLock *num_conns_mutex;
+extern char *pid_file;
+extern char *start_pid_file;
+extern int should_detach;
+extern int connection_type; /* JCM - Evil. Solve by creating a real connection constructor & destructor */
+#ifndef HAVE_TIME_R
+extern PRLock *time_func_mutex;
+#endif /* HAVE_TIME_R */
+extern PRLock *currenttime_mutex;
+extern time_t starttime;
+extern char *configfile;
+#if defined( _WIN32 )
+extern LPTSTR pszServerName;
+#endif
+#if defined( _WIN32 )
+/* String constants (no change for international) */
+extern HANDLE hSlapdEventSource;
+extern SERVICE_STATUS LDAPServerStatus;
+extern SERVICE_STATUS_HANDLE hLDAPServerServiceStatus;
+#endif
+
+/*
+ * auth.c
+ *
+ */
+void client_auth_init();
+void handle_handshake_done (PRFileDesc *prfd, void* clientData);
+int handle_bad_certificate (void* clientData, PRFileDesc *prfd);
+
+/*
+ * connection.c
+ */
+void op_thread_cleanup();
+
+/*
+ * ntuserpin.c - Prompts for the key database passphrase.
+ */
+#include "svrcore.h"
+typedef struct SVRCORENTUserPinObj SVRCORENTUserPinObj;
+SVRCOREError SVRCORE_CreateNTUserPinObj(SVRCORENTUserPinObj **out);
+void SVRCORE_SetNTUserPinInteractive(SVRCORENTUserPinObj *obj, PRBool interactive);
+void SVRCORE_DestroyNTUserPinObj(SVRCORENTUserPinObj *obj);
+
+/*
+ * connection.c
+ */
+void connection_abandon_operations( Connection *conn );
+int connection_activity( Connection *conn );
+void init_op_threads();
+int connection_new_private(Connection *conn);
+void connection_remove_operation( Connection *conn, Operation *op );
+int connection_operations_pending( Connection *conn, Operation *op2ignore,
+ int test_resultsent );
+void connection_done(Connection *conn);
+void connection_cleanup(Connection *conn);
+void connection_reset(Connection* conn, int ns, PRNetAddr * from, int fromLen, int is_SSL);
+
+/*
+ * conntable.c
+ */
+
+/*
+ * Note: the correct order to use when acquiring multiple locks is
+ * c[i]->c_mutex followed by table_mutex.
+ */
+struct connection_table
+{
+ int size;
+ /* An array of connections, file descriptors, and a mapping between them. */
+ Connection *c;
+ struct POLL_STRUCT *fd;
+ PRLock *table_mutex;
+};
+typedef struct connection_table Connection_Table;
+
+extern Connection_Table *the_connection_table; /* JCM - Exported from globals.c for daemon.c, monitor.c, puke, gag, etc */
+
+Connection_Table *connection_table_new(int table_size);
+void connection_table_free(Connection_Table *ct);
+void connection_table_abandon_all_operations(Connection_Table *ct);
+Connection *connection_table_get_connection(Connection_Table *ct, int sd);
+void connection_table_move_connection_out_of_active_list(Connection_Table *ct, Connection *c);
+void connection_table_move_connection_on_to_active_list(Connection_Table *ct, Connection *c);
+void connection_table_as_entry(Connection_Table *ct, Slapi_Entry *e);
+void connection_table_dump_activity_to_errors_log(Connection_Table *ct);
+Connection* connection_table_get_first_active_connection (Connection_Table *ct);
+Connection* connection_table_get_next_active_connection (Connection_Table *ct, Connection *c);
+typedef int (*Connection_Table_Iterate_Function)(Connection *c, void *arg);
+int connection_table_iterate_active_connections(Connection_Table *ct, void* arg, Connection_Table_Iterate_Function f);
+#if defined( _WIN32 )
+Connection* connection_table_get_connection_from_fd(Connection_Table *ct,PRFileDesc *prfd);
+#endif
+#if 0
+void connection_table_dump(Connection_Table *ct);
+#endif
+
+/*
+ * daemon.c
+ */
+int signal_listner();
+int daemon_pre_setuid_init(daemon_ports_t *ports);
+void slapd_daemon( daemon_ports_t *ports );
+void daemon_register_connection();
+int slapd_listenhost2addr( const char *listenhost, PRNetAddr *addr );
+int daemon_register_reslimits( void );
+int secure_read_function( int ignore , void *buffer, int count, struct lextiof_socket_private *handle );
+int secure_write_function( int ignore, const void *buffer, int count, struct lextiof_socket_private *handle );
+int read_function(int ignore, void *buffer, int count, struct lextiof_socket_private *handle );
+int write_function(int ignore, const void *buffer, int count, struct lextiof_socket_private *handle );
+PRFileDesc * get_ssl_listener_fd();
+int configure_pr_socket( PRFileDesc **pr_socket, int secure );
+void configure_ns_socket( int * ns );
+
+/*
+ * sasl_io.c
+ */
+int sasl_read_function(int ignore, void *buffer, int count, struct lextiof_socket_private *handle );
+int sasl_write_function(int ignore, const void *buffer, int count, struct lextiof_socket_private *handle );
+int sasl_io_enable(Connection *c);
+int sasl_recv_connection(Connection *c, char *buffer, size_t count,PRInt32 *err);
+
+/*
+ * sasl_map.c
+ */
+int sasl_map_config_add(Slapi_PBlock *pb, Slapi_Entry* entryBefore, Slapi_Entry* e, int *returncode, char *returntext, void *arg);
+int sasl_map_config_delete(Slapi_PBlock *pb, Slapi_Entry* entryBefore, Slapi_Entry* e, int *returncode, char *returntext, void *arg);
+int sasl_map_domap(char *sasl_user, char *sasl_realm, char **ldap_search_base, char **ldap_search_filter);
+int sasl_map_init();
+int sasl_map_done();
+
+#endif
diff --git a/ldap/servers/slapd/fedse.c b/ldap/servers/slapd/fedse.c
new file mode 100644
index 00000000..e2779e08
--- /dev/null
+++ b/ldap/servers/slapd/fedse.c
@@ -0,0 +1,1886 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+/*
+ * fedse.c - Front End DSE (DSA-Specific Entry) persistent storage.
+ *
+ * The DSE store is an LDIF file contained in the file
+ * INSTANCEDIR/config/dse.ldif, where INSTANCEDIR is
+ * the directory of the server instance.
+ *
+ * In core, the DSEs are stored in an AVL tree, keyed on
+ * DN. Whenever a modification is made to a DSE, the
+ * in-core entry is updated, then dse_write_file() is
+ * called to commit the changes to disk.
+ *
+ * This is designed for a small number of DSEs, say
+ * a maximum of 10 or 20. Currently, there is only
+ * one DSE, the root DSE. If large numbers of DSEs
+ * need to be stored, this approach of writing out
+ * the entire contents on every modification will
+ * be insufficient.
+ *
+ */
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <prio.h>
+#include <prcountr.h>
+#include "slap.h"
+#include "fe.h"
+
+#if !defined (_WIN32)
+#include <pwd.h>
+#endif /* _WIN32 */
+
+extern char ** getSupportedCiphers();
+
+static const char *internal_entries[] =
+{
+ "dn:\n"
+ "objectclass: top\n"
+ "aci: (targetattr != \"aci\")(version 3.0; aci \"rootdse anon read access\"; allow(read,search,compare) userdn=\"ldap:///anyone\";)\n",
+
+ "dn:cn=features,cn=config\n"
+ "objectclass:top\n"
+ "objectclass:nsContainer\n"
+ "cn:features\n",
+
+ "dn:oid=2.16.840.1.113730.3.4.9,cn=features,cn=config\n"
+ "objectclass:top\n"
+ "objectclass:directoryServerFeature\n"
+ "oid:2.16.840.1.113730.3.4.9\n"
+ "cn: VLV Request Control\n"
+ "aci: (targetattr != \"aci\")(version 3.0; acl \"VLV Request Control\"; allow( read, search, compare, proxy ) userdn = \"ldap:///all\";)\n",
+
+ "dn:oid="EXTOP_BULK_IMPORT_START_OID",cn=features,cn=config\n"
+ "objectclass:top\n"
+ "objectclass:directoryServerFeature\n"
+ "cn: Bulk Import\n",
+
+ "dn:cn=options,cn=features,cn=config\n"
+ "objectclass:top\n"
+ "objectclass:nsContainer\n"
+ "cn:options\n",
+
+ "dn:cn=encryption,cn=config\n"
+ "objectclass:top\n"
+ "objectclass:nsEncryptionConfig\n"
+ "cn:encryption\n"
+ "nsSSLSessionTimeout:0\n"
+ "nsSSLClientAuth:allowed\n"
+ "nsSSL2:off\n"
+ "nsSSL3:off\n",
+
+ "dn:cn=monitor\n"
+ "objectclass:top\n"
+ "objectclass:extensibleObject\n"
+ "cn:monitor\n"
+ "aci: (target =\"ldap:///cn=monitor*\")(targetattr != \"aci || connection\")(version 3.0; acl \"monitor\"; allow( read, search, compare ) userdn = \"ldap:///anyone\";)\n",
+
+ "dn:cn=snmp,cn=monitor\n"
+ "objectclass:top\n"
+ "objectclass:extensibleObject\n"
+ "cn:snmp\n",
+
+ "dn:cn=counters,cn=monitor\n"
+ "objectclass:top\n"
+ "objectclass:extensibleObject\n"
+ "cn:counters\n",
+
+ "dn:cn=sasl,cn=config\n"
+ "objectclass:top\n"
+ "objectclass:nsContainer\n"
+ "cn:sasl\n",
+
+ "dn:cn=mapping,cn=sasl,cn=config\n"
+ "objectclass:top\n"
+ "objectclass:nsContainer\n"
+ "cn:mapping\n",
+
+ "dn:cn=SNMP,cn=config\n"
+ "objectclass:top\n"
+ "objectclass:nsSNMP\n"
+ "cn:SNMP\n"
+ "nsSNMPEnabled:on\n"
+ "nsSNMPOrganization:\n"
+ "nsSNMPLocation:\n"
+ "nsSNMPContact:\n"
+ "nsSNMPDescription:\n"
+ "nsSNMPMasterHost:\n"
+ "nsSNMPMasterPort:\n"
+ "aci:(target=\"ldap:///cn=SNMP,cn=config\")(targetattr !=\"aci\")(version 3.0;acl \"snmp\";allow (read, search, compare)(userdn = \"ldap:///anyone\");)\n",
+};
+
+static int NUM_INTERNAL_ENTRIES = sizeof(internal_entries)/sizeof(internal_entries[0]);
+
+static char *easter_egg_entry=
+{
+"1E14405A150F47341F0E09191B0A1F5A3E13081F190E1508035A2E1F1B1756191447171514"
+"130E1508701518101F190E39161B0909405A0E150A701518101F190E39161B0909405A1508"
+"1D1B1413001B0E1315141B162F14130E701518101F190E39161B0909405A1E13081F190E15"
+"0803040E1F1B17041F020E1F14091318161F041518101F190E70150F405A341F0E09191B0A"
+"1F5A291F190F08130E035A2915160F0E1315140970150F405A341F0E09191B0A1F5A3E1308"
+"1F190E1508035A2E1F1B17701E1F091908130A0E131514405A3E1B0C131E5A3815081F121B"
+"17565A301B190B0F1F1613141F5A3815081F121B17565A3B140E121514035A3C15020D1508"
+"0E12565A3B161511705A5A3D15141E121B161F111B08565A3508161B5A321F1D1B080E0356"
+"5A3415081311155A3215091513565A341B0E121B145A3113141E1F08565A3E1F15145A361B"
+"19111F0356705A5A2E1215171B095A361B19111F03565A281319125A371F1D1D1314091514"
+"565A2D1316165A371508081309565A3F161613150E5A291912161F1D1F161713161912565A"
+"705A5A371B08115A2917130E12565A5A2815185A2D1F160E171B14565A2F161C5A2D1F160E"
+"171B14565A5A39121F090E15145A2D131616131B1709701E1F091908130A0E131514405A3B"
+"141E5A1B16165A0E121F5A150E121F08095A0D12155A121B0C1F5A1D15141F5A181F1C1508"
+"1F5A0F095470705A70707070"
+};
+
+#define NUM_EASTER_EGG_PHOTOS 3
+
+static const char *easter_egg_photo1 =
+"jpegphoto:: /9j/4AAQSkZJRgABAgAAZABkAAD/7AARRHVja3kAAQAEAAAAHgAA/+4ADkFkb2JlAGTAAAAA"
+"Af/bAIQAEAsLCwwLEAwMEBcPDQ8XGxQQEBQbHxcXFxcXHx4XGhoaGhceHiMlJyUjHi8vMzMv"
+"L0BAQEBAQEBAQEBAQEBAQAERDw8RExEVEhIVFBEUERQaFBYWFBomGhocGhomMCMeHh4eIzAr"
+"LicnJy4rNTUwMDU1QEA/QEBAQEBAQEBAQEBA/8AAEQgBewHnAwEiAAIRAQMRAf/EAKgAAAID"
+"AQEBAAAAAAAAAAAAAAQFAgMGAQAHAQADAQEBAAAAAAAAAAAAAAAAAQIDBAUQAAIBAwIDBQYD"
+"BQUFBwQDAAECAwARBCESMUEFUWFxIhOBkaEyFAaxQiPB0VIzFeFicoKS8KKyQyTxwlM0JTUW"
+"0uJzg/JjkxEAAgIBBAEDAwIFBAMAAAAAAAERAiExQRIDUWFxIoEyE1IEkaHBQiPwseFi0XIz"
+"/9oADAMBAAIRAxEAPwDNRpcMb2VQSCeFxpbxqYkIvtIVbBSdbXH76aR46C8U4ADRX5m8hHK3"
+"940E2MqKSNBtU8NfONfdeseaZUE8eOO6Dam8CzBzxIa5IPKwohbvGgI9Ro7FuwF23248gKEn"
+"ikgkQuxZiLFOwniB4VYilIpV1UqdDe+4HS/vqHGslaFrY43bksscpICq2qFTrfwqeQjQpH5X"
+"MWrb2Fka5Fl8QKrV2jygioHZbgAi9iQra+6u5mS07Isy3Ubl2HTaSSbAjShTKFnwQVQwaUED"
+"b8ykX3HbuueyrE9SNGnQEJYElgLany6VAH9IopJZQN1zcEHmNOVSzQ8eq6xunlIFrqDwokGc"
+"DiaVHYEMT5teK2vbjVebGZVMse677ShDcBzDDUGpqsog9bdqlxe1z5iB8eVWMkxxwyy7CRqR"
+"+W//APHjQ25QRgUrmyodsouFN+Vx28u6m2DaRo5IwNqkPtOl7eY8B8KTZeO8TXdmYm12Itc1"
+"XBkzY7gxyGM3sCDwrS1eSwyJawayXbHaSS6tcqg4gE226t2dtCuHaEhRaxPqAak2NgV99JW6"
+"jNLA8c0jO1/KxPbyql2lgjVxOCz/AJEJuPGs10tbj5DxvRg2GR9vpqAxI01YaewVATwGVv1h"
+"tYENr8qlfdSQ52TY3bcpB0bXjVKkufKpJP5V/sql1eWLl6GoiUSWkZg25ShI57uY8KXf0b1p"
+"XLyEJYkyNqd3Hb8aVR5U8LmzEMNNDRa9YyEVY0eyn52Iv3cKOF19rHK3Q0XHxulRtINpljIB"
+"LcWYBTp43NIZHUylillPLgdanLlZEsnqs5dr3BI07OFVLG8u9+OwbmJPfaqpV1l2ctibnQ80"
+"pKBWN1X5ewa1C4bhoOZq30gYmYpqCLG4FrX0tRqpCMVUx45JXchpQFIjvptFz7eBqm4CCiPp"
+"2Q+55V2xwoZSW0ut9unjVDIuhivbna+1fbzpr1TEaFY5sqb1SyhWSK4VDa4Tncjneg4kd3/U"
+"crDFa0a+ZmBF9APiaStiZCNimbHCSLArepIbbj+UE6m3bRkUGVhRxZBQbGJBBtqRbW9U4kCS"
+"vkxtodoEdyAb7hcAnSniKZcaPpmQg9YKCsYvYi1+J56VN7RC18lJF0GbHkhGVSkpYEo1gQL7"
+"bjtGtU9Wf0MNikjJul3Mm7Ugj99URStJF9MCIss3VCCD5EvcXF+PChMmbLkzCZf1CAFtt0rN"
+"U+XiMwNvAJJNktGx22hlYDThuXs99dxDkjHmCPsjIu7E29ntorISNRHjYswVW8027QK47KqX"
+"NDwJiuoeNWJ3KdpOvOtdsIiPU7jYLlklmt6b/KCdSD4VZnvgokkccX6l7B7m620t30Nl9QWW"
+"R1giEIYBAq+awBvxq2DAeVv+qkCKYmljJO7dYE/GlDmbOPQeNEheGXdYcK83OqVte99at486"
+"1gmB3hpn4no5MCPPjzojyBAAd2qgH20xaOXIBMqskZNjHcHaW5m3IV3ouFjTdFgchVmYuPVW"
+"+9Qh48fdVkUVx6kkjDHU7tpHFhwLNqbVy3ct4yi3XGBflNtknZb+nGiKreBF65jJiyZGPMHO"
+"9UuLi/6t2tvoeXI9dnga6Y3qEiVbmO7a2Y2qcCqsK+g4ZEciSULYqLEWBJ18apJpEQHZE4jx"
+"WiWIvIoUSHivnvrfv5Upg9Y3jVtrSaC4+YqPl/fRHSUny2kgLEbxuLkkqqDuphnRJGY5IVt9"
+"OrK1vLxU8u+hQnx1G4xALgjb/wBXkFI0IHqEgnhrbWmKT40iiT0wGPCVhwHctJscATQzZDho"
+"YxudCrDnpxHfTGDLiypzEPIFBNv4rd9R2Vct5eP4CgOklMO4MdEGjP2ngAKAeXIyDFOsR3jy"
+"ofyndpotdcxZRI3bkj1YaWBA/Nzq7Nc/TxiJvSeELcd54a1KWi3AX5yxY+Qs9i21hv0uGIoG"
+"QvNlBEVQRqOQ11pvk47Zm2bY3oRXEi6A3B+Ve80vGD6rqYmsHU2I7lvxrWrUJvXQAOUrjysq"
+"OTItiHvz5iuwtk5Eh9J7SseN7Ht41WMZjO0Ol1J1OlEY/pYWSrMPVdSA6Ecf8NaPTy4EXSpn"
+"xMpyg2mgl+Ye+r8qCV1iaA6myMd3mZjwNuw0zyshJ4kxpBckqUA02gr2js4caFkjgxyWcpsI"
+"ssZF9wW3Pjf3VlyUoBUks+NMwldlax8tyQT/AGUyh6sJoFVSFe4V/wCI3Op1q5ZOmRxRTSRl"
+"jLuZDbygoDxBsbe2gJMCKV48uSRZYJTa8QYN/pIGutU6Jrk1oMYwzYyNcOFK6l/mPaB3eNRn"
+"6nJeVYlDhjZVI5DmKD+vgw1jjSDayi0it8+7iOXKuP1HIkmaQxNCI1LDaoDa663rNUzMClhx"
+"kWSCFtqvG5BJ+UK5Pxoz1YhjXMZuTuJCm9Ioup75EZkBgS14lGp28KNw8vLyVky5ZlhxUfSK"
+"1207DRattxhkk6LCufuO1rkgAHaxAXzdlVZMEM2EciVyYLi7W12AggVJuoQS48u9gYWJ23AU"
+"MD7OZFL83qh82LFKDALDYvAqQOfaKda5WopPPJFl5imA+nIjBolUabRxJ7729lFQ9NxYsR5Z"
+"SHY2DuW1vfW1AYcuFjyvKNwGxlY9xZRYc+fGpSZE2XkxY8BtAOIY2FudOybwpqktQDBi9OlH"
+"r46FUjJ9ZtxAKi9xavUSkMcSfRbr7o2AHMBuP416sZf/AG4+QgEPqS5iqmi7lJX+6PObe6uu"
+"NrAEXRUba1rDd6rbd3vFFIiS5RKtZXRXdT+Wyqy27Ryqua7xRPMhWOV5QCON122/G9am0YFq"
+"xSSeg7eZkm2NfUEmx5UYuIzYcsk9lb5UXQ7lLFrjw/fRMBMLO0YG5yEBHG4u7t7bVwRMkXp2"
+"ZolO8seJbazBaHOiCAeVTvkUaEI242G6+3jVEeL6xl9UaAFwD83AL++ipFjlP6IJTaSSw81y"
+"NtjbsuKjhv6uTGbFlJKEcC9wT+yhToJlMMLfq22s51a3Cw10+Huq9AjRiFr6rblZSb6ipZEW"
+"PjCOSNi3lN1A5NcG9TI2yPBtAW8YDdgBKqfjQx7QVY4JQKy7ZZHVTcWuD8t6teKKU5CfIUUp"
+"Go1G4FbaeHDxqyWISOo3kn0Yyzka2Rb8u38a9LHFuYxkRul/WF9AVN2t32F6WQ0AZIlbZuFp"
+"EZWdbXA/IT76VfS4YlkZ2M0guVRBoSxrRTGMS7UiWZXREkbcRuBG3y7TzNL8mFsHIfJijVce"
+"NSskbaHcbjb43/fV0b0Ja3EuR0+bGiSdiCJOA56C+tC2O7UceFqZFMjKQ5U42Q2G0X43O2yj"
+"2UFKpLXC210FbJ+dSXqUm5GpsRpbuqxMmSMEQn07jzFdL1wwsNzGwCkBtdRepQYhyZhFCSbg"
+"nW3AU8CRXdtu1h311IJZLFF0JsL8yNaZ/RYmTisMUgZEY85kYKLLt4X53oMQ+mISkoVpFB48"
+"CSR+FJWn0HBQiyOSV82mtdhQvKsSkrvYAns8aYYixYeO8uUhkaQmOOG1iHTa2/XsBoaJt7fU"
+"s6o0flAK+Ykgtfs46UTrAQNem9Pwb5AZPUlgYBw4vwBvt8CD7LUZ9TFj4ZmxiZfSDLc8L6rv"
+"9zV7puQozkULf1LyPID+m5IbTw83woXr50GPjLaJfPJY32nW48Ky1tk00WBbLmT5GNtn/kRH"
+"RVsCXbXcx51Tiyv69w7KjrskIsW9PmBeqv8AqMhtkYLWG4qOxRxriwP6TTcNpC2B1JrWFEGc"
+"5k0KYGOcnHMaho9pYMLAG19u5e3to7qQglx0ZJVhngPlmNhu2ggoGFzfWgoLv0gJcs+3yvx2"
+"MxPtAI4cqGxiBLEiZCs5O8xyC+0nyrY/xVjnWdC/6l8EMuDKZReQySMr7SAbJrc3/ivQOVJm"
+"wvBBYrkITezXe5bg1OOorkYk0U8eOqSzFh6rkbDpsAuOw6+ak8mVHbISCEyS21ygd23Xa3Ln"
+"eqUvOoPwDpjerkFUUyyAkut7/wCI34WFRjjO1/LtQDzHmbH8tSxpZBAYbem24kkEhpNPlY34"
+"VWssJ8rEiw1Gt7r+UeNXkmDkbLGfVtuQ3BX8wHDXxqcOU6QOC6yBYyqqwuVDG3lPtvVQjdh6"
+"kYsBrbsr0uN5d24bmFyovxFPG4gdRY1YBc6VWqEnWpxrpf4VQGv+1caVsD1iymFHcheLb9AT"
+"2cKKz45JDbB2GOB7kEXAB+aw/MNao+03v058e+pmbs5qv7q7LmLg5rQyqzGVyEN97bDbTlXN"
+"aeVmlOTSVCBHxHx5X+mZcuGQC2ARbcp43PDymhMroMibJEk9H6hijRAnRgN9uPDlT1o4cVjH"
+"ZoxPdRJe4G4W8tz2mqsmM58MMeHOrzQkiUEhNxJH5vmqa9v0FKf/ACLej4bh5MkkmCL9NgNN"
+"zHXb5reNWQ5Es7HduZJGu7NwF7hW91OvTdV2SC8ZjVtosLEW3WA433H3ClOSk+TH6aRvGd25"
+"mcBCqj5RQrTZvBLWcEo+mmbD1ezM5G78wUEgLbvqvM6fDEpUMEd4zYC5I2i1vE1fleqnTt4u"
+"H9RrjTbu7D3d9EYrbUgVITaQfqMSGbdp5mY8qWdXYOPlg2MkCYuPJPaHAkIQtEb3kOmvhVss"
+"MORk/TxMrRYu0vIRuLk/lv2qKaTS4ZwmgWIBUbaq2AFzY7l770vjMUYeNSBJcWhA1Peal3S0"
+"mRtpYR55tvrmJVLxfqqFF7N8o9wquHbkBJtqxxRqAEGhG8bW91QjxMv1JI5CBGzfqMhs21ba"
+"adpoWV5kncIvlmkBFrm1v4KpNPCZEhE3TYMpiXLI7MWQ7fmA0PDl30LJ09I8MQKoeaRv5wGn"
+"zbgATRedkNFj7AzL642MxJvtPE93fQ8GbgRzIqsbwixDaXYDiviaptwozANlK4uYkAilcRxq"
+"fJuuWN+S91LpZ8mF/RkLAA6WAvtPfrTyXMjnWLJsBGCCFY32tfbreheqzqAMcRKHyF3+pzUf"
+"3fCiryk0nIirHERKzZE7NBGGRV0Juw8wseRvaicXqWN6h9FDobkCwAIG1XXhypV9NkRj1/SP"
+"05JCX0NgOJqfqRQ+izB0ZTukvoGXgbHl5a0dJ3b9ikgqPEkyMmWeaNnUOd7tqbMO0cCLVYuB"
+"kuHlnk3XJCqGs1iL+Y6U7wi2YI0CmFYgrqpAAdeXCiJ8EsjSsQZLgqqjzWvqLeFTnSDTijPT"
+"dNlxFaeNAI0spI1NmtbXmReoZfR51hBD2QEi4vYjVuQ561qZ8eM4ixG4W50tqwI1PdXUx5PS"
+"WNWBMNrbrWJvf9tKWHFNmLmx8ZcWxZnIZN7G+iFhu2jttS7KDJkPCb+jG7bBflfQ+6tPNhva"
+"eO24EOp9PQ3I0B9prORvDLDbJJ3BQEIHHxNbUyiLKCzHn9KJ1TzRvY+Ya+XlTPBgGdslVTFH"
+"Cu4spGrKeHtpRj4zmRlTUgE6a0y6Q0u7YiFZL3aTUrbvWpvprAl4Yy/p26Qzq7hlILHiTqXI"
+"94r1FtsGOqJIRGSWMnM30NeqOX/ZxH8yoRyDHCyCeVf5kUdwOA9QqCp/0CqeovIIgzLtELq5"
+"Rb23Fdo08RR8YX0XjDFhGLPc2ZbA3v8ACgMuGVsF5A7M6CN3BI8x2ttb3moWpoymMGyDcCYy"
+"VMnDVgdxP+mjnb9G+2wCFnOp3NtVfAcaCBAmlKEfonfa1uChG/fRhKJBZbHdsUgkhrMPcTbW"
+"qhiKY98GNOwYFtAgGp2EroTVYVCnrHymAMQBwJJa3DxrptsZEuY7gXOhYLryomOQPDIqoACd"
+"u0A63G5bHxpACyRM87ILBhsYknQB7MwHfUo4nGYsbeYFh5TxKgXvr2GvM+yciNS7pGAoPHy3"
+"83javAkZpZ7ho5FBZfmGrFrX7hThgFRRBJ44jYDciAmw3KEN7/5taodFFnd9C28WGu0Da1/G"
+"uy+mJAHPrLEQxBNj6b34c9NalGwlUyAhrjcpA0NgUb40hlSJGiASbdpj3vxJ2sbKF7Cp1rpx"
+"jkp6eV5rMWAbk17ebtFuHcagSsckMafq23h+/T/YUTkxSLFBLFuUgIpB8t1QbR4cNacNCQNk"
+"wxTRBWiuu8LGL2AIIJF7dhpfLgLLnZDWsgT9Phobfl7qOlmk9MlbhiQdgFwdq7X1v2LUFhOR"
+"ZmFiEcoeFyugAt/iq1hCaTYFHirHhNjpCZ8qRlcPwVUsS17876Uujxc2bKdkI9QCzFRoLj5f"
+"KK2CqxiRiV9VlCmQKdGjGot2/tpdirZXVQEdAWjBHzcefbrehW1E6mfdYooQXjIkYXZGP5fl"
+"v+2mC9MyV9DMw4/VbaPIRu2n5x3cKKnGFDK2VkGzbNqqbsfIwIC/4qV5XWepIfTS+MjeZQuh"
+"I1sb+001L0/mLC1JZMmZDkxZOZ509Q2hY+UWsCLHgKBaKN4neNgCH27WNjYjja1XMZ5sePKc"
+"AmNjGWPBrDcN16tx+nvJkxLkG3rAu6jRlUa34dmoqphE66FOLm5GIb47FlUEPcaeYWIr0s+d"
+"mb5Xa4Ita4W4HGw52phl/beXisXx3E0Wp2toxCi5uKvi6DHk46ZWVL9OXuFQWCrb99LnXXA4"
+"toZ9RJGu4XW9xfgCp0OtOkiRsaPEVI1Z1MsTsbsSQNwdhwsKY5+IrYkONhxolm9MMRfdddT/"
+"AL1EJ0rHMUUVtksMlmZRx3qPm/Gpd5XgpVgV5bQTYCIu9EwkAaReEuthttxuSDrwoObp8rRx"
+"TQfIV3NfivPd5daYSZuIcRMGIO8cSsJJDooHbyvc/wBlKsvqciSsuLL+kY1jUBbWQch7RRVW"
+"2C3qSyOpZE+NH0/JYenASVJWzam53UJ9QUUpAx28DyJ9gocNI7FmJJa+p1JNXY2HlZEghjTz"
+"nj2acya14rQgI37woCnciWJNitxe5taqocWOWS7yemliWbjc8gAO2nMfRI0gLOrycCzqDtHw"
+"quF8bEyI1RBIRIDGCPM27y7GIOq0uNllD9wKDpmWyyFkcxxFVYgE2La2tQ4dUZwWYEAqARYG"
+"3I01lknlllKo+OZ5A5guVUiMcCSdT4UJnelO4eBQJCtpbi17fmHGpTbfuECsWABPE8qIiRWI"
+"5X4VxICV2kqT460XgYE+SzvGt4oLGU9gJsKtgkN+mdF6nLEWhl9GNuG24JNSyMPPw8hWmHqS"
+"L8sh4m/ZWrxGgxceISGyqoFgKNyFxpccOdpjIuCbD/irLWWaxXSDKPiHqUILqyzIvk1OxwD5"
+"7kD8aGcdOwplaNLzKw3XvcAdhPZVc/Usc5EsjSgBQ6Qxxk6sjHVwvFT30HAWzZDjw2ADF5H5"
+"WsVNr+NZPr8tqq1M7JbBy9dgml+n9O8Lki7G1u/dU3LQMTNKTjL8iKLlifMAe2hF6GyI+5rA"
+"DeP4vKOFW4+bO2GrSkBQ20AgD5jZSDU8euPiQMXyIZImRWUq6gAhS5NwbrtXnV2KIxisiq0q"
+"o1okf9PdfT5dWsOWtIsPbivJMxLM5dBbk4HDvverP6zlxIscR8yEjebNoeQuO6rrSJSqmvLK"
+"qs6D6VxLAsYBhksChisPNaxVbCho/llaIgtc75T3cr0ngyvuCYNJFLdV+VWRCL+xRV+J1SZr"
+"Q5USo48osCO6xHf20W67WeWmirUer0DYZJHjjkCXRvmO61wb+/XlXXijij9YlfUUsF2i+nyk"
+"ip7kEPkG3HUbgoFjuGvA0ow36tkbzBCMki9pLbVF++4F/ZS66cm+K+0j0SJ5eTLLAWZQWitc"
+"W118t7+HGlIVXkSRpFF9Ru1YEcqbTS5uHEwz4GETCzXGhJ4XZaAkbHEPk3B5G0HILWyo66rU"
+"UZK8Zgi2fzKHuwsSDpa48K5kPeUSAtZtNpve3ZTXGhh9IJpfkLgVHIjjVCtwG5XIuK04LzlF"
+"OuJKMMSmF013y+UOVc3HhbQjlTCXAgYeh6DSzFrliyqtiAl3vcjUcKqWUSrjSIQxSyzgX8hX"
+"UPR8OS9lKKFDOGlN76t3m5Otqws2i1BXjvnYUcfrH1sNbWMd3kiVANDcAstN4MmPMCTQaowN"
+"2uLNcC3hxoQkMgi3ek20h1U2bykpYbfjVGN05UAbClfEcAM23VWbQEtGbjjUc/OCoGuhY7Au"
+"xLGwc2W+vZQMxlx2EXrqsjkAA3fUG+oburkeRnY8ZXJiXJjA0yoiAf8AMj6+6hz137dOW+RL"
+"kKHNwPKxuRz0U2om1vtU+wPG8e4P1DMy4InkORG4QkIoXbua+ugNZ+GPHXYjvuMoLOttoRr9"
+"vPStRkf0/OR/pZI542tbbZit9T3i9Z2fprxdQy4Y1Lrjta3cRcVp1Ws8PUydXrMlvSQkPU40"
+"ckRSgqwGpOl7cadPDBHGXxWtA9gFfW4rOY0u3Nh/NdxuUG2p0rTgI6LeMwlCFZQRa3C/l7aq"
+"7h5UyaUqn9C+CLG+kkdnRYgh2JxJe3Lu769VCIzRslwpQk2bgQTay6V6oxEesyVwUTktZR6j"
+"Ri4MgLSdpOwjXu0rzYqPjPGhZg6IhQLc7YyTx7q4LDKlUNb1NxXUk7gpNeiLpiFySjnRWGut"
+"wPwBFZxG4gM4wWSZ3FmcsFY2ADjyj8KtyGcyaqQrhF4fMUHlNuVhUXLxzi3mgRxvB/MSDuP+"
+"7VWVLtyIg7XG1kBsR4WPhaqX9Bf+SFyygMN23yrrZb31Y+y1GRszxsEFp4yzoeIYrqPwtS6W"
+"QeYL5EV2uON7G1EtkxnGUC8ckNxre7a+cDvIIptMEdJWNkLeZtAqj+Aq11Ptq5Hgs87HcAOL"
+"aHzswbQ6aGg8zIhZfUuSzbWW3JWFnW3fUsff6iIbemoBkIs4523cL6m9OMZFOQqK0yNPLffK"
+"LhV1ATXu1tf3UEjyQ2xz54wG22/ML/l/xAUzhu7pISGChVW3ltfRrr22NBZCGKdSq2Qgbgdd"
+"WJ07amryN6SQiRQ25m3MwawXUbjw+FHTPGI44t1rEK4A4vwe3tNAOJU+TaRuBB5Dfx9x0tQR"
+"fqE2aIyhWGM+YXuLX1JPDdVqjtnYSf1D8uZA4RNVWylAOZVwzA8edSxiJUZi1kgZQGOm4HRT"
+"w0+WkbdQfFyzHO29Y2IVrX4Cw8RTKJ127omukhUoVN9NfbxpurSgUjFW/UM7nyMzRkjQsdF3"
+"bRwOv7aFYmJFZ/lO4kngGtof31dNPHLjrsaz7grJbbcBfm/fVCneqK7A3IFiBcW4HXSpQ5Fp"
+"hy87ID7QsQuQpuPKTa48K9lHGknlYSKBDtFmF1GwBdqjvotnkSfyG2oCHl2V7F6fgyLIsl3M"
+"pYgklb/3quSUXZXS4JcjBnxFWNGUvkJrYlTodh7RV+Bit9fPPlne0RtHpYekAQx2jhV0TIvy"
+"gp6YWMg/w2UV3Cnx1lyHw7PInp7kW7aszbt3bepUtwVGQ4xS5sTR4MTAcS7A6n/E3bSXqH2p"
+"1LFhOX9QGkHmKEacdK3c5yI8QnGQNNYWQ8L++s99z5ebixw47qrjIUmRhptYHUCtojQdYfj+"
+"ogTq+McdHnIjyopLSREaEbLXHLjTRvSmwpnSzvNEwYrY6xjdxHMBqyHUmaOdiPz2PstUoOqT"
+"/SjCjG1eZGhO7QjTlas317ol2y0SyovTsigFdgZ1Q7h7e+gHCqN40a9iL/Ci5Xia5jbUm2y9"
+"lt2DnVUOFNLvkVf5Yu+741Sfklts5FCzI0psigXsON+XHtp39vfTlmcjyu6IeZIHG3jS+fBZ"
+"VDmRWEiNJsF1UFOI8baitT9oQEzxZLRja+9L20BCp5h401bKGq/yUjnLzeqfTtHgdP2i1l9R"
+"QF9oJFYbrPTcv6lpS0TE3JELCwPHgOFfRJMWNZciSee++OxQmwX+9WSGN07+nCWOR2zQ+2RS"
+"fLxPAdlqqfYtVTXkRZ2YZceABzvMSJNf5fLy7j22pn9rdLx+ozPNkLux4QAI76Mx7fCkOWFa"
+"R4x+S5OnPhWs+08Wbp+Nk7zv2yD5eY2g0qwiYljPL+0uiThj6Hpu/wCdDYi3ZSnCxpvt/qpx"
+"mvkYfUEMSN+ZXW5G7tp7H1VZxIfSYLCNxYagjupH1Hr0Alwsxo2GOk9n4XsVIvbup2ygiGjV"
+"RQxvCpcC41FC5yKMZmf5IkdwvO668KuWeKNQrm8DaxuNRY6gaVm/uLrcTJNi4zeq7Ltcg6Kp"
+"4/hWSUo05QhQPTU2QFbk3LW3G4FxpQzZBxntjMYEI2uFHmPtrQfa+Fi5AkyMwA48AA3MbLu4"
+"60x6vH9szQGcmERk7BKg/Nbh5edX8UojlszNUdt43MunXHlIgnuR+Z11drDRbeNcR5ZIDHM6"
+"pEVuCBYyEElRSySFYs9Gx23Rl/03te9j2G1X+qIzeTzFH+S9/L/CvGotRbJLcmIlFy5L7jrd"
+"HuArakXG3cD20x6N0yDIVZ3O9b6r3igMh4YpWfGKhZkBCcdoPEHdwNNOn4eRDAscM5UqxJK8"
+"Dr30V0biC+pS3g0KJEsVo1AUC2lZrr6hZ0kA0PlLD+LiKaOsuVGiM7A2IaxIvbS9U5vT8Z8Q"
+"RSnd8tzYAmx7rUJ5k6LJtQUrN9cscFxbbeVk01OhX21osKFI4URFCqgsAOApF07HYTHeg2Qt"
+"siYG10B5+ym308yZKTCW8fD0xpcUUaj4+WY9axITkqkkbJIoMbCzA6gjvrC5+PDBvKLb0ZCL"
+"3/Lfyj2Vq9ue0rOz7oWJ04BR4WrMdbuRIq+Z5jt2gcbHQjvrWbNRE6Cu0mmVHDR41d5VCyHy"
+"iwBB8bcK9NixPO5glS72N7hrWGuutqXYMm9xFkSFIBpJyuvMd1T6j9FA1+mzFozfcOfvpJMH"
+"dcdBx05duMXU3O4h9up2i3xqzHTNzduPhJeNTctw+NLsMzrgAJGzFbySONRe1+IrY9BVMHpc"
+"Rm8rON7EC991Twau3Zb4kVWraCifE6rjkPKhcLdifmFzxOmtFdOynmj+piJkRFCyoxswYHWz"
+"a+6nsufiKiu7eV9FNuNZnq8ydLkmyII1MOUUK202vruO0doFHZ11tWVqgnj7Cn7m6rk52d/T"
+"sLcsCgXQeXe5FzuvyF6WN9v9SiTeyixGgGv7KZ9KkEvVZs+YWaQAgKDxJ4ADwrSx5+NMrqqs"
+"fTBLAixtVV+NUljBVaq2XufPU+qwpg67opFOjDlbt7RW16RMuVgZPUCyDMzAwcdjIu02tprQ"
+"GZNg5rSRrHtOo3XF+fIUnxvqMZipYxqrNrf83DSnfNJ3T1M7ri8FsuNkYzbpV2gm6uO6tRIu"
+"1fOdbcSOZseXjWfw40yIpPqXLya7E7+0mjYeqZUcgiyI/VBCgFRwHI258K57OcboOu6Q1kUG"
+"dDe4C7Sdey40r1DQ5rl5G9Bke5NiQQwF9B316lP+5ryXr/AkQRPMQwj9Jw40vYAMDqPZUIJb"
+"Y4jmO9Qu9CdOLWue216HWddshJuGbeAOQu4sag+x4YyzXkDPsKnnrIo15UcdCJL3lBZCw8iB"
+"SxXixLD83tqvqU2O88LBiA5kJYny3OhsO5hVUcp9KRGPygE7e5r0NnEerCSdCmo5BrcvEWq1"
+"XKE3g8jNIza6gi4/Lx/bRoUZEd5CA6rujFgLi5HKlsTEENqFY2PYQtX6yIo3bbC2n5bty99O"
+"yEmQybMhY2GqpYcgCPN8Kugbeq48J2s25XHCwvz8aFnkCb1e7FgoJ5qRcW92tW4+T5CiqAyi"
+"+7tO42vTawE5Hyyo49NiA0agsbaAnyNfnQs0jMdboLbmvxA7/dQ0MoaNw7fqyFrk6E7gLj2G"
+"vSuZZRcnRRz/AITWVaw2U7SjmTMYoocYHduk3Bv7t7ge+ttjR4i4CxOY77d0iXHHjqKwTv8A"
+"UdZgikNl3ongLitn9L0HGy3llazY0Y9XcSUG7tHbWy8ehSSjfWcGM+5Y8GTJlTFZWKi4K8Lj"
+"jrS7pEjmORQ2iMGVeev/AGUbL/TTPkmBy53MU00Ka0p6WSkjv+XQH33qmsQT2ayaMOkgJceQ"
+"mykkkjd83uoUSski7vOL/Mey/CqYMhwPNZVvdSSbk1x2BmNr7AbW4i54ioiJIkuLMMrYOBuN"
+"172a3dUDnTmZl9DzgFQb8Bfda+nZVUjESrf5dvz94HH2VXkOkiAoxsFuwBtrxuT7acB7EMnq"
+"mRMpjW8VmJYoTrftpx9mdWxem5c6ZjgRzILPYnzobjh23rOCTZ5VHzDUcbg1ZDHKXVUW7OwV"
+"F5lm0Ap6LAk8n1zNmmMSLjhmMjqGKWuE4ki5HZWW+7ch3yIY23Kq8ENja9r8DWoZZYYI3W29"
+"VXcDwJtWN6tntnTlnjRCjEXUa6aamiXubVhLCEXVRuniVFDdu7Qe+o9KwIJ2kXJlCLGCQguN"
+"zX+XdyuL1J2DncSCztcfuFdWWSJrlQ5vuYHgSDce6pdnEIh5bYQsOP6uUuzYY/JENGFge06l"
+"rcbVOCRRDJvXcs9gyMNbqRp4EUMMlHVzp60txLxDFe63bf4VwTRsDjXIQ8Ct/LYHQX5GiBFu"
+"XkyrjtB6fkv5JCLWOu/aewXtTpeqN0/oeF9GwWddxPPi17G9Z55A2LFFGNU3BiRqd/KnGNih"
+"4ooupmSFZCFi2RkhBa49Q8geXPnQ6WcQtyq2SbnwbOB8TKgTNJKHLiViN1gRa9j4XrJdVgjw"
+"8l2QrsYXCKxawGtarE6UknSMbGyBZ4ksGU22mkmf9sZDsYokLIfz7r6d541UWmFBVbV4vJhB"
+"+o0mvmck3vyvWy+1uozy4zRzKPUhIRiPzi17t30CvS+gQucP6h3zZTYSRL6kSHXyHUDcfhTP"
+"oHRJ0wJJoDuSSQtCTZWsPL5h2aVdqNJmVbZDsrOWBGEYjAe4ZW8ntrK/dmVGcfGx0RV8zSHb"
+"wOlr05zsvKEiwTYbNIpsGV9q+29Zj7jxcsdQ3yeYMgKa6W/MFHZelWWirWWBh0L7k9LAkwsx"
+"2PoDdjk3JK6Wiv48L0AVWGUtI25Dq/aToW48b2NAKnpxbPzGzNV52tCXBO5SLBjfy8aVq5JV"
+"tPQ+oRx9OGDJPHGrY86iVUjGh0sAAOdI/urGw8fpuNjLAI19T1CFI4lfNfvpX9ldQyFz/oL7"
+"op1LbTyKDd5fGmn3PPHM/wBOyPG6i92IVfG1talYcG1YeW53M/lx9PSVJsNW9BQGCubm9vNx"
+"pVJtJuvlsLkHtJJovMkAVFA/TFxfvoA7joTqNSTTS3M7vMFgtI4O0EfmHstWi6XlH6Yqp8yA"
+"ArzsBakUGFlyreONrEaMRZQPE0Z0wXknjRgZ4mv/AIh8p/276WHKQUtFkPYnIAKq5PiLC/8A"
+"mrmdKFjuTuKgn3VWuXZCBHaTgTfn4VRki2LLLIbWVtl+Je2gA560oN7XUYOdOzJJmLKbkMJG"
+"tfg7AAU/mbcoBUsvJg203pb0PomYmAuRJG0P8aS6EgG91Xj4XpqQ6L5AGHYamqab9zLreGVM"
+"xEW1QRcWCk34d9B9U6KBjSZMJIzJIyoa9hcgADsF6OjieSZLgXLDyip5nTsssfTmZBa3pt50"
+"+P767OiNW4nH0OfvbbjwfPBeFWdEuG04WKMNGW1V42/Iljj2ed5BqewG9rVp83psb5jK6mKa"
+"YXIALIxA+ZW/EGgMaCHGPqxRl5rtFGb2s50ZvYKqvVZdqTU1b1E+yro4cWS0HfT44IzNGrFv"
+"We6RqN22+jMeQFO44MZoUSVd4jsEJHZQ+Bgx40Koi62G49p76NVSLkcGsT7Rzp/ulVvknpgP"
+"2tmlxejyDt9O+2NkvtJuluApL9zqksKY0MQAA3KzHbttwt231p+ywcQo3DW9yayOV/UMzPZp"
+"IjAGCkq6/Kg5+YCuO7aqdF2mT6VgYihoJ33KUAZr21u+4A916N3dJ6duSO6rIrAkDduNJeqE"
+"4caPiqTsF5X1K2c2DX8aFwpsfJjZp5S1iwCMpawJ5bXXlTo5qn9CuuyiB/i/01ofWVQJCC2o"
+"vSDLWN3lcXDK7EAAjcCSTQuVkx484XHkbbtIIA2hddNtyfxp306PKfCikHyKC0pewuGJbn40"
+"dtuNE/8AsT2WnB6BIZcVcjDiH1B02sbHXTjfXWiIIcuRUlmTZsBXawswKEkEV5M7GhAjncKV"
+"baVAA19n5ajH1fGmO6QlQpsgJ+YDiSP31zQ238fWTNF4ZZVE7kbolMiEDixG6x8LV6ro8hVj"
+"aRoowpuVNu23sr1HKvnH9TT8iiIM88gRhtYlWsW76ugyFCBmX9RLbezhbhQJ+XtFtakj2U37"
+"dPEV0cSJLw5s1jqe3xvVMkplZBbUHQ9/CuMwJJ5cKpbiLcP2mnApCYjqFY6AE391eaTRypIs"
+"NT33B091V8Dcdw1rzXKEkaG2nfzogJJ5JtqOD2DjvtrVcbKCDxtx764+qC3EA3Hf/sKgjWt3"
+"iiMAHQ5BEgJttPmAPIm9/fepmZV3sra8PdQQWRnVhqRa1EJA6/zLDW57TTVJHJVk5MIlXKXc"
+"kwZbDkzKRr7q1GdlRxZb5mXj6ywr6LPcws1rlhbS5HbwrOrgwtOJXa6kbdltOFbBOs4uX076"
+"SdEScrsEbWCPYWDLfs7KLVayaVtsYDOnLSPLFaxBBK8PN2V7BTbjORa7a3I7KYdd6Rm4kREs"
+"W1eIK22nnp20khyZYdEPl5g8KayiL6hwcLx1twJ5VL1yADawNyfdQ65ayEBhtP41YSWAGl7a"
+"86UEkJX8q8RbUKar3kqbHQ6n28qsaxUeNN+iY+BhqvVepeeMNtxYrXDvrufwWmlIFfTvtzMy"
+"gkk9saFjZXkvuN+G1OJrU9O6V07p/pmNRJIh88z+ZgAN11C8KF/qq5ebNLit6308I9MXtdi5"
+"a53cbKKYRq4SNGJ8ylblgt/0+OlVxSAYQ56STP06QkOo/Rfk2gYp4resv1Lo2bG7ISI0bc7T"
+"H5UQcWbvphlxyTbJIWUNHtZdp81wL3uT3/Ckf3H9wZHU0i6fCSdgtklCdsr24WHHbS45kvlF"
+"RCk7xO4Q3UGwv2D99WwyqunG973N+NWr0bM9MOwCBpBEDcHzEMWNh2baa9S6JgxRQPDviaeY"
+"Qi53LtF7tY68qp1T1M8iUyqWO8kNcksBrcjnXI5WZioXduuFNtbnnUGBZA+3UjcP8JJ/dV2N"
+"cAubXbQdwpV65tAWtCk1v2bhQS5Es0sYd4kQgkXsxPH4Vr3hjdtzC7bSo7PNbX4Ui+ySP6bI"
+"pTaRJffb5wR291aLT3U7/c0sQC0yQVtAx07b/GkOV1DK6zO/T+msYcRDtyMscxwKpTPqsORk"
+"RJjQMUWdgs7jiIwPNbx4VfjYkGHAsOOmyNBoO88T3mknGdwAOn/b/Tem2aKP1Jhf9WTU68e4"
+"UU0irBKIvKsasECi1you1h2VOVtqqN2vfztSLO6m0ErRqysyQygW/jPl/bVJTlgc61KY4sTq"
+"EaiRZVF7m3EXUVh8/Lny8t5cgBXHlEa6hQOVbvqWNboyY3zNisqtbW17sPcDWGzISG9Uagkq"
+"1uTDh8K060tSLFBNxY60ZgdHz+ptsxITIBoW4IPFjpRP210f+r9REUlxjRDfMRoSOAUHv/Cv"
+"pcUEOLCsOOgjjSwVFFgBejttXSJYUT1M99ufaj9KnObkOrzlCqxpwW/HzGjeuYX9Rx02AXU3"
+"BIuw7RTDPXKlxmixbLJL5PUJtsU8W8aDCx9I6YwzshpdCLniSRbavOsOHJKHlvCNa24uWjJZ"
+"nRDj7MVv1pZrOY1B3L2Lp3UR0noU7l4lxxtc3aWZPltpYFhTr7dxJ/SOVkIROSEiZ739M6nj"
+"3c60Fq0u6cfxuqtCifUzSbfKYl6GI67g5XSMVskASoo/JoqAcCRpbWsTFLLFIJEdlkGoYH31"
+"9S+45klwZsQJ6gkAEt72t83L+HjWAQYkkXoFFjyYrqSFB3WNu/U1i6LrryVfu1HOYQ16Dk5f"
+"WJfp1xw0qjzzg7Y1Xhdh+wcfjW2wej4uIRIQJcgC3qsBoOxF4KKp+3OlR9M6ZFGqhZZR6kx0"
+"uWPI/wCEaU1ply9zlr8daHbp8DG4uvgaJrm7W1uVAKdiqHEghO5F83adTVpAI118a8GuL12g"
+"GBZ/TY8qBlRVWb/lufynt0rOQdE6i2bH60O1Ea8jC22/zG3+Jq2Ferbr/cXomlmfOxlfprZp"
+"6R4A48HQeodOwfvoloY2FiOVqnXqytZ21ZdaqugOMDHDbrX7jVeZ0rEzR+qpBtYlTa47DRle"
+"pJwN51M7kfaiy488HqhopEKRpttt5rrfka+dZnSOpdJyPRmHplhoSDqO616+z1lvv/HVulJk"
+"AfqQuAD3PoaSqlMKJyOT55HjxACSRmkm3Cy28tu886fRzTthtLksY4UssEOiglR2GlT5QTFj"
+"gjINjcsBqDe+lXpj9QyfT9TdIsh/Tu1+3keFRbKl4h7inUoyMx5xGrkBY1CIAOQ7TzqksUcr"
+"e6njTLHwIocoJJaSQEqY/wAvv4VTm46xMURAxHzMDu4/hSVqvCE3IVDnpLhyRSm7woUQ8NCu"
+"h9lepWpaK7AC0g2nstXqnguWmBSXWBvckDlYVzQHafl93GrGQra/AjT8DVTKb8eAsR4Gtijr"
+"EAacOdVgncByrragHlwrhuNtv8VMRO9wb6jT3Vy3lJ7OXtrqi6sDxHCuAmzX8aAJbrID23/G"
+"uBQpuTqNPfXPyi3sHjU4FMk4DcBqfZx+FAB+OoSPcRd7a/jaoSON4HEMCfcagZiBDy9UlifE"
+"3/CqoZPUmJ5RuV94rRYwMJJbbZTYjUHvoI48mQ/qZjlmBsqqbACjX08bXIqlnCspPC9j7aGA"
+"JJJ1GeNEM5ECgpFGWJCpc6c6HGAwUh2H90j9tHFAuOz21Uqb35btaryJAth2mlAC1kZCb6gc"
+"xVsMzA7TqCKpkZgzLfQmxrgazK3YalgMIVaaRYhoXYKCeAvzrmXltPKBuPox3WBTwVBoth3g"
+"a1GGb0w72821lXxYbL/Ghj2064QmEQTPDKkqHay6g37613TvuLFmjx45iIJ43W/l8pABW9zW"
+"NjNwP2VMH3acTVxIpg3ryt9DM8bElUOxgq2+VwLEeFCdLw4sZYYY1O7au5gouS6szatQ/Qky"
+"R07JjmB27f0SzWv2ge+mWOo+pgDW0W5uxPBZOVIopFxHAjBtskzkhlHMMOXjSnqmRs6bCQVL"
+"w5LOuhBKOpsaJy81IsePIjQXVjZVb/Ehas7nZHqwRrdv0wB5taBF7IfRVrkqqJELC3CPcT/v"
+"VVjtdWA/KK7G4HT491tzzOwvx2gItRjG2VlvbcDb2U6uBWUn1ToSxr0nEWPRRElx3lQx/Gjm"
+"bnSf7YnXJ6JiMlriMI3+KMbDTUqWfb+Vbe2oeoyLP5RrYnlU2Y8Bqa8wuVIt/ZVUj2lTvuG8"
+"O2gAXJmRWO61lJAJPaLisJ1HIdpJpWsCxYacNadZs74uXkY0hs3qBgxvqhtbTupL1KD0kkQ+"
+"bQ2NuIOorRLBJs2gfJgy438oeUFDa17IBesf9KZ16ljqt2iUTp/kYA/7rmtR9sZ0/UMCTIn8"
+"0lxG3ADyrbQDuNL8GL0/uiWH8uRDIq9mov8AsoraJ9Aa0GH2Pheh0k5LLZ8p92vHYvlX9tP5"
+"XsrHjt5d9xauwQpjwxwoLJGoVR3KKX9Synx5AoBKsoLG2gKuOPjeo+60laIZgWUChsjBhyMi"
+"GaYb/RuUQ/LuPOiq5bnUptDOGwI5cTVaS7ppI76rYAey5/GoZ0phxzMovsK3Hau4Bhr3UL05"
+"HlnfLdvmLbY+OjbQCf8ATVKvxdhN5SKusSwxRkyD9GM3Kf8AiynVU8Bxas/gdIwsnq8MzKWl"
+"b9WQg+TcP7tq1WfiCSCQjWTYwU23EbuO0drUp+1nx5JJ9rASxEIyX817atbsqbqa1hvFsiX3"
+"GjAsLV6vXqII3EdtIokagyk8DY8O2usbEChY3Z57a2JJN1I4d9DcDQSOzmND41OqL6lj+Y6V"
+"6edY1IB8xGlVxbiNweFkvuL2516gcF2adw7XO2456Xo6k1DgScnq9XgQeHLQ16kB6vV6vUAe"
+"rP8A3uP/AEKTn50/GtBSD71NuhSEcd6fE0AfP4EEUbuoUSKy2uAeRvxorDCSySZMkpEy6qL2"
+"H97TsoIOixvvPEi3dY6++q5HVyTFcKOHbY1m036eopDm6iZ3OPEoiilO0tw0vxNqYYmKyzEN"
+"HuiaMDdwRmTiTftrP4uT6cq3IVdwDaXO3n8K1cuV0+RFUsDGQABuK6e8Vl3TWFVOGswAkbGH"
+"1u10CJuBWIahjcWFepmE6cCGB3uWJ7Qq8O29eqPyPSLRECET3GoNzpxqIsd3aLgmoycBY8P3"
+"V3cnA9vxrsGSKElVGoN65JY3tytVqG+i6Hh4XqNuwfmB9goAjwa3b+2uAFbjXUWJqbAW3k6j"
+"S1RYndcaX7e+kB0gFRt1IGtWYq2WV+G1LD/NpVMi2tbgQdaKjTbiEk6ub27lqq6ggfKfbBCw"
+"4xta3cKswdjSTMeF1f3j+ygMhyUEZAJQnibmr+mK3oyyk6eWMDw837arcYfE+9HkP5ixv3LS"
+"1pTLjyycl228b0fMwhwGI4sNq+2hIcaV8JII1Jlne4XnqbCmARkf+RktxZf7aW5EhkhhfmQw"
+"bxBrQdSxhhR+k+1iou7H5bn8o7TSKbbNHYKEIOlhYGpkbUARNzXeyonRiDyqXHTj3UhBNgMd"
+"DzcngdQF0tVRNqgtxodK8SbdtPYRahIW3M0RjwvPKkEdt8rKq3HNjaqI1BANqfdB6VNkuMxZ"
+"DH6Ei7bcWKspa3sqloLc04HpR7CwOxSp8h5WH7KrjZvqw1+EbGwSx03dvjRWQrWcWe/n/N/f"
+"oPLnESSSkMCkR4tblQUZLLcvjY+vlVWRiR+bczamgZtIz49vO9Np0U45x0sq7gym9xdQ0Zv4"
+"0qy0ZLbuRv23pNigtxUeZ4oFPcD2X1NXZaLDKY2NyDobW0txoroeJiiCTqGXuAiIEKnVXJNj"
+"cA3NUZkpyMgyuBeTUhRZfYPZT0QG++y0Veh45Gu9pWP+sj9lPFPnOmpJ17hSP7LyY5eipEuj"
+"47Mj/wCZi4Pxpw0qxpLKx0RSb1IyXCRTprp7aB6pK0OM0qayRuLDtUnhRCF22MbaAEHvtelH"
+"XXGHlxZDgtBKCrLxAf8Ait7KaWRMD+5o1mTFz04SIAe/TcKRtIcqP0nF3RSoftH5b0+ndZui"
+"zQ3DjHZZI27YpGIHu1rOCQxvZfmuQAOJJ0FVOASlj37QyZ06VltjY7ZLJP5F3BRYoNdzdluV"
+"C5GbkvnxZmQghfGYEqtwQgN2BbwrTYWOnReiRY5tvRd0hHN21asR1zN8/oA/qTeaQ/3eS+2s"
+"lZzBpxSUv6H00MHQMh0cXVh2HWhsiKVriNA+7TzfLr291BfaucMzomOxN3hHovftTQfC1NGl"
+"Xdstc9lOJJTgjCzooWd1L8tv9tXAg8DegMnqGJjCyr6kzaLHGAxueFyNBS3L6zkY+S0buPUd"
+"dqRoNyo3dpdjVKjfoQ7JDXqGRjRRelOfLKGt3ldbVR0pzKC6RhIwAL9tU43SGyIY2z3YkHcq"
+"A2tf+Lv1pmWhgUJcRqBoLhR8aTUOE5KVvjlFPU5hDhyO+4oB5wnzEdl+VYbIyTj+nHFdHncE"
+"MhsQAbg343rTfcHUohiHHjlUySEAoh3Nt53PKsHG4k6rHckgyW7gB2UXqnRT+qRL7j6BgZ+S"
+"IgJH9Ts3DX3ip9F6hN1DM6g7ECKCVYI0HLau5j72oBn9OIFeG06d9X/a2J9HBOzOWORKX8w1"
+"LfmbSoroaXhPA8lYRqXPBRQ2KVdXkBvyFFkAjXhXBGiiwFhe9G6klPDXkpdkUAsL66CuyRKQ"
+"XYcda62OjOHNyRwHKvTeqyFVG2/PnWqekCsC4RvmOB+VLaeIphQWDEIQWb5mO29G1N/uFXQr"
+"hQLua2rkk1ZXvCuE24++pGdrhvbTjXgytwINdoA9SL7xifI6ScZNHkdbE8PL5qe1mPurqECO"
+"kTAuIxdlHBS3M1N7OtZWuiA+fkHGyAs6+p6TAul9Gsb29tGYOPjZspMshG7zCKPS9zwv3VPM"
+"xMOZPqUnCMw8sfzA+bX8aY4fTcLDdZi7SFFvuOi8ONqyt2J03Vn6biIx9O6UvqRFBtsd8pOg"
+"PLU86lh4uFC6LHJumdGCliNhPx17K5LL0yfHLqnqQggTemT5GbgSKKhhxYoRPdxCmqMLBlHf"
+"prWLtZJZup86CB26bKVUxkFh/Ma1mta+3bXqsXJwJBdJGYKxla7We41J9lq9U87+HMgZp73B"
+"Br2y9rkAdh/srxuF/A1wixDdl/hau8ZNZVDBgNb3BN9KmZLqbGwvahrlrGuBmB4870MAlCWX"
+"aBdtbntFSK7iL8RwqiNyvmBNx38jVvqEqSRYtqefGpjIErGwXsvXsTGXLyIoG0Mjqofmu9rX"
+"rxe5PAAAFvCiulMp6rhJbbedAR4MKY66hvW/t6TpZDThZY3PlcftFK8UK2KqRixaRgR7a3f3"
+"mm/ABP5SDWL6fGI8cS6WBcjxLVVS7aL1I5a7mWMfKlhanXQsJN31T2JQmOHuNrs3uNKEjLy9"
+"3E+N6YS5Yw4Y4Y3/AF5fKpvYKp4tRdwgopcl64kXWuvpiNc4cAO+2m4jU1b9zdO6fEI4MeFY"
+"r38y8dKG6P0PNaWCYTej6zEqQRuIBN+YNV9fhyx1maNpt6AhlsNLEDvNqhedjTTDX0Mq0Kmc"
+"o3IkH2G1GehDHjFwvmBBuOOhqmVQuawU3G46+2iZr/Tt4Vexl5AJYyjbtwZX8wYd/LxqJA4n"
+"41K902kajnXVXtpohnUHcLVuumCOPpeAqBfNHua6niXuxJrCEutgovfga032/lz5UDQ5GX6S"
+"xR7YRtBNr87lRVNhVZ9x1lzRo3CPUuOJGpbvpLntkTtsxo9sSgK7AGzt2X7KJyzFIy+plKSt"
+"5CNmpPZ5SRTLpOLJ1F5FjcRwoqM8gHAv5ti99qmzxgpLyY/JGQzLGSSWuNoFuBNC6spR0uqm"
+"zDsPbW/6n0DIZ3kgCnQlCoBbd+VeQA7TWJ6pi5OPkySuNtyBIo8LVKZVq7rKDYJmg6RHCt1L"
+"u5bZoxHBb34ilzPultb5Rx51P1gYoQCQ8alW7Nt7r+JqvEi+oyUjVgGmYLrwBY7dapvQg+j/"
+"AGj084fSUkYES5R9Rgezgnw1q7qkrNEmJEbyZMoQ2/h3XPwpkiCGJIl4RoFH+UWpbDDfN9aZ"
+"wzR3KqOROlNbsTGDFC2wWLKtwKSfdjIcXHTQuWJF+wCnJlVZlW+hsPZasz1XpnWMzNmkUerG"
+"DaIKwsE5eU066iegv6dkhMLqOObHfGjKOYO/bb40V9v9KGRnf1KYf9NjWKA/mlt/3alh/b4w"
+"Ek6j1cqmOg3fTg3LkDQNbQ68qLyuspIHxcOH08aOISmYWQWbXypaleX9pVI3wVfcXWo1Vtf0"
+"04D+NuysM0kk+T68mpY3J5AUR1eWc5W+Q+Qr+koPAd/fQSsdRrYa28KhKCrWn2Q46H9w5PSx"
+"NEh/SnYbiOKsPzLTXJyc0BcjIchZ1EiOWKl1bgbWB4cqy6qHRdvFyCB8K+mzdJxsqbFxZ1vH"
+"jwAWBtqLKK067f7GdkZKPqDEGFFYRkhi68SR4056MMiHKGV9LLM8g2epIrMVHaG7Kf43QumY"
+"zB44QXHAsd340faw05VT7Fsp9xKj8mZ6p17Og3KY3xl4BijX9jGs9l9Wmms8rSSngCxrW9Wy"
+"MmbIGFjQiZVF33LuG48Brpwpcn2jPInqTSrFIb2jC7gByFxVV4wm4pJLmXHyMdn58isIo7oS"
+"Lsx+bwobHe00JBIYMNT41f8AcGJJh9SeCW29FUEjUaigV8oQ31vp76x7NXuaVeEfRBaSAHup"
+"10lCuGhLiTdqGAtYfw1nOmZCzYSODe6gjx9lH9GyshYWjikQ+cgI/IjjwqOtNykadrShmir1"
+"LZs/KhUMyIRzIv7a6OoTcP0yeVr8O8Vp+Kxl+SvkY1FvlJva3E9lLpM/KUgBb8CbLpbu1oOf"
+"6vLi/wCokYKeCDyj21S6bPwJ9tUD/cnXY8WHHx+nzLJlPOgFiG4G53WpvH1jGkKIoZpX4oBw"
+"Nqw83SAnX0nWxjRd7WAFm4KP21ougSpJ1KSPZfYgYN/Ce+s+z434mtUnTkPlyQ3GOQW7hUwy"
+"sPke3O//AG1bXr0iSlolYWWO1+d7VQ+JOSdsu0dnH8aNvXqaYC3LXMx8SWWOW0iLcFhcW56V"
+"i5czF6hkOASuSwLB34hxpe3dxr6FMoeN0IuCpBvw1FfMc7H9TOxJpXCCZP1GDBlVgOF17bVn"
+"2w+MtrWICMk8TpjzyLK5h9CJyr6eVtv5lA/io/Lx8fJBhu4SIi4BA+YEbALfgaXzdSMEaYmI"
+"NvDbLcWfQMSR312PJXMzk3boyCCyr5gzWF9OVqxi7+TScTGBuX6k/TbCgfIiSNYAEZYTcNcr"
+"qTe5bX+KhpMmfIYySX2XBMZICm1WyCN+pDKHlxZSEXd+YqL7gvZpVfUIJIx6hQgDjcCxHK1a"
+"1rPytq/5egQRhnw4ciYyIdrqPTUanzaW7tDXqXE2Y25209t69T/FXlyz/ERw22heHMH21Ig2"
+"Y8bXFQJNrc+Aru+xLW0OlaCIhCov2i49tekS3411juJIFhwA8BXm3EDTUAAUgIxhmY7RqeA7"
+"auUHygjzcCBVIJDC2vIirHLoSpuhOtuHdQB13AXzC5sND2Vbgy+jm40wNljlQn2Fb0ExPO9W"
+"K6+mNLm+ooYLU+n/AHQhk6ewGoa341kMDp3UZ8CN8fGeWNSy7xwLAknTjWsklOZ9uY07as0U"
+"ZJ7xYGiftkKvR4UT5UaRbjS9nbWiryzW32r0wZGPEyIJtmTEYvLfzDm3CkObJeaQWPrBiFPY"
+"q8BW9+4OmZ0okyIlEjm+0LxCgGvn+ZE4lbf819fGob+WSscMG8+0vSysRMouRPGpRYwdNh1/"
+"Gs71gti5k87EjabEaG7Nw1FC9HM0KblYqb3UgkEVd1FGyYzG5uTrfv5VOmIK5YbnVGfBu+7n"
+"u19tFTtaA+yvDpuQsbMF3kG2ndUcrckQDAgg6g1stDGdQSxY6D2VOJSGG8ac7dlQZSrmx04j"
+"wIqyEebde4tzppNtIh4UhczQuqoikW50w6BHBJ1FIp1UxujghhfW1x20sXWi+mhz1DHEZs28"
+"ajkOfwrrVKxGvuYWs5nT2NDk/b3T/SkmTJeJY1LbCbgk8Pmp99swY8XSmWNg0LMW3X4javE0"
+"BmdPGThZCpLtuosW1AuR41b070+j4UWNkNfEydw9UiwV7ahu48qw76pQ0jbps7JptneoQ4GP"
+"0jKnxZ2LzfymZyPzWOwVjMyD/o2czeqZAC173U7rWPurQfcoxnx4PSYuNupuCAOy3KszNOhg"
+"WFW04knmeAFYLOiOizSTlz7gVnJXTysvl14gcaLiih2yysyxGNVEca6Et/bQ9yEUqDz0bttb"
+"3GuII2W5JJU6gc6Zl7H1fByJZemY8kn854kLntJANdjiCydpI2m19L60N05wnTcMnzAwR3/0"
+"jtpT1P7qk6dkGM4LFgSyPIwCv/eG1apPAmh/kG2SkSi50v4CrZMcsPIQDaxArIY/WPunqkwk"
+"giXHxn4uEG3TXRn41w533g2M8gWPapsSFTc3eNaJXkIBfuTqLMz4wYkI+0Em4JXjahIZyYNt"
+"rEWueI2jXb4UHkxZ/qepmxutybXTYtz2aCica22x4HT3025BSsCSd5WPqS6s2uvZVkTIxAJt"
+"cWIquX1DdNpKqdNOFqsRolWMFRvA8/K1uyoYB/Q8U5nUcHEI8rS7m7dtwT/w19XEX/U+r2pt"
+"+N6xH2RhRS9SbICt/wBHHs3HgWcWHwvW8pp4A9Xjwr1coA4QbXHHnVbPsQl2A7AKskNkOttD"
+"rQO2ILfazgC1yNKqqkluD5795Lu63KxPFVIHspE23YNdbcPbWk+640mzzK7GMs2xTa6iwvcj"
+"jWclieO275dQGGoOumtHZ9wV0RoftTKb9TGIJQD1Ax79LVo+m4yJPPcXbduW55MO41kPtqRo"
+"8709bSroOVwb/hW4RREyzHssxsDpU9Tjs9y+xcur2CtSCpW1+HGvQosbs4Q7jxNr1JX0LKQw"
+"tccjVkU8buwK20+a9dLeNDmqvUqnmTYbnbt9hod5/UhV4tQwtZtDRGS6qLmyqOLEE0JI423t"
+"bmq9g/ibsqL24pQa9VOTc5Qm6liB2aUyyRy2JOxiFt2WrPxZ06ZM82POyj1CEZGZfKOHtpz1"
+"rLhVfTmZ7OLOIyA+3tBYGs2kawD01O/mGuD82vLnWDbeW8s1cLCUQO4vuXrcY8uWx/xhX/4h"
+"Vo+8eugXEyN4xr+ykVyRqbeNQYsdF/H91KWEGiX7561dgRCQOBKH9jivN96dbJuGhA7BH/8A"
+"cazG1xuAN7i3dxqRBuN3GnLA0Mn3p1xkIEka30uIx+0mksDwekUl33JJhMXlCycjbgapJsNK"
+"I6dsbedoZo2DEHncWG3tN6m7mvsEFsKElk2I7Ag7kFrbtWb2cDamDtDiQZM8yFJJbRRbSBIU"
+"5lSTbla/GorHHYgkELpuUaG4uBrY1HqcQkV8w7W9QhBGfm3LxYD/AAis5Klx7CkZMjTJPIoC"
+"IAscYvtCgjRb1t8sdP6pC0bt6REaOhJ2jcL39mtYo4zNlssikRq3mJ0G0akU0zpnWHBkiY7J"
+"IlVh386010JXqBSdPmXKWB7K5YBCeB83bXqJOTC6KXnJcOVCEXIC3ZTpw10416lzzpb+AQaP"
+"/wCLdNjjYPCvqAgK25rAc+dJ+o9NixVBTGhcEX13Gx/1Vs5W3xhzoSASPdWb6plRCTYV8yhw"
+"dDbzbrfGsF29nJqZNuFOMwZiSO+noxoCL3Xd2d5oJWtr7NKaEb47202k/A0o00HOuilm5nYx"
+"vVKINV9p9Kx41Xree10jcrjxCxMjjQsb8hWl6yOj52Huy4jIy32rHZZLjUebkKq+2IQ3QcK4"
+"BO2Ui9jqZOIoyWNUBYICGPmBFwQwtTfYldVBUmsnzWbAlDsfTKx3Oy7Le3frVAxZlksq37AC"
+"D+Brb9XwYhCzlF1100+YLfhWejijXeUUXXcQeYteqcRJEbGt+2n+t+10iA1TdF42NwfjTP7f"
+"wZun4b40rblWQmLuVrG3vpL9g5MX9JlhLjfHKSV7FYC3xFafEkMsAkP5i1h3XIqVqaNvjpuS"
+"kBYpbkwv4cCKDyuhdOy9ZoRuAKh18pte4osuTMYxyIb2GrqFDkgzb/aSh7wTAR/3h5h7qtxu"
+"i9K3/TkfUzKCZHubL2U+PuqEUEUO701C7zdj2mnBXJiHqHQ8Mxt6P6LKrkW+UlFB5996yHWs"
+"B1SWF7etBZgV1DLa/uINbzrkyQ4xsf1HVkUf47XJ9grFyztPMN/JRH4qvlHwpNkYkzD7iBbi"
+"NPZVkQKi1WuBFc7QLH41C4I3cB8K1onyQrNQy1RpWg6P9vZjSx5c7HGRfMi8ZGHhyFDfbOPH"
+"JmPlSrvjw09TaeBc+VL/AI0Pn9bzcmZgZWVNxsFJAtfurqTMoNzEiQqY1JJbW5NybcKYxjGz"
+"MX0JkV1tZ42r5R9RMrhxI24cGub0fi9f6hA4JkZhwIJN/YajsorLwyquBh9wdHxsXLMMMr/T"
+"23emW0XX5b9lZnMZWm2x/Ig2i3D2U/6h6OfjDJXKebImYRxQaA725OTyqn/4vIZJVWYbIIPX"
+"d2Bu3z+VQP8ABWVelpzJpbsTUJCaN2XT4VNYldCVuNpuVHO3Gij0fOVkV0srqr7gRba4JB+F"
+"Mcro0eBjpIHLZGpdNNoVfnPgLhfGteCahozThmu6f6n9KxPUsH9FTYcLW8vwpd1ZVy/RgMbS"
+"qpO8CNm4jgCOGtH9Gl9bpWO7asimI/8A6zt/Cs91fNnjy5sZpJdLekqvsQjnfhXJGX6G2w5T"
+"dF9OuqJG8cccXc/lBOvKxq4oEwSt9A1gTSbp0on6mbOpUiJlVW3W9EgW/wB6tBkxn0XBPlF+"
+"HdSGK88B8YAkKCPM3lva396sypRA2wkqGspPH9laOdxs3MCTbyis5kl5BJfQhwbfspoTAJox"
+"acig4gLlmFwCO7hTGQfzgNLA/BaBHyoBqCdRzpLMjstPY232n13p+CZcHKtC8sjSCe42nkFb"
+"s4VtVkRxdGDA6ixvXxpyTky7v4iLc6+t4PpP0/GlK33Qob21ttFUSRlzplneGGISbLXJa3Ed"
+"lqF/quY0RIxlNw2m4305cK7Fs+rySilBuXjzui62NQO1Vfs9RiLdmi1z373W7rCw9zavWmk/"
+"QU5v3fnQs0RxEU8DctfWicDrpzOmZM+UUieJrKgYDcLBhbce2s/10/qM38bX7+JrmHhYuRh7"
+"isnrmQqHA3RqtlsDa5vW/V2cmsJGfZSEwPrDmfH3hgSJAxN+0EUocst2Gug3L+U343pz1CAx"
+"QSxH5lsfcaTHeLi/cPxq+zUzroOPtd8ZcyRnbYQoEKnXn5q18mZGsaFAXX1FRu7cbCvncDFJ"
+"FksFdG3LbhpyNfRIsJzHdH0axswvwqa1s3K2L/JVLjYtUlQwZtFJAuRwquCVfXkjMgMuhVb6"
+"2q9YdtyeZufGqDFfKVtq6qRf82luFdtdM+Djt9zjQNQB0If231oDJxZwbq4dCSxDcSeVyOIH"
+"ZRy+WwHAdlqhOLra17dvCo4pvJStZLDMhnYZnySjG7udXJA058eVA5uMMdFDqyyEmxbgyDy3"
+"A8RTbOdVyWRjtLgBX4d2238JpRnzTS+mJjdo9yKf7gsQCe69c3av8zVcKq/ob9bf403mWCa2"
+"t+6q3uO7xP7hUr1W7EcOfOoNCtr71F9CCT7PGrVIGnLwqhyDItzwBqYa/D8aGBae6iOlM0ck"
+"pBQbl2lnAIW4IuAeetDctdaN6UryJkRqtxYMWI0ULqam6mrAbdRaGUKYpDJjLt3K1kIa/BVH"
+"Z20E8rN+lIpbcRd38xBIB07DUcp5ItASzFgAeRFSEnpz+mPOr7fTbkb67rnn5ayScFE8rFCg"
+"qhJaZidg8xUAG3m58asmcp02CAnaSkoIK33EEWN+XCuOpuhswDHajNrqTYWI48zXsqCSPLSN"
+"H3oSEfbqqhhtPt406tzqJoWrHGM4pMSF3WVxYm5F07vmr1N8vAikaBVsFWQBhcb9i6X0/bXq"
+"vktZ2iJK4vSFrJtZhoSRfkb+NqzvUwQWZQNxRr6dlaCa8gIB043HfWe6rcLI44bND7RXHMXw"
+"b1XxEBXbE99fIb+6kTG5sOVP5f5Ml+Ow/hSBV83jXX05k5+5aH1D7fUr0zpsYtb6cuTrxLqf"
+"20ZmMqLFf+I7vAKxqnoUf/pvTrcRi27+K17rCMxx0AuDKQR2j03qO372yqbIT9acmN0B7Rfs"
+"AFIIdYiO1XJP+Umn3WyqhjbjwHiLVn4g3oSEcRFJ/wAJp1s3UVqpM59r5csXVYoodEyj6Lj4"
+"qfYa+n4n/l0023FyvYSbmvkv2+SnVcJ7/JPHp/mFfXybAnsreNzPk+PH1BWmCTTudCiAeN72"
+"oomwvSaXKQy5KahtquFIsSqNY06qOtzPuxCqXPyFdrxlVv5fKG/bVLdXaPVwbd0VvjuppkRK"
+"wuRSTqcYED24gGpcpwxMW9W6uM2RQukcYub6a0gicmQE8SL+81PNYiP0xo0hC+w6mqUJ9cj+"
+"Gw+FMBdOwE0i9jMNfGobhbje9RzUtmzi9vOfjrVca7XHmv3VvW2goNP00nE+358oaGaYrw4h"
+"V2j4k1nb61oc/ITH+3cXC09SVFkYcCNzGQN7eFZxTXS3CRHktvcV6og20qVPURKKV4pFkQ2d"
+"CGU9hFbDD6pj5fS8+UAJkegwkTuWPaLd16xtWxSyIHCGwdSj96niKcAavFmjKwFwWEMMJsBf"
+"zCJCg8dz6VVnpIu6WcArjqrSqNRuB/QxVP5vMdz1z7XkjyJJzJ88AhKLfiRH6e72babZUO5F"
+"W3mW8g0B/VbQNr/CCT40JiYN9qZDN0/Ihb54ZATf++P3ilX3LYzo5GhGhtrTDpW7EzXwgoWG"
+"WFvTF7sXjO4sx7WuaX9XkEyPCbF4muCT77Vydii79cm9XNUAdMyvps3GkNhGrbWYfwv5Tu8L"
+"1v5VJje/YbV8zclV08fdX03GmXKw4cheE0av7WUGsyhBPezXNrAnSs45uJD/ABEfjTrrbmFH"
+"ANjpb20iBvEb9op7CKGfzydhLE+wUImTGnlMSsL3JI19hvRFtJD2hj8KX211pVhyO2xeswMp"
+"k1uTf319i6Syv0vEZflMEdv9Ir4utr19f+2XL9AwWOv6QHuuv7KokslX/qJG/iCjv0FCemuo"
+"5bmJ8ASaNmsZn14WHwoWbyxE89ePZXD2/wD0t7nV1/avYyvXt2hKgJcKoOp5m/wp59nKD0ma"
+"63Pqk68/KtI+vSDeQza2YDTibr+6n/2WC3SJSeDSsB/pWt+nYy7QD7jiSQjaigWbdtFzbh5m"
+"vWNCx7d4a55KbVu+usIunAfmnfafCO5/GvnpK3BD2Pf/AGV09miMa7k9g3WWx01I7q+o438p"
+"b9gr5piYwkmjZ23RXG+3ceFfSsc/pJt4WHuqun+76Gfa9Cxx2Gl0hH9RhUHXa5+FHOaF2qc0"
+"SEahCo7rkV0V0MmGRgjn7K5OPKefdXk7OVem+Q2pf3BsYnrLKnUA7MCBe6X1Ite9AZuTBkiJ"
+"owUcA+oCNLmw09gq/r7Bc/1ABcC57+ylUjEaWFzXLd/5Lv1ak6aL4VW2p0Nu15VCQ615CBav"
+"ScagsHc2lHhpVik9utUuf1QO6rBpQwReL2pp0RJVDSekWjk3DeG2gW4E91zShW0NaPpWTt6Q"
+"LEBRuS7cA993xGlTZwvcNyOV6Ym9Jz8kYTzWPm5kcOJFV4cORjzQvARIs8hMcUi3UXJUO3gO"
+"fKix0zGnxhM2QVWRfNZN5V+OrX8ovpauRSR48rjHkZWhVRGSQ24ANcgW7eHdUPRwWo3Cs4S4"
+"yKEYyRzkoykFvTI85NzwGh9lVF45Xx5AWAX9NURLhCTZmPMkcB40Lj5GdkzuAzSjIUiddANt"
+"id0YvYGw40yedsJkiD7IVB2oANyuCBudWB7bjWo0/wCCsNPBa0WWFM0d0iYgPIdH23HaNd3Z"
+"Xqgz7coO+QrlnBRdfTWxA0J5DUmvVM19Y8hwc6I0GQL/ADX2rc7QbXuOdIepj/piqi1wo8Ba"
+"9q0GRbgNb2HvpD1hQBInZr8awUybL7TOyu1pNPKE0H40qVWNgFtf303lU+nKePlsaUjQlQbj"
+"lXb07nP3ao+ndLukOCg024aED/EVonMQs8R/gct7CCP21ThC2RAmt0wodPb/AGURlkDdfSxB"
+"rPv1f0H16ozXWzuidhx4X99IIL+jLb/w5Ln/ACtTvrRYwNdjY6nlzFZ8SBYci/ONlAHa1HXo"
+"vcd9WCYTlMyB/wCGVWPsYV9jNiLdtfGIjZgewg+6vsiuvorIxsu0G/da9dWxgIOpYz4ubFkB"
+"rwv+k4Nz/MZdbmtHQGVGcyK0aKYmFyzjUjkV7Kn03JaeArL/ADoT6cneRwb21l1wnZLfKGET"
+"ny1murZO3encyn/Nw/Gi+u9YyMLISGJVK7dzbr63uKz+bnDLu23axN2HgKdl8pJbFGUS2ZEo"
+"4WJ8LCqsQmSRpDpuJoiRT6suRptjVRfvfgB7qvg6bkwYkc8iFI5LBC2hYnsFAjP9SNuoT628"
+"w/AVTEGeRUX5mIUeJNhRE/pT5kjMp/ULf2GqISI5A0b7WUgqeYKm4IrRPQY7+4sQ4ue8TSX2"
+"KgjU3+QKFt7xSYcSK0MOBN1np+R1HJnZ5cZkQkgfIRcsx52vSTMx0x5fI+9ORtatn2JuFsLg"
+"4krqSm9RU3rvA3q09yCwVYANhNVKRTLp3S83qTCLFjLXI3PwVR2k0WukvBVahP2tP6PWUhYD"
+"blXhueTcV/Ctv1DERMdrSMrEfMtgRQvSug9P6NH6z2mzOJmI+W/8A5VdkQ5mbGwgAUHgzmw+"
+"Armt3XeKSb066L5WiPXcy3SUbI+4IRNLI/pxyOgLGxdfLr7DQXUycXqkrWuLm4PfTROl9Q6X"
+"1vCyZFV4STCzobgGQMLG9jzqv7qxSkkc9rB7g37RS+U/LWCW0/t09DPudwJPM30re/as7S9C"
+"x9xBMe6K/cjWHwrBhb+2tf8AY8rSdNyIf/Cn8v8AnUfupALfukbclFHC1yPbSTcApHEgE02+"
+"5Z1l6tMqG4i8ntHH40nI0bX8ppvQW5WTZHI5LY+0UDZORN6Ot+m/b/ZQKjT8amu5V9jwQXuD"
+"8K+nfYmV63QliPHHkaMeB84/4q+aJGbdlaz7R6icDpuW1r7Z0P8AqRh/3atKcEGxlYNNLbgC"
+"Bp3AUNlsBE4sOB48a9iZQyoGyF0V3Jt7bmvZQ48ybm/dbWuDtX+S3/sdVPtXsZL7gVfV33Lk"
+"kljwAvyFaP7KYDojXNh6zge5az/3EsnqMq/KDu7vNTv7QDHoOQF+ZZXK+IVa6ulYRl2vLB/u"
+"UzCeKNlAhTeVNxqWNzp7qwjwyCVkCX2Gw58DX0nrEUfUOmJlx6tGN4t/vD2WrGSgRzE7SwYA"
+"24V02+1GFdQXFVnmij2MfMAzAEAa9lfR8cbYVHYBWIhmjd41CkAEWNyB+NbPGcekATqBT6Ev"
+"kR27Fj8aHJtPt4nTX21YZCSbeyhQQJ3UnzkAjvHdXTGDEYRnXtrsgurDuqqN7GzaX51dIqxw"
+"s7NoBfTnUPDKR88z2hOdlyOOLEKDccDt0t2Urc3mIHAC4rXPmQbSxxYWdTtN0Bv43pH1DDnM"
+"xfYoK/NsAC/CuOz+T92diXxXsLAdfCvSV7UsQR/212UXTSkJ6AjEetVl2qlzaSrkNNgixTp3"
+"2rUdIwRP0qE72X1bAMOCkNoayvhW06Niyz9Iw0uXjCMfTVtt/O1wT2VFtBrUcWEKXDpJERub"
+"9O26UrqWUX7Lms/PAqTQz+kUtqdhB48F0JNhe9OJZch41hxCvqINjobbkbiGs3zXqqHFmjxB"
+"JnKvqPdY7+YqePnU6c6xq0vdlcdyvDgWKNZ1kEWQu6NlNtb3A4cKjj45GQJ7SZMMpZZkfUws"
+"NTu7P8XOi8SFgjNZCr/k46DTs5GipcRJAjbxGQTHIiXVZE0IGv5hyqonUegBkJ+pDG5tIW3p"
+"LbytELfhxr1EmNDGEAJCEi5FiN1wSPZXqjEaY/qE41Yzy4wykkXNtBw5WpF1UWjA2+Z9B4AV"
+"oss2XTs4Vn+rv+gbakCwt2HSsYi8G1XNTP5FhFL3KR8L0qxliM6LISsbMN5A1Ck6kU1kQOjI"
+"xsrDaWGtBHHxY3HpyOzAjQjv7a6/26lMw7tUfUYPpBkCKKxniiRWJ+b0/wAt6pziTvA0sOP4"
+"VTjadbzHI1+nx9f/APSrMsMxc20Nh8ay/cvb1H1LP0M51tVGOzXJItt1v/twrNlR6MzDgqE+"
+"+tL10SJjsxUchr48fjWbbTFn/wDxkD/UKOp4+pXZq/YAQEm3bX17pkiz9KxZHsQ8KFr8PlF6"
+"+RxEFgSbV9T6ajQ/b2NFJ5WMKJY8bvZR+NdRzoYRFZIkYCyFQQvcRQGZLF07KTKLAJINkyDj"
+"t5OB/dpkoVFCLwUAD2Vkfuud06igFxtiG0+JN7VLSw90Bf8AdaB2xslLNG6ldw1B/MNazhbb"
+"VsvUMk4QxhaSEOJFUk7kNiGAP8JvS1Z55ztVQt+PE2ppNkPUPwpIBkB8iL14EcO0d7biqlV1"
+"7i16J+4OsHMBljHppGm2JD/EfCgkARAg1PEntNCdRYhFUC9rtx5/7Gk0pQ6zAlRyGDcwbE1e"
+"cba5JNgCdeWlUkoDYai4LCiWckafJxAPI+NaLVFDPAzjH03MxUa3qvExHaq7rj8KEyo/UjNu"
+"PKhkbY2730wKOhMbqVccVPHWm1lvyNNOqXgUxtcW5ijcLDyM6ZcfGjMsr8FH4nuqeB0eXO6r"
+"HhReUzG+61wqgXY+yvpGJg9M+3cGRox/LUNLKfncnQa/sq/ycVGrJ4SxDH9odO6biHI6nPun"
+"tdEGiBrfL2tVuJ91KcILBjrCg0W1lFhp5gOdZ3r33DN1HKYwAhRouugApPGzhGEjXUaBAbeZ"
+"uB77VHF21KdlXCyfR+i5EvUQ2ZId0KMUTsLL8x8BTXEzoslnEZukbmPd2sONqR9OeSDoOJj4"
+"9hNJEqxm9gGe7km3dUehpJjfqyXWIXWFDxd3IMkxHK/BR2VqutKr/kYPtbtLYX905UaQLjKA"
+"Wk48iAO8c6yfWM7PzFikmQnGjUCIglraWJckXvpR33Dn+t1KeO9xGVVe7yi499dx5PS6fA6g"
+"y+oNV7SSbj2WrPsUVSNKat+TOCTsFr1q/stpBi9R9KwYtHsY8nIYUjy8YSA5EUXopYllPC4N"
+"rCnf2mywdOzZXNl9QXPDRU4/GsjQQdTjxYckrDO2TMCTLIQFTceSjWhY7kSE/wAJNVsw3WXm"
+"bDwvV5V4/WjfRlVlI7xVMW5FLEN2cPhVZ6dkAXuljrxsalDYA+NFr1GFrRkbSBYHlcd9Z11Z"
+"dtgI42UqcFI7QQaN6UzDFyon0JeJ1/y+oD+NRlyEK2W169hkF/Z+2taaoztobboPm6aVGhDN"
+"c95orKHla5to3DwNA/bzAYLjj+ofiFo3NBsVJI8pPZyrg7V/kt/7HVTRexlPuILdbcQb6n2U"
+"++xz/wClTIRp6xHvVaQ9dRAqPqzmwJPK4DU/+xv/AG6fs9b/ALq109WiMezcaTdNRcd4MYiJ"
+"GBAB1C39tY/rfTD0r0jJIriTcAeB0tbS9bvIkx8aJ8iY7UQXJrAyZiZedPO6+SRmYK/nsL6c"
+"a6Jbq/BluhdHkRvKiKQTewUHU61r4bRoqi97DSk2GuK067Y03X8rbQLU/VNmp1vxtxFaftoa"
+"szP9xiEcLaXGhoZo3mBBNirbkP8ACaJfTUMSKpv52vaxtXS/tMK6l6HcB6gtYa1RnytLAYYH"
+"uo1db6kdgq23qWuSFHEVI7I0ZraAaE0o08hL0MpLIfQlYldl2+U3IbdsXUc70ThKclX2E2YX"
+"OlxwI1A8b3ojB6VAC+Q7BhktuMUi3CkNpY0cMNIpHkDqEc3sABYWsEuK8vsfyt7s9Cv2r2ML"
+"OpjndSL2NqpmbatuZozO2pkyWOgYgWPZS+Q7jVokGdryEirUJ299U6bz2VfGQBeqYI6Cw4it"
+"j9r5TJ03TYFjmAD6bruQSLmsisgJsRpTzouL68QVUkf0pRIArBU0A+YHiayv9rKWpoxJhtOx"
+"jkSMhlaRW8jecaH92tTMyensmuSQwiK2YBr/AMQ/CoSYhjlf041XysSTYt5LuqnxJNhVmP6u"
+"Jhj1XaRtvE23bmOtgaiFgsCM029cYArpufgfmYlRZdOJq/Hmmc/Sypsdbm7MCw2m+7UcL1Iv"
+"NJIiHYqFl9R9ikso/NpawXxrzuglusilUK7t4BYg3GnOk14Bg65peU48gI2jap3aFeR4fGvV"
+"cIYXYBgCI7qGFt1jzb9lepQTnQ0GQRbTQWsBWd6lPAYZAw3SXAUXIXceG49lP8j5bDidB8az"
+"PXl/QDRptXdZ9NdeG721hVN2k3WKwKpCRA5PZQmFEs+VHFISFY6299GSgvAQuulA4BJzoEBs"
+"TIgv3E2rs/b6W9zDu+5H0dAF6nkldCYoQfZuqeTL+iRzsfwqI/8Ac8o9ixKPca5ORY29ntrm"
+"/cN/kefBp1JQjPfcMjPAFHE6tfuFZzazY0uurLYc/wAy9lP/ALglUxKpbzN2Dw/fSSJikTtc"
+"iy2v/mFadKwvcXZq/YGxMEs6yM+1QRfcpGnO16+pbxnY2LNjEegzJLdtDsXXhXy4NJLIoXzM"
+"xAUHtPjX07oBv0XC/wDxL8K6rJHOgqbzCwUt4Af2Ui+4cF58JZFVvUgJbz/MUPEC1+HGnz37"
+"vG9v2VmOtQJjTS5Chn3+bXI9MBjyC21rNqWWtDNSypALsQCeFU4c/lIZNl9dw4Nehcouz+o4"
+"37jbU628a8Y8gIm66Ky3QWtdeFxVJOCeNdw6aYRC/NtAKDnJKozHzA6a91dAZ23ObkaDurk4"
+"uqjS9yaaUCAGjVwNvlI4nuqYDhOB2jS57anFFqQ9yNp9hq6XauOEAvcg38BtpqZQnoDd1O8q"
+"T1IMCV4yJ54i00pYneEPprYctFpIFN7n3UxTIZlj9Ql1iQRpbkoN9Paa3SxZehKtDRt+h9Gi"
+"6asPUJ2/XdNrKbbU9S22lv3T1Iy4s+OG19RGIGnlAKlT4MKIn6p1DqXTyMLDlGJtEZYgbnAN"
+"tCSPbas112DJWTHaWKSNniG4OBuaRS25vKTfSsq0s7I1d6KryuWwpZrCw0vVI1arJBZbniOV"
+"VRcb1rd5MFobLoPU/qDh4LjzpvVm5WCkqfdpTNXMnVCn/Iw0uB2nt/ZWR6JlfT9RilIJCiQt"
+"bkNja+ytSXSPo+RngHfkQ7lvxC28v43rSrkysoZkJJmkyZJGNyzFj7TetFKfo+nYwtrtW47C"
+"/wD21msaxyBu1XcAQewm1avrW1AQRfaw2qO42UVzdjlnTRYF2XO0kM0MYvHEPO9tNzMNK70/"
+"Hzc3pkuHiOkatMGl3EgkFAFGg4aa0QpE2FNHxhjsciYcGmY32L3KulUfa8rHrBhU2SSB9y96"
+"EMDWZZR07oLM5ycwqYYz5UQ3Lsp1v2Ac6H6tEUypXIsJgXHLRv7b03x8hsPq2ThOPK7s8V+A"
+"Ju1vbel/Xn3OSz75QrBiPlUCxC37daYhVCV59tVuIwSW26nka7G2ulQeIk6DUmpWrKeiOFQf"
+"kb2Ci8AMhJN+VDBQNdtraUXj6XOmttK0rqiHobb7eF8EntfjR2aGYEm3yNbW3KgPtlv/AExz"
+"z9SxPu4UblEEH83kNr8OBrh7sdtl5sdPXmq9jK9daS6JoFt5gBxOnM1ovskr/TpgP/F/7q0g"
+"616nlL2VNugHFjf8KafYkxIy4D2o4+KmturYz7Nwv7uizXhjMZ/6ZQS6jiW76yDIwgewIJU7"
+"Tw1r6bkQrNEUYAg8jWF6rHJiM+AwvAbyY7EC+1tdp7r3rso5rBg9RHjTZsEiSC428zwIrb47"
+"h4klVrhwCDy1r58jbztEak8q2XQZN2AsbLseLysvxp/t21Z1e+Se9TVNbDJgNSvlPPsNCyFV"
+"ck6GworhpS7MJDqF4m4rr2ZzLUYDXaOQFU5bHYEXnqfCrFNkAPIVVKNGdr3tew5CnUNzmPu9"
+"ESEgWXyC3MCyHs43ofMdmZBEwXYwYhQflA1ub2tersSaVoWCjc6kWDaL5TtvpUMuZllljjx2"
+"JRDIXA8rDbqgtxY3rxG5tZerPSSwvYxOcQ2VIBqu4299L5rKdoOppj1HeMhmaNoiQDscWPCl"
+"bi53H210V0I3LplUYeOwGu5wzduo0qtOQHE1KRy2HGp5O1h412BCup402KuhP5dALt21pftQ"
+"OY5FAuzSWF+Go0PstWdLC1wLGn/2xmenBJuW9t5U25myan/NWd0+LKTyPc59jFgzKqgM7L5h"
+"sJ83tqjHeaWS/qXupeJWOhN9b+HZXHhSWMu8pihZtslx5dgJItzLV5p1CzGP+VCoQH5mudL2"
+"4XNQpbKLJPX1WNyTYXtYm/DS1qsEUAjjYkPIx272UX3e0crUEmVJJJzVl1XSwvU+p5bxYoQh"
+"TuEjiNj823XS3frVDDVfFAYs6iQ+YG3zHgrX22sK9WSPVMg5TFZrAqkLMtwpjUcxXqfEnkj6"
+"HJ/MUE86znWWaVnDyWUHdttr5joP21o5haRTzuLe6sz1NrEta5YjU93H4GuKqhnTqhdJpCwH"
+"Icaq6LjQZPV4YppvQTeCGIvuZTdUHjUpWBia/ZrQBnbHnWeO26Mq6gjS6ncL119GjMO7VH02"
+"JQ+dlMwNiUtcWOgPbXMpkjU6DQX17jQPR8uXLaeeQ6ybGtyG4HhReSR6TX1sDXN3v5tRuX1r"
+"Qzf3GymO47eQt/D+6kWPdhKvLYf9vhTzrnmhJPP91JMLjIR/A34Vr0aL3F26v2KIHEeRG55O"
+"hH+oV9J+3JFbo+MoI3RhlK8xZ2/ZWAxYk/SnsGNwmwi/KvpPTsU4+NGhUK20GSwFyx1Ndb1O"
+"daSEsL0o6tDHLAwkFwLkac6cbSABx76WdSH6bXHKs7al10PnWUg9MnsatN90rHH0zpsYQB9g"
+"1tYgKi6e81ns1SolXhtfn409+7mc/QoxuBDe/aTa5+FUtRMzYterVZPQuU3OrcSRt8LcaoJt"
+"XRjzuWIQsDqpBA/Gq3J2ItDPIQIhtjfQbdAx48+yq5EKkqdWXjaiZWZEWKVNgTW24HnfgL0N"
+"MC0a2a4OpFrWvyqqrKh5JbKCQTx9lGYpFgKDMehq7HkYEDT2ca263Fsmd1NcDqDq2figJBkM"
+"qroqcV/0mq8/qs+cuyXbE8llmnAJPpj8oHIdtuNB3vVcjBVux0rVuuuDNJgWVtVCF1BPZVEd"
+"72q7JDtIAgLDsUXo6GGH6cOi71NyX0HI6d2tct7TZnRSvxLvt/p8mbnLGbrEFJnbsj4Ef5uF"
+"az7gZY+lZAFgCgVR2ai1V/bGNhw9NbLUW9ZvPuIJ8htbTle9L/ufKaeHanliDAWHPib1r14r"
+"JjbN/Yzqq6+cW8rBzc2+U34Vo89JusdR+nxL+mpvK44KD2mhoegM0QachS632AXNjqDe4rRw"
+"YLwwo4ypY0Kq3pqqKt7DjprXNaNUzpqA9aTH6d0dcSHyoSFTtYjzMT3mk32oSPuGI3teKUH/"
+"AE3ov7nlEsuORMGiVSVU3uW3bW5UF9sf+/Y/97eunYUNTGBsK+6w0XVVkK2imQNFIBYhk8rC"
+"/cbUD1KaGXBRkZTJc+pbjdgLn4Ub94dawssjAgiLPiyH/qCbC9trBR7KzJlYowOmn41WwicR"
+"F9avuA7Br7e4d1BRm5tTvE6Lm5cIyItoRydpYnlp2doqYyOcAgCbhfW3aO2rNqIAVIJP+2tF"
+"ZPSMyCIswUhRckMDx7uNLSzg+bUga+FOv3IG8M2v2q2/CkTskU+/jTbNVU0PAr/ZSP7MYyY2"
+"QRraRbj/ACmnucB5STZitrey9c3dVc7v1NetuK+xk+turRoCCSq7t3Acbftq77MmMfVgp4TR"
+"sh8R5h+FVdZTbgoTqzaX8DS3pWacHNiyCGKxMrEDmBx+FV1bC7Nz6nWc+6sENgpMBdonIJ57"
+"ZBr8abnqeJ6SSI29ZVDJbmp4ULnucvCdZl9HHcDc7kLbW4NzXVWU0zFnzzFglRtzIGRTe2gO"
+"moOtaLozbGtt2h18w04jnpxuDSbJRos4hMhJMePb+rbRlAsSefwpp0fJh+kdsj9NVZiTft1u"
+"OdT1Nrur/Bh2JOjHMkq9h8aW5nzxsNdx2j3iijlYUcInMxWIi4Lnbp2+YcKTzdVxJ82FI23R"
+"q2+SQXC8ufCvRlcXk40nOmg9Z0WwJAPZeqMydIYmZiCSpsONEhYXQFWCKxsrAgXJ0telc0Ec"
+"mcU3GR1sX3AsAlwLcu2k71rVtvRBWrdko3C8PbJYwsA0qawm2j6HjbxNV9SiEcZlWRlKLcjh"
+"vJ4c/fV2MSzhRCW8wDcjGbXuLcRVsipkBYpQGYMGD2GtuAN/dXjxFpxqeltg+f8AVJmlzJLk"
+"tbaLnXlQRSwovqCyx9RyQy2HqG3+EaLw7qp3KRqQK3RkDot2Fx8osKvNhoKqBAkYX0sNatBU"
+"amhjRODHkyZVgQeZzx7BWyw+mw4eCIYbOwUO5Iv5ie7upB0WK2PkZwteIWUEjgdOHHvp0cpP"
+"TxtzNGFBuQR5ra317Le6s7tzAJaEMp3eED1GDs1gNmhsbabrVVNHNFEi32ytcgFbpa+l7EX4"
+"URn2KauZMpEWUomgjjtu3HdwY0KZndmjZ9ztZgW0J23v3UljL0KbOCbPQ6CBtbgedf2mgeoR"
+"dWzWR3WJRGpVVVjzN+dGsGBIPEVJfU2kgGw7K0haizoKIOl56s7OEB9Nth3cWK28a9TBMlSH"
+"lsbltvsAr1EZkNoNplSFbmxOtvgazGeS87XOigKPEHWneTmY/G7eXlsc6+b+7WfzMlnlZoYJ"
+"juJs/pOdtyGuBtHhXJWrb0OmUkAzeWGTl5TagJFBiLX1J4eyj2jyJSUXGm2tpcxtw91V/wBJ"
+"6jJ5Vx5SO9SPxro6UknLSMO6bNQmzW/a534shvawj/4TTWZbgi/HSlH2zj5ONjyJkRmNv0wN"
+"3OwN6bTk2OtrGuT9x/8ARtM26Z4qTKdfRQhfnYWuddSKU4RvLb+634U264P0yRxvb8RSzpMT"
+"z5ixJbcVbjwsBXR04SbejI7ZbaWrQ2+18E5XUIQwukJMj9ll+Ue+voAFqzP2xhSYWTL6pW8i"
+"ALY9960966JTyjnhrDOHt5Uu6gLxseVHudO7toXMF42AGlqixVTAdXi9MvItvOb7eeh40y+7"
+"Zt8mJGR5kgDFuTF+z3UqzQzPKrG5vtv7bU7+9ECHCHZGy/6dtUn8gehlSdaYwuFVIuAtYe2l"
+"bMAb8e6vQ5LRT2k3SIt9oUag+NUSOJOiwzN6ks1uNwB+NLpujdVynK4EUkuKpukhURq1uYJN"
+"M4JcrLWyYkhgY7WYkDQ6N8Kv6vNnzQ/T4uLOtrqvk02jgNGNS7Lz/Eri/E+xmJIMjFJjm8rq"
+"fMCb/GrUxw8MeQhu5LCw/umu/wBA61LoMZlBPFyFHxNMMT7f61FD6eyOwJYEvwv4ULsSebIH"
+"12axUDSQAWfQ9lUZMik7QL0xz+nZHTsdpeoqjRy+WNYyd3qDUG9tAKz3ruTbjrxq/wAnLMyi"
+"HTjiB50Hpz9SkyIo9WijDAXtck20o3/47n4YkGR+lhCxdwbpt7dLnSufacTHHypo13TB1UsD"
+"rtte1r61d1iPq+WCsOHPsNt1wtj4eaodvl9yRaT4xx+pDpOTizY+R03IcBDIZMW5IS4uCL6c"
+"bCqs3HC7scEk6FE3Xtb5r3pPsyfqHxRAzTRmzKupBHbRGRj5YlL5avFI58zNa/tsa6fyU4mH"
+"47cpNdLlY6qiySiNVjQbiCb+XThSbqHWUhmcxZEhZrAhiSGtY30OnDSuZ+IXb0zl488IUKv6"
+"vpsLDnpalZ6dZrHIxh3mUt+C1zK9djfhYpzOoSTyhlZpLCwBsbC96PwcpunQr1BGUZJQiLdY"
+"2drpcDuoVOmQqQr9QhF+aLI1vcoobqMIT00x5zlKoNyEKBTfgA3GhNSJpwQaRmcu5DMzFm9u"
+"vKq5WuALaX0NRjDgWZGv2jSrVVG0kjO3kAbG/uNU2oEpK4yOJNbrosrf0OHbe3mt/rasX6OP"
+"bSI372P7qISeWNBGhkVBwVXew9gIqRmiyOjdSdvKqqTcXZhpz76UdVhmwFHrTxSS8oVO5wD3"
+"AcPE0FPl5bxkCWUk/wB9j+LUGFnkJurMTzI1qpE0bn7Gn3YeQZCGJlQGwtbStJmWubjXZqe4"
+"isz9lRunT5FdShaa9jpwFaLKYsrMx/Ibj2Vx9l/lZPydFK4r7Gb61b6QcrKW17xwHurOMshB"
+"2EC/G9zWg66iukaq24ruLBeHK1zSKKNXJDQSzgW/lGxHj5Wq+p6SK61Rufth8OTpsMrsDPjR"
+"hZVI+QgtrbvoPrn3ViPBJixK4YG5cgWIGmmvG+tKOkRBHlvBNDHIux1kktvHHy7lXhaludl4"
+"8OQ8fouYzqFZx+ABFdKe6MWtmXwMcjLhiBEau4LytyHwrT5PSppcURRiGWF1vKzXDP8A3dw7"
+"ayEKRSRrIYFO651nC3F+w0XDJPj/AMhkiXs+rH4A0Vuk5YrUb0HHUocfHxxiQRqfKQLDzbSN"
+"Vu3KhE6VBNhxSxowVo9+0aCNjrdu6gpppcg/qCJj2+sxv7qpW4Nlx425aNMdOzymle7tEOEg"
+"p1pfdljqJHyukrCrmP09pUY+1iyqRbiDrei8iDKimjzoI3WUjY4YjaVOoJHceyk3T58vEnMm"
+"PjiNiu07Y5m0J76Mmk6zknccmcD/AMNMY7f95xTXYnWLsT63M1C8d3aRytmeQjyg2KbL30vU"
+"kaQmNtIQqvvQn5dSPLbjqL0rHSshiWWCRnbixhAJ8SZhUf6NkG98Z7/4UH4y1g6y9TbIm67h"
+"tHnPIpYxTElGbu4ilJWFdXO4++tJ1TpWVFgySvCyouoYldCT2KTWehgjc2kJAHC1a1Uoztgq"
+"vCZBe20CuloSdLr3i9V5EJR9LleWlFYuMrwlnjJa+jd1VCFIww45I+mnIWxjMmzbfzPuG73D"
+"bRkubjy/TTSY4RIVs20nzC9uBqPROhy5uPIfWWJYnsqtryBvx76ZD7WPBstP9P8A91ZtKXkt"
+"TGgLH1LoZZg0Uq7xZm9Ribe+i4s/oG/1AGDm+pLH5uNdH2p2ZaeAA/8AqqY+0W4mcexAf+9S"
+"heWVnwiUvUOiSuWc3JNyQWBv7hXBldIZDHFIyvY7Qbkaa3rv/wATUD+e3+kW/Guj7VgU7jNJ"
+"u7Qq0vjGrH8vCB1foyQec+lMQWAXcQ+7gWYHia9RH9AxGZllkkum1EtbhwHLtr1EqNwhzoO/"
+"VYjTXsvrXgwtrcHwpF/8gQcAR7TUf/ka3ta57zWXsjX6j0ut/KbHwru8tx/CkD/cS2/li9dT"
+"7kA19O9ON2g+po4SdQBepTXKnTkf20t6V1RMuf01FiVLEg8NvKmMzeQXF/jzrn7dSq6mX68H"
+"VCykAMRyueNB/bKl+qIijzFH+Aoz7hP6Q158eHAk1nEyp8VhNA5jkGm5dDZtDW/UppBn2OLS"
+"fRxjS8hY91eQ9Ugk/Rn3ILXhmUsOXBxY18/Xr3VF4ZMh/wAzfvrrdd6m3Gdj4k/vrRUstGS7"
+"1eqk+ly5DyxbWRo3FmDqbgMO7iRXMjMRcN5gNxCnQDn/AIeNfL26v1A/85veaLhyJSgaR2dj"
+"rqdKpp6tiSTwkW5oyXYhIpdxYMziNraa9lPvuDIi6pgxy29N413JvYb7nRgV5UiGRu02i/b/"
+"ALGoGUsdY1PiKJgrhIslk2naB5uBNGL13qgUKGUACwsijh7Ku9WxuI0A/wANdGY/AKoHctDu"
+"nqpEuprS0FLda6tINpmNjpoAv4VAZvUzwnm9jNRYzZ+RHutXvrckfmtQrL9KH+N/qZXjdT6t"
+"A24M8h5CTcwHson+uddceRB/ljNU/X5RPznWvHMyCLbzSbX6UHB/rZX1CXrPUo1iyY3ZUO5d"
+"sZU3tago+g50hskMl+Vxb8aOOTN/GffXGyGB1kvoNb01aNEkJ9aerbOQdI69jKyxJJGGN2Ac"
+"Lf8A3qm3TevH5i3+aUf/AF1X9T2yfGujJA/OPfQ7Pwhrrr+phvR+n5eB600sYklcjbaVOA11"
+"17ah1ODqubJu+nVeJuZUv/x0Mcxfl9Qa99cOXHw9RffS528B+Onk9F0bNYDf6aX+Ylwbe4mj"
+"R0BARuzYrd1z+JFLzlRX/mj31z6uAf8AMHvomwcKef5jI9ExAdc5bcyEP/1V0dEwDxzgf8n9"
+"tKzlwHjIPfXlzMca7gffRN/UfHr/ANMbHovS1GuYf9Iro6T0nnlO3gFpR9bjk6m/ZxrwyoP4"
+"rD20pv5Ycev0HX9L6JbWWS/iv7qj9F0QDR5D33H7qUHOxhoG+BqJzscC26/+WlN/Ucdfp/Ee"
+"DE6Dz3n/ADf2V44nRAAUVj4uf2UgOdBfifdViZ2IrAkte3IUPn5Yf4/Q1/Svp0ZcfHAVb+oA"
+"WJvY62pxlJo99V2+Udlqyf27lx5XVYPRBtGsm8kWsCtazKberqPzDaPabVz3blzqPEqNDPdc"
+"S0ComoCFvarbdffSPB62/SGcpEJPXAGpItsueXjT3rsV4SxvqNNdBc3/AGVjc4jyEd4rbpWM"
+"mfY4eBzlfdX1Q2z4kcijgGLH9tAv1PDc3+hiH+s/9+lO6vb+6t0ktjN2bHCdZhjt6eHAtuF4"
+"93/ETTCDreU67gIIVPACIfsrMb6ITM2qBs+Wk14HWy3NL/WMzlkqP8MQH7ai3WM0/NmOPBFr"
+"PjqNjfZXf6l2x39tTFi+VB6erSnRsyY37FQfsrg6qWP/AJvJHbYqB/w0h+uv/wAoD21360/w"
+"fGiGHKg8/qJtf6jKP/7APwFcPVTzlyj/APtP7KRjOb+D41IZz30jFEMOVf8ASHM3UIZAVcTO"
+"G+YNIxX3ULuwFJIjdfAg/soE5rkfy199R+rf+Ee+mpBuj2TDh/Tmb9WNpb8ATa3uq8Hp6Af9"
+"OSp1A9Rh+BpT9W9+A+NS+rlP5Vt260OfIlw8Icrl4SLtXDjt/e3Mfxr39SxV4YkQ/wAv76TD"
+"KfibVE5Tkg2FKH5ZXKuyQ9HVkUXGLD3HYKmv3Hkxi0SRoP7q2/CkRynPICuevJ2CiA5L/SNA"
+"fufP7Ft2Wrw+5s3sUeys968vd7q5683K3uogXJGpXrub9M+ULWVwliNfMN5H4Wr1JPVtgM4b"
+"9H1RGi2Fy+3dJKy9wsAK9U7/AFgcoF+s027CfbXPq+yP41XJE0LlWHgRUQ3IcTWnFGfKyeS/"
+"6oEeZbeGtdGbEBYqQapCNe9QZCTpQ6ofJs1P2dL9R1CQhAiRRm543Lmw/CtTNfaACTfjpWc+"
+"wIrfWMRr+kAT/nNaeZiFNuI9vbXH3/dg26299TK9fAERGxjxJPAcf7ay2SLRkjtFvfWs6+fU"
+"SRQCCBe5PJbXrJ5P8s+ytuj7TLt1BQz8b17fJ210Vy1dBkd3v21aMvKAsHsB3Cqq7akNNrRl"
+"n1mUdPUt7BXvqsr/AMQ/CqudWpFK4uqkiiF6BNvLOGbIPGU1z1JrayN76n9NPx2Gu/TTfw/h"
+"Sleg/l/2K/UmP/Mb31zdJzc+81b9JPyXjXBjTE2tr405XlBFvDKrvyY++veb+I++rvo5xbT4"
+"11sSbibe+lK8hFvDB7kcSffXr9pog4kw4299cGJIdQRbxpyvIcbeGD7b17bbjqKJGJJ2j310"
+"4kg5ijkvIcLeAcIvZXto8KvGK5Ntwqz6KTtFJ2XkFWz2BNo7KkFFuFEjCYcWFcOG1/nFHJeR"
+"8H4BiorwFqJ+jJ/5growz/GPdRyXkOFvANuN67cdgvRAw/7/AMK8cRR/zPhRKDiwfbfW1cKj"
+"sooYw/8AE+FeOMv8V/ZSlBxfgE291S291FDEXk58LV5cVObn3U+SDgzQfY6iMZUh4lo0HbqG"
+"rVZLMq7lOm7h7azv2jEI/WCkEbhe/E+U2rSva1zqAbmuXs+5vybUwkvBn+sR/pFnLMdt9dRq"
+"eAHtrHZeoXuNbjrbg4zFGVTtFja5rFZg079xrXq95M7gRArwAqe08Odd9Nuw1uZEAO6vEVcI"
+"n5KfdUlxpm1CMfAGkOAcLUrCr/pMjiI3/wBJorF6dkupJgcjt2mhsarIurppoOnSlyghbcOI"
+"2m9Wf0nIt/5d/wDQanmiuDE1q9qKdr0nJc2XGcn/AAGut0XNXzfSyAdvpmhXnYPxvyIiTz4V"
+"JStNZMCWJN8sLKhNtzKQL+Jqq0INtL9lHL0Dh6oBAubmpWT+yjQ0XDy3rzTQAEkqKUvwx8V5"
+"QAwHIGuW7qLORAeDrXfqMe3zrTl+BcV5A7G1ds96JE8F7mRRUhkYwP8ANBon0BL1BbNa9q55"
+"jyNGxyRyOEjYMzEBRrqTRAxZATfla4uL3YbgLd41pcvQfGdyjBgM8c6sQscMbzkniWClFA8W"
+"YV6rvUjTyksu8bT5WF9RblXqmXMwVCiJRMoDowuDyNRXFg/8MX9tB5DyqpdGJFyDcn82lBSy"
+"zBreo1+4mrSfkl3XiR6IIhoY1HsqXoxjgqj2UhhllvYufeaLEc0rJGhJdyFAvrcm1J9be8DX"
+"cq/2ybf7QjVhlgcbpc91jTfJWysL9v7aB+1ITj4k28FHaSxDCx8oHb40wyrEk2ub2Hwrn7ap"
+"Y1yVW7tZuIkzHWrGGQEgeU2v2isjkBjEbC/C1q1fXWQYtwAGOgvzH5jWcgb9QeB/CtehY+pH"
+"a8i7a4FypA8KmsMrGyoSe4UxlUtHYak2Hvq6DSXIJ47rD2V0RkwkVjFyCbCNifCuth5Q1aJh"
+"7Kexj9Q+FeyASLdlOA5MQDHlJtsN6PxYpFh8ykW7qgSwf31JGYbj28KiynBVbtOUW7gvzCwq"
+"TRkg6XAquS7Mi34sKvLk6DmaX41JX5rehUVbhbWobGU3CeY99Fbbk13ZblT4VQvy2BTuUhnH"
+"gKi0hOm21EyqSdulQkXUAAeNHBSH5bFETAllsSVFzepCInUEAHlU4kUvIfzaacqI2i2gpqiF"
+"+S3kCB2Msdr7ja/jRJwwTrIBUSn60ZI/MDV7kW3Dhc+2jgth/ktpJX9EVswe/dwqiUuoYgXt"
+"V7sx5k+JqmQuqMw7CeFLivAfkfkkmPuiSRvmYXPtr0eO8lztGhA0qWyT0QQddovROLG9g1+Y"
+"vT4rwL8lvJT/AE9raadh5XqONhPNkmLcQApYnvBAouUkFrNqovbvq7pBLZJZRc7D8SKmPQfJ"
+"+TMPmTKxAtxIGnfURlzXvp7qpl+du25/GohhTheBcn5CTkznmPcKuhE8rKAw1Njew40FvvTP"
+"pQWQ66kMLe2iB8n5CYYp1uDGrWNvNw+FW4wUF/UjV+FtwNhpRVx8o43vamPTMZZknDgGxWxP"
+"tqWhq3kM+2RG0sxRFUKUI2DQkq3bTSRrKbfma3vND9JgWDIk2C1wC594Fqvk00FrbrVz9nj1"
+"NqatinqokaNtxCwOu2wGoN/7KWfaPoN1ZlnRXVon2hwCLhlPOnHUAu3zoWTgV4A1n/t6Jsjq"
+"npqdpaN9R7O2r624km8SbeNunNmosccVtrHRVFiPZRxTEtfalu2wrKp0/Mx8ssD5mU27her5"
+"Is0YMszTDcmpjsSXH7+ytObmIJ4KJk0vpxWFlXu0FSVVA0AA7qyqdazhEvFFCi29CCSNKJw+"
+"vyKgSZbuzN5h2acqpXRLo/JorAcK8DckW4Uvh6tjySKpbbcNx7qMjyI3YqGDEHl4U1ZMl1aJ"
+"bQG3gWJ0JtqRU6qkkQcTw191WaGmhHALN414lSCL8RVckhWeFeT7r+wXrrfzFI5gj8KAM/8A"
+"dhROitF2yKR76+dnWVdeINjW/wDvVb4qm+1Qbbe03r56zfqWGluFFXj2YWWnqdUkZCjuPxqM"
+"pFmvwrm4/UoD3ioSt5nHK1UIFHzCpXHtqI+cVw8TSGdXjXVcg3rg41MJcW5mgAvCzRi5MWQ8"
+"YlWNgxQki4pxhZWLlZ6oBeMxiUsdXEqR7CATp8tZwta4q+KSSMhkYq1iLjTQiotWSq3a9jTT"
+"7Xjjgx5VI+nCTSrYINhLFtDbVeNeoOGL08GR9hN4CBqdo3L81epcHETgrmtTuVgZGGXgyYip"
+"IJAPDTsPA0lniZOPvr7IYsefSWNZF+WzAEeY99Zjr/2bAwM3TyIiQbwNfaT/AHTy9tNPclrM"
+"Hz2NrOCaaRqxnQKPzD8aW5OPPi5DQ5CGOVDZkbQinWDteeIj/F7LVTJNz9ueocJyTc+o2p1O"
+"gWictHAJ7eB9lQ+3E/8ATie12/ZRGSjSeTge2ubso9fU2pbMGS62B6TbRdrEX7OZFZuLR9Ow"
+"/hWm69HLeUK5GzzMRYLqe81m47b7dlX1JpC7HkKUAGO/JhdfA3q/ExmeJpNy2BLkFgGIvbQU"
+"MrAyX7Ln3CrIZBGg57bn31o7ZMkgpADIW5E2Hs417JsNoHPjVULhUVyTcliedSlbcyA8yPdT"
+"dnAQCxJjPKA5ZQTqxIAHuBoj6fBUXL70J1CElv8AeUCgwLNrRTJCqqEYsTxuLVK+o/oiMseM"
+"Z4hCXtck77ctRbbXUjvKByq3FOL64+puFAJVhyNjyuONFf8ApahTCZJJNd24BR3Wtem7R5Yk"
+"vYpEI41wxi9W/wBTwUYK0Ba1r3NTTJxZrbIyoN+Ivzpfkf6bFcV5QFKvmqDoLdreFMWkxA1t"
+"qkjiWXS3srsmZ01ACAgJ1IKkn20vyW/TYOK8oUxLqxtxNFen5QbVdJkdNMe9CSd1isaDUns3"
+"W4V1OoYKRgnGlkPPco4++muyy1pb6E8V5QG8Y9RT2H8K6yeUCjG6p0khWfHe63soW1yeZO6h"
+"peqdLOqY01+S3FvxpfltP2XHxXlFRSwqEsY9Br9lTHUMVxrBJfkARb8a4+ZjzR7PpzHY3BuS"
+"W7uNObPZhC8oNREEdiAfKBr4cqtxkRUt/eNVSMFNgCABXYpgFBsdSa0U7ks68amSQAcTa9Ed"
+"MVYsk30smvvFDLMC8lSxprzSkckqYzPqMyOQo9V7fxG3vqi1qulcF38T+NVEiqA8L09+3yVE"
+"hvpuW4tyN6RKwvTroUi2lB5lbfGpspUMEPRIq+b9lE4JOQJWDH9Mrw0437KVvINmh9tG9EyN"
+"seQCNXZAPZeodFOhSY+6UG9WUsSQUUC+pv5qJlIHD5g3Z3Go9HWOWSUqLWCaeBNHzY6Fge+5"
+"9oIpfiTyUrxgQdRMjQswUkXuT7L/ALKS/agZOuoJBrskBHftrVZ4SPGfdpbd48LcKy32+yp1"
+"2Jjfawddx0uWU2p1pEha0mzVR9YGI0KkXoiRVsbi456VGJAZQeQ4e6r3RSDVpEtgmTHjIoeV"
+"V2jUki+lCzdLx5cdnjUAsbqQORI/ZTQpoLGuKpCAHU60nUORnW6PKDZG1Aa1VSwZ+MxKkliA"
+"BtPZrr760tluCR21xUja7c1a9++1qjh4L5mTycjKCNt3HQasbN38KOTr+QkdjZtlgCdCeVM8"
+"jFga4ZVO/Th2muzdLxNgUIADxtS42lwPkt/5iyPq7nKgE1n2s4FjyKm4o09RRsmB7lY133v3"
+"ig5+lRR5MEam27ezN2bQOHvqjKxZY3jhja/qhiL93bUt2XsNKr9wf7smkmhmc/y1ZRHw4cz7"
+"6wWQ49QWPOtZ1w+ngyQSgExmzOL7r8OZrGP83GtqOVPkzvqiwt+uvbfjVbNctXjfeDUeZqyC"
+"INmBrhr3Oum2nLtoA8vzV67A8a8vzCvHiaAPXvRajcoPdQgomN7RgX4UAaXFlR+kzWA3rCQQ"
+"QRoE23r1BYeSBiSxHQGKSx7TtNeqQNZJ1GaOIOkzEkkm9j8vDhTb69MvH9Tbo6qyg8Qxteh5"
+"MKCSF0Cjcx2qQP4rrVEmNkYm+PdZIwNRpo3L2VlLWhvh66iX7kxosmFp5RudQWVh83lW3jSP"
+"o5ZZNrggxjUHjrwp31ElYiWckgHQ/hSjD2o0jgG7kHmT7zVdbnBF41PoP22wbpd7cXb9lHSI"
+"hu2y1xcGgPtdx/SEPa7/AI0zlkUAe/Tuq2kyE2ZH7hEe2Ubfmsp8Rdh7mFZInab2t31sOtyp"
+"HjMEivxBYkCxK3v2njWOmdT5bgnspwoE3k7HKLuxIG1SRfvsv7a4Z7KADzqkkhTY6ka1Vve1"
+"ialpAmMfqhtVSQB4241KTKWwO5QRw816WyOStuPsFVlmPIe4VUIQS+Ra1muSeVeXOYaE8KDu"
+"a5rSgBhJmqV0Bv21UuTIATuOnfQw1NifhUm0TvNEIZd9TuFjqe29W4mcYZt7ruABFv8AtoKP"
+"jU2XUngKIQDMdTicgEbb+FceWBvMJACeR00pWurW4jnUzcE21HfrRADOKeOMLd1PE8bcaKOX"
+"jGHSRL37RSEmy/jXt1kOl/bTQBuRMrsQOZtQQfUivb9KgvdQBYZyp0vVsWW7yomvmZV434nw"
+"oNzrVuEwGZCxNrOpueVjegDVZa2ZyO8VQrAxqNe/31yfOifd57991/fSyXJHJyLcdf3VSeAY"
+"zRSptobi5qGHIBJOG5gLy/bSxMn0zYnW3aT8aobIBcm2viaSEBSDztYcz+NQq1uJqFAyNO+g"
+"AnfYDQgfBqTACtB9uINsrdrqB7j++kAVNuSIjgT4VZ093WF5OyVR71q2ZEIIJF7VPFSJINpt"
+"rKpt7KTQ0aj7eL7JQQN1gfxplIZrgC5PM8udL+kSwJJJtYAFQL8OBo9p0JOvy0sRqVDnQWdQ"
+"fIcNCUOp0OmpIIH41kunl16vBuFgsn4AithlZSHcysDY6jTsrFY8ynq0Pm1OQB72tVJqNRNP"
+"wfRMVgSp8aJa1A4ps4H+3CjSRpQJkmNhXha3sqE7bV+FdU6nuFAjlhssK6IxYgaE1G/Be7Wp"
+"K4IJoGDeV3CcfN+FXSKSdOyhMWJlzJ3LEq7XUchYcqLkcBb9tqhaMp6oGMIkyopHvuVXW3Kx"
+"t+6ouIjkxKfyK9j4kD9tW4rGSVnPED8TXZYES8ii7BbfG9ESpQTDyYv7wiRN7rr6z7m8RpWK"
+"lA3i1bf7zBISTgpstu8C9Yplu+tVTQmzyUkeaonUmrdpMo9tVlTrVCIjVhXjxNetrXiDxPGg"
+"DnOumvWN69Y0AeFWA6VCxtwqQvQAxxDfHlYnhG+nfavVTiWYSI3D0pG9oQkfGvVP90FbG46X"
+"10z51p2WOBVLXcj5gfLVvWOv4wUiO0xGp2nS9ZMsFQ9pqh2vWYcgjM6hNmzl30DG+0cKlHIq"
+"jQ0Ig1B7KtFNQTJq+hdRmjxEhvtju5DHUXovM6nkqlhMCArcB/ZXvt3GSXosDFQTuluT/iNE"
+"ZnTccoBs12m5BpQ5NU1Bjep5mRLAzeqmrWK383DjakaFvUudTWk6lgQRg6cT39gpQ0Ea6/hV"
+"zCyZ2eQdtR41DaaK2R99cMcfKlyQgcqbVH0zRW1eyvBB2Wp8hAnpGuemaN2rao7B40cgBFjN"
+"6lJGbCiQADrXmUE8KfIAVEIqwqTx1q3Z5bWtXQtLkgKo4V1PPlUjCaIRdL1MpS5DQC8VVmLh"
+"pR7RiqinmFNWQMHEOludSEFqvANSNxRyABMFya6sFiDaiCDXgppywOGNQPKxB9w/GoGFiO2i"
+"ArX766Y5ORpcgBVgsNRXPp9b2or0n7RXtjW40cggEaAVz6aiSproU0uQQCjGFOOksmPjvcXJ"
+"e/G3K1BbKKhX9LiOPA8aOQQHS5Km25Rt5a1AZIWx2jRr/MKDkPdXlFDsM0XTsibJ9QQWugBY"
+"AnmbCmBedQQytyv5u6hvstNcxiPyxge9jTx0QMRYaj/sqYnJpW0KDJZ8m6UtErg6a30tfgbc"
+"6QxLIc+JiLkSoTa/Jga3GbCih0UAa/sNZN0AkJAsQSaaTqK95Npj50Affe4AJ3LfT3VfB1XH"
+"lyY4k3ElrKTa1/brWJSeZCSpIY/LY7beyr+nZcydQx5JHayyAm5tw76fIk3+VJawtfUa11Jg"
+"FZmsNedKZ+opKbrILXGga/8A3ajl5uxRsl2E8wR2d60csscYHCSpIC6kGw5d9cBNjUYSTjoT"
+"qWUEn2VFpEVTc8jRIFkfzHuX8alKt0A7KF+qjhDs5sLAXq1c7HlW6tp4Nb8KKwD1LcaMKWIH"
+"G1cznKQEjjU4GBDEUN1Rj6QUcTer2J3Mx91oj9Pxi3zs5P8Aumsj9OhfgdBWu+6GPoYkdtRu"
+"PuCis0qEsb8KltqED1BVxk9XgdBVr4aDD3hdfW0PPayE2/3KtVRuJvyq9mRcKMSLeMTBnUHb"
+"uBDG16m1nj3FApEC3qYWMR7NiHW9yvm8L0RJ6Zc+krKutlZtxHwFFYY6d6Z9ZN0/ISMVhtb/"
+"APrG6/jRyY4FnpxXv6a+wn99c9KE/k+NED5joOJsOI99cueG0eyjkBR6MP8ACR8ammNA3zMU"
+"8Vv+FT2c+FTCCwokUEoMeNCxDXUo6lrHgyleFeqyJLK/YVNepTmStoKXvcXqB41OTjUKSkln"
+"VFTHGopUudAjffbDFehQm19XJ9rmjJ3uB221HdQX23/7BDw/P/xnjRD33H+y/CqzGhpgzXVX"
+"bayNrbT4UhlW4p71f/mceI8eFJJKHyIZR6ffUtgHE1Kucx+2l8hHVTdwNdEP941ZF8p4VIW7"
+"qWRlXor21WyKOdEta35Pjeh29lPIELC971EkXualpblXB7KYjt9LV4A3rvOuDj+6gCwEjQe6"
+"ulmIqo8akeAowM8xNQvrXWqI+b91GBnrtXS1c5GvUIRy+tSr1cPHlTAmCO29XIAeP4VQKITh"
+"SY0RawBqtjpVx4HhVL8KAKy1eDVE12kGSW6r0ewA0obnVi/KKALXavKw7ag1++uLe9MDZfZy"
+"hsXLN7Hemv8AlP76bzQuHWx1/spN9l3+lyeP8xez+Gn0vzLx5/spqIKUiPqIkiVpN9yb29lZ"
+"oKfU760/Vv5B8G41mFtvPtpPQRNr6X41zQEbuHdXTbdy9te58qQBGMwDaEhRw0FGmQnnex56"
+"Uuj/ANu2jE5cePt9lQ5KRoIuoqoRSSbIOVwNPCqMzPZUurHXnYUKL3H875Rxtf2VDL/lD5rX"
+"/PVZjQZwZ0kwkjZ7qB2CqseV4N2xmsT5he9x2a1Vj/8AM4ezx51Jvl/2vxoUiNV0Ri+DvIA3"
+"O1vAV7qJ3NbsAHvqHQP/AGtOPztx8eVSy/5h/wAtar7V7E7iH7hx/VSJ/wCAEcbcTWcKvFdg"
+"SrciDW0z/wDyh48eVZPqPz8/bUMGULlZINy4YgfmAP76sbqD+jZ1SRmY3BFgtgLWsPGhR7ag"
+"1vZ31FuO41J55NxFolA4kAnX21M5XC+Oug011+NVNttpb2X/AG1WeNGPUMlk2Q8jswiUA6Dt"
+"H+mwqkPP/D8K6vGpjlxqlAjqlrWYVbbThVR486sX/a9AB0MQEDtbUoRXqIT/AMseHOvUbAf/"
+"2Q==";
+
+static const char *easter_egg_photo2 =
+"jpegphoto:: /9j/4AAQSkZJRgABAQAAAQABAAD//gBtQ1JFQVRPUjogWFYgVmVyc2lvbiAzLjEwYS"
+ "BSZXY6IDEyLzI5Lzk0IChqcC1leHRlbnNpb24gNS4zLjMgKyBQTkcgcGF0Y2ggMS4yZCkgIFF1YWx"
+ "pdHkgPSA1MSwgU21vb3RoaW5nID0gMAr/2wBDABALDA4MChAODQ4SERATGCcZGBYWGDAiJBwnOTI8"
+ "OzgyNzY/R1pMP0NVRDY3TmtPVV1gZWZlPUtvd25idlpjZWH/2wBDARESEhgVGC4ZGS5hQTdBYWFhY"
+ "WFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWH/wAARCAF+AYkDAS"
+ "IAAhEBAxEB/8QAHwAAAQUBAQEBAQEAAAAAAAAAAAECAwQFBgcICQoL/8QAtRAAAgEDAwIEAwUFBAQ"
+ "AAAF9AQIDAAQRBRIhMUEGE1FhByJxFDKBkaEII0KxwRVS0fAkM2JyggkKFhcYGRolJicoKSo0NTY3"
+ "ODk6Q0RFRkdISUpTVFVWV1hZWmNkZWZnaGlqc3R1dnd4eXqDhIWGh4iJipKTlJWWl5iZmqKjpKWmp"
+ "6ipqrKztLW2t7i5usLDxMXGx8jJytLT1NXW19jZ2uHi4+Tl5ufo6erx8vP09fb3+Pn6/8QAHwEAAw"
+ "EBAQEBAQEBAQAAAAAAAAECAwQFBgcICQoL/8QAtREAAgECBAQDBAcFBAQAAQJ3AAECAxEEBSExBhJ"
+ "BUQdhcRMiMoEIFEKRobHBCSMzUvAVYnLRChYkNOEl8RcYGRomJygpKjU2Nzg5OkNERUZHSElKU1RV"
+ "VldYWVpjZGVmZ2hpanN0dXZ3eHl6goOEhYaHiImKkpOUlZaXmJmaoqOkpaanqKmqsrO0tba3uLm6w"
+ "sPExcbHyMnK0tPU1dbX2Nna4uPk5ebn6Onq8vP09fb3+Pn6/9oADAMBAAIRAxEAPwDaR28tk5GF3M"
+ "T/AL2P5U2AO0sreYHVQShI9Rio7WXMt7I4LEQp07HcR0qvJqIjmFrCoMvTLdB+Fcyi9jf0Lt3HlSg"
+ "XcX9OOg4pHWWCBGZHaRiytnpg1Nalz87De3U5/nWjFIs42wnKg8sapQIuznbZpLZkgXO3zS5OcgdO"
+ "9W7G8eCQLK+6SUkEZyCMnoK2JbUGMrCqqcYUkVSt4IN/2shUYDy8E5AOf0qZQaHzEer3EVts8yINE"
+ "+U3jgg+mazysCopWXdG0p6nB7cfgQKv3KzymaAxq9u/KnGdp+tY11ZzSzhoY9sTY3I68E9zn1o9R2"
+ "NdhvXywB/eAzwc9R7VEAQ3mxhzksuCOhBz/jUPlkrHHFLmNf3bjb8zen5VegkeSNVU4jIBZnGO/wD"
+ "OpsDKZm2SJ+6ypLEg/eYEf41bYPK/zOkSMPlU8k0kkK24aVFMkrnBcDkD6VRZmjkSYhy24EZbt78U"
+ "rXEl3JXkltQIreB3GQGYDcRxxxUj3TSMpbJB3EbuwA54+tZgWa/BNruTbIcsGIA/GtmwdCqpMysYg"
+ "cM3Xk4PNWlZDbvsRmNlt55GAOQSn0/wwKr6jardzrOgQR8BXjbrxwP5/nWu8TO+2LDerb+lMuLa4i"
+ "jcwIAoO7aAOenSnysLnOeROs3znywGIwecZAq7Fvjg2fOJI34Kjtjj6fWpGu5YHV7htvOdpHJJq5G"
+ "sj7pJHZUz8q98etDugGwyiMRSNIWZ1JRmPQd+e/YVXuIkDLsUhfvZz93POPxqRQxOHKuifMg6ke2a"
+ "inefzGcsQnVVKjkVm0N7DyChiCqWQFlyOxyD/jTtSl22u4HiPaSwOOjDj8qh0ne5mt5Gy0kIljOO4"
+ "yDSaqXTR5wAo3INx9/akoiWrLuqSMJWjgCBxghm6jPOfpVX7QQ6tuZ1UEFiOSccmnRXiXsMF1Coll"
+ "KrG6kfcOOaSeXY4ijtmJYZLYwB2PP41TvfQV2Z14UN6wdGEc2D5mMHaF7+4zRqboFjVh+927ix6Dg"
+ "ZFaAt1kCIqBlKkZ6nPbr9KguNLYzO85YrsCcDk4GKEm2hSaMq0DXVoyy48tGyhz94cZx9OKRpP3m9"
+ "SCdm09uR1Na2n6bGMJFJvRFOFYbcZGP6VTvLUxqbxAJIW42pyFz/AI02hXL9rL59q8Yy21BvI+gOa"
+ "iaRd4lywXDbSehHSm6aH2xF/lV3xuXox9Ppilbi4MWwCKMqqY6HJ4/rWDiyi7csou2mJViYhsTrz3"
+ "J/OlhX7QivchopXX5tvBz3/kKgmGLuJmk+ZjsXjj72T+gH51Np9zHfkpE6lQxXgHOQec01oNk0ttC"
+ "LUQiRwWH3mAJJJA/lSjTDaRgxynj5QWH1x/OpXZHmjwNwQfw+n+RUu7DGNJDlCCMjsfX171roxJGS"
+ "tvJG0obcMSBkPUEn+nNTzQghlaQeU3zJt4wO/wCtPdwJGdtvloGOCOcjkZpj7gcNggRtk9MdM/yNZ"
+ "opdyN7h1EYdDsXlu20KCT9c/wBaoQ3kV7IkuW3MwwpHGev51q+aHilV1GCgUn3OM1mS2rfbxOgRYO"
+ "qrGOSRj8utWtmTd31HwO6ytBgw+VE5Yr0LMcfp1pSRJCV4EeBtI9OaJY5RM6AgFiAcdWOOfyH86VL"
+ "WQxqFPyg7mJI4wen5VkkIatxF54cttVsoV9CODmrIcK/lLuyxXcw7gf5/Wq15A0krPEIgWPLZHGep"
+ "qa1gl3bC0buyAEh+vStI6aiNPT547oSxzfOnQb+/+RWJ4j0iC10O5ktjvQyRypznaM4x+tJLbywyQ"
+ "o0iLDG2XcPk45yCPyrftxG1qkOxZbcQlc9c89K6VJNA43V0cLpEnlXTIXDDpnNdCJS6knsK5/WLUJ"
+ "qH/EtESIDhUD8/jmr6zXkWmZKjzJRk+wH/ANeq5hbosMcuVOMEc1W1G1llgiMJwQ21xnAPHFUIZLp"
+ "7lkcsuCQSw4rZkElxp6iHCtuO8PznPTAFLmvoL0MWC2u1D2tzCVVSCkhXI3ZHGar7j/s/rWrBIUk8"
+ "q42qMEhopWGMeoNP2Q/8/Y/77/8ArUnKwM0tNuVmn1L7NJkmAMjYHJXPOK5mB5pH+0Ehyct15zXV6"
+ "baQWevRLDGFEscgYBsg9Dmqh0pLe82QLjDEPvB6e1D0TLb0sW7GWSQrGitvYgYI6cVuP5enWQVQcD"
+ "0Gai0mzePM8xJdhhQRjAqvq0lw90YoFJ2qp+vJyP5UR1V2StWWotRjkKphlYjPTkD1qrdQRmcqcEO"
+ "ASvTJz1NQpbXKzpK00eQOC3JA9KiuXJuCkjtJlDgBT7c+3Shy0NEjShu7eZ/KRS20AnKjA9KnuLIT"
+ "I2JHUnnliRWRCYrd9vILNuGGyOueK3llVoc56jFCaZLutjEmDW0fluiySk84+6PT8aaZEmxKzGNSA"
+ "Fzxhvp9afqE6XLtCgDGNhlunPfmiO1hfDsFLhcEE57+lZSjZldCvJGY5BumkjMjE5Xv/wDWFE0jSE"
+ "QrbG4hVTvZ26Y9KsTAtC0kcfmYHygDv/kVlzadHcOrrI8EgPzkED67hmknYTdtzXgtBPBF9iUQwqc"
+ "PGRjt/PpWXdb4LyYSZyTtRyOFBOecVps8en2yJFyu4DOec+pxUaW738Czzpv3ufLZcgqvYNzzWi1Q"
+ "htvK9u6oil4CwJYOCcn0rY+04UsdvHYnFUnsxDbsyqrFT8qxjGOKzkXzTE0rMhU52t8xPNF2g3JNR"
+ "urJ9SVyEYhcyNg/KoqWK4+1K0qLsjz8pYHkVnX1q8KTtGm8FvN9M88DHoOSfwpNG1aCGMpcMqqzEr"
+ "np9KLofkjRuLK2e1aVZZd24FcNkA5p2GlEqPGcgbl34JosZY4jI+SLd3MiLj7owB/PNNa88mbIj3M"
+ "VxuA6gDjNTJ9BFB7xLLWLAg5UM8btn+8Rx+FGvMkNleLuXdI4jAx6Hk/lWbrz75IGEYiO5iB7DGM+"
+ "9ULm6nvHVp5CxAwKpR0Q7o0/DbyDUEhVsROd8gPovNWbiWeYu0TFlmm8pDnGAuWY8flWdphZJJpl6"
+ "xxkL7s3yj+ZP4VsMXszbwRBXECbn553GlN2AktSfKiMkp8zaM9OR1rWhk3kK7fLtLqO3Hr+dZJhWW"
+ "b5VLsvUk5K8f41J5ixQMhBJ2EE85AHas4ysydya2MSws8SHYZAXwOD16fkKpO/lW9zCxI3LlCPXGf"
+ "0xUkMhlXgyRKuCVwMDGOc1noZXlkeNc+UBtyMN054/EVXNbUDQglWGBotq7oFDEHj/wDVio1ERK7y"
+ "uHYP8p6Y/wA/rUFjE00ksSyLl1LSFuhznjNXV05YAk8sgPHyoO/tUN3KbRXkmD3BkYAhSDF6H/OKk"
+ "shFB5wt02sdzFgeMkiktvs7Tyx8tERjaR93NSQQeS33QwIJDL07Cs3oS9C0Zj5bRhdi7N24fXv+tI"
+ "9wLeSMAhjnkdSOvNQeYzPyu6NiUbnk8Y4FUbcym8b9w8hZuT+X8qcW+pSNC78u6iSTBJc5GRjrxz+"
+ "v5UnmpHbPLhyi9R1LZJPT8RTk2qrjqdwYgfwjr/n60iYjgUu7KXUFlHdjjipvqPqRPfwhWaRAsXKg"
+ "4zz1OfxIqG0LI0RmfjBGDzz657df0pShdGh6kn8M9cfyqMzxQhRLtA3YbHRe+f0rS+lgsXm2Rv5gD"
+ "Ybdgj3OKfa2c2wyyb9x42E5AFQyzGULFGxZlkVWI/ukZJ/D+takcwSVdvKLwcHqacY33ERPprfZ/J"
+ "VU2MOc5yOeazIYls72aNjsTaW3AZx06fka6cnBJ6+gqjeWMc6mQnE208A9RWsoK2hFzF1KK0uIjOP"
+ "OC3HB2gDYRjOfrxU2kahEkbpvTch+6GHA6AVdUCG2EJtyRIf3h69ehrEi0G3hEryzxfMzcbjtIPsK"
+ "laFJ26D9T8ma+WOK38iTBkmx1x7fWop4LW6T94/lofmCntjsTVR4J7HzRDNHLJIdm52JMagcDp61b"
+ "kt4TKss8nzgAqG4VsDn8KblYV10KFjayR4lMZljXcrNtzk4OOPQcc0tlLdHHlTv5EfLHYcMPTNWLk"
+ "6mJJJkKwxKo3sCFwp/WqNvfLbvtvIGlVTlUJIUn1qlsUi5qFk10ym0hf8A1ZIyM5ODnnuDWJ/Z+of"
+ "88H/74NdFa6i8twjsEVQcNtHqcc+g54/Gtz7dYf8APZf++jUttD5blKxtri31O0EgiO1jkoc8MD1r"
+ "pHt4ncOygtjGaybPQ/s+r/bBKWjAwq/5+tbdapGTdwxUbIrZ3DrwakqIODuwQxB7VQFK8t0jhCj7g"
+ "4A9KzH3IXZWcnIAJ5x+Fb8hQRky42AZOelZXmiUKFUt8+U2nkVnNFpshliRlDCM7sDYpA7en51Wl1"
+ "ERq0WMHJGDwa0FCypmZZYWUn5j2Gf/AK1UrywgvcypIsik43hef0rNKw0ymZFncqsjrsYEbl4JPfH"
+ "U1faEbZHVyZTgElCAo71AsBto2cKJOn+sG4/p0qWSWd4WbaGBBOewpN6lk4mKeWyEtjgZX2pIDG1n"
+ "JHIU8xm5AXkgeuaoxXKWzIlzlPMz5YbgMP8A9Zq/dIzLGRtj8w4G35g3tVJMWhnXNrOx8yz8ucjcM"
+ "hgNufWtOC6Gn6dAjNJK23LHbnHr0qrp8VrPfzFo5YGGBuLY3+/4VbNklncjySQshJ3AcKe+f8arVI"
+ "nQZHrDSTqhRgA3OAcFCMhunvV57S1uY95VDg7gVrOuNOb7Ys9tdssaj5o0PU/0FRXF3OkMV0u2I4O"
+ "6NmwAPX3pJsppPYtRC2kbYlypEZIId+tZ1xbxWqsZBbSf3dnUnNVJ9Un1FDHEipHkAvjDMR1plrbb"
+ "CCxJOehPNV7NW1IdiZyGLAPMFIwAW4HvUschWHZuKnOQ3vUqwjPTJ681MLXKfMOBz9KTUWOzOb1cg"
+ "XUcQbcETk56knJqnjnin3EomupZAOCxxn07U1eta2siepq+HgGvCkke6N+P+Bj5h/I/nV8tHK7TRZ"
+ "Vny+SueP4s+46fjTdPg+zz6TF/E5a4f8uKSZp4rxoizESMX3qPlA64rnlqNrQt210HTMjrCfujn71"
+ "WZkWKMTOo2NwCDmsWVgJd8TkhQQ2R0z3HP4Vt6fcQnT0jndSTwyk8/lUxj3HYzJZYrZXOQysAeDyR"
+ "xTLVZPMjkMZUNlGG7O1cYzn8agnQWl47mUyRjKqccBc9/ft+FaVtcRNbvGvA2cuB71TSsDKq3ToDD"
+ "bosf94gZZfqTTb4tKoO6RyFHsAQeTn6VbjVQ1wfMLyFxg568E85qtO6yRMkuEU5OB1Y9PwqLoExLS"
+ "LZGMyB3YFiR8ue4FaaBoYVhUHMb7W9wecj6E4/GsyBohOW+YAAKDuzwBzV+OR/PhcSDBfLgHO1Tzg"
+ "1MloDWhJHcCQRSYAULt2gc57/ANaqvcbJpPmZCAT8q9R71Irx4YBPkSYLFz1JGSamZ7Jk3zQjAbaz"
+ "qO/TrQl3BXaKUFxFIC6EkSFh6E5x0q1MhLxmMBzngHseuaqI8T3khhVljjBx83UirIlktYVeRA0jE"
+ "D0wMVnbXQa8xzxruXzY96k5cg45qlfMbWQiGMlXyiYXOPfnrVxZfMmwAqgZLA9Qe3tTHmt/McPvMp"
+ "OPlAU5x1zWkXYLkc8slvKY1ACqm922glvqe3ep7e4+cSopILiMjGMZ71Et3FvcHZGrLw7DJbtRb7p"
+ "SzuSDuG/B4LcjNKUnshNmubzBYSEYLcEelHnxFSWl2GTgYHPsBVVIIlU4DEdTk1HNDP8Aao5YwDFE"
+ "pVV6/wCe1VCfcSsXrnMkal4pAGO3hsH06Vz6qumXFwoiLRltygE8qRjkduTWrNM/kxSzM0ZD7iCMg"
+ "/WoGt4rmXc0+GIypwcHjj8KqUl0C9mVgfmHlsPlUK4ODn3PoalvLUG6jMYXcFKbj05WoYraWGfy5A"
+ "pV9zNN1ViOmfzqxdahDbx7pQBKHC7gc4BHJrON27CTRBexzu/2V5FERXJ2kbzgdh71Ld6Pbz6a0Uk"
+ "7xuADH5mM560s+n3Bg83TZow5O4vIMtjHb3qg5uPMt4LuUs4H7ve2Czc9V5xW7TjqJu46GwMcESCb"
+ "IDF2ITG454HPYYp/2WX/AJ7Sf9+xVlpkciORRlHAxu545PNUv+Eig/59G/Osk+bcqLZ1umSNJYxF8"
+ "7lGw59Rx/SrVUNIkD2zkdPMNXmbGPc4rqg7xuQ9xTXPvqCoXAuSrMxCqeK3zXI3ExW9lEjtmQYjVh"
+ "xnn+eaVR2Q4uyYl3qjG3+adpAGwQrDn2P5VGJGv4pHtJkSRQrKu0ghgef0rOu7fyhJKAwXhSCMAe/"
+ "45rLW9ksroOCSAc+4rK7karbU9A0/UUvl8qWKQSNHtZXwNxxziq1pbiCQuEeO3c7VVx93rWbpfiC2"
+ "crKVAl4UkjPWtcJcOGeG4DAkbo3GV688dR+FXe6sQ4kDOEjIGchiMgYFPWKGQjBbO7Jyc7sUuoWqb"
+ "2O8rFIACB6j3qKGPdCm11zgng4OMj/69YybT1GpdyV7aOXzdy+YjMSoK52Ent7U3ptT7Q4CDBUAA5"
+ "qrDdSvcFWBjRGznPJqeOGS8ndhGzbcn723nt9aqMr6CTRM8ivuYIuO2Bnmm3FwVi8pXUOeu44z7Cm"
+ "pcwCQ+bGyiEhcOcEtj09Kz9S1WER5WPdKOGweBTs9iiK7v2tSzEFZAMADuKpJa3GoSC5vCVhPRc80"
+ "62CR7r7UPuqfkX3x/wDqrPvdTmv5wQSsSjAUcYFUtNgXmdBb2iJEjQ5AY/xHmraQAHj+I8VU0u/Se"
+ "GKLuoxg98f/AK61BkLkEAClzNopJBHDs28k45Oe9RahMYdOuJCcEIQPqeBUu5lG1eQe9YevXf8Ao4"
+ "gBzvcZP0/yKUdWPZGGq4x7CrFpD9quo4Adu9gM+g7n8s1ADkUokaMkIfmYbQR15rd7GK3NuG7+2a8"
+ "9xED5MEZVB7Dgfn1rUF6JG+6BwMZGcGqekWZtdPxJxK53N9O1LNsbKn5WHSslZmkkWo7bzyY4oUBB"
+ "3YTAx9frT5tMd4i0uFCncyk8DH4cis+K5ntpmeJirdOOeKm/t67AKuynjuOarkRJE2lRvJG8kwMeC"
+ "rBU+U+9TRWVsrrCCSCezetRfb96YZRj3PHNH2xAC6qVPU4rCUJ9CWi79nE9yJJBhQwxnqewpjCJLd"
+ "riVCfmIVduSef8RUB1iFQqkbcjA3GpX1hWgPlOu9uOeAo9qhQmndjsZu2NZmea+CAsVLBGy2cEj6f"
+ "41rx6fFBOitdRqsnzBCCCwNRRX1vFB5caF2P3t2TUMuqBukK5ToxOSK6FC61GzWuLVb2zCFdstvID"
+ "8vQj1H4fyrJlS5g+b7O/lkHdHn3NVm1GXJLSsCewOKEvmbIMjcj14puN1qNLsXbSILI/7tthUsEK4"
+ "xx3q2YisoYp5jepPA9qzBeCMfMxywqX7Y4G5SPnGCM9axdNrVA0WJbWS6tnHllSOIyGGCM+naqz2U"
+ "ghjiuT5crnMcyjIHHc0qTEIyL/ABdwcH6e1RXVy4iKg+WojBKueWPOaTWhm0yOdPJVEmVGcAsuRnk"
+ "H+XSpre63FkUnChSBnAOeuPxzVGW4hvdzgFGCjAH4VNc4t4oMLiRwWGTgICe9ZNsV2aVveyYba3DH"
+ "CknofepRqIWSPIMrMRg54BNY0LiWBzJmH0Ytwxzz1/zzUtzOjM6iEDy4gHKnJU4BpWfQetjYmdpE8"
+ "uUZySCCelZzS43yKYn8s7Qu7GMepqWK7TySXkBMgPPbJ/h/LFQuPtLb0QCTqQvAP1pKVndi1TFe7k"
+ "SNraO2HlgZLM2Rgdh379qbM9hqcP2fzVQnIUjse/8AShrVp0mExc7TuQKNzZAycfyrCfT5H3eWki7"
+ "VLglduScYA/z2rRNSdyb66mzZ/b9NnO6RGtwSpKnJX86vTvYTvFPtU3KDIcjAXjqff0rGsjNLbGK4"
+ "jIMTq5znfnoAa7HT7YPbBriNfMYfMvUD2rpTlJWNHa1zDRLESiVnLqCW2AdTkdfpUO6P+7bfkK6O7"
+ "06OQFokUOevGAw9Kpf2SP8An1jqeTlFdGvB5ZQGLbtPPHSqt5ckahbWycuwZz7YHH61hWOoPpF4Le"
+ "4cyWsn3ZMcqferKXSy+LXwciOIKD69z/OtYyTjcdtR9r4jRN0OoI0UyHDEDIJp9832m3+QRyq2Cjk"
+ "HI/Kp9X0OHUT5qnypx/Fjg/Wq2lRT2C/ZrjAdR8rr0YUTHdGde6bMNLuGR8oASx/vdCPyrjJZNowT"
+ "ke9euo6vkdQeoNeXeK9NbTdUeMD9zJ88Z9j2/CnGCS0En0MoTbGymVrvtBvvtulJOrASRfu5hj8jX"
+ "nqbC48wlRnGetb+lXqwRmztWYrMw3H8fzqJrsOL1Ozug0sIEYXJ+8O7Vnr5sUOHdEyDyG6VDPqv2K"
+ "d8YkRMgg/nSvrdjdxI6SQxIwyUkHfuDisZRchyhfVEm9jMrO4bHADHt6j60r38tmFRm2BWLFweoIq"
+ "pNq+kJCPmJdCMeUDjjtzWHqGrJctKUlb5sgB0HA+tbRhdE2tuXNS8RCaTYrsF6M2OtPsPsl5dwRRs"
+ "ZMBpZOOoUZx+NcyY4j1lbdj+7Vvw/eCx1mCc52qcNgdjxVyVo6Am2X9bu3uJoY1VtxXcUC5/L1/+t"
+ "Wd548oqo47mtTXBBZ6hFqFnckSs4KRAYCgU/WbGKa0XVdNVfs0vM0Y/5ZN349KhbFO9yLw/Kftyc5"
+ "XIXnsCa66Zx8w9DgYrjPDQ8y7OAdq8k+npXVs8jIrFsY7bamS1Ki7k6uFKhzgMQCxPTP8A9eub1x9"
+ "+omPp5XBx696u6nfoiPANrO644/h96raopuhZ3kalmukAYD++vBqoRs9Qm9DNZgq1p6Pp+6UXVxjA"
+ "+6p9aLTRpJW+ccnqewrbMWwCNQNgGMU5O7siUrDDMUf5zwx7Cobl127wwUZ9KlmGEK4A9BWVeTFOo"
+ "yDS5SriyOZCQhP51Qm3I+4nJzURnCOwHHPpUTTeYME9DQr3G7FgXP73G4Y67vSpI7ocBy2HPzbf6V"
+ "lyHByMYParNtNKNjooJXjkVoRcfJK8gZ9+8n5SrDJH0qLzD5YLMQVPIJ/lUgjGFYttLcZHG0+9Ld2"
+ "/lRjd94nqehHtSC+g2O8kRvkLA9BUj3hGAD1HJx0NVIUcP8qkii8icHeoOD15oC5MHd9ybgCOTnji"
+ "nRTFernGcbRxVLIc5f6n3qUyK0rSYBJwMD6VLKRpo0bjqWI54NSeYI4wQM1UtGxwI8E9TnmpiWOfm"
+ "wPftU3KJ4rjDfO238auxyR3G3dHkr03GskAKvUHPerMLEEfP+dP1Jeprx2VuJPNjUszdVxnv2xTpH"
+ "t5ZgsjRg8/Kw649f0qtb3B/vAe4rb09re6g+yXMaSE5b5gDmp9lGRLirFGXTFlt8ypDFFjjPr9e1O"
+ "0zS7OeeSSKQSqGzIMZG76nrV660qO5uFiljV4UGU3E/L7cVbsLCPT0dYVIDckbsjNWqUUZ3sU59Cg"
+ "dSY5JY23buW3DP8ASsW2sbjzp2RiyBsEj5TVq812b7aoAEcSPhlBznB9a6IKrwAw7RlflOOKJUkwR"
+ "z0IL6kkcM5UIp3jr35H1o1tZYZvLhtJJo2UElRnB5qvrNvPZRLcqX8+aQiURjiuj0yY3WnxSHqVxk"
+ "jvUqirWG9Vc8+gnnWaZJdwIIO18/1+ldxpdyWso9x/eCPOPWuZOj2y6pKJJpraQu5Z3wyOM8AVcuo"
+ "rzTzFNbypPAIyU5wWx2xWi0ViltqdepBUEdKXFYtnrLC2zd2k0TgdMdRUv9v2fqaq5Li7mdqVtLNZ"
+ "vGIMsowEU9fc4rF0eR4NblSQMT2z1BGP6V3u1DztH5Vx3iiJdO1WDUINvzj51HYjjNZey0dik+h16"
+ "ShlBFR3KB1PY9j6VnWEq3SRypJtyM46g1pEOR1X86Sk2h8qTMwXBgl2N1zwe1U9fgTUrf7PceUXY/"
+ "unHymNv6j2rZkwB8zIPxqvvgBGZFJB470JyTHy3PL7vTpbeUpKhHOAwBwagikksZ1liJDL616fera"
+ "XoVZ2X5ehHBqk/hnSrpHLK+4jhkfofpVKaejJcGjG028sNSU/bMIWwXBPX6Vjy6VuYvbbmj3HaMdB"
+ "nitCw8PRxyTpdzY2v5aGNuCfX+VVZ7GSwunieTcMEDDEdelJWWzKtdaoqS6bNEiu7DazbeMmpJ9Fe"
+ "Ir/AKTDzzy2KvaOqAt5wyrNtG7n6mrkK6dITHLEoG/aGC+3WnzsXJc5eW2CEqZYyR/dbNXNNitvLe"
+ "WWXEiHIA5zWnPZ2MxYRQgtnrk81cm0DTo9JW5EjRu3OU/UYpOXNoHLY5q6ujf3TSSDpwAKvaTcXFo"
+ "/yxl4j95SMgr3B9qgBtrZyY4N754Z2z+lTJq92jAphfYKBVNaWQk9bnS22m26wmbSwPJlO4jPKe30"
+ "q3Lp1w1uR9rERPUqma5nSNXksrsBuImOSuOBmt6+8SWSRK1urSSddrDCis1e9jSytoZVxoslvayXT"
+ "SZVBkswwCfQVa0hkbSINwziVwCRyAQM1larrV3qm1ZCBGDxGvAFWbGdY0t4RkhQSee5qpXZK0OkO1"
+ "YcIcEDHFRmUsgB6iktJFkG2TBOMjmmSoQ2O1UlYGxZTvGQQSPWsXWSBCCAVNajvznb26is7U13xMW"
+ "79DmqRJgqHdiR2607yTuLHlT6daEyu7nnpVwKrJuRhnFAisturEhicg9fQVPHG0eUfBB6H0oiO84P"
+ "B9+9Wlw67T94dOaBBBErqY22tuGMkVL5IIMUgBA4U02EFQGAByeD2q1Hy2WHHqO1MCg9ltG6NsEdR"
+ "UF7h0HBB/iGK27q3ymFALkZHY1izxsjYYc+5pDMxg0fIwVPWlU8ZVckHOaklt225DD6VFCArYcZ+l"
+ "DQ0zQidXQSDKMP1qwhc4IAOeoquluyLvVSFPbNWUHyZ7VnYu5JsDEfJtXHWlaM54HT3qWBCeMZGPS"
+ "rIi3Lgr+dMRWgJzhSffNalqWAHHI6EGs8xYbJQg/WrEDhR8p5HrRsUmbdvqaW0RE74ywAY+9a6SK6"
+ "5VgR6g1xd8hurOWIMAWXg+nvVXTDc2qRbZnlDHL4zwBVc1kQ4ps1fFX2W0ntmUIpn3bgo64xz+taH"
+ "hu8862MYbcqEgHPSuR1u3lu3W4eby5YxhYWHY1Z8O3EljEcONzNjaBncaOfQIxWqNvxBI8+0WzsAf"
+ "vA9Dxwf6VT0nxE1nYrb3EeJY8Ag8ECi9uhMZVtTlhhwTwMdx+uaox2cWoXSttdmJClmNTzMIrozZF"
+ "5b3Mss0jOyNwYnQY/A9RTI5LMYaWNmQMVUK33AenHrWNdoINQuLdZmCo2AcbjRGz2tk6O2XeUSDjq"
+ "oB/xphdGz50STEZEy8qAxwfxp/mr/wA8YvyH+FctJfx+bJI0jKXZiigc9eK0Psl76n/vqjYNzd1ie"
+ "+tV/doBEoAVy3BPc/8A1qqaZaSTxxS3pEitG7tv5A56Y9KljN/p0CWwsxcRzMflmP3fxrQeIm3Nqk"
+ "b4lyhdACEFOXZElTQ9MjksluLaR4g+cxkZUHPb2rRlsZvLYmUFvRVxxU+n2/2GzWEksF5zjFTzSBY"
+ "+MEnoCcZ/GlyIOaRk3FoI5VjaRyZAdpyOw6YqC+sLU6fLKscsuEz+5lO7PsOhq1c4S4junjOYuvO4"
+ "YPWqyXKmW4ljkcwvjZkYC47UtEPU5uLTNTmw4juxnnlc7adfxapp9qZVnLIRhvlKMvvzXaJcgEbSC"
+ "cAkVDrEtvdaFdGR3WLYdxXqMVSSbC8kefW1+8gigXI2n73cknk1tXiMdjysJXx8vZjj29aytKsl8x"
+ "ZiTx94HsD3rX3xybS673PJI/hx71lJWZabZiSg20iAFigOfm5qrJcDzCISSMn+fausaGEwqJv3gzj"
+ "G0HqayprGGykkkGI0k6E8Yp3Q9RNIt2kUP1J6Zo1rU9tqumwkDyyd5HfnOM1Tl1v7MvkWZzj+PtVa"
+ "O0nvb6KJAxeYjJPp3NEY2d2JuysaGk6Ot0nnzOFiB9eWrSksIIciOIhf7w5q7bRQ2kTwIRtLDBIqK"
+ "WZEzvOcfmKqL1ItoZ0llFOu2QYA6OOtNXSYY8CSZn44wMZrTBSdQysCCOlDxhlGBkj8DV2EY+qwQ2"
+ "9uvlRKoJ/E1WsmXcu7GMj8KueJMBIFbryapWqoHDbsr60NDR0lriNiRliV7VYDsY8kZYnj0NUrWVh"
+ "IseRtK8HvmrwUGPHAHQGhCZCwJXd0H9aoX2DFsIxmtGQkdPXms68yMAjOTwfSmIwtm1yfyq5BGGGM"
+ "DB6Glnt9soC9/WpYYwASASBTAWKPJKlR7H1oMTbhhfmHP1qzHjnAye1SwxEk89OfzpAMKhoi4Xy2H"
+ "BA6H8KfbqzDbkED1p7cPzgpjB9aEQrnkDjII6GgCKWR4m8thkDo1PeBLmHBAVgODUW/z5AM5x156V"
+ "bBKoFyDjv3FMDAdHinKlRwcbfWnLCss6hYyDnp2rXu7bzT5i8P7dDUVvbOkmWB479qVgJFjQRmIrh"
+ "xVaVFVBkjjsTVvdmUuMNt5JHaqF3Lt3H5SG7UWBMmt3ZWwX2jtV5GxyGyaxYndmXC4+hrWijHl8jD"
+ "UrFkjgyKDgn0OagUGMgsCVJ5IomYqBtHXj8aiSUyAqeCRRYL2JYmK3QReuRtBrYh0CORhJLI28cnb"
+ "0zWGkm2dG3EEEDIHIrVmuXz8l7cr8ucFAMn8DUyaW4rmf4gtbdLiFUEhZcli4xn04p0W/yt3lQASn"
+ "aBGoBAxz0+tSGISSAzgvj+Jzk1KrOTMfNRjGvy7RwPwrJTTTEm+pUlhjjDxoyHomM55P8A+qkt9Vh"
+ "s5YLeyUcqTLIF+6e2M0xUJZw3f+IjtVTgOBHFGxQkA56VEZi5tSVAZGluZg3mMwGc4OTWjb20kjOI"
+ "ljldE2guM8Hr/Kq9t5jZBiDEHkdsetWnKqN+fLB6cdSOxx9atNlJEz6G0loy/ZrYMVwrKoBHHXPWs"
+ "jzJ/wC63/fVXvKhkLE3m0/e2hDxUflWv/Pyv/fo0XYjpbbTWU+ZPKZJD95jV5IgoxmolvYCmRImOg"
+ "wc04XUROA3P0roumTZk2KpXsKsczySeTwdqjgY9ana6RRnk/SqF5qOV2bAFbg55o0Gkytqdz9mtvM"
+ "hKlmQko4yGHbFctpF62pvNYGN2jlclMHBH41rXaiRFALdMEGs7SiljezxiMK6chqwlZpmnRJG3JdW"
+ "trbrHPLtUqFQZy3HGcisvVt91pEy2srMikOVHVsf0qVVgvbRJQvByec5HJzUunx2zE7UYZBUgZP1q"
+ "oaIT0ONtdSkt3I5YHA/CrMWsSRByIwd3Ymqmq2RsNSngOSEb5SRgle1R21vJcyCNCBnua0aRKbRcl"
+ "129kAXzAgH90D+dUpZpJxukkZ/qa17bw2JJdst1t9cLmtT/hGLS1h3vK8rehAApOyGm3ocpYWj3F7"
+ "GigkMwGSOK6dLF7fUZFiL7o48tLj5R6irEEI+yStEgWRTtT0BwapR3dzcXIhDkoxG49sDrUp82o7W"
+ "LU8n7rcG5J57cZqjJeEmMnjsTT7y4DzOBjaBgY9KihtmlKkEcevFO2o29DVtlV0JB5PZeMVJNOYrc"
+ "uSXb0x1qOMABRxuHfvTk+VSsjHB9atGdzE19zLNbjn7mcHsc0afC2CcZTGCKbMTdalvAJGePpWzDA"
+ "IYyGBUscYpsLgp2TxugPGOvpWixXYeQB6GqCwTZJIG0Hgj0q1KwYDbgkjOPUUgI5ZMSkAjHUVBOP3"
+ "e7OVHXHrSzxMsWc4I9aC7CJfl3ZO0+2elMCv5fmKJD2p8ceMnGcZBApEKrlcEEHp7GpASjK55JbDY"
+ "H60CAJhR3UnBz1FSAnkjnjGPWh0GDnO0jse9MilIU7hgg4P1FKw7jmdUkXByh4zT3UDAB6Dt6VGxQ"
+ "ds4OWU/zFI0qLhM844oEKsPlSb1IKng+ooeV1ICMWFQmUgbRyo56UhkUSADJ/2SeRTAtpKOnB9qgm"
+ "vvlCgYGPyqCRmMpC5zjsKSS1kjUO6nDe/SgBbNywkJ6bTyKzmdTKVb5gT3HStCAeRAzuQFcFRWS33"
+ "z1Jz1oA17WGPhhOmQPukYrTcRqoKHJPasfTUYOMgkex5rTuy6wk5I9sVJTKU7sYmIOQCCCD3pLYhy"
+ "DgnceMdqptOVYKO55q1HsXHOB7UxXHHcZ8HqGwBWg1tvfPmjnrl6zyv+kBScjgkkc1faFQwPnAHGe"
+ "lYVug0Si12xFgSy5zn0/wAahjlNxI55VW+UkL1FKICwyLhzxwMGo3gIYgs3sAaxVkGg2T/WuF+YYx"
+ "nOKje3jRQA79Ou6nG05JBbGM9KkeBUCg5Y9cZqlYVkVA4jJVQSD1JY1ZTU5oU+VYyBxyM1BNAVbhc"
+ "k+g6UqxuiZ8kt9Vqx3Jf7UlZv9VASeCdgFN/tL/phD/3zUcfmGUZgAHstS+fL/wA+4/75pXDUyLRN"
+ "URHk8shY8F89s1sxandYQPJzt5Cr0NasjwST+XkeU9tJyT1IfpVKEIsi7oFRSwBPXg1qotq472G/a"
+ "by4RdhK8DJPGT3pqwzswMkpPPT0rRvZPs1w8ccI2D7vriqy3jMhj2KFJ59afK9wuMljUMvUkj9akW"
+ "1Ej8RjPXNSoxBB8scf3u9LNKzD5dqnuUaocWgRSlWMRN5QAPTqak05pY2Obt0TtiMHFKZQij5CxJw"
+ "Cvb3q0kRm5+UL0z70k3sDOV8ZOX1VH3bj5K84Azyah0iPYynPPeq+qTG81qQKdyK2xcdwK17KBUxt"
+ "BBbqa26ak9TUhH7xSBjnNLdyO4IySB1BqRFVdxHamWk63E0m5QQnP1FJq5SdivDJjTpWPGWFUbcrF"
+ "5jAgAg1f1FN1osMLH5m3cDtWNN+7iZQeQetSlbQpsrPMQzdwwxn2rY0/ItFc4Kng1zm7zJgi101tG"
+ "yWG0dh0q0iGMlkIKoMMc8H0pLyUqiwbsvK2AfQcZqs4a2y7OSOufU+lEG2RXvWywQDbn1qiSzawL9"
+ "ulCgHYNoq9uLKw29CDVCy3qzSD/lo2eauxkbsOcE8UATMSI/0qYxoNnt0xWffTSwpGQuemfersMxZ"
+ "A45Ujt2oAjvseUcDOD+dZ0Vxuj5zuyelT3cwlnTZjHQ1mQM29gCDskOaBlrLSAZGD796WKVnMq5yO"
+ "q+o9RSopTKM25cnBqPAQsf4mHagRLPIApAzuHUe1RwS73bbngDPv61SeQlm5JK8H1q7aI2C4/LPWg"
+ "B29vNAzuJHUjqB2okceZnAOTT3jfcTH1IyRnpVeMFyWUMBnrQA8lzKyICRjtT/ACQPmIYnHHHNEat"
+ "54GxjnvVuJdgP3Q59e1ADrRGtYBI4AdjxntTZ545cowJ7gg1n3+oYQorliOAapw3/AJAMj85GMZoA"
+ "vSqJIEUsE2jPJzWYSPMIU5Hpikur8T8hBH64OaLZDIxYfMO9AG7py7YyWGQe2auXcgRAoLLGRhtp5"
+ "qrZfLACqYI5NFzPtUmVs+YegXpSKMwbHuejYHc1cVVCjLoNx4LZwPyqom0MzHcCehzU0gGQApUqOo"
+ "bNAiwsYFyhjK7TyDnIqw7yKwAmjyPaqUDCLBYkke3SpHvVDDAYcdkBrCpqxkz3MoOBKrr6AVCtwUb"
+ "IQZ9SKcupxhSpRjnvgCmx6hB5nz5VT3xnH5VlZ9gEkvJHXAIUZ6AUwXMxGMceuKfLeWyyny2Lrjgh"
+ "MUz7YJQMflimlqMYbqbI5PBz0p8eo3aDCzMPoKj8+EFt6SE+zYpDPZso/cuT7tVWAnOraimMXMg/G"
+ "l/tbUf+fl/zqoXhVs+V9Pmo+0r/AM8j+dAF+CC4njtUDANH5hb/AHd2c1pXqW7wFY2ZpGYYGMDqKR"
+ "H8phtO7arAbvUmmfaMuu9sgEEA961u0Fy3Myh3aSMSsTnJOPwpnmpjAhiTj+7Uvk3Ej58lj+OKV4n"
+ "iA8yIrn1bNKLtuJ6lZpCRgjJqAswHEaj3q3JcxDjBJ9lNR/K4+43J7jFX7SPYmz7lTfIBhFUUpZ/I"
+ "kc4+RCevHAqwY0QlgQy9MelZXiO4jt9LMUTAvMQvHYdTTjU1skHL1uc1p/N2D3FdNbFRHI4OOlc9p"
+ "JVHLHj3rXtJxJGU3DvUzlqXFGitwfIJyOR0psQVbK4Zc7tvOD0p6WazxqA2D9akuo/sdq6IMuRyTQ"
+ "noN7lGCZnZW3AFV2jPesjVpipKp0JqSWVoSFLHdwcfWm3USLCJ5j8zcgDuapITZW02FQxdzz1FdDF"
+ "JiEoXyGHAHUVzkFwAxJX5T+lWjeiEeZuHAOB60Lckl1LBulg34WMZb/aJ7fpVt5IpYjEOAME44APH"
+ "Fc/DctNM0rsS8jdP7vvWvbMiOqNyD09/f9KoRqW5zICw+6MmlUebOwGV2tjIqtHOQMZySgbP8/51b"
+ "TbDl2YZfkc+lAFiZQ0G2Trx17VWt22sVjJG3k1VmvCWf5s9xUdm+5yckL+ooAsTDzG3jgnnjpWbA/"
+ "l3Bk/vZDD1rWlG2Lb0JPHvWNAS8rgdwCM/XFAy9JKIZSOmcEccVELrzWGQFZf1pk28zqWwAQeOxxV"
+ "e0CvMHc4GT19aBFhV3xSTFDjHBHXNW7X50BVhkjJU8YqGOYnfFGvydDgdKmFwIRt4wvHHegCTy3Dl"
+ "gwCr+lSxhIhuP8XOBVP7S0jAH5hnoTTocuxOcgcikBdE5Y7gNqg4AqC9k2xZc/Mw6CpY9sfzNyc9C"
+ "OBWRql0rSfJ2GKAM25kBYgZGKrSs6qu4cdqV3O7mmzPvXGKYhI2LNggkVpwSm3P7tuSO4rKtZNr+1"
+ "a1rA1ychcAdyKBo3VdpLNCjKCeoPHNUpDM2fMPSrVvJCqLG6qdvVSOpovCvlLsUZycqBSAr+SFgXA"
+ "5J2j/AOvTJv3bqAct7fyp0fzDIPJOCuaYFO/rnJoGW45liUNu+ZucYzQt0ygsig+5UVKI5VQEQRfU"
+ "tzSJHeO/7sRrjnOa5W7saI21B8Y8pD9UxVc3bgk+SgB9quyPe9Hli/ErUXnzAndPGD7YNMCo1w2cm"
+ "GPP0pxuC0fESAj/AGakkkl6+crc9cCmB35JuET8DzQgK7zSY/1a/gtMFxIM4Q+3FTlgV5uW3+2cUq"
+ "xll5vAMdjnFVoGpXE8pI3Eg1J58v8AeNNMOckup989aPsyf3j/AN9CjQNTpGc4APAAI6evNVjGXcD"
+ "aze+2rYZQAFI/E0p89+Q4I/66YqnFPdoVx8FvMyf68IPdiKc1qVXLXq5/2cmkW23jL3KIOpA5p4jt"
+ "E5aUuOwGacYpBcgdUxjzX/DvUYEI/ickVO8lurAbD+AqOWRVz8wCnpheau6j0Yte5G0pzgA49K53x"
+ "VvzbM2QuGAz+FdGGGwsqjHrnBrC8UtutYVMbA7ydxOe1VzeQJeZzsMvlowB6+lW7W4IPXH41QjTfh"
+ "DxnvU0XycdTnGRUtJji7G1HfycKuc1pWuoz3N6kZG1F4bjrWZZRLIVYgkiuh0+3gkaOQZXy2O/jrg"
+ "VEbbGjuZN3aiS7knkOQzYQDsKzNXfasMRblRWteyFrhkX+9kD0FZHiVQHhbGDtxWpmZyuoHXiq8kp"
+ "kbAJwKiNAoJLEfUcn8KuJOyjAJI9+orPVjmrUMMkqfujuITe3PQZoA2LWbE3LBQyZAPp6Uy5u/MDE"
+ "OQvUZqnKylBszxzn/GmiCSRvL6sPegZILje2RyatR3TIMr0I9P0qq8IRY9rqxGeRxj2I9aryy7VIA"
+ "II7ZpAa6ag8hCFs4IxxU0aqJzHjrzWXpu2JhMckxnlSev+c1ZjvNsmTyOuaLjNWZB5boeoz2qmQkd"
+ "ttbDehX19ac16GTMedzkdjUNww2lVAHOSKYiKK4aF3wQM9aWWYsuecgVVcqp3MelPgmEjFsAgdqQE"
+ "9tI7MMY45yauLJtBBIz161jyTyCX5OBQZnYbRyxoA0brUSUKK2fp3rOZiVPy59acsYQhnIJHJAqGV"
+ "90h8teT+lADDFwWqJx8uM0kpkQ/eOD0pkatKevHfNAhIRh+Ohrft7kLCkaj5h3rn2BhlI647jvV+y"
+ "uAGB6UDR0DP+4Z5mAbHCkc1XTzUjXJJLnOKuyNbYXzG3yBckY6DFUp76PcmCOB+YpgJvwoBGGGeaI"
+ "uSp69/eoxKXJGBVq2Vt5I7DHapk9BokVkzho2wfU0NJGoyrOuO2Cf60OuznfL+FMBB6SMfUFQa53Y"
+ "pCCVd2d/J5HB/wAaGlcElChB9VApjMm7lQT9AKRtrH/V/qKVh3FLE5O0H+VN80Ff9WB+NNJKnCcUF"
+ "mIyR+tMBfMTuAPc80FYmHyyBSexIApMjAzGCPWm/LuyEBHvTESeQE5Mikezg/1puIv+ev8AKkwv90"
+ "fhS/J/dpAdNkIMllwfWkefPDFcegHX8arO+WIUqVPUZpUMZG3nP510RjDZszbY03SL8qpk9vm4p0c"
+ "kh+VwNx6AVGyL5hBAI/WnqjEjC7Rnkk809Y/CG+5YMEm3LYH409LYYB38n0H9agVwiAh2b8eKCu77"
+ "xP0FHPNrVhypdCRokP8Ay0DDuMZrnvE3lraqq5z5mRn6GtqeRuRDGDgdDxWL4hhlntI5PKKiM/N9D"
+ "Qld6lLTQ5Zc544q1BtIBBOc9KjjVk5B60QuyyYPc0COp0SESyhGHB4rWncx3ckYO1C2MVjaYsodHi"
+ "yWPJwO1N1LUXg1RjJhlGMEHr6/rmoitbmjehckjVrkuTjrWX4ij32KSY5U4pg1GR+cg5z+NWnhGoa"
+ "dIsknlsCcqR6DIrS5mckBxR3xR2GetGaBEiAbs+lW7eUwt5o7kqw7YNVYV3uByQSC2PQdasPGUumi"
+ "iV5FDHgDJIGaALUMzo5VURxINpDDIP8AhVpbciITgKN6FQGGNx9QfWqQVBG7xygMoBUE43fT3Ga1N"
+ "Ot7uW3bzlZ7ab5iY9pH4Dt+FIZnSkoDk7jkhs9RVQKzMGP3c4zWtd2IeBnR2JA+6eD9fcfyrOhimu"
+ "ALeEBv4iR2zxyfSgbJGnjHzIuAQR+OOtR28TzjaPvk4FXLbSJgUmbYyA4eMtzVxtLfyy0b/PnlSOM"
+ "HH8qnmQWZCUlsl8u4URyrk/NnIH4cVQuJX+Vh0I3A9jVn+zLhp3BkYRJ0ZurGq8unTsJDjaFYKBnl"
+ "icdPzp8yEZ88zyNyfyqWzn8uTDnAPepbjTnjGwffQ4f0HpipbbSWM+2ZW2k7Mr2PY/SmA9seYSuHU"
+ "DJ25o+YqGiUMG6evXH9auR6EYnjb7WOnzAdq07a2trYP5XzFup2571DkirE2n+EnmIF1MFAJLBDnI"
+ "xx9OapjS4La7nh3byrAZPoRWzBqAjjZNxy4wB+FaZW2mtriWaJELAFG7tgcUuYq3U4PW9MKwiaIZ8"
+ "sfMPb1rIs4/NfYWIB46V1sqSuxIBZT2GTxXJyq1rfOhyu1+4qou+hD0ZeuNHlW2MqndtXdjGOKq6W"
+ "0SymSYkIgz93P4Gumsb0lNhJ2NxwcqR6Vyl1G1peTQHgKxGPUZqkD7mpc3bM0jkFHY5A/wBk9qpF2"
+ "D7gMj09KIpWlZS33QMZJ7VaWAFwDj3x0oAi+1PlSoxgelbGm5mgZ2Ut82OuMVjMn748nGeK3LCHFs"
+ "vz8EknA71E9rFLYsfJnARj/utmnMkZGPIkZvc0gVhwpx77aBCx5Mh/I1lYBpsnxuKKg92qJ44xxkH"
+ "6ZpzooJyxJ+lR7GPTkUwE8tT2o8pT2NAUjsRSM2MZ7nApABRcY3EfhQEQ8bv0oBx1GKXPvQALGnXf"
+ "j/gNP8uL/np/47TM+1JuPpQBpSxtGMhkHrmiJJZhiO5iBPbft/pTXnVxjbIv0IFRrJFEcorE9ctLX"
+ "T9Xn2M/ax7l+PTp4yXdlc+okBqdYNmGlliUf3S+4msuS+lkwAqYHp1pouJx91wM1Sw0+5PtYmpNI7"
+ "fKgbaO6xgCo8vjJOPq1ZzvLIMSOx+hNIyg4/eEH3NH1drW4e1vsi9JMoTqpPsah37xySQRyuBjH51"
+ "UKqT05HdX604rnozj6gGqdJ20Yc6uVmsbQMQsb5P+0OBUS6TAXymR9TV9JADt3qe+MUySUB8/KB7k"
+ "rUeyfcfOuxLAZbewe3hIVn6y5GcdhXOarAyT4+Yqx45z9R+ea2XKynhifo+cVWu7UzQuiv8AMvzLm"
+ "hU2uoc6ZStULNlk6qStbUMhjiICcOOSQOc8Vn6SWkhcsBgEDJ7cVclDJ06fWpcJbj5kclMhjldD1V"
+ "iKjxitHWYGiu/MIwJRkfXvVFevPSmG4sTlDx3p0suWDIxVu+D0oEQb15qWXT5Y4Gl25VeCaAIhNK2"
+ "RuzuPPFdTpMM/2XNhIyjALwt1jfvj2IrlIlYyqE+9kbfrXXJeZubUyYjYptfbxtJ4yPoR0pMaE1YS"
+ "LAZSoD/dLA9DjvjpS2EUVlCo8tWcryxPXIFUtSuWIxuyXyTn0PrV3zPkEYjRiABwtHLfoNuxa81Cx"
+ "+Xvzg0jTJg/KcZzyarEchhCaZKMD5l4Pt0qHCIKTLgaFuWLUgWAkHJJBzk1Ujx0U5HrThhQfmH0pq"
+ "kwckWBawsDjaQTkk4p4i4G0Zx0waoMWY5UHH1o2zYwAfqD0pOD7hzF/wAkg8qx+tIWEZwFx9ap7p1"
+ "PDNj3NH2mRRjGT3OKThId0XVODkMw9cLip5r4yEB3OFGAA2KyjNM3Rc59jR5k69QTScWFzTFwpGN5"
+ "HFcz4lgxdpcp0lGDx3Faiytn+P6DFVtVQ3FoVO7cCCowOtEVYT1KOjeaCXVhtBwVp/iK1KmC7AP71"
+ "drE/wB4f/WqLT45oi26PPp9c1v6wi3eiSIwIeIb1/D/ACa02Y+hy9mGlcRp1PP610Z0+SCPzHKlGH"
+ "GOa53SXCXqEsAP9rpXbvEsljvbeMDJAPP/AOqmxI5wW5bcQM1rpH5UMUYBJC5bIzyasaNY+ddABMo"
+ "RzmjUoAt9OuCuHwPpWRpLRWIGLbR0UfWmeZnhSWPtTkjY9XHFOVSowXX8VNF0QM2EDJYj8RSGVlXA"
+ "diKbIATgOhqMgKcZqWA7zWB+8351DcqZkG3O9DuU56mn8ehpQyj+DJ+tCYxI5PNQP8oJHI9KduJP/"
+ "wBaq+7y5yCAI5DkY7GrAManlWP40MAJPekz7UNIgP3cA8daNw9BSAmNknck/U0xorWIfOwz6ZzSm1"
+ "Y5EsjZ9PSmpaQo2SuT65zXqc3eRy8ttkTlrXjbnGP7v/16a91GowsEhx3CinCFNvCihhkDNZvkvq2"
+ "UubsRfbDnAtpCO/OP5U8XcI+9asT74NIIcvljvHqOKkVAFAAB45JINDUHoC5houyzY8pUPpjrRJdM"
+ "xysaH2xSMiueDgg9qYE2jaEdgPaqSguhPvPqKuoSqCDboAfRaQ3oZQHgT8FxTShY/Llf97ilEYwMk"
+ "Z9jUXpdivf7ii6QdYVP1oSW1LbpIAO/WlKRgfe49etRSSwjjDH/AIDiqUab2Qm5LqJG1rDJMyr8rt"
+ "leenAzUou7YL82c+wzUKtG3bGfpSuoA5IH4Ueyj5hzyK+rrBe2LGNwWj+ZeME+ornIhlhmunYRZG7"
+ "JHoFrCuolt79lUfu2ORx2NRUhGK0KjJvctx2w2iQjAx3HWteaL/ilrgMc4QN06HcBVI3DGBBjIUcH"
+ "HWtVx52hXwUEK0XAPtz/AErDqbdDkrARteIko3KeOa0jOHuC2B97j1ArIgJEmcn8KvCVhl2OTjAzV"
+ "MlD7uTzJm6kDjNbnysAV344rnASfrXWogjRQoxwP5U436MUinJB3JbH0NJ5RCHbEx/HFXH3sOWoVf"
+ "lI70+W7vcVyglu5XmIjn1zinCDaD+7P4ir44AyKdmm3LuFkZ44GMD9abvk6Bf1xWgEVzjIppjU9qX"
+ "Mw5UUg0qjjr9aa7XB78HrV4IgHegp7/mKXN5BYziZcYDNn2pdxUZJ59xV5sA8j9KBtxT5vILFNJ+/"
+ "A/Cqmo3vygDacH0rSZ1XOVGPpVK8Eco+UZOfpUSl0GkUIb9ifuitNLkywkMhwy4qtBbqD8wHWtING"
+ "kfTpWbaNEchH+7uOoG1uprudInEy+TuGCOi5Nc2LYNIT5YOT3Fbmj23kyh/uFe4PNVcSRu6Y8cRJX"
+ "r1Ax1+tZmpszX8zEgEnJ28gGpdMB8wcnj3qHUGJvHbMR3H+Ht9aldipdyrvYdwaDOQff3FPMmBwo/"
+ "CoGDN8wBpOK6E3JPOd/4FOP8AZFG98fcT8RimAHHzBvwpuG/u1NkMeWOPuLTcbj93j2pOR1I+lByV"
+ "5IxRoAyaNHQq272471FE4YYbIdeGFTL8vOahut6p5sWNyjB75FACbg0nygtt6+maXzD/AHTUsMaiM"
+ "BW46896f5af3qQF3BI5YHPXNJuVe4z6A0R2+2IM0uD2VnBNKbeVUyFHr1ya7koPcxvJbDS0W4ELz3"
+ "ySab5mSSXA9sUwK+ed2e9ISVz97jsorVcvQzvIkeQ7dqlsdyFpvODyfpmoikknI81F/CnGGQN/rW2"
+ "jtmlyodx4yOvFKF2nocHvzTCJf9nHqTzSZdSBwf8AgRpKLHdCtGxyQuRnucVIOF4VR9DmmAuOCv5t"
+ "TtvfgfQ0crFdDGVmOdxGKQDHBJI61MAAQeR9aRhHnqSal3KIwFyGCqB704lV54P4UrBABhtv60jSx"
+ "KMu3H0oATCn74A+tZOvxJ5EUqqAyNt49P8AIrVa4hzyCfSs+/kjukCIBt3ZOetZylbdFJFXT5TcqE"
+ "DY2jcRnGcV06yxTaVcRJKNywsCMf7Nc9FDBDsKKCR3q9dXoFncuPlLIS3HU4xWV1ctHMW65ORV0Qh"
+ "yM9uTS6XbebFJITgpgD0PFSH5Vd2ODjCgirEQwqDLg8g/zrp1YqMEmubszm5TP94E10uR94Z5pxt1"
+ "BiZJ5GafnHftSck89KTcPr6VWiJFJ3dzS7cDOSaQFj1wBQ0qgZ3fpRcdgBIyQv0pM5HUk01pVcgZ6"
+ "U0uqkAECs3Idh+dvXrTHJHpio5J1U8sCffpSi4B4yBx2qOYdhwb1p+RjtmoTMCcb1z/AL1KuSCxK/"
+ "nR7RofKQSyLnBYDtVOdxvwpB9xVibodzJ+YFUZcAggqfpUc1x2J0lKAZWpZLhcADqfbFUVkw1Skhx"
+ "y35mgCyjYI9PrVxXCQ/e5NZaLGCMkZ+tWiAI/vYU+9MLFqCZlk/dyOp9mpWLK2CxJ65IqgshjbcAp"
+ "H0qRZvNbJO0+lSO1yyZMjAA/lTS7AEENj/eqNgir8zfypUZSM8n2AqkxCsxYYNG3jA3A08tGgBOR+"
+ "NOEyEZyv507CuQbSPX86TIH8JNPZk6BRn65pmcZyMVGwbi717hhR5nGMGmNJkcDmmhnbgAE/Si4DY"
+ "H2M0J/hOV9xUu7/ZNVpy/DKuHXpjv7U37b7SU7Jhc6ERnHKs35Uu0g8qR+AqTClcqWH0NCGQtjaXP"
+ "YFgK6/aX+JGPJpoyMZUkgkA+oprDadw6/SrZjYL0wT2zmon4IAKZ9DVc8Rcsit83+yR9aC248EZ+l"
+ "WAMsQVAI9DSmNV7En0ovFj1RWwDzx+VCqM8jB9cYqcpg9vp1xUbEDnBb6D/GncVhhQE/40bVC/MT+"
+ "HFPGWXO059yBQqiTdh8BVJOT/KpuOxHsTdkA/nTdmRjFTyQMiKxYfONwwc8VGFz0qvRiI/LOeTSSR"
+ "DdnGfaptgx701kB6E5+lJsaKUwAU8BfaqDxqDx39603t1PLt+JqjLGivhWzj2xXNOTb1NEhmxFTjv"
+ "71I0Ams5EDc7c4pRswAan8tFiJV+1TcZR0+zJQxCVkQ/McDqaszbXjEbKML32gE0+zXkvnA9Kc+GY"
+ "ninzMCtboizrtHeto4A6VlbBuytWlDuv3v1pKTuBZMhxgCmuQRk5B6VXMTjrLg/WomXJwZBx681Tk"
+ "Is748cyDj3qJmJH30x9ahxEPvOP50L5LuFV3JPYKM1LkMkdQVARxnvk0N5wUYRGqvOkcbkZlLehGK"
+ "asJxnbKvGc5FRdjJHjmb+ELSBBja6E++MUuREAd8nPuDSq8znKNn64pXbARfIHWNgKd5ltztgc+vN"
+ "O825UcLG2emGFOAunjIERDlsYpWKuQ77UHLW+D6E9Kry7ZXLBAFHvT7uC5hlMdxES4GcEZqNJTHG6"
+ "tBH8/AJXBWmkK41EwM7ePU8A05sugwOM5x3qLzGwFAZlB4XtTjJIRgxlV+lWh3JhCrKSFJcc49RUg"
+ "UsvYr7tiq8bY5jdgR3HWpOccDdnrmgCeIBjsMIZjwCrf/WqZ7dUBK8gdeRn8iKpxsw+9tI9CKd5rH"
+ "hdgJ7YqbXAk2RlsbZCT6YqRoY0i4Rt+epPIqKNpB1wfo1P3K5/iHt5nH8qaTQhpRjyqMR6kU6MyRE"
+ "OpjQ+4B/SnCRo+Imjz780wfaHb+LPsKT5luA13Zzywz1yFxSAkdCKvtbsbf5rZg//AD0aXH6GqZHR"
+ "TjPrUu7HsQ8s2M7c96VkwxUMDzgGrXkEtxt/xpDBg/MqH65qlTkTzIq/KuQxOfambIf7p/OrciIoy"
+ "wUfTNRZT1/ShxaFdM113qBuLE1MmHJABO3rmnTFI0LurqoGScVXtpE2bt+1pDvIIPGf/rYrq0XUjV"
+ "9C2VyOE2moXV0+aRUZf1qQM/8AC4P40F5O6mqeothwZXX5emOMU11dsNuwoHOBk1GT7SD6OaA2P4m"
+ "x74NQ7oZCZohISR8w6E07zWP3dpA5yDUhWFyNwzj2pjwQE5jIUj8RUcs+47xIxvYMzMv32AUjGAMV"
+ "XhkkN1NvlXyxgAds4qZ9tvaO7Nk5bbjuScCotPhYxeaxzuOQp9emapOQNIso2T9//voUblA+Zl4py"
+ "xjPzgc/jTSpU5QL/wB81fPJdCbIYSu7kgg9BScbsYXHpupWT5sug+uT/jQzRHH3eOe3+FQ6r7DUSn"
+ "clBK2XGT2Wq5A7irUpiYnBXn1XP9KpvF8+VbI9uKwlK5ew7jtS/KRyaUqMjacge1Skr5LKFwxHpSu"
+ "BDGCoBwcHpipFBbOAcio4oscuDntU8aPgnYv5U7jIwCpB28euKnUoePMQfUUxw7MAcbe+BR5UIP3p"
+ "PypNgSkQ9DKh+gNMaGJgD5yc+g6VH5SscB+PcYqQWvBHmx/iaVwGLFBG24ShsjoVzTUjtV5Z5Mjn5"
+ "VFPMH+2hHtmmNEw+6OapCIHG7LBnI9GxmlByACXwO1SFJDk0qxv1pMBgjjPVX/MU5BGP+Wefxpxjb"
+ "+8P5U5IyTglf8AvqkGohaELnyRn6D+tO89Nm0CdQeyACpktsqRuUmmvBIhyYg//AqNA1M25CmQlWk"
+ "H+8cmq3Poc/WtCWJy2WTA+tIpVRxESfpVICmit64NTY8oYcbiecqc4qwxZ+BGwHsKBBIf4KaQXI1w"
+ "eFUn6jFDI+MdKmFtJ3FP+zsO/wCtOz7BcpBGPJ61PGoHLK2fUYqyts/t+dOa2z/Ex/CjlbC5Ar+Xj"
+ "YCB74p8KRseVTrzk1MLRQSSSR9KNqR/d2j60eyfUOYWIRqWzsX0KjOaa0jgkhW+o4pCyn+NabvQdW"
+ "/KqUYrqS2xhkJOTGxPqaDM/ZMfUVIJ0HqfwppZW6Rke5OKJPTRgis0shPOR9BigkkDLk1OYyRn5f8"
+ "Avqos49c/WsbsojKE+9Gw+n6VJkk9TT/Kl/55t+tAzRu/nVYjtG9uflPQcmpk8tm6x+ud2MU4FTLk"
+ "k5UY6/59KSMqzSEt/FgZ+ldHsF3I9p5EqHGOpHsVNTOYWQqkTq3qRUCpCW52nP8As0CODts/KqVLz"
+ "BzXYQsoOGYfrSswxxgD88UnkwZzwD7E1IQjIF3naO2avll5E3RTuV82MqsyDPUginhEj3KoUqDwAw"
+ "6U9raE7cHPI79qdIkIXauzJ6ZUZFK0gujNlIkuVL8RRISBnqf8mrkKARIAD07U14rdVH3CrMEHH8I"
+ "6n9DT0VdoOVUnnAB4/WoXMim0KRjoD/Klxx0P51G0iIcGUfm3+NRtPERj52/4Ef603O24rDpY0fnz"
+ "APqwNVXiYHAw3uKkLoFx5fB9cGodkec4Yf8AAqwnJMpKw1gR95GH4U0sg705kQ9N34tUZhbPGKzKF"
+ "3J60oZOwppideNpx37090UQnIkL4+UYAFFhElvOInLAAnaQMjOKmkmmmTDSfJ6AYzWaqSZ5XH0qdG"
+ "cdVXPbNCSHcewUdTmk+isfwqOUOzfdQf7tS7lYDcZ8/Xim12AM46K2fpikd9oGVI+pppSNuNkhz6m"
+ "jZEgIKvz7iiwA04Hc0z7SD03UuIB/C/5ikxGfuqfzpiEM57Zo3se9LtXH3D/31SbR2XH1aiwDghP3"
+ "n/ClCDsB+VNwQP4f1pcnHABFAxwTHp+FPwgGf61EWYDIHPpimou4Eupz7U0ImLIo9PwpBMg70woq9"
+ "MFvQ0zapJ3bRVc1h2LH2hccMKat3GM5YsfZaqsmDwNw68UCQDHymlzMdi19rB6I38qcbkgcAfnUAL"
+ "Yzj8xSskezJcbvTbT5mTYl+0E8kqP1pBcP2zj6VWBUnjj61J5u37oH5UuZjsiRpWZfvPn0podQeUZ"
+ "z70qzv12rzTvObgBRn2zRePcBplbosYX8DUbNIepOPpUyyynICEmkMk38UZx9DRaL6i1GIMj5pdv4"
+ "VKsQAyJM1G1y5XDIPwWoWlYnoQPpQnFdAs2XPLGMHr64FL5K92I/KqYduu+nBxjDfN+NP2kewcpa2"
+ "Kv/AC0b/vql3L/z1f8A77NUeDwTSbfY1PtfIfKdEuCD8qgk0RqoUgoOpPBqRonPKT4X1Kg1CVnVd3"
+ "mxkAZJxiuz5GRbtVRXd9vKox6/h/Wq5ReAM0R+d5LN8p3ED8KT95/Egx/un+lSrJ3dx62AxjHQ/nR"
+ "5Y9/zpN3IGF/Mj+lMWQEFyF2nhR5g6etHNEVpDyijLZbCjkD3/wAmqFvgy3UuxwGfaD69hipJrkK7"
+ "4UkKoPDA88//AFqLZcCKMg7sl2wODzn/ABqZTXRlpPqTG3RphEAVSNck/j/9Y0445/dsfypkcgdn+"
+ "/8AO3GV6gcf40suxh/y1U+oBxSUutwaGNFGTzC4/GofLTnG8flQ0ZXkOzD3VhURHqKiUgSBwVOACa"
+ "jPmAY2fpT+Owpp2gdRWLsMj3SD+E/iKPNcHoKcPLPVhS7bfvilYYnmyd8CnAux6ZprRxq2Dx9eDTS"
+ "qnng49aLIQ9ZgT95fxpwl9x+VRBU/uD8qcCVP3aYDzMB1A/KnCTK5Cj8xUR+Y/Nmhoo+yD8aYEnme"
+ "oUfiKa0gB4CU1Yo/7qg/WkcbGwopgBk/2U/Ok80jtHSDdkZ6fyqeaCBIfMS5SRv7mw5p3DlK5uNx+"
+ "6v4Cjz8jAUflSNjkgAfpSEqMYI/OncVhSWI5GfrSDcCADigOueWH509SPUGpsOwoIJyz8/SnfPzg8"
+ "euKQH2z+FBfAxh8+y0WHYgZHzkuP8AvmmAPv5b/wAdq5IYEgDrLI0v9zyiMfjVc3KE8kn3xRYLEwu"
+ "JVwUVBgdMUyS482TdJFHnPYY4qJpUzkNx7cUxmTJI3tx1pWHcnihD7irkZPT0pWiC8Zz7moYZQCCM"
+ "D3qwZYmHzZb6CmmxaDRHD3bn6VMiwqeGH51Xd1JwufoRSpG7fcRie/y1aqW6CsWGKkf60j6UwuoHE"
+ "z/gaDbXAXm3fHrtqu8Mikkpg+mKTnfogsTeeuP9fL/30angmsyv75bh29RJVAxMyBgmPxo2svAcfn"
+ "U840W2lt85UuM9iab5sHq1V8zHqc4pQ7Y5x+VHtGFh8jIQMbse4pgwASAcU8+aU65UelQqgJ5HFQ3"
+ "cY8SLnnn8aXzx/d/WkCKGzn8xTsR/31/KlcDoWkAkdCcdOnPaoFMcrBS42KP++j/hU8kMYZ9i53Dq"
+ "TznFRtaqoz5jJxjIPSuu9XczaiWGQbE29xmm+WfemXUZMyqjkhEVTgccDnmiO2kkULFKFfsCeG/H1"
+ "pqpLawuRdxJkkKbEyGbj8O9KVMa8A4AwBioCt1G7BiuemGJFOiedX3yKpVD6kjPcn2FN1Gvshy+ZR"
+ "Zd2ojzVG9B93uTnpUocptLRKGfcFXr0+UfqaapuY1a4VDvlO7f356UwNKHgO07ohwPb3/H+VRzLqi"
+ "2n0LXkyQqFSFSAAM881E7ypwYgPzppvbrPp+FBvbg9T+lJuPZk6iGeQ8dB+NKpkY4DY+qio2nY8tE"
+ "h/DB/So95z2FYyLRZ8yVeRsOPaka6kI5WI/Vag8xugGSfQUwzYB+U/U1IEju8gwUh5/2altrAQp9p"
+ "n5/55Rn+I+p9hUumxx7WvLzi3j6D++3oKhvNUkupy7AKMYVR0UelAyGZZZGYybCxOScc0wIduCFIp"
+ "PPOeacJh3NO4hggb1J9qcNycHNP80f36B8x4INAhjLv65qVY8IPkU++KQjb97iniaPbjd+dAxAoP8"
+ "AyzX/AL5phXnhfyp6yRg8N+lOYKxwpNAEexmIxGePeneVKwx5YpDH3yaQZB5Y/nVK3UWo77KxHIUC"
+ "mRW4ZSPlypKkY9KUShf42/Oo0m2ztiQ4YA/j/nFaJx7E2ZaWFk+6UH/AadskxgyfpUH2n/ppn61J9"
+ "obH3l/Kq5qfYPeHqHXpIfyFDByTl2Oaj+0H+8tL9ox3T8qanT7CtIQwKw5GfqBURsoe+76ZqQ3Qx9"
+ "5B+FM+0n/nso+i0+eHYVpCfYof7p/Og2qDpkfjS+fjnzs1GbiQtw9L2kOw7S7j1tY+m2nG2APy8Uq"
+ "O5Gd/6U7zWA5Zv0pc8H0HaRE1qxOetMCKDhiVNTPKW43M1MI3dV49azk1f3UNX6jgqngSMfbNBtg3"
+ "dvzqFkI5XpRvK/xt+VP2i6xFZ9yQ2WB8pP0NQSxxwRF5AFUdak86XoGJFQXDPLGyONwPGDWcnGWyG"
+ "r9RYWilXMTEjp05qcRx45Jz71i2czwzeXhm5xgd61V3McGRVODgE4qZRaZVxxiUk4bntSMm1ep/Ck"
+ "VnBBDcippp7i4UCR9wHQYA/lU2HdFbGBzSfJ70/Bzik2H1FCEdLsRG3MrFc4BMhyfenrGsrAoAidx"
+ "94n/CorfzdoRjuZSFA4AIzjPHenSKyTt5rOY1zg5J+lQq0r6mvISZJkYPjqcFe1GTkCNW3noetQks"
+ "PllDzOPmU425/HqajnnkjucHdEgxgRr19ec11RxNlsZSo31LV1Kkdvm6XLjARgMsSeAD61W1NGgtv"
+ "s0aEMylfr61WyWkZ2n3KrZjVxjcR0Ofr/KmzXE6SsksoYoQmxhg5PJI/IVaxGpPsx10FjNvGqMZGY"
+ "N8nouM061dd00hLfNIVHHQD/6+aqPM5uZJUb5UGwt6cZx+Of0p1vdyxW0a71Axk5HOep/nWnt49ie"
+ "R2LNx9pLZgaPb7gf1qq0t8vUcf7KinNfy9FZGP+70pn2iZuhX8qidWMgjGS3I1Ys5ExYfU07EROBz"
+ "+Oaa0MsrfdBP1qT7BOgBC8n0Irn6mgjDH3M/jTo7VWhM04Kxr0x1c+gqaC2dFMl0jCMfdUclz7e3v"
+ "TJ5ZpWJeFwMYCqvCimMrXE73DKCQEUYRB0Uf571GkO7oKYvzsewHc9qe86hdkR49fWpAc8UaDHJf9"
+ "KQKvcCog2e9SqQVxmiwhQUOQACR14pwJHQY+lRwKYwSxBZjk+lShc9z9RVCDAccv8AnmnrZhh96o8"
+ "up64p63RUYIzilcZJ9mRBuy1IRzkZpv2rzP4aQTZbG3FMBzZxg1G2R83UUhmBO3nNPUHnCOfwzQA3"
+ "CkcgGkKR5B2jigHafuMB6FTTtykEYP5UAJ5ceB8opSdv3SQTxwaFlj25P8qaXRpVOThRkfWgQ9o1Z"
+ "sPk470fZo++aDIvc0olQdW/SgYhtY8cLmmeTGMgpj8al+1RD+KmfaI8k7gaYXE8qLoUNNeOMnjP40"
+ "G5APytkfSkEjMfWkA5VweDUoPrz+FRrzyTj8KmXbjqTS1AZhM424pMcfdNOZ17LmkDn6CnZgNYjON"
+ "ppm1R2qUkmopGIxSaAcsa7c5pNgHQ1EWOKillMSbu/apswM+cG2vVDuGzlt3TGac8peSJx8+3aSeh"
+ "5NJcqPnkkViSgVeO9IS7CFVQ/dG4jpxyP1rYRqRyJJnGdwPIJGRVC4acahH5ZOD1FNVvNVpVG35sE"
+ "k9wKTTop7q4yhDEBmG84zjsD+NJR6gajkhchh16H0pMe4qFJFdNjZViOjdahxJ6tWVhnVPBJFApj3"
+ "7VYEKRgq2Rkf1pWTdC6hy4kOR6r/8AWqSG6LRqyOTZudpUAlkPsT/nGarXrSLMYxhiDsA7n04HbHN"
+ "Yx1VzXmJ47poyYZ0Dpt2q7dR7H1xUEi4Yrbu4XITA6Hjnj25NMt7u2ikRLosJD8mQcoH9cduKkSJN"
+ "jtFMJGLlU+fOB3PrzV05KL2Fq1oJcxKsKNLKoRF2qMHDdj+NQFI5JYVlwYgrSlu/Pb8sUt9cZ0ufd"
+ "gKMEc4I/wA4qJHG6SJG3BmWJSf4l6n+dd0Yx6GTbC5skt7dVKtmZgCM925z/Oq0oijslkViGJwob+"
+ "LAHNX9QuhKlpA6hmaXg9CAAcj+VVpbR7tLRTjaQoI7qO9aWXVEXbsUI5GQhBFuY8896dLK8exCiq7"
+ "84B6D3qtdEC5aSM4Qn5VznA7CnxgZCqC0j9v6VyOKRp1J7mUCUIithQAW9T61Zt7byk8+5J5GY4wc"
+ "Fj6n0FTb4IvK3Rq1yqBCrdEI4yfX6VBPM8r5YByTySaWg2JJLNIxkZjn2OAB7Un78IWeSZQe27k/S"
+ "pSnlDe2MnkRjr9TUbsSC5OSeT7VLAqyCR+CpUelNED9sVbBbZytNAJX0+tCAriJhxg5oCOrdQPrUz"
+ "EjjNMII4JBH1qhDkZgRyuB2p4O4dTn0FQiME5DYoctEmdo47il1sBOiM7gENt9cVN9mTBOCabbuxT"
+ "+VOBbrzVBqILZf7hH40kkSKOOtSKx3d6SQFl4xmiwER9CopuBmkkZkUs2cD2p2G67T+VFmIcuR0LD"
+ "6MaXeyn774/3jTNw704OPTP40AI21iQzMwHI5qMRx47inkqHGeO1LlR1PNACIFB2qWJ9OtT7Gxgxt"
+ "/3zUI2Zzkj3p/m7eBM4+ppprqD8iKSFM8q4/CoTFF/t01r1jKYpGO7tz1qzDLEFwyjd/eNGlwuV/J"
+ "i9WqRBt47VIZQWHC8/QUjN3HSlp3C49XAGAdtOMiDAyCT7VA+OvGMetMQEsTnoKTY7lh2Uc5A/CmG"
+ "RS33qYQMfeJpu3jrzSbESMwxwxzUeCeTk0mKT5jU3YD+Fxt3E+9V7rmMyYGFI6/WpXcrGxAJYDpTJ"
+ "JEe2G87NwzjGe1NAFzF9ot2jJAJ5H1pLW7cwG3kCYXkAr909Dj9Km2M/zYI9qqXMLRP5pBwxwfYcD"
+ "NNS6AA221owGWHJPvmslOECjO5TkexrajYbZCWJAUY6YqlEySXDZU7mJBx046VcWA2SVvJIfAZcbR"
+ "7fWneZH/z1f86r6gqlF+bEoOxlH51F9jHo351VgO0tUlMChZWWMHa0YIbaRkg59+lRapPG9nFcRF/"
+ "NjGyVFONnPGfp0p2kzJK/kEEK24nccFTj/OKmkhSS8jhkxs2YuWXg8/dYkds4/EGuKG+pSZnoym3V"
+ "Ad8kmG3P2bB/xrV0yU29uFlUuMfuznDJ7c9etZS2KCXBmZFJKIrLwB9elX2YpsspCVb/AKadHHYfW"
+ "nLsUWdaVJbBJEKYmdYx9c5J/SoNMtlnjjiMmyaNd3J7t2/L+dVNQaCaIw2shbym+4x5UnC59xzSXc"
+ "ctnN9pjlLSscqqd1HHI/A1vTnZag1dEl/EU1JFlLFYkLkD1yKvtFGsPnI+SIv4D36Vlm4k1C+TIGf"
+ "KC9cFst1qZY0fzjAxhkiXeSfuu3cEfStnW1TIUOhQng82+KQJuKgA88Z7k+gFI08VqGjtzukb78xH"
+ "6KOw96fLKZogVGwHh8dWb3qEQ5IC8kngY5qJz5ncSVhkEZkOFByTn61ejt/szhmUNL2GeF+vvTAPs"
+ "gEYXdKPf7v/ANephLMcZjA29M8/nULcohdBGxdpCzH1NR/vm/u49aJiAxyDnvUDT7SyjBHrVWJuTt"
+ "5vfP4GnIM4DM1VFkX1NW0A2bvOX6d6kYkm4NwOKYgL9TU6xrIMmcZ7ACobtPItmkWYbs4C45NVcLM"
+ "R/kONppN7OCDgAjHSpILV5oYZHkUGQ42nqD71LNaGBM+bG5BwQKltBZkOnylEdDyVar6S+qVTgIUH"
+ "5ACT2pLmZraIuSu0euateQi+ZVAyVI/CkFzEf4wPqKoh3njVwNyHpimmMehFV7SSJcS3dSLcGGFGB"
+ "Dv8xHoOaukk9gRWEQoORkU5JWH3ZiPxq1WfVE8jNkqp/gH5UhiQ9UWs0XMwAzN+YqVL+XpujP4Vft"
+ "o9hcsizLbRbM7SDkd6RrRD8p6npz1qBrt5Bhtn4EioVuXaIBicjgYNS5wfQaUihqWUuAI3dAM7t3A"
+ "9vrVy0hnlGZWQcA7P4h9ap6mS481lLMBhQx6c1NZQyuUnlchlGF57e/rUe7bYrUZcW0sVzuLDexG0"
+ "etWIInlQtsPDFSM9xVW/knadHlYgQyDA/wA/SrdtczIzh8Dcd/A65p+49w1IraXzriWIwlHi75qby"
+ "5M9KijuWGozmNF+cDcSDyR3q4WxhtwyR/COlJxiHMyADkj26U+NQWcccGo53VBuCghgRkHofemRjl"
+ "9rZy3FZ8vYdyK8vhaz7PK3L3arClGh81DxjIB4zWbcod8jCMEM20E9SR1q1abxbqkrnczDB9utDjo"
+ "O5ZfC4AOT3pu8lSe2cUkjL5xJc8L3+v8A9aoBI6IPl3ZB/E1HKwLClN3zNwBUTRB5CnbYSCPWq6v+"
+ "96ny8jg8danLEXGM9F/rTtZgLFJKYVPDNj1x+dOkbNq5kG04OQTkCq8MrI0iscKHIyeabdyN5DrJg"
+ "gqcGlbUCCaVrQMvBDJsz6+hqpazATBXbCZBJFWdQjZrOOQ4xtBOO/FJp6xlGLD5gOlaq3KHUvG3hk"
+ "KgAkk5BHH41H5Enq1LHLmRgvXOAT6VLv8AepuwL+o2xhniuY8ROCS/OSGXH5juPrU+nh5LlZJdyPK"
+ "2AG5xDjg/n/On2l6mrRuDF5fmQgY6jr/Tb+vtUumS77gqdzu2USRjyqjtx9axeg1sNuYv9OeZkxEs"
+ "e8BwSCnTJx1/xAqr9saeQTmIMCuNq5HyL09wa1L2TzmnRR8yFSpP91uNp9s4Nc7OyRvMQGZZGV1BO"
+ "CAVOOfam11LvoSNcxTTRvbxPuTaFV8cvuGfrW9KYmhhSHayhchT95ADjGfzzXMZzcRPASkip5hz0z"
+ "kCtywlaPTtpY8nejADPY8/nUy0s0EWyhJJFaazHJE5kC5dcjGG9D7ZrWSFBcxyeYAsgOQRgEkfMPw"
+ "qgIUuNUfcqhhEGYgYG4k9vyrVtYJJLeW2kl3ZQlWxyrDv+VdEUmrie5imP7NdNCr+bn5GVRyf/r01"
+ "t1ocxsGZsgSZ6ew9/Wte8gcr+6dYi43llXk/jWGmAzWzElGOQe6t60Si7XJur2EQnOdw3Dn1qwpLD"
+ "cHJ+oxVaNEU/MCSD61LK6o4HzKvXApWaAjkjbdkk881TuECSHaDjFXTmQs6Hao7Hmob+B4okcuDu4"
+ "xitIxbJdiqoOQArZPSpfNK7VdG565NPn3RPFnBxVaSdpWA2gBeamwyUsgPKlQfWoJjG0ZbzNoJwBU"
+ "EskjOWbb8rVLKVaFFKLyewp8oieF/4RISVGc5piXn+kbCDj1qQW42hhwx4qIqY7hmO0kKR0qWkUaE"
+ "Ug7OuT6Uy/3eQxLggDG31pump5kG88YPam38oMUiY5BABP1oSswLenOzWq5IAXgbeAamdlKnJwR71"
+ "Fbo8SxxkgqFzimXALHAwO/Sk2FhSF4/eLg+tSC3jxnzkz9KpEc80/aCBnJoVwZL5RP3QH/OgRMDlo"
+ "uvtQi4wVZh+NT7pCAGkYj61aS6k6lcxkuMAhc4xjmnxKFkZQMH0prbo1XJzu46mmuds6MR1GDzRZA"
+ "JexxTApIpG1C2T2NR6XJJJbBcDEfy59ahuZVM0kbKfmG3g+g/+vTtGYC1PLBi3UGmloK5YvokliID"
+ "hXXlefvH0qaFkliVkJIYVHds6EAsX5BXJ6GpLcIlvH8g+7yaNAuUplP22IhtvDHn2I4qUsRwTxUhS"
+ "N1tpWHPmZOB1BzxUt4YoIxiMkb1AyexNNwC5TlXdEwyeBuH1x/9aktJSlqWkBDgjCgdfSo1jlnMwW"
+ "TaM7MewNOuA9vNJEG+YQ5Df5/GotYY+NfNeV35ZQRhei//AF6S1SRZWDtlR9zPbNV7WXYHQZPzjJP"
+ "csOtW1VlDkHnkc/h/9ekxj53XyZivLN8o4pksuUKbRu7ADvTpl2qq54DqP1pkR33DMScDgVNwK0qK"
+ "iAk5U8EjrTo5wGJ3kLs2HHc1akgD7lJ49hVKJPJvGSTDDnGB3xVppoRLFgPIhbdkBsn1qG4Yum1zz"
+ "0I21aaJYrpAv8QIPvWRMzSSnec7snilHUC1fTgW6RqBhhjA9sVDYsY5Sj9xgmqjtkgDORnJpdxTjO"
+ "atRA0I5BhYwPmJ3HB9zVryR7/nWbEAsaSSZO44GOO4/wAau4f+/UyKP//Z";
+
+static const char *easter_egg_photo3 =
+"jpegphoto:: /9j/4AAQSkZJRgABAQAAAQABAAD//gBtQ1JFQVRPUjogWFYgVmVyc2lvbiAzLjEwYS"
+ "BSZXY6IDEyLzI5Lzk0IChqcC1leHRlbnNpb24gNS4zLjMgKyBQTkcgcGF0Y2ggMS4yZCkgIFF1YWx"
+ "pdHkgPSA4NywgU21vb3RoaW5nID0gMAr/2wBDAAQDAwQDAwQEAwQFBAQFBgoHBgYGBg0JCggKDw0Q"
+ "EA8NDw4RExgUERIXEg4PFRwVFxkZGxsbEBQdHx0aHxgaGxr/2wBDAQQFBQYFBgwHBwwaEQ8RGhoaG"
+ "hoaGhoaGhoaGhoaGhoaGhoaGhoaGhoaGhoaGhoaGhoaGhoaGhoaGhoaGhoaGhr/wAARCAEZAXcDAS"
+ "IAAhEBAxEB/8QAHwAAAQUBAQEBAQEAAAAAAAAAAAECAwQFBgcICQoL/8QAtRAAAgEDAwIEAwUFBAQ"
+ "AAAF9AQIDAAQRBRIhMUEGE1FhByJxFDKBkaEII0KxwRVS0fAkM2JyggkKFhcYGRolJicoKSo0NTY3"
+ "ODk6Q0RFRkdISUpTVFVWV1hZWmNkZWZnaGlqc3R1dnd4eXqDhIWGh4iJipKTlJWWl5iZmqKjpKWmp"
+ "6ipqrKztLW2t7i5usLDxMXGx8jJytLT1NXW19jZ2uHi4+Tl5ufo6erx8vP09fb3+Pn6/8QAHwEAAw"
+ "EBAQEBAQEBAQAAAAAAAAECAwQFBgcICQoL/8QAtREAAgECBAQDBAcFBAQAAQJ3AAECAxEEBSExBhJ"
+ "BUQdhcRMiMoEIFEKRobHBCSMzUvAVYnLRChYkNOEl8RcYGRomJygpKjU2Nzg5OkNERUZHSElKU1RV"
+ "VldYWVpjZGVmZ2hpanN0dXZ3eHl6goOEhYaHiImKkpOUlZaXmJmaoqOkpaanqKmqsrO0tba3uLm6w"
+ "sPExcbHyMnK0tPU1dbX2Nna4uPk5ebn6Onq8vP09fb3+Pn6/9oADAMBAAIRAxEAPwD7+ooooAKKKK"
+ "ACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAK"
+ "KKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooo"
+ "oAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigA"
+ "ooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACii"
+ "igAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKA"
+ "CiiigAoorC8TeM/D3g2ye88VazY6RbKMl7qdU/maAN2ivlPxt+378MPDTSQ+H11HxRcLkA2kPlxZ/"
+ "33xke4BrwrxH/wAFJPFVy7r4U8H6Rp0f8LX08ly35LsH86AP0hor8mdR/b0+NF6xNtq+l6aD0FvpU"
+ "TY/7+Bqyf8Ahtz46793/CbLj+7/AGNY4/8AROaAP18or8mtO/b0+NFiwN1q2l6kB1FxpUS5/wC/YW"
+ "vRfDn/AAUk8U2zovivwdpOox/xPYzyWzfXDbx/KgD9IKK+VfBP7fnwv8TNHDr/APaPhe5fAP2yHfF"
+ "n/fQnA9yBX0d4a8ZeH/GNkl54W1iy1a2cZD206v8AyoA3KKKKACiiigAooooAKKKKACiiigAooooA"
+ "KKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAK5fx38RfDPw10"
+ "WTV/GmrW+l2ig7fMb55D/dRerH6V4v+0d+1v4f+CltNpOiCLXvGUifu7QP+6tc9HmI/9BHJ9utfl9"
+ "8QfiX4o+KOuzaz411WbUruQnarHEcS/wB1EHCgegoA+s/jD/wUG1nV3uNN+E1kNHs+V/tG6UPOw9V"
+ "Xov618c+JfF2u+MdQe/8AFOrXmrXbkkyXMxfH0B4H4Vi0+KJ55FjhRpJGOFVRkk0DSbdkMoru9K+F"
+ "mp3sQkvpo7EMMhSN7fiO351oSfCGcD91qkbH0aEj+tcbxmHi7OR9DT4czarBTjRdvNpP7m7nmlFdr"
+ "d/C/XLfPkCC6H+xJg/riqA+H/iEvt/s9h7l1x/OtFiaL1Ul95yTybMqcuWVCX3N/kczRXoFl8JtSm"
+ "UNe3cFrn+EAuR/IVePwgbHGrDP/Xv/APZVm8bh07cx2w4azepHmVF/Npfg2eY1t+GfGGveDb9L/wA"
+ "LaveaTdoQQ9tMUz9QOD+Nb+pfCzVrRC9nLDegfwrlW/I/41xVxbS2kzQ3MbRSqcMrDBFb061Or8Du"
+ "eXi8uxeAdsRTcfy+/Y+3vg7/AMFBtX0t7fTfi1YjVbPhf7StVCzKPVl6N+GK+9fA3xC8NfEjRY9X8"
+ "Gatb6pZuBkxN80Z9GXqp+tfhPXX/Dv4n+KfhZrsWs+CtVm066QjegOY5l/uunRhWp55+59FfOf7OX"
+ "7Wnh7422selasItB8Yxp+9smf93c46vCx6+6nke45r6MoAKKKKACiiigAooooAKKKKACiiigAoooo"
+ "AKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAK+RP2tv2sofhnbT+EPAdwk/iudMX"
+ "Fwp3LYqf/Z/btXbftYftEW/wP8GfZtIkSbxfq6NHp8PXyE6NOw9B0A7n2Br8kNS1K71e/ub/AFO4k"
+ "u7y5kMk00rbmdickk0AJqGoXWq3s97qVxJdXdw5klllYszsepJNVqKKACvTvhRoscn2nVJ0DMjeVC"
+ "SOhxkn+VeY17/4KsBpfhexRxtZ4/Of6tz/ACxXm5hUcKNl1PtOEMIsTmPtJLSCv89l/n8joaK43SP"
+ "iJp+o6lPZ3QFmVcrFI7/K+D69jXX+dHt3eYm3Gc7hivnqlKdJ2krH7DhcdhsbBzoTUkh9FV4b+1uG"
+ "K29zDKw6hJAcVYrNprc64yjNXi7jZE8xGTcy7hjKnBFcP4w0o6Zpk99ba1d20qDKI82Q59AK7qqV/"
+ "pFjqhT+0LWK52fd8xc4rWjU9nNN7HnZjg/rlCUIr3raNtq3npqeX+BvGOsXGsQWN1I97BKcHcMlPf"
+ "Ndj448KQ67p0k8MYW/gUsjAcuB/Ca6Gz0yy08YsbWG3/3EAq3W9TEJ1VUpLlsebg8nnHASweNqe1v"
+ "36el9dNz5fIwcHrRXR+OdG/sbxDcxou2CY+dF6YPUfgc1zlfUQmqkVJdT8LxWHnhK86E94totadqN"
+ "3pF9b32mXEtpeW7iSGaJirIw6EEV+n37Jf7WMHxRtIPCfjmdIPFtumIZ2IVb5R3/AN/1Hevy3q3pe"
+ "qXmiaja6jpVxJaXtrIssM0bbWRgcgg1ZzH750V8/wD7Kn7Qtt8cfBflanIkPi3SUWPUoAceavRZ1H"
+ "o3f0P4V9AUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFF"
+ "FFABWF4y8Wab4G8L6p4h12YQ2GnW7TSknrgcKPcnAH1rdr4B/4KG/F9l/sz4caRPgEC91PY3/AH7Q"
+ "/qaAPjj4u/EzVPi3491XxRrcjF7qQi3izlYIR9xB7AfrmuGopVUsQFBYnoAKAEorUt/DWsXQBg0y7"
+ "cHofJIFXF8EeIG6aXP+OB/WsnVpreS+87YYDF1FeNKT9Iv/ACMnTbQ3+oWtqvWaVU/M4r6TESrCIg"
+ "MIF2ge2MV4JYWV34W17TLjWrZ7ZBMG+bH3c4J49K98R1kRXjYMjDII6EV4uZS5nFrY/TuCqKpQrqa"
+ "tO6uno7W00+bPCfFnhC90O9mkWJpbN3LJIozjPY+lJp3gjX9TgV4rdo4W5XzX2gj6V7wyq4KuAwPY"
+ "jNL06VmsyqKCVlfudMuDMHLESqc8lF9F0+fY8is/hTqe4PNew25H90kkflXpHh7SJNE09bae8lvX3"
+ "Fi8hzj2HtWrRXJWxVWurTZ9Dl+R4HLJ89CLv3bb/wCB+AUUUVynuhRRRQBwfxR0f7Zo8d9GuZbRvm"
+ "x/cPX+leN19MX1ol9Zz20wykqFSPqK+cNQs30++uLWUYeGQqfwr6HLavNBwfQ/H+NMD7LFQxUVpNW"
+ "fqv8AgfkVqKKK9c/PTvPg58UNU+EHxA0rxRort/o77LqHOFngb78Z+o6ehAPav2o8JeKNO8a+GtL8"
+ "QaFMJ9P1G3WeFgexHQ+4PB9xX4N1+g//AATy+L7Twan8OdYuMmIG80vef4f+WiD+f50Afe9FFFABR"
+ "RRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFAFTVdRg0jTLzULxx"
+ "Hb2sLzSMTgBVGT/Kvw8+Kfje4+I3xC8Q+Jrx2c6hePJGCfuxg4QfgoFfqj+2Z40bwZ8AfEjwSeXda"
+ "oE06HBwT5pw2P+ABj+Ffj9QBoaJpE+ualDZWo+eQ8seigdSa900Dwpp3h6BVtYVefHzzOMsT/AEry"
+ "74Y3kNp4lCTkKZ4WjQn+9kH+le214GY1Z8/J0P1vg3AYV4Z4ppOd2vT09e4UUUV4x+jnjvxXZzrts"
+ "GzsFuNv5nNUPDXxAv8AQY0tpl+2Wa8KjHDIPY/0r0zxh4Si8T2qbXEN3Dny3I4PsfavFdY0K/0K4M"
+ "OowNGc/K/VW+hr6LCyo4iiqUt10PxzPaOY5TmU8dRbUZP4lt6P/gnsuk/EDRdU2q0/2WU/wTcfr0r"
+ "p45Y5lDwusinoVORXzFV+w1vUNMYNY3csOOwbj8qiplkXrTdvU68HxtVhaOKp83mtH9235H0jRXju"
+ "m/FTUrbauoQxXajqR8rV3nh/xxpniB1hic290ekUnf6HvXmVcHWpK7Wh9tgeIstzCShTnaT6PR/5f"
+ "idNRRRXGfRBRRRQAV4f8S7VbfxPKyDAljVz9cV7hXj3ju0k1vxtFYWhHmsiICegOMnNell75azb2s"
+ "z4rjCn7XL4xSvJzSXq7nAUVu614Q1bQsteWxaEf8tY/mX8fT8awq+jjOM1eLuj8Zr4ethpunWi4vs"
+ "1YK7X4SeObj4cfEbw94ltXKfYbxGmwfvRE4cH8Ca4qirMD99NNv4dU0+1vrRg8FzEssbA9VYZH86t"
+ "V4T+x94zbxr8BfDM88nmXWno1hMScnMZ2jP1GDXu1ABRRRQAUUUUAFFFFABRRRQAUUVwHjn42+A/h"
+ "pren6N478QwaHe6jCZrb7RHJ5boG2kmQKVXn+8RQB39FY/h3xXoPi6yN74V1rTtbtAQDNYXaToCex"
+ "Kk4NbFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFAHwl/wAFKfELQ6B4F0BH+W6u7m9kXP8AzzRUX/0"
+ "a35V+d1faf/BSS/aT4m+EbAn5YNBMwHu88in/ANFiviygB0cjwyLJExR0IKsDgg17b4I8Zx6/brbX"
+ "rBNQjHPbzB6j3rxCpba5ls50ntpGjlQ5VlOCDXLicPHEQs9+h72TZxWyivzx1i/iXf8A4K6H03RXF"
+ "+DfHUGuolpfssOoAYGTgSfT39q7SvlalOVKXLJH7vgsbQx9FVqErp/h5PzCq95ZW2oQNBewpPE3VX"
+ "XIqxRUJtO6OuUYzTjJXTPM9e+FUblptBm8s9fIlOR+Df4153qeh6hpEhTULWSE+pHB+hr6QqOaCK5"
+ "jMdxGkqHqrLkV6VHMKtPSeq/E+IzHhDBYpudB+zl5ar7uny+4+Y6mtZnt7mGWNijo4YMD0INe1ar8"
+ "N9G1Dc0CNZyHvEePyrkL74UajExNhcw3C9g+VNerDHUKis3b1PhMTwtmuElzQhzpdYv9Nz1yGQTQx"
+ "yKcq6hgR7in1zXgzT9X0vTmtNbaNxGQISr7iF9DXS181UioSaTuftWErSxFCNScXFtap7phRRRUHU"
+ "Fcvo3hZrbXb7WNRdZbmZyIQOiL/jXUUVcZygml1OWthaWInCdRX5Hdevf5dBGVXUq4DKeoIzmuI8R"
+ "fDaw1PfNpeLG5PO1R+7Y/Tt+FdxRVU6s6TvB2M8ZgMNmFP2eIgpL8V6PofNmqaVdaNeSWl/GY5U/I"
+ "j1HtVKvXPizYRvpdpfBQJo5vLJ9VIJ/mK8jr6rDVvb0lNn4NnWXLK8bLDxd1uvRn6Mf8E2vELXHhb"
+ "xnoLvkWl9FdopPQSJtP6x19zV+bf/BNu/aLx94xsgfln0qKUj3SQj/2evsz9oX40/8ACh/AkPik6I"
+ "deWTUIrM24uvs+3ertu3bG6bMYx3610niHrFFfLnwb/bU0L4k6V4y1fxRoq+DtL8L2kN1PO9/9p80"
+ "SMyhVURqd2VAAGSSwFeVj/gojqGv+PdK0jwn4OtYdFvdRhtBPqFwzTsjyBd+1MKhwc4y31oA+96Kr"
+ "319a6ZZXF7qVxFaWdtG0s88zhEjRRkszHgAAZzXxV8Tv+Ciug6HqM+n/AAy8PP4jETFTqN7MbeBiO"
+ "6IAXZfc7fpQB9u0V+cmi/8ABSjxLHeofEXgjSbqzLfMtldSwyAexbeCfwFfafwb+OHhP44+Hn1bwb"
+ "dOJbchL2wuAFntXPQMoJBBwcMCQcHuCAAekUUV8m/Gz9u3wl8NdVutB8HWDeMNatWMdxJHcCK0gcc"
+ "FfMwS7A9Qox23ZyKAPrKvze/4KUf8j34I/wCwRN/6OqzpH/BSnxDHfKde8DaXPYlvmW0vJIpAPYsG"
+ "BP4CvOP2yfjB4a+Nd94F8ReDp5DENMmhurWdQs1rKJclHAJHQgggkEGgD3b/AIJp/wDItfED/r+s/"
+ "wD0XJXsn7Y/xQ8T/CP4W6f4g8CX66fqR1yC3d3gSZXiaKYlSrgjBKryMHjrXjf/AATT/wCRa+IH/X"
+ "9Z/wDouSu0/wCCiX/JCdO/7GO2/wDRM9AH1Nol3Jf6Lp11cEGae1ilcgYG5lBP6mr9eC+NP2kPDXw"
+ "s03w94et7LUPFvjO60+BrfQdHi86fBjBBkxnYD9C2OcY5rz3Uv2u/iL4Uj/tPxz8A/EGk+Hl+aW7S"
+ "6Z2hT+82YQo/4EV+tAH15RXE/C34r+F/jF4Xi8QeCL77ValvLnhkXZNbSYyY5E7H8wRyCRXak4BJ7"
+ "UALRXy7qX7Yc2u6ld2HwV+GXib4hC1kMUt8kLWtsHHUBijH/voKaybn9sbxV4HuLeT4zfBnX/CejT"
+ "SBDqUE5uEjJ9QUVT9N2fQGgD64qrqWo2uj6dd6jqc6WtlZwvPcTOcLHGgLMx9gATVPwz4m0nxloFh"
+ "r3hm+i1HSdQiE1tcRH5XU/qCDkEHkEEHkV84/tCfH7V9CsvHngyD4XeLNTsv7Kntf7dt7ZjZ7ZbbJ"
+ "k3bMbU3nPP8ACaAPfvAXxA8P/Ezw8mv+C706jpEk0kMdx5TRh2RtrYDAHGR1xzXTV+en7Lv7Q2s/D"
+ "z4QaboOnfCjxf4rghurmQajpdqzwOWkJKghDyOh5r7F+D/xPv8A4paPqF/qngzXPBUlpciBbbWITH"
+ "JMNobeoKj5ecfUUAejUUUUAfmH/wAFHkYfGvw85+6fC0AH1F1dZ/mK+Pa+3/8AgpRpjReOfBGp4+W"
+ "40ma3z7xy7v8A2rXxBQAUUUUAOR2jYMjFWU5BBwQa9M8J/EsqEs/ETZA4S57/APAv8a8xAycDrX3d"
+ "8Hf+CfkfiTwtYa58R9budPn1CBZ4rGzQbokYZXex74I4HSsK1CFeNpo9TLszxWV1faUJW7ro/VHls"
+ "M0dxEssDrJGwyrKcg1JXUfGv9lzxT8ANPfxH4J1CbxJ4ViObuGWP95bD+8QP4fcdO9eWaB4+0vWgs"
+ "cjizuj1jkOAT7Gvna+CqUdVqj9hyribBZilCb5J9ns/R/0zq6KAQRkciiuA+tCiiigAooooAKKKKA"
+ "CiiigAoopksqQRvJMwSNBlmJwAKBNpK7PPfi3ehNMsbMH55ZjIR7KMf8As1eSV0fjXXx4g1qSaIn7"
+ "NEPLh+g7/ia5yvrcJSdKiovc/n7iDGxx+ZVKsHeOy9Fp+O59kf8ABONGPxa8RMPujQmB/GaOvo7/A"
+ "IKCLu/Z/J/u63aH9JB/WvDv+Ca+mNL4r8cakR8sFhbwA+7uxP8A6AK9w/4KCkj4Acd9btM/98yV1n"
+ "z5+avgLQfEPjjXLTwR4TLSXHiC7hjMG7ajsm4hnP8AdQM7H0Az2Ffob4F/4J6eDvDb6TqWu+J9b1D"
+ "XbGeK5L2vlQ2/mIwYAIyM23Ix97J9q+Y/2B4I5v2iNOeRAzQ6ZePGT/C2zbn8mI/Gv1ioA+DP+CiX"
+ "xevNPg0b4baLcNBHfQjUdXKNgyR7ysMR9tyMxH+ylYf7EH7Mfhzxl4dm+IPxF02LWbeS5e30mwuBu"
+ "gIQ4eZ16P8ANlQDwNrEg5GPIv27ZJpP2kPECzk7I7OyWHP9z7Oh4/4EWrmPAPwo+OfibwrY6n8PLH"
+ "xFP4dmMgtXstT8qI7XZXwvmDHzBs8daAP0P+N/7KfgDx94G1WHw/4Y0vQPEVvbPLpt5ptqlsfNVSV"
+ "RwgAZWI2nIOM5GDX53fsrfEi7+GXxu8M3cczR6fqd0mmalHnCvDMwTLf7rFX/AOA+9dN/won9po9d"
+ "L8Xf+Dn/AO21m+Hv2TfjRb+INKnn8DahBHHeQu8rTQ4QBwSx+ft1oA+9/wBtL4sXfwt+Ddyuh3DW2"
+ "t+IJxptrKjYeFGUtLIp7EIpUHsXB7V+bXwD+D178cviRYeF7WdrO02NdajdhdxgtkI3MB3YllUe7D"
+ "PGa+t/+CmDzCz+GyDP2cy6iW9N2LfH6E1zf/BNVbb/AITLx2z7ftg0y3EXr5fmtv8A12fpQB9NW37"
+ "FHwVg0QaY/hNp38va17Jfz/aC39/cHAB74AA9q/OT9pT4ISfAf4jy6DDcyX2j3cC3mmXEgG9oWYrt"
+ "fHG5WUgkdRg4GcD9na/N7/gpOR/wnngkd/7Im/8AR1AHcf8ABNP/AJFr4gf9f1n/AOi5K7T/AIKJf"
+ "8kJ07/sY7b/ANEz1xf/AATT/wCRa+IH/X9Z/wDouSu0/wCCiX/JCdO/7GO2/wDRM9AHo37M/wAJLb"
+ "wD4Gs9d1lRf+NvEdvHfaxqUwDSkyKGWEH+FEBA2jjIJ9Me2yRpLG8cqK8bgqysMgg9QRWX4X/5FrR"
+ "v+vGD/wBFitagD4e+H2lR/Ar9uDVPBvh8fZPC/jTTWu4bNeI4m2PKuB/svFMq+ivivuGvjP4m/wDK"
+ "QX4Vf9i+3/oN9X114g1/TvC2h6jrevXSWWmadbvcXU79EjUZJ9+nQcmgC5a2lvYwiGygitoQSwjiQ"
+ "IoJOScD1JJ/GqfiDQNN8VaJf6Lr9pHfaZfwNBcwSLlXRhgj6+h7HmvkzRfiL8a/2np7q8+FE9r8Mv"
+ "h3HM0MOsXcAmvbzacEoDkf987QDkbyQa3T+xxquqjzPFvxu+IGq3J5Zor4wpn2VmfFAGJ+wbf3WjQ"
+ "/E/4e3Vw9xb+FNfKWxY9FdpI2A9Bug3Y9WPrX0d8Y/wDkkfj3/sXdQ/8ASd6+Vv2FNGXw78SPjpo6"
+ "XU96unarDai4uW3SyiOa6Xe57scZJ9TX1T8Y/wDkkfj3/sXdQ/8ASd6APH/2Dv8Ak3LRP+v+9/8AR"
+ "zV9K181fsHf8m5aJ/1/3v8A6OavpWgAooooA+Kv+Cj/AIZa/wDh54V1+NNzaXqjwOcfdSZOv/fUaj"
+ "8a/Nav2k/aZ8Dn4g/BHxdo8Mfm3Qszc2y45MsRDqB9SuPxr85f2Wf2ddG+P13r9rrOvXOj3GmJG6R"
+ "wQq5dWOCTkjoaAPnWiv0g/wCHbXhf/odNU/8AARP8aP8Ah214X/6HTVP/AAET/GgD84Y22SK390g1"
+ "+6Pwy8VWXjb4feG9e0mRXtr7T4ZAFOdjbAGQ+6sCPwr8xf2jP2P9b+ClouuaHdS+IfDOds1wItsls"
+ "f8AbUZ+U+tdF+xV+0fH8Ndck8I+NdQMPhbUW3W8spytpOe/srd/fmgD9Pbu0gvrWa1vYUuLaZCksU"
+ "ihldSMEEHqK/Mz42fsO+MrP4myr8J9I+3+GNUk862czqi2JP3o3JOQoP3TzxgdRX6Z2l3Bf20VzZT"
+ "R3FvKoaOSNgysD3BHWpqAPxw+IXw++Jn7PF9aWnjK3jksrlcwTRyGe3k9VD4BBHpxVXSPilpd6FTU"
+ "o3sJT3Pzp+Y5H5V+t/xE+Hmg/FDwteeHfFtmt1Y3K8HHzxP2dD2YV+QHx5+BuufAzxjLpGro1xpk5"
+ "MmnX4X5LiPP6MO4rjq4OjW1as/I+jwHEWY5faMJ80e0tV/mvkz0K01C1v4xJZXEU6Hujg1Zr5lt7u"
+ "e0kElrNJC4/iRiDXSWHxD12xwDci5UdpVz+teZUyya+CVz7jC8b4eatiabi+61X6P8z3aivK7T4uS"
+ "jAvdPVvUxvj9DWzb/ABV0iTHnw3EJ/wB3NcUsFiI/ZPo6PEuU1tqyXrdfmd3RXJR/EjQHHNy6/WM0"
+ "9viJ4fUf8fbH6Iay+r1v5X9x3LOMuav7eP8A4EjqqK4e5+KejQg+Qk859Am3+dcvq3xUv7pWj0yBL"
+ "RTxvPzN/hW0MFXm/ht6nnYnibKsNG/tOZ9o6/8AA/E9N1jX9P0KAy6jcLH/AHUHLN9BXkHivx3d+I"
+ "S1vbg2tjn7gPzP/vH+lcvdXc97M013K80rdWc5NQ17WHwMKPvS1Z+a5vxRisyTpU/cpvp1fq/0X4h"
+ "RRSqpdgqglicADua9E+OP0u/4Jy+GW0/4a+ItdlTa2qap5cZx1jiQD/0ItXTf8FBv+SAD/sOWn/oM"
+ "leq/s4eCT8P/AIL+E9GlTy7lbNZ7gY/5aSfO36mo/wBoj4NSfHX4e/8ACKQawmht9uhu/tL2xnHyB"
+ "ht27l67uue1AH59fsBf8nC2n/YJvP8A0Fa/V2vkr9nz9i+5+B3xFh8WzeMotbWOzmtvsq6YYCd4Az"
+ "u81umOmK+taAPz2/4KK/Ce9/tPRfiTpVu01i1uum6qUXPkurEwyN7MGKZ6Aqo7iuL/AGQf2ttN+EO"
+ "mTeDfiHHcHw3LcNcWV9BGZGs3bG9WQcmMkbvlyQSeDnj9MdW0mw17TLvTNas4L/T7uJori3njDpIh"
+ "GCrA8EV8V/Eb/gnNoOr30178NfEs3h5JGLf2ffQm5hQnskgIdR9d596APfY/2rfgzJai5X4g6QIyM"
+ "7WLq/8A3wV3fpU/w8/aV+HfxU8ZXHhbwJq02q38Fm940v2R4oSisqkAuAScuDwMYzzXxnB/wTa8bN"
+ "Ni48Y+Ho4c/eSOd2x9Co/nX0D+z5+xdY/BHxbb+LLrxbeazq8MEkKww2q29uVddrBgSzN6jleQKAL"
+ "n7c3wuu/iH8G31DRYGuNU8M3P9oLGgy0lvtKzKB7KQ/8A2zr87f2f/jJefA34kWXie2ga9sWja11G"
+ "0VtpntnILAH+8CqsPdQDwTX7WEBgQwBB4INfGnxm/wCCf/h/xpq1zrfw21VPCl5cuZJtPlgMloznk"
+ "lNpDRZPYBh6AUAejxftt/BSTRP7TbxVJHJs3GxbT5/tAb+5tC7c9s7se9fnL+0b8Zrv47/EO58TrZ"
+ "zWOi20a2OmwycmOFSzDeRxvYszEDpnHOM19B+Hv+CbPiWW/T/hK/Gmk2lgGy50+CWeVh6DeEAPvz9"
+ "DXuPxG/Yg8OeIvhx4b8GeBNTXwxDpF7Jdz3k9p9qmvJHQKzSEMnzfKvsAMAAUAcB/wTTI/wCEa+II"
+ "zz9us/8A0CSu1/4KJf8AJCdO/wCxjtv/AETPXb/sxfs33H7PFp4lt7jxJH4hXWZLeRdlibfyjGJAe"
+ "rtnO8emMV0H7R3wTl+PfgK28MQa0mhNDqUV79oe1NwCESRdu0MvXzM5z2oA9J8L/wDItaN/14wf+i"
+ "xWrVTSrI6bpllZF/MNtBHDvxjdtUDOPwq3QB8afE3/AJSC/Cr/ALF9v/Qb6vQv24ftx/Zv8Uf2bvK"
+ "+dZ/atnXyftCZ/DO3PtW/4o+A0viP9ofwn8V111LeLQdPNmdMNoWabInG7zd42/6/ptP3ffj1rXNE"
+ "0/xLo1/o+uWsd9pt/A9vcwSDKyRsMEH8DQB53+zVc6ZdfAT4ePoJjNquiW8b+XjAnVcTA+/mB8++a"
+ "7zxV4p0nwV4e1HX/Et5HYaVp8LTXE0hwAo7D1JPAA5JIAr5X039lv4q/CS/vYv2fvijDpvh27lMo0"
+ "vW7bzVhJ9DsdSenzBVJwM5rodL/Zf8U+ONWstS/aQ+IVx43trKUTQaDZQ/ZbDeOhkChd/02qfUkZF"
+ "AHm/7AmvnxV46+NWutEbc6tf2175R6p5st0+Pw3V9c/FWzl1D4YeNbS2UvNPoN9HGoGSWMDgD864n"
+ "4W/Ar/hWXxO+IXi201eGew8Wyxypp0dn5X2QqzHAbcQw+duiivY2UOpVgGUjBB7igD5h/YFv4Lv9n"
+ "mxggkVpbPVLyKZQeVYuHAP/AAF1P419P18gyfsqfET4aeKtY1P9nH4h23hnR9Xm82fSdSt/MiiOTw"
+ "uUdWAyQDtDAcZPWvb/AAT4a+I2g/DfV7Hxv4qg8U+MJzPJb3ttbrbrGGQBI14C5BDEMVAywyOKAPT"
+ "6K8p+Dml+K9N/tIeKZb+S2YL5Zvp2kYv3ChvmGOQx+6xwR3ooA9TliSaJ45VDI6lWB7g14h8Bfg14"
+ "X+Feu+NP7Ft5E1m5v2aZ5Hz/AKO53xhB2Xkj8K9yrjfFQPh/WLDxNEP9HUC01ID/AJ4sflc/7rfoa"
+ "AOyopFYOoZCGUjII7iloArahp9rq1jcWOpW8d3Z3EZjmhlUMrqRggg9RXwR8Z/+Ce11d6tPqnwbv7"
+ "SG1nYu2lX8hQRE9o5ADx7Hp61+gFFAHin7Lnwr8S/CL4ZxaF431JL/AFFrl5hHFMZY7dD0jViBn1+"
+ "pr2uiigArlPH/AMN/DPxP0J9G8baVDqdkxyocYaNv7yMOVPuK6uigD4v1r/gnF4HvLppdF8S61psL"
+ "HIhby5Qo9ASM/mal0z/gnH4At8HUvEev3p7gNFGD+SV9l013WJGeRgiKCWYnAA9aAPlUf8E+/hMI9"
+ "p/totj73245/lXNSf8ABOLwSdcS4j8S6yukAfPaERly3tJjgfhn3r3LQf2n/hZ4j8VTeG9M8VWp1K"
+ "OQxjzcxxSMDghHPDV12v8AxW8G+GNa0fRtb8Q2NtqesTLBZQGUFpHJwOnTJIAJ9aAPne9/4J4fDC4"
+ "gKWmoa9aSY4dblW5+hWuOf/gmtohuw0fjnUBaZ5RrJC+P97OP0r7qooA/Hb9qr4M+H/gd4907w74V"
+ "1G71CObTEurj7Wyl0dndeqgDBC5xXhVe1ftaeKz4v/aB8a3Svvgs7z+z4ecgLAojOP8AgSsfxrxWg"
+ "AooooAK9f8A2ZfhrJ8UPjD4f0oxF7G2mF5enHAijO7n6kAV5BX6j/sH/BtvA3w/l8Xazb+Xq/iIBo"
+ "Q64aO1H3f++jz+VAH1rHGsUaRxqFRAFUDsBTqKKACiiigAooooAKKKKACiiigAooooAKKKKACiiig"
+ "AooooAKKKKACiiigAooooAKKKKACorq1hvbaa2uo1lgmQpIjDIZSMEGpaKAOO8J3c2i30vhPVpGea"
+ "2jM2mzuebm0Bx17vGSFb6qe9djWH4o8PDxBZRfZ5jZ6nZyC4sLtRkwygEZ91IJVl7gkVH4X8SHWop"
+ "rbUIRZazZkJeWpOdrf3lPdD1B/rQB0FFFFABRRRQAUUUUAFVtQsYNTsbmyvFL29zE0Uqg4yrDBGfo"
+ "as0UAfHPjn/gnp4H1PTpG8Balf+H9UU7ommlM8RPoQeR9Qa5b4U/sE63ovj3SvEXxJ8UW+pWuk3Md"
+ "zDb2pd3maNgyBmf7q5AyBmvu+igArI8UeJNO8H+HtS13XbmO00/T7d55pHbAAUZx9T0A7k1rkgDJ4"
+ "Ar8wv24/2gW8d+J/+EH8MXhPh7RpD9raNvlubkdc+oXoPfNAHybrmpy61rWpancMXmvbqW4dj3Z2L"
+ "H+dXNH8G+I/ENu9xoGgarqtuhw8tpYyTKp9CVBFdN8Nvgl44+LF2sPgvQrm8h3bXumXZBH9XPFfqv"
+ "8AsyfBvUPgh8No/D2t6hDqF9LcvcymAHy4y2PlBPX60AfkLP4I8T2uftXhzWIMdfM0+Vf5rWfJoup"
+ "QkiXT7uMjrugYf0r978D0Fc74q19dJjhs9Pt0vtavSUs7Yjgnu7eiL1J/CgD8oP2V/gJefGX4jQR6"
+ "nbSxeGdHZbnVJWUqGGcpCPdiPwAJ9K/VGL4h+D9Nv00GDVIIZLZltgiRP5MTDAEZkC+Wp6DaWzWl4"
+ "Y8MR+HtMmiaTz9QvHM19dbcNNMwwW9gAAAOwAFfP0vwi8SReI5RHo8ExMbWiXBVBE8bF83DP97eN+"
+ "ceo6dDQB9QUVBY25tLK2t2cyNDEqFj/FgAZqegAooooAKKKKACiiigAooooAKKKKACiiigAooooAK"
+ "KKKACiiigAooooAKKKKACiiigAooooAK5zxJ4afUpYdS0eYWOuWg/cT4+V17xyDup/TtXR0UAc94c"
+ "8UprDy2F/CdO1u1ANzZSHkD++h/iQ9mH0ODXQ1ieIfC9n4ijheZpbS/tiWtL62bZPbt6q3Qj1UgqR"
+ "wQaxofFWoeG5UtPHcSLETti1i2Qi3l9PMXkxN9cqex7AA7SimRTRzxrJA6yRsMqynII+tPoAKKKKA"
+ "CiiigAooooA+TP2xv2m4Phlok/g/wncCTxZqMOJnRv+PKJh94/7RHQfjXzp+y9+x/f/E+eHxd8So5"
+ "7Pw0X82G3fKy35znJzyE9+9faOrfsofDLXviNdeO9d0q51PV7mVZpILm6Z7YyAABvL/AcEke1e1Qw"
+ "x28SQ28axRRqFREGAoHQAUAUdD0HTPDOl2+maBYwadYW6BIoIIwiqB7CtGmTTR28TSzyLFGgyzOcA"
+ "D61x03ijUPE0j2ngWNRBnbLrFwhMEfr5S8ea3/jo7k9KANLxH4pXSJYdP02A6lrl0ubazQ8henmSH"
+ "+BB3Y/QZNJ4a8Mtpck2o6tOL/W7sf6RcYwFHaNB/Cg/Xqas+HvDFl4cimNuZbm9uWD3d7cNvnuH9W"
+ "b+SjCgcAAVtUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUAFFFFABRRRQAUUUUA"
+ "FFFFABRRRQAUUUUAFFFFABTJYo543jmRZI3GGVhkEehFPooA4+TwXPpEjT+CtQbSiTuazlBktXP+7"
+ "1T/AICfwpB4yvdIIj8XaLc2YHBu7NTcwH3O0b1+mD9a7GgjIweRQBnaTr+l67GZNH1C2vlX73kyhi"
+ "p9COoPsa0awdV8FeH9alE2oaVbPcL92dU2SL9HXBH4Gs//AIQee1/5A3inX9PX+49yl2v/AJMJIcf"
+ "QigDrqK5L+x/GUPFt4q06ZfW80Mu35xzxj9KUad457+I/Dv8A4Ts//wAm0AdZRXJ/2P4xm4ufFWnQ"
+ "r62ehlG/OSeQfpTT4Hmuv+Qz4o1/UF7ot0tov/kusZx9SaAN3Vtf0vQow+sahbWSt90TShSx9AOpP"
+ "sK58+Mb7Vzs8I6LcXanpeXqm2gHuAfnb6YH1rT0rwXoGiyNLp2lW0c7ffmZN8je5c5JP1Nb3TpQBx"
+ "8XgubVpFuPGl+2rMDuWzjHl2qH/c6v/wAC/KuujiSGNY4UWONBhVUYAHoBTqKACiiigAooooAKKKK"
+ "ACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAK"
+ "KKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooo"
+ "oAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigA"
+ "ooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACii"
+ "igAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKA"
+ "CiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKAP//Z";
+
+static const char *easter_egg_photos[NUM_EASTER_EGG_PHOTOS + 1];
+
+/*
+ * fedse is based the general implementation in dse
+ */
+
+static struct dse *pfedse= NULL;
+
+static void
+internal_add_helper(Slapi_Entry *e, int dont_write_file)
+{
+ int plugin_actions = 0;
+ Slapi_PBlock newpb;
+ Slapi_Operation *op;
+
+ pblock_init(&newpb);
+ slapi_add_entry_internal_set_pb(&newpb, e, NULL,
+ plugin_get_default_component_id(),
+ plugin_actions);
+ slapi_pblock_set(&newpb, SLAPI_TARGET_DN, (void*)slapi_entry_get_dn_const(e));
+ slapi_pblock_set(&newpb, SLAPI_DSE_DONT_WRITE_WHEN_ADDING,
+ (void *)&dont_write_file);
+ slapi_pblock_get(&newpb, SLAPI_OPERATION, &op);
+ operation_set_flag(op, OP_FLAG_ACTION_NOLOG);
+
+ slapi_add_internal_pb(&newpb);
+ pblock_done(&newpb);
+}
+
+/*
+ * Attempt to initialize the DSE file. First we attempt to read
+ * the file and convert it to the avl tree of DSEs. If the
+ * file doesn't exist, we try to create it and put a minimal
+ * root DSE into it.
+ *
+ * Returns 1 for OK, 0 for Fail.
+ */
+static int
+init_dse_file(const char *configdir, Slapi_DN *config)
+{
+ int rc= 1; /* OK */
+ Slapi_PBlock pb;
+
+ memset(&pb, 0, sizeof(pb));
+
+ if(pfedse==NULL)
+ {
+ pfedse= dse_new(DSE_FILENAME,DSE_TMPFILE,DSE_BACKFILE, DSE_STARTOKFILE, configdir);
+ rc= (pfedse!=NULL);
+ }
+ if(rc)
+ {
+ int dont_write = 1;
+ dse_register_callback(pfedse,DSE_OPERATION_READ,DSE_FLAG_PREOP,config,
+ LDAP_SCOPE_SUBTREE,"(objectclass=nsslapdPlugin)",
+ load_plugin_entry, NULL);
+ dse_register_callback(pfedse,DSE_OPERATION_READ,DSE_FLAG_PREOP,config,
+ LDAP_SCOPE_BASE,"(objectclass=*)",
+ load_config_dse,NULL);
+
+ slapi_pblock_set(&pb, SLAPI_CONFIG_DIRECTORY, (void*)configdir);
+ /* don't write out the file when reading */
+ slapi_pblock_set(&pb, SLAPI_DSE_DONT_WRITE_WHEN_ADDING, (void*)&dont_write);
+ if(!(rc = dse_read_file(pfedse, &pb)))
+ {
+ slapi_log_error( SLAPI_LOG_FATAL, "dse",
+ "Could not load config file [%s]\n",
+ DSE_FILENAME );
+ }
+ }
+ return rc;
+}
+
+void add_internal_entries()
+{
+ /* add the internal only entries */
+ int i;
+ for(i=0;i<NUM_INTERNAL_ENTRIES;i++)
+ {
+ Slapi_Entry *e;
+ char *p;
+ p= slapi_ch_strdup(internal_entries[i]);
+ e = slapi_str2entry( p, 0 );
+ internal_add_helper(e, 0); /* 0 writes file */
+ slapi_ch_free((void**)&p);
+ }
+}
+
+
+static int
+egg_char2nibble( unsigned char c )
+{
+ return ( c < 'A' ) ? c - '0' : 10 + c -'A';
+}
+
+/* decode in place (output is guaranteed to be smaller than input) */
+static void
+egg_decode( char *s )
+{
+ const char *pin;
+ char *pout;
+
+
+ pin = pout = s;
+ while ( *pin!='\0' ) {
+ *pout = egg_char2nibble(*pin++)<<4;
+ *pout |= egg_char2nibble(*pin++);
+ *pout^= 122;
+ pout++;
+ }
+ *pout= '\0';
+}
+
+static
+void add_easter_egg_entry()
+{
+ Slapi_Entry* e= NULL;
+ char *src;
+
+ easter_egg_photos[0] = easter_egg_photo1;
+ easter_egg_photos[1] = easter_egg_photo2;
+ easter_egg_photos[2] = easter_egg_photo3;
+ easter_egg_photos[NUM_EASTER_EGG_PHOTOS] = NULL;
+
+ src= slapi_ch_strdup(easter_egg_entry);
+ egg_decode( src ); /* twiddle bits */
+ e= slapi_str2entry (src, 0);
+ if ( NULL != e )
+ {
+ internal_add_helper(e, 1); /* 1 tells it to not write these entries to the dse file */
+ }
+ slapi_ch_free((void**)&src);
+}
+
+static int
+dont_allow_that(Slapi_PBlock *pb, Slapi_Entry* entryBefore, Slapi_Entry* e, int *returncode, char *returntext, void *arg)
+{
+ *returncode = LDAP_UNWILLING_TO_PERFORM;
+ return SLAPI_DSE_CALLBACK_ERROR;
+}
+
+/*This function takes care of the search on the attribute nssslsupportedciphers in cn=encryption,cn=config" entry. This would get the list of supported ciphers from the table in ssl.c and always return that value */
+int
+search_encryption( Slapi_PBlock *pb, Slapi_Entry *entry, Slapi_Entry *entryAfter, int *returncode, char *returntext, void *arg)
+{
+
+ struct berval *vals[2];
+ struct berval val;
+ char ** cipherList = getSupportedCiphers(); /*Get the string array of supported ciphers here */
+ vals[0] = &val;
+ vals[1] = NULL;
+
+ attrlist_delete ( &entry->e_attrs, "nsSSLSupportedCiphers");
+ while (*cipherList) /* iterarate thru each of them and add to the attr value */
+ {
+ char *cipher = *cipherList;
+ val.bv_val = (char* ) cipher;
+ val.bv_len = strlen ( val.bv_val );
+ attrlist_merge ( &entry->e_attrs, "nsSSLSupportedCiphers", vals);
+ cipherList++;
+ }
+
+ return SLAPI_DSE_CALLBACK_OK;
+}
+
+/*
+ * This function protects the easter egg entry from being seen,
+ * unless you specifically ask for them.
+ */
+int
+search_easter_egg( Slapi_PBlock *pb, Slapi_Entry *entryBefore, Slapi_Entry *entryAfter, int *returncode, char *returntext, void *arg)
+{
+ char *fstr= NULL;
+ int retmalloc= 0;
+ char eggfilter[64];
+ sprintf(eggfilter,"(objectclass=%s)",EGG_OBJECT_CLASS);
+ slapi_pblock_get( pb, SLAPI_SEARCH_STRFILTER, &fstr );
+ if(fstr!=NULL && strcasecmp(fstr,eggfilter)==0)
+ {
+ static int twiddle= -1;
+ char *type, *value, *copy;
+ char *errmsg = NULL;
+ int vlen;
+ struct berval bv;
+ struct berval *bvals[2];
+ if (twiddle < 0) {
+ twiddle = slapi_rand();
+ }
+ bvals[0] = &bv;
+ bvals[1] = NULL;
+ copy= slapi_ch_strdup(easter_egg_photos[twiddle%NUM_EASTER_EGG_PHOTOS]);
+ if ( (retmalloc = ldif_parse_line(copy, &type, &value, &vlen, &errmsg)) < 0 ) {
+ if ( errmsg != NULL ) {
+ slapi_log_error( SLAPI_LOG_PARSE, "dse", "%s", errmsg );
+ /* the memory below was not allocated by the slapi_ch_ functions */
+ slapi_ch_free( (void**)&errmsg );
+ }
+ return SLAPI_DSE_CALLBACK_ERROR;
+ }
+ bv.bv_val = value;
+ bv.bv_len = vlen;
+ slapi_entry_attr_delete(entryBefore, "jpegphoto");
+ slapi_entry_attr_merge(entryBefore, "jpegphoto", bvals);
+ slapi_ch_free((void**)&copy);
+ twiddle++;
+ /* the memory below was not allocated by the slapi_ch_ functions */
+ slapi_ch_free( (void**)&errmsg );
+ if (retmalloc) slapi_ch_free( (void**)&value );
+ return SLAPI_DSE_CALLBACK_OK;
+ }
+ return SLAPI_DSE_CALLBACK_ERROR;
+}
+
+int
+search_counters(Slapi_PBlock *pb, Slapi_Entry* entryBefore, Slapi_Entry* e, int *returncode, char *returntext, void *arg)
+{
+ counters_as_entry(entryBefore);
+ return SLAPI_DSE_CALLBACK_OK;
+}
+
+int
+search_snmp(Slapi_PBlock *pb, Slapi_Entry* entryBefore, Slapi_Entry* e, int *returncode, char *returntext, void *arg)
+{
+ snmp_as_entry(entryBefore);
+ return SLAPI_DSE_CALLBACK_OK;
+}
+
+/*
+ * Called from config.c to install the internal backends
+ */
+int
+setup_internal_backends(char *configdir)
+{
+ int rc = init_schema_dse(configdir);
+ Slapi_DN config;
+
+ slapi_sdn_init_dn_byref(&config,"cn=config");
+
+ if (rc)
+ {
+ rc= init_dse_file(configdir, &config);
+ }
+
+ if(rc)
+ {
+ Slapi_DN monitor;
+ Slapi_DN counters;
+ Slapi_DN snmp;
+ Slapi_DN root;
+ Slapi_Backend *be;
+ Slapi_DN encryption;
+ Slapi_DN saslmapping;
+
+ slapi_sdn_init_dn_byref(&monitor,"cn=monitor");
+ slapi_sdn_init_dn_byref(&counters,"cn=counters,cn=monitor");
+ slapi_sdn_init_dn_byref(&snmp,"cn=snmp,cn=monitor");
+ slapi_sdn_init_dn_byref(&root,"");
+
+ slapi_sdn_init_dn_byref(&encryption,"cn=encryption,cn=config");
+ slapi_sdn_init_dn_byref(&saslmapping,"cn=mapping,cn=sasl,cn=config");
+
+ /* Search */
+ dse_register_callback(pfedse,SLAPI_OPERATION_SEARCH,DSE_FLAG_PREOP,&config,LDAP_SCOPE_BASE,"(objectclass=*)",read_config_dse,NULL);
+ dse_register_callback(pfedse,SLAPI_OPERATION_SEARCH,DSE_FLAG_PREOP,&monitor,LDAP_SCOPE_BASE,"(objectclass=*)",monitor_info,NULL);
+ dse_register_callback(pfedse,SLAPI_OPERATION_SEARCH,DSE_FLAG_PREOP,&root,LDAP_SCOPE_BASE,"(objectclass=*)",read_root_dse,NULL);
+ dse_register_callback(pfedse,SLAPI_OPERATION_SEARCH,DSE_FLAG_PREOP,&monitor,LDAP_SCOPE_SUBTREE,EGG_FILTER,search_easter_egg,NULL); /* Egg */
+ dse_register_callback(pfedse,SLAPI_OPERATION_SEARCH,DSE_FLAG_PREOP,&counters,LDAP_SCOPE_BASE,"(objectclass=*)",search_counters,NULL);
+ dse_register_callback(pfedse,SLAPI_OPERATION_SEARCH,DSE_FLAG_PREOP,&snmp,LDAP_SCOPE_BASE,"(objectclass=*)",search_snmp,NULL);
+ dse_register_callback(pfedse,SLAPI_OPERATION_SEARCH,DSE_FLAG_PREOP,&encryption,LDAP_SCOPE_BASE,"(objectclass=*)",search_encryption,NULL);
+
+ /* Modify */
+ dse_register_callback(pfedse,SLAPI_OPERATION_MODIFY,DSE_FLAG_PREOP,&config,LDAP_SCOPE_BASE,"(objectclass=*)",modify_config_dse,NULL);
+ dse_register_callback(pfedse,SLAPI_OPERATION_MODIFY,DSE_FLAG_POSTOP,&config,LDAP_SCOPE_BASE,"(objectclass=*)",postop_modify_config_dse,NULL);
+ dse_register_callback(pfedse,SLAPI_OPERATION_MODIFY,DSE_FLAG_PREOP,&root,LDAP_SCOPE_BASE,"(objectclass=*)",modify_root_dse,NULL);
+
+ /* Delete */
+ dse_register_callback(pfedse,SLAPI_OPERATION_DELETE,DSE_FLAG_PREOP,&config,LDAP_SCOPE_BASE,"(objectclass=*)",dont_allow_that,NULL);
+ dse_register_callback(pfedse,SLAPI_OPERATION_DELETE,DSE_FLAG_PREOP,&monitor,LDAP_SCOPE_BASE,"(objectclass=*)",dont_allow_that,NULL);
+ dse_register_callback(pfedse,SLAPI_OPERATION_DELETE,DSE_FLAG_PREOP,&counters,LDAP_SCOPE_BASE,"(objectclass=*)",dont_allow_that,NULL);
+ dse_register_callback(pfedse,SLAPI_OPERATION_DELETE,DSE_FLAG_PREOP,&snmp,LDAP_SCOPE_BASE,"(objectclass=*)",dont_allow_that,NULL);
+ dse_register_callback(pfedse,SLAPI_OPERATION_DELETE,DSE_FLAG_PREOP,&root,LDAP_SCOPE_BASE,"(objectclass=*)",dont_allow_that,NULL);
+ dse_register_callback(pfedse,SLAPI_OPERATION_DELETE,DSE_FLAG_PREOP,&encryption,LDAP_SCOPE_BASE,"(objectclass=*)",dont_allow_that,NULL);
+
+ dse_register_callback(pfedse,SLAPI_OPERATION_DELETE,DSE_FLAG_PREOP,&saslmapping,LDAP_SCOPE_SUBTREE,"(objectclass=nsSaslMapping)",sasl_map_config_delete,NULL);
+
+ /* Write */
+ dse_register_callback(pfedse,DSE_OPERATION_WRITE,DSE_FLAG_PREOP,&monitor,LDAP_SCOPE_SUBTREE,EGG_FILTER,dont_allow_that,NULL); /* Egg */
+
+ dse_register_callback(pfedse,SLAPI_OPERATION_ADD,DSE_FLAG_PREOP,&saslmapping,LDAP_SCOPE_SUBTREE,"(objectclass=nsSaslMapping)",sasl_map_config_add,NULL);
+
+ be= be_new_internal(pfedse, "DSE", DSE_BACKEND);
+ be_addsuffix(be,&root);
+ be_addsuffix(be,&monitor);
+ be_addsuffix(be,&config);
+
+ add_internal_entries();
+
+ add_easter_egg_entry();
+
+ slapi_sdn_done(&monitor);
+ slapi_sdn_done(&counters);
+ slapi_sdn_done(&snmp);
+ slapi_sdn_done(&root);
+ slapi_sdn_done(&saslmapping);
+ } else {
+ slapi_log_error( SLAPI_LOG_FATAL, "dse",
+ "Please edit the file to correct the reported problems"
+ " and then restart the server.\n" );
+ exit( 1 );
+ }
+
+ slapi_sdn_done(&config);
+ return rc;
+}
+
+int fedse_create_startOK(char *filename, char *startokfilename, const char *configdir)
+{
+ const char *config_sub_dir = "config";
+ char *id = config_get_instancedir();
+ char *realconfigdir = NULL;
+ char *dse_filename = NULL;
+ char *dse_filestartOK = NULL;
+ int rc = -1;
+
+ if (configdir!=NULL) {
+ realconfigdir = slapi_ch_malloc(strlen(configdir)+1);
+ strcpy(realconfigdir, configdir);
+ } else if (id!=NULL) {
+ realconfigdir = slapi_ch_malloc(strlen(id)+strlen(config_sub_dir)+3);
+ sprintf(realconfigdir, "%s/%s", id, config_sub_dir);
+ }
+ slapi_ch_free_string(&id);
+ if(realconfigdir!=NULL)
+ {
+ /* Set the full path name for the config DSE entry */
+ if (!strstr(filename, realconfigdir))
+ {
+ dse_filename = slapi_ch_malloc( strlen( realconfigdir ) +
+ strlen( filename ) + 3 );
+ sprintf( dse_filename, "%s/%s", realconfigdir, filename );
+ }
+ else
+ dse_filename = slapi_ch_strdup(filename);
+
+ if (!strstr(startokfilename, realconfigdir)) {
+ dse_filestartOK = slapi_ch_malloc( strlen( realconfigdir ) +
+ strlen( startokfilename ) + 3 );
+ sprintf( dse_filestartOK, "%s/%s", realconfigdir, startokfilename );
+ }
+ else
+ dse_filestartOK = slapi_ch_strdup(startokfilename);
+
+ rc = slapi_copy(dse_filename, dse_filestartOK);
+ if ( rc != 0 )
+ {
+ slapi_log_error( SLAPI_LOG_FATAL, "dse", "Cannot copy"
+ " DSE file \"%s\" to \"%s\" OS error %d (%s)\n",
+ dse_filename, dse_filestartOK,
+ rc, slapd_system_strerror(rc) );
+ }
+
+ slapi_ch_free_string(&dse_filename);
+ slapi_ch_free_string(&dse_filestartOK);
+ slapi_ch_free_string(&realconfigdir);
+ }
+
+ return rc;
+}
diff --git a/ldap/servers/slapd/fileio.c b/ldap/servers/slapd/fileio.c
new file mode 100644
index 00000000..f3e90b10
--- /dev/null
+++ b/ldap/servers/slapd/fileio.c
@@ -0,0 +1,336 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+/* fileio.c - layer to adjust EOL to use DOS format via PR_Read/Write on NT */
+
+#include <stdio.h>
+#include <string.h>
+#include <sys/types.h>
+#include <errno.h>
+#include <stdlib.h>
+#ifndef _WIN32
+#include <sys/param.h>
+#include <unistd.h>
+#include <pwd.h>
+#endif
+#include "slap.h"
+#include "pw.h"
+#include <prio.h>
+
+#if defined( XP_WIN32 )
+
+#include <prinit.h> /* PR_CallOnce */
+#include <string.h> /* memmove, memcpy */
+
+#define g_EOF (-1)
+
+static PRInt32 PR_CALLBACK readText(PRFileDesc *f, void *buf, PRInt32 amount)
+{
+ auto PRInt32 size = *(signed char*)&(f->secret);
+ auto char* readAhead = ((char*)&(f->secret)) + 1;
+ if ( size == g_EOF ) {
+ f->secret = NULL;
+ return 0;
+ }
+ if ( size > amount ) {
+ return 0;
+ }
+ if ( size > 0 ) {
+ memcpy( buf, readAhead, size );
+ }
+ f->secret = NULL;
+ while (1) {
+ auto PRInt32 len = amount - size;
+ auto char* head;
+ auto PRInt32 rval;
+ if (len > 0) {
+ head = (char*)buf + size;
+ } else if (size > 0 && '\r' == ((char*)buf)[size-1]) {
+ head = readAhead;
+ len = 1;
+ } else {
+ break;
+ }
+ rval = PR_Read( f->lower, head, len );
+ if ( rval < 0 ) { /* error */
+ return rval;
+ }
+ if ( rval == 0 ) { /* EOF */
+ if ( size ) {
+ *(signed char*)&(f->secret) = g_EOF;
+ }
+ return size;
+ }
+ if (head == readAhead) {
+ if ( '\n' == *readAhead ) {
+ ((char*)buf)[size-1] = '\n';
+ } else {
+ *(signed char*)&(f->secret) = rval;
+ }
+ break;
+ } else {
+ auto char* tail = head + rval;
+ auto char* dest = NULL;
+ auto char* p;
+ for ( p = head; p < tail; p++ ) {
+ if ( *p == '\n' && p > (char*)buf && *(p - 1) == '\r' )
+ {
+ if ( dest == NULL ) { /* first CRLF */
+ dest = p - 1;
+ } else {
+ auto size_t len = (p - 1) - head;
+ memmove( dest, head, len );
+ dest += len;
+ }
+ head = p; /* '\n' */
+ --rval; /* ignore '\r' */
+ }
+ }
+ if ( dest != NULL ) {
+ auto size_t len = tail - head;
+ memmove( dest, head, len );
+ }
+ size += rval;
+ }
+ }
+ return size;
+}
+
+static PRInt32 PR_CALLBACK seekText(PRFileDesc *f, PRInt32 offset, PRSeekWhence how)
+{
+ f->secret = NULL;
+ return PR_Seek(f->lower, offset, how);
+}
+
+static PRInt64 PR_CALLBACK seek64Text(PRFileDesc *f, PRInt64 offset, PRSeekWhence how)
+{
+ f->secret = NULL;
+ return PR_Seek64(f->lower, offset, how);
+}
+
+static PRInt32 PR_CALLBACK writeText(PRFileDesc *f, const void *buf, PRInt32 amount)
+{
+ /* note: buf might not be null-terminated */
+ auto PRInt32 size = 0;
+ auto char* head = (char*)buf;
+ auto char* tail = head + amount;
+ auto char* p;
+ for ( p = head; p <= tail; ++p ) {
+ if ( p == tail || *p == '\n' ) {
+ auto PRInt32 len = p - head;
+ auto PRInt32 rval;
+ if ( len > 0 ) {
+ rval = PR_Write( f->lower, head, len );
+ if ( rval < 0 ) {
+ return rval;
+ }
+ size += rval;
+ if ( rval < len ) {
+ break;
+ }
+ }
+ if ( p == tail ) {
+ break;
+ }
+ rval = PR_Write( f->lower, "\r", 1 );
+ if ( rval < 0 ) {
+ return rval;
+ }
+ if ( rval < 1 ) {
+ break;
+ }
+ head = p;
+ }
+ }
+ return size;
+}
+
+static PRInt32 PR_CALLBACK writevText(PRFileDesc *fd, const PRIOVec *iov, PRInt32 size, PRIntervalTime timeout)
+{
+ auto PRInt32 i;
+ auto size_t total = 0;
+ for (i = 0; i < size; ++i) {
+ register PRInt32 rval = PR_Write(fd, iov[i].iov_base, iov[i].iov_len);
+ if (rval < 0) return rval;
+ total += rval;
+ if (rval < iov[i].iov_len) break;
+ }
+ return total;
+}
+
+/* ONREPL - this is bad because it allows only one thread to use this functionality.
+ Noriko said she would fix this before 5.0 ships.
+ */
+
+static const char* const g_LayerName = "MdsTextIO";
+static PRDescIdentity g_LayerID;
+static PRIOMethods g_IoMethods;
+
+static PRStatus PR_CALLBACK closeLayer(PRFileDesc* stack)
+{
+ auto PRFileDesc* layer = PR_PopIOLayer(stack, g_LayerID);
+ if (!layer)
+ return PR_FAILURE;
+ if (layer->dtor) {
+ layer->secret = NULL;
+ layer->dtor(layer);
+ }
+ return PR_Close(stack);
+}
+
+static PRStatus PR_CALLBACK initialize(void)
+{
+ g_LayerID = PR_GetUniqueIdentity(g_LayerName);
+ if (PR_INVALID_IO_LAYER == g_LayerID) {
+ return PR_FAILURE;
+ } else {
+ auto const PRIOMethods* defaults = PR_GetDefaultIOMethods();
+ if (!defaults) {
+ return PR_FAILURE;
+ } else {
+ memcpy (&g_IoMethods, defaults, sizeof(g_IoMethods));
+ }
+ }
+ /* Customize methods: */
+ g_IoMethods.read = readText;
+ g_IoMethods.seek = seekText;
+ g_IoMethods.seek64 = seek64Text;
+ g_IoMethods.write = writeText;
+ g_IoMethods.writev = writevText; /* ??? Is this necessary? */
+ g_IoMethods.close = closeLayer; /* ??? Is this necessary? */
+ return PR_SUCCESS;
+}
+
+static PRCallOnceType g_callOnce = {0,0};
+
+/* Push a layer that converts from "\n" to the local filesystem's
+ * end-of-line sequence on output, and vice-versa on input.
+ * The layer pops itself (if necessary) when the file is closed.
+ *
+ * This layer does not affect the behavior of PR_Seek or PR_Seek64;
+ * their parameters still measure bytes in the lower-level file,
+ * and consequently will not add up with the results of PR_Read
+ * or PR_Write. For example, if you add up PR_Read return values,
+ * and seek backward in the file that many bytes, the cursor will
+ * *not* be restored to its original position (unless the data you
+ * read didn't require conversion; that is, they didn't contain
+ * any newlines, or you're running on Unix).
+ *
+ * Likewise, the results of PR_Read or PR_Write won't add up to
+ * the 'size' field in the result of PRFileInfo or PRFileInfo64.
+ */
+static PRStatus pushTextIOLayer(PRFileDesc* stack)
+{
+ auto PRStatus rv = PR_CallOnce(&g_callOnce, initialize);
+ if (PR_SUCCESS == rv) {
+ auto PRFileDesc* layer = PR_CreateIOLayerStub(g_LayerID, &g_IoMethods);
+ layer->secret = NULL;
+ rv = PR_PushIOLayer(stack, PR_TOP_IO_LAYER, layer);
+ }
+ return rv;
+}
+
+static PRFileDesc *popTextIOLayer(PRFileDesc* stack)
+{
+ PRFileDesc *layer;
+ layer = PR_PopIOLayer(stack, g_LayerID);
+ if (layer && layer->dtor) {
+ layer->secret = NULL;
+ layer->dtor(layer);
+ }
+ return layer;
+}
+
+#endif /* XP_WIN32 */
+
+PRInt32
+slapi_read_buffer( PRFileDesc *fd, void *buf, PRInt32 amount )
+{
+ PRInt32 rval = 0;
+#if defined( XP_WIN32 )
+ PRStatus rv;
+
+ rv = pushTextIOLayer( fd );
+ if ( PR_SUCCESS != rv ) {
+ return -1;
+ }
+#endif
+
+ rval = PR_Read( fd, buf, amount );
+
+#if defined( XP_WIN32 )
+ popTextIOLayer( fd );
+#endif
+
+ return rval;
+}
+
+/*
+ * slapi_write_buffer -- same as PR_Write
+ * except '\r' is added before '\n'.
+ * Return value: written bytes not including '\r' characters.
+ */
+PRInt32
+slapi_write_buffer( PRFileDesc *fd, void *buf, PRInt32 amount )
+{
+ PRInt32 rval = 0;
+#if defined( XP_WIN32 )
+ PRStatus rv;
+
+ rv = pushTextIOLayer( fd );
+ if ( PR_SUCCESS != rv ) {
+ return -1;
+ }
+#endif
+
+ rval = PR_Write( fd, buf, amount );
+
+#if defined( XP_WIN32 )
+ popTextIOLayer( fd );
+#endif
+
+ return rval;
+}
+
+/*
+ * This function renames a file to a new name. Unlike PR_Rename or NT rename, this
+ * function can be used if the destfilename exists, and it will overwrite the dest
+ * file name
+ */
+int
+slapi_destructive_rename(const char *srcfilename, const char *destfilename)
+{
+ int rv = 0;
+#if defined( XP_WIN32 )
+ if (!MoveFileEx(srcfilename, destfilename, MOVEFILE_REPLACE_EXISTING)) {
+ rv = GetLastError();
+ }
+#else
+ if ( rename(srcfilename, destfilename) < 0 ) {
+ rv = errno;
+ }
+#endif
+ return rv;
+}
+
+/*
+ * This function copies the source into the dest
+ */
+int
+slapi_copy(const char *srcfilename, const char *destfilename)
+{
+ int rv = 0;
+#if defined( XP_WIN32 )
+ if (!CopyFile(srcfilename, destfilename, FALSE)) {
+ rv = GetLastError();
+ }
+#else
+ unlink(destfilename);
+ if ( link(srcfilename, destfilename) < 0 ) {
+ rv = errno;
+ }
+#endif
+ return rv;
+}
diff --git a/ldap/servers/slapd/filter.c b/ldap/servers/slapd/filter.c
new file mode 100644
index 00000000..2f22e317
--- /dev/null
+++ b/ldap/servers/slapd/filter.c
@@ -0,0 +1,1505 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+/* filter.c - routines for parsing and dealing with filters */
+
+#include <stdio.h>
+#include <string.h>
+#include <sys/types.h>
+#ifndef _WIN32
+#include <sys/socket.h>
+#endif
+#include "slap.h"
+#include "slapi-plugin.h"
+
+static int
+get_filter_list( Connection *conn, BerElement *ber,
+ struct slapi_filter **f, char **fstr, int maxdepth, int curdepth,
+ int *subentry_dont_rewrite, int *has_tombstone_filter);
+static int get_substring_filter();
+static int get_extensible_filter( BerElement *ber, mr_filter_t* );
+
+static int get_filter_internal( Connection *conn, BerElement *ber,
+ struct slapi_filter **filt, char **fstr, int maxdepth, int curdepth,
+ int *subentry_dont_rewrite, int *has_tombstone_filter);
+static int tombstone_check_filter(Slapi_Filter *f);
+static void filter_optimize(Slapi_Filter *f);
+
+
+
+/*
+ * Read a filter off the wire and create a slapi_filter and string representation.
+ * Both filt and fstr are allocated by this function, so must be freed by the caller.
+ *
+ * If the scope is not base and (objectclass=ldapsubentry) does not occur
+ * in the filter then we add (!(objectclass=ldapsubentry)) to the filter
+ * so that subentries are not returned.
+ * If the scope is base or (objectclass=ldapsubentry) occurs in the filter,
+ * then the caller is explicitly handling subentries himself and so we leave
+ * the filter as is.
+ */
+int
+get_filter( Connection *conn, BerElement *ber, int scope,
+ struct slapi_filter **filt, char **fstr )
+{
+ int subentry_dont_rewrite = 0; /* Re-write unless we're told not to */
+ int has_tombstone_filter = 0; /* Check if nsTombstone appears */
+ int return_value = 0;
+ char *logbuf = NULL;
+ size_t logbufsize = 0;
+
+ return_value = get_filter_internal(conn, ber, filt, fstr,
+ config_get_max_filter_nest_level(), /* maximum depth */
+ 0, /* current depth */
+ &subentry_dont_rewrite, &has_tombstone_filter);
+
+ if (0 == return_value) { /* Don't try to re-write if there was an error */
+ if (subentry_dont_rewrite || scope == LDAP_SCOPE_BASE)
+ (*filt)->f_flags |= SLAPI_FILTER_LDAPSUBENTRY;
+ if (has_tombstone_filter)
+ (*filt)->f_flags |= SLAPI_FILTER_TOMBSTONE;
+ }
+
+ if (LDAPDebugLevelIsSet( LDAP_DEBUG_FILTER ) && *filt != NULL
+ && *fstr != NULL) {
+ logbufsize = strlen(*fstr) + 1;
+ logbuf = slapi_ch_malloc(logbufsize);
+ *logbuf = '\0';
+ slapi_log_error( SLAPI_LOG_FATAL, "get_filter", "before optimize: %s\n",
+ slapi_filter_to_string(*filt, logbuf, logbufsize), 0, 0 );
+ }
+
+ filter_optimize(*filt);
+
+ if (NULL != logbuf) {
+ slapi_log_error( SLAPI_LOG_FATAL, "get_filter", " after optimize: %s\n",
+ slapi_filter_to_string(*filt, logbuf, logbufsize), 0, 0 );
+ slapi_ch_free_string( &logbuf );
+ }
+
+ return return_value;
+}
+
+
+#define FILTER_EQ_FMT "(%s=%s)"
+#define FILTER_GE_FMT "(%s>=%s)"
+#define FILTER_LE_FMT "(%s<=%s)"
+#define FILTER_APROX_FMT "(%s~=%s)"
+#define FILTER_EXTENDED_FMT "(%s%s%s%s:=%s)"
+#define FILTER_EQ_LEN 4
+#define FILTER_GE_LEN 5
+#define FILTER_LE_LEN 5
+#define FILTER_APROX_LEN 5
+
+
+/* returns escaped filter string for extended filters only*/
+
+static char *
+filter_escape_filter_value_extended(struct slapi_filter *f)
+{
+ char ebuf[BUFSIZ], *ptr;
+ const char *estr;
+ size_t len = 9;
+
+ estr = escape_filter_value( f->f_mr_value.bv_val, f->f_mr_value.bv_len, ebuf );
+ if ( f->f_mr_type ) {
+ len += strlen( f->f_mr_type );
+ }
+ len += strlen(estr);
+ if ( f->f_mr_oid ) {
+ len += strlen( f->f_mr_oid );
+ }
+ ptr = slapi_ch_malloc( len );
+ sprintf( ptr, FILTER_EXTENDED_FMT,
+ f->f_mr_type ? f->f_mr_type : "",
+ f->f_mr_dnAttrs ? ":dn" : "",
+ f->f_mr_oid ? ":" : "",
+ f->f_mr_oid ? f->f_mr_oid : "",
+ estr );
+ return ptr;
+}
+
+/* returns escaped filter string for EQ, LE, GE and APROX filters */
+
+static char *
+filter_escape_filter_value(struct slapi_filter *f, const char *fmt, size_t len)
+{
+ char ebuf[BUFSIZ], *ptr;
+ const char *estr;
+
+ estr = escape_filter_value( f->f_avvalue.bv_val, f->f_avvalue.bv_len, ebuf );
+ filter_compute_hash(f);
+ ptr = slapi_ch_malloc( len + strlen(f->f_avtype) + strlen( estr ));
+ sprintf( ptr, fmt, f->f_avtype, estr );
+ return ptr;
+}
+
+
+/*
+ * get_filter_internal(): extract an LDAP filter from a BerElement and create
+ * a slapi_filter structure (*filt) and a string equivalent (*fstr).
+ *
+ * This function is recursive. It calls itself (to process NOT filters) and
+ * it calls get_filter_list() for AND and OR filters, and get_filter_list()
+ * calls this function again.
+ */
+static int
+get_filter_internal( Connection *conn, BerElement *ber,
+ struct slapi_filter **filt, char **fstr, int maxdepth, int curdepth,
+ int *subentry_dont_rewrite, int *has_tombstone_filter )
+{
+ unsigned long len;
+ int err;
+ struct slapi_filter *f;
+ char *ftmp, *type;
+
+ LDAPDebug( LDAP_DEBUG_FILTER, "=> get_filter_internal\n", 0, 0, 0 );
+
+ /*
+ * Track and check the depth of nesting. Use post-increment on
+ * current depth here because this function is called for the
+ * top-level filter (which does not count towards the maximum depth).
+ */
+ if ( ( curdepth++ > maxdepth ) && ( maxdepth > 0 )) {
+ *filt = NULL;
+ *fstr = NULL;
+ err = LDAP_UNWILLING_TO_PERFORM;
+ LDAPDebug( LDAP_DEBUG_FILTER, "<= get_filter_internal %d"
+ " (maximum nesting level of %d exceeded)\n",
+ err, maxdepth, 0 );
+ return( err );
+ }
+
+ /*
+ * 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 --v3 only
+ * }
+ *
+ * SubstringFilter ::= SEQUENCE {
+ * type AttributeType,
+ * SEQUENCE OF CHOICE {
+ * initial [0] IA5String,
+ * any [1] IA5String,
+ * final [2] IA5String
+ * }
+ * }
+ *
+ * The extensibleMatch was added in LDAPv3:
+ *
+ * MatchingRuleAssertion ::= SEQUENCE {
+ * matchingRule [1] MatchingRuleID OPTIONAL,
+ * type [2] AttributeDescription OPTIONAL,
+ * matchValue [3] AssertionValue,
+ * dnAttributes [4] BOOLEAN DEFAULT FALSE
+ * }
+ */
+
+ f = (struct slapi_filter *) slapi_ch_calloc( 1, sizeof(struct slapi_filter) );
+
+ err = 0;
+ *fstr = NULL;
+ f->f_choice = ber_peek_tag( ber, &len );
+ switch ( f->f_choice ) {
+ case LDAP_FILTER_EQUALITY:
+ LDAPDebug( LDAP_DEBUG_FILTER, "EQUALITY\n", 0, 0, 0 );
+ if ( (err = get_ava( ber, &f->f_ava )) == 0 ) {
+
+ if ( 0 == strcasecmp ( f->f_avtype, "objectclass")) {
+ /* Process objectclass oid's here */
+ if (strchr (f->f_avvalue.bv_val, '.')) {
+ char *ocname = oc_find_name( f->f_avvalue.bv_val );
+
+ if ( NULL != ocname ) {
+ slapi_ch_free((void**)&f->f_avvalue.bv_val );
+ f->f_avvalue.bv_val = ocname;
+ f->f_avvalue.bv_len = strlen ( f->f_avvalue.bv_val );
+ }
+ }
+
+ /*
+ * Process subentry searches here.
+ * Only set (*subentry_dont_rewrite) if it's not already set.
+ */
+
+ if (!(*subentry_dont_rewrite)) {
+ *subentry_dont_rewrite = subentry_check_filter(f);
+ }
+ /*
+ * Check if it's a Tomstone filter.
+ * We need to do it once per filter, so if flag is already set,
+ * don't bother doing it
+ */
+ if (!(*has_tombstone_filter)) {
+ *has_tombstone_filter = tombstone_check_filter(f);
+ }
+ }
+ *fstr=filter_escape_filter_value(f, FILTER_EQ_FMT, FILTER_EQ_LEN);
+ }
+ break;
+
+ case LDAP_FILTER_SUBSTRINGS:
+ LDAPDebug( LDAP_DEBUG_FILTER, "SUBSTRINGS\n", 0, 0, 0 );
+ err = get_substring_filter( conn, ber, f, fstr );
+ break;
+
+ case LDAP_FILTER_GE:
+ LDAPDebug( LDAP_DEBUG_FILTER, "GE\n", 0, 0, 0 );
+ if ( (err = get_ava( ber, &f->f_ava )) == 0 ) {
+ *fstr=filter_escape_filter_value(f, FILTER_GE_FMT, FILTER_GE_LEN);
+ }
+ break;
+
+ case LDAP_FILTER_LE:
+ LDAPDebug( LDAP_DEBUG_FILTER, "LE\n", 0, 0, 0 );
+ if ( (err = get_ava( ber, &f->f_ava )) == 0 ) {
+ *fstr=filter_escape_filter_value(f, FILTER_LE_FMT, FILTER_LE_LEN);
+ }
+ break;
+
+ case LDAP_FILTER_PRESENT:
+ LDAPDebug( LDAP_DEBUG_FILTER, "PRESENT\n", 0, 0, 0 );
+ if ( ber_scanf( ber, "a", &type ) == LBER_ERROR ) {
+ err = LDAP_PROTOCOL_ERROR;
+ } else {
+ err = LDAP_SUCCESS;
+ f->f_type = slapi_attr_syntax_normalize( type );
+ free( type );
+ filter_compute_hash(f);
+ *fstr = slapi_ch_malloc( 5 + strlen( f->f_type ) );
+ sprintf( *fstr, "(%s=*)", f->f_type );
+ }
+ break;
+
+ case LDAP_FILTER_APPROX:
+ LDAPDebug( LDAP_DEBUG_FILTER, "APPROX\n", 0, 0, 0 );
+ if ( (err = get_ava( ber, &f->f_ava )) == 0 ) {
+ *fstr=filter_escape_filter_value(f, FILTER_APROX_FMT, FILTER_APROX_LEN);
+ }
+ break;
+
+ case LDAP_FILTER_EXTENDED:
+ LDAPDebug( LDAP_DEBUG_FILTER, "EXTENDED\n", 0, 0, 0 );
+ if ( conn->c_ldapversion < 3 ) {
+ LDAPDebug( LDAP_DEBUG_ANY,
+ "extensible filter received from v2 client\n",
+ 0, 0, 0 );
+ err = LDAP_PROTOCOL_ERROR;
+ } else if ( (err = get_extensible_filter( ber, &f->f_mr )) == LDAP_SUCCESS ) {
+ *fstr=filter_escape_filter_value_extended(f);
+ LDAPDebug (LDAP_DEBUG_FILTER, "%s\n", *fstr, 0, 0);
+ if(f->f_mr_oid==NULL) {
+ /*
+ * We accept:
+ * A) attr ":=" value
+ * B) attr ":dn" ":=" value
+ */
+ err = LDAP_SUCCESS;
+ } else {
+ err = plugin_mr_filter_create (&f->f_mr);
+ }
+ }
+ break;
+
+ case LDAP_FILTER_AND:
+ LDAPDebug( LDAP_DEBUG_FILTER, "AND\n", 0, 0, 0 );
+ if ( (err = get_filter_list( conn, ber, &f->f_and, &ftmp, maxdepth,
+ curdepth, subentry_dont_rewrite, has_tombstone_filter ))
+ == 0 ) {
+ filter_compute_hash(f);
+ *fstr = slapi_ch_malloc( 4 + strlen( ftmp ) );
+ sprintf( *fstr, "(&%s)", ftmp );
+ slapi_ch_free((void**)&ftmp );
+ }
+ break;
+
+ case LDAP_FILTER_OR:
+ LDAPDebug( LDAP_DEBUG_FILTER, "OR\n", 0, 0, 0 );
+ if ( (err = get_filter_list( conn, ber, &f->f_or, &ftmp, maxdepth,
+ curdepth, subentry_dont_rewrite, has_tombstone_filter ))
+ == 0 ) {
+ filter_compute_hash(f);
+ *fstr = slapi_ch_malloc( 4 + strlen( ftmp ) );
+ sprintf( *fstr, "(|%s)", ftmp );
+ slapi_ch_free((void**)&ftmp );
+ }
+ break;
+
+ case LDAP_FILTER_NOT:
+ LDAPDebug( LDAP_DEBUG_FILTER, "NOT\n", 0, 0, 0 );
+ (void) ber_skip_tag( ber, &len );
+ if ( (err = get_filter_internal( conn, ber, &f->f_not, &ftmp, maxdepth,
+ curdepth, subentry_dont_rewrite, has_tombstone_filter ))
+ == 0 ) {
+ filter_compute_hash(f);
+ *fstr = slapi_ch_malloc( 4 + strlen( ftmp ) );
+ sprintf( *fstr, "(!%s)", ftmp );
+ slapi_ch_free((void**)&ftmp );
+ }
+ break;
+
+ default:
+ LDAPDebug( LDAP_DEBUG_ANY, "get_filter_internal: unknown type 0x%lX\n",
+ f->f_choice, 0, 0 );
+ err = LDAP_PROTOCOL_ERROR;
+ break;
+ }
+
+ if ( err != 0 ) {
+ slapi_filter_free( f, 1 );
+ f = NULL;
+ slapi_ch_free( (void**)fstr );
+ }
+ *filt = f;
+ LDAPDebug( LDAP_DEBUG_FILTER, "<= get_filter_internal %d\n", err, 0, 0 );
+ return( err );
+}
+
+static int
+get_filter_list( Connection *conn, BerElement *ber,
+ struct slapi_filter **f, char **fstr, int maxdepth,
+ int curdepth, int *subentry_dont_rewrite,
+ int *has_tombstone_filter)
+{
+ struct slapi_filter **new;
+ int err;
+ unsigned long tag, len;
+ char *last;
+
+ LDAPDebug( LDAP_DEBUG_FILTER, "=> get_filter_list\n", 0, 0, 0 );
+
+ *fstr = NULL;
+ new = f;
+ for ( tag = ber_first_element( ber, &len, &last );
+ tag != LBER_ERROR && tag != LBER_END_OF_SEQORSET;
+ tag = ber_next_element( ber, &len, last ) ) {
+ char *ftmp;
+ if ( (err = get_filter_internal( conn, ber, new, &ftmp, maxdepth,
+ curdepth, subentry_dont_rewrite, has_tombstone_filter))
+ != 0 ) {
+ if ( *fstr != NULL ) {
+ slapi_ch_free((void**)fstr );
+ }
+ return( err );
+ }
+ if ( *fstr == NULL ) {
+ *fstr = ftmp;
+ } else {
+ *fstr = slapi_ch_realloc( *fstr, strlen( *fstr ) +
+ strlen( ftmp ) + 1 );
+ strcat( *fstr, ftmp );
+ slapi_ch_free((void**)&ftmp );
+ }
+ new = &(*new)->f_next;
+ }
+ *new = NULL;
+
+ if ( tag == LBER_ERROR && *fstr != NULL ) {
+ slapi_ch_free((void**)fstr );
+ }
+
+ LDAPDebug( LDAP_DEBUG_FILTER, "<= get_filter_list\n", 0, 0, 0 );
+ return(( *fstr == NULL ) ? LDAP_PROTOCOL_ERROR : 0 );
+}
+
+static int
+get_substring_filter(
+ Connection *conn,
+ BerElement *ber,
+ struct slapi_filter *f,
+ char **fstr
+)
+{
+ unsigned long tag, len, rc;
+ char *val, *last, *type;
+ char ebuf[BUFSIZ];
+
+ LDAPDebug( LDAP_DEBUG_FILTER, "=> get_substring_filter\n", 0, 0, 0 );
+
+ if ( ber_scanf( ber, "{a", &type ) == LBER_ERROR ) {
+ return( LDAP_PROTOCOL_ERROR );
+ }
+ f->f_sub_type = slapi_attr_syntax_normalize( type );
+ free( type );
+ f->f_sub_initial = NULL;
+ f->f_sub_any = NULL;
+ f->f_sub_final = NULL;
+
+ *fstr = slapi_ch_malloc( strlen( f->f_sub_type ) + 3 );
+ sprintf( *fstr, "(%s=", f->f_sub_type );
+ for ( tag = ber_first_element( ber, &len, &last );
+ tag != LBER_ERROR && tag != LBER_END_OF_SEQORSET;
+ tag = ber_next_element( ber, &len, last ) )
+ {
+ rc = ber_scanf( ber, "a", &val );
+ if ( rc == LBER_ERROR ) {
+ return( LDAP_PROTOCOL_ERROR );
+ }
+ if ( val == NULL || *val == '\0' ) {
+ if ( val != NULL ) {
+ free( val );
+ }
+ return( LDAP_INVALID_SYNTAX );
+ }
+
+ switch ( tag ) {
+ case LDAP_SUBSTRING_INITIAL:
+ LDAPDebug( LDAP_DEBUG_FILTER, " INITIAL\n", 0, 0, 0 );
+ if ( f->f_sub_initial != NULL ) {
+ return( LDAP_PROTOCOL_ERROR );
+ }
+ f->f_sub_initial = val;
+ /* jcm: Had to cast away a const */
+ val = (char*)escape_filter_value( val, -1, ebuf );
+ *fstr = slapi_ch_realloc( *fstr, strlen( *fstr ) +
+ strlen( val ) + 1 );
+ strcat( *fstr, val );
+ break;
+
+ case LDAP_SUBSTRING_ANY:
+ LDAPDebug( LDAP_DEBUG_FILTER, " ANY\n", 0, 0, 0 );
+ charray_add( &f->f_sub_any, val );
+ /* jcm: Had to cast away a const */
+ val = (char*)escape_filter_value( val, -1, ebuf );
+ *fstr = slapi_ch_realloc( *fstr, strlen( *fstr ) +
+ strlen( val ) + 2 );
+ strcat( *fstr, "*" );
+ strcat( *fstr, val );
+ break;
+
+ case LDAP_SUBSTRING_FINAL:
+ LDAPDebug( LDAP_DEBUG_FILTER, " FINAL\n", 0, 0, 0 );
+ if ( f->f_sub_final != NULL ) {
+ return( LDAP_PROTOCOL_ERROR );
+ }
+ f->f_sub_final = val;
+ /* jcm: Had to cast away a const */
+ val = (char*)escape_filter_value( val, -1, ebuf );
+ *fstr = slapi_ch_realloc( *fstr, strlen( *fstr ) +
+ strlen( val ) + 2 );
+ strcat( *fstr, "*" );
+ strcat( *fstr, val );
+ break;
+
+ default:
+ LDAPDebug( LDAP_DEBUG_FILTER, " unknown tag 0x%lX\n", tag, 0, 0 );
+ return( LDAP_PROTOCOL_ERROR );
+ }
+ }
+
+ if ( tag == LBER_ERROR ) {
+ return( LDAP_PROTOCOL_ERROR );
+ }
+ if ( f->f_sub_initial == NULL && f->f_sub_any == NULL &&
+ f->f_sub_final == NULL ) {
+ return( LDAP_PROTOCOL_ERROR );
+ }
+
+ filter_compute_hash(f);
+ *fstr = slapi_ch_realloc( *fstr, strlen( *fstr ) + 3 );
+ if ( f->f_sub_final == NULL ) {
+ strcat( *fstr, "*" );
+ }
+ strcat( *fstr, ")" );
+
+ LDAPDebug( LDAP_DEBUG_FILTER, "<= get_substring_filter\n", 0, 0, 0 );
+ return( 0 );
+}
+
+static int
+get_extensible_filter( BerElement *ber, mr_filter_t* mrf )
+{
+ int gotelem, gotoid, gotvalue;
+ unsigned long tag, len;
+ char *last;
+ int rc = LDAP_PROTOCOL_ERROR;
+
+ LDAPDebug( LDAP_DEBUG_FILTER, "=> get_extensible_filter\n", 0, 0, 0 );
+ memset (mrf, 0, sizeof (mr_filter_t));
+
+ gotelem = gotoid = gotvalue = 0;
+ for ( tag = ber_first_element( ber, &len, &last );
+ tag != LBER_ERROR && tag != LBER_END_OF_SEQORSET;
+ tag = ber_next_element( ber, &len, last ) ) {
+ /*
+ * order of elements goes like this:
+ *
+ * [oid][type]value[dnattr]
+ *
+ * where either oid or type is required.
+ */
+ switch ( tag ) {
+ case LDAP_TAG_MRA_OID:
+ if ( gotelem != 0 ) {
+ goto parsing_error;
+ }
+ rc = ber_scanf( ber, "a", &mrf->mrf_oid );
+ gotoid = 1;
+ gotelem++;
+ break;
+ case LDAP_TAG_MRA_TYPE:
+ if ( gotelem != 0 ) {
+ if ( gotelem != 1 || gotoid != 1 ) {
+ goto parsing_error;
+ }
+ }
+ {
+ char* type;
+ if (ber_scanf( ber, "a", &type ) == LBER_ERROR) {
+ rc = LDAP_PROTOCOL_ERROR;
+ } else {
+ mrf->mrf_type = slapi_attr_syntax_normalize(type);
+ free (type);
+ }
+ }
+ gotelem++;
+ break;
+ case LDAP_TAG_MRA_VALUE:
+ if ( gotelem != 1 && gotelem != 2 ) {
+ goto parsing_error;
+ }
+ rc = ber_scanf( ber, "o", &mrf->mrf_value );
+ gotvalue = 1;
+ gotelem++;
+ break;
+ case LDAP_TAG_MRA_DNATTRS:
+ if ( gotvalue != 1 ) {
+ goto parsing_error;
+ }
+ rc = ber_scanf( ber, "b", &mrf->mrf_dnAttrs );
+ gotelem++;
+ break;
+ default:
+ goto parsing_error;
+ }
+ if ( rc == -1 ) {
+ goto parsing_error;
+ }
+ rc = LDAP_SUCCESS;
+ }
+
+ if ( tag == LBER_ERROR ) {
+ goto parsing_error;
+ }
+
+ LDAPDebug( LDAP_DEBUG_FILTER, "<= get_extensible_filter %i\n", rc, 0, 0 );
+ return rc;
+
+parsing_error:;
+ LDAPDebug( LDAP_DEBUG_ANY, "error parsing extensible filter\n",
+ 0, 0, 0 );
+ return( LDAP_PROTOCOL_ERROR );
+}
+
+
+Slapi_Filter *
+slapi_filter_dup(Slapi_Filter *f)
+{
+ Slapi_Filter *out = 0;
+ struct slapi_filter *fl = 0;
+ struct slapi_filter **outl = 0;
+ struct slapi_filter *lastout = 0;
+
+ if ( f == NULL ) {
+ return NULL;
+ }
+
+ out = (struct slapi_filter*)calloc(1, sizeof(struct slapi_filter));
+ if ( out == NULL ) {
+ LDAPDebug(LDAP_DEBUG_ANY, "slapi_filter_dup: memory allocation error\n",
+ 0, 0, 0 );
+ return NULL;
+ }
+
+ out->f_choice = f->f_choice;
+ out->f_hash = f->f_hash;
+
+ LDAPDebug( LDAP_DEBUG_FILTER, "slapi_filter_dup type 0x%lX\n", f->f_choice, 0, 0 );
+ switch ( f->f_choice ) {
+ case LDAP_FILTER_EQUALITY:
+ case LDAP_FILTER_GE:
+ case LDAP_FILTER_LE:
+ case LDAP_FILTER_APPROX:
+ out->f_ava.ava_type = slapi_ch_strdup(f->f_ava.ava_type);
+ out->f_ava.ava_value.bv_val = slapi_ch_malloc(f->f_ava.ava_value.bv_len+1);
+ memcpy(out->f_ava.ava_value.bv_val,f->f_ava.ava_value.bv_val,f->f_ava.ava_value.bv_len);
+ out->f_ava.ava_value.bv_val[f->f_ava.ava_value.bv_len] = 0; /* terminate */
+ out->f_ava.ava_value.bv_len = f->f_ava.ava_value.bv_len;
+ break;
+
+ case LDAP_FILTER_SUBSTRINGS:
+
+ out->f_sub_type = slapi_ch_strdup(f->f_sub_type);
+ out->f_sub_initial = slapi_ch_strdup(f->f_sub_initial );
+ out->f_sub_any = charray_dup( f->f_sub_any );
+ out->f_sub_final = slapi_ch_strdup(f->f_sub_final );
+ break;
+
+ case LDAP_FILTER_PRESENT:
+ out->f_type = slapi_ch_strdup( f->f_type );
+ break;
+
+ case LDAP_FILTER_AND:
+ case LDAP_FILTER_OR:
+ case LDAP_FILTER_NOT:
+ outl = &out->f_list;
+
+/* out->f_list = slapi_filter_dup(f->f_list);
+*/
+ for (fl = f->f_list; fl != NULL; fl = fl->f_next) {
+ (*outl) = slapi_filter_dup( fl );
+ (*outl)->f_next = 0;
+ if(lastout)
+ lastout->f_next = *outl;
+ lastout = *outl;
+ outl = &((*outl)->f_next);
+ }
+ break;
+
+ case LDAP_FILTER_EXTENDED:
+ /* something needs to be done here, but Im not sure how to do it
+ slapi_ch_free((void**)&f->f_mr_oid);
+ slapi_ch_free((void**)&f->f_mr_type);
+ slapi_ch_free((void **)&f->f_mr_value.bv_val );
+ if (f->f_mr.mrf_destroy != NULL) {
+ Slapi_PBlock pb;
+ pblock_init (&pb);
+ if ( ! slapi_pblock_set (&pb, SLAPI_PLUGIN_OBJECT, f->f_mr.mrf_object)) {
+ f->f_mr.mrf_destroy (&pb);
+ }
+ }
+ */
+ break;
+
+ default:
+ LDAPDebug(LDAP_DEBUG_FILTER, "slapi_filter_dup: unknown type 0x%lX\n",
+ f->f_choice, 0, 0 );
+ break;
+ }
+
+ return out;
+}
+
+void
+slapi_filter_free( struct slapi_filter *f, int recurse )
+{
+ if ( f == NULL ) {
+ return;
+ }
+
+ LDAPDebug( LDAP_DEBUG_FILTER, "slapi_filter_free type 0x%lX\n", f->f_choice, 0, 0 );
+ switch ( f->f_choice ) {
+ case LDAP_FILTER_EQUALITY:
+ case LDAP_FILTER_GE:
+ case LDAP_FILTER_LE:
+ case LDAP_FILTER_APPROX:
+ ava_done( &f->f_ava );
+ break;
+
+ case LDAP_FILTER_SUBSTRINGS:
+ slapi_ch_free((void**)&f->f_sub_type );
+ slapi_ch_free((void**)&f->f_sub_initial );
+ charray_free( f->f_sub_any );
+ slapi_ch_free((void**)&f->f_sub_final );
+ break;
+
+ case LDAP_FILTER_PRESENT:
+ slapi_ch_free((void**)&f->f_type );
+ break;
+
+ case LDAP_FILTER_AND:
+ case LDAP_FILTER_OR:
+ case LDAP_FILTER_NOT:
+ if ( recurse ) {
+ struct slapi_filter *fl, *next;
+
+ for (fl = f->f_list; fl != NULL; fl = next) {
+ next = fl->f_next;
+ fl->f_next = NULL;
+ slapi_filter_free( fl, recurse );
+ fl = next;
+ }
+ }
+ break;
+
+ case LDAP_FILTER_EXTENDED:
+ slapi_ch_free((void**)&f->f_mr_oid);
+ slapi_ch_free((void**)&f->f_mr_type);
+ slapi_ch_free((void **)&f->f_mr_value.bv_val );
+ if (f->f_mr.mrf_destroy != NULL) {
+ Slapi_PBlock pb;
+ pblock_init (&pb);
+ if ( ! slapi_pblock_set (&pb, SLAPI_PLUGIN_OBJECT, f->f_mr.mrf_object)) {
+ f->f_mr.mrf_destroy (&pb);
+ }
+ }
+ break;
+
+ default:
+ LDAPDebug( LDAP_DEBUG_ANY, "slapi_filter_free: unknown type 0x%lX\n",
+ f->f_choice, 0, 0 );
+ break;
+ }
+ slapi_ch_free((void**)&f);
+}
+
+#if 0
+static void
+filter_list_insert( struct slapi_filter **into, struct slapi_filter *from )
+{
+ struct slapi_filter *f;
+ if (into == NULL || from == NULL) return;
+ if (*into != NULL) {
+ for (f = from; f->f_next != NULL; f = f->f_next);
+ f->f_next = *into;
+ }
+ *into = from;
+}
+#endif
+
+
+struct slapi_filter *
+slapi_filter_join( int ftype, struct slapi_filter *f1, struct slapi_filter *f2)
+{
+ return slapi_filter_join_ex( ftype, f1, f2, 1 );
+
+}
+
+
+struct slapi_filter *
+slapi_filter_join_ex( int ftype, struct slapi_filter *f1, struct slapi_filter *f2, int recurse_always )
+{
+ struct slapi_filter *fjoin;
+ struct slapi_filter *add_to;
+ struct slapi_filter *add_this;
+ struct slapi_filter *return_this;
+ int insert = 0;
+
+ if(!recurse_always)
+ {
+ /* try to optimise the filter join */
+ switch(ftype)
+ {
+ case LDAP_FILTER_AND:
+ case LDAP_FILTER_OR:
+ if(ftype == (int)f1->f_choice)
+ {
+ add_to = f1;
+ add_this = f2;
+ insert = 1;
+ }
+ else if(ftype == (int)f2->f_choice)
+ {
+ add_to = f2;
+ add_this = f1;
+ insert = 1;
+ }
+
+ default:
+ break;
+ }
+ }
+
+ if(insert)
+ {
+ /* try to avoid ! filters as the first arg */
+ if(add_to->f_list->f_choice == LDAP_FILTER_NOT)
+ {
+ add_this->f_next = add_to->f_list;
+ add_to->f_list = add_this;
+ filter_compute_hash(add_to);
+ return_this = add_to;
+ }
+ else
+ {
+ /* find end of list, add the filter */
+ for (fjoin = add_to->f_list; fjoin != NULL; fjoin = fjoin->f_next) {
+ if(fjoin->f_next == NULL)
+ {
+ fjoin->f_next = add_this;
+ filter_compute_hash(add_to);
+ return_this = add_to;
+ break;
+ }
+ }
+ }
+ }
+ else
+ {
+ fjoin = (struct slapi_filter *) slapi_ch_calloc( 1, sizeof(struct slapi_filter) );
+ fjoin->f_choice = ftype;
+ fjoin->f_next = NULL;
+ /* try to ensure ! filters dont cause allid search */
+ if(f1->f_choice == LDAP_FILTER_NOT && f2)
+ {
+ fjoin->f_list = f2;
+ f2->f_next = f1;
+ }
+ else
+ {
+ fjoin->f_list = f1;
+ f1->f_next = f2;
+ }
+ filter_compute_hash(fjoin);
+ return_this = fjoin;
+ }
+
+ return( return_this );
+}
+
+int
+slapi_filter_get_choice( struct slapi_filter *f )
+{
+ return( f->f_choice );
+}
+
+int
+slapi_filter_get_ava( struct slapi_filter *f, char **type, struct berval **bval )
+{
+ switch ( f->f_choice ) {
+ case LDAP_FILTER_EQUALITY:
+ case LDAP_FILTER_GE:
+ case LDAP_FILTER_LE:
+ case LDAP_FILTER_APPROX:
+ break;
+ default:
+ *type = NULL;
+ *bval = NULL;
+ return( -1 );
+ }
+ *type = f->f_avtype;
+ *bval = &f->f_avvalue;
+ return( 0 );
+}
+
+/* Deprecated--use slapi_filter_get_attribute_type() now */
+
+SLAPI_DEPRECATED int
+slapi_filter_get_type( struct slapi_filter *f, char **type )
+{
+ if ( f->f_choice != LDAP_FILTER_PRESENT ) {
+ return( -1 );
+ }
+ *type = f->f_type;
+
+ return( 0 );
+}
+
+/*
+ * Return the attribute type for all simple filter choices into type.
+ * ie. for all except LDAP_FILTER_AND, LDAP_FILTER_OR and LDAP_FILTER_NOT.
+ *
+ * The returned type is "as is" and so may not be normalized.
+ * Returns 0 for success, -1 otherwise.
+*/
+
+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_APPROX:
+ case LDAP_FILTER_EQUALITY:
+ *type = f->f_ava.ava_type;
+ break;
+ case LDAP_FILTER_SUBSTRINGS:
+ *type = f->f_sub_type;
+ break;
+ case LDAP_FILTER_PRESENT:
+ *type = f->f_type;
+ break;
+ case LDAP_FILTER_EXTENDED:
+ *type = f->f_mr_type;
+ break;
+ case LDAP_FILTER_AND:
+ case LDAP_FILTER_OR:
+ case LDAP_FILTER_NOT:
+ return(-1);
+ default:
+ /* Unknown filter choice */
+ return -1;
+ }
+
+ /* success */
+ return(0);
+}
+
+struct slapi_filter *
+slapi_filter_list_first( struct slapi_filter *f )
+{
+ if ( f->f_choice != LDAP_FILTER_AND && f->f_choice != LDAP_FILTER_OR
+ && f->f_choice != LDAP_FILTER_NOT ) {
+ return( NULL );
+ }
+ return( f->f_list );
+}
+
+struct slapi_filter *
+slapi_filter_list_next( struct slapi_filter *f, struct slapi_filter *fprev )
+{
+ return( fprev->f_next );
+}
+
+int
+slapi_filter_get_subfilt(
+ struct slapi_filter *f,
+ char **type,
+ char **initial,
+ char ***any,
+ char **final
+)
+{
+ if ( f->f_choice != LDAP_FILTER_SUBSTRINGS ) {
+ return( -1 );
+ }
+ *type = f->f_sub_type;
+ *initial = f->f_sub_initial;
+ *any = f->f_sub_any;
+ *final = f->f_sub_final;
+
+ return( 0 );
+}
+
+static void
+filter_normalize_ava( struct ava *ava, int ftype )
+{
+ char *tmp;
+
+ if ( ava == NULL ) {
+ return;
+ }
+ tmp = ava->ava_type;
+ ava->ava_type = slapi_attr_syntax_normalize(tmp);
+ slapi_ch_free((void**)&tmp );
+ /* value will be normalized later */
+}
+
+
+void filter_normalize( struct slapi_filter *f );
+
+static void
+filter_normalize_list( struct slapi_filter *flist )
+{
+ struct slapi_filter *f;
+
+ for ( f = flist; f != NULL; f = f->f_next ) {
+ filter_normalize( f );
+ }
+}
+
+
+
+/*
+ * Normalize all values and types in a filter. This isn't necessary
+ * when we've read the slapi_filter off the wire, but if we've hand-constructed
+ * a filter inside slapd (e.g. when calling the routines in wrapper.c),
+ * we've called slapi_str2filter on something which *didn't* come over the wire,
+ * so the attribute names and filters in the filter struct aren't
+ * normalized.
+ */
+void
+filter_normalize( struct slapi_filter *f )
+{
+ char *tmp;
+
+ if ( f == NULL ) {
+ return;
+ }
+
+ switch ( f->f_choice ) {
+ case LDAP_FILTER_GE:
+ case LDAP_FILTER_LE:
+ case LDAP_FILTER_APPROX:
+ case LDAP_FILTER_EQUALITY:
+ filter_normalize_ava( &f->f_ava, f->f_choice );
+ break;
+ case LDAP_FILTER_SUBSTRINGS:
+ tmp = f->f_sub_type;
+ f->f_sub_type = slapi_attr_syntax_normalize(tmp);
+ slapi_ch_free((void**)&tmp );
+ /* value will be normalized later */
+ break;
+ case LDAP_FILTER_PRESENT:
+ tmp = f->f_type;
+ f->f_type = slapi_attr_syntax_normalize(tmp);
+ slapi_ch_free((void**)&tmp );
+ break;
+ case LDAP_FILTER_EXTENDED:
+ tmp = f->f_mr_type;
+ f->f_mr_type = slapi_attr_syntax_normalize(tmp);
+ slapi_ch_free((void**)&tmp );
+ break;
+ case LDAP_FILTER_AND:
+ filter_normalize_list( f->f_and );
+ break;
+ case LDAP_FILTER_OR:
+ filter_normalize_list( f->f_or );
+ break;
+ case LDAP_FILTER_NOT:
+ filter_normalize_list( f->f_not );
+ break;
+ default:
+ return;
+ }
+}
+
+void
+filter_print( struct slapi_filter *f )
+{
+ int i;
+ struct slapi_filter *p;
+
+ if ( f == NULL ) {
+ printf( "NULL" );
+ return;
+ }
+
+ switch ( f->f_choice ) {
+ case LDAP_FILTER_EQUALITY:
+ printf( "(%s=%s)", f->f_ava.ava_type,
+ f->f_ava.ava_value.bv_val );
+ break;
+
+ case LDAP_FILTER_GE:
+ printf( "(%s>=%s)", f->f_ava.ava_type,
+ f->f_ava.ava_value.bv_val );
+ break;
+
+ case LDAP_FILTER_LE:
+ printf( "(%s<=%s)", f->f_ava.ava_type,
+ f->f_ava.ava_value.bv_val );
+ break;
+
+ case LDAP_FILTER_APPROX:
+ printf( "(%s~=%s)", f->f_ava.ava_type,
+ f->f_ava.ava_value.bv_val );
+ break;
+
+ case LDAP_FILTER_SUBSTRINGS:
+ printf( "(%s=", f->f_sub_type );
+ if ( f->f_sub_initial != NULL ) {
+ printf( "%s", f->f_sub_initial );
+ }
+ if ( f->f_sub_any != NULL ) {
+ for ( i = 0; f->f_sub_any[i] != NULL; i++ ) {
+ printf( "*%s", f->f_sub_any[i] );
+ }
+ }
+ if ( f->f_sub_final != NULL ) {
+ printf( "*%s", f->f_sub_final );
+ }
+ printf( ")" );
+ break;
+
+ case LDAP_FILTER_PRESENT:
+ printf( "(%s=*)", f->f_type );
+ break;
+
+ case LDAP_FILTER_AND:
+ case LDAP_FILTER_OR:
+ case LDAP_FILTER_NOT:
+ printf( "(%c", f->f_choice == LDAP_FILTER_AND ? '&' :
+ f->f_choice == LDAP_FILTER_OR ? '|' : '!' );
+ for ( p = f->f_list; p != NULL; p = p->f_next ) {
+ filter_print( p );
+ }
+ printf( ")" );
+ break;
+
+ default:
+ printf( "unknown type 0x%lX", f->f_choice );
+ break;
+ }
+ fflush( stdout );
+}
+
+/* filter_to_string
+ * ----------------
+ * translates the supplied filter to
+ * the string representation and places
+ * the result in buf
+ *
+ * NOTE: intended for debug purposes, buffer must be
+ * large enough to contain filter string
+ */
+
+char *
+slapi_filter_to_string_internal( const struct slapi_filter *f, char *buf, size_t *bufsize )
+{
+ int i;
+ char *return_buf = buf;
+ struct slapi_filter *p;
+ size_t size;
+ char *operator = ""; /* for comparison operators */
+
+ if(buf == NULL)
+ return 0;
+ else
+ *buf = 0; /* make sure buf is null terminated */
+
+ if ( f == NULL ) {
+ sprintf( buf, "NULL" );
+ return 0;
+ }
+
+ switch ( f->f_choice ) {
+ case LDAP_FILTER_EQUALITY:
+ operator = "=";
+ break;
+
+ case LDAP_FILTER_GE:
+ operator = ">=";
+ break;
+
+ case LDAP_FILTER_LE:
+ operator = "<=";
+ break;
+
+ case LDAP_FILTER_APPROX:
+ operator = "~=";
+ break;
+
+ default: break;
+ }
+
+ switch ( f->f_choice ) {
+ case LDAP_FILTER_EQUALITY:
+ case LDAP_FILTER_GE:
+ case LDAP_FILTER_LE:
+ case LDAP_FILTER_APPROX:
+ /* +3 -> 1 for (, 1 for ), and one for the trailing null */
+ size = strlen(f->f_ava.ava_type) + f->f_ava.ava_value.bv_len + strlen(operator) + 3;
+
+ if(size < *bufsize)
+ {
+ /* bv_val may not be null terminated, so use the max field width
+ specifier .* with the bv_len as the length to avoid reading
+ past bv_len in bv_val */
+ sprintf( buf, "(%s%s%.*s)", f->f_ava.ava_type, operator,
+ f->f_ava.ava_value.bv_len,
+ f->f_ava.ava_value.bv_val );
+ *bufsize -= size;
+ }
+ break;
+
+ case LDAP_FILTER_SUBSTRINGS:
+ size = strlen(f->f_sub_type) + 2;
+
+ if(size < *bufsize)
+ {
+ sprintf( buf, "(%s=", f->f_sub_type );
+ *bufsize -= size;
+
+ if ( f->f_sub_initial != NULL ) {
+ size = strlen(f->f_sub_initial);
+
+ if(size < *bufsize)
+ {
+ buf += strlen(buf);
+ sprintf( buf, "%s", f->f_sub_initial );
+ *bufsize -= size;
+ }
+ }
+ if ( f->f_sub_any != NULL ) {
+ for ( i = 0; f->f_sub_any[i] != NULL; i++ ) {
+ size = strlen(f->f_sub_any[i]) + 1;
+
+ if(size < *bufsize)
+ {
+ buf += strlen(buf);
+ sprintf( buf, "*%s", f->f_sub_any[i] );
+ *bufsize -= size;
+ }
+ }
+ }
+ if ( f->f_sub_final != NULL ) {
+ size = strlen(f->f_sub_final) + 1;
+
+ if(size < *bufsize)
+ {
+ buf += strlen(buf);
+ sprintf( buf, "*%s", f->f_sub_final );
+ *bufsize -= size;
+ }
+ }
+ buf += strlen(buf);
+
+ if(1 < *bufsize)
+ {
+ sprintf( buf, ")" );
+ *bufsize--;
+ }
+ }
+ break;
+
+ case LDAP_FILTER_PRESENT:
+ size = strlen(f->f_type) + 4;
+
+ if(size < *bufsize)
+ {
+ sprintf( buf, "(%s=*)", f->f_type );
+ *bufsize -= size;
+ }
+ break;
+
+ case LDAP_FILTER_AND:
+ case LDAP_FILTER_OR:
+ case LDAP_FILTER_NOT:
+ if(2 < *bufsize)
+ {
+ sprintf( buf, "(%c", f->f_choice == LDAP_FILTER_AND ? '&' :
+ f->f_choice == LDAP_FILTER_OR ? '|' : '!' );
+ *bufsize -= 2;
+
+ for ( p = f->f_list; p != NULL; p = p->f_next ) {
+ buf += strlen(buf);
+ slapi_filter_to_string_internal( p, buf, bufsize );
+ }
+ buf += strlen(buf);
+
+ if(1 < *bufsize)
+ {
+ sprintf( buf, ")" );
+ *bufsize--;
+ }
+ }
+ break;
+
+ default:
+ size = 25;
+
+ if(size < *bufsize)
+ {
+ sprintf( buf, "unsupported type 0x%lX", f->f_choice );
+ *bufsize -= 25;
+ }
+ break;
+ }
+
+ return return_buf;
+}
+
+char *
+slapi_filter_to_string( const struct slapi_filter *f, char *buf, size_t bufsize )
+{
+ size_t size = bufsize;
+
+ return slapi_filter_to_string_internal( f, buf, &size );
+}
+
+/* rbyrne */
+
+static int
+filter_apply_list( struct slapi_filter *flist, FILTER_APPLY_FN fn, caddr_t arg,
+ int *error_code )
+{
+ struct slapi_filter *f;
+ int rc;
+
+ for ( f = flist; f != NULL; f = f->f_next ) {
+ rc = slapi_filter_apply( f, fn, arg, error_code );
+ if ( rc == SLAPI_FILTER_SCAN_STOP || rc == SLAPI_FILTER_SCAN_ERROR) {
+ return(rc);
+ }
+ }
+
+ /* If we get here we've applied the whole list sucessfully so return 0 */
+
+ return(SLAPI_FILTER_SCAN_NOMORE);
+}
+
+/*
+ *
+ * The idea here is to apply, fn() to each "simple filter" in f as follows:
+ * fn( Slapi_Filter *simple_filter, caddr_t arg).
+ *
+ * A 'simple filter' is anything other than AND, OR or NOT.
+ *
+ * If fn() wants the seasrch to abort it returns FILTER_SCAN_STOP.
+ * In this case, FILTER_SCAN_STOP is returned by slapi_filter_apply().
+ * Otherwise fn() should return FILTER_SCAN_CONTINUE.
+ *
+ * If the whole filter is traversed, FILTER_SCAN_NO_MORE is returned.
+ * If an error occurred during the traverse, the scan is aborted and
+ * FILTER_SCAN_ERROR is returned, and in this case error_code can be checked
+ * for more details--right now the only error is
+ * SLAPI_FILTER_UNKNOWN_FILTER_TYPE.
+ *
+ *
+ */
+int
+slapi_filter_apply( struct slapi_filter *f, FILTER_APPLY_FN fn, void *arg,
+ int *error_code)
+{
+ int rc = SLAPI_FILTER_SCAN_ERROR;
+
+ if ( f == NULL ) {
+ return SLAPI_FILTER_SCAN_NOMORE;
+ }
+
+ switch ( f->f_choice ) {
+ case LDAP_FILTER_GE:
+ case LDAP_FILTER_LE:
+ case LDAP_FILTER_APPROX:
+ case LDAP_FILTER_EQUALITY:
+ rc = (*fn)(f, arg );
+ break;
+ case LDAP_FILTER_SUBSTRINGS:
+ rc = (*fn)(f, arg);
+ /* value will be normalized later */
+ break;
+ case LDAP_FILTER_PRESENT:
+ rc = (*fn)(f, arg);
+ break;
+ case LDAP_FILTER_EXTENDED:
+ rc = (*fn)(f, arg);
+ break;
+ case LDAP_FILTER_AND:
+ rc = filter_apply_list( f->f_and, fn, arg, error_code );
+ break;
+ case LDAP_FILTER_OR:
+ rc = filter_apply_list( f->f_or, fn, arg, error_code );
+ break;
+ case LDAP_FILTER_NOT:
+ rc = filter_apply_list( f->f_not, fn, arg, error_code );
+ break;
+ default:
+ /* Unknown filter choice */
+ *error_code = SLAPI_FILTER_UNKNOWN_FILTER_TYPE;
+ rc = SLAPI_FILTER_SCAN_ERROR;
+ }
+
+ /*
+ * We propagate back FILTER_SCAN_ERROR and
+ * FILTER_SCAN_STOP, anything else is success.
+ */
+
+ if (rc != SLAPI_FILTER_SCAN_ERROR && rc != SLAPI_FILTER_SCAN_STOP) {
+ rc = SLAPI_FILTER_SCAN_NOMORE;
+ }
+
+ return(rc);
+}
+
+
+int
+filter_flag_is_set(const Slapi_Filter *f, unsigned char flag) {
+ return(f->f_flags & flag);
+}
+
+
+static int
+tombstone_check_filter(Slapi_Filter *f)
+{
+ if ( 0 == strcasecmp ( f->f_avvalue.bv_val, SLAPI_ATTR_VALUE_TOMBSTONE)) {
+ return 1; /* Contains a nsTombstone filter */
+ }
+ return 0; /* Not nsTombstone filter */
+}
+
+/* filter_optimize
+ * ---------------
+ * takes a filter and optimizes it for fast evaluation
+ * currently this merely ensures that any AND or OR
+ * does not start with a NOT sub-filter if possible
+ */
+static void
+filter_optimize(Slapi_Filter *f)
+{
+ if(!f)
+ return;
+
+ switch(f->f_choice)
+ {
+ case LDAP_FILTER_AND:
+ case LDAP_FILTER_OR:
+ {
+ /* first optimize children */
+ filter_optimize(f->f_list);
+
+ /* optimize this */
+ if(f->f_list->f_choice == LDAP_FILTER_NOT)
+ {
+ Slapi_Filter *f_prev = 0;
+ Slapi_Filter *f_child = 0;
+
+ /* grab a non not filter to place at start */
+ for(f_child = f->f_list; f_child != 0; f_child = f_child->f_next)
+ {
+ if(f_child->f_choice != LDAP_FILTER_NOT)
+ {
+ /* we have a winner, do swap */
+ f_prev->f_next = f_child->f_next;
+ f_child->f_next = f->f_list;
+ f->f_list = f_child;
+ break;
+ }
+
+ f_prev = f_child;
+ }
+ }
+ }
+ default:
+ filter_optimize(f->f_next);
+ break;
+ }
+}
+
+
+/* slapi_filter_changetype
+ * ------------------------
+ * changes the type used in equality/>/</approx filters
+ * handy for features that do type mapping
+ */
+int slapi_filter_changetype(Slapi_Filter *f, const char *newtype)
+{
+ char **target = 0;
+
+ switch ( f->f_choice ) {
+ case LDAP_FILTER_EQUALITY:
+ case LDAP_FILTER_GE:
+ case LDAP_FILTER_LE:
+ case LDAP_FILTER_APPROX:
+ target = &f->f_ava.ava_type;
+ break;
+
+ case LDAP_FILTER_SUBSTRINGS:
+ target = &f->f_sub_type;
+ break;
+
+ case LDAP_FILTER_PRESENT:
+ target = &f->f_type;
+ break;
+
+ case LDAP_FILTER_AND:
+ case LDAP_FILTER_OR:
+ case LDAP_FILTER_NOT:
+ default:
+ goto bail;
+ break;
+ }
+
+ slapi_ch_free_string(target);
+ *target = slapi_ch_strdup(newtype);
+
+bail:
+ return (!target);
+}
+
diff --git a/ldap/servers/slapd/filter.h b/ldap/servers/slapd/filter.h
new file mode 100644
index 00000000..93b02ca5
--- /dev/null
+++ b/ldap/servers/slapd/filter.h
@@ -0,0 +1,33 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+
+#ifndef _FILTER_H_
+#define _FILTER_H_
+
+#include "slapi-plugin.h" /* struct berval, Slapi_PBlock, mrFilterMatchFn */
+
+typedef Slapi_Attr* attr_ptr;
+
+typedef int (*mrf_plugin_fn) (Slapi_PBlock*);
+
+#define MRF_ANY_TYPE 1
+#define MRF_ANY_VALUE 2
+
+typedef struct mr_filter_t {
+ char* mrf_oid;
+ char* mrf_type;
+ struct berval mrf_value;
+ char mrf_dnAttrs;
+ struct slapdplugin* mrf_plugin;
+ mrFilterMatchFn mrf_match;
+ mrf_plugin_fn mrf_index;
+ unsigned int mrf_reusable; /* MRF_ANY_xxx */
+ mrf_plugin_fn mrf_reset;
+ void* mrf_object; /* whatever the implementation needs */
+ mrf_plugin_fn mrf_destroy;
+} mr_filter_t;
+
+#endif
diff --git a/ldap/servers/slapd/filtercmp.c b/ldap/servers/slapd/filtercmp.c
new file mode 100644
index 00000000..4275df94
--- /dev/null
+++ b/ldap/servers/slapd/filtercmp.c
@@ -0,0 +1,402 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+/* filtercmp.c - routines for comparing filters */
+
+#include <stdio.h>
+#include <string.h>
+#include <sys/types.h>
+#include "slap.h"
+
+
+/* very simple hash function */
+static PRUint32 addhash(PRUint32 hash, unsigned char *data, int size)
+{
+ int i;
+
+ if (!data || !size)
+ return hash;
+ for (i = 0; i < size; i++)
+ hash = (hash << 5) + (hash >> 27) + data[i];
+ return hash;
+}
+#define addhash_long(h, l) addhash((h), (unsigned char *)&(l), sizeof(long))
+#define addhash_str(h, str) addhash((h), (unsigned char *)(str), strlen(str))
+#define addhash_bv(h, bv) addhash((h), (unsigned char *)(bv).bv_val, \
+ (bv).bv_len)
+
+static PRUint32 addhash_casestr(PRUint32 hash, char *data)
+{
+ unsigned char *normstr;
+
+ normstr = slapi_utf8StrToLower((unsigned char *)data);
+ hash = addhash(hash, normstr, strlen((char *)normstr));
+ if ((char *)normstr != data)
+ slapi_ch_free((void **)&normstr);
+ return hash;
+}
+
+static PRUint32 stir(PRUint32 hash, PRUint32 x)
+{
+ hash = (hash << 5) + (hash >> 27);
+ hash = hash ^ (x << 16);
+ hash = hash ^ (x >> 16);
+ return hash;
+}
+#define STIR(h) (h) = stir((h), 0x2EC6DEAD);
+
+static Slapi_Value **get_normalized_value(struct ava *ava)
+{
+ void *plugin;
+ Slapi_Value *svlist[2], **keylist, sv;
+
+ slapi_attr_type2plugin(ava->ava_type, &plugin);
+ sv.bv = ava->ava_value;
+ sv.v_csnset = NULL;
+ svlist[0] = &sv;
+ svlist[1] = NULL;
+ if ((slapi_call_syntax_values2keys_sv(plugin, svlist, &keylist,
+ LDAP_FILTER_EQUALITY) != 0) ||
+ !keylist || !keylist[0])
+ return NULL;
+ return keylist;
+}
+
+/* this is not pretty. matching rules seem to be pretty elaborate to use,
+ * so comparing these kind of filters may be undesirably slow just because
+ * of the overhead of normalizing the values. most of this code is stolen
+ * from the backend vlv code (matchrule.c)
+ */
+static Slapi_PBlock *get_mr_normval(char *oid, char *type,
+ struct berval **inval,
+ struct berval ***outval)
+{
+ Slapi_PBlock *pb = slapi_pblock_new();
+ unsigned int sort_indicator = SLAPI_PLUGIN_MR_USAGE_SORT;
+ IFP mrIndex = NULL;
+
+ if (!pb)
+ return NULL;
+ slapi_pblock_set(pb, SLAPI_PLUGIN_MR_OID, oid);
+ slapi_pblock_set(pb, SLAPI_PLUGIN_MR_TYPE, type);
+ slapi_pblock_set(pb, SLAPI_PLUGIN_MR_USAGE, (void *)&sort_indicator);
+ if (slapi_mr_indexer_create(pb) != 0) {
+ slapi_pblock_destroy(pb);
+ return NULL;
+ }
+ if ((slapi_pblock_get(pb, SLAPI_PLUGIN_MR_INDEX_FN, &mrIndex) != 0) ||
+ !mrIndex) {
+ /* shouldn't ever happen */
+ slapi_pblock_destroy(pb);
+ return NULL;
+ }
+
+ /* now, call the indexer */
+ slapi_pblock_set(pb, SLAPI_PLUGIN_MR_VALUES, inval);
+ (*mrIndex)(pb);
+ slapi_pblock_get(pb, SLAPI_PLUGIN_MR_KEYS, outval);
+ return pb;
+}
+
+/* the opposite of above: shut down the matching rule pblock and free
+ * the memory.
+ */
+static void done_mr_normval(Slapi_PBlock *pb)
+{
+ IFP mrDestroy = NULL;
+
+ if (slapi_pblock_get(pb, SLAPI_PLUGIN_DESTROY_FN, &mrDestroy) == 0) {
+ if (mrDestroy)
+ (*mrDestroy)(pb);
+ }
+ slapi_pblock_destroy(pb);
+}
+
+static int hash_filters = 0;
+
+void set_hash_filters(int i) { hash_filters = i; }
+
+/* calculate the hash value of a node in a filter (assumes that any sub-nodes
+ * of the filter have already had their hash value calculated).
+ * -- the annoying part of this is normalizing any values in the filter.
+ */
+void filter_compute_hash(struct slapi_filter *f)
+{
+ PRUint32 h;
+ char **a;
+ struct slapi_filter *fx;
+ Slapi_Value **keylist;
+ Slapi_PBlock *pb;
+ struct berval *inval[2], **outval;
+
+ if (! hash_filters)
+ return;
+
+ h = addhash_long(0, f->f_choice);
+ switch (f->f_choice) {
+ case LDAP_FILTER_EQUALITY:
+ case LDAP_FILTER_GE:
+ case LDAP_FILTER_LE:
+ case LDAP_FILTER_APPROX:
+ keylist = get_normalized_value(&f->f_ava);
+ if (keylist) {
+ h = addhash_str(h, f->f_avtype);
+ STIR(h);
+ h = addhash_bv(h, *(slapi_value_get_berval(keylist[0])));
+ valuearray_free(&keylist);
+ }
+ break;
+ case LDAP_FILTER_SUBSTRINGS:
+ h = addhash_str(h, f->f_sub_type);
+ STIR(h);
+ if (f->f_sub_initial)
+ h = addhash_casestr(h, f->f_sub_initial);
+ if (f->f_sub_any) {
+ for (a = f->f_sub_any; *a; a++) {
+ STIR(h);
+ h = addhash_casestr(h, *a);
+ }
+ }
+ STIR(h);
+ if (f->f_sub_final)
+ h = addhash_casestr(h, f->f_sub_final);
+ break;
+ case LDAP_FILTER_PRESENT:
+ h = addhash_str(h, f->f_type);
+ break;
+ case LDAP_FILTER_AND:
+ case LDAP_FILTER_OR:
+ case LDAP_FILTER_NOT:
+ /* should be able to just mix in the hashes from lower levels */
+ for (fx = f->f_list; fx; fx = fx->f_next)
+ h = h ^ fx->f_hash;
+ break;
+ case LDAP_FILTER_EXTENDED:
+ if (f->f_mr_oid)
+ h = addhash_str(h, f->f_mr_oid);
+ STIR(h);
+ if (f->f_mr_type)
+ h = addhash_str(h, f->f_mr_type);
+ inval[0] = &f->f_mr_value;
+ inval[1] = NULL;
+ /* get the normalized value (according to the matching rule) */
+ pb = get_mr_normval(f->f_mr_oid, f->f_mr_type, inval, &outval);
+ if (pb && outval && outval[0]) {
+ STIR(h);
+ h = addhash_bv(h, *(outval[0]));
+ }
+ done_mr_normval(pb);
+ if (f->f_mr_dnAttrs)
+ STIR(h);
+ break;
+ default:
+ LDAPDebug(LDAP_DEBUG_ANY, "$$$ can't handle filter type %d !\n",
+ f->f_choice, 0, 0);
+ }
+
+ f->f_hash = h;
+}
+
+
+/* match compare: given two arrays of size N, determine if each item in
+ * the first array matches with each item in the second array, with a
+ * one-to-one correspondence. this will be DOG SLOW for large values of N
+ * (it scales as N^2) but we generally expect N < 5.
+ */
+static int filter_compare_substrings(struct slapi_filter *f1,
+ struct slapi_filter *f2)
+{
+ int buf[20], *tally;
+ char **a1, **a2;
+ int count1 = 0, count2 = 0, ret, i, j, ok;
+
+ /* ok to pass NULL to utf8casecmp */
+
+ if ((slapi_UTF8CASECMP(f1->f_sub_initial, f2->f_sub_initial) != 0) ||
+ (slapi_UTF8CASECMP(f1->f_sub_final, f2->f_sub_final) != 0))
+ return 1;
+ /* match compare (would be expensive for large numbers of 'any'
+ * substrings, which we don't expect to see)
+ */
+ for (a1 = f1->f_sub_any; a1 && *a1; a1++, count1++);
+ for (a2 = f2->f_sub_any; a2 && *a2; a2++, count2++);
+ if (count1 != count2)
+ return 1;
+ ret = 1; /* assume failure until done comparing */
+ if (count1 > 20)
+ tally = (int *)malloc(count1);
+ else
+ tally = buf;
+ if (!tally)
+ goto done; /* this is bad; out of memory */
+ for (i = 0; i < count1; i++)
+ tally[i] = 0;
+ /* ok. the theory is we tally up all the matched pairs we find,
+ * stopping if we can't find a match that hasn't already been paired.
+ */
+ a1 = f1->f_sub_any;
+ for (i = 0; i < count1; i++, a1++) {
+ a2 = f2->f_sub_any;
+ ok = 0;
+ for (j = 0; j < count1; j++, a2++) {
+ if (!tally[j] && (slapi_UTF8CASECMP(*a1, *a2) == 0)) {
+ tally[j] = ok = 1;
+ break;
+ }
+ }
+ if (!ok)
+ goto done; /* didn't find a match for that one */
+ }
+ /* done! matched */
+ ret = 0;
+
+done:
+ if ((count1 > 20) && tally)
+ free(tally);
+ return ret;
+}
+
+/* same as above, but this time for lists of filter nodes */
+static int filter_compare_lists(struct slapi_filter *f1,
+ struct slapi_filter *f2)
+{
+ int buf[20], *tally;
+ struct slapi_filter *fx1, *fx2;
+ int count1 = 0, count2 = 0, ret, i, j, ok;
+
+ for (fx1 = f1->f_list; fx1; fx1 = fx1->f_next, count1++);
+ for (fx2 = f2->f_list; fx2; fx2 = fx2->f_next, count2++);
+ if (count1 != count2)
+ return 1;
+ ret = 1;
+ if (count1 > 20)
+ tally = (int *)malloc(count1);
+ else
+ tally = buf;
+ if (!tally)
+ goto done; /* very bad */
+ for (i = 0; i < count1; i++)
+ tally[i] = 0;
+ /* brute-force match compare now */
+ fx1 = f1->f_list;
+ for (i = 0; i < count1; i++, fx1 = fx1->f_next) {
+ fx2 = f2->f_list;
+ ok = 0;
+ for (j = 0; j < count1; j++, fx2 = fx2->f_next) {
+ if (!tally[j] && (slapi_filter_compare(fx1, fx2) == 0)) {
+ tally[j] = ok = 1;
+ break;
+ }
+ }
+ if (!ok)
+ goto done; /* no match */
+ }
+ /* done! all matched */
+ ret = 0;
+
+done:
+ if ((count1 > 20) && tally)
+ free(tally);
+ return ret;
+}
+
+/* returns 0 if two filters are "identical"
+ * (items under AND/OR are allowed to be in different order)
+ */
+int slapi_filter_compare(struct slapi_filter *f1, struct slapi_filter *f2)
+{
+ Slapi_Value **key1, **key2;
+ Slapi_PBlock *pb1, *pb2;
+ struct berval *inval1[2], *inval2[2], **outval1, **outval2;
+ int ret;
+
+ LDAPDebug(LDAP_DEBUG_TRACE, "=> filter compare\n", 0, 0, 0);
+
+ /* allow for the possibility that one of the filters hasn't had a hash
+ * computed (and is therefore 0). this means that a filter node whose
+ * hash is computed as 0 will always get compared the expensive way,
+ * but this should happen VERY rarely (if ever).
+ */
+ if ((f1->f_hash != f2->f_hash) && (f1->f_hash) && (f2->f_hash)) {
+ ret = 1;
+ goto done;
+ }
+
+ /* brute-force comparison now */
+ if (f1->f_choice != f2->f_choice) {
+ ret = 1;
+ goto done;
+ }
+ switch (f1->f_choice) {
+ case LDAP_FILTER_EQUALITY:
+ case LDAP_FILTER_GE:
+ case LDAP_FILTER_LE:
+ case LDAP_FILTER_APPROX:
+ if (slapi_UTF8CASECMP(f1->f_avtype, f2->f_avtype) != 0) {
+ ret = 1;
+ break;
+ }
+ key1 = get_normalized_value(&f1->f_ava);
+ if (key1) {
+ key2 = get_normalized_value(&f2->f_ava);
+ if (key2) {
+ ret = memcmp(slapi_value_get_string(key1[0]),
+ slapi_value_get_string(key2[0]),
+ slapi_value_get_length(key1[0]));
+ valuearray_free(&key1);
+ valuearray_free(&key2);
+ break;
+ }
+ valuearray_free(&key1);
+ }
+ ret = 1;
+ break;
+ case LDAP_FILTER_PRESENT:
+ ret = (slapi_UTF8CASECMP(f1->f_type, f2->f_type));
+ break;
+ case LDAP_FILTER_SUBSTRINGS:
+ ret = filter_compare_substrings(f1, f2);
+ break;
+ case LDAP_FILTER_AND:
+ case LDAP_FILTER_OR:
+ case LDAP_FILTER_NOT:
+ ret = filter_compare_lists(f1, f2);
+ break;
+ case LDAP_FILTER_EXTENDED:
+ if ((slapi_UTF8CASECMP(f1->f_mr_oid, f2->f_mr_oid) != 0) ||
+ (slapi_UTF8CASECMP(f1->f_mr_type, f2->f_mr_type) != 0) ||
+ (f1->f_mr_dnAttrs != f2->f_mr_dnAttrs)) {
+ ret = 1;
+ break;
+ }
+ /* painstakingly compare the values (using the matching rule) */
+ inval1[0] = &f1->f_mr_value;
+ inval2[0] = &f2->f_mr_value;
+ inval1[1] = inval2[1] = NULL;
+ pb1 = get_mr_normval(f1->f_mr_oid, f1->f_mr_type, inval1, &outval1);
+ pb2 = get_mr_normval(f2->f_mr_oid, f2->f_mr_type, inval2, &outval2);
+ if (!pb1 || !pb2 || !outval1 || !outval2 || !outval1[0] ||
+ !outval2[0] || (outval1[0]->bv_len != outval2[0]->bv_len) ||
+ (memcmp(outval1[0]->bv_val, outval2[0]->bv_val,
+ outval1[0]->bv_len) != 0)) {
+ ret = 1;
+ } else {
+ ret = 0;
+ }
+ if (pb1)
+ done_mr_normval(pb1);
+ if (pb2)
+ done_mr_normval(pb2);
+ break;
+ default:
+ LDAPDebug(LDAP_DEBUG_ANY, "ERR can't handle filter %d\n", f1->f_choice,
+ 0, 0);
+ ret = 1;
+ }
+
+done:
+ LDAPDebug(LDAP_DEBUG_TRACE, "<= filter compare: %d\n", ret, 0, 0);
+ return ret;
+}
diff --git a/ldap/servers/slapd/filterentry.c b/ldap/servers/slapd/filterentry.c
new file mode 100644
index 00000000..6c6bc4b4
--- /dev/null
+++ b/ldap/servers/slapd/filterentry.c
@@ -0,0 +1,1000 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+/* filterentry.c - apply a filter to an entry */
+
+#include <stdio.h>
+#include <string.h>
+#include <sys/types.h>
+#ifndef _WIN32
+#include <sys/socket.h>
+#endif
+#include "slap.h"
+
+static int test_filter_list();
+static int test_extensible_filter();
+
+static int vattr_test_filter_list();
+static int test_filter_access( Slapi_PBlock *pb, Slapi_Entry*e,
+ char * attr_type, struct berval *attr_val);
+static int slapi_vattr_filter_test_ext_internal( Slapi_PBlock *pb, Slapi_Entry *e,
+ struct slapi_filter *f, int verify_access, int only_check_access, int *access_check_done);
+
+static char *opt_str = 0;
+static int opt = 0;
+
+static int optimise_filter_acl_tests()
+{
+ if(!opt_str)
+ {
+ opt_str = getenv( "NS_DS_OPT_FILT_ACL_EVAL" );
+ if(opt_str)
+ opt = !strcasecmp(opt_str, "false");
+ else
+ opt = 0;
+ if(!opt_str)
+ opt_str = "dummy";
+ }
+
+ return opt;
+}
+
+/*
+ * slapi_filter_test - test a filter against a single entry.
+ * returns 0 filter matched
+ * -1 filter did not match
+ * >0 an ldap error code
+ */
+
+int
+slapi_filter_test(
+ Slapi_PBlock *pb,
+ Slapi_Entry *e,
+ struct slapi_filter *f,
+ int verify_access
+)
+{
+ return slapi_filter_test_ext(pb,e,f,verify_access,0);
+}
+
+/*
+ * slapi_filter_test_simple - test without checking access control
+ *
+ * returns 0 filter matched
+ * -1 filter did not match
+ * >0 an ldap error code
+ */
+int
+slapi_filter_test_simple(
+ Slapi_Entry *e,
+ struct slapi_filter *f
+)
+{
+ return slapi_vattr_filter_test_ext(NULL,e,f,0,0);
+}
+
+/*
+ * slapi_filter_test_ext - full-feature filter test function
+ *
+ * returns 0 filter matched
+ * -1 filter did not match
+ * >0 an ldap error code
+ */
+
+int
+slapi_filter_test_ext_internal(
+ Slapi_PBlock *pb,
+ Slapi_Entry *e,
+ struct slapi_filter *f,
+ int verify_access,
+ int only_check_access,
+ int *access_check_done
+)
+{
+ int rc;
+
+ LDAPDebug( LDAP_DEBUG_FILTER, "=> slapi_filter_test_ext\n", 0, 0, 0 );
+
+ /*
+ * RJP: Not sure if this is semantically right, but we have to
+ * return something if f is NULL. If there is no filter,
+ * then we say that it did match and return 0.
+ */
+ if ( f == NULL) {
+ return(0);
+ }
+
+ switch ( f->f_choice ) {
+ case LDAP_FILTER_EQUALITY:
+ LDAPDebug( LDAP_DEBUG_FILTER, " EQUALITY\n", 0, 0, 0 );
+ rc = test_ava_filter( pb, e, e->e_attrs, &f->f_ava, LDAP_FILTER_EQUALITY,
+ verify_access , only_check_access, access_check_done);
+ break;
+
+ case LDAP_FILTER_SUBSTRINGS:
+ LDAPDebug( LDAP_DEBUG_FILTER, " SUBSTRINGS\n", 0, 0, 0 );
+ rc = test_substring_filter( pb, e, f, verify_access , only_check_access, access_check_done);
+ break;
+
+ case LDAP_FILTER_GE:
+ LDAPDebug( LDAP_DEBUG_FILTER, " GE\n", 0, 0, 0 );
+ rc = test_ava_filter( pb, e, e->e_attrs, &f->f_ava, LDAP_FILTER_GE,
+ verify_access , only_check_access, access_check_done);
+ break;
+
+ case LDAP_FILTER_LE:
+ LDAPDebug( LDAP_DEBUG_FILTER, " LE\n", 0, 0, 0 );
+ rc = test_ava_filter( pb, e, e->e_attrs, &f->f_ava, LDAP_FILTER_LE,
+ verify_access , only_check_access, access_check_done);
+ break;
+
+ case LDAP_FILTER_PRESENT:
+ LDAPDebug( LDAP_DEBUG_FILTER, " PRESENT\n", 0, 0, 0 );
+ rc = test_presence_filter( pb, e, f->f_type, verify_access , only_check_access, access_check_done);
+ break;
+
+ case LDAP_FILTER_APPROX:
+ LDAPDebug( LDAP_DEBUG_FILTER, " APPROX\n", 0, 0, 0 );
+ rc = test_ava_filter( pb, e, e->e_attrs, &f->f_ava, LDAP_FILTER_APPROX,
+ verify_access , only_check_access, access_check_done);
+ break;
+
+ case LDAP_FILTER_EXTENDED:
+ LDAPDebug( LDAP_DEBUG_FILTER, " EXTENDED\n", 0, 0, 0 );
+ rc = test_extensible_filter( pb, e, &f->f_mr, verify_access , only_check_access, access_check_done);
+ break;
+
+ case LDAP_FILTER_AND:
+ LDAPDebug( LDAP_DEBUG_FILTER, " AND\n", 0, 0, 0 );
+ rc = test_filter_list( pb, e, f->f_and,
+ LDAP_FILTER_AND , verify_access, only_check_access, access_check_done);
+ break;
+
+ case LDAP_FILTER_OR:
+ LDAPDebug( LDAP_DEBUG_FILTER, " OR\n", 0, 0, 0 );
+ rc = test_filter_list( pb, e, f->f_or,
+ LDAP_FILTER_OR , verify_access, only_check_access, access_check_done);
+ break;
+
+ case LDAP_FILTER_NOT:
+ LDAPDebug( LDAP_DEBUG_FILTER, " NOT\n", 0, 0, 0 );
+ rc = slapi_filter_test_ext_internal( pb, e, f->f_not , verify_access, only_check_access, access_check_done);
+ if(!(verify_access && only_check_access)) /* dont play with access control return codes */
+ {
+ if(verify_access && !rc && !(*access_check_done))
+ {
+ /* the filter failed so access control was not checked
+ * for NOT filters this is significant so we must ensure
+ * access control is checked
+ */
+ /* check access control only */
+ rc = slapi_filter_test_ext_internal( pb, e, f->f_not , verify_access, -1 /*only_check_access*/, access_check_done);
+ /* preserve error code if any */
+ if(!rc)
+ rc = !rc;
+ }
+ else
+ rc = !rc;
+ }
+
+ break;
+
+ default:
+ LDAPDebug( LDAP_DEBUG_ANY, " unknown filter type 0x%lX\n",
+ f->f_choice, 0, 0 );
+ rc = -1;
+ }
+
+ LDAPDebug( LDAP_DEBUG_FILTER, "<= slapi_filter_test %d\n", rc, 0, 0 );
+ return( rc );
+}
+
+
+int
+slapi_filter_test_ext(
+ Slapi_PBlock *pb,
+ Slapi_Entry *e,
+ struct slapi_filter *f,
+ int verify_access,
+ int only_check_access
+)
+{
+ int rc = 0; /* a no op request succeeds */
+ int access_check_done = 0;
+
+ switch ( f->f_choice ) {
+ case LDAP_FILTER_AND:
+ case LDAP_FILTER_OR:
+ case LDAP_FILTER_NOT:
+ /*
+ * optimize acl checking by only doing it once it is
+ * known that the whole filter passes and so the entry
+ * is eligible to be returned.
+ * then we check the filter only for access
+ *
+ * complex filters really benefit from
+ * separate stages, filter eval, followed by acl check...
+ */
+ if(!only_check_access)
+ {
+ rc = slapi_filter_test_ext_internal(pb,e,f,0,0, &access_check_done);
+ }
+
+ if(rc == 0 && verify_access)
+ {
+ rc = slapi_filter_test_ext_internal(pb,e,f,-1,-1, &access_check_done);
+ }
+
+ break;
+
+ default:
+ /*
+ * ...but simple filters are better off doing eval and
+ * acl check at once
+ */
+ rc = slapi_filter_test_ext_internal(pb,e,f,verify_access,only_check_access, &access_check_done);
+ break;
+ }
+
+ return rc;
+}
+
+
+int test_ava_filter(
+ Slapi_PBlock *pb,
+ Slapi_Entry *e,
+ Slapi_Attr *a,
+ struct ava *ava,
+ int ftype,
+ int verify_access,
+ int only_check_access,
+ int *access_check_done
+)
+{
+ int rc;
+
+ LDAPDebug( LDAP_DEBUG_FILTER, "=> test_ava_filter\n", 0, 0, 0 );
+
+ *access_check_done = 0;
+
+ if(optimise_filter_acl_tests())
+ {
+ rc = 0;
+
+ if(!only_check_access)
+ {
+ rc = -1;
+ for ( ; a != NULL; a = a->a_next ) {
+ if ( slapi_attr_type_cmp( ava->ava_type, a->a_type, SLAPI_TYPE_CMP_SUBTYPE ) == 0 ) {
+ rc = plugin_call_syntax_filter_ava( a, ftype, ava );
+ if ( rc == 0 ) {
+ break;
+ }
+ }
+ }
+ }
+
+ if ( rc == 0 && verify_access && pb != NULL ) {
+ char *attrs[2] = { NULL, NULL };
+ attrs[0] = ava->ava_type;
+ rc = plugin_call_acl_plugin( pb, e, attrs, &ava->ava_value,
+ SLAPI_ACL_SEARCH, ACLPLUGIN_ACCESS_DEFAULT, NULL );
+ *access_check_done = -1;
+ }
+
+ }
+ else
+ {
+ if ( verify_access && pb != NULL ) {
+ char *attrs[2] = { NULL, NULL };
+ attrs[0] = ava->ava_type;
+ rc = plugin_call_acl_plugin( pb, e, attrs, &ava->ava_value,
+ SLAPI_ACL_SEARCH, ACLPLUGIN_ACCESS_DEFAULT, NULL );
+ *access_check_done = -1;
+ if ( only_check_access || rc != LDAP_SUCCESS ) {
+ LDAPDebug( LDAP_DEBUG_FILTER, "<= test_ava_filter %d\n",
+ rc, 0, 0 );
+ return( rc );
+ }
+ }
+
+ rc = -1;
+ for ( ; a != NULL; a = a->a_next ) {
+ if ( slapi_attr_type_cmp( ava->ava_type, a->a_type, SLAPI_TYPE_CMP_SUBTYPE ) == 0 ) {
+ rc = plugin_call_syntax_filter_ava( a, ftype, ava );
+ if ( rc == 0 ) {
+ break;
+ }
+ }
+ }
+ }
+
+ LDAPDebug( LDAP_DEBUG_FILTER, "<= test_ava_filter %d\n", rc, 0, 0 );
+ return( rc );
+}
+
+int
+test_presence_filter(
+ Slapi_PBlock *pb,
+ Slapi_Entry *e,
+ char *type,
+ int verify_access,
+ int only_check_access,
+ int *access_check_done
+)
+{
+ int rc;
+ void *hint = NULL;
+
+ *access_check_done = 0;
+
+ if(optimise_filter_acl_tests())
+ {
+ rc = 0;
+ /* Use attrlist_find_ex to get subtype matching */
+ if(!only_check_access)
+ {
+ rc = attrlist_find_ex( e->e_attrs, type,
+ NULL, NULL, &hint ) != NULL ? 0 : -1;
+ }
+
+ if (rc == 0 && verify_access && pb != NULL) {
+ char *attrs[2] = { NULL, NULL };
+ attrs[0] = type;
+ rc = plugin_call_acl_plugin( pb, e, attrs, NULL, SLAPI_ACL_SEARCH,
+ ACLPLUGIN_ACCESS_DEFAULT, NULL );
+ *access_check_done = -1;
+ }
+ }
+ else
+ {
+ if (verify_access && pb != NULL) {
+ char *attrs[2] = { NULL, NULL };
+ attrs[0] = type;
+ rc = plugin_call_acl_plugin( pb, e, attrs, NULL, SLAPI_ACL_SEARCH,
+ ACLPLUGIN_ACCESS_DEFAULT, NULL );
+ *access_check_done = -1;
+ if ( only_check_access || rc != LDAP_SUCCESS ) {
+ return( rc );
+ }
+ }
+
+ /* Use attrlist_find_ex to get subtype matching */
+ rc = attrlist_find_ex( e->e_attrs, type,
+ NULL, NULL, &hint ) != NULL ? 0 : -1;
+ }
+
+ return rc;
+}
+
+/*
+ * Convert a DN into a list of attribute values.
+ * The caller must free the returned attributes.
+ */
+static Slapi_Attr*
+dn2attrs(const char *dn)
+{
+ int rc= 0;
+ Slapi_Attr* dnAttrs = NULL;
+ char** rdns = ldap_explode_dn (dn, 0);
+ if (rdns)
+ {
+ char** rdn = rdns;
+ for (; !rc && *rdn; ++rdn)
+ {
+ char** avas = ldap_explode_rdn (*rdn, 0);
+ if (avas)
+ {
+ char** ava = avas;
+ for (; !rc && *ava; ++ava)
+ {
+ char* val = strchr (*ava, '=');
+ if (val)
+ {
+ struct berval bv;
+ struct berval* bvec[] = {NULL, NULL};
+ size_t type_len = val - *ava;
+ char* type = slapi_ch_malloc (type_len + 1);
+ memcpy (type, *ava, type_len);
+ type[type_len] = '\0';
+ ++val; /* skip the '=' */
+ bv.bv_val = val;
+ bv.bv_len = strlen(val);
+ bvec[0] = &bv;
+ attrlist_merge (&dnAttrs, type, bvec);
+ }
+ }
+ ldap_value_free (avas);
+ }
+ }
+ ldap_value_free (rdns);
+ }
+ return dnAttrs;
+}
+
+static int
+test_extensible_filter(
+ Slapi_PBlock *callers_pb,
+ Slapi_Entry *e,
+ mr_filter_t *mrf,
+ int verify_access,
+ int only_check_access,
+ int *access_check_done
+)
+{
+ /*
+ * The ABNF for extensible filters is
+ *
+ * attr [":dn"] [":" matchingrule] ":=" value
+ * [":dn"] ":" matchingrule ":=" value
+ *
+ * So, sigh, there are six possible combinations:
+ *
+ * A) attr ":=" value
+ * B) attr ":dn" ":=" value
+ * C) attr ":" matchingrule ":=" value
+ * D) attr ":dn" ":" matchingrule ":=" value
+ * E) ":" matchingrule ":=" value
+ * F) ":dn" ":" matchingrule ":=" value
+ */
+ int rc;
+
+ LDAPDebug( LDAP_DEBUG_FILTER, "=> test_extensible_filter\n", 0, 0, 0 );
+
+ *access_check_done = 0;
+
+ if(optimise_filter_acl_tests())
+ {
+ rc = LDAP_SUCCESS;
+
+ if(!only_check_access)
+ {
+ if (mrf->mrf_match==NULL)
+ {
+ /*
+ * Could be A or B
+ * No matching function. So use a regular equality filter.
+ * Check the regular attributes for the attribute value.
+ */
+ struct ava a;
+ a.ava_type= mrf->mrf_type;
+ a.ava_value.bv_len= mrf->mrf_value.bv_len;
+ a.ava_value.bv_val = mrf->mrf_value.bv_val;
+ rc= test_ava_filter( callers_pb, e, e->e_attrs, &a, LDAP_FILTER_EQUALITY, 0 /* Don't Verify Access */ , 0 /* don't just verify access */, access_check_done );
+ if(rc!=LDAP_SUCCESS && mrf->mrf_dnAttrs)
+ {
+ /* B) Also check the DN attributes for the attribute value */
+ Slapi_Attr* dnattrs= dn2attrs(slapi_entry_get_dn_const(e));
+ rc= test_ava_filter( callers_pb, e, dnattrs, &a, LDAP_FILTER_EQUALITY, 0 /* Don't Verify Access */ , 0 /* don't just verify access */, access_check_done );
+ slapi_attr_free( &dnattrs );
+ }
+ }
+ else
+ {
+ /*
+ * Could be C, D, E, or F
+ * We have a matching rule.
+ */
+ rc = mrf->mrf_match (mrf->mrf_object, e, e->e_attrs);
+ if(rc!=LDAP_SUCCESS && mrf->mrf_dnAttrs)
+ {
+ /* D & F) Also check the DN attributes for the attribute value */
+ Slapi_Attr* dnattrs= dn2attrs(slapi_entry_get_dn_const(e));
+ mrf->mrf_match (mrf->mrf_object, e, dnattrs);
+ slapi_attr_free( &dnattrs );
+ }
+ }
+ }
+
+ if(rc == 0 && mrf->mrf_type!=NULL && verify_access)
+ {
+ char *attrs[2] = { NULL, NULL };
+ /* Could be A, B, C, or D */
+ /* Check we have access to this attribute on this entry */
+ attrs[0] = mrf->mrf_type;
+ rc= plugin_call_acl_plugin (callers_pb, e, attrs, &(mrf->mrf_value),
+ SLAPI_ACL_SEARCH, ACLPLUGIN_ACCESS_DEFAULT, NULL );
+ *access_check_done = -1;
+ }
+ }
+ else
+ {
+ rc = LDAP_SUCCESS;
+
+ if(mrf->mrf_type!=NULL && verify_access)
+ {
+ char *attrs[2] = { NULL, NULL };
+ /* Could be A, B, C, or D */
+ /* Check we have access to this attribute on this entry */
+ attrs[0] = mrf->mrf_type;
+ rc= plugin_call_acl_plugin (callers_pb, e, attrs, &(mrf->mrf_value),
+ SLAPI_ACL_SEARCH, ACLPLUGIN_ACCESS_DEFAULT, NULL );
+ *access_check_done = -1;
+
+ if ( only_check_access ) {
+ return rc;
+ }
+ }
+ if(rc==LDAP_SUCCESS)
+ {
+ if (mrf->mrf_match==NULL)
+ {
+ /*
+ * Could be A or B
+ * No matching function. So use a regular equality filter.
+ * Check the regular attributes for the attribute value.
+ */
+ struct ava a;
+ a.ava_type= mrf->mrf_type;
+ a.ava_value.bv_len= mrf->mrf_value.bv_len;
+ a.ava_value.bv_val = mrf->mrf_value.bv_val;
+ rc= test_ava_filter( callers_pb, e, e->e_attrs, &a, LDAP_FILTER_EQUALITY, 0 /* Don't Verify Access */ , 0 /* don't just verify access */, access_check_done );
+ if(rc!=LDAP_SUCCESS && mrf->mrf_dnAttrs)
+ {
+ /* B) Also check the DN attributes for the attribute value */
+ Slapi_Attr* dnattrs= dn2attrs(slapi_entry_get_dn_const(e));
+ rc= test_ava_filter( callers_pb, e, dnattrs, &a, LDAP_FILTER_EQUALITY, 0 /* Don't Verify Access */ , 0 /* don't just verify access */, access_check_done );
+ slapi_attr_free( &dnattrs );
+ }
+ }
+ else
+ {
+ /*
+ * Could be C, D, E, or F
+ * We have a matching rule.
+ */
+ rc = mrf->mrf_match (mrf->mrf_object, e, e->e_attrs);
+ if(rc!=LDAP_SUCCESS && mrf->mrf_dnAttrs)
+ {
+ /* D & F) Also check the DN attributes for the attribute value */
+ Slapi_Attr* dnattrs= dn2attrs(slapi_entry_get_dn_const(e));
+ mrf->mrf_match (mrf->mrf_object, e, dnattrs);
+ slapi_attr_free( &dnattrs );
+ }
+ }
+ }
+ }
+
+ LDAPDebug( LDAP_DEBUG_FILTER, "<= test_extensible_filter %d\n", rc, 0, 0 );
+ return( rc );
+}
+
+
+static int
+test_filter_list(
+ Slapi_PBlock *pb,
+ Slapi_Entry *e,
+ struct slapi_filter *flist,
+ int ftype,
+ int verify_access,
+ int only_check_access,
+ int *access_check_done
+)
+{
+ int nomatch;
+ struct slapi_filter *f;
+ int access_check_tmp = -1;
+
+ LDAPDebug( LDAP_DEBUG_FILTER, "=> test_filter_list\n", 0, 0, 0 );
+
+ *access_check_done = -1;
+
+ nomatch = 1;
+ for ( f = flist; f != NULL; f = f->f_next ) {
+ if ( slapi_filter_test_ext_internal( pb, e, f, verify_access, only_check_access, &access_check_tmp ) != 0 ) {
+ /* optimize AND evaluation */
+ if ( ftype == LDAP_FILTER_AND ) {
+ /* one false is failure */
+ nomatch = 1;
+ break;
+ }
+ } else {
+ nomatch = 0;
+
+ /* optimize OR evaluation too */
+ if ( ftype == LDAP_FILTER_OR ) {
+ /* only one needs to be true */
+ break;
+ }
+ }
+
+ if(!access_check_tmp)
+ *access_check_done = 0;
+ }
+
+ LDAPDebug( LDAP_DEBUG_FILTER, "<= test_filter_list %d\n", nomatch, 0, 0 );
+ return( nomatch );
+}
+
+void
+filter_strcpy_special( char *d, char *s )
+{
+ for ( ; *s; s++ ) {
+ switch ( *s ) {
+ case '.':
+ case '\\':
+ case '[':
+ case ']':
+ case '*':
+ case '+':
+ case '^':
+ case '$':
+ *d++ = '\\';
+ /* FALL */
+ default:
+ *d++ = *s;
+ }
+ }
+ *d = '\0';
+}
+
+int test_substring_filter(
+ Slapi_PBlock *pb,
+ Slapi_Entry *e,
+ struct slapi_filter *f,
+ int verify_access,
+ int only_check_access,
+ int *access_check_done
+)
+{
+ Slapi_Attr *a;
+ int rc;
+
+ LDAPDebug( LDAP_DEBUG_FILTER, "=> test_substring_filter\n", 0, 0, 0 );
+
+ *access_check_done = 0;
+
+ if(optimise_filter_acl_tests())
+ {
+ rc = 0;
+
+ if(!only_check_access)
+ {
+ rc = -1;
+ for ( a = e->e_attrs; a != NULL; a = a->a_next ) {
+ if ( slapi_attr_type_cmp( f->f_sub_type, a->a_type, SLAPI_TYPE_CMP_SUBTYPE ) == 0 ) {
+ rc = plugin_call_syntax_filter_sub( a, &f->f_sub );
+ if ( rc == 0 ) {
+ break;
+ }
+ }
+ }
+ }
+
+ if ( rc == 0 && verify_access && pb != NULL) {
+ char *attrs[2] = { NULL, NULL };
+ attrs[0] = f->f_sub_type;
+ rc = plugin_call_acl_plugin( pb, e, attrs, NULL,
+ SLAPI_ACL_SEARCH, ACLPLUGIN_ACCESS_DEFAULT, NULL );
+ *access_check_done = -1;
+ }
+ }
+ else
+ {
+ if ( verify_access && pb != NULL) {
+ char *attrs[2] = { NULL, NULL };
+ attrs[0] = f->f_sub_type;
+ rc = plugin_call_acl_plugin( pb, e, attrs, NULL,
+ SLAPI_ACL_SEARCH, ACLPLUGIN_ACCESS_DEFAULT, NULL );
+ *access_check_done = -1;
+ if ( only_check_access || rc != LDAP_SUCCESS ) {
+ return( rc );
+ }
+ }
+
+
+ rc = -1;
+ for ( a = e->e_attrs; a != NULL; a = a->a_next ) {
+ if ( slapi_attr_type_cmp( f->f_sub_type, a->a_type, SLAPI_TYPE_CMP_SUBTYPE ) == 0 ) {
+ rc = plugin_call_syntax_filter_sub( a, &f->f_sub );
+ if ( rc == 0 ) {
+ break;
+ }
+ }
+ }
+ }
+
+ LDAPDebug( LDAP_DEBUG_FILTER, "<= test_substring_filter %d\n",
+ rc, 0, 0 );
+ return( rc );
+}
+
+/*
+ * Here's a duplicate vattr filter test code modified to support vattrs.
+*/
+
+/*
+ * slapi_vattr_filter_test - test a filter against a single entry.
+ *
+ * Supports the case where the filter mentions virtual attributes.
+ * Performance for a real attr only filter is same as for slapi_filter_test()
+ * No explicit support for vattrs in extended filters because:
+ * 1. the matching rules must support virtual attributes themselves.
+ * 2. if no matching rule is specified it defaults to equality so
+ * could just use a normal filter with equality.
+ * 3. virtual naming attributes are probably too complex to support.
+ *
+ * returns 0 filter matched
+ * -1 filter did not match
+ * >0 an ldap error code
+ */
+
+int
+slapi_vattr_filter_test(
+ Slapi_PBlock *pb,
+ Slapi_Entry *e,
+ struct slapi_filter *f,
+ int verify_access
+)
+{
+ return slapi_vattr_filter_test_ext(pb,e,f,verify_access,0);
+}
+
+/*
+ * vattr_filter_test_ext - full-feature filter test function
+ *
+ * returns 0 filter matched
+ * -1 filter did not match
+ * >0 an ldap error code
+ */
+int
+slapi_vattr_filter_test_ext(
+ Slapi_PBlock *pb,
+ Slapi_Entry *e,
+ struct slapi_filter *f,
+ int verify_access,
+ int only_check_access
+)
+{
+ int rc = 0; /* a no op request succeeds */
+ int access_check_done = 0;
+
+ switch ( f->f_choice ) {
+ case LDAP_FILTER_AND:
+ case LDAP_FILTER_OR:
+ case LDAP_FILTER_NOT:
+ /*
+ * optimize acl checking by only doing it once it is
+ * known that the whole filter passes and so the entry
+ * is eligible to be returned.
+ * then we check the filter only for access
+ *
+ * complex filters really benefit from
+ * separate stages, filter eval, followed by acl check...
+ */
+ if(!only_check_access)
+ {
+ rc = slapi_vattr_filter_test_ext_internal(pb,e,f,0,0, &access_check_done);
+ }
+
+ if(rc == 0 && verify_access)
+ {
+ rc = slapi_vattr_filter_test_ext_internal(pb,e,f,-1,-1, &access_check_done);
+ }
+
+ break;
+
+ default:
+ /*
+ * ...but simple filters are better off doing eval and
+ * acl check at once
+ */
+ rc = slapi_vattr_filter_test_ext_internal(pb,e,f,verify_access,only_check_access, &access_check_done);
+ break;
+ }
+
+ return rc;
+}
+
+static int
+slapi_vattr_filter_test_ext_internal(
+ Slapi_PBlock *pb,
+ Slapi_Entry *e,
+ struct slapi_filter *f,
+ int verify_access,
+ int only_check_access,
+ int *access_check_done
+)
+{
+ int rc = LDAP_SUCCESS;
+
+ LDAPDebug( LDAP_DEBUG_FILTER, "=> slapi_vattr_filter_test_ext\n", 0, 0, 0 );
+
+ /*
+ * RJP: Not sure if this is semantically right, but we have to
+ * return something if f is NULL. If there is no filter,
+ * then we say that it did match and return 0.
+ */
+ if ( f == NULL) {
+ return(0);
+ }
+
+ LDAPDebug( LDAP_DEBUG_FILTER, "=> test_substring_filter\n", 0, 0, 0 );
+
+ switch ( f->f_choice ) {
+ case LDAP_FILTER_EQUALITY:
+ LDAPDebug( LDAP_DEBUG_FILTER, " EQUALITY\n", 0, 0, 0 );
+ if ( verify_access ) {
+ rc = test_filter_access( pb, e, f->f_ava.ava_type,
+ &f->f_ava.ava_value);
+ *access_check_done = 1;
+ }
+ if ( only_check_access || rc != LDAP_SUCCESS ) {
+ return( rc );
+ }
+ rc = vattr_test_filter( e, f, FILTER_TYPE_AVA, f->f_ava.ava_type );
+ break;
+
+ case LDAP_FILTER_SUBSTRINGS:
+ LDAPDebug( LDAP_DEBUG_FILTER, " SUBSTRINGS\n", 0, 0, 0 );
+ if ( verify_access ) {
+ rc = test_filter_access( pb, e, f->f_sub_type, NULL);
+ *access_check_done = 1;
+ }
+ if ( only_check_access || rc != LDAP_SUCCESS ) {
+ return( rc );
+ }
+ rc = vattr_test_filter( e, f, FILTER_TYPE_SUBSTRING, f->f_sub_type);
+ break;
+
+ case LDAP_FILTER_GE:
+ LDAPDebug( LDAP_DEBUG_FILTER, " GE\n", 0, 0, 0 );
+ if ( verify_access ) {
+ rc = test_filter_access( pb, e, f->f_ava.ava_type,
+ &f->f_ava.ava_value);
+ *access_check_done = 1;
+ }
+ if ( only_check_access || rc != LDAP_SUCCESS ) {
+ return( rc );
+ }
+ rc = vattr_test_filter( e, f, FILTER_TYPE_AVA, f->f_ava.ava_type);
+ break;
+
+ case LDAP_FILTER_LE:
+ LDAPDebug( LDAP_DEBUG_FILTER, " LE\n", 0, 0, 0 );
+ if ( verify_access ) {
+ rc = test_filter_access( pb, e, f->f_ava.ava_type,
+ &f->f_ava.ava_value);
+ *access_check_done = 1;
+ }
+ if ( only_check_access || rc != LDAP_SUCCESS ) {
+ return( rc );
+ }
+ rc = vattr_test_filter( e, f, FILTER_TYPE_AVA, f->f_ava.ava_type);
+ break;
+
+ case LDAP_FILTER_PRESENT:
+ LDAPDebug( LDAP_DEBUG_FILTER, " PRESENT\n", 0, 0, 0 );
+ if ( verify_access ) {
+ rc = test_filter_access( pb, e, f->f_type, NULL);
+ *access_check_done = 1;
+ }
+ if ( only_check_access || rc != LDAP_SUCCESS ) {
+ return( rc );
+ }
+ rc = vattr_test_filter( e, f, FILTER_TYPE_PRES, f->f_type);
+ break;
+
+ case LDAP_FILTER_APPROX:
+ LDAPDebug( LDAP_DEBUG_FILTER, " APPROX\n", 0, 0, 0 );
+ if ( verify_access ) {
+ rc = test_filter_access( pb, e, f->f_ava.ava_type,
+ &f->f_ava.ava_value);
+ *access_check_done = 1;
+ }
+ if ( only_check_access || rc != LDAP_SUCCESS ) {
+ return( rc );
+ }
+ rc = vattr_test_filter( e, f, FILTER_TYPE_AVA, f->f_ava.ava_type);
+ break;
+
+ case LDAP_FILTER_EXTENDED:
+ LDAPDebug( LDAP_DEBUG_FILTER, " EXTENDED\n", 0, 0, 0 );
+ rc = test_extensible_filter( pb, e, &f->f_mr, verify_access ,
+ only_check_access, access_check_done);
+ break;
+
+ case LDAP_FILTER_AND:
+ LDAPDebug( LDAP_DEBUG_FILTER, " AND\n", 0, 0, 0 );
+ rc = vattr_test_filter_list( pb, e, f->f_and,
+ LDAP_FILTER_AND , verify_access, only_check_access, access_check_done);
+ break;
+
+ case LDAP_FILTER_OR:
+ LDAPDebug( LDAP_DEBUG_FILTER, " OR\n", 0, 0, 0 );
+ rc = vattr_test_filter_list( pb, e, f->f_or,
+ LDAP_FILTER_OR , verify_access, only_check_access, access_check_done);
+ break;
+
+ case LDAP_FILTER_NOT:
+ LDAPDebug( LDAP_DEBUG_FILTER, " NOT\n", 0, 0, 0 );
+ rc = slapi_vattr_filter_test_ext_internal( pb, e, f->f_not , verify_access, only_check_access, access_check_done);
+ if(!(verify_access && only_check_access)) /* dont play with access control return codes */
+ {
+ if(verify_access && !rc && !(*access_check_done))
+ {
+ /* the filter failed so access control was not checked
+ * for NOT filters this is significant so we must ensure
+ * access control is checked
+ */
+ /* check access control only */
+ rc = slapi_vattr_filter_test_ext_internal( pb, e, f->f_not , verify_access, -1 /*only_check_access*/, access_check_done);
+ /* preserve error code if any */
+ if(!rc)
+ rc = !rc;
+ }
+ else
+ rc = !rc;
+ }
+ break;
+
+ default:
+ LDAPDebug( LDAP_DEBUG_ANY, " unknown filter type 0x%lX\n",
+ f->f_choice, 0, 0 );
+ rc = -1;
+ }
+
+
+ LDAPDebug( LDAP_DEBUG_FILTER, "<= slapi_vattr_filter_test %d\n", rc, 0, 0 );
+ return( rc );
+}
+
+static int test_filter_access( Slapi_PBlock *pb,
+ Slapi_Entry *e,
+ char *attr_type,
+ struct berval *attr_val) {
+ /*
+ * attr_type--attr_type to test for.
+ * attr_val--attr value to test for
+ */
+ int rc;
+ char *attrs[2] = { NULL, NULL };
+ attrs[0] = attr_type;
+
+ rc = plugin_call_acl_plugin( pb, e, attrs, attr_val,
+ SLAPI_ACL_SEARCH, ACLPLUGIN_ACCESS_DEFAULT, NULL );
+
+ return(rc);
+}
+
+static int
+vattr_test_filter_list(
+ Slapi_PBlock *pb,
+ Slapi_Entry *e,
+ struct slapi_filter *flist,
+ int ftype,
+ int verify_access,
+ int only_check_access,
+ int *access_check_done
+)
+{
+ int nomatch;
+ struct slapi_filter *f;
+
+ LDAPDebug( LDAP_DEBUG_FILTER, "=> vattr_test_filter_list\n", 0, 0, 0 );
+
+ nomatch = 1;
+ for ( f = flist; f != NULL; f = f->f_next ) {
+ if ( slapi_vattr_filter_test_ext_internal( pb, e, f, verify_access, only_check_access, access_check_done ) != 0 ) {
+ /* optimize AND evaluation */
+ if ( ftype == LDAP_FILTER_AND ) {
+ /* one false is failure */
+ nomatch = 1;
+ break;
+ }
+ } else {
+ nomatch = 0;
+
+ /* optimize OR evaluation too */
+ if ( ftype == LDAP_FILTER_OR ) {
+ /* only one needs to be true */
+ break;
+ }
+ }
+ }
+
+ LDAPDebug( LDAP_DEBUG_FILTER, "<= test_filter_list %d\n", nomatch, 0, 0 );
+ return( nomatch );
+}
diff --git a/ldap/servers/slapd/generation.c b/ldap/servers/slapd/generation.c
new file mode 100644
index 00000000..24988493
--- /dev/null
+++ b/ldap/servers/slapd/generation.c
@@ -0,0 +1,140 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+
+#include <stdio.h>
+#include <time.h>
+
+#include "rwlock.h"
+#include "slap.h"
+
+/*
+ * Create a new data version string.
+ */
+static const char *
+new_dataversion()
+{
+ struct tm t;
+ char* dataversion;
+ time_t curtime= current_time();
+#ifdef _WIN32
+ memcpy (&t, gmtime (&curtime), sizeof(t));
+#else
+ gmtime_r (&curtime, &t);
+#endif
+ dataversion = slapi_ch_malloc(16);
+ sprintf (dataversion, "0%.4li%.2i%.2i%.2i%.2i%.2i", 1900L + t.tm_year, 1 + t.tm_mon, t.tm_mday, t.tm_hour, t.tm_min, t.tm_sec);
+ return dataversion;
+}
+
+/* ---------------- Database Data Version ---------------- */
+
+/*
+ * Return the generation ID for the database whose mapping tree node is "dn"
+ */
+char *
+get_database_dataversion(const char *dn)
+{
+ char *dataversion= NULL;
+ int r;
+ Slapi_PBlock *resultpb= NULL;
+ Slapi_Entry** entry = NULL;
+ resultpb= slapi_search_internal( dn, LDAP_SCOPE_BASE, "objectclass=*", NULL, NULL, 1);
+ slapi_pblock_get( resultpb, SLAPI_PLUGIN_INTOP_SEARCH_ENTRIES, &entry );
+ slapi_pblock_get( resultpb, SLAPI_PLUGIN_INTOP_RESULT, &r );
+ if(r==LDAP_SUCCESS && entry!=NULL && entry[0]!=NULL)
+ {
+ dataversion= slapi_entry_attr_get_charptr( entry[0], "nsslapd-dataversion"); /* JCMREPL - Shouldn't be a Netscape specific attribute name */
+ }
+ slapi_free_search_results_internal(resultpb);
+ slapi_pblock_destroy(resultpb);
+ return dataversion;
+}
+
+void
+set_database_dataversion(const char *dn, const char *dataversion)
+{
+ LDAPMod gen_mod;
+ LDAPMod *mods[2];
+ struct berval* gen_vals[2];
+ struct berval gen_val;
+ Slapi_PBlock *pb;
+
+ memset (&gen_mod, 0, sizeof(gen_mod));
+
+ gen_mod.mod_op = LDAP_MOD_REPLACE | LDAP_MOD_BVALUES;
+ gen_mod.mod_type = "nsslapd-dataversion"; /* JCMREPL - Shouldn't be a Netscape specific attribute name */
+ gen_mod.mod_bvalues = gen_vals;
+ gen_vals[0] = &gen_val;
+ gen_vals[1] = NULL;
+ gen_val.bv_val = (char *)dataversion;
+ gen_val.bv_len = strlen (gen_val.bv_val);
+ mods[0] = &gen_mod;
+ mods[1] = NULL;
+
+ pb = slapi_modify_internal (dn, mods, 0, 0 /* !log_change */);
+ if (NULL != pb)
+ {
+ Slapi_Entry *e;
+ slapi_pblock_get (pb, SLAPI_ENTRY_PRE_OP, &e);
+ slapi_entry_free(e);
+ }
+ slapi_pblock_destroy(pb);
+}
+
+/* ---------------- Server Data Version ---------------- */
+
+static char *server_dataversion_id= NULL;
+
+const char *
+get_server_dataversion()
+{
+ lenstr *l = NULL;
+ Slapi_Backend *be;
+ char *cookie;
+
+ /* we already cached the copy - just return it */
+ if(server_dataversion_id!=NULL)
+ {
+ return server_dataversion_id;
+ }
+
+ l= lenstr_new();
+
+ /* Loop over the backends collecting the backend data versions */
+ /* Combine them into a single blob */
+ be = slapi_get_first_backend(&cookie);
+ while ( be )
+ {
+ /* Don't generate dataversion for remote entries */
+ if((!be->be_private) && !(slapi_be_is_flag_set(be,SLAPI_BE_FLAG_REMOTE_DATA)))
+ {
+ const char * dataversion;
+ Slapi_DN be_configdn;
+ slapi_sdn_init(&be_configdn);
+ (void)be_getconfigdn(be, &be_configdn);
+ dataversion= get_database_dataversion(slapi_sdn_get_ndn(&be_configdn));
+ if(dataversion==NULL)
+ {
+ /* The database either doesn't support the storage of a dataverion, */
+ /* or has just been created, or has been reinitialised */
+ dataversion= new_dataversion();
+ set_database_dataversion(slapi_sdn_get_ndn(&be_configdn), dataversion);
+ }
+ addlenstr(l, dataversion);
+ slapi_ch_free((void**)&dataversion);
+ slapi_sdn_done(&be_configdn);
+ }
+
+ be = slapi_get_next_backend(cookie);
+ }
+ slapi_ch_free ((void **)&cookie);
+ if(l->ls_buf!=NULL)
+ {
+ server_dataversion_id= slapi_ch_strdup(l->ls_buf);
+ }
+ lenstr_free(&l);
+ return server_dataversion_id;
+}
diff --git a/ldap/servers/slapd/getfilelist.c b/ldap/servers/slapd/getfilelist.c
new file mode 100644
index 00000000..f24ed85a
--- /dev/null
+++ b/ldap/servers/slapd/getfilelist.c
@@ -0,0 +1,233 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+/***********************************************************************
+** NAME
+** getfilelist.c
+**
+** DESCRIPTION
+**
+**
+** AUTHOR
+** Rich Megginson <richm@netscape.com>
+**
+***********************************************************************/
+
+/***********************************************************************
+** Includes
+***********************************************************************/
+
+#include "prio.h"
+#include "slap.h"
+#include "avl.h"
+#if defined( MACOS ) || defined( DOS ) || defined( _WIN32 ) || defined( NEED_BSDREGEX )
+#include "regex.h"
+#endif
+
+struct data_wrapper {
+ char **list;
+ int n;
+ int max;
+ const char *dirname;
+};
+
+static int
+add_file_to_list(caddr_t data, caddr_t arg)
+{
+ struct data_wrapper *dw = (struct data_wrapper *)arg;
+ if (dw) {
+ size_t size;
+ /* max is number of entries; the range of n is 0 - max-1 */
+ PR_ASSERT(dw->n <= dw->max);
+ PR_ASSERT(dw->list);
+ PR_ASSERT(data);
+ /* this strdup is free'd by free_filelist */
+ size = strlen(dw->dirname) + strlen(data) + 5;
+ dw->list[dw->n] = slapi_ch_calloc(size, 1);
+ sprintf(dw->list[dw->n++], "%s/%s", dw->dirname, data);
+ return 0;
+ }
+
+ return -1;
+}
+
+static void
+free_string(caddr_t data)
+{
+ slapi_ch_free((void **)&data);
+}
+
+static int
+file_is_type_x(const char *dirname, const char *filename, PRFileType x)
+{
+ struct PRFileInfo inf;
+ int status = 0;
+ size_t size = strlen(dirname) + strlen(filename) + 2; /* 1 for slash + 1 for null */
+ char *fullpath = slapi_ch_calloc(sizeof(char), size);
+
+ sprintf(fullpath, "%s/%s", dirname, filename);
+ if (PR_SUCCESS == PR_GetFileInfo(fullpath, &inf) &&
+ inf.type == x)
+ status = 1;
+
+ slapi_ch_free((void **)&fullpath);
+
+ return status;
+}
+
+/* return true if the given path and file corresponds to a directory */
+static int
+is_a_dir(const char *dirname, const char *filename)
+{
+ return file_is_type_x(dirname, filename, PR_FILE_DIRECTORY);
+}
+
+/* return true if the given path and file corresponds to a regular file */
+static int
+is_a_file(const char *dirname, const char *filename)
+{
+ return file_is_type_x(dirname, filename, PR_FILE_FILE);
+}
+
+static int
+matches(const char *filename, const char *pattern)
+{
+ int match = 0;
+ char *s = 0;
+ if (!pattern)
+ return 1; /* null pattern matches everything */
+
+ slapd_re_lock();
+ s = slapd_re_comp((char *)pattern);
+ if (!s)
+ match = slapd_re_exec((char *)filename);
+ slapd_re_unlock();
+
+ return match;
+}
+
+/**
+ * getfilelist will return a list of all files and directories in the
+ * given directory matching the given pattern. If dirname is NULL, the
+ * current directory "." will be used. If the pattern is NULL, all files
+ * and directories will be returned. The additional integer arguments
+ * control which files and directories are selected. The default value
+ * for all of them is 0, which will not return hidden files (e.g. files
+ * beginning with . on unix), but will return both files and directories
+ * If nofiles is non-zero, only directory names will be returned. If
+ * nodirs is non-zero, only filenames will be returned.
+ * The pattern is a grep style regular expression, not a shell or command
+ * interpreter style regular expression. For example, to get all files ending
+ * in .ldif, use ".*\\.ldif" instead of "*.ldif"
+ * The return value is a NULL terminated array of names.
+ */
+char **
+get_filelist(
+ const char *dirname, /* directory path; if NULL, uses "." */
+ const char *pattern, /* grep (not shell!) file pattern regex */
+ int hiddenfiles, /* if true, return hidden files and directories too */
+ int nofiles, /* if true, do not return files */
+ int nodirs /* if true, do not return directories */
+)
+{
+ Avlnode *filetree = 0;
+ PRDir *dirptr = 0;
+ PRDirEntry *dirent = 0;
+ PRDirFlags dirflags = PR_SKIP_BOTH & PR_SKIP_HIDDEN;
+ char **retval = 0;
+ int num = 0;
+ struct data_wrapper dw;
+
+ if (!dirname)
+ dirname = ".";
+
+ if (hiddenfiles)
+ dirflags = PR_SKIP_BOTH;
+
+ if (!(dirptr = PR_OpenDir(dirname))) {
+ return NULL;
+ }
+
+ /* read the directory entries into an ascii sorted avl tree */
+ for (dirent = PR_ReadDir(dirptr, dirflags); dirent ;
+ dirent = PR_ReadDir(dirptr, dirflags)) {
+
+ if (nofiles && is_a_file(dirname, dirent->name))
+ continue;
+
+ if (nodirs && is_a_dir(dirname, dirent->name))
+ continue;
+
+ if (matches(dirent->name, pattern)) {
+ /* this strdup is free'd by free_string */
+ char *newone = slapi_ch_strdup(dirent->name);
+ avl_insert(&filetree, newone, strcmp, 0);
+ num++;
+ }
+ }
+ PR_CloseDir(dirptr);
+
+ /* allocate space for the list */
+ retval = (char **)slapi_ch_calloc(num+1, sizeof(char *));
+
+ /* traverse the avl tree and copy the filenames into the list */
+ dw.list = retval;
+ dw.n = 0;
+ dw.max = num;
+ dw.dirname = dirname;
+ (void)avl_apply(filetree, add_file_to_list, &dw, -1, AVL_INORDER);
+ retval[num] = 0; /* set last entry to null */
+
+ /* delete the avl tree and all its data */
+ avl_free(filetree, free_string);
+
+ return retval;
+}
+
+
+void
+free_filelist(char **filelist)
+{
+ int ii;
+ for (ii = 0; filelist && filelist[ii]; ++ii)
+ slapi_ch_free((void **)&filelist[ii]);
+
+ slapi_ch_free((void **)&filelist);
+}
+
+/**
+ * Returns a list of files in order of "priority" where priority is defined
+ * as:
+ * The filename must begin with the letter S. The next two characters in
+ * the filename are digits representing a number from 00 to 99. The lower the
+ * number the higher the priority. For example, S00 is in the list before S01,
+ * and S99 is the last item in the list. The ordering of files with the same
+ * priority cannot be guaranteed. The pattern is the grep style regular expression
+ * of filenames to match which is applied to the end of the string.
+ * If you are a Solaris person, you may recognize this as the rules for init level
+ * initialization using shell scripts under /etc/rcX.d/
+ */
+char **
+get_priority_filelist(const char *directory, const char *pattern)
+{
+ char *basepattern = "^[0-9][0-9]";
+ char *genericpattern = ".*"; /* used if pattern is null */
+ char *bigpattern = 0;
+ size_t len = 0;
+ char **retval = 0;
+
+ if (!pattern)
+ pattern = genericpattern;
+
+ len = strlen(basepattern) + strlen(pattern) + 1;
+ bigpattern = slapi_ch_calloc(sizeof(char), len);
+ sprintf(bigpattern, "%s%s", basepattern, pattern);
+
+ retval = get_filelist(directory, bigpattern, 0, 0, 1);
+
+ slapi_ch_free((void **)&bigpattern);
+
+ return retval;
+}
diff --git a/ldap/servers/slapd/getopt_ext.c b/ldap/servers/slapd/getopt_ext.c
new file mode 100644
index 00000000..97d2e6d3
--- /dev/null
+++ b/ldap/servers/slapd/getopt_ext.c
@@ -0,0 +1,236 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+
+#include "getopt_ext.h"
+
+char *optarg_ext;
+int optind_ext=1;
+int optopt_ext;
+int opterr_ext;
+int optind_last;
+
+static int _getopt_ext_inited = 0;
+static int _optind_firstHandled = 0;
+static int _optind_firstRecognized = 0;
+static int _getopt_ext_done_long = 0;
+
+static
+int _getopt_ext_init(int argc)
+{
+ _getopt_ext_inited = 1;
+ /* optind_ext = 1;*/
+ optind_last = argc;
+ _optind_firstHandled = argc;
+ _optind_firstRecognized = argc;
+ /* optind = 1;*/
+ _getopt_ext_done_long = 0;
+ return(0);
+}
+
+#if 0
+static
+int _getopt_ext_done()
+{
+ _getopt_ext_done_long = 1;
+ return(0);
+}
+#endif
+
+static
+int _getopt_ext_find(int argc,
+ char **argv,
+ const struct opt_ext *longOpts)
+{
+ int i=0;
+ struct opt_ext *lopt;
+
+ i=0;
+ lopt = (struct opt_ext *)longOpts;
+ while(lopt->o_string) {
+ if(0 != strcmp(argv[optind_ext]+2,lopt->o_string)) {
+ i++;
+ lopt++;
+ continue;
+ }
+ /*
+ * found it
+ */
+ return(i);
+ }
+ /* should never come here */
+ return(-1);
+}
+
+static
+int _getopt_ext_tailit(int argc,
+ char **argv,
+ int hasArg,
+ int recognized)
+{
+ char *_optPtr=NULL;
+ char *_optArgPtr=NULL;
+ int _endIndex=optind_last;
+ int _nextDest=optind_ext;
+ int _nextSource=optind_ext;
+
+ _optPtr = argv[optind_ext];
+ if(hasArg) {
+ _nextSource = optind_ext + 2;
+ _endIndex = ((recognized)?_optind_firstRecognized:_optind_firstHandled);
+ _optArgPtr = argv[optind_ext + 1];
+ } else {
+ _nextSource = optind_ext + 1;
+ _endIndex = ((recognized)?_optind_firstRecognized:_optind_firstHandled);
+ _optArgPtr = NULL;
+ }
+
+ while(_nextSource < _endIndex) {
+ argv[_nextDest] = argv[_nextSource];
+ _nextSource++;
+ _nextDest++;
+ }
+
+ argv[_nextDest] = _optPtr;
+ /* argv[_nextDest] = NULL; */
+ if(hasArg) {
+ argv[_nextDest + 1] = _optArgPtr;
+ if(recognized) {
+ _optind_firstRecognized -=2;
+ }
+ _optind_firstHandled -=2;
+ } else {
+ if(recognized) {
+ _optind_firstRecognized -=1;
+ }
+ _optind_firstHandled -=1;
+ }
+ optind_last = _optind_firstRecognized;
+ return(0);
+}
+
+/*
+ * First process all the long options (using exact string matches)
+ * Then, follow up with regular option processing using good ol' getopt
+ *
+ * return the same return codes as getopt
+ *
+ */
+
+int getopt_ext(int argc,
+ char **argv,
+ const char *optstring,
+ const struct opt_ext *longOpts,
+ int *longOptIndex)
+{
+ int retVal;
+
+ if(!_getopt_ext_inited) {
+ _getopt_ext_init(argc);
+ }
+
+ if(_optind_firstHandled < optind_ext) {
+ /* we are not processing extended options anymore...
+ let getopt handle it.
+ */
+ goto _doneWithExtendedOptions;
+ }
+
+ /*
+ * if we are here, we are not done with extended options...
+ *
+ */
+ while(_optind_firstHandled > optind_ext) {
+ if(0 != strncmp(argv[optind_ext], "--", 2)) {
+ optind_ext++;
+ continue;
+ }
+ /*
+ * possibly an extended option
+ */
+ retVal = _getopt_ext_find(argc,argv,longOpts);
+ if(-1 == retVal) {
+ /*
+ * unrecognized long option...
+ * we will let getopt handle it later...
+ *
+ */
+ optind_ext++;
+ continue;
+ }
+ /*
+ * we found an extended option
+ * now find its arg...
+ */
+ switch((longOpts+retVal)->o_type) {
+ case ArgNone:
+ default:
+ {
+ /* send this option to the end of the arglist */
+ _getopt_ext_tailit(argc,argv,0,1);
+ optarg_ext = NULL;
+ *longOptIndex = retVal;
+ return((longOpts+retVal)->o_return);
+ }
+ case ArgRequired:
+ {
+ /*
+ * make sure the next arg is a "valid" argument
+ */
+ if(((optind_ext + 1) == _optind_firstHandled)) {
+ /*
+ * did not find the argument
+ * let getopt handle it
+ * we will just push it to the end and continue
+ * looking for more extended options
+ *
+ * _getopt_ext_tailit(argc,argv,0,0);
+ */
+ optind_ext++;
+ fprintf(stderr, "%s: option requires an argument -- %s\n",
+ argv[0],(longOpts+retVal)->o_string);
+ exit(0);
+ continue;
+ }
+ /* like getopt, we don't care what the arg looks like! */
+ /* send this option to the end of the arglist */
+ _getopt_ext_tailit(argc,argv,1,1);
+ optarg_ext = argv[_optind_firstRecognized + 1];
+ *longOptIndex = retVal;
+ return((longOpts+retVal)->o_return);
+ }
+ case ArgOptional:
+ {
+ if(((optind_ext + 1) == optind_last) ||
+ ('-' == argv[optind_ext + 1][0])){
+ /*
+ * did not find the argument
+ *
+ */
+ _getopt_ext_tailit(argc,argv,0,1);
+ optarg_ext = NULL;
+ *longOptIndex = retVal;
+ return((longOpts+retVal)->o_return);
+ }
+ /* send this option to the end of the arglist */
+ _getopt_ext_tailit(argc,argv,1,1);
+ optarg_ext = argv[optind_last + 1];
+ *longOptIndex = retVal;
+ return((longOpts+retVal)->o_return);
+ }
+ }
+
+ /* optind_ext++;*/
+ }
+
+_doneWithExtendedOptions:
+ retVal = getopt(optind_last,argv,optstring);
+ optarg_ext = optarg;
+ /* optopt_ext = optopt; */
+ optind_last = _optind_firstHandled;
+ return(retVal);
+
+}
+
diff --git a/ldap/servers/slapd/getopt_ext.h b/ldap/servers/slapd/getopt_ext.h
new file mode 100644
index 00000000..aab66728
--- /dev/null
+++ b/ldap/servers/slapd/getopt_ext.h
@@ -0,0 +1,111 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+/*
+ * getopt_ext.h - long option names
+ *
+ *
+ *
+ */
+
+#ifndef _GETOPT_EXT_H
+#define _GETOPT_EXT_H
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+
+#if defined( _WIN32 )
+#include <io.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <stdarg.h>
+#include <ldap.h>
+#include "ntslapdmessages.h"
+#include "proto-ntutil.h"
+#endif
+
+#ifdef LINUX
+#include <getopt.h>
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
+/*
+ * getopt_ext() is a rudimentary extension to getopt() to facilitate
+ * handling of long (wordier) option names.
+ *
+ * A long option is indicated by placing a "--" in front of the option
+ * name.
+ *
+ * Like getopt(), getopt_ext() also returns a single letter (actually an int)
+ * when an option is recognized. Therefore, the loop for processing long
+ * options and single letter options can be combined (see example in
+ * slapd/main.c)
+ *
+ * getopt_ext() first processes all the long options it can find. Currently,
+ * it does a "strcmp" to check for the validity of the option name (i.e.,
+ * the option name has to match exactly).
+ *
+ * Once all the long options are handled, getopt_ext() uses getopt() to
+ * process the remaining options.
+ *
+ * getopt_ext() rearranges "argv" when it finds long options that it
+ * recognizes. The recognized options (and their parameters) are pushed
+ * to the end.
+ *
+ * Single letter options are specified similar to getopt()
+ * Long options are specified using a list of "struct opt_ext". Each long
+ * option consists of string that identifies the option, a type that specifies
+ * if the option requires an argument and the single letter returned by
+ * getopt_ext() when the option is encountered. For example,
+ * {"verbose",ArgNone,'v'} specifies a long option (--verbose) that requires
+ * no arguments and for which, getopt_ext() returns a 'v' as the return value.
+ * {"instancedir",ArgRequired,'D'} specifies a long option (--instancedir dir)
+ * that requires an argument.
+ *
+ *
+ */
+
+
+extern char *optarg_ext;
+extern int optind_ext;
+extern int optopt_ext;
+extern int opterr_ext;
+extern int optind_last;
+
+extern int optind, opterr, optopt;
+extern char *optarg;
+
+typedef enum {
+ ArgNone,
+ ArgRequired,
+ ArgOptional
+} GetOptExtArgType;
+
+struct opt_ext {
+ const char *o_string;
+ const GetOptExtArgType o_type;
+ const char o_return;
+};
+
+int getopt_ext(int argc,
+ char **argv,
+ const char *optstring,
+ const struct opt_ext *longOpts,
+ int *longOptIndex);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
+
+
+
diff --git a/ldap/servers/slapd/globals.c b/ldap/servers/slapd/globals.c
new file mode 100644
index 00000000..08aae06d
--- /dev/null
+++ b/ldap/servers/slapd/globals.c
@@ -0,0 +1,142 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+/*
+ * Copyright (c) 1996 Regents of the University of Michigan.
+ * All rights reserved.
+ *
+ * SLAPD globals.c -- SLAPD library global variables
+ */
+
+#if defined(NET_SSL)
+#include "ldap.h"
+#include <sslproto.h> /* cipher suite names */
+#include <ldap_ssl.h>
+
+#undef OFF
+#undef LITTLE_ENDIAN
+
+#endif
+
+#include <stdio.h>
+#include <string.h>
+#include <sys/types.h>
+#include <time.h>
+#include <stdarg.h>
+#include <stdlib.h>
+#if defined( _WIN32 )
+#include "ntslapdmessages.h"
+#include "proto-ntutil.h"
+#else
+#include <sys/time.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+#endif
+#include "slap.h"
+#include "fe.h"
+
+/* On UNIX, there's only one copy of slapd_ldap_debug */
+/* On NT, each module keeps its own module_ldap_debug, which */
+/* points to the process' slapd_ldap_debug */
+#ifdef _WIN32
+int *module_ldap_debug;
+#endif
+
+int should_detach = 1;
+time_t starttime;
+PRThread *listener_tid;
+Slapi_PBlock *repl_pb = NULL;
+
+/*
+ * global variables that need mutex protection
+ */
+int active_threads;
+PRInt32 ops_initiated;
+PRInt32 ops_completed;
+PRLock *ops_mutex;
+int num_conns;
+PRLock *num_conns_mutex;
+
+
+/*
+ DEC/COMPAQ has released a patch for 4.0d (e?) which will speed up
+ malloc/free considerably in multithreaded multiprocessor
+ applications (like directory server!), sort of like SmartHeap but
+ not as snazzy. The last three parameters only take effect if the
+ patch is installed, otherwise they are ignored. The rest of the
+ parameters are included along with their default values, but they
+ are commented out except:
+ unsigned long __noshrink = 1; old - default is 0; apparently this is ignored for now
+ int __fast_free_max = INT_MAX; old - default is 13; may cause excessive memory consumption
+*/
+#if defined(OSF1) && defined(LDAP_DONT_USE_SMARTHEAP)
+/* From an email from Dave Long at DEC/Compaq:
+
+ The following is an example of how to tune for maximum speed on a
+ system with three or more CPUs and with no concern for memory used:
+
+ #include <limits.h>
+ #include <sys/types.h>
+*/
+unsigned long __noshrink = 1; /* old - default is 0; apparently this is ignored for now */
+/*
+ size_t __minshrink = 65536;
+ double __minshrinkfactor = 0.001;
+ size_t __mingrow = 65536;
+ double __mingrowfactor = 0.1;
+ unsigned long __madvisor = 0;
+ unsigned long __small_buff = 0;
+*/
+int __fast_free_max = INT_MAX; /* old - default is 13; may cause excessive memory consumption */
+/*
+ unsigned long __sbrk_override = 0;
+ unsigned long __taso_mode = 0;
+*/
+
+/*
+ These are the new parameters
+*/
+int __max_cache = 27;
+int __first_fit = 2;
+int __delayed_free = 1;
+/*
+ Note that the allowed values for the new __max_cache tuning variable
+ are: 15, 18, 21, 24, 27. Any other value is likely to actually harm
+ performance or even cause a core dump.
+*/
+#endif
+
+
+#if defined( _WIN32 )
+/* String constants (no change for international) */
+SERVICE_STATUS LDAPServerStatus;
+SERVICE_STATUS_HANDLE hLDAPServerServiceStatus;
+#endif
+
+Connection_Table *the_connection_table = NULL;
+
+char *pid_file = "/dev/null";
+char *start_pid_file = "/dev/null";
+
+char *attr_dataversion = ATTR_DATAVERSION;
+
+extern void set_dll_entry_points( slapdEntryPoints *sep );
+void
+set_entry_points()
+{
+ slapdEntryPoints *sep;
+
+ sep = (slapdEntryPoints *) slapi_ch_malloc( sizeof( slapdEntryPoints ));
+ sep->sep_ps_wakeup_all = (caddr_t)ps_wakeup_all;
+ sep->sep_ps_service = (caddr_t)ps_service_persistent_searches;
+ sep->sep_disconnect_server = (caddr_t)disconnect_server;
+ sep->sep_slapd_SSL_client_init = (caddr_t)slapd_SSL_client_init;
+ sep->sep_slapd_ssl_init = (caddr_t)slapd_ssl_init;
+ sep->sep_slapd_ssl_init2 = (caddr_t)slapd_ssl_init2;
+ set_dll_entry_points( sep );
+
+}
diff --git a/ldap/servers/slapd/house.c b/ldap/servers/slapd/house.c
new file mode 100644
index 00000000..25cdb435
--- /dev/null
+++ b/ldap/servers/slapd/house.c
@@ -0,0 +1,91 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#include "slap.h"
+
+#define SLAPD_HOUSEKEEPING_INTERVAL 30 /* seconds */
+
+static PRThread *housekeeping_tid = NULL;
+static PRLock *housekeeping_mutex = NULL;
+static PRCondVar *housekeeping_cvar = NULL;
+
+
+static void
+housecleaning(void *cur_time)
+{
+ int interval;
+
+ interval = PR_SecondsToInterval( SLAPD_HOUSEKEEPING_INTERVAL );
+ while ( !g_get_shutdown() ) {
+ /*
+ * Looks simple, but could potentially take a long time.
+ */
+ be_flushall();
+
+ log_access_flush();
+
+ if ( g_get_shutdown() ) {
+ break;
+ }
+ PR_Lock( housekeeping_mutex );
+ PR_WaitCondVar( housekeeping_cvar, interval );
+ PR_Unlock( housekeeping_mutex );
+ }
+}
+
+PRThread*
+housekeeping_start(time_t cur_time, void *arg)
+{
+ static time_t thread_start_time;
+
+ if ( housekeeping_tid ) {
+ return housekeeping_tid;
+ }
+
+ if ( ( housekeeping_mutex = PR_NewLock()) == NULL ) {
+ slapi_log_error(SLAPI_LOG_FATAL, NULL,
+ "housekeeping cannot create new lock. "
+ SLAPI_COMPONENT_NAME_NSPR " error %d (%s)\n",
+ PR_GetError(), slapd_pr_strerror( PR_GetError() ));
+ }
+ else if ( ( housekeeping_cvar = PR_NewCondVar( housekeeping_mutex )) == NULL ) {
+ slapi_log_error(SLAPI_LOG_FATAL, NULL,
+ "housekeeping cannot create new condition variable. "
+ SLAPI_COMPONENT_NAME_NSPR " error %d (%s)\n",
+ PR_GetError(), slapd_pr_strerror( PR_GetError() ));
+ }
+ else {
+ thread_start_time = cur_time;
+ if ((housekeeping_tid = PR_CreateThread(PR_USER_THREAD,
+ (VFP) housecleaning, (void*)&thread_start_time,
+ PR_PRIORITY_NORMAL, PR_GLOBAL_THREAD, PR_JOINABLE_THREAD,
+ SLAPD_DEFAULT_THREAD_STACKSIZE)) == NULL) {
+ slapi_log_error(SLAPI_LOG_FATAL, NULL,
+ "housekeeping PR_CreateThread failed. "
+ SLAPI_COMPONENT_NAME_NSPR " error %d (%s)\n",
+ PR_GetError(), slapd_pr_strerror( PR_GetError() ));
+ }
+ }
+
+ return housekeeping_tid;
+}
+
+void
+housekeeping_stop()
+{
+ if ( housekeeping_tid ) {
+ PR_Lock( housekeeping_mutex );
+ PR_NotifyCondVar( housekeeping_cvar );
+ PR_Unlock( housekeeping_mutex );
+ (void)PR_JoinThread( housekeeping_tid );
+ }
+}
diff --git a/ldap/servers/slapd/http.h b/ldap/servers/slapd/http.h
new file mode 100644
index 00000000..7c1654a5
--- /dev/null
+++ b/ldap/servers/slapd/http.h
@@ -0,0 +1,43 @@
+/**
+ * PROPRIETARY/CONFIDENTIAL. Use of this product is subject to
+ * license terms. Copyright 2001 Sun Microsystems, Inc.
+ * Some preexisting portions Copyright 2001 Netscape Communications Corp.
+ * All rights reserved.
+ */
+
+#ifndef _HTTP_H_
+#define _HTTP_H_
+
+/* mechanics */
+
+
+typedef void (*api_http_init)();
+typedef int (*api_get_http_text)(char *url, char *text_data);
+typedef int (*api_get_http_binary)(char *url, char* bin_data, int *len);
+typedef void (*api_http_shutdown)();
+
+/* API ID for http_apib_get_interface */
+
+#define HTTP_v1_0_GUID "0A340151-6FB3-11d3-80D2-006008A6EFF3"
+
+/* API */
+
+/* the api broker reserves api[0] for its use */
+
+#define http_init() \
+ ((api_http_init*)(api))[1]()
+
+#define get_http_text(url, text_data) \
+ ((api_get_http_text*)(api))[2]( url, text_data)
+
+#define get_http_binary(url, bin_data, len) \
+ ((api_get_http_binary*)(api))[3](url,bin_data, len)
+
+#define set_http_shutdown() \
+ ((api_http_shutdown*)(api))[4]()
+
+/* HTTP to be passed to http_register() by presence sps*/
+#define http_api(api) api[5]
+
+
+#endif /*_HTTP_H_*/
diff --git a/ldap/servers/slapd/index_subsys.h b/ldap/servers/slapd/index_subsys.h
new file mode 100644
index 00000000..190659c5
--- /dev/null
+++ b/ldap/servers/slapd/index_subsys.h
@@ -0,0 +1,47 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2002 Netscape Communications Corporation. All rights reserved.
+ * END COPYRIGHT BLOCK **/
+
+#ifndef _INDEX_SUBSYS_H_
+#define _INDEX_SUBSYS_H_
+
+#include "slapi-plugin.h"
+
+typedef void IndexEntryList;
+typedef unsigned int IndexEntryID;
+
+typedef int (*index_search_callback)(Slapi_Filter *filter, IndexEntryList **results, void *user_data );
+typedef int (*index_validate_callback)();
+
+typedef struct __indexed_item
+{
+ /* item that is indexed, an LDAP string filter description of the index
+ * x=* = presence
+ * x=** = equality
+ * x=?* = substrings
+ */
+ char *index_filter; /* item that is indexed, an LDAP string filter description of the index e.g. (presence=*) */
+ index_search_callback search_op; /* search call back */
+ char **associated_attrs; /* null terminated list of filter groupable attributes */
+ Slapi_DN *namespace_dn; /* the namespace this index is valid for */
+} indexed_item;
+
+
+#define INDEX_FILTER_EVALUTED 0
+#define INDEX_FILTER_UNEVALUATED 1
+
+
+/* prototypes */
+
+/* for index plugins */
+int slapi_index_entry_list_create(IndexEntryList **list);
+int slapi_index_entry_list_add(IndexEntryList **list, IndexEntryID id);
+int slapi_index_register_decoder(char *plugin_id, index_validate_callback validate_op);
+int slapi_index_register_index(char *plugin_id, indexed_item *registration_item, void *user_data);
+
+/* for backends */
+int index_subsys_assign_filter_decoders(Slapi_PBlock *pb);
+int index_subsys_filter_decoders_done(Slapi_PBlock *pb);
+int index_subsys_evaluate_filter(Slapi_Filter *f, Slapi_DN *namespace_dn, IndexEntryList **out);
+
+#endif /*_INDEX_SUBSYS_H_*/
diff --git a/ldap/servers/slapd/index_subsystem.c b/ldap/servers/slapd/index_subsystem.c
new file mode 100644
index 00000000..e696860e
--- /dev/null
+++ b/ldap/servers/slapd/index_subsystem.c
@@ -0,0 +1,1286 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2002 Netscape Communications Corporation. All rights reserved.
+ * END COPYRIGHT BLOCK **/
+
+/* The indexing subsystem
+ * ----------------------
+ *
+ * This provides support for indexing plugins and assigning
+ * those plugins to sub-filters of a search filter. Currently
+ * the old indexing code still exists and operates on those
+ * indexes which do not have a plugin assigned. This indexing
+ * abstraction is intended to eventually decouple the index mechanics
+ * from the back-end where that is possible. Hopefully, while
+ * supporting the needs of virtual attribute indexes, it will allow
+ * easier integration of other back ends.
+ *
+ */
+
+/* includes */
+#include "slap.h"
+#include "./back-ldbm/back-ldbm.h"
+#include "./back-ldbm/idlapi.h"
+#include "index_subsys.h"
+
+#define INDEX_IDLIST_INITIAL_SIZE 128 /* make this a good size to avoid constant reallocs */
+
+/* data */
+static void **idl_api;
+
+struct _indexLinkedList
+{
+ void *pNext;
+ void *pPrev;
+};
+typedef struct _indexLinkedList indexLinkedList;
+
+struct _indexEntry
+{
+ indexLinkedList list;
+ char *indexedAttribute;
+ Slapi_Filter *indexfilter;
+ char *indexfilterstr;
+ char **associated_attrs;
+ void *user_data;
+ Slapi_DN *namespace_dn;
+ index_search_callback lookup_func; /* search call back */
+};
+typedef struct _indexEntry indexEntry;
+
+struct _indexPlugin
+{
+ indexLinkedList list;
+ char *id;
+ indexEntry *indexes;
+ index_validate_callback validate_op;
+};
+typedef struct _indexPlugin indexPlugin;
+
+struct _globalIndexCache
+{
+ indexPlugin *pPlugins;
+ indexEntry **ppIndexIndex; /* sorted list with key: indexEntry.indexedAttribute */
+ int index_count;
+ PRRWLock *cache_lock;
+};
+typedef struct _globalIndexCache globalIndexCache;
+
+static globalIndexCache *theCache = 0;
+
+/* prototypes */
+static int index_subsys_decoder_done(Slapi_Filter *f);
+static int index_subsys_assign_decoders(Slapi_Filter *f);
+static int index_subsys_assign_decoder(Slapi_Filter *f);
+static int index_subsys_group_decoders(Slapi_Filter *f);
+static indexEntry *index_subsys_find_decoder(Slapi_Filter *f);
+static int index_subsys_unlink_subfilter(Slapi_Filter *fcomplex, Slapi_Filter *fsub);
+static int index_subsys_index_matches_associated(indexEntry *index, Slapi_Filter *f);
+
+/* matching alg - note : values 0/1/2/3 supported right now*/
+#define INDEX_MATCH_NONE 0
+#define INDEX_MATCH_EQUALITY 1
+#define INDEX_MATCH_PRESENCE 2
+#define INDEX_MATCH_SUBSTRING 3
+#define INDEX_MATCH_COMPLEX 4
+static int index_subsys_index_matches_filter(indexEntry *index, Slapi_Filter *f);
+
+static void index_subsys_read_lock()
+{
+ PR_RWLock_Rlock(theCache->cache_lock);
+}
+
+static void index_subsys_write_lock()
+{
+ PR_RWLock_Wlock(theCache->cache_lock);
+}
+
+static void index_subsys_unlock()
+{
+ PR_RWLock_Unlock(theCache->cache_lock);
+}
+
+int slapi_index_entry_list_create(IndexEntryList **list)
+{
+ if(idl_api)
+ *list = (IndexEntryList*)IDList_alloc(idl_api, INDEX_IDLIST_INITIAL_SIZE);
+ else
+ *list = 0;
+
+ return !(*list);
+}
+
+int slapi_index_entry_list_add(IndexEntryList **list, IndexEntryID id)
+{
+ if(idl_api)
+ IDList_insert(idl_api, (IDList **)list, (ID)id);
+
+ return 0; /* no way to tell failure */
+}
+
+
+static int index_subsys_index_matches_filter(indexEntry *index, Slapi_Filter *f)
+{
+ int ret = INDEX_MATCH_NONE;
+
+ /* simple filters only right now */
+ if(slapi_attr_types_equivalent(index->indexedAttribute, f->f_type))
+ {
+ /* ok we have some type of match, lets find out which */
+
+ switch(index->indexfilter->f_choice)
+ {
+ case LDAP_FILTER_PRESENT:
+ /* present means "x=*" */
+ if(f->f_choice == LDAP_FILTER_PRESENT)
+ ret = INDEX_MATCH_PRESENCE;
+ break;
+ case LDAP_FILTER_SUBSTRINGS:
+ /* our equality filters look like this "x=**"
+ * that means the filter will be assigned
+ * a substring f_choice by the filter code
+ * in str2filter.c
+ * but we need to differentiate so we take
+ * advantage of the fact that this creates a
+ * special substring filter with no substring
+ * to look for...
+ */
+ if( index->indexfilter->f_sub_initial == 0 &&
+ index->indexfilter->f_sub_any == 0 &&
+ index->indexfilter->f_sub_final == 0
+ )
+ {
+ /* this is an index equality filter */
+ if(f->f_choice == LDAP_FILTER_EQUALITY)
+ ret = INDEX_MATCH_EQUALITY;
+ }
+ else
+ {
+ /* this is a regualar substring filter */
+ if(f->f_choice == LDAP_FILTER_SUBSTRINGS)
+ ret = INDEX_MATCH_SUBSTRING;
+ }
+
+ break;
+
+ default:
+ /* we don't know about any other type yet */
+ break;
+ }
+ }
+
+ return ret;
+}
+
+/* index_subsys_assign_filter_decoders
+ * -----------------------------------
+ * assigns index plugins to sub-filters
+ */
+int index_subsys_assign_filter_decoders(Slapi_PBlock *pb)
+{
+ int rc;
+ Slapi_Filter *f;
+ char *subsystem = "index_subsys_assign_filter_decoders";
+ char logbuf[ 1024 ];
+
+ /* extract the filter */
+ slapi_pblock_get(pb, SLAPI_SEARCH_FILTER, &f);
+
+ if ( LDAPDebugLevelIsSet( LDAP_DEBUG_FILTER ) && NULL != f ) {
+ logbuf[0] = '\0';
+ slapi_log_error( SLAPI_LOG_FATAL, subsystem, "before: %s\n",
+ slapi_filter_to_string(f, logbuf, sizeof(logbuf)));
+ }
+
+ /* find decoders */
+ rc = index_subsys_assign_decoders(f);
+
+ if ( LDAPDebugLevelIsSet( LDAP_DEBUG_FILTER ) && NULL != f ) {
+ logbuf[0] = '\0';
+ slapi_log_error( SLAPI_LOG_FATAL, subsystem, " after: %s\n",
+ slapi_filter_to_string(f, logbuf, sizeof(logbuf)));
+ }
+
+ return rc;
+}
+
+/* index_subsys_filter_decoders_done
+ * ---------------------------------
+ * removes assigned index plugins in
+ * sub-filters
+ */
+int index_subsys_filter_decoders_done(Slapi_PBlock *pb)
+{
+ Slapi_Filter *f;
+
+ /* extract the filter */
+ slapi_pblock_get(pb, SLAPI_SEARCH_FILTER, &f);
+
+ /* remove decoders */
+ return index_subsys_decoder_done(f);
+}
+
+
+/* index_subsys_unlink_subfilter
+ * -----------------------------
+ * removes the sub-filter from
+ * the complex filter list
+ * does NOT deallocate the sub-filter
+ */
+static int index_subsys_unlink_subfilter(Slapi_Filter *fcomplex, Slapi_Filter *fsub)
+{
+ int ret = -1;
+ Slapi_Filter *f;
+ Slapi_Filter *f_prev = 0;
+
+ for(f=fcomplex->f_list; f != NULL; f = f->f_next)
+ {
+ if(f == fsub)
+ {
+ if(f_prev)
+ {
+ f_prev->f_next = f->f_next;
+ f->f_next = 0;
+ ret = 0;
+ break;
+ }
+ else
+ {
+ /* was at the beginning of the list */
+ fcomplex->f_list = f->f_next;
+ f->f_next = 0;
+ ret = 0;
+ break;
+ }
+ }
+
+ f_prev = f;
+ }
+
+ return ret;
+}
+
+/* index_subsys_index_matches_associated
+ * -------------------------------------
+ * determines if there is any kind of match
+ * between the specified type and the index.
+ *
+ * matches could be on the indexed type or
+ * on any associated attribute
+ * returns:
+ * 0 when false
+ * non-zero when true
+ */
+static int index_subsys_index_matches_associated(indexEntry *index, Slapi_Filter *f)
+{
+ int ret = 0;
+ char **associated_attrs = index->associated_attrs;
+
+ if(INDEX_MATCH_NONE != index_subsys_index_matches_filter(index, f))
+ {
+ /* matched on indexed attribute */
+ ret = -1;
+ goto bail;
+ }
+
+ /* check associated attributes */
+ if(associated_attrs)
+ {
+ int i;
+ char *type = f->f_type;
+
+ for(i=0; associated_attrs[i]; i++)
+ {
+ if(slapi_attr_types_equivalent(associated_attrs[i], type))
+ {
+ /* matched on associated attribute */
+ ret = -1;
+ break;
+ }
+ }
+ }
+
+bail:
+ return ret;
+}
+
+
+/* index_subsys_flatten_filter
+ * ---------------------------
+ * takes a complex filter as argument (assumed)
+ * and merges all compatible complex sub-filters
+ * such that their list of sub-filters are moved
+ * to the main list of sub-filters in f.
+ *
+ * This "flattens" the filter so that there are
+ * the minimum number of nested complex filters
+ * possible.
+ *
+ * What is a "compatible complex sub-filter?"
+ * Answer: a complex sub-filter which is of the
+ * same type (AND or OR) as the containing complex
+ * filter and which is either assigned the same
+ * index decoder or no index decoder is assigned to
+ * either complex filter.
+ *
+ * This function assumes that it has already
+ * been called for every complex sub-filter of f
+ * i.e. it only looks one layer deep.
+ *
+ * Note that once a filter has been processed in
+ * this fashion, rearranging the filter based
+ * on the optimal evaluation order becomes very
+ * much simpler. It should also have benefits for
+ * performance when a filter is evaluated many
+ * times since a linear list traversal is faster than a
+ * context switch to recurse down into a complex
+ * filter structure.
+ *
+ */
+static void index_subsys_flatten_filter(Slapi_Filter *flist)
+{
+ struct slapi_filter *f = flist->f_list;
+ struct slapi_filter *fprev = 0;
+ struct slapi_filter *flast = 0;
+
+ while(f)
+ {
+ if(f->assigned_decoder == flist->assigned_decoder)
+ {
+ /* mmmk, but is the filter complex? */
+ if(f->f_choice == LDAP_FILTER_AND || f->f_choice == LDAP_FILTER_OR)
+ {
+ if(f->f_choice == flist->f_choice)
+ {
+ /* flatten this, and remember
+ * we expect that any complex sub-filters
+ * have already been flattened, so we
+ * simply transfer the contents of this
+ * sub-filter to the main sub-filter and
+ * remove this complex sub-filter
+ *
+ * take care not to change the filter
+ * ordering in any way (it may have been
+ * optimized)
+ */
+ struct slapi_filter *fnext = f->f_next;
+ struct slapi_filter *fsub = 0;
+
+ while(f->f_list)
+ {
+ fsub = f->f_list;
+ index_subsys_unlink_subfilter(f, f->f_list);
+ fsub->f_next = fnext;
+
+ if(flast)
+ {
+ /* we inserted something before - insert after */
+ flast->f_next = fsub;
+ }
+ else
+ {
+ /* first insertion */
+ if(fprev)
+ {
+ fprev->f_next = fsub;
+ }
+ else
+ {
+ /* insert at list start */
+ flist->f_list = fsub;
+ }
+
+ fprev = fsub;
+ }
+
+ flast = fsub;
+ }
+
+ /* zero for dont recurse - recursing would
+ * be bad since we have created a mutant
+ * complex filter with no children
+ */
+ slapi_filter_free(f, 0);
+ f = fnext;
+ }
+ else
+ {
+ fprev = f;
+ f = f->f_next;
+ }
+ }
+ else
+ {
+ fprev = f;
+ f = f->f_next;
+ }
+ }
+ else
+ {
+ fprev = f;
+ f = f->f_next;
+ }
+ }
+}
+
+/* index_subsys_group_decoders
+ * ---------------------------
+ * looks for grouping opportunities
+ * such that a complex filter may
+ * be assigned to a single index.
+ *
+ * it is assumed that any complex sub-filters
+ * have already been assigned decoders
+ * using this function if it
+ * was possible to do so
+ */
+static int index_subsys_group_decoders(Slapi_Filter *flist)
+{
+ int ret = 0;
+ struct slapi_filter *f = 0;
+ struct slapi_filter *f_indexed = 0;
+ struct slapi_filter *fhead = 0;
+ int index_count = 0;
+ int matched = 1;
+
+ switch(flist->f_choice)
+ {
+ case LDAP_FILTER_AND:
+ case LDAP_FILTER_OR:
+ break;
+
+ default:
+ /* any other result not handled by this code */
+ goto bail;
+ }
+
+ /* make sure we start with an unassigned filter */
+ flist->assigned_decoder = 0;
+
+ /* Since this function is about optimal grouping of complex filters,
+ * lets explain what is happening here:
+ *
+ * Beyond detecting that what was passed in is already optimal,
+ * there are 4 basic problems we need to solve here:
+ *
+ * Input this function Output
+ * 1) (&(indexed)(other)(associated)) -> X -> (&(&(indexed)(associated))(other))
+ * 2) (&(&(indexed)(other))(associated)) -> X -> (&(&(indexed)(associated))(other))
+ * 3) (&(&(associated)(other))(indexed)) -> X -> (&(&(indexed)(associated))(other))
+ * 4) (&(&(indexed)(associated))(associated)) -> X -> (&(indexed)(associated)(associated))
+ *
+ * To avoid having special code for 2) and 3) we make them look like
+ * 1) by flattening the filter - note this will only flatten subfilters
+ * which have no decoder assigned since the filter we flatten has no
+ * decoder assigned - and this behaviour is exactly what we want.
+ * 4) is a special case of 1) and since that is the case, we can allow
+ * the code for 1) to process it but make sure we flatten the filter
+ * before exit. If 4) is exactly as stated without any other non-indexed
+ * or associated references then in fact it will be detected as a completely
+ * matching filter prior to reaching the code for 1).
+ */
+
+ index_subsys_flatten_filter(flist);
+ fhead = flist->f_list;
+
+ /* find the first index assigned */
+ for ( f_indexed = fhead; f_indexed != NULL; f_indexed = f_indexed->f_next )
+ {
+ if( f_indexed->assigned_decoder )
+ {
+ /* non-null decoder means assigned */
+ break;
+ }
+ }
+
+ if(NULL == f_indexed)
+ /* nothing to process */
+ goto bail;
+
+ /* determine if whole filter matches
+ * to avoid allocations where it is
+ * not necessary
+ */
+ for ( f = fhead; f != NULL; f = f->f_next )
+ {
+ if(f->assigned_decoder != f_indexed->assigned_decoder)
+ {
+ switch(f->f_choice)
+ {
+ case LDAP_FILTER_AND:
+ case LDAP_FILTER_OR:
+ /*
+ * All complex subfilters are guaranteed to have the correct
+ * decoder assigned already, so this is a mismatch.
+ */
+
+ matched = 0;
+ break;
+
+ default:
+ if(!index_subsys_index_matches_associated(f_indexed->assigned_decoder, f))
+ {
+ matched = 0;
+ }
+ break;
+ }
+
+ if(!matched)
+ break;
+ }
+ }
+
+ if(matched)
+ {
+ /* whole filter matches - assign to this decoder */
+ flist->assigned_decoder = f_indexed->assigned_decoder;
+ /* finally lets flatten this filter if possible
+ * Didn't we do that already? No, we flattened the
+ * filter *prior* to assigning a decoder
+ */
+ index_subsys_flatten_filter(flist);
+ goto bail;
+ }
+
+ /* whole filter does not match so,
+ * if the sub-filter count is > 2
+ * for each assigned sub-filter,
+ * match other groupable filters
+ * and extract them into another sub-filter
+ */
+
+ /* count */
+ for ( f = fhead; f != NULL && index_count < 3; f = f->f_next )
+ {
+ index_count++;
+ }
+
+ if(index_count > 2)
+ {
+ /* this is where we start re-arranging the filter assertions
+ * into groups which can be serviced by a single plugin
+ * at this point we know that:
+ * a) the filter has at least 2 assertions that cannot be grouped
+ * b) there are more than 2 assertions and so grouping is still possible
+ */
+
+ struct slapi_filter *f_listposition=f_indexed; /* flist subfilter list iterator */
+ int main_iterate; /* controls whether to iterate to the next sub-filter of flist */
+
+ while(f_listposition)
+ {
+ /* the target grouping container - we move sub-filters here */
+ struct slapi_filter *targetf=0;
+
+ /* indicates we found an existing targetf */
+ int assigned = 0;
+
+ struct slapi_filter *f_last = 0; /* previos filter in list */
+
+ /* something to join with next compatible
+ * subfilter we find - this will be the
+ * first occurence of a filter assigned
+ * to a particular decoder
+ */
+ struct slapi_filter *saved_filter = 0;
+
+ struct slapi_filter *f_tmp = 0; /* save filter for list fixups */
+
+ /* controls whether to iterate to the
+ * next sub-filter of flist
+ * inner loop
+ */
+ int iterate = 1;
+
+ f_indexed = f_listposition;
+ main_iterate = 1;
+
+ /* finding a convenient existing sub-filter of the same
+ * type as the containing filter avoids allocation
+ * so lets look for one
+ */
+
+ for ( f = fhead; f != NULL; f = f->f_next)
+ {
+ switch(f->f_choice)
+ {
+ case LDAP_FILTER_AND:
+ case LDAP_FILTER_OR:
+ if( f->f_choice == flist->f_choice &&
+ f->assigned_decoder == f_indexed->assigned_decoder)
+ {
+ targetf = f;
+ assigned = 1;
+ }
+ break;
+
+ default:
+ break;
+ }
+
+ if(assigned)
+ break;
+ }
+
+ /* now look for grouping opportunities */
+ for ( f = fhead; f != NULL; (iterate && f) ? f = f->f_next : f )
+ {
+ iterate = 1;
+
+ if(f != targetf)
+ {
+ switch(f->f_choice)
+ {
+ case LDAP_FILTER_AND:
+ case LDAP_FILTER_OR:
+ if( (targetf && f->f_choice == targetf->f_choice)
+ && f->assigned_decoder == f_indexed->assigned_decoder)
+ {
+ /* ok we have a complex filter we can group - group it
+ * it is worth noting that if we got here, then we must
+ * have found a complex filter suitable for for putting
+ * sub-filters in, so there is no need to add the newly
+ * merged complex filter to the main complex filter,
+ * since it is already there
+ */
+
+ /* main filter list fix ups */
+ f_tmp = f;
+ f = f->f_next;
+ iterate = 0;
+
+ if(f_tmp == f_listposition)
+ {
+ f_listposition = f;
+ main_iterate = 0;
+ }
+
+ /* remove f from the main complex filter */
+ index_subsys_unlink_subfilter(flist, f_tmp);
+
+
+ /* merge - note, not true merge since f gets
+ * added to targetf as a sub-filter
+ */
+ slapi_filter_join_ex(targetf->f_choice, targetf, f_tmp, 0);
+
+ /* since it was not a true merge, lets do the true merge now */
+ index_subsys_flatten_filter(targetf);
+ }
+ break;
+
+ default:
+ if(index_subsys_index_matches_associated(f_indexed->assigned_decoder, f))
+ {
+ if(targetf)
+ {
+ /* main filter list fix ups */
+ f_tmp = f;
+ f = f->f_next;
+ iterate = 0;
+
+ if(f_tmp == f_listposition)
+ {
+ f_listposition = f;
+ main_iterate = 0;
+ }
+
+ index_subsys_unlink_subfilter(flist, f_tmp);
+ targetf = slapi_filter_join_ex( targetf->f_choice, targetf, f_tmp, 0 );
+ }
+ else
+ {
+ if(saved_filter)
+ {
+ /* main filter list fix ups */
+ f_tmp = f;
+ f = f->f_next;
+ iterate = 0;
+
+ if(f_tmp == f_listposition)
+ {
+ f_listposition = f;
+ main_iterate = 0;
+ }
+
+ index_subsys_unlink_subfilter(flist, f_tmp);
+ index_subsys_unlink_subfilter(flist, saved_filter);
+ targetf = slapi_filter_join_ex( flist->f_choice, saved_filter, f_tmp, 0 );
+ targetf->assigned_decoder = f_indexed->assigned_decoder;
+ }
+ else
+ {
+ /* nothing to join so save this for
+ * when we find another compatible
+ * filter
+ */
+ saved_filter = f;
+ }
+ }
+
+ if(!assigned && targetf)
+ {
+ /* targetf has just been created, so
+ * we must add it to the main complex filter
+ */
+ targetf->f_next = flist->f_list;
+ flist->f_list = targetf;
+ assigned = 1;
+ }
+ }
+
+ break;
+ }
+ }
+
+ f_last = f;
+ }
+
+ /* iterate through the main list
+ * to the next indexed sub-filter
+ */
+ if( f_listposition &&
+ (main_iterate ||
+ (!main_iterate &&
+ !f_listposition->assigned_decoder)))
+ {
+ if(!f_listposition->f_next)
+ {
+ f_listposition = 0;
+ break;
+ }
+
+ for ( f_listposition = f_listposition->f_next; f_listposition != NULL; f_listposition = f_listposition->f_next )
+ {
+ if( f_listposition->assigned_decoder )
+ {
+ /* non-null decoder means assigned */
+ break;
+ }
+ }
+ }
+ }
+ }
+
+bail:
+
+ return ret;
+}
+
+
+/* index_subsys_assign_decoders
+ * ----------------------------
+ * recurses through complex filters
+ * assigning decoders
+ */
+static int index_subsys_assign_decoders(Slapi_Filter *f)
+{
+ int ret = 0;
+ Slapi_Filter *subf;
+
+ switch ( slapi_filter_get_choice( f ) )
+ {
+ case LDAP_FILTER_AND:
+ case LDAP_FILTER_OR:
+ case LDAP_FILTER_NOT:
+ /* assign simple filters first */
+ f->assigned_decoder = 0;
+ for(subf=f->f_list; subf != NULL; subf = subf->f_next )
+ ret = index_subsys_assign_decoders(subf);
+
+ /* now check for filter grouping opportunities... */
+ if(slapi_filter_get_choice( f ) != LDAP_FILTER_NOT)
+ index_subsys_group_decoders(f);
+ else
+ {
+ /* LDAP_FILTER_NOT is a special case
+ * the contained sub-filter determines
+ * the assigned index - the index plugin has
+ * the option to refuse to service the
+ * NOT filter when it is presented
+ */
+ f->assigned_decoder = f->f_list->assigned_decoder;
+ }
+
+ break;
+
+ default:
+ /* find a decoder that fits this simple filter */
+ ret = index_subsys_assign_decoder(f);
+ }
+
+ return ret;
+}
+
+/* index_subsys_decoder_done
+ * -------------------------
+ * recurses through complex filters
+ * removing decoders
+ */
+static int index_subsys_decoder_done(Slapi_Filter *f)
+{
+ int ret = 0;
+ Slapi_Filter *subf;
+ indexEntry *index;
+ indexEntry *next_index;
+
+ switch ( slapi_filter_get_choice( f ) )
+ {
+ case LDAP_FILTER_AND:
+ case LDAP_FILTER_OR:
+ case LDAP_FILTER_NOT:
+ /* remove simple filters first */
+ for(subf=f->f_list; subf != NULL; subf = subf->f_next )
+ ret = index_subsys_decoder_done(subf);
+
+ break;
+
+ default:
+ /* free the decoders - shallow free */
+ index = f->assigned_decoder;
+
+ while(index)
+ {
+ next_index = index->list.pNext;
+ slapi_ch_free((void**)index);
+ index = next_index;
+ }
+
+ f->assigned_decoder = 0;
+ }
+
+ return ret;
+}
+
+/* index_subsys_evaluate_filter
+ * ----------------------------
+ * passes the filter to the correct plugin
+ * index_subsys_assign_filter_decoders() must
+ * have been called previously on this filter
+ * this function can be safely called on all
+ * filters post index_subsys_assign_filter_decoders()
+ * whether they are assigned to a plugin or not
+ *
+ * returns:
+ * INDEX_FILTER_EVALUTED: a candidate list is produced
+ * INDEX_FILTER_UNEVALUATED: filter not considered
+ */
+int index_subsys_evaluate_filter(Slapi_Filter *f, Slapi_DN *namespace_dn, IndexEntryList **out)
+{
+ int ret = INDEX_FILTER_UNEVALUATED;
+ indexEntry *index = (indexEntry*)f->assigned_decoder;
+
+ if(index && theCache)
+ {
+ index_subsys_read_lock();
+
+ /* there is a list of indexes which may
+ * provide an answer for this filter, we
+ * need to invoke the first one that matches
+ * the namespace requested
+ */
+ for(; index; index = index->list.pNext)
+ {
+ /* check namespace */
+ if(slapi_sdn_compare(index->namespace_dn, namespace_dn))
+ {
+ /* wrong namespace */
+ continue;
+ }
+
+ /* execute the search */
+ if(index->lookup_func)
+ {
+ ret = (index->lookup_func)(f, out, index->user_data);
+ break;
+ }
+ }
+
+ index_subsys_unlock();
+ }
+
+ return ret;
+}
+
+
+/* slapi_index_register_decoder
+ * ----------------------------
+ * This allows a decoder to register itself,
+ * it also builds the initial cache when first
+ * called. Note, there is no way to deregister
+ * once registered - this allows a lock free cache
+ * at the expense of a restart to clear old
+ * indexes, this shouldnt be a problem since it is
+ * not expected that indexes will be removed
+ * often.
+ */
+int slapi_index_register_decoder(char *plugin_id, index_validate_callback validate_op)
+{
+ static int firstTime = 1;
+ static int gotIDLapi = 0;
+ int ret = 0;
+ indexPlugin *plg;
+
+ if(firstTime)
+ {
+ /* create the cache */
+ theCache = (globalIndexCache*)slapi_ch_malloc(sizeof(globalIndexCache));
+ if(theCache)
+ {
+ theCache->pPlugins = 0;
+ theCache->ppIndexIndex = 0;
+ theCache->index_count = 0;
+ theCache->cache_lock = PR_NewRWLock(PR_RWLOCK_RANK_NONE, "Index Plugins");;
+ firstTime = 0;
+
+ if(!gotIDLapi)
+ {
+ if(slapi_apib_get_interface(IDL_v1_0_GUID, &idl_api))
+ {
+ gotIDLapi = 1;
+ }
+ }
+ }
+ else
+ {
+ ret = -1;
+ goto bail;
+ }
+ }
+
+ index_subsys_write_lock();
+
+ /* add the index decoder to the cache - no checks, better register once only*/
+ plg = (indexPlugin*)slapi_ch_malloc(sizeof(indexPlugin));
+ if(plg)
+ {
+ plg->id = slapi_ch_strdup(plugin_id);
+ plg->indexes = 0;
+ plg->validate_op = validate_op;
+
+ /* we always add to the start of the linked list */
+ plg->list.pPrev = 0;
+
+ if(theCache->pPlugins)
+ {
+ plg->list.pNext = theCache->pPlugins;
+ theCache->pPlugins->list.pPrev = plg;
+ }
+ else
+ plg->list.pNext = 0;
+
+
+ theCache->pPlugins = plg;
+ }
+ else
+ ret = -1;
+
+ index_subsys_unlock();
+
+bail:
+ return ret;
+}
+
+
+/* slapi_index_register_index
+ * --------------------------
+ * a plugin that has registered itself may
+ * then proceed to register individual indexes
+ * that it looks after. This function adds
+ * the indexes to the plugin cache.
+ */
+int slapi_index_register_index(char *plugin_id, indexed_item *registration_item, void *user_data)
+{
+ int ret = 0;
+ indexPlugin *plg;
+ indexEntry *index;
+ int a_matched_index = 0;
+ Slapi_Filter *tmp_f = slapi_str2filter(registration_item->index_filter);
+ int i = 0;
+ Slapi_Backend *be;
+
+ if(!theCache)
+ return -1;
+
+ index_subsys_write_lock();
+
+ /* first lets find the plugin */
+ plg = theCache->pPlugins;
+
+ while(plg)
+ {
+ if(!slapi_UTF8CASECMP(plugin_id, plg->id))
+ {
+ /* found plugin */
+ break;
+ }
+
+ plg = plg->list.pNext;
+ }
+
+ if(0 == plg)
+ {
+ /* not found */
+ ret = -1;
+ goto bail;
+ }
+
+ /* now add the new index - we shall assume indexes
+ * will not be registered twice by different plugins,
+ * in that event, the last one added wins
+ * the first matching index in the list is always
+ * the current one, other matching indexes are ignored
+ * therefore reregistering an index with NULL
+ * callbacks disables the index for that plugin
+ */
+
+ /* find the index if already registered */
+
+ index = plg->indexes;
+
+ while(index)
+ {
+ if(index_subsys_index_matches_filter(index, tmp_f))
+ {
+ /* found it - free what is currently there, it will be replaced */
+ slapi_ch_free((void**)&index->indexfilterstr);
+ slapi_filter_free(index->indexfilter, 1);
+ slapi_ch_free((void**)&index->indexedAttribute);
+
+ charray_free( index->associated_attrs );
+ index->associated_attrs = 0;
+
+ a_matched_index = 1;
+ break;
+ }
+
+ index = index->list.pNext;
+ }
+
+ if(!index)
+ index = (indexEntry*)slapi_ch_calloc(1,sizeof(indexEntry));
+
+ index->indexfilterstr = slapi_ch_strdup(registration_item->index_filter);
+ index->indexfilter = tmp_f;
+ index->lookup_func = registration_item->search_op;
+ index->user_data = user_data;
+
+ /* map the namespace dn to a backend dn */
+ be = slapi_be_select( registration_item->namespace_dn );
+
+ if(be == defbackend_get_backend())
+ {
+ ret = -1;
+ goto bail;
+ }
+
+ index->namespace_dn = (Slapi_DN*)slapi_be_getsuffix(be, 0);
+
+ /* for now, we support simple filters only */
+ index->indexedAttribute = slapi_ch_strdup(index->indexfilter->f_type);
+
+ /* add associated attributes */
+ if(registration_item->associated_attrs)
+ {
+ index->associated_attrs =
+ cool_charray_dup( registration_item->associated_attrs );
+ }
+
+ if(!a_matched_index)
+ {
+ if(plg->indexes)
+ {
+ index->list.pNext = plg->indexes;
+ plg->indexes->list.pPrev = plg;
+ }
+ else
+ index->list.pNext = 0;
+
+ index->list.pPrev = 0;
+
+ plg->indexes = index;
+
+ theCache->index_count++;
+ }
+
+ /* now we need to rebuild the index (onto the indexed items)
+ * this is a bit inefficient since
+ * every new index that is added triggers
+ * an index rebuild - but this is countered
+ * by the fact this will probably happen once
+ * at start up most of the time, and very rarely
+ * otherwise, so normal server performance should
+ * not be unduly effected effected
+ * we take care to build the index and only then swap it in
+ * for the old one
+ * PARPAR: we need to RW (or maybe a ref count) lock here
+ * only alternative would be to not have an index :(
+ * for few plugins with few indexes thats a possibility
+ * traditionally many indexes have not been a good idea
+ * anyway so...
+ */
+
+/* indexIndex = (indexEntry**)slapi_ch_malloc(sizeof(indexEntry*) * (theCache->index_count+1));
+*/
+ /* for now, lets see how fast things are without an index
+ * that should not be a problem at all to begin with since
+ * presence will be the only index decoder. Additionally,
+ * adding an index means we need locks - um, no.
+ * so no more to do
+ */
+
+bail:
+ index_subsys_unlock();
+
+ return ret;
+}
+
+/* index_subsys_index_matches_index
+ * --------------------------------
+ * criteria for a match is that the types
+ * are the same and that all the associated
+ * attributes that are configured for cmp_to
+ * are also in cmp_with.
+ */
+int index_subsys_index_matches_index(indexEntry *cmp_to, indexEntry *cmp_with)
+{
+ int ret = 0;
+
+ if(slapi_attr_types_equivalent(cmp_to->indexedAttribute, cmp_with->indexedAttribute))
+ {
+ /* now check associated */
+ if(cmp_to->associated_attrs)
+ {
+ if(cmp_with->associated_attrs)
+ {
+ int x,y;
+
+ ret = 1;
+
+ for(x=0; cmp_to->associated_attrs[x] && ret == 1; x++)
+ {
+ ret = 0;
+
+ for(y=0; cmp_with->associated_attrs[y]; y++)
+ {
+ if(slapi_attr_types_equivalent(
+ cmp_to->associated_attrs[x],
+ cmp_with->associated_attrs[y]
+ ))
+ {
+ /* matched on associated attribute */
+ ret = 1;
+ break;
+ }
+ }
+ }
+ }
+ }
+ else
+ {
+ /* no associated is an auto match */
+ ret = 1;
+ }
+
+ }
+
+ return ret;
+}
+
+indexEntry *index_subsys_index_shallow_dup(indexEntry *dup_this)
+{
+ indexEntry *ret = (indexEntry *)slapi_ch_calloc(1,sizeof(indexEntry));
+
+ /* shallow copy - dont copy linked list pointers */
+ ret->indexedAttribute = dup_this->indexedAttribute;
+ ret->indexfilter = dup_this->indexfilter;
+ ret->indexfilterstr = dup_this->indexfilterstr;
+ ret->user_data = dup_this->user_data;
+ ret->namespace_dn = dup_this->namespace_dn;
+ ret->lookup_func = dup_this->lookup_func;
+ ret->associated_attrs = dup_this->associated_attrs;
+
+ return ret;
+}
+
+/* index_subsys_assign_decoder
+ * ---------------------------
+ * finds a decoder which will service
+ * the filter if one is available and assigns
+ * the decoder to the filter. Currently this
+ * function supports only simple filters, but
+ * may in the future support complex filter
+ * assignment (possibly including filter rewriting
+ * to make more matches possible)
+ *
+ * populates f->alternate_decoders if more than one
+ * index could deal with a filter - only filters that
+ * have a match with all associated attributes of the
+ * first found filter are said to match - their
+ * namespaces may be different
+ */
+static int index_subsys_assign_decoder(Slapi_Filter *f)
+{
+ int ret = 0; /* always succeed */
+ indexPlugin *plg;
+ indexEntry *index = 0;
+ indexEntry *last = 0;
+
+ f->assigned_decoder = 0;
+
+ if(!theCache)
+ return 0;
+
+ index_subsys_read_lock();
+
+ plg = theCache->pPlugins;
+
+ while(plg)
+ {
+ index = plg->indexes;
+
+ while(index)
+ {
+ if(INDEX_MATCH_NONE != index_subsys_index_matches_filter(index, f))
+ {
+ /* we have a match, assign this decoder if not disabled */
+ if(index->lookup_func)
+ {
+ if(!f->assigned_decoder)
+ {
+ f->assigned_decoder = index_subsys_index_shallow_dup(index);
+ last = f->assigned_decoder;
+ }
+ else
+ {
+ /* add to alternate list - we require that they all
+ * have the same associated attributes configuration for now
+ * though they may have different namespaces
+ */
+ if(index_subsys_index_matches_index(f->assigned_decoder, index))
+ {
+ /* add to end */
+ last->list.pNext = index_subsys_index_shallow_dup(index);
+ last = last->list.pNext;
+ }
+ }
+ }
+ else
+ {
+ /* index disabled, so we must allow another plugin to
+ * get a crack at servicing the index
+ */
+ break;
+ }
+ }
+
+ index = index->list.pNext;
+ }
+
+ plg = plg->list.pNext;
+ }
+
+ index_subsys_unlock();
+
+ return ret;
+}
+
diff --git a/ldap/servers/slapd/init.c b/ldap/servers/slapd/init.c
new file mode 100644
index 00000000..3bd4d934
--- /dev/null
+++ b/ldap/servers/slapd/init.c
@@ -0,0 +1,63 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+/* init.c - initialize various things */
+
+#include <stdio.h>
+#include <string.h>
+#include <sys/types.h>
+#ifndef _WIN32
+#include <sys/time.h>
+#include <netinet/in.h>
+#include <sys/socket.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+#endif
+#include "slap.h"
+#include "fe.h"
+#if defined( MACOS ) || defined( DOS ) || defined( _WIN32 ) || defined( NEED_BSDREGEX )
+#include "regex.h"
+#endif
+
+void
+slapd_init()
+{
+#ifdef _WIN32
+ WSADATA wsadata;
+ int err;
+
+ if( err = WSAStartup(0x0101, &wsadata ) != 0 ) {
+ LDAPDebug( LDAP_DEBUG_ANY,
+ "Windows Sockets initialization failed, error %d (%s)\n",
+ err, slapd_system_strerror( err ), 0 );
+ exit( 1 );
+ }
+#endif /* _WIN32 */
+
+ ops_mutex = PR_NewLock();
+ num_conns_mutex = PR_NewLock();
+ g_set_num_sent_mutex( PR_NewLock() );
+ g_set_current_conn_count_mutex( PR_NewLock() );
+ slapd_re_init();
+
+ if ( ops_mutex == NULL ||
+ num_conns_mutex == NULL ||
+ g_get_num_sent_mutex() == NULL ||
+ g_get_current_conn_count_mutex() == NULL )
+ {
+ LDAPDebug( LDAP_DEBUG_ANY,
+ "init: PR_NewLock failed\n", 0, 0, 0 );
+ exit( -1 );
+ }
+
+#ifndef HAVE_TIME_R
+ if ((time_func_mutex = PR_NewLock()) == NULL ) {
+ LDAPDebug( LDAP_DEBUG_ANY,
+ "init: PR_NewLock failed\n", 0, 0, 0 );
+ exit(-1);
+ }
+
+#endif /* HAVE_TIME_R */
+}
diff --git a/ldap/servers/slapd/intrinsics.h b/ldap/servers/slapd/intrinsics.h
new file mode 100644
index 00000000..f0b63431
--- /dev/null
+++ b/ldap/servers/slapd/intrinsics.h
@@ -0,0 +1,112 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+
+/* Header file used to declare functions which we beat on heavily as intrinsic */
+
+/* For NT ...*/
+
+#ifdef _WIN32
+__inline static int strcmpi_fast(const char * dst, const char * src)
+{
+ int f,l;
+ do {
+ if ( ((f = (unsigned char)(*(dst++))) >= 'A') && (f <= 'Z') )
+ f -= ('A' - 'a');
+ if ( ((l = (unsigned char)(*(src++))) >= 'A') && (l <= 'Z') )
+ l -= ('A' - 'a');
+ } while ( f && (f == l) );
+ return(f - l);
+}
+#ifdef strcasecmp
+#undef strcasecmp
+#endif
+#define strcasecmp(x,y) strcmpi_fast(x,y)
+#ifdef strcmpi
+#undef strcmpi
+#endif
+#define strcmpi(x,y) strcmpi_fast(x,y)
+
+__inline static int tolower_fast(int c)
+{
+ if ( (c >= 'A') && (c <= 'Z') )
+ c = c + ('a' - 'A');
+ return c;
+}
+#ifdef tolower
+#undef tolower
+#endif
+#define tolower(x) tolower_fast(x)
+
+#else
+
+#ifdef HPUX
+#pragma INLINE strcmpi_fast,tolower_fast,toupper_fast,strncasecmp_fast
+#endif
+#ifdef LINUX
+#define INLINE_DIRECTIVE __inline__
+#else
+#define INLINE_DIRECTIVE
+#endif
+
+INLINE_DIRECTIVE static int strcmpi_fast(const char * dst, const char * src)
+{
+ int f,l;
+ do {
+ if ( ((f = (unsigned char)(*(dst++))) >= 'A') && (f <= 'Z') )
+ f -= ('A' - 'a');
+ if ( ((l = (unsigned char)(*(src++))) >= 'A') && (l <= 'Z') )
+ l -= ('A' - 'a');
+ } while ( f && (f == l) );
+ return(f - l);
+}
+#ifdef strcasecmp
+#undef strcasecmp
+#endif
+#define strcasecmp(x,y) strcmpi_fast(x,y)
+#ifdef strcmpi
+#undef strcmpi
+#endif
+#define strcmpi(x,y) strcmpi_fast(x,y)
+
+INLINE_DIRECTIVE static int tolower_fast(int c)
+{
+ if ( (c >= 'A') && (c <= 'Z') )
+ c = c + ('a' - 'A');
+ return c;
+}
+#ifdef tolower
+#undef tolower
+#endif
+#define tolower(x) tolower_fast(x)
+
+INLINE_DIRECTIVE static int toupper_fast(int c)
+{
+ if ( (c >= 'a') && (c <= 'z') )
+ c = c - ('a' - 'A');
+ return c;
+}
+#ifdef toupper
+#undef toupper
+#endif
+#define toupper(x) toupper_fast(x)
+
+INLINE_DIRECTIVE static int strncasecmp_fast(const char * dst, const char * src, int n)
+{
+ int f,l,x=0;
+ do {
+ if ( ((f = (unsigned char)(*(dst++))) >= 'A') && (f <= 'Z') )
+ f -= ('A' - 'a');
+ if ( ((l = (unsigned char)(*(src++))) >= 'A') && (l <= 'Z') )
+ l -= ('A' - 'a');
+ } while ( f && (f == l) && ++x < n );
+ return(f - l);
+}
+
+#ifdef strncasecmp
+#undef strncasecmp
+#endif
+#define strncasecmp(x,y,z) strncasecmp_fast(x,y,z)
+#endif /* NT */
diff --git a/ldap/servers/slapd/ldbmlinktest.c b/ldap/servers/slapd/ldbmlinktest.c
new file mode 100644
index 00000000..a4df5bed
--- /dev/null
+++ b/ldap/servers/slapd/ldbmlinktest.c
@@ -0,0 +1,38 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+#if 0
+int detached;
+int error_logfp;
+#endif
+
+main()
+{
+ ldbm_back_bind();
+ ldbm_back_unbind();
+ ldbm_back_search();
+ ldbm_back_compare();
+ ldbm_back_modify();
+ ldbm_back_modrdn();
+ ldbm_back_add();
+ ldbm_back_delete();
+ ldbm_back_abandon();
+ ldbm_back_config();
+ ldbm_back_init();
+ ldbm_back_close();
+ ldbm_back_flush();
+}
+
+#if 0
+slapi_access_allowed(){}
+send_ldap_result(){}
+slapi_op_abandoned(){}
+be_issuffix(){}
+slapi_pw_find(){}
+send_ldap_search_entry(){}
+slapi_pblock_get(){}
+slapi_pblock_set(){}
+slapi_acl_check_mods(){}
+#endif
diff --git a/ldap/servers/slapd/lenstr.c b/ldap/servers/slapd/lenstr.c
new file mode 100644
index 00000000..f7b19054
--- /dev/null
+++ b/ldap/servers/slapd/lenstr.c
@@ -0,0 +1,80 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+
+#include <string.h>
+#include "slapi-private.h"
+
+#define LS_INCRSIZE 256
+
+/*
+ * Function: addlenstr
+ * Arguments: l - pointer to an allocated lenstr structure
+ * str - the (null-terminated) string to append
+ * Returns: nothing
+ * Description: Add "str" to the lenstr, increasing the size if needed.
+ */
+void
+addlenstr( lenstr *l, const char *str )
+{
+ size_t len = strlen( str );
+
+ if ( l->ls_buf == NULL ) {
+ /* string is empty */
+ l->ls_maxlen = ( len > LS_INCRSIZE ) ? len : LS_INCRSIZE;
+ l->ls_len = len;
+ l->ls_buf = slapi_ch_malloc( l->ls_maxlen + 1 );
+ memcpy( l->ls_buf, str, len + 1 );
+ } else {
+ if ( l->ls_len + len > l->ls_maxlen ) {
+ l->ls_maxlen *= 2;
+ if (l->ls_maxlen < l->ls_len + len) {
+ l->ls_maxlen += len;
+ }
+ l->ls_buf = slapi_ch_realloc( l->ls_buf, l->ls_maxlen + 1 );
+ }
+ memcpy( l->ls_buf + l->ls_len, str, len + 1 );
+ l->ls_len += len;
+ }
+}
+
+
+
+/*
+ * Function: lenstr_free
+ * Arguments: l - pointer to an allocated lenstr structure
+ * Returns: nothing
+ * Description: Free a lenstr.
+ */
+void
+lenstr_free( lenstr **l )
+{
+ if ( NULL != l && NULL != *l ) {
+ lenstr *tl = *l;
+ if ( tl->ls_buf != NULL ) {
+ slapi_ch_free((void **) &tl->ls_buf );
+ }
+ slapi_ch_free((void **) l );
+ }
+}
+
+
+
+/*
+ * Function: lenstr_new
+ * Returns: an empty, newly-allocated lenstr
+ */
+lenstr *
+lenstr_new()
+{
+ lenstr *l;
+
+ l = ( lenstr * ) slapi_ch_malloc( sizeof( lenstr ));
+ l->ls_buf = NULL;
+ l->ls_len = l->ls_maxlen = 0;
+ return l;
+}
+
+
diff --git a/ldap/servers/slapd/libglobs.c b/ldap/servers/slapd/libglobs.c
new file mode 100644
index 00000000..ac9ac10c
--- /dev/null
+++ b/ldap/servers/slapd/libglobs.c
@@ -0,0 +1,4051 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+/*
+ *
+ * libglobs.c -- SLAPD library global variables
+ */
+/* for windows only
+ we define slapd_ldap_debug here, so we don't want to declare
+ it in any header file which might conflict with our definition
+*/
+#define DONT_DECLARE_SLAPD_LDAP_DEBUG /* see ldaplog.h */
+
+#if defined(NET_SSL)
+#include "ldap.h"
+#include <sslproto.h>
+#include <ldap_ssl.h>
+
+#undef OFF
+#undef LITTLE_ENDIAN
+
+#endif
+
+#include <stdio.h>
+#include <string.h>
+#include <sys/types.h>
+#include <time.h>
+#include <stdarg.h>
+#include <stdlib.h>
+#if defined( _WIN32 )
+#define R_OK 04
+#include "ntslapdmessages.h"
+#include "proto-ntutil.h"
+#else
+#include <sys/time.h>
+#include <sys/param.h> /* MAXPATHLEN */
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+#include <unistd.h>
+#include <pwd.h> /* pwdnam */
+#endif
+#ifdef USE_SYSCONF
+#include <unistd.h>
+#endif /* USE_SYSCONF */
+#include "slap.h"
+#include "plhash.h"
+
+#define REMOVE_CHANGELOG_CMD "remove"
+
+/* On UNIX, there's only one copy of slapd_ldap_debug */
+/* On NT, each module keeps its own module_ldap_debug, which */
+/* points to the process' slapd_ldap_debug */
+#ifdef _WIN32
+int *module_ldap_debug;
+int __declspec(dllexport) slapd_ldap_debug = LDAP_DEBUG_ANY;
+#else
+int slapd_ldap_debug = LDAP_DEBUG_ANY;
+#endif
+
+char *ldap_srvtab = "";
+
+/* Note that the 'attrname' arguments are used only for log messages */
+typedef int (*ConfigSetFunc)(const char *attrname, char *value,
+ char *errorbuf, int apply);
+typedef int (*LogSetFunc)(const char *attrname, char *value, int whichlog,
+ char *errorbuf, int apply);
+
+typedef enum {
+ CONFIG_INT, /* maps to int */
+ CONFIG_LONG, /* maps to long */
+ CONFIG_STRING, /* maps to char* */
+ CONFIG_CHARRAY, /* maps to char** */
+ CONFIG_ON_OFF, /* maps 0/1 to "off"/"on" */
+ CONFIG_STRING_OR_OFF, /* use "off" instead of null or an empty string */
+ CONFIG_STRING_OR_UNKNOWN, /* use "unknown" instead of an empty string */
+ CONFIG_CONSTANT_INT, /* for #define values, e.g. */
+ CONFIG_CONSTANT_STRING, /* for #define values, e.g. */
+ CONFIG_SPECIAL_REFERRALLIST, /* this is a berval list */
+ CONFIG_SPECIAL_SSLCLIENTAUTH, /* maps strings to an enumeration */
+ CONFIG_SPECIAL_ERRORLOGLEVEL, /* requires & with LDAP_DEBUG_ANY */
+ CONFIG_STRING_OR_EMPTY /* use an empty string */
+} ConfigVarType;
+
+static int config_set_onoff( const char *attrname, char *value,
+ int *configvalue, char *errorbuf, int apply );
+static int config_set_schemareplace ( const char *attrname, char *value,
+ char *errorbuf, int apply );
+
+static int
+isIntegralType(ConfigVarType type)
+{
+ return type == CONFIG_INT || type == CONFIG_LONG || type == CONFIG_ON_OFF;
+}
+
+/* the caller will typically have to cast the result based on the ConfigVarType */
+typedef void *(*ConfigGetFunc)(void);
+
+/* static Ref_Array global_referrals; */
+static slapdFrontendConfig_t global_slapdFrontendConfig;
+
+static struct config_get_and_set {
+ const char *attr_name; /* the name of the attribute */
+ ConfigSetFunc setfunc; /* the function to call to set the value */
+ LogSetFunc logsetfunc; /* log functions are special */
+ int whichlog; /* ACCESS, ERROR, AUDIT, etc. */
+ void** config_var_addr; /* address of member of slapdFrontendConfig struct */
+ ConfigVarType config_var_type; /* cast to this type when getting */
+ ConfigGetFunc getfunc; /* for special handling */
+} ConfigList[] = {
+ {CONFIG_AUDITLOG_MODE_ATTRIBUTE, NULL,
+ log_set_mode, SLAPD_AUDIT_LOG,
+ (void**)&global_slapdFrontendConfig.auditlog_mode, CONFIG_STRING, NULL},
+ {CONFIG_AUDITLOG_LOGROTATIONSYNCENABLED_ATTRIBUTE, NULL,
+ log_set_rotationsync_enabled, SLAPD_AUDIT_LOG,
+ (void**)&global_slapdFrontendConfig.auditlog_rotationsync_enabled, CONFIG_ON_OFF, NULL},
+ {CONFIG_AUDITLOG_LOGROTATIONSYNCHOUR_ATTRIBUTE, NULL,
+ log_set_rotationsynchour, SLAPD_AUDIT_LOG,
+ (void**)&global_slapdFrontendConfig.auditlog_rotationsynchour, CONFIG_INT, NULL},
+ {CONFIG_AUDITLOG_LOGROTATIONSYNCMIN_ATTRIBUTE, NULL,
+ log_set_rotationsyncmin, SLAPD_AUDIT_LOG,
+ (void**)&global_slapdFrontendConfig.auditlog_rotationsyncmin, CONFIG_INT, NULL},
+ {CONFIG_AUDITLOG_LOGROTATIONTIME_ATTRIBUTE, NULL,
+ log_set_rotationtime, SLAPD_AUDIT_LOG,
+ (void**)&global_slapdFrontendConfig.auditlog_rotationtime, CONFIG_INT, NULL},
+ {CONFIG_ACCESSLOG_MODE_ATTRIBUTE, NULL,
+ log_set_mode, SLAPD_ACCESS_LOG,
+ (void**)&global_slapdFrontendConfig.accesslog_mode, CONFIG_STRING, NULL},
+ {CONFIG_ACCESSLOG_MAXNUMOFLOGSPERDIR_ATTRIBUTE, NULL,
+ log_set_numlogsperdir, SLAPD_ACCESS_LOG,
+ (void**)&global_slapdFrontendConfig.accesslog_maxnumlogs, CONFIG_INT, NULL},
+ {CONFIG_LOGLEVEL_ATTRIBUTE, config_set_errorlog_level,
+ NULL, 0,
+ (void**)&global_slapdFrontendConfig.errorloglevel,
+ CONFIG_SPECIAL_ERRORLOGLEVEL, NULL},
+ {CONFIG_ERRORLOG_LOGGING_ENABLED_ATTRIBUTE, NULL,
+ log_set_logging, SLAPD_ERROR_LOG,
+ (void**)&global_slapdFrontendConfig.errorlog_logging_enabled, CONFIG_ON_OFF, NULL},
+ {CONFIG_ERRORLOG_MODE_ATTRIBUTE, NULL,
+ log_set_mode, SLAPD_ERROR_LOG,
+ (void**)&global_slapdFrontendConfig.errorlog_mode, CONFIG_STRING, NULL},
+ {CONFIG_ERRORLOG_LOGEXPIRATIONTIME_ATTRIBUTE, NULL,
+ log_set_expirationtime, SLAPD_ERROR_LOG,
+ (void**)&global_slapdFrontendConfig.errorlog_exptime, CONFIG_INT, NULL},
+ {CONFIG_ACCESSLOG_LOGGING_ENABLED_ATTRIBUTE, NULL,
+ log_set_logging, SLAPD_ACCESS_LOG,
+ (void**)&global_slapdFrontendConfig.accesslog_logging_enabled, CONFIG_ON_OFF, NULL},
+ {CONFIG_PORT_ATTRIBUTE, config_set_port,
+ NULL, 0,
+ (void**)&global_slapdFrontendConfig.port, CONFIG_INT, NULL},
+ {CONFIG_WORKINGDIR_ATTRIBUTE, config_set_workingdir,
+ NULL, 0,
+ (void**)&global_slapdFrontendConfig.workingdir, CONFIG_STRING_OR_EMPTY, NULL},
+ {CONFIG_MAXTHREADSPERCONN_ATTRIBUTE, config_set_maxthreadsperconn,
+ NULL, 0,
+ (void**)&global_slapdFrontendConfig.maxthreadsperconn, CONFIG_INT, NULL},
+ {CONFIG_ACCESSLOG_LOGEXPIRATIONTIME_ATTRIBUTE, NULL,
+ log_set_expirationtime, SLAPD_ACCESS_LOG,
+ (void**)&global_slapdFrontendConfig.accesslog_exptime, CONFIG_INT, NULL},
+#ifndef _WIN32
+ {CONFIG_LOCALUSER_ATTRIBUTE, config_set_localuser,
+ NULL, 0,
+ (void**)&global_slapdFrontendConfig.localuser, CONFIG_STRING, NULL},
+#endif
+ {CONFIG_ERRORLOG_LOGROTATIONSYNCENABLED_ATTRIBUTE, NULL,
+ log_set_rotationsync_enabled, SLAPD_ERROR_LOG,
+ (void**)&global_slapdFrontendConfig.errorlog_rotationsync_enabled, CONFIG_ON_OFF, NULL},
+ {CONFIG_ERRORLOG_LOGROTATIONSYNCHOUR_ATTRIBUTE, NULL,
+ log_set_rotationsynchour, SLAPD_ERROR_LOG,
+ (void**)&global_slapdFrontendConfig.errorlog_rotationsynchour, CONFIG_INT, NULL},
+ {CONFIG_ERRORLOG_LOGROTATIONSYNCMIN_ATTRIBUTE, NULL,
+ log_set_rotationsyncmin, SLAPD_ERROR_LOG,
+ (void**)&global_slapdFrontendConfig.errorlog_rotationsyncmin, CONFIG_INT, NULL},
+ {CONFIG_ERRORLOG_LOGROTATIONTIME_ATTRIBUTE, NULL,
+ log_set_rotationtime, SLAPD_ERROR_LOG,
+ (void**)&global_slapdFrontendConfig.errorlog_rotationtime, CONFIG_INT, NULL},
+ {CONFIG_PW_INHISTORY_ATTRIBUTE, config_set_pw_inhistory,
+ NULL, 0,
+ (void**)&global_slapdFrontendConfig.pw_policy.pw_inhistory, CONFIG_INT, NULL},
+ {CONFIG_PW_STORAGESCHEME_ATTRIBUTE, config_set_pw_storagescheme,
+ NULL, 0, NULL, CONFIG_STRING, (ConfigGetFunc)config_get_pw_storagescheme},
+ {CONFIG_PW_UNLOCK_ATTRIBUTE, config_set_pw_unlock,
+ NULL, 0,
+ (void**)&global_slapdFrontendConfig.pw_policy.pw_unlock, CONFIG_ON_OFF, NULL},
+ {CONFIG_PW_GRACELIMIT_ATTRIBUTE, config_set_pw_gracelimit,
+ NULL, 0,
+ (void**)&global_slapdFrontendConfig.pw_policy.pw_gracelimit, CONFIG_INT, NULL},
+ {CONFIG_ACCESSLOG_LOGROTATIONSYNCENABLED_ATTRIBUTE, NULL,
+ log_set_rotationsync_enabled, SLAPD_ACCESS_LOG,
+ (void**)&global_slapdFrontendConfig.accesslog_rotationsync_enabled, CONFIG_ON_OFF, NULL},
+ {CONFIG_ACCESSLOG_LOGROTATIONSYNCHOUR_ATTRIBUTE, NULL,
+ log_set_rotationsynchour, SLAPD_ACCESS_LOG,
+ (void**)&global_slapdFrontendConfig.accesslog_rotationsynchour, CONFIG_INT, NULL},
+ {CONFIG_ACCESSLOG_LOGROTATIONSYNCMIN_ATTRIBUTE, NULL,
+ log_set_rotationsyncmin, SLAPD_ACCESS_LOG,
+ (void**)&global_slapdFrontendConfig.accesslog_rotationsyncmin, CONFIG_INT, NULL},
+ {CONFIG_ACCESSLOG_LOGROTATIONTIME_ATTRIBUTE, NULL,
+ log_set_rotationtime, SLAPD_ACCESS_LOG,
+ (void**)&global_slapdFrontendConfig.accesslog_rotationtime, CONFIG_INT, NULL},
+ {CONFIG_PW_MUSTCHANGE_ATTRIBUTE, config_set_pw_must_change,
+ NULL, 0,
+ (void**)&global_slapdFrontendConfig.pw_policy.pw_must_change, CONFIG_ON_OFF, NULL},
+ {CONFIG_PWPOLICY_LOCAL_ATTRIBUTE, config_set_pwpolicy_local,
+ NULL, 0,
+ (void**)&global_slapdFrontendConfig.pwpolicy_local, CONFIG_ON_OFF, NULL},
+ {CONFIG_AUDITLOG_MAXLOGDISKSPACE_ATTRIBUTE, NULL,
+ log_set_maxdiskspace, SLAPD_AUDIT_LOG,
+ (void**)&global_slapdFrontendConfig.auditlog_maxdiskspace, CONFIG_INT, NULL},
+ {CONFIG_SIZELIMIT_ATTRIBUTE, config_set_sizelimit,
+ NULL, 0,
+ (void**)&global_slapdFrontendConfig.sizelimit, CONFIG_INT, NULL},
+ {CONFIG_AUDITLOG_MAXLOGSIZE_ATTRIBUTE, NULL,
+ log_set_logsize, SLAPD_AUDIT_LOG,
+ (void**)&global_slapdFrontendConfig.auditlog_maxlogsize, CONFIG_INT, NULL},
+ {CONFIG_PW_WARNING_ATTRIBUTE, config_set_pw_warning,
+ NULL, 0,
+ (void**)&global_slapdFrontendConfig.pw_policy.pw_warning, CONFIG_LONG, NULL},
+ {CONFIG_READONLY_ATTRIBUTE, config_set_readonly,
+ NULL, 0,
+ (void**)&global_slapdFrontendConfig.readonly, CONFIG_ON_OFF, NULL},
+ {CONFIG_THREADNUMBER_ATTRIBUTE, config_set_threadnumber,
+ NULL, 0,
+ (void**)&global_slapdFrontendConfig.threadnumber, CONFIG_INT, NULL},
+ {CONFIG_PW_LOCKOUT_ATTRIBUTE, config_set_pw_lockout,
+ NULL, 0,
+ (void**)&global_slapdFrontendConfig.pw_policy.pw_lockout, CONFIG_ON_OFF, NULL},
+ {CONFIG_ENQUOTE_SUP_OC_ATTRIBUTE, config_set_enquote_sup_oc,
+ NULL, 0,
+ (void**)&global_slapdFrontendConfig.enquote_sup_oc, CONFIG_ON_OFF, NULL},
+ {CONFIG_LOCALHOST_ATTRIBUTE, config_set_localhost,
+ NULL, 0,
+ (void**)&global_slapdFrontendConfig.localhost, CONFIG_STRING, NULL},
+ {CONFIG_IOBLOCKTIMEOUT_ATTRIBUTE, config_set_ioblocktimeout,
+ NULL, 0,
+ (void**)&global_slapdFrontendConfig.ioblocktimeout, CONFIG_INT, NULL},
+ {CONFIG_MAX_FILTER_NEST_LEVEL_ATTRIBUTE, config_set_max_filter_nest_level,
+ NULL, 0, (void**)&global_slapdFrontendConfig.max_filter_nest_level,
+ CONFIG_INT, NULL},
+ {CONFIG_ERRORLOG_MAXLOGDISKSPACE_ATTRIBUTE, NULL,
+ log_set_maxdiskspace, SLAPD_ERROR_LOG,
+ (void**)&global_slapdFrontendConfig.errorlog_maxdiskspace, CONFIG_INT, NULL},
+ {CONFIG_PW_MINLENGTH_ATTRIBUTE, config_set_pw_minlength,
+ NULL, 0,
+ (void**)&global_slapdFrontendConfig.pw_policy.pw_minlength, CONFIG_INT, NULL},
+ {CONFIG_ERRORLOG_ATTRIBUTE, config_set_errorlog,
+ NULL, 0,
+ (void**)&global_slapdFrontendConfig.errorlog, CONFIG_STRING_OR_EMPTY, NULL},
+ {CONFIG_AUDITLOG_LOGEXPIRATIONTIME_ATTRIBUTE, NULL,
+ log_set_expirationtime, SLAPD_AUDIT_LOG,
+ (void**)&global_slapdFrontendConfig.auditlog_exptime, CONFIG_INT, NULL},
+ {CONFIG_SCHEMACHECK_ATTRIBUTE, config_set_schemacheck,
+ NULL, 0,
+ (void**)&global_slapdFrontendConfig.schemacheck, CONFIG_ON_OFF, NULL},
+ {CONFIG_DS4_COMPATIBLE_SCHEMA_ATTRIBUTE, config_set_ds4_compatible_schema,
+ NULL, 0,
+ (void**)&global_slapdFrontendConfig.ds4_compatible_schema,
+ CONFIG_ON_OFF, NULL},
+ {CONFIG_SCHEMA_IGNORE_TRAILING_SPACES,
+ config_set_schema_ignore_trailing_spaces, NULL, 0,
+ (void**)&global_slapdFrontendConfig.schema_ignore_trailing_spaces,
+ CONFIG_ON_OFF, NULL},
+ {CONFIG_SCHEMAREPLACE_ATTRIBUTE, config_set_schemareplace, NULL, 0,
+ (void**)&global_slapdFrontendConfig.schemareplace,
+ CONFIG_STRING_OR_OFF, NULL},
+ {CONFIG_ACCESSLOG_MAXLOGDISKSPACE_ATTRIBUTE, NULL,
+ log_set_maxdiskspace, SLAPD_ACCESS_LOG,
+ (void**)&global_slapdFrontendConfig.accesslog_maxdiskspace, CONFIG_INT, NULL},
+ {CONFIG_REFERRAL_ATTRIBUTE, (ConfigSetFunc)config_set_defaultreferral,
+ NULL, 0,
+ (void**)&global_slapdFrontendConfig.defaultreferral,
+ CONFIG_SPECIAL_REFERRALLIST, NULL},
+ {CONFIG_PW_MAXFAILURE_ATTRIBUTE, config_set_pw_maxfailure,
+ NULL, 0,
+ (void**)&global_slapdFrontendConfig.pw_policy.pw_maxfailure, CONFIG_INT, NULL},
+ {CONFIG_ACCESSLOG_ATTRIBUTE, config_set_accesslog,
+ NULL, 0,
+ (void**)&global_slapdFrontendConfig.accesslog, CONFIG_STRING_OR_EMPTY, NULL},
+ {CONFIG_LASTMOD_ATTRIBUTE, config_set_lastmod,
+ NULL, 0,
+ (void**)&global_slapdFrontendConfig.lastmod, CONFIG_ON_OFF, NULL},
+ {CONFIG_ROOTPWSTORAGESCHEME_ATTRIBUTE, config_set_rootpwstoragescheme,
+ NULL, 0, NULL, CONFIG_STRING, (ConfigGetFunc)config_get_rootpwstoragescheme},
+ {CONFIG_PW_HISTORY_ATTRIBUTE, config_set_pw_history,
+ NULL, 0,
+ (void**)&global_slapdFrontendConfig.pw_policy.pw_history, CONFIG_ON_OFF, NULL},
+ {CONFIG_SECURITY_ATTRIBUTE, config_set_security,
+ NULL, 0,
+ (void**)&global_slapdFrontendConfig.security, CONFIG_ON_OFF, NULL},
+ {CONFIG_PW_MAXAGE_ATTRIBUTE, config_set_pw_maxage,
+ NULL, 0,
+ (void**)&global_slapdFrontendConfig.pw_policy.pw_maxage, CONFIG_LONG, NULL},
+ {CONFIG_AUDITLOG_LOGROTATIONTIMEUNIT_ATTRIBUTE, NULL,
+ log_set_rotationtimeunit, SLAPD_AUDIT_LOG,
+ (void**)&global_slapdFrontendConfig.auditlog_rotationunit,
+ CONFIG_STRING_OR_UNKNOWN, NULL},
+ {CONFIG_PW_RESETFAILURECOUNT_ATTRIBUTE, config_set_pw_resetfailurecount,
+ NULL, 0,
+ (void**)&global_slapdFrontendConfig.pw_policy.pw_resetfailurecount, CONFIG_LONG, NULL},
+ {CONFIG_PW_ISGLOBAL_ATTRIBUTE, config_set_pw_is_global_policy,
+ NULL, 0,
+ (void**)&global_slapdFrontendConfig.pw_is_global_policy, CONFIG_ON_OFF, NULL},
+ {CONFIG_AUDITLOG_MAXNUMOFLOGSPERDIR_ATTRIBUTE, NULL,
+ log_set_numlogsperdir, SLAPD_AUDIT_LOG,
+ (void**)&global_slapdFrontendConfig.auditlog_maxnumlogs, CONFIG_INT, NULL},
+ {CONFIG_ERRORLOG_LOGEXPIRATIONTIMEUNIT_ATTRIBUTE, NULL,
+ log_set_expirationtimeunit, SLAPD_ERROR_LOG,
+ (void**)&global_slapdFrontendConfig.errorlog_exptimeunit,
+ CONFIG_STRING_OR_UNKNOWN, NULL},
+ /* errorlog list is read only, so no set func and no config var addr */
+ {CONFIG_ERRORLOG_LIST_ATTRIBUTE, NULL, NULL, 0, NULL,
+ CONFIG_CHARRAY, (ConfigGetFunc)config_get_errorlog_list},
+ {CONFIG_GROUPEVALNESTLEVEL_ATTRIBUTE, config_set_groupevalnestlevel,
+ NULL, 0,
+ (void**)&global_slapdFrontendConfig.groupevalnestlevel, CONFIG_INT, NULL},
+ {CONFIG_ACCESSLOG_LOGEXPIRATIONTIMEUNIT_ATTRIBUTE, NULL,
+ log_set_expirationtimeunit, SLAPD_ACCESS_LOG,
+ (void**)&global_slapdFrontendConfig.accesslog_exptimeunit,
+ CONFIG_STRING_OR_UNKNOWN, NULL},
+ {CONFIG_ROOTPW_ATTRIBUTE, config_set_rootpw,
+ NULL, 0,
+ (void**)&global_slapdFrontendConfig.rootpw, CONFIG_STRING, NULL},
+ {CONFIG_PW_CHANGE_ATTRIBUTE, config_set_pw_change,
+ NULL, 0,
+ (void**)&global_slapdFrontendConfig.pw_policy.pw_change, CONFIG_ON_OFF, NULL},
+ {CONFIG_ACCESSLOGLEVEL_ATTRIBUTE, config_set_accesslog_level,
+ NULL, 0,
+ (void**)&global_slapdFrontendConfig.accessloglevel, CONFIG_INT, NULL},
+ {CONFIG_ERRORLOG_LOGROTATIONTIMEUNIT_ATTRIBUTE, NULL,
+ log_set_rotationtimeunit, SLAPD_ERROR_LOG,
+ (void**)&global_slapdFrontendConfig.errorlog_rotationunit,
+ CONFIG_STRING_OR_UNKNOWN, NULL},
+ {CONFIG_SECUREPORT_ATTRIBUTE, config_set_secureport,
+ NULL, 0,
+ (void**)&global_slapdFrontendConfig.secureport, CONFIG_INT, NULL},
+ {CONFIG_BASEDN_ATTRIBUTE, config_set_basedn,
+ NULL, 0,
+ (void**)&global_slapdFrontendConfig.certmap_basedn, CONFIG_STRING, NULL},
+ {CONFIG_TIMELIMIT_ATTRIBUTE, config_set_timelimit,
+ NULL, 0,
+ (void**)&global_slapdFrontendConfig.timelimit, CONFIG_INT, NULL},
+ {CONFIG_ERRORLOG_MAXLOGSIZE_ATTRIBUTE, NULL,
+ log_set_logsize, SLAPD_ERROR_LOG,
+ (void**)&global_slapdFrontendConfig.errorlog_maxlogsize, CONFIG_INT, NULL},
+ {CONFIG_RESERVEDESCRIPTORS_ATTRIBUTE, config_set_reservedescriptors,
+ NULL, 0,
+ (void**)&global_slapdFrontendConfig.reservedescriptors, CONFIG_INT, NULL},
+ /* access log list is read only, no set func, no config var addr */
+ {CONFIG_ACCESSLOG_LIST_ATTRIBUTE, NULL, NULL, 0,
+ NULL, CONFIG_CHARRAY, (ConfigGetFunc)config_get_accesslog_list},
+ {CONFIG_SVRTAB_ATTRIBUTE, config_set_srvtab,
+ NULL, 0,
+ (void**)&global_slapdFrontendConfig.srvtab, CONFIG_STRING, NULL},
+ {CONFIG_PW_EXP_ATTRIBUTE, config_set_pw_exp,
+ NULL, 0,
+ (void**)&global_slapdFrontendConfig.pw_policy.pw_exp, CONFIG_ON_OFF, NULL},
+ {CONFIG_ACCESSCONTROL_ATTRIBUTE, config_set_accesscontrol,
+ NULL, 0,
+ (void**)&global_slapdFrontendConfig.accesscontrol, CONFIG_ON_OFF, NULL},
+ {CONFIG_AUDITLOG_LIST_ATTRIBUTE, NULL, NULL, 0,
+ NULL, CONFIG_CHARRAY, (ConfigGetFunc)config_get_auditlog_list},
+ {CONFIG_ACCESSLOG_LOGROTATIONTIMEUNIT_ATTRIBUTE, NULL,
+ log_set_rotationtimeunit, SLAPD_ACCESS_LOG,
+ (void**)&global_slapdFrontendConfig.accesslog_rotationunit, CONFIG_STRING, NULL},
+ {CONFIG_PW_LOCKDURATION_ATTRIBUTE, config_set_pw_lockduration,
+ NULL, 0,
+ (void**)&global_slapdFrontendConfig.pw_policy.pw_lockduration, CONFIG_LONG, NULL},
+ {CONFIG_ACCESSLOG_MAXLOGSIZE_ATTRIBUTE, NULL,
+ log_set_logsize, SLAPD_ACCESS_LOG,
+ (void**)&global_slapdFrontendConfig.accesslog_maxlogsize, CONFIG_INT, NULL},
+ {CONFIG_IDLETIMEOUT_ATTRIBUTE, config_set_idletimeout,
+ NULL, 0,
+ (void**)&global_slapdFrontendConfig.idletimeout, CONFIG_INT, NULL},
+ {CONFIG_NAGLE_ATTRIBUTE, config_set_nagle,
+ NULL, 0,
+ (void**)&global_slapdFrontendConfig.nagle, CONFIG_ON_OFF, NULL},
+ {CONFIG_ERRORLOG_MINFREEDISKSPACE_ATTRIBUTE, NULL,
+ log_set_mindiskspace, SLAPD_ERROR_LOG,
+ (void**)&global_slapdFrontendConfig.errorlog_minfreespace, CONFIG_INT, NULL},
+ {CONFIG_AUDITLOG_LOGGING_ENABLED_ATTRIBUTE, NULL,
+ log_set_logging, SLAPD_AUDIT_LOG,
+ (void**)&global_slapdFrontendConfig.auditlog_logging_enabled, CONFIG_ON_OFF, NULL},
+ {CONFIG_ACCESSLOG_BUFFERING_ATTRIBUTE, config_set_accesslogbuffering,
+ NULL, 0,
+ (void**)&global_slapdFrontendConfig.accesslogbuffering, CONFIG_ON_OFF, NULL},
+ {CONFIG_CSNLOGGING_ATTRIBUTE, config_set_csnlogging,
+ NULL, 0,
+ (void**)&global_slapdFrontendConfig.csnlogging, CONFIG_ON_OFF, NULL},
+ {CONFIG_AUDITLOG_LOGEXPIRATIONTIMEUNIT_ATTRIBUTE, NULL,
+ log_set_expirationtimeunit, SLAPD_AUDIT_LOG,
+ (void**)&global_slapdFrontendConfig.auditlog_exptimeunit,
+ CONFIG_STRING_OR_UNKNOWN, NULL},
+ {CONFIG_PW_SYNTAX_ATTRIBUTE, config_set_pw_syntax,
+ NULL, 0,
+ (void**)&global_slapdFrontendConfig.pw_policy.pw_syntax, CONFIG_ON_OFF, NULL},
+ {CONFIG_LISTENHOST_ATTRIBUTE, config_set_listenhost,
+ NULL, 0,
+ (void**)&global_slapdFrontendConfig.listenhost, CONFIG_STRING, NULL},
+ {CONFIG_ACCESSLOG_MINFREEDISKSPACE_ATTRIBUTE, NULL,
+ log_set_mindiskspace, SLAPD_ACCESS_LOG,
+ (void**)&global_slapdFrontendConfig.accesslog_minfreespace, CONFIG_INT, NULL},
+ {CONFIG_ERRORLOG_MAXNUMOFLOGSPERDIR_ATTRIBUTE, NULL,
+ log_set_numlogsperdir, SLAPD_ERROR_LOG,
+ (void**)&global_slapdFrontendConfig.errorlog_maxnumlogs, CONFIG_INT, NULL},
+ {CONFIG_SECURELISTENHOST_ATTRIBUTE, config_set_securelistenhost,
+ NULL, 0,
+ (void**)&global_slapdFrontendConfig.securelistenhost, CONFIG_STRING, NULL},
+ {CONFIG_AUDITLOG_MINFREEDISKSPACE_ATTRIBUTE, NULL,
+ log_set_mindiskspace, SLAPD_AUDIT_LOG,
+ (void**)&global_slapdFrontendConfig.auditlog_minfreespace, CONFIG_INT, NULL},
+ {CONFIG_ROOTDN_ATTRIBUTE, config_set_rootdn,
+ NULL, 0,
+ (void**)&global_slapdFrontendConfig.rootdn, CONFIG_STRING, NULL},
+ {CONFIG_PW_MINAGE_ATTRIBUTE, config_set_pw_minage,
+ NULL, 0,
+ (void**)&global_slapdFrontendConfig.pw_policy.pw_minage, CONFIG_LONG, NULL},
+ {CONFIG_AUDITFILE_ATTRIBUTE, config_set_auditlog,
+ NULL, 0,
+ (void**)&global_slapdFrontendConfig.auditlog, CONFIG_STRING_OR_EMPTY, NULL},
+ {CONFIG_RETURN_EXACT_CASE_ATTRIBUTE, config_set_return_exact_case,
+ NULL, 0,
+ (void**)&global_slapdFrontendConfig.return_exact_case, CONFIG_ON_OFF, NULL},
+ {CONFIG_RESULT_TWEAK_ATTRIBUTE, config_set_result_tweak,
+ NULL, 0,
+ (void**)&global_slapdFrontendConfig.result_tweak, CONFIG_ON_OFF, NULL},
+ {CONFIG_ATTRIBUTE_NAME_EXCEPTION_ATTRIBUTE, config_set_attrname_exceptions,
+ NULL, 0,
+ (void**)&global_slapdFrontendConfig.attrname_exceptions, CONFIG_ON_OFF, NULL},
+ {CONFIG_MAXBERSIZE_ATTRIBUTE, config_set_maxbersize,
+ NULL, 0,
+ (void**)&global_slapdFrontendConfig.maxbersize, CONFIG_INT, NULL},
+ {CONFIG_VERSIONSTRING_ATTRIBUTE, config_set_versionstring,
+ NULL, 0,
+ (void**)&global_slapdFrontendConfig.versionstring, CONFIG_STRING, NULL},
+ {CONFIG_REFERRAL_MODE_ATTRIBUTE, config_set_referral_mode,
+ NULL, 0,
+ (void**)&global_slapdFrontendConfig.refer_url, CONFIG_STRING, NULL},
+#if !defined(_WIN32) && !defined(AIX)
+ {CONFIG_MAXDESCRIPTORS_ATTRIBUTE, config_set_maxdescriptors,
+ NULL, 0,
+ (void**)&global_slapdFrontendConfig.maxdescriptors, CONFIG_INT, NULL},
+#endif
+ {CONFIG_CONNTABLESIZE_ATTRIBUTE, config_set_conntablesize,
+ NULL, 0,
+ (void**)&global_slapdFrontendConfig.conntablesize, CONFIG_INT, NULL},
+ {CONFIG_SSLCLIENTAUTH_ATTRIBUTE, config_set_SSLclientAuth,
+ NULL, 0,
+ (void **)&global_slapdFrontendConfig.SSLclientAuth, CONFIG_SPECIAL_SSLCLIENTAUTH, NULL},
+ {CONFIG_SSL_CHECK_HOSTNAME_ATTRIBUTE, config_set_ssl_check_hostname,
+ NULL, 0, NULL, CONFIG_ON_OFF, (ConfigGetFunc)config_get_ssl_check_hostname},
+ {CONFIG_CONFIG_ATTRIBUTE, 0, NULL, 0, (void**)SLAPD_CONFIG_DN,
+ CONFIG_CONSTANT_STRING, NULL},
+ {CONFIG_HASH_FILTERS_ATTRIBUTE, config_set_hash_filters,
+ NULL, 0, NULL, CONFIG_ON_OFF, (ConfigGetFunc)config_get_hash_filters},
+ {CONFIG_INSTANCEDIR_ATTRIBUTE, NULL /* read only */,
+ NULL, 0,
+ (void**)&global_slapdFrontendConfig.instancedir, CONFIG_STRING, NULL},
+ {CONFIG_REWRITE_RFC1274_ATTRIBUTE, config_set_rewrite_rfc1274,
+ NULL, 0,
+ (void**)&global_slapdFrontendConfig.rewrite_rfc1274, CONFIG_ON_OFF, NULL},
+ {CONFIG_OUTBOUND_LDAP_IO_TIMEOUT_ATTRIBUTE,
+ config_set_outbound_ldap_io_timeout,
+ NULL, 0,
+ (void **)&global_slapdFrontendConfig.outbound_ldap_io_timeout,
+ CONFIG_INT, NULL}
+};
+
+/*
+ * hashNocaseString - used for case insensitive hash lookups
+ */
+static PLHashNumber
+hashNocaseString(const void *key)
+{
+ PLHashNumber h = 0;
+ const unsigned char *s;
+
+ for (s = key; *s; s++)
+ h = (h >> 28) ^ (h << 4) ^ (tolower(*s));
+ return h;
+}
+
+/*
+ * hashNocaseCompare - used for case insensitive hash key comparisons
+ */
+static PRIntn
+hashNocaseCompare(const void *v1, const void *v2)
+{
+ return (strcasecmp((char *)v1, (char *)v2) == 0);
+}
+
+static PLHashTable *confighash = 0;
+
+static void
+init_config_get_and_set()
+{
+ if (!confighash) {
+ int ii = 0;
+ int tablesize = sizeof(ConfigList)/sizeof(ConfigList[0]);
+ confighash = PL_NewHashTable(tablesize+1, hashNocaseString,
+ hashNocaseCompare,
+ PL_CompareValues, 0, 0);
+ for (ii = 0; ii < tablesize; ++ii) {
+ if (PL_HashTableLookup(confighash, ConfigList[ii].attr_name))
+ printf("error: %s is already in the list\n",
+ ConfigList[ii].attr_name);
+ if (!PL_HashTableAdd(confighash, ConfigList[ii].attr_name, &ConfigList[ii]))
+ printf("error: could not add %s to the list\n",
+ ConfigList[ii].attr_name);
+ }
+ }
+}
+
+#if 0
+#define GOLDEN_RATIO 0x9E3779B9U
+
+PR_IMPLEMENT(PLHashEntry **)
+PL_HashTableRawLookup(PLHashTable *ht, PLHashNumber keyHash, const void *key)
+{
+ PLHashEntry *he, **hep, **hep0;
+ PLHashNumber h;
+
+#ifdef HASHMETER
+ ht->nlookups++;
+#endif
+ h = keyHash * GOLDEN_RATIO;
+ h >>= ht->shift;
+ hep = hep0 = &ht->buckets[h];
+ while ((he = *hep) != 0) {
+ if (he->keyHash == keyHash && (*ht->keyCompare)(key, he->key)) {
+ /* Move to front of chain if not already there */
+ if (hep != hep0) {
+ *hep = he->next;
+ he->next = *hep0;
+ *hep0 = he;
+ }
+ return hep0;
+ }
+ hep = &he->next;
+#ifdef HASHMETER
+ ht->nsteps++;
+#endif
+ }
+ return hep;
+}
+
+static void
+debugHashTable(const char *key)
+{
+ int ii = 0;
+ PLHashEntry **hep = PL_HashTableRawLookup(confighash, hashNocaseString(key),
+ key);
+ if (!hep || !*hep)
+ printf("raw lookup failed for %s\n", key);
+ else if (hep && *hep)
+ printf("raw lookup found %s -> %ul %s\n", key, (*hep)->keyHash, (*hep)->key);
+
+ printf("hash table has %d entries\n", confighash->nentries);
+ for (ii = 0; ii < confighash->nentries; ++ii)
+ {
+ PLHashEntry *he = confighash->buckets[ii];
+ if (!he)
+ printf("hash table entry %d is null\n", ii);
+ else {
+ printf("hash bucket %d:\n", ii);
+ while (he) {
+ int keys = !hashNocaseCompare(key, he->key);
+ int hash = (hashNocaseString(key) == he->keyHash);
+ printf("\thashval = %ul key = %s\n", he->keyHash, he->key);
+ if (keys && hash) {
+ printf("\t\tFOUND\n");
+ } else if (keys) {
+ printf("\t\tkeys match but hash vals do not\n");
+ } else if (hash) {
+ printf("\t\thash match but keys do not\n");
+ }
+ he = he->next;
+ }
+ }
+ }
+}
+#endif
+
+static void
+bervalarray_free(struct berval **bvec)
+{
+ int ii = 0;
+ for(ii = 0; bvec && bvec[ii]; ++ii) {
+ slapi_ch_free((void **)&bvec[ii]->bv_val);
+ slapi_ch_free((void **)&bvec[ii]);
+ }
+ slapi_ch_free((void**)&bvec);
+}
+
+static struct berval **
+strarray2bervalarray(const char **strarray)
+{
+ int ii = 0;
+ struct berval **newlist = 0;
+
+ /* first, count the number of items in the list */
+ for (ii = 0; strarray && strarray[ii]; ++ii);
+
+ /* if no items, return null */
+ if (!ii)
+ return newlist;
+
+ /* allocate the list */
+ newlist = (struct berval **)slapi_ch_malloc((ii+1) * sizeof(struct berval *));
+ newlist[ii] = 0;
+ for (; ii; --ii) {
+ newlist[ii-1] = (struct berval *)slapi_ch_malloc(sizeof(struct berval));
+ newlist[ii-1]->bv_val = slapi_ch_strdup(strarray[ii-1]);
+ newlist[ii-1]->bv_len = strlen(strarray[ii-1]);
+ }
+
+ return newlist;
+}
+
+/*
+** Setting this flag forces the server to shutdown.
+*/
+static int slapd_shutdown;
+
+void g_set_shutdown( int reason )
+{
+ slapd_shutdown = reason;
+}
+
+int g_get_shutdown()
+{
+ return slapd_shutdown;
+}
+
+
+static int cmd_shutdown;
+
+void c_set_shutdown()
+{
+ cmd_shutdown = SLAPI_SHUTDOWN_SIGNAL;
+}
+
+int c_get_shutdown()
+{
+ return cmd_shutdown;
+}
+
+slapdFrontendConfig_t *
+getFrontendConfig()
+{
+ return &global_slapdFrontendConfig;
+}
+
+/*
+ * FrontendConfig_init:
+ * Put all default values for config stuff here.
+ * If there's no default value, the value will be NULL if it's not set in dse.ldif
+ */
+
+void
+FrontendConfig_init () {
+ slapdFrontendConfig_t *cfg = getFrontendConfig();
+
+ /* initialize the read/write configuration lock */
+ if ( (cfg->cfg_rwlock = rwl_new()) == NULL ) {
+ LDAPDebug ( LDAP_DEBUG_ANY,
+ "FrontendConfig_init: failed to initialize cfg_rwlock. Exiting now.",
+ 0,0,0 );
+ exit(-1);
+ }
+
+ cfg->port = LDAP_PORT;
+ cfg->secureport = LDAPS_PORT;
+ cfg->threadnumber = SLAPD_DEFAULT_MAX_THREADS;
+ cfg->maxthreadsperconn = SLAPD_DEFAULT_MAX_THREADS_PER_CONN;
+ cfg->reservedescriptors = SLAPD_DEFAULT_RESERVE_FDS;
+ cfg->idletimeout = SLAPD_DEFAULT_IDLE_TIMEOUT;
+ cfg->ioblocktimeout = SLAPD_DEFAULT_IOBLOCK_TIMEOUT;
+ cfg->outbound_ldap_io_timeout = SLAPD_DEFAULT_OUTBOUND_LDAP_IO_TIMEOUT;
+ cfg->max_filter_nest_level = SLAPD_DEFAULT_MAX_FILTER_NEST_LEVEL;
+
+#ifdef _WIN32
+ cfg->conntablesize = SLAPD_DEFAULT_CONNTABLESIZE;
+#else
+#ifdef USE_SYSCONF
+ cfg->conntablesize = sysconf( _SC_OPEN_MAX );
+#else /* USE_SYSCONF */
+ cfg->conntablesize = getdtablesize();
+#endif /* USE_SYSCONF */
+#endif /* _WIN32 */
+
+ cfg->accesscontrol = LDAP_ON;
+ cfg->security = LDAP_OFF;
+ cfg->ssl_check_hostname = LDAP_ON;
+ cfg->return_exact_case = LDAP_ON;
+ cfg->result_tweak = LDAP_OFF;
+ cfg->reservedescriptors = SLAPD_DEFAULT_RESERVE_FDS;
+ cfg->useroc = slapi_ch_strdup ( "" );
+ cfg->userat = slapi_ch_strdup ( "" );
+/* kexcoff: should not be initialized by default here
+ cfg->rootpwstoragescheme = pw_name2scheme( SHA1_SCHEME_NAME );
+ cfg->pw_storagescheme = pw_name2scheme( SHA1_SCHEME_NAME );
+*/
+ cfg->slapd_type = 0;
+ cfg->versionstring = SLAPD_VERSION_STR;
+ cfg->sizelimit = SLAPD_DEFAULT_SIZELIMIT;
+ cfg->timelimit = SLAPD_DEFAULT_TIMELIMIT;
+ cfg->schemacheck = LDAP_ON;
+ cfg->ds4_compatible_schema = LDAP_OFF;
+ cfg->enquote_sup_oc = LDAP_OFF;
+ cfg->lastmod = LDAP_ON;
+ cfg->rewrite_rfc1274 = LDAP_OFF;
+ cfg->schemareplace = slapi_ch_strdup( CONFIG_SCHEMAREPLACE_STR_REPLICATION_ONLY );
+ cfg->schema_ignore_trailing_spaces = SLAPD_DEFAULT_SCHEMA_IGNORE_TRAILING_SPACES;
+
+ cfg->pwpolicy_local = LDAP_OFF;
+ cfg->pw_policy.pw_change = LDAP_ON;
+ cfg->pw_policy.pw_must_change = LDAP_OFF;
+ cfg->pw_policy.pw_syntax = LDAP_OFF;
+ cfg->pw_policy.pw_exp = LDAP_OFF;
+ cfg->pw_policy.pw_minlength = 6;
+ cfg->pw_policy.pw_maxage = 8640000; /* 100 days */
+ cfg->pw_policy.pw_minage = 0;
+ cfg->pw_policy.pw_warning = 86400; /* 1 day */
+ cfg->pw_policy.pw_history = LDAP_OFF;
+ cfg->pw_policy.pw_inhistory = 6;
+ cfg->pw_policy.pw_lockout = LDAP_OFF;
+ cfg->pw_policy.pw_maxfailure = 3;
+ cfg->pw_policy.pw_unlock = LDAP_ON;
+ cfg->pw_policy.pw_lockduration = 3600; /* 60 minutes */
+ cfg->pw_policy.pw_resetfailurecount = 600; /* 10 minutes */
+ cfg->pw_policy.pw_gracelimit = 0;
+ cfg->pw_is_global_policy = LDAP_OFF;
+
+ cfg->accesslog_logging_enabled = LDAP_ON;
+ cfg->accesslog_mode = slapi_ch_strdup("600");
+ cfg->accesslog_maxnumlogs = 10;
+ cfg->accesslog_maxlogsize = 100;
+ cfg->accesslog_rotationtime = 1;
+ cfg->accesslog_rotationunit = slapi_ch_strdup("day");
+ cfg->accesslog_rotationsync_enabled = LDAP_OFF;
+ cfg->accesslog_rotationsynchour = 0;
+ cfg->accesslog_rotationsyncmin = 0;
+ cfg->accesslog_maxdiskspace = 500;
+ cfg->accesslog_minfreespace = 5;
+ cfg->accesslog_exptime = 1;
+ cfg->accesslog_exptimeunit = slapi_ch_strdup("month");
+ cfg->accessloglevel = 256;
+ cfg->accesslogbuffering = LDAP_ON;
+ cfg->csnlogging = LDAP_ON;
+
+ cfg->errorlog_logging_enabled = LDAP_ON;
+ cfg->errorlog_mode = slapi_ch_strdup("600");
+ cfg->errorlog_maxnumlogs = 1;
+ cfg->errorlog_maxlogsize = 100;
+ cfg->errorlog_rotationtime = 1;
+ cfg->errorlog_rotationunit = slapi_ch_strdup ("week");
+ cfg->errorlog_rotationsync_enabled = LDAP_OFF;
+ cfg->errorlog_rotationsynchour = 0;
+ cfg->errorlog_rotationsyncmin = 0;
+ cfg->errorlog_maxdiskspace = 100;
+ cfg->errorlog_minfreespace = 5;
+ cfg->errorlog_exptime = 1;
+ cfg->errorlog_exptimeunit = slapi_ch_strdup("month");
+ cfg->errorloglevel = 0;
+
+ cfg->auditlog_logging_enabled = LDAP_OFF;
+ cfg->auditlog_mode = slapi_ch_strdup("600");
+ cfg->auditlog_maxnumlogs = 1;
+ cfg->auditlog_maxlogsize = 100;
+ cfg->auditlog_rotationtime = 1;
+ cfg->auditlog_rotationunit = slapi_ch_strdup ("week");
+ cfg->auditlog_rotationsync_enabled = LDAP_OFF;
+ cfg->auditlog_rotationsynchour = 0;
+ cfg->auditlog_rotationsyncmin = 0;
+ cfg->auditlog_maxdiskspace = 100;
+ cfg->auditlog_minfreespace = 5;
+ cfg->auditlog_exptime = 1;
+ cfg->auditlog_exptimeunit = slapi_ch_strdup("month");
+
+ init_config_get_and_set();
+}
+
+int
+g_get_global_lastmod()
+{
+ return config_get_lastmod();
+}
+
+
+int g_get_slapd_security_on(){
+ return config_get_security();
+}
+
+
+
+#ifdef _WIN32
+void libldap_init_debug_level(int *val_ptr)
+{
+ module_ldap_debug = val_ptr;
+}
+#endif
+
+struct snmp_vars_t global_snmp_vars;
+
+struct snmp_vars_t * g_get_global_snmp_vars(){
+ return &global_snmp_vars;
+}
+
+static slapdEntryPoints *sep = NULL;
+void
+set_dll_entry_points( slapdEntryPoints *p )
+{
+ if ( NULL == sep )
+ {
+ sep = p;
+ }
+}
+
+
+int
+get_entry_point( int ep_name, caddr_t *ep_addr )
+{
+ int rc = 0;
+
+ if(sep!=NULL)
+ {
+ switch ( ep_name ) {
+ case ENTRY_POINT_PS_WAKEUP_ALL:
+ *ep_addr = sep->sep_ps_wakeup_all;
+ break;
+ case ENTRY_POINT_PS_SERVICE:
+ *ep_addr = sep->sep_ps_service;
+ break;
+ case ENTRY_POINT_DISCONNECT_SERVER:
+ *ep_addr = sep->sep_disconnect_server;
+ break;
+ case ENTRY_POINT_SLAPD_SSL_CLIENT_INIT:
+ *ep_addr = sep->sep_slapd_SSL_client_init;
+ break;
+ case ENTRY_POINT_SLAPD_SSL_INIT:
+ *ep_addr = sep->sep_slapd_ssl_init;
+ break;
+ case ENTRY_POINT_SLAPD_SSL_INIT2:
+ *ep_addr = sep->sep_slapd_ssl_init2;
+ break;
+ default:
+ rc = -1;
+ }
+ }
+ else
+ {
+ rc= -1;
+ }
+ return rc;
+}
+
+
+/*
+ * Utility function called by many of the config_set_XXX() functions.
+ * Returns a non-zero value if 'value' is NULL and zero if not.
+ * Also constructs an error message in 'errorbuf' if value is NULL.
+ * If or_zero_length is non-zero, zero length values are treated as
+ * equivalent to NULL (i.e., they will cause a non-zero value to be
+ * returned by this function).
+ */
+static int
+config_value_is_null( const char *attrname, const char *value, char *errorbuf,
+ int or_zero_length )
+{
+ if ( NULL == value || ( or_zero_length && *value == '\0' )) {
+ PR_snprintf( errorbuf, SLAPI_DSE_RETURNTEXT_SIZE, "%s: NULL value",
+ attrname );
+ return 1;
+ }
+
+ return 0;
+}
+
+
+
+int
+config_set_port( const char *attrname, char *port, char *errorbuf, int apply ) {
+ int nPort;
+ slapdFrontendConfig_t *slapdFrontendConfig = getFrontendConfig();
+ int retVal = LDAP_SUCCESS;
+
+ if ( config_value_is_null( attrname, port, errorbuf, 0 )) {
+ return LDAP_OPERATIONS_ERROR;
+ }
+
+ nPort = atoi( port );
+
+ if ( nPort == 0 ) {
+ LDAPDebug( LDAP_DEBUG_ANY,
+ "Information: Non-Secure Port Disabled, server only contactable via secure port\n", 0, 0, 0 );
+ }
+ else if (nPort > LDAP_PORT_MAX || nPort < 0 ) {
+ retVal = LDAP_OPERATIONS_ERROR;
+ PR_snprintf ( errorbuf, SLAPI_DSE_RETURNTEXT_SIZE,
+ "%s: %d is invalid, ports must range from 1 to %d",
+ attrname, nPort, LDAP_PORT_MAX );
+ }
+
+ if ( apply ) {
+ CFG_LOCK_WRITE(slapdFrontendConfig);
+
+ slapdFrontendConfig->port = nPort;
+ /* n_port = nPort; */
+
+ CFG_UNLOCK_WRITE(slapdFrontendConfig);
+ }
+ return retVal;
+}
+
+int
+config_set_secureport( const char *attrname, char *port, char *errorbuf, int apply ) {
+ int nPort = atoi ( port );
+ slapdFrontendConfig_t *slapdFrontendConfig = getFrontendConfig();
+ int retVal = LDAP_SUCCESS;
+
+ if ( config_value_is_null( attrname, port, errorbuf, 0 )) {
+ return LDAP_OPERATIONS_ERROR;
+ }
+
+ if (nPort > LDAP_PORT_MAX || nPort <= 0 ) {
+ retVal = LDAP_OPERATIONS_ERROR;
+ PR_snprintf ( errorbuf, SLAPI_DSE_RETURNTEXT_SIZE,
+ "%s: %d is invalid, ports must range from 1 to %d",
+ attrname, nPort, LDAP_PORT_MAX );
+ }
+
+ if (apply) {
+ CFG_LOCK_WRITE(slapdFrontendConfig);
+
+ slapdFrontendConfig->secureport = nPort;
+
+ CFG_UNLOCK_WRITE(slapdFrontendConfig);
+ }
+ return retVal;
+}
+
+
+int
+config_set_SSLclientAuth( const char *attrname, char *value, char *errorbuf, int apply ) {
+
+ int retVal = LDAP_SUCCESS;
+ slapdFrontendConfig_t *slapdFrontendConfig = getFrontendConfig();
+
+ if ( config_value_is_null( attrname, value, errorbuf, 0 )) {
+ retVal = LDAP_OPERATIONS_ERROR;
+ }
+ /* first check the value, return an error if it's invalid */
+ else if ( strcasecmp (value, "off") != 0 &&
+ strcasecmp (value, "allowed") != 0 &&
+ strcasecmp (value, "required")!= 0 ) {
+ retVal = LDAP_OPERATIONS_ERROR;
+ if( errorbuf )
+ PR_snprintf ( errorbuf, SLAPI_DSE_RETURNTEXT_SIZE,
+ "%s: unsupported value: %s", attrname, value );
+ return retVal;
+ }
+ else if ( !apply ) {
+ /* return success now, if we aren't supposed to apply the change */
+ return retVal;
+ }
+
+ CFG_LOCK_WRITE(slapdFrontendConfig);
+
+ if ( !strcasecmp( value, "off" )) {
+ slapdFrontendConfig->SSLclientAuth = SLAPD_SSLCLIENTAUTH_OFF;
+ }
+ else if ( !strcasecmp( value, "allowed" )) {
+ slapdFrontendConfig->SSLclientAuth = SLAPD_SSLCLIENTAUTH_ALLOWED;
+ }
+ else if ( !strcasecmp( value, "required" )) {
+ slapdFrontendConfig->SSLclientAuth = SLAPD_SSLCLIENTAUTH_REQUIRED;
+ }
+ else {
+ retVal = LDAP_OPERATIONS_ERROR;
+ if( errorbuf )
+ PR_snprintf ( errorbuf, SLAPI_DSE_RETURNTEXT_SIZE,
+ "%s: unsupported value: %s", attrname, value );
+ }
+
+ CFG_UNLOCK_WRITE(slapdFrontendConfig);
+
+ return retVal;
+}
+
+
+int
+config_set_ssl_check_hostname(const char *attrname, char *value,
+ char *errorbuf, int apply)
+{
+ int retVal = LDAP_SUCCESS;
+ slapdFrontendConfig_t *slapdFrontendConfig = getFrontendConfig();
+
+ retVal = config_set_onoff(attrname,
+ value,
+ &(slapdFrontendConfig->ssl_check_hostname),
+ errorbuf,
+ apply);
+
+ return retVal;
+}
+
+int
+config_set_localhost( const char *attrname, char *value, char *errorbuf, int apply ) {
+ int retVal = LDAP_SUCCESS;
+ slapdFrontendConfig_t *slapdFrontendConfig = getFrontendConfig();
+
+ if ( config_value_is_null( attrname, value, errorbuf, 0 )) {
+ return LDAP_OPERATIONS_ERROR;
+ }
+
+ if (apply) {
+ CFG_LOCK_WRITE(slapdFrontendConfig);
+
+ slapi_ch_free ( (void **) &(slapdFrontendConfig->localhost) );
+ slapdFrontendConfig->localhost = slapi_ch_strdup ( value );
+
+ CFG_UNLOCK_WRITE(slapdFrontendConfig);
+ }
+ return retVal;
+}
+
+int
+config_set_listenhost( const char *attrname, char *value, char *errorbuf, int apply ) {
+ int retVal = LDAP_SUCCESS;
+ slapdFrontendConfig_t *slapdFrontendConfig = getFrontendConfig();
+
+ if ( config_value_is_null( attrname, value, errorbuf, 0 )) {
+ return LDAP_OPERATIONS_ERROR;
+ }
+
+ if ( apply) {
+ CFG_LOCK_WRITE(slapdFrontendConfig);
+
+ slapi_ch_free ( (void **) &(slapdFrontendConfig->listenhost) );
+ slapdFrontendConfig->listenhost = slapi_ch_strdup ( value );
+
+ CFG_UNLOCK_WRITE(slapdFrontendConfig);
+ }
+ return retVal;
+}
+
+int
+config_set_securelistenhost( const char *attrname, char *value, char *errorbuf, int apply ) {
+ int retVal = LDAP_SUCCESS;
+ slapdFrontendConfig_t *slapdFrontendConfig = getFrontendConfig();
+
+ if ( config_value_is_null( attrname, value, errorbuf, 0 )) {
+ return LDAP_OPERATIONS_ERROR;
+ }
+
+ if ( apply ) {
+ CFG_LOCK_WRITE(slapdFrontendConfig);
+
+ slapi_ch_free ( (void **) &(slapdFrontendConfig->securelistenhost) );
+ slapdFrontendConfig->securelistenhost = slapi_ch_strdup ( value );
+
+ CFG_UNLOCK_WRITE(slapdFrontendConfig);
+ }
+ return retVal;
+}
+
+int
+config_set_srvtab( const char *attrname, char *value, char *errorbuf, int apply ) {
+ int retVal = LDAP_SUCCESS;
+ slapdFrontendConfig_t *slapdFrontendConfig = getFrontendConfig();
+
+
+ if ( config_value_is_null( attrname, value, errorbuf, 0 )) {
+ return LDAP_OPERATIONS_ERROR;
+ }
+
+ if (apply) {
+ CFG_LOCK_WRITE(slapdFrontendConfig);
+
+ slapi_ch_free ( (void **) &(slapdFrontendConfig->srvtab) );
+ ldap_srvtab = slapi_ch_strdup ( value );
+ slapdFrontendConfig->srvtab = slapi_ch_strdup ( value );
+
+ CFG_UNLOCK_WRITE(slapdFrontendConfig);
+ }
+ return retVal;
+}
+
+int
+config_set_sizelimit( const char *attrname, char *value, char *errorbuf, int apply ) {
+ int retVal = LDAP_SUCCESS;
+ Slapi_Backend *be;
+ char *cookie;
+ slapdFrontendConfig_t *slapdFrontendConfig = getFrontendConfig();
+
+ int sizelimit = atoi ( value );
+
+ if ( config_value_is_null( attrname, value, errorbuf, 0 )) {
+ return LDAP_OPERATIONS_ERROR;
+ }
+
+ if ( sizelimit < -1 ) {
+ PR_snprintf ( errorbuf, SLAPI_DSE_RETURNTEXT_SIZE, "%s: %d is too small",
+ attrname, sizelimit );
+ retVal = LDAP_OPERATIONS_ERROR;
+ return retVal;
+ }
+
+ if (apply) {
+
+ CFG_LOCK_WRITE(slapdFrontendConfig);
+
+ slapdFrontendConfig->sizelimit= sizelimit;
+ g_set_defsize (sizelimit);
+ cookie = NULL;
+ be = slapi_get_first_backend(&cookie);
+ while (be) {
+ be->be_sizelimit = slapdFrontendConfig->sizelimit;
+ be = slapi_get_next_backend(cookie);
+ }
+
+ CFG_UNLOCK_WRITE(slapdFrontendConfig);
+ slapi_ch_free ((void **)&cookie);
+
+ }
+ return retVal;
+}
+
+int
+config_set_pw_storagescheme( const char *attrname, char *value, char *errorbuf, int apply ) {
+ int retVal = LDAP_SUCCESS;
+ slapdFrontendConfig_t *slapdFrontendConfig = getFrontendConfig();
+ struct pw_scheme *new_scheme = NULL;
+ char * scheme_list = NULL;
+
+ if ( config_value_is_null( attrname, value, errorbuf, 1 )) {
+ return LDAP_OPERATIONS_ERROR;
+ }
+
+ scheme_list = plugin_get_pwd_storage_scheme_list(PLUGIN_LIST_PWD_STORAGE_SCHEME);
+
+ new_scheme = pw_name2scheme(value);
+ if ( new_scheme == NULL) {
+ if ( scheme_list != NULL ) {
+ PR_snprintf ( errorbuf, SLAPI_DSE_RETURNTEXT_SIZE,
+ "%s: invalid scheme - %s. Valid schemes are: %s",
+ attrname, value, scheme_list );
+ } else {
+ PR_snprintf ( errorbuf, SLAPI_DSE_RETURNTEXT_SIZE,
+ "%s: invalid scheme - %s (no pwdstorage scheme"
+ " plugin loaded)",
+ attrname, value);
+ }
+ retVal = LDAP_OPERATIONS_ERROR;
+ slapi_ch_free_string(&scheme_list);
+ return retVal;
+ }
+ else if ( new_scheme->pws_enc == NULL )
+ {
+ /* For example: the NS-MTA-MD5 password scheme is for comparision only and for backward
+ compatibility with an Old Messaging Server that was setting passwords in the
+ directory already encrypted. The scheme cannot and don't encrypt password if
+ they are in clear. We don't take it */
+
+ if ( scheme_list != NULL ) {
+ sprintf( errorbuf,
+ "pw_storagescheme: invalid encoding scheme - %s\nValid values are: %s\n", value, scheme_list );
+ }
+ retVal = LDAP_UNWILLING_TO_PERFORM;
+ slapi_ch_free_string(&scheme_list);
+ return retVal;
+ }
+
+ if ( apply ) {
+ CFG_LOCK_WRITE(slapdFrontendConfig);
+
+ free_pw_scheme(slapdFrontendConfig->pw_storagescheme);
+ slapdFrontendConfig->pw_storagescheme = new_scheme;
+
+ CFG_UNLOCK_WRITE(slapdFrontendConfig);
+ }
+ slapi_ch_free_string(&scheme_list);
+
+ return retVal;
+}
+
+
+int
+config_set_pw_change( const char *attrname, char *value, char *errorbuf, int apply ) {
+ int retVal = LDAP_SUCCESS;
+ slapdFrontendConfig_t *slapdFrontendConfig = getFrontendConfig();
+
+ retVal = config_set_onoff ( attrname,
+ value,
+ &(slapdFrontendConfig->pw_policy.pw_change),
+ errorbuf,
+ apply);
+
+ if (retVal == LDAP_SUCCESS) {
+ /* LP: Update ACI to reflect the value ! */
+ if (apply)
+ pw_mod_allowchange_aci(!slapdFrontendConfig->pw_policy.pw_change &&
+ !slapdFrontendConfig->pw_policy.pw_must_change);
+ }
+
+ return retVal;
+}
+
+
+int
+config_set_pw_history( const char *attrname, char *value, char *errorbuf, int apply ) {
+ int retVal = LDAP_SUCCESS;
+ slapdFrontendConfig_t *slapdFrontendConfig = getFrontendConfig();
+
+ retVal = config_set_onoff ( attrname,
+ value,
+ &(slapdFrontendConfig->pw_policy.pw_history),
+ errorbuf,
+ apply);
+
+ return retVal;
+}
+
+
+
+int
+config_set_pw_must_change( const char *attrname, char *value, char *errorbuf, int apply ) {
+ int retVal = LDAP_SUCCESS;
+ slapdFrontendConfig_t *slapdFrontendConfig = getFrontendConfig();
+
+ retVal = config_set_onoff ( attrname,
+ value,
+ &(slapdFrontendConfig->pw_policy.pw_must_change),
+ errorbuf,
+ apply);
+
+ if (retVal == LDAP_SUCCESS) {
+ /* LP: Update ACI to reflect the value ! */
+ if (apply)
+ pw_mod_allowchange_aci(!slapdFrontendConfig->pw_policy.pw_change &&
+ !slapdFrontendConfig->pw_policy.pw_must_change);
+ }
+
+ return retVal;
+}
+
+
+int
+config_set_pwpolicy_local( const char *attrname, char *value, char *errorbuf, int apply ) {
+ int retVal = LDAP_SUCCESS;
+ slapdFrontendConfig_t *slapdFrontendConfig = getFrontendConfig();
+
+ retVal = config_set_onoff ( attrname,
+ value,
+ &(slapdFrontendConfig->pwpolicy_local),
+ errorbuf,
+ apply);
+
+ return retVal;
+}
+
+int
+config_set_pw_syntax( const char *attrname, char *value, char *errorbuf, int apply ) {
+ int retVal = LDAP_SUCCESS;
+ slapdFrontendConfig_t *slapdFrontendConfig = getFrontendConfig();
+
+ retVal = config_set_onoff ( attrname,
+ value,
+ &(slapdFrontendConfig->pw_policy.pw_syntax),
+ errorbuf,
+ apply);
+
+ return retVal;
+}
+
+
+
+int
+config_set_pw_minlength( const char *attrname, char *value, char *errorbuf, int apply ) {
+ int retVal = LDAP_SUCCESS, minLength = 0;
+ slapdFrontendConfig_t *slapdFrontendConfig = getFrontendConfig();
+
+ if ( config_value_is_null( attrname, value, errorbuf, 0 )) {
+ return LDAP_OPERATIONS_ERROR;
+ }
+
+ minLength = atoi(value);
+ if ( minLength < 2 || minLength > 512 ) {
+ PR_snprintf ( errorbuf, SLAPI_DSE_RETURNTEXT_SIZE,
+ "password minimum length \"%s\" is invalid. "
+ "The minimum length must range from 2 to 512.",
+ value );
+ retVal = LDAP_OPERATIONS_ERROR;
+ return retVal;
+ }
+
+ if ( apply ) {
+ CFG_LOCK_WRITE(slapdFrontendConfig);
+
+ slapdFrontendConfig->pw_policy.pw_minlength = minLength;
+
+ CFG_UNLOCK_WRITE(slapdFrontendConfig);
+ }
+
+ return retVal;
+}
+
+int
+config_set_pw_maxfailure( const char *attrname, char *value, char *errorbuf, int apply ) {
+ int retVal = LDAP_SUCCESS, maxFailure = 0;
+ slapdFrontendConfig_t *slapdFrontendConfig = getFrontendConfig();
+
+ if ( config_value_is_null( attrname, value, errorbuf, 0 )) {
+ return LDAP_OPERATIONS_ERROR;
+ }
+
+ maxFailure = atoi(value);
+ if ( maxFailure <= 0 || maxFailure > 32767 ) {
+ PR_snprintf ( errorbuf, SLAPI_DSE_RETURNTEXT_SIZE,
+ "password maximum retry \"%s\" is invalid. "
+ "Password maximum failure must range from 1 to 32767",
+ value );
+ retVal = LDAP_OPERATIONS_ERROR;
+ return retVal;
+ }
+
+ if ( apply ) {
+ CFG_LOCK_WRITE(slapdFrontendConfig);
+
+ slapdFrontendConfig->pw_policy.pw_maxfailure = maxFailure;
+
+ CFG_UNLOCK_WRITE(slapdFrontendConfig);
+ }
+
+ return retVal;
+}
+
+
+
+int
+config_set_pw_inhistory( const char *attrname, char *value, char *errorbuf, int apply ) {
+ int retVal = LDAP_SUCCESS, history = 0;
+ slapdFrontendConfig_t *slapdFrontendConfig = getFrontendConfig();
+
+ if ( config_value_is_null( attrname, value, errorbuf, 0 )) {
+ return LDAP_OPERATIONS_ERROR;
+ }
+
+ history = atoi(value);
+ if ( history < 2 || history > 24 ) {
+ PR_snprintf ( errorbuf, SLAPI_DSE_RETURNTEXT_SIZE,
+ "password history length \"%s\" is invalid. "
+ "The password history must range from 2 to 24",
+ value );
+ retVal = LDAP_OPERATIONS_ERROR;
+ return retVal;
+ }
+
+ if ( apply ) {
+ CFG_LOCK_WRITE(slapdFrontendConfig);
+
+ slapdFrontendConfig->pw_policy.pw_inhistory = history;
+
+ CFG_UNLOCK_WRITE(slapdFrontendConfig);
+ }
+
+ return retVal;
+}
+
+
+int
+config_set_pw_lockduration( const char *attrname, char *value, char *errorbuf, int apply ) {
+ int retVal = LDAP_SUCCESS;
+ long duration = 0; /* in minutes */
+ slapdFrontendConfig_t *slapdFrontendConfig = getFrontendConfig();
+
+ if ( config_value_is_null( attrname, value, errorbuf, 0 )) {
+ return LDAP_OPERATIONS_ERROR;
+ }
+
+ /* in seconds */
+ duration = strtol (value, NULL, 0);
+
+ if ( duration <= 0 || duration > (MAX_ALLOWED_TIME_IN_SECS - current_time()) ) {
+ PR_snprintf ( errorbuf, SLAPI_DSE_RETURNTEXT_SIZE,
+ "password lockout duration \"%s\" seconds is invalid. ",
+ value );
+ retVal = LDAP_OPERATIONS_ERROR;
+ return retVal;
+ }
+
+ if ( apply ) {
+ slapdFrontendConfig->pw_policy.pw_lockduration = duration;
+ }
+
+ return retVal;
+}
+
+
+int
+config_set_pw_resetfailurecount( const char *attrname, char *value, char *errorbuf, int apply ) {
+ int retVal = LDAP_SUCCESS;
+ long duration = 0; /* in minutes */
+ slapdFrontendConfig_t *slapdFrontendConfig = getFrontendConfig();
+
+ if ( config_value_is_null( attrname, value, errorbuf, 0 )) {
+ return LDAP_OPERATIONS_ERROR;
+ }
+
+ /* in seconds */
+ duration = strtol (value, NULL, 0);
+ if ( duration < 0 || duration > (MAX_ALLOWED_TIME_IN_SECS - current_time()) ) {
+ PR_snprintf ( errorbuf, SLAPI_DSE_RETURNTEXT_SIZE,
+ "password reset count duration \"%s\" seconds is invalid. ",
+ value );
+ retVal = LDAP_OPERATIONS_ERROR;
+ return retVal;
+ }
+
+ if ( apply ) {
+ slapdFrontendConfig->pw_policy.pw_resetfailurecount = duration;
+ }
+
+ return retVal;
+}
+
+int
+config_set_pw_is_global_policy( const char *attrname, char *value, char *errorbuf, int apply ) {
+ int retVal = LDAP_SUCCESS;
+ slapdFrontendConfig_t *slapdFrontendConfig = getFrontendConfig();
+
+ retVal = config_set_onoff ( attrname,
+ value,
+ &(slapdFrontendConfig->pw_is_global_policy),
+ errorbuf,
+ apply);
+
+ return retVal;
+}
+
+int
+config_set_pw_exp( const char *attrname, char *value, char *errorbuf, int apply ) {
+ int retVal = LDAP_SUCCESS;
+ slapdFrontendConfig_t *slapdFrontendConfig = getFrontendConfig();
+
+ /* password policy is disabled in DirLite. */
+ if ( config_is_slapd_lite() ) {
+ if ( NULL != value && strcasecmp(value, "off") != 0 ) {
+ PR_snprintf ( errorbuf, SLAPI_DSE_RETURNTEXT_SIZE, LITE_PW_EXP_ERR );
+ retVal = LDAP_UNWILLING_TO_PERFORM;
+ return retVal;
+ }
+ }
+ retVal = config_set_onoff ( attrname,
+ value,
+ &(slapdFrontendConfig->pw_policy.pw_exp),
+ errorbuf,
+ apply);
+
+ return retVal;
+}
+
+int
+config_set_pw_unlock( const char *attrname, char *value, char *errorbuf, int apply ) {
+ int retVal = LDAP_SUCCESS;
+ slapdFrontendConfig_t *slapdFrontendConfig = getFrontendConfig();
+
+ retVal = config_set_onoff ( attrname,
+ value,
+ &(slapdFrontendConfig->pw_policy.pw_unlock),
+ errorbuf,
+ apply);
+
+ return retVal;
+}
+
+
+int
+config_set_pw_lockout( const char *attrname, char *value, char *errorbuf, int apply ) {
+ int retVal = LDAP_SUCCESS;
+ slapdFrontendConfig_t *slapdFrontendConfig = getFrontendConfig();
+
+ retVal = config_set_onoff ( attrname,
+ value,
+ &(slapdFrontendConfig->pw_policy.pw_lockout),
+ errorbuf,
+ apply);
+
+ return retVal;
+}
+
+
+int
+config_set_pw_gracelimit( const char *attrname, char *value, char *errorbuf, int apply ) {
+ int retVal = LDAP_SUCCESS, gracelimit = 0;
+ slapdFrontendConfig_t *slapdFrontendConfig = getFrontendConfig();
+
+ if ( config_value_is_null( attrname, value, errorbuf, 0 )) {
+ return LDAP_OPERATIONS_ERROR;
+ }
+
+ gracelimit = atoi(value);
+ if ( gracelimit < 0 ) {
+ PR_snprintf ( errorbuf, SLAPI_DSE_RETURNTEXT_SIZE,
+ "password grace limit \"%s\" is invalid.",
+ value );
+ retVal = LDAP_OPERATIONS_ERROR;
+ return retVal;
+ }
+
+ if ( apply ) {
+ CFG_LOCK_WRITE(slapdFrontendConfig);
+
+ slapdFrontendConfig->pw_policy.pw_gracelimit = gracelimit;
+
+ CFG_UNLOCK_WRITE(slapdFrontendConfig);
+ }
+
+ return retVal;
+}
+
+
+int
+config_set_lastmod( const char *attrname, char *value, char *errorbuf, int apply ) {
+ int retVal = LDAP_SUCCESS;
+ Slapi_Backend *be = NULL;
+ char *cookie;
+ slapdFrontendConfig_t *slapdFrontendConfig = getFrontendConfig();
+
+ retVal = config_set_onoff ( attrname,
+ value,
+ &(slapdFrontendConfig->lastmod),
+ errorbuf,
+ apply);
+
+ if ( retVal == LDAP_SUCCESS && apply ) {
+ CFG_LOCK_WRITE(slapdFrontendConfig);
+
+ cookie = NULL;
+ be = slapi_get_first_backend (&cookie);
+ while (be) {
+ be->be_lastmod = slapdFrontendConfig->lastmod;
+ be = slapi_get_next_backend (cookie);
+ }
+
+ CFG_UNLOCK_WRITE(slapdFrontendConfig);
+
+ slapi_ch_free ((void **)&cookie);
+
+ }
+
+ return retVal;
+}
+
+
+int
+config_set_nagle( const char *attrname, char *value, char *errorbuf, int apply ) {
+ int retVal = LDAP_SUCCESS;
+ slapdFrontendConfig_t *slapdFrontendConfig = getFrontendConfig();
+
+ retVal = config_set_onoff ( attrname,
+ value,
+ &(slapdFrontendConfig->nagle),
+ errorbuf,
+ apply);
+
+ return retVal;
+}
+
+
+int
+config_set_accesscontrol( const char *attrname, char *value, char *errorbuf, int apply ) {
+ int retVal = LDAP_SUCCESS;
+ slapdFrontendConfig_t *slapdFrontendConfig = getFrontendConfig();
+
+ retVal = config_set_onoff ( attrname,
+ value,
+ &(slapdFrontendConfig->accesscontrol),
+ errorbuf,
+ apply);
+
+ return retVal;
+}
+
+
+
+int
+config_set_return_exact_case( const char *attrname, char *value, char *errorbuf, int apply ) {
+ int retVal = LDAP_SUCCESS;
+ slapdFrontendConfig_t *slapdFrontendConfig = getFrontendConfig();
+
+ retVal = config_set_onoff ( attrname,
+ value,
+ &(slapdFrontendConfig->return_exact_case),
+ errorbuf,
+ apply);
+
+ return retVal;
+}
+
+
+int
+config_set_result_tweak( const char *attrname, char *value, char *errorbuf, int apply ) {
+ int retVal = LDAP_SUCCESS;
+ slapdFrontendConfig_t *slapdFrontendConfig = getFrontendConfig();
+
+ retVal = config_set_onoff ( attrname,
+ value,
+ &(slapdFrontendConfig->result_tweak),
+ errorbuf,
+ apply);
+
+ return retVal;
+}
+
+
+int
+config_set_security( const char *attrname, char *value, char *errorbuf, int apply ) {
+ int retVal = LDAP_SUCCESS;
+ slapdFrontendConfig_t *slapdFrontendConfig = getFrontendConfig();
+
+ retVal = config_set_onoff ( attrname,
+ value,
+ &(slapdFrontendConfig->security),
+ errorbuf,
+ apply);
+
+ return retVal;
+}
+
+
+static int
+config_set_onoff ( const char *attrname, char *value, int *configvalue,
+ char *errorbuf, int apply )
+{
+ int retVal = LDAP_SUCCESS;
+ slapdFrontendConfig_t *slapdFrontendConfig = getFrontendConfig();
+
+ if ( config_value_is_null( attrname, value, errorbuf, 0 )) {
+ return LDAP_OPERATIONS_ERROR;
+ }
+
+ if ( strcasecmp ( value, "on" ) != 0 &&
+ strcasecmp ( value, "off") != 0 ) {
+ PR_snprintf ( errorbuf, SLAPI_DSE_RETURNTEXT_SIZE,
+ "%s: invalid value \"%s\". Valid values are \"on\" or \"off\".",
+ attrname, value );
+ retVal = LDAP_OPERATIONS_ERROR;
+ }
+
+ if ( !apply ) {
+ /* we can return now if we aren't applying the changes */
+ return retVal;
+ }
+
+ CFG_LOCK_WRITE(slapdFrontendConfig);
+
+ if ( strcasecmp ( value, "on" ) == 0 ) {
+ *configvalue = LDAP_ON;
+ }
+ else if ( strcasecmp ( value, "off" ) == 0 ) {
+ *configvalue = LDAP_OFF;
+ }
+
+ CFG_UNLOCK_WRITE(slapdFrontendConfig);
+ return retVal;
+}
+
+int
+config_set_readonly( const char *attrname, char *value, char *errorbuf, int apply ) {
+ int retVal = LDAP_SUCCESS;
+ slapdFrontendConfig_t *slapdFrontendConfig = getFrontendConfig();
+
+ retVal = config_set_onoff ( attrname,
+ value,
+ &(slapdFrontendConfig->readonly),
+ errorbuf,
+ apply );
+
+ return retVal;
+}
+
+
+int
+config_set_schemacheck( const char *attrname, char *value, char *errorbuf, int apply ) {
+ int retVal = LDAP_SUCCESS;
+ slapdFrontendConfig_t *slapdFrontendConfig = getFrontendConfig();
+
+ retVal = config_set_onoff ( attrname,
+ value,
+ &(slapdFrontendConfig->schemacheck),
+ errorbuf,
+ apply);
+
+ return retVal;
+}
+
+
+int
+config_set_ds4_compatible_schema( const char *attrname, char *value, char *errorbuf, int apply ) {
+ int retVal = LDAP_SUCCESS;
+ slapdFrontendConfig_t *slapdFrontendConfig = getFrontendConfig();
+
+ retVal = config_set_onoff ( attrname,
+ value,
+ &(slapdFrontendConfig->ds4_compatible_schema),
+ errorbuf,
+ apply);
+
+ return retVal;
+}
+
+
+int
+config_set_schema_ignore_trailing_spaces( const char *attrname, char *value,
+ char *errorbuf, int apply ) {
+ int retVal = LDAP_SUCCESS;
+ slapdFrontendConfig_t *slapdFrontendConfig = getFrontendConfig();
+
+ retVal = config_set_onoff ( attrname,
+ value,
+ &(slapdFrontendConfig->schema_ignore_trailing_spaces),
+ errorbuf,
+ apply);
+
+ return retVal;
+}
+
+
+int
+config_set_enquote_sup_oc( const char *attrname, char *value, char *errorbuf, int apply ) {
+ int retVal = LDAP_SUCCESS;
+ slapdFrontendConfig_t *slapdFrontendConfig = getFrontendConfig();
+
+ retVal = config_set_onoff ( attrname,
+ value,
+ &(slapdFrontendConfig->enquote_sup_oc),
+ errorbuf,
+ apply);
+
+ return retVal;
+}
+
+int
+config_set_rootdn( const char *attrname, char *value, char *errorbuf, int apply ) {
+ int retVal = LDAP_SUCCESS;
+ slapdFrontendConfig_t *slapdFrontendConfig = getFrontendConfig();
+
+ if ( config_value_is_null( attrname, value, errorbuf, 0 )) {
+ return LDAP_OPERATIONS_ERROR;
+ }
+
+ if ( apply ) {
+ CFG_LOCK_WRITE(slapdFrontendConfig);
+ slapi_ch_free ( (void **) &(slapdFrontendConfig->rootdn) );
+ slapdFrontendConfig->rootdn = slapi_dn_normalize (slapi_ch_strdup ( value ) );
+ CFG_UNLOCK_WRITE(slapdFrontendConfig);
+ }
+ return retVal;
+}
+
+int
+config_set_rootpw( const char *attrname, char *value, char *errorbuf, int apply ) {
+ int retVal = LDAP_SUCCESS;
+ slapdFrontendConfig_t *slapdFrontendConfig = getFrontendConfig();
+ char *hashedpw = NULL;
+ struct pw_scheme *is_hashed = NULL;
+
+ if ( config_value_is_null( attrname, value, errorbuf, 0 )) {
+ return LDAP_OPERATIONS_ERROR;
+ }
+
+ if (!apply) {
+ return retVal;
+ }
+
+ CFG_LOCK_WRITE(slapdFrontendConfig);
+
+ slapi_ch_free ( (void **) &(slapdFrontendConfig->rootpw) );
+
+ is_hashed = pw_val2scheme ( value, NULL, 0 );
+
+ if ( is_hashed ) {
+ slapdFrontendConfig->rootpw = slapi_ch_strdup ( value );
+ free_pw_scheme(is_hashed);
+ }
+ else {
+ hashedpw = (slapdFrontendConfig->rootpwstoragescheme->pws_enc)(value);
+ slapdFrontendConfig->rootpw = slapi_ch_strdup ( hashedpw );
+ }
+
+ CFG_UNLOCK_WRITE(slapdFrontendConfig);
+ return retVal;
+}
+
+
+int
+config_set_rootpwstoragescheme( const char *attrname, char *value, char *errorbuf, int apply ) {
+ int retVal = LDAP_SUCCESS;
+ slapdFrontendConfig_t *slapdFrontendConfig = getFrontendConfig();
+ struct pw_scheme *new_scheme = NULL;
+
+ if ( config_value_is_null( attrname, value, errorbuf, 0 )) {
+ return LDAP_OPERATIONS_ERROR;
+ }
+
+ new_scheme = pw_name2scheme ( value );
+ if (new_scheme == NULL ) {
+ char * scheme_list = plugin_get_pwd_storage_scheme_list(PLUGIN_LIST_PWD_STORAGE_SCHEME);
+ if ( scheme_list != NULL ) {
+ PR_snprintf ( errorbuf, SLAPI_DSE_RETURNTEXT_SIZE,
+ "%s: invalid scheme - %s. Valid schemes are: %s",
+ attrname, value, scheme_list );
+ } else {
+ PR_snprintf ( errorbuf, SLAPI_DSE_RETURNTEXT_SIZE,
+ "%s: invalid scheme - %s (no pwdstorage scheme"
+ " plugin loaded)", attrname, value);
+ }
+ slapi_ch_free_string(&scheme_list);
+ retVal = LDAP_OPERATIONS_ERROR;
+ return retVal;
+ }
+
+ CFG_LOCK_WRITE(slapdFrontendConfig);
+ free_pw_scheme(slapdFrontendConfig->rootpwstoragescheme);
+ slapdFrontendConfig->rootpwstoragescheme = new_scheme;
+ CFG_UNLOCK_WRITE(slapdFrontendConfig);
+
+ return retVal;
+}
+
+/*
+ * kexcoff: to replace default initialization in FrontendConfig_init()
+ */
+int config_set_storagescheme() {
+
+ slapdFrontendConfig_t *slapdFrontendConfig = getFrontendConfig();
+ struct pw_scheme *new_scheme = NULL;
+
+ CFG_LOCK_WRITE(slapdFrontendConfig);
+
+ new_scheme = pw_name2scheme("SSHA");
+ free_pw_scheme(slapdFrontendConfig->pw_storagescheme);
+ slapdFrontendConfig->pw_storagescheme = new_scheme;
+
+ new_scheme = pw_name2scheme("SSHA");
+ slapdFrontendConfig->rootpwstoragescheme = new_scheme;
+
+ CFG_UNLOCK_WRITE(slapdFrontendConfig);
+
+ return ( new_scheme == NULL );
+
+}
+
+#ifndef _WIN32
+int
+config_set_localuser( const char *attrname, char *value, char *errorbuf, int apply ) {
+ int retVal = LDAP_SUCCESS;
+ slapdFrontendConfig_t *slapdFrontendConfig = getFrontendConfig();
+
+ if ( config_value_is_null( attrname, value, errorbuf, 0 )) {
+ return LDAP_OPERATIONS_ERROR;
+ }
+
+ if (apply) {
+ CFG_LOCK_WRITE(slapdFrontendConfig);
+ slapi_ch_free ( (void **) &slapdFrontendConfig->localuser );
+ slapdFrontendConfig->localuser = slapi_ch_strdup ( value );
+ CFG_UNLOCK_WRITE(slapdFrontendConfig);
+ }
+ return retVal;
+
+}
+#endif /* _WIN32 */
+
+int
+config_set_workingdir( const char *attrname, char *value, char *errorbuf, int apply ) {
+ int retVal = LDAP_SUCCESS;
+ slapdFrontendConfig_t *slapdFrontendConfig = getFrontendConfig();
+
+ if ( config_value_is_null( attrname, value, errorbuf, 0 )) {
+ return LDAP_OPERATIONS_ERROR;
+ }
+
+ if ( PR_Access ( value, PR_ACCESS_EXISTS ) != 0 ) {
+ PR_snprintf ( errorbuf, SLAPI_DSE_RETURNTEXT_SIZE, "Working directory \"%s\" does not exist.", value );
+ retVal = LDAP_OPERATIONS_ERROR;
+ return retVal;
+ }
+ if ( PR_Access ( value, PR_ACCESS_WRITE_OK ) != 0 ) {
+ PR_snprintf ( errorbuf, SLAPI_DSE_RETURNTEXT_SIZE, "Working directory \"%s\" is not writeable.", value );
+ retVal = LDAP_OPERATIONS_ERROR;
+ return retVal;
+ }
+
+ if ( apply) {
+ CFG_LOCK_WRITE(slapdFrontendConfig);
+ slapdFrontendConfig->workingdir = slapi_ch_strdup ( value );
+#ifdef _WIN32
+ dostounixpath(slapdFrontendConfig->workingdir);
+#endif /* _WIN32 */
+ CFG_UNLOCK_WRITE(slapdFrontendConfig);
+ }
+ return retVal;
+}
+
+int
+config_set_instancedir( const char *attrname, char *value, char *errorbuf, int apply ) {
+ int retVal = LDAP_SUCCESS;
+ slapdFrontendConfig_t *slapdFrontendConfig = getFrontendConfig();
+
+ if ( config_value_is_null( attrname, value, errorbuf, 0 )) {
+ return LDAP_OPERATIONS_ERROR;
+ }
+
+ if ( PR_Access ( value, PR_ACCESS_READ_OK ) != 0 ) {
+ PR_snprintf ( errorbuf, SLAPI_DSE_RETURNTEXT_SIZE, "Directory \"%s\" is not accessible.", value );
+ retVal = LDAP_OPERATIONS_ERROR;
+ return retVal;
+ }
+
+ if ( apply) {
+ CFG_LOCK_WRITE(slapdFrontendConfig);
+ slapdFrontendConfig->instancedir = slapi_ch_strdup ( value );
+#ifdef _WIN32
+ dostounixpath(slapdFrontendConfig->instancedir);
+#endif /* _WIN32 */
+ CFG_UNLOCK_WRITE(slapdFrontendConfig);
+
+ /* Set the slapd type also */
+ config_set_slapd_type ();
+
+ /* Set the configdir if not set */
+ if (!slapdFrontendConfig->configdir)
+ {
+ char newdir[MAXPATHLEN+1];
+ PR_snprintf ( newdir, sizeof(newdir), "%s/%s",
+ slapdFrontendConfig->instancedir, CONFIG_SUBDIR_NAME);
+ retVal = config_set_configdir(attrname, newdir, errorbuf, apply);
+ }
+ }
+ return retVal;
+}
+
+/* alias of encryption key and certificate files is now retrieved through */
+/* calls to psetFullCreate() and psetGetAttrSingleValue(). See ssl.c, */
+/* where this function is still used to set the global variable */
+int
+config_set_encryptionalias( const char *attrname, char *value, char *errorbuf, int apply ) {
+ int retVal = LDAP_SUCCESS;
+ slapdFrontendConfig_t *slapdFrontendConfig = getFrontendConfig();
+
+ if ( config_value_is_null( attrname, value, errorbuf, 0 )) {
+ return LDAP_OPERATIONS_ERROR;
+ }
+ if (apply) {
+ CFG_LOCK_WRITE(slapdFrontendConfig);
+ slapi_ch_free ( (void **) &(slapdFrontendConfig->encryptionalias) );
+
+ slapdFrontendConfig->encryptionalias = slapi_ch_strdup ( value );
+ CFG_UNLOCK_WRITE(slapdFrontendConfig);
+ }
+ return retVal;
+}
+
+int
+config_set_threadnumber( const char *attrname, char *value, char *errorbuf, int apply ) {
+ int retVal = LDAP_SUCCESS, threadnum = 0;
+ slapdFrontendConfig_t *slapdFrontendConfig = getFrontendConfig();
+
+ if ( config_value_is_null( attrname, value, errorbuf, 0 )) {
+ return LDAP_OPERATIONS_ERROR;
+ }
+
+ threadnum = atoi ( value );
+
+ if ( threadnum < 1 || threadnum > 65535 ) {
+ PR_snprintf ( errorbuf, SLAPI_DSE_RETURNTEXT_SIZE, "%s: invalid value %d, maximum thread number must range from 1 to 65535", attrname, threadnum );
+ retVal = LDAP_OPERATIONS_ERROR;
+ }
+
+ if (apply) {
+ CFG_LOCK_WRITE(slapdFrontendConfig);
+ /* max_threads = threadnum; */
+ slapdFrontendConfig->threadnumber = threadnum;
+ CFG_UNLOCK_WRITE(slapdFrontendConfig);
+ }
+ return retVal;
+}
+
+int
+config_set_maxthreadsperconn( const char *attrname, char *value, char *errorbuf, int apply ) {
+ int retVal = LDAP_SUCCESS, maxthreadnum = 0;
+ slapdFrontendConfig_t *slapdFrontendConfig = getFrontendConfig();
+
+ if ( config_value_is_null( attrname, value, errorbuf, 0 )) {
+ return LDAP_OPERATIONS_ERROR;
+ }
+
+ maxthreadnum = atoi ( value );
+
+ if ( maxthreadnum < 1 || maxthreadnum > 65535 ) {
+ PR_snprintf ( errorbuf, SLAPI_DSE_RETURNTEXT_SIZE, "%s: invalid value %d, maximum thread number per connection must range from 1 to 65535", attrname, maxthreadnum );
+ retVal = LDAP_OPERATIONS_ERROR;
+ }
+
+ if (apply) {
+ CFG_LOCK_WRITE(slapdFrontendConfig);
+ /* max_threads_per_conn = maxthreadnum; */
+ slapdFrontendConfig->maxthreadsperconn = maxthreadnum;
+ CFG_UNLOCK_WRITE(slapdFrontendConfig);
+ }
+ return retVal;
+}
+
+#if !defined(_WIN32) && !defined(AIX)
+#include <sys/resource.h>
+int
+config_set_maxdescriptors( const char *attrname, char *value, char *errorbuf, int apply ) {
+ int retVal = LDAP_SUCCESS, nValue = 0;
+ int maxVal = 65535;
+ struct rlimit rlp;
+ slapdFrontendConfig_t *slapdFrontendConfig = getFrontendConfig();
+
+ if ( config_value_is_null( attrname, value, errorbuf, 0 )) {
+ return LDAP_OPERATIONS_ERROR;
+ }
+
+ nValue = atoi ( value );
+ if ( 0 == getrlimit( RLIMIT_NOFILE, &rlp ) ) {
+ maxVal = (int)rlp.rlim_max;
+ }
+
+ /* DirLite: limit the number of concurent connections by limiting
+ * maxdescriptors.
+ */
+
+ if ( config_is_slapd_lite() && nValue > SLAPD_LITE_MAXDESCRIPTORS ) {
+ PR_snprintf ( errorbuf, SLAPI_DSE_RETURNTEXT_SIZE, LITE_MAXDESCRIPTORS_ERR );
+ retVal = LDAP_UNWILLING_TO_PERFORM;
+ nValue = SLAPD_LITE_MAXDESCRIPTORS;
+ }
+
+ if ( nValue < 1 || nValue > maxVal ) {
+ PR_snprintf ( errorbuf, SLAPI_DSE_RETURNTEXT_SIZE, "%s: invalid value %d, maximum file descriptors must range from 1 to %d (the current process limit)",
+ attrname, nValue, maxVal );
+ if ( nValue < 1 ) {
+ retVal = LDAP_OPERATIONS_ERROR;
+ } else {
+ nValue = maxVal;
+ retVal = LDAP_UNWILLING_TO_PERFORM;
+ }
+ }
+
+ if (apply) {
+ CFG_LOCK_WRITE(slapdFrontendConfig);
+ slapdFrontendConfig->maxdescriptors = nValue;
+ CFG_UNLOCK_WRITE(slapdFrontendConfig);
+ }
+ return retVal;
+
+}
+#endif /* !_WIN32 && !AIX */
+
+int
+config_set_conntablesize( const char *attrname, char *value, char *errorbuf, int apply ) {
+ int retVal = LDAP_SUCCESS, nValue = 0;
+ int maxVal = 65535;
+#ifndef _WIN32
+ struct rlimit rlp;
+#endif
+ slapdFrontendConfig_t *slapdFrontendConfig = getFrontendConfig();
+
+ if ( config_value_is_null( attrname, value, errorbuf, 0 )) {
+ return LDAP_OPERATIONS_ERROR;
+ }
+
+ nValue = atoi ( value );
+
+#ifdef _WIN32
+ if ( nValue < 1 || nValue > 0xfffffe ) {
+ PR_snprintf ( errorbuf, SLAPI_DSE_RETURNTEXT_SIZE, "%s: invalid value %d, connection table size must range from 1 to 0xfffffe", attrname, nValue );
+ retVal = LDAP_OPERATIONS_ERROR;
+ }
+#elif !defined(AIX)
+ if ( 0 == getrlimit( RLIMIT_NOFILE, &rlp ) ) {
+ maxVal = (int)rlp.rlim_max;
+ }
+
+ if ( nValue < 1 || nValue > maxVal ) {
+ PR_snprintf ( errorbuf, SLAPI_DSE_RETURNTEXT_SIZE, "%s: invalid value %d, connection table size must range from 1 to %d (the current process maxdescriptors limit)",
+ attrname, nValue, maxVal );
+ if ( nValue < 1 ) {
+ retVal = LDAP_OPERATIONS_ERROR;
+ } else {
+ nValue = maxVal;
+ retVal = LDAP_UNWILLING_TO_PERFORM;
+ }
+ }
+#endif
+
+ if (apply) {
+ CFG_LOCK_WRITE(slapdFrontendConfig);
+ slapdFrontendConfig->conntablesize = nValue;
+ CFG_UNLOCK_WRITE(slapdFrontendConfig);
+ }
+ return retVal;
+
+}
+
+int
+config_set_reservedescriptors( const char *attrname, char *value, char *errorbuf, int apply ) {
+ int retVal = LDAP_SUCCESS, nValue = 0;
+ slapdFrontendConfig_t *slapdFrontendConfig = getFrontendConfig();
+
+ if ( config_value_is_null( attrname, value, errorbuf, 0 )) {
+ return LDAP_OPERATIONS_ERROR;
+ }
+
+ nValue = atoi ( value );
+
+ if ( nValue < 1 || nValue > 65535 ) {
+ PR_snprintf ( errorbuf, SLAPI_DSE_RETURNTEXT_SIZE, "%s: invalid value %d, reserved file descriptors must range from 1 to 65535", attrname, nValue );
+ retVal = LDAP_OPERATIONS_ERROR;
+ }
+
+ if (apply) {
+ CFG_LOCK_WRITE(slapdFrontendConfig);
+ slapdFrontendConfig->reservedescriptors = nValue;
+ CFG_UNLOCK_WRITE(slapdFrontendConfig);
+ }
+ return retVal;
+
+}
+
+
+
+int
+config_set_ioblocktimeout( const char *attrname, char *value, char *errorbuf, int apply ) {
+ int retVal = LDAP_SUCCESS, nValue = 0;
+ slapdFrontendConfig_t *slapdFrontendConfig = getFrontendConfig();
+
+ if ( config_value_is_null( attrname, value, errorbuf, 0 )) {
+ return LDAP_OPERATIONS_ERROR;
+ }
+
+ nValue = atoi ( value );
+
+#if defined(IRIX)
+ /* on IRIX poll can only handle timeouts up to
+ 2147483 without failing, cap it at 30 minutes */
+
+ if ( nValue > SLAPD_DEFAULT_IOBLOCK_TIMEOUT ) {
+ nValue = SLAPD_DEFAULT_IOBLOCK_TIMEOUT;
+ }
+#endif /* IRIX */
+
+ if ( apply ) {
+ CFG_LOCK_WRITE(slapdFrontendConfig);
+ slapdFrontendConfig->ioblocktimeout = nValue;
+ /* g_ioblock_timeout= nValue; */
+ CFG_UNLOCK_WRITE(slapdFrontendConfig);
+ }
+ return retVal;
+
+}
+
+
+int
+config_set_idletimeout( const char *attrname, char *value, char *errorbuf, int apply ) {
+ int retVal = LDAP_SUCCESS, nValue = 0;
+ slapdFrontendConfig_t *slapdFrontendConfig = getFrontendConfig();
+
+ if ( config_value_is_null( attrname, value, errorbuf, 0 )) {
+ return LDAP_OPERATIONS_ERROR;
+ }
+
+ nValue = atoi ( value );
+
+ if (apply) {
+ CFG_LOCK_WRITE(slapdFrontendConfig);
+ slapdFrontendConfig->idletimeout = nValue;
+ /* g_idle_timeout= nValue; */
+ CFG_UNLOCK_WRITE(slapdFrontendConfig);
+ }
+ return retVal;
+
+}
+
+
+int
+config_set_groupevalnestlevel( const char *attrname, char * value, char *errorbuf, int apply ) {
+ int retVal = LDAP_SUCCESS, nValue = 0;
+ slapdFrontendConfig_t *slapdFrontendConfig = getFrontendConfig();
+
+ if ( config_value_is_null( attrname, value, errorbuf, 0 )) {
+ return LDAP_OPERATIONS_ERROR;
+ }
+
+ nValue = atoi ( value );
+
+ if ( nValue < 1 ) {
+ PR_snprintf ( errorbuf, SLAPI_DSE_RETURNTEXT_SIZE,
+ "%s: invalid value %d, must be a positive number",
+ attrname, nValue );
+ }
+ if (apply) {
+ CFG_LOCK_WRITE(slapdFrontendConfig);
+ slapdFrontendConfig->groupevalnestlevel = nValue;
+ CFG_UNLOCK_WRITE(slapdFrontendConfig);
+ }
+ return retVal;
+
+}
+
+int
+config_set_defaultreferral( const char *attrname, struct berval **value, char *errorbuf, int apply ) {
+ int retVal = LDAP_SUCCESS;
+ slapdFrontendConfig_t *slapdFrontendConfig = getFrontendConfig();
+
+ if ( config_is_slapd_lite() ) {
+ PR_snprintf ( errorbuf, SLAPI_DSE_RETURNTEXT_SIZE, LITE_DEFAULT_REFERRAL_ERR );
+ retVal = LDAP_UNWILLING_TO_PERFORM;
+ return retVal;
+ }
+
+ if ( config_value_is_null( attrname, (char *)value, errorbuf, 0 )) {
+ return LDAP_OPERATIONS_ERROR;
+ }
+
+ if (apply) {
+ CFG_LOCK_WRITE(slapdFrontendConfig);
+ g_set_default_referral( value );
+ CFG_UNLOCK_WRITE(slapdFrontendConfig);
+ }
+ return retVal;
+}
+
+int
+config_set_userat( const char *attrname, char *value, char *errorbuf, int apply ) {
+ int retVal = LDAP_SUCCESS;
+ slapdFrontendConfig_t *slapdFrontendConfig = getFrontendConfig();
+
+ if ( config_value_is_null( attrname, value, errorbuf, 1 )) {
+ return LDAP_OPERATIONS_ERROR;
+ }
+
+ if ( apply ) {
+ CFG_LOCK_WRITE(slapdFrontendConfig);
+ slapi_ch_free( (void **) &(slapdFrontendConfig->userat) );
+ slapdFrontendConfig->userat = slapi_ch_strdup(value);
+ CFG_UNLOCK_WRITE(slapdFrontendConfig);
+ }
+ return retVal;
+}
+
+int
+config_set_timelimit( const char *attrname, char *value, char *errorbuf, int apply ) {
+ int retVal = LDAP_SUCCESS, nVal = 0;
+ slapdFrontendConfig_t *slapdFrontendConfig = getFrontendConfig();
+ Slapi_Backend *be = NULL;
+ char *cookie;
+
+ *errorbuf = 0;
+
+ if ( config_value_is_null( attrname, value, errorbuf, 1 )) {
+ return LDAP_OPERATIONS_ERROR;
+ }
+
+ nVal = atoi(value);
+ if ( nVal < 0 ) {
+ PR_snprintf ( errorbuf, SLAPI_DSE_RETURNTEXT_SIZE,
+ "%s: invalid value %d", attrname, nVal );
+ }
+
+ if ( apply ) {
+ CFG_LOCK_WRITE(slapdFrontendConfig);
+ g_set_deftime ( nVal );
+ slapdFrontendConfig->timelimit = nVal;
+ be = slapi_get_first_backend (&cookie);
+ while (be) {
+ be->be_timelimit = slapdFrontendConfig->timelimit;
+ be = slapi_get_next_backend (cookie);
+ }
+ CFG_UNLOCK_WRITE(slapdFrontendConfig);
+
+ slapi_ch_free ((void **)&cookie);
+ }
+ return retVal;
+}
+
+int
+config_set_useroc( const char *attrname, char *value, char *errorbuf, int apply ) {
+ int retVal = LDAP_SUCCESS;
+ slapdFrontendConfig_t *slapdFrontendConfig = getFrontendConfig();
+
+ if ( config_value_is_null( attrname, value, errorbuf, 1 )) {
+ return LDAP_OPERATIONS_ERROR;
+ }
+
+ if ( apply ) {
+ CFG_LOCK_WRITE(slapdFrontendConfig);
+ slapi_ch_free ( (void **) &(slapdFrontendConfig->useroc) );
+ slapdFrontendConfig->useroc = slapi_ch_strdup( value );
+ CFG_UNLOCK_WRITE(slapdFrontendConfig);
+ }
+ return retVal;
+}
+
+
+int
+config_set_accesslog( const char *attrname, char *value, char *errorbuf, int apply ) {
+ int retVal = LDAP_SUCCESS;
+ slapdFrontendConfig_t *slapdFrontendConfig = getFrontendConfig();
+
+ if ( config_value_is_null( attrname, value, errorbuf, 1 )) {
+ return LDAP_OPERATIONS_ERROR;
+ }
+
+ retVal = log_update_accesslogdir ( value, apply );
+
+ if ( retVal != LDAP_SUCCESS ) {
+ PR_snprintf ( errorbuf, SLAPI_DSE_RETURNTEXT_SIZE,
+ "Cannot open accesslog directory \"%s\", client accesses will "
+ "not be logged.", value );
+ }
+
+ if ( apply ) {
+ CFG_LOCK_WRITE(slapdFrontendConfig);
+ slapi_ch_free ( (void **) &(slapdFrontendConfig->accesslog) );
+ slapdFrontendConfig->accesslog = slapi_ch_strdup ( value );
+ CFG_UNLOCK_WRITE(slapdFrontendConfig);
+ }
+ return retVal;
+}
+
+int
+config_set_errorlog( const char *attrname, char *value, char *errorbuf, int apply ) {
+ int retVal = LDAP_SUCCESS;
+ slapdFrontendConfig_t *slapdFrontendConfig = getFrontendConfig();
+
+ if ( config_value_is_null( attrname, value, errorbuf, 1 )) {
+ return LDAP_OPERATIONS_ERROR;
+ }
+
+ retVal = log_update_errorlogdir ( value, apply );
+
+ if ( retVal != LDAP_SUCCESS ) {
+ PR_snprintf ( errorbuf, SLAPI_DSE_RETURNTEXT_SIZE,
+ "Cannot open errorlog directory \"%s\", errors will "
+ "not be logged.", value );
+ }
+
+ if ( apply ) {
+ CFG_LOCK_WRITE(slapdFrontendConfig);
+ slapi_ch_free ( (void **) &(slapdFrontendConfig->errorlog) );
+ slapdFrontendConfig->errorlog = slapi_ch_strdup ( value );
+ CFG_UNLOCK_WRITE(slapdFrontendConfig);
+ }
+ return retVal;
+}
+
+int
+config_set_auditlog( const char *attrname, char *value, char *errorbuf, int apply ) {
+ int retVal = LDAP_SUCCESS;
+ slapdFrontendConfig_t *slapdFrontendConfig = getFrontendConfig();
+
+ if ( config_value_is_null( attrname, value, errorbuf, 1 )) {
+ return LDAP_OPERATIONS_ERROR;
+ }
+
+ retVal = log_update_auditlogdir ( value, apply );
+
+ if ( retVal != LDAP_SUCCESS ) {
+ PR_snprintf ( errorbuf, SLAPI_DSE_RETURNTEXT_SIZE,
+ "Cannot open auditlog directory \"%s\"", value );
+ }
+
+ if ( apply ) {
+ CFG_LOCK_WRITE(slapdFrontendConfig);
+ slapi_ch_free ( (void **) &(slapdFrontendConfig->auditlog) );
+ slapdFrontendConfig->auditlog = slapi_ch_strdup ( value );
+ CFG_UNLOCK_WRITE(slapdFrontendConfig);
+ }
+ return retVal;
+}
+int
+config_set_pw_maxage( const char *attrname, char *value, char *errorbuf, int apply ) {
+ int retVal = LDAP_SUCCESS;
+ long age;
+ slapdFrontendConfig_t *slapdFrontendConfig = getFrontendConfig();
+
+ if ( config_value_is_null( attrname, value, errorbuf, 1 )) {
+ return LDAP_OPERATIONS_ERROR;
+ }
+
+ /* age in seconds */
+ age = strtol(value, NULL, 0 );
+ if ( age <= 0 || age > (MAX_ALLOWED_TIME_IN_SECS - current_time()) ) {
+ PR_snprintf ( errorbuf, SLAPI_DSE_RETURNTEXT_SIZE,
+ "password maximum age \"%s\" seconds is invalid. ",
+ value );
+ retVal = LDAP_OPERATIONS_ERROR;
+ return retVal;
+ }
+
+ if ( apply ) {
+ slapdFrontendConfig->pw_policy.pw_maxage = age;
+ }
+ return retVal;
+}
+
+int
+config_set_pw_minage( const char *attrname, char *value, char *errorbuf, int apply ) {
+ int retVal = LDAP_SUCCESS;
+ long age;
+ char *endPtr = NULL;
+ slapdFrontendConfig_t *slapdFrontendConfig = getFrontendConfig();
+
+ if ( config_value_is_null( attrname, value, errorbuf, 1 )) {
+ return LDAP_OPERATIONS_ERROR;
+ }
+
+ /* age in seconds */
+ age = strtol(value, &endPtr, 0 );
+ /* endPtr should never be NULL, but we check just in case; if the
+ value contains no digits, or a string that does not begin with
+ a valid digit (e.g. "z2"), the days will be 0, and endPtr will
+ point to the beginning of value; if days contains at least 1
+ valid digit string, endPtr will point to the character after
+ the end of the first valid digit string in value. Example:
+ value = " 2 3 " endPtr will point at the space character
+ between the 2 and the 3. So, we should be able to simply
+ check to see if the character at *(endPtr - 1) is a digit.
+ */
+ if ( (age < 0) ||
+ (age > (MAX_ALLOWED_TIME_IN_SECS - current_time())) ||
+ (endPtr == NULL) || (endPtr == value) || !isdigit(*(endPtr-1)) ) {
+ PR_snprintf ( errorbuf, SLAPI_DSE_RETURNTEXT_SIZE,
+ "password minimum age \"%s\" seconds is invalid. ",
+ value );
+ retVal = LDAP_OPERATIONS_ERROR;
+ return retVal;
+ }
+
+ if ( apply ) {
+ slapdFrontendConfig->pw_policy.pw_minage = age;
+ }
+ return retVal;
+}
+
+int
+config_set_pw_warning( const char *attrname, char *value, char *errorbuf, int apply ) {
+ int retVal = LDAP_SUCCESS;
+ long sec;
+ slapdFrontendConfig_t *slapdFrontendConfig = getFrontendConfig();
+
+ if ( config_value_is_null( attrname, value, errorbuf, 1 )) {
+ return LDAP_OPERATIONS_ERROR;
+ }
+
+ /* in seconds */
+ sec = strtol(value, NULL, 0);
+ if (sec < 0) {
+ PR_snprintf ( errorbuf, SLAPI_DSE_RETURNTEXT_SIZE,
+ "password warning age \"%s\" seconds is invalid, password warning "
+ "age must be >= 0 seconds",
+ value );
+ retVal = LDAP_OPERATIONS_ERROR;
+ return retVal;
+ }
+ /* translate to seconds */
+ if ( apply ) {
+ slapdFrontendConfig->pw_policy.pw_warning = sec;
+ }
+ return retVal;
+}
+
+
+
+int
+config_set_errorlog_level( const char *attrname, char *value, char *errorbuf, int apply ) {
+ int retVal = LDAP_SUCCESS, level = 0;
+ slapdFrontendConfig_t *slapdFrontendConfig = getFrontendConfig();
+
+ if ( config_value_is_null( attrname, value, errorbuf, 1 )) {
+ return LDAP_OPERATIONS_ERROR;
+ }
+
+ if ( apply ) {
+ CFG_LOCK_WRITE(slapdFrontendConfig);
+ level = atoi ( value );
+ level |= LDAP_DEBUG_ANY;
+
+#ifdef _WIN32
+ *module_ldap_debug = level;
+#else
+ slapd_ldap_debug = level;
+#endif
+ slapdFrontendConfig->errorloglevel = level;
+ CFG_UNLOCK_WRITE(slapdFrontendConfig);
+ }
+ return retVal;
+}
+
+
+int
+config_set_accesslog_level( const char *attrname, char *value, char *errorbuf, int apply ) {
+ int retVal = LDAP_SUCCESS, level = 0;
+ slapdFrontendConfig_t *slapdFrontendConfig = getFrontendConfig();
+
+ if ( config_value_is_null( attrname, value, errorbuf, 1 )) {
+ return LDAP_OPERATIONS_ERROR;
+ }
+
+ if ( apply ) {
+ CFG_LOCK_WRITE(slapdFrontendConfig);
+ level = atoi ( value );
+ g_set_accesslog_level ( level );
+ slapdFrontendConfig->accessloglevel = level;
+ CFG_UNLOCK_WRITE(slapdFrontendConfig);
+ }
+ return retVal;
+}
+
+/* set the referral-mode url (which puts us into referral mode) */
+int config_set_referral_mode(const char *attrname, char *url, char *errorbuf, int apply)
+{
+ slapdFrontendConfig_t *slapdFrontendConfig = getFrontendConfig();
+
+ slapdFrontendConfig->refer_mode=REFER_MODE_OFF;
+ if ( config_is_slapd_lite() ) {
+ PR_snprintf ( errorbuf, SLAPI_DSE_RETURNTEXT_SIZE, LITE_REFERRAL_MODE_ERR );
+ return LDAP_UNWILLING_TO_PERFORM;
+ }
+
+ if ((!url) || (!url[0])) {
+ strcpy(errorbuf, "referral url must have a value");
+ return LDAP_OPERATIONS_ERROR;
+ }
+ if (apply) {
+ CFG_LOCK_WRITE(slapdFrontendConfig);
+ slapdFrontendConfig->refer_url = slapi_ch_strdup(url);
+ slapdFrontendConfig->refer_mode = REFER_MODE_ON;
+ CFG_UNLOCK_WRITE(slapdFrontendConfig);
+ }
+ return LDAP_SUCCESS;
+}
+
+int
+config_set_versionstring( const char *attrname, char *version, char *errorbuf, int apply ) {
+ slapdFrontendConfig_t *slapdFrontendConfig = getFrontendConfig();
+
+ if ((!version) || (!version[0])) {
+ strcpy(errorbuf, "versionstring must have a value");
+ return LDAP_OPERATIONS_ERROR;
+ }
+ if (apply) {
+ CFG_LOCK_WRITE(slapdFrontendConfig);
+ slapdFrontendConfig->versionstring = slapi_ch_strdup(version);
+ CFG_UNLOCK_WRITE(slapdFrontendConfig);
+ }
+ return LDAP_SUCCESS;
+}
+
+
+
+
+#define config_copy_strval( s ) s ? slapi_ch_strdup (s) : NULL;
+
+int
+config_get_port(){
+ int retVal;
+ slapdFrontendConfig_t *slapdFrontendConfig = getFrontendConfig();
+
+ CFG_LOCK_READ(slapdFrontendConfig);
+ retVal = slapdFrontendConfig->port;
+ CFG_UNLOCK_READ(slapdFrontendConfig);
+
+ return retVal;
+
+}
+
+char *
+config_get_workingdir() {
+ slapdFrontendConfig_t *slapdFrontendConfig = getFrontendConfig();
+ char *retVal;
+
+ CFG_LOCK_READ(slapdFrontendConfig);
+ retVal = slapi_ch_strdup(slapdFrontendConfig->workingdir);
+ CFG_UNLOCK_READ(slapdFrontendConfig);
+
+ return retVal;
+}
+
+char *
+config_get_versionstring() {
+
+ slapdFrontendConfig_t *slapdFrontendConfig = getFrontendConfig();
+ char *retVal;
+
+ CFG_LOCK_READ(slapdFrontendConfig);
+ retVal = slapi_ch_strdup(slapdFrontendConfig->versionstring);
+ CFG_UNLOCK_READ(slapdFrontendConfig);
+
+ return retVal;
+
+}
+
+
+char *
+config_get_buildnum(void)
+{
+ return slapi_ch_strdup(BUILD_NUM);
+}
+
+int
+config_get_secureport() {
+ slapdFrontendConfig_t *slapdFrontendConfig = getFrontendConfig();
+ int retVal;
+
+ CFG_LOCK_READ(slapdFrontendConfig);
+ retVal = slapdFrontendConfig->secureport;
+ CFG_UNLOCK_READ(slapdFrontendConfig);
+
+ return retVal;
+}
+
+
+int
+config_get_SSLclientAuth() {
+ slapdFrontendConfig_t *slapdFrontendConfig = getFrontendConfig();
+ int retVal;
+
+ CFG_LOCK_READ(slapdFrontendConfig);
+ retVal = slapdFrontendConfig->SSLclientAuth;
+ CFG_UNLOCK_READ(slapdFrontendConfig);
+
+ return retVal;
+}
+
+
+int
+config_get_ssl_check_hostname()
+{
+ slapdFrontendConfig_t *slapdFrontendConfig = getFrontendConfig();
+ return slapdFrontendConfig->ssl_check_hostname;
+}
+
+
+char *
+config_get_localhost() {
+ slapdFrontendConfig_t *slapdFrontendConfig = getFrontendConfig();
+ char *retVal;
+
+ CFG_LOCK_READ(slapdFrontendConfig);
+ retVal = config_copy_strval ( slapdFrontendConfig->localhost );
+ CFG_UNLOCK_READ(slapdFrontendConfig);
+
+ return retVal;
+
+}
+
+char *
+config_get_listenhost() {
+ slapdFrontendConfig_t *slapdFrontendConfig = getFrontendConfig();
+ char *retVal;
+
+ CFG_LOCK_READ(slapdFrontendConfig);
+ retVal = config_copy_strval ( slapdFrontendConfig->listenhost );
+ CFG_UNLOCK_READ(slapdFrontendConfig);
+
+ return retVal;
+}
+
+char *
+config_get_securelistenhost() {
+ slapdFrontendConfig_t *slapdFrontendConfig = getFrontendConfig();
+ char *retVal;
+
+ CFG_LOCK_READ(slapdFrontendConfig);
+ retVal = config_copy_strval( slapdFrontendConfig->securelistenhost );
+ CFG_UNLOCK_READ(slapdFrontendConfig);
+
+ return retVal;
+}
+
+char *
+config_get_srvtab() {
+ slapdFrontendConfig_t *slapdFrontendConfig = getFrontendConfig();
+ char *retVal;
+
+ CFG_LOCK_READ(slapdFrontendConfig);
+ retVal = config_copy_strval(slapdFrontendConfig->srvtab);
+ CFG_UNLOCK_READ(slapdFrontendConfig);
+
+ return retVal;
+}
+
+int
+config_get_sizelimit() {
+ slapdFrontendConfig_t *slapdFrontendConfig = getFrontendConfig();
+ int retVal;
+
+ CFG_LOCK_READ(slapdFrontendConfig);
+ retVal = slapdFrontendConfig->sizelimit;
+ CFG_UNLOCK_READ(slapdFrontendConfig);
+
+ return retVal;
+}
+
+char *
+config_get_pw_storagescheme() {
+ slapdFrontendConfig_t *slapdFrontendConfig = getFrontendConfig();
+ char *retVal = 0;
+
+ CFG_LOCK_READ(slapdFrontendConfig);
+ retVal = config_copy_strval(slapdFrontendConfig->pw_storagescheme->pws_name);
+ CFG_UNLOCK_READ(slapdFrontendConfig);
+
+ return retVal;
+}
+
+
+int
+config_get_pw_change() {
+ slapdFrontendConfig_t *slapdFrontendConfig = getFrontendConfig();
+ int retVal;
+
+ CFG_LOCK_READ(slapdFrontendConfig);
+ retVal = slapdFrontendConfig->pw_policy.pw_change;
+ CFG_UNLOCK_READ(slapdFrontendConfig);
+
+ return retVal;
+}
+
+
+int
+config_get_pw_history() {
+ slapdFrontendConfig_t *slapdFrontendConfig = getFrontendConfig();
+ int retVal;
+
+ CFG_LOCK_READ(slapdFrontendConfig);
+ retVal = slapdFrontendConfig->pw_policy.pw_history;
+ CFG_UNLOCK_READ(slapdFrontendConfig);
+
+ return retVal;
+}
+
+
+
+int
+config_get_pw_must_change() {
+ slapdFrontendConfig_t *slapdFrontendConfig = getFrontendConfig();
+ int retVal;
+
+ CFG_LOCK_READ(slapdFrontendConfig);
+ retVal = slapdFrontendConfig->pw_policy.pw_must_change;
+ CFG_UNLOCK_READ(slapdFrontendConfig);
+
+ return retVal;
+}
+
+
+int
+config_get_pw_syntax() {
+ slapdFrontendConfig_t *slapdFrontendConfig = getFrontendConfig();
+ int retVal;
+
+ CFG_LOCK_READ(slapdFrontendConfig);
+ retVal = slapdFrontendConfig->pw_policy.pw_syntax;
+ CFG_UNLOCK_READ(slapdFrontendConfig);
+
+ return retVal;
+}
+
+
+
+int
+config_get_pw_minlength() {
+ slapdFrontendConfig_t *slapdFrontendConfig = getFrontendConfig();
+ int retVal;
+
+ CFG_LOCK_READ(slapdFrontendConfig);
+ retVal = slapdFrontendConfig->pw_policy.pw_minlength;
+ CFG_UNLOCK_READ(slapdFrontendConfig);
+
+ return retVal;
+}
+
+int
+config_get_pw_maxfailure() {
+ slapdFrontendConfig_t *slapdFrontendConfig = getFrontendConfig();
+ int retVal;
+
+ CFG_LOCK_READ(slapdFrontendConfig);
+ retVal = slapdFrontendConfig->pw_policy.pw_maxfailure;
+ CFG_UNLOCK_READ(slapdFrontendConfig);
+ return retVal;
+
+}
+
+
+
+int
+config_get_pw_inhistory() {
+ slapdFrontendConfig_t *slapdFrontendConfig = getFrontendConfig();
+ int retVal;
+
+ CFG_LOCK_READ(slapdFrontendConfig);
+ retVal = slapdFrontendConfig->pw_policy.pw_inhistory;
+ CFG_UNLOCK_READ(slapdFrontendConfig);
+
+ return retVal;
+}
+
+
+
+
+long
+config_get_pw_lockduration() {
+ slapdFrontendConfig_t *slapdFrontendConfig = getFrontendConfig();
+ long retVal;
+
+ CFG_LOCK_READ(slapdFrontendConfig);
+ retVal = slapdFrontendConfig->pw_policy.pw_lockduration;
+ CFG_UNLOCK_READ(slapdFrontendConfig);
+
+ return retVal;
+
+}
+
+
+long
+config_get_pw_resetfailurecount() {
+ slapdFrontendConfig_t *slapdFrontendConfig = getFrontendConfig();
+ long retVal;
+
+ CFG_LOCK_READ(slapdFrontendConfig);
+ retVal = slapdFrontendConfig->pw_policy.pw_resetfailurecount;
+ CFG_UNLOCK_READ(slapdFrontendConfig);
+
+ return retVal;
+}
+
+int
+config_get_pw_is_global_policy() {
+ slapdFrontendConfig_t *slapdFrontendConfig = getFrontendConfig();
+ int retVal;
+
+ CFG_LOCK_READ(slapdFrontendConfig);
+ retVal = slapdFrontendConfig->pw_is_global_policy;
+ CFG_UNLOCK_READ(slapdFrontendConfig);
+
+ return retVal;
+}
+
+int
+config_get_pw_exp() {
+ slapdFrontendConfig_t *slapdFrontendConfig = getFrontendConfig();
+ int retVal;
+
+ CFG_LOCK_READ(slapdFrontendConfig);
+ retVal = slapdFrontendConfig->pw_policy.pw_exp;
+ CFG_UNLOCK_READ(slapdFrontendConfig);
+
+ return retVal;
+}
+
+
+int
+config_get_pw_unlock() {
+ slapdFrontendConfig_t *slapdFrontendConfig = getFrontendConfig();
+ int retVal;
+
+ CFG_LOCK_READ(slapdFrontendConfig);
+ retVal = slapdFrontendConfig->pw_policy.pw_unlock;
+ CFG_UNLOCK_READ(slapdFrontendConfig);
+
+ return retVal;
+}
+
+
+int
+config_get_pw_lockout(){
+ slapdFrontendConfig_t *slapdFrontendConfig = getFrontendConfig();
+ int retVal;
+
+ CFG_LOCK_READ(slapdFrontendConfig);
+ retVal = slapdFrontendConfig->pw_policy.pw_lockout;
+ CFG_UNLOCK_READ(slapdFrontendConfig);
+
+ return retVal;
+}
+
+
+int
+config_get_pw_gracelimit() {
+ slapdFrontendConfig_t *slapdFrontendConfig = getFrontendConfig();
+ int retVal=0;
+
+ CFG_LOCK_READ(slapdFrontendConfig);
+ retVal = slapdFrontendConfig->pw_policy.pw_gracelimit;
+ CFG_UNLOCK_READ(slapdFrontendConfig);
+
+ return retVal;
+
+}
+
+
+int
+config_get_lastmod(){
+ slapdFrontendConfig_t *slapdFrontendConfig = getFrontendConfig();
+ int retVal;
+
+ CFG_LOCK_READ(slapdFrontendConfig);
+ retVal = slapdFrontendConfig->lastmod;
+ CFG_UNLOCK_READ(slapdFrontendConfig);
+
+ return retVal;
+}
+
+
+int
+config_get_enquote_sup_oc(){
+ slapdFrontendConfig_t *slapdFrontendConfig = getFrontendConfig();
+ int retVal;
+
+ CFG_LOCK_READ(slapdFrontendConfig);
+ retVal = slapdFrontendConfig->enquote_sup_oc;
+ CFG_UNLOCK_READ(slapdFrontendConfig);
+
+ return retVal;
+}
+
+
+int
+config_get_nagle() {
+ slapdFrontendConfig_t *slapdFrontendConfig = getFrontendConfig();
+ int retVal;
+
+ CFG_LOCK_READ(slapdFrontendConfig);
+ retVal = slapdFrontendConfig->nagle;
+ CFG_UNLOCK_READ(slapdFrontendConfig);
+return retVal; }
+
+
+int
+config_get_accesscontrol() {
+ slapdFrontendConfig_t *slapdFrontendConfig = getFrontendConfig();
+ int retVal;
+
+ CFG_LOCK_READ(slapdFrontendConfig);
+ retVal = slapdFrontendConfig->accesscontrol;
+ CFG_UNLOCK_READ(slapdFrontendConfig);
+
+ return retVal;
+}
+
+int
+config_get_return_exact_case() {
+ slapdFrontendConfig_t *slapdFrontendConfig = getFrontendConfig();
+ int retVal;
+
+ retVal = slapdFrontendConfig->return_exact_case;
+
+ return retVal;
+}
+
+int
+config_get_result_tweak() {
+ slapdFrontendConfig_t *slapdFrontendConfig = getFrontendConfig();
+ int retVal;
+
+ CFG_LOCK_READ(slapdFrontendConfig);
+ retVal = slapdFrontendConfig->result_tweak;
+ CFG_UNLOCK_READ(slapdFrontendConfig);
+
+ return retVal;
+}
+
+int
+config_get_security() {
+ slapdFrontendConfig_t *slapdFrontendConfig = getFrontendConfig();
+ int retVal;
+
+ CFG_LOCK_READ(slapdFrontendConfig);
+ retVal = slapdFrontendConfig->security;
+ CFG_UNLOCK_READ(slapdFrontendConfig);
+
+ return retVal;
+ }
+
+int
+slapi_config_get_readonly() {
+ slapdFrontendConfig_t *slapdFrontendConfig = getFrontendConfig();
+ int retVal;
+
+ CFG_LOCK_READ(slapdFrontendConfig);
+ retVal = slapdFrontendConfig->readonly;
+ CFG_UNLOCK_READ(slapdFrontendConfig);
+
+ return retVal;
+ }
+
+
+int
+config_get_schemacheck() {
+ slapdFrontendConfig_t *slapdFrontendConfig = getFrontendConfig();
+ int retVal;
+
+ CFG_LOCK_READ(slapdFrontendConfig);
+ retVal = slapdFrontendConfig->schemacheck;
+ CFG_UNLOCK_READ(slapdFrontendConfig);
+
+ return retVal;
+ }
+
+int
+config_get_ds4_compatible_schema() {
+ slapdFrontendConfig_t *slapdFrontendConfig = getFrontendConfig();
+ int retVal;
+
+ CFG_LOCK_READ(slapdFrontendConfig);
+ retVal = slapdFrontendConfig->ds4_compatible_schema;
+ CFG_UNLOCK_READ(slapdFrontendConfig);
+
+ return retVal;
+ }
+
+int
+config_get_schema_ignore_trailing_spaces() {
+ slapdFrontendConfig_t *slapdFrontendConfig = getFrontendConfig();
+ int retVal;
+
+ CFG_LOCK_READ(slapdFrontendConfig);
+ retVal = slapdFrontendConfig->schema_ignore_trailing_spaces;
+ CFG_UNLOCK_READ(slapdFrontendConfig);
+
+ return retVal;
+ }
+
+char *
+config_get_rootdn() {
+ slapdFrontendConfig_t *slapdFrontendConfig = getFrontendConfig();
+ char *retVal;
+
+ CFG_LOCK_READ(slapdFrontendConfig);
+ retVal = config_copy_strval (slapdFrontendConfig->rootdn);
+ CFG_UNLOCK_READ(slapdFrontendConfig);
+
+ return retVal;
+}
+
+char * slapi_get_rootdn() {
+ return config_get_rootdn();
+}
+
+char *
+config_get_rootpw() {
+ slapdFrontendConfig_t *slapdFrontendConfig = getFrontendConfig();
+ char *retVal;
+
+ CFG_LOCK_READ(slapdFrontendConfig);
+ retVal = config_copy_strval (slapdFrontendConfig->rootpw);
+ CFG_UNLOCK_READ(slapdFrontendConfig);
+
+ return retVal;
+}
+
+
+char *
+config_get_rootpwstoragescheme() {
+ slapdFrontendConfig_t *slapdFrontendConfig = getFrontendConfig();
+ char *retVal;
+
+ CFG_LOCK_READ(slapdFrontendConfig);
+ retVal = config_copy_strval(slapdFrontendConfig->rootpwstoragescheme->pws_name);
+ CFG_UNLOCK_READ(slapdFrontendConfig);
+
+ return retVal;
+}
+
+
+#ifndef _WIN32
+
+char *
+config_get_localuser() {
+ slapdFrontendConfig_t *slapdFrontendConfig = getFrontendConfig();
+ char *retVal;
+
+ CFG_LOCK_READ(slapdFrontendConfig);
+ retVal = config_copy_strval(slapdFrontendConfig->localuser);
+ CFG_UNLOCK_READ(slapdFrontendConfig);
+
+ return retVal;
+}
+
+#endif /* _WIN32 */
+
+char *
+config_get_instancedir() {
+ slapdFrontendConfig_t *slapdFrontendConfig = getFrontendConfig();
+ char *retVal;
+
+ CFG_LOCK_READ(slapdFrontendConfig);
+ retVal = config_copy_strval( slapdFrontendConfig->instancedir );
+ CFG_UNLOCK_READ(slapdFrontendConfig);
+
+ return retVal;
+}
+
+/* alias of encryption key and certificate files is now retrieved through */
+/* calls to psetFullCreate() and psetGetAttrSingleValue(). See ssl.c, */
+/* where this function is still used to set the global variable */
+char *
+config_get_encryptionalias() {
+ slapdFrontendConfig_t *slapdFrontendConfig = getFrontendConfig();
+ char *retVal;
+
+ CFG_LOCK_READ(slapdFrontendConfig);
+ retVal = config_copy_strval(slapdFrontendConfig->encryptionalias);
+ CFG_UNLOCK_READ(slapdFrontendConfig);
+
+ return retVal;
+}
+
+int
+config_get_threadnumber() {
+ slapdFrontendConfig_t *slapdFrontendConfig = getFrontendConfig();
+ int retVal;
+
+ CFG_LOCK_READ(slapdFrontendConfig);
+ retVal = slapdFrontendConfig->threadnumber;
+ CFG_UNLOCK_READ(slapdFrontendConfig);
+
+ return retVal;
+}
+
+int
+config_get_maxthreadsperconn(){
+ slapdFrontendConfig_t *slapdFrontendConfig = getFrontendConfig();
+ int retVal;
+
+ CFG_LOCK_READ(slapdFrontendConfig);
+ retVal = slapdFrontendConfig->maxthreadsperconn;
+ CFG_UNLOCK_READ(slapdFrontendConfig);
+
+ return retVal;
+}
+
+#if !defined(_WIN32) && !defined(AIX)
+int
+config_get_maxdescriptors() {
+ slapdFrontendConfig_t *slapdFrontendConfig = getFrontendConfig();
+ int retVal;
+
+ CFG_LOCK_READ(slapdFrontendConfig);
+ retVal = slapdFrontendConfig->maxdescriptors;
+ CFG_UNLOCK_READ(slapdFrontendConfig);
+ return retVal;
+}
+
+#endif /* !_WIN32 && !AIX */
+
+int
+config_get_reservedescriptors(){
+ slapdFrontendConfig_t *slapdFrontendConfig = getFrontendConfig();
+ int retVal;
+
+ CFG_LOCK_READ(slapdFrontendConfig);
+ retVal = slapdFrontendConfig->reservedescriptors;
+ CFG_UNLOCK_READ(slapdFrontendConfig);
+
+ return retVal;
+}
+
+
+
+int
+config_get_ioblocktimeout(){
+ slapdFrontendConfig_t *slapdFrontendConfig = getFrontendConfig();
+ int retVal;
+
+ CFG_LOCK_READ(slapdFrontendConfig);
+ retVal = slapdFrontendConfig->ioblocktimeout;
+ CFG_UNLOCK_READ(slapdFrontendConfig);
+
+ return retVal;
+ }
+
+
+int
+config_get_idletimeout(){
+ slapdFrontendConfig_t *slapdFrontendConfig = getFrontendConfig();
+ int retVal;
+
+ CFG_LOCK_READ(slapdFrontendConfig);
+ retVal = slapdFrontendConfig->idletimeout;
+ CFG_UNLOCK_READ(slapdFrontendConfig);
+
+ return retVal;
+}
+
+
+int
+config_get_groupevalnestlevel(){
+ slapdFrontendConfig_t *slapdFrontendConfig = getFrontendConfig();
+ int retVal;
+
+ CFG_LOCK_READ(slapdFrontendConfig);
+ retVal = slapdFrontendConfig->groupevalnestlevel;
+ CFG_UNLOCK_READ(slapdFrontendConfig);
+
+ return retVal;
+}
+
+struct berval **
+config_get_defaultreferral() {
+ slapdFrontendConfig_t *slapdFrontendConfig = getFrontendConfig();
+ struct berval **refs;
+ int nReferrals = 0;
+
+ CFG_LOCK_READ(slapdFrontendConfig);
+ /* count the number of referrals */
+ for ( nReferrals = 0;
+ slapdFrontendConfig->defaultreferral &&
+ slapdFrontendConfig->defaultreferral[nReferrals];
+ nReferrals++)
+ ;
+
+ refs = (struct berval **)
+ slapi_ch_malloc( (nReferrals + 1) * sizeof(struct berval *) );
+
+ /*terminate the end, and add the referrals backwards */
+ refs [nReferrals--] = NULL;
+
+ while ( nReferrals >= 0 ) {
+ refs[nReferrals] = (struct berval *) slapi_ch_malloc( sizeof(struct berval) );
+ refs[nReferrals]->bv_val =
+ config_copy_strval( slapdFrontendConfig->defaultreferral[nReferrals]->bv_val );
+ refs[nReferrals]->bv_len = slapdFrontendConfig->defaultreferral[nReferrals]->bv_len;
+ nReferrals--;
+ }
+ CFG_UNLOCK_READ(slapdFrontendConfig);
+
+ return refs;
+}
+
+char *
+config_get_userat ( ){
+ slapdFrontendConfig_t *slapdFrontendConfig = getFrontendConfig();
+ char *retVal;
+ CFG_LOCK_READ(slapdFrontendConfig);
+ retVal = config_copy_strval( slapdFrontendConfig->userat );
+ CFG_UNLOCK_READ(slapdFrontendConfig);
+
+ return retVal;
+}
+
+int
+config_get_timelimit(){
+ slapdFrontendConfig_t *slapdFrontendConfig = getFrontendConfig();
+ int retVal;
+
+ CFG_LOCK_READ(slapdFrontendConfig);
+ retVal= slapdFrontendConfig->timelimit;
+ CFG_UNLOCK_READ(slapdFrontendConfig);
+
+ return retVal;
+}
+
+char*
+config_get_useroc(){
+ slapdFrontendConfig_t *slapdFrontendConfig = getFrontendConfig();
+ char *retVal;
+
+ CFG_LOCK_WRITE(slapdFrontendConfig);
+ retVal = config_copy_strval(slapdFrontendConfig->useroc );
+ CFG_UNLOCK_WRITE(slapdFrontendConfig);
+
+ return retVal;
+}
+
+char *
+config_get_accesslog(){
+ slapdFrontendConfig_t *slapdFrontendConfig = getFrontendConfig();
+ char *retVal;
+
+ CFG_LOCK_READ(slapdFrontendConfig);
+ retVal = config_copy_strval(slapdFrontendConfig->accesslog);
+ CFG_UNLOCK_READ(slapdFrontendConfig);
+
+ return retVal;
+}
+
+char *
+config_get_errorlog( ){
+ slapdFrontendConfig_t *slapdFrontendConfig = getFrontendConfig();
+ char *retVal;
+
+ CFG_LOCK_READ(slapdFrontendConfig);
+ retVal = config_copy_strval(slapdFrontendConfig->errorlog);
+ CFG_UNLOCK_READ(slapdFrontendConfig);
+
+ return retVal;
+
+}
+
+char *
+config_get_auditlog( ){
+ slapdFrontendConfig_t *slapdFrontendConfig = getFrontendConfig();
+ char *retVal;
+
+ CFG_LOCK_READ(slapdFrontendConfig);
+ retVal = config_copy_strval(slapdFrontendConfig->auditlog);
+ CFG_UNLOCK_READ(slapdFrontendConfig);
+
+ return retVal;
+}
+
+long
+config_get_pw_maxage() {
+ slapdFrontendConfig_t *slapdFrontendConfig = getFrontendConfig();
+ long retVal;
+
+ CFG_LOCK_READ(slapdFrontendConfig);
+ retVal = slapdFrontendConfig->pw_policy.pw_maxage;
+ CFG_UNLOCK_READ(slapdFrontendConfig);
+ return retVal;
+}
+
+long
+config_get_pw_minage(){
+
+ slapdFrontendConfig_t *slapdFrontendConfig = getFrontendConfig();
+ long retVal;
+
+ CFG_LOCK_READ(slapdFrontendConfig);
+ retVal = slapdFrontendConfig->pw_policy.pw_minage;
+ CFG_UNLOCK_READ(slapdFrontendConfig);
+
+ return retVal;
+}
+
+
+long
+config_get_pw_warning() {
+ slapdFrontendConfig_t *slapdFrontendConfig = getFrontendConfig();
+ long retVal;
+
+ CFG_LOCK_READ(slapdFrontendConfig);
+ retVal = slapdFrontendConfig->pw_policy.pw_warning;
+ CFG_UNLOCK_READ(slapdFrontendConfig);
+
+ return retVal;
+}
+
+
+int
+config_get_errorlog_level(){
+ slapdFrontendConfig_t *slapdFrontendConfig = getFrontendConfig();
+ int retVal;
+
+ CFG_LOCK_READ(slapdFrontendConfig);
+ retVal = slapdFrontendConfig->errorloglevel;
+ CFG_UNLOCK_READ(slapdFrontendConfig);
+
+ return retVal;
+}
+
+
+/* return integer -- don't worry about locking similar to config_check_referral_mode
+ below */
+
+int
+config_get_accesslog_level(){
+ slapdFrontendConfig_t *slapdFrontendConfig = getFrontendConfig();
+ int retVal;
+
+ retVal = slapdFrontendConfig->accessloglevel;
+
+ return retVal;
+}
+
+
+char *config_get_referral_mode(void)
+{
+ slapdFrontendConfig_t *slapdFrontendConfig = getFrontendConfig();
+ char *ret;
+
+ CFG_LOCK_READ(slapdFrontendConfig);
+ ret = config_copy_strval(slapdFrontendConfig->refer_url);
+ CFG_UNLOCK_READ(slapdFrontendConfig);
+
+ return ret;
+}
+
+int
+config_get_conntablesize(void){
+ slapdFrontendConfig_t *slapdFrontendConfig = getFrontendConfig();
+ int retVal;
+
+ CFG_LOCK_READ(slapdFrontendConfig);
+ retVal = slapdFrontendConfig->conntablesize;
+ CFG_UNLOCK_READ(slapdFrontendConfig);
+
+ return retVal;
+ }
+
+
+/* return yes/no without actually copying the referral url
+ we don't worry about another thread changing this value
+ since we now return an integer */
+int config_check_referral_mode(void)
+{
+ slapdFrontendConfig_t *slapdFrontendConfig = getFrontendConfig();
+ return(slapdFrontendConfig->refer_mode & REFER_MODE_ON);
+}
+
+
+int
+config_get_outbound_ldap_io_timeout(void)
+{
+ slapdFrontendConfig_t *slapdFrontendConfig = getFrontendConfig();
+ int retVal;
+
+ CFG_LOCK_READ(slapdFrontendConfig);
+ retVal = slapdFrontendConfig->outbound_ldap_io_timeout;
+ CFG_UNLOCK_READ(slapdFrontendConfig);
+ return retVal;
+}
+
+int
+config_is_slapd_lite ()
+{
+ return ( SLAPD_FULL );
+}
+
+/* This function is called once at the startup time and no more */
+void
+config_set_slapd_type( )
+{
+ char *root = NULL;
+ char *s_root = NULL;
+ slapdFrontendConfig_t *slapdFrontendConfig = getFrontendConfig();
+
+ CFG_LOCK_WRITE(slapdFrontendConfig);
+ if ( slapdFrontendConfig->instancedir )
+ s_root = root = slapi_ch_strdup ( slapdFrontendConfig->instancedir );
+
+ if ( (root = strrchr( root, '/' )) != NULL ) {
+ *root = '\0';
+ }
+ slapdFrontendConfig->slapd_type = 0;
+ slapdFrontendConfig->versionstring = SLAPD_VERSION_STR;
+ CFG_UNLOCK_WRITE(slapdFrontendConfig);
+ slapi_ch_free ( (void **) &s_root );
+}
+
+int
+config_set_maxbersize( const char *attrname, char *value, char *errorbuf, int apply )
+{
+ int retVal = LDAP_SUCCESS;
+ slapdFrontendConfig_t *slapdFrontendConfig = getFrontendConfig();
+
+ if ( config_value_is_null( attrname, value, errorbuf, 0 )) {
+ return LDAP_OPERATIONS_ERROR;
+ }
+
+ if ( !apply ) {
+ return retVal;
+ }
+
+ CFG_LOCK_WRITE(slapdFrontendConfig);
+
+ slapdFrontendConfig->maxbersize = atoi(value);
+
+ CFG_UNLOCK_WRITE(slapdFrontendConfig);
+ return retVal;
+}
+
+unsigned long
+config_get_maxbersize()
+{
+ unsigned long maxbersize;
+ slapdFrontendConfig_t *slapdFrontendConfig = getFrontendConfig();
+
+ maxbersize = slapdFrontendConfig->maxbersize;
+ if(maxbersize==0)
+ maxbersize= 2 * 1024 * 1024; /* Default: 2Mb */
+ return maxbersize;
+
+}
+
+int
+config_set_max_filter_nest_level( const char *attrname, char *value,
+ char *errorbuf, int apply )
+{
+ int retVal = LDAP_SUCCESS;
+ slapdFrontendConfig_t *slapdFrontendConfig = getFrontendConfig();
+
+ if ( config_value_is_null( attrname, value, errorbuf, 0 )) {
+ return LDAP_OPERATIONS_ERROR;
+ }
+
+ if ( !apply ) {
+ return retVal;
+ }
+
+ CFG_LOCK_WRITE(slapdFrontendConfig);
+ slapdFrontendConfig->max_filter_nest_level = atoi(value);
+ CFG_UNLOCK_WRITE(slapdFrontendConfig);
+ return retVal;
+}
+
+int
+config_get_max_filter_nest_level()
+{
+ slapdFrontendConfig_t *slapdFrontendConfig = getFrontendConfig();
+ int retVal;
+
+ CFG_LOCK_READ(slapdFrontendConfig);
+ retVal = slapdFrontendConfig->max_filter_nest_level;
+ CFG_UNLOCK_READ(slapdFrontendConfig);
+ return retVal;
+}
+
+
+char *
+config_get_basedn() {
+ slapdFrontendConfig_t *slapdFrontendConfig = getFrontendConfig();
+ char *retVal;
+
+ CFG_LOCK_READ(slapdFrontendConfig);
+ retVal = config_copy_strval ( slapdFrontendConfig->certmap_basedn );
+ CFG_UNLOCK_READ(slapdFrontendConfig);
+
+ return retVal;
+}
+
+int
+config_set_basedn ( const char *attrname, char *value, char *errorbuf, int apply ) {
+ int retVal = LDAP_SUCCESS;
+ slapdFrontendConfig_t *slapdFrontendConfig = getFrontendConfig();
+
+ if ( config_value_is_null( attrname, value, errorbuf, 0 )) {
+ return LDAP_OPERATIONS_ERROR;
+ }
+
+ if ( !apply ) {
+ return retVal;
+ }
+
+ CFG_LOCK_WRITE(slapdFrontendConfig);
+ slapi_ch_free ( (void **) &slapdFrontendConfig->certmap_basedn );
+
+ slapdFrontendConfig->certmap_basedn = slapi_dn_normalize( slapi_ch_strdup(value) );
+
+ CFG_UNLOCK_WRITE(slapdFrontendConfig);
+ return retVal;
+}
+
+char *
+config_get_configdir()
+{
+ slapdFrontendConfig_t *slapdFrontendConfig = getFrontendConfig();
+ char *retVal;
+
+ CFG_LOCK_READ(slapdFrontendConfig);
+ retVal = config_copy_strval(slapdFrontendConfig->configdir);
+ CFG_UNLOCK_READ(slapdFrontendConfig);
+
+ return retVal;
+}
+
+int
+config_set_configdir(const char *attrname, char *value, char *errorbuf, int apply)
+{
+ int retVal = LDAP_SUCCESS;
+ slapdFrontendConfig_t *slapdFrontendConfig = getFrontendConfig();
+
+ if ( config_value_is_null( attrname, value, errorbuf, 0 )) {
+ return LDAP_OPERATIONS_ERROR;
+ }
+
+ if (!apply) {
+ return retVal;
+ }
+
+ CFG_LOCK_WRITE(slapdFrontendConfig);
+ slapi_ch_free((void **)&slapdFrontendConfig->configdir);
+
+ slapdFrontendConfig->configdir = slapi_ch_strdup(value);
+
+ CFG_UNLOCK_WRITE(slapdFrontendConfig);
+ return retVal;
+}
+
+char **
+config_get_errorlog_list()
+{
+ return log_get_loglist(SLAPD_ERROR_LOG);
+}
+
+char **
+config_get_accesslog_list()
+{
+ return log_get_loglist(SLAPD_ACCESS_LOG);
+}
+
+char **
+config_get_auditlog_list()
+{
+ return log_get_loglist(SLAPD_AUDIT_LOG);
+}
+
+int
+config_set_accesslogbuffering(const char *attrname, char *value, char *errorbuf, int apply)
+{
+ int retVal = LDAP_SUCCESS;
+ slapdFrontendConfig_t *slapdFrontendConfig = getFrontendConfig();
+
+ retVal = config_set_onoff(attrname,
+ value,
+ &(slapdFrontendConfig->accesslogbuffering),
+ errorbuf,
+ apply);
+
+ return retVal;
+}
+
+int
+config_set_csnlogging(const char *attrname, char *value, char *errorbuf, int apply)
+{
+ int retVal = LDAP_SUCCESS;
+ slapdFrontendConfig_t *slapdFrontendConfig = getFrontendConfig();
+
+ retVal = config_set_onoff(attrname,
+ value,
+ &(slapdFrontendConfig->csnlogging),
+ errorbuf,
+ apply);
+
+ return retVal;
+}
+
+int
+config_get_csnlogging()
+{
+ slapdFrontendConfig_t *slapdFrontendConfig = getFrontendConfig();
+ return slapdFrontendConfig->csnlogging;
+}
+
+int
+config_set_attrname_exceptions(const char *attrname, char *value, char *errorbuf, int apply)
+{
+ int retVal = LDAP_SUCCESS;
+ slapdFrontendConfig_t *slapdFrontendConfig = getFrontendConfig();
+
+ retVal = config_set_onoff(attrname,
+ value,
+ &(slapdFrontendConfig->attrname_exceptions),
+ errorbuf,
+ apply);
+
+ return retVal;
+}
+
+int
+config_get_attrname_exceptions()
+{
+ slapdFrontendConfig_t *slapdFrontendConfig = getFrontendConfig();
+ return slapdFrontendConfig->attrname_exceptions;
+}
+
+int
+config_set_hash_filters(const char *attrname, char *value, char *errorbuf, int apply)
+{
+ int val = 0;
+ int retVal = LDAP_SUCCESS;
+
+ retVal = config_set_onoff(attrname,
+ value,
+ &val,
+ errorbuf,
+ apply);
+
+ if (retVal == LDAP_SUCCESS) {
+ set_hash_filters(val);
+ }
+
+ return retVal;
+}
+
+int
+config_get_hash_filters()
+{
+ return 0; /* for now */
+}
+
+int
+config_set_rewrite_rfc1274(const char *attrname, char *value, char *errorbuf, int apply)
+{
+ int retVal = LDAP_SUCCESS;
+ slapdFrontendConfig_t *slapdFrontendConfig = getFrontendConfig();
+
+ retVal = config_set_onoff(attrname,
+ value,
+ &(slapdFrontendConfig->rewrite_rfc1274),
+ errorbuf,
+ apply);
+
+ return retVal;
+}
+
+
+/* we don't worry about another thread changing this flag since it is an
+ integer */
+int
+config_get_rewrite_rfc1274()
+{
+ slapdFrontendConfig_t *slapdFrontendConfig = getFrontendConfig();
+ int retVal;
+
+ retVal = slapdFrontendConfig->rewrite_rfc1274;
+ return retVal;
+}
+
+
+static int
+config_set_schemareplace( const char *attrname, char *value, char *errorbuf, int apply )
+{
+ int retVal = LDAP_SUCCESS;
+
+ if ( config_value_is_null( attrname, value, errorbuf, 0 )) {
+ retVal = LDAP_OPERATIONS_ERROR;
+ } else {
+ /*
+ * check that the value is one we allow.
+ */
+ if ( 0 != strcasecmp( value, CONFIG_SCHEMAREPLACE_STR_OFF ) &&
+ 0 != strcasecmp( value, CONFIG_SCHEMAREPLACE_STR_ON ) &&
+ 0 != strcasecmp( value, CONFIG_SCHEMAREPLACE_STR_REPLICATION_ONLY )) {
+ retVal = LDAP_OPERATIONS_ERROR;
+ if( errorbuf ) {
+ PR_snprintf ( errorbuf, SLAPI_DSE_RETURNTEXT_SIZE, "unsupported value: %s", value );
+ }
+ }
+ }
+
+ if ( LDAP_SUCCESS == retVal && apply ) {
+ slapdFrontendConfig_t *slapdFrontendConfig = getFrontendConfig();
+
+ CFG_LOCK_WRITE(slapdFrontendConfig);
+ slapi_ch_free( (void **)&slapdFrontendConfig->schemareplace );
+ slapdFrontendConfig->schemareplace = slapi_ch_strdup( value );
+ CFG_UNLOCK_WRITE(slapdFrontendConfig);
+ }
+
+ return retVal;
+}
+
+
+int
+config_set_outbound_ldap_io_timeout( const char *attrname, char *value,
+ char *errorbuf, int apply )
+{
+ slapdFrontendConfig_t *slapdFrontendConfig = getFrontendConfig();
+
+ if ( config_value_is_null( attrname, value, errorbuf, 0 )) {
+ return LDAP_OPERATIONS_ERROR;
+ }
+
+ if ( apply ) {
+ CFG_LOCK_WRITE(slapdFrontendConfig);
+ slapdFrontendConfig->outbound_ldap_io_timeout = atoi( value );
+ CFG_UNLOCK_WRITE(slapdFrontendConfig);
+ }
+ return LDAP_SUCCESS;
+}
+
+
+/*
+ * This function is intended to be used from the dse code modify callback. It
+ * is "optimized" for that case because it takes a berval** of values, which is
+ * currently what is used by ldapmod to hold the values. We could easily switch
+ * this to take a Slapi_Value array or even a Slapi_Attr. Most config params
+ * have simple config_set_XXX functions which take a char* argument holding the
+ * value. The log_set_XXX functions have an additional parameter which
+ * discriminates the log to use. The config parameters with types CONFIG_SPECIAL_XXX
+ * require special handling to set their values.
+ */
+int
+config_set(const char *attr, struct berval **values, char *errorbuf, int apply)
+{
+ int ii = 0;
+ int retval = LDAP_SUCCESS;
+ struct config_get_and_set *cgas = 0;
+ cgas = (struct config_get_and_set *)PL_HashTableLookup(confighash, attr);
+ if (!cgas)
+ {
+#if 0
+ debugHashTable(attr);
+#endif
+ PR_snprintf ( errorbuf, SLAPI_DSE_RETURNTEXT_SIZE, "Unknown attribute %s will be ignored", attr);
+ slapi_log_error(SLAPI_LOG_FATAL, "config", "%s\n", errorbuf);
+ return retval;
+ }
+
+ switch (cgas->config_var_type)
+ {
+ case CONFIG_SPECIAL_REFERRALLIST:
+ if (NULL == values) /* special token which means to remove referrals */
+ {
+ struct berval val;
+ struct berval *vals[2] = {0, 0};
+ vals[0] = &val;
+ val.bv_val = REFERRAL_REMOVE_CMD;
+ val.bv_len = strlen(REFERRAL_REMOVE_CMD);
+ retval = config_set_defaultreferral(attr, vals, errorbuf, apply);
+ }
+ else
+ {
+ retval = config_set_defaultreferral(attr, values, errorbuf, apply);
+ }
+ break;
+
+ default:
+ for (ii = 0; !retval && values && values[ii]; ++ii)
+ {
+ if (cgas->setfunc)
+ retval = (cgas->setfunc)(cgas->attr_name,
+ (char *)values[ii]->bv_val, errorbuf, apply);
+ else if (cgas->logsetfunc)
+ retval = (cgas->logsetfunc)(cgas->attr_name,
+ (char *)values[ii]->bv_val, cgas->whichlog,
+ errorbuf, apply);
+ else
+ LDAPDebug(LDAP_DEBUG_ANY,
+ "config_set: the attribute %s is read only; ignoring new value %s\n",
+ attr, values[ii]->bv_val, 0);
+ }
+ break;
+ }
+
+ return retval;
+}
+
+static void
+config_set_value(
+ Slapi_Entry *e,
+ struct config_get_and_set *cgas,
+ void **value
+)
+{
+ struct berval **values = 0;
+ char *sval = 0;
+
+ /* for null values, just set the attr value to the empty
+ string */
+ if (!value) {
+ slapi_entry_attr_set_charptr(e, cgas->attr_name, "");
+ return;
+ }
+
+ switch (cgas->config_var_type) {
+ case CONFIG_ON_OFF: /* convert 0,1 to "off","on" */
+ slapi_entry_attr_set_charptr(e, cgas->attr_name,
+ (value && *((int *)value)) ? "on" : "off");
+ break;
+
+ case CONFIG_INT:
+ if (value)
+ slapi_entry_attr_set_int(e, cgas->attr_name, *((int *)value));
+ else
+ slapi_entry_attr_set_charptr(e, cgas->attr_name, "");
+ break;
+
+ case CONFIG_LONG:
+ if (value)
+ slapi_entry_attr_set_long(e, cgas->attr_name, *((long *)value));
+ else
+ slapi_entry_attr_set_charptr(e, cgas->attr_name, "");
+ break;
+
+ case CONFIG_STRING:
+ slapi_entry_attr_set_charptr(e, cgas->attr_name,
+ (value && *((char **)value)) ?
+ *((char **)value) : "");
+ break;
+
+ case CONFIG_CHARRAY:
+ values = strarray2bervalarray((const char **)*((char ***)value));
+ if (!values) {
+ slapi_entry_attr_set_charptr(e, cgas->attr_name, "");
+ } else {
+ slapi_entry_attr_replace(e, cgas->attr_name, values);
+ bervalarray_free(values);
+ }
+ break;
+
+ case CONFIG_SPECIAL_REFERRALLIST:
+ /* referral list is already an array of berval* */
+ if (value)
+ slapi_entry_attr_replace(e, cgas->attr_name, (struct berval**)*value);
+ else
+ slapi_entry_attr_set_charptr(e, cgas->attr_name, "");
+ break;
+
+ case CONFIG_CONSTANT_STRING:
+ PR_ASSERT(value); /* should be a constant value */
+ slapi_entry_attr_set_charptr(e, cgas->attr_name, (char*)value);
+ break;
+
+ case CONFIG_CONSTANT_INT:
+ PR_ASSERT(value); /* should be a constant value */
+ slapi_entry_attr_set_int(e, cgas->attr_name, (int)value);
+ break;
+
+ case CONFIG_SPECIAL_SSLCLIENTAUTH:
+ if (!value) {
+ slapi_entry_attr_set_charptr(e, cgas->attr_name, "off");
+ break;
+ }
+
+ if (*((int *)value) == SLAPD_SSLCLIENTAUTH_ALLOWED) {
+ sval = "allowed";
+ } else if (*((int *)value) == SLAPD_SSLCLIENTAUTH_REQUIRED) {
+ sval = "required";
+ } else {
+ sval = "off";
+ }
+ slapi_entry_attr_set_charptr(e, cgas->attr_name, sval);
+ break;
+
+ case CONFIG_STRING_OR_OFF:
+ slapi_entry_attr_set_charptr(e, cgas->attr_name,
+ (value && *((char **)value)) ?
+ *((char **)value) : "off");
+ break;
+
+ case CONFIG_STRING_OR_EMPTY:
+ slapi_entry_attr_set_charptr(e, cgas->attr_name,
+ (value && *((char **)value)) ?
+ *((char **)value) : "");
+ break;
+
+ case CONFIG_STRING_OR_UNKNOWN:
+ slapi_entry_attr_set_charptr(e, cgas->attr_name,
+ (value && *((char **)value)) ?
+ *((char **)value) : "unknown");
+ break;
+
+ case CONFIG_SPECIAL_ERRORLOGLEVEL:
+ if (value) {
+ int ival = *(int *)value;
+ ival &= ~LDAP_DEBUG_ANY;
+ slapi_entry_attr_set_int(e, cgas->attr_name, ival);
+ }
+ else
+ slapi_entry_attr_set_charptr(e, cgas->attr_name, "");
+ break;
+
+ default:
+ PR_ASSERT(0); /* something went horribly wrong . . . */
+ break;
+ }
+
+ return;
+}
+
+/*
+ * Fill in the given slapi_entry with the config attributes and values
+ */
+int
+config_set_entry(Slapi_Entry *e)
+{
+ int ii = 0;
+ int tablesize = sizeof(ConfigList)/sizeof(ConfigList[0]);
+ slapdFrontendConfig_t *slapdFrontendConfig = getFrontendConfig();
+
+ /*
+ * Avoid recursive calls to the readers/writer
+ * lock as it causes deadlock under stress. Each
+ * individual config get function acquires a read
+ * lock where necessary.
+ */
+
+ /*
+ * Pass 1: Values which do not have a get function.
+ */
+
+ CFG_LOCK_READ(slapdFrontendConfig);
+ for (ii = 0; ii < tablesize; ++ii) {
+ struct config_get_and_set *cgas = &ConfigList[ii];
+ void **value = 0;
+
+ PR_ASSERT(cgas);
+ value = cgas->config_var_addr;
+ PR_ASSERT(cgas->attr_name);
+
+ /* Skip values handled in pass 2 */
+ if (NULL == value && cgas->getfunc) {
+ continue;
+ }
+
+ config_set_value(e, cgas, value);
+ }
+ CFG_UNLOCK_READ(slapdFrontendConfig);
+
+ /*
+ * Pass 2: Values which do have a get function.
+ */
+ for (ii = 0; ii < tablesize; ++ii) {
+ struct config_get_and_set *cgas = &ConfigList[ii];
+ void **value = 0;
+ void *alloc_val;
+ int needs_free = 0;
+
+ PR_ASSERT(cgas);
+ value = cgas->config_var_addr;
+ PR_ASSERT(cgas->attr_name);
+
+ /* Skip values handled in pass 1 */
+ if (NULL != value || cgas->getfunc == NULL) {
+ continue;
+ }
+
+ alloc_val = (cgas->getfunc)();
+
+ value = &alloc_val; /* value must be address of pointer */
+ if (!isIntegralType(cgas->config_var_type))
+ needs_free = 1; /* get funcs must return alloc'd memory except for get
+ funcs which return a simple integral type e.g. int */
+
+ config_set_value(e, cgas, value);
+
+ if (needs_free && value) { /* assumes memory allocated by slapi_ch_Xalloc */
+ if (CONFIG_CHARRAY == cgas->config_var_type) {
+ charray_free(*((char ***)value));
+ } else {
+ slapi_ch_free(value);
+ }
+ }
+ }
+
+ return 1;
+}
diff --git a/ldap/servers/slapd/libmakefile b/ldap/servers/slapd/libmakefile
new file mode 100644
index 00000000..8eaf4604
--- /dev/null
+++ b/ldap/servers/slapd/libmakefile
@@ -0,0 +1,174 @@
+#
+# BEGIN COPYRIGHT BLOCK
+# Copyright 2001 Sun Microsystems, Inc.
+# Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+# All rights reserved.
+# END COPYRIGHT BLOCK
+#
+#
+# GNU Makefile for libslapd
+#
+
+FLAG_LDAP_4SLAPD=true
+LDAP_SRC = ../..
+MCOM_ROOT = ../../../..
+
+NOSTDCLEAN=true # don't let nsconfig.mk define target clean
+NOSTDSTRIP=true # don't let nsconfig.mk define target strip
+NSPR20=true # probably should be defined somewhere else (not sure where)
+
+OBJDEST = $(OBJDIR)/servers/obj
+BINDIR = $(LIB_RELDIR)
+LIBDIR = $(OBJDIR)/lib
+ifndef INSTDIR
+INSTDIR = /netscape/server4/
+endif
+
+include $(MCOM_ROOT)/ldapserver/nsdefs.mk
+include $(MCOM_ROOT)/ldapserver/nsconfig.mk
+include $(LDAP_SRC)/nsldap.mk
+ifndef LDAP_USE_OLD_DB
+include $(MCOM_ROOT)/ldapserver/ns_usedb.mk
+_ldap_db_depend:=$(DB_LIB_DEP)
+INCLUDES+=-I$(DB_INCLUDE)
+endif
+include $(LDAP_SRC)/nsdeps.mk
+
+INCLUDES += -I. -I$(MCOM_ROOT)/ldapserver/lib
+# uncomment the following to include support in slapd for hacky echo control
+#CFLAGS += -DSLAPD_ECHO_CONTROL
+CFLAGS+=$(SLCFLAGS)
+
+LIBSLAPD_OBJS=plugin_role.o getfilelist.o libglobs.o log.o ch_malloc.o entry.o pblock.o \
+ modutil.o schema.o attr.o value.o time.o dn.o \
+ filter.o filtercmp.o filterentry.o operation.o schemaparse.o pw.o \
+ backend.o defbackend.o ava.o charray.o regex.o \
+ str2filter.o dynalib.o plugin.o plugin_syntax.o plugin_mr.o \
+ slapi2nspr.o rwlock.o control.o plugin_internal_op.o \
+ result.o pw_retry.o agtmmap.o referral.o snmp_collator.o util.o \
+ dse.o errormap.o computed.o match.o fileio.o \
+ generation.o localhost.o ssl.o factory.o auditlog.o \
+ lenstr.o eventq.o uuid.o uniqueid.o uniqueidgen.o \
+ csngen.o utf8compare.o ntuserpin.o entrywsi.o valueset.o \
+ attrsyntax.o opshared.o add.o modify.o modrdn.o delete.o dl.o\
+ plugin_acl.o counters.o subentry.o object.o objset.o apibroker.o \
+ csn.o csnset.o slapd_plhash.o attrlist.o vattr.o bitset.o rdn.o \
+ mapping_tree.o backend_manager.o task.o resourcelimit.o \
+ bulk_import.o security_wrappers.o index_subsystem.o sasl_map.o
+
+ifeq ($(ARCH), AIX)
+ifeq ($(DEBUG), optimize)
+TEMP_CFLAGS = $(subst -O,,$(CFLAGS))
+$(OBJDEST)/vattr.o: vattr.c
+ $(CC) -o $(OBJDEST)/vattr.o -c $(TEMP_CFLAGS) $(MCC_INCLUDE) vattr.c
+endif
+endif
+ifeq ($(ARCH), WINNT)
+#find out why this isn't needed on UNIX
+DLL_LDFLAGS += $(NSPRLINK) /IMPLIB:$(LIBSLAPD)
+DLL_LDFLAGS += -def:"libslapd.def"
+LIBSLAPD_DEF = $(LDAP_SRC)/servers/slapd/libslapd.def
+SUBSYSTEM=windows
+
+LDAP_COMMON_EXTRALIBSLIST = libsi18n
+LDAP_COMMON_EXTRALIBS = $(addsuffix .$(LIB_SUFFIX), \
+ $(addprefix $(LDAP_LIBDIR)/, $(LDAP_COMMON_EXTRALIBSLIST)))
+LDAP_COMMON_LINK += libbase.$(LIB_SUFFIX)
+
+EXTRA_LIBS_DEP = $(SECURITY_DEP) $(NSPR_DEP) \
+ $(LDAP_COMMON_LIBS_DEP) $(LDAPSDK_DEP) \
+ $(LDAP_SDK_LIBSSLDAP_LIB_DEP) $(LIBLDAPU_DEP) $(_ldap_db_depend) \
+ $(SVRCORE_DEP) \
+ $(LDAP_COMMON_EXTRALIBS)
+
+EXTRA_LIBS += $(LDAPLINK) $(LIBSVRCORE) $(LIBSECURITY) $(LIBNSPR) \
+ $(LDAP_COMMON_LINK) \
+ $(LIBLDAPU) \
+ $(LDAP_COMMON_EXTRALIBS)
+
+# JCM - Warnings as Errors!
+CFLAGS += /WX
+else
+LDFLAGS = $(SSLLIBFLAG)
+EXTRA_LIBS_DEP = $(SECURITY_DEP) \
+ $(NSPR_DEP) $(LDAPSDK_DEP) $(SVRCORE_DEP) \
+ $(LDAP_LIBLDBM_DEP) $(LDAP_LIBAVL_DEP) $(LDAP_LIBLDIF_DEP) \
+ $(_ldap_db_depend) $(SVRCORE_DEP)
+EXTRA_LIBS = $(LDAP_LIBLITEKEY) -lavl -lldif \
+ $(SVRCORELINK) $(LDAPLINK) \
+ $(SECURITYLINK) $(NSPRLINK) \
+ $(ALIBS) $(DYNALIBS) $(THREADSLIB)
+endif
+ifeq ($(ARCH), AIX)
+LD=ld
+EXTRA_LIBS = $(LDAPLINK) $(SVRCORELINK) $(SECURITYLINK) $(NSPRLINK) \
+ $(LDAP_COMMON_LINK) \
+ $(LIBLDAPU) \
+ $(DYNALIBS) $(THREADSLIB) \
+ $(DLL_EXTRA_LIBS) \
+ $(LDAP_LIBLITEKEY)
+endif
+
+# for Solaris, our most common unix build platform, we check for undefined
+# symbols at link time so we don't catch them at run time. To do this, we
+# set the -z defs flag. We also have to add explicitly link with the C and
+# C++ runtime libraries (e.g., -lc) because, even though ld and CC link
+# with them implicitly, -z defs will throw errors if we do not link with
+# them explicitly.
+ifeq ($(ARCH), SOLARIS)
+LINK_DLL += -z defs
+# removed -lcx from the following line
+EXTRA_LIBS += -lCstd -lCrun -lm -lw -lc
+# with the Forte 6 and later compilers, we must use CC to link
+LD=CC
+endif
+
+#ifeq ($(ARCH), OSF1)
+#DLL_LDFLAGS=-shared -all -error_unresolved -taso -ySVRCORE_RegisterPinObj
+#EXTRA_LIBS += -lc
+#endif
+
+OBJS = $(addprefix $(OBJDEST)/, $(LIBSLAPD_OBJS))
+ERRORMAP.O = $(addprefix $(OBJDEST)/, errormap.o)
+
+all: $(OBJDEST) $(LIBDIR) $(BINDIR) $(BUILD_DEP) $(LIBSLAPD_DLL) $(LIBSLAPD_RELDLLS)
+
+static: $(OBJDEST) $(LIBDIR) $(LIBSLAPD)
+
+dummy:
+ echo $(LINK_DLL)
+ echo $(EXTRA_LIBS)
+
+clientSDK: static
+
+$(LIBSLAPD_DLL): $(EXTRA_LIBS_DEP) $(OBJS) $(LIBSLAPD_DEF)
+ $(LINK_DLL) $(EXTRA_LIBS)
+
+veryclean: clean
+
+clean:
+ -$(RM) $(OBJS)
+ -$(RM) $(LIBSLAPD_DLL)
+
+# compilation dependencies:
+
+$(ERRORMAP.O): $(DIRVERDIR)/dberrstrs.h
+
+$(DIRVERDIR)/dberrstrs.h: $(DB_INCLUDE)/db.h
+ifeq ($(ARCH), WINNT)
+ $(PERL) mkDBErrStrs.pl -nt -i $(DB_INCLUDE) -o $(DIRVERDIR)
+else
+ $(PERL) mkDBErrStrs.pl -i $(DB_INCLUDE) -o $(DIRVERDIR)
+endif
+
+ifeq ($(ARCH), WINNT)
+$(OBJS): $(OBJDEST)/%.o : %.c
+endif
+
+# Target to push the built binary to an installed server
+LIBSLAPD_PUSH = $(addprefix $(INSTDIR)/, bin/slapd/server/libslapd.dll)
+push: $(LIBSLAPD_PUSH)
+
+$(LIBSLAPD_PUSH): $(LIBSLAPD_DLL)
+ cp $(LIBSLAPD_DLL) $(LIBSLAPD_PUSH)
+
diff --git a/ldap/servers/slapd/libsh_stub/Makefile b/ldap/servers/slapd/libsh_stub/Makefile
new file mode 100644
index 00000000..d82833c0
--- /dev/null
+++ b/ldap/servers/slapd/libsh_stub/Makefile
@@ -0,0 +1,63 @@
+#
+# BEGIN COPYRIGHT BLOCK
+# Copyright 2001 Sun Microsystems, Inc.
+# Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+# All rights reserved.
+# END COPYRIGHT BLOCK
+#
+#
+# GNU Makefile for libsh_stub.so
+#
+LDAP_SRC = ../../..
+MCOM_ROOT = ../../../../..
+
+NOSTDCLEAN=true # don't let nsconfig.mk define target clean
+NOSTDSTRIP=true # don't let nsconfig.mk define target strip
+
+OBJDEST = $(OBJDIR)/lib/libsh_stub
+
+include $(MCOM_ROOT)/ldapserver/nsconfig.mk
+include $(LDAP_SRC)/nsldap.mk
+include $(LDAP_SRC)/nsdeps.mk
+
+
+LIBSH_STUB_C= libsh_stub.c
+
+SHARE_LIB = $(addprefix $(OBJDEST)/, libsh_stub.so)
+
+# share lib
+CFLAGS = -G $(SLCFLAGS)
+
+# link libmtmalloc.so when built on 5.8 or 5.9
+ifeq ($(ARCH), SOLARIS)
+ ifeq ($(NSOS_RELEASE), 5.8)
+ LINKFLAGS = -lmtmalloc
+ endif
+ ifeq ($(NSOS_RELEASE), 5.9)
+ LINKFLAGS = -lmtmalloc
+ endif
+ LIBSH_STUB = libsh_stub
+else
+ # do nothing on non-SOLARIS platforms
+ LIBSH_STUB = no_op
+endif
+
+all: $(LIBSH_STUB)
+
+.PHONY: libsh_stub no_op
+
+libsh_stub: $(OBJDEST) $(SHARE_LIB)
+
+$(SHARE_LIB): $(LIBSH_STUB_C)
+ $(CC) -o $(SHARE_LIB) $(CFLAGS) $(LIBSH_STUB_C) $(LINKFLAGS)
+
+no_op:
+ -@echo libsh_stub.so is not built on $(ARCH) platform
+
+$(OBJDEST) :
+ $(MKDIR) $@
+
+veryclean: clean
+
+clean:
+ $(RM) $(SHARE_LIB)
diff --git a/ldap/servers/slapd/libsh_stub/libsh_stub.c b/ldap/servers/slapd/libsh_stub/libsh_stub.c
new file mode 100644
index 00000000..56a08c9f
--- /dev/null
+++ b/ldap/servers/slapd/libsh_stub/libsh_stub.c
@@ -0,0 +1,7 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+
+int MemRegisterTask(void) { return 0; }
diff --git a/ldap/servers/slapd/libslapd.def b/ldap/servers/slapd/libslapd.def
new file mode 100644
index 00000000..072439a9
--- /dev/null
+++ b/ldap/servers/slapd/libslapd.def
@@ -0,0 +1,1147 @@
+; BEGIN COPYRIGHT BLOCK
+; Copyright 2001 Sun Microsystems, Inc.
+; Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+; All rights reserved.
+; END COPYRIGHT BLOCK
+;
+DESCRIPTION 'Directory Server 7 Utility Library'
+EXPORTS
+ slapd_log_error_proc @2
+ slapd_log_audit_proc @3
+ ldapi_init_extended_ops @4
+ audit_log_openf @5
+ slapi_log_error @6
+ slapi_log_access @7
+ g_set_detached @8
+ access_log_openf @9
+ error_log_openf @10
+ slapi_ch_strdup @11
+ slapi_ch_malloc @12
+ slapi_ch_calloc @13
+ slapi_ch_realloc @14
+ slapi_ch_free @15
+ slapi_sdn_scope_test @16
+ slapi_pblock_get @17
+ slapi_pblock_set @18
+ slapi_str2entry @19
+ slapi_entry2str @20
+ slapi_entry_alloc @21
+ slapi_entry_free @22
+ slapi_entry_dup @23
+ slapi_entry_get_dn @24
+ slapi_entry_set_dn @25
+ slapi_entry_attr_find @26
+ slapi_entry_first_attr @27
+ slapi_entry_next_attr @28
+ slapi_entry_attr_merge @29
+ slapi_entry_schema_check @30
+ slapi_entry_add_values @31
+ slapi_entry_delete_values @32
+ g_get_global_oc_nolock @34
+ config_set_maxbersize @35
+ g_get_global_lastmod @36
+ current_time @37
+ slapi_attr_free @38
+ attr_syntax_create @39
+ valueset_get_valuearray @40
+ attrlist_merge @41
+ attrlist_find @42
+ attrlist_delete @43
+ slapi_register_plugin @44
+ csn_max @45
+ slapi_dn_normalize @46
+ slapi_dn_ignore_case @47
+ slapi_dn_normalize_case @48
+ slapi_dn_parent @49
+ slapi_dn_issuffix @50
+ slapi_attr_get_valueset @51
+ slapi_read_buffer @52
+ slapi_write_buffer @53
+ add_control @54
+ pblock_init @56
+ pblock_init_common @57
+ slapi_pblock_get_common @58
+ slapi_valueset_count @59
+ slapi_mtn_get_backend_name @60
+ get_timestring @61
+ free_timestring @62
+ slapi_op_abandoned @63
+ slapi_filter_get_choice @64
+ slapi_filter_get_ava @65
+ slapi_filter_get_type @66
+ slapi_filter_get_subfilt @67
+ slapi_filter_list_first @68
+ slapi_filter_list_next @69
+ slapi_filter_join @70
+ slapi_filter_free @71
+ slapi_filter_test @72
+ slapi_access_allowed @73
+ slapi_acl_check_mods @74
+ dse_search @75
+ slapi_pw_find @76
+ send_read_referrals @77
+ ref_array_dup @78
+ ref_array_dup_free @79
+ slapi_dn_beparent @80
+ slapi_ldap_init @81
+ slapi_ldap_unbind @82
+ slapi_dn_isroot @83
+ slapi_dn_isbesuffix @84
+ slapi_value_new_berval @85
+ plugin_call_plugins @86
+ slapi_mtn_be_disable @87
+ slapi_be_new @88
+ get_operation_object_type @89
+ be_flushall @90
+ slapi_be_select @91
+ charray_free @92
+ charray_dup @93
+ slapi_str2filter @94
+ charray_add @95
+ value_done @96
+ rdn2ava @97
+ pw_encodevals @98
+ slapi_valueset_set_from_smod @99
+ ava_done @100
+ pw_name2scheme @101
+ generate_componentid @102
+ g_set_deftime @103
+ g_set_defsize @104
+ plugin_setup @105
+ slapi_new_mutex @106
+ slapi_destroy_mutex @107
+ slapi_lock_mutex @108
+ slapi_unlock_mutex @109
+ slapi_valueset_set_valueset @110
+ dse_modify @111
+ plugin_call_acl_plugin @112
+ get_filter @113
+ be_unbindall @114
+ plugin_call_exop_plugins @115
+ filter_normalize @116
+ plugin_call_acl_mods_access @117
+ normalize_oc @118
+ ref_adjust @119
+ config_get_maxbersize @120
+ entry_replace_values @121
+ filter_strcpy_special @122
+ be_addsuffix @123
+ slapi_be_getsuffix @124
+ slapi_entry_attr_hasvalue @125
+ sym_load @126
+ str2charray @127
+ charray_merge @128
+ libldap_init_debug_level @129
+ slapi_seq_callback @130
+ slapi_be_getentrypoint @131
+ write_audit_log_entry @132
+ rwl_new @133
+ slapi_send_ldap_search_entry @134
+ slapi_send_ldap_result @135
+ slapi_send_ldap_referral @136
+ g_log_init @137
+ slapi_get_first_backend @138
+ slapi_get_next_backend @139
+ be_cleanupall @140
+ slapi_attr_new @141
+ slapi_acl_verify_aci_syntax @142
+ plugin_call_acl_mods_update @143
+ plugin_call_acl_verify_syntax @144
+ poll_current_time @145
+ slapi_dn_isparent @146
+ freepmods @147
+ get_ldapmessage_controls @148
+ init_controls @149
+ slapi_control_present @150
+ plugin_closeall @151
+ plugin_startall @152
+ internal_res_callback @153
+ internal_srch_entry_callback @154
+ internal_ref_entry_callback @155
+ slapi_search_internal_callback @156
+ slapi_search_internal @157
+ slapi_modify_internal @158
+ normalize_mods2bvals @159
+ slapi_add_internal @160
+ slapi_delete_internal @161
+ slapi_modrdn_internal @162
+ slapi_compare_internal @163
+ slapi_free_search_results_internal @164
+ send_ldap_result @165
+ send_ldapv3_referral @166
+ send_ldap_referral @167
+ send_ldap_search_entry @168
+ slapi_mtn_be_enable @169
+ slapi_attr_get_oid_copy @170
+ set_db_default_result_handlers @171
+ g_get_num_bytes_sent @172
+ g_set_num_bytes_sent @173
+ g_get_num_entries_sent @174
+ g_set_num_entries_sent @175
+ g_get_num_sent_mutex @176
+ g_set_num_sent_mutex @177
+ g_get_default_referral @178
+ g_set_default_referral @179
+ slapi_ch_bvdup @180
+ rwl_free @181
+ get_server_dataversion @182
+ get_localhost_DNS @183
+ get_localhost_DN @184
+ slapi_pblock_new @185
+ slapi_pblock_destroy @186
+ slapi_ch_bvecdup @187
+ slapi_mr_filter_index @188
+ slapi_mr_indexer_create @189
+ slapi_berval_cmp @190
+ filter_print @191
+ write_controls @192
+ modify_schema_dse @193
+ read_schema_dse @194
+ attr_syntax_add @195
+ addlenstr @196
+ config_set_versionstring @197
+ slapi_entry_attr_replace @198
+ slapi_be_select_by_instance_name @199
+ slapd_ssl_init @200
+ slapd_SSL_client_init @201
+ format_localTime @202
+ parse_localTime @203
+ strntoul @204
+ time_plus_sec @205
+ read_localTime @206
+ get_entry @207
+ slapi_get_supported_controls_copy @208
+ attrlist_replace @209
+ update_pw_retry @210
+ g_get_global_snmp_vars @211
+ snmp_collator_start @212
+ snmp_collator_stop @213
+ config_set_schema_ignore_trailing_spaces @214
+ send_nobackend_ldap_result @215
+ set_dll_entry_points @216
+ slapi_add_entry_internal @217
+ dse_register_callback @218
+ slapi_entry_init @219
+ slapd_ldap_debug @220 DATA
+ slapd_ssl_init2 @221
+ slapd_SSL_client_bind_s @222
+ slapd_ssl_getCipherSuiteInfo @223
+ slapi_mods_dump @224
+ g_get_slapd_security_on @225
+ slapi_register_supported_control @226
+ slapi_get_supported_controls @227
+ slapi_register_supported_saslmechanism @228
+ slapi_get_supported_saslmechanisms_copy @229
+ slapi_get_supported_saslmechanisms @230
+ bervalarray_add_berval_fast @231
+ slapi_get_supported_extended_ops_copy @232
+ slapi_get_supported_extended_ops @233
+ slapi_be_logchanges @234
+ slapi_rdn_remove_attr @235
+ slapd_security_library_is_initialized @236
+ slapi_ch_bvfree @237
+ slapi_config_register_callback @238
+ slapi_entry_has_children @239
+ slapi_config_remove_callback @240
+ entry_apply_mods @241
+ slapi_entry_set_flag @242
+ slapi_entry_clear_flag @243
+ write_localTime @244
+ get_entry_object_type @245
+ factory_create_extension @246
+ factory_destroy_extension @247
+ factory_register_type @248
+ g_get_access_log @249
+ g_get_error_log @250
+ get_entry_point @251
+ set_snmp_interaction_row @252
+ slapi_attr_get_type @254
+ slapi_attr_get_oid @255
+ slapi_attr_get_values @256
+ slapi_attr_get_flags @257
+ slapi_attr_flag_is_set @258
+ slapi_attr_value_cmp @259
+ plugin_syntax_find @260
+ plugin_syntax2oid @261
+ plugin_call_syntax_filter_ava @262
+ plugin_call_syntax_filter_sub @263
+ slapi_call_syntax_values2keys @264
+ slapi_call_syntax_assertion2keys_ava @265
+ slapi_call_syntax_assertion2keys_sub @266
+ slapi_attr_value_find @267
+ slapi_attr_type2plugin @268
+ slapd_re_comp @269
+ slapd_re_exec @270
+ slapd_re_modw @271
+ slapd_re_subs @272
+ FrontendConfig_init @273
+ slapi_entry_get_dn_const @274
+ slapi_register_object_extension @275
+ slapi_get_object_extension @276
+ slapi_be_delete_onexit @277
+ slapi_be_exist @278
+ slapi_sdn_add_rdn @279
+ slapi_mods_add_string @280
+ lenstr_free @281
+ lenstr_new @282
+ plugin_call_syntax_get_compare_fn @283
+ slapi_attr_type_cmp @284
+ slapi_attr_basetype @285
+ snmp_collator_update @286
+ escape_string @287
+ eq_start @288
+ eq_stop @289
+ slapi_rdn2typeval @290
+ slapi_entry_rdn_values_present @291
+ slapi_entry_attr_get_long @292
+ slapi_dn_plus_rdn @293
+ ref_array_replace @294
+ slapi_setbit_uchar @295
+ slapi_unsetbit_uchar @296
+ slapi_isbitset_uchar @297
+ slapi_setbit_int @298
+ slapi_unsetbit_int @299
+ referrals_free @300
+ slapi_isbitset_int @301
+ slapi_be_private @302
+ ref_array_moddn @304
+ g_set_current_conn_count_mutex @305
+ g_get_current_conn_count_mutex @306
+ g_get_current_conn_count @307
+ g_increment_current_conn_count @308
+ g_decrement_current_conn_count @309
+ g_set_accesslog_level @310
+ slapi_entry_add_rdn_values @311
+ slapd_re_init @312
+ slapd_re_lock @313
+ slapd_re_unlock @314
+ escape_string_with_punctuation @315
+ getFrontendConfig @316
+ log_set_numlogsperdir @317
+ log_set_logsize @318
+ log_set_rotationtime @319
+ log_set_rotationtimeunit @320
+ log_set_maxdiskspace @321
+ log_set_mindiskspace @322
+ log_set_expirationtime @323
+ log_set_expirationtimeunit @324
+ log_update_accesslogdir @325
+ log_update_errorlogdir @326
+ dse_remove_callback @327
+ log_get_loglist @328
+ log_update_auditlogdir @329
+ g_get_audit_log @330
+ dse_new @331
+ dse_deletedse @332
+ dse_read_file @333
+ slapi_attr_get_bervals_copy @334
+ strarray2str @335
+ operation_out_of_disk_space @336
+ dse_delete @337
+ oc_lock_read @338
+ oc_lock_write @339
+ oc_unlock @340
+ slapi_entry_attr_set_long @341
+ slapi_entry_attr_delete @342
+ slapi_entry_attr_get_charptr @343
+ slapi_entry_attr_set_charptr @344
+ slapi_entry_attr_get_int @345
+ slapi_get_global_syntax_plugins @346
+ slapi_get_global_mr_plugins @347
+ slapi_new_condvar @348
+ slapi_destroy_condvar @349
+ slapi_wait_condvar @350
+ slapi_notify_condvar @351
+ defbackend_init @352
+ defbackend_get_backend @353
+ log_set_logging @354
+ pwpolicy_make_response_control @355
+ delete_passwdPolicy @356
+ g_get_user_backend @357
+ new_passwdPolicy @358
+ slapi_dn_normalize_to_end @359
+ slapi_ch_free_string @360
+ send_ldap_search_entry_ext @361
+ send_ldap_result_ext @362
+ slapd_pr_strerror @363
+ slapd_system_strerror @364
+ cool_charray_free @365
+ cool_charray_dup @366
+ ldapi_register_extended_op @367
+ config_set_attrname_exceptions @368
+ g_get_deftime @369
+ g_get_defsize @370
+ pw_val2scheme @371
+ slapi_attr_add_value @372
+ be_getconfigdn @373
+ be_getmonitordn @374
+ be_nbackends_public @375
+ entry_add_rdn_csn @376
+ slapd_versatile_strerror @377
+ config_set_enquote_sup_oc @378
+ config_get_enquote_sup_oc @379
+ log_access_flush @380
+ be_writeconfig @381
+ plugin_extended_op_oid2string @382
+ slapi_eq_get_arg @383
+ g_set_shutdown @384
+ g_get_shutdown @385
+ compute_init @386
+ compute_terminate @387
+ slapi_compute_add_evaluator @388
+ be_new_internal @389
+ slapi_schema_expand_objectclasses @390
+ slapi_compute_add_search_rewriter @391
+ g_get_global_mrl @392
+ g_set_global_mrl @393
+ slapi_matchingrule_new @394
+ slapi_matchingrule_free @395
+ slapi_matchingrule_register @396
+ slapi_matchingrule_unregister @397
+ slapi_matchingrule_get @398
+ slapi_matchingrule_set @399
+ config_set_port @400
+; Available for reuse @401
+ config_set_secureport @402
+ config_set_SSLclientAuth @403
+ config_set_workingdir @404
+ config_set_localhost @405
+ config_set_listenhost @406
+ config_set_securelistenhost @407
+ config_set_srvtab @408
+ config_set_sizelimit @409
+ config_set_pw_storagescheme @410
+ slapi_filter_test_ext @411
+ config_set_pw_change @412
+ config_set_pw_history @413
+ config_set_pw_must_change @414
+ config_set_pw_syntax @415
+ config_set_pw_minlength @416
+ config_set_pw_maxfailure @417
+ config_set_pw_inhistory @418
+ config_set_pw_lockduration @419
+ config_set_pw_resetfailurecount @420
+ config_set_pw_exp @421
+ config_set_pw_unlock @422
+ config_set_pw_lockout @423
+ config_set_lastmod @424
+ config_set_nagle @425
+ config_set_accesscontrol @426
+ config_set_result_tweak @427
+ config_set_pw_gracelimit @428
+; Available for reuse @429
+ config_set_security @430
+ config_set_pwpolicy_local @431
+ config_set_readonly @432
+ config_set_schemacheck @433
+ config_set_rootdn @434
+ config_set_rootpw @435
+ config_set_rootpwstoragescheme @436
+ slapi_build_control @437
+ slapi_entry_get_ndn @438
+ dse_unset_dont_ever_write_dse_files @439
+ config_set_instancedir @440
+ config_set_encryptionalias @441
+ config_set_threadnumber @442
+ config_set_maxthreadsperconn @443
+ slapi_eq_repeat @444
+ config_set_reservedescriptors @445
+ config_set_ioblocktimeout @446
+ config_set_idletimeout @447
+ config_set_groupevalnestlevel @448
+ config_set_defaultreferral @449
+ config_set_userat @450
+ config_set_timelimit @451
+ config_set_useroc @452
+ config_set_accesslog @453
+ config_set_errorlog @454
+ config_set_auditlog @455
+ config_set_pw_maxage @456
+ config_set_pw_minage @457
+ config_set_pw_warning @458
+ config_set_errorlog_level @459
+ config_set_accesslog_level @460
+ compute_rewrite_search_filter @461
+ slapi_entry_get_sdn_const @462
+ dse_set_dont_ever_write_dse_files @463
+ slapi_eq_once @464
+ config_get_port @465
+ config_get_pw_is_global_policy @466
+ config_get_secureport @467
+ config_get_SSLclientAuth @468
+ config_get_workingdir @469
+ config_get_localhost @470
+ config_get_listenhost @471
+ config_get_securelistenhost @472
+ config_get_srvtab @473
+ config_get_sizelimit @474
+ config_get_pw_storagescheme @475
+ slapi_eq_cancel @476
+ config_get_pw_change @477
+ config_get_pw_history @478
+ config_get_pw_must_change @479
+ config_get_pw_syntax @480
+ config_get_pw_minlength @481
+ config_get_pw_maxfailure @482
+ config_get_pw_inhistory @483
+ config_get_pw_lockduration @484
+ config_get_pw_resetfailurecount @485
+ config_get_pw_exp @486
+ config_get_pw_unlock @487
+ config_get_pw_lockout @488
+ config_get_lastmod @489
+ config_get_nagle @490
+ config_get_accesscontrol @491
+ config_get_result_tweak @492
+ config_get_conntablesize @493
+ config_get_pw_gracelimit @494
+ config_get_security @495
+ slapi_config_get_readonly @496
+ config_get_schemacheck @497
+ config_get_rootdn @498
+ config_get_rootpw @499
+ config_get_rootpwstoragescheme @500
+ slapi_entry_set_sdn @501
+ slapi_sdn_copy @502
+ config_set_basedn @503
+ config_get_instancedir @504
+ config_get_encryptionalias @505
+ config_get_threadnumber @506
+ config_get_maxthreadsperconn @507
+ config_get_basedn @508
+ config_get_reservedescriptors @509
+ config_get_ioblocktimeout @510
+ config_get_idletimeout @511
+ config_get_groupevalnestlevel @512
+ config_get_defaultreferral @513
+ config_get_userat @514
+ config_get_timelimit @515
+ config_get_useroc @516
+ config_get_accesslog @517
+ config_get_errorlog @518
+ config_get_auditlog @519
+ config_get_pw_maxage @520
+ config_get_pw_minage @521
+ config_get_pw_warning @522
+ config_get_errorlog_level @523
+ config_get_accesslog_level @524
+ slapi_sdn_compare @525
+ slapi_sdn_dup @526
+ slapi_sdn_set_dn_passin @527
+ slapi_entry_get_sdn @528
+ slapi_sdn_get_ndn_len @529
+ charray_inlist @530
+ config_set_referral_mode @531
+ config_get_referral_mode @532
+ config_check_referral_mode @533
+ attr_set_deletion_csn @534
+ slapi_mod_dump @535
+ config_is_slapd_lite @536
+ config_set_slapd_type @537
+ config_get_versionstring @538
+ slapi_is_rootdse @539
+ slapi_find_matching_paren @540
+ str2simple @541
+ substr_dn_normalize @542
+ get_data_source @543
+ slapi_sdn_get_backend_parent @544
+ slapi_sdn_new_dn_passin @545
+ slapi_sdn_isempty @546
+ slapi_sdn_isparent @547
+ attr_value_find_wsi @548
+ entry_computed_attr_init @549
+ slapi_attr_init @550
+ slapi_value_init @551
+ valueset_isempty @552
+ valueset_add_string @553
+ attr_done @554
+ operation_clear_flag @555
+ operation_set_flag @556
+ slapi_sdn_get_rdn @557
+ slapi_sdn_set_rdn @558
+ slapi_sdn_set_parent @559
+ slapi_entry_delete_string @560
+ attr_get_deletion_csn @561
+ entry_add_present_attribute_wsi @562
+ slapi_value_get_length @563
+ operation_parameters_dup @564
+ operation_parameters_free @565
+ operation_is_flag_set @566
+ counters_as_entry @567
+ counters_to_errors_log @568
+ counters_as_entry @567
+ counters_to_errors_log @568
+ slapi_value_new_string @569
+ charray_utf8_inlist @570
+ charray_get_index @571
+; LDAPU ENTRY POINTS
+ ldapu_member_certificate_match @580
+ ref_register_callback @581
+ ref_remove_callback @582
+ slapi_is_encoded @583
+ slapi_encode @584
+; UniqueID ENTRY POINTS
+ slapi_uniqueIDNew @585
+ slapi_uniqueIDDestroy @586
+ slapi_uniqueIDCompare @587
+ slapi_uniqueIDFormat @588
+ slapi_uniqueIDScan @589
+ slapi_uniqueIDIsUUID @590
+ slapi_uniqueIDSize @591
+ slapi_uniqueIDDup @592
+ uniqueIDGenInit @593
+ uniqueIDGenCleanup @594
+ slapi_uniqueIDCompareString @595
+ slapi_uniqueIDGenerate @596
+ slapi_uniqueIDGenerateString @597
+ slapi_uniqueIDGenerateFromName @600
+ slapi_uniqueIDGenerateFromNameString @601
+; MORE ENTRY ENTRY POINTS
+ slapi_entry_get_uniqueid @602
+ slapi_entry_set_uniqueid @603
+ slapi_entry_merge_values_sv @604
+; Slapi_DN entry points
+ slapi_sdn_new @605
+ slapi_sdn_init @606
+ slapi_sdn_new_dn_byval @607
+ slapi_sdn_new_ndn_byval @608
+; slapi_sdn_new_cndn_byval @609
+ slapi_sdn_new_dn_byref @610
+ slapi_sdn_new_ndn_byref @611
+; slapi_sdn_new_cndn_byref @612
+ slapi_sdn_set_dn_byval @613
+ slapi_sdn_set_dn_byref @614
+ slapi_sdn_set_ndn_byval @615
+ slapi_sdn_set_ndn_byref @616
+; slapi_sdn_set_cndn_byval @617
+; slapi_sdn_set_cndn_byref @618
+ slapi_sdn_done @619
+ slapi_sdn_free @620
+ slapi_sdn_get_dn @621
+ slapi_sdn_get_ndn @622
+; slapi_sdn_get_cndn @623
+ slapi_sdn_get_parent @624
+; Slapi_Mod & Slapi_Mods entry points
+ slapi_mods_init @625
+ slapi_mods_insert_at @626
+ slapi_mods_insert_before @627
+ slapi_mods_insert_after @628
+ slapi_mods_add @629
+ slapi_mods_add_ldapmod @630
+ slapi_mods_add_modbvps @631
+ slapi_mods_remove @632
+ slapi_mods_get_first_mod @633
+ slapi_mods_get_next_mod @634
+ slapi_mods_iterator_backone @635
+ slapi_mods_get_ldapmods_byref @636
+ slapi_mods_get_num_mods @637
+ slapi_mod_init @638
+ slapi_mod_add_value @639
+ slapi_mod_remove_value @640
+ slapi_mod_get_first_value @641
+ slapi_mod_get_next_value @642
+ slapi_mod_get_num_values @643
+ entry_purge_state_information @644
+ attr_set_csn @645
+ entry_get_dncsn @646
+ entry_get_dncsnset @647
+ entry_add_dncsn @648
+ value_contains_csn @649
+ csnset_get_previous_csn @650
+ task_init @651
+ attrlist_merge_valuearray @652
+ slapi_value_new_value @653
+ entry_get_maxcsn @654
+ entry_set_maxcsn @655
+ entry_assign_operation_csn @656
+ slapi_operation_set_csngen_handler @657
+ slapi_operation_set_replica_attr_handler @658
+ slapi_operation_get_replica_attr @659
+ plugin_call_syntax_filter_sub_sv @664
+ plugin_call_syntax_filter_ava_sv @665
+ slapi_pw_find_sv @666
+ csn_new_by_string @667
+ csn_set_replicaid @668
+ valuearray_get_bervalarray @670
+ slapi_call_syntax_values2keys_sv @671
+ valuearray_init_bervalarray @672
+ slapi_valueset_free @673
+ slapi_entry_add_values_sv @674
+ entry_set_csn @675
+ csn_as_string @676
+ entry_attr_find_wsi @678
+ csn_get_time @679
+ csn_new @680
+ csn_set_time @681
+ csn_get_seqnum @682
+ csn_free @683
+ slapi_search_internal_get_entry @684
+ csn_compare @685
+ csn_set_seqnum @686
+ csn_dup @687
+ slapi_valueset_first_value @688
+ slapi_valueset_next_value @689
+ slapi_valueset_done @690
+ slapi_utf8StrToLower @691
+ slapi_utf8ToLower @692
+ slapi_utf8isUpper @693
+ slapi_utf8StrToUpper @694
+ slapi_utf8ToUpper @695
+ slapi_utf8isLower @696
+ slapi_utf8casecmp @697
+ slapi_utf8ncasecmp @698
+ slapi_has8thBit @699
+ slapi_sdn_init_dn_byref @700
+ slapi_sdn_init_dn_byval @701
+ slapi_sdn_init_dn_passin @702
+ slapi_attr_first_value @703
+ slapi_attr_next_value @704
+ slapi_value_get_berval @705
+ slapi_attr_get_numvalues @706
+ value_get_csn @707
+ value_update_csn @708
+ slapi_value_set_berval @709
+ slapi_mods_init_byref @711
+ slapi_mods_init_passin @712
+ slapi_value_new @713
+ slapi_value_free @714
+ value_add_csn @715
+ value_remove_csn @716
+ slapi_sdn_issuffix @717
+ slapi_mods_new @718
+ slapi_mods_free @719
+ slapi_mods_insert_smod_at @720
+ slapi_mods_insert_smod_before @721
+ slapi_mods_insert_smod_after @722
+ slapi_mods_add_smod @723
+ slapi_mods_get_first_smod @724
+ slapi_mods_get_next_smod @725
+ slapi_mod_new @726
+ slapi_mod_init_byref @727
+ slapi_mod_init_passin @728
+ slapi_value_init_string @729
+ slapi_mod_get_ldapmod_passout @730
+ slapi_mod_get_type @731
+ slapi_mod_get_operation @732
+ slapi_mod_set_type @733
+ slapi_mod_set_operation @734
+ slapi_mod_get_ldapmod_byref @735
+ slapi_mod_free @736
+ csn_time_difference @737
+ slapi_mod_isvalid @738
+ slapi_entry_size @739
+ attr_first_deleted_value @740
+ attr_next_deleted_value @741
+ slapi_mods_get_ldapmods_passout @742
+ slapi_value_init_berval @743
+ entry_add_dncsn_ext @744
+ slapi_value_set @745
+ operation_get_csn @750
+ entry_apply_mods_wsi @751
+ slapi_mod_done @752
+ slapi_mods_done @753
+ operation_set_csn @754
+; entry_update_deleted_attribute @755
+ entry_first_deleted_attribute @756
+ entry_next_deleted_attribute @757
+; config_get_storestateinfo @758
+; config_set_storestateinfo @759
+ slapi_value_set_string @760
+ slapi_is_loglevel_set @761
+ operation_set_target_spec @762
+ operation_set_target_spec_str @763
+ operation_get_target_spec @764
+ operation_set_abandoned_op @765
+ operation_get_abandoned_op @766
+ slapi_value_get_string @767
+ slapi_value_get_int @768
+ slapi_value_set_int @769
+ slapi_add_internal_pb @770
+ slapi_add_internal_set_pb @771
+ slapi_modify_internal_set_pb @772
+ slapi_modify_internal_pb @773
+ slapi_modrdn_internal_pb @774
+ slapi_rename_internal_set_pb @775
+ slapi_delete_internal_set_pb @776
+ slapi_delete_internal_pb @777
+ slapi_search_internal_pb @778
+ slapi_search_internal_set_pb @779
+ slapi_search_internal_callback_pb @780
+ plugin_build_operation_action_bitmap @781
+ plugin_get_server_plg @782
+ add_pwd_control @783
+ pw_mod_allowchange_aci @784
+ do_add @785
+ do_modify @786
+ do_delete @787
+ do_modrdn @788
+ op_shared_search @789
+ slapi_mod_init_byval @790
+ slapi_add_entry_internal_set_pb @792
+ config_set_return_exact_case @793
+ slapi_rdn_new @794
+ slapi_rdn_new_dn @795
+ slapi_rdn_new_sdn @796
+ slapi_rdn_new_rdn @797
+ slapi_rdn_init @798
+ slapi_rdn_init_dn @799
+ slapi_rdn_init_sdn @800
+ slapi_rdn_init_rdn @801
+ slapi_rdn_set_dn @802
+ slapi_rdn_set_sdn @803
+ slapi_rdn_set_rdn @804
+ slapi_rdn_free @805
+ slapi_rdn_done @806
+ slapi_rdn_get_first @807
+ slapi_rdn_get_next @808
+ slapi_rdn_get_index @809
+ slapi_rdn_contains @810
+ slapi_rdn_add @811
+ slapi_rdn_remove_index @812
+ slapi_rdn_remove @813
+ slapi_rdn_isempty @814
+ slapi_rdn_get_num_components @815
+ slapi_rdn_compare @816
+ slapi_rdn_get_rdn @817
+ slapi_rdn_get_nrdn @819
+ slapi_value_dup @820
+ slapi_value_set_value @821
+ rel2abspath @822
+ slapi_value_compare @823
+ attr_get_present_values @824
+ dl_get @825
+ dl_new @826
+ dl_free @827
+ dl_init @828
+ dl_cleanup @829
+ dl_add @830
+ dl_get_first @831
+ dl_get_next @832
+ dl_delete @833
+ dl_get_count @834
+ slapi_entry2mods @837
+ slapi_mods2entry @838
+ operation_parameters_new @839
+ operation_parameters_done @840
+ slapi_ch_start_recording @841
+ slapi_ch_stop_recording @842
+ snmp_as_entry @843
+ slapi_filter_compare @844
+; probably temporary:
+ set_hash_filters @845
+ operation_new @846
+ operation_free @847
+ operation_set_type @848
+ slapi_mods_add_mod_values @849
+ slapi_sdn_init_ndn_byref @850
+ slapi_sdn_init_ndn_byval @851
+ objset_new @852
+ objset_delete @853
+ objset_add_obj @854
+ objset_remove_obj @855
+ objset_find @856
+ objset_first_obj @857
+ objset_next_obj @858
+ objset_is_empty @859
+ object_new @860
+ object_acquire @861
+ object_release @862
+ object_get_data @863
+ slapi_UTF8STRTOLOWER @864
+ slapi_UTF8TOLOWER @865
+ slapi_UTF8ISUPPER @866
+ slapi_UTF8STRTOUPPER @867
+ slapi_UTF8TOUPPER @868
+ slapi_UTF8ISLOWER @869
+ slapi_UTF8CASECMP @870
+ slapi_UTF8NCASECMP @871
+ slapi_apib_get_interface @872
+ slapi_apib_unregister @873
+ slapi_apib_register @874
+ slapi_attr_types_equivalent @875
+ dse_read_next_entry @895
+ config_set_entry @896
+ config_set @897
+ init_schema_dse @898
+ PL_HashTableLookup_const @899
+; dse_add_entry_pb @900
+ vattr_init @901
+ vattr_cleanup @902
+ slapi_vattrspi_register @903
+ plugin_get_by_name @904
+ objset_size @905
+ slapi_attr_dup @906
+ slapi_entry_add_value @907
+ slapi_entry_add_string @908
+ be_create_instance @909
+ be_remove_instance @910
+ mapping_tree_init @911
+ slapi_mapping_tree_select @912
+ slapi_sdn_isgrandparent @913
+ config_set_storagescheme @914
+ slapi_berval_get_string_copy @918
+ slapi_vattr_value_compare @919
+ slapi_vattr_value_compare_sp @920
+ slapi_vattr_values_get_sp @921
+ slapi_vattr_values_get @922
+ slapi_vattr_values_free @923
+ slapi_vattr_list_attrs @924
+ slapi_vattr_attrs_free @925
+ slapi_vattrspi_regattr @926
+ slapi_be_get_instance_info @927
+ slapi_be_set_instance_info @928
+ slapi_be_setentrypoint @929
+ plugin_get_dn @930
+ operation_get_type @931
+ slapi_be_set_flag @932
+ slapi_be_is_flag_set @933
+ free_pw_scheme @934
+ slapi_vattrspi_add_type @935
+ mapping_tree_free @936
+ slapi_get_mapping_tree_node_by_dn @937
+ slapi_get_mapping_tree_node_configdn @938
+ slapi_valueset_init @939
+ slapi_valueset_add_value @940
+ slapi_filter_get_attribute_type @941
+ slapi_filter_apply @942
+ slapi_attr_syntax_normalize @943
+ charray_remove @946
+; csn_next_in_sequence @947
+ csngen_new @948
+ csngen_free @949
+ csngen_new_csn @950
+ csngen_abort_csn @951
+ csngen_adjust_time @952
+ csngen_is_local_csn @953
+ csngen_register_callbacks @954
+ csngen_unregister_callbacks @955
+ csngen_update_time @956
+ csngen_get_state @957
+ csn_init_by_csn @958
+ csn_init_by_string @959
+ csn_get_replicaid @960
+ csn_string_size @961
+ csn_as_attr_option_string @962
+ slapi_get_mapping_tree_node_root @963
+ slapi_get_mapping_tree_config_root @964
+ mapping_tree_get_extension_type @965
+ slapi_mapping_tree_node_is_set @966
+ slapi_entry_flag_is_set @967
+ slapi_task_status_changed @968
+ slapi_valueset_new @969
+ slapi_be_issuffix @970
+ slapi_be_addsuffix @971
+ slapi_reslimit_register @972
+ slapi_reslimit_get_integer_limit @973
+ reslimit_cleanup @974
+ search_register_reslimits @975
+ bind_credentials_set @976
+ bind_credentials_clear @977
+ pw_add_allowchange_aci @978
+ slapi_valueset_add_value_ext @979
+ is_abspath @980
+ slapi_dup_control @981
+ slapi_get_first_suffix @982
+ slapi_get_next_suffix @983
+ slapi_is_root_suffix @984
+ slapi_be_get_name @985
+ slapi_entry2str_with_options @986
+ slapi_destructive_rename @987
+ slapi_moddn_get_newdn @988
+ plugin_get_default_component_id @989
+ slapi_be_gettype @990
+ csnset_get_first_csn @991
+ csnset_get_next_csn @992
+ value_get_csnset @993
+ entry_add_deleted_attribute_wsi @994
+ attr_add_deleted_value @995
+ slapi_disconnect_server @996
+ eq_init @997
+ slapi_set_object_extension @998
+ task_shutdown @999
+ slapi_mtn_set_referral @1000
+ slapi_mtn_set_state @1001
+ slapi_mtn_get_referral @1002
+ slapi_mtn_get_state @1003
+ slapi_mtn_be_started @1004
+ slapi_mtn_be_stopping @1005
+ slapi_start_bulk_import @1006
+ slapi_stop_bulk_import @1007
+ slapi_import_entry @1008
+ slapi_entry_add_valueset @1009
+ vattr_typethang_get_flags @1010
+ vattr_typethang_get_name @1011
+ vattr_typethang_next @1012
+ vattr_typethang_first @1013
+ charray_subtract @1014
+ slapi_schema_list_attribute_names @1015
+ config_get_ds4_compatible_schema @1016
+ config_set_ds4_compatible_schema @1017
+ pw_apply_mods @1018
+ pw_set_componentID @1019
+ pw_get_componentID @1020
+ parse_genTime @1021
+ format_genTime @1022
+ be_set_sizelimit @1023
+ be_set_timelimit @1024
+ slapi_be_free @1025
+ slapi_be_Unlock @1026
+ slapi_task_log_status @1027
+ slapi_task_log_notice @1028
+ plugin_add_descriptive_attributes @1029
+
+ slapi_get_rootdn @1030
+ slapi_mtn_be_set_readonly @1031
+ slapi_be_set_readonly @1032
+ slapi_be_get_readonly @1033
+ slapi_op_get_type @1034
+ slapi_entry_attr_get_ulong @1035
+ slapi_entry_attr_get_uint @1036
+ slapi_entry_attr_set_int @1037
+ slapi_entry_attr_set_ulong @1038
+ slapi_entry_attr_set_uint @1039
+ slapi_value_get_ulong @1040
+ slapi_value_get_uint @1041
+
+ config_set_rewrite_rfc1274 @1042
+ config_get_rewrite_rfc1274 @1043
+ slapi_mapping_tree_find_backend_for_sdn @1044
+ slapi_register_backend_state_change @1045
+ slapi_unregister_backend_state_change @1046
+
+ slapd_ssl_handshakeCallback @1047
+ slapd_ssl_badCertHook @1048
+ slapd_ssl_peerCertificate @1049
+ slapd_ssl_getChannelInfo @1050
+ pblock_done @1051
+ pw_rever_decode @1052
+ slapd_ssl_listener_is_initialized @1053
+ op_shared_log_error_access @1054
+ slapd_ssl_importFD @1055
+ slapd_ssl_resetHandshake @1056
+ slapi_build_control_from_berval @1057
+; MORE ENTRY ENTRY POINTS
+ slapi_entry_delete_values_sv @1058
+ slapi_entry_attr_replace_sv @1059
+
+ valuearray_free @1061
+ slapd_Client_auth @1062
+ slapi_rand_r @1063
+ slapi_rand @1064
+ slapi_copy @1065
+ slapd_get_tmp_dir @1066
+ slapi_call_syntax_assertion2keys_ava_sv @1067
+ slapi_call_syntax_assertion2keys_sub_sv @1068
+ slapi_value_get_long @1069
+ valuearray_add_valuearray @1070
+ pw_rever_encode @1071
+
+ slapd_nss_init @1072
+ slapd_pk11_configurePKCS11 @1073
+ slapd_pk11_freeSlot @1074
+ slapd_pk11_freeSymKey @1075
+ slapd_pk11_findSlotByName @1076
+ slapd_pk11_createPBEAlgorithmID @1077
+ slapd_pk11_pbeKeyGen @1078
+ slapd_pk11_algtagToMechanism @1079
+ slapd_pk11_paramFromAlgid @1080
+ slapd_pk11_mapPBEMechanismToCryptoMechanism @1081
+ slapd_pk11_getBlockSize @1082
+ slapd_pk11_createContextBySymKey @1083
+ slapd_pk11_cipherOp @1084
+ slapd_pk11_finalize @1085
+ slapd_pk11_getInternalKeySlot @1086
+ slapd_pk11_getInternalSlot @1087
+ slapd_pk11_authenticate @1088
+ slapd_pk11_setSlotPWValues @1089
+ slapd_pk11_isFIPS @1090
+ slapd_pk11_findCertFromNickname @1091
+ slapd_pk11_findKeyByAnyCert @1092
+ slapd_pk11_fortezzaHasKEA @1093
+ filter_flag_is_set @1094
+ slapd_sasl_ext_client_bind @1095
+ checkPrefix @1096
+ DS_Sleep @1097
+ slapi_mtn_get_dn @1098
+ dl_add_index @1099
+ dl_replace @1100
+ send_referrals_from_entry @1101
+ escape_filter_value @1102
+ slapd_pk11_destroyContext @1103
+ secoid_destroyAlgorithmID @1104
+ op_shared_is_allowed_attr @1105
+ get_config_DN @1106
+ slapi_sdn_init_dn_ndn_byref @1107
+ check_log_max_size @1108
+ attrlist_find_ex @1109
+ slapi_entry_vattrcache_merge_sv @1110
+ slapi_entry_vattrcache_find_values_and_type_ex @1111
+ slapi_entry_vattrcache_watermark_isvalid @1112
+ slapi_entry_vattrcache_watermark_set @1113
+ slapi_entry_vattrcache_watermark_invalidate @1114
+ slapi_entrycache_vattrcache_watermark_invalidate @1115
+ slapi_filter_test_simple @1116
+ slapi_register_role_check @1117
+ slapi_role_check @1118
+ slapi_valueset_find @1119
+ slapi_vattr_filter_test @1120
+ slapi_attr_set_valueset @1121
+ slapi_vattrcache_iscacheable @1122
+ slapi_value_new_string_passin @1123
+ slapi_value_init_string_passin @1124
+ slapi_value_set_string_passin @1125
+ slapi_entry_attr_has_syntax_value @1126
+ plugin_call_entryfetch_plugins @1127
+ plugin_call_entrystore_plugins @1128
+ config_get_buildnum @1129
+ plugin_print_versions @1130
+ slapi_ch_array_free @1131
+ slapi_vattrcache_cache_all @1132
+ slapi_vattrcache_cache_none @1133
+ slapi_filter_dup @1134
+ slapi_filter_to_string @1135
+ slapi_filter_join_ex @1136
+ slapi_vattr_schema_check_type @1137
+ slapi_vattr_filter_test_ext @1138
+ index_subsys_evaluate_filter @1139
+ slapi_index_entry_list_create @1140
+ slapi_index_entry_list_add @1141
+ slapi_index_register_decoder @1142
+ slapi_index_register_index @1143
+ slapi_entry_attr_get_charray @1144
+ getSupportedCiphers @1145
+ slapi_operation_set_flag @1146
+ slapi_operation_clear_flag @1147
+ slapi_operation_is_flag_set @1148
+ slapi_op_reserved @1149
+ c_set_shutdown @1150
+ c_get_shutdown @1151
+ slapi_vattr_namespace_values_get @1152
+ slapi_vattr_namespace_value_compare @1153
+ slapi_vattr_namespace_values_get_sp @1154
+ slapi_vattr_namespace_value_compare_sp @1155
+ slapi_vattrspi_register_ex @1156
+ slapi_filter_changetype @1157
+ slapi_rand_array @1158
+ slapi_entries_diff @1159
+ dup_global_schema_csn @1160
+ slapd_pk11_CERT_DestroyCertificate @1161
+ slapd_CERT_ExtractPublicKey @1162
+ slapd_pk11_FindPrivateKeyFromCert @1163
+ slapd_pk11_GetInternalKeySlot @1164
+ slapd_pk11_PubWrapSymKey @1165
+ slapd_pk11_KeyGen @1166
+ slapd_pk11_FreeSlot @1167
+ slapd_pk11_FreeSymKey @1168
+ slapd_pk11_DestroyContext @1169
+ slapd_pk11_ParamFromIV @1170
+ slapd_pk11_PubUnwrapSymKey @1171
+ slapd_SECKEY_PublicKeyStrength @1172
+ slapd_pk11_Finalize @1173
+ slapd_pk11_DigestFinal @1174
+ sasl_map_config_add @1175
+ sasl_map_config_delete @1176
+ sasl_map_domap @1177
+ sasl_map_init @1178
+ sasl_map_done @1179
+ slapd_SECITEM_FreeItem @1180
diff --git a/ldap/servers/slapd/listConfigAttrs.pl b/ldap/servers/slapd/listConfigAttrs.pl
new file mode 100644
index 00000000..25b63293
--- /dev/null
+++ b/ldap/servers/slapd/listConfigAttrs.pl
@@ -0,0 +1,106 @@
+#
+# BEGIN COPYRIGHT BLOCK
+# Copyright 2001 Sun Microsystems, Inc.
+# Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+# All rights reserved.
+# END COPYRIGHT BLOCK
+#
+
+# give several files on the command line
+# from the first file will be extracted the following mappings:
+# directive #define name to "real" name and vice versa
+# attr #define name to "real" name and vice versa
+# directive name to attr name and vice versa
+# this file will typically be slap.h
+#
+# from the second file will be extracted
+# the list of config var members of the slapd frontend config structure
+# this file will also usually be slap.h
+#
+# from the third file will be extracted
+# the mapping of directive #define name to the function which sets its value
+# this file will typically be config.c
+
+%DIRECTIVEDEF2NAME = ();
+%DIRECTIVENAME2DEF = ();
+%ATTRDEF2NAME = ();
+%ATTRNAME2DEF = ();
+%DIRECTIVE2ATTR = ();
+%ATTR2DIRECTIVE = ();
+%SETFUNC2VAR = ();
+
+# these are the ldbm specific attributes
+%LDBMATTRS = ();
+
+$filename = 'slap.h';
+open(F, $filename) or die "Error: could not open $filename: $!";
+while (<F>) {
+ if (/(CONFIG_.+?_ATTRIBUTE)\s+[\"](.+?)[\"]/) {
+ # "
+ $ATTRDEF2NAME{$1} = $2;
+ $ATTRNAME2DEF{$2} = $1;
+ }
+}
+close F;
+
+$filename = 'libglobs.c';
+open(F, $filename) or die "Error: could not open $filename: $!";
+while (<F>) {
+ if (/^\s*\{(CONFIG_.+?_ATTRIBUTE),/) {
+ $attrdef = $1;
+ $def = $_;
+ do {
+ $_ = <F>;
+ $def .= $_;
+ } until ($def =~ /\}[,]?\s*$/);
+ ($ignore, $setfunc, $logsetfunc, $whichlog, $varname, $type, $getfunc) =
+ split(/\s*\,\s*/, $def);
+# print "attrdef = $attrdef\n";
+# print "attrname = $ATTRDEF2NAME{$attrdef}\n";
+# print "type = $type\n";
+# print "setfunc = $setfunc\n";
+ print "$ATTRDEF2NAME{$attrdef} $type";
+ if ((($setfunc =~ /0/) || ($setfunc =~ /NULL/)) &&
+ (($logsetfunc =~ /0/) || ($logsetfunc =~ /NULL/))) {
+ print " is read only";
+ }
+ print "\n";
+ }
+}
+print "\nTypes:\n";
+print "\tCONFIG_INT\t\tan integer\n";
+print "\tCONFIG_LONG\t\tan integer\n";
+print "\tCONFIG_STRING\t\ta string\n";
+print "\tCONFIG_CHARRAY\t\ta list of strings\n";
+print "\tCONFIG_ON_OFF\t\tthe string \"on\" or \"off\"\n";
+print "\tCONFIG_STRING_OR_OFF\ta string or \"off\" if not applicable\n";
+print "\tCONFIG_STRING_OR_UNKNOWN\ta string or \"unknown\" if not applicable\n";
+print "\tCONFIG_CONSTANT_INT\tan integer\n";
+print "\tCONFIG_CONSTANT_STRING\ta string\n";
+print "\tCONFIG_SPECIAL_REFERRALLIST\ta list of strings\n";
+print "\tCONFIG_SPECIAL_STORESTATEINFO\tan integer\n";
+print "\tCONFIG_SPECIAL_SSLCLIENTAUTH\t\"off\" or \"allowed\" or \"required\"\n";
+print "\tCONFIG_SPECIAL_ERRORLOGLEVEL\tan integer\n";
+
+# get a list of ldbm attributes and directives
+$filename = 'back-ldbm/backldbm_configdse.c';
+open(F, $filename) or die "Error: could not open $filename: $!";
+while (<F>) {
+ if (/attr_replace[^"]+["]([^"]+)["]/) {
+ $LDBMATTRS{$1} = "\n";
+ }
+ if (/sprintf[^"]+["](\w+)\\t/) {
+ $LDBMDIRECTIVES{$1} = "\n";
+ }
+}
+close F;
+
+$filename = 'back-ldbm/dblayer.c';
+open(F, $filename) or die "Error: could not open $filename: $!";
+while (<F>) {
+ if (/dblayer_config_type_[^"]+["]([^"]+)["]/) {
+ $LDBMDIRECTIVES{$1} = "\n";
+ }
+}
+close F;
+
diff --git a/ldap/servers/slapd/lite_entries.c b/ldap/servers/slapd/lite_entries.c
new file mode 100644
index 00000000..980bf3fe
--- /dev/null
+++ b/ldap/servers/slapd/lite_entries.c
@@ -0,0 +1,106 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+/* lite_entries.c -
+ *
+ * These entries are added under cn=options,cn=features,cn=config for
+ * Directory Lite. These entries tell the console which modules ( ds features )
+ * are disabled, and which attributes in cn=config are disabled.
+ */
+
+
+
+
+#include "slap.h"
+
+static void del_dslite_entries();
+static void add_dslite_entries();
+static const char *lite_entries[] =
+{
+ "dn:" LITE_DISABLED_ATTRS_DN "\n"
+ "cn:attributes\n"
+ "objectclass:top\n"
+ "objectclass:extensibleObject\n"
+ "objectclass:directoryServerFeature\n"
+ "cn=config|nsslapd-referral:off\n"
+ "cn=config|nsslapd-maxdescriptors:off\n",
+
+ "dn:" LITE_DISABLED_MODULES_DN "\n"
+ "objectclass:top\n"
+ "objectclass:directoryServerFeature\n"
+ "objectclass:extensibleObject\n"
+ "cn:modules\n"
+ "replication:off\n"
+ "passwordpolicy:off\n"
+ "accountlockout:off\n"
+ "snmpsettings:off\n"
+ "backup:off\n",
+
+ NULL
+};
+
+/* add_dslite_entries:
+ *
+ * Add the DirLite specific entries.
+ * First we try to delete them in case they were already loaded from dse.ldif
+ * but are in some sort of invalid state.
+ * Then we add them back.
+ * It would have been better to make sure that these entries never get written
+ * to dse.ldif, but it doesn't look like we're able to add a dse_callback function
+ * on the DN's of these entries but they get added at the wrong time.
+ */
+
+
+
+static void
+add_dslite_entries() {
+ int i;
+
+ del_dslite_entries();
+ LDAPDebug( LDAP_DEBUG_TRACE, "Adding lite entries.\n",0,0,0);
+ for( i = 0; lite_entries[i]; i++ ) {
+ Slapi_PBlock *resultpb;
+ char *estr = slapi_ch_strdup ( lite_entries[i] );
+ Slapi_Entry *e = slapi_str2entry( estr, 0 );
+ if ( NULL != e ) {
+ resultpb = slapi_add_entry_internal( e, NULL, 0 );
+ slapi_ch_free ( (void **) &resultpb );
+ slapi_ch_free ( (void **) &estr );
+ }
+ }
+}
+
+
+/* del_dslite_entries: delete the DirLite specific entries */
+static void
+del_dslite_entries() {
+ Slapi_PBlock *resultpb;
+ LDAPDebug( LDAP_DEBUG_TRACE, "Deleting lite entries if they exist\n",0,0,0);
+ resultpb = slapi_delete_internal ( LITE_DISABLED_ATTRS_DN, NULL, 0 );
+ slapi_pblock_destroy ( resultpb );
+ resultpb = slapi_delete_internal ( LITE_DISABLED_MODULES_DN, NULL, 0 );
+ slapi_pblock_destroy ( resultpb );
+}
+
+/* lite_entries_init()
+ * Add the appropriate entries under cn=options,cn=features,cn=config if the
+ * server is running as Directory Lite.
+ *
+ * Otherwise, if the server is the Full Directory, try to delete the entries if
+ * they're already there.
+ */
+
+void
+lite_entries_init() {
+ if ( config_is_slapd_lite() ) {
+ add_dslite_entries();
+ }
+ else {
+ del_dslite_entries();
+ }
+}
+
+
+
diff --git a/ldap/servers/slapd/localhost.c b/ldap/servers/slapd/localhost.c
new file mode 100644
index 00000000..fedf8a19
--- /dev/null
+++ b/ldap/servers/slapd/localhost.c
@@ -0,0 +1,228 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+
+#include <stdio.h>
+#include <sys/types.h>
+#include <string.h>
+#ifdef _WIN32
+#define MAXHOSTNAMELEN 256
+#else
+#include <sys/param.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+#include <arpa/nameser.h>
+#include <resolv.h>
+#endif
+#include <errno.h>
+#include "slap.h"
+#if defined(USE_SYSCONF) || defined(LINUX)
+#include <unistd.h>
+#endif /* USE_SYSCONF */
+
+#if defined( NET_SSL )
+#include <ssl.h>
+#include "fe.h"
+#endif /* defined(NET_SSL) */
+
+#ifndef _PATH_RESCONF /* usually defined in <resolv.h> */
+#define _PATH_RESCONF "/etc/resolv.conf"
+#endif
+
+#if !defined(NO_DOMAINNAME) && defined(_WINDOWS)
+#define NO_DOMAINNAME 1
+#endif
+
+static char*
+find_localhost_DNS()
+{
+ /* This implementation could (and should) be entirely replaced by:
+ dns_ip2host ("127.0.0.1", 1); defined in ldapserver/lib/base/dns.c
+ */
+ char hostname [MAXHOSTNAMELEN + 1];
+ struct hostent *hp;
+#ifdef GETHOSTBYNAME_BUF_T
+ struct hostent hent;
+ GETHOSTBYNAME_BUF_T hbuf;
+ int err;
+#endif
+ char** alias;
+ FILE* f;
+ char* cp;
+ char* domain;
+ char line [MAXHOSTNAMELEN + 8];
+
+ if (gethostname (hostname, MAXHOSTNAMELEN)) {
+ int oserr = errno;
+
+ LDAPDebug (LDAP_DEBUG_ANY, "gethostname() failed, error %d (%s)\n",
+ oserr, slapd_system_strerror( oserr ), 0 );
+ return NULL;
+ }
+ hp = GETHOSTBYNAME (hostname, &hent, hbuf, sizeof(hbuf), &err);
+ if (hp == NULL) {
+ int oserr = errno;
+
+ LDAPDebug( LDAP_DEBUG_ANY,
+ "gethostbyname(\"%s\") failed, error %d (%s)\n",
+ hostname, oserr, slapd_system_strerror( oserr ));
+ return NULL;
+ }
+ if (hp->h_name == NULL) {
+ LDAPDebug (LDAP_DEBUG_ANY, "gethostbyname(\"%s\")->h_name == NULL\n", hostname, 0, 0);
+ return NULL;
+ }
+ if (strchr (hp->h_name, '.') != NULL) {
+ LDAPDebug (LDAP_DEBUG_CONFIG, "h_name == %s\n", hp->h_name, 0, 0);
+ return slapi_ch_strdup (hp->h_name);
+ } else if (hp->h_aliases != NULL) {
+ for (alias = hp->h_aliases; *alias != NULL; ++alias) {
+ if (strchr (*alias, '.') != NULL &&
+ strncmp (*alias, hp->h_name, strlen (hp->h_name))) {
+ LDAPDebug (LDAP_DEBUG_CONFIG, "h_alias == %s\n", *alias, 0, 0);
+ return slapi_ch_strdup (*alias);
+ }
+ }
+ }
+ /* The following is copied from dns_guess_domain(),
+ in ldapserver/lib/base/dnsdmain.c */
+ domain = NULL;
+ f = fopen (_PATH_RESCONF, "r"); /* This fopen() will fail on NT, as expected */
+ if (f != NULL) {
+ while (fgets (line, sizeof(line), f)) {
+ if (strncasecmp (line, "domain", 6) == 0 && isspace (line[6])) {
+ LDAPDebug (LDAP_DEBUG_CONFIG, "%s: %s\n", _PATH_RESCONF, line, 0);
+ for (cp = &line[7]; *cp && isspace(*cp); ++cp);
+ if (*cp) {
+ domain = cp;
+ /* ignore subsequent whitespace: */
+ for (; *cp && ! isspace (*cp); ++cp);
+ if (*cp) {
+ *cp = '\0';
+ }
+ }
+ break;
+ }
+ }
+ fclose (f);
+ }
+#ifndef NO_DOMAINNAME
+ if (domain == NULL) {
+ /* No domain found. Try getdomainname. */
+ getdomainname (line, sizeof(line));
+ LDAPDebug (LDAP_DEBUG_CONFIG, "getdomainname(%s)\n", line, 0, 0);
+ if (line[0] != 0) {
+ domain = &line[0];
+ }
+ }
+#endif
+ if (domain == NULL) {
+ return NULL;
+ }
+ strcpy (hostname, hp->h_name);
+ if (domain[0] == '.') ++domain;
+ if (domain[0]) {
+ strcat (hostname, ".");
+ strcat (hostname, domain);
+ }
+ LDAPDebug (LDAP_DEBUG_CONFIG, "hostname == %s\n", hostname, 0, 0);
+ return slapi_ch_strdup (hostname);
+}
+
+static const char* const RDN = "dc=";
+
+static char*
+convert_DNS_to_DN (char* DNS)
+{
+ char* DN;
+ char* dot;
+ size_t components;
+ if (*DNS == '\0') {
+ return slapi_ch_strdup ("");
+ }
+ components = 1;
+ for (dot = strchr (DNS, '.'); dot != NULL; dot = strchr (dot + 1, '.')) {
+ ++components;
+ }
+ DN = slapi_ch_malloc (strlen (DNS) + (components * strlen(RDN)) + 1);
+ strcpy (DN, RDN);
+ for (dot = strchr (DNS, '.'); dot != NULL; dot = strchr (dot + 1, '.')) {
+ *dot = '\0';
+ strcat (DN, DNS);
+ strcat (DN, ",");
+ strcat (DN, RDN);
+ DNS = dot + 1;
+ *dot = '.';
+ }
+ strcat (DN, DNS);
+ slapi_dn_normalize (DN);
+ return DN;
+}
+
+static char* localhost_DN = NULL;
+
+char*
+get_localhost_DNS()
+{
+ char *retVal;
+ if ( (retVal = config_get_localhost()) == NULL) {
+ /* find_localhost_DNS() returns strdup result */
+ retVal = find_localhost_DNS();
+ }
+ return retVal;
+}
+
+static void
+set_localhost_DN()
+{
+ char *localhost_DNS = config_get_localhost();
+
+ if (localhost_DNS != NULL) {
+ localhost_DN = convert_DNS_to_DN (localhost_DNS);
+ LDAPDebug (LDAP_DEBUG_CONFIG, "DNS %s -> DN %s\n", localhost_DNS, localhost_DN, 0);
+ }
+ slapi_ch_free( (void **) &localhost_DNS );
+}
+
+
+char*
+get_localhost_DN()
+/* Return the Distinguished Name of the local host; that is,
+ its DNS name converted to a DN according to RFC 1279.
+ The caller should _not_ free this pointer. */
+{
+ if (localhost_DN == NULL) {
+ set_localhost_DN();
+ }
+ return localhost_DN;
+}
+
+static char* config_DN = NULL;
+
+char *
+get_config_DN()
+{
+ char *c;
+ char *host;
+
+ if ( config_DN == NULL )
+ {
+ host = get_localhost_DN();
+ if ( host )
+ c = slapi_ch_malloc (20 + strlen (host));
+ else {
+ LDAPDebug (LDAP_DEBUG_CONFIG, "get_locahost_DN() returned \"\"\n",
+ 0, 0, 0);
+ c = slapi_ch_malloc (20);
+ }
+ sprintf (c, "cn=ldap://%s:%lu", host ? host : "", config_get_port());
+ config_DN = c;
+ }
+
+ return config_DN;
+}
+
diff --git a/ldap/servers/slapd/lock.c b/ldap/servers/slapd/lock.c
new file mode 100644
index 00000000..d0f2fc48
--- /dev/null
+++ b/ldap/servers/slapd/lock.c
@@ -0,0 +1,82 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+/* lock.c - routines to open and apply an advisory lock to a file */
+
+#include <stdio.h>
+#include <sys/types.h>
+#ifndef _WIN32
+#include <sys/time.h>
+#include <sys/param.h>
+#include <sys/file.h>
+#include <sys/socket.h>
+#endif
+#include "slap.h"
+#ifdef USE_LOCKF
+#include <unistd.h>
+#endif
+
+FILE *
+lock_fopen( char *fname, char *type, FILE **lfp )
+{
+ FILE *fp;
+ char buf[MAXPATHLEN];
+
+ /* open the lock file */
+ strcpy( buf, fname );
+ strcat( buf, ".lock" );
+ if ( (*lfp = fopen( buf, "w" )) == NULL ) {
+ LDAPDebug( LDAP_DEBUG_ANY, "could not open \"%s\"\n", buf, 0, 0 );
+ return( NULL );
+ }
+
+ /* acquire the lock */
+#ifdef _WIN32
+ while ( _locking( _fileno( *lfp ), LK_NBLCK, 0xFFFFFFFF ) != 0 ) {
+#else
+#ifdef USE_LOCKF
+ while ( lockf( fileno( *lfp ), F_LOCK, 0 ) != 0 ) {
+#else /* _WIN32 */
+ while ( flock( fileno( *lfp ), LOCK_EX ) != 0 ) {
+#endif
+#endif /* _WIN32 */
+ ; /* NULL */
+ }
+
+ /* open the log file */
+ if ( (fp = fopen( fname, type )) == NULL ) {
+ LDAPDebug( LDAP_DEBUG_ANY, "could not open \"%s\"\n", fname, 0, 0 );
+#ifdef _WIN32
+ _locking( _fileno( *lfp ), LK_UNLCK, 0xFFFFFFFF );
+#else /* _WIN32 */
+#ifdef USE_LOCKF
+ lockf( fileno( *lfp ), F_ULOCK, 0 );
+#else
+ flock( fileno( *lfp ), LOCK_UN );
+#endif
+#endif /* _WIN32 */
+ return( NULL );
+ }
+
+ return( fp );
+}
+
+int
+lock_fclose( FILE *fp, FILE *lfp )
+{
+ /* unlock */
+#ifdef _WIN32
+ _locking( _fileno( lfp ), LK_UNLCK, 0xFFFFFFFF );
+#else /* _WIN32 */
+#ifdef USE_LOCKF
+ lockf( fileno( lfp ), F_ULOCK, 0 );
+#else
+ flock( fileno( lfp ), LOCK_UN );
+#endif
+#endif /* _WIN32 */
+ fclose( lfp );
+
+ return( fclose( fp ) );
+}
diff --git a/ldap/servers/slapd/log.c b/ldap/servers/slapd/log.c
new file mode 100644
index 00000000..32c0fa2f
--- /dev/null
+++ b/ldap/servers/slapd/log.c
@@ -0,0 +1,3664 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+/*
+**
+** log.c
+**
+** Routines for writing access and error/debug logs
+**
+**
+** History:
+** As of DS 4.0, we support log rotation for the ACCESS/ERROR/AUDIT log.
+*/
+
+#include "log.h"
+#include "fe.h"
+
+#if defined( XP_WIN32 )
+#include <fcntl.h>
+#include "ntslapdmessages.h"
+#include "proto-ntutil.h"
+extern HANDLE hSlapdEventSource;
+extern LPTSTR pszServerName;
+#endif
+/**************************************************************************
+ * GLOBALS, defines, and ...
+ *************************************************************************/
+/* main struct which contains all the information about logging */
+PRUintn logbuf_tsdindex;
+struct logbufinfo *logbuf_accum;
+static struct logging_opts loginfo;
+static int detached=0;
+
+/* used to lock the timestamp info used by vslapd_log_access */
+static PRLock *ts_time_lock = NULL;
+
+/*
+ * Note: the order of the values in the slapi_log_map array must exactly
+ * match that of the SLAPI_LOG_XXX #defines found in slapi-plugin.h (this is
+ * so we can use the SLAPI_LOG_XXX values to index directly into the array).
+ */
+static int slapi_log_map[] = {
+ LDAP_DEBUG_ANY, /* SLAPI_LOG_FATAL */
+ LDAP_DEBUG_TRACE, /* SLAPI_LOG_TRACE */
+ LDAP_DEBUG_PACKETS, /* SLAPI_LOG_PACKETS */
+ LDAP_DEBUG_ARGS, /* SLAPI_LOG_ARGS */
+ LDAP_DEBUG_CONNS, /* SLAPI_LOG_CONNS */
+ LDAP_DEBUG_BER, /* SLAPI_LOG_BER */
+ LDAP_DEBUG_FILTER, /* SLAPI_LOG_FILTER */
+ LDAP_DEBUG_CONFIG, /* SLAPI_LOG_CONFIG */
+ LDAP_DEBUG_ACL, /* SLAPI_LOG_ACL */
+ LDAP_DEBUG_SHELL, /* SLAPI_LOG_SHELL */
+ LDAP_DEBUG_PARSE, /* SLAPI_LOG_PARSE */
+ LDAP_DEBUG_HOUSE, /* SLAPI_LOG_HOUSE */
+ LDAP_DEBUG_REPL, /* SLAPI_LOG_REPL */
+ LDAP_DEBUG_CACHE, /* SLAPI_LOG_CACHE */
+ LDAP_DEBUG_PLUGIN, /* SLAPI_LOG_PLUGIN */
+ LDAP_DEBUG_TIMING, /* SLAPI_LOG_TIMING */
+ LDAP_DEBUG_ACLSUMMARY, /* SLAPI_LOG_ACLSUMMARY */
+};
+
+#define SLAPI_LOG_MIN SLAPI_LOG_FATAL /* from slapi-plugin.h */
+#define SLAPI_LOG_MAX SLAPI_LOG_ACLSUMMARY /* from slapi-plugin.h */
+#define TBUFSIZE 50 /* size for time buffers */
+#define SLAPI_LOG_BUFSIZ 2048 /* size for data buffers */
+/**************************************************************************
+ * PROTOTYPES
+ *************************************************************************/
+static int log__open_accesslogfile(int logfile_type, int locked);
+static int log__open_errorlogfile(int logfile_type, int locked);
+static int log__open_auditlogfile(int logfile_type, int locked);
+static int log__needrotation(LOGFD fp, int logtype);
+static int log__delete_access_logfile();
+static int log__delete_error_logfile();
+static int log__delete_audit_logfile();
+static int log__access_rotationinfof(char *pathname);
+static int log__error_rotationinfof(char *pathname);
+static int log__audit_rotationinfof(char *pathname);
+static int log__extract_logheader (FILE *fp, long *f_ctime, int *f_size);
+static int log__getfilesize(LOGFD fp);
+static int log__enough_freespace(char *path);
+
+static int vslapd_log_error(LOGFD fp, char *subsystem, char *fmt, va_list ap );
+static int vslapd_log_access(char *fmt, va_list ap );
+static void log_convert_time (time_t ctime, char *tbuf, int type);
+static LogBufferInfo *log_create_buffer(size_t sz);
+static void log_append_buffer2(time_t tnl, LogBufferInfo *lbi, char *msg1, size_t size1, char *msg2, size_t size2);
+static void log_flush_buffer(LogBufferInfo *lbi, int type, int sync_now);
+static void log_write_title(LOGFD fp);
+
+
+static int
+slapd_log_error_proc_internal(
+ char *subsystem, /* omitted if NULL */
+ char *fmt,
+ va_list ap_err,
+ va_list ap_file);
+
+/*
+ * these macros are used for opening a log file, closing a log file, and
+ * writing out to a log file. we have to do this because currently NSPR
+ * is extremely under-performant on NT, while fopen/fwrite fail on several
+ * unix platforms if there are more than 128 files open.
+ *
+ * LOG_OPEN_APPEND(fd, filename, mode) returns true if successful. 'fd' should
+ * be of type LOGFD (check log.h). the file is open for appending to.
+ * LOG_OPEN_WRITE(fd, filename, mode) is the same but truncates the file and
+ * starts writing at the beginning of the file.
+ * LOG_WRITE(fd, buffer, size, headersize) writes into a LOGFD
+ * LOG_WRITE_NOW(fd, buffer, size, headersize) writes into a LOGFD and flushes the
+ * buffer if necessary
+ * LOG_CLOSE(fd) closes the logfile
+ */
+#ifdef XP_WIN32
+#define LOG_OPEN_APPEND(fd, filename, mode) \
+ (((fd) = fopen((filename), "a")) != NULL)
+#define LOG_OPEN_WRITE(fd, filename, mode) \
+ (((fd) = fopen((filename), "w")) != NULL)
+#define LOG_WRITE(fd, buffer, size, headersize) \
+ if ( fwrite((buffer), (size), 1, (fd)) != 1 ) \
+ {\
+ ReportSlapdEvent(EVENTLOG_INFORMATION_TYPE, MSG_SERVER_FAILED_TO_WRITE_LOG, 1, (buffer)); \
+ }
+#define LOG_WRITE_NOW(fd, buffer, size, headersize) do {\
+ if ( fwrite((buffer), (size), 1, (fd)) != 1 ) \
+ { \
+ ReportSlapdEvent(EVENTLOG_INFORMATION_TYPE, MSG_SERVER_FAILED_TO_WRITE_LOG, 1, (buffer)); \
+ }; \
+ fflush((fd)); \
+ } while (0)
+#define LOG_CLOSE(fd) \
+ fclose((fd))
+#else /* xp_win32 */
+#define LOG_OPEN_APPEND(fd, filename, mode) \
+ (((fd) = PR_Open((filename), PR_WRONLY | PR_APPEND | PR_CREATE_FILE , \
+ mode)) != NULL)
+#define LOG_OPEN_WRITE(fd, filename, mode) \
+ (((fd) = PR_Open((filename), PR_WRONLY | PR_TRUNCATE | \
+ PR_CREATE_FILE, mode)) != NULL)
+#define LOG_WRITE(fd, buffer, size, headersize) \
+ if ( slapi_write_buffer((fd), (buffer), (size)) != (size) ) \
+ { \
+ PRErrorCode prerr = PR_GetError(); \
+ syslog(LOG_ERR, "Failed to write log, " SLAPI_COMPONENT_NAME_NSPR " error %d (%s): %s\n", prerr, slapd_pr_strerror(prerr), (buffer)+(headersize) ); \
+ }
+#define LOG_WRITE_NOW(fd, buffer, size, headersize) do {\
+ if ( slapi_write_buffer((fd), (buffer), (size)) != (size) ) \
+ { \
+ PRErrorCode prerr = PR_GetError(); \
+ syslog(LOG_ERR, "Failed to write log, " SLAPI_COMPONENT_NAME_NSPR " error %d (%s): %s\n", prerr, slapd_pr_strerror(prerr), (buffer)+(headersize) ); \
+ } \
+ /* Should be a flush in here ?? Yes because PR_SYNC doesn't work ! */ \
+ PR_Sync(fd); \
+ } while (0)
+#define LOG_CLOSE(fd) \
+ PR_Close((fd))
+#endif
+
+
+/******************************************************************************
+* Set the access level
+******************************************************************************/
+void g_set_accesslog_level(int val)
+{
+ LOG_ACCESS_LOCK_WRITE( );
+ loginfo.log_access_level = val;
+ LOG_ACCESS_UNLOCK_WRITE();
+}
+
+/******************************************************************************
+* Set whether the process is alive or dead
+* If it is detached, then we write the error in 'stderr'
+******************************************************************************/
+void g_set_detached(int val)
+{
+ detached = val;
+}
+
+/******************************************************************************
+* Tell me whether logging begins or not
+******************************************************************************/
+void g_log_init(int log_enabled)
+{
+ slapdFrontendConfig_t *slapdFrontendConfig = getFrontendConfig();
+ char * instancedir = NULL;
+
+ ts_time_lock = PR_NewLock();
+ if (! ts_time_lock)
+ exit(-1);
+
+#if defined( XP_WIN32 )
+ pszServerName = slapi_ch_malloc( MAX_SERVICE_NAME );
+ instancedir = config_get_instancedir();
+ unixtodospath(instancedir);
+ if( !SlapdGetServerNameFromCmdline(pszServerName, instancedir, 1) )
+ {
+ MessageBox(GetDesktopWindow(), "Failed to get the Directory"
+ " Server name from the command-line argument.",
+ " ", MB_ICONEXCLAMATION | MB_OK);
+ exit( 1 );
+ }
+ slapi_ch_free((void **)&instancedir);
+
+ /* Register with the NT EventLog */
+ hSlapdEventSource = RegisterEventSource(NULL, pszServerName );
+ if( !hSlapdEventSource )
+ {
+ char szMessage[256];
+ sprintf( szMessage, "Directory Server %s is terminating. Failed "
+ "to set the EventLog source.", pszServerName);
+ MessageBox(GetDesktopWindow(), szMessage, " ",
+ MB_ICONEXCLAMATION | MB_OK);
+ exit( 1 );
+ }
+#endif
+
+ /* ACCESS LOG */
+ loginfo.log_access_state = 0;
+ loginfo.log_access_mode = SLAPD_DEFAULT_FILE_MODE;
+ loginfo.log_access_maxnumlogs = 1;
+ loginfo.log_access_maxlogsize = -1;
+ loginfo.log_access_rotationsync_enabled = 0;
+ loginfo.log_access_rotationsynchour = -1;
+ loginfo.log_access_rotationsyncmin = -1;
+ loginfo.log_access_rotationsyncclock = -1;
+ loginfo.log_access_rotationtime = -1;
+ loginfo.log_access_rotationunit = -1;
+ loginfo.log_access_rotationtime_secs = -1;
+ loginfo.log_access_maxdiskspace = -1;
+ loginfo.log_access_minfreespace = -1;
+ loginfo.log_access_exptime = -1;
+ loginfo.log_access_exptimeunit = -1;
+ loginfo.log_access_exptime_secs = -1;
+ loginfo.log_access_level = LDAP_DEBUG_STATS;
+ loginfo.log_access_ctime = 0L;
+ loginfo.log_access_fdes = NULL;
+ loginfo.log_access_file = NULL;
+ loginfo.log_numof_access_logs = 1;
+ loginfo.log_access_logchain = NULL;
+ loginfo.log_access_buffer = log_create_buffer(LOG_BUFFER_MAXSIZE);
+ if (loginfo.log_access_buffer == NULL)
+ exit(-1);
+ if ((loginfo.log_access_buffer->lock = PR_NewLock())== NULL )
+ exit (-1);
+ slapdFrontendConfig->accessloglevel = LDAP_DEBUG_STATS;
+
+ /* ERROR LOG */
+ loginfo.log_error_state = 0;
+ loginfo.log_error_mode = SLAPD_DEFAULT_FILE_MODE;
+ loginfo.log_error_maxnumlogs = 1;
+ loginfo.log_error_maxlogsize = -1;
+ loginfo.log_error_rotationsync_enabled = 0;
+ loginfo.log_error_rotationsynchour = -1;
+ loginfo.log_error_rotationsyncmin = -1;
+ loginfo.log_error_rotationsyncclock = -1;
+ loginfo.log_error_rotationtime = -1;
+ loginfo.log_error_rotationunit = -1;
+ loginfo.log_error_rotationtime_secs = -1;
+ loginfo.log_error_maxdiskspace = -1;
+ loginfo.log_error_minfreespace = -1;
+ loginfo.log_error_exptime = -1;
+ loginfo.log_error_exptimeunit = -1;
+ loginfo.log_error_exptime_secs = -1;
+ loginfo.log_error_ctime = 0L;
+ loginfo.log_error_file = NULL;
+ loginfo.log_error_fdes = NULL;
+ loginfo.log_numof_error_logs = 1;
+ loginfo.log_error_logchain = NULL;
+ if ((loginfo.log_error_rwlock =rwl_new())== NULL ) {
+ exit (-1);
+ }
+
+ /* AUDIT LOG */
+ loginfo.log_audit_state = 0;
+ loginfo.log_audit_mode = SLAPD_DEFAULT_FILE_MODE;
+ loginfo.log_audit_maxnumlogs = 1;
+ loginfo.log_audit_maxlogsize = -1;
+ loginfo.log_audit_rotationsync_enabled = 0;
+ loginfo.log_audit_rotationsynchour = -1;
+ loginfo.log_audit_rotationsyncmin = -1;
+ loginfo.log_audit_rotationsyncclock = -1;
+ loginfo.log_audit_rotationtime = -1;
+ loginfo.log_audit_rotationunit = -1;
+ loginfo.log_audit_rotationtime_secs = -1;
+ loginfo.log_audit_maxdiskspace = -1;
+ loginfo.log_audit_minfreespace = -1;
+ loginfo.log_audit_exptime = -1;
+ loginfo.log_audit_exptimeunit = -1;
+ loginfo.log_audit_exptime_secs = -1;
+ loginfo.log_audit_ctime = 0L;
+ loginfo.log_audit_file = NULL;
+ loginfo.log_numof_audit_logs = 1;
+ loginfo.log_audit_fdes = NULL;
+ loginfo.log_audit_logchain = NULL;
+ if ((loginfo.log_audit_rwlock =rwl_new())== NULL ) {
+ exit (-1);
+ }
+}
+
+/******************************************************************************
+* Tell me if log is enabled or disabled
+******************************************************************************/
+int
+log_set_logging(const char *attrname, char *value, int logtype, char *errorbuf, int apply)
+{
+ int v;
+ slapdFrontendConfig_t *fe_cfg = getFrontendConfig();
+
+ if ( NULL == value ) {
+ PR_snprintf( errorbuf, SLAPI_DSE_RETURNTEXT_SIZE,
+ "%s: NULL value; valid values "
+ "are \"on\" or \"off\"", attrname );
+ return LDAP_OPERATIONS_ERROR;
+ }
+
+ if (strcasecmp(value, "on") == 0) {
+ v = LOGGING_ENABLED;
+ }
+ else if (strcasecmp(value, "off") == 0 ) {
+ v = 0;
+ }
+ else {
+ PR_snprintf( errorbuf, SLAPI_DSE_RETURNTEXT_SIZE,
+ "%s: invalid value \"%s\", valid values "
+ "are \"on\" or \"off\"", attrname, value );
+ return LDAP_OPERATIONS_ERROR;
+ }
+
+ if ( !apply ){
+ return LDAP_SUCCESS;
+ }
+
+ switch (logtype) {
+ case SLAPD_ACCESS_LOG:
+ LOG_ACCESS_LOCK_WRITE( );
+ fe_cfg->accesslog_logging_enabled = v;
+ if(v) {
+ loginfo.log_access_state |= LOGGING_ENABLED;
+ }
+ else {
+ loginfo.log_access_state &= ~LOGGING_ENABLED;
+ }
+ LOG_ACCESS_UNLOCK_WRITE();
+ break;
+ case SLAPD_ERROR_LOG:
+ LOG_ERROR_LOCK_WRITE( );
+ fe_cfg->errorlog_logging_enabled = v;
+ if (v) {
+ loginfo.log_error_state |= LOGGING_ENABLED;
+ }
+ else {
+ loginfo.log_error_state &= ~LOGGING_ENABLED;
+ }
+ LOG_ERROR_UNLOCK_WRITE();
+ break;
+ case SLAPD_AUDIT_LOG:
+ LOG_AUDIT_LOCK_WRITE( );
+ fe_cfg->auditlog_logging_enabled = v;
+ if (v) {
+ loginfo.log_audit_state |= LOGGING_ENABLED;
+ }
+ else {
+ loginfo.log_audit_state &= ~LOGGING_ENABLED;
+ }
+ LOG_AUDIT_UNLOCK_WRITE();
+ break;
+ }
+
+ return LDAP_SUCCESS;
+
+}
+/******************************************************************************
+* Tell me the access log file name inc path
+******************************************************************************/
+char *
+g_get_access_log () {
+ char *logfile = NULL;
+
+ LOG_ACCESS_LOCK_READ();
+ if ( loginfo.log_access_file)
+ logfile = slapi_ch_strdup (loginfo.log_access_file);
+ LOG_ACCESS_UNLOCK_READ();
+
+ return logfile;
+
+}
+/******************************************************************************
+* Point to a new access logdir
+*
+* Returns:
+* LDAP_SUCCESS -- success
+* LDAP_UNWILLING_TO_PERFORM -- when trying to open a invalid log file
+* LDAP_LOCAL_ERRO -- some error
+******************************************************************************/
+int
+log_update_accesslogdir(char *pathname, int apply)
+{
+ int rv = LDAP_SUCCESS;
+ LOGFD fp;
+
+ /* try to open the file, we may have a incorrect path */
+ if (! LOG_OPEN_APPEND(fp, pathname, loginfo.log_access_mode)) {
+ LDAPDebug(LDAP_DEBUG_ANY, "WARNING: can't open file %s. "
+ "errno %d (%s)\n",
+ pathname, errno, slapd_system_strerror(errno));
+ /* stay with the current log file */
+ return LDAP_UNWILLING_TO_PERFORM;
+ }
+ LOG_CLOSE(fp);
+
+ /* skip the rest if we aren't doing this for real */
+ if ( !apply ) {
+ return LDAP_SUCCESS;
+ }
+
+ /*
+ ** The user has changed the access log directory. That means we
+ ** need to start fresh.
+ */
+ LOG_ACCESS_LOCK_WRITE ();
+ if (loginfo.log_access_fdes) {
+ LogFileInfo *logp, *d_logp;
+
+ LDAPDebug(LDAP_DEBUG_TRACE,
+ "LOGINFO:Closing the access log file. "
+ "Moving to a new access log file (%s)\n", pathname,0,0);
+
+ LOG_CLOSE(loginfo.log_access_fdes);
+ loginfo.log_access_fdes = 0;
+ loginfo.log_access_ctime = 0;
+ logp = loginfo.log_access_logchain;
+ while (logp) {
+ d_logp = logp;
+ logp = logp->l_next;
+ slapi_ch_free((void**)&d_logp);
+ }
+ loginfo.log_access_logchain = NULL;
+ slapi_ch_free((void**)&loginfo.log_access_file);
+ loginfo.log_access_file = NULL;
+ loginfo.log_numof_access_logs = 1;
+ }
+
+ /* Now open the new access log file */
+ if ( access_log_openf (pathname, 1 /* locked */)) {
+ rv = LDAP_LOCAL_ERROR; /* error Unable to use the new dir */
+ }
+ LOG_ACCESS_UNLOCK_WRITE();
+ return rv;
+}
+/******************************************************************************
+* Tell me the error log file name inc path
+******************************************************************************/
+char *
+g_get_error_log() {
+ char *logfile = NULL;
+
+ LOG_ERROR_LOCK_READ();
+ if ( loginfo.log_error_file)
+ logfile = slapi_ch_strdup (loginfo.log_error_file);
+ LOG_ERROR_UNLOCK_READ();
+
+ return logfile;
+}
+/******************************************************************************
+* Point to a new error logdir
+*
+* Returns:
+* LDAP_SUCCESS -- success
+* LDAP_UNWILLING_TO_PERFORM -- when trying to open a invalid log file
+* LDAP_LOCAL_ERRO -- some error
+******************************************************************************/
+int
+log_update_errorlogdir(char *pathname, int apply)
+{
+
+ int rv = LDAP_SUCCESS;
+ LOGFD fp;
+
+ /* try to open the file, we may have a incorrect path */
+ if (! LOG_OPEN_APPEND(fp, pathname, loginfo.log_error_mode)) {
+ LDAPDebug(LDAP_DEBUG_ANY, "WARNING: can't open file %s. "
+ "errno %d (%s)\n",
+ pathname, errno, slapd_system_strerror(errno));
+ /* stay with the current log file */
+ return LDAP_UNWILLING_TO_PERFORM;
+ }
+
+ /* skip the rest if we aren't doing this for real */
+ if ( !apply ) {
+ return LDAP_SUCCESS;
+ }
+ LOG_CLOSE(fp);
+
+ /*
+ ** The user has changed the error log directory. That means we
+ ** need to start fresh.
+ */
+ LOG_ERROR_LOCK_WRITE ();
+ if (loginfo.log_error_fdes) {
+ LogFileInfo *logp, *d_logp;
+
+ LOG_CLOSE(loginfo.log_error_fdes);
+ loginfo.log_error_fdes = 0;
+ loginfo.log_error_ctime = 0;
+ logp = loginfo.log_error_logchain;
+ while (logp) {
+ d_logp = logp;
+ logp = logp->l_next;
+ slapi_ch_free((void**)&d_logp);
+ }
+ loginfo.log_error_logchain = NULL;
+ slapi_ch_free((void**)&loginfo.log_error_file);
+ loginfo.log_error_file = NULL;
+ loginfo.log_numof_error_logs = 1;
+ }
+
+ /* Now open the new errorlog */
+ if ( error_log_openf (pathname, 1 /* obtained lock */)) {
+ rv = LDAP_LOCAL_ERROR; /* error: Unable to use the new dir */
+ }
+
+ LOG_ERROR_UNLOCK_WRITE();
+ return rv;
+}
+/******************************************************************************
+* Tell me the audit log file name inc path
+******************************************************************************/
+char *
+g_get_audit_log() {
+ char *logfile = NULL;
+
+ LOG_AUDIT_LOCK_READ();
+ if ( loginfo.log_audit_file)
+ logfile = slapi_ch_strdup (loginfo.log_audit_file);
+ LOG_AUDIT_UNLOCK_READ();
+
+ return logfile;
+}
+/******************************************************************************
+* Point to a new audit logdir
+*
+* Returns:
+* LDAP_SUCCESS -- success
+* LDAP_UNWILLING_TO_PERFORM -- when trying to open a invalid log file
+* LDAP_LOCAL_ERRO -- some error
+******************************************************************************/
+int
+log_update_auditlogdir(char *pathname, int apply)
+{
+ int rv = LDAP_SUCCESS;
+ LOGFD fp;
+
+ /* try to open the file, we may have a incorrect path */
+ if (! LOG_OPEN_APPEND(fp, pathname, loginfo.log_audit_mode)) {
+ LDAPDebug(LDAP_DEBUG_ANY, "WARNING: can't open file %s. "
+ "errno %d (%s)\n",
+ pathname, errno, slapd_system_strerror(errno));
+ /* stay with the current log file */
+ return LDAP_UNWILLING_TO_PERFORM;
+ }
+
+ /* skip the rest if we aren't doing this for real */
+ if ( !apply ) {
+ return LDAP_SUCCESS;
+ }
+ LOG_CLOSE(fp);
+
+ /*
+ ** The user has changed the audit log directory. That means we
+ ** need to start fresh.
+ */
+ LOG_AUDIT_LOCK_WRITE ();
+ if (loginfo.log_audit_fdes) {
+ LogFileInfo *logp, *d_logp;
+ LDAPDebug(LDAP_DEBUG_TRACE,
+ "LOGINFO:Closing the audit log file. "
+ "Moving to a new audit file (%s)\n", pathname,0,0);
+
+ LOG_CLOSE(loginfo.log_audit_fdes);
+ loginfo.log_audit_fdes = 0;
+ loginfo.log_audit_ctime = 0;
+ logp = loginfo.log_audit_logchain;
+ while (logp) {
+ d_logp = logp;
+ logp = logp->l_next;
+ slapi_ch_free((void**)&d_logp);
+ }
+ loginfo.log_audit_logchain = NULL;
+ slapi_ch_free((void**)&loginfo.log_audit_file);
+ loginfo.log_audit_file = NULL;
+ loginfo.log_numof_audit_logs = 1;
+ }
+
+ /* Now open the new errorlog */
+ if ( audit_log_openf (pathname, 1 /* locked */)) {
+ rv = LDAP_LOCAL_ERROR; /* error: Unable to use the new dir */
+ }
+ LOG_AUDIT_UNLOCK_WRITE();
+ return rv;
+}
+
+int
+log_set_mode (const char *attrname, char *value, int logtype, char *errorbuf, int apply)
+{
+ int v = 0;
+ slapdFrontendConfig_t *fe_cfg = getFrontendConfig();
+
+ if ( NULL == value ) {
+ PR_snprintf( errorbuf, SLAPI_DSE_RETURNTEXT_SIZE,
+ "%s: null value; valid values "
+ "are are of the format \"yz-yz-yz-\" where y could be 'r' or '-',"
+ " and z could be 'w' or '-'", attrname );
+ return LDAP_OPERATIONS_ERROR;
+ }
+
+ if ( !apply ){
+ return LDAP_SUCCESS;
+ }
+
+ v = strtol (value, NULL, 8);
+
+ switch (logtype) {
+ case SLAPD_ACCESS_LOG:
+ LOG_ACCESS_LOCK_WRITE( );
+ slapi_ch_free ( (void **) &fe_cfg->accesslog_mode );
+ fe_cfg->accesslog_mode = slapi_ch_strdup (value);
+ loginfo.log_access_mode = v;
+ LOG_ACCESS_UNLOCK_WRITE();
+ break;
+ case SLAPD_ERROR_LOG:
+ LOG_ERROR_LOCK_WRITE( );
+ slapi_ch_free ( (void **) &fe_cfg->errorlog_mode );
+ fe_cfg->errorlog_mode = slapi_ch_strdup (value);
+ loginfo.log_error_mode = v;
+ LOG_ERROR_UNLOCK_WRITE();
+ break;
+ case SLAPD_AUDIT_LOG:
+ LOG_AUDIT_LOCK_WRITE( );
+ slapi_ch_free ( (void **) &fe_cfg->auditlog_mode );
+ fe_cfg->auditlog_mode = slapi_ch_strdup (value);
+ loginfo.log_audit_mode = v;
+ LOG_AUDIT_UNLOCK_WRITE();
+ break;
+ }
+ return LDAP_SUCCESS;
+}
+
+/******************************************************************************
+* MAX NUMBER OF LOGS
+******************************************************************************/
+int
+log_set_numlogsperdir(const char *attrname, char *numlogs_str, int logtype, char *returntext, int apply)
+{
+ slapdFrontendConfig_t *fe_cfg = getFrontendConfig();
+
+ int rv = LDAP_SUCCESS;
+ int numlogs;
+
+ if ( logtype != SLAPD_ACCESS_LOG &&
+ logtype != SLAPD_ERROR_LOG &&
+ logtype != SLAPD_AUDIT_LOG ) {
+ rv = LDAP_OPERATIONS_ERROR;
+ PR_snprintf( returntext, SLAPI_DSE_RETURNTEXT_SIZE,
+ "%s: invalid log type %d", attrname, logtype );
+ }
+ if ( !apply || !numlogs_str || !*numlogs_str) {
+ return rv;
+ }
+
+ numlogs = atoi(numlogs_str);
+
+ if (numlogs >= 1) {
+ switch (logtype) {
+ case SLAPD_ACCESS_LOG:
+ LOG_ACCESS_LOCK_WRITE( );
+ loginfo.log_access_maxnumlogs = numlogs;
+ fe_cfg->accesslog_maxnumlogs = numlogs;
+ LOG_ACCESS_UNLOCK_WRITE();
+ break;
+ case SLAPD_ERROR_LOG:
+ LOG_ERROR_LOCK_WRITE( );
+ loginfo.log_error_maxnumlogs = numlogs;
+ fe_cfg->errorlog_maxnumlogs = numlogs;
+ LOG_ERROR_UNLOCK_WRITE();
+ break;
+ case SLAPD_AUDIT_LOG:
+ LOG_AUDIT_LOCK_WRITE( );
+ loginfo.log_audit_maxnumlogs = numlogs;
+ fe_cfg->auditlog_maxnumlogs = numlogs;
+ LOG_AUDIT_UNLOCK_WRITE();
+ break;
+ default:
+ rv = LDAP_OPERATIONS_ERROR;
+ LDAPDebug( LDAP_DEBUG_ANY,
+ "log_set_numlogsperdir: invalid log type %d", logtype,0,0 );
+ }
+ }
+ return rv;
+}
+
+/******************************************************************************
+* LOG SIZE
+* Return Values:
+* LDAP_OPERATIONS_ERROR -- fail
+* LDAP_SUCCESS -- success
+*
+* NOTE: The config struct should contain the maxlogsize in MB and not in bytes.
+******************************************************************************/
+int
+log_set_logsize(const char *attrname, char *logsize_str, int logtype, char *returntext, int apply)
+{
+ int rv = LDAP_SUCCESS;
+ int mdiskspace= 0;
+ int max_logsize;
+ int logsize;
+ slapdFrontendConfig_t *fe_cfg = getFrontendConfig();
+
+ if (!apply || !logsize_str || !*logsize_str)
+ return rv;
+
+ logsize = atoi(logsize_str);
+
+ /* convert it to bytes */
+ max_logsize = logsize * LOG_MB_IN_BYTES;
+
+ if (max_logsize <= 0) {
+ max_logsize = -1;
+ }
+
+ switch (logtype) {
+ case SLAPD_ACCESS_LOG:
+ LOG_ACCESS_LOCK_WRITE( );
+ mdiskspace = loginfo.log_access_maxdiskspace;
+ break;
+ case SLAPD_ERROR_LOG:
+ LOG_ERROR_LOCK_WRITE( );
+ mdiskspace = loginfo.log_error_maxdiskspace;
+ break;
+ case SLAPD_AUDIT_LOG:
+ LOG_AUDIT_LOCK_WRITE( );
+ mdiskspace = loginfo.log_audit_maxdiskspace;
+ break;
+ default:
+ PR_snprintf( returntext, SLAPI_DSE_RETURNTEXT_SIZE,
+ "%s: invalid logtype %d", attrname, logtype );
+ rv = LDAP_OPERATIONS_ERROR;
+ }
+
+ if ((max_logsize > mdiskspace) && (mdiskspace != -1))
+ rv = 2;
+
+ switch (logtype) {
+ case SLAPD_ACCESS_LOG:
+ if (!rv && apply) {
+ loginfo.log_access_maxlogsize = max_logsize;
+ fe_cfg->accesslog_maxlogsize = logsize;
+ }
+ LOG_ACCESS_UNLOCK_WRITE();
+ break;
+ case SLAPD_ERROR_LOG:
+ if (!rv && apply) {
+ loginfo.log_error_maxlogsize = max_logsize;
+ fe_cfg->errorlog_maxlogsize = logsize;
+ }
+ LOG_ERROR_UNLOCK_WRITE();
+ break;
+ case SLAPD_AUDIT_LOG:
+ if (!rv && apply) {
+ loginfo.log_audit_maxlogsize = max_logsize;
+ fe_cfg->auditlog_maxlogsize = logsize;
+ }
+ LOG_AUDIT_UNLOCK_WRITE();
+ break;
+ default:
+ rv = 1;
+ }
+ /* logsize will be in n MB. Convert it to bytes */
+ if (rv == 2) {
+ LDAPDebug (LDAP_DEBUG_ANY,
+ "Invalid value for Maximum log size:"
+ "Maxlogsize:%d MB Maxdisksize:%d MB\n",
+ logsize, mdiskspace/LOG_MB_IN_BYTES,0);
+
+ rv = LDAP_OPERATIONS_ERROR;
+ }
+ return rv;
+}
+
+time_t
+log_get_rotationsyncclock(int synchour, int syncmin)
+{
+ struct tm *currtm;
+ time_t currclock;
+ time_t syncclock;
+ int hours, minutes;
+
+ time( &currclock);
+ currtm = localtime( &currclock );
+
+ if ( syncmin < currtm->tm_min ) {
+ minutes = syncmin + 60 - currtm->tm_min;
+ hours = synchour - 1 - currtm->tm_hour;
+ } else {
+ minutes = syncmin - currtm->tm_min;
+ hours = synchour - currtm->tm_hour;
+ }
+ if ( hours < 0 ) hours += 24;
+
+ syncclock = currclock + hours * 3600 + minutes * 60;
+ return syncclock;
+}
+
+int
+log_set_rotationsync_enabled(const char *attrname, char *value, int logtype, char *errorbuf, int apply)
+{
+ int v;
+ slapdFrontendConfig_t *fe_cfg = getFrontendConfig();
+
+ if ( NULL == value ) {
+ PR_snprintf( errorbuf, SLAPI_DSE_RETURNTEXT_SIZE,
+ "%s: NULL value; valid values "
+ "are \"on\" or \"off\"", attrname );
+ return LDAP_OPERATIONS_ERROR;
+ }
+
+ if (strcasecmp(value, "on") == 0) {
+ v = LDAP_ON;
+ }
+ else if (strcasecmp(value, "off") == 0 ) {
+ v = LDAP_OFF;
+ }
+ else {
+ PR_snprintf( errorbuf, SLAPI_DSE_RETURNTEXT_SIZE,
+ "%s: invalid value \"%s\", valid values "
+ "are \"on\" or \"off\"", attrname, value );
+ return LDAP_OPERATIONS_ERROR;
+ }
+
+ if ( !apply ){
+ return LDAP_SUCCESS;
+ }
+
+ switch (logtype) {
+ case SLAPD_ACCESS_LOG:
+ LOG_ACCESS_LOCK_WRITE( );
+ fe_cfg->accesslog_rotationsync_enabled = v;
+ loginfo.log_access_rotationsync_enabled = v;
+ LOG_ACCESS_UNLOCK_WRITE();
+ break;
+ case SLAPD_ERROR_LOG:
+ LOG_ERROR_LOCK_WRITE( );
+ fe_cfg->errorlog_rotationsync_enabled = v;
+ loginfo.log_error_rotationsync_enabled = v;
+ LOG_ERROR_UNLOCK_WRITE();
+ break;
+ case SLAPD_AUDIT_LOG:
+ LOG_AUDIT_LOCK_WRITE( );
+ fe_cfg->auditlog_rotationsync_enabled = v;
+ loginfo.log_audit_rotationsync_enabled = v;
+ LOG_AUDIT_UNLOCK_WRITE();
+ break;
+ }
+ return LDAP_SUCCESS;
+}
+
+int
+log_set_rotationsynchour(const char *attrname, char *rhour_str, int logtype, char *returntext, int apply)
+{
+ int rhour = -1;
+ int rv = LDAP_SUCCESS;
+ slapdFrontendConfig_t *fe_cfg = getFrontendConfig();
+
+ if ( logtype != SLAPD_ACCESS_LOG &&
+ logtype != SLAPD_ERROR_LOG &&
+ logtype != SLAPD_AUDIT_LOG ) {
+ PR_snprintf( returntext, SLAPI_DSE_RETURNTEXT_SIZE,
+ "%s: invalid log type: %d", attrname, logtype );
+ return LDAP_OPERATIONS_ERROR;
+ }
+
+ /* return if we aren't doing this for real */
+ if ( !apply ) {
+ return rv;
+ }
+
+ if ( rhour_str && *rhour_str != '\0' )
+ rhour = atol( rhour_str );
+ if ( rhour > 23 )
+ rhour = rhour % 24;
+
+ switch (logtype) {
+ case SLAPD_ACCESS_LOG:
+ LOG_ACCESS_LOCK_WRITE( );
+ loginfo.log_access_rotationsynchour = rhour;
+ loginfo.log_access_rotationsyncclock = log_get_rotationsyncclock( rhour, loginfo.log_access_rotationsyncmin );
+ fe_cfg->accesslog_rotationsynchour = rhour;
+ LOG_ACCESS_UNLOCK_WRITE();
+ break;
+ case SLAPD_ERROR_LOG:
+ LOG_ERROR_LOCK_WRITE( );
+ loginfo.log_error_rotationsynchour = rhour;
+ loginfo.log_error_rotationsyncclock = log_get_rotationsyncclock( rhour, loginfo.log_error_rotationsyncmin );
+ fe_cfg->errorlog_rotationsynchour = rhour;
+ LOG_ERROR_UNLOCK_WRITE();
+ break;
+ case SLAPD_AUDIT_LOG:
+ LOG_AUDIT_LOCK_WRITE( );
+ loginfo.log_audit_rotationsynchour = rhour;
+ loginfo.log_audit_rotationsyncclock = log_get_rotationsyncclock( rhour, loginfo.log_audit_rotationsyncmin );
+ fe_cfg->auditlog_rotationsynchour = rhour;
+ LOG_AUDIT_UNLOCK_WRITE();
+ break;
+ default:
+ rv = 1;
+ }
+
+ return rv;
+}
+
+int
+log_set_rotationsyncmin(const char *attrname, char *rmin_str, int logtype, char *returntext, int apply)
+{
+ int rmin = -1;
+ int rv = LDAP_SUCCESS;
+ slapdFrontendConfig_t *fe_cfg = getFrontendConfig();
+
+ if ( logtype != SLAPD_ACCESS_LOG &&
+ logtype != SLAPD_ERROR_LOG &&
+ logtype != SLAPD_AUDIT_LOG ) {
+ PR_snprintf( returntext, SLAPI_DSE_RETURNTEXT_SIZE,
+ "%s: invalid log type: %d", attrname, logtype );
+ return LDAP_OPERATIONS_ERROR;
+ }
+
+ /* return if we aren't doing this for real */
+ if ( !apply ) {
+ return rv;
+ }
+
+ if ( rmin_str && *rmin_str != '\0' )
+ rmin = atol( rmin_str );
+ if ( rmin > 59 )
+ rmin = rmin % 60;
+
+ switch (logtype) {
+ case SLAPD_ACCESS_LOG:
+ LOG_ACCESS_LOCK_WRITE( );
+ loginfo.log_access_rotationsyncmin = rmin;
+ fe_cfg->accesslog_rotationsyncmin = rmin;
+ loginfo.log_access_rotationsyncclock = log_get_rotationsyncclock( loginfo.log_access_rotationsynchour, rmin );
+ LOG_ACCESS_UNLOCK_WRITE();
+ break;
+ case SLAPD_ERROR_LOG:
+ LOG_ERROR_LOCK_WRITE( );
+ loginfo.log_error_rotationsyncmin = rmin;
+ loginfo.log_error_rotationsyncclock = log_get_rotationsyncclock( loginfo.log_error_rotationsynchour, rmin );
+ fe_cfg->errorlog_rotationsyncmin = rmin;
+ LOG_ERROR_UNLOCK_WRITE();
+ break;
+ case SLAPD_AUDIT_LOG:
+ LOG_AUDIT_LOCK_WRITE( );
+ loginfo.log_audit_rotationsyncmin = rmin;
+ fe_cfg->auditlog_rotationsyncmin = rmin;
+ loginfo.log_audit_rotationsyncclock = log_get_rotationsyncclock( loginfo.log_audit_rotationsynchour, rmin );
+ LOG_AUDIT_UNLOCK_WRITE();
+ break;
+ default:
+ rv = 1;
+ }
+
+ return rv;
+}
+
+/******************************************************************************
+* ROTATION TIME
+* Return Values:
+* 1 -- fail
+* 0 -- success
+******************************************************************************/
+int
+log_set_rotationtime(const char *attrname, char *rtime_str, int logtype, char *returntext, int apply)
+{
+
+ int runit= 0;
+ int value, rtime;
+ int rv = LDAP_SUCCESS;
+ slapdFrontendConfig_t *fe_cfg = getFrontendConfig();
+
+ if ( logtype != SLAPD_ACCESS_LOG &&
+ logtype != SLAPD_ERROR_LOG &&
+ logtype != SLAPD_AUDIT_LOG ) {
+ PR_snprintf( returntext, SLAPI_DSE_RETURNTEXT_SIZE,
+ "%s: invalid log type: %d", attrname, logtype );
+ return LDAP_OPERATIONS_ERROR;
+ }
+
+ /* return if we aren't doing this for real */
+ if ( !apply || !rtime_str || !*rtime_str) {
+ return rv;
+ }
+
+ rtime = atoi(rtime_str);
+
+ switch (logtype) {
+ case SLAPD_ACCESS_LOG:
+ LOG_ACCESS_LOCK_WRITE( );
+ loginfo.log_access_rotationtime = rtime;
+ runit = loginfo.log_access_rotationunit;
+ break;
+ case SLAPD_ERROR_LOG:
+ LOG_ERROR_LOCK_WRITE( );
+ loginfo.log_error_rotationtime = rtime;
+ runit = loginfo.log_error_rotationunit;
+ break;
+ case SLAPD_AUDIT_LOG:
+ LOG_AUDIT_LOCK_WRITE( );
+ loginfo.log_audit_rotationtime = rtime;
+ runit = loginfo.log_audit_rotationunit;
+ break;
+ default:
+ rv = 1;
+ }
+
+ /* find out the rotation unit we have se right now */
+ if (runit == LOG_UNIT_MONTHS) {
+ value = 31 * 24 * 60 * 60 * rtime;
+ } else if (runit == LOG_UNIT_WEEKS) {
+ value = 7 * 24 * 60 * 60 * rtime;
+ } else if (runit == LOG_UNIT_DAYS ) {
+ value = 24 * 60 * 60 * rtime;
+ } else if (runit == LOG_UNIT_HOURS) {
+ value = 3600 * rtime;
+ } else if (runit == LOG_UNIT_MINS) {
+ value = 60 * rtime;
+ } else {
+ /* In this case we don't rotate */
+ value = -1;
+ }
+
+ switch (logtype) {
+ case SLAPD_ACCESS_LOG:
+ fe_cfg->accesslog_rotationtime = rtime;
+ loginfo.log_access_rotationtime_secs = value;
+ LOG_ACCESS_UNLOCK_WRITE();
+ break;
+ case SLAPD_ERROR_LOG:
+ fe_cfg->errorlog_rotationtime = rtime;
+ loginfo.log_error_rotationtime_secs = value;
+ LOG_ERROR_UNLOCK_WRITE();
+ break;
+ case SLAPD_AUDIT_LOG:
+ fe_cfg->auditlog_rotationtime = rtime;
+ loginfo.log_audit_rotationtime_secs = value;
+ LOG_AUDIT_UNLOCK_WRITE();
+ break;
+ default:
+ rv = 1;
+ }
+ return rv;
+}
+/******************************************************************************
+* ROTATION TIME UNIT
+* Return Values:
+* 1 -- fail
+* 0 -- success
+******************************************************************************/
+int log_set_rotationtimeunit(const char *attrname, char *runit, int logtype, char *errorbuf, int apply)
+{
+ int value= 0;
+ int runitType;
+ int rv = 0;
+
+ slapdFrontendConfig_t *fe_cfg = getFrontendConfig();
+
+ if ( logtype != SLAPD_ACCESS_LOG &&
+ logtype != SLAPD_ERROR_LOG &&
+ logtype != SLAPD_AUDIT_LOG ) {
+ PR_snprintf ( errorbuf, SLAPI_DSE_RETURNTEXT_SIZE,
+ "%s: invalid log type: %d", attrname, logtype );
+ return LDAP_OPERATIONS_ERROR;
+ }
+
+ if ( (strcasecmp(runit, "month") == 0) ||
+ (strcasecmp(runit, "week") == 0) ||
+ (strcasecmp(runit, "day") == 0) ||
+ (strcasecmp(runit, "hour") == 0) ||
+ (strcasecmp(runit, "minute") == 0)) {
+ /* all good values */
+ } else {
+ PR_snprintf ( errorbuf, SLAPI_DSE_RETURNTEXT_SIZE,
+ "%s: unknown unit \"%s\"", attrname, runit );
+ rv = LDAP_OPERATIONS_ERROR;
+ }
+
+ /* return if we aren't doing this for real */
+ if ( !apply ) {
+ return rv;
+ }
+
+ switch (logtype) {
+ case SLAPD_ACCESS_LOG:
+ LOG_ACCESS_LOCK_WRITE( );
+ value = loginfo.log_access_rotationtime;
+ break;
+ case SLAPD_ERROR_LOG:
+ LOG_ERROR_LOCK_WRITE( );
+ value = loginfo.log_error_rotationtime;
+ break;
+ case SLAPD_AUDIT_LOG:
+ LOG_AUDIT_LOCK_WRITE( );
+ value = loginfo.log_audit_rotationtime;
+ break;
+ default:
+ rv = 1;
+ }
+
+ if (strcasecmp(runit, "month") == 0) {
+ runitType = LOG_UNIT_MONTHS;
+ value *= 31 * 24 * 60 * 60;
+ } else if (strcasecmp(runit, "week") == 0) {
+ runitType = LOG_UNIT_WEEKS;
+ value *= 7 * 24 * 60 * 60;
+ } else if (strcasecmp(runit, "day") == 0) {
+ runitType = LOG_UNIT_DAYS;
+ value *= 24 * 60 * 60;
+ } else if (strcasecmp(runit, "hour") == 0) {
+ runitType = LOG_UNIT_HOURS;
+ value *= 3600;
+ } else if (strcasecmp(runit, "minute") == 0) {
+ runitType = LOG_UNIT_MINS;
+ value *= 60;
+ } else {
+ /* In this case we don't rotate */
+ runitType = LOG_UNIT_UNKNOWN;
+ value = -1;
+ }
+
+ switch (logtype) {
+ case SLAPD_ACCESS_LOG:
+ loginfo.log_access_rotationtime_secs = value;
+ loginfo.log_access_rotationunit = runitType;
+ slapi_ch_free ( (void **) &fe_cfg->accesslog_rotationunit);
+ fe_cfg->accesslog_rotationunit = slapi_ch_strdup ( runit );
+ LOG_ACCESS_UNLOCK_WRITE();
+ break;
+ case SLAPD_ERROR_LOG:
+ loginfo.log_error_rotationtime_secs = value;
+ loginfo.log_error_rotationunit = runitType;
+ slapi_ch_free ( (void **) &fe_cfg->errorlog_rotationunit) ;
+ fe_cfg->errorlog_rotationunit = slapi_ch_strdup ( runit );
+ LOG_ERROR_UNLOCK_WRITE();
+ break;
+ case SLAPD_AUDIT_LOG:
+ loginfo.log_audit_rotationtime_secs = value;
+ loginfo.log_audit_rotationunit = runitType;
+ slapi_ch_free ( (void **) &fe_cfg->auditlog_rotationunit);
+ fe_cfg->auditlog_rotationunit = slapi_ch_strdup ( runit );
+ LOG_AUDIT_UNLOCK_WRITE();
+ break;
+ default:
+ rv = 1;
+ }
+ return rv;
+}
+/******************************************************************************
+* MAXIMUM DISK SPACE
+* Return Values:
+* 1 -- fail
+* 0 -- success
+*
+* NOTE:
+* The config struct should contain the value in MB and not in bytes.
+******************************************************************************/
+int
+log_set_maxdiskspace(const char *attrname, char *maxdiskspace_str, int logtype, char *errorbuf, int apply)
+{
+ int rv = 0;
+ int mlogsize;
+ int maxdiskspace;
+ int s_maxdiskspace;
+
+ slapdFrontendConfig_t *fe_cfg = getFrontendConfig();
+
+ if ( logtype != SLAPD_ACCESS_LOG &&
+ logtype != SLAPD_ERROR_LOG &&
+ logtype != SLAPD_AUDIT_LOG ) {
+ PR_snprintf( errorbuf, SLAPI_DSE_RETURNTEXT_SIZE,
+ "%s: invalid log type: %d", attrname, logtype );
+ return LDAP_OPERATIONS_ERROR;
+ }
+
+ if (!apply || !maxdiskspace_str || !*maxdiskspace_str)
+ return rv;
+
+ maxdiskspace = atoi(maxdiskspace_str);
+ s_maxdiskspace = maxdiskspace;
+
+ /* Disk space are in MB but store in bytes */
+ switch (logtype) {
+ case SLAPD_ACCESS_LOG:
+ LOG_ACCESS_LOCK_WRITE( );
+ mlogsize = loginfo.log_access_maxlogsize;
+ break;
+ case SLAPD_ERROR_LOG:
+ LOG_ERROR_LOCK_WRITE( );
+ mlogsize = loginfo.log_error_maxlogsize;
+ break;
+ case SLAPD_AUDIT_LOG:
+ LOG_AUDIT_LOCK_WRITE( );
+ mlogsize = loginfo.log_audit_maxlogsize;
+ break;
+ default:
+ rv = 1;
+ mlogsize = -1;
+ }
+ maxdiskspace *= LOG_MB_IN_BYTES;
+ if (maxdiskspace < 0) {
+ maxdiskspace = -1;
+ }
+ else if (maxdiskspace < mlogsize) {
+ rv = LDAP_OPERATIONS_ERROR;
+ PR_snprintf( errorbuf, SLAPI_DSE_RETURNTEXT_SIZE,
+ "%s: maxdiskspace \"%d\" is less than max log size \"%d\"",
+ attrname, maxdiskspace, mlogsize );
+ }
+
+ switch (logtype) {
+ case SLAPD_ACCESS_LOG:
+ if (rv== 0 && apply) {
+ loginfo.log_access_maxdiskspace = maxdiskspace;
+ fe_cfg->accesslog_maxdiskspace = s_maxdiskspace ;
+ }
+ LOG_ACCESS_UNLOCK_WRITE();
+ break;
+ case SLAPD_ERROR_LOG:
+ if (rv== 0 && apply) {
+ loginfo.log_error_maxdiskspace = maxdiskspace;
+ fe_cfg->errorlog_maxdiskspace = s_maxdiskspace;
+ }
+ LOG_ERROR_UNLOCK_WRITE();
+ break;
+ case SLAPD_AUDIT_LOG:
+ if (rv== 0 && apply) {
+ loginfo.log_audit_maxdiskspace = maxdiskspace;
+ fe_cfg->auditlog_maxdiskspace = s_maxdiskspace;
+ }
+ LOG_AUDIT_UNLOCK_WRITE();
+ break;
+ default:
+ PR_snprintf( errorbuf, SLAPI_DSE_RETURNTEXT_SIZE,
+ "%s: invalid maximum log disk size:"
+ "Maxdiskspace:%d MB Maxlogsize:%d MB \n",
+ attrname, maxdiskspace, mlogsize);
+ rv = LDAP_OPERATIONS_ERROR;
+ }
+ return rv;
+
+}
+/******************************************************************************
+* MINIMUM FREE SPACE
+* Return Values:
+* 1 -- fail
+* 0 -- success
+******************************************************************************/
+int
+log_set_mindiskspace(const char *attrname, char *minfreespace_str, int logtype, char *errorbuf, int apply)
+{
+ int rv=LDAP_SUCCESS;
+ int minfreespaceB;
+ int minfreespace;
+
+ slapdFrontendConfig_t *fe_cfg = getFrontendConfig();
+
+ if ( logtype != SLAPD_ACCESS_LOG &&
+ logtype != SLAPD_ERROR_LOG &&
+ logtype != SLAPD_AUDIT_LOG ) {
+ PR_snprintf( errorbuf, SLAPI_DSE_RETURNTEXT_SIZE,
+ "%s: invalid log type: %d", attrname, logtype );
+ rv = LDAP_OPERATIONS_ERROR;
+ }
+
+ /* return if we aren't doing this for real */
+ if ( !apply || !minfreespace_str || !*minfreespace_str) {
+ return rv;
+ }
+
+ minfreespace = atoi(minfreespace_str);
+
+ /* Disk space are in MB but store in bytes */
+ if (minfreespace >= 1 ) {
+ minfreespaceB = minfreespace * LOG_MB_IN_BYTES;
+ switch (logtype) {
+ case SLAPD_ACCESS_LOG:
+ LOG_ACCESS_LOCK_WRITE( );
+ loginfo.log_access_minfreespace = minfreespaceB;
+ fe_cfg->accesslog_minfreespace = minfreespace;
+ LOG_ACCESS_UNLOCK_WRITE();
+ break;
+ case SLAPD_ERROR_LOG:
+ LOG_ERROR_LOCK_WRITE( );
+ loginfo.log_error_minfreespace = minfreespaceB;
+ fe_cfg->errorlog_minfreespace = minfreespace;
+ LOG_ERROR_UNLOCK_WRITE();
+ break;
+ case SLAPD_AUDIT_LOG:
+ LOG_AUDIT_LOCK_WRITE( );
+ loginfo.log_audit_minfreespace = minfreespaceB;
+ fe_cfg->auditlog_minfreespace = minfreespace;
+ LOG_AUDIT_UNLOCK_WRITE();
+ break;
+ default:
+ rv = 1;
+ }
+ }
+ return rv;
+}
+/******************************************************************************
+* LOG EXPIRATION TIME
+* Return Values:
+* 1 -- fail
+* 0 -- success
+******************************************************************************/
+int
+log_set_expirationtime(const char *attrname, char *exptime_str, int logtype, char *errorbuf, int apply)
+{
+
+ int eunit, value, exptime;
+ int rsec=0;
+ int rv = 0;
+ slapdFrontendConfig_t *fe_cfg = getFrontendConfig();
+
+ if ( logtype != SLAPD_ACCESS_LOG &&
+ logtype != SLAPD_ERROR_LOG &&
+ logtype != SLAPD_AUDIT_LOG ) {
+ PR_snprintf( errorbuf, SLAPI_DSE_RETURNTEXT_SIZE,
+ "%s: invalid log type: %d", attrname, logtype );
+ rv = LDAP_OPERATIONS_ERROR;
+ }
+
+ /* return if we aren't doing this for real */
+ if ( !apply || !exptime_str || !*exptime_str) {
+ return rv;
+ }
+
+ exptime = atoi(exptime_str);
+
+ switch (logtype) {
+ case SLAPD_ACCESS_LOG:
+ LOG_ACCESS_LOCK_WRITE( );
+ loginfo.log_access_exptime = exptime;
+ eunit = loginfo.log_access_exptimeunit;
+ rsec = loginfo.log_access_rotationtime_secs;
+ break;
+ case SLAPD_ERROR_LOG:
+ LOG_ERROR_LOCK_WRITE( );
+ loginfo.log_error_exptime = exptime;
+ eunit = loginfo.log_error_exptimeunit;
+ rsec = loginfo.log_error_rotationtime_secs;
+ break;
+ case SLAPD_AUDIT_LOG:
+ LOG_AUDIT_LOCK_WRITE( );
+ loginfo.log_audit_exptime = exptime;
+ eunit = loginfo.log_audit_exptimeunit;
+ rsec = loginfo.log_audit_rotationtime_secs;
+ break;
+ default:
+ rv = 1;
+ eunit = -1;
+ }
+
+ if (eunit == LOG_UNIT_MONTHS) {
+ value = 31 * 24 * 60 * 60 * exptime;
+ } else if (eunit == LOG_UNIT_WEEKS) {
+ value = 7 * 24 * 60 * 60 * exptime;
+ } else if (eunit == LOG_UNIT_DAYS) {
+ value = 24 * 60 * 60 * exptime;
+ } else {
+ /* In this case we don't expire */
+ value = -1;
+ }
+
+ if (value < rsec) {
+ value = rsec;
+ }
+
+ switch (logtype) {
+ case SLAPD_ACCESS_LOG:
+ loginfo.log_access_exptime_secs = value;
+ fe_cfg->accesslog_exptime = exptime;
+ LOG_ACCESS_UNLOCK_WRITE();
+ break;
+ case SLAPD_ERROR_LOG:
+ loginfo.log_error_exptime_secs = value;
+ fe_cfg->errorlog_exptime = exptime;
+ LOG_ERROR_UNLOCK_WRITE();
+ break;
+ case SLAPD_AUDIT_LOG:
+ loginfo.log_audit_exptime_secs = value;
+ fe_cfg->auditlog_exptime = exptime;
+ LOG_AUDIT_UNLOCK_WRITE();
+ break;
+ default:
+ rv = 1;
+ }
+
+ return rv;
+}
+/******************************************************************************
+* LOG EXPIRATION TIME UNIT
+* Return Values:
+* 1 -- fail
+* 0 -- success
+******************************************************************************/
+int
+log_set_expirationtimeunit(const char *attrname, char *expunit, int logtype, char *errorbuf, int apply)
+{
+ int value = 0;
+ int rv = 0;
+ int eunit, etimeunit, rsecs;
+ slapdFrontendConfig_t *fe_cfg = getFrontendConfig();
+
+ if ( logtype != SLAPD_ACCESS_LOG &&
+ logtype != SLAPD_ERROR_LOG &&
+ logtype != SLAPD_AUDIT_LOG ) {
+ PR_snprintf( errorbuf, SLAPI_DSE_RETURNTEXT_SIZE,
+ "%s: invalid log type: %d", attrname, logtype );
+ return LDAP_OPERATIONS_ERROR;
+ }
+
+ if ( NULL == expunit ) {
+ PR_snprintf( errorbuf, SLAPI_DSE_RETURNTEXT_SIZE,
+ "%s: NULL value", attrname );
+ return LDAP_OPERATIONS_ERROR;
+ }
+
+ if ( (strcasecmp(expunit, "month") == 0) ||
+ (strcasecmp(expunit, "week") == 0) ||
+ (strcasecmp(expunit, "day") == 0)) {
+ /* we have good values */
+ } else {
+ PR_snprintf( errorbuf, SLAPI_DSE_RETURNTEXT_SIZE,
+ "%s: invalid time unit \"%s\"", attrname, expunit );
+ rv = LDAP_OPERATIONS_ERROR;;
+ }
+
+ /* return if we aren't doing this for real */
+ if ( !apply ) {
+ return rv;
+ }
+
+ switch (logtype) {
+ case SLAPD_ACCESS_LOG:
+ LOG_ACCESS_LOCK_WRITE( );
+ etimeunit = loginfo.log_access_exptime;
+ rsecs = loginfo.log_access_rotationtime_secs;
+ break;
+ case SLAPD_ERROR_LOG:
+ LOG_ERROR_LOCK_WRITE( );
+ etimeunit = loginfo.log_error_exptime;
+ rsecs = loginfo.log_error_rotationtime_secs;
+ break;
+ case SLAPD_AUDIT_LOG:
+ LOG_AUDIT_LOCK_WRITE( );
+ etimeunit = loginfo.log_audit_exptime;
+ rsecs = loginfo.log_audit_rotationtime_secs;
+ break;
+ default:
+ rv = 1;
+ etimeunit = -1;
+ rsecs = -1;
+ }
+
+ if (strcasecmp(expunit, "month") == 0) {
+ eunit = LOG_UNIT_MONTHS;
+ value = 31 * 24 * 60 * 60 * etimeunit;
+ } else if (strcasecmp(expunit, "week") == 0) {
+ eunit = LOG_UNIT_WEEKS;
+ value = 7 * 24 * 60 * 60 * etimeunit;
+ } else if (strcasecmp(expunit, "day") == 0) {
+ eunit = LOG_UNIT_DAYS;
+ value = 24 * 60 * 60 * etimeunit;
+ } else {
+ eunit = LOG_UNIT_UNKNOWN;
+ value = -1;
+ }
+
+ if ((value> 0) && value < rsecs ) {
+ value = rsecs;
+ }
+
+ switch (logtype) {
+ case SLAPD_ACCESS_LOG:
+ loginfo.log_access_exptime_secs = value;
+ slapi_ch_free ( (void **) &(fe_cfg->accesslog_exptimeunit) );
+ fe_cfg->accesslog_exptimeunit = slapi_ch_strdup ( expunit );
+ LOG_ACCESS_UNLOCK_WRITE();
+ break;
+ case SLAPD_ERROR_LOG:
+ loginfo.log_error_exptime_secs = value;
+ slapi_ch_free ( (void **) &(fe_cfg->errorlog_exptimeunit) );
+ fe_cfg->errorlog_exptimeunit = slapi_ch_strdup ( expunit );
+ LOG_ERROR_UNLOCK_WRITE();
+ break;
+ case SLAPD_AUDIT_LOG:
+ loginfo.log_audit_exptime_secs = value;
+ slapi_ch_free ( (void **) &(fe_cfg->auditlog_exptimeunit) );
+ fe_cfg->auditlog_exptimeunit = slapi_ch_strdup ( expunit );
+ LOG_AUDIT_UNLOCK_WRITE();
+ break;
+ default:
+ rv = 1;
+ }
+
+ return rv;
+}
+
+/******************************************************************************
+ * Write title line in log file
+ *****************************************************************************/
+static void
+log_write_title (LOGFD fp)
+{
+ slapdFrontendConfig_t *fe_cfg = getFrontendConfig();
+ char *buildnum = config_get_buildnum();
+ char buff[512];
+ int bufflen = sizeof(buff);
+
+ PR_snprintf(buff, bufflen, "\t%s B%s\n",
+ fe_cfg->versionstring ? fe_cfg->versionstring : "Netscape-Directory",
+ buildnum ? buildnum : "");
+ LOG_WRITE_NOW(fp, buff, strlen(buff), 0);
+
+ if (fe_cfg->localhost) {
+ PR_snprintf(buff, bufflen, "\t%s:%d (%s)\n\n",
+ fe_cfg->localhost,
+ fe_cfg->security ? fe_cfg->secureport : fe_cfg->port,
+ fe_cfg->instancedir ? fe_cfg->instancedir : "");
+ }
+ else {
+ /* If fe_cfg->localhost is not set, ignore fe_cfg->port since
+ * it is the default and might be misleading.
+ */
+ PR_snprintf(buff, bufflen, "\t<host>:<port> (%s)\n\n",
+ fe_cfg->instancedir ? fe_cfg->instancedir : "");
+ }
+ LOG_WRITE_NOW(fp, buff, strlen(buff), 0);
+ slapi_ch_free((void **)&buildnum);
+}
+
+/******************************************************************************
+* init function for the error log
+* Returns:
+* 0 - success
+* 1 - fail
+******************************************************************************/
+int error_log_openf( char *pathname, int locked)
+{
+
+ int rv = 0;
+ int logfile_type =0;
+ char buf[BUFSIZ];
+
+ if (!locked) LOG_ERROR_LOCK_WRITE ();
+ /* save the file name */
+ slapi_ch_free ((void**)&loginfo.log_error_file);
+ loginfo.log_error_file = slapi_ch_strdup(pathname);
+
+ /* store the rotation info fiel path name */
+ sprintf (buf, "%s.rotationinfo",pathname);
+ slapi_ch_free ((void**)&loginfo.log_errorinfo_file);
+ loginfo.log_errorinfo_file = slapi_ch_strdup ( buf );
+
+ /*
+ ** Check if we have a log file already. If we have it then
+ ** we need to parse the header info and update the loginfo
+ ** struct.
+ */
+ logfile_type = log__error_rotationinfof(loginfo.log_errorinfo_file);
+
+ if (log__open_errorlogfile(logfile_type, 1/* got lock*/) != LOG_SUCCESS) {
+ rv = 1;
+ }
+
+ if (!locked) LOG_ERROR_UNLOCK_WRITE();
+ return rv;
+
+}
+/******************************************************************************
+* init function for the audit log
+* Returns:
+* 0 - success
+* 1 - fail
+******************************************************************************/
+int
+audit_log_openf( char *pathname, int locked)
+{
+
+ int rv=0;
+ int logfile_type = 0;
+ char buf[BUFSIZ];
+
+ if (!locked) LOG_AUDIT_LOCK_WRITE( );
+
+ /* store the path name */
+ loginfo.log_audit_file = slapi_ch_strdup ( pathname );
+
+ /* store the rotation info file path name */
+ sprintf (buf, "%s.rotationinfo",pathname);
+ loginfo.log_auditinfo_file = slapi_ch_strdup ( buf );
+
+ /*
+ ** Check if we have a log file already. If we have it then
+ ** we need to parse the header info and update the loginfo
+ ** struct.
+ */
+ logfile_type = log__audit_rotationinfof(loginfo.log_auditinfo_file);
+
+ if (log__open_auditlogfile(logfile_type, 1/* got lock*/) != LOG_SUCCESS) {
+ rv = 1;
+ }
+
+ if (!locked) LOG_AUDIT_UNLOCK_WRITE();
+
+ return rv;
+}
+/******************************************************************************
+* write in the audit log
+******************************************************************************/
+int
+slapd_log_audit_proc (
+ char *buffer,
+ int buf_len)
+{
+ if ( (loginfo.log_audit_state & LOGGING_ENABLED) && (loginfo.log_audit_file != NULL) ){
+ LOG_AUDIT_LOCK_WRITE( );
+ if (log__needrotation(loginfo.log_audit_fdes,
+ SLAPD_AUDIT_LOG) == LOG_ROTATE) {
+ if (log__open_auditlogfile(LOGFILE_NEW, 1) != LOG_SUCCESS) {
+ LDAPDebug(LDAP_DEBUG_ANY,
+ "LOGINFO: Unable to open audit file:%s\n",
+ loginfo.log_audit_file,0,0);
+ LOG_AUDIT_UNLOCK_WRITE();
+ return 0;
+ }
+ while (loginfo.log_audit_rotationsyncclock <= loginfo.log_audit_ctime) {
+ loginfo.log_audit_rotationsyncclock += loginfo.log_audit_rotationtime_secs;
+ }
+ }
+ if (loginfo.log_audit_state & LOGGING_NEED_TITLE) {
+ log_write_title( loginfo.log_audit_fdes);
+ loginfo.log_audit_state &= ~LOGGING_NEED_TITLE;
+ }
+ LOG_WRITE_NOW(loginfo.log_audit_fdes, buffer, buf_len, 0);
+ LOG_AUDIT_UNLOCK_WRITE();
+ return 0;
+ }
+ return 0;
+}
+/******************************************************************************
+* write in the error log
+******************************************************************************/
+int
+slapd_log_error_proc(
+ char *subsystem, /* omitted if NULL */
+ char *fmt,
+ ... )
+{
+ va_list ap_err;
+ va_list ap_file;
+ va_start( ap_err, fmt );
+ va_start( ap_file, fmt );
+ slapd_log_error_proc_internal(subsystem, fmt, ap_err, ap_file);
+ va_end(ap_err);
+ va_end(ap_file);
+ return 0;
+}
+
+static int
+slapd_log_error_proc_internal(
+ char *subsystem, /* omitted if NULL */
+ char *fmt,
+ va_list ap_err,
+ va_list ap_file)
+{
+ int rc = 0;
+
+ if ( (loginfo.log_error_state & LOGGING_ENABLED) && (loginfo.log_error_file != NULL) ) {
+ LOG_ERROR_LOCK_WRITE( );
+ if (log__needrotation(loginfo.log_error_fdes,
+ SLAPD_ERROR_LOG) == LOG_ROTATE) {
+ if (log__open_errorlogfile(LOGFILE_NEW, 1) != LOG_SUCCESS) {
+ LOG_ERROR_UNLOCK_WRITE();
+ return 0;
+ }
+ while (loginfo.log_error_rotationsyncclock <= loginfo.log_error_ctime) {
+ loginfo.log_error_rotationsyncclock += loginfo.log_error_rotationtime_secs;
+ }
+ }
+
+ if (!(detached)) {
+ rc = vslapd_log_error( NULL, subsystem, fmt, ap_err );
+ }
+ if ( loginfo.log_error_fdes != NULL ) {
+ if (loginfo.log_error_state & LOGGING_NEED_TITLE) {
+ log_write_title(loginfo.log_error_fdes);
+ loginfo.log_error_state &= ~LOGGING_NEED_TITLE;
+ }
+ rc = vslapd_log_error( loginfo.log_error_fdes, subsystem, fmt, ap_file );
+ }
+ LOG_ERROR_UNLOCK_WRITE();
+ } else {
+ /* log the problem in the stderr */
+ rc = vslapd_log_error( NULL, subsystem, fmt, ap_err );
+ }
+ return( rc );
+}
+
+static int
+vslapd_log_error(
+ LOGFD fp,
+ char *subsystem, /* omitted if NULL */
+ char *fmt,
+ va_list ap )
+{
+ time_t tnl;
+ long tz;
+ struct tm *tmsp, tms;
+ char tbuf[ TBUFSIZE ];
+ char sign;
+ char buffer[SLAPI_LOG_BUFSIZ];
+ int blen;
+ char *vbuf;
+ int header_len = 0;
+
+ tnl = current_time();
+#ifdef _WIN32
+ {
+ struct tm *pt = localtime( &tnl );
+ tmsp = &tms;
+ memcpy(&tms, pt, sizeof(struct tm) );
+ }
+#else
+ (void)localtime_r( &tnl, &tms );
+ tmsp = &tms;
+#endif
+#ifdef BSD_TIME
+ tz = tmsp->tm_gmtoff;
+#else /* BSD_TIME */
+ tz = - timezone;
+ if ( tmsp->tm_isdst ) {
+ tz += 3600;
+ }
+#endif /* BSD_TIME */
+ sign = ( tz >= 0 ? '+' : '-' );
+ if ( tz < 0 ) {
+ tz = -tz;
+ }
+ (void)strftime( tbuf, (size_t)TBUFSIZE, "%d/%b/%Y:%H:%M:%S", tmsp);
+ sprintf( buffer, "[%s %c%02d%02d]%s%s - ", tbuf, sign,
+ (int)( tz / 3600 ), (int)( tz % 3600 ),
+ subsystem ? " " : "",
+ subsystem ? subsystem : "");
+
+ /* Bug 561525: to be able to remove timestamp to not over pollute syslog, we may need
+ to skip the timestamp part of the message.
+ The size of the header is:
+ the size of the time string
+ + size of space
+ + size of one char (sign)
+ + size of 2 char
+ + size of 2 char
+ + size of [
+ + size of ]
+ */
+
+ header_len = strlen(tbuf) + 8;
+
+ if ((vbuf = PR_vsmprintf(fmt, ap)) == NULL) {
+ return -1;
+ }
+ blen = strlen(buffer);
+ if ((unsigned int)(SLAPI_LOG_BUFSIZ - blen ) < strlen(vbuf)) {
+ free (vbuf);
+ return -1;
+ }
+
+ sprintf (buffer+blen, "%s", vbuf);
+
+ if (fp)
+ LOG_WRITE_NOW(fp, buffer, strlen(buffer), header_len);
+
+
+ else /* stderr is always unbuffered */
+ fprintf(stderr, "%s", buffer);
+
+ free (vbuf);
+ return( 0 );
+}
+
+int
+slapi_log_error( int severity, char *subsystem, char *fmt, ... )
+{
+ va_list ap1;
+ va_list ap2;
+ int rc;
+
+ if ( severity < SLAPI_LOG_MIN || severity > SLAPI_LOG_MAX ) {
+ (void)slapd_log_error_proc( subsystem,
+ "slapi_log_error: invalid severity %d (message %s)\n",
+ severity, fmt );
+ return( -1 );
+ }
+
+
+#ifdef _WIN32
+ if ( *module_ldap_debug
+#else
+ if ( slapd_ldap_debug
+#endif
+ & slapi_log_map[ severity ] ) {
+ va_start( ap1, fmt );
+ va_start( ap2, fmt );
+ rc = slapd_log_error_proc_internal( subsystem, fmt, ap1, ap2 );
+ va_end( ap1 );
+ va_end( ap2 );
+ } else {
+ rc = 0; /* nothing to be logged --> always return success */
+ }
+
+ return( rc );
+}
+
+int
+slapi_is_loglevel_set ( const int loglevel )
+{
+
+ return (
+#ifdef _WIN32
+ *module_ldap_debug
+#else
+ slapd_ldap_debug
+#endif
+ & slapi_log_map[ loglevel ] ? 1 : 0);
+}
+
+
+/******************************************************************************
+* write in the access log
+******************************************************************************/
+static int vslapd_log_access(char *fmt, va_list ap)
+{
+ time_t tnl;
+ long tz;
+ struct tm *tmsp, tms;
+ char tbuf[ TBUFSIZE ];
+ char sign;
+ char buffer[SLAPI_LOG_BUFSIZ];
+ char vbuf[SLAPI_LOG_BUFSIZ];
+ int blen, vlen;
+ /* info needed to keep us from calling localtime/strftime so often: */
+ static time_t old_time = 0;
+ static char old_tbuf[TBUFSIZE];
+ static int old_blen = 0;
+
+ tnl = current_time();
+
+ /* check if we can use the old strftime buffer */
+ PR_Lock(ts_time_lock);
+ if (tnl == old_time) {
+ strcpy(buffer, old_tbuf);
+ blen = old_blen;
+ PR_Unlock(ts_time_lock);
+ } else {
+ /* nope... painstakingly create the new strftime buffer */
+#ifdef _WIN32
+ {
+ struct tm *pt = localtime( &tnl );
+ tmsp = &tms;
+ memcpy(&tms, pt, sizeof(struct tm) );
+ }
+#else
+ (void)localtime_r( &tnl, &tms );
+ tmsp = &tms;
+#endif
+
+#ifdef BSD_TIME
+ tz = tmsp->tm_gmtoff;
+#else /* BSD_TIME */
+ tz = - timezone;
+ if ( tmsp->tm_isdst ) {
+ tz += 3600;
+ }
+#endif /* BSD_TIME */
+ sign = ( tz >= 0 ? '+' : '-' );
+ if ( tz < 0 ) {
+ tz = -tz;
+ }
+ (void)strftime( tbuf, (size_t)TBUFSIZE, "%d/%b/%Y:%H:%M:%S", tmsp);
+ sprintf( buffer, "[%s %c%02d%02d] ", tbuf, sign,
+ (int)( tz / 3600 ), (int)( tz % 3600));
+ old_time = tnl;
+ strcpy(old_tbuf, buffer);
+ blen = strlen(buffer);
+ old_blen = blen;
+ PR_Unlock(ts_time_lock);
+ }
+
+ vlen = PR_vsnprintf(vbuf, SLAPI_LOG_BUFSIZ, fmt, ap);
+ if (! vlen) {
+ return -1;
+ }
+
+ if (SLAPI_LOG_BUFSIZ - blen < vlen) {
+ return -1;
+ }
+
+ log_append_buffer2(tnl, loginfo.log_access_buffer, buffer, blen, vbuf, vlen);
+
+ return( 0 );
+}
+
+int
+slapi_log_access( int level,
+ char *fmt,
+ ... )
+{
+ va_list ap;
+ int rc=0;
+
+ if (!(loginfo.log_access_state & LOGGING_ENABLED)) {
+ return 0;
+ }
+ va_start( ap, fmt );
+ if (( level & loginfo.log_access_level ) &&
+ ( loginfo.log_access_fdes != NULL ) && (loginfo.log_access_file != NULL) ) {
+ rc = vslapd_log_access(fmt, ap);
+ }
+ va_end( ap );
+ return( rc );
+}
+
+/******************************************************************************
+* access_log_openf
+*
+* Open the access log file
+*
+* Returns:
+* 0 -- success
+* 1 -- fail
+******************************************************************************/
+int access_log_openf(char *pathname, int locked)
+{
+ int rv=0;
+ int logfile_type = 0;
+ char buf[BUFSIZ];
+
+ if (!locked) LOG_ACCESS_LOCK_WRITE( );
+
+ /* store the path name */
+ loginfo.log_access_file = slapi_ch_strdup ( pathname );
+
+ /* store the rotation info fiel path name */
+ sprintf (buf, "%s.rotationinfo",pathname);
+ loginfo.log_accessinfo_file = slapi_ch_strdup ( buf );
+
+ /*
+ ** Check if we have a log file already. If we have it then
+ ** we need to parse the header info and update the loginfo
+ ** struct.
+ */
+ logfile_type = log__access_rotationinfof(loginfo.log_accessinfo_file);
+
+ if (log__open_accesslogfile(logfile_type, 1/* got lock*/) != LOG_SUCCESS) {
+ rv = 1;
+ }
+
+ if (!locked) LOG_ACCESS_UNLOCK_WRITE();
+
+
+ return rv;
+}
+
+/******************************************************************************
+* log__open_accesslogfile
+*
+* Open a new log file. If we have run out of the max logs we can have
+* then delete the oldest file.
+******************************************************************************/
+static int
+log__open_accesslogfile(int logfile_state, int locked)
+{
+
+ time_t now;
+ LOGFD fp;
+ LOGFD fpinfo = NULL;
+ char tbuf[TBUFSIZE];
+ struct logfileinfo *logp;
+ char buffer[BUFSIZ];
+
+ if (!locked) LOG_ACCESS_LOCK_WRITE( );
+
+ /*
+ ** Here we are trying to create a new log file.
+ ** If we alredy have one, then we need to rename it as
+ ** "filename.time", close it and update it's information
+ ** in the array stack.
+ */
+ if (loginfo.log_access_fdes != NULL) {
+ struct logfileinfo *log;
+ char newfile[BUFSIZ];
+ int f_size;
+
+ /* get rid of the old one */
+ if ((f_size = log__getfilesize(loginfo.log_access_fdes)) == -1) {
+ /* Then assume that we have the max size */
+ f_size = loginfo.log_access_maxlogsize;
+ }
+
+ /* Check if I have to delete any old file, delete it if it is required.
+ ** If there is just one file, then access and access.rotation files
+ ** are deleted. After that we start fresh
+ */
+ while (log__delete_access_logfile());
+
+ /* close the file */
+ LOG_CLOSE(loginfo.log_access_fdes);
+ /*
+ * loginfo.log_access_fdes is not set to NULL here, otherwise
+ * slapi_log_access() will not send a message to the access log
+ * if it is called between this point and where this field is
+ * set again after calling LOG_OPEN_APPEND.
+ */
+ if ( loginfo.log_access_maxnumlogs > 1 ) {
+ log = (struct logfileinfo *) slapi_ch_malloc (sizeof (struct logfileinfo));
+ log->l_ctime = loginfo.log_access_ctime;
+ log->l_size = f_size;
+
+ log_convert_time (log->l_ctime, tbuf, 1 /*short */);
+ sprintf(newfile, "%s.%s", loginfo.log_access_file, tbuf);
+ if (PR_Rename (loginfo.log_access_file, newfile) != PR_SUCCESS) {
+ loginfo.log_access_fdes = NULL;
+ if (!locked) LOG_ACCESS_UNLOCK_WRITE();
+ return LOG_UNABLE_TO_OPENFILE;
+ }
+ /* add the log to the chain */
+ log->l_next = loginfo.log_access_logchain;
+ loginfo.log_access_logchain = log;
+ loginfo.log_numof_access_logs++;
+ }
+ }
+
+
+ /* open a new log file */
+ if (! LOG_OPEN_APPEND(fp, loginfo.log_access_file, loginfo.log_access_mode)) {
+ int oserr = errno;
+ loginfo.log_access_fdes = NULL;
+ if (!locked) LOG_ACCESS_UNLOCK_WRITE();
+ LDAPDebug( LDAP_DEBUG_ANY, "access file open %s failed errno %d (%s)\n",
+ loginfo.log_access_file,
+ oserr, slapd_system_strerror(oserr));
+ return LOG_UNABLE_TO_OPENFILE;
+ }
+
+ loginfo.log_access_fdes = fp;
+ if (logfile_state == LOGFILE_REOPENED) {
+ /* we have all the information */
+ if (!locked) LOG_ACCESS_UNLOCK_WRITE( );
+ return LOG_SUCCESS;
+ }
+
+ loginfo.log_access_state |= LOGGING_NEED_TITLE;
+
+ if (! LOG_OPEN_WRITE(fpinfo, loginfo.log_accessinfo_file, loginfo.log_access_mode)) {
+ int oserr = errno;
+ if (!locked) LOG_ACCESS_UNLOCK_WRITE();
+ LDAPDebug( LDAP_DEBUG_ANY, "accessinfo file open %s failed errno %d (%s)\n",
+ loginfo.log_accessinfo_file,
+ oserr, slapd_system_strerror(oserr));
+ return LOG_UNABLE_TO_OPENFILE;
+ }
+
+
+ /* write the header in the log */
+ now = current_time();
+ log_convert_time (now, tbuf, 2 /* long */);
+ sprintf (buffer,"LOGINFO:Log file created at: %s (%lu)\n", tbuf, now);
+ LOG_WRITE(fpinfo, buffer, strlen(buffer), 0);
+
+ logp = loginfo.log_access_logchain;
+ while ( logp) {
+ log_convert_time (logp->l_ctime, tbuf, 1 /*short*/);
+ sprintf(buffer, "LOGINFO:Previous Log File:%s.%s (%lu) (%u)\n",
+ loginfo.log_access_file, tbuf, logp->l_ctime, logp->l_size);
+ LOG_WRITE(fpinfo, buffer, strlen(buffer), 0);
+ logp = logp->l_next;
+ }
+ /* Close the info file. We need only when we need to rotate to the
+ ** next log file.
+ */
+ if (fpinfo) LOG_CLOSE(fpinfo);
+
+ /* This is now the current access log */
+ loginfo.log_access_ctime = now;
+
+ if (!locked) LOG_ACCESS_UNLOCK_WRITE( );
+ return LOG_SUCCESS;
+}
+/******************************************************************************
+* log__needrotation
+*
+* Do we need to rotate the log file ?
+* Find out based on rotation time and the max log size;
+*
+* Return:
+* LOG_CONTINUE -- Use the same one
+* LOG_ROTATE -- log need to be rotated
+*
+* Note:
+* A READ LOCK is obtained.
+********************************************************************************/
+#define LOG_SIZE_EXCEEDED 1
+#define LOG_EXPIRED 2
+static int
+log__needrotation(LOGFD fp, int logtype)
+{
+ time_t curr_time;
+ time_t log_createtime= 0;
+ time_t syncclock;
+ int type = LOG_CONTINUE;
+ int f_size = 0;
+ int maxlogsize, nlogs;
+ int rotationtime_secs = -1;
+ int sync_enabled, synchour, syncmin, timeunit;
+
+ if (fp == NULL) {
+ return LOG_ROTATE;
+ }
+
+ switch (logtype) {
+ case SLAPD_ACCESS_LOG:
+ nlogs = loginfo.log_access_maxnumlogs;
+ maxlogsize = loginfo.log_access_maxlogsize;
+ sync_enabled = loginfo.log_access_rotationsync_enabled;
+ synchour = loginfo.log_access_rotationsynchour;
+ syncmin = loginfo.log_access_rotationsyncmin;
+ syncclock = loginfo.log_access_rotationsyncclock;
+ timeunit = loginfo.log_access_rotationunit;
+ rotationtime_secs = loginfo.log_access_rotationtime_secs;
+ log_createtime = loginfo.log_access_ctime;
+ break;
+ case SLAPD_ERROR_LOG:
+ nlogs = loginfo.log_error_maxnumlogs;
+ maxlogsize = loginfo.log_error_maxlogsize;
+ sync_enabled = loginfo.log_error_rotationsync_enabled;
+ synchour = loginfo.log_error_rotationsynchour;
+ syncmin = loginfo.log_error_rotationsyncmin;
+ syncclock = loginfo.log_error_rotationsyncclock;
+ timeunit = loginfo.log_error_rotationunit;
+ rotationtime_secs = loginfo.log_error_rotationtime_secs;
+ log_createtime = loginfo.log_error_ctime;
+ break;
+ case SLAPD_AUDIT_LOG:
+ nlogs = loginfo.log_audit_maxnumlogs;
+ maxlogsize = loginfo.log_audit_maxlogsize;
+ sync_enabled = loginfo.log_audit_rotationsync_enabled;
+ synchour = loginfo.log_audit_rotationsynchour;
+ syncmin = loginfo.log_audit_rotationsyncmin;
+ syncclock = loginfo.log_audit_rotationsyncclock;
+ timeunit = loginfo.log_audit_rotationunit;
+ rotationtime_secs = loginfo.log_audit_rotationtime_secs;
+ log_createtime = loginfo.log_audit_ctime;
+ break;
+ default: /* error */
+ maxlogsize = -1;
+ nlogs = 1;
+
+ }
+
+ /* If we have one log then can't rotate at all */
+ if (nlogs == 1)
+ return LOG_CONTINUE;
+
+ if ((f_size = log__getfilesize(fp)) == -1) {
+ /* The next option is to rotate based on the rotation time */
+ f_size = 0;
+ }
+
+ /* If the log size is more than the limit, then it's time to rotate. */
+ if ((maxlogsize > 0) && (f_size >= maxlogsize)) {
+ type = LOG_SIZE_EXCEEDED;
+ goto log_rotate;
+ }
+
+ /* If rotation interval <= 0 then can't rotate by time */
+ if (rotationtime_secs <= 0)
+ return LOG_CONTINUE;
+
+ /*
+ ** If the log is older than the time allowed to be active,
+ ** then it's time to move on (i.e., rotate).
+ */
+ time (&curr_time);
+
+ if ( !sync_enabled || timeunit == LOG_UNIT_HOURS || timeunit == LOG_UNIT_MINS ) {
+ if (curr_time - log_createtime > rotationtime_secs) {
+ type = LOG_EXPIRED;
+ goto log_rotate;
+ }
+
+ } else if (curr_time > syncclock) {
+ type = LOG_EXPIRED;
+ goto log_rotate;
+ }
+
+log_rotate:
+ /*
+ ** Don't send messages to the error log whilst we're rotating it.
+ ** This'll lead to a recursive call to the logging function, and
+ ** an assertion trying to relock the write lock.
+ */
+ if (logtype!=SLAPD_ERROR_LOG)
+ {
+ if (type == LOG_SIZE_EXCEEDED) {
+ LDAPDebug (LDAP_DEBUG_TRACE,
+ "LOGINFO:End of Log because size exceeded(Max:%d bytes) (Is:%d bytes)\n", maxlogsize, f_size, 0);
+ } else if ( type == LOG_EXPIRED) {
+ LDAPDebug(LDAP_DEBUG_TRACE,
+ "LOGINFO:End of Log because time exceeded(Max:%d secs) (Is:%d secs)\n",
+ rotationtime_secs, curr_time - log_createtime,0);
+ }
+ }
+ return (type == LOG_CONTINUE) ? LOG_CONTINUE : LOG_ROTATE;
+}
+
+/******************************************************************************
+* log__delete_access_logfile
+*
+* Do we need to delete a logfile. Find out if we need to delete the log
+* file based on expiration time, max diskspace, and minfreespace.
+* Delete the file if we need to.
+*
+* Assumption: A WRITE lock has been acquired for the ACCESS
+******************************************************************************/
+
+static int
+log__delete_access_logfile()
+{
+
+ struct logfileinfo *logp = NULL;
+ struct logfileinfo *delete_logp = NULL;
+ struct logfileinfo *p_delete_logp = NULL;
+ struct logfileinfo *prev_logp = NULL;
+ int total_size=0;
+ time_t cur_time;
+ int f_size;
+ int numoflogs=loginfo.log_numof_access_logs;
+ int rv = 0;
+ char *logstr;
+ char buffer[BUFSIZ];
+ char tbuf[TBUFSIZE];
+
+ /* If we have only one log, then will delete this one */
+ if (loginfo.log_access_maxnumlogs == 1) {
+ LOG_CLOSE(loginfo.log_access_fdes);
+ loginfo.log_access_fdes = NULL;
+ sprintf (buffer, "%s", loginfo.log_access_file);
+ if (PR_Delete(buffer) != PR_SUCCESS) {
+ LDAPDebug(LDAP_DEBUG_TRACE,
+ "LOGINFO:Unable to remove file:%s\n", loginfo.log_access_file,0,0);
+ }
+
+ /* Delete the rotation file also. */
+ sprintf (buffer, "%s.rotationinfo", loginfo.log_access_file);
+ if (PR_Delete(buffer) != PR_SUCCESS) {
+ LDAPDebug(LDAP_DEBUG_TRACE,
+ "LOGINFO:Unable to remove file:%s.rotationinfo\n", loginfo.log_access_file,0,0);
+ }
+ return 0;
+ }
+
+ /* If we have already the maximum number of log files, we
+ ** have to delete one any how.
+ */
+ if (++numoflogs > loginfo.log_access_maxnumlogs) {
+ logstr = "Exceeded max number of logs allowed";
+ goto delete_logfile;
+ }
+
+ /* Now check based on the maxdiskspace */
+ if (loginfo.log_access_maxdiskspace > 0) {
+ logp = loginfo.log_access_logchain;
+ while (logp) {
+ total_size += logp->l_size;
+ logp = logp->l_next;
+ }
+ if ((f_size = log__getfilesize(loginfo.log_access_fdes)) == -1) {
+ /* then just assume the max size */
+ total_size += loginfo.log_access_maxlogsize;
+ } else {
+ total_size += f_size;
+ }
+
+ /* If we have exceeded the max disk space or we have less than the
+ ** minimum, then we have to delete a file.
+ */
+ if (total_size >= loginfo.log_access_maxdiskspace) {
+ logstr = "exceeded maximum log disk space";
+ goto delete_logfile;
+ }
+ }
+
+ /* Now check based on the free space */
+ if ( loginfo.log_access_minfreespace > 0) {
+ rv = log__enough_freespace(loginfo.log_access_file);
+ if ( rv == 0) {
+ /* Not enough free space */
+ logstr = "Not enough free disk space";
+ goto delete_logfile;
+ }
+ }
+
+ /* Now check based on the expiration time */
+ if ( loginfo.log_access_exptime_secs > 0 ) {
+ /* is the file old enough */
+ time (&cur_time);
+ prev_logp = logp = loginfo.log_access_logchain;
+ while (logp) {
+ if ((cur_time - logp->l_ctime) > loginfo.log_access_exptime_secs) {
+ delete_logp = logp;
+ p_delete_logp = prev_logp;
+ logstr = "The file is older than the log expiration time";
+ goto delete_logfile;
+ }
+ prev_logp = logp;
+ logp = logp->l_next;
+ }
+ }
+
+ /* No log files to delete */
+ return 0;
+
+delete_logfile:
+ if (delete_logp == NULL) {
+ time_t oldest;
+
+ time(&oldest);
+
+ prev_logp = logp = loginfo.log_access_logchain;
+ while (logp) {
+ if (logp->l_ctime <= oldest) {
+ oldest = logp->l_ctime;
+ delete_logp = logp;
+ p_delete_logp = prev_logp;
+ }
+ prev_logp = logp;
+ logp = logp->l_next;
+ }
+ /* We might face this case if we have only one log file and
+ ** trying to delete it because of deletion requirement.
+ */
+ if (!delete_logp) {
+ return 0;
+ }
+ }
+
+ if (p_delete_logp == delete_logp) {
+ /* then we are deleteing the first one */
+ loginfo.log_access_logchain = delete_logp->l_next;
+ } else {
+ p_delete_logp->l_next = delete_logp->l_next;
+ }
+
+
+ /* Delete the access file */
+ log_convert_time (delete_logp->l_ctime, tbuf, 1 /*short */);
+ sprintf (buffer, "%s.%s", loginfo.log_access_file, tbuf);
+ if (PR_Delete(buffer) != PR_SUCCESS) {
+ LDAPDebug(LDAP_DEBUG_TRACE,
+ "LOGINFO:Unable to remove file:%s.%s\n",
+ loginfo.log_access_file,tbuf,0);
+
+ } else {
+ LDAPDebug(LDAP_DEBUG_TRACE,
+ "LOGINFO:Removed file:%s.%s because of (%s)\n",
+ loginfo.log_access_file, tbuf,
+ logstr);
+ }
+ slapi_ch_free((void**)&delete_logp);
+ loginfo.log_numof_access_logs--;
+
+ return 1;
+}
+/******************************************************************************
+* log__access_rotationinfof
+*
+* Try to open the log file. If we have one already, then try to read the
+* header and update the information.
+*
+* Assumption: Lock has been acquired already
+******************************************************************************/
+static int
+log__access_rotationinfof( char *pathname)
+{
+ long f_ctime;
+ int f_size;
+ int main_log = 1;
+ time_t now;
+ FILE *fp;
+
+
+ /*
+ ** Okay -- I confess, we want to use NSPR calls but I want to
+ ** use fgets and not use PR_Read() and implement a complicated
+ ** parsing module. Since this will be called only during the startup
+ ** and never aftre that, we can live by it.
+ */
+
+ if ((fp = fopen (pathname, "r")) == NULL) {
+ return LOGFILE_NEW;
+ }
+
+ loginfo.log_numof_access_logs = 0;
+
+ /*
+ ** We have reopened the log access file. Now we need to read the
+ ** log file info and update the values.
+ */
+ while (log__extract_logheader(fp, &f_ctime, &f_size) == LOG_CONTINUE) {
+ /* first we would get the main log info */
+ if (f_ctime == 0 && f_size == 0)
+ continue;
+
+ time (&now);
+ if (main_log) {
+ if (f_ctime > 0L)
+ loginfo.log_access_ctime = f_ctime;
+ else {
+ loginfo.log_access_ctime = now;
+ }
+ main_log = 0;
+ } else {
+ struct logfileinfo *logp;
+
+ logp = (struct logfileinfo *) slapi_ch_malloc (sizeof (struct logfileinfo));
+ if (f_ctime > 0L)
+ logp->l_ctime = f_ctime;
+ else
+ logp->l_ctime = now;
+ if (f_size > 0)
+ logp->l_size = f_size;
+ else {
+ /* make it the max log size */
+ logp->l_size = loginfo.log_access_maxlogsize;
+ }
+
+ logp->l_next = loginfo.log_access_logchain;
+ loginfo.log_access_logchain = logp;
+ }
+ loginfo.log_numof_access_logs++;
+ }
+
+ /* Check if there is a rotation overdue */
+ if (loginfo.log_access_rotationsync_enabled &&
+ loginfo.log_access_rotationunit != LOG_UNIT_HOURS &&
+ loginfo.log_access_rotationunit != LOG_UNIT_MINS &&
+ loginfo.log_access_ctime < loginfo.log_access_rotationsyncclock - loginfo.log_access_rotationtime_secs) {
+ loginfo.log_access_rotationsyncclock -= loginfo.log_access_rotationtime_secs;
+ }
+ fclose (fp);
+ return LOGFILE_REOPENED;
+}
+
+/******************************************************************************
+* log__extract_logheader
+*
+* Extract each LOGINFO heder line. From there extract the time and
+* size info of all the old log files.
+******************************************************************************/
+static int
+log__extract_logheader (FILE *fp, long *f_ctime, int *f_size)
+{
+
+ char buf[BUFSIZ];
+ char *p, *s, *next;
+
+ *f_ctime = 0L;
+ *f_size = 0;
+
+ if ( fp == NULL)
+ return LOG_ERROR;
+
+ if (fgets(buf, BUFSIZ, fp) == NULL) {
+ return LOG_ERROR;
+ }
+
+ if ((p=strstr(buf, "LOGINFO")) == NULL) {
+ return LOG_ERROR;
+ }
+
+ s = p;
+ if ((p = strchr(p, '(')) == NULL) {
+ return LOG_CONTINUE;
+ }
+ if ((next= strchr(p, ')')) == NULL) {
+ return LOG_CONTINUE;
+ }
+
+ p++;
+ s = next;
+ next++;
+ *s = '\0';
+
+ /* Now p must hold the ctime value */
+ *f_ctime = atoi(p);
+
+ if ((p = strchr(next, '(')) == NULL) {
+ /* that's fine -- it means we have no size info */
+ *f_size = 0;
+ return LOG_CONTINUE;
+ }
+
+ if ((next= strchr(p, ')')) == NULL) {
+ return LOG_CONTINUE;
+ }
+
+ p++;
+ *next = '\0';
+
+ /* Now p must hold the size value */
+ *f_size = atoi(p);
+
+ return LOG_CONTINUE;
+
+}
+
+/******************************************************************************
+* log__getfilesize
+* Get the file size
+*
+* Assumption: Lock has been acquired already.
+******************************************************************************/
+/* this kinda has to be diff't on each platform :( */
+/* using an int implies that all logfiles will be under 2G. this is
+ * probably a safe assumption for now.
+ */
+#ifdef XP_WIN32
+static int
+log__getfilesize(LOGFD fp)
+{
+ struct stat info;
+ int rv;
+
+ if ((rv = fstat(fileno(fp), &info)) != 0) {
+ return -1;
+ }
+ return info.st_size;
+}
+#else
+static int
+log__getfilesize(LOGFD fp)
+{
+ PRFileInfo info;
+ int rv;
+
+ if ((rv = PR_GetOpenFileInfo (fp, &info)) == PR_FAILURE) {
+ return -1;
+ }
+ return info.size;
+}
+#endif
+
+
+/******************************************************************************
+* log__enough_freespace
+*
+* Returns:
+* 1 - we have enough space
+* 0 - No the avialable space is less than recomended
+* Assumption: Lock has been acquired already.
+******************************************************************************/
+static int
+log__enough_freespace(char *path)
+{
+
+#ifdef _WIN32
+DWORD sectorsPerCluster, bytesPerSector, freeClusters, totalClusters;
+char rootpath[4];
+#else
+#ifdef LINUX
+ struct statfs buf;
+#else
+ struct statvfs buf;
+#endif /* LINUX */
+#endif
+ PRInt64 freeBytes;
+ PRInt64 tmpval;
+
+
+#ifdef _WIN32
+ strncpy(rootpath, path, 3);
+ rootpath[3] = '\0';
+ /* we should consider using GetDiskFreeSpaceEx here someday */
+ if ( !GetDiskFreeSpace(rootpath, &sectorsPerCluster, &bytesPerSector,
+ &freeClusters, &totalClusters)) {
+ LDAPDebug(LDAP_DEBUG_ANY,
+ "log__enough_freespace: Unable to get the free space\n",0,0,0);
+ return 1;
+ } else {
+ LL_UI2L(freeBytes, freeClusters);
+ LL_UI2L(tmpval, sectorsPerCluster);
+ LL_MUL(freeBytes, freeBytes, tmpval);
+ LL_UI2L(tmpval, bytesPerSector);
+ LL_MUL(freeBytes, freeBytes, tmpval);
+/* freeBytes = freeClusters * sectorsPerCluster * bytesPerSector; */
+
+ }
+
+#else
+#ifdef LINUX
+ if (statfs(path, &buf) == -1)
+#else
+ if (statvfs(path, &buf) == -1)
+#endif
+ {
+ int oserr = errno;
+ LDAPDebug(LDAP_DEBUG_ANY,
+ "log__enough_freespace: Unable to get the free space (errno:%d)\n",
+ oserr,0,0);
+ return 1;
+ } else {
+ LL_UI2L(freeBytes, buf.f_bavail);
+ LL_UI2L(tmpval, buf.f_bsize);
+ LL_MUL(freeBytes, freeBytes, tmpval);
+ /* freeBytes = buf.f_bavail * buf.f_bsize; */
+ }
+#endif
+ LL_UI2L(tmpval, loginfo.log_access_minfreespace);
+ if (LL_UCMP(freeBytes, <, tmpval)) {
+ /* if (freeBytes < loginfo.log_access_minfreespace) { */
+ return 0;
+ }
+ return 1;
+}
+/******************************************************************************
+* log__getaccesslist
+* Update the previous access files in the slapdFrontendConfig_t.
+* Returns:
+* num > 1 -- how many are there
+* 0 -- otherwise
+******************************************************************************/
+char **
+log_get_loglist(int logtype)
+{
+ char **list=NULL;
+ int num, i;
+ LogFileInfo *logp = NULL;
+ char buf[BUFSIZ];
+ char tbuf[TBUFSIZE];
+ char *file;
+
+ switch (logtype) {
+ case SLAPD_ACCESS_LOG:
+ LOG_ACCESS_LOCK_READ( );
+ num = loginfo.log_numof_access_logs;
+ logp = loginfo.log_access_logchain;
+ file = loginfo.log_access_file;
+ break;
+ case SLAPD_ERROR_LOG:
+ LOG_ERROR_LOCK_READ( );
+ num = loginfo.log_numof_error_logs;
+ logp = loginfo.log_error_logchain;
+ file = loginfo.log_error_file;
+ break;
+ case SLAPD_AUDIT_LOG:
+ LOG_AUDIT_LOCK_READ( );
+ num = loginfo.log_numof_audit_logs;
+ logp = loginfo.log_audit_logchain;
+ file = loginfo.log_audit_file;
+ break;
+ default:
+ return NULL;
+ }
+ list = (char **) slapi_ch_calloc(1, num * sizeof(char *));
+ i = 0;
+ while (logp) {
+ log_convert_time (logp->l_ctime, tbuf, 1 /*short */);
+ sprintf(buf, "%s.%s", file, tbuf);
+ list[i] = slapi_ch_strdup(buf);
+ i++;
+ logp = logp->l_next;
+ }
+ list[i] = NULL;
+
+ switch (logtype) {
+ case SLAPD_ACCESS_LOG:
+ LOG_ACCESS_UNLOCK_READ();
+ break;
+ case SLAPD_ERROR_LOG:
+ LOG_ERROR_UNLOCK_READ();
+ break;
+ case SLAPD_AUDIT_LOG:
+ LOG_AUDIT_UNLOCK_READ();
+ break;
+ }
+ return list;
+}
+
+/******************************************************************************
+* log__delete_error_logfile
+*
+* Do we need to delete a logfile. Find out if we need to delete the log
+* file based on expiration time, max diskspace, and minfreespace.
+* Delete the file if we need to.
+*
+* Assumption: A WRITE lock has been acquired for the error log.
+******************************************************************************/
+
+static int
+log__delete_error_logfile()
+{
+
+ struct logfileinfo *logp = NULL;
+ struct logfileinfo *delete_logp = NULL;
+ struct logfileinfo *p_delete_logp = NULL;
+ struct logfileinfo *prev_logp = NULL;
+ int total_size=0;
+ time_t cur_time;
+ int f_size;
+ int numoflogs=loginfo.log_numof_error_logs;
+ int rv = 0;
+ char *logstr;
+ char buffer[BUFSIZ];
+ char tbuf[TBUFSIZE];
+
+
+ /* If we have only one log, then will delete this one */
+ if (loginfo.log_error_maxnumlogs == 1) {
+ LOG_CLOSE(loginfo.log_error_fdes);
+ loginfo.log_error_fdes = NULL;
+ sprintf (buffer, "%s", loginfo.log_error_file);
+ if (PR_Delete(buffer) != PR_SUCCESS) {
+ LDAPDebug(LDAP_DEBUG_TRACE,
+ "LOGINFO:Unable to remove file:%s\n", loginfo.log_error_file,0,0);
+ }
+
+ /* Delete the rotation file also. */
+ sprintf (buffer, "%s.rotationinfo", loginfo.log_error_file);
+ if (PR_Delete(buffer) != PR_SUCCESS) {
+ LDAPDebug(LDAP_DEBUG_TRACE,
+ "LOGINFO:Unable to remove file:%s.rotationinfo\n", loginfo.log_error_file,0,0);
+ }
+ return 0;
+ }
+
+ /* If we have already the maximum number of log files, we
+ ** have to delete one any how.
+ */
+ if (++numoflogs > loginfo.log_error_maxnumlogs) {
+ logstr = "Exceeded max number of logs allowed";
+ goto delete_logfile;
+ }
+
+ /* Now check based on the maxdiskspace */
+ if (loginfo.log_error_maxdiskspace > 0) {
+ logp = loginfo.log_error_logchain;
+ while (logp) {
+ total_size += logp->l_size;
+ logp = logp->l_next;
+ }
+ if ((f_size = log__getfilesize(loginfo.log_error_fdes)) == -1) {
+ /* then just assume the max size */
+ total_size += loginfo.log_error_maxlogsize;
+ } else {
+ total_size += f_size;
+ }
+
+ /* If we have exceeded the max disk space or we have less than the
+ ** minimum, then we have to delete a file.
+ */
+ if (total_size >= loginfo.log_error_maxdiskspace) {
+ logstr = "exceeded maximum log disk space";
+ goto delete_logfile;
+ }
+ }
+
+ /* Now check based on the free space */
+ if ( loginfo.log_error_minfreespace > 0) {
+ rv = log__enough_freespace(loginfo.log_error_file);
+ if ( rv == 0) {
+ /* Not enough free space */
+ logstr = "Not enough free disk space";
+ goto delete_logfile;
+ }
+ }
+
+ /* Now check based on the expiration time */
+ if ( loginfo.log_error_exptime_secs > 0 ) {
+ /* is the file old enough */
+ time (&cur_time);
+ prev_logp = logp = loginfo.log_error_logchain;
+ while (logp) {
+ if ((cur_time - logp->l_ctime) > loginfo.log_error_exptime_secs) {
+ delete_logp = logp;
+ p_delete_logp = prev_logp;
+ logstr = "The file is older than the log expiration time";
+ goto delete_logfile;
+ }
+ prev_logp = logp;
+ logp = logp->l_next;
+ }
+ }
+
+ /* No log files to delete */
+ return 0;
+
+delete_logfile:
+ if (delete_logp == NULL) {
+ time_t oldest;
+
+ time(&oldest);
+
+ prev_logp = logp = loginfo.log_error_logchain;
+ while (logp) {
+ if (logp->l_ctime <= oldest) {
+ oldest = logp->l_ctime;
+ delete_logp = logp;
+ p_delete_logp = prev_logp;
+ }
+ prev_logp = logp;
+ logp = logp->l_next;
+ }
+ /* We might face this case if we have only one log file and
+ ** trying to delete it because of deletion requirement.
+ */
+ if (!delete_logp) {
+ return 0;
+ }
+ }
+
+ if (p_delete_logp == delete_logp) {
+ /* then we are deleteing the first one */
+ loginfo.log_error_logchain = delete_logp->l_next;
+ } else {
+ p_delete_logp->l_next = delete_logp->l_next;
+ }
+
+ /* Delete the error file */
+ log_convert_time (delete_logp->l_ctime, tbuf, 1 /*short */);
+ sprintf (buffer, "%s.%s", loginfo.log_error_file, tbuf);
+ PR_Delete(buffer);
+ slapi_ch_free((void**)&delete_logp);
+ loginfo.log_numof_error_logs--;
+
+ return 1;
+}
+
+/******************************************************************************
+* log__delete_audit_logfile
+*
+* Do we need to delete a logfile. Find out if we need to delete the log
+* file based on expiration time, max diskspace, and minfreespace.
+* Delete the file if we need to.
+*
+* Assumption: A WRITE lock has been acquired for the audit
+******************************************************************************/
+
+static int
+log__delete_audit_logfile()
+{
+ struct logfileinfo *logp = NULL;
+ struct logfileinfo *delete_logp = NULL;
+ struct logfileinfo *p_delete_logp = NULL;
+ struct logfileinfo *prev_logp = NULL;
+ int total_size=0;
+ time_t cur_time;
+ int f_size;
+ int numoflogs=loginfo.log_numof_audit_logs;
+ int rv = 0;
+ char *logstr;
+ char buffer[BUFSIZ];
+ char tbuf[TBUFSIZE];
+
+ /* If we have only one log, then will delete this one */
+ if (loginfo.log_audit_maxnumlogs == 1) {
+ LOG_CLOSE(loginfo.log_audit_fdes);
+ loginfo.log_audit_fdes = NULL;
+ sprintf (buffer, "%s", loginfo.log_audit_file);
+ if (PR_Delete(buffer) != PR_SUCCESS) {
+ LDAPDebug(LDAP_DEBUG_TRACE,
+ "LOGINFO:Unable to remove file:%s\n", loginfo.log_audit_file,0,0);
+ }
+
+ /* Delete the rotation file also. */
+ sprintf (buffer, "%s.rotationinfo", loginfo.log_audit_file);
+ if (PR_Delete(buffer) != PR_SUCCESS) {
+ LDAPDebug(LDAP_DEBUG_TRACE,
+ "LOGINFO:Unable to remove file:%s.rotationinfo\n", loginfo.log_audit_file,0,0);
+ }
+ return 0;
+ }
+
+ /* If we have already the maximum number of log files, we
+ ** have to delete one any how.
+ */
+ if (++numoflogs > loginfo.log_audit_maxnumlogs) {
+ logstr = "Exceeded max number of logs allowed";
+ goto delete_logfile;
+ }
+
+ /* Now check based on the maxdiskspace */
+ if (loginfo.log_audit_maxdiskspace > 0) {
+ logp = loginfo.log_audit_logchain;
+ while (logp) {
+ total_size += logp->l_size;
+ logp = logp->l_next;
+ }
+ if ((f_size = log__getfilesize(loginfo.log_audit_fdes)) == -1) {
+ /* then just assume the max size */
+ total_size += loginfo.log_audit_maxlogsize;
+ } else {
+ total_size += f_size;
+ }
+
+ /* If we have exceeded the max disk space or we have less than the
+ ** minimum, then we have to delete a file.
+ */
+ if (total_size >= loginfo.log_audit_maxdiskspace) {
+ logstr = "exceeded maximum log disk space";
+ goto delete_logfile;
+ }
+ }
+
+ /* Now check based on the free space */
+ if ( loginfo.log_audit_minfreespace > 0) {
+ rv = log__enough_freespace(loginfo.log_audit_file);
+ if ( rv == 0) {
+ /* Not enough free space */
+ logstr = "Not enough free disk space";
+ goto delete_logfile;
+ }
+ }
+
+ /* Now check based on the expiration time */
+ if ( loginfo.log_audit_exptime_secs > 0 ) {
+ /* is the file old enough */
+ time (&cur_time);
+ prev_logp = logp = loginfo.log_audit_logchain;
+ while (logp) {
+ if ((cur_time - logp->l_ctime) > loginfo.log_audit_exptime_secs) {
+ delete_logp = logp;
+ p_delete_logp = prev_logp;
+ logstr = "The file is older than the log expiration time";
+ goto delete_logfile;
+ }
+ prev_logp = logp;
+ logp = logp->l_next;
+ }
+ }
+
+ /* No log files to delete */
+ return 0;
+
+delete_logfile:
+ if (delete_logp == NULL) {
+ time_t oldest;
+
+ time(&oldest);
+
+ prev_logp = logp = loginfo.log_audit_logchain;
+ while (logp) {
+ if (logp->l_ctime <= oldest) {
+ oldest = logp->l_ctime;
+ delete_logp = logp;
+ p_delete_logp = prev_logp;
+ }
+ prev_logp = logp;
+ logp = logp->l_next;
+ }
+ /* We might face this case if we have only one log file and
+ ** trying to delete it because of deletion requirement.
+ */
+ if (!delete_logp) {
+ return 0;
+ }
+ }
+
+ if (p_delete_logp == delete_logp) {
+ /* then we are deleteing the first one */
+ loginfo.log_audit_logchain = delete_logp->l_next;
+ } else {
+ p_delete_logp->l_next = delete_logp->l_next;
+ }
+
+ /* Delete the audit file */
+ log_convert_time (delete_logp->l_ctime, tbuf, 1 /*short */);
+ sprintf (buffer, "%s.%s", loginfo.log_audit_file, tbuf );
+ if (PR_Delete(buffer) != PR_SUCCESS) {
+ LDAPDebug(LDAP_DEBUG_TRACE,
+ "LOGINFO:Unable to remove file:%s.%s\n",
+ loginfo.log_audit_file, tbuf,0);
+
+ } else {
+ LDAPDebug(LDAP_DEBUG_TRACE,
+ "LOGINFO:Removed file:%s.%s because of (%s)\n",
+ loginfo.log_audit_file, tbuf,
+ logstr);
+ }
+ slapi_ch_free((void**)&delete_logp);
+ loginfo.log_numof_audit_logs--;
+
+ return 1;
+}
+
+/******************************************************************************
+* log__error_rotationinfof
+*
+* Try to open the log file. If we have one already, then try to read the
+* header and update the information.
+*
+* Assumption: Lock has been acquired already
+******************************************************************************/
+static int
+log__error_rotationinfof( char *pathname)
+{
+ long f_ctime;
+ int f_size;
+ int main_log = 1;
+ time_t now;
+ FILE *fp;
+
+
+ /*
+ ** Okay -- I confess, we want to use NSPR calls but I want to
+ ** use fgets and not use PR_Read() and implement a complicated
+ ** parsing module. Since this will be called only during the startup
+ ** and never aftre that, we can live by it.
+ */
+
+ if ((fp = fopen (pathname, "r")) == NULL) {
+ return LOGFILE_NEW;
+ }
+
+ loginfo.log_numof_error_logs = 0;
+
+ /*
+ ** We have reopened the log error file. Now we need to read the
+ ** log file info and update the values.
+ */
+ while (log__extract_logheader(fp, &f_ctime, &f_size) == LOG_CONTINUE) {
+ /* first we would get the main log info */
+ if (f_ctime == 0 && f_size == 0)
+ continue;
+
+ time (&now);
+ if (main_log) {
+ if (f_ctime > 0L)
+ loginfo.log_error_ctime = f_ctime;
+ else {
+ loginfo.log_error_ctime = now;
+ }
+ main_log = 0;
+ } else {
+ struct logfileinfo *logp;
+
+ logp = (struct logfileinfo *) slapi_ch_malloc (sizeof (struct logfileinfo));
+ if (f_ctime > 0L)
+ logp->l_ctime = f_ctime;
+ else
+ logp->l_ctime = now;
+ if (f_size > 0)
+ logp->l_size = f_size;
+ else {
+ /* make it the max log size */
+ logp->l_size = loginfo.log_error_maxlogsize;
+ }
+
+ logp->l_next = loginfo.log_error_logchain;
+ loginfo.log_error_logchain = logp;
+ }
+ loginfo.log_numof_error_logs++;
+ }
+
+ /* Check if there is a rotation overdue */
+ if (loginfo.log_error_rotationsync_enabled &&
+ loginfo.log_error_rotationunit != LOG_UNIT_HOURS &&
+ loginfo.log_error_rotationunit != LOG_UNIT_MINS &&
+ loginfo.log_error_ctime < loginfo.log_error_rotationsyncclock - loginfo.log_error_rotationtime_secs) {
+ loginfo.log_error_rotationsyncclock -= loginfo.log_error_rotationtime_secs;
+ }
+
+ fclose (fp);
+ return LOGFILE_REOPENED;
+}
+
+/******************************************************************************
+* log__audit_rotationinfof
+*
+* Try to open the log file. If we have one already, then try to read the
+* header and update the information.
+*
+* Assumption: Lock has been acquired already
+******************************************************************************/
+static int
+log__audit_rotationinfof( char *pathname)
+{
+ long f_ctime;
+ int f_size;
+ int main_log = 1;
+ time_t now;
+ FILE *fp;
+
+
+ /*
+ ** Okay -- I confess, we want to use NSPR calls but I want to
+ ** use fgets and not use PR_Read() and implement a complicated
+ ** parsing module. Since this will be called only during the startup
+ ** and never aftre that, we can live by it.
+ */
+
+ if ((fp = fopen (pathname, "r")) == NULL) {
+ return LOGFILE_NEW;
+ }
+
+ loginfo.log_numof_audit_logs = 0;
+
+ /*
+ ** We have reopened the log audit file. Now we need to read the
+ ** log file info and update the values.
+ */
+ while (log__extract_logheader(fp, &f_ctime, &f_size) == LOG_CONTINUE) {
+ /* first we would get the main log info */
+ if (f_ctime == 0 && f_size == 0)
+ continue;
+
+ time (&now);
+ if (main_log) {
+ if (f_ctime > 0L)
+ loginfo.log_audit_ctime = f_ctime;
+ else {
+ loginfo.log_audit_ctime = now;
+ }
+ main_log = 0;
+ } else {
+ struct logfileinfo *logp;
+
+ logp = (struct logfileinfo *) slapi_ch_malloc (sizeof (struct logfileinfo));
+ if (f_ctime > 0L)
+ logp->l_ctime = f_ctime;
+ else
+ logp->l_ctime = now;
+ if (f_size > 0)
+ logp->l_size = f_size;
+ else {
+ /* make it the max log size */
+ logp->l_size = loginfo.log_audit_maxlogsize;
+ }
+
+ logp->l_next = loginfo.log_audit_logchain;
+ loginfo.log_audit_logchain = logp;
+ }
+ loginfo.log_numof_audit_logs++;
+ }
+
+ /* Check if there is a rotation overdue */
+ if (loginfo.log_audit_rotationsync_enabled &&
+ loginfo.log_audit_rotationunit != LOG_UNIT_HOURS &&
+ loginfo.log_audit_rotationunit != LOG_UNIT_MINS &&
+ loginfo.log_audit_ctime < loginfo.log_audit_rotationsyncclock - loginfo.log_audit_rotationtime_secs) {
+ loginfo.log_audit_rotationsyncclock -= loginfo.log_audit_rotationtime_secs;
+ }
+
+ fclose (fp);
+ return LOGFILE_REOPENED;
+}
+
+/******************************************************************************
+* log__open_errorlogfile
+*
+* Open a new log file. If we have run out of the max logs we can have
+* then delete the oldest file.
+******************************************************************************/
+static int
+log__open_errorlogfile(int logfile_state, int locked)
+{
+
+ time_t now;
+ LOGFD fp;
+ LOGFD fpinfo = NULL;
+ char tbuf[TBUFSIZE];
+ struct logfileinfo *logp;
+ char buffer[BUFSIZ];
+
+ if (!locked) LOG_ERROR_LOCK_WRITE( );
+
+ /*
+ ** Here we are trying to create a new log file.
+ ** If we alredy have one, then we need to rename it as
+ ** "filename.time", close it and update it's information
+ ** in the array stack.
+ */
+ if (loginfo.log_error_fdes != NULL) {
+ struct logfileinfo *log;
+ char newfile[BUFSIZ];
+ int f_size;
+
+ /* get rid of the old one */
+ if ((f_size = log__getfilesize(loginfo.log_error_fdes)) == -1) {
+ /* Then assume that we have the max size */
+ f_size = loginfo.log_error_maxlogsize;
+ }
+
+
+ /* Check if I have to delete any old file, delete it if it is required.*/
+ while (log__delete_error_logfile());
+
+ /* close the file */
+ if ( loginfo.log_error_fdes != NULL ) {
+ LOG_CLOSE(loginfo.log_error_fdes);
+ }
+ loginfo.log_error_fdes = NULL;
+
+ if ( loginfo.log_error_maxnumlogs > 1 ) {
+ log = (struct logfileinfo *) slapi_ch_malloc (sizeof (struct logfileinfo));
+ log->l_ctime = loginfo.log_error_ctime;
+ log->l_size = f_size;
+
+ log_convert_time (log->l_ctime, tbuf, 1/*short */);
+ sprintf(newfile, "%s.%s", loginfo.log_error_file, tbuf);
+ if (PR_Rename (loginfo.log_error_file, newfile) != PR_SUCCESS) {
+ return LOG_UNABLE_TO_OPENFILE;
+ }
+
+ /* add the log to the chain */
+ log->l_next = loginfo.log_error_logchain;
+ loginfo.log_error_logchain = log;
+ loginfo.log_numof_error_logs++;
+ }
+ }
+
+
+ /* open a new log file */
+ if (! LOG_OPEN_APPEND(fp, loginfo.log_error_file, loginfo.log_error_mode)) {
+ LDAPDebug(LDAP_DEBUG_ANY, "WARNING: can't open file %s. "
+ "errno %d (%s)\n",
+ loginfo.log_error_file, errno, slapd_system_strerror(errno));
+ if (!locked) LOG_ERROR_UNLOCK_WRITE();
+ /*if I have an old log file -- I should log a message
+ ** that I can't open the new file. Let the caller worry
+ ** about logging message.
+ */
+ return LOG_UNABLE_TO_OPENFILE;
+ }
+
+ loginfo.log_error_fdes = fp;
+ if (logfile_state == LOGFILE_REOPENED) {
+ /* we have all the information */
+ if (!locked) LOG_ERROR_UNLOCK_WRITE( );
+ return LOG_SUCCESS;
+ }
+
+ loginfo.log_error_state |= LOGGING_NEED_TITLE;
+
+ if (! LOG_OPEN_WRITE(fpinfo, loginfo.log_errorinfo_file, loginfo.log_error_mode)) {
+ LDAPDebug(LDAP_DEBUG_ANY, "WARNING: can't open file %s. "
+ "errno %d (%s)\n",
+ loginfo.log_errorinfo_file, errno, slapd_system_strerror(errno));
+ if (!locked) LOG_ERROR_UNLOCK_WRITE();
+ return LOG_UNABLE_TO_OPENFILE;
+ }
+
+ /* write the header in the log */
+ now = current_time();
+ log_convert_time (now, tbuf, 2 /*long */);
+ sprintf (buffer,"LOGINFO:Log file created at: %s (%lu)\n", tbuf, now);
+ LOG_WRITE(fpinfo, buffer, strlen(buffer), 0);
+
+ logp = loginfo.log_error_logchain;
+ while ( logp) {
+ log_convert_time (logp->l_ctime, tbuf, 1 /*short */);
+ sprintf(buffer, "LOGINFO:Previous Log File:%s.%s (%lu) (%u)\n",
+ loginfo.log_error_file, tbuf, logp->l_ctime, logp->l_size);
+ LOG_WRITE(fpinfo, buffer, strlen(buffer), 0);
+ logp = logp->l_next;
+ }
+ /* Close the info file. We need only when we need to rotate to the
+ ** next log file.
+ */
+ if (fpinfo) LOG_CLOSE(fpinfo);
+
+ /* This is now the current error log */
+ loginfo.log_error_ctime = now;
+
+ if (!locked) LOG_ERROR_UNLOCK_WRITE( );
+ return LOG_SUCCESS;
+}
+
+/******************************************************************************
+* log__open_auditlogfile
+*
+* Open a new log file. If we have run out of the max logs we can have
+* then delete the oldest file.
+******************************************************************************/
+static int
+log__open_auditlogfile(int logfile_state, int locked)
+{
+
+ time_t now;
+ LOGFD fp;
+ LOGFD fpinfo = NULL;
+ char tbuf[TBUFSIZE];
+ struct logfileinfo *logp;
+ char buffer[BUFSIZ];
+
+ if (!locked) LOG_AUDIT_LOCK_WRITE( );
+
+ /*
+ ** Here we are trying to create a new log file.
+ ** If we alredy have one, then we need to rename it as
+ ** "filename.time", close it and update it's information
+ ** in the array stack.
+ */
+ if (loginfo.log_audit_fdes != NULL) {
+ struct logfileinfo *log;
+ char newfile[BUFSIZ];
+ int f_size;
+
+
+ /* get rid of the old one */
+ if ((f_size = log__getfilesize(loginfo.log_audit_fdes)) == -1) {
+ /* Then assume that we have the max size */
+ f_size = loginfo.log_audit_maxlogsize;
+ }
+
+
+ /* Check if I have to delete any old file, delete it if it is required. */
+ while (log__delete_audit_logfile());
+
+ /* close the file */
+ LOG_CLOSE(loginfo.log_audit_fdes);
+ loginfo.log_audit_fdes = NULL;
+
+ if ( loginfo.log_audit_maxnumlogs > 1 ) {
+ log = (struct logfileinfo *) slapi_ch_malloc (sizeof (struct logfileinfo));
+ log->l_ctime = loginfo.log_audit_ctime;
+ log->l_size = f_size;
+
+ log_convert_time (log->l_ctime, tbuf, 1 /*short */);
+ sprintf(newfile, "%s.%s", loginfo.log_audit_file, tbuf);
+ if (PR_Rename (loginfo.log_audit_file, newfile) != PR_SUCCESS) {
+ if (!locked) LOG_AUDIT_UNLOCK_WRITE();
+ return LOG_UNABLE_TO_OPENFILE;
+ }
+
+ /* add the log to the chain */
+ log->l_next = loginfo.log_audit_logchain;
+ loginfo.log_audit_logchain = log;
+ loginfo.log_numof_audit_logs++;
+ }
+ }
+
+
+ /* open a new log file */
+ if (! LOG_OPEN_APPEND(fp, loginfo.log_audit_file, loginfo.log_audit_mode)) {
+ LDAPDebug(LDAP_DEBUG_ANY, "WARNING: can't open file %s. "
+ "errno %d (%s)\n",
+ loginfo.log_audit_file, errno, slapd_system_strerror(errno));
+ if (!locked) LOG_AUDIT_UNLOCK_WRITE();
+ /*if I have an old log file -- I should log a message
+ ** that I can't open the new file. Let the caller worry
+ ** about logging message.
+ */
+ return LOG_UNABLE_TO_OPENFILE;
+ }
+
+ loginfo.log_audit_fdes = fp;
+ if (logfile_state == LOGFILE_REOPENED) {
+ /* we have all the information */
+ if (!locked) LOG_AUDIT_UNLOCK_WRITE();
+ return LOG_SUCCESS;
+ }
+
+ loginfo.log_audit_state |= LOGGING_NEED_TITLE;
+
+ if (! LOG_OPEN_WRITE(fpinfo, loginfo.log_auditinfo_file, loginfo.log_audit_mode)) {
+ LDAPDebug(LDAP_DEBUG_ANY, "WARNING: can't open file %s. "
+ "errno %d (%s)\n",
+ loginfo.log_auditinfo_file, errno, slapd_system_strerror(errno));
+ if (!locked) LOG_AUDIT_UNLOCK_WRITE();
+ return LOG_UNABLE_TO_OPENFILE;
+ }
+
+ /* write the header in the log */
+ now = current_time();
+ log_convert_time (now, tbuf, 2 /*long */);
+ sprintf (buffer,"LOGINFO:Log file created at: %s (%lu)\n", tbuf, now);
+ LOG_WRITE(fpinfo, buffer, strlen(buffer), 0);
+
+ logp = loginfo.log_audit_logchain;
+ while ( logp) {
+ log_convert_time (logp->l_ctime, tbuf, 1 /*short */);
+ sprintf(buffer, "LOGINFO:Previous Log File:%s.%s (%d) (%d)\n",
+ loginfo.log_audit_file, tbuf, (int)logp->l_ctime, logp->l_size);
+ LOG_WRITE(fpinfo, buffer, strlen(buffer), 0);
+ logp = logp->l_next;
+ }
+ /* Close the info file. We need only when we need to rotate to the
+ ** next log file.
+ */
+ if (fpinfo) LOG_CLOSE(fpinfo);
+
+ /* This is now the current audit log */
+ loginfo.log_audit_ctime = now;
+
+ if (!locked) LOG_AUDIT_UNLOCK_WRITE( );
+ return LOG_SUCCESS;
+}
+
+/*
+** Log Buffering
+** only supports access log at this time
+*/
+
+static LogBufferInfo *log_create_buffer(size_t sz)
+{
+ LogBufferInfo *lbi;
+
+ lbi = (LogBufferInfo *) slapi_ch_malloc(sizeof(LogBufferInfo));
+ lbi->top = (char *) slapi_ch_malloc(sz);
+ lbi->current = lbi->top;
+ lbi->maxsize = sz;
+ lbi->refcount = 0;
+ return lbi;
+}
+
+#if 0
+/* for some reason, we never call this. */
+static void log_destroy_buffer(LogBufferInfo *lbi)
+{
+ slapi_ch_free((void *)&(lbi->top));
+ slapi_ch_free((void *)&lbi);
+}
+#endif
+
+/*
+ Some notes about this function. It is written the
+ way it is for performance reasons.
+ Tests showed that on 4 processor systems, there is
+ significant contention for the
+ lbi->lock. This is because the lock was held for
+ the duration of the copy of the
+ log message into the buffer. Therefore the routine
+ was re-written to avoid holding
+ the lock for that time. Instead we gain the lock,
+ take a copy of the buffer pointer
+ where we need to copy our message, increase the
+ size, move the current pointer beyond
+ our portion of the buffer, then increment a reference
+ count.
+ Then we release the lock and do the actual copy
+ in to the reserved buffer area.
+ We then atomically decrement the reference count.
+ The reference count is used to ensure that when
+ the buffer is flushed to the
+ filesystem, there are no threads left copying
+ data into the buffer.
+ The wait on zero reference count is implemented
+ in the flush routine because
+ it is also called from log_access_flush().
+ Tests show this speeds up searches by 10% on 4-way systems.
+ */
+
+static void log_append_buffer2(time_t tnl, LogBufferInfo *lbi, char *msg1, size_t size1, char *msg2, size_t size2)
+{
+ slapdFrontendConfig_t *slapdFrontendConfig = getFrontendConfig();
+ size_t size = size1 + size2;
+ char* insert_point = NULL;
+
+ /* While holding the lock, we determine if there is space in the buffer for our payload,
+ and if we need to flush.
+ */
+ PR_Lock(lbi->lock);
+ if ( ((lbi->current - lbi->top) + size > lbi->maxsize) ||
+ (tnl >= loginfo.log_access_rotationsyncclock &&
+ loginfo.log_access_rotationsync_enabled) ) {
+
+ log_flush_buffer(lbi, SLAPD_ACCESS_LOG,
+ 0 /* do not sync to disk right now */ );
+
+ }
+ insert_point = lbi->current;
+ lbi->current += size;
+ /* Increment the copy refcount */
+ PR_AtomicIncrement(&(lbi->refcount));
+ PR_Unlock(lbi->lock);
+
+ /* Now we can copy without holding the lock */
+ memcpy(insert_point, msg1, size1);
+ memcpy(insert_point + size1, msg2, size2);
+
+ /* Decrement the copy refcount */
+ PR_AtomicDecrement(&(lbi->refcount));
+
+ /* If we are asked to sync to disk immediately, do so */
+ if (!slapdFrontendConfig->accesslogbuffering) {
+ PR_Lock(lbi->lock);
+ log_flush_buffer(lbi, SLAPD_ACCESS_LOG, 1 /* sync to disk now */ );
+ PR_Unlock(lbi->lock);
+ }
+
+}
+
+/* this function assumes the lock is already acquired */
+/* if sync_now is non-zero, data is flushed to physical storage */
+static void log_flush_buffer(LogBufferInfo *lbi, int type, int sync_now)
+{
+ slapdFrontendConfig_t *slapdFrontendConfig = getFrontendConfig();
+
+ if (type == SLAPD_ACCESS_LOG) {
+
+ /* It is only safe to flush once any other threads which are copying are finished */
+ while (lbi->refcount > 0) {
+ /* It's ok to sleep for a while because we only flush every second or so */
+ DS_Sleep (PR_MillisecondsToInterval(1));
+ }
+
+ if ((lbi->current - lbi->top) == 0) return;
+
+ if (log__needrotation(loginfo.log_access_fdes,
+ SLAPD_ACCESS_LOG) == LOG_ROTATE) {
+ if (log__open_accesslogfile(LOGFILE_NEW, 1) != LOG_SUCCESS) {
+ LDAPDebug(LDAP_DEBUG_ANY,
+ "LOGINFO: Unable to open access file:%s\n",
+ loginfo.log_access_file,0,0);
+ lbi->current = lbi->top; /* reset counter to prevent overwriting rest of lbi struct */
+ return;
+ }
+ while (loginfo.log_access_rotationsyncclock <= loginfo.log_access_ctime) {
+ loginfo.log_access_rotationsyncclock += loginfo.log_access_rotationtime_secs;
+ }
+ }
+
+ if (loginfo.log_access_state & LOGGING_NEED_TITLE) {
+ log_write_title(loginfo.log_access_fdes);
+ loginfo.log_access_state &= ~LOGGING_NEED_TITLE;
+ }
+ if (!sync_now && slapdFrontendConfig->accesslogbuffering) {
+ LOG_WRITE(loginfo.log_access_fdes, lbi->top, lbi->current - lbi->top, 0);
+ } else {
+ LOG_WRITE_NOW(loginfo.log_access_fdes, lbi->top, lbi->current - lbi->top, 0);
+ }
+
+ lbi->current = lbi->top;
+ }
+}
+
+void log_access_flush()
+{
+ LOG_ACCESS_LOCK_WRITE();
+ log_flush_buffer(loginfo.log_access_buffer, SLAPD_ACCESS_LOG,
+ 1 /* sync to disk now */ );
+ LOG_ACCESS_UNLOCK_WRITE();
+}
+
+/*
+ *
+ * log_convert_time
+ * returns the time converted into the string format.
+ *
+ */
+static void
+log_convert_time (time_t ctime, char *tbuf, int type)
+{
+
+ struct tm *tmsp, tms;
+
+#ifdef _WIN32
+ {
+ struct tm *pt = localtime( &ctime );
+ tmsp = &tms;
+ memcpy(&tms, pt, sizeof(struct tm) );
+ }
+#else
+ (void)localtime_r( &ctime, &tms );
+ tmsp = &tms;
+#endif
+ if (type == 1) /* get the short form */
+ (void) strftime (tbuf, (size_t) TBUFSIZE, "%Y%m%d-%H%M%S",tmsp);
+ else /* wants the long form */
+ (void) strftime (tbuf, (size_t) TBUFSIZE, "%d/%b/%Y:%H:%M:%S",tmsp);
+
+}
+
+int
+check_log_max_size( char *maxdiskspace_str,
+ char *mlogsize_str,
+ int maxdiskspace,
+ int mlogsize,
+ char * returntext,
+ int logtype)
+{
+ slapdFrontendConfig_t *slapdFrontendConfig = getFrontendConfig();
+ int rc = LDAP_SUCCESS;
+ int current_mlogsize = -1;
+ int current_maxdiskspace = -1;
+
+ switch (logtype)
+ {
+ case SLAPD_ACCESS_LOG:
+ current_mlogsize = slapdFrontendConfig->accesslog_maxlogsize;
+ current_maxdiskspace = slapdFrontendConfig->accesslog_maxdiskspace;
+ break;
+ case SLAPD_ERROR_LOG:
+ current_mlogsize = slapdFrontendConfig->errorlog_maxlogsize;
+ current_maxdiskspace = slapdFrontendConfig->errorlog_maxdiskspace;
+ break;
+ case SLAPD_AUDIT_LOG:
+ current_mlogsize = slapdFrontendConfig->auditlog_maxlogsize;
+ current_maxdiskspace = slapdFrontendConfig->auditlog_maxdiskspace;
+ break;
+ default:
+ current_mlogsize = -1;
+ current_maxdiskspace = -1;
+ }
+
+ if ( maxdiskspace == -1 )
+ maxdiskspace = current_maxdiskspace;
+ if ( mlogsize == -1 )
+ mlogsize = current_mlogsize;
+
+ if ( maxdiskspace < mlogsize )
+ {
+ /* fail */
+ PR_snprintf ( returntext, SLAPI_DSE_RETURNTEXT_SIZE,
+ "%s: maxdiskspace \"%d\" is less than max log size \"%d\"",
+ maxdiskspace_str, maxdiskspace*LOG_MB_IN_BYTES, mlogsize*LOG_MB_IN_BYTES );
+ rc = LDAP_OPERATIONS_ERROR;
+ }
+ switch (logtype)
+ {
+ case SLAPD_ACCESS_LOG:
+ loginfo.log_access_maxlogsize = mlogsize * LOG_MB_IN_BYTES;
+ loginfo.log_access_maxdiskspace = maxdiskspace * LOG_MB_IN_BYTES;
+ break;
+ case SLAPD_ERROR_LOG:
+ loginfo.log_error_maxlogsize = mlogsize * LOG_MB_IN_BYTES;
+ loginfo.log_error_maxdiskspace = maxdiskspace * LOG_MB_IN_BYTES;
+ break;
+ case SLAPD_AUDIT_LOG:
+ loginfo.log_audit_maxlogsize = mlogsize * LOG_MB_IN_BYTES;
+ loginfo.log_audit_maxdiskspace = maxdiskspace * LOG_MB_IN_BYTES;
+ break;
+ default:
+ break;
+ }
+
+ return rc;
+}
+
+/************************************************************************************/
+/* E N D */
+/************************************************************************************/
+
diff --git a/ldap/servers/slapd/log.h b/ldap/servers/slapd/log.h
new file mode 100644
index 00000000..b92b58f7
--- /dev/null
+++ b/ldap/servers/slapd/log.h
@@ -0,0 +1,186 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+
+/***********************************************************************
+ * log.h
+ *
+ * structures related to logging facility.
+ *
+ *************************************************************************/
+#include <stdio.h>
+#include <time.h>
+#include <stdarg.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#ifndef _WIN32
+#include <errno.h>
+#ifdef LINUX
+#include <sys/statfs.h>
+#else
+#include <sys/statvfs.h>
+#endif
+#endif
+#include <fcntl.h>
+#include "prio.h"
+#include "prprf.h"
+#include "slap.h"
+#include "slapi-plugin.h"
+
+#define LOG_MB_IN_BYTES (1024 * 1024)
+
+#define LOG_SUCCESS 0 /* fine & dandy */
+#define LOG_CONTINUE LOG_SUCCESS
+#define LOG_ERROR 1 /* default error case */
+#define LOG_EXCEEDED 2 /*err: > max logs allowed */
+#define LOG_ROTATE 3 /*ok; go to the next log */
+#define LOG_UNABLE_TO_OPENFILE 4
+
+#define LOG_UNIT_UNKNOWN 0
+#define LOG_UNIT_MONTHS 1
+#define LOG_UNIT_WEEKS 2
+#define LOG_UNIT_DAYS 3
+#define LOG_UNIT_HOURS 4
+#define LOG_UNIT_MINS 5
+
+
+#define LOGFILE_NEW 0
+#define LOGFILE_REOPENED 1
+
+
+#define LOG_UNIT_TYPE_UNKNOWN "unknown"
+#define LOG_UNIT_TYPE_MONTHS "month"
+#define LOG_UNIT_TYPE_WEEKS "week"
+#define LOG_UNIT_TYPE_DAYS "day"
+#define LOG_UNIT_TYPE_HOURS "hour"
+#define LOG_UNIT_TYPE_MINUTES "minute"
+
+#define LOG_BUFFER_MAXSIZE 512 * 1024
+
+/* see log.c for why this is done */
+#ifdef XP_WIN32
+typedef FILE *LOGFD;
+#else
+typedef PRFileDesc *LOGFD;
+#endif
+
+
+struct logfileinfo {
+ int l_size; /* size is in KB */
+ time_t l_ctime; /* log creation time*/
+ struct logfileinfo *l_next; /* next log */
+};
+typedef struct logfileinfo LogFileInfo;
+
+struct logbufinfo {
+ char *top; /* beginning of the buffer */
+ char *current; /* current pointer into buffer */
+ size_t maxsize; /* size of buffer */
+ PRLock *lock; /* lock for access logging */
+ PRInt32 refcount; /* Reference count for buffer copies */
+};
+typedef struct logbufinfo LogBufferInfo;
+
+struct logging_opts {
+ /* These are access log specific */
+ int log_access_state;
+ int log_access_mode; /* access mode */
+ int log_access_maxnumlogs; /* Number of logs */
+ int log_access_maxlogsize; /* max log size in bytes*/
+ int log_access_rotationtime; /* time in units. */
+ int log_access_rotationunit; /* time in units. */
+ int log_access_rotationtime_secs; /* time in seconds */
+ int log_access_rotationsync_enabled;/* 0 or 1*/
+ int log_access_rotationsynchour; /* 0-23 */
+ int log_access_rotationsyncmin; /* 0-59 */
+ time_t log_access_rotationsyncclock; /* clock in seconds */
+ int log_access_maxdiskspace; /* space in bytes */
+ int log_access_minfreespace; /* free space in bytes */
+ int log_access_exptime; /* time */
+ int log_access_exptimeunit; /* unit time */
+ int log_access_exptime_secs; /* time in secs */
+
+ int log_access_level; /* access log level */
+ char *log_access_file; /* access log file path */
+ LOGFD log_access_fdes; /* fp for the cur access log */
+ unsigned int log_numof_access_logs; /* number of logs */
+ time_t log_access_ctime; /* log creation time */
+ LogFileInfo *log_access_logchain; /* all the logs info */
+ char *log_accessinfo_file; /* access log rotation info file */
+ LogBufferInfo *log_access_buffer; /* buffer for access log */
+
+ /* These are error log specific */
+ int log_error_state;
+ int log_error_mode; /* access mode */
+ int log_error_maxnumlogs; /* Number of logs */
+ int log_error_maxlogsize; /* max log size in bytes*/
+ int log_error_rotationtime; /* time in units. */
+ int log_error_rotationunit; /* time in units. */
+ int log_error_rotationtime_secs; /* time in seconds */
+ int log_error_rotationsync_enabled;/* 0 or 1*/
+ int log_error_rotationsynchour; /* 0-23 */
+ int log_error_rotationsyncmin; /* 0-59 */
+ time_t log_error_rotationsyncclock; /* clock in seconds */
+ int log_error_maxdiskspace; /* space in bytes */
+ int log_error_minfreespace; /* free space in bytes */
+ int log_error_exptime; /* time */
+ int log_error_exptimeunit; /* unit time */
+ int log_error_exptime_secs; /* time in secs */
+
+ char *log_error_file; /* error log file path */
+ LOGFD log_error_fdes; /* fp for the cur error log */
+ unsigned int log_numof_error_logs; /* number of logs */
+ time_t log_error_ctime; /* log creation time */
+ LogFileInfo *log_error_logchain; /* all the logs info */
+ char *log_errorinfo_file; /* error log rotation info file */
+ rwl *log_error_rwlock; /* lock on error*/
+
+ /* These are audit log specific */
+ int log_audit_state;
+ int log_audit_mode; /* access mode */
+ int log_audit_maxnumlogs; /* Number of logs */
+ int log_audit_maxlogsize; /* max log size in bytes*/
+ int log_audit_rotationtime; /* time in units. */
+ int log_audit_rotationunit; /* time in units. */
+ int log_audit_rotationtime_secs; /* time in seconds */
+ int log_audit_rotationsync_enabled;/* 0 or 1*/
+ int log_audit_rotationsynchour; /* 0-23 */
+ int log_audit_rotationsyncmin; /* 0-59 */
+ time_t log_audit_rotationsyncclock; /* clock in seconds */
+ int log_audit_maxdiskspace; /* space in bytes */
+ int log_audit_minfreespace; /* free space in bytes */
+ int log_audit_exptime; /* time */
+ int log_audit_exptimeunit; /* unit time */
+ int log_audit_exptime_secs; /* time in secs */
+
+ char *log_audit_file; /* aufit log name */
+ LOGFD log_audit_fdes; /* audit log fdes */
+ unsigned int log_numof_audit_logs; /* number of logs */
+ time_t log_audit_ctime; /* log creation time */
+ LogFileInfo *log_audit_logchain; /* all the logs info */
+ char *log_auditinfo_file; /* audit log rotation info file */
+ rwl *log_audit_rwlock; /* lock on audit*/
+
+};
+
+/* For log_state */
+#define LOGGING_ENABLED (int) 0x1 /* logging is enabled */
+#define LOGGING_NEED_TITLE 0x2 /* need to write title */
+
+#define LOG_ACCESS_LOCK_READ() PR_Lock(loginfo.log_access_buffer->lock)
+#define LOG_ACCESS_UNLOCK_READ() PR_Unlock(loginfo.log_access_buffer->lock)
+#define LOG_ACCESS_LOCK_WRITE() PR_Lock(loginfo.log_access_buffer->lock)
+#define LOG_ACCESS_UNLOCK_WRITE() PR_Unlock(loginfo.log_access_buffer->lock)
+
+#define LOG_ERROR_LOCK_READ() loginfo.log_error_rwlock->rwl_acquire_read_lock(loginfo.log_error_rwlock)
+#define LOG_ERROR_UNLOCK_READ() loginfo.log_error_rwlock->rwl_relinquish_read_lock(loginfo.log_error_rwlock)
+#define LOG_ERROR_LOCK_WRITE() loginfo.log_error_rwlock->rwl_acquire_write_lock(loginfo.log_error_rwlock)
+#define LOG_ERROR_UNLOCK_WRITE() loginfo.log_error_rwlock->rwl_relinquish_write_lock(loginfo.log_error_rwlock)
+
+#define LOG_AUDIT_LOCK_READ() loginfo.log_audit_rwlock->rwl_acquire_read_lock(loginfo.log_audit_rwlock)
+#define LOG_AUDIT_UNLOCK_READ() loginfo.log_audit_rwlock->rwl_relinquish_read_lock(loginfo.log_audit_rwlock)
+#define LOG_AUDIT_LOCK_WRITE() loginfo.log_audit_rwlock->rwl_acquire_write_lock(loginfo.log_audit_rwlock)
+#define LOG_AUDIT_UNLOCK_WRITE() loginfo.log_audit_rwlock->rwl_relinquish_write_lock(loginfo.log_audit_rwlock)
+
diff --git a/ldap/servers/slapd/main.c b/ldap/servers/slapd/main.c
new file mode 100644
index 00000000..b52df26d
--- /dev/null
+++ b/ldap/servers/slapd/main.c
@@ -0,0 +1,2753 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+
+#if defined(NET_SSL)
+#include <ldap.h>
+#undef OFF
+#undef LITTLE_ENDIAN
+#endif
+
+#include <stdio.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#if !defined(_WIN32) && !defined(aix)
+#include <sys/fcntl.h>
+#else
+#include <fcntl.h>
+#endif
+#include <time.h>
+#include <stdarg.h>
+#include <signal.h>
+#include <stdlib.h>
+#if defined( _WIN32 )
+#include "ntslapdmessages.h"
+#include "proto-ntutil.h"
+#else
+#include <sys/time.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+#include <pwd.h> /* getpwnam */
+#if !defined(IRIX) && !defined(LINUX)
+union semun {
+ int val;
+ struct semid_ds *buf;
+ ushort *array;
+};
+#endif
+#endif
+#if !defined(_WIN32)
+#include <unistd.h> /* dup2 */
+#include <sys/ipc.h>
+#include <sys/sem.h>
+#include <sys/param.h> /* MAXPATHLEN */
+#endif
+#if defined(__sun)
+#include <sys/utsname.h>
+#include <sys/systeminfo.h>
+#endif
+#include "slap.h"
+#include "slapi-plugin.h"
+#include "prinit.h"
+#include "snmp_collator.h"
+#include "fe.h" /* client_auth_init() */
+#include "protect_db.h"
+#include "getopt_ext.h"
+#include "fe.h"
+
+#ifndef LDAP_DONT_USE_SMARTHEAP
+#include "smrtheap.h"
+#endif
+
+#if defined( XP_WIN32 )
+void dostounixpath(char *szText);
+#endif
+
+/* Forward Declarations */
+static void register_objects();
+static void process_command_line(int argc, char **argv, char *myname, char **extraname);
+static int slapd_exemode_ldif2db();
+static int slapd_exemode_db2ldif(int argc, char **argv);
+static int slapd_exemode_db2index();
+static int slapd_exemode_archive2db();
+static int slapd_exemode_db2archive();
+#if defined(UPGRADEDB)
+static int slapd_exemode_upgradedb();
+#endif
+static int slapd_exemode_dbtest();
+static int slapd_exemode_suffix2instance();
+static int slapd_debug_level_string2level( const char *s );
+static void slapd_debug_level_log( int level );
+static void slapd_debug_level_usage( void );
+static void cmd_set_shutdown(int);
+/*
+ * global variables
+ */
+
+static int slapd_exemode = SLAPD_EXEMODE_UNKNOWN;
+
+static int init_cmd_shutdown_detect()
+{
+
+#ifndef _WIN32
+ /* First of all, we must reset the signal mask to get rid of any blockages
+ * the process may have inherited from its parent (such as the console), which
+ * might result in the process not delivering those blocked signals, and thus,
+ * misbehaving....
+ */
+ {
+ int rc;
+ sigset_t proc_mask;
+
+ LDAPDebug( LDAP_DEBUG_TRACE, "Reseting signal mask....\n", 0, 0, 0);
+ (void)sigemptyset( &proc_mask );
+ rc = pthread_sigmask( SIG_SETMASK, &proc_mask, NULL );
+ LDAPDebug( LDAP_DEBUG_TRACE, " %s \n",
+ rc ? "Failed to reset signal mask":"....Done (signal mask reset)!!", 0, 0 );
+ }
+#endif
+
+#if defined ( HPUX10 )
+ PR_CreateThread ( PR_USER_THREAD,
+ catch_signals,
+ NULL,
+ PR_PRIORITY_NORMAL,
+ PR_GLOBAL_THREAD,
+ PR_UNJOINABLE_THREAD,
+ SLAPD_DEFAULT_THREAD_STACKSIZE);
+#elif defined ( HPUX11 )
+ /* In the optimized builds for HPUX, the signal handler doesn't seem
+ * to get set correctly unless the primordial thread gets a chance
+ * to run before we make the call to SIGNAL. (At this point the
+ * the primordial thread has spawned the daemon thread which called
+ * this function.) The call to DS_Sleep will give the primordial
+ * thread a chance to run. */
+ DS_Sleep(0);
+#endif
+#ifndef _WIN32
+ (void) SIGNAL( SIGPIPE, SIG_IGN );
+ (void) SIGNAL( SIGCHLD, slapd_wait4child );
+#ifndef LINUX
+ /* linux uses USR1/USR2 for thread synchronization, so we aren't
+ * allowed to mess with those.
+ */
+ (void) SIGNAL( SIGUSR1, slapd_do_nothing );
+ (void) SIGNAL( SIGUSR2, cmd_set_shutdown );
+#endif
+ (void) SIGNAL( SIGTERM, cmd_set_shutdown );
+ (void) SIGNAL( SIGHUP, cmd_set_shutdown );
+ (void) SIGNAL( SIGINT, cmd_set_shutdown );
+#endif /* _WIN32 */
+ return 0;
+}
+
+static void
+cmd_set_shutdown (int sig)
+{
+ /* don't log anything from a signal handler:
+ * you could be holding a lock when the signal was trapped. more
+ * specifically, you could be holding the logfile lock (and deadlock
+ * yourself).
+ */
+
+#if 0
+ LDAPDebug( LDAP_DEBUG_ANY, "slapd got shutdown signal\n", 0, 0, 0 );
+#endif
+ c_set_shutdown();
+#ifndef _WIN32
+#ifndef LINUX
+ /* don't mess with USR1/USR2 on linux, used by libpthread */
+ (void) SIGNAL( SIGUSR2, cmd_set_shutdown );
+#endif
+ (void) SIGNAL( SIGTERM, cmd_set_shutdown );
+ (void) SIGNAL( SIGHUP, cmd_set_shutdown );
+#endif
+}
+
+#ifdef HPUX10
+extern void collation_init();
+#endif
+
+#ifndef WIN32
+
+/* Changes the ownership of the given file/directory iff not
+ already the owner
+ Returns 0 upon success or non-zero otherwise, usually -1 if
+ some system error occurred
+*/
+static int
+chown_if_not_owner(const char *filename, uid_t uid, gid_t gid)
+{
+ struct stat statbuf;
+ int result = 1;
+ if (!filename)
+ return result;
+
+ memset(&statbuf, '\0', sizeof(statbuf));
+ if (!(result = stat(filename, &statbuf)))
+ {
+ if (((uid != -1) && (uid != statbuf.st_uid)) ||
+ ((gid != -1) && (gid != statbuf.st_gid)))
+ {
+ result = chown(filename, uid, gid);
+ }
+ }
+
+ return result;
+}
+
+/*
+ Four cases:
+ - change ownership of all files in directory (strip_fn=PR_FALSE)
+ - change ownership of all files in directory; but trailing fn needs to be stripped (strip_fn=PR_TRUE)
+ - fn is relative to root directory (/access); we print error message and let user shoot his foot
+ - fn is relative to current directory (access); we print error message and let user shoot his other foot
+
+ The docs say any valid filename.
+*/
+
+static void
+chown_dir_files(char *name, struct passwd *pw, PRBool strip_fn)
+{
+ PRDir *dir;
+ PRDirEntry *entry;
+ char dirname[MAXPATHLEN + 1], file[MAXPATHLEN + 1];
+ char *log=NULL, *ptr=NULL;
+ int rc=0;
+
+ log=strdup(name);
+ if(strip_fn)
+ {
+ if((ptr=strrchr(log,'/'))==NULL)
+ {
+ LDAPDebug(LDAP_DEBUG_ANY, "Caution changing ownership of ./%s \n",name,0,0);
+ chown_if_not_owner(log, pw->pw_uid, -1 );
+ rc=1;
+ } else if(log==ptr) {
+ LDAPDebug(LDAP_DEBUG_ANY, "Caution changing ownership of / directory and its contents to %s\n",pw->pw_name,0,0);
+ *(++ptr)='\0';
+ } else {
+ *ptr='\0';
+ }
+ }
+ if ((!rc) && ((dir = PR_OpenDir(log)) != NULL ))
+ {
+ /* change the owner for each of the files in the dir */
+ while( (entry = PR_ReadDir(dir , PR_SKIP_BOTH )) !=NULL )
+ {
+ sprintf(file,"%s/%s",log,entry->name);
+ chown_if_not_owner( file, pw->pw_uid, -1 );
+ }
+ PR_CloseDir( dir );
+ }
+ free(log);
+}
+
+/* Changes the owner of the files in the logs and
+ * config directorys to the user that the server runs as.
+*/
+
+static void
+fix_ownership()
+{
+ int len, n;
+ struct passwd* pw=NULL;
+ char dirname[MAXPATHLEN + 1];
+
+ slapdFrontendConfig_t *slapdFrontendConfig = getFrontendConfig();
+
+
+ if ( slapdFrontendConfig->localuser != NULL ) {
+ if ( (pw = getpwnam( slapdFrontendConfig->localuser )) == NULL )
+ return;
+ }
+ else {
+ return;
+ }
+
+ /* The instance directory needs to be owned by the local user */
+ chown_if_not_owner( slapdFrontendConfig->instancedir, pw->pw_uid, -1 );
+ sprintf(dirname,"%s/config",slapdFrontendConfig->instancedir);
+ chown_dir_files(dirname, pw, PR_FALSE); /* config directory */
+ chown_dir_files(slapdFrontendConfig->accesslog, pw, PR_TRUE); /* do access log directory */
+ chown_dir_files(slapdFrontendConfig->auditlog, pw, PR_TRUE); /* do audit log directory */
+ chown_dir_files(slapdFrontendConfig->errorlog, pw, PR_TRUE); /* do error log directory */
+
+}
+#endif
+
+/* Changes identity to the named user
+ * If username == NULL, does nothing.
+ * Does nothing on NT regardless.
+ */
+static int main_setuid(char *username)
+{
+#ifndef _WIN32
+ if (username != NULL) {
+ struct passwd *pw;
+ /* Make sure everything in the log and config directory
+ * is owned by the correct user */
+ fix_ownership();
+ pw = getpwnam (username);
+ if (pw == NULL) {
+ int oserr = errno;
+
+ LDAPDebug (LDAP_DEBUG_ANY, "getpwnam(%s) == NULL, error %d (%s)\n",
+ username, oserr, slapd_system_strerror(oserr));
+ } else {
+ if (setgid (pw->pw_gid) != 0) {
+ int oserr = errno;
+
+ LDAPDebug (LDAP_DEBUG_ANY, "setgid(%li) != 0, error %d (%s)\n",
+ (long)pw->pw_gid, oserr, slapd_system_strerror(oserr));
+ return -1;
+ }
+ if (setuid (pw->pw_uid) != 0) {
+ int oserr = errno;
+
+ LDAPDebug (LDAP_DEBUG_ANY, "setuid(%li) != 0, error %d (%s)\n",
+ (long)pw->pw_uid, oserr, slapd_system_strerror(oserr));
+ return -1;
+ }
+ }
+ }
+#endif
+ return 0;
+}
+
+/* set good defaults for front-end config in referral mode */
+static void referral_set_defaults(void)
+{
+#if !defined(_WIN32) && !defined(AIX)
+ char errorbuf[SLAPI_DSE_RETURNTEXT_SIZE];
+ config_set_maxdescriptors( CONFIG_MAXDESCRIPTORS_ATTRIBUTE, "1024", errorbuf, 1);
+#endif
+}
+
+static int
+name2exemode( char *progname, char *s, int exit_if_unknown )
+{
+ int exemode;
+
+ if ( strcmp( s, "db2ldif" ) == 0 ) {
+ exemode = SLAPD_EXEMODE_DB2LDIF;
+ } else if ( strcmp( s, "ldif2db" ) == 0 ) {
+ exemode = SLAPD_EXEMODE_LDIF2DB;
+ } else if ( strcmp( s, "archive2db" ) == 0 ) {
+ exemode = SLAPD_EXEMODE_ARCHIVE2DB;
+ } else if ( strcmp( s, "db2archive" ) == 0 ) {
+ exemode = SLAPD_EXEMODE_DB2ARCHIVE;
+ } else if ( strcmp( s, "server" ) == 0 ) {
+ exemode = SLAPD_EXEMODE_SLAPD;
+ } else if ( strcmp( s, "dbtest" ) == 0 ) {
+ exemode = SLAPD_EXEMODE_DBTEST;
+ } else if ( strcmp( s, "db2index" ) == 0 ) {
+ exemode = SLAPD_EXEMODE_DB2INDEX;
+ } else if ( strcmp( s, "refer" ) == 0 ) {
+ exemode = SLAPD_EXEMODE_REFERRAL;
+ } else if ( strcmp( s, "suffix2instance" ) == 0 ) {
+ exemode = SLAPD_EXEMODE_SUFFIX2INSTANCE;
+ }
+#if defined(UPGRADEDB)
+ else if ( strcmp( s, "upgradedb" ) == 0 )
+ {
+ exemode = SLAPD_EXEMODE_UPGRADEDB;
+ }
+#endif
+ else if ( exit_if_unknown ) {
+ fprintf( stderr, "usage: %s -D instancedir "
+ "[ldif2db | db2ldif | archive2db "
+ "| db2archive | db2index | refer | suffix2instance"
+#if defined(UPGRADEDB)
+ " | upgradedb] "
+#else
+ "] "
+#endif
+ "[options]\n", progname );
+ exit( 1 );
+ } else {
+ exemode = SLAPD_EXEMODE_UNKNOWN;
+ }
+
+ return( exemode );
+}
+
+
+static void
+usage( char *name, char *extraname )
+{
+ char *usagestr = NULL;
+ char *extraspace;
+
+ if ( extraname == NULL ) {
+ extraspace = extraname = "";
+ } else {
+ extraspace = " ";
+ }
+
+ switch( slapd_exemode ) {
+ case SLAPD_EXEMODE_DB2LDIF:
+ usagestr = "usage: %s %s%s-D instancedir [-n backend-instance-name] [-d debuglevel] "
+ "[-N] [-a outputfile] [-r] [-C] [{-s includesuffix}*] "
+ "[{-x excludesuffix}*] [-u] [-U] [-m] [-M] [-E]\n"
+ "Note: either \"-n backend_instance_name\" or \"-s includesuffix\" is required.\n";
+ break;
+ case SLAPD_EXEMODE_LDIF2DB:
+ usagestr = "usage: %s %s%s-D instancedir [-d debuglevel] "
+ "[-n backend_instance_name] [-O] [-g uniqueid_type] [--namespaceid uniqueID]"
+ "[{-s includesuffix}*] [{-x excludesuffix}*] [-E] {-i ldif-file}*\n"
+ "Note: either \"-n backend_instance_name\" or \"-s includesuffix\" is required.\n";
+ break;
+ case SLAPD_EXEMODE_DB2ARCHIVE:
+ usagestr = "usage: %s %s%s-D instancedir [-d debuglevel] -a archivedir\n";
+ break;
+ case SLAPD_EXEMODE_ARCHIVE2DB:
+ usagestr = "usage: %s %s%s-D instancedir [-d debuglevel] -a archivedir\n";
+ break;
+ case SLAPD_EXEMODE_DB2INDEX:
+ usagestr = "usage: %s %s%s-D instancedir -n backend-instance-name "
+ "[-d debuglevel] {-t attributetype}* {-T VLV Search Name}*\n";
+ /* JCM should say 'Address Book' or something instead of VLV */
+ break;
+ case SLAPD_EXEMODE_REFERRAL:
+ usagestr = "usage: %s %s%s-D instancedir -r referral-url [-p port]\n";
+ break;
+ case SLAPD_EXEMODE_DBTEST:
+ usagestr = "usage: %s %s%s-D instancedir -n backend-instance-name "
+ "[-d debuglevel] [-S] [-v]\n";
+ break;
+ case SLAPD_EXEMODE_SUFFIX2INSTANCE:
+ usagestr = "usage: %s %s%s -D instancedir {-s suffix}*\n";
+ break;
+#if defined(UPGRADEDB)
+ case SLAPD_EXEMODE_UPGRADEDB:
+ usagestr = "usage: %s %s%s-D instancedir [-d debuglevel] [-f] -a archivedir\n";
+ break;
+#endif
+
+ default: /* SLAPD_EXEMODE_SLAPD */
+ usagestr = "usage: %s %s%s-D instancedir [-d debuglevel] "
+ "[-i pidlogfile] [-v] [-V]\n";
+ }
+
+ fprintf( stderr, usagestr, name, extraname, extraspace );
+}
+
+
+/*
+ * These nasty globals are the settings collected from the
+ * command line by the process_command_line function. The
+ * various slapd_exemode functions read these to drive their
+ * execution.
+ */
+static char *extraname;
+static char *myname;
+static int n_port = 0;
+static int s_port = 0;
+static char **ldif_file = NULL;
+static int ldif_files = 0;
+static int ldif_backend = 0;
+static char *cmd_line_instance_name = NULL;
+static char **cmd_line_instance_names = NULL;
+static int skip_db_protect_check = 0;
+static char **db2ldif_include = NULL;
+static char **db2ldif_exclude = NULL;
+static int ldif2db_removedupvals = 1;
+static int ldif2db_noattrindexes = 0;
+static char **db2index_attrs = NULL;
+static int ldif_printkey = EXPORT_PRINTKEY|EXPORT_APPENDMODE;
+static char *archive_name = NULL;
+static int db2ldif_dump_replica = 0;
+static int db2ldif_dump_uniqueid = 1;
+static int ldif2db_generate_uniqueid = SLAPI_UNIQUEID_GENERATE_TIME_BASED;
+static int ldif2db_load_state= 1;
+static char *ldif2db_namespaceid = NULL;
+int importexport_encrypt = 0;
+#if defined(UPGRADEDB)
+static int upgradedb_force = 0;
+#endif
+
+/* taken from idsktune */
+#if defined(__sun)
+static void ids_get_platform_solaris(char *buf)
+{
+ struct utsname u;
+ char sbuf[128];
+ FILE *fp;
+
+#if defined(sparc) || defined(__sparc)
+ int is_u = 0;
+
+ sbuf[0] = '\0';
+ sysinfo(SI_MACHINE,sbuf,128);
+
+ if (strcmp(sbuf,"sun4u") == 0) {
+ is_u = 1;
+ }
+
+ sbuf[0] = '\0';
+ sysinfo(SI_PLATFORM,sbuf,128);
+
+ sprintf(buf,"%ssparc%s-%s-solaris",
+ is_u ? "u" : "",
+ sizeof(long) == 4 ? "" : "v9",
+ sbuf);
+#else
+#if defined(i386) || defined(__i386)
+ sprintf(buf,"i386-unknown-solaris");
+#else
+ sprintf(buf,"unknown-unknown-solaris");
+#endif /* not i386 */
+#endif /* not sparc */
+
+ uname(&u);
+ if (isascii(u.release[0]) && isdigit(u.release[0])) strcat(buf,u.release);
+
+ fp = fopen("/etc/release","r");
+
+ if (fp != NULL) {
+ char *rp;
+
+ sbuf[0] = '\0';
+ fgets(sbuf,128,fp);
+ fclose(fp);
+ rp = strstr(sbuf,"Solaris");
+ if (rp) {
+ rp += 8;
+ while(*rp != 's' && *rp != '\0') rp++;
+ if (*rp == 's') {
+ char *rp2;
+ rp2 = strchr(rp,' ');
+ if (rp2) *rp2 = '\0';
+ strcat(buf,"_");
+ strcat(buf,rp);
+ }
+ }
+ }
+}
+#endif
+
+static void slapd_print_version(int verbose)
+{
+#if defined(__sun)
+ char buf[8192];
+#endif
+ char *versionstring = config_get_versionstring();
+ char *buildnum = config_get_buildnum();
+
+ printf( SLAPD_VENDOR_NAME "\n%s B%s\n", versionstring, buildnum);
+
+ /* not here in Win32 */
+#if !defined(_WIN32)
+ if (strcmp(buildnum,BUILD_NUM) != 0) {
+ printf( "ns-slapd: B%s\n", BUILD_NUM);
+ }
+#endif
+
+ slapi_ch_free( (void **)&versionstring);
+ slapi_ch_free( (void **)&buildnum);
+
+ if (verbose == 0) return;
+
+#if defined(__sun)
+ ids_get_platform_solaris(buf);
+ printf("System: %s\n",buf);
+#endif
+
+ /* this won't print much with the -v flag as the dse.ldif file
+ * hasn't be read yet.
+ */
+ plugin_print_versions();
+}
+
+#if defined( _WIN32 )
+/* On Windows, we signal the SCM when we're still starting up */
+static int
+write_start_pid_file()
+{
+ if( SlapdIsAService() )
+ {
+ /* Initialization complete and successful. Set service to running */
+ LDAPServerStatus.dwCurrentState = SERVICE_START_PENDING;
+ LDAPServerStatus.dwCheckPoint = 1;
+ LDAPServerStatus.dwWaitHint = 1000;
+
+ if (!SetServiceStatus(hLDAPServerServiceStatus, &LDAPServerStatus)) {
+ ReportSlapdEvent(EVENTLOG_INFORMATION_TYPE, MSG_SERVER_START_FAILED, 1,
+ "Could not set Service status.");
+ exit(1);
+ }
+ }
+
+ ReportSlapdEvent(EVENTLOG_INFORMATION_TYPE, MSG_SERVER_STARTED, 0, NULL );
+ return 0;
+}
+#else /* WIN32 */
+/* On UNIX, we create a file with our PID in it */
+static int
+write_start_pid_file()
+{
+ FILE *fp = NULL;
+ /*
+ * The following section of code is closely coupled with the
+ * admin programs. Please do not make changes here without
+ * consulting the start/stop code for the admin code.
+ */
+ if ( (fp = fopen( start_pid_file, "w" )) != NULL ) {
+ fprintf( fp, "%d\n", getpid() );
+ fclose( fp );
+ return 0;
+ } else
+ {
+ return -1;
+ }
+}
+#endif /* WIN32 */
+
+int
+main( int argc, char **argv)
+{
+ int return_value = 0;
+ slapdFrontendConfig_t *slapdFrontendConfig = getFrontendConfig();
+ daemon_ports_t arg = {0};
+ Slapi_Backend *be = NULL;
+ int init_ssl;
+#ifndef __LP64__
+#if defined(__hpux)
+ /* for static constructors */
+ _main();
+#endif
+#endif
+ /*
+ * Initialize NSPR very early. NSPR supports implicit initialization,
+ * but it is not bulletproof -- so it is better to be explicit.
+ */
+ PR_Init( PR_USER_THREAD, PR_PRIORITY_NORMAL, 0 );
+ FrontendConfig_init();
+
+#ifdef _WIN32
+ /* Break into the debugger if DEBUG_BREAK is set in the environment
+ to "slapd" */
+ {
+ char *s = getenv( "DEBUG_BREAK" );
+ if ( (s != NULL) && !stricmp(s, "slapd") )
+ DebugBreak();
+ }
+
+ /* do module debug level init for slapd, and libslapd */
+ module_ldap_debug = &slapd_ldap_debug;
+ libldap_init_debug_level(&slapd_ldap_debug);
+
+ dostounixpath( argv[0] );
+ _strlwr( argv[0] );
+
+#else /* _WIN32 */
+ /* Pause for the debugger if DEBUG_SLEEP is set in the environment */
+ {
+ char *s = getenv( "DEBUG_SLEEP" );
+ if ( (s != NULL) && isdigit(*s) ) {
+ int secs = atoi(s);
+ printf("slapd pid is %d\n", getpid());
+ sleep(secs);
+ }
+ }
+
+
+/* used to set configfile to the default config file name here */
+
+#endif /* _WIN32 */
+
+ if ( (myname = strrchr( argv[0], '/' )) == NULL ) {
+ myname = slapi_ch_strdup( argv[0] );
+ } else {
+ myname = slapi_ch_strdup( myname + 1 );
+ }
+
+#if defined( XP_WIN32 )
+ /* Strip ".exe" if it's there */
+ {
+ char *pdot;
+ if ( (pdot = strrchr( myname, '.' )) != NULL ) {
+ *pdot = '\0';
+ }
+ }
+#endif
+
+ process_command_line(argc,argv,myname,&extraname);
+
+ if (!slapdFrontendConfig->instancedir ||
+ !slapdFrontendConfig->configdir) {
+ usage( myname, extraname );
+ exit( 1 );
+ }
+
+
+ /* display debugging level if it is anything other than the default */
+ if ( 0 != ( slapd_ldap_debug & ~LDAP_DEBUG_ANY )) {
+ slapd_debug_level_log( slapd_ldap_debug );
+ }
+
+#ifndef LDAP_DONT_USE_SMARTHEAP
+ MemRegisterTask();
+#endif
+
+ slapd_init();
+ g_log_init(1);
+ vattr_init();
+
+ if (slapd_exemode == SLAPD_EXEMODE_REFERRAL) {
+ /* make up the config stuff */
+ referral_set_defaults();
+ n_port = config_get_port();
+ s_port = config_get_secureport();
+ register_objects();
+
+ } else {
+ slapdFrontendConfig_t *slapdFrontendConfig = getFrontendConfig();
+ /* The 2 calls below have been moved to this place to make sure that they
+ * are called before setup_internal_backends to avoid bug 524439 */
+ /*
+ * The 2 calls below where being sometimes called AFTER ldapi_register_extended_op
+ * (such fact was being stated and reproducible for some optimized installations
+ * at startup (bug 524439)... Such bad call was happening in the context of
+ * setup_internal_backends -> dse_read_file -> load_plugin_entry ->
+ * plugin_setup -> replication_multimaster_plugin_init ->
+ * slapi_register_plugin -> plugin_setup -> multimaster_start_extop_init ->
+ * slapi_pblock_set -> ldapi_register_extended_op... Unfortunately, the server
+ * design is such that it is assumed that ldapi_init_extended_ops is always
+ * called first.
+ * THE FIX: Move the two calls below before a call to setup_internal_backends
+ * (down in this same function)
+ */
+ init_saslmechanisms();
+ ldapi_init_extended_ops();
+
+
+ /*
+ * Initialize the default backend. This should be done before we
+ * process the config. files
+ */
+ defbackend_init();
+
+ /*
+ * Register the extensible objects with the factory.
+ */
+ register_objects();
+ /*
+ * Register the controls that we support.
+ */
+ init_controls();
+
+ /*
+ * Process the config files.
+ */
+ if (0 == slapd_bootstrap_config(slapdFrontendConfig->configdir)) {
+ slapi_log_error(SLAPI_LOG_FATAL, "startup",
+ "The configuration files in directory %s could not be read or were not found. Please refer to the error log or output for more information.\n",
+ slapdFrontendConfig->configdir);
+ exit(1);
+ }
+
+ /* -sduloutre: must be done before any internal search */
+ /* do it before splitting off to other modes too -robey */
+ /* -richm: must be done before reading config files */
+ if (0 != (return_value = compute_init())) {
+ LDAPDebug(LDAP_DEBUG_ANY, "Initialization Failed 0 %d\n",return_value,0,0);
+ exit (1);
+ }
+ entry_computed_attr_init();
+
+ if (0 == setup_internal_backends(slapdFrontendConfig->configdir)) {
+ slapi_log_error(SLAPI_LOG_FATAL, "startup",
+ "The configuration files in directory %s could not be read or were not found. Please refer to the error log or output for more information.\n",
+ slapdFrontendConfig->configdir);
+ exit(1);
+ }
+
+ n_port = config_get_port();
+ s_port = config_get_secureport();
+ }
+
+ raise_process_limits(); /* should be done ASAP once config file read */
+
+#ifdef PUMPKIN_HOUR
+ if ( time( NULL ) > (PUMPKIN_HOUR - 10) ) {
+ LDAPDebug( LDAP_DEBUG_ANY,
+ "ERROR: ** This beta software has expired **\n", 0, 0, 0 );
+ exit( 1 );
+ }
+#endif
+
+ /* Set entry points in libslapd */
+ set_entry_points();
+
+ /*
+ * Initialise NSS once for the whole slapd process, whether SSL
+ * is enabled or not. We use NSS for random number generation and
+ * other things even if we are not going to accept SSL connections.
+ * We also need NSS for attribute encryption/decryption on import and export.
+ */
+ init_ssl = ( (slapd_exemode == SLAPD_EXEMODE_SLAPD) || importexport_encrypt)
+ && config_get_security()
+ && (0 != s_port) && (s_port <= LDAP_PORT_MAX);
+ /* As of DS 6.1, always do a full initialization so that other
+ * modules can assume NSS is available
+ */
+ if ( slapd_nss_init((slapd_exemode == SLAPD_EXEMODE_SLAPD),
+ (slapd_exemode != SLAPD_EXEMODE_REFERRAL) /* have config? */ )) {
+ LDAPDebug(LDAP_DEBUG_ANY,
+ "ERROR: NSS Initialization Failed.\n", 0, 0, 0);
+ exit (1);
+ }
+
+ if (slapd_exemode == SLAPD_EXEMODE_SLAPD) {
+ client_auth_init();
+ }
+
+ if ( init_ssl && ( 0 != slapd_ssl_init())) {
+ LDAPDebug(LDAP_DEBUG_ANY,
+ "ERROR: SSL Initialization Failed.\n", 0, 0, 0 );
+ exit( 1 );
+ }
+
+ /*
+ * if we were called upon to do special database stuff, do it and be
+ * done.
+ */
+ switch ( slapd_exemode ) {
+ case SLAPD_EXEMODE_LDIF2DB:
+ return slapd_exemode_ldif2db();
+
+ case SLAPD_EXEMODE_DB2LDIF:
+ return slapd_exemode_db2ldif(argc,argv);
+
+ case SLAPD_EXEMODE_DB2INDEX:
+ return slapd_exemode_db2index();
+
+ case SLAPD_EXEMODE_ARCHIVE2DB:
+ return slapd_exemode_archive2db();
+
+ case SLAPD_EXEMODE_DB2ARCHIVE:
+ return slapd_exemode_db2archive();
+
+ case SLAPD_EXEMODE_DBTEST:
+ return slapd_exemode_dbtest();
+
+ case SLAPD_EXEMODE_REFERRAL:
+ /* check that all the necessary info was given, then go on */
+ if (! config_check_referral_mode()) {
+ LDAPDebug(LDAP_DEBUG_ANY,
+ "ERROR: No referral URL supplied\n", 0, 0, 0);
+ usage( myname, extraname );
+ exit(1);
+ }
+ break;
+
+ case SLAPD_EXEMODE_SUFFIX2INSTANCE:
+ return slapd_exemode_suffix2instance();
+
+#if defined(UPGRADEDB)
+ case SLAPD_EXEMODE_UPGRADEDB:
+ return slapd_exemode_upgradedb();
+#endif
+
+ case SLAPD_EXEMODE_PRINTVERSION:
+ slapd_print_version(1);
+ exit(1);
+ }
+
+#if defined( XP_WIN32 )
+ /* Register with the NT EventLog */
+ hSlapdEventSource = RegisterEventSource(NULL, pszServerName );
+ if( !hSlapdEventSource )
+ {
+ char szMessage[256];
+ sprintf( szMessage, "Directory Server %s is terminating. Failed "
+ "to set the EventLog source.", pszServerName);
+ MessageBox(GetDesktopWindow(), szMessage, " ",
+ MB_ICONEXCLAMATION | MB_OK);
+ exit( 1 );
+ }
+
+ /* Check to ensure there isn't a copy of this server already running. */
+ if( MultipleInstances() )
+ exit( 1 );
+#endif
+
+ /*
+ * Detach ourselves from the terminal (unless running in debug mode).
+ * We must detach before we start any threads since detach forks() on
+ * UNIX.
+ */
+ detach();
+
+ /*
+ * Now write our PID to the startup PID file.
+ * This is used by the start up script to determine our PID quickly
+ * after we fork, without needing to wait for the 'real' pid file to be
+ * written. That could take minutes. And the start script will wait
+ * that long looking for it. With this new 'early pid' file, it can avoid
+ * doing that, by detecting the pid and watching for the process exiting.
+ * This removes the blank stares all round from start-slapd when the server
+ * fails to start for some reason
+ */
+ write_start_pid_file();
+
+ /* Make sure we aren't going to run slapd in
+ * a mode that is going to conflict with other
+ * slapd processes that are currently running
+ */
+ if ((slapd_exemode != SLAPD_EXEMODE_REFERRAL) &&
+ ( add_new_slapd_process(slapd_exemode, db2ldif_dump_replica,
+ skip_db_protect_check) == -1 )) {
+ LDAPDebug( LDAP_DEBUG_ANY,
+ "Shutting down due to possible conflicts with other slapd processes\n",
+ 0, 0, 0 );
+ exit(1);
+ }
+
+
+ /*
+ * Now it is safe to log our first startup message. If we were to
+ * log anything earlier than now it would appear on the admin startup
+ * screen twice because before we detach everything is sent to both
+ * stderr and our error log. Yuck.
+ */
+ if (1) {
+ char *versionstring = config_get_versionstring();
+ char *buildnum = config_get_buildnum();
+ LDAPDebug( LDAP_DEBUG_ANY, "%s B%s starting up\n",
+ versionstring, buildnum, 0 );
+ slapi_ch_free((void **)&buildnum);
+ slapi_ch_free((void **)&versionstring);
+ }
+
+ /*
+ * After we read the config file we should make
+ * sure that everything we needed to read in has
+ * been read in and we'll start whatever threads,
+ * etc the backends need to start
+ */
+
+
+ /* Important: up 'till here we could be running as root (on unix).
+ * we believe that we've not created any files before here, otherwise
+ * they'd be owned by root, which is bad. We're about to change identity
+ * to some non-root user, but before we do, we call the daemon code
+ * to let it open the listen sockets. If these sockets are low-numbered,
+ * we need to be root in order to open them.
+ */
+
+ {
+ arg.n_port = (unsigned short)n_port;
+ if ( slapd_listenhost2addr( config_get_listenhost(),
+ &arg.n_listenaddr ) != 0 ) {
+ return(1);
+ }
+
+ arg.s_port = (unsigned short)s_port;
+ if ( slapd_listenhost2addr( config_get_securelistenhost(),
+ &arg.s_listenaddr ) != 0 ) {
+ return(1);
+ }
+
+ return_value = daemon_pre_setuid_init(&arg);
+ if (0 != return_value) {
+ LDAPDebug( LDAP_DEBUG_ANY, "Failed to init daemon\n",
+ 0, 0, 0 );
+ exit(1);
+ }
+ }
+
+ /* Now, sockets are open, so we can safely change identity now */
+
+#ifndef _WIN32
+ return_value = main_setuid(slapdFrontendConfig->localuser);
+ if (0 != return_value) {
+ LDAPDebug( LDAP_DEBUG_ANY, "Failed to change user and group identity to that of %s\n",
+ slapdFrontendConfig->localuser, 0, 0 );
+ exit(1);
+ }
+#endif
+
+
+ /* -sduloutre: compute_init() and entry_computed_attr_init() moved up */
+
+ if (slapd_exemode != SLAPD_EXEMODE_REFERRAL) {
+ int rc;
+ Slapi_DN *sdn;
+
+ fedse_create_startOK(DSE_FILENAME, DSE_STARTOKFILE,
+ slapdFrontendConfig->configdir);
+
+ eq_init(); /* must be done before plugins started */
+ snmp_collator_start();
+ ps_init_psearch_system(); /* must come before plugin_startall() */
+
+ /* Initailize the mapping tree */
+
+ if (mapping_tree_init())
+ {
+ LDAPDebug( LDAP_DEBUG_ANY, "Failed to init mapping tree\n",
+ 0, 0, 0 );
+ exit(1);
+ }
+
+
+ /* initialize UniqueID generator - must be done once backends are started
+ and event queue is initialized but before plugins are started */
+ sdn = slapi_sdn_new_dn_byval ("cn=uniqueid generator,cn=config");
+ rc = uniqueIDGenInit (NULL, sdn, slapd_exemode == SLAPD_EXEMODE_SLAPD);
+ slapi_sdn_free (&sdn);
+ if (rc != UID_SUCCESS)
+ {
+ LDAPDebug( LDAP_DEBUG_ANY,
+ "Fatal Error---Failed to initialize uniqueid generator; error = %d. "
+ "Exiting now.\n", rc, 0, 0 );
+ exit( 1 );
+ }
+
+ /* --ugaston: register the start-tls plugin */
+#ifndef _WIN32
+ if ( slapd_security_library_is_initialized() != 0 ) {
+ start_tls_register_plugin();
+ LDAPDebug( LDAP_DEBUG_PLUGIN,
+ "Start TLS plugin registered.\n",
+ 0, 0, 0 );
+ }
+#endif
+
+ plugin_startall(argc, argv, 1 /* Start Backends */, 1 /* Start Globals */);
+ if (housekeeping_start((time_t)0, NULL) == NULL) {
+ exit (1);
+ }
+
+ eq_start(); /* must be done after plugins started */
+
+#ifdef HPUX10
+ /* HPUX linker voodoo */
+ if (collation_init == NULL) {
+ exit (1);
+ }
+
+#endif /* HPUX */
+
+ normalize_oc();
+
+ if (n_port) {
+#if defined(NET_SSL)
+ } else if ( config_get_security()) {
+#endif
+ } else {
+#ifdef _WIN32
+ if( SlapdIsAService() )
+ {
+ LDAPServerStatus.dwCurrentState = SERVICE_STOPPED;
+ LDAPServerStatus.dwCheckPoint = 0;
+ LDAPServerStatus.dwWaitHint = 0;
+ LDAPServerStatus.dwWin32ExitCode = 1;
+ LDAPServerStatus.dwServiceSpecificExitCode = 1;
+
+ SetServiceStatus(hLDAPServerServiceStatus, &LDAPServerStatus);
+
+ /* Log this event */
+ ReportSlapdEvent(EVENTLOG_INFORMATION_TYPE, MSG_SERVER_START_FAILED, 1,
+ "Check server port specification");
+ }
+ else
+ {
+ char szMessage[256];
+ sprintf( szMessage, "The Directory Server %s is terminating due to an error. Check server port specification", pszServerName);
+ MessageBox(GetDesktopWindow(), szMessage, " ", MB_ICONEXCLAMATION | MB_OK);
+ }
+#endif
+ exit(1);
+ }
+ }
+
+ {
+ Slapi_PBlock pb;
+ memset( &pb, '\0', sizeof(pb) );
+ pb.pb_backend = be;
+ }
+
+ if (slapd_exemode != SLAPD_EXEMODE_REFERRAL) {
+ /* else do this after seteuid() */
+ lite_entries_init();
+
+ /* setup cn=tasks tree */
+ task_init();
+
+ /* pw_init() needs to be here since it uses aci function calls. */
+ pw_init();
+ /* Initialize the sasl mapping code */
+ if (sasl_map_init()) {
+ LDAPDebug( LDAP_DEBUG_ANY, "Failed to initialize sasl mapping code\n", 0, 0, 0 );
+ }
+ }
+
+ /*
+ * search_register_reslimits() and daemon_register_reslimits() can
+ * be called any time before we start accepting client connections.
+ * We call these even when running in referral mode because they
+ * do little harm and registering at least one resource limit forces
+ * the reslimit subsystem to initialize itself... which prevents
+ * strange error messages from being logged to the error log for
+ * the first LDAP connection.
+ */
+ if ( search_register_reslimits() != SLAPI_RESLIMIT_STATUS_SUCCESS ||
+ daemon_register_reslimits() != SLAPI_RESLIMIT_STATUS_SUCCESS ) {
+ exit( 1 );
+ }
+
+ {
+ time( &starttime );
+
+ slapd_daemon(&arg);
+ }
+ LDAPDebug( LDAP_DEBUG_ANY, "slapd stopped.\n", 0, 0, 0 );
+ reslimit_cleanup();
+ compute_terminate();
+ vattr_cleanup();
+ sasl_map_done();
+ PR_Cleanup();
+#ifdef _WIN32
+ /* Clean up the mutex used to interlock processes, before we exit */
+ remove_slapd_process();
+#endif
+#if ( defined( hpux ) || defined( irix ) || defined( aix ) || defined( OSF1 ))
+ exit( 0 );
+#else
+ return 0;
+#endif
+}
+
+
+#if ( defined( hpux ) || defined( irix ))
+void *
+signal2sigaction( int s, void *a )
+{
+ struct sigaction act;
+
+ memset(&act, 0, sizeof(struct sigaction));
+ act.sa_handler = (VFP)a;
+ act.sa_flags = 0;
+ (void)sigemptyset( &act.sa_mask );
+ (void)sigaddset( &act.sa_mask, s );
+ (void)sigaction( s, &act, NULL );
+}
+#endif /* hpux || irix */
+
+static void
+register_objects()
+{
+ get_operation_object_type();
+ daemon_register_connection();
+ get_entry_object_type();
+ mapping_tree_get_extension_type ();
+}
+
+static void
+process_command_line(int argc, char **argv, char *myname,
+ char **extraname)
+{
+ int i;
+ char errorbuf[SLAPI_DSE_RETURNTEXT_SIZE];
+ char *opts;
+ static struct opt_ext *long_opts;
+ int longopt_index=0;
+
+ /*
+ * Refer to the file getopt_ext.h for an overview of how to use the
+ * long option names
+ *
+ */
+
+
+ /*
+ * when a new option letter is used, please move it from the "available"
+ * list to the "used" list.
+ *
+ */
+ /*
+ * single-letter options already in use:
+ *
+ * a C c D E d f G g i
+ * L l N m n O o P p r S s T t
+ * u v V w x Z z
+ *
+ * 1
+ *
+ */
+
+ /*
+ * single-letter options still available:
+ *
+ * A B b e F H h I J j
+ * K k M Q q R
+ * W X Y y
+ *
+ * 2 3 4 5 6 7 8 9 0
+ *
+ */
+
+ char *opts_dbtest = "vd:n:SD:";
+ struct opt_ext long_options_dbtest[] = {
+ {"version",ArgNone,'v'},
+ {"debug",ArgRequired,'d'},
+ {"backend",ArgRequired,'n'},
+ {"allowMultipleProcesses",ArgNone,'S'},
+ {"instanceDir",ArgRequired,'D'},
+ {0,0,0}};
+
+
+ char *opts_db2ldif = "vd:D:ENa:rs:x:CSut:n:UmMo1";
+ struct opt_ext long_options_db2ldif[] = {
+ {"version",ArgNone,'v'},
+ {"debug",ArgRequired,'d'},
+ {"dontPrintKey",ArgNone,'n'},
+ {"archive",ArgRequired,'a'},
+ {"replica",ArgNone,'r'},
+ {"include",ArgRequired,'s'},
+ {"exclude",ArgRequired,'x'},
+ /*{"whatshouldwecallthis",ArgNone,'C'},*/
+ {"allowMultipleProcesses",ArgNone,'S'},
+ {"noUniqueIds",ArgNone,'u'},
+ {"instanceDir",ArgRequired,'D'},
+ {"encrypt",ArgOptional,'E'},
+ {"nowrap",ArgNone,'U'},
+ {"minimalEncode",ArgNone,'m'},
+ {"oneOutputFile",ArgNone,'o'},
+ {"multipleOutputFile",ArgNone,'M'},
+ {"noVersionNum",ArgNone,'1'},
+ {0,0,0}};
+
+ char *opts_ldif2db = "vd:i:g:G:n:s:x:NOCc:St:D:E";
+ struct opt_ext long_options_ldif2db[] = {
+ {"version",ArgNone,'v'},
+ {"debug",ArgRequired,'d'},
+ {"ldiffile",ArgRequired,'i'},
+ {"generateUniqueId",ArgOptional,'g'},
+ {"backend",ArgRequired,'n'},
+ {"include",ArgRequired,'s'},
+ {"exclude",ArgRequired,'x'},
+ {"noindex",ArgNone,'O'},
+ /*{"whatshouldwecallthis",ArgNone,'C'},*/
+ /*{"whatshouldwecallthis",ArgRequired,'c'},*/
+ {"allowMultipleProcesses",ArgNone,'S'},
+ {"namespaceid", ArgRequired, 'G'},
+ {"nostate",ArgNone,'Z'},
+ {"instanceDir",ArgRequired,'D'},
+ {"encrypt",ArgOptional,'E'},
+ {0,0,0}};
+
+ char *opts_archive2db = "vd:i:a:SD:";
+ struct opt_ext long_options_archive2db[] = {
+ {"version",ArgNone,'v'},
+ {"debug",ArgRequired,'d'},
+ {"pidfile",ArgRequired,'i'},
+ {"archive",ArgRequired,'a'},
+ {"allowMultipleProcesses",ArgNone,'S'},
+ {"instanceDir",ArgRequired,'D'},
+ {0,0,0}};
+
+
+ char *opts_db2archive = "vd:i:a:SD:";
+ struct opt_ext long_options_db2archive[] = {
+ {"version",ArgNone,'v'},
+ {"debug",ArgRequired,'d'},
+ {"pidfile",ArgRequired,'i'},
+ {"archive",ArgRequired,'a'},
+ {"allowMultipleProcesses",ArgNone,'S'},
+ {"instanceDir",ArgRequired,'D'},
+ {0,0,0}};
+
+ char *opts_db2index = "vd:a:t:T:SD:n:s:x:";
+ struct opt_ext long_options_db2index[] = {
+ {"version",ArgNone,'v'},
+ {"debug",ArgRequired,'d'},
+ {"backend",ArgRequired,'n'},
+ {"archive",ArgRequired,'a'},
+ {"indexAttribute",ArgRequired,'t'},
+ {"vlvIndex",ArgRequired,'T'},
+ {"allowMultipleProcesses",ArgNone,'S'},
+ {"instanceDir",ArgRequired,'D'},
+ {"include",ArgRequired,'s'},
+ {"exclude",ArgRequired,'x'},
+ {0,0,0}};
+
+#if defined(UPGRADEDB)
+ char *opts_upgradedb = "vfd:a:D:";
+ struct opt_ext long_options_upgradedb[] = {
+ {"version",ArgNone,'v'},
+ {"debug",ArgRequired,'d'},
+ {"force",ArgNone,'f'},
+ {"archive",ArgRequired,'a'},
+ {"instanceDir",ArgRequired,'D'},
+ {0,0,0}};
+#endif
+
+ char *opts_referral = "vd:p:r:SD:";
+ struct opt_ext long_options_referral[] = {
+ {"version",ArgNone,'v'},
+ {"debug",ArgRequired,'d'},
+ {"port",ArgRequired,'p'},
+ {"referralMode",ArgRequired,'r'},
+ {"allowMultipleProcesses",ArgNone,'S'},
+ {"instanceDir",ArgRequired,'D'},
+ {0,0,0}};
+
+ char *opts_suffix2instance = "s:D:";
+ struct opt_ext long_options_suffix2instance[] = {
+ {"suffix",ArgRequired,'s'},
+ {"instanceDir",ArgRequired,'D'},
+ {0,0,0}};
+
+ char *opts_slapd = "vVd:i:SD:w:";
+ struct opt_ext long_options_slapd[] = {
+ {"version",ArgNone,'v'},
+ {"versionFull",ArgNone,'V'},
+ {"debug",ArgRequired,'d'},
+ {"pidfile",ArgRequired,'i'},
+ {"allowMultipleProcesses",ArgNone,'S'},
+ {"instanceDir",ArgRequired,'D'},
+ {"startpidfile",ArgRequired,'w'},
+ {0,0,0}};
+
+ /*
+ * determine which of serveral modes we are executing in.
+ */
+ *extraname = NULL;
+ if (( slapd_exemode = name2exemode( myname, myname, 0 ))
+ == SLAPD_EXEMODE_UNKNOWN ) {
+
+
+ if ( argv[1] != NULL && argv[1][0] != '-' ) {
+ slapd_exemode = name2exemode( myname, argv[1], 1 );
+ *extraname = argv[1];
+ optind_ext = 2; /* make getopt() skip argv[1] */
+ optind = 2;
+ }
+ }
+ if ( slapd_exemode == SLAPD_EXEMODE_UNKNOWN ) {
+ slapd_exemode = SLAPD_EXEMODE_SLAPD; /* default */
+ }
+ /*
+ * richm: If running in regular slapd server mode, allow the front
+ * end dse files (dse.ldif and ldbm.ldif) to be written in case of
+ * additions or modifications. In all other modes, these files
+ * should only be read and never written.
+ */
+
+ if (slapd_exemode == SLAPD_EXEMODE_SLAPD ||
+ slapd_exemode == SLAPD_EXEMODE_ARCHIVE2DB || /* bak2db adjusts config */
+ slapd_exemode == SLAPD_EXEMODE_UPGRADEDB) /* update idl-switch */
+ dse_unset_dont_ever_write_dse_files();
+
+ /* maintain compatibility with pre-5.x options */
+ switch( slapd_exemode ) {
+ case SLAPD_EXEMODE_DBTEST:
+ opts = opts_dbtest;
+ long_opts = long_options_dbtest;
+ break;
+ case SLAPD_EXEMODE_DB2LDIF:
+ opts = opts_db2ldif;
+ long_opts = long_options_db2ldif;
+ break;
+ case SLAPD_EXEMODE_LDIF2DB:
+ opts = opts_ldif2db;
+ long_opts = long_options_ldif2db;
+ break;
+ case SLAPD_EXEMODE_ARCHIVE2DB:
+ opts = opts_archive2db;
+ long_opts = long_options_archive2db;
+ break;
+ case SLAPD_EXEMODE_DB2ARCHIVE:
+ init_cmd_shutdown_detect();
+ opts = opts_db2archive;
+ long_opts = long_options_db2archive;
+ break;
+ case SLAPD_EXEMODE_DB2INDEX:
+ opts = opts_db2index;
+ long_opts = long_options_db2index;
+ break;
+ case SLAPD_EXEMODE_REFERRAL:
+ opts = opts_referral;
+ long_opts = long_options_referral;
+ break;
+ case SLAPD_EXEMODE_SUFFIX2INSTANCE:
+ opts = opts_suffix2instance;
+ long_opts = long_options_suffix2instance;
+ break;
+#if defined(UPGRADEDB)
+ case SLAPD_EXEMODE_UPGRADEDB:
+ opts = opts_upgradedb;
+ long_opts = long_options_upgradedb;
+ break;
+#endif
+ default: /* SLAPD_EXEMODE_SLAPD */
+ opts = opts_slapd;
+ long_opts = long_options_slapd;
+ }
+
+ while ( (i = getopt_ext( argc, argv, opts,
+ long_opts, &longopt_index)) != EOF ) {
+ char *instancedir = 0;
+ switch ( i ) {
+#ifdef LDAP_DEBUG
+ case 'd': /* turn on debugging */
+ if ( optarg_ext[0] == '?'
+ || 0 == strcasecmp( optarg_ext, "help" )) {
+ slapd_debug_level_usage();
+ exit( 1 );
+ } else {
+ should_detach = 0;
+ slapd_ldap_debug = slapd_debug_level_string2level( optarg_ext );
+ if ( slapd_ldap_debug < 0 ) {
+ slapd_debug_level_usage();
+ exit( 1 );
+ }
+ slapd_ldap_debug |= LDAP_DEBUG_ANY;
+ }
+ break;
+#else
+ case 'd': /* turn on debugging */
+ fprintf( stderr,
+ "must compile with LDAP_DEBUG for debugging\n" );
+ break;
+#endif
+
+ case 'D': /* config dir */
+ instancedir = rel2abspath( optarg_ext );
+
+#if defined( XP_WIN32 )
+ pszServerName = slapi_ch_malloc( MAX_SERVICE_NAME );
+ if( !SlapdGetServerNameFromCmdline(pszServerName, instancedir, 1) )
+ {
+ MessageBox(GetDesktopWindow(), "Failed to get the Directory"
+ " Server name from the command-line argument.",
+ " ", MB_ICONEXCLAMATION | MB_OK);
+ exit( 1 );
+ }
+#endif
+ if ( config_set_instancedir( "instancedir (-D)",
+ instancedir, errorbuf, 1) != LDAP_SUCCESS ) {
+ fprintf( stderr, "%s: aborting now\n", errorbuf );
+ usage( myname, *extraname );
+ exit( 1 );
+ }
+ slapi_ch_free((void **)&instancedir);
+
+ break;
+
+ case 'p': /* port on which to listen (referral mode only) */
+ if ( config_set_port ( "portnumber (-p)", optarg_ext,
+ errorbuf, CONFIG_APPLY ) != LDAP_SUCCESS ) {
+ fprintf( stderr, "%s: aborting now\n", errorbuf );
+ usage( myname, *extraname );
+ exit( 1 );
+ }
+ break;
+
+ case 'i': /* set pid log file or ldif2db LDIF file */
+ if ( slapd_exemode == SLAPD_EXEMODE_LDIF2DB ) {
+ char *p;
+ /* if LDIF comes through standard input, skip path checking */
+ if ( optarg_ext[0] != '-' || strlen(optarg_ext) != 1) {
+#if defined( XP_WIN32 )
+ if ( optarg_ext[0] != '/' && optarg_ext[0] != '\\'
+ && (!isalpha( optarg_ext[0] ) || (optarg_ext[1] != ':')) ) {
+ fprintf( stderr, "%s file could not be opened: absolute path "
+ " required.\n", optarg_ext );
+ break;
+ }
+#else
+ if ( optarg_ext[ 0 ] != '/' ) {
+ fprintf( stderr, "%s file could not be opened: absolute path "
+ " required.\n", optarg_ext );
+ break;
+ }
+#endif
+ }
+ p = (char *) slapi_ch_malloc(strlen(optarg_ext) + 1);
+
+ strcpy(p, optarg_ext);
+ charray_add(&ldif_file, p);
+ ldif_files++;
+ } else {
+ pid_file = rel2abspath( optarg_ext );
+ }
+ break;
+ case 'w': /* set startup pid file */
+ start_pid_file = rel2abspath( optarg_ext );
+ break;
+ case 'n': /* which backend to do ldif2db for */
+ if (slapd_exemode == SLAPD_EXEMODE_LDIF2DB ||
+ slapd_exemode == SLAPD_EXEMODE_DBTEST ||
+ slapd_exemode == SLAPD_EXEMODE_DB2INDEX) {
+ /* The -n argument will give the name of a backend instance. */
+ cmd_line_instance_name = optarg_ext;
+ } else if (slapd_exemode == SLAPD_EXEMODE_DB2LDIF) {
+ char *s = slapi_ch_strdup(optarg_ext);
+ charray_add(&cmd_line_instance_names, s);
+ } else {
+ ldif_backend = atoi( optarg_ext );
+ }
+ break;
+ case 's': /* which suffix to include in import/export */
+ {
+ char *s= slapi_dn_normalize ( slapi_ch_strdup(optarg_ext) );
+ charray_add(&db2ldif_include,s);
+ }
+ break;
+ case 'x': /* which suffix to exclude in import/export */
+ {
+ char *s= slapi_dn_normalize ( slapi_ch_strdup(optarg_ext) );
+ charray_add(&db2ldif_exclude,s);
+ }
+ break;
+ case 'r': /* db2ldif for replication */
+ if (slapd_exemode == SLAPD_EXEMODE_REFERRAL) {
+ if (config_set_referral_mode( "referral (-r)", optarg_ext,
+ errorbuf, CONFIG_APPLY) != LDAP_SUCCESS) {
+ fprintf(stderr, "%s: aborting now\n",
+ errorbuf);
+ usage(myname, *extraname);
+ exit(1);
+ }
+ break;
+ }
+ if (slapd_exemode != SLAPD_EXEMODE_DB2LDIF ) {
+ usage( myname, *extraname );
+ exit( 1 );
+ }
+ db2ldif_dump_replica = 1;
+ break;
+ case 'N': /* do not do ldif2db duplicate value check */
+ if ( slapd_exemode != SLAPD_EXEMODE_LDIF2DB &&
+ slapd_exemode != SLAPD_EXEMODE_DB2LDIF) {
+ usage( myname, *extraname );
+ exit( 1 );
+ }
+ /*
+ * -N flag is obsolete, but we silently accept it
+ * so we don't break customer's scripts.
+ */
+
+ /* The -N flag now does what the -n flag used to do for db2ldif.
+ * This is so -n cane be used for the instance name just like
+ * with ldif2db. */
+ if ( slapd_exemode == SLAPD_EXEMODE_DB2LDIF ) {
+ ldif_printkey &= ~EXPORT_PRINTKEY;
+ }
+
+ break;
+
+ case 'U': /* db2ldif only */
+ if ( slapd_exemode != SLAPD_EXEMODE_DB2LDIF ) {
+ usage( myname, *extraname );
+ exit( 1 );
+ }
+
+ /*
+ * don't fold (wrap) long lines (default is to fold),
+ * as of ldapsearch -T
+ */
+ ldif_printkey |= EXPORT_NOWRAP;
+
+ break;
+
+ case 'm': /* db2ldif only */
+ if ( slapd_exemode != SLAPD_EXEMODE_DB2LDIF ) {
+ usage( myname, *extraname );
+ exit( 1 );
+ }
+
+ /* minimal base64 encoding */
+ ldif_printkey |= EXPORT_MINIMAL_ENCODING;
+
+ break;
+
+ case 'M': /* db2ldif only */
+ if ( slapd_exemode != SLAPD_EXEMODE_DB2LDIF ) {
+ usage( myname, *extraname );
+ exit( 1 );
+ }
+
+ /*
+ * output ldif is stored in several file called intance_filename.
+ * by default, all instances are stored in the single filename.
+ */
+ ldif_printkey &= ~EXPORT_APPENDMODE;
+
+ break;
+
+ case 'o': /* db2ldif only */
+ if ( slapd_exemode != SLAPD_EXEMODE_DB2LDIF ) {
+ usage( myname, *extraname );
+ exit( 1 );
+ }
+
+ /*
+ * output ldif is stored in one file.
+ * by default, each instance is stored in instance_filename.
+ */
+ ldif_printkey |= EXPORT_APPENDMODE;
+
+ break;
+
+ case 'C':
+ if (slapd_exemode == SLAPD_EXEMODE_LDIF2DB) {
+ /* used to mean "Cool new import" (which is now
+ * the default) -- ignore
+ */
+ break;
+ }
+ if (slapd_exemode == SLAPD_EXEMODE_DB2LDIF) {
+ /* possibly corrupted db -- don't look at any
+ * file except id2entry. yet another overloaded
+ * flag.
+ */
+ ldif_printkey |= EXPORT_ID2ENTRY_ONLY;
+ break;
+ }
+ usage( myname, *extraname );
+ exit( 1 );
+
+ case 'c': /* merge chunk size for Cool new import */
+ if ( slapd_exemode != SLAPD_EXEMODE_LDIF2DB ) {
+ usage( myname, *extraname );
+ exit( 1 );
+ }
+ ldif2db_removedupvals = atoi(optarg_ext); /* We overload this flag---ok since we always check for dupes in the new code */
+ break;
+
+ case 'O': /* only create core db, no attr indexes */
+ if ( slapd_exemode != SLAPD_EXEMODE_LDIF2DB ) {
+ usage( myname, *extraname );
+ exit( 1 );
+ }
+ ldif2db_noattrindexes = 1;
+ break;
+
+ case 't': /* attribute type to index - may be repeated */
+ case 'T': /* VLV Search to index - may be repeated */
+ if ( slapd_exemode == SLAPD_EXEMODE_DB2INDEX ) {
+ char *p= slapi_ch_malloc(strlen(optarg_ext) + 2);
+ sprintf(p,"%c%s",i,optarg_ext);
+ charray_add( &db2index_attrs, p);
+ break;
+ }
+ usage( myname, *extraname );
+ exit(1);
+
+ case 'v': /* print version and exit */
+ slapd_print_version(0);
+ exit( 1 );
+ break;
+
+ case 'V':
+ slapd_exemode = SLAPD_EXEMODE_PRINTVERSION;
+ break;
+
+ case 'a': /* archive pathname for db */
+ archive_name = optarg_ext;
+ break;
+
+ case 'Z':
+ if (slapd_exemode == SLAPD_EXEMODE_LDIF2DB)
+ {
+ ldif2db_load_state= 0;
+ break;
+ }
+ usage( myname, *extraname );
+ exit(1);
+ case 'S': /* skip the check for slad running in conflicting modes */
+ skip_db_protect_check = 1;
+ break;
+ case 'u': /* do not dump uniqueid for db2ldif */
+ if ( slapd_exemode != SLAPD_EXEMODE_DB2LDIF ) {
+ usage( myname, *extraname );
+ exit( 1 );
+ }
+ db2ldif_dump_uniqueid = 0;
+ break;
+ case 'g': /* generate uniqueid for ldif2db */
+ if ( slapd_exemode != SLAPD_EXEMODE_LDIF2DB ) {
+ usage( myname, *extraname );
+ exit( 1 );
+ }
+ if (optarg_ext == NULL){
+ printf ("ldif2db: generation type is not specified for -g; "
+ "random generation is used\n");
+ ldif2db_generate_uniqueid = SLAPI_UNIQUEID_GENERATE_TIME_BASED;
+ }
+ else if (strcasecmp (optarg_ext, "none") == 0)
+ ldif2db_generate_uniqueid = SLAPI_UNIQUEID_GENERATE_NONE;
+ else if (strcasecmp (optarg_ext, "deterministic") == 0) /* name based */
+ ldif2db_generate_uniqueid = SLAPI_UNIQUEID_GENERATE_NAME_BASED;
+ else /* default - time based */
+ ldif2db_generate_uniqueid = SLAPI_UNIQUEID_GENERATE_TIME_BASED;
+ break;
+ case 'G': /* namespace id for name based uniqueid generation for ldif2db */
+ if ( slapd_exemode != SLAPD_EXEMODE_LDIF2DB ) {
+ usage( myname, *extraname );
+ exit( 1 );
+ }
+
+ ldif2db_namespaceid = optarg_ext;
+ break;
+ case 'E': /* encrypt data if importing, decrypt if exporting */
+ if ( (slapd_exemode != SLAPD_EXEMODE_LDIF2DB) && (slapd_exemode != SLAPD_EXEMODE_DB2LDIF)) {
+ usage( myname, *extraname );
+ exit( 1 );
+ }
+ importexport_encrypt = 1;
+ break;
+#if defined(UPGRADEDB)
+ case 'f': /* upgradedb only */
+ if ( slapd_exemode != SLAPD_EXEMODE_UPGRADEDB ) {
+ usage( myname, *extraname );
+ exit( 1 );
+ }
+ upgradedb_force = SLAPI_UPGRADEDB_FORCE;
+ break;
+#endif
+ case '1': /* db2ldif only */
+ if ( slapd_exemode != SLAPD_EXEMODE_DB2LDIF ) {
+ usage( myname, *extraname );
+ exit( 1 );
+ }
+
+ /*
+ * do not output "version: 1" to the ldif file
+ */
+ ldif_printkey |= EXPORT_NOVERSION;
+
+ break;
+ default:
+ usage( myname, *extraname );
+ exit( 1 );
+ }
+ }
+
+ if ((NULL != cmd_line_instance_names)
+ && (NULL != cmd_line_instance_names[1])
+ && (ldif_printkey & EXPORT_APPENDMODE))
+ {
+ fprintf(stderr, "WARNING: several backends are being"
+ " exported to a single ldif file\n");
+ fprintf(stderr, " use option -M to export to"
+ " multiple ldif files\n");
+ }
+ /* Any leftover arguments? */
+ if ( optind_last > optind ) {
+ usage( myname, *extraname );
+ exit( 1 );
+ }
+
+ return;
+}
+
+static int
+lookup_instance_name_by_suffix(char *suffix,
+ char ***suffixes, char ***instances, int isexact)
+{
+ Slapi_PBlock *pb = slapi_pblock_new();
+ Slapi_Entry **entries = NULL, **ep;
+ char *query;
+ char *backend;
+ char *fullsuffix;
+ int rval = -1;
+
+ if (pb == NULL)
+ goto done;
+
+ query = slapi_ch_malloc(strlen((const char *)suffix) + 80); /* round up */
+
+ if (query == NULL)
+ goto done;
+
+ if (isexact)
+ sprintf(query, "(&(objectclass=nsmappingtree)(|(cn=\"%s\")(cn=%s)))", suffix, suffix);
+ else
+ sprintf(query, "(&(objectclass=nsmappingtree)(|(cn=*%s\")(cn=*%s)))", suffix, suffix);
+
+ slapi_search_internal_set_pb(pb, "cn=mapping tree,cn=config",
+ LDAP_SCOPE_SUBTREE, query, NULL, 0, NULL, NULL,
+ (void *)plugin_get_default_component_id(), 0);
+ slapi_search_internal_pb(pb);
+ slapi_ch_free((void **)&query);
+
+ slapi_pblock_get(pb, SLAPI_PLUGIN_INTOP_RESULT, &rval);
+ if (rval != LDAP_SUCCESS)
+ goto done;
+
+ slapi_pblock_get(pb, SLAPI_PLUGIN_INTOP_SEARCH_ENTRIES, &entries);
+ if ((entries == NULL) || (entries[0] == NULL))
+ goto done;
+
+ rval = 0;
+ for (ep = entries; *ep; ep++) {
+ backend = slapi_entry_attr_get_charptr(*ep, "nsslapd-backend");
+ if (backend) {
+ charray_add(instances, backend);
+ if (suffixes) {
+ fullsuffix = slapi_entry_attr_get_charptr(*ep, "cn");
+ charray_add(suffixes, fullsuffix); /* NULL is ok */
+ }
+ }
+ }
+
+done:
+ slapi_free_search_results_internal(pb);
+ slapi_pblock_destroy(pb);
+ return rval;
+}
+
+int
+lookup_instance_name_by_suffixes(char **included, char **excluded,
+ char ***instances)
+{
+ char **incl_instances, **excl_instances;
+ char **p;
+ int rval = -1;
+
+ incl_instances = NULL;
+ for (p = included; p && *p; p++) {
+ if (lookup_instance_name_by_suffix(*p, NULL, &incl_instances, 0) < 0)
+ return rval;
+ }
+
+ excl_instances = NULL;
+ for (p = excluded; p && *p; p++) {
+ if (lookup_instance_name_by_suffix(*p, NULL, &excl_instances, 0) < 0)
+ return rval;
+ }
+
+ rval = 0;
+ charray_subtract(incl_instances, excl_instances, NULL);
+ charray_free(excl_instances);
+ *instances = incl_instances;
+ return rval;
+}
+
+/* helper function for ldif2db & friends -- given an instance name, lookup
+ * the plugin name in the DSE. this assumes the DSE has already been loaded.
+ */
+static struct slapdplugin *lookup_plugin_by_instance_name(const char *name)
+{
+ Slapi_Entry **entries = NULL;
+ Slapi_PBlock *pb = slapi_pblock_new();
+ struct slapdplugin *plugin;
+ char *query, *dn, *cn;
+ int ret = 0;
+
+ if (pb == NULL)
+ return NULL;
+
+ query = slapi_ch_malloc(strlen(name) + 80); /* round up */
+ if (query == NULL) {
+ slapi_pblock_destroy(pb);
+ return NULL;
+ }
+ sprintf(query, "(&(cn=%s)(objectclass=nsBackendInstance))", name);
+
+ slapi_search_internal_set_pb(pb, "cn=plugins,cn=config",
+ LDAP_SCOPE_SUBTREE, query, NULL, 0, NULL, NULL,
+ (void *)plugin_get_default_component_id(), 0);
+ slapi_search_internal_pb(pb);
+ slapi_ch_free((void **)&query);
+
+ slapi_pblock_get(pb, SLAPI_PLUGIN_INTOP_RESULT, &ret);
+ if (ret != LDAP_SUCCESS) {
+ slapi_free_search_results_internal(pb);
+ slapi_pblock_destroy(pb);
+ return NULL;
+ }
+
+ slapi_pblock_get(pb, SLAPI_PLUGIN_INTOP_SEARCH_ENTRIES, &entries);
+ if ((entries == NULL) || (entries[0] == NULL)) {
+ slapi_free_search_results_internal(pb);
+ slapi_pblock_destroy(pb);
+ return NULL;
+ }
+
+ /* okay -- have the entry for this instance, now let's chop up the dn */
+ /* parent dn is the plugin */
+ dn = slapi_dn_parent(slapi_entry_get_dn(entries[0]));
+
+ /* clean up */
+ slapi_free_search_results_internal(pb);
+ entries = NULL;
+ slapi_pblock_destroy(pb);
+ pb = NULL; /* this seems redundant . . . until we add code after this line */
+
+ /* now... look up the parent */
+ pb = slapi_pblock_new();
+ slapi_search_internal_set_pb(pb, dn, LDAP_SCOPE_BASE,
+ "(objectclass=nsSlapdPlugin)", NULL, 0, NULL, NULL,
+ (void *)plugin_get_default_component_id(), 0);
+ slapi_search_internal_pb(pb);
+ slapi_ch_free((void **)&dn);
+
+ slapi_pblock_get(pb, SLAPI_PLUGIN_INTOP_RESULT, &ret);
+ if (ret != LDAP_SUCCESS) {
+ slapi_free_search_results_internal(pb);
+ slapi_pblock_destroy(pb);
+ return NULL;
+ }
+ slapi_pblock_get(pb, SLAPI_PLUGIN_INTOP_SEARCH_ENTRIES, &entries);
+ if ((entries == NULL) || (entries[0] == NULL)) {
+ slapi_free_search_results_internal(pb);
+ slapi_pblock_destroy(pb);
+ return NULL;
+ }
+
+ cn = slapi_entry_attr_get_charptr(entries[0], "cn");
+ slapi_free_search_results_internal(pb);
+ slapi_pblock_destroy(pb);
+
+ plugin = plugin_get_by_name(cn);
+ slapi_ch_free((void **)&cn);
+
+ return plugin;
+}
+
+static int
+slapd_exemode_ldif2db()
+{
+ int return_value= 0;
+ Slapi_PBlock pb;
+ struct slapdplugin *plugin;
+ slapdFrontendConfig_t *slapdFrontendConfig = getFrontendConfig();
+
+ if ( ldif_file == NULL ) {
+ LDAPDebug( LDAP_DEBUG_ANY,
+ "ERROR: Required argument -i <ldiffile> missing\n",
+ 0, 0, 0 );
+ usage( myname, extraname );
+ exit( 1 );
+ }
+
+ /* this should be the first time this are called! if the init order
+ * is ever changed, these lines should be changed (or erased)!
+ */
+ mapping_tree_init();
+
+ /*
+ * if instance is given, just use it to get the backend.
+ * otherwise, we use included/excluded suffix list to specify a backend.
+ */
+ if (NULL == cmd_line_instance_name) {
+ char **instances, **ip;
+ int counter;
+
+ if (lookup_instance_name_by_suffixes(db2ldif_include, db2ldif_exclude,
+ &instances) < 0) {
+ LDAPDebug(LDAP_DEBUG_ANY,
+ "ERROR: backend instances name [-n <name>] or "
+ "included suffix [-s <suffix>] need to be specified.\n",
+ 0, 0, 0);
+ exit(1);
+ }
+
+ if (instances) {
+ for (ip = instances, counter = 0; ip && *ip; ip++, counter++)
+ ;
+
+ if (counter == 0) {
+ LDAPDebug(LDAP_DEBUG_ANY,
+ "ERROR 1: There is no backend instance to import to.\n",
+ 0, 0, 0);
+ exit(1);
+ } else if (counter > 1) {
+ int i;
+ LDAPDebug(LDAP_DEBUG_ANY,
+ "ERROR: There are multiple backend instances specified:\n",
+ 0, 0, 0);
+ for (i = 0; i < counter; i++)
+ LDAPDebug(LDAP_DEBUG_ANY, " : %s\n",
+ instances[i], 0, 0);
+ exit(1);
+ } else {
+ LDAPDebug(LDAP_DEBUG_ANY, "Backend Instance: %s\n",
+ *instances, 0, 0);
+ cmd_line_instance_name = *instances;
+ }
+ } else {
+ LDAPDebug(LDAP_DEBUG_ANY,
+ "ERROR 2: There is no backend instance to import to.\n",
+ 0, 0, 0);
+ exit(1);
+ }
+ }
+
+ plugin = lookup_plugin_by_instance_name(cmd_line_instance_name);
+ if (plugin == NULL) {
+ LDAPDebug(LDAP_DEBUG_ANY,
+ "ERROR: Could not find backend '%s'.\n",
+ cmd_line_instance_name, 0, 0);
+ exit(1);
+ }
+
+ /* Make sure we aren't going to run slapd in
+ * a mode that is going to conflict with other
+ * slapd processes that are currently running
+ */
+ if ( add_new_slapd_process(slapd_exemode, db2ldif_dump_replica,
+ skip_db_protect_check) == -1 ) {
+
+ LDAPDebug( LDAP_DEBUG_ANY,
+ "Shutting down due to possible conflicts with other slapd processes\n",
+ 0, 0, 0 );
+ exit(1);
+ }
+ /* check for slapi v2 support */
+ if (! SLAPI_PLUGIN_IS_V2(plugin)) {
+ LDAPDebug(LDAP_DEBUG_ANY, "ERROR: %s is too old to do imports.\n",
+ plugin->plg_name, 0, 0);
+ exit(1);
+ }
+
+ memset( &pb, '\0', sizeof(pb) );
+ pb.pb_backend = NULL;
+ pb.pb_plugin = plugin;
+ pb.pb_removedupvals = ldif2db_removedupvals;
+ pb.pb_ldif2db_noattrindexes = ldif2db_noattrindexes;
+ pb.pb_ldif_generate_uniqueid = ldif2db_generate_uniqueid;
+ pb.pb_ldif_namespaceid = ldif2db_namespaceid;
+ pb.pb_ldif_encrypt = importexport_encrypt;
+/* pb.pb_ldif_load_state = ldif2db_load_state; */
+ pb.pb_instance_name = cmd_line_instance_name;
+ pb.pb_ldif_files = ldif_file;
+ pb.pb_ldif_include = db2ldif_include;
+ pb.pb_ldif_exclude = db2ldif_exclude;
+ pb.pb_task_flags = TASK_RUNNING_FROM_COMMANDLINE;
+#ifndef _WIN32
+ main_setuid(slapdFrontendConfig->localuser);
+#endif
+ if ( plugin->plg_ldif2db != NULL ) {
+ return_value = (*plugin->plg_ldif2db)( &pb );
+ } else {
+ LDAPDebug( LDAP_DEBUG_ANY,
+ "ERROR: no ldif2db function defined for "
+ "%s\n", plugin->plg_name, 0, 0 );
+ return_value = -1;
+ }
+ slapi_ch_free((void**)&myname );
+ charray_free( db2index_attrs );
+ charray_free(ldif_file);
+ return( return_value );
+}
+
+static int
+slapd_exemode_db2ldif(int argc, char** argv)
+{
+ int return_value= 0;
+ Slapi_PBlock pb;
+ struct slapdplugin *plugin;
+ slapdFrontendConfig_t *slapdFrontendConfig = getFrontendConfig();
+ char *my_ldiffile;
+ char **instp;
+
+ /* this should be the first time this are called! if the init order
+ * is ever changed, these lines should be changed (or erased)!
+ */
+ mapping_tree_init();
+
+ /*
+ * if instance is given, just pass it to the backend.
+ * otherwise, we use included/excluded suffix list to specify a backend.
+ */
+ if (NULL == cmd_line_instance_names) {
+ char **instances, **ip;
+ int counter;
+
+ if (lookup_instance_name_by_suffixes(db2ldif_include, db2ldif_exclude,
+ &instances) < 0) {
+ LDAPDebug(LDAP_DEBUG_ANY,
+ "ERROR: backend instances name [-n <name>] or "
+ "included suffix [-s <suffix>] need to be specified.\n",
+ 0, 0, 0);
+ exit(1);
+ }
+
+ if (instances) {
+ for (ip = instances, counter = 0; ip && *ip; ip++, counter++)
+ ;
+
+ if (counter == 0) {
+ LDAPDebug(LDAP_DEBUG_ANY,
+ "ERROR 1: There is no backend instance to export from.\n",
+ 0, 0, 0);
+ exit(1);
+ } else {
+ LDAPDebug(LDAP_DEBUG_ANY, "Backend Instance: %s\n",
+ *instances, 0, 0);
+ cmd_line_instance_names = instances;
+ }
+ } else {
+ LDAPDebug(LDAP_DEBUG_ANY,
+ "ERROR 2: There is no backend instance to export from.\n",
+ 0, 0, 0);
+ exit(1);
+ }
+ }
+
+#ifndef _WIN32
+ /* [622984] db2lidf -r changes database file ownership
+ * should call setuid before "db2ldif_dump_replica" */
+ main_setuid(slapdFrontendConfig->localuser);
+#endif
+ for (instp = cmd_line_instance_names; instp && *instp; instp++) {
+ int release_me = 0;
+
+ plugin = lookup_plugin_by_instance_name(*instp);
+ if (plugin == NULL) {
+ LDAPDebug(LDAP_DEBUG_ANY,
+ "ERROR: Could not find backend '%s'.\n",
+ *instp, 0, 0);
+ exit(1);
+ }
+
+ if (plugin->plg_db2ldif == NULL) {
+ LDAPDebug(LDAP_DEBUG_ANY, "ERROR: no db2ldif function defined for "
+ "backend %s - cannot export\n", *instp, 0, 0);
+ exit(1);
+ }
+
+ /* Make sure we aren't going to run slapd in
+ * a mode that is going to conflict with other
+ * slapd processes that are currently running
+ */
+ if ( add_new_slapd_process(slapd_exemode, db2ldif_dump_replica,
+ skip_db_protect_check) == -1 ) {
+ LDAPDebug( LDAP_DEBUG_ANY,
+ "Shutting down due to possible conflicts "
+ "with other slapd processes\n",
+ 0, 0, 0 );
+ exit(1);
+ }
+ if ( config_is_slapd_lite () &&
+ !slapi_config_get_readonly () && is_slapd_running() ) {
+ LDAPDebug( LDAP_DEBUG_ANY, "%s\n", LITE_BACKUP_ERR, 0, 0);
+ exit ( 1 );
+ }
+
+ if (! (SLAPI_PLUGIN_IS_V2(plugin))) {
+ LDAPDebug(LDAP_DEBUG_ANY, "ERROR: %s is too old to do exports.\n",
+ plugin->plg_name, 0, 0);
+ exit(1);
+ }
+
+ memset( &pb, '\0', sizeof(pb) );
+ pb.pb_backend = NULL;
+ pb.pb_plugin = plugin;
+ pb.pb_ldif_include = db2ldif_include;
+ pb.pb_ldif_exclude = db2ldif_exclude;
+ pb.pb_ldif_dump_replica = db2ldif_dump_replica;
+ pb.pb_ldif_dump_uniqueid = db2ldif_dump_uniqueid;
+ pb.pb_ldif_encrypt = importexport_encrypt;
+ pb.pb_instance_name = *instp;
+ pb.pb_task_flags = TASK_RUNNING_FROM_COMMANDLINE;
+ if (is_slapd_running())
+ pb.pb_server_running = 1;
+ else
+ pb.pb_server_running = 0;
+
+ if (db2ldif_dump_replica) {
+ eq_init(); /* must be done before plugins started */
+ ps_init_psearch_system(); /* must come before plugin_startall() */
+ plugin_startall(argc, argv, 1 /* Start Backends */,
+ 1 /* Start Globals */);
+ eq_start(); /* must be done after plugins started */
+ }
+
+ pb.pb_ldif_file = NULL;
+ if ( archive_name ) { /* redirect stdout to this file: */
+ char *p, *q;
+#if defined( XP_WIN32 )
+ char sep = '\\';
+ if (NULL != strchr(archive_name, '/'))
+ sep = '/';
+#else
+ char sep = '/';
+#endif
+ my_ldiffile = archive_name;
+ if (ldif_printkey & EXPORT_APPENDMODE) {
+ if (instp == cmd_line_instance_names) { /* first export */
+ ldif_printkey |= EXPORT_APPENDMODE_1;
+ } else {
+ ldif_printkey &= ~EXPORT_APPENDMODE_1;
+ }
+ } else { /* not APPENDMODE */
+ if (strcmp(archive_name, "-")) { /* not '-' */
+ my_ldiffile =
+ (char *)slapi_ch_malloc((unsigned long)(strlen(archive_name)
+ + strlen(*instp) + 2));
+ p = strrchr(archive_name, sep);
+ if (NULL == p) {
+ sprintf(my_ldiffile, "%s_%s", *instp, archive_name);
+ } else {
+ q = p + 1;
+ *p = '\0';
+ sprintf(my_ldiffile, "%s%c%s_%s",
+ archive_name, sep, *instp, q);
+ *p = sep;
+ }
+ release_me = 1;
+ }
+ }
+
+ fprintf(stderr, "ldiffile: %s\n", my_ldiffile);
+ /* just send the filename to the backend and let
+ * the backend open it (so they can do special
+ * stuff for 64-bit fs)
+ */
+ pb.pb_ldif_file = my_ldiffile;
+ pb.pb_ldif_printkey = ldif_printkey;
+ }
+
+ return_value = (plugin->plg_db2ldif)( &pb );
+
+ if (release_me) {
+ slapi_ch_free((void **)&my_ldiffile);
+ }
+ }
+ slapi_ch_free( (void**)&myname );
+ if (db2ldif_dump_replica) {
+ plugin_closeall( 1 /* Close Backends */, 1 /* Close Globals */);
+ }
+ return( return_value );
+}
+
+static int
+slapd_exemode_suffix2instance()
+{
+ int return_value = 0;
+ char **instances = NULL;
+ char **suffixes = NULL;
+ char **p, **q, **r;
+
+ /* this should be the first time this are called! if the init order
+ * is ever changed, these lines should be changed (or erased)!
+ */
+ mapping_tree_init();
+
+ for (p = db2ldif_include; p && *p; p++) {
+ if (lookup_instance_name_by_suffix(*p, &suffixes, &instances, 0) < 0)
+ continue;
+ fprintf(stderr, "Suffix, Instance name pair(s) under \"%s\":\n", *p);
+ if (instances)
+ for (q = suffixes, r = instances; *r; q++, r++)
+ fprintf(stderr, "\tsuffix %s; instance name \"%s\"\n",
+ *q?*q:"-", *r);
+ else
+ fprintf(stderr, "\tNo instance\n");
+ charray_free(suffixes);
+ suffixes = NULL;
+ charray_free(instances);
+ instances = NULL;
+ }
+ return (return_value);
+}
+
+static int slapd_exemode_db2index()
+{
+ int return_value= 0;
+ struct slapdplugin *plugin;
+ Slapi_PBlock pb;
+ slapdFrontendConfig_t *slapdFrontendConfig = getFrontendConfig();
+
+ mapping_tree_init();
+
+ /*
+ * if instance is given, just use it to get the backend.
+ * otherwise, we use included/excluded suffix list to specify a backend.
+ */
+ if (NULL == cmd_line_instance_name) {
+ char **instances, **ip;
+ int counter;
+
+ if (lookup_instance_name_by_suffixes(db2ldif_include, db2ldif_exclude,
+ &instances) < 0) {
+ LDAPDebug(LDAP_DEBUG_ANY,
+ "ERROR: backend instances name [-n <name>] or "
+ "included suffix [-s <suffix>] need to be specified.\n",
+ 0, 0, 0);
+ exit(1);
+ }
+
+ if (instances) {
+ for (ip = instances, counter = 0; ip && *ip; ip++, counter++)
+ ;
+
+ if (counter == 0) {
+ LDAPDebug(LDAP_DEBUG_ANY,
+ "ERROR 1: There is no backend instance to import to.\n",
+ 0, 0, 0);
+ exit(1);
+ } else if (counter > 1) {
+ int i;
+ LDAPDebug(LDAP_DEBUG_ANY,
+ "ERROR: There are multiple backend instances specified:\n",
+ 0, 0, 0);
+ for (i = 0; i < counter; i++)
+ LDAPDebug(LDAP_DEBUG_ANY, " : %s\n",
+ instances[i], 0, 0);
+ exit(1);
+ } else {
+ LDAPDebug(LDAP_DEBUG_ANY, "Backend Instance: %s\n",
+ *instances, 0, 0);
+ cmd_line_instance_name = *instances;
+ }
+ } else {
+ LDAPDebug(LDAP_DEBUG_ANY,
+ "ERROR 2: There is no backend instance to import to.\n",
+ 0, 0, 0);
+ exit(1);
+ }
+ }
+
+ plugin = lookup_plugin_by_instance_name(cmd_line_instance_name);
+ if (plugin == NULL) {
+ LDAPDebug(LDAP_DEBUG_ANY,
+ "ERROR: Could not find backend '%s'.\n",
+ cmd_line_instance_name, 0, 0);
+ exit(1);
+ }
+
+ /* make sure nothing else is running */
+ if (add_new_slapd_process(slapd_exemode, db2ldif_dump_replica,
+ skip_db_protect_check) == -1) {
+ LDAPDebug(LDAP_DEBUG_ANY,
+ "Shutting down due to possible conflicts with other "
+ "slapd processes.\n", 0, 0, 0);
+ exit(1);
+ }
+
+ if ( db2index_attrs == NULL ) {
+ usage( myname, extraname );
+ exit( 1 );
+ }
+ memset( &pb, '\0', sizeof(pb) );
+ pb.pb_backend = NULL;
+ pb.pb_plugin = plugin;
+ pb.pb_db2index_attrs = db2index_attrs;
+ pb.pb_instance_name = cmd_line_instance_name;
+ pb.pb_task_flags = TASK_RUNNING_FROM_COMMANDLINE;
+#ifndef _WIN32
+ main_setuid(slapdFrontendConfig->localuser);
+#endif
+ return_value = (*plugin->plg_db2index)( &pb );
+
+ slapi_ch_free( (void**)&myname );
+ return( return_value );
+}
+
+
+static int
+slapd_exemode_db2archive()
+{
+ int return_value= 0;
+ Slapi_Backend *be = NULL;
+ Slapi_PBlock pb;
+ struct slapdplugin *backend_plugin;
+ slapdFrontendConfig_t *slapdFrontendConfig = getFrontendConfig();
+
+ if ((backend_plugin = plugin_get_by_name("ldbm database")) == NULL) {
+ LDAPDebug(LDAP_DEBUG_ANY,
+ "ERROR: Could not find the ldbm backend plugin.\n",
+ 0, 0, 0);
+ exit(1);
+ }
+ if (NULL == archive_name) {
+ LDAPDebug( LDAP_DEBUG_ANY,
+ "ERROR: no archive directory supplied\n",
+ 0, 0, 0 );
+ exit( 1 );
+ }
+
+ if ( config_is_slapd_lite () && !slapi_config_get_readonly () && is_slapd_running ()) {
+ LDAPDebug( LDAP_DEBUG_ANY, "%s\n", LITE_BACKUP_ERR, 0, 0);
+ exit ( 1 );
+ }
+
+ /* Make sure we aren't going to run slapd in
+ * a mode that is going to conflict with other
+ * slapd processes that are currently running
+ */
+ if ( add_new_slapd_process(slapd_exemode, db2ldif_dump_replica,
+ skip_db_protect_check) == -1 ) {
+ LDAPDebug( LDAP_DEBUG_ANY,
+ "Shutting down due to possible conflicts with other slapd processes\n",
+ 0, 0, 0 );
+ exit(1);
+ }
+ if (compute_init()) {
+ LDAPDebug(LDAP_DEBUG_ANY, "Initialization Failed 0 %d\n",return_value,0,0);
+ exit (1);
+ }
+
+ memset( &pb, '\0', sizeof(pb) );
+ pb.pb_backend = NULL;
+ pb.pb_plugin = backend_plugin;
+ pb.pb_instance_name = cmd_line_instance_name;
+ pb.pb_seq_val = archive_name;
+ pb.pb_task_flags = TASK_RUNNING_FROM_COMMANDLINE;
+#ifndef _WIN32
+ main_setuid(slapdFrontendConfig->localuser);
+#endif
+ return_value = (backend_plugin->plg_db2archive)( &pb );
+ return return_value;
+}
+
+static int
+slapd_exemode_archive2db()
+{
+ int return_value= 0;
+ Slapi_Backend *be = NULL;
+ Slapi_PBlock pb;
+ struct slapdplugin *backend_plugin;
+ slapdFrontendConfig_t *slapdFrontendConfig = getFrontendConfig();
+
+ if ((backend_plugin = plugin_get_by_name("ldbm database")) == NULL) {
+ LDAPDebug(LDAP_DEBUG_ANY,
+ "ERROR: Could not find the ldbm backend plugin.\n",
+ 0, 0, 0);
+ exit(1);
+ }
+ if (NULL == archive_name) {
+ LDAPDebug( LDAP_DEBUG_ANY,
+ "ERROR: no archive directory supplied\n",
+ 0, 0, 0 );
+ exit( 1 );
+ }
+
+ /* Make sure we aren't going to run slapd in
+ * a mode that is going to conflict with other
+ * slapd processes that are currently running
+ */
+ if ( add_new_slapd_process(slapd_exemode, db2ldif_dump_replica,
+ skip_db_protect_check) == -1 ) {
+ LDAPDebug( LDAP_DEBUG_ANY,
+ "Shutting down due to possible conflicts with other slapd processes\n",
+ 0, 0, 0 );
+ exit(1);
+ }
+ if (compute_init()) {
+ LDAPDebug(LDAP_DEBUG_ANY, "Initialization Failed 0 %d\n",return_value,0,0);
+ exit (1);
+ }
+
+ memset( &pb, '\0', sizeof(pb) );
+ pb.pb_backend = NULL;
+ pb.pb_plugin = backend_plugin;
+ pb.pb_instance_name = cmd_line_instance_name;
+ pb.pb_seq_val = archive_name;
+ pb.pb_task_flags = TASK_RUNNING_FROM_COMMANDLINE;
+#ifndef _WIN32
+ main_setuid(slapdFrontendConfig->localuser);
+#endif
+ return_value = (backend_plugin->plg_archive2db)( &pb );
+ return return_value;
+}
+
+#if defined(UPGRADEDB)
+/*
+ * functions to convert idl from the old format to the new one
+ * (604921) Support a database uprev process any time post-install
+ */
+static int
+slapd_exemode_upgradedb()
+{
+ int return_value= 0;
+ Slapi_PBlock pb;
+ struct slapdplugin *backend_plugin;
+ slapdFrontendConfig_t *slapdFrontendConfig = getFrontendConfig();
+
+ if ( archive_name == NULL ) {
+ LDAPDebug( LDAP_DEBUG_ANY,
+ "ERROR: Required argument -a <backup_dir> missing\n",
+ 0, 0, 0 );
+ usage( myname, extraname );
+ exit( 1 );
+ }
+
+ /* this should be the first time this are called! if the init order
+ * is ever changed, these lines should be changed (or erased)!
+ */
+ mapping_tree_init();
+
+ if ((backend_plugin = plugin_get_by_name("ldbm database")) == NULL) {
+ LDAPDebug(LDAP_DEBUG_ANY,
+ "ERROR: Could not find the ldbm backend plugin.\n",
+ 0, 0, 0);
+ exit(1);
+ }
+
+ /* Make sure we aren't going to run slapd in
+ * a mode that is going to conflict with other
+ * slapd processes that are currently running
+ */
+ if (add_new_slapd_process(slapd_exemode, 0, skip_db_protect_check) == -1) {
+ LDAPDebug( LDAP_DEBUG_ANY,
+ "Shutting down due to possible conflicts with other slapd processes\n",
+ 0, 0, 0 );
+ exit(1);
+ }
+ /* check for slapi v2 support */
+ if (! SLAPI_PLUGIN_IS_V2(backend_plugin)) {
+ LDAPDebug(LDAP_DEBUG_ANY, "ERROR: %s is too old to do convert idl.\n",
+ backend_plugin->plg_name, 0, 0);
+ exit(1);
+ }
+
+ memset( &pb, '\0', sizeof(pb) );
+ pb.pb_backend = NULL;
+ pb.pb_plugin = backend_plugin;
+ pb.pb_seq_val = archive_name;
+ pb.pb_seq_type = upgradedb_force;
+ pb.pb_task_flags = TASK_RUNNING_FROM_COMMANDLINE;
+ /* borrowing import code, so need to set up the import variables */
+ pb.pb_ldif_generate_uniqueid = ldif2db_generate_uniqueid;
+ pb.pb_ldif_namespaceid = ldif2db_namespaceid;
+ pb.pb_ldif2db_noattrindexes = 0;
+ pb.pb_removedupvals = 0;
+#ifndef _WIN32
+ main_setuid(slapdFrontendConfig->localuser);
+#endif
+ if ( backend_plugin->plg_upgradedb != NULL ) {
+ return_value = (*backend_plugin->plg_upgradedb)( &pb );
+ } else {
+ LDAPDebug( LDAP_DEBUG_ANY,
+ "ERROR: no upgradedb function defined for "
+ "%s\n", backend_plugin->plg_name, 0, 0 );
+ return_value = -1;
+ }
+ slapi_ch_free((void**)&myname );
+ return( return_value );
+}
+#endif
+
+
+static int
+slapd_exemode_dbtest()
+{
+ int return_value= 0;
+ Slapi_PBlock pb;
+ struct slapdplugin *plugin;
+
+ if (NULL == cmd_line_instance_name) {
+ LDAPDebug(LDAP_DEBUG_ANY,
+ "dbtest: Required argument -n <instance name> missing\n", 0, 0, 0);
+ usage( myname, extraname );
+ exit(1);
+ }
+
+ mapping_tree_init();
+
+ plugin = lookup_plugin_by_instance_name(cmd_line_instance_name);
+ if (plugin == NULL) {
+ LDAPDebug(LDAP_DEBUG_ANY,
+ "ERROR: Could not find backend '%s'.\n",
+ cmd_line_instance_name, 0, 0);
+ exit(1);
+ }
+
+ /* Make sure we aren't going to run slapd in
+ * a mode that is going to conflict with other
+ * slapd processes that are currently running
+ */
+ if ( add_new_slapd_process(slapd_exemode, db2ldif_dump_replica,
+ skip_db_protect_check) == -1 ) {
+
+ LDAPDebug( LDAP_DEBUG_ANY,
+ "Shutting down due to possible conflicts with other slapd processes\n",
+ 0, 0, 0 );
+ exit(1);
+ }
+
+ pb.pb_backend = NULL;
+ pb.pb_plugin = plugin;
+ pb.pb_instance_name = cmd_line_instance_name;
+ /* For dbtest, we do _not_ change identity (no setuid()) */
+ return_value= (*plugin->plg_dbtest)( &pb );
+ return return_value;
+}
+
+
+
+#ifdef LDAP_DEBUG
+/*
+ * Table to associate a string with a debug level.
+ */
+static struct slapd_debug_level_entry {
+ int dle_level; /* LDAP_DEBUG_XXX value */
+ const char *dle_string; /* string equivalent; NULL marks end of list */
+ char dle_hide;
+} slapd_debug_level_map[] = {
+ { LDAP_DEBUG_TRACE, "trace", 0 },
+ { LDAP_DEBUG_PACKETS, "packets", 0 },
+ { LDAP_DEBUG_ARGS, "arguments", 0 },
+ { LDAP_DEBUG_ARGS, "args", 1 },
+ { LDAP_DEBUG_CONNS, "connections", 0 },
+ { LDAP_DEBUG_CONNS, "conn", 1 },
+ { LDAP_DEBUG_CONNS, "conns", 1 },
+ { LDAP_DEBUG_BER, "ber", 0 },
+ { LDAP_DEBUG_FILTER, "filters", 0 },
+ { LDAP_DEBUG_CONFIG, "config", 0 },
+ { LDAP_DEBUG_ACL, "accesscontrol", 0 },
+ { LDAP_DEBUG_ACL, "acl", 1 },
+ { LDAP_DEBUG_ACL, "acls", 1 },
+ { LDAP_DEBUG_STATS, "stats", 0 },
+ { LDAP_DEBUG_STATS2, "stats2", 0 },
+ { LDAP_DEBUG_SHELL, "shell", 1 },
+ { LDAP_DEBUG_PARSE, "parsing", 0 },
+ { LDAP_DEBUG_HOUSE, "housekeeping", 0 },
+ { LDAP_DEBUG_REPL, "replication", 0 },
+ { LDAP_DEBUG_REPL, "repl", 1 },
+ { LDAP_DEBUG_ANY, "errors", 0 },
+ { LDAP_DEBUG_ANY, "ANY", 1 },
+ { LDAP_DEBUG_ANY, "error", 1 },
+ { LDAP_DEBUG_CACHE, "caches", 0 },
+ { LDAP_DEBUG_CACHE, "cache", 1 },
+ { LDAP_DEBUG_PLUGIN, "plugins", 0 },
+ { LDAP_DEBUG_PLUGIN, "plugin", 1 },
+ { LDAP_DEBUG_TIMING, "timing", 0 },
+ { LDAP_DEBUG_ACLSUMMARY,"accesscontrolsummary", 0 },
+ { LDAP_DEBUG_ALL_LEVELS,"ALL", 0 },
+ { 0, NULL, 0 }
+};
+
+
+
+/*
+ * Given a string represention of a debug level, map it to a integer value
+ * and return that value. -1 is returned upon error, with a message
+ * printed to stderr.
+ */
+static int
+slapd_debug_level_string2level( const char *s )
+{
+ int level, i;
+ char *cur, *next, *scopy;
+
+ level = 0;
+ cur = scopy = slapi_ch_strdup( s );
+
+ for ( cur = scopy; cur != NULL; cur = next ) {
+ if (( next = strchr( cur, '+' )) != NULL ) {
+ *next++ = '\0';
+ }
+
+ if ( isdigit( *cur )) {
+ level |= atoi( cur );
+ } else {
+ for ( i = 0; NULL != slapd_debug_level_map[i].dle_string; ++i ) {
+ if ( strcasecmp( cur, slapd_debug_level_map[i].dle_string )
+ == 0 ) {
+ level |= slapd_debug_level_map[i].dle_level;
+ break;
+ }
+ }
+
+ if ( NULL == slapd_debug_level_map[i].dle_string ) {
+ fprintf( stderr, "Unrecognized debug level \"%s\"\n", cur );
+ return -1;
+ }
+ }
+ }
+
+ slapi_ch_free( (void **)&scopy );
+
+ return level;
+}
+
+
+/*
+ * Print to stderr the string equivalent of level.
+ * The ANY level is omitted because it is always present.
+ */
+static void
+slapd_debug_level_log( int level )
+{
+ int i, count, len;
+ char *msg, *p;
+
+ level &= ~LDAP_DEBUG_ANY;
+
+ /* first pass: determine space needed for the debug level string */
+ len = 1; /* room for '\0' terminator */
+ count = 0;
+ for ( i = 0; NULL != slapd_debug_level_map[i].dle_string; ++i ) {
+ if ( !slapd_debug_level_map[i].dle_hide &&
+ slapd_debug_level_map[i].dle_level != LDAP_DEBUG_ALL_LEVELS
+ && 0 != ( level & slapd_debug_level_map[i].dle_level )) {
+ if ( count > 0 ) {
+ ++len; /* room for '+' character */
+ }
+ len += strlen( slapd_debug_level_map[i].dle_string );
+ ++count;
+ }
+ }
+
+ /* second pass: construct the debug level string */
+ p = msg = slapi_ch_malloc( len );
+ count = 0;
+ for ( i = 0; NULL != slapd_debug_level_map[i].dle_string; ++i ) {
+ if ( !slapd_debug_level_map[i].dle_hide &&
+ slapd_debug_level_map[i].dle_level != LDAP_DEBUG_ALL_LEVELS
+ && 0 != ( level & slapd_debug_level_map[i].dle_level )) {
+ if ( count > 0 ) {
+ *p++ = '+';
+ }
+ strcpy( p, slapd_debug_level_map[i].dle_string );
+ p += strlen( p );
+ ++count;
+ }
+ }
+
+ slapi_log_error( SLAPI_LOG_FATAL, SLAPD_VERSION_STR,
+ "%s: %s (%d)\n", "debug level", msg, level );
+ slapi_ch_free( (void **)&msg );
+}
+
+
+/*
+ * Display usage/help for the debug level flag (-d)
+ */
+static void
+slapd_debug_level_usage( void )
+{
+ int i;
+
+ fprintf( stderr, "Debug levels:\n" );
+ for ( i = 0; NULL != slapd_debug_level_map[i].dle_string; ++i ) {
+ if ( !slapd_debug_level_map[i].dle_hide
+ && slapd_debug_level_map[i].dle_level
+ != LDAP_DEBUG_ALL_LEVELS) {
+ fprintf( stderr, " %6d - %s%s\n",
+ slapd_debug_level_map[i].dle_level,
+ slapd_debug_level_map[i].dle_string,
+ ( 0 == ( slapd_debug_level_map[i].dle_level &
+ LDAP_DEBUG_ANY )) ? "" :
+ " (always logged)" );
+ }
+ }
+ fprintf( stderr, "To activate multiple levels, add the numeric"
+ " values together or separate the\n"
+ "values with a + character, e.g., all of the following"
+ " have the same effect:\n"
+ " -d connections+filters\n"
+ " -d 8+32\n"
+ " -d 40\n" );
+}
+#endif /* LDAP_DEBUG */
+
diff --git a/ldap/servers/slapd/mapping_tree.c b/ldap/servers/slapd/mapping_tree.c
new file mode 100644
index 00000000..797ea14d
--- /dev/null
+++ b/ldap/servers/slapd/mapping_tree.c
@@ -0,0 +1,3436 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+/* mapping_tree.c - Maps the DIT onto backends and/or referrals. */
+
+
+#include "slap.h"
+
+/* distribution plugin prototype */
+typedef int (* mtn_distrib_fct) (Slapi_PBlock *pb, Slapi_DN * target_dn,
+ char **mtn_be_names, int be_count, Slapi_DN * mtn_node_dn, int *mtn_be_states);
+
+struct mt_node
+{
+ Slapi_DN *mtn_subtree; /* dn for the node's subtree */
+ Slapi_Backend **mtn_be; /* backend pointer - the list of backends implementing this
+ * node usually there is only one back-end here, there can
+ * be several only when distribution is done on this node
+ */
+ int *mtn_be_states; /* states for the backends in table mtn_be */
+ char **mtn_backend_names; /* list of backend names */
+ int mtn_be_list_size; /* size of the previous three tables */
+ int mtn_be_count; /* number of backends implementing this node */
+ char **mtn_referral; /* referral or list of referrals */
+ Slapi_Entry *mtn_referral_entry; /* referral or list of referrals */
+ struct mt_node *mtn_children;
+ struct mt_node *mtn_parent;
+ struct mt_node *mtn_brother; /* list of other nodes with the same parent */
+ int mtn_state;
+ int mtn_private; /* Never show this node to the user. only used for
+ * cn=config, cn=schema and root node */
+ char * mtn_dstr_plg_lib; /* distribution plugin library name */
+ char * mtn_dstr_plg_name; /* distribution plugin function name */
+ mtn_distrib_fct mtn_dstr_plg; /* pointer to the actual ditribution function */
+ void *mtn_extension; /* plugins can extend a mapping tree node */
+};
+
+#define BE_LIST_INIT_SIZE 10
+#define BE_LIST_INCREMENT 10
+
+/* Mapping tree locking strategy
+ *
+ * There are two goals for the mapping tree locking
+ * - protect the mapping tree structures
+ * - prevent stop or re-initialisation of a backend which is currently
+ * used by an LDAP operation
+ * This must be done without preventing parallelisation of LDAP operations
+ *
+ * we use
+ * - one RW lock will be used to protect access to mapping tree structures
+ * accessed throuh mtn_lock(), mtn_unlock(), mtn_wlock()
+ * - one RW lock for each backend. This lock is taken in READ for each
+ * LDAP operation in progree on the backend
+ * and is taken in write for administrative operations like stop or
+ * disable the backend
+ * accessed through slapi_be_Rlock(), slapi_be_Wlock(), slapi_be_Unlock()
+ * - a state flag for each backend : mtn_be_states[]
+ * this state is set to SLAPI_BE_STATE_DELETE or SLAPI_BE_STATE_OFFLINE at the beginning
+ * of stop/disable operation to ensure that no new operation starts
+ * while the backend is stopped/disabled
+ *
+ * The algorithme for LDAP OPERATIONS is :
+ *
+ * lock mapping tree in read mode
+ * get backend
+ * check that backend is not in SLAPI_BE_STATE_DELETE or SLAPI_BE_STATE_OFFLINE state
+ * lock backend in read mode
+ * unlock mapping tree
+ * do LDAP operation
+ * release backend lock
+ *
+ * The algorithme for maintenance operation is
+ * lock mapping tree in write mode
+ * set state to SLAPI_BE_STATE_DELETE or SLAPI_BE_STATE_OFFLINE
+ * unlock mapping tree
+ * get backend lock in write mode
+ * release backend lock
+ *
+ */
+static PRRWLock *myLock; /* global lock on the mapping tree structures */
+
+
+static mapping_tree_node *mapping_tree_root = NULL;
+static int mapping_tree_inited = 0;
+static int mapping_tree_freed = 0;
+static int extension_type = -1; /* type returned from the factory */
+
+/* The different states a mapping tree node can be in. */
+#define MTN_DISABLED 0 /* The server acts like the node isn't there. */
+#define MTN_BACKEND 1 /* This node represents a backend. */
+#define MTN_REFERRAL 2 /* A referral is returned instead of a backend. */
+#define MTN_REFERRAL_ON_UPDATE 3 /* A referral is returned for update operations. */
+#define MTN_CONTAINER 4 /* This node represents a container for backends. */
+
+/* Need to add a modifier flag to the state - such as round robin */
+
+#define MAPPING_TREE_BASE_DN "cn=mapping tree,cn=config"
+#define MAPPING_TREE_PARENT_ATTRIBUTE "nsslapd-parent-suffix"
+
+void mtn_wlock();
+void mtn_lock();
+void mtn_unlock();
+
+static mapping_tree_node * mtn_get_mapping_tree_node_by_entry(
+ mapping_tree_node* node, const Slapi_DN *dn);
+static void mtn_remove_node(mapping_tree_node * node);
+static void mtn_free_node (mapping_tree_node **node);
+static int mtn_get_be_distributed(Slapi_PBlock *pb,
+ mapping_tree_node * target_node, Slapi_DN *target_sdn, int * flag_stop);
+static int mtn_get_be(mapping_tree_node *target_node, Slapi_PBlock *pb,
+ Slapi_Backend **be, int * index, Slapi_Entry **referral, char *errorbuf);
+static mapping_tree_node * mtn_get_next_node(mapping_tree_node * node,
+ mapping_tree_node * node_list, int scope);
+static mapping_tree_node * mtn_get_first_node(mapping_tree_node * node,
+ int scope);
+static mapping_tree_node *
+ get_mapping_tree_node_by_name(mapping_tree_node * node, char * be_name);
+
+#ifdef DEBUG
+static void dump_mapping_tree(mapping_tree_node *parent, int depth);
+#endif
+
+/* structure and static local variable used to store the
+ * list of plugins that have registered to a callback when backend state
+ * change
+ */
+struct mtn_be_ch_list {
+ void * handle;
+ struct mtn_be_ch_list * next;
+ slapi_backend_state_change_fnptr fnct;
+ };
+
+static struct mtn_be_ch_list * mtn_plug_list = NULL;
+
+/* API for registering to a callback when backend state change */
+void slapi_register_backend_state_change(void * handle, slapi_backend_state_change_fnptr funct)
+{
+ struct mtn_be_ch_list * new_be_ch_plg;
+ new_be_ch_plg = (struct mtn_be_ch_list *)
+ slapi_ch_malloc(sizeof(struct mtn_be_ch_list));
+ new_be_ch_plg->next = mtn_plug_list;
+ new_be_ch_plg->handle = handle;
+ new_be_ch_plg->fnct = funct;
+ mtn_plug_list = new_be_ch_plg;
+}
+
+/* To unregister all the state change callbacks registered on the mapping tree */
+int slapi_unregister_backend_state_change_all()
+{
+ struct mtn_be_ch_list *cur_be_ch_plg;
+ while (mtn_plug_list)
+ {
+ cur_be_ch_plg = mtn_plug_list;
+ mtn_plug_list = mtn_plug_list->next;
+ slapi_ch_free((void **) &cur_be_ch_plg);
+ }
+ return 1;
+}
+
+
+int slapi_unregister_backend_state_change(void * handle)
+{
+ struct mtn_be_ch_list * cur_be_ch_plg = mtn_plug_list;
+ struct mtn_be_ch_list * prev_be_ch_plg = mtn_plug_list;
+ while (cur_be_ch_plg)
+ {
+ if (cur_be_ch_plg->handle == handle)
+ {
+ if (cur_be_ch_plg == mtn_plug_list)
+ {
+ mtn_plug_list = mtn_plug_list->next;
+ slapi_ch_free((void **) &cur_be_ch_plg);
+ return 0;
+ }
+ else
+ {
+ prev_be_ch_plg->next = cur_be_ch_plg->next;
+ slapi_ch_free((void **) &cur_be_ch_plg);
+ return 0;
+ }
+ }
+ prev_be_ch_plg = cur_be_ch_plg;
+ cur_be_ch_plg = cur_be_ch_plg->next;
+ }
+ return 1;
+}
+
+void mtn_be_state_change(char * be_name, int old_state, int new_state)
+{
+ struct mtn_be_ch_list * cur_be_ch_plg = mtn_plug_list;
+
+ while (cur_be_ch_plg)
+ {
+ (* (cur_be_ch_plg->fnct))(cur_be_ch_plg->handle, be_name,
+ old_state, new_state);
+ cur_be_ch_plg = cur_be_ch_plg->next;
+ }
+}
+
+
+Slapi_DN* slapi_mtn_get_dn(mapping_tree_node *node)
+{
+ return (node->mtn_subtree);
+}
+
+/* this will turn an array of url into a referral entry */
+static Slapi_Entry *
+referral2entry(char ** url_array, const char *target)
+{
+ int i;
+ struct berval bv0,bv1,*bvals[3];
+ Slapi_Entry *anEntry;
+
+ if (url_array == NULL)
+ return NULL;
+
+ anEntry = slapi_entry_alloc();
+ slapi_entry_set_dn(anEntry,slapi_ch_strdup(target));
+
+ bvals[2]=NULL;
+ bvals[1]=&bv1;
+ bv1.bv_val="referral";
+ bv1.bv_len=strlen(bv1.bv_val);
+ bvals[0]=&bv0;
+ bv0.bv_val="top";
+ bv0.bv_len=strlen(bv0.bv_val);
+ slapi_entry_add_values( anEntry, "objectClass", bvals);
+
+ bvals[1]=NULL;
+ for (i=0; url_array[i]; i++) {
+ bv0.bv_val=url_array[i];
+ bv0.bv_len=strlen(bv0.bv_val);
+ slapi_entry_attr_merge( anEntry, "ref", bvals);
+ }
+ return anEntry;
+}
+
+
+/* mapping tree node extension */
+int
+mapping_tree_get_extension_type ()
+{
+ if(extension_type==-1)
+ {
+ /* The factory is given the name of the object type, in
+ * return for a type handle. Whenever the object is created
+ * or destroyed the factory is called with the handle so
+ * that it may call the constructors or destructors registered
+ * with it.
+ */
+ extension_type= factory_register_type(SLAPI_EXT_MTNODE,
+ offsetof(mapping_tree_node, mtn_extension));
+ }
+ return extension_type;
+}
+
+static mapping_tree_node *
+mapping_tree_node_new(Slapi_DN *dn, Slapi_Backend **be, char **backend_names, int *be_states,
+ int count, int size,
+ char **referral, mapping_tree_node *parent,
+ int state, int private, char *plg_lib, char *plg_fct,
+ mtn_distrib_fct plg)
+{
+ Slapi_RDN rdn;
+ mapping_tree_node *node;
+ node = (mapping_tree_node *) slapi_ch_calloc(1, sizeof(mapping_tree_node));
+ node->mtn_subtree = dn;
+ node->mtn_be = be;
+ node->mtn_be_states = be_states;
+ node->mtn_backend_names = backend_names;
+ node->mtn_referral = referral;
+ node->mtn_referral_entry = referral2entry(referral, slapi_sdn_get_dn(dn)) ;
+ node->mtn_parent = parent;
+ node->mtn_children = NULL;
+ node->mtn_brother = NULL;
+ node->mtn_state = state;
+ node->mtn_private = private;
+ node->mtn_be_list_size = size;
+ node->mtn_be_count = count;
+ /* We use this count of the rdn components in the mapping tree to help
+ * when selecting a mapping tree node for a dn. */
+ slapi_rdn_init_sdn(&rdn,dn);
+ slapi_rdn_done(&rdn);
+ node->mtn_dstr_plg_lib = plg_lib;
+ node->mtn_dstr_plg_name = plg_fct;
+ node->mtn_dstr_plg = plg;
+
+ return node;
+}
+
+/*
+ * Description:
+ * Adds a mapping tree node to the child list of another mapping tree node.
+ *
+ * Arguments:
+ * parent and child are pointers to mapping tree nodes. child will be added
+ * to parent's child list. For now, the child is added to the head of the
+ * linked list. Later we may come up a way to ordering the entries in the
+ * list.
+ *
+ * Returns:
+ * nothing
+ */
+static void
+mapping_tree_node_add_child(mapping_tree_node *parent, mapping_tree_node* child)
+{
+ /* WARNING:
+ * As for now the mapping tree is not locked when a child is added
+ * this is possible only because the child is added into the mapping
+ * the structure by a single operation after being fully initialized
+ * should this be changed, the lock policy would have to be checked
+ * see mapping_tree_entry_add_callback()
+ */
+ child->mtn_brother = parent->mtn_children;
+ parent->mtn_children = child;
+ /* for debugging: dump_mapping_tree(mapping_tree_root, 0); */
+}
+
+static Slapi_DN *
+get_parent_from_entry(Slapi_Entry * entry)
+{
+ Slapi_Attr *attr = NULL;
+ char * parent;
+ Slapi_Value *val = NULL;
+ Slapi_DN *parent_sdn;
+
+ if (slapi_entry_attr_find(entry, MAPPING_TREE_PARENT_ATTRIBUTE, &attr))
+ return NULL;
+
+ slapi_attr_first_value(attr, &val);
+
+ parent = slapi_ch_strdup(slapi_value_get_string(val));
+ if(parent[0]=='\"')
+ {
+ parent[strlen(parent) - 1] = '\0';
+ parent_sdn = slapi_sdn_new_dn_byval(parent+1);
+ }
+ else
+ parent_sdn = slapi_sdn_new_dn_byval(parent);
+ slapi_ch_free((void **) &parent);
+
+ return parent_sdn;
+}
+
+/* extract the subtree managed by a mapping tree entry from the entry
+ */
+static Slapi_DN *
+get_subtree_from_entry(Slapi_Entry * entry)
+{
+ Slapi_Attr *attr = NULL;
+ char * cn;
+ Slapi_Value *val = NULL;
+ Slapi_DN *subtree;
+
+ if (slapi_entry_attr_find(entry, "cn", &attr))
+ return NULL;
+
+ /* should check that there is only one value for cn attribute */
+ slapi_attr_first_value(attr, &val);
+
+ /* The value of cn is the dn of the subtree for this node.
+ * There is a slight problem though. The cn value is
+ * quoted. We have to remove the quotes here. I'm sure
+ * there is a proper way to do this, but for now we'll
+ * just assume that the first and last chars are ". Later
+ * we'll have to revisit this because things could be a
+ * lot more complicated. Especially if there are quotes
+ * in the dn of the subtree root dn! */
+ /* JCM - Need to dequote correctly. */
+ /* GB : I think removing the first and last " in the cn value
+ * is the right stuff to do
+ */
+ cn = slapi_ch_strdup(slapi_value_get_string(val));
+ if(cn[0]=='\"')
+ {
+ cn[strlen(cn) - 1] = '\0';
+ subtree = slapi_sdn_new_dn_byval(cn+1);
+ }
+ else
+ subtree = slapi_sdn_new_dn_byval(cn);
+ slapi_ch_free((void **) &cn);
+
+ return subtree;
+}
+
+static int
+mtn_state_to_int(const char * state_string, Slapi_Entry *entry)
+{
+ if (!strcasecmp(state_string, "disabled")) {
+ return MTN_DISABLED;
+ } else if (!strcasecmp(state_string, "backend")) {
+ return MTN_BACKEND;
+ } else if (!strcasecmp(state_string, "referral")) {
+ return MTN_REFERRAL;
+ } else if (!strcasecmp(state_string, "referral on update")) {
+ return MTN_REFERRAL_ON_UPDATE;
+ } else if (!strcasecmp(state_string, "container")) {
+ return MTN_CONTAINER;
+ } else {
+ LDAPDebug(LDAP_DEBUG_ANY,
+ "Warning: Unknown state, %s, for mapping tree node %s."
+ " Defaulting to DISABLED\n",
+ state_string, slapi_entry_get_dn(entry), 0);
+ return MTN_DISABLED;
+ }
+}
+
+static char **
+mtn_get_referral_from_entry(Slapi_Entry * entry)
+{
+ int nb;
+ int hint;
+ char ** referral;
+ Slapi_Attr * attr;
+ Slapi_Value *val = NULL;
+
+ if (slapi_entry_attr_find(entry, "nsslapd-referral", &attr))
+ return NULL;
+
+ slapi_attr_get_numvalues(attr, &nb);
+ referral = (char **) slapi_ch_malloc(sizeof(char *) * (nb+1));
+ hint = slapi_attr_first_value(attr, &val);
+ if (NULL == val) {
+ LDAPDebug(LDAP_DEBUG_ANY, "Warning: The nsslapd-referral attribute has no value for the mapping tree node %s\n", slapi_entry_get_dn(entry), 0, 0);
+ return NULL;
+ }
+
+ nb = 0;
+ while (val)
+ {
+ referral[nb++] = slapi_ch_strdup(slapi_value_get_string(val));
+ hint = slapi_attr_next_value(attr, hint, &val);
+ }
+ referral[nb] = NULL;
+
+ return referral;
+}
+
+static int
+get_backends_from_attr(Slapi_Attr *attr, backend ***be_list, char ***be_names,
+ int ** be_states, int *be_list_count, int *be_list_size,
+ mapping_tree_node * node)
+{
+ Slapi_Value *val = NULL;
+ backend *tmp_be = NULL;
+ char * tmp_backend_name;
+ int hint;
+ mapping_tree_node* new_node;
+
+ *be_list_size = BE_LIST_INIT_SIZE;
+ *be_list_count = 0;
+
+ *be_list = (backend **) slapi_ch_malloc(sizeof(backend *) * BE_LIST_INIT_SIZE);
+ *be_names = (char **) slapi_ch_malloc(sizeof(char *) * BE_LIST_INIT_SIZE);
+ *be_states = (int *) slapi_ch_malloc(sizeof(int) * BE_LIST_INIT_SIZE);
+
+ hint = slapi_attr_first_value(attr, &val);
+ if (NULL == val) {
+ slapi_ch_free ((void **)be_list);
+ *be_list = NULL;
+ return 0;
+ }
+
+ while (val)
+ {
+ tmp_backend_name = (char *) slapi_ch_strdup(slapi_value_get_string(val));
+ if (*be_list_count >= *be_list_size)
+ {
+ (*be_list_size) += BE_LIST_INCREMENT;
+ *be_names = (char **) slapi_ch_realloc((char *) (*be_names),
+ sizeof(char *) * (*be_list_size));
+ *be_list = (backend **) slapi_ch_realloc((char *) (*be_list),
+ sizeof(backend *) * (*be_list_size));
+ *be_states = (int *) slapi_ch_realloc((char *) (*be_states),
+ sizeof(int) * (*be_list_size));
+ }
+ (*be_names)[*be_list_count] = tmp_backend_name;
+
+ /* set backend as started by default */
+ (*be_states)[*be_list_count] = SLAPI_BE_STATE_ON;
+
+ /* We now need to find the backend with name backend_name. */
+ tmp_be= slapi_be_select_by_instance_name(tmp_backend_name);
+ new_node = get_mapping_tree_node_by_name(mapping_tree_root,
+ tmp_backend_name);
+ if (new_node && (new_node != node))
+ {
+ LDAPDebug(LDAP_DEBUG_ANY,
+ "ERROR: backend %s is already pointed to by a mapping tree"
+ " node. Only one mapping tree node can point to a backend\n",
+ tmp_backend_name, 0, 0);
+ tmp_be = NULL;
+ return -1;
+ }
+ if(tmp_be!=NULL)
+ {
+ tmp_be->be_mapped = 1;
+ (*be_list)[*be_list_count] = tmp_be;
+ }
+ else
+ {
+ /* It's just not here yet. That's OK. We'll fix it up at runtime. */
+ (*be_list)[*be_list_count] = NULL;
+ }
+ (*be_list_count) ++;
+ hint = slapi_attr_next_value(attr, hint, &val);
+ }
+
+ return 0;
+}
+
+/*
+ * Description:
+ * Release the memory allocated by the routine above.
+ * Call this when the backend not put into structure and need to cleanup these tmp allocations
+ */
+static void
+free_get_backends_from_attr(backend ***be_list, char ***be_names,
+ int ** be_states, int *be_list_count)
+{
+ int i;
+
+ /* sanity check */
+ PR_ASSERT (be_list != NULL && be_names != NULL && be_states != NULL && be_list_count != NULL);
+
+ if (*be_names != NULL) for (i = 0; i < *be_list_count; ++i) {
+ slapi_ch_free ((void **)&((*be_names)[i]));
+ }
+ slapi_ch_free ((void **)be_names);
+ slapi_ch_free ((void **)be_list);
+ slapi_ch_free ((void **)be_states);
+ *be_list_count = 0;
+}
+
+/*
+ * Description:
+ * Takes an entry and creates a mapping tree node from it. Loops through the
+ * attributes, pulling needed info from them. Right now, each node can only
+ * have one backend and one referral. Once we move to supporting more than
+ * one node and more than one referral, this function will need to be
+ * massaged a little.
+ *
+ * We should make a objectclass for a mapping tree node entry. That way
+ * schema checking would make this function more robust.
+ *
+ * Arguments:
+ * A mapping tree node entry read in from the DIT.
+ *
+ * Returns:
+ * An LDAP result code (LDAP_SUCCESS if all goes well).
+ * If the return value is LDAP_SUCCESS, *newnodep is set to the new mapping
+ * tree node (guaranteed to be non-NULL).
+ */
+static int
+mapping_tree_entry_add(Slapi_Entry *entry, mapping_tree_node **newnodep )
+{
+ Slapi_DN *subtree = NULL;
+ const char *tmp_ndn;
+ int be_list_count = 0;
+ int be_list_size;
+ backend **be_list = NULL;
+ char **be_names = NULL;
+ int * be_states = NULL;
+ char * plugin_funct = NULL;
+ char * plugin_lib = NULL;
+ mtn_distrib_fct plugin = NULL;
+
+ char **referral = NULL;
+ int state = MTN_DISABLED;
+ Slapi_Attr *attr = NULL;
+ mapping_tree_node *node = NULL;
+ mapping_tree_node *parent_node = mapping_tree_root;
+ int rc;
+ int lderr = LDAP_UNWILLING_TO_PERFORM; /* our default result code */
+ char *tmp_backend_name;
+ Slapi_Backend *be;
+
+ PR_ASSERT(newnodep != NULL);
+ *newnodep = NULL;
+
+ subtree = get_subtree_from_entry(entry);
+ /* Make sure we know the root dn of the subtree for this node. */
+ if (NULL == subtree) {
+ LDAPDebug(LDAP_DEBUG_ANY, "Warning: Unable to determine the subtree represented by the mapping tree node %s\n",
+ slapi_entry_get_dn(entry), 0, 0);
+ return lderr;
+ }
+
+ tmp_ndn = slapi_sdn_get_ndn( subtree );
+ if (tmp_ndn && ( '\0' == *tmp_ndn)) {
+
+ /* This entry is associated with the "" subtree. Treat this is
+ * a special case (no parent; will replace the internal root
+ * node (mapping_tree_root) with data from this entry).
+ */
+ slapi_log_error( SLAPI_LOG_ARGS, "mapping_tree_entry_add", "NULL suffix\n" );
+ parent_node = NULL;
+ }
+
+
+ /* Make sure a node does not already exist for this subtree */
+ if ( parent_node != NULL && NULL != mtn_get_mapping_tree_node_by_entry(mapping_tree_root,
+ subtree)) {
+ LDAPDebug(LDAP_DEBUG_ANY, "Warning: a mapping tree node for the"
+ " subtree %s already exists; unable to add the node %s\n",
+ slapi_sdn_get_dn(subtree), slapi_entry_get_dn(entry), 0);
+ slapi_sdn_free(&subtree);
+ return LDAP_ALREADY_EXISTS;
+ }
+
+ /* Loop through the attributes and handle the ones we care about. */
+ for (rc = slapi_entry_first_attr(entry, &attr);
+ !rc && attr;
+ rc = slapi_entry_next_attr(entry, attr, &attr)) {
+
+ char *type = NULL;
+ Slapi_Value *val = NULL;
+
+ slapi_attr_get_type(attr, &type);
+ if (NULL == type) {
+ /* strange... I wonder if we should give a warning here? */
+ continue;
+ }
+
+ if (!strcasecmp(type, "nsslapd-backend")) {
+
+ if (get_backends_from_attr(attr, &be_list, &be_names, &be_states,
+ &be_list_count, &be_list_size, NULL)) {
+ free_get_backends_from_attr(&be_list, &be_names, &be_states, &be_list_count);
+ slapi_sdn_free(&subtree);
+ return lderr;
+ }
+
+ if (NULL == be_list)
+ {
+ LDAPDebug(LDAP_DEBUG_ANY,
+ "Warning: The nsslapd-backend attribute has no value for the mapping tree node %s\n",
+ slapi_entry_get_dn(entry), 0, 0);
+ continue;
+ }
+
+ } else if (!strcasecmp(type, "nsslapd-referral")) {
+ referral = mtn_get_referral_from_entry(entry);
+
+ } else if (!strcasecmp(type, "nsslapd-state")) {
+ const char *state_string;
+
+ slapi_attr_first_value(attr, &val);
+ if (NULL == val) {
+ LDAPDebug(LDAP_DEBUG_ANY, "Warning: Can't determine the state of the mapping tree node %s\n",
+ slapi_entry_get_dn(entry), 0, 0);
+ continue;
+ }
+ /* Convert the string representation for the state to an int */
+ state_string = slapi_value_get_string(val);
+ state = mtn_state_to_int(state_string, entry);
+
+ } else if (!strcasecmp(type, "nsslapd-distribution-plugin")) {
+ slapi_attr_first_value(attr, &val);
+ if (NULL == val) {
+ LDAPDebug(LDAP_DEBUG_ANY, "Warning: The nsslapd-distribution-plugin attribute has no value for the mapping tree node %s\n",
+ slapi_entry_get_dn(entry), 0, 0);
+ continue;
+ }
+ plugin_lib = slapi_ch_strdup(slapi_value_get_string(val));
+ } else if (!strcasecmp(type, "nsslapd-distribution-funct")) {
+ slapi_attr_first_value(attr, &val);
+ if (NULL == val) {
+ LDAPDebug(LDAP_DEBUG_ANY, "Warning: The nsslapd-distribution-plugin attribute has no value for the mapping tree node %s\n",
+ slapi_entry_get_dn(entry), 0, 0);
+ continue;
+ }
+ plugin_funct = slapi_ch_strdup(slapi_value_get_string(val));
+ } else if (!strcasecmp(type, MAPPING_TREE_PARENT_ATTRIBUTE)) {
+ Slapi_DN *parent_node_dn = get_parent_from_entry(entry);
+ parent_node = mtn_get_mapping_tree_node_by_entry(
+ mapping_tree_root, parent_node_dn);
+
+ if (parent_node == NULL)
+ {
+ parent_node = mapping_tree_root;
+ LDAPDebug(LDAP_DEBUG_ANY,
+ "Warning: could not find parent for %s defaulting to root\n",
+ slapi_entry_get_dn(entry), 0, 0);
+ }
+ slapi_sdn_free(&parent_node_dn);
+ }
+ }
+
+ if (state == MTN_CONTAINER)
+ {
+ /* this can be extended later to include the general
+ null suffix, */
+ /* The "default" backend is used by the container node */
+ be = defbackend_get_backend();
+ if(be == NULL)
+ {
+ LDAPDebug(LDAP_DEBUG_ANY,
+ "ERROR: default container has not been created for the NULL SUFFIX node.\n",
+ 0, 0, 0);
+ return -1;
+ }
+
+ be_list_size = 1;
+ be_list_count = 0;
+
+ be_list = (backend **) slapi_ch_calloc(1, sizeof(backend *) );
+ be_names = (char **) slapi_ch_calloc(1, sizeof(char *) );
+ be_states = (int *) slapi_ch_calloc(1, sizeof(int) );
+
+ tmp_backend_name = (char *) slapi_ch_strdup("default"); /* "NULL_CONTAINER" */
+ (be_names)[be_list_count] = tmp_backend_name;
+
+ /* set backend as started by default */
+ (be_states)[be_list_count] = SLAPI_BE_STATE_ON;
+
+ be->be_mapped = 1;
+ (be_list)[be_list_count] = be;
+ be_list_count++;
+
+ }
+ /* check that all required attributes for the givene state are there :
+ * state backend -> need nsslapd-backend attribute
+ * state referral or referral on update -> need nsslapd-referral attribute
+ */
+
+ if (((state == MTN_BACKEND) || (state == MTN_REFERRAL_ON_UPDATE))
+ && (be_names == NULL))
+ {
+ LDAPDebug(LDAP_DEBUG_ANY,
+ "ERROR: node %s must define a backend\n",
+ slapi_entry_get_dn(entry), 0, 0);
+ slapi_sdn_free(&subtree);
+ free_get_backends_from_attr(&be_list, &be_names, &be_states, &be_list_count);
+ return lderr;
+ }
+ if (((state == MTN_REFERRAL) || (state == MTN_REFERRAL_ON_UPDATE))
+ && (referral == NULL))
+ {
+ LDAPDebug(LDAP_DEBUG_ANY,
+ "ERROR: node %s must define referrals to be in referral state\n",
+ slapi_entry_get_dn(entry), 0, 0);
+ slapi_sdn_free(&subtree);
+ free_get_backends_from_attr(&be_list, &be_names, &be_states, &be_list_count);
+ return lderr;
+ }
+
+ if (plugin_lib && plugin_funct)
+ {
+ PRLibrary *lib = PR_LoadLibrary(plugin_lib);
+ if (lib)
+ {
+ plugin = (mtn_distrib_fct) PR_FindSymbol(lib, plugin_funct);
+ }
+ else
+ {
+ LDAPDebug(LDAP_DEBUG_ANY, "ERROR: can't load plugin lib %s. "
+ SLAPI_COMPONENT_NAME_NSPR " %d (%s)\n",
+ plugin_lib, PR_GetError(), slapd_pr_strerror(PR_GetError()));
+ }
+
+ if (plugin == NULL)
+ {
+ LDAPDebug(LDAP_DEBUG_ANY,
+ "ERROR: node %s cannot find distribution plugin. "
+ SLAPI_COMPONENT_NAME_NSPR " %d (%s)\n",
+ slapi_entry_get_dn(entry), PR_GetError(), slapd_pr_strerror(PR_GetError()));
+ slapi_sdn_free(&subtree);
+ slapi_ch_free((void **) &plugin_funct);
+ slapi_ch_free((void **) &plugin_lib);
+ free_get_backends_from_attr(&be_list, &be_names, &be_states, &be_list_count);
+ return lderr;
+ }
+ }
+ else if ((plugin_lib == NULL) && (plugin_funct == NULL))
+ {
+ /* nothing configured -> OK */
+ plugin = NULL;
+ }
+ else
+ {
+ /* only one parameter configured -> ERROR */
+ LDAPDebug(LDAP_DEBUG_ANY,
+ "ERROR: node %s must define both lib and funct"
+ " for distribution plugin\n",
+ slapi_entry_get_dn(entry), 0, 0);
+ slapi_sdn_free(&subtree);
+ slapi_ch_free((void **) &plugin_funct);
+ slapi_ch_free((void **) &plugin_lib);
+ free_get_backends_from_attr(&be_list, &be_names, &be_states, &be_list_count);
+ return lderr;
+ }
+
+ /* Now we can create the node for this mapping tree entry. */
+ node= mapping_tree_node_new(subtree, be_list, be_names, be_states, be_list_count,
+ be_list_size, referral, parent_node, state,
+ 0 /* Normal node. People can see and change it. */,
+ plugin_lib, plugin_funct, plugin);
+
+ tmp_ndn = slapi_sdn_get_ndn( subtree );
+ if ( NULL != node && NULL == parent_node && tmp_ndn
+ && ('\0' == *tmp_ndn )) {
+ /* The new node is actually the "" node. Replace the root
+ * node with this new one by copying all information (we can't
+ * free the root node completely because children of the root
+ * node hold pointers to it in their mtn_parent field).
+ */
+
+ slapi_log_error( SLAPI_LOG_ARGS, "mapping_tree_entry_add", "fix up NULL suffix\n" );
+
+ node->mtn_children = mapping_tree_root->mtn_children;
+ node->mtn_brother = mapping_tree_root->mtn_brother;
+ *mapping_tree_root = *node; /* struct copy */
+ slapi_ch_free( (void **)&node );
+ node = mapping_tree_root;
+ }
+
+
+ if ( NULL != node ) {
+ lderr = LDAP_SUCCESS;
+ *newnodep = node;
+ }
+
+ return lderr;
+}
+
+/*
+ * Recursive procedure used to create node extensions once the mapping tree
+ * is fully initialized
+ * This is done after full init of the mapping tree so that the extensions can do
+ * searches
+ */
+void mtn_create_extension(mapping_tree_node *node)
+{
+ if (node == NULL)
+ return;
+
+ node->mtn_extension = factory_create_extension(mapping_tree_get_extension_type(),
+ node, NULL /* parent */);
+
+ mtn_create_extension(node->mtn_children);
+ mtn_create_extension(node->mtn_brother);
+}
+
+
+/*
+ * Description:
+ * Does the main work of building the in memory mapping tree form the entries
+ * in the DIT. This function is called recursively on all the given nodes
+ * children to build up the tree. Basically it does an internal search for
+ * all the entries who have the target node as a parent.
+ *
+ * Arguments:
+ * The target node and a flag that tells if it's the root of the tree.
+ *
+ * Returns:
+ * Nothing
+ */
+static int
+mapping_tree_node_get_children(mapping_tree_node *target, int is_root)
+{
+ Slapi_PBlock *pb;
+ char * filter = NULL;
+ int res;
+ Slapi_Entry **entries = NULL;
+ int x;
+ int result = 0;
+
+ pb = slapi_pblock_new();
+
+ /* Remember that the root node of the mapping tree is the NULL suffix.
+ * Since we don't really support it, children of the root node won't
+ * have a MAPPING_TREE_PARENT_ATTRIBUTE. */
+ if (is_root) {
+ filter = slapi_ch_malloc(strlen(MAPPING_TREE_PARENT_ATTRIBUTE)+38);
+ sprintf(filter, "(&(objectclass=nsMappingTree)(!(%s=*)))",
+ MAPPING_TREE_PARENT_ATTRIBUTE);
+ } else {
+ filter = slapi_ch_malloc(strlen(slapi_sdn_get_dn(target->mtn_subtree))
+ + strlen(MAPPING_TREE_PARENT_ATTRIBUTE) + 36);
+ sprintf(filter, "(&(objectclass=nsMappingTree)(%s=\"%s\"))",
+ MAPPING_TREE_PARENT_ATTRIBUTE,
+ slapi_sdn_get_dn(target->mtn_subtree));
+ }
+
+ slapi_search_internal_set_pb(pb, MAPPING_TREE_BASE_DN, LDAP_SCOPE_ONELEVEL,
+ filter, NULL, 0, NULL, NULL, (void *) plugin_get_default_component_id(), 0);
+ slapi_search_internal_pb(pb);
+ slapi_pblock_get(pb, SLAPI_PLUGIN_INTOP_RESULT, &res);
+
+ if (res != LDAP_SUCCESS) {
+ LDAPDebug(LDAP_DEBUG_ANY, "WARNING: Mapping tree unable to read %s: %d\n",
+ MAPPING_TREE_BASE_DN, res, 0);
+ result = -1;
+ goto done;
+ }
+
+ /* We now create the mapping tree node and call this function for each
+ * of the target's children. */
+ slapi_pblock_get(pb, SLAPI_PLUGIN_INTOP_SEARCH_ENTRIES, &entries);
+ if (NULL == entries) {
+ LDAPDebug(LDAP_DEBUG_ANY, "WARNING: No mapping tree node entries found under %s\n",
+ MAPPING_TREE_BASE_DN, 0, 0);
+ result = -1;
+ goto done;
+ }
+
+ for(x = 0; entries[x] != NULL; x++) {
+ mapping_tree_node *child = NULL;
+
+ if (LDAP_SUCCESS != mapping_tree_entry_add(entries[x], &child)) {
+ LDAPDebug(LDAP_DEBUG_ANY,
+ "ERROR: could not add mapping tree node %s\n",
+ slapi_entry_get_dn(entries[x]), 0, 0);
+ result = -1;
+ goto done;
+ }
+ if(target == child){
+ /* the mapping tree root got replaced
+ * nothing to do
+ */
+ } else {
+
+ child->mtn_parent = target;
+ mapping_tree_node_add_child(target, child);
+ }
+
+
+ if (mapping_tree_node_get_children(child, 0 /* not the root node */))
+ {
+ result = -1;
+ goto done;
+ }
+ }
+ slapi_free_search_results_internal(pb);
+
+done:
+ slapi_pblock_destroy(pb);
+ if (filter)
+ slapi_ch_free((void **) &filter);
+ return result;
+}
+
+/*
+ * Description:
+ * A first attempt at walking over the mapping tree and making sure things
+ * make sense. Right now it just makes sure that each parent node has a
+ * subtree that is the suffix of its children's subtrees. This function
+ * is called recursively.
+ *
+ * Arguments:
+ * The root node of the mapping tree.
+ *
+ * Returns:
+ * Nothing - it just prints warnings. This should probably change.
+ */
+static void
+mapping_tree_node_validate(mapping_tree_node *node)
+{
+ mapping_tree_node *child_entry;
+
+ /* Call this function for all of nodes children */
+ for (child_entry = node->mtn_children; child_entry; child_entry = child_entry->mtn_brother) {
+ mapping_tree_node_validate(child_entry);
+ }
+
+ if (node->mtn_parent) {
+ if (!slapi_sdn_issuffix(node->mtn_subtree, node->mtn_parent->mtn_subtree)) {
+ LDAPDebug(LDAP_DEBUG_ANY,
+ "Warning: Invalid mapping tree. %s can not be a child of %s\n",
+ slapi_sdn_get_ndn(node->mtn_subtree),
+ slapi_sdn_get_ndn(node->mtn_parent->mtn_subtree), 0);
+ }
+ }
+}
+
+static void
+mtn_free_referral_in_node (mapping_tree_node *node)
+{
+ char ** referral = node->mtn_referral;
+
+ if (referral)
+ {
+ int i;
+
+ for (i=0; referral[i]; i++)
+ slapi_ch_free((void **) &(referral[i]));
+ slapi_ch_free((void **) &referral);
+ }
+ if (node->mtn_referral_entry)
+ slapi_entry_free(node->mtn_referral_entry);
+
+ node->mtn_referral = NULL;
+ node->mtn_referral_entry = NULL;
+}
+
+int mapping_tree_entry_modify_callback(Slapi_PBlock *pb, Slapi_Entry* entryBefore, Slapi_Entry* entryAfter, int *returncode, char *returntext, void *arg)
+{
+ LDAPMod **mods;
+ int i;
+ mapping_tree_node * node;
+ Slapi_DN * subtree;
+ Slapi_Attr * attr;
+ Slapi_Value *val = NULL;
+ int be_list_count = 0;
+ int be_list_size = 0;
+ backend **backends = NULL;
+ char **be_names = NULL;
+ int * be_states = NULL;
+ char * plugin_fct = NULL;
+ char * plugin_lib = NULL;
+ int plugin_flag = 0;
+ mtn_distrib_fct plugin = NULL;
+
+ slapi_pblock_get(pb, SLAPI_MODIFY_MODS, &mods);
+ subtree = get_subtree_from_entry(entryAfter);
+ node = mtn_get_mapping_tree_node_by_entry(mapping_tree_root, subtree);
+ if (node == NULL)
+ {
+ /* should never happen */
+ *returncode = LDAP_OPERATIONS_ERROR;
+ return SLAPI_DSE_CALLBACK_ERROR;
+ }
+
+ for (i = 0; mods[i] != NULL; i++) {
+ if ( (strcasecmp(mods[i]->mod_type, "cn") == 0) ||
+ (strcasecmp(mods[i]->mod_type,
+ MAPPING_TREE_PARENT_ATTRIBUTE) == 0) )
+ {
+ mapping_tree_node * parent_node;
+ /* if we are deleting this attribute the new parent
+ * node will be mapping_tree_root
+ */
+ if ((mods[i]->mod_op & ~LDAP_MOD_BVALUES) == LDAP_MOD_DELETE)
+ {
+ parent_node = mapping_tree_root;
+ }
+ else
+ {
+ /* we have to find the new parent node */
+ Slapi_DN *parent_node_dn;
+ parent_node_dn = get_parent_from_entry(entryAfter);
+ parent_node = mtn_get_mapping_tree_node_by_entry(
+ mapping_tree_root, parent_node_dn);
+ if (parent_node == NULL)
+ {
+ parent_node = mapping_tree_root;
+ LDAPDebug(LDAP_DEBUG_ANY,
+ "Error: could not find parent for %s\n",
+ slapi_entry_get_dn(entryAfter), 0, 0);
+ slapi_sdn_free(&subtree);
+ *returncode = LDAP_UNWILLING_TO_PERFORM;
+ return SLAPI_DSE_CALLBACK_ERROR;
+ }
+ slapi_sdn_free(&parent_node_dn);
+ }
+
+ mtn_wlock();
+ /* modifying the parent of a node means moving it to an
+ * other place of the tree
+ * this can be done simply by removing it from its old place and
+ * moving it to the new one
+ */
+ mtn_remove_node(node);
+ mapping_tree_node_add_child(parent_node, node);
+ node->mtn_parent = parent_node;
+ mtn_unlock();
+
+ }
+ else if (strcasecmp(mods[i]->mod_type, "nsslapd-backend" ) == 0)
+ {
+ slapi_entry_attr_find(entryAfter, "nsslapd-backend", &attr);
+ if (NULL == attr)
+ {
+ /* if nsslapd-backend attribute is empty all backends have
+ * been suppressed, set backend list to NULL
+ * checks on the state are done a bit later
+ */
+ backends = NULL;
+ be_names = NULL;
+ be_states = NULL;
+ be_list_count = 0;
+ be_list_size = 0;
+ }
+ else if (get_backends_from_attr(attr, &backends, &be_names,
+ &be_states, &be_list_count, &be_list_size, node))
+ {
+ free_get_backends_from_attr(&backends, &be_names, &be_states, &be_list_count);
+ slapi_sdn_free(&subtree);
+ *returncode = LDAP_UNWILLING_TO_PERFORM;
+ return SLAPI_DSE_CALLBACK_ERROR;
+ }
+
+ mtn_wlock();
+
+ if ((backends == NULL) && (node->mtn_state == MTN_BACKEND))
+ {
+ sprintf(returntext, "mapping tree entry need at least one nsslapd-backend\n");
+ *returncode = LDAP_UNWILLING_TO_PERFORM;
+ mtn_unlock();
+ free_get_backends_from_attr(&backends, &be_names, &be_states, &be_list_count);
+ slapi_sdn_free(&subtree);
+ return SLAPI_DSE_CALLBACK_ERROR;
+ }
+
+ node->mtn_be_states = be_states;
+ node->mtn_be = backends;
+ node->mtn_backend_names = be_names;
+ node->mtn_be_count = be_list_count;
+ node->mtn_be_list_size = be_list_size;
+
+ mtn_unlock();
+
+ }
+ else if (strcasecmp(mods[i]->mod_type, "nsslapd-state" ) == 0)
+ {
+ Slapi_Value * val;
+ const char * new_state;
+ Slapi_Attr * attr;
+
+ /* state change
+ * for now only allow replace
+ */
+ if ((mods[i]->mod_op & ~LDAP_MOD_BVALUES) != LDAP_MOD_REPLACE)
+ {
+ sprintf(returntext, "must use replace operation to change state\n");
+ *returncode = LDAP_UNWILLING_TO_PERFORM;
+ slapi_sdn_free(&subtree);
+ return SLAPI_DSE_CALLBACK_ERROR;
+ }
+ if ((mods[i]->mod_bvalues == NULL) || (mods[i]->mod_bvalues[0] == NULL))
+ {
+ slapi_sdn_free(&subtree);
+ *returncode = LDAP_OPERATIONS_ERROR;
+ return SLAPI_DSE_CALLBACK_ERROR;
+ }
+
+ slapi_entry_attr_find(entryAfter, "nsslapd-state", &attr);
+ slapi_attr_first_value(attr, &val);
+ new_state = slapi_value_get_string(val);
+
+ if (mtn_state_to_int(new_state, entryAfter) == MTN_BACKEND)
+ {
+ if (slapi_entry_attr_find(entryAfter, "nsslapd-backend", &attr))
+ {
+ sprintf(returntext, "need to set nsslapd-backend before moving to backend state\n");
+ slapi_sdn_free(&subtree);
+ *returncode = LDAP_UNWILLING_TO_PERFORM;
+ return SLAPI_DSE_CALLBACK_ERROR;
+ }
+ }
+
+ if ((mtn_state_to_int(new_state, entryAfter) == MTN_REFERRAL) ||
+ (mtn_state_to_int(new_state, entryAfter) == MTN_REFERRAL_ON_UPDATE))
+ {
+ if (slapi_entry_attr_find(entryAfter, "nsslapd-referral", &attr))
+ {
+ sprintf(returntext, "need to set nsslapd-referral before moving to referral state\n");
+ slapi_sdn_free(&subtree);
+ *returncode = LDAP_UNWILLING_TO_PERFORM;
+ return SLAPI_DSE_CALLBACK_ERROR;
+ }
+ }
+
+ mtn_wlock();
+
+ node->mtn_state = mtn_state_to_int(new_state, entryAfter);
+
+ mtn_unlock();
+ }
+ else if (strcasecmp(mods[i]->mod_type, "nsslapd-referral" ) == 0)
+ {
+ char ** referral;
+
+ mtn_wlock();
+
+ if (((mods[i]->mod_op & ~LDAP_MOD_BVALUES) == LDAP_MOD_REPLACE)
+ || ((mods[i]->mod_op & ~LDAP_MOD_BVALUES) == LDAP_MOD_ADD))
+ {
+ /* delete old referrals, set new ones */
+ mtn_free_referral_in_node(node);
+ referral = mtn_get_referral_from_entry(entryAfter);
+ node->mtn_referral = referral;
+ node->mtn_referral_entry =
+ referral2entry(referral, slapi_sdn_get_dn(subtree));
+ } else if ((mods[i]->mod_op & ~LDAP_MOD_BVALUES) == LDAP_MOD_DELETE)
+ {
+ /* it is not OK to delete the referrals if they are still
+ * used
+ */
+ if ((node->mtn_state == MTN_REFERRAL) ||
+ (node->mtn_state == MTN_REFERRAL_ON_UPDATE))
+ {
+ sprintf(returntext,
+ "cannot delete referrals in this state\n");
+ *returncode = LDAP_UNWILLING_TO_PERFORM;
+ mtn_unlock();
+ slapi_sdn_free(&subtree);
+ return SLAPI_DSE_CALLBACK_ERROR;
+ }
+
+ mtn_free_referral_in_node(node);
+
+ } else
+ {
+ *returncode = LDAP_OPERATIONS_ERROR;
+ mtn_unlock();
+ slapi_sdn_free(&subtree);
+ return SLAPI_DSE_CALLBACK_ERROR;
+ }
+
+ mtn_unlock();
+ slapi_sdn_free(&subtree);
+ *returncode = LDAP_SUCCESS;
+ return SLAPI_DSE_CALLBACK_OK;
+ }
+ else if (strcasecmp(mods[i]->mod_type,
+ "nsslapd-distribution-funct" ) == 0)
+ {
+ if (((mods[i]->mod_op & ~LDAP_MOD_BVALUES) == LDAP_MOD_REPLACE)
+ || ((mods[i]->mod_op & ~LDAP_MOD_BVALUES) == LDAP_MOD_ADD))
+ {
+ slapi_entry_attr_find(entryAfter,
+ "nsslapd-distribution-funct", &attr);
+ slapi_attr_first_value(attr, &val);
+ if (NULL == val) {
+ LDAPDebug(LDAP_DEBUG_ANY,
+ "Warning: The nsslapd-distribution-funct attribute"
+ " has no value for the mapping tree node %s\n",
+ slapi_entry_get_dn(entryAfter), 0, 0);
+ plugin_fct = NULL;
+ }
+ plugin_fct = slapi_ch_strdup(slapi_value_get_string(val));
+ }
+ else if ((mods[i]->mod_op & ~LDAP_MOD_BVALUES) == LDAP_MOD_DELETE)
+ {
+ plugin_fct = NULL;
+ }
+ plugin_flag = 1;
+ }
+ else if (strcasecmp(mods[i]->mod_type,
+ "nsslapd-distribution-plugin" ) == 0)
+ {
+ if (((mods[i]->mod_op & ~LDAP_MOD_BVALUES) == LDAP_MOD_REPLACE)
+ || ((mods[i]->mod_op & ~LDAP_MOD_BVALUES) == LDAP_MOD_ADD))
+ {
+ slapi_entry_attr_find(entryAfter,
+ "nsslapd-distribution-plugin", &attr);
+ slapi_attr_first_value(attr, &val);
+ if (NULL == val) {
+ LDAPDebug(LDAP_DEBUG_ANY,
+ "Warning: The nsslapd-distribution-plugin attribute"
+ " has no value for the mapping tree node %s\n",
+ slapi_entry_get_dn(entryAfter), 0, 0);
+ plugin_lib = NULL;
+ }
+ plugin_lib = slapi_ch_strdup(slapi_value_get_string(val));
+ }
+ else if ((mods[i]->mod_op & ~LDAP_MOD_BVALUES) == LDAP_MOD_DELETE)
+ {
+ plugin_lib = NULL;
+ }
+ plugin_flag = 1;
+
+ }
+ }
+
+ /* if distribution plugin has been configured or modified
+ * check that the library and function exist
+ * and if yes apply the modifications
+ */
+ if (plugin_flag)
+ {
+ if (plugin_lib && plugin_fct)
+ {
+ PRLibrary *lib = PR_LoadLibrary(plugin_lib);
+ if (lib)
+ plugin = (mtn_distrib_fct) PR_FindSymbol(lib, plugin_fct);
+
+ if (plugin == NULL)
+ {
+ sprintf(returntext, "cannot find distribution plugin\n");
+ slapi_ch_free((void **) &plugin_fct);
+ slapi_ch_free((void **) &plugin_lib);
+ slapi_sdn_free(&subtree);
+ *returncode = LDAP_UNWILLING_TO_PERFORM;
+ return SLAPI_DSE_CALLBACK_ERROR;
+ }
+ }
+ else if ((plugin_lib == NULL) && (plugin_fct == NULL))
+ {
+ /* nothing configured -> OK */
+ plugin = NULL;
+ }
+ else
+ {
+ /* only one parameter configured -> ERROR */
+ sprintf(returntext,
+ "must define distribution function and library\n");
+ slapi_ch_free((void **) &plugin_fct);
+ slapi_ch_free((void **) &plugin_lib);
+ slapi_sdn_free(&subtree);
+ *returncode = LDAP_UNWILLING_TO_PERFORM;
+ return SLAPI_DSE_CALLBACK_ERROR;
+ }
+
+ mtn_wlock();
+ if (node->mtn_dstr_plg_lib)
+ slapi_ch_free((void **) &node->mtn_dstr_plg_lib);
+ node->mtn_dstr_plg_lib = plugin_lib;
+ if (node->mtn_dstr_plg_name)
+ slapi_ch_free((void **) &node->mtn_dstr_plg_name);
+ node->mtn_dstr_plg_name = plugin_fct;
+ node->mtn_dstr_plg = plugin;
+ mtn_unlock();
+ }
+
+ slapi_sdn_free(&subtree);
+ return SLAPI_DSE_CALLBACK_OK;
+}
+
+int mapping_tree_entry_add_callback(Slapi_PBlock *pb, Slapi_Entry* entryBefore, Slapi_Entry* e, int *returncode, char *returntext, void *arg)
+{
+ mapping_tree_node *node;
+ int i;
+ backend * be;
+
+ /* WARNING
+ * for adds we don't need to grab the mapping tree global lock,
+ * because the add operation in the tree is atomic because
+ * only one pointer is updated in the tree.
+ * Should the mapping tree stucture change, this would have to
+ * be checked again
+ */
+ *returncode = mapping_tree_entry_add(entryBefore, &node);
+ if (LDAP_SUCCESS != *returncode)
+ {
+ return SLAPI_DSE_CALLBACK_ERROR;
+ }
+
+ if(node && node->mtn_parent != NULL && node != mapping_tree_root )
+ {
+ /* If the node has a parent and the node is not the mapping tree root,
+ * then add it as a child node. Note that the special case when the
+ * node is the mapping tree root and has no parent is handled inside
+ * the mapping_tree_entry_add() function by replacing the contents of
+ * the mapping tree root node with information from the add request.
+ */
+ mapping_tree_node_add_child(node->mtn_parent, node);
+ }
+
+ for (i = 0; ((i < node->mtn_be_count) && (node->mtn_backend_names) &&
+ (node->mtn_backend_names[i])); i++)
+ {
+ if ((be = slapi_be_select_by_instance_name(node->mtn_backend_names[i]))
+ && (be->be_state == BE_STATE_STARTED))
+ {
+ mtn_be_state_change(node->mtn_backend_names[i], SLAPI_BE_STATE_DELETE,
+ node->mtn_be_states[i]);
+ }
+ }
+
+ node->mtn_extension = factory_create_extension(mapping_tree_get_extension_type(), node, NULL);
+
+ return SLAPI_DSE_CALLBACK_OK;
+}
+
+/* utility function to remove a node from the tree of mapping_tree_node
+ */
+static void mtn_remove_node(mapping_tree_node * node)
+{
+ if (node->mtn_parent->mtn_children == node)
+ node->mtn_parent->mtn_children = node->mtn_brother;
+ else
+ {
+ mapping_tree_node * tmp_node = node->mtn_parent->mtn_children;
+ while (tmp_node && (tmp_node->mtn_brother != node))
+ tmp_node = tmp_node->mtn_brother;
+
+ PR_ASSERT(tmp_node != NULL);
+
+ tmp_node->mtn_brother = node->mtn_brother;
+ }
+ node->mtn_brother = NULL;
+}
+
+int mapping_tree_entry_delete_callback(Slapi_PBlock *pb, Slapi_Entry* entryBefore, Slapi_Entry* e, int *returncode, char *returntext, void *arg)
+{
+ int result;
+ mapping_tree_node *node = NULL;
+ Slapi_DN * subtree;
+ int i;
+ int removed = 0;
+
+ mtn_wlock();
+ subtree = get_subtree_from_entry(entryBefore);
+
+ if (subtree == NULL)
+ {
+ /* there is no cn attribute in this entry
+ * -> this is not a mapping tree node
+ * -> nothing to do
+ */
+ result = SLAPI_DSE_CALLBACK_OK;
+ goto done;
+ }
+
+ node = slapi_get_mapping_tree_node_by_dn(subtree);
+ if (node == NULL)
+ {
+ /* should never happen */
+ *returncode = LDAP_OPERATIONS_ERROR;
+ result = SLAPI_DSE_CALLBACK_ERROR;
+ goto done;
+ }
+
+ if (slapi_sdn_compare(subtree, node->mtn_subtree))
+ {
+ /* There is no node associated to this entry
+ * -> nothing to do
+ */
+ result = SLAPI_DSE_CALLBACK_OK;
+ goto done;
+ }
+
+ /* if node has children we must refuse the delete */
+ if (node->mtn_children)
+ {
+ result = SLAPI_DSE_CALLBACK_ERROR;
+ *returncode = LDAP_UNWILLING_TO_PERFORM;
+ sprintf(returntext, "this node has some children");
+ goto done;
+ }
+
+ /* at this point the node should be different from mapping_tree_root
+ * and therefore have a parent
+ */
+ PR_ASSERT(node->mtn_parent != NULL);
+
+ /* lets get the node out of the mapping tree */
+ mtn_remove_node(node);
+
+ result = SLAPI_DSE_CALLBACK_OK;
+ removed = 1;
+
+done:
+ mtn_unlock();
+ slapi_sdn_free(&subtree);
+ if (SLAPI_DSE_CALLBACK_OK == result && removed)
+ {
+ /* Signal the plugins that a new backend-suffix has been deleted
+ * rq : we have to unlock the mapping tree in that case because
+ * most of the plugins will try to search upon this notification
+ * and should we keep the lock we would end with a dead-lock
+ */
+ for (i = 0; ((i < node->mtn_be_count) && (node->mtn_backend_names) &&
+ (node->mtn_backend_names[i])); i++)
+ {
+ if ((node->mtn_be_states[i] != SLAPI_BE_STATE_DELETE) &&
+ (NULL != slapi_be_select_by_instance_name(
+ node->mtn_backend_names[i])))
+ {
+ mtn_be_state_change(node->mtn_backend_names[i],
+ node->mtn_be_states[i], SLAPI_BE_STATE_DELETE);
+ }
+ }
+
+ /* at this point the node is out of the mapping tree,
+ * we can now free the structure
+ */
+ mtn_free_node(&node);
+ }
+ return result;
+}
+
+/*
+ * Add an internal mapping tree node.
+ */
+static mapping_tree_node *
+add_internal_mapping_tree_node(const char *subtree, Slapi_Backend *be, mapping_tree_node *parent)
+{
+ Slapi_DN *dn;
+ mapping_tree_node *node;
+ backend ** be_list = (backend **) slapi_ch_malloc(sizeof(backend **));
+
+ be_list[0] = be;
+
+ dn = slapi_sdn_new_dn_byval(subtree);
+ node= mapping_tree_node_new(
+ dn,
+ be_list,
+ NULL, /* backend_name */
+ NULL,
+ 1, /* number of backends at this node */
+ 1, /* size of backend list structure */
+ NULL, /* referral */
+ parent,
+ MTN_BACKEND,
+ 1, /* The config node is a private node.
+ * People can't see or change it. */
+ NULL, NULL, NULL);
+ return node;
+}
+
+/*
+ * Description:
+ * Inits the mapping tree. The mapping tree is rooted at a node with
+ * subtree "". Think of this node as the node for the NULL suffix
+ * even though we don't really support it. This function will
+ * create the root node and then consult the DIT for the rest of
+ * the nodes. It will also add the node for cn=config.
+ *
+ * One thing to note... Until the mapping tree is inited. We use
+ * slapi_be_select for all our selection needs. To read in the mapping
+ * tree from the DIT, we need to some internal operations. These
+ * operations need to use slapi_be_select.
+ *
+ * Arguments:
+ * Nothing
+ *
+ * Returns:
+ * Right now it always returns 0. This will most likely change. Right
+ * now, we just log warnings when ever something goes wrong.
+ */
+int
+mapping_tree_init()
+{
+ Slapi_Backend *be;
+ mapping_tree_node *node;
+ /* Create the root of the mapping tree. */
+
+ /* The root of the mapping tree is the NULL suffix. It's always there,
+ * but, because we don't really support it, we won't have an entry in
+ * the dit for the NULL suffix mapping tree node. */
+
+ /* Once we support the NULL suffix we should do something more clever here.
+ * For now will use the current backend we use for "" */
+
+ /* I'm not really sure what the state of the root node should be. The root
+ * node will end up being selected if none of the suffices for the backends
+ * would work with the target. For now when the root node is selected,
+ * the default backend will be returned. (The special case where the
+ * target dn is "" is handled differently.) */
+
+ /* we call this function from a single thread, so it should be ok */
+
+ if(mapping_tree_freed){
+ /* shutdown has been detected */
+ return 0;
+ }
+
+ if (mapping_tree_inited)
+ return 0;
+
+ /* ONREPL - I have moved this up because otherwise we can endup calling this
+ * function recursively */
+
+ mapping_tree_inited = 1;
+
+ slapi_register_supported_control(MTN_CONTROL_USE_ONE_BACKEND_OID,
+ SLAPI_OPERATION_SEARCH);
+ slapi_register_supported_control(MTN_CONTROL_USE_ONE_BACKEND_EXT_OID,
+ SLAPI_OPERATION_SEARCH);
+
+ myLock = PR_NewRWLock(PR_RWLOCK_RANK_NONE, "mapping tree");
+
+ be= slapi_be_select_by_instance_name(DSE_BACKEND);
+ mapping_tree_root= add_internal_mapping_tree_node("", be, NULL);
+
+ /* We also need to add the config and schema backends to the mapping tree.
+ * They are special in that users will not know about it's node in the
+ * mapping tree. This is to prevent them from disableing it or
+ * returning a referral for it. */
+ node= add_internal_mapping_tree_node("cn=config", be, mapping_tree_root);
+ mapping_tree_node_add_child(mapping_tree_root, node);
+ node= add_internal_mapping_tree_node("cn=monitor", be, mapping_tree_root);
+ mapping_tree_node_add_child(mapping_tree_root, node);
+ be= slapi_be_select_by_instance_name( "schema-internal" );
+ node= add_internal_mapping_tree_node("cn=schema", be, mapping_tree_root);
+ mapping_tree_node_add_child(mapping_tree_root, node);
+
+ /*
+ * Now we need to look under cn=mapping tree, cn=config to find the rest
+ * of the mapping tree entries.
+ * Builds the mapping tree from entries in the DIT. This function just
+ * calls mapping_tree_node_get_children with the special case for the
+ * root node.
+ */
+ if (mapping_tree_node_get_children(mapping_tree_root, 1))
+ return -1;
+
+ mtn_create_extension(mapping_tree_root);
+
+ /* setup the dse callback functions for the ldbm instance config entry */
+ {
+ slapi_config_register_callback(SLAPI_OPERATION_MODIFY,
+ DSE_FLAG_PREOP, MAPPING_TREE_BASE_DN, LDAP_SCOPE_SUBTREE,
+ "(objectclass=nsMappingTree)",
+ mapping_tree_entry_modify_callback, NULL);
+ slapi_config_register_callback(SLAPI_OPERATION_ADD,
+ DSE_FLAG_PREOP, MAPPING_TREE_BASE_DN, LDAP_SCOPE_SUBTREE,
+ "(objectclass=nsMappingTree)", mapping_tree_entry_add_callback,
+ NULL);
+ slapi_config_register_callback(SLAPI_OPERATION_DELETE,
+ DSE_FLAG_PREOP, MAPPING_TREE_BASE_DN, LDAP_SCOPE_SUBTREE,
+ "(objectclass=nsMappingTree)", mapping_tree_entry_delete_callback,
+ NULL);
+ }
+ return 0;
+}
+
+static void
+mtn_free_node (mapping_tree_node **node)
+{
+ mapping_tree_node *child = (*node)->mtn_children;
+
+ /* free children first */
+ while (child)
+ {
+ mapping_tree_node * tmp_child = child->mtn_brother;
+ mtn_free_node (&child);
+ child = tmp_child;
+ }
+ (*node)->mtn_children = NULL;
+ (*node)->mtn_parent = NULL;
+
+ /* free this node */
+ /* ONREPL - not quite sure which fields should be freed. For now,
+ only freeing fields explicitely allocated in the new_node function */
+ factory_destroy_extension (mapping_tree_get_extension_type(), *node, NULL,
+ &((*node)->mtn_extension));
+
+ slapi_sdn_free(&((*node)->mtn_subtree));
+
+ mtn_free_referral_in_node(*node);
+
+ if ((*node)->mtn_be_count > 0)
+ {
+ if ((*node)->mtn_be)
+ slapi_ch_free((void **) &((*node)->mtn_be));
+
+ if ((*node)->mtn_backend_names)
+ slapi_ch_free((void **) &((*node)->mtn_backend_names));
+
+ if ((*node)->mtn_be_states)
+ slapi_ch_free((void **) &((*node)->mtn_be_states));
+ }
+
+ slapi_ch_free ((void**) node);
+}
+
+/* Description: frees the tree; should be called when the server shuts down
+ */
+void
+mapping_tree_free ()
+{
+ /* unregister dse callbacks */
+ slapi_config_remove_callback(SLAPI_OPERATION_MODIFY, DSE_FLAG_PREOP, MAPPING_TREE_BASE_DN, LDAP_SCOPE_BASE, "(objectclass=*)", mapping_tree_entry_modify_callback);
+ slapi_config_remove_callback(SLAPI_OPERATION_ADD, DSE_FLAG_PREOP, MAPPING_TREE_BASE_DN, LDAP_SCOPE_BASE, "(objectclass=*)", mapping_tree_entry_add_callback);
+ slapi_config_remove_callback(SLAPI_OPERATION_DELETE, DSE_FLAG_PREOP, MAPPING_TREE_BASE_DN, LDAP_SCOPE_BASE, "(objectclass=*)", mapping_tree_entry_delete_callback);
+
+ /* The state change plugins registered on the mapping tree
+ * should not get any state change information
+ * - unregister all those callbacks
+ */
+ slapi_unregister_backend_state_change_all();
+ /* recursively free tree nodes */
+ mtn_free_node (&mapping_tree_root);
+ mapping_tree_freed = 1;
+}
+
+/* This function returns the first node to parse when a search is done
+ * on a given node in the mapping tree
+ */
+static mapping_tree_node *
+mtn_get_first_node(mapping_tree_node * node, int scope)
+{
+ if (node == NULL)
+ return NULL;
+
+ /* never climb down the tree from base "" */
+ if (node == mapping_tree_root)
+ {
+ return node;
+ }
+
+ if (scope == LDAP_SCOPE_BASE)
+ return node;
+
+ if (scope == LDAP_SCOPE_ONELEVEL)
+ {
+ if (node->mtn_children)
+ return node->mtn_children;
+ else
+ return node;
+ }
+
+ while (node->mtn_children)
+ node = node->mtn_children;
+
+ return node;
+}
+
+int slapi_mtn_get_first_be(mapping_tree_node * node_list,
+ mapping_tree_node ** node, Slapi_PBlock *pb, Slapi_Backend **be,
+ int * be_index, Slapi_Entry **referral, char *errorbuf, int scope)
+{
+ *node = mtn_get_first_node(node_list, scope);
+ if (scope == LDAP_SCOPE_BASE)
+ *be_index = -1;
+ else
+ *be_index = 0;
+
+ return mtn_get_be(*node, pb, be, be_index, referral, errorbuf);
+}
+
+int slapi_mtn_get_next_be(mapping_tree_node * node_list,
+ mapping_tree_node ** node, Slapi_PBlock *pb, Slapi_Backend **be,
+ int * be_index, Slapi_Entry **referral, char *errorbuf, int scope)
+{
+ int rc;
+
+ if (((*node)->mtn_parent == NULL) || /* -> node has been deleted */
+ (scope == LDAP_SCOPE_BASE))
+
+ {
+ *node = NULL;
+ *be = NULL;
+ *referral = NULL;
+ return 0;
+ }
+
+ /* never climb down the tree from the rootDSE */
+ if (node_list == mapping_tree_root)
+ {
+ *node = NULL;
+ *be = NULL;
+ *referral = NULL;
+ return 0;
+ }
+
+ rc = mtn_get_be(*node, pb, be, be_index, referral, errorbuf);
+
+ if (rc != LDAP_SUCCESS)
+ {
+ *node = mtn_get_next_node(*node, node_list, scope);
+ return rc;
+ }
+
+ if ((*be == NULL) && (*referral == NULL))
+ {
+ *node = mtn_get_next_node(*node, node_list, scope);
+ if (*node == NULL)
+ {
+ *be = NULL;
+ return 0;
+ }
+ *be_index = 0;
+ return mtn_get_be(*node, pb, be, be_index, referral, errorbuf);
+ }
+
+ return LDAP_SUCCESS;
+}
+
+/* This function returns the next node to parse when a subtree search is done
+ * on a given node in the mapping tree
+ */
+static mapping_tree_node *
+mtn_get_next_node(mapping_tree_node * node, mapping_tree_node * node_list, int scope)
+{
+ if (scope == LDAP_SCOPE_BASE)
+ return NULL;
+
+ /* if we are back to the top of the subtree searched then we have finished */
+ if (node == node_list)
+ node = NULL;
+
+ else if (node->mtn_brother)
+ {
+ node = node->mtn_brother;
+ if (scope == LDAP_SCOPE_SUBTREE)
+ while (node->mtn_children)
+ node = node->mtn_children;
+ }
+ else
+ node = node->mtn_parent;
+
+ return node;
+}
+
+/* Description :
+ * return 0 if the given entry does not have any child node in the mapping tree
+ * != otherwise
+ *
+ */
+int
+mtn_sdn_has_child(Slapi_DN *target_sdn)
+{
+ mapping_tree_node *node;
+
+ /* algo : get the target node for the given dn
+ * then loop through all its child to check if one of them is below
+ * the target dn
+ */
+ node = slapi_get_mapping_tree_node_by_dn(target_sdn);
+
+ /* if there is no node for this dn then there is no child either */
+ if (node == NULL)
+ return 0;
+
+ node = node->mtn_children;
+ while (node)
+ {
+ if (slapi_sdn_issuffix(node->mtn_subtree, target_sdn))
+ return 1;
+ node = node->mtn_brother;
+ }
+ return 0;
+}
+
+
+/* Description:
+ * Find the backend that would be used to store a dn.
+ */
+Slapi_Backend *slapi_mapping_tree_find_backend_for_sdn(Slapi_DN *sdn)
+{
+ mapping_tree_node *target_node;
+ Slapi_Backend *be;
+ int flag_stop = 0, index;
+ Slapi_PBlock *pb;
+ Slapi_Operation *op;
+
+ mtn_lock();
+ target_node = slapi_get_mapping_tree_node_by_dn(sdn);
+
+ if ((target_node == mapping_tree_root) &&
+ (slapi_sdn_get_ndn_len(sdn) > 0)) {
+ /* couldn't find a matching node */
+ be = defbackend_get_backend();
+ goto done;
+ }
+
+ if ((target_node == NULL) || (target_node->mtn_be_count == 0)) {
+ /* no backend configured for this node */
+ be = NULL;
+ goto done;
+ }
+
+ if (target_node->mtn_be_count == 1) {
+ /* not distributed, so we've already found it */
+ if (target_node->mtn_be[0] == NULL) {
+ target_node->mtn_be[0] = slapi_be_select_by_instance_name(
+ target_node->mtn_backend_names[0]);
+ }
+ be = target_node->mtn_be[0];
+ goto done;
+ }
+
+ /* have to call the distribution plugin */
+ be = defbackend_get_backend();
+ pb = slapi_pblock_new();
+ if (!pb) {
+ goto done;
+ }
+ op = internal_operation_new(SLAPI_OPERATION_ADD, 0);
+ if (!op) {
+ slapi_pblock_destroy(pb);
+ goto done;
+ }
+ operation_set_target_spec(op, sdn);
+ slapi_pblock_set(pb, SLAPI_OPERATION, op);
+ index = mtn_get_be_distributed(pb, target_node, sdn, &flag_stop);
+ slapi_pblock_destroy(pb); /* also frees the operation */
+
+ if (target_node->mtn_be[index] == NULL) {
+ target_node->mtn_be[index] = slapi_be_select_by_instance_name(
+ target_node->mtn_backend_names[index]);
+ }
+ be = target_node->mtn_be[index];
+
+done:
+ mtn_unlock();
+ return be;
+}
+
+/* Check if the target dn is '\0' - the null dn */
+static int sdn_is_nulldn(const Slapi_DN *sdn){
+
+ if(sdn){
+ const char *dn= slapi_sdn_get_ndn(sdn);
+ if(dn && ( '\0' == *dn)){
+ return 1;
+ }
+ }
+ return 0;
+}
+
+/*
+ * Description:
+ * The reason we have a mapping tree. This function selects a backend or
+ * referral to handle a given request. Uses the target of the operation to
+ * find a mapping tree node, then based on the operation type, bind dn, state
+ * of the node, etc. it selects a backend or referral.
+ *
+ * In this initial implementation of the mapping tree, each node can only have
+ * one backend and one referral. Later we should change this so each node has
+ * a list of backends and a list of referrals. Then we should add a modifier
+ * to the state of the node. For example, MTN_MODIFIER_ROUND_ROBIN could be a
+ * modifer on the way a backend or referral is returned from the lists.
+ *
+ * Arguments:
+ * pb is the pblock being used to service the operation.
+ * be is an output param that will be set to the selected backend.
+ * referral is an output param that will be set to the selected referral.
+ * errorbuf is a pointer to a buffer that an error string will be written to
+ * if there is an error. The caller is responsible for passing in a big
+ * enough chunk of memory. BUFSIZ should be fine. If errorbuf is NULL,
+ * no error string is written to it. The string returned in errorbuf
+ * would be a good candidate for sending back to the client to describe the
+ * error.
+ *
+ * Returns:
+ * LDAP_SUCCESS on success, other LDAP result codes if there is a problem.
+ */
+int slapi_mapping_tree_select(Slapi_PBlock *pb, Slapi_Backend **be, Slapi_Entry **referral, char *errorbuf)
+{
+ Slapi_DN *target_sdn = NULL;
+ mapping_tree_node *target_node;
+ Slapi_Operation *op;
+ int index;
+ int ret;
+ int scope=LDAP_SCOPE_BASE;
+ int op_type;
+
+
+ if(mapping_tree_freed){
+ /* shutdown detected */
+ return LDAP_OPERATIONS_ERROR;
+ }
+
+
+ if (errorbuf) {
+ errorbuf[0] = '\0';
+ }
+
+ slapi_pblock_get(pb, SLAPI_OPERATION, &op);
+ slapi_pblock_get(pb, SLAPI_OPERATION_TYPE, &op_type);
+ slapi_pblock_get(pb, SLAPI_SEARCH_SCOPE, &scope);
+
+ /* Get the target for this op */
+ target_sdn = operation_get_target_spec (op);
+
+ if(!mapping_tree_inited) {
+ mapping_tree_init();
+ }
+
+ be[0] = NULL;
+ referral[0] = NULL;
+
+ mtn_lock();
+
+ /* Get the mapping tree node that is the best match for the target dn. */
+ target_node = slapi_get_mapping_tree_node_by_dn(target_sdn);
+ if (target_node == NULL)
+ target_node = mapping_tree_root;
+
+ /* The processing of the base scope root DSE search and all other LDAP operations on ""
+ * will be transferred to the internal DSE backend
+ */
+ if( sdn_is_nulldn(target_sdn) &&
+ ((op_type == SLAPI_OPERATION_SEARCH) && (scope == LDAP_SCOPE_BASE) ||
+ (op_type != SLAPI_OPERATION_SEARCH)) ) {
+
+ mtn_unlock();
+ *be = slapi_be_select_by_instance_name(DSE_BACKEND);
+ if(*be != NULL)
+ {
+ ret = LDAP_SUCCESS;
+ slapi_be_Rlock(*be); /* also done inside mtn_get_be() below */
+ } else {
+ ret = LDAP_OPERATIONS_ERROR;
+ }
+ return ret;
+ }
+
+ /* index == -1 is used to specify that we want only one backend not a list
+ * used for BASE search, ADD, DELETE, MODIFY
+ */
+ index = -1;
+ ret = mtn_get_be(target_node, pb, be, &index, referral, errorbuf);
+ slapi_pblock_set(pb, SLAPI_BACKEND_COUNT, &index);
+
+ mtn_unlock();
+
+ /* if a backend was returned, make sure that all non-search operations
+ * fail if the backend is read-only,
+ * or if the whole server is readonly AND backend is public (!private)
+ */
+ if ((ret == LDAP_SUCCESS) && *be &&
+ ((*be)->be_readonly ||
+ (slapi_config_get_readonly() && !slapi_be_private(*be)))) {
+ unsigned long op_type = operation_get_type(op);
+
+ if ((op_type != SLAPI_OPERATION_SEARCH) &&
+ (op_type != SLAPI_OPERATION_COMPARE) &&
+ (op_type != SLAPI_OPERATION_BIND) &&
+ (op_type != SLAPI_OPERATION_UNBIND)) {
+ ret = LDAP_UNWILLING_TO_PERFORM;
+ strcpy(errorbuf, slapi_config_get_readonly() ?
+ "Server is read-only" :
+ "database is read-only");
+ slapi_be_Unlock(*be);
+ *be = NULL;
+ }
+ }
+
+ return ret;
+}
+
+int slapi_mapping_tree_select_all(Slapi_PBlock *pb, Slapi_Backend **be_list,
+ Slapi_Entry **referral_list, char *errorbuf)
+{
+ Slapi_DN *target_sdn = NULL;
+ mapping_tree_node *node_list;
+ mapping_tree_node *node;
+ Slapi_Operation *op;
+ int index;
+ int ret;
+ int ret_code = LDAP_SUCCESS;
+ int be_index = 0 ;
+ int referral_index = 0 ;
+ int be_list_size = 0;
+ int referral_list_size = 0;
+ Slapi_Backend * be;
+ Slapi_Entry * referral;
+ int scope = LDAP_SCOPE_BASE;
+ Slapi_DN sdn;
+ char *base;
+ int flag_partial_result = 0;
+ int op_type;
+
+ if(mapping_tree_freed){
+ return LDAP_OPERATIONS_ERROR;
+ }
+
+ if (errorbuf) {
+ errorbuf[0] = '\0';
+ }
+
+ /* get the operational parameters */
+ slapi_pblock_get(pb, SLAPI_SEARCH_TARGET, &base);
+ slapi_sdn_init_dn_ndn_byref(&sdn, base); /* normalized in opshared.c */
+ slapi_pblock_get(pb, SLAPI_OPERATION, &op);
+ target_sdn = operation_get_target_spec (op);
+ slapi_pblock_get(pb, SLAPI_OPERATION_TYPE, &op_type);
+ slapi_pblock_get(pb, SLAPI_SEARCH_SCOPE, &scope);
+
+ if(!mapping_tree_inited){
+ mapping_tree_init();
+ }
+
+ mtn_lock();
+
+ be_list_size = BE_LIST_SIZE;
+ referral_list_size = BE_LIST_SIZE;
+ be_list[0] = NULL;
+ referral_list[0] = NULL;
+
+ /* Get the mapping tree node that is the best match for the target dn. */
+ node_list = slapi_get_mapping_tree_node_by_dn(target_sdn);
+ if (node_list == NULL)
+ node_list = mapping_tree_root;
+
+ if( sdn_is_nulldn(target_sdn) && ( op_type == SLAPI_OPERATION_SEARCH)
+ && (scope == LDAP_SCOPE_BASE) ) {
+ mtn_unlock();
+ be = slapi_be_select_by_instance_name(DSE_BACKEND);
+ if(be != NULL)
+ {
+ be_list[0]=be;
+ be_list[1] = NULL;
+ ret_code = LDAP_SUCCESS;
+ slapi_be_Rlock(be); /* also done inside mtn_get_be() below */
+ } else {
+ ret_code = LDAP_OPERATIONS_ERROR;
+ }
+ return ret_code;
+ }
+
+ ret = slapi_mtn_get_first_be(node_list, &node, pb, &be, &index, &referral, errorbuf, scope);
+
+ while ((node) &&(index < BE_LIST_SIZE))
+ {
+ if (ret != LDAP_SUCCESS)
+ {
+ /* flag we have problems at least on part of the tree */
+ flag_partial_result = 1;
+ }
+ else if ( ( ((!slapi_sdn_issuffix(&sdn, slapi_mtn_get_dn(node))
+ && !slapi_sdn_issuffix(slapi_mtn_get_dn(node), &sdn)))
+ || ((node_list == mapping_tree_root) && node->mtn_private
+ && (scope != LDAP_SCOPE_BASE)) )
+ && (!be || strncmp(be->be_name, "default", 8)))
+ {
+ if (be)
+ {
+ /* wrong backend or referall, ignore it */
+ slapi_log_error(SLAPI_LOG_ARGS, NULL,
+ "mapping tree release backend : %s\n",
+ slapi_be_get_name(be));
+ slapi_be_Unlock(be);
+ }
+ }
+ else
+ {
+ if (be)
+ {
+ be_list[be_index++]=be;
+ }
+
+ if (referral)
+ {
+ referral_list[referral_index++] = referral;
+
+ /* if we hit a referral at the base of the search
+ * we must return a REFERRAL error with only this referral
+ * all backend or referral below this node are ignored
+ */
+ if (slapi_sdn_issuffix(target_sdn, slapi_mtn_get_dn(node)))
+ {
+ ret_code = LDAP_REFERRAL;
+ break; /* get out of the while loop */
+ }
+ }
+ }
+
+ ret = slapi_mtn_get_next_be(node_list, &node, pb, &be, &index,
+ &referral, errorbuf, scope);
+ }
+ mtn_unlock();
+ slapi_sdn_done(&sdn);
+ be_list[be_index] = NULL;
+ referral_list[referral_index] = NULL;
+
+ if (flag_partial_result)
+ {
+ /* if no node in active has been found -> return LDAP_OPERATIONS_ERROR
+ * but if only part of the nodes are disabled
+ * do not return an error to allow directory browser to work OK
+ * in the console
+ * It would be better to return a meaningfull error
+ * unfortunately LDAP_PARTIAL_RESULTS is not usable because
+ * it is already used for V2 referrals
+ * leave no error for now and fix this later
+ */
+ if ((be_index == 0) && (referral_index == 0))
+ return LDAP_OPERATIONS_ERROR;
+ else
+ return ret_code;
+ }
+ else
+ return ret_code;
+}
+
+void slapi_mapping_tree_free_all(Slapi_Backend **be_list, Slapi_Entry **referral_list)
+{
+ int index = 0;
+
+ /* go through the list of all backends that was used for the operation
+ * and unlock them
+ * go through the list of referrals and free them
+ * free the two tables that were used to store the two lists
+ */
+ if (be_list[index] != NULL)
+ {
+ Slapi_Backend * be;
+
+ while (be = be_list[index++])
+ {
+ slapi_log_error(SLAPI_LOG_ARGS, NULL, "mapping tree release backend : %s\n", slapi_be_get_name(be));
+ slapi_be_Unlock(be);
+ }
+ }
+
+ index = 0;
+ if (referral_list[index] != NULL)
+ {
+ Slapi_Entry * referral;
+ while (referral = referral_list[index++])
+ {
+ slapi_entry_free(referral);
+ }
+ }
+}
+
+
+/* same as slapi_mapping_tree_select() but will also check that the supplied
+ * newdn is in the same backend
+ */
+int slapi_mapping_tree_select_and_check(Slapi_PBlock *pb,char *newdn, Slapi_Backend **be, Slapi_Entry **referral, char *errorbuf)
+{
+ Slapi_DN *target_sdn = NULL;
+ Slapi_DN dn_newdn;
+ Slapi_Backend * new_be = NULL;
+ Slapi_Entry * new_referral = NULL;
+ mapping_tree_node *target_node;
+ int index;
+ Slapi_Operation *op;
+ int ret;
+
+ if(mapping_tree_freed){
+ return LDAP_OPERATIONS_ERROR;
+ }
+
+ slapi_sdn_init(&dn_newdn);
+ slapi_pblock_get(pb, SLAPI_OPERATION, &op);
+ target_sdn = operation_get_target_spec (op);
+
+ mtn_lock();
+
+ * referral = NULL;
+ ret = slapi_mapping_tree_select(pb, be, referral, errorbuf);
+ if (ret)
+ goto unlock_and_return;
+
+ slapi_sdn_init_dn_byref(&dn_newdn,newdn);
+ target_node = slapi_get_mapping_tree_node_by_dn(&dn_newdn);
+ if (target_node == NULL)
+ target_node = mapping_tree_root;
+ index = -1;
+ ret = mtn_get_be(target_node, pb, &new_be, &index, &new_referral, errorbuf);
+ if (ret)
+ goto unlock_and_return;
+
+ if ((*be) && ((*be != new_be) || mtn_sdn_has_child(target_sdn)))
+ {
+ ret = LDAP_UNWILLING_TO_PERFORM;
+ sprintf(errorbuf, "Cannot move entries accross backends\n");
+ goto unlock_and_return;
+ }
+
+unlock_and_return:
+ slapi_sdn_done(&dn_newdn);
+
+ if (new_be)
+ slapi_be_Unlock(new_be);
+
+ if (new_referral)
+ slapi_entry_free(new_referral);
+
+ if (ret != LDAP_SUCCESS)
+ {
+ if (*be)
+ {
+ slapi_be_Unlock(*be);
+ *be = NULL;
+ }
+ if (*referral)
+ {
+ slapi_entry_free(*referral);
+ *referral = NULL;
+ }
+ }
+
+ mtn_unlock();
+
+ return ret;
+}
+
+/*
+ * allow to solve the distribution problem when several back-ends are defined
+ */
+static int
+mtn_get_be_distributed(Slapi_PBlock *pb, mapping_tree_node * target_node,
+ Slapi_DN *target_sdn, int * flag_stop)
+{
+ int index;
+ *flag_stop = 0;
+
+ if (target_node->mtn_dstr_plg)
+ {
+ index = (*target_node->mtn_dstr_plg)(pb, target_sdn,
+ target_node->mtn_backend_names, target_node->mtn_be_count,
+ target_node->mtn_subtree, target_node->mtn_be_states);
+
+ if (index == SLAPI_BE_ALL_BACKENDS)
+ {
+ /* special value to indicate all backends must be scanned
+ * start with first one
+ */
+ index = 0;
+ }
+ /* paranoid check, never trust another programmer */
+ else if ((index >= target_node->mtn_be_count) || (index < 0))
+ {
+ LDAPDebug(LDAP_DEBUG_ANY,
+ "Warning: distribution plugin returned wrong backend"
+ " : %d for entry %s at node %s\n",
+ index, slapi_sdn_get_ndn(target_sdn),
+ slapi_sdn_get_ndn(target_node->mtn_subtree));
+ index = 0;
+ }
+ else
+ {
+ /* only one backend to scan
+ * set flag_stop to indicate we must stop the search here
+ */
+ *flag_stop = 1;
+ }
+ }
+ else
+ {
+ /* there is several backends but no distribution function
+ * return the first backend
+ */
+ LDAPDebug(LDAP_DEBUG_ANY,
+ "Warning: distribution plugin not configured at node : %s\n",
+ slapi_sdn_get_ndn(target_node->mtn_subtree), 0, 0);
+ index = 0;
+ }
+
+ return index;
+}
+/*
+ * this function is in charge of choosing the right backend for a given
+ * mapping tree node
+ * In case when several backends are used it is in charge of the spanning the
+ * request among all the backend or choosing the only backend to use depending
+ * on the type and scope of the LDAP operation
+ *
+ * index == -1 is used to specify that we want only the one best backend
+ * used for BASE search, ADD, DELETE, MODIFY
+ * index >0 means we are doing a SUBTREE or ONELEVEL search and that the be in
+ * that position must be returned
+ */
+static int mtn_get_be(mapping_tree_node *target_node, Slapi_PBlock *pb,
+ Slapi_Backend **be, int * index, Slapi_Entry **referral, char *errorbuf)
+{
+ Slapi_DN *target_sdn;
+ Slapi_Operation *op;
+ int result = LDAP_SUCCESS;
+ int override_referral = 0;
+ unsigned long op_type;
+ int flag_stop = 0;
+ struct slapi_componentid *cid = NULL;
+
+ if(mapping_tree_freed){
+ /* shut down detected */
+ return LDAP_OPERATIONS_ERROR;
+ }
+ /* Get usefull stuff like the type of operation, target dn */
+ slapi_pblock_get(pb, SLAPI_OPERATION, &op);
+ op_type = operation_get_type(op);
+ target_sdn = operation_get_target_spec (op);
+
+ if (target_node->mtn_state == MTN_DISABLED) {
+ if (errorbuf)
+ sprintf(errorbuf,
+ "Warning: Operation attempted on a disabled node : %s\n",
+ slapi_sdn_get_dn(target_node->mtn_subtree));
+ result = LDAP_OPERATIONS_ERROR;
+ return result;
+ }
+
+ slapi_pblock_get (pb, SLAPI_PLUGIN_IDENTITY, &cid);
+
+ override_referral =
+ ((cid != NULL) && (pw_get_componentID() != NULL) && (pw_get_componentID() == cid)) ||
+ operation_is_flag_set(op, OP_FLAG_LEGACY_REPLICATION_DN) || /* 4.0 lgacy update */
+ operation_is_flag_set(op, OP_FLAG_REPLICATED) || /* 5.0 replication update */
+ operation_is_flag_set(op, OP_FLAG_TOMBSTONE_ENTRY); /* 5.1 fix to enable tombstone delete on a R-O consumer */
+ if ((target_node->mtn_state == MTN_BACKEND) ||
+ (target_node->mtn_state == MTN_CONTAINER ) ||
+ ((target_node->mtn_state == MTN_REFERRAL_ON_UPDATE) &&
+ ((SLAPI_OPERATION_SEARCH == op_type)||(SLAPI_OPERATION_BIND == op_type) ||
+ (SLAPI_OPERATION_UNBIND == op_type) || (SLAPI_OPERATION_COMPARE == op_type))) ||
+ override_referral) {
+ if ((target_node == mapping_tree_root) ){
+ /* If we got here, then we couldn't find a matching node
+ * for the target. We'll use the default backend. Once
+ * we fully support the NULL suffix, we should do something more
+ * clever here.
+ */
+ *be = defbackend_get_backend();
+
+ } else {
+ if ((*index == -1) || (*index == 0)) {
+ /* In this case, we are doing
+ * a READ, ADD, MODIDY or DELETE on a single entry
+ * or we are starting a SEARCH
+ * if there is several possible backend we want to apply
+ * the distribution plugin
+ */
+ if (target_node->mtn_be_count <= 1) {
+ /* there is only one backend no choice possible */
+ *index = 0;
+ } else {
+ *index = mtn_get_be_distributed(pb, target_node,
+ target_sdn, &flag_stop);
+ }
+ }
+
+ if ((*index == -2) || (*index >= target_node->mtn_be_count)) {
+ /* we have already returned all backends -> return NULL */
+ *be = NULL;
+ *referral = NULL;
+ } else {
+ /* return next backend, increment index */
+ *be = target_node->mtn_be[*index];
+ if(*be==NULL) {
+ if (target_node->mtn_be_states[*index] == SLAPI_BE_STATE_DELETE) {
+ /* This MTN is being deleted */
+ *be = defbackend_get_backend();
+ } else {
+ /* This MTN has not been linked to its backend
+ * instance yet. */
+ target_node->mtn_be[*index] =
+ slapi_be_select_by_instance_name(
+ target_node->mtn_backend_names[*index]);
+ *be = target_node->mtn_be[*index];
+ if(*be==NULL) {
+ LDAPDebug(LDAP_DEBUG_ANY,
+ "Warning: Mapping tree node entry for %s point to "
+ "an unknown backend : %s\n",
+ slapi_sdn_get_dn(target_node->mtn_subtree),
+ target_node->mtn_backend_names[*index], 0);
+ /* Well there's still not backend instance for
+ * this MTN, so let's have the default backend
+ * deal with this.
+ */
+ *be = defbackend_get_backend();
+ }
+ }
+ }
+ if ((target_node->mtn_be_states) &&
+ (target_node->mtn_be_states[*index] == SLAPI_BE_STATE_OFFLINE)) {
+ LDAPDebug(LDAP_DEBUG_TRACE,
+ "Warning: Operation attempted on backend in OFFLINE "
+ "state : %s\n",
+ target_node->mtn_backend_names[*index], 0, 0);
+ result = LDAP_OPERATIONS_ERROR;
+ *be = defbackend_get_backend();
+ }
+ if (flag_stop)
+ *index = -2;
+ else
+ (*index)++;
+ }
+ }
+ *referral = NULL;
+ } else {
+ /* otherwise we must return the referral
+ * if ((target_node->mtn_state == MTN_REFERRAL) ||
+ * (target_node->mtn_state == MTN_REFERRAL_ON_UPDATE)) */
+
+ if (*index > 0) {
+ /* we have already returned this referral
+ * send back NULL to jump to next node
+ */
+ *be = NULL;
+ *referral = NULL;
+ result = LDAP_SUCCESS;
+ } else {
+ /* first time we hit this referral -> return it
+ * set the be variable to NULL to indicate we use a referral
+ * and increment index to rememeber later that we already
+ * returned this referral
+ */
+ *be = NULL;
+ *referral = (target_node->mtn_referral_entry ?
+ slapi_entry_dup(target_node->mtn_referral_entry) :
+ NULL);
+ (*index)++;
+ if (NULL == *referral) {
+ if (errorbuf) {
+ sprintf(errorbuf,
+ "Mapping tree node for %s is set to return a referral,"
+ " but no referral is configured for it",
+ slapi_sdn_get_ndn(target_node->mtn_subtree));
+ }
+ result = LDAP_OPERATIONS_ERROR;
+ } else {
+ result = LDAP_SUCCESS;
+ }
+ }
+ }
+
+ if (result == LDAP_SUCCESS) {
+ if (*be) {
+ slapi_log_error(SLAPI_LOG_ARGS, NULL,
+ "mapping tree selected backend : %s\n",
+ slapi_be_get_name(*be));
+ slapi_be_Rlock(*be);
+ } else if (*referral) {
+ slapi_log_error(SLAPI_LOG_ARGS, NULL,
+ "mapping tree selected referral at node : %s\n",
+ slapi_sdn_get_dn(target_node->mtn_subtree));
+ }
+ }
+
+ return result;
+}
+
+/*
+ * Description:
+ * Finds the best match for the targetdn from the children of parent. Uses
+ * slapi_sdn_issuffix and the number of rdns to pick the best node.
+ *
+ * Arguments:
+ * parent is a pointer to a mapping tree node.
+ * targetdn is the dn we're trying to find the best match for.
+ *
+ * Returns:
+ * A pointer to the child of parent that best matches the targetdn. NULL
+ * if there were no good matches.
+ */
+static mapping_tree_node *best_matching_child(mapping_tree_node *parent,
+ const Slapi_DN *targetdn)
+{
+ mapping_tree_node *highest_match_node = NULL;
+ mapping_tree_node *current;
+
+ if(mapping_tree_freed){
+ /* shutdown detected */
+ return NULL;
+ }
+ for (current = parent->mtn_children; current;
+ current = current->mtn_brother) {
+ if (slapi_sdn_issuffix(targetdn, current->mtn_subtree)) {
+ if ( (highest_match_node == NULL) ||
+ ((slapi_sdn_get_ndn_len(current->mtn_subtree)) >
+ slapi_sdn_get_ndn_len(highest_match_node->mtn_subtree)) ) {
+ highest_match_node = current;
+ }
+ }
+ }
+
+ return highest_match_node;
+}
+
+
+/*
+ * look for the exact mapping tree node corresponding to a given entry dn
+ */
+static mapping_tree_node *
+mtn_get_mapping_tree_node_by_entry(mapping_tree_node* node, const Slapi_DN *dn)
+{
+ mapping_tree_node *found_node = NULL;
+
+ if(mapping_tree_freed){
+ /* shutdown detected */
+ return NULL;
+ }
+
+ if (slapi_sdn_compare(node->mtn_subtree, dn) == 0)
+ {
+ return node;
+ }
+
+ if (node->mtn_children)
+ {
+ found_node = mtn_get_mapping_tree_node_by_entry(node->mtn_children, dn);
+ if (found_node)
+ return found_node;
+ }
+
+ if (node->mtn_brother)
+ {
+ found_node = mtn_get_mapping_tree_node_by_entry(node->mtn_brother, dn);
+ }
+ return found_node;
+}
+/*
+ * Description:
+ * Gets a mapping tree node that best matches the given dn. If the root
+ * node is returned and the target dn is not "", then no match was found.
+ *
+ * Arguments:
+ * dn is the target of the search.
+ *
+ * Returns:
+ * The best matching node for the dn
+ * if nothing match, NULL is returned
+ */
+mapping_tree_node *
+slapi_get_mapping_tree_node_by_dn(const Slapi_DN *dn)
+{
+ mapping_tree_node *current_best_match = mapping_tree_root;
+ mapping_tree_node *next_best_match = mapping_tree_root;
+
+ if(mapping_tree_freed){
+ /* shutdown detected */
+ return NULL;
+ }
+ /* Handle special case where the dn is "" and the mapping root
+ * does not belong to the frontend-internal (DSE_BACKEND);
+ * it has been assigned to a different backend.
+ * e.g: a container backend
+ */
+ if ( sdn_is_nulldn(dn) && mapping_tree_root && mapping_tree_root->mtn_be[0] &&
+ mapping_tree_root->mtn_be[0] != slapi_be_select_by_instance_name(DSE_BACKEND)) {
+ return( mapping_tree_root );
+ }
+
+ /* Start at the root and walk down the tree to find the best match. */
+ while (next_best_match) {
+ current_best_match = next_best_match;
+ next_best_match = best_matching_child(current_best_match, dn);
+ }
+
+ if (current_best_match == mapping_tree_root)
+ return NULL;
+ else
+ return current_best_match;
+}
+
+
+static mapping_tree_node *
+get_mapping_tree_node_by_name(mapping_tree_node * node, char * be_name)
+{
+ int i;
+ mapping_tree_node *found_node = NULL;
+
+ if(mapping_tree_freed){
+ /* shutdown detected */
+ return NULL;
+ }
+ /* now search the backend in this node */
+ i = 0;
+ while ( ( i < node->mtn_be_count) &&
+ (node->mtn_backend_names) &&
+ (node->mtn_backend_names[i]) &&
+ (strcmp(node->mtn_backend_names[i],be_name)))
+ {
+ i++;
+ }
+
+ if ((i < node->mtn_be_count) &&
+ (node->mtn_backend_names != NULL) &&
+ (node->mtn_backend_names[i] != NULL))
+ {
+ return node;
+ }
+
+ if (node->mtn_children)
+ {
+ found_node = get_mapping_tree_node_by_name(node->mtn_children, be_name);
+ if (found_node)
+ return found_node;
+ }
+
+ if (node->mtn_brother)
+ {
+ found_node = get_mapping_tree_node_by_name(node->mtn_brother, be_name);
+ }
+ return found_node;
+}
+
+/*
+ * Description: construct the dn of the configuration entry for the
+ * node originated at the root. The function just constructs
+ * the dn it does not verify that the entry actually exist.
+ * The format of the dn is
+ * cn="<normalized root>",cn=mapping tree,cn=config
+ *
+ * Arguments: root - root of the node
+ *
+ * Returns: dn of the configuration entry if successful and null otherwise.
+ */
+char*
+slapi_get_mapping_tree_node_configdn (const Slapi_DN *root)
+{
+ int len;
+ char *dn;
+
+ if(mapping_tree_freed){
+ /* shutdown detected */
+ return NULL;
+ }
+ if (root == NULL)
+ return NULL;
+
+ len = strlen (slapi_sdn_get_dn(root)) + strlen (MAPPING_TREE_BASE_DN) + 7; /* cn= + " + " + , + \0 */
+ dn = (char*)slapi_ch_malloc (len);
+ sprintf (dn, "cn=\"%s\",%s", slapi_sdn_get_dn(root), MAPPING_TREE_BASE_DN);
+
+ return dn;
+}
+
+/*
+ * Description: this function returns root of the subtree to which the node applies
+ *
+ * Arguments: node - mapping tree node
+ *
+ * Returns: root of the subtree if function is successful and NULL otherwise.
+ */
+
+const Slapi_DN* slapi_get_mapping_tree_node_root (const mapping_tree_node *node)
+{
+ if (node)
+ return node->mtn_subtree;
+ else
+ return NULL;
+}
+
+/* GB : there is a potential problems with this function
+ * when several backends are used
+ */
+PRBool slapi_mapping_tree_node_is_set (const mapping_tree_node *node, PRUint32 flag)
+{
+ if (flag & SLAPI_MTN_LOCAL)
+ return PR_TRUE;
+
+ if (flag & SLAPI_MTN_PRIVATE)
+ return ((node->mtn_be_count>0) && node->mtn_be && node->mtn_be[0] && node->mtn_private);
+
+ if (flag & SLAPI_MTN_READONLY)
+ return ((node->mtn_be_count>0) && node->mtn_be && node->mtn_be[0] && node->mtn_be[0]->be_readonly);
+
+ return PR_FALSE;
+}
+
+/*
+ * Description: this function returns root of the subtree to which the node applies
+ *
+ * Arguments: node
+ *
+ * Returns: dn of the parent of mapping tree node configuration entry.
+ */
+
+const char* slapi_get_mapping_tree_config_root ()
+{
+ return MAPPING_TREE_BASE_DN;
+}
+
+/*
+ * slapi_be_select() finds the backend that should be used to service dn.
+ * If no backend with an appropriate suffix is configured, the default backend
+ * is returned. This function never returns NULL.
+ */
+Slapi_Backend *
+slapi_be_select( const Slapi_DN *sdn ) /* JCM - The name of this should change??? */
+{
+ Slapi_Backend *be;
+ mapping_tree_node *node= slapi_get_mapping_tree_node_by_dn(sdn);
+ if(node!=NULL)
+ be= node->mtn_be[0];
+ else
+ be = NULL;
+
+ if(be==NULL)
+ be= defbackend_get_backend();
+
+ return be;
+}
+
+/* Check if the dn targets an internal reserved backends */
+int
+slapi_on_internal_backends(const Slapi_DN *sdn)
+{
+ char *backend_names[] = {"frontend-internal", "schema-internal"};
+ int internal = 1;
+ int numOfInternalBackends = 2;
+ int count;
+
+ Slapi_Backend *internal_be;
+
+ Slapi_Backend *be = slapi_be_select(sdn);
+
+
+ for (count=0; count < numOfInternalBackends ; ++count){
+ /* the internal backends are always in the begining of the list
+ * so should not be very inefficient
+ */
+ internal_be = slapi_be_select_by_instance_name(backend_names[count]);
+ if(be == internal_be){
+ return internal;
+ }
+ }
+ return 0;
+}
+
+/* Some of the operations are not allowed from the plugins
+ * but default to specialized use of those operations
+ * e.g rootDse search, NetscapeRoot searches
+ * cn=config, cn=schema etc
+ */
+
+int slapi_op_reserved(Slapi_PBlock *pb)
+{
+ int scope=LDAP_SCOPE_BASE;
+ int reservedOp=0;
+ int op_type;
+ Slapi_Operation *op = NULL;
+ Slapi_DN *target_sdn=NULL;
+
+ slapi_pblock_get(pb, SLAPI_OPERATION, &op);
+ slapi_pblock_get(pb, SLAPI_OPERATION_TYPE, &op_type);
+ /* Get the target for this op */
+ target_sdn = operation_get_target_spec (op);
+
+ if( op_type == SLAPI_OPERATION_SEARCH){
+ slapi_pblock_get(pb, SLAPI_SEARCH_SCOPE, &scope);
+ if( sdn_is_nulldn(target_sdn) && (scope == LDAP_SCOPE_BASE) ){
+ reservedOp = 1;
+ }
+
+ }
+
+ if(slapi_on_internal_backends(target_sdn)){
+ reservedOp = 1;
+ }
+
+ return reservedOp;
+}
+
+
+
+/*
+ * Returns the name of the Backend that contains specified DN,
+ * if only one matches. Otherwise returns NULL
+ * The name is pointing to the mapping tree structure
+ * and should not be altered.
+ */
+const char *
+slapi_mtn_get_backend_name( const Slapi_DN *sdn)
+{
+ mapping_tree_node *node= slapi_get_mapping_tree_node_by_dn(sdn);
+ if ((node != NULL) &&
+ (node->mtn_be_count == 1) &&
+ (node->mtn_backend_names != NULL))
+ /* There's only one name, return it */
+ return node->mtn_backend_names[0];
+ else
+ return NULL;
+}
+
+/* Check if the backend that contains specified DN exists */
+int
+slapi_be_exist(const Slapi_DN *sdn) /* JCM - The name of this should change??? */
+{
+ Slapi_Backend *def_be = defbackend_get_backend();
+ Slapi_Backend *be = slapi_be_select (sdn);
+
+ return (be != def_be);
+}
+
+/* The two following functions can be used to
+ * parse the list of the root suffix of the DIT
+ * Using
+ */
+Slapi_DN *
+slapi_get_first_suffix(void ** node, int show_private)
+{
+ mapping_tree_node * first_node = mapping_tree_root->mtn_children;
+ *node = (void * ) first_node ;
+ while (first_node && (first_node->mtn_private && (show_private == 0)))
+ first_node = first_node->mtn_brother;
+ return (first_node ? first_node->mtn_subtree : NULL);
+}
+
+Slapi_DN *
+slapi_get_next_suffix(void ** node, int show_private)
+{
+ mapping_tree_node * next_node = *node;
+
+ if (next_node == NULL)
+ return NULL;
+
+ next_node = next_node->mtn_brother;
+ while (next_node && (next_node->mtn_private && (show_private == 0)))
+ next_node = next_node->mtn_brother;
+ *node = next_node;
+ return (next_node ? next_node->mtn_subtree : NULL);
+}
+
+/* check if a suffix is a root of the DIT
+ * return 1 if yes, 0 if no
+ */
+int slapi_is_root_suffix(Slapi_DN * dn)
+{
+ void * node;
+ Slapi_DN * suffix = slapi_get_first_suffix (&node, 1);
+
+ while (suffix)
+ {
+ if ( slapi_sdn_compare(dn, suffix) == 0 )
+ return 1;
+ suffix = slapi_get_next_suffix(&node, 1);
+ }
+ return 0 ;
+}
+
+/*
+ * set referrals for the node
+ * notes :
+ * - referral is consumed by this function
+ * - node must exist before calling this function
+ * - mapping tree node state is not changed by this function
+ */
+int
+slapi_mtn_set_referral(const Slapi_DN *sdn, char ** referral)
+{
+ Slapi_PBlock pb;
+ Slapi_Mods smods;
+ int rc = LDAP_SUCCESS,i = 0, j = 0;
+ char * node_dn;
+ char **values = NULL;
+ int do_modify = 0;
+
+ slapi_mods_init (&smods, 0);
+ node_dn = slapi_get_mapping_tree_node_configdn(sdn);
+ if(!node_dn){
+ /* shutdown has been detected */
+ return LDAP_OPERATIONS_ERROR;
+ }
+
+ if ((referral == NULL) || (referral[0] == NULL))
+ {
+ /* NULL referral means we want to delete existing referral
+ */
+ slapi_mods_add(&smods, LDAP_MOD_DELETE, "nsslapd-referral", 0, NULL);
+ do_modify = 1;
+ }
+ else
+ {
+ int changes = 1;
+ int referralCount = 0;
+
+ for(; referral[referralCount]; referralCount++);
+ if ( (values = slapi_mtn_get_referral(sdn)) != NULL )
+ {
+ /* Check if there are differences between current values and values to be set */
+
+ for (i=0; values[i]; i++);
+ if (i == referralCount) {
+ changes = 0;
+ for (i=0;values[i];i++){
+ int found = 0;
+ for (j=0;referral[j];j++){
+ if (strcmp(values[i], referral[j]) == 0){
+ found = 1;
+ break;
+ }
+ }
+ if (!found) {
+ changes = 1;
+ break;
+ }
+ }
+ }
+
+ i=0;
+ while(values[i])
+ slapi_ch_free((void**)&values[i++]);
+ slapi_ch_free((void**)&values);
+
+ }
+ if (changes){
+ Slapi_Value *val;
+ Slapi_Value ** svals = NULL;
+
+ do_modify = 1;
+ for (j =0; referral[j];j++) {
+ val = slapi_value_new_string(referral[j]);
+ valuearray_add_value(&svals, val);
+ slapi_value_free(&val);
+ }
+ slapi_mods_add_mod_values(&smods, LDAP_MOD_REPLACE, "nsslapd-referral", svals);
+ valuearray_free(&svals);
+ }
+ }
+
+ if ( do_modify )
+ {
+ pblock_init (&pb);
+ slapi_modify_internal_set_pb (&pb, node_dn,
+ slapi_mods_get_ldapmods_byref(&smods), NULL,
+ NULL, (void *) plugin_get_default_component_id(), 0);
+ slapi_modify_internal_pb (&pb);
+
+ slapi_pblock_get (&pb, SLAPI_PLUGIN_INTOP_RESULT, &rc);
+ pblock_done(&pb);
+ }
+
+ slapi_mods_done(&smods);
+ slapi_ch_free((void **) &node_dn);
+
+ return rc;
+}
+
+/*
+ * Change the state of a mapping tree node entry
+ * notes :
+ * - sdn argument is the dn of the subtree of the DIT managed by this node
+ * not the dn of the mapping tree entry
+ * - mapping tree node must exist before calling this function
+ */
+int
+slapi_mtn_set_state(const Slapi_DN *sdn, char *state)
+{
+ Slapi_PBlock pb;
+ Slapi_Mods smods;
+ int rc = LDAP_SUCCESS;
+ char * node_dn;
+ char * value;
+
+ node_dn = slapi_get_mapping_tree_node_configdn(sdn);
+ if(!node_dn){
+ /* shutdown has been detected */
+ return LDAP_OPERATIONS_ERROR;
+ }
+
+ if ( (value = slapi_mtn_get_state(sdn)) != NULL )
+ {
+ if ( strcasecmp(value, state) == 0 )
+ {
+ /* Same state, don't change anything */
+ slapi_ch_free((void **) &value);
+ slapi_ch_free((void **) &node_dn);
+ return rc;
+ }
+ }
+
+ /* Otherwise, means that the state has changed, modify it */
+ slapi_mods_init (&smods, 1);
+ slapi_mods_add(&smods, LDAP_MOD_REPLACE, "nsslapd-state", strlen(state), state);
+ pblock_init (&pb);
+ slapi_modify_internal_set_pb (&pb, node_dn,
+ slapi_mods_get_ldapmods_byref(&smods), NULL,
+ NULL, (void *) plugin_get_default_component_id(), 0);
+ slapi_modify_internal_pb (&pb);
+
+ slapi_pblock_get (&pb, SLAPI_PLUGIN_INTOP_RESULT, &rc);
+
+ slapi_mods_done(&smods);
+ slapi_ch_free((void **) &node_dn);
+ pblock_done(&pb);
+ slapi_ch_free((void **) &value);
+
+ return rc;
+}
+
+/*
+ returns a copy of the attr - the caller must slapi_attr_free it
+*/
+Slapi_Attr *
+mtn_get_attr(char* node_dn, char * type)
+{
+ Slapi_PBlock pb;
+ int res = 0;
+ Slapi_Entry **entries = NULL;
+ Slapi_Attr *attr = NULL;
+ Slapi_Attr *ret_attr = NULL;
+ char **attrs = NULL;
+
+ attrs = (char **)slapi_ch_calloc(2, sizeof(char *));
+ attrs[0] = slapi_ch_strdup(type);
+ pblock_init(&pb);
+ slapi_search_internal_set_pb(&pb, node_dn, LDAP_SCOPE_BASE,
+ "objectclass=nsMappingTree", attrs, 0, NULL, NULL,
+ (void *) plugin_get_default_component_id(), 0);
+ slapi_search_internal_pb(&pb);
+ slapi_pblock_get(&pb, SLAPI_PLUGIN_INTOP_RESULT, &res);
+
+ if (res != LDAP_SUCCESS) {
+ goto done;
+ }
+
+ slapi_pblock_get(&pb, SLAPI_PLUGIN_INTOP_SEARCH_ENTRIES, &entries);
+ if (NULL == entries || NULL == entries[0]) {
+ goto done;
+ }
+
+ /* always at most one entry entries[0] */
+ res = slapi_entry_attr_find(entries[0], type, &attr);
+ if (res == 0)
+ /* we need to make a copy here so we can free the search results */
+ ret_attr = slapi_attr_dup(attr);
+
+ slapi_free_search_results_internal(&pb);
+
+done:
+ slapi_ch_free((void **)&attrs[0]);
+ slapi_ch_free((void **)&attrs);
+ pblock_done(&pb);
+ return ret_attr;
+}
+
+/*
+ * Get the referral associated to the mapping tree node entry
+ * notes :
+ * - sdn argument is the dn of the subtree of the DIT managed by this node
+ * not the dn of the mapping tree entry
+ * - return NULL if no referral
+ * - caller is reponsible for freeing the returned referrals
+ */
+char **
+slapi_mtn_get_referral(const Slapi_DN *sdn)
+{
+ int i, hint, nb;
+ char * node_dn;
+ Slapi_Attr *attr;
+ char ** referral = NULL;
+ Slapi_Value *val = NULL;
+
+ node_dn = slapi_get_mapping_tree_node_configdn(sdn);
+ if(!node_dn){
+ /* shutdown has been detected */
+ return NULL;
+ }
+
+ attr = mtn_get_attr(node_dn, "nsslapd-referral");
+
+ if (attr)
+ {
+ /* if there are some referrals set in the entry build a list
+ * to be returned to the caller
+ */
+ slapi_attr_get_numvalues(attr, &nb);
+ referral = (char**) slapi_ch_malloc(sizeof(char*) * (nb+1));
+ hint = slapi_attr_first_value(attr, &val);
+ i = 0;
+
+ while (val)
+ {
+ referral[i++] = slapi_ch_strdup(slapi_value_get_string(val));
+ hint = slapi_attr_next_value(attr, hint, &val);
+ }
+ referral[i] = NULL;
+ slapi_attr_free(&attr);
+ }
+
+ slapi_ch_free((void **) &node_dn);
+ return referral;
+}
+
+/*
+ * Get the state of a mapping tree node entry
+ * notes :
+ * - sdn argument is the dn of the subtree of the DIT managed by this node
+ * not the dn of the mapping tree entry
+ * - the state is return in a newly allocated string that must be freed by
+ * the caller
+ */
+char *
+slapi_mtn_get_state(const Slapi_DN *sdn)
+{
+ int hint;
+ char * node_dn;
+ Slapi_Attr *attr;
+ char * state = NULL;
+ Slapi_Value *val = NULL;
+
+ node_dn = slapi_get_mapping_tree_node_configdn(sdn);
+ if(!node_dn){
+ /* shutdown has been detected */
+ return NULL;
+ }
+
+ attr = mtn_get_attr(node_dn, "nsslapd-state");
+
+ if (attr)
+ {
+ /* entry state was found */
+ hint = slapi_attr_first_value(attr, &val);
+ state = slapi_ch_strdup(slapi_value_get_string(val));
+ slapi_attr_free(&attr);
+ }
+
+ slapi_ch_free((void **) &node_dn);
+ return state;
+}
+
+static void
+mtn_internal_be_set_state(Slapi_Backend *be, int state)
+{
+ mapping_tree_node * node;
+ char * be_name;
+ int i;
+ int change_callback = 0;
+ int old_state;
+
+ mtn_wlock();
+ be_name = slapi_ch_strdup(slapi_be_get_name(be));
+ node = get_mapping_tree_node_by_name(mapping_tree_root, be_name);
+ if (node == NULL)
+ {
+ LDAPDebug(LDAP_DEBUG_TRACE,
+ "Warning: backend %s is not declared in mapping tree\n",
+ be_name, 0 ,0);
+ goto done;
+ }
+
+
+ /* now search the backend in this node */
+ i = 0;
+ while ( (i < node->mtn_be_count) &&
+ (node->mtn_backend_names) &&
+ (node->mtn_backend_names[i]) &&
+ (strcmp(node->mtn_backend_names[i],be_name)))
+ {
+ i++;
+ }
+
+ if ( (i >= node->mtn_be_count) || (node->mtn_backend_names == NULL) ||
+ (node->mtn_backend_names[i] == NULL) )
+ {
+ /* backend is not declared in the mapping tree node
+ * print out a warning
+ */
+ LDAPDebug(LDAP_DEBUG_TRACE,
+ "Warning: backend %s is not declared in mapping node entry\n",
+ be_name, 0 ,0);
+ goto done;
+ }
+
+ change_callback = 1;
+ old_state = node->mtn_be_states[i];
+
+ /* OK we found the backend at last, now do the real job: set the state */
+ switch (state)
+ {
+ case SLAPI_BE_STATE_OFFLINE:
+ node->mtn_be[i] = be;
+ node->mtn_be_states[i] = SLAPI_BE_STATE_OFFLINE;
+ break;
+
+ case SLAPI_BE_STATE_ON:
+ node->mtn_be[i] = be;
+ node->mtn_be_states[i] = SLAPI_BE_STATE_ON;
+ break;
+
+ case SLAPI_BE_STATE_DELETE:
+ node->mtn_be[i] = NULL;
+ node->mtn_be_states[i] = SLAPI_BE_STATE_DELETE;
+ break;
+ }
+
+done:
+ mtn_unlock();
+ if (change_callback)
+ mtn_be_state_change(be_name, old_state, state);
+ slapi_ch_free( (void **) &be_name);
+}
+
+/*
+ * This procedure must be called by previously stopped backends
+ * to signal that they have started and are ready to process requests
+ * The backend must be fully ready to handle requests before calling this
+ * procedure
+ * At startup tiem it is not mandatory for the backends to
+ * call this procedure: backends are assumed on by default
+ */
+void
+slapi_mtn_be_started(Slapi_Backend *be)
+{
+ /* Find the node where this backend stay
+ * then update the backend structure
+ * In the long term, the backend should have only one suffix and
+ * stay in only one node as for now, check all suffixes
+ * Rq : since mapping tree is initiatized very soon in the server
+ * startup, we can be sure at that time that the mapping
+ * tree is initialized
+ */
+
+ mtn_internal_be_set_state(be, SLAPI_BE_STATE_ON);
+}
+
+/* these procedure can be called when backends need to be put in maintenance mode
+ * after call to slapi_mtn_be_disable, the backend will still be known
+ * by a server but the mapping tree won't route requests to it anymore
+ * The slapi_mtn_be_enable function enable to route requests to the backend
+ * again
+ * the slapi_mtn_be_disable function only returns when there is no more
+ * request in progress in the backend
+ */
+void
+slapi_mtn_be_disable(Slapi_Backend *be)
+{
+ mtn_internal_be_set_state(be, SLAPI_BE_STATE_OFFLINE);
+
+ /* the two following lines can seem weird, but they allow to check that no
+ * LDAP operation is in progress on the backend
+ */
+ slapi_be_Wlock(be);
+ slapi_be_Unlock(be);
+}
+
+void
+slapi_mtn_be_enable(Slapi_Backend *be)
+{
+ mtn_internal_be_set_state(be, SLAPI_BE_STATE_ON);
+}
+
+/*
+ * This procedure must be called by backends before stopping
+ * if some operations are in progress when this procedure
+ * is called, this procedure will block until completion
+ * of these operations
+ * The backend must wait return from this procedure before stopping operation
+ * Backends must serve operation until the return from this procedure.
+ * Once this procedure return they will not be issued request anymore
+ * and they have been removed from the server list of backends
+ * It is also the bakend responsability to free the Slapi_Backend structures
+ * that was given by slapi_be_new at startup time.
+ * Should the backend start again, it would need to issue slapi_be_new again
+ */
+void
+slapi_mtn_be_stopping(Slapi_Backend *be)
+{
+ mtn_internal_be_set_state(be, SLAPI_BE_STATE_DELETE);
+
+ /* the two following lines can seem weird, but they allow to check that no
+ * LDAP operation is in progress on the backend
+ */
+ slapi_be_Wlock(be);
+ slapi_be_Unlock(be);
+
+ slapi_be_stopping(be);
+}
+
+/*
+ * Switch a backend into read-only mode, or back to read-write mode.
+ * To switch to read-only mode, we need to wait for all pending operations
+ * to finish.
+ */
+void
+slapi_mtn_be_set_readonly(Slapi_Backend *be, int readonly)
+{
+ if (readonly) {
+ slapi_be_Wlock(be);
+ slapi_be_set_readonly(be, 1);
+ slapi_be_Unlock(be);
+ } else {
+ slapi_be_set_readonly(be, 0);
+ }
+}
+
+
+#ifdef DEBUG
+static int lock_count = 0;
+#endif
+
+void mtn_wlock()
+{
+ PR_RWLock_Wlock(myLock);
+#ifdef DEBUG
+ lock_count--;
+ LDAPDebug(LDAP_DEBUG_ARGS, "mtn_wlock : lock count : %d\n", lock_count, 0, 0);
+#endif
+}
+
+void mtn_lock()
+{
+ PR_RWLock_Rlock(myLock);
+#ifdef DEBUG
+ lock_count++;
+ LDAPDebug(LDAP_DEBUG_ARGS, "mtn_lock : lock count : %d\n", lock_count, 0, 0);
+#endif
+}
+
+void mtn_unlock()
+{
+
+#ifdef DEBUG
+ if (lock_count > 0)
+ lock_count--;
+ else if (lock_count < 0)
+ lock_count++;
+ else
+ lock_count = (int) 11111111; /* this happening means problems */
+ LDAPDebug(LDAP_DEBUG_ARGS, "mtn_unlock : lock count : %d\n", lock_count, 0, 0);
+#endif
+ PR_RWLock_Unlock(myLock);
+}
+
+#ifdef TEST_FOR_REGISTER_CHANGE
+void my_test_fnct1(void *handle, char *be_name, int old_state, int new_state)
+{
+ slapi_log_error(SLAPI_LOG_ARGS, NULL,
+ "my_test_fnct1 : handle %d, be %s, old state %d, new state %d\n",
+ handle,be_name, old_state, new_state);
+
+ if (old_state == 2)
+ slapi_unregister_backend_state_change(handle);
+}
+
+void my_test_fnct2(void *handle, char *be_name, int old_state, int new_state)
+{
+ slapi_log_error(SLAPI_LOG_ARGS, NULL,
+ "my_test_fnct2 : handle %d, be %s, old state %d, new state %d\n",
+ handle, be_name, old_state, new_state);
+}
+
+void test_register()
+{
+ slapi_register_backend_state_change((void *) 1234, my_test_fnct1);
+ slapi_register_backend_state_change((void *) 4321, my_test_fnct2);
+}
+#endif
+
+#ifdef DEBUG
+static void dump_mapping_tree(mapping_tree_node *parent, int depth)
+{
+ mapping_tree_node *current = NULL;
+ static char dump_indent[256];
+ int i;
+
+ if (depth == 0)
+ {
+ LDAPDebug(LDAP_DEBUG_ANY, "dump_mapping_tree\n", 0, 0, 0);
+ }
+ dump_indent[0] = '\0';
+ for (i = 0; i < depth; i++)
+ strcat(dump_indent, " ");
+ for (current = parent->mtn_children; current;
+ current = current->mtn_brother)
+ {
+ if (strlen(current->mtn_subtree->dn) == 0)
+ {
+ LDAPDebug(LDAP_DEBUG_ANY, "MT_DUMP: %s%s (0x%x)\n",
+ dump_indent, "none", current);
+ }
+ else
+ {
+ LDAPDebug(LDAP_DEBUG_ANY, "MT_DUMP: %s%s (0x%x)\n",
+ dump_indent, current->mtn_subtree->dn, current);
+ }
+ dump_mapping_tree(current, depth+1);
+ }
+ return;
+}
+#endif
diff --git a/ldap/servers/slapd/match.c b/ldap/servers/slapd/match.c
new file mode 100644
index 00000000..9ff2305e
--- /dev/null
+++ b/ldap/servers/slapd/match.c
@@ -0,0 +1,237 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+/*
+ * match.c
+ *
+ * routines to "register" matching rules with the server
+ *
+ *
+ *
+ *
+ */
+
+#include "slap.h"
+
+
+struct matchingRuleList *g_get_global_mrl(void);
+void g_set_global_mrl(struct matchingRuleList *newglobalmrl);
+int slapi_matchingrule_register(Slapi_MatchingRuleEntry *mrule);
+int slapi_matchingrule_unregister(char *oid);
+Slapi_MatchingRuleEntry *slapi_matchingrule_new(void);
+void slapi_matchingrule_free(Slapi_MatchingRuleEntry **mrEntry,
+ int freeMembers);
+int slapi_matchingrule_get(Slapi_MatchingRuleEntry *mr, int arg, void *value);
+int slapi_matchingrule_set(Slapi_MatchingRuleEntry *mr, int arg, void *value);
+
+
+static int _mr_alloc_new(struct matchingRuleList **mrl);
+
+static struct matchingRuleList *global_mrl=NULL;
+
+struct matchingRuleList*
+g_get_global_mrl(void)
+{
+ return global_mrl;
+}
+
+void
+g_set_global_mrl(struct matchingRuleList *newglobalmrl)
+{
+ global_mrl = newglobalmrl;
+}
+
+int
+slapi_matchingrule_set(Slapi_MatchingRuleEntry *mr, int arg, void *value)
+{
+ if(NULL == mr) {
+ return(-1);
+ }
+ switch(arg) {
+ case SLAPI_MATCHINGRULE_NAME:
+ {
+ mr->mr_name = (char *)value;
+ break;
+ }
+ case SLAPI_MATCHINGRULE_OID:
+ {
+ mr->mr_oid = (char *)value;
+ break;
+ }
+ case SLAPI_MATCHINGRULE_DESC:
+ {
+ mr->mr_desc = (char *)value;
+ break;
+ }
+ case SLAPI_MATCHINGRULE_SYNTAX:
+ {
+ mr->mr_syntax = (char *)value;
+ break;
+ }
+ case SLAPI_MATCHINGRULE_OBSOLETE:
+ {
+ mr->mr_obsolete = *((int *)value);
+ break;
+ }
+ default:
+ {
+ break;
+ }
+ }
+ return(0);
+}
+
+int
+slapi_matchingrule_get(Slapi_MatchingRuleEntry *mr, int arg, void *value)
+{
+ if((NULL == mr) || (NULL == value)) {
+ return(-1);
+ }
+ switch(arg) {
+ case SLAPI_MATCHINGRULE_NAME:
+ {
+ (*(char **)value) = mr->mr_name;
+ break;
+ }
+ case SLAPI_MATCHINGRULE_OID:
+ {
+ (*(char **)value) = mr->mr_oid;
+ break;
+ }
+ case SLAPI_MATCHINGRULE_DESC:
+ {
+ (*(char **)value) = mr->mr_desc;
+ break;
+ }
+ case SLAPI_MATCHINGRULE_SYNTAX:
+ {
+ (*(char **)value) = mr->mr_syntax;
+ break;
+ }
+ case SLAPI_MATCHINGRULE_OBSOLETE:
+ {
+ (*(int *)value) = mr->mr_obsolete;
+ break;
+ }
+ default:
+ {
+ return(-1);
+ }
+ }
+ return(0);
+}
+
+Slapi_MatchingRuleEntry *
+slapi_matchingrule_new(void)
+{
+ Slapi_MatchingRuleEntry *mrEntry=NULL;
+ mrEntry = (Slapi_MatchingRuleEntry *)
+ slapi_ch_calloc(1, sizeof(Slapi_MatchingRuleEntry));
+ return(mrEntry);
+}
+
+void
+slapi_matchingrule_free(Slapi_MatchingRuleEntry **mrEntry,
+ int freeMembers)
+{
+ if((NULL == mrEntry) || (NULL == *mrEntry)) {
+ return;
+ }
+ if(freeMembers) {
+ slapi_ch_free((void **)&((*mrEntry)->mr_name));
+ slapi_ch_free((void **)&((*mrEntry)->mr_oid));
+ slapi_ch_free((void **)&((*mrEntry)->mr_desc));
+ slapi_ch_free((void **)&((*mrEntry)->mr_syntax));
+ slapi_ch_free((void **)&((*mrEntry)->mr_oidalias));
+ }
+ slapi_ch_free((void **)mrEntry);
+ return;
+}
+
+static int
+_mr_alloc_new(struct matchingRuleList **mrl)
+{
+ if(!mrl) {
+ return(-1);
+ }
+ *mrl = NULL;
+ *mrl = (struct matchingRuleList *)
+ slapi_ch_calloc(1, sizeof(struct matchingRuleList));
+
+
+ (*mrl)->mr_entry = (Slapi_MatchingRuleEntry *)
+ slapi_ch_calloc(1, sizeof(Slapi_MatchingRuleEntry));
+ return(0);
+}
+
+#if 0
+static int
+_mr_free(struct matchingRuleList **mrl /*, int freeEntry */)
+{
+ slapi_ch_free((void **)mrl);
+ return(0);
+}
+#endif
+
+int slapi_matchingrule_register(Slapi_MatchingRuleEntry *mrule)
+{
+ struct matchingRuleList *mrl=NULL;
+ struct matchingRuleList *newmrl=NULL;
+ int rc=0;
+
+ if(NULL == mrule) {
+ return(-1);
+ }
+ if((rc = _mr_alloc_new(&newmrl)) != 0) {
+ return(-1);
+ }
+ if(NULL != mrule->mr_name) {
+ newmrl->mr_entry->mr_name =
+ slapi_ch_strdup((char *) mrule->mr_name);
+ }
+ if(NULL != mrule->mr_oid) {
+ newmrl->mr_entry->mr_oid =
+ slapi_ch_strdup((char *) mrule->mr_oid);
+ }
+ if(NULL != mrule->mr_oidalias) {
+ newmrl->mr_entry->mr_oidalias =
+ slapi_ch_strdup((char *) mrule->mr_oidalias);
+ }
+ if(NULL != mrule->mr_desc) {
+ newmrl->mr_entry->mr_desc =
+ slapi_ch_strdup((char *) mrule->mr_desc);
+ }
+ if(NULL != mrule->mr_syntax) {
+ newmrl->mr_entry->mr_syntax =
+ slapi_ch_strdup((char *) mrule->mr_syntax);
+ }
+ newmrl->mr_entry->mr_obsolete = mrule->mr_obsolete;
+
+ for(mrl = g_get_global_mrl();
+ ((NULL != mrl) && (NULL != mrl->mrl_next));
+ mrl = mrl->mrl_next);
+
+ if(NULL == mrl) {
+ g_set_global_mrl(newmrl);
+ mrl = newmrl;
+ }
+ mrl->mrl_next = newmrl;
+ newmrl->mrl_next = NULL;
+ return(LDAP_SUCCESS);
+}
+
+int slapi_matchingrule_unregister(char *oid)
+{
+ /*
+ * Currently, not implemented.
+ * For now, the matching rules are read at startup and cannot be modified.
+ * If and when, we do allow dynamic modifications, this routine will
+ * have to do some work.
+ */
+ return(0);
+}
+
+
+
diff --git a/ldap/servers/slapd/mkDBErrStrs.pl b/ldap/servers/slapd/mkDBErrStrs.pl
new file mode 100755
index 00000000..c93cf261
--- /dev/null
+++ b/ldap/servers/slapd/mkDBErrStrs.pl
@@ -0,0 +1,83 @@
+#!/usr/local/bin/perl
+#
+# BEGIN COPYRIGHT BLOCK
+# Copyright 2001 Sun Microsystems, Inc.
+# Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+# All rights reserved.
+# END COPYRIGHT BLOCK
+#
+#
+# Perl script to generate dberrstrs.h, which is used in errormap.c.
+#
+
+sub numerically { $a <=> $b; }
+
+$dbdir = "";
+$isNT = 0;
+$i = 0;
+$outdir = "";
+
+while ($i <= $#ARGV) {
+ if ("$ARGV[$i]" eq "-nt" || "$ARGV[$i]" eq "-NT") { # NT
+ $isNT = 1;
+ } elsif ("$ARGV[$i]" eq "-o" || "$ARGV[$i]" eq "-O") { # output dir
+ $i++;
+ $outdir = $ARGV[$i];
+ } elsif ("$ARGV[$i]" eq "-i" || "$ARGV[$i]" eq "-I") { # input db dir
+ $i++;
+ $dbdir = $ARGV[$i];
+ }
+ $i++;
+}
+
+if ($dbdir eq "") {
+ print(STDERR "Usage: $0 [-nt] <db_dir_path>\n");
+ exit(1);
+}
+
+if ($isNT == 1) {
+ $dirsep = "\\";
+} else {
+ $dirsep = "/";
+}
+
+$dbh = sprintf("%s%sdb.h", $dbdir, $dirsep);
+
+open(FOO, $dbh) || die "Cannot open $dbh\n";
+@lines = <FOO>;
+close(FOO);
+
+$i = 0;
+$j = 0;
+while ($i < $#lines) {
+ chop($lines[$i]);
+ if ($lines[$i] =~ /^#define[ ][_A-Z]*[ ]*\(-[0-9]*/) {
+ ($h, $t) = split(/\(/, $lines[$i], 2);
+ ($num[$j], $tt) = split(/\)/, $t, 2);
+ ($h, $ttt) = split(/\/\* /, $tt, 2);
+ ($errstr, $tttt) = split(/ \*\//, $ttt, 2);
+ if ($errstr ne "") {
+ $errstr =~ s/\"/\\\"/g; # Escape quotes
+ $numStrPair{$num[$j]} = $errstr;
+ }
+ $j++;
+ }
+ $i++;
+}
+
+sort numerically num;
+
+if ($outdir eq "") {
+ $myheader = "dberrstrs.h";
+} else {
+ $myheader = sprintf("%s%sdberrstrs.h", $outdir, $dirsep);
+}
+
+open(FOO, "> $myheader") || die "Cannot open $myheader\n";
+print( FOO "/* DO NOT EDIT: This is an automatically generated file by $0 */\n" );
+$i = 0;
+while ($i < $j) {
+ print( FOO "{$num[$i],\t\"$numStrPair{$num[$i]}\"},\n" );
+ $i++;
+}
+close(FOO);
diff --git a/ldap/servers/slapd/modify.c b/ldap/servers/slapd/modify.c
new file mode 100644
index 00000000..56c4de7c
--- /dev/null
+++ b/ldap/servers/slapd/modify.c
@@ -0,0 +1,966 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+/*
+ * 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 <stdio.h>
+#include <string.h>
+#include <time.h>
+#include <string.h>
+#include <sys/types.h>
+#ifndef _WIN32
+#include <sys/socket.h>
+#endif
+#include "slap.h"
+#include "pratom.h"
+#if defined(irix) || defined(aix) || defined(_WIN32)
+#include <time.h>
+#endif
+
+/* Forward declarations */
+static int modify_internal_pb (Slapi_PBlock *pb);
+static void op_shared_modify (Slapi_PBlock *pb, int pw_change, char *old_pw);
+static void remove_mod (Slapi_Mods *smods, const char *type, Slapi_Mods *smod_unhashed);
+static int op_shared_allow_pw_change (Slapi_PBlock *pb, LDAPMod *mod, char **old_pw);
+
+#ifdef LDAP_DEBUG
+static const char*
+mod_op_image (int op)
+{
+ switch (op & ~LDAP_MOD_BVALUES) {
+ case LDAP_MOD_ADD: return "add";
+ case LDAP_MOD_DELETE: return "delete";
+ case LDAP_MOD_REPLACE: return "replace";
+ default: break;
+ }
+ return "???";
+}
+#endif
+
+/* an AttrCheckFunc function should return an LDAP result code (LDAP_SUCCESS if all goes well). */
+typedef int (*AttrCheckFunc)(const char *attr_name, char *value, long minval, long maxval, char *errorbuf);
+
+static struct attr_value_check {
+ const char *attr_name; /* the name of the attribute */
+ AttrCheckFunc checkfunc;
+ long minval;
+ long maxval;
+} AttrValueCheckList[] = {
+ {CONFIG_PW_SYNTAX_ATTRIBUTE, attr_check_onoff, 0, 0},
+ {CONFIG_PW_CHANGE_ATTRIBUTE, attr_check_onoff, 0, 0},
+ {CONFIG_PW_LOCKOUT_ATTRIBUTE, attr_check_onoff, 0, 0},
+ {CONFIG_PW_MUSTCHANGE_ATTRIBUTE, attr_check_onoff, 0, 0},
+ {CONFIG_PW_EXP_ATTRIBUTE, attr_check_onoff, 0, 0},
+ {CONFIG_PW_UNLOCK_ATTRIBUTE, attr_check_onoff, 0, 0},
+ {CONFIG_PW_HISTORY_ATTRIBUTE, attr_check_onoff, 0, 0},
+ {CONFIG_PW_MINAGE_ATTRIBUTE, check_pw_minage_value, -1, -1},
+ {CONFIG_PW_WARNING_ATTRIBUTE, attr_check_minmax, 0, -1},
+ {CONFIG_PW_MINLENGTH_ATTRIBUTE, attr_check_minmax, 2, 512},
+ {CONFIG_PW_MAXFAILURE_ATTRIBUTE, attr_check_minmax, 1, 32767},
+ {CONFIG_PW_INHISTORY_ATTRIBUTE, attr_check_minmax, 2, 24},
+ {CONFIG_PW_LOCKDURATION_ATTRIBUTE, check_pw_lockduration_value, -1, -1},
+ {CONFIG_PW_RESETFAILURECOUNT_ATTRIBUTE, check_pw_resetfailurecount_value, -1, -1},
+ {CONFIG_PW_GRACELIMIT_ATTRIBUTE, attr_check_minmax, 0, -1},
+ {CONFIG_PW_STORAGESCHEME_ATTRIBUTE, check_pw_storagescheme_value, -1, -1}
+};
+
+/* This function is called to process operation that come over external connections */
+void
+do_modify( Slapi_PBlock *pb )
+{
+ Slapi_Operation *operation;
+ BerElement *ber;
+ char *last, *type;
+ unsigned long tag, len;
+ LDAPMod *mod;
+ LDAPMod **mods;
+ Slapi_Mods smods;
+ int err;
+ int pw_change = 0; /* 0= no password change */
+ int ignored_some_mods = 0;
+ char *old_pw = NULL; /* remember the old password */
+ char *dn;
+ LDAPControl **ctrlp = NULL;
+
+ LDAPDebug( LDAP_DEBUG_TRACE, "do_modify\n", 0, 0, 0 );
+
+ slapi_pblock_get( pb, SLAPI_OPERATION, &operation);
+ ber = operation->o_ber;
+
+ slapi_pblock_get(pb, SLAPI_REQCONTROLS, &ctrlp);
+
+ /* count the modify request */
+ PR_AtomicIncrement(g_get_global_snmp_vars()->ops_tbl.dsModifyEntryOps);
+
+ /*
+ * 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( ber, "{a", &dn ) == LBER_ERROR )
+ {
+ LDAPDebug( LDAP_DEBUG_ANY,
+ "ber_scanf failed (op=Modify; params=DN)\n", 0, 0, 0 );
+ op_shared_log_error_access (pb, "MOD", "???", "decoding error");
+ send_ldap_result( pb, LDAP_PROTOCOL_ERROR, NULL, NULL, 0,
+ NULL );
+ return;
+ }
+ }
+
+ LDAPDebug( LDAP_DEBUG_ARGS, "do_modify: dn (%s)\n", dn, 0, 0 );
+
+ slapi_pblock_set( pb, SLAPI_REQUESTOR_ISROOT, &pb->pb_op->o_isroot);
+ slapi_pblock_set( pb, SLAPI_ORIGINAL_TARGET, dn );
+
+ /* collect modifications & save for later */
+ slapi_mods_init(&smods, 0);
+ for ( tag = ber_first_element( ber, &len, &last );
+ tag != LBER_ERROR && tag != LBER_END_OF_SEQORSET;
+ tag = ber_next_element( ber, &len, last ) )
+ {
+ long long_mod_op;
+ mod = (LDAPMod *) slapi_ch_malloc( sizeof(LDAPMod) );
+ mod->mod_bvalues = NULL;
+
+ if ( ber_scanf( ber, "{i{a[V]}}", &long_mod_op, &type,
+ &mod->mod_bvalues ) == LBER_ERROR )
+ {
+ op_shared_log_error_access (pb, "MOD", dn, "decoding error");
+ send_ldap_result( pb, LDAP_PROTOCOL_ERROR, NULL,
+ "decoding error", 0, NULL );
+ slapi_ch_free((void **)&mod);
+ goto free_and_return;
+ }
+ mod->mod_op = long_mod_op;
+ mod->mod_type = slapi_attr_syntax_normalize(type);
+ if ( !mod->mod_type || !*mod->mod_type ) {
+ char ebuf[BUFSIZ];
+ PR_snprintf (ebuf, BUFSIZ, "invalid type '%s'", type);
+ ebuf[BUFSIZ-1] = '\0';
+ op_shared_log_error_access (pb, "MOD", dn, ebuf);
+ send_ldap_result( pb, LDAP_INVALID_SYNTAX, NULL, ebuf, 0, NULL );
+ slapi_ch_free((void **)&type);
+ ber_bvecfree(mod->mod_bvalues);
+ slapi_ch_free((void **)&mod);
+ goto free_and_return;
+ }
+ slapi_ch_free((void **)&type);
+
+ if ( mod->mod_op != LDAP_MOD_ADD &&
+ mod->mod_op != LDAP_MOD_DELETE &&
+ mod->mod_op != LDAP_MOD_REPLACE )
+ {
+ op_shared_log_error_access (pb, "MOD", dn, "unrecognized modify operation");
+ send_ldap_result( pb, LDAP_PROTOCOL_ERROR, NULL,
+ "unrecognized modify operation", 0, NULL );
+ ber_bvecfree(mod->mod_bvalues);
+ slapi_ch_free((void **)&(mod->mod_type));
+ slapi_ch_free((void **)&mod);
+ goto free_and_return;
+ }
+
+ if ( mod->mod_bvalues == NULL
+ && mod->mod_op != LDAP_MOD_DELETE
+ && mod->mod_op != LDAP_MOD_REPLACE )
+ {
+ op_shared_log_error_access (pb, "MOD", dn, "no values given");
+ send_ldap_result( pb, LDAP_PROTOCOL_ERROR, NULL,
+ "no values given", 0, NULL );
+ ber_bvecfree(mod->mod_bvalues);
+ slapi_ch_free((void **)&(mod->mod_type));
+ slapi_ch_free((void **)&mod);
+ goto free_and_return;
+ }
+
+ /* check if user is allowed to modify the specified attribute */
+ if (!op_shared_is_allowed_attr (mod->mod_type, pb->pb_conn->c_isreplication_session))
+ {
+ /* for now we just ignore attributes that client is not allowed
+ to modify so not to break existing clients */
+ ++ignored_some_mods;
+ ber_bvecfree(mod->mod_bvalues);
+ slapi_ch_free((void **)&(mod->mod_type));
+ slapi_ch_free((void **)&mod);
+ continue;
+ /* send_ldap_result( pb, LDAP_UNWILLING_TO_PERFORM, NULL, NULL, 0, NULL );
+ goto free_and_return; */
+ }
+
+ /* check for password change */
+ if ( mod->mod_bvalues != NULL &&
+ strcasecmp( mod->mod_type, SLAPI_USERPWD_ATTR ) == 0 ){
+ if ( (err = get_ldapmessage_controls( pb, ber, NULL )) != 0 ) {
+ op_shared_log_error_access (pb, "MOD", dn, "failed to decode LDAP controls");
+ send_ldap_result( pb, err, NULL, NULL, 0, NULL );
+ goto free_and_return;
+ }
+ pw_change = op_shared_allow_pw_change (pb, mod, &old_pw);
+ if (pw_change == -1)
+ {
+ ber_bvecfree(mod->mod_bvalues);
+ slapi_ch_free((void **)&(mod->mod_type));
+ slapi_ch_free((void **)&mod);
+ goto free_and_return;
+ }
+ }
+
+ mod->mod_op |= LDAP_MOD_BVALUES;
+ slapi_mods_add_ldapmod (&smods, mod);
+ }
+
+ if ( tag == LBER_ERROR && !ctrlp )
+ {
+ op_shared_log_error_access (pb, "MOD", dn, "decoding error");
+ send_ldap_result( pb, LDAP_PROTOCOL_ERROR, NULL, "decoding error", 0, NULL );
+ goto free_and_return;
+ }
+
+ if ( slapi_mods_get_num_mods (&smods) == 0 )
+ {
+ int lderr;
+ char *emsg;
+
+ if ( ignored_some_mods ) {
+ lderr = LDAP_UNWILLING_TO_PERFORM;
+ emsg = "no modifiable attributes specified";
+ } else {
+ lderr = LDAP_PROTOCOL_ERROR;
+ emsg = "no modifications specified";
+ }
+ op_shared_log_error_access (pb, "MOD", dn, emsg);
+ send_ldap_result( pb, lderr, NULL, emsg, 0, NULL );
+ goto free_and_return;
+ }
+
+ if (!pb->pb_conn->c_isreplication_session &&
+ pb->pb_conn->c_needpw && pw_change == 0 )
+ {
+ (void)add_pwd_control ( pb, LDAP_CONTROL_PWEXPIRED, 0);
+ op_shared_log_error_access (pb, "MOD", dn, "need new password");
+ send_ldap_result( pb, LDAP_UNWILLING_TO_PERFORM, NULL, NULL, 0, NULL );
+ goto free_and_return;
+ }
+
+ /*
+ * in LDAPv3 there can be optional control extensions on
+ * the end of an LDAPMessage. we need to read them in and
+ * pass them to the backend.
+ */
+ if ( !ctrlp ) {
+ if ( (err = get_ldapmessage_controls( pb, ber, NULL )) != 0 )
+ {
+ op_shared_log_error_access (pb, "MOD", dn, "failed to decode LDAP controls");
+ send_ldap_result( pb, err, NULL, NULL, 0, NULL );
+ goto free_and_return;
+ }
+ }
+
+#ifdef LDAP_DEBUG
+ LDAPDebug( LDAP_DEBUG_ARGS, "modifications:\n", 0, 0, 0 );
+ for (mod = slapi_mods_get_first_mod(&smods); mod != NULL;
+ mod = slapi_mods_get_next_mod(&smods))
+ {
+ LDAPDebug( LDAP_DEBUG_ARGS, "\t%s: %s\n",
+ mod_op_image( mod->mod_op ), mod->mod_type, 0 );
+ }
+#endif
+
+ mods = slapi_mods_get_ldapmods_passout (&smods);
+
+ slapi_pblock_set( pb, SLAPI_MODIFY_MODS, mods);
+
+ op_shared_modify ( pb, pw_change, old_pw );
+
+ slapi_pblock_get(pb, SLAPI_MODIFY_MODS, &mods);
+ ldap_mods_free (mods, 1 /* Free the Array and the Elements */);
+
+free_and_return:;
+ slapi_ch_free ((void**)&dn);
+ slapi_mods_done(&smods);
+}
+
+/* This function is used to issue internal modify operation
+ This is an old style API. Its use is discoraged because it is not extendable and
+ because it does not allow to check whether plugin has right to access part of the
+ tree it is trying to modify. Use slapi_modify_internal_pb instead */
+Slapi_PBlock*
+slapi_modify_internal(const char *idn,
+ LDAPMod **mods,
+ LDAPControl **controls,
+ int dummy)
+{
+ Slapi_PBlock pb;
+ Slapi_PBlock *result_pb = NULL;
+ int opresult;
+
+ pblock_init(&pb);
+
+ slapi_modify_internal_set_pb (&pb, idn, (LDAPMod**)mods, controls, NULL,
+ (void *)plugin_get_default_component_id(), 0);
+
+ modify_internal_pb (&pb);
+
+ result_pb = slapi_pblock_new();
+ if (result_pb)
+ {
+ slapi_pblock_get(&pb, SLAPI_PLUGIN_INTOP_RESULT, &opresult);
+ slapi_pblock_set(result_pb, SLAPI_PLUGIN_INTOP_RESULT, &opresult);
+ }
+ pblock_done(&pb);
+
+ return result_pb;
+}
+
+/* This is new style API to issue internal modify operation.
+ pblock should contain the following data (can be set via call to slapi_modify_internal_set_pb):
+ For uniqueid based operation:
+ SLAPI_TARGET_DN set to dn that allows to select right backend, can be stale
+ SLAPI_TARGET_UNIQUEID set to the uniqueid of the entry we are looking for
+ SLAPI_MODIFY_MODS set to the mods
+ SLAPI_CONTROLS_ARG set to request controls if present
+
+ For dn based search:
+ SLAPI_TARGET_DN set to the entry dn
+ SLAPI_MODIFY_MODS set to the mods
+ SLAPI_CONTROLS_ARG set to request controls if present
+ */
+int slapi_modify_internal_pb (Slapi_PBlock *pb)
+{
+ if (pb == NULL)
+ return -1;
+
+ if (!allow_operation (pb))
+ {
+ slapi_send_ldap_result( pb, LDAP_UNWILLING_TO_PERFORM, NULL,
+ "This plugin is not configured to access operation target data", 0, NULL );
+ return 0;
+ }
+
+ return modify_internal_pb (pb);
+}
+
+/* Initialize a pblock for a call to slapi_modify_internal_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)
+{
+ Operation *op;
+ PR_ASSERT (pb != NULL);
+ if (pb == NULL || dn == NULL || mods == NULL)
+ {
+ slapi_log_error(SLAPI_LOG_FATAL, NULL,
+ "slapi_modify_internal_set_pb: NULL parameter\n");
+ return;
+ }
+
+ op= internal_operation_new(SLAPI_OPERATION_MODIFY,operation_flags);
+ slapi_pblock_set(pb, SLAPI_OPERATION, op);
+ slapi_pblock_set(pb, SLAPI_ORIGINAL_TARGET, (void*)dn);
+ slapi_pblock_set(pb, SLAPI_MODIFY_MODS, mods);
+ slapi_pblock_set(pb, SLAPI_CONTROLS_ARG, controls);
+ if (uniqueid)
+ {
+ slapi_pblock_set(pb, SLAPI_TARGET_UNIQUEID, (void*)uniqueid);
+ }
+ slapi_pblock_set(pb, SLAPI_PLUGIN_IDENTITY, plugin_identity);
+}
+
+/* Helper functions */
+
+static int modify_internal_pb (Slapi_PBlock *pb)
+{
+ LDAPControl **controls;
+ Operation *op;
+ int opresult = 0;
+ LDAPMod **normalized_mods = NULL;
+ LDAPMod **mods;
+ LDAPMod **mod;
+ int pw_change = 0;
+ char *old_pw = NULL;
+
+ PR_ASSERT (pb != NULL);
+
+ slapi_pblock_get(pb, SLAPI_MODIFY_MODS, &mods);
+ slapi_pblock_get(pb, SLAPI_CONTROLS_ARG, &controls);
+
+ if(mods == NULL)
+ {
+ opresult = LDAP_PARAM_ERROR;
+ slapi_pblock_set(pb, SLAPI_PLUGIN_INTOP_RESULT, &opresult);
+ return 0;
+ }
+
+ /* first normalize the mods so they are bvalue
+ * Note: We don't add any special
+ * attributes such as "creatorsname".
+ * for CIR we don't want to change them, for other
+ * plugins the writer should change these if it wants too by explicitly
+ * adding them to the mods
+ */
+ normalized_mods = normalize_mods2bvals((const LDAPMod**)mods);
+ if (normalized_mods == NULL)
+ {
+ opresult = LDAP_PARAM_ERROR;
+ slapi_pblock_set(pb, SLAPI_PLUGIN_INTOP_RESULT, &opresult);
+ return 0;
+ }
+
+ /* check for password change */
+ mod = normalized_mods;
+ while (*mod)
+ {
+ if ((*mod)->mod_bvalues != NULL && strcasecmp((*mod)->mod_type, SLAPI_USERPWD_ATTR) == 0)
+ {
+ pw_change = op_shared_allow_pw_change (pb, *mod, &old_pw);
+ if (pw_change == -1)
+ {
+ opresult = LDAP_PARAM_ERROR;
+ slapi_pblock_set(pb, SLAPI_PLUGIN_INTOP_RESULT, &opresult);
+ return 0;
+ }
+ }
+
+ mod ++;
+ }
+
+ slapi_pblock_get(pb, SLAPI_OPERATION, &op);
+ op->o_handler_data = &opresult;
+ op->o_result_handler = internal_getresult_callback;
+
+ slapi_pblock_set(pb, SLAPI_MODIFY_MODS, normalized_mods);
+ slapi_pblock_set(pb, SLAPI_REQCONTROLS, controls);
+
+ /* set parameters common for all internal operations */
+ set_common_params (pb);
+
+ /* set actions taken to process the operation */
+ set_config_params (pb);
+
+ /* perform modify operation */
+ op_shared_modify (pb, pw_change, old_pw);
+
+ /* free the normalized_mods don't forget to add this*/
+ slapi_pblock_get(pb, SLAPI_MODIFY_MODS, &normalized_mods);
+ if (normalized_mods != NULL)
+ {
+ ldap_mods_free(normalized_mods, 1);
+ }
+
+ /* return original mods here */
+ slapi_pblock_set(pb, SLAPI_MODIFY_MODS, mods);
+ /* set result */
+ slapi_pblock_set(pb, SLAPI_PLUGIN_INTOP_RESULT, &opresult);
+
+ return 0;
+}
+
+static void op_shared_modify (Slapi_PBlock *pb, int pw_change, char *old_pw)
+{
+ Slapi_Backend *be = NULL;
+ Slapi_Entry *pse;
+ Slapi_Entry *referral;
+ Slapi_Entry *ecopy = NULL;
+ Slapi_Entry *e = NULL;
+ char ebuf[BUFSIZ];
+ char *dn;
+ Slapi_DN sdn;
+ LDAPMod **mods, *pw_mod, **tmpmods = NULL;
+ Slapi_Mods smods;
+ Slapi_Mods unhashed_pw_smod;
+ int repl_op, internal_op, lastmod;
+ char *unhashed_pw_attr = NULL;
+ Slapi_Operation *operation;
+ char errorbuf[BUFSIZ];
+ int err;
+ LDAPMod *lc_mod = NULL;
+ struct slapdplugin *p = NULL;
+ int numattr, i;
+
+ slapi_pblock_get (pb, SLAPI_ORIGINAL_TARGET, &dn);
+ slapi_pblock_get (pb, SLAPI_MODIFY_MODS, &mods);
+ slapi_pblock_get (pb, SLAPI_MODIFY_MODS, &tmpmods);
+ slapi_pblock_get (pb, SLAPI_IS_REPLICATED_OPERATION, &repl_op);
+ slapi_pblock_get (pb, SLAPI_OPERATION, &operation);
+ internal_op= operation_is_flag_set(operation, OP_FLAG_INTERNAL);
+
+ if (dn == NULL)
+ {
+ slapi_sdn_init_dn_byref (&sdn, "");
+ }
+ else
+ {
+ slapi_sdn_init_dn_byref (&sdn, dn);
+ }
+
+ slapi_pblock_set(pb, SLAPI_MODIFY_TARGET, (void*)slapi_sdn_get_ndn (&sdn));
+
+ slapi_mods_init_passin (&smods, mods);
+
+ slapi_mods_init(&unhashed_pw_smod, 0);
+
+ /* target spec is used to decide which plugins are applicable for the operation */
+ operation_set_target_spec (pb->pb_op, &sdn);
+
+ if (operation_is_flag_set(operation,OP_FLAG_ACTION_LOG_ACCESS))
+ {
+ if ( !internal_op )
+ {
+ slapi_log_access(LDAP_DEBUG_STATS, "conn=%d op=%d MOD dn=\"%s\"\n",
+ pb->pb_conn->c_connid,
+ pb->pb_op->o_opid,
+ escape_string(slapi_sdn_get_dn(&sdn), ebuf));
+ }
+ else
+ {
+ slapi_log_access(LDAP_DEBUG_ARGS, "conn=%s op=%d MOD dn=\"%s\"\n",
+ LOG_INTERNAL_OP_CON_ID,
+ LOG_INTERNAL_OP_OP_ID,
+ escape_string(slapi_sdn_get_dn(&sdn), ebuf));
+ }
+ }
+
+ /*
+ * We could be serving multiple database backends. Select the
+ * appropriate one.
+ */
+ if ((err = slapi_mapping_tree_select(pb, &be, &referral, errorbuf)) != LDAP_SUCCESS) {
+ send_ldap_result(pb, err, NULL, errorbuf, 0, NULL);
+ be = NULL;
+ goto free_and_return;
+ }
+
+ if (referral)
+ {
+ int managedsait;
+
+ slapi_pblock_get(pb, SLAPI_MANAGEDSAIT, &managedsait);
+ if (managedsait)
+ {
+ send_ldap_result(pb, LDAP_UNWILLING_TO_PERFORM, NULL,
+ "cannot update referral", 0, NULL);
+ slapi_entry_free(referral);
+ goto free_and_return;
+ }
+
+ send_referrals_from_entry(pb,referral);
+ slapi_entry_free(referral);
+ goto free_and_return;
+ }
+
+ slapi_pblock_set(pb, SLAPI_BACKEND, be);
+
+ /* The following section checks the valid values of fine-grained
+ * password policy attributes.
+ * 1. First, it checks if the entry has "passwordpolicy" objectclass.
+ * 2. If yes, then if the mods contain any passwdpolicy specific attributes.
+ * 3. If yes, then it invokes corrosponding checking function.
+ */
+ if ( !repl_op && !internal_op && dn &&
+ (e = get_entry(pb, slapi_dn_normalize(dn))) )
+ {
+ Slapi_Value target;
+ slapi_value_init(&target);
+ slapi_value_set_string(&target,"passwordpolicy");
+ if ((slapi_entry_attr_has_syntax_value(e, "objectclass", &target)) == 1)
+ {
+ numattr = sizeof(AttrValueCheckList)/sizeof(AttrValueCheckList[0]);
+ while ( tmpmods && *tmpmods )
+ {
+ if ((*tmpmods)->mod_bvalues != NULL &&
+ (((*tmpmods)->mod_op & ~LDAP_MOD_BVALUES) != LDAP_MOD_DELETE))
+ {
+ for (i=0; i < numattr; i++)
+ {
+ if (slapi_attr_type_cmp((*tmpmods)->mod_type,
+ AttrValueCheckList[i].attr_name, SLAPI_TYPE_CMP_SUBTYPE) == 0)
+ {
+ /* The below function call is good for
+ * single-valued attrs only
+ */
+ if ( (err = AttrValueCheckList[i].checkfunc (AttrValueCheckList[i].attr_name,
+ (*tmpmods)->mod_bvalues[0]->bv_val, AttrValueCheckList[i].minval,
+ AttrValueCheckList[i].maxval, errorbuf))
+ != LDAP_SUCCESS)
+ {
+ /* return error */
+ send_ldap_result(pb, err, NULL, errorbuf, 0, NULL);
+ goto free_and_return;
+ }
+ }
+ }
+ }
+ tmpmods++;
+ } /* end of (while */
+ } /* end of if (found */
+ value_done (&target);
+ } /* end of if (!repl_op */
+
+ /* can get lastmod only after backend is selected */
+ slapi_pblock_get(pb, SLAPI_BE_LASTMOD, &lastmod);
+
+ /* if this is replication session - leave mod attributes alone */
+ if (!repl_op && lastmod)
+ {
+ modify_update_last_modified_attr(pb, &smods);
+ }
+
+ /*
+ * Add the unhashed password pseudo-attribute before
+ * calling the preop plugins
+ */
+
+ if (pw_change)
+ {
+ Slapi_Value **va= NULL;
+
+ unhashed_pw_attr = slapi_attr_syntax_normalize(PSEUDO_ATTR_UNHASHEDUSERPASSWORD);
+
+ for ( pw_mod = slapi_mods_get_first_mod(&smods); pw_mod;
+ pw_mod = slapi_mods_get_next_mod(&smods) )
+ {
+ if (strcasecmp (pw_mod->mod_type, SLAPI_USERPWD_ATTR) != 0)
+ continue;
+
+ /* add pseudo password attribute */
+ valuearray_init_bervalarray(pw_mod->mod_bvalues, &va);
+ slapi_mods_add_mod_values(&smods, pw_mod->mod_op, unhashed_pw_attr, va);
+ valuearray_free(&va);
+
+ /* Init new value array for hashed value */
+ valuearray_init_bervalarray(pw_mod->mod_bvalues, &va);
+
+ /* encode password */
+ pw_encodevals(va);
+
+ /* remove current clear value of userpassword */
+ ber_bvecfree(pw_mod->mod_bvalues);
+ /* add the cipher in the structure */
+ valuearray_get_bervalarray(va, &pw_mod->mod_bvalues);
+
+ valuearray_free(&va);
+ }
+ }
+ for ( p = get_plugin_list(PLUGIN_LIST_REVER_PWD_STORAGE_SCHEME); p != NULL; p = p->plg_next )
+ {
+ char *L_attr = NULL;
+ int i = 0;
+
+ /* Get the appropriate encoding function */
+ for ( L_attr = p->plg_argv[i]; i<p->plg_argc; L_attr = p->plg_argv[++i])
+ {
+ char *L_normalized = slapi_attr_syntax_normalize(L_attr);
+
+ for ( lc_mod = slapi_mods_get_first_mod(&smods); lc_mod;
+ lc_mod = slapi_mods_get_next_mod(&smods) )
+ {
+ Slapi_Value **va= NULL;
+
+ if (strcasecmp (lc_mod->mod_type, L_normalized) != 0)
+ continue;
+
+ switch (lc_mod->mod_op & ~LDAP_MOD_BVALUES)
+ {
+ case LDAP_MOD_ADD:
+ case LDAP_MOD_REPLACE:
+
+ /* Init new value array for hashed value */
+ valuearray_init_bervalarray(lc_mod->mod_bvalues, &va);
+ if ( va )
+ {
+ /* encode local credentials */
+ pw_rever_encode(va, L_normalized);
+ /* remove current clear value of userpassword */
+ ber_bvecfree(lc_mod->mod_bvalues);
+ /* add the cipher in the structure */
+ valuearray_get_bervalarray(va, &lc_mod->mod_bvalues);
+
+ valuearray_free(&va);
+ }
+ break;
+ default:
+ /* for LDAP_MOD_DELETE, don't do anything */
+ /* for LDAP_MOD_BVALUES, don't do anything */
+ ;
+ }
+ }
+ if (L_normalized)
+ slapi_ch_free ((void**)&L_normalized);
+ }
+ }
+
+ /*
+ * call the pre-mod plugins. if they succeed, call
+ * the backend mod function. then call the post-mod
+ * plugins.
+ */
+ slapi_pblock_set (pb, SLAPI_MODIFY_MODS, (void*)slapi_mods_get_ldapmods_passout (&smods));
+ if (plugin_call_plugins(pb, internal_op ? SLAPI_PLUGIN_INTERNAL_PRE_MODIFY_FN :
+ SLAPI_PLUGIN_PRE_MODIFY_FN) == 0)
+ {
+ int rc;
+
+ slapi_pblock_set(pb, SLAPI_PLUGIN, be->be_database);
+ set_db_default_result_handlers(pb);
+
+ /* Remove the unhashed password pseudo-attribute prior */
+ /* to db access */
+ if (pw_change)
+ {
+ slapi_pblock_get (pb, SLAPI_MODIFY_MODS, &mods);
+ slapi_mods_init_passin (&smods, mods);
+ remove_mod (&smods, unhashed_pw_attr, &unhashed_pw_smod);
+ slapi_pblock_set (pb, SLAPI_MODIFY_MODS,
+ (void*)slapi_mods_get_ldapmods_passout (&smods));
+ }
+
+ if (be->be_modify != NULL)
+ {
+ if ((rc = (*be->be_modify)(pb)) == 0)
+ {
+ /* acl is not used for internal operations */
+ /* don't update aci store for remote acis */
+ if ((!internal_op) &&
+ (!slapi_be_is_flag_set(be,SLAPI_BE_FLAG_REMOTE_DATA)))
+ {
+ plugin_call_acl_mods_update (pb, SLAPI_OPERATION_MODIFY);
+ }
+
+ if (operation_is_flag_set(operation,OP_FLAG_ACTION_LOG_AUDIT))
+ write_audit_log_entry(pb); /* Record the operation in the audit log */
+
+ if (pw_change && (!slapi_be_is_flag_set(be,SLAPI_BE_FLAG_REMOTE_DATA)))
+ {
+ /* update the password info */
+ update_pw_info (pb, old_pw);
+ }
+ slapi_pblock_get(pb, SLAPI_ENTRY_POST_OP, &pse);
+ do_ps_service(pse, NULL, LDAP_CHANGETYPE_MODIFY, 0);
+ }
+ else
+ {
+ if (rc == SLAPI_FAIL_DISKFULL)
+ {
+ operation_out_of_disk_space();
+ goto free_and_return;
+ }
+ }
+ }
+ else
+ {
+ send_ldap_result(pb, LDAP_UNWILLING_TO_PERFORM, NULL,
+ "Function not implemented", 0, NULL);
+ }
+ /* Add the pseudo-attribute prior to calling the postop plugins */
+ if (pw_change)
+ {
+ LDAPMod *lc_mod = NULL;
+
+ slapi_pblock_get (pb, SLAPI_MODIFY_MODS, &mods);
+ slapi_mods_init_passin (&smods, mods);
+ for ( lc_mod = slapi_mods_get_first_mod(&unhashed_pw_smod); lc_mod;
+ lc_mod = slapi_mods_get_next_mod(&unhashed_pw_smod) )
+ {
+ Slapi_Mod lc_smod;
+ slapi_mod_init_byval(&lc_smod, lc_mod); /* copies lc_mod */
+ /* this extracts the copy of lc_mod and finalizes lc_smod too */
+ slapi_mods_add_ldapmod(&smods,
+ slapi_mod_get_ldapmod_passout(&lc_smod));
+ }
+ slapi_pblock_set (pb, SLAPI_MODIFY_MODS,
+ (void*)slapi_mods_get_ldapmods_passout (&smods));
+ slapi_mods_done(&unhashed_pw_smod); /* can finalize now */
+ }
+
+ slapi_pblock_set(pb, SLAPI_PLUGIN_OPRETURN, &rc);
+ plugin_call_plugins(pb, internal_op ? SLAPI_PLUGIN_INTERNAL_POST_MODIFY_FN :
+ SLAPI_PLUGIN_POST_MODIFY_FN);
+
+ }
+
+free_and_return:
+ slapi_pblock_get(pb, SLAPI_ENTRY_PRE_OP, &ecopy);
+ slapi_entry_free(ecopy);
+ slapi_pblock_get(pb, SLAPI_ENTRY_POST_OP, &ecopy);
+ slapi_entry_free(ecopy);
+ slapi_entry_free(e);
+
+ if (be)
+ slapi_be_Unlock(be);
+ slapi_sdn_done(&sdn);
+
+ if (unhashed_pw_attr)
+ slapi_ch_free ((void**)&unhashed_pw_attr);
+}
+
+static void remove_mod (Slapi_Mods *smods, const char *type, Slapi_Mods *smod_unhashed)
+{
+ LDAPMod *mod;
+ Slapi_Mod smod;
+
+ for (mod = slapi_mods_get_first_mod(smods); mod; mod = slapi_mods_get_next_mod(smods))
+ {
+ if (strcasecmp (mod->mod_type, type) == 0)
+ {
+ slapi_mod_init_byval (&smod, mod);
+ slapi_mods_add_smod(smod_unhashed, &smod);
+ slapi_mods_remove (smods);
+ }
+ }
+}
+
+static int op_shared_allow_pw_change (Slapi_PBlock *pb, LDAPMod *mod, char **old_pw)
+{
+ int isroot, internal_op, repl_op, pwresponse_req = 0;
+ char *dn;
+ Slapi_DN sdn;
+ passwdPolicy *pwpolicy;
+ int rc = 0;
+ char ebuf[BUFSIZ];
+ Slapi_Value **values= NULL;
+ Slapi_Operation *operation;
+
+ slapi_pblock_get (pb, SLAPI_IS_REPLICATED_OPERATION, &repl_op);
+ if (repl_op) {
+ /* Treat like there's no password */
+ return (0);
+ }
+
+ *old_pw = NULL;
+
+ slapi_pblock_get (pb, SLAPI_ORIGINAL_TARGET, &dn);
+ slapi_pblock_get (pb, SLAPI_REQUESTOR_ISROOT, &isroot);
+ slapi_pblock_get (pb, SLAPI_OPERATION, &operation);
+ slapi_pblock_get (pb, SLAPI_PWPOLICY, &pwresponse_req);
+ internal_op= operation_is_flag_set(operation, OP_FLAG_INTERNAL);
+
+
+ slapi_sdn_init_dn_byref (&sdn, dn);
+ pwpolicy = new_passwdPolicy(pb, (char *)slapi_sdn_get_ndn(&sdn));
+
+ /* internal operation has root permisions for subtrees it is allowed to access */
+ if (!internal_op)
+ {
+ /* Check first if password policy allows users to change their passwords.*/
+ if (!pb->pb_op->o_isroot && slapi_sdn_compare(&sdn, &pb->pb_op->o_sdn)==0 &&
+ !pb->pb_conn->c_needpw && !pwpolicy->pw_change)
+ {
+ if ( pwresponse_req == 1 ) {
+ pwpolicy_make_response_control ( pb, -1, -1, LDAP_PWPOLICY_PWDMODNOTALLOWED );
+ }
+ send_ldap_result(pb, LDAP_UNWILLING_TO_PERFORM, NULL,
+ "user is not allowed to change password", 0, NULL);
+
+ if (operation_is_flag_set(operation,OP_FLAG_ACTION_LOG_ACCESS))
+ {
+ slapi_log_access(LDAP_DEBUG_STATS, "conn=%d op=%d MOD dn=\"%s\", %s\n",
+ pb->pb_conn->c_connid, pb->pb_op->o_opid,
+ escape_string(slapi_sdn_get_dn(&sdn), ebuf),
+ "user is not allowed to change password");
+ }
+
+ rc = -1;
+ goto done;
+ }
+ }
+
+ /* check if password is within password minimum age;
+ error result is sent directly from check_pw_minage */
+ if ((internal_op || !pb->pb_conn->c_needpw) &&
+ check_pw_minage(pb, &sdn, mod->mod_bvalues) == 1)
+ {
+ if (operation_is_flag_set(operation,OP_FLAG_ACTION_LOG_ACCESS))
+ {
+ if ( !internal_op )
+ {
+ slapi_log_access(LDAP_DEBUG_STATS, "conn=%d op=%d MOD dn=\"%s\", %s\n",
+ pb->pb_conn->c_connid,
+ pb->pb_op->o_opid,
+ escape_string(slapi_sdn_get_dn(&sdn), ebuf),
+ "within password minimum age");
+ }
+ else
+ {
+ slapi_log_access(LDAP_DEBUG_ARGS, "conn=%s op=%d MOD dn=\"%s\", %s\n",
+ LOG_INTERNAL_OP_CON_ID,
+ LOG_INTERNAL_OP_OP_ID,
+ escape_string(slapi_sdn_get_dn(&sdn), ebuf),
+ "within password minimum age");
+ }
+ }
+
+ rc = -1;
+ goto done;
+ }
+
+
+ /* check password syntax; remember the old password;
+ error sent directly from check_pw_syntax function */
+ valuearray_init_bervalarray(mod->mod_bvalues, &values);
+ switch (check_pw_syntax (pb, &sdn, values, old_pw, NULL, 1))
+ {
+ case 0: /* success */
+ rc = 1;
+ break;
+
+ case 1: /* failed checking */
+ if (operation_is_flag_set(operation,OP_FLAG_ACTION_LOG_ACCESS))
+ {
+ if ( !internal_op )
+ {
+ slapi_log_access(LDAP_DEBUG_STATS, "conn=%d op=%d MOD dn=\"%s\", %s\n",
+ pb->pb_conn->c_connid,
+ pb->pb_op->o_opid,
+ escape_string(slapi_sdn_get_dn(&sdn), ebuf), "invalid password syntax");
+ }
+ else
+ {
+ slapi_log_access(LDAP_DEBUG_ARGS, "conn=%s op=%d MOD dn=\"%s\", %s\n",
+ LOG_INTERNAL_OP_CON_ID,
+ LOG_INTERNAL_OP_OP_ID,
+ escape_string(slapi_sdn_get_dn(&sdn), ebuf), "invalid password syntax");
+ }
+ }
+ rc = -1;
+ break;
+
+ case -1: /* The entry is not found. No password checking is done. Countinue execution
+ and it should get caught later and send "no such object back. */
+ rc = 0;
+ break;
+
+ default: break;
+ }
+ valuearray_free(&values);
+
+done:
+ slapi_sdn_done (&sdn);
+ delete_passwdPolicy(&pwpolicy);
+ return rc;
+}
diff --git a/ldap/servers/slapd/modrdn.c b/ldap/servers/slapd/modrdn.c
new file mode 100644
index 00000000..3e6c4bf6
--- /dev/null
+++ b/ldap/servers/slapd/modrdn.c
@@ -0,0 +1,501 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+/*
+ * 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 <stdio.h>
+#include <string.h>
+#include <sys/types.h>
+#ifndef _WIN32
+#include <sys/socket.h>
+#endif
+#include "slap.h"
+#include "pratom.h"
+
+/* Forward declarations */
+static int rename_internal_pb (Slapi_PBlock *pb);
+static void op_shared_rename (Slapi_PBlock *pb, int passin_args );
+
+/* This function is called to process operation that come over external connections */
+void
+do_modrdn( Slapi_PBlock *pb )
+{
+ Slapi_Operation *operation;
+ BerElement *ber;
+ char *dn, *newsuperior = NULL;
+ char *newrdn = NULL;
+ int err, deloldrdn;
+ unsigned long len;
+
+ LDAPDebug( LDAP_DEBUG_TRACE, "do_modrdn\n", 0, 0, 0 );
+
+ /* count the modrdn request */
+ PR_AtomicIncrement(g_get_global_snmp_vars()->ops_tbl.dsModifyRDNOps);
+
+ slapi_pblock_get( pb, SLAPI_OPERATION, &operation);
+ ber = operation->o_ber;
+
+ /*
+ * 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( ber, "{aab", &dn, &newrdn, &deloldrdn )
+ == LBER_ERROR ) {
+ LDAPDebug( LDAP_DEBUG_ANY,
+ "ber_scanf failed (op=ModRDN; params=DN,newRDN,deleteOldRDN)\n",
+ 0, 0, 0 );
+ op_shared_log_error_access (pb, "MODRDN", "???", "decoding error");
+ send_ldap_result( pb, LDAP_PROTOCOL_ERROR, NULL,
+ "unable to decode DN, newRDN, or deleteOldRDN parameters",
+ 0, NULL );
+ return;
+ }
+
+ if ( ber_peek_tag( ber, &len ) == LDAP_TAG_NEWSUPERIOR ) {
+ if ( pb->pb_conn->c_ldapversion < LDAP_VERSION3 ) {
+ LDAPDebug( LDAP_DEBUG_ANY,
+ "got newSuperior in LDAPv2 modrdn op\n", 0, 0, 0 );
+ op_shared_log_error_access (pb, "MODRDN", dn, "decoding error");
+ send_ldap_result( pb, LDAP_PROTOCOL_ERROR, NULL,
+ "received newSuperior in LDAPv2 modrdn", 0, NULL );
+ goto free_and_return;
+ }
+ if ( ber_scanf( ber, "a", &newsuperior ) == LBER_ERROR ) {
+ LDAPDebug( LDAP_DEBUG_ANY,
+ "ber_scanf failed (op=ModRDN; params=newSuperior)\n",
+ 0, 0, 0 );
+ op_shared_log_error_access (pb, "MODRDN", dn, "decoding error");
+ send_ldap_result( pb, LDAP_PROTOCOL_ERROR, NULL,
+ "unable to decode newSuperior parameter", 0, NULL );
+ goto free_and_return;
+ }
+ }
+
+ /*
+ * in LDAPv3 there can be optional control extensions on
+ * the end of an LDAPMessage. we need to read them in and
+ * pass them to the backend.
+ */
+ if ( (err = get_ldapmessage_controls( pb, ber, NULL )) != 0 ) {
+ op_shared_log_error_access (pb, "MODRDN", dn, "failed to decode LDAP controls");
+ send_ldap_result( pb, err, NULL, NULL, 0, NULL );
+ goto free_and_return;
+ }
+
+ LDAPDebug( LDAP_DEBUG_ARGS,
+ "do_moddn: dn (%s) newrdn (%s) deloldrdn (%d)\n", dn, newrdn,
+ deloldrdn );
+
+ slapi_pblock_set( pb, SLAPI_REQUESTOR_ISROOT, &pb->pb_op->o_isroot );
+ slapi_pblock_set( pb, SLAPI_ORIGINAL_TARGET, dn );
+ slapi_pblock_set( pb, SLAPI_MODRDN_NEWRDN, newrdn );
+ slapi_pblock_set( pb, SLAPI_MODRDN_NEWSUPERIOR, newsuperior );
+ slapi_pblock_set( pb, SLAPI_MODRDN_DELOLDRDN, &deloldrdn );
+
+ op_shared_rename(pb, 1 /* pass in ownership of string arguments */ );
+ return;
+
+free_and_return:;
+ slapi_ch_free((void **) &dn );
+ slapi_ch_free((void **) &newrdn );
+ slapi_ch_free((void **) &newsuperior );
+}
+
+/* This function is used to issue internal modrdn operation
+ This is an old style API. Its use is discoraged because it is not extendable and
+ because it does not allow to check whether plugin has right to access part of the
+ tree it is trying to modify. Use slapi_modrdn_internal_pb instead */
+Slapi_PBlock *
+slapi_modrdn_internal(const char *iodn, const char *inewrdn, int deloldrdn, LDAPControl **controls, int dummy)
+{
+ return slapi_rename_internal(iodn, inewrdn, NULL, deloldrdn, controls, dummy);
+}
+
+Slapi_PBlock *
+slapi_rename_internal(const char *iodn, const char *inewrdn, const char *inewsuperior, int deloldrdn, LDAPControl **controls, int dummy)
+{
+ Slapi_PBlock pb;
+ Slapi_PBlock *result_pb = NULL;
+ int opresult= 0;
+
+ pblock_init (&pb);
+
+ slapi_rename_internal_set_pb (&pb, iodn, inewrdn, inewsuperior, deloldrdn,
+ controls, NULL, plugin_get_default_component_id(), 0);
+ rename_internal_pb (&pb);
+
+ result_pb = slapi_pblock_new();
+ if (result_pb)
+ {
+ slapi_pblock_get(&pb, SLAPI_PLUGIN_INTOP_RESULT, &opresult);
+ slapi_pblock_set(result_pb, SLAPI_PLUGIN_INTOP_RESULT, &opresult);
+ }
+ pblock_done(&pb);
+
+ return result_pb;
+}
+
+/* This is new style API to issue internal add operation.
+ pblock should contain the following data (can be set via call to slapi_rename_internal_set_pb):
+ For uniqueid based operation:
+ SLAPI_TARGET_DN set to dn that allows to select right backend, can be stale
+ SLAPI_TARGET_UNIQUEID set to the uniqueid of the entry we are looking for
+ SLAPI_MODRDN_NEWRDN set to new rdn of the entry
+ SLAPI_MODRDN_DELOLDRDN tells whether old rdn should be kept in the entry
+ LAPI_CONTROLS_ARG set to request controls if present
+
+ For dn based search:
+ SLAPI_TARGET_DN set to the entry dn
+ SLAPI_MODRDN_NEWRDN set to new rdn of the entry
+ SLAPI_MODRDN_DELOLDRDN tells whether old rdn should be kept in the entry
+ SLAPI_CONTROLS_ARG set to request controls if present
+ */
+int slapi_modrdn_internal_pb (Slapi_PBlock *pb)
+{
+ if (pb == NULL)
+ return -1;
+
+ return rename_internal_pb (pb);
+}
+
+/* Initialize a pblock for a call to slapi_modrdn_internal_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)
+{
+ Operation *op;
+ PR_ASSERT (pb != NULL);
+ if (pb == NULL || olddn == NULL || newrdn == NULL)
+ {
+ slapi_log_error(SLAPI_LOG_FATAL, NULL,
+ "slapi_rename_internal_set_pb: NULL parameter\n");
+ return;
+ }
+
+ op= internal_operation_new(SLAPI_OPERATION_MODRDN,operation_flags);
+ slapi_pblock_set(pb, SLAPI_OPERATION, op);
+ slapi_pblock_set(pb, SLAPI_ORIGINAL_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, &deloldrdn);
+ slapi_pblock_set(pb, SLAPI_CONTROLS_ARG, controls);
+ slapi_pblock_set(pb, SLAPI_MODIFY_MODS, NULL);
+ if (uniqueid)
+ {
+ slapi_pblock_set(pb, SLAPI_TARGET_UNIQUEID, (void*)uniqueid);
+ }
+ slapi_pblock_set(pb, SLAPI_PLUGIN_IDENTITY, plugin_identity);
+}
+
+/* Helper functions */
+
+static int rename_internal_pb (Slapi_PBlock *pb)
+{
+ LDAPControl **controls;
+ Operation *op;
+ int opresult = 0;
+
+ PR_ASSERT (pb != NULL);
+
+ slapi_pblock_get(pb, SLAPI_CONTROLS_ARG, &controls);
+
+ slapi_pblock_get(pb, SLAPI_OPERATION, &op);
+ op->o_handler_data = &opresult;
+ op->o_result_handler = internal_getresult_callback;
+
+ slapi_pblock_set(pb, SLAPI_REQCONTROLS, controls);
+
+ /* set parameters common for all internal operations */
+ set_common_params (pb);
+
+ /* set actions taken to process the operation */
+ set_config_params (pb);
+
+ op_shared_rename (pb, 0 /* not passing ownership of args */ );
+
+ slapi_pblock_set(pb, SLAPI_PLUGIN_INTOP_RESULT, &opresult);
+
+ return 0;
+}
+
+
+/*
+ * op_shared_rename() -- common frontend code for modDN operations.
+ *
+ * Beware: this function resets the following pblock elements that were
+ * set by the caller:
+ *
+ * SLAPI_MODRDN_TARGET
+ * SLAPI_MODRDN_NEWRDN
+ * SLAPI_MODRDN_NEWSUPERIOR
+ */
+static void
+op_shared_rename(Slapi_PBlock *pb, int passin_args)
+{
+ char *dn, *newsuperior, *newrdn, *newdn = NULL;
+ char **rdns;
+ int deloldrdn;
+ Slapi_Backend *be = NULL;
+ Slapi_DN sdn;
+ Slapi_Mods smods;
+ char dnbuf[BUFSIZ];
+ char newrdnbuf[BUFSIZ];
+ char newsuperiorbuf[BUFSIZ];
+ int internal_op, repl_op, lastmod;
+ Slapi_Operation *operation;
+ Slapi_Entry *referral;
+ char errorbuf[BUFSIZ];
+ int err;
+
+ slapi_pblock_get(pb, SLAPI_ORIGINAL_TARGET, &dn);
+ slapi_pblock_get(pb, SLAPI_MODRDN_NEWRDN, &newrdn);
+ slapi_pblock_get(pb, SLAPI_MODRDN_NEWSUPERIOR, &newsuperior);
+ slapi_pblock_get(pb, SLAPI_MODRDN_DELOLDRDN, &deloldrdn);
+ slapi_pblock_get(pb, SLAPI_IS_REPLICATED_OPERATION, &repl_op);
+ slapi_pblock_get (pb, SLAPI_OPERATION, &operation);
+ internal_op= operation_is_flag_set(operation, OP_FLAG_INTERNAL);
+
+ /*
+ * If ownership has not been passed to this function, we replace the
+ * string input fields within the pblock with strdup'd copies. Why?
+ * Because some pre- and post-op plugins may change them, and the
+ * convention is that plugins should place a malloc'd string in the
+ * pblock. Therefore, we need to be able to retrieve and free them
+ * later. But the callers of the internal modrdn calls are promised
+ * that we will not free these parameters... so if passin_args is
+ * zero, we need to make copies.
+ *
+ * In the case of SLAPI_MODRDN_TARGET and SLAPI_MODRDN_NEWSUPERIOR, we
+ * replace the existing values with normalized values (because plugins
+ * expect these DNs to be normalized).
+ */
+ if ( passin_args ) {
+ slapi_sdn_init_dn_passin(&sdn,dn); /* freed by slapi_sdn_done() */
+ } else {
+ slapi_sdn_init_dn_byref(&sdn,dn);
+ }
+ if ( !passin_args ) {
+ newrdn = slapi_ch_strdup( newrdn );
+ newsuperior = slapi_ch_strdup( newsuperior );
+ }
+ if ( NULL != newsuperior ) {
+ slapi_dn_normalize_case( newsuperior ); /* normalize in place */
+ }
+ slapi_pblock_set (pb, SLAPI_MODRDN_TARGET,
+ (void*)slapi_ch_strdup(slapi_sdn_get_ndn (&sdn)));
+ slapi_pblock_set(pb, SLAPI_MODRDN_NEWRDN, (void *)newrdn );
+ slapi_pblock_set(pb, SLAPI_MODRDN_NEWSUPERIOR, (void *)newsuperior);
+
+ /*
+ * first, log the operation to the access log,
+ * then check rdn and newsuperior,
+ * and - if applicable - log reason of any error to the errors log
+ */
+ if (operation_is_flag_set(operation,OP_FLAG_ACTION_LOG_ACCESS))
+ {
+ if ( !internal_op )
+ {
+ slapi_log_access(LDAP_DEBUG_STATS,
+ "conn=%d op=%d MODRDN dn=\"%s\" newrdn=\"%s\" newsuperior=\"%s\"\n",
+ pb->pb_conn->c_connid,
+ pb->pb_op->o_opid,
+ escape_string(dn, dnbuf),
+ (NULL == newrdn) ? "(null)" : escape_string(newrdn, newrdnbuf),
+ (NULL == newsuperior) ? "(null)" : escape_string(newsuperior, newsuperiorbuf));
+ }
+ else
+ {
+ slapi_log_access(LDAP_DEBUG_ARGS,
+ "conn=%s op=%d MODRDN dn=\"%s\" newrdn=\"%s\" newsuperior=\"%s\"\n",
+ LOG_INTERNAL_OP_CON_ID,
+ LOG_INTERNAL_OP_OP_ID,
+ escape_string(dn, dnbuf),
+ (NULL == newrdn) ? "(null)" : escape_string(newrdn, newrdnbuf),
+ (NULL == newsuperior) ? "(null)" : escape_string(newsuperior, newsuperiorbuf));
+ }
+ }
+
+ /* check that the rdn is formatted correctly */
+ if ((rdns = ldap_explode_rdn(newrdn, 0)) == NULL)
+ {
+ slapi_log_error(SLAPI_LOG_FATAL, NULL,
+ "conn=%d op=%d MODRDN invalid new RDN (\"%s\")\n",
+ pb->pb_conn->c_connid,
+ pb->pb_op->o_opid,
+ (NULL == newrdn) ? "(null)" : newrdn);
+ send_ldap_result(pb, LDAP_INVALID_DN_SYNTAX, NULL, "invalid RDN", 0, NULL);
+ goto free_and_return_nolock;
+ }
+ else
+ {
+ ldap_value_free(rdns);
+ }
+
+ /* check that the dn is formatted correctly */
+ if ((rdns = ldap_explode_dn(newsuperior, 0)) == NULL)
+ {
+ LDAPDebug(LDAP_DEBUG_ARGS, "ldap_explode_dn of newSuperior failed\n", 0, 0, 0);
+ slapi_log_error(SLAPI_LOG_FATAL, NULL,
+ "conn=%d op=%d MODRDN invalid new superior (\"%s\")",
+ pb->pb_conn->c_connid,
+ pb->pb_op->o_opid,
+ (NULL == newsuperior) ? "(null)" : newsuperiorbuf);
+ send_ldap_result(pb, LDAP_PROTOCOL_ERROR, NULL,
+ "newSuperior does not look like a DN", 0, NULL);
+ goto free_and_return_nolock;
+ }
+ else
+ {
+ ldap_value_free(rdns);
+ }
+
+ if (newsuperior != NULL)
+ {
+ LDAPDebug(LDAP_DEBUG_ARGS, "do_moddn: newsuperior (%s)\n", newsuperior, 0, 0);
+ }
+
+ /* target spec is used to decide which plugins are applicable for the operation */
+ operation_set_target_spec (pb->pb_op, &sdn);
+
+ /*
+ * Construct the new DN (code copied from backend
+ * and modified to handle newsuperior)
+ */
+ newdn = slapi_moddn_get_newdn(&sdn,newrdn,newsuperior);
+
+ /*
+ * 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 ((err = slapi_mapping_tree_select_and_check(pb, newdn, &be, &referral, errorbuf)) != LDAP_SUCCESS)
+ {
+ send_ldap_result(pb, err, NULL, errorbuf, 0, NULL);
+ goto free_and_return_nolock;
+ }
+
+ if (referral)
+ {
+ int managedsait;
+
+ slapi_pblock_get(pb, SLAPI_MANAGEDSAIT, &managedsait);
+ if (managedsait)
+ {
+ send_ldap_result(pb, LDAP_UNWILLING_TO_PERFORM, NULL,
+ "cannot update referral", 0, NULL);
+ slapi_entry_free(referral);
+ goto free_and_return;
+ }
+
+ send_referrals_from_entry(pb,referral);
+ slapi_entry_free(referral);
+ goto free_and_return;
+ }
+
+ slapi_pblock_set(pb, SLAPI_BACKEND, be);
+
+ /* can get lastmod only after backend is selected */
+ slapi_pblock_get(pb, SLAPI_BE_LASTMOD, &lastmod);
+
+ /* if it is a replicated operation - leave lastmod attributes alone */
+ slapi_mods_init (&smods, 2);
+ if (!repl_op && lastmod)
+ {
+ modify_update_last_modified_attr(pb, &smods);
+ slapi_pblock_set(pb, SLAPI_MODIFY_MODS, (void*)slapi_mods_get_ldapmods_passout(&smods));
+ }
+ else {
+ slapi_mods_done (&smods);
+ }
+
+ /*
+ * call the pre-modrdn plugins. if they succeed, call
+ * the backend modrdn function. then call the
+ * post-modrdn plugins.
+ */
+ if (plugin_call_plugins(pb, internal_op ? SLAPI_PLUGIN_INTERNAL_PRE_MODRDN_FN :
+ SLAPI_PLUGIN_PRE_MODRDN_FN) == 0)
+ {
+ int rc= LDAP_OPERATIONS_ERROR;
+ slapi_pblock_set(pb, SLAPI_PLUGIN, be->be_database);
+ set_db_default_result_handlers(pb);
+ if (be->be_modrdn != NULL)
+ {
+ if ((rc = (*be->be_modrdn)(pb)) == 0)
+ {
+ Slapi_Entry *pse;
+ Slapi_Entry *ecopy;
+ /* we don't perform acl check for internal operations */
+ /* dont update aci store for remote acis */
+ if ((!internal_op) &&
+ (!slapi_be_is_flag_set(be,SLAPI_BE_FLAG_REMOTE_DATA)))
+ plugin_call_acl_mods_update (pb, SLAPI_OPERATION_MODRDN);
+
+ if (operation_is_flag_set(operation,OP_FLAG_ACTION_LOG_AUDIT))
+ write_audit_log_entry(pb); /* Record the operation in the audit log */
+
+ slapi_pblock_get(pb, SLAPI_ENTRY_POST_OP, &pse);
+ slapi_pblock_get(pb, SLAPI_ENTRY_PRE_OP, &ecopy);
+ /* GGOODREPL persistent search system needs the changenumber, oops. */
+ do_ps_service(pse, ecopy, LDAP_CHANGETYPE_MODDN, 0UL);
+ }
+ }
+ else
+ {
+ send_ldap_result(pb, LDAP_UNWILLING_TO_PERFORM, NULL, "Function not implemented", 0, NULL);
+ }
+
+ slapi_pblock_set(pb, SLAPI_PLUGIN_OPRETURN, &rc);
+ plugin_call_plugins(pb, internal_op ? SLAPI_PLUGIN_INTERNAL_POST_MODRDN_FN :
+ SLAPI_PLUGIN_POST_MODRDN_FN);
+ }
+
+free_and_return:
+ if (be)
+ slapi_be_Unlock(be);
+free_and_return_nolock:
+ {
+ /* Free up everything left in the PBlock */
+ Slapi_Entry *pse;
+ Slapi_Entry *ecopy;
+ LDAPMod **mods;
+ char *s;
+
+ slapi_ch_free((void **) &newdn);
+ slapi_sdn_done(&sdn);
+ slapi_pblock_get(pb, SLAPI_ENTRY_PRE_OP, &ecopy);
+ slapi_entry_free(ecopy);
+ slapi_pblock_get(pb, SLAPI_ENTRY_POST_OP, &pse);
+ slapi_entry_free(pse);
+ slapi_pblock_get( pb, SLAPI_MODIFY_MODS, &mods );
+ ldap_mods_free( mods, 1 );
+
+ /* retrieve these in case a pre- or post-op plugin has changed them */
+ slapi_pblock_get(pb, SLAPI_MODRDN_TARGET, &s);
+ slapi_ch_free((void **)&s);
+ slapi_pblock_get(pb, SLAPI_MODRDN_NEWRDN, &s);
+ slapi_ch_free((void **)&s);
+ slapi_pblock_get(pb, SLAPI_MODRDN_NEWSUPERIOR, &s);
+ slapi_ch_free((void **)&s);
+ slapi_pblock_get(pb, SLAPI_URP_NAMING_COLLISION_DN, &s);
+ slapi_ch_free((void **)&s);
+ }
+}
diff --git a/ldap/servers/slapd/modutil.c b/ldap/servers/slapd/modutil.c
new file mode 100644
index 00000000..ac5d2c5d
--- /dev/null
+++ b/ldap/servers/slapd/modutil.c
@@ -0,0 +1,774 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+/* modutil.c - modify utility routine */
+
+#include <stdio.h>
+#include <string.h>
+#include <time.h>
+#include <sys/types.h>
+#ifndef _WIN32
+#include <sys/socket.h>
+#endif /* _WIN32 */
+#include "slap.h"
+
+#define SIZE_INIT 4 /* initial element size */
+#define SIZE_INC 2 /* size increment */
+/*
+ * Free an array of LDAPMod structures. Just like ldap_mods_free,
+ * except that it assumes that the mods are expressed as a bervec.
+ */
+void
+freepmods( LDAPMod **pmods )
+{
+ int i;
+
+ for ( i = 0; pmods[ i ] != NULL; ++i ) {
+ if ( pmods[ i ]->mod_bvalues != NULL ) {
+ ber_bvecfree( pmods[ i ]->mod_bvalues );
+ }
+ if ( pmods[ i ]->mod_type != NULL ) {
+ slapi_ch_free((void**)&pmods[ i ]->mod_type );
+ }
+ slapi_ch_free((void**)&pmods[ i ] );
+ }
+ slapi_ch_free((void**)&pmods );
+}
+
+/* ======= Utility functions for manipulating a list of LDAPMods ======= */
+
+/*
+ * Slapi_Mods can be used in two ways:
+ * 1) To wrap an existing array of LDAPMods, or
+ * 2) To create a new array of LDAPMods.
+ *
+ * Slapi_Mods provides memory management and array manipulation
+ * functions to make using (LDAPMod**) easier.
+ *
+ */
+
+Slapi_Mods*
+slapi_mods_new()
+{
+ Slapi_Mods *mods;
+ mods = (Slapi_Mods*) slapi_ch_calloc (1, sizeof (Slapi_Mods));
+ return mods;
+}
+
+/*
+ * Initialise a Slapi_Mod suggesting how big an LDAPMod array is needed.
+ * It will be free'd when the Slapi_Mods is destroyed.
+ */
+void
+slapi_mods_init(Slapi_Mods *smods, int initCount)
+{
+ memset (smods, 0, sizeof (*smods));
+ smods->free_mods = 1;
+ if (initCount > 0)
+ {
+ smods->num_elements= initCount + 1; /* one for NULL element */
+ smods->mods = (LDAPMod **) slapi_ch_calloc( 1, smods->num_elements * sizeof(LDAPMod *) );
+ }
+}
+
+/*
+ * Initialise a Slapi_Mod passing in responsibility for the (LDAPMod **).
+ * It will be free'd when the Slapi_Mods is destroyed.
+ */
+void
+slapi_mods_init_passin(Slapi_Mods *smods, LDAPMod **mods)
+{
+ slapi_mods_init_byref(smods, mods);
+ smods->free_mods = 1;
+}
+
+/*
+ * Initialise a Slapi_Mod passing in a reference to the (LDAPMod **)
+ * It will *not* be free'd when the Slapi_Mods is destroyed.
+ */
+void
+slapi_mods_init_byref(Slapi_Mods *smods, LDAPMod **mods)
+{
+ memset (smods, 0, sizeof (*smods));
+ if(mods!=NULL)
+ {
+ smods->mods = mods;
+ for ( smods->num_mods = 0; mods[smods->num_mods] != NULL; smods->num_mods++ );
+ smods->num_elements= smods->num_mods+1; /* We assume there's nothing spare on the end. */
+ }
+}
+
+void
+slapi_mods_free(Slapi_Mods **smods)
+{
+ if(smods!=NULL && *smods!=NULL)
+ {
+ slapi_mods_done(*smods);
+ slapi_ch_free ((void**)smods);
+ *smods= NULL;
+ }
+}
+
+void
+slapi_mods_done(Slapi_Mods *smods)
+{
+ PR_ASSERT(smods!=NULL);
+ if (smods->mods!=NULL)
+ {
+ if(smods->free_mods)
+ {
+ ldap_mods_free (smods->mods, 1 /* Free the Array and the Elements */);
+ }
+ }
+ memset (smods, 0, sizeof(smods));
+}
+
+static void
+slapi_mods_add_one_element(Slapi_Mods *smods)
+{
+ int need = smods->num_mods + 2;
+ if ( smods->num_elements == 0 )
+ {
+ PR_ASSERT(smods->mods==NULL);
+ smods->num_elements = SIZE_INIT;
+ smods->mods = (LDAPMod **) slapi_ch_malloc( smods->num_elements * sizeof(LDAPMod *) );
+ smods->free_mods= 1;
+ }
+ if ( smods->num_elements < need )
+ {
+ PR_ASSERT(smods->free_mods);
+ smods->num_elements *= SIZE_INC;
+ smods->mods = (LDAPMod **) slapi_ch_realloc( (char *) smods->mods, smods->num_elements * sizeof(LDAPMod *) );
+ }
+}
+
+/*
+ * Shift everything down to make room to insert the new mod.
+ */
+void
+slapi_mods_insert_at(Slapi_Mods *smods, LDAPMod *mod, int pos)
+{
+ int i;
+ slapi_mods_add_one_element(smods);
+ for( i=smods->num_mods-1; i>=pos; i--)
+ {
+ smods->mods[i+1]= smods->mods[i];
+ }
+ smods->mods[pos]= mod;
+ smods->num_mods++;
+ smods->mods[smods->num_mods]= NULL;
+}
+
+void
+slapi_mods_insert_smod_at(Slapi_Mods *smods, Slapi_Mod *smod, int pos)
+{
+ slapi_mods_insert_at (smods, smod->mod, pos);
+}
+
+/*
+ * Shift everything down to make room to insert the new mod.
+ */
+void
+slapi_mods_insert_before(Slapi_Mods *smods, LDAPMod *mod)
+{
+ slapi_mods_insert_at(smods, mod, smods->iterator);
+ smods->iterator++;
+}
+
+void
+slapi_mods_insert_smod_before(Slapi_Mods *smods, Slapi_Mod *smod)
+{
+ slapi_mods_insert_before(smods, smod->mod);
+}
+
+/*
+ * Shift everything down to make room to insert the new mod.
+ */
+void
+slapi_mods_insert_after(Slapi_Mods *smods, LDAPMod *mod)
+{
+ slapi_mods_insert_at(smods, mod, smods->iterator+1);
+}
+
+void slapi_mods_insert_smod_after(Slapi_Mods *smods, Slapi_Mod *smod)
+{
+ slapi_mods_insert_after(smods, smod->mod);
+}
+
+/*
+ * Add the LDAPMod to the end of the array.
+ * Does NOT copy the mod.
+ */
+void
+slapi_mods_add_ldapmod(Slapi_Mods *smods, LDAPMod *mod)
+{
+ slapi_mods_insert_at(smods,mod,smods->num_mods);
+}
+
+void
+slapi_mods_add_smod(Slapi_Mods *smods, Slapi_Mod *smod)
+{
+ slapi_mods_add_ldapmod(smods, smod->mod);
+}
+
+/*
+ * Makes a copy of everything.
+ */
+void
+slapi_mods_add_modbvps( Slapi_Mods *smods, int modtype, const char *type, struct berval **bvps )
+{
+ LDAPMod *mod;
+
+ mod = (LDAPMod *) slapi_ch_malloc(sizeof(LDAPMod));
+ mod->mod_type = slapi_ch_strdup( type );
+ mod->mod_op = modtype | LDAP_MOD_BVALUES;
+ mod->mod_bvalues = NULL;
+
+ if (NULL != bvps)
+ {
+ int num_values, i;
+ num_values = 0;
+ /* Count mods */
+ while (NULL != bvps[num_values])
+ {
+ num_values++;
+ }
+ mod->mod_bvalues = (struct berval **)slapi_ch_malloc((num_values + 1) *
+ sizeof(struct berval *));
+ for (i = 0; i < num_values; i++)
+ {
+ mod->mod_bvalues[i] = ber_bvdup((struct berval *)bvps[i]); /* jcm had to cast away const */
+ }
+ mod->mod_bvalues[num_values] = NULL;
+ }
+ slapi_mods_add_ldapmod(smods, mod);
+}
+
+/*
+ * Makes a copy of everything.
+ */
+void
+slapi_mods_add_mod_values( Slapi_Mods *smods, int modtype, const char *type, Slapi_Value **va )
+{
+ LDAPMod *mod= (LDAPMod *) slapi_ch_malloc( sizeof(LDAPMod) );
+ mod->mod_type = slapi_ch_strdup( type );
+ mod->mod_op = modtype | LDAP_MOD_BVALUES;
+ mod->mod_bvalues= NULL;
+ valuearray_get_bervalarray(va,&mod->mod_bvalues);
+ slapi_mods_add_ldapmod(smods, mod);
+}
+
+/*
+ * Makes a copy of everything.
+ */
+void
+slapi_mods_add( Slapi_Mods *smods, int modtype, const char *type, unsigned long len, const char *val)
+{
+ struct berval bv;
+ struct berval *bvps[2];
+ if(len>0)
+ {
+ bv.bv_len= len;
+ bv.bv_val= (void*)val; /* We cast away the const, but we're not going to change anything */
+ bvps[0] = &bv;
+ bvps[1] = NULL;
+ }
+ else
+ {
+ bvps[0]= NULL;
+ }
+ slapi_mods_add_modbvps( smods, modtype, type, bvps );
+}
+
+/*
+ * Makes a copy of everything.
+ */
+void
+slapi_mods_add_string( Slapi_Mods *smods, int modtype, const char *type, const char *val)
+{
+ slapi_mods_add( smods, modtype, type, strlen(val), val);
+}
+
+void
+slapi_mods_remove(Slapi_Mods *smods)
+{
+ smods->mods[smods->iterator]->mod_op= LDAP_MOD_IGNORE;
+}
+
+LDAPMod *
+slapi_mods_get_first_mod(Slapi_Mods *smods)
+{
+ /* Reset the iterator in the mod structure */
+ smods->iterator= -1;
+ return slapi_mods_get_next_mod(smods);
+}
+
+LDAPMod *
+slapi_mods_get_next_mod(Slapi_Mods *smods)
+{
+ /* Move the iterator forward */
+ LDAPMod *r= NULL;
+ smods->iterator++;
+
+ PR_ASSERT (smods->iterator >= 0);
+
+ /* skip deleted mods if any */
+ while (smods->iterator < smods->num_mods && smods->mods[smods->iterator]->mod_op == LDAP_MOD_IGNORE)
+ smods->iterator ++;
+
+ if(smods->iterator<smods->num_mods)
+ {
+ r= smods->mods[smods->iterator];
+ }
+ return r;
+}
+
+static void
+mod2smod (LDAPMod *mod, Slapi_Mod *smod)
+{
+ smod->mod = mod;
+ smod->iterator = 0;
+ smod->num_values = 0;
+
+ if (mod->mod_op & LDAP_MOD_BVALUES)
+ {
+ while (mod->mod_bvalues[smod->num_values])
+ {
+ smod->num_values ++;
+ }
+ }
+ else
+ {
+ PR_ASSERT(0); /* ggood shouldn't ever use string values in server */
+ while (mod->mod_values[smod->num_values])
+ {
+ smod->num_values ++;
+ }
+ }
+
+ smod->num_elements = smod->num_values + 1; /* 1- for null char */
+}
+
+Slapi_Mod *
+slapi_mods_get_first_smod(Slapi_Mods *smods, Slapi_Mod *smod)
+{
+ LDAPMod *mod = slapi_mods_get_first_mod (smods);
+
+ if (mod == NULL)
+ return NULL;
+
+ mod2smod (mod, smod);
+
+ return smod;
+}
+
+Slapi_Mod *
+slapi_mods_get_next_smod(Slapi_Mods *smods, Slapi_Mod *smod)
+{
+ LDAPMod *mod = slapi_mods_get_next_mod(smods);
+
+ if (mod == NULL)
+ {
+ return NULL;
+ }
+ else
+ {
+ mod2smod(mod, smod);
+ }
+ return smod;
+}
+
+void
+slapi_mods_iterator_backone(Slapi_Mods *smods)
+{
+ smods->iterator--;
+}
+
+
+static void
+pack_mods(LDAPMod ***modsp)
+{
+ LDAPMod **mods = NULL;
+ if (NULL != modsp && NULL != *modsp)
+ {
+ int i;
+ int num_slots;
+ int src_index, dst_index;
+ mods = *modsp;
+
+ /* Make a pass through the array, freeing any marked LDAP_MODS_IGNORE */
+ i = 0;
+ while (NULL != mods[i])
+ {
+ if (LDAP_MOD_IGNORE == (mods[i]->mod_op & ~LDAP_MOD_BVALUES))
+ {
+ /* Free current slot */
+ slapi_ch_free((void**)&mods[i]->mod_type);
+ ber_bvecfree(mods[i]->mod_bvalues);
+ slapi_ch_free((void **)&mods[i]);
+ }
+ i++;
+ }
+ num_slots = i + 1; /* Remember total number of slots */
+
+ /* Make another pass, packing the array */
+ dst_index = src_index = 0;
+ while (src_index < num_slots)
+ {
+ if (NULL != mods[src_index])
+ {
+ mods[dst_index] = mods[src_index];
+ dst_index++;
+ }
+ src_index++;
+ }
+ mods[dst_index] = NULL;
+ if (NULL == mods[0])
+ {
+ /* Packed it down to size zero - deallocate */
+ slapi_ch_free((void **)modsp);
+ }
+ }
+}
+
+
+LDAPMod **
+slapi_mods_get_ldapmods_byref(Slapi_Mods *smods)
+{
+ pack_mods(&smods->mods); /* XXXggood const gets in the way of this */
+ return smods->mods;
+}
+
+LDAPMod **
+slapi_mods_get_ldapmods_passout(Slapi_Mods *smods)
+{
+ LDAPMod **mods;
+ pack_mods(&smods->mods);
+ mods = smods->mods;
+ smods->free_mods = 0;
+ slapi_mods_done(smods);
+ return mods;
+}
+
+int
+slapi_mods_get_num_mods(const Slapi_Mods *smods)
+{
+ return smods->num_mods;
+}
+
+void
+slapi_mods_dump(const Slapi_Mods *smods, const char *text)
+{
+ int i;
+ LDAPDebug( LDAP_DEBUG_ANY, "smod - %s\n", text, 0, 0);
+ for(i=0;i<smods->num_mods;i++)
+ {
+ slapi_mod_dump(smods->mods[i],i);
+ }
+}
+
+/* ======== Utility functions for manipulating an LDAPMod ======= */
+
+/*
+ * Slapi_Mod can be used in two ways:
+ * 1) To wrap an existing array of LDAPMods, or
+ * 2) To create a new array of LDAPMods.
+ *
+ * Slapi_Mods provides memory management and array manipulation
+ * functions to make using (LDAPMod**) easier.
+ *
+ */
+
+Slapi_Mod *
+slapi_mod_new ()
+{
+ Slapi_Mod *mod = (Slapi_Mod*)slapi_ch_calloc (1, sizeof (Slapi_Mod));
+ return mod;
+}
+
+void
+slapi_mod_init(Slapi_Mod *smod, int initCount)
+{
+ PR_ASSERT(smod!=NULL);
+ memset (smod, 0, sizeof (*smod));
+ smod->free_mod= 1;
+ smod->num_elements = initCount + 1;
+ smod->mod = (LDAPMod *)slapi_ch_calloc (1, sizeof (LDAPMod));
+ if (smod->num_elements)
+ smod->mod->mod_bvalues = (struct berval**)slapi_ch_calloc (smod->num_elements, sizeof (struct berval*));
+}
+
+void
+slapi_mod_init_passin(Slapi_Mod *smod, LDAPMod *mod)
+{
+ PR_ASSERT(smod!=NULL);
+ memset (smod, 0, sizeof (*smod));
+ smod->free_mod= 1;
+ if (mod!=NULL)
+ {
+ smod->mod= mod;
+ if(smod->mod->mod_bvalues!=NULL)
+ {
+ while (smod->mod->mod_bvalues[smod->num_values]!=NULL)
+ smod->num_values++;
+ smod->num_elements= smod->num_values +1; /* We assume there's nothing spare on the end. */
+ }
+ }
+}
+
+void
+slapi_mod_init_byref(Slapi_Mod *smod, LDAPMod *mod)
+{
+ PR_ASSERT(smod!=NULL);
+ memset (smod, 0, sizeof (*smod));
+ if (mod!=NULL)
+ {
+ smod->mod= mod;
+ if(smod->mod->mod_bvalues!=NULL)
+ {
+ while (smod->mod->mod_bvalues[smod->num_values]!=NULL)
+ smod->num_values++;
+ smod->num_elements= smod->num_values +1; /* We assume there's nothing spare on the end. */
+ }
+ }
+}
+
+void
+slapi_mod_init_byval (Slapi_Mod *smod, const LDAPMod *mod)
+{
+ PR_ASSERT(smod!=NULL);
+ memset (smod, 0, sizeof (*smod));
+ if (mod!=NULL)
+ {
+ smod->mod = (LDAPMod *)slapi_ch_calloc (1, sizeof (LDAPMod));
+ smod->free_mod = 1;
+ slapi_mod_set_operation (smod, mod->mod_op);
+ slapi_mod_set_type (smod, mod->mod_type);
+ if(mod->mod_bvalues!=NULL)
+ {
+ while (mod->mod_bvalues[smod->num_values]!=NULL)
+ {
+ slapi_mod_add_value (smod, mod->mod_bvalues[smod->num_values]);
+ }
+ }
+ }
+}
+
+void
+slapi_mod_free (Slapi_Mod **smod)
+{
+ slapi_mod_done(*smod);
+ slapi_ch_free((void**)smod);
+ *smod= NULL;
+}
+
+void
+slapi_mod_done(Slapi_Mod *smod)
+{
+ PR_ASSERT(smod!=NULL);
+ if(smod->free_mod)
+ {
+ ber_bvecfree(smod->mod->mod_bvalues);
+ slapi_ch_free((void**)&(smod->mod->mod_type));
+ slapi_ch_free((void**)&(smod->mod));
+ }
+ memset (smod, 0, sizeof(smod));
+}
+
+/*
+ * Add a value to the list of values in the modification.
+ */
+void
+slapi_mod_add_value(Slapi_Mod *smod, const struct berval *val)
+{
+ PR_ASSERT(smod!=NULL);
+ PR_ASSERT(val!=NULL);
+/* PR_ASSERT(slapi_mod_get_operation(smod) & LDAP_MOD_BVALUES);*/
+
+ bervalarray_add_berval_fast(&(smod->mod->mod_bvalues),(struct berval*)val,smod->num_values,&smod->num_elements);
+ smod->num_values++;
+}
+
+/*
+ * Remove the value at the iterator from the list of values in
+ * the LDAP modification.
+ */
+void
+slapi_mod_remove_value(Slapi_Mod *smod)
+{
+ /* loop over the mod values moving them down to cover up the value to be removed */
+ struct berval **vals;
+ int i, k;
+ PR_ASSERT(smod!=NULL);
+ PR_ASSERT(smod->mod!=NULL);
+ vals= smod->mod->mod_bvalues;
+ i= smod->iterator-1;
+ ber_bvfree( vals[i] );
+ for ( k = i + 1; vals[k] != NULL; k++ ) {
+ vals[k - 1] = vals[k];
+ }
+ vals[k - 1] = NULL;
+ /* Reset the iterator */
+ smod->num_values--;
+ smod->iterator--;
+}
+
+struct berval *
+slapi_mod_get_first_value(Slapi_Mod *smod)
+{
+ PR_ASSERT(smod!=NULL);
+ /* Reset the iterator in the mod structure */
+ smod->iterator= 0;
+ return slapi_mod_get_next_value(smod);
+}
+
+struct berval *
+slapi_mod_get_next_value(Slapi_Mod *smod)
+{
+ /* Move the iterator forward */
+ struct berval *r= NULL;
+ PR_ASSERT(smod!=NULL);
+ PR_ASSERT(smod->mod!=NULL);
+ if(smod->iterator<smod->num_values)
+ {
+ r= smod->mod->mod_bvalues[smod->iterator];
+ smod->iterator++;
+ }
+ return r;
+}
+
+int
+slapi_mod_get_num_values(const Slapi_Mod *smod)
+{
+ PR_ASSERT(smod!=NULL);
+ return smod->num_values;
+}
+
+const char *
+slapi_mod_get_type (const Slapi_Mod *smod)
+{
+ PR_ASSERT(smod!=NULL);
+ PR_ASSERT(smod->mod!=NULL);
+ return smod->mod->mod_type;
+}
+
+int
+slapi_mod_get_operation (const Slapi_Mod *smod)
+{
+ PR_ASSERT(smod!=NULL);
+ PR_ASSERT(smod->mod!=NULL);
+ return smod->mod->mod_op;
+}
+
+void
+slapi_mod_set_type (Slapi_Mod *smod, const char *type)
+{
+ PR_ASSERT(smod!=NULL);
+ PR_ASSERT(smod->mod!=NULL);
+ if(smod->mod->mod_type!=NULL)
+ {
+ slapi_ch_free((void**)&smod->mod->mod_type);
+ }
+ smod->mod->mod_type = slapi_ch_strdup (type);
+}
+
+void
+slapi_mod_set_operation (Slapi_Mod *smod, int op)
+{
+ PR_ASSERT(smod!=NULL);
+ PR_ASSERT(smod->mod!=NULL);
+ smod->mod->mod_op = op;
+}
+
+const LDAPMod *
+slapi_mod_get_ldapmod_byref(const Slapi_Mod *smod)
+{
+ PR_ASSERT(smod!=NULL);
+ return smod->mod;
+}
+
+LDAPMod *
+slapi_mod_get_ldapmod_passout(Slapi_Mod *smod)
+{
+ LDAPMod *mod;
+ PR_ASSERT(smod!=NULL);
+ mod= smod->mod;
+ smod->free_mod= 0;
+ slapi_mod_done(smod);
+ return mod;
+}
+
+/* a valid LDAPMod is one with operation of LDAP_MOD_ADD, *_DELETE, *_REPLACE
+ ored with LDAP_MOD_BVALUES; non-null type and at list one value
+ for add and replace operations
+ */
+int
+slapi_mod_isvalid (const Slapi_Mod *mod)
+{
+ int op;
+
+ if (mod == NULL || mod->mod == NULL)
+ return 0;
+
+ op = mod->mod->mod_op && ~LDAP_MOD_BVALUES;
+
+ if (op != LDAP_MOD_ADD && op != LDAP_MOD_DELETE && op != LDAP_MOD_REPLACE)
+ return 0;
+
+ if (mod->mod->mod_type == NULL)
+ return 0;
+
+ if (op != LDAP_MOD_DELETE && mod->num_values == 0)
+ return 0;
+
+ return 1;
+}
+
+void
+slapi_mod_dump(LDAPMod *mod, int n)
+{
+ if(mod!=NULL)
+ {
+ int operationtype= mod->mod_op & ~LDAP_MOD_BVALUES;
+ switch ( operationtype )
+ {
+ case LDAP_MOD_ADD:
+ LDAPDebug( LDAP_DEBUG_ANY, "smod %d - add: %s\n", n, mod->mod_type, 0);
+ break;
+
+ case LDAP_MOD_DELETE:
+ LDAPDebug( LDAP_DEBUG_ANY, "smod %d - delete: %s\n", n, mod->mod_type, 0);
+ break;
+
+ case LDAP_MOD_REPLACE:
+ LDAPDebug( LDAP_DEBUG_ANY, "smod %d - replace: %s\n", n, mod->mod_type, 0);
+ break;
+
+ case LDAP_MOD_IGNORE:
+ LDAPDebug( LDAP_DEBUG_ANY, "smod %d - ignore: %s\n", n, mod->mod_type, 0);
+ break;
+ }
+ if(operationtype!=LDAP_MOD_IGNORE)
+ {
+ int i;
+ for ( i = 0; mod->mod_bvalues != NULL && mod->mod_bvalues[i] != NULL; i++ )
+ {
+ char *buf, *bufp;
+ int len = strlen( mod->mod_type );
+ len = LDIF_SIZE_NEEDED( len, mod->mod_bvalues[i]->bv_len ) + 1;
+ buf = slapi_ch_malloc( len );
+ bufp = buf;
+ ldif_put_type_and_value( &bufp, mod->mod_type, mod->mod_bvalues[i]->bv_val, mod->mod_bvalues[i]->bv_len );
+ *bufp = '\0';
+ LDAPDebug( LDAP_DEBUG_ANY, "smod %d - value: %s", n, buf, 0);
+ slapi_ch_free( (void**)&buf );
+ }
+ }
+ }
+ else
+ {
+ LDAPDebug( LDAP_DEBUG_ANY, "smod - null\n", 0, 0, 0);
+ }
+}
diff --git a/ldap/servers/slapd/monitor.c b/ldap/servers/slapd/monitor.c
new file mode 100644
index 00000000..f877e679
--- /dev/null
+++ b/ldap/servers/slapd/monitor.c
@@ -0,0 +1,176 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+/*
+ * 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 <stdio.h>
+#include <string.h>
+#include <sys/types.h>
+#include <time.h>
+#ifndef _WIN32
+#include <sys/socket.h>
+#endif
+#include "slap.h"
+#include "fe.h"
+
+#if defined( SLAPD_MONITOR_DN )
+
+
+int
+monitor_info(Slapi_PBlock *pb, Slapi_Entry* e, Slapi_Entry* entryAfter, int *returncode, char *returntext, void *arg)
+{
+ char buf[BUFSIZ];
+ struct berval val;
+ struct berval *vals[2];
+ time_t curtime = current_time();
+ struct tm utm;
+ Slapi_Backend *be;
+ char *cookie;
+ PRUint32 len;
+
+ vals[0] = &val;
+ vals[1] = NULL;
+
+ /* "version" value */
+ val.bv_val = slapd_get_version_value();
+ val.bv_len = strlen( val.bv_val );
+ attrlist_replace( &e->e_attrs, "version", vals );
+ slapi_ch_free( (void **) &val.bv_val );
+
+ sprintf( buf, "%d", active_threads );
+ val.bv_val = buf;
+ val.bv_len = strlen( buf );
+ attrlist_replace( &e->e_attrs, "threads", vals );
+
+ connection_table_as_entry(the_connection_table, e);
+
+ PR_Lock( ops_mutex );
+ sprintf( buf, "%ld", (long) ops_initiated );
+ PR_Unlock( ops_mutex );
+ val.bv_val = buf;
+ val.bv_len = strlen( buf );
+ attrlist_replace( &e->e_attrs, "opsinitiated", vals );
+
+ PR_Lock( ops_mutex );
+ sprintf( buf, "%ld", (long) ops_completed );
+ PR_Unlock( ops_mutex );
+ val.bv_val = buf;
+ val.bv_len = strlen( buf );
+ attrlist_replace( &e->e_attrs, "opscompleted", vals );
+
+ PR_Lock( g_get_num_sent_mutex() );
+ len = PR_snprintf ( buf, BUFSIZ, "%llu", g_get_num_entries_sent() );
+ PR_Unlock( g_get_num_sent_mutex() );
+ val.bv_val = buf;
+ val.bv_len = ( unsigned long ) len;
+ attrlist_replace( &e->e_attrs, "entriessent", vals );
+
+ PR_Lock( g_get_num_sent_mutex() );
+ len = PR_snprintf ( buf, BUFSIZ, "%llu", g_get_num_bytes_sent() );
+ PR_Unlock( g_get_num_sent_mutex() );
+ val.bv_val = buf;
+ val.bv_len = ( unsigned long ) len;
+ attrlist_replace( &e->e_attrs, "bytessent", vals );
+
+#ifdef _WIN32
+ {
+ struct tm *pt;
+ pt = gmtime( &curtime );
+ memcpy(&utm, pt, sizeof(struct tm) );
+ }
+#else
+ gmtime_r( &curtime, &utm );
+#endif
+ strftime( buf, sizeof(buf), "%Y%m%d%H%M%SZ", &utm );
+ val.bv_val = buf;
+ val.bv_len = strlen( buf );
+ attrlist_replace( &e->e_attrs, "currenttime", vals );
+
+#ifdef _WIN32
+ {
+ struct tm *pt;
+ pt = gmtime( &starttime );
+ memcpy(&utm, pt, sizeof(struct tm) );
+ }
+#else
+ gmtime_r( &starttime, &utm );
+#endif
+ strftime( buf, sizeof(buf), "%Y%m%d%H%M%SZ", &utm );
+ val.bv_val = buf;
+ val.bv_len = strlen( buf );
+ attrlist_replace( &e->e_attrs, "starttime", vals );
+
+ sprintf( buf, "%d", be_nbackends_public() );
+ val.bv_val = buf;
+ val.bv_len = strlen( buf );
+ attrlist_replace( &e->e_attrs, "nbackends", vals );
+
+#ifdef THREAD_SUNOS5_LWP
+ sprintf( buf, "%d", thr_getconcurrency() );
+ val.bv_val = buf;
+ val.bv_len = strlen( buf );
+ attrlist_replace( &e->e_attrs, "concurrency", vals );
+#endif
+
+ /*Loop through the backends, and stuff the monitordns
+ into the entry we're sending back*/
+ attrlist_delete( &e->e_attrs, "backendmonitordn");
+ cookie = NULL;
+ be = slapi_get_first_backend(&cookie);
+ while (be)
+ {
+ if ( !be->be_private )
+ {
+ Slapi_DN dn;
+ slapi_sdn_init(&dn);
+ be_getmonitordn(be,&dn);
+ val.bv_val = (char*)slapi_sdn_get_dn(&dn);
+ val.bv_len = strlen( val.bv_val );
+ attrlist_merge( &e->e_attrs, "backendmonitordn", vals );
+ slapi_sdn_done(&dn);
+ }
+ be = slapi_get_next_backend(cookie);
+ }
+
+ slapi_ch_free((void **)&cookie);
+
+ *returncode= LDAP_SUCCESS;
+ return SLAPI_DSE_CALLBACK_OK;
+}
+
+#endif /* SLAPD_MONITOR_DN */
+
+
+/*
+ * Return a malloc'd version value.
+ * Used for the monitor entry's 'version' attribute.
+ * Used for the root DSE's 'vendorVersion' attribute.
+ */
+char *
+slapd_get_version_value( void )
+{
+ char *versionstring, *buildnum, *vs;
+
+ versionstring = config_get_versionstring();
+ buildnum = config_get_buildnum();
+
+ vs = slapi_ch_malloc( strlen(versionstring) + strlen( buildnum) + 3 );
+ sprintf( vs, "%s B%s", versionstring, buildnum );
+
+ slapi_ch_free( (void **) &buildnum);
+ slapi_ch_free( (void **) &versionstring);
+
+ return vs;
+}
diff --git a/ldap/servers/slapd/ntmsgdll/Makefile b/ldap/servers/slapd/ntmsgdll/Makefile
new file mode 100644
index 00000000..06b7a1bc
--- /dev/null
+++ b/ldap/servers/slapd/ntmsgdll/Makefile
@@ -0,0 +1,62 @@
+#
+# BEGIN COPYRIGHT BLOCK
+# Copyright 2001 Sun Microsystems, Inc.
+# Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+# All rights reserved.
+# END COPYRIGHT BLOCK
+#
+#
+# GNU Makefile for Directory Server NT messages DLL
+#
+
+LDAP_SRC = ../../..
+MCOM_ROOT = ../../../../..
+
+NOSTDCLEAN=true # don't let nsconfig.mk define target clean
+NOSTDSTRIP=true # don't let nsconfig.mk define target strip
+NSPR20=true # probably should be defined somewhere else (not sure where)
+
+OBJDEST = $(OBJDIR)/ntmsgdll/obj
+BINDIR = $(LDAP_SERVER_RELDIR)
+
+include $(MCOM_ROOT)/ldapserver/nsconfig.mk
+include $(LDAP_SRC)/nsldap.mk
+
+MSGFILE = slapdmessages30
+
+MSG_SRC = ntslapdmessages
+
+NTMSGDLL = $(addprefix $(BINDIR)/, $(MSGFILE).$(DLL_SUFFIX))
+
+LDAP_MSG_OBJS= $(MSG_SRC).o $(MSG_SRC).res
+
+OBJS = $(addprefix $(OBJDEST)/, $(LDAP_MSG_OBJS))
+
+clientSDK: all
+
+all: $(OBJDEST) $(BINDIR) $(NTMSGDLL)
+
+veryclean: clean
+
+clean:
+ $(RM) $(OBJS)
+ $(RM) $(OBJDEST)/msg00001.bin
+ $(RM) $(LDAP_HDIR)/$(MSG_SRC).h
+ $(RM) $(OBJDEST)/$(MSG_SRC).rc
+ $(RM) $(NTMSGDLL)
+
+$(OBJDEST):
+ $(MKDIR) $@
+
+$(NTMSGDLL): $(OBJS) $(LDAP_HDIR)/$(MSG_SRC).h
+ $(LINK_DLL)
+
+$(LDAP_HDIR)/$(MSG_SRC).h: $(MSG_SRC).mc
+
+$(OBJDEST)/msg0001.bin: $(MSG_SRC).mc
+
+$(OBJDEST)/$(MSG_SRC).rc: $(MSG_SRC).mc
+ $(MC) $< -h $(LDAP_HDIR) -r $(OBJDEST)
+
+$(OBJDEST)/$(MSG_SRC).res: $(OBJDEST)/$(MSG_SRC).rc
+ $(RSC) -fo $(OBJDEST)/$(MSG_SRC).res $<
diff --git a/ldap/servers/slapd/ntmsgdll/ntslapdmessages.c b/ldap/servers/slapd/ntmsgdll/ntslapdmessages.c
new file mode 100644
index 00000000..9d8e9ab5
--- /dev/null
+++ b/ldap/servers/slapd/ntmsgdll/ntslapdmessages.c
@@ -0,0 +1,16 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+
+/* This is the required stub entry point for the message-table DLL */
+#if defined( _WIN32 )
+#include <windows.h>
+
+BOOL WINAPI DllMain(HINSTANCE hDLL, DWORD dwReason, LPVOID lpReserved)
+{
+ return(TRUE);
+}
+
+#endif /* _WIN32 */
diff --git a/ldap/servers/slapd/ntmsgdll/ntslapdmessages.mc b/ldap/servers/slapd/ntmsgdll/ntslapdmessages.mc
new file mode 100644
index 00000000..242df822
--- /dev/null
+++ b/ldap/servers/slapd/ntmsgdll/ntslapdmessages.mc
@@ -0,0 +1,283 @@
+; /*
+; BEGIN COPYRIGHT BLOCK
+; Copyright 2001 Sun Microsystems, Inc.
+; Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+; All rights reserved.
+; END COPYRIGHT BLOCK
+;
+; Microsoft Developer Support
+; Copyright (c) 1992 Microsoft Corporation
+;
+; This file contains the message definitions for the Win32
+; messages.exe sample program.
+
+
+;-------------------------------------------------------------------------
+; HEADER SECTION
+;
+; The header section defines names and language identifiers for use
+; by the message definitions later in this file. The MessageIdTypedef,
+; SeverityNames, FacilityNames, and LanguageNames keywords are
+; optional and not required.
+;
+;
+;
+; The MessageIdTypedef keyword gives a typedef name that is used in a
+; type cast for each message code in the generated include file. Each
+; message code appears in the include file with the format: #define
+; name ((type) 0xnnnnnnnn) The default value for type is empty, and no
+; type cast is generated. It is the programmer's responsibility to
+; specify a typedef statement in the application source code to define
+; the type. The type used in the typedef must be large enough to
+; accomodate the entire 32-bit message code.
+;
+MessageIdTypedef=DWORD
+;
+; The SeverityNames keyword defines the set of names that are allowed
+; as the value of the Severity keyword in the message definition. The
+; set is delimited by left and right parentheses. Associated with each
+; severity name is a number that, when shifted left by 30, gives the
+; bit pattern to logical-OR with the Facility value and MessageId
+; value to form the full 32-bit message code. The default value of
+; this keyword is:
+;
+; SeverityNames=(
+; Success=0x0
+; Informational=0x1
+; Warning=0x2
+; Error=0x3
+; )
+;
+; Severity values occupy the high two bits of a 32-bit message code.
+; Any severity value that does not fit in two bits is an error. The
+; severity codes can be given symbolic names by following each value
+; with :name
+;
+SeverityNames=(Success=0x0:STATUS_SEVERITY_SUCCESS
+ Informational=0x1:STATUS_SEVERITY_INFORMATIONAL
+ Warning=0x2:STATUS_SEVERITY_WARNING
+ Error=0x3:STATUS_SEVERITY_ERROR
+ )
+;
+; The FacilityNames keyword defines the set of names that are allowed
+; as the value of the Facility keyword in the message definition. The
+; set is delimited by left and right parentheses. Associated with each
+; facility name is a number that, when shift it left by 16 bits, gives
+; the bit pattern to logical-OR with the Severity value and MessageId
+; value to form the full 32-bit message code. The default value of
+; this keyword is:
+;
+; FacilityNames=(
+; System=0x0FF
+; Application=0xFFF
+; )
+;
+; Facility codes occupy the low order 12 bits of the high order
+; 16-bits of a 32-bit message code. Any facility code that does not
+; fit in 12 bits is an error. This allows for 4,096 facility codes.
+; The first 256 codes are reserved for use by the system software. The
+; facility codes can be given symbolic names by following each value
+; with :name
+;
+FacilityNames=(System=0x0FF:FACILITY_SYSTEM
+ Runtime=0x200:FACILITY_RUNTIME
+ Cgi=0x201:FACILITY_CGI
+ Slapd=0x202:FACILITY_SERVICE
+ Network=0x203:FACILITY_NETWORK
+ Startup=0x204:FACILITY_STARTUP
+ Filesytem=0x205:FACILITY_FILESYSTEM
+ Registry=0x206:FACILITY_REGISTRY
+ )
+;
+; The LanguageNames keyword defines the set of names that are allowed
+; as the value of the Language keyword in the message definition. The
+; set is delimited by left and right parentheses. Associated with each
+; language name is a number and a file name that are used to name the
+; generated resource file that contains the messages for that
+; language. The number corresponds to the language identifier to use
+; in the resource table. The number is separated from the file name
+; with a colon. The initial value of LanguageNames is:
+;
+; LanguageNames=(English=1:MSG00001)
+;
+; Any new names in the source file which don't override the built-in
+; names are added to the list of valid languages. This allows an
+; application to support private languages with descriptive names.
+;
+;
+;-------------------------------------------------------------------------
+; MESSAGE DEFINITION SECTION
+;
+; Following the header section is the body of the Message Compiler
+; source file. The body consists of zero or more message definitions.
+; Each message definition begins with one or more of the following
+; statements:
+;
+; MessageId = [number|+number]
+; Severity = severity_name
+; Facility = facility_name
+; SymbolicName = name
+;
+; The MessageId statement marks the beginning of the message
+; definition. A MessageID statement is required for each message,
+; although the value is optional. If no value is specified, the value
+; used is the previous value for the facility plus one. If the value
+; is specified as +number then the value used is the previous value
+; for the facility, plus the number after the plus sign. Otherwise, if
+; a numeric value is given, that value is used. Any MessageId value
+; that does not fit in 16 bits is an error.
+;
+; The Severity and Facility statements are optional. These statements
+; specify additional bits to OR into the final 32-bit message code. If
+; not specified they default to the value last specified for a message
+; definition. The initial values prior to processing the first message
+; definition are:
+;
+; Severity=Success
+; Facility=Application
+;
+; The value associated with Severity and Facility must match one of
+; the names given in the FacilityNames and SeverityNames statements in
+; the header section. The SymbolicName statement allows you to
+; associate a C/C++ symbolic constant with the final 32-bit message
+; code.
+; */
+
+MessageId=0x1
+Severity=Success
+Facility=Startup
+SymbolicName=MSG_SERVER_STARTED
+Language=English
+Netscape Directory Server: %1.
+The directory server has successfully started.
+.
+MessageId=0x2
+Severity=Success
+Facility=Slapd
+SymbolicName=MSG_SERVER_SHUTDOWN
+Language=English
+Netscape Directory Server: %1.
+The directory server has shutdown.
+.
+MessageId=0x3
+Severity=Success
+Facility=Slapd
+SymbolicName=MSG_SERVER_STOPPED
+Language=English
+Netscape Directory Server: %1.
+The directory server has stopped.
+.
+MessageId=0x4
+Severity=Success
+Facility=Startup
+SymbolicName=MSG_SERVER_STARTING
+Language=English
+Netscape Directory Server: %1.
+The directory server is starting.
+.
+MessageId=0x5
+Severity=Error
+Facility=Startup
+SymbolicName=MSG_SERVER_START_FAILED
+Language=English
+Netscape Directory Server: %1.
+The directory server has failed on startup. %2
+.
+MessageId=0x6
+Severity=Error
+Facility=Startup
+SymbolicName=MSG_SERVER_START_FAILED_CTRL_HANDLER
+Language=English
+Netscape Directory Server: %1.
+The directory server has failed on startup. Failed to Register with Service Control handler.
+.
+MessageId=0x7
+Severity=Error
+Facility=Startup
+SymbolicName=MSG_SERVER_PASSWORD_DIALOG_FAILED
+Language=English
+Netscape Directory Server: %1.
+The directory server failed to create the Key Database Passphrase dialog.
+.
+MessageId=0x8
+Severity=Error
+Facility=Registry
+SymbolicName=MSG_WD_REGISTRY
+Language=English
+Netscape Directory Server: %1.
+Could not open registry key: %2
+.
+MessageId=0x9
+Severity=Error
+Facility=Startup
+SymbolicName=MSG_WD_BADCMDLINE
+Language=English
+Netscape Directory Server: %1
+Invalid command line specified: %2
+.
+MessageId=0xA
+Severity=Error
+Facility=Startup
+SymbolicName=MSG_WD_BADPASSWORD
+Language=English
+Netscape Directory Server: %1.
+Incorrect SSL password entered.
+.
+MessageId=0xB
+Severity=Error
+Facility=Startup
+SymbolicName=MSG_WD_STRING
+Language=English
+Netscape Directory Server: %1.
+%2
+.
+MessageId=0xC
+Severity=Error
+Facility=Startup
+SymbolicName=MSG_WD_STARTFAILED
+Language=English
+Netscape Directory Server: %1.
+The directory server could not be started.
+Command line used: %2
+.
+MessageId=0xD
+Severity=Error
+Facility=Startup
+SymbolicName=MSG_WD_RESTART
+Language=English
+Netscape Directory Server: %1.
+The directory server terminated abnormally with error code %2.
+An attempt will be made to restart it.
+.
+MessageId=0xE
+Severity=Error
+Facility=Startup
+SymbolicName=MSG_CRON_STARTFAILED
+Language=English
+Netscape Directory Server: %1.
+The scheduled job (%2) could not be started.
+.
+MessageId=0xF
+Severity=Error
+Facility=Slapd
+SymbolicName=MSG_SERVER_SHUTDOWN_STARTING
+Language=English
+Netscape Directory Server: %1.
+The directory server is shutting down.
+.
+MessageId=0x10
+Severity=Error
+Facility=Startup
+SymbolicName=MSG_SERVER_KEYDB_PASSPHRASE_WRONG
+Language=English
+Netscape Directory Server: %1.
+The specified key database passphrase is incorrect.
+.
+MessageId=0x11
+Severity=Error
+Facility=Slapd
+SymbolicName=MSG_SERVER_FAILED_TO_WRITE_LOG
+Language=English
+Netscape Directory Server: %1.
+Failed to write log: %2.
+.
diff --git a/ldap/servers/slapd/ntperfdll/Makefile b/ldap/servers/slapd/ntperfdll/Makefile
new file mode 100644
index 00000000..bed046a6
--- /dev/null
+++ b/ldap/servers/slapd/ntperfdll/Makefile
@@ -0,0 +1,52 @@
+#
+# BEGIN COPYRIGHT BLOCK
+# Copyright 2001 Sun Microsystems, Inc.
+# Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+# All rights reserved.
+# END COPYRIGHT BLOCK
+#
+
+MCOM_ROOT = ../../../../..
+LDAP_SRC = ../../..
+
+OBJDEST = $(OBJDIR)/servers/obj
+BINDIR = $(LDAP_SERVER_RELDIR)
+ALLDIRS = $(BINDIR) $(OBJDEST)
+
+include $(MCOM_ROOT)/ldapserver/nsdefs.mk
+include $(MCOM_ROOT)/ldapserver/nsconfig.mk
+include $(LDAP_SRC)/nsldap.mk
+
+LINK32=link.exe
+
+MC=mc #message compiler?
+
+DLLS = nsldapctr
+
+# -D's get all screwed up: we need to fix them:
+CFLAGS+= -UNS_PERSONAL -DNS_DS
+# this lets us pickup regparms.h
+CFLAGS += -I $(MCOM_ROOT)/ldapserver/include
+
+MCS = nsldapctrmc
+DEPMC = $(addsuffix .h, $(MCS))
+
+OBJS = $(OBJDEST)/nsldapctr.o $(OBJDEST)/nsldapctrutil.o
+
+RELFILES = nsldapctrs.ini nsldapctrdef.h
+
+DESTRELFILES = $(addprefix $(LDAP_INSTALL_BIN_RELDIR)/, $(RELFILES))
+
+all: $(ALLDIRS) $(addprefix $(BINDIR)/, $(addsuffix .dll, $(DLLS) ) ) $(DESTRELFILES)
+
+nsldapctrmc.h:
+ $(MC) -s -v $*.mc
+
+$(OBJDEST)/%.o: %.cpp
+ cl -nologo -c $(CFLAGS) $(MCC_INCLUDE) /Tp $< -Fo$@
+
+$(BINDIR)/nsldapctr.dll: $(DEPMC) $(OBJS)
+ $(LINK_DLL) /OUT:$@ /DEF:exports.def
+
+$(LDAP_INSTALL_BIN_RELDIR)/%: % $(LDAP_INSTALL_BIN_RELDIR)
+ $(CP) $< $(dir $@)
diff --git a/ldap/servers/slapd/ntperfdll/exports.def b/ldap/servers/slapd/ntperfdll/exports.def
new file mode 100644
index 00000000..c5805d9d
--- /dev/null
+++ b/ldap/servers/slapd/ntperfdll/exports.def
@@ -0,0 +1,9 @@
+; BEGIN COPYRIGHT BLOCK
+; Copyright 2001 Sun Microsystems, Inc.
+; Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+; All rights reserved.
+; END COPYRIGHT BLOCK
+;
+EXPORTS OpenNSPerformanceData
+EXPORTS CollectNSPerformanceData
+EXPORTS CloseNSPerformanceData
diff --git a/ldap/servers/slapd/ntperfdll/nsldapctr.cpp b/ldap/servers/slapd/ntperfdll/nsldapctr.cpp
new file mode 100644
index 00000000..21bf429d
--- /dev/null
+++ b/ldap/servers/slapd/ntperfdll/nsldapctr.cpp
@@ -0,0 +1,985 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+/*
+ nsctr.c
+
+ Netscape server performance monitor hooks.
+
+
+ ***********************************************************************
+ HOW TO ADD A NEW PERFMON STATISTIC
+ 1. add to StatSlot or StatHeader struct
+ 2. add new counter definition to NS_DATA_DEFINITION in nsctrs.h
+ 3. define the offset of your new counter in nsctrdef.h
+ 4. add your counter initialization to NSDataDefinition in nsctr.cpp
+ 5. update CollectNSPerformanceData to collect your data
+ 6. modify nsctrs.ini to contain the text info for your counter
+ these are keyed off the "tag" you used in step 3
+ ***********************************************************************
+ HOW TO UPDATE THE REGISTRY
+ 1. run regini nsreg.ini
+ 2. run lodctr nsctrs.ini
+ ***********************************************************************
+ */
+
+#define UNICODE
+
+#include <windows.h>
+#include <string.h>
+#include <winperf.h>
+#include <stdio.h>
+#include <regstr.h>
+#include "nsldapctrs.h"
+#include "nsldapctrmsg.h"
+#include "nsldapctrutil.h"
+#include "nsldapctrmc.h"
+#include "nsldapctrdef.h"
+
+#include "nt/regparms.h"
+
+#include "../agtmmap.h"
+
+#define NUM_INSTANCES 0
+#define MAGT_MAX_LINELEN 255
+
+
+/* --- Constant Performance Counter Declaration --------------------------------------------*/
+
+NS_DATA_DEFINITION NSDataDefinition = {
+
+ { sizeof(NS_DATA_DEFINITION) + SIZE_OF_NS_PERFORMANCE_DATA,
+ sizeof(NS_DATA_DEFINITION),
+ sizeof(PERF_OBJECT_TYPE),
+ NS_OBJ,
+ 0,
+ NS_OBJ,
+ 0,
+ PERF_DETAIL_NOVICE,
+ (sizeof(NS_DATA_DEFINITION)-sizeof(PERF_OBJECT_TYPE))/
+ sizeof(PERF_COUNTER_DEFINITION),
+ 4L,
+ NUM_INSTANCES,
+ 0,
+ 0,
+ 0
+ },
+ {
+ sizeof(PERF_COUNTER_DEFINITION),
+ CONN_RATE,
+ 0,
+ CONN_RATE,
+ 0,
+ 0,
+ PERF_DETAIL_NOVICE,
+ PERF_COUNTER_COUNTER,
+ sizeof(DWORD),
+ NUM_CONN_RATE_OFFSET
+ },
+ {
+ sizeof(PERF_COUNTER_DEFINITION),
+ THROUGHPUT,
+ 0,
+ THROUGHPUT,
+ 0,
+ -3,
+ PERF_DETAIL_NOVICE,
+ PERF_COUNTER_COUNTER,
+ sizeof(DWORD),
+ NUM_THROUGHPUT_OFFSET
+ },
+ {
+ sizeof(PERF_COUNTER_DEFINITION),
+ TOTAL_BYTES_WRITTEN,
+ 0,
+ TOTAL_BYTES_WRITTEN,
+ 0,
+ -3,
+ PERF_DETAIL_NOVICE,
+ PERF_COUNTER_RAWCOUNT,
+ sizeof(DWORD),
+ NUM_TOTAL_BYTES_WRITTEN_OFFSET
+ },
+ {
+ sizeof(PERF_COUNTER_DEFINITION),
+ TOTAL_BYTES_READ,
+ 0,
+ TOTAL_BYTES_READ,
+ 0,
+ -3,
+ PERF_DETAIL_NOVICE,
+ PERF_COUNTER_RAWCOUNT,
+ sizeof(DWORD),
+ NUM_TOTAL_BYTES_READ_OFFSET
+ },
+ {
+ sizeof(PERF_COUNTER_DEFINITION),
+ OP_RATE,
+ 0,
+ OP_RATE,
+ 0,
+ -1,
+ PERF_DETAIL_NOVICE,
+ PERF_COUNTER_COUNTER,
+ sizeof(DWORD),
+ NUM_OP_RATE_OFFSET
+ },
+ {
+ sizeof(PERF_COUNTER_DEFINITION),
+ TOTAL_ERRORS,
+ 0,
+ TOTAL_ERRORS,
+ 0,
+ 0,
+ PERF_DETAIL_NOVICE,
+ PERF_COUNTER_RAWCOUNT,
+ sizeof(DWORD),
+ NUM_TOTAL_ERRORS_OFFSET
+ },
+ {
+ sizeof(PERF_COUNTER_DEFINITION),
+ SEARCH_RATE,
+ 0,
+ SEARCH_RATE,
+ 0,
+ -1,
+ PERF_DETAIL_NOVICE,
+ PERF_COUNTER_COUNTER,
+ sizeof(DWORD),
+ NUM_SEARCH_RATE_OFFSET
+ } ,
+ {
+ sizeof(PERF_COUNTER_DEFINITION),
+ ADD_RATE,
+ 0,
+ ADD_RATE,
+ 0,
+ 0,
+ PERF_DETAIL_NOVICE,
+ PERF_COUNTER_COUNTER,
+ sizeof(DWORD),
+ ADD_RATE_OFFSET
+ },
+ {
+ sizeof(PERF_COUNTER_DEFINITION),
+ DELETE_RATE,
+ 0,
+ DELETE_RATE,
+ 0,
+ 0,
+ PERF_DETAIL_NOVICE,
+ PERF_COUNTER_COUNTER,
+ sizeof(DWORD),
+ DELETE_RATE_OFFSET
+ },
+ {
+ sizeof(PERF_COUNTER_DEFINITION),
+ MODIFY_RATE,
+ 0,
+ MODIFY_RATE,
+ 0,
+ 0,
+ PERF_DETAIL_NOVICE,
+ PERF_COUNTER_COUNTER,
+ sizeof(DWORD),
+ MODIFY_RATE_OFFSET
+ },
+ {
+ sizeof(PERF_COUNTER_DEFINITION),
+ COMPARE_RATE,
+ 0,
+ COMPARE_RATE,
+ 0,
+ -1,
+ PERF_DETAIL_NOVICE,
+ PERF_COUNTER_COUNTER,
+ sizeof(DWORD),
+ COMPARE_RATE_OFFSET
+ },
+ {
+ sizeof(PERF_COUNTER_DEFINITION),
+ MODDN_RATE,
+ 0,
+ MODDN_RATE,
+ 0,
+ 0,
+ PERF_DETAIL_NOVICE,
+ PERF_COUNTER_COUNTER,
+ sizeof(DWORD),
+ MODDN_RATE_OFFSET
+ },
+ {
+ sizeof(PERF_COUNTER_DEFINITION),
+ CONNECTIONS,
+ 0,
+ CONNECTIONS,
+ 0,
+ 0,
+ PERF_DETAIL_NOVICE,
+ PERF_COUNTER_RAWCOUNT,
+ sizeof(DWORD),
+ CONNECTIONS_OFFSET
+ },
+ {
+ sizeof(PERF_COUNTER_DEFINITION),
+ BIND_RATE,
+ 0,
+ BIND_RATE,
+ 0,
+ -1,
+ PERF_DETAIL_NOVICE,
+ PERF_COUNTER_COUNTER,
+ sizeof(DWORD),
+ BIND_RATE_OFFSET
+ },
+ {
+ sizeof(PERF_COUNTER_DEFINITION),
+ ENTRIES_RETURNED,
+ 0,
+ ENTRIES_RETURNED,
+ 0,
+ 0,
+ PERF_DETAIL_NOVICE,
+ PERF_COUNTER_RAWCOUNT,
+ sizeof(DWORD),
+ ENTRIES_RETURNED_OFFSET
+ },
+ {
+ sizeof(PERF_COUNTER_DEFINITION),
+ ENTRIES_RETURNED_RATE,
+ 0,
+ ENTRIES_RETURNED_RATE,
+ 0,
+ -1,
+ PERF_DETAIL_NOVICE,
+ PERF_COUNTER_COUNTER,
+ sizeof(DWORD),
+ ENTRIES_RETURNED_RATE_OFFSET
+ },
+ {
+ sizeof(PERF_COUNTER_DEFINITION),
+ REFERRALS_RETURNED,
+ 0,
+ REFERRALS_RETURNED,
+ 0,
+ 0,
+ PERF_DETAIL_NOVICE,
+ PERF_COUNTER_RAWCOUNT,
+ sizeof(DWORD),
+ REFERRALS_RETURNED_OFFSET
+ },
+ {
+ sizeof(PERF_COUNTER_DEFINITION),
+ REFERRALS_RETURNED_RATE,
+ 0,
+ REFERRALS_RETURNED_RATE,
+ 0,
+ -1,
+ PERF_DETAIL_NOVICE,
+ PERF_COUNTER_COUNTER,
+ sizeof(DWORD),
+ REFERRALS_RETURNED_RATE_OFFSET
+ },
+ {
+ sizeof(PERF_COUNTER_DEFINITION),
+ BYTES_READ_RATE,
+ 0,
+ BYTES_READ_RATE,
+ 0,
+ -3,
+ PERF_DETAIL_NOVICE,
+ PERF_COUNTER_COUNTER,
+ sizeof(DWORD),
+ BYTES_READ_RATE_OFFSET
+ },
+ {
+ sizeof(PERF_COUNTER_DEFINITION),
+ BYTES_WRITTEN_RATE,
+ 0,
+ BYTES_WRITTEN_RATE,
+ 0,
+ -3,
+ PERF_DETAIL_NOVICE,
+ PERF_COUNTER_COUNTER,
+ sizeof(DWORD),
+ BYTES_WRITTEN_RATE_OFFSET
+ }
+
+
+};
+
+/* --- Data structs ----------------------------------------------------------------------- */
+typedef struct instance_list_t {
+ PERF_INSTANCE_DEFINITION instance;
+ PWSTR pInstanceName;
+ PWSTR pConfPath;
+ agt_stats_t * pData;
+ struct instance_list_t * pNext;
+} instance_list_t;
+
+
+/* --- Globals ---------------------------------------------------------------------------- */
+static BOOL bInitialized = FALSE;
+static DWORD dwOpenCount = 0; /* Count of threads holding DLL open */
+static DWORD dwInstanceCount = 0;
+static instance_list_t *pInstanceList = NULL;
+
+#define export extern "C"
+
+
+/*-------------------------------------------------------------------------
+ *
+ * MagtReadLine: Reads one line of text (up to n chars) from specified
+ * file.
+ *
+ * Returns: Len read - No error
+ * -1 - Errors
+ *
+ *-----------------------------------------------------------------------*/
+
+int MagtReadLine(char *buf, int n, FILE *fp)
+{
+ if (fgets(buf, n, fp) != NULL)
+ {
+ return(strlen(buf));
+ }
+ else
+ {
+ return(-1);
+ }
+}
+
+
+
+/* --- strips quotes off of a quoted string -------------------------------------- */
+
+
+char *dequote(char *quoted_string)
+{
+ char *return_string = (char *)malloc((strlen(quoted_string) - 2) * sizeof(char) );
+ char *pQuo = quoted_string;
+ char *pRet = return_string;
+
+ for(; *pQuo; pQuo++) {
+ if (*pQuo != '\"')
+ *(pRet++) = *pQuo;
+ }
+ *pRet = '\0';
+
+ return return_string;
+
+}
+
+/* --- gets the instance dir from conf file ------------------------------------- */
+
+
+/*
+ * The body of this function is pretty much copied from
+ * ldapserver/ldap/servers/snmp/ntagt/nsldapagt_nt.c
+ *
+ */
+char *getRootDirFromConfFile(PWSTR confpath)
+{
+ char *rootDir = NULL;
+ const char *config = "\\config\0" ;
+ char instanceDir[MAGT_MAX_LINELEN + 1] = "";
+ size_t len ;
+ char filename[256];
+
+ if (confpath) {
+ sprintf(filename, "%S", confpath);
+ len = strlen(filename) - strlen(config) ;
+ strncpy(instanceDir, filename, len);
+ rootDir = _strdup(instanceDir) ; // allocate memory for rootDir and set up to value pointed by instanceDir
+ return rootDir ;
+ }
+ else return NULL ;
+}
+
+static DWORD MapSharedMem(char* path, agt_stats_t **ptr)
+{
+ HANDLE hFile = NULL;
+ HANDLE hMapFile = NULL;
+ LPVOID memory = NULL;
+
+ *ptr = NULL;
+ /* Open existing disk file for read */
+ hFile = CreateFileA(path,
+ GENERIC_READ | GENERIC_WRITE,
+ FILE_SHARE_READ | FILE_SHARE_WRITE,
+ NULL,
+ OPEN_EXISTING,
+ FILE_ATTRIBUTE_NORMAL,
+ NULL);
+
+ if ( hFile == INVALID_HANDLE_VALUE || hFile == NULL ) return GetLastError();
+
+ /* Create mapped file handle for reading */
+ hMapFile = CreateFileMapping( hFile, NULL, PAGE_READONLY, 0,
+ sizeof(struct agt_stats_t),
+ NULL);
+ if ( hMapFile == NULL ) {
+ CloseHandle( hFile );
+ return GetLastError();
+ }
+
+ /* Create addr ptr to the start of the file */
+ memory = MapViewOfFileEx( hMapFile, FILE_MAP_READ, 0, 0,
+ sizeof(struct agt_stats_t), NULL );
+ CloseHandle( hMapFile );
+ CloseHandle( hFile );
+ if ( memory == NULL ) {
+ return GetLastError();
+ }
+ *ptr = (agt_stats_t *)memory;
+ return 0;
+}
+
+static DWORD UnmapSharedMem(agt_stats_t **ptr)
+{
+ return UnmapViewOfFile( (LPVOID)*ptr) ? 0 : -1;
+}
+
+/* --- Open Function --------------------------------------------------------------------- */
+
+
+/* _FindNetscapeServers()
+ * Function to loop through registry looking for netscape servers
+ * Stores them into pInstanceList as it finds them.
+ */
+
+#define MAX_KEY_SIZE 128
+DWORD
+_FindNetscapeServers()
+{
+ LONG regStatus,
+ status;
+ HKEY hKeyNetscape = NULL,
+ hKeyNetscapeConf;
+ DWORD dwKey,
+ type,
+ dwServerKeySize,
+ size,
+ dwServerCount = 0;
+ WCHAR szServerKeyName[MAX_KEY_SIZE],
+ szConfKeyName[MAX_KEY_SIZE + sizeof(KEY_SOFTWARE_NETSCAPE)],
+ szPath[MAX_KEY_SIZE];
+ FILETIME fileTime;
+ instance_list_t *pNew;
+ DWORD iUniqueID = 0;
+
+ regStatus = RegOpenKeyEx(
+ HKEY_LOCAL_MACHINE,
+ TEXT(KEY_SOFTWARE_NETSCAPE) TEXT("\\") TEXT(DS_KEY_ROOT),
+ 0L,
+ KEY_ALL_ACCESS,
+ &hKeyNetscape);
+
+ if (regStatus != ERROR_SUCCESS) {
+ goto ExitPoint;
+ }
+
+ dwKey = 0;
+ do {
+ dwServerKeySize = MAX_KEY_SIZE;
+ regStatus = RegEnumKeyEx(
+ hKeyNetscape,
+ dwKey,
+ szServerKeyName,
+ &dwServerKeySize,
+ NULL,
+ 0,
+ 0,
+ &fileTime);
+ dwKey++;
+
+ if (regStatus == ERROR_SUCCESS) {
+
+ regStatus = RegOpenKeyEx(
+ hKeyNetscape,
+ szServerKeyName,
+ 0L,
+ KEY_ALL_ACCESS,
+ &hKeyNetscapeConf);
+
+ if (regStatus != ERROR_SUCCESS) {
+ continue;
+ }
+
+ /* Now look for "ConfigurationPath" to find 3.0 netscape servers */
+ size = MAX_KEY_SIZE;
+ status = RegQueryValueEx(
+ hKeyNetscapeConf,
+ TEXT(VALUE_CONFIG_PATH),
+ 0L,
+ &type,
+ (LPBYTE)szPath,
+ &size);
+ if ( status == ERROR_SUCCESS ) {
+ /* this is a netscape server */
+ if ( (pNew = (instance_list_t *)malloc(sizeof(instance_list_t))) == NULL) {
+ status = (unsigned long)-1;
+ RegCloseKey(hKeyNetscapeConf);
+ goto ExitPoint;
+ }
+ if ( (pNew->pInstanceName = (PWCH)malloc(sizeof(WCHAR) *(dwServerKeySize+1))) == NULL) {
+ status = (unsigned long)-1;
+ RegCloseKey(hKeyNetscapeConf);
+ goto ExitPoint;
+ }
+
+ if ( (pNew->pConfPath = (PWCH)malloc(sizeof(WCHAR) *(size+1))) == NULL) {
+ status = (unsigned long)-1;
+ RegCloseKey(hKeyNetscapeConf);
+ goto ExitPoint;
+ }
+
+
+ pNew->pData = NULL;
+
+ pNew->instance.ParentObjectTitleIndex = 0;
+ pNew->instance.ParentObjectInstance = 0;
+ pNew->instance.UniqueID = -1;
+ pNew->instance.NameOffset = sizeof(PERF_INSTANCE_DEFINITION);
+ lstrcpy(pNew->pInstanceName, szServerKeyName);
+ lstrcpy(pNew->pConfPath, szPath);
+
+ pNew->instance.NameLength = (dwServerKeySize+1) * sizeof(WCHAR);
+ pNew->instance.ByteLength = sizeof(PERF_INSTANCE_DEFINITION) +
+ (((pNew->instance.NameLength + sizeof(DWORD)-1)/sizeof(DWORD))*sizeof(DWORD));
+ pNew->instance.UniqueID = iUniqueID++;
+
+ pNew->pNext = pInstanceList;
+ pInstanceList = pNew;
+
+ dwServerCount++;
+ }
+
+ RegCloseKey(hKeyNetscapeConf);
+ }
+
+ } while ( regStatus != ERROR_NO_MORE_ITEMS );
+
+ExitPoint:
+ if (hKeyNetscape)
+ RegCloseKey (hKeyNetscape);
+
+ return dwServerCount;
+}
+
+/* _OpenNetscapeServers()
+ * Once the pInstanceList has been created, this routine will open the instances
+ * of the netscape servers;
+ */
+#define MAX_FILE_LEN 128
+DWORD
+_OpenNetscapeServers()
+{
+ LONG status;
+ DWORD dwServerCount = 0;
+ instance_list_t *pInstance;
+ char *szRootDir;
+ char tmpstatsfile[MAX_FILE_LEN];
+ int err;
+
+ for (pInstance = pInstanceList; pInstance; pInstance = pInstance->pNext) {
+
+ /* open the memory map */
+
+ /*
+ * Get directory for our stats file
+ */
+
+ szRootDir = getRootDirFromConfFile(pInstance->pConfPath);
+ if( szRootDir == NULL){
+ status = GetLastError();
+ continue ;
+ }
+ wsprintfA(tmpstatsfile, "%s/logs/%s", szRootDir, AGT_STATS_FILE);
+ err = MapSharedMem(tmpstatsfile,&pInstance->pData);
+ if ( 0 != err ) {
+ REPORT_ERROR (NSPERF_UNABLE_MAP_VIEW_OF_FILE, LOG_USER);
+ status = GetLastError(); // return error
+ continue;
+ } else {
+ dwServerCount++;
+ }
+
+ if(szRootDir != NULL){
+ free(szRootDir);
+ }
+
+ }
+
+ return dwServerCount;
+}
+
+export DWORD APIENTRY
+OpenNSPerformanceData(LPWSTR lpDeviceNames)
+{
+ LONG status;
+ TCHAR szMappedObject[] = TEXT(SVR_ID_SERVICE) TEXT("Statistics");
+ HKEY hKeyDriverPerf;
+ DWORD size;
+ DWORD type;
+ DWORD dwFirstCounter;
+ DWORD dwFirstHelp;
+
+ if (!dwOpenCount) {
+
+ hEventLog = MonOpenEventLog();
+
+
+ if ( !_FindNetscapeServers() ) {
+ /* No netscape servers found */
+ status = (unsigned long)-1;
+ goto OpenExitPoint;
+ }
+
+ if ( !(dwInstanceCount = _OpenNetscapeServers()) ) {
+ /* No netscape servers are active */
+ status = (unsigned long)-1;
+ goto OpenExitPoint;
+ }
+
+ /* Now load help keys from registry */
+
+ status = RegOpenKeyEx (
+ HKEY_LOCAL_MACHINE,
+ TEXT("System\\CurrentControlSet\\Services") TEXT("\\") TEXT(SVR_ID_SERVICE) TEXT(SVR_VERSION) TEXT("\\") TEXT(KEY_PERFORMANCE),
+ 0L,
+ KEY_ALL_ACCESS,
+ &hKeyDriverPerf);
+
+ if (status != ERROR_SUCCESS) {
+ REPORT_ERROR_DATA (NSPERF_UNABLE_OPEN_DRIVER_KEY, LOG_USER,
+ &status, sizeof(status));
+ goto OpenExitPoint;
+ }
+
+ size = sizeof (DWORD);
+ status = RegQueryValueEx(
+ hKeyDriverPerf,
+ TEXT("First Counter"),
+ 0L,
+ &type,
+ (LPBYTE)&dwFirstCounter,
+ &size);
+
+ if (status != ERROR_SUCCESS) {
+ REPORT_ERROR_DATA (NSPERF_UNABLE_READ_FIRST_COUNTER, LOG_USER,
+ &status, sizeof(status));
+ goto OpenExitPoint;
+ }
+
+ size = sizeof (DWORD);
+ status = RegQueryValueEx(
+ hKeyDriverPerf,
+ TEXT("First Help"),
+ 0L,
+ &type,
+ (LPBYTE)&dwFirstHelp,
+ &size);
+
+ if (status != ERROR_SUCCESS) {
+ REPORT_ERROR_DATA (NSPERF_UNABLE_READ_FIRST_HELP, LOG_USER,
+ &status, sizeof(status));
+ goto OpenExitPoint;
+ }
+
+ NSDataDefinition.NS_ObjectType.ObjectNameTitleIndex += dwFirstCounter;
+ NSDataDefinition.NS_ObjectType.ObjectHelpTitleIndex += dwFirstHelp;
+
+ NSDataDefinition.connection_rate.CounterNameTitleIndex += dwFirstCounter;
+ NSDataDefinition.connection_rate.CounterHelpTitleIndex += dwFirstHelp;
+ NSDataDefinition.throughput.CounterNameTitleIndex += dwFirstCounter;
+ NSDataDefinition.throughput.CounterHelpTitleIndex += dwFirstHelp;
+ NSDataDefinition.total_bytes_written.CounterNameTitleIndex += dwFirstCounter;
+ NSDataDefinition.total_bytes_written.CounterHelpTitleIndex += dwFirstHelp;
+ NSDataDefinition.total_bytes_read.CounterNameTitleIndex += dwFirstCounter;
+ NSDataDefinition.total_bytes_read.CounterHelpTitleIndex += dwFirstHelp;
+ NSDataDefinition.operation_rate.CounterNameTitleIndex += dwFirstCounter;
+ NSDataDefinition.operation_rate.CounterHelpTitleIndex += dwFirstHelp;
+ NSDataDefinition.total_errors.CounterNameTitleIndex += dwFirstCounter;
+ NSDataDefinition.total_errors.CounterHelpTitleIndex += dwFirstHelp;
+ NSDataDefinition.search_rate.CounterNameTitleIndex += dwFirstCounter;
+ NSDataDefinition.search_rate.CounterHelpTitleIndex += dwFirstHelp;
+ NSDataDefinition.add_rate.CounterNameTitleIndex += dwFirstCounter;
+ NSDataDefinition.add_rate.CounterHelpTitleIndex += dwFirstHelp;
+ NSDataDefinition.delete_rate.CounterNameTitleIndex += dwFirstCounter;
+ NSDataDefinition.delete_rate.CounterHelpTitleIndex += dwFirstHelp;
+ NSDataDefinition.modify_rate.CounterNameTitleIndex += dwFirstCounter;
+ NSDataDefinition.modify_rate.CounterHelpTitleIndex += dwFirstHelp;
+ NSDataDefinition.compare_rate.CounterNameTitleIndex += dwFirstCounter;
+ NSDataDefinition.compare_rate.CounterHelpTitleIndex += dwFirstHelp;
+ NSDataDefinition.moddn_rate.CounterNameTitleIndex += dwFirstCounter;
+ NSDataDefinition.moddn_rate.CounterHelpTitleIndex += dwFirstHelp;
+ NSDataDefinition.connections.CounterNameTitleIndex += dwFirstCounter;
+ NSDataDefinition.connections.CounterHelpTitleIndex += dwFirstHelp;
+ NSDataDefinition.bind_rate.CounterNameTitleIndex += dwFirstCounter;
+ NSDataDefinition.bind_rate.CounterHelpTitleIndex += dwFirstHelp;
+ NSDataDefinition.entries_returned.CounterNameTitleIndex += dwFirstCounter;
+ NSDataDefinition.entries_returned.CounterHelpTitleIndex += dwFirstHelp;
+ NSDataDefinition.entries_returned_rate.CounterNameTitleIndex += dwFirstCounter;
+ NSDataDefinition.entries_returned_rate.CounterHelpTitleIndex += dwFirstHelp;
+ NSDataDefinition.referrals_returned.CounterNameTitleIndex += dwFirstCounter;
+ NSDataDefinition.referrals_returned.CounterHelpTitleIndex += dwFirstHelp;
+ NSDataDefinition.referrals_returned_rate.CounterNameTitleIndex += dwFirstCounter;
+ NSDataDefinition.referrals_returned_rate.CounterHelpTitleIndex += dwFirstHelp;
+ NSDataDefinition.bytes_read_rate.CounterNameTitleIndex += dwFirstCounter;
+ NSDataDefinition.bytes_read_rate.CounterHelpTitleIndex += dwFirstHelp;
+ NSDataDefinition.bytes_written_rate.CounterNameTitleIndex += dwFirstCounter;
+ NSDataDefinition.bytes_written_rate.CounterHelpTitleIndex += dwFirstHelp;
+
+ RegCloseKey (hKeyDriverPerf);
+
+ bInitialized = TRUE;
+ }
+
+ dwOpenCount++;
+
+ status = ERROR_SUCCESS;
+
+OpenExitPoint:
+
+ return status;
+}
+
+/* --- Close Function -------------------------------------------------------------------- */
+export DWORD APIENTRY
+CloseNSPerformanceData()
+{
+ instance_list_t *pInstance, *pDead;
+
+ if (!(--dwOpenCount)) {
+
+ for (pDead = NULL, pInstance = pInstanceList; pInstance; pInstance=pInstance->pNext) {
+ if (pDead)
+ free(pDead);
+
+ /* I probably need to free stats too... make sure to add that later */
+ if (pInstance->pData)
+ UnmapSharedMem(&pInstance->pData);
+
+ free(pInstance->pInstanceName);
+ free(pInstance->pConfPath);
+ pDead = pInstance;
+ }
+ if (pDead) /* cleanup last instance */
+ free(pDead);
+
+ MonCloseEventLog();
+
+ bInitialized = FALSE;
+ }
+
+ return ERROR_SUCCESS;
+}
+
+struct _status_struct_s {
+ DWORD connection_rate;
+ DWORD throughput;
+ DWORD tot_bytes_written;
+ DWORD tot_bytes_read;
+ DWORD op_rate;
+ DWORD tot_errs;
+ DWORD search_rate;
+ DWORD add_rate;
+ DWORD delete_rate;
+ DWORD modify_rate;
+ DWORD compare_rate;
+ DWORD moddn_rate;
+ DWORD connections;
+ DWORD bind_rate;
+ DWORD entries_returned;
+ DWORD entries_returned_rate;
+ DWORD referrals_returned;
+ DWORD referrals_returned_rate;
+ DWORD bytes_read_rate;
+ DWORD bytes_written_rate;
+};
+
+void
+Get_Actual_Data(agt_stats_t *smem,
+ struct _status_struct_s *results)
+{
+ /* Copy over the counters from the shared memory region */
+ struct ops_stats_t *pOpsStats = &(smem->ops_stats);
+
+ results->search_rate = pOpsStats->dsSearchOps;
+ results->modify_rate = pOpsStats->dsModifyEntryOps;
+ results->add_rate = pOpsStats->dsAddEntryOps ;
+ results->compare_rate = pOpsStats->dsCompareOps ;
+ results->moddn_rate = pOpsStats->dsModifyRDNOps ;
+ results->delete_rate = pOpsStats->dsRemoveEntryOps ;
+ results->bind_rate = pOpsStats->dsAnonymousBinds + pOpsStats->dsStrongAuthBinds + pOpsStats->dsSimpleAuthBinds ;
+ results->op_rate = results->search_rate + results->add_rate + results->delete_rate +
+ results->modify_rate + results->compare_rate + results->moddn_rate + results->bind_rate;
+ results->connections = 0;
+ results->tot_errs = pOpsStats->dsErrors ;
+ results->connections = pOpsStats->dsConnections ;
+ results->tot_bytes_written = pOpsStats->dsBytesSent ;
+ results->tot_bytes_read = pOpsStats->dsBytesRecv ;
+ results->throughput = pOpsStats->dsBytesSent + pOpsStats->dsBytesRecv;
+ results->connection_rate = pOpsStats->dsConnectionSeq ;
+ results->entries_returned = pOpsStats->dsEntriesReturned ;
+ results->entries_returned_rate = pOpsStats->dsEntriesReturned ;
+ results->referrals_returned = pOpsStats->dsReferralsReturned ;
+ results->referrals_returned_rate = pOpsStats->dsReferralsReturned ;
+ results->bytes_read_rate = pOpsStats->dsBytesRecv ;
+ results->bytes_written_rate = pOpsStats->dsBytesSent ;
+ /* Still to do : connections, throughput, db hit ratio, entry cache hit ratio */
+}
+
+/* --- Collect Function ------------------------------------------------------------------- */
+export DWORD APIENTRY
+CollectNSPerformanceData(
+ IN LPWSTR lpValueName,
+ IN OUT LPVOID *lppData,
+ IN OUT LPDWORD lpcbTotalBytes,
+ IN OUT LPDWORD lpNumObjectTypes
+)
+{
+ ULONG SpaceNeeded;
+ PDWORD pdwCounter;
+ PERF_COUNTER_BLOCK *pPerfCounterBlock;
+ NS_DATA_DEFINITION *pNSDataDefinition;
+ DWORD dwQueryType;
+ instance_list_t *pInstance;
+
+ if (!bInitialized) {
+ *lpcbTotalBytes = (DWORD) 0;
+ *lpNumObjectTypes = (DWORD) 0;
+ return ERROR_SUCCESS;
+ }
+
+ dwQueryType = GetQueryType (lpValueName);
+
+ if (dwQueryType == QUERY_FOREIGN) {
+ // this routine does not service requests for data from
+ // Non-NT computers
+ *lpcbTotalBytes = (DWORD) 0;
+ *lpNumObjectTypes = (DWORD) 0;
+ return ERROR_SUCCESS;
+ }
+
+ if (dwQueryType == QUERY_ITEMS){
+ if ( !(IsNumberInUnicodeList (NSDataDefinition.NS_ObjectType.ObjectNameTitleIndex, lpValueName))) {
+ // request received for data object not provided by this routine
+ *lpcbTotalBytes = (DWORD) 0;
+ *lpNumObjectTypes = (DWORD) 0;
+ return ERROR_SUCCESS;
+ }
+ }
+ /* -------- OK DO THE REAL WORK HERE ---------- */
+
+
+ /* -------------------------------------------- */
+ /* | PERF_DATA_BLOCK (header) | */
+ /* -------------------------------------------- */
+ /* | PERF_OBJECT_TYPE 1 | */
+ /* -------------------------------------------- */
+ /* | PERF_OBJECT_TYPE 2 | */
+ /* -------------------------------------------- */
+ /* | . | */
+ /* | . | */
+ /* | . | */
+ /* | | */
+ /* | | */
+ /* -------------------------------------------- */
+
+
+ /* -------------------------------------------- */
+ /* | PERF_OBJECT_TYPE (header) | */
+ /* -------------------------------------------- */
+ /* | PERF_COUNTER_DEFINITION 1 | */
+ /* -------------------------------------------- */
+ /* | PERF_COUNTER_DEFINITION 2 | */
+ /* -------------------------------------------- */
+ /* | . | */
+ /* | . | */
+ /* | . | */
+ /* | | */
+ /* -------------------------------------------- */
+ /* | PERF_INSTANCE_DEFINITION 1 | */
+ /* -------------------------------------------- */
+ /* | PERF_INSTANCE_DEFINITION 2 | */
+ /* -------------------------------------------- */
+ /* | . | */
+ /* | . | */
+ /* | . | */
+ /* | | */
+ /* | | */
+ /* -------------------------------------------- */
+
+
+ /* -------------------------------------------- */
+ /* | PERF_INSTANCE_DEFINITION (header) | */
+ /* -------------------------------------------- */
+ /* | Instance Name (variable) | */
+ /* -------------------------------------------- */
+ /* | PERF_COUNTER_BLOCK (header) | */
+ /* -------------------------------------------- */
+ /* | Counter Data (variable) | */
+ /* -------------------------------------------- */
+
+
+
+ /* Check to see if there is enough space in caller's buffer */
+
+ pNSDataDefinition = (NS_DATA_DEFINITION *) *lppData;
+
+ SpaceNeeded = sizeof(NS_DATA_DEFINITION) + (dwInstanceCount *
+ (SIZE_OF_NS_PERFORMANCE_DATA + MAX_KEY_SIZE + sizeof(PERF_COUNTER_BLOCK) +
+ sizeof(PERF_INSTANCE_DEFINITION)));
+
+ if ( *lpcbTotalBytes < SpaceNeeded ) {
+ *lpcbTotalBytes = (DWORD) 0;
+ *lpNumObjectTypes = (DWORD) 0;
+ return ERROR_MORE_DATA;
+ }
+
+ /* Set the PERF_OBJECT_TYPE definition and PERF_COUNTER_DEFINITIONs */
+ NSDataDefinition.NS_ObjectType.NumInstances = dwInstanceCount;
+ memmove(pNSDataDefinition, &NSDataDefinition, sizeof(NS_DATA_DEFINITION));
+
+ pdwCounter = (PDWORD) &(pNSDataDefinition[1]);
+
+ for ( pInstance = pInstanceList; pInstance; pInstance=pInstance->pNext) {
+
+ if ( pInstance->pData ) {
+
+ /* Set the PERF_INSTANCE_DEFINITION */
+ memmove(pdwCounter, &(pInstance->instance), sizeof(PERF_INSTANCE_DEFINITION));
+ pdwCounter += ((sizeof(PERF_INSTANCE_DEFINITION))/sizeof(DWORD));
+
+ /* Set the Instance Name */
+ memmove(pdwCounter, pInstance->pInstanceName, pInstance->instance.NameLength);
+ pdwCounter = pdwCounter + ((pInstance->instance.NameLength + sizeof(DWORD)-1)/sizeof(DWORD));
+
+ /* Set the PERF_COUNTER_BLOCK */
+ pPerfCounterBlock = (PERF_COUNTER_BLOCK *) pdwCounter;
+ pPerfCounterBlock->ByteLength = SIZE_OF_NS_PERFORMANCE_DATA + sizeof(PERF_COUNTER_BLOCK);
+ pdwCounter = (PDWORD) (&pPerfCounterBlock[1]);
+
+ /* Set the Instance Data */
+ Get_Actual_Data(pInstance->pData,(struct _status_struct_s*)pdwCounter);
+
+ {
+ DWORD x = (SIZE_OF_NS_PERFORMANCE_DATA) / sizeof(DWORD);
+
+ pdwCounter += x;
+ }
+ }
+ }
+
+ *lppData = (PVOID)(pdwCounter);
+ *lpNumObjectTypes = 1;
+ *lpcbTotalBytes = (PBYTE) pdwCounter - (PBYTE) pNSDataDefinition;
+ pNSDataDefinition->NS_ObjectType.TotalByteLength = *lpcbTotalBytes;
+
+ return ERROR_SUCCESS;
+}
+
diff --git a/ldap/servers/slapd/ntperfdll/nsldapctrdef.h b/ldap/servers/slapd/ntperfdll/nsldapctrdef.h
new file mode 100644
index 00000000..77b5d279
--- /dev/null
+++ b/ldap/servers/slapd/ntperfdll/nsldapctrdef.h
@@ -0,0 +1,33 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+/*
+ * nsctrdef.h
+ *
+ * Defines offsets of netscape server performance monitor counters.
+ */
+
+#define NS_OBJ 0
+#define CONN_RATE 2
+#define THROUGHPUT 4
+#define TOTAL_BYTES_WRITTEN 6
+#define TOTAL_BYTES_READ 8
+#define OP_RATE 10
+#define TOTAL_ERRORS 12
+#define SEARCH_RATE 14
+#define ADD_RATE 16
+#define DELETE_RATE 18
+#define MODIFY_RATE 20
+#define COMPARE_RATE 22
+#define MODDN_RATE 24
+#define CONNECTIONS 26
+#define BIND_RATE 28
+#define ENTRIES_RETURNED 30
+#define ENTRIES_RETURNED_RATE 32
+#define REFERRALS_RETURNED 34
+#define REFERRALS_RETURNED_RATE 36
+#define BYTES_READ_RATE 38
+#define BYTES_WRITTEN_RATE 40
+
diff --git a/ldap/servers/slapd/ntperfdll/nsldapctrmc.h b/ldap/servers/slapd/ntperfdll/nsldapctrmc.h
new file mode 100644
index 00000000..2178d6be
--- /dev/null
+++ b/ldap/servers/slapd/ntperfdll/nsldapctrmc.h
@@ -0,0 +1,122 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+/*
+ * nsctrs.h
+ */
+//
+#ifndef _NSCTRMC_H_
+#define _NSCTRMC_H_
+//
+//
+// Perfutil messages
+//
+//
+// Values are 32 bit values layed 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: UTIL_LOG_OPEN
+//
+// MessageText:
+//
+// An extensible counter has opened the Event Log for NSCTRS.DLL
+//
+#define UTIL_LOG_OPEN ((DWORD)0x4000076CL)
+
+//
+//
+// MessageId: UTIL_CLOSING_LOG
+//
+// MessageText:
+//
+// An extensible counter has closed the Event Log for NSCTRS.DLL
+//
+#define UTIL_CLOSING_LOG ((DWORD)0x400007CFL)
+
+//
+//
+// MessageId: NSPERF_OPEN_FILE_MAPPING_ERROR
+//
+// MessageText:
+//
+// Unable to open mapped file containing NS driver performance data.
+//
+#define NSPERF_OPEN_FILE_MAPPING_ERROR ((DWORD)0xC00007D0L)
+
+//
+//
+// MessageId: NSPERF_UNABLE_MAP_VIEW_OF_FILE
+//
+// MessageText:
+//
+// Unable to map to shared memory file containing NS driver performance data.
+//
+#define NSPERF_UNABLE_MAP_VIEW_OF_FILE ((DWORD)0xC00007D1L)
+
+//
+//
+// MessageId: NSPERF_UNABLE_OPEN_DRIVER_KEY
+//
+// MessageText:
+//
+// Unable open "Performance" key of NS driver in registry. Status code is returned in data.
+//
+#define NSPERF_UNABLE_OPEN_DRIVER_KEY ((DWORD)0xC00007D2L)
+
+//
+//
+// MessageId: NSPERF_UNABLE_READ_FIRST_COUNTER
+//
+// MessageText:
+//
+// Unable to read the "First Counter" value under the NS\Performance Key. Status codes returned in data.
+//
+#define NSPERF_UNABLE_READ_FIRST_COUNTER ((DWORD)0xC00007D3L)
+
+//
+//
+// MessageId: NSPERF_UNABLE_READ_FIRST_HELP
+//
+// MessageText:
+//
+// Unable to read the "First Help" value under the NS\Performance Key. Status codes returned in data.
+//
+#define NSPERF_UNABLE_READ_FIRST_HELP ((DWORD)0xC00007D4L)
+
+//
+#endif // _NSCTRMC_H_
diff --git a/ldap/servers/slapd/ntperfdll/nsldapctrmc.mc b/ldap/servers/slapd/ntperfdll/nsldapctrmc.mc
new file mode 100644
index 00000000..10c8341f
--- /dev/null
+++ b/ldap/servers/slapd/ntperfdll/nsldapctrmc.mc
@@ -0,0 +1,74 @@
+; BEGIN COPYRIGHT BLOCK
+; Copyright 2001 Sun Microsystems, Inc.
+; Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+; All rights reserved.
+; END COPYRIGHT BLOCK
+;
+;/*
+; * nsctrs.h
+; */
+;//
+;#ifndef _NSCTRMC_H_
+;#define _NSCTRMC_H_
+;//
+MessageIdTypedef=DWORD
+;//
+;// Perfutil messages
+;//
+MessageId=1900
+Severity=Informational
+Facility=Application
+SymbolicName=UTIL_LOG_OPEN
+Language=English
+An extensible counter has opened the Event Log for NSCTRS.DLL
+.
+;//
+MessageId=1999
+Severity=Informational
+Facility=Application
+SymbolicName=UTIL_CLOSING_LOG
+Language=English
+An extensible counter has closed the Event Log for NSCTRS.DLL
+.
+;//
+MessageId=2000
+Severity=Error
+Facility=Application
+SymbolicName=NSPERF_OPEN_FILE_MAPPING_ERROR
+Language=English
+Unable to open mapped file containing NS driver performance data.
+.
+;//
+MessageId=+1
+Severity=Error
+Facility=Application
+SymbolicName=NSPERF_UNABLE_MAP_VIEW_OF_FILE
+Language=English
+Unable to map to shared memory file containing NS driver performance data.
+.
+;//
+MessageId=+1
+Severity=Error
+Facility=Application
+SymbolicName=NSPERF_UNABLE_OPEN_DRIVER_KEY
+Language=English
+Unable open "Performance" key of NS driver in registry. Status code is returned in data.
+.
+;//
+MessageId=+1
+Severity=Error
+Facility=Application
+SymbolicName=NSPERF_UNABLE_READ_FIRST_COUNTER
+Language=English
+Unable to read the "First Counter" value under the NS\Performance Key. Status codes returned in data.
+.
+;//
+MessageId=+1
+Severity=Error
+Facility=Application
+SymbolicName=NSPERF_UNABLE_READ_FIRST_HELP
+Language=English
+Unable to read the "First Help" value under the NS\Performance Key. Status codes returned in data.
+.
+;//
+;#endif // _NSCTRMC_H_
diff --git a/ldap/servers/slapd/ntperfdll/nsldapctrmsg.h b/ldap/servers/slapd/ntperfdll/nsldapctrmsg.h
new file mode 100644
index 00000000..684ed821
--- /dev/null
+++ b/ldap/servers/slapd/ntperfdll/nsldapctrmsg.h
@@ -0,0 +1,60 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+/*
+ * nsctrmsg.h
+ *
+ * Defines EventLog error handling stuff for performance monitor dll.
+ *
+ */
+
+
+#ifndef _NSCTRMSG_H_
+#define _NSCTRMSG_H_
+//
+// Report error message ID's for Counters
+//
+
+#define APP_NAME "nsctrs"
+
+/* Levels: LOG_NONE = No event log messages ever
+ * LOG_USER = User event log messages (e.g. errors)
+ * LOG_DEBUG = Minimum Debugging
+ * LOG_VERBOSE = Maximum Debugging
+ */
+
+#define LOG_NONE 0
+#define LOG_USER 1
+#define LOG_DEBUG 2
+#define LOG_VERBOSE 3
+
+#define MESSAGE_LEVEL_DEFAULT LOG_USER
+
+#define REPORT_SUCCESS(i,l) (MESSAGE_LEVEL >= l ? ReportEvent (hEventLog, EVENTLOG_INFORMATION_TYPE, \
+ 0, i, (PSID)NULL, 0, 0, NULL, (PVOID)NULL) : FALSE)
+
+#define REPORT_INFORMATION(i,l) (MESSAGE_LEVEL >= l ? ReportEvent (hEventLog, EVENTLOG_INFORMATION_TYPE, \
+ 0, i, (PSID)NULL, 0, 0, NULL, (PVOID)NULL) : FALSE)
+
+#define REPORT_WARNING(i,l) (MESSAGE_LEVEL >= l ? ReportEvent (hEventLog, EVENTLOG_WARNING_TYPE, \
+ 0, i, (PSID)NULL, 0, 0, NULL, (PVOID)NULL) : FALSE)
+
+#define REPORT_ERROR(i,l) (MESSAGE_LEVEL >= l ? ReportEvent (hEventLog, EVENTLOG_ERROR_TYPE, \
+ 0, i, (PSID)NULL, 0, 0, NULL, (PVOID)NULL) : FALSE)
+
+#define REPORT_INFORMATION_DATA(i,l,d,s) (MESSAGE_LEVEL >= l ? ReportEvent (hEventLog, EVENTLOG_INFORMATION_TYPE, \
+ 0, i, (PSID)NULL, 0, s, NULL, (PVOID)(d)) : FALSE)
+
+#define REPORT_WARNING_DATA(i,l,d,s) (MESSAGE_LEVEL >= l ? ReportEvent (hEventLog, EVENTLOG_WARNING_TYPE, \
+ 0, i, (PSID)NULL, 0, s, NULL, (PVOID)(d)) : FALSE)
+
+#define REPORT_ERROR_DATA(i,l,d,s) (MESSAGE_LEVEL >= l ? ReportEvent (hEventLog, EVENTLOG_ERROR_TYPE, \
+ 0, i, (PSID)NULL, 0, s, NULL, (PVOID)(d)) : FALSE)
+
+extern HANDLE hEventLog; /* handle to event log */
+extern DWORD dwLogUsers; /* counter of event log using routines */
+extern DWORD MESSAGE_LEVEL; /* event logging detail level */
+
+#endif /* _NSCTRMSG_H_ */
diff --git a/ldap/servers/slapd/ntperfdll/nsldapctrs.h b/ldap/servers/slapd/ntperfdll/nsldapctrs.h
new file mode 100644
index 00000000..3bd4c70a
--- /dev/null
+++ b/ldap/servers/slapd/ntperfdll/nsldapctrs.h
@@ -0,0 +1,67 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+/*
+
+ nsctrs.h
+
+ */
+
+#ifndef _NSCTRS_H_
+#define _NSCTRS_H_
+
+#pragma pack (4)
+
+#define NS_NUM_PERF_OBJECT_TYPES 1
+#define NUM_CONN_RATE_OFFSET sizeof(DWORD)
+#define NUM_THROUGHPUT_OFFSET NUM_CONN_RATE_OFFSET + sizeof(DWORD)
+#define NUM_TOTAL_BYTES_WRITTEN_OFFSET NUM_THROUGHPUT_OFFSET + sizeof(DWORD)
+#define NUM_TOTAL_BYTES_READ_OFFSET NUM_TOTAL_BYTES_WRITTEN_OFFSET + sizeof(DWORD)
+#define NUM_OP_RATE_OFFSET NUM_TOTAL_BYTES_READ_OFFSET + sizeof(DWORD)
+#define NUM_TOTAL_ERRORS_OFFSET NUM_OP_RATE_OFFSET + sizeof(DWORD)
+#define NUM_SEARCH_RATE_OFFSET NUM_TOTAL_ERRORS_OFFSET + sizeof(DWORD)
+#define ADD_RATE_OFFSET NUM_SEARCH_RATE_OFFSET + sizeof(DWORD)
+#define DELETE_RATE_OFFSET ADD_RATE_OFFSET + sizeof(DWORD)
+#define MODIFY_RATE_OFFSET DELETE_RATE_OFFSET + sizeof(DWORD)
+#define COMPARE_RATE_OFFSET MODIFY_RATE_OFFSET + sizeof(DWORD)
+#define MODDN_RATE_OFFSET COMPARE_RATE_OFFSET + sizeof(DWORD)
+#define CONNECTIONS_OFFSET MODDN_RATE_OFFSET + sizeof(DWORD)
+#define BIND_RATE_OFFSET CONNECTIONS_OFFSET + sizeof(DWORD)
+#define ENTRIES_RETURNED_OFFSET BIND_RATE_OFFSET + sizeof(DWORD)
+#define ENTRIES_RETURNED_RATE_OFFSET ENTRIES_RETURNED_OFFSET + sizeof(DWORD)
+#define REFERRALS_RETURNED_OFFSET ENTRIES_RETURNED_RATE_OFFSET + sizeof(DWORD)
+#define REFERRALS_RETURNED_RATE_OFFSET REFERRALS_RETURNED_OFFSET + sizeof(DWORD)
+#define BYTES_READ_RATE_OFFSET REFERRALS_RETURNED_RATE_OFFSET + sizeof(DWORD)
+#define BYTES_WRITTEN_RATE_OFFSET BYTES_READ_RATE_OFFSET + sizeof(DWORD)
+#define SIZE_OF_NS_PERFORMANCE_DATA BYTES_WRITTEN_RATE_OFFSET + sizeof(DWORD)
+
+typedef struct _NS_DATA_DEFINITION {
+ PERF_OBJECT_TYPE NS_ObjectType;
+ PERF_COUNTER_DEFINITION connection_rate;
+ PERF_COUNTER_DEFINITION throughput;
+ PERF_COUNTER_DEFINITION total_bytes_written;
+ PERF_COUNTER_DEFINITION total_bytes_read;
+ PERF_COUNTER_DEFINITION operation_rate;
+ PERF_COUNTER_DEFINITION total_errors;
+ PERF_COUNTER_DEFINITION search_rate;
+ PERF_COUNTER_DEFINITION add_rate;
+ PERF_COUNTER_DEFINITION delete_rate;
+ PERF_COUNTER_DEFINITION modify_rate;
+ PERF_COUNTER_DEFINITION compare_rate;
+ PERF_COUNTER_DEFINITION moddn_rate;
+ PERF_COUNTER_DEFINITION connections;
+ PERF_COUNTER_DEFINITION bind_rate;
+ PERF_COUNTER_DEFINITION entries_returned;
+ PERF_COUNTER_DEFINITION entries_returned_rate;
+ PERF_COUNTER_DEFINITION referrals_returned;
+ PERF_COUNTER_DEFINITION referrals_returned_rate;
+ PERF_COUNTER_DEFINITION bytes_read_rate;
+ PERF_COUNTER_DEFINITION bytes_written_rate;
+} NS_DATA_DEFINITION;
+
+#pragma pack ()
+
+#endif /* _NSCTRS_H_ */
+
diff --git a/ldap/servers/slapd/ntperfdll/nsldapctrs.ini b/ldap/servers/slapd/ntperfdll/nsldapctrs.ini
new file mode 100644
index 00000000..82db3140
--- /dev/null
+++ b/ldap/servers/slapd/ntperfdll/nsldapctrs.ini
@@ -0,0 +1,57 @@
+#
+# BEGIN COPYRIGHT BLOCK
+# Copyright 2001 Sun Microsystems, Inc.
+# Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+# All rights reserved.
+# END COPYRIGHT BLOCK
+#
+[info]
+drivername=slapd7
+symbolfile=nsldapctrdef.h
+
+[languages]
+009=English
+
+[text]
+NS_OBJ_009_NAME=Netscape Directory Server 7.0
+NS_OBJ_009_HELP=Netscape Directory Server
+CONN_RATE_009_NAME=Client Connections/sec
+CONN_RATE_009_HELP=Rate of incoming client connections
+THROUGHPUT_009_NAME=Server Network Throughput (bytes/sec)
+THROUGHPUT_009_HELP=Number of bytes both sent and received per second on client connections
+TOTAL_BYTES_WRITTEN_009_NAME=Total Bytes Sent
+TOTAL_BYTES_WRITTEN_009_HELP=Total number of Bytes sent by the server since startup
+TOTAL_BYTES_READ_009_NAME=Total Bytes Received
+TOTAL_BYTES_READ_009_HELP=Total number of Bytes received by the server since startup
+OP_RATE_009_NAME=Operations/sec
+OP_RATE_009_HELP=Number of Operations (total of search, bind, modify, compare, modDN, delete) serviced per second
+SEARCH_RATE_009_NAME=Searches/sec
+SEARCH_RATE_009_HELP=Number of Search operations performed per second
+TOTAL_ERRORS_009_NAME=Total Number of Errors
+TOTAL_ERRORS_009_HELP=Total number of Errors seen by the server since startup
+ADD_RATE_009_NAME=Adds/sec
+ADD_RATE_009_HELP=Number of Add operations performed per second
+DELETE_RATE_009_NAME=Deletes/sec
+DELETE_RATE_009_HELP=Number of Delete operations performed per second
+MODIFY_RATE_009_NAME=Modifies/sec
+MODIFY_RATE_009_HELP=Number of Modify operations performed per second
+COMPARE_RATE_009_NAME=Compares/sec
+COMPARE_RATE_009_HELP=Number of Compare operations performed per second
+MODDN_RATE_009_NAME=ModDNs/sec
+MODDN_RATE_009_HELP=Number of ModDN operations performed per second
+CONNECTIONS_009_NAME=Connected Clients
+CONNECTIONS_009_HELP=Number of client sessions currently connected
+BIND_RATE_009_NAME=Binds/sec
+BIND_RATE_009_HELP=Number of Bind operations performed per second
+ENTRIES_RETURNED_009_NAME=Entries Returned
+ENTRIES_RETURNED_009_HELP=Number of entries returned to clients since startup
+ENTRIES_RETURNED_RATE_009_NAME=Entries Returned/sec
+ENTRIES_RETURNED_RATE_009_HELP=Number of entries returned to clients per second
+REFERRALS_RETURNED_009_NAME=Referrals Returned
+REFERRALS_RETURNED_009_HELP=Number of referrals returned to clients since startup
+REFERRALS_RETURNED_RATE_009_NAME=Referrals Returned/sec
+REFERRALS_RETURNED_RATE_009_HELP=Number of referrals returned to clients per second
+BYTES_READ_RATE_009_NAME=Network Bytes Read/sec
+BYTES_READ_RATE_009_HELP=Number of bytes per second read from connected clients
+BYTES_WRITTEN_RATE_009_NAME=Network Bytes Written/sec
+BYTES_WRITTEN_RATE_009_HELP=Number of bytes per second written to connected clients
diff --git a/ldap/servers/slapd/ntperfdll/nsldapctrutil.cpp b/ldap/servers/slapd/ntperfdll/nsldapctrutil.cpp
new file mode 100644
index 00000000..1783aee2
--- /dev/null
+++ b/ldap/servers/slapd/ntperfdll/nsldapctrutil.cpp
@@ -0,0 +1,364 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+/*
+
+ nsctrutil.c
+
+ Performance Monitor utility functions
+
+ This file implements the utility routines used to construct the
+ common parts of a PERF_INSTANCE_DEFINITION (see winperf.h) and
+ perform event logging functions.
+
+ */
+
+#include <windows.h>
+#include <string.h>
+#include <winperf.h>
+#include "nsldapctrmc.h"
+#include "nsldapctrmsg.h"
+#include "nsldapctrutil.h"
+
+#define INITIAL_SIZE 1024L
+#define EXTEND_SIZE 1024L
+
+//
+// Global data definitions.
+//
+
+ULONG ulInfoBufferSize = 0;
+
+HANDLE hEventLog; // handle to event log
+
+DWORD dwLogUsers = 0; // count of functions using event log
+
+DWORD MESSAGE_LEVEL = 0;
+
+WCHAR GLOBAL_STRING[] = L"Global";
+WCHAR FOREIGN_STRING[] = L"Foreign";
+WCHAR COSTLY_STRING[] = L"Costly";
+
+WCHAR NULL_STRING[] = L"\0"; // pointer to null string
+
+// test for delimiter, end of line and non-digit characters
+// used by IsNumberInUnicodeList routine
+//
+#define DIGIT 1
+#define DELIMITER 2
+#define INVALID 3
+
+#define EvalThisChar(c,d) ( \
+ (c == d) ? DELIMITER : \
+ (c == 0) ? DELIMITER : \
+ (c < (WCHAR)'0') ? INVALID : \
+ (c > (WCHAR)'9') ? INVALID : \
+ DIGIT)
+
+HANDLE
+MonOpenEventLog (
+)
+/*++
+
+Routine Description:
+
+ Reads the level of event logging from the registry and opens the
+ channel to the event logger for subsequent event log entries.
+
+Arguments:
+
+ None
+
+Return Value:
+
+ Handle to the event log for reporting events.
+ NULL if open not successful.
+
+--*/
+
+
+{
+
+
+ HKEY hAppKey;
+
+
+ TCHAR LogLevelKeyName[] = "SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Perflib";
+
+
+ TCHAR LogLevelValueName[] = "EventLogLevel";
+
+ LONG lStatus;
+
+ DWORD dwLogLevel;
+ DWORD dwValueType;
+ DWORD dwValueSize;
+
+ // if global value of the logging level not initialized or is disabled,
+ // check the registry to see if it should be updated.
+
+ if (!MESSAGE_LEVEL) {
+
+ lStatus = RegOpenKeyEx (HKEY_LOCAL_MACHINE,
+ LogLevelKeyName,
+ 0,
+ KEY_READ,
+ &hAppKey);
+
+ dwValueSize = sizeof (dwLogLevel);
+
+ if (lStatus == ERROR_SUCCESS) {
+ lStatus = RegQueryValueEx (hAppKey,
+ LogLevelValueName,
+ (LPDWORD)NULL,
+ &dwValueType,
+ (LPBYTE)&dwLogLevel,
+ &dwValueSize);
+
+ if (lStatus == ERROR_SUCCESS) {
+ MESSAGE_LEVEL = dwLogLevel;
+ } else {
+ MESSAGE_LEVEL = MESSAGE_LEVEL_DEFAULT;
+ }
+ RegCloseKey (hAppKey);
+ } else {
+
+
+ MESSAGE_LEVEL = MESSAGE_LEVEL_DEFAULT;
+ }
+ }
+
+ if (hEventLog == NULL){
+ hEventLog = RegisterEventSource (
+ (LPTSTR)NULL, // Use Local Machine
+ APP_NAME); // event log app name to find in registry
+ }
+
+ if (hEventLog != NULL) {
+ dwLogUsers++; // increment count of perfctr log users
+ }
+ return (hEventLog);
+}
+
+VOID
+MonCloseEventLog (
+)
+/*++
+
+Routine Description:
+
+ Closes the handle to the event logger if this is the last caller
+
+Arguments:
+
+ None
+
+Return Value:
+
+ None
+
+--*/
+{
+ if (hEventLog != NULL) {
+ dwLogUsers--; // decrement usage
+ if (dwLogUsers <= 0) { // and if we're the last, then close up log
+ DeregisterEventSource (hEventLog);
+ }
+ }
+}
+
+DWORD
+GetQueryType (
+ IN LPWSTR lpValue
+)
+/*++
+
+GetQueryType
+
+ returns the type of query described in the lpValue string so that
+ the appropriate processing method may be used
+
+Arguments
+
+ IN lpValue
+ string passed to PerfRegQuery Value for processing
+
+Return Value
+
+ QUERY_GLOBAL
+ if lpValue == 0 (null pointer)
+ lpValue == pointer to Null string
+ lpValue == pointer to "Global" string
+
+ QUERY_FOREIGN
+ if lpValue == pointer to "Foreign" string
+
+ QUERY_COSTLY
+ if lpValue == pointer to "Costly" string
+
+ otherwise:
+
+ QUERY_ITEMS
+
+--*/
+{
+ WCHAR *pwcArgChar, *pwcTypeChar;
+ BOOL bFound;
+
+ if (lpValue == 0) {
+ return QUERY_GLOBAL;
+ } else if (*lpValue == 0) {
+ return QUERY_GLOBAL;
+ }
+
+ // check for "Global" request
+
+ pwcArgChar = lpValue;
+ pwcTypeChar = GLOBAL_STRING;
+ bFound = TRUE; // assume found until contradicted
+
+ // check to the length of the shortest string
+
+ while ((*pwcArgChar != 0) && (*pwcTypeChar != 0)) {
+ if (*pwcArgChar++ != *pwcTypeChar++) {
+ bFound = FALSE; // no match
+ break; // bail out now
+ }
+ }
+
+ if (bFound) return QUERY_GLOBAL;
+
+ // check for "Foreign" request
+
+ pwcArgChar = lpValue;
+ pwcTypeChar = FOREIGN_STRING;
+ bFound = TRUE; // assume found until contradicted
+
+ // check to the length of the shortest string
+
+ while ((*pwcArgChar != 0) && (*pwcTypeChar != 0)) {
+ if (*pwcArgChar++ != *pwcTypeChar++) {
+ bFound = FALSE; // no match
+ break; // bail out now
+ }
+ }
+
+ if (bFound) return QUERY_FOREIGN;
+
+ // check for "Costly" request
+
+ pwcArgChar = lpValue;
+ pwcTypeChar = COSTLY_STRING;
+ bFound = TRUE; // assume found until contradicted
+
+ // check to the length of the shortest string
+
+ while ((*pwcArgChar != 0) && (*pwcTypeChar != 0)) {
+ if (*pwcArgChar++ != *pwcTypeChar++) {
+ bFound = FALSE; // no match
+ break; // bail out now
+ }
+ }
+
+ if (bFound) return QUERY_COSTLY;
+
+ // if not Global and not Foreign and not Costly,
+ // then it must be an item list
+
+ return QUERY_ITEMS;
+
+}
+
+BOOL
+IsNumberInUnicodeList (
+ IN DWORD dwNumber,
+ IN LPWSTR lpwszUnicodeList
+)
+/*++
+
+IsNumberInUnicodeList
+
+Arguments:
+
+ IN dwNumber
+ DWORD number to find in list
+
+ IN lpwszUnicodeList
+ Null terminated, Space delimited list of decimal numbers
+
+Return Value:
+
+ TRUE:
+ dwNumber was found in the list of unicode number strings
+
+ FALSE:
+ dwNumber was not found in the list.
+
+--*/
+{
+ DWORD dwThisNumber;
+ WCHAR *pwcThisChar;
+ BOOL bValidNumber;
+ BOOL bNewItem;
+ //BOOL bReturnValue;
+ WCHAR wcDelimiter; // could be an argument to be more flexible
+
+ if (lpwszUnicodeList == 0) return FALSE; // null pointer, # not found
+
+ pwcThisChar = lpwszUnicodeList;
+ dwThisNumber = 0;
+ wcDelimiter = (WCHAR)' ';
+ bValidNumber = FALSE;
+ bNewItem = TRUE;
+
+ while (TRUE) {
+ switch (EvalThisChar (*pwcThisChar, wcDelimiter)) {
+ case DIGIT:
+ // if this is the first digit after a delimiter, then
+ // set flags to start computing the new number
+ if (bNewItem) {
+ bNewItem = FALSE;
+ bValidNumber = TRUE;
+ }
+ if (bValidNumber) {
+ dwThisNumber *= 10;
+ dwThisNumber += (*pwcThisChar - (WCHAR)'0');
+ }
+ break;
+
+ case DELIMITER:
+ // a delimiter is either the delimiter character or the
+ // end of the string ('\0') if when the delimiter has been
+ // reached a valid number was found, then compare it to the
+ // number from the argument list. if this is the end of the
+ // string and no match was found, then return.
+ //
+ if (bValidNumber) {
+ if (dwThisNumber == dwNumber) return TRUE;
+ bValidNumber = FALSE;
+ }
+ if (*pwcThisChar == 0) {
+ return FALSE;
+ } else {
+ bNewItem = TRUE;
+ dwThisNumber = 0;
+ }
+ break;
+
+ case INVALID:
+ // if an invalid character was encountered, ignore all
+ // characters up to the next delimiter and then start fresh.
+ // the invalid number is not compared.
+ bValidNumber = FALSE;
+ break;
+
+ default:
+ break;
+
+ }
+ pwcThisChar++;
+ }
+
+} // IsNumberInUnicodeList
diff --git a/ldap/servers/slapd/ntperfdll/nsldapctrutil.h b/ldap/servers/slapd/ntperfdll/nsldapctrutil.h
new file mode 100644
index 00000000..37384b3a
--- /dev/null
+++ b/ldap/servers/slapd/ntperfdll/nsldapctrutil.h
@@ -0,0 +1,120 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+
+
+/*++ BUILD Version: 0001 // Increment this if a change has global effects
+
+Copyright (c) 1992 Microsoft Corporation
+
+Module Name:
+
+ perfutil.h
+
+Abstract:
+
+
+
+ This file supports routines used to parse and create Performance Monitor Data
+ Structures. It actually supports Performance Object types with multiple instances
+
+
+
+Revision History:
+
+--*/
+#ifndef _PERFUTIL_H_
+#define _PERFUTIL_H_
+
+// enable this define to log process heap data to the event log
+#ifdef PROBE_HEAP_USAGE
+#undef PROBE_HEAP_USAGE
+#endif
+//
+
+
+// Utility macro. This is used to reserve a DWORD multiple of bytes for Unicode strings
+// embedded in the definitional data, viz., object instance names.
+
+
+//
+
+
+#define DWORD_MULTIPLE(x) (((x+sizeof(DWORD)-1)/sizeof(DWORD))*sizeof(DWORD))
+
+
+
+// (assumes dword is 4 bytes long and pointer is a dword in size)
+
+
+#define ALIGN_ON_DWORD(x) ((VOID *)( ((DWORD) x & 0x00000003) ? ( ((DWORD) x & 0xFFFFFFFC) + 4 ) : ( (DWORD) x ) ))
+
+
+
+extern WCHAR GLOBAL_STRING[]; // Global command (get all local ctrs)
+extern WCHAR FOREIGN_STRING[]; // get data from foreign computers
+extern WCHAR COSTLY_STRING[];
+
+
+extern WCHAR NULL_STRING[];
+
+
+
+#define QUERY_GLOBAL 1
+#define QUERY_ITEMS 2
+#define QUERY_FOREIGN 3
+#define QUERY_COSTLY 4
+
+//
+
+
+// The definition of the only routine of perfutil.c, It builds part of a performance data
+// instance (PERF_INSTANCE_DEFINITION) as described in winperf.h
+
+
+//
+
+HANDLE MonOpenEventLog ();
+VOID MonCloseEventLog ();
+DWORD GetQueryType (IN LPWSTR);
+BOOL IsNumberInUnicodeList (DWORD, LPWSTR);
+
+typedef struct _LOCAL_HEAP_INFO_BLOCK {
+ DWORD AllocatedEntries;
+ DWORD AllocatedBytes;
+ DWORD FreeEntries;
+ DWORD FreeBytes;
+} LOCAL_HEAP_INFO, *PLOCAL_HEAP_INFO;
+
+
+//
+// Memory Probe macro
+//
+#ifdef PROBE_HEAP_USAGE
+
+#define HEAP_PROBE() { \
+ DWORD dwHeapStatus[5]; \
+ NTSTATUS CallStatus; \
+ dwHeapStatus[4] = __LINE__; \
+ if (!(CallStatus = memprobe (dwHeapStatus, 16L, NULL))) { \
+ REPORT_INFORMATION_DATA (VGA_HEAP_STATUS, LOG_DEBUG, \
+ &dwHeapStatus, sizeof(dwHeapStatus)); \
+ } else { \
+ REPORT_ERROR_DATA (VGA_HEAP_STATUS_ERROR, LOG_DEBUG, \
+ &CallStatus, sizeof (DWORD)); \
+ } \
+}
+
+#else
+
+#define HEAP_PROBE() ;
+
+
+
+
+
+#endif
+
+#endif //_PERFUTIL_H_
diff --git a/ldap/servers/slapd/ntperfdll/nsldapreg.ini b/ldap/servers/slapd/ntperfdll/nsldapreg.ini
new file mode 100644
index 00000000..6ca7a2c2
--- /dev/null
+++ b/ldap/servers/slapd/ntperfdll/nsldapreg.ini
@@ -0,0 +1,18 @@
+#
+# BEGIN COPYRIGHT BLOCK
+# Copyright 2001 Sun Microsystems, Inc.
+# Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+# All rights reserved.
+# END COPYRIGHT BLOCK
+#
+\Registry\Machine
+ System
+ CurrentControlSet
+ Services
+ slapd7
+ Performance
+ Close = REG_SZ CloseNSPerformanceData
+ Collect = REG_SZ CollectNSPerformanceData
+ Library = REG_SZ nsctr.dll
+ Open = REG_SZ OpenNSPerformanceData
+
diff --git a/ldap/servers/slapd/ntuserpin.c b/ldap/servers/slapd/ntuserpin.c
new file mode 100644
index 00000000..6e0de7d4
--- /dev/null
+++ b/ldap/servers/slapd/ntuserpin.c
@@ -0,0 +1,178 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+/******************************************************
+ *
+ * ntuserpin.c - Prompts for the key
+ * database passphrase.
+ *
+ ******************************************************/
+
+#if defined( _WIN32 ) && defined ( NET_SSL )
+
+#include <windows.h>
+#include "ntwatchdog.h"
+#include "slapi-plugin.h"
+#include "fe.h"
+
+#undef Debug
+#undef OFF
+#undef LITTLE_ENDIAN
+
+#include <stdio.h>
+#include <string.h>
+#include <sys/types.h>
+#include "slap.h"
+#include "fe.h"
+
+static int i=0;
+static int cbRemotePassword = 0;
+
+extern char* NT_PromptForPin(const char *tokenName);
+
+static const char nt_retryWarning[] =
+"Warning: You entered an incorrect PIN. Incorrect PIN may result in disabling the token";
+
+struct SVRCORENTUserPinObj
+{
+ SVRCOREPinObj base;
+};
+static const struct SVRCOREPinMethods vtable;
+
+/* ------------------------------------------------------------ */
+SVRCOREError
+SVRCORE_CreateNTUserPinObj(SVRCORENTUserPinObj **out)
+{
+ SVRCOREError err = 0;
+ SVRCORENTUserPinObj *obj = 0;
+
+ do {
+ obj = (SVRCORENTUserPinObj*)malloc(sizeof (SVRCORENTUserPinObj));
+ if (!obj) { err = 1; break; }
+
+ obj->base.methods = &vtable;
+
+ } while(0);
+
+ if (err)
+ {
+ SVRCORE_DestroyNTUserPinObj(obj);
+ obj = 0;
+ }
+
+ *out = obj;
+ return err;
+}
+
+void
+SVRCORE_DestroyNTUserPinObj(SVRCORENTUserPinObj *obj)
+{
+ if (obj) free(obj);
+}
+
+static void destroyObject(SVRCOREPinObj *obj)
+{
+ SVRCORE_DestroyNTUserPinObj((SVRCORENTUserPinObj*)obj);
+}
+
+/* First try to retrieve the password from the watchdog,
+ if this is not available, prompt for the passphrase. */
+
+static char *getPin(SVRCOREPinObj *obj, const char *tokenName, PRBool retry)
+{
+ HWND hwndRemote;
+ char *szRemotePassword = NULL;
+ HANDLE hRemoteProcess;
+ DWORD dwNumberOfBytesRead=0;
+ DWORD dwNumberOfBytesWritten=0;
+ PK11_PIN *buf= NULL;
+ char *password = NULL;
+ char pin[MAX_PASSWORD];
+ BOOL ret;
+ DWORD err = 0;
+
+ // Find Watchdog application window
+ if( pszServerName && (hwndRemote = FindWindow("slapd", pszServerName)) &&
+ (hRemoteProcess = (HANDLE)GetWindowLong( hwndRemote,
+ GWL_PROCESS_HANDLE)))
+ {
+ cbRemotePassword = GetWindowLong(hwndRemote, GWL_PASSWORD_LENGTH);
+ szRemotePassword = (HANDLE)GetWindowLong(hwndRemote, GWL_PASSWORD_ADDR);
+
+ // if retry, don't get the pin from watchdog
+ if (retry)
+ {
+ MessageBox(GetDesktopWindow(), nt_retryWarning,
+ "Netscape Server", MB_ICONEXCLAMATION | MB_OK);
+ } else {
+ if((cbRemotePassword != 0) && (szRemotePassword != 0))
+ {
+ buf = (PK11_PIN *)slapi_ch_malloc(sizeof
+ (PK11_PIN)*cbRemotePassword);
+ if(ReadProcessMemory(hRemoteProcess, szRemotePassword,
+ (LPVOID)buf,sizeof(PK11_PIN)*cbRemotePassword ,
+ &dwNumberOfBytesRead))
+ {
+
+ for (i=0; i < cbRemotePassword; i++) {
+ if (strncmp (tokenName, buf[i].TokenName,
+ buf[i].TokenLength)==0)
+ {
+ memset(pin, '\0', MAX_PASSWORD);
+ strncpy (pin, buf[i].Password,
+ buf[i].PasswordLength);
+ slapi_ch_free ((void **) &buf);
+ return slapi_ch_strdup(pin);
+ }
+ }
+ }
+ }
+ }
+ }
+
+ /* Didn't get the password from Watchdog, or this is a retry,
+ prompt the user. */
+
+ password = NT_PromptForPin(tokenName);
+
+ /* Store the password back to nt watchdog */
+ if (password != NULL && hwndRemote && hRemoteProcess)
+ {
+ slapi_ch_free ((void **) &buf);
+ buf = (PK11_PIN *)slapi_ch_malloc(sizeof(PK11_PIN));
+ strcpy (buf[0].TokenName, tokenName);
+ buf[0].TokenLength=strlen(tokenName);
+ strcpy (buf[0].Password, password);
+ buf[0].PasswordLength=strlen(password);
+ if (i== cbRemotePassword)
+ {
+ /* Add a new token and password to the end of the table.*/
+
+ SetWindowLong(hwndRemote, GWL_PASSWORD_LENGTH,
+ (LONG)cbRemotePassword+1);
+ ret = WriteProcessMemory(hRemoteProcess,
+ szRemotePassword+cbRemotePassword*sizeof(PK11_PIN),
+ (LPVOID)buf, sizeof(PK11_PIN), &dwNumberOfBytesWritten);
+ if( !ret )
+ err = GetLastError();
+ } else {
+ /* This is a retry due to a wrong password stored in watchdog. */
+ ret = WriteProcessMemory(hRemoteProcess,
+ szRemotePassword+i*sizeof(PK11_PIN),(LPVOID)buf,
+ sizeof(PK11_PIN), &dwNumberOfBytesWritten);
+ if( !ret )
+ err = GetLastError();
+ }
+ }
+ slapi_ch_free ((void **) &buf);
+ return (password);
+}
+
+/*
+ * VTable
+ */
+static const SVRCOREPinMethods vtable =
+{ 0, 0, destroyObject, getPin };
+#endif /* defined( _WIN32 ) && defined ( NET_SSL ) */
diff --git a/ldap/servers/slapd/ntwdog/Makefile b/ldap/servers/slapd/ntwdog/Makefile
new file mode 100644
index 00000000..f727232c
--- /dev/null
+++ b/ldap/servers/slapd/ntwdog/Makefile
@@ -0,0 +1,60 @@
+#
+# BEGIN COPYRIGHT BLOCK
+# Copyright 2001 Sun Microsystems, Inc.
+# Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+# All rights reserved.
+# END COPYRIGHT BLOCK
+#
+#
+# GNU Makefile for LDAP Server NT Service watchdog
+#
+
+LDAP_SRC = ../../..
+MCOM_ROOT = ../../../../..
+
+NOSTDCLEAN=true # don't let nsconfig.mk define target clean
+NOSTDSTRIP=true # don't let nsconfig.mk define target strip
+NSPR20=true # probably should be defined somewhere else (not sure where)
+
+OBJDEST = $(OBJDIR)/ntwdog/obj
+SLAPD_OBJDEST= $(OBJDIR)/obj
+BINDIR = $(LDAP_SERVER_RELDIR)
+LDAP_LIBDIR = $(OBJDIR)/lib
+
+include $(MCOM_ROOT)/ldapserver/nsconfig.mk
+include $(LDAP_SRC)/nsldap.mk
+
+ifeq ($(ARCH), WINNT)
+SUBSYSTEM=windows
+endif
+
+#INCLUDES += -I$(MCOM_ROOT)/ldapserver/include/libadmin
+
+DS_SERVER_DEFS = -DNS_DS
+
+NTWDOG_OBJS= ntwatchdog.o
+
+OBJS = $(addprefix $(OBJDEST)/, $(NTWDOG_OBJS))
+
+LIBS_DEP = $(LDAP_LIBUTIL_DEP) $(LIBADMIN_DEP)
+
+LIBS = $(LDAP_LIBUTIL)
+
+EXTRA_LIBS += $(LIBS)
+
+NTWDOG = $(addprefix $(BINDIR)/, ns-slapd.exe)
+
+all: $(OBJDEST) $(BINDIR) $(NTWDOG)
+
+$(OBJDEST):
+ $(MKDIR) $(OBJDEST)
+
+$(NTWDOG): $(OBJS) $(LIBS_DEP)
+ $(LINK_EXE)
+
+veryclean: clean
+
+clean:
+ $(RM) $(OBJS)
+ $(RM) $(NTWDOG)
+
diff --git a/ldap/servers/slapd/ntwdog/cron_conf.c b/ldap/servers/slapd/ntwdog/cron_conf.c
new file mode 100644
index 00000000..8d60191a
--- /dev/null
+++ b/ldap/servers/slapd/ntwdog/cron_conf.c
@@ -0,0 +1,654 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+
+#include <windows.h>
+#include <stdio.h>
+#include <string.h>
+#include <process.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include "cron_conf.h"
+
+#define NSAPI_PUBLIC
+
+#ifndef BUF_SIZE
+#define BUF_SIZE 4096
+#endif
+
+#ifndef S_BUF_SIZE
+#define S_BUF_SIZE 1024
+#endif
+
+#ifdef XP_WIN32
+#pragma warning (disable: 4005) // macro redifinition //
+#define MALLOC(size) malloc(size)
+#define REALLOC(x, size) realloc(x, size)
+#define FREE(x) free((void*) x)
+#define STRDUP(x) strdup(x)
+#define strcasecmp(x, y) stricmp(x, y)
+#pragma warning (default: 4005) // macro redifinition //
+#endif
+
+static char *admroot;
+static char *nsroot;
+
+#define DAILY "Sun Mon Tue Wed Thu Fri Sat"
+
+static cron_conf_list *cclist = NULL;
+static cron_conf_list *cctail = NULL;
+static char *conffile = NULL;
+
+#ifndef CRON_CONF_STAND_ALONE
+static void set_roots()
+{
+ char *ar = ADMCONFDIR;
+ if(ar)
+ admroot = STRDUP(ar);
+}
+#endif
+
+/* General note: strtok() is not MT safe on Unix , but it is okay to call
+ here because this file is NT only and strtok() is MT safe on NT */
+
+static char *nocr(char *buf)
+{
+ if (buf)
+ {
+ if(buf[strlen(buf) - 1] == '\n')
+ buf[strlen(buf) - 1] = '\0';
+ }
+
+ return buf;
+}
+
+static int debug(char *fmt, ...)
+{
+ va_list args;
+ char buf[BUF_SIZE];
+
+ va_start(args, fmt);
+ vsprintf(buf, fmt, args);
+ va_end(args);
+
+ fprintf(stdout, "<<DEBUG>> %s <<DEBUG>>\n", buf);
+ fflush(stdout);
+
+ return 1;
+}
+
+static char *get_conf_file()
+{
+ static char conffile [S_BUF_SIZE];
+ char nsconfile[S_BUF_SIZE];
+ char buf [BUF_SIZE];
+ char *r, *p;
+ FILE *fp;
+ int flag = 0;
+
+ if (admroot)
+ sprintf(nsconfile, "%s/ns-cron.conf", admroot);
+ else
+ sprintf(nsconfile, "%s/admin-serv/config/ns-cron.conf", nsroot);
+
+ if (!(fp = fopen(nsconfile, "r")))
+ return NULL;
+
+ while(fgets(buf, sizeof(buf), fp))
+ {
+ r = strtok(buf, " \t\n");
+ if (!r) /* bad line, ignore */
+ continue;
+
+ p = strtok(NULL, " \t\n");
+ if (!p) /* bad line, ignore */
+ continue;
+
+ if (!strcasecmp(r, "ConfFile"))
+ {
+ /* if filename without path is specified, default to admin svr dir */
+ if((strchr(p, '\\') == NULL) &&
+ (strchr(p, '/') == NULL))
+ sprintf(conffile, "%s/%s", admroot, p);
+ else
+ sprintf(conffile, "%s", p);
+ flag++;
+ break;
+ }
+ }
+
+ fclose(fp);
+
+ if (!flag)
+ return NULL;
+
+ return conffile;
+}
+
+
+#ifndef CRON_CONF_STAND_ALONE
+NSAPI_PUBLIC
+#endif
+cron_conf_obj *cron_conf_create_obj(char *name, char *command, char *dir,
+ char *user, char *start_time, char *days)
+{
+ cron_conf_obj *object;
+ char *d = NULL;
+
+ object = (cron_conf_obj*)MALLOC(sizeof(cron_conf_obj));
+
+ object->name = (name) ? STRDUP(name) : NULL;
+ object->command = (command) ? STRDUP(command) : NULL;
+ object->dir = (dir) ? STRDUP(dir) : NULL;
+ object->user = (user) ? STRDUP(user) : NULL;
+ object->start_time = (start_time) ? STRDUP(start_time) : NULL;
+
+#if 1
+ if (days)
+ {
+ if (!(strcasecmp(days, "Daily")))
+ d = STRDUP(DAILY);
+ else
+ d = STRDUP(days);
+ }
+#else
+ d = STRDUP("Wed Thu");
+#endif
+
+ object->days = d;
+
+ return object;
+}
+
+
+static void cron_conf_free_listobj(cron_conf_list *lobj)
+{
+ cron_conf_obj *obj = lobj->obj;
+
+
+ if (obj)
+ {
+ if(obj->name) FREE(obj->name);
+ if(obj->command) FREE(obj->command);
+ if(obj->dir) FREE(obj->dir);
+ if(obj->user) FREE(obj->user);
+ if(obj->start_time) FREE(obj->start_time);
+ if(obj->days) FREE(obj->days);
+
+ FREE(obj);
+ }
+
+ FREE(lobj);
+}
+
+
+static cron_conf_obj *get_object(FILE *fp)
+{
+ cron_conf_obj *object;
+ char name [S_BUF_SIZE];
+ char command [S_BUF_SIZE];
+ char dir [S_BUF_SIZE];
+ char user [S_BUF_SIZE];
+ char start_time[S_BUF_SIZE];
+ char days [S_BUF_SIZE];
+ char buf [BUF_SIZE];
+ char *p, *q;
+ int flag = 0;
+ int hascom, hasdir, hasuser, hastime, hasdays;
+
+ p = fgets(buf, sizeof(buf), fp);
+
+ if (!p)
+ return NULL;
+ /* else debug("Read line '%s'", nocr(buf)); */
+
+ if (strncmp(buf, "<Object", 7))
+ return NULL;
+
+ hascom = hasdir = hasuser = hastime = hasdays = 0;
+
+ p = strtok(buf, "<=>\n\t ");
+ if (!p)
+ return NULL;
+
+ p = strtok(NULL, "<=>\n\t ");
+ if (!p)
+ return NULL;
+
+ p = strtok(NULL, "<=>\n\t ");
+ if (!p)
+ return NULL;
+
+ sprintf(name, "%s", p);
+ /* debug("Setting name to '%s'", name); */
+
+ while(fgets(buf, sizeof(buf), fp))
+ {
+ /* debug("Read line '%s'", nocr(buf)); */
+
+ p = strtok(buf, " \t\n");
+
+ if (!p)
+ continue;
+
+ if (!strcasecmp(p, "</Object>"))
+ {
+ flag++;
+ break;
+ }
+
+ if(!strcasecmp(p, "Command"))
+ {
+ q = strtok(NULL, "\n");
+
+ if (q)
+ q = strchr(q, '\"');
+
+ if (q)
+ q++;
+
+ if (q)
+ {
+ if (!hascom)
+ {
+ /* get rid of quotes */
+ p = strrchr(q, '\"');
+
+ if (p)
+ *p = '\0';
+
+ if (q)
+ {
+ sprintf(command, "%s", q);
+ /* debug("Setting command to '%s'", command); */
+ hascom++;
+ }
+ }
+ else /* already has a command */
+ ; /* ignore */
+ }
+ continue;
+ }
+
+ if(!strcasecmp(p, "Dir"))
+ {
+ q = strtok(NULL, "\n");
+
+ if (q)
+ q = strchr(q, '\"');
+
+ if (q)
+ q++;
+
+ if (q)
+ {
+ if (!hasdir)
+ {
+ /* get rid of quotes */
+ p = strrchr(q, '\"');
+
+ if (p)
+ *p = '\0';
+
+ if (q)
+ {
+ sprintf(dir, "%s", q);
+ /* debug("Setting dir to '%s'", dir); */
+ hasdir++;
+ }
+ }
+ else /* already has a dir */
+ ; /* ignore */
+ }
+ continue;
+ }
+
+ else if(!strcasecmp(p, "User"))
+ {
+ q = strtok(NULL, " \t\n");
+
+ if (q)
+ {
+ if (!hasuser)
+ {
+ sprintf(user, "%s", q);
+ /* debug("Setting user to '%s'", user); */
+ hasuser++;
+ }
+ else /* already has a user */
+ ; /* ignore */
+ }
+ continue;
+ }
+
+ else if(!strcasecmp(p, "Time"))
+ {
+ q = strtok(NULL, "\n");
+
+ if (q)
+ {
+ if (!hastime)
+ {
+ sprintf(start_time, "%s", q);
+ /* debug("Setting time to '%s'", start_time); */
+ hastime++;
+ }
+ else /* already has a time */
+ ; /* ignore */
+ }
+ continue;
+ }
+
+ else if(!strcasecmp(p, "Days"))
+ {
+ q = strtok(NULL, "\n");
+
+ if (q)
+ {
+ if (!hasdays)
+ {
+ sprintf(days, "%s", q);
+ /* debug("Setting days to '%s'", days); */
+ hasdays++;
+ }
+ else /* already has days */
+ ; /* ignore */
+ }
+ continue;
+ }
+
+ else
+ {
+ /* gibberish... ignore... will be fixed when
+ file is rewritten */
+ continue;
+ }
+ }
+
+ object = cron_conf_create_obj(name,
+ (hascom) ? command : NULL,
+ (hasdir) ? dir : NULL,
+ (hasuser) ? user : NULL,
+ (hastime) ? start_time : NULL,
+ (hasdays) ? days : NULL);
+
+ return object;
+}
+
+
+static void cron_conf_write_stream(FILE *fp)
+{
+ cron_conf_obj *obj;
+ cron_conf_list *lobj;
+
+ for(lobj = cclist; lobj; lobj = lobj->next)
+ {
+ obj = lobj->obj;
+
+ fprintf(fp, "<Object name=%s>\n", (obj->name) ? obj->name : "?");
+ fprintf(fp, " Command \"%s\"\n", (obj->command) ? obj->command : "?");
+ if (obj->dir)
+ fprintf(fp, " Dir \"%s\"\n", obj->dir);
+ if (obj->user)
+ fprintf(fp, " User %s\n", obj->user);
+ fprintf(fp, " Time %s\n", (obj->start_time) ? obj->start_time : "?");
+ fprintf(fp, " Days %s\n", (obj->days) ? obj->days : "?");
+ fprintf(fp, "</Object>\n");
+ }
+}
+
+
+static void cron_conf_delete(char *name, cron_conf_obj *cco)
+{
+ cron_conf_list *lobj = NULL;
+ cron_conf_list *pobj = NULL;
+
+ if (!cclist)
+ return;
+
+ if (!strcmp(cclist->name, name))
+ {
+ lobj = cclist;
+ cclist = cclist->next;
+ if (cctail == lobj)
+ cctail = cclist;
+
+ cron_conf_free_listobj(lobj);
+ }
+ else
+ {
+ pobj = cclist;
+
+ for(lobj = cclist->next; lobj; lobj = lobj->next)
+ {
+ if(!strcmp(lobj->name, name))
+ {
+ if (lobj == cctail)
+ cctail = pobj;
+
+ pobj->next = lobj->next;
+ cron_conf_free_listobj(lobj);
+
+ break;
+ }
+
+ pobj = lobj;
+ }
+ }
+
+ return;
+}
+
+#ifndef CRON_CONF_STAND_ALONE
+NSAPI_PUBLIC
+#endif
+int cron_conf_read()
+{
+ FILE *fp;
+ cron_conf_obj *obj;
+ cron_conf_list *lobj;
+
+#ifndef CRON_CONF_STAND_ALONE
+ set_roots();
+#endif
+
+ if (!(conffile = get_conf_file()))
+ {
+ /* debug("Conffile is null"); */
+ return 0;
+ }
+ /* else debug("Conffile: '%s'", conffile); */
+
+ if (!(fp = fopen(conffile, "r")))
+ {
+ /* debug("Couldn't open conffile"); */
+ return 0;
+ }
+
+ while((obj = get_object(fp)))
+ {
+ lobj = (cron_conf_list*)MALLOC(sizeof(struct cron_conf_list));
+ lobj->name = obj->name;
+ lobj->obj = obj;
+ lobj->next = NULL;
+
+ /* debug("Created a list object named '%s'", lobj->name); */
+
+ if(cclist == NULL) /* first object */
+ {
+ cclist = cctail = lobj;
+ }
+ else
+ {
+ cctail->next = lobj;
+ cctail = lobj;
+ }
+
+ /* debug("List now, head: '%s', tail: '%s'",
+ cclist->name, cctail->name); */
+ }
+
+ fclose(fp);
+
+ return 1;
+}
+
+#ifndef CRON_CONF_STAND_ALONE
+NSAPI_PUBLIC
+#endif
+cron_conf_obj *cron_conf_get(char *name)
+{
+ cron_conf_obj *obj = NULL;
+ cron_conf_list *lobj = NULL;
+
+ /* find object */
+ for(lobj = cclist; lobj; lobj = lobj->next)
+ {
+ if(!strcmp(lobj->name, name))
+ {
+ obj = lobj->obj;
+ break;
+ }
+ }
+
+#if 0
+ if (obj)
+ {
+ debug("Found object %s", obj->name);
+ debug("obj->command = %s", (obj->command) ? obj->command : "NULL");
+ debug("obj->dir = %s", (obj->dir) ? obj->dir : "NULL");
+ debug("obj->user = %s", (obj->user) ? obj->user : "NULL");
+ debug("obj->start_time = %s", (obj->start_time) ? obj->start_time : "NULL");
+ debug("obj->days = %s", (obj->days) ? obj->days : "NULL");
+ }
+#endif
+
+ return obj;
+}
+
+
+#ifndef CRON_CONF_STAND_ALONE
+NSAPI_PUBLIC
+#endif
+cron_conf_list *cron_conf_get_list()
+{
+ return cclist;
+}
+
+#ifndef CRON_CONF_STAND_ALONE
+NSAPI_PUBLIC
+#endif
+cron_conf_obj *cron_conf_set(char *name, cron_conf_obj *cco)
+{
+ cron_conf_obj *obj = NULL;
+ cron_conf_list *lobj = NULL;
+
+ if (!name)
+ return NULL;
+
+ if (!cco)
+ {
+ cron_conf_delete(name, cco);
+ return NULL;
+ }
+ else /* cco exists */
+ {
+ /* find object */
+ obj = cron_conf_get(name);
+
+
+ if (obj) /* found it */
+ {
+ if (cco->command)
+ {
+ FREE(obj->command);
+ obj->command = cco->command;
+ }
+
+ if (cco->dir)
+ {
+ FREE(obj->dir);
+ obj->dir = cco->dir;
+ }
+
+ if (cco->user)
+ {
+ FREE(obj->user);
+ obj->user = cco->user;
+ }
+
+ if (cco->start_time)
+ {
+ FREE(obj->start_time);
+ obj->start_time = cco->start_time;
+ }
+
+ if (cco->days)
+ {
+ FREE(obj->days);
+ obj->days = cco->days;
+ }
+
+ FREE(cco);
+ }
+ else /* couldn't find it */
+ {
+ obj = cco;
+
+ lobj = (cron_conf_list*)MALLOC(sizeof(cron_conf_list));
+ lobj->name = obj->name;
+ lobj->obj = obj;
+ lobj->next = NULL;
+
+ if(cclist == NULL) /* first object */
+ {
+ cclist = cctail = lobj;
+ }
+ else
+ {
+ cctail->next = lobj;
+ cctail = lobj;
+ }
+ }
+ }
+
+ return obj;
+}
+
+void cron_conf_write()
+{
+ FILE *fp;
+
+ if (!conffile)
+ conffile = get_conf_file();
+
+ if(!(fp = fopen(conffile, "w")))
+ return;
+
+ cron_conf_write_stream(fp);
+
+ fclose(fp);
+}
+
+
+#ifndef CRON_CONF_STAND_ALONE
+NSAPI_PUBLIC
+#endif
+void cron_conf_free()
+{
+ cron_conf_list *lobj = NULL;
+
+ /* find object */
+ while(cclist)
+ {
+ lobj = cclist;
+ cclist = cclist->next;
+
+ cron_conf_free_listobj(lobj);
+ }
+
+ cclist = cctail = NULL;
+}
+
diff --git a/ldap/servers/slapd/ntwdog/cron_conf.h b/ldap/servers/slapd/ntwdog/cron_conf.h
new file mode 100644
index 00000000..23a32463
--- /dev/null
+++ b/ldap/servers/slapd/ntwdog/cron_conf.h
@@ -0,0 +1,86 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+/***********************************************************************
+** $Id: cron_conf.h,v 1.1 2005/01/21 00:40:52 cvsadm Exp $
+**
+**
+** NAME
+** cron_conf.h
+**
+** DESCRIPTION
+**
+**
+** AUTHOR
+** <robw@netscape.com>
+**
+***********************************************************************/
+
+#ifndef _CRON_CONF_H_
+#define _CRON_CONF_H_
+
+/***********************************************************************
+** Includes
+***********************************************************************/
+/* read and write to cron.conf, cron_conf.c */
+/* Alex Feygin, 3/22/96 */
+typedef struct cron_conf_obj
+{
+ char *name;
+ char *command;
+ char *dir;
+ char *user;
+ char *start_time;
+ char *days;
+}
+cron_conf_obj;
+
+typedef struct cron_conf_list
+{
+ char *name;
+ cron_conf_obj *obj;
+ struct cron_conf_list *next;
+}
+cron_conf_list;
+
+/* Reads cron.conf to a null terminated list of cron_conf_objects; returns
+ 0 if unable to do a read; 1 otherwise */
+int cron_conf_read();
+
+/* gets a cron object, NULL if it doesnt exist */
+cron_conf_obj *cron_conf_get(char *name);
+
+/* returns a NULL-terminated cron_conf_list of all the cron conf objects */
+cron_conf_list *cron_conf_get_list();
+
+/* Creates a cron conf object; all these args get STRDUP'd in the function
+ so make sure to free up the space later if need be */
+cron_conf_obj *cron_conf_create_obj(char *name, char *command,
+ char *dir, char *user,
+ char *start_time, char *days);
+
+/* Puts a cron conf object into list or updates it if it already in there.
+ Returns either the object passed or the object in there already;
+ cco may be FREE'd during this operation so if you need the object
+ back, call it like so:
+
+ cco = cron_conf_set(cco->name, cco);
+
+ calling cron_conf_set with a NULL cco will cause the 'name' object
+ to be deleted.
+*/
+cron_conf_obj *cron_conf_set(char *name, cron_conf_obj *cco);
+
+/* write out current list of cron_conf_objects to cron.conf file */
+void cron_conf_write();
+
+/* free all cron conf data structures */
+void cron_conf_free();
+
+#define MAGNUS_CONF "magnus.conf"
+#define ADMCONFDIR "../config/"
+
+
+#endif /* _CRON_CONF_H_ */
diff --git a/ldap/servers/slapd/ntwdog/ntcron.c b/ldap/servers/slapd/ntwdog/ntcron.c
new file mode 100644
index 00000000..98b4cae3
--- /dev/null
+++ b/ldap/servers/slapd/ntwdog/ntcron.c
@@ -0,0 +1,156 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+// //
+// Name: NTCRON //
+// Platforms: WIN32 //
+// Description: unix cron functionality in a separate thread //
+// Notes: //
+// The following assumptions are made: //
+// - gszServerRoot is set to c:\netscape\server //
+// - ns-cron.conf and cron.conf are available //
+// Todo: //
+// - handle time format variations of hh:mm //
+// - keep track of children //
+// ...................................................................... //
+// Revision History: //
+// 03-26-96 Initial Version, Andy Hakim (ahakim@netscape.com) //
+// 07-10-96 Modified for Directory Server, pkennedy@netscape.com //
+//--------------------------------------------------------------------------//
+#include <windows.h>
+#include "ntwatchdog.h"
+#include "ntslapdmessages.h" // event log msgs constants //
+#include "cron_conf.h"
+
+static cron_conf_list *cclist = NULL;
+
+
+//--------------------------------------------------------------------------//
+// //
+//--------------------------------------------------------------------------//
+BOOL CRON_CheckDay(LPSYSTEMTIME lpstNow, char *szDays)
+{
+ BOOL bReturn = FALSE;
+ char szToday[16];
+ if(GetDateFormat((LCID)NULL, 0, lpstNow, "ddd", szToday, sizeof(szToday)) != 0)
+ {
+ strlwr(szDays);
+ strlwr(szToday);
+ if(strstr(szDays, szToday) != NULL)
+ bReturn = TRUE;
+ }
+ return(bReturn);
+}
+
+
+
+//--------------------------------------------------------------------------//
+// //
+//--------------------------------------------------------------------------//
+BOOL CRON_CheckTime(LPSYSTEMTIME lpstNow, char *szTime)
+{
+ BOOL bReturn = FALSE;
+ char szCurrentTime[16];
+ char szStartTime[16];
+
+ strcpy(szStartTime, szTime);
+
+ if(szTime[1] == ':')
+ wsprintf(szStartTime, "0%s", szTime);
+
+ if(GetTimeFormat((LCID)LOCALE_SYSTEM_DEFAULT, TIME_FORCE24HOURFORMAT, lpstNow, "hh:mm", szCurrentTime, sizeof(szCurrentTime)) != 0)
+ {
+ if(strcmp(szCurrentTime, szStartTime) == 0)
+ bReturn = TRUE;
+ }
+ return(bReturn);
+}
+
+
+
+//--------------------------------------------------------------------------//
+// //
+//--------------------------------------------------------------------------//
+BOOL CRON_StartJob(PROCESS_INFORMATION *pi, cron_conf_obj *cco)
+{
+ BOOL bReturn = FALSE;
+ STARTUPINFO sui;
+
+ sui.cb = sizeof(STARTUPINFO);
+ sui.lpReserved = 0;
+ sui.lpDesktop = NULL;
+ sui.lpTitle = NULL;
+ sui.dwX = 0;
+ sui.dwY = 0;
+ sui.dwXSize = 0;
+ sui.dwYSize = 0;
+ sui.dwXCountChars = 0;
+ sui.dwYCountChars = 0;
+ sui.dwFillAttribute = 0;
+ sui.dwFlags = STARTF_USESHOWWINDOW;
+ sui.wShowWindow = SW_SHOWMINIMIZED;
+ sui.cbReserved2 = 0;
+ sui.lpReserved2 = 0;
+ sui.hStdInput = 0;
+ sui.hStdOutput = 0;
+ sui.hStdError = 0;
+
+ bReturn = CreateProcess(NULL, cco->command, NULL, NULL,
+ TRUE, 0, NULL, cco->dir, &sui, pi );
+ if(!bReturn)
+ WD_SysLog(EVENTLOG_ERROR_TYPE, MSG_CRON_STARTFAILED, cco->name);
+
+ return(bReturn);
+}
+
+
+//--------------------------------------------------------------------------//
+// //
+//--------------------------------------------------------------------------//
+BOOL CRON_CheckConfFile()
+{
+ BOOL bReturn = FALSE;
+ PROCESS_INFORMATION pi;
+ SYSTEMTIME stNow;
+
+ GetLocalTime(&stNow); // note: this provides time adjusted for local time zone
+
+ if(cron_conf_read())
+ cclist = cron_conf_get_list();
+
+ while((cclist) && (cclist->obj))
+ {
+ cron_conf_obj *cco = cclist->obj;
+ if((cco->days) && (cco->start_time) && (cco->command))
+ {
+ if(CRON_CheckDay(&stNow, cco->days) && CRON_CheckTime(&stNow, cco->start_time))
+ {
+ bReturn = CRON_StartJob(&pi, cco);
+ CLOSEHANDLE(pi.hProcess);
+ CLOSEHANDLE(pi.hThread);
+ }
+ }
+ cclist = cclist->next;
+ }
+ cron_conf_free();
+ return bReturn;
+}
+
+
+
+//--------------------------------------------------------------------------//
+// //
+//--------------------------------------------------------------------------//
+LPTHREAD_START_ROUTINE CRON_ThreadProc(HANDLE hevWatchDogExit)
+{
+ BOOL bExit = FALSE;
+ while(!bExit)
+ {
+ CRON_CheckConfFile();
+ if(WaitForSingleObject(hevWatchDogExit, 1000*DEFAULT_CRON_TIME) != WAIT_TIMEOUT)
+ bExit = TRUE;
+ }
+ return 0;
+}
diff --git a/ldap/servers/slapd/ntwdog/ntwatchdog.c b/ldap/servers/slapd/ntwdog/ntwatchdog.c
new file mode 100644
index 00000000..ef8e795c
--- /dev/null
+++ b/ldap/servers/slapd/ntwdog/ntwatchdog.c
@@ -0,0 +1,1163 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+#pragma warning(disable : 4001)
+// disable warning C4001: nonstandard extension 'single line comment' was used
+
+// //
+// Name: NTWATCHDOG //
+// Platforms: WIN32 //
+// Description: shell for nt directory server, runs as service, launches //
+// server, monitors it, re-launches if server crashes, //
+// Notes: //
+// ...................................................................... //
+// Watchdog can be run as an application or a service. When run as a //
+// service, it uses the service name from the SCM for the server name. //
+// When run as an application, it uses the command line to determine //
+// the server name. The command line can be one of two formats: //
+// c:\navgold\server\slapd-kennedy\config //
+// or //
+// slapd-kennedy //
+// ...................................................................... //
+// server file "lib\base\servssl.c" was changed //
+// - added code to get password from WatchDog process //
+// ...................................................................... //
+// server file "httpd\src\ntmain.c" was changed //
+// - server always runs as an application //
+// - changed hServerDoneEvent global name to "NS_service_name" //
+// this was necessary so that WatchDog can trap the "service_name" event //
+// - above changes were also made in MultipleInstances() //
+// ...................................................................... //
+// server file "lib\libmessages\messages.mc" was changed //
+// - added a couple of extra messages for watchdog event logging //
+// - watchdog is dependent on the server's eventlog source name //
+// ...................................................................... //
+// Revision History: //
+// 01-12-96 Initial Version, Andy Hakim (ahakim@netscape.com) //
+// 02-01-96 changed restart logic, now based on infant mortality time //
+// instead of server exit code //
+// 07-10-96 Modified for Directory Server, pkennedy@netscape.com //
+// //
+//--------------------------------------------------------------------------//
+#include <windows.h>
+#include <stdio.h>
+#include <string.h>
+#include <process.h>
+#include "ntslapdmessages.h" // event log msgs constants //
+#include "regparms.h" // product name, etc //
+#include "ntwatchdog.h"
+#include "version.h"
+#include "ntresource.h"
+#include "proto-ntutil.h"
+
+#ifdef PUMPKIN_HOUR
+#include <time.h>
+#endif
+
+//--------------------------------------------------------------------------//
+// global variables //
+//--------------------------------------------------------------------------//
+SERVICE_STATUS_HANDLE gsshServiceStatus = 0L;
+HWND ghWndMain = NULL;
+HANDLE ghevWatchDogExit = NULL;
+HINSTANCE ghInstance = NULL;
+HANDLE ghdlgPassword = NULL; // handle to password dialog window
+HANDLE ghDuplicateProcess = NULL; // process handle with PROCESS_VM_READ access
+HANDLE ghServerProcess = NULL; // used by app window in TerminateProcess()
+HANDLE ghServerThread0 = NULL; // used by app window in Suspend/ResumeThread()
+HANDLE ghWdogProcess = NULL;
+char gszServerConfig[MAX_LINE]; // ex: c:\netscape\server\slapd-kennedy\config
+char gszServerName[MAX_LINE]; // ex: slapd-kennedy
+char gszServerRoot[MAX_LINE]; // ex: c:\netscape\server
+char gszPassword[2048];
+DWORD gdwServiceError = NO_ERROR; // return error code for service
+DWORD gdwLastStatus = SERVICE_RUNNING;
+
+//--------------------------------------------------------------------------//
+// This is the shutdown handler we register via SetConsoleCtrlHandler()
+// It is really the only guaranteed means we have of shutting down gracefully
+// when the sytem is shutting down. The Service Manager mechanism is not
+// guaranteed to work.
+//--------------------------------------------------------------------------//
+
+BOOL WINAPI WD_ControlHandler(DWORD dwCtrlType)
+{
+ if (dwCtrlType == CTRL_SHUTDOWN_EVENT) {
+ SetEvent(ghevWatchDogExit);
+ WaitForSingleObject(ghWdogProcess, 1000 * DEFAULT_KILL_TIME);
+ return TRUE;
+ }
+ return FALSE;
+}
+
+
+//--------------------------------------------------------------------------//
+// calc szServerRoot given szServerName //
+//--------------------------------------------------------------------------//
+BOOL WD_GetServerConfig(char *szServerId, char *szServerRoot, LPDWORD cbServerRoot)
+{
+ BOOL bReturn = FALSE;
+ HANDLE hSlapdKey = 0;
+ char szSlapdKey[MAX_PATH];
+ DWORD dwValueType;
+ DWORD dwResult = 0;
+
+ // don't want to monitor Admin server
+ if(strcmp(ADM_KEY_ROOT, szServerId) == 0)
+ return(bReturn);
+
+ // query registry key to figure out config directory
+ sprintf(szSlapdKey, "%s\\%s\\%s", KEY_SOFTWARE_NETSCAPE, SVR_KEY_ROOT,
+ szServerId);
+
+ dwResult = RegOpenKey(HKEY_LOCAL_MACHINE, szSlapdKey, &hSlapdKey);
+ if(dwResult == ERROR_SUCCESS)
+ {
+ dwResult = RegQueryValueEx(hSlapdKey, VALUE_CONFIG_PATH, NULL,
+ &dwValueType, (LPBYTE)szServerRoot, cbServerRoot);
+ if(dwResult == ERROR_SUCCESS)
+ bReturn = TRUE;
+ RegCloseKey(hSlapdKey);
+ }
+ return(bReturn);
+}
+
+
+
+//--------------------------------------------------------------------------//
+// get server's id based on index value that corresponds to the order in //
+// which it is listed under the the registry \SOFTWARE\Netscape\.. //
+//--------------------------------------------------------------------------//
+BOOL WD_GetServerId(IN DWORD dwSubKey, OUT char *szServerId, IN OUT LPDWORD cbServerId)
+{
+ BOOL bReturn = FALSE;
+ static HANDLE hSlapdKey = 0;
+ DWORD dwResult = ERROR_SUCCESS;
+ FILETIME ftLastWrite;
+ char szSlapdKey[MAX_LINE];
+
+ if(dwSubKey == 0) {
+ sprintf(szSlapdKey, "%s\\%s", KEY_SOFTWARE_NETSCAPE, SVR_KEY_ROOT);
+ dwResult = RegOpenKey(HKEY_LOCAL_MACHINE, szSlapdKey,
+ &hSlapdKey);
+ }
+
+ if(dwResult == ERROR_SUCCESS)
+ {
+ dwResult = RegEnumKeyEx(hSlapdKey, dwSubKey, szServerId,
+ cbServerId, NULL, NULL, NULL, &ftLastWrite);
+ if(dwResult == ERROR_SUCCESS)
+ {
+ bReturn = TRUE;
+ }
+ else
+ {
+ RegCloseKey(hSlapdKey);
+ hSlapdKey = 0;
+ }
+ }
+ return(bReturn);
+}
+
+
+
+//--------------------------------------------------------------------------//
+// //
+//--------------------------------------------------------------------------//
+BOOL WD_IsServiceRunning(char *szServerId)
+{
+ BOOL bReturn = FALSE;
+ SC_HANDLE hscManager;
+ SC_HANDLE hscService;
+ SERVICE_STATUS ssServiceStatus;
+
+ if(hscManager = OpenSCManager(NULL, NULL, GENERIC_READ))
+ {
+ if(hscService = OpenService(hscManager, szServerId, SERVICE_QUERY_STATUS))
+ {
+ if(QueryServiceStatus(hscService, &ssServiceStatus))
+ {
+ if(ssServiceStatus.dwCurrentState != SERVICE_STOPPED)
+ {
+ bReturn = TRUE;
+ }
+ }
+ CloseServiceHandle(hscService);
+ }
+ CloseServiceHandle(hscManager);
+ }
+ return(bReturn);
+}
+
+
+
+
+
+//--------------------------------------------------------------------------//
+// get a list of installed servers //
+//--------------------------------------------------------------------------//
+int WD_GetRunningServerCount(void)
+{
+ int nServerCount = 0;
+ int nEnumIndex = 0;
+ char szServerId[MAX_PATH];
+ DWORD cbServerId = sizeof(szServerId);
+ char szServerRoot[MAX_PATH];
+ DWORD cbServerRoot = sizeof(szServerRoot);
+
+ while(WD_GetServerId(nEnumIndex++, szServerId, &cbServerId))
+ {
+ cbServerId = sizeof(szServerId);
+ // we have an entry that MIGHT be a server, but check to see if it really is one
+ if(WD_GetServerConfig(szServerId, szServerRoot, &cbServerRoot))
+ {
+ if(WD_IsServiceRunning(szServerId))
+ nServerCount++;
+ }
+ cbServerRoot = sizeof(szServerRoot);
+ }
+
+ return(nServerCount);
+}
+
+
+
+//--------------------------------------------------------------------------//
+// //
+//--------------------------------------------------------------------------//
+DWORD WD_GetDefaultKeyValue(char *szServerName, char *szKeyName, DWORD dwDefault)
+{
+ HANDLE hSlapdKey = 0;
+ char szSlapdKey[MAX_LINE];
+ DWORD dwValueType;
+ DWORD dwValue = dwDefault;
+ DWORD cbValue = sizeof(dwValue);
+
+ // query registry key to figure out config directory
+ sprintf(szSlapdKey, "%s\\%s\\%s", KEY_SOFTWARE_NETSCAPE, SVR_KEY_ROOT,
+ szServerName);
+ if(RegOpenKey(HKEY_LOCAL_MACHINE, szSlapdKey, &hSlapdKey) == ERROR_SUCCESS)
+ {
+ RegQueryValueEx(hSlapdKey, szKeyName, NULL, &dwValueType,
+ (LPBYTE)&dwValue, &cbValue);
+ RegCloseKey(hSlapdKey);
+ }
+
+ return(dwValue);
+}
+
+
+//--------------------------------------------------------------------------//
+// figure out if we are running under Windows NT //
+//--------------------------------------------------------------------------//
+BOOL WD_IsWindowsNT(void)
+{
+ BOOL bReturn = FALSE;
+ OSVERSIONINFO osVersionInfo;
+
+ osVersionInfo.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
+ if(GetVersionEx(&osVersionInfo))
+ {
+ bReturn = (osVersionInfo.dwPlatformId == VER_PLATFORM_WIN32_NT);
+ }
+ return(bReturn);
+}
+
+
+
+//--------------------------------------------------------------------------//
+// figure out if we have enough physical memory to operate server //
+//--------------------------------------------------------------------------//
+BOOL WD_IsEnoughResources(void)
+{
+ BOOL bReturn = TRUE;
+ MEMORYSTATUS ms;
+ DWORD dwMinRamFree = 0;
+ DWORD dwMinRamTotal = DEFAULT_MINRAMTOTAL;
+ DWORD dwMinRamPerServer = DEFAULT_MINRAMPERSERVER;
+
+ dwMinRamFree = WD_GetDefaultKeyValue(gszServerName, MINRAMFREE_KEY, DEFAULT_MINRAMFREE);
+ dwMinRamTotal = WD_GetDefaultKeyValue(gszServerName, MINRAMTOTAL_KEY, DEFAULT_MINRAMTOTAL);
+ dwMinRamPerServer = WD_GetDefaultKeyValue(gszServerName, MINRAMPERSERVER_KEY, DEFAULT_MINRAMPERSERVER);
+
+ ZeroMemory((PVOID)&ms, sizeof(ms));
+ GlobalMemoryStatus(&ms);
+
+ if((ms.dwTotalPhys < (dwMinRamTotal * 1024)) || (ms.dwAvailPhys < (dwMinRamFree * 1024)))
+ bReturn = FALSE;
+
+ if(ms.dwTotalPhys < (WD_GetRunningServerCount() * dwMinRamPerServer * 1024))
+ bReturn = FALSE;
+
+ return(bReturn);
+}
+
+
+
+//--------------------------------------------------------------------------//
+// write error to EventLog service //
+//--------------------------------------------------------------------------//
+BOOL WD_SysLog(WORD fwEventType, DWORD IDEvent, char *szData)
+{
+ BOOL bReturn = FALSE;
+ HANDLE hEventSource;
+ WORD fwCategory = 0; // event category
+ PSID pUserSid = NULL; // user security identifier (optional)
+ WORD cStrings = 1; // number of strings to merge with message
+ DWORD cbData = 0; // size of binary data, in bytes
+ LPCTSTR lpszStrings[64]; // array of strings to merge with message
+ LPVOID lpvData = 0; // address of binary data
+
+ hEventSource = RegisterEventSource(NULL, TEXT(EVENTLOG_APPNAME));
+ if( hEventSource != NULL)
+ {
+ lpszStrings[0] = (LPCTSTR)gszServerName;
+ if(szData != NULL)
+ {
+ lpszStrings[1] = (LPCTSTR)szData;
+ cStrings++;
+ }
+
+ bReturn = ReportEvent(hEventSource, fwEventType, fwCategory,
+ IDEvent, pUserSid, cStrings, cbData,
+ lpszStrings, lpvData);
+ DeregisterEventSource(hEventSource);
+ }
+
+ return(bReturn);
+}
+
+
+
+//--------------------------------------------------------------------------//
+// converts '/' chars to '\' //
+//--------------------------------------------------------------------------//
+void WD_UnixToDosPath(char *szText)
+{
+ if(szText)
+ {
+ while(*szText)
+ {
+ if(*szText == '/')
+
+ *szText = '\\';
+ szText++;
+ }
+ }
+}
+
+
+
+//--------------------------------------------------------------------------//
+// calc szServerRoot given szServerConfig, and store szServerRoot in //
+// SLAPD_ROOT environment variable. //
+//--------------------------------------------------------------------------//
+BOOL WD_GetServerRoot(char *szServerRoot, char *szServerConfig)
+{
+ char szTemp[MAX_LINE], szServerRootEnvVar[MAX_LINE];
+ BOOL bReturn = FALSE;
+ char *szChar = NULL;
+
+ strcpy(szTemp, szServerConfig);
+ // szTemp should be something like c:\navgold\server\slapd-kennedy\config
+ if(szChar = strrchr(szTemp,'\\'))
+ {
+ *szChar = 0;
+ // szTemp should be c:\navgold\server\slapd-kennedy
+ if(szChar = strrchr(szTemp, '\\'))
+ {
+ *szChar = 0;
+ // szTemp should be c:\navgold\server
+ strcpy( szServerRoot, szTemp );
+ wsprintf(szServerRootEnvVar, "%s=%s", SLAPD_ROOT, szTemp);
+ putenv(szServerRootEnvVar);
+ bReturn = TRUE;
+ }
+ }
+ return(bReturn);
+}
+
+
+
+//--------------------------------------------------------------------------//
+// calc szServerConfig given szServerName //
+//--------------------------------------------------------------------------//
+BOOL WD_GetConfigFromRegistry(char *szServerConfig, char *szServerName)
+{
+ BOOL bReturn = FALSE;
+ HANDLE hSlapdKey = 0;
+ char szSlapdKey[MAX_LINE];
+ DWORD dwValueType;
+ char szValueString[MAX_LINE];
+ DWORD cbValueString = sizeof(szValueString);
+ DWORD dwResult = 0;
+
+ // query registry key to figure out config directory
+ sprintf(szSlapdKey, "%s\\%s\\%s", KEY_SOFTWARE_NETSCAPE, SVR_KEY_ROOT,
+ szServerName);
+
+ dwResult = RegOpenKey(HKEY_LOCAL_MACHINE, szSlapdKey, &hSlapdKey);
+ if(dwResult != ERROR_SUCCESS)
+ {
+ WD_SysLog(EVENTLOG_ERROR_TYPE, MSG_WD_REGISTRY, szSlapdKey);
+ return(bReturn);
+ }
+
+ dwResult = RegQueryValueEx(hSlapdKey, VALUE_CONFIG_PATH, NULL,
+ &dwValueType, szValueString, &cbValueString);
+ if(dwResult != ERROR_SUCCESS)
+ {
+ WD_SysLog(EVENTLOG_ERROR_TYPE, MSG_WD_REGISTRY, szSlapdKey);
+ }
+ else
+ {
+ strcpy(szServerConfig, szValueString);
+ WD_UnixToDosPath(szServerConfig);
+ WD_GetServerRoot(gszServerRoot, szServerConfig);
+ bReturn = TRUE;
+ }
+ RegCloseKey(hSlapdKey);
+ return(bReturn);
+}
+
+
+//--------------------------------------------------------------------------//
+// calc szServerConfig and szServerName given szCmdLine //
+//--------------------------------------------------------------------------//
+BOOL WD_GetConfigFromCmdline(char *szServerConfig, char *szServerName, char *szCmdLine)
+{
+ BOOL bReturn = FALSE;
+ char *szChar = NULL;
+
+ if(!szCmdLine || !(strcmp(szCmdLine, "")) )
+ {
+ WD_SysLog(EVENTLOG_ERROR_TYPE, MSG_WD_BADCMDLINE, szCmdLine);
+ return(bReturn);
+ }
+
+ strcpy(szServerConfig, szCmdLine);
+ WD_UnixToDosPath(szCmdLine);
+ WD_GetServerRoot(gszServerRoot, szCmdLine);
+
+ // szCmdLine should be something like c:\navgold\server\slapd-kennedy\config
+ if(szChar = strrchr(szCmdLine, '\\'))
+ {
+ *szChar = 0;
+ // szCmdLine should be c:\navgold\server\slapd-kennedy
+ if(szChar = strrchr(szCmdLine, '\\'))
+ {
+ szChar++;
+ // szChar should point to slapd-kennedy
+ strcpy(szServerName, szChar);
+ WD_GetConfigFromRegistry(szServerConfig, szServerName);
+ bReturn = TRUE;
+
+ }
+ }
+ else
+ {
+ // szCmdLine should be something like slapd-kennedy
+ strcpy(szServerName, szCmdLine);
+ bReturn = WD_GetConfigFromRegistry(szServerConfig, szServerName);
+ }
+
+ if(strlen(szServerName) == 0)
+ {
+ WD_SysLog(EVENTLOG_ERROR_TYPE, MSG_WD_BADCMDLINE, szCmdLine);
+ bReturn = FALSE;
+ }
+
+ return(bReturn);
+}
+
+
+
+//--------------------------------------------------------------------------//
+// parse server config file to see if it security is enabled //
+//--------------------------------------------------------------------------//
+BOOL WD_IsServerSecure(void)
+{
+ BOOL bReturn = FALSE;
+ char szFileName[MAX_PATH];
+ char szText[MAX_LINE];
+ char szSeperators[] = " \t\n";
+ char *szTemp;
+ FILE *fh = NULL;
+
+ sprintf(szFileName, "%s\\%s", gszServerConfig, SLAPD_CONF);
+ if(fh = fopen(szFileName, "r"))
+ {
+ while(!feof(fh))
+ {
+ if(fgets(szText, sizeof(szText), fh))
+ {
+ strlwr(szText);
+
+ /* strtok() is not MT safe on Unix , but it is okay to call
+ here because this file is NT only and strtok() is MT safe on NT */
+
+ if(szTemp = strtok(szText, szSeperators))
+ {
+ if(strcmp(szTemp, "security") == 0)
+ {
+ if(szTemp = strtok(NULL, szSeperators))
+ {
+ if(strcmp(szTemp, "on") == 0)
+ bReturn = TRUE;
+ }
+ break;
+ }
+ }
+ }
+ }
+ fclose(fh);
+ }
+
+ return(bReturn);
+}
+
+//--------------------------------------------------------------------------//
+// message proc window for app window //
+//--------------------------------------------------------------------------//
+LONG APIENTRY WD_MainWndProc(HWND hWnd, UINT message, UINT wParam, LONG lParam)
+{
+ switch(message)
+ {
+ case WM_CREATE:
+ break;
+
+ case WM_CLOSE:
+ SetEvent(ghevWatchDogExit);
+ break;
+
+ case WM_COMMAND:
+ {
+ switch(LOWORD(wParam))
+ {
+ case ID_SERVER_SHUTDOWN:
+ {
+ HANDLE hevShutdown = NULL;
+ char szShutdownEvent[MAX_LINE];
+
+ // shutdown web server, it should exit with 0, WatchDog won't restart it
+ sprintf(szShutdownEvent, "NS_%s", gszServerName);
+ hevShutdown = OpenEvent(EVENT_MODIFY_STATE, FALSE, szShutdownEvent);
+ if(hevShutdown)
+ {
+ SetEvent(hevShutdown); // try to exit gracefully
+ CLOSEHANDLE(hevShutdown);
+ }
+ break;
+ }
+
+ case ID_SERVER_RESTART:
+ {
+ // shutdown web server, it should exit with 2, WatchDog will restart it
+ if(ghServerProcess)
+ {
+ CLOSEHANDLE(ghServerProcess);
+ TerminateProcess(ghServerProcess, 2);
+ }
+ break;
+ }
+
+ case ID_SERVER_SUSPEND:
+ {
+ if(ghServerThread0)
+ SuspendThread(ghServerThread0);
+ break;
+ }
+
+ case ID_SERVER_RESUME:
+ {
+ if(ghServerThread0)
+ ResumeThread(ghServerThread0);
+ break;
+ }
+
+ case ID_FILE_EXIT:
+ PostMessage(hWnd, WM_CLOSE, 0, 0);
+ break;
+ }
+ break;
+ }
+
+ default:
+ return(DefWindowProc(hWnd, message, wParam, lParam));
+ }
+ return(0);
+}
+
+
+
+//--------------------------------------------------------------------------//
+// This window serves as an IPC method with the server process. It has //
+// pointers in it's storage area that the server uses to access the SSL //
+// password. Quite strange, but it works perfectly well. //
+//--------------------------------------------------------------------------//
+HWND WD_CreateWindow()
+{
+ HWND hWndMain = NULL;
+ WNDCLASS wc;
+
+ wc.style = 0;
+ wc.lpfnWndProc = (WNDPROC)WD_MainWndProc;
+ wc.cbClsExtra = 0;
+ wc.cbWndExtra = sizeof(LONG) * 4;
+ wc.hIcon = LoadIcon(ghInstance, MAKEINTRESOURCE(IDI_LOGO));
+ wc.hInstance = ghInstance;
+ wc.hCursor = LoadCursor(NULL, IDC_ARROW);
+ wc.hbrBackground = GetStockObject(GRAY_BRUSH);
+ wc.lpszMenuName = MAKEINTRESOURCE(IDR_MENU);
+ wc.lpszClassName = "slapd";
+
+ RegisterClass(&wc); // class may be registered if more than one instance
+
+ hWndMain = CreateWindow(
+ wc.lpszClassName, /* See RegisterClass() call. */
+ gszServerName, /* Text for window title bar. */
+ WS_OVERLAPPEDWINDOW | /* Window style. */
+ WS_POPUP, /* Window style. */
+ CW_USEDEFAULT, /* Default horizontal position. */
+ CW_USEDEFAULT, /* Default vertical position. */
+ 320, /* Default width. */
+ 0, /* Default height. */
+ NULL, /* Overlapped windows have no parent. */
+ NULL, /* Use the window class menu. */
+ ghInstance, /* This instance owns this window. */
+ NULL /* Pointer not needed. */
+ );
+
+ if(hWndMain)
+ {
+#ifdef SHOW_DEBUG_WINDOW
+ ShowWindow(hWndMain, SW_SHOWDEFAULT);
+#else
+ ShowWindow(hWndMain, SW_HIDE);
+#endif
+ UpdateWindow(hWndMain);
+ }
+ return hWndMain;
+}
+
+
+
+//--------------------------------------------------------------------------//
+// //
+//--------------------------------------------------------------------------//
+void WD_WindowThreadProc(LPDWORD lpdwParam)
+{
+ HANDLE hevWindowCreated = (HANDLE)lpdwParam;
+ MSG msg;
+
+ // the ghWndMain global is used all over the place
+ ghWndMain = WD_CreateWindow();
+
+ // inform parent that window creation is complete because it is waiting on us
+ SetEvent(hevWindowCreated);
+
+ if(ghWndMain)
+ {
+ while(GetMessage(&msg, ghWndMain, 0, 0) == TRUE)
+ {
+ TranslateMessage(&msg); // Translates virtual key codes
+ DispatchMessage(&msg); // Dispatches message to window
+ }
+ }
+}
+
+
+
+//--------------------------------------------------------------------------//
+// //
+//--------------------------------------------------------------------------//
+void WD_PasswordThreadProc(LPDWORD lpdwParam)
+{
+ // app window must be created sometime during initialization
+ if(ghWndMain)
+ {
+ ZeroMemory(gszPassword, sizeof(gszPassword));
+ SetWindowLong(ghWndMain, GWL_PASSWORD_ADDR, (LONG)gszPassword);
+ SetWindowLong(ghWndMain, GWL_PASSWORD_LENGTH, (LONG)0);
+ }
+}
+
+
+
+//--------------------------------------------------------------------------//
+// //
+//--------------------------------------------------------------------------//
+BOOL WD_StartServer(PROCESS_INFORMATION *pi)
+{
+ BOOL bReturn = FALSE;
+ char szCmdLine[MAX_LINE];
+ char szServerPath[MAX_PATH];
+ char szInstancePath [MAX_PATH];
+ char *szChar;
+ STARTUPINFO sui;
+ DWORD fdwCreate = DETACHED_PROCESS; /* flags for CreateProcess */
+ int i;
+ char *posfile;
+ UNALIGNED long *posfhnd;
+
+ if(!WD_IsEnoughResources())
+ {
+ WD_SysLog(EVENTLOG_ERROR_TYPE, MSG_WD_STRING, MSG_RESOURCES);
+ gdwServiceError = ERROR_SERVICE_NOT_ACTIVE;
+ return(FALSE);
+ }
+
+ strcpy(szServerPath, gszServerConfig);
+ WD_UnixToDosPath(szServerPath);
+
+ // szServerPath should now be something similar to
+ // c:\navgold\server\slapd-kennedy\config
+ if(szChar = strrchr(szServerPath, '\\'))
+ {
+ *szChar = 0;
+ strcpy (szInstancePath, szServerPath);
+ if(szChar = strrchr(szServerPath, '\\'))
+ {
+ *szChar = 0;
+ }
+ }
+
+ // For Directory Server, service-name is defined as slapd.exe,
+ // in ldapserver/include/nt/regpargms.h
+ sprintf( szCmdLine, "%s\\bin\\%s\\server\\%s -D \"%s\"", szServerPath,
+ PRODUCT_NAME, SERVICE_EXE, szInstancePath );
+ // szCmdLine ex: c:\navgold\server\bin\slapd\slapd.exe
+ // -f c:\navgold\server\slapd-kennedy\config
+
+ memset(&sui,0,sizeof(sui));
+ sui.cb = sizeof(STARTUPINFO);
+
+ /* All of this, to CreateProcess(), allows us to run a console
+ app (slapd.exe) from the service (ns-slapd.exe), without a
+ new console being opened for the app.
+ See dospawn.c in the crt src for more details.
+ */
+ sui.cbReserved2 = (WORD)(sizeof( int ) + (3 *
+ (sizeof( char ) + sizeof( long ))));
+
+ sui.lpReserved2 = calloc( sui.cbReserved2, 1 );
+
+ *((UNALIGNED int *)(sui.lpReserved2)) = 3;
+
+ posfile = (char *)(sui.lpReserved2 + sizeof( int ));
+
+ posfhnd = (UNALIGNED long *)(sui.lpReserved2 + sizeof( int ) +
+ (3 * sizeof( char )));
+
+ for ( i = 0,
+ posfile = (char *)(sui.lpReserved2 + sizeof( int )),
+ posfhnd = (UNALIGNED long *)(sui.lpReserved2 + sizeof( int )
+ + (3 * sizeof( char ))) ;
+ i < 3 ;
+ i++, posfile++, posfhnd++ )
+ {
+ *posfile = 0;
+ *posfhnd = (long)INVALID_HANDLE_VALUE;
+ }
+
+ fdwCreate |= CREATE_SUSPENDED;
+ bReturn = CreateProcess(NULL, szCmdLine, NULL, NULL,
+ TRUE, fdwCreate, NULL, NULL, &sui, pi );
+ if(bReturn)
+ {
+ ghServerProcess = pi->hProcess; // used by app window
+ ghServerThread0 = pi->hThread; // used by app window
+ if(DuplicateHandle(GetCurrentProcess(), GetCurrentProcess(),
+ pi->hProcess, (LPHANDLE)&ghDuplicateProcess,
+ (DWORD)PROCESS_VM_READ | PROCESS_VM_WRITE |
+ PROCESS_ALL_ACCESS, FALSE, (DWORD)0))
+ {
+ SetWindowLong(ghWndMain, GWL_PROCESS_HANDLE,
+ (LONG)ghDuplicateProcess);
+ }
+ ResumeThread(pi->hThread);
+ }
+ else
+ {
+ WD_SysLog(EVENTLOG_ERROR_TYPE, MSG_WD_STARTFAILED, szCmdLine);
+ }
+
+ free( sui.lpReserved2 );
+
+ return(bReturn);
+}
+
+
+
+
+//------------------------------------------------------z--------------------//
+// //
+//--------------------------------------------------------------------------//
+BOOL WD_CreatePasswordThread(void)
+{
+ #define NUM_WAIT_OBJECTS 2
+ enum { CHILD_PROCESS, EXIT_EVENT };
+
+ BOOL bReturn = FALSE;
+ HANDLE lphObject[NUM_WAIT_OBJECTS];
+ HANDLE hPasswordThread;
+ DWORD dwThreadID;
+ DWORD dwResult;
+
+ lphObject[EXIT_EVENT] = ghevWatchDogExit;
+
+ hPasswordThread = CreateThread((LPSECURITY_ATTRIBUTES)NULL, 0,
+ (LPTHREAD_START_ROUTINE)WD_PasswordThreadProc, NULL, 0, &dwThreadID);
+ if(hPasswordThread)
+ {
+ lphObject[CHILD_PROCESS] = hPasswordThread;
+ dwResult = WaitForMultipleObjects(NUM_WAIT_OBJECTS, lphObject, FALSE, INFINITE);
+ CLOSEHANDLE(hPasswordThread);
+ if(dwResult == WAIT_OBJECT_0 + EXIT_EVENT) // user stopped service
+ {
+ EndDialog(ghdlgPassword, 1);
+ }
+ bReturn = TRUE;
+ }
+ else
+ {
+ WD_SysLog(EVENTLOG_ERROR_TYPE, MSG_WD_BADPASSWORD, NULL);
+ }
+ return(bReturn);
+}
+
+
+//--------------------------------------------------------------------------//
+// //
+//--------------------------------------------------------------------------//
+BOOL WD_CreateWindowThread(void)
+{
+ BOOL bReturn = FALSE;
+ DWORD dwThreadID;
+ HANDLE hWindowThread;
+ HANDLE hevWindowCreated = NULL;
+
+ if(hevWindowCreated = CreateEvent(NULL, FALSE, FALSE, NULL))
+ {
+ if(hWindowThread = CreateThread((LPSECURITY_ATTRIBUTES)NULL, 0,
+ (LPTHREAD_START_ROUTINE)WD_WindowThreadProc, (LPVOID)hevWindowCreated, 0, &dwThreadID))
+ {
+ // make sure ghHwndMain is created otherwise
+ // SetWindowLong(ghWndMain) will fail in other threads
+ WaitForSingleObject(hevWindowCreated, INFINITE);
+ CLOSEHANDLE(hWindowThread);
+ bReturn = TRUE;
+ }
+ }
+ return(bReturn);
+}
+
+
+
+//--------------------------------------------------------------------------//
+// //
+//--------------------------------------------------------------------------//
+BOOL WD_CreateCronThread(HANDLE hevWatchDogExit)
+{
+ BOOL bReturn = FALSE;
+ DWORD dwThreadID = 0;
+ HANDLE hWindowThread = NULL;
+#if 0
+ if(hWindowThread = CreateThread((LPSECURITY_ATTRIBUTES)NULL, 0,
+ (LPTHREAD_START_ROUTINE)CRON_ThreadProc, (LPVOID)hevWatchDogExit, 0, &dwThreadID))
+ {
+ CLOSEHANDLE(hWindowThread);
+ bReturn = TRUE;
+ }
+#endif
+ return(bReturn);
+}
+
+
+//--------------------------------------------------------------------------//
+// signals event create by SNMP agent for notification of server shutdown //
+//--------------------------------------------------------------------------//
+#if 0
+BOOL WS_SendSNMPTrapSignal(void)
+{
+ BOOL bReturn = FALSE;
+ HANDLE hevShutdown = NULL;
+ char szShutdownEvent[MAX_LINE];
+
+ sprintf(szShutdownEvent, NSEV_SNMPTRAP_HTTP);
+ hevShutdown = OpenEvent(EVENT_MODIFY_STATE, FALSE, szShutdownEvent);
+ if(hevShutdown)
+ {
+ SetEvent(hevShutdown);
+ CLOSEHANDLE(hevShutdown);
+ bReturn = TRUE;
+ }
+ return(bReturn);
+}
+#endif
+
+
+//--------------------------------------------------------------------------//
+// //
+//--------------------------------------------------------------------------//
+BOOL WD_MonitorServer(void)
+{
+ #define NUM_WAIT_OBJECTS 2
+ enum { SERVER_PROCESS, WATCHDOG_EXIT };
+
+ BOOL bReturn = FALSE;
+ HANDLE lphObject[NUM_WAIT_OBJECTS];
+ DWORD dwResult = 0;
+ DWORD dwExitCode = 0;
+ PROCESS_INFORMATION pi;
+ HANDLE hevServerDone = NULL;
+ char szServerDoneEvent[MAX_LINE];
+ char szText[MAX_LINE];
+ DWORD dwTickCount = 0;
+
+ lphObject[WATCHDOG_EXIT] = ghevWatchDogExit;
+
+ while(WD_StartServer(&pi))
+ {
+ dwTickCount = GetTickCount();
+ lphObject[SERVER_PROCESS] = pi.hProcess;
+ dwResult = WaitForMultipleObjects(NUM_WAIT_OBJECTS, lphObject, FALSE, INFINITE);
+
+ //WS_SendSNMPTrapSignal();
+
+ if(dwResult == WAIT_OBJECT_0 + WATCHDOG_EXIT)
+ {
+ // shutdown web server
+ //CLOSEHANDLE(pi.hProcess); // XXXahakim close them after TerminateProcess()
+ //CLOSEHANDLE(pi.hThread);
+ sprintf(szServerDoneEvent, "NS_%s", gszServerName);
+ hevServerDone = OpenEvent(EVENT_MODIFY_STATE, FALSE, szServerDoneEvent);
+ if(hevServerDone)
+ {
+ SetEvent(hevServerDone); // try to exit gracefully
+ CLOSEHANDLE(hevServerDone);
+ WaitForSingleObject(lphObject[SERVER_PROCESS], 1000 * DEFAULT_KILL_TIME);
+ }
+ // but just in case it's still alive, swat it again, harder!
+ TerminateProcess(lphObject[SERVER_PROCESS], 1);
+ CLOSEHANDLE(pi.hProcess); // XXXahakim moved from above 03/06/96
+ CLOSEHANDLE(pi.hThread);
+ bReturn = TRUE;
+ }
+ else
+ if(dwResult == WAIT_OBJECT_0 + SERVER_PROCESS)
+ {
+ // why did web server shutdown?
+ // GetExitCodeProcess(lphObject[SERVER_PROCESS], &dwExitCode);
+ // if(dwExitCode != 0)
+ // checking the exit code is bogus because a crashed process can return
+ // anything, including 0, so we use another method to determine if the
+ // server shutdown legitimately, which is similar to how unix works
+ // according to robm.
+
+ // check to see if a specified amount of time has elapsed since the server
+ // started. If it's "infant mortality" don't bother restarting it
+ // because chances are it will continue to fail (such as when the password
+ // is bad, or if there is some other severe startup problem)
+ if(GetTickCount() - dwTickCount > 1000 * WD_GetDefaultKeyValue(gszServerName, MORTALITY_KEY, DEFAULT_MORTALITY_TIME))
+ {
+ sprintf(szText, "%d", dwExitCode);
+ WD_SysLog(EVENTLOG_ERROR_TYPE, MSG_WD_RESTART, szText);
+ CLOSEHANDLE(pi.hProcess);
+ CLOSEHANDLE(pi.hThread);
+ CLOSEHANDLE(ghDuplicateProcess);
+ Sleep(DEFAULT_RESTART_TIME * 1000);
+ continue;
+ }
+ // server closed legitimately
+ else
+ bReturn = TRUE;
+ }
+ CLOSEHANDLE(pi.hProcess);
+ CLOSEHANDLE(pi.hThread);
+ CLOSEHANDLE(ghDuplicateProcess);
+ break;
+ }
+
+ return(bReturn);
+}
+
+
+
+//--------------------------------------------------------------------------//
+// //
+//--------------------------------------------------------------------------//
+BOOL WD_SetServiceStatus(DWORD dwCurrentState, DWORD dwError)
+{
+ BOOL bReturn = FALSE;
+ SERVICE_STATUS ssStatus;
+
+ if(gsshServiceStatus)
+ {
+ gdwLastStatus = dwCurrentState;
+ ssStatus.dwServiceType = SERVICE_WIN32_OWN_PROCESS;
+ ssStatus.dwCurrentState = dwCurrentState;
+ ssStatus.dwControlsAccepted = SERVICE_ACCEPT_STOP |
+ SERVICE_ACCEPT_PAUSE_CONTINUE |
+ SERVICE_ACCEPT_SHUTDOWN;
+ ssStatus.dwWin32ExitCode = dwError;
+ ssStatus.dwServiceSpecificExitCode = (NO_ERROR ? 0 : 1);
+ ssStatus.dwCheckPoint = 0;
+ ssStatus.dwWaitHint = (1000 * ((dwCurrentState==SERVICE_STOP_PENDING)?600:DEFAULT_KILL_TIME + 1));
+ bReturn = SetServiceStatus(gsshServiceStatus, &ssStatus);
+ }
+ return(FALSE);
+}
+
+
+
+//--------------------------------------------------------------------------//
+// //
+//--------------------------------------------------------------------------//
+VOID WINAPI WD_ServiceHandler(DWORD fdwControl)
+{
+ switch(fdwControl)
+ {
+ case SERVICE_CONTROL_STOP:
+ case SERVICE_CONTROL_SHUTDOWN:
+ WD_SetServiceStatus(SERVICE_STOP_PENDING, gdwServiceError);
+ SetEvent(ghevWatchDogExit);
+ return;
+
+ case SERVICE_CONTROL_PAUSE:
+ if(ghServerThread0)
+ {
+ WD_SetServiceStatus(SERVICE_PAUSE_PENDING, gdwServiceError);
+ SuspendThread(ghServerThread0);
+ WD_SetServiceStatus(SERVICE_PAUSED, gdwServiceError);
+ return;
+ }
+ break;
+
+ case SERVICE_CONTROL_CONTINUE:
+ if(ghServerThread0)
+ {
+ WD_SetServiceStatus(SERVICE_CONTINUE_PENDING, gdwServiceError);
+ ResumeThread(ghServerThread0);
+ WD_SetServiceStatus(SERVICE_RUNNING, gdwServiceError);
+ return;
+ }
+ break;
+
+ case SERVICE_CONTROL_INTERROGATE:
+ WD_SetServiceStatus(gdwLastStatus, gdwServiceError);
+ return;
+
+ default:
+ WD_SysLog(EVENTLOG_ERROR_TYPE, MSG_WD_RESTART, "unknown service event");
+ return;
+ }
+ WD_SetServiceStatus(SERVICE_RUNNING, gdwServiceError);
+}
+
+
+
+//--------------------------------------------------------------------------//
+// //
+//--------------------------------------------------------------------------//
+VOID WD_ServiceMain(DWORD dwArgc, LPTSTR *lpszArgv)
+{
+ BOOL bOkToProceed = TRUE;
+ // if SCM calls us lpszArgv will not be NULL
+ BOOL bIsService = (lpszArgv != NULL);
+
+ // register our custom control handler to handle shutdown
+ ghWdogProcess = GetCurrentProcess();
+ SetConsoleCtrlHandler(WD_ControlHandler, TRUE);
+
+ if(bIsService)
+ {
+ gsshServiceStatus = RegisterServiceCtrlHandler(lpszArgv[0],
+ (LPHANDLER_FUNCTION)WD_ServiceHandler);
+ bOkToProceed = (gsshServiceStatus != (SERVICE_STATUS_HANDLE)NULL);
+ if(bOkToProceed)
+ {
+ strcpy(gszServerName, lpszArgv[0]);
+ bOkToProceed = WD_GetConfigFromRegistry(gszServerConfig,
+ gszServerName);
+ }
+ }
+
+ WD_SetServiceStatus(SERVICE_START_PENDING, gdwServiceError);
+
+ if(bOkToProceed)
+ {
+ if(ghevWatchDogExit = CreateEvent(NULL, TRUE, FALSE, gszServerName))
+ {
+ WD_SetServiceStatus(SERVICE_RUNNING, gdwServiceError);
+ WD_CreateWindowThread();
+#if 0
+ WD_CreateCronThread(ghevWatchDogExit);
+#endif
+
+ if(WD_IsServerSecure())
+ {
+ bOkToProceed = WD_CreatePasswordThread();
+ }
+
+ if(bOkToProceed)
+ {
+ WD_MonitorServer();
+ }
+ CLOSEHANDLE(ghevWatchDogExit);
+ }
+ }
+ WD_SetServiceStatus(SERVICE_STOPPED, gdwServiceError);
+}
+
+
+
+//--------------------------------------------------------------------------//
+// //
+//--------------------------------------------------------------------------//
+WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine,
+ int nCmdShow)
+{
+ SERVICE_TABLE_ENTRY steServiceTable[2];
+
+#ifdef PUMPKIN_HOUR
+ if(time(NULL) > (PUMPKIN_HOUR - 10))
+ {
+ char szMessage[256];
+ sprintf( szMessage, " ** This beta software has expired **\n");
+ MessageBox(GetDesktopWindow(), szMessage,
+ DS_NAME_FULL_VERSION, MB_ICONEXCLAMATION | MB_OK);
+ exit(1);
+ }
+#endif
+
+ if(!hPrevInstance) // other instances of app running?
+ {
+ ghInstance = hInstance;
+ memset(gszPassword, 0, sizeof(gszPassword));
+ memset(gszServerConfig, 0, sizeof(gszServerConfig));
+ memset(gszServerName, 0, sizeof(gszServerName));
+ if(WD_IsWindowsNT() && (lpCmdLine) && (strlen(lpCmdLine) == 0))
+ {
+ // run as service
+ steServiceTable[0].lpServiceName = TEXT(PRODUCT_NAME);
+ steServiceTable[0].lpServiceProc =
+ (LPSERVICE_MAIN_FUNCTION)WD_ServiceMain;
+ steServiceTable[1].lpServiceName = NULL;
+ steServiceTable[1].lpServiceProc = NULL;
+ StartServiceCtrlDispatcher(steServiceTable);
+ }
+ else
+ {
+ // run as application
+ if(WD_GetConfigFromCmdline(gszServerConfig,
+ gszServerName, lpCmdLine))
+ {
+ WD_ServiceMain(0, (LPTSTR *)NULL);
+ }
+ }
+ }
+ return(FALSE);
+}
diff --git a/ldap/servers/slapd/object.c b/ldap/servers/slapd/object.c
new file mode 100644
index 00000000..7fb90164
--- /dev/null
+++ b/ldap/servers/slapd/object.c
@@ -0,0 +1,95 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+
+/* Implementation note:
+ PR_AtomicIncrement and PR_AtomicDecrement both return a value whose
+ sign is the same sign (or zero) as the variable *after* it was updated.
+ They do not return the previous value.
+*/
+
+#include "slapi-plugin.h"
+#include "slapi-private.h"
+
+typedef struct object
+{
+ PRInt32 refcnt; /* reference count for the object */
+ FNFree destructor; /* Destructor for the object */
+ void *data; /* pointer to actual node data */
+} object;
+
+
+/*
+ * Create a new object.
+ * The object is created with refcnt set to 1. The caller implicitly gets
+ * a reference to the object, to prevent a race condition where the object
+ * is destroyed immediately after contruction.
+ * The provided destructor function will be called when all references to
+ * the object have been released.
+ *
+ * Returns a pointer to the new object.
+ */
+Object *
+object_new(void *user_data, FNFree destructor)
+{
+ Object *o;
+ o = (object *)slapi_ch_malloc(sizeof(object));
+ o->refcnt = 1;
+ o->destructor = destructor;
+ o->data = user_data;
+ return o;
+}
+
+
+/*
+ * Acquire a reference object. The caller must hold a reference
+ * to the object, or know for certain that a reference is held
+ * and will continue to be held while this call is in progress.
+ */
+void
+object_acquire(Object *o)
+{
+ PR_ASSERT(NULL != o);
+ PR_AtomicIncrement(&o->refcnt);
+}
+
+
+/*
+ * Release a reference to an object. The pointer to the object
+ * should not be referenced after this call is made, since the
+ * object may be destroyed if this is the last reference to it.
+ */
+void
+object_release(Object *o)
+{
+ PRInt32 refcnt_after_release;
+
+ PR_ASSERT(NULL != o);
+ refcnt_after_release = PR_AtomicDecrement(&o->refcnt);
+ PR_ASSERT(refcnt_after_release >= 0);
+ if (refcnt_after_release == 0)
+ {
+ /* Object can be destroyed */
+ if (o->destructor)
+ o->destructor(&o->data);
+ /* Make it harder to reuse a dangling pointer */
+ o->data = NULL;
+ o->destructor = NULL;
+ o->refcnt = -9999;
+ slapi_ch_free((void **)&o);
+ }
+}
+
+/*
+ * Get the data pointer from an object.
+ * Results are undefined if the caller does not hold a reference
+ * to the object.
+ */
+void *
+object_get_data(Object *o)
+{
+ PR_ASSERT(NULL != o);
+ return o->data;
+}
diff --git a/ldap/servers/slapd/objset.c b/ldap/servers/slapd/objset.c
new file mode 100644
index 00000000..ceb5f225
--- /dev/null
+++ b/ldap/servers/slapd/objset.c
@@ -0,0 +1,380 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+
+#include "slapi-plugin.h"
+#include "slapi-private.h"
+
+/*
+ * The "wrapper" placed around objects in our object set.
+ */
+typedef struct objset_object
+{
+ Object *obj; /* pointer to actual object */
+ struct objset_object *next; /* pointer to next object in list */
+} objset_object;
+
+/*
+ * The object set itself.
+ */
+typedef struct objset
+{
+ objset_object *head; /* pointer to linked list of objects */
+ objset_object *tail; /* pointer to tail of linked list */
+ PRLock *lock; /* Lock - protects addition/deletion from list */
+ FNFree destructor; /* Destructor callback for objset itself */
+} objset;
+
+/* Forward declarations */
+static void unlinkObjsetObjectNoLock(Objset *o, objset_object *obj_to_unlink);
+
+
+/*
+ * Create a new, empty object set.
+ * Returns a pointer to the new object set, or NULL if an error occurred.
+ */
+Objset *
+objset_new(FNFree objset_destructor)
+{
+ objset *set;
+
+ set = (objset *)slapi_ch_malloc(sizeof(objset));
+ set->lock = PR_NewLock();
+ if (NULL == set->lock)
+ {
+ slapi_ch_free((void **)&set);
+ }
+ else
+ {
+ set->head = set->tail = NULL;
+ set->destructor = objset_destructor;
+ }
+ return set;
+}
+
+
+/*
+ * Delete an object set. All references to contained objects
+ * are released, and the objset is deleted.
+ */
+void
+objset_delete(Objset **setp)
+{
+ objset_object *o, *o_next;
+ Objset *set;
+
+ PR_ASSERT(NULL != setp);
+ set = *setp;
+ PR_ASSERT(NULL != set);
+ PR_Lock(set->lock);
+ o = set->head;
+ while (NULL != o)
+ {
+ o_next = o->next;
+ object_release(o->obj); /* release our reference */
+ slapi_ch_free((void **)&o); /* Free wrapper */
+ o = o_next;
+ }
+ PR_Unlock(set->lock);
+ PR_DestroyLock(set->lock);
+ if (NULL != set->destructor)
+ {
+ set->destructor((void **)setp);
+ }
+ slapi_ch_free((void **)setp);
+}
+
+
+
+/*
+ * Add a new object to an object set.
+ * Return values:
+ * OBJSET_SUCCESS: the insertion was succesful.
+ * OBJSET_ALREADY_EXISTS: the object already exists in the set.
+ */
+int
+objset_add_obj(Objset *set, Object *object)
+{
+ objset_object *p;
+ int exists = 0;
+ int rc = OBJSET_SUCCESS;
+
+ PR_ASSERT(NULL != set);
+ PR_ASSERT(NULL != object);
+
+ PR_Lock(set->lock);
+ /* Make sure this object isn't already in the set */
+ p = set->head;
+ while (NULL != p)
+ {
+ if (p->obj == object)
+ {
+ exists = 1;
+ break;
+ }
+ p = p->next;
+ }
+ if (exists)
+ {
+ rc = OBJSET_ALREADY_EXISTS;
+ }
+ else
+ {
+ objset_object *new_node = (objset_object *)slapi_ch_malloc(sizeof(objset_object));
+ object_acquire(object); /* Record our reference */
+ new_node->obj = object;
+ new_node->next = NULL;
+
+ if (NULL == set->head)
+ {
+ set->head = set->tail = new_node;
+ }
+ else
+ {
+ set->tail->next = new_node;
+ set->tail = new_node;
+ }
+ }
+ PR_Unlock(set->lock);
+ return rc;
+}
+
+
+/*
+ * Locate an object in an object set.
+ *
+ * Arguments:
+ * set: the object set to search
+ * compare_fn: a caller-provided function used to compare the
+ * name against an object. This function should return
+ * a negtive value, zero, or a positive value if the
+ * object's name is, respectively, less that, equal
+ * to, or greater than the provided name.
+ * name: the name (value) to find.
+ *
+ * The returned object, if any, is referenced. The caller must
+ * call object_release() when finished with the object.
+ *
+ * Returns the object, if found. Otherwise, returns NULL.
+ * Implementation note: since we store objects in an unordered
+ * linked list, all that's really important is that the compare_fn
+ * return 0 when a match is found, and non-zero otherwise.
+ * Other types of implementations might try to optimize the
+ * storage, e.g. binary search.
+ */
+Object *
+objset_find(Objset *set, CMPFn compare_fn, const void *name)
+{
+ objset_object *found = NULL;
+
+ PR_ASSERT(NULL != set);
+ PR_ASSERT(NULL != name);
+ PR_ASSERT(NULL != compare_fn);
+
+ PR_Lock(set->lock);
+ found = set->head;
+ while (NULL != found)
+ {
+ if (compare_fn(found->obj, name) == 0)
+ {
+ break;
+ }
+ found = found->next;
+ }
+ if (NULL != found)
+ {
+ /* acquire object */
+ object_acquire(found->obj);
+ }
+ PR_Unlock(set->lock);
+ return found == NULL ? NULL : found->obj;
+}
+
+
+
+
+/*
+ * Remove an object from an objset.
+ * Returns OBJSET_SUCCESS if the object was found and removed, or
+ * OBJSET_NO_SUCH_OBJECT if the object was not found in the list.
+ */
+int
+objset_remove_obj(Objset *set, Object *object)
+{
+ int rc = OBJSET_SUCCESS;
+ objset_object *found;
+
+ PR_ASSERT(NULL != set);
+ PR_ASSERT(NULL != object);
+
+ PR_Lock(set->lock);
+ found = set->head;
+ while (NULL != found)
+ {
+ if (found->obj == object)
+ {
+ break;
+ }
+ found = found->next;
+ }
+ if (NULL == found)
+ {
+ rc = OBJSET_NO_SUCH_OBJECT;
+ }
+ else
+ {
+ Object *saved = found->obj;
+
+ /* Unlink from list */
+ unlinkObjsetObjectNoLock(set, found);
+
+ /* Release reference on object */
+ object_release(saved);
+ }
+ PR_Unlock(set->lock);
+ return rc;
+}
+
+
+
+/*
+ * Prepare for iteration across an object set. Returns the first
+ * object in the set. The returned object is referenced, therefore
+ * the caller must either release the object, or
+ * pass it to an objset_next_obj call, which will
+ * implicitly release the object.
+ * Returns the first object, or NULL if the objset contains no
+ * objects.
+ */
+Object *
+objset_first_obj(Objset *set)
+{
+ Object *return_object;
+
+ /* Be tolerant (for the replication plugin) */
+ if (set == NULL) return NULL;
+
+ PR_Lock(set->lock);
+ if (NULL == set->head)
+ {
+ return_object = NULL;
+ }
+ else
+ {
+ object_acquire(set->head->obj);
+ return_object = set->head->obj;
+ }
+ PR_Unlock(set->lock);
+ return return_object;
+}
+
+
+/*
+ * Return the next object in an object set, or NULL if there are
+ * no more objects.
+ * The returned object is referenced, therefore
+ * the caller must either release the object, or
+ * pass it to an objset_next_ob call, which will
+ * implicitly release the object.
+ */
+Object *
+objset_next_obj(Objset *set, Object *previous)
+{
+ Object *return_object = NULL;
+ objset_object *p;
+
+ PR_ASSERT(NULL != set);
+ PR_Lock(set->lock);
+
+ /* First, find the current object */
+ p = set->head;
+ while (NULL != p && p->obj != previous)
+ {
+ p = p->next;
+ }
+ /* Find the next object */
+ if (NULL != p && NULL != p->next)
+ {
+ return_object = p->next->obj;
+ object_acquire(return_object);
+ }
+ PR_Unlock(set->lock);
+ object_release(previous); /* Release the previous object */
+ return return_object;
+}
+
+
+
+/*
+ * Return a non-zero value if the object set is empty, or
+ * zero if the object set is empty.
+ */
+int
+objset_is_empty(Objset *set)
+{
+ int return_value;
+
+ PR_ASSERT(NULL != set);
+
+ PR_Lock(set->lock);
+ return_value = (set->head == NULL);
+ PR_Unlock(set->lock);
+ return return_value;
+}
+
+
+
+/*
+ * Return the count of objects in the object set.
+ */
+int objset_size(Objset *set)
+{
+ int count = 0;
+ objset_object *p;
+
+ PR_ASSERT(NULL != set);
+ PR_Lock(set->lock);
+ for (p = set->head; p; p = p->next)
+ count++;
+ PR_Unlock(set->lock);
+ return count;
+}
+
+
+
+/*
+ * Utility function: remove object from list.
+ * This needs to be called with the objset locked.
+ */
+static void
+unlinkObjsetObjectNoLock(Objset *o, objset_object *obj_to_unlink)
+{
+
+ objset_object *p = o->head;
+
+ PR_ASSERT(NULL != o->head);
+ /* Unlink from list */
+ if (o->head == obj_to_unlink) {
+ /* Object to unlink was at head of list */
+ p = o->head->next;
+ o->head = obj_to_unlink->next;
+ } else {
+ while (NULL != p->next && p->next != obj_to_unlink) {
+ p = p->next;
+ }
+ if (NULL != p->next)
+ {
+ /* p points to object prior to one being removed */
+ p->next = p->next->next;
+ }
+ }
+ if (o->tail == obj_to_unlink)
+ {
+ o->tail = p;
+ }
+
+ /* Free the wrapper */
+ slapi_ch_free((void **)&obj_to_unlink);
+}
diff --git a/ldap/servers/slapd/operation.c b/ldap/servers/slapd/operation.c
new file mode 100644
index 00000000..a049e382
--- /dev/null
+++ b/ldap/servers/slapd/operation.c
@@ -0,0 +1,410 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+/* operation.c - routines to deal with pending ldap operations */
+
+#include <stdio.h>
+#include <string.h>
+#include <sys/types.h>
+#ifndef _WIN32
+#include <sys/socket.h>
+#endif
+#include "slap.h"
+
+int
+slapi_op_abandoned( Slapi_PBlock *pb )
+{
+ int op_status;
+
+ op_status = pb->pb_op->o_status;
+
+ return( op_status == SLAPI_OP_STATUS_ABANDONED );
+}
+
+void
+operation_out_of_disk_space()
+{
+ LDAPDebug(LDAP_DEBUG_ANY, "*** DISK FULL ***\n", 0, 0, 0);
+ LDAPDebug(LDAP_DEBUG_ANY, "Attempting to shut down gracefully.\n", 0, 0, 0);
+ g_set_shutdown( SLAPI_SHUTDOWN_DISKFULL );
+}
+
+/* Setting the flags on the operation allows more control to the plugin
+ * to disable and enable checks
+ * Flags that we support for setting in the operation from the plugin are
+ * SLAPI_OP_FLAG_NO_ACCESS_CHECK - do not check for access control
+ * This function can be extended to support other flag setting as well
+ */
+
+void slapi_operation_set_flag(Slapi_Operation *op, unsigned long flag)
+{
+
+ operation_set_flag(op, flag);
+
+}
+
+void slapi_operation_clear_flag(Slapi_Operation *op, unsigned long flag)
+{
+ operation_clear_flag(op, flag);
+
+}
+
+int slapi_operation_is_flag_set(Slapi_Operation *op, unsigned long flag)
+{
+ return operation_is_flag_set(op, flag);
+}
+
+
+
+static int operation_type = -1; /* The type number assigned by the Factory for 'Operation' */
+
+int
+get_operation_object_type()
+{
+ if(operation_type==-1)
+ {
+ /* The factory is given the name of the object type, in
+ * return for a type handle. Whenever the object is created
+ * or destroyed the factory is called with the handle so
+ * that it may call the constructors or destructors registered
+ * with it.
+ */
+ operation_type= factory_register_type(SLAPI_EXT_OPERATION,offsetof(Operation,o_extension));
+ }
+ return operation_type;
+}
+
+/*
+ * Allocate a new Slapi_Operation.
+ * The flag parameter indicates whether the the operation is
+ * external (from an LDAP Client), or internal (from a plugin).
+ */
+Slapi_Operation *
+operation_new(int flags)
+{
+ /* To improve performance, we allocate the Operation, BerElement and
+ * ber buffer in one block, instead of a separate malloc() for each.
+ * Subsequently, ber_special_free() frees them all; we're careful
+ * not to free the Operation separately, and the ber software knows
+ * not to free the buffer separately.
+ */
+ BerElement *ber = NULL;
+ Slapi_Operation *o;
+ if(flags & OP_FLAG_INTERNAL)
+ {
+ o = (Slapi_Operation *) slapi_ch_malloc(sizeof(Slapi_Operation));
+ }
+ else
+ {
+ o= (Slapi_Operation *) ber_special_alloc( sizeof(Slapi_Operation), &ber );
+ }
+ if (NULL != o)
+ {
+ memset(o,0,sizeof(Slapi_Operation));
+ o->o_ber = ber;
+ o->o_msgid = -1;
+ o->o_tag = LBER_DEFAULT;
+ o->o_status = SLAPI_OP_STATUS_PROCESSING;
+ slapi_sdn_init(&(o->o_sdn));
+ o->o_authtype = NULL;
+ o->o_isroot = 0;
+ o->o_time = current_time();
+ o->o_opid = 0;
+ o->o_connid = 0;
+ o->o_next = NULL;
+ o->o_flags= flags;
+ if ( config_get_accesslog_level() & LDAP_DEBUG_TIMING ) {
+ o->o_interval = PR_IntervalNow();
+ } else {
+ o->o_interval = (PRIntervalTime)0;
+ }
+ }
+ return o;
+}
+
+void
+operation_free( Slapi_Operation **op, Connection *conn )
+{
+ if(op!=NULL && *op!=NULL)
+ {
+ /* Call the plugin extension destructors */
+ factory_destroy_extension(get_operation_object_type(),*op,conn,&((*op)->o_extension));
+ slapi_sdn_done(&(*op)->o_sdn);
+ slapi_sdn_free(&(*op)->o_target_spec);
+ free( (*op)->o_authtype );
+ if ( (*op)->o_searchattrs != NULL ) {
+ cool_charray_free( (*op)->o_searchattrs );
+ }
+ if ( NULL != (*op)->o_params.request_controls ) {
+ ldap_controls_free( (*op)->o_params.request_controls );
+ }
+ if ( NULL != (*op)->o_results.result_controls ) {
+ ldap_controls_free( (*op)->o_results.result_controls );
+ }
+ if(operation_is_flag_set(*op, OP_FLAG_INTERNAL))
+ {
+ slapi_ch_free((void**)op);
+ }
+ else
+ {
+ ber_special_free( *op , (*op)->o_ber);
+ }
+ /* counters_to_errors_log("after operation"); */
+ }
+}
+
+void
+slapi_operation_set_csngen_handler ( Slapi_Operation *op, void *callback )
+{
+ op->o_csngen_handler = (csngen_handler) callback;
+}
+
+void
+slapi_operation_set_replica_attr_handler ( Slapi_Operation *op, void *callback )
+{
+ op->o_replica_attr_handler = (replica_attr_handler) callback;
+}
+
+int
+slapi_operation_get_replica_attr ( Slapi_PBlock *pb, Slapi_Operation *op, const char *type, void *value )
+{
+ int rc = -1;
+
+ if (op->o_replica_attr_handler)
+ {
+ rc = op->o_replica_attr_handler ( pb, type, value );
+ }
+
+ return rc;
+}
+
+CSN *
+operation_get_csn(Slapi_Operation *op)
+{
+ return op->o_params.csn;
+}
+
+void
+operation_set_csn(Slapi_Operation *op,CSN *csn)
+{
+ op->o_params.csn= csn;
+}
+
+unsigned long
+slapi_op_get_type(Slapi_Operation *op)
+{
+ return op->o_params.operation_type;
+}
+
+/* DEPRECATED : USE FUNCTION ABOVE FOR NEW DVLPT */
+unsigned long
+operation_get_type(Slapi_Operation *op)
+{
+ return op->o_params.operation_type;
+}
+
+void
+operation_set_type(Slapi_Operation *op, unsigned long type)
+{
+ op->o_params.operation_type= type;
+}
+
+void
+operation_set_flag(Slapi_Operation *op, int flag)
+{
+ op->o_flags|= flag;
+}
+
+void
+operation_clear_flag(Slapi_Operation *op, int flag)
+{
+ op->o_flags &= ~flag;
+}
+
+int
+operation_is_flag_set(Slapi_Operation *op, int flag)
+{
+ return op->o_flags & flag;
+}
+
+Slapi_DN*
+operation_get_target_spec (Slapi_Operation *op)
+{
+ return op->o_target_spec;
+}
+
+void
+operation_set_target_spec (Slapi_Operation *op, const Slapi_DN *target_spec)
+{
+ PR_ASSERT (op);
+ PR_ASSERT (target_spec);
+
+ op->o_target_spec = slapi_sdn_dup(target_spec);
+}
+
+void
+operation_set_target_spec_str (Slapi_Operation *op, const char *target_spec)
+{
+ PR_ASSERT (op);
+
+ op->o_target_spec = slapi_sdn_new_dn_byval (target_spec);
+}
+
+unsigned long operation_get_abandoned_op (const Slapi_Operation *op)
+{
+ PR_ASSERT (op);
+
+ return op->o_abandoned_op;
+}
+
+void operation_set_abandoned_op (Slapi_Operation *op, unsigned long abandoned_op)
+{
+ PR_ASSERT (op);
+
+ op->o_abandoned_op = abandoned_op;
+}
+
+/* slapi_operation_parameters manipulation functions */
+
+struct slapi_operation_parameters *operation_parameters_new()
+{
+ return (slapi_operation_parameters *)slapi_ch_calloc (1, sizeof (slapi_operation_parameters));
+}
+
+static LDAPMod **
+copy_mods(LDAPMod **orig_mods)
+{
+ LDAPMod **new_mods = NULL;
+ LDAPMod *mod;
+ Slapi_Mods smods_old;
+ Slapi_Mods smods_new;
+ slapi_mods_init_byref(&smods_old,orig_mods);
+ slapi_mods_init_passin(&smods_new,new_mods);
+ mod= slapi_mods_get_first_mod(&smods_old);
+ while(mod!=NULL)
+ {
+ slapi_mods_add_modbvps(&smods_new,mod->mod_op,mod->mod_type,mod->mod_bvalues);
+ mod= slapi_mods_get_next_mod(&smods_old);
+ }
+ new_mods= slapi_mods_get_ldapmods_passout(&smods_new);
+ slapi_mods_done(&smods_old);
+ slapi_mods_done(&smods_new);
+ return new_mods;
+}
+
+struct slapi_operation_parameters *
+operation_parameters_dup(struct slapi_operation_parameters *sop)
+{
+ struct slapi_operation_parameters *sop_new= (struct slapi_operation_parameters *)
+ slapi_ch_malloc(sizeof(struct slapi_operation_parameters));
+ memcpy(sop_new,sop,sizeof(struct slapi_operation_parameters));
+ if(sop->target_address.dn!=NULL)
+ {
+ sop_new->target_address.dn= slapi_ch_strdup(sop->target_address.dn);
+ }
+ if(sop->target_address.uniqueid!=NULL)
+ {
+ sop_new->target_address.uniqueid= slapi_ch_strdup(sop->target_address.uniqueid);
+ }
+
+ sop_new->csn= csn_dup(sop->csn);
+ switch(sop->operation_type)
+ {
+ case SLAPI_OPERATION_ADD:
+ sop_new->p.p_add.target_entry= slapi_entry_dup(sop->p.p_add.target_entry);
+ sop_new->p.p_add.parentuniqueid = slapi_ch_strdup(sop->p.p_add.parentuniqueid);
+ break;
+ case SLAPI_OPERATION_MODIFY:
+ sop_new->p.p_modify.modify_mods= NULL;
+ if (sop->p.p_modify.modify_mods!=NULL)
+ {
+ sop_new->p.p_modify.modify_mods = copy_mods(sop->p.p_modify.modify_mods);
+ }
+ break;
+ case SLAPI_OPERATION_MODRDN:
+ if(sop->p.p_modrdn.modrdn_newrdn!=NULL)
+ {
+ sop_new->p.p_modrdn.modrdn_newrdn= slapi_ch_strdup(sop->p.p_modrdn.modrdn_newrdn);
+ }
+ if(sop->p.p_modrdn.modrdn_newsuperior_address.dn!=NULL)
+ {
+ sop_new->p.p_modrdn.modrdn_newsuperior_address.dn =
+ slapi_ch_strdup(sop->p.p_modrdn.modrdn_newsuperior_address.dn);
+ }
+ if(sop->p.p_modrdn.modrdn_newsuperior_address.uniqueid!=NULL)
+ {
+ sop_new->p.p_modrdn.modrdn_newsuperior_address.uniqueid =
+ slapi_ch_strdup(sop->p.p_modrdn.modrdn_newsuperior_address.uniqueid);
+ }
+ sop_new->p.p_modrdn.modrdn_mods= NULL;
+ if (sop->p.p_modrdn.modrdn_mods!=NULL)
+ {
+ sop_new->p.p_modrdn.modrdn_mods = copy_mods(sop->p.p_modrdn.modrdn_mods);
+ }
+ break;
+ case SLAPI_OPERATION_DELETE:
+ /* Has no extra parameters. */
+ case SLAPI_OPERATION_BIND:
+ case SLAPI_OPERATION_COMPARE:
+ case SLAPI_OPERATION_SEARCH:
+ default:
+ /* We are not interested in these. */
+ break;
+ }
+ return sop_new;
+}
+
+void
+operation_parameters_done (struct slapi_operation_parameters *sop)
+{
+ if(sop!=NULL)
+ {
+ slapi_ch_free((void **)&sop->target_address.dn);
+ slapi_ch_free((void **)&sop->target_address.uniqueid);
+ csn_free(&sop->csn);
+
+ switch(sop->operation_type)
+ {
+ case SLAPI_OPERATION_ADD:
+ slapi_entry_free(sop->p.p_add.target_entry);
+ sop->p.p_add.target_entry= NULL;
+ slapi_ch_free((void **)&(sop->p.p_add.parentuniqueid));
+ break;
+ case SLAPI_OPERATION_MODIFY:
+ ldap_mods_free(sop->p.p_modify.modify_mods, 1 /* Free the Array and the Elements */);
+ sop->p.p_modify.modify_mods= NULL;
+ break;
+ case SLAPI_OPERATION_MODRDN:
+ slapi_ch_free((void **)&(sop->p.p_modrdn.modrdn_newrdn));
+ slapi_ch_free((void **)&(sop->p.p_modrdn.modrdn_newsuperior_address.dn));
+ slapi_ch_free((void **)&(sop->p.p_modrdn.modrdn_newsuperior_address.uniqueid));
+ ldap_mods_free(sop->p.p_modrdn.modrdn_mods, 1 /* Free the Array and the Elements */);
+ sop->p.p_modrdn.modrdn_mods= NULL;
+ break;
+ case SLAPI_OPERATION_DELETE:
+ /* Has no extra parameters. */
+ case SLAPI_OPERATION_BIND:
+ case SLAPI_OPERATION_COMPARE:
+ case SLAPI_OPERATION_SEARCH:
+ default:
+ /* We are not interested in these */
+ break;
+ }
+ }
+}
+
+void operation_parameters_free(struct slapi_operation_parameters **sop)
+{
+ if (sop)
+ {
+ operation_parameters_done (*sop);
+ slapi_ch_free ((void**)sop);
+ }
+}
+
+
+
+
+
diff --git a/ldap/servers/slapd/opshared.c b/ldap/servers/slapd/opshared.c
new file mode 100644
index 00000000..e265d420
--- /dev/null
+++ b/ldap/servers/slapd/opshared.c
@@ -0,0 +1,1163 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+/* opshared.c - functions shared between regular and internal operations */
+
+#include "slap.h"
+#include "index_subsys.h"
+
+/* helper functions */
+static void compute_limits (Slapi_PBlock *pb);
+
+/* attributes that no clients are allowed to add or modify */
+static char *protected_attrs_all [] = { PSEUDO_ATTR_UNHASHEDUSERPASSWORD,
+ NULL
+ };
+static char *pwpolicy_lock_attrs_all [] = { "passwordRetryCount",
+ "retryCountResetTime",
+ "accountUnlockTime",
+ NULL};
+/* Forward declarations */
+static void compute_limits (Slapi_PBlock *pb);
+static int send_results (Slapi_PBlock *pb, int send_result, int *nentries);
+static int process_entry(Slapi_PBlock *pb, Slapi_Entry *e, int send_result);
+
+int op_shared_is_allowed_attr (const char *attr_name, int replicated_op)
+{
+ int i;
+ slapdFrontendConfig_t *slapdFrontendConfig = getFrontendConfig();
+
+ /* check list of attributes that no client is allowed to specify */
+ for (i = 0; protected_attrs_all[i]; i ++)
+ {
+ if (strcasecmp (attr_name, protected_attrs_all[i]) == 0)
+ {
+ /* this attribute is not allowed */
+ return 0;
+ }
+ }
+
+ /* ONREPL - should allow backends to plugin here to specify
+ attributes that are not allowed */
+
+ if (!replicated_op)
+ {
+ /*
+ * check to see if attribute is marked as one clients can't modify
+ */
+ struct asyntaxinfo *asi;
+ int no_user_mod = 0;
+
+ asi = attr_syntax_get_by_name( attr_name );
+ if ( NULL != asi &&
+ 0 != ( asi->asi_flags & SLAPI_ATTR_FLAG_NOUSERMOD ))
+ {
+ /* this attribute is not allowed */
+ no_user_mod = 1;
+ }
+ attr_syntax_return( asi );
+
+ if ( no_user_mod ) {
+ return( 0 );
+ }
+ } else if (!slapdFrontendConfig->pw_is_global_policy) {
+ /* check list of password policy attributes for locking accounts */
+ for (i = 0; pwpolicy_lock_attrs_all[i]; i ++)
+ {
+ if (strcasecmp (attr_name, pwpolicy_lock_attrs_all[i]) == 0)
+ {
+ /* this attribute is not allowed */
+ return 0;
+ }
+ }
+ }
+
+ /* this attribute is ok */
+ return 1;
+}
+
+
+static ps_service_fn_ptr ps_service_fn = NULL;
+
+void
+do_ps_service(Slapi_Entry *e, Slapi_Entry *eprev, int chgtype, int chgnum)
+{
+ if (NULL == ps_service_fn) {
+ if (get_entry_point(ENTRY_POINT_PS_SERVICE, (caddr_t *)(&ps_service_fn)) < 0) {
+ return;
+ }
+ }
+ (ps_service_fn)(e, eprev, chgtype, chgnum);
+}
+
+void modify_update_last_modified_attr(Slapi_PBlock *pb, Slapi_Mods *smods)
+{
+ char buf[20];
+ struct berval bv;
+ struct berval *bvals[2];
+ time_t curtime;
+ struct tm utm;
+ Operation *op;
+
+ LDAPDebug(LDAP_DEBUG_TRACE, "modify_update_last_modified_attr\n", 0, 0, 0);
+
+ slapi_pblock_get(pb, SLAPI_OPERATION, &op);
+
+ bvals[0] = &bv;
+ bvals[1] = NULL;
+
+ /* fill in modifiersname */
+ if (slapi_sdn_isempty(&op->o_sdn)) {
+ bv.bv_val = "";
+ bv.bv_len = strlen(bv.bv_val);
+ } else {
+ bv.bv_val = (char*)slapi_sdn_get_dn(&op->o_sdn);
+ bv.bv_len = strlen(bv.bv_val);
+ }
+
+ slapi_mods_add_modbvps(smods, LDAP_MOD_REPLACE | LDAP_MOD_BVALUES,
+ "modifiersname", bvals);
+
+ /* fill in modifytimestamp */
+ curtime = current_time();
+#ifdef _WIN32
+{
+ struct tm *pt;
+ pt = gmtime(&curtime);
+ memcpy(&utm, pt, sizeof(struct tm));
+}
+#else
+ gmtime_r(&curtime, &utm);
+#endif
+ strftime(buf, sizeof(buf), "%Y%m%d%H%M%SZ", &utm);
+ bv.bv_val = buf;
+ bv.bv_len = strlen(bv.bv_val);
+ slapi_mods_add_modbvps(smods, LDAP_MOD_REPLACE | LDAP_MOD_BVALUES,
+ "modifytimestamp", bvals);
+}
+
+/*
+ * Returns: 0 - if the operation is successful
+ * < 0 - if operation fails.
+ * Note that an operation is considered "failed" if a result is sent
+ * directly to the client when send_result is 0.
+ */
+void
+op_shared_search (Slapi_PBlock *pb, int send_result)
+{
+ char *base, *fstr;
+ int scope;
+ Slapi_Backend *be = NULL;
+ Slapi_Backend *be_single = NULL;
+ Slapi_Backend *be_list[BE_LIST_SIZE];
+ Slapi_Entry *referral_list[BE_LIST_SIZE];
+ char ebuf[ BUFSIZ ];
+ char attrlistbuf[ 1024 ], *attrliststr, **attrs = NULL;
+ int rc = 0;
+ int internal_op;
+ Slapi_DN sdn;
+ Slapi_Operation *operation;
+ Slapi_Entry *referral = NULL;
+
+ char errorbuf[BUFSIZ];
+ int nentries,pnentries;
+ int flag_search_base_found = 0;
+ int flag_no_such_object = 0;
+ int flag_referral = 0;
+ int flag_psearch = 0;
+ int err_code = LDAP_SUCCESS;
+ LDAPControl **ctrlp;
+ struct berval *ctl_value = NULL;
+ int iscritical = 0;
+ char * be_name = NULL;
+ int index = 0;
+
+ be_list[0] = NULL;
+ referral_list[0] = NULL;
+
+ /* get search parameters */
+ slapi_pblock_get(pb, SLAPI_SEARCH_TARGET, &base);
+ slapi_pblock_get(pb, SLAPI_SEARCH_SCOPE, &scope);
+ slapi_pblock_get(pb, SLAPI_SEARCH_STRFILTER, &fstr);
+ slapi_pblock_get(pb, SLAPI_SEARCH_ATTRS, &attrs);
+ slapi_pblock_get (pb, SLAPI_OPERATION, &operation);
+ internal_op= operation_is_flag_set(operation, OP_FLAG_INTERNAL);
+ flag_psearch = operation_is_flag_set(operation, OP_FLAG_PS);
+
+ slapi_sdn_init_dn_byref(&sdn, base);
+
+ if (operation_is_flag_set(operation,OP_FLAG_ACTION_LOG_ACCESS))
+ {
+ char *fmtstr;
+
+#define SLAPD_SEARCH_FMTSTR_BASE "conn=%d op=%d SRCH base=\"%s\" scope=%d "
+#define SLAPD_SEARCH_FMTSTR_BASE_INT "conn=%s op=%d SRCH base=\"%s\" scope=%d "
+#define SLAPD_SEARCH_FMTSTR_REMAINDER " attrs=%s%s\n"
+
+ if ( strlen(fstr) > 1024 )
+ {
+ /*
+ * slapi_log_access() throws away log lines that are longer than
+ * 2048 characters, so we limit the filter string to 1024 (better
+ * to log something rather than nothing)
+ */
+ if ( !internal_op )
+ {
+ fmtstr = SLAPD_SEARCH_FMTSTR_BASE "filter=\"%.1024s...\"" SLAPD_SEARCH_FMTSTR_REMAINDER;
+ }
+ else
+ {
+ fmtstr = SLAPD_SEARCH_FMTSTR_BASE_INT "filter=\"%.1024s...\"" SLAPD_SEARCH_FMTSTR_REMAINDER;
+ }
+ } else {
+ if ( !internal_op )
+ {
+ fmtstr = SLAPD_SEARCH_FMTSTR_BASE "filter=\"%s\"" SLAPD_SEARCH_FMTSTR_REMAINDER;
+ }
+ else
+ {
+ fmtstr = SLAPD_SEARCH_FMTSTR_BASE_INT "filter=\"%s\"" SLAPD_SEARCH_FMTSTR_REMAINDER;
+ }
+ }
+
+ if ( NULL == attrs ) {
+ attrliststr = "ALL";
+ } else {
+ strarray2str( attrs, attrlistbuf, sizeof( attrlistbuf ),
+ 1 /* include quotes */ );
+ attrliststr = attrlistbuf;
+ }
+
+ if ( !internal_op )
+ {
+ slapi_log_access(LDAP_DEBUG_STATS, fmtstr,
+ pb->pb_conn->c_connid,
+ pb->pb_op->o_opid,
+ escape_string(slapi_sdn_get_dn (&sdn), ebuf),
+ scope, fstr, attrliststr,
+ flag_psearch ? " options=persistent" : "");
+ }
+ else
+ {
+ slapi_log_access(LDAP_DEBUG_ARGS, fmtstr,
+ LOG_INTERNAL_OP_CON_ID,
+ LOG_INTERNAL_OP_OP_ID,
+ escape_string(slapi_sdn_get_dn (&sdn), ebuf),
+ scope, fstr, attrliststr,
+ flag_psearch ? " options=persistent" : "");
+ }
+ }
+
+ slapi_pblock_set(pb, SLAPI_SEARCH_TARGET, (void*)slapi_sdn_get_ndn (&sdn));
+
+ /* target spec is used to decide which plugins are applicable for the operation */
+ operation_set_target_spec (pb->pb_op, &sdn);
+
+ /* this is time to check if mapping tree specific control
+ * was used to specify that we want to parse only
+ * one backend
+ */
+ slapi_pblock_get(pb, SLAPI_REQCONTROLS, &ctrlp);
+ if (NULL != ctrlp)
+ {
+ if (slapi_control_present(ctrlp, MTN_CONTROL_USE_ONE_BACKEND_EXT_OID,
+ &ctl_value, &iscritical))
+ {
+ /* this control is the smart version of MTN_CONTROL_USE_ONE_BACKEND_OID,
+ * it works out for itself what back end is required (thereby relieving
+ * the client of working out which backend it needs) by looking at the
+ * base of the search if no value is supplied
+ */
+
+ if((ctl_value->bv_len != 0) && ctl_value->bv_val)
+ {
+ be_name = ctl_value->bv_val;
+ }
+ else
+ {
+ /* we don't need no steenkin values */
+ Slapi_Backend *searchbe = slapi_be_select( &sdn );
+
+ if(searchbe && searchbe != defbackend_get_backend())
+ {
+ be_name = slapi_be_get_name(searchbe);
+ }
+ }
+ }
+ else
+ {
+ if (slapi_control_present(ctrlp, MTN_CONTROL_USE_ONE_BACKEND_OID,
+ &ctl_value, &iscritical))
+ {
+ if ((ctl_value->bv_len == 0) || (ctl_value->bv_val == NULL))
+ {
+ rc = -1;
+ send_ldap_result(pb, LDAP_PROTOCOL_ERROR, NULL, NULL, 0, NULL);
+ goto free_and_return_nolock;
+ }
+ else
+ {
+ be_name = ctl_value->bv_val;
+ if (be_name == NULL)
+ {
+ rc = -1;
+ send_ldap_result(pb, LDAP_PROTOCOL_ERROR, NULL, NULL, 0, NULL);
+ goto free_and_return_nolock;
+ }
+ }
+ }
+ }
+
+ if ( slapi_control_present (ctrlp, LDAP_CONTROL_GET_EFFECTIVE_RIGHTS,
+ &ctl_value, &iscritical) )
+ {
+ operation->o_flags |= OP_FLAG_GET_EFFECTIVE_RIGHTS;
+ }
+ }
+
+ if (be_name == NULL)
+ {
+ /* no specific backend was requested, use the mapping tree
+ */
+ err_code = slapi_mapping_tree_select_all(pb, be_list, referral_list, errorbuf);
+ if (((err_code != LDAP_SUCCESS) && (err_code != LDAP_OPERATIONS_ERROR) && (err_code != LDAP_REFERRAL))
+ || ((err_code == LDAP_OPERATIONS_ERROR) && ((be_list == NULL) || be_list[0] == NULL)))
+
+ {
+ send_ldap_result(pb, err_code, NULL, errorbuf, 0, NULL);
+ rc = -1;
+ goto free_and_return;
+ }
+ if (be_list[0] != NULL)
+ {
+ index = 0;
+ while (be_list[index] && be_list[index+1])
+ index++;
+ be = be_list[index];
+ }
+ else
+ be = NULL;
+ }
+ else
+ {
+ /* specific backend be_name was requested, use slapi_be_select_by_instance_name
+ */
+ be_single = be = slapi_be_select_by_instance_name(be_name);
+ if (be_single)
+ slapi_be_Rlock(be_single);
+ be_list[0] = NULL;
+ referral_list[0] = NULL;
+ referral = NULL;
+ }
+
+ slapi_pblock_set(pb, SLAPI_BACKEND_COUNT, &index);
+
+ if (be)
+ {
+ slapi_pblock_set(pb, SLAPI_BACKEND, be);
+
+ /* adjust time and size limits */
+ compute_limits (pb);
+
+ /* call the pre-search plugins. if they succeed, call the backend
+ search function. then call the post-search plugins. */
+
+ /* ONREPL - should regular plugin be called for internal searches ? */
+ if (plugin_call_plugins(pb, SLAPI_PLUGIN_PRE_SEARCH_FN) == 0)
+ {
+ slapi_pblock_set(pb, SLAPI_PLUGIN, be->be_database);
+ set_db_default_result_handlers(pb);
+
+ /* Now would be a good time to call the search rewriters for computed attrs */
+ rc = compute_rewrite_search_filter(pb);
+ switch (rc)
+ {
+ case 1: /* A rewriter says that we should refuse to perform this search.
+ The potential exists that we will refuse to perform a search
+ which we were going to refer, perhaps to a server which would
+ be willing to perform the search. That's bad. The rewriter
+ could be clever enough to spot this and do the right thing though. */
+ send_ldap_result(pb, LDAP_UNWILLING_TO_PERFORM, NULL, "Search not supported", 0, NULL);
+ rc = -1;
+ goto free_and_return;
+
+ case -2: /* memory was allocated */
+ /* take note of any changes */
+ slapi_pblock_get(pb, SLAPI_SEARCH_TARGET, &base);
+ slapi_pblock_get(pb, SLAPI_SEARCH_SCOPE, &scope);
+
+ slapi_sdn_set_dn_byref(&sdn, base);
+ break;
+
+ case -1:
+ case 0: /* OK */
+ break;
+
+ case 2: /* Operations error */
+ send_ldap_result(pb, LDAP_OPERATIONS_ERROR, NULL, "search rewriter", 0, NULL);
+ rc = -1;
+ goto free_and_return;
+ }
+
+ } else {
+ /*
+ * A pre-operation plugin handled this search. Grab the return code
+ * (it may have been set by a plugin) and return.
+ *
+ * In DS 5.x, the following two lines of code did not exist, which
+ * means a pre-search function might return a non-zero value (which
+ * indicates that a result was returned to the client) but the code
+ * below would execute the search anyway. This was a regression from
+ * the documented plugin API behavior (and from DS 3.x and 4.x).
+ */
+ slapi_pblock_get(pb, SLAPI_PLUGIN_OPRETURN, &rc);
+ goto free_and_return;
+ }
+ }
+
+ /* PAR: now filters have been rewritten, we can assign plugins to work on them */
+ index_subsys_assign_filter_decoders(pb);
+
+ nentries = 0;
+ rc = -1; /* zero backends would mean failure */
+ while (be)
+ {
+ const Slapi_DN * be_suffix;
+
+ if (be->be_search == NULL)
+ {
+ send_ldap_result(pb, LDAP_UNWILLING_TO_PERFORM, NULL, "Function not implemented", 0, NULL);
+ rc = -1;
+ goto free_and_return;
+ }
+
+ pnentries = 0;
+
+ /* the backends returns no such object when a
+ * search is attempted in a node above their nsslapd-suffix
+ * this is correct but a bit annoying when a backends
+ * is below another backend because in that case the
+ * such searches should sometimes succeed
+ * To allow this we therefore have to change the
+ * SLAPI_SEARCH_TARGET parameter in the pblock
+ *
+ * Also when we climb down the mapping tree we have to
+ * change ONE-LEVEL searches to BASE
+ */
+
+ /* that's mean we only support one suffix per backend */
+ be_suffix = slapi_be_getsuffix(be, 0);
+
+ /* be_suffix null means that we are searching the default backend
+ * -> don't change the search parameters in pblock
+ */
+ if (be_suffix != NULL)
+ {
+ if ((be_name == NULL) && (scope == LDAP_SCOPE_ONELEVEL))
+ {
+ /* one level searches
+ * - depending on the suffix of the backend we might have to
+ * do a one level search or a base search
+ * - we might also have to change the search target
+ */
+ if (slapi_sdn_isparent(&sdn, be_suffix)
+ || (slapi_sdn_get_ndn_len(&sdn) == 0))
+ {
+ int tmp_scope = LDAP_SCOPE_BASE;
+ slapi_pblock_set(pb, SLAPI_SEARCH_SCOPE, &tmp_scope);
+ slapi_pblock_set(pb, SLAPI_SEARCH_TARGET,
+ (void *)slapi_sdn_get_ndn(be_suffix));
+ }
+ else if (slapi_sdn_issuffix(&sdn, be_suffix))
+ {
+ int tmp_scope = LDAP_SCOPE_ONELEVEL;
+ slapi_pblock_set(pb, SLAPI_SEARCH_SCOPE, &tmp_scope);
+ slapi_pblock_set(pb, SLAPI_SEARCH_TARGET,
+ (void *)slapi_sdn_get_ndn (&sdn));
+ }
+ else
+ goto next_be;
+ }
+
+ /* subtree searches :
+ * if the search was started above the backend suffix
+ * - temporarily set the SLAPI_SEARCH_TARGET to the
+ * base of the node so that we don't get a NO SUCH OBJECT error
+ * - do not change the scope
+ */
+ if (scope == LDAP_SCOPE_SUBTREE)
+ {
+ if (slapi_sdn_issuffix(be_suffix, &sdn))
+ {
+ slapi_pblock_set(pb, SLAPI_SEARCH_TARGET,
+ (void *)slapi_sdn_get_ndn(be_suffix));
+ }
+ else
+ slapi_pblock_set(pb, SLAPI_SEARCH_TARGET, (void *)slapi_sdn_get_ndn(&sdn));
+ }
+ }
+
+ slapi_pblock_set(pb, SLAPI_BACKEND, be);
+ slapi_pblock_set(pb, SLAPI_PLUGIN, be->be_database);
+ slapi_pblock_set(pb, SLAPI_SEARCH_RESULT_SET, NULL);
+
+ /* ONREPL - we need to be able to tell the backend not to send results directly */
+ rc = (*be->be_search)(pb);
+ switch (rc)
+ {
+ int err;
+ case 1: /* backend successfully sent result to the client */
+ rc = SLAPI_FAIL_GENERAL;
+ /* fall through */
+
+ case -1: /* an error occurred */
+ slapi_pblock_get(pb, SLAPI_RESULT_CODE, &err);
+ if (err == LDAP_NO_SUCH_OBJECT)
+ {
+ /* may be the object exist somewhere else
+ * wait the end of the loop to send back this error
+ */
+ flag_no_such_object = 1;
+ break;
+ }
+ else
+ {
+ /* for error other than LDAP_NO_SUCH_OBJECT
+ * the error has already been sent
+ * stop the search here
+ */
+ goto free_and_return;
+ }
+
+ /* when rc == SLAPI_FAIL_DISKFULL this case is executed */
+
+ case SLAPI_FAIL_DISKFULL:
+ operation_out_of_disk_space();
+ goto free_and_return;
+
+ case 0: /* search was successful and we need to send the result */
+ flag_search_base_found++;
+ rc = send_results (pb, 1, &pnentries);
+
+ /* if rc != 0 an error occurred while sending back the entries
+ * to the LDAP client
+ * LDAP error should already have been sent to the client
+ * stop the search, free and return
+ */
+ if (rc != 0)
+ goto free_and_return;
+ break;
+ }
+
+ nentries += pnentries;
+
+ next_be:
+ if (be_list[0] == NULL)
+ {
+ be = NULL;
+ }
+ else
+ {
+ index--;
+ if (index>=0)
+ be = be_list[index];
+ else
+ be = NULL;
+ }
+ }
+
+ /* if referrals were sent back by the mapping tree
+ * add them to the list of referral in the pblock instead
+ * of searching the backend
+ */
+ index = 0;
+ while ((referral = referral_list[index++]) != NULL)
+ {
+ slapi_pblock_set(pb, SLAPI_BACKEND, NULL);
+ if (err_code == LDAP_REFERRAL)
+ {
+ send_referrals_from_entry(pb,referral);
+ goto free_and_return;
+ }
+ else
+ {
+ if (process_entry(pb, referral, 1))
+ {
+ flag_referral++;
+ }
+ else
+ {
+ /* Manage DSA was set, referral must be sent as an entry */
+ int attrsonly;
+ char **attrs = NULL;
+
+ slapi_pblock_get(pb, SLAPI_SEARCH_ATTRS, &attrs);
+ slapi_pblock_get(pb, SLAPI_SEARCH_ATTRSONLY, &attrsonly);
+ slapi_pblock_set(pb, SLAPI_SEARCH_RESULT_ENTRY, referral);
+ switch (send_ldap_search_entry(pb, referral, NULL, attrs, attrsonly))
+ {
+ case 0:
+ flag_search_base_found++;
+ nentries++;
+ break;
+ case 1: /* entry not sent */
+ case -1: /* connection closed */
+ break;
+ }
+ }
+ }
+ }
+
+ if (flag_search_base_found || flag_referral) {
+ rc = 0;
+ }
+
+ /* ONREPL - we currently call postop only if operation is successful;
+ We should always send result and pass error code to the plugin */
+ if (rc == 0) {
+ plugin_call_plugins(pb, SLAPI_PLUGIN_POST_SEARCH_FN);
+ }
+ else
+ {
+ plugin_call_plugins(pb, SLAPI_PLUGIN_POST_SEARCH_FAIL_FN);
+ }
+
+ if (send_result) {
+ if (rc == 0)
+ {
+ /* at least one backend returned something and there was no critical error
+ * from the LDAP client point of view the search was successful
+ */
+ struct berval **urls = NULL;
+
+ slapi_pblock_get(pb, SLAPI_SEARCH_REFERRALS, &urls);
+ send_ldap_result(pb, err_code, NULL, NULL, nentries, urls);
+ }
+ else if (flag_no_such_object)
+ {
+ /* there was at least 1 backend that was called to process
+ * the operation and all backends returned NO SUCH OBJECTS
+ */
+ slapi_send_ldap_result_from_pb(pb);
+ }
+ else
+ {
+ /* No backend was found in the mapping tree to process
+ * the operation : return NO SUCH OBJECT
+ */
+ send_ldap_result(pb, LDAP_NO_SUCH_OBJECT, NULL, NULL, 0, NULL);
+ }
+ } else {
+ /* persistent search: ignore error locating base entry */
+ rc = 0;
+ }
+
+ free_and_return:
+ if ((be_list[0] != NULL) || (referral_list[0] != NULL))
+ slapi_mapping_tree_free_all(be_list, referral_list);
+ else if (be_single)
+ slapi_be_Unlock(be_single);
+
+ free_and_return_nolock:
+ slapi_pblock_set(pb, SLAPI_SEARCH_TARGET, base);
+ slapi_pblock_set(pb, SLAPI_PLUGIN_OPRETURN, &rc);
+ index_subsys_filter_decoders_done(pb);
+ slapi_sdn_done(&sdn);
+}
+
+/* Returns 1 if this processing on this entry is finished
+ * and doesn't need to be sent.
+ */
+static int
+process_entry(Slapi_PBlock *pb, Slapi_Entry *e, int send_result)
+{
+ int managedsait;
+ Slapi_Attr *a=NULL;
+ int numValues=0, i;
+
+ if (!send_result)
+ {
+ /* server requested that we don't send results to the client,
+ for instance, in case of a persistent search
+ */
+ return 1;
+ }
+
+ /* ONREPL - check if the entry should be referred (because of the copyingFrom) */
+
+ /*
+ * If this is a referral, and the managedsait control is not present,
+ * arrange for a referral to be sent. For v2 connections,
+ * the referrals are just squirreled away and sent with the
+ * final result. For v3, the referrals are sent in separate LDAP messages.
+ */
+ slapi_pblock_get(pb, SLAPI_MANAGEDSAIT, &managedsait);
+ if (!managedsait && slapi_entry_attr_find(e, "ref", &a)== 0)
+ {
+ /* to fix 522189: when rootDSE, don't interpret attribute ref as a referral entry */
+
+ if ( slapi_is_rootdse(slapi_entry_get_dn_const(e)) )
+ return 0; /* more to do for this entry, e.g., send it back to the client */
+
+ /* end fix */
+ slapi_attr_get_numvalues(a, &numValues );
+ if (numValues == 0)
+ {
+ char ebuf[ BUFSIZ ];
+ LDAPDebug(LDAP_DEBUG_ANY, "null ref in (%s)\n",
+ escape_string(slapi_entry_get_dn_const(e), ebuf), 0, 0);
+ }
+ else
+ {
+ Slapi_Value *val=NULL;
+ struct berval **refscopy=NULL;
+ struct berval **urls, **tmpUrls=NULL;
+ tmpUrls=(struct berval **) slapi_ch_malloc((numValues + 1) * sizeof(struct berval*));
+ for ( i = slapi_attr_first_value(a, &val); i != -1;
+ i = slapi_attr_next_value(a, i, &val)) {
+ tmpUrls[i]=(struct berval*)slapi_value_get_berval(val);
+ }
+ tmpUrls[numValues]=NULL;
+ refscopy = ref_adjust(pb, tmpUrls, slapi_entry_get_sdn_const(e), 1);
+ slapi_pblock_get(pb, SLAPI_SEARCH_REFERRALS, &urls);
+ send_ldap_referral(pb, e, refscopy, &urls);
+ slapi_pblock_set(pb, SLAPI_SEARCH_REFERRALS, urls);
+ if (NULL != refscopy)
+ {
+ ber_bvecfree(refscopy);
+ refscopy = NULL;
+ }
+ if( NULL != tmpUrls) {
+ slapi_ch_free( (void **)&tmpUrls );
+ }
+ }
+
+ return 1; /* done with this entry */
+ }
+
+ return 0;
+}
+
+
+/* Loops through search entries and sends them to the client.
+ * returns -1 on error, 0 if result packet was sent or 1 if
+ * result packet wasn't sent
+ */
+static int
+iterate_with_lookahead(Slapi_PBlock *pb, Slapi_Backend *be, int send_result, int *pnentries)
+{
+ int rc;
+ int attrsonly;
+ int done = 0;
+ Slapi_Entry *e;
+ void *backend_info_ptr;
+ Slapi_Entry *next_e;
+ void *next_backend_info_ptr;
+ char **attrs = NULL;
+ int send_result_status = 0;
+
+ slapi_pblock_get(pb, SLAPI_SEARCH_ATTRS, &attrs);
+ slapi_pblock_get(pb, SLAPI_SEARCH_ATTRSONLY, &attrsonly);
+
+ /* setup for the loop */
+ rc = be->be_next_search_entry_ext(pb, 1);
+ if (rc < 0)
+ {
+ /*
+ * Some exceptional condition occurred. Results
+ * have been sent, so we're finished.
+ */
+ if (rc == SLAPI_FAIL_DISKFULL)
+ {
+ operation_out_of_disk_space();
+ }
+ return -1;
+ }
+
+ slapi_pblock_get(pb, SLAPI_SEARCH_RESULT_ENTRY, &next_e);
+ slapi_pblock_get(pb, SLAPI_SEARCH_RESULT_ENTRY_EXT, &next_backend_info_ptr);
+ if (NULL == next_e)
+ {
+ /* no entries */
+ done = 1;
+ }
+
+ backend_info_ptr = NULL;
+
+ /* Done setting up the loop, now here it comes */
+
+ while (!done)
+ {
+ /* Allow the backend to free the entry we just finished using */
+ /* It is ok to call this when backend_info_ptr is NULL */
+ be->be_entry_release(pb, backend_info_ptr);
+ e = next_e;
+ backend_info_ptr = next_backend_info_ptr;
+
+ rc = be->be_next_search_entry_ext(pb, 1);
+ if (rc < 0)
+ {
+ /*
+ * Some exceptional condition occurred. Results
+ * have been sent, so we're finished.
+ */
+ if (rc == SLAPI_FAIL_DISKFULL)
+ {
+ operation_out_of_disk_space();
+ }
+ return -1;
+ }
+ else
+ {
+ slapi_pblock_get(pb, SLAPI_SEARCH_RESULT_ENTRY, &next_e);
+ slapi_pblock_get(pb, SLAPI_SEARCH_RESULT_ENTRY_EXT, &next_backend_info_ptr);
+ if (next_e == NULL)
+ {
+ /* no more entries */
+ done = 1;
+ }
+ }
+
+ if (process_entry(pb, e, send_result))
+ {
+ /* shouldn't send this entry */
+ continue;
+ }
+
+ /*
+ * It's a regular entry, or it's a referral and
+ * managedsait control is on. In either case, send the entry.
+ */
+ if (done)
+ {
+ struct berval **urls = NULL;
+ /* Send the entry and the result at the same time */
+ slapi_pblock_get(pb, SLAPI_SEARCH_REFERRALS, &urls);
+ rc = send_ldap_search_entry_ext(pb, e, NULL, attrs, attrsonly, 1,
+ (*pnentries)+1, urls);
+ if (rc == 1)
+ {
+ /* this means we didn't have access to the entry. Since the
+ * entry was not sent, we need to send the done packet.
+ */
+ send_result_status = 1;
+ }
+ }
+ else
+ {
+ /* Send the entry */
+ rc = send_ldap_search_entry(pb, e, NULL, attrs,
+ attrsonly);
+ }
+ switch (rc)
+ {
+ case 0: /* entry sent ok */
+ (*pnentries)++;
+ slapi_pblock_set(pb, SLAPI_NENTRIES, pnentries);
+ break;
+ case 1: /* entry not sent */
+ break;
+ case -1: /* connection closed */
+ /*
+ * mark the operation as abandoned so the backend
+ * next entry function gets called again and has
+ * a chance to clean things up.
+ */
+ pb->pb_op->o_status = SLAPI_OP_STATUS_ABANDONED;
+ break;
+ }
+ }
+
+ be->be_entry_release(pb, backend_info_ptr);
+ if (*pnentries == 0 || send_result_status)
+ {
+ /* We didn't send the result done message so the caller
+ * must send it */
+ return 1;
+ }
+ else
+ {
+ /* The result message has been sent */
+ return 0;
+ }
+}
+
+/* Loops through search entries and sends them to the client.
+ * returns -1 on error or 1 if result packet wasn't sent.
+ * This function never returns 0 because it doesn't send
+ * the result packet back with the last entry like
+ * iterate_with_lookahead trys to do.
+ */
+static int
+iterate(Slapi_PBlock *pb, Slapi_Backend *be, int send_result, int *pnentries)
+{
+ int rc;
+ int attrsonly;
+ int done = 0;
+ Slapi_Entry *e;
+ char **attrs = NULL;
+
+ slapi_pblock_get(pb, SLAPI_SEARCH_ATTRS, &attrs);
+ slapi_pblock_get(pb, SLAPI_SEARCH_ATTRSONLY, &attrsonly);
+
+ *pnentries = 0;
+
+ while (!done)
+ {
+ rc = be->be_next_search_entry(pb);
+ if (rc < 0)
+ {
+ /*
+ * Some exceptional condition occurred. Results have been sent, so we're finished.
+ */
+ if (rc == SLAPI_FAIL_DISKFULL)
+ {
+ operation_out_of_disk_space();
+ }
+ return -1;
+ }
+ else
+ {
+ slapi_pblock_get(pb, SLAPI_SEARCH_RESULT_ENTRY, &e);
+ if (e == NULL)
+ {
+ /* no more entries */
+ done = 1;
+ continue;
+ }
+ }
+
+ if (process_entry(pb, e, send_result))
+ {
+ /* shouldn't send this entry */
+ continue;
+ }
+
+ /*
+ * It's a regular entry, or it's a referral and
+ * managedsait control is on. In either case, send
+ * the entry.
+ */
+ switch (send_ldap_search_entry(pb, e, NULL, attrs, attrsonly))
+ {
+ case 0: /* entry sent ok */
+ (*pnentries)++;
+ slapi_pblock_set(pb, SLAPI_NENTRIES, pnentries);
+ break;
+ case 1: /* entry not sent */
+ break;
+ case -1: /* connection closed */
+ /*
+ * mark the operation as abandoned so the backend
+ * next entry function gets called again and has
+ * a chance to clean things up.
+ */
+ pb->pb_op->o_status = SLAPI_OP_STATUS_ABANDONED;
+ break;
+ }
+ }
+
+ return 1;
+}
+
+
+static int timelimit_reslimit_handle = -1;
+static int sizelimit_reslimit_handle = -1;
+
+/*
+ * Register size and time limit with the binder-based resource limits
+ * subsystem. A SLAPI_RESLIMIT_STATUS_... code is returned.
+ */
+int
+search_register_reslimits( void )
+{
+ int rc1, rc2;
+
+ rc1 = slapi_reslimit_register( SLAPI_RESLIMIT_TYPE_INT,
+ "nsSizeLimit" , &sizelimit_reslimit_handle );
+ rc2 = slapi_reslimit_register( SLAPI_RESLIMIT_TYPE_INT,
+ "nsTimeLimit", &timelimit_reslimit_handle );
+
+ if ( rc1 != SLAPI_RESLIMIT_STATUS_SUCCESS ) {
+ return( rc1 );
+ } else {
+ return( rc2 );
+ }
+}
+
+
+/*
+ * Compute size and time limits based on the connection (bind identity).
+ * Binder-based resource limits get top priority, followed by those associated
+ * with the backend we are using.
+ *
+ * If the binder is the root DN and there is nothing in the root DN's entry
+ * to say otherwise, no limits are used. Otherwise, the lower of the limit
+ * that was sent in the LDAP request and that available based on the
+ * connection bind identity or configured backend limit is used.
+ */
+static void
+compute_limits (Slapi_PBlock *pb)
+{
+ int timelimit, sizelimit;
+ int requested_timelimit, max_timelimit, requested_sizelimit, max_sizelimit;
+ int isroot;
+ int isCertAuth;
+ Slapi_ComponentId *component_id = NULL;
+ Slapi_Backend *be;
+
+ slapi_pblock_get (pb, SLAPI_SEARCH_TIMELIMIT, &requested_timelimit);
+ slapi_pblock_get (pb, SLAPI_SEARCH_SIZELIMIT, &requested_sizelimit);
+ slapi_pblock_get (pb, SLAPI_REQUESTOR_ISROOT, &isroot);
+ slapi_pblock_get (pb, SLAPI_BACKEND, &be);
+
+
+ /* If the search belongs to the client authentication process, take the value at
+ * nsslapd-timelimit as the actual time limit.
+ */
+
+ slapi_pblock_get (pb, SLAPI_PLUGIN_IDENTITY, &component_id);
+ if (component_id) {
+ isCertAuth = (! strcasecmp(component_id->sci_component_name, COMPONENT_CERT_AUTH) ) ? 1 : 0;
+ if (isCertAuth) {
+ timelimit = config_get_timelimit();
+ goto set_timelimit;
+ }
+ }
+
+ /*
+ * Compute the time limit.
+ */
+ if ( slapi_reslimit_get_integer_limit( pb->pb_conn,
+ timelimit_reslimit_handle, &max_timelimit )
+ != SLAPI_RESLIMIT_STATUS_SUCCESS ) {
+ /*
+ * no limit associated with binder/connection or some other error
+ * occurred. use the default maximum.
+ */
+ if ( isroot ) {
+ max_timelimit = -1; /* no limit */
+ } else {
+ max_timelimit = be->be_timelimit;
+ }
+ }
+
+ if ( requested_timelimit == 0 ) {
+ timelimit = ( max_timelimit == -1 ) ? -1 : max_timelimit;
+ } else if ( max_timelimit == -1 || requested_timelimit < max_timelimit ) {
+ timelimit = requested_timelimit;
+ } else {
+ timelimit = max_timelimit;
+ }
+
+ set_timelimit:
+ slapi_pblock_set(pb, SLAPI_SEARCH_TIMELIMIT, &timelimit);
+
+
+ /*
+ * Compute the size limit.
+ */
+ if ( slapi_reslimit_get_integer_limit( pb->pb_conn,
+ sizelimit_reslimit_handle, &max_sizelimit )
+ != SLAPI_RESLIMIT_STATUS_SUCCESS ) {
+ /*
+ * no limit associated with binder/connection or some other error
+ * occurred. use the default maximum.
+ */
+ if ( isroot ) {
+ max_sizelimit = -1; /* no limit */
+ } else {
+ max_sizelimit = be->be_sizelimit;
+ }
+ }
+
+ if ( requested_sizelimit == 0 ) {
+ sizelimit = ( max_sizelimit == -1 ) ? -1 : max_sizelimit;
+ } else if ( max_sizelimit == -1 || requested_sizelimit < max_sizelimit ) {
+ sizelimit = requested_sizelimit;
+ } else {
+ sizelimit = max_sizelimit;
+ }
+ slapi_pblock_set(pb, SLAPI_SEARCH_SIZELIMIT, &sizelimit);
+
+ LDAPDebug( LDAP_DEBUG_TRACE,
+ "=> compute_limits: sizelimit=%d, timelimit=%d\n",
+ sizelimit, timelimit, 0 );
+}
+
+
+/* Iterates through results and send them to the client.
+ * Returns 0 if successful and -1 otherwise
+ */
+static int send_results (Slapi_PBlock *pb, int send_result, int * nentries)
+{
+ Slapi_Backend *be;
+ int rc;
+ struct berval **urls = NULL;
+
+ slapi_pblock_get (pb, SLAPI_BACKEND, &be);
+
+ if (be->be_next_search_entry == NULL)
+ {
+ /* we need to send the result, but the function to iterate through
+ the result set is not implemented */
+ /* ONREPL - log error */
+ send_ldap_result(pb, LDAP_UNWILLING_TO_PERFORM, NULL, "Search not supported", 0, NULL);
+ return -1;
+ }
+
+ /* Iterate through the returned result set */
+ if (be->be_next_search_entry_ext != NULL)
+ {
+ /* The iterate look ahead is causing a whole mess with the ACL.
+ ** the entries are now visiting the ACL land in a random way
+ ** and not the ordered way it was before. Until we figure out
+ ** let's not change the behavior.
+ **
+ ** Don't use iterate_with_lookahead because it sends the result
+ * in the same times as the entry and this can cause failure
+ * of the mapping tree scanning algorithme
+ * if (getFrontendConfig()->result_tweak)
+ * {
+ * rc = iterate_with_lookahead(pb, be, send_result, nentries);
+ * }
+ * else
+ */
+ rc = iterate(pb, be, send_result, nentries);
+ }
+ else
+ {
+ rc = iterate(pb, be, send_result, nentries);
+ }
+
+ switch(rc)
+ {
+ case -1: /* an error occured */
+
+ case 0 : /* everything is ok - result is sent */
+ /* If this happens we are dead but hopefully iterate
+ * never sends the result itself
+ */
+ break;
+
+ case 1: /* everything is ok - don't send the result */
+ rc = 0;
+ }
+
+ return rc;
+}
+
+void op_shared_log_error_access (Slapi_PBlock *pb, const char *type, const char *dn, const char *msg)
+{
+ char ebuf[BUFSIZ];
+ slapi_log_access( LDAP_DEBUG_STATS, "conn=%d op=%d %s dn=\"%s\", %s\n",
+ ( pb->pb_conn ? pb->pb_conn->c_connid : 0),
+ ( pb->pb_op ? pb->pb_op->o_opid : 0),
+ type,
+ escape_string( dn, ebuf ),
+ msg ? msg : "" );
+
+}
+
diff --git a/ldap/servers/slapd/pblock.c b/ldap/servers/slapd/pblock.c
new file mode 100644
index 00000000..d4431ddb
--- /dev/null
+++ b/ldap/servers/slapd/pblock.c
@@ -0,0 +1,2930 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+
+
+#include <stdio.h>
+#include <string.h>
+#include <sys/types.h>
+#include "slap.h"
+#include "cert.h"
+
+void
+pblock_init( Slapi_PBlock *pb )
+{
+ memset( pb, '\0', sizeof(Slapi_PBlock) );
+}
+
+void
+pblock_init_common(
+ Slapi_PBlock *pb,
+ Slapi_Backend *be,
+ Connection *conn,
+ Operation *op
+)
+{
+ PR_ASSERT( NULL != pb );
+ memset( pb, '\0', sizeof(Slapi_PBlock) );
+ pb->pb_backend = be;
+ pb->pb_conn = conn;
+ pb->pb_op = op;
+}
+
+void
+slapi_pblock_get_common(
+ Slapi_PBlock *pb,
+ Slapi_Backend **be,
+ Connection **conn,
+ Operation **op
+)
+{
+ PR_ASSERT( NULL != pb );
+ PR_ASSERT( NULL != be );
+ PR_ASSERT( NULL != conn );
+ PR_ASSERT( NULL != op );
+ *be = pb->pb_backend;
+ *conn = pb->pb_conn;
+ *op = pb->pb_op;
+}
+
+Slapi_PBlock *
+slapi_pblock_new()
+{
+ Slapi_PBlock *pb;
+
+ pb = (Slapi_PBlock *) slapi_ch_calloc( 1, sizeof(Slapi_PBlock) );
+ return pb;
+}
+
+void
+pblock_done( Slapi_PBlock *pb )
+{
+ if(pb->pb_op!=NULL)
+ {
+ operation_free(&pb->pb_op,pb->pb_conn);
+ }
+ slapi_ch_free((void**)&(pb->pb_result_text));
+}
+
+void
+slapi_pblock_destroy( Slapi_PBlock* pb )
+{
+ if(pb!=NULL)
+ {
+ pblock_done(pb);
+ slapi_ch_free((void**)&pb);
+ }
+}
+
+/* JCM - when pb_o_params is used, check the operation type. */
+/* JCM - when pb_o_results is used, check the operation type. */
+
+#define SLAPI_PLUGIN_TYPE_CHECK(PBLOCK,TYPE) \
+if ( PBLOCK ->pb_plugin->plg_type != TYPE) return( -1 )
+
+
+/*
+ * Macro used to safely retrieve a plugin related pblock value (if the
+ * pb_plugin element is NULL, NULL is returned).
+ */
+#define SLAPI_PBLOCK_GET_PLUGIN_RELATED_POINTER( pb, element ) \
+ ((pb)->pb_plugin == NULL ? NULL : (pb)->pb_plugin->element)
+
+
+int
+slapi_pblock_get( Slapi_PBlock *pblock, int arg, void *value )
+{
+ char *authtype;
+ Slapi_Backend *be;
+
+ PR_ASSERT( NULL != pblock );
+ PR_ASSERT( NULL != value );
+ be = pblock->pb_backend;
+
+ switch ( arg ) {
+ case SLAPI_BACKEND:
+ (*(Slapi_Backend **)value) = be;
+ break;
+ case SLAPI_BACKEND_COUNT:
+ (*(int *)value) = pblock->pb_backend_count;
+ break;
+ case SLAPI_BE_TYPE:
+ if ( NULL == be ) {
+ return( -1 );
+ }
+ (*(char **)value) = be->be_type;
+ break;
+ case SLAPI_BE_READONLY:
+ if ( NULL == be ) {
+ (*(int *)value) = 0; /* default value */
+ } else {
+ (*(int *)value) = be->be_readonly;
+ }
+ break;
+ case SLAPI_BE_LASTMOD:
+ if ( NULL == be ) {
+ (*(int *)value) = (g_get_global_lastmod() == LDAP_ON);
+ } else {
+ (*(int *)value) = (be->be_lastmod == LDAP_ON || (be->be_lastmod
+ == LDAP_UNDEFINED && g_get_global_lastmod() == LDAP_ON));
+ }
+ break;
+ case SLAPI_CONNECTION:
+ (*(Connection **)value) = pblock->pb_conn;
+ break;
+ case SLAPI_CONN_ID:
+ if (pblock->pb_conn == NULL) {
+ LDAPDebug( LDAP_DEBUG_ANY,
+ "Connection is NULL and hence cannot access SLAPI_CONN_ID \n", 0, 0, 0 );
+ return (-1);
+ }
+ (*(int *)value) = pblock->pb_conn->c_connid;
+ break;
+ case SLAPI_CONN_DN:
+ /*
+ * NOTE: we have to make a copy of this that the caller
+ * is responsible for freeing. otherwise, they would get
+ * a pointer that could be freed out from under them.
+ */
+ if (pblock->pb_conn == NULL) {
+ LDAPDebug( LDAP_DEBUG_ANY,
+ "Connection is NULL and hence cannot access SLAPI_CONN_DN \n", 0, 0, 0 );
+ return (-1);
+ }
+ PR_Lock( pblock->pb_conn->c_mutex );
+ (*(char **)value) = (NULL == pblock->pb_conn->c_dn ? NULL :
+ slapi_ch_strdup( pblock->pb_conn->c_dn ));
+ PR_Unlock( pblock->pb_conn->c_mutex );
+ break;
+ case SLAPI_CONN_AUTHTYPE:/* deprecated */
+ if (pblock->pb_conn == NULL) {
+ LDAPDebug( LDAP_DEBUG_ANY,
+ "Connection is NULL and hence cannot access SLAPI_CONN_AUTHTYPE \n", 0, 0, 0 );
+ return (-1);
+ }
+ PR_Lock( pblock->pb_conn->c_mutex );
+ authtype = pblock->pb_conn->c_authtype;
+ PR_Unlock( pblock->pb_conn->c_mutex );
+ if (authtype == NULL) {
+ (*(char **)value) = NULL;
+ } else if (strcasecmp(authtype, SLAPD_AUTH_NONE) == 0) {
+ (*(char **)value) = SLAPD_AUTH_NONE;
+ } else if (strcasecmp(authtype, SLAPD_AUTH_SIMPLE) == 0) {
+ (*(char **)value) = SLAPD_AUTH_SIMPLE;
+ } else if (strcasecmp(authtype, SLAPD_AUTH_SSL) == 0) {
+ (*(char **)value) = SLAPD_AUTH_SSL;
+ } else if (strncasecmp(authtype, SLAPD_AUTH_SASL,
+ strlen(SLAPD_AUTH_SASL)) == 0) {
+ (*(char **)value) = SLAPD_AUTH_SASL;
+ } else {
+ (*(char **)value) = "unknown";
+ }
+ break;
+ case SLAPI_CONN_AUTHMETHOD:
+ /* returns a copy */
+ if (pblock->pb_conn == NULL) {
+ LDAPDebug( LDAP_DEBUG_ANY,
+ "Connection is NULL and hence cannot access SLAPI_CONN_AUTHMETHOD \n", 0, 0, 0 );
+ return (-1);
+ }
+ PR_Lock( pblock->pb_conn->c_mutex );
+ (*(char **)value) = pblock->pb_conn->c_authtype ?
+ slapi_ch_strdup(pblock->pb_conn->c_authtype) : NULL;
+ PR_Unlock( pblock->pb_conn->c_mutex );
+ break;
+ case SLAPI_CONN_CLIENTNETADDR:
+ if (pblock->pb_conn == NULL)
+ {
+ memset( value, 0, sizeof( PRNetAddr ));
+ break;
+ }
+ PR_Lock( pblock->pb_conn->c_mutex );
+ if ( pblock->pb_conn->cin_addr == NULL ) {
+ memset( value, 0, sizeof( PRNetAddr ));
+ } else {
+ (*(PRNetAddr *)value) =
+ *(pblock->pb_conn->cin_addr);
+ }
+ PR_Unlock( pblock->pb_conn->c_mutex );
+ break;
+ case SLAPI_CONN_SERVERNETADDR:
+ if (pblock->pb_conn == NULL)
+ {
+ memset( value, 0, sizeof( PRNetAddr ));
+ break;
+ }
+ PR_Lock( pblock->pb_conn->c_mutex );
+ if ( pblock->pb_conn->cin_destaddr == NULL ) {
+ memset( value, 0, sizeof( PRNetAddr ));
+ } else {
+ (*(PRNetAddr *)value) =
+ *(pblock->pb_conn->cin_destaddr);
+ }
+ PR_Unlock( pblock->pb_conn->c_mutex );
+ break;
+ case SLAPI_CONN_CLIENTIP:
+ if (pblock->pb_conn == NULL)
+ {
+ memset( value, 0, sizeof( struct in_addr ));
+ break;
+ }
+ PR_Lock( pblock->pb_conn->c_mutex );
+ if ( pblock->pb_conn->cin_addr == NULL ) {
+ memset( value, 0, sizeof( struct in_addr ));
+ } else {
+
+ if ( PR_IsNetAddrType(pblock->pb_conn->cin_addr,
+ PR_IpAddrV4Mapped) ) {
+
+ (*(struct in_addr *)value).s_addr =
+ (*(pblock->pb_conn->cin_addr)).ipv6.ip.pr_s6_addr32[3];
+
+ } else {
+ memset( value, 0, sizeof( struct in_addr ));
+ }
+ }
+ PR_Unlock( pblock->pb_conn->c_mutex );
+ break;
+ case SLAPI_CONN_SERVERIP:
+ if (pblock->pb_conn == NULL)
+ {
+ memset( value, 0, sizeof( struct in_addr ));
+ break;
+ }
+ PR_Lock( pblock->pb_conn->c_mutex );
+ if ( pblock->pb_conn->cin_destaddr == NULL ) {
+ memset( value, 0, sizeof( PRNetAddr ));
+ } else {
+
+ if ( PR_IsNetAddrType(pblock->pb_conn->cin_destaddr,
+ PR_IpAddrV4Mapped) ) {
+
+ (*(struct in_addr *)value).s_addr =
+ (*(pblock->pb_conn->cin_destaddr)).ipv6.ip.pr_s6_addr32[3];
+
+ } else {
+ memset( value, 0, sizeof( struct in_addr ));
+ }
+
+ }
+ PR_Unlock( pblock->pb_conn->c_mutex );
+ break;
+ case SLAPI_CONN_IS_REPLICATION_SESSION:
+ if (pblock->pb_conn == NULL) {
+ LDAPDebug( LDAP_DEBUG_ANY,
+ "Connection is NULL and hence cannot access SLAPI_CONN_IS_REPLICATION_SESSION \n", 0, 0, 0 );
+ return (-1);
+ }
+ PR_Lock( pblock->pb_conn->c_mutex );
+ (*(int *)value) = pblock->pb_conn->c_isreplication_session;
+ PR_Unlock( pblock->pb_conn->c_mutex );
+ break;
+ case SLAPI_CONN_IS_SSL_SESSION:
+ if (pblock->pb_conn == NULL) {
+ LDAPDebug( LDAP_DEBUG_ANY,
+ "Connection is NULL and hence cannot access SLAPI_CONN_IS_SSL_SESSION \n", 0, 0, 0 );
+ return (-1);
+ }
+ PR_Lock( pblock->pb_conn->c_mutex );
+ (*(int *)value) = pblock->pb_conn->c_flags & CONN_FLAG_SSL;
+ PR_Unlock( pblock->pb_conn->c_mutex );
+ break;
+ case SLAPI_CONN_CERT:
+ if (pblock->pb_conn == NULL) {
+ LDAPDebug( LDAP_DEBUG_ANY,
+ "Connection is NULL and hence cannot access SLAPI_CONN_CERT \n", 0, 0, 0 );
+ return (-1);
+ }
+ ( *(CERTCertificate **) value) = pblock->pb_conn->c_client_cert;
+ break;
+ case SLAPI_OPERATION:
+ (*(Operation **)value) = pblock->pb_op;
+ break;
+ case SLAPI_OPERATION_TYPE:
+ (*(int *)value) = pblock->pb_op->o_params.operation_type;
+ break;
+ case SLAPI_OPINITIATED_TIME:
+ (*(int *)value) = pblock->pb_op->o_time;
+ break;
+ case SLAPI_REQUESTOR_ISROOT:
+ (*(int *)value) = pblock->pb_requestor_isroot;
+ break;
+ case SLAPI_IS_REPLICATED_OPERATION:
+ if(pblock->pb_op==NULL)
+ {
+ (*(int *)value) = 0; /* No Operation -> Not Replicated */
+ }
+ else
+ {
+ (*(int *)value) = (pblock->pb_op->o_flags & (OP_FLAG_REPLICATED | OP_FLAG_LEGACY_REPLICATION_DN));
+ }
+ break;
+ case SLAPI_IS_MMR_REPLICATED_OPERATION:
+ if(pblock->pb_op==NULL)
+ {
+ (*(int *)value) = 0; /* No Operation -> Not Replicated */
+ }
+ else
+ {
+ (*(int *)value) = (pblock->pb_op->o_flags & OP_FLAG_REPLICATED);
+ }
+ break;
+ case SLAPI_IS_LEGACY_REPLICATED_OPERATION:
+ if(pblock->pb_op==NULL)
+ {
+ (*(int *)value) = 0; /* No Operation -> Not Replicated */
+ }
+ else
+ {
+ (*(int *)value) = (pblock->pb_op->o_flags & OP_FLAG_LEGACY_REPLICATION_DN);
+ }
+ break;
+
+ case SLAPI_OPERATION_PARAMETERS:
+ if(pblock->pb_op!=NULL)
+ {
+ (*(struct slapi_operation_parameters **)value) = &pblock->pb_op->o_params;
+ }
+ break;
+
+ /* stuff related to config file processing */
+ case SLAPI_CONFIG_FILENAME:
+ case SLAPI_CONFIG_LINENO:
+ case SLAPI_CONFIG_ARGC:
+ case SLAPI_CONFIG_ARGV:
+ return (-1); /* deprecated since DS 5.0 (no longer useful) */
+
+ /* pblock memory management */
+ case SLAPI_DESTROY_CONTENT:
+ (*(int *)value) = pblock->pb_destroy_content;
+ break;
+
+ /* stuff related to the current plugin */
+ case SLAPI_PLUGIN:
+ (*(struct slapdplugin **)value) = pblock->pb_plugin;
+ break;
+ case SLAPI_PLUGIN_PRIVATE:
+ (*(void **)value) = pblock->pb_plugin->plg_private;
+ break;
+ case SLAPI_PLUGIN_TYPE:
+ (*(int *)value) = pblock->pb_plugin->plg_type;
+ break;
+ case SLAPI_PLUGIN_ARGV:
+ (*(char ***)value) = pblock->pb_plugin->plg_argv;
+ break;
+ case SLAPI_PLUGIN_ARGC:
+ (*(int *)value) = pblock->pb_plugin->plg_argc;
+ break;
+ case SLAPI_PLUGIN_VERSION:
+ (*(char **)value) = pblock->pb_plugin->plg_version;
+ break;
+ case SLAPI_PLUGIN_OPRETURN:
+ (*(int *)value) = pblock->pb_opreturn;
+ break;
+ case SLAPI_PLUGIN_OBJECT:
+ (*(void **)value) = pblock->pb_object;
+ break;
+ case SLAPI_PLUGIN_DESTROY_FN:
+ (*(IFP*)value) = pblock->pb_destroy_fn;
+ break;
+ case SLAPI_PLUGIN_DESCRIPTION:
+ (*(Slapi_PluginDesc *)value) = pblock->pb_plugin->plg_desc;
+ break;
+ case SLAPI_PLUGIN_IDENTITY:
+ (*(void**)value) = pblock->pb_plugin_identity;
+ break;
+ case SLAPI_PLUGIN_INTOP_RESULT:
+ (*(int *)value) = pblock->pb_internal_op_result;
+ break;
+ case SLAPI_PLUGIN_INTOP_SEARCH_ENTRIES:
+ (*(Slapi_Entry ***)value) = pblock->pb_plugin_internal_search_op_entries;
+ break;
+ case SLAPI_PLUGIN_INTOP_SEARCH_REFERRALS:
+ (*(char ***)value) = pblock->pb_plugin_internal_search_op_referrals;
+ break;
+
+ /* database plugin functions */
+ case SLAPI_PLUGIN_DB_BIND_FN:
+ if ( pblock->pb_plugin->plg_type != SLAPI_PLUGIN_DATABASE ) {
+ return( -1 );
+ }
+ (*(IFP *)value) = pblock->pb_plugin->plg_bind;
+ break;
+ case SLAPI_PLUGIN_DB_UNBIND_FN:
+ if ( pblock->pb_plugin->plg_type != SLAPI_PLUGIN_DATABASE ) {
+ return( -1 );
+ }
+ (*(IFP *)value) = pblock->pb_plugin->plg_unbind;
+ break;
+ case SLAPI_PLUGIN_DB_SEARCH_FN:
+ if ( pblock->pb_plugin->plg_type != SLAPI_PLUGIN_DATABASE ) {
+ return( -1 );
+ }
+ (*(IFP *)value) = pblock->pb_plugin->plg_search;
+ break;
+ case SLAPI_PLUGIN_DB_NEXT_SEARCH_ENTRY_FN:
+ if ( pblock->pb_plugin->plg_type != SLAPI_PLUGIN_DATABASE ) {
+ return( -1 );
+ }
+ (*(IFP *)value) = pblock->pb_plugin->plg_next_search_entry;
+ break;
+ case SLAPI_PLUGIN_DB_NEXT_SEARCH_ENTRY_EXT_FN:
+ if ( pblock->pb_plugin->plg_type != SLAPI_PLUGIN_DATABASE ) {
+ return( -1 );
+ }
+ (*(IFP *)value) = pblock->pb_plugin->plg_next_search_entry_ext;
+ break;
+ case SLAPI_PLUGIN_DB_ENTRY_RELEASE_FN:
+ if ( pblock->pb_plugin->plg_type != SLAPI_PLUGIN_DATABASE ) {
+ return( -1 );
+ }
+ (*(IFP *)value) = pblock->pb_plugin->plg_entry_release;
+ break;
+ case SLAPI_PLUGIN_DB_COMPARE_FN:
+ if ( pblock->pb_plugin->plg_type != SLAPI_PLUGIN_DATABASE ) {
+ return( -1 );
+ }
+ (*(IFP *)value) = pblock->pb_plugin->plg_compare;
+ break;
+ case SLAPI_PLUGIN_DB_MODIFY_FN:
+ if ( pblock->pb_plugin->plg_type != SLAPI_PLUGIN_DATABASE ) {
+ return( -1 );
+ }
+ (*(IFP *)value) = pblock->pb_plugin->plg_modify;
+ break;
+ case SLAPI_PLUGIN_DB_MODRDN_FN:
+ if ( pblock->pb_plugin->plg_type != SLAPI_PLUGIN_DATABASE ) {
+ return( -1 );
+ }
+ (*(IFP *)value) = pblock->pb_plugin->plg_modrdn;
+ break;
+ case SLAPI_PLUGIN_DB_ADD_FN:
+ if ( pblock->pb_plugin->plg_type != SLAPI_PLUGIN_DATABASE ) {
+ return( -1 );
+ }
+ (*(IFP *)value) = pblock->pb_plugin->plg_add;
+ break;
+ case SLAPI_PLUGIN_DB_DELETE_FN:
+ if ( pblock->pb_plugin->plg_type != SLAPI_PLUGIN_DATABASE ) {
+ return( -1 );
+ }
+ (*(IFP *)value) = pblock->pb_plugin->plg_delete;
+ break;
+ case SLAPI_PLUGIN_DB_ABANDON_FN:
+ if ( pblock->pb_plugin->plg_type != SLAPI_PLUGIN_DATABASE ) {
+ return( -1 );
+ }
+ (*(IFP *)value) = pblock->pb_plugin->plg_abandon;
+ break;
+ case SLAPI_PLUGIN_DB_CONFIG_FN:
+ if ( pblock->pb_plugin->plg_type != SLAPI_PLUGIN_DATABASE ) {
+ return( -1 );
+ }
+ (*(IFP *)value) = pblock->pb_plugin->plg_config;
+ break;
+ case SLAPI_PLUGIN_CLOSE_FN:
+ (*(IFP *)value) = pblock->pb_plugin->plg_close;
+ break;
+ case SLAPI_PLUGIN_CLEANUP_FN:
+ (*(IFP *)value) = pblock->pb_plugin->plg_cleanup;
+ break;
+ case SLAPI_PLUGIN_DB_FLUSH_FN:
+ (*(IFP *)value) = pblock->pb_plugin->plg_flush;
+ break;
+ case SLAPI_PLUGIN_START_FN:
+ (*(IFP *)value) = pblock->pb_plugin->plg_start;
+ break;
+ case SLAPI_PLUGIN_POSTSTART_FN:
+ (*(IFP *)value) = pblock->pb_plugin->plg_poststart;
+ break;
+ case SLAPI_PLUGIN_DB_WIRE_IMPORT_FN:
+ (*(IFP *)value) = pblock->pb_plugin->plg_wire_import;
+ break;
+ case SLAPI_PLUGIN_DB_SEQ_FN:
+ if ( pblock->pb_plugin->plg_type != SLAPI_PLUGIN_DATABASE ) {
+ return( -1 );
+ }
+ (*(IFP *)value) = pblock->pb_plugin->plg_seq;
+ break;
+ case SLAPI_PLUGIN_DB_ENTRY_FN:
+ (*(IFP *)value) = SLAPI_PBLOCK_GET_PLUGIN_RELATED_POINTER( pblock,
+ plg_entry );
+ break;
+ case SLAPI_PLUGIN_DB_REFERRAL_FN:
+ (*(IFP *)value) = SLAPI_PBLOCK_GET_PLUGIN_RELATED_POINTER( pblock,
+ plg_referral );
+ break;
+ case SLAPI_PLUGIN_DB_RESULT_FN:
+ (*(IFP *)value) = SLAPI_PBLOCK_GET_PLUGIN_RELATED_POINTER( pblock,
+ plg_result );
+ break;
+ case SLAPI_PLUGIN_DB_RMDB_FN:
+ if ( pblock->pb_plugin->plg_type != SLAPI_PLUGIN_DATABASE ) {
+ return( -1 );
+ }
+ (*(IFP *)value) = pblock->pb_plugin->plg_rmdb;
+ break;
+ case SLAPI_PLUGIN_DB_INIT_INSTANCE_FN:
+ if ( pblock->pb_plugin->plg_type != SLAPI_PLUGIN_DATABASE ) {
+ return( -1 );
+ }
+ (*(IFP *)value) = pblock->pb_plugin->plg_init_instance;
+ break;
+ case SLAPI_PLUGIN_DB_LDIF2DB_FN:
+ if ( pblock->pb_plugin->plg_type != SLAPI_PLUGIN_DATABASE ) {
+ return( -1 );
+ }
+ (*(IFP *)value) = pblock->pb_plugin->plg_ldif2db;
+ break;
+ case SLAPI_PLUGIN_DB_DB2LDIF_FN:
+ if ( pblock->pb_plugin->plg_type != SLAPI_PLUGIN_DATABASE ) {
+ return( -1 );
+ }
+ (*(IFP *)value) = pblock->pb_plugin->plg_db2ldif;
+ break;
+ case SLAPI_PLUGIN_DB_DB2INDEX_FN:
+ if ( pblock->pb_plugin->plg_type != SLAPI_PLUGIN_DATABASE ) {
+ return( -1 );
+ }
+ (*(IFP *)value) = pblock->pb_plugin->plg_db2index;
+ break;
+ case SLAPI_PLUGIN_DB_ARCHIVE2DB_FN:
+ if ( pblock->pb_plugin->plg_type != SLAPI_PLUGIN_DATABASE ) {
+ return( -1 );
+ }
+ (*(IFP *)value) = pblock->pb_plugin->plg_archive2db;
+ break;
+ case SLAPI_PLUGIN_DB_DB2ARCHIVE_FN:
+ if ( pblock->pb_plugin->plg_type != SLAPI_PLUGIN_DATABASE ) {
+ return( -1 );
+ }
+ (*(IFP *)value) = pblock->pb_plugin->plg_db2archive;
+ break;
+#if defined(UPGRADEDB)
+ case SLAPI_PLUGIN_DB_UPGRADEDB_FN:
+ if ( pblock->pb_plugin->plg_type != SLAPI_PLUGIN_DATABASE ) {
+ return( -1 );
+ }
+ (*(IFP *)value) = pblock->pb_plugin->plg_upgradedb;
+ break;
+#endif
+ case SLAPI_PLUGIN_DB_BEGIN_FN:
+ if ( pblock->pb_plugin->plg_type != SLAPI_PLUGIN_DATABASE ) {
+ return( -1 );
+ }
+ (*(IFP *)value) = pblock->pb_plugin->plg_un.plg_un_db.plg_un_db_begin;
+ break;
+ case SLAPI_PLUGIN_DB_COMMIT_FN:
+ if ( pblock->pb_plugin->plg_type != SLAPI_PLUGIN_DATABASE ) {
+ return( -1 );
+ }
+ (*(IFP *)value) = pblock->pb_plugin->plg_un.plg_un_db.plg_un_db_commit;
+ break;
+ case SLAPI_PLUGIN_DB_ABORT_FN:
+ if ( pblock->pb_plugin->plg_type != SLAPI_PLUGIN_DATABASE ) {
+ return( -1 );
+ }
+ (*(IFP *)value) = pblock->pb_plugin->plg_un.plg_un_db.plg_un_db_abort;
+ break;
+ case SLAPI_PLUGIN_DB_SIZE_FN:
+ if ( pblock->pb_plugin->plg_type != SLAPI_PLUGIN_DATABASE ) {
+ return( -1 );
+ }
+ (*(IFP *)value) = pblock->pb_plugin->plg_un.plg_un_db.plg_un_db_dbsize;
+ break;
+ case SLAPI_PLUGIN_DB_TEST_FN:
+ if ( pblock->pb_plugin->plg_type != SLAPI_PLUGIN_DATABASE ) {
+ return( -1 );
+ }
+ (*(IFP *)value) = pblock->pb_plugin->plg_un.plg_un_db.plg_un_db_dbtest;
+ break;
+ /* database plugin-specific parameters */
+ case SLAPI_PLUGIN_DB_NO_ACL:
+ if ( pblock->pb_plugin && pblock->pb_plugin->plg_type != SLAPI_PLUGIN_DATABASE ) {
+ return( -1 );
+ }
+ if ( NULL == be ) {
+ (*(int *)value) = 0; /* default value */
+ } else {
+ (*(int *)value) = be->be_noacl;
+ }
+ break;
+
+ /* extendedop plugin functions */
+ case SLAPI_PLUGIN_EXT_OP_FN:
+ if ( pblock->pb_plugin->plg_type != SLAPI_PLUGIN_EXTENDEDOP ) {
+ return( -1 );
+ }
+ (*(IFP *)value) = pblock->pb_plugin->plg_exhandler;
+ break;
+ case SLAPI_PLUGIN_EXT_OP_OIDLIST:
+ if ( pblock->pb_plugin->plg_type != SLAPI_PLUGIN_EXTENDEDOP ) {
+ return( -1 );
+ }
+ (*(char ***)value) = pblock->pb_plugin->plg_exoids;
+ break;
+ case SLAPI_PLUGIN_EXT_OP_NAMELIST:
+ if ( pblock->pb_plugin->plg_type != SLAPI_PLUGIN_EXTENDEDOP ) {
+ return( -1 );
+ }
+ (*(char ***)value) = pblock->pb_plugin->plg_exnames;
+ break;
+
+ /* preoperation plugin functions */
+ case SLAPI_PLUGIN_PRE_BIND_FN:
+ if (pblock->pb_plugin->plg_type != SLAPI_PLUGIN_PREOPERATION) {
+ return( -1 );
+ }
+ (*(IFP *)value) = pblock->pb_plugin->plg_prebind;
+ break;
+ case SLAPI_PLUGIN_PRE_UNBIND_FN:
+ if (pblock->pb_plugin->plg_type != SLAPI_PLUGIN_PREOPERATION) {
+ return( -1 );
+ }
+ (*(IFP *)value) = pblock->pb_plugin->plg_preunbind;
+ break;
+ case SLAPI_PLUGIN_PRE_SEARCH_FN:
+ if (pblock->pb_plugin->plg_type != SLAPI_PLUGIN_PREOPERATION) {
+ return( -1 );
+ }
+ (*(IFP *)value) = pblock->pb_plugin->plg_presearch;
+ break;
+ case SLAPI_PLUGIN_PRE_COMPARE_FN:
+ if (pblock->pb_plugin->plg_type != SLAPI_PLUGIN_PREOPERATION) {
+ return( -1 );
+ }
+ (*(IFP *)value) = pblock->pb_plugin->plg_precompare;
+ break;
+ case SLAPI_PLUGIN_PRE_MODIFY_FN:
+ if (pblock->pb_plugin->plg_type != SLAPI_PLUGIN_PREOPERATION) {
+ return( -1 );
+ }
+ (*(IFP *)value) = pblock->pb_plugin->plg_premodify;
+ break;
+ case SLAPI_PLUGIN_PRE_MODRDN_FN:
+ if (pblock->pb_plugin->plg_type != SLAPI_PLUGIN_PREOPERATION) {
+ return( -1 );
+ }
+ (*(IFP *)value) = pblock->pb_plugin->plg_premodrdn;
+ break;
+ case SLAPI_PLUGIN_PRE_ADD_FN:
+ if (pblock->pb_plugin->plg_type != SLAPI_PLUGIN_PREOPERATION) {
+ return( -1 );
+ }
+ (*(IFP *)value) = pblock->pb_plugin->plg_preadd;
+ break;
+ case SLAPI_PLUGIN_PRE_DELETE_FN:
+ if (pblock->pb_plugin->plg_type != SLAPI_PLUGIN_PREOPERATION) {
+ return( -1 );
+ }
+ (*(IFP *)value) = pblock->pb_plugin->plg_predelete;
+ break;
+ case SLAPI_PLUGIN_PRE_ABANDON_FN:
+ if (pblock->pb_plugin->plg_type != SLAPI_PLUGIN_PREOPERATION) {
+ return( -1 );
+ }
+ (*(IFP *)value) = pblock->pb_plugin->plg_preabandon;
+ break;
+ case SLAPI_PLUGIN_PRE_ENTRY_FN:
+ if (pblock->pb_plugin->plg_type != SLAPI_PLUGIN_PREOPERATION) {
+ return( -1 );
+ }
+ (*(IFP *)value) = pblock->pb_plugin->plg_preentry;
+ break;
+ case SLAPI_PLUGIN_PRE_REFERRAL_FN:
+ if (pblock->pb_plugin->plg_type != SLAPI_PLUGIN_PREOPERATION) {
+ return( -1 );
+ }
+ (*(IFP *)value) = pblock->pb_plugin->plg_prereferral;
+ break;
+ case SLAPI_PLUGIN_PRE_RESULT_FN:
+ if (pblock->pb_plugin->plg_type != SLAPI_PLUGIN_PREOPERATION) {
+ return( -1 );
+ }
+ (*(IFP *)value) = pblock->pb_plugin->plg_preresult;
+ break;
+
+ /* postoperation plugin functions */
+ case SLAPI_PLUGIN_POST_BIND_FN:
+ if (pblock->pb_plugin->plg_type != SLAPI_PLUGIN_POSTOPERATION) {
+ return( -1 );
+ }
+ (*(IFP *)value) = pblock->pb_plugin->plg_postbind;
+ break;
+ case SLAPI_PLUGIN_POST_UNBIND_FN:
+ if (pblock->pb_plugin->plg_type != SLAPI_PLUGIN_POSTOPERATION) {
+ return( -1 );
+ }
+ (*(IFP *)value) = pblock->pb_plugin->plg_postunbind;
+ break;
+ case SLAPI_PLUGIN_POST_SEARCH_FN:
+ if (pblock->pb_plugin->plg_type != SLAPI_PLUGIN_POSTOPERATION) {
+ return( -1 );
+ }
+ (*(IFP *)value) = pblock->pb_plugin->plg_postsearch;
+ break;
+ case SLAPI_PLUGIN_POST_SEARCH_FAIL_FN:
+ if (pblock->pb_plugin->plg_type != SLAPI_PLUGIN_POSTOPERATION) {
+ return( -1 );
+ }
+ (*(IFP *)value) = pblock->pb_plugin->plg_postsearchfail;
+ break;
+ case SLAPI_PLUGIN_POST_COMPARE_FN:
+ if (pblock->pb_plugin->plg_type != SLAPI_PLUGIN_POSTOPERATION) {
+ return( -1 );
+ }
+ (*(IFP *)value) = pblock->pb_plugin->plg_postcompare;
+ break;
+ case SLAPI_PLUGIN_POST_MODIFY_FN:
+ if (pblock->pb_plugin->plg_type != SLAPI_PLUGIN_POSTOPERATION) {
+ return( -1 );
+ }
+ (*(IFP *)value) = pblock->pb_plugin->plg_postmodify;
+ break;
+ case SLAPI_PLUGIN_POST_MODRDN_FN:
+ if (pblock->pb_plugin->plg_type != SLAPI_PLUGIN_POSTOPERATION) {
+ return( -1 );
+ }
+ (*(IFP *)value) = pblock->pb_plugin->plg_postmodrdn;
+ break;
+ case SLAPI_PLUGIN_POST_ADD_FN:
+ if (pblock->pb_plugin->plg_type != SLAPI_PLUGIN_POSTOPERATION) {
+ return( -1 );
+ }
+ (*(IFP *)value) = pblock->pb_plugin->plg_postadd;
+ break;
+ case SLAPI_PLUGIN_POST_DELETE_FN:
+ if (pblock->pb_plugin->plg_type != SLAPI_PLUGIN_POSTOPERATION) {
+ return( -1 );
+ }
+ (*(IFP *)value) = pblock->pb_plugin->plg_postdelete;
+ break;
+ case SLAPI_PLUGIN_POST_ABANDON_FN:
+ if (pblock->pb_plugin->plg_type != SLAPI_PLUGIN_POSTOPERATION) {
+ return( -1 );
+ }
+ (*(IFP *)value) = pblock->pb_plugin->plg_postabandon;
+ break;
+ case SLAPI_PLUGIN_POST_ENTRY_FN:
+ if (pblock->pb_plugin->plg_type != SLAPI_PLUGIN_POSTOPERATION) {
+ return( -1 );
+ }
+ (*(IFP *)value) = pblock->pb_plugin->plg_postentry;
+ break;
+ case SLAPI_PLUGIN_POST_REFERRAL_FN:
+ if (pblock->pb_plugin->plg_type != SLAPI_PLUGIN_POSTOPERATION) {
+ return( -1 );
+ }
+ (*(IFP *)value) = pblock->pb_plugin->plg_postreferral;
+ break;
+ case SLAPI_PLUGIN_POST_RESULT_FN:
+ if (pblock->pb_plugin->plg_type != SLAPI_PLUGIN_POSTOPERATION) {
+ return( -1 );
+ }
+ (*(IFP *)value) = pblock->pb_plugin->plg_postresult;
+ break;
+
+ case SLAPI_ENTRY_PRE_OP:
+ (*(Slapi_Entry **)value) = pblock->pb_pre_op_entry;
+ break;
+ case SLAPI_ENTRY_POST_OP:
+ (*(Slapi_Entry **)value) = pblock->pb_post_op_entry;
+ break;
+
+ /* backend preoperation plugin */
+ case SLAPI_PLUGIN_BE_PRE_MODIFY_FN:
+ if (pblock->pb_plugin->plg_type != SLAPI_PLUGIN_BEPREOPERATION) {
+ return( -1 );
+ }
+ (*(IFP *)value) = pblock->pb_plugin->plg_bepremodify;
+ break;
+ case SLAPI_PLUGIN_BE_PRE_MODRDN_FN:
+ if (pblock->pb_plugin->plg_type != SLAPI_PLUGIN_BEPREOPERATION) {
+ return( -1 );
+ }
+ (*(IFP *)value) = pblock->pb_plugin->plg_bepremodrdn;
+ break;
+ case SLAPI_PLUGIN_BE_PRE_ADD_FN:
+ if (pblock->pb_plugin->plg_type != SLAPI_PLUGIN_BEPREOPERATION) {
+ return( -1 );
+ }
+ (*(IFP *)value) = pblock->pb_plugin->plg_bepreadd;
+ break;
+ case SLAPI_PLUGIN_BE_PRE_DELETE_FN:
+ if (pblock->pb_plugin->plg_type != SLAPI_PLUGIN_BEPREOPERATION) {
+ return( -1 );
+ }
+ (*(IFP *)value) = pblock->pb_plugin->plg_bepredelete;
+ break;
+
+ /* backend postoperation plugin */
+ case SLAPI_PLUGIN_BE_POST_MODIFY_FN:
+ if (pblock->pb_plugin->plg_type != SLAPI_PLUGIN_BEPOSTOPERATION) {
+ return( -1 );
+ }
+ (*(IFP *)value) = pblock->pb_plugin->plg_bepostmodify;
+ break;
+ case SLAPI_PLUGIN_BE_POST_MODRDN_FN:
+ if (pblock->pb_plugin->plg_type != SLAPI_PLUGIN_BEPOSTOPERATION) {
+ return( -1 );
+ }
+ (*(IFP *)value) = pblock->pb_plugin->plg_bepostmodrdn;
+ break;
+ case SLAPI_PLUGIN_BE_POST_ADD_FN:
+ if (pblock->pb_plugin->plg_type != SLAPI_PLUGIN_BEPOSTOPERATION) {
+ return( -1 );
+ }
+ (*(IFP *)value) = pblock->pb_plugin->plg_bepostadd;
+ break;
+ case SLAPI_PLUGIN_BE_POST_DELETE_FN:
+ if (pblock->pb_plugin->plg_type != SLAPI_PLUGIN_BEPOSTOPERATION) {
+ return( -1 );
+ }
+ (*(IFP *)value) = pblock->pb_plugin->plg_bepostdelete;
+ break;
+
+ /* internal preoperation plugin */
+ case SLAPI_PLUGIN_INTERNAL_PRE_MODIFY_FN:
+ if (pblock->pb_plugin->plg_type != SLAPI_PLUGIN_INTERNAL_PREOPERATION) {
+ return( -1 );
+ }
+ (*(IFP *)value) = pblock->pb_plugin->plg_internal_pre_modify;
+ break;
+ case SLAPI_PLUGIN_INTERNAL_PRE_MODRDN_FN:
+ if (pblock->pb_plugin->plg_type != SLAPI_PLUGIN_INTERNAL_PREOPERATION) {
+ return( -1 );
+ }
+ (*(IFP *)value) = pblock->pb_plugin->plg_internal_pre_modrdn;
+ break;
+ case SLAPI_PLUGIN_INTERNAL_PRE_ADD_FN:
+ if (pblock->pb_plugin->plg_type != SLAPI_PLUGIN_INTERNAL_PREOPERATION) {
+ return( -1 );
+ }
+ (*(IFP *)value) = pblock->pb_plugin->plg_internal_pre_add;
+ break;
+ case SLAPI_PLUGIN_INTERNAL_PRE_DELETE_FN:
+ if (pblock->pb_plugin->plg_type != SLAPI_PLUGIN_INTERNAL_PREOPERATION) {
+ return( -1 );
+ }
+ (*(IFP *)value) = pblock->pb_plugin->plg_internal_pre_delete;
+ break;
+
+ /* internal postoperation plugin */
+ case SLAPI_PLUGIN_INTERNAL_POST_MODIFY_FN:
+ if (pblock->pb_plugin->plg_type != SLAPI_PLUGIN_INTERNAL_POSTOPERATION) {
+ return( -1 );
+ }
+ (*(IFP *)value) = pblock->pb_plugin->plg_internal_post_modify;
+ break;
+ case SLAPI_PLUGIN_INTERNAL_POST_MODRDN_FN:
+ if (pblock->pb_plugin->plg_type != SLAPI_PLUGIN_INTERNAL_POSTOPERATION) {
+ return( -1 );
+ }
+ (*(IFP *)value) = pblock->pb_plugin->plg_internal_post_modrdn;
+ break;
+ case SLAPI_PLUGIN_INTERNAL_POST_ADD_FN:
+ if (pblock->pb_plugin->plg_type != SLAPI_PLUGIN_INTERNAL_POSTOPERATION) {
+ return( -1 );
+ }
+ (*(IFP *)value) = pblock->pb_plugin->plg_internal_post_add;
+ break;
+ case SLAPI_PLUGIN_INTERNAL_POST_DELETE_FN:
+ if (pblock->pb_plugin->plg_type != SLAPI_PLUGIN_INTERNAL_POSTOPERATION) {
+ return( -1 );
+ }
+ (*(IFP *)value) = pblock->pb_plugin->plg_internal_post_delete;
+ break;
+
+ /* target address & controls for all operations should be normalized */
+ case SLAPI_TARGET_ADDRESS:
+ if(pblock->pb_op!=NULL)
+ {
+ (*(entry_address **)value) = &(pblock->pb_op->o_params.target_address);
+ }
+ break;
+ /* should be normalized */
+ case SLAPI_TARGET_DN:
+ if(pblock->pb_op!=NULL)
+ {
+ (*(char **)value) = pblock->pb_op->o_params.target_address.dn;
+ }
+ break;
+ case SLAPI_ORIGINAL_TARGET_DN:
+ if(pblock->pb_op!=NULL)
+ {
+ (*(char **)value) = pblock->pb_op->o_params.target_address.udn;
+ }
+ break;
+ case SLAPI_TARGET_UNIQUEID:
+ if(pblock->pb_op!=NULL)
+ {
+ (*(char **)value) = pblock->pb_op->o_params.target_address.uniqueid;
+ }
+ break;
+ case SLAPI_REQCONTROLS:
+ (*(LDAPControl ***)value) = pblock->pb_op->o_params.request_controls;
+ break;
+ case SLAPI_RESCONTROLS:
+ (*(LDAPControl ***)value) = pblock->pb_op->o_results.result_controls;
+ break;
+ case SLAPI_CONTROLS_ARG: /* used to pass control argument before operation is created */
+ (*(LDAPControl ***)value) = pblock->pb_ctrls_arg;
+ break;
+ /* notes to be added to the access log RESULT line for this op. */
+ case SLAPI_OPERATION_NOTES:
+ (*(unsigned int *)value) = pblock->pb_operation_notes;
+ break;
+
+ /* syntax plugin functions */
+ case SLAPI_PLUGIN_SYNTAX_FILTER_AVA:
+ if ( pblock->pb_plugin->plg_type != SLAPI_PLUGIN_SYNTAX ) {
+ return( -1 );
+ }
+ (*(IFP *)value) = pblock->pb_plugin->plg_syntax_filter_ava;
+ break;
+ case SLAPI_PLUGIN_SYNTAX_FILTER_SUB:
+ if ( pblock->pb_plugin->plg_type != SLAPI_PLUGIN_SYNTAX ) {
+ return( -1 );
+ }
+ (*(IFP *)value) = pblock->pb_plugin->plg_syntax_filter_sub;
+ break;
+ case SLAPI_PLUGIN_SYNTAX_VALUES2KEYS:
+ if ( pblock->pb_plugin->plg_type != SLAPI_PLUGIN_SYNTAX ) {
+ return( -1 );
+ }
+ (*(IFP *)value) = pblock->pb_plugin->plg_syntax_values2keys;
+ break;
+ case SLAPI_PLUGIN_SYNTAX_ASSERTION2KEYS_AVA:
+ if ( pblock->pb_plugin->plg_type != SLAPI_PLUGIN_SYNTAX ) {
+ return( -1 );
+ }
+ (*(IFP *)value) = pblock->pb_plugin->plg_syntax_assertion2keys_ava;
+ break;
+ case SLAPI_PLUGIN_SYNTAX_ASSERTION2KEYS_SUB:
+ if ( pblock->pb_plugin->plg_type != SLAPI_PLUGIN_SYNTAX ) {
+ return( -1 );
+ }
+ (*(IFP *)value) = pblock->pb_plugin->plg_syntax_assertion2keys_sub;
+ break;
+ case SLAPI_PLUGIN_SYNTAX_NAMES:
+ if ( pblock->pb_plugin->plg_type != SLAPI_PLUGIN_SYNTAX ) {
+ return( -1 );
+ }
+ (*(char ***)value) = pblock->pb_plugin->plg_syntax_names;
+ break;
+ case SLAPI_PLUGIN_SYNTAX_OID:
+ if ( pblock->pb_plugin->plg_type != SLAPI_PLUGIN_SYNTAX ) {
+ return( -1 );
+ }
+ (*(char **)value) = pblock->pb_plugin->plg_syntax_oid;
+ break;
+ case SLAPI_PLUGIN_SYNTAX_FLAGS:
+ if ( pblock->pb_plugin->plg_type != SLAPI_PLUGIN_SYNTAX ) {
+ return( -1 );
+ }
+ (*(int *)value) = pblock->pb_plugin->plg_syntax_flags;
+ break;
+ case SLAPI_PLUGIN_SYNTAX_COMPARE:
+ if ( pblock->pb_plugin->plg_type != SLAPI_PLUGIN_SYNTAX ) {
+ return( -1 );
+ }
+ (*(IFP *)value) = pblock->pb_plugin->plg_syntax_compare;
+ break;
+
+ /* controls we know about */
+ case SLAPI_MANAGEDSAIT:
+ (*(int *)value) = pblock->pb_managedsait;
+ break;
+ case SLAPI_PWPOLICY:
+ (*(int *)value) = pblock->pb_pwpolicy_ctrl;
+ break;
+
+ /* add arguments */
+ case SLAPI_ADD_ENTRY:
+ if(pblock->pb_op!=NULL)
+ {
+ (*(Slapi_Entry **)value) = pblock->pb_op->o_params.p.p_add.target_entry;
+ }
+ break;
+ case SLAPI_ADD_EXISTING_DN_ENTRY:
+ (*(Slapi_Entry **)value) = pblock->pb_existing_dn_entry;
+ break;
+ case SLAPI_ADD_EXISTING_UNIQUEID_ENTRY:
+ (*(Slapi_Entry **)value) = pblock->pb_existing_uniqueid_entry;
+ break;
+ case SLAPI_ADD_PARENT_ENTRY:
+ (*(Slapi_Entry **)value) = pblock->pb_parent_entry;
+ break;
+ case SLAPI_ADD_PARENT_UNIQUEID:
+ if(pblock->pb_op!=NULL)
+ {
+ (*(char **)value) = pblock->pb_op->o_params.p.p_add.parentuniqueid;
+ }
+ break;
+
+ /* bind arguments */
+ case SLAPI_BIND_METHOD:
+ if(pblock->pb_op!=NULL)
+ {
+ (*(int *)value) = pblock->pb_op->o_params.p.p_bind.bind_method;
+ }
+ break;
+ case SLAPI_BIND_CREDENTIALS:
+ if(pblock->pb_op!=NULL)
+ {
+ (*(struct berval **)value) = pblock->pb_op->o_params.p.p_bind.bind_creds;
+ }
+ break;
+ case SLAPI_BIND_SASLMECHANISM:
+ if(pblock->pb_op!=NULL)
+ {
+ (*(char **)value) = pblock->pb_op->o_params.p.p_bind.bind_saslmechanism;
+ }
+ break;
+ /* bind return values */
+ case SLAPI_BIND_RET_SASLCREDS:
+ if(pblock->pb_op!=NULL)
+ {
+ (*(struct berval **)value) = pblock->pb_op->o_results.r.r_bind.bind_ret_saslcreds;
+ }
+ break;
+
+ /* compare arguments */
+ case SLAPI_COMPARE_TYPE:
+ if(pblock->pb_op!=NULL)
+ {
+ (*(char **)value) = pblock->pb_op->o_params.p.p_compare.compare_ava.ava_type;
+ }
+ break;
+ case SLAPI_COMPARE_VALUE:
+ if(pblock->pb_op!=NULL)
+ {
+ (*(struct berval **)value) = &pblock->pb_op->o_params.p.p_compare.compare_ava.ava_value;
+ }
+ break;
+
+ /* modify arguments */
+ case SLAPI_MODIFY_MODS:
+ PR_ASSERT(pblock->pb_op);
+ if(pblock->pb_op!=NULL)
+ {
+ if(pblock->pb_op->o_params.operation_type==SLAPI_OPERATION_MODIFY)
+ {
+ (*(LDAPMod ***)value) = pblock->pb_op->o_params.p.p_modify.modify_mods;
+ }
+ else if(pblock->pb_op->o_params.operation_type==SLAPI_OPERATION_MODRDN)
+ {
+ (*(LDAPMod ***)value) = pblock->pb_op->o_params.p.p_modrdn.modrdn_mods;
+ }
+ else
+ {
+ PR_ASSERT(0); /* JCM */
+ }
+ }
+ break;
+
+ /* modrdn arguments */
+ case SLAPI_MODRDN_NEWRDN:
+ if(pblock->pb_op!=NULL)
+ {
+ (*(char **)value) = pblock->pb_op->o_params.p.p_modrdn.modrdn_newrdn;
+ }
+ break;
+ case SLAPI_MODRDN_DELOLDRDN:
+ if(pblock->pb_op!=NULL)
+ {
+ (*(int *)value) = pblock->pb_op->o_params.p.p_modrdn.modrdn_deloldrdn;
+ }
+ break;
+ case SLAPI_MODRDN_NEWSUPERIOR:
+ if(pblock->pb_op!=NULL)
+ {
+ (*(char **)value) =
+ pblock->pb_op->o_params.p.p_modrdn.modrdn_newsuperior_address.dn;
+ }
+ break;
+ case SLAPI_MODRDN_PARENT_ENTRY:
+ (*(Slapi_Entry **)value) = pblock->pb_parent_entry;
+ break;
+ case SLAPI_MODRDN_NEWPARENT_ENTRY:
+ (*(Slapi_Entry **)value) = pblock->pb_newparent_entry;
+ break;
+ case SLAPI_MODRDN_TARGET_ENTRY:
+ (*(Slapi_Entry **)value) = pblock->pb_target_entry;
+ break;
+ case SLAPI_MODRDN_NEWSUPERIOR_ADDRESS:
+ if(pblock->pb_op!=NULL)
+ {
+ (*(entry_address **)value) = &(pblock->pb_op->o_params.p.p_modrdn.modrdn_newsuperior_address);
+ break;
+ }
+
+ /* search arguments */
+ case SLAPI_SEARCH_SCOPE:
+ if(pblock->pb_op!=NULL)
+ {
+ (*(int *)value) = pblock->pb_op->o_params.p.p_search.search_scope;
+ }
+ break;
+ case SLAPI_SEARCH_DEREF:
+ if(pblock->pb_op!=NULL)
+ {
+ (*(int *)value) = pblock->pb_op->o_params.p.p_search.search_deref;
+ }
+ break;
+ case SLAPI_SEARCH_SIZELIMIT:
+ if(pblock->pb_op!=NULL)
+ {
+ (*(int *)value) = pblock->pb_op->o_params.p.p_search.search_sizelimit;
+ }
+ break;
+ case SLAPI_SEARCH_TIMELIMIT:
+ if(pblock->pb_op!=NULL)
+ {
+ (*(int *)value) = pblock->pb_op->o_params.p.p_search.search_timelimit;
+ }
+ break;
+ case SLAPI_SEARCH_FILTER:
+ if(pblock->pb_op!=NULL)
+ {
+ (*(struct slapi_filter **)value) = pblock->pb_op->o_params.p.p_search.search_filter;
+ }
+ break;
+ case SLAPI_SEARCH_STRFILTER:
+ if(pblock->pb_op!=NULL)
+ {
+ (*(char **)value) = pblock->pb_op->o_params.p.p_search.search_strfilter;
+ }
+ break;
+ case SLAPI_SEARCH_ATTRS:
+ if(pblock->pb_op!=NULL)
+ {
+ (*(char ***)value) = pblock->pb_op->o_params.p.p_search.search_attrs;
+ }
+ break;
+ case SLAPI_SEARCH_ATTRSONLY:
+ if(pblock->pb_op!=NULL)
+ {
+ (*(int *)value) = pblock->pb_op->o_params.p.p_search.search_attrsonly;
+ }
+ break;
+ case SLAPI_SEARCH_IS_AND:
+ if(pblock->pb_op!=NULL)
+ {
+ (*(int *)value) = pblock->pb_op->o_params.p.p_search.search_is_and;
+ }
+ break;
+
+ case SLAPI_ABANDON_MSGID:
+ if(pblock->pb_op!=NULL)
+ {
+ (*(int *)value) = pblock->pb_op->o_params.p.p_abandon.abandon_targetmsgid;
+ }
+ break;
+
+ /* extended operation arguments */
+ case SLAPI_EXT_OP_REQ_OID:
+ if(pblock->pb_op!=NULL)
+ {
+ (*(char **) value) = pblock->pb_op->o_params.p.p_extended.exop_oid;
+ }
+ break;
+ case SLAPI_EXT_OP_REQ_VALUE:
+ if(pblock->pb_op!=NULL)
+ {
+ (*(struct berval **)value) = pblock->pb_op->o_params.p.p_extended.exop_value;
+ }
+ break;
+ /* extended operation return values */
+ case SLAPI_EXT_OP_RET_OID:
+ if(pblock->pb_op!=NULL)
+ {
+ (*(char **) value) = pblock->pb_op->o_results.r.r_extended.exop_ret_oid;
+ }
+ break;
+ case SLAPI_EXT_OP_RET_VALUE:
+ if(pblock->pb_op!=NULL)
+ {
+ (*(struct berval **)value) = pblock->pb_op->o_results.r.r_extended.exop_ret_value;
+ }
+ break;
+
+ /* matching rule plugin functions */
+ case SLAPI_PLUGIN_MR_FILTER_CREATE_FN:
+ SLAPI_PLUGIN_TYPE_CHECK (pblock, SLAPI_PLUGIN_MATCHINGRULE);
+ (*(IFP *)value) = pblock->pb_plugin->plg_mr_filter_create;
+ break;
+ case SLAPI_PLUGIN_MR_INDEXER_CREATE_FN:
+ SLAPI_PLUGIN_TYPE_CHECK (pblock, SLAPI_PLUGIN_MATCHINGRULE);
+ (*(IFP *)value) = pblock->pb_plugin->plg_mr_indexer_create;
+ break;
+ case SLAPI_PLUGIN_MR_FILTER_MATCH_FN:
+ (*(mrFilterMatchFn *)value) = pblock->pb_mr_filter_match_fn;
+ break;
+ case SLAPI_PLUGIN_MR_FILTER_INDEX_FN:
+ (*(IFP *)value) = pblock->pb_mr_filter_index_fn;
+ break;
+ case SLAPI_PLUGIN_MR_FILTER_RESET_FN:
+ (*(IFP *)value) = pblock->pb_mr_filter_reset_fn;
+ break;
+ case SLAPI_PLUGIN_MR_INDEX_FN:
+ (*(IFP *)value) = pblock->pb_mr_index_fn;
+ break;
+
+ /* matching rule plugin arguments */
+ case SLAPI_PLUGIN_MR_OID:
+ (*(char **) value) = pblock->pb_mr_oid;
+ break;
+ case SLAPI_PLUGIN_MR_TYPE:
+ (*(char **) value) = pblock->pb_mr_type;
+ break;
+ case SLAPI_PLUGIN_MR_VALUE:
+ (*(struct berval **) value) = pblock->pb_mr_value;
+ break;
+ case SLAPI_PLUGIN_MR_VALUES:
+ (*(struct berval ***) value) = pblock->pb_mr_values;
+ break;
+ case SLAPI_PLUGIN_MR_KEYS:
+ (*(struct berval ***) value) = pblock->pb_mr_keys;
+ break;
+ case SLAPI_PLUGIN_MR_FILTER_REUSABLE:
+ (*(unsigned int *) value) = pblock->pb_mr_filter_reusable;
+ break;
+ case SLAPI_PLUGIN_MR_QUERY_OPERATOR:
+ (*(int *) value) = pblock->pb_mr_query_operator;
+ break;
+ case SLAPI_PLUGIN_MR_USAGE:
+ (*(unsigned int *) value) = pblock->pb_mr_usage;
+ break;
+
+ /* seq arguments */
+ case SLAPI_SEQ_TYPE:
+ (*(int *)value) = pblock->pb_seq_type;
+ break;
+ case SLAPI_SEQ_ATTRNAME:
+ (*(char **)value) = pblock->pb_seq_attrname;
+ break;
+ case SLAPI_SEQ_VAL:
+ (*(char **)value) = pblock->pb_seq_val;
+ break;
+
+ /* ldif2db arguments */
+ case SLAPI_LDIF2DB_FILE:
+ (*(char ***)value) = pblock->pb_ldif_files;
+ break;
+ case SLAPI_LDIF2DB_REMOVEDUPVALS:
+ (*(int *)value) = pblock->pb_removedupvals;
+ break;
+ case SLAPI_DB2INDEX_ATTRS:
+ (*(char ***)value) = pblock->pb_db2index_attrs;
+ break;
+ case SLAPI_LDIF2DB_NOATTRINDEXES:
+ (*(int *)value) = pblock->pb_ldif2db_noattrindexes;
+ break;
+ case SLAPI_LDIF2DB_INCLUDE:
+ (*(char ***)value) = pblock->pb_ldif_include;
+ break;
+ case SLAPI_LDIF2DB_EXCLUDE:
+ (*(char ***)value) = pblock->pb_ldif_exclude;
+ break;
+ case SLAPI_LDIF2DB_GENERATE_UNIQUEID:
+ (*(int *)value) = pblock->pb_ldif_generate_uniqueid;
+ break;
+ case SLAPI_LDIF2DB_ENCRYPT:
+ case SLAPI_DB2LDIF_DECRYPT:
+ (*(int *)value) = pblock->pb_ldif_encrypt;
+ break;
+ case SLAPI_LDIF2DB_NAMESPACEID:
+ (*(char **)value) = pblock->pb_ldif_namespaceid;
+ break;
+
+ /* db2ldif arguments */
+ case SLAPI_DB2LDIF_PRINTKEY:
+ (*(int *)value) = pblock->pb_ldif_printkey;
+ break;
+ case SLAPI_DB2LDIF_DUMP_UNIQUEID:
+ (*(int *)value) = pblock->pb_ldif_dump_uniqueid;
+ break;
+ case SLAPI_DB2LDIF_FILE:
+ (*(char **)value) = pblock->pb_ldif_file;
+ break;
+
+ /* db2ldif/ldif2db/db2bak/bak2db arguments */
+ case SLAPI_BACKEND_INSTANCE_NAME:
+ (*(char **)value) = pblock->pb_instance_name;
+ break;
+ case SLAPI_BACKEND_TASK:
+ (*(Slapi_Task **)value) = pblock->pb_task;
+ break;
+ case SLAPI_TASK_FLAGS:
+ (*(int *)value) = pblock->pb_task_flags;
+ break;
+ case SLAPI_DB2LDIF_SERVER_RUNNING:
+ (*(int *)value) = pblock->pb_server_running;
+ break;
+ case SLAPI_BULK_IMPORT_ENTRY:
+ (*(Slapi_Entry **)value) = pblock->pb_import_entry;
+ break;
+ case SLAPI_BULK_IMPORT_STATE:
+ (*(int *)value) = pblock->pb_import_state;
+ break;
+
+ /* transaction arguments */
+ case SLAPI_PARENT_TXN:
+ (*(void **)value) = pblock->pb_parent_txn;
+ break;
+ case SLAPI_TXN:
+ (*(void **)value) = pblock->pb_txn;
+ break;
+
+ /* Search results set */
+ case SLAPI_SEARCH_RESULT_SET:
+ if(pblock->pb_op!=NULL)
+ {
+ (*(void **)value) = pblock->pb_op->o_results.r.r_search.search_result_set;
+ }
+ break;
+ /* Entry returned from iterating over results set */
+ case SLAPI_SEARCH_RESULT_ENTRY:
+ if(pblock->pb_op!=NULL)
+ {
+ (*(void **)value) = pblock->pb_op->o_results.r.r_search.search_result_entry;
+ }
+ break;
+ case SLAPI_SEARCH_RESULT_ENTRY_EXT:
+ if(pblock->pb_op!=NULL)
+ {
+ (*(void **)value) = pblock->pb_op->o_results.r.r_search.opaque_backend_ptr;
+ }
+ break;
+ /* Number of entries returned from search */
+ case SLAPI_NENTRIES:
+ if(pblock->pb_op!=NULL)
+ {
+ (*(int *)value) = pblock->pb_op->o_results.r.r_search.nentries;
+ }
+ break;
+ /* Referrals encountered while iterating over result set */
+ case SLAPI_SEARCH_REFERRALS:
+ if(pblock->pb_op!=NULL)
+ {
+ (*(struct berval ***)value) = pblock->pb_op->o_results.r.r_search.search_referrals;
+ }
+ break;
+
+ case SLAPI_RESULT_CODE:
+ if (pblock->pb_op != NULL)
+ * ((int *) value) = pblock->pb_op->o_results.result_code;
+ break;
+ case SLAPI_RESULT_MATCHED:
+ if (pblock->pb_op != NULL)
+ * ((char **) value) = pblock->pb_op->o_results.result_matched;
+ break;
+ case SLAPI_RESULT_TEXT:
+ if (pblock->pb_op != NULL)
+ * ((char **) value) = pblock->pb_op->o_results.result_text;
+ break;
+ case SLAPI_PB_RESULT_TEXT:
+ * ((char **) value) = pblock->pb_result_text;
+ break;
+
+ /* Size of the database, in kb */
+ case SLAPI_DBSIZE:
+ (*(unsigned int *)value) = pblock->pb_dbsize;
+ break;
+
+ /* ACL Plugin */
+ case SLAPI_PLUGIN_ACL_INIT:
+ (*(IFP *)value) = pblock->pb_plugin->plg_acl_init;
+ break;
+ case SLAPI_PLUGIN_ACL_SYNTAX_CHECK:
+ (*(IFP *)value) = pblock->pb_plugin->plg_acl_syntax_check;
+ break;
+ case SLAPI_PLUGIN_ACL_ALLOW_ACCESS:
+ (*(IFP *)value) = pblock->pb_plugin->plg_acl_access_allowed;
+ break;
+ case SLAPI_PLUGIN_ACL_MODS_ALLOWED:
+ (*(IFP *)value) = pblock->pb_plugin->plg_acl_mods_allowed;
+ break;
+ case SLAPI_PLUGIN_ACL_MODS_UPDATE:
+ (*(IFP *)value) = pblock->pb_plugin->plg_acl_mods_update;
+ break;
+
+ case SLAPI_REQUESTOR_DN:
+ /* NOTE: It's not a copy of the DN */
+ {
+ char *dn= (char*)slapi_sdn_get_dn(&pblock->pb_op->o_sdn);
+ if(dn==NULL)
+ (*( char **)value ) = "";
+ else
+ (*( char **)value ) = dn;
+ }
+ break;
+
+ case SLAPI_OPERATION_AUTHTYPE:
+ if(pblock->pb_op->o_authtype==NULL)
+ (*( char **)value ) = "";
+ else
+ (*( char **)value ) = pblock->pb_op->o_authtype;
+ break;
+
+ case SLAPI_CLIENT_DNS:
+ if (pblock->pb_conn == NULL) {
+ LDAPDebug( LDAP_DEBUG_ANY,
+ "Connection is NULL and hence cannot access SLAPI_CLIENT_DNS \n", 0, 0, 0 );
+ return (-1);
+ }
+ (*(struct berval ***)value ) = pblock->pb_conn->c_domain;
+ break;
+
+ case SLAPI_BE_MAXNESTLEVEL:
+ if ( NULL == be ) {
+ return( -1 );
+ }
+ (*(int *)value ) = be->be_maxnestlevel;
+ break;
+ case SLAPI_OPERATION_ID:
+ (*(int *)value ) = pblock->pb_op->o_opid;
+ break;
+ /* Command line arguments */
+ case SLAPI_ARGC:
+ (*(int *)value) = pblock->pb_slapd_argc;
+ break;
+ case SLAPI_ARGV:
+ (*(char ***)value) = pblock->pb_slapd_argv;
+ break;
+
+ /* Config file directory */
+ case SLAPI_CONFIG_DIRECTORY:
+ (*(char **)value) = pblock->pb_slapd_configdir;
+ break;
+
+ /* password storage scheme (kexcoff */
+ case SLAPI_PLUGIN_PWD_STORAGE_SCHEME_NAME:
+ (*(char **)value) = pblock->pb_plugin->plg_pwdstorageschemename;
+ break;
+ case SLAPI_PLUGIN_PWD_STORAGE_SCHEME_USER_PWD:
+ (*(char **)value) = pblock->pb_pwd_storage_scheme_user_passwd;
+ break;
+
+ case SLAPI_PLUGIN_PWD_STORAGE_SCHEME_DB_PWD:
+ (*(char **)value) = pblock->pb_pwd_storage_scheme_db_passwd;
+ break;
+
+ case SLAPI_PLUGIN_PWD_STORAGE_SCHEME_ENC_FN:
+ (*(CFP *)value) = pblock->pb_plugin->plg_pwdstorageschemeenc;
+ break;
+
+ case SLAPI_PLUGIN_PWD_STORAGE_SCHEME_DEC_FN:
+ (*(IFP *)value) = pblock->pb_plugin->plg_pwdstorageschemedec;
+ break;
+
+ case SLAPI_PLUGIN_PWD_STORAGE_SCHEME_CMP_FN:
+ (*(IFP *)value) = pblock->pb_plugin->plg_pwdstorageschemecmp;
+ break;
+
+ /* entry fetch/store plugin */
+ case SLAPI_PLUGIN_ENTRY_FETCH_FUNC:
+ (*(IFP *)value) = pblock->pb_plugin->plg_entryfetchfunc;
+ break;
+
+ case SLAPI_PLUGIN_ENTRY_STORE_FUNC:
+ (*(IFP *)value) = pblock->pb_plugin->plg_entrystorefunc;
+ break;
+
+ /* DSE add parameters */
+ case SLAPI_DSE_DONT_WRITE_WHEN_ADDING:
+ (*(int *)value) = pblock->pb_dse_dont_add_write;
+ break;
+
+ /* DSE add parameters */
+ case SLAPI_DSE_MERGE_WHEN_ADDING:
+ (*(int *)value) = pblock->pb_dse_add_merge;
+ break;
+
+ /* DSE add parameters */
+ case SLAPI_DSE_DONT_CHECK_DUPS:
+ (*(int *)value) = pblock->pb_dse_dont_check_dups;
+ break;
+
+ /* DSE modify parameters */
+ case SLAPI_DSE_REAPPLY_MODS:
+ (*(int *)value) = pblock->pb_dse_reapply_mods;
+ break;
+
+ /* DSE read parameters */
+ case SLAPI_DSE_IS_PRIMARY_FILE:
+ (*(int *)value) = pblock->pb_dse_is_primary_file;
+ break;
+
+ /* used internally by schema code */
+ case SLAPI_SCHEMA_USER_DEFINED_ONLY:
+ (*(int *)value) = pblock->pb_schema_user_defined_only;
+ break;
+
+ case SLAPI_URP_NAMING_COLLISION_DN:
+ (*(char **)value) = pblock->pb_urp_naming_collision_dn;
+ break;
+
+ case SLAPI_URP_TOMBSTONE_UNIQUEID:
+ (*(char **)value) = pblock->pb_urp_tombstone_uniqueid;
+ break;
+
+ default:
+ LDAPDebug( LDAP_DEBUG_ANY,
+ "Unknown parameter block argument %d\n", arg, 0, 0 );
+ return( -1 );
+ }
+
+ return( 0 );
+}
+
+int
+slapi_pblock_set( Slapi_PBlock *pblock, int arg, void *value )
+{
+ char *authtype;
+
+ PR_ASSERT( NULL != pblock );
+
+ switch ( arg ) {
+ case SLAPI_BACKEND:
+ pblock->pb_backend = (Slapi_Backend *) value;
+ break;
+ case SLAPI_BACKEND_COUNT:
+ pblock->pb_backend_count = *((int *) value);
+ break;
+ case SLAPI_CONNECTION:
+ pblock->pb_conn = (Connection *) value;
+ break;
+ case SLAPI_OPERATION:
+ pblock->pb_op = (Operation *) value;
+ break;
+ case SLAPI_OPINITIATED_TIME:
+ pblock->pb_op->o_time = *((time_t *) value);
+ break;
+ case SLAPI_REQUESTOR_ISROOT:
+ pblock->pb_requestor_isroot = *((int *) value);
+ break;
+ case SLAPI_IS_REPLICATED_OPERATION:
+ PR_ASSERT(0);
+ break;
+ case SLAPI_OPERATION_PARAMETERS:
+ PR_ASSERT(0);
+ break;
+ case SLAPI_CONN_ID:
+ if (pblock->pb_conn == NULL) {
+ LDAPDebug( LDAP_DEBUG_ANY,
+ "Connection is NULL and hence cannot access SLAPI_CONN_ID \n", 0, 0, 0 );
+ return (-1);
+ }
+ pblock->pb_conn->c_connid = *((int *) value);
+ break;
+ case SLAPI_CONN_DN:
+ /*
+ * Slightly crazy but we must pass a copy of the current
+ * authtype into bind_credentials_set() since it will
+ * free the current authtype.
+ */
+ if (pblock->pb_conn == NULL) {
+ LDAPDebug( LDAP_DEBUG_ANY,
+ "Connection is NULL and hence cannot access SLAPI_CONN_DN \n", 0, 0, 0 );
+ return (-1);
+ }
+ slapi_pblock_get(pblock, SLAPI_CONN_AUTHMETHOD, &authtype);
+ bind_credentials_set( pblock->pb_conn, authtype,
+ (char *)value, NULL, NULL, NULL , NULL);
+ slapi_ch_free((void**)&authtype);
+ break;
+ case SLAPI_CONN_AUTHTYPE: /* deprecated */
+ case SLAPI_CONN_AUTHMETHOD:
+ if (pblock->pb_conn == NULL) {
+ LDAPDebug( LDAP_DEBUG_ANY,
+ "Connection is NULL and hence cannot access SLAPI_CONN_AUTHMETHOD \n", 0, 0, 0 );
+ return (-1);
+ }
+ PR_Lock( pblock->pb_conn->c_mutex );
+ slapi_ch_free((void**)&pblock->pb_conn->c_authtype);
+ pblock->pb_conn->c_authtype = slapi_ch_strdup((char *) value);
+ PR_Unlock( pblock->pb_conn->c_mutex );
+ break;
+ case SLAPI_CONN_IS_REPLICATION_SESSION:
+ if (pblock->pb_conn == NULL) {
+ LDAPDebug( LDAP_DEBUG_ANY,
+ "Connection is NULL and hence cannot access SLAPI_CONN_IS_REPLICATION_SESSION \n", 0, 0, 0 );
+ return (-1);
+ }
+ PR_Lock( pblock->pb_conn->c_mutex );
+ pblock->pb_conn->c_isreplication_session = *((int *) value);
+ PR_Unlock( pblock->pb_conn->c_mutex );
+ break;
+
+ /* stuff related to config file processing */
+ case SLAPI_CONFIG_FILENAME:
+ case SLAPI_CONFIG_LINENO:
+ case SLAPI_CONFIG_ARGC:
+ case SLAPI_CONFIG_ARGV:
+ return (-1); /* deprecated since DS 5.0 (no longer useful) */
+
+ /* pblock memory management */
+ case SLAPI_DESTROY_CONTENT:
+ pblock->pb_destroy_content = *((int *) value);
+ break;
+
+ /* stuff related to the current plugin */
+ case SLAPI_PLUGIN:
+ pblock->pb_plugin = (struct slapdplugin *) value;
+ break;
+ case SLAPI_PLUGIN_PRIVATE:
+ pblock->pb_plugin->plg_private = (void *) value;
+ break;
+ case SLAPI_PLUGIN_TYPE:
+ pblock->pb_plugin->plg_type = *((int *) value);
+ break;
+ case SLAPI_PLUGIN_ARGV:
+ pblock->pb_plugin->plg_argv = (char **) value;
+ break;
+ case SLAPI_PLUGIN_ARGC:
+ pblock->pb_plugin->plg_argc = *((int *) value);
+ break;
+ case SLAPI_PLUGIN_VERSION:
+ pblock->pb_plugin->plg_version = (char *) value;
+ break;
+ case SLAPI_PLUGIN_OPRETURN:
+ pblock->pb_opreturn = *((int *) value);
+ break;
+ case SLAPI_PLUGIN_OBJECT:
+ pblock->pb_object = (void *) value;
+ break;
+ case SLAPI_PLUGIN_IDENTITY:
+ pblock->pb_plugin_identity = (void*)value;
+ break;
+ case SLAPI_PLUGIN_DESTROY_FN:
+ pblock->pb_destroy_fn = (IFP) value;
+ break;
+ case SLAPI_PLUGIN_DESCRIPTION:
+ pblock->pb_plugin->plg_desc = *((Slapi_PluginDesc *)value);
+ break;
+ case SLAPI_PLUGIN_INTOP_RESULT:
+ pblock->pb_internal_op_result = *((int *) value);
+ break;
+ case SLAPI_PLUGIN_INTOP_SEARCH_ENTRIES:
+ pblock->pb_plugin_internal_search_op_entries = (Slapi_Entry **) value;
+ break;
+ case SLAPI_PLUGIN_INTOP_SEARCH_REFERRALS:
+ pblock->pb_plugin_internal_search_op_referrals = (char **) value;
+ break;
+ /* database plugin functions */
+ case SLAPI_PLUGIN_DB_BIND_FN:
+ if ( pblock->pb_plugin->plg_type != SLAPI_PLUGIN_DATABASE ) {
+ return( -1 );
+ }
+ pblock->pb_plugin->plg_bind = (IFP) value;
+ break;
+ case SLAPI_PLUGIN_DB_UNBIND_FN:
+ if ( pblock->pb_plugin->plg_type != SLAPI_PLUGIN_DATABASE ) {
+ return( -1 );
+ }
+ pblock->pb_plugin->plg_unbind = (IFP) value;
+ break;
+ case SLAPI_PLUGIN_DB_SEARCH_FN:
+ if ( pblock->pb_plugin->plg_type != SLAPI_PLUGIN_DATABASE ) {
+ return( -1 );
+ }
+ pblock->pb_plugin->plg_search = (IFP) value;
+ break;
+ case SLAPI_PLUGIN_DB_NEXT_SEARCH_ENTRY_FN:
+ if ( pblock->pb_plugin->plg_type != SLAPI_PLUGIN_DATABASE ) {
+ return( -1 );
+ }
+ pblock->pb_plugin->plg_next_search_entry = (IFP) value;
+ break;
+ case SLAPI_PLUGIN_DB_NEXT_SEARCH_ENTRY_EXT_FN:
+ if ( pblock->pb_plugin->plg_type != SLAPI_PLUGIN_DATABASE ) {
+ return( -1 );
+ }
+ pblock->pb_plugin->plg_next_search_entry_ext = (IFP) value;
+ break;
+ case SLAPI_PLUGIN_DB_ENTRY_RELEASE_FN:
+ if ( pblock->pb_plugin->plg_type != SLAPI_PLUGIN_DATABASE ) {
+ return( -1 );
+ }
+ pblock->pb_plugin->plg_entry_release = (IFP) value;
+ break;
+ case SLAPI_PLUGIN_DB_COMPARE_FN:
+ if ( pblock->pb_plugin->plg_type != SLAPI_PLUGIN_DATABASE ) {
+ return( -1 );
+ }
+ pblock->pb_plugin->plg_compare = (IFP) value;
+ break;
+ case SLAPI_PLUGIN_DB_MODIFY_FN:
+ if ( pblock->pb_plugin->plg_type != SLAPI_PLUGIN_DATABASE ) {
+ return( -1 );
+ }
+ pblock->pb_plugin->plg_modify = (IFP) value;
+ break;
+ case SLAPI_PLUGIN_DB_MODRDN_FN:
+ if ( pblock->pb_plugin->plg_type != SLAPI_PLUGIN_DATABASE ) {
+ return( -1 );
+ }
+ pblock->pb_plugin->plg_modrdn = (IFP) value;
+ break;
+ case SLAPI_PLUGIN_DB_ADD_FN:
+ if ( pblock->pb_plugin->plg_type != SLAPI_PLUGIN_DATABASE ) {
+ return( -1 );
+ }
+ pblock->pb_plugin->plg_add = (IFP) value;
+ break;
+ case SLAPI_PLUGIN_DB_DELETE_FN:
+ if ( pblock->pb_plugin->plg_type != SLAPI_PLUGIN_DATABASE ) {
+ return( -1 );
+ }
+ pblock->pb_plugin->plg_delete = (IFP) value;
+ break;
+ case SLAPI_PLUGIN_DB_ABANDON_FN:
+ if ( pblock->pb_plugin->plg_type != SLAPI_PLUGIN_DATABASE ) {
+ return( -1 );
+ }
+ pblock->pb_plugin->plg_abandon = (IFP) value;
+ break;
+ case SLAPI_PLUGIN_DB_CONFIG_FN:
+ if ( pblock->pb_plugin->plg_type != SLAPI_PLUGIN_DATABASE ) {
+ return( -1 );
+ }
+ pblock->pb_plugin->plg_config = (IFP) value;
+ break;
+ case SLAPI_PLUGIN_CLOSE_FN:
+ pblock->pb_plugin->plg_close = (IFP) value;
+ break;
+ case SLAPI_PLUGIN_CLEANUP_FN:
+ pblock->pb_plugin->plg_cleanup = (IFP) value;
+ break;
+ case SLAPI_PLUGIN_DB_FLUSH_FN:
+ if ( pblock->pb_plugin->plg_type != SLAPI_PLUGIN_DATABASE ) {
+ return( -1 );
+ }
+ pblock->pb_plugin->plg_flush = (IFP) value;
+ break;
+ case SLAPI_PLUGIN_START_FN:
+ pblock->pb_plugin->plg_start = (IFP) value;
+ break;
+ case SLAPI_PLUGIN_POSTSTART_FN:
+ pblock->pb_plugin->plg_poststart = (IFP) value;
+ break;
+ case SLAPI_PLUGIN_DB_WIRE_IMPORT_FN:
+ pblock->pb_plugin->plg_wire_import = (IFP) value;
+ break;
+ case SLAPI_PLUGIN_DB_SEQ_FN:
+ if ( pblock->pb_plugin->plg_type != SLAPI_PLUGIN_DATABASE ) {
+ return( -1 );
+ }
+ pblock->pb_plugin->plg_seq = (IFP) value;
+ break;
+ case SLAPI_PLUGIN_DB_ENTRY_FN:
+ pblock->pb_plugin->plg_entry = (IFP) value;
+ break;
+ case SLAPI_PLUGIN_DB_REFERRAL_FN:
+ pblock->pb_plugin->plg_referral = (IFP) value;
+ break;
+ case SLAPI_PLUGIN_DB_RESULT_FN:
+ pblock->pb_plugin->plg_result = (IFP) value;
+ break;
+ case SLAPI_PLUGIN_DB_RMDB_FN:
+ if ( pblock->pb_plugin->plg_type != SLAPI_PLUGIN_DATABASE ) {
+ return( -1 );
+ }
+ pblock->pb_plugin->plg_rmdb = (IFP) value;
+ break;
+ case SLAPI_PLUGIN_DB_INIT_INSTANCE_FN:
+ if ( pblock->pb_plugin->plg_type != SLAPI_PLUGIN_DATABASE ) {
+ return( -1 );
+ }
+ pblock->pb_plugin->plg_init_instance = (IFP) value;
+ break;
+
+ case SLAPI_PLUGIN_DB_LDIF2DB_FN:
+ if ( pblock->pb_plugin->plg_type != SLAPI_PLUGIN_DATABASE ) {
+ return( -1 );
+ }
+ pblock->pb_plugin->plg_ldif2db = (IFP) value;
+ break;
+ case SLAPI_PLUGIN_DB_DB2LDIF_FN:
+ if ( pblock->pb_plugin->plg_type != SLAPI_PLUGIN_DATABASE ) {
+ return( -1 );
+ }
+ pblock->pb_plugin->plg_db2ldif = (IFP) value;
+ break;
+ case SLAPI_PLUGIN_DB_DB2INDEX_FN:
+ if ( pblock->pb_plugin->plg_type != SLAPI_PLUGIN_DATABASE ) {
+ return( -1 );
+ }
+ pblock->pb_plugin->plg_db2index = (IFP) value;
+ break;
+ case SLAPI_PLUGIN_DB_ARCHIVE2DB_FN:
+ if ( pblock->pb_plugin->plg_type != SLAPI_PLUGIN_DATABASE ) {
+ return( -1 );
+ }
+ pblock->pb_plugin->plg_archive2db = (IFP) value;
+ break;
+ case SLAPI_PLUGIN_DB_DB2ARCHIVE_FN:
+ if ( pblock->pb_plugin->plg_type != SLAPI_PLUGIN_DATABASE ) {
+ return( -1 );
+ }
+ pblock->pb_plugin->plg_db2archive = (IFP) value;
+ break;
+#if defined(UPGRADEDB)
+ case SLAPI_PLUGIN_DB_UPGRADEDB_FN:
+ if ( pblock->pb_plugin->plg_type != SLAPI_PLUGIN_DATABASE ) {
+ return( -1 );
+ }
+ pblock->pb_plugin->plg_upgradedb = (IFP) value;
+ break;
+#endif
+ case SLAPI_PLUGIN_DB_BEGIN_FN:
+ if ( pblock->pb_plugin->plg_type != SLAPI_PLUGIN_DATABASE ) {
+ return( -1 );
+ }
+ pblock->pb_plugin->plg_un.plg_un_db.plg_un_db_begin = (IFP) value;
+ break;
+ case SLAPI_PLUGIN_DB_COMMIT_FN:
+ if ( pblock->pb_plugin->plg_type != SLAPI_PLUGIN_DATABASE ) {
+ return( -1 );
+ }
+ pblock->pb_plugin->plg_un.plg_un_db.plg_un_db_commit = (IFP) value;
+ break;
+ case SLAPI_PLUGIN_DB_ABORT_FN:
+ if ( pblock->pb_plugin->plg_type != SLAPI_PLUGIN_DATABASE ) {
+ return( -1 );
+ }
+ pblock->pb_plugin->plg_un.plg_un_db.plg_un_db_abort = (IFP) value;
+ break;
+ case SLAPI_PLUGIN_DB_SIZE_FN:
+ if ( pblock->pb_plugin->plg_type != SLAPI_PLUGIN_DATABASE ) {
+ return( -1 );
+ }
+ pblock->pb_plugin->plg_un.plg_un_db.plg_un_db_dbsize = (IFP) value;
+ break;
+ case SLAPI_PLUGIN_DB_TEST_FN:
+ if ( pblock->pb_plugin->plg_type != SLAPI_PLUGIN_DATABASE ) {
+ return( -1 );
+ }
+ pblock->pb_plugin->plg_un.plg_un_db.plg_un_db_dbtest = (IFP) value;
+ break;
+ /* database plugin-specific parameters */
+ case SLAPI_PLUGIN_DB_NO_ACL:
+ if ( pblock->pb_plugin && pblock->pb_plugin->plg_type != SLAPI_PLUGIN_DATABASE ) {
+ return( -1 );
+ }
+ if ( NULL == pblock->pb_backend ) {
+ return( -1 );
+ }
+ pblock->pb_backend->be_noacl = *((int *)value);
+ break;
+
+
+ /* extendedop plugin functions */
+ case SLAPI_PLUGIN_EXT_OP_FN:
+ if ( pblock->pb_plugin->plg_type != SLAPI_PLUGIN_EXTENDEDOP ) {
+ return( -1 );
+ }
+ pblock->pb_plugin->plg_exhandler = (IFP) value;
+ break;
+ case SLAPI_PLUGIN_EXT_OP_OIDLIST:
+ if ( pblock->pb_plugin->plg_type != SLAPI_PLUGIN_EXTENDEDOP ) {
+ return( -1 );
+ }
+ pblock->pb_plugin->plg_exoids = (char **) value;
+ ldapi_register_extended_op( (char **)value );
+ break;
+ case SLAPI_PLUGIN_EXT_OP_NAMELIST:
+ if ( pblock->pb_plugin->plg_type != SLAPI_PLUGIN_EXTENDEDOP ) {
+ return( -1 );
+ }
+ pblock->pb_plugin->plg_exnames = (char **) value;
+ break;
+
+ /* preoperation plugin functions */
+ case SLAPI_PLUGIN_PRE_BIND_FN:
+ if (pblock->pb_plugin->plg_type != SLAPI_PLUGIN_PREOPERATION) {
+ return( -1 );
+ }
+ pblock->pb_plugin->plg_prebind = (IFP) value;
+ break;
+ case SLAPI_PLUGIN_PRE_UNBIND_FN:
+ if (pblock->pb_plugin->plg_type != SLAPI_PLUGIN_PREOPERATION) {
+ return( -1 );
+ }
+ pblock->pb_plugin->plg_preunbind = (IFP) value;
+ break;
+ case SLAPI_PLUGIN_PRE_SEARCH_FN:
+ if (pblock->pb_plugin->plg_type != SLAPI_PLUGIN_PREOPERATION) {
+ return( -1 );
+ }
+ pblock->pb_plugin->plg_presearch = (IFP) value;
+ break;
+ case SLAPI_PLUGIN_PRE_COMPARE_FN:
+ if (pblock->pb_plugin->plg_type != SLAPI_PLUGIN_PREOPERATION) {
+ return( -1 );
+ }
+ pblock->pb_plugin->plg_precompare = (IFP) value;
+ break;
+ case SLAPI_PLUGIN_PRE_MODIFY_FN:
+ if (pblock->pb_plugin->plg_type != SLAPI_PLUGIN_PREOPERATION) {
+ return( -1 );
+ }
+ pblock->pb_plugin->plg_premodify = (IFP) value;
+ break;
+ case SLAPI_PLUGIN_PRE_MODRDN_FN:
+ if (pblock->pb_plugin->plg_type != SLAPI_PLUGIN_PREOPERATION) {
+ return( -1 );
+ }
+ pblock->pb_plugin->plg_premodrdn = (IFP) value;
+ break;
+ case SLAPI_PLUGIN_PRE_ADD_FN:
+ if (pblock->pb_plugin->plg_type != SLAPI_PLUGIN_PREOPERATION) {
+ return( -1 );
+ }
+ pblock->pb_plugin->plg_preadd = (IFP) value;
+ break;
+ case SLAPI_PLUGIN_PRE_DELETE_FN:
+ if (pblock->pb_plugin->plg_type != SLAPI_PLUGIN_PREOPERATION) {
+ return( -1 );
+ }
+ pblock->pb_plugin->plg_predelete = (IFP) value;
+ break;
+ case SLAPI_PLUGIN_PRE_ABANDON_FN:
+ if (pblock->pb_plugin->plg_type != SLAPI_PLUGIN_PREOPERATION) {
+ return( -1 );
+ }
+ pblock->pb_plugin->plg_preabandon = (IFP) value;
+ break;
+ case SLAPI_PLUGIN_PRE_ENTRY_FN:
+ if (pblock->pb_plugin->plg_type != SLAPI_PLUGIN_PREOPERATION) {
+ return( -1 );
+ }
+ pblock->pb_plugin->plg_preentry = (IFP) value;
+ break;
+ case SLAPI_PLUGIN_PRE_REFERRAL_FN:
+ if (pblock->pb_plugin->plg_type != SLAPI_PLUGIN_PREOPERATION) {
+ return( -1 );
+ }
+ pblock->pb_plugin->plg_prereferral = (IFP) value;
+ break;
+ case SLAPI_PLUGIN_PRE_RESULT_FN:
+ if (pblock->pb_plugin->plg_type != SLAPI_PLUGIN_PREOPERATION) {
+ return( -1 );
+ }
+ pblock->pb_plugin->plg_preresult = (IFP) value;
+ break;
+
+ /* postoperation plugin functions */
+ case SLAPI_PLUGIN_POST_BIND_FN:
+ if (pblock->pb_plugin->plg_type != SLAPI_PLUGIN_POSTOPERATION) {
+ return( -1 );
+ }
+ pblock->pb_plugin->plg_postbind = (IFP) value;
+ break;
+ case SLAPI_PLUGIN_POST_UNBIND_FN:
+ if (pblock->pb_plugin->plg_type != SLAPI_PLUGIN_POSTOPERATION) {
+ return( -1 );
+ }
+ pblock->pb_plugin->plg_postunbind = (IFP) value;
+ break;
+ case SLAPI_PLUGIN_POST_SEARCH_FN:
+ if (pblock->pb_plugin->plg_type != SLAPI_PLUGIN_POSTOPERATION) {
+ return( -1 );
+ }
+ pblock->pb_plugin->plg_postsearch = (IFP) value;
+ break;
+ case SLAPI_PLUGIN_POST_SEARCH_FAIL_FN:
+ if (pblock->pb_plugin->plg_type != SLAPI_PLUGIN_POSTOPERATION) {
+ return( -1 );
+ }
+ pblock->pb_plugin->plg_postsearchfail = (IFP) value;
+ break;
+ case SLAPI_PLUGIN_POST_COMPARE_FN:
+ if (pblock->pb_plugin->plg_type != SLAPI_PLUGIN_POSTOPERATION) {
+ return( -1 );
+ }
+ pblock->pb_plugin->plg_postcompare = (IFP) value;
+ break;
+ case SLAPI_PLUGIN_POST_MODIFY_FN:
+ if (pblock->pb_plugin->plg_type != SLAPI_PLUGIN_POSTOPERATION) {
+ return( -1 );
+ }
+ pblock->pb_plugin->plg_postmodify = (IFP) value;
+ break;
+ case SLAPI_PLUGIN_POST_MODRDN_FN:
+ if (pblock->pb_plugin->plg_type != SLAPI_PLUGIN_POSTOPERATION) {
+ return( -1 );
+ }
+ pblock->pb_plugin->plg_postmodrdn = (IFP) value;
+ break;
+ case SLAPI_PLUGIN_POST_ADD_FN:
+ if (pblock->pb_plugin->plg_type != SLAPI_PLUGIN_POSTOPERATION) {
+ return( -1 );
+ }
+ pblock->pb_plugin->plg_postadd = (IFP) value;
+ break;
+ case SLAPI_PLUGIN_POST_DELETE_FN:
+ if (pblock->pb_plugin->plg_type != SLAPI_PLUGIN_POSTOPERATION) {
+ return( -1 );
+ }
+ pblock->pb_plugin->plg_postdelete = (IFP) value;
+ break;
+ case SLAPI_PLUGIN_POST_ABANDON_FN:
+ if (pblock->pb_plugin->plg_type != SLAPI_PLUGIN_POSTOPERATION) {
+ return( -1 );
+ }
+ pblock->pb_plugin->plg_postabandon = (IFP) value;
+ break;
+ case SLAPI_PLUGIN_POST_ENTRY_FN:
+ if (pblock->pb_plugin->plg_type != SLAPI_PLUGIN_POSTOPERATION) {
+ return( -1 );
+ }
+ pblock->pb_plugin->plg_postentry = (IFP) value;
+ break;
+ case SLAPI_PLUGIN_POST_REFERRAL_FN:
+ if (pblock->pb_plugin->plg_type != SLAPI_PLUGIN_POSTOPERATION) {
+ return( -1 );
+ }
+ pblock->pb_plugin->plg_postreferral = (IFP) value;
+ break;
+ case SLAPI_PLUGIN_POST_RESULT_FN:
+ if (pblock->pb_plugin->plg_type != SLAPI_PLUGIN_POSTOPERATION) {
+ return( -1 );
+ }
+ pblock->pb_plugin->plg_postresult = (IFP) value;
+ break;
+
+ /* backend preoperation plugin */
+ case SLAPI_PLUGIN_BE_PRE_MODIFY_FN:
+ if (pblock->pb_plugin->plg_type != SLAPI_PLUGIN_BEPREOPERATION) {
+ return( -1 );
+ }
+ pblock->pb_plugin->plg_bepremodify = (IFP) value;
+ break;
+ case SLAPI_PLUGIN_BE_PRE_MODRDN_FN:
+ if (pblock->pb_plugin->plg_type != SLAPI_PLUGIN_BEPREOPERATION) {
+ return( -1 );
+ }
+ pblock->pb_plugin->plg_bepremodrdn = (IFP) value;
+ break;
+ case SLAPI_PLUGIN_BE_PRE_ADD_FN:
+ if (pblock->pb_plugin->plg_type != SLAPI_PLUGIN_BEPREOPERATION) {
+ return( -1 );
+ }
+ pblock->pb_plugin->plg_bepreadd = (IFP) value;
+ break;
+ case SLAPI_PLUGIN_BE_PRE_DELETE_FN:
+ if (pblock->pb_plugin->plg_type != SLAPI_PLUGIN_BEPREOPERATION) {
+ return( -1 );
+ }
+ pblock->pb_plugin->plg_bepredelete = (IFP) value;
+ break;
+
+ /* backend postoperation plugin */
+ case SLAPI_PLUGIN_BE_POST_MODIFY_FN:
+ if (pblock->pb_plugin->plg_type != SLAPI_PLUGIN_BEPOSTOPERATION) {
+ return( -1 );
+ }
+ pblock->pb_plugin->plg_bepostmodify = (IFP) value;
+ break;
+ case SLAPI_PLUGIN_BE_POST_MODRDN_FN:
+ if (pblock->pb_plugin->plg_type != SLAPI_PLUGIN_BEPOSTOPERATION) {
+ return( -1 );
+ }
+ pblock->pb_plugin->plg_bepostmodrdn = (IFP) value;
+ break;
+ case SLAPI_PLUGIN_BE_POST_ADD_FN:
+ if (pblock->pb_plugin->plg_type != SLAPI_PLUGIN_BEPOSTOPERATION) {
+ return( -1 );
+ }
+ pblock->pb_plugin->plg_bepostadd = (IFP) value;
+ break;
+ case SLAPI_PLUGIN_BE_POST_DELETE_FN:
+ if (pblock->pb_plugin->plg_type != SLAPI_PLUGIN_BEPOSTOPERATION) {
+ return( -1 );
+ }
+ pblock->pb_plugin->plg_bepostdelete = (IFP) value;
+ break;
+
+
+ /* internal preoperation plugin */
+ case SLAPI_PLUGIN_INTERNAL_PRE_MODIFY_FN:
+ if (pblock->pb_plugin->plg_type != SLAPI_PLUGIN_INTERNAL_PREOPERATION) {
+ return( -1 );
+ }
+ pblock->pb_plugin->plg_internal_pre_modify = (IFP) value;
+ break;
+ case SLAPI_PLUGIN_INTERNAL_PRE_MODRDN_FN:
+ if (pblock->pb_plugin->plg_type != SLAPI_PLUGIN_INTERNAL_PREOPERATION) {
+ return( -1 );
+ }
+ pblock->pb_plugin->plg_internal_pre_modrdn = (IFP) value;
+ break;
+ case SLAPI_PLUGIN_INTERNAL_PRE_ADD_FN:
+ if (pblock->pb_plugin->plg_type != SLAPI_PLUGIN_INTERNAL_PREOPERATION) {
+ return( -1 );
+ }
+ pblock->pb_plugin->plg_internal_pre_add = (IFP) value;
+ break;
+ case SLAPI_PLUGIN_INTERNAL_PRE_DELETE_FN:
+ if (pblock->pb_plugin->plg_type != SLAPI_PLUGIN_INTERNAL_PREOPERATION) {
+ return( -1 );
+ }
+ pblock->pb_plugin->plg_internal_pre_delete = (IFP) value;
+ break;
+
+ /* internal postoperation plugin */
+ case SLAPI_PLUGIN_INTERNAL_POST_MODIFY_FN:
+ if (pblock->pb_plugin->plg_type != SLAPI_PLUGIN_INTERNAL_POSTOPERATION) {
+ return( -1 );
+ }
+ pblock->pb_plugin->plg_internal_post_modify = (IFP) value;
+ break;
+ case SLAPI_PLUGIN_INTERNAL_POST_MODRDN_FN:
+ if (pblock->pb_plugin->plg_type != SLAPI_PLUGIN_INTERNAL_POSTOPERATION) {
+ return( -1 );
+ }
+ pblock->pb_plugin->plg_internal_post_modrdn = (IFP) value;
+ break;
+ case SLAPI_PLUGIN_INTERNAL_POST_ADD_FN:
+ if (pblock->pb_plugin->plg_type != SLAPI_PLUGIN_INTERNAL_POSTOPERATION) {
+ return( -1 );
+ }
+ pblock->pb_plugin->plg_internal_post_add = (IFP) value;
+ break;
+ case SLAPI_PLUGIN_INTERNAL_POST_DELETE_FN:
+ if (pblock->pb_plugin->plg_type != SLAPI_PLUGIN_INTERNAL_POSTOPERATION) {
+ return( -1 );
+ }
+ pblock->pb_plugin->plg_internal_post_delete = (IFP) value;
+ break;
+
+ /* syntax plugin functions */
+ case SLAPI_PLUGIN_SYNTAX_FILTER_AVA:
+ if ( pblock->pb_plugin->plg_type != SLAPI_PLUGIN_SYNTAX ) {
+ return( -1 );
+ }
+ pblock->pb_plugin->plg_syntax_filter_ava = (IFP) value;
+ break;
+ case SLAPI_PLUGIN_SYNTAX_FILTER_SUB:
+ if ( pblock->pb_plugin->plg_type != SLAPI_PLUGIN_SYNTAX ) {
+ return( -1 );
+ }
+ pblock->pb_plugin->plg_syntax_filter_sub = (IFP) value;
+ break;
+ case SLAPI_PLUGIN_SYNTAX_VALUES2KEYS:
+ if ( pblock->pb_plugin->plg_type != SLAPI_PLUGIN_SYNTAX ) {
+ return( -1 );
+ }
+ pblock->pb_plugin->plg_syntax_values2keys = (IFP) value;
+ break;
+ case SLAPI_PLUGIN_SYNTAX_ASSERTION2KEYS_AVA:
+ if ( pblock->pb_plugin->plg_type != SLAPI_PLUGIN_SYNTAX ) {
+ return( -1 );
+ }
+ pblock->pb_plugin->plg_syntax_assertion2keys_ava = (IFP) value;
+ break;
+ case SLAPI_PLUGIN_SYNTAX_ASSERTION2KEYS_SUB:
+ if ( pblock->pb_plugin->plg_type != SLAPI_PLUGIN_SYNTAX ) {
+ return( -1 );
+ }
+ pblock->pb_plugin->plg_syntax_assertion2keys_sub = (IFP) value;
+ break;
+ case SLAPI_PLUGIN_SYNTAX_NAMES:
+ if ( pblock->pb_plugin->plg_type != SLAPI_PLUGIN_SYNTAX ) {
+ return( -1 );
+ }
+ pblock->pb_plugin->plg_syntax_names = (char **) value;
+ break;
+ case SLAPI_PLUGIN_SYNTAX_OID:
+ if ( pblock->pb_plugin->plg_type != SLAPI_PLUGIN_SYNTAX ) {
+ return( -1 );
+ }
+ pblock->pb_plugin->plg_syntax_oid = (char *) value;
+ break;
+ case SLAPI_PLUGIN_SYNTAX_FLAGS:
+ if ( pblock->pb_plugin->plg_type != SLAPI_PLUGIN_SYNTAX ) {
+ return( -1 );
+ }
+ pblock->pb_plugin->plg_syntax_flags = *((int *) value);
+ break;
+ case SLAPI_PLUGIN_SYNTAX_COMPARE:
+ if ( pblock->pb_plugin->plg_type != SLAPI_PLUGIN_SYNTAX ) {
+ return( -1 );
+ }
+ pblock->pb_plugin->plg_syntax_compare = (IFP) value;
+ break;
+ case SLAPI_ENTRY_PRE_OP:
+ pblock->pb_pre_op_entry = (Slapi_Entry *) value;
+ break;
+ case SLAPI_ENTRY_POST_OP:
+ pblock->pb_post_op_entry = (Slapi_Entry *) value;
+ break;
+
+ /* target address for all operations */
+ case SLAPI_TARGET_ADDRESS:
+ PR_ASSERT (PR_FALSE); /* can't do this */
+ break;
+ case SLAPI_TARGET_DN:
+ if(pblock->pb_op!=NULL)
+ {
+ pblock->pb_op->o_params.target_address.dn = (char *)value;
+ }
+ break;
+ case SLAPI_ORIGINAL_TARGET_DN:
+ if(pblock->pb_op!=NULL)
+ {
+ pblock->pb_op->o_params.target_address.udn = (char *)value;
+ }
+ break;
+ case SLAPI_TARGET_UNIQUEID:
+ if(pblock->pb_op!=NULL)
+ {
+ pblock->pb_op->o_params.target_address.uniqueid = (char *) value;
+ }
+ break;
+ case SLAPI_REQCONTROLS:
+ pblock->pb_op->o_params.request_controls = (LDAPControl **) value;
+ break;
+ case SLAPI_RESCONTROLS:
+ pblock->pb_op->o_results.result_controls = (LDAPControl **) value;
+ break;
+ case SLAPI_CONTROLS_ARG: /* used to pass control argument before operation is created */
+ pblock->pb_ctrls_arg = (LDAPControl **) value;
+ break;
+ case SLAPI_ADD_RESCONTROL:
+ add_control( &pblock->pb_op->o_results.result_controls, (LDAPControl *)value );
+ break;
+
+ /* notes to be added to the access log RESULT line for this op. */
+ case SLAPI_OPERATION_NOTES:
+ if ( value == NULL ) {
+ pblock->pb_operation_notes = 0; /* cleared */
+ } else {
+ pblock->pb_operation_notes |= *((unsigned int *)value );
+ }
+ break;
+
+ /* controls we know about */
+ case SLAPI_MANAGEDSAIT:
+ pblock->pb_managedsait = *((int *) value);
+ break;
+ case SLAPI_PWPOLICY:
+ pblock->pb_pwpolicy_ctrl = *((int *) value);
+ break;
+
+ /* add arguments */
+ case SLAPI_ADD_ENTRY:
+ if(pblock->pb_op!=NULL)
+ {
+ pblock->pb_op->o_params.p.p_add.target_entry = (Slapi_Entry *) value;
+ }
+ break;
+ case SLAPI_ADD_EXISTING_DN_ENTRY:
+ pblock->pb_existing_dn_entry = (Slapi_Entry *) value;
+ break;
+ case SLAPI_ADD_EXISTING_UNIQUEID_ENTRY:
+ pblock->pb_existing_uniqueid_entry = (Slapi_Entry *) value;
+ break;
+ case SLAPI_ADD_PARENT_ENTRY:
+ pblock->pb_parent_entry = (Slapi_Entry *) value;
+ break;
+ case SLAPI_ADD_PARENT_UNIQUEID:
+ if(pblock->pb_op!=NULL)
+ {
+ pblock->pb_op->o_params.p.p_add.parentuniqueid = (char *) value;
+ }
+ break;
+
+ /* bind arguments */
+ case SLAPI_BIND_METHOD:
+ if(pblock->pb_op!=NULL)
+ {
+ pblock->pb_op->o_params.p.p_bind.bind_method = *((int *) value);
+ }
+ break;
+ case SLAPI_BIND_CREDENTIALS:
+ if(pblock->pb_op!=NULL)
+ {
+ pblock->pb_op->o_params.p.p_bind.bind_creds = (struct berval *) value;
+ }
+ break;
+ case SLAPI_BIND_SASLMECHANISM:
+ if(pblock->pb_op!=NULL)
+ {
+ pblock->pb_op->o_params.p.p_bind.bind_saslmechanism = (char *) value;
+ }
+ break;
+ /* bind return values */
+ case SLAPI_BIND_RET_SASLCREDS:
+ if(pblock->pb_op!=NULL)
+ {
+ pblock->pb_op->o_results.r.r_bind.bind_ret_saslcreds = (struct berval *) value;
+ }
+ break;
+
+ /* compare arguments */
+ case SLAPI_COMPARE_TYPE:
+ if(pblock->pb_op!=NULL)
+ {
+ pblock->pb_op->o_params.p.p_compare.compare_ava.ava_type = (char *) value;
+ }
+ break;
+ case SLAPI_COMPARE_VALUE:
+ if(pblock->pb_op!=NULL)
+ {
+ pblock->pb_op->o_params.p.p_compare.compare_ava.ava_value = *((struct berval *) value);
+ }
+ break;
+
+ /* modify arguments */
+ case SLAPI_MODIFY_MODS:
+ PR_ASSERT(pblock->pb_op);
+ if(pblock->pb_op!=NULL)
+ {
+ if(pblock->pb_op->o_params.operation_type==SLAPI_OPERATION_MODIFY)
+ {
+ pblock->pb_op->o_params.p.p_modify.modify_mods = (LDAPMod **) value;
+ }
+ else if(pblock->pb_op->o_params.operation_type==SLAPI_OPERATION_MODRDN)
+ {
+ pblock->pb_op->o_params.p.p_modrdn.modrdn_mods = (LDAPMod **) value;
+ }
+ else
+ {
+ PR_ASSERT(0);
+ }
+ }
+ break;
+
+ /* modrdn arguments */
+ case SLAPI_MODRDN_NEWRDN:
+ if(pblock->pb_op!=NULL)
+ {
+ pblock->pb_op->o_params.p.p_modrdn.modrdn_newrdn = (char *) value;
+ }
+ break;
+ case SLAPI_MODRDN_DELOLDRDN:
+ if(pblock->pb_op!=NULL)
+ {
+ pblock->pb_op->o_params.p.p_modrdn.modrdn_deloldrdn = *((int *) value);
+ }
+ break;
+ case SLAPI_MODRDN_NEWSUPERIOR:
+ if(pblock->pb_op!=NULL)
+ {
+ pblock->pb_op->o_params.p.p_modrdn.modrdn_newsuperior_address.dn = (char *) value;
+ }
+ break;
+ case SLAPI_MODRDN_PARENT_ENTRY:
+ pblock->pb_parent_entry = (Slapi_Entry *) value;
+ break;
+ case SLAPI_MODRDN_NEWPARENT_ENTRY:
+ pblock->pb_newparent_entry = (Slapi_Entry *) value;
+ break;
+ case SLAPI_MODRDN_TARGET_ENTRY:
+ pblock->pb_target_entry = (Slapi_Entry *) value;
+ break;
+ case SLAPI_MODRDN_NEWSUPERIOR_ADDRESS:
+ PR_ASSERT (PR_FALSE); /* can't do this */
+
+ /* search arguments */
+ case SLAPI_SEARCH_SCOPE:
+ if(pblock->pb_op!=NULL)
+ {
+ pblock->pb_op->o_params.p.p_search.search_scope = *((int *) value);
+ }
+ break;
+ case SLAPI_SEARCH_DEREF:
+ if(pblock->pb_op!=NULL)
+ {
+ pblock->pb_op->o_params.p.p_search.search_deref = *((int *) value);
+ }
+ break;
+ case SLAPI_SEARCH_SIZELIMIT:
+ if(pblock->pb_op!=NULL)
+ {
+ pblock->pb_op->o_params.p.p_search.search_sizelimit = *((int *) value);
+ }
+ break;
+ case SLAPI_SEARCH_TIMELIMIT:
+ if(pblock->pb_op!=NULL)
+ {
+ pblock->pb_op->o_params.p.p_search.search_timelimit = *((int *) value);
+ }
+ break;
+ case SLAPI_SEARCH_FILTER:
+ if(pblock->pb_op!=NULL)
+ {
+ pblock->pb_op->o_params.p.p_search.search_filter = (struct slapi_filter *) value;
+ }
+ break;
+ case SLAPI_SEARCH_STRFILTER:
+ if(pblock->pb_op!=NULL)
+ {
+ pblock->pb_op->o_params.p.p_search.search_strfilter = (char *) value;
+ }
+ break;
+ case SLAPI_SEARCH_ATTRS:
+ if(pblock->pb_op!=NULL)
+ {
+ pblock->pb_op->o_params.p.p_search.search_attrs = (char **) value;
+ }
+ break;
+ case SLAPI_SEARCH_ATTRSONLY:
+ if(pblock->pb_op!=NULL)
+ {
+ pblock->pb_op->o_params.p.p_search.search_attrsonly = *((int *) value);
+ }
+ break;
+ case SLAPI_SEARCH_IS_AND:
+ if(pblock->pb_op!=NULL)
+ {
+ pblock->pb_op->o_params.p.p_search.search_is_and = *((int *) value);
+ }
+ break;
+
+ /* abandon operation arguments */
+ case SLAPI_ABANDON_MSGID:
+ if(pblock->pb_op!=NULL)
+ {
+ pblock->pb_op->o_params.p.p_abandon.abandon_targetmsgid = *((int *) value);
+ }
+ break;
+
+ /* extended operation arguments */
+ case SLAPI_EXT_OP_REQ_OID:
+ if(pblock->pb_op!=NULL)
+ {
+ pblock->pb_op->o_params.p.p_extended.exop_oid = (char *) value;
+ }
+ break;
+ case SLAPI_EXT_OP_REQ_VALUE:
+ if(pblock->pb_op!=NULL)
+ {
+ pblock->pb_op->o_params.p.p_extended.exop_value = (struct berval *) value;
+ }
+ break;
+ /* extended operation return values */
+ case SLAPI_EXT_OP_RET_OID:
+ if(pblock->pb_op!=NULL)
+ {
+ pblock->pb_op->o_results.r.r_extended.exop_ret_oid = (char *) value;
+ }
+ break;
+ case SLAPI_EXT_OP_RET_VALUE:
+ if(pblock->pb_op!=NULL)
+ {
+ pblock->pb_op->o_results.r.r_extended.exop_ret_value = (struct berval *) value;
+ }
+ break;
+
+ /* matching rule plugin functions */
+ case SLAPI_PLUGIN_MR_FILTER_CREATE_FN:
+ SLAPI_PLUGIN_TYPE_CHECK (pblock, SLAPI_PLUGIN_MATCHINGRULE);
+ pblock->pb_plugin->plg_mr_filter_create = (IFP) value;
+ break;
+ case SLAPI_PLUGIN_MR_INDEXER_CREATE_FN:
+ SLAPI_PLUGIN_TYPE_CHECK (pblock, SLAPI_PLUGIN_MATCHINGRULE);
+ pblock->pb_plugin->plg_mr_indexer_create = (IFP) value;
+ break;
+ case SLAPI_PLUGIN_MR_FILTER_MATCH_FN:
+ pblock->pb_mr_filter_match_fn = (mrFilterMatchFn) value;
+ break;
+ case SLAPI_PLUGIN_MR_FILTER_INDEX_FN:
+ pblock->pb_mr_filter_index_fn = (IFP) value;
+ break;
+ case SLAPI_PLUGIN_MR_FILTER_RESET_FN:
+ pblock->pb_mr_filter_reset_fn = (IFP) value;
+ break;
+ case SLAPI_PLUGIN_MR_INDEX_FN:
+ pblock->pb_mr_index_fn = (IFP) value;
+ break;
+
+ /* matching rule plugin arguments */
+ case SLAPI_PLUGIN_MR_OID:
+ pblock->pb_mr_oid = (char *) value;
+ break;
+ case SLAPI_PLUGIN_MR_TYPE:
+ pblock->pb_mr_type = (char *) value;
+ break;
+ case SLAPI_PLUGIN_MR_VALUE:
+ pblock->pb_mr_value = (struct berval *) value;
+ break;
+ case SLAPI_PLUGIN_MR_VALUES:
+ pblock->pb_mr_values = (struct berval **) value;
+ break;
+ case SLAPI_PLUGIN_MR_KEYS:
+ pblock->pb_mr_keys = (struct berval **) value;
+ break;
+ case SLAPI_PLUGIN_MR_FILTER_REUSABLE:
+ pblock->pb_mr_filter_reusable = *(unsigned int *) value;
+ break;
+ case SLAPI_PLUGIN_MR_QUERY_OPERATOR:
+ pblock->pb_mr_query_operator = *(int *) value;
+ break;
+ case SLAPI_PLUGIN_MR_USAGE:
+ pblock->pb_mr_usage = *(unsigned int *) value;
+ break;
+
+ /* seq arguments */
+ case SLAPI_SEQ_TYPE:
+ pblock->pb_seq_type = *((int *)value);
+ break;
+ case SLAPI_SEQ_ATTRNAME:
+ pblock->pb_seq_attrname = (char *) value;
+ break;
+ case SLAPI_SEQ_VAL:
+ pblock->pb_seq_val = (char *) value;
+ break;
+
+ /* ldif2db arguments */
+ case SLAPI_LDIF2DB_FILE:
+ pblock->pb_ldif_file = (char *) value;
+ break;
+ case SLAPI_LDIF2DB_REMOVEDUPVALS:
+ pblock->pb_removedupvals = *((int *)value);
+ break;
+ case SLAPI_DB2INDEX_ATTRS:
+ pblock->pb_db2index_attrs = (char **) value;
+ break;
+ case SLAPI_LDIF2DB_NOATTRINDEXES:
+ pblock->pb_ldif2db_noattrindexes = *((int *)value);
+ break;
+ case SLAPI_LDIF2DB_GENERATE_UNIQUEID:
+ pblock->pb_ldif_generate_uniqueid = *((int *)value);
+ break;
+ case SLAPI_LDIF2DB_NAMESPACEID:
+ pblock->pb_ldif_namespaceid = (char *)value;
+ break;
+
+ /* db2ldif arguments */
+ case SLAPI_DB2LDIF_PRINTKEY:
+ pblock->pb_ldif_printkey = *((int *)value);
+ break;
+ case SLAPI_DB2LDIF_DUMP_UNIQUEID:
+ pblock->pb_ldif_dump_uniqueid = *((int *)value);
+ break;
+
+ /* db2ldif/ldif2db/db2bak/bak2db arguments */
+ case SLAPI_BACKEND_INSTANCE_NAME:
+ pblock->pb_instance_name = (char *) value;
+ break;
+ case SLAPI_BACKEND_TASK:
+ pblock->pb_task = (Slapi_Task *)value;
+ break;
+ case SLAPI_TASK_FLAGS:
+ pblock->pb_task_flags = *((int *)value);
+ break;
+ case SLAPI_DB2LDIF_SERVER_RUNNING:
+ pblock->pb_server_running = *((int *)value);
+ break;
+ case SLAPI_BULK_IMPORT_ENTRY:
+ pblock->pb_import_entry = (Slapi_Entry *)value;
+ break;
+ case SLAPI_BULK_IMPORT_STATE:
+ pblock->pb_import_state = *((int *)value);
+ break;
+
+ /* transaction arguments */
+ case SLAPI_PARENT_TXN:
+ pblock->pb_parent_txn = (void *)value;
+ break;
+ case SLAPI_TXN:
+ pblock->pb_txn = (void *)value;
+ break;
+
+ /* Search results set */
+ case SLAPI_SEARCH_RESULT_SET:
+ if(pblock->pb_op!=NULL)
+ {
+ pblock->pb_op->o_results.r.r_search.search_result_set = (void *)value;
+ }
+ break;
+ /* Search result - entry returned from iterating over result set */
+ case SLAPI_SEARCH_RESULT_ENTRY:
+ if(pblock->pb_op!=NULL)
+ {
+ pblock->pb_op->o_results.r.r_search.search_result_entry = (void *)value;
+ }
+ break;
+ case SLAPI_SEARCH_RESULT_ENTRY_EXT:
+ if(pblock->pb_op!=NULL)
+ {
+ pblock->pb_op->o_results.r.r_search.opaque_backend_ptr = (void *)value;
+ }
+ break;
+ /* Number of entries returned from search */
+ case SLAPI_NENTRIES:
+ if(pblock->pb_op!=NULL)
+ {
+ pblock->pb_op->o_results.r.r_search.nentries = *((int *)value);
+ }
+ break;
+ /* Referrals encountered while iterating over the result set */
+ case SLAPI_SEARCH_REFERRALS:
+ if(pblock->pb_op!=NULL)
+ {
+ pblock->pb_op->o_results.r.r_search.search_referrals = (struct berval **)value;
+ }
+ break;
+
+ case SLAPI_RESULT_CODE:
+ if (pblock->pb_op != NULL)
+ pblock->pb_op->o_results.result_code = (* (int *) value);
+ break;
+ case SLAPI_RESULT_MATCHED:
+ if (pblock->pb_op != NULL)
+ pblock->pb_op->o_results.result_matched = (char *) value;
+ break;
+ case SLAPI_RESULT_TEXT:
+ if (pblock->pb_op != NULL)
+ pblock->pb_op->o_results.result_text = (char *) value;
+ break;
+ case SLAPI_PB_RESULT_TEXT:
+ slapi_ch_free((void**)&(pblock->pb_result_text));
+ pblock->pb_result_text = slapi_ch_strdup ((char *) value);
+ break;
+
+ /* Size of the database, in kb */
+ case SLAPI_DBSIZE:
+ pblock->pb_dbsize = *((unsigned int *)value);
+ break;
+
+ /* ACL Plugin */
+ case SLAPI_PLUGIN_ACL_INIT:
+ pblock->pb_plugin->plg_acl_init = (IFP) value;
+ break;
+
+ case SLAPI_PLUGIN_ACL_SYNTAX_CHECK:
+ pblock->pb_plugin->plg_acl_syntax_check = (IFP) value;
+ break;
+ case SLAPI_PLUGIN_ACL_ALLOW_ACCESS:
+ pblock->pb_plugin->plg_acl_access_allowed = (IFP) value;
+ break;
+ case SLAPI_PLUGIN_ACL_MODS_ALLOWED:
+ pblock->pb_plugin->plg_acl_mods_allowed = (IFP) value;
+ break;
+ case SLAPI_PLUGIN_ACL_MODS_UPDATE:
+ pblock->pb_plugin->plg_acl_mods_update = (IFP) value;
+ break;
+ case SLAPI_CLIENT_DNS:
+ if (pblock->pb_conn == NULL) {
+ LDAPDebug( LDAP_DEBUG_ANY,
+ "Connection is NULL and hence cannot access SLAPI_CLIENT_DNS \n", 0, 0, 0 );
+ return (-1);
+ }
+ pblock->pb_conn->c_domain = *((struct berval ***) value );
+ break;
+ /* Command line arguments */
+ case SLAPI_ARGC:
+ pblock->pb_slapd_argc= *((int *)value);
+ break;
+ case SLAPI_ARGV:
+ pblock->pb_slapd_argv = *((char***)value);
+ break;
+
+ /* Config file directory */
+ case SLAPI_CONFIG_DIRECTORY:
+ pblock->pb_slapd_configdir = (char *)value;
+ break;
+
+ /* password storage scheme (kexcoff) */
+ case SLAPI_PLUGIN_PWD_STORAGE_SCHEME_NAME:
+ pblock->pb_plugin->plg_pwdstorageschemename = (char *)value;
+ break;
+ case SLAPI_PLUGIN_PWD_STORAGE_SCHEME_USER_PWD:
+ pblock->pb_pwd_storage_scheme_user_passwd = (char *)value;
+ break;
+
+ case SLAPI_PLUGIN_PWD_STORAGE_SCHEME_DB_PWD:
+ pblock->pb_pwd_storage_scheme_db_passwd = (char *)value;
+ break;
+
+ case SLAPI_PLUGIN_PWD_STORAGE_SCHEME_ENC_FN:
+ pblock->pb_plugin->plg_pwdstorageschemeenc = (CFP)value;
+ break;
+
+ case SLAPI_PLUGIN_PWD_STORAGE_SCHEME_DEC_FN:
+ pblock->pb_plugin->plg_pwdstorageschemedec = (IFP)value;
+ break;
+
+ case SLAPI_PLUGIN_PWD_STORAGE_SCHEME_CMP_FN:
+ /* must provide a comparison function */
+ if ( value == NULL )
+ {
+ return(-1);
+ }
+ pblock->pb_plugin->plg_pwdstorageschemecmp = (IFP)value;
+ break;
+
+ /* entry fetch store */
+ case SLAPI_PLUGIN_ENTRY_FETCH_FUNC:
+ pblock->pb_plugin->plg_entryfetchfunc = (IFP) value;
+ break;
+
+ case SLAPI_PLUGIN_ENTRY_STORE_FUNC:
+ pblock->pb_plugin->plg_entrystorefunc = (IFP) value;
+ break;
+
+ /* DSE add parameters */
+ case SLAPI_DSE_DONT_WRITE_WHEN_ADDING:
+ pblock->pb_dse_dont_add_write = *((int *)value);
+ break;
+
+ /* DSE add parameters */
+ case SLAPI_DSE_MERGE_WHEN_ADDING:
+ pblock->pb_dse_add_merge = *((int *)value);
+ break;
+
+ /* DSE add parameters */
+ case SLAPI_DSE_DONT_CHECK_DUPS:
+ pblock->pb_dse_dont_check_dups = *((int *)value);
+ break;
+
+ /* DSE modify parameters */
+ case SLAPI_DSE_REAPPLY_MODS:
+ pblock->pb_dse_reapply_mods = *((int *)value);
+ break;
+
+ /* DSE read parameters */
+ case SLAPI_DSE_IS_PRIMARY_FILE:
+ pblock->pb_dse_is_primary_file = *((int *)value);
+ break;
+
+ /* used internally by schema code only */
+ case SLAPI_SCHEMA_USER_DEFINED_ONLY:
+ pblock->pb_schema_user_defined_only = *((int *)value);
+ break;
+
+ case SLAPI_URP_NAMING_COLLISION_DN:
+ pblock->pb_urp_naming_collision_dn = (char *)value;
+ break;
+
+ case SLAPI_URP_TOMBSTONE_UNIQUEID:
+ pblock->pb_urp_tombstone_uniqueid = (char *)value;
+ break;
+
+ case SLAPI_LDIF2DB_ENCRYPT:
+ case SLAPI_DB2LDIF_DECRYPT:
+ pblock->pb_ldif_encrypt = (int)value;
+ break;
+
+ default:
+ LDAPDebug( LDAP_DEBUG_ANY,
+ "Unknown parameter block argument %d\n", arg, 0, 0 );
+ return( -1 );
+ }
+
+ return( 0 );
+}
+
+
+/*
+ * Clears (and free's as appropriate) the bind DN and related credentials
+ * for the connection `conn'.
+ *
+ * If `lock_conn' is true, 'conn' is locked before touching it; otherwise
+ * this function assumes that conn->c_mutex is ALREADY locked.
+ *
+ * If `clear_externalcreds' is true, the external DN, external authtype,
+ * and client certificate are also cleared and free'd.
+ *
+ * Connection structure members that are potentially changed by this function:
+ * c_dn, c_isroot, c_authtype
+ * c_external_dn, c_external_authtype, c_client_cert
+ *
+ * This function might better belong on bind.c or perhaps connection.c but
+ * it needs to be in libslapd because FE code and libslapd code calls it.
+ */
+void
+bind_credentials_clear( Connection *conn, PRBool lock_conn,
+ PRBool clear_externalcreds )
+{
+ if ( lock_conn ) {
+ PR_Lock( conn->c_mutex );
+ }
+
+ if ( conn->c_dn != NULL ) { /* a non-anonymous bind has occurred */
+ reslimit_update_from_entry( conn, NULL ); /* clear resource limits */
+
+ if ( conn->c_dn != conn->c_external_dn ) {
+ slapi_ch_free((void**)&conn->c_dn);
+ }
+ conn->c_dn = NULL;
+ }
+ slapi_ch_free((void**)&conn->c_authtype);
+ conn->c_isroot = 0;
+ conn->c_authtype = slapi_ch_strdup(SLAPD_AUTH_NONE);
+
+ if ( clear_externalcreds ) {
+ slapi_ch_free( (void**)&conn->c_external_dn );
+ conn->c_external_dn = NULL;
+ conn->c_external_authtype = SLAPD_AUTH_NONE;
+ if ( conn->c_client_cert ) {
+ CERT_DestroyCertificate (conn->c_client_cert);
+ conn->c_client_cert = NULL;
+ }
+ }
+
+ if ( lock_conn ) {
+ PR_Unlock( conn->c_mutex );
+ }
+
+}
+
+
+/*
+ * Clear and then set the bind DN and related credentials for the
+ * connection `conn'.
+ *
+ * `authtype' should be one of the SLAPD_AUTH_... constants defined in
+ * slapu-plugin.h or NULL.
+ *
+ * `normdn' must be a normalized DN and it must be malloc'd memory (it
+ * is consumed by this function). If there is an existing DN value
+ * associated with the connection, it is free'd. Pass NULL for `normdn'
+ * to clear the DN.
+ *
+ * If `extauthtype' is non-NULL we also clear and then set the
+ * external (e.g., SSL) credentials from the `externaldn' and `clientcert'.
+ * Note that it is okay for `externaldn' and 'normdn' to have the same
+ * (pointer) value. This code and that in bind_credentials_clear()
+ * is smart enough to know to only free the memory once. Like `normdn',
+ * `externaldn' and `clientcert' should be NULL or point to malloc'd memory
+ * as they are both consumed by this function.
+ *
+ * We also:
+ *
+ * 1) Test to see if the DN is the root DN and set the c_isroot flag
+ * appropriately.
+ * And
+ *
+ * 2) Call the binder-based resource limits subsystem so it can
+ * update the per-connection resource limit data it maintains.
+ *
+ * Note that this function should ALWAYS be used instead of manipulating
+ * conn->c_dn directly; otherwise, subsystems like the binder-based resource
+ * limits (see resourcelimit.c) won't be called.
+ *
+ * It is also acceptable to set the DN via a call slapi_pblock_set(), e.g.,
+ *
+ * slapi_pblock_set( pb, SLAPI_CONN_DN, ndn );
+ *
+ * because it calls this function.
+ *
+ * Connection structure members that are potentially changed by this function:
+ * c_dn, c_isroot, c_authtype
+ * c_external_dn, c_external_authtype, c_client_cert
+ *
+ * This function might better belong on bind.c or perhaps connection.c but
+ * it needs to be in libslapd because FE code and libslapd code calls it.
+ */
+void
+bind_credentials_set( Connection *conn, char *authtype, char *normdn,
+ char *extauthtype, char *externaldn, CERTCertificate *clientcert, Slapi_Entry * bind_target_entry )
+{
+ PR_Lock( conn->c_mutex );
+
+ /* clear credentials */
+ bind_credentials_clear( conn, PR_FALSE /* conn is already locked */,
+ ( extauthtype != NULL ) /* clear external creds. if requested */ );
+
+ /* set primary credentials */
+ slapi_ch_free((void**)&conn->c_authtype);
+ conn->c_authtype = slapi_ch_strdup(authtype);
+ conn->c_dn = normdn;
+ conn->c_isroot = slapi_dn_isroot( normdn );
+
+
+ /* set external credentials if requested */
+ if ( extauthtype != NULL ) {
+ conn->c_external_authtype = extauthtype;
+ conn->c_external_dn = externaldn;
+ conn->c_client_cert = clientcert;
+ }
+
+
+ /* notify binder-based resource limit subsystem about the change in DN */
+ if ( !conn->c_isroot )
+ {
+ if ( conn->c_dn != NULL ) {
+ if ( bind_target_entry == NULL )
+ {
+ Slapi_DN *sdn;
+
+ sdn = slapi_sdn_new_dn_byref( conn->c_dn ); /* set */
+ reslimit_update_from_dn( conn, sdn );
+ slapi_sdn_free( &sdn );
+ }
+ else
+ reslimit_update_from_entry( conn, bind_target_entry );
+ }
+ }
+
+ PR_Unlock( conn->c_mutex );
+}
diff --git a/ldap/servers/slapd/plugin.c b/ldap/servers/slapd/plugin.c
new file mode 100644
index 00000000..5477f3e2
--- /dev/null
+++ b/ldap/servers/slapd/plugin.c
@@ -0,0 +1,2833 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+/* plugin.c - routines for setting up and calling plugins */
+
+#include <stddef.h>
+#include <stdio.h>
+#include <plhash.h>
+#include "slap.h"
+
+/* this defines are used for plugin configuration */
+#define LOCAL_DATA "local data"
+#define REMOTE_DATA "remote data"
+#define ALL_DATA "*"
+#define ROOT_BIND "directory manager"
+#define ANONYMOUS_BIND "anonymous"
+
+/* Forward Declarations */
+static int plugin_call_list (struct slapdplugin *list, int operation, Slapi_PBlock *pb);
+static int plugin_call_one (struct slapdplugin *list, int operation, Slapi_PBlock *pb);
+static int plugin_call_func (struct slapdplugin *list, int operation, Slapi_PBlock *pb, int call_one);
+
+static PRBool plugin_invoke_plugin_pb (struct slapdplugin *plugin, int operation, Slapi_PBlock *pb);
+static PRBool plugin_matches_operation (Slapi_DN *target_spec, PluginTargetData *ptd,
+ PRBool bindop, PRBool isroot, PRBool islocal, int method);
+static int isApprovedPlugin( struct slapdplugin *plugin );
+
+static void plugin_config_init (struct pluginconfig *config);
+static void plugin_config_cleanup (struct pluginconfig *config);
+static int plugin_config_set_action (int *action, char *value);
+static struct pluginconfig* plugin_get_config (struct slapdplugin *plugin);
+static void default_plugin_init();
+static void ptd_init (PluginTargetData *ptd);
+static void ptd_cleanup (PluginTargetData *ptd);
+static void ptd_add_subtree (PluginTargetData *ptd, Slapi_DN *subtree);
+static void ptd_set_special_data (PluginTargetData *ptd, int type);
+static Slapi_DN *ptd_get_first_subtree (const PluginTargetData *ptd, int *cookie);
+static Slapi_DN *ptd_get_next_subtree (const PluginTargetData *ptd, int *cookie);
+static PRBool ptd_is_special_data_set (const PluginTargetData *ptd, int type);
+int ptd_get_subtree_count (const PluginTargetData *ptd);
+static void plugin_set_global (PluginTargetData *ptd);
+static PRBool plugin_is_global (const PluginTargetData *ptd);
+static void plugin_set_default_access (struct pluginconfig *config);
+
+static PLHashTable *global_plugin_dns = NULL;
+
+/* The global plugin list is indexed by the PLUGIN_LIST_* constants defined in slap.h */
+static struct slapdplugin *global_plugin_list[PLUGIN_LIST_GLOBAL_MAX];
+
+/* plugin structure used to configure internal operation issued by the core server */
+static int global_server_plg_initialised= 0;
+struct slapdplugin global_server_plg;
+
+/* plugin structure used to configure internal operation issued by the core server */
+static int global_server_plg_id_initialised= 0;
+struct slapi_componentid global_server_id_plg;
+
+/* plugin structure used to configure operations issued by the old plugins that
+ do not pass their identity in the operation */
+static struct slapdplugin global_default_plg;
+
+/* Enable/disable plugin callbacks for clean startup */
+static int global_plugin_callbacks_enabled = 0;
+
+
+static void
+add_plugin_to_list(struct slapdplugin **list, struct slapdplugin *plugin)
+{
+ struct slapdplugin **tmp;
+ for ( tmp = list; *tmp; tmp = &(*tmp)->plg_next )
+ {
+ ; /* NULL */
+ }
+ *tmp = plugin;
+}
+
+struct slapdplugin *
+get_plugin_list(int plugin_list_index)
+{
+ return global_plugin_list[plugin_list_index];
+}
+
+/*
+ * As the plugin configuration information is read an array of
+ * entries is built which reflect the plugins. The entries
+ * are added after the syntax plugins are started so that the
+ * nodes in the attribute tree are initialised correctly.
+ */
+typedef struct entry_and_plugin {
+ Slapi_Entry *e;
+ struct slapdplugin *plugin;
+ struct entry_and_plugin *next;
+} entry_and_plugin_t;
+
+static entry_and_plugin_t *plugin_entries = NULL;
+static entry_and_plugin_t *dep_plugin_entries = NULL; /* for dependencies */
+
+#if 0
+static void
+add_plugin_entries()
+{
+ entry_and_plugin_t *ep = plugin_entries;
+ entry_and_plugin_t *deleteep = 0;
+ while (ep)
+ {
+ int plugin_actions = 0;
+ Slapi_PBlock newpb;
+ pblock_init(&newpb);
+ slapi_add_entry_internal_set_pb(&newpb, ep->e, NULL,
+ ep->plugin, plugin_actions);
+ slapi_pblock_set(&newpb, SLAPI_TARGET_DN, (void*)slapi_entry_get_dn_const(ep->e));
+ slapi_add_internal_pb(&newpb);
+ deleteep = ep;
+ ep = ep->next;
+ slapi_ch_free((void**)&deleteep);
+ pblock_done(&newpb);
+ }
+
+ plugin_entries = NULL;
+}
+#endif
+
+static void
+new_plugin_entry(entry_and_plugin_t **ep, Slapi_Entry *e, struct slapdplugin *plugin)
+{
+ entry_and_plugin_t *oldep = 0;
+ entry_and_plugin_t *iterep = *ep;
+
+ entry_and_plugin_t *newep =
+ (entry_and_plugin_t*)slapi_ch_calloc(1,sizeof(entry_and_plugin_t));
+ newep->e = e;
+ newep->plugin = plugin;
+
+ while(iterep)
+ {
+ oldep = iterep;
+ iterep = iterep->next;
+ }
+
+ newep->next = 0;
+
+ if(oldep)
+ oldep->next = newep;
+ else
+ *ep = newep;
+}
+
+
+static void
+add_plugin_entry_dn(const Slapi_DN *plugin_dn)
+{
+ if (!global_plugin_dns)
+ {
+ global_plugin_dns = PL_NewHashTable(20, PL_HashString,
+ PL_CompareStrings,
+ PL_CompareValues, 0, 0);
+ }
+
+ PL_HashTableAdd(global_plugin_dns,
+ slapi_sdn_get_ndn(plugin_dn),
+ (void*)plugin_dn);
+}
+
+#define SLAPI_PLUGIN_NONE_IF_NULL( s ) ((s) == NULL ? "none" : (s))
+
+/*
+ * Allows a plugin to register a plugin.
+ * This was added so that 'object' plugins could register all
+ * the plugin interfaces that it supports.
+ */
+int
+slapi_register_plugin(
+ const char *plugintype,
+ int enabled,
+ const char *initsymbol,
+ slapi_plugin_init_fnptr initfunc,
+ const char *name,
+ char **argv,
+ void *group_identity
+)
+{
+ int ii = 0;
+ int rc = 0;
+ Slapi_Entry *e = slapi_entry_alloc();
+ char *dn = slapi_ch_calloc(1, strlen(name) + strlen(PLUGIN_BASE_DN) + 10);
+
+ sprintf(dn, "cn=%s, %s", name, PLUGIN_BASE_DN);
+ /* this function consumes dn */
+ slapi_entry_init(e, dn, NULL);
+
+ slapi_entry_attr_set_charptr(e, "cn", name);
+ slapi_entry_attr_set_charptr(e, ATTR_PLUGIN_TYPE, plugintype);
+ if (!enabled)
+ slapi_entry_attr_set_charptr(e, ATTR_PLUGIN_ENABLED, "off");
+
+ slapi_entry_attr_set_charptr(e, ATTR_PLUGIN_INITFN, initsymbol);
+
+ for (ii = 0; argv && argv[ii]; ++ii) {
+ char argname[64];
+ sprintf(argname, "%s%d", ATTR_PLUGIN_ARG, ii);
+ slapi_entry_attr_set_charptr(e, argname, argv[ii]);
+ }
+
+ /* plugin_setup copies the given entry */
+ plugin_setup(e, group_identity, initfunc, 0);
+ slapi_entry_free(e);
+
+ return rc;
+}
+
+int
+plugin_call_plugins( Slapi_PBlock *pb, int whichfunction )
+{
+ int plugin_list_number= -1;
+ int rc= 0;
+ int do_op = global_plugin_callbacks_enabled;
+
+ if ( pb == NULL )
+ {
+ return( 0 );
+ }
+
+ switch ( whichfunction ) {
+ 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:
+ plugin_list_number= PLUGIN_LIST_PREOPERATION;
+ break;
+ case SLAPI_PLUGIN_POST_BIND_FN:
+ case SLAPI_PLUGIN_POST_UNBIND_FN:
+ case SLAPI_PLUGIN_POST_SEARCH_FN:
+ case SLAPI_PLUGIN_POST_SEARCH_FAIL_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:
+ plugin_list_number= PLUGIN_LIST_POSTOPERATION;
+ break;
+ case SLAPI_PLUGIN_BE_PRE_MODIFY_FN:
+ case SLAPI_PLUGIN_BE_PRE_MODRDN_FN:
+ case SLAPI_PLUGIN_BE_PRE_ADD_FN:
+ case SLAPI_PLUGIN_BE_PRE_DELETE_FN:
+ plugin_list_number= PLUGIN_LIST_BEPREOPERATION;
+ do_op = 1; /* always allow backend callbacks (even during startup) */
+ break;
+ case SLAPI_PLUGIN_BE_POST_MODIFY_FN:
+ case SLAPI_PLUGIN_BE_POST_MODRDN_FN:
+ case SLAPI_PLUGIN_BE_POST_ADD_FN:
+ case SLAPI_PLUGIN_BE_POST_DELETE_FN:
+ plugin_list_number= PLUGIN_LIST_BEPOSTOPERATION;
+ do_op = 1; /* always allow backend callbacks (even during startup) */
+ break;
+ case SLAPI_PLUGIN_INTERNAL_PRE_MODIFY_FN:
+ case SLAPI_PLUGIN_INTERNAL_PRE_MODRDN_FN:
+ case SLAPI_PLUGIN_INTERNAL_PRE_ADD_FN:
+ case SLAPI_PLUGIN_INTERNAL_PRE_DELETE_FN:
+ plugin_list_number= PLUGIN_LIST_INTERNAL_PREOPERATION;
+ break;
+ case SLAPI_PLUGIN_INTERNAL_POST_MODIFY_FN:
+ case SLAPI_PLUGIN_INTERNAL_POST_MODRDN_FN:
+ case SLAPI_PLUGIN_INTERNAL_POST_ADD_FN:
+ case SLAPI_PLUGIN_INTERNAL_POST_DELETE_FN:
+ plugin_list_number= PLUGIN_LIST_INTERNAL_POSTOPERATION;
+ break;
+ }
+ if(plugin_list_number!=-1 && do_op)
+ {
+ /* We stash the pblock plugin pointer to preserve the callers context */
+ struct slapdplugin *p;
+ slapi_pblock_get(pb, SLAPI_PLUGIN, &p);
+ /* Call the operation on the Global Plugins */
+ rc= plugin_call_list(global_plugin_list[plugin_list_number], whichfunction, pb);
+
+ slapi_pblock_set(pb, SLAPI_PLUGIN, p);
+ }
+ else
+ {
+ /* Programmer error! or the callback is denied during startup */
+ }
+ return rc;
+}
+
+
+void
+plugin_call_entrystore_plugins(char **entrystr, uint *size)
+{
+ struct slapdplugin *p;
+ for (p = global_plugin_list[PLUGIN_LIST_LDBM_ENTRY_FETCH_STORE];
+ p != NULL; p = p->plg_next )
+ {
+ if (p->plg_entrystorefunc)
+ (*p->plg_entrystorefunc)(entrystr, size);
+ }
+}
+
+void
+plugin_call_entryfetch_plugins(char **entrystr, uint *size)
+{
+ struct slapdplugin *p;
+ for (p = global_plugin_list[PLUGIN_LIST_LDBM_ENTRY_FETCH_STORE];
+ p != NULL; p = p->plg_next )
+ {
+ if (p->plg_entryfetchfunc)
+ (*p->plg_entryfetchfunc)(entrystr, size);
+ }
+}
+
+/*
+ * call extended operation plugins
+ *
+ * return SLAPI_PLUGIN_EXTENDED_SENT_RESULT if one of the extended operation
+ * plugins sent a result.
+ * return SLAPI_PLUGIN_EXTENDED_NOT_HANDLED if no extended operation plugins
+ * handled the operation.
+ * otherwise, return an LDAP error code (possibly a merge of the errors
+ * returned by the plugins we called).
+ */
+int
+plugin_call_exop_plugins( Slapi_PBlock *pb, char *oid )
+{
+ struct slapdplugin *p;
+ int i, rc;
+ int lderr = SLAPI_PLUGIN_EXTENDED_NOT_HANDLED;
+
+ for ( p = global_plugin_list[PLUGIN_LIST_EXTENDED_OPERATION]; p != NULL; p = p->plg_next ) {
+ if ( p->plg_exhandler != NULL ) {
+ if ( p->plg_exoids != NULL ) {
+ for ( i = 0; p->plg_exoids[i] != NULL; i++ ) {
+ if ( strcasecmp( oid, p->plg_exoids[i] )
+ == 0 ) {
+ break;
+ }
+ }
+ if ( p->plg_exoids[i] == NULL ) {
+ continue;
+ }
+ }
+
+ slapi_pblock_set( pb, SLAPI_PLUGIN, p );
+ set_db_default_result_handlers( pb );
+ if ( (rc = (*p->plg_exhandler)( pb ))
+ == SLAPI_PLUGIN_EXTENDED_SENT_RESULT ) {
+ return( rc ); /* result sent */
+ } else if ( rc != SLAPI_PLUGIN_EXTENDED_NOT_HANDLED ) {
+ /*
+ * simple merge: report last real error
+ */
+ if ( lderr == SLAPI_PLUGIN_EXTENDED_NOT_HANDLED
+ || rc != LDAP_SUCCESS ) {
+ lderr = rc;
+ }
+ }
+ }
+ }
+
+ return( lderr );
+}
+
+
+/*
+ * Attempt to convert the extended operation 'oid' to a string by
+ * examining the registered plugins. Returns NULL if no plugin is
+ * registered for this OID.
+ *
+ * Our first choice is to use an OID-specific name that has been
+ * registered by a plugin via the SLAPI_PLUGIN_EXT_OP_NAMELIST pblock setting.
+ * Our second choice is to use the plugin's ID (short name).
+ * Our third choice is to use the plugin's RDN (under cn=config).
+ */
+const char *
+plugin_extended_op_oid2string( const char *oid )
+{
+ struct slapdplugin *p;
+ int i, j;
+ const char *rval = NULL;
+
+ for ( p = global_plugin_list[PLUGIN_LIST_EXTENDED_OPERATION]; p != NULL;
+ p = p->plg_next ) {
+ if ( p->plg_exhandler != NULL && p->plg_exoids != NULL ) {
+ for ( i = 0; p->plg_exoids[i] != NULL; i++ ) {
+ if ( strcasecmp( oid, p->plg_exoids[i] ) == 0 ) {
+ if ( NULL != p->plg_exnames ) {
+ for ( j = 0; j < i && p->plg_exnames[j] != NULL; ++j ) {
+ ;
+ }
+ rval = p->plg_exnames[j]; /* OID-related name */
+ }
+
+ if ( NULL == rval ) {
+ if ( NULL != p->plg_desc.spd_id ) {
+ rval = p->plg_desc.spd_id; /* short name */
+ } else {
+ rval = p->plg_name; /* RDN */
+ }
+ }
+ break;
+ }
+ if ( p->plg_exoids[i] != NULL ) {
+ break;
+ }
+ }
+ }
+ }
+
+ return( rval );
+}
+
+
+/*
+ * kexcoff: return the slapdplugin structure
+ */
+struct slapdplugin *
+plugin_get_pwd_storage_scheme(char *name, int len, int index)
+{
+ /* index could be PLUGIN_LIST_PWD_STORAGE_SCHEME or PLUGIN_LIST_REVER_PWD_STORAGE_SCHEME */
+ struct slapdplugin *p;
+
+ for ( p = global_plugin_list[index]; p != NULL; p = p->plg_next ) {
+ if (strncasecmp(p->plg_pwdstorageschemename, name, len) == 0)
+ return( p );
+ }
+ return( NULL );
+}
+
+char *
+plugin_get_pwd_storage_scheme_list(int index)
+{
+ /* index could be PLUGIN_LIST_PWD_STORAGE_SCHEME or PLUGIN_LIST_REVER_PWD_STORAGE_SCHEME */
+
+ struct slapdplugin *p = NULL;
+ char *names_list = NULL;
+ int len = 0;
+
+ /* first pass - calculate space needed for comma delimited list */
+ for ( p = global_plugin_list[index]; p != NULL; p = p->plg_next ) {
+ if ( p->plg_pwdstorageschemeenc != NULL )
+ {
+ /* + 1 for comma, 1 for space, 1 for null */
+ len += strlen(p->plg_pwdstorageschemename) + 3;
+ }
+ }
+
+ /* no plugins? */
+ if (!len)
+ return NULL;
+
+ /* next, allocate the space */
+ names_list = (char *)slapi_ch_malloc(len+1);
+ *names_list = 0;
+
+ /* second pass - write the string */
+ for ( p = global_plugin_list[index]; p != NULL; p = p->plg_next ) {
+ if ( p->plg_pwdstorageschemeenc != NULL )
+ {
+ strcat(names_list, p->plg_pwdstorageschemename);
+ if (p->plg_next != NULL)
+ strcat(names_list, ", ");
+ }
+ }
+ return( names_list );
+}
+
+int
+slapi_send_ldap_search_entry( Slapi_PBlock *pb, Slapi_Entry *e, LDAPControl **ectrls,
+ char **attrs, int attrsonly )
+{
+ IFP fn = NULL;
+ slapi_pblock_get(pb,SLAPI_PLUGIN_DB_ENTRY_FN,(void*)&fn);
+ if (NULL == fn)
+ {
+ return -1;
+ }
+ return (*fn)(pb,e,ectrls,attrs,attrsonly);
+}
+
+void
+slapi_set_ldap_result( Slapi_PBlock *pb, int err, char *matched, char *text,
+ int nentries, struct berval **urls )
+{
+ char * old_matched = NULL;
+ char * old_text = NULL;
+ char * matched_copy = slapi_ch_strdup(matched);
+ char * text_copy = slapi_ch_strdup(text);
+
+ /* free the old matched and text, if any */
+ slapi_pblock_get(pb, SLAPI_RESULT_MATCHED, &old_matched);
+ slapi_ch_free_string(&old_matched);
+
+ slapi_pblock_get(pb, SLAPI_RESULT_TEXT, &old_text);
+ slapi_ch_free_string(&old_text);
+
+ /* set the new stuff */
+ slapi_pblock_set(pb, SLAPI_RESULT_CODE, &err);
+ slapi_pblock_set(pb, SLAPI_RESULT_MATCHED, matched_copy);
+ slapi_pblock_set(pb, SLAPI_RESULT_TEXT, text_copy);
+}
+
+void
+slapi_send_ldap_result_from_pb( Slapi_PBlock *pb)
+{
+ int err;
+ char *matched;
+ char *text;
+ IFP fn = NULL;
+
+ slapi_pblock_get(pb, SLAPI_RESULT_CODE, &err);
+ slapi_pblock_get(pb, SLAPI_RESULT_TEXT, &text);
+ slapi_pblock_get(pb, SLAPI_RESULT_MATCHED, &matched);
+
+ slapi_pblock_get(pb,SLAPI_PLUGIN_DB_RESULT_FN,(void*)&fn);
+ if (NULL != fn)
+ {
+ (*fn)(pb,err,matched,text,0,NULL);
+ }
+
+ slapi_pblock_set(pb, SLAPI_RESULT_TEXT, NULL);
+ slapi_pblock_set(pb, SLAPI_RESULT_MATCHED, NULL);
+ slapi_ch_free((void **)&matched);
+ slapi_ch_free((void **)&text);
+}
+
+void
+slapi_send_ldap_result( Slapi_PBlock *pb, int err, char *matched, char *text,
+ int nentries, struct berval **urls )
+{
+ IFP fn = NULL;
+ Slapi_Operation *operation;
+ long op_type;
+
+ /* GB : for spanning requests over multiple backends */
+ if (err == LDAP_NO_SUCH_OBJECT)
+ {
+ slapi_pblock_get (pb, SLAPI_OPERATION, &operation);
+
+ op_type = operation_get_type(operation);
+ if (op_type == SLAPI_OPERATION_SEARCH)
+ {
+ if (urls || nentries)
+ {
+ LDAPDebug( LDAP_DEBUG_ANY, "ERROR : urls or nentries set"
+ "in sendldap_result while NO_SUCH_OBJECT returned\n",0,0,0);
+ }
+
+ slapi_set_ldap_result(pb, err, matched, text, 0, NULL);
+ return;
+ }
+ }
+
+ slapi_pblock_set(pb, SLAPI_RESULT_CODE, &err);
+
+ slapi_pblock_get(pb,SLAPI_PLUGIN_DB_RESULT_FN,(void*)&fn);
+ if (NULL == fn)
+ {
+ return ;
+ }
+
+ /*
+ * Call the result function. It should set pb->pb_op->o_status to
+ * SLAPI_OP_STATUS_RESULT_SENT right after sending the result to
+ * the client or otherwise consuming it.
+ */
+ (*fn)(pb,err,matched,text,nentries,urls);
+}
+
+int
+slapi_send_ldap_referral( Slapi_PBlock *pb, Slapi_Entry *e, struct berval **refs,
+ struct berval ***urls )
+{
+ IFP fn = NULL;
+ slapi_pblock_get(pb,SLAPI_PLUGIN_DB_REFERRAL_FN,(void*)&fn);
+ if (NULL == fn)
+ {
+ return -1;
+ }
+ return (*fn)(pb,e,refs,urls);
+}
+
+
+/***********************************************************
+ start of plugin dependency code
+************************************************************/
+
+/* struct _plugin_dep_type
+ * we shall not presume to know all plugin types
+ * so as to allow new types to be added without
+ * requiring changes to this code (hopefully)
+ * so we need to dynamically keep track of them
+ */
+
+typedef struct _plugin_dep_type{
+ char *type; /* the string descriptor */
+ int num_not_started; /* the count of plugins which have yet to be started for this type */
+ struct _plugin_dep_type *next;
+} *plugin_dep_type;
+
+/* _plugin_dep_config
+ * we need somewhere to collect the plugin configurations
+ * prior to attempting to resolve dependencies
+ */
+
+typedef struct _plugin_dep_config {
+ char *name;
+ char *type;
+ Slapi_PBlock pb;
+ struct slapdplugin *plugin;
+ Slapi_Entry *e;
+ int entry_created;
+ int op_done;
+ char **depends_type_list;
+ int total_type;
+ char **depends_named_list;
+ int total_named;
+} plugin_dep_config;
+
+/* list of plugins which should be shutdown in reverse order */
+static plugin_dep_config *global_plugin_shutdown_order = 0;
+static int global_plugins_started = 0;
+
+/*
+ * find_plugin_type
+ *
+ * searches the list for the plugin type
+ * and returns the plugin_dep_type if found
+ */
+
+static plugin_dep_type
+find_plugin_type(plugin_dep_type head, char *type)
+{
+ plugin_dep_type ret = 0;
+ plugin_dep_type iter = head;
+
+ while(iter)
+ {
+ if(!slapi_UTF8CASECMP(iter->type, type))
+ {
+ ret = iter;
+ break;
+ }
+
+ iter = iter->next;
+ }
+
+ return ret;
+}
+
+
+/*
+ * increment_plugin_type
+ *
+ * searches the list for the plugin type
+ * and increments its not started value
+ * returns the current type count on success -1 on failure
+ * to find the type
+ */
+
+static int
+increment_plugin_type(plugin_dep_type head, char *type)
+{
+ int ret = -1;
+ plugin_dep_type the_type;
+
+ if ((the_type = find_plugin_type(head, type)) != NULL)
+ ret = ++the_type->num_not_started;
+
+ return ret;
+}
+
+
+/*
+ * decrement_plugin_type
+ *
+ * searches the list for the plugin type
+ * and decrements its not started value
+ * returns the current type count on success -1 on failure
+ * to find the type
+ */
+
+static int
+decrement_plugin_type(plugin_dep_type head, char *type)
+{
+ int ret = -1;
+ plugin_dep_type the_type;
+
+ if ((the_type = find_plugin_type(head, type)) != NULL)
+ ret = --the_type->num_not_started;
+
+ return ret;
+}
+
+/*
+ * add_plugin_type
+ *
+ * Either increments the count of the plugin type
+ * or when it does not exist, adds it to the list
+ */
+
+static int
+add_plugin_type(plugin_dep_type *head, char *type)
+{
+ int ret = -1;
+
+ if(*head)
+ {
+ if(0 < increment_plugin_type(*head, type))
+ {
+ ret = 0;
+ }
+ }
+
+ if(ret)
+ {
+ /* create new head */
+ plugin_dep_type tmp_head;
+
+ tmp_head = (plugin_dep_type)slapi_ch_malloc(sizeof(struct _plugin_dep_type));
+ tmp_head->num_not_started = 1;
+ tmp_head->type = slapi_ch_strdup(type);
+ ret = 0;
+ tmp_head->next = *head;
+ (*head) = tmp_head;
+ }
+
+ return ret;
+}
+
+
+/*
+ * plugin_create_stringlist
+ *
+ * Creates a string list from values of the entries
+ * attribute passed in as args - used to track dependencies
+ *
+ */
+
+int
+plugin_create_stringlist( Slapi_Entry *plugin_entry, char *attr_name,
+ int *total_strings, char ***list)
+{
+ Slapi_Attr *attr = 0;
+ int hint =0;
+ int num_vals = 0;
+ int val_index = 0;
+ Slapi_Value *val;
+
+ if(0 == slapi_entry_attr_find( plugin_entry, attr_name, &attr ))
+ {
+ /* allocate memory for the string array */
+ slapi_attr_get_numvalues( attr, &num_vals);
+
+ if(num_vals)
+ {
+ *total_strings = num_vals;
+ *list = (char **)slapi_ch_malloc(sizeof(char*) * num_vals);
+ }
+ else
+ goto bail; /* if this ever happens, then they are running on a TSR-80 */
+
+ val_index = 0;
+
+ hint = slapi_attr_first_value( attr, &val );
+ while(val_index < num_vals)
+ {
+ /* add the value to the array */
+ (*list)[val_index] = (char*)slapi_ch_strdup(slapi_value_get_string(val));
+
+ hint = slapi_attr_next_value( attr, hint, &val );
+ val_index++;
+ }
+ }
+ else
+ *total_strings = num_vals;
+
+bail:
+ return num_vals;
+}
+
+
+
+/*
+ * plugin_dependency_startall()
+ *
+ * Starts all plugins (apart from syntax and matching rule) in order
+ * of dependency.
+ *
+ * Dependencies will be determined by these multi-valued attributes:
+ *
+ * nsslapd-plugin-depends-on-type : all plugins whose type value matches one of these values must
+ * be started prior to this plugin
+ *
+ * nsslapd-plugin-depends-on-named : the plugin whose cn value matches one of these values must
+ * be started prior to this plugin
+ */
+
+static int
+plugin_dependency_startall(int argc, char** argv, char *errmsg, int operation)
+{
+ int ret = 0;
+ Slapi_PBlock pb;
+ int total_plugins = 0;
+ plugin_dep_config *config = 0;
+ plugin_dep_type plugin_head = 0;
+ int plugin_index = 0;
+ Slapi_Entry *plugin_entry;
+ int i = 0; /* general index iterator */
+ plugin_dep_type the_plugin_type;
+ int index = 0;
+ char * value;
+ int plugins_started;
+ int num_plg_started;
+ struct slapdplugin *plugin;
+ entry_and_plugin_t *ep = dep_plugin_entries;
+ int shutdown_index = 0;
+
+ /*
+ * Disable registered plugin functions so preops/postops/etc
+ * dont get called prior to the plugin being started (due to
+ * plugins performing ops on the DIT)
+ */
+
+ global_plugin_callbacks_enabled = 0;
+
+ /* Count the plugins so we can allocate memory for the config array */
+ while(ep)
+ {
+ total_plugins++;
+
+ ep = ep->next;
+ }
+
+ /* allocate the config array */
+ config = (plugin_dep_config*)slapi_ch_malloc(sizeof(plugin_dep_config) * total_plugins);
+
+ if(config)
+ memset(config, 0, sizeof(plugin_dep_config) * total_plugins);
+ else
+ {
+ ret = -1;
+ goto bail;
+ }
+
+ ep = dep_plugin_entries;
+
+ /* Collect relevant config */
+ while(ep)
+ {
+ plugin = ep->plugin;
+
+ if(plugin == 0)
+ continue;
+
+ pblock_init(&pb);
+ slapi_pblock_set( &pb, SLAPI_ARGC, &argc);
+ slapi_pblock_set( &pb, SLAPI_ARGV, &argv);
+
+ config[plugin_index].pb = pb;
+ config[plugin_index].e = ep->e;
+
+ /* add type */
+ plugin_entry = ep->e;
+ ep->e = NULL; /* consumed by the operation above, and eventually by the
+ slapi_internal_add operation below */
+
+ if(plugin_entry)
+ {
+ /*
+ * Pass the plugin DN in SLAPI_TARGET_DN and the plugin entry
+ * in SLAPI_ADD_ENTRY. For this to actually work, we need to
+ * create an operation and include that in the pblock as well,
+ * because these two items are stored in the operation parameters.
+ */
+ Operation *op = internal_operation_new(SLAPI_OPERATION_ADD, 0);
+ slapi_pblock_set(&(config[plugin_index].pb), SLAPI_OPERATION, op);
+ slapi_pblock_set(&(config[plugin_index].pb), SLAPI_TARGET_DN,
+ (void*)(slapi_entry_get_dn_const(plugin_entry)));
+ slapi_pblock_set(&(config[plugin_index].pb), SLAPI_ADD_ENTRY,
+ plugin_entry );
+
+ value = slapi_entry_attr_get_charptr(plugin_entry, "nsslapd-plugintype");
+ if(value)
+ {
+ add_plugin_type( &plugin_head, value);
+ config[plugin_index].type = value;
+ value = NULL;
+ }
+
+ /* now the name */
+ value = slapi_entry_attr_get_charptr(plugin_entry, "cn");
+ if(value)
+ {
+ config[plugin_index].name = value;
+ value = NULL;
+ }
+
+
+ config[plugin_index].plugin = plugin;
+
+ /* now add dependencies */
+ plugin_create_stringlist( plugin_entry, "nsslapd-plugin-depends-on-named",
+ &(config[plugin_index].total_named), &(config[plugin_index].depends_named_list));
+
+ plugin_create_stringlist( plugin_entry, "nsslapd-plugin-depends-on-type",
+ &(config[plugin_index].total_type), &(config[plugin_index].depends_type_list));
+ }
+
+ plugin_index++;
+ ep = ep->next;
+ }
+
+ /* prepare list of shutdown order (we need nothing fancier right now
+ * than the reverse startup order) The list may include NULL entries,
+ * these will be plugins which were never started
+ */
+ shutdown_index = total_plugins - 1;
+
+ global_plugin_shutdown_order = (plugin_dep_config*)slapi_ch_malloc(sizeof(plugin_dep_config) * total_plugins);
+ if(global_plugin_shutdown_order)
+ memset(global_plugin_shutdown_order, 0, sizeof(plugin_dep_config) * total_plugins);
+ else
+ {
+ ret = -1;
+ goto bail;
+ }
+
+ /* now resolve dependencies
+ * cycle through list, if a plugin has no dependencies then start it
+ * then remove it from the dependency lists of all other plugins
+ * and decrement the corresponding element of the plugin types array
+ * for depends_type we will need to check the array of plugin types
+ * to see if all type dependencies are at zero prior to start
+ * if one cycle fails to load any plugins we have failed, however
+ * we shall continue loading plugins in case a configuration error
+ * can correct itself
+ */
+
+ plugins_started = 1;
+ num_plg_started = 0;
+
+ while(plugins_started && num_plg_started < total_plugins)
+ {
+ plugins_started = 0;
+
+ for(plugin_index=0; plugin_index < total_plugins; plugin_index++)
+ {
+ /* perform op on plugins only once */
+ if(config[plugin_index].op_done == 0)
+ {
+ int enabled = 0;
+ int satisfied = 0;
+ int break_out = 0;
+
+ /*
+ * determine if plugin is enabled
+ * some processing is necessary even
+ * if it is not
+ */
+ if ( NULL != config[plugin_index].e && (value = slapi_entry_attr_get_charptr(config[plugin_index].e,
+ ATTR_PLUGIN_ENABLED)) &&
+ !strcasecmp(value, "on"))
+ {
+ enabled = 1;
+ }
+ else
+ enabled = 0;
+
+ slapi_ch_free((void**)&value);
+
+ /*
+ * make sure named dependencies have been satisfied
+ * that means that the list of names should contain all
+ * null entries
+ */
+
+ if(enabled && config[plugin_index].total_named)
+ {
+ i = 0;
+
+ while(break_out == 0 && i < config[plugin_index].total_named)
+ {
+ satisfied = 1;
+
+ if((config[plugin_index].depends_named_list)[i] != 0)
+ {
+ satisfied = 0;
+ break_out = 1;
+ }
+
+ i++;
+ }
+
+ if(!satisfied)
+ continue;
+ }
+
+ /*
+ * make sure the type dependencies have been satisfied
+ * that means for each type in the list, it's number of
+ * plugins left not started is zero
+ *
+ */
+ satisfied = 0;
+ break_out = 0;
+
+ if(enabled && config[plugin_index].total_type)
+ {
+ i = 0;
+
+ while(break_out == 0 && i < config[plugin_index].total_type)
+ {
+ satisfied = 1;
+
+ the_plugin_type = find_plugin_type(plugin_head, (config[plugin_index].depends_type_list)[i]);
+
+ if(the_plugin_type && the_plugin_type->num_not_started != 0)
+ {
+ satisfied = 0;
+ break_out = 1;
+ }
+
+ i++;
+ }
+
+ if(!satisfied)
+ continue;
+ }
+
+ /**** This plugins dependencies have now been satisfied ****/
+
+ satisfied = 1; /* symbolic only */
+
+ /*
+ * Add the plugins entry to the DSE so the plugin can get
+ * its config (both enabled and disabled have entries
+ */
+
+ if(!config[plugin_index].entry_created)
+ {
+ int plugin_actions = 0;
+ Slapi_PBlock newpb;
+ Slapi_Entry *newe;
+
+ pblock_init(&newpb);
+ /*
+ * config[plugin_index].e is freed up by
+ * below function calls, but we may need
+ * it later, so create a copy
+ */
+ newe = slapi_entry_dup( config[plugin_index].e );
+ slapi_add_entry_internal_set_pb(&newpb, newe, NULL,
+ plugin_get_default_component_id(), plugin_actions);
+ slapi_pblock_set(&newpb, SLAPI_TARGET_DN, (void*)slapi_entry_get_dn_const(newe));
+ slapi_add_internal_pb(&newpb);
+ pblock_done(&newpb);
+ config[plugin_index].entry_created = 1;
+ }
+
+ /*
+ * only actually start plugin and remove from lists if its enabled
+ * we do remove from plugin type list however - rule is dependency on
+ * zero or more for type
+ */
+
+ if (enabled)
+ {
+ /* finally, perform the op on the plugin */
+
+ LDAPDebug( LDAP_DEBUG_PLUGIN, "Starting %s plugin %s\n" , config[plugin_index].type, config[plugin_index].name, 0 );
+
+ ret = plugin_call_one( config[plugin_index].plugin, operation, &(config[plugin_index].pb));
+
+ pblock_done(&(config[plugin_index].pb));
+
+ if(ret)
+ {
+ /*
+ * We will not exit here. If we allow plugins to load normally it is
+ * possible that a configuration error (dependedncies which were not
+ * configured properly) can be recovered from. If there really is a
+ * problem then the plugin will never start and eventually it will
+ * trigger an exit anyway.
+ */
+ LDAPDebug( LDAP_DEBUG_ANY, "Failed to start %s plugin %s\n" , config[plugin_index].type, config[plugin_index].name, 0 );
+ continue;
+ }
+
+ /* Add this plugin to the shutdown list */
+
+ global_plugin_shutdown_order[shutdown_index] = config[plugin_index];
+ shutdown_index--;
+ global_plugins_started++;
+
+ /* remove this named plugin from other plugins lists */
+
+ for(i=0; i<total_plugins; i++)
+ {
+ index = 0;
+
+ while(index < config[i].total_named)
+ {
+ if((config[i].depends_named_list)[index] != 0 && !slapi_UTF8CASECMP((config[i].depends_named_list)[index], config[plugin_index].name))
+ {
+ slapi_ch_free((void**)&((config[i].depends_named_list)[index]));
+ (config[i].depends_named_list)[index] = 0;
+ }
+
+ index++;
+ }
+ }
+ }
+
+ /* decrement the type counter for this plugin type */
+
+ decrement_plugin_type(plugin_head, config[plugin_index].type);
+ config[plugin_index].op_done = 1;
+ num_plg_started++;
+ plugins_started = 1;
+ }
+
+ }
+ }
+
+ if(plugins_started == 0)
+ {
+ /* a dependency was not resolved - error */
+ LDAPDebug( LDAP_DEBUG_ANY, "Error: Failed to resolve plugin dependencies\n" , 0, 0, 0 );
+
+ /* list the plugins yet to perform op */
+ index = 0;
+
+ while(i < total_plugins)
+ {
+ if(config[i].op_done == 0)
+ {
+ LDAPDebug( LDAP_DEBUG_ANY, "Error: %s plugin %s is not started\n" , config[i].type, config[i].name, 0 );
+ }
+
+ i++;
+ }
+
+ exit(1);
+ }
+
+bail:
+
+ /*
+ * need the details in config to hang around for shutdown
+ * config itself may be deleted since its contents have been
+ * copied by value to the shutdown list
+ */
+
+ if(config)
+ {
+ /*
+ index = 0;
+
+ while(index < total_plugins)
+ {
+ if(config[index].depends_named_list)
+ {
+ slapi_ch_free((void**)&(config[index].depends_named_list));
+ }
+
+ if(config[index].depends_type_list)
+ {
+ i = 0;
+
+ while(i < config[index].total_type)
+ {
+ slapi_ch_free((void**)&(config[index].depends_type_list)[i]);
+
+ i++;
+ }
+
+ slapi_ch_free((void**)&(config[index].depends_type_list));
+ }
+
+ slapi_ch_free((void**)&(config[index].name));
+ slapi_ch_free((void**)&(config[index].type));
+
+ index++;
+ }
+ */
+
+ slapi_ch_free((void**)&config);
+ }
+
+ /* Finally enable registered plugin functions */
+
+ global_plugin_callbacks_enabled = 1;
+
+ return ret;
+}
+
+/*
+ * plugin_dependency_closeall
+ *
+ * uses the shutdown list created at startup to close
+ * plugins in the correct order
+ *
+ * For now this leaks the list and contents, but since
+ * it hangs around until shutdown anyway, we don't care
+ *
+ */
+void
+plugin_dependency_closeall()
+{
+ Slapi_PBlock pb;
+ int plugins_closed = 0;
+ int index = 0;
+
+ while(plugins_closed<global_plugins_started)
+ {
+ /*
+ * the first few entries may not be valid
+ * since the list was created in the reverse
+ * order and some plugins may have been counted
+ * for the purpose of list allocation but are
+ * disabled and so were never started
+ *
+ * we check that here
+ */
+ if(global_plugin_shutdown_order[index].name)
+ {
+ pblock_init(&pb);
+ plugin_call_one( global_plugin_shutdown_order[index].plugin, SLAPI_PLUGIN_CLOSE_FN, &pb );
+ plugins_closed++;
+ }
+
+ index++;
+ }
+}
+
+/***********************************************************
+ end of plugin dependency code
+************************************************************/
+
+
+/*
+ * Function: plugin_startall
+ *
+ * Returns: squat
+ *
+ * Description: Some plugins may need to do some stuff after all the config
+ * stuff is done with. So this function goes through and starts all plugins
+ */
+void
+plugin_startall(int argc, char** argv, int start_backends, int start_global)
+{
+ /* initialize special plugin structures */
+ default_plugin_init ();
+
+ plugin_dependency_startall(argc, argv, "plugin startup failed\n", SLAPI_PLUGIN_START_FN);
+}
+
+/*
+ * Function: plugin_close_all
+ *
+ * Returns: squat
+ *
+ * Description: cleanup routine, allows plugins to kill threads, free memory started in start fn
+ *
+ */
+void
+plugin_closeall(int close_backends, int close_globals)
+{
+ plugin_dependency_closeall();
+}
+
+
+static int
+plugin_call_list (struct slapdplugin *list, int operation, Slapi_PBlock *pb)
+{
+ return plugin_call_func(list, operation, pb, 0);
+}
+
+static int
+plugin_call_one (struct slapdplugin *list, int operation, Slapi_PBlock *pb)
+{
+ return plugin_call_func(list, operation, pb, 1);
+}
+
+
+/*
+ * Return codes:
+ * - For preoperation plugins, returns the return code passed back from the first
+ * plugin that fails, or zero if all plugins succeed.
+ * - For bepreop and bepostop plugins, returns a bitwise OR of the return codes
+ * returned by all the plugins called (there's only one bepreop and one bepostop
+ * in DS 5.0 anyway).
+ * - For postoperation plugins, returns 0.
+ */
+static int
+plugin_call_func (struct slapdplugin *list, int operation, Slapi_PBlock *pb, int call_one)
+{
+ /* Invoke the operation on the plugins that are registered for the subtree effected by the operation. */
+ int rc;
+ int return_value = 0;
+ int count= 0;
+ for (; list != NULL; list = list->plg_next)
+ {
+ IFP func = NULL;
+
+ slapi_pblock_set (pb, SLAPI_PLUGIN, list);
+ set_db_default_result_handlers (pb); /* JCM: What's this do? Is it needed here? */
+ if (slapi_pblock_get (pb, operation, &func) == 0 && func != NULL &&
+ plugin_invoke_plugin_pb (list, operation, pb))
+ {
+ char *n= list->plg_name;
+ LDAPDebug( LDAP_DEBUG_TRACE, "Calling plugin '%s' #%d type %d\n", (n==NULL?"noname":n), count, operation );
+ /* counters_to_errors_log("before plugin call"); */
+ if (( rc = func (pb)) != 0 )
+ {
+ if (SLAPI_PLUGIN_PREOPERATION == list->plg_type ||
+ SLAPI_PLUGIN_INTERNAL_PREOPERATION == list->plg_type ||
+ SLAPI_PLUGIN_START_FN == operation )
+ {
+ /*
+ * We bail out of plugin processing for preop plugins
+ * that return a non-zero return code. This allows preop
+ * plugins to cause further preop processing to terminate, and
+ * causes the operation to be vetoed.
+ */
+ return_value = rc;
+ break;
+ } else if (SLAPI_PLUGIN_BEPREOPERATION == list->plg_type ||
+ SLAPI_PLUGIN_BEPOSTOPERATION == list->plg_type)
+ {
+ /* OR the result into the return value for be pre/postops */
+ return_value |= rc;
+ }
+ }
+ /* counters_to_errors_log("after plugin call"); */
+ }
+
+ count++;
+
+ if(call_one)
+ break;
+ }
+ return( return_value );
+}
+
+int
+slapi_berval_cmp (const struct berval* L, const struct berval* R) /* JCM - This does not belong here. But, where should it go? */
+{
+ int result = 0;
+ if (L->bv_len < R->bv_len) {
+ result = memcmp (L->bv_val, R->bv_val, L->bv_len);
+ if (result == 0)
+ result = -1;
+ } else {
+ result = memcmp (L->bv_val, R->bv_val, R->bv_len);
+ if (result == 0 && (L->bv_len > R->bv_len))
+ result = 1;
+ }
+ return result;
+}
+
+
+static char **supported_saslmechanisms = NULL;
+static PRRWLock *supported_saslmechanisms_lock = NULL;
+
+/*
+ * register a supported SASL mechanism so it will be returned as part of the
+ * root DSE.
+ */
+void
+slapi_register_supported_saslmechanism( char *mechanism )
+{
+ if ( mechanism != NULL ) {
+ if (NULL == supported_saslmechanisms_lock) {
+ /* This is thread safe, as it gets executed by
+ * a single thread at init time (main->init_saslmechanisms) */
+ supported_saslmechanisms_lock = PR_NewRWLock(PR_RWLOCK_RANK_NONE,
+ "supported saslmechanisms rwlock");
+ if (NULL == supported_saslmechanisms_lock) {
+ /* Out of resources */
+ slapi_log_error(SLAPI_LOG_FATAL, "startup",
+ "slapi_register_supported_saslmechanism: failed to create lock.\n");
+ exit (1);
+ }
+ }
+ PR_RWLock_Wlock(supported_saslmechanisms_lock);
+ charray_add( &supported_saslmechanisms, slapi_ch_strdup( mechanism ));
+ PR_RWLock_Unlock(supported_saslmechanisms_lock);
+ }
+}
+
+
+/*
+ * return pointer to NULL-terminated array of supported SASL mechanisms.
+ * This function is not MTSafe and should be deprecated.
+ * slapi_get_supported_saslmechanisms_copy should be used instead.
+ */
+char **
+slapi_get_supported_saslmechanisms( void )
+{
+ return( supported_saslmechanisms );
+}
+
+
+/*
+ * return pointer to NULL-terminated array of supported SASL mechanisms.
+ */
+char **
+slapi_get_supported_saslmechanisms_copy( void )
+{
+ char ** ret = NULL;
+ PR_RWLock_Rlock(supported_saslmechanisms_lock);
+ ret = charray_dup(supported_saslmechanisms);
+ PR_RWLock_Unlock(supported_saslmechanisms_lock);
+ return( ret );
+}
+
+
+static char **supported_extended_ops = NULL;
+static PRRWLock *extended_ops_lock = NULL;
+
+/*
+ * register all of the LDAPv3 extended operations we know about.
+ */
+void
+ldapi_init_extended_ops( void )
+{
+ extended_ops_lock = PR_NewRWLock(PR_RWLOCK_RANK_NONE,
+ "supported extended ops rwlock");
+ if (NULL == extended_ops_lock) {
+ /* Out of resources */
+ slapi_log_error(SLAPI_LOG_FATAL, "startup",
+ "ldapi_init_extended_ops: failed to create lock.\n");
+ exit (1);
+ }
+
+ PR_RWLock_Wlock(extended_ops_lock);
+ charray_add(&supported_extended_ops,
+ slapi_ch_strdup(EXTOP_BULK_IMPORT_START_OID));
+ charray_add(&supported_extended_ops,
+ slapi_ch_strdup(EXTOP_BULK_IMPORT_DONE_OID));
+ /* add future supported extops here... */
+ PR_RWLock_Unlock(extended_ops_lock);
+}
+
+
+/*
+ * register an extended op. so it can be returned as part of the root DSE.
+ */
+void
+ldapi_register_extended_op( char **opoids )
+{
+ int i;
+
+ PR_RWLock_Wlock(extended_ops_lock);
+ for ( i = 0; opoids != NULL && opoids[i] != NULL; ++i ) {
+ if ( !charray_inlist( supported_extended_ops, opoids[i] )) {
+ charray_add( &supported_extended_ops, slapi_ch_strdup( opoids[i] ));
+ }
+ }
+ PR_RWLock_Unlock(extended_ops_lock);
+}
+
+
+/*
+ * retrieve supported extended operation OIDs
+ * return 0 if successful and -1 if not.
+ * This function is not MTSafe and should be deprecated.
+ * slapi_get_supported_extended_ops_copy should be used instead.
+ */
+char **
+slapi_get_supported_extended_ops( void )
+{
+ return( supported_extended_ops );
+}
+
+
+/*
+ * retrieve supported extended operation OIDs
+ * return 0 if successful and -1 if not.
+ */
+char **
+slapi_get_supported_extended_ops_copy( void )
+{
+ char ** ret = NULL;
+ PR_RWLock_Rlock(extended_ops_lock);
+ ret = charray_dup(supported_extended_ops);
+ PR_RWLock_Unlock(extended_ops_lock);
+ return( ret );
+}
+
+
+/* isApprovedPlugin:
+ * returns 1 if the plugin is approved to be loaded, 0 otherwise.
+ *
+ * If the server is running as the Full version, all plugins are approved,
+ * otherwise, if the server is running as DirectoryLite, only plugins from
+ * Netscape are approved.
+ *
+ * We have a special case for the NT Synch plugin, which is disabled for DLite.
+ */
+static int
+isApprovedPlugin( struct slapdplugin *plugin )
+{
+ if ( config_is_slapd_lite() == 0 ) {
+ /* All the plugins are approved for Directory Full */
+ return 1;
+ }
+
+ if ( plugin == NULL ) {
+ LDAPDebug( LDAP_DEBUG_ANY, "isApprovedPlugin: plugin is NULL\n", 0,0,0 );
+ return 0;
+ }
+ if (plugin->plg_desc.spd_vendor == NULL ) {
+ LDAPDebug( LDAP_DEBUG_ANY, "isApprovedPlugin: plugin vendor is NULL\n",0,0,0 );
+ return 0;
+ }
+
+ LDAPDebug ( LDAP_DEBUG_TRACE, "isApprovedPlugin() looking at plugin \"%s\" from vendor %s\n",
+ plugin->plg_name, plugin->plg_desc.spd_vendor, 0 );
+
+ /*
+ * approved plugins must have their vendor string set to PLUGIN_MAGIC VENDOR_STR. External
+ * plugins are not allowed for Lite.
+ */
+ if ( strcmp( plugin->plg_desc.spd_vendor, PLUGIN_MAGIC_VENDOR_STR ) == 0)
+ return 1;
+
+ LDAPDebug ( LDAP_DEBUG_ANY, "isApprovedPlugin() plugin \"%s\" is not approved for Directory Lite\n",
+ plugin->plg_name, 0,0 );
+ return 0;
+}
+
+
+/*
+ looks up the given string type to convert to the internal integral type; also
+ returns the plugin list associated with the plugin type
+ returns 0 upon success and non-zero upon failure
+*/
+static int
+plugin_get_type_and_list(
+ const char *plugintype,
+ int *type,
+ struct slapdplugin ***plugin_list
+)
+{
+ int plugin_list_index = -1;
+ if ( strcasecmp( plugintype, "database" ) == 0 ) {
+ *type = SLAPI_PLUGIN_DATABASE;
+ plugin_list_index= PLUGIN_LIST_DATABASE;
+ } else if ( strcasecmp( plugintype, "extendedop" ) == 0 ) {
+ *type = SLAPI_PLUGIN_EXTENDEDOP;
+ plugin_list_index= PLUGIN_LIST_EXTENDED_OPERATION;
+ } else if ( strcasecmp( plugintype, "preoperation" ) == 0 ) {
+ *type = SLAPI_PLUGIN_PREOPERATION;
+ plugin_list_index= PLUGIN_LIST_PREOPERATION;
+ } else if ( strcasecmp( plugintype, "postoperation" ) == 0 ) {
+ *type = SLAPI_PLUGIN_POSTOPERATION;
+ plugin_list_index= PLUGIN_LIST_POSTOPERATION;
+ } else if ( strcasecmp( plugintype, "matchingrule" ) == 0 ) {
+ *type = SLAPI_PLUGIN_MATCHINGRULE;
+ plugin_list_index= PLUGIN_LIST_MATCHINGRULE;
+ } else if ( strcasecmp( plugintype, "syntax" ) == 0 ) {
+ *type = SLAPI_PLUGIN_SYNTAX;
+ plugin_list_index= PLUGIN_LIST_SYNTAX;
+ } else if ( strcasecmp( plugintype, "accesscontrol" ) == 0 ) {
+ *type = SLAPI_PLUGIN_ACL;
+ plugin_list_index= PLUGIN_LIST_ACL;
+ } else if ( strcasecmp( plugintype, "bepreoperation" ) == 0 ) {
+ *type = SLAPI_PLUGIN_BEPREOPERATION;
+ plugin_list_index= PLUGIN_LIST_BEPREOPERATION;
+ } else if ( strcasecmp( plugintype, "bepostoperation" ) == 0 ) {
+ *type = SLAPI_PLUGIN_BEPOSTOPERATION;
+ plugin_list_index= PLUGIN_LIST_BEPOSTOPERATION;
+ } else if ( strcasecmp( plugintype, "internalpreoperation" ) == 0 ) {
+ *type = SLAPI_PLUGIN_INTERNAL_PREOPERATION;
+ plugin_list_index= PLUGIN_LIST_INTERNAL_PREOPERATION;
+ } else if ( strcasecmp( plugintype, "internalpostoperation" ) == 0 ) {
+ *type = SLAPI_PLUGIN_INTERNAL_POSTOPERATION;
+ plugin_list_index= PLUGIN_LIST_INTERNAL_POSTOPERATION;
+ } else if ( strcasecmp( plugintype, "entry" ) == 0 ) {
+ *type = SLAPI_PLUGIN_ENTRY;
+ plugin_list_index= PLUGIN_LIST_ENTRY;
+ } else if ( strcasecmp( plugintype, "object" ) == 0 ) {
+ *type = SLAPI_PLUGIN_TYPE_OBJECT;
+ plugin_list_index= PLUGIN_LIST_OBJECT;
+ } else if ( strcasecmp( plugintype, "pwdstoragescheme" ) == 0 ) {
+ *type = SLAPI_PLUGIN_PWD_STORAGE_SCHEME;
+ plugin_list_index= PLUGIN_LIST_PWD_STORAGE_SCHEME;
+ } else if ( strcasecmp( plugintype, "reverpwdstoragescheme" ) == 0 ) {
+ *type = SLAPI_PLUGIN_REVER_PWD_STORAGE_SCHEME;
+ plugin_list_index= PLUGIN_LIST_REVER_PWD_STORAGE_SCHEME;
+ } else if ( strcasecmp( plugintype, "vattrsp" ) == 0 ) {
+ *type = SLAPI_PLUGIN_VATTR_SP;
+ plugin_list_index= PLUGIN_LIST_VATTR_SP;
+ } else if ( strcasecmp( plugintype, "ldbmentryfetchstore" ) == 0 ) {
+ *type = SLAPI_PLUGIN_LDBM_ENTRY_FETCH_STORE;
+ plugin_list_index= PLUGIN_LIST_LDBM_ENTRY_FETCH_STORE;
+ } else if ( strcasecmp( plugintype, "index" ) == 0 ) {
+ *type = SLAPI_PLUGIN_INDEX;
+ plugin_list_index= PLUGIN_LIST_INDEX;
+ } else {
+ return( 1 ); /* unknown plugin type - pass to backend */
+ }
+
+ if (plugin_list_index >= 0)
+ *plugin_list = &global_plugin_list[plugin_list_index];
+
+ return 0;
+}
+
+static const char *
+plugin_exists(const Slapi_DN *plugin_dn)
+{
+ /* check to see if the plugin name is unique */
+ const char *retval = 0;
+ if (global_plugin_dns && PL_HashTableLookup(global_plugin_dns,
+ slapi_sdn_get_ndn(plugin_dn)))
+ {
+ retval = slapi_sdn_get_dn(plugin_dn);
+ }
+
+ return retval;
+}
+
+static int
+plugin_set_subtree_config(PluginTargetData *subtree_config, const char *val)
+{
+ int status = 0;
+
+ if (strcasecmp (val, ALL_DATA) == 0) /* allow access to both local and remote data */
+ {
+ plugin_set_global (subtree_config);
+ }
+ else if (strcasecmp (val, LOCAL_DATA) == 0) /* allow access to all locally hosted data */
+ {
+ ptd_set_special_data (subtree_config, PLGC_DATA_LOCAL);
+ }
+ else if (strcasecmp (val, REMOTE_DATA) == 0)/* allow access to requests for remote data */
+ {
+ ptd_set_special_data (subtree_config, PLGC_DATA_REMOTE);
+ }
+ else /* dn */
+ {
+ ptd_add_subtree (subtree_config, slapi_sdn_new_dn_byval(val));
+ }
+ /* I suppose we could check the val at this point to make sure
+ its a valid DN . . . */
+
+ return status;
+}
+
+static int
+set_plugin_config_from_entry(
+ const Slapi_Entry *plugin_entry,
+ struct slapdplugin *plugin
+)
+{
+ struct pluginconfig *config = &plugin->plg_conf;
+ char *value = 0;
+ int status = 0;
+ PRBool target_seen = PR_FALSE;
+ PRBool bind_seen = PR_FALSE;
+
+ if ((value = slapi_entry_attr_get_charptr(plugin_entry,
+ ATTR_PLUGIN_SCHEMA_CHECK)) != NULL)
+ {
+ if (plugin_config_set_action(&config->plgc_schema_check, value))
+ {
+ LDAPDebug(LDAP_DEBUG_PLUGIN, "Error: invalid value %s for attribute %s "
+ "from entry %s\n", value, ATTR_PLUGIN_SCHEMA_CHECK,
+ slapi_entry_get_dn_const(plugin_entry));
+ status = 1;
+ }
+ slapi_ch_free((void**)&value);
+ }
+
+ if ((value = slapi_entry_attr_get_charptr(plugin_entry,
+ ATTR_PLUGIN_LOG_ACCESS)) != NULL)
+ {
+ if (plugin_config_set_action(&config->plgc_log_access, value))
+ {
+ LDAPDebug(LDAP_DEBUG_PLUGIN, "Error: invalid value %s for attribute %s "
+ "from entry %s\n", value, ATTR_PLUGIN_LOG_ACCESS,
+ slapi_entry_get_dn_const(plugin_entry));
+ status = 1;
+ }
+ slapi_ch_free((void**)&value);
+ }
+
+ if ((value = slapi_entry_attr_get_charptr(plugin_entry,
+ ATTR_PLUGIN_LOG_AUDIT)) != NULL)
+ {
+ if (plugin_config_set_action(&config->plgc_log_audit, value))
+ {
+ LDAPDebug(LDAP_DEBUG_PLUGIN, "Error: invalid value %s for attribute %s "
+ "from entry %s\n", value, ATTR_PLUGIN_LOG_AUDIT,
+ slapi_entry_get_dn_const(plugin_entry));
+ status = 1;
+ }
+ slapi_ch_free((void**)&value);
+ }
+
+ if ((value = slapi_entry_attr_get_charptr(plugin_entry,
+ ATTR_PLUGIN_INVOKE_FOR_REPLOP)) != NULL)
+ {
+ if (plugin_config_set_action(&config->plgc_invoke_for_replop, value))
+ {
+ LDAPDebug(LDAP_DEBUG_PLUGIN, "Error: invalid value %s for attribute %s "
+ "from entry %s\n", value, ATTR_PLUGIN_INVOKE_FOR_REPLOP,
+ slapi_entry_get_dn_const(plugin_entry));
+ status = 1;
+ }
+ slapi_ch_free((void**)&value);
+ }
+
+ if ((value = slapi_entry_attr_get_charptr(plugin_entry,
+ ATTR_PLUGIN_TARGET_SUBTREE)) != NULL)
+ {
+ if (plugin_set_subtree_config(&(config->plgc_target_subtrees), value))
+ {
+ LDAPDebug(LDAP_DEBUG_PLUGIN, "Error: invalid value %s for attribute %s "
+ "from entry %s\n", value, ATTR_PLUGIN_TARGET_SUBTREE,
+ slapi_entry_get_dn_const(plugin_entry));
+ status = 1;
+ }
+ else
+ {
+ target_seen = PR_TRUE;
+ }
+ slapi_ch_free((void**)&value);
+ }
+
+ if ((value = slapi_entry_attr_get_charptr(plugin_entry,
+ ATTR_PLUGIN_BIND_SUBTREE)) != NULL)
+ {
+ if (plugin_set_subtree_config(&(config->plgc_bind_subtrees), value))
+ {
+ LDAPDebug(LDAP_DEBUG_PLUGIN, "Error: invalid value %s for attribute %s "
+ "from entry %s\n", value, ATTR_PLUGIN_BIND_SUBTREE,
+ slapi_entry_get_dn_const(plugin_entry));
+ status = 1;
+ }
+ else
+ {
+ bind_seen = PR_TRUE;
+ }
+ slapi_ch_free((void**)&value);
+ }
+
+ /* set target subtree default - allow access to all data */
+ if (!target_seen)
+ {
+ plugin_set_global(&(config->plgc_target_subtrees));
+ }
+
+ /* set bind subtree default - allow access to local data only */
+ if (!bind_seen)
+ {
+ ptd_set_special_data(&(config->plgc_bind_subtrees), PLGC_DATA_LOCAL);
+ ptd_set_special_data(&(config->plgc_bind_subtrees), PLGC_DATA_REMOTE);
+ }
+
+ return status;
+}
+
+#if 0
+static PRBool
+plugin_matches_key (char *arg, char *key)
+{
+ PRBool haveVal = strlen (arg) > strlen (key);
+ return (haveVal && strncasecmp (arg, key, strlen (key)) == 0);
+}
+
+static char*
+plugin_get_str_val (char *arg, char *key)
+{
+ return &(arg[strlen (key)]);
+}
+
+static PRBool
+plugin_get_bool_val (char*arg, char *key, char *true_val)
+{
+ return (strcasecmp (&(arg[strlen (key)]), true_val) == 0);
+}
+#endif
+
+/* This function is called after the plugin init function has been called
+ which fills in the desc part of the plugin
+*/
+static int
+add_plugin_description(Slapi_Entry *e, const char *attrname, char *val)
+{
+ struct berval desc;
+ struct berval *newval[2] = {0, 0};
+ int status = 0;
+
+ desc.bv_val = SLAPI_PLUGIN_NONE_IF_NULL( val );
+ desc.bv_len = strlen(desc.bv_val);
+ newval[0] = &desc;
+ if ((status = entry_replace_values(e, attrname, newval)) != 0)
+ {
+ LDAPDebug(LDAP_DEBUG_PLUGIN, "Error: failed to add value %s to "
+ "attribute %s of entry %s\n", val, attrname,
+ slapi_entry_get_dn_const(e));
+ status = 1;
+ }
+
+ return status;
+}
+
+
+/*
+ * The plugin initfunc sets some vendor and version information in the plugin.
+ * This function extracts that and adds it as attributes to `e'. If
+ * `plugin' is NULL, the plugin is located based on the DN in `e'.
+ *
+ * Returns 0 if all goes well and 1 if not.
+ */
+int
+plugin_add_descriptive_attributes( Slapi_Entry *e, struct slapdplugin *plugin )
+{
+ int status = 0;
+
+ if ( NULL == plugin ) {
+ int i;
+ const Slapi_DN *ednp = slapi_entry_get_sdn_const( e );
+ Slapi_DN pdn;
+ struct slapdplugin *plugtmp;
+
+ for( i = 0; NULL == plugin && i < PLUGIN_LIST_GLOBAL_MAX; ++i )
+ {
+ for ( plugtmp = global_plugin_list[i]; NULL == plugin && plugtmp;
+ plugtmp = plugtmp->plg_next)
+ {
+ slapi_sdn_init_dn_byref( &pdn, plugtmp->plg_dn );
+ if ( 0 == slapi_sdn_compare( &pdn, ednp ))
+ {
+ plugin = plugtmp;
+ }
+ slapi_sdn_done( &pdn );
+ }
+ }
+
+ if ( NULL == plugin )
+ {
+ LDAPDebug(LDAP_DEBUG_PLUGIN,
+ "Error: failed to add descriptive values for plugin %s"
+ " (could not find plugin entry)\n",
+ slapi_entry_get_dn_const(e), 0, 0 );
+ return 1; /* failure */
+ }
+ }
+
+
+ if (add_plugin_description(e, ATTR_PLUGIN_PLUGINID,
+ plugin->plg_desc.spd_id))
+ {
+ status = 1;
+ }
+
+ if (add_plugin_description(e, ATTR_PLUGIN_VERSION,
+ plugin->plg_desc.spd_version))
+ {
+ status = 1;
+ }
+
+ if (add_plugin_description(e, ATTR_PLUGIN_VENDOR,
+ plugin->plg_desc.spd_vendor))
+ {
+ status = 1;
+ }
+
+ if (add_plugin_description(e, ATTR_PLUGIN_DESC,
+ plugin->plg_desc.spd_description))
+ {
+ status = 1;
+ }
+
+ return status;
+}
+
+
+/*
+ clean up the memory associated with the plugin
+*/
+static void
+plugin_free(struct slapdplugin *plugin)
+{
+ charray_free(plugin->plg_argv);
+ slapi_ch_free((void**)&plugin->plg_libpath);
+ slapi_ch_free((void**)&plugin->plg_initfunc);
+ slapi_ch_free((void**)&plugin->plg_name);
+ slapi_ch_free((void**)&plugin->plg_dn);
+ if (!plugin->plg_group)
+ plugin_config_cleanup(&plugin->plg_conf);
+ slapi_ch_free((void**)&plugin);
+}
+
+/***********************************
+This is the main entry point for plugin configuration. The plugin_entry argument
+should already contain the necessary fields required to initialize the plugin and
+to give it a proper name in the plugin configuration DIT.
+
+Argument:
+Slapi_Entry *plugin_entry - the required attributes are
+ dn: the dn of the plugin entry
+ cn: the unique name of the plugin
+ nsslapd-pluginType: one of the several recognized plugin types e.g. "postoperation"
+
+if p_initfunc is given, pluginPath and pluginInitFunc are optional
+ nsslapd-pluginPath: full path and file name of the dll implementing the plugin
+ nsslapd-pluginInitFunc: the name of the plugin initialization function
+
+the optional attributes are:
+ nsslapd-pluginArg0
+ ...
+ nsslapd-pluginArg[N-1] - the (old style) arguments to the plugin, where N varies
+ from 0 to the number of arguments. The numbers must be consecutive i.e. no
+ skipping
+
+ Instead of using nsslapd-pluginArgN, it is encouraged for you to use named
+ parameters e.g.
+ nsslapd-tweakThis: 1
+ nsslapd-tweakThat: 2
+ etc.
+
+ nsslapd-pluginEnabled: "on"|"off" - by default, the plugin will be enabled unless
+ this attribute is present and has the value "off"
+
+ for other known attributes, see set_plugin_config_from_entry() above
+
+ all other attributes will be ignored
+
+ The reason this parameter is not const is because it may be modified. This
+ function will modify it if the plugin init function is called successfully
+ to add the description attributes, and the plugin init function may modify
+ it as well.
+
+Argument:
+group - the group to which this plugin will belong - each member of a plugin group
+ shares the pluginconfig of the group leader; refer to the function plugin_get_config
+ for more information
+
+Argument:
+add_entry - if true, the entry will be added to the DIT using the given
+ DN in the plugin_entry - this is the default behavior; if false, the
+ plugin entry will not show up in the DIT
+************************************/
+int
+plugin_setup(Slapi_Entry *plugin_entry, struct slapi_componentid *group,
+ slapi_plugin_init_fnptr p_initfunc, int add_entry)
+{
+ int ii = 0;
+ char attrname[BUFSIZ];
+ char *value = 0;
+ struct slapdplugin *plugin = NULL;
+ struct slapdplugin **plugin_list = NULL;
+ struct slapi_componentid *cid=NULL;
+ const char *existname = 0;
+ slapi_plugin_init_fnptr initfunc = p_initfunc;
+ Slapi_PBlock pb;
+ int status = 0;
+ int approved = 1;
+ int enabled = 1;
+ char *configdir = 0;
+
+ attrname[0] = '\0';
+
+ if (!slapi_entry_get_sdn_const(plugin_entry))
+ {
+ LDAPDebug(LDAP_DEBUG_ANY, "Error: DN is missing from the plugin.\n",
+ 0, 0, 0);
+ return -1;
+ }
+
+ if ((existname = plugin_exists(slapi_entry_get_sdn_const(plugin_entry))) != NULL)
+ {
+ LDAPDebug(LDAP_DEBUG_ANY, "Error: the plugin named %s "
+ "already exists.\n", existname, 0, 0);
+ return -1;
+ }
+
+ /*
+ * create a new plugin structure, fill it in, and prepare to
+ * call the plugin's init function. the init function will
+ * set the plugin function pointers.
+ */
+ plugin = (struct slapdplugin *)slapi_ch_calloc(1, sizeof(struct slapdplugin));
+
+ plugin->plg_dn = slapi_ch_strdup(slapi_entry_get_dn_const(plugin_entry));
+
+ if (!(value = slapi_entry_attr_get_charptr(plugin_entry,
+ ATTR_PLUGIN_TYPE)))
+ {
+ /* error: required attribute %s missing */
+ LDAPDebug(LDAP_DEBUG_ANY, "Error: required attribute %s is missing "
+ "from entry \"%s\"\n", ATTR_PLUGIN_TYPE,
+ slapi_entry_get_dn_const(plugin_entry), 0);
+ status = -1;
+ goto PLUGIN_CLEANUP;
+ }
+ else
+ {
+ status = plugin_get_type_and_list(value, &plugin->plg_type,
+ &plugin_list);
+
+ if ( status != 0 ) {
+ /* error: unknown plugin type */
+ LDAPDebug(LDAP_DEBUG_ANY, "Error: unknown plugin type \"%s\" "
+ "in entry \"%s\"\n",
+ value, slapi_entry_get_dn_const(plugin_entry), 0);
+ slapi_ch_free((void**)&value);
+ status = -1;
+ goto PLUGIN_CLEANUP;
+ }
+ slapi_ch_free((void**)&value);
+ }
+
+ if (!status &&
+ !(value = slapi_entry_attr_get_charptr(plugin_entry, "cn")))
+ {
+ /* error: required attribute %s missing */
+ LDAPDebug(LDAP_DEBUG_ANY, "Error: required attribute %s is missing "
+ "from entry \"%s\"\n", "cn",
+ slapi_entry_get_dn_const(plugin_entry), 0);
+ status = -1;
+ goto PLUGIN_CLEANUP;
+ }
+ else
+ {
+ plugin->plg_name = value; /* plugin owns value's memory now, don't free */
+ }
+
+ if (!(value = slapi_entry_attr_get_charptr(plugin_entry,
+ ATTR_PLUGIN_INITFN)))
+ {
+ if (!initfunc)
+ {
+ /* error: required attribute %s missing */
+ LDAPDebug(LDAP_DEBUG_ANY, "Error: required attribute %s is missing "
+ "from entry \"%s\"\n", ATTR_PLUGIN_INITFN,
+ slapi_entry_get_dn_const(plugin_entry), 0);
+ status = -1;
+ goto PLUGIN_CLEANUP;
+ }
+ }
+ else
+ {
+ plugin->plg_initfunc = value; /* plugin owns value's memory now, don't free */
+ }
+
+ if (!initfunc)
+ {
+ if (!(value = slapi_entry_attr_get_charptr(plugin_entry,
+ ATTR_PLUGIN_PATH)))
+ {
+ /* error: required attribute %s missing */
+ LDAPDebug(LDAP_DEBUG_ANY, "Error: required attribute %s is missing "
+ "from entry \"%s\"\n", ATTR_PLUGIN_PATH,
+ slapi_entry_get_dn_const(plugin_entry), 0);
+ status = -1;
+ goto PLUGIN_CLEANUP;
+ }
+ else
+ {
+ plugin->plg_libpath = value; /* plugin owns value's memory now, don't free */
+ }
+
+ /*
+ * load the plugin's init function
+ */
+ if ((initfunc = (slapi_plugin_init_fnptr)sym_load(plugin->plg_libpath,
+ plugin->plg_initfunc, plugin->plg_name, 1 /* report errors */
+ )) == NULL)
+ {
+ status = -1;
+ goto PLUGIN_CLEANUP;
+ }
+#ifdef _WIN32
+ {
+ set_debug_level_fn_t fn;
+ /* for Win32 only, attempt to get its debug level init function */
+ if ((fn = (set_debug_level_fn_t)sym_load(plugin->plg_libpath,
+ "plugin_init_debug_level", plugin->plg_name,
+ 0 /* do not report errors */ )) != NULL) {
+ /* we hooked the function, so call it */
+ (*fn)(module_ldap_debug);
+ }
+ }
+#endif
+ }
+
+ if (!status && group) /* uses group's config; see plugin_get_config */
+ {
+ struct slapi_componentid * cid = (struct slapi_componentid *) group;
+ plugin->plg_group = (struct slapdplugin *) cid->sci_plugin;
+ }
+ else if (!status) /* using own config */
+ {
+ plugin_config_init(&(plugin->plg_conf));
+ set_plugin_config_from_entry(plugin_entry, plugin);
+ }
+
+ /* add the plugin arguments */
+ value = 0;
+ ii = 0;
+ sprintf(attrname, "%s%d", ATTR_PLUGIN_ARG, ii);
+ while ((value = slapi_entry_attr_get_charptr(plugin_entry, attrname)) != NULL)
+ {
+ charray_add(&plugin->plg_argv, value);
+ plugin->plg_argc++;
+ ++ii;
+ sprintf(attrname, "%s%d", ATTR_PLUGIN_ARG, ii);
+ }
+
+ memset((char *)&pb, '\0', sizeof(pb));
+ slapi_pblock_set(&pb, SLAPI_PLUGIN, plugin);
+ slapi_pblock_set(&pb, SLAPI_PLUGIN_VERSION, (void *)SLAPI_PLUGIN_CURRENT_VERSION);
+
+ cid = generate_componentid (plugin,NULL);
+ slapi_pblock_set(&pb, SLAPI_PLUGIN_IDENTITY, (void*)cid);
+
+ configdir = config_get_configdir();
+ slapi_pblock_set(&pb, SLAPI_CONFIG_DIRECTORY, configdir);
+
+ if ((*initfunc)(&pb) != 0)
+ {
+ LDAPDebug(LDAP_DEBUG_ANY, "Init function \"%s\" for \"%s\" plugin"
+ " in library \"%s\" failed\n",
+ plugin->plg_initfunc, plugin->plg_name,
+ plugin->plg_libpath);
+ status = -1;
+ goto PLUGIN_CLEANUP;
+ }
+
+ if ( !status ) {
+ status = plugin_add_descriptive_attributes( plugin_entry, plugin );
+ }
+
+ /* see if the plugin is approved or not */
+ if ((approved = isApprovedPlugin(plugin)) != 0)
+ {
+ if ((!plugin->plg_version) ||
+ (!SLAPI_PLUGIN_IS_COMPAT(plugin->plg_version))) {
+ LDAPDebug( LDAP_DEBUG_ANY, "Plugin \"%s\" from library \"%s\""
+ " has wrong version (supported versions: %s)\n",
+ plugin->plg_name, plugin->plg_libpath,
+ SLAPI_PLUGIN_SUPPORTED_VERSIONS);
+ approved = 0;
+ }
+ }
+
+ /* see if the plugin is enabled or not */
+ if ((value = slapi_entry_attr_get_charptr(plugin_entry,
+ ATTR_PLUGIN_ENABLED)) &&
+ !strcasecmp(value, "off"))
+ {
+ enabled = 0;
+ }
+ else
+ {
+ enabled = 1;
+ }
+
+ if (value)
+ slapi_ch_free((void**)&value);
+
+ if (!approved) {
+ enabled = 0;
+ LDAPDebug(LDAP_DEBUG_ANY, "Plugin \"%s\" is disabled.\n",
+ plugin->plg_name,0,0);
+ }
+
+ if (approved)
+ {
+ if(enabled)
+ {
+ /* don't use raw pointer from plugin_entry because it
+ will be freed later by the caller */
+ Slapi_DN *dn_copy = slapi_sdn_dup(slapi_entry_get_sdn_const(plugin_entry));
+ add_plugin_to_list(plugin_list, plugin);
+ add_plugin_entry_dn(dn_copy);
+ }
+
+ if (add_entry)
+ {
+ /* make a copy of the plugin entry for our own use because it will
+ be freed later by the caller */
+ Slapi_Entry *e_copy = slapi_entry_dup(plugin_entry);
+ /* new_plugin_entry(&plugin_entries, plugin_entry, plugin); */
+ new_plugin_entry(&dep_plugin_entries, e_copy, plugin);
+ }
+ }
+
+
+PLUGIN_CLEANUP:
+ if (status)
+ plugin_free(plugin);
+ slapi_ch_free((void **)&configdir);
+
+ return status;
+}
+
+/* set default configuration parameters */
+static void
+plugin_config_init (struct pluginconfig *config)
+{
+ PR_ASSERT (config);
+
+ ptd_init (&config->plgc_target_subtrees);
+ ptd_init (&config->plgc_bind_subtrees);
+ config->plgc_schema_check = PLGC_ON;
+ config->plgc_invoke_for_replop = PLGC_ON;
+ /* currently, we leave it up to plugin, but don't actually tell plugins that they can choose.
+ We want changes to always be logged by regular plugins to avoid data inconsistency, but we
+ want to allow internal plugins like replication to make the decision.*/
+ config->plgc_log_change = PLGC_UPTOPLUGIN;
+ config->plgc_log_access = PLGC_OFF;
+ config->plgc_log_audit = PLGC_OFF;
+}
+
+static int
+plugin_config_set_action (int *action, char *value)
+{
+ PR_ASSERT (action);
+ PR_ASSERT (value);
+
+ if (strcasecmp (value, "on") == 0)
+ {
+ *action = PLGC_ON;
+ }
+ else if (strcasecmp (value, "off") == 0)
+ {
+ *action = PLGC_OFF;
+ }
+ else if (strcasecmp (value, "uptoplugin") == 0)
+ {
+ *action = PLGC_UPTOPLUGIN;
+ }
+ else
+ {
+ slapi_log_error(SLAPI_LOG_FATAL, NULL,
+ "plugin_config_set_action: invalid action %s\n", value);
+ return -1;
+ }
+
+ return 0;
+}
+
+static void
+plugin_config_cleanup (struct pluginconfig *config)
+{
+ PR_ASSERT (config);
+
+ ptd_cleanup (&config->plgc_target_subtrees);
+ ptd_cleanup (&config->plgc_bind_subtrees);
+}
+
+#if 0
+static char*
+plugin_config_action_to_string (int action)
+{
+ switch (action)
+ {
+ case PLGC_ON: return "on";
+ case PLGC_OFF: return "off";
+ case PLGC_UPTOPLUGIN: return "uptoplugin";
+ default: return NULL;
+ }
+}
+#endif
+
+static struct pluginconfig*
+plugin_get_config (struct slapdplugin *plugin)
+{
+ struct slapdplugin *temp = plugin;
+
+ PR_ASSERT (plugin);
+
+ while (temp->plg_group)
+ {
+ temp = temp->plg_group;
+ }
+
+ return &(temp->plg_conf);
+}
+
+static PRBool
+plugin_invoke_plugin_pb (struct slapdplugin *plugin, int operation, Slapi_PBlock *pb)
+{
+ Slapi_DN *target_spec;
+ PRBool rc;
+
+ PR_ASSERT (plugin);
+ PR_ASSERT (pb);
+
+ /* we always allow initialization and cleanup operations */
+ if (operation == SLAPI_PLUGIN_START_FN || operation == SLAPI_PLUGIN_POSTSTART_FN ||
+ operation == SLAPI_PLUGIN_CLOSE_FN || operation == SLAPI_PLUGIN_CLEANUP_FN)
+ return PR_TRUE;
+
+ PR_ASSERT (pb->pb_op);
+
+ target_spec = operation_get_target_spec (pb->pb_op);
+
+ PR_ASSERT (target_spec);
+
+ rc = plugin_invoke_plugin_sdn (plugin, operation, pb, target_spec);
+
+ return rc;
+}
+
+PRBool
+plugin_invoke_plugin_sdn (struct slapdplugin *plugin, int operation, Slapi_PBlock *pb, Slapi_DN *target_spec)
+{
+ PluginTargetData *ptd;
+ struct pluginconfig *config;
+ Slapi_Backend *be;
+ int isroot;
+ PRBool islocal;
+ PRBool bindop;
+ unsigned long op;
+ PRBool rc;
+ int method = -1;
+
+ PR_ASSERT (plugin);
+
+ /* get configuration from the group plugin if necessary */
+ config = plugin_get_config (plugin);
+ slapi_pblock_get(pb, SLAPI_BIND_METHOD, &method);
+ /* check if plugin is configured to service replicated operations */
+ if (!config->plgc_invoke_for_replop)
+ {
+ int repl_op;
+
+ /* if pb is NULL we assume it is not a replicated operation */
+ if (pb)
+ {
+ slapi_pblock_get (pb, SLAPI_IS_REPLICATED_OPERATION, &repl_op);
+ if (repl_op)
+ return PR_FALSE;
+ }
+ }
+
+ if (pb)
+ {
+ if (pb->pb_op)
+ {
+ op = operation_get_type(pb->pb_op);
+
+ if (op == SLAPI_OPERATION_BIND || op == SLAPI_OPERATION_UNBIND)
+ {
+ bindop = PR_TRUE;
+ }
+ else
+ {
+ bindop = PR_FALSE;
+ }
+
+ slapi_pblock_get (pb, SLAPI_REQUESTOR_ISROOT, &isroot);
+ }
+ else
+ {
+ bindop = PR_FALSE;
+ isroot = 1;
+ }
+
+ slapi_pblock_get (pb, SLAPI_BACKEND, &be);
+
+ /* determine whether data are local or remote */
+ /* remote if chaining backend or default backend */
+
+ if ( be!=NULL ) {
+ islocal=!(slapi_be_is_flag_set(be,SLAPI_BE_FLAG_REMOTE_DATA));
+ } else {
+ islocal = be != defbackend_get_backend();
+ }
+
+ }
+ else
+ {
+ bindop = PR_FALSE;
+ islocal = PR_TRUE;
+ isroot = 1;
+ }
+
+ if (bindop)
+ {
+ ptd = &(config->plgc_bind_subtrees);
+ }
+ else
+ {
+ ptd = &(config->plgc_target_subtrees);
+ }
+
+ rc = plugin_matches_operation (target_spec, ptd, bindop, isroot, islocal, method);
+
+ return rc;
+}
+
+/* this interface is exposed to be used by internal operations.
+ */
+char* plugin_get_dn (const struct slapdplugin *plugin)
+{
+ char *plugindn;
+ char *pattern = "cn=%s," PLUGIN_BASE_DN;
+
+ if (plugin == NULL) /* old plugin that does not pass identity - use default */
+ plugin = &global_default_plg;
+
+ if (plugin->plg_name == NULL)
+ return NULL;
+
+ plugindn = (char*)slapi_ch_malloc (strlen (pattern) + strlen (plugin->plg_name));
+ if (plugindn)
+ sprintf (plugindn, pattern, plugin->plg_name);
+
+ return plugindn;
+}
+
+static PRBool plugin_is_global (const PluginTargetData *ptd)
+{
+ /* plugin is considered to be global if it is invoked for
+ global data, local data and anonymous bind (bind target
+ data only). We don't include directory manager here
+ as it is considered to be part of local data */
+ return (ptd_is_special_data_set (ptd, PLGC_DATA_LOCAL) &&
+ ptd_is_special_data_set (ptd, PLGC_DATA_REMOTE) &&
+ ptd_is_special_data_set (ptd, PLGC_DATA_BIND_ANONYMOUS) &&
+ ptd_is_special_data_set (ptd, PLGC_DATA_BIND_ROOT));
+}
+
+static void plugin_set_global (PluginTargetData *ptd)
+{
+ PR_ASSERT (ptd);
+
+ /* plugin is global if it is allowed access to all data */
+ ptd_set_special_data (ptd, PLGC_DATA_LOCAL);
+ ptd_set_special_data (ptd, PLGC_DATA_REMOTE);
+ ptd_set_special_data (ptd, PLGC_DATA_BIND_ANONYMOUS);
+ ptd_set_special_data (ptd, PLGC_DATA_BIND_ROOT);
+}
+
+static void plugin_set_default_access (struct pluginconfig *config)
+{
+ /* by default, plugins are invoked if dn is local for bind operations,
+ and for all requests for all other operations */
+ PR_ASSERT (config);
+
+ plugin_set_global (&config->plgc_target_subtrees);
+ ptd_set_special_data (&config->plgc_bind_subtrees, PLGC_DATA_LOCAL);
+ ptd_set_special_data (&config->plgc_bind_subtrees, PLGC_DATA_REMOTE);
+}
+
+/* determine whether operation should be allowed based on plugin configuration */
+PRBool plugin_allow_internal_op (Slapi_DN *target_spec, struct slapdplugin *plugin)
+{
+ struct pluginconfig *config = plugin_get_config (plugin);
+ Slapi_Backend *be;
+ int islocal;
+
+ if (plugin_is_global (&config->plgc_target_subtrees))
+ return PR_TRUE;
+
+ /* ONREPL - we do be_select to decide whether the request is for local
+ or remote data. We might need to reconsider how to do this
+ for performance reasons since be_select will be done again
+ once the operation goes through */
+ be = slapi_be_select(target_spec);
+
+ /* determine whether data are local or remote */
+ /* remote if chaining backend or default backend */
+
+ if ( be!=NULL ) {
+ islocal=!(slapi_be_is_flag_set(be,SLAPI_BE_FLAG_REMOTE_DATA));
+ } else {
+ islocal = be != defbackend_get_backend();
+ }
+ /* SIMPLE auth method sends us through original code path in plugin_mathches_operation */
+ return plugin_matches_operation (target_spec, &config->plgc_target_subtrees,
+ PR_FALSE, PR_FALSE, islocal, LDAP_AUTH_SIMPLE);
+}
+
+static PRBool plugin_matches_operation (Slapi_DN *target_spec, PluginTargetData *ptd,
+ PRBool bindop, PRBool isroot, PRBool islocal, int method)
+{
+ int cookie;
+ Slapi_DN *subtree;
+
+ /* check for special cases */
+
+ if (plugin_is_global (ptd))
+ return PR_TRUE;
+
+ /* if method is SASL we can have a null DN so bypass this check*/
+ if(method != LDAP_AUTH_SASL) {
+ if (bindop && target_spec && (slapi_sdn_get_dn (target_spec) == NULL ||
+ slapi_sdn_get_dn (target_spec)[0] == '\0'))
+ {
+ return (ptd_is_special_data_set (ptd, PLGC_DATA_BIND_ANONYMOUS));
+ }
+ }
+
+ /* check for root bind */
+ if (bindop && isroot)
+ {
+ return (ptd_is_special_data_set (ptd, PLGC_DATA_BIND_ROOT));
+ }
+
+ /* check for local data */
+ if (ptd_is_special_data_set (ptd, PLGC_DATA_LOCAL) && islocal)
+ {
+ return PR_TRUE;
+ }
+
+ /* check for remote data */
+ if (ptd_is_special_data_set (ptd, PLGC_DATA_REMOTE) && !islocal)
+ {
+ return (PR_TRUE);
+ }
+
+ subtree = ptd_get_first_subtree (ptd, &cookie);
+ while (subtree)
+ {
+ if (slapi_sdn_issuffix (target_spec, subtree))
+ return (PR_TRUE);
+
+ subtree = ptd_get_next_subtree (ptd, &cookie);
+ }
+
+ return PR_FALSE;
+
+}
+
+/* build operation action bitmap based on plugin configuration and actions specified for the operation */
+int plugin_build_operation_action_bitmap (int input_actions, const struct slapdplugin *plugin)
+{
+ int result_actions = 0;
+
+ /* old plugin that does not pass its identity to the operation */
+ if (plugin == NULL)
+ plugin = &global_default_plg;
+
+ if (plugin->plg_conf.plgc_log_access)
+ result_actions |= OP_FLAG_ACTION_LOG_ACCESS;
+
+ if (plugin->plg_conf.plgc_log_audit)
+ result_actions |= OP_FLAG_ACTION_LOG_AUDIT;
+
+ /*
+ * OP_FLAG_ACTION_INVOKE_FOR_REPLOP is now used only by URP code.
+ * If someday this code needs to reclaim the flag, it has to use
+ * another flag to avoid the conflict with URP code.
+ *
+ * if (plugin->plg_conf.plgc_invoke_for_replop)
+ * result_actions |= OP_FLAG_ACTION_INVOKE_FOR_REPLOP;
+ */
+
+ switch (plugin->plg_conf.plgc_schema_check)
+ {
+ case PLGC_OFF: result_actions &= ~OP_FLAG_ACTION_SCHEMA_CHECK;
+ break;
+
+ case PLGC_ON: result_actions |= OP_FLAG_ACTION_SCHEMA_CHECK;
+ break;
+
+ case PLGC_UPTOPLUGIN: break;
+
+ default: PR_ASSERT (PR_FALSE);
+ }
+
+ switch (plugin->plg_conf.plgc_log_change)
+ {
+ case PLGC_OFF: result_actions &= ~OP_FLAG_ACTION_LOG_CHANGES;
+ break;
+
+ case PLGC_ON: result_actions |= OP_FLAG_ACTION_LOG_CHANGES;
+ break;
+
+ case PLGC_UPTOPLUGIN: break;
+
+ default: PR_ASSERT (PR_FALSE);
+ }
+
+ return result_actions;
+}
+
+const struct slapdplugin*
+plugin_get_server_plg()
+{
+ if(!global_server_plg_initialised)
+ {
+ global_server_plg.plg_name = "server";
+ plugin_set_global (&global_server_plg.plg_conf.plgc_target_subtrees);
+ global_server_plg.plg_conf.plgc_log_access = 1;
+ global_server_plg.plg_conf.plgc_log_audit = 1;
+ global_server_plg.plg_conf.plgc_schema_check = 1;
+ global_server_plg.plg_conf.plgc_log_change = 1;
+ global_server_plg_initialised= 1;
+ global_server_plg_initialised= 1;
+ }
+ return &global_server_plg;
+}
+
+struct slapi_componentid * plugin_get_default_component_id() {
+
+ if(!global_server_plg_id_initialised) {
+ global_server_id_plg.sci_plugin=plugin_get_server_plg();
+ global_server_id_plg.sci_component_name=
+ plugin_get_dn(global_server_id_plg.sci_plugin);
+ global_server_plg_id_initialised=1;
+ }
+ return &global_server_id_plg;
+}
+
+static void
+default_plugin_init()
+{
+ global_default_plg.plg_name = "old plugin";
+ plugin_config_init (&global_default_plg.plg_conf);
+ plugin_set_default_access (&global_default_plg.plg_conf);
+}
+
+#if 0
+static void trace_plugin_invocation (Slapi_DN *target_spec, PluginTargetData *ptd,
+ PRBool bindop, PRBool isroot, PRBool islocal, int invoked)
+{
+ int cookie, i = 0;
+ Slapi_DN *sdn;
+
+
+ slapi_log_error (SLAPI_LOG_FATAL, NULL,
+ "Invocation parameters: target_spec = %s, bindop = %d, isroot=%d, islocal=%d\n"
+ "Plugin configuration: local_data=%d, remote_data=%d, anonymous_bind=%d, root_bind=%d\n",
+ slapi_sdn_get_ndn (target_spec), bindop, isroot, islocal, ptd->special_data[0],
+ ptd->special_data[1], ptd->special_data[2], ptd->special_data[3]);
+
+ sdn = ptd_get_first_subtree (ptd, &cookie);
+ while (sdn)
+ {
+ slapi_log_error (SLAPI_LOG_FATAL, NULL, "target_subtree%d: %s\n", i, slapi_sdn_get_ndn (sdn));
+ sdn = ptd_get_next_subtree (ptd, &cookie);
+ }
+
+ slapi_log_error (SLAPI_LOG_FATAL, NULL, invoked ? "Plugin is invoked\n" : "Plugin is not invoked\n");
+}
+#endif
+
+/* functions to manipulate PluginTargetData type */
+static void ptd_init (PluginTargetData *ptd)
+{
+ PR_ASSERT (ptd);
+
+ dl_init (&ptd->subtrees, 0 /* initial count */);
+ memset (&ptd->special_data, 0, sizeof (ptd->special_data));
+}
+
+static void ptd_cleanup (PluginTargetData *ptd)
+{
+ PR_ASSERT (ptd);
+
+ dl_cleanup (&ptd->subtrees, (FREEFN)slapi_sdn_free);
+ memset (&ptd->special_data, 0, sizeof (ptd->special_data));
+}
+
+static void ptd_add_subtree (PluginTargetData *ptd, Slapi_DN *subtree)
+{
+ PR_ASSERT (ptd);
+ PR_ASSERT (subtree);
+
+ dl_add (&ptd->subtrees, subtree);
+}
+
+static void ptd_set_special_data (PluginTargetData *ptd, int type)
+{
+ PR_ASSERT (ptd);
+ PR_ASSERT (type >= 0 && type < PLGC_DATA_MAX);
+
+ ptd->special_data [type] = PR_TRUE;
+}
+
+#if 0
+static void ptd_clear_special_data (PluginTargetData *ptd, int type)
+{
+ PR_ASSERT (ptd);
+ PR_ASSERT (type >= 0 && type < PLGC_DATA_MAX);
+
+ ptd->special_data [type] = PR_FALSE;
+}
+#endif
+
+static Slapi_DN *ptd_get_first_subtree (const PluginTargetData *ptd, int *cookie)
+{
+ PR_ASSERT (ptd);
+
+ return dl_get_first (&ptd->subtrees, cookie);
+}
+
+static Slapi_DN *ptd_get_next_subtree (const PluginTargetData *ptd, int *cookie)
+{
+ PR_ASSERT (ptd);
+
+ return dl_get_next (&ptd->subtrees, cookie);
+}
+
+static PRBool ptd_is_special_data_set (const PluginTargetData *ptd, int type)
+{
+ PR_ASSERT (ptd);
+ PR_ASSERT (type >= 0 && type < PLGC_DATA_MAX);
+
+ return ptd->special_data [type];
+}
+
+#if 0
+static Slapi_DN* ptd_delete_subtree (PluginTargetData *ptd, Slapi_DN *subtree)
+{
+ PR_ASSERT (ptd);
+ PR_ASSERT (subtree);
+
+ return (Slapi_DN*)dl_delete (&ptd->subtrees, subtree, (CMPFN)slapi_sdn_compare, NULL);
+}
+#endif
+
+int ptd_get_subtree_count (const PluginTargetData *ptd)
+{
+ PR_ASSERT (ptd);
+
+ return dl_get_count (&ptd->subtrees);
+}
+
+/* needed by command-line tasks to find an instance's plugin */
+struct slapdplugin *plugin_get_by_name(char *name)
+{
+ int x;
+ struct slapdplugin *plugin;
+
+ for(x = 0; x < PLUGIN_LIST_GLOBAL_MAX; x++) {
+ for(plugin = global_plugin_list[x]; plugin; plugin = plugin->plg_next) {
+ if (!strcmp(name, plugin->plg_name)) {
+ return plugin;
+ }
+ }
+ }
+
+ return NULL;
+}
+
+struct slapi_componentid *
+generate_componentid ( struct slapdplugin * pp , char * name )
+{
+ struct slapi_componentid * idp;
+
+ idp = (struct slapi_componentid *) slapi_ch_calloc(1, sizeof( *idp ));
+ if ( pp )
+ idp->sci_plugin=pp;
+ else
+ idp->sci_plugin=(struct slapdplugin *) plugin_get_server_plg();
+
+ if ( name )
+ idp->sci_component_name = slapi_ch_strdup(name);
+ else
+ /* Use plugin dn */
+ idp->sci_component_name = plugin_get_dn( idp->sci_plugin );
+
+ if (idp->sci_component_name)
+ slapi_dn_normalize(idp->sci_component_name);
+ return idp;
+}
+
+void release_componentid ( struct slapi_componentid * id )
+{
+ if ( id ) {
+ if ( id->sci_component_name ) {
+ slapi_ch_free((void **)&id->sci_component_name);
+ id->sci_component_name=NULL;
+ }
+ slapi_ch_free((void **)&id);
+ }
+}
+
+/* used in main.c if -V flag is given */
+
+static void slapd_print_plugin_version (
+ struct slapdplugin *plg,
+ struct slapdplugin *prev
+)
+{
+ if (plg == NULL || plg->plg_libpath == NULL) return;
+
+ /* same library as previous - don't print twice */
+ if (prev != NULL && prev->plg_libpath != NULL) {
+ if (strcmp(prev->plg_libpath,plg->plg_libpath) == 0) {
+ return;
+ }
+ }
+
+ printf("%s: %s\n",
+ plg->plg_libpath,
+ plg->plg_desc.spd_version ? plg->plg_desc.spd_version : "");
+}
+
+static void slapd_print_pluginlist_versions(struct slapdplugin *plg)
+{
+ struct slapdplugin *p,*prev = NULL;
+
+ for (p = plg; p != NULL; p = p->plg_next) {
+ slapd_print_plugin_version(p,prev);
+ prev = p;
+ }
+}
+
+void plugin_print_versions(void)
+{
+ int i;
+
+ for (i = 0; i < PLUGIN_LIST_GLOBAL_MAX; i++) {
+ slapd_print_pluginlist_versions(get_plugin_list(i));
+ }
+
+}
diff --git a/ldap/servers/slapd/plugin_acl.c b/ldap/servers/slapd/plugin_acl.c
new file mode 100644
index 00000000..2115dfbd
--- /dev/null
+++ b/ldap/servers/slapd/plugin_acl.c
@@ -0,0 +1,192 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+
+/*
+ * plugin_acl.c - routines for calling access control plugins
+ */
+
+#include "slap.h"
+
+static int
+acl_default_access ( Slapi_PBlock *pb , Slapi_Entry *e, int access)
+{
+
+ int isRoot, rootdse, accessCheckDisabled;
+ int rc;
+
+ slapi_pblock_get ( pb, SLAPI_REQUESTOR_ISROOT, &isRoot);
+ if ( isRoot ) return LDAP_SUCCESS;
+
+ rc = slapi_pblock_get ( pb, SLAPI_PLUGIN_DB_NO_ACL, &accessCheckDisabled );
+ if ( rc != -1 && accessCheckDisabled ) return LDAP_SUCCESS;
+
+ rootdse = slapi_is_rootdse ( slapi_entry_get_ndn ( e ) );
+ if ( rootdse && (access & (SLAPI_ACL_READ | SLAPI_ACL_SEARCH) ) )
+ return LDAP_SUCCESS;
+
+ return LDAP_INSUFFICIENT_ACCESS;
+}
+
+int
+plugin_call_acl_plugin ( Slapi_PBlock *pb, Slapi_Entry *e, char **attrs,
+ struct berval *val, int access , int flags, char **errbuf)
+{
+ struct slapdplugin *p;
+ int rc = LDAP_INSUFFICIENT_ACCESS;
+ int aclplugin_initialized = 0;
+ Operation *operation;
+
+ slapi_pblock_get (pb, SLAPI_OPERATION, &operation);
+
+ /* we don't perform acl check for internal operations and if the plugin has set it not to be checked */
+ if (operation_is_flag_set(operation, SLAPI_OP_FLAG_NO_ACCESS_CHECK|OP_FLAG_INTERNAL|OP_FLAG_REPLICATED|OP_FLAG_LEGACY_REPLICATION_DN))
+ return LDAP_SUCCESS;
+
+ /* call the global plugins first and then the backend specific */
+ for ( p = get_plugin_list(PLUGIN_LIST_ACL); p != NULL; p = p->plg_next ) {
+ if (plugin_invoke_plugin_sdn (p, SLAPI_PLUGIN_ACL_ALLOW_ACCESS, pb,
+ (Slapi_DN*)slapi_entry_get_sdn_const (e))){
+ aclplugin_initialized = 1;
+ rc = (*p->plg_acl_access_allowed)(pb, e, attrs, val, access, flags, errbuf);
+ if ( rc != LDAP_SUCCESS ) break;
+ }
+ }
+
+ if (! aclplugin_initialized ) {
+ rc = acl_default_access ( pb, e, access);
+ }
+ return rc;
+}
+
+int
+plugin_call_acl_mods_access ( Slapi_PBlock *pb, Slapi_Entry *e, LDAPMod **mods, char **errbuf )
+{
+
+ struct slapdplugin *p;
+ int aclplugin_initialized = 0;
+ int rc = LDAP_INSUFFICIENT_ACCESS;
+ Operation *operation;
+
+ slapi_pblock_get (pb, SLAPI_OPERATION, &operation);
+
+ /* we don't perform acl check for internal operations and if the plugin has set it not to be checked */
+ if (operation_is_flag_set(operation, SLAPI_OP_FLAG_NO_ACCESS_CHECK|OP_FLAG_INTERNAL|OP_FLAG_REPLICATED|OP_FLAG_LEGACY_REPLICATION_DN))
+ return LDAP_SUCCESS;
+
+ /* call the global plugins first and then the backend specific */
+ for ( p = get_plugin_list(PLUGIN_LIST_ACL); p != NULL; p = p->plg_next ) {
+ if (plugin_invoke_plugin_sdn (p, SLAPI_PLUGIN_ACL_MODS_ALLOWED, pb,
+ (Slapi_DN*)slapi_entry_get_sdn_const (e))){
+ aclplugin_initialized = 1;
+ rc = (*p->plg_acl_mods_allowed)( pb, e, mods, errbuf );
+ if ( rc != LDAP_SUCCESS ) break;
+ }
+ }
+ if (! aclplugin_initialized ) {
+ rc = acl_default_access ( pb, e, SLAPI_ACL_WRITE);
+ }
+ return rc;
+}
+
+/* This plugin should be called immediatly after the changes have been comitted */
+/* This function is now fully executed for internal and replicated ops. */
+int
+plugin_call_acl_mods_update ( Slapi_PBlock *pb, int optype )
+{
+ struct slapdplugin *p;
+ char *dn;
+ int rc = 0;
+ void *change = NULL;
+ Slapi_Entry *te = NULL;
+ Slapi_DN sdn;
+ Operation *operation;
+
+ slapi_pblock_get (pb, SLAPI_OPERATION, &operation);
+
+ (void)slapi_pblock_get( pb, SLAPI_TARGET_DN, &dn );
+
+ switch ( optype ) {
+ case SLAPI_OPERATION_MODIFY:
+ (void)slapi_pblock_get( pb, SLAPI_MODIFY_MODS, &change );
+ break;
+ case SLAPI_OPERATION_ADD:
+ (void)slapi_pblock_get( pb, SLAPI_ADD_ENTRY, &change );
+ te = (Slapi_Entry *)change;
+ if(!slapi_sdn_isempty(slapi_entry_get_sdn(te)))
+ {
+ dn= (char*)slapi_sdn_get_ndn(slapi_entry_get_sdn(te)); /* jcm - Had to cast away const */
+ }
+ break;
+ case SLAPI_OPERATION_MODRDN:
+ (void)slapi_pblock_get( pb, SLAPI_MODRDN_NEWRDN, &change );
+ break;
+ }
+
+ slapi_sdn_init_dn_byref (&sdn, dn);
+ /* call the global plugins first and then the backend specific */
+ for ( p = get_plugin_list(PLUGIN_LIST_ACL); p != NULL; p = p->plg_next ) {
+ if (plugin_invoke_plugin_sdn (p, SLAPI_PLUGIN_ACL_MODS_UPDATE, pb, &sdn)){
+ rc = (*p->plg_acl_mods_update)(pb, optype, dn, change );
+ if ( rc != LDAP_SUCCESS ) break;
+ }
+ }
+
+ slapi_sdn_done (&sdn);
+ return rc;
+}
+
+int
+plugin_call_acl_verify_syntax ( Slapi_PBlock *pb, Slapi_Entry *e, char **errbuf )
+{
+
+ struct slapdplugin *p;
+ int rc = 0;
+ int plugin_called = 0;
+ Operation *operation;
+
+ slapi_pblock_get (pb, SLAPI_OPERATION, &operation);
+
+ /* we don't perform acl check for internal operations and if the plugin has set it not to be checked */
+ if (operation_is_flag_set(operation, SLAPI_OP_FLAG_NO_ACCESS_CHECK|OP_FLAG_INTERNAL|OP_FLAG_REPLICATED|OP_FLAG_LEGACY_REPLICATION_DN))
+ return LDAP_SUCCESS;
+
+ /* call the global plugins first and then the backend specific */
+ for ( p = get_plugin_list(PLUGIN_LIST_ACL); p != NULL; p = p->plg_next ) {
+ if (plugin_invoke_plugin_sdn (p, SLAPI_PLUGIN_ACL_SYNTAX_CHECK, pb,
+ (Slapi_DN*)slapi_entry_get_sdn_const (e))){
+ plugin_called = 1;
+ rc = (*p->plg_acl_syntax_check)( e, errbuf );
+ if ( rc != LDAP_SUCCESS ) break;
+ }
+ }
+
+ if ( !plugin_called ) {
+ LDAPDebug ( LDAP_DEBUG_ANY, "The ACL plugin is not initialized. The aci syntax cannot be verified\n",0,0,0);
+ }
+ return rc;
+}
+
+int slapi_access_allowed( Slapi_PBlock *pb, Slapi_Entry *e, char *attr,
+ struct berval *val, int access )
+{
+ char *attrs[2] = { NULL, NULL };
+
+ attrs[0] = attr;
+ return ( plugin_call_acl_plugin ( pb, e, attrs, val, access, ACLPLUGIN_ACCESS_DEFAULT, NULL ) );
+}
+
+int slapi_acl_check_mods( Slapi_PBlock *pb, Slapi_Entry *e, LDAPMod **mods, char **errbuf )
+{
+
+ return ( plugin_call_acl_mods_access ( pb, e, mods, errbuf ) );
+
+
+}
+
+int slapi_acl_verify_aci_syntax (Slapi_PBlock *pb, Slapi_Entry *e, char **errbuf)
+{
+ return ( plugin_call_acl_verify_syntax ( pb, e, errbuf ) );
+}
diff --git a/ldap/servers/slapd/plugin_internal_op.c b/ldap/servers/slapd/plugin_internal_op.c
new file mode 100644
index 00000000..63a46e1b
--- /dev/null
+++ b/ldap/servers/slapd/plugin_internal_op.c
@@ -0,0 +1,864 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+/* stevross@netscape.com June 13 1997 */
+
+#include <stdio.h>
+#include <sys/types.h>
+#include "slapi-plugin.h"
+#include "slap.h"
+
+#if defined(NET_SSL)
+#include <ssl.h>
+#endif
+
+/* entry list node */
+typedef struct Entry_Node{
+ Slapi_Entry *data;
+ struct Entry_Node *next;
+} Entry_Node;
+
+/* referral list node */
+typedef struct Referral_Node{
+ char *data;
+ struct Referral_Node *next;
+} Referral_Node;
+
+/* data that must be passed to slapi_search_internal_callback */
+typedef struct plugin_search_internal_data{
+ int rc;
+ int num_entries;
+ int num_referrals;
+ Entry_Node *entry_list_head;
+ Referral_Node *referral_list_head;
+} plugin_search_internal_data;
+
+/* callback functions */
+typedef struct callback_fn_ptrs{
+ plugin_result_callback p_res_callback;
+ plugin_search_entry_callback p_srch_entry_callback;
+ plugin_referral_entry_callback p_ref_entry_callback;
+ void *callback_data;
+}callback_fn_ptrs;
+
+/* forward declarations */
+static int seq_internal_callback_pb (Slapi_PBlock *pb, void *callback_data,
+ plugin_result_callback prc,
+ plugin_search_entry_callback psec,
+ plugin_referral_entry_callback prec);
+static int search_internal_pb (Slapi_PBlock *pb);
+static int search_internal_callback_pb (Slapi_PBlock *pb, void *callback_data, plugin_result_callback prc,
+ plugin_search_entry_callback psec, plugin_referral_entry_callback prec);
+
+void
+internal_getresult_callback(struct conn *unused1,
+ struct op *op,
+ int err,
+ char *unused2,
+ char *unused3,
+ int unused4,
+ struct berval **unused5)
+{
+
+ if (op != NULL)
+ {
+ *((int *)op->o_handler_data) = err;
+ }
+
+}
+
+void
+internal_res_callback(struct conn *unused1, struct op *op, int err, char *unused2,
+ char *unused3, int unused4, struct berval **unused5)
+{
+ /* make sure the user has a callback defined, if so do it, otherwise do nothing */
+ if( ((callback_fn_ptrs *)op->o_handler_data) != NULL
+ && ((callback_fn_ptrs *)op->o_handler_data)->p_res_callback != NULL
+ )
+ {
+ ((callback_fn_ptrs *)op->o_handler_data)->p_res_callback
+ (err, ((callback_fn_ptrs *)op->o_handler_data)->callback_data);
+ }
+
+}
+
+int
+internal_srch_entry_callback(Slapi_Backend* be, Connection* conn,
+ Operation* op, Slapi_Entry* e)
+{
+ /* make sure the user has a callback defined, if so do it, otherwise do nothing */
+ if( ((callback_fn_ptrs *)op->o_handler_data) != NULL
+ && ((callback_fn_ptrs *)op->o_handler_data)->p_srch_entry_callback != NULL
+ )
+ {
+ return(((callback_fn_ptrs *)op->o_handler_data)->p_srch_entry_callback
+ (e, ((callback_fn_ptrs *)op->o_handler_data)->callback_data));
+ }
+
+ return(0);
+}
+
+int
+internal_ref_entry_callback(Slapi_Backend * be, Connection *conn,
+ Operation *op, struct berval **ireferral)
+{
+
+ int i;
+
+ /* make sure the user has a callback defined, if so do it, otherwise do nothing */
+ if( ((callback_fn_ptrs *)op->o_handler_data) != NULL
+ && ((callback_fn_ptrs *)op->o_handler_data)->p_ref_entry_callback != NULL
+ && ireferral != NULL
+ )
+ {
+ /* loop over referrals calling callback for each one */
+ for(i=0; ireferral[i] != NULL; i++)
+ {
+ ((callback_fn_ptrs *)op->o_handler_data)->p_ref_entry_callback(ireferral[i]->bv_val,
+ ((callback_fn_ptrs *)op->o_handler_data)->callback_data);
+ }
+ }
+ return(0);
+}
+
+Slapi_Operation*
+internal_operation_new(unsigned long op_type, int flags)
+{
+ Slapi_Operation *op= operation_new(flags | OP_FLAG_INTERNAL /*Internal*/);
+ /* set operation type: add, delete, etc */
+ operation_set_type(op,op_type);
+ /* Call the plugin extension constructors */
+ op->o_extension = factory_create_extension(get_operation_object_type(),op,NULL /* Parent */);
+ return op;
+}
+
+/******************************************************************************
+*
+* do_disconnect_server
+*
+*
+*
+*
+*******************************************************************************/
+
+/* this is just a wrapper exposed to the plugins */
+void
+slapi_disconnect_server(Slapi_Connection *conn)
+{
+ do_disconnect_server(conn, conn->c_connid, -1);
+}
+
+static get_disconnect_server_fn_ptr disconnect_server_fn = NULL;
+
+void
+do_disconnect_server(Connection *conn, int opconnid, int opid)
+{
+ if (NULL == disconnect_server_fn) {
+ if (get_entry_point(ENTRY_POINT_DISCONNECT_SERVER, (caddr_t *)(&disconnect_server_fn)) < 0) {
+ return;
+ }
+ }
+ /* It seems that we only call this from result.c when the ber_flush fails. */
+ (disconnect_server_fn)(conn, opconnid, opid, SLAPD_DISCONNECT_BER_FLUSH, 0);
+}
+
+
+/******************************************************************************
+*
+* slapi_compare_internal
+*
+* no plans to implement this, but placeholder incase we change our mind
+*
+*
+*******************************************************************************/
+
+
+Slapi_PBlock *
+slapi_compare_internal(char * dn,
+ char *attr,
+ char *value,
+ LDAPControl **controls)
+{
+ printf("slapi_compare_internal not yet implemented \n");
+ return(0);
+}
+
+int
+slapi_seq_callback( const char *ibase,
+ int type,
+ char *attrname,
+ char *val,
+ char **attrs,
+ int attrsonly,
+ void *callback_data,
+ LDAPControl **controls,
+ plugin_result_callback res_callback,
+ plugin_search_entry_callback srch_callback,
+ plugin_referral_entry_callback ref_callback)
+{
+ int r;
+ Slapi_PBlock pb;
+
+ if (ibase == NULL)
+ {
+ slapi_log_error(SLAPI_LOG_FATAL, NULL,
+ "slapi_seq_callback: NULL parameter\n");
+ return -1;
+ }
+
+ pblock_init(&pb);
+
+ slapi_seq_internal_set_pb(&pb, (char *)ibase, type, attrname, val, attrs, attrsonly, controls,
+ plugin_get_default_component_id(), 0);
+
+ r= seq_internal_callback_pb (&pb, callback_data, res_callback, srch_callback, ref_callback);
+ pblock_done(&pb);
+ return r;
+}
+
+
+
+/* pblock should contain the following data (can be set via call to slapi_seq_internal_set_pb):
+ SLAPI_SEARCH_TARGET set to search base
+ SAPI_SEQ_TYPE set to sequential access type (SLAPI_SEQ_FIRST, SLAPI_SEQ_NEXT, etc.
+ SLAPI_SEQ_ATTRNAME the next two fields define attribute value assertion
+ SLAPI_SEQ_VAL relative to which access is performed.
+ SLAPI_CONTROLS_ARG set to request controls if present
+ SLAPI_SEARCH_ATTRS set to the list of attributes to return
+ SLAPI_SEARCH_ATTRSONLY tells whether attribute values should be returned.
+ */
+
+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)
+{
+ if (pb == NULL)
+ return -1;
+
+ if (!allow_operation (pb))
+ {
+ send_ldap_result( pb, LDAP_UNWILLING_TO_PERFORM, NULL,
+ "This plugin is not configured to access operation target data", 0, NULL );
+ return 0;
+ }
+
+ return (seq_internal_callback_pb (pb, callback_data, res_callback, srch_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)
+{
+ Operation *op;
+ if (pb == NULL || base == NULL)
+ {
+ slapi_log_error(SLAPI_LOG_FATAL, NULL,
+ "slapi_search_internal_set_pb: NULL parameter\n");
+ return;
+ }
+
+ op= internal_operation_new(SLAPI_OPERATION_SEARCH,operation_flags);
+ slapi_pblock_set(pb, SLAPI_OPERATION, op);
+ slapi_pblock_set(pb, SLAPI_SEARCH_TARGET, (void*)base);
+ slapi_pblock_set(pb, SLAPI_SEARCH_SCOPE, &scope);
+ slapi_pblock_set(pb, SLAPI_SEARCH_STRFILTER, (void*)filter);
+ slapi_pblock_set(pb, SLAPI_CONTROLS_ARG, controls);
+ slapi_pblock_set(pb, SLAPI_SEARCH_ATTRS, attrs);
+ slapi_pblock_set(pb, SLAPI_SEARCH_ATTRSONLY, &attrsonly);
+ if (uniqueid)
+ {
+ slapi_pblock_set(pb, SLAPI_TARGET_UNIQUEID, (void*)uniqueid);
+ }
+ slapi_pblock_set(pb, SLAPI_PLUGIN_IDENTITY, (void*)plugin_identity);
+}
+
+void slapi_seq_internal_set_pb(Slapi_PBlock *pb, char *base, int type, char *attrname, char *val,
+ char **attrs, int attrsonly, LDAPControl **controls,
+ Slapi_ComponentId *plugin_identity, int operation_flags)
+{
+ Operation *op;
+ if (pb == NULL || base == NULL)
+ {
+ slapi_log_error(SLAPI_LOG_FATAL, NULL,
+ "slapi_seq_internal_set_pb: NULL parameter\n");
+ return;
+ }
+
+ op= internal_operation_new(SLAPI_OPERATION_SEARCH,operation_flags);
+ slapi_pblock_set(pb, SLAPI_OPERATION, op);
+ slapi_pblock_set(pb, SLAPI_SEARCH_TARGET, base);
+ slapi_pblock_set(pb, SLAPI_SEQ_TYPE, &type);
+ slapi_pblock_set(pb, SLAPI_SEQ_ATTRNAME, attrname);
+ slapi_pblock_set(pb, SLAPI_SEQ_VAL, val);
+ slapi_pblock_set(pb, SLAPI_SEARCH_ATTRS, attrs);
+ slapi_pblock_set(pb, SLAPI_SEARCH_ATTRSONLY, &attrsonly);
+ slapi_pblock_set(pb, SLAPI_CONTROLS_ARG, controls);
+ slapi_pblock_set(pb, SLAPI_PLUGIN_IDENTITY, plugin_identity);
+}
+
+static int seq_internal_callback_pb (Slapi_PBlock *pb, void *callback_data,
+ plugin_result_callback prc,
+ plugin_search_entry_callback psec,
+ plugin_referral_entry_callback prec)
+{
+ int rc;
+ LDAPControl **controls;
+ Operation *op;
+ struct callback_fn_ptrs callback_handler_data;
+ Slapi_Backend *be;
+ Slapi_DN sdn;
+ char *base;
+ char *attrname, *val;
+
+ slapi_pblock_get(pb, SLAPI_SEARCH_TARGET, &base);
+ slapi_pblock_get(pb, SLAPI_CONTROLS_ARG, &controls);
+
+ if (base == NULL) {
+ slapi_sdn_init_dn_byval(&sdn,"");
+ } else {
+ slapi_sdn_init_dn_byval(&sdn, base);
+ }
+ be = slapi_be_select(&sdn);
+
+ callback_handler_data.p_res_callback = prc;
+ callback_handler_data.p_srch_entry_callback = psec;
+ callback_handler_data.p_ref_entry_callback = prec;
+ callback_handler_data.callback_data = callback_data;
+
+ slapi_pblock_get(pb, SLAPI_OPERATION, &op);
+ op->o_handler_data = (void *)&callback_handler_data;
+ op->o_result_handler = internal_res_callback;
+ op->o_search_entry_handler = internal_srch_entry_callback;
+ op->o_search_referral_handler = internal_ref_entry_callback;
+
+ /* set target specification of the operation used to decide which plugins are called for the operation */
+ operation_set_target_spec (op, &sdn);
+
+
+ /* Normalize the attribute type and value */
+ slapi_pblock_get (pb, SLAPI_SEQ_ATTRNAME, &attrname);
+ slapi_pblock_get (pb, SLAPI_SEQ_VAL, &val);
+ attrname = slapi_attr_syntax_normalize(attrname);
+ val = ( NULL == val ) ? NULL : slapi_ch_strdup( val );
+
+ slapi_pblock_set(pb, SLAPI_BACKEND, be);
+ slapi_pblock_set(pb, SLAPI_PLUGIN, be->be_database);
+ slapi_pblock_set(pb, SLAPI_SEQ_ATTRNAME, attrname);
+ slapi_pblock_set(pb, SLAPI_SEQ_VAL, val);
+ slapi_pblock_set(pb, SLAPI_REQCONTROLS, controls);
+
+ /* set actions taken to process the operation */
+ set_config_params (pb);
+
+ /* set common parameters */
+ set_common_params (pb);
+
+ if (be->be_seq != NULL)
+ {
+ rc = (*be->be_seq)(pb);
+ }
+ else
+ {
+ send_ldap_result(pb, LDAP_UNWILLING_TO_PERFORM, NULL, "Function not implemented", 0, NULL);
+ rc = 0;
+ }
+
+ slapi_ch_free((void **) &attrname);
+ slapi_ch_free((void **) &val);
+
+ slapi_sdn_done(&sdn);
+
+ return rc;
+}
+
+int
+slapi_search_internal_callback(const char *ibase,
+ int scope,
+ const char *ifstr,
+ char **attrs,
+ int attrsonly,
+ void *callback_data,
+ LDAPControl **controls,
+ plugin_result_callback res_callback,
+ plugin_search_entry_callback srch_callback,
+ plugin_referral_entry_callback ref_callback)
+{
+ Slapi_PBlock pb;
+ int rc = 0;
+
+ pblock_init(&pb);
+
+ slapi_search_internal_set_pb (&pb, ibase, scope, ifstr, attrs, attrsonly,
+ controls, NULL, plugin_get_default_component_id(), 0);
+
+ rc = search_internal_callback_pb (&pb, callback_data, res_callback,
+ srch_callback, ref_callback);
+ pblock_done(&pb);
+ return (rc);
+}
+
+Slapi_PBlock *
+slapi_search_internal(const char *base,
+ int scope,
+ const char *filter,
+ LDAPControl **controls,
+ char **attrs,
+ int attrsonly)
+{
+
+ Slapi_PBlock *pb;
+
+ /* initialize pb */
+ pb = slapi_pblock_new();
+ if (pb)
+ {
+ slapi_search_internal_set_pb (pb, base, scope, filter, attrs, attrsonly, controls,
+ NULL, plugin_get_default_component_id(), 0);
+
+ search_internal_pb (pb);
+ }
+
+ return pb;
+}
+
+/* This function free searchs result or referral set on internal_ops in pblocks */
+void
+slapi_free_search_results_internal(Slapi_PBlock *pb)
+{
+ int i;
+
+ if(pb->pb_plugin_internal_search_op_entries != NULL)
+ {
+ for(i=0; pb->pb_plugin_internal_search_op_entries[i] != NULL; i++)
+ {
+ slapi_entry_free(pb->pb_plugin_internal_search_op_entries[i]);
+ }
+ slapi_ch_free((void**)&(pb->pb_plugin_internal_search_op_entries));
+ }
+
+ /* free v3 referrals made from result handler */
+ if(pb->pb_plugin_internal_search_op_referrals != NULL)
+ {
+ for(i=0; pb->pb_plugin_internal_search_op_referrals[i] != NULL; i++)
+ {
+ slapi_ch_free((void**)&(pb->pb_plugin_internal_search_op_referrals[i]));
+ }
+ slapi_ch_free((void**)&(pb->pb_plugin_internal_search_op_referrals));
+ }
+}
+
+/* this functions can be used for dn as well as uniqueid based operations */
+
+/* pblock should contain the following data (can be set via call to slapi_search_internal_set_pb):
+ For uniqueid based search:
+ SLAPI_TARGET_DN set to dn that allows to select right backend
+ SLAPI_TARGET_UNIQUEID set to the uniqueid of the entry we are looking for
+
+ For dn based search:
+ SLAPI_TARGET_DN set to search base
+ SLAPI_SEARCH_SCOPE set to search scope
+ SLAPI_SEARCH_STRFILTER set to search filter
+ SLAPI_CONTROLS_ARG set to request controls if present
+ SLAPI_SEARCH_ATTRS set to the list of attributes to return
+ SLAPI_SEARCH_ATTRSONLY tells whether attribute values should be returned.
+ */
+int slapi_search_internal_pb (Slapi_PBlock *pb)
+{
+ if (pb == NULL)
+ return -1;
+
+ if (!allow_operation (pb))
+ {
+ slapi_send_ldap_result( pb, LDAP_UNWILLING_TO_PERFORM, NULL,
+ "This plugin is not configured to access operation target data", 0, NULL );
+ return 0;
+ }
+
+ return search_internal_pb (pb);
+}
+
+/* pblock should contain the same data as for slapi_search_internal_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)
+{
+ if (pb == NULL)
+ return -1;
+
+ if (!allow_operation (pb))
+ {
+ send_ldap_result( pb, LDAP_UNWILLING_TO_PERFORM, NULL,
+ "This plugin is not configured to access operation target data", 0, NULL );
+ return 0;
+ }
+
+ return (search_internal_callback_pb (pb, callback_data, prc, psec, prec));
+}
+
+static int
+internal_plugin_search_entry_callback(Slapi_Entry *e, void *callback_data)
+{
+ Entry_Node *this_entry;
+
+ /* add this entry to the list of entries we are making */
+ this_entry = (Entry_Node *)slapi_ch_calloc(1,sizeof(Entry_Node));
+
+ if ((this_entry->data = slapi_entry_dup(e)) == NULL)
+ {
+ return(0);
+ }
+
+ this_entry->next = ((plugin_search_internal_data *) callback_data)->entry_list_head;
+
+ ((plugin_search_internal_data *) callback_data)->entry_list_head = this_entry;
+ ((plugin_search_internal_data *) callback_data)->num_entries++;
+
+ return(0);
+}
+
+static int
+internal_plugin_search_referral_callback(char *referral, void *callback_data)
+{
+ Referral_Node *this_referral;
+
+ /* add this to the list of referrals we are making */
+ this_referral = (Referral_Node *)slapi_ch_calloc(1,sizeof(Referral_Node));
+
+ if ((this_referral->data = slapi_ch_strdup(referral)) == NULL)
+ {
+ return(0);
+ }
+
+ this_referral->next = ((plugin_search_internal_data *) callback_data)->referral_list_head;
+
+ ((plugin_search_internal_data *) callback_data)->referral_list_head = this_referral;
+ ((plugin_search_internal_data *) callback_data)->num_referrals++;
+
+ return(0);
+}
+
+static void internal_plugin_result_callback(int rc, void *callback_data)
+{
+ /* put the result into pb_op_result */
+ ((plugin_search_internal_data *) callback_data)->rc = rc;
+}
+
+static int search_internal_pb (Slapi_PBlock *pb)
+{
+ plugin_search_internal_data psid;
+ Entry_Node *iterator, *tmp;
+ Referral_Node *ref_iterator, *ref_tmp;
+ int i;
+ int opresult = 0;
+ Slapi_Entry **pb_search_entries = NULL;
+ char **pb_search_referrals = NULL;
+ int rc;
+
+ PR_ASSERT (pb);
+
+ /* initialize psid */
+ psid.rc = -1;
+ psid.num_entries =0;
+ psid.num_referrals =0;
+ psid.entry_list_head = NULL;
+ psid.referral_list_head = NULL;
+
+ /* setup additional pb data */
+ slapi_pblock_set(pb, SLAPI_PLUGIN_INTOP_RESULT, &opresult);
+ slapi_pblock_set(pb, SLAPI_PLUGIN_INTOP_SEARCH_ENTRIES, pb_search_entries);
+ slapi_pblock_set(pb, SLAPI_PLUGIN_INTOP_SEARCH_REFERRALS, pb_search_referrals);
+
+ /* call internal search callback, define search_entry_callback, and result_callback such
+ that the results of the search are stuffed into pb */
+ rc = search_internal_callback_pb (pb, &psid, internal_plugin_result_callback,
+ internal_plugin_search_entry_callback,
+ internal_plugin_search_referral_callback);
+ opresult = psid.rc;
+
+ /* stuff search entry pointers from linked list to contiguous array for pblock */
+ pb_search_entries = (Slapi_Entry **)slapi_ch_calloc((psid.num_entries + 1), sizeof(Slapi_Entry *));
+
+ for(i=0,iterator = psid.entry_list_head; iterator !=NULL; iterator=iterator->next, i++)
+ {
+ pb_search_entries[i]= iterator->data;
+ }
+ pb_search_entries[i]=NULL;
+
+ /* free the linked list now that data has been put in the array */
+ iterator=psid.entry_list_head;
+ while(iterator != NULL)
+ {
+ /* free the data held in this node */
+ tmp = iterator;
+ iterator = iterator->next;
+
+ /* free the node */
+ if(tmp != NULL)
+ {
+ slapi_ch_free((void **) &tmp);
+ }
+ }
+ psid.entry_list_head = NULL;
+
+ /* stuff referrals list into an array if we got any to put into the pblock */
+ if(psid.num_referrals != 0)
+ {
+ pb_search_referrals = (char **)slapi_ch_calloc((psid.num_referrals + 1), sizeof(char *));
+
+ for(i=0, ref_iterator = psid.referral_list_head; ref_iterator !=NULL; ref_iterator=ref_iterator->next, i++)
+ {
+ pb_search_referrals[i]= ref_iterator->data;
+ }
+ pb_search_referrals[i]=NULL;
+
+ /* free the linked list now that data has been put in the array */
+ ref_iterator=psid.referral_list_head;
+ while(ref_iterator != NULL)
+ {
+
+ ref_tmp = ref_iterator;
+ ref_iterator = ref_iterator->next;
+
+ /* free the node */
+ if(ref_tmp != NULL)
+ {
+ slapi_ch_free((void **) &ref_tmp);
+ }
+ }
+ psid.referral_list_head = NULL;
+ }
+
+ /* set the result, the array of entries, and the array of referrals in pb */
+ slapi_pblock_set(pb, SLAPI_NENTRIES, &psid.num_entries);
+ slapi_pblock_set(pb, SLAPI_PLUGIN_INTOP_RESULT, &opresult);
+ slapi_pblock_set(pb, SLAPI_PLUGIN_INTOP_SEARCH_ENTRIES, pb_search_entries);
+ slapi_pblock_set(pb, SLAPI_PLUGIN_INTOP_SEARCH_REFERRALS, pb_search_referrals);
+
+ return 0;
+}
+
+static int search_internal_callback_pb (Slapi_PBlock *pb, void *callback_data,
+ plugin_result_callback prc,
+ plugin_search_entry_callback psec,
+ plugin_referral_entry_callback prec)
+{
+ LDAPControl **controls;
+ Operation *op;
+ struct slapi_filter *filter = NULL;
+ char *fstr = NULL;
+ struct callback_fn_ptrs callback_handler_data;
+ int scope;
+ char *ifstr;
+ int opresult;
+ int rc = 0;
+ char *original_base = 0;
+ char *new_base = 0;
+
+ PR_ASSERT (pb);
+
+ /* retrieve search parameters */
+ slapi_pblock_get(pb, SLAPI_SEARCH_SCOPE, &scope);
+ slapi_pblock_get(pb, SLAPI_SEARCH_STRFILTER, &ifstr);
+ slapi_pblock_get(pb, SLAPI_CONTROLS_ARG, &controls);
+
+ /* data validation */
+ if (ifstr == NULL || (scope != LDAP_SCOPE_BASE && scope != LDAP_SCOPE_ONELEVEL
+ && scope != LDAP_SCOPE_SUBTREE))
+ {
+ opresult = LDAP_PARAM_ERROR;
+ slapi_pblock_set(pb, SLAPI_PLUGIN_INTOP_RESULT, &opresult);
+ return -1;
+ }
+
+ callback_handler_data.p_res_callback = prc;
+ callback_handler_data.p_srch_entry_callback = psec;
+ callback_handler_data.p_ref_entry_callback = prec;
+ callback_handler_data.callback_data = callback_data;
+
+ slapi_pblock_get(pb, SLAPI_OPERATION, &op);
+ op->o_handler_data = (void *)&callback_handler_data;
+ op->o_result_handler = internal_res_callback;
+ op->o_search_entry_handler = internal_srch_entry_callback;
+ op->o_search_referral_handler = internal_ref_entry_callback;
+
+ filter = slapi_str2filter(ifstr ? (fstr = slapi_ch_strdup(ifstr)) : "");
+ if(scope == LDAP_SCOPE_BASE) filter->f_flags |= (SLAPI_FILTER_LDAPSUBENTRY | SLAPI_FILTER_TOMBSTONE);
+ if (NULL == filter)
+ {
+ send_ldap_result(pb, LDAP_FILTER_ERROR, NULL, NULL, 0, NULL);
+ rc = -1;
+ goto done;
+ }
+ filter_normalize(filter);
+
+ slapi_pblock_set(pb, SLAPI_SEARCH_FILTER, filter);
+ slapi_pblock_set(pb, SLAPI_REQCONTROLS, controls);
+
+ /* set actions taken to process the operation */
+ set_config_params (pb);
+
+ /* set parameters common for all internal operations */
+ set_common_params (pb);
+ {
+ int timelimit = -1;
+ int sizelimit = -1;
+ int deref = LDAP_DEREF_ALWAYS;
+ slapi_pblock_set(pb, SLAPI_SEARCH_DEREF, &deref);
+ slapi_pblock_set(pb, SLAPI_SEARCH_TIMELIMIT, &timelimit);
+ slapi_pblock_set(pb, SLAPI_SEARCH_SIZELIMIT, &sizelimit);
+ }
+
+ /* plugins which play with the search may
+ * change the search params may allocate
+ * memory so we need to keep track of
+ * changed base search strings
+ */
+ slapi_pblock_get(pb, SLAPI_SEARCH_TARGET, &original_base);
+
+ op_shared_search (pb, 1);
+
+ slapi_pblock_get(pb, SLAPI_SEARCH_TARGET, &new_base);
+ slapi_pblock_get(pb, SLAPI_SEARCH_FILTER, &filter);
+
+done:
+ slapi_ch_free((void **) & fstr);
+ if (filter != NULL)
+ {
+ slapi_filter_free(filter, 1 /* recurse */);
+ }
+
+ if(original_base != new_base)
+ slapi_ch_free((void**)new_base);
+
+ return(rc);
+}
+
+/* allow/disallow operation based of the plugin configuration */
+PRBool allow_operation (Slapi_PBlock *pb)
+{
+ char *dn = NULL;
+ struct slapdplugin *plugin = NULL;
+ Slapi_DN sdn;
+ PRBool allow;
+ struct slapi_componentid * cid=NULL;
+
+ PR_ASSERT (pb);
+
+ /* make sure that users of new API provide plugin identity */
+ slapi_pblock_get (pb, SLAPI_PLUGIN_IDENTITY, &cid);
+ if (cid == NULL)
+ {
+ slapi_log_error( SLAPI_LOG_FATAL, NULL, "allow_operation: component identity is NULL\n");
+ return PR_FALSE;
+ }
+ plugin=(struct slapdplugin *) cid->sci_plugin;
+ if (plugin == NULL)
+ {
+ slapi_log_error( SLAPI_LOG_FATAL, NULL, "allow_operation: plugin identity is NULL\n");
+ return PR_FALSE;
+ }
+
+ slapi_pblock_get (pb, SLAPI_TARGET_DN, &dn);
+ if (dn == NULL) {
+ slapi_sdn_init_dn_byval(&sdn,"");
+ } else {
+ slapi_sdn_init_dn_byval(&sdn, dn);
+ }
+
+ allow = plugin_allow_internal_op (&sdn, plugin);
+
+ slapi_sdn_done (&sdn);
+
+ return allow;
+}
+
+/* set operation configuration based on the plugin configuration */
+void set_config_params (Slapi_PBlock *pb)
+{
+ Slapi_Operation *operation;
+ struct slapdplugin *plugin = NULL;
+ char *dn;
+ struct slapi_componentid * cid=NULL;
+
+ slapi_pblock_get (pb, SLAPI_PLUGIN_IDENTITY, &cid);
+ if (cid)
+ plugin=(struct slapdplugin *) cid->sci_plugin;
+
+ /* set actions */
+ slapi_pblock_get( pb, SLAPI_OPERATION, &operation);
+ operation_set_flag(operation,plugin_build_operation_action_bitmap(operation->o_flags, plugin));
+
+ /* Check if we have a flag to keep the operation secret */
+ if (operation_is_flag_set(operation, OP_FLAG_ACTION_NOLOG)) {
+ /* Clear the LOG_AUDIT and LOG_CHANGES flag */
+ operation_clear_flag(operation, OP_FLAG_ACTION_LOG_AUDIT | OP_FLAG_ACTION_LOG_CHANGES);
+ }
+
+ /* set name to be used for creator's and modifiers attributes */
+ dn = plugin_get_dn (plugin);
+ if (dn)
+ slapi_sdn_init_dn_passin(&operation->o_sdn, dn);
+}
+
+/* set parameters common for all internal operations */
+void set_common_params (Slapi_PBlock *pb)
+{
+ int isroot = 1;
+ LDAPControl **controls;
+
+ slapi_pblock_get(pb, SLAPI_CONTROLS_ARG, &controls);
+
+ if (NULL != controls)
+ {
+ int managedsait = slapi_control_present(controls,
+ LDAP_CONTROL_MANAGEDSAIT, NULL, NULL);
+ int pwpolicy_ctrl = slapi_control_present(controls,
+ LDAP_X_CONTROL_PWPOLICY_REQUEST, NULL, NULL);
+
+ slapi_pblock_set(pb, SLAPI_MANAGEDSAIT, &managedsait);
+ slapi_pblock_set(pb, SLAPI_PWPOLICY, &pwpolicy_ctrl);
+ }
+
+ slapi_pblock_set(pb, SLAPI_REQUESTOR_ISROOT, &isroot);
+}
+
+
+/*
+ * Given a DN, find an entry by doing an internal search. An LDAP error
+ * code is returned.
+ */
+int
+slapi_search_internal_get_entry( Slapi_DN *dn, char ** attrs, Slapi_Entry **ret_entry , void * component_identity)
+{
+ Slapi_Entry **entries = NULL;
+ Slapi_PBlock *int_search_pb = NULL;
+ int rc = 0;
+
+ *ret_entry = NULL;
+ int_search_pb = slapi_pblock_new ();
+ slapi_search_internal_set_pb ( int_search_pb, slapi_sdn_get_dn(dn), LDAP_SCOPE_BASE, "(|(objectclass=*)(objectclass=ldapsubentry))",
+ attrs ,
+ 0 /* attrsonly */, NULL /* controls */,
+ NULL /* uniqueid */,
+ component_identity, 0 /* actions */ );
+ slapi_search_internal_pb ( int_search_pb );
+ slapi_pblock_get( int_search_pb, SLAPI_PLUGIN_INTOP_RESULT, &rc );
+ if ( LDAP_SUCCESS == rc ) {
+ slapi_pblock_get( int_search_pb, SLAPI_PLUGIN_INTOP_SEARCH_ENTRIES, &entries );
+ if ( NULL != entries && NULL != entries[ 0 ]) {
+ Slapi_Entry *temp_entry = NULL;
+ temp_entry = entries[ 0 ];
+ *ret_entry = slapi_entry_dup(temp_entry);
+ } else {
+ /* No entry there */
+ rc = LDAP_NO_SUCH_OBJECT;
+ }
+ }
+ slapi_free_search_results_internal(int_search_pb);
+ slapi_pblock_destroy(int_search_pb);
+ int_search_pb = NULL;
+ return rc;
+}
diff --git a/ldap/servers/slapd/plugin_mr.c b/ldap/servers/slapd/plugin_mr.c
new file mode 100644
index 00000000..2579ff64
--- /dev/null
+++ b/ldap/servers/slapd/plugin_mr.c
@@ -0,0 +1,181 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+
+/*
+ * plugin_mr.c - routines for calling matching rule plugins
+ */
+
+#include "slap.h"
+
+static oid_item_t* global_mr_oids = NULL;
+static PRLock* global_mr_oids_lock = NULL;
+
+static void
+init_global_mr_lock()
+{
+ if(global_mr_oids_lock==NULL)
+ {
+ global_mr_oids_lock = PR_NewLock();
+ }
+}
+
+struct slapdplugin *
+slapi_get_global_mr_plugins()
+{
+ return get_plugin_list(PLUGIN_LIST_MATCHINGRULE);
+}
+
+static struct slapdplugin*
+plugin_mr_find (char* oid)
+{
+ oid_item_t* i;
+ init_global_mr_lock();
+ PR_Lock (global_mr_oids_lock);
+ i = global_mr_oids;
+ PR_Unlock (global_mr_oids_lock);
+ for (; i != NULL; i = i->oi_next)
+ {
+ if (!strcasecmp (oid, i->oi_oid))
+ {
+ LDAPDebug (LDAP_DEBUG_FILTER, "plugin_mr_find(%s) != NULL\n", oid, 0, 0);
+ return i->oi_plugin;
+ }
+ }
+ LDAPDebug (LDAP_DEBUG_FILTER, "plugin_mr_find(%s) == NULL\n", oid, 0, 0);
+ return NULL;
+}
+
+static void
+plugin_mr_bind (char* oid, struct slapdplugin* plugin)
+{
+ oid_item_t* i = (oid_item_t*) slapi_ch_malloc (sizeof (oid_item_t));
+ LDAPDebug (LDAP_DEBUG_FILTER, "=> plugin_mr_bind(%s)\n", oid, 0, 0);
+ init_global_mr_lock();
+ i->oi_oid = slapi_ch_strdup (oid);
+ i->oi_plugin = plugin;
+ PR_Lock (global_mr_oids_lock);
+ i->oi_next = global_mr_oids;
+ global_mr_oids = i;
+ PR_Unlock (global_mr_oids_lock);
+ LDAPDebug (LDAP_DEBUG_FILTER, "<= plugin_mr_bind\n", 0, 0, 0);
+}
+
+int /* an LDAP error code, hopefully LDAP_SUCCESS */
+slapi_mr_indexer_create (Slapi_PBlock* opb)
+{
+ int rc;
+ char* oid;
+ if (!(rc = slapi_pblock_get (opb, SLAPI_PLUGIN_MR_OID, &oid)))
+ {
+ IFP createFn = NULL;
+ struct slapdplugin* mrp = plugin_mr_find (oid);
+ if (mrp != NULL)
+ {
+ if (!(rc = slapi_pblock_set (opb, SLAPI_PLUGIN, mrp)) &&
+ !(rc = slapi_pblock_get (opb, SLAPI_PLUGIN_MR_INDEXER_CREATE_FN, &createFn)) &&
+ createFn != NULL)
+ {
+ rc = createFn (opb);
+ }
+ }
+ else
+ {
+ /* call each plugin, until one is able to handle this request. */
+ rc = LDAP_UNAVAILABLE_CRITICAL_EXTENSION;
+ for (mrp = get_plugin_list(PLUGIN_LIST_MATCHINGRULE); mrp != NULL; mrp = mrp->plg_next)
+ {
+ IFP indexFn = NULL;
+ Slapi_PBlock pb;
+ memcpy (&pb, opb, sizeof(Slapi_PBlock));
+ if (!(rc = slapi_pblock_set (&pb, SLAPI_PLUGIN, mrp)) &&
+ !(rc = slapi_pblock_get (&pb, SLAPI_PLUGIN_MR_INDEXER_CREATE_FN, &createFn)) &&
+ createFn != NULL &&
+ !(rc = createFn (&pb)) &&
+ !(rc = slapi_pblock_get (&pb, SLAPI_PLUGIN_MR_INDEX_FN, &indexFn)) &&
+ indexFn != NULL)
+ {
+ /* Success: this plugin can handle it. */
+ memcpy (opb, &pb, sizeof(Slapi_PBlock));
+ plugin_mr_bind (oid, mrp); /* for future reference */
+ break;
+ }
+ }
+ }
+ }
+ return rc;
+}
+
+static int
+attempt_mr_filter_create (mr_filter_t* f, struct slapdplugin* mrp, Slapi_PBlock* pb)
+{
+ int rc;
+ IFP mrf_create = NULL;
+ f->mrf_match = NULL;
+ pblock_init (pb);
+ if (!(rc = slapi_pblock_set (pb, SLAPI_PLUGIN, mrp)) &&
+ !(rc = slapi_pblock_get (pb, SLAPI_PLUGIN_MR_FILTER_CREATE_FN, &mrf_create)) &&
+ mrf_create != NULL &&
+ !(rc = slapi_pblock_set (pb, SLAPI_PLUGIN_MR_OID, f->mrf_oid)) &&
+ !(rc = slapi_pblock_set (pb, SLAPI_PLUGIN_MR_TYPE, f->mrf_type)) &&
+ !(rc = slapi_pblock_set (pb, SLAPI_PLUGIN_MR_VALUE, &(f->mrf_value))) &&
+ !(rc = mrf_create (pb)) &&
+ !(rc = slapi_pblock_get (pb, SLAPI_PLUGIN_MR_FILTER_MATCH_FN, &(f->mrf_match)))) {
+ if (f->mrf_match == NULL)
+ {
+ rc = LDAP_UNAVAILABLE_CRITICAL_EXTENSION;
+ }
+ }
+ return rc;
+}
+
+int /* an LDAP error code, hopefully LDAP_SUCCESS */
+plugin_mr_filter_create (mr_filter_t* f)
+{
+ int rc = LDAP_UNAVAILABLE_CRITICAL_EXTENSION;
+ struct slapdplugin* mrp = plugin_mr_find (f->mrf_oid);
+ Slapi_PBlock pb;
+
+ if (mrp != NULL)
+ {
+ rc = attempt_mr_filter_create (f, mrp, &pb);
+ }
+ else
+ {
+ /* call each plugin, until one is able to handle this request. */
+ for (mrp = get_plugin_list(PLUGIN_LIST_MATCHINGRULE); mrp != NULL; mrp = mrp->plg_next)
+ {
+ if (!(rc = attempt_mr_filter_create (f, mrp, &pb)))
+ {
+ plugin_mr_bind (f->mrf_oid, mrp); /* for future reference */
+ break;
+ }
+ }
+ }
+ if (!rc)
+ {
+ /* This plugin has created the desired filter. */
+ f->mrf_plugin = mrp;
+ slapi_pblock_get (&pb, SLAPI_PLUGIN_MR_FILTER_INDEX_FN, &(f->mrf_index));
+ slapi_pblock_get (&pb, SLAPI_PLUGIN_MR_FILTER_REUSABLE, &(f->mrf_reusable));
+ slapi_pblock_get (&pb, SLAPI_PLUGIN_MR_FILTER_RESET_FN, &(f->mrf_reset));
+ slapi_pblock_get (&pb, SLAPI_PLUGIN_OBJECT, &(f->mrf_object));
+ slapi_pblock_get (&pb, SLAPI_PLUGIN_DESTROY_FN, &(f->mrf_destroy));
+ }
+ return rc;
+}
+
+int /* an LDAP error code, hopefully LDAP_SUCCESS */
+slapi_mr_filter_index (Slapi_Filter* f, Slapi_PBlock* pb)
+{
+ int rc = LDAP_UNAVAILABLE_CRITICAL_EXTENSION;
+ if (f->f_choice == LDAP_FILTER_EXTENDED && f->f_mr.mrf_index != NULL &&
+ !(rc = slapi_pblock_set (pb, SLAPI_PLUGIN_OBJECT, f->f_mr.mrf_object)))
+ {
+ rc = f->f_mr.mrf_index (pb);
+ }
+ return rc;
+}
+
diff --git a/ldap/servers/slapd/plugin_role.c b/ldap/servers/slapd/plugin_role.c
new file mode 100644
index 00000000..362b08cc
--- /dev/null
+++ b/ldap/servers/slapd/plugin_role.c
@@ -0,0 +1,30 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+
+/*
+ * plugin_role.c - routines for calling roles plugins
+ */
+
+#include "slap.h"
+
+static roles_check_fn_type roles_check_exported = NULL;
+
+int slapi_role_check(Slapi_Entry *entry_to_check, Slapi_DN *role_dn, int *present)
+{
+ int rc = 0;
+
+ if ( roles_check_exported != NULL )
+ {
+ rc = (roles_check_exported)(entry_to_check, role_dn, present);
+ }
+
+ return rc;
+}
+
+void slapi_register_role_check(roles_check_fn_type check_fn)
+{
+ roles_check_exported = check_fn;
+}
diff --git a/ldap/servers/slapd/plugin_syntax.c b/ldap/servers/slapd/plugin_syntax.c
new file mode 100644
index 00000000..caace965
--- /dev/null
+++ b/ldap/servers/slapd/plugin_syntax.c
@@ -0,0 +1,360 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+
+/*
+ * plugin_syntax.c - routines for calling syntax plugins
+ */
+
+
+#include "slap.h"
+
+
+struct slapdplugin *
+plugin_syntax_find( const char *nameoroid )
+{
+ struct slapdplugin *pi;
+
+ /* LDAPDebug( LDAP_DEBUG_FILTER, "=> plugin_syntax_find (%s)\n", nameoroid, 0, 0 ); */
+ for ( pi = get_plugin_list(PLUGIN_LIST_SYNTAX); pi != NULL; pi = pi->plg_next ) {
+ if ( charray_inlist( pi->plg_syntax_names, (char *)nameoroid ) ) {
+ break;
+ }
+ }
+ /* LDAPDebug( LDAP_DEBUG_FILTER, "<= plugin_syntax_find %d\n", pi, 0, 0 ); */
+ return ( pi );
+}
+
+
+/*
+ * Enumerate all the syntax plugins, calling (*sef)() for each one.
+ */
+void
+plugin_syntax_enumerate( SyntaxEnumFunc sef, void *arg )
+{
+ struct slapdplugin *pi;
+
+ for ( pi = get_plugin_list(PLUGIN_LIST_SYNTAX); pi != NULL;
+ pi = pi->plg_next ) {
+ (*sef)( pi->plg_syntax_names, &pi->plg_desc, arg );
+ }
+}
+
+
+struct slapdplugin *
+slapi_get_global_syntax_plugins()
+{
+ return get_plugin_list(PLUGIN_LIST_SYNTAX);
+}
+
+char *
+plugin_syntax2oid( struct slapdplugin *pi )
+{
+ return( pi->plg_syntax_oid );
+}
+
+int
+plugin_call_syntax_get_compare_fn(
+ void *vpi,
+ value_compare_fn_type *compare_fn
+)
+{
+ struct slapdplugin *pi = vpi;
+ *compare_fn = NULL;
+
+ LDAPDebug( LDAP_DEBUG_TRACE,
+ "=> plugin_call_syntax_get_compare_fn\n",0,0, 0 );
+
+ if ( pi == NULL ) {
+ LDAPDebug( LDAP_DEBUG_TRACE,
+ "<= plugin_syntax no plugin for attribute type\n",
+ 0, 0, 0 );
+ return( LDAP_PROTOCOL_ERROR ); /* syntax unkonwn */
+ }
+
+ if ( (pi->plg_syntax_flags & SLAPI_PLUGIN_SYNTAX_FLAG_ORDERING) == 0 ) {
+ return( LDAP_PROTOCOL_ERROR );
+ }
+
+ if (pi->plg_syntax_filter_ava == NULL) {
+ LDAPDebug( LDAP_DEBUG_ANY, "<= plugin_call_syntax_get_compare_fn: "
+ "no filter_ava found for attribute type\n", 0, 0, 0 );
+ return( LDAP_PROTOCOL_ERROR );
+ }
+
+ if (pi->plg_syntax_compare == NULL) {
+ return( LDAP_PROTOCOL_ERROR );
+ }
+
+ *compare_fn = (value_compare_fn_type) pi->plg_syntax_compare;
+
+ LDAPDebug( LDAP_DEBUG_TRACE,
+ "<= plugin_call_syntax_get_compare_fn \n", 0, 0, 0 );
+ return( 0 );
+}
+
+int
+plugin_call_syntax_filter_ava(
+ const Slapi_Attr *a,
+ int ftype,
+ struct ava *ava
+)
+{
+ return(plugin_call_syntax_filter_ava_sv(a,ftype,ava,NULL,0 /*Present*/));
+}
+
+int
+plugin_call_syntax_filter_ava_sv(
+ const Slapi_Attr *a,
+ int ftype,
+ struct ava *ava,
+ Slapi_Value **retVal,
+ int useDeletedValues
+)
+{
+ int rc;
+ Slapi_PBlock pipb;
+
+ LDAPDebug( LDAP_DEBUG_FILTER,
+ "=> plugin_call_syntax_filter_ava %s=%s\n", ava->ava_type,
+ ava->ava_value.bv_val, 0 );
+
+ if ( a->a_plugin == NULL ) {
+ LDAPDebug( LDAP_DEBUG_FILTER,
+ "<= plugin_syntax no plugin for attr (%s)\n",
+ a->a_type, 0, 0 );
+ return( LDAP_PROTOCOL_ERROR ); /* syntax unkonwn */
+ }
+
+ pblock_init( &pipb );
+ slapi_pblock_set( &pipb, SLAPI_PLUGIN, (void *) a->a_plugin );
+
+ rc = -1; /* does not match by default */
+ switch ( ftype ) {
+ case LDAP_FILTER_GE:
+ case LDAP_FILTER_LE:
+ if ( (a->a_plugin->plg_syntax_flags &
+ SLAPI_PLUGIN_SYNTAX_FLAG_ORDERING) == 0 ) {
+ rc = LDAP_PROTOCOL_ERROR;
+ break;
+ }
+ /* FALL */
+ case LDAP_FILTER_EQUALITY:
+ case LDAP_FILTER_APPROX:
+ if ( a->a_plugin->plg_syntax_filter_ava != NULL )
+ {
+ /* JCM - Maybe the plugin should use the attr value iterator too... */
+ Slapi_Value **va;
+ if(useDeletedValues)
+ va= valueset_get_valuearray(&a->a_deleted_values);
+ else
+ va= valueset_get_valuearray(&a->a_present_values);
+ if(va!=NULL)
+ {
+ rc = a->a_plugin->plg_syntax_filter_ava( &pipb,
+ &ava->ava_value,
+ va,
+ ftype, retVal );
+ }
+ }
+ break;
+ default:
+ LDAPDebug( LDAP_DEBUG_ANY, "plugin_call_syntax_filter: "
+ "unknown filter type %d\n", ftype, 0, 0 );
+ rc = LDAP_PROTOCOL_ERROR;
+ break;
+ }
+
+ LDAPDebug( LDAP_DEBUG_FILTER,
+ "<= plugin_call_syntax_filter_ava %d\n", rc, 0, 0 );
+ return( rc );
+}
+
+int
+plugin_call_syntax_filter_sub(
+ Slapi_Attr *a,
+ struct subfilt *fsub
+)
+{
+ return(plugin_call_syntax_filter_sub_sv(a,fsub));
+}
+
+int
+plugin_call_syntax_filter_sub_sv(
+ Slapi_Attr *a,
+ struct subfilt *fsub
+)
+{
+ Slapi_PBlock pipb;
+ int rc;
+
+ LDAPDebug( LDAP_DEBUG_FILTER,
+ "=> plugin_call_syntax_filter_sub\n", 0, 0, 0 );
+
+ if ( a->a_plugin == NULL ) {
+ LDAPDebug( LDAP_DEBUG_FILTER,
+ "<= plugin_call_syntax_filter no plugin\n", 0, 0, 0 );
+ return( -1 ); /* syntax unkonwn - does not match */
+ }
+
+ if ( a->a_plugin->plg_syntax_filter_sub != NULL )
+ {
+ Slapi_Value **va= valueset_get_valuearray(&a->a_present_values);
+ pblock_init( &pipb );
+ slapi_pblock_set( &pipb, SLAPI_PLUGIN, (void *) a->a_plugin );
+ rc = a->a_plugin->plg_syntax_filter_sub( &pipb,
+ fsub->sf_initial, fsub->sf_any, fsub->sf_final, va);
+ } else {
+ rc = -1;
+ }
+
+ LDAPDebug( LDAP_DEBUG_FILTER, "<= plugin_call_syntax_filter_sub_sv %d\n",
+ rc, 0, 0 );
+ return( rc );
+}
+
+SLAPI_DEPRECATED int
+slapi_call_syntax_values2keys( /* JCM SLOW FUNCTION */
+ void *vpi,
+ struct berval **vals,
+ struct berval ***ivals,
+ int ftype
+)
+{
+ int rc;
+ Slapi_Value **svin= NULL;
+ Slapi_Value **svout= NULL;
+ valuearray_init_bervalarray(vals,&svin); /* JCM SLOW FUNCTION */
+ rc= slapi_call_syntax_values2keys_sv(vpi,svin,&svout,ftype);
+ valuearray_get_bervalarray(svout,ivals); /* JCM SLOW FUNCTION */
+ valuearray_free(&svout);
+ valuearray_free(&svin);
+ return rc;
+}
+
+int
+slapi_call_syntax_values2keys_sv(
+ void *vpi,
+ Slapi_Value **vals,
+ Slapi_Value ***ivals,
+ int ftype
+)
+{
+ int rc;
+ Slapi_PBlock pipb;
+ struct slapdplugin *pi = vpi;
+
+ LDAPDebug( LDAP_DEBUG_FILTER, "=> slapi_call_syntax_values2keys\n",
+ 0, 0, 0 );
+
+ pblock_init( &pipb );
+ slapi_pblock_set( &pipb, SLAPI_PLUGIN, vpi );
+
+ *ivals = NULL;
+ rc = -1; /* means no values2keys function */
+ if ( pi != NULL && pi->plg_syntax_values2keys != NULL ) {
+ rc = pi->plg_syntax_values2keys( &pipb, vals, ivals, ftype );
+ }
+
+ LDAPDebug( LDAP_DEBUG_FILTER,
+ "<= slapi_call_syntax_values2keys %d\n", rc, 0, 0 );
+ return( rc );
+}
+
+SLAPI_DEPRECATED int
+slapi_call_syntax_assertion2keys_ava( /* JCM SLOW FUNCTION */
+ void *vpi,
+ struct berval *val,
+ struct berval ***ivals,
+ int ftype
+)
+{
+ int rc;
+ Slapi_Value svin;
+ Slapi_Value **svout= NULL;
+ slapi_value_init_berval(&svin, val);
+ rc= slapi_call_syntax_assertion2keys_ava_sv(vpi,&svin,&svout,ftype);
+ valuearray_get_bervalarray(svout,ivals); /* JCM SLOW FUNCTION */
+ valuearray_free(&svout);
+ value_done(&svin);
+ return rc;
+}
+
+SLAPI_DEPRECATED int
+slapi_call_syntax_assertion2keys_ava_sv(
+ void *vpi,
+ Slapi_Value *val,
+ Slapi_Value ***ivals,
+ int ftype
+)
+{
+ int rc;
+ Slapi_PBlock pipb;
+ struct slapdplugin *pi = vpi;
+
+ LDAPDebug( LDAP_DEBUG_FILTER,
+ "=> slapi_call_syntax_assertion2keys_ava\n", 0, 0, 0 );
+
+ pblock_init( &pipb );
+ slapi_pblock_set( &pipb, SLAPI_PLUGIN, vpi );
+
+ rc = -1; /* means no assertion2keys function */
+ if ( pi->plg_syntax_assertion2keys_ava != NULL ) {
+ rc = pi->plg_syntax_assertion2keys_ava( &pipb, val, ivals, ftype );
+ }
+
+ LDAPDebug( LDAP_DEBUG_FILTER,
+ "<= slapi_call_syntax_assertion2keys_ava %d\n", rc, 0, 0 );
+ return( rc );
+}
+
+SLAPI_DEPRECATED int
+slapi_call_syntax_assertion2keys_sub( /* JCM SLOW FUNCTION */
+ void *vpi,
+ char *initial,
+ char **any,
+ char *final,
+ struct berval ***ivals
+)
+{
+ int rc;
+ Slapi_Value **svout= NULL;
+ rc= slapi_call_syntax_assertion2keys_sub_sv(vpi,initial,any,final,&svout);
+ valuearray_get_bervalarray(svout,ivals); /* JCM SLOW FUNCTION */
+ valuearray_free(&svout);
+ return rc;
+}
+
+int
+slapi_call_syntax_assertion2keys_sub_sv(
+ void *vpi,
+ char *initial,
+ char **any,
+ char *final,
+ Slapi_Value ***ivals
+)
+{
+ int rc;
+ Slapi_PBlock pipb;
+ struct slapdplugin *pi = vpi;
+
+ LDAPDebug( LDAP_DEBUG_FILTER,
+ "=> slapi_call_syntax_assertion2keys_sub\n", 0, 0, 0 );
+
+ pblock_init( &pipb );
+ slapi_pblock_set( &pipb, SLAPI_PLUGIN, vpi );
+
+ rc = -1; /* means no assertion2keys function */
+ *ivals = NULL;
+ if ( pi->plg_syntax_assertion2keys_sub != NULL ) {
+ rc = pi->plg_syntax_assertion2keys_sub( &pipb, initial, any,
+ final, ivals );
+ }
+
+ LDAPDebug( LDAP_DEBUG_FILTER,
+ "<= slapi_call_syntax_assertion2keys_sub %d\n", rc, 0, 0 );
+ return( rc );
+}
+
diff --git a/ldap/servers/slapd/poll_using_select.c b/ldap/servers/slapd/poll_using_select.c
new file mode 100644
index 00000000..5c25a57b
--- /dev/null
+++ b/ldap/servers/slapd/poll_using_select.c
@@ -0,0 +1,122 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+/*
+ * poll_using_select.c
+ *
+ * poll_using_select() is an implementation of poll using select.
+ * I borrowed this code from the client source, ns/nspr/src/mdunix.c
+ * from the Nav404_OEM_BRANCH
+ *
+ * The reason we need this is on Linux 2.0 (and possibly other platforms)
+ * there is no poll. ldapserver/ldap/servers/slapd/daemon.c calls poll
+ *
+ * In Linux 2.1 there will be a native poll, so this will go away for Linux
+ * but other platforms may need it.
+ *
+ * 1-30-98
+ * sspitzer
+ */
+#include "poll_using_select.h"
+
+int
+poll_using_select(struct my_pollfd *filed, int nfds, int timeout)
+{
+ int width;
+ fd_set rd;
+ fd_set wr;
+ fd_set ex;
+ struct timeval tv;
+ int a, b, retval;
+
+#ifdef DEBUG_POLL_AS_SELECT
+ LDAPDebug(LDAP_DEBUG_ANY, "poll convert nfds=%d, timeout=%d\n", nfds, timeout,0);
+#endif /* DEBUG_POLL_AS_SELECT */
+
+ FD_ZERO( &rd );
+ FD_ZERO( &wr );
+ FD_ZERO( &ex );
+
+ /*
+ * If the fd member of all pollfd structures is less than 0,
+ * the poll() function will return 0 and have no other results.
+ */
+ for(a=0, b=0; a<nfds; a++){
+ if( filed[a].fd >= 0 ) b++;
+ }
+ if( b == 0 ){ return 0; }
+
+ for( width=0,a=0; a<nfds; a++ ){
+ short temp_events;
+
+#ifdef DEBUG_POLL_AS_SELECT
+ if (filed[a].events != 0) {
+ LDAPDebug(LDAP_DEBUG_ANY, "poll events = %d for fd=%d\n", filed[a].events, filed[a].fd,0);
+ }
+#endif /* DEBUG_POLL_AS_SELECT */
+
+ temp_events = filed[a].events;
+
+ filed[a].revents = 0;
+
+ if( temp_events & POLLIN ) FD_SET( filed[a].fd, &rd );
+ if( temp_events & POLLOUT ) FD_SET( filed[a].fd, &wr );
+ if( temp_events & POLLPRI ) FD_SET( filed[a].fd, &ex );
+
+ temp_events &= ~(POLLIN|POLLOUT|POLLPRI);
+
+#ifdef DEBUG_POLL_AS_SELECT
+ if( temp_events != 0 ){
+ LDAPDebug(LDAP_DEBUG_ANY, "Unhandled poll event type=0x%x on FD(%d)\n",filed[a].events,filed[a].fd,0);
+ }
+#endif /* DEBUG_POLL_AS_SELECT */
+
+ width = width>(filed[a].fd+1)?width:(filed[a].fd+1);
+ }
+
+ if( timeout != -1 ){
+ tv.tv_sec = timeout/MSECONDS;
+ tv.tv_usec= timeout%MSECONDS;
+
+ retval = select ( width, &rd, &wr, &ex, &tv );
+ }
+ else
+ {
+ retval = select ( width, &rd, &wr, &ex, 0 );
+ }
+
+ if( retval <= 0 ) return( retval );
+
+#ifdef DEBUG_POLL_AS_SELECT
+ LDAPDebug(LDAP_DEBUG_ANY, "For\n",0,0,0);
+#endif /* DEBUG_POLL_AS_SELECT */
+
+ for( b=0; b<nfds;b++){
+
+ if( filed[b].fd >= 0 ){
+ a = filed[b].fd;
+
+ if( FD_ISSET( a, &rd )) filed[b].revents |= POLLIN;
+ if( FD_ISSET( a, &wr )) filed[b].revents |= POLLOUT;
+ if( FD_ISSET( a, &ex )) filed[b].revents |= POLLPRI;
+
+#ifdef DEBUG_POLL_AS_SELECT
+ if ((filed[b].events != 0) || (filed[b].revents != 0)) {
+ LDAPDebug(LDAP_DEBUG_ANY, " file des=%d, events=0x%x, revents=0x%x\n", filed[b].fd, filed[b].events ,filed[b].revents);
+ }
+#endif /* DEBUG_POLL_AS_SELECT */
+ }
+ }
+
+ for( a=0, retval=0 ; a<nfds; a++ ){
+ if( filed[a].revents ) retval++;
+ }
+
+#ifdef DEBUG_POLL_AS_SELECT
+ LDAPDebug(LDAP_DEBUG_ANY, "poll returns %d (converted poll)\n",retval,0,0);
+#endif /* DEBUG_POLL_AS_SELECT */
+
+ return( retval );
+}
diff --git a/ldap/servers/slapd/poll_using_select.h b/ldap/servers/slapd/poll_using_select.h
new file mode 100644
index 00000000..de117729
--- /dev/null
+++ b/ldap/servers/slapd/poll_using_select.h
@@ -0,0 +1,43 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+#ifndef _POLL_USING_SELECT_H
+#define _POLL_USING_SELECT_H
+
+#include <sys/time.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include "slap.h"
+
+/* uncomment for debugging info
+#define DEBUG_POLL_AS_SELECT
+*/
+#define MSECONDS 1000
+
+struct my_pollfd {
+ int fd;
+ short events;
+ short revents;
+};
+
+/* poll events */
+
+#define POLLIN 0x0001 /* fd is readable */
+#define POLLPRI 0x0002 /* high priority info at fd */
+#define POLLOUT 0x0004 /* fd is writeable (won't block) */
+#define POLLRDNORM 0x0040 /* normal data is readable */
+#define POLLWRNORM POLLOUT
+#define POLLRDBAND 0x0080 /* out-of-band data is readable */
+#define POLLWRBAND 0x0100 /* out-of-band data is writeable */
+
+#define POLLNORM POLLRDNORM
+
+#define POLLERR 0x0008 /* fd has error condition */
+#define POLLHUP 0x0010 /* fd has been hung up on */
+#define POLLNVAL 0x0020 /* invalid pollfd entry */
+
+int poll_using_select(struct my_pollfd *filedes, int nfds, int timeout);
+
+#endif /* _POLL_USING_SELECT_H */
diff --git a/ldap/servers/slapd/prerrstrs.h b/ldap/servers/slapd/prerrstrs.h
new file mode 100644
index 00000000..3f34f277
--- /dev/null
+++ b/ldap/servers/slapd/prerrstrs.h
@@ -0,0 +1,121 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+/*
+ * pserrstrs.h - map NSPR errors to strings (used by errormap.c)
+ *
+ */
+
+/*
+ ****************************************************************************
+ * The code below this point was provided by Nelson Bolyard <nelsonb> of the
+ * Netscape Certificate Server team on 27-March-1998.
+ * Taken from the file ns/security/cmd/lib/NSPRerrs.h on NSS_1_BRANCH.
+ * Last updated from there: 24-July-1998 by Mark Smith <mcs>
+ *
+ * All of the Directory Server specific changes are enclosed inside
+ * #ifdef NS_DS.
+ ****************************************************************************
+ */
+/* General NSPR 2.0 errors */
+/* Caller must #include "prerror.h" */
+
+ER2( PR_OUT_OF_MEMORY_ERROR, "Memory allocation attempt failed." )
+ER2( PR_BAD_DESCRIPTOR_ERROR, "Invalid file descriptor." )
+ER2( PR_WOULD_BLOCK_ERROR, "The operation would have blocked." )
+ER2( PR_ACCESS_FAULT_ERROR, "Invalid memory address argument." )
+ER2( PR_INVALID_METHOD_ERROR, "Invalid function for file type." )
+ER2( PR_ILLEGAL_ACCESS_ERROR, "Invalid memory address argument." )
+ER2( PR_UNKNOWN_ERROR, "Some unknown error has occurred." )
+ER2( PR_PENDING_INTERRUPT_ERROR,"Operation interrupted by another thread." )
+ER2( PR_NOT_IMPLEMENTED_ERROR, "function not implemented." )
+ER2( PR_IO_ERROR, "I/O function error." )
+ER2( PR_IO_TIMEOUT_ERROR, "I/O operation timed out." )
+ER2( PR_IO_PENDING_ERROR, "I/O operation on busy file descriptor." )
+ER2( PR_DIRECTORY_OPEN_ERROR, "The directory could not be opened." )
+ER2( PR_INVALID_ARGUMENT_ERROR, "Invalid function argument." )
+ER2( PR_ADDRESS_NOT_AVAILABLE_ERROR, "Network address not available (in use?)." )
+ER2( PR_ADDRESS_NOT_SUPPORTED_ERROR, "Network address type not supported." )
+ER2( PR_IS_CONNECTED_ERROR, "Already connected." )
+ER2( PR_BAD_ADDRESS_ERROR, "Network address is invalid." )
+ER2( PR_ADDRESS_IN_USE_ERROR, "Local Network address is in use." )
+ER2( PR_CONNECT_REFUSED_ERROR, "Connection refused by peer." )
+ER2( PR_NETWORK_UNREACHABLE_ERROR, "Network address is presently unreachable." )
+ER2( PR_CONNECT_TIMEOUT_ERROR, "Connection attempt timed out." )
+ER2( PR_NOT_CONNECTED_ERROR, "Network file descriptor is not connected." )
+ER2( PR_LOAD_LIBRARY_ERROR, "Failure to load dynamic library." )
+ER2( PR_UNLOAD_LIBRARY_ERROR, "Failure to unload dynamic library." )
+ER2( PR_FIND_SYMBOL_ERROR,
+"Symbol not found in any of the loaded dynamic libraries." )
+ER2( PR_INSUFFICIENT_RESOURCES_ERROR, "Insufficient system resources." )
+ER2( PR_DIRECTORY_LOOKUP_ERROR,
+"A directory lookup on a network address has failed." )
+ER2( PR_TPD_RANGE_ERROR,
+"Attempt to access a TPD key that is out of range." )
+ER2( PR_PROC_DESC_TABLE_FULL_ERROR, "Process open FD table is full." )
+ER2( PR_SYS_DESC_TABLE_FULL_ERROR, "System open FD table is full." )
+ER2( PR_NOT_SOCKET_ERROR,
+"Network operation attempted on non-network file descriptor." )
+ER2( PR_NOT_TCP_SOCKET_ERROR,
+"TCP-specific function attempted on a non-TCP file descriptor." )
+ER2( PR_SOCKET_ADDRESS_IS_BOUND_ERROR, "TCP file descriptor is already bound." )
+ER2( PR_NO_ACCESS_RIGHTS_ERROR, "Access Denied." )
+ER2( PR_OPERATION_NOT_SUPPORTED_ERROR,
+"The requested operation is not supported by the platform." )
+ER2( PR_PROTOCOL_NOT_SUPPORTED_ERROR,
+"The host operating system does not support the protocol requested." )
+ER2( PR_REMOTE_FILE_ERROR, "Access to the remote file has been severed." )
+ER2( PR_BUFFER_OVERFLOW_ERROR,
+"The value requested is too large to be stored in the data buffer provided." )
+ER2( PR_CONNECT_RESET_ERROR, "TCP connection reset by peer." )
+ER2( PR_RANGE_ERROR, "Unused." )
+ER2( PR_DEADLOCK_ERROR, "The operation would have deadlocked." )
+ER2( PR_FILE_IS_LOCKED_ERROR, "The file is already locked." )
+ER2( PR_FILE_TOO_BIG_ERROR,
+"Write would result in file larger than the system allows." )
+ER2( PR_NO_DEVICE_SPACE_ERROR, "The device for storing the file is full." )
+ER2( PR_PIPE_ERROR, "Unused." )
+ER2( PR_NO_SEEK_DEVICE_ERROR, "Unused." )
+ER2( PR_IS_DIRECTORY_ERROR,
+"Cannot perform a normal file operation on a directory." )
+ER2( PR_LOOP_ERROR, "Symbolic link loop." )
+ER2( PR_NAME_TOO_LONG_ERROR, "File name is too long." )
+ER2( PR_FILE_NOT_FOUND_ERROR, "File not found." )
+ER2( PR_NOT_DIRECTORY_ERROR,
+"Cannot perform directory operation on a normal file." )
+ER2( PR_READ_ONLY_FILESYSTEM_ERROR,
+"Cannot write to a read-only file system." )
+ER2( PR_DIRECTORY_NOT_EMPTY_ERROR,
+"Cannot delete a directory that is not empty." )
+ER2( PR_FILESYSTEM_MOUNTED_ERROR,
+"Cannot delete or rename a file object while the file system is busy." )
+ER2( PR_NOT_SAME_DEVICE_ERROR,
+"Cannot rename a file to a file system on another device." )
+ER2( PR_DIRECTORY_CORRUPTED_ERROR,
+"The directory object in the file system is corrupted." )
+ER2( PR_FILE_EXISTS_ERROR,
+"Cannot create or rename a filename that already exists." )
+ER2( PR_MAX_DIRECTORY_ENTRIES_ERROR,
+"Directory is full. No additional filenames may be added." )
+ER2( PR_INVALID_DEVICE_STATE_ERROR,
+"The required device was in an invalid state." )
+ER2( PR_DEVICE_IS_LOCKED_ERROR, "The device is locked." )
+ER2( PR_NO_MORE_FILES_ERROR, "No more entries in the directory." )
+ER2( PR_END_OF_FILE_ERROR, "Encountered end of file." )
+ER2( PR_FILE_SEEK_ERROR, "Seek error." )
+ER2( PR_FILE_IS_BUSY_ERROR, "The file is busy." )
+ER2( PR_OPERATION_ABORTED_ERROR, "The I/O operation was aborted" )
+ER2( PR_IN_PROGRESS_ERROR,
+"Operation is still in progress (probably a non-blocking connect)." )
+ER2( PR_ALREADY_INITIATED_ERROR,
+"Operation has already been initiated (probably a non-blocking connect)." )
+ER2( PR_GROUP_EMPTY_ERROR, "The wait group is empty." )
+ER2( PR_INVALID_STATE_ERROR, "Object state improper for request." )
+ER2( PR_NETWORK_DOWN_ERROR, "Network is down" )
+ER2( PR_SOCKET_SHUTDOWN_ERROR, "Socket shutdown" )
+ER2( PR_CONNECT_ABORTED_ERROR, "Connection aborted" )
+ER2( PR_HOST_UNREACHABLE_ERROR, "Host is unreachable" )
+
+ER2( PR_MAX_ERROR, "Placeholder for the end of the list" )
diff --git a/ldap/servers/slapd/protect_db.c b/ldap/servers/slapd/protect_db.c
new file mode 100644
index 00000000..34a63e80
--- /dev/null
+++ b/ldap/servers/slapd/protect_db.c
@@ -0,0 +1,758 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+/* protect_db.c
+ * Used to police access to the db. Prevents different instances of
+ * slapd from clobbering each other
+ */
+
+
+
+#ifndef _WIN32
+
+#define LOCK_DIR "locks"
+#define LOCK_FILE "lock"
+#define IMPORT_DIR "imports"
+#define EXPORT_DIR "exports"
+#define SERVER_DIR "server"
+#define NUM_TRIES 20
+#define WAIT_TIME 250
+
+#include <sys/types.h>
+#include <sys/time.h>
+#include <sys/stat.h>
+#include <fcntl.h> /* open */
+#include <unistd.h>
+#include <errno.h>
+#include <signal.h>
+#include <sys/param.h> /* MAXPATHLEN */
+#include <dirent.h>
+#include <pwd.h>
+#endif
+
+#include "protect_db.h"
+
+#include "slap.h"
+
+
+#ifndef _WIN32
+/* This is the unix version of the code to protect the db. */
+
+static int
+grab_lockfile()
+{
+ pid_t pid, owning_pid;
+ char lockfile[MAXPATHLEN];
+ int fd, x;
+ int removed_lockfile = 0;
+ struct timeval t;
+ slapdFrontendConfig_t *slapdFrontendConfig = getFrontendConfig();
+
+
+ /* Don't use anything that makes a NSPR call here (like LDAPDebug)! This function
+ gets called by an atexit function, and NSPR is long gone by then. */
+
+ /* Get the name of the lockfile */
+ sprintf(lockfile, "%s/%s", slapdFrontendConfig->instancedir, LOCK_FILE);
+ /* Get our pid */
+ pid = getpid();
+
+ /* Try to grab it */
+ if ((fd = open(lockfile, O_RDWR | O_CREAT | O_EXCL, 0664)) != -1) {
+ /* We got the lock, write our pid to the file */
+ write(fd, (void *) &pid, sizeof(pid_t));
+ close(fd);
+ return 0;
+ }
+
+ /* We weren't able to get the lock. Find out why. */
+ if (errno != EEXIST) {
+ /* Hmm, something happened that we weren't prepared to handle */
+ fprintf(stderr, ERROR_ACCESSING_LOCKFILE, lockfile);
+ return -1;
+ }
+
+ while(1) {
+ /* Try to grab the lockfile NUM_TRIES times waiting WAIT_TIME milliseconds after each try */
+ t.tv_sec = 0;
+ t.tv_usec = WAIT_TIME * 1000;
+ for(x = 0; x < NUM_TRIES; x++) {
+ if ((fd = open(lockfile, O_RDWR | O_CREAT | O_EXCL)) != -1) {
+ /* Got the lock */
+ write(fd, (void *) &pid, sizeof(pid_t));
+ close(fd);
+ return 0;
+ }
+ select(0, NULL, NULL, NULL, &t);
+ }
+
+ /* We still haven't got the lockfile. Find out who owns it and see if they are still up */
+ if ((fd = open(lockfile, O_RDONLY)) != -1) {
+ size_t nb_bytes=0;
+
+ nb_bytes = read(fd, (void *) &owning_pid, sizeof(pid_t));
+ if ( (nb_bytes != (size_t)(sizeof(pid_t)) ) || (owning_pid == 0) || (kill(owning_pid, 0) != 0 && errno == ESRCH) ) {
+ /* The process that owns the lock is dead. Try to remove the old lockfile. */
+ if (unlink(lockfile) != 0) {
+ /* Unable to remove the stale lockfile. */
+ fprintf(stderr, LOCKFILE_DEAD_OWNER, lockfile, owning_pid);
+ return -1;
+ }
+ if (removed_lockfile) {
+ /* We already removed the lockfile once. Best thing to do is give up. */
+ /* I'm not sure what should be given as an error here */
+ fprintf(stderr, UNABLE_TO_GET_LOCKFILE, lockfile);
+ return -1;
+ } else {
+ removed_lockfile = 1;
+ }
+ } else {
+ /* It looks like the owner of the lockfile is still up and running. */
+ fprintf(stderr, LOCKFILE_ALREADY_OWNED, owning_pid);
+ return -1;
+ }
+ }
+ }
+}
+
+static void
+release_lockfile()
+{
+ char lockfile[MAXPATHLEN];
+ slapdFrontendConfig_t *slapdFrontendConfig = getFrontendConfig();
+
+ /* This function assumes that the caller owns the lock, it doesn't check to make sure! */
+
+ sprintf(lockfile, "%s/%s", slapdFrontendConfig->instancedir, LOCK_FILE);
+ unlink(lockfile);
+}
+
+
+/* Takes the pid of a process. Returns 1 if the process seems
+ * to be up, otherwise it returns 0.
+ */
+static int
+is_process_up(pid_t pid)
+{
+ if (kill(pid, 0) == -1 && errno == ESRCH) {
+ return 0;
+ } else {
+ return 1;
+ }
+}
+
+/* Make sure the directory 'dir' exists and is owned bye the user
+ * the server runs as. Returns 0 if everything is ok.
+ */
+static int
+make_sure_dir_exists(char *dir)
+{
+ struct passwd* pw;
+ struct stat stat_buffer;
+ slapdFrontendConfig_t *slapdFrontendConfig = getFrontendConfig();
+
+ /* Make sure it exists */
+ if (PR_MkDir(dir, 0755) != PR_SUCCESS) {
+ PRErrorCode prerr = PR_GetError();
+ if (prerr != PR_FILE_EXISTS_ERROR) {
+ LDAPDebug(LDAP_DEBUG_ANY, FILE_CREATE_ERROR, dir, prerr, slapd_pr_strerror(prerr));
+ return 1;
+ }
+ }
+
+ /* Make sure it's owned by the correct user */
+ if (slapdFrontendConfig->localuser != NULL) {
+ if ( (pw = getpwnam(slapdFrontendConfig->localuser)) == NULL ) {
+ LDAPDebug(LDAP_DEBUG_ANY, GETPWNAM_WARNING, slapdFrontendConfig->localuser, errno, strerror(errno));
+ } else {
+ if (chown(dir, pw->pw_uid, -1) == -1) {
+ stat(dir, &stat_buffer);
+ if (stat_buffer.st_uid != pw->pw_uid) {
+ LDAPDebug(LDAP_DEBUG_ANY, CHOWN_WARNING, dir, 0, 0);
+ }
+ }
+ } /* else */
+ }
+
+ return 0;
+}
+
+/* Creates a file in the directory 'dir_name' whose name is the
+ * pid for this process.
+ */
+static void
+add_this_process_to(char *dir_name)
+{
+ char file_name[MAXPATHLEN];
+ struct passwd* pw;
+ struct stat stat_buffer;
+ PRFileDesc* prfd;
+ slapdFrontendConfig_t *slapdFrontendConfig = getFrontendConfig();
+
+ sprintf(file_name, "%s/%d", dir_name, getpid());
+
+ if ((prfd = PR_Open(file_name, PR_RDWR | PR_CREATE_FILE, 0666)) == NULL) {
+ LDAPDebug(LDAP_DEBUG_ANY, FILE_CREATE_WARNING, file_name, 0, 0);
+ return;
+ }
+
+ /* Make sure the owner is of the file is the user the server
+ * runs as. */
+ if (slapdFrontendConfig->localuser != NULL) {
+ if ( (pw = getpwnam(slapdFrontendConfig->localuser)) == NULL ) {
+ LDAPDebug(LDAP_DEBUG_ANY, GETPWNAM_WARNING, slapdFrontendConfig->localuser, errno, strerror(errno));
+ } else {
+ if (chown(file_name, pw->pw_uid, -1) == -1) {
+ stat(file_name, &stat_buffer);
+ if (stat_buffer.st_uid != pw->pw_uid) {
+ LDAPDebug(LDAP_DEBUG_ANY, CHOWN_WARNING, file_name, 0, 0);
+ }
+ }
+ } /* else */
+ }
+ PR_Close(prfd);
+}
+
+
+/* The directory 'dir_name' is expected to contain files whose
+ * names are the pid of running slapd processes. This
+ * function will check each entry in the directory and remove
+ * any files that represent processes that don't exist.
+ * Returns 0 if there are no processes represented, or
+ * the pid of one of the processes.
+ */
+static long
+sample_and_update(char *dir_name)
+{
+ PRDir *dir;
+ PRDirEntry *entry;
+ pid_t pid;
+ long result = 0;
+ char *endp;
+ char file_name[MAXPATHLEN];
+
+ if ((dir = PR_OpenDir(dir_name)) == NULL) {
+ return 0;
+ }
+
+ while((entry = PR_ReadDir(dir, PR_SKIP_BOTH)) != NULL) {
+ pid = (pid_t) strtol(entry->name, &endp, 0);
+ if (*endp != '\0') {
+ /* not quite sure what this file was, but we
+ * didn't put it there */
+ continue;
+ }
+ if (is_process_up(pid)) {
+ result = (long) pid;
+ } else {
+ sprintf(file_name, "%s/%s", dir_name, entry->name);
+ PR_Delete(file_name);
+ }
+ }
+ PR_CloseDir(dir);
+ return result;
+}
+
+
+/* Removes any stale pid entries and the pid entry for this
+ * process from the directory 'dir_name'.
+ */
+static void
+remove_and_update(char *dir_name)
+{
+ /* since this is called from an atexit function, we can't use
+ * NSPR. */
+
+ DIR *dir;
+ struct dirent *entry;
+ pid_t pid;
+ pid_t our_pid;
+ char *endp;
+ char file_name[MAXPATHLEN];
+
+ /* get our pid */
+ our_pid = getpid();
+
+ if ((dir = opendir(dir_name)) == NULL) {
+ return;
+ }
+
+ while((entry = readdir(dir)) != NULL) {
+
+ /* skip dot and dot-dot */
+ if (strcmp(entry->d_name, ".") == 0 ||
+ strcmp(entry->d_name, "..") == 0)
+ continue;
+
+ pid = (pid_t) strtol(entry->d_name, &endp, 0);
+ if (*endp != '\0') {
+ /* not quite sure what this file was, but we
+ * didn't put it there */
+ continue;
+ }
+ if (!is_process_up(pid) || pid == our_pid) {
+ sprintf(file_name, "%s/%s", dir_name, entry->d_name);
+ unlink(file_name);
+ }
+ }
+ closedir(dir);
+}
+
+
+
+/* Walks through all the pid directories and clears any stale
+ * pids. It also removes the files for this process.
+ */
+void
+remove_slapd_process()
+{
+ char lock_dir[MAXPATHLEN];
+ char import_dir[MAXPATHLEN];
+ char export_dir[MAXPATHLEN];
+ char server_dir[MAXPATHLEN];
+ slapdFrontendConfig_t *slapdFrontendConfig = getFrontendConfig();
+
+
+ /* Create the name of the directories that hold the pids of the currently running
+ * ns-slapd processes */
+ sprintf(lock_dir, "%s/%s", slapdFrontendConfig->instancedir, LOCK_DIR);
+ sprintf(import_dir, "%s/%s/%s", slapdFrontendConfig->instancedir, LOCK_DIR, IMPORT_DIR);
+ sprintf(export_dir, "%s/%s/%s", slapdFrontendConfig->instancedir, LOCK_DIR, EXPORT_DIR);
+ sprintf(server_dir, "%s/%s/%s", slapdFrontendConfig->instancedir, LOCK_DIR, SERVER_DIR);
+
+ /* Grab the lockfile */
+ if (grab_lockfile() != 0) {
+ /* Unable to grab the lockfile */
+ return;
+ }
+
+
+ remove_and_update(import_dir);
+ remove_and_update(export_dir);
+ remove_and_update(server_dir);
+
+ release_lockfile();
+}
+
+int
+add_new_slapd_process(int exec_mode, int r_flag, int skip_flag)
+{
+ char lock_dir[MAXPATHLEN];
+ char import_dir[MAXPATHLEN];
+ char export_dir[MAXPATHLEN];
+ char server_dir[MAXPATHLEN];
+ int running, importing, exporting;
+ int result = 0;
+ slapdFrontendConfig_t *slapdFrontendConfig = getFrontendConfig();
+
+ if (skip_flag) {
+ return 0;
+ }
+
+ /* Create the name of the directories that hold the pids of the currently running
+ * ns-slapd processes */
+ sprintf(lock_dir, "%s/%s", slapdFrontendConfig->instancedir, LOCK_DIR);
+ sprintf(import_dir, "%s/%s/%s", slapdFrontendConfig->instancedir, LOCK_DIR, IMPORT_DIR);
+ sprintf(export_dir, "%s/%s/%s", slapdFrontendConfig->instancedir, LOCK_DIR, EXPORT_DIR);
+ sprintf(server_dir, "%s/%s/%s", slapdFrontendConfig->instancedir, LOCK_DIR, SERVER_DIR);
+
+ /* Grab the lockfile */
+ if (grab_lockfile() != 0) {
+ /* Unable to grab the lockfile */
+ return -1;
+ }
+
+ /* Make sure the directories exist */
+ if (make_sure_dir_exists(lock_dir) != 0 ||
+ make_sure_dir_exists(import_dir) != 0 ||
+ make_sure_dir_exists(export_dir) != 0 ||
+ make_sure_dir_exists(server_dir) != 0) {
+ release_lockfile();
+ return -1;
+ }
+
+ /* Go through the directories and find out what's going on.
+ * Clear any stale pids encountered. */
+ importing = sample_and_update(import_dir);
+ exporting = sample_and_update(export_dir);
+ running = sample_and_update(server_dir);
+
+ switch (exec_mode) {
+ case SLAPD_EXEMODE_SLAPD:
+ if (running) {
+ result = -1;
+ LDAPDebug(LDAP_DEBUG_ANY, NO_SERVER_DUE_TO_SERVER, running, 0, 0);
+ } else if (importing) {
+ result = -1;
+ LDAPDebug(LDAP_DEBUG_ANY, NO_SERVER_DUE_TO_IMPORT, importing, 0, 0);
+ } else {
+ add_this_process_to(server_dir);
+ result = 0;
+ }
+ break;
+ case SLAPD_EXEMODE_DB2LDIF:
+ if (r_flag) {
+ /* When the -r flag is used in db2ldif we need to make sure
+ * we get a consistent snapshot of the server. As a result
+ * it needs to run by itself, so no other slapd process can
+ * change the database while it is running. */
+ if (running || importing) {
+ LDAPDebug(LDAP_DEBUG_ANY, NO_DB2LDIFR_DUE_TO_USE, 0, 0, 0);
+ result = -1;
+ } else {
+ /* Even though this is really going to export code, we will
+ * but it in the importing dir so no other process can change
+ * things while we are doing ldif2db with the -r flag. */
+ add_this_process_to(import_dir);
+ result = 0;
+ }
+ } else {
+ if (importing) {
+ LDAPDebug(LDAP_DEBUG_ANY, NO_DB2LDIF_DUE_TO_IMPORT, importing, 0, 0);
+ result = -1;
+ } else {
+ add_this_process_to(export_dir);
+ result = 0;
+ }
+ }
+ break;
+ case SLAPD_EXEMODE_DB2ARCHIVE:
+ if (importing) {
+ LDAPDebug(LDAP_DEBUG_ANY, NO_DB2BAK_DUE_TO_IMPORT, importing, 0, 0);
+ result = -1;
+ } else {
+ add_this_process_to(export_dir);
+ result = 0;
+ }
+ break;
+ case SLAPD_EXEMODE_ARCHIVE2DB:
+ case SLAPD_EXEMODE_LDIF2DB:
+ if (running || importing || exporting) {
+ LDAPDebug(LDAP_DEBUG_ANY, NO_IMPORT_DUE_TO_USE, 0, 0, 0);
+ result = -1;
+ } else {
+ add_this_process_to(import_dir);
+ result = 0;
+ }
+ break;
+ case SLAPD_EXEMODE_DB2INDEX:
+ if (running || importing || exporting) {
+ LDAPDebug(LDAP_DEBUG_ANY, NO_DB2INDEX_DUE_TO_USE, 0, 0, 0);
+ result = -1;
+ } else {
+ add_this_process_to(import_dir);
+ result = 0;
+ }
+ break;
+#if defined(UPGRADEDB)
+ case SLAPD_EXEMODE_UPGRADEDB:
+ if (running || importing || exporting) {
+ LDAPDebug(LDAP_DEBUG_ANY, NO_UPGRADEDB_DUE_TO_USE, 0, 0, 0);
+ result = -1;
+ } else {
+ add_this_process_to(import_dir);
+ result = 0;
+ }
+ break;
+#endif
+ case SLAPD_EXEMODE_DBTEST:
+ if (running || importing || exporting) {
+ LDAPDebug(LDAP_DEBUG_ANY, NO_DBTEST_DUE_TO_USE, 0, 0, 0);
+ result = -1;
+ } else {
+ add_this_process_to(import_dir);
+ result = 0;
+ }
+ break;
+ }
+
+ release_lockfile();
+
+ if (result == 0) {
+ atexit(remove_slapd_process);
+ }
+
+ return result;
+}
+
+
+
+/* is_slapd_running()
+ * returns 1 if slapd is running, 0 if not, -1 on error
+ */
+
+
+int
+is_slapd_running() {
+ char server_dir[MAXPATHLEN];
+ char lock_dir[MAXPATHLEN];
+ slapdFrontendConfig_t *cfg = getFrontendConfig();
+ int running = 0;
+
+ sprintf(lock_dir, "%s/%s", cfg->instancedir, LOCK_DIR);
+ sprintf( server_dir, "%s/%s/%s", cfg->instancedir, LOCK_DIR, SERVER_DIR);
+
+ /* Grab the lockfile */
+ if (grab_lockfile() != 0) {
+ /* Unable to grab the lockfile */
+ return -1;
+ }
+
+ /* Make sure the directories exist */
+ if (make_sure_dir_exists(lock_dir) != 0 ||
+ make_sure_dir_exists(server_dir) != 0) {
+ release_lockfile();
+ return -1;
+ }
+
+ running = sample_and_update(server_dir);
+ release_lockfile();
+ return running;
+}
+
+
+#else /* _WIN32 */
+
+/* The NT version of this code */
+
+/* Returns 1 if the mutex named 'mutexName' otherwise it
+ * returns 0
+ */
+int
+mutex_exists( char *mutexName )
+{
+ if ( OpenMutex( SYNCHRONIZE, FALSE, mutexName ) == NULL ) {
+ return( 0 );
+ } else {
+ return( 1 );
+ }
+}
+
+/* is_slapd_running():
+ * returns 1 if slapd is running, 0 if not
+ */
+
+int
+is_slapd_running() {
+ char mutexName[ MAXPATHLEN + 1 ];
+ char serverMutexName[ MAXPATHLEN + 1 ];
+ int result = 0;
+ slapdFrontendConfig_t *cfg = getFrontendConfig();
+
+ strncpy( mutexName, cfg->instancedir, MAXPATHLEN );
+ strncpy( serverMutexName, cfg->instancedir, MAXPATHLEN );
+ mutexName[ MAXPATHLEN ] = '\0';
+
+ serverMutexName[ MAXPATHLEN ] = '\0';
+ strcat( serverMutexName, "/server" );
+
+ return mutex_exists ( serverMutexName );
+}
+
+static void fix_mutex_name(char *name)
+{
+ /* On NT mutex names cannot contain the '\' character.
+ * This functions replaces '\' with '/' in the supplied
+ * name. */
+ int x;
+
+ for (x = 0; name[x] != '\0'; x++) {
+ if ('\\' == name[x]) {
+ name[x] = '/';
+ }
+ }
+}
+
+/*
+ * We retain any opened handle to the mutex we create here,
+ * to use when we shutdown, to delete the mutex prior to
+ * signaling that we've exited.
+ */
+static HANDLE open_mutex = NULL;
+
+/*
+ * Call this to clean up the locks, before signaling
+ * that the server is down.
+ */
+void
+remove_slapd_process()
+{
+ if (open_mutex) {
+ CloseHandle(open_mutex);
+ }
+}
+
+/* This function makes sure different instances of slapd don't
+ * run in conflicting modes at the same time. The WIN32 version
+ * uses mutexes as names that the kernel handles. Basically there
+ * is a server mutex, and import mutex, and an export mutex. If
+ * the server mutex exists, then the server is running. If the
+ * import mutex exists, then either ldif2db or bak2db is running.
+ * If the export mutex exists, then one or more of db2ldif or db2bak
+ * are running. There is also a mutex that is actually locked when
+ * checking the existence of the other mutexes. The OS will
+ * automatically remove a mutex if no process has a handle on it.
+ * returns a 0 if it is ok for the process to run
+ * returns a -1 if the process can't run do to a conflict with other
+ * slapd processes
+ */
+int
+add_new_slapd_process(int exec_mode, int r_flag, int skip_flag)
+{
+ char mutexName[ MAXPATHLEN + 1 ];
+ char serverMutexName[ MAXPATHLEN + 1 ];
+ char importMutexName[ MAXPATHLEN + 1 ];
+ char exportMutexName[ MAXPATHLEN + 1 ];
+
+ HANDLE mutex;
+ SECURITY_ATTRIBUTES mutexAttributes;
+ PSECURITY_DESCRIPTOR pSD;
+ LPVOID lpMsgBuf;
+
+ int result = 0;
+
+ slapdFrontendConfig_t *slapdFrontendConfig = getFrontendConfig();
+
+ if (skip_flag) {
+ return 0;
+ }
+
+ /* Create the names for the mutexes */
+ strcpy(mutexName, slapdFrontendConfig->instancedir);
+
+ /* Make sure the name of the mutex is legal. */
+ fix_mutex_name(mutexName);
+
+ sprintf(serverMutexName, "%s/server", mutexName);
+ sprintf(importMutexName, "%s/import", mutexName);
+ sprintf(exportMutexName, "%s/export", mutexName);
+
+ /* Fill in the security crap for the mutex */
+ pSD = (PSECURITY_DESCRIPTOR)slapi_ch_malloc( sizeof( SECURITY_DESCRIPTOR ) );
+ InitializeSecurityDescriptor( pSD, SECURITY_DESCRIPTOR_REVISION );
+ SetSecurityDescriptorDacl( pSD, TRUE, NULL, FALSE );
+ mutexAttributes.nLength = sizeof( mutexAttributes );
+ mutexAttributes.lpSecurityDescriptor = pSD;
+ mutexAttributes.bInheritHandle = FALSE;
+
+ /* Get a handle to the main mutex */
+ if ( ( mutex = CreateMutex( &mutexAttributes, FALSE, mutexName ) ) == NULL ) {
+ FormatMessage(
+ FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
+ NULL,
+ GetLastError(),
+ MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), /* Default language */
+ (LPTSTR) &lpMsgBuf,
+ 0,
+ NULL
+ );
+
+ LDAPDebug( LDAP_DEBUG_ANY, CREATE_MUTEX_ERROR, lpMsgBuf, 0, 0 );
+ LocalFree( lpMsgBuf );
+ exit( 1 );
+ }
+
+ /* Lock the main mutex */
+ if ( WaitForSingleObject( mutex, INFINITE ) == WAIT_FAILED ) {
+ FormatMessage(
+ FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
+ NULL,
+ GetLastError(),
+ MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), /* Default language */
+ (LPTSTR) &lpMsgBuf,
+ 0,
+ NULL
+ );
+
+ LDAPDebug( LDAP_DEBUG_ANY, WAIT_ERROR, lpMsgBuf, 0, 0 );
+ LocalFree( lpMsgBuf );
+ exit( 1 );
+ }
+
+
+ switch (exec_mode) {
+ case SLAPD_EXEMODE_SLAPD:
+ if ( mutex_exists( serverMutexName ) ||
+ mutex_exists( importMutexName ) ) {
+ LDAPDebug( LDAP_DEBUG_ANY, NO_SERVER_DUE_TO_USE, 0, 0, 0);
+ result = -1;
+ } else {
+ open_mutex = CreateMutex( &mutexAttributes, FALSE, serverMutexName );
+ result = 0;
+ }
+ break;
+ case SLAPD_EXEMODE_DB2LDIF:
+ if (r_flag) {
+ /* When the -r flag is used in db2ldif we need to make sure
+ * we get a consistent snapshot of the server. As a result
+ * it needs to run by itself, so no other slapd process can
+ * change the database while it is running. */
+ if ( mutex_exists( serverMutexName ) ||
+ mutex_exists( importMutexName ) ||
+ mutex_exists( exportMutexName ) ) {
+ LDAPDebug(LDAP_DEBUG_ANY, NO_DB2LDIFR_DUE_TO_USE, 0, 0, 0);
+ result = -1;
+ } else {
+ CreateMutex( &mutexAttributes, FALSE, exportMutexName );
+ result = 0;
+ }
+ break;
+ }
+ case SLAPD_EXEMODE_DB2ARCHIVE:
+ if ( mutex_exists( importMutexName ) ) {
+ LDAPDebug(LDAP_DEBUG_ANY, NO_EXPORT_DUE_TO_IMPORT, 0, 0, 0);
+ result = -1;
+ } else {
+ CreateMutex( &mutexAttributes, FALSE, exportMutexName );
+ result = 0;
+ }
+ break;
+ case SLAPD_EXEMODE_ARCHIVE2DB:
+ case SLAPD_EXEMODE_LDIF2DB:
+ if ( mutex_exists( serverMutexName ) ||
+ mutex_exists( importMutexName ) ||
+ mutex_exists( exportMutexName ) ) {
+ LDAPDebug(LDAP_DEBUG_ANY, NO_IMPORT_DUE_TO_USE, 0, 0, 0);
+ result = -1;
+ } else {
+ CreateMutex( &mutexAttributes, FALSE, importMutexName );
+ result = 0;
+ }
+ break;
+#if defined(UPGRADEDB)
+ case SLAPD_EXEMODE_UPGRADEDB:
+ if ( mutex_exists( serverMutexName ) ||
+ mutex_exists( importMutexName ) ||
+ mutex_exists( exportMutexName ) ) {
+ LDAPDebug(LDAP_DEBUG_ANY, NO_UPGRADEDB_DUE_TO_USE, 0, 0, 0);
+ result = -1;
+ } else {
+ CreateMutex( &mutexAttributes, FALSE, importMutexName );
+ result = 0;
+ }
+ break;
+#endif
+ case SLAPD_EXEMODE_DBTEST:
+ if ( mutex_exists( serverMutexName ) ||
+ mutex_exists( importMutexName ) ||
+ mutex_exists( exportMutexName ) ) {
+ LDAPDebug(LDAP_DEBUG_ANY, NO_DBTEST_DUE_TO_USE, 0, 0, 0);
+ result = -1;
+ } else {
+ CreateMutex( &mutexAttributes, FALSE, importMutexName );
+ result = 0;
+ }
+ break;
+ }
+
+ /* release the main mutex */
+ ReleaseMutex( mutex );
+
+ slapi_ch_free((void**)&pSD );
+
+ return( result );
+}
+#endif /* _WIN32 */
+
diff --git a/ldap/servers/slapd/protect_db.h b/ldap/servers/slapd/protect_db.h
new file mode 100644
index 00000000..6d6d59d0
--- /dev/null
+++ b/ldap/servers/slapd/protect_db.h
@@ -0,0 +1,75 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+/* Header file for protect_db.c */
+
+int add_new_slapd_process(int exec_mode, int r_flag, int skip_flag);
+int is_slapd_running();
+void remove_slapd_process();
+
+/*
+ * These are the format strings used in the error messages in protect_db.c.
+ * After each string is a short description of what gets inserted into the
+ * string.
+ */
+
+#define ERROR_ACCESSING_LOCKFILE "Error - Problem accessing the lockfile %s\n"
+ /* name of lockfile */
+
+#define LOCKFILE_DEAD_OWNER "Error - The lockfile, %s, is held by process %d,\nwhich no longer seems to be running. If this is\nthe case, please remove the lockfile\n"
+ /* name of lockfile, pid of owning process */
+
+#define UNABLE_TO_GET_LOCKFILE "Error - Unable to acquire the lockfile, %s.\nPlease make sure no instances of the ns-slapd process are running, remove the lockfile and try again\n"
+ /* name of lockfile */
+
+#define LOCKFILE_ALREADY_OWNED "Error - Unable to start because process %d holds a lock.\nPlease make sure that the process is running correctly\nIf it is not, kill it and try again.\n"
+ /* pid of owning process */
+
+#define FILE_CREATE_ERROR "Error - Unable to create %s, " SLAPI_COMPONENT_NAME_NSPR " error %d (%s)\n"
+ /* name of file */
+
+#define FILE_CREATE_WARNING "Warning - Unable to create %s\n"
+ /* name of file */
+
+#define GETPWNAM_WARNING "Warning - Unable to find user %s in system account database, errno %d (%s)\n"
+ /* user name, error code, error message */
+
+#define CHOWN_WARNING "Warning - couldn't set the ownership for %s\n"
+ /* file name */
+
+#define NO_SERVER_DUE_TO_SERVER "Unable to start slapd because it is already running as process %d\n"
+ /* pid of running slapd process */
+
+#define NO_SERVER_DUE_TO_IMPORT "Unable to start slapd because a database is being imported by process %d\n"
+ /* pid of importing process */
+
+#define NO_SERVER_DUE_TO_USE "Unable to start slapd because the database is in use\nby another slapd process\n"
+
+#define NO_DB2LDIFR_DUE_TO_USE "Unable to run db2ldif with the -r flag because the database is being used by another slapd process.\n"
+
+#define NO_DB2LDIF_DUE_TO_IMPORT "Unable to run db2ldif because the process %d is importing the database.\n"
+ /* pid of importing process */
+
+#define NO_DB2BAK_DUE_TO_IMPORT "Unable to run db2bak because the process %d is importing the database.\n"
+ /* pid of importing process */
+
+#define NO_EXPORT_DUE_TO_IMPORT "Unable to export the database because it is being imported.\n"
+
+#define NO_IMPORT_DUE_TO_USE "Unable to import the database because it is being used by another slapd process.\n"
+
+#define NO_DBTEST_DUE_TO_USE "Unable to test the database because it is being used by another slapd process.\n"
+
+#define NO_DB2INDEX_DUE_TO_USE "Unable to create an index because the database is being used by another slapd process.\n"
+
+#if defined(UPGRADEDB)
+#define NO_UPGRADEDB_DUE_TO_USE "Unable to recreate index files because the database is being used by another slapd process.\n"
+#endif
+
+#define CREATE_MUTEX_ERROR "Error - CreateMutex failed: %s\n"
+ /* reason for failure */
+
+#define WAIT_ERROR "Error - wait failed: %s\n"
+ /* reason for failure */
+
diff --git a/ldap/servers/slapd/proto-slap.h b/ldap/servers/slapd/proto-slap.h
new file mode 100644
index 00000000..06d718eb
--- /dev/null
+++ b/ldap/servers/slapd/proto-slap.h
@@ -0,0 +1,1163 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+#ifndef _PROTO_SLAP
+#define _PROTO_SLAP
+
+/*
+ * Forward structure declarations
+ */
+struct dse;
+struct dsecallback;
+
+/*
+ * abandon.c
+ */
+void do_abandon( Slapi_PBlock *pb );
+
+
+/*
+ * add.c
+ */
+void do_add( Slapi_PBlock *pb );
+
+
+/*
+ * attr.c
+ */
+void attr_done(Slapi_Attr *a);
+int attr_add_valuearray(Slapi_Attr *a, Slapi_Value **vals, const char *dn);
+void attr_replace(Slapi_Attr *a, Slapi_Value **vals);
+int attr_check_onoff ( const char *attr_name, char *value, long minval, long maxval, char *errorbuf );
+int attr_check_minmax ( const char *attr_name, char *value, long minval, long maxval, char *errorbuf );
+
+/*
+ * attrlist.c
+ */
+
+void attrlist_free(Slapi_Attr *alist);
+int attrlist_find_or_create(Slapi_Attr **alist, const char *type, Slapi_Attr ***a);
+int attrlist_find_or_create_locking_optional(Slapi_Attr **alist, const char *type, Slapi_Attr ***a, PRBool use_lock, PRBool ref_count);
+void attrlist_merge( Slapi_Attr **alist, const char *type, struct berval **vals );
+void attrlist_merge_valuearray( Slapi_Attr **alist, const char *type, Slapi_Value **vals );
+int attrlist_delete( Slapi_Attr **attrs, const char *type );
+Slapi_Attr *attrlist_find( Slapi_Attr *a, const char *type );
+Slapi_Attr *attrlist_remove(Slapi_Attr **attrs, const char *type);
+void attrlist_add(Slapi_Attr **attrs, Slapi_Attr *a);
+int attrlist_count_subtypes(Slapi_Attr *a, const char *type);
+Slapi_Attr *attrlist_find_ex( Slapi_Attr *a, const char *type, int *type_name_disposition, char** actual_type_name, void **hint );
+void attrlist_replace(Slapi_Attr **alist, const char *type, struct berval **vals);
+
+/*
+ * attrsyntax.c
+ */
+void attr_syntax_read_lock(void);
+void attr_syntax_unlock_read(void);
+int attr_syntax_exists (const char *attr_name);
+void attr_syntax_delete ( struct asyntaxinfo *asip );
+#define SLAPI_SYNTAXLENGTH_NONE (-1) /* for syntaxlength parameter */
+int attr_syntax_create( const char *attr_oid, char *const*attr_names,
+ int num_names, const char *attr_desc, const char *attr_superior,
+ const char *mr_equality, const char *mr_ordering,
+ const char *mr_substring, char *const *attr_origins,
+ const char *attr_syntax, int syntaxlength, unsigned long flags,
+ struct asyntaxinfo **asip );
+void attr_syntax_free( struct asyntaxinfo *a );
+int attr_syntax_add( struct asyntaxinfo *asip );
+char *attr_syntax_normalize_no_lookup( const char *s );
+void attr_syntax_enumerate_attrs(AttrEnumFunc aef, void *arg, PRBool writelock);
+void attr_syntax_all_clear_flag( unsigned long flag );
+void attr_syntax_delete_all_not_flagged( unsigned long flag );
+struct asyntaxinfo *attr_syntax_get_by_oid ( const char *oid );
+struct asyntaxinfo *attr_syntax_get_by_name ( const char *name );
+struct asyntaxinfo *attr_syntax_get_by_name_locking_optional ( const char *name, PRBool use_lock, PRBool ref_count );
+/*
+ * Call attr_syntax_return() when you are done using a value returned
+ * by attr_syntax_get_by_oid() or attr_syntax_get_by_name().
+ */
+void attr_syntax_return( struct asyntaxinfo *asi );
+void attr_syntax_return_locking_optional( struct asyntaxinfo *asi, PRBool use_lock );
+
+/*
+ * value.c
+ */
+size_t value_size(const Slapi_Value *v);
+
+/*
+ * valueset.c
+ */
+int valuearray_init_bervalarray(struct berval **bvals, Slapi_Value ***cvals);
+int valuearray_get_bervalarray(Slapi_Value **cvals, struct berval ***bvals); /* JCM SLOW FUNCTION */
+void valuearray_free(Slapi_Value ***va);
+Slapi_Value *valuearray_remove_value(const Slapi_Attr *a, Slapi_Value **va, const Slapi_Value *v);
+void valuearray_remove_value_atindex(Slapi_Value **va, int index);
+int valuearray_isempty( Slapi_Value **va);
+void valuearray_update_csn(Slapi_Value **va, CSNType t, const CSN *csn);
+int valuearray_count( Slapi_Value **va);
+int valuearray_next_value( Slapi_Value **va, int index, Slapi_Value **v);
+int valuearray_first_value( Slapi_Value **va, Slapi_Value **v );
+
+void valuearrayfast_init(struct valuearrayfast *vaf,Slapi_Value **va);
+void valuearrayfast_done(struct valuearrayfast *vaf);
+void valuearrayfast_add_value(struct valuearrayfast *vaf,const Slapi_Value *v);
+void valuearrayfast_add_value_passin(struct valuearrayfast *vaf,Slapi_Value *v);
+void valuearrayfast_add_valuearrayfast(struct valuearrayfast *vaf,const struct valuearrayfast *vaf_add);
+
+int valuetree_add_value( const char *type, struct slapdplugin *pi, const Slapi_Value *va, Avlnode **valuetreep);
+int valuetree_add_valuearray( const char *type, struct slapdplugin *pi, Slapi_Value **va, Avlnode **valuetreep, int *duplicate_index);
+void valuetree_free( Avlnode **valuetreep );
+
+/* Valueset functions */
+
+int valueset_isempty( const Slapi_ValueSet *vs);
+Slapi_Value *valueset_find(const Slapi_Attr *a, const Slapi_ValueSet *vs, const Slapi_Value *v);
+Slapi_Value *valueset_remove_value(const Slapi_Attr *a, Slapi_ValueSet *vs, const Slapi_Value *v);
+int valueset_remove_valuearray(Slapi_ValueSet *vs, const Slapi_Attr *a, Slapi_Value **valuestodelete, int flags, Slapi_Value ***va_out);
+int valueset_purge(Slapi_ValueSet *vs, const CSN *csn);
+Slapi_Value **valueset_get_valuearray(const Slapi_ValueSet *vs);
+size_t valueset_size(const Slapi_ValueSet *vs);
+void valueset_add_valuearray(Slapi_ValueSet *vs, Slapi_Value **addvals);
+void valueset_add_valuearray_ext(Slapi_ValueSet *vs, Slapi_Value **addvals, PRUint32 flags);
+void valueset_add_string(Slapi_ValueSet *vs, const char *s, CSNType t, const CSN *csn);
+void valueset_update_csn(Slapi_ValueSet *vs, CSNType t, const CSN *csn);
+void valueset_add_valueset(Slapi_ValueSet *vs1, const Slapi_ValueSet *vs2);
+int valueset_intersectswith_valuearray(Slapi_ValueSet *vs, const Slapi_Attr *a, Slapi_Value **values, int *duplicate_index);
+Slapi_ValueSet *valueset_dup(const Slapi_ValueSet *dupee);
+void valueset_remove_string(const Slapi_Attr *a, Slapi_ValueSet *vs, const char *s);
+void valueset_replace(Slapi_ValueSet *vs, Slapi_Value **vals);
+void valueset_update_csn_for_valuearray(Slapi_ValueSet *vs, const Slapi_Attr *a, Slapi_Value **valuestoupdate, CSNType t, const CSN *csn, Slapi_Value ***valuesupdated);
+void valueset_set_valuearray_byval(Slapi_ValueSet *vs, Slapi_Value **addvals);
+void valueset_set_valuearray_passin(Slapi_ValueSet *vs, Slapi_Value **addvals);
+
+
+/*
+ * ava.c
+ */
+int get_ava( BerElement *ber, struct ava *ava );
+void ava_done( struct ava *ava );
+int rdn2ava( char *rdn, struct ava *ava );
+
+/*
+ * backend.c
+ */
+void be_init(Slapi_Backend *be, const char *type, const char *name, int isprivate, int logchanges, int sizelimit, int timelimit );
+void be_done(Slapi_Backend *be);
+void be_addsuffix(Slapi_Backend *be,const Slapi_DN *suffix);
+Slapi_DN *be_getconfigdn(Slapi_Backend *be,Slapi_DN *dn);
+Slapi_DN *be_getmonitordn(Slapi_Backend *be,Slapi_DN *dn);
+int be_writeconfig (Slapi_Backend *be);
+
+/*
+ * backend_manager.c
+ */
+Slapi_Backend *be_new_internal(struct dse *pdse, const char *type, const char *name);
+int fedse_create_startOK(char *filename, char *startokfilename, const char *configdir);
+void be_cleanupall();
+void be_flushall();
+int be_remove( Slapi_Backend *be );
+void g_set_defsize(int val);
+void g_set_deftime(int val);
+Slapi_Backend *g_get_user_backend( int n );
+int g_get_defsize();
+int g_get_deftime();
+void be_unbindall( Connection *conn, Operation *op);
+int be_nbackends_public();
+
+/*
+ * bind.c
+ */
+void do_bind( Slapi_PBlock *pb );
+void init_saslmechanisms( void );
+
+
+/*
+ * compare.c
+ */
+void do_compare( Slapi_PBlock *pb );
+
+/*
+ * computed.c
+ */
+int compute_attribute(char *type, Slapi_PBlock *pb,BerElement *ber,Slapi_Entry *e,int attrsonly,char *requested_type);
+int compute_init();
+int compute_terminate();
+
+
+/*
+ * config.c
+ */
+int slapd_bootstrap_config(const char *configdir);
+int config_set_storagescheme();
+int get_netsite_root_path(char *pathname);
+int slapd_write_config ();
+int config_set_port( const char *attrname, char *port, char *errorbuf, int apply );
+int config_set_secureport( const char *attrname, char *port, char *errorbuf, int apply );
+int config_set_SSLclientAuth( const char *attrname, char *value, char *errorbuf, int apply );
+int config_set_ssl_check_hostname( const char *attrname, char *value, char *errorbuf, int apply );
+int config_set_SSL3ciphers( const char *attrname, char *value, char *errorbuf, int apply );
+int config_set_localhost( const char *attrname, char *value, char *errorbuf, int apply );
+int config_set_listenhost( const char *attrname, char *value, char *errorbuf, int apply );
+int config_set_securelistenhost( const char *attrname, char *value, char *errorbuf, int apply );
+int config_set_srvtab( const char *attrname, char *value, char *errorbuf, int apply );
+int config_set_sizelimit( const char *attrname, char *value, char *errorbuf, int apply );
+int config_set_lastmod( const char *attrname, char *value, char *errorbuf, int apply );
+int config_set_nagle( const char *attrname, char *value, char *errorbuf, int apply );
+int config_set_accesscontrol( const char *attrname, char *value, char *errorbuf, int apply );
+int config_set_security( const char *attrname, char *value, char *errorbuf, int apply );
+int config_set_readonly( const char *attrname, char *value, char *errorbuf, int apply );
+int config_set_schemacheck( const char *attrname, char *value, char *errorbuf, int apply );
+int config_set_ds4_compatible_schema( const char *attrname, char *value, char *errorbuf, int apply );
+int config_set_schema_ignore_trailing_spaces( const char *attrname, char *value, char *errorbuf, int apply );
+int config_set_rootdn( const char *attrname, char *value, char *errorbuf, int apply );
+int config_set_rootpw( const char *attrname, char *value, char *errorbuf, int apply );
+int config_set_rootpwstoragescheme( const char *attrname, char *value, char *errorbuf, int apply );
+int config_set_workingdir( const char *attrname, char *value, char *errorbuf, int apply );
+int config_set_instancedir( const char *attrname, char *value, char *errorbuf, int apply );
+int config_set_encryptionalias( const char *attrname, char *value, char *errorbuf, int apply );
+int config_set_threadnumber( const char *attrname, char *value, char *errorbuf, int apply );
+int config_set_maxthreadsperconn( const char *attrname, char *value, char *errorbuf, int apply );
+int config_set_reservedescriptors( const char *attrname, char *value, char *errorbuf, int apply );
+int config_set_ioblocktimeout( const char *attrname, char *value, char *errorbuf, int apply );
+int config_set_idletimeout( const char *attrname, char *value, char *errorbuf, int apply );
+int config_set_max_filter_nest_level( const char *attrname, char *value, char *errorbuf, int apply );
+int config_set_groupevalnestlevel( const char *attrname, char *value, char *errorbuf, int apply );
+int config_set_defaultreferral( const char *attrname, struct berval **value, char *errorbuf, int apply );
+int config_set_timelimit(const char *attrname, char *value, char *errorbuf, int apply );
+int config_set_errorlog_level(const char *attrname, char *value, char *errorbuf, int apply );
+int config_set_accesslog_level(const char *attrname, char *value, char *errorbuf, int apply );
+int config_set_auditlog(const char *attrname, char *value, char *errorbuf, int apply );
+int config_set_userat(const char *attrname, char *value, char *errorbuf, int apply );
+int config_set_accesslog(const char *attrname, char *value, char *errorbuf, int apply );
+int config_set_errorlog(const char *attrname, char *value, char *errorbuf, int apply );
+int config_set_pw_change(const char *attrname, char *value, char *errorbuf, int apply );
+int config_set_pw_must_change(const char *attrname, char *value, char *errorbuf, int apply );
+int config_set_pwpolicy_local(const char *attrname, char *value, char *errorbuf, int apply );
+int config_set_pw_syntax(const char *attrname, char *value, char *errorbuf, int apply );
+int config_set_pw_minlength(const char *attrname, char *value, char *errorbuf, int apply );
+int config_set_pw_exp(const char *attrname, char *value, char *errorbuf, int apply );
+int config_set_pw_maxage(const char *attrname, char *value, char *errorbuf, int apply );
+int config_set_pw_minage(const char *attrname, char *value, char *errorbuf, int apply );
+int config_set_pw_warning(const char *attrname, char *value, char *errorbuf, int apply );
+int config_set_pw_history(const char *attrname, char *value, char *errorbuf, int apply );
+int config_set_pw_inhistory(const char *attrname, char *value, char *errorbuf, int apply );
+int config_set_pw_lockout(const char *attrname, char *value, char *errorbuf, int apply );
+int config_set_pw_storagescheme(const char *attrname, char *value, char *errorbuf, int apply );
+int config_set_pw_maxfailure(const char *attrname, char *value, char *errorbuf, int apply );
+int config_set_pw_unlock(const char *attrname, char *value, char *errorbuf, int apply );
+int config_set_pw_lockduration(const char *attrname, char *value, char *errorbuf, int apply );
+int config_set_pw_resetfailurecount(const char *attrname, char *value, char *errorbuf, int apply );
+int config_set_pw_is_global_policy(const char *attrname, char *value, char *errorbuf, int apply );
+int config_set_pw_gracelimit(const char *attrname, char *value, char *errorbuf, int apply );
+int config_set_useroc(const char *attrname, char *value, char *errorbuf, int apply );
+int config_set_return_exact_case(const char *attrname, char *value, char *errorbuf, int apply );
+int config_set_result_tweak(const char *attrname, char *value, char *errorbuf, int apply );
+int config_set_referral_mode(const char *attrname, char *url, char *errorbuf, int apply);
+int config_set_conntablesize(const char *attrname, char *url, char *errorbuf, int apply);
+int config_set_maxbersize(const char *attrname, char *value, char *errorbuf, int apply );
+int config_set_versionstring(const char *attrname, char *versionstring, char *errorbuf, int apply );
+int config_set_enquote_sup_oc(const char *attrname, char *value, char *errorbuf, int apply );
+int config_set_basedn( const char *attrname, char *value, char *errorbuf, int apply );
+int config_set_configdir( const char *attrname, char *value, char *errorbuf, int apply );
+int config_set_attrname_exceptions( const char *attrname, char *value, char *errorbuf, int apply );
+int config_set_hash_filters( const char *attrname, char *value, char *errorbuf, int apply );
+int config_set_rewrite_rfc1274( const char *attrname, char *value, char *errorbuf, int apply );
+int config_set_outbound_ldap_io_timeout( const char *attrname, char *value,
+ char *errorbuf, int apply );
+int config_set_accesslogbuffering(const char *attrname, char *value, char *errorbuf, int apply);
+int config_set_csnlogging(const char *attrname, char *value, char *errorbuf, int apply);
+
+#if !defined(_WIN32) && !defined(AIX)
+int config_set_maxdescriptors( const char *attrname, char *value, char *errorbuf, int apply );
+#endif /* !_WIN_32 && !AIX */
+
+#ifndef _WIN32
+int config_set_localuser( const char *attrname, char *value, char *errorbuf, int apply );
+#endif /* !_WIN32 */
+
+int config_get_SSLclientAuth();
+int config_get_ssl_check_hostname();
+char *config_get_SSL3ciphers();
+char *config_get_localhost();
+char *config_get_listenhost();
+char *config_get_securelistenhost();
+char *config_get_srvtab();
+int config_get_sizelimit();
+char *config_get_pw_storagescheme();
+int config_get_pw_change();
+int config_get_pw_history();
+int config_get_pw_must_change();
+int config_get_pw_syntax();
+int config_get_pw_minlength();
+int config_get_pw_maxfailure();
+int config_get_pw_inhistory();
+long config_get_pw_lockduration();
+long config_get_pw_resetfailurecount();
+int config_get_pw_exp();
+int config_get_pw_unlock();
+int config_get_pw_lockout();
+int config_get_pw_gracelimit();
+int config_get_lastmod();
+int config_get_nagle();
+int config_get_accesscontrol();
+int config_get_return_exact_case();
+int config_get_result_tweak();
+int config_get_security();
+int config_get_schemacheck();
+int config_get_ds4_compatible_schema();
+int config_get_schema_ignore_trailing_spaces();
+char *config_get_rootdn();
+char *config_get_rootpw();
+char *config_get_rootpwstoragescheme();
+#ifndef _WIN32
+char *config_get_localuser();
+#endif /* _WIN32 */
+char *config_get_workingdir();
+char *config_get_instancedir();
+char *config_get_encryptionalias();
+int config_get_threadnumber();
+int config_get_maxthreadsperconn();
+#if !defined(_WIN32) && !defined(AIX)
+int config_get_maxdescriptors();
+#endif /* !_WIN32 && !AIX */
+int config_get_reservedescriptors();
+int config_get_ioblocktimeout();
+int config_get_idletimeout();
+int config_get_max_filter_nest_level();
+int config_get_groupevalnestlevel();
+struct berval **config_get_defaultreferral();
+char *config_get_userat();
+int config_get_timelimit();
+char* config_get_useroc();
+char *config_get_accesslog();
+char *config_get_errorlog();
+char *config_get_auditlog();
+long config_get_pw_maxage();
+long config_get_pw_minage();
+long config_get_pw_warning();
+int config_get_errorlog_level();
+int config_get_accesslog_level();
+char *config_get_referral_mode(void);
+int config_get_conntablesize(void);
+int config_check_referral_mode(void);
+unsigned long config_get_maxbersize();
+char *config_get_versionstring();
+char *config_get_buildnum(void);
+int config_get_enquote_sup_oc();
+char *config_get_basedn();
+char *config_get_configdir();
+char **config_get_errorlog_list();
+char **config_get_accesslog_list();
+char **config_get_auditlog_list();
+int config_get_attrname_exceptions();
+int config_get_hash_filters();
+int config_get_rewrite_rfc1274();
+int config_get_outbound_ldap_io_timeout(void);
+int config_get_csnlogging();
+int is_abspath(const char *);
+char* rel2abspath( char * );
+
+/*
+ * configdse.c
+ */
+int read_config_dse (Slapi_PBlock *pb, Slapi_Entry* e, Slapi_Entry* entryAfter, int *returncode, char *returntext, void *arg);
+int load_config_dse(Slapi_PBlock *pb, Slapi_Entry* e, Slapi_Entry* entryAfter, int *returncode, char *returntext, void *arg);
+int modify_config_dse(Slapi_PBlock *pb, Slapi_Entry* e, Slapi_Entry* entryAfter, int *returncode, char *returntext, void *arg);
+int postop_modify_config_dse(Slapi_PBlock *pb, Slapi_Entry* e, Slapi_Entry* entryAfter, int *returncode, char *returntext, void *arg);
+int add_root_dse( Slapi_PBlock *pb );
+int load_plugin_entry(Slapi_PBlock *pb, Slapi_Entry* e, Slapi_Entry* entryAfter, int *returncode, char *returntext, void *arg);
+
+
+/*
+ * controls.c
+ */
+void init_controls( void );
+int get_ldapmessage_controls( Slapi_PBlock *pb, BerElement *ber,
+ LDAPControl ***controls );
+int write_controls( BerElement *ber, LDAPControl **ctrls );
+void add_control( LDAPControl ***ctrlsp, LDAPControl *newctrl );
+
+
+/*
+ * delete.c
+ */
+void do_delete( Slapi_PBlock *pb );
+
+
+/*
+ * detach.c
+ */
+void detach( void );
+#ifndef _WIN32
+void close_all_files( void );
+#endif
+void raise_process_limits( void );
+
+
+/*
+ * dn.c
+ */
+char *substr_dn_normalize( char *dn, char *end );
+int hexchar2int( char c );
+
+
+/*
+ * dynalib.c
+ */
+void *sym_load( char *libpath, char *symbol, char *plugin, int report_errors );
+
+
+/*
+ * filter.c
+ */
+int get_filter( Connection *conn, BerElement *ber, int scope,
+ struct slapi_filter **filt, char **fstr );
+void filter_print( struct slapi_filter *f );
+void filter_normalize( struct slapi_filter *f );
+
+
+/*
+ * filtercmp.c
+ */
+void filter_compute_hash(struct slapi_filter *f);
+void set_hash_filters(int i);
+
+
+/*
+ * filterentry.c
+ */
+void filter_strcpy_special( char *d, char *s );
+
+
+/*
+ * entry.c
+ */
+int is_rootdse( const char *dn );
+int get_entry_object_type();
+int entry_computed_attr_init();
+void send_referrals_from_entry(Slapi_PBlock *pb, Slapi_Entry *referral);
+
+
+/*
+ * dse.c
+ */
+#define DSE_OPERATION_READ 0x100
+#define DSE_OPERATION_WRITE 0x200
+
+#define DSE_BACKEND "frontend-internal"
+
+#define SLAPI_DSE_RETURNTEXT_SIZE 512 /* for use by callback functions */
+
+struct dse *dse_new( char *filename, char *tmpfilename, char *backfilename, char *startokfilename, const char *configdir);
+struct dse *dse_new_with_filelist(char *filename, char *tmpfilename, char *backfilename, char *startokfilename, const char *configdir, char **filelist);
+int dse_deletedse(Slapi_PBlock *pb);
+int dse_read_file(struct dse *pdse, Slapi_PBlock *pb);
+int dse_bind( Slapi_PBlock *pb );
+int dse_unbind( Slapi_PBlock *pb );
+int dse_search(Slapi_PBlock *pb);
+int dse_modify(Slapi_PBlock *pb);
+int dse_add(Slapi_PBlock *pb);
+int dse_delete(Slapi_PBlock *pb);
+struct dse_callback *dse_register_callback(struct dse* pdse, int operation, int flags, const Slapi_DN *base, int scope, const char *filter, dseCallbackFn fn, void *fn_arg);
+void dse_remove_callback(struct dse* pdse, int operation, int flags, const Slapi_DN *base, int scope, const char *filter, dseCallbackFn fn);
+void dse_set_dont_ever_write_dse_files(void);
+void dse_unset_dont_ever_write_dse_files(void);
+int dse_next_search_entry (Slapi_PBlock *pb);
+char *dse_read_next_entry( char *buf, char **lastp );
+
+
+/*
+ * fedse.c
+ */
+
+int setup_internal_backends();
+
+
+/*
+ * extendedop.c
+ */
+void ldapi_init_extended_ops( void );
+void ldapi_register_extended_op( char **opoids );
+void do_extended( Slapi_PBlock *pb );
+
+
+/*
+ * house.c
+ */
+PRThread* housekeeping_start(time_t cur_time, void *arg);
+void housekeeping_stop();
+
+/*
+ * lock.c
+ */
+FILE * lock_fopen( char *fname, char *type, FILE **lfp );
+int lock_fclose( FILE *fp, FILE *lfp );
+
+
+/*
+ * log.c
+ */
+int slapd_log_error_proc( char *subsystem, char *fmt, ... );
+int slapi_log_access( int level, char *fmt, ... );
+int slapd_log_audit_proc(char *buffer, int buf_len);
+void log_access_flush();
+
+
+int access_log_openf( char *pathname, int locked);
+int error_log_openf( char *pathname, int locked);
+int audit_log_openf( char *pathname, int locked);
+
+void g_set_detached(int);
+void g_log_init(int log_enabled);
+char *g_get_access_log();
+char *g_get_error_log();
+char *g_get_audit_log();
+void g_set_accesslog_level(int val);
+
+int log_set_mode(const char *attrname, char *mode_str, int logtype, char *errorbuf, int apply);
+int log_set_numlogsperdir(const char *attrname, char *numlogs_str, int logtype, char *errorbuf, int apply);
+int log_set_logsize(const char *attrname, char *logsize_str, int logtype, char *errorbuf, int apply);
+int log_set_rotationsync_enabled(const char *attrname, char *rsync_str, int logtype, char *errorbuf, int apply);
+int log_set_rotationsynchour(const char *attrname, char *rhour_str, int logtype, char *errorbuf, int apply);
+int log_set_rotationsyncmin(const char *attrname, char *rmin_str, int logtype, char *errorbuf, int apply);
+int log_set_rotationtime(const char *attrname, char *rtime_str, int logtype, char *errorbuf, int apply);
+int log_set_rotationtimeunit(const char *attrname, char *runit, int logtype, char *errorbuf, int apply);
+int log_set_maxdiskspace(const char *attrname, char *maxdiskspace_str, int logtype, char *errorbuf, int apply);
+int log_set_mindiskspace(const char *attrname, char *minfreespace_str, int logtype, char *errorbuf, int apply);
+int log_set_expirationtime(const char *attrname, char *exptime_str , int logtype, char *errorbuf, int apply);
+int log_set_expirationtimeunit(const char *attrname, char *expunit, int logtype, char *errorbuf, int apply);
+char **log_get_loglist(int logtype);
+int log_update_accesslogdir(char *pathname, int apply);
+int log_update_errorlogdir(char *pathname, int apply);
+int log_update_auditlogdir(char *pathname, int apply);
+int log_set_logging (const char *attrname, char *value, int logtype, char *errorbuf, int apply);
+int check_log_max_size(
+ char *maxdiskspace_str,
+ char *mlogsize_str,
+ int maxdiskspace,
+ int mlogsize,
+ char * returntext,
+ int logtype);
+
+
+void g_set_accesslog_level(int val);
+
+
+/*
+ * util.c
+ */
+void slapd_nasty(char* str, int c, int err);
+int strarray2str( char **a, char *buf, size_t buflen, int include_quotes );
+
+/*
+ * modify.c
+ */
+void do_modify( Slapi_PBlock *pb );
+
+/*
+ * modrdn.c
+ */
+void do_modrdn( Slapi_PBlock *pb );
+
+
+/*
+ * modutil.c
+ */
+int entry_replace_values( Slapi_Entry *e, const char *type, struct berval **vals );
+int entry_apply_mods( Slapi_Entry *e, LDAPMod **mods );
+int entry_apply_mod( Slapi_Entry *e, const LDAPMod *mod );
+void freepmods( LDAPMod **pmods );
+
+
+/*
+ * monitor.c
+ */
+int monitor_info( Slapi_PBlock *pb, Slapi_Entry *entryBefore, Slapi_Entry *entryAfter, int *returncode, char *returntext, void *arg);
+char *slapd_get_version_value( void );
+
+
+
+/*
+ * operation.c
+ */
+Slapi_Operation *operation_new(int flags);
+void operation_free( Slapi_Operation **op, Connection *conn );
+int slapi_op_abandoned( Slapi_PBlock *pb );
+void operation_out_of_disk_space();
+int get_operation_object_type();
+Slapi_DN* operation_get_target_spec (Slapi_Operation *op);
+void operation_set_target_spec (Slapi_Operation *op, const Slapi_DN *target_spec);
+void operation_set_target_spec_str (Slapi_Operation *op, const char *target_spec);
+unsigned long operation_get_abandoned_op (const Slapi_Operation *op);
+void operation_set_abandoned_op (Slapi_Operation *op, unsigned long abndoned_op);
+void operation_set_type(Slapi_Operation *op, unsigned long type);
+
+
+/*
+ * plugin.c
+ */
+int plugin_call_plugins( Slapi_PBlock *, int );
+int plugin_setup(Slapi_Entry *plugin_entry, struct slapi_componentid *group,
+ slapi_plugin_init_fnptr initfunc, int add_to_dit);
+int plugin_call_exop_plugins( Slapi_PBlock *pb, char *oid );
+const char *plugin_extended_op_oid2string( const char *oid );
+void plugin_closeall(int close_backends, int close_globals);
+void plugin_startall(int argc,char **argv,int start_backends, int start_global);
+struct slapdplugin *get_plugin_list(int plugin_list_index);
+PRBool plugin_invoke_plugin_sdn (struct slapdplugin *plugin, int operation, Slapi_PBlock *pb, Slapi_DN *target_spec);
+struct slapdplugin *plugin_get_by_name(char *name);
+struct slapdplugin *plugin_get_pwd_storage_scheme(char *name, int len, int index);
+char *plugin_get_pwd_storage_scheme_list(int index);
+int plugin_add_descriptive_attributes( Slapi_Entry *e,
+ struct slapdplugin *plugin );
+void plugin_call_entryfetch_plugins(char **entrystr, uint *size);
+void plugin_call_entrystore_plugins(char **entrystr, uint *size);
+void plugin_print_versions(void);
+
+/*
+ * plugin_mr.c
+ */
+struct slapdplugin *slapi_get_global_mr_plugins();
+int plugin_mr_filter_create (mr_filter_t* f);
+
+/*
+ * plugin_syntax.c
+ */
+struct slapdplugin *slapi_get_global_syntax_plugins();
+int plugin_call_syntax_filter_ava( const Slapi_Attr *a, int ftype, struct ava *ava );
+int plugin_call_syntax_filter_ava_sv( const Slapi_Attr *a, int ftype, struct ava *ava, Slapi_Value **retVal, int useDeletedValues );
+int plugin_call_syntax_filter_sub( Slapi_Attr *a, struct subfilt *fsub );
+int plugin_call_syntax_filter_sub_sv( Slapi_Attr *a, struct subfilt *fsub );
+int plugin_call_syntax_get_compare_fn(void *vpi, value_compare_fn_type *compare_fn);
+struct slapdplugin *plugin_syntax_find( const char *nameoroid );
+void plugin_syntax_enumerate( SyntaxEnumFunc sef, void *arg );
+char *plugin_syntax2oid( struct slapdplugin *pi );
+
+/*
+ * plugin_acl.c
+ */
+int plugin_call_acl_plugin ( Slapi_PBlock *pb, Slapi_Entry *e,
+ char **attrs, struct berval *val, int access, int flags, char **errbuf);
+int plugin_call_acl_mods_access ( Slapi_PBlock *pb, Slapi_Entry *e, LDAPMod **mods, char **errbuf );
+int plugin_call_acl_mods_update ( Slapi_PBlock *pb, int optype );
+int plugin_call_acl_verify_syntax ( Slapi_PBlock *pb, Slapi_Entry *e, char **errbuf );
+
+/*
+ * pw_mgmt.c
+ */
+void pw_init( void );
+int need_new_pw( Slapi_PBlock *pb, long *t, Slapi_Entry *e, int pwresponse_req );
+int update_pw_info( Slapi_PBlock *pb , char *old_pw );
+int check_pw_syntax( Slapi_PBlock *pb, const Slapi_DN *sdn, Slapi_Value **vals,
+ char **old_pw, Slapi_Entry *e, int mod_op );
+int check_account_lock( Slapi_PBlock *pb, Slapi_Entry * bind_target_entry, int pwresponse_req);
+int check_pw_minage( Slapi_PBlock *pb, const Slapi_DN *sdn, struct berval **vals) ;
+int add_pwd_control( Slapi_PBlock *pb, char *arg, long time );
+void add_password_attrs( Slapi_PBlock *pb, Operation *op, Slapi_Entry *e );
+void mod_allowchange_aci(char *val);
+void pw_mod_allowchange_aci(int pw_prohibit_change);
+void pw_add_allowchange_aci(Slapi_Entry *e, int pw_prohibit_change);
+
+/*
+ * pw_retry.c
+ */
+int update_pw_retry ( Slapi_PBlock *pb );
+void pw_apply_mods(const char *dn, Slapi_Mods *mods);
+void pw_set_componentID(struct slapi_componentid * cid);
+struct slapi_componentid * pw_get_componentID();
+
+/*
+ * referral.c
+ */
+void referrals_free ();
+Ref_Array * ref_array_dup();
+void ref_array_dup_free(Ref_Array *the_copy);
+void update_global_referrals( Slapi_PBlock *pb );
+struct berval **ref_adjust( Slapi_PBlock *pb, struct berval **urls, const Slapi_DN *refcontainerdn, int is_reference );
+/* GGOODREPL temporarily in slapi-plugin.h struct berval **get_data_source( char *dn, int orc, Ref_Array * ); */
+
+
+/*
+ * resourcelimit.c
+ */
+int reslimit_update_from_dn( Slapi_Connection *conn, Slapi_DN *dn );
+int reslimit_update_from_entry( Slapi_Connection *conn, Slapi_Entry *e );
+void reslimit_cleanup( void );
+
+
+/*
+ * result.c
+ */
+void g_set_num_entries_sent( PRUint64 val );
+PRUint64 g_get_num_entries_sent();
+void g_set_num_bytes_sent( PRUint64 val );
+PRUint64 g_get_num_bytes_sent();
+void g_set_num_sent_mutex( PRLock *plock );
+void g_set_default_referral( struct berval **ldap_url );
+struct berval **g_get_default_referral();
+PRLock *g_get_num_sent_mutex();
+void disconnect_server( Connection *conn, int opconnid, int opid, PRErrorCode reason, PRInt32 error );
+int send_ldap_search_entry( Slapi_PBlock *pb, Slapi_Entry *e, LDAPControl **ectrls,
+ char **attrs, int attrsonly );
+void send_ldap_result( Slapi_PBlock *pb, int err, char *matched, char *text,
+ int nentries, struct berval **urls );
+int send_ldap_search_entry_ext( Slapi_PBlock *pb, Slapi_Entry *e, LDAPControl **ectrls,
+ char **attrs, int attrsonly, int send_result, int nentries, struct berval **urls );
+void send_ldap_result_ext( Slapi_PBlock *pb, int err, char *matched, char *text,
+ int nentries, struct berval **urls, BerElement *ber );
+void send_nobackend_ldap_result( Slapi_PBlock *pb );
+int send_ldap_referral( Slapi_PBlock *pb, Slapi_Entry *e, struct berval **refs,
+ struct berval ***urls );
+int send_ldapv3_referral( Slapi_PBlock *pb, struct berval **urls );
+int set_db_default_result_handlers(Slapi_PBlock *pb);
+void disconnect_server_nomutex( Connection *conn, int opconnid, int opid, PRErrorCode reason, PRInt32 error );
+long g_get_current_conn_count();
+void g_increment_current_conn_count();
+void g_decrement_current_conn_count();
+void g_set_current_conn_count_mutex( PRLock *plock );
+PRLock *g_get_current_conn_count_mutex();
+int encode_attr(Slapi_PBlock *pb,BerElement *ber,Slapi_Entry *e,Slapi_Attr *a,int attrsonly,char *type);
+
+
+/*
+ * schema.c
+ */
+int init_schema_dse(const char *configdir);
+char *oc_find_name( const char *name_or_oid );
+int oc_schema_check( Slapi_Entry *e );
+int modify_schema_dse (Slapi_PBlock *pb, Slapi_Entry *entryBefore, Slapi_Entry *entryAfter, int *returncode, char *returntext, void *arg);
+int read_schema_dse ( Slapi_PBlock *pb, Slapi_Entry *entryBefore, Slapi_Entry *entryAfter, int *returncode, char *returntext, void *arg);
+void oc_lock_read( void );
+void oc_lock_write( void );
+void oc_unlock( void );
+/* Note: callers of g_get_global_oc_nolock() must hold a read or write lock */
+struct objclass* g_get_global_oc_nolock();
+/* Note: callers of g_set_global_oc_nolock() must hold a write lock */
+void g_set_global_oc_nolock(struct objclass *newglobaloc);
+/* Note: callers of g_get_global_schema_csn() must hold a read lock */
+const CSN *g_get_global_schema_csn();
+/* Note: callers of g_set_global_schema_csn() must hold a write lock. */
+/* csn is consumed. */
+void g_set_global_schema_csn(CSN *csn);
+void slapi_schema_expand_objectclasses( Slapi_Entry *e );
+
+/*
+ * schemaparse.c
+ */
+void normalize_oc( void );
+/* Note: callers of oc_update_inheritance_nolock() must hold a write lock */
+void oc_update_inheritance_nolock( struct objclass *oc );
+
+/*
+ * search.c
+ */
+void do_search( Slapi_PBlock *pb );
+
+
+/*
+ * ssl.c
+ */
+int slapd_SSL_client_init();
+int slapd_SSL_client_bind_s( LDAP* ld, char* DN, char* pw, int use_SSL, int LDAPv);
+int slapd_sasl_ext_client_bind( LDAP* ld, int **msgid);
+int slapd_nss_init(int init_ssl, int config_available);
+int slapd_ssl_init();
+int slapd_ssl_init2(PRFileDesc **fd, int startTLS);
+int slapd_security_library_is_initialized();
+int slapd_ssl_listener_is_initialized();
+int sasl_io_cleanup(Connection *c);
+
+
+/*
+ * security_wrappers.c
+ */
+int slapd_ssl_handshakeCallback(PRFileDesc *fd, void * callback, void * client_data);
+int slapd_ssl_badCertHook(PRFileDesc *fd, void * callback, void * client_data);
+CERTCertificate * slapd_ssl_peerCertificate(PRFileDesc *fd);
+SECStatus slapd_ssl_getChannelInfo(PRFileDesc *fd, SSLChannelInfo *sinfo, PRUintn len);
+SECStatus slapd_ssl_getCipherSuiteInfo(PRUint16 ciphersuite, SSLCipherSuiteInfo *cinfo, PRUintn len);
+PRFileDesc * slapd_ssl_importFD(PRFileDesc *model, PRFileDesc *fd);
+SECStatus slapd_ssl_resetHandshake(PRFileDesc *fd, PRBool asServer);
+void slapd_pk11_configurePKCS11(char *man, char *libdes, char *tokdes, char *ptokdes,
+ char *slotdes, char *pslotdes, char *fslotdes,
+ char *fpslotdes, int minPwd,
+ int pwdRequired);
+void slapd_pk11_freeSlot(PK11SlotInfo *slot);
+void slapd_pk11_freeSymKey(PK11SymKey *key);
+PK11SlotInfo *slapd_pk11_findSlotByName(char *name);
+SECAlgorithmID *slapd_pk11_createPBEAlgorithmID(SECOidTag algorithm, int iteration, SECItem *salt);
+PK11SymKey *slapd_pk11_pbeKeyGen(PK11SlotInfo *slot, SECAlgorithmID *algid, SECItem *pwitem,
+ PRBool faulty3DES, void *wincx);
+CK_MECHANISM_TYPE slapd_pk11_algtagToMechanism(SECOidTag algTag);
+SECItem *slapd_pk11_paramFromAlgid(SECAlgorithmID *algid);
+CK_RV slapd_pk11_mapPBEMechanismToCryptoMechanism(CK_MECHANISM_PTR pPBEMechanism,
+ CK_MECHANISM_PTR pCryptoMechanism,
+ SECItem *pbe_pwd, PRBool bad3DES);
+int slapd_pk11_getBlockSize(CK_MECHANISM_TYPE type,SECItem *params);
+PK11Context * slapd_pk11_createContextBySymKey(CK_MECHANISM_TYPE type,
+ CK_ATTRIBUTE_TYPE operation,
+ PK11SymKey *symKey, SECItem *param);
+SECStatus slapd_pk11_cipherOp(PK11Context *context, unsigned char * out, int *outlen,
+ int maxout, unsigned char *in, int inlen);
+SECStatus slapd_pk11_finalize(PK11Context *context);
+PK11SlotInfo *slapd_pk11_getInternalKeySlot();
+PK11SlotInfo *slapd_pk11_getInternalSlot();
+SECStatus slapd_pk11_authenticate(PK11SlotInfo *slot, PRBool loadCerts, void *wincx);
+void slapd_pk11_setSlotPWValues(PK11SlotInfo *slot,int askpw, int timeout);
+PRBool slapd_pk11_isFIPS();
+CERTCertificate *slapd_pk11_findCertFromNickname(char *nickname, void *wincx);
+SECKEYPrivateKey *slapd_pk11_findKeyByAnyCert(CERTCertificate *cert, void *wincx);
+PRBool slapd_pk11_fortezzaHasKEA(CERTCertificate *cert);
+void slapd_pk11_destroyContext(PK11Context *context, PRBool freeit);
+void secoid_destroyAlgorithmID(SECAlgorithmID *algid, PRBool freeit);
+void slapd_pk11_CERT_DestroyCertificate(CERTCertificate *cert);
+SECKEYPublicKey *slapd_CERT_ExtractPublicKey(CERTCertificate *cert);
+SECKEYPrivateKey * slapd_pk11_FindPrivateKeyFromCert(PK11SlotInfo *slot,CERTCertificate *cert, void *wincx);
+PK11SlotInfo *slapd_pk11_GetInternalKeySlot(void);
+SECStatus slapd_pk11_PubWrapSymKey(CK_MECHANISM_TYPE type, SECKEYPublicKey *pubKey,PK11SymKey *symKey, SECItem *wrappedKey);
+PK11SymKey *slapd_pk11_KeyGen(PK11SlotInfo *slot,CK_MECHANISM_TYPE type,SECItem *param, int keySize,void *wincx);
+void slapd_pk11_FreeSlot(PK11SlotInfo *slot);
+void slapd_pk11_FreeSymKey(PK11SymKey *key);
+void slapd_pk11_DestroyContext(PK11Context *context, PRBool freeit);
+SECItem *slapd_pk11_ParamFromIV(CK_MECHANISM_TYPE type,SECItem *iv);
+PK11SymKey *slapd_pk11_PubUnwrapSymKey(SECKEYPrivateKey *wrappingKey, SECItem *wrappedKey,CK_MECHANISM_TYPE target, CK_ATTRIBUTE_TYPE operation, int keySize);
+unsigned slapd_SECKEY_PublicKeyStrength(SECKEYPublicKey *pubk);
+SECStatus slapd_pk11_Finalize(PK11Context *context);
+SECStatus slapd_pk11_DigestFinal(PK11Context *context, unsigned char *data,unsigned int *outLen, unsigned int length);
+void slapd_SECITEM_FreeItem (SECItem *zap, PRBool freeit);
+
+/*
+ * start_tls_extop.c
+ */
+int start_tls( Slapi_PBlock *pb );
+int start_tls_graceful_closure( Connection *conn, Slapi_PBlock *pb, int is_initiator );
+int start_tls_register_plugin();
+int start_tls_init( Slapi_PBlock *pb );
+
+/*
+ * slapi_str2filter.c
+ */
+struct slapi_filter *slapi_str2filter( char *str );
+char *slapi_find_matching_paren( const char *str);
+struct slapi_filter *str2simple();
+
+
+/*
+ * time.c
+ */
+char *get_timestring(time_t *t);
+void free_timestring(char *timestr);
+time_t current_time();
+time_t poll_current_time();
+char* format_localTime (time_t from);
+time_t parse_localTime (char* from);
+
+#ifndef HAVE_TIME_R
+int gmtime_r( const time_t *timer, struct tm *result );
+int localtime_r( const time_t *timer, struct tm *result );
+int ctime_r( const time_t *timer, char *buffer, int buflen );
+#endif
+
+char *format_genTime (time_t from);
+time_t parse_genTime (char *from);
+
+/*
+ * unbind.c
+ */
+void do_unbind( Slapi_PBlock *pb );
+
+
+/*
+ * pblock.c
+ */
+void pblock_init( Slapi_PBlock *pb );
+void pblock_init_common( Slapi_PBlock *pb, Slapi_Backend *be, Connection *conn, Operation *op );
+void pblock_done( Slapi_PBlock *pb );
+void bind_credentials_set( Connection *conn,
+ char *authtype, char *normdn,
+ char *extauthtype, char *externaldn, CERTCertificate *clientcert , Slapi_Entry * binded);
+void bind_credentials_clear( Connection *conn, PRBool lock_conn,
+ PRBool clear_externalcreds );
+
+
+/*
+ * libglobs.c
+ */
+void g_set_shutdown( int reason );
+int g_get_shutdown();
+void c_set_shutdown();
+int c_get_shutdown();
+int g_get_global_lastmod();
+/* Ref_Array *g_get_global_referrals(void); */
+struct snmp_vars_t * g_get_global_snmp_vars();
+void FrontendConfig_init();
+int g_get_slapd_security_on();
+void config_set_slapd_type ();
+char *config_get_versionstring();
+
+void libldap_init_debug_level(int *);
+int get_entry_point( int, caddr_t* );
+
+int config_set_entry(Slapi_Entry *e);
+int config_set(const char *attr_name, struct berval **values, char *errorbuf, int apply);
+
+void free_pw_scheme(struct pw_scheme *pwsp);
+
+/*
+ * rootdse.c
+ */
+int read_root_dse( Slapi_PBlock *pb, Slapi_Entry *e, Slapi_Entry *entryAfter, int *returncode, char *returntext, void *arg );
+int modify_root_dse( Slapi_PBlock *pb, Slapi_Entry *e, Slapi_Entry *entryAfter, int *returncode, char *returntext, void *arg );
+
+/*
+ * psearch.c
+ */
+void ps_init_psearch_system();
+void ps_stop_psearch_system();
+void ps_add( Slapi_PBlock *pb, int changetypes, int send_entchg_controls );
+void ps_wakeup_all();
+void ps_service_persistent_searches( Slapi_Entry *e, Slapi_Entry *eprev, int chgtype,
+ int chgnum );
+int ps_parse_control_value( struct berval *psbvp, int *changetypesp,
+ int *changesonlyp, int *returnecsp );
+
+/*
+ * globals.c
+ */
+void set_entry_points();
+
+/*
+ * defbackend.c
+ */
+void defbackend_init( void );
+Slapi_Backend *defbackend_get_backend( void );
+
+/*
+ * plugin_internal_op.c
+ */
+void do_disconnect_server( Connection *conn, int opconnid, int opid );
+
+/*
+ * regex.c
+ */
+int re_init( void );
+
+/*
+ * secpwd.c
+ */
+char* getPassword();
+
+/*
+ * match.c
+ */
+struct matchingRuleList *g_get_global_mrl();
+void g_set_global_mrl(struct matchingRuleList *newglobalmrl);
+
+/*
+ * lite_entries.c
+ */
+void lite_entries_init();
+
+/*
+ * generation.c
+ */
+
+/*
+ * factory.c
+ */
+
+int factory_register_type(const char *name,size_t offset);
+void *factory_create_extension(int type,void *object,void *parent);
+void factory_destroy_extension(int type,void *object,void *parent,void **extension);
+
+/*
+ * auditlog.c
+ */
+
+void write_audit_log_entry( Slapi_PBlock *pb);
+
+/*
+ * eventq.c
+ */
+void eq_init();
+void eq_start();
+void eq_stop();
+
+/*
+ * uniqueidgen.c
+ */
+
+/* Function: uniqueIDGenInit
+ Description: this function initializes the generator
+ Parameters: configDir - directory in which generators state is stored
+ configDN - DIT entry with state information
+ mtGen - indicates whether multiple threads will use generator
+ Return: UID_SUCCESS if function succeeds
+ UID_BADDATA if invalif directory is passed
+ UID_SYSTEM_ERROR if any other failure occurs
+*/
+int uniqueIDGenInit (const char *configDir, const Slapi_DN *configDN, PRBool mtGen);
+
+/* Function: uniqueIDGenCleanup
+ Description: cleanup
+ Parameters: none
+ Return: none
+*/
+void uniqueIDGenCleanup ();
+
+/*
+ * init.c
+ */
+void slapd_init();
+
+/*
+ * plugin.c
+ */
+
+/* this interface is exposed to be used by internal operations. It assumes no dynamic
+ plugin configuration as it provides no data locking. The interface will change once
+ we decide to support dynamic configuration
+ */
+const char* plugin_get_name (const struct slapdplugin *plugin);
+const DataList* plugin_get_target_subtrees (const struct slapdplugin *plugin);
+const DataList* plugin_get_bind_subtrees (const struct slapdplugin *plugin);
+int plugin_get_schema_check (const struct slapdplugin *plugin);
+int plugin_get_log_access (const struct slapdplugin *plugin);
+int plugin_get_log_audit (const struct slapdplugin *plugin);
+
+/*
+ * getfilelist.c
+ */
+char **get_filelist(
+ const char *dirname, /* directory path; if NULL, uses "." */
+ const char *pattern, /* grep (not shell!) file pattern regex */
+ int hiddenfiles, /* if true, return hidden files and directories too */
+ int nofiles, /* if true, do not return files */
+ int nodirs /* if true, do not return directories */
+);
+void free_filelist(char **filelist);
+char **get_priority_filelist(const char *directory, const char *pattern);
+
+/* this interface is exposed to be used by internal operations.
+ */
+char* plugin_get_dn (const struct slapdplugin *plugin);
+/* determine whether operation should be allowed based on plugin configuration */
+PRBool plugin_allow_internal_op (Slapi_DN *target, struct slapdplugin *plugin);
+/* build operation action bitmap based on plugin configuration and actions specified for the operation */
+int plugin_build_operation_action_bitmap (int input_actions, const struct slapdplugin *plugin);
+const struct slapdplugin* plugin_get_server_plg ();
+
+/* opshared.c - functions shared between regular and internal operations */
+void op_shared_search (Slapi_PBlock *pb, int send_result);
+int op_shared_is_allowed_attr (const char *attr_name, int replicated_op);
+void op_shared_log_error_access (Slapi_PBlock *pb, const char *type, const char *dn, const char *msg);
+int search_register_reslimits( void );
+
+
+/* plugin_internal_op.c - functions shared by several internal operations */
+LDAPMod **normalize_mods2bvals(const LDAPMod **mods);
+Slapi_Operation* internal_operation_new(unsigned long op_type, int flags);
+void internal_getresult_callback(struct conn *unused1, struct op *op, int err, char *unused2,
+ char *unused3, int unused4, struct berval **unused5);
+/* allow/disallow operation based of the plugin configuration */
+PRBool allow_operation (Slapi_PBlock *pb);
+/* set operation configuration based on the plugin configuration */
+void set_config_params (Slapi_PBlock *pb);
+/* set parameters common for all internal operations */
+void set_common_params (Slapi_PBlock *pb);
+void do_ps_service(Slapi_Entry *e, Slapi_Entry *eprev, int chgtype, int chgnum);
+void modify_update_last_modified_attr(Slapi_PBlock *pb, Slapi_Mods *smods);
+
+/*
+ * debugdump.cpp
+ */
+void debug_thread_wrapper(void *thread_start_function);
+
+/*
+ * counters.c
+ */
+void counters_as_entry(Slapi_Entry* e);
+void counters_to_errors_log(const char *text);
+
+/*
+ * ch_malloc.c
+ */
+void slapi_ch_start_recording();
+void slapi_ch_stop_recording();
+
+/*
+ * snmpcollator.c
+ */
+void snmp_as_entry(Slapi_Entry* e);
+
+/*
+ * subentry.c
+ */
+int subentry_check_filter(Slapi_Filter *f);
+void subentry_create_filter(Slapi_Filter** filter);
+
+/*
+ * vattr.c
+ */
+void vattr_init();
+void vattr_cleanup();
+void vattrcache_entry_READ_LOCK(const Slapi_Entry *e);
+void vattrcache_entry_READ_UNLOCK(const Slapi_Entry *e);
+void vattrcache_entry_WRITE_LOCK(const Slapi_Entry *e);
+void vattrcache_entry_WRITE_UNLOCK(const Slapi_Entry *e);
+
+/*
+ * slapd_plhash.c - supplement to NSPR plhash
+ */
+void *PL_HashTableLookup_const(
+ void *ht, /* really a PLHashTable */
+ const void *key);
+
+/*
+ * mapping_tree.c
+ */
+int mapping_tree_init();
+void mapping_tree_free ();
+int mapping_tree_get_extension_type ();
+
+/*
+ * connection.c
+ */
+int connection_acquire_nolock (Connection *conn);
+int connection_release_nolock (Connection *conn);
+int connection_is_free (Connection *conn);
+int connection_is_active_nolock (Connection *conn);
+
+/*
+ * bind.c
+ */
+void add_auth_response_control( Slapi_PBlock *pb, const char *binddn );
+
+/*
+ * saslbind.c
+ */
+int ids_sasl_init(void);
+char **ids_sasl_listmech(Slapi_PBlock *pb);
+void ids_sasl_check_bind(Slapi_PBlock *pb);
+void ids_sasl_server_new(Connection *conn);
+
+/*
+ * daemon.c
+ */
+#ifndef LINUX
+void slapd_do_nothing(int);
+#endif
+#ifndef _WIN32
+void slapd_wait4child (int);
+#else
+void *slapd_service_exit_wait();
+#endif
+
+/*
+ * main.c
+ */
+#if ( defined( hpux ) || defined( irix ))
+void * signal2sigaction( int s, void *a );
+#endif
+#endif /* _PROTO_SLAP */
diff --git a/ldap/servers/slapd/psearch.c b/ldap/servers/slapd/psearch.c
new file mode 100644
index 00000000..f1df43a6
--- /dev/null
+++ b/ldap/servers/slapd/psearch.c
@@ -0,0 +1,723 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+/*
+ *
+ * psearch.c - persistent search
+ * August 1997, ggood@netscape.com
+ *
+ * Open issues:
+ * - we increment and decrement active_threads in here. Are there
+ * conditions under which this can prevent a server shutdown?
+ */
+
+#include "slap.h"
+#include "fe.h"
+
+/*
+ * A structure used to create a linked list
+ * of entries being sent by a particular persistent
+ * search result thread.
+ * The ctrl is an "Entry Modify Notification" control
+ * which we may send back with entries.
+ */
+typedef struct _ps_entry_queue_node {
+ Slapi_Entry *pe_entry;
+ LDAPControl *pe_ctrls[2];
+ struct _ps_entry_queue_node *pe_next;
+} PSEQNode;
+
+/*
+ * Information about a single persistent search
+ */
+typedef struct _psearch {
+ Slapi_PBlock *ps_pblock;
+ PRLock *ps_lock;
+ PRThread *ps_tid;
+ PRInt32 ps_complete;
+ PSEQNode *ps_eq_head;
+ PSEQNode *ps_eq_tail;
+ time_t ps_lasttime;
+ int ps_changetypes;
+ int ps_send_entchg_controls;
+ struct _psearch *ps_next;
+} PSearch;
+
+/*
+ * A list of outstanding persistent searches.
+ */
+typedef struct _psearch_list {
+ rwl *pl_rwlock; /* R/W lock struct to serialize access */
+ PSearch *pl_head; /* Head of list */
+ PRLock *pl_cvarlock; /* Lock for cvar */
+ PRCondVar *pl_cvar; /* ps threads sleep on this */
+} PSearch_List;
+
+/*
+ * Convenience macros for locking the list of persistent searches
+ */
+#define PSL_LOCK_READ() psearch_list->pl_rwlock->rwl_acquire_read_lock( psearch_list->pl_rwlock)
+#define PSL_UNLOCK_READ() psearch_list->pl_rwlock->rwl_relinquish_read_lock( psearch_list->pl_rwlock )
+#define PSL_LOCK_WRITE() psearch_list->pl_rwlock->rwl_acquire_write_lock( psearch_list->pl_rwlock )
+#define PSL_UNLOCK_WRITE() psearch_list->pl_rwlock->rwl_relinquish_write_lock( psearch_list->pl_rwlock )
+
+
+/*
+ * Convenience macro for checking if the Persistent Search subsystem has
+ * been initialized.
+ */
+#define PS_IS_INITIALIZED() (psearch_list != NULL)
+
+/* Main list of outstanding persistent searches */
+static PSearch_List *psearch_list = NULL;
+
+/* Forward declarations */
+static void ps_send_results( void *arg );
+static PSearch *psearch_alloc();
+static void ps_add_ps( PSearch *ps );
+static void ps_remove( PSearch *dps );
+static void pe_ch_free( PSEQNode **pe );
+static int create_entrychange_control( int chgtype, int chgnum,
+ const char *prevdn, LDAPControl **ctrlp );
+
+
+/*
+ * Initialize the list structure which contains the list
+ * of outstanding persistent searches. This must be
+ * called early during server startup.
+ */
+void
+ps_init_psearch_system()
+{
+ if ( !PS_IS_INITIALIZED()) {
+ psearch_list = (PSearch_List *) slapi_ch_calloc( 1, sizeof( PSearch_List ));
+ if (( psearch_list->pl_rwlock = rwl_new()) == NULL ) {
+ LDAPDebug( LDAP_DEBUG_ANY, "init_psearch_list: cannot initialize lock structure. "
+ "The server is terminating.\n", 0, 0, 0 );
+ exit( -1 );
+ }
+ if (( psearch_list->pl_cvarlock = PR_NewLock()) == NULL ) {
+ LDAPDebug( LDAP_DEBUG_ANY, "init_psearch_list: cannot create new lock. "
+ "The server is terminating.\n", 0, 0, 0 );
+ exit( -1 );
+ }
+ if (( psearch_list->pl_cvar = PR_NewCondVar( psearch_list->pl_cvarlock )) == NULL ) {
+ LDAPDebug( LDAP_DEBUG_ANY, "init_psearch_list: cannot create new condition variable. "
+ "The server is terminating.\n", 0, 0, 0 );
+ exit( -1 );
+ }
+ psearch_list->pl_head = NULL;
+ }
+}
+
+
+
+
+/*
+ * Close all outstanding persistent searches.
+ * To be used when the server is shutting down.
+ */
+void
+ps_stop_psearch_system()
+{
+ PSearch *ps;
+
+ if ( PS_IS_INITIALIZED()) {
+ PSL_LOCK_WRITE();
+ for ( ps = psearch_list->pl_head; NULL != ps; ps = ps->ps_next ) {
+ PR_AtomicIncrement( &ps->ps_complete );
+ }
+ PSL_UNLOCK_WRITE();
+ ps_wakeup_all();
+ }
+}
+
+
+
+
+/*
+ * Add the given pblock to the list of outstanding persistent searches.
+ * Then, start a thread to send the results to the client as they
+ * are dispatched by add, modify, and modrdn operations.
+ */
+void
+ps_add( Slapi_PBlock *pb, int changetypes, int send_entchg_controls )
+{
+ PSearch *ps;
+
+ if ( PS_IS_INITIALIZED() && NULL != pb ) {
+ /* Create the new node */
+ ps = psearch_alloc();
+ ps->ps_pblock = pb;
+ ps->ps_changetypes = changetypes;
+ ps->ps_send_entchg_controls = send_entchg_controls;
+
+ /* Add it to the head of the list of persistent searches */
+ ps_add_ps( ps );
+
+ /* Start a thread to send the results */
+ ps->ps_tid = PR_CreateThread( PR_USER_THREAD, ps_send_results,
+ (void *) ps, PR_PRIORITY_NORMAL, PR_GLOBAL_THREAD,
+ PR_UNJOINABLE_THREAD, SLAPD_DEFAULT_THREAD_STACKSIZE );
+
+ /* Checking if the thread is succesfully created and
+ * if the thread is not created succesfully.... we send
+ * error messages to the Log file
+ */
+ if(NULL == (ps->ps_tid)){
+ int prerr;
+ prerr = PR_GetError();
+ LDAPDebug(LDAP_DEBUG_ANY,"persistent search PR_CreateThread()failed in the "
+ "ps_add function: " SLAPI_COMPONENT_NAME_NSPR " error %d (%s)\n",
+ prerr, slapd_pr_strerror(prerr), 0);
+
+ /* Now remove the ps from the list so call the function ps_remove */
+ ps_remove(ps);
+ PR_DestroyLock ( ps->ps_lock );
+ ps->ps_lock = NULL;
+ slapi_ch_free((void **) &ps->ps_pblock );
+ slapi_ch_free((void **) &ps );
+ }
+ }
+}
+
+
+
+/*
+ * Remove the given PSearch from the list of outstanding persistent
+ * searches and delete its resources.
+ */
+static void
+ps_remove( PSearch *dps )
+{
+ PSearch *ps;
+
+ if ( PS_IS_INITIALIZED() && NULL != dps ) {
+ PSL_LOCK_WRITE();
+ if ( dps == psearch_list->pl_head ) {
+ /* Remove from head */
+ psearch_list->pl_head = psearch_list->pl_head->ps_next;
+ } else {
+ /* Find and remove from list */
+ ps = psearch_list->pl_head;
+ while ( NULL != ps->ps_next ) {
+ if ( ps->ps_next == dps ) {
+ ps->ps_next = ps->ps_next->ps_next;
+ break;
+ } else {
+ ps = ps->ps_next;
+ }
+ }
+ }
+ PSL_UNLOCK_WRITE();
+ }
+}
+
+/*
+ * Free a persistent search node (and everything it holds).
+ */
+static void
+pe_ch_free( PSEQNode **pe )
+{
+ if ( pe != NULL && *pe != NULL ) {
+ if ( (*pe)->pe_entry != NULL ) {
+ slapi_entry_free( (*pe)->pe_entry );
+ (*pe)->pe_entry = NULL;
+ }
+
+ if ( (*pe)->pe_ctrls[0] != NULL ) {
+ ldap_control_free( (*pe)->pe_ctrls[0] );
+ (*pe)->pe_ctrls[0] = NULL;
+ }
+
+ slapi_ch_free( (void **)pe );
+ }
+}
+
+
+/*
+ * Thread routine for sending search results to a client
+ * which is persistently waiting for them.
+ *
+ * This routine will terminate when either (a) the ps_complete
+ * flag is set, or (b) the associated operation is abandoned.
+ * In any case, the thread won't notice until it wakes from
+ * sleeping on the ps_list condition variable, so it needs
+ * to be awakened.
+ */
+static void
+ps_send_results( void *arg )
+{
+ PSearch *ps = (PSearch *)arg;
+ PSEQNode *peq, *peqnext;
+ struct slapi_filter *filter = 0;
+ char *base = NULL;
+ char *fstr = NULL;
+ char **pbattrs = NULL;
+ int conn_acq_flag = 0;
+
+ PR_AtomicIncrement( &active_threads );
+
+ /* need to acquire a reference to this connection so that it will not
+ be released or cleaned up out from under us */
+ PR_Lock( ps->ps_pblock->pb_conn->c_mutex );
+ conn_acq_flag = connection_acquire_nolock(ps->ps_pblock->pb_conn);
+ PR_Unlock( ps->ps_pblock->pb_conn->c_mutex );
+
+ if (conn_acq_flag) {
+ slapi_log_error(SLAPI_LOG_CONNS, "Persistent Search",
+ "conn=%d op=%d Could not acquire the connection - psearch aborted\n",
+ ps->ps_pblock->pb_conn->c_connid, ps->ps_pblock->pb_op->o_opid);
+ }
+
+ PR_Lock( psearch_list->pl_cvarlock );
+
+ while ( (conn_acq_flag == 0) && !ps->ps_complete ) {
+ /* Check for an abandoned operation */
+ if ( ps->ps_pblock->pb_op == NULL || slapi_op_abandoned( ps->ps_pblock ) ) {
+ slapi_log_error(SLAPI_LOG_CONNS, "Persistent Search",
+ "conn=%d op=%d The operation has been abandoned\n",
+ ps->ps_pblock->pb_conn->c_connid, ps->ps_pblock->pb_op->o_opid);
+ break;
+ }
+ if ( NULL == ps->ps_eq_head ) {
+ /* Nothing to do */
+ PR_WaitCondVar( psearch_list->pl_cvar, PR_INTERVAL_NO_TIMEOUT );
+ } else {
+ /* dequeue the item */
+ int attrsonly;
+ char **attrs;
+ LDAPControl **ectrls;
+ Slapi_Entry *ec;
+ Slapi_Filter *f = NULL;
+
+ PR_Lock( ps->ps_lock );
+
+ peq = ps->ps_eq_head;
+ ps->ps_eq_head = peq->pe_next;
+ if ( NULL == ps->ps_eq_head ) {
+ ps->ps_eq_tail = NULL;
+ }
+
+ PR_Unlock( ps->ps_lock );
+
+ /* Get all the information we need to send the result */
+ ec = peq->pe_entry;
+ slapi_pblock_get( ps->ps_pblock, SLAPI_SEARCH_ATTRS, &attrs );
+ slapi_pblock_get( ps->ps_pblock, SLAPI_SEARCH_ATTRSONLY, &attrsonly );
+ if ( !ps->ps_send_entchg_controls || peq->pe_ctrls[0] == NULL ) {
+ ectrls = NULL;
+ } else {
+ ectrls = peq->pe_ctrls;
+ }
+
+ /*
+ * Send the result. Since send_ldap_search_entry can block for
+ * up to 30 minutes, we relinquish all locks before calling it.
+ */
+ PR_Unlock(psearch_list->pl_cvarlock);
+
+ /*
+ * The entry is in the right scope and matches the filter
+ * but we need to redo the filter test here to check access
+ * controls. See the comments at the slapi_filter_test()
+ * call in ps_service_persistent_searches().
+ */
+ slapi_pblock_get( ps->ps_pblock, SLAPI_SEARCH_FILTER, &f );
+
+ /* See if the entry meets the filter and ACL criteria */
+ if ( slapi_vattr_filter_test( ps->ps_pblock, ec, f,
+ 1 /* verify_access */ ) == 0 ) {
+ int rc = 0;
+ slapi_pblock_set( ps->ps_pblock, SLAPI_SEARCH_RESULT_ENTRY, ec );
+ rc = send_ldap_search_entry( ps->ps_pblock, ec,
+ ectrls, attrs, attrsonly );
+ if (rc) {
+ slapi_log_error(SLAPI_LOG_CONNS, "Persistent Search",
+ "conn=%d op=%d Error %d sending entry %s with op status %d\n",
+ ps->ps_pblock->pb_conn->c_connid, ps->ps_pblock->pb_op->o_opid,
+ rc, slapi_entry_get_dn_const(ec), ps->ps_pblock->pb_op->o_status);
+ }
+ }
+
+ PR_Lock(psearch_list->pl_cvarlock);
+
+ /* Deallocate our wrapper for this entry */
+ pe_ch_free( &peq );
+ }
+ }
+ PR_Unlock( psearch_list->pl_cvarlock );
+ ps_remove( ps );
+
+ /* indicate the end of search */
+ plugin_call_plugins( ps->ps_pblock , SLAPI_PLUGIN_POST_SEARCH_FN );
+
+ /* free things from the pblock that were not free'd in do_search() */
+ /* Free SLAPI_SEARCH_* before deleting op since those are held by op */
+ slapi_pblock_get( ps->ps_pblock, SLAPI_SEARCH_TARGET, &base );
+ slapi_pblock_set( ps->ps_pblock, SLAPI_SEARCH_TARGET, NULL );
+ slapi_ch_free_string(&base);
+
+ slapi_pblock_get( ps->ps_pblock, SLAPI_SEARCH_STRFILTER, &fstr );
+ slapi_pblock_set( ps->ps_pblock, SLAPI_SEARCH_STRFILTER, NULL );
+ slapi_ch_free_string(&fstr);
+
+ slapi_pblock_get( ps->ps_pblock, SLAPI_SEARCH_ATTRS, &pbattrs );
+ slapi_pblock_set( ps->ps_pblock, SLAPI_SEARCH_ATTRS, NULL );
+ if ( pbattrs != NULL )
+ {
+ charray_free( pbattrs );
+ }
+
+ slapi_pblock_get(ps->ps_pblock, SLAPI_SEARCH_FILTER, &filter );
+ slapi_pblock_set(ps->ps_pblock, SLAPI_SEARCH_FILTER, NULL );
+ slapi_filter_free(filter, 1);
+
+ /* Clean up the connection structure */
+ PR_Lock( ps->ps_pblock->pb_conn->c_mutex );
+
+ slapi_log_error(SLAPI_LOG_CONNS, "Persistent Search",
+ "conn=%d op=%d Releasing the connection and operation\n",
+ ps->ps_pblock->pb_conn->c_connid, ps->ps_pblock->pb_op->o_opid);
+ /* Delete this op from the connection's list */
+ connection_remove_operation( ps->ps_pblock->pb_conn, ps->ps_pblock->pb_op );
+ operation_free(&(ps->ps_pblock->pb_op),ps->ps_pblock->pb_conn);
+ ps->ps_pblock->pb_op=NULL;
+
+ /* Decrement the connection refcnt */
+ if (conn_acq_flag == 0) { /* we acquired it, so release it */
+ connection_release_nolock (ps->ps_pblock->pb_conn);
+ }
+ PR_Unlock( ps->ps_pblock->pb_conn->c_mutex );
+
+ PR_DestroyLock ( ps->ps_lock );
+ ps->ps_lock = NULL;
+
+ slapi_ch_free((void **) &ps->ps_pblock );
+ for ( peq = ps->ps_eq_head; peq; peq = peqnext) {
+ peqnext = peq->pe_next;
+ pe_ch_free( &peq );
+ }
+ slapi_ch_free((void **) &ps );
+ PR_AtomicDecrement(&active_threads);
+}
+
+
+
+/*
+ * Allocate and initialize an empty PSearch node.
+ */
+static PSearch *
+psearch_alloc()
+{
+ PSearch *ps;
+
+ ps = (PSearch *) slapi_ch_calloc( 1, sizeof( PSearch ));
+
+ ps->ps_pblock = NULL;
+ if (( ps->ps_lock = PR_NewLock()) == NULL ) {
+ LDAPDebug( LDAP_DEBUG_ANY, "psearch_add: cannot create new lock. "
+ "Persistent search abandoned.\n", 0, 0, 0 );
+ return( NULL );
+ }
+ ps->ps_tid = (PRThread *) NULL;
+ ps->ps_complete = 0;
+ ps->ps_eq_head = ps->ps_eq_tail = (PSEQNode *) NULL;
+ ps->ps_lasttime = (time_t) 0L;
+ ps->ps_next = NULL;
+ return ps;
+}
+
+
+
+/*
+ * Add the given persistent search to the
+ * head of the list of persistent searches.
+ */
+static void
+ps_add_ps( PSearch *ps )
+{
+ if ( PS_IS_INITIALIZED() && NULL != ps ) {
+ PSL_LOCK_WRITE();
+ ps->ps_next = psearch_list->pl_head;
+ psearch_list->pl_head = ps;
+ PSL_UNLOCK_WRITE();
+ }
+}
+
+
+
+/*
+ * Wake up all threads sleeping on
+ * the psearch_list condition variable.
+ */
+void
+ps_wakeup_all()
+{
+ if ( PS_IS_INITIALIZED()) {
+ PR_Lock( psearch_list->pl_cvarlock );
+ PR_NotifyAllCondVar( psearch_list->pl_cvar );
+ PR_Unlock( psearch_list->pl_cvarlock );
+ }
+}
+
+
+/*
+ * Check if there are any persistent searches. If so,
+ * the check to see if the chgtype is one of those the
+ * client is interested in. If so, then check to see if
+ * the entry matches any of the filters the searches.
+ * If so, then enqueue the entry on that persistent search's
+ * ps_entryqueue and signal it to wake up and send the entry.
+ *
+ * Note that if eprev is NULL we assume that the entry's DN
+ * was not changed by the op. that called this function. If
+ * chgnum is 0 it is unknown so we won't ever send it to a
+ * client in the EntryChangeNotification control.
+ */
+void
+ps_service_persistent_searches( Slapi_Entry *e, Slapi_Entry *eprev, int chgtype,
+ int chgnum )
+{
+ LDAPControl *ctrl = NULL;
+ PSearch *ps = NULL;
+ PSEQNode *pe = NULL;
+ int matched = 0;
+ const char *edn;
+
+ if ( !PS_IS_INITIALIZED()) {
+ return;
+ }
+
+ if ( NULL == e ) {
+ /* For now, some backends such as the chaining backend do not provide a post-op entry */
+ return;
+ }
+
+ PSL_LOCK_READ();
+ edn = slapi_entry_get_dn_const(e);
+
+ for ( ps = psearch_list ? psearch_list->pl_head : NULL; NULL != ps; ps = ps->ps_next ) {
+ Slapi_DN base;
+ Slapi_Filter *f;
+ char *basedn;
+ int scope;
+
+ /* Skip the node that doesn't meet the changetype,
+ * or is unable to use the change in ps_send_results()
+ */
+ if (( ps->ps_changetypes & chgtype ) == 0 ||
+ ps->ps_pblock->pb_op == NULL ||
+ slapi_op_abandoned( ps->ps_pblock ) ) {
+ continue;
+ }
+
+ slapi_log_error(SLAPI_LOG_CONNS, "Persistent Search",
+ "conn=%d op=%d entry %s with chgtype %d "
+ "matches the ps changetype %d\n",
+ ps->ps_pblock->pb_conn->c_connid, ps->ps_pblock->pb_op->o_opid,
+ edn, chgtype, ps->ps_changetypes);
+
+ slapi_pblock_get( ps->ps_pblock, SLAPI_SEARCH_FILTER, &f );
+ slapi_pblock_get( ps->ps_pblock, SLAPI_SEARCH_TARGET, &basedn );
+ slapi_pblock_get( ps->ps_pblock, SLAPI_SEARCH_SCOPE, &scope );
+ slapi_sdn_init_dn_byref(&base,basedn);
+
+
+ /*
+ * See if the entry meets the scope and filter criteria.
+ * We cannot do the acl check here as this thread
+ * would then potentially clash with the ps_send_results()
+ * thread on the aclpb in ps->ps_pblock.
+ * By avoiding the acl check in this thread, and leaving all the acl
+ * checking to the ps_send_results() thread we avoid
+ * the ps_pblock contention problem.
+ * The lesson here is "Do not give multiple threads arbitary access
+ * to the same pblock" this kind of muti-threaded access
+ * to the same pblock must be done carefully--there is currently no
+ * generic satisfactory way to do this.
+ */
+ if ( slapi_sdn_scope_test( slapi_entry_get_sdn_const(e), &base, scope ) &&
+ slapi_vattr_filter_test( ps->ps_pblock, e, f, 0 /* verify_access */ ) == 0 ) {
+ PSEQNode *pOldtail;
+
+ /* The scope and the filter match - enqueue it */
+
+ matched++;
+ pe = (PSEQNode *)slapi_ch_calloc( 1, sizeof( PSEQNode ));
+ pe->pe_entry = slapi_entry_dup( e );
+ if ( ps->ps_send_entchg_controls ) {
+ /* create_entrychange_control() is more
+ * expensive than slapi_dup_control()
+ */
+ if ( ctrl == NULL ) {
+ int rc;
+ rc = create_entrychange_control( chgtype, chgnum,
+ eprev ? slapi_entry_get_dn_const(eprev) : NULL,
+ &ctrl );
+ if ( rc != LDAP_SUCCESS ) {
+ char ebuf[ BUFSIZ ];
+ LDAPDebug( LDAP_DEBUG_ANY, "ps_service_persistent_searches:"
+ " unable to create EntryChangeNotification control for"
+ " entry \"%s\" -- control won't be sent.\n",
+ escape_string( slapi_entry_get_dn_const(e), ebuf), 0, 0 );
+ }
+ }
+ if ( ctrl ) {
+ pe->pe_ctrls[0] = slapi_dup_control( ctrl );
+ }
+ }
+
+ /* Put it on the end of the list for this pers search */
+ PR_Lock( ps->ps_lock );
+ pOldtail = ps->ps_eq_tail;
+ ps->ps_eq_tail = pe;
+ if ( NULL == ps->ps_eq_head ) {
+ ps->ps_eq_head = ps->ps_eq_tail;
+ }
+ else {
+ pOldtail->pe_next = ps->ps_eq_tail;
+ }
+ PR_Unlock( ps->ps_lock );
+ }
+ slapi_sdn_done(&base);
+ }
+
+ PSL_UNLOCK_READ();
+
+ /* Were there any matches? */
+ if ( matched ) {
+ ldap_control_free( ctrl );
+ /* Turn 'em loose */
+ ps_wakeup_all();
+ LDAPDebug( LDAP_DEBUG_TRACE, "ps_service_persistent_searches: enqueued entry "
+ "\"%s\" on %d persistent search lists\n", slapi_entry_get_dn_const(e), matched, 0 );
+ } else {
+ LDAPDebug( LDAP_DEBUG_TRACE, "ps_service_persistent_searches: entry "
+ "\"%s\" not enqueued on any persistent search lists\n", slapi_entry_get_dn_const(e), 0, 0 );
+ }
+
+}
+
+/*
+ * Parse the value from an LDAPv3 "Persistent Search" control. They look
+ * like this:
+ *
+ * PersistentSearch ::= SEQUENCE {
+ * changeTypes INTEGER,
+ * -- the changeTypes field is the logical OR of
+ * -- one or more of these values: add (1), delete (2),
+ * -- modify (4), modDN (8). It specifies which types of
+ * -- changes will cause an entry to be returned.
+ * changesOnly BOOLEAN, -- skip initial search?
+ * returnECs BOOLEAN, -- return "Entry Change" controls?
+ * }
+ *
+ * Return an LDAP error code (LDAP_SUCCESS if all goes well).
+ *
+ * This function is standalone; it does not require initialization of
+ * the PS subsystem.
+ */
+int
+ps_parse_control_value( struct berval *psbvp, int *changetypesp, int *changesonlyp, int *returnecsp )
+{
+ int rc= LDAP_SUCCESS;
+ long long_changetypesp;
+
+ if ( psbvp->bv_len == 0 || psbvp->bv_val == NULL )
+ {
+ rc= LDAP_PROTOCOL_ERROR;
+ }
+ else
+ {
+ BerElement *ber= ber_init( psbvp );
+ if ( ber == NULL )
+ {
+ rc= LDAP_OPERATIONS_ERROR;
+ }
+ else
+ {
+ if ( ber_scanf( ber, "{ibb}", &long_changetypesp, changesonlyp, returnecsp ) == LBER_ERROR )
+ {
+ rc= LDAP_PROTOCOL_ERROR;
+ }
+ *changetypesp = (int) long_changetypesp;
+ /* the ber encoding is no longer needed */
+ ber_free(ber,1);
+ }
+ }
+
+ return( rc );
+}
+
+
+/*
+ * Create an LDAPv3 "Entry Change Notification" control. They look like this:
+ *
+ * EntryChangeNotification ::= SEQUENCE {
+ * changeType ENUMERATED {
+ * add (1), -- LDAP_CHANGETYPE_ADD
+ * delete (2), -- LDAP_CHANGETYPE_DELETE
+ * modify (4), -- LDAP_CHANGETYPE_MODIFY
+ * moddn (8), -- LDAP_CHANGETYPE_MODDN
+ * },
+ * previousDN LDAPDN OPTIONAL, -- included for MODDN ops. only
+ * changeNumber INTEGER OPTIONAL, -- included if supported by DSA
+ * }
+ *
+ * This function returns an LDAP error code (LDAP_SUCCESS if all goes well).
+ * The value returned in *ctrlp should be free'd use ldap_control_free().
+ * If chgnum is 0 we omit it from the control.
+ */
+static int
+create_entrychange_control( int chgtype, int chgnum, const char *dn,
+ LDAPControl **ctrlp )
+{
+ int rc;
+ BerElement *ber;
+ struct berval *bvp;
+ const char *prevdn= dn;
+
+ if ( prevdn == NULL ) {
+ prevdn = "";
+ }
+
+ if ( ctrlp == NULL || ( ber = der_alloc()) == NULL ) {
+ return( LDAP_OPERATIONS_ERROR );
+ }
+
+ *ctrlp = NULL;
+
+ if (( rc = ber_printf( ber, "{e", chgtype )) != -1 ) {
+ if ( chgtype == LDAP_CHANGETYPE_MODDN ) {
+ rc = ber_printf( ber, "s", prevdn );
+ }
+ if ( rc != -1 && chgnum != 0 ) {
+ rc = ber_printf( ber, "i", chgnum );
+ }
+ if ( rc != -1 ) {
+ rc = ber_printf( ber, "}" );
+ }
+ }
+
+ if ( rc != -1 ) {
+ rc = ber_flatten( ber, &bvp );
+ }
+ ber_free( ber, 1 );
+
+ if ( rc == -1 ) {
+ return( LDAP_OPERATIONS_ERROR );
+ }
+
+ *ctrlp = (LDAPControl *)slapi_ch_malloc( sizeof( LDAPControl ));
+ (*ctrlp)->ldctl_iscritical = 0;
+ (*ctrlp)->ldctl_oid = slapi_ch_strdup( LDAP_CONTROL_ENTRYCHANGE );
+ (*ctrlp)->ldctl_value = *bvp; /* struct copy */
+
+ bvp->bv_val = NULL;
+ ber_bvfree( bvp );
+
+ return( LDAP_SUCCESS );
+}
diff --git a/ldap/servers/slapd/pw.c b/ldap/servers/slapd/pw.c
new file mode 100644
index 00000000..ab026de7
--- /dev/null
+++ b/ldap/servers/slapd/pw.c
@@ -0,0 +1,1540 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+/*
+ * slapd hashed password routines
+ *
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <sys/types.h>
+#if defined(NET_SSL)
+#include <sechash.h>
+#if defined( _WIN32 )
+#undef DEBUG
+#endif /* _WIN32 */
+#if defined( _WIN32 )
+#undef LDAPDebug
+#endif /* _WIN32 */
+
+#endif /* NET_SSL */
+
+#include "slap.h"
+
+
+#define DENY_PW_CHANGE_ACI "(targetattr = \"userPassword\") ( version 3.0; acl \"disallow_pw_change_aci\"; deny (write ) userdn = \"ldap:///self\";)"
+#define GENERALIZED_TIME_LENGTH 15
+
+static int pw_in_history(Slapi_Value **history_vals, const Slapi_Value *pw_val);
+static int update_pw_history( Slapi_PBlock *pb, char *dn, char *old_pw );
+static int check_trivial_words (Slapi_PBlock *, Slapi_Entry *, Slapi_Value **, char *attrtype );
+static int pw_boolean_str2value (const char *str);
+/* static LDAPMod* pw_malloc_mod (char* name, char* value, int mod_op); */
+
+
+/*
+ * Like slapi_value_find, except for passwords.
+ * returns 0 if password "v" is found in "vals"; non-zero otherwise
+ */
+SLAPI_DEPRECATED int
+slapi_pw_find(
+ struct berval **vals,
+ struct berval *v
+)
+{
+ int rc;
+ Slapi_Value **svin_vals= NULL;
+ Slapi_Value svin_v;
+ slapi_value_init_berval(&svin_v,v);
+ valuearray_init_bervalarray(vals,&svin_vals); /* JCM SLOW FUNCTION */
+ rc= slapi_pw_find_sv(svin_vals,&svin_v);
+ valuearray_free(&svin_vals);
+ value_done(&svin_v);
+ return rc;
+}
+
+/*
+ * Like slapi_value_find, except for passwords.
+ * returns 0 if password "v" is found in "vals"; non-zero otherwise
+ */
+
+int
+slapi_pw_find_sv(
+ Slapi_Value **vals,
+ const Slapi_Value *v
+)
+{
+ struct pw_scheme *pwsp;
+ char *valpwd;
+ int i;
+
+ LDAPDebug( LDAP_DEBUG_TRACE, "=> slapi_pw_find value: \"%s\"\n", slapi_value_get_string(v), 0, 0 ); /* JCM Innards */
+
+ for ( i = 0; vals[i] != NULL; i++ )
+ {
+ pwsp = pw_val2scheme( (char*)slapi_value_get_string(vals[i]), &valpwd, 1 ); /* JCM Innards*/
+ if ( pwsp != NULL &&
+ (*(pwsp->pws_cmp))( (char*)slapi_value_get_string(v), valpwd ) == 0 ) /* JCM Innards*/
+ {
+ LDAPDebug( LDAP_DEBUG_TRACE,
+ "<= slapi_pw_find matched \"%s\" using scheme \"%s\"\n",
+ valpwd, pwsp->pws_name, 0 );
+ free_pw_scheme( pwsp );
+ return( 0 ); /* found it */
+ }
+ free_pw_scheme( pwsp );
+ }
+
+ LDAPDebug( LDAP_DEBUG_TRACE, "<= slapi_pw_find no matching password\n", 0, 0, 0 );
+
+ return( 1 ); /* no match */
+}
+
+/* Checks if the specified value is encoded.
+ Returns 1 if it is and 0 otherwise
+ */
+int slapi_is_encoded (char *value)
+{
+ struct pw_scheme *is_hashed = NULL;
+ int is_encoded = 0;
+
+ is_hashed = pw_val2scheme ( value, NULL, 0 );
+ if ( is_hashed != NULL )
+ {
+ free_pw_scheme( is_hashed );
+ is_encoded = 1;
+ }
+ return (is_encoded);
+}
+
+
+char* slapi_encode (char *value, char *alg)
+{
+ struct pw_scheme *enc_scheme = NULL;
+ char *hashedval = NULL;
+ int need_to_free = 0;
+
+ if (alg == NULL) /* use server encoding scheme */
+ {
+ slapdFrontendConfig_t * slapdFrontendConfig = getFrontendConfig();
+
+ enc_scheme = slapdFrontendConfig->pw_storagescheme;
+
+ if (enc_scheme == NULL)
+ {
+ slapi_log_error( SLAPI_LOG_FATAL, NULL,
+ "slapi_encode: no server scheme\n" );
+ return NULL;
+ }
+ }
+ else
+ {
+ enc_scheme = pw_name2scheme(alg);
+ if ( enc_scheme == NULL)
+ {
+ char * scheme_list = plugin_get_pwd_storage_scheme_list(PLUGIN_LIST_PWD_STORAGE_SCHEME);
+ if ( scheme_list != NULL ) {
+ slapi_log_error( SLAPI_LOG_FATAL, NULL,
+ "slapi_encode: invalid scheme - %s\n"
+ "Valid values are: %s\n", alg, scheme_list );
+ slapi_ch_free((void **)&scheme_list);
+ } else {
+ slapi_log_error( SLAPI_LOG_FATAL, NULL,
+ "slapi_encode: invalid scheme - %s\n"
+ "no pwdstorage scheme plugin loaded", alg);
+ }
+ return NULL;
+ }
+ need_to_free = 1;
+ }
+
+ hashedval = enc_scheme->pws_enc(value);
+ if (need_to_free)
+ free_pw_scheme(enc_scheme);
+
+ return hashedval;
+}
+
+/*
+ * Return a pointer to the pw_scheme struct for scheme "name"
+ * NULL is returned is no matching scheme is found.
+ */
+
+struct pw_scheme *
+pw_name2scheme( char *name )
+{
+ struct pw_scheme *pwsp;
+ struct slapdplugin *p;
+
+ if ( (p = plugin_get_pwd_storage_scheme(name, strlen(name), PLUGIN_LIST_PWD_STORAGE_SCHEME)) != NULL ) {
+ pwsp = (struct pw_scheme *) slapi_ch_malloc (sizeof(struct pw_scheme));
+ if ( pwsp != NULL ) {
+ typedef int (*CMPFP)(char *, char *);
+ typedef char * (*ENCFP)(char *);
+ pwsp->pws_name = slapi_ch_strdup( p->plg_pwdstorageschemename );
+ pwsp->pws_cmp = (CMPFP)p->plg_pwdstorageschemecmp;
+ pwsp->pws_enc = (ENCFP)p->plg_pwdstorageschemeenc;
+ pwsp->pws_len = strlen(pwsp->pws_name) ;
+ return(pwsp);
+ }
+ }
+
+ return( NULL );
+}
+
+void free_pw_scheme(struct pw_scheme *pwsp)
+{
+ if ( pwsp != NULL )
+ {
+ slapi_ch_free( (void**)&pwsp->pws_name );
+ slapi_ch_free( (void**)&pwsp );
+ }
+}
+
+/*
+ * Return the password scheme for value "val". This is determined by
+ * checking "val" against our scheme prefixes.
+ *
+ * If "valpwdp" is not NULL, it is set to point to the value with any
+ * prefix removed.
+ *
+ * If no matching scheme is found and first_is_default is non-zero, the
+ * first scheme is returned. If no matching scheme is found and
+ * first_is_default is zero, NULL is returned.
+ */
+
+struct pw_scheme *
+pw_val2scheme( char *val, char **valpwdp, int first_is_default )
+{
+ struct pw_scheme *pwsp;
+ int namelen, prefixlen;
+ char *end, buf[ PWD_MAX_NAME_LEN + 1 ];
+
+ if ( *val != PWD_HASH_PREFIX_START ||
+ ( end = strchr( val, PWD_HASH_PREFIX_END )) == NULL ||
+ ( namelen = end - val - 1 ) > PWD_MAX_NAME_LEN ) {
+ if ( !first_is_default ) {
+ return( NULL );
+ }
+ pwsp = pw_name2scheme("CLEAR"); /* default to first scheme */
+ prefixlen = 0;
+ } else {
+ memcpy( buf, val + 1, namelen );
+ buf[ namelen ] = '\0';
+ pwsp = pw_name2scheme(buf);
+ if ( pwsp == NULL ) {
+ if ( !first_is_default ) {
+ return( NULL );
+ }
+ pwsp = pw_name2scheme("CLEAR");
+ prefixlen = 0;
+ } else {
+ prefixlen = pwsp->pws_len + 2;
+ }
+ }
+
+ if ( valpwdp != NULL ) {
+ *valpwdp = val + prefixlen;
+ }
+
+ return( pwsp );
+}
+
+
+/*
+ * re-encode the password values in "vals" using a hashing algorithm
+ * vals[n] is assumed to be an alloc'd Slapi_Value that can be free'd and
+ * replaced. If a value is already encoded, we do not re-encode it.
+ * Return 0 if all goes well and < 0 if an error occurs.
+ */
+
+int
+pw_encodevals( Slapi_Value **vals )
+{
+ int i;
+ char *enc;
+ slapdFrontendConfig_t * slapdFrontendConfig = getFrontendConfig();
+
+
+ if ( vals == NULL || slapdFrontendConfig->pw_storagescheme == NULL ||
+ slapdFrontendConfig->pw_storagescheme->pws_enc == NULL ) {
+ return( 0 );
+ }
+
+ for ( i = 0; vals[ i ] != NULL; ++i ) {
+ struct pw_scheme *pwsp;
+ if ( (pwsp=pw_val2scheme( (char*)slapi_value_get_string(vals[ i ]), NULL, 0)) != NULL ) { /* JCM Innards */
+ free_pw_scheme( pwsp );
+ continue; /* don't touch pre-encoded values */
+ }
+ if (( enc = (*slapdFrontendConfig->pw_storagescheme->pws_enc)( (char*)slapi_value_get_string(vals[ i ]) )) /* JCM Innards */
+ == NULL ) {
+ free_pw_scheme( pwsp );
+ return( -1 );
+ }
+ slapi_value_free(&vals[ i ]);
+ vals[ i ] = slapi_value_new_string_passin(enc);
+ free_pw_scheme( pwsp );
+ }
+
+ return( 0 );
+}
+
+/*
+ * Check if the prefix of the cipher is the one that is supposed to be
+ * Extract from the whole cipher the encrypted password (remove the prefix)
+ */
+int checkPrefix(char *cipher, char *schemaName, char **encrypt)
+{
+ int namelen;
+ /* buf contains the extracted schema name */
+ char *end, buf[ 3*PWD_MAX_NAME_LEN + 1 ];
+
+ if ( (*cipher == PWD_HASH_PREFIX_START) &&
+ ((end = strchr(cipher, PWD_HASH_PREFIX_END)) != NULL) &&
+ ((namelen = end - cipher - 1 ) <= (3*PWD_MAX_NAME_LEN)) )
+ {
+ memcpy( buf, cipher + 1, namelen );
+ buf[ namelen ] = '\0';
+ if ( strcasecmp( buf, schemaName) != 0 )
+ {
+ /* schema names are different, error */
+ return 1;
+ }
+ else
+ {
+ /* extract the encrypted password */
+ *encrypt = cipher + strlen(schemaName) + 2;
+ return 0;
+ }
+ }
+ /* cipher is not prefixed, already in clear ? */
+ return -1;
+}
+
+/*
+* Decode the attribute "attr_name" with one of the reversible encryption mechanism
+* Returns -1 on error
+* Returns 0 on success with strdup'ed plain
+* Returns 1 on success with *plain=cipher
+*/
+int
+pw_rever_decode(char *cipher, char **plain, const char * attr_name)
+{
+ char *dec = NULL;
+ struct pw_scheme *pwsp = NULL;
+ struct slapdplugin *p = NULL;
+
+ int ret_code = 1;
+
+ for ( p = get_plugin_list(PLUGIN_LIST_REVER_PWD_STORAGE_SCHEME); p != NULL; p = p->plg_next )
+ {
+ char *L_attr = NULL;
+ int i = 0;
+ char *encrypt = NULL;
+ int prefixOK = -1;
+
+ /* Get the appropriate decoding function */
+ for ( L_attr = p->plg_argv[i]; i<p->plg_argc; L_attr = p->plg_argv[++i] )
+ {
+ if (slapi_attr_types_equivalent(L_attr, attr_name))
+ {
+ typedef int (*CMPFP)(char *, char *);
+ typedef char * (*ENCFP)(char *);
+
+ pwsp = (struct pw_scheme *) slapi_ch_calloc (1, sizeof(struct pw_scheme));
+
+ pwsp->pws_dec = (ENCFP)p->plg_pwdstorageschemedec;
+ pwsp->pws_name = slapi_ch_strdup( p->plg_pwdstorageschemename );
+ pwsp->pws_len = strlen(pwsp->pws_name) ;
+ if ( pwsp->pws_dec != NULL )
+ {
+ /* check that the prefix of the cipher is the same name
+ as the schema name */
+ prefixOK = checkPrefix(cipher, pwsp->pws_name, &encrypt);
+ if ( prefixOK == -1 )
+ {
+ /* no prefix, already in clear ? */
+ *plain = cipher;
+ ret_code = 1;
+ goto free_and_return;
+ }
+ else if ( prefixOK == 1 )
+ {
+ /* schema names are different */
+ ret_code = -1;
+ goto free_and_return;
+ }
+ else
+ {
+ if ( ( *plain = (pwsp->pws_dec)( encrypt )) == NULL )
+ {
+ /* pb during decoding */
+ ret_code = -1;
+ goto free_and_return;
+ }
+ /* decoding is OK */
+ ret_code = 0;
+ goto free_and_return;
+ }
+ }
+ free_pw_scheme( pwsp );
+ pwsp = NULL;
+ }
+ }
+ }
+free_and_return:
+ if ( pwsp != NULL )
+ {
+ free_pw_scheme( pwsp );
+ }
+ return(ret_code);
+}
+
+/*
+ * Encode the attribute "attr_name" with one of the reversible encryption mechanism
+ */
+int
+pw_rever_encode(Slapi_Value **vals, char * attr_name)
+{
+ char *enc;
+ struct pw_scheme *pwsp = NULL;
+ struct slapdplugin *p;
+
+ if (vals == NULL){
+ return (0);
+ }
+
+ for ( p = get_plugin_list(PLUGIN_LIST_REVER_PWD_STORAGE_SCHEME); p != NULL; p = p->plg_next )
+ {
+ char *L_attr = NULL;
+ int i = 0;
+
+ /* Get the appropriate encoding function */
+ for ( L_attr = p->plg_argv[i]; i<p->plg_argc; L_attr = p->plg_argv[++i] )
+ {
+ if (slapi_attr_types_equivalent(L_attr, attr_name))
+ {
+ typedef int (*CMPFP)(char *, char *);
+ typedef char * (*ENCFP)(char *);
+
+ pwsp = (struct pw_scheme *) slapi_ch_calloc (1, sizeof(struct pw_scheme));
+
+ pwsp->pws_enc = (ENCFP)p->plg_pwdstorageschemeenc;
+ pwsp->pws_name = slapi_ch_strdup( p->plg_pwdstorageschemename );
+ if ( pwsp->pws_enc != NULL )
+ {
+ for ( i = 0; vals[i] != NULL; ++i )
+ {
+ char *encrypt = NULL;
+ int prefixOK;
+
+ prefixOK = checkPrefix((char*)slapi_value_get_string(vals[i]),
+ pwsp->pws_name,
+ &encrypt);
+ if ( prefixOK == 0 )
+ {
+ /* Don't touch already encoded value */
+ continue; /* don't touch pre-encoded values */
+ }
+ else if (prefixOK == 1 )
+ {
+ /* credential is already encoded, but not with this schema. Error */
+ free_pw_scheme( pwsp );
+ return( -1 );
+ }
+
+
+ if ( ( enc = (pwsp->pws_enc)( (char*)slapi_value_get_string(vals[ i ]) )) == NULL )
+ {
+ free_pw_scheme( pwsp );
+ return( -1 );
+ }
+ slapi_value_free(&vals[ i ]);
+ vals[ i ] = slapi_value_new_string_passin(enc);
+ free_pw_scheme( pwsp );
+ return (0);
+ }
+ }
+ }
+ }
+ }
+
+ free_pw_scheme( pwsp );
+ return(-1);
+}
+
+/* ONREPL - below are the functions moved from pw_mgmt.c.
+ this is done to allow the functions to be used
+ by functions linked into libslapd.
+ */
+
+/* update_pw_info is called after password is modified successfully */
+/* it should update passwordHistory, and passwordExpirationTime */
+/* SLAPI_ENTRY_POST_OP must be set */
+
+int
+update_pw_info ( Slapi_PBlock *pb , char *old_pw) {
+
+ Slapi_Mods smods;
+ char *timestr;
+ Slapi_PBlock *mod_result_pb = NULL;
+ time_t pw_exp_date;
+ time_t cur_time;
+ char *dn;
+ passwdPolicy *pwpolicy = NULL;
+
+ cur_time = current_time();
+ slapi_pblock_get( pb, SLAPI_TARGET_DN, &dn );
+
+ pwpolicy = new_passwdPolicy(pb, dn);
+
+ /* update passwordHistory */
+ if ( old_pw != NULL && pwpolicy->pw_history == 1 ) {
+ update_pw_history(pb, dn, old_pw);
+ slapi_ch_free ( (void**)&old_pw );
+ }
+
+ slapi_mods_init(&smods, 0);
+
+ /* update password allow change time */
+ if ( pwpolicy->pw_minage != 0) {
+ timestr = format_genTime( time_plus_sec( cur_time,
+ pwpolicy->pw_minage ));
+ slapi_mods_add_string(&smods, LDAP_MOD_REPLACE, "passwordAllowChangeTime", timestr);
+ slapi_ch_free((void **)&timestr);
+ }
+
+ /* Fix for Bug 560707
+ Removed the restriction that the lock variables (retry count) will
+ be set only when root resets the passwd.
+ Now admins will also have these privileges.
+ */
+ if (pwpolicy->pw_lockout) {
+ set_retry_cnt_mods (pb, &smods, 0 );
+ }
+
+ /* Clear the passwordgraceusertime from the user entry */
+ slapi_mods_add_string(&smods, LDAP_MOD_REPLACE, "passwordgraceusertime", "0");
+
+ /* if the password is reset by root, mark it the first time logon */
+
+ if ( pb->pb_requestor_isroot == 1 &&
+ pwpolicy->pw_must_change){
+ pw_exp_date = NO_TIME;
+
+ } else if ( pwpolicy->pw_exp == 1 ) {
+ Slapi_Entry *pse = NULL;
+
+ /* update password expiration date */
+ pw_exp_date = time_plus_sec ( cur_time,
+ pwpolicy->pw_maxage );
+
+ slapi_pblock_get(pb,SLAPI_ENTRY_POST_OP,&pse);
+
+ if (pse) {
+ char *prev_exp_date_str;
+
+ /* if the password expiry time is SLAPD_END_TIME,
+ * don't roll it back
+ */
+ prev_exp_date_str = slapi_entry_attr_get_charptr(pse,"passwordExpirationTime");
+
+ if (prev_exp_date_str) {
+ time_t prev_exp_date;
+
+ prev_exp_date = parse_genTime(prev_exp_date_str);
+
+ if (prev_exp_date == NO_TIME ||
+ prev_exp_date == NOT_FIRST_TIME) {
+ /* ignore as will replace */
+ } else if (prev_exp_date == SLAPD_END_TIME) {
+ /* Special entries' passwords never expire */
+ slapi_ch_free((void**)&prev_exp_date_str);
+ pw_apply_mods(dn, &smods);
+ slapi_mods_done(&smods);
+ delete_passwdPolicy(&pwpolicy);
+ return 0;
+ }
+
+ slapi_ch_free((void**)&prev_exp_date_str);
+ }
+ } /* post op entry */
+
+ } else if (pwpolicy->pw_must_change) {
+ /*
+ * pw is not changed by root, and must change pw first time
+ * log on
+ */
+ pw_exp_date = NOT_FIRST_TIME;
+ } else {
+ pw_apply_mods(dn, &smods);
+ slapi_mods_done(&smods);
+ delete_passwdPolicy(&pwpolicy);
+ return 0;
+ }
+
+ delete_passwdPolicy(&pwpolicy);
+
+ timestr = format_genTime ( pw_exp_date );
+ slapi_mods_add_string(&smods, LDAP_MOD_REPLACE, "passwordExpirationTime", timestr);
+ slapi_ch_free((void **)&timestr);
+
+ slapi_mods_add_string(&smods, LDAP_MOD_REPLACE, "passwordExpWarned", "0");
+
+ pw_apply_mods(dn, &smods);
+ slapi_mods_done(&smods);
+ /* reset c_needpw to 0 */
+ pb->pb_conn->c_needpw = 0;
+ return 0;
+}
+
+int
+check_pw_minage ( Slapi_PBlock *pb, const Slapi_DN *sdn, struct berval **vals)
+{
+ char *dn= (char*)slapi_sdn_get_ndn(sdn); /* jcm - Had to cast away const */
+ passwdPolicy *pwpolicy=NULL;
+ int pwresponse_req = 0;
+
+ pwpolicy = new_passwdPolicy(pb, dn);
+ slapi_pblock_get ( pb, SLAPI_PWPOLICY, &pwresponse_req );
+
+ if ( !pb->pb_op->o_isroot &&
+ pwpolicy->pw_minage != 0 ) {
+
+ Slapi_Entry *e;
+ char *passwordAllowChangeTime;
+
+ /* retrieve the entry */
+ e = get_entry ( pb, dn );
+ if ( e == NULL ) {
+ delete_passwdPolicy(&pwpolicy);
+ return ( -1 );
+ }
+ /* get passwordAllowChangeTime attribute */
+ passwordAllowChangeTime= slapi_entry_attr_get_charptr(e, "passwordAllowChangeTime");
+
+ if (passwordAllowChangeTime!=NULL)
+ {
+ time_t pw_allowchange_date;
+ char *cur_time_str = NULL;
+
+ pw_allowchange_date = parse_genTime(passwordAllowChangeTime);
+ slapi_ch_free((void **) &passwordAllowChangeTime );
+
+ /* check if allow to change the password */
+ cur_time_str = format_genTime ( current_time() );
+ if ( difftime ( pw_allowchange_date,
+ parse_genTime ( cur_time_str )) > 0 )
+ {
+ if ( pwresponse_req == 1 ) {
+ pwpolicy_make_response_control ( pb, -1, -1,
+ LDAP_PWPOLICY_PWDTOOYOUNG );
+ }
+ send_ldap_result ( pb,
+ LDAP_CONSTRAINT_VIOLATION, NULL,
+ "within password minimum age", 0, NULL );
+ slapi_entry_free( e );
+ slapi_ch_free((void **) &cur_time_str );
+ delete_passwdPolicy(&pwpolicy);
+ return ( 1 );
+ }
+ slapi_ch_free((void **) &cur_time_str );
+ }
+ slapi_entry_free( e );
+ }
+ delete_passwdPolicy(&pwpolicy);
+ return ( 0 );
+}
+
+/* check_pw_syntax is called before add or modify operation on userpassword attribute*/
+
+int
+check_pw_syntax ( Slapi_PBlock *pb, const Slapi_DN *sdn, Slapi_Value **vals,
+ char **old_pw, Slapi_Entry *e, int mod_op)
+{
+ Slapi_Attr* attr;
+ int i, pwresponse_req = 0;
+ char *dn= (char*)slapi_sdn_get_ndn(sdn); /* jcm - Had to cast away const */
+ passwdPolicy *pwpolicy = NULL;
+
+ pwpolicy = new_passwdPolicy(pb, dn);
+ slapi_pblock_get ( pb, SLAPI_PWPOLICY, &pwresponse_req );
+
+ if ( pwpolicy->pw_syntax == 1 ) {
+ /* check for the minimum password lenght */
+ for ( i = 0; vals[ i ] != NULL; ++i ) {
+ if ( pwpolicy->pw_minlength
+ > (int)slapi_value_get_length(vals[ i ]) ) { /* jcm: had to cast unsigned int to signed int */
+ if ( pwresponse_req == 1 ) {
+ pwpolicy_make_response_control ( pb, -1, -1,
+ LDAP_PWPOLICY_PWDTOOSHORT );
+ }
+ send_ldap_result ( pb,
+ LDAP_CONSTRAINT_VIOLATION, NULL,
+ "invalid password syntax", 0, NULL );
+ delete_passwdPolicy(&pwpolicy);
+ return ( 1 );
+ }
+ }
+ }
+
+ /* get the entry and check for the password history and syntax if this is called by a modify operation */
+ if ( mod_op ) {
+ /* retrieve the entry */
+ e = get_entry ( pb, dn );
+ if ( e == NULL ) {
+ delete_passwdPolicy(&pwpolicy);
+ return ( -1 );
+ }
+
+ /* check for password history */
+ if ( pwpolicy->pw_history == 1 ) {
+ attr = attrlist_find(e->e_attrs, "passwordHistory");
+ if (attr &&
+ !valueset_isempty(&attr->a_present_values))
+ {
+ Slapi_Value **va= attr_get_present_values(attr);
+ if ( pw_in_history( va, vals[0] ) == 0 ) {
+ if ( pwresponse_req == 1 ) {
+ pwpolicy_make_response_control ( pb, -1, -1,
+ LDAP_PWPOLICY_PWDINHISTORY );
+ }
+ send_ldap_result ( pb,
+ LDAP_CONSTRAINT_VIOLATION, NULL,
+ "password in history", 0, NULL );
+ slapi_entry_free( e );
+ delete_passwdPolicy(&pwpolicy);
+ return ( 1 );
+ }
+ }
+
+ /* get current password. check it and remember it */
+ attr = attrlist_find(e->e_attrs, "userpassword");
+ if (attr && !valueset_isempty(&attr->a_present_values))
+ {
+ Slapi_Value **va= valueset_get_valuearray(&attr->a_present_values);
+ if (slapi_is_encoded((char*)slapi_value_get_string(vals[0])))
+ {
+ if (slapi_attr_value_find(attr, (struct berval *)slapi_value_get_berval(vals[0])) == 0 )
+ {
+ send_ldap_result ( pb,
+ LDAP_CONSTRAINT_VIOLATION ,NULL,
+ "password in history", 0, NULL);
+ slapi_entry_free( e );
+ delete_passwdPolicy(&pwpolicy);
+ return ( 1 );
+ }
+ } else
+ {
+ if ( slapi_pw_find_sv ( va, vals[0] ) == 0 )
+ {
+ send_ldap_result ( pb,
+ LDAP_CONSTRAINT_VIOLATION ,NULL,
+ "password in history", 0, NULL);
+ slapi_entry_free( e );
+ delete_passwdPolicy(&pwpolicy);
+ return ( 1 );
+ }
+ }
+ /* We copy the 1st value of the userpassword attribute.
+ * This is because password policy assumes that there's only one
+ * password in the userpassword attribute.
+ */
+ *old_pw = slapi_ch_strdup(slapi_value_get_string(va[0]));
+ } else {
+ *old_pw = NULL;
+ }
+ }
+
+ if ( pwpolicy->pw_syntax == 1 ) {
+ /* check for trivial words */
+ if ( check_trivial_words ( pb, e, vals, "uid" ) == 1 ||
+ check_trivial_words ( pb, e, vals, "cn" ) == 1 ||
+ check_trivial_words ( pb, e, vals, "sn" ) == 1 ||
+ check_trivial_words ( pb, e, vals, "givenname" ) == 1 ||
+ check_trivial_words ( pb, e, vals, "ou" ) == 1 ||
+ check_trivial_words ( pb, e, vals, "mail" ) == 1) {
+ slapi_entry_free( e );
+ delete_passwdPolicy(&pwpolicy);
+ return 1;
+ }
+ }
+ }
+
+ delete_passwdPolicy(&pwpolicy);
+
+ if ( mod_op ) {
+ /* free e only when called by modify operation */
+ slapi_entry_free( e );
+ }
+ return 0; /* success */
+
+}
+
+static
+int update_pw_history( Slapi_PBlock *pb, char *dn, char *old_pw ) {
+
+ time_t t, old_t, cur_time;
+ int i = 0, oldest = 0;
+ int res;
+ Slapi_Entry *e;
+ Slapi_Attr *attr;
+ LDAPMod attribute;
+ char *values_replace[25]; /* 2-24 passwords in history */
+ LDAPMod *list_of_mods[2];
+ Slapi_PBlock mod_pb;
+ char *history_str;
+ char *str;
+ passwdPolicy *pwpolicy = NULL;
+
+ pwpolicy = new_passwdPolicy(pb, dn);
+
+ /* retrieve the entry */
+ e = get_entry ( pb, dn );
+ if ( e == NULL ) {
+ delete_passwdPolicy(&pwpolicy);
+ return ( 1 );
+ }
+
+ history_str = (char *)slapi_ch_malloc(GENERALIZED_TIME_LENGTH + strlen(old_pw) + 1);
+ /* get password history, and find the oldest password in history */
+ cur_time = current_time ();
+ old_t = cur_time;
+ str = format_genTime ( cur_time );
+ attr = attrlist_find(e->e_attrs, "passwordHistory");
+ if (attr && !valueset_isempty(&attr->a_present_values))
+ {
+ Slapi_Value **va= valueset_get_valuearray(&attr->a_present_values);
+ for ( i = oldest = 0 ;
+ (va[i] != NULL) && (slapi_value_get_length(va[i]) > 0) ;
+ i++ ) {
+
+ values_replace[i] = (char*)slapi_value_get_string(va[i]);
+ strncpy( history_str, values_replace[i], GENERALIZED_TIME_LENGTH);
+ history_str[GENERALIZED_TIME_LENGTH] = '\0';
+ if (history_str[GENERALIZED_TIME_LENGTH - 1] != 'Z'){
+ /* The time is not a generalized Time. Probably a password history from 4.x */
+ history_str[GENERALIZED_TIME_LENGTH - 1] = '\0';
+ }
+ t = parse_genTime ( history_str );
+ if ( difftime ( t, old_t ) < 0 ) {
+ oldest = i;
+ old_t = t;
+ }
+ }
+ }
+ strcpy ( history_str, str );
+ strcat ( history_str, old_pw );
+ if ( i == pwpolicy->pw_inhistory ) {
+ /* replace the oldest password in history */
+ values_replace [oldest] = history_str;
+ values_replace[i]=NULL;
+ } else {
+ /* add old_pw at the end of password history */
+ values_replace[i] = history_str;
+ values_replace[++i]=NULL;
+ }
+
+ /* modify the attribute */
+ attribute.mod_type = "passwordHistory";
+ attribute.mod_op = LDAP_MOD_REPLACE;
+ attribute.mod_values = values_replace;
+
+ list_of_mods[0] = &attribute;
+ list_of_mods[1] = NULL;
+
+ pblock_init(&mod_pb);
+ slapi_modify_internal_set_pb(&mod_pb, dn, list_of_mods, NULL, NULL,
+ pw_get_componentID(), 0);
+ slapi_modify_internal_pb(&mod_pb);
+ slapi_pblock_get(&mod_pb, SLAPI_PLUGIN_INTOP_RESULT, &res);
+ if (res != LDAP_SUCCESS){
+ LDAPDebug(LDAP_DEBUG_ANY, "WARNING: passwordPolicy modify error %d on entry '%s'\n",
+ res, dn, 0);
+ }
+
+ pblock_done(&mod_pb);
+
+ slapi_ch_free((void **) &str );
+ slapi_ch_free((void **) &history_str );
+ slapi_entry_free( e );
+ delete_passwdPolicy(&pwpolicy);
+ return 0;
+}
+
+static
+int pw_in_history( Slapi_Value **history_vals, const Slapi_Value *pw_val)
+{
+ Slapi_Value *history[25];
+ Slapi_Value historycv[25];
+ int i;
+ int ret = -1;
+ const char *pw_str = slapi_value_get_string(pw_val);
+
+ if (slapi_is_encoded((char*)pw_str)){
+ /* If the password is encoded, we just do a string match with all previous passwords */
+ for ( i = 0; history_vals[i] != NULL; i++){
+ const char * h_val = slapi_value_get_string(history_vals[i]);
+
+ if ( h_val != NULL &&
+ slapi_value_get_length(history_vals[i]) >= 14 )
+ {
+ int pos = 14;
+ if (h_val[pos] == 'Z')
+ pos++;
+ if (strcmp(&(h_val[pos]), pw_str) == 0){
+ /* Password found */
+ /* Let's just return */
+ return (0);
+ }
+ }
+ }
+ }
+ else { /* Password is in clear */
+ /* strip the timestamps */
+ for ( i = 0; history_vals[i] != NULL; i++ )
+ {
+ char *h_val = (char *)slapi_value_get_string(history_vals[i]);
+ size_t h_len = slapi_value_get_length(history_vals[i]);
+
+ historycv[i].v_csnset = NULL; /* JCM - I don't understand this */
+ history[i] = &historycv[i];
+ if ( h_val != NULL &&
+ h_len >= 14 )
+ {
+ /* LP: With the new genTime, the password history format has changed */
+ int pos = 14;
+ if (h_val[pos] == 'Z')
+ pos++;
+ historycv[i].bv.bv_val = &(h_val[pos]);
+ historycv[i].bv.bv_len = h_len - pos;
+ } else {
+ historycv[i].bv.bv_val = NULL;
+ historycv[i].bv.bv_len = 0;
+ }
+ }
+ history[i] = NULL;
+ ret = slapi_pw_find_sv( history, pw_val);
+ }
+
+ return ( ret );
+}
+
+int
+add_pwd_control ( Slapi_PBlock *pb, char *arg, long time) {
+ LDAPControl new_ctrl;
+ char buf[12];
+
+ LDAPDebug( LDAP_DEBUG_TRACE, "=> add_pwd_control\n", 0, 0, 0 );
+
+ sprintf( buf, "%ld", time );
+ new_ctrl.ldctl_oid = arg;
+ new_ctrl.ldctl_value.bv_val = buf;
+ new_ctrl.ldctl_value.bv_len = strlen( buf );
+ new_ctrl.ldctl_iscritical = 0; /* 0 = false. */
+
+ if ( slapi_pblock_set( pb, SLAPI_ADD_RESCONTROL, &new_ctrl ) != 0 ) {
+ return( -1 );
+ }
+
+ return( 0 );
+}
+
+void
+pw_mod_allowchange_aci(int pw_prohibit_change) {
+ const Slapi_DN *base;
+ char *values_mod[2];
+ LDAPMod mod;
+ LDAPMod *mods[2];
+ char *aci_pw = NULL;
+ Slapi_Backend *be;
+ char *cookie = NULL;
+
+ mods[0] = &mod;
+ mods[1] = NULL;
+ mod.mod_type = "aci";
+ mod.mod_values = values_mod;
+
+ if (pw_prohibit_change) {
+ mod.mod_op = LDAP_MOD_ADD;
+ }
+ else
+ {
+ /* Allow change password by default */
+ /* remove the aci if it is there. it is ok to fail */
+ mod.mod_op = LDAP_MOD_DELETE;
+ }
+
+ be = slapi_get_first_backend (&cookie);
+ /* Foreach backend... */
+ while (be)
+ {
+ /* Don't add aci on a chaining backend holding remote entries */
+ if((!be->be_private) && (!slapi_be_is_flag_set(be,SLAPI_BE_FLAG_REMOTE_DATA)))
+ {
+ /* There's only One suffix per DB now. No need to loop */
+ base = slapi_be_getsuffix(be, 0);
+ if (base != NULL)
+ {
+ Slapi_PBlock pb;
+ int rc;
+
+ pblock_init (&pb);
+ values_mod[0] = DENY_PW_CHANGE_ACI;
+ values_mod[1] = NULL;
+ slapi_modify_internal_set_pb(&pb, slapi_sdn_get_dn(base), mods, NULL, NULL, pw_get_componentID(), 0);
+ slapi_modify_internal_pb(&pb);
+ slapi_pblock_get(&pb, SLAPI_PLUGIN_INTOP_RESULT, &rc);
+ if (rc == LDAP_SUCCESS){
+ /*
+ ** Since we modified the acl
+ ** successfully, let's update the
+ ** in-memory acl list
+ */
+ slapi_pblock_set(&pb, SLAPI_TARGET_DN, (char*)slapi_sdn_get_dn(base) ); /* jcm: cast away const */
+ plugin_call_acl_mods_update (&pb, LDAP_REQ_MODIFY );
+ }
+ pblock_done(&pb);
+ }
+ }
+ be = slapi_get_next_backend (cookie);
+ }
+ slapi_ch_free((void **) &cookie);
+}
+
+void
+add_password_attrs( Slapi_PBlock *pb, Operation *op, Slapi_Entry *e )
+{
+ struct berval bv;
+ struct berval *bvals[2];
+ Slapi_Attr **a, **next;
+ passwdPolicy *pwpolicy = NULL;
+ char *dn = slapi_entry_get_ndn(e);
+
+ pwpolicy = new_passwdPolicy(pb, dn);
+
+ LDAPDebug( LDAP_DEBUG_TRACE, "add_password_attrs\n", 0, 0, 0 );
+
+ bvals[0] = &bv;
+ bvals[1] = NULL;
+
+ if ( pwpolicy->pw_must_change) {
+ /* must change password when first time logon */
+ bv.bv_val = format_genTime ( NO_TIME );
+ } else {
+ /* If passwordexpirationtime is specified by the user, don't
+ try to assign the initial value */
+ for ( a = &e->e_attrs; *a != NULL; a = next ) {
+ if ( strcasecmp( (*a)->a_type,
+ "passwordexpirationtime" ) == 0) {
+ delete_passwdPolicy(&pwpolicy);
+ return;
+ }
+ next = &(*a)->a_next;
+ }
+
+ bv.bv_val = format_genTime ( time_plus_sec ( current_time (),
+ pwpolicy->pw_maxage ) );
+ }
+ if ( pwpolicy->pw_exp || pwpolicy->pw_must_change ) {
+ bv.bv_len = strlen( bv.bv_val );
+ slapi_entry_attr_merge( e, "passwordexpirationtime", bvals );
+ }
+ slapi_ch_free((void **) &bv.bv_val );
+
+ /*
+ * If the password minimum age is not 0, calculate when the password
+ * is allowed to be changed again and store the result
+ * in passwordallowchangetime in the user's entry.
+ */
+ if ( pwpolicy->pw_minage != 0 ) {
+ bv.bv_val = format_genTime ( time_plus_sec ( current_time (),
+ pwpolicy->pw_minage ) );
+ bv.bv_len = strlen( bv.bv_val );
+
+ slapi_entry_attr_merge( e, "passwordallowchangetime", bvals );
+ slapi_ch_free((void **) &bv.bv_val );
+ }
+
+ delete_passwdPolicy(&pwpolicy);
+}
+
+static int
+check_trivial_words (Slapi_PBlock *pb, Slapi_Entry *e, Slapi_Value **vals, char *attrtype )
+{
+ Slapi_Attr *attr;
+ int i, pwresponse_req = 0;
+
+ slapi_pblock_get ( pb, SLAPI_PWPOLICY, &pwresponse_req );
+ attr = attrlist_find(e->e_attrs, attrtype);
+ if (attr && !valueset_isempty(&attr->a_present_values))
+ {
+ Slapi_Value **va= attr_get_present_values(attr);
+ for ( i = 0; va[i] != NULL; i++ )
+ {
+ if( strcasecmp( slapi_value_get_string(va[i]), slapi_value_get_string(vals[0])) == 0) /* JCM Innards */
+ {
+ if ( pwresponse_req == 1 ) {
+ pwpolicy_make_response_control ( pb, -1, -1,
+ LDAP_PWPOLICY_INVALIDPWDSYNTAX );
+ }
+ send_ldap_result ( pb,
+ LDAP_CONSTRAINT_VIOLATION, NULL,
+ "Password failed triviality check."
+ " Please choose a different password.",
+ 0, NULL );
+ return ( 1 );
+ }
+ }
+ }
+ return ( 0 );
+}
+
+
+void
+pw_add_allowchange_aci(Slapi_Entry *e, int pw_prohibit_change) {
+ char *aci_pw = NULL;
+ const char *aciattr = "aci";
+
+ aci_pw = slapi_ch_strdup(DENY_PW_CHANGE_ACI);
+
+ if (pw_prohibit_change) {
+ /* Add ACI */
+ slapi_entry_add_string(e, aciattr, aci_pw);
+ } else {
+ /* Remove ACI */
+ slapi_entry_delete_string(e, aciattr, aci_pw);
+ }
+ slapi_ch_free((void **) &aci_pw);
+}
+
+/* This function creates a passwdPolicy structure, loads it from either
+ * slapdFrontendconfig or the entry pointed by pwdpolicysubentry and
+ * returns the structure.
+ */
+passwdPolicy *
+new_passwdPolicy(Slapi_PBlock *pb, char *dn)
+{
+ Slapi_ValueSet *values = NULL;
+ Slapi_Entry *e = NULL, *pw_entry = NULL;
+ int type_name_disposition = 0;
+ char *actual_type_name = NULL;
+ int attr_free_flags = 0;
+ int rc=0;
+ passwdPolicy *pwdpolicy = NULL;
+ Slapi_Attr *attr;
+ char *attr_name;
+ Slapi_Value **sval;
+ slapdFrontendConfig_t *slapdFrontendConfig;
+ Slapi_Operation *op;
+ char ebuf[ BUFSIZ ];
+ int optype = -1;
+
+ slapdFrontendConfig = getFrontendConfig();
+ pwdpolicy = (passwdPolicy *)slapi_ch_calloc(1, sizeof(passwdPolicy));
+
+ slapi_pblock_get( pb, SLAPI_OPERATION, &op);
+ slapi_pblock_get( pb, SLAPI_OPERATION_TYPE, &optype );
+
+ if (slapdFrontendConfig->pwpolicy_local == 1) {
+ if ( !operation_is_flag_set( op, OP_FLAG_INTERNAL ) && dn ) {
+
+ /* If we're doing an add, COS does not apply yet so we check
+ parents for the pwdpolicysubentry. We look only for virtual
+ attributes, because real ones are for single-target policy. */
+ if (optype == SLAPI_OPERATION_ADD) {
+ char *parentdn = slapi_ch_strdup(dn);
+ char *nextdn = NULL;
+ while ((nextdn = slapi_dn_parent( parentdn )) != NULL) {
+ if (((e = get_entry( pb, nextdn )) != NULL)) {
+ if ((slapi_vattr_values_get(e, "pwdpolicysubentry",
+ &values, &type_name_disposition, &actual_type_name,
+ SLAPI_VIRTUALATTRS_REQUEST_POINTERS |
+ SLAPI_VIRTUALATTRS_ONLY,
+ &attr_free_flags)) == 0) {
+ /* pwdpolicysubentry found! */
+ break;
+ } else {
+ /* Parent didn't have it, check grandparent... */
+ slapi_ch_free_string( &parentdn );
+ parentdn = nextdn;
+ slapi_entry_free( e );
+ e = NULL;
+ }
+ } else {
+ /* Reached the top without finding a pwdpolicysubentry. */
+ break;
+ }
+ }
+
+ slapi_ch_free_string( &parentdn );
+ slapi_ch_free_string( &nextdn );
+
+ /* If we're not doing an add, we look for the pwdpolicysubentry
+ attribute in the target entry itself. */
+ } else {
+ if ( (e = get_entry( pb, dn )) != NULL ) {
+ rc = slapi_vattr_values_get(e, "pwdpolicysubentry", &values,
+ &type_name_disposition, &actual_type_name,
+ SLAPI_VIRTUALATTRS_REQUEST_POINTERS, &attr_free_flags);
+ if (rc) {
+ values = NULL;
+ }
+ }
+ }
+
+ if (values != NULL) {
+ Slapi_Value *v = NULL;
+ const struct berval *bvp = NULL;
+
+ if ( ((rc = slapi_valueset_first_value( values, &v )) != -1) &&
+ ( bvp = slapi_value_get_berval( v )) != NULL ) {
+ if ( bvp != NULL ) {
+ /* we got the pwdpolicysubentry value */
+ pw_entry = get_entry ( pb, bvp->bv_val);
+ }
+ }
+
+ slapi_vattr_values_free(&values, &actual_type_name, attr_free_flags);
+
+ slapi_entry_free( e );
+
+ if ( pw_entry == NULL ) {
+ LDAPDebug(LDAP_DEBUG_ANY, "loading global password policy for %s"
+ "--local policy entry not found\n", escape_string(dn, ebuf),0,0);
+ goto done;
+ }
+
+ for (slapi_entry_first_attr(pw_entry, &attr); attr;
+ slapi_entry_next_attr(pw_entry, attr, &attr))
+ {
+ slapi_attr_get_type(attr, &attr_name);
+ if (!strcasecmp(attr_name, "passwordminage")) {
+ if (sval = attr_get_present_values(attr)) {
+ pwdpolicy->pw_minage = slapi_value_get_long(*sval);
+ }
+ }
+ else
+ if (!strcasecmp(attr_name, "passwordmaxage")) {
+ if (sval = attr_get_present_values(attr)) {
+ pwdpolicy->pw_maxage = slapi_value_get_long(*sval);
+ }
+ }
+ else
+ if (!strcasecmp(attr_name, "passwordwarning")) {
+ if (sval = attr_get_present_values(attr)) {
+ pwdpolicy->pw_warning = slapi_value_get_long(*sval);
+ }
+ }
+ else
+ if (!strcasecmp(attr_name, "passwordhistory")) {
+ if (sval = attr_get_present_values(attr)) {
+ pwdpolicy->pw_history =
+ pw_boolean_str2value(slapi_value_get_string(*sval));
+ }
+ }
+ else
+ if (!strcasecmp(attr_name, "passwordinhistory")) {
+ if (sval = attr_get_present_values(attr)) {
+ pwdpolicy->pw_inhistory = slapi_value_get_int(*sval);
+ }
+ }
+ else
+ if (!strcasecmp(attr_name, "passwordlockout")) {
+ if (sval = attr_get_present_values(attr)) {
+ pwdpolicy->pw_lockout =
+ pw_boolean_str2value(slapi_value_get_string(*sval));
+ }
+ }
+ else
+ if (!strcasecmp(attr_name, "passwordmaxfailure")) {
+ if (sval = attr_get_present_values(attr)) {
+ pwdpolicy->pw_maxfailure = slapi_value_get_int(*sval);
+ }
+ }
+ else
+ if (!strcasecmp(attr_name, "passwordunlock")) {
+ if (sval = attr_get_present_values(attr)) {
+ pwdpolicy->pw_unlock =
+ pw_boolean_str2value(slapi_value_get_string(*sval));
+ }
+ }
+ else
+ if (!strcasecmp(attr_name, "passwordlockoutduration")) {
+ if (sval = attr_get_present_values(attr)) {
+ pwdpolicy->pw_lockduration = slapi_value_get_long(*sval);
+ }
+ }
+ else
+ if (!strcasecmp(attr_name, "passwordresetfailurecount")) {
+ if (sval = attr_get_present_values(attr)) {
+ pwdpolicy->pw_resetfailurecount = slapi_value_get_long(*sval);
+ }
+ }
+ else
+ if (!strcasecmp(attr_name, "passwordchange")) {
+ if (sval = attr_get_present_values(attr)) {
+ pwdpolicy->pw_change =
+ pw_boolean_str2value(slapi_value_get_string(*sval));
+ }
+ }
+ else
+ if (!strcasecmp(attr_name, "passwordmustchange")) {
+ if (sval = attr_get_present_values(attr)) {
+ pwdpolicy->pw_must_change =
+ pw_boolean_str2value(slapi_value_get_string(*sval));
+ }
+ }
+ else
+ if (!strcasecmp(attr_name, "passwordchecksyntax")) {
+ if (sval = attr_get_present_values(attr)) {
+ pwdpolicy->pw_syntax =
+ pw_boolean_str2value(slapi_value_get_string(*sval));
+ }
+ }
+ else
+ if (!strcasecmp(attr_name, "passwordminlength")) {
+ if (sval = attr_get_present_values(attr)) {
+ pwdpolicy->pw_minlength = slapi_value_get_int(*sval);
+ }
+ }
+ else
+ if (!strcasecmp(attr_name, "passwordexp")) {
+ if (sval = attr_get_present_values(attr)) {
+ pwdpolicy->pw_exp =
+ pw_boolean_str2value(slapi_value_get_string(*sval));
+ }
+ }
+ else
+ if (!strcasecmp(attr_name, "passwordgracelimit")) {
+ if (sval = attr_get_present_values(attr)) {
+ pwdpolicy->pw_gracelimit = slapi_value_get_int(*sval);
+ }
+ }
+
+ } /* end of for() loop */
+ return pwdpolicy;
+ }
+ }
+ }
+
+done:
+ /* If we are here, that means we need to load the passwdPolicy
+ * structure from slapdFrontendconfig
+ */
+
+ *pwdpolicy = slapdFrontendConfig->pw_policy;
+ return pwdpolicy;
+
+} /* End of new_passwdPolicy() */
+
+void
+delete_passwdPolicy( passwdPolicy **pwpolicy)
+{
+ slapi_ch_free((void **)pwpolicy);
+}
+
+/*
+ * Encode the PWPOLICY RESPONSE control.
+ *
+ * Create a password policy response control,
+ * and add it to the PBlock to be returned to the client.
+ *
+ * Returns:
+ * success ( 0 )
+ * operationsError (1),
+ */
+int
+pwpolicy_make_response_control (Slapi_PBlock *pb, int seconds, int logins, int error)
+{
+ BerElement *ber= NULL;
+ struct berval *bvp = NULL;
+ int rc = -1;
+
+ /*
+ PasswordPolicyResponseValue ::= SEQUENCE {
+ warning [0] CHOICE OPTIONAL {
+ timeBeforeExpiration [0] INTEGER (0 .. maxInt),
+ graceLoginsRemaining [1] INTEGER (0 .. maxInt) }
+ error [1] ENUMERATED OPTIONAL {
+ passwordExpired (0),
+ accountLocked (1),
+ changeAfterReset (2),
+ passwordModNotAllowed (3),
+ mustSupplyOldPassword (4),
+ invalidPasswordSyntax (5),
+ passwordTooShort (6),
+ passwordTooYoung (7),
+ passwordInHistory (8) } }
+ */
+
+ LDAPDebug( LDAP_DEBUG_TRACE, "=> pwpolicy_make_response_control", 0, 0, 0 );
+ if ( ( ber = ber_alloc()) == NULL )
+ {
+ return rc;
+ }
+
+ rc = ber_printf( ber, "{" );
+ if ( seconds >= 0 || logins >= 0 ) {
+ if ( seconds >= 0 ) {
+ rc = ber_printf( ber, "t{ti}", LDAP_TAG_PWP_WARNING,
+ LDAP_TAG_PWP_SECSLEFT,
+ seconds );
+ }
+ else {
+ rc = ber_printf( ber, "t{ti}", LDAP_TAG_PWP_WARNING,
+ LDAP_TAG_PWP_GRCLOGINS,
+ logins );
+ }
+ }
+ if ( error >= 0 ) {
+ rc = ber_printf( ber, "te", LDAP_TAG_PWP_ERROR, error );
+ }
+ rc = ber_printf( ber, "}" );
+
+ if ( rc != -1 )
+ {
+ rc = ber_flatten( ber, &bvp );
+ }
+
+ ber_free( ber, 1 );
+
+ if ( rc != -1 )
+ {
+ LDAPControl new_ctrl = {0};
+ new_ctrl.ldctl_oid = LDAP_X_CONTROL_PWPOLICY_RESPONSE;
+ new_ctrl.ldctl_value = *bvp;
+ new_ctrl.ldctl_iscritical = 0;
+ rc= slapi_pblock_set( pb, SLAPI_ADD_RESCONTROL, &new_ctrl );
+ ber_bvfree(bvp);
+ }
+
+ LDAPDebug( LDAP_DEBUG_TRACE, "<= pwpolicy_make_response_control", 0, 0, 0 );
+
+ return (rc==-1?LDAP_OPERATIONS_ERROR:LDAP_SUCCESS);
+}
+
+static int
+pw_boolean_str2value (const char *str)
+{
+ if ( !strcasecmp(str, "true") ||
+ !strcasecmp(str, "on") ||
+ !strcasecmp(str, "1") ) {
+ return ( LDAP_ON );
+ }
+
+ if ( !strcasecmp(str, "false") ||
+ !strcasecmp(str, "off") ||
+ !strcasecmp(str, "0") ) {
+ return ( LDAP_OFF );
+ }
+
+ return (-1);
+}
+
+int
+check_pw_minage_value( const char *attr_name, char *value, long minval, long maxval, char *errorbuf )
+{
+ int retVal = LDAP_SUCCESS;
+ int age;
+ char *endPtr = NULL;
+
+ age = strtol(value, &endPtr, 0 );
+ if ( (age < 0) ||
+ (age > (MAX_ALLOWED_TIME_IN_SECS - current_time())) ||
+ (endPtr == NULL) || (endPtr == value) || !isdigit(*(endPtr-1)) )
+ {
+ PR_snprintf ( errorbuf, BUFSIZ,
+ "password minimum age \"%s\" seconds is invalid. ",
+ value );
+ retVal = LDAP_CONSTRAINT_VIOLATION;
+ }
+
+ return retVal;
+}
+
+int
+check_pw_lockduration_value( const char *attr_name, char *value, long minval, long maxval, char *errorbuf )
+{
+ int retVal = LDAP_SUCCESS;
+ long duration = 0; /* in minutes */
+
+ /* in seconds */
+ duration = strtol (value, NULL, 0);
+
+ if ( duration <= 0 || duration > (MAX_ALLOWED_TIME_IN_SECS - current_time()) ) {
+ PR_snprintf ( errorbuf, BUFSIZ,
+ "password lockout duration \"%s\" seconds is invalid. ",
+ value );
+ retVal = LDAP_CONSTRAINT_VIOLATION;
+ }
+
+ return retVal;
+}
+
+int
+check_pw_resetfailurecount_value( const char *attr_name, char *value, long minval, long maxval, char *errorbuf )
+{
+ int retVal = LDAP_SUCCESS;
+ long duration = 0; /* in minutes */
+
+ /* in seconds */
+ duration = strtol (value, NULL, 0);
+ if ( duration < 0 || duration > (MAX_ALLOWED_TIME_IN_SECS - current_time()) ) {
+ PR_snprintf ( errorbuf, BUFSIZ,
+ "password reset count duration \"%s\" seconds is invalid. ",
+ value );
+ retVal = LDAP_CONSTRAINT_VIOLATION;
+ }
+
+ return retVal;
+}
+
+int
+check_pw_storagescheme_value( const char *attr_name, char *value, long minval, long maxval, char *errorbuf )
+{
+ int retVal = LDAP_SUCCESS;
+ struct pw_scheme *new_scheme = NULL;
+ char * scheme_list = NULL;
+
+ scheme_list = plugin_get_pwd_storage_scheme_list(PLUGIN_LIST_PWD_STORAGE_SCHEME);
+ new_scheme = pw_name2scheme(value);
+ if ( new_scheme == NULL) {
+ if ( scheme_list != NULL ) {
+ PR_snprintf ( errorbuf, BUFSIZ,
+ "%s: invalid scheme - %s. Valid schemes are: %s",
+ CONFIG_PW_STORAGESCHEME_ATTRIBUTE, value, scheme_list );
+ } else {
+ PR_snprintf ( errorbuf, BUFSIZ,
+ "%s: invalid scheme - %s (no pwdstorage scheme"
+ " plugin loaded)",
+ CONFIG_PW_STORAGESCHEME_ATTRIBUTE, value);
+ }
+ retVal = LDAP_CONSTRAINT_VIOLATION;
+ }
+ else if ( new_scheme->pws_enc == NULL )
+ {
+ /* For example: the NS-MTA-MD5 password scheme is for comparision only
+ and for backward compatibility with an Old Messaging Server that was
+ setting passwords in the directory already encrypted. The scheme cannot
+ and won't encrypt passwords if they are in clear. We don't take it
+ */
+
+ if ( scheme_list != NULL ) {
+ sprintf( errorbuf,
+ "%s: invalid encoding scheme - %s\nValid values are: %s\n",
+ CONFIG_PW_STORAGESCHEME_ATTRIBUTE, value, scheme_list );
+ }
+
+ retVal = LDAP_CONSTRAINT_VIOLATION;
+ }
+
+ slapi_ch_free_string(&scheme_list);
+
+ return retVal;
+}
+
diff --git a/ldap/servers/slapd/pw.h b/ldap/servers/slapd/pw.h
new file mode 100644
index 00000000..b74bbd66
--- /dev/null
+++ b/ldap/servers/slapd/pw.h
@@ -0,0 +1,70 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+#ifndef _SLAPD_PW_H_
+#define _SLAPD_PW_H_
+
+#define PWD_MAX_NAME_LEN 10
+
+#define PWD_HASH_PREFIX_START '{'
+#define PWD_HASH_PREFIX_END '}'
+
+/* Password Policy Response Control stuff */
+#define LDAP_PWPOLICY_PWDEXPIRED 0
+#define LDAP_PWPOLICY_ACCTLOCKED 1
+#define LDAP_PWPOLICY_CHGAFTERRESET 2
+#define LDAP_PWPOLICY_PWDMODNOTALLOWED 3
+#define LDAP_PWPOLICY_MUSTSUPPLYOLDPWD 4
+#define LDAP_PWPOLICY_INVALIDPWDSYNTAX 5
+#define LDAP_PWPOLICY_PWDTOOSHORT 6
+#define LDAP_PWPOLICY_PWDTOOYOUNG 7
+#define LDAP_PWPOLICY_PWDINHISTORY 8
+
+/*
+ *
+ * structure for holding password scheme info.
+ */
+struct pw_scheme {
+ /* case-insensitive name used in prefix of passwords that use scheme */
+ char *pws_name;
+
+ /* length of pws_name */
+ int pws_len;
+
+ /* thread-safe comparison function; returns 0 for positive matches */
+ /* userpwd is value sent over LDAP bind; dbpwd is from the database */
+ int (*pws_cmp)( char *userpwd, char *dbpwd );
+
+ /* thread-safe encoding function (returns pointer to malloc'd string) */
+ char *(*pws_enc)( char *pwd );
+
+ /* thread-safe decoding function (returns pointer to malloc'd string) */
+ char *(*pws_dec)( char *pwd );
+};
+
+/*
+ * Public functions from pw.c:
+ */
+struct pw_scheme *pw_name2scheme( char *name );
+struct pw_scheme *pw_val2scheme( char *val, char **valpwdp, int first_is_default );
+int pw_encodevals( Slapi_Value **vals );
+int checkPrefix(char *cipher, char *schemaName, char **encrypt);
+struct passwordpolicyarray *new_passwdPolicy ( Slapi_PBlock *pb, char *dn );
+void delete_passwdPolicy( struct passwordpolicyarray **pwpolicy);
+int pwpolicy_make_response_control (Slapi_PBlock *pb, int seconds, int logins, int error);
+
+/* function for checking the values of fine grained password policy attributes */
+int check_pw_minage_value( const char *attr_name, char *value, long minval, long maxval, char *errorbuf );
+int check_pw_lockduration_value( const char *attr_name, char *value, long minval, long maxval, char *errorbuf );
+int check_pw_resetfailurecount_value( const char *attr_name, char *value, long minval, long maxval, char *errorbuf );
+int check_pw_storagescheme_value( const char *attr_name, char *value, long minval, long maxval, char *errorbuf );
+
+/*
+ * Public functions from pw_retry.c:
+ */
+Slapi_Entry *get_entry ( Slapi_PBlock *pb, const char *dn );
+void set_retry_cnt_mods ( Slapi_PBlock *pb, Slapi_Mods *smods, int count);
+
+#endif /* _SLAPD_PW_H_ */
diff --git a/ldap/servers/slapd/pw_mgmt.c b/ldap/servers/slapd/pw_mgmt.c
new file mode 100644
index 00000000..b400efab
--- /dev/null
+++ b/ldap/servers/slapd/pw_mgmt.c
@@ -0,0 +1,398 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+/* pw_mgmt.c
+*/
+
+#include <time.h>
+#include <string.h>
+#include "slap.h"
+
+/****************************************************************************/
+/* prototypes */
+/****************************************************************************/
+
+/* need_new_pw() is called when non rootdn bind operation succeeds with authentication */
+int
+need_new_pw( Slapi_PBlock *pb, long *t, Slapi_Entry *e, int pwresponse_req )
+{
+ time_t cur_time, pw_exp_date;
+ LDAPMod *mod;
+ Slapi_Mods smods;
+ double diff_t = 0;
+ char *cur_time_str = NULL;
+ char *passwordExpirationTime;
+ char *timestring;
+ char *dn;
+ passwdPolicy *pwpolicy = NULL;
+ int pwdGraceUserTime = 0;
+ char graceUserTime[8];
+
+ slapi_mods_init (&smods, 0);
+ dn = slapi_entry_get_ndn( e );
+ pwpolicy = new_passwdPolicy(pb, dn);
+
+ /* after the user binds with authentication, clear the retry count */
+ if ( pwpolicy->pw_lockout == 1)
+ {
+ if(slapi_entry_attr_get_int( e, "passwordRetryCount") > 0)
+ {
+ slapi_mods_add_string(&smods, LDAP_MOD_REPLACE, "passwordRetryCount", "0");
+ }
+ }
+
+ cur_time = current_time();
+
+ /* get passwordExpirationTime attribute */
+ passwordExpirationTime= slapi_entry_attr_get_charptr(e, "passwordExpirationTime");
+
+ if (passwordExpirationTime == NULL)
+ {
+ /* password expiration date is not set.
+ * This is ok for data that has been loaded via ldif2ldbm
+ * Set expiration time if needed,
+ * don't do further checking and return 0 */
+ if ( pwpolicy->pw_exp == 1) {
+ pw_exp_date = time_plus_sec ( cur_time,
+ pwpolicy->pw_maxage );
+
+ timestring = format_genTime (pw_exp_date);
+ slapi_mods_add_string(&smods, LDAP_MOD_REPLACE, "passwordExpirationTime", timestring);
+ slapi_ch_free((void **)&timestring);
+ slapi_mods_add_string(&smods, LDAP_MOD_REPLACE, "passwordExpWarned", "0");
+
+ pw_apply_mods(dn, &smods);
+ }
+ slapi_mods_done(&smods);
+ delete_passwdPolicy(&pwpolicy);
+ return ( 0 );
+ }
+
+ pw_exp_date = parse_genTime(passwordExpirationTime);
+
+ slapi_ch_free((void**)&passwordExpirationTime);
+
+ /* Check if password has been reset */
+ if ( pw_exp_date == NO_TIME ) {
+
+ /* check if changing password is required */
+ if ( pwpolicy->pw_must_change ) {
+ /* set c_needpw for this connection to be true. this client
+ now can only change its own password */
+ pb->pb_conn->c_needpw = 1;
+ *t=0;
+ /* We need to include "changeafterreset" error in
+ * passwordpolicy response control. So, we will not be
+ * done here. We remember this scenario by (c_needpw=1)
+ * and check it before sending the control from various
+ * places. We will also add LDAP_CONTROL_PWEXPIRED control
+ * as the return value used to be (1).
+ */
+ goto skip;
+ }
+ /* Mark that first login occured */
+ pw_exp_date = NOT_FIRST_TIME;
+ timestring = format_genTime(pw_exp_date);
+ slapi_mods_add_string(&smods, LDAP_MOD_REPLACE, "passwordExpirationTime", timestring);
+ slapi_ch_free((void **)&timestring);
+ }
+
+skip:
+ /* if password never expires, don't need to go on; return 0 */
+ if ( pwpolicy->pw_exp == 0 ) {
+ /* check for "changeafterreset" condition */
+ if (pb->pb_conn->c_needpw == 1) {
+ if (pwresponse_req) {
+ pwpolicy_make_response_control ( pb, -1, -1, LDAP_PWPOLICY_CHGAFTERRESET );
+ }
+ add_pwd_control ( pb, LDAP_CONTROL_PWEXPIRED, 0);
+ }
+ pw_apply_mods(dn, &smods);
+ slapi_mods_done(&smods);
+ delete_passwdPolicy(&pwpolicy);
+ return ( 0 );
+ }
+
+ /* check if password expired. If so, abort bind. */
+ cur_time_str = format_genTime ( cur_time );
+ if ( pw_exp_date != NO_TIME &&
+ pw_exp_date != NOT_FIRST_TIME &&
+ (diff_t = difftime ( pw_exp_date,
+ parse_genTime ( cur_time_str ))) <= 0 ) {
+
+ /* password has expired. Check the value of
+ * passwordGraceUserTime and compare it
+ * against the value of passwordGraceLimit */
+ pwdGraceUserTime = slapi_entry_attr_get_int( e, "passwordGraceUserTime");
+ if ( pwpolicy->pw_gracelimit > pwdGraceUserTime ) {
+ pwdGraceUserTime++;
+ sprintf ( graceUserTime, "%d", pwdGraceUserTime );
+ slapi_mods_add_string(&smods, LDAP_MOD_REPLACE,
+ "passwordGraceUserTime", graceUserTime);
+ pw_apply_mods(dn, &smods);
+ slapi_mods_done(&smods);
+ if (pwresponse_req) {
+ /* check for "changeafterreset" condition */
+ if (pb->pb_conn->c_needpw == 1) {
+ pwpolicy_make_response_control( pb, -1,
+ ((pwpolicy->pw_gracelimit) - pwdGraceUserTime),
+ LDAP_PWPOLICY_CHGAFTERRESET);
+ } else {
+ pwpolicy_make_response_control( pb, -1,
+ ((pwpolicy->pw_gracelimit) - pwdGraceUserTime),
+ -1);
+ }
+ }
+
+ if (pb->pb_conn->c_needpw == 1) {
+ add_pwd_control ( pb, LDAP_CONTROL_PWEXPIRED, 0);
+ }
+ delete_passwdPolicy(&pwpolicy);
+ return ( 0 );
+ }
+
+ /* password expired and user exceeded limit of grace attemps.
+ * Send result and also the control */
+ add_pwd_control ( pb, LDAP_CONTROL_PWEXPIRED, 0);
+ if (pwresponse_req) {
+ pwpolicy_make_response_control ( pb, -1, -1, LDAP_PWPOLICY_PWDEXPIRED );
+ }
+ slapi_send_ldap_result ( pb, LDAP_INVALID_CREDENTIALS, NULL,
+ "password expired!", 0, NULL );
+
+ /* abort bind */
+ /* pass pb to do_unbind(). pb->pb_op->o_opid and
+ pb->pb_op->o_tag are not right but I don't see
+ do_unbind() checking for those. We might need to
+ create a pb for unbind operation. Also do_unbind calls
+ pre and post ops. Maybe we don't want to call them */
+ if (pb->pb_conn && (LDAP_VERSION2 == pb->pb_conn->c_ldapversion)) {
+ /* We close the connection only with LDAPv2 connections */
+ do_unbind( pb );
+ }
+ /* Apply current modifications */
+ pw_apply_mods(dn, &smods);
+ slapi_mods_done(&smods);
+ slapi_ch_free((void **) &cur_time_str );
+ delete_passwdPolicy(&pwpolicy);
+ return (-1);
+ }
+ slapi_ch_free((void **) &cur_time_str );
+
+ /* check if password is going to expire within "passwordWarning" */
+ /* Note that if pw_exp_date is NO_TIME or NOT_FIRST_TIME,
+ * we must send warning first and this changes the expiration time.
+ * This is done just below since diff_t is 0
+ */
+ if ( diff_t <= pwpolicy->pw_warning ) {
+ int pw_exp_warned = 0;
+
+ pw_exp_warned= slapi_entry_attr_get_int( e, "passwordExpWarned");
+ if ( !pw_exp_warned ){
+ /* first time send out a warning */
+ /* reset the expiration time to current + warning time
+ * and set passwordExpWarned to true
+ */
+ if (pb->pb_conn->c_needpw != 1) {
+ pw_exp_date = time_plus_sec ( cur_time,
+ pwpolicy->pw_warning );
+ }
+
+ timestring = format_genTime(pw_exp_date);
+ /* At this time passwordExpirationTime may already be
+ * in the list of mods: Remove it */
+ for (mod = slapi_mods_get_first_mod(&smods); mod != NULL;
+ mod = slapi_mods_get_next_mod(&smods))
+ {
+ if (!strcmp(mod->mod_type, "passwordExpirationTime"))
+ slapi_mods_remove(&smods);
+ }
+
+ slapi_mods_add_string(&smods, LDAP_MOD_REPLACE, "passwordExpirationTime", timestring);
+ slapi_ch_free((void **)&timestring);
+
+ slapi_mods_add_string(&smods, LDAP_MOD_REPLACE, "passwordExpWarned", "1");
+
+ *t = pwpolicy->pw_warning;
+
+ } else {
+ *t = (long)diff_t; /* jcm: had to cast double to long */
+ }
+
+ pw_apply_mods(dn, &smods);
+ slapi_mods_done(&smods);
+ if (pwresponse_req) {
+ /* check for "changeafterreset" condition */
+ if (pb->pb_conn->c_needpw == 1) {
+ pwpolicy_make_response_control( pb, *t, -1,
+ LDAP_PWPOLICY_CHGAFTERRESET);
+ } else {
+ pwpolicy_make_response_control( pb, *t, -1,
+ -1);
+ }
+ }
+
+ if (pb->pb_conn->c_needpw == 1) {
+ add_pwd_control ( pb, LDAP_CONTROL_PWEXPIRED, 0);
+ }
+ delete_passwdPolicy(&pwpolicy);
+ return (2);
+ }
+
+ pw_apply_mods(dn, &smods);
+ slapi_mods_done(&smods);
+ /* Leftover from "changeafterreset" condition */
+ if (pb->pb_conn->c_needpw == 1) {
+ add_pwd_control ( pb, LDAP_CONTROL_PWEXPIRED, 0);
+ }
+ delete_passwdPolicy(&pwpolicy);
+ /* passes checking, return 0 */
+ return( 0 );
+}
+
+/* check_account_lock is called before bind opeation; this could be a pre-op. */
+int
+check_account_lock ( Slapi_PBlock *pb, Slapi_Entry * bind_target_entry, int pwresponse_req) {
+
+ time_t unlock_time;
+ time_t cur_time;
+ double diff_t;
+ char *cur_time_str = NULL;
+ char *accountUnlockTime;
+ passwdPolicy *pwpolicy = NULL;
+ char *dn = NULL;
+
+ /* kexcoff: account inactivation */
+ int rc = 0;
+ Slapi_ValueSet *values = NULL;
+ int type_name_disposition = 0;
+ char *actual_type_name = NULL;
+ int attr_free_flags = 0;
+ /* kexcoff - end */
+
+ if ( bind_target_entry == NULL )
+ return -1;
+
+ dn = slapi_entry_get_ndn(bind_target_entry);
+ pwpolicy = new_passwdPolicy(pb, dn);
+
+ /* kexcoff: account inactivation */
+ /* check if the entry is locked by nsAccountLock attribute - account inactivation feature */
+
+ rc = slapi_vattr_values_get(bind_target_entry, "nsAccountLock",
+ &values,
+ &type_name_disposition, &actual_type_name,
+ SLAPI_VIRTUALATTRS_REQUEST_POINTERS,
+ &attr_free_flags);
+ if ( rc == 0 )
+ {
+ Slapi_Value *v = NULL;
+ const struct berval *bvp = NULL;
+
+ if ( (slapi_valueset_first_value( values, &v ) != -1) &&
+ ( bvp = slapi_value_get_berval( v )) != NULL )
+ {
+ if ( (bvp != NULL) && (strcasecmp(bvp->bv_val, "true") == 0) )
+ {
+ /* account inactivated */
+ if (pwresponse_req) {
+ pwpolicy_make_response_control ( pb, -1, -1,
+ LDAP_PWPOLICY_ACCTLOCKED );
+ }
+ send_ldap_result ( pb, LDAP_UNWILLING_TO_PERFORM, NULL,
+ "Account inactivated. Contact system administrator.",
+ 0, NULL );
+ slapi_vattr_values_free(&values, &actual_type_name, attr_free_flags);
+ goto locked;
+ }
+ } /* else, account "activated", keep on the process */
+
+ if ( values != NULL )
+ slapi_vattr_values_free(&values, &actual_type_name, attr_free_flags);
+ }
+ /* kexcoff - end */
+
+ /*
+ * Check if the password policy has to be checked or not
+ */
+ if ( pwpolicy->pw_lockout == 0 ) {
+ goto notlocked;
+ }
+
+ /*
+ * Check the attribute of the password policy
+ */
+
+ /* check if account is locked out. If so, send result and return 1 */
+ {
+ unsigned int maxfailure= pwpolicy->pw_maxfailure;
+ /* It's locked if passwordRetryCount >= maxfailure */
+ if ( slapi_entry_attr_get_uint(bind_target_entry,"passwordRetryCount") < maxfailure )
+ {
+ /* Not locked */
+ goto notlocked;
+ }
+ }
+
+ /* locked but maybe it's time to unlock it */
+ accountUnlockTime= slapi_entry_attr_get_charptr(bind_target_entry, "accountUnlockTime");
+ if (accountUnlockTime != NULL)
+ {
+ unlock_time = parse_genTime(accountUnlockTime);
+ slapi_ch_free((void **) &accountUnlockTime );
+
+ if ( pwpolicy->pw_unlock == 0 &&
+ unlock_time == NO_TIME ) {
+
+ /* account is locked forever. contact admin to reset */
+ if (pwresponse_req) {
+ pwpolicy_make_response_control ( pb, -1, -1,
+ LDAP_PWPOLICY_ACCTLOCKED );
+ }
+ send_ldap_result ( pb, LDAP_CONSTRAINT_VIOLATION, NULL,
+ "Exceed password retry limit. Contact system administrator to reset."
+ , 0, NULL );
+ goto locked;
+ }
+ cur_time = current_time();
+ cur_time_str = format_genTime( cur_time);
+ if ( ( diff_t = difftime ( parse_genTime( cur_time_str ),
+ unlock_time ) ) < 0 ) {
+
+ /* account is locked, cannot do anything */
+ if (pwresponse_req) {
+ pwpolicy_make_response_control ( pb, -1, -1,
+ LDAP_PWPOLICY_ACCTLOCKED );
+ }
+ send_ldap_result ( pb, LDAP_CONSTRAINT_VIOLATION, NULL,
+ "Exceed password retry limit. Please try later." , 0, NULL );
+ slapi_ch_free((void **) &cur_time_str );
+ goto locked;
+ }
+ slapi_ch_free((void **) &cur_time_str );
+ }
+
+notlocked:
+ /* account is not locked. */
+ delete_passwdPolicy(&pwpolicy);
+ return ( 0 );
+locked:
+ delete_passwdPolicy(&pwpolicy);
+ return (1);
+
+}
+
+void
+pw_init ( void ) {
+ slapdFrontendConfig_t *slapdFrontendConfig;
+
+ pw_set_componentID(generate_componentid(NULL, COMPONENT_PWPOLICY));
+
+ slapdFrontendConfig = getFrontendConfig();
+ pw_mod_allowchange_aci (!slapdFrontendConfig->pw_policy.pw_change &&
+ !slapdFrontendConfig->pw_policy.pw_must_change);
+}
+
+
diff --git a/ldap/servers/slapd/pw_retry.c b/ldap/servers/slapd/pw_retry.c
new file mode 100644
index 00000000..3fda4421
--- /dev/null
+++ b/ldap/servers/slapd/pw_retry.c
@@ -0,0 +1,253 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+/* pw_retry.c
+*/
+
+#include <time.h>
+#include "slap.h"
+
+/****************************************************************************/
+/* prototypes */
+/****************************************************************************/
+/* Slapi_Entry *get_entry ( Slapi_PBlock *pb, const char *dn ); */
+static void set_reset_time ( Slapi_PBlock *pb, time_t cur_time );
+static void set_retry_cnt ( Slapi_PBlock *pb, int count);
+static void set_retry_cnt_and_time ( Slapi_PBlock *pb, int count, time_t cur_time);
+
+/*
+ * update_pw_retry() is called when bind operation fails
+ * with LDAP_INVALID_CREDENTIALS (in backend bind.c ).
+ * It checks to see if the retry count can be reset,
+ * increments retry count, and then check if need to lock the acount.
+ * To have a global password policy, these mods should be chained to the
+ * master, and not applied locally. If they are applied locally, they should
+ * not get replicated from master...
+ */
+
+int update_pw_retry ( Slapi_PBlock *pb )
+{
+ Slapi_Entry *e;
+ int retry_cnt=0;
+ time_t reset_time;
+ time_t cur_time;
+ char *cur_time_str = NULL;
+ char *retryCountResetTime;
+ int passwordRetryCount;
+
+ /* get the entry */
+ e = get_entry ( pb, NULL );
+ if ( e == NULL ) {
+ return ( 1 );
+ }
+
+ cur_time = current_time();
+
+ /* check if the retry count can be reset. */
+ retryCountResetTime= slapi_entry_attr_get_charptr(e, "retryCountResetTime");
+ if(retryCountResetTime!=NULL)
+ {
+ reset_time = parse_genTime (retryCountResetTime);
+ slapi_ch_free((void **) &retryCountResetTime );
+
+ cur_time_str = format_genTime ( cur_time );
+ if ( difftime ( parse_genTime( cur_time_str ), reset_time) >= 0 )
+ {
+ /* set passwordRetryCount to 1 */
+ /* reset retryCountResetTime */
+ set_retry_cnt_and_time ( pb, 1, cur_time );
+ slapi_ch_free((void **) &cur_time_str );
+ slapi_entry_free( e );
+ return ( 0 ); /* success */
+ } else {
+ slapi_ch_free((void **) &cur_time_str );
+ }
+ } else {
+ /* initialize passwordRetryCount and retryCountResetTime */
+ set_retry_cnt_and_time ( pb, 1, cur_time );
+ slapi_entry_free( e );
+ return ( 0 ); /* success */
+ }
+ passwordRetryCount = slapi_entry_attr_get_int(e, "passwordRetryCount");
+ if (passwordRetryCount >= 0)
+ {
+ retry_cnt = passwordRetryCount + 1;
+ if ( retry_cnt == 1 ) {
+ /* set retryCountResetTime */
+ set_retry_cnt_and_time ( pb, retry_cnt, cur_time );
+ } else {
+ /* set passwordRetryCount to retry_cnt */
+ set_retry_cnt ( pb, retry_cnt );
+ }
+ }
+ slapi_entry_free( e );
+ return 0; /* success */
+}
+
+static
+void set_retry_cnt_and_time ( Slapi_PBlock *pb, int count, time_t cur_time ) {
+ char *dn;
+ Slapi_Mods smods;
+ time_t reset_time;
+ char *timestr;
+ slapdFrontendConfig_t *slapdFrontendConfig = getFrontendConfig();
+ passwdPolicy *pwpolicy = NULL;
+
+ slapi_pblock_get( pb, SLAPI_TARGET_DN, &dn );
+ pwpolicy = new_passwdPolicy(pb, dn);
+
+ slapi_mods_init(&smods, 0);
+
+ reset_time = time_plus_sec ( cur_time,
+ pwpolicy->pw_resetfailurecount );
+
+ timestr = format_genTime ( reset_time );
+ slapi_mods_add_string(&smods, LDAP_MOD_REPLACE, "retryCountResetTime", timestr);
+ slapi_ch_free((void **)&timestr);
+
+ set_retry_cnt_mods(pb, &smods, count);
+
+ pw_apply_mods(dn, &smods);
+ slapi_mods_done(&smods);
+ delete_passwdPolicy(&pwpolicy);
+}
+
+void set_retry_cnt_mods(Slapi_PBlock *pb, Slapi_Mods *smods, int count)
+{
+ char *timestr;
+ time_t unlock_time;
+ char retry_cnt[8]; /* 1-65535 */
+ slapdFrontendConfig_t *slapdFrontendConfig = getFrontendConfig();
+ char *dn = NULL;
+ passwdPolicy *pwpolicy = NULL;
+
+ slapi_pblock_get( pb, SLAPI_TARGET_DN, &dn );
+ pwpolicy = new_passwdPolicy(pb, dn);
+
+ if (smods) {
+ sprintf ( retry_cnt, "%d", count );
+ slapi_mods_add_string(smods, LDAP_MOD_REPLACE, "passwordRetryCount", retry_cnt);
+ /* lock account if reache retry limit */
+ if ( count >= pwpolicy->pw_maxfailure ) {
+ /* Remove lock_account function to perform all mods at once */
+ /* lock_account ( pb ); */
+ /* reach the retry limit, lock the account */
+ if ( pwpolicy->pw_unlock == 0 ) {
+ /* lock until admin reset password */
+ unlock_time = NO_TIME;
+ } else {
+ unlock_time = time_plus_sec ( current_time(),
+ pwpolicy->pw_lockduration );
+ }
+ timestr= format_genTime ( unlock_time );
+ slapi_mods_add_string(smods, LDAP_MOD_REPLACE, "accountUnlockTime", timestr);
+ slapi_ch_free((void **)&timestr);
+ }
+ }
+ delete_passwdPolicy(&pwpolicy);
+ return;
+}
+
+static
+void set_retry_cnt ( Slapi_PBlock *pb, int count) {
+ char *dn;
+ Slapi_Mods smods;
+
+ slapi_pblock_get( pb, SLAPI_TARGET_DN, &dn );
+ slapi_mods_init(&smods, 0);
+ set_retry_cnt_mods(pb, &smods, count);
+ pw_apply_mods(dn, &smods);
+ slapi_mods_done(&smods);
+}
+
+static
+void set_reset_time ( Slapi_PBlock *pb, time_t cur_time ) {
+ char *dn;
+ Slapi_Mods smods;
+ time_t reset_time;
+ char *timestr;
+ int resetfailurecount;
+ passwdPolicy *pwpolicy = NULL;
+
+ slapi_pblock_get( pb, SLAPI_TARGET_DN, &dn );
+
+ pwpolicy = new_passwdPolicy(pb, dn);
+ resetfailurecount = pwpolicy->pw_resetfailurecount;
+
+ slapi_mods_init(&smods, 0);
+
+ reset_time = time_plus_sec ( cur_time, resetfailurecount );
+
+ timestr = format_genTime ( reset_time );
+ slapi_mods_add_string(&smods, LDAP_MOD_REPLACE, "retryCountResetTime", timestr);
+ slapi_ch_free((void **)&timestr);
+
+ pw_apply_mods(dn, &smods);
+ slapi_mods_done(&smods);
+ delete_passwdPolicy(&pwpolicy);
+}
+
+Slapi_Entry *get_entry ( Slapi_PBlock *pb, const char *dn)
+{
+ int search_result = 0;
+ Slapi_Entry *retentry = NULL;
+ Slapi_DN sdn;
+
+ if ( dn == NULL ) {
+ char *t;
+ slapi_pblock_get( pb, SLAPI_TARGET_DN, &t );
+ dn= t;
+ }
+
+ slapi_sdn_init_dn_byref(&sdn, dn);
+
+ if ((search_result = slapi_search_internal_get_entry(&sdn, NULL, &retentry, pw_get_componentID())) != LDAP_SUCCESS){
+ LDAPDebug (LDAP_DEBUG_TRACE, "WARNING: 'get_entry' can't find entry '%s', err %d\n", dn, search_result, 0);
+ }
+ slapi_sdn_done(&sdn);
+ return retentry;
+}
+
+void pw_apply_mods(const char *dn, Slapi_Mods *mods)
+{
+ Slapi_PBlock pb;
+ int res;
+
+ if (mods && (slapi_mods_get_num_mods(mods) > 0))
+ {
+ pblock_init(&pb);
+ slapi_modify_internal_set_pb (&pb, dn,
+ slapi_mods_get_ldapmods_byref(mods),
+ NULL, /* Controls */
+ NULL, /* UniqueID */
+ pw_get_componentID(), /* PluginID */
+ 0); /* Flags */
+ slapi_modify_internal_pb (&pb);
+
+ slapi_pblock_get(&pb, SLAPI_PLUGIN_INTOP_RESULT, &res);
+ if (res != LDAP_SUCCESS){
+ LDAPDebug(LDAP_DEBUG_ANY, "WARNING: passwordPolicy modify error %d on entry '%s'\n",
+ res, dn, 0);
+ }
+
+ pblock_done(&pb);
+ }
+
+ return;
+}
+
+/* Handle the component ID for the password policy */
+
+static struct slapi_componentid * pw_componentid = NULL;
+
+void pw_set_componentID(struct slapi_componentid *cid)
+{
+ pw_componentid = cid;
+}
+
+struct slapi_componentid * pw_get_componentID()
+{
+ return pw_componentid;
+}
diff --git a/ldap/servers/slapd/rdn.c b/ldap/servers/slapd/rdn.c
new file mode 100644
index 00000000..1eeb75ee
--- /dev/null
+++ b/ldap/servers/slapd/rdn.c
@@ -0,0 +1,410 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+
+#include "slap.h"
+
+#define FLAG_RDNS 0
+
+Slapi_RDN *
+slapi_rdn_new()
+{
+ Slapi_RDN *rdn= (Slapi_RDN *)slapi_ch_malloc(sizeof(struct slapi_rdn));
+ slapi_rdn_init(rdn);
+ return rdn;
+}
+
+Slapi_RDN *
+slapi_rdn_new_dn(const char *dn)
+{
+ Slapi_RDN *rdn= slapi_rdn_new();
+ slapi_rdn_init_dn(rdn,dn);
+ return rdn;
+}
+
+Slapi_RDN *
+slapi_rdn_new_sdn(const Slapi_DN *sdn)
+{
+ Slapi_RDN *rdn= slapi_rdn_new();
+ slapi_rdn_init_sdn(rdn,sdn);
+ return rdn;
+}
+
+Slapi_RDN *
+slapi_rdn_new_rdn(const Slapi_RDN *fromrdn)
+{
+ Slapi_RDN *rdn= slapi_rdn_new();
+ slapi_rdn_init_rdn(rdn,fromrdn);
+ return rdn;
+}
+
+void
+slapi_rdn_init(Slapi_RDN *rdn)
+{
+ rdn->flag= 0;
+ rdn->rdn= NULL;
+ rdn->rdns= NULL;
+ rdn->butcheredupto= -1; /* Means we haven't started converting '=' to '\0' in rdns */
+}
+
+void
+slapi_rdn_init_dn(Slapi_RDN *rdn,const char *dn)
+{
+ slapi_rdn_init(rdn);
+ if(dn!=NULL)
+ {
+ char **dns= ldap_explode_dn(dn, 0);
+ if(dns!=NULL)
+ {
+ rdn->rdn= slapi_ch_strdup(dns[0]);
+ ldap_value_free(dns);
+ }
+ }
+}
+
+void
+slapi_rdn_init_sdn(Slapi_RDN *rdn,const Slapi_DN *sdn)
+{
+ if(sdn!=NULL)
+ {
+ slapi_rdn_init_dn(rdn,slapi_sdn_get_dn(sdn));
+ }
+ else
+ {
+ slapi_rdn_init(rdn);
+ }
+}
+
+void
+slapi_rdn_init_rdn(Slapi_RDN *rdn,const Slapi_RDN *fromrdn)
+{
+ slapi_rdn_init(rdn);
+ rdn->rdn= slapi_ch_strdup(fromrdn->rdn);
+}
+
+void
+slapi_rdn_set_dn(Slapi_RDN *rdn,const char *dn)
+{
+ slapi_rdn_done(rdn);
+ slapi_rdn_init_dn(rdn,dn);
+}
+
+void
+slapi_rdn_set_sdn(Slapi_RDN *rdn,const Slapi_DN *sdn)
+{
+ slapi_rdn_done(rdn);
+ slapi_rdn_init_sdn(rdn,sdn);
+}
+
+void
+slapi_rdn_set_rdn(Slapi_RDN *rdn,const Slapi_RDN *fromrdn)
+{
+ slapi_rdn_done(rdn);
+ slapi_rdn_init_rdn(rdn,fromrdn);
+}
+
+static char **
+slapi_rdn_get_rdns(Slapi_RDN *rdn)
+{
+ char **rdns= NULL;
+ /* Check if rdns is upto date */
+ if(!slapi_isbitset_uchar(rdn->flag,FLAG_RDNS))
+ {
+ if(rdn->rdns!=NULL)
+ {
+ ldap_value_free(rdn->rdns);
+ rdn->rdns= NULL;
+ }
+ if(rdn->rdn!=NULL)
+ {
+ rdn->rdns = ldap_explode_rdn( rdn->rdn, 0 );
+ rdns= rdn->rdns;
+ }
+ slapi_setbit_uchar(rdn->flag,FLAG_RDNS);
+ rdn->butcheredupto= -1;
+ }
+ return rdns;
+}
+
+
+void
+slapi_rdn_free(Slapi_RDN **rdn)
+{
+ if(rdn!=NULL)
+ {
+ slapi_rdn_done(*rdn);
+ slapi_ch_free((void**)rdn);
+ }
+}
+
+void
+slapi_rdn_done(Slapi_RDN *rdn)
+{
+ if(rdn!=NULL)
+ {
+ slapi_ch_free((void**)&(rdn->rdn));
+ ldap_value_free(rdn->rdns);
+ slapi_rdn_init(rdn);
+ }
+}
+
+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)
+{
+ int returnindex;
+ PR_ASSERT(index>=0);
+ if(rdn->rdns==NULL)
+ {
+ rdn->rdns= slapi_rdn_get_rdns(rdn);
+ }
+
+ if (rdn->rdns == NULL || rdn->rdns[index]==NULL)
+ {
+ *type= NULL;
+ *value= NULL;
+ returnindex= -1;
+ }
+ else
+ {
+ if(rdn->butcheredupto>=index)
+ {
+ /* the '=' has already been converted to a '\0' */
+ *type= rdn->rdns[index];
+ *value= *type + strlen(*type) + 1;
+ returnindex= ++index;
+ }
+ else
+ {
+ *type= strchr(rdn->rdns[index],'=');
+ if(*type==NULL)
+ {
+ /* This just shouldn't happen... */
+ *type= NULL;
+ *value= NULL;
+ returnindex= -1;
+ }
+ else
+ {
+ **type = '\0';
+ *value= *type;
+ (*value)++; /* Skip the '\0' */
+ *type = rdn->rdns[index];
+ rdn->butcheredupto= index;
+ returnindex= ++index;
+ }
+ }
+ }
+ return returnindex;
+}
+
+int
+slapi_rdn_get_index(Slapi_RDN *rdn, const char *type, const char *value, size_t length)
+{
+ int result;
+ char *theValue;
+ result= slapi_rdn_get_index_attr(rdn, type, &theValue);
+ if(result!=-1)
+ {
+ if(theValue==NULL || (strncasecmp(value,theValue,length)!=0))
+ {
+ result= -1;
+ }
+ }
+ return result;
+}
+
+int
+slapi_rdn_get_index_attr(Slapi_RDN *rdn, const char *type, char **value)
+{
+ int result= -1;
+ int index;
+ char *theType;
+ index= slapi_rdn_get_first(rdn, &theType, value);
+ while(index!=-1)
+ {
+ if(theType!=NULL && value!=NULL &&
+ (strcasecmp(type,theType)==0))
+ {
+ result= index;
+ index= -1;
+ }
+ else
+ {
+ index= slapi_rdn_get_next(rdn, index, &theType, value);
+ }
+ }
+ return result;
+}
+
+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_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_add(Slapi_RDN *rdn, const char *type, const char *value)
+{
+ PR_ASSERT(NULL != type);
+ PR_ASSERT(NULL != value);
+ if(rdn->rdn==NULL)
+ {
+ /* type=value '\0' */
+ rdn->rdn= slapi_ch_malloc(strlen(type)+1+strlen(value)+1);
+ strcpy( rdn->rdn, type );
+ strcat( rdn->rdn, "=" );
+ strcat( rdn->rdn, value );
+ }
+ else
+ {
+ /* type=value+rdn '\0' */
+ char *newrdn= slapi_ch_malloc(strlen(type)+1+strlen(value)+1+strlen(rdn->rdn)+1);
+ strcpy( newrdn, type );
+ strcat( newrdn, "=" );
+ strcat( newrdn, value );
+ strcat( newrdn, "+" );
+ strcat( newrdn, rdn->rdn );
+ slapi_ch_free((void**)&rdn->rdn);
+ rdn->rdn= newrdn;
+ }
+ slapi_unsetbit_uchar(rdn->flag,FLAG_RDNS);
+ return 1;
+}
+
+int
+slapi_rdn_remove_index(Slapi_RDN *rdn, int atindex)
+{
+ Slapi_RDN newrdn;
+ int result= 0;
+ int index;
+ char *theType;
+ char *theValue;
+ slapi_rdn_init(&newrdn);
+ index= slapi_rdn_get_first(rdn, &theType, &theValue);
+ while(index!=-1)
+ {
+ if(index!=atindex)
+ {
+ slapi_rdn_add(&newrdn,theType,theValue);
+ }
+ else
+ {
+ result= 1;
+ }
+ index= slapi_rdn_get_next(rdn, index, &theType, &theValue);
+ }
+ if(result)
+ {
+ slapi_rdn_set_rdn(rdn,&newrdn);
+ }
+ slapi_rdn_done(&newrdn);
+ return result;
+}
+
+int
+slapi_rdn_remove(Slapi_RDN *rdn, const char *type, const char *value, size_t length)
+{
+ int result= 0;
+ if(rdn->rdn!=NULL)
+ {
+ int atindex= slapi_rdn_get_index(rdn, type, value, length);
+ if(atindex!=-1)
+ {
+ result= slapi_rdn_remove_index(rdn, atindex);
+ }
+ }
+ return result;
+}
+
+int
+slapi_rdn_remove_attr(Slapi_RDN *rdn, const char *type)
+{
+ int result= 0;
+ if(rdn->rdn!=NULL)
+ {
+ char *value;
+ int atindex= slapi_rdn_get_index_attr(rdn, type, &value);
+ if(atindex!=-1)
+ {
+ result= slapi_rdn_remove_index(rdn, atindex);
+ }
+ }
+ return result;
+}
+
+int
+slapi_rdn_isempty(const Slapi_RDN *rdn)
+{
+ return (rdn->rdn==NULL || rdn->rdn[0]=='\0');
+}
+
+int
+slapi_rdn_get_num_components(Slapi_RDN *rdn)
+{
+ int i= 0;
+ char **rdns= slapi_rdn_get_rdns(rdn);
+ if(rdns!=NULL)
+ {
+ for(i=0; rdns[i]!=NULL; i++);
+ }
+ return i;
+}
+
+int
+slapi_rdn_compare(Slapi_RDN *rdn1, Slapi_RDN *rdn2)
+{
+ int r= 1;
+ int n1= slapi_rdn_get_num_components(rdn1);
+ int n2= slapi_rdn_get_num_components(rdn2);
+ if (n1==n2)
+ {
+ char *type, *value;
+ int i= slapi_rdn_get_first(rdn1, &type, &value);
+ while(r==1 && i!=-1)
+ {
+ r= slapi_rdn_contains(rdn2, type, value, strlen(value));
+ i= slapi_rdn_get_next(rdn1, i, &type, &value);
+ }
+ if(r==1) /* All rdn1's rdn components were in rdn2 */
+ {
+ r= 0; /* SAME */
+ }
+ else
+ {
+ r= -1; /* NOT SAME */
+ }
+ }
+ else
+ {
+ r= -1; /* NOT SAME */
+ }
+ return r;
+}
+
+const char *
+slapi_rdn_get_rdn(const Slapi_RDN *rdn)
+{
+ return rdn->rdn;
+}
+
+const char *
+slapi_rdn_get_nrdn(const Slapi_RDN *rdn)
+{
+ /* JCM - Normalised RDN? */
+ PR_ASSERT(0);
+ return NULL;
+}
diff --git a/ldap/servers/slapd/referral.c b/ldap/servers/slapd/referral.c
new file mode 100644
index 00000000..98fedadf
--- /dev/null
+++ b/ldap/servers/slapd/referral.c
@@ -0,0 +1,1205 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+/*
+ * referrals.c - LDAP referral-related routines.
+ * a) manage in-memory copiedfrom and copyingfrom referrals.
+ * b) function to adjust smart referrals to match operation parameters.
+ */
+#include <stdio.h>
+#include "slap.h"
+
+/* Forward Decls */
+static int ref_array_del(Ref **target, int write, int read);
+static int ref_array_add(const char *dn, struct berval *referral, int write, int read);
+static Ref **ref_array_find(const char *dn);
+static int ref_array_mod(Ref **target, struct berval *referral, int write, int read);
+static void strcat_escaped( char *s1, char *s2 );
+static void adjust_referral_basedn( char **urlp, const Slapi_DN *refcontainerdn, char *opdn_norm, int isreference );
+static int dn_is_below( const char *dn_norm, const char *ancestor_norm );
+static Ref_Array *g_get_global_referrals(void);
+static void ref_free (Ref **goner);
+static Ref_Array global_referrals;
+
+struct refCb {
+ int type;
+ char *cbName;
+ void (*cb)(Slapi_PBlock *, void *);
+ void *cbData;
+ struct refCb *next;
+};
+
+struct refCb *refCbList=NULL;
+
+int
+ref_register_callback(int type, char *description,
+ void (*cb)(Slapi_PBlock *, void *), void *cbData);
+int
+ref_remove_callback(char *description);
+static
+int ref_call_cbs(int type, Slapi_PBlock *pb);
+
+
+#define SLAPD_DEFAULT_REFARRAY_SIZE 10
+
+/*
+ * Function: g_get_global_referrals
+ *
+ * Returns: nothing
+ *
+ * Description: Fills the global_referrals referral array.
+ * The size is set to "size". The mutex is created.
+ * The array is calloc'd.
+ */
+static Ref_Array *
+g_get_global_referrals(void)
+{
+ if (global_referrals.ra_rwlock == NULL)
+ {
+ /* Make a new lock */
+ global_referrals.ra_rwlock = rwl_new();
+
+ if (global_referrals.ra_rwlock == NULL) {
+ LDAPDebug( LDAP_DEBUG_ANY,
+ "ref_array_init: new lock creation failed\n", 0, 0, 0);
+ exit (-1);
+ }
+
+ /* Initialize all the fields. */
+ global_referrals.ra_size = SLAPD_DEFAULT_REFARRAY_SIZE;
+ global_referrals.ra_nextindex = 0;
+ global_referrals.ra_readcount = 0;
+ global_referrals.ra_refs = (Ref **) slapi_ch_calloc(SLAPD_DEFAULT_REFARRAY_SIZE, sizeof( Ref * ));
+ }
+ return( &global_referrals );
+}
+
+/*
+ * Function: ref_array_del
+ *
+ * Returns: 0 good, -1 bad.
+ *
+ * Description: finds "dn" in the list and unsets the read or write
+ * flag(s). If both are then zero, then it frees up that entry.
+ * target should point to the referral that is to be deleted.
+ * Note: This does NOT lock global_referrals.ra_rwlock.
+ *
+ * Author: RJP
+ */
+static int
+ref_array_del(Ref **target, int write, int read)
+{
+ Ref **lastref = NULL;
+ Ref_Array *grefs = NULL;
+
+ grefs = g_get_global_referrals();
+
+ if (target == NULL) {
+ return(-1);
+ }
+
+ /*Unset either or both flags*/
+ if (write) {
+ (*target)->ref_writes = 0;
+ }
+ if (read) {
+ (*target)->ref_reads = 0;
+ grefs->ra_readcount--;
+ }
+
+ /*If there is a flag set, then don't delete the referral*/
+ if (((*target)->ref_writes == 1) || (*target)->ref_reads == 1){
+ return(0);
+ }
+
+ /* Free up target */
+ ref_free(target);
+
+
+ /*
+ * Okay, we want to maintain our array's compactedness,
+ * so we take the referral that's in the last position, and
+ * put that in target's place. If they are one and the
+ * same, then no problem. This shouldn't ever seg fault.
+ * (famous last words).
+ */
+ lastref = &grefs->ra_refs[grefs->ra_nextindex - 1];
+
+ *target = *lastref;
+
+ grefs->ra_refs[grefs->ra_nextindex - 1] = NULL;
+
+ /*reset the next_index*/
+ grefs->ra_nextindex--;
+
+ return(0);
+
+}
+
+
+/*
+ * Function: ref_array_replace
+ *
+ * Returns: 0 good, -1 bad.
+ *
+ * Description: Locks the mutex associated with global_referrals,
+ * adds that referral and "dn" to the global_referrals if it
+ * doesn't exist already. If it does exist, and the new
+ * referral is different, then the old one gets clobbered.
+ * If referral is NULL, then it deletes the referral
+ * associated with dn.
+ * Note: This locks global_referrals.ra_rwlock.
+ *
+ * Author: RJP
+ */
+int
+ref_array_replace(const char *dn, struct berval *referral, int write, int read)
+{
+ Ref **target = NULL;
+ int err;
+ Ref_Array *grefs = NULL;
+
+ grefs = g_get_global_referrals();
+
+ if (dn == NULL) {
+ return(0);
+ }
+
+ GR_LOCK_WRITE();
+
+ /* Find the referral, if any. */
+ target = ref_array_find(dn);
+
+ /* If datasource is NULL, then delete target. */
+ if ( referral == NULL ){
+
+ /* If target is null, then there is nothing to do. */
+ if (target == NULL) {
+ GR_UNLOCK_WRITE();
+ return(0);
+ }
+ err = ref_array_del(target, write, read);
+
+ GR_UNLOCK_WRITE();
+
+ return(err);
+ }
+
+ /* If target is NULL, then add target to the end. */
+ if ( target == NULL) {
+ err = ref_array_add(dn, referral, write, read);
+ GR_UNLOCK_WRITE();
+ return(err);
+ }
+
+ /* Else, the referral already exists and we should modify it. */
+ err = ref_array_mod(target, referral, write, read);
+ GR_UNLOCK_WRITE();
+ return(err);
+}
+
+
+/*
+ * Function: ref_array_mod
+ *
+ * Returns: 0 good, -1 bad.
+ *
+ * Description: modifies the existing referral.
+ * First it checks the host:port in datasource
+ * against the host:port in the existing referral.
+ * If they don't match, then it replaces the referral
+ *
+ * Note: This does NOT lock global_referrals.ra_rwlock.
+ *
+ * Author: RJP
+ */
+static int
+ref_array_mod(Ref **target, struct berval *referral, int write, int read)
+{
+ Ref_Array *grefs = NULL;
+
+ grefs = g_get_global_referrals();
+
+ if (referral == NULL || target == NULL) {
+ return(0);
+ }
+
+ /* Actually, instead of comparing them, we might as well just swap */
+ ber_bvfree( (*target)->ref_referral );
+
+ (*target)->ref_referral = referral ;
+
+ /*
+ * We have to update the read/write flags which
+ * refer reads and writes, respectively
+ */
+ if (write) {
+ (*target)->ref_writes = 1;
+ }
+ if (read) {
+ /*Don't update the readcount unnecessarily*/
+ if ((*target)->ref_reads == 0) {
+ grefs->ra_readcount++;
+ }
+
+ (*target)->ref_reads = 1;
+ }
+
+ return(0);
+
+}
+
+
+
+/*
+ * Function: ref_array_add
+ *
+ * Returns: 0 good, -1 bad.
+ *
+ * Description: adds that referral and "dn" to the global_referrals
+ * Note: This does NOT lock global_referrals.ra_rwlock.
+ *
+ * Author: RJP
+ */
+static int
+ref_array_add(const char *dn, struct berval *referral, int write, int read)
+{
+ Ref **target = NULL;
+ Ref_Array *grefs = NULL;
+
+ grefs = g_get_global_referrals();
+
+ if (dn == NULL || referral == NULL) {
+ return(0);
+ }
+
+
+ /* We may have to realloc if we are about to index an out-of-range slot */
+ if (grefs->ra_nextindex >= grefs->ra_size){
+ /* reset the size */
+ grefs->ra_size += 10;
+
+ /* reallocate */
+ grefs->ra_refs = (Ref **) slapi_ch_realloc((char *) grefs->ra_refs,
+ grefs->ra_size * (sizeof(Ref *)));
+ }
+
+ /* Tack the new referral to the end. */
+ target = &(grefs->ra_refs[grefs->ra_nextindex]);
+
+ /* Malloc and fill the fields of the new referral */
+ (*target) = (Ref *) slapi_ch_malloc( sizeof(Ref));
+ (*target)->ref_dn = slapi_dn_normalize_case(slapi_ch_strdup(dn));
+ (*target)->ref_referral = referral;
+
+ /* Update the next available index */
+ grefs->ra_nextindex++;
+
+ (*target)->ref_writes = 0;
+ (*target)->ref_reads = 0;
+
+ /*
+ * We have to update the read/write flags which
+ * refer reads and writes, respectively
+ */
+ if (write) {
+ (*target)->ref_writes = 1;
+ }
+ if (read) {
+ (*target)->ref_reads = 1;
+ grefs->ra_readcount++;
+ }
+
+ return(0);
+}
+
+/*
+ * Function: ref_array_find
+ *
+ * Returns: a pointer to a pointer to a Ref, or NULL
+ *
+ * Description: Traverses the array of referrals, until
+ * it either finds a match or gets to the end.
+ * Note: This DOES NOT lock global_referrals.ra_rwlock.
+ *
+ * Author: RJP
+ *
+ */
+static Ref **
+ref_array_find(const char *dn)
+{
+ int walker;
+ Ref_Array *grefs = NULL;
+
+ grefs = g_get_global_referrals();
+
+ if (dn == NULL) {
+ return(NULL);
+ }
+
+ /* Walk down the array, testing for a match */
+ for (walker = 0; walker < grefs->ra_nextindex; walker++){
+
+ if (strcasecmp (grefs->ra_refs[walker]->ref_dn, dn) == 0) {
+ return(&grefs->ra_refs[walker]);
+ }
+ }
+ return(NULL);
+}
+
+/*
+ * Function: send_read_referrals
+ *
+ * Returns: A copy of global_referrals
+ *
+ * Description: Given a dn, this function sends all the copyingfrom
+ * referrals beneath "dn" that are within "scope."
+ * returns a copy of the global_referrals array
+ * that it makes for use later. This is to avoid
+ * any race conditions of ORC ending in the middle
+ * of the search and scewing things up. NULL is returned
+ * if there are no copyingfrom referrals in there.
+ *
+ * If "dn" does not exactly match a referral's dn, we append
+ * "/referralDN" to the referral itself, i.e, we send a
+ * referral like this:
+ * ldap://host:port/dn
+ * instead of one like this:
+ * ldap://host:port
+ * We do not append the referral DN to the referrals present
+ * in the copy of the global_referrals array that we return.
+ *
+ * Author: RJP
+ *
+ */
+Ref_Array *
+send_read_referrals(Slapi_PBlock *pb, int scope, char *dn,
+ struct berval ***urls)
+{
+ int walker, urllen, dnlen;
+ struct berval *refs[2], refcopy;
+ char *urlcopy;
+ Ref_Array *grefs = NULL;
+ Ref_Array *the_copy = NULL;
+ int found_one = 0;
+
+ /* Get a pointer to global_referrals */
+ grefs = g_get_global_referrals();
+
+ GR_LOCK_READ();
+
+ /*If no copyingfroms, just return*/
+ if (grefs->ra_readcount <= 0) {
+ GR_UNLOCK_READ();
+ return(NULL);
+ }
+
+ refs[1] = NULL;
+
+ /*
+ * Walk through the refs in the_copy and send any referrals
+ * that are below "dn". Take "scope" into account as well.
+ */
+ for (walker = 0; walker < grefs->ra_nextindex; walker++) {
+ if ( grefs->ra_refs[walker]->ref_reads &&
+ (( scope == LDAP_SCOPE_BASE &&
+ strcasecmp(grefs->ra_refs[walker]->ref_dn, dn) == 0 ) ||
+ ( scope == LDAP_SCOPE_ONELEVEL &&
+ slapi_dn_isparent(dn, grefs->ra_refs[walker]->ref_dn)) ||
+ ( scope == LDAP_SCOPE_SUBTREE &&
+ slapi_dn_issuffix(grefs->ra_refs[walker]->ref_dn, dn)))) {
+ found_one = 1;
+
+ /*
+ * Make an array of 1 referral. If the referral DN is below "dn",
+ * i.e, it is not the same as "dn", we make a copy and append a
+ * URL-escaped version of the referral DN to the original referral.
+ */
+ if ( scope == LDAP_SCOPE_BASE ||
+ strcasecmp( grefs->ra_refs[walker]->ref_dn, dn ) == 0 ) {
+ refs[0] = grefs->ra_refs[walker]->ref_referral;
+ urlcopy = NULL;
+ } else {
+ urllen = strlen( grefs->ra_refs[walker]->ref_referral->bv_val );
+ dnlen = strlen( grefs->ra_refs[walker]->ref_dn );
+ /* space for worst-case expansion due to escape plus room for '/' */
+ urlcopy = slapi_ch_malloc( urllen + 3 * dnlen + 2 );
+
+ strcpy( urlcopy, grefs->ra_refs[walker]->ref_referral->bv_val );
+ urlcopy[urllen] = '/';
+ ++urllen;
+ urlcopy[urllen] = '\0';
+ strcat_escaped( urlcopy + urllen, grefs->ra_refs[walker]->ref_dn );
+
+ refcopy.bv_val = urlcopy;
+ refcopy.bv_len = strlen( urlcopy );
+ refs[0] = &refcopy;
+ }
+
+ send_ldap_referral( pb, NULL, refs, urls );
+ slapi_pblock_set( pb, SLAPI_SEARCH_REFERRALS, *urls );
+
+ if ( urlcopy != NULL ) {
+ slapi_ch_free( (void **)&urlcopy );
+ }
+ }
+ }
+
+ /* Make a copy of global_referrals to avoid any race conditions */
+ if (found_one) {
+ the_copy = ref_array_dup();
+ }
+
+ GR_UNLOCK_READ();
+
+ /*
+ * After we sent all the referrals, return the copy of
+ * global_referrals for use later. If there were none found, return
+ * NULL
+ */
+ return(the_copy);
+}
+
+/*
+ * Function: ref_array_dup
+ *
+ * Returns: a copy of global_referrals
+ *
+ * Description: Makes a copy of global_referrals and returns that puppy
+ * Note: Does not lock global_referrals.
+ *
+ * Author: RJP
+ *
+ */
+Ref_Array *
+ref_array_dup(void)
+{
+ Ref_Array *grefs = NULL;
+ Ref_Array *the_copy = NULL;
+ int walker;
+
+ /*Allocate the first structure*/
+ the_copy = (Ref_Array *) slapi_ch_calloc(1, sizeof(Ref_Array));
+
+ /* Don't bother with the lock, it's only a local copy. */
+ the_copy->ra_rwlock = NULL;
+
+ /*Grab a reference to the global_referrals*/
+ grefs = g_get_global_referrals();
+
+ /* Initialize all the fields of the copy. */
+ the_copy->ra_size = grefs->ra_size;
+ the_copy->ra_nextindex = grefs->ra_nextindex;
+ the_copy->ra_readcount = grefs->ra_readcount;
+ the_copy->ra_refs = (Ref **) slapi_ch_calloc(the_copy->ra_size, sizeof( Ref * ));
+
+ /*Walk down grefs, copying each Ref struct */
+ for (walker = 0; walker < grefs->ra_nextindex; walker++) {
+ the_copy->ra_refs[walker] = (Ref *)slapi_ch_calloc(1, sizeof(Ref));
+ the_copy->ra_refs[walker]->ref_dn = slapi_ch_strdup(grefs->ra_refs[walker]->ref_dn);
+ the_copy->ra_refs[walker]->ref_referral = slapi_ch_bvdup(grefs->ra_refs[walker]->ref_referral);
+ the_copy->ra_refs[walker]->ref_reads = grefs->ra_refs[walker]->ref_reads;
+ the_copy->ra_refs[walker]->ref_writes = grefs->ra_refs[walker]->ref_writes;
+ }
+
+ return(the_copy);
+
+}
+
+
+/*
+ * Function: ref_free
+ *
+ * Returns: nothing
+ *
+ * Description: frees up "goner"
+ *
+ * Author: RJP
+ *
+ */
+static void
+ref_free (Ref **goner)
+{
+ slapi_ch_free((void**) &((*goner)->ref_dn));
+ ber_bvfree( (*goner)->ref_referral );
+ slapi_ch_free((void**) goner);
+}
+
+/*
+ * Function: ref_array_dup_free
+ *
+ * Returns: nothingness
+ *
+ * Description: takes a Ref_Array dup and frees that puppy
+ *
+ * Author: RJP
+ *
+ */
+void
+ref_array_dup_free(Ref_Array *the_copy)
+{
+ int walker;
+
+ if (the_copy == NULL) {
+ return;
+ }
+
+ /* Walk down the array, deleting each referral */
+ for (walker = 0; walker < the_copy->ra_nextindex; walker++)
+ {
+ ref_free (&the_copy->ra_refs[walker]);
+ }
+
+ /* free the array of pointers */
+ slapi_ch_free((void **) &the_copy->ra_refs);
+ slapi_ch_free((void **) &the_copy);
+
+ return;
+}
+
+
+
+/*
+ * Function: referrals_free
+ *
+ * Returns: nothing
+ *
+ * Description: frees up everything.
+ *
+ * Author: RJP
+ *
+ */
+void
+referrals_free (void)
+{
+ int walker;
+ Ref_Array *grefs = NULL;
+
+ grefs = g_get_global_referrals();
+
+ GR_LOCK_WRITE();
+
+ /* Walk down the array, deleting each referral */
+ for (walker = 0; walker < grefs->ra_nextindex; walker++){
+ ref_free (&grefs->ra_refs[walker]);
+
+ }
+
+ slapi_ch_free ((void **) &grefs->ra_refs);
+
+ GR_UNLOCK_WRITE();
+
+ rwl_free( &grefs->ra_rwlock );
+}
+
+/*
+ * Function: ref_array_moddn
+ *
+ * Returns: 0 good, -1 bad.
+ *
+ * Description: modifies the existing referral's dn.
+ * First it locks global_referrals.ra_rwlock.
+ * Then it clobbers the existing dn.
+ * Then it replaces it with a new dn constructed
+ * from newrdn.
+ * Note: This locks global_referrals.ra_rwlock.
+ *
+ * Author: RJP
+ */
+void
+ref_array_moddn(const char *dn, char *newrdn, Slapi_PBlock *pb)
+{
+ char *pdn = NULL;
+ char *newdn = NULL;
+ Ref **target = NULL;
+ Ref_Array *grefs = NULL;
+
+ grefs = g_get_global_referrals();
+
+ if (dn == NULL) {
+ return;
+ }
+
+ GR_LOCK_WRITE();
+
+ /* Find the referral. */
+ target = ref_array_find(dn);
+
+ /*
+ * If we can't find it, then we're done. This is okay, because this
+ * is the only check that is made to see if the entry has a
+ * copiedfrom in it.
+ */
+ if (target == NULL) {
+ GR_UNLOCK_WRITE();
+ return;
+ }
+ /* construct the new dn */
+ if ( (pdn = slapi_dn_beparent( pb, dn )) != NULL ) {
+ /* parent + rdn + separator(s) + null */
+ newdn = (char *) slapi_ch_malloc( strlen( pdn ) + strlen( newrdn ) + 3 );
+ strcpy( newdn, newrdn );
+ strcat( newdn, ", " );
+ strcat( newdn, pdn );
+ } else {
+ newdn = (char *) slapi_ch_strdup( newrdn );
+ }
+ slapi_ch_free((void **) &pdn );
+ (void) slapi_dn_normalize_case( newdn );
+
+
+ /* We have found the referral. blow away the dn*/
+ slapi_ch_free((void**) &((*target)->ref_dn));
+
+ /* stick in the new one. */
+ (*target)->ref_dn = newdn;
+
+ GR_UNLOCK_WRITE();
+
+ return;
+}
+
+
+/*
+ * HREF_CHAR_ACCEPTABLE was copied from libldap/tmplout.c
+ */
+/* Note: an identical function is in ../plugins/replication/replutil.c */
+#define HREF_CHAR_ACCEPTABLE( c ) (( c >= '-' && c <= '9' ) || \
+ ( c >= '@' && c <= 'Z' ) || \
+ ( c == '_' ) || \
+ ( c >= 'a' && c <= 'z' ))
+
+/*
+ * Function: strcat_escaped
+ *
+ * Returns: nothing
+ *
+ * Description: Appends string s2 to s1, URL-escaping (%HH) unsafe
+ * characters in s2 as appropriate. This function was
+ * copied from libldap/tmplout.c.
+ *
+ * Author: MCS
+ */
+/*
+ * append s2 to s1, URL-escaping (%HH) unsafe characters
+ */
+/* Note: an identical function is in ../plugins/replication/replutil.c */
+static void
+strcat_escaped( char *s1, char *s2 )
+{
+ char *p, *q;
+ char *hexdig = "0123456789ABCDEF";
+
+ p = s1 + strlen( s1 );
+ for ( q = s2; *q != '\0'; ++q ) {
+ if ( HREF_CHAR_ACCEPTABLE( *q )) {
+ *p++ = *q;
+ } else {
+ *p++ = '%';
+ *p++ = hexdig[ 0x0F & ((*(unsigned char*)q) >> 4) ];
+ *p++ = hexdig[ 0x0F & *q ];
+ }
+ }
+
+ *p = '\0';
+}
+
+void
+update_global_referrals(Slapi_PBlock *pb)
+{
+ ref_call_cbs(0,pb);
+ return;
+}
+
+/*
+ * ref_adjust() -- adjust referrals based on operation-specific data.
+ * The general idea is for us (the server) to be smart so LDAP clients
+ * can be as dumb as possible.
+ *
+ * XXXmcs: We always duplicate the referrals even if no adjustments need to
+ * be made. It would be more efficient but more complicated to avoid making
+ * a copy if we don't need to change anything. If that were done, the
+ * interface to this function would need to change since the caller would
+ * need to know whether the returned memory needed to be freed or not.
+ *
+ * Parameters:
+ * pb is the pblock for the operation we are working on.
+ * urls is a list of referrals.
+ * refsdn is the Slapi_DN of the entry where the referral is stored.
+ * is_reference is true if a searchResultReference is being returned.
+ *
+ * Returns:
+ * a (possibly modified) copy of the urls. It should be freed by
+ * calling ber_bvecfree().
+ *
+ *
+ * First we strip off any labels from the referral URLs. For example, a
+ * referral like this:
+ *
+ * ldap://directory.airius.com/ou=people,o=airius.com Ref to people tree
+ *
+ * is changed to:
+ *
+ * ldap://directory.airius.com/ou=people,o=airius.com
+ *
+ * Next, if the referral includes a baseDN we potentially modify it or strip
+ * it off entirely. If the referral doesn't include a baseDN and we're
+ * processing a continuation reference, we add a baseDN.
+ *
+ * Finally, if we are processing a continuation reference that resulted from
+ * a one-level search, we append "??base" to the referral so the client will
+ * use the correct scope when chasing the reference.
+ */
+struct berval **
+ref_adjust( Slapi_PBlock *pb, struct berval **urls, const Slapi_DN *refsdn,
+ int is_reference )
+{
+ int i, len, scope;
+ char *p, *opdn_norm;
+ struct berval **urlscopy;
+ Operation *op;
+
+ if ( NULL == urls || NULL == urls[0] ) {
+ return( NULL );
+ }
+
+ PR_ASSERT( pb != NULL );
+
+ /*
+ * grab the operation target DN and operation structure.
+ * if the operation is a search, get the scope as well.
+ */
+ if ( slapi_pblock_get( pb, SLAPI_TARGET_DN, &p ) != 0 || p == NULL ||
+ slapi_pblock_get( pb, SLAPI_OPERATION, &op ) != 0 || op == NULL ||
+ ( operation_get_type(op) == SLAPI_OPERATION_SEARCH && slapi_pblock_get( pb,
+ SLAPI_SEARCH_SCOPE, &scope ) != 0 )) {
+ LDAPDebug( LDAP_DEBUG_ANY, "ref_adjust: referrals suppressed "
+ "(could not get target DN, operation, or scope from pblock)\n",
+ 0, 0, 0 );
+ return( NULL );
+ }
+
+ /*
+ * normalize the DNs we plan to compare with others later.
+ */
+ opdn_norm = slapi_dn_normalize( slapi_ch_strdup( p ));
+
+
+ /*
+ * count referrals and duplicate the array
+ */
+ for ( i = 0; urls[i] != NULL; ++i ) {
+ ;
+ }
+ urlscopy = (struct berval **) slapi_ch_malloc(( i + 1 ) *
+ sizeof( struct berval * ));
+
+ for ( i = 0; urls[i] != NULL; ++i ) {
+ /*
+ * duplicate the URL, stripping off the label if there is one and
+ * leaving extra room for "??base" in case we need to append that.
+ */
+ urlscopy[i] = (struct berval *)slapi_ch_malloc(
+ sizeof( struct berval ));
+ if (( p = strchr( urls[i]->bv_val, ' ' )) == NULL ) {
+ len = strlen( urls[i]->bv_val );
+ } else {
+ len = p - urls[i]->bv_val;
+ }
+ urlscopy[i]->bv_val = (char *)slapi_ch_malloc( len + 7 );
+ memcpy( urlscopy[i]->bv_val, urls[i]->bv_val, len );
+ urlscopy[i]->bv_val[len] = '\0';
+
+ /*
+ * adjust the baseDN as needed and set the length
+ */
+ adjust_referral_basedn( &urlscopy[i]->bv_val, refsdn,
+ opdn_norm, is_reference );
+ urlscopy[i]->bv_len = strlen( urlscopy[i]->bv_val );
+
+ /*
+ * if we are dealing with a continuation reference that resulted
+ * from a one-level search, add a scope of base to the URL.
+ */
+ if ( is_reference && operation_get_type(op) == SLAPI_OPERATION_SEARCH &&
+ scope == LDAP_SCOPE_ONELEVEL ) {
+ strcat( urlscopy[i]->bv_val, "??base" );
+ urlscopy[i]->bv_len += 6;
+ }
+ }
+
+ urlscopy[i] = NULL; /* NULL terminate the new referrals array */
+
+ /*
+ * log what we did (for debugging purposes)
+ */
+ if ( LDAPDebugLevelIsSet( LDAP_DEBUG_ARGS )) {
+ for ( i = 0; urlscopy[i] != NULL; ++i ) {
+ LDAPDebug( LDAP_DEBUG_ARGS, "ref_adjust: \"%s\" -> \"%s\"\n",
+ urls[i]->bv_val, urlscopy[i]->bv_val, 0 );
+ }
+ }
+
+ /*
+ * done -- clean up and return the new referrals array
+ */
+ slapi_ch_free( (void **)&opdn_norm );
+
+ return( urlscopy );
+}
+
+
+
+/*
+ * adjust_referral_basedn() -- pull the referral apart and modify the
+ * baseDN contained in the referral if appropriate not.
+ * If the referral does not have baseDN and we're not processing a
+ * continuation reference, we do not need to do anything. If we're
+ * processing a continuation reference, we need to add a baseDN.
+ *
+ * If it does have one, there are two special cases we deal with:
+ *
+ * 1) The referral's baseDN is an ancestor of the operation's baseDN,
+ * which means that the LDAP client already has a more specific
+ * baseDN than is contained in the referral. If so, we strip off
+ * the baseDN so the client will re-use its operation DN when
+ * chasing the referral.
+ *
+ * 2) The referral's baseDN is not an ancestor of the operation's
+ * baseDN and the DN of the entry that contains the referral is
+ * an ancestor of the operation DN. If so, we remove the portion
+ * of the operation DN that matches the DN of the entry containing
+ * the referral and prepend what's left to the referral base DN.
+ *
+ * We assume that the baseDN is the right-most piece of the URL, i.e., there
+ * is no attribute list, scope, or options after the baseDN.
+ *
+ * The code also assumes that the baseDN doesn't contain any '/' character.
+ * This later assumption NEED to be removed and code changed appropriately.
+ */
+static void
+adjust_referral_basedn( char **urlp, const Slapi_DN *refsdn,
+ char *opdn_norm, int isreference )
+{
+ LDAPURLDesc *ludp = NULL;
+ char *p, *refdn_norm;
+ int rc = 0;
+
+ PR_ASSERT( urlp != NULL );
+ PR_ASSERT( *urlp != NULL );
+ PR_ASSERT( refsdn != NULL );
+ PR_ASSERT( opdn_norm != NULL );
+
+ if (opdn_norm == NULL){
+ /* Be safe but this should never ever happen */
+ return;
+ }
+
+ rc = ldap_url_parse( *urlp, &ludp );
+
+ if ((rc != 0) &&
+ (rc != LDAP_URL_ERR_NODN))
+ /*
+ * rc != LDAP_URL_ERR_NODN is to circumvent a pb in the C-SDK
+ * in ldap_url_parse. The function will return an error if the
+ * URL contains no DN (though it is a valid URL according to
+ * RFC 2255.
+ */
+ {
+ /* Nothing to do, just return */
+ return;
+ }
+
+ if (ludp && (ludp->lud_dn != NULL)) {
+
+ refdn_norm = slapi_dn_normalize( slapi_ch_strdup( ludp->lud_dn ));
+
+ if ( dn_is_below( opdn_norm, refdn_norm )) {
+ /*
+ * Strip off the baseDN.
+ */
+ if (( p = strrchr( *urlp, '/' )) != NULL ) {
+ *p = '\0';
+ }
+
+ } else if ( dn_is_below( opdn_norm, slapi_sdn_get_ndn(refsdn) )) {
+ int cur_len, add_len;
+
+ /*
+ * Prepend the portion of the operation DN that does not match
+ * the ref container DN to the referral baseDN.
+ */
+ add_len = strlen( opdn_norm ) - strlen( slapi_sdn_get_ndn(refsdn) );
+ cur_len = strlen( *urlp );
+ /* + 7 because we keep extra space in case we add ??base */
+ *urlp = slapi_ch_realloc( *urlp, cur_len + add_len + 7 );
+
+ if (( p = strrchr( *urlp, '/' )) != NULL ) {
+ ++p;
+ memmove( p + add_len, p, strlen( p ) + 1 );
+ memmove( p, opdn_norm, add_len );
+ }
+
+ } /* else: leave the original referral baseDN intact. */
+
+ slapi_ch_free( (void **)&refdn_norm );
+ } else if (isreference) {
+ int cur_len, add_len;
+
+ /* If ludp->lud_dn == NULL and isreference : Add the DN of the ref entry*/
+ if ( dn_is_below( opdn_norm, slapi_sdn_get_ndn(refsdn) )){
+ add_len = strlen(opdn_norm);
+ p = opdn_norm;
+ } else {
+ add_len = strlen(slapi_sdn_get_ndn(refsdn));
+ p = (char *)slapi_sdn_get_ndn(refsdn);
+ }
+
+ cur_len = strlen( *urlp );
+ /* + 8 because we keep extra space in case we add a / and/or ??base */
+ *urlp = slapi_ch_realloc( *urlp, cur_len + add_len + 8 );
+ if ((*urlp)[cur_len - 1] != '/'){
+ /* The URL is not ending with a /, let's add one */
+ strcat(*urlp, "/");
+ }
+ strcat(*urlp, p);
+ }
+
+ if ( ludp != NULL ) {
+ ldap_free_urldesc( ludp );
+ }
+ return;
+}
+
+
+/*
+ * dn_is_below(): return non-zero if "dn_norm" is below "ancestor_norm" in
+ * the DIT and return zero if not.
+ */
+static int
+dn_is_below( const char *dn_norm, const char *ancestor_norm )
+{
+ PR_ASSERT( dn_norm != NULL );
+ PR_ASSERT( ancestor_norm != NULL );
+
+ if ( 0 == strcasecmp( dn_norm, ancestor_norm )) {
+ return( 0 ); /* same DN --> not an ancestor relationship */
+ }
+
+ return( slapi_dn_issuffix( dn_norm, ancestor_norm ));
+}
+
+
+
+/*
+ * This function is useful to discover the source of data and provide
+ * this as a referral. It is also useful if someone simply wants
+ * to know if a dn is mastered somewhere else.
+ *
+ * For a given dn, traverse the referral list and look for the copiedFrom
+ * attribute. If such an attribute is found get the server hostname
+ * and port in the form "ldap://hostname:port".
+ * Else return NULL.
+ *
+ * ORC extension: if orc == 1, this function will search for copyingFrom
+ * which will refer searches and compares on trees that are half-baked.
+ *
+ * ORC extension: if cf_refs is NULL, then global_referrals is consulted,
+ * otherwise, cf_refs is consulted.
+ */
+struct berval **
+get_data_source(Slapi_PBlock *pb, const Slapi_DN *sdn, int orc, void *cfrp)
+{
+ int walker;
+ struct berval **bvp;
+ struct berval *bv;
+ int found_it;
+ Ref_Array *grefs = NULL;
+ Ref_Array *the_refs = NULL;
+ Ref_Array *cf_refs = (Ref_Array *)cfrp;
+
+ /* If no Ref_Array is given, use global_referrals */
+ if (cf_refs == NULL) {
+ grefs = g_get_global_referrals();
+ the_refs = grefs;
+ GR_LOCK_READ();
+ } else {
+ the_refs = cf_refs;
+ }
+
+ /* optimization: if orc is 1 (a read), then check the readcount*/
+ if (orc && the_refs->ra_readcount == 0) {
+ if (cf_refs == NULL) {
+ GR_UNLOCK_READ();
+ }
+ return (NULL);
+ }
+
+
+ bvp = NULL;
+ bv = NULL;
+ found_it = 0;
+
+ /* Walk down the array, testing each dn to make see if it's a parent of "dn" */
+ for (walker = 0; walker < the_refs->ra_nextindex; walker++){
+
+ if ( slapi_dn_issuffix(slapi_sdn_get_ndn(sdn), the_refs->ra_refs[walker]->ref_dn)) {
+ found_it = 1;
+ break;
+
+ }
+ }
+
+ /* no referral, so return NULL */
+ if (!found_it) {
+ if (cf_refs == NULL) {
+ GR_UNLOCK_READ();
+ }
+ return (NULL);
+ }
+
+ /*
+ * Gotta make sure we're returning the right one. If orc is 1, then
+ * only return a read referral. if orc is 0, then only return a
+ * write referral.
+ */
+ if (orc && the_refs->ra_refs[walker]->ref_reads != 1) {
+ if (cf_refs == NULL) {
+ GR_UNLOCK_READ();
+ }
+ return (NULL);
+ }
+
+ if (!orc && the_refs->ra_refs[walker]->ref_writes != 1) {
+ if (cf_refs == NULL) {
+ GR_UNLOCK_READ();
+ }
+ return (NULL);
+ }
+
+
+ /* Fix for 310968 --- return an SSL referral to an SSL client */
+ if ( 0 != ( pb->pb_conn->c_flags & CONN_FLAG_SSL )) {
+ /* SSL connection */
+ char * old_referral_string = NULL;
+ char * new_referral_string = NULL;
+ char *p = NULL;
+ /* Get the basic referral */
+ bv = slapi_ch_bvdup(the_refs->ra_refs[walker]->ref_referral);
+ old_referral_string = bv->bv_val;
+ /* The longest new string will be one character longer than the old one */
+ new_referral_string = slapi_ch_malloc(bv->bv_len + 1);
+ /* Re-write it to replace ldap with ldaps, and remove the port information */
+ /* The original string will look like this: ldap://host:port */
+ /* We need to make it look like this: ldaps://host */
+ /* Potentially the ":port" part might be missing from the original */
+ sprintf(new_referral_string, "%s%s" , LDAPS_URL_PREFIX, old_referral_string + strlen(LDAP_URL_PREFIX) );
+ /* Go looking for the port */
+ p = new_referral_string + (strlen(LDAPS_URL_PREFIX) + 1);
+ while (*p != '\0' && *p != ':') p++;
+ if (':' == *p) {
+ /* It had a port, zap it */
+ *p = '\0';
+ }
+ /* Fix the bv to point to this new string */
+ bv->bv_val = new_referral_string;
+ /* Fix its length */
+ bv->bv_len = strlen(bv->bv_val);
+ /* Free the copy we made of the original */
+ slapi_ch_free((void**)&old_referral_string);
+ } else {
+ /* regular connection */
+ bv = (struct berval *) slapi_ch_bvdup(the_refs->ra_refs[walker]->ref_referral);
+ }
+
+ /* Package it up and send that puppy. */
+ bvp = (struct berval **) slapi_ch_malloc( 2 * sizeof(struct berval *) );
+ bvp[0] = bv;
+ bvp[1] = NULL;
+
+ if (cf_refs == NULL) {
+ GR_UNLOCK_READ();
+ }
+
+ return(bvp);
+
+}
+
+
+int
+ref_register_callback(int type, char *description,
+ void (*cb)(Slapi_PBlock *, void *), void *cbData)
+{
+ struct refCb *cbPtr;
+ struct refCb *newCb;
+
+ if(NULL == (newCb =
+ (struct refCb *)slapi_ch_calloc(1,sizeof(struct refCb)))) {
+ /* out of memory? */
+ return(-1);
+ }
+ newCb->type = type;
+ newCb->next = NULL;
+ newCb->cb = cb;
+ newCb->cbData = cbData;
+ newCb->cbName = slapi_ch_strdup(description);
+
+ if(NULL == refCbList) {
+ refCbList = newCb;
+ return(0);
+ }
+ cbPtr = refCbList;
+ while(NULL != cbPtr->next) cbPtr = cbPtr->next;
+ cbPtr->next=newCb;
+
+ return(0);
+}
+
+int
+ref_remove_callback(char *description)
+{
+ struct refCb *cbPtr = refCbList;
+ struct refCb *cbPrev = refCbList;
+
+ if((NULL == description) || (NULL == cbPtr))
+ return(-1);
+
+ while(cbPtr) {
+ if(!strcmp(description,cbPtr->cbName)) {
+ if(cbPrev == refCbList) {
+ refCbList = cbPtr->next;
+ } else {
+ cbPrev->next = cbPtr->next;
+ }
+ slapi_ch_free((void **)&cbPtr->cbName);
+ /* we don't know how the cbData was allocated...we won't attempt
+ to free it */
+ slapi_ch_free((void **)&cbPtr);
+ break;
+ }
+ cbPrev = cbPtr;
+ cbPtr = cbPtr->next;
+ }
+
+ return(0);
+}
+
+static
+int ref_call_cbs(int type, Slapi_PBlock *pb)
+{
+ struct refCb *cbPtr = refCbList;
+
+ if(NULL == cbPtr) {
+ return(0);
+ }
+
+ while(cbPtr) {
+ (*cbPtr->cb)(pb, cbPtr->cbData);
+ cbPtr = cbPtr->next;
+ }
+
+ return(0);
+}
+
diff --git a/ldap/servers/slapd/regex.c b/ldap/servers/slapd/regex.c
new file mode 100644
index 00000000..d9382b24
--- /dev/null
+++ b/ldap/servers/slapd/regex.c
@@ -0,0 +1,1045 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+#include "slap.h" /* must come before regex.h */
+#include "portable.h"
+
+static PRLock *regex_mutex = NULL;
+
+int
+slapd_re_init( void )
+{
+ if ( NULL == regex_mutex ) {
+ regex_mutex = PR_NewLock();
+ }
+ return( NULL == regex_mutex ? -1 : 0 );
+}
+
+void
+slapd_re_lock( void )
+{
+ PR_ASSERT( NULL != regex_mutex );
+ PR_Lock( regex_mutex );
+}
+
+int
+slapd_re_unlock( void )
+{
+ PR_ASSERT( NULL != regex_mutex );
+ return( PR_Unlock( regex_mutex ) );
+}
+
+#if defined( MACOS ) || defined( DOS ) || defined( _WIN32 ) || defined( NEED_BSDREGEX )
+#include "regex.h"
+
+/*
+ * regex - Regular expression pattern matching and replacement
+ *
+ * By: Ozan S. Yigit (oz)
+ * Dept. of Computer Science
+ * York University
+ *
+ * These routines are the PUBLIC DOMAIN equivalents of regex
+ * routines as found in 4.nBSD UN*X, with minor extensions.
+ *
+ * These routines are derived from various implementations found
+ * in software tools books, and Conroy's grep. They are NOT derived
+ * from licensed/restricted software.
+ * For more interesting/academic/complicated implementations,
+ * see Henry Spencer's regexp routines, or GNU Emacs pattern
+ * matching module.
+ *
+ * Modification history:
+ *
+ * $Log: regex.c,v $
+ * Revision 1.1 2005/01/21 00:40:51 cvsadm
+ * Initial revision
+ *
+ * Revision 1.3.20.1.2.11 2005/01/14 01:22:12 nhosoi
+ * For the open-source project.
+ * 1) eliminated 'netsite' level
+ * 2) moved ns/config one level lower
+ * 3) moved fasttime to lib/base
+ *
+ * Revision 1.3.20.1.2.10 2004/10/01 18:46:09 dboreham
+ * Rename the built in regex functions to avoid colliding with the native OS versions, where presnet
+ *
+ * Revision 1.2 2004/10/01 06:29:11 david
+ * rename regex functions to avoid collision with native OS functions on Solaris
+ *
+ * Revision 1.1.1.1 2004/06/03 22:32:48 telackey
+ * Initial import Thu Jun 3 15:32:43 PDT 2004
+ *
+ * Revision 1.3.20.1.2.9 2003/09/22 19:42:12 ulfw
+ * Update copyright years from 2001 to 2001-2003
+ *
+ * Revision 1.3.20.1.2.8 2001/11/03 00:13:55 richm
+ * XXX use new copyright XXX
+ *
+ * Revision 1.3.20.1.2.7 2001/10/07 00:59:03 richm
+ * ldapserver/ldap/servers/slapd/regex.c
+ * 1.3.20.1.2.7
+ * 20010918
+ *
+ * Remove copyright caracter form copyright
+ *
+ *
+ * ====================================================
+ *
+ * Revision 1.3.20.1.2.7 2001/09/18 11:43:06 rmarco
+ * Remove copyright caracter form copyright
+ *
+ * Revision 1.3.20.1.2.6 2001/02/13 09:45:16 rmarco
+ * copyrights
+ *
+ * Revision 1.3.20.1.2.5 1999/12/06 10:49:33 robey
+ * fix gcc warning
+ *
+ * Revision 1.3.20.1.2.4 1999/11/18 01:26:26 robey
+ * fix gcc warnings
+ *
+ * Revision 1.3.20.1.2.3 1999/08/20 23:13:33 merrells
+ * tidy up extern things
+ *
+ * Revision 1.3.20.1.2.2 1999/05/19 23:41:58 merrells
+ * Keep the Solaris compiler quiet
+ *
+ * Revision 1.3.20.1.2.1 1999/05/14 18:39:50 merrells
+ * value manipulation code extraction and reworking.
+ *
+ * Revision 1.3.20.1 1998/10/10 02:28:25 ggood
+ * Copy from Directory40RtmBranchpoint to DirectoryBranch
+ *
+ * Revision 1.3.10.5 1997/12/11 23:12:01 kristian
+ * fix bugs 97502, 97504 & 96569: handle 8-bit char's (especially UTF-8) correctly.
+ *
+ * Revision 1.3.10.4 1997/12/07 22:13:08 howes
+ * Always compile re_init(), re_lock(), and re_unlock(), even
+ * on platforms where we use the native regex stuff.
+ *
+ * Revision 1.3.10.3 1997/12/07 00:01:16 howes
+ * Add re_init(), re_lock(), and re_unlock() routines, to avoid race
+ * between acl and syntax code.
+ *
+ * Revision 1.3.10.2 1997/07/17 07:49:13 mcs
+ * merge changes made on ldapsdk_10_branch into server3_directory_branch
+ *
+ * Revision 1.3.10.1 1997/03/27 06:39:33 ggood
+ * Fix up more compiler warnings.
+ *
+ * Revision 1.3 1996/11/07 00:44:44 mcs
+ * eliminate a few compiler warnings
+ *
+ * Revision 1.2 1996/11/05 21:38:05 kristian
+ * copied from Directory_1996_11_04
+ *
+ * Revision 1.1.1.1.2.1 1996/05/07 19:54:53 kristian
+ * Merged UMich ldap-3_3 into Dogbert
+ *
+ * Revision 1.1.1.2 1996/05/04 19:11:02 kristian
+ * UMich version 3.3
+ *
+ * Revision 1.2 1996/04/25 16:24:11 mcs
+ * make re_exec() match "" with ".*" and similar patterns
+ * hopefully this change doesn't break anything else!
+ *
+ * Revision 1.1 1995/02/03 15:56:52 tim
+ * Initial revision
+ *
+ * Revision 1.11 1994/12/14 21:33:45 mcs
+ * use new NEED_BSDREGEX
+ * fix pmatch() prototype
+ *
+ * Revision 1.10 1994/12/12 18:16:39 mcs
+ * use on NetBSD
+ *
+ * Revision 1.9 1994/11/15 19:16:35 mcs
+ * add (CHAR) cast to make VisualC++ happy
+ *
+ * Revision 1.8 1994/11/08 21:14:32 mcs
+ * WIN32 changes
+ *
+ * Revision 1.7 1994/07/23 19:51:24 mcs
+ * use ANSI-style inline function parameters
+ *
+ * Revision 1.6 1993/10/18 01:52:32 tim
+ * include for VMS
+ *
+ * Revision 1.5 1993/09/28 21:37:54 mcs
+ * HP/UX needs the regex we include (not in its libc)
+ *
+ * Revision 1.4 1993/08/27 15:59:52 mcs
+ * use CHAR for deftab
+ *
+ * Revision 1.3 1993/08/27 15:49:47 mcs
+ * added missing 0 to octal constants
+ * use unsigned char for CHAR under DOS
+ *
+ * Revision 1.2 1993/08/27 14:57:48 mcs
+ * add proto. for pmatch
+ *
+ * Revision 1.1 1993/08/18 21:20:02 mcs
+ * Initial revision
+ *
+ * Revision 1.4 1991/10/17 03:56:42 oz
+ * miscellaneous changes, small cleanups etc.
+ *
+ * Revision 1.3 1989/04/01 14:18:09 oz
+ * Change all references to a dfa: this is actually an nfa.
+ *
+ * Revision 1.2 88/08/28 15:36:04 oz
+ * Use a complement bitmap to represent NCL.
+ * This removes the need to have seperate
+ * code in the pmatch case block - it is
+ * just CCL code now.
+ *
+ * Use the actual CCL code in the CLO
+ * section of pmatch. No need for a recursive
+ * pmatch call.
+ *
+ * Use a bitmap table to set char bits in an
+ * 8-bit chunk.
+ *
+ * Interfaces:
+ * The following three interfaces were added to avoid a race
+ * condition in slapd. The better long-term solution is to make
+ * the regex code thread-safe, by passing in the buffers needed.
+ *
+ * re_init: initializes the regex system. must be called
+ * before any other regex calls are made.
+ *
+ * re_lock: locks the regex system. must be called to avoid
+ * races between calls to re_comp and re_exec.
+ *
+ * re_unlock unlocks the regex system. must be called after
+ * a set of calls to re_comp and re_exec.
+ *
+ * re_comp: compile a regular expression into a NFA.
+ *
+ * char *re_comp(s)
+ * char *s;
+ *
+ * re_exec: execute the NFA to match a pattern.
+ *
+ * int re_exec(s)
+ * char *s;
+ *
+ * re_modw change re_exec's understanding of what a "word"
+ * looks like (for \< and \>) by adding into the
+ * hidden word-syntax table.
+ *
+ * void re_modw(s)
+ * char *s;
+ *
+ * re_subs: substitute the matched portions in a new string.
+ *
+ * int re_subs(src, dst)
+ * char *src;
+ * char *dst;
+ *
+ * re_fail: failure routine for re_exec.
+ *
+ * void re_fail(msg, op)
+ * char *msg;
+ * char op;
+ *
+ * Regular Expressions:
+ *
+ * [1] char matches itself, unless it is a special
+ * character (metachar): . \ [ ] * + ^ $
+ *
+ * [2] . matches any character.
+ *
+ * [3] \ matches the character following it, except
+ * when followed by a left or right round bracket,
+ * a digit 1 to 9 or a left or right angle bracket.
+ * (see [7], [8] and [9])
+ * It is used as an escape character for all
+ * other meta-characters, and itself. When used
+ * in a set ([4]), it is treated as an ordinary
+ * character.
+ *
+ * [4] [set] matches one of the characters in the set.
+ * If the first character in the set is "^",
+ * it matches a character NOT in the set, i.e.
+ * complements the set. A shorthand S-E is
+ * used to specify a set of characters S upto
+ * E, inclusive. The special characters "]" and
+ * "-" have no special meaning if they appear
+ * as the first chars in the set.
+ * examples: match:
+ *
+ * [a-z] any lowercase alpha
+ *
+ * [^]-] any char except ] and -
+ *
+ * [^A-Z] any char except uppercase
+ * alpha
+ *
+ * [a-zA-Z] any alpha
+ *
+ * [5] * any regular expression form [1] to [4], followed by
+ * closure char (*) matches zero or more matches of
+ * that form.
+ *
+ * [6] + same as [5], except it matches one or more.
+ *
+ * [7] a regular expression in the form [1] to [10], enclosed
+ * as \(form\) matches what form matches. The enclosure
+ * creates a set of tags, used for [8] and for
+ * pattern substution. The tagged forms are numbered
+ * starting from 1.
+ *
+ * [8] a \ followed by a digit 1 to 9 matches whatever a
+ * previously tagged regular expression ([7]) matched.
+ *
+ * [9] \< a regular expression starting with a \< construct
+ * \> and/or ending with a \> construct, restricts the
+ * pattern matching to the beginning of a word, and/or
+ * the end of a word. A word is defined to be a character
+ * string beginning and/or ending with the characters
+ * A-Z a-z 0-9 and _. It must also be preceded and/or
+ * followed by any character outside those mentioned.
+ *
+ * [10] a composite regular expression xy where x and y
+ * are in the form [1] to [10] matches the longest
+ * match of x followed by a match for y.
+ *
+ * [11] ^ a regular expression starting with a ^ character
+ * $ and/or ending with a $ character, restricts the
+ * pattern matching to the beginning of the line,
+ * or the end of line. [anchors] Elsewhere in the
+ * pattern, ^ and $ are treated as ordinary characters.
+ *
+ *
+ * Acknowledgements:
+ *
+ * HCR's Hugh Redelmeier has been most helpful in various
+ * stages of development. He convinced me to include BOW
+ * and EOW constructs, originally invented by Rob Pike at
+ * the University of Toronto.
+ *
+ * References:
+ * Software tools Kernighan & Plauger
+ * Software tools in Pascal Kernighan & Plauger
+ * Grep [rsx-11 C dist] David Conroy
+ * ed - text editor Un*x Programmer's Manual
+ * Advanced editing on Un*x B. W. Kernighan
+ * RegExp routines Henry Spencer
+ *
+ * Notes:
+ *
+ * This implementation uses a bit-set representation for character
+ * classes for speed and compactness. Each character is represented
+ * by one bit in a 128-bit block. Thus, CCL always takes a
+ * constant 16 bytes in the internal nfa, and re_exec does a single
+ * bit comparison to locate the character in the set.
+ *
+ * Examples:
+ *
+ * pattern: foo*.*
+ * compile: CHR f CHR o CLO CHR o END CLO ANY END END
+ * matches: fo foo fooo foobar fobar foxx ...
+ *
+ * pattern: fo[ob]a[rz]
+ * compile: CHR f CHR o CCL bitset CHR a CCL bitset END
+ * matches: fobar fooar fobaz fooaz
+ *
+ * pattern: foo\\+
+ * compile: CHR f CHR o CHR o CHR \ CLO CHR \ END END
+ * matches: foo\ foo\\ foo\\\ ...
+ *
+ * pattern: \(foo\)[1-3]\1 (same as foo[1-3]foo)
+ * compile: BOT 1 CHR f CHR o CHR o EOT 1 CCL bitset REF 1 END
+ * matches: foo1foo foo2foo foo3foo
+ *
+ * pattern: \(fo.*\)-\1
+ * compile: BOT 1 CHR f CHR o CLO ANY END EOT 1 CHR - REF 1 END
+ * matches: foo-foo fo-fo fob-fob foobar-foobar ...
+ */
+
+#define MAXNFA 1024
+#define MAXTAG 10
+
+#define OKP 1
+#define NOP 0
+
+#define CHR 1
+#define ANY 2
+#define CCL 3
+#define BOL 4
+#define EOL 5
+#define BOT 6
+#define EOT 7
+#define BOW 8
+#define EOW 9
+#define REF 10
+#define CLO 11
+
+#define END 0
+
+/*
+ * The following defines are not meant to be changeable.
+ * They are for readability only.
+ */
+#define MAXCHR 128
+#define CHRBIT 8
+#define BITBLK MAXCHR/CHRBIT
+#define BLKIND 0170
+#define BITIND 07
+
+#define ASCIIB 0177
+
+typedef unsigned char UCHAR;
+/* char, on the other hand, may be signed or unsigned;
+ * it's platform-dependent. A hard fact of life, in C.
+ */
+
+static int tagstk[MAXTAG]; /* subpat tag stack..*/
+static UCHAR nfa[MAXNFA]; /* automaton.. */
+static int sta = NOP; /* status of lastpat */
+
+static UCHAR bittab[BITBLK]; /* bit table for CCL */
+ /* pre-set bits... */
+static UCHAR bitarr[] = {1,2,4,8,16,32,64,128};
+
+#ifdef DEBUG
+static void nfadump( UCHAR *ap);
+#endif
+
+static void
+chset(UCHAR c)
+{
+ bittab[((c) & (unsigned)BLKIND) >> 3] |= bitarr[(c) & BITIND];
+}
+
+#define badpat(x) (*nfa = END, x)
+#define store(x) *mp++ = x
+
+char *
+slapd_re_comp( char *pat )
+{
+ register UCHAR *p; /* pattern pointer */
+ register UCHAR *mp=nfa; /* nfa pointer */
+ register UCHAR *lp; /* saved pointer.. */
+ register UCHAR *sp=nfa; /* another one.. */
+
+ register int tagi = 0; /* tag stack index */
+ register int tagc = 1; /* actual tag count */
+
+ register int n;
+ register UCHAR mask; /* xor mask -CCL/NCL */
+ int c1, c2;
+
+ if (!pat || !*pat) {
+ if (sta)
+ return 0;
+ else
+ return badpat("No previous regular expression");
+ }
+ sta = NOP;
+
+ for (p = (UCHAR*)pat; *p; p++) {
+ lp = mp;
+ switch(*p) {
+
+ case '.': /* match any char.. */
+ store(ANY);
+ break;
+
+ case '^': /* match beginning.. */
+ if (p == (UCHAR*)pat)
+ store(BOL);
+ else {
+ store(CHR);
+ store(*p);
+ }
+ break;
+
+ case '$': /* match endofline.. */
+ if (!*(p+1))
+ store(EOL);
+ else {
+ store(CHR);
+ store(*p);
+ }
+ break;
+
+ case '[': /* match char class..*/
+ store(CCL);
+
+ if (*++p == '^') {
+ mask = 0377;
+ p++;
+ }
+ else
+ mask = 0;
+
+ if (*p == '-') /* real dash */
+ chset(*p++);
+ if (*p == ']') /* real brac */
+ chset(*p++);
+ while (*p && *p != ']') {
+ if (*p == '-' && *(p+1) && *(p+1) != ']') {
+ p++;
+ c1 = *(p-2) + 1;
+ c2 = *p++;
+ while (c1 <= c2)
+ chset((UCHAR)c1++);
+ }
+#ifdef EXTEND
+ else if (*p == '\\' && *(p+1)) {
+ p++;
+ chset(*p++);
+ }
+#endif
+ else
+ chset(*p++);
+ }
+ if (!*p)
+ return badpat("Missing ]");
+
+ for (n = 0; n < BITBLK; bittab[n++] = (UCHAR) 0)
+ store(mask ^ bittab[n]);
+
+ break;
+
+ case '*': /* match 0 or more.. */
+ case '+': /* match 1 or more.. */
+ if (p == (UCHAR*)pat)
+ return badpat("Empty closure");
+ lp = sp; /* previous opcode */
+ if (*lp == CLO) /* equivalence.. */
+ break;
+ switch(*lp) {
+
+ case BOL:
+ case BOT:
+ case EOT:
+ case BOW:
+ case EOW:
+ case REF:
+ return badpat("Illegal closure");
+ default:
+ break;
+ }
+
+ if (*p == '+')
+ for (sp = mp; lp < sp; lp++)
+ store(*lp);
+
+ store(END);
+ store(END);
+ sp = mp;
+ while (--mp > lp)
+ *mp = mp[-1];
+ store(CLO);
+ mp = sp;
+ break;
+
+ case '\\': /* tags, backrefs .. */
+ switch(*++p) {
+
+ case '(':
+ if (tagc < MAXTAG) {
+ tagstk[++tagi] = tagc;
+ store(BOT);
+ store(tagc++);
+ }
+ else
+ return badpat("Too many \\(\\) pairs");
+ break;
+ case ')':
+ if (*sp == BOT)
+ return badpat("Null pattern inside \\(\\)");
+ if (tagi > 0) {
+ store(EOT);
+ store(tagstk[tagi--]);
+ }
+ else
+ return badpat("Unmatched \\)");
+ break;
+ case '<':
+ store(BOW);
+ break;
+ case '>':
+ if (*sp == BOW)
+ return badpat("Null pattern inside \\<\\>");
+ store(EOW);
+ break;
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9':
+ n = *p-'0';
+ if (tagi > 0 && tagstk[tagi] == n)
+ return badpat("Cyclical reference");
+ if (tagc > n) {
+ store(REF);
+ store(n);
+ }
+ else
+ return badpat("Undetermined reference");
+ break;
+#ifdef EXTEND
+ case 'b':
+ store(CHR);
+ store('\b');
+ break;
+ case 'n':
+ store(CHR);
+ store('\n');
+ break;
+ case 'f':
+ store(CHR);
+ store('\f');
+ break;
+ case 'r':
+ store(CHR);
+ store('\r');
+ break;
+ case 't':
+ store(CHR);
+ store('\t');
+ break;
+#endif
+ default:
+ store(CHR);
+ store(*p);
+ }
+ break;
+
+ default : /* an ordinary char */
+ store(CHR);
+ store(*p);
+ break;
+ }
+ sp = lp;
+ }
+ if (tagi > 0)
+ return badpat("Unmatched \\(");
+ store(END);
+ sta = OKP;
+ return 0;
+}
+
+
+static UCHAR *bol;
+static UCHAR *bopat[MAXTAG];
+static UCHAR *eopat[MAXTAG];
+#ifdef NEEDPROTOS
+static UCHAR *pmatch( UCHAR *lp, UCHAR *ap );
+#else /* NEEDPROTOS */
+static UCHAR *pmatch();
+#endif /* NEEDPROTOS */
+
+/*
+ * re_exec:
+ * execute nfa to find a match.
+ *
+ * special cases: (nfa[0])
+ * BOL
+ * Match only once, starting from the
+ * beginning.
+ * CHR
+ * First locate the character without
+ * calling pmatch, and if found, call
+ * pmatch for the remaining string.
+ * END
+ * re_comp failed, poor luser did not
+ * check for it. Fail fast.
+ *
+ * If a match is found, bopat[0] and eopat[0] are set
+ * to the beginning and the end of the matched fragment,
+ * respectively.
+ *
+ */
+
+int
+slapd_re_exec( char *lp )
+{
+ register UCHAR c;
+ register UCHAR *ep = 0;
+ register UCHAR *ap = nfa;
+
+ bol = (UCHAR*)lp;
+
+ bopat[0] = 0;
+ bopat[1] = 0;
+ bopat[2] = 0;
+ bopat[3] = 0;
+ bopat[4] = 0;
+ bopat[5] = 0;
+ bopat[6] = 0;
+ bopat[7] = 0;
+ bopat[8] = 0;
+ bopat[9] = 0;
+
+ switch(*ap) {
+
+ case BOL: /* anchored: match from BOL only */
+ ep = pmatch((UCHAR*)lp,ap);
+ break;
+ case CHR: /* ordinary char: locate it fast */
+ c = *(ap+1);
+ while (*lp && *(UCHAR*)lp != c)
+ lp++;
+ if (!*lp) /* if EOS, fail, else fall thru. */
+ return 0;
+ default: /* regular matching all the way. */
+ do {
+ if ((ep = pmatch((UCHAR*)lp,ap)))
+ break;
+ lp++;
+ } while (*lp);
+
+ break;
+ case END: /* munged automaton. fail always */
+ return 0;
+ }
+ if (!ep)
+ return 0;
+
+ bopat[0] = (UCHAR*)lp;
+ eopat[0] = ep;
+ return 1;
+}
+
+/*
+ * pmatch: internal routine for the hard part
+ *
+ * This code is partly snarfed from an early grep written by
+ * David Conroy. The backref and tag stuff, and various other
+ * innovations are by oz.
+ *
+ * special case optimizations: (nfa[n], nfa[n+1])
+ * CLO ANY
+ * We KNOW .* will match everything upto the
+ * end of line. Thus, directly go to the end of
+ * line, without recursive pmatch calls. As in
+ * the other closure cases, the remaining pattern
+ * must be matched by moving backwards on the
+ * string recursively, to find a match for xy
+ * (x is ".*" and y is the remaining pattern)
+ * where the match satisfies the LONGEST match for
+ * x followed by a match for y.
+ * CLO CHR
+ * We can again scan the string forward for the
+ * single char and at the point of failure, we
+ * execute the remaining nfa recursively, same as
+ * above.
+ *
+ * At the end of a successful match, bopat[n] and eopat[n]
+ * are set to the beginning and end of subpatterns matched
+ * by tagged expressions (n = 1 to 9).
+ *
+ */
+
+#ifndef re_fail
+void re_fail();
+#endif /* re_fail */
+
+/*
+ * character classification table for word boundary operators BOW
+ * and EOW. the reason for not using ctype macros is that we can
+ * let the user add into our own table. see re_modw. This table
+ * is not in the bitset form, since we may wish to extend it in the
+ * future for other character classifications.
+ *
+ * TRUE for 0-9 A-Z a-z _
+ */
+static char chrtyp[MAXCHR] = {
+ 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, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 0, 0,
+ 0, 0, 0, 0, 0, 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, 1, 0, 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
+ };
+
+#define inascii(x) (0177&(x))
+#define iswordc(x) chrtyp[inascii(x)]
+#define isinset(x,y) ((x)[((y)&BLKIND)>>3] & bitarr[(y)&BITIND])
+
+/*
+ * skip values for CLO XXX to skip past the closure
+ */
+
+#define ANYSKIP 2 /* [CLO] ANY END ... */
+#define CHRSKIP 3 /* [CLO] CHR chr END ... */
+#define CCLSKIP 18 /* [CLO] CCL 16bytes END ... */
+
+static UCHAR *
+pmatch( UCHAR *lp, UCHAR *ap)
+{
+ register int op, c, n;
+ register UCHAR *e; /* extra pointer for CLO */
+ register UCHAR *bp; /* beginning of subpat.. */
+ register UCHAR *ep; /* ending of subpat.. */
+ UCHAR *are; /* to save the line ptr. */
+
+ while ((op = *ap++) != END)
+ switch(op) {
+
+ case CHR:
+ if (*lp++ != *ap++)
+ return 0;
+ break;
+ case ANY:
+ if (!*lp++)
+ return 0;
+ break;
+ case CCL:
+ c = *lp++;
+ if (!isinset(ap,c))
+ return 0;
+ ap += BITBLK;
+ break;
+ case BOL:
+ if (lp != bol)
+ return 0;
+ break;
+ case EOL:
+ if (*lp)
+ return 0;
+ break;
+ case BOT:
+ bopat[*ap++] = lp;
+ break;
+ case EOT:
+ eopat[*ap++] = lp;
+ break;
+ case BOW:
+ if ((lp!=bol && iswordc(lp[-1])) || !iswordc(*lp))
+ return 0;
+ break;
+ case EOW:
+ if (lp==bol || !iswordc(lp[-1]) || iswordc(*lp))
+ return 0;
+ break;
+ case REF:
+ n = *ap++;
+ bp = bopat[n];
+ ep = eopat[n];
+ while (bp < ep)
+ if (*bp++ != *lp++)
+ return 0;
+ break;
+ case CLO:
+ are = lp;
+ switch(*ap) {
+
+ case ANY:
+ while (*lp)
+ lp++;
+ n = ANYSKIP;
+ break;
+ case CHR:
+ c = *(ap+1);
+ while (*lp && c == *lp)
+ lp++;
+ n = CHRSKIP;
+ break;
+ case CCL:
+ while ((c = *lp) && isinset(ap+1,c))
+ lp++;
+ n = CCLSKIP;
+ break;
+ default:
+ re_fail("closure: bad nfa.", *ap);
+ return 0;
+ }
+
+ ap += n;
+
+ while (lp >= are) {
+ if ((e = pmatch(lp, ap)) != NULL)
+ return e;
+ --lp;
+ }
+ return 0;
+ default:
+ re_fail("re_exec: bad nfa.", op);
+ return 0;
+ }
+ return lp;
+}
+
+/*
+ * re_modw:
+ * add new characters into the word table to change re_exec's
+ * understanding of what a word should look like. Note that we
+ * only accept additions into the word definition.
+ *
+ * If the string parameter is 0 or null string, the table is
+ * reset back to the default containing A-Z a-z 0-9 _. [We use
+ * the compact bitset representation for the default table]
+ */
+
+static UCHAR deftab[16] = {
+ 0, 0, 0, 0, 0, 0, 0377, 003, 0376, 0377, 0377, 0207,
+ 0376, 0377, 0377, 007
+};
+
+void
+slapd_re_modw( char *s )
+{
+ register int i;
+
+ if (!s || !*s) {
+ for (i = 0; i < MAXCHR; i++)
+ if (!isinset(deftab,i))
+ iswordc(i) = 0;
+ }
+ else
+ while(*s)
+ iswordc(*s++) = 1;
+}
+
+/*
+ * re_subs:
+ * substitute the matched portions of the src in dst.
+ *
+ * & substitute the entire matched pattern.
+ *
+ * \digit substitute a subpattern, with the given tag number.
+ * Tags are numbered from 1 to 9. If the particular
+ * tagged subpattern does not exist, null is substituted.
+ */
+int
+slapd_re_subs( char *src, char *dst)
+{
+ register char c;
+ register int pin;
+ register UCHAR *bp;
+ register UCHAR *ep;
+
+ if (!*src || !bopat[0])
+ return 0;
+
+ while ((c = *src++) != 0) {
+ switch(c) {
+
+ case '&':
+ pin = 0;
+ break;
+
+ case '\\':
+ c = *src++;
+ if (c >= '0' && c <= '9') {
+ pin = c - '0';
+ break;
+ }
+
+ default:
+ *dst++ = c;
+ continue;
+ }
+
+ if ((bp = bopat[pin]) && (ep = eopat[pin])) {
+ while (*bp && bp < ep)
+ *dst++ = *(char*)bp++;
+ if (bp < ep)
+ return 0;
+ }
+ }
+ *dst = (char) 0;
+ return 1;
+}
+
+#ifdef DEBUG
+/*
+ * symbolic - produce a symbolic dump of the nfa
+ */
+void
+symbolic( char *s )
+{
+ printf("pattern: %s\n", s);
+ printf("nfacode:\n");
+ nfadump(nfa);
+}
+
+static void
+nfadump( UCHAR *ap)
+{
+ register int n;
+
+ while (*ap != END)
+ switch(*ap++) {
+ case CLO:
+ printf("CLOSURE");
+ nfadump(ap);
+ switch(*ap) {
+ case CHR:
+ n = CHRSKIP;
+ break;
+ case ANY:
+ n = ANYSKIP;
+ break;
+ case CCL:
+ n = CCLSKIP;
+ break;
+ }
+ ap += n;
+ break;
+ case CHR:
+ printf("\tCHR %c\n",*ap++);
+ break;
+ case ANY:
+ printf("\tANY .\n");
+ break;
+ case BOL:
+ printf("\tBOL -\n");
+ break;
+ case EOL:
+ printf("\tEOL -\n");
+ break;
+ case BOT:
+ printf("BOT: %d\n",*ap++);
+ break;
+ case EOT:
+ printf("EOT: %d\n",*ap++);
+ break;
+ case BOW:
+ printf("BOW\n");
+ break;
+ case EOW:
+ printf("EOW\n");
+ break;
+ case REF:
+ printf("REF: %d\n",*ap++);
+ break;
+ case CCL:
+ printf("\tCCL [");
+ for (n = 0; n < MAXCHR; n++)
+ if (isinset(ap,(UCHAR)n)) {
+ if (n < ' ')
+ printf("^%c", n ^ 0x040);
+ else
+ printf("%c", n);
+ }
+ printf("]\n");
+ ap += BITBLK;
+ break;
+ default:
+ printf("bad nfa. opcode %o\n", ap[-1]);
+ exit(1);
+ break;
+ }
+}
+#endif
+#endif /* MACOS or DOS or NEED_BSDREGEX */
diff --git a/ldap/servers/slapd/resourcelimit.c b/ldap/servers/slapd/resourcelimit.c
new file mode 100644
index 00000000..3f0d3840
--- /dev/null
+++ b/ldap/servers/slapd/resourcelimit.c
@@ -0,0 +1,653 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+/* resourcelimit.c - binder-based resource limits implementation */
+
+
+/*
+ * Implementation notes:
+ *
+ * At present this code only provides support for integer-based
+ * resource limits.
+ *
+ * When a successful bind occurs (i.e., when bind_credentials_set() is
+ * called), reslimit_update_from_dn() or reslimit_update_from_entry()
+ * must be called. These functions look in the binder entry and pull
+ * out attribute values that correspond to resource limits. Typically
+ * operational attributes are used, e.g., nsSizeLimit to hold a
+ * binder-specific search size limit. The attributes should be single
+ * valued; if not, this code ignores all but the first value it finds.
+ * The virtual attribute interface is used to retrieve the binder entry
+ * values, so they can be based on COS, etc.
+ *
+ * Any resource limits found in the binder entry are cached in the
+ * connection structure. A connection object extension is used for this
+ * purpose. This means that if the attributes that correspond to binder
+ * entry are changed the resource limit won't be affected until the next
+ * bind occurs as that entry. The data in the connection extension is
+ * protected using a single writer/multiple reader locking scheme.
+ *
+ * A plugin or server subsystem that wants to use the resource limit
+ * subsystem should call slapi_reslimit_register() once for each limit it
+ * wants tracked. Note that slapi_reslimit_register() should be called
+ * early, i.e., before any client connections are accepted.
+ * slapi_reslimit_register() gives back an integer handle that is used
+ * later to refer to the limit in question. Here's a sample call:
+ */
+#if SLAPI_RESLIMIT_SAMPLE_CODE
+ static int sizelimit_reslimit_handle = -1;
+
+ if ( slapi_reslimit_register( SLAPI_RESLIMIT_TYPE_INT, "nsSizeLimit",
+ &sizelimit_reslimit_handle ) != SLAPI_RESLIMIT_STATUS_SUCCESS ) {
+ /* limit could not be registered -- fatal error? */
+ }
+ ...
+#endif
+
+/*
+ * A successful call to slapi_reslimit_register() results in a new
+ * entry in the reslimit_map, which is private to this source file.
+ * The map data structure is protected using a single writer/multiple
+ * reader locking scheme.
+ *
+ * To retrieve a binder-based limit, simple call
+ * slapi_reslimit_get_integer_limit(). If a value was present in the
+ * binder entry, it will be given back to the caller and
+ * SLAPI_RESLIMIT_STATUS_SUCCESS will be returned. If no value was
+ * present or the connection is NULL, SLAPI_RESLIMIT_STATUS_NOVALUE is
+ * returned. Other errors may be returned also. Here's a sample call:
+ */
+#if SLAPI_RESLIMIT_SAMPLE_CODE
+ int rc, sizelimit;
+
+ rc = slapi_reslimit_get_integer_limit( conn, sizelimit_reslimit_handle,
+ &sizelimit );
+
+ switch( rc ) {
+ case SLAPI_RESLIMIT_STATUS_SUCCESS: /* got a value */
+ break;
+ case SLAPI_RESLIMIT_STATUS_NOVALUE: /* no limit value available */
+ sizelimit = 500; /* use a default value */
+ break;
+ default: /* some other error occurred */
+ sizelimit = 500; /* use a default value */
+ }
+#endif
+
+/*
+ * The function reslimit_cleanup() is called from main() to dispose of
+ * memory, locks, etc. so tools like Purify() don't report leaks at exit.
+ */
+/* End of implementation notes */
+
+#include "slap.h"
+
+
+/*
+ * Macros.
+ */
+#define SLAPI_RESLIMIT_MODULE "binder-based resource limits"
+/* #define SLAPI_RESLIMIT_DEBUG */ /* define this to enable extra logging */
+ /* also forces trace log messages to */
+ /* always be logged */
+
+#ifdef SLAPI_RESLIMIT_DEBUG
+#define SLAPI_RESLIMIT_TRACELEVEL LDAP_DEBUG_ANY
+#else /* SLAPI_RESLIMIT_DEBUG */
+#define SLAPI_RESLIMIT_TRACELEVEL LDAP_DEBUG_TRACE
+#endif /* SLAPI_RESLIMIT_DEBUG */
+
+
+/*
+ * Structures and types.
+ */
+/* Per-connection resource limits data */
+typedef struct slapi_reslimit_conndata {
+ PRRWLock *rlcd_rwlock; /* to serialize access to the rest */
+ int rlcd_integer_count; /* size of rlcd_integer_limit array */
+ PRBool *rlcd_integer_available; /* array that says whether each */
+ /* value is available */
+ int *rlcd_integer_value; /* array that holds limit values */
+} SLAPIResLimitConnData;
+
+/* Mapping between attribute and limit */
+typedef struct slapi_reslimit_map {
+ int rlmap_type; /* always SLAPI_RESLIMIT_TYPE_INT for now */
+ char *rlmap_at; /* attribute type name */
+} SLAPIResLimitMap;
+
+
+/*
+ * Static variables (module globals).
+ */
+static int reslimit_inited = 0;
+static int reslimit_connext_objtype = 0;
+static int reslimit_connext_handle = 0;
+static struct slapi_reslimit_map *reslimit_map = NULL;
+static int reslimit_map_count = 0;
+static struct slapi_componentid *reslimit_componentid=NULL;
+
+/*
+ * reslimit_map_rwlock is used to serialize access to
+ * reslimit_map and reslimit_map_count
+ */
+static PRRWLock *reslimit_map_rwlock = NULL;
+
+/*
+ * Static functions.
+ */
+static int reslimit_init( void );
+static void *reslimit_connext_constructor( void *object, void *parent );
+static void reslimit_connext_destructor( void *extension, void *object,
+ void *parent );
+static int reslimit_get_ext( Slapi_Connection *conn, const char *logname,
+ SLAPIResLimitConnData **rlcdpp );
+static int reslimit_bv2int( const struct berval *bvp );
+static char ** reslimit_get_registered_attributes();
+
+
+/*
+ * reslimit_init() must be called before any resource related work
+ * is done. It is safe to call this more than once, but reslimit_inited
+ * can be tested to avoid a call.
+ *
+ * Returns zero if all goes well and non-zero if not.
+ */
+static int
+reslimit_init( void )
+{
+ if ( reslimit_inited == 0 ) {
+ if ( slapi_register_object_extension( SLAPI_RESLIMIT_MODULE,
+ SLAPI_EXT_CONNECTION, reslimit_connext_constructor,
+ reslimit_connext_destructor,
+ &reslimit_connext_objtype, &reslimit_connext_handle )
+ != 0 ) {
+ slapi_log_error( SLAPI_LOG_FATAL, SLAPI_RESLIMIT_MODULE,
+ "reslimit_init: slapi_register_object_extension()"
+ " failed\n" );
+ return( -1 );
+ }
+
+ if (( reslimit_map_rwlock = PR_NewRWLock( PR_RWLOCK_RANK_NONE,
+ "resourcelimit map rwlock" )) == NULL ) {
+ slapi_log_error( SLAPI_LOG_FATAL, SLAPI_RESLIMIT_MODULE,
+ "reslimit_init: PR_NewRWLock() failed\n" );
+ return( -1 );
+ }
+
+ reslimit_inited = 1;
+ }
+
+ reslimit_componentid=generate_componentid(NULL,COMPONENT_RESLIMIT);
+
+ return( 0 );
+}
+
+
+
+
+/*
+ * Dispose of any allocated memory, locks, other resources. Called when
+ * server is shutting down.
+ */
+void
+reslimit_cleanup( void )
+{
+ int i;
+
+ if ( reslimit_map != NULL ) {
+ for ( i = 0; i < reslimit_map_count; ++i ) {
+ if ( reslimit_map[ i ].rlmap_at != NULL ) {
+ slapi_ch_free( (void **)&reslimit_map[ i ].rlmap_at );
+ }
+ }
+ slapi_ch_free( (void **)&reslimit_map );
+ }
+
+ if ( reslimit_map_rwlock != NULL ) {
+ PR_DestroyRWLock( reslimit_map_rwlock );
+ }
+
+ if ( reslimit_componentid != NULL ) {
+ release_componentid( reslimit_componentid );
+ }
+}
+
+
+/*
+ * constructor for the connection object extension.
+ */
+static void *
+reslimit_connext_constructor( void *object, void *parent )
+{
+ SLAPIResLimitConnData *rlcdp;
+ PRRWLock *rwlock;
+
+ if (( rwlock = PR_NewRWLock( PR_RWLOCK_RANK_NONE,
+ "resource limit connection data rwlock" )) == NULL ) {
+ slapi_log_error( SLAPI_LOG_FATAL, SLAPI_RESLIMIT_MODULE,
+ "reslimit_connext_constructor: PR_NewRWLock() failed\n" );
+ return( NULL );
+ }
+
+ rlcdp = (SLAPIResLimitConnData *)slapi_ch_calloc( 1,
+ sizeof( SLAPIResLimitConnData ));
+ rlcdp->rlcd_rwlock = rwlock;
+
+ return( rlcdp );
+}
+
+
+/*
+ * destructor for the connection object extension.
+ */
+static void
+reslimit_connext_destructor( void *extension, void *object, void *parent )
+{
+ SLAPIResLimitConnData *rlcdp = (SLAPIResLimitConnData *)extension;
+
+ if ( rlcdp->rlcd_integer_available != NULL ) {
+ slapi_ch_free( (void **)&rlcdp->rlcd_integer_available );
+ }
+ if ( rlcdp->rlcd_integer_value != NULL ) {
+ slapi_ch_free( (void **)&rlcdp->rlcd_integer_value );
+ }
+ PR_DestroyRWLock( rlcdp->rlcd_rwlock );
+ slapi_ch_free( (void **)&rlcdp );
+}
+
+
+/*
+ * utility function to retrieve the connection object extension.
+ *
+ * if logname is non-NULL, errors are logged.
+ */
+static int
+reslimit_get_ext( Slapi_Connection *conn, const char *logname,
+ SLAPIResLimitConnData **rlcdpp )
+{
+ if ( !reslimit_inited && reslimit_init() != 0 ) {
+ if ( NULL != logname ) {
+ slapi_log_error( SLAPI_LOG_FATAL, SLAPI_RESLIMIT_MODULE,
+ "%s: reslimit_init() failed\n", logname );
+ }
+ return( SLAPI_RESLIMIT_STATUS_INIT_FAILURE );
+ }
+
+ if (( *rlcdpp = (SLAPIResLimitConnData *)slapi_get_object_extension(
+ reslimit_connext_objtype, conn,
+ reslimit_connext_handle )) == NULL ) {
+ if ( NULL != logname ) {
+ slapi_log_error( SLAPI_LOG_FATAL, SLAPI_RESLIMIT_MODULE,
+ "%s: slapi_get_object_extension() returned NULL\n", logname );
+ }
+ return( SLAPI_RESLIMIT_STATUS_INTERNAL_ERROR );
+ }
+
+ return( SLAPI_RESLIMIT_STATUS_SUCCESS );
+}
+
+
+/*
+ * utility function to convert a string-represented integer to an int.
+ *
+ * XXXmcs: wouldn't need this if slapi_value_get_int() returned a signed int!
+ */
+static int
+reslimit_bv2int( const struct berval *bvp )
+{
+ int rc = 0;
+ char smallbuf[ 25 ], *buf;
+
+ if ( bvp != NULL ) {
+ /* make a copy to ensure it is zero-terminated */
+ if ( bvp->bv_len < sizeof( smallbuf )) {
+ buf = smallbuf;
+ } else {
+ buf = slapi_ch_malloc( bvp->bv_len + 1 );
+ }
+ memcpy( buf, bvp->bv_val, bvp->bv_len);
+ buf[ bvp->bv_len ] = '\0';
+
+ rc = atoi( buf );
+
+ if ( buf != smallbuf ) {
+ slapi_ch_free( (void **)&smallbuf );
+ }
+ }
+
+ return( rc );
+}
+
+
+/**** Semi-public functions start here ***********************************/
+/*
+ * These functions are exposed to other parts of the server only, i.e.,
+ * they are NOT part of the official SLAPI API.
+ */
+
+/*
+ * Set the resource limits associated with connection `conn' based on the
+ * entry named by `dn'. If `dn' is NULL, limits are returned to their
+ * default state.
+ *
+ * A SLAPI_RESLIMIT_STATUS_... code is returned.
+ */
+int
+reslimit_update_from_dn( Slapi_Connection *conn, Slapi_DN *dn )
+{
+ Slapi_Entry *e;
+ int rc;
+
+ e = NULL;
+ if ( dn != NULL ) {
+
+ char ** attrs = reslimit_get_registered_attributes();
+ (void) slapi_search_internal_get_entry( dn, attrs, &e , reslimit_componentid);
+ charray_free(attrs);
+ }
+
+ rc = reslimit_update_from_entry( conn, e );
+
+ if ( NULL != e ) {
+ slapi_entry_free( e );
+ }
+
+ return( rc );
+}
+
+
+/*
+ * Set the resource limits associated with connection `conn' based on the
+ * entry `e'. If `e' is NULL, limits are returned to their default state.
+ * If `conn' is NULL, nothing is done.
+ *
+ * A SLAPI_RESLIMIT_STATUS_... code is returned.
+ */
+int
+reslimit_update_from_entry( Slapi_Connection *conn, Slapi_Entry *e )
+{
+ char *fnname = "reslimit_update_from_entry()";
+ char *actual_type_name, *get_ext_logname;
+ int i, rc, type_name_disposition, free_flags;
+ SLAPIResLimitConnData *rlcdp;
+ Slapi_ValueSet *vs;
+
+ LDAPDebug( SLAPI_RESLIMIT_TRACELEVEL, "=> %s conn=0x%x, entry=0x%x\n",
+ fnname, conn, e );
+
+ rc = SLAPI_RESLIMIT_STATUS_SUCCESS; /* optimistic */
+
+ /* if conn is NULL, there is nothing to be done */
+ if ( conn == NULL ) {
+ goto log_and_return;
+ }
+
+
+ if ( NULL == e ) {
+ get_ext_logname = NULL; /* do not log errors if resetting limits */
+ } else {
+ get_ext_logname = fnname;
+ }
+ if (( rc = reslimit_get_ext( conn, get_ext_logname, &rlcdp )) !=
+ SLAPI_RESLIMIT_STATUS_SUCCESS ) {
+ goto log_and_return;
+ }
+
+ /* LOCK FOR READ -- map lock */
+ PR_RWLock_Rlock( reslimit_map_rwlock );
+ /* LOCK FOR WRITE -- conn. data lock */
+ PR_RWLock_Wlock( rlcdp->rlcd_rwlock );
+
+ if ( rlcdp->rlcd_integer_value == NULL ) {
+ rlcdp->rlcd_integer_count = reslimit_map_count;
+ if ( rlcdp->rlcd_integer_count > 0 ) {
+ rlcdp->rlcd_integer_available = (PRBool *)slapi_ch_calloc(
+ rlcdp->rlcd_integer_count, sizeof( PRBool ));
+ rlcdp->rlcd_integer_value = (int *)slapi_ch_calloc(
+ rlcdp->rlcd_integer_count, sizeof( int ));
+ }
+ }
+
+ for ( i = 0; i < rlcdp->rlcd_integer_count; ++i ) {
+ if ( reslimit_map[ i ].rlmap_type != SLAPI_RESLIMIT_TYPE_INT ) {
+ continue;
+ }
+
+ LDAPDebug( SLAPI_RESLIMIT_TRACELEVEL,
+ "%s: setting limit for handle %d (based on %s)\n",
+ fnname, i, reslimit_map[ i ].rlmap_at );
+
+ rlcdp->rlcd_integer_available[ i ] = PR_FALSE;
+
+ if ( NULL != e && 0 == slapi_vattr_values_get( e,
+ reslimit_map[ i ].rlmap_at, &vs, &type_name_disposition,
+ &actual_type_name, 0, &free_flags )) {
+ Slapi_Value *v;
+ int index;
+ const struct berval *bvp;
+
+ if (( index = slapi_valueset_first_value( vs, &v )) != -1 &&
+ ( bvp = slapi_value_get_berval( v )) != NULL ) {
+ rlcdp->rlcd_integer_value[ i ] = reslimit_bv2int( bvp );
+ rlcdp->rlcd_integer_available[ i ] = PR_TRUE;
+
+ LDAPDebug( SLAPI_RESLIMIT_TRACELEVEL,
+ "%s: set limit based on %s to %d\n",
+ fnname, reslimit_map[ i ].rlmap_at,
+ rlcdp->rlcd_integer_value[ i ] );
+
+ if ( slapi_valueset_next_value( vs, index, &v ) != -1 ) {
+ char ebuf[ BUFSIZ ];
+ slapi_log_error( SLAPI_LOG_FATAL, SLAPI_RESLIMIT_MODULE,
+ "%s: ignoring multiple values for %s in entry \n",
+ fnname, reslimit_map[ i ].rlmap_at,
+ escape_string( slapi_entry_get_dn_const( e ),
+ ebuf ));
+ }
+ }
+
+ slapi_vattr_values_free( &vs, &actual_type_name, free_flags);
+ }
+ }
+
+ PR_RWLock_Unlock( rlcdp->rlcd_rwlock );
+ /* UNLOCKED -- conn. data lock */
+ PR_RWLock_Unlock( reslimit_map_rwlock );
+ /* UNLOCKED -- map lock */
+
+log_and_return:
+ LDAPDebug( SLAPI_RESLIMIT_TRACELEVEL, "<= %s returning status %d\n",
+ fnname, rc, 0 );
+
+ return( rc );
+}
+
+/* return the list of registered attributes */
+
+static char ** reslimit_get_registered_attributes()
+{
+
+ int i;
+ char **attrs=NULL;
+
+ /* LOCK FOR READ -- map lock */
+ PR_RWLock_Rlock( reslimit_map_rwlock );
+
+ for ( i = 0; i < reslimit_map_count; ++i ) {
+ if ( reslimit_map[ i ].rlmap_at != NULL ) {
+ charray_add(&attrs, slapi_ch_strdup(reslimit_map[ i ].rlmap_at));
+ }
+ }
+
+ PR_RWLock_Unlock( reslimit_map_rwlock );
+
+ return attrs;
+}
+
+
+/**** Public functions can be found below this point *********************/
+/*
+ * These functions are exposed to plugins, i.e., they are part of the
+ * official SLAPI API.
+ */
+
+/*
+ * Register a new resource to be tracked. `type' must be
+ * SLAPI_RESLIMIT_TYPE_INT and `attrname' is an LDAP attribute type that
+ * is consulted in the bound entry to determine the limit's value.
+ *
+ * A SLAPI_RESLIMIT_STATUS_... code is returned. If it is ...SUCCESS, then
+ * `*handlep' is set to an opaque integer value that should be used in
+ * subsequent calls to slapi_reslimit_get_integer_limit().
+ */
+int
+slapi_reslimit_register( int type, const char *attrname, int *handlep )
+{
+ char *fnname = "slapi_reslimit_register()";
+ int i, rc;
+
+ LDAPDebug( SLAPI_RESLIMIT_TRACELEVEL, "=> %s attrname=%s\n",
+ fnname, attrname, 0 );
+
+ rc = SLAPI_RESLIMIT_STATUS_SUCCESS; /* optimistic */
+
+ /* initialize if necessary */
+ if ( !reslimit_inited && reslimit_init() != 0 ) {
+ slapi_log_error( SLAPI_LOG_FATAL, SLAPI_RESLIMIT_MODULE,
+ "%s: reslimit_init() failed\n", fnname );
+ rc = SLAPI_RESLIMIT_STATUS_INIT_FAILURE;
+ goto log_and_return;
+ }
+
+ /* sanity check parameters */
+ if ( type != SLAPI_RESLIMIT_TYPE_INT || attrname == NULL
+ || handlep == NULL ) {
+ slapi_log_error( SLAPI_LOG_FATAL, SLAPI_RESLIMIT_MODULE,
+ "%s: parameter error\n", fnname );
+ rc = SLAPI_RESLIMIT_STATUS_PARAM_ERROR;
+ goto log_and_return;
+ }
+
+ /* LOCK FOR WRITE -- map lock */
+ PR_RWLock_Wlock( reslimit_map_rwlock );
+
+ /*
+ * check that attrname is not already registered
+ */
+ for ( i = 0; i < reslimit_map_count; ++i ) {
+ if ( 0 == slapi_attr_type_cmp( reslimit_map[ i ].rlmap_at,
+ attrname, SLAPI_TYPE_CMP_EXACT )) {
+ slapi_log_error( SLAPI_LOG_FATAL, SLAPI_RESLIMIT_MODULE,
+ "%s: parameter error (%s already registered)\n",
+ attrname, fnname );
+ rc = SLAPI_RESLIMIT_STATUS_PARAM_ERROR;
+ goto unlock_and_return;
+ }
+ }
+
+ /*
+ * expand the map array and add the new element
+ */
+ reslimit_map = (SLAPIResLimitMap *)slapi_ch_realloc(
+ (char *)reslimit_map,
+ (1 + reslimit_map_count) * sizeof( SLAPIResLimitMap ));
+ reslimit_map[ reslimit_map_count ].rlmap_type = type;
+ reslimit_map[ reslimit_map_count ].rlmap_at
+ = slapi_ch_strdup( attrname );
+ *handlep = reslimit_map_count;
+ ++reslimit_map_count;
+
+unlock_and_return:
+ PR_RWLock_Unlock( reslimit_map_rwlock );
+ /* UNLOCKED -- map lock */
+
+log_and_return:
+ LDAPDebug( SLAPI_RESLIMIT_TRACELEVEL,
+ "<= %s returning status=%d, handle=%d\n", fnname, rc,
+ (handlep == NULL) ? -1 : *handlep );
+
+ return( rc );
+}
+
+
+/*
+ * Retrieve the integer limit associated with connection `conn' for
+ * the resource identified by `handle'.
+ *
+ * A SLAPI_RESLIMIT_STATUS_... code is returned:
+ *
+ * SLAPI_RESLIMIT_STATUS_SUCCESS -- `*limitp' is set to the limit value.
+ * SLAPI_RESLIMIT_STATUS_NOVALUE -- no limit value is available (use default).
+ * Another SLAPI_RESLIMIT_STATUS_... code -- some more fatal error occurred.
+ *
+ * If `conn' is NULL, SLAPI_RESLIMIT_STATUS_NOVALUE is returned.
+ */
+int
+slapi_reslimit_get_integer_limit( Slapi_Connection *conn, int handle,
+ int *limitp )
+{
+ char *fnname = "slapi_reslimit_get_integer_limit()";
+ int rc;
+ SLAPIResLimitConnData *rlcdp;
+
+ LDAPDebug( SLAPI_RESLIMIT_TRACELEVEL, "=> %s conn=0x%x, handle=%d\n",
+ fnname, conn, handle );
+
+ rc = SLAPI_RESLIMIT_STATUS_SUCCESS; /* optimistic */
+
+ /* sanity check parameters */
+ if ( limitp == NULL ) {
+ slapi_log_error( SLAPI_LOG_FATAL, SLAPI_RESLIMIT_MODULE,
+ "%s: parameter error\n", fnname );
+ rc = SLAPI_RESLIMIT_STATUS_PARAM_ERROR;
+ goto log_and_return;
+ }
+
+ if ( conn == NULL ) {
+ rc = SLAPI_RESLIMIT_STATUS_NOVALUE;
+ goto log_and_return;
+ }
+
+ if (( rc = reslimit_get_ext( conn, fnname, &rlcdp )) !=
+ SLAPI_RESLIMIT_STATUS_SUCCESS ) {
+ goto log_and_return;
+ }
+ if(rlcdp->rlcd_integer_count==0) { /* peek at it to avoid lock */
+ rc = SLAPI_RESLIMIT_STATUS_NOVALUE;
+ } else {
+ PR_RWLock_Rlock( rlcdp->rlcd_rwlock );
+ if(rlcdp->rlcd_integer_count==0) {
+ rc = SLAPI_RESLIMIT_STATUS_NOVALUE;
+ } else if ( handle < 0 || handle >= rlcdp->rlcd_integer_count ) {
+ slapi_log_error( SLAPI_LOG_FATAL, SLAPI_RESLIMIT_MODULE,
+ "%s: unknown handle %d\n", fnname, handle );
+ rc = SLAPI_RESLIMIT_STATUS_UNKNOWN_HANDLE;
+ } else if ( rlcdp->rlcd_integer_available[ handle ] ) {
+ *limitp = rlcdp->rlcd_integer_value[ handle ];
+ } else {
+ rc = SLAPI_RESLIMIT_STATUS_NOVALUE;
+ }
+ PR_RWLock_Unlock( rlcdp->rlcd_rwlock );
+ }
+
+
+log_and_return:
+ if ( LDAPDebugLevelIsSet( LDAP_DEBUG_TRACE )) {
+ if ( rc == SLAPI_RESLIMIT_STATUS_SUCCESS ) {
+ LDAPDebug( SLAPI_RESLIMIT_TRACELEVEL,
+ "<= %s returning SUCCESS, value=%d\n", fnname, *limitp, 0 );
+ } else if ( rc == SLAPI_RESLIMIT_STATUS_NOVALUE ) {
+ LDAPDebug( SLAPI_RESLIMIT_TRACELEVEL, "<= %s returning NO VALUE\n",
+ fnname, 0, 0 );
+ } else {
+ LDAPDebug( SLAPI_RESLIMIT_TRACELEVEL, "<= %s returning ERROR %d\n",
+ fnname, rc, 0 );
+ }
+ }
+
+ return( rc );
+}
+/**** Public functions END ***********************************************/
diff --git a/ldap/servers/slapd/result.c b/ldap/servers/slapd/result.c
new file mode 100644
index 00000000..b8be99f7
--- /dev/null
+++ b/ldap/servers/slapd/result.c
@@ -0,0 +1,1794 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+/* result.c - routines to send ldap results, errors, and referrals */
+
+#include <stdio.h>
+#include <string.h>
+#include <sys/types.h>
+#ifndef _WIN32
+#include <sys/time.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <unistd.h>
+#endif
+#include <errno.h>
+#include <signal.h>
+#include "slap.h"
+#include "pratom.h"
+#include "fe.h"
+#include "vattr_spi.h"
+
+
+#if defined( NET_SSL )
+#include <ssl.h>
+#endif
+
+PRUint64 num_entries_sent;
+PRUint64 num_bytes_sent;
+PRLock *num_sent_mutex;
+
+static long current_conn_count;
+static PRLock *current_conn_count_mutex;
+
+static int flush_ber( Slapi_PBlock *pb, Connection *conn,
+ Operation *op, BerElement *ber, int type );
+static char *notes2str( unsigned int notes, char *buf, size_t buflen );
+static void log_result( Slapi_PBlock *pb, Operation *op, int err,
+ unsigned long tag, int nentries );
+static void log_entry( Operation *op, Slapi_Entry *e );
+static void log_referral( Operation *op );
+
+#define _LDAP_SEND_RESULT 0
+#define _LDAP_SEND_REFERRAL 1
+#define _LDAP_SEND_ENTRY 2
+
+#define SLAPI_SEND_VATTR_FLAG_REALONLY 0x01
+#define SLAPI_SEND_VATTR_FLAG_VIRTUALONLY 0x02
+
+void g_set_num_entries_sent( PRUint64 val )
+{
+ num_entries_sent = val;
+}
+
+PRUint64 g_get_num_entries_sent()
+{
+ return( num_entries_sent );
+}
+
+void g_set_num_bytes_sent( PRUint64 val )
+{
+ num_bytes_sent = val;
+}
+
+PRUint64 g_get_num_bytes_sent()
+{
+ return( num_bytes_sent );
+}
+
+void g_set_num_sent_mutex( PRLock *plock )
+{
+ num_sent_mutex = plock;
+}
+
+PRLock *g_get_num_sent_mutex()
+{
+ return( num_sent_mutex );
+}
+
+static void
+delete_default_referral(struct berval **referrals)
+{
+ if (referrals)
+ {
+ int ii = 0;
+ for (ii = 0; referrals[ii]; ++ii)
+ ber_bvfree(referrals[ii]);
+ slapi_ch_free((void**)&referrals);
+ }
+}
+
+void
+g_set_default_referral( struct berval **ldap_url ) {
+ struct berval **default_referral;
+ slapdFrontendConfig_t *slapdFrontendConfig = getFrontendConfig();
+ int nReferrals;
+
+ /* check to see if we want to delete all referrals */
+ if ( ldap_url && ldap_url[0] &&
+ strcasecmp ( (char *)ldap_url[0]->bv_val, REFERRAL_REMOVE_CMD) == 0 ) {
+ delete_default_referral(slapdFrontendConfig->defaultreferral);
+ slapdFrontendConfig->defaultreferral = NULL;
+ return;
+ }
+
+ /* count the number of referrals */
+ for ( nReferrals = 0; ldap_url && ldap_url[nReferrals]; nReferrals++ )
+ ;
+
+ default_referral = (struct berval **)
+ slapi_ch_malloc( (nReferrals + 1) * sizeof(struct berval *) );
+
+ /* terminate the end, and add the referrals backwards */
+ default_referral[nReferrals--] = NULL;
+
+ while ( nReferrals >= 0 ) {
+ default_referral[nReferrals] = ber_bvdup(ldap_url[nReferrals]);
+ nReferrals--;
+ }
+
+ delete_default_referral(slapdFrontendConfig->defaultreferral);
+ slapdFrontendConfig->defaultreferral = default_referral;
+}
+
+struct berval**
+g_get_default_referral() {
+ slapdFrontendConfig_t *slapdFrontendConfig = getFrontendConfig();
+ return slapdFrontendConfig->defaultreferral;
+}
+
+/*
+ * routines to manage keeping track of the current number of connections
+ * to the server. this information is used by the listener thread to
+ * determine when to stop listening for new connections, which it does
+ * when the total number of descriptors available minus the number of
+ * current connections drops below the reservedescriptors mark.
+ */
+
+void g_set_current_conn_count_mutex( PRLock *plock )
+{
+ PR_ASSERT( NULL != plock );
+
+ current_conn_count_mutex = plock;
+}
+
+PRLock *g_get_current_conn_count_mutex()
+{
+ return( current_conn_count_mutex );
+}
+
+long g_get_current_conn_count()
+{
+ long tmp;
+
+ PR_ASSERT( NULL != current_conn_count_mutex );
+
+ PR_Lock( current_conn_count_mutex );
+ tmp = current_conn_count;
+ PR_Unlock( current_conn_count_mutex );
+
+ return( tmp );
+}
+
+void g_increment_current_conn_count()
+{
+ PR_ASSERT( NULL != current_conn_count_mutex );
+
+ PR_Lock( current_conn_count_mutex );
+ current_conn_count++;
+ PR_Unlock( current_conn_count_mutex );
+}
+
+void g_decrement_current_conn_count()
+{
+ PR_ASSERT( NULL != current_conn_count_mutex );
+
+ PR_Lock( current_conn_count_mutex );
+ current_conn_count--;
+/* PR_ASSERT( current_conn_count >= 0 ); JCM BASTARD */
+ PR_Unlock( current_conn_count_mutex );
+}
+
+
+void
+send_ldap_result(
+ Slapi_PBlock *pb,
+ int err,
+ char *matched,
+ char *text,
+ int nentries,
+ struct berval **urls
+)
+{
+ send_ldap_result_ext(pb, err, matched, text, nentries, urls, NULL);
+}
+
+
+static int
+check_and_send_extended_result(Slapi_PBlock *pb, unsigned long tag, BerElement *ber)
+{
+ /*
+ * if this is an LDAPv3 ExtendedResponse to an ExtendedRequest,
+ * check to see if the optional responseName and response OCTET
+ * STRING need to be appended.
+ */
+ int rc= 0;
+ char *exop_oid;
+ struct berval *exop_value;
+ slapi_pblock_get(pb, SLAPI_EXT_OP_RET_OID, &exop_oid);
+ slapi_pblock_get(pb, SLAPI_EXT_OP_RET_VALUE, &exop_value);
+ if ( LDAP_RES_EXTENDED == tag ) {
+ if (exop_oid != NULL) {
+ rc = ber_printf( ber, "ts",
+ LDAP_TAG_EXOP_RES_OID, exop_oid);
+ }
+ if (rc != LBER_ERROR && exop_value != NULL) {
+ rc = ber_printf( ber, "to",
+ LDAP_TAG_EXOP_RES_VALUE,
+ exop_value->bv_val,
+ exop_value->bv_len );
+ }
+ }
+ return rc;
+}
+
+static int
+check_and_send_SASL_response(Slapi_PBlock *pb, unsigned long tag, BerElement *ber, Connection *conn)
+{
+ /*
+ * if this is an LDAPv3 BindResponse, check to see if the
+ * optional serverSaslCreds OCTET STRING is present and needs
+ * to be appended.
+ */
+ int rc= 0;
+ if ( LDAP_RES_BIND == tag && conn->c_ldapversion >= LDAP_VERSION3 )
+ {
+ struct berval *bind_ret_saslcreds; /* v3 serverSaslCreds */
+ slapi_pblock_get(pb, SLAPI_BIND_RET_SASLCREDS, &bind_ret_saslcreds);
+ if ( bind_ret_saslcreds != NULL ) {
+ rc = ber_printf( ber, "to",
+ LDAP_TAG_SASL_RES_CREDS,
+ bind_ret_saslcreds->bv_val,
+ bind_ret_saslcreds->bv_len );
+ }
+ }
+ return rc;
+}
+
+
+/*
+ * the input ber, if present, is not consumed
+ */
+void
+send_ldap_result_ext(
+ Slapi_PBlock *pb,
+ int err,
+ char *matched,
+ char *text,
+ int nentries,
+ struct berval **urls,
+ BerElement *ber
+)
+{
+ Connection *conn = pb->pb_conn;
+ int i, rc, logit = 0;
+ unsigned long tag;
+ int flush_ber_element = 1;
+ Slapi_Operation *operation;
+ char *dn;
+ passwdPolicy *pwpolicy = NULL;
+
+
+ slapi_pblock_get( pb, SLAPI_TARGET_DN, &dn );
+ pwpolicy = new_passwdPolicy(pb, dn);
+
+ slapi_pblock_get (pb, SLAPI_OPERATION, &operation);
+
+ if ( ber != NULL ) {
+ flush_ber_element = 0;
+ }
+
+
+ if(err != LDAP_SUCCESS){
+ /* count the error for snmp */
+ /* first check for security errors */
+ if( err == LDAP_INVALID_CREDENTIALS
+ || err == LDAP_INAPPROPRIATE_AUTH
+ || err == LDAP_AUTH_METHOD_NOT_SUPPORTED
+ || err == LDAP_STRONG_AUTH_NOT_SUPPORTED
+ || err == LDAP_STRONG_AUTH_REQUIRED
+ || err == LDAP_CONFIDENTIALITY_REQUIRED
+ || err == LDAP_INSUFFICIENT_ACCESS
+ || err == LDAP_AUTH_UNKNOWN )
+ {
+ if(g_get_global_snmp_vars()->ops_tbl.dsSecurityErrors!=NULL)
+ PR_AtomicIncrement(g_get_global_snmp_vars()->ops_tbl.dsSecurityErrors);
+ }else if( err != LDAP_REFERRAL
+ && err != LDAP_OPT_REFERRALS
+ && err != LDAP_PARTIAL_RESULTS)
+ {
+ /*madman man spec says not to count as normal errors
+ --security errors
+ --referrals
+ -- partially seviced operations will not be conted as an error
+ */
+ if(g_get_global_snmp_vars()->ops_tbl.dsErrors!=NULL)
+ PR_AtomicIncrement(g_get_global_snmp_vars()->ops_tbl.dsErrors);
+ }
+
+ }
+
+ LDAPDebug( LDAP_DEBUG_TRACE, "=> send_ldap_result %d:%s:%s\n", err,
+ matched ? matched : "", text ? text : "" );
+
+ switch ( operation->o_tag ) {
+ case LBER_DEFAULT:
+ tag = LBER_SEQUENCE;
+ break;
+
+ case LDAP_REQ_SEARCH:
+ tag = LDAP_RES_SEARCH_RESULT;
+ break;
+
+ case LDAP_REQ_DELETE:
+ tag = LDAP_RES_DELETE;
+ break;
+
+ case LDAP_REFERRAL:
+ if ( conn->c_ldapversion > LDAP_VERSION2 ) {
+ tag = LDAP_TAG_REFERRAL;
+ break;
+ }
+ /* fallthru */
+
+ default:
+ tag = operation->o_tag + 1;
+ break;
+ }
+
+ if ( conn == NULL ) {
+ if ( operation->o_result_handler != NULL ) {
+ operation->o_result_handler( conn, operation, err,
+ matched, text, nentries, urls );
+ logit = 1;
+ }
+ goto log_and_return;
+ }
+
+ /* invalid password. Update the password retry here */
+ /* put this here for now. It could be a send_result pre-op plugin. */
+ if ( err == LDAP_INVALID_CREDENTIALS &&
+ pwpolicy->pw_lockout == 1 ) {
+
+ update_pw_retry ( pb );
+ }
+
+ if ( ber == NULL ) {
+ if ( (ber = der_alloc()) == NULL ) {
+ LDAPDebug( LDAP_DEBUG_ANY, "ber_alloc failed\n", 0, 0, 0 );
+ goto log_and_return;
+ }
+ }
+
+ /* there is no admin limit exceeded in v2 - change to size limit XXX */
+ if ( err == LDAP_ADMINLIMIT_EXCEEDED &&
+ conn->c_ldapversion < LDAP_VERSION3 ) {
+ err = LDAP_SIZELIMIT_EXCEEDED;
+ }
+
+ if ( conn->c_ldapversion < LDAP_VERSION3 || urls == NULL ) {
+ char *save, *buf = NULL;
+
+ /*
+ * if there are v2 referrals to send, construct
+ * the v2 referral string.
+ */
+ if ( urls != NULL ) {
+ int len;
+
+ /* count the referral */
+ PR_AtomicIncrement(g_get_global_snmp_vars()->ops_tbl.dsReferrals);
+
+ /*
+ * figure out how much space we need
+ */
+ len = 10; /* strlen("Referral:") + NULL */
+ for ( i = 0; urls[i] != NULL; i++ ) {
+ len += urls[i]->bv_len + 1; /* newline + ref */
+ }
+ if ( text != NULL ) {
+ len += strlen( text ) + 1; /* text + newline */
+ }
+ /*
+ * allocate buffer and fill it in with the error
+ * message plus v2-style referrals.
+ */
+ buf = slapi_ch_malloc( len );
+ *buf = '\0';
+ if ( text != NULL ) {
+ strcpy( buf, text );
+ strcat( buf, "\n" );
+ }
+ strcat( buf, "Referral:" );
+ for ( i = 0; urls[i] != NULL; i++ ) {
+ strcat( buf, "\n" );
+ strcat( buf, urls[i]->bv_val );
+ }
+ save = text;
+ text = buf;
+ }
+
+ if ( (conn->c_ldapversion < LDAP_VERSION3 &&
+ err == LDAP_REFERRAL) || urls != NULL ) {
+ err = LDAP_PARTIAL_RESULTS;
+ }
+ rc = ber_printf( ber, "{it{ess", operation->o_msgid, tag, err,
+ matched ? matched : "", text ? text : "" );
+
+ /*
+ * if this is an LDAPv3 ExtendedResponse to an ExtendedRequest,
+ * check to see if the optional responseName and response OCTET
+ * STRING need to be appended.
+ */
+ if ( rc != LBER_ERROR )
+ {
+ rc= check_and_send_extended_result(pb, tag, ber);
+ }
+
+ /*
+ * if this is an LDAPv3 BindResponse, check to see if the
+ * optional serverSaslCreds OCTET STRING is present and needs
+ * to be appended.
+ */
+ if ( rc != LBER_ERROR )
+ {
+ rc= check_and_send_SASL_response(pb, tag, ber, conn);
+/* XXXmcs: should we also check for a missing auth response control? */
+ }
+
+ if ( rc != LBER_ERROR ) {
+ rc = ber_printf( ber, "}" ); /* one more } to come */
+ }
+
+ if ( buf != NULL ) {
+ text = save;
+ slapi_ch_free( (void**)&buf );
+ }
+ } else {
+ /*
+ * there are v3 referrals to add to the result
+ */
+ /* count the referral */
+ if (! config_check_referral_mode())
+ PR_AtomicIncrement(g_get_global_snmp_vars()->ops_tbl.dsReferrals);
+ rc = ber_printf( ber, "{it{esst{s", operation->o_msgid, tag, err,
+ matched ? matched : "", text ? text : "", LDAP_TAG_REFERRAL,
+ urls[0]->bv_val );
+ for ( i = 1 ; urls[i] != NULL && rc != LBER_ERROR; i++ ) {
+ rc = ber_printf( ber, "s", urls[i]->bv_val );
+ }
+ if ( rc != LBER_ERROR ) {
+ rc = ber_printf( ber, "}" ); /* two more } to come */
+ }
+
+ /*
+ * if this is an LDAPv3 ExtendedResponse to an ExtendedRequest,
+ * check to see if the optional responseName and response OCTET
+ * STRING need to be appended.
+ */
+ if ( rc != LBER_ERROR )
+ {
+ rc= check_and_send_extended_result(pb, tag, ber);
+ }
+
+ /*
+ * if this is an LDAPv3 BindResponse, check to see if the
+ * optional serverSaslCreds OCTET STRING is present and needs
+ * to be appended.
+ */
+ if ( rc != LBER_ERROR )
+ {
+ rc= check_and_send_SASL_response(pb, tag, ber, conn);
+ }
+
+ if ( rc != LBER_ERROR ) {
+ rc = ber_printf( ber, "}" ); /* one more } to come */
+ }
+ }
+ if ( operation->o_results.result_controls != NULL
+ && conn->c_ldapversion >= LDAP_VERSION3
+ && write_controls( ber, operation->o_results.result_controls ) != 0 ) {
+ rc = LBER_ERROR;
+ }
+
+ if ( rc != LBER_ERROR ) { /* end the LDAPMessage sequence */
+ rc = ber_put_seq( ber );
+ }
+
+ if ( rc == LBER_ERROR ) {
+ LDAPDebug( LDAP_DEBUG_ANY, "ber_printf failed\n", 0, 0, 0 );
+ if (flush_ber_element == 1) {
+ /* we alloced the ber */
+ ber_free( ber, 1 /* freebuf */ );
+ }
+ goto log_and_return;
+ }
+
+ if ( flush_ber_element ) {
+ /* write only one pdu at a time - wait til it's our turn */
+ if ( flush_ber( pb, conn, operation, ber, _LDAP_SEND_RESULT ) == 0 ) {
+ logit = 1;
+ }
+ }
+
+log_and_return:
+ operation->o_status = SLAPI_OP_STATUS_RESULT_SENT; /* in case this has not yet been set */
+
+ if ( logit && operation_is_flag_set( operation,
+ OP_FLAG_ACTION_LOG_ACCESS )) {
+ log_result( pb, operation, err, tag, nentries );
+ }
+
+ delete_passwdPolicy (&pwpolicy);
+ LDAPDebug( LDAP_DEBUG_TRACE, "<= send_ldap_result\n", 0, 0, 0 );
+}
+
+
+void
+send_nobackend_ldap_result( Slapi_PBlock *pb )
+{
+ struct berval **refurls;
+ int err;
+
+ refurls = g_get_default_referral();
+ err = ( refurls == NULL ) ? LDAP_NO_SUCH_OBJECT : LDAP_REFERRAL;
+ /* richm 20010831 - bug 556992 - the post op code needs to know what the
+ ldap error sent to the client was - slapi_send_ldap_result sets the
+ err in the pblock, so this function needs to also */
+ slapi_pblock_set(pb, SLAPI_RESULT_CODE, &err);
+
+ send_ldap_result( pb, err, NULL, NULL, 0, refurls );
+}
+
+
+int
+send_ldapv3_referral(
+ Slapi_PBlock *pb,
+ struct berval **urls
+)
+{
+ Connection *conn = pb->pb_conn;
+ BerElement *ber;
+ int i, rc, logit = 0;
+ Slapi_Operation *operation;
+
+ slapi_pblock_get (pb, SLAPI_OPERATION, &operation);
+
+ LDAPDebug( LDAP_DEBUG_TRACE, "=> send_ldapv3_referral\n", 0, 0, 0 );
+
+ if ( conn == NULL ) {
+ if ( operation->o_search_referral_handler != NULL ) {
+ if (( rc = (*operation->o_search_referral_handler)(
+ pb->pb_backend, conn, operation, urls )) == 0 ) {
+ logit = 1;
+ }
+ goto log_and_return;
+ }
+ return( 0 );
+ }
+ if ( urls == NULL ) {
+ return( 0 );
+ }
+
+ if ( (ber = der_alloc()) == NULL ) {
+ LDAPDebug( LDAP_DEBUG_ANY, "ber_alloc failed\n", 0, 0, 0 );
+ send_ldap_result( pb, LDAP_OPERATIONS_ERROR, NULL,
+ "ber_alloc", 0, NULL );
+ return( -1 );
+ }
+
+ /*
+ * send the ldapv3 SearchResultReference. it looks like this:
+ *
+ * SearchResultReference ::= [APPLICATION 19] SEQUENCE OF LDAPURL
+ *
+ * all wrapped up in an LDAPMessage sequence which looks like this:
+ * LDAPMessage ::= SEQUENCE {
+ * messageID MessageID,
+ * SearchResultReference
+ * controls [0] Controls OPTIONAL
+ * }
+ */
+
+ for ( i = 0, rc = ber_printf( ber, "{it{", operation->o_msgid,
+ LDAP_RES_SEARCH_REFERENCE );
+ rc != LBER_ERROR && urls[i] != NULL; i++ ) {
+ rc = ber_printf( ber, "s", urls[i]->bv_val );
+ }
+ if ( rc == LBER_ERROR ) {
+ LDAPDebug( LDAP_DEBUG_ANY, "ber_printf failed\n", 0, 0, 0 );
+ send_ldap_result( pb, LDAP_OPERATIONS_ERROR, NULL,
+ "ber_printf", 0, NULL );
+ return( -1 );
+ }
+ if ( ber_printf( ber, "}}" ) == LBER_ERROR ) {
+ LDAPDebug( LDAP_DEBUG_ANY, "ber_printf failed\n", 0, 0, 0 );
+ send_ldap_result( pb, LDAP_OPERATIONS_ERROR, NULL,
+ "ber_printf", 0, NULL );
+ return( -1 );
+ }
+
+ /* write only one pdu at a time - wait til it's our turn */
+ if ( (rc = flush_ber( pb, conn, operation, ber, _LDAP_SEND_REFERRAL
+ )) == 0 ) {
+ logit = 1;
+ }
+
+log_and_return:
+ if ( logit && operation_is_flag_set( operation,
+ OP_FLAG_ACTION_LOG_ACCESS)){
+ log_referral( operation );
+ }
+
+ return( rc );
+}
+
+/*
+ * send_ldap_referral - called to send a referral (SearchResultReference)
+ * to a v3 client during a search. for v2 clients, it just adds the
+ * referral(s) to the url list passed in the third parameter. this list
+ * is then returned to v2 clients when it is passed to send_ldap_result().
+ */
+int
+send_ldap_referral (
+ Slapi_PBlock *pb,
+ Slapi_Entry *e,
+ struct berval **refs,
+ struct berval ***urls
+)
+{
+ char *refAttr = "ref";
+ char *attrs[2] = { NULL, NULL };
+
+ /* count the referral */
+ PR_AtomicIncrement(g_get_global_snmp_vars()->ops_tbl.dsReferrals);
+
+ attrs[0] = refAttr;
+ if ( e != NULL &&
+ plugin_call_acl_plugin (pb, e, attrs, NULL,
+ SLAPI_ACL_READ, ACLPLUGIN_ACCESS_DEFAULT, NULL)
+ != LDAP_SUCCESS ) {
+ return( 0 );
+ }
+ if ( pb->pb_conn && pb->pb_conn->c_ldapversion > LDAP_VERSION2 ) {
+ /*
+ * v3 connection - send the referral(s) in a
+ * SearchResultReference packet right now.
+ */
+ return( send_ldapv3_referral( pb, refs ) );
+ } else {
+ /*
+ * v2 connection - add the referral(s) to the
+ * list being maintained in urls. they will be
+ * sent to the client later when send_ldap_result()
+ * is called.
+ */
+ int i, need, have;
+
+ if ( refs == NULL && urls == NULL ) {
+ return( 0 );
+ }
+
+ for ( have = 0; *urls != NULL && (*urls)[have] != NULL;
+ have++ ) {
+ ; /* NULL */
+ }
+ for ( need = 0; refs != NULL && refs[need] != NULL; need++ ) {
+ ; /* NULL */
+ }
+
+ *urls = (struct berval **) slapi_ch_realloc( (char *) *urls,
+ (need + have + 1) * sizeof(struct berval *) );
+ for ( i = have; i < have + need; i++ ) {
+ (*urls)[i] = ber_bvdup( refs[i - have] );
+ }
+ (*urls)[i] = NULL;
+ }
+
+ return( 0 );
+}
+
+int
+encode_attr_2(
+ Slapi_PBlock *pb,
+ BerElement *ber,
+ Slapi_Entry *e,
+ Slapi_ValueSet *vs,
+ int attrsonly,
+ const char *attribute_type,
+ const char *returned_type
+)
+{
+
+ char *attrs[2] = { NULL, NULL };
+
+ attrs[0] = (char*)attribute_type;
+
+#if !defined(DISABLE_ACL_CHECK)
+ if ( plugin_call_acl_plugin (pb, e, attrs, NULL, SLAPI_ACL_READ,
+ ACLPLUGIN_ACCESS_READ_ON_ATTR, NULL ) != LDAP_SUCCESS ) {
+ return( 0 );
+ }
+#endif
+
+ if ( ber_printf( ber, "{s[", returned_type ) == -1 ) {
+ LDAPDebug( LDAP_DEBUG_ANY, "ber_printf failed\n", 0, 0, 0 );
+ ber_free( ber, 1 );
+ send_ldap_result( pb, LDAP_OPERATIONS_ERROR, NULL,
+ "ber_printf type", 0, NULL );
+ return( -1 );
+ }
+
+ if ( ! attrsonly )
+ {
+ Slapi_Value *v;
+ int i= slapi_valueset_first_value(vs,&v);
+ while(i!=-1)
+ {
+ if ( ber_printf( ber, "o", v->bv.bv_val,v->bv.bv_len ) == -1 )
+ {
+ LDAPDebug( LDAP_DEBUG_ANY,
+ "ber_printf failed\n", 0, 0, 0 );
+ ber_free( ber, 1 );
+ send_ldap_result( pb, LDAP_OPERATIONS_ERROR,
+ NULL, "ber_printf value", 0, NULL );
+ return( -1 );
+ }
+ i= slapi_valueset_next_value(vs,i,&v);
+ }
+ }
+
+ if ( ber_printf( ber, "]}" ) == -1 ) {
+ LDAPDebug( LDAP_DEBUG_ANY, "ber_printf failed\n", 0, 0, 0 );
+ ber_free( ber, 1 );
+ send_ldap_result( pb, LDAP_OPERATIONS_ERROR, NULL,
+ "ber_printf type end", 0, NULL );
+ return( -1 );
+ }
+
+ return( 0 );
+}
+
+int
+encode_attr(
+ Slapi_PBlock *pb,
+ BerElement *ber,
+ Slapi_Entry *e,
+ Slapi_Attr *a,
+ int attrsonly,
+ char *type
+)
+{
+ return encode_attr_2(pb,ber,e,&(a->a_present_values),attrsonly,a->a_type,type);
+}
+
+#define LASTMODATTR( x ) (strcasecmp( x, "modifytimestamp" ) == 0 \
+ || strcasecmp( x, "modifiersname" ) == 0 \
+ || strcasecmp( x, "createtimestamp" ) == 0 \
+ || strcasecmp( x, "creatorsname" ) == 0)
+
+/*
+ * send_ldap_search_entry:
+ * return 0 if OK
+ * return 1 if this entry not sent
+ * return -1 if error result sent or fatal error
+ */
+int
+send_ldap_search_entry(
+ Slapi_PBlock *pb,
+ Slapi_Entry *e,
+ LDAPControl **ectrls,
+ char **attrs,
+ int attrsonly
+)
+{
+ return send_ldap_search_entry_ext(pb, e, ectrls, attrs, attrsonly, 0, 0, NULL);
+}
+
+/*
+ * LDAPv2 attr names from RFC1274 and their LDAPv3 equivalent.
+ *
+ * The ;binary attrs are deliberately reversed.
+ */
+static const char *idds_v2_attrt[][2] = {
+ {"commonName","cn"},
+ {"surname","sn"},
+ {"userCertificate;binary","userCertificate"},
+ {"caCertificate;binary","caCertificate"},
+ {"countryName","c"},
+ {"localityName","l"},
+ {"stateOrProvinceName","st"},
+ {"streetAddress","street"},
+ {"organizationName","o"},
+ {"organizationalUnitName","ou"},
+ {"userid","uid"},
+ {"rfc822Mailbox","mail"},
+ {"domainComponent","dc"},
+ {"mobileTelephoneNumber","mobile"},
+ {"pagerTelephoneNumber","pager"},
+ {"friendlyCountryName","co"},
+ {NULL,NULL}
+};
+
+/*
+ * Map an LDAPv3 attribute name to its LDAPv2 equivalent.
+ */
+static const char *idds_map_attrt_v3(
+ const char *atin
+)
+{
+ int i;
+
+ for (i = 0; idds_v2_attrt[i][0] != NULL; i++) {
+ if (strcasecmp(atin, idds_v2_attrt[i][1]) == 0) {
+ return (idds_v2_attrt[i][0]);
+ }
+ }
+
+ return NULL;
+}
+
+/*
+ * RFC: 2251 Page: 29
+ *
+ * attributes: A list of the attributes to be returned from each entry
+ * which matches the search filter. There are two special values which
+ * may be used: an empty list with no attributes, and the attribute
+ * description string "*". Both of these signify that all user
+ * attributes are to be returned. (The "*" allows the client to
+ * request all user attributes in addition to specific operational
+ * attributes).
+ *
+ * Attributes MUST be named at most once in the list, and are returned
+ * at most once in an entry. If there are attribute descriptions in
+ * the list which are not recognized, they are ignored by the server.
+ *
+ * If the client does not want any attributes returned, it can specify
+ * a list containing only the attribute with OID "1.1". This OID was
+ * chosen arbitrarily and does not correspond to any attribute in use.
+ */
+
+
+/* Helper functions */
+
+static int send_all_attrs(Slapi_Entry *e,char **attrs,Slapi_Operation *op,Slapi_PBlock *pb,BerElement *ber,int attrsonly,int ldapversion,int *dontsendattr, int real_attrs_only, int some_named_attrs)
+{
+ int i = 0;
+ int rc = 0;
+
+ int typelist_flags = 0;
+ vattr_type_thang *typelist = NULL;
+ vattr_type_thang *current_type = NULL;
+ char *current_type_name = NULL;
+ int rewrite_rfc1274 = 0;
+ int vattr_flags = 0;
+
+ if(real_attrs_only == SLAPI_SEND_VATTR_FLAG_REALONLY)
+ vattr_flags = SLAPI_REALATTRS_ONLY;
+ else
+ {
+ vattr_flags = SLAPI_VIRTUALATTRS_REQUEST_POINTERS;
+ if(real_attrs_only == SLAPI_SEND_VATTR_FLAG_VIRTUALONLY)
+ vattr_flags |= SLAPI_VIRTUALATTRS_ONLY;
+ }
+
+ if (some_named_attrs) {
+ /*
+ * If the client listed some attribute types by name, one or
+ * more of the requested types MAY be operational. Inform the
+ * virtual attributes subsystem (certain optimizations are done
+ * by the vattrs code and vattr service providers if operational
+ * attributes are NOT requested).
+ */
+ vattr_flags |= SLAPI_VIRTUALATTRS_LIST_OPERATIONAL_ATTRS;
+ }
+
+ rc = slapi_vattr_list_attrs(e,&typelist,vattr_flags,&typelist_flags);
+ if (0 != rc) {
+ goto exit;
+ }
+
+ if (typelist_flags & SLAPI_VIRTUALATTRS_REALATTRS_ONLY) {
+ /*
+ * There is no point in consulting the vattr service providers
+ * for every attr if they didn't contribute to the attr list.
+ */
+ vattr_flags |= SLAPI_REALATTRS_ONLY;
+ }
+
+ rewrite_rfc1274 = config_get_rewrite_rfc1274();
+
+ /* Send the attrs back to the client */
+ for (current_type = vattr_typethang_first(typelist); current_type; current_type = vattr_typethang_next(current_type) ) {
+
+ Slapi_ValueSet **values = NULL;
+ int attr_free_flags = 0;
+ unsigned long current_type_flags = 0;
+ int sendit = 0;
+ char *name_to_return = NULL;
+ int *type_name_disposition = 0;
+ char **actual_type_name = NULL;
+ const char *v2name = NULL;
+
+ current_type_name = vattr_typethang_get_name(current_type);
+ current_type_flags = vattr_typethang_get_flags(current_type);
+
+ name_to_return = current_type_name;
+ /* We only return operational attributes if the client is LDAPv2 and the attribute is one of a special set,
+ OR if the client also requested the attribute by name. If it did, we use the specified name rather than
+ the base name.
+ */
+ if ( current_type_flags & SLAPI_ATTR_FLAG_OPATTR ) {
+ if ( LDAP_VERSION2 == ldapversion && LASTMODATTR( current_type_name) ) {
+ sendit = 1;
+ } else {
+ for ( i = 0; attrs != NULL && attrs[i] != NULL; i++ ) {
+ if ( slapi_attr_type_cmp( attrs[i], current_type_name, SLAPI_TYPE_CMP_SUBTYPE ) == 0 ) {
+ sendit = 1;
+ name_to_return = op->o_searchattrs[i];
+ break;
+ }
+ }
+ }
+ /*
+ * it's a user attribute. send it.
+ */
+ } else {
+ sendit = 1;
+ }
+ /* Now send to the client */
+ if (sendit) {
+ /**********************************************/
+ int item_count = 0;
+ int iter = 0;
+ int j = 0;
+ Slapi_DN *namespace_dn;
+ Slapi_Backend *backend=0;
+ vattr_context *ctx;
+
+ /* get the namespace dn */
+ slapi_pblock_get( pb, SLAPI_BACKEND, (void *)&backend);
+ namespace_dn = (Slapi_DN*)slapi_be_getsuffix(backend, 0);
+
+ /* Get the attribute value from the vattr service */
+ /* ctx will be freed by attr_context_ungrok() */
+ ctx = vattr_context_new ( pb );
+ rc = slapi_vattr_namespace_values_get_sp(
+ ctx,
+ e,
+ namespace_dn,
+ current_type_name,
+ &values,
+ &type_name_disposition,
+ &actual_type_name,
+ vattr_flags | SLAPI_VIRTUALATTRS_SUPPRESS_SUBTYPES,
+ &attr_free_flags,
+ &item_count
+ );
+ if (0 == rc && item_count > 0) {
+
+ for(iter=0; iter<item_count; iter++)
+ {
+ if (SLAPI_VIRTUALATTRS_TYPE_NAME_MATCHED_SUBTYPE == type_name_disposition[iter]) {
+ name_to_return = actual_type_name[iter];
+ }
+
+ /*
+ * The dontsendattr array is used to track whether attributes
+ * that were explicitly requested by the client have been
+ * returned. Check here to see if the attribute we just
+ * arranged to send back was explicitly requested, and if so,
+ * set its dontsendattr flag so the send_specific_attrs()
+ * function does not return it a second time.
+ */
+ for ( i = 0; attrs != NULL && attrs[i] != NULL; i++ ) {
+ if ( !dontsendattr[i] && slapi_attr_type_cmp( current_type_name, attrs[i], SLAPI_TYPE_CMP_SUBTYPE ) == 0 ) {
+ /* Client is also asking for an attr which is in '*', zap it. */
+ dontsendattr[i]= 1;
+ }
+ }
+
+ rc = encode_attr_2( pb, ber, e, values[iter], attrsonly, current_type_name, name_to_return );
+
+ if (rewrite_rfc1274 != 0) {
+ v2name = idds_map_attrt_v3(current_type_name);
+ if (v2name != NULL) {
+ /* also return values with RFC1274 attr name */
+ rc = encode_attr_2(pb, ber, e, values[iter],
+ attrsonly,
+ current_type_name,
+ v2name);
+ }
+ }
+
+ slapi_vattr_values_free(&(values[iter]), &(actual_type_name[iter]), attr_free_flags);
+ if ( rc != 0 ) {
+ goto exit;
+ }
+ }
+
+ slapi_ch_free((void**)&actual_type_name);
+ slapi_ch_free((void**)&type_name_disposition);
+ slapi_ch_free((void**)&values);
+
+ } else {
+ rc = 0;
+ }
+ }
+ }
+exit:
+ if (NULL != typelist) {
+ slapi_vattr_attrs_free(&typelist,typelist_flags);
+ }
+ return rc;
+}
+
+int send_specific_attrs(Slapi_Entry *e,char **attrs,Slapi_Operation *op,Slapi_PBlock *pb,BerElement *ber,int attrsonly,int ldapversion,int *dontsendattr, int real_attrs_only)
+{
+ int i,j = 0;
+ int rc = 0;
+ int vattr_flags = 0;
+ vattr_context *ctx;
+
+ if(real_attrs_only == SLAPI_SEND_VATTR_FLAG_REALONLY)
+ vattr_flags = SLAPI_REALATTRS_ONLY;
+ else
+ {
+ vattr_flags = SLAPI_VIRTUALATTRS_REQUEST_POINTERS;
+ if(real_attrs_only == SLAPI_SEND_VATTR_FLAG_VIRTUALONLY)
+ vattr_flags |= SLAPI_VIRTUALATTRS_ONLY;
+ }
+
+ for ( i = 0; attrs[i] != NULL; i++ )
+ {
+ char *current_type_name = attrs[i];
+ if(!dontsendattr[i]) {
+ Slapi_ValueSet **values = NULL;
+ int attr_free_flags = 0;
+ char *name_to_return = NULL;
+ char **actual_type_name= NULL;
+ int *type_name_disposition = 0;
+ int item_count = 0;
+ int iter = 0;
+ Slapi_DN *namespace_dn;
+ Slapi_Backend *backend=0;
+
+ /*
+ * Here we call the computed attribute code to see whether
+ * the requested attribute is to be computed.
+ * The subroutine compute_attribute calls encode_attr on our behalf, in order
+ * to avoid the inefficiency of returning a complex structure
+ * which we'd have to free
+ */
+ rc = compute_attribute(attrs[i],pb,ber,e,attrsonly,op->o_searchattrs[i]);
+ if (0 == rc) {
+ continue; /* Means this was a computed attr and we prcessed it OK. */
+ }
+ if (-1 != rc) {
+ /* Means that some error happened */
+ return rc;
+ }
+ else {
+ rc = 0; /* Means that we just didn't recognize this as a computed attr */
+ }
+
+ /* get the namespace dn */
+ slapi_pblock_get( pb, SLAPI_BACKEND, (void *)&backend);
+ namespace_dn = (Slapi_DN*)slapi_be_getsuffix(backend, 0);
+
+ /* Get the attribute value from the vattr service */
+ /* ctx will be freed by attr_context_ungrok() */
+ ctx = vattr_context_new ( pb );
+ rc = slapi_vattr_namespace_values_get_sp(
+ ctx,
+ e,
+ namespace_dn,
+ current_type_name,
+ &values,
+ &type_name_disposition,
+ &actual_type_name,
+ vattr_flags,
+ &attr_free_flags,
+ &item_count
+ );
+ if (0 == rc && item_count > 0) {
+
+ for(iter=0; iter<item_count; iter++)
+ {
+ if (SLAPI_VIRTUALATTRS_TYPE_NAME_MATCHED_SUBTYPE == type_name_disposition[iter]) {
+ name_to_return = actual_type_name[iter];
+ } else {
+ name_to_return = op->o_searchattrs[i];
+ }
+
+ /*
+ * The client may have specified a list of attributes
+ * with duplicates, 'cn cn cn'.
+ * We need to determine which of any duplicates take precedence
+ * For subtypes, the attribute which is most generic should be
+ * returned (since it will also trigger the return of the less
+ * generic attribute subtypes.
+ */
+ for ( j = i+1; attrs != NULL && attrs[j] != NULL && dontsendattr[i]==0; j++ )
+ {
+ if ( !dontsendattr[j] && slapi_attr_type_cmp( attrs[j], actual_type_name[iter], SLAPI_TYPE_CMP_SUBTYPE ) == 0 )
+ {
+ /* discover which is the more generic attribute and cancel the other*/
+ int attrbase = slapi_attr_type_cmp( attrs[j], current_type_name, SLAPI_TYPE_CMP_EXACT );
+
+ if(attrbase >= 0)
+ dontsendattr[j]= 1;
+ else
+ dontsendattr[i]= 1; /* the current value is superceeded later */
+ }
+ }
+
+ /* we may have just cancelled ourselves so check */
+ if(!dontsendattr[i])
+ rc = encode_attr_2( pb, ber, e, values[iter], attrsonly, current_type_name, name_to_return );
+
+ slapi_vattr_values_free(&(values[iter]), &(actual_type_name[iter]), attr_free_flags);
+ if ( rc != 0 ) {
+ goto exit;
+ }
+ }
+
+ slapi_ch_free((void**)&actual_type_name);
+ slapi_ch_free((void**)&type_name_disposition);
+ slapi_ch_free((void**)&values);
+
+ } else {
+ rc = 0;
+ }
+ }
+ }
+exit:
+ return rc;
+
+}
+
+
+int
+send_ldap_search_entry_ext(
+ Slapi_PBlock *pb,
+ Slapi_Entry *e,
+ LDAPControl **ectrls,
+ char **attrs,
+ int attrsonly,
+ int send_result,
+ int nentries,
+ struct berval **urls
+)
+{
+ Connection *conn = pb->pb_conn;
+ Operation *op = pb->pb_op;
+ BerElement *ber;
+ int i, rc = 0, logit = 0;
+ int alluserattrs, noattrs, some_named_attrs;
+ int *dontsendattr= NULL;
+ Slapi_Operation *operation;
+ int real_attrs_only = 0;
+ LDAPControl **ctrlp = 0;
+
+ slapi_pblock_get (pb, SLAPI_OPERATION, &operation);
+
+ LDAPDebug( LDAP_DEBUG_TRACE, "=> send_ldap_search_entry (%s)\n",
+ slapi_entry_get_dn_const(e), 0, 0 );
+
+ if ( conn == NULL ) {
+ if ( op->o_search_entry_handler != NULL ) {
+ if (( rc = (*op->o_search_entry_handler)(
+ pb->pb_backend, conn, op, e )) == 0 ) {
+ logit = 1;
+ goto log_and_return;
+ } else {
+ return rc;
+ }
+ }
+ return 0;
+ }
+
+#if !defined(DISABLE_ACL_CHECK)
+ if ( plugin_call_acl_plugin (pb, e, attrs, NULL,
+ SLAPI_ACL_READ, ACLPLUGIN_ACCESS_READ_ON_ENTRY, NULL ) != LDAP_SUCCESS ) {
+ LDAPDebug( LDAP_DEBUG_ACL, "acl: access to entry not allowed\n",
+ 0, 0, 0 );
+ return( 1 );
+ }
+#endif
+
+ /* Check for possible get_effective_rights control */
+ if ( operation->o_flags & OP_FLAG_GET_EFFECTIVE_RIGHTS ) {
+ char *errbuf = NULL;
+ rc = plugin_call_acl_plugin (pb, e, attrs, NULL, SLAPI_ACL_ALL,
+ ACLPLUGIN_ACCESS_GET_EFFECTIVE_RIGHTS, &errbuf);
+ if ( rc != LDAP_SUCCESS ) {
+ LDAPDebug( LDAP_DEBUG_ANY,
+ "Failed to get effective rights for entry (%s), rc=%d\n",
+ slapi_entry_get_dn_const(e), rc, 0 );
+ /* Send error result and abort op if the control is critical */
+ send_ldap_result( pb, rc, NULL, errbuf, 0, NULL );
+ slapi_ch_free ( (void**)&errbuf );
+ return( -1 );
+ }
+ slapi_ch_free ( (void**)&errbuf );
+ }
+
+ if ( (ber = der_alloc()) == NULL ) {
+ LDAPDebug( LDAP_DEBUG_ANY, "ber_alloc failed\n", 0, 0, 0 );
+ send_ldap_result( pb, LDAP_OPERATIONS_ERROR, NULL,
+ "ber_alloc", 0, NULL );
+ return( -1 );
+ }
+
+ rc = ber_printf( ber, "{it{s{", op->o_msgid,
+ LDAP_RES_SEARCH_ENTRY, slapi_entry_get_dn_const(e) );
+
+ if ( rc == -1 ) {
+ LDAPDebug( LDAP_DEBUG_ANY, "ber_printf failed\n", 0, 0, 0 );
+ ber_free( ber, 1 );
+ send_ldap_result( pb, LDAP_OPERATIONS_ERROR, NULL,
+ "ber_printf dn", 0, NULL );
+ return( -1 );
+ }
+
+ /*
+ * in ldapv3, the special attribute "*" means all user attributes,
+ * NULL means all user attributes, and "1.1" means no attributes.
+ * operational attributes are only retrieved if they are named
+ * specifically.
+ */
+
+ /* figure out if we want all user attributes or no attributes at all */
+ alluserattrs = 0;
+ noattrs = 0;
+ some_named_attrs = 0;
+ if ( attrs == NULL ) {
+ alluserattrs = 1;
+ } else {
+ for ( i = 0; attrs[i] != NULL; i++ ) {
+ if ( strcmp( LDAP_ALL_USER_ATTRS, attrs[i] ) == 0 ) {
+ alluserattrs = 1;
+ } else if ( strcmp( LDAP_NO_ATTRS, attrs[i] ) == 0 ) {
+ noattrs = 1;
+ } else {
+ some_named_attrs = 1;
+ }
+ }
+ if ( i > 1 && noattrs ) {
+ /*
+ * user has specified the special "1.1" noattrs attr
+ * and some other stuff. this is not allowed, but
+ * what should we do? we'll allow them to keep going.
+ */
+ LDAPDebug( LDAP_DEBUG_TRACE,
+ "Accepting illegal other attributes specified with "
+ "special \"1.1\" attribute\n", 0, 0, 0 );
+ }
+ /*
+ * We maintain a flag array so that we can remove requests
+ * for duplicate attributes.
+ */
+ dontsendattr= (int*) slapi_ch_calloc( i+1, sizeof(int) );
+ }
+
+
+ /* determine whether we are to return virtual attributes */
+ slapi_pblock_get(pb, SLAPI_REQCONTROLS, &ctrlp);
+ if(slapi_control_present(ctrlp, LDAP_CONTROL_REAL_ATTRS_ONLY, NULL, NULL))
+ real_attrs_only = SLAPI_SEND_VATTR_FLAG_REALONLY;
+
+ if(slapi_control_present(ctrlp, LDAP_CONTROL_VIRT_ATTRS_ONLY, NULL, NULL))
+ {
+ if(real_attrs_only != SLAPI_SEND_VATTR_FLAG_REALONLY)
+ real_attrs_only = SLAPI_SEND_VATTR_FLAG_VIRTUALONLY;
+ else
+ {
+ /* we cannot service a request for virtual only and real only */
+ ber_free( ber, 1 );
+ slapi_ch_free( (void **) &dontsendattr );
+ send_ldap_result( pb, LDAP_OPERATIONS_ERROR, NULL,
+ "Both real and virtual attributes only controls", 0, NULL );
+ return( -1 );
+ }
+ }
+
+ /* look through each attribute in the entry */
+ if ( alluserattrs ) {
+ rc = send_all_attrs(e,attrs,op,pb,ber,attrsonly,conn->c_ldapversion,dontsendattr, real_attrs_only, some_named_attrs);
+ }
+
+ /* if the client explicitly specified a list of attributes look through each attribute requested */
+ if( (rc == 0) && (attrs!=NULL)) {
+ rc = send_specific_attrs(e,attrs,op,pb,ber,attrsonly,conn->c_ldapversion,dontsendattr,real_attrs_only);
+ }
+
+ /* Append effective rights to the stream of attribute list */
+ if ( operation->o_flags & OP_FLAG_GET_EFFECTIVE_RIGHTS )
+ {
+ char *gerstr;
+ char *entryrights;
+ char *attributerights;
+ char *p;
+
+ slapi_pblock_get (pb, SLAPI_PB_RESULT_TEXT, &gerstr);
+
+ /* Syntax check - see acleffectiverights.c */
+ if (gerstr && (p = strchr(gerstr, '\n')) != NULL &&
+ strncasecmp (gerstr, "entryLevelRights: ",
+ strlen("entryLevelRights: ")) == 0 &&
+ strncasecmp (p+1, "attributeLevelRights: ",
+ strlen("attributeLevelRights: ")) == 0 )
+ {
+ entryrights = gerstr + strlen ("entryLevelRights: ");
+ *p = '\0';
+ attributerights = p + 1 + strlen ("attributeLevelRights: ");
+ ber_printf( ber, "{s[o]}", "entryLevelRights", entryrights, strlen(entryrights) );
+ ber_printf( ber, "{s[o]}", "attributeLevelRights", attributerights, strlen(attributerights) );
+ }
+ }
+
+ slapi_ch_free( (void **) &dontsendattr ); /* I know it looks like we could free this when it wasn't allocated, the function ignores null pointers */
+
+ if (rc != 0) {
+ ber_free( ber, 1 );
+ goto exit;
+ }
+
+ rc = ber_printf( ber, "}}" );
+
+ if ( conn->c_ldapversion >= LDAP_VERSION3 ) {
+ if ( ectrls != NULL ) {
+ rc = write_controls( ber, ectrls );
+ }
+ /*
+ * The get-effective-rights control is called within
+ * the current function. Hence it can't be already in
+ * ectrls
+ */
+ if ( operation->o_flags & OP_FLAG_GET_EFFECTIVE_RIGHTS ) {
+ LDAPControl *gerctrl[2];
+ slapi_pblock_get (pb, SLAPI_RESCONTROLS, &ctrlp);
+ for ( i = 0; ctrlp != NULL && ctrlp[i] != NULL; i++ ) {
+ if (strcmp(ctrlp[i]->ldctl_oid, LDAP_CONTROL_GET_EFFECTIVE_RIGHTS ) == 0 ) {
+ gerctrl[0] = ctrlp[i];
+ gerctrl[1] = NULL;
+ rc = write_controls( ber, gerctrl );
+ break;
+ }
+ }
+ }
+ }
+
+ if ( rc != -1 ) {
+ rc = ber_printf( ber, "}" );
+ }
+
+ if ( rc == -1 ) {
+ LDAPDebug( LDAP_DEBUG_ANY, "ber_printf failed\n", 0, 0, 0 );
+ ber_free( ber, 1 );
+ send_ldap_result( pb, LDAP_OPERATIONS_ERROR, NULL,
+ "ber_printf entry end", 0, NULL );
+ return( -1 );
+ }
+
+ if (send_result) {
+ send_ldap_result_ext( pb, LDAP_SUCCESS, NULL, NULL, nentries, urls, ber);
+ }
+
+ /* write only one pdu at a time - wait til it's our turn */
+ if ( (rc = flush_ber( pb, conn, op, ber, _LDAP_SEND_ENTRY )) == 0 ) {
+ logit = 1;
+ }
+
+log_and_return:
+ if ( logit && operation_is_flag_set(operation,
+ OP_FLAG_ACTION_LOG_ACCESS)){
+
+ log_entry( op, e );
+
+ if (send_result) {
+ unsigned long tag;
+
+ switch ( op->o_tag ) {
+ case LBER_DEFAULT:
+ tag = LBER_SEQUENCE;
+ break;
+
+ case LDAP_REQ_SEARCH:
+ tag = LDAP_RES_SEARCH_RESULT;
+ break;
+
+ case LDAP_REQ_DELETE:
+ tag = LDAP_RES_DELETE;
+ break;
+
+ case LDAP_REFERRAL:
+ if ( conn != NULL && conn->c_ldapversion > LDAP_VERSION2 ) {
+ tag = LDAP_TAG_REFERRAL;
+ break;
+ }
+ /* fallthru */
+
+ default:
+ tag = op->o_tag + 1;
+ break;
+ }
+
+ log_result( pb, op, LDAP_SUCCESS, tag, nentries );
+ }
+ }
+
+ LDAPDebug( LDAP_DEBUG_TRACE, "<= send_ldap_search_entry\n", 0, 0, 0 );
+exit:
+ return( rc );
+}
+
+
+
+
+/*
+ * always frees the ber
+ */
+static int
+flush_ber(
+ Slapi_PBlock *pb,
+ Connection *conn,
+ Operation *op,
+ BerElement *ber,
+ int type
+)
+{
+ unsigned long bytes;
+ int rc = 0;
+
+ switch ( type ) {
+ case _LDAP_SEND_RESULT:
+ rc = plugin_call_plugins( pb, SLAPI_PLUGIN_PRE_RESULT_FN );
+ break;
+ case _LDAP_SEND_REFERRAL:
+ rc = plugin_call_plugins( pb, SLAPI_PLUGIN_PRE_REFERRAL_FN );
+ break;
+ case _LDAP_SEND_ENTRY:
+ rc = plugin_call_plugins( pb, SLAPI_PLUGIN_PRE_ENTRY_FN );
+ break;
+ }
+
+ if ( rc != 0 ) {
+ ber_free( ber, 1 );
+ return( rc );
+ }
+
+ if ((conn->c_flags & CONN_FLAG_CLOSING) || slapi_op_abandoned(pb)) {
+ LDAPDebug(LDAP_DEBUG_CONNS, "ber_flush skipped because the "
+ "connection was marked to be closed or abandoned\n", 0, 0, 0);
+ ber_free( ber, 1 );
+ /* One of the failure can be because the client has reset the connection ( closed )
+ * and the status needs to be updated to reflect it */
+ op->o_status = SLAPI_OP_STATUS_ABANDONED;
+ rc = -1;
+ } else {
+ ber_get_option( ber, LBER_OPT_BYTES_TO_WRITE, &bytes );
+
+ PR_Lock( conn->c_pdumutex );
+ rc = ber_flush( conn->c_sb, ber, 1 );
+ PR_Unlock( conn->c_pdumutex );
+
+ if ( rc != 0 ) {
+ int oserr = errno;
+ /* One of the failure can be because the client has reset the connection ( closed )
+ * and the status needs to be updated to reflect it */
+ op->o_status = SLAPI_OP_STATUS_ABANDONED;
+
+ LDAPDebug( LDAP_DEBUG_CONNS,
+ "ber_flush failed, error %d (%s)\n",
+ oserr, slapd_system_strerror( oserr ), 0 );
+ if (op->o_flags & OP_FLAG_PS) {
+ /* We need to tell disconnect_server() not to ding
+ * all the psearches if one if them disconnected
+ * But we do need to terminate all persistent searches that are using
+ * this connection
+ * op->o_flags |= OP_FLAG_PS_SEND_FAILED;
+ */
+ }
+ do_disconnect_server( conn, op->o_connid, op->o_opid );
+ ber_free( ber, 1 );
+ } else {
+ PRUint64 b;
+ LDAPDebug( LDAP_DEBUG_BER,
+ "flush_ber() wrote %lu bytes to socket %d\n",
+ bytes, conn->c_sd, 0 );
+ LL_I2L ( b, bytes ) ;
+ LL_ADD ( num_bytes_sent, num_bytes_sent, b);
+
+ if ( type == _LDAP_SEND_ENTRY ) {
+ LL_I2L ( b, 1 );
+ LL_ADD ( num_entries_sent, num_entries_sent, b );
+ }
+ if (! config_check_referral_mode())
+ (*(g_get_global_snmp_vars()->ops_tbl.dsBytesSent))+= bytes;
+ }
+ }
+
+ switch ( type ) {
+ case _LDAP_SEND_RESULT:
+ plugin_call_plugins( pb, SLAPI_PLUGIN_POST_RESULT_FN );
+ break;
+ case _LDAP_SEND_REFERRAL:
+ PR_AtomicIncrement(g_get_global_snmp_vars()->ops_tbl.dsReferralsReturned);
+ plugin_call_plugins( pb, SLAPI_PLUGIN_POST_REFERRAL_FN );
+ break;
+ case _LDAP_SEND_ENTRY:
+ PR_AtomicIncrement(g_get_global_snmp_vars()->ops_tbl.dsEntriesReturned);
+ plugin_call_plugins( pb, SLAPI_PLUGIN_POST_ENTRY_FN );
+ break;
+ }
+
+ return( rc );
+}
+
+/*
+ Puts the default result handlers into the pblock.
+ This routine is called before any server call to a
+ database backend.
+ Returns : 0 on success, -1 on failure.
+*/
+int set_db_default_result_handlers(Slapi_PBlock *pb)
+{
+ int rc = -1;
+ if (0 != pb)
+ {
+ rc = slapi_pblock_set( pb, SLAPI_PLUGIN_DB_ENTRY_FN,
+ (void *) send_ldap_search_entry );
+ rc |= slapi_pblock_set( pb, SLAPI_PLUGIN_DB_RESULT_FN,
+ (void *) send_ldap_result );
+ rc |= slapi_pblock_set( pb, SLAPI_PLUGIN_DB_REFERRAL_FN,
+ (void *) send_ldap_referral );
+ }
+ return rc;
+}
+
+
+struct slapi_note_map {
+ unsigned int snp_noteid;
+ char *snp_string;
+};
+
+static struct slapi_note_map notemap[] = {
+ { SLAPI_OP_NOTE_UNINDEXED, "U" },
+};
+
+#define SLAPI_NOTEMAP_COUNT ( sizeof(notemap) / sizeof(struct slapi_note_map))
+
+
+/*
+ * fill buf with a string representation of the bits present in notes.
+ *
+ * each bit is mapped to a character string (see table above).
+ * the result looks like "notes=U,Z" or similar.
+ * if no known notes are present, a zero-length string is generated.
+ * if buflen is too small, the output is truncated.
+ *
+ * Return value: buf itself.
+ */
+static char *
+notes2str( unsigned int notes, char *buf, size_t buflen )
+{
+ char *p;
+ int i;
+ size_t len;
+
+ *buf = '\0';
+ --buflen;
+ if ( buflen < 7 ) { /* must be room for "notes=X" at least */
+ return( buf );
+ }
+
+ p = buf;
+ for ( i = 0; i < SLAPI_NOTEMAP_COUNT; ++i ) {
+ if (( notemap[i].snp_noteid & notes ) != 0 ) {
+ if ( p > buf && buflen > 0 ) {
+ *p++ = ',';
+ *p = '\0';
+ --buflen;
+ } else {
+ strcpy( p, "notes=" );
+ p += 6;
+ buflen -= 6;
+ }
+ len = strlen( notemap[i].snp_string );
+ if ( buflen < len ) {
+ break; /* bail out (result is truncated) */
+ }
+ memcpy( p, notemap[i].snp_string, len );
+ buflen -= len;
+ p += len;
+ *p ='\0';
+ }
+ }
+
+ return( buf );
+}
+
+
+#define ETIME_BUFSIZ 16 /* room for 99999999.999999 */
+
+static void
+log_result( Slapi_PBlock *pb, Operation *op, int err, unsigned long tag,
+ int nentries )
+{
+ char *notes_str, notes_buf[ 256 ];
+ int internal_op;
+ CSN *operationcsn = NULL;
+ char csn_str[CSN_STRSIZE + 5];
+ char etime[ETIME_BUFSIZ];
+
+ internal_op = operation_is_flag_set( op, OP_FLAG_INTERNAL );
+
+ if ( (config_get_accesslog_level() & LDAP_DEBUG_TIMING) &&
+ (op->o_interval != (PRIntervalTime) 0) ) {
+ PRIntervalTime delta = PR_IntervalNow() - op->o_interval;
+ PR_snprintf(etime, ETIME_BUFSIZ, "%f",
+ (PRFloat64)delta/PR_TicksPerSecond());
+ } else {
+ PR_snprintf(etime, ETIME_BUFSIZ, "%d", current_time() - op->o_time);
+ }
+
+ if ( 0 == pb->pb_operation_notes ) {
+ notes_str = "";
+ } else {
+ notes_str = notes_buf;
+ *notes_buf = ' ';
+ notes2str( pb->pb_operation_notes, notes_buf + 1,
+ sizeof( notes_buf ) - 1 );
+ }
+
+ csn_str[0] = '\0';
+ if (config_get_csnlogging() == LDAP_ON)
+ {
+ operationcsn = operation_get_csn(op);
+ if (NULL != operationcsn)
+ {
+ char tmp_csn_str[CSN_STRSIZE];
+ sprintf(csn_str, " csn=%s", csn_as_string(operationcsn, PR_FALSE, tmp_csn_str));
+ }
+ }
+
+ if (op->o_tag == LDAP_REQ_BIND && err == LDAP_SASL_BIND_IN_PROGRESS) {
+ /*
+ * Not actually an error.
+ * Make that clear in the log.
+ */
+ if ( !internal_op )
+ {
+ slapi_log_access( LDAP_DEBUG_STATS,
+ "conn=%d op=%d RESULT err=%d"
+ " tag=%d nentries=%d etime=%s%s%s"
+ ", SASL bind in progress\n",
+ op->o_connid,
+ op->o_opid,
+ err, tag, nentries,
+ etime,
+ notes_str, csn_str );
+ }
+ else
+ {
+ slapi_log_access( LDAP_DEBUG_ARGS,
+ "conn=%s op=%d RESULT err=%d"
+ " tag=%d nentries=%d etime=%s%s%s"
+ ", SASL bind in progress\n",
+ LOG_INTERNAL_OP_CON_ID,
+ LOG_INTERNAL_OP_OP_ID,
+ err, tag, nentries,
+ etime,
+ notes_str, csn_str );
+ }
+ } else if (op->o_tag == LDAP_REQ_BIND && err == LDAP_SUCCESS) {
+ char *dn = NULL;
+
+ /*
+ * For methods other than simple, the dn in the bind request
+ * may be irrelevant. Log the actual authenticated dn.
+ */
+ slapi_pblock_get(pb, SLAPI_CONN_DN, &dn);
+ if ( !internal_op )
+ {
+ slapi_log_access( LDAP_DEBUG_STATS,
+ "conn=%d op=%d RESULT err=%d"
+ " tag=%d nentries=%d etime=%s%s%s"
+ " dn=\"%s\"\n",
+ op->o_connid,
+ op->o_opid,
+ err, tag, nentries,
+ etime,
+ notes_str, csn_str, dn ? dn : "");
+ }
+ else
+ {
+ slapi_log_access( LDAP_DEBUG_ARGS,
+ "conn=%s op=%d RESULT err=%d"
+ " tag=%d nentries=%d etime=%s%s%s"
+ " dn=\"%s\"\n",
+ LOG_INTERNAL_OP_CON_ID,
+ LOG_INTERNAL_OP_OP_ID,
+ err, tag, nentries,
+ etime,
+ notes_str, csn_str, dn ? dn : "");
+ }
+ slapi_ch_free((void**)&dn);
+ } else {
+ if ( !internal_op )
+ {
+ slapi_log_access( LDAP_DEBUG_STATS,
+ "conn=%d op=%d RESULT err=%d"
+ " tag=%d nentries=%d etime=%s%s%s\n",
+ op->o_connid,
+ op->o_opid,
+ err, tag, nentries,
+ etime,
+ notes_str, csn_str );
+ }
+ else
+ {
+ slapi_log_access( LDAP_DEBUG_ARGS,
+ "conn=%s op=%d RESULT err=%d"
+ " tag=%d nentries=%d etime=%s%s%s\n",
+ LOG_INTERNAL_OP_CON_ID,
+ LOG_INTERNAL_OP_OP_ID,
+ err, tag, nentries,
+ etime,
+ notes_str, csn_str );
+ }
+ }
+}
+
+
+static void
+log_entry( Operation *op, Slapi_Entry *e )
+{
+ int internal_op;
+ char ebuf[ BUFSIZ ];
+
+ internal_op = operation_is_flag_set( op, OP_FLAG_INTERNAL );
+
+ if ( !internal_op )
+ {
+ slapi_log_access( LDAP_DEBUG_STATS2, "conn=%d op=%d ENTRY dn=\"%s\"\n",
+ op->o_connid, op->o_opid,
+ escape_string( slapi_entry_get_dn_const(e), ebuf ));
+ }
+ else
+ {
+ if ( config_get_accesslog_level() & LDAP_DEBUG_STATS2 )
+ {
+ slapi_log_access( LDAP_DEBUG_ARGS, "conn=%s op=%d ENTRY dn=\"%s\"\n",
+ LOG_INTERNAL_OP_CON_ID, LOG_INTERNAL_OP_OP_ID,
+ escape_string( slapi_entry_get_dn_const(e), ebuf ));
+ }
+ }
+}
+
+
+static void
+log_referral( Operation *op )
+{
+ int internal_op;
+
+ internal_op = operation_is_flag_set( op, OP_FLAG_INTERNAL );
+
+ if ( !internal_op )
+ {
+ slapi_log_access( LDAP_DEBUG_STATS2, "conn=%d op=%d REFERRAL\n",
+ op->o_connid, op->o_opid );
+ }
+ else
+ {
+ if ( config_get_accesslog_level() & LDAP_DEBUG_STATS2 )
+ {
+ slapi_log_access( LDAP_DEBUG_ARGS, "conn=%s op=%d REFERRAL\n",
+ LOG_INTERNAL_OP_CON_ID, LOG_INTERNAL_OP_OP_ID );
+ }
+ }
+}
diff --git a/ldap/servers/slapd/rootdse.c b/ldap/servers/slapd/rootdse.c
new file mode 100644
index 00000000..f438cceb
--- /dev/null
+++ b/ldap/servers/slapd/rootdse.c
@@ -0,0 +1,331 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+/* rootdse.c - routines to manage the root DSE */
+
+#include <stdio.h>
+#include "slap.h"
+#include "fe.h"
+
+/* XXXmcs: why not look at the NO-USER-MODIFICATION flag instead? */
+static char *readonly_attributes[] = {
+ "namingcontexts",
+ "nsBackendSuffix",
+ "subschemasubentry",
+ "supportedldapversion",
+ "supportedcontrol",
+ "supportedextension",
+ "supportedsaslmechanisms",
+ "dataversion",
+ "ref",
+ "vendorName",
+ "vendorVersion",
+ ATTR_NETSCAPEMDSUFFIX,
+ NULL
+};
+
+
+static char *writable_attributes[] = {
+ "copiedfrom",
+ "copyingfrom",
+ "aci",
+ NULL
+};
+
+
+/*
+ * function: is_readonly_attr
+ * args: attr - candidate attribute name
+ * returns: 0 if this attribute may be written to the root DSE
+ * 1 if it may not be written.
+ * notes: should probably be integrated into syntax AVL tree, so that
+ * this list can be configured at runtime.
+ */
+static int
+rootdse_is_readonly_attr( char *attr )
+{
+ int i;
+
+ if ( NULL == attr ) {
+ return 1; /* I guess. It's not really an attribute at all */
+ }
+
+ if ( NULL == readonly_attributes ) {
+ return 0;
+ }
+
+ /*
+ * optimization: check for attributes we're likely to be writing
+ * frequently.
+ */
+ for ( i = 0; NULL != writable_attributes[ i ]; i++ ) {
+ if ( strncasecmp( attr, writable_attributes[ i ],
+ strlen( writable_attributes[ i ])) == 0 ) {
+ return 0;
+ }
+ }
+
+ for ( i = 0; NULL != readonly_attributes[ i ]; i++ ) {
+ if ( strncasecmp( attr, readonly_attributes[ i ],
+ strlen( readonly_attributes[ i ])) == 0 ) {
+ return 1;
+ }
+ }
+ return 0;
+}
+
+
+
+
+
+/*
+ * Handle a read operation on the root DSE (the entry with DN "");
+ * Note: we're copying a lot of attributes here. It might be better
+ * to keep track of which we really need to free, and arrange that
+ * the others are unlinked from the attribute list of the entry
+ * before calling slapi_entry_free().
+ */
+int
+read_root_dse( Slapi_PBlock *pb, Slapi_Entry *e, Slapi_Entry *entryAfter, int *returncode, char *returntext, void *arg )
+{
+ int i;
+ struct berval *vals[2];
+ struct berval val;
+ struct berval **bvals;
+ Slapi_Backend *be;
+ char *cookie = NULL;
+ char **strs;
+ void *node;
+ Slapi_DN *sdn;
+
+ /*
+ * Check that we were doing a base search on the root dse.
+ */
+ {
+ int scope;
+ slapi_pblock_get( pb, SLAPI_SEARCH_SCOPE, &scope );
+ if(scope!=LDAP_SCOPE_BASE)
+ {
+ *returncode= LDAP_NO_SUCH_OBJECT;
+ return SLAPI_DSE_CALLBACK_ERROR;
+ }
+ }
+
+ vals[0] = &val;
+ vals[1] = NULL;
+
+ /* loop through backend suffixes to get namingcontexts attr */
+ attrlist_delete( &e->e_attrs, "namingcontexts");
+
+ sdn = slapi_get_first_suffix(&node, 0);
+ while (sdn)
+ {
+ val.bv_val = (char*)slapi_sdn_get_dn(sdn); /* jcm: had to cast away const */
+ val.bv_len = strlen( val.bv_val );
+ attrlist_merge( &e->e_attrs, "namingcontexts", vals );
+ sdn = slapi_get_next_suffix(&node, 0);
+ }
+
+ attrlist_delete( &e->e_attrs, "nsBackendSuffix");
+ for (be = slapi_get_first_backend(&cookie); be != NULL;
+ be = slapi_get_next_backend(cookie)) {
+
+ char * base;
+ char * be_name;
+ const Slapi_DN *be_suffix;
+
+ if (slapi_be_private(be)) continue;
+
+ /* tolerate a backend under construction containing no suffix */
+ if ((be_suffix = slapi_be_getsuffix(be, 0)) == NULL) continue;
+
+ if ((base = (char *)slapi_sdn_get_dn(be_suffix)) == NULL) continue;
+ if ((be_name = slapi_be_get_name(be)) == NULL) continue;
+
+ val.bv_len = strlen(base)+strlen(be_name)+1;
+ val.bv_val = slapi_ch_malloc(val.bv_len+1);
+ sprintf(val.bv_val, "%s:%s", be_name, base);
+ attrlist_merge(&e->e_attrs, "nsBackendSuffix", vals);
+ slapi_ch_free((void **) &val.bv_val);
+ }
+ slapi_ch_free((void **)&cookie);
+
+ /* schema entry */
+ val.bv_val = SLAPD_SCHEMA_DN;
+ val.bv_len = sizeof( SLAPD_SCHEMA_DN ) - 1;
+ attrlist_replace( &e->e_attrs, "subschemasubentry", vals );
+
+ /* supported extended operations */
+ attrlist_delete( &e->e_attrs, "supportedExtension");
+ if (( strs = slapi_get_supported_extended_ops_copy()) != NULL ) {
+ for ( i = 0; strs[i] != NULL; ++i ) {
+ val.bv_val = strs[i];
+ val.bv_len = strlen( strs[i] );
+ attrlist_merge( &e->e_attrs, "supportedExtension", vals );
+ }
+ charray_free(strs);
+ }
+
+ /* supported controls */
+ attrlist_delete( &e->e_attrs, "supportedControl");
+ if ( slapi_get_supported_controls_copy( &strs, NULL ) == 0
+ && strs != NULL ) {
+ for ( i = 0; strs[i] != NULL; ++i ) {
+ val.bv_val = strs[i];
+ val.bv_len = strlen( strs[i] );
+ attrlist_merge( &e->e_attrs, "supportedControl", vals );
+ }
+ charray_free(strs);
+ }
+
+ /* supported sasl mechanisms */
+ attrlist_delete( &e->e_attrs, "supportedSASLMechanisms");
+ if (( strs = ids_sasl_listmech (pb)) != NULL ) {
+ for ( i = 0; strs[i] != NULL; ++i ) {
+ val.bv_val = strs[i];
+ val.bv_len = strlen( strs[i] );
+ attrlist_merge( &e->e_attrs, "supportedSASLMechanisms", vals );
+ }
+ charray_free(strs);
+ }
+
+
+ /* supported LDAP versions */
+ val.bv_val = "2";
+ val.bv_len = 1;
+ attrlist_replace( &e->e_attrs, "supportedldapversion", vals );
+ val.bv_val = "3";
+ val.bv_len = 1;
+ attrlist_merge( &e->e_attrs, "supportedldapversion", vals );
+
+ /* superior references (ref attribute) */
+ attrlist_delete( &e->e_attrs, "ref");
+ if (( bvals = g_get_default_referral()) != NULL ) {
+ for ( i = 0; bvals[i] != NULL; ++i ) {
+ val.bv_val = bvals[i]->bv_val;
+ val.bv_len = bvals[i]->bv_len;
+ attrlist_merge( &e->e_attrs, "ref", vals );
+ }
+ }
+
+ /* RFC 3045 attributes: vendorName and vendorVersion */
+ val.bv_val = SLAPD_VENDOR_NAME;
+ val.bv_len = strlen( val.bv_val );
+ attrlist_replace( &e->e_attrs, "vendorName", vals );
+ val.bv_val = slapd_get_version_value();
+ val.bv_len = strlen( val.bv_val );
+ attrlist_replace( &e->e_attrs, "vendorVersion", vals );
+ slapi_ch_free( (void **)&val.bv_val );
+
+ /* Server Data Version */
+ if (( val.bv_val = (char*)get_server_dataversion()) != NULL ) { /* jcm cast away const */
+ val.bv_len = strlen( val.bv_val );
+ attrlist_replace( &e->e_attrs, attr_dataversion, vals );
+ }
+
+ /* machine data suffix
+ * this has been added in 4.0 for replication purpose
+ * and since 5.0 is now unused by the core server,
+ * however some functionalities of the console framework 5.01
+ * still depend on this
+ */
+ if (( val.bv_val = get_config_DN()) != NULL ) {
+ val.bv_len = strlen( val.bv_val );
+ attrlist_replace( &e->e_attrs, ATTR_NETSCAPEMDSUFFIX, vals );
+ }
+
+#ifdef notdef
+ /* XXXggood testing - print the size of the changelog db */
+ {
+ unsigned int clsize;
+ clsize = get_changelog_size();
+ sprintf( buf, "%u", clsize );
+ val.bv_val = buf;
+ val.bv_len = strlen( buf );
+ attrlist_replace( &e->e_attrs, "changelogsize", vals );
+ slapi_ch_free((void**)&val.bv_val );
+ }
+#endif /* notdef */
+
+ /* vlvsearch is list of dns to VLV Search Specifications */
+ attrlist_delete( &e->e_attrs, "vlvsearch");
+ cookie = NULL;
+ be = slapi_get_first_backend(&cookie);
+ while (be)
+ {
+ if(!be->be_private)
+ {
+ /* Generate searches to find the VLV Search Specifications */
+ int r;
+ Slapi_PBlock *resultpb= NULL;
+ Slapi_Entry** entry = NULL;
+ Slapi_DN dn;
+ slapi_sdn_init(&dn);
+ be_getconfigdn(be,&dn);
+ resultpb= slapi_search_internal( slapi_sdn_get_ndn(&dn), LDAP_SCOPE_ONELEVEL, "objectclass=vlvsearch", NULL, NULL, 1);
+ slapi_sdn_done(&dn);
+ slapi_pblock_get( resultpb, SLAPI_PLUGIN_INTOP_SEARCH_ENTRIES, &entry );
+ slapi_pblock_get( resultpb, SLAPI_PLUGIN_INTOP_RESULT, &r );
+ if(r==LDAP_SUCCESS)
+ {
+ for (; *entry; ++entry)
+ {
+ val.bv_val = slapi_entry_get_dn(*entry);
+ val.bv_len = strlen( val.bv_val );
+ attrlist_merge( &e->e_attrs, "vlvsearch", vals );
+ }
+ }
+ slapi_free_search_results_internal(resultpb);
+ slapi_pblock_destroy(resultpb);
+ }
+
+ be = slapi_get_next_backend (cookie);
+ }
+ slapi_ch_free ((void **)&cookie);
+ *returncode= LDAP_SUCCESS;
+ return SLAPI_DSE_CALLBACK_OK;
+}
+
+
+
+/*
+ * Handle a modification request on the root DSE. Only certain
+ * attributes are writable. If an attempt to modify an attribute
+ * which is not allowed, return LDAP_UNWILLING_TO_PERFORM, unless
+ * the modification is disallowed because of ACL checking, in which
+ * case LDAP_INSUFFICIENT_ACCESS is returned.
+ */
+int
+modify_root_dse( Slapi_PBlock *pb, Slapi_Entry *entryBefore, Slapi_Entry *e, int *returncode, char *returntext, void *arg )
+{
+ LDAPMod **mods;
+
+ /*
+ * Make a pass through the attributes and check them
+ * to make sure none are computed. Also check for
+ * reflected attributes and reject those as well.
+ * Eventually, some reflected attributes might be
+ * writable, but for now, none are.
+ */
+
+ slapi_pblock_get( pb, SLAPI_MODIFY_MODS, &mods );
+ if ( NULL != mods )
+ {
+ int i;
+ for ( i = 0; NULL != mods[ i ]; i++ )
+ {
+ if ( rootdse_is_readonly_attr( mods[ i ]->mod_type ))
+ {
+ /* The modification is disallowed */
+ *returncode = LDAP_UNWILLING_TO_PERFORM;
+ strcpy(returntext,"Modification of these root DSE attributes not allowed");
+ return SLAPI_DSE_CALLBACK_ERROR;
+ }
+ }
+ }
+
+ *returncode= LDAP_SUCCESS;
+ return SLAPI_DSE_CALLBACK_OK; /* success -- apply the changes */
+}
diff --git a/ldap/servers/slapd/rwlock.c b/ldap/servers/slapd/rwlock.c
new file mode 100644
index 00000000..8642a9ea
--- /dev/null
+++ b/ldap/servers/slapd/rwlock.c
@@ -0,0 +1,222 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+/*
+ * rwlock.c - generic multiple reader, single-writer locking routines.
+ *
+ * The general idea is:
+ *
+ * If you have a data structure which you'd like to allow multiple threads
+ * to read, but only one thread at a time to write, you include in your
+ * data structure a pointer to an rwl structure, and call rwl_new() to
+ * obtain an allocated and initialized rwl structure.
+ *
+ * Then, call the appropriate functions via the provided function pointers
+ * to acquire/relinquish read or write locks on your data structure. You
+ * may want to provide some convenience macros to make the code prettier.
+ *
+ * The semantics are:
+ * - a thread attempting to obtain a read lock will succeed immediately as
+ * long as there are no threads with write locks.
+ * - a thread attempting to obtain a write lock will wait until all readers
+ * have relinquished their read locks.
+ * - a thread attempting to obtain a write lock blocks other threads from
+ * obtaining read locks. As long as all readers release their locks,
+ * the write will eventually get the lock.
+ */
+
+#include "slap.h"
+
+#include <prlock.h>
+#include <prcvar.h>
+#include "rwlock.h"
+
+/*
+ * Function: __rwl_acquire_read_lock
+ *
+ * Description: acquire a read lock.
+ *
+ * Arguments: rp: pointer to an rwl stucture
+ *
+ * Returns: 0 on success, -1 on failure.
+ */
+static int
+__rwl_acquire_read_lock( rwl *rp )
+{
+ if ( rp == NULL ) {
+ return -1;
+ }
+ PR_Lock( rp->rwl_writers_mutex );
+ PR_Lock( rp->rwl_readers_mutex );
+ rp->rwl_num_readers++;
+ (void)PR_Unlock( rp->rwl_readers_mutex );
+ (void)PR_Unlock( rp->rwl_writers_mutex );
+ return 0;
+}
+
+
+
+
+/*
+ * Function: __rwl_acquire_write_lock
+ *
+ * Description: acquire a write lock.
+ *
+ * Arguments: rp: pointer to an rwl stucture
+ *
+ * Returns: 0 on success, -1 on failure.
+ */
+static int
+__rwl_acquire_write_lock( rwl *rp )
+{
+ if ( rp == NULL ) {
+ return -1;
+ }
+ PR_Lock( rp->rwl_writers_mutex );
+ PR_Lock( rp->rwl_readers_mutex );
+ rp->rwl_writer_waiting = 1;
+ while ( rp->rwl_num_readers > 0 ) {
+ if ( PR_WaitCondVar( rp->rwl_writer_waiting_cv, PR_INTERVAL_NO_TIMEOUT ) != 0 ) {
+ (void)PR_Unlock( rp->rwl_writers_mutex );
+ (void)PR_Unlock( rp->rwl_readers_mutex );
+ return -1;
+ }
+ }
+ /* XXXggood should rwl_writer_waiting be set zero here? */
+ return 0;
+}
+
+
+
+
+/*
+ * Function: __rwl_relinquish_read_lock
+ *
+ * Description: relinquish a read lock.
+ *
+ * Arguments: rp: pointer to an rwl stucture
+ *
+ * Returns: 0 on success, -1 on failure.
+ */
+static int
+__rwl_relinquish_read_lock( rwl *rp )
+{
+ if ( rp == NULL ) {
+ return -1;
+ }
+ PR_Lock( rp->rwl_readers_mutex );
+ if ( --rp->rwl_num_readers == 0 && rp->rwl_writer_waiting ) {
+ PR_NotifyCondVar( rp->rwl_writer_waiting_cv );
+ }
+ (void)PR_Unlock( rp->rwl_readers_mutex );
+ return 0;
+}
+
+
+
+
+/*
+ * Function: __rwl_relinquish_write_lock
+ *
+ * Description: relinquish a write lock.
+ *
+ * Arguments: rp: pointer to an rwl stucture
+ *
+ * Returns: 0 on success, -1 on failure.
+ */
+static int
+__rwl_relinquish_write_lock( rwl *rp )
+{
+ if ( rp == NULL ) {
+ return -1;
+ }
+ rp->rwl_writer_waiting = 0;
+ (void)PR_Unlock( rp->rwl_readers_mutex );
+ (void)PR_Unlock( rp->rwl_writers_mutex );
+ return 0;
+}
+
+
+
+/*
+ * Function: rwl_new
+ *
+ * Description: allocate and initialize a wrl structure.
+ *
+ * Arguments: none
+ *
+ * Returns: on success, returns a pointer to an allocated, initialized rwl structure.
+ * on failure, returns NULL.
+ *
+ */
+rwl *
+rwl_new()
+{
+ rwl *rp;
+
+ if (( rp = (rwl *)malloc( sizeof( rwl ))) == NULL ) {
+ return NULL;
+ }
+
+ if (( rp->rwl_readers_mutex = PR_NewLock()) == NULL ) {
+ free( rp );
+ return NULL;
+ }
+ if (( rp->rwl_writers_mutex = PR_NewLock()) == NULL ) {
+ PR_DestroyLock( rp->rwl_readers_mutex );
+ free( rp );
+ return NULL;
+ }
+ if (( rp->rwl_writer_waiting_cv = PR_NewCondVar( rp->rwl_readers_mutex )) == NULL ) {
+ PR_DestroyLock( rp->rwl_readers_mutex );
+ PR_DestroyLock( rp->rwl_writers_mutex );
+ free( rp );
+ }
+ rp->rwl_num_readers = rp->rwl_writer_waiting = 0;
+
+ rp->rwl_acquire_read_lock = __rwl_acquire_read_lock;
+ rp->rwl_relinquish_read_lock = __rwl_relinquish_read_lock;
+
+ rp->rwl_acquire_write_lock = __rwl_acquire_write_lock;
+ rp->rwl_relinquish_write_lock = __rwl_relinquish_write_lock;
+
+ return rp;
+}
+
+
+
+
+/*
+ * Function: rwl_free
+ *
+ * Description: deallocates and frees an rwl structure.
+ *
+ * Arguments: rh: handle to an rwl structure.
+ *
+ * Returns: nothing
+ */
+void
+rwl_free( rwl **rh )
+{
+ rwl *rp;
+
+ if ( rh == NULL || *rh == NULL ) {
+ return;
+ }
+ rp = *rh;
+
+ if ( rp->rwl_readers_mutex != NULL ) {
+ PR_DestroyLock( rp->rwl_readers_mutex );
+ }
+ if ( rp->rwl_writers_mutex != NULL ) {
+ PR_DestroyLock( rp->rwl_writers_mutex );
+ }
+ if ( rp->rwl_writer_waiting_cv != NULL ) {
+ PR_DestroyCondVar( rp->rwl_writer_waiting_cv );
+ }
+ memset( rp, '\0', sizeof( rwl ));
+ free( rp );
+ *rh = NULL;
+}
diff --git a/ldap/servers/slapd/rwlock.h b/ldap/servers/slapd/rwlock.h
new file mode 100644
index 00000000..112f1de6
--- /dev/null
+++ b/ldap/servers/slapd/rwlock.h
@@ -0,0 +1,28 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+#ifndef _RWLOCK_H_
+#define _RWLOCK_H_
+
+#include <prlock.h>
+#include <prcvar.h>
+
+typedef struct _rwl {
+ PRLock *rwl_readers_mutex;
+ PRLock *rwl_writers_mutex;
+ PRCondVar *rwl_writer_waiting_cv;
+ int rwl_num_readers;
+ int rwl_writer_waiting;
+
+ int (*rwl_acquire_read_lock)( struct _rwl * );
+ int (*rwl_relinquish_read_lock)( struct _rwl * );
+
+ int (*rwl_acquire_write_lock)( struct _rwl * );
+ int (*rwl_relinquish_write_lock)( struct _rwl * );
+} rwl;
+
+extern rwl *rwl_new();
+extern void rwl_free( rwl **rh );
+#endif /* _RWLOCK_H_ */
diff --git a/ldap/servers/slapd/sasl_io.c b/ldap/servers/slapd/sasl_io.c
new file mode 100644
index 00000000..7345be35
--- /dev/null
+++ b/ldap/servers/slapd/sasl_io.c
@@ -0,0 +1,334 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2004 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+
+#include "slap.h"
+#include "slapi-plugin.h"
+#include "fe.h"
+#include <sasl.h>
+
+/*
+ * I/O Shim Layer for SASL Encryption
+ * The 'handle' is a pointer to a sasl_connection structure.
+ */
+
+#define SASL_IO_BUFFER_SIZE 1024
+
+/*
+ * SASL sends its encrypted PDU's with an embedded 4-byte length
+ * at the beginning (in network byte order). We peek inside the
+ * received data off the wire to find this length, and use it
+ * to determine when we have read an entire SASL PDU.
+ * So when we have that there is no need for the SASL layer
+ * to do any fancy buffering with it, we always hand it
+ * a full packet.
+ */
+
+struct _sasl_io_private {
+ struct lextiof_socket_private *real_handle;
+ struct lber_x_ext_io_fns *real_iofns;
+ char *decrypted_buffer;
+ size_t decrypted_buffer_size;
+ size_t decrypted_buffer_count;
+ size_t decrypted_buffer_offset;
+ char *encrypted_buffer;
+ size_t encrypted_buffer_size;
+ size_t encrypted_buffer_count;
+ size_t encrypted_buffer_offset;
+ Connection *conn;
+};
+
+int
+sasl_io_enable(Connection *c)
+{
+ int ret = 0;
+
+ LDAPDebug( LDAP_DEBUG_CONNS,
+ "sasl_io_enable for connection %d\n", c->c_connid, 0, 0 );
+ /* Flag that we should enable SASL I/O for the next read operation on this connection */
+ c->c_enable_sasl_io = 1;
+
+ return ret;
+}
+
+static void
+sasl_io_init_buffers(sasl_io_private *sp)
+{
+ sp->decrypted_buffer = slapi_ch_malloc(SASL_IO_BUFFER_SIZE);
+ sp->decrypted_buffer_size = SASL_IO_BUFFER_SIZE;
+ sp->encrypted_buffer = slapi_ch_malloc(SASL_IO_BUFFER_SIZE);
+ sp->encrypted_buffer_size = SASL_IO_BUFFER_SIZE;
+}
+
+/* This function should be called under the connection mutex */
+int
+sasl_io_setup(Connection *c)
+{
+ int ret = 0;
+ struct lber_x_ext_io_fns *func_pointers = NULL;
+ sasl_io_private *sp = (sasl_io_private*) slapi_ch_calloc(1, sizeof(sasl_io_private));
+
+ LDAPDebug( LDAP_DEBUG_CONNS,
+ "sasl_io_setup for connection %d\n", c->c_connid, 0, 0 );
+ /* Get the current functions and store them for later */
+ ber_sockbuf_get_option( c->c_sb, LBER_SOCKBUF_OPT_EXT_IO_FNS, &func_pointers);
+ sp->real_iofns = func_pointers;
+ func_pointers = NULL;
+
+ /* Set up the private structure */
+ sp->real_handle = (struct lextiof_socket_private*) c->c_prfd;
+ sp->conn = c;
+ /* Store the private structure in the connection */
+ c->c_sasl_io_private = sp;
+ /* Insert the sasl i/o functions into the ber layer */
+ func_pointers = (struct lber_x_ext_io_fns *) slapi_ch_malloc(LBER_X_EXTIO_FNS_SIZE);
+ func_pointers->lbextiofn_size = LBER_X_EXTIO_FNS_SIZE;
+ func_pointers->lbextiofn_read = sasl_read_function;
+ func_pointers->lbextiofn_write = sasl_write_function;
+ func_pointers->lbextiofn_writev = NULL;
+ func_pointers->lbextiofn_socket_arg = (struct lextiof_socket_private *) sp;
+ ber_sockbuf_set_option( c->c_sb, LBER_SOCKBUF_OPT_EXT_IO_FNS, func_pointers);
+ /* Setup the data buffers for the fast read path */
+ sasl_io_init_buffers(sp);
+ /* Reset the enable flag, so we don't process it again */
+ c->c_enable_sasl_io = 0;
+ /* Mark the connection as having SASL I/O */
+ c->c_sasl_io = 1;
+ return ret;
+}
+
+int
+sasl_io_cleanup(Connection *c)
+{
+ int ret = 0;
+ sasl_io_private *sp = c->c_sasl_io_private;
+ if (sp) {
+ LDAPDebug( LDAP_DEBUG_CONNS,
+ "sasl_io_cleanup for connection %d\n", c->c_connid, 0, 0 );
+ /* Free the buffers */
+ slapi_ch_free((void**)&(sp->encrypted_buffer));
+ slapi_ch_free((void**)&(sp->decrypted_buffer));
+ /* Put the I/O functions back how they were */
+ ber_sockbuf_set_option( c->c_sb, LBER_SOCKBUF_OPT_EXT_IO_FNS, sp->real_iofns);
+ slapi_ch_free((void**)&sp);
+ c->c_sasl_io_private = NULL;
+ c->c_enable_sasl_io = 0;
+ c->c_sasl_io = 0;
+ }
+ return ret;
+}
+
+
+static void sasl_io_resize_encrypted_buffer(sasl_io_private *sp, size_t requested_size)
+{
+ if (requested_size > sp->encrypted_buffer_size) {
+ sp->encrypted_buffer = slapi_ch_realloc(sp->encrypted_buffer, requested_size);
+ sp->encrypted_buffer_size = requested_size;
+ }
+}
+
+static void sasl_io_resize_decrypted_buffer(sasl_io_private *sp, size_t requested_size)
+{
+ if (requested_size > sp->decrypted_buffer_size) {
+ sp->decrypted_buffer = slapi_ch_realloc(sp->decrypted_buffer, requested_size);
+ sp->decrypted_buffer_size = requested_size;
+ }
+}
+
+static int
+sasl_io_reading_packet(sasl_io_private *sp)
+{
+ return (sp->encrypted_buffer_count > 0);
+}
+
+static int
+sasl_io_finished_packet(sasl_io_private *sp)
+{
+ return (sp->encrypted_buffer_count && (sp->encrypted_buffer_offset == sp->encrypted_buffer_count) );
+}
+
+static int
+sasl_io_start_packet(Connection *c, PRInt32 *err)
+{
+ int ret = 0;
+ unsigned char buffer[4];
+ size_t packet_length = 0;
+
+ ret = PR_Recv(c->c_prfd,buffer,sizeof(buffer),0,PR_INTERVAL_NO_WAIT);
+ if (ret < 0) {
+ *err = PR_GetError();
+ return -1;
+ }
+ if (ret != 0 && ret < sizeof(buffer)) {
+ LDAPDebug( LDAP_DEBUG_ANY,
+ "failed to read sasl packet length on connection %d\n", c->c_connid, 0, 0 );
+ return -1;
+
+ }
+ if (ret == sizeof(buffer)) {
+ /* Decode the length (could use ntohl here ??) */
+ packet_length = buffer[0] << 24 | buffer[1] << 16 | buffer[2] << 8 | buffer[3];
+ LDAPDebug( LDAP_DEBUG_CONNS,
+ "read sasl packet length %ld on connection %d\n", packet_length, c->c_connid, 0 );
+ sasl_io_resize_encrypted_buffer(c->c_sasl_io_private, packet_length);
+ c->c_sasl_io_private->encrypted_buffer_count = packet_length;
+ c->c_sasl_io_private->encrypted_buffer_offset = 0;
+ }
+ return 0;
+}
+static int
+sasl_io_read_packet(Connection *c, PRInt32 *err)
+{
+ size_t ret = 0;
+ sasl_io_private *sp = c->c_sasl_io_private;
+ size_t bytes_remaining_to_read = sp->encrypted_buffer_count - sp->encrypted_buffer_offset;
+
+ ret = PR_Recv(c->c_prfd,sp->encrypted_buffer + sp->encrypted_buffer_offset,bytes_remaining_to_read,0,PR_INTERVAL_NO_WAIT);
+ if (ret < 0) {
+ *err = PR_GetError();
+ return -1;
+ }
+ if (ret > 0) {
+ sp->encrypted_buffer_offset += ret;
+ }
+ return ret;
+}
+
+/* Special recv function for the server connection code */
+/* Here, we return bytes to the caller, either the bytes
+ remaining in the decrypted data buffer, from 'before',
+ or the number of bytes we get decrypted from sasl,
+ or the requested number of bytes whichever is lower.
+ */
+int
+sasl_recv_connection(Connection *c, char *buffer, size_t count,PRInt32 *err)
+{
+ int ret = 0;
+ size_t bytes_in_buffer = 0;
+ sasl_io_private *sp = c->c_sasl_io_private;
+
+ *err = 0;
+ LDAPDebug( LDAP_DEBUG_CONNS,
+ "sasl_recv_connection for connection %d\n", c->c_connid, 0, 0 );
+ /* Do we have decrypted data buffered from 'before' ? */
+ bytes_in_buffer = sp->decrypted_buffer_count - sp->decrypted_buffer_offset;
+ if (0 == bytes_in_buffer) {
+ /* If there wasn't buffered decrypted data, we need to get some... */
+ if (!sasl_io_reading_packet(sp)) {
+ /* First read the packet length and so on */
+ ret = sasl_io_start_packet(c, err);
+ if (0 != ret) {
+ /* Most likely the i/o timed out */
+ return ret;
+ }
+ }
+ /* We now have the packet length
+ * we now must read more data off the wire until we have the complete packet
+ */
+ ret = sasl_io_read_packet(c,err);
+ if (0 == ret || -1 == ret) {
+ return ret;
+ }
+ /* Are we there yet ? */
+ if (sasl_io_finished_packet(sp)) {
+ const char *output_buffer = NULL;
+ unsigned int output_length = 0;
+ LDAPDebug( LDAP_DEBUG_CONNS,
+ "sasl_recv_connection finished reading packet for connection %d\n", c->c_connid, 0, 0 );
+ /* Now decode it */
+ ret = sasl_decode(c->c_sasl_conn,sp->encrypted_buffer,sp->encrypted_buffer_count,&output_buffer,&output_length);
+ if (SASL_OK == ret) {
+ LDAPDebug( LDAP_DEBUG_CONNS,
+ "sasl_recv_connection decoded packet length %d for connection %d\n", output_length, c->c_connid, 0 );
+ if (output_length) {
+ sasl_io_resize_decrypted_buffer(sp,output_length);
+ memcpy(sp->decrypted_buffer,output_buffer,output_length);
+ sp->decrypted_buffer_count = output_length;
+ sp->decrypted_buffer_offset = 0;
+ sp->encrypted_buffer_offset = 0;
+ sp->encrypted_buffer_count = 0;
+ }
+ } else {
+ LDAPDebug( LDAP_DEBUG_ANY,
+ "sasl_recv_connection failed to decode packet for connection %d\n", c->c_connid, 0, 0 );
+ }
+ }
+ }
+ /* Finally, return data from the buffer to the caller */
+ {
+ size_t bytes_to_return = sp->decrypted_buffer_count - sp->decrypted_buffer_offset;
+ if (bytes_to_return > count) {
+ bytes_to_return = count;
+ }
+ memcpy(buffer, sp->decrypted_buffer, bytes_to_return);
+ if (bytes_in_buffer == bytes_to_return) {
+ sp->decrypted_buffer_offset = 0;
+ sp->decrypted_buffer_count = 0;
+ } else {
+ sp->decrypted_buffer_offset += bytes_to_return;
+ }
+ ret = bytes_to_return;
+ }
+ return ret;
+}
+
+int
+sasl_read_function(int ignore, void *buffer, int count, struct lextiof_socket_private *handle )
+{
+ int ret = 0;
+ sasl_io_private *sp = (sasl_io_private*) handle;
+
+ /* First we look to see if we have buffered data that we can return to the caller */
+ if ( (NULL == sp->decrypted_buffer) || ((sp->decrypted_buffer_count - sp->decrypted_buffer_offset) <= 0) ) {
+ /* If we didn't have buffered data, we need to perform I/O and decrypt */
+ PRUint32 buffer_length = 0;
+ /* Read the packet length */
+ ret = read_function(0, &buffer_length, sizeof(buffer_length), sp->real_handle);
+ if (ret) {
+ }
+ /* Read the payload */
+ ret = read_function(0, sp->encrypted_buffer, buffer_length, sp->real_handle);
+ if (ret) {
+ }
+ /* Now we can call sasl to decrypt */
+ /* ret = sasl_decode(sp->conn->c_sasl_conn,sp->encrypted_buffer, buffer_length, sp->decrypted_buffer, &sp->decrypted_buffer_count ); */
+ }
+ /* If things went well, copy the payload for the caller */
+ if ( 0 == ret ) {
+/* size_t real_count = 0;
+
+ if (count >= (sp->buffer_count - sp->buffer_offset) ) {
+ real_count = count;
+ } else {
+ real_count = (sp->buffer_count - sp->buffer_offset);
+ }
+ memcpy(buffer, sp->buffer, real_count);
+ sp->buffer_offset += real_count; */
+ }
+
+ return ret;
+}
+
+int
+sasl_write_function(int ignore, const void *buffer, int count, struct lextiof_socket_private *handle)
+{
+ int ret = 0;
+ sasl_io_private *sp = (sasl_io_private*) handle;
+ const char *crypt_buffer = NULL;
+ unsigned crypt_buffer_size = 0;
+
+ LDAPDebug( LDAP_DEBUG_CONNS,
+ "sasl_write_function writing %d bytes\n", count, 0, 0 );
+ /* Get SASL to encrypt the buffer */
+ ret = sasl_encode(sp->conn->c_sasl_conn, buffer, count, &crypt_buffer, &crypt_buffer_size);
+ LDAPDebug( LDAP_DEBUG_CONNS,
+ "sasl_write_function encoded as %d bytes\n", crypt_buffer_size, 0, 0 );
+
+ ret = write_function(0, crypt_buffer, crypt_buffer_size, sp->real_handle);
+ if (ret) {
+ }
+
+ return ret;
+}
+
diff --git a/ldap/servers/slapd/sasl_map.c b/ldap/servers/slapd/sasl_map.c
new file mode 100644
index 00000000..b9f9fbcf
--- /dev/null
+++ b/ldap/servers/slapd/sasl_map.c
@@ -0,0 +1,472 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2004 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+
+#include "slap.h"
+#include "slapi-plugin.h"
+#include "fe.h"
+#if defined( MACOS ) || defined( DOS ) || defined( _WIN32 ) || defined( NEED_BSDREGEX )
+#include "regex.h"
+#endif
+
+/*
+ * Map SASL identities to LDAP searches
+ */
+
+/*
+ * We maintain a list of mappings to consult
+ */
+
+typedef struct sasl_map_data_ sasl_map_data;
+struct sasl_map_data_ {
+ char *name;
+ char *regular_expression;
+ char *template_base_dn;
+ char *template_search_filter;
+ sasl_map_data *next; /* For linked list */
+};
+
+typedef struct _sasl_map_private {
+ PRLock *lock;
+ sasl_map_data *map_data_list;
+} sasl_map_private;
+
+static char * configDN = "cn=mapping,cn=sasl,cn=config";
+
+/*
+ * DBDB: this is ugly, but right now there is _no_ server-wide
+ * dynamic structure (like a Slapi_Server * type thing). All the code
+ * that needs such a thing instead maintains a static or global variable.
+ * Until we implement the 'right thing', we'll just follow suit here :(
+ */
+
+static sasl_map_private *sasl_map_static_priv = NULL;
+
+static
+sasl_map_private *sasl_map_get_global_priv()
+{
+ /* ASSERT(sasl_map_static_priv) */
+ return sasl_map_static_priv;
+}
+
+static
+sasl_map_private *sasl_map_new_private()
+{
+ PRLock *new_lock = PR_NewLock();
+ sasl_map_private *new_priv = NULL;
+ if (NULL == new_lock) {
+ return NULL;
+ }
+ new_priv = (sasl_map_private *)slapi_ch_calloc(1,sizeof(sasl_map_private));
+ new_priv->lock = new_lock;
+ if (NULL == new_lock) {
+ slapi_ch_free((void**)new_priv);
+ return NULL;
+ }
+ return new_priv;
+}
+
+static void
+sasl_map_free_private(sasl_map_private **priv)
+{
+ PR_DestroyLock((*priv)->lock);
+ slapi_ch_free((void**)priv);
+ *priv = NULL;
+}
+
+/* This function does a shallow copy on the payload data supplied, so the caller should not free it, and it needs to be allocated using slapi_ch_malloc() */
+static
+sasl_map_data *sasl_map_new_data(char *name, char *regex, char *dntemplate, char *filtertemplate)
+{
+ sasl_map_data *new_dp = (sasl_map_data *) slapi_ch_calloc(1,sizeof(sasl_map_data));
+ new_dp->name = name;
+ new_dp->regular_expression = regex;
+ new_dp->template_base_dn = dntemplate;
+ new_dp->template_search_filter = filtertemplate;
+ return new_dp;
+}
+
+static
+sasl_map_data *sasl_map_next(sasl_map_data *dp)
+{
+ return dp->next;
+}
+
+static void
+sasl_map_free_data(sasl_map_data **dp)
+{
+ slapi_ch_free((void**)dp);
+}
+
+static int
+sasl_map_remove_list_entry(sasl_map_private *priv, char *removeme)
+{
+ int ret = 0;
+ int foundit = 0;
+ sasl_map_data *current = NULL;
+ sasl_map_data *previous = NULL;
+ PR_Lock(priv->lock);
+ current = priv->map_data_list;
+ while (current) {
+ if (0 == strcmp(current->name,removeme)) {
+ foundit = 1;
+ if (previous) {
+ /* Unlink it */
+ previous->next = current->next;
+ } else {
+ /* That was the only entry, and now there are none */
+ priv->map_data_list = NULL;
+ }
+ /* Payload free */
+ sasl_map_free_data(&current);
+ /* And no need to look further */
+ break;
+ }
+ previous = current;
+ current = current->next;
+ }
+ if (!foundit) {
+ ret = -1;
+ }
+ PR_Unlock(priv->lock);
+ return ret;
+}
+
+static int
+sasl_map_insert_list_entry(sasl_map_private *priv, sasl_map_data *dp)
+{
+ int ret = 0;
+ int ishere = 0;
+ sasl_map_data *current = NULL;
+ PR_Lock(priv->lock);
+ /* Check to see if it's here already */
+ current = priv->map_data_list;
+ while (current && current->next) {
+ current = current->next;
+ }
+ if (ishere) {
+ return -1;
+ }
+ /* current now points to the end of the list or NULL */
+ if (NULL == priv->map_data_list) {
+ priv->map_data_list = dp;
+ } else {
+ current->next = dp;
+ }
+ PR_Unlock(priv->lock);
+ return ret;
+}
+
+/*
+ * Functions to handle config operations
+ */
+
+/**
+ * Get a list of child DNs
+ * DBDB these functions should be folded into libslapd because it's a copy of a function in ssl.c
+ */
+static char **
+getChildren( char *dn ) {
+ Slapi_PBlock *new_pb = NULL;
+ Slapi_Entry **e;
+ int search_result = 1;
+ int nEntries = 0;
+ char **list = NULL;
+
+ new_pb = slapi_search_internal ( dn, LDAP_SCOPE_ONELEVEL,
+ "(objectclass=nsSaslMapping)",
+ NULL, NULL, 0);
+
+ slapi_pblock_get( new_pb, SLAPI_NENTRIES, &nEntries);
+ if ( nEntries > 0 ) {
+ slapi_pblock_get( new_pb, SLAPI_PLUGIN_INTOP_RESULT, &search_result);
+ slapi_pblock_get( new_pb, SLAPI_PLUGIN_INTOP_SEARCH_ENTRIES, &e);
+ if ( e != NULL ) {
+ int i;
+ list = (char **)slapi_ch_malloc( sizeof(*list) * (nEntries + 1));
+ for ( i = 0; e[i] != NULL; i++ ) {
+ list[i] = slapi_ch_strdup(slapi_entry_get_dn(e[i]));
+ }
+ list[nEntries] = NULL;
+ }
+ }
+ slapi_free_search_results_internal(new_pb);
+ slapi_pblock_destroy(new_pb );
+ return list;
+}
+
+/**
+ * Free a list of child DNs
+ */
+static void
+freeChildren( char **list ) {
+ if ( list != NULL ) {
+ int i;
+ for ( i = 0; list[i] != NULL; i++ ) {
+ slapi_ch_free( (void **)(&list[i]) );
+ }
+ slapi_ch_free( (void **)(&list) );
+ }
+}
+
+
+/**
+ * Get a particular entry
+ */
+static Slapi_Entry *
+getConfigEntry( const char *dn, Slapi_Entry **e2 ) {
+ Slapi_DN sdn;
+
+ slapi_sdn_init_dn_byref( &sdn, dn );
+ slapi_search_internal_get_entry( &sdn, NULL, e2,
+ plugin_get_default_component_id());
+ slapi_sdn_done( &sdn );
+ return *e2;
+}
+
+/**
+ * Free an entry
+ */
+static void
+freeConfigEntry( Slapi_Entry ** e ) {
+ if ( (e != NULL) && (*e != NULL) ) {
+ slapi_entry_free( *e );
+ *e = NULL;
+ }
+}
+
+static int
+sasl_map_config_parse_entry(Slapi_Entry *entry, sasl_map_data **new_dp)
+{
+ int ret = 0;
+ char *regex = NULL;
+ char *basedntemplate = NULL;
+ char *filtertemplate = NULL;
+ char *map_name = NULL;
+
+ *new_dp = NULL;
+ regex = slapi_entry_attr_get_charptr( entry, "nsSaslMapRegexString" );
+ basedntemplate = slapi_entry_attr_get_charptr( entry, "nsSaslMapBaseDNTemplate" );
+ filtertemplate = slapi_entry_attr_get_charptr( entry, "nsSaslMapFilterTemplate" );
+ map_name = slapi_entry_attr_get_charptr( entry, "cn" );
+
+ if ( (NULL == regex) || (NULL == basedntemplate) || (NULL == filtertemplate) ) {
+ /* Invalid entry */
+ ret = -1;
+ } else {
+ /* Make the new dp */
+ *new_dp = sasl_map_new_data(map_name, regex, basedntemplate, filtertemplate);
+ }
+
+ if (ret) {
+ slapi_ch_free((void **) &regex);
+ slapi_ch_free((void **) &basedntemplate);
+ slapi_ch_free((void **) &filtertemplate);
+ }
+ return ret;
+}
+
+static int
+sasl_map_read_config_startup(sasl_map_private *priv)
+{
+ char **map_entry_list = NULL;
+ int ret = 0;
+
+ LDAPDebug( LDAP_DEBUG_TRACE, "-> sasl_map_read_config_startup\n", 0, 0, 0 );
+ if((map_entry_list = getChildren(configDN))) {
+ char **map_entry = NULL;
+ Slapi_Entry *entry = NULL;
+ sasl_map_data *dp = NULL;
+
+ for (map_entry = map_entry_list; *map_entry && !ret; map_entry++) {
+ getConfigEntry( *map_entry, &entry );
+ if ( entry == NULL ) {
+ continue;
+ }
+ ret = sasl_map_config_parse_entry(entry,&dp);
+ if (ret) {
+ LDAPDebug( LDAP_DEBUG_ANY, "sasl_map_read_config_startup failed to parse entry\n", 0, 0, 0 );
+ } else {
+ ret = sasl_map_insert_list_entry(priv,dp);
+ if (ret) {
+ LDAPDebug( LDAP_DEBUG_ANY, "sasl_map_read_config_startup failed to insert entry\n", 0, 0, 0 );
+ }
+ }
+ freeConfigEntry( &entry );
+ }
+ freeChildren( map_entry_list );
+ }
+ LDAPDebug( LDAP_DEBUG_TRACE, "<- sasl_map_read_config_startup\n", 0, 0, 0 );
+ return ret;
+}
+
+int
+sasl_map_config_add(Slapi_PBlock *pb, Slapi_Entry* entryBefore, Slapi_Entry* e, int *returncode, char *returntext, void *arg)
+{
+ int ret = 0;
+ sasl_map_data *dp = NULL;
+ sasl_map_private *priv = sasl_map_get_global_priv();
+ LDAPDebug( LDAP_DEBUG_TRACE, "-> sasl_map_config_add\n", 0, 0, 0 );
+ ret = sasl_map_config_parse_entry(entryBefore,&dp);
+ if (!ret && dp) {
+ ret = sasl_map_insert_list_entry(priv,dp);
+ }
+ if (0 == ret) {
+ ret = SLAPI_DSE_CALLBACK_OK;
+ } else {
+ returntext = "sasl map entry rejected";
+ *returncode = LDAP_UNWILLING_TO_PERFORM;
+ ret = SLAPI_DSE_CALLBACK_ERROR;
+ }
+ LDAPDebug( LDAP_DEBUG_TRACE, "<- sasl_map_config_add\n", 0, 0, 0 );
+ return ret;
+}
+
+int
+sasl_map_config_delete(Slapi_PBlock *pb, Slapi_Entry* entryBefore, Slapi_Entry* e, int *returncode, char *returntext, void *arg)
+{
+ int ret = 0;
+ sasl_map_private *priv = sasl_map_get_global_priv();
+ char *entry_name = NULL;
+
+ LDAPDebug( LDAP_DEBUG_TRACE, "-> sasl_map_config_delete\n", 0, 0, 0 );
+ entry_name = slapi_entry_attr_get_charptr( entryBefore, "cn" );
+ if (entry_name) {
+ /* remove this entry from the list */
+ ret = sasl_map_remove_list_entry(priv,entry_name);
+ slapi_ch_free((void **) &entry_name);
+ }
+ if (ret) {
+ ret = SLAPI_DSE_CALLBACK_ERROR;
+ returntext = "can't delete sasl map entry";
+ *returncode = LDAP_OPERATIONS_ERROR;
+ } else {
+ ret = SLAPI_DSE_CALLBACK_OK;
+ }
+ LDAPDebug( LDAP_DEBUG_TRACE, "<- sasl_map_config_delete\n", 0, 0, 0 );
+ return ret;
+}
+
+/* Start and stop the sasl mapping code */
+int sasl_map_init()
+{
+ int ret = 0;
+ sasl_map_private *priv = NULL;
+ /* Make the private structure */
+ priv = sasl_map_new_private();
+ if (priv) {
+ /* Store in the static var */
+ sasl_map_static_priv = priv;
+ /* Read the config on startup */
+ ret = sasl_map_read_config_startup(priv);
+ } else {
+ ret = -1;
+ }
+ return ret;
+}
+
+int sasl_map_done()
+{
+ int ret = 0;
+ /* Free the map list */
+ /* Free the private structure */
+ return ret;
+}
+
+static sasl_map_data*
+sasl_map_first(sasl_map_private *priv)
+{
+ sasl_map_data *result = NULL;
+ PR_Lock(priv->lock);
+ result = priv->map_data_list;
+ PR_Unlock(priv->lock);
+ return result;
+}
+
+static int
+sasl_map_check(sasl_map_data *dp, char *sasl_user_and_realm, char **ldap_search_base, char **ldap_search_filter)
+{
+ int ret = 0;
+ int matched = 0;
+ char *recomp_result = NULL;
+ LDAPDebug( LDAP_DEBUG_TRACE, "-> sasl_map_check\n", 0, 0, 0 );
+ /* DBDB: currently using the rather old internal slapd regex library, which is not thread-safe */
+ /* So lock it first */
+ slapd_re_lock();
+ /* Compile the regex */
+ recomp_result = slapd_re_comp(dp->regular_expression);
+ if (recomp_result) {
+ LDAPDebug( LDAP_DEBUG_ANY, "sasl_map_check : re_comp failed for expression (%s)\n", dp->regular_expression, 0, 0 );
+ }
+ matched = slapd_re_exec(sasl_user_and_realm);
+ LDAPDebug( LDAP_DEBUG_TRACE, "regex: %s, id: %s, %s\n", dp->regular_expression, sasl_user_and_realm, matched ? "matched" : "didn't match" );
+ if (matched) {
+ if (matched == 1) {
+ /* Allocate buffers for the returned strings */
+ /* We already computed this, so we could pass it in to speed up a little */
+ size_t userrealmlen = strlen(sasl_user_and_realm);
+ /* These lengths could be precomputed and stored in the dp */
+ *ldap_search_base = (char *) slapi_ch_malloc(userrealmlen + strlen(dp->template_base_dn) + 1);
+ *ldap_search_filter = (char *) slapi_ch_malloc(userrealmlen + strlen(dp->template_search_filter) + 1);
+ slapd_re_subs(dp->template_base_dn,*ldap_search_base);
+ slapd_re_subs(dp->template_search_filter,*ldap_search_filter);
+ LDAPDebug( LDAP_DEBUG_TRACE, "mapped base dn: %s, filter: %s\n", ldap_search_base, ldap_search_filter, 0 );
+ ret = 1;
+ } else {
+ LDAPDebug( LDAP_DEBUG_ANY, "sasl_map_check : re_exec failed\n", 0, 0, 0 );
+ }
+ }
+ slapd_re_unlock();
+ LDAPDebug( LDAP_DEBUG_TRACE, "<- sasl_map_check\n", 0, 0, 0 );
+ return ret;
+}
+
+static char *
+sasl_map_str_concat(char *s1, char *s2)
+{
+ if (NULL == s2) {
+ return (slapi_ch_strdup(s1));
+ } else {
+ size_t s1len = strlen(s1);
+ size_t length = s1len + + strlen(s2);
+ char *newstr = slapi_ch_malloc(length + 2);
+ sprintf(newstr,"%s@%s",s1,s2);
+ return newstr;
+ }
+}
+
+/* Actually perform a mapping
+ * Takes a sasl identity string, and returns an LDAP search spec to be used to find the entry
+ * returns 1 if matched, 0 otherwise
+ */
+int
+sasl_map_domap(char *sasl_user, char *sasl_realm, char **ldap_search_base, char **ldap_search_filter)
+{
+ int ret = 0;
+ sasl_map_data *this_map = NULL;
+ char *sasl_user_and_realm = NULL;
+ sasl_map_private *priv = sasl_map_get_global_priv();
+ *ldap_search_base = NULL;
+ *ldap_search_filter = NULL;
+ LDAPDebug( LDAP_DEBUG_TRACE, "-> sasl_map_domap\n", 0, 0, 0 );
+ sasl_user_and_realm = sasl_map_str_concat(sasl_user,sasl_realm);
+ /* Walk the list of maps */
+ this_map = sasl_map_first(priv);
+ while (this_map) {
+ int matched = 0;
+ /* If one matches, then make the search params */
+ matched = sasl_map_check(this_map, sasl_user_and_realm, ldap_search_base, ldap_search_filter);
+ if (1 == matched) {
+ ret = 1;
+ break;
+ }
+ this_map = sasl_map_next(this_map);
+ }
+ if (sasl_user_and_realm) {
+ slapi_ch_free((void**)&sasl_user_and_realm);
+ }
+ LDAPDebug( LDAP_DEBUG_TRACE, "<- sasl_map_domap (%s)\n", (1 == ret) ? "mapped" : "not mapped", 0, 0 );
+ return ret;
+}
+
diff --git a/ldap/servers/slapd/saslbind.c b/ldap/servers/slapd/saslbind.c
new file mode 100644
index 00000000..6a488d4f
--- /dev/null
+++ b/ldap/servers/slapd/saslbind.c
@@ -0,0 +1,908 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+
+#include <slap.h>
+#include <fe.h>
+#include <sasl.h>
+#include <saslplug.h>
+#include <saslmod.h>
+#ifndef _WIN32
+#include <unistd.h>
+#endif
+
+/* No GSSAPI on Windows */
+#if !defined(_WIN32)
+#define BUILD_GSSAPI 1
+#endif
+
+static char *serverfqdn;
+
+/*
+ * utility functions needed by the sasl library
+ */
+
+int sasl_os_gethost(char *buf, int len)
+{
+ int rc;
+
+ rc = gethostname(buf, len);
+ LDAPDebug(LDAP_DEBUG_TRACE, "sasl_os_gethost %s\n", buf, 0, 0);
+ return ( rc == 0 ? SASL_OK : SASL_FAIL );
+}
+
+void *sasl_mutex_alloc(void)
+{
+ return PR_NewLock();
+}
+
+int sasl_mutex_lock(void *mutex)
+{
+ PR_Lock(mutex);
+ return SASL_OK;
+}
+
+int sasl_mutex_unlock(void *mutex)
+{
+ if (PR_Unlock(mutex) == PR_SUCCESS) return SASL_OK;
+ return SASL_FAIL;
+}
+
+void sasl_mutex_free(void *mutex)
+{
+ PR_DestroyLock(mutex);
+}
+
+/*
+ * sasl library callbacks
+ */
+
+static int ids_sasl_getopt(
+ void *context,
+ const char *plugin_name,
+ const char *option,
+ const char **result,
+ unsigned *len
+)
+{
+ unsigned tmplen;
+
+ LDAPDebug(LDAP_DEBUG_TRACE, "ids_sasl_getopt: plugin=%s option=%s\n",
+ plugin_name ? plugin_name : "", option, 0);
+
+ if (len == NULL) len = &tmplen;
+
+ *result = NULL;
+ *len = 0;
+
+ if (strcasecmp(option, "enable") == 0) {
+ *result = "USERDB/DIGEST-MD5,GSSAPI/GSSAPI";
+ } else if (strcasecmp(option, "has_plain_passwords") == 0) {
+ *result = "yes";
+ } else if (strcasecmp(option, "LOG_LEVEL") == 0) {
+ if (LDAPDebugLevelIsSet(LDAP_DEBUG_TRACE)) {
+ *result = "6"; /* SASL_LOG_TRACE */
+ }
+ }
+
+ if (*result) *len = strlen(*result);
+
+ return SASL_OK;
+}
+
+static int ids_sasl_log(
+ void *context,
+ int level,
+ const char *message
+)
+{
+ switch (level) {
+ case SASL_LOG_ERR: /* log unusual errors (default) */
+ slapi_log_error(SLAPI_LOG_FATAL, "sasl", "%s", message);
+ break;
+
+ case SASL_LOG_FAIL: /* log all authentication failures */
+ case SASL_LOG_WARN: /* log non-fatal warnings */
+ case SASL_LOG_NOTE: /* more verbose than LOG_WARN */
+ case SASL_LOG_DEBUG: /* more verbose than LOG_NOTE */
+ case SASL_LOG_TRACE: /* traces of internal protocols */
+ case SASL_LOG_PASS: /* traces of internal protocols, including
+ * passwords */
+ LDAPDebug(LDAP_DEBUG_ANY, "sasl(%d): %s", level, message, 0);
+ break;
+
+ case SASL_LOG_NONE: /* don't log anything */
+ default:
+ break;
+ }
+ return SASL_OK;
+}
+
+static int ids_sasl_proxy_policy(
+ sasl_conn_t *conn,
+ void *context,
+ const char *requested_user, int rlen,
+ const char *auth_identity, int alen,
+ const char *def_realm, int urlen,
+ struct propctx *propctx
+)
+{
+ int retVal = SASL_OK;
+ /* do not permit sasl proxy authorization */
+ /* if the auth_identity is null or empty string, allow the sasl request to go thru */
+ if ( (auth_identity != NULL ) && ( strlen(auth_identity) > 0 ) ) {
+ Slapi_DN authId , reqUser;
+ slapi_sdn_init_dn_byref(&authId,auth_identity);
+ slapi_sdn_init_dn_byref(&reqUser,requested_user);
+ if (slapi_sdn_compare((const Slapi_DN *)&reqUser,(const Slapi_DN *) &authId) != 0) {
+ LDAPDebug(LDAP_DEBUG_TRACE,
+ "sasl proxy auth not permitted authid=%s user=%s\n",
+ auth_identity, requested_user, 0);
+ retVal = SASL_NOAUTHZ;
+ }
+ slapi_sdn_done(&authId);
+ slapi_sdn_done(&reqUser);
+ }
+ return retVal;
+}
+
+static void ids_sasl_user_search(
+ char *basedn,
+ int scope,
+ char *filter,
+ LDAPControl **ctrls,
+ char **attrs,
+ int attrsonly,
+ Slapi_Entry **ep,
+ int *foundp
+)
+{
+ Slapi_Entry **entries = NULL;
+ Slapi_PBlock *pb;
+ int i, ret;
+
+ LDAPDebug(LDAP_DEBUG_TRACE, "sasl user search basedn=\"%s\" filter=\"%s\"\n", basedn, filter, 0);
+
+ /* TODO: set size and time limits */
+
+ pb = slapi_search_internal(basedn, scope, filter,
+ ctrls, attrs, attrsonly);
+ if (pb == NULL) {
+ LDAPDebug(LDAP_DEBUG_TRACE, "null pblock from slapi_search_internal\n", 0, 0, 0);
+ goto out;
+ }
+
+ slapi_pblock_get(pb, SLAPI_PLUGIN_INTOP_RESULT, &ret);
+ if (ret != LDAP_SUCCESS) {
+ LDAPDebug(LDAP_DEBUG_TRACE, "sasl user search failed basedn=\"%s\" "
+ "filter=\"%s\": %s\n",
+ basedn, filter, ldap_err2string(ret));
+ goto out;
+ }
+
+ slapi_pblock_get(pb, SLAPI_PLUGIN_INTOP_SEARCH_ENTRIES, &entries);
+ if (entries == NULL) goto out;
+
+ for (i = 0; entries[i]; i++) {
+ (*foundp)++;
+ if (*ep != NULL) {
+ slapi_entry_free(*ep);
+ }
+ *ep = slapi_entry_dup(entries[i]);
+ LDAPDebug(LDAP_DEBUG_TRACE, "sasl user search found dn=%s\n",
+ slapi_entry_get_dn(*ep), 0, 0);
+ }
+
+ out:
+
+ if (pb) slapi_free_search_results_internal(pb);
+ return;
+}
+
+/*
+ * Search for an entry representing the sasl user.
+ */
+static Slapi_Entry *ids_sasl_user_to_entry(
+ sasl_conn_t *conn,
+ void *context,
+ const char *user,
+ const char *user_realm
+)
+{
+ int found = 0;
+ unsigned fsize = 0, ulen, rlen = 0;
+ int attrsonly = 0, scope = LDAP_SCOPE_SUBTREE;
+ char filter[1024], *fptr = filter;
+ LDAPControl **ctrls = NULL;
+ Slapi_Entry *entry = NULL;
+ Slapi_DN *sdn;
+ char **attrs = NULL;
+ char *userattr = "uid", *realmattr = NULL, *ufilter = NULL;
+ void *node;
+ int regexmatch = 0;
+ char *regex_ldap_search_base = NULL;
+ char *regex_ldap_search_filter = NULL;
+
+ /* TODO: userattr & realmattr should be configurable */
+
+ /*
+ * Check for dn: prefix. See RFC 2829 section 9.
+ */
+ if (strncasecmp(user, "dn:", 3) == 0) {
+ sprintf(fptr, "(objectclass=*)");
+ scope = LDAP_SCOPE_BASE;
+ ids_sasl_user_search((char*)user+3, scope, filter,
+ ctrls, attrs, attrsonly,
+ &entry, &found);
+ } else {
+ int offset = 0;
+ if (strncasecmp(user,"u:",2) == 0 )
+ offset = 2;
+ /* TODO: quote the filter values */
+
+ /* New regex-based identity mapping : we call it here before the old code.
+ * If there's a match, we skip the old way, otherwise we plow ahead for backwards compatibility reasons
+ */
+
+ regexmatch = sasl_map_domap((char*)user, (char*)user_realm, &regex_ldap_search_base, &regex_ldap_search_filter);
+ if (regexmatch) {
+
+ ids_sasl_user_search(regex_ldap_search_base, scope, regex_ldap_search_filter,
+ ctrls, attrs, attrsonly,
+ &entry, &found);
+
+ /* Free the filter etc */
+ slapi_ch_free((void**)&regex_ldap_search_base);
+ slapi_ch_free((void**)&regex_ldap_search_filter);
+ } else {
+
+ /* Ensure no buffer overflow. */
+ /* We don't know what the upper limits on username and
+ * realm lengths are. There don't seem to be any defined
+ * in the relevant standards. We may find in the future
+ * that a 1K buffer is insufficient for some mechanism,
+ * but it seems unlikely given that the values are exposed
+ * to the end user.
+ */
+ ulen = strlen(user+offset);
+ fsize += strlen(userattr) + ulen;
+ if (realmattr && user_realm) {
+ rlen = strlen(user_realm);
+ fsize += strlen(realmattr) + rlen;
+ }
+ if (ufilter) fsize += strlen(ufilter);
+ fsize += 100; /* includes a good safety margin */
+ if (fsize > 1024) {
+ LDAPDebug(LDAP_DEBUG_ANY, "sasl user name and/or realm too long"
+ " (ulen=%u, rlen=%u)\n", ulen, rlen, 0);
+ return NULL;
+ }
+
+ /* now we can safely write the filter */
+ sprintf(fptr, "(&(%s=%s)", userattr, user+offset);
+ fptr += strlen(fptr);
+ if (realmattr && user_realm) {
+ sprintf(fptr, "(%s=%s)", realmattr, user_realm);
+ fptr += strlen(fptr);
+ }
+ if (ufilter) {
+ if (*ufilter == '(') {
+ sprintf(fptr, "%s", ufilter);
+ } else {
+ sprintf(fptr, "(%s)", ufilter);
+ }
+ fptr += strlen(fptr);
+ }
+ sprintf(fptr, ")");
+
+ /* iterate through the naming contexts */
+ for (sdn = slapi_get_first_suffix(&node, 0); sdn != NULL;
+ sdn = slapi_get_next_suffix(&node, 0)) {
+
+ ids_sasl_user_search((char*)slapi_sdn_get_dn(sdn), scope, filter,
+ ctrls, attrs, attrsonly,
+ &entry, &found);
+ }
+ }
+ }
+
+ if (found == 1) {
+ LDAPDebug(LDAP_DEBUG_TRACE, "sasl user search found this entry: dn:%s, matching filter=%s\n", entry->e_sdn.dn, filter, 0);
+ return entry;
+ }
+
+ if (found == 0) {
+ LDAPDebug(LDAP_DEBUG_TRACE, "sasl user search found no entries matching filter=%s\n", filter, 0, 0);
+ } else {
+ LDAPDebug(LDAP_DEBUG_TRACE, "sasl user search found more than one entry matching filter=%s\n", filter, 0, 0);
+ }
+
+ if (entry) slapi_entry_free(entry);
+ return NULL;
+}
+
+static char *buf2str(const char *buf, unsigned buflen)
+{
+ char *ret;
+
+ ret = (char*)slapi_ch_malloc(buflen+1);
+ memcpy(ret, buf, buflen);
+ ret[buflen] = '\0';
+
+ return ret;
+}
+
+/* Note that in this sasl1 API, when it says 'authid' it really means 'authzid'. */
+static int ids_sasl_canon_user(
+ sasl_conn_t *conn,
+ void *context,
+ const char *userbuf, unsigned ulen,
+ const char *authidbuf, unsigned alen,
+ unsigned flags, const char *user_realm,
+ char *out_user, unsigned out_umax, unsigned *out_ulen,
+ char *out_authid, unsigned out_amax, unsigned *out_alen
+)
+{
+ struct propctx *propctx = sasl_auxprop_getctx(conn);
+ Slapi_Entry *entry = NULL;
+ Slapi_DN *sdn = NULL;
+ char *pw = NULL;
+ char *user = NULL;
+ char *authid = NULL;
+ const char *dn;
+ int isroot = 0;
+ char *clear = NULL;
+
+ user = buf2str(userbuf, ulen);
+ if (user == NULL) {
+ goto fail;
+ }
+ authid = buf2str(authidbuf, alen);
+
+ LDAPDebug(LDAP_DEBUG_TRACE,
+ "ids_sasl_canon_user(user=%s, authzid=%s, realm=%s)\n",
+ user, authid, user_realm ? user_realm : "");
+
+ if (strncasecmp(user, "dn:", 3) == 0) {
+ sdn = slapi_sdn_new();
+ slapi_sdn_set_dn_byval(sdn, user+3);
+ isroot = slapi_dn_isroot(slapi_sdn_get_ndn(sdn));
+ }
+
+ if (isroot) {
+ /* special case directory manager */
+ dn = slapi_sdn_get_ndn(sdn);
+ pw = config_get_rootpw();
+ } else {
+ /* map the sasl username into an entry */
+ entry = ids_sasl_user_to_entry(conn, context, user, user_realm);
+ if (entry == NULL) {
+ goto fail;
+ }
+ dn = slapi_entry_get_ndn(entry);
+ pw = slapi_entry_attr_get_charptr(entry, "userpassword");
+ }
+
+ if (prop_set(propctx, "dn", dn, -1) != 0) {
+ LDAPDebug(LDAP_DEBUG_TRACE, "prop_set(dn) failed\n", 0, 0, 0);
+ goto fail;
+ }
+
+ clear = pw;
+ if (clear) {
+ if (prop_set(propctx, "userpassword", clear, -1) != 0) {
+ /* Failure is benign here because some mechanisms don't support this property */
+ /*LDAPDebug(LDAP_DEBUG_TRACE, "prop_set(userpassword) failed\n", 0, 0, 0);
+ goto fail */ ;
+ }
+ }
+
+ /* TODO: canonicalize */
+ strcpy(out_user, dn);
+ if (authid )
+ {
+ int offset = 0;
+ /* The authid can start with dn:. In such case remove it */
+ if (strncasecmp(authid,"dn:",3) == 0 )
+ offset = 3;
+ strcpy(out_authid, authid+offset);
+ }
+ *out_ulen = -1;
+ *out_alen = -1;
+
+ slapi_entry_free(entry);
+ slapi_ch_free((void**)&user);
+ slapi_ch_free((void**)&authid);
+ slapi_ch_free((void**)&pw);
+ slapi_sdn_free(&sdn);
+
+ return SASL_OK;
+
+ fail:
+ slapi_entry_free(entry);
+ slapi_ch_free((void**)&user);
+ slapi_ch_free((void**)&authid);
+ slapi_ch_free((void**)&pw);
+ slapi_sdn_free(&sdn);
+
+ return SASL_FAIL;
+}
+
+static sasl_callback_t ids_sasl_callbacks[5] =
+{
+ SASL_CB_GETOPT,
+ (IFP) ids_sasl_getopt,
+ NULL,
+ SASL_CB_LOG,
+ (IFP) ids_sasl_log,
+ NULL,
+ SASL_CB_PROXY_POLICY,
+ (IFP) ids_sasl_proxy_policy,
+ NULL,
+ SASL_CB_SERVER_CANON_USER,
+ (IFP) ids_sasl_canon_user,
+ NULL,
+ SASL_CB_LIST_END,
+ (IFP) NULL,
+ NULL
+};
+
+static const char *dn_propnames[] = { "dn", 0 };
+
+/*
+ * initialize the sasl library
+ */
+int ids_sasl_init(void)
+{
+ static int inited = 0;
+ int result;
+
+ LDAPDebug( LDAP_DEBUG_TRACE, "=> ids_sasl_init\n", 0, 0, 0 );
+
+ PR_ASSERT(inited == 0);
+ inited = 1;
+
+ serverfqdn = get_localhost_DNS();
+
+ LDAPDebug(LDAP_DEBUG_TRACE, "sasl service fqdn is: %s\n",
+ serverfqdn, 0, 0);
+
+ result = sasl_server_init(ids_sasl_callbacks, "iDS");
+
+ if (result != SASL_OK) {
+ LDAPDebug(LDAP_DEBUG_TRACE, "failed to initialize sasl library\n",
+ 0, 0, 0);
+ return result;
+ }
+
+ result = sasl_server_add_plugin("USERDB", sasl_userdb_init);
+
+ if (result != SASL_OK) {
+ LDAPDebug(LDAP_DEBUG_TRACE, "failed to add LDAP sasl plugin\n",
+ 0, 0, 0);
+ return result;
+ }
+
+#if defined(BUILD_GSSAPI)
+ result = sasl_server_add_plugin("GSSAPI", sasl_gssapi_init);
+
+ if (result != SASL_OK) {
+ LDAPDebug(LDAP_DEBUG_TRACE, "failed to add LDAP gssapi plugin\n",
+ 0, 0, 0);
+ }
+#endif
+
+ LDAPDebug( LDAP_DEBUG_TRACE, "<= ids_sasl_init\n", 0, 0, 0 );
+
+ return result;
+}
+
+/*
+ * create a sasl server connection
+ */
+void ids_sasl_server_new(Connection *conn)
+{
+ int rc;
+ sasl_conn_t *sasl_conn = NULL;
+ struct propctx *propctx;
+ sasl_security_properties_t secprops = {0};
+
+ LDAPDebug( LDAP_DEBUG_TRACE, "=> ids_sasl_server_new (%s)\n", serverfqdn, 0, 0 );
+
+ rc = sasl_server_new("ldap",
+ serverfqdn,
+ NULL, /* user_realm */
+ NULL, /* iplocalport */
+ NULL, /* ipremoteport */
+ ids_sasl_callbacks,
+ SASL_SUCCESS_DATA,
+ &sasl_conn);
+
+ if (rc != SASL_OK) {
+ LDAPDebug(LDAP_DEBUG_ANY, "sasl_server_new: %s\n",
+ sasl_errstring(rc, NULL, NULL), 0, 0);
+ }
+
+ if (rc == SASL_OK) {
+ propctx = sasl_auxprop_getctx(sasl_conn);
+ if (propctx != NULL) {
+ prop_request(propctx, dn_propnames);
+ }
+ }
+
+ /* Enable security for this connection */
+ secprops.maxbufsize = 2048; /* DBDB: hack */
+ secprops.max_ssf = 0xffffffff;
+ rc = sasl_setprop(sasl_conn, SASL_SEC_PROPS, &secprops);
+ if (rc != SASL_OK) {
+ LDAPDebug(LDAP_DEBUG_ANY, "sasl_setprop: %s\n",
+ sasl_errstring(rc, NULL, NULL), 0, 0);
+ }
+
+ conn->c_sasl_conn = sasl_conn;
+
+ LDAPDebug( LDAP_DEBUG_TRACE, "<= ids_sasl_server_new\n", 0, 0, 0 );
+
+ return;
+}
+
+/*
+ * return sasl mechanisms available on this connection.
+ * caller must free returned charray.
+ */
+char **ids_sasl_listmech(Slapi_PBlock *pb)
+{
+ char **ret, **others;
+ const char *str;
+ char *dupstr;
+ sasl_conn_t *sasl_conn;
+
+ LDAPDebug( LDAP_DEBUG_TRACE, "=> ids_sasl_listmech\n", 0, 0, 0 );
+
+ PR_ASSERT(pb);
+
+ /* hard-wired mechanisms and slapi plugin registered mechanisms */
+ ret = slapi_get_supported_saslmechanisms_copy();
+
+ if (pb->pb_conn == NULL) return ret;
+
+ sasl_conn = (sasl_conn_t*)pb->pb_conn->c_sasl_conn;
+ if (sasl_conn == NULL) return ret;
+
+ /* sasl library mechanisms are connection dependent */
+ PR_Lock(pb->pb_conn->c_mutex);
+ if (sasl_listmech(sasl_conn,
+ NULL, /* username */
+ "", ",", "",
+ &str, NULL, NULL) == SASL_OK) {
+ LDAPDebug(LDAP_DEBUG_TRACE, "sasl library mechs: %s\n", str, 0, 0);
+ /* merge into result set */
+ dupstr = slapi_ch_strdup(str);
+ others = str2charray(dupstr, ",");
+ charray_merge(&ret, others, 1);
+ charray_free(others);
+ slapi_ch_free((void**)&dupstr);
+ }
+ PR_Unlock(pb->pb_conn->c_mutex);
+
+ LDAPDebug( LDAP_DEBUG_TRACE, ">= ids_sasl_listmech\n", 0, 0, 0 );
+
+ return ret;
+}
+
+/*
+ * Determine whether a given sasl mechanism is supported by
+ * this sasl connection. Returns true/false.
+ */
+static int
+ids_sasl_mech_supported(Slapi_PBlock *pb, sasl_conn_t *sasl_conn, const char *mech)
+{
+ int i, ret = 0;
+ char **mechs;
+ char *dupstr;
+ const char *str;
+ int sasl_result = 0;
+
+ LDAPDebug( LDAP_DEBUG_TRACE, "=> ids_sasl_mech_supported\n", 0, 0, 0 );
+
+
+ /* sasl_listmech is not thread-safe, so we lock here */
+ PR_Lock(pb->pb_conn->c_mutex);
+ sasl_result = sasl_listmech(sasl_conn,
+ NULL, /* username */
+ "", ",", "",
+ &str, NULL, NULL);
+ PR_Unlock(pb->pb_conn->c_mutex);
+ if (sasl_result != SASL_OK) {
+ return 0;
+ }
+
+ dupstr = slapi_ch_strdup(str);
+ mechs = str2charray(dupstr, ",");
+
+ for (i = 0; mechs[i] != NULL; i++) {
+ if (strcasecmp(mech, mechs[i]) == 0) {
+ ret = 1;
+ break;
+ }
+ }
+
+ charray_free(mechs);
+ slapi_ch_free((void**)&dupstr);
+
+ LDAPDebug( LDAP_DEBUG_TRACE, "<= ids_sasl_mech_supported\n", 0, 0, 0 );
+
+ return ret;
+}
+
+/*
+ * do a sasl bind and return a result
+ */
+void ids_sasl_check_bind(Slapi_PBlock *pb)
+{
+ int rc, isroot;
+ long t;
+ sasl_conn_t *sasl_conn;
+ struct propctx *propctx;
+ sasl_ssf_t *ssfp;
+ char *activemech = NULL, *mech = NULL;
+ char *username, *dn = NULL;
+ const char *sdata, *errstr;
+ unsigned slen;
+ int continuing = 0;
+ int pwresponse_requested = 0;
+ LDAPControl **ctrls;
+ struct berval bvr, *cred;
+ struct propval dnval[2];
+ char authtype[256]; /* >26 (strlen(SLAPD_AUTH_SASL)+SASL_MECHNAMEMAX+1) */
+ Slapi_Entry *bind_target_entry = NULL, *referral = NULL;
+ Slapi_Backend *be = NULL;
+ char errorbuf[BUFSIZ];
+
+ LDAPDebug( LDAP_DEBUG_TRACE, "=> ids_sasl_check_bind\n", 0, 0, 0 );
+
+ PR_ASSERT(pb);
+ PR_ASSERT(pb->pb_conn);
+
+ continuing = pb->pb_conn->c_flags & CONN_FLAG_SASL_CONTINUE;
+ pb->pb_conn->c_flags &= ~CONN_FLAG_SASL_CONTINUE; /* reset flag */
+
+ sasl_conn = (sasl_conn_t*)pb->pb_conn->c_sasl_conn;
+ if (sasl_conn == NULL) {
+ send_ldap_result( pb, LDAP_AUTH_METHOD_NOT_SUPPORTED, NULL,
+ "sasl library unavailable", 0, NULL );
+ return;
+ }
+
+ slapi_pblock_get(pb, SLAPI_BIND_SASLMECHANISM, &mech);
+ slapi_pblock_get(pb, SLAPI_BIND_CREDENTIALS, &cred);
+ slapi_pblock_get(pb, SLAPI_PWPOLICY, &pwresponse_requested);
+ PR_ASSERT(mech);
+ PR_ASSERT(cred);
+
+ /* Work around a bug in the sasl library. We've told the
+ * library that CRAM-MD5 is disabled, but it gives us a
+ * different error code to SASL_NOMECH.
+ */
+ if (!ids_sasl_mech_supported(pb, sasl_conn, mech)) {
+ rc = SASL_NOMECH;
+ goto sasl_check_result;
+ }
+
+ /* can't do any harm */
+ if (cred->bv_len == 0) cred->bv_val = NULL;
+
+ if (continuing) {
+ /*
+ * RFC 2251: a client may abort a sasl bind negotiation by
+ * sending a bindrequest with a different value in the
+ * mechanism field.
+ */
+ sasl_getprop(sasl_conn, SASL_MECHNAME, (const void**)&activemech);
+ if (activemech == NULL) {
+ LDAPDebug(LDAP_DEBUG_TRACE, "could not get active sasl mechanism\n", 0, 0, 0);
+ goto sasl_start;
+ }
+ if (strcasecmp(activemech, mech) != 0) {
+ LDAPDebug(LDAP_DEBUG_TRACE, "sasl mechanisms differ: active=%s current=%s\n", 0, 0, 0);
+ goto sasl_start;
+ }
+
+ rc = sasl_server_step(sasl_conn,
+ cred->bv_val, cred->bv_len,
+ &sdata, &slen);
+ goto sasl_check_result;
+ }
+
+ sasl_start:
+
+ rc = sasl_server_start(sasl_conn, mech,
+ cred->bv_val, cred->bv_len,
+ &sdata, &slen);
+
+ sasl_check_result:
+
+ switch (rc) {
+ case SASL_OK: /* complete */
+
+ /* retrieve the authenticated username */
+ if (sasl_getprop(sasl_conn, SASL_USERNAME,
+ (const void**)&username) != SASL_OK) {
+ send_ldap_result(pb, LDAP_OPERATIONS_ERROR, NULL,
+ "could not obtain sasl username", 0, NULL);
+ break;
+ }
+
+ LDAPDebug(LDAP_DEBUG_TRACE, "sasl authenticated mech=%s user=%s\n",
+ mech, username, 0);
+
+ /*
+ * Retrieve the DN corresponding to the authenticated user.
+ * This should have been set by the user canon callback
+ * in an auxiliary property called "dn".
+ */
+ propctx = sasl_auxprop_getctx(sasl_conn);
+ if (prop_getnames(propctx, dn_propnames, dnval) == 1) {
+ if (dnval[0].values && dnval[0].values[0]) {
+ dn = slapi_ch_strdup(dnval[0].values[0]);
+ }
+ }
+ if (dn == NULL) {
+ send_ldap_result(pb, LDAP_OPERATIONS_ERROR, NULL,
+ "could not get auth dn from sasl", 0, NULL);
+ break;
+ }
+
+ isroot = slapi_dn_isroot(dn);
+
+ if (!isroot )
+ {
+ /* check if the account is locked */
+ bind_target_entry = get_entry(pb, dn);
+ if ( bind_target_entry == NULL )
+ {
+ break;
+ }
+ if ( check_account_lock(pb, bind_target_entry, pwresponse_requested) == 1) {
+ slapi_entry_free(bind_target_entry);
+ break;
+ }
+ }
+
+ /* see if we negotiated a security layer */
+ if ((sasl_getprop(sasl_conn, SASL_SSF,
+ (const void**)&ssfp) == SASL_OK) && (*ssfp > 0)) {
+ LDAPDebug(LDAP_DEBUG_TRACE, "sasl ssf=%u\n", (unsigned)*ssfp, 0, 0);
+
+ if (pb->pb_conn->c_flags & CONN_FLAG_SSL) {
+ send_ldap_result(pb, LDAP_OPERATIONS_ERROR, NULL,
+ "sasl encryption not supported over ssl",
+ 0, NULL);
+ if ( bind_target_entry != NULL )
+ slapi_entry_free(bind_target_entry);
+ break;
+ } else {
+ /* Enable SASL I/O on the connection now */
+ /* Note that this doesn't go into effect until the next _read_ operation is done */
+ if (0 != sasl_io_enable(pb->pb_conn) ) {
+ send_ldap_result(pb, LDAP_OPERATIONS_ERROR, NULL,
+ "failed to enable sasl i/o",
+ 0, NULL);
+ }
+ }
+ }
+
+ /* set the connection bind credentials */
+ sprintf(authtype, "%s%s", SLAPD_AUTH_SASL, mech);
+ bind_credentials_set(pb->pb_conn, authtype, dn,
+ NULL, NULL, NULL, bind_target_entry);
+
+ /* set the auth response control if requested */
+ slapi_pblock_get(pb, SLAPI_REQCONTROLS, &ctrls);
+ if (slapi_control_present(ctrls, LDAP_CONTROL_AUTH_REQUEST,
+ NULL, NULL)) {
+ add_auth_response_control(pb, dn);
+ }
+
+ if (slapi_mapping_tree_select(pb, &be, &referral, errorbuf) != LDAP_SUCCESS) {
+ send_nobackend_ldap_result( pb );
+ be = NULL;
+ LDAPDebug( LDAP_DEBUG_TRACE, "<= ids_sasl_check_bind\n", 0, 0, 0 );
+ return;
+ }
+
+ if (referral) {
+ send_referrals_from_entry(pb,referral);
+ goto out;
+ }
+
+ slapi_pblock_set( pb, SLAPI_BACKEND, be );
+
+ slapi_pblock_set( pb, SLAPI_PLUGIN, be->be_database );
+ set_db_default_result_handlers(pb);
+
+ /* check password expiry */
+ if (!isroot) {
+ int pwrc;
+
+ pwrc = need_new_pw (pb, &t, bind_target_entry, pwresponse_requested);
+ if ( bind_target_entry != NULL ) {
+ slapi_entry_free(bind_target_entry);
+ bind_target_entry = NULL;
+ }
+
+ switch (pwrc) {
+ case 1:
+ add_pwd_control(pb, LDAP_CONTROL_PWEXPIRED, 0);
+ break;
+ case 2:
+ add_pwd_control(pb, LDAP_CONTROL_PWEXPIRING, t);
+ break;
+ case -1:
+ goto out;
+ default:
+ break;
+ }
+ }
+
+ /* attach the sasl data */
+ if (slen != 0) {
+ bvr.bv_val = (char*)sdata;
+ bvr.bv_len = slen;
+ slapi_pblock_set(pb, SLAPI_BIND_RET_SASLCREDS, &bvr);
+ }
+
+ /* send successful result */
+ send_ldap_result( pb, LDAP_SUCCESS, NULL, NULL, 0, NULL );
+
+ /* TODO: enable sasl security layer */
+
+ /* remove the sasl data from the pblock */
+ slapi_pblock_set(pb, SLAPI_BIND_RET_SASLCREDS, NULL);
+
+ break;
+
+ case SASL_CONTINUE: /* another step needed */
+ pb->pb_conn->c_flags |= CONN_FLAG_SASL_CONTINUE;
+
+ /* attach the sasl data */
+ bvr.bv_val = (char*)sdata;
+ bvr.bv_len = slen;
+ slapi_pblock_set(pb, SLAPI_BIND_RET_SASLCREDS, &bvr);
+
+ /* send continuation result */
+ send_ldap_result( pb, LDAP_SASL_BIND_IN_PROGRESS, NULL,
+ NULL, 0, NULL );
+
+ /* remove the sasl data from the pblock */
+ slapi_pblock_set(pb, SLAPI_BIND_RET_SASLCREDS, NULL);
+
+ break;
+
+ case SASL_NOMECH:
+
+ send_ldap_result(pb, LDAP_AUTH_METHOD_NOT_SUPPORTED, NULL,
+ "sasl mechanism not supported", 0, NULL);
+ break;
+
+ default: /* other error */
+ errstr = sasl_errdetail(sasl_conn);
+
+ send_ldap_result(pb, LDAP_INVALID_CREDENTIALS, NULL,
+ (char*)errstr, 0, NULL);
+ break;
+ }
+
+ out:
+ if (referral)
+ slapi_entry_free(referral);
+ if (be)
+ slapi_be_Unlock(be);
+
+ LDAPDebug( LDAP_DEBUG_TRACE, "=> ids_sasl_check_bind\n", 0, 0, 0 );
+
+ return;
+}
+
diff --git a/ldap/servers/slapd/schema.c b/ldap/servers/slapd/schema.c
new file mode 100644
index 00000000..9404dccb
--- /dev/null
+++ b/ldap/servers/slapd/schema.c
@@ -0,0 +1,4664 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+/* schema.c - routines to enforce schema definitions */
+
+#include <stdio.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <prio.h>
+#include <plstr.h>
+#include <plhash.h>
+#include "slap.h"
+
+typedef struct sizedbuffer
+{
+ char *buffer;
+ size_t size;
+} sizedbuffer;
+
+
+typedef char *(*schema_strstr_fn_t)( const char *big, const char *little);
+
+
+
+/*
+ * The schema_oc_kind_strings array is indexed by oc_kind values, i.e.,
+ * OC_KIND_STRUCTURAL (0), OC_KIND_AUXILIARY (1), or OC_KIND_ABSTRACT (2).
+ * The leading and trailing spaces are intentional.
+ */
+#define SCHEMA_OC_KIND_COUNT 3
+static char *schema_oc_kind_strings_with_spaces[] = {
+ " STRUCTURAL ",
+ " AUXILIARY ",
+ " ABSTRACT ",
+};
+
+/* constant strings (used in a few places) */
+static const char *schema_obsolete_with_spaces = " OBSOLETE ";
+static const char *schema_collective_with_spaces = " COLLECTIVE ";
+static const char *schema_nousermod_with_spaces = " NO-USER-MODIFICATION ";
+
+/* user defined origin array */
+static char *schema_user_defined_origin[] = {
+ "user defined",
+ NULL
+};
+
+/*
+ * pschemadse is based on the general implementation in dse
+ */
+
+static struct dse *pschemadse= NULL;
+
+static void oc_add_nolock(struct objclass *newoc);
+static int oc_delete_nolock (char *ocname);
+static int oc_replace_nolock(const char *ocname, struct objclass *newoc);
+static int oc_check_required(Slapi_PBlock *, Slapi_Entry *,struct objclass *);
+static int oc_check_allowed_sv(Slapi_PBlock *, Slapi_Entry *e, const char *type, struct objclass **oclist );
+static char **read_dollar_values ( char *vals);
+static int schema_delete_objectclasses ( Slapi_Entry *entryBefore,
+ LDAPMod *mod, char *errorbuf, size_t errorbufsize,
+ int schema_ds4x_compat );
+static int schema_delete_attributes ( Slapi_Entry *entryBefore,
+ LDAPMod *mod, char *errorbuf, size_t errorbufsize);
+static int schema_add_attribute ( Slapi_PBlock *pb, LDAPMod *mod,
+ char *errorbuf, size_t errorbufsize, int schema_ds4x_compat );
+static int schema_add_objectclass ( Slapi_PBlock *pb, LDAPMod *mod,
+ char *errorbuf, size_t errorbufsize, int schema_ds4x_compat );
+static int schema_replace_attributes ( Slapi_PBlock *pb, LDAPMod *mod,
+ char *errorbuf, size_t errorbufsize );
+static int schema_replace_objectclasses ( Slapi_PBlock *pb, LDAPMod *mod,
+ char *errorbuf, size_t errorbufsize );
+static int read_oc_ldif ( const char *input, struct objclass **oc,
+ char *errorbuf, size_t errorbufsize, int nolock, int is_user_defined,
+ int schema_ds4x_compat );
+static int schema_check_name(char *name, PRBool isAttribute, char *errorbuf,
+ size_t errorbufsize );
+static int schema_check_oid(const char *name, const char *oid,
+ PRBool isAttribute, char *errorbuf, size_t errorbufsize);
+static int has_smart_referral( Slapi_Entry *e );
+static int isExtensibleObjectclass(const char *objectclass);
+static int strip_oc_options ( struct objclass *poc );
+static char *stripOption (char *attr);
+static int read_at_ldif(const char *input, struct asyntaxinfo **asipp,
+ char *errorbuf, size_t errorbufsize, int nolock, int is_user_defined,
+ int schema_ds4x_compat, int is_remote);
+static char **parse_qdlist(const char *s, int *n, int strip_options);
+static void free_qdlist(char **vals, int n);
+static char **parse_qdescrs(const char *s, int *n);
+static char **parse_qdstrings(const char *s, int *n);
+static int get_flag_keyword( const char *keyword, int flag_value,
+ const char **inputp, schema_strstr_fn_t strstr_fn );
+static char *get_tagged_oid( const char *tag, const char **inputp,
+ schema_strstr_fn_t strstr_fn );
+static int put_tagged_oid( char *outp, const char *tag, const char *oid,
+ const char *suffix, int enquote );
+static void strcat_oids( char *buf, char *prefix, char **oids,
+ int schema_ds4x_compat );
+static size_t strcat_qdlist( char *buf, char *prefix, char **qdlist );
+static size_t strlen_null_ok(const char *s);
+static int strcpy_count( char *dst, const char *src );
+static int element_is_user_defined( char * const * origins );
+static char **parse_origin_list( const char *schema_value, int *num_originsp,
+ char **default_list );
+static int refresh_user_defined_schema(Slapi_PBlock *pb, Slapi_Entry* entryBefore, Slapi_Entry* e, int *returncode, char *returntext, void *arg);
+static int schema_check_oc_attrs ( struct objclass *poc, char *errorbuf,
+ size_t errorbufsize, int stripOptions );
+static struct objclass *oc_find_nolock( const char *ocname_or_oid );
+static struct objclass *oc_find_oid_nolock( const char *ocoid );
+static void oc_free( struct objclass **ocp );
+static PRBool oc_equal( struct objclass *oc1, struct objclass *oc2 );
+static PRBool attr_syntax_equal( struct asyntaxinfo *asi1,
+ struct asyntaxinfo *asi2 );
+static int schema_strcmp( const char *s1, const char *s2 );
+static int schema_strcmp_array( char **sa1, char **sa2,
+ const char *ignorestr );
+static PRBool schema_type_is_interesting( const char *type );
+static void schema_create_errormsg( char *errorbuf, size_t errorbufsize,
+ const char *prefix, const char *name, const char *fmt, ... );
+
+/* Some utility functions for dealing with a dynamic buffer */
+
+static struct sizedbuffer *sizedbuffer_construct(size_t size);
+static void sizedbuffer_destroy(struct sizedbuffer *p);
+static void sizedbuffer_allocate(struct sizedbuffer *p, size_t sizeneeded);
+
+
+/*
+ * Constant strings that we pass to schema_create_errormsg().
+ */
+static const char *schema_errprefix_oc = "object class %s: ";
+static const char *schema_errprefix_at = "attribute type %s: ";
+static const char *schema_errprefix_generic = "%s: ";
+
+
+/*
+ * A "cached" copy of the "ignore trailing spaces" config. setting.
+ * This is set during initialization only (server restart required for
+ * changes to take effect). We do things this way to avoid lock/unlock
+ * mutex sequences inside performance critical code.
+ */
+static int schema_ignore_trailing_spaces =
+ SLAPD_DEFAULT_SCHEMA_IGNORE_TRAILING_SPACES;
+
+/* R/W lock used to serialize access to the schema DSE */
+static PRRWLock *schema_dse_lock = NULL;
+
+/*
+ * The schema_dse_mandatory_init_callonce structure is used by NSPR to ensure
+ * that schema_dse_mandatory_init() is called at most once.
+ */
+static PRCallOnceType schema_dse_mandatory_init_callonce = { 0, 0, 0 };
+
+
+/* Essential initialization. Returns PRSuccess if successful */
+static PRStatus
+schema_dse_mandatory_init( void )
+{
+ if ( NULL == ( schema_dse_lock = PR_NewRWLock( PR_RWLOCK_RANK_NONE,
+ "schema DSE rwlock" ))) {
+ slapi_log_error( SLAPI_LOG_FATAL, "schema_dse_mandatory_init",
+ "PR_NewRWLock() for schema DSE lock failed\n" );
+ return PR_FAILURE;
+ }
+
+ schema_ignore_trailing_spaces = config_get_schema_ignore_trailing_spaces();
+ return PR_SUCCESS;
+}
+
+
+static void
+schema_dse_lock_read( void )
+{
+ if ( NULL != schema_dse_lock ||
+ PR_SUCCESS == PR_CallOnce( &schema_dse_mandatory_init_callonce,
+ schema_dse_mandatory_init )) {
+ PR_RWLock_Rlock( schema_dse_lock );
+ }
+}
+
+
+static void
+schema_dse_lock_write( void )
+{
+ if ( NULL != schema_dse_lock ||
+ PR_SUCCESS == PR_CallOnce( &schema_dse_mandatory_init_callonce,
+ schema_dse_mandatory_init )) {
+ PR_RWLock_Wlock( schema_dse_lock );
+ }
+}
+
+
+static void
+schema_dse_unlock( void )
+{
+ if ( schema_dse_lock != NULL ) {
+ PR_RWLock_Unlock( schema_dse_lock );
+ }
+}
+
+
+static int
+dont_allow_that(Slapi_PBlock *pb, Slapi_Entry* entryBefore, Slapi_Entry* e, int *returncode, char *returntext, void *arg)
+{
+ *returncode = LDAP_UNWILLING_TO_PERFORM;
+ return SLAPI_DSE_CALLBACK_ERROR;
+}
+
+#if 0
+/*
+ * hashNocaseString - used for case insensitive hash lookups
+ */
+static PLHashNumber
+hashNocaseString(const void *key)
+{
+ PLHashNumber h = 0;
+ const unsigned char *s;
+
+ for (s = key; *s; s++)
+ h = (h >> 28) ^ (h << 4) ^ (tolower(*s));
+ return h;
+}
+#endif
+
+static const char *
+skipWS(const char *s)
+{
+ while (s && isascii(*s) && isspace(*s) )
+ ++s;
+
+ if ((isascii(*s)) == 0) {
+ return NULL;
+ }
+ return s;
+}
+
+
+/*
+ * like strchr() but strings within single quotes are skipped.
+ */
+static char *
+strchr_skip_quoted_strings( char *s, int c )
+{
+ int in_quote = 0;
+
+ while ( *s != '\0' ) {
+ if ( *s == '\'' ) {
+ in_quote = 1 - in_quote; /* toggle */
+ } else if ( !in_quote && *s == c ) {
+ return s;
+ }
+ ++s;
+ }
+
+ return( NULL );
+}
+
+
+/**
+ * parses a string containing a qdescrs or qdstrings (as described by
+ * RFC 2252, section 4.1) into an array of strings; the second parameter
+ * will hold the actual number of strings in the array. The returned array
+ * is NULL terminated.
+ *
+ * This function can handle qdescrs or qdstrings because the only
+ * difference between the two is that fewer characters are allowed in
+ * a qdescr (our parsing code does not check anyway) and we want to
+ * strip attribute options when parsing qdescrs (indicated by a non-zero
+ * strip_options parameter).
+ */
+static char **
+parse_qdlist(const char *s, int *n, int strip_options)
+{
+ char **retval = 0;
+ char *work = 0;
+ char *start = 0, *end = 0;
+ int num = 0;
+ int in_quote = 0;
+
+ if (n)
+ *n = 0;
+
+ if (!s || !*s || !n) {
+ return retval;
+ }
+
+ /* make a working copy of the given string */
+ work = slapi_ch_strdup(s);
+
+ /* count the number of qdescr items in the string e.g. just count
+ the number of spaces */
+ /* for a single qdescr, the terminal character will be the final
+ single quote; for a qdesclist, the terminal will be the close
+ parenthesis */
+ end = strrchr(work, '\'');
+ if ((start = strchr_skip_quoted_strings(work, '(')) != NULL)
+ end = strchr_skip_quoted_strings(work, ')');
+ else
+ start = strchr(work, '\'');
+
+ if (!end) /* already nulled out */
+ end = work + strlen(work);
+
+ if (start) {
+ num = 1;
+ /* first pass: count number of items and zero out non useful tokens */
+ for (; *start && (start != end); ++start) {
+ if (*start == '\'' ) {
+ in_quote = 1 - in_quote; /* toggle */
+ *start = 0;
+ } else if ( !in_quote && ((*start == ' ') || (*start == '(') ||
+ (*start == ')'))) {
+ if (*start == ' ') {
+ num++;
+ }
+ *start = 0;
+ }
+ }
+ *start = 0;
+
+ /* allocate retval; num will be >= actual number of items */
+ retval = (char**)slapi_ch_calloc(num+1, sizeof(char *));
+
+ /* second pass: copy strings into the return value and set the
+ actual number of items returned */
+ start = work;
+ while (start != end) {
+ /* skip over nulls */
+ while (!*start && (start != end))
+ ++start;
+ if (start == end)
+ break;
+ retval[*n] = slapi_ch_strdup(start);
+ /*
+ * A qdescr list may contain attribute options; we just strip
+ * them here. In the future, we may want to support them or do
+ * something really fancy with them
+ */
+ if ( strip_options ) {
+ stripOption(retval[*n]);
+ }
+ (*n)++;
+ start += strlen(start);
+ }
+ PR_ASSERT( *n <= num ); /* sanity check */
+ retval[*n] = NULL;
+ } else {
+ /* syntax error - no start and/or end delimiters */
+ }
+
+ /* free the working string */
+ slapi_ch_free((void **)&work);
+
+ return retval;
+}
+
+
+static void
+free_qdlist(char **vals, int n)
+{
+ int ii;
+ for (ii = 0; ii < n; ++ii)
+ slapi_ch_free((void **)&(vals[ii]));
+ slapi_ch_free((void **)&vals);
+}
+
+
+/**
+ * parses a string containing a qdescrs (as described by RFC 2252, section 4.1)
+ * into an array of strings; the second parameter will hold the actual number
+ * of strings in the array. The returned array is NULL terminated.
+ */
+static char **
+parse_qdescrs(const char *s, int *n)
+{
+ return parse_qdlist( s, n, 1 /* strip attribute options */ );
+}
+
+
+/*
+ * Parses a string containing a qdstrings (see RFC 2252, section 4.1) into
+ * an array of strings; the second parameter will hold the actual number
+ * of strings in the array.
+ */
+static char **
+parse_qdstrings(const char *s, int *n)
+{
+ return parse_qdlist( s, n, 0 /* DO NOT strip attribute options */ );
+}
+
+
+/*
+ * slapi_entry_schema_check - check that entry e conforms to the schema
+ * required by its object class(es). returns 0 if so, non-zero otherwise.
+ * [ the pblock is only used to check if this is a replicated operation.
+ * you may pass in NULL if this isn't part of an operation. ]
+ */
+int
+slapi_entry_schema_check( Slapi_PBlock *pb, Slapi_Entry *e )
+{
+ struct objclass **oclist;
+ struct objclass *oc;
+ const char *ocname;
+ Slapi_Attr *a, *aoc;
+ Slapi_Value *v;
+ int ret = 0;
+ int schemacheck = config_get_schemacheck();
+ int is_replicated_operation = 0;
+ int is_extensible_object = 0;
+ int i, oc_count = 0;
+ int unknown_class = 0;
+ char errtext[ BUFSIZ ];
+
+ /* smart referrals are not allowed in Directory Lite */
+ if ( config_is_slapd_lite() ) {
+ if ( has_smart_referral(e) ) {
+ return 1;
+ }
+ }
+
+ /*
+ * say the schema checked out ok if we're not checking schema at
+ * all, or if this is a replication update.
+ */
+ if (pb != NULL)
+ slapi_pblock_get(pb, SLAPI_IS_REPLICATED_OPERATION, &is_replicated_operation);
+ if ( schemacheck == 0 || is_replicated_operation ) {
+ return( 0 );
+ }
+
+ /* find the object class attribute - could error out here */
+ if ( (aoc = attrlist_find( e->e_attrs, "objectclass" )) == NULL ) {
+ char ebuf[ BUFSIZ ];
+ LDAPDebug( LDAP_DEBUG_ANY,
+ "Entry \"%s\" required attribute \"objectclass\" missing\n",
+ escape_string( slapi_entry_get_dn_const(e), ebuf ), 0, 0 );
+ if (pb) {
+ PR_snprintf( errtext, sizeof( errtext ),
+ "missing required attribute \"objectclass\"\n" );
+ slapi_pblock_set( pb, SLAPI_PB_RESULT_TEXT, errtext );
+ }
+ return( 1 );
+ }
+
+ /*
+ * Create an array of pointers to the objclass definitions.
+ */
+
+ i= slapi_attr_first_value(aoc,&v);
+ while (i != -1) {
+ oc_count++;
+ i= slapi_attr_next_value(aoc,i,&v);
+ }
+
+ oclist = (struct objclass**)
+ slapi_ch_malloc((oc_count+1)*sizeof(struct objclass*));
+
+ /*
+ * Need the read lock to create the oc array and while we use it.
+ */
+ oc_lock_read();
+
+ oc_count = 0;
+ for (i= slapi_attr_first_value(aoc,&v); i != -1;
+ i= slapi_attr_next_value(aoc,i,&v)) {
+
+ ocname = slapi_value_get_string(v);
+
+ if ( isExtensibleObjectclass( ocname )) {
+ /*
+ * if the entry is an extensibleObject, just check to see if
+ * the required attributes for whatever other objectclasses the
+ * entry might be are present. All other attributes are allowed
+ */
+ is_extensible_object = 1;
+ continue;
+ }
+
+ if ((oc = oc_find_nolock( ocname )) != NULL ) {
+ oclist[oc_count++] = oc;
+ } else {
+ /* we don't know about the oc; return an appropriate error message */
+ char ebuf[ BUFSIZ ];
+ char ebuf2[ BUFSIZ ];
+ size_t ocname_len = ( ocname == NULL ) ? 0 : strlen( ocname );
+ const char *extra_msg = "";
+
+ if ( ocname_len > 0 && isspace( ocname[ ocname_len-1 ] )) {
+ if ( ocname_len > 1 && isspace( ocname[ ocname_len-2 ] )) {
+ extra_msg = " (remove the trailing spaces)";
+ } else {
+ extra_msg = " (remove the trailing space)";
+ }
+ }
+
+ LDAPDebug( LDAP_DEBUG_ANY,
+ "Entry \"%s\" has unknown object class \"%s\"%s\n",
+ escape_string( slapi_entry_get_dn_const(e), ebuf ),
+ escape_string(ocname, ebuf2), extra_msg );
+ if (pb) {
+ PR_snprintf( errtext, sizeof( errtext ),
+ "unknown object class \"%s\"%s\n",
+ escape_string(ocname, ebuf2), extra_msg );
+ slapi_pblock_set( pb, SLAPI_PB_RESULT_TEXT, errtext );
+ }
+ unknown_class = 1;
+ }
+
+ }
+ oclist[oc_count] = NULL;
+
+ if (unknown_class) {
+ /* failure */
+ ret = 1;
+ goto out;
+ }
+
+ /*
+ * go through all the checking so we can log everything
+ * wrong with the entry. some day, we might want to return
+ * this information to the client as an error message.
+ */
+
+ /*
+ * check that the entry has required attrs for each oc
+ */
+ for (i = 0; oclist[i] != NULL; i++) {
+ if ( oc_check_required( pb, e, oclist[i] ) != 0 ) {
+ ret = 1;
+ goto out;
+ }
+ }
+
+ /*
+ * check that each attr in the entry is allowed by some oc,
+ * and that single-valued attrs only have one value
+ */
+
+ {
+ Slapi_Attr *prevattr;
+ i = slapi_entry_first_attr(e, &a);
+ while (-1 != i && 0 == ret)
+ {
+ if (is_extensible_object == 0 &&
+ unknown_class == 0 &&
+ !slapi_attr_flag_is_set(a, SLAPI_ATTR_FLAG_OPATTR))
+ {
+ char *attrtype;
+ slapi_attr_get_type(a, &attrtype);
+ if (oc_check_allowed_sv(pb, e, attrtype, oclist) != 0)
+ {
+ ret = 1;
+ }
+ }
+
+ if ( slapi_attr_flag_is_set( a, SLAPI_ATTR_FLAG_SINGLE ) ) {
+ if (slapi_valueset_count(&a->a_present_values) > 1)
+ {
+ char ebuf[ BUFSIZ ];
+ LDAPDebug( LDAP_DEBUG_ANY,
+ "Entry \"%s\" single-valued attribute \"%s\" has multiple values\n",
+ escape_string( slapi_entry_get_dn_const(e), ebuf ),
+ a->a_type, 0 );
+ if (pb) {
+ PR_snprintf( errtext, sizeof( errtext ),
+ "single-valued attribute \"%s\" has multiple values\n",
+ a->a_type );
+ slapi_pblock_set( pb, SLAPI_PB_RESULT_TEXT, errtext );
+ }
+ ret = 1;
+ }
+ }
+ prevattr = a;
+ i = slapi_entry_next_attr(e, prevattr, &a);
+ }
+ }
+
+ out:
+ /* Done with the oc array so can release the lock */
+ oc_unlock();
+ slapi_ch_free((void**)&oclist);
+
+ return( ret );
+}
+
+/*
+ * The caller must obtain a read lock first by calling oc_lock_read().
+ */
+static int
+oc_check_required( Slapi_PBlock *pb, Slapi_Entry *e, struct objclass *oc )
+{
+ int i;
+ int rc = 0; /* success, by default */
+ Slapi_Attr *a;
+
+ if (oc == NULL || oc->oc_required == NULL || oc->oc_required[0] == NULL) {
+ return 0; /* success, as none required */
+ }
+
+ /* for each required attribute */
+ for ( i = 0; oc->oc_required[i] != NULL; i++ ) {
+ /* see if it's in the entry */
+ for ( a = e->e_attrs; a != NULL; a = a->a_next ) {
+ if ( slapi_attr_type_cmp( oc->oc_required[i], a->a_type,
+ SLAPI_TYPE_CMP_SUBTYPE ) == 0 ) {
+ break;
+ }
+ }
+
+ /* not there => schema violation */
+ if ( a == NULL ) {
+ char errtext[ BUFSIZ ];
+ char ebuf[ BUFSIZ ];
+ LDAPDebug( LDAP_DEBUG_ANY,
+ "Entry \"%s\" missing attribute \"%s\" required"
+ " by object class \"%s\"\n",
+ escape_string( slapi_entry_get_dn_const(e), ebuf ),
+ oc->oc_required[i], oc->oc_name);
+ if (pb) {
+ PR_snprintf( errtext, sizeof( errtext ),
+ "missing attribute \"%s\" required"
+ " by object class \"%s\"\n",
+ oc->oc_required[i], oc->oc_name );
+ slapi_pblock_set( pb, SLAPI_PB_RESULT_TEXT, errtext );
+ }
+ rc = 1; /* failure */
+ }
+ }
+
+ return rc;
+}
+
+
+
+/*
+ * The caller must obtain a read lock first by calling oc_lock_read().
+ */
+static int
+oc_check_allowed_sv(Slapi_PBlock *pb, Slapi_Entry *e, const char *type, struct objclass **oclist )
+{
+ struct objclass *oc;
+ int i, j;
+ int rc = 1; /* failure */
+
+ /* always allow objectclass and entryid attributes */
+ /* MFW XXX THESE SHORTCUTS SHOULD NOT BE NECESSARY BUT THEY MASK
+ * MFW XXX OTHER BUGS IN THE SERVER.
+ */
+ if ( slapi_attr_type_cmp( type, "objectclass", SLAPI_TYPE_CMP_EXACT ) == 0 ) {
+ return( 0 );
+ } else if ( slapi_attr_type_cmp( type, "entryid", SLAPI_TYPE_CMP_EXACT ) == 0 ) {
+ return( 0 );
+ }
+
+ /* check that the type appears as req or opt in at least one oc */
+ for (i = 0; rc != 0 && oclist[i] != NULL; i++) {
+ oc = oclist[i];
+
+ /* does it require the type? */
+ for ( j = 0; oc->oc_required && oc->oc_required[j] != NULL; j++ ) {
+ if ( slapi_attr_type_cmp( oc->oc_required[j],
+ type, SLAPI_TYPE_CMP_SUBTYPE ) == 0 ) {
+ rc = 0;
+ break;
+ }
+ }
+
+ if ( 0 != rc ) {
+ /* does it allow the type? */
+ for ( j = 0; oc->oc_allowed && oc->oc_allowed[j] != NULL; j++ ) {
+ if ( slapi_attr_type_cmp( oc->oc_allowed[j],
+ type, SLAPI_TYPE_CMP_SUBTYPE ) == 0 ||
+ strcmp( oc->oc_allowed[j],"*" ) == 0 ) {
+ rc = 0;
+ break;
+ }
+ }
+ /* maybe the next oc allows it */
+ }
+ }
+
+ if ( 0 != rc ) {
+ char errtext[ BUFSIZ ];
+ char ebuf[ BUFSIZ ];
+ char ebuf2[ BUFSIZ ];
+ LDAPDebug( LDAP_DEBUG_ANY,
+ "Entry \"%s\" -- attribute \"%s\" not allowed\n",
+ escape_string( slapi_entry_get_dn_const(e), ebuf ),
+ escape_string( type, ebuf2 ),
+ 0);
+
+ if (pb) {
+ PR_snprintf( errtext, sizeof( errtext ),
+ "attribute \"%s\" not allowed\n",
+ escape_string( type, ebuf2 ) );
+ slapi_pblock_set( pb, SLAPI_PB_RESULT_TEXT, errtext );
+ }
+ }
+
+ return rc;
+}
+
+
+
+
+/*
+ * oc_find_name() will return a strdup'd string or NULL if the objectclass
+ * could not be found.
+ */
+char *
+oc_find_name( const char *name_or_oid )
+{
+ struct objclass *oc;
+ char *ocname = NULL;
+
+ oc_lock_read();
+ if ( NULL != ( oc = oc_find_nolock( name_or_oid ))) {
+ ocname = slapi_ch_strdup( oc->oc_name );
+ }
+ oc_unlock();
+
+ return ocname;
+}
+
+
+/*
+ * oc_find_nolock will return a pointer to the objectclass which has the
+ * same name OR oid.
+ * NULL is returned if no match is found or `name_or_oid' is NULL.
+ */
+static struct objclass *
+oc_find_nolock( const char *ocname_or_oid )
+{
+ struct objclass *oc;
+
+ if ( NULL != ocname_or_oid ) {
+ if ( !schema_ignore_trailing_spaces ) {
+ for ( oc = g_get_global_oc_nolock(); oc != NULL; oc = oc->oc_next ) {
+ if ( ( strcasecmp( oc->oc_name, ocname_or_oid ) == 0 )
+ || ( oc->oc_oid &&
+ strcasecmp( oc->oc_oid, ocname_or_oid ) == 0 )) {
+ return( oc );
+ }
+ }
+ } else {
+ const char *p;
+ size_t len;
+
+ /*
+ * Ignore trailing spaces when comparing object class names.
+ */
+ for ( p = ocname_or_oid, len = 0; (*p != '\0') && (*p != ' ');
+ p++, len++ ) {
+ ; /* NULL */
+ }
+
+ for ( oc = g_get_global_oc_nolock(); oc != NULL; oc = oc->oc_next ) {
+ if ( ( (strncasecmp( oc->oc_name, ocname_or_oid, len ) == 0)
+ && (len == strlen(oc->oc_name)) )
+ ||
+ ( oc->oc_oid &&
+ ( strncasecmp( oc->oc_oid, ocname_or_oid, len ) == 0)
+ && (len == strlen(oc->oc_oid)) ) ) {
+ return( oc );
+ }
+ }
+ }
+ }
+
+ return( NULL );
+}
+
+/*
+ * oc_find_oid_nolock will return a pointer to the objectclass which has
+ * the same oid.
+ * NULL is returned if no match is found or `ocoid' is NULL.
+ */
+static struct objclass *
+oc_find_oid_nolock( const char *ocoid )
+{
+ struct objclass *oc;
+
+ if ( NULL != ocoid ) {
+ for ( oc = g_get_global_oc_nolock(); oc != NULL; oc = oc->oc_next ) {
+ if ( ( oc->oc_oid &&
+ ( strcasecmp( oc->oc_oid, ocoid ) == 0)) ){
+ return( oc );
+ }
+ }
+ }
+
+ return( NULL );
+}
+
+
+/*
+ We need to keep the objectclasses in the same order as defined in the ldif files. If not
+ SUP dependencies will break. When the user redefines an existing objectclass this code
+ makes sure it is put back in the same order it was read to from the ldif file. It also
+ verifies that the entries oc_superior value preceeds it in the chain. If not it will not
+ allow the entry to be added. This makes sure that the ldif will be written back correctly.
+*/
+
+static int
+oc_replace_nolock(const char *ocname, struct objclass *newoc) {
+ struct objclass *oc, *pnext;
+ int rc = LDAP_SUCCESS;
+ PRBool saw_sup=PR_FALSE;
+
+ oc = g_get_global_oc_nolock();
+
+ if(newoc->oc_superior == NULL)
+ {
+ saw_sup=PR_TRUE;
+ }
+ /* don't check SUP dependency for first one because it always/should be top */
+ if (strcasecmp (oc->oc_name, ocname) == 0) {
+ newoc->oc_next=oc->oc_next;
+ g_set_global_oc_nolock ( newoc );
+ oc_free( &oc );
+ } else {
+ for (pnext = oc ; pnext != NULL;
+ oc = pnext, pnext = pnext->oc_next) {
+ if((pnext->oc_name != NULL) && (newoc->oc_superior != NULL)) {
+ if(strcasecmp( pnext->oc_name, newoc->oc_superior) == 0)
+ {
+ saw_sup=PR_TRUE;
+ }
+ }
+ if (strcasecmp ( pnext->oc_name, ocname ) == 0) {
+ if(saw_sup)
+ {
+ oc->oc_next=newoc;
+ newoc->oc_next=pnext->oc_next;
+ oc_free( &pnext );
+ break;
+
+ } else
+ {
+ rc = LDAP_TYPE_OR_VALUE_EXISTS;
+ break;
+ }
+
+ }
+ }
+ }
+ return rc;
+}
+
+
+static int
+oc_delete_nolock (char *ocname)
+{
+ struct objclass *oc, *pnext;
+ int rc = 0; /* failure */
+
+ oc = g_get_global_oc_nolock();
+
+ /* special case if we're removing the first oc */
+ if (strcasecmp (oc->oc_name, ocname) == 0) {
+ g_set_global_oc_nolock ( oc->oc_next );
+ oc_free( &oc );
+ rc = 1;
+ } else {
+ for (pnext = oc->oc_next ; pnext != NULL;
+ oc = pnext, pnext = pnext->oc_next) {
+ if (strcasecmp ( pnext->oc_name, ocname ) == 0) {
+ oc->oc_next = pnext->oc_next;
+ oc_free( &pnext );
+ rc = 1;
+ break;
+ }
+ }
+ }
+
+ return rc;
+}
+
+
+/*
+ * Compare two objectclass definitions for equality. Return PR_TRUE if
+ * they are equivalent and PR_FALSE if not.
+ *
+ * The oc_required and oc_allowed arrays are ignored.
+ * The string "user defined" is ignored within the origins array.
+ * The following flags are ignored:
+ * OC_FLAG_STANDARD_OC
+ * OC_FLAG_USER_OC
+ * OC_FLAG_REDEFINED_OC
+ */
+static PRBool
+oc_equal( struct objclass *oc1, struct objclass *oc2 )
+{
+ PRUint8 flagmask;
+
+ if ( schema_strcmp( oc1->oc_name, oc2->oc_name ) != 0
+ || schema_strcmp( oc1->oc_desc, oc2->oc_desc ) != 0
+ || schema_strcmp( oc1->oc_oid, oc2->oc_oid ) != 0
+ || schema_strcmp( oc1->oc_superior, oc2->oc_superior ) != 0 ) {
+ return PR_FALSE;
+ }
+
+ flagmask = ~(OC_FLAG_STANDARD_OC | OC_FLAG_USER_OC | OC_FLAG_REDEFINED_OC);
+ if ( oc1->oc_kind != oc2->oc_kind
+ || ( oc1->oc_flags & flagmask ) != ( oc2->oc_flags & flagmask )) {
+ return PR_FALSE;
+ }
+
+ if ( schema_strcmp_array( oc1->oc_orig_required, oc2->oc_orig_required,
+ NULL ) != 0
+ || schema_strcmp_array( oc1->oc_orig_allowed, oc2->oc_orig_allowed,
+ NULL ) != 0
+ || schema_strcmp_array( oc1->oc_origin, oc2->oc_origin,
+ schema_user_defined_origin[0] ) != 0 ) {
+ return PR_FALSE;
+ }
+
+ return PR_TRUE;
+}
+
+
+#ifdef OC_DEBUG
+
+static int
+oc_print( struct objclass *oc )
+{
+ int i;
+
+ printf( "object class %s\n", oc->oc_name );
+ if ( oc->oc_required != NULL ) {
+ printf( "\trequires %s", oc->oc_required[0] );
+ for ( i = 1; oc->oc_required[i] != NULL; i++ ) {
+ printf( ",%s", oc->oc_required[i] );
+ }
+ printf( "\n" );
+ }
+ if ( oc->oc_allowed != NULL ) {
+ printf( "\tallows %s", oc->oc_allowed[0] );
+ for ( i = 1; oc->oc_allowed[i] != NULL; i++ ) {
+ printf( ",%s", oc->oc_allowed[i] );
+ }
+ printf( "\n" );
+ }
+ return 0;
+}
+#endif
+
+
+/*
+ * Compare two attrsyntax definitions for equality. Return PR_TRUE if
+ * they are equivalent and PR_FALSE if not.
+ *
+ * The string "user defined" is ignored within the origins array.
+ * The following flags are ignored:
+ * SLAPI_ATTR_FLAG_STD_ATTR
+ * SLAPI_ATTR_FLAG_NOLOCKING
+ * SLAPI_ATTR_FLAG_OVERRIDE
+ */
+static PRBool
+attr_syntax_equal( struct asyntaxinfo *asi1, struct asyntaxinfo *asi2 )
+{
+ unsigned long flagmask;
+
+ flagmask = ~( SLAPI_ATTR_FLAG_STD_ATTR | SLAPI_ATTR_FLAG_NOLOCKING
+ | SLAPI_ATTR_FLAG_OVERRIDE );
+
+ if ( schema_strcmp( asi1->asi_oid, asi2->asi_oid ) != 0
+ || schema_strcmp( asi1->asi_name, asi2->asi_name ) != 0
+ || schema_strcmp( asi1->asi_desc, asi2->asi_desc ) != 0
+ || schema_strcmp( asi1->asi_superior, asi2->asi_superior ) != 0
+ || schema_strcmp( asi1->asi_mr_equality, asi2->asi_mr_equality )
+ != 0
+ || schema_strcmp( asi1->asi_mr_ordering, asi2->asi_mr_ordering )
+ != 0
+ || schema_strcmp( asi1->asi_mr_substring,
+ asi2->asi_mr_substring ) != 0 ) {
+ return PR_FALSE;
+ }
+
+ if ( schema_strcmp_array( asi1->asi_aliases, asi2->asi_aliases, NULL ) != 0
+ || schema_strcmp_array( asi1->asi_origin, asi2->asi_origin,
+ schema_user_defined_origin[0] ) != 0
+ || asi1->asi_plugin != asi2->asi_plugin
+ || ( asi1->asi_flags & flagmask ) !=
+ ( asi2->asi_flags & flagmask )
+ || asi1->asi_syntaxlength != asi2->asi_syntaxlength ) {
+ return PR_FALSE;
+ }
+
+ return PR_TRUE;
+}
+
+
+
+/*
+ * Like strcmp(), but a NULL string pointer is treated as equivalent to
+ * another NULL one and NULL is treated as "less than" all non-NULL values.
+ */
+static int
+schema_strcmp( const char *s1, const char *s2 )
+{
+ if ( s1 == NULL ) {
+ if ( s2 == NULL ) {
+ return 0; /* equal */
+ }
+ return -1; /* s1 < s2 */
+ }
+
+ if ( s2 == NULL ) {
+ return 1; /* s1 > s2 */
+ }
+
+ return strcmp( s1, s2 );
+}
+
+
+/*
+ * Invoke strcmp() on each string in an array. If one array has fewer elements
+ * than the other, it is treated as "less than" the other. Two NULL or
+ * empty arrays (or one NULL and one empty) are considered to be equivalent.
+ *
+ * If ignorestr is non-NULL, occurrences of that string are ignored.
+ */
+static int
+schema_strcmp_array( char **sa1, char **sa2, const char *ignorestr )
+{
+ int i1, i2, rc;
+
+ if ( sa1 == NULL || *sa1 == NULL ) {
+ if ( sa2 == NULL || *sa2 == NULL ) {
+ return 0; /* equal */
+ }
+ return -1; /* sa1 < sa2 */
+ }
+
+ if ( sa2 == NULL || *sa2 == NULL ) {
+ return 1; /* sa1 > sa2 */
+ }
+
+ rc = 0;
+ i1 = i2 = 0;
+ while ( sa1[i1] != NULL && sa2[i2] != NULL ) {
+ if ( NULL != ignorestr ) {
+ if ( 0 == strcmp( sa1[i1], ignorestr )) {
+ ++i1;
+ continue;
+ }
+ if ( 0 == strcmp( sa2[i2], ignorestr )) {
+ ++i2;
+ continue;
+ }
+ }
+ rc = strcmp( sa1[i1], sa2[i2] );
+ ++i1;
+ ++i2;
+ }
+
+ if ( rc == 0 ) { /* all matched so far */
+ /* get rid of trailing ignored strings (if any) */
+ if ( NULL != ignorestr ) {
+ if ( sa1[i1] != NULL && 0 == strcmp( sa1[i1], ignorestr )) {
+ ++i1;
+ }
+ if ( sa2[i2] != NULL && 0 == strcmp( sa2[i2], ignorestr )) {
+ ++i2;
+ }
+ }
+
+ /* check for differing array lengths */
+ if ( sa2[i2] != NULL ) {
+ rc = -1; /* sa1 < sa2 -- fewer elements */
+ } else if ( sa1[i1] != NULL ) {
+ rc = 1; /* sa1 > sa2 -- more elements */
+ }
+ }
+
+ return rc;
+}
+
+
+struct attr_enum_wrapper {
+ Slapi_Attr **attrs;
+ int enquote_sup_oc;
+ struct sizedbuffer *psbAttrTypes;
+ int user_defined_only;
+ int schema_ds4x_compat;
+};
+
+static int
+schema_attr_enum_callback(struct asyntaxinfo *asip, void *arg)
+{
+ struct attr_enum_wrapper *aew = (struct attr_enum_wrapper *)arg;
+ int aliaslen = 0;
+ struct berval val;
+ struct berval *vals[2] = {0, 0};
+ const char *attr_desc, *syntaxoid;
+ char *outp, syntaxlengthbuf[ 128 ];
+ int i;
+
+ vals[0] = &val;
+
+ if (!asip) {
+ LDAPDebug(LDAP_DEBUG_ANY,
+ "Error: no attribute types in schema_attr_enum_callback\n",
+ 0, 0, 0);
+ return ATTR_SYNTAX_ENUM_NEXT;
+ }
+
+ if (aew->user_defined_only &&
+ (asip->asi_flags & SLAPI_ATTR_FLAG_STD_ATTR)) {
+ return ATTR_SYNTAX_ENUM_NEXT; /* not user defined */
+ }
+
+ if ( aew->schema_ds4x_compat ) {
+ attr_desc = ( asip->asi_flags & SLAPI_ATTR_FLAG_STD_ATTR)
+ ? ATTR_STANDARD_STRING : ATTR_USERDEF_STRING;
+ } else {
+ attr_desc = asip->asi_desc;
+ }
+
+ if ( asip->asi_aliases != NULL ) {
+ for ( i = 0; asip->asi_aliases[i] != NULL; ++i ) {
+ aliaslen += strlen( asip->asi_aliases[i] );
+ }
+ }
+
+ syntaxoid = plugin_syntax2oid(asip->asi_plugin);
+
+ if ( !aew->schema_ds4x_compat &&
+ asip->asi_syntaxlength != SLAPI_SYNTAXLENGTH_NONE ) {
+ /* sprintf() is safe because syntaxlengthbuf is large enough */
+ sprintf( syntaxlengthbuf, "{%d}", asip->asi_syntaxlength );
+ } else {
+ *syntaxlengthbuf = '\0';
+ }
+
+ /*
+ * XXX: 256 is a magic number... it must be big enough to account for
+ * all of the fixed sized items we output.
+ */
+ sizedbuffer_allocate(aew->psbAttrTypes,256+strlen(asip->asi_oid)+
+ strlen(asip->asi_name) +
+ aliaslen + strlen_null_ok(attr_desc) +
+ strlen(syntaxoid) +
+ strlen_null_ok(asip->asi_superior) +
+ strlen_null_ok(asip->asi_mr_equality) +
+ strlen_null_ok(asip->asi_mr_ordering) +
+ strlen_null_ok(asip->asi_mr_substring) +
+ strcat_qdlist( NULL, "X-ORIGIN", asip->asi_origin ));
+
+ /*
+ * Overall strategy is to maintain a pointer to the next location in
+ * the output buffer so we can do simple strcpy's, sprintf's, etc.
+ * That pointer is `outp'. Each item that is output includes a trailing
+ * space, so there is no need to include a leading one in the next item.
+ */
+ outp = aew->psbAttrTypes->buffer;
+ outp += sprintf(outp, "( %s NAME ", asip->asi_oid);
+ if ( asip->asi_aliases == NULL || asip->asi_aliases[0] == NULL ) {
+ /* only one name */
+ outp += sprintf(outp, "'%s' ", asip->asi_name);
+ } else {
+ /* several names */
+ outp += sprintf(outp, "( '%s' ", asip->asi_name);
+ for ( i = 0; asip->asi_aliases[i] != NULL; ++i ) {
+ outp += sprintf(outp, "'%s' ", asip->asi_aliases[i]);
+ }
+ outp += strcpy_count(outp, ") ");
+ }
+
+ /* DESC is optional */
+ if (attr_desc && *attr_desc) {
+ outp += sprintf( outp, "DESC '%s'", attr_desc );
+ }
+ if ( !aew->schema_ds4x_compat &&
+ ( asip->asi_flags & SLAPI_ATTR_FLAG_OBSOLETE )) {
+ outp += strcpy_count( outp, schema_obsolete_with_spaces );
+ } else {
+ outp += strcpy_count( outp, " " );
+ }
+
+ if ( !aew->schema_ds4x_compat ) {
+ outp += put_tagged_oid( outp, "SUP ",
+ asip->asi_superior, NULL, aew->enquote_sup_oc );
+ outp += put_tagged_oid( outp, "EQUALITY ",
+ asip->asi_mr_equality, NULL, aew->enquote_sup_oc );
+ outp += put_tagged_oid( outp, "ORDERING ",
+ asip->asi_mr_ordering, NULL, aew->enquote_sup_oc );
+ outp += put_tagged_oid( outp, "SUBSTR ",
+ asip->asi_mr_substring, NULL, aew->enquote_sup_oc );
+ }
+
+ outp += put_tagged_oid( outp, "SYNTAX ", syntaxoid, syntaxlengthbuf,
+ aew->enquote_sup_oc );
+
+ if (asip->asi_flags & SLAPI_ATTR_FLAG_SINGLE) {
+ outp += strcpy_count(outp, "SINGLE-VALUE ");
+ }
+ if ( !aew->schema_ds4x_compat ) {
+ if (asip->asi_flags & SLAPI_ATTR_FLAG_COLLECTIVE ) {
+ outp += strcpy_count( outp, 1 + schema_collective_with_spaces );
+ }
+ if (asip->asi_flags & SLAPI_ATTR_FLAG_NOUSERMOD ) {
+ outp += strcpy_count( outp, 1 + schema_nousermod_with_spaces );
+ }
+ if (asip->asi_flags & SLAPI_ATTR_FLAG_OPATTR) {
+ outp += strcpy_count(outp, "USAGE directoryOperation ");
+ }
+
+ outp += strcat_qdlist( outp, "X-ORIGIN", asip->asi_origin );
+ }
+
+ outp += strcpy_count(outp, ")");
+
+ val.bv_val = aew->psbAttrTypes->buffer;
+ val.bv_len = outp - aew->psbAttrTypes->buffer;
+ attrlist_merge(aew->attrs, "attributetypes", vals);
+
+ return ATTR_SYNTAX_ENUM_NEXT;
+}
+
+
+struct syntax_enum_wrapper {
+ Slapi_Attr **attrs;
+ struct sizedbuffer *psbSyntaxDescription;
+};
+
+static int
+schema_syntax_enum_callback(char **names, Slapi_PluginDesc *plugindesc,
+ void *arg)
+{
+ struct syntax_enum_wrapper *sew = (struct syntax_enum_wrapper *)arg;
+ char *oid, *desc;
+ int i;
+ struct berval val;
+ struct berval *vals[2] = {0, 0};
+ vals[0] = &val;
+
+ oid = NULL;
+ if ( names != NULL ) {
+ for ( i = 0; names[i] != NULL; ++i ) {
+ if ( isdigit( names[i][0] )) {
+ oid = names[i];
+ break;
+ }
+ }
+ }
+
+ if ( oid == NULL ) { /* must have an OID */
+ LDAPDebug(LDAP_DEBUG_ANY, "Error: no OID found in"
+ " schema_syntax_enum_callback for syntax %s\n",
+ ( names == NULL ) ? "unknown" : names[0], 0, 0);
+ return 1;
+ }
+
+ desc = names[0]; /* by convention, the first name is the "official" one */
+
+ /*
+ * RFC 2252 section 4.3.3 Syntax Description says:
+ *
+ * The following BNF may be used to associate a short description with a
+ * syntax OBJECT IDENTIFIER. Implementors should note that future
+ * versions of this document may expand this definition to include
+ * additional terms. Terms whose identifier begins with "X-" are
+ * reserved for private experiments, and MUST be followed by a
+ * <qdstrings>.
+ *
+ * SyntaxDescription = "(" whsp
+ * numericoid whsp
+ * [ "DESC" qdstring ]
+ * whsp ")"
+ *
+ * And section 5.3.1 ldapSyntaxes says:
+ *
+ * Servers MAY use this attribute to list the syntaxes which are
+ * implemented. Each value corresponds to one syntax.
+ *
+ * ( 1.3.6.1.4.1.1466.101.120.16 NAME 'ldapSyntaxes'
+ * EQUALITY objectIdentifierFirstComponentMatch
+ * SYNTAX 1.3.6.1.4.1.1466.115.121.1.54 USAGE directoryOperation )
+ */
+ if ( desc == NULL ) {
+ /* allocate enough room for "( )" and '\0' at end */
+ sizedbuffer_allocate(sew->psbSyntaxDescription, strlen(oid) + 5);
+ sprintf(sew->psbSyntaxDescription->buffer, "( %s )", oid );
+ } else {
+ /* allocate enough room for "( ) DESC '' " and '\0' at end */
+ sizedbuffer_allocate(sew->psbSyntaxDescription,
+ strlen(oid) + strlen(desc) + 13);
+ sprintf(sew->psbSyntaxDescription->buffer, "( %s DESC '%s' )",
+ oid, desc );
+ }
+
+ val.bv_val = sew->psbSyntaxDescription->buffer;
+ val.bv_len = strlen(sew->psbSyntaxDescription->buffer);
+ attrlist_merge(sew->attrs, "ldapSyntaxes", vals);
+
+ return 1;
+}
+
+struct listargs{
+ char **attrs;
+ unsigned long flag;
+};
+
+static int
+schema_list_attributes_callback(struct asyntaxinfo *asi, void *arg)
+{
+ struct listargs *aew = (struct listargs *)arg;
+
+ if (!asi) {
+ LDAPDebug(LDAP_DEBUG_ANY, "Error: no attribute types in schema_list_attributes_callback\n",
+ 0, 0, 0);
+ return ATTR_SYNTAX_ENUM_NEXT;
+ }
+ if (aew->flag && (asi->asi_flags & aew->flag)) {
+ charray_add(&aew->attrs, slapi_ch_strdup(asi->asi_name));
+ if (NULL != asi->asi_aliases) {
+ int i;
+
+ for ( i = 0; asi->asi_aliases[i] != NULL; ++i ) {
+ charray_add(&aew->attrs,
+ slapi_ch_strdup(asi->asi_aliases[i]));
+ }
+ }
+ }
+ return ATTR_SYNTAX_ENUM_NEXT;
+}
+
+/* Return the list of attributes names matching attribute flags */
+
+char ** slapi_schema_list_attribute_names(unsigned long flag)
+{
+ struct listargs aew;
+ memset(&aew,0,sizeof(struct listargs));
+ aew.flag=flag;
+
+ attr_syntax_enumerate_attrs(schema_list_attributes_callback, &aew,
+ PR_FALSE);
+ return aew.attrs;
+}
+
+
+/*
+ * returntext is always at least SLAPI_DSE_RETURNTEXT_SIZE bytes in size.
+ */
+int
+read_schema_dse(
+ Slapi_PBlock *pb,
+ Slapi_Entry *pschema_info_e,
+ Slapi_Entry *entryAfter,
+ int *returncode,
+ char *returntext /* not used */,
+ void *arg /* not used */ )
+{
+ struct berval val;
+ struct berval *vals[2];
+ int i;
+ struct objclass *oc;
+ struct matchingRuleList *mrl=NULL;
+ struct sizedbuffer *psbObjectClasses= sizedbuffer_construct(BUFSIZ);
+ struct sizedbuffer *psbAttrTypes= sizedbuffer_construct(BUFSIZ);
+ struct sizedbuffer *psbMatchingRule= sizedbuffer_construct(BUFSIZ);
+ struct sizedbuffer *psbSyntaxDescription = sizedbuffer_construct(BUFSIZ);
+ struct attr_enum_wrapper aew;
+ struct syntax_enum_wrapper sew;
+ int enquote_sup_oc = config_get_enquote_sup_oc();
+ int user_defined_only = 0;
+ char **allowed, **required;
+ char *mr_desc, *mr_name, *oc_description;
+ int schema_ds4x_compat = config_get_ds4_compatible_schema();
+ const CSN *csn;
+
+ vals[0] = &val;
+ vals[1] = NULL;
+
+ slapi_pblock_get(pb, SLAPI_SCHEMA_USER_DEFINED_ONLY, (void*)&user_defined_only);
+
+ attrlist_delete (&pschema_info_e->e_attrs, "objectclasses");
+ attrlist_delete (&pschema_info_e->e_attrs, "attributetypes");
+ attrlist_delete (&pschema_info_e->e_attrs, "matchingRules");
+ attrlist_delete (&pschema_info_e->e_attrs, "ldapSyntaxes");
+ /*
+ attrlist_delete (&pschema_info_e->e_attrs, "matchingRuleUse");
+ */
+
+ schema_dse_lock_read();
+
+ /* return the objectclasses */
+ oc_lock_read();
+ for (oc = g_get_global_oc_nolock(); oc != NULL; oc = oc->oc_next)
+ {
+ size_t size= 0;
+ int need_extra_space = 1;
+
+ if (user_defined_only &&
+ !(oc->oc_flags & OC_FLAG_USER_OC ||
+ oc->oc_flags & OC_FLAG_REDEFINED_OC)) {
+ continue;
+ }
+
+ /*
+ * XXX: 256 is a magic number... it must be large enough to fit
+ * all of the fixed size items including description (DESC),
+ * kind (STRUCTURAL, AUXILIARY, or ABSTRACT), and the OBSOLETE flag.
+ */
+ if ( schema_ds4x_compat ) {
+ oc_description = (oc->oc_flags & OC_FLAG_STANDARD_OC) ?
+ OC_STANDARD_STRING : OC_USERDEF_STRING;
+ } else {
+ oc_description = oc->oc_desc;
+ }
+
+ size= 256+strlen_null_ok(oc->oc_oid) + strlen(oc->oc_name) +
+ strlen_null_ok(oc_description) +
+ strcat_qdlist( NULL, "X-ORIGIN", oc->oc_origin );
+ required = schema_ds4x_compat ? oc->oc_required : oc->oc_orig_required;
+ if (required && required[0]) {
+ for (i = 0 ; required[i]; i++)
+ size+= 16 + strlen(required[i]);
+ }
+ allowed = schema_ds4x_compat ? oc->oc_allowed : oc->oc_orig_allowed;
+ if (allowed && allowed[0]) {
+ for (i = 0 ; allowed[i]; i++)
+ size+= 16 + strlen(allowed[i]);
+ }
+ sizedbuffer_allocate(psbObjectClasses,size);
+ /* put the OID and the NAME */
+ sprintf (psbObjectClasses->buffer,
+ "( %s NAME '%s'",
+ (oc->oc_oid) ? oc->oc_oid : "",
+ oc->oc_name);
+ /* The DESC (description) is OPTIONAL */
+ if (oc_description && *oc_description) {
+ strcat(psbObjectClasses->buffer, " DESC '");
+ strcat(psbObjectClasses->buffer, oc_description);
+ strcat(psbObjectClasses->buffer, "'");
+ need_extra_space = 1;
+ }
+ /* put the OBSOLETE keyword */
+ if (!schema_ds4x_compat && (oc->oc_flags & OC_FLAG_OBSOLETE)) {
+ strcat(psbObjectClasses->buffer, schema_obsolete_with_spaces);
+ need_extra_space = 0;
+ }
+ /* put the SUP superior objectclass */
+ if (0 != strcasecmp(oc->oc_name, "top")) { /* top has no SUP */
+ /* some AUXILIARY AND ABSTRACT objectclasses may not have a SUP either */
+ /* for compatability, every objectclass other than top must have a SUP */
+ if (schema_ds4x_compat || (oc->oc_superior && *oc->oc_superior)) {
+ if (need_extra_space) {
+ strcat(psbObjectClasses->buffer, " ");
+ }
+ strcat(psbObjectClasses->buffer, "SUP ");
+ strcat(psbObjectClasses->buffer, (enquote_sup_oc ? "'" : ""));
+ strcat(psbObjectClasses->buffer,
+ ((oc->oc_superior && *oc->oc_superior) ?
+ oc->oc_superior : "top"));
+ strcat(psbObjectClasses->buffer, (enquote_sup_oc ? "'" : ""));
+ need_extra_space = 1;
+ }
+ }
+ /* put the kind of objectclass */
+ if (schema_ds4x_compat) {
+ if (need_extra_space) {
+ strcat(psbObjectClasses->buffer, " ");
+ }
+ } else {
+ strcat(psbObjectClasses->buffer, schema_oc_kind_strings_with_spaces[oc->oc_kind]);
+ }
+
+ strcat_oids( psbObjectClasses->buffer, "MUST", required,
+ schema_ds4x_compat );
+ strcat_oids( psbObjectClasses->buffer, "MAY", allowed,
+ schema_ds4x_compat );
+ if ( !schema_ds4x_compat ) {
+ strcat_qdlist( psbObjectClasses->buffer, "X-ORIGIN", oc->oc_origin );
+ }
+ strcat( psbObjectClasses->buffer, ")");
+
+ val.bv_val = psbObjectClasses->buffer;
+ val.bv_len = strlen (psbObjectClasses->buffer);
+ attrlist_merge (&pschema_info_e->e_attrs, "objectclasses", vals);
+ }
+ oc_unlock();
+
+ /* now return the attrs */
+
+ aew.attrs = &pschema_info_e->e_attrs;
+ aew.enquote_sup_oc = enquote_sup_oc;
+ aew.psbAttrTypes = psbAttrTypes;
+ aew.user_defined_only = user_defined_only;
+ aew.schema_ds4x_compat = schema_ds4x_compat;
+ attr_syntax_enumerate_attrs(schema_attr_enum_callback, &aew, PR_FALSE);
+
+ /* return the set of matching rules we support */
+ for (mrl = g_get_global_mrl(); !user_defined_only && mrl != NULL; mrl = mrl->mrl_next) {
+ mr_name = mrl->mr_entry->mr_name ? mrl->mr_entry->mr_name : "";
+ mr_desc = mrl->mr_entry->mr_desc ? mrl->mr_entry->mr_desc : "";
+ sizedbuffer_allocate(psbMatchingRule,128+
+ strlen_null_ok(mrl->mr_entry->mr_oid) +
+ strlen(mr_name)+ strlen(mr_desc)+
+ strlen_null_ok(mrl->mr_entry->mr_syntax));
+ if ( schema_ds4x_compat ) {
+ sprintf(psbMatchingRule->buffer,
+ "( %s NAME '%s' DESC '%s' SYNTAX %s%s%s )",
+ (mrl->mr_entry->mr_oid ? mrl->mr_entry->mr_oid : ""),
+ mr_name, mr_desc,
+ enquote_sup_oc ? "'" : "",
+ mrl->mr_entry->mr_syntax ? mrl->mr_entry->mr_syntax : "" ,
+ enquote_sup_oc ? "'" : "");
+ } else if ( NULL != mrl->mr_entry->mr_oid &&
+ NULL != mrl->mr_entry->mr_syntax ) {
+ char *p;
+
+ sprintf(psbMatchingRule->buffer, "( %s ", mrl->mr_entry->mr_oid );
+ p = psbMatchingRule->buffer + strlen(psbMatchingRule->buffer);
+ if ( *mr_name != '\0' ) {
+ sprintf(p, "NAME '%s' ", mr_name );
+ p += strlen(p);
+ }
+
+ if ( *mr_desc != '\0' ) {
+ sprintf(p, "DESC '%s' ", mr_desc );
+ p += strlen(p);
+ }
+ sprintf(p, "SYNTAX %s )", mrl->mr_entry->mr_syntax );
+ }
+
+ val.bv_val = psbMatchingRule->buffer;
+ val.bv_len = strlen (psbMatchingRule->buffer);
+ attrlist_merge (&pschema_info_e->e_attrs, "matchingRules", vals);
+ }
+
+ if ( !schema_ds4x_compat && !user_defined_only ) {
+ /* return the set of syntaxes we support */
+ sew.attrs = &pschema_info_e->e_attrs;
+ sew.psbSyntaxDescription = psbSyntaxDescription;
+ plugin_syntax_enumerate(schema_syntax_enum_callback, &sew);
+ }
+
+ csn = g_get_global_schema_csn();
+ if (NULL != csn) {
+ char csn_str[CSN_STRSIZE + 1];
+ csn_as_string(csn, PR_FALSE, csn_str);
+ slapi_entry_attr_delete(pschema_info_e, "nsschemacsn");
+ slapi_entry_add_string(pschema_info_e, "nsschemacsn", csn_str);
+ }
+
+ schema_dse_unlock();
+
+ sizedbuffer_destroy(psbObjectClasses);
+ sizedbuffer_destroy(psbAttrTypes);
+ sizedbuffer_destroy(psbMatchingRule);
+ sizedbuffer_destroy(psbSyntaxDescription);
+ *returncode= LDAP_SUCCESS;
+ return SLAPI_DSE_CALLBACK_OK;
+}
+
+/* helper for deleting mods (we do not want to be applied) from the mods array */
+static void
+mod_free(LDAPMod *mod)
+{
+ ber_bvecfree(mod->mod_bvalues);
+ slapi_ch_free((void**)&(mod->mod_type));
+ slapi_ch_free((void**)&mod);
+}
+
+/*
+ * modify_schema_dse: called by do_modify() when target is cn=schema
+ *
+ * Add/Delete attributes and objectclasses from the schema
+ * Supported mod_ops are LDAP_MOD_DELETE and LDAP_MOD_ADD
+ *
+ * Note that the in-memory DSE Slapi_Entry object does NOT hold the
+ * attributeTypes and objectClasses attributes -- it only holds
+ * non-schema related attributes such as aci.
+ *
+ * returntext is always at least SLAPI_DSE_RETURNTEXT_SIZE bytes in size.
+ */
+int
+modify_schema_dse (Slapi_PBlock *pb, Slapi_Entry *entryBefore, Slapi_Entry *entryAfter, int *returncode, char *returntext, void *arg)
+{
+ int i, rc= SLAPI_DSE_CALLBACK_OK; /* default is to apply changes to the DSE */
+ char *schema_dse_attr_name;
+ LDAPMod **mods = NULL;
+ int num_mods = 0; /* count the number of mods */
+ int schema_ds4x_compat = config_get_ds4_compatible_schema();
+ int reapply_mods = 0;
+ int is_replicated_operation = 0;
+
+ slapi_pblock_get( pb, SLAPI_MODIFY_MODS, &mods );
+ slapi_pblock_get( pb, SLAPI_IS_REPLICATED_OPERATION, &is_replicated_operation);
+ schema_dse_lock_write();
+
+ /*
+ * Process each modification. Stop as soon as we hit an error.
+ *
+ * XXXmcs: known bugs: we don't operate on a copy of the schema, so it
+ * is possible for some schema changes to be made but not all of them.
+ * True for DS 4.x as well, although it tried to keep going even after
+ * an error was detected (which was very wrong).
+ */
+ for (i = 0; rc == SLAPI_DSE_CALLBACK_OK && mods[i]; i++) {
+ schema_dse_attr_name = (char *) mods[i]->mod_type;
+ num_mods++; /* incr the number of mods */
+
+ /*
+ * skip attribute types that we do not recognize (the DSE code will
+ * handle them).
+ */
+ if ( !schema_type_is_interesting( schema_dse_attr_name )) {
+ continue;
+ }
+
+ /*
+ * Delete an objectclass or attribute
+ */
+ if ( (mods[i]->mod_op & ~LDAP_MOD_BVALUES) == LDAP_MOD_DELETE) {
+ if (strcasecmp (mods[i]->mod_type, "objectclasses") == 0) {
+ *returncode = schema_delete_objectclasses (entryBefore, mods[i],
+ returntext, SLAPI_DSE_RETURNTEXT_SIZE, schema_ds4x_compat );
+ }
+ else if (strcasecmp (mods[i]->mod_type, "attributetypes") == 0) {
+ *returncode = schema_delete_attributes (entryBefore, mods[i],
+ returntext, SLAPI_DSE_RETURNTEXT_SIZE );
+ }
+ else {
+ *returncode= LDAP_NO_SUCH_ATTRIBUTE;
+ schema_create_errormsg( returntext, SLAPI_DSE_RETURNTEXT_SIZE,
+ schema_errprefix_generic, mods[i]->mod_type,
+ "Only object classes and attribute types may be deleted" );
+ }
+
+ if ( LDAP_SUCCESS != *returncode ) {
+ rc= SLAPI_DSE_CALLBACK_ERROR;
+ } else {
+ reapply_mods = 1;
+ }
+ }
+
+ /*
+ * Replace an objectclass,attribute, or schema CSN
+ */
+ else if ( (mods[i]->mod_op & ~LDAP_MOD_BVALUES) == LDAP_MOD_REPLACE) {
+ int replace_allowed = 0;
+ slapdFrontendConfig_t *slapdFrontendConfig;
+
+ slapdFrontendConfig = getFrontendConfig();
+ CFG_LOCK_READ( slapdFrontendConfig );
+ if ( 0 == strcasecmp( slapdFrontendConfig->schemareplace,
+ CONFIG_SCHEMAREPLACE_STR_ON )) {
+ replace_allowed = 1;
+ } else if ( 0 == strcasecmp( slapdFrontendConfig->schemareplace,
+ CONFIG_SCHEMAREPLACE_STR_REPLICATION_ONLY )) {
+ replace_allowed = is_replicated_operation;
+ }
+ CFG_UNLOCK_READ( slapdFrontendConfig );
+
+ if ( !replace_allowed ) {
+ *returncode= LDAP_UNWILLING_TO_PERFORM;
+ schema_create_errormsg( returntext, SLAPI_DSE_RETURNTEXT_SIZE,
+ schema_errprefix_generic, mods[i]->mod_type,
+ "Replace is not allowed on the subschema subentry" );
+ rc = SLAPI_DSE_CALLBACK_ERROR;
+ } else {
+ if (strcasecmp (mods[i]->mod_type, "attributetypes") == 0) {
+ /*
+ * Replace all attribute types
+ */
+ *returncode = schema_replace_attributes( pb, mods[i], returntext,
+ SLAPI_DSE_RETURNTEXT_SIZE );
+ } else if (strcasecmp (mods[i]->mod_type, "objectclasses") == 0) {
+ /*
+ * Replace all objectclasses
+ */
+ *returncode = schema_replace_objectclasses( pb, mods[i],
+ returntext, SLAPI_DSE_RETURNTEXT_SIZE );
+ } else if (strcasecmp (mods[i]->mod_type, "nsschemacsn") == 0) {
+ if (is_replicated_operation) {
+ /* Update the schema CSN */
+ if (mods[i]->mod_bvalues && mods[i]->mod_bvalues[0] &&
+ mods[i]->mod_bvalues[0]->bv_val &&
+ mods[i]->mod_bvalues[0]->bv_len > 0) {
+ char new_csn_string[CSN_STRSIZE + 1];
+ CSN *new_schema_csn;
+ memcpy(new_csn_string, mods[i]->mod_bvalues[0]->bv_val,
+ mods[i]->mod_bvalues[0]->bv_len);
+ new_csn_string[mods[i]->mod_bvalues[0]->bv_len] = '\0';
+ new_schema_csn = csn_new_by_string(new_csn_string);
+ if (NULL != new_schema_csn) {
+ g_set_global_schema_csn(new_schema_csn); /* csn is consumed */
+ }
+ }
+ }
+ } else {
+ *returncode= LDAP_UNWILLING_TO_PERFORM; /* XXXmcs: best error? */
+ schema_create_errormsg( returntext, SLAPI_DSE_RETURNTEXT_SIZE,
+ schema_errprefix_generic, mods[i]->mod_type,
+ "Only object classes and attribute types may be replaced" );
+ }
+ }
+
+ if ( LDAP_SUCCESS != *returncode ) {
+ rc= SLAPI_DSE_CALLBACK_ERROR;
+ } else {
+ reapply_mods = 1; /* we have at least some modifications we need to reapply */
+ }
+ }
+
+
+ /*
+ * Add an objectclass or attribute
+ */
+ else if ( (mods[i]->mod_op & ~LDAP_MOD_BVALUES) == LDAP_MOD_ADD) {
+ if (strcasecmp (mods[i]->mod_type, "attributetypes") == 0) {
+ /*
+ * Add a new attribute
+ */
+ *returncode = schema_add_attribute ( pb, mods[i], returntext,
+ SLAPI_DSE_RETURNTEXT_SIZE, schema_ds4x_compat );
+ }
+ else if (strcasecmp (mods[i]->mod_type, "objectclasses") == 0) {
+ /*
+ * Add a new objectclass
+ */
+ *returncode = schema_add_objectclass ( pb, mods[i], returntext,
+ SLAPI_DSE_RETURNTEXT_SIZE, schema_ds4x_compat );
+ }
+ else {
+ if ( schema_ds4x_compat ) {
+ *returncode= LDAP_NO_SUCH_ATTRIBUTE;
+ } else {
+ *returncode= LDAP_UNWILLING_TO_PERFORM; /* XXXmcs: best error? */
+ }
+ schema_create_errormsg( returntext, SLAPI_DSE_RETURNTEXT_SIZE,
+ schema_errprefix_generic, mods[i]->mod_type,
+ "Only object classes and attribute types may be added" );
+ }
+
+ if ( LDAP_SUCCESS != *returncode ) {
+ rc= SLAPI_DSE_CALLBACK_ERROR;
+ } else {
+ reapply_mods = 1; /* we have at least some modifications we need to reapply */
+ }
+ }
+
+ /*
+ ** No value was specified to modify, the user probably tried
+ ** to delete all attributetypes or all objectclasses, which
+ ** isn't allowed
+ */
+ if (!mods[i]->mod_vals.modv_strvals)
+ {
+ if ( schema_ds4x_compat ) {
+ *returncode= LDAP_INVALID_SYNTAX;
+ } else {
+ *returncode= LDAP_UNWILLING_TO_PERFORM; /* XXXmcs: best error? */
+ }
+ schema_create_errormsg( returntext, SLAPI_DSE_RETURNTEXT_SIZE,
+ schema_errprefix_generic, mods[i]->mod_type,
+ "No target attribute type or object class specified" );
+ rc= SLAPI_DSE_CALLBACK_ERROR;
+ }
+ }
+
+ if(rc==SLAPI_DSE_CALLBACK_OK && reapply_mods)
+ {
+ CSN *new_schema_csn;
+ int newindex = 0; /* mods array index */
+
+ /* tell the "unholy" dse_modify code to reapply the mods and use
+ that result instead of the initial result; we must remove the attributes
+ we manage in this code from the mods
+ */
+ slapi_pblock_set(pb, SLAPI_DSE_REAPPLY_MODS, (void *)&reapply_mods);
+
+ /* because we are reapplying the mods, we want the entryAfter to
+ look just like the entryBefore, except that "our" attributes
+ will have been removed
+ */
+ /* delete the mods from the mods array */
+ for (i = 0; i < num_mods ; i++) {
+ const char *attrname = mods[i]->mod_type;
+
+ /* delete this attr from the entry */
+ slapi_entry_attr_delete(entryAfter, attrname);
+
+ if ( schema_type_is_interesting( attrname )) {
+ mod_free(mods[i]);
+ mods[i] = NULL;
+ } else {
+ /* add the original value of the attr back to the entry after */
+ Slapi_Attr *origattr = NULL;
+ Slapi_ValueSet *origvalues = NULL;
+ slapi_entry_attr_find(entryBefore, attrname, &origattr);
+ if (NULL != origattr) {
+ slapi_attr_get_valueset(origattr, &origvalues);
+ if (NULL != origvalues) {
+ slapi_entry_add_valueset(entryAfter, attrname, origvalues);
+ slapi_valueset_free(origvalues);
+ }
+ }
+ mods[newindex++] = mods[i];
+ }
+ }
+ mods[newindex] = NULL;
+
+ /*
+ * Since we successfully updated the schema, we need to generate
+ * a new schema CSN for non-replicated operations.
+ */
+ /* XXXmcs: I wonder if we should update the schema CSN even when no
+ * attribute types or OCs were changed? That way, an administrator
+ * could force schema replication to occur by submitting a modify
+ * operation that did not really do anything, such as:
+ *
+ * dn:cn=schema
+ * changetype:modify
+ * replace:cn
+ * cn:schema
+ */
+ if (!is_replicated_operation)
+ {
+ new_schema_csn = csn_new();
+ if (NULL != new_schema_csn) {
+ char csn_str[CSN_STRSIZE + 1];
+ csn_set_replicaid(new_schema_csn, 0);
+ csn_set_time(new_schema_csn, current_time());
+ g_set_global_schema_csn(new_schema_csn);
+ slapi_entry_attr_delete(entryBefore, "nsschemacsn");
+ csn_as_string(new_schema_csn, PR_FALSE, csn_str);
+ slapi_entry_add_string(entryBefore, "nsschemacsn", csn_str);
+ }
+ }
+ }
+
+ schema_dse_unlock();
+
+ return rc;
+}
+
+CSN *
+dup_global_schema_csn()
+{
+ CSN *schema_csn;
+
+ schema_dse_lock_read();
+ schema_csn = csn_dup ( g_get_global_schema_csn() );
+ schema_dse_unlock();
+ return schema_csn;
+}
+
+/*
+ * Remove all attribute types and objectclasses from the entry and
+ * then add back the user defined ones based on the contents of the
+ * schema hash tables.
+ *
+ * Returns SLAPI_DSE_CALLBACK_OK is all goes well.
+ *
+ * returntext is always at least SLAPI_DSE_RETURNTEXT_SIZE bytes in size.
+ */
+static int
+refresh_user_defined_schema( Slapi_PBlock *pb, Slapi_Entry *pschema_info_e, Slapi_Entry *entryAfter, int *returncode, char *returntext, void *arg /* not used */ )
+{
+ int user_defined_only = 1;
+ int rc;
+ Slapi_PBlock *mypbptr = pb;
+ Slapi_PBlock mypb;
+ const CSN *schema_csn;
+
+ pblock_init(&mypb);
+
+ slapi_entry_attr_delete( pschema_info_e, "objectclasses");
+ slapi_entry_attr_delete( pschema_info_e, "attributetypes");
+
+ /* for write callbacks, no pb is supplied, so use our own */
+ if (!mypbptr) {
+ mypbptr = &mypb;
+ }
+
+ slapi_pblock_set(mypbptr, SLAPI_SCHEMA_USER_DEFINED_ONLY, &user_defined_only);
+ rc = read_schema_dse(mypbptr, pschema_info_e, NULL, returncode, returntext, NULL);
+ schema_csn = g_get_global_schema_csn();
+ if (NULL != schema_csn) {
+ char csn_str[CSN_STRSIZE + 1];
+ slapi_entry_attr_delete(pschema_info_e, "nsschemacsn");
+ csn_as_string(schema_csn, PR_FALSE, csn_str);
+ slapi_entry_add_string(pschema_info_e, "nsschemacsn", csn_str);
+ }
+ pblock_done(&mypb);
+
+ return rc;
+}
+
+
+/* oc_add_nolock
+ * Add the objectClass newoc to the global list of objectclasses
+ */
+static void
+oc_add_nolock(struct objclass *newoc)
+{
+ struct objclass *poc;
+
+ poc = g_get_global_oc_nolock();
+
+ if ( NULL == poc ) {
+ g_set_global_oc_nolock(newoc);
+ } else {
+ for ( ; (poc != NULL) && (poc->oc_next != NULL); poc = poc->oc_next) {
+ ;
+ }
+ poc->oc_next = newoc;
+ newoc->oc_next = NULL;
+ }
+}
+
+
+static char **read_dollar_values ( char *vals) {
+ int i,k;
+ char **retVal;
+ static const char *charsToRemove = " ()";
+
+ /* get rid of all the parens and spaces */
+ for ( i = 0, k = 0; vals[i]; i++) {
+ if (!strchr(charsToRemove, vals[i])) {
+ vals[k++] = vals[i];
+ }
+ }
+ vals[k] = '\0';
+ retVal = str2charray (vals, "$");
+ return retVal;
+}
+
+/*
+ * Delete one or more objectClasses from our internal data structure.
+ *
+ * Return an LDAP error code (LDAP_SUCCESS if all goes well).
+ * If an error occurs, explanatory text is copied into 'errorbuf'.
+ *
+ * This function should not send an LDAP result; that is the caller's
+ * responsibility.
+ */
+static int
+schema_delete_objectclasses( Slapi_Entry *entryBefore, LDAPMod *mod,
+ char *errorbuf, size_t errorbufsize, int schema_ds4x_compat )
+{
+ int i;
+ int rc = LDAP_SUCCESS; /* optimistic */
+ struct objclass *poc, *poc2, *delete_oc = NULL;
+
+ if ( NULL == mod->mod_bvalues ) {
+ schema_create_errormsg( errorbuf, errorbufsize, schema_errprefix_oc,
+ NULL, "Cannot remove all schema object classes" );
+ return LDAP_UNWILLING_TO_PERFORM;
+ }
+
+ for (i = 0; mod->mod_bvalues[i]; i++) {
+ if ( LDAP_SUCCESS != ( rc = read_oc_ldif (
+ (const char *)mod->mod_bvalues[i]->bv_val, &delete_oc,
+ errorbuf, errorbufsize, 0, 0, schema_ds4x_compat))) {
+ return rc;
+ }
+
+ oc_lock_write();
+
+ if ((poc = oc_find_nolock(delete_oc->oc_name)) != NULL) {
+
+ /* check to see if any objectclasses inherit from this oc */
+ for (poc2 = g_get_global_oc_nolock(); poc2 != NULL; poc2 = poc2->oc_next) {
+ if (poc2->oc_superior &&
+ (strcasecmp (poc2->oc_superior, delete_oc->oc_name) == 0)) {
+ schema_create_errormsg( errorbuf, errorbufsize, schema_errprefix_oc,
+ delete_oc->oc_name, "Cannot delete an object class"
+ " which has child object classes" );
+ rc = LDAP_UNWILLING_TO_PERFORM;
+ goto unlock_and_return;
+ }
+ }
+
+ if ( (poc->oc_flags & OC_FLAG_STANDARD_OC) == 0) {
+ oc_delete_nolock (poc->oc_name);
+ }
+
+ else {
+ schema_create_errormsg( errorbuf, errorbufsize, schema_errprefix_oc,
+ delete_oc->oc_name, "Cannot delete a standard object class" );
+ rc = LDAP_UNWILLING_TO_PERFORM;
+ goto unlock_and_return;
+ }
+ }
+ else {
+ schema_create_errormsg( errorbuf, errorbufsize, schema_errprefix_oc,
+ delete_oc->oc_name, "Is unknown. Cannot delete." );
+ rc = schema_ds4x_compat ? LDAP_NO_SUCH_OBJECT : LDAP_NO_SUCH_ATTRIBUTE;
+ goto unlock_and_return;
+ }
+
+ oc_free( &delete_oc );
+ oc_unlock();
+ }
+
+ return rc;
+
+unlock_and_return:
+ oc_free( &delete_oc );
+ oc_unlock();
+ return rc;
+}
+
+
+static int
+schema_return(int rc,struct sizedbuffer * psb1,struct sizedbuffer *psb2,struct sizedbuffer *psb3,struct sizedbuffer *psb4)
+{
+ sizedbuffer_destroy(psb1);
+ sizedbuffer_destroy(psb2);
+ sizedbuffer_destroy(psb3);
+ sizedbuffer_destroy(psb4);
+ return rc;
+}
+
+/*
+ * Delete one or more attributeTypes from our internal data structure.
+ *
+ * Return an LDAP error code (LDAP_SUCCESS if all goes well).
+ * If an error occurs, explanatory text is copied into 'errorbuf'.
+ *
+ * This function should not send an LDAP result; that is the caller's
+ * responsibility.
+ */
+static int
+schema_delete_attributes ( Slapi_Entry *entryBefore, LDAPMod *mod,
+ char *errorbuf, size_t errorbufsize)
+{
+ char *attr_ldif, *oc_list_type = "";
+ asyntaxinfo *a;
+ struct objclass *oc = NULL;
+ int i, k, attr_in_use_by_an_oc = 0;
+ struct sizedbuffer *psbAttrName= sizedbuffer_construct(BUFSIZ);
+ struct sizedbuffer *psbAttrOid= sizedbuffer_construct(BUFSIZ);
+ struct sizedbuffer *psbAttrSyntax= sizedbuffer_construct(BUFSIZ);
+
+ if (NULL == mod->mod_bvalues) {
+ schema_create_errormsg( errorbuf, errorbufsize, schema_errprefix_at,
+ NULL, "Cannot remove all schema attribute types" );
+ return schema_return(LDAP_UNWILLING_TO_PERFORM,psbAttrOid,psbAttrName,
+ psbAttrSyntax,NULL);
+ }
+
+ for (i = 0; mod->mod_bvalues[i]; i++) {
+ attr_ldif =(char *) mod->mod_bvalues[i]->bv_val;
+
+ /* normalize the attr ldif */
+ for ( k = 0; attr_ldif[k]; k++) {
+ if (attr_ldif[k] == '\'' ||
+ attr_ldif[k] == '(' ||
+ attr_ldif[k] == ')' ) {
+ attr_ldif[k] = ' ';
+ }
+ attr_ldif[k] = tolower (attr_ldif[k]);
+
+ }
+
+ sizedbuffer_allocate(psbAttrName,strlen(attr_ldif));
+ sizedbuffer_allocate(psbAttrOid,strlen(attr_ldif));
+ sizedbuffer_allocate(psbAttrSyntax,strlen(attr_ldif));
+
+ sscanf (attr_ldif, "%s name %s syntax %s",
+ psbAttrOid->buffer, psbAttrName->buffer, psbAttrSyntax->buffer);
+ if ((a = attr_syntax_get_by_name ( psbAttrName->buffer)) != NULL ) {
+ /* only modify attrs which were user defined */
+ if (a->asi_flags & SLAPI_ATTR_FLAG_STD_ATTR) {
+ schema_create_errormsg( errorbuf, errorbufsize, schema_errprefix_at,
+ psbAttrName->buffer,
+ "Cannot delete a standard attribute type" );
+ attr_syntax_return( a );
+ return schema_return(LDAP_UNWILLING_TO_PERFORM,psbAttrOid,psbAttrName,
+ psbAttrSyntax,NULL);
+ }
+
+ /* Do not allow deletion if referenced by an object class. */
+ oc_lock_read();
+ attr_in_use_by_an_oc = 0;
+ for ( oc = g_get_global_oc_nolock(); oc != NULL; oc = oc->oc_next ) {
+ if (NULL != oc->oc_required) {
+ for ( k = 0; oc->oc_required[k] != NULL; k++ ) {
+ if ( 0 == slapi_attr_type_cmp( oc->oc_required[k], a->asi_name,
+ SLAPI_TYPE_CMP_EXACT )) {
+ oc_list_type = "MUST";
+ attr_in_use_by_an_oc = 1;
+ break;
+ }
+ }
+ }
+
+ if (!attr_in_use_by_an_oc && NULL != oc->oc_allowed) {
+ for ( k = 0; oc->oc_allowed[k] != NULL; k++ ) {
+ if ( 0 == slapi_attr_type_cmp( oc->oc_allowed[k], a->asi_name,
+ SLAPI_TYPE_CMP_EXACT )) {
+ oc_list_type = "MAY";
+ attr_in_use_by_an_oc = 1;
+ break;
+ }
+ }
+ }
+
+ if (attr_in_use_by_an_oc) {
+ schema_create_errormsg( errorbuf, errorbufsize, schema_errprefix_at,
+ psbAttrName->buffer, "Is included in the %s list for object class %s. Cannot delete.",
+ oc_list_type, oc->oc_name );
+ break;
+ }
+ }
+ oc_unlock();
+ if (attr_in_use_by_an_oc) {
+ attr_syntax_return( a );
+ return schema_return(LDAP_UNWILLING_TO_PERFORM,psbAttrOid,psbAttrName,
+ psbAttrSyntax,NULL);
+ }
+
+ /* Delete it. */
+ attr_syntax_delete( a );
+ attr_syntax_return( a );
+ }
+ else {
+ /* unknown attribute */
+ schema_create_errormsg( errorbuf, errorbufsize, schema_errprefix_at,
+ psbAttrName->buffer, "Is unknown. Cannot delete." );
+ return schema_return(LDAP_NO_SUCH_ATTRIBUTE,psbAttrOid,psbAttrName,
+ psbAttrSyntax,NULL);
+ }
+ }
+
+ return schema_return(LDAP_SUCCESS,psbAttrOid,psbAttrName,psbAttrSyntax,
+ NULL);
+}
+
+static int
+schema_add_attribute ( Slapi_PBlock *pb, LDAPMod *mod, char *errorbuf,
+ size_t errorbufsize, int schema_ds4x_compat )
+{
+ int i;
+ char *attr_ldif;
+/* LPXXX: Eventually, we should not allocate the buffers in read_at_ldif
+ * for each attribute, but use the same buffer for all.
+ * This is not done yet, so it's useless to allocate buffers for nothing.
+ */
+/* struct sizedbuffer *psbAttrName= sizedbuffer_construct(BUFSIZ); */
+/* struct sizedbuffer *psbAttrOid= sizedbuffer_construct(BUFSIZ); */
+/* struct sizedbuffer *psbAttrDesc= sizedbuffer_construct(BUFSIZ); */
+/* struct sizedbuffer *psbAttrSyntax= sizedbuffer_construct(BUFSIZ); */
+ int status = 0;
+
+ for (i = 0; LDAP_SUCCESS == status && mod->mod_bvalues[i]; i++) {
+ int nolock = 0; /* lock global resources during normal operation */
+ attr_ldif = (char *) mod->mod_bvalues[i]->bv_val;
+
+ status = read_at_ldif(attr_ldif, NULL, errorbuf, errorbufsize,
+ nolock, 1 /* user defined */, schema_ds4x_compat, 1);
+ if ( LDAP_SUCCESS != status ) {
+ break; /* stop on first error */
+ }
+ }
+
+ /* free everything */
+/* sizedbuffer_destroy(psbAttrOid); */
+/* sizedbuffer_destroy(psbAttrName); */
+/* sizedbuffer_destroy(psbAttrDesc); */
+/* sizedbuffer_destroy(psbAttrSyntax); */
+
+ return status;
+}
+
+/*
+ * Returns an LDAP error code (LDAP_SUCCESS if all goes well)
+ */
+static int
+add_oc_internal(struct objclass *pnew_oc, char *errorbuf, size_t errorbufsize,
+ int schema_ds4x_compat )
+{
+ struct objclass *oldoc_by_name, *oldoc_by_oid, *psup_oc = NULL;
+ int redefined_oc = 0, rc=0;
+ asyntaxinfo *pasyntaxinfo = 0;
+
+ oc_lock_write();
+
+ oldoc_by_name = oc_find_nolock (pnew_oc->oc_name);
+ oldoc_by_oid = oc_find_nolock (pnew_oc->oc_oid);
+
+ /* Check to see if the objectclass name and the objectclass oid are already
+ * in use by an existing objectclass. If an existing objectclass is already
+ * using the name or oid, the name and the oid should map to the same objectclass.
+ * Otherwise, return an error.
+ */
+ if ( oldoc_by_name != oldoc_by_oid ) {
+ schema_create_errormsg( errorbuf, errorbufsize, schema_errprefix_oc,
+ pnew_oc->oc_name, "The name does not match the OID \"%s\". "
+ "Another object class is already using the name or OID.",
+ pnew_oc->oc_oid);
+ rc = LDAP_TYPE_OR_VALUE_EXISTS;
+ }
+
+ /*
+ * Set a flag so we know if we are updating an existing OC definition.
+ */
+ if ( !rc ) {
+ if ( NULL != oldoc_by_name ) {
+ redefined_oc = 1;
+ } else {
+ /*
+ * If we are not updating an existing OC, check that the new
+ * oid is not already in use.
+ */
+ if ( NULL != oldoc_by_oid ) {
+ schema_create_errormsg( errorbuf, errorbufsize,
+ schema_errprefix_oc, pnew_oc->oc_name,
+ "The OID \"%s\" is already used by the object class \"%s\"",
+ pnew_oc->oc_oid, oldoc_by_oid->oc_name);
+ rc = LDAP_TYPE_OR_VALUE_EXISTS;
+ }
+ }
+ }
+
+ /* check to see if the superior oc exists */
+ if (!rc && pnew_oc->oc_superior &&
+ ((psup_oc = oc_find_nolock (pnew_oc->oc_superior)) == NULL)) {
+ schema_create_errormsg( errorbuf, errorbufsize, schema_errprefix_oc,
+ pnew_oc->oc_name, "Superior object class \"%s\" does not exist",
+ pnew_oc->oc_superior);
+ rc = LDAP_TYPE_OR_VALUE_EXISTS;
+ }
+
+ /* inherit the attributes from the superior oc */
+ if (!rc && psup_oc ) {
+ if ( psup_oc->oc_required ) {
+ charray_merge( &pnew_oc->oc_required, psup_oc->oc_required, 1 );
+ }
+ if ( psup_oc->oc_allowed ) {
+ charray_merge ( &pnew_oc->oc_allowed, psup_oc->oc_allowed, 1 );
+ }
+ }
+
+ /* check to see if the oid is already in use by an attribute */
+ if (!rc && (pasyntaxinfo = attr_syntax_get_by_oid(pnew_oc->oc_oid))) {
+ schema_create_errormsg( errorbuf, errorbufsize, schema_errprefix_oc,
+ pnew_oc->oc_name,
+ "The OID \"%s\" is also used by the attribute type \"%s\"",
+ pnew_oc->oc_oid, pasyntaxinfo->asi_name);
+ rc = LDAP_TYPE_OR_VALUE_EXISTS;
+ attr_syntax_return( pasyntaxinfo );
+ }
+
+ /* check to see if the objectclass name is valid */
+ if (!rc && schema_check_name ( pnew_oc->oc_name, PR_FALSE,
+ errorbuf, errorbufsize ) == 0 ) {
+ rc = schema_ds4x_compat ? LDAP_OPERATIONS_ERROR : LDAP_INVALID_SYNTAX;
+ }
+
+ /* check to see if the oid is valid */
+ if (!rc)
+ {
+ struct sizedbuffer *psbOcOid, *psbOcName;
+
+ psbOcName = sizedbuffer_construct(strlen(pnew_oc->oc_name) + 1);
+ psbOcOid = sizedbuffer_construct(strlen(pnew_oc->oc_oid) + 1);
+ strcpy(psbOcName->buffer, pnew_oc->oc_name);
+ strcpy(psbOcOid->buffer, pnew_oc->oc_oid);
+
+ if (!schema_check_oid ( psbOcName->buffer, psbOcOid->buffer, PR_FALSE,
+ errorbuf, errorbufsize))
+ rc = schema_ds4x_compat ? LDAP_OPERATIONS_ERROR : LDAP_INVALID_SYNTAX;
+
+ sizedbuffer_destroy(psbOcName);
+ sizedbuffer_destroy(psbOcOid);
+ }
+
+ /* check to see if the oc's attributes are valid */
+ if (!rc && schema_check_oc_attrs ( pnew_oc, errorbuf, errorbufsize,
+ 0 /* don't strip options */ ) == 0 ) {
+ rc = schema_ds4x_compat ? LDAP_OPERATIONS_ERROR : LDAP_INVALID_SYNTAX;
+ }
+ /* insert new objectclass exactly where the old one one in the linked list*/
+ if ( !rc && redefined_oc ) {
+ pnew_oc->oc_flags |= OC_FLAG_REDEFINED_OC;
+ rc=oc_replace_nolock( pnew_oc->oc_name, pnew_oc);
+ }
+
+ if (!rc && !redefined_oc ) {
+ oc_add_nolock(pnew_oc);
+ }
+
+ if (!rc && redefined_oc ) {
+ oc_update_inheritance_nolock( pnew_oc );
+ }
+
+ oc_unlock();
+ return rc;
+}
+
+
+/*
+ * Process a replace modify suboperation for attributetypes.
+ *
+ * XXXmcs: At present, readonly (bundled) schema definitions can't be
+ * removed. If that is attempted, we just keep them without generating
+ * an error.
+ *
+ * Our algorithm is:
+ *
+ * Clear the "keep" flags on the all existing attr. definitions.
+ *
+ * For each replacement value:
+ * If the value exactly matches an existing schema definition,
+ * set that definition's keep flag.
+ *
+ * Else if the OID in the replacement value matches an existing
+ * definition, delete the old definition and add the new one. Set
+ * the keep flag on the newly added definition.
+ *
+ * Else add the new definition. Set the keep flag on the newly
+ * added definition.
+ *
+ * For each definition that is not flagged keep, delete.
+ *
+ * Clear all remaining "keep" flags.
+ *
+ * Note that replace was not supported at all before iDS 5.0.
+ */
+static int
+schema_replace_attributes ( Slapi_PBlock *pb, LDAPMod *mod, char *errorbuf,
+ size_t errorbufsize )
+{
+ int i, rc = LDAP_SUCCESS;
+ struct asyntaxinfo *newasip, *oldasip;
+
+ if ( NULL == mod->mod_bvalues ) {
+ schema_create_errormsg( errorbuf, errorbufsize, schema_errprefix_at,
+ NULL, "Cannot remove all schema attribute types" );
+ return LDAP_UNWILLING_TO_PERFORM;
+ }
+
+ /* clear all of the "keep" flags */
+ attr_syntax_all_clear_flag( SLAPI_ATTR_FLAG_KEEP );
+
+ for ( i = 0; mod->mod_bvalues[i] != NULL; ++i ) {
+ if ( LDAP_SUCCESS != ( rc = read_at_ldif( mod->mod_bvalues[i]->bv_val,
+ &newasip, errorbuf, errorbufsize, 0, 1, 0, 0 ))) {
+ goto clean_up_and_return;
+ }
+
+ /*
+ * Check for a match with an existing type and
+ * handle the various cases.
+ */
+ if ( NULL == ( oldasip =
+ attr_syntax_get_by_oid( newasip->asi_oid ))) {
+ /* new attribute type */
+ LDAPDebug( LDAP_DEBUG_TRACE, "schema_replace_attributes:"
+ " new type %s (OID %s)\n",
+ newasip->asi_name, newasip->asi_oid, 0 );
+ } else {
+ /* the name matches -- check the rest */
+ if ( attr_syntax_equal( newasip, oldasip )) {
+ /* unchanged attribute type -- just mark it as one to keep */
+ oldasip->asi_flags |= SLAPI_ATTR_FLAG_KEEP;
+ attr_syntax_free( newasip );
+ newasip = NULL;
+ } else {
+ /* modified attribute type */
+ LDAPDebug( LDAP_DEBUG_TRACE, "schema_replace_attributes:"
+ " replacing type %s (OID %s)\n",
+ newasip->asi_name, newasip->asi_oid, 0 );
+ attr_syntax_delete( oldasip );
+ }
+
+ attr_syntax_return( oldasip );
+ }
+
+ if ( NULL != newasip ) { /* add new or replacement definition */
+ rc = attr_syntax_add( newasip );
+ if ( LDAP_SUCCESS != rc ) {
+ schema_create_errormsg( errorbuf, errorbufsize,
+ schema_errprefix_at, newasip->asi_name,
+ "Could not be added (OID is \"%s\")",
+ newasip->asi_oid );
+ attr_syntax_free( newasip );
+ goto clean_up_and_return;
+ }
+
+ newasip->asi_flags |= SLAPI_ATTR_FLAG_KEEP;
+ }
+ }
+
+ /*
+ * Delete all of the definitions that are not marked "keep" or "standard".
+ *
+ * XXXmcs: we should consider reporting an error if any read only types
+ * remain....
+ */
+ attr_syntax_delete_all_not_flagged( SLAPI_ATTR_FLAG_KEEP
+ | SLAPI_ATTR_FLAG_STD_ATTR );
+
+clean_up_and_return:
+ /* clear all of the "keep" flags */
+ attr_syntax_all_clear_flag( SLAPI_ATTR_FLAG_KEEP );
+
+ return rc;
+}
+
+
+static int
+schema_add_objectclass ( Slapi_PBlock *pb, LDAPMod *mod, char *errorbuf,
+ size_t errorbufsize, int schema_ds4x_compat )
+{
+ struct objclass *pnew_oc;
+ char *newoc_ldif;
+ int j, rc=0;
+
+ for (j = 0; mod->mod_bvalues[j]; j++) {
+ newoc_ldif = (char *) mod->mod_bvalues[j]->bv_val;
+ if ( LDAP_SUCCESS != (rc = read_oc_ldif ( newoc_ldif, &pnew_oc,
+ errorbuf, errorbufsize, 0, 1 /* user defined */,
+ schema_ds4x_compat))) {
+ return rc;
+ }
+
+ if ( LDAP_SUCCESS != (rc = add_oc_internal(pnew_oc, errorbuf,
+ errorbufsize, schema_ds4x_compat))) {
+ oc_free( &pnew_oc );
+ return rc;
+ }
+
+ normalize_oc();
+ }
+
+ return LDAP_SUCCESS;
+}
+
+
+
+/*
+ * Process a replace modify suboperation for objectclasses.
+ *
+ * XXXmcs: At present, readonly (bundled) schema definitions can't be
+ * removed. If that is attempted, we just keep them without generating
+ * an error.
+ *
+ * Our algorithm is:
+ *
+ * Lock the global objectclass linked list.
+ *
+ * Create a new empty (temporary) linked list, initially empty.
+ *
+ * For each replacement value:
+ * If the value exactly matches an existing schema definition,
+ * move the existing definition from the current global list to the
+ * temporary list
+ *
+ * Else if the OID in the replacement value matches an existing
+ * definition, delete the old definition from the current global
+ * list and add the new one to the temporary list.
+ *
+ * Else add the new definition to the temporary list.
+ *
+ * Delete all definitions that remain on the current global list.
+ *
+ * Make the temporary list the current global list.
+ *
+ * Note that since the objectclass definitions are stored in a linked list,
+ * this algorithm is O(N * M) where N is the number of existing objectclass
+ * definitions and M is the number of replacement definitions.
+ * XXXmcs: Yuck. We should use a hash table for the OC definitions.
+ *
+ * Note that replace was not supported at all by DS versions prior to 5.0
+ */
+
+static int
+schema_replace_objectclasses ( Slapi_PBlock *pb, LDAPMod *mod, char *errorbuf,
+ size_t errorbufsize )
+{
+ struct objclass *newocp, *curlisthead, *prevocp, *tmpocp;
+ struct objclass *newlisthead = NULL, *newlistend = NULL;
+ int i, rc = LDAP_SUCCESS;
+
+ if ( NULL == mod->mod_bvalues ) {
+ schema_create_errormsg( errorbuf, errorbufsize, schema_errprefix_oc,
+ NULL, "Cannot remove all schema object classes" );
+ return LDAP_UNWILLING_TO_PERFORM;
+ }
+
+ oc_lock_write();
+
+ curlisthead = g_get_global_oc_nolock();
+
+ for ( i = 0; mod->mod_bvalues[i] != NULL; ++i ) {
+ struct objclass *addocp = NULL;
+
+ if ( LDAP_SUCCESS != ( rc = read_oc_ldif( mod->mod_bvalues[i]->bv_val,
+ &newocp, errorbuf, errorbufsize, 1 /* no locking */,
+ 1 /* user defined */, 0 /* no DS 4.x compat issues */ ))) {
+ rc = LDAP_INVALID_SYNTAX;
+ goto clean_up_and_return;
+ }
+
+ prevocp = NULL;
+ for ( tmpocp = curlisthead; tmpocp != NULL; tmpocp = tmpocp->oc_next ) {
+ if ( 0 == strcasecmp( tmpocp->oc_oid, newocp->oc_oid ) ) {
+ /* the names match -- remove from the current list */
+ if ( tmpocp == curlisthead ) {
+ curlisthead = tmpocp->oc_next;
+ } else {
+ prevocp->oc_next = tmpocp->oc_next;
+ }
+ tmpocp->oc_next = NULL;
+
+ /* check for a full match */
+ if ( oc_equal( tmpocp, newocp )) {
+ /* no changes: keep existing definition and discard new */
+ oc_free( &newocp );
+ addocp = tmpocp;
+ } else {
+ /* some differences: discard old and keep the new one */
+ oc_free( &tmpocp );
+ LDAPDebug( LDAP_DEBUG_TRACE, "schema_replace_objectclasses:"
+ " replacing object class %s (OID %s)\n",
+ newocp->oc_name, newocp->oc_oid, 0 );
+ addocp = newocp;
+ }
+ break; /* we found it -- exit the loop */
+
+ }
+ prevocp = tmpocp;
+ }
+
+ if ( NULL == addocp ) {
+ LDAPDebug( LDAP_DEBUG_TRACE, "schema_replace_objectclasses:"
+ " new object class %s (OID %s)\n",
+ newocp->oc_name, newocp->oc_oid, 0 );
+ addocp = newocp;
+ }
+
+ /* add the objectclass to the end of the new list */
+ if ( NULL != addocp ) {
+ if ( NULL == newlisthead ) {
+ newlisthead = addocp;
+ } else {
+ newlistend->oc_next = addocp;
+ }
+ newlistend = addocp;
+ }
+ }
+
+clean_up_and_return:
+ if ( LDAP_SUCCESS == rc ) {
+ /*
+ * Delete all remaining OCs that are on the old list AND are not
+ * "standard" classes.
+ */
+ struct objclass *nextocp;
+
+ prevocp = NULL;
+ for ( tmpocp = curlisthead; tmpocp != NULL; tmpocp = nextocp ) {
+ if ( 0 == ( tmpocp->oc_flags & OC_FLAG_STANDARD_OC )) {
+ /* not a standard definition -- remove it */
+ if ( tmpocp == curlisthead ) {
+ curlisthead = tmpocp->oc_next;
+ } else {
+ prevocp->oc_next = tmpocp->oc_next;
+ }
+ nextocp = tmpocp->oc_next;
+ oc_free( &tmpocp );
+ } else {
+ /*
+ * XXXmcs: we could generate an error, but for now we do not.
+ */
+ nextocp = tmpocp->oc_next;
+ prevocp = tmpocp;
+
+#if 0
+
+ schema_create_errormsg( errorbuf, errorbufsize,
+ schema_errprefix_oc, tmpocp->oc_name,
+ "Cannot delete a standard object class" );
+ rc = LDAP_UNWILLING_TO_PERFORM;
+ break;
+#endif
+ }
+ }
+ }
+
+ /*
+ * Combine the two lists by adding the new list to the end of the old
+ * one.
+ */
+ if ( NULL != curlisthead ) {
+ for ( tmpocp = curlisthead; tmpocp->oc_next != NULL;
+ tmpocp = tmpocp->oc_next ) {
+ ;/*NULL*/
+ }
+ tmpocp->oc_next = newlisthead;
+ newlisthead = curlisthead;
+ }
+
+ /*
+ * Install the new list as the global one, replacing the old one.
+ */
+ g_set_global_oc_nolock( newlisthead );
+
+ oc_unlock();
+ return rc;
+}
+
+
+/*
+ * read_oc_ldif_return
+ * Free all the memory that read_oc_ldif() allocated, and return the retVal
+ *
+ * It's nice to do all the freeing in one spot, as read_oc_ldif() returns sideways
+ */
+
+static int
+read_oc_ldif_return( int retVal,
+ char *oid,
+ struct sizedbuffer *name,
+ char *sup,
+ char **origins,
+ int num_origins,
+ char *desc )
+{
+ slapi_ch_free((void **)&oid);
+ sizedbuffer_destroy( name );
+ slapi_ch_free((void **)&sup);
+ free_qdlist( origins, num_origins );
+ slapi_ch_free((void **)&desc);
+ return retVal;
+}
+
+/*
+ * read_oc_ldif
+ * Read the value of the objectclasses attribute in cn=schema, convert it
+ * into an objectclass struct.
+ *
+ * Arguments:
+ *
+ * input : value of objectclasses attribute to read
+ * oc : pointer write the objectclass to
+ * errorbuf : buffer to write any errors to
+ * is_user_defined : if non-zero, force objectclass to be user defined
+ * nolock : if non-zero, assume oc_lock_*() has been called.
+ * schema_ds4x_compat: if non-zero, act like Netscape DS 4.x
+ *
+ * Returns: an LDAP error code
+ *
+ * LDAP_SUCCESS if the objectclass was sucessfully read, the new
+ * objectclass will be written to oc
+ *
+ * All others: there was an error, an error message will
+ * be written to errorbuf
+ */
+static int
+read_oc_ldif ( const char *input, struct objclass **oc, char *errorbuf,
+ size_t errorbufsize, int nolock, int is_user_defined,
+ int schema_ds4x_compat ) {
+ int i, j, num_origins;
+ const char *pstart, *nextinput;
+ struct objclass *pnew_oc, *psup_oc;
+ char **RequiredAttrsArray, **AllowedAttrsArray;
+ char **OrigRequiredAttrsArray, **OrigAllowedAttrsArray;
+ char **oc_origins;
+ char *pend, *pOcOid, *pOcSup, *pOcDesc;
+ struct sizedbuffer *psbOcName= sizedbuffer_construct(BUFSIZ);
+ PRUint8 kind, flags;
+ int invalid_syntax_error;
+ schema_strstr_fn_t keyword_strstr_fn;
+
+ /*
+ * From RFC 2252 section 4.4:
+ *
+ * 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 ")"
+ *
+ * XXXmcs: Our parsing technique is poor. In (Netscape) DS 4.12 and earlier
+ * releases, parsing was mostly done by looking anywhere within the input
+ * string for various keywords such as "MUST". But if, for example, a
+ * description contains the word "must", the parser would take assume that
+ * the tokens following the word were attribute types or OIDs. Bad news.
+ *
+ * In iDS 5.0 and later, we parse in order left to right and advance a
+ * pointer as we consume the input string (the nextinput variable). We
+ * also use a case-insensitive search when looking for keywords such as
+ * DESC. But the parser will still be fooled by sequences like:
+ *
+ * ( 1.2.3.4 NAME 'testOC' MUST ( DESC cn ) )
+ *
+ * Someday soon we will need to write a real parser.
+ *
+ * Compatibility notes: if schema_ds4x_compat is set, we:
+ * 1. always parse from the beginning of the string
+ * 2. use a case-insensitive compare when looking for keywords, e.g., MUST
+ */
+
+ if ( schema_ds4x_compat ) {
+ keyword_strstr_fn = PL_strcasestr;
+ invalid_syntax_error = LDAP_OPERATIONS_ERROR;
+ } else {
+ keyword_strstr_fn = PL_strstr;
+ invalid_syntax_error = LDAP_INVALID_SYNTAX;
+ }
+
+ flags = 0;
+ num_origins = 0;
+ oc_origins = NULL;
+ pOcOid = pOcSup = pOcDesc = NULL;
+
+ if ( NULL == input || '\0' == input[0] ) {
+
+ schema_create_errormsg( errorbuf, errorbufsize, schema_errprefix_oc, NULL,
+ "One or more values are required for the objectClasses attribute" );
+ LDAPDebug ( LDAP_DEBUG_ANY, "NULL args passed to read_oc_ldif\n",0,0,0);
+ return read_oc_ldif_return( LDAP_OPERATIONS_ERROR, pOcOid, psbOcName,
+ pOcSup, oc_origins, num_origins, pOcDesc );
+ }
+
+
+ nextinput = input;
+
+ /* look for the OID */
+ if ( NULL == ( pOcOid = get_tagged_oid( "(", &nextinput,
+ keyword_strstr_fn ))) {
+ schema_create_errormsg( errorbuf, errorbufsize, schema_errprefix_oc,
+ input, "Value is malformed. It must include a \"(\"");
+ return read_oc_ldif_return( invalid_syntax_error, pOcOid, psbOcName,
+ pOcSup, oc_origins, num_origins, pOcDesc );
+ }
+
+ if ( schema_ds4x_compat || ( strcasecmp(pOcOid, "NAME") == 0))
+ nextinput = input;
+
+ /* look for the NAME */
+ if ( (pstart = (*keyword_strstr_fn)(nextinput, "NAME '")) != NULL ) {
+ pstart += 6;
+ sizedbuffer_allocate(psbOcName,strlen(pstart));
+ if ( sscanf ( pstart, "%s", psbOcName->buffer ) > 0 ) {
+ /* strip the trailing single quote */
+ if ( psbOcName->buffer[strlen(psbOcName->buffer)-1] == '\'' ) {
+ psbOcName->buffer[strlen(psbOcName->buffer)-1] = '\0';
+ nextinput = pstart + strlen(psbOcName->buffer) + 1;
+ } else {
+ schema_create_errormsg( errorbuf, errorbufsize, schema_errprefix_oc,
+ input, "Value is malformed. It must include a single quote around"
+ " the name" );
+ return read_oc_ldif_return( invalid_syntax_error, pOcOid, psbOcName,
+ pOcSup, oc_origins, num_origins, pOcDesc );
+ }
+ }
+ }
+ else {
+ schema_create_errormsg( errorbuf, errorbufsize, schema_errprefix_oc,
+ input, "Value is malformed. It must include a \"NAME '\"");
+ return read_oc_ldif_return( invalid_syntax_error, pOcOid, psbOcName,
+ pOcSup, oc_origins, num_origins, pOcDesc );
+ }
+
+ /*
+ ** if the objectclass ldif doesn't have an OID, we'll make the oid
+ ** ocname-oid
+ */
+ if ( strcasecmp ( pOcOid, "NAME" ) == 0 ) {
+ free( pOcOid );
+ pOcOid = slapi_ch_malloc( 8 + strlen(psbOcName->buffer));
+ sprintf(pOcOid, "%s-oid", psbOcName->buffer );
+ }
+
+ if ( schema_ds4x_compat ) nextinput = input;
+
+ /* look for an optional DESCription */
+ if ( (pstart = (*keyword_strstr_fn) ( nextinput, " DESC '")) != NULL ) {
+ pstart += 7;
+ if (( pend = strchr( pstart, '\'' )) == NULL ) {
+ pend = (char *)(pstart + strlen(pstart));
+ }
+ pOcDesc = slapi_ch_malloc( pend - pstart + 1 );
+ memcpy( pOcDesc, pstart, pend - pstart );
+ pOcDesc[ pend - pstart ] = '\0';
+ nextinput = pend + 1;
+ }
+
+ if ( schema_ds4x_compat ) nextinput = input;
+
+ /* look for the optional OBSOLETE marker */
+ flags |= get_flag_keyword( schema_obsolete_with_spaces,
+ OC_FLAG_OBSOLETE, &nextinput, keyword_strstr_fn );
+
+ if (!nolock) {
+ oc_lock_read(); /* needed because we access the superior oc */
+ }
+
+ if ( schema_ds4x_compat ) nextinput = input;
+
+ /*
+ * Look for the superior objectclass. We first look for a parenthesized
+ * list and if not found we look for a simple OID.
+ *
+ * XXXmcs: Since we do not yet support multiple superior objectclasses, we
+ * just grab the first OID in a parenthesized list.
+ */
+ if ( NULL == ( pOcSup = get_tagged_oid( " SUP ( ", &nextinput,
+ keyword_strstr_fn ))) {
+ pOcSup = get_tagged_oid( " SUP ", &nextinput, keyword_strstr_fn );
+ }
+ psup_oc = oc_find_nolock ( pOcSup );
+
+ if ( schema_ds4x_compat ) nextinput = input;
+
+ /* look for the optional kind (ABSTRACT, STRUCTURAL, AUXILIARY) */
+ for ( i = 0; i < SCHEMA_OC_KIND_COUNT; ++i ) {
+ if ( NULL != ( pstart = (*keyword_strstr_fn)( nextinput,
+ schema_oc_kind_strings_with_spaces[i] ))) {
+ kind = i;
+ nextinput = pstart + strlen( schema_oc_kind_strings_with_spaces[i] ) - 1;
+ break;
+ }
+ }
+ if ( i >= SCHEMA_OC_KIND_COUNT ) { /* not found */
+ if ( NULL != psup_oc && OC_KIND_ABSTRACT != psup_oc->oc_kind ) {
+ /* inherit kind from superior class if not ABSTRACT */
+ kind = psup_oc->oc_kind;
+ } else {
+ /* according to RFC 2252, the default is structural */
+ kind = OC_KIND_STRUCTURAL;
+ }
+ }
+
+ if ( schema_ds4x_compat ) nextinput = input;
+
+ /* look for required attributes (MUST) */
+ if ( (pstart = (*keyword_strstr_fn) (nextinput, " MUST ")) != NULL ) {
+ char *pRequiredAttrs;
+ int saw_open_paren = 0;
+
+ pstart += 6;
+ pstart = skipWS( pstart ); /* skip past any extra white space */
+ if ( *pstart == '(' ) {
+ saw_open_paren = 1;
+ ++pstart;
+ }
+ pRequiredAttrs = slapi_ch_strdup ( pstart );
+ if ( saw_open_paren && (pend = strchr (pRequiredAttrs, ')')) != NULL ) {
+ *pend = '\0';
+ } else if ((pend = strchr (pRequiredAttrs, ' ' )) != NULL ) {
+ *pend = '\0';
+ } else {
+ pend = pRequiredAttrs + strlen(pRequiredAttrs); /* at end of string */
+ }
+ nextinput = pstart + ( pend - pRequiredAttrs );
+ RequiredAttrsArray = read_dollar_values (pRequiredAttrs);
+ slapi_ch_free((void**)&pRequiredAttrs);
+ } else {
+ RequiredAttrsArray = (char **) slapi_ch_malloc (1 * sizeof(char *)) ;
+ RequiredAttrsArray[0] = NULL;
+ }
+
+ if ( schema_ds4x_compat ) nextinput = input;
+
+ /* look for allowed attributes (MAY) */
+ if ( (pstart = (*keyword_strstr_fn) (nextinput, " MAY ")) != NULL ) {
+ char *pAllowedAttrs;
+ int saw_open_paren = 0;
+
+ pstart += 5;
+ pstart = skipWS( pstart ); /* skip past any extra white space */
+ if ( *pstart == '(' ) {
+ saw_open_paren = 1;
+ ++pstart;
+ }
+ pAllowedAttrs = slapi_ch_strdup ( pstart );
+ if ( saw_open_paren && (pend = strchr (pAllowedAttrs, ')')) != NULL ) {
+ *pend = '\0';
+ } else if ((pend = strchr (pAllowedAttrs, ' ' )) != NULL ) {
+ *pend = '\0';
+ } else {
+ pend = pAllowedAttrs + strlen(pAllowedAttrs); /* at end of string */
+ }
+ nextinput = pstart + ( pend - pAllowedAttrs );
+ AllowedAttrsArray = read_dollar_values (pAllowedAttrs);
+ slapi_ch_free((void**)&pAllowedAttrs);
+ } else {
+ AllowedAttrsArray = (char **) slapi_ch_malloc (1 * sizeof(char *)) ;
+ AllowedAttrsArray[0] = NULL;
+ }
+
+ if ( schema_ds4x_compat ) nextinput = input;
+
+ /* look for X-ORIGIN list */
+ oc_origins = parse_origin_list( nextinput, &num_origins,
+ schema_user_defined_origin );
+
+ /* set remaining flags */
+ if ( element_is_user_defined( oc_origins )) {
+ flags |= OC_FLAG_USER_OC;
+ } else if ( is_user_defined ) {
+ flags |= OC_FLAG_USER_OC;
+ /* add missing user defined origin string */
+ charray_add( &oc_origins, slapi_ch_strdup( schema_user_defined_origin[0] ));
+ ++num_origins;
+ } else {
+ flags |= OC_FLAG_STANDARD_OC;
+ }
+
+ /* generate OrigRequiredAttrsArray and OrigAllowedAttrsArray */
+
+ if (psup_oc) {
+ int found_it;
+
+ OrigRequiredAttrsArray = (char **) slapi_ch_malloc (1 * sizeof(char *)) ;
+ OrigRequiredAttrsArray[0] = NULL;
+ OrigAllowedAttrsArray = (char **) slapi_ch_malloc (1 * sizeof(char *)) ;
+ OrigAllowedAttrsArray[0] = NULL;
+
+ if (psup_oc->oc_required) {
+ for (i = 0; RequiredAttrsArray[i]; i++) {
+ for (j = 0, found_it = 0; psup_oc->oc_required[j]; j++) {
+ if (strcasecmp (psup_oc->oc_required[j], RequiredAttrsArray[i]) == 0) {
+ found_it = 1;
+ }
+ }
+ if (!found_it) {
+ charray_add (&OrigRequiredAttrsArray, slapi_ch_strdup ( RequiredAttrsArray[i] ) );
+ }
+ }
+ }
+ if (psup_oc->oc_allowed) {
+ for (i = 0; AllowedAttrsArray[i]; i++) {
+ for (j = 0, found_it=0; psup_oc->oc_allowed[j]; j++) {
+ if (strcasecmp (psup_oc->oc_allowed[j], AllowedAttrsArray[i]) == 0) {
+ found_it = 1;
+ }
+ }
+ if (!found_it) {
+ charray_add (&OrigAllowedAttrsArray, slapi_ch_strdup (AllowedAttrsArray[i]) );
+ }
+ }
+ }
+ }
+ else {
+ /* if no parent oc */
+ OrigRequiredAttrsArray = charray_dup ( RequiredAttrsArray );
+ OrigAllowedAttrsArray = charray_dup ( AllowedAttrsArray );
+ }
+
+ if (!nolock) {
+ oc_unlock(); /* we are done accessing superior oc (psup_oc) */
+ }
+
+ /* finally -- create new objclass structure */
+ pnew_oc = (struct objclass *) slapi_ch_malloc (1 * sizeof (struct objclass));
+ pnew_oc->oc_name = slapi_ch_strdup ( psbOcName->buffer );
+ pnew_oc->oc_superior = pOcSup;
+ pOcSup = NULL; /* don't free this later */
+ pnew_oc->oc_oid = pOcOid;
+ pOcOid = NULL; /* don't free this later */
+ pnew_oc->oc_desc = pOcDesc;
+ pOcDesc = NULL; /* don't free this later */
+ pnew_oc->oc_required = RequiredAttrsArray;
+ pnew_oc->oc_allowed = AllowedAttrsArray;
+ pnew_oc->oc_orig_required = OrigRequiredAttrsArray;
+ pnew_oc->oc_orig_allowed = OrigAllowedAttrsArray;
+ pnew_oc->oc_origin = cool_charray_dup( oc_origins );
+ pnew_oc->oc_next = NULL;
+ pnew_oc->oc_flags = flags;
+ pnew_oc->oc_kind = kind;
+
+ *oc = pnew_oc;
+ return read_oc_ldif_return( LDAP_SUCCESS, pOcOid, psbOcName, pOcSup,
+ oc_origins, num_origins, pOcDesc );
+}
+
+
+static void
+oc_free( struct objclass **ocp )
+{
+ struct objclass *oc;
+
+ if ( NULL != ocp && NULL != *ocp ) {
+ oc = *ocp;
+ slapi_ch_free( (void **)&oc->oc_name );
+ slapi_ch_free( (void **)&oc->oc_desc );
+ slapi_ch_free( (void **)&oc->oc_oid );
+ slapi_ch_free( (void **)&oc->oc_superior );
+ charray_free( oc->oc_required );
+ charray_free( oc->oc_allowed );
+ charray_free( oc->oc_orig_required );
+ charray_free( oc->oc_orig_allowed );
+ cool_charray_free( oc->oc_origin );
+ slapi_ch_free( (void **)&oc );
+ *ocp = NULL;
+ }
+}
+
+struct supargs{
+ char *sup, *oid;
+ unsigned long rc;
+};
+
+static int
+at_sup_dependency_callback(struct asyntaxinfo *asi, void *arg)
+{
+ struct supargs *aew = (struct supargs *)arg;
+ int rc=ATTR_SYNTAX_ENUM_NEXT;
+
+ if (!asi) {
+ LDAPDebug(LDAP_DEBUG_ANY, "Error: no attribute types in at_schema_attributes_callback\n",
+ 0, 0, 0);
+ }
+ else
+ {
+ if (strcasecmp (asi->asi_oid, aew->oid ) == 0) {
+ rc=ATTR_SYNTAX_ENUM_STOP;
+ } else {
+ if(asi->asi_name != NULL) {
+ if (strcasecmp (asi->asi_name, aew->sup ) == 0) {
+ aew->rc=0;
+ }
+ }
+ }
+ }
+ return rc;
+}
+
+/* walks down attribute types and makes sure that the superior value is found in an attribute type
+ preceeding in the hash table. I have concerns about collisions messing with the order here but this
+ may be the best we can do.
+*/
+
+static int
+slapi_check_at_sup_dependency(char *sup, char *oid)
+{
+ struct supargs aew;
+
+ memset(&aew,0,sizeof(struct supargs));
+ aew.rc=LDAP_TYPE_OR_VALUE_EXISTS;
+ aew.sup=sup;
+ aew.oid=oid;
+ attr_syntax_enumerate_attrs(at_sup_dependency_callback, &aew, PR_FALSE);
+ return aew.rc;
+}
+
+
+
+/*
+ * if asipp is NULL, the attribute type is added to the global set of schema.
+ * if asipp is not NULL, the AT is not added but *asipp is set.
+ *
+ * if nolock is true, locking of global resources is turned off; this saves
+ * time during initialization since the server operates in single threaded
+ * mode at that time.
+ *
+ * if is_user_defined is true, force attribute type to be user defined.
+ *
+ * returns an LDAP error code (LDAP_SUCCESS if all goes well)
+*/
+static int
+read_at_ldif(const char *input, struct asyntaxinfo **asipp, char *errorbuf,
+ size_t errorbufsize, int nolock, int is_user_defined,
+ int schema_ds4x_compat, int is_remote)
+{
+ char *pStart, *pEnd;
+ char *pOid, *pSyntax, *pSuperior, *pMREquality, *pMROrdering, *pMRSubstring;
+ const char *nextinput;
+ struct sizedbuffer *psbAttrName= sizedbuffer_construct(BUFSIZ);
+ struct sizedbuffer *psbAttrDesc= sizedbuffer_construct(BUFSIZ);
+ int status = 0;
+ int syntaxlength;
+ char **attr_names = NULL;
+ char *first_attr_name = NULL;
+ char **attr_origins = NULL;
+ int num_names = 0;
+ int num_origins = 0;
+ unsigned long flags = SLAPI_ATTR_FLAG_OVERRIDE;
+ const char *ss = 0;
+ struct asyntaxinfo *tmpasip;
+ int invalid_syntax_error;
+ schema_strstr_fn_t keyword_strstr_fn;
+
+ /*
+ * From RFC 2252 section 4.2:
+ *
+ * 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 ] ; see section 4.3
+ * [ "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
+ *
+ * XXXmcs: Our parsing technique is poor. In (Netscape) DS 4.12 and earlier
+ * releases, parsing was mostly done by looking anywhere within the input
+ * string for various keywords such as "EQUALITY". But if, for example, a
+ * description contains the word "equality", the parser would take assume
+ * that the token following the word was a matching rule. Bad news.
+ *
+ * In iDS 5.0 and later, we parse in order left to right and advance a
+ * pointer as we consume the input string (the nextinput variable). We
+ * also use a case-insensitive search when looking for keywords such as
+ * DESC. This is still less than ideal.
+ *
+ * Someday soon we will need to write a real parser.
+ *
+ * Compatibility notes: if schema_ds4x_compat is set, we:
+ * 1. always parse from the beginning of the string
+ * 2. use a case-insensitive compare when looking for keywords, e.g., DESC
+ */
+
+ if ( schema_ds4x_compat ) {
+ keyword_strstr_fn = PL_strcasestr;
+ invalid_syntax_error = LDAP_OPERATIONS_ERROR;
+ } else {
+ keyword_strstr_fn = PL_strstr;
+ invalid_syntax_error = LDAP_INVALID_SYNTAX;
+ }
+
+ if (nolock)
+ flags |= SLAPI_ATTR_FLAG_NOLOCKING;
+
+ psbAttrName->buffer[0] = '\0';
+ psbAttrDesc->buffer[0] = '\0';
+ pOid = pSyntax = pSuperior = NULL;
+ pMREquality = pMROrdering = pMRSubstring = NULL;
+ syntaxlength = SLAPI_SYNTAXLENGTH_NONE;
+
+ nextinput = input;
+
+ /* get the OID */
+ pOid = get_tagged_oid( "(", &nextinput, keyword_strstr_fn );
+
+ if (NULL == pOid) {
+ schema_create_errormsg( errorbuf, errorbufsize, schema_errprefix_at,
+ input, "Missing or invalid OID" );
+ status = invalid_syntax_error;
+ goto done;
+ }
+
+ if ( schema_ds4x_compat || (strcasecmp(pOid, "NAME") == 0))
+ nextinput = input;
+
+ /* look for the NAME (single or list of names) */
+ if ( (pStart = (*keyword_strstr_fn) ( nextinput, "NAME ")) != NULL ) {
+ pStart += 5;
+ sizedbuffer_allocate(psbAttrName,strlen(pStart)+1);
+ strcpy ( psbAttrName->buffer, pStart);
+ if (*pStart == '(')
+ pEnd = strchr(psbAttrName->buffer, ')');
+ else
+ pEnd = strchr(psbAttrName->buffer+1, '\'');
+ if (pEnd)
+ *(pEnd+1) = 0;
+ nextinput = pStart + strlen(psbAttrName->buffer) + 1;
+ attr_names = parse_qdescrs(psbAttrName->buffer, &num_names);
+ if ( NULL != attr_names ) {
+ first_attr_name = attr_names[0];
+ }
+ }
+
+ if ( schema_ds4x_compat ) nextinput = input;
+
+ /*
+ * if the attribute ldif doesn't have an OID, we'll make the oid
+ * attrname-oid
+ */
+ if ( strcasecmp ( pOid, "NAME" ) == 0 ) {
+ free( pOid );
+ pOid = slapi_ch_malloc( 8 + strlen(first_attr_name));
+ sprintf(pOid, "%s-oid", first_attr_name );
+ }
+
+ /* look for the optional DESCription */
+ if ( (pStart = (*keyword_strstr_fn) ( nextinput, "DESC '")) != NULL ) {
+ pStart += 6;
+ sizedbuffer_allocate(psbAttrDesc,strlen(pStart));
+ strcpy ( psbAttrDesc->buffer, pStart);
+ if ( (pEnd = strchr (psbAttrDesc->buffer, '\'' )) != NULL ){
+ *pEnd ='\0';
+ }
+ nextinput = pStart + strlen(psbAttrDesc->buffer) + 1;
+ }
+
+ if ( schema_ds4x_compat ) nextinput = input;
+
+ /* look for the optional OBSOLETE marker */
+ flags |= get_flag_keyword( schema_obsolete_with_spaces,
+ SLAPI_ATTR_FLAG_OBSOLETE, &nextinput, keyword_strstr_fn );
+
+ if ( schema_ds4x_compat ) nextinput = input;
+
+ /* look for the optional SUPerior type */
+ pSuperior = get_tagged_oid( "SUP ", &nextinput, keyword_strstr_fn );
+
+ if ( schema_ds4x_compat ) nextinput = input;
+
+ /* look for the optional matching rules */
+ pMREquality = get_tagged_oid( "EQUALITY ", &nextinput, keyword_strstr_fn );
+ if ( schema_ds4x_compat ) nextinput = input;
+ pMROrdering = get_tagged_oid( "ORDERING ", &nextinput, keyword_strstr_fn );
+ if ( schema_ds4x_compat ) nextinput = input;
+ pMRSubstring = get_tagged_oid( "SUBSTR ", &nextinput, keyword_strstr_fn );
+ if ( schema_ds4x_compat ) nextinput = input;
+
+ /* look for the optional SYNTAX */
+ if ( NULL != ( pSyntax = get_tagged_oid( "SYNTAX ", &nextinput,
+ keyword_strstr_fn ))) {
+ /*
+ * Check for an optional {LEN}, which if present indicates a
+ * suggested maximum size for values of this attribute type.
+ *
+ * XXXmcs: we do not enforce length restrictions, but we do read
+ * and include them in the subschemasubentry.
+ */
+ if ( (pEnd = strchr ( pSyntax, '{')) != NULL /* balance } */ ) {
+ *pEnd = '\0';
+ syntaxlength = atoi( pEnd + 1 );
+ }
+ }
+
+ if ( schema_ds4x_compat ) nextinput = input;
+
+ /* look for the optional SINGLE-VALUE marker */
+ flags |= get_flag_keyword( " SINGLE-VALUE ",
+ SLAPI_ATTR_FLAG_SINGLE, &nextinput, keyword_strstr_fn );
+
+ if ( schema_ds4x_compat ) nextinput = input;
+
+ /* look for the optional COLLECTIVE marker */
+ flags |= get_flag_keyword( schema_collective_with_spaces,
+ SLAPI_ATTR_FLAG_COLLECTIVE, &nextinput, keyword_strstr_fn );
+
+ if ( schema_ds4x_compat ) nextinput = input;
+
+ /* look for the optional NO-USER-MODIFICATION marker */
+ flags |= get_flag_keyword( schema_nousermod_with_spaces,
+ SLAPI_ATTR_FLAG_NOUSERMOD, &nextinput, keyword_strstr_fn );
+
+ if ( schema_ds4x_compat ) nextinput = input;
+
+ /* look for the optional USAGE */
+ if (NULL != (ss = (*keyword_strstr_fn)(nextinput, " USAGE "))) {
+ ss += 7;
+ ss = skipWS(ss);
+ if (ss) {
+ if ( !PL_strncmp(ss, "directoryOperation",
+ strlen("directoryOperation"))) {
+ flags |= SLAPI_ATTR_FLAG_OPATTR;
+ }
+ if ( NULL == ( nextinput = strchr( ss, ' ' ))) {
+ nextinput = ss + strlen(ss);
+ }
+ }
+ }
+
+ if ( schema_ds4x_compat ) nextinput = input;
+
+ /* X-ORIGIN list */
+ attr_origins = parse_origin_list( nextinput, &num_origins,
+ schema_user_defined_origin );
+
+ /* Do some sanity checking to make sure everything was read correctly */
+
+ if (NULL == pOid) {
+ schema_create_errormsg( errorbuf, errorbufsize, schema_errprefix_at,
+ first_attr_name, "Missing OID" );
+ status = invalid_syntax_error;
+ }
+ if (!status && (!attr_names || !num_names)) {
+ schema_create_errormsg( errorbuf, errorbufsize, schema_errprefix_at,
+ first_attr_name,
+ "Missing name (OID is \"%s\")", pOid );
+ status = invalid_syntax_error;
+ }
+
+ if (!status && (NULL != pSuperior)) {
+ struct asyntaxinfo *asi_parent;
+
+ asi_parent = attr_syntax_get_by_name(pSuperior);
+ /* if we find no match then server won't start or add the attribute type */
+ if (asi_parent == NULL) {
+ LDAPDebug (LDAP_DEBUG_PARSE,
+ "Cannot find parent attribute type \"%s\"\n",pSuperior,
+ NULL,NULL);
+ schema_create_errormsg( errorbuf, errorbufsize,
+ schema_errprefix_at, first_attr_name,
+ "Missing parent attribute syntax OID");
+ status = invalid_syntax_error;
+ } else {
+ char *pso = plugin_syntax2oid(asi_parent->asi_plugin);
+
+ if (pso) {
+ slapi_ch_free ((void **)&pSyntax);
+ pSyntax = slapi_ch_strdup(pso);
+ LDAPDebug (LDAP_DEBUG_TRACE,
+ "Inheriting syntax %s from parent type %s\n",
+ pSyntax, pSuperior,NULL);
+ } else {
+ schema_create_errormsg( errorbuf, errorbufsize,
+ schema_errprefix_at, first_attr_name,
+ "Missing parent attribute syntax OID");
+ status = invalid_syntax_error;
+ }
+ }
+ }
+ /*
+ if we are remote (via modify_schema_dse) then check sup dependencies. Locally
+ was done in if statement above
+ */
+
+ if(!status)
+ {
+ if(is_remote && (pSuperior != NULL))
+ {
+ status=slapi_check_at_sup_dependency(pSuperior, pOid);
+ }
+ if(LDAP_SUCCESS != status) {
+ schema_create_errormsg( errorbuf, errorbufsize,
+ schema_errprefix_at, first_attr_name,
+ "Missing parent attribute syntax OID");
+ status = LDAP_TYPE_OR_VALUE_EXISTS;
+ }
+ }
+
+ if (!status && (NULL == pSyntax)) {
+ schema_create_errormsg( errorbuf, errorbufsize, schema_errprefix_at,
+ first_attr_name, "Missing attribute syntax OID");
+ status = invalid_syntax_error;
+
+ }
+
+ if (!status && (plugin_syntax_find ( pSyntax ) == NULL) ) {
+ schema_create_errormsg( errorbuf, errorbufsize, schema_errprefix_at,
+ first_attr_name, "Unknown attribute syntax OID \"%s\"",
+ pSyntax );
+ status = invalid_syntax_error;
+ }
+
+ if (!status) {
+ struct objclass *poc;
+ /* check to make sure that the OID isn't being used by an objectclass */
+ oc_lock_read();
+ poc = oc_find_oid_nolock( pOid );
+ if ( poc != NULL) {
+ schema_create_errormsg( errorbuf, errorbufsize,
+ schema_errprefix_at, first_attr_name,
+ "The OID \"%s\" is also used by the object class \"%s\"",
+ pOid, poc->oc_name);
+ status = LDAP_TYPE_OR_VALUE_EXISTS;
+ }
+ oc_unlock();
+ }
+
+ if (!status && !element_is_user_defined( attr_origins )) {
+ if ( is_user_defined ) {
+ /* add missing user defined origin string */
+ charray_add( &attr_origins,
+ slapi_ch_strdup( schema_user_defined_origin[0] ));
+ ++num_origins;
+ } else {
+ flags |= SLAPI_ATTR_FLAG_STD_ATTR;
+ }
+ }
+
+ if (!status) {
+ int ii;
+ /* check to see if the attribute name is valid */
+ for (ii = 0; !status && (ii < num_names); ++ii) {
+ if ( schema_check_name(attr_names[ii], PR_TRUE, errorbuf,
+ errorbufsize) == 0 ) {
+ status = invalid_syntax_error;
+ }
+ else if (!(flags & SLAPI_ATTR_FLAG_OVERRIDE) &&
+ attr_syntax_exists(attr_names[ii])) {
+ schema_create_errormsg( errorbuf, errorbufsize,
+ schema_errprefix_at, attr_names[ii],
+ "Could not be added because it already exists" );
+ status = LDAP_TYPE_OR_VALUE_EXISTS;
+ }
+ }
+ }
+
+ if (!status) {
+ if ( schema_check_oid ( first_attr_name, pOid, PR_TRUE, errorbuf,
+ errorbufsize ) == 0 ) {
+ status = invalid_syntax_error;
+ }
+ }
+
+ if (!status) {
+ struct asyntaxinfo *tmpasi;
+
+ if (!(flags & SLAPI_ATTR_FLAG_OVERRIDE) &&
+ ( NULL != ( tmpasi = attr_syntax_get_by_oid(pOid)))) {
+ schema_create_errormsg( errorbuf, errorbufsize,
+ schema_errprefix_at, first_attr_name,
+ "Could not be added because the OID \"%s\" is already in use",
+ pOid);
+ status = LDAP_TYPE_OR_VALUE_EXISTS;
+ attr_syntax_return( tmpasi );
+ }
+ }
+
+
+ if (!status) {
+ status = attr_syntax_create( pOid, attr_names, num_names,
+ *psbAttrDesc->buffer == '\0' ? NULL : psbAttrDesc->buffer,
+ pSuperior,
+ pMREquality, pMROrdering, pMRSubstring, attr_origins,
+ pSyntax, syntaxlength, flags, &tmpasip );
+ }
+
+ if (!status) {
+ if ( NULL != asipp ) {
+ *asipp = tmpasip; /* just return it */
+ } else { /* add the new attribute to the global store */
+ status = attr_syntax_add( tmpasip );
+ if ( LDAP_SUCCESS != status ) {
+ if ( 0 != (flags & SLAPI_ATTR_FLAG_OVERRIDE) &&
+ LDAP_TYPE_OR_VALUE_EXISTS == status ) {
+ /*
+ * This can only occur if the name and OID don't match the
+ * attribute we are trying to override (all other cases of
+ * "type or value exists" were trapped above).
+ */
+ schema_create_errormsg( errorbuf, errorbufsize,
+ schema_errprefix_at, first_attr_name,
+ "Does not match the OID \"%s\". Another attribute"
+ " type is already using the name or OID.", pOid);
+ } else {
+ schema_create_errormsg( errorbuf, errorbufsize,
+ schema_errprefix_at, first_attr_name,
+ "Could not be added (OID is \"%s\")", pOid );
+ }
+ attr_syntax_free( tmpasip );
+ }
+ }
+ }
+
+ done:
+ /* free everything */
+ free_qdlist(attr_names, num_names);
+ free_qdlist(attr_origins, num_origins);
+ sizedbuffer_destroy(psbAttrName);
+ sizedbuffer_destroy(psbAttrDesc);
+ slapi_ch_free((void **)&pOid);
+ slapi_ch_free((void **)&pSuperior);
+ slapi_ch_free((void **)&pMREquality);
+ slapi_ch_free((void **)&pMROrdering);
+ slapi_ch_free((void **)&pMRSubstring);
+ slapi_ch_free((void **)&pSyntax);
+
+ return status;
+}
+
+
+/*
+ * schema_check_oc_attrs:
+ * Check to see if the required and allowed attributes are valid attributes
+ *
+ * arguments: poc : pointer to the objectclass to check
+ * errorbuf : buffer to write any error messages to
+ * stripOptions: 1 if you want to silently strip any options
+ * 0 if options should cause an error
+ *
+ * Returns:
+ *
+ * 0 if there's a unknown attribute, and errorbuf will contain an
+ * error message.
+ *
+ * 1 if everything is ok
+ *
+ * Note: no locking of poc is needed because poc is always a newly allocated
+ * objclass struct (this function is only called by add_oc_internal).
+ */
+static int
+schema_check_oc_attrs ( struct objclass *poc,
+ char *errorbuf, size_t errorbufsize,
+ int stripOptions )
+{
+ int i;
+
+ if ( errorbuf == NULL || poc == NULL || poc->oc_name == NULL) {
+ /* error */
+ LDAPDebug (LDAP_DEBUG_PARSE,
+ "Null args passed to schema_check_oc_attrs\n",
+ NULL, NULL, NULL);
+ return -1;
+ }
+
+ /* remove any options like ;binary from the oc's attributes */
+ if ( strip_oc_options( poc ) && !stripOptions) {
+ /* there were options present, this oc should be rejected */
+ schema_create_errormsg( errorbuf, errorbufsize, schema_errprefix_oc,
+ poc->oc_name, "Contains attribute options. "
+ "Attribute options, such as \";binary\" are not allowed in "
+ "object class definitions." );
+ return 0;
+ }
+
+ for ( i = 0; poc->oc_allowed && poc->oc_allowed[i]; i++ ) {
+ if ( attr_syntax_exists ( poc->oc_allowed[i] ) == 0 ) {
+ schema_create_errormsg( errorbuf, errorbufsize, schema_errprefix_oc,
+ poc->oc_name, "Unknown allowed attribute type \"%s\"",
+ poc->oc_allowed[i]);
+ return 0;
+ }
+ }
+
+
+ for ( i = 0; poc->oc_required && poc->oc_required[i]; i++ ) {
+ if ( attr_syntax_exists ( poc->oc_required[i] ) == 0 ) {
+ schema_create_errormsg( errorbuf, errorbufsize, schema_errprefix_oc,
+ poc->oc_name, "Unknown required attribute type \"%s\"",
+ poc->oc_required[i]);
+ return 0;
+ }
+ }
+
+ return 1;
+}
+
+/*
+ * schema_check_name:
+ * Check if the attribute or objectclass name is valid. Names can only contain
+ * characters, digits, and hyphens. In addition, names must begin with
+ * a character. If the nsslapd-attribute-name-exceptions attribute in cn=config
+ * is true, then we also allow underscores.
+ *
+ * XXX We're also supposed to allow semicolons, but we already use them to deal
+ * with attribute options XXX
+ *
+ * returns 1 if the attribute has a legal name
+ * 0 if not
+ *
+ * If the attribute name is invalid, an error message will be written to msg
+ */
+
+static int
+schema_check_name(char *name, PRBool isAttribute, char *errorbuf,
+ size_t errorbufsize )
+{
+ int i;
+
+ /* allowed characters */
+ static char allowed[] = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890-";
+
+ /* additional characters to allow if allow_exceptions is true */
+ static char allowedExceptions[] = "_";
+ int allow_exceptions = config_get_attrname_exceptions();
+
+ if ( name == NULL || errorbuf == NULL) {
+ /* this is bad */
+ return 0;
+ }
+
+ /* attribute names must begin with a letter */
+ if ( (isascii (name[0]) == 0) || (isalpha (name[0]) == 0)) {
+ if ( (strlen(name) + 80) < BUFSIZ ) {
+ schema_create_errormsg( errorbuf, errorbufsize, schema_errprefix_at,
+ name, "The name is invalid. Names must begin with a letter" );
+ }
+ else {
+ schema_create_errormsg( errorbuf, errorbufsize, schema_errprefix_at,
+ name, "The name is invalid, and probably too long. "
+ "Names must begin with a letter" );
+ }
+ return 0;
+ }
+
+ for (i = 1; name[i]; i++ ) {
+ if ( (NULL == strchr( allowed, name[i] )) &&
+ (!allow_exceptions ||
+ (NULL == strchr(allowedExceptions, name[i])) ) ) {
+ if ( (strlen(name) + 80) < BUFSIZ ) {
+ schema_create_errormsg( errorbuf, errorbufsize, schema_errprefix_at,
+ name, "The name contains the invalid character \"%c\"", name[i] );
+ }
+ else {
+ schema_create_errormsg( errorbuf, errorbufsize, schema_errprefix_at,
+ name, "The name contains the invalid character \"%c\". The name"
+ " is also probably too long.", name[i] );
+ }
+ return 0;
+ }
+ }
+ return 1;
+}
+
+
+
+/*
+ * schema_check_oid:
+ * Check if the oid is valid.
+ *
+ * returns 1 if the attribute has a legal oid
+ * 0 if not
+ *
+ * If the oid is invalid, an error message will be written to errorbuf
+ *
+ * Oids can either have the form <attr/oc name>-oid or
+ * start and end with a digit, and contain only digits and periods
+ */
+
+static int
+schema_check_oid( const char *name, const char *oid, PRBool isAttribute,
+ char *errorbuf, size_t errorbufsize ) {
+
+ int i = 0, length_oid = 0, rc = 0;
+ char *namePlusOid = NULL;
+
+ if ( name == NULL || oid == NULL) {
+ /* this is bad */
+ LDAPDebug (LDAP_DEBUG_ANY, "NULL passed to schema_check_oid\n",0,0,0);
+ return 0;
+ }
+
+ /* check to see if the OID is <name>-oid */
+ namePlusOid = (char *) slapi_ch_malloc( strlen(name) + 256);
+ sprintf(namePlusOid, "%s-oid", name );
+ rc = strcasecmp( oid, namePlusOid );
+ slapi_ch_free( (void **) &namePlusOid );
+
+ if ( 0 == rc ) {
+ return 1;
+ }
+
+ /* If not, the OID must begin and end with a digit, and contain only
+ digits and dots */
+
+ /* check to see that it begins and ends with a digit */
+ length_oid = strlen(oid);
+ if ( !isdigit(oid[0]) ||
+ !isdigit(oid[length_oid-1]) ) {
+ schema_create_errormsg( errorbuf, errorbufsize,
+ isAttribute ? schema_errprefix_at : schema_errprefix_oc,
+ name,
+ "The OID \"%s\" must begin and end with a digit, or be \"%s-oid\"",
+ oid, name );
+ return 0;
+ }
+
+ /* check to see that it contains only digits and dots */
+ for ( i = 0; i < length_oid; i++ ) {
+ if ( !isdigit(oid[i]) && oid[i] != '.' ){
+ schema_create_errormsg( errorbuf, errorbufsize,
+ isAttribute ? schema_errprefix_at : schema_errprefix_oc,
+ name,
+ "The OID \"%s\" contains an invalid character: \"%c\"; the"
+ " OID must contain only digits and periods, or be \"%s-oid\"",
+ oid, oid[i], name );
+ return 0;
+ }
+ }
+
+ /* The oid is OK if we're here */
+ return 1;
+
+
+}
+
+
+/*
+ * Some utility functions for dealing with a dynamically
+ * allocated buffer.
+ */
+
+static struct sizedbuffer *sizedbuffer_construct(size_t size)
+{
+ struct sizedbuffer *p= (struct sizedbuffer *)slapi_ch_malloc(sizeof(struct sizedbuffer));
+ p->size= size;
+ if(size>0)
+ {
+ p->buffer= (char*)slapi_ch_malloc(size);
+ p->buffer[0]= '\0';
+ }
+ else
+ {
+ p->buffer= NULL;
+ }
+ return p;
+}
+
+static void sizedbuffer_destroy(struct sizedbuffer *p)
+{
+ if(p!=NULL)
+ {
+ slapi_ch_free((void**)&p->buffer);
+ }
+ slapi_ch_free((void**)&p);
+}
+
+static void sizedbuffer_allocate(struct sizedbuffer *p, size_t sizeneeded)
+{
+ if(p!=NULL)
+ {
+ if(sizeneeded>p->size)
+ {
+ if(p->buffer!=NULL)
+ {
+ slapi_ch_free((void**)&p->buffer);
+ }
+ p->buffer= (char*)slapi_ch_malloc(sizeneeded);
+ p->buffer[0]= '\0';
+ p->size= sizeneeded;
+ }
+ }
+}
+
+/*
+ * has_smart_referral: returns 1 if the entry contains a ref attribute,
+ * or a referral objectclass.
+ *
+ * Returns 0 If not.
+ */
+
+
+static int
+has_smart_referral( Slapi_Entry *e ) {
+
+ Slapi_Attr *aoc;
+ char ebuf[BUFSIZ];
+
+ /* Look for the ref attribute */
+ if ( (aoc = attrlist_find( e->e_attrs, "ref" )) != NULL ) {
+ LDAPDebug ( LDAP_DEBUG_ANY, "Entry \"%s\" contains a ref attrbute. Smart referrals are disabled in Directory Lite.\n", escape_string(slapi_entry_get_dn_const(e), ebuf),0,0 );
+ return 1;
+ }
+
+ /* Look for the referral objectclass */
+ if ( (aoc = attrlist_find( e->e_attrs, "objectclass" )) != NULL ) {
+ Slapi_Value target, *found;
+ slapi_value_init(&target);
+ slapi_value_set_string(&target,"referral");
+ found= slapi_valueset_find(aoc, &aoc->a_present_values, &target);
+ value_done(&target);
+ if(found!=NULL)
+ {
+ LDAPDebug ( LDAP_DEBUG_ANY, "Entry \"%s\" is a referral object class. Smart referrals are disabled in Directory Lite.\n", escape_string(slapi_entry_get_dn_const(e), ebuf),0,0 );
+ return 1;
+ }
+ }
+
+ /* No smart referral here */
+ return 0;
+}
+
+/*
+ * Check if the object class is extensible
+ */
+static int isExtensibleObjectclass(const char *objectclass)
+{
+ if ( strcasecmp( objectclass, "extensibleobject" ) == 0 ) {
+ return( 1 );
+ }
+ /* The Easter Egg is based on a special object class */
+ if ( strcasecmp( objectclass, EGG_OBJECT_CLASS ) == 0 ) {
+ return( 1 );
+ }
+ return 0;
+}
+
+
+
+/*
+ * strip_oc_options: strip any attribute options from the objectclass'
+ * attributes (remove things like ;binary from the attrs)
+ *
+ * argument: pointer to an objectclass, attributes will have their
+ * options removed in place
+ *
+ * returns: number of options removed
+ *
+ * Note: no locking of poc is needed because poc is always a newly allocated
+ * objclass struct (this function is only called by schema_check_oc_attrs,
+ * which is only called by add_oc_internal).
+ */
+
+static int
+strip_oc_options( struct objclass *poc ) {
+ int i, numRemoved = 0;
+ char *mod = NULL;
+
+ for ( i = 0; poc->oc_allowed && poc->oc_allowed[i]; i++ ) {
+ if ( (mod = stripOption( poc->oc_allowed[i] )) != NULL ){
+ LDAPDebug (LDAP_DEBUG_ANY,
+ "Removed option \"%s\" from allowed attribute type "
+ "\"%s\" in object class \"%s\".\n",
+ mod, poc->oc_allowed[i], poc->oc_name );
+ numRemoved++;
+ }
+ }
+
+ for ( i = 0; poc->oc_required && poc->oc_required[i]; i++ ) {
+ if ( (mod = stripOption( poc->oc_required[i] )) != NULL ){
+ LDAPDebug (LDAP_DEBUG_ANY,
+ "Removed option \"%s\" from required attribute type "
+ "\"%s\" in object class \"%s\".\n",
+ mod, poc->oc_required[i], poc->oc_name );
+ numRemoved++;
+ }
+ }
+ return numRemoved;
+}
+
+
+/*
+ * stripOption:
+ * removes options such as ";binary" from attribute names
+ *
+ * argument: pointer to an attribute name, such as "userCertificate;binary"
+ *
+ * returns: pointer to the option, such as "binary"
+ * NULL if there's no option
+ *
+ */
+
+static char *
+stripOption(char *attr) {
+ char *pSemiColon = strchr( attr, ';' );
+
+ if (pSemiColon) {
+ *pSemiColon = '\0';
+ }
+
+ return pSemiColon ? pSemiColon + 1 : NULL;
+}
+
+
+/*
+ * load_schema_dse: called by dse_read_file() when target is cn=schema
+ *
+ * Initialize attributes and objectclasses from the schema
+ *
+ * Note that this function removes all values for `attributetypes'
+ * and `objectclasses' attributes from the entry `e'.
+ *
+ * returntext is always at least SLAPI_DSE_RETURNTEXT_SIZE bytes in size.
+ */
+int
+load_schema_dse(Slapi_PBlock *pb, Slapi_Entry *e, Slapi_Entry *ignored,
+ int *returncode, char *returntext, void *arg)
+{
+ Slapi_Attr *attr = 0;
+ int nolock = 1; /* don't lock global resources during initialization */
+ int primary_file = 0; /* this is the primary (writeable) schema file */
+ int schema_ds4x_compat = config_get_ds4_compatible_schema();
+
+ *returncode = 0;
+
+ /*
+ * Note: there is no need to call schema_lock_write() here because this
+ * function is only called during server startup.
+ */
+
+ slapi_pblock_get( pb, SLAPI_DSE_IS_PRIMARY_FILE, &primary_file );
+
+ if (!slapi_entry_attr_find(e, "attributetypes", &attr) && attr)
+ {
+ /* enumerate the values in attr */
+ Slapi_Value *v = 0;
+ int index = 0;
+ for (index = slapi_attr_first_value(attr, &v);
+ v && (index != -1);
+ index = slapi_attr_next_value(attr, index, &v))
+ {
+ const char *s = slapi_value_get_string(v);
+ if (!s)
+ continue;
+ if ((*returncode = read_at_ldif(s, NULL, returntext,
+ SLAPI_DSE_RETURNTEXT_SIZE, nolock,
+ primary_file /* force user defined? */,
+ schema_ds4x_compat, 0)) != 0)
+ break;
+ }
+ slapi_entry_attr_delete(e, "attributetypes");
+ }
+
+ if (*returncode)
+ return SLAPI_DSE_CALLBACK_ERROR;
+
+ if (!slapi_entry_attr_find(e, "objectclasses", &attr) && attr)
+ {
+ /* enumerate the values in attr */
+ Slapi_Value *v = 0;
+ int index = 0;
+ for (index = slapi_attr_first_value(attr, &v);
+ v && (index != -1);
+ index = slapi_attr_next_value(attr, index, &v))
+ {
+ struct objclass *oc = 0;
+ const char *s = slapi_value_get_string(v);
+ if (!s)
+ continue;
+ if ( LDAP_SUCCESS != (*returncode = read_oc_ldif(s, &oc, returntext,
+ SLAPI_DSE_RETURNTEXT_SIZE, nolock,
+ primary_file /* force user defined? */,
+ schema_ds4x_compat))) {
+ break;
+ }
+ if ( LDAP_SUCCESS != (*returncode = add_oc_internal(oc, returntext,
+ SLAPI_DSE_RETURNTEXT_SIZE, schema_ds4x_compat))) {
+ oc_free( &oc );
+ break;
+ }
+ }
+ slapi_entry_attr_delete(e, "objectclasses");
+ }
+
+ /* Set the schema CSN */
+ if (!slapi_entry_attr_find(e, "nsschemacsn", &attr) && attr)
+ {
+ Slapi_Value *v = NULL;
+ slapi_attr_first_value(attr, &v);
+ if (NULL != v) {
+ const char *s = slapi_value_get_string(v);
+ if (NULL != s) {
+ CSN *csn = csn_new_by_string(s);
+ g_set_global_schema_csn(csn);
+ }
+ }
+ }
+
+ return (*returncode == LDAP_SUCCESS) ? SLAPI_DSE_CALLBACK_OK
+ : SLAPI_DSE_CALLBACK_ERROR;
+}
+
+/*
+ * Try to initialize the schema from the LDIF file. Read
+ * the file and convert it to the avl tree of DSEs. If the
+ * file doesn't exist, we try to create it and put a minimal
+ * schema entry into it.
+ *
+ * Returns 1 for OK, 0 for Fail.
+ */
+int
+init_schema_dse(const char *configdir)
+{
+ int rc= 1; /* OK */
+ char *schemadir = 0;
+ char *userschemafile = 0;
+ char *userschematmpfile = 0;
+ char **filelist = 0;
+ Slapi_DN schema;
+
+ slapi_sdn_init_dn_byref(&schema,"cn=schema");
+
+ schemadir = slapi_ch_calloc(1, strlen(configdir)
+ + strlen(SCHEMA_SUBDIR_NAME) + 5);
+ sprintf(schemadir, "%s/%s", configdir, SCHEMA_SUBDIR_NAME);
+
+ filelist = get_priority_filelist(schemadir, ".*ldif$");
+ if (!filelist || !*filelist)
+ {
+ slapi_log_error(SLAPI_LOG_FATAL, "schema",
+ "No schema files were found in the directory %s\n", schemadir);
+ free_filelist(filelist);
+ rc = 0;
+ }
+ else
+ {
+ /* figure out the last file in the list; it is the user schema */
+ int ii = 0;
+ while (filelist[ii]) ++ii;
+ userschemafile = filelist[ii-1];
+ userschematmpfile = slapi_ch_calloc(1, strlen(userschemafile) + 5);
+ sprintf(userschematmpfile, "%s.tmp", userschemafile);
+ }
+
+ if(rc && (pschemadse==NULL))
+ {
+ pschemadse= dse_new_with_filelist(userschemafile,userschematmpfile, NULL, NULL,
+ schemadir,filelist);
+ PR_ASSERT(pschemadse);
+ if ((rc= (pschemadse!=NULL)) != 0)
+ dse_register_callback(pschemadse,DSE_OPERATION_READ,DSE_FLAG_PREOP,&schema,
+ LDAP_SCOPE_BASE,NULL,
+ load_schema_dse,NULL);
+ slapi_ch_free((void**)&userschematmpfile);
+ }
+ slapi_ch_free((void**)&schemadir);
+
+ if(rc)
+ {
+ char errorbuf[SLAPI_DSE_RETURNTEXT_SIZE] = {0};
+ int dont_write = 1;
+ int merge = 1;
+ int dont_dup_check = 1;
+ Slapi_PBlock pb;
+ memset(&pb, 0, sizeof(pb));
+ /* don't write out the file when reading */
+ slapi_pblock_set(&pb, SLAPI_DSE_DONT_WRITE_WHEN_ADDING, (void*)&dont_write);
+ /* duplicate entries are allowed */
+ slapi_pblock_set(&pb, SLAPI_DSE_MERGE_WHEN_ADDING, (void*)&merge);
+ /* use the non duplicate checking str2entry */
+ slapi_pblock_set(&pb, SLAPI_DSE_DONT_CHECK_DUPS, (void*)&dont_dup_check);
+
+ /* add the objectclass attribute so we can do some basic schema
+ checking during initialization; this will be overridden when
+ its "real" definition is read from the schema conf files */
+ rc = read_at_ldif("attributeTypes: ( 2.5.4.0 NAME 'objectClass' "
+ "DESC 'Standard schema for LDAP' SYNTAX "
+ "1.3.6.1.4.1.1466.115.121.1.15 X-ORIGIN 'RFC 2252' )",
+ NULL, errorbuf, SLAPI_DSE_RETURNTEXT_SIZE, 1, 0, 0, 0);
+ if (rc)
+ {
+ slapi_log_error(SLAPI_LOG_FATAL, "schema", "Could not add"
+ " attribute type \"objectClass\" to the schema: %s\n",
+ errorbuf, 0, 0);
+ }
+
+ rc = dse_read_file(pschemadse, &pb);
+ }
+
+ if (rc)
+ {
+ /* make sure the schema is normalized */
+ normalize_oc();
+
+ /* register callbacks */
+ dse_register_callback(pschemadse,SLAPI_OPERATION_SEARCH,DSE_FLAG_PREOP,&schema,
+ LDAP_SCOPE_BASE,NULL,read_schema_dse,NULL);
+ dse_register_callback(pschemadse,SLAPI_OPERATION_MODIFY,DSE_FLAG_PREOP,&schema,
+ LDAP_SCOPE_BASE,NULL,modify_schema_dse,NULL);
+ dse_register_callback(pschemadse,SLAPI_OPERATION_DELETE,DSE_FLAG_PREOP,&schema,
+ LDAP_SCOPE_BASE,NULL,dont_allow_that,NULL);
+ dse_register_callback(pschemadse,DSE_OPERATION_WRITE,DSE_FLAG_PREOP,&schema,
+ LDAP_SCOPE_BASE,NULL,refresh_user_defined_schema,
+ NULL);
+
+ if (rc) {
+ Slapi_Backend *be;
+
+ /* add as a backend */
+ be= be_new_internal(pschemadse,"DSE", "schema-internal"); /* JCM - should be a #define */
+ be_addsuffix(be,&schema);
+ }
+ }
+
+ slapi_sdn_done(&schema);
+ return rc;
+}
+
+
+
+/*
+ * Look for `keyword' within `*inputp' and return the flag_value if found
+ * (zero if returned if not found).
+ *
+ * If the keyword is found, `*inputp' is set to point just beyond the end of
+ * the keyword. If the keyword is not found, `*inputp' is not changed.
+ *
+ * The `strstr_fn' function pointer is used to search for `keyword', e.g., it
+ * could be PL_strcasestr().
+ *
+ * The string passed in `keyword' MUST include a trailing space, e.g.,
+ *
+ * flag |= get_flag_keyword( " COLLECTIVE ", SLAPI_ATTR_FLAG_COLLECTIVE,
+ * &input, PL_strcasestr );
+ *
+ */
+static int
+get_flag_keyword( const char *keyword, int flag_value, const char **inputp,
+ schema_strstr_fn_t strstr_fn )
+{
+ const char *kw;
+
+ PR_ASSERT( NULL != inputp );
+ PR_ASSERT( NULL != *inputp );
+ PR_ASSERT( ' ' == keyword[ strlen( keyword ) - 1 ] );
+
+ if ( NULL == strstr_fn ) {
+ strstr_fn = PL_strcasestr;
+ }
+
+ kw = (*strstr_fn)( *inputp, keyword );
+ if ( NULL == kw ) {
+ flag_value = 0; /* not found -- return no value */
+ } else {
+ *inputp = kw + strlen( keyword ) - 1; /* advance input */
+ }
+
+ return flag_value;
+}
+
+/*
+ * Look for `tag' within `*inputp' and return the OID string following `tag'.
+ * If the OID has single quotes around it they are removed (they are allowed
+ * for compatibility with DS 3.x and 4.x).
+ *
+ * If the tag is found, `*inputp' is set to point just beyond the end of
+ * the OID that was extracted and returned. If the tag is not found,
+ * `*inputp' is not changed.
+ *
+ * The `strstr_fn' function pointer is used to search for `tag', e.g., it
+ * could be PL_strcasestr().
+ *
+ * The string passed in `tag' MUST include a trailing space, e.g.,
+ *
+ * pSuperior = get_tagged_oid( "SUP ", &input, PL_strcasestr );
+ *
+ * A malloc'd string is returned if `tag; is found and NULL if not.
+ */
+static char *
+get_tagged_oid( const char *tag, const char **inputp,
+ schema_strstr_fn_t strstr_fn )
+{
+ const char *startp, *endp;
+ char *oid;
+
+ PR_ASSERT( NULL != inputp );
+ PR_ASSERT( NULL != *inputp );
+ PR_ASSERT( NULL != tag );
+ PR_ASSERT( '\0' != tag[ 0 ] );
+ if('(' !=tag[0])
+ PR_ASSERT( ' ' == tag[ strlen( tag ) - 1 ] );
+
+ if ( NULL == strstr_fn ) {
+ strstr_fn = PL_strcasestr;
+ }
+
+ oid = NULL;
+ if ( NULL != ( startp = (*strstr_fn)( *inputp, tag ))) {
+ startp += strlen( tag );
+
+ /* skip past any extra white space */
+ if ( NULL == ( startp = skipWS( startp ))) {
+ return( NULL );
+ }
+
+ /* skip past the leading single quote, if present */
+ if ( *startp == '\'' ) {
+ ++startp;
+ /* skip past any extra white space */
+ startp = skipWS( startp );
+ }
+
+ /* locate the end of the OID */
+ if ((NULL != ( endp = strchr( startp, ' '))) ||
+ (NULL != (endp = strchr( startp, ')'))) ) {
+ if ( '\'' == *(endp-1) && endp > startp ) {
+ --endp; /* ignore trailing quote */
+ }
+ } else {
+ endp = startp + strlen( startp ); /* remainder of input */
+ }
+
+ oid = slapi_ch_malloc( endp - startp + 1 );
+ memcpy( oid, startp, endp - startp );
+ oid[ endp - startp ] = '\0';
+ *inputp = endp;
+ }
+
+ return( oid );
+}
+
+
+/*
+ * sprintf to `outp' the contents of `tag' followed by `oid' followed by a
+ * trailing space. If enquote is non-zero, single quotes are included
+ * around the `oid' string. If `suffix' is not NULL, it is output directly
+ * after the `oid' (before the trailing space).
+ * Note that `tag' should typically include a trailing space, e.g.,
+ *
+ * outp += put_tagged_oid( outp, "SUP ", "1.2.3.4", NULL, enquote_oids );
+ *
+ * Returns the number of bytes copied to `outp' or 0 if `oid' is NULL.
+ */
+static int
+put_tagged_oid( char *outp, const char *tag, const char *oid,
+ const char *suffix, int enquote )
+{
+ int count = 0;
+
+ if ( NULL == suffix ) {
+ suffix = "";
+ }
+
+ if ( NULL != oid ) {
+ if ( enquote ) {
+ count = sprintf( outp, "%s'%s%s' ", tag, oid, suffix );
+ } else {
+ count = sprintf( outp, "%s%s%s ", tag, oid, suffix );
+ }
+ }
+
+ return( count );
+}
+
+
+/*
+ * Add to `buf' a string of the form:
+ *
+ * prefix SPACE ( oid1 $ oid2 ... ) SPACE
+ * OR
+ * prefix SPACE oid SPACE
+ *
+ * The part after <prefix> matches the `oids' definition
+ * from RFC 2252 section 4.1.
+ *
+ * If oids is NULL or an empty array, `buf' is not touched.
+ */
+static void
+strcat_oids( char *buf, char *prefix, char **oids, int schema_ds4x_compat )
+{
+ char *p;
+ int i;
+
+ if ( NULL != oids && NULL != oids[0] ) {
+ p = buf + strlen(buf); /* skip past existing content */
+ if ( NULL == oids[1] && !schema_ds4x_compat ) {
+ sprintf( p, "%s %s ", prefix, oids[0] ); /* just one oid */
+ } else {
+ sprintf( p, "%s ( ", prefix ); /* oidlist */
+ for ( i = 0; oids[i] != NULL; ++i ) {
+ if ( i > 0 ) {
+ strcat( p, " $ " );
+ }
+ strcat( p, oids[i] );
+ }
+ strcat( p, " ) " );
+ }
+ }
+}
+
+
+/*
+ * Add to `buf' a string of the form:
+ *
+ * prefix SPACE ( 's1' 's2' ... ) SPACE
+ * OR
+ * prefix SPACE 's1' SPACE
+ *
+ * The part after <prefix> matches the qdescs definition
+ * from RFC 2252 section 4.1.
+ *
+ * A count of the number of bytes added to buf or needed is returned.
+ *
+ * If buf is NULL, no copying is done but the number of bytes needed
+ * is calculated and returned. This is useful if you need to allocate
+ * space before calling this function will a buffer.
+ *
+ * If qdlist is NULL or an empty array, `buf' is not touched and zero
+ * is returned.
+ */
+static size_t
+strcat_qdlist( char *buf, char *prefix, char **qdlist )
+{
+ int i;
+ char *start, *p;
+ size_t len = 0;
+
+ if ( NULL != qdlist && NULL != qdlist[0] ) {
+ if ( NULL == buf ) { /* calculate length only */
+ len += strlen( prefix );
+ if ( NULL != qdlist[1] ) {
+ len += 4; /* surrounding spaces and '(' and ')' */
+ }
+ for ( i = 0; NULL != qdlist[i]; ++i ) {
+ len += 3; /* leading space and quote marks */
+ len += strlen(qdlist[i]);
+ }
+ ++len; /* trailing space */
+
+ } else {
+ p = start = buf + strlen(buf); /* skip past existing content */
+ if ( NULL == qdlist[1] ) { /* just one string */
+ p += sprintf( p, "%s '%s' ", prefix, qdlist[0] );
+ } else { /* a list of strings */
+ p += sprintf( p, "%s (", prefix );
+ for ( i = 0; qdlist[i] != NULL; ++i ) {
+ p += sprintf( p, " '%s'", qdlist[i] );
+ }
+ *p++ = ' ';
+ *p++ = ')';
+ *p++ = ' ';
+ *p = '\0';
+ }
+ len = p - start;
+ }
+ }
+
+ return( len );
+}
+
+
+/*
+ * Just like strlen() except that 0 is returned if `s' is NULL.
+ */
+static size_t
+strlen_null_ok(const char *s)
+{
+ if ( NULL == s ) {
+ return( 0 );
+ }
+
+ return( strlen( s ));
+}
+
+
+
+/*
+ * Like strcpy() except a count of the number of bytes copied is returned.
+ */
+static int
+strcpy_count( char *dst, const char *src )
+{
+ char *p;
+
+ p = dst;
+ while ( *src != '\0' ) {
+ *p++ = *src++;
+ }
+
+ *p = '\0';
+ return( p - dst );
+}
+
+
+/*
+ * Look for an X-ORIGIN list in `schema_value' and return a
+ * qdstrings array (or NULL if no list is present). *num_originsp is
+ * set to a count of the number of origin strings returned.
+ *
+ * If no X-ORIGIN list is found and `default_list' is non-NULL, a copy
+ * of default list is returned.
+ */
+static char **
+parse_origin_list( const char *schema_value, int *num_originsp,
+ char **default_list )
+{
+ char *start, *end, *origin_tmp;
+ char **origins = NULL;
+
+ if (( start = PL_strstr ( schema_value, "X-ORIGIN ")) != NULL ) {
+ start += 9;
+ origin_tmp = slapi_ch_strdup( start );
+ if ( *start == '(' ) {
+ end = strchr( origin_tmp, ')' );
+ } else {
+ end = strchr( origin_tmp + 1, '\'' );
+ }
+ if (end) {
+ *(end+1) = 0;
+ }
+ origins = parse_qdstrings( origin_tmp, num_originsp );
+ slapi_ch_free( (void **)&origin_tmp );
+ } else {
+ origins = NULL;
+ *num_originsp = 0;
+ }
+
+ if ( NULL == origins && NULL != default_list ) {
+ int i;
+
+ for ( i = 0; default_list[i] != NULL; ++i ) {
+ ;
+ }
+ *num_originsp = i;
+ origins = (char **)slapi_ch_malloc( (i+1) * sizeof(char *));
+ for ( i = 0; default_list[i] != NULL; ++i ) {
+ origins[i] = slapi_ch_strdup( default_list[i] );
+ }
+ origins[i] = NULL;
+ }
+
+if ( origins == NULL || origins[0] == NULL ) {
+ LDAPDebug( LDAP_DEBUG_ANY, "no origin (%s)\n", schema_value, 0, 0 );
+}
+
+ return origins;
+}
+
+
+/*
+ * Determine based on origin whether a schema element is standard or
+ * user-defined. Algorithm: perform a case insensitive search for
+ * "user defined". If found, return 1 (user defined); if not, return 0.
+ */
+static int
+element_is_user_defined( char * const *origins )
+{
+ int i;
+
+ if ( origins != NULL ) {
+ for ( i = 0; origins[i] != NULL; ++i ) {
+ if ( 0 == strcasecmp( schema_user_defined_origin[0], origins[i] )) {
+ return 1;
+ }
+ }
+ }
+
+ return 0;
+}
+
+
+/*
+ * Return PR_TRUE if the attribute type named 'type' is one of those that
+ * we handle directly in this file (in the scheme DSE callbacks).
+ * Other types are handled by the generic DSE code in dse.c.
+ */
+/* subschema DSE attribute types we handle within the DSE callback */
+static char *schema_interesting_attr_types[] = {
+ "dITStructureRules",
+ "nameForms",
+ "dITContentRules",
+ "objectClasses",
+ "attributeTypes",
+ "matchingRules",
+ "matchingRuleUse",
+ "ldapSyntaxes",
+ "nsschemacsn",
+ NULL
+};
+
+
+static PRBool
+schema_type_is_interesting( const char *type )
+{
+ int i;
+
+ for ( i = 0; schema_interesting_attr_types[i] != NULL; ++i ) {
+ if ( 0 == strcasecmp( type, schema_interesting_attr_types[i] )) {
+ return PR_TRUE;
+ }
+ }
+
+ return PR_FALSE;
+}
+
+
+static void
+schema_create_errormsg(
+ char *errorbuf,
+ size_t errorbufsize,
+ const char *prefix,
+ const char *name,
+ const char *fmt,
+ ...
+)
+{
+ if ( NULL != errorbuf ) {
+ va_list ap;
+ int rc = 0;
+
+ va_start( ap, fmt );
+
+ if ( NULL != name ) {
+ rc = PR_snprintf( errorbuf, errorbufsize, prefix, name );
+ }
+ if ( rc >= 0 ) {
+ (void)PR_vsnprintf( errorbuf + rc, errorbufsize - rc, fmt, ap );
+ }
+ va_end( ap );
+ }
+}
+
+
+/*
+ * va_locate_oc_val finds an objectclass within the array of values in va.
+ * First oc_name is used, falling back to oc_oid. oc_oid can be NULL.
+ * oc_name and oc_oid should be official names (no trailing spaces). But
+ * trailing spaces within the va are ignored if appropriate.
+ *
+ * Returns >=0 if found (index into va) and -1 if not found.
+ */
+static int
+va_locate_oc_val( Slapi_Value **va, const char *oc_name, const char *oc_oid )
+{
+ int i;
+ const char *strval;
+
+ if ( NULL == va || oc_name == NULL ) { /* nothing to look for */
+ return -1;
+ }
+
+ if ( !schema_ignore_trailing_spaces ) {
+ for ( i = 0; va[i] != NULL; i++ ) {
+ strval = slapi_value_get_string(va[i]);
+ if ( NULL != strval ) {
+ if ( 0 == strcasecmp(strval, oc_name)) {
+ return i;
+ }
+
+ if ( NULL != oc_oid
+ && 0 == strcasecmp( strval, oc_oid )) {
+ return i;
+ }
+ }
+ }
+ } else {
+ /*
+ * Ignore trailing spaces when comparing object class names.
+ */
+ size_t len;
+ const char *p;
+
+ for ( i = 0; va[i] != NULL; i++ ) {
+ strval = slapi_value_get_string(va[i]);
+ if ( NULL != strval ) {
+ for ( p = strval, len = 0; (*p != '\0') && (*p != ' ');
+ p++, len++ ) {
+ ; /* NULL */
+ }
+
+ if ( 0 == strncasecmp(oc_name, strval, len )
+ && ( len == strlen(oc_name))) {
+ return i;
+ }
+
+ if ( NULL != oc_oid
+ && ( 0 == strncasecmp( oc_oid, strval, len ))
+ && ( len == strlen(oc_oid))) {
+ return i;
+ }
+ }
+ }
+ }
+
+ return -1; /* not found */
+}
+
+
+/*
+ * va_expand_one_oc is used to add missing superclass values to the
+ * objectclass attribute when an entry is added or modified.
+ *
+ * missing values are always added to the end of the 'vap' array.
+ *
+ * Note: calls to this function MUST be bracketed by lock()/unlock(), i.e.,
+ *
+ * oc_lock_read();
+ * va_expand_one_oc( b, o );
+ * oc_unlock();
+ */
+static void
+va_expand_one_oc( const char *dn, Slapi_Value ***vap, const char *ocs )
+{
+ struct objclass *this_oc, *sup_oc;
+ int p,i;
+ Slapi_Value **newva;
+ char ebuf[BUFSIZ];
+
+ this_oc = oc_find_nolock( ocs );
+
+ if ( this_oc == NULL ) {
+ return; /* skip unknown object classes */
+ }
+
+ if ( this_oc->oc_superior == NULL ) {
+ return; /* no superior */
+ }
+
+ sup_oc = oc_find_nolock( this_oc->oc_superior );
+ if ( sup_oc == NULL ) {
+ return; /* superior is unknown -- ignore */
+ }
+
+ p = va_locate_oc_val( *vap, sup_oc->oc_name, sup_oc->oc_oid );
+
+ if ( p != -1 ) {
+ return; /* value already present -- done! */
+ }
+
+ /* parent was not found. add to the end */
+ for ( i = 0; (*vap)[i] != NULL; i++ ) {
+ ;
+ }
+
+ /* prevent loops: stop if more than 1000 OC values are present */
+ if ( i > 1000 ) {
+ return;
+ }
+
+ newva = (Slapi_Value **)slapi_ch_realloc( (char *)*vap,
+ ( i + 2 )*sizeof(Slapi_Value **));
+
+ newva[i] = slapi_value_new_string(sup_oc->oc_name);
+ newva[i+1] = NULL;
+
+ *vap = newva;
+ LDAPDebug( LDAP_DEBUG_TRACE,
+ "Entry \"%s\": added missing objectClass value %s\n",
+ escape_string( dn, ebuf ), sup_oc->oc_name, 0 );
+}
+
+
+/*
+ * Expand the objectClass values in 'e' to take superior classes into account.
+ * All missing superior classes are added to the objectClass attribute, as
+ * is 'top' if it is missing.
+ */
+void
+slapi_schema_expand_objectclasses( Slapi_Entry *e )
+{
+ Slapi_Attr *sa;
+ Slapi_Value **va;
+ const char *dn = slapi_entry_get_dn_const( e );
+ int i;
+
+ if ( 0 != slapi_entry_attr_find( e, SLAPI_ATTR_OBJECTCLASS, &sa )) {
+ return; /* no OC values -- nothing to do */
+ }
+
+ va = attr_get_present_values( sa );
+
+ if ( va == NULL || va[0] == NULL ) {
+ return; /* no OC values -- nothing to do */
+ }
+
+ oc_lock_read();
+
+ /*
+ * This loop relies on the fact that bv_expand_one_oc()
+ * always adds to the end
+ */
+ for ( i = 0; va[i] != NULL; ++i ) {
+ if ( NULL != slapi_value_get_string(va[i]) ) {
+ va_expand_one_oc( dn, &va, slapi_value_get_string(va[i]) );
+ }
+ }
+
+ /* top must always be present */
+ va_expand_one_oc( dn, &va, "top" );
+
+ /*
+ * Reset the present values in the set because we may have realloc'd it.
+ * Note that this is the counterpart to the attr_get_present_values()
+ * call we made above... nothing new has been allocated, but sa holds
+ * a pointer to the original (pre realloc) va.
+ */
+ sa->a_present_values.va = va;
+
+ oc_unlock();
+}
diff --git a/ldap/servers/slapd/schemaparse.c b/ldap/servers/slapd/schemaparse.c
new file mode 100644
index 00000000..c6fb51fe
--- /dev/null
+++ b/ldap/servers/slapd/schemaparse.c
@@ -0,0 +1,262 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+/* schemaparse.c - routines to support objectclass definitions */
+
+#include <stdio.h>
+#include <string.h>
+#include <sys/types.h>
+#include "slap.h"
+
+
+/* global_oc and global_schema_csn are both protected by oc locks */
+struct objclass *global_oc;
+CSN *global_schema_csn = NULL; /* Timestamp for last update CSN. NULL = epoch */
+
+static int is_duplicate( char *target, char **list, int list_max );
+static void normalize_list( char **list );
+
+
+
+/* R/W lock used to protect the global objclass linked list. */
+static PRRWLock *oc_lock = NULL;
+
+/*
+ * The oc_init_lock_callonce structure is used by NSPR to ensure
+ * that oc_init_lock() is called at most once.
+ */
+static PRCallOnceType oc_init_lock_callonce = { 0, 0, 0 };
+
+
+/* Create the objectclass read/write lock. Returns PRSuccess if successful */
+static PRStatus
+oc_init_lock( void )
+{
+ if ( NULL == ( oc_lock = PR_NewRWLock( PR_RWLOCK_RANK_NONE,
+ "objectclass rwlock" ))) {
+ slapi_log_error( SLAPI_LOG_FATAL, "oc_init_lock",
+ "PR_NewRWLock() for objectclass lock failed\n" );
+ return PR_FAILURE;
+ }
+
+ return PR_SUCCESS;
+}
+
+
+void
+oc_lock_read( void )
+{
+ if ( NULL != oc_lock ||
+ PR_SUCCESS == PR_CallOnce( &oc_init_lock_callonce, oc_init_lock )) {
+ PR_RWLock_Rlock( oc_lock );
+ }
+}
+
+
+void
+oc_lock_write( void )
+{
+ if ( NULL != oc_lock ||
+ PR_SUCCESS == PR_CallOnce( &oc_init_lock_callonce, oc_init_lock )) {
+ PR_RWLock_Wlock( oc_lock );
+ }
+}
+
+
+void
+oc_unlock( void )
+{
+ if ( oc_lock != NULL ) {
+ PR_RWLock_Unlock( oc_lock );
+ }
+}
+
+
+/*
+ * Note: callers of g_get_global_oc_nolock() must hold a read or write lock
+ */
+struct objclass* g_get_global_oc_nolock()
+{
+ return global_oc;
+}
+
+/*
+ * Note: callers of g_set_global_oc_nolock() must hold a write lock
+ */
+void
+g_set_global_oc_nolock( struct objclass *newglobaloc )
+{
+ global_oc = newglobaloc;
+}
+
+/*
+ * Note: callers of g_get_global_schema_csn() must hold a read lock
+ */
+const CSN *
+g_get_global_schema_csn()
+{
+ return global_schema_csn;
+}
+
+/*
+ * Note: callers of g_set_global_schema_csn() must hold a write lock.
+ * csn is consumed.
+ */
+void
+g_set_global_schema_csn(CSN *csn)
+{
+ CSN *tmp = NULL;
+ if (NULL != global_schema_csn)
+ {
+ tmp = global_schema_csn;
+ }
+ global_schema_csn = csn;
+ if (NULL != tmp)
+ {
+ csn_free(&tmp);
+ }
+}
+
+/*
+ * There are two kinds of objectclasses:
+ * Standard Objectclasses and User Defined Objectclasses
+ *
+ * Standard Objectclasses are the objectclasses which come with the Directory Server.
+ * These objectclasses are always expected to be there and shouldn't be accidentally
+ * changed by the end user. We dont' allow these objectclasses to be deleted, and the
+ * admin CGIs will not allow the end user to change their definitions. However, we
+ * will allow these objectclasses to be redefined via ldap_modify, by doing an LDAP_MOD_ADD.
+ * The new definition will override the previous definition. The updated objectclass
+ * will be written out the 00user.ldif and the original definition will stay
+ * whereever it was originally defined. At startup, slapd will use the last definition
+ * read as the real definition of an objectclass.
+ *
+ * User Defined ObjectClasses are objectclasses which were added to the Directory Server
+ * by the end user. These objectclasses are also kept in 99user.ldif. These objectclasses
+ * can be deleted by the end user.
+ *
+ * Every objectclass contains an array of attributes called oc_orig_required,
+ * which are the required attributes for that objectclass which were not inherited from
+ * any other objectclass. Likewise, there's also an array called oc_orig_allowed which
+ * contains the allowed attributes which were not inherited from any other objectclass.
+ *
+ * The arrays oc_required and oc_allowed contain all the required and allowed attributes for
+ * that objectclass, including the ones inherited from its parent and also the ones in
+ * oc_orig_required and oc_orig_allowed.
+ *
+ * When an oc is updated, we go through the global list of objectclasses and see if
+ * any ocs inherited from it. If so, we delete its oc_required and oc_allowed arrays,
+ * copy the oc_orig_required and oc_orig_allowed arrays to oc_required and oc_allowed,
+ * and then merge the parent's oc_required and oc_allowed onto oc_required and oc_allowed.
+ *
+ *
+ */
+
+
+static int
+is_duplicate( char *target, char **list, int list_size ) {
+ int i;
+ for ( i = 0; i < list_size; i++ ) {
+ if ( !strcasecmp( target, list[i] ) ) {
+ return 1;
+ }
+ }
+ return 0;
+}
+
+/*
+ * Make normalized copies of all non-duplicate values in a list; free all old
+ * values. The list is not resized.
+ */
+static void
+normalize_list( char **list ) {
+ int i, j;
+
+ for ( i = 0, j = 0; list != NULL && list[i] != NULL; i++ ) {
+ char *norm = slapi_attr_syntax_normalize( list[i] );
+ char *save = list[i];
+ if ( !is_duplicate( norm, list, j ) ) {
+ list[j++] = norm;
+ } else {
+ slapi_ch_free((void **)&norm );
+ }
+ slapi_ch_free((void**)&save );
+ }
+ for ( ; j < i; j++ ) {
+ list[j] = NULL;
+ }
+}
+
+/*
+ * normalize types contained in object class definitions. do this
+ * after the whole config file is read so there is no order dependency
+ * on inclusion of attributes and object classes.
+ */
+
+void
+normalize_oc( void )
+{
+ struct objclass *oc;
+
+ oc_lock_write();
+
+ for ( oc = g_get_global_oc_nolock(); oc != NULL; oc = oc->oc_next ) {
+ LDAPDebug (LDAP_DEBUG_PARSE,
+ "normalize_oc: normalizing '%s'\n", oc->oc_name, 0, 0);
+ /* required attributes */
+ normalize_list( oc->oc_required );
+ normalize_list( oc->oc_orig_required );
+
+ /* optional attributes */
+ normalize_list( oc->oc_allowed );
+ normalize_list( oc->oc_orig_allowed );
+ }
+
+ oc_unlock();
+}
+
+/*
+ * oc_update_inheritance_nolock:
+ * If an objectclass is redefined, we need to make sure that any objectclasses
+ * which inherit from the redefined objectclass have their required and allowed
+ * attributes updated.
+ *
+ * Every objectclass contains an array of attributes called oc_orig_required,
+ * which are the required attributes for that objectclass which were not inherited from
+ * any other objectclass. Likewise, there's also an array called oc_orig_allowed which
+ * contains the allowed attributes which were not inherited from any other objectclass.
+ *
+ * The arrays oc_required and oc_allowed contain all the required and allowed attributes for
+ * that objectclass, including the ones inherited from its parent and also the ones in
+ * oc_orig_required and oc_orig_allowed.
+ *
+ * When an oc is updated, we go through the global list of objectclasses and see if
+ * any ocs inherited from it. If so, we delete its oc_requried and oc_allowed arrays,
+ * copy the oc_orig_required and oc_orig_allowed arrays to oc_required and oc_allowed,
+ * and then merge the parent's oc_required and oc_allowed onto oc_required and oc_allowed.
+ */
+
+void
+oc_update_inheritance_nolock( struct objclass *psuperior_oc )
+{
+ struct objclass *oc;
+
+ for ( oc = g_get_global_oc_nolock(); oc != NULL; oc = oc->oc_next ) {
+ if ( oc->oc_superior &&
+ (strcasecmp( oc->oc_superior, psuperior_oc->oc_name ) == 0) ) {
+ if (oc->oc_required ) {
+ charray_free (oc->oc_required);
+ }
+ if (oc->oc_allowed) {
+ charray_free (oc->oc_allowed);
+ }
+ oc->oc_required = charray_dup ( oc->oc_orig_required );
+ oc->oc_allowed = charray_dup ( oc->oc_orig_allowed );
+ charray_merge ( &(oc->oc_required), psuperior_oc->oc_required, 1 );
+ charray_merge ( &(oc->oc_allowed), psuperior_oc->oc_allowed, 1 );
+ oc_update_inheritance_nolock ( oc );
+ }
+ }
+}
diff --git a/ldap/servers/slapd/search.c b/ldap/servers/slapd/search.c
new file mode 100644
index 00000000..471d3157
--- /dev/null
+++ b/ldap/servers/slapd/search.c
@@ -0,0 +1,283 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+/*
+ * 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 <stdio.h>
+#include <string.h>
+#include <sys/types.h>
+#include "slap.h"
+#include "pratom.h"
+#include "snmp_collator.h"
+
+static void log_search_access (Slapi_PBlock *pb, const char *base, int scope, const char *filter, const char *msg);
+
+void
+do_search( Slapi_PBlock *pb )
+{
+ Slapi_Operation *operation;
+ BerElement *ber;
+ int i, err;
+ int scope, deref, attrsonly;
+ int sizelimit, timelimit;
+ long long_scope,long_deref,long_sizelimit,long_timelimit;
+ char *base = NULL, *fstr = NULL;
+ struct slapi_filter *filter = NULL;
+ char **attrs = NULL;
+ int psearch = 0;
+ struct berval *psbvp;
+ int changetypes;
+ int send_entchg_controls;
+ int changesonly = 0;
+ int rc = -1;
+ char *original_base = 0;
+ char *new_base = 0;
+
+ LDAPDebug( LDAP_DEBUG_TRACE, "do_search\n", 0, 0, 0 );
+
+ slapi_pblock_get( pb, SLAPI_OPERATION, &operation);
+ ber = operation->o_ber;
+
+ /* count the search request */
+ PR_AtomicIncrement(g_get_global_snmp_vars()->ops_tbl.dsSearchOps);
+
+ /*
+ * Parse 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
+ * }
+ */
+
+ /* baseObject, scope, derefAliases, sizelimit, timelimit, attrsOnly */
+ if ( ber_scanf( ber, "{aiiiib", &base, &long_scope, &long_deref, &long_sizelimit, &long_timelimit, &attrsonly ) == LBER_ERROR ){
+ slapi_ch_free((void**)&base );
+ log_search_access (pb, "???", -1, "???", "decoding error");
+ send_ldap_result( pb, LDAP_PROTOCOL_ERROR, NULL, NULL, 0, NULL );
+ return;
+ }
+ scope = long_scope;
+ deref = long_deref;
+ sizelimit = long_sizelimit;
+ timelimit = long_timelimit;
+
+ /*
+ * ignore negative time and size limits since they make no sense
+ */
+ if ( timelimit < 0 ) {
+ timelimit = 0;
+ }
+ if ( sizelimit < 0 ) {
+ sizelimit = 0;
+ }
+
+ if ( scope != LDAP_SCOPE_BASE && scope != LDAP_SCOPE_ONELEVEL
+ && scope != LDAP_SCOPE_SUBTREE ) {
+ log_search_access (pb, base, scope, "???", "Unknown search scope");
+ send_ldap_result( pb, LDAP_PROTOCOL_ERROR, NULL,
+ "Unknown search scope", 0, NULL );
+ goto free_and_return;
+ }
+ /* check and record the scope for snmp */
+ if ( scope == LDAP_SCOPE_ONELEVEL) {
+ /* count the one level search request */
+ PR_AtomicIncrement(g_get_global_snmp_vars()->ops_tbl.dsOneLevelSearchOps);
+
+ } else if (scope == LDAP_SCOPE_SUBTREE) {
+ /* count the subtree search request */
+ PR_AtomicIncrement(g_get_global_snmp_vars()->ops_tbl.dsWholeSubtreeSearchOps);
+ }
+
+ /* filter - returns a "normalized" version */
+ filter = NULL;
+ fstr = NULL;
+ if ( (err = get_filter( pb->pb_conn, ber, scope, &filter, &fstr )) != 0 ) {
+ char *errtxt;
+
+ if ( LDAP_UNWILLING_TO_PERFORM == err ) {
+ errtxt = "The search filter is too deeply nested";
+ } else {
+ errtxt = "Bad search filter";
+ }
+ log_search_access( pb, base, scope, "???", errtxt );
+ send_ldap_result( pb, err, NULL, errtxt, 0, NULL );
+ goto free_and_return;
+ }
+
+ /* attributes */
+ attrs = NULL;
+ if ( ber_scanf( ber, "{v}}", &attrs ) == LBER_ERROR ) {
+ log_search_access (pb, base, scope, fstr, "decoding error");
+ send_ldap_result( pb, LDAP_PROTOCOL_ERROR, NULL, NULL, 0,
+ NULL );
+ goto free_and_return;
+ }
+
+ /*
+ * This search is performed against the legacy consumer, so ask explicitly
+ * for the aci attribute as it is an operational in 5.0
+ */
+ if ( operation->o_flags & OP_FLAG_LEGACY_REPLICATION_DN )
+ {
+ /* If attrs==NULL in a 4.x request, means that we want all the attributes, as aci is
+ * now operational, we need to ask it explicilty as well as all the attributes
+ */
+ if ( (attrs == NULL) || (attrs[0] == NULL) )
+ {
+ charray_add(&attrs, slapi_attr_syntax_normalize("aci"));
+ charray_add(&attrs, slapi_attr_syntax_normalize(LDAP_ALL_USER_ATTRS));
+ }
+ else
+ {
+ for ( i = 0; attrs[i] != NULL; i++ )
+ {
+ if ( strcasecmp(attrs[i], LDAP_ALL_USER_ATTRS) == 0 )
+ {
+ charray_add(&attrs, slapi_attr_syntax_normalize("aci"));
+ break;
+ }
+ }
+ }
+ }
+
+ if ( attrs != NULL ) {
+ operation->o_searchattrs = cool_charray_dup( attrs );
+ for ( i = 0; attrs[i] != NULL; i++ ) {
+ char *type;
+
+ type = slapi_attr_syntax_normalize(attrs[i]);
+ slapi_ch_free( (void**)&(attrs[i]) );
+ attrs[i] = type;
+ }
+ }
+ if ( slapd_ldap_debug & LDAP_DEBUG_ARGS ) {
+ char abuf[ 1024 ], *astr;
+
+ if ( NULL == attrs ) {
+ astr = "ALL";
+ } else {
+ strarray2str( attrs, abuf, sizeof( abuf ), 1 /* include quotes */);
+ astr = abuf;
+ }
+ slapi_log_error( SLAPI_LOG_ARGS, NULL, "SRCH base=\"%s\" "
+ "scope=%d deref=%d "
+ "sizelimit=%d timelimit=%d attrsonly=%d filter=\"%s\" "
+ "attrs=%s\n", base, scope, deref, sizelimit, timelimit,
+ attrsonly, fstr, astr );
+ }
+
+ /*
+ * in LDAPv3 there can be optional control extensions on
+ * the end of an LDAPMessage. we need to read them in and
+ * pass them to the backend. get_ldapmessage_controls()
+ * reads the controls and sets any we know about in the pb.
+ */
+ if ( (err = get_ldapmessage_controls( pb, ber, NULL )) != 0 ) {
+ log_search_access (pb, base, scope, fstr, "failed to decode LDAP controls");
+ send_ldap_result( pb, err, NULL, NULL, 0, NULL );
+ goto free_and_return;
+ }
+
+ /* we support persistent search for regular operations only */
+ if ( slapi_control_present( operation->o_params.request_controls,
+ LDAP_CONTROL_PERSISTENTSEARCH, &psbvp, NULL )){
+ operation_set_flag(operation, OP_FLAG_PS);
+ psearch = 1;
+ if ( ps_parse_control_value( psbvp, &changetypes,
+ &changesonly, &send_entchg_controls ) != LDAP_SUCCESS )
+ {
+ changetypes = LDAP_CHANGETYPE_ANY;
+ send_entchg_controls = 0;
+ }
+ else if ( changesonly )
+ {
+ operation_set_flag(operation, OP_FLAG_PS_CHANGESONLY);
+ }
+ }
+
+ slapi_pblock_set( pb, SLAPI_SEARCH_TARGET, base );
+ slapi_pblock_set( pb, SLAPI_SEARCH_SCOPE, &scope );
+ slapi_pblock_set( pb, SLAPI_SEARCH_DEREF, &deref );
+ slapi_pblock_set( pb, SLAPI_SEARCH_FILTER, filter );
+ slapi_pblock_set( pb, SLAPI_SEARCH_STRFILTER, fstr );
+ slapi_pblock_set( pb, SLAPI_SEARCH_ATTRS, attrs );
+ slapi_pblock_set( pb, SLAPI_SEARCH_ATTRSONLY, &attrsonly );
+ slapi_pblock_set( pb, SLAPI_REQUESTOR_ISROOT, &operation->o_isroot );
+ slapi_pblock_set( pb, SLAPI_SEARCH_SIZELIMIT, &sizelimit );
+ slapi_pblock_set( pb, SLAPI_SEARCH_TIMELIMIT, &timelimit );
+
+ /* plugins which play with the search may
+ * change the search params may allocate
+ * memory so we need to keep track of
+ * changed base search strings
+ */
+ slapi_pblock_get(pb, SLAPI_SEARCH_TARGET, &original_base);
+
+ op_shared_search (pb, psearch ? 0 : 1/* send result */);
+
+ slapi_pblock_get(pb, SLAPI_SEARCH_TARGET, &new_base);
+ slapi_pblock_get (pb, SLAPI_PLUGIN_OPRETURN, &rc);
+ slapi_pblock_get( pb, SLAPI_SEARCH_FILTER, &filter );
+
+ if ( psearch && rc == 0 ) {
+ ps_add( pb, changetypes, send_entchg_controls );
+ }
+
+free_and_return:;
+ if ( !psearch || rc < 0 ) {
+ if(original_base != new_base) {
+ slapi_ch_free_string(&new_base);
+ }
+ slapi_ch_free_string(&base);
+ slapi_ch_free_string(&fstr);
+ slapi_filter_free( filter, 1 );
+ if ( attrs != NULL ) {
+ charray_free( attrs );
+ }
+ /*
+ * Fix for defect 526719 / 553356 : Persistent search op failed.
+ * Marking it as non-persistent so that operation resources get freed
+ */
+ if (psearch){
+ operation->o_flags &= ~OP_FLAG_PS;
+ }
+ }
+}
+
+static void log_search_access (Slapi_PBlock *pb, const char *base, int scope, const char *fstr, const char *msg)
+{
+ char ebuf[BUFSIZ];
+ slapi_log_access(LDAP_DEBUG_STATS,
+ "conn=%d op=%d SRCH base=\"%s\" scope=%d filter=\"%s\", %s\n",
+ pb->pb_conn->c_connid, pb->pb_op->o_opid,
+ escape_string(base, ebuf), scope, fstr, msg ? msg : "");
+
+}
diff --git a/ldap/servers/slapd/secerrstrs.h b/ldap/servers/slapd/secerrstrs.h
new file mode 100644
index 00000000..05be8170
--- /dev/null
+++ b/ldap/servers/slapd/secerrstrs.h
@@ -0,0 +1,431 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+/*
+ * secerrstrs.h - map security errors to strings (used by errormap.c)
+ *
+ */
+
+/*
+ ****************************************************************************
+ * The code below this point was provided by Nelson Bolyard <nelsonb> of the
+ * Netscape Certificate Server team on 27-March-1998.
+ * Taken from the file ns/security/cmd/lib/SECerrs.h on NSS_1_BRANCH.
+ * Last updated from there: 24-July-1998 by Mark Smith <mcs>
+ *
+ * All of the Directory Server specific changes are enclosed inside
+ * #ifdef NS_DS.
+ ****************************************************************************
+ */
+
+/* General security error codes */
+/* Caller must #include "secerr.h" */
+
+
+ER3(SEC_ERROR_IO, SEC_ERROR_BASE + 0,
+"An I/O error occurred during security authorization.")
+
+ER3(SEC_ERROR_LIBRARY_FAILURE, SEC_ERROR_BASE + 1,
+"security library failure.")
+
+ER3(SEC_ERROR_BAD_DATA, SEC_ERROR_BASE + 2,
+"security library: received bad data.")
+
+ER3(SEC_ERROR_OUTPUT_LEN, SEC_ERROR_BASE + 3,
+"security library: output length error.")
+
+ER3(SEC_ERROR_INPUT_LEN, SEC_ERROR_BASE + 4,
+"security library has experienced an input length error.")
+
+ER3(SEC_ERROR_INVALID_ARGS, SEC_ERROR_BASE + 5,
+"security library: invalid arguments.")
+
+ER3(SEC_ERROR_INVALID_ALGORITHM, SEC_ERROR_BASE + 6,
+"security library: invalid algorithm.")
+
+ER3(SEC_ERROR_INVALID_AVA, SEC_ERROR_BASE + 7,
+"security library: invalid AVA.")
+
+ER3(SEC_ERROR_INVALID_TIME, SEC_ERROR_BASE + 8,
+"Improperly formatted time string.")
+
+ER3(SEC_ERROR_BAD_DER, SEC_ERROR_BASE + 9,
+"security library: improperly formatted DER-encoded message.")
+
+ER3(SEC_ERROR_BAD_SIGNATURE, SEC_ERROR_BASE + 10,
+"Peer's certificate has an invalid signature.")
+
+ER3(SEC_ERROR_EXPIRED_CERTIFICATE, SEC_ERROR_BASE + 11,
+"Peer's Certificate has expired.")
+
+ER3(SEC_ERROR_REVOKED_CERTIFICATE, SEC_ERROR_BASE + 12,
+"Peer's Certificate has been revoked.")
+
+ER3(SEC_ERROR_UNKNOWN_ISSUER, SEC_ERROR_BASE + 13,
+"Peer's Certificate issuer is not recognized.")
+
+ER3(SEC_ERROR_BAD_KEY, SEC_ERROR_BASE + 14,
+"Peer's public key is invalid.")
+
+ER3(SEC_ERROR_BAD_PASSWORD, SEC_ERROR_BASE + 15,
+"The security password entered is incorrect.")
+
+ER3(SEC_ERROR_RETRY_PASSWORD, SEC_ERROR_BASE + 16,
+"New password entered incorrectly. Please try again.")
+
+ER3(SEC_ERROR_NO_NODELOCK, SEC_ERROR_BASE + 17,
+"security library: no nodelock.")
+
+ER3(SEC_ERROR_BAD_DATABASE, SEC_ERROR_BASE + 18,
+"security library: bad database.")
+
+ER3(SEC_ERROR_NO_MEMORY, SEC_ERROR_BASE + 19,
+"security library: memory allocation failure.")
+
+ER3(SEC_ERROR_UNTRUSTED_ISSUER, SEC_ERROR_BASE + 20,
+"Peer's certificate issuer has been marked as not trusted by the user.")
+
+ER3(SEC_ERROR_UNTRUSTED_CERT, SEC_ERROR_BASE + 21,
+"Peer's certificate has been marked as not trusted by the user.")
+
+ER3(SEC_ERROR_DUPLICATE_CERT, (SEC_ERROR_BASE + 22),
+"Certificate already exists in your database.")
+
+ER3(SEC_ERROR_DUPLICATE_CERT_NAME, (SEC_ERROR_BASE + 23),
+"Downloaded certificate's name duplicates one already in your database.")
+
+ER3(SEC_ERROR_ADDING_CERT, (SEC_ERROR_BASE + 24),
+"Error adding certificate to database.")
+
+ER3(SEC_ERROR_FILING_KEY, (SEC_ERROR_BASE + 25),
+"Error refiling the key for this certificate.")
+
+ER3(SEC_ERROR_NO_KEY, (SEC_ERROR_BASE + 26),
+"The private key for this certificate cannot be found in key database")
+
+ER3(SEC_ERROR_CERT_VALID, (SEC_ERROR_BASE + 27),
+"This certificate is valid.")
+
+ER3(SEC_ERROR_CERT_NOT_VALID, (SEC_ERROR_BASE + 28),
+"This certificate is not valid.")
+
+ER3(SEC_ERROR_CERT_NO_RESPONSE, (SEC_ERROR_BASE + 29),
+"Cert Library: No Response")
+
+ER3(SEC_ERROR_EXPIRED_ISSUER_CERTIFICATE, (SEC_ERROR_BASE + 30),
+"The certificate issuer's certificate has expired. Check your system date and time.")
+
+ER3(SEC_ERROR_CRL_EXPIRED, (SEC_ERROR_BASE + 31),
+"The CRL for the certificate's issuer has expired. Update it or check your system data and time.")
+
+ER3(SEC_ERROR_CRL_BAD_SIGNATURE, (SEC_ERROR_BASE + 32),
+"The CRL for the certificate's issuer has an invalid signature.")
+
+ER3(SEC_ERROR_CRL_INVALID, (SEC_ERROR_BASE + 33),
+"New CRL has an invalid format.")
+
+ER3(SEC_ERROR_EXTENSION_VALUE_INVALID, (SEC_ERROR_BASE + 34),
+"Certificate extension value is invalid.")
+
+ER3(SEC_ERROR_EXTENSION_NOT_FOUND, (SEC_ERROR_BASE + 35),
+"Certificate extension not found.")
+
+ER3(SEC_ERROR_CA_CERT_INVALID, (SEC_ERROR_BASE + 36),
+"Issuer certificate is invalid.")
+
+ER3(SEC_ERROR_PATH_LEN_CONSTRAINT_INVALID, (SEC_ERROR_BASE + 37),
+"Certificate path length constraint is invalid.")
+
+ER3(SEC_ERROR_CERT_USAGES_INVALID, (SEC_ERROR_BASE + 38),
+"Certificate usages field is invalid.")
+
+ER3(SEC_INTERNAL_ONLY, (SEC_ERROR_BASE + 39),
+"**Internal ONLY module**")
+
+ER3(SEC_ERROR_INVALID_KEY, (SEC_ERROR_BASE + 40),
+"The key does not support the requested operation.")
+
+ER3(SEC_ERROR_UNKNOWN_CRITICAL_EXTENSION, (SEC_ERROR_BASE + 41),
+"Certificate contains unknown critical extension.")
+
+ER3(SEC_ERROR_OLD_CRL, (SEC_ERROR_BASE + 42),
+"New CRL is not later than the current one.")
+
+ER3(SEC_ERROR_NO_EMAIL_CERT, (SEC_ERROR_BASE + 43),
+"Not encrypted or signed: you do not yet have an email certificate.")
+
+ER3(SEC_ERROR_NO_RECIPIENT_CERTS_QUERY, (SEC_ERROR_BASE + 44),
+"Not encrypted: you do not have certificates for each of the recipients.")
+
+ER3(SEC_ERROR_NOT_A_RECIPIENT, (SEC_ERROR_BASE + 45),
+"Cannot decrypt: you are not a recipient, or matching certificate and \
+private key not found.")
+
+ER3(SEC_ERROR_PKCS7_KEYALG_MISMATCH, (SEC_ERROR_BASE + 46),
+"Cannot decrypt: key encryption algorithm does not match your certificate.")
+
+ER3(SEC_ERROR_PKCS7_BAD_SIGNATURE, (SEC_ERROR_BASE + 47),
+"Signature verification failed: no signer found, too many signers found, \
+or improper or corrupted data.")
+
+ER3(SEC_ERROR_UNSUPPORTED_KEYALG, (SEC_ERROR_BASE + 48),
+"Unsupported or unknown key algorithm.")
+
+ER3(SEC_ERROR_DECRYPTION_DISALLOWED, (SEC_ERROR_BASE + 49),
+"Cannot decrypt: encrypted using a disallowed algorithm or key size.")
+
+
+/* Fortezza Alerts */
+ER3(XP_SEC_FORTEZZA_BAD_CARD, (SEC_ERROR_BASE + 50),
+"Fortezza card has not been properly initialized. \
+Please remove it and return it to your issuer.")
+
+ER3(XP_SEC_FORTEZZA_NO_CARD, (SEC_ERROR_BASE + 51),
+"No Fortezza cards Found")
+
+ER3(XP_SEC_FORTEZZA_NONE_SELECTED, (SEC_ERROR_BASE + 52),
+"No Fortezza card selected")
+
+ER3(XP_SEC_FORTEZZA_MORE_INFO, (SEC_ERROR_BASE + 53),
+"Please select a personality to get more info on")
+
+ER3(XP_SEC_FORTEZZA_PERSON_NOT_FOUND, (SEC_ERROR_BASE + 54),
+"Personality not found")
+
+ER3(XP_SEC_FORTEZZA_NO_MORE_INFO, (SEC_ERROR_BASE + 55),
+"No more information on that Personality")
+
+ER3(XP_SEC_FORTEZZA_BAD_PIN, (SEC_ERROR_BASE + 56),
+"Invalid Pin")
+
+ER3(XP_SEC_FORTEZZA_PERSON_ERROR, (SEC_ERROR_BASE + 57),
+"Couldn't initialize Fortezza personalities.")
+/* end fortezza alerts. */
+
+ER3(SEC_ERROR_NO_KRL, (SEC_ERROR_BASE + 58),
+"No KRL for this site's certificate has been found.")
+
+ER3(SEC_ERROR_KRL_EXPIRED, (SEC_ERROR_BASE + 59),
+"The KRL for this site's certificate has expired.")
+
+ER3(SEC_ERROR_KRL_BAD_SIGNATURE, (SEC_ERROR_BASE + 60),
+"The KRL for this site's certificate has an invalid signature.")
+
+ER3(SEC_ERROR_REVOKED_KEY, (SEC_ERROR_BASE + 61),
+"The key for this site's certificate has been revoked.")
+
+ER3(SEC_ERROR_KRL_INVALID, (SEC_ERROR_BASE + 62),
+"New KRL has an invalid format.")
+
+ER3(SEC_ERROR_NEED_RANDOM, (SEC_ERROR_BASE + 63),
+"security library: need random data.")
+
+ER3(SEC_ERROR_NO_MODULE, (SEC_ERROR_BASE + 64),
+"security library: no security module can perform the requested operation.")
+
+ER3(SEC_ERROR_NO_TOKEN, (SEC_ERROR_BASE + 65),
+"The security card or token does not exist, needs to be initialized, or has been removed.")
+
+ER3(SEC_ERROR_READ_ONLY, (SEC_ERROR_BASE + 66),
+"security library: read-only database.")
+
+ER3(SEC_ERROR_NO_SLOT_SELECTED, (SEC_ERROR_BASE + 67),
+"No slot or token was selected.")
+
+ER3(SEC_ERROR_CERT_NICKNAME_COLLISION, (SEC_ERROR_BASE + 68),
+"A certificate with the same nickname already exists.")
+
+ER3(SEC_ERROR_KEY_NICKNAME_COLLISION, (SEC_ERROR_BASE + 69),
+"A key with the same nickname already exists.")
+
+ER3(SEC_ERROR_SAFE_NOT_CREATED, (SEC_ERROR_BASE + 70),
+"error while creating safe object")
+
+ER3(SEC_ERROR_BAGGAGE_NOT_CREATED, (SEC_ERROR_BASE + 71),
+"error while creating baggage object")
+
+ER3(XP_JAVA_REMOVE_PRINCIPAL_ERROR, (SEC_ERROR_BASE + 72),
+"Couldn't remove the principal")
+
+ER3(XP_JAVA_DELETE_PRIVILEGE_ERROR, (SEC_ERROR_BASE + 73),
+"Couldn't delete the privilege")
+
+ER3(XP_JAVA_CERT_NOT_EXISTS_ERROR, (SEC_ERROR_BASE + 74),
+"This principal doesn't have a certificate")
+
+ER3(SEC_ERROR_BAD_EXPORT_ALGORITHM, (SEC_ERROR_BASE + 75),
+"Required algorithm is not allowed.")
+
+ER3(SEC_ERROR_EXPORTING_CERTIFICATES, (SEC_ERROR_BASE + 76),
+"Error attempting to export certificates.")
+
+ER3(SEC_ERROR_IMPORTING_CERTIFICATES, (SEC_ERROR_BASE + 77),
+"Error attempting to import certificates.")
+
+ER3(SEC_ERROR_PKCS12_DECODING_PFX, (SEC_ERROR_BASE + 78),
+"Unable to import. Decoding error. File not valid.")
+
+ER3(SEC_ERROR_PKCS12_INVALID_MAC, (SEC_ERROR_BASE + 79),
+"Unable to import. Invalid MAC. Incorrect password or corrupt file.")
+
+ER3(SEC_ERROR_PKCS12_UNSUPPORTED_MAC_ALGORITHM, (SEC_ERROR_BASE + 80),
+"Unable to import. MAC algorithm not supported.")
+
+ER3(SEC_ERROR_PKCS12_UNSUPPORTED_TRANSPORT_MODE,(SEC_ERROR_BASE + 81),
+"Unable to import. Only password integrity and privacy modes supported.")
+
+ER3(SEC_ERROR_PKCS12_CORRUPT_PFX_STRUCTURE, (SEC_ERROR_BASE + 82),
+"Unable to import. File structure is corrupt.")
+
+ER3(SEC_ERROR_PKCS12_UNSUPPORTED_PBE_ALGORITHM, (SEC_ERROR_BASE + 83),
+"Unable to import. Encryption algorithm not supported.")
+
+ER3(SEC_ERROR_PKCS12_UNSUPPORTED_VERSION, (SEC_ERROR_BASE + 84),
+"Unable to import. File version not supported.")
+
+ER3(SEC_ERROR_PKCS12_PRIVACY_PASSWORD_INCORRECT,(SEC_ERROR_BASE + 85),
+"Unable to import. Incorrect privacy password.")
+
+ER3(SEC_ERROR_PKCS12_CERT_COLLISION, (SEC_ERROR_BASE + 86),
+"Unable to import. Same nickname already exists in database.")
+
+ER3(SEC_ERROR_USER_CANCELLED, (SEC_ERROR_BASE + 87),
+"The user pressed cancel.")
+
+ER3(SEC_ERROR_PKCS12_DUPLICATE_DATA, (SEC_ERROR_BASE + 88),
+"Not imported, already in database.")
+
+ER3(SEC_ERROR_MESSAGE_SEND_ABORTED, (SEC_ERROR_BASE + 89),
+"Message not sent.")
+
+ER3(SEC_ERROR_INADEQUATE_KEY_USAGE, (SEC_ERROR_BASE + 90),
+"Certificate key usage inadequate for attempted operation.")
+
+ER3(SEC_ERROR_INADEQUATE_CERT_TYPE, (SEC_ERROR_BASE + 91),
+"Certificate type not approved for application.")
+
+ER3(SEC_ERROR_CERT_ADDR_MISMATCH, (SEC_ERROR_BASE + 92),
+"Address in signing certificate does not match address in message headers.")
+
+ER3(SEC_ERROR_PKCS12_UNABLE_TO_IMPORT_KEY, (SEC_ERROR_BASE + 93),
+"Unable to import. Error attempting to import private key.")
+
+ER3(SEC_ERROR_PKCS12_IMPORTING_CERT_CHAIN, (SEC_ERROR_BASE + 94),
+"Unable to import. Error attempting to import certificate chain.")
+
+ER3(SEC_ERROR_PKCS12_UNABLE_TO_LOCATE_OBJECT_BY_NAME, (SEC_ERROR_BASE + 95),
+"Unable to export. Unable to locate certificate or key by nickname.")
+
+ER3(SEC_ERROR_PKCS12_UNABLE_TO_EXPORT_KEY, (SEC_ERROR_BASE + 96),
+"Unable to export. Private Key could not be located and exported.")
+
+ER3(SEC_ERROR_PKCS12_UNABLE_TO_WRITE, (SEC_ERROR_BASE + 97),
+"Unable to export. Unable to write the export file.")
+
+ER3(SEC_ERROR_PKCS12_UNABLE_TO_READ, (SEC_ERROR_BASE + 98),
+"Unable to import. Unable to read the import file.")
+
+ER3(SEC_ERROR_PKCS12_KEY_DATABASE_NOT_INITIALIZED, (SEC_ERROR_BASE + 99),
+"Unable to export. Key database corrupt or deleted.")
+
+ER3(SEC_ERROR_KEYGEN_FAIL, (SEC_ERROR_BASE + 100),
+"Unable to generate public/private key pair.")
+
+ER3(SEC_ERROR_INVALID_PASSWORD, (SEC_ERROR_BASE + 101),
+"Password entered is invalid. Please pick a different one.")
+
+ER3(SEC_ERROR_RETRY_OLD_PASSWORD, (SEC_ERROR_BASE + 102),
+"Old password entered incorrectly. Please try again.")
+
+ER3(SEC_ERROR_BAD_NICKNAME, (SEC_ERROR_BASE + 103),
+"Certificate nickname already in use.")
+
+ER3(SEC_ERROR_NOT_FORTEZZA_ISSUER, (SEC_ERROR_BASE + 104),
+"Peer FORTEZZA chain has a non-FORTEZZA Certificate.")
+
+/* ER3(SEC_ERROR_UNKNOWN, (SEC_ERROR_BASE + 105), */
+
+ER3(SEC_ERROR_JS_INVALID_MODULE_NAME, (SEC_ERROR_BASE + 106),
+"Invalid module name.")
+
+ER3(SEC_ERROR_JS_INVALID_DLL, (SEC_ERROR_BASE + 107),
+"Invalid module path/filename")
+
+ER3(SEC_ERROR_JS_ADD_MOD_FAILURE, (SEC_ERROR_BASE + 108),
+"Unable to add module")
+
+ER3(SEC_ERROR_JS_DEL_MOD_FAILURE, (SEC_ERROR_BASE + 109),
+"Unable to delete module")
+
+ER3(SEC_ERROR_OLD_KRL, (SEC_ERROR_BASE + 110),
+"New KRL is not later than the current one.")
+
+ER3(SEC_ERROR_CKL_CONFLICT, (SEC_ERROR_BASE + 111),
+"New CKL has different issuer than current CKL. Delete current CKL.")
+
+ER3(SEC_ERROR_CERT_NOT_IN_NAME_SPACE, (SEC_ERROR_BASE + 112),
+"The Certifying Authority for this certificate is not permitted to issue a \
+certificate with this name.")
+
+ER3(SEC_ERROR_KRL_NOT_YET_VALID, (SEC_ERROR_BASE + 113),
+"The key revocation list for this certificate is not yet valid.")
+
+ER3(SEC_ERROR_CRL_NOT_YET_VALID, (SEC_ERROR_BASE + 114),
+"The certificate revocation list for this certificate is not yet valid.")
+
+ER3(SEC_ERROR_UNKNOWN_CERT, (SEC_ERROR_BASE + 115),
+"The requested certificate could not be found.")
+
+ER3(SEC_ERROR_UNKNOWN_SIGNER, (SEC_ERROR_BASE + 116),
+"The signer's certificate could not be found.")
+
+ER3(SEC_ERROR_CERT_BAD_ACCESS_LOCATION, (SEC_ERROR_BASE + 117),
+"The location for the certificate status server has invalid format.")
+
+ER3(SEC_ERROR_OCSP_UNKNOWN_RESPONSE_TYPE, (SEC_ERROR_BASE + 118),
+"The OCSP response cannot be fully decoded; it is of an unknown type.")
+
+ER3(SEC_ERROR_OCSP_BAD_HTTP_RESPONSE, (SEC_ERROR_BASE + 119),
+"The OCSP server returned unexpected/invalid HTTP data.")
+
+ER3(SEC_ERROR_OCSP_MALFORMED_REQUEST, (SEC_ERROR_BASE + 120),
+"The OCSP server found the request to be corrupted or improperly formed.")
+
+ER3(SEC_ERROR_OCSP_SERVER_ERROR, (SEC_ERROR_BASE + 121),
+"The OCSP server experienced an internal error.")
+
+ER3(SEC_ERROR_OCSP_TRY_SERVER_LATER, (SEC_ERROR_BASE + 122),
+"The OCSP server suggests trying again later.")
+
+ER3(SEC_ERROR_OCSP_REQUEST_NEEDS_SIG, (SEC_ERROR_BASE + 123),
+"The OCSP server requires a signature on this request.")
+
+ER3(SEC_ERROR_OCSP_UNAUTHORIZED_REQUEST, (SEC_ERROR_BASE + 124),
+"The OCSP server has refused this request as unauthorized.")
+
+ER3(SEC_ERROR_OCSP_UNKNOWN_RESPONSE_STATUS, (SEC_ERROR_BASE + 125),
+"The OCSP server returned an unrecognizable status.")
+
+ER3(SEC_ERROR_OCSP_UNKNOWN_CERT, (SEC_ERROR_BASE + 126),
+"The OCSP server has no status for the certificate.")
+
+ER3(SEC_ERROR_OCSP_NOT_ENABLED, (SEC_ERROR_BASE + 127),
+"You must enable OCSP before performing this operation.")
+
+ER3(SEC_ERROR_OCSP_NO_DEFAULT_RESPONDER, (SEC_ERROR_BASE + 128),
+"You must set the OCSP default responder before performing this operation.")
+
+ER3(SEC_ERROR_OCSP_MALFORMED_RESPONSE, (SEC_ERROR_BASE + 129),
+"The response from the OCSP server was corrupted or improperly formed.")
+
+ER3(SEC_ERROR_OCSP_UNAUTHORIZED_RESPONSE, (SEC_ERROR_BASE + 130),
+"The signer of the OCSP response is not authorized to give status for \
+this certificate.")
+
+ER3(SEC_ERROR_OCSP_FUTURE_RESPONSE, (SEC_ERROR_BASE + 131),
+"The OCSP response is not yet valid (contains a date in the future).")
+
+ER3(SEC_ERROR_OCSP_OLD_RESPONSE, (SEC_ERROR_BASE + 132),
+"The OCSP response contains out-of-date information.")
diff --git a/ldap/servers/slapd/security_wrappers.c b/ldap/servers/slapd/security_wrappers.c
new file mode 100644
index 00000000..d4b898d8
--- /dev/null
+++ b/ldap/servers/slapd/security_wrappers.c
@@ -0,0 +1,339 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+#include <ssl.h>
+#include <nss.h>
+#include <key.h>
+#include <certdb.h>
+#include <cert.h>
+#include <sslproto.h>
+#include <secoid.h>
+#include <secmod.h>
+#include <secmodt.h>
+#include <prtypes.h>
+#include <seccomon.h>
+#include <pkcs11.h>
+#include <pk11func.h>
+
+#include "slap.h"
+
+
+
+/*
+ * Set of security wrapper functions, aimed at forcing every security-related call
+ * pass through libslapd library.
+ * Thus, we avoid finding any ambiguity or data inconsistency (which we would,
+ * otherwise, have to face on NT.
+ */
+
+
+/*
+ * Note: slapd_ssl_handshakeCallback() is called with a Connection * in
+ * client_data. That connection must have its c_mutex locked.
+ */
+int
+slapd_ssl_handshakeCallback(PRFileDesc *fd, void * callback, void * client_data)
+{
+ return SSL_HandshakeCallback(fd, (SSLHandshakeCallback) callback, client_data);
+}
+
+
+/*
+ * Note: slapd_ssl_badCertHook() is called with a Connection * in
+ * client_data. That connection must have its c_mutex locked.
+ */
+int
+slapd_ssl_badCertHook(PRFileDesc *fd, void * callback, void * client_data)
+{
+ return SSL_BadCertHook(fd, (SSLBadCertHandler) callback, client_data);
+}
+
+
+CERTCertificate *
+slapd_ssl_peerCertificate(PRFileDesc *fd)
+{
+ return SSL_PeerCertificate(fd);
+}
+
+
+SECStatus
+slapd_ssl_getChannelInfo(PRFileDesc *fd, SSLChannelInfo *sinfo, PRUintn len)
+{
+ return SSL_GetChannelInfo(fd, sinfo, len);
+}
+
+
+SECStatus
+slapd_ssl_getCipherSuiteInfo(PRUint16 ciphersuite, SSLCipherSuiteInfo *cinfo, PRUintn len)
+{
+ return SSL_GetCipherSuiteInfo(ciphersuite, cinfo, len);
+}
+
+PRFileDesc *
+slapd_ssl_importFD(PRFileDesc *model, PRFileDesc *fd)
+{
+ return SSL_ImportFD(model, fd);
+}
+
+
+SECStatus
+slapd_ssl_resetHandshake(PRFileDesc *fd, PRBool asServer)
+{
+ return SSL_ResetHandshake(fd, asServer);
+}
+
+
+void
+slapd_pk11_configurePKCS11(char *man, char *libdes, char *tokdes, char *ptokdes,
+ char *slotdes, char *pslotdes, char *fslotdes,
+ char *fpslotdes, int minPwd,
+ int pwdRequired)
+{
+ PK11_ConfigurePKCS11(man, libdes, tokdes, ptokdes,
+ slotdes, pslotdes, fslotdes, fpslotdes, minPwd,
+ pwdRequired);
+ return;
+}
+
+
+void
+slapd_pk11_freeSlot(PK11SlotInfo *slot)
+{
+ PK11_FreeSlot(slot);
+ return;
+}
+
+
+void
+slapd_pk11_freeSymKey(PK11SymKey *key)
+{
+ PK11_FreeSymKey(key);
+ return;
+}
+
+
+PK11SlotInfo *
+slapd_pk11_findSlotByName(char *name)
+{
+ return PK11_FindSlotByName(name);
+}
+
+
+SECAlgorithmID *
+slapd_pk11_createPBEAlgorithmID(SECOidTag algorithm, int iteration, SECItem *salt)
+{
+ return PK11_CreatePBEAlgorithmID(algorithm, iteration, salt);
+}
+
+
+PK11SymKey *
+slapd_pk11_pbeKeyGen(PK11SlotInfo *slot, SECAlgorithmID *algid, SECItem *pwitem,
+ PRBool faulty3DES, void *wincx)
+{
+ return PK11_PBEKeyGen(slot, algid, pwitem,
+ faulty3DES, wincx);
+}
+
+
+CK_MECHANISM_TYPE
+slapd_pk11_algtagToMechanism(SECOidTag algTag)
+{
+ return PK11_AlgtagToMechanism(algTag);
+}
+
+
+SECItem *
+slapd_pk11_paramFromAlgid(SECAlgorithmID *algid)
+{
+ return PK11_ParamFromAlgid(algid);
+}
+
+
+CK_RV
+slapd_pk11_mapPBEMechanismToCryptoMechanism(CK_MECHANISM_PTR pPBEMechanism,
+ CK_MECHANISM_PTR pCryptoMechanism,
+ SECItem *pbe_pwd, PRBool bad3DES)
+{
+ return PK11_MapPBEMechanismToCryptoMechanism(pPBEMechanism,
+ pCryptoMechanism,
+ pbe_pwd, bad3DES);
+}
+
+
+int
+slapd_pk11_getBlockSize(CK_MECHANISM_TYPE type,SECItem *params)
+{
+ return PK11_GetBlockSize(type,params);
+}
+
+
+PK11Context *
+slapd_pk11_createContextBySymKey(CK_MECHANISM_TYPE type,
+ CK_ATTRIBUTE_TYPE operation,
+ PK11SymKey *symKey, SECItem *param)
+{
+ return PK11_CreateContextBySymKey(type,
+ operation, symKey, param);
+}
+
+
+SECStatus
+slapd_pk11_cipherOp(PK11Context *context, unsigned char * out, int *outlen,
+ int maxout, unsigned char *in, int inlen)
+{
+ return PK11_CipherOp(context, out, outlen, maxout, in, inlen);
+}
+
+
+SECStatus
+slapd_pk11_finalize(PK11Context *context)
+{
+ return PK11_Finalize(context);
+}
+
+
+PK11SlotInfo *
+slapd_pk11_getInternalKeySlot()
+{
+ return PK11_GetInternalKeySlot();
+}
+
+
+PK11SlotInfo *
+slapd_pk11_getInternalSlot()
+{
+ return PK11_GetInternalSlot();
+}
+
+
+SECStatus
+slapd_pk11_authenticate(PK11SlotInfo *slot, PRBool loadCerts, void *wincx)
+{
+ return PK11_Authenticate(slot, loadCerts, wincx);
+}
+
+
+void
+slapd_pk11_setSlotPWValues(PK11SlotInfo *slot,int askpw, int timeout)
+{
+ PK11_SetSlotPWValues(slot, askpw, timeout);
+ return;
+}
+
+
+PRBool
+slapd_pk11_isFIPS()
+{
+ return PK11_IsFIPS();
+}
+
+
+CERTCertificate *
+slapd_pk11_findCertFromNickname(char *nickname, void *wincx)
+{
+ return PK11_FindCertFromNickname(nickname, wincx);
+}
+
+
+SECKEYPrivateKey *
+slapd_pk11_findKeyByAnyCert(CERTCertificate *cert, void *wincx)
+{
+ return PK11_FindKeyByAnyCert(cert, wincx);
+}
+
+
+PRBool
+slapd_pk11_fortezzaHasKEA(CERTCertificate *cert)
+{
+ return PK11_FortezzaHasKEA(cert);
+}
+
+void
+slapd_pk11_destroyContext(PK11Context *context, PRBool freeit)
+{
+ PK11_DestroyContext(context, freeit);
+}
+
+void secoid_destroyAlgorithmID(SECAlgorithmID *algid, PRBool freeit)
+{
+ SECOID_DestroyAlgorithmID(algid, freeit);
+}
+
+void slapd_pk11_CERT_DestroyCertificate(CERTCertificate *cert)
+{
+ CERT_DestroyCertificate(cert);
+}
+
+SECKEYPublicKey *slapd_CERT_ExtractPublicKey(CERTCertificate *cert)
+{
+ return CERT_ExtractPublicKey(cert);
+}
+
+SECKEYPrivateKey * slapd_pk11_FindPrivateKeyFromCert(PK11SlotInfo *slot,CERTCertificate *cert, void *wincx)
+{
+ return PK11_FindPrivateKeyFromCert(slot,cert,wincx);
+}
+
+PK11SlotInfo *slapd_pk11_GetInternalKeySlot(void)
+{
+ return PK11_GetInternalKeySlot();
+}
+
+SECStatus slapd_pk11_PubWrapSymKey(CK_MECHANISM_TYPE type, SECKEYPublicKey *pubKey,PK11SymKey *symKey, SECItem *wrappedKey)
+{
+ return PK11_PubWrapSymKey(type,pubKey,symKey,wrappedKey);
+}
+
+PK11SymKey *slapd_pk11_KeyGen(PK11SlotInfo *slot,CK_MECHANISM_TYPE type,SECItem *param, int keySize,void *wincx)
+{
+ return PK11_KeyGen(slot,type,param,keySize,wincx);
+}
+
+void slapd_pk11_FreeSlot(PK11SlotInfo *slot)
+{
+ PK11_FreeSlot(slot);
+}
+
+void slapd_pk11_FreeSymKey(PK11SymKey *key)
+{
+ PK11_FreeSymKey(key);
+}
+
+void slapd_pk11_DestroyContext(PK11Context *context, PRBool freeit)
+{
+ PK11_DestroyContext(context,freeit);
+}
+
+SECItem *slapd_pk11_ParamFromIV(CK_MECHANISM_TYPE type,SECItem *iv)
+{
+ return PK11_ParamFromIV(type,iv);
+}
+
+PK11SymKey *slapd_pk11_PubUnwrapSymKey(SECKEYPrivateKey *wrappingKey, SECItem *wrappedKey,CK_MECHANISM_TYPE target, CK_ATTRIBUTE_TYPE operation, int keySize)
+{
+ return PK11_PubUnwrapSymKey(wrappingKey,wrappedKey,target,operation,keySize);
+}
+
+unsigned slapd_SECKEY_PublicKeyStrength(SECKEYPublicKey *pubk)
+{
+ return SECKEY_PublicKeyStrength(pubk);
+}
+
+SECStatus slapd_pk11_Finalize(PK11Context *context)
+{
+ return PK11_Finalize(context);
+}
+
+SECStatus slapd_pk11_DigestFinal(PK11Context *context, unsigned char *data,unsigned int *outLen, unsigned int length)
+{
+ return PK11_DigestFinal(context, data, outLen, length);
+}
+
+void
+slapd_SECITEM_FreeItem (SECItem *zap, PRBool freeit)
+{
+ SECITEM_FreeItem(zap,freeit);
+}
+
diff --git a/ldap/servers/slapd/slap.h b/ldap/servers/slapd/slap.h
new file mode 100644
index 00000000..ad1ccdda
--- /dev/null
+++ b/ldap/servers/slapd/slap.h
@@ -0,0 +1,1944 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+/* slap.h - stand alone ldap server include file */
+
+#ifndef _SLDAPD_H_
+#define _SLDAPD_H_
+
+
+/* Used by SSL and DES plugin */
+#ifndef _TOK
+#define _TOK
+static char tokDes[34] = "Communicator Generic Crypto Svcs";
+static char ptokDes[34] = "Internal (Software) Token ";
+#endif
+
+/*
+ * The slapd executable can function in on of several modes.
+ */
+#define SLAPD_EXEMODE_UNKNOWN 0
+#define SLAPD_EXEMODE_SLAPD 1
+#define SLAPD_EXEMODE_DB2LDIF 2
+#define SLAPD_EXEMODE_LDIF2DB 3
+#define SLAPD_EXEMODE_DB2ARCHIVE 4
+#define SLAPD_EXEMODE_ARCHIVE2DB 5
+#define SLAPD_EXEMODE_DBTEST 6
+#define SLAPD_EXEMODE_DB2INDEX 7
+#define SLAPD_EXEMODE_REFERRAL 8
+#define SLAPD_EXEMODE_SUFFIX2INSTANCE 9
+#define SLAPD_EXEMODE_PRINTVERSION 10
+#if defined(UPGRADEDB)
+#define SLAPD_EXEMODE_UPGRADEDB 11
+#endif
+
+#ifdef _WIN32
+#ifndef DONT_DECLARE_SLAPD_LDAP_DEBUG
+extern __declspec(dllimport) int slapd_ldap_debug; /* XXXmcs: should eliminate this */
+#endif /* DONT_DECLARE_SLAPD_LDAP_DEBUG */
+typedef char *caddr_t;
+void *dlsym(void *a, char *b);
+#define LOG_PID 0x01
+#define LOG_NOWAIT 0x10
+#define LOG_DEBUG 7
+#define POLL_STRUCT PRPollDesc
+#define POLL_FN PR_Poll
+#define RLIM_TYPE int
+#else /* _WIN32 */
+#define LDAP_SYSLOG
+#include <syslog.h>
+#define RLIM_TYPE int
+#include <poll.h>
+#define POLL_STRUCT PRPollDesc
+#define POLL_FN PR_Poll
+#endif /* _WIN32 */
+
+#include <stdio.h> /* for FILE */
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include <errno.h>
+
+/* there's a bug in the dbm code we import (from where?) -- FIXME */
+#ifdef LINUX
+#define LITTLE_ENDIAN __LITTLE_ENDIAN
+#endif
+#include <cert.h>
+
+#ifndef _WIN32
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#endif /* _WIN32 */
+
+#ifdef _WIN32
+#define LDAP_IOCP
+#endif
+
+#define LOG_INTERNAL_OP_CON_ID "Internal"
+#define LOG_INTERNAL_OP_OP_ID -1
+
+#define MAX_SERVICE_NAME 25
+
+#define SLAPD_TYPICAL_ATTRIBUTE_NAME_MAX_LENGTH 256
+
+#if defined(NET_SSL)
+typedef struct symbol_t {
+ const char* name;
+ unsigned number;
+} symbol_t;
+
+#define SLAPD_SSLCLIENTAUTH_OFF 0
+#define SLAPD_SSLCLIENTAUTH_ALLOWED 1 /* server asks for cert, but client need not send one */
+#define SLAPD_SSLCLIENTAUTH_REQUIRED 2 /* server will refuse SSL session unless client sends cert */
+#define SLAPD_SSLCLIENTAUTH_DEFAULT SLAPD_SSLCLIENTAUTH_ALLOWED
+#endif /* NET_SSL */
+
+#define SLAPD_LOGGING 1
+#define NUM_SNMP_INT_TBL_ROWS 5
+
+/* include NSPR header files */
+#include "nspr.h"
+#include "plhash.h"
+
+/* include NSS header files */
+#include "ssl.h"
+
+#include <sys/types.h> /* this should be moved into avl.h */
+
+#include "avl.h"
+#include "ldap.h"
+#include "ldaprot.h"
+#include "ldif.h"
+#include "ldaplog.h"
+#include "portable.h"
+#include "rwlock.h"
+#include "litekey.h"
+#include "disconnect_errors.h"
+
+#include "dirver.h"
+#include "csngen.h"
+#include "uuid.h"
+
+#if defined(OS_solaris)
+# include <thread.h>
+# define GET_THREAD_ID() thr_self()
+#else
+# if defined(_WIN32)
+# define GET_THREAD_ID() GetCurrentThreadId()
+# else
+# include <pthread.h>
+# define GET_THREAD_ID() pthread_self()
+# endif
+#endif
+
+
+/*
+ * XXXmcs: these are defined by ldap.h or ldap-extension.h,
+ * but only in a newer release than we use with DS today.
+ */
+#ifndef LDAP_CONTROL_AUTH_REQUEST
+#define LDAP_CONTROL_AUTH_REQUEST "2.16.840.1.113730.3.4.16"
+#endif
+#ifndef LDAP_CONTROL_AUTH_RESPONSE
+#define LDAP_CONTROL_AUTH_RESPONSE "2.16.840.1.113730.3.4.15"
+#endif
+#ifndef LDAP_CONTROL_REAL_ATTRS_ONLY
+#define LDAP_CONTROL_REAL_ATTRS_ONLY "2.16.840.1.113730.3.4.17"
+#endif
+#ifndef LDAP_CONTROL_VIRT_ATTRS_ONLY
+#define LDAP_CONTROL_VIRT_ATTRS_ONLY "2.16.840.1.113730.3.4.19"
+#endif
+#ifndef LDAP_CONTROL_GET_EFFECTIVE_RIGHTS
+#define LDAP_CONTROL_GET_EFFECTIVE_RIGHTS "1.3.6.1.4.1.42.2.27.9.5.2"
+#endif
+
+#define SLAPD_VENDOR_NAME "Netscape Communications Corp."
+#define SLAPD_VERSION_STR "Netscape-Directory/" PRODUCTTEXT
+#define SLAPD_SHORT_VERSION_STR PRODUCTTEXT
+
+typedef void (*VFP)(void *);
+typedef void (*VFP0)();
+#define LDAPI_INTERNAL 1
+#include "slapi-private.h"
+#include "pw.h"
+/*
+ * call the appropriate signal() function.
+ */
+#if ( defined( hpux ) || defined ( irix ))
+/*
+ * we should not mix POSIX signal library function (sigaction)
+ * with SYSV's (sigset) on IRIX. nspr uses POSIX internally.
+ */
+#define SIGNAL( s, a ) signal2sigaction( s, (void *) a )
+#elif ( defined( SYSV ) || defined( aix ))
+#define SIGNAL sigset
+#else
+#define SIGNAL signal
+#endif
+
+/*
+ * SLAPD_PR_WOULD_BLOCK_ERROR() returns non-zero if prerrno is an NSPR
+ * error code that indicates a temporary non-blocking I/O error,
+ * e.g., PR_WOULD_BLOCK_ERROR.
+ */
+#define SLAPD_PR_WOULD_BLOCK_ERROR( prerrno ) \
+ ((prerrno) == PR_WOULD_BLOCK_ERROR || (prerrno) == PR_IO_TIMEOUT_ERROR)
+
+/*
+ * SLAPD_SYSTEM_WOULD_BLOCK_ERROR() returns non-zero if syserrno is an OS
+ * error code that indicates a temporary non-blocking I/O error,
+ * e.g., EAGAIN.
+ */
+#define SLAPD_SYSTEM_WOULD_BLOCK_ERROR( syserrno ) \
+ ((syserrno)==EAGAIN || (syserrno)==EWOULDBLOCK)
+
+
+#define LDAP_ON 1
+#define LDAP_OFF 0
+#define LDAP_UNDEFINED (-1)
+
+#ifndef SLAPD_INVALID_SOCKET
+#ifdef _WIN32
+#define SLAPD_INVALID_SOCKET 0
+#else
+#define SLAPD_INVALID_SOCKET 0
+#endif
+#endif
+
+#define SLAPD_INVALID_SOCKET_INDEX (-1)
+
+#ifdef _WIN32
+#define SLAPD_DEFAULT_FILE_MODE S_IREAD | S_IWRITE
+#define SLAPD_DEFAULT_DIR_MODE 0
+#else /* _WIN32 */
+#define SLAPD_DEFAULT_FILE_MODE S_IRUSR | S_IWUSR
+#define SLAPD_DEFAULT_DIR_MODE S_IRWXU
+#endif
+
+#define SLAPD_DEFAULT_IDLE_TIMEOUT 0 /* seconds - 0 == never */
+#define SLAPD_DEFAULT_SIZELIMIT 2000 /* use -1 for no limit */
+#define SLAPD_DEFAULT_TIMELIMIT 3600 /* use -1 for no limit */
+#define SLAPD_DEFAULT_LOOKTHROUGHLIMIT 5000 /* use -1 for no limit */
+#define SLAPD_DEFAULT_GROUPNESTLEVEL 5
+#define SLAPD_DEFAULT_MAX_FILTER_NEST_LEVEL 40 /* use -1 for no limit */
+#define SLAPD_DEFAULT_IOBLOCK_TIMEOUT 1800000 /* half hour in ms */
+#define SLAPD_DEFAULT_OUTBOUND_LDAP_IO_TIMEOUT 300000 /* 5 minutes in ms */
+#define SLAPD_DEFAULT_RESERVE_FDS 64
+#define SLAPD_DEFAULT_MAX_THREADS 30 /* connection pool threads */
+#define SLAPD_DEFAULT_MAX_THREADS_PER_CONN 5 /* allowed per connection */
+#define SLAPD_DEFAULT_SCHEMA_IGNORE_TRAILING_SPACES LDAP_OFF
+
+ /* We'd like this number to be prime for
+ the hash into the Connection table */
+#define SLAPD_DEFAULT_CONNTABLESIZE 4093 /* connection table size */
+
+#define SLAPD_MONITOR_DN "cn=monitor"
+#define SLAPD_SCHEMA_DN "cn=schema"
+#define SLAPD_CONFIG_DN "cn=config"
+
+#define EGG_OBJECT_CLASS "directory~team~extensible~object"
+#define EGG_FILTER "(objectclass=directory~team~extensible~object)"
+
+#define BE_LIST_SIZE 100 /* used by mapping tree code to hold be_list stuff */
+
+#define REPL_DBTYPE "ldbm"
+#define REPL_DBTAG "repl"
+
+#define ATTR_NETSCAPEMDSUFFIX "netscapemdsuffix"
+
+/* Used to make unhashed passwords available to plugins. */
+#define PSEUDO_ATTR_UNHASHEDUSERPASSWORD "unhashed#user#password"
+
+#define REFERRAL_REMOVE_CMD "remove"
+
+/* Filenames for DSE storage */
+#define DSE_FILENAME "dse.ldif"
+#define DSE_TMPFILE "dse.ldif.tmp"
+#define DSE_BACKFILE "dse.ldif.bak"
+#define DSE_STARTOKFILE "dse.ldif.startOK"
+#define DSE_LDBM_FILENAME "ldbm.ldif"
+#define DSE_LDBM_TMPFILE "ldbm.ldif.tmp"
+/* for now, we are using the dse file for the base config file */
+#define CONFIG_FILENAME DSE_FILENAME
+/* the default configuration sub directory of the instance directory */
+#define CONFIG_SUBDIR_NAME "config"
+/* the default schema sub directory of the config sub directory */
+#define SCHEMA_SUBDIR_NAME "schema"
+
+struct subfilt {
+ char *sf_type;
+ char *sf_initial;
+ char **sf_any;
+ char *sf_final;
+ void *sf_private; /* data private to syntax handler */
+};
+
+#include "filter.h" /* mr_filter_t */
+
+/*
+ * represents a search filter
+ */
+struct slapi_filter {
+ int f_flags;
+ unsigned long f_choice; /* values taken from ldap.h */
+ PRUint32 f_hash; /* for quick comparisons */
+ void *assigned_decoder;
+
+ union {
+ /* present */
+ char *f_un_type;
+
+ /* equality, lessorequal, greaterorequal, approx */
+ struct ava f_un_ava;
+
+ /* and, or, not */
+ struct slapi_filter *f_un_complex;
+
+ /* substrings */
+ struct subfilt f_un_sub;
+
+ /* extended -- v3 only */
+ mr_filter_t f_un_extended;
+ } f_un;
+#define f_type f_un.f_un_type
+#define f_ava f_un.f_un_ava
+#define f_avtype f_un.f_un_ava.ava_type
+#define f_avvalue f_un.f_un_ava.ava_value
+#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
+#define f_sub f_un.f_un_sub
+#define f_sub_type f_un.f_un_sub.sf_type
+#define f_sub_initial f_un.f_un_sub.sf_initial
+#define f_sub_any f_un.f_un_sub.sf_any
+#define f_sub_final f_un.f_un_sub.sf_final
+#define f_mr f_un.f_un_extended
+#define f_mr_oid f_un.f_un_extended.mrf_oid
+#define f_mr_type f_un.f_un_extended.mrf_type
+#define f_mr_value f_un.f_un_extended.mrf_value
+#define f_mr_dnAttrs f_un.f_un_extended.mrf_dnAttrs
+
+ struct slapi_filter *f_next;
+};
+
+struct csn
+{
+ time_t tstamp;
+ PRUint16 seqnum;
+ ReplicaId rid;
+ PRUint16 subseqnum;
+};
+
+struct csnset_node
+{
+ CSNType type;
+ CSN csn;
+ CSNSet *next;
+};
+
+struct slapi_value
+{
+ struct berval bv;
+ CSNSet *v_csnset;
+};
+
+/*
+ * JCM: This structure, slapi_value_set, seems useless,
+ * but in the future we could:
+ *
+ * {
+ * unsigned char flag;
+ * union single
+ * {
+ * struct slapi_value *va;
+ * };
+ * union multiple_array
+ * {
+ * short num;
+ * short max;
+ * struct slapi_value **va;
+ * };
+ * union multiple_tree
+ * {
+ * struct slapi_value_tree *vt;
+ * };
+ */
+struct slapi_value_set
+{
+ struct slapi_value **va;
+};
+
+struct valuearrayfast
+{
+ int num; /* The number of values in the array */
+ int max; /* The number of slots in the array */
+ struct slapi_value **va;
+};
+
+struct bervals2free {
+ struct berval **bvals;
+ struct bervals2free *next;
+};
+
+/*
+ * represents an attribute instance (type + values + syntax)
+ */
+
+struct slapi_attr {
+ char *a_type;
+ struct slapi_value_set a_present_values;
+ unsigned long a_flags; /* SLAPI_ATTR_FLAG_... */
+ struct slapdplugin *a_plugin;
+ struct slapi_value_set a_deleted_values;
+ struct bervals2free *a_listtofree; /* JCM: EVIL... For DS4 Slapi compatibility. */
+ struct slapi_attr *a_next;
+ CSN *a_deletioncsn; /* The point in time at which this attribute was last deleted */
+};
+
+typedef struct oid_item {
+ char *oi_oid;
+ struct slapdplugin *oi_plugin;
+ struct oid_item *oi_next;
+} oid_item_t;
+
+/* attribute description (represents an attribute, but not the value) */
+typedef struct asyntaxinfo {
+ char *asi_oid; /* OID */
+ char *asi_name; /* normalized name */
+ char **asi_aliases; /* alternative names */
+ char *asi_desc; /* textual description */
+ char *asi_superior; /* derived from */
+ char *asi_mr_equality; /* equality matching rule */
+ char *asi_mr_ordering; /* ordering matching rule */
+ char *asi_mr_substring; /* substring matching rule */
+ char **asi_origin; /* X-ORIGIN extension */
+ struct slapdplugin *asi_plugin; /* syntax */
+ unsigned long asi_flags; /* SLAPI_ATTR_FLAG_... */
+ int asi_syntaxlength; /* length associated w/syntax */
+ int asi_refcnt; /* outstanding references */
+ PRBool asi_marked_for_delete; /* delete at next opportunity */
+} asyntaxinfo;
+
+/*
+ * Note: most of the asi_flags values are defined in slapi-plugin.h, but
+ * these ones are private to the DS.
+ */
+#define SLAPI_ATTR_FLAG_OVERRIDE 0x0010 /* when adding a new attribute,
+ override the existing attribute,
+ if any */
+#define SLAPI_ATTR_FLAG_NOLOCKING 0x0020 /* the init code doesn't lock the
+ tables */
+#define SLAPI_ATTR_FLAG_CMP_BITBYBIT 0x4000 /* do memcmp, not syntax cmp */
+#define SLAPI_ATTR_FLAG_KEEP 0x8000 /* keep when replacing all */
+
+/* This is the type of the function passed into attr_syntax_enumerate_attrs */
+typedef int (*AttrEnumFunc)(struct asyntaxinfo *asi, void *arg);
+/* Possible return values for an AttrEnumFunc */
+#define ATTR_SYNTAX_ENUM_NEXT 0 /* continue */
+#define ATTR_SYNTAX_ENUM_STOP 1 /* halt the enumeration */
+#define ATTR_SYNTAX_ENUM_REMOVE 2 /* unhash current node and continue */
+
+/* This is the type of the function passed into plugin_syntax_enumerate */
+typedef int (*SyntaxEnumFunc)(char **names, Slapi_PluginDesc *plugindesc,
+ void *arg);
+
+/* OIDs for some commonly used syntaxes */
+#define BINARY_SYNTAX_OID "1.3.6.1.4.1.1466.115.121.1.5"
+#define BOOLEAN_SYNTAX_OID "1.3.6.1.4.1.1466.115.121.1.7"
+#define COUNTRYSTRING_SYNTAX_OID "1.3.6.1.4.1.1466.115.121.1.11"
+#define DN_SYNTAX_OID "1.3.6.1.4.1.1466.115.121.1.12"
+#define DIRSTRING_SYNTAX_OID "1.3.6.1.4.1.1466.115.121.1.15"
+#define GENERALIZEDTIME_SYNTAX_OID "1.3.6.1.4.1.1466.115.121.1.24"
+#define IA5STRING_SYNTAX_OID "1.3.6.1.4.1.1466.115.121.1.26"
+#define INTEGER_SYNTAX_OID "1.3.6.1.4.1.1466.115.121.1.27"
+#define JPEG_SYNTAX_OID "1.3.6.1.4.1.1466.115.121.1.28"
+#define OCTETSTRING_SYNTAX_OID "1.3.6.1.4.1.1466.115.121.1.40"
+#define OID_SYNTAX_OID "1.3.6.1.4.1.1466.115.121.1.38"
+#define POSTALADDRESS_SYNTAX_OID "1.3.6.1.4.1.1466.115.121.1.41"
+#define TELEPHONE_SYNTAX_OID "1.3.6.1.4.1.1466.115.121.1.50"
+#define SPACE_INSENSITIVE_STRING_SYNTAX_OID "2.16.840.1.113730.3.7.1"
+
+/* OIDs for some commonly used matching rules */
+#define DNMATCH_OID "2.5.13.1" /* distinguishedNameMatch */
+#define CASEIGNOREMATCH_OID "2.5.13.2" /* caseIgnoreMatch */
+#define INTFIRSTCOMPMATCH_OID "2.5.13.29" /* integerFirstComponentMatch */
+#define OIDFIRSTCOMPMATCH_OID "2.5.13.30" /* objectIdentifierFirstComponentMatch */
+
+/* Names for some commonly used matching rules */
+#define DNMATCH_NAME "distinguishedNameMatch"
+#define CASEIGNOREMATCH_NAME "caseIgnoreMatch"
+#define INTFIRSTCOMPMATCH_NAME "integerFirstComponentMatch"
+#define OIDFIRSTCOMPMATCH_NAME "objectIdentifierFirstComponentMatch"
+
+#define ATTR_STANDARD_STRING "Standard Attribute"
+#define ATTR_USERDEF_STRING "User Defined Attribute"
+#define OC_STANDARD_STRING "Standard ObjectClass"
+#define OC_USERDEF_STRING "User Defined ObjectClass"
+
+/* modifiers used to define attributes */
+#define ATTR_MOD_OPERATIONAL "operational"
+#define ATTR_MOD_OVERRIDE "override"
+#define ATTR_MOD_SINGLE "single"
+
+/* extended operations supported by the server */
+#define EXTOP_BULK_IMPORT_START_OID "2.16.840.1.113730.3.5.7"
+#define EXTOP_BULK_IMPORT_DONE_OID "2.16.840.1.113730.3.5.8"
+
+/*
+ * Represents a Distinguished Name of an entry
+ * WARNING, if you change this stucture you MUST update dn_size()
+ * function in entry.c
+ */
+struct slapi_dn
+{
+ unsigned char flag;
+ const char *dn; /* DN */
+ const char *ndn; /* Case Normalised DN */
+ int ndn_len; /* normalize dn len */
+};
+
+/*
+ * Represents a Relative Distinguished Name.
+ */
+
+struct slapi_rdn
+{
+ unsigned char flag;
+ char *rdn;
+ char **rdns; /* Valid when FLAG_RDNS is set. */
+ int butcheredupto; /* How far through rdns we've gone converting '=' to '\0' */
+};
+
+/*
+ * representation of uniqueID. Defined in uuid.h
+ */
+#define UID_SIZE 16 /* size of unique id in bytes */
+
+/*
+ * max 1G attr values per entry
+ * in case, libdb returned bogus entry string from db (blackflag #623569)
+ */
+#define ENTRY_MAX_ATTRIBUTE_VALUE_COUNT 1073741824
+
+/*
+ * represents an entry in core
+ * WARNING, if you change this stucture you MUST update slapi_entry_size()
+ * function
+ */
+struct slapi_entry {
+ struct slapi_dn e_sdn; /* DN of this entry */
+ char *e_uniqueid; /* uniqueID of this entry */
+ CSNSet *e_dncsnset; /* The set of DN CSNs for this entry */
+ CSN *e_maxcsn; /* maximum CSN of the entry */
+ Slapi_Attr *e_attrs; /* list of attributes and values */
+ Slapi_Attr *e_deleted_attrs; /* deleted list of attributes and values */
+ Slapi_Attr *e_virtual_attrs; /* list of virtual attributes */
+ time_t e_virtual_watermark; /* indicates cache consistency when compared to global watermark */
+ PRRWLock *e_virtual_lock; /* for access to cached vattrs */
+ void *e_extension; /* A list of entry object extensions */
+ unsigned char e_flags;
+};
+
+/*
+ * represents schema information for a database
+ */
+/* values for oc_flags (only space for 8 of these right now!) */
+#define OC_FLAG_STANDARD_OC 1
+#define OC_FLAG_USER_OC 2
+#define OC_FLAG_REDEFINED_OC 4
+#define OC_FLAG_OBSOLETE 8
+
+/* values for oc_kind */
+#define OC_KIND_STRUCTURAL 0
+#define OC_KIND_AUXILIARY 1
+#define OC_KIND_ABSTRACT 2
+
+
+/* XXXmcs: ../plugins/cos/cos_cache.c has its own copy of this definition! */
+struct objclass {
+ char *oc_name; /* NAME */
+ char *oc_desc; /* DESC */
+ char *oc_oid; /* object identifier */
+ char *oc_superior; /* SUP -- XXXmcs: should be an array */
+ PRUint8 oc_kind; /* ABSTRACT/STRUCTURAL/AUXILIARY */
+ PRUint8 oc_flags; /* misc. flags, e.g., OBSOLETE */
+ char **oc_required;
+ char **oc_allowed;
+ char **oc_orig_required; /* MUST */
+ char **oc_orig_allowed; /* MAY */
+ char **oc_origin; /* X-ORIGIN extension */
+ struct objclass *oc_next;
+};
+
+typedef struct slapi_matchingRuleEntry {
+ char *mr_oid;
+ char *mr_oidalias;
+ char *mr_name;
+ char *mr_desc;
+ char *mr_syntax;
+ int mr_obsolete;
+} slapi_matchingRuleEntry;
+
+struct matchingRuleList {
+ Slapi_MatchingRuleEntry *mr_entry;
+ struct matchingRuleList *mrl_next;
+};
+
+/* List of the plugin index numbers */
+
+/* Backend & Global Plugins */
+#define PLUGIN_LIST_DATABASE 0
+#define PLUGIN_LIST_PREOPERATION 1
+#define PLUGIN_LIST_POSTOPERATION 2
+#define PLUGIN_LIST_BEPREOPERATION 3
+#define PLUGIN_LIST_BEPOSTOPERATION 4
+#define PLUGIN_LIST_INTERNAL_PREOPERATION 5
+#define PLUGIN_LIST_INTERNAL_POSTOPERATION 6
+#define PLUGIN_LIST_EXTENDED_OPERATION 7
+#define PLUGIN_LIST_BACKEND_MAX 8
+
+/* Global Plugins */
+#define PLUGIN_LIST_ACL 9
+#define PLUGIN_LIST_MATCHINGRULE 10
+#define PLUGIN_LIST_SYNTAX 11
+#define PLUGIN_LIST_ENTRY 12
+#define PLUGIN_LIST_OBJECT 13
+#define PLUGIN_LIST_PWD_STORAGE_SCHEME 14
+#define PLUGIN_LIST_VATTR_SP 15 /* DBDB */
+#define PLUGIN_LIST_REVER_PWD_STORAGE_SCHEME 16
+#define PLUGIN_LIST_LDBM_ENTRY_FETCH_STORE 17
+#define PLUGIN_LIST_INDEX 18
+#define PLUGIN_LIST_GLOBAL_MAX 19
+
+/* plugin configuration attributes */
+#define ATTR_PLUGIN_PATH "nsslapd-pluginPath"
+#define ATTR_PLUGIN_INITFN "nsslapd-pluginInitFunc"
+#define ATTR_PLUGIN_TYPE "nsslapd-pluginType"
+#define ATTR_PLUGIN_PLUGINID "nsslapd-pluginId"
+#define ATTR_PLUGIN_VERSION "nsslapd-pluginVersion"
+#define ATTR_PLUGIN_VENDOR "nsslapd-pluginVendor"
+#define ATTR_PLUGIN_DESC "nsslapd-pluginDescription"
+#define ATTR_PLUGIN_ENABLED "nsslapd-pluginEnabled"
+#define ATTR_PLUGIN_ARG "nsslapd-pluginArg"
+#define ATTR_PLUGIN_BACKEND "nsslapd-backend"
+#define ATTR_PLUGIN_SCHEMA_CHECK "nsslapd-schemaCheck"
+#define ATTR_PLUGIN_LOG_ACCESS "nsslapd-logAccess"
+#define ATTR_PLUGIN_LOG_AUDIT "nsslapd-logAudit"
+#define ATTR_PLUGIN_TARGET_SUBTREE "nsslapd-targetSubtree"
+#define ATTR_PLUGIN_BIND_SUBTREE "nsslapd-bindSubtree"
+#define ATTR_PLUGIN_INVOKE_FOR_REPLOP "nsslapd-invokeForReplOp"
+
+/* plugin action states */
+enum
+{
+ PLGC_OFF, /* internal operation action is on */
+ PLGC_ON, /* internal operation action is off */
+ PLGC_UPTOPLUGIN /* internal operation action is left up to plugin */
+};
+
+/* special data specifications */
+enum
+{
+ PLGC_DATA_LOCAL, /* plugin has access to all data hosted by this server */
+ PLGC_DATA_REMOTE, /* plugin has access to all requests for data not hosted by this server */
+ PLGC_DATA_BIND_ANONYMOUS, /* plugin bind function should be invoked for anonymous binds */
+ PLGC_DATA_BIND_ROOT, /* plugin bind function should be invoked for directory manager binds */
+ PLGC_DATA_MAX
+};
+
+/* DataList definition */
+struct datalist
+{
+ void **elements; /* array of elements */
+ int element_count; /* number of elements in the array */
+ int alloc_count; /* number of allocated nodes in the array */
+}datalist;
+
+/* data available to plugins */
+typedef struct target_data
+{
+ DataList subtrees; /* regular DIT subtrees acessible to the plugin */
+ PRBool special_data [PLGC_DATA_MAX]; /* array of special data specification */
+}PluginTargetData;
+
+struct pluginconfig{
+ PluginTargetData plgc_target_subtrees; /* list of subtrees accessible by the plugin */
+ PluginTargetData plgc_bind_subtrees; /* the list of subtrees for which plugin in invoked during bind operation */
+ PRBool plgc_schema_check; /* inidcates whether schema check is performed during internal op */
+ PRBool plgc_log_change; /* indicates whether changes are logged during internal op */
+ PRBool plgc_log_access; /* indicates whether internal op is recorded in access log */
+ PRBool plgc_log_audit; /* indicates whether internal op is recorded in audit log */
+ PRBool plgc_invoke_for_replop;/* indicates that plugin should be invoked for internal operations */
+};
+
+struct slapdplugin {
+ void *plg_private; /* data private to plugin */
+ char *plg_version; /* version of this plugin */
+ int plg_argc; /* argc from config file */
+ char **plg_argv; /* args from config file */
+ char *plg_libpath; /* library path for dll/so */
+ char *plg_initfunc; /* init symbol */
+ IFP plg_close; /* close function */
+ Slapi_PluginDesc plg_desc; /* vendor's info */
+ char *plg_name; /* used for plugin rdn in cn=config */
+ struct slapdplugin *plg_next; /* for plugin lists */
+ int plg_type; /* discriminates union */
+ char *plg_dn; /* config dn for this plugin */
+ struct slapdplugin *plg_group; /* pointer to the group to which this plugin belongs */
+ struct pluginconfig plg_conf; /* plugin configuration parameters */
+ IFP plg_cleanup; /* cleanup function */
+ IFP plg_start; /* start function */
+ IFP plg_poststart; /* poststart function */
+
+/* NOTE: These LDIF2DB and DB2LDIF fn pointers are internal only for now.
+ I don't believe you can get these functions from a plug-in and
+ then call them without knowing what IFP or VFP0 are. (These aren't
+ declared in slapi-plugin.h.) More importantly, it's a pretty ugly
+ way to get to these functions. (Do we want people to get locked into
+ this?)
+
+ The correct way to do this would be to expose these functions as
+ front-end API functions. We can fix this for the next release.
+ (No one has the time right now.)
+ */
+ union { /* backend database plugin structure */
+ struct plg_un_database_backend {
+ IFP plg_un_db_bind; /* bind */
+ IFP plg_un_db_unbind; /* unbind */
+ IFP plg_un_db_search; /* search */
+ IFP plg_un_db_next_search_entry; /* iterate */
+ IFP plg_un_db_next_search_entry_ext;
+ IFP plg_un_db_entry_release;
+ IFP plg_un_db_compare; /* compare */
+ IFP plg_un_db_modify; /* modify */
+ IFP plg_un_db_modrdn; /* modrdn */
+ IFP plg_un_db_add; /* add */
+ IFP plg_un_db_delete; /* delete */
+ IFP plg_un_db_abandon; /* abandon */
+ IFP plg_un_db_config; /* config */
+ IFP plg_un_db_flush; /* close */
+ IFP plg_un_db_seq; /* sequence */
+ IFP plg_un_db_entry; /* entry send */
+ IFP plg_un_db_referral; /* referral send */
+ IFP plg_un_db_result; /* result send */
+ IFP plg_un_db_ldif2db; /* ldif 2 database */
+ IFP plg_un_db_db2ldif; /* database 2 ldif */
+ IFP plg_un_db_db2index; /* database 2 index */
+ IFP plg_un_db_archive2db; /* ldif 2 database */
+ IFP plg_un_db_db2archive; /* database 2 ldif */
+#if defined(UPGRADEDB)
+ IFP plg_un_db_upgradedb; /* convert old idl to new */
+#endif
+ IFP plg_un_db_begin; /* dbase txn begin */
+ IFP plg_un_db_commit; /* dbase txn commit */
+ IFP plg_un_db_abort; /* dbase txn abort */
+ IFP plg_un_db_dbsize; /* database size */
+ IFP plg_un_db_dbtest; /* database size */
+ IFP plg_un_db_rmdb; /* database remove */
+ IFP plg_un_db_register_dn_callback; /* Register a function to call when a operation is applied to a given DN */
+ IFP plg_un_db_register_oc_callback; /* Register a function to call when a operation is applied to a given ObjectClass */
+ IFP plg_un_db_init_instance; /* initializes new db instance */
+ IFP plg_un_db_wire_import; /* fast replica update */
+ } plg_un_db;
+#define plg_bind plg_un.plg_un_db.plg_un_db_bind
+#define plg_unbind plg_un.plg_un_db.plg_un_db_unbind
+#define plg_search plg_un.plg_un_db.plg_un_db_search
+#define plg_next_search_entry plg_un.plg_un_db.plg_un_db_next_search_entry
+#define plg_next_search_entry_ext plg_un.plg_un_db.plg_un_db_next_search_entry_ext
+#define plg_entry_release plg_un.plg_un_db.plg_un_db_entry_release
+#define plg_compare plg_un.plg_un_db.plg_un_db_compare
+#define plg_modify plg_un.plg_un_db.plg_un_db_modify
+#define plg_modrdn plg_un.plg_un_db.plg_un_db_modrdn
+#define plg_add plg_un.plg_un_db.plg_un_db_add
+#define plg_delete plg_un.plg_un_db.plg_un_db_delete
+#define plg_abandon plg_un.plg_un_db.plg_un_db_abandon
+#define plg_config plg_un.plg_un_db.plg_un_db_config
+#define plg_flush plg_un.plg_un_db.plg_un_db_flush
+#define plg_seq plg_un.plg_un_db.plg_un_db_seq
+#define plg_entry plg_un.plg_un_db.plg_un_db_entry
+#define plg_referral plg_un.plg_un_db.plg_un_db_referral
+#define plg_result plg_un.plg_un_db.plg_un_db_result
+#define plg_ldif2db plg_un.plg_un_db.plg_un_db_ldif2db
+#define plg_db2ldif plg_un.plg_un_db.plg_un_db_db2ldif
+#define plg_db2index plg_un.plg_un_db.plg_un_db_db2index
+#define plg_archive2db plg_un.plg_un_db.plg_un_db_archive2db
+#define plg_db2archive plg_un.plg_un_db.plg_un_db_db2archive
+#if defined(UPGRADEDB)
+#define plg_upgradedb plg_un.plg_un_db.plg_un_db_upgradedb
+#endif
+#define plg_dbsize plg_un.plg_un_db.plg_un_db_dbsize
+#define plg_dbtest plg_un.plg_un_db.plg_un_db_dbtest
+#define plg_rmdb plg_un.plg_un_db.plg_un_db_rmdb
+#define plg_init_instance plg_un.plg_un_db.plg_un_db_init_instance
+#define plg_wire_import plg_un.plg_un_db.plg_un_db_wire_import
+
+ /* extended operation plugin structure */
+ struct plg_un_protocol_extension {
+ char **plg_un_pe_exoids; /* exop oids */
+ char **plg_un_pe_exnames; /* exop names (may be NULL) */
+ IFP plg_un_pe_exhandler; /* handler */
+ } plg_un_pe;
+#define plg_exoids plg_un.plg_un_pe.plg_un_pe_exoids
+#define plg_exnames plg_un.plg_un_pe.plg_un_pe_exnames
+#define plg_exhandler plg_un.plg_un_pe.plg_un_pe_exhandler
+
+
+ /* pre-operation plugin structure */
+ struct plg_un_pre_operation {
+ IFP plg_un_pre_bind; /* bind */
+ IFP plg_un_pre_unbind; /* unbind */
+ IFP plg_un_pre_search; /* search */
+ IFP plg_un_pre_compare; /* compare */
+ IFP plg_un_pre_modify; /* modify */
+ IFP plg_un_pre_modrdn; /* modrdn */
+ IFP plg_un_pre_add; /* add */
+ IFP plg_un_pre_delete; /* delete */
+ IFP plg_un_pre_abandon; /* abandon */
+ IFP plg_un_pre_entry; /* entry send */
+ IFP plg_un_pre_referral; /* referral send */
+ IFP plg_un_pre_result; /* result send */
+ } plg_un_pre;
+#define plg_prebind plg_un.plg_un_pre.plg_un_pre_bind
+#define plg_preunbind plg_un.plg_un_pre.plg_un_pre_unbind
+#define plg_presearch plg_un.plg_un_pre.plg_un_pre_search
+#define plg_precompare plg_un.plg_un_pre.plg_un_pre_compare
+#define plg_premodify plg_un.plg_un_pre.plg_un_pre_modify
+#define plg_premodrdn plg_un.plg_un_pre.plg_un_pre_modrdn
+#define plg_preadd plg_un.plg_un_pre.plg_un_pre_add
+#define plg_predelete plg_un.plg_un_pre.plg_un_pre_delete
+#define plg_preabandon plg_un.plg_un_pre.plg_un_pre_abandon
+#define plg_preentry plg_un.plg_un_pre.plg_un_pre_entry
+#define plg_prereferral plg_un.plg_un_pre.plg_un_pre_referral
+#define plg_preresult plg_un.plg_un_pre.plg_un_pre_result
+
+ /* post-operation plugin structure */
+ struct plg_un_post_operation {
+ IFP plg_un_post_bind; /* bind */
+ IFP plg_un_post_unbind; /* unbind */
+ IFP plg_un_post_search; /* search */
+ IFP plg_un_post_searchfail; /* failed search */
+ IFP plg_un_post_compare; /* compare */
+ IFP plg_un_post_modify; /* modify */
+ IFP plg_un_post_modrdn; /* modrdn */
+ IFP plg_un_post_add; /* add */
+ IFP plg_un_post_delete; /* delete */
+ IFP plg_un_post_abandon; /* abandon */
+ IFP plg_un_post_entry; /* entry send */
+ IFP plg_un_post_referral; /* referral send */
+ IFP plg_un_post_result; /* result send */
+ } plg_un_post;
+#define plg_postbind plg_un.plg_un_post.plg_un_post_bind
+#define plg_postunbind plg_un.plg_un_post.plg_un_post_unbind
+#define plg_postsearch plg_un.plg_un_post.plg_un_post_search
+#define plg_postsearchfail plg_un.plg_un_post.plg_un_post_searchfail
+#define plg_postcompare plg_un.plg_un_post.plg_un_post_compare
+#define plg_postmodify plg_un.plg_un_post.plg_un_post_modify
+#define plg_postmodrdn plg_un.plg_un_post.plg_un_post_modrdn
+#define plg_postadd plg_un.plg_un_post.plg_un_post_add
+#define plg_postdelete plg_un.plg_un_post.plg_un_post_delete
+#define plg_postabandon plg_un.plg_un_post.plg_un_post_abandon
+#define plg_postentry plg_un.plg_un_post.plg_un_post_entry
+#define plg_postreferral plg_un.plg_un_post.plg_un_post_referral
+#define plg_postresult plg_un.plg_un_post.plg_un_post_result
+
+ /* backend pre-operation plugin structure */
+ struct plg_un_bepre_operation {
+ IFP plg_un_bepre_modify; /* modify */
+ IFP plg_un_bepre_modrdn; /* modrdn */
+ IFP plg_un_bepre_add; /* add */
+ IFP plg_un_bepre_delete; /* delete */
+ } plg_un_bepre;
+#define plg_bepremodify plg_un.plg_un_bepre.plg_un_bepre_modify
+#define plg_bepremodrdn plg_un.plg_un_bepre.plg_un_bepre_modrdn
+#define plg_bepreadd plg_un.plg_un_bepre.plg_un_bepre_add
+#define plg_bepredelete plg_un.plg_un_bepre.plg_un_bepre_delete
+
+ /* backend post-operation plugin structure */
+ struct plg_un_bepost_operation {
+ IFP plg_un_bepost_modify; /* modify */
+ IFP plg_un_bepost_modrdn; /* modrdn */
+ IFP plg_un_bepost_add; /* add */
+ IFP plg_un_bepost_delete; /* delete */
+ } plg_un_bepost;
+#define plg_bepostmodify plg_un.plg_un_bepost.plg_un_bepost_modify
+#define plg_bepostmodrdn plg_un.plg_un_bepost.plg_un_bepost_modrdn
+#define plg_bepostadd plg_un.plg_un_bepost.plg_un_bepost_add
+#define plg_bepostdelete plg_un.plg_un_bepost.plg_un_bepost_delete
+
+ /* internal pre-operation plugin structure */
+ struct plg_un_internal_pre_operation {
+ IFP plg_un_internal_pre_modify; /* modify */
+ IFP plg_un_internal_pre_modrdn; /* modrdn */
+ IFP plg_un_internal_pre_add; /* add */
+ IFP plg_un_internal_pre_delete; /* delete */
+ } plg_un_internal_pre;
+#define plg_internal_pre_modify plg_un.plg_un_internal_pre.plg_un_internal_pre_modify
+#define plg_internal_pre_modrdn plg_un.plg_un_internal_pre.plg_un_internal_pre_modrdn
+#define plg_internal_pre_add plg_un.plg_un_internal_pre.plg_un_internal_pre_add
+#define plg_internal_pre_delete plg_un.plg_un_internal_pre.plg_un_internal_pre_delete
+
+ /* internal post-operation plugin structure */
+ struct plg_un_internal_post_operation {
+ IFP plg_un_internal_post_modify; /* modify */
+ IFP plg_un_internal_post_modrdn; /* modrdn */
+ IFP plg_un_internal_post_add; /* add */
+ IFP plg_un_internal_post_delete; /* delete */
+ } plg_un_internal_post;
+#define plg_internal_post_modify plg_un.plg_un_internal_post.plg_un_internal_post_modify
+#define plg_internal_post_modrdn plg_un.plg_un_internal_post.plg_un_internal_post_modrdn
+#define plg_internal_post_add plg_un.plg_un_internal_post.plg_un_internal_post_add
+#define plg_internal_post_delete plg_un.plg_un_internal_post.plg_un_internal_post_delete
+
+ /* matching rule plugin structure */
+ struct plg_un_matching_rule {
+ IFP plg_un_mr_filter_create; /* factory function */
+ IFP plg_un_mr_indexer_create; /* factory function */
+ } plg_un_mr;
+#define plg_mr_filter_create plg_un.plg_un_mr.plg_un_mr_filter_create
+#define plg_mr_indexer_create plg_un.plg_un_mr.plg_un_mr_indexer_create
+
+ /* syntax plugin structure */
+ struct plg_un_syntax_struct {
+ IFP plg_un_syntax_filter_ava;
+ IFP plg_un_syntax_filter_ava_sv;
+ IFP plg_un_syntax_filter_sub;
+ IFP plg_un_syntax_filter_sub_sv;
+ IFP plg_un_syntax_values2keys;
+ IFP plg_un_syntax_values2keys_sv;
+ IFP plg_un_syntax_assertion2keys_ava;
+ IFP plg_un_syntax_assertion2keys_sub;
+ int plg_un_syntax_flags;
+/*
+ from slapi-plugin.h
+#define SLAPI_PLUGIN_SYNTAX_FLAG_ORKEYS 1
+#define SLAPI_PLUGIN_SYNTAX_FLAG_ORDERING 2
+*/
+ char **plg_un_syntax_names;
+ char *plg_un_syntax_oid;
+ IFP plg_un_syntax_compare;
+ } plg_un_syntax;
+#define plg_syntax_filter_ava plg_un.plg_un_syntax.plg_un_syntax_filter_ava
+#define plg_syntax_filter_sub plg_un.plg_un_syntax.plg_un_syntax_filter_sub
+#define plg_syntax_values2keys plg_un.plg_un_syntax.plg_un_syntax_values2keys
+#define plg_syntax_assertion2keys_ava plg_un.plg_un_syntax.plg_un_syntax_assertion2keys_ava
+#define plg_syntax_assertion2keys_sub plg_un.plg_un_syntax.plg_un_syntax_assertion2keys_sub
+#define plg_syntax_flags plg_un.plg_un_syntax.plg_un_syntax_flags
+#define plg_syntax_names plg_un.plg_un_syntax.plg_un_syntax_names
+#define plg_syntax_oid plg_un.plg_un_syntax.plg_un_syntax_oid
+#define plg_syntax_compare plg_un.plg_un_syntax.plg_un_syntax_compare
+
+ struct plg_un_acl_struct {
+ IFP plg_un_acl_init;
+ IFP plg_un_acl_syntax_check;
+ IFP plg_un_acl_access_allowed;
+ IFP plg_un_acl_mods_allowed;
+ IFP plg_un_acl_mods_update;
+ } plg_un_acl;
+#define plg_acl_init plg_un.plg_un_acl.plg_un_acl_init
+#define plg_acl_syntax_check plg_un.plg_un_acl.plg_un_acl_syntax_check
+#define plg_acl_access_allowed plg_un.plg_un_acl.plg_un_acl_access_allowed
+#define plg_acl_mods_allowed plg_un.plg_un_acl.plg_un_acl_mods_allowed
+#define plg_acl_mods_update plg_un.plg_un_acl.plg_un_acl_mods_update
+
+ /* password storage scheme (kexcoff) */
+ struct plg_un_pwd_storage_scheme_struct {
+ char *plg_un_pwd_storage_scheme_name; /* SHA, SSHA...*/
+ CFP plg_un_pwd_storage_scheme_enc;
+ IFP plg_un_pwd_storage_scheme_dec;
+ IFP plg_un_pwd_storage_scheme_cmp;
+ } plg_un_pwd_storage_scheme;
+#define plg_pwdstorageschemename plg_un.plg_un_pwd_storage_scheme.plg_un_pwd_storage_scheme_name
+#define plg_pwdstorageschemeenc plg_un.plg_un_pwd_storage_scheme.plg_un_pwd_storage_scheme_enc
+#define plg_pwdstorageschemedec plg_un.plg_un_pwd_storage_scheme.plg_un_pwd_storage_scheme_dec
+#define plg_pwdstorageschemecmp plg_un.plg_un_pwd_storage_scheme.plg_un_pwd_storage_scheme_cmp
+
+ /* entry fetch/store */
+ struct plg_un_entry_fetch_store_struct {
+ IFP plg_un_entry_fetch_func;
+ IFP plg_un_entry_store_func;
+ } plg_un_entry_fetch_store;
+#define plg_entryfetchfunc plg_un.plg_un_entry_fetch_store.plg_un_entry_fetch_func
+#define plg_entrystorefunc plg_un.plg_un_entry_fetch_store.plg_un_entry_store_func
+ } plg_un;
+};
+
+/*
+ * represents a "database"
+ */
+
+typedef struct backend {
+ Slapi_DN **be_suffix; /* the DN suffixes of data in this backend */
+ PRLock *be_suffixlock;
+ int be_suffixcount;
+ char *be_basedn; /* The base dn for the config & monitor dns */
+ char *be_configdn; /* The config dn for this backend */
+ char *be_monitordn; /* The monitor dn for this backend */
+ int be_readonly; /* 1 => db is in "read only" mode */
+ int be_sizelimit; /* size limit for this backend */
+ int be_timelimit; /* time limit for this backend */
+ int be_maxnestlevel; /* Max nest level for acl group evaluation */
+ int be_noacl; /* turn off front end acl for this be */
+ int be_lastmod; /* keep track of lastmodified{by,time} */
+ char *be_type; /* type of database */
+ char *be_backendconfig; /* backend config filename */
+ char **be_include; /* include files within this db definition */
+ int be_private; /* Internal backends use this to hide from the user */
+ int be_logchanges; /* changes to this backend should be logged in the changelog */
+ int (*be_writeconfig)(Slapi_PBlock *pb); /* function to call to make this
+ backend write its conf file */
+ /*
+ * backend database api function ptrs and args (to do operations)
+ */
+ struct slapdplugin *be_database; /* single plugin */
+#define be_bind be_database->plg_bind
+#define be_unbind be_database->plg_unbind
+#define be_search be_database->plg_search
+#define be_next_search_entry be_database->plg_next_search_entry
+#define be_next_search_entry_ext be_database->plg_next_search_entry_ext
+#define be_entry_release be_database->plg_entry_release
+#define be_compare be_database->plg_compare
+#define be_modify be_database->plg_modify
+#define be_modrdn be_database->plg_modrdn
+#define be_add be_database->plg_add
+#define be_delete be_database->plg_delete
+#define be_abandon be_database->plg_abandon
+#define be_config be_database->plg_config
+#define be_close be_database->plg_close
+#define be_flush be_database->plg_flush
+#define be_start be_database->plg_start
+#define be_poststart be_database->plg_poststart
+#define be_seq be_database->plg_seq
+#define be_ldif2db be_database->plg_ldif2db
+#if defined(UPGRADEDB)
+#define be_upgradedb be_database->plg_upgradedb
+#endif
+#define be_db2ldif be_database->plg_db2ldif
+#define be_db2index be_database->plg_db2index
+#define be_archive2db be_database->plg_archive2db
+#define be_db2archive be_database->plg_db2archive
+#define be_dbsize be_database->plg_dbsize
+#define be_dbtest be_database->plg_dbtest
+#define be_rmdb be_database->plg_rmdb
+#define be_result be_database->plg_result
+#define be_init_instance be_database->plg_init_instance
+#define be_cleanup be_database->plg_cleanup
+#define be_wire_import be_database->plg_wire_import
+
+ void *be_instance_info; /* If the database plugin pointed to by
+ * be_database supports more than one instance,
+ * it can use this to keep track of the
+ * multiple instances. */
+
+ char *be_name; /* The mapping tree and command line utils
+ * refer to backends by name. */
+ int be_mapped; /* True if the be is represented by a node
+ * in the mapping tree. */
+
+ /*struct slapdplugin *be_plugin_list[PLUGIN_LIST_BACKEND_MAX]; list of plugins */
+
+ int be_delete_on_exit; /* marks db for deletion - used to remove changelog*/
+ int be_state; /* indicates current database state */
+ PRLock *be_state_lock; /* lock under which to modify the state */
+
+ int be_flags; /* misc properties. See BE_FLAG_xxx defined in slapi-private.h */
+ PRRWLock *be_lock;
+ PRRWLock *vlvSearchList_lock;
+ void *vlvSearchList;
+} backend;
+
+enum
+{
+ BE_STATE_STOPPED = 1, /* backend is initialized but not started */
+ BE_STATE_STARTED, /* backend is started */
+ BE_STATE_CLEANED, /* backend was cleaned up */
+ BE_STATE_DELETED /* backend is removed */
+};
+
+struct conn;
+struct op;
+
+typedef void (*result_handler)( struct conn *, struct op *, int, char *,
+ char *, int, struct berval ** );
+typedef int (*search_entry_handler)( Slapi_Backend *, struct conn *, struct op *,
+ struct slapi_entry * );
+typedef int (*search_referral_handler)( Slapi_Backend *, struct conn *, struct op *,
+ struct berval ** );
+typedef CSN * (*csngen_handler)( Slapi_PBlock *pb, const CSN *basecsn );
+typedef int (*replica_attr_handler)( Slapi_PBlock *pb, const char *type, void **value );
+
+/*
+ * LDAP Operation results.
+ */
+typedef struct slapi_operation_results
+{
+ unsigned long operation_type;
+
+ int opreturn;
+
+ LDAPControl **result_controls;/* ctrls to be returned w/result */
+
+ int result_code;
+ char* result_text;
+ char* result_matched;
+
+ union
+ {
+ struct bind_results
+ {
+ struct berval *bind_ret_saslcreds; /* v3 serverSaslCreds */
+ } r_bind;
+
+ struct search_results
+ {
+ /*
+ * Search results set - opaque cookie passed between backend
+ * and frontend to iterate over search results.
+ */
+ void *search_result_set;
+ /* Next entry returned from iterating */
+ Slapi_Entry *search_result_entry;
+ /* opaque pointer owned by the backend. Used in searches with
+ * lookahead */
+ void *opaque_backend_ptr;
+ /* number of entries sent in response to this search request */
+ int nentries;
+ /* Any referrals encountered during the search */
+ struct berval **search_referrals;
+ } r_search;
+
+ struct extended_results
+ {
+ char *exop_ret_oid;
+ struct berval *exop_ret_value;
+ } r_extended;
+ } r;
+} slapi_operation_results;
+
+/*
+ * represents an operation pending from an ldap client
+ */
+typedef struct op {
+ BerElement *o_ber; /* ber of the request */
+ long o_msgid; /* msgid of the request */
+ unsigned long o_tag; /* tag of the request */
+ time_t o_time; /* time op was initiated */
+ PRIntervalTime o_interval; /* precise time op was initiated */
+ int o_isroot; /* requestor is manager */
+ Slapi_DN o_sdn; /* dn bound when op was initiated */
+ char *o_authtype; /* auth method used to bind dn */
+ int o_opid; /* id of this operation */
+ int o_connid; /* id of conn initiating this op; for logging only */
+ void *o_handler_data;
+ result_handler o_result_handler;
+ search_entry_handler o_search_entry_handler;
+ search_referral_handler o_search_referral_handler;
+ csngen_handler o_csngen_handler;
+ replica_attr_handler o_replica_attr_handler;
+ struct op *o_next; /* next operation pending */
+ int o_status; /* status (see SLAPI_OP_STATUS_... below */
+ char **o_searchattrs;/* original attr names requested */ /* JCM - Search Param */
+ unsigned long o_flags; /* flags for this operation */
+ void *o_extension; /* plugins are able to extend the Operation object */
+ Slapi_DN *o_target_spec; /* used to decide which plugins should be called for the operation */
+ unsigned long o_abandoned_op; /* operation abandoned by this operation - used to decide which plugins to invoke */
+ struct slapi_operation_parameters o_params;
+ struct slapi_operation_results o_results;
+} Operation;
+
+/*
+ * Operation status (o_status) values.
+ * The normal progression is from PROCESSING to RESULT_SENT, with
+ * WILL_COMPLETE as an optional intermediate state.
+ * For operations that are abandoned, the progression is from PROCESSING
+ * to ABANDONED.
+ */
+#define SLAPI_OP_STATUS_PROCESSING 0 /* the normal state */
+#define SLAPI_OP_STATUS_ABANDONED 1 /* op. has been abandoned */
+#define SLAPI_OP_STATUS_WILL_COMPLETE 2 /* no more abandon checks
+ will be done */
+#define SLAPI_OP_STATUS_RESULT_SENT 3 /* result has been sent to the
+ client (or we tried to do
+ so and failed) */
+
+/*
+ * represents a connection from an ldap client
+ */
+
+struct Conn_Private;
+typedef struct Conn_private Conn_private;
+struct _sasl_io_private;
+typedef struct _sasl_io_private sasl_io_private;
+
+typedef struct conn {
+ Sockbuf *c_sb; /* ber connection stuff */
+ int c_sd; /* the actual socket descriptor */
+ int c_ldapversion; /* version of LDAP protocol */
+ char *c_dn; /* current DN bound to this conn */
+ int c_isroot; /* c_dn was rootDN at time of bind? */
+ int c_isreplication_session; /* this connection is a replication session */
+ char *c_authtype; /* auth method used to bind c_dn */
+ char *c_external_dn; /* client DN of this SSL session */
+ char *c_external_authtype; /* used for c_external_dn */
+ PRNetAddr *cin_addr; /* address of client on this conn */
+ PRNetAddr *cin_destaddr; /* address client connected to */
+ struct berval **c_domain; /* DNS names of client */
+ Operation *c_ops; /* list of pending operations */
+ int c_gettingber; /* in the middle of ber_get_next */
+ BerElement *c_currentber; /* ber we're getting */
+ time_t c_starttime; /* when the connection was opened */
+ int c_connid; /* id of this connection for stats*/
+ int c_opsinitiated; /* # ops initiated/next op id */
+ PRInt32 c_opscompleted; /* # ops completed */
+ PRInt32 c_threadnumber; /* # threads used in this conn */
+ int c_refcnt; /* # ops refering to this conn */
+ PRLock *c_mutex; /* protect each conn structure */
+ PRLock *c_pdumutex; /* only write one pdu at a time */
+ time_t c_idlesince; /* last time of activity on conn */
+ Conn_private *c_private; /* data which is not shared outside*/
+ /* connection.c */
+ int c_flags; /* Misc flags used only for SSL */
+ /* status currently */
+ int c_needpw; /* need new password */
+ CERTCertificate *c_client_cert; /* Client's Cert */
+ PRFileDesc * c_prfd; /* NSPR 2.1 FileDesc */
+ int c_ci; /* An index into the Connection array. For printing. */
+ int c_fdi; /* An index into the FD array. The FD this connection is using. */
+ struct conn * c_next; /* Pointer to the next and previous */
+ struct conn * c_prev; /* active connections in the table*/
+ Slapi_Backend *c_bi_backend; /* which backend is doing the import */
+ void *c_extension; /* plugins are able to extend the Connection object */
+ void *c_sasl_conn; /* sasl library connection sasl_conn_t */
+ sasl_io_private *c_sasl_io_private; /* Private data for SASL I/O Layer */
+ int c_enable_sasl_io; /* Flag to tell us to enable SASL I/O on the next read */
+ int c_sasl_io; /* Flag to tell us to enable SASL I/O on the next read */
+} Connection;
+#define CONN_FLAG_SSL 1 /* Is this connection an SSL connection or not ?
+ * Used to direct I/O code when SSL is handled differently
+ */
+#define CONN_FLAG_CLOSING 2 /* If this flag is set, then the connection has
+ * been marked for closing by a worker thread
+ * and the listener thread should close it. */
+#define CONN_FLAG_IMPORT 4 /* This connection has begun a bulk import
+ * (aka "fast replica init" aka "wire import"),
+ * so it can only accept adds & ext-ops.
+ */
+
+
+#define CONN_FLAG_SASL_CONTINUE 8 /* We're in a multi-stage sasl bind */
+
+#define CONN_FLAG_START_TLS 16 /* Flag set when an SSL connection is so after an
+ * Start TLS request operation.
+ */
+
+
+
+#define START_TLS_OID "1.3.6.1.4.1.1466.20037"
+
+
+#ifndef _WIN32
+#define SLAPD_POLL_FLAGS (POLLIN)
+#else
+#define SLAPD_POLL_FLAGS (PR_POLL_READ)
+#endif
+
+typedef struct slapi_pblock {
+ /* common */
+ Slapi_Backend *pb_backend;
+ Connection *pb_conn;
+ Operation *pb_op;
+ struct slapdplugin *pb_plugin; /* plugin being called */
+ int pb_opreturn;
+ void* pb_object; /* points to data private to plugin */
+ IFP pb_destroy_fn;
+ int pb_requestor_isroot;
+ /* config file */
+ char *pb_config_fname;
+ int pb_config_lineno;
+ int pb_config_argc;
+ char **pb_config_argv;
+
+ /* [pre|post]add arguments */
+ struct slapi_entry *pb_target_entry; /* JCM - Duplicated */
+ struct slapi_entry *pb_existing_dn_entry;
+ struct slapi_entry *pb_existing_uniqueid_entry;
+ struct slapi_entry *pb_parent_entry;
+ struct slapi_entry *pb_newparent_entry;
+
+ /* state of entry before and after add/delete/modify/moddn/modrdn */
+ struct slapi_entry *pb_pre_op_entry;
+ struct slapi_entry *pb_post_op_entry;
+ /* seq access arguments */
+ int pb_seq_type;
+ char *pb_seq_attrname;
+ char *pb_seq_val;
+ /* ldif2db arguments */
+ char *pb_ldif_file;
+ int pb_removedupvals;
+ char **pb_db2index_attrs;
+ int pb_ldif2db_noattrindexes;
+ /* db2ldif arguments */
+ int pb_ldif_printkey;
+ /* ldif2db/db2ldif/db2bak/bak2db args */
+ char *pb_instance_name;
+ Slapi_Task *pb_task;
+ int pb_task_flags;
+ /* matching rule arguments */
+ mrFilterMatchFn pb_mr_filter_match_fn;
+ IFP pb_mr_filter_index_fn;
+ IFP pb_mr_filter_reset_fn;
+ IFP pb_mr_index_fn;
+ char* pb_mr_oid;
+ char* pb_mr_type;
+ struct berval* pb_mr_value;
+ struct berval** pb_mr_values;
+ struct berval** pb_mr_keys;
+ unsigned int pb_mr_filter_reusable;
+ int pb_mr_query_operator;
+ unsigned int pb_mr_usage;
+
+ /* arguments for password storage scheme (kexcoff) */
+ char *pb_pwd_storage_scheme_user_passwd;
+ char *pb_pwd_storage_scheme_db_passwd;
+
+ /* controls we know about */
+ int pb_managedsait;
+
+ /* additional fields for plugin_internal_ldap_ops */
+ /* result code of internal ldap_operation */
+ int pb_internal_op_result;
+ /* pointer to array of results returned on search */
+ Slapi_Entry **pb_plugin_internal_search_op_entries;
+ char **pb_plugin_internal_search_op_referrals;
+ void *pb_plugin_identity; /* identifies plugin for internal operation */
+ void *pb_parent_txn; /* parent transaction ID */
+ void *pb_txn; /* transaction ID */
+
+ /* Size of the database on disk, in kilobytes */
+ unsigned int pb_dbsize;
+
+ /* THINGS BELOW THIS LINE EXIST ONLY IN SLAPI v2 (slapd 4.0+) */
+
+ /* ldif2db: array of files to import all at once */
+ char **pb_ldif_files;
+
+ char **pb_ldif_include;
+ char **pb_ldif_exclude;
+ int pb_ldif_dump_replica;
+ int pb_ldif_dump_uniqueid; /* dump uniqueid during db2ldif */
+ int pb_ldif_generate_uniqueid; /* generate uniqueid during db2ldif */
+/* JCMREPL int pb_ldif_load_state; */
+ char* pb_ldif_namespaceid; /* used for name based uniqueid generation */
+ int pb_ldif_encrypt; /* used to enable encrypt/decrypt on import and export */
+ /*
+ * notes to log with RESULT line in the access log
+ * these are actually stored as a bitmap; see slapi-plugin.h for
+ * defined notes.
+ */
+ unsigned int pb_operation_notes;
+ /*
+ * slapd command line arguments
+ */
+ int pb_slapd_argc;
+ char** pb_slapd_argv;
+ char *pb_slapd_configdir; /* the config directory passed to slapd on the command line */
+ LDAPControl **pb_ctrls_arg; /* allows to pass controls as arguments before
+ operation object is created */
+ int pb_dse_dont_add_write; /* if true, the dse is not written when an entry is added */
+ int pb_dse_add_merge; /* if true, if a duplicate entry is found when adding, the
+ new values are merged into the old entry */
+ int pb_dse_dont_check_dups; /* if false, use the "enhanced" version of str2entry to catch
+ more errors when adding dse entries; this can only be done
+ after the schema and syntax and matching rule plugins are
+ running */
+ int pb_dse_is_primary_file; /* for read callbacks: non-zero for primary file */
+ int pb_schema_user_defined_only; /* return user defined schema only */
+
+ /* NEW in 5.0 for getting back the backend result in frontend */
+ int pb_result_code; /* operation result code */
+ char * pb_result_text; /* result text when available */
+ char * pb_result_matched; /* macthed dn when NO SUCH OBJECT error */
+ int pb_nentries; /* number of entries to be returned */
+ struct berval **urls; /* urls of referrals to be returned */
+
+ /*
+ * wire import (fast replica init) arguments
+ */
+ struct slapi_entry *pb_import_entry;
+ int pb_import_state;
+
+ int pb_destroy_content; /* flag to indicate that pblock content should be
+ destroyed when pblock is destroyed */
+ int pb_dse_reapply_mods; /* if true, dse_modify will reapply mods after modify callback */
+ char * pb_urp_naming_collision_dn; /* replication naming conflict removal */
+ char * pb_urp_tombstone_uniqueid; /* replication change tombstone */
+ int pb_server_running; /* indicate that server is running */
+ int pb_backend_count; /* instance count involved in the op */
+
+ /* For password policy control */
+ int pb_pwpolicy_ctrl;
+} slapi_pblock;
+
+/* The referral element */
+typedef struct ref {
+ char *ref_dn; /* The DN of the entry that contains the referral */
+ struct berval *ref_referral; /* The referral. It looks like: ldap://host:port */
+ int ref_reads; /* 1 if refer searches, 0 else */
+ int ref_writes; /* 1 if refer modifications, 0 else */
+} Ref;
+
+/* The head of the referral array. */
+typedef struct ref_array {
+ rwl *ra_rwlock; /* Read-write lock struct to protect this thing */
+ int ra_size; /* The size of this puppy (NOT the number of entries)*/
+ int ra_nextindex; /* The next free index */
+ int ra_readcount; /* The number of copyingfroms in the list */
+ Ref **ra_refs; /* The array of referrals*/
+} Ref_Array;
+
+#define GR_LOCK_READ() grefs->ra_rwlock->rwl_acquire_read_lock( grefs->ra_rwlock)
+#define GR_UNLOCK_READ() grefs->ra_rwlock->rwl_relinquish_read_lock( grefs->ra_rwlock )
+#define GR_LOCK_WRITE() grefs->ra_rwlock->rwl_acquire_write_lock( grefs->ra_rwlock )
+#define GR_UNLOCK_WRITE() grefs->ra_rwlock->rwl_relinquish_write_lock( grefs->ra_rwlock )
+
+/*
+ * This structure is used to pass a pair of port numbers to the daemon
+ * function. The daemon is the root of a forked thread.
+ */
+
+typedef struct daemon_ports_s {
+ int n_port;
+ int s_port;
+ PRNetAddr n_listenaddr;
+ PRNetAddr s_listenaddr;
+#if defined( XP_WIN32 )
+ int n_socket;
+ int s_socket_native;
+#else
+ PRFileDesc *n_socket;
+#endif
+ PRFileDesc *s_socket;
+} daemon_ports_t;
+
+
+/* Definition for plugin syntax compare routine */
+typedef int (*value_compare_fn_type)(const struct berval *,const struct berval *);
+
+#include "pw.h"
+
+#include "proto-slap.h"
+LDAPMod** entry2mods(Slapi_Entry *, LDAPMod **, int *, int);
+
+/* SNMP Variables */
+struct snmp_ops_tbl_t{
+ PRInt32 *dsAnonymousBinds;
+ PRInt32 *dsUnAuthBinds;
+ PRInt32 *dsSimpleAuthBinds;
+ PRInt32 *dsStrongAuthBinds;
+ PRInt32 *dsBindSecurityErrors;
+ PRInt32 *dsInOps;
+ PRInt32 *dsReadOps;
+ PRInt32 *dsCompareOps;
+ PRInt32 *dsAddEntryOps;
+ PRInt32 *dsRemoveEntryOps;
+ PRInt32 *dsModifyEntryOps;
+ PRInt32 *dsModifyRDNOps;
+ PRInt32 *dsListOps;
+ PRInt32 *dsSearchOps;
+ PRInt32 *dsOneLevelSearchOps;
+ PRInt32 *dsWholeSubtreeSearchOps;
+ PRInt32 *dsReferrals;
+ PRInt32 *dsChainings;
+ PRInt32 *dsSecurityErrors;
+ PRInt32 *dsErrors;
+ PRInt32 *dsConnections; /* Number of currently connected clients */
+ PRInt32 *dsConnectionSeq; /* Monotonically increasing number bumped on each new conn est */
+ PRInt32 *dsBytesRecv; /* Count of bytes read from clients */
+ PRInt32 *dsBytesSent; /* Count of bytes sent to clients */
+ PRInt32 *dsEntriesReturned;
+ PRInt32 *dsReferralsReturned;
+};
+
+struct snmp_entries_tbl_t{
+ /* entries table */
+ PRInt32 *dsMasterEntries;
+ PRInt32 *dsCopyEntries;
+ PRInt32 *dsCacheEntries;
+ PRInt32 *dsCacheHits;
+ PRInt32 *dsSlaveHits;
+};
+
+struct snmp_int_tbl_t{
+
+ /* interaction table */
+ PRInt32 *dsIntIndex;
+ char *dsName;
+ time_t *dsTimeOfCreation;
+ time_t *dsTimeOfLastAttempt;
+ time_t *dsTimeOfLastSuccess;
+ PRInt32 *dsFailuresSinceLastSuccess;
+ PRInt32 *dsFailures;
+ PRInt32 *dsSuccesses;
+ char *dsURL;
+};
+
+/* operation statistics */
+struct snmp_vars_t{
+ struct snmp_ops_tbl_t ops_tbl;
+ struct snmp_entries_tbl_t entries_tbl;
+ struct snmp_int_tbl_t int_tbl[NUM_SNMP_INT_TBL_ROWS];
+};
+
+#define ENTRY_POINT_PS_WAKEUP_ALL 102
+#define ENTRY_POINT_PS_SERVICE 105
+#define ENTRY_POINT_DISCONNECT_SERVER 107
+#define ENTRY_POINT_SLAPD_SSL_CLIENT_INIT 108
+#define ENTRY_POINT_SLAPD_SSL_INIT 109
+#define ENTRY_POINT_SLAPD_SSL_INIT2 110
+
+typedef void (*ps_wakeup_all_fn_ptr)( void );
+typedef void (*ps_service_fn_ptr)(Slapi_Entry *, Slapi_Entry *, int, int );
+typedef char *(*get_config_dn_fn_ptr)();
+typedef void (*get_disconnect_server_fn_ptr)(Connection *conn, int opconnid, int opid, PRErrorCode reason, PRInt32 error );
+typedef int (*slapd_SSL_client_init_fn_ptr)( void );
+typedef int (*modify_config_dse_fn_ptr)( Slapi_PBlock *pb );
+typedef int (*slapd_ssl_init_fn_ptr)( void );
+typedef int (*slapd_ssl_init_fn_ptr2)( PRFileDesc **s, int StartTLS);
+
+/*
+ * A structure of entry points in the NT exe which need
+ * to be available to DLLs.
+ */
+typedef struct _slapdEntryPoints {
+ caddr_t sep_ps_wakeup_all;
+ caddr_t sep_ps_service;
+ caddr_t sep_disconnect_server;
+ caddr_t sep_slapd_SSL_client_init;
+ caddr_t sep_slapd_ssl_init;
+ caddr_t sep_slapd_ssl_init2;
+} slapdEntryPoints;
+
+#if defined( XP_WIN32 )
+#define DLL_IMPORT_DATA _declspec( dllimport )
+#else
+#define DLL_IMPORT_DATA
+#endif
+
+/* Log types */
+#define SLAPD_ACCESS_LOG 0x1
+#define SLAPD_ERROR_LOG 0x2
+#define SLAPD_AUDIT_LOG 0x4
+
+#define CONFIG_DATABASE_ATTRIBUTE "nsslapd-database"
+#define CONFIG_PLUGIN_ATTRIBUTE "nsslapd-plugin"
+#define CONFIG_SIZELIMIT_ATTRIBUTE "nsslapd-sizelimit"
+#define CONFIG_TIMELIMIT_ATTRIBUTE "nsslapd-timelimit"
+#define CONFIG_SUFFIX_ATTRIBUTE "nsslapd-suffix"
+#define CONFIG_READONLY_ATTRIBUTE "nsslapd-readonly"
+#define CONFIG_REFERRAL_ATTRIBUTE "nsslapd-referral"
+#define CONFIG_OBJECTCLASS_ATTRIBUTE "nsslapd-objectclass"
+#define CONFIG_ATTRIBUTE_ATTRIBUTE "nsslapd-attribute"
+#define CONFIG_SCHEMACHECK_ATTRIBUTE "nsslapd-schemacheck"
+#define CONFIG_DS4_COMPATIBLE_SCHEMA_ATTRIBUTE "nsslapd-ds4-compatible-schema"
+#define CONFIG_SCHEMA_IGNORE_TRAILING_SPACES "nsslapd-schema-ignore-trailing-spaces"
+#define CONFIG_SCHEMAREPLACE_ATTRIBUTE "nsslapd-schemareplace"
+#define CONFIG_LOGLEVEL_ATTRIBUTE "nsslapd-errorlog-level"
+#define CONFIG_ACCESSLOGLEVEL_ATTRIBUTE "nsslapd-accesslog-level"
+#define CONFIG_ACCESSLOG_MODE_ATTRIBUTE "nsslapd-accesslog-mode"
+#define CONFIG_ERRORLOG_MODE_ATTRIBUTE "nsslapd-errorlog-mode"
+#define CONFIG_AUDITLOG_MODE_ATTRIBUTE "nsslapd-auditlog-mode"
+#define CONFIG_ACCESSLOG_MAXNUMOFLOGSPERDIR_ATTRIBUTE "nsslapd-accesslog-maxlogsperdir"
+#define CONFIG_ERRORLOG_MAXNUMOFLOGSPERDIR_ATTRIBUTE "nsslapd-errorlog-maxlogsperdir"
+#define CONFIG_AUDITLOG_MAXNUMOFLOGSPERDIR_ATTRIBUTE "nsslapd-auditlog-maxlogsperdir"
+#define CONFIG_ACCESSLOG_MAXLOGSIZE_ATTRIBUTE "nsslapd-accesslog-maxlogsize"
+#define CONFIG_ERRORLOG_MAXLOGSIZE_ATTRIBUTE "nsslapd-errorlog-maxlogsize"
+#define CONFIG_AUDITLOG_MAXLOGSIZE_ATTRIBUTE "nsslapd-auditlog-maxlogsize"
+#define CONFIG_ACCESSLOG_LOGROTATIONSYNCENABLED_ATTRIBUTE "nsslapd-accesslog-logrotationsync-enabled"
+#define CONFIG_ERRORLOG_LOGROTATIONSYNCENABLED_ATTRIBUTE "nsslapd-errorlog-logrotationsync-enabled"
+#define CONFIG_AUDITLOG_LOGROTATIONSYNCENABLED_ATTRIBUTE "nsslapd-auditlog-logrotationsync-enabled"
+#define CONFIG_ACCESSLOG_LOGROTATIONSYNCHOUR_ATTRIBUTE "nsslapd-accesslog-logrotationsynchour"
+#define CONFIG_ERRORLOG_LOGROTATIONSYNCHOUR_ATTRIBUTE "nsslapd-errorlog-logrotationsynchour"
+#define CONFIG_AUDITLOG_LOGROTATIONSYNCHOUR_ATTRIBUTE "nsslapd-auditlog-logrotationsynchour"
+#define CONFIG_ACCESSLOG_LOGROTATIONSYNCMIN_ATTRIBUTE "nsslapd-accesslog-logrotationsyncmin"
+#define CONFIG_ERRORLOG_LOGROTATIONSYNCMIN_ATTRIBUTE "nsslapd-errorlog-logrotationsyncmin"
+#define CONFIG_AUDITLOG_LOGROTATIONSYNCMIN_ATTRIBUTE "nsslapd-auditlog-logrotationsyncmin"
+#define CONFIG_ACCESSLOG_LOGROTATIONTIME_ATTRIBUTE "nsslapd-accesslog-logrotationtime"
+#define CONFIG_ERRORLOG_LOGROTATIONTIME_ATTRIBUTE "nsslapd-errorlog-logrotationtime"
+#define CONFIG_AUDITLOG_LOGROTATIONTIME_ATTRIBUTE "nsslapd-auditlog-logrotationtime"
+#define CONFIG_ACCESSLOG_LOGROTATIONTIMEUNIT_ATTRIBUTE "nsslapd-accesslog-logrotationtimeunit"
+#define CONFIG_ERRORLOG_LOGROTATIONTIMEUNIT_ATTRIBUTE "nsslapd-errorlog-logrotationtimeunit"
+#define CONFIG_AUDITLOG_LOGROTATIONTIMEUNIT_ATTRIBUTE "nsslapd-auditlog-logrotationtimeunit"
+#define CONFIG_ACCESSLOG_MAXLOGDISKSPACE_ATTRIBUTE "nsslapd-accesslog-logmaxdiskspace"
+#define CONFIG_ERRORLOG_MAXLOGDISKSPACE_ATTRIBUTE "nsslapd-errorlog-logmaxdiskspace"
+#define CONFIG_AUDITLOG_MAXLOGDISKSPACE_ATTRIBUTE "nsslapd-auditlog-logmaxdiskspace"
+#define CONFIG_ACCESSLOG_MINFREEDISKSPACE_ATTRIBUTE "nsslapd-accesslog-logminfreediskspace"
+#define CONFIG_ERRORLOG_MINFREEDISKSPACE_ATTRIBUTE "nsslapd-errorlog-logminfreediskspace"
+#define CONFIG_AUDITLOG_MINFREEDISKSPACE_ATTRIBUTE "nsslapd-auditlog-logminfreediskspace"
+#define CONFIG_ACCESSLOG_LOGEXPIRATIONTIME_ATTRIBUTE "nsslapd-accesslog-logexpirationtime"
+#define CONFIG_ERRORLOG_LOGEXPIRATIONTIME_ATTRIBUTE "nsslapd-errorlog-logexpirationtime"
+#define CONFIG_AUDITLOG_LOGEXPIRATIONTIME_ATTRIBUTE "nsslapd-auditlog-logexpirationtime"
+#define CONFIG_ACCESSLOG_LOGEXPIRATIONTIMEUNIT_ATTRIBUTE "nsslapd-accesslog-logexpirationtimeunit"
+#define CONFIG_ERRORLOG_LOGEXPIRATIONTIMEUNIT_ATTRIBUTE "nsslapd-errorlog-logexpirationtimeunit"
+#define CONFIG_AUDITLOG_LOGEXPIRATIONTIMEUNIT_ATTRIBUTE "nsslapd-auditlog-logexpirationtimeunit"
+#define CONFIG_ACCESSLOG_LOGGING_ENABLED_ATTRIBUTE "nsslapd-accesslog-logging-enabled"
+#define CONFIG_ERRORLOG_LOGGING_ENABLED_ATTRIBUTE "nsslapd-errorlog-logging-enabled"
+#define CONFIG_AUDITLOG_LOGGING_ENABLED_ATTRIBUTE "nsslapd-auditlog-logging-enabled"
+#define CONFIG_ROOTDN_ATTRIBUTE "nsslapd-rootdn"
+#define CONFIG_ROOTPW_ATTRIBUTE "nsslapd-rootpw"
+#define CONFIG_ROOTPWSTORAGESCHEME_ATTRIBUTE "nsslapd-rootpwstoragescheme"
+#define CONFIG_AUDITFILE_ATTRIBUTE "nsslapd-auditlog"
+#define CONFIG_LASTMOD_ATTRIBUTE "nsslapd-lastmod"
+#define CONFIG_INCLUDE_ATTRIBUTE "nsslapd-include"
+#define CONFIG_DYNAMICCONF_ATTRIBUTE "nsslapd-dynamicconf"
+#define CONFIG_USEROC_ATTRIBUTE "nsslapd-useroc"
+#define CONFIG_USERAT_ATTRIBUTE "nsslapd-userat"
+#define CONFIG_SVRTAB_ATTRIBUTE "nsslapd-svrtab"
+#ifndef _WIN32
+#define CONFIG_LOCALUSER_ATTRIBUTE "nsslapd-localuser"
+#endif /* !_WIN32 */
+#define CONFIG_LOCALHOST_ATTRIBUTE "nsslapd-localhost"
+#define CONFIG_PORT_ATTRIBUTE "nsslapd-port"
+#define CONFIG_WORKINGDIR_ATTRIBUTE "nsslapd-workingdir"
+#define CONFIG_LISTENHOST_ATTRIBUTE "nsslapd-listenhost"
+#define CONFIG_SECURITY_ATTRIBUTE "nsslapd-security"
+#define CONFIG_SSL3CIPHERS_ATTRIBUTE "nsslapd-SSL3ciphers"
+#define CONFIG_ACCESSLOG_ATTRIBUTE "nsslapd-accesslog"
+#define CONFIG_ERRORLOG_ATTRIBUTE "nsslapd-errorlog"
+#define CONFIG_INSTANCEDIR_ATTRIBUTE "nsslapd-instancedir"
+#define CONFIG_SECUREPORT_ATTRIBUTE "nsslapd-securePort"
+#define CONFIG_SECURELISTENHOST_ATTRIBUTE "nsslapd-securelistenhost"
+#define CONFIG_THREADNUMBER_ATTRIBUTE "nsslapd-threadnumber"
+#define CONFIG_MAXTHREADSPERCONN_ATTRIBUTE "nsslapd-maxthreadsperconn"
+#if !defined(_WIN32) && !defined(AIX)
+#define CONFIG_MAXDESCRIPTORS_ATTRIBUTE "nsslapd-maxdescriptors"
+#endif /* !_WIN32 && ! AIX */
+#define CONFIG_CONNTABLESIZE_ATTRIBUTE "nsslapd-conntablesize"
+#define CONFIG_RESERVEDESCRIPTORS_ATTRIBUTE "nsslapd-reservedescriptors"
+#define CONFIG_IDLETIMEOUT_ATTRIBUTE "nsslapd-idletimeout"
+#define CONFIG_IOBLOCKTIMEOUT_ATTRIBUTE "nsslapd-ioblocktimeout"
+#define CONFIG_ACCESSCONTROL_ATTRIBUTE "nsslapd-accesscontrol"
+#define CONFIG_GROUPEVALNESTLEVEL_ATTRIBUTE "nsslapd-groupevalnestlevel"
+#define CONFIG_NAGLE_ATTRIBUTE "nsslapd-nagle"
+#define CONFIG_PWPOLICY_LOCAL_ATTRIBUTE "nsslapd-pwpolicy-local"
+#define CONFIG_PW_CHANGE_ATTRIBUTE "passwordChange"
+#define CONFIG_PW_MUSTCHANGE_ATTRIBUTE "passwordMustChange"
+#define CONFIG_PW_SYNTAX_ATTRIBUTE "passwordCheckSyntax"
+#define CONFIG_PW_MINLENGTH_ATTRIBUTE "passwordMinLength"
+#define CONFIG_PW_EXP_ATTRIBUTE "passwordExp"
+#define CONFIG_PW_MAXAGE_ATTRIBUTE "passwordMaxAge"
+#define CONFIG_PW_MINAGE_ATTRIBUTE "passwordMinAge"
+#define CONFIG_PW_WARNING_ATTRIBUTE "passwordWarning"
+#define CONFIG_PW_HISTORY_ATTRIBUTE "passwordHistory"
+#define CONFIG_PW_INHISTORY_ATTRIBUTE "passwordInHistory"
+#define CONFIG_PW_LOCKOUT_ATTRIBUTE "passwordLockout"
+#define CONFIG_PW_STORAGESCHEME_ATTRIBUTE "passwordStorageScheme"
+#define CONFIG_PW_MAXFAILURE_ATTRIBUTE "passwordMaxFailure"
+#define CONFIG_PW_UNLOCK_ATTRIBUTE "passwordUnlock"
+#define CONFIG_PW_LOCKDURATION_ATTRIBUTE "passwordLockoutDuration"
+#define CONFIG_PW_RESETFAILURECOUNT_ATTRIBUTE "passwordResetFailureCount"
+#define CONFIG_PW_ISGLOBAL_ATTRIBUTE "passwordIsGlobalPolicy"
+#define CONFIG_PW_GRACELIMIT_ATTRIBUTE "passwordGraceLimit"
+#define CONFIG_ACCESSLOG_BUFFERING_ATTRIBUTE "nsslapd-accesslog-logbuffering"
+#define CONFIG_CSNLOGGING_ATTRIBUTE "nsslapd-csnlogging"
+#define CONFIG_RETURN_EXACT_CASE_ATTRIBUTE "nsslapd-return-exact-case"
+#define CONFIG_RESULT_TWEAK_ATTRIBUTE "nsslapd-result-tweak"
+#define CONFIG_REFERRAL_MODE_ATTRIBUTE "nsslapd-referralmode"
+#define CONFIG_ATTRIBUTE_NAME_EXCEPTION_ATTRIBUTE "nsslapd-attribute-name-exceptions"
+#define CONFIG_MAXBERSIZE_ATTRIBUTE "nsslapd-maxbersize"
+#define CONFIG_MAX_FILTER_NEST_LEVEL_ATTRIBUTE "nsslapd-max-filter-nest-level"
+#define CONFIG_VERSIONSTRING_ATTRIBUTE "nsslapd-versionstring"
+#define CONFIG_ENQUOTE_SUP_OC_ATTRIBUTE "nsslapd-enquote-sup-oc"
+#define CONFIG_BASEDN_ATTRIBUTE "nsslapd-certmap-basedn"
+#define CONFIG_ACCESSLOG_LIST_ATTRIBUTE "nsslapd-accesslog-list"
+#define CONFIG_ERRORLOG_LIST_ATTRIBUTE "nsslapd-errorlog-list"
+#define CONFIG_AUDITLOG_LIST_ATTRIBUTE "nsslapd-auditlog-list"
+#define CONFIG_REWRITE_RFC1274_ATTRIBUTE "nsslapd-rewrite-rfc1274"
+
+#define CONFIG_CONFIG_ATTRIBUTE "nsslapd-config"
+#define CONFIG_SSLCLIENTAUTH_ATTRIBUTE "nsslapd-SSLclientAuth"
+#define CONFIG_SSL_CHECK_HOSTNAME_ATTRIBUTE "nsslapd-ssl-check-hostname"
+#define CONFIG_HASH_FILTERS_ATTRIBUTE "nsslapd-hash-filters"
+#define CONFIG_OUTBOUND_LDAP_IO_TIMEOUT_ATTRIBUTE "nsslapd-outbound-ldap-io-timeout"
+
+/* flag used to indicate that the change to the config parameter should be saved */
+#define CONFIG_APPLY 1
+
+#define CFG_LOCK_READ(cfg) cfg->cfg_rwlock->rwl_acquire_read_lock( cfg->cfg_rwlock )
+#define CFG_UNLOCK_READ(cfg) cfg->cfg_rwlock->rwl_relinquish_read_lock( cfg->cfg_rwlock )
+#define CFG_LOCK_WRITE(cfg) cfg->cfg_rwlock->rwl_acquire_write_lock( cfg->cfg_rwlock )
+#define CFG_UNLOCK_WRITE(cfg) cfg->cfg_rwlock->rwl_relinquish_write_lock( cfg->cfg_rwlock )
+
+#define REFER_MODE_OFF 0
+#define REFER_MODE_ON 1
+
+#define MAX_ALLOWED_TIME_IN_SECS 2147483647
+
+typedef struct passwordpolicyarray {
+ int pw_change; /* 1 - indicates that users are allowed to change the pwd */
+ int pw_must_change; /* 1 - indicates that users must change pwd upon reset */
+ int pw_syntax;
+ int pw_minlength;
+ int pw_exp;
+ long pw_maxage;
+ long pw_minage;
+ long pw_warning;
+ int pw_history;
+ int pw_inhistory;
+ int pw_lockout;
+ int pw_maxfailure;
+ int pw_unlock;
+ long pw_lockduration;
+ long pw_resetfailurecount;
+ int pw_gracelimit;
+} passwdPolicy;
+
+typedef struct _slapdFrontendConfig {
+ rwl *cfg_rwlock; /* read/write lock to serialize access */
+ struct pw_scheme *rootpwstoragescheme;
+ int accesscontrol;
+ int groupevalnestlevel;
+ int idletimeout;
+ int ioblocktimeout;
+ int lastmod;
+#if !defined(_WIN32) && !defined(AIX)
+ int maxdescriptors;
+#endif /* !_WIN32 && !AIX */
+ int conntablesize;
+ int maxthreadsperconn;
+ int outbound_ldap_io_timeout;
+ int nagle;
+ int port;
+ int readonly;
+ int reservedescriptors;
+ int schemacheck;
+ int ds4_compatible_schema;
+ int schema_ignore_trailing_spaces;
+ int secureport;
+ int security;
+ int SSLclientAuth;
+ int ssl_check_hostname;
+ int sizelimit;
+ int SNMPenabled;
+ char *SNMPdescription;
+ char *SNMPorganization;
+ char *SNMPlocation;
+ char *SNMPcontact;
+ int threadnumber;
+ int timelimit;
+ char *accesslog;
+ struct berval **defaultreferral;
+ char *encryptionalias;
+ char *errorlog;
+ char *listenhost;
+ char *instancedir;
+#ifndef _WIN32
+ char *localuser;
+#endif /* _WIN32 */
+ char *localhost;
+ char *rootdn;
+ char *rootpw;
+ char *securelistenhost;
+ char *srvtab;
+ char *SSL3ciphers;
+ char *userat;
+ char *useroc;
+ char *versionstring;
+ char **backendconfig;
+ char **include;
+ char **plugin;
+ struct pw_scheme *pw_storagescheme;
+
+ int pwpolicy_local;
+ int pw_is_global_policy;
+ passwdPolicy pw_policy;
+
+ /* ACCESS LOG */
+ int accesslog_logging_enabled;
+ char *accesslog_mode;
+ int accesslog_maxnumlogs;
+ int accesslog_maxlogsize;
+ int accesslog_rotationsync_enabled;
+ int accesslog_rotationsynchour;
+ int accesslog_rotationsyncmin;
+ int accesslog_rotationtime;
+ char *accesslog_rotationunit;
+ int accesslog_maxdiskspace;
+ int accesslog_minfreespace;
+ int accesslog_exptime;
+ char *accesslog_exptimeunit;
+ int accessloglevel;
+ int accesslogbuffering;
+ int csnlogging;
+
+ /* ERROR LOG */
+ int errorlog_logging_enabled;
+ char *errorlog_mode;
+ int errorlog_maxnumlogs;
+ int errorlog_maxlogsize;
+ int errorlog_rotationsync_enabled;
+ int errorlog_rotationsynchour;
+ int errorlog_rotationsyncmin;
+ int errorlog_rotationtime;
+ char *errorlog_rotationunit;
+ int errorlog_maxdiskspace;
+ int errorlog_minfreespace;
+ int errorlog_exptime;
+ char *errorlog_exptimeunit;
+ int errorloglevel;
+
+ /* AUDIT LOG */
+ char *auditlog; /* replication audit file */
+ int auditloglevel;
+ int auditlog_logging_enabled;
+ char *auditlog_mode;
+ int auditlog_maxnumlogs;
+ int auditlog_maxlogsize;
+ int auditlog_rotationsync_enabled;
+ int auditlog_rotationsynchour;
+ int auditlog_rotationsyncmin;
+ int auditlog_rotationtime;
+ char *auditlog_rotationunit;
+ int auditlog_maxdiskspace;
+ int auditlog_minfreespace;
+ int auditlog_exptime;
+ char *auditlog_exptimeunit;
+
+ int return_exact_case; /* Return attribute names with the same case
+ * as they appear in at.conf */
+
+ int result_tweak;
+ char *refer_url; /* for referral mode */
+ int refer_mode; /* for quick test */
+ int slapd_type; /* Directory type; Full or Lite */
+
+ unsigned long maxbersize; /* Maximum BER element size we'll accept */
+ int max_filter_nest_level;/* deepest nested filter we will accept */
+ int enquote_sup_oc; /* put single quotes around an oc's
+ superior oc in cn=schema */
+
+ char *certmap_basedn; /* Default Base DN for certmap */
+
+ char *workingdir; /* full path of directory before detach */
+ char *configdir; /* full path name of directory containing configuration files */
+ int attrname_exceptions; /* if true, allow questionable attribute names */
+ int rewrite_rfc1274; /* return attrs for both v2 and v3 names */
+ char *schemareplace; /* see CONFIG_SCHEMAREPLACE_* #defines below */
+} slapdFrontendConfig_t;
+
+#define SLAPD_FULL 0
+#define SLAPD_LITE 1
+
+/* possible values for slapdFrontendConfig_t.schemareplace */
+#define CONFIG_SCHEMAREPLACE_STR_OFF "off"
+#define CONFIG_SCHEMAREPLACE_STR_ON "on"
+#define CONFIG_SCHEMAREPLACE_STR_REPLICATION_ONLY "replication-only"
+
+
+slapdFrontendConfig_t *getFrontendConfig();
+
+/* LP: NO_TIME cannot be -1, it generates wrong GeneralizedTime
+ * And causes some errors on AIX also
+ */
+/* #if defined( XP_WIN32 ) */
+#define NO_TIME (time_t)0 /* cannot be -1, NT's localtime( -1 ) returns NULL */
+/* #else */
+/* #define NO_TIME (time_t)-1 / * a value that time() does not return */
+/* #endif */
+#define NOT_FIRST_TIME (time_t)1 /* not the first logon */
+#define SLAPD_END_TIME (time_t)2147483647 /* (2^31)-1, in 2038 */
+
+extern char *attr_dataversion;
+#define ATTR_DATAVERSION "dataVersion"
+#define ATTR_WITH_DIRSTRING_SYNTAX "cn"
+
+#define SLAPD_SNMP_UPDATE_INTERVAL (10 * 1000) /* 10 seconds */
+
+#define LDAP_AUTH_KRBV41 0x81L
+#define LDAP_AUTH_KRBV42 0x82L
+
+/* for timing certain operations */
+#ifdef USE_TIMERS
+
+#ifndef _WIN32
+#include <sys/time.h>
+#ifdef LINUX
+#define GTOD(t) gettimeofday(t, NULL)
+#else
+#define GTOD(t) gettimeofday(t)
+#endif
+#define TIMER_DECL(x) struct timeval x##_start, x##_end
+#define TIMER_START(x) GTOD(&(x##_start))
+#define TIMER_STOP(x) GTOD(&(x##_end))
+#define TIMER_GET_US(x) (unsigned)(((x##_end).tv_sec - (x##_start).tv_sec) * 100000L + \
+ ((x##_end).tv_usec - (x##_start).tv_usec))
+#else
+#define TIMER_DECL(x) LARGE_INTEGER x##_freq, x##_start, x##_end
+#define TIMER_START(x) do { \
+ QueryPerformanceFrequency(&(x##_freq)); \
+ QueryPerformanceCounter(&(x##_start)); \
+} while(0)
+#define TIMER_STOP(x) QueryPerformanceCounter(&(x##_end))
+#define TIMER_GET_US(x) (unsigned int)((x##_end.QuadPart - x##_start.QuadPart) / x##_freq.QuadPart)
+#endif /* _WIN32 */
+
+#define TIMER_AVG_DECL(x) \
+ TIMER_DECL(x); static unsigned int x##_total, x##_count
+#define TIMER_AVG_START(x) \
+ TIMER_START(x)
+#define TIMER_AVG_STOP(x) do { \
+ TIMER_STOP(x); \
+ if (TIMER_GET_US(x) < 10000) { \
+ x##_count++; \
+ x##_total += TIMER_GET_US(x); \
+ } \
+} while (0)
+#define TIMER_AVG_GET_US(x) (unsigned int)(x##_total / x##_count)
+#define TIMER_AVG_CHECK(x) do { \
+ if (x##_count >= 1000) { \
+ printf("timer %s: %d\n", #x, TIMER_AVG_GET_US(x)); \
+ x##_total = x##_count = 0; \
+ } \
+} while (0)
+
+#else
+
+#define TIMER_DECL(x)
+#define TIMER_START(x)
+#define TIMER_STOP(x)
+#define TIMER_GET_US(x) 0L
+
+#endif /* USE_TIMERS */
+
+#define LDIF_CSNPREFIX_MAXLENGTH 6 /* sizeof(xxcsn-) */
+
+#include "intrinsics.h"
+
+/* task flag (pb_task_flags)*/
+#define TASK_RUNNING_AS_TASK 0x0
+#define TASK_RUNNING_FROM_COMMANDLINE 0x1
+
+/* printkey: import & export */
+#define EXPORT_PRINTKEY 0x1
+#define EXPORT_NOWRAP 0x2
+#define EXPORT_APPENDMODE 0x4
+#define EXPORT_MINIMAL_ENCODING 0x8
+#define EXPORT_ID2ENTRY_ONLY 0x10
+#define EXPORT_NOVERSION 0x20
+#define EXPORT_APPENDMODE_1 0x40
+#define EXPORT_INTERNAL 0x100
+
+#define MTN_CONTROL_USE_ONE_BACKEND_OID "2.16.840.1.113730.3.4.14"
+#define MTN_CONTROL_USE_ONE_BACKEND_EXT_OID "2.16.840.1.113730.3.4.20"
+
+/* virtualListViewError is a relatively new concept that was added long
+ * after we implemented VLV. Until added to LDAP SDK, we define
+ * virtualListViewError here. Once it's added, this define would go away. */
+#ifndef LDAP_VIRTUAL_LIST_VIEW_ERROR
+#define LDAP_VIRTUAL_LIST_VIEW_ERROR 0x4C /* 76 */
+#endif
+
+#endif /* _slap_h_ */
diff --git a/ldap/servers/slapd/slapd.lite.key b/ldap/servers/slapd/slapd.lite.key
new file mode 100644
index 00000000..0ff28a14
--- /dev/null
+++ b/ldap/servers/slapd/slapd.lite.key
@@ -0,0 +1,8 @@
+#
+# BEGIN COPYRIGHT BLOCK
+# Copyright 2001 Sun Microsystems, Inc.
+# Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+# All rights reserved.
+# END COPYRIGHT BLOCK
+#
+key:1877108
diff --git a/ldap/servers/slapd/slapd.normal.key b/ldap/servers/slapd/slapd.normal.key
new file mode 100644
index 00000000..0f1ce157
--- /dev/null
+++ b/ldap/servers/slapd/slapd.normal.key
@@ -0,0 +1,9 @@
+#
+# BEGIN COPYRIGHT BLOCK
+# Copyright 2001 Sun Microsystems, Inc.
+# Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+# All rights reserved.
+# END COPYRIGHT BLOCK
+#
+key:2003722
+
diff --git a/ldap/servers/slapd/slapd_plhash.c b/ldap/servers/slapd/slapd_plhash.c
new file mode 100644
index 00000000..0206440e
--- /dev/null
+++ b/ldap/servers/slapd/slapd_plhash.c
@@ -0,0 +1,59 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+
+/*
+ This file contains a function which augments the standard nspr PL_HashTable
+ api. The problem is that the hash table lookup function in the standard NSPR
+ actually modifies the hash table being searched, which means that it cannot be
+ used with read locks in a multi threaded environment. This function is a
+ lookup function which is guaranteed not to modify the hash table passed in,
+ so that it can be used with read locks.
+*/
+
+#include "plhash.h"
+
+/*
+** Multiplicative hash, from Knuth 6.4.
+*/
+#define GOLDEN_RATIO 0x9E3779B9U
+
+PR_IMPLEMENT(PLHashEntry **)
+PL_HashTableRawLookup_const(PLHashTable *ht, PLHashNumber keyHash, const void *key)
+{
+ PLHashEntry *he, **hep;
+ PLHashNumber h;
+
+#ifdef HASHMETER
+ ht->nlookups++;
+#endif
+ h = keyHash * GOLDEN_RATIO;
+ h >>= ht->shift;
+ hep = &ht->buckets[h];
+ while ((he = *hep) != 0) {
+ if (he->keyHash == keyHash && (*ht->keyCompare)(key, he->key)) {
+ return hep;
+ }
+ hep = &he->next;
+#ifdef HASHMETER
+ ht->nsteps++;
+#endif
+ }
+ return hep;
+}
+
+PR_IMPLEMENT(void *)
+PL_HashTableLookup_const(PLHashTable *ht, const void *key)
+{
+ PLHashNumber keyHash;
+ PLHashEntry *he, **hep;
+
+ keyHash = (*ht->keyHash)(key);
+ hep = PL_HashTableRawLookup_const(ht, keyHash, key);
+ if ((he = *hep) != 0) {
+ return he->value;
+ }
+ return 0;
+}
diff --git a/ldap/servers/slapd/slapi-plugin-compat4.h b/ldap/servers/slapd/slapi-plugin-compat4.h
new file mode 100644
index 00000000..198a72c0
--- /dev/null
+++ b/ldap/servers/slapd/slapi-plugin-compat4.h
@@ -0,0 +1,132 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+
+/*
+ * Netscape Directory Server 4.x plugin API backwards compatible header file.
+ */
+#ifndef _SLAPIPLUGINCOMPAT4
+#define _SLAPIPLUGINCOMPAT4
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
+/******* Deprecated (obsolete) macros ****************************************/
+/*
+ * The SLAPI_BIND_FAIL_OR_ANONYMOUS bind function return code is deprecated.
+ * Use SLAPI_BIND_FAIL or SLAPI_BIND_ANONYMOUS instead.
+ */
+#define SLAPI_BIND_FAIL_OR_ANONYMOUS 1 /* back end should send result */
+
+
+/******* Deprecated (obsolete) pblock arg identifiers ************************/
+#define SLAPI_CONFIG_FILENAME 40 /* use config. entries instead */
+#define SLAPI_CONFIG_LINENO 41 /* use config. entries instead */
+#define SLAPI_CONFIG_ARGC 42 /* use config. entries instead */
+#define SLAPI_CONFIG_ARGV 43 /* use config. entries instead */
+#define SLAPI_CONN_AUTHTYPE 144 /* use SLAPI_CONN_AUTHTYPE */
+#define SLAPI_REQUESTOR_ISUPDATEDN SLAPI_IS_REPLICATED_OPERATION
+#define SLAPI_CONN_CLIENTIP 145 /* use SLAPI_CONN_CLIENTNETADDR */
+#define SLAPI_CONN_SERVERIP 146 /* use SLAPI_CONN_SERVERNETADDR */
+
+/******* Deprecated (obsolete) functions *************************************/
+/*
+ * Please use the new functions in slapi-plugin.h instead of the ones
+ * below that are marked SLAPI_DEPRECATED.
+ */
+#define SLAPI_DEPRECATED
+
+
+/*
+ *
+ * The following functions that deal with bervals are deprecated
+ * and their use is not recommended. For each deprecated function, you
+ * will find in slapi-plugin.h a corresponding function with a _sv
+ * extension that uses Slapi_Values instead of bervals. The new
+ * functions are more efficient than the old ones, and some of the old
+ * functions are much less efficient than they were previously.
+ */
+SLAPI_DEPRECATED int slapi_entry_attr_merge( Slapi_Entry *e, const char *type,
+ struct berval **vals );
+SLAPI_DEPRECATED int slapi_entry_add_values( Slapi_Entry *e, const char *type,
+ struct berval **vals );
+SLAPI_DEPRECATED int slapi_entry_delete_values( Slapi_Entry *e,
+ const char *type, struct berval **vals );
+SLAPI_DEPRECATED int slapi_entry_attr_replace( Slapi_Entry *e,
+ const char *type, struct berval **vals );
+SLAPI_DEPRECATED int slapi_attr_get_values( Slapi_Attr *attr,
+ struct berval ***vals );
+SLAPI_DEPRECATED int slapi_attr_get_oid( const Slapi_Attr *attr, char **oid );
+SLAPI_DEPRECATED int slapi_filter_get_type( Slapi_Filter *f, char **type );
+SLAPI_DEPRECATED int slapi_pw_find( struct berval **vals, struct berval *v );
+SLAPI_DEPRECATED int slapi_call_syntax_values2keys( void *vpi,
+ struct berval **vals, struct berval ***ivals, int ftype );
+SLAPI_DEPRECATED int slapi_call_syntax_assertion2keys_ava( void *vpi,
+ struct berval *val, struct berval ***ivals, int ftype );
+SLAPI_DEPRECATED int slapi_call_syntax_assertion2keys_sub( void *vpi,
+ char *initial, char **any, char *final, struct berval ***ivals );
+
+/*
+ * slapi_entry_attr_hasvalue() has been deprecated in favor of
+ * slapi_entry_attr_has_syntax_value() which respects the syntax of the
+ * attribute type (slapi_entry_attr_hasvalue() assumes the value is a
+ * caseIgnore ASCII string).
+ */
+SLAPI_DEPRECATED int slapi_entry_attr_hasvalue(const Slapi_Entry *e,
+ const char *type, const char *value);
+
+/*
+ * The following "internal operation" calls are deprecated. The new internal
+ * operation functions that are defined in slapi-plugin.h take a Slapi_PBlock
+ * for extensibility and support the new plugin configuration capabilities
+ * as well.
+ */
+SLAPI_DEPRECATED int slapi_search_internal_callback( const char *ibase,
+ int scope, const char *ifstr, char **attrs, int attrsonly,
+ void *callback_data, LDAPControl **controls,
+ plugin_result_callback prc, plugin_search_entry_callback psec,
+ plugin_referral_entry_callback prec );
+SLAPI_DEPRECATED int slapi_seq_callback( const char *ibase, int type,
+ char *attrname, char *val, char **attrs, int attrsonly,
+ void *callback_data, LDAPControl **controls,
+ plugin_result_callback res_callback,
+ plugin_search_entry_callback srch_callback,
+ plugin_referral_entry_callback ref_callback);
+SLAPI_DEPRECATED Slapi_PBlock *slapi_search_internal( const char *base,
+ int scope, const char *filter, LDAPControl **controls, char **attrs,
+ int attrsonly );
+SLAPI_DEPRECATED Slapi_PBlock *slapi_modify_internal( const char *dn,
+ LDAPMod **mods, LDAPControl **controls, int dummy );
+SLAPI_DEPRECATED Slapi_PBlock *slapi_add_internal( const char * dn,
+ LDAPMod **attrs, LDAPControl **controls, int dummy );
+SLAPI_DEPRECATED Slapi_PBlock *slapi_add_entry_internal( Slapi_Entry *e,
+ LDAPControl **controls, int dummy );
+SLAPI_DEPRECATED Slapi_PBlock *slapi_delete_internal( const char * dn,
+ LDAPControl **controls, int dummy );
+SLAPI_DEPRECATED Slapi_PBlock *slapi_modrdn_internal( const char * olddn,
+ const char * newrdn, int deloldrdn, LDAPControl **controls, int dummy);
+SLAPI_DEPRECATED Slapi_PBlock *slapi_rename_internal( const char *iodn,
+ const char *inewrdn, const char *inewsuperior, int deloldrdn,
+ LDAPControl **controls, int dummy);
+
+/*
+ * These following three functions are not multi-thread (MT) safe (they return
+ * a pointer to unprotected data). Use the new functions from slapi-plugin.h
+ * that end in _copy() instead, e.g., use slapi_get_supported_controls_copy()
+ * instead of slapi_get_supported_controls().
+ */
+SLAPI_DEPRECATED int slapi_get_supported_controls( char ***ctrloidsp,
+ unsigned long **ctrlopsp );
+SLAPI_DEPRECATED char **slapi_get_supported_extended_ops( void );
+SLAPI_DEPRECATED char **slapi_get_supported_saslmechanisms( void );
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _SLAPIPLUGINCOMPAT4 */
diff --git a/ldap/servers/slapd/slapi-plugin.h b/ldap/servers/slapd/slapi-plugin.h
new file mode 100644
index 00000000..52c437aa
--- /dev/null
+++ b/ldap/servers/slapd/slapi-plugin.h
@@ -0,0 +1,1652 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+
+/* slapi-plugin.h - public Directory Server plugin interface */
+
+#ifndef _SLAPIPLUGIN
+#define _SLAPIPLUGIN
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include "ldap.h"
+
+/*
+ * The slapi_attr_get_flags() routine returns a bitmap that contains one or
+ * more of these values.
+ *
+ * Note that the flag values 0x0010, 0x0020, 0x4000, and 0x8000 are reserved.
+ */
+#define SLAPI_ATTR_FLAG_SINGLE 0x0001 /* single-valued attribute */
+#define SLAPI_ATTR_FLAG_OPATTR 0x0002 /* operational attribute */
+#define SLAPI_ATTR_FLAG_READONLY 0x0004 /* read from shipped config file */
+#define SLAPI_ATTR_FLAG_STD_ATTR SLAPI_ATTR_FLAG_READONLY /* alias for read only */
+#define SLAPI_ATTR_FLAG_OBSOLETE 0x0040 /* an outdated definition */
+#define SLAPI_ATTR_FLAG_COLLECTIVE 0x0080 /* collective (not supported) */
+#define SLAPI_ATTR_FLAG_NOUSERMOD 0x0100 /* can't be modified over LDAP */
+
+/* operation flags */
+#define SLAPI_OP_FLAG_NEVER_CHAIN 0x00800 /* Do not chain the operation */
+#define SLAPI_OP_FLAG_NO_ACCESS_CHECK 0x10000 /* Do not check for access control - bypass them */
+
+/*
+ * access control 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
+
+
+/*
+ * filter types
+ */
+#define LDAP_FILTER_AND 0xa0L
+#define LDAP_FILTER_OR 0xa1L
+#define LDAP_FILTER_NOT 0xa2L
+#define LDAP_FILTER_EQUALITY 0xa3L
+#define LDAP_FILTER_SUBSTRINGS 0xa4L
+#define LDAP_FILTER_GE 0xa5L
+#define LDAP_FILTER_LE 0xa6L
+#define LDAP_FILTER_PRESENT 0x87L
+#define LDAP_FILTER_APPROX 0xa8L
+#define LDAP_FILTER_EXTENDED 0xa9L
+
+
+/*
+ * Sequential access types
+ */
+#define SLAPI_SEQ_FIRST 1
+#define SLAPI_SEQ_LAST 2
+#define SLAPI_SEQ_PREV 3
+#define SLAPI_SEQ_NEXT 4
+
+
+/*
+ * return codes from a backend API call
+ */
+#define SLAPI_FAIL_GENERAL -1
+#define SLAPI_FAIL_DISKFULL -2
+
+
+/*
+ * return codes used by BIND functions
+ */
+#define SLAPI_BIND_SUCCESS 0 /* front end will send result */
+#define SLAPI_BIND_FAIL 2 /* back end should send result */
+#define SLAPI_BIND_ANONYMOUS 3 /* front end will send result */
+
+
+/* commonly used attributes names */
+#define SLAPI_ATTR_UNIQUEID "nsuniqueid"
+#define SLAPI_ATTR_OBJECTCLASS "objectclass"
+#define SLAPI_ATTR_VALUE_TOMBSTONE "nsTombstone"
+#define SLAPI_ATTR_VALUE_PARENT_UNIQUEID "nsParentUniqueID"
+#define SLAPI_ATTR_NSCP_ENTRYDN "nscpEntryDN"
+
+
+/* opaque structures */
+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_value_set Slapi_ValueSet;
+typedef struct slapi_filter Slapi_Filter;
+typedef struct slapi_matchingRuleEntry Slapi_MatchingRuleEntry;
+typedef struct backend Slapi_Backend;
+typedef struct _guid_t Slapi_UniqueID;
+typedef struct op Slapi_Operation;
+typedef struct conn 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;
+
+
+/*
+ * The default thread stacksize for nspr21 is 64k (except on IRIX! It's 32k!).
+ * For OSF, we require a larger stacksize as actual storage allocation is
+ * higher i.e pointers are allocated 8 bytes but lower 4 bytes are used.
+ * The value 0 means use the default stacksize.
+ *
+ * larger stacksize (256KB) is needed on IRIX due to its 4KB BUFSIZ.
+ * (@ pthread IRIX porting -- 01/11/99)
+ *
+ * Don't know why HP was defined as follows up until DS6.1x. HP BUFSIZ is 1KB
+ #elif ( defined( hpux ))
+ #define SLAPD_DEFAULT_THREAD_STACKSIZE 262144L
+ */
+#if ( defined( irix ))
+#define SLAPD_DEFAULT_THREAD_STACKSIZE 262144L
+#elif ( defined ( OSF1 ))
+#define SLAPD_DEFAULT_THREAD_STACKSIZE 262144L
+#elif ( defined ( AIX ))
+#define SLAPD_DEFAULT_THREAD_STACKSIZE 262144L
+/* All 64-bit builds get a bigger stack size */
+#elif ( defined ( __LP64__ )) || defined (_LP64)
+#define SLAPD_DEFAULT_THREAD_STACKSIZE 262144L
+#else
+#define SLAPD_DEFAULT_THREAD_STACKSIZE 0
+#endif
+
+/*
+ * parameter block routines
+ */
+Slapi_PBlock *slapi_pblock_new( void ); /* allocate and initialize */
+int slapi_pblock_get( Slapi_PBlock *pb, int arg, void *value );
+int slapi_pblock_set( Slapi_PBlock *pb, int arg, void *value );
+void slapi_pblock_destroy( Slapi_PBlock *pb );
+
+
+/*
+ * entry routines
+ */
+Slapi_Entry *slapi_str2entry( char *s, int flags );
+/* Flags for slapi_str2entry() */
+/* Remove duplicate values */
+#define SLAPI_STR2ENTRY_REMOVEDUPVALS 1
+/* Add any missing values from RDN */
+#define SLAPI_STR2ENTRY_ADDRDNVALS 2
+/* Provide a hint that the entry is large; this enables some optimizations
+ related to large entries. */
+#define SLAPI_STR2ENTRY_BIGENTRY 4
+/* Check to see if the entry is a tombstone; if so, set the tombstone flag
+ (SLAPI_ENTRY_FLAG_TOMBSTONE) */
+#define SLAPI_STR2ENTRY_TOMBSTONE_CHECK 8
+/* Ignore entry state information if present */
+#define SLAPI_STR2ENTRY_IGNORE_STATE 16
+/* Return entries that have a "version: 1" line as part of the LDIF
+ representation */
+#define SLAPI_STR2ENTRY_INCLUDE_VERSION_STR 32
+/* Add any missing ancestor values based on the object class hierarchy */
+#define SLAPI_STR2ENTRY_EXPAND_OBJECTCLASSES 64
+/* Inform slapi_str2entry() that the LDIF input is not well formed.
+ Well formed LDIF has no duplicate attribute values, already
+ has the RDN as an attribute of the entry, and has all values for a
+ given attribute type listed contiguously. */
+#define SLAPI_STR2ENTRY_NOT_WELL_FORMED_LDIF 128
+
+char *slapi_entry2str_with_options( Slapi_Entry *e, int *len, int options );
+/* Options for slapi_entry2str_with_options() */
+#define SLAPI_DUMP_STATEINFO 1 /* replication state */
+#define SLAPI_DUMP_UNIQUEID 2 /* unique ID */
+#define SLAPI_DUMP_NOOPATTRS 4 /* suppress operational attrs */
+#define SLAPI_DUMP_NOWRAP 8 /* no line breaks */
+#define SLAPI_DUMP_MINIMAL_ENCODING 16 /* use less base64 encoding */
+
+char *slapi_entry2str( Slapi_Entry *e, int *len );
+Slapi_Entry *slapi_entry_alloc(void);
+void slapi_entry_init(Slapi_Entry *e, char *dn, Slapi_Attr *a);
+void slapi_entry_free( Slapi_Entry *e );
+Slapi_Entry *slapi_entry_dup( const Slapi_Entry *e );
+char *slapi_entry_get_dn( Slapi_Entry *e );
+char *slapi_entry_get_ndn( Slapi_Entry *e );
+const Slapi_DN *slapi_entry_get_sdn_const( const Slapi_Entry *e );
+Slapi_DN *slapi_entry_get_sdn( Slapi_Entry *e );
+const char *slapi_entry_get_dn_const( const Slapi_Entry *e );
+void slapi_entry_set_dn( Slapi_Entry *e, char *dn );
+void slapi_entry_set_sdn( Slapi_Entry *e, const Slapi_DN *sdn );
+int slapi_entry_attr_find( const Slapi_Entry *e, const char *type, Slapi_Attr **attr );
+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 );
+int slapi_entry_attr_delete( Slapi_Entry *e, const char *type );
+ char **slapi_entry_attr_get_charray(const Slapi_Entry* e, const char *type);
+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);
+unsigned int slapi_entry_attr_get_uint(const Slapi_Entry* e, const char *type);
+long slapi_entry_attr_get_long( const Slapi_Entry* e, const char *type);
+unsigned long slapi_entry_attr_get_ulong( const Slapi_Entry* e, const char *type);
+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_attr_has_syntax_value(const Slapi_Entry *e, const char *type, const Slapi_Value *value);
+int slapi_entry_has_children(const Slapi_Entry *e);
+int slapi_is_rootdse( const char *dn );
+size_t slapi_entry_size(Slapi_Entry *e);
+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);
+void slapi_entry_diff(Slapi_Mods *smods, Slapi_Entry *e1, Slapi_Entry *e2, int diff_ctrl);
+
+
+/*
+ * Entry flags.
+ */
+#define SLAPI_ENTRY_FLAG_TOMBSTONE 1
+int slapi_entry_flag_is_set( const Slapi_Entry *e, unsigned char flag );
+void slapi_entry_set_flag( Slapi_Entry *e, unsigned char flag);
+void slapi_entry_clear_flag( Slapi_Entry *e, unsigned char flag);
+
+/* exported vattrcache routines */
+
+int slapi_entry_vattrcache_watermark_isvalid(const Slapi_Entry *e);
+void slapi_entry_vattrcache_watermark_set(Slapi_Entry *e);
+void slapi_entry_vattrcache_watermark_invalidate(Slapi_Entry *e);
+void slapi_entrycache_vattrcache_watermark_invalidate();
+
+
+
+
+/*
+ * Slapi_DN routines
+ */
+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);
+
+
+/*
+ * Slapi_RDN functions
+ */
+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);
+
+
+/*
+ * utility routines for dealing with DNs
+ */
+char *slapi_dn_normalize( char *dn );
+char *slapi_dn_normalize_to_end( char *dn, char *end );
+char *slapi_dn_ignore_case( char *dn );
+char *slapi_dn_normalize_case( char *dn );
+char *slapi_dn_beparent( Slapi_PBlock *pb, const char *dn );
+char *slapi_dn_parent( const char *dn );
+int slapi_dn_issuffix( const char *dn, const char *suffix );
+int slapi_dn_isparent( const char *parentdn, const char *childdn );
+int slapi_dn_isroot( const char *dn );
+int slapi_dn_isbesuffix( Slapi_PBlock *pb, const char *dn );
+int slapi_rdn2typeval( char *rdn, char **type, struct berval *bv );
+char *slapi_dn_plus_rdn(const char *dn, const char *rdn);
+
+
+/*
+ * thread safe random functions
+ */
+int slapi_rand_r(unsigned int * seed);
+void slapi_rand_array(void *randx, size_t len);
+int slapi_rand();
+
+
+/*
+ * attribute routines
+ */
+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( 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, const struct berval *v );
+
+int slapi_attr_type_cmp( const char *t1, const char *t2, int opt );
+/* Mode of operation (opt) values for slapi_attr_type_cmp() */
+#define SLAPI_TYPE_CMP_EXACT 0
+#define SLAPI_TYPE_CMP_BASE 1
+#define SLAPI_TYPE_CMP_SUBTYPE 2
+
+int slapi_attr_types_equivalent(const char *t1, const char *t2);
+char *slapi_attr_basetype( const char *type, char *buf, size_t bufsiz );
+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);
+/* Make the valuset in Slapi_Attr be *vs--not a copy */
+int slapi_attr_set_valueset(Slapi_Attr *a, const Slapi_ValueSet *vs);
+int slapi_attr_get_bervals_copy( Slapi_Attr *a, struct berval ***vals );
+char * slapi_attr_syntax_normalize( const char *s );
+void slapi_valueset_set_valueset(Slapi_ValueSet *vs1, const Slapi_ValueSet *vs2);
+void slapi_valueset_set_from_smod(Slapi_ValueSet *vs, Slapi_Mod *smod);
+
+
+/*
+ * value routines
+ */
+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_new_string_passin(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_init_string_passin(Slapi_Value *v, 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_string_passin(Slapi_Value *value, 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);
+
+
+/*
+ * Valueset functions.
+ */
+#define SLAPI_VALUE_FLAG_PASSIN 0x1
+#define SLAPI_VALUE_FLAG_IGNOREERROR 0x2
+#define SLAPI_VALUE_FLAG_PRESERVECSNSET 0x4
+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);
+void slapi_valueset_add_value_ext(Slapi_ValueSet *vs, Slapi_Value *addval, unsigned long flags);
+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_from_smod(Slapi_ValueSet *vs, Slapi_Mod *smod);
+void slapi_valueset_set_valueset(Slapi_ValueSet *vs1, const Slapi_ValueSet *vs2);
+Slapi_Value *slapi_valueset_find(const Slapi_Attr *a, const Slapi_ValueSet *vs, const Slapi_Value *v);
+
+
+/*
+ * operation routines
+ */
+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);
+int slapi_op_reserved(Slapi_PBlock *pb);
+void slapi_operation_set_csngen_handler ( Slapi_Operation *op, void *callback );
+void slapi_operation_set_replica_attr_handler ( Slapi_Operation *op, void *callback );
+int slapi_operation_get_replica_attr ( Slapi_PBlock *pb, Slapi_Operation *op, const char *type, void *value );
+
+
+/*
+ * LDAPMod manipulation routines
+ */
+Slapi_Mods* slapi_mods_new( void );
+void slapi_mods_init(Slapi_Mods *smods, int initCount);
+void slapi_mods_init_byref(Slapi_Mods *smods, LDAPMod **mods);
+void slapi_mods_init_passin(Slapi_Mods *smods, LDAPMod **mods);
+void slapi_mods_free(Slapi_Mods **smods);
+void slapi_mods_done(Slapi_Mods *smods);
+void slapi_mods_insert_at(Slapi_Mods *smods, LDAPMod *mod, int pos);
+void slapi_mods_insert_smod_at(Slapi_Mods *smods, Slapi_Mod *smod, int pos);
+void slapi_mods_insert_before(Slapi_Mods *smods, LDAPMod *mod);
+void slapi_mods_insert_smod_before(Slapi_Mods *smods, Slapi_Mod *smod);
+void slapi_mods_insert_after(Slapi_Mods *smods, LDAPMod *mod);
+void slapi_mods_insert_after(Slapi_Mods *smods, LDAPMod *mod);
+void slapi_mods_add( Slapi_Mods *smods, int modtype, const char *type, unsigned long len, const char *val);
+void slapi_mods_add_ldapmod(Slapi_Mods *smods, LDAPMod *mod);
+void slapi_mods_add_modbvps( Slapi_Mods *smods, int modtype, const char *type, struct berval **bvps );
+void slapi_mods_add_mod_values( Slapi_Mods *smods, int modtype, const char *type, Slapi_Value **va );
+void slapi_mods_add_smod(Slapi_Mods *smods, Slapi_Mod *smod);
+void slapi_mods_add_string( Slapi_Mods *smods, int modtype, const char *type, const char *val);
+void slapi_mods_remove(Slapi_Mods *smods);
+LDAPMod *slapi_mods_get_first_mod(Slapi_Mods *smods);
+LDAPMod *slapi_mods_get_next_mod(Slapi_Mods *smods);
+Slapi_Mod *slapi_mods_get_first_smod(Slapi_Mods *smods, Slapi_Mod *smod);
+Slapi_Mod *slapi_mods_get_next_smod(Slapi_Mods *smods, Slapi_Mod *smod);
+void slapi_mods_iterator_backone(Slapi_Mods *smods);
+LDAPMod **slapi_mods_get_ldapmods_byref(Slapi_Mods *smods);
+LDAPMod **slapi_mods_get_ldapmods_passout(Slapi_Mods *smods);
+int slapi_mods_get_num_mods(const Slapi_Mods *smods);
+void slapi_mods_dump(const Slapi_Mods *smods, const char *text);
+
+Slapi_Mod* slapi_mod_new( void );
+void slapi_mod_init(Slapi_Mod *smod, int initCount);
+void slapi_mod_init_byval(Slapi_Mod *smod, const LDAPMod *mod);
+void slapi_mod_init_byref(Slapi_Mod *smod, LDAPMod *mod);
+void slapi_mod_init_passin(Slapi_Mod *smod, LDAPMod *mod);
+void slapi_mod_add_value(Slapi_Mod *smod, const struct berval *val);
+void slapi_mod_remove_value(Slapi_Mod *smod);
+struct berval *slapi_mod_get_first_value(Slapi_Mod *smod);
+struct berval *slapi_mod_get_next_value(Slapi_Mod *smod);
+const char *slapi_mod_get_type(const Slapi_Mod *smod);
+int slapi_mod_get_operation(const Slapi_Mod *smod);
+void slapi_mod_set_type(Slapi_Mod *smod, const char *type);
+void slapi_mod_set_operation(Slapi_Mod *smod, int op);
+int slapi_mod_get_num_values(const Slapi_Mod *smod);
+const LDAPMod *slapi_mod_get_ldapmod_byref(const Slapi_Mod *smod);
+LDAPMod *slapi_mod_get_ldapmod_passout(Slapi_Mod *smod);
+void slapi_mod_free(Slapi_Mod **smod);
+void slapi_mod_done(Slapi_Mod *mod);
+int slapi_mod_isvalid(const Slapi_Mod *mod);
+void slapi_mod_dump(LDAPMod *mod, int n);
+
+
+/* helper functions to translate between entry and a set of mods */
+int slapi_mods2entry(Slapi_Entry **e, const char *dn, LDAPMod **attrs);
+int slapi_entry2mods(const Slapi_Entry *e, char **dn, LDAPMod ***attrs);
+
+
+/*
+ * routines for dealing with filters
+ */
+int slapi_filter_get_choice( Slapi_Filter *f );
+int slapi_filter_get_ava( Slapi_Filter *f, char **type, struct berval **bval );
+int slapi_filter_get_attribute_type( Slapi_Filter *f, char **type );
+int slapi_filter_get_subfilt( Slapi_Filter *f, char **type, char **initial,
+ char ***any, char **final );
+Slapi_Filter *slapi_filter_list_first( Slapi_Filter *f );
+Slapi_Filter *slapi_filter_list_next( Slapi_Filter *f, Slapi_Filter *fprev );
+Slapi_Filter *slapi_str2filter( char *str );
+Slapi_Filter *slapi_filter_join( int ftype, Slapi_Filter *f1,
+ Slapi_Filter *f2 );
+Slapi_Filter *slapi_filter_join_ex( int ftype, Slapi_Filter *f1,
+ Slapi_Filter *f2, int recurse_always );
+
+void slapi_filter_free( Slapi_Filter *f, int recurse );
+int slapi_filter_test( Slapi_PBlock *pb, Slapi_Entry *e, Slapi_Filter *f,
+ int verify_access );
+int slapi_vattr_filter_test( Slapi_PBlock *pb, Slapi_Entry *e,
+ struct slapi_filter *f, int verify_access);
+int slapi_filter_test_simple( Slapi_Entry *e, Slapi_Filter *f);
+char *slapi_find_matching_paren( const char *str );
+int slapi_filter_test_ext( Slapi_PBlock *pb, Slapi_Entry *e, Slapi_Filter *f,
+ int verify_access , int only_test_access);
+int slapi_vattr_filter_test_ext( Slapi_PBlock *pb, Slapi_Entry *e, Slapi_Filter *f,
+ int verify_access , int only_test_access);
+int slapi_filter_compare(struct slapi_filter *f1, struct slapi_filter *f2);
+Slapi_Filter *slapi_filter_dup(Slapi_Filter *f);
+int slapi_filter_changetype(Slapi_Filter *f, const char *newtype);
+
+
+/*
+ * slapi_filter_apply() is used to apply a function to each simple filter
+ * component within a complex filter. A 'simple filter' is anything other
+ * than AND, OR or NOT.
+ */
+typedef int (*FILTER_APPLY_FN)( Slapi_Filter *f, void *arg);
+int slapi_filter_apply( struct slapi_filter *f, FILTER_APPLY_FN fn, void *arg,
+ int *error_code );
+/*
+ * Possible return values for slapi_filter_apply() and FILTER_APPLY_FNs.
+ * Note that a FILTER_APPLY_FN should return _STOP or _CONTINUE only.
+ */
+#define SLAPI_FILTER_SCAN_STOP -1 /* premature abort */
+#define SLAPI_FILTER_SCAN_ERROR -2 /* an error occurred */
+#define SLAPI_FILTER_SCAN_NOMORE 0 /* success */
+#define SLAPI_FILTER_SCAN_CONTINUE 1 /* continue scanning */
+/* Error codes that slapi_filter_apply() may set in *error_code */
+#define SLAPI_FILTER_UNKNOWN_FILTER_TYPE 2
+
+
+/*
+ * Bit-Twiddlers
+ */
+unsigned char slapi_setbit_uchar(unsigned char f,unsigned char bitnum);
+unsigned char slapi_unsetbit_uchar(unsigned char f,unsigned char bitnum);
+int slapi_isbitset_uchar(unsigned char f,unsigned char bitnum);
+unsigned int slapi_setbit_int(unsigned int f,unsigned int bitnum);
+unsigned int slapi_unsetbit_int(unsigned int f,unsigned int bitnum);
+int slapi_isbitset_int(unsigned int f,unsigned int bitnum);
+
+
+/*
+ * routines for sending entries and results to the client
+ */
+int slapi_send_ldap_search_entry( Slapi_PBlock *pb, Slapi_Entry *e,
+ LDAPControl **ectrls, char **attrs, int attrsonly );
+void slapi_send_ldap_result( Slapi_PBlock *pb, int err, char *matched,
+ char *text, int nentries, struct berval **urls );
+int slapi_send_ldap_referral( Slapi_PBlock *pb, Slapi_Entry *e,
+ struct berval **refs, struct berval ***urls );
+typedef int (*send_ldap_search_entry_fn_ptr_t)( Slapi_PBlock *pb,
+ Slapi_Entry *e, LDAPControl **ectrls, char **attrs, int attrsonly );
+typedef void (*send_ldap_result_fn_ptr_t)( Slapi_PBlock *pb, int err,
+ char *matched, char *text, int nentries, struct berval **urls );
+typedef int (*send_ldap_referral_fn_ptr_t)( Slapi_PBlock *pb,
+ Slapi_Entry *e, struct berval **refs, struct berval ***urls );
+
+
+/*
+ * matching rule
+ */
+typedef int (*mrFilterMatchFn) (void* filter, Slapi_Entry*, Slapi_Attr* vals);
+/* returns: 0 filter matched
+ * -1 filter did not match
+ * >0 an LDAP error code
+ */
+int slapi_mr_indexer_create(Slapi_PBlock* opb);
+int slapi_mr_filter_index(Slapi_Filter* f, Slapi_PBlock* pb);
+int slapi_berval_cmp(const struct berval* L, const struct berval* R);
+#define SLAPI_BERVAL_EQ(L,R) ((L)->bv_len == (R)->bv_len && \
+ ! memcmp ((L)->bv_val, (R)->bv_val, (L)->bv_len))
+
+Slapi_MatchingRuleEntry *slapi_matchingrule_new(void);
+void slapi_matchingrule_free(Slapi_MatchingRuleEntry **mrEntry,
+ int freeMembers);
+int slapi_matchingrule_get(Slapi_MatchingRuleEntry *mr, int arg, void *value);
+int slapi_matchingrule_set(Slapi_MatchingRuleEntry *mr, int arg, void *value);
+int slapi_matchingrule_register(Slapi_MatchingRuleEntry *mrEntry);
+int slapi_matchingrule_unregister(char *oid);
+
+/*
+ * access control
+ */
+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 );
+int slapi_acl_verify_aci_syntax(Slapi_PBlock *pb, Slapi_Entry *e, char **errbuf);
+
+
+/*
+ * attribute stuff
+ */
+int slapi_value_find( void *plugin, struct berval **vals, struct berval *v );
+
+
+/*
+ * password handling
+ */
+#define SLAPI_USERPWD_ATTR "userpassword"
+int slapi_pw_find_sv( Slapi_Value **vals, const Slapi_Value *v );
+
+/* value encoding encoding */
+/* checks if the value is encoded with any known algorithm*/
+int slapi_is_encoded(char *value);
+/* encode value with the specified algorithm */
+char* slapi_encode(char *value, char *alg);
+
+
+/* UTF8 related */
+int slapi_has8thBit(unsigned char *s);
+unsigned char *slapi_utf8StrToLower(unsigned char *s);
+void slapi_utf8ToLower(unsigned char *s, unsigned char *d, int *ssz, int *dsz);
+int slapi_utf8isUpper(unsigned char *s);
+unsigned char *slapi_utf8StrToUpper(unsigned char *s);
+void slapi_utf8ToUpper(unsigned char *s, unsigned char *d, int *ssz, int *dsz);
+int slapi_utf8isLower(unsigned char *s);
+int slapi_utf8casecmp(unsigned char *s0, unsigned char *s1);
+int slapi_utf8ncasecmp(unsigned char *s0, unsigned char *s1, int n);
+
+unsigned char *slapi_UTF8STRTOLOWER(char *s);
+void slapi_UTF8TOLOWER(char *s, char *d, int *ssz, int *dsz);
+int slapi_UTF8ISUPPER(char *s);
+unsigned char *slapi_UTF8STRTOUPPER(char *s);
+void slapi_UTF8TOUPPER(char *s, char *d, int *ssz, int *dsz);
+int slapi_UTF8ISLOWER(char *s);
+int slapi_UTF8CASECMP(char *s0, char *s1);
+int slapi_UTF8NCASECMP(char *s0, char *s1, int n);
+
+
+
+/*
+ * Interface to the API broker service
+ *
+ * The API broker allows plugins to publish an API that may be discovered
+ * and used dynamically at run time by other subsystems e.g. other plugins.
+ */
+
+/* Function: slapi_apib_register
+ Description: this function allows publication of an interface
+ Parameters: guid - a string constant that uniquely identifies the
+ interface (must exist for the life of the server)
+ api - a vtable for the published api (must exist for the
+ life of the server or until the reference count,
+ if it exists, reaches zero)
+ Return: 0 if function succeeds
+ non-zero otherwise
+*/
+int slapi_apib_register(char *guid, void **api); /* publish an interface */
+
+/* Function: slapi_apib_unregister
+ Description: this function allows removal of a published interface
+ Parameters: guid - a string constant that uniquely identifies the interface
+ Return: 0 if function succeeds
+ non-zero otherwise
+*/
+int slapi_apib_unregister(char *guid); /* remove interface from published list */
+
+
+/* Function: slapi_apib_get_interface
+ Description: this function allows retrieval of a published interface,
+ if the api reference counted, then the reference
+ count is incremented
+ Parameters: guid - a string constant that uniquely identifies the
+ interface requested
+ api - the retrieved vtable for the published api (must NOT
+ be freed)
+ Return: 0 if function succeeds
+ non-zero otherwise
+*/
+int slapi_apib_get_interface(char *guid, void ***api); /* retrieve an interface for use */
+
+
+/* Function: slapi_apib_make_reference_counted
+ Description: this function makes an interface a reference counted interface
+ it must be called prior to registering the interface
+ Parameters: api - the api to make a reference counted api
+ callback - if non-zero, this must be a pointer to a function
+ which the api broker will call when the ref count for
+ the api reaches zero. This function must return 0 if
+ it deregisters the api, non-zero otherwise
+ api - the retrieved vtable for the published api (must NOT
+ be freed)
+ Return: 0 if function succeeds
+ non-zero otherwise
+*/
+typedef int (*slapi_apib_callback_on_zero)(void **api);
+
+int slapi_apib_make_reference_counted(void **api,
+ slapi_apib_callback_on_zero callback);
+
+
+/* Function: slapi_apib_addref
+ Description: this function adds to the reference count of an api - a
+ call to this function should be paired with a call
+ to slapi_apib_release
+ - ONLY FOR REFERENCE COUNTED APIS
+ Parameters: api - the api to add a reference to
+ Return: the new reference count
+*/
+int slapi_apib_addref(void **api);
+
+
+/* Function: slapi_apib_release
+ Description: this function adds to the reference count of an api - a
+ call to this function should be paired with a prior call
+ to slapi_apib_addref or slapi_apib_get_interface
+ - ONLY FOR REFERENCE COUNTED APIS
+ Parameters: api - the api to add a reference to
+ Return: the new reference count
+*/
+int slapi_apib_release(void **api);
+
+/**** End of API broker interface. *******************************************/
+
+
+/*
+ * routines for dealing with controls
+ */
+int slapi_control_present( LDAPControl **controls, char *oid,
+ struct berval **val, int *iscritical );
+void slapi_register_supported_control( char *controloid,
+ unsigned long controlops );
+LDAPControl * slapi_dup_control( LDAPControl *ctrl );
+
+#define SLAPI_OPERATION_BIND 0x00000001UL
+#define SLAPI_OPERATION_UNBIND 0x00000002UL
+#define SLAPI_OPERATION_SEARCH 0x00000004UL
+#define SLAPI_OPERATION_MODIFY 0x00000008UL
+#define SLAPI_OPERATION_ADD 0x00000010UL
+#define SLAPI_OPERATION_DELETE 0x00000020UL
+#define SLAPI_OPERATION_MODDN 0x00000040UL
+#define SLAPI_OPERATION_MODRDN SLAPI_OPERATION_MODDN
+#define SLAPI_OPERATION_COMPARE 0x00000080UL
+#define SLAPI_OPERATION_ABANDON 0x00000100UL
+#define SLAPI_OPERATION_EXTENDED 0x00000200UL
+#define SLAPI_OPERATION_ANY 0xFFFFFFFFUL
+#define SLAPI_OPERATION_NONE 0x00000000UL
+int slapi_get_supported_controls_copy( char ***ctrloidsp,
+ unsigned long **ctrlopsp );
+int slapi_build_control( char *oid, BerElement *ber,
+ char iscritical, LDAPControl **ctrlp );
+int slapi_build_control_from_berval( char *oid, struct berval *bvp,
+ char iscritical, LDAPControl **ctrlp );
+
+
+/*
+ * routines for dealing with extended operations
+ */
+char **slapi_get_supported_extended_ops_copy( void );
+
+
+/*
+ * bind, including SASL
+ */
+void slapi_register_supported_saslmechanism( char *mechanism );
+char ** slapi_get_supported_saslmechanisms_copy( void );
+
+/*
+ * routine for freeing the ch_arrays returned by the slapi_get*_copy functions above
+ */
+void slapi_ch_array_free( char **array );
+
+
+/*
+ * checking routines for allocating and freeing memory
+ */
+char * slapi_ch_malloc( unsigned long size );
+char * slapi_ch_realloc( char *block, unsigned long size );
+char * slapi_ch_calloc( unsigned long nelem, unsigned long size );
+char * slapi_ch_strdup( const char *s );
+void slapi_ch_free( void **ptr );
+void slapi_ch_free_string( char **s );
+struct berval* slapi_ch_bvdup(const struct berval*);
+struct berval** slapi_ch_bvecdup(struct berval**);
+void slapi_ch_bvfree(struct berval** v);
+
+
+/*
+ * syntax plugin routines
+ */
+int slapi_call_syntax_values2keys_sv( void *vpi, Slapi_Value **vals,
+ Slapi_Value ***ivals, int ftype );
+int slapi_call_syntax_assertion2keys_ava_sv( void *vpi, Slapi_Value *val,
+ Slapi_Value ***ivals, int ftype );
+int slapi_call_syntax_assertion2keys_sub_sv( void *vpi, char *initial,
+ char **any, char *final, Slapi_Value ***ivals );
+
+
+/*
+ * internal operation and plugin callback 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);
+
+
+/*
+ * The following functions can be used for internal operations based on DN
+ * as well as on uniqueid. These functions should be used by all new plugins
+ * and preferrably old plugins should be changed to use them to take
+ * advantage of new plugin configuration capabilities and to use an
+ * extensible interface.
+ *
+ * These functions return -1 if pb is NULL and 0 otherwise.
+ * The SLAPI_PLUGIN_INTOP_RESULT pblock parameter should be checked to
+ * check if the operation was successful.
+ *
+ * Helper functions are provided to set up pblock parameters currently used
+ * by the functions, e.g., slapi_search_internal_set_pb().
+ * Additional parameters may be set directly in the pblock.
+ */
+
+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);
+
+/*
+ * slapi_search_internal_get_entry() finds an entry given a dn. It returns
+ * an LDAP error code (LDAP_SUCCESS if all goes well).
+ */
+int slapi_search_internal_get_entry( Slapi_DN *dn, char ** attrlist,
+ Slapi_Entry **ret_entry , void *caller_identity);
+
+/*
+ * interface for registering 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);
+
+/* objects that can be extended (possible values for the objectname param.) */
+#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);
+
+/*
+ * interface to allow a plugin to register additional plugins.
+ */
+typedef int (*slapi_plugin_init_fnptr)( Slapi_PBlock *pb );
+int slapi_register_plugin( const char *plugintype, int enabled,
+ const char *initsymbol, slapi_plugin_init_fnptr initfunc,
+ const char *name, char **argv, void *group_identity);
+
+
+/*
+ * logging
+ */
+int slapi_log_error( int severity, char *subsystem, char *fmt, ... );
+/* allowed values for the "severity" parameter */
+#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_LOG_ACLSUMMARY 16
+
+int slapi_is_loglevel_set( const int loglevel );
+
+
+/*
+ * 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 );
+
+
+/*
+ * computed attributes
+ */
+struct _computed_attr_context;
+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);
+
+
+/*
+ * routines for dealing with backends
+ */
+Slapi_Backend *slapi_be_new( const char *type, const char *name,
+ int isprivate, int logchanges );
+void slapi_be_free(Slapi_Backend **be);
+Slapi_Backend *slapi_be_select( const Slapi_DN *sdn );
+Slapi_Backend *slapi_be_select_by_instance_name( const char *name );
+int slapi_be_exist(const Slapi_DN *sdn);
+void slapi_be_delete_onexit(Slapi_Backend *be);
+void slapi_be_set_readonly(Slapi_Backend *be, int readonly);
+int slapi_be_get_readonly(Slapi_Backend *be);
+int slapi_be_getentrypoint(Slapi_Backend *be, int entrypoint, void **ret_fnptr,
+ Slapi_PBlock *pb);
+int slapi_be_setentrypoint(Slapi_Backend *be, int entrypoint, void *ret_fnptr,
+ Slapi_PBlock *pb);
+int slapi_be_logchanges(Slapi_Backend *be);
+int slapi_be_issuffix(const Slapi_Backend *be, const Slapi_DN *suffix );
+void slapi_be_addsuffix(Slapi_Backend *be,const Slapi_DN *suffix);
+char * slapi_be_get_name(Slapi_Backend * be);
+const Slapi_DN *slapi_be_getsuffix(Slapi_Backend *be, int n);
+Slapi_Backend* slapi_get_first_backend(char **cookie);
+Slapi_Backend* slapi_get_next_backend(char *cookie);
+int slapi_be_private( Slapi_Backend *be );
+void * slapi_be_get_instance_info(Slapi_Backend * be);
+void slapi_be_set_instance_info(Slapi_Backend * be, void * data);
+Slapi_DN * slapi_get_first_suffix(void ** node, int show_private);
+Slapi_DN * slapi_get_next_suffix(void ** node, int show_private);
+int slapi_is_root_suffix(Slapi_DN * dn);
+const char * slapi_be_gettype(Slapi_Backend *be);
+
+int slapi_be_is_flag_set(Slapi_Backend * be, int flag);
+void slapi_be_set_flag(Slapi_Backend * be, int flag);
+#define SLAPI_BE_FLAG_REMOTE_DATA 0x1 /* entries held by backend are remote */
+#define SLAPI_BE_FLAG_DONT_BYPASS_FILTERTEST 0x10 /* force to call filter_test (search only) */
+
+
+/* These functions allow a plugin to register for callback when
+ * a backend state change
+ */
+typedef void (*slapi_backend_state_change_fnptr)(void *handle, char *be_name,
+ int old_be_state, int new_be_state);
+void slapi_register_backend_state_change(void * handle, slapi_backend_state_change_fnptr funct);
+int slapi_unregister_backend_state_change(void * handle);
+#define SLAPI_BE_STATE_ON 1 /* backend is ON */
+#define SLAPI_BE_STATE_OFFLINE 2 /* backend is OFFLINE (import process) */
+#define SLAPI_BE_STATE_DELETE 3 /* backend has been deleted */
+
+/*
+ * Distribution.
+ */
+/* SLAPI_BE_ALL_BACKENDS is a special value that is returned by
+ * a distribution plugin function to indicate that all backends
+ * should be searched (it is only used for search operations).
+ */
+#define SLAPI_BE_ALL_BACKENDS -1
+
+
+
+/*
+ * virtual attribute service
+ */
+
+/* General flags (flags parameter) */
+#define SLAPI_REALATTRS_ONLY 1
+#define SLAPI_VIRTUALATTRS_ONLY 2
+#define SLAPI_VIRTUALATTRS_REQUEST_POINTERS 4 /* I want to receive pointers into the entry, if possible */
+#define SLAPI_VIRTUALATTRS_LIST_OPERATIONAL_ATTRS 8 /* Include operational attributes in attribute lists */
+#define SLAPI_VIRTUALATTRS_SUPPRESS_SUBTYPES 16 /* I want only the requested attribute */
+
+/* Buffer disposition flags (buffer_flags parameter) */
+#define SLAPI_VIRTUALATTRS_RETURNED_POINTERS 1
+#define SLAPI_VIRTUALATTRS_RETURNED_COPIES 2
+#define SLAPI_VIRTUALATTRS_REALATTRS_ONLY 4
+
+/* Attribute type name disposition values (type_name_disposition parameter) */
+#define SLAPI_VIRTUALATTRS_TYPE_NAME_MATCHED_EXACTLY_OR_ALIAS 1
+#define SLAPI_VIRTUALATTRS_TYPE_NAME_MATCHED_SUBTYPE 2
+#define SLAPI_VIRTUALATTRS_NOT_FOUND -1
+#define SLAPI_VIRTUALATTRS_LOOP_DETECTED -2
+
+typedef struct _vattr_type_thang vattr_type_thang;
+typedef struct _vattr_get_thang vattr_get_thang;
+vattr_get_thang *slapi_vattr_getthang_first(vattr_get_thang *t);
+vattr_get_thang *slapi_vattr_getthang_next(vattr_get_thang *t);
+
+int slapi_vattr_values_type_thang_get(
+ /* Entry we're interested in */ Slapi_Entry *e,
+ /* attr type */ vattr_type_thang *type_thang,
+ /* pointer to result set */ Slapi_ValueSet** results,
+ int *type_name_disposition, char **actual_type_name, int flags,
+ int *buffer_flags);
+int slapi_vattr_values_get(
+ /* Entry we're interested in */ Slapi_Entry *e,
+ /* attr type name */ char *type,
+ /* pointer to result set */ Slapi_ValueSet** results,
+ int *type_name_disposition, char **actual_type_name, int flags,
+ int *buffer_flags);
+int slapi_vattr_values_get_ex(
+ /* Entry we're interested in */ Slapi_Entry *e,
+ /* attr type name */ char *type,
+ /* pointer to result set */ Slapi_ValueSet*** results,
+ int **type_name_disposition, char ***actual_type_name, int flags,
+ int *buffer_flags, int *subtype_count);
+int slapi_vattr_namespace_values_get(
+ /* Entry we're interested in */ Slapi_Entry *e,
+ /* backend namespace dn */ Slapi_DN *namespace_dn,
+ /* attr type name */ char *type,
+ /* pointer to result set */ Slapi_ValueSet*** results,
+ int **type_name_disposition, char ***actual_type_name, int flags,
+ int *buffer_flags, int *subtype_count);
+void slapi_vattr_values_free(Slapi_ValueSet **value, char **actual_type_name,
+ int flags);
+int slapi_vattr_value_compare(
+ /* Entry we're interested in */ Slapi_Entry *e,
+ /* attr type name */ char *type,
+ Slapi_Value *test_this,/* pointer to result */ int *result,
+ int flags);
+int slapi_vattr_namespace_value_compare(
+ /* Entry we're interested in */ Slapi_Entry *e,
+ /* backend namespace dn */ Slapi_DN *namespace_dn,
+ /* attr type name */ const char *type,
+ Slapi_Value *test_this,/* pointer to result */ int *result,
+ int flags);
+int slapi_vattr_list_attrs(
+ /* Entry we're interested in */ Slapi_Entry *e,
+ /* pointer to receive the list */ vattr_type_thang **types,
+ int flags, int *buffer_flags);
+void slapi_vattr_attrs_free(vattr_type_thang **types, int flags);
+char *vattr_typethang_get_name(vattr_type_thang *t);
+unsigned long vattr_typethang_get_flags(vattr_type_thang *t);
+vattr_type_thang *vattr_typethang_next(vattr_type_thang *t);
+vattr_type_thang *vattr_typethang_first(vattr_type_thang *t);
+int slapi_vattr_schema_check_type(Slapi_Entry *e, char *type);
+
+
+/* roles */
+typedef int (*roles_check_fn_type)(Slapi_Entry *entry_to_check, Slapi_DN *role_dn, int *present);
+
+int slapi_role_check(Slapi_Entry *entry_to_check, Slapi_DN *role_dn, int *present);
+void slapi_register_role_check(roles_check_fn_type check_fn);
+
+
+
+/* Binder-based (connection centric) resource limits */
+/*
+ * Valid values for `type' parameter to slapi_reslimit_register().
+ */
+#define SLAPI_RESLIMIT_TYPE_INT 0
+
+/*
+ * Status codes returned by all functions.
+ */
+#define SLAPI_RESLIMIT_STATUS_SUCCESS 0 /* goodness */
+#define SLAPI_RESLIMIT_STATUS_NOVALUE 1 /* no value is available */
+#define SLAPI_RESLIMIT_STATUS_INIT_FAILURE 2 /* initialization failed */
+#define SLAPI_RESLIMIT_STATUS_PARAM_ERROR 3 /* bad parameter */
+#define SLAPI_RESLIMIT_STATUS_UNKNOWN_HANDLE 4 /* unregistered handle */
+#define SLAPI_RESLIMIT_STATUS_INTERNAL_ERROR 5 /* unexpected error */
+
+/*
+ * Functions.
+ */
+int slapi_reslimit_register( int type, const char *attrname, int *handlep );
+int slapi_reslimit_get_integer_limit( Slapi_Connection *conn, int handle,
+ int *limitp );
+/* END of Binder-based resource limits API */
+
+
+
+/*
+ * Plugin and parameter block related macros (remainder of this file).
+ */
+
+/*
+ * Plugin version. Note that the Directory Server will load version 01
+ * and 02 plugins, but some server features require 03 plugins.
+ */
+#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
+#define SLAPI_PLUGIN_IS_COMPAT(x) \
+ ((strcmp((x), SLAPI_PLUGIN_VERSION_01) == 0) || \
+ (strcmp((x), SLAPI_PLUGIN_VERSION_02) == 0) || \
+ (strcmp((x), SLAPI_PLUGIN_VERSION_03) == 0))
+#define SLAPI_PLUGIN_IS_V2(x) \
+ ((strcmp((x)->plg_version, SLAPI_PLUGIN_VERSION_02) == 0) || \
+ (strcmp((x)->plg_version, SLAPI_PLUGIN_VERSION_03) == 0))
+#define SLAPI_PLUGIN_IS_V3(x) \
+ (strcmp((x)->plg_version, SLAPI_PLUGIN_VERSION_03) == 0)
+
+/* this one just has to be human readable */
+#define SLAPI_PLUGIN_SUPPORTED_VERSIONS "01,02,03"
+
+/*
+ * types of plugin interfaces
+ */
+#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_ACL 7
+#define SLAPI_PLUGIN_BEPREOPERATION 8
+#define SLAPI_PLUGIN_BEPOSTOPERATION 9
+#define SLAPI_PLUGIN_ENTRY 10
+#define SLAPI_PLUGIN_TYPE_OBJECT 11
+#define SLAPI_PLUGIN_INTERNAL_PREOPERATION 12
+#define SLAPI_PLUGIN_INTERNAL_POSTOPERATION 13
+#define SLAPI_PLUGIN_PWD_STORAGE_SCHEME 14
+#define SLAPI_PLUGIN_VATTR_SP 15
+#define SLAPI_PLUGIN_REVER_PWD_STORAGE_SCHEME 16
+#define SLAPI_PLUGIN_LDBM_ENTRY_FETCH_STORE 17
+#define SLAPI_PLUGIN_INDEX 18
+
+/*
+ * special return values for extended operation plugins (zero or positive
+ * return values should be LDAP error codes as defined in ldap.h)
+ */
+#define SLAPI_PLUGIN_EXTENDED_SENT_RESULT -1
+#define SLAPI_PLUGIN_EXTENDED_NOT_HANDLED -2
+
+/*
+ * the following can be used as the second argument to the
+ * slapi_pblock_get() and slapi_pblock_set() calls.
+ */
+
+/* backend, connection, operation */
+#define SLAPI_BACKEND 130
+#define SLAPI_CONNECTION 131
+#define SLAPI_OPERATION 132
+#define SLAPI_REQUESTOR_ISROOT 133
+#define SLAPI_BE_TYPE 135
+#define SLAPI_BE_READONLY 136
+#define SLAPI_BE_LASTMOD 137
+#define SLAPI_CONN_ID 139
+#define SLAPI_BACKEND_COUNT 860
+
+/* operation */
+#define SLAPI_OPINITIATED_TIME 140
+#define SLAPI_REQUESTOR_DN 141
+#define SLAPI_OPERATION_PARAMETERS 138
+#define SLAPI_OPERATION_TYPE 590
+#define SLAPI_OPERATION_AUTHTYPE 741
+#define SLAPI_OPERATION_ID 744
+#define SLAPI_IS_REPLICATED_OPERATION 142
+#define SLAPI_IS_MMR_REPLICATED_OPERATION 153
+#define SLAPI_IS_LEGACY_REPLICATED_OPERATION 154
+
+/* connection */
+#define SLAPI_CONN_DN 143
+#define SLAPI_CONN_CLIENTNETADDR 850
+#define SLAPI_CONN_SERVERNETADDR 851
+#define SLAPI_CONN_IS_REPLICATION_SESSION 149
+#define SLAPI_CONN_IS_SSL_SESSION 747
+#define SLAPI_CONN_CERT 743
+#define SLAPI_CONN_AUTHMETHOD 746
+
+/*
+ * Types of authentication for SLAPI_CONN_AUTHMETHOD
+ * (and deprecated SLAPI_CONN_AUTHTYPE)
+ */
+#define SLAPD_AUTH_NONE "none"
+#define SLAPD_AUTH_SIMPLE "simple"
+#define SLAPD_AUTH_SSL "SSL"
+#define SLAPD_AUTH_SASL "SASL " /* followed by the mechanism name */
+
+
+/* Command Line Arguments */
+#define SLAPI_ARGC 147
+#define SLAPI_ARGV 148
+
+/* Slapd config file directory */
+#define SLAPI_CONFIG_DIRECTORY 281
+
+/* DSE flags */
+#define SLAPI_DSE_DONT_WRITE_WHEN_ADDING 282
+#define SLAPI_DSE_MERGE_WHEN_ADDING 283
+#define SLAPI_DSE_DONT_CHECK_DUPS 284
+#define SLAPI_DSE_REAPPLY_MODS 287
+#define SLAPI_DSE_IS_PRIMARY_FILE 289
+
+/* internal schema flags */
+#define SLAPI_SCHEMA_USER_DEFINED_ONLY 285
+
+/* urp flags */
+#define SLAPI_URP_NAMING_COLLISION_DN 286
+#define SLAPI_URP_TOMBSTONE_UNIQUEID 288
+
+/* common to all plugins */
+#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
+typedef struct slapi_plugindesc {
+ char *spd_id;
+ char *spd_vendor;
+ char *spd_version;
+ char *spd_description;
+} Slapi_PluginDesc;
+
+#define SLAPI_PLUGIN_IDENTITY 13
+
+/* common for internal plugin_ops */
+#define SLAPI_PLUGIN_INTOP_RESULT 15
+#define SLAPI_PLUGIN_INTOP_SEARCH_ENTRIES 16
+#define SLAPI_PLUGIN_INTOP_SEARCH_REFERRALS 17
+
+/* miscellaneous plugin functions */
+#define SLAPI_PLUGIN_CLOSE_FN 210
+#define SLAPI_PLUGIN_START_FN 212
+#define SLAPI_PLUGIN_CLEANUP_FN 232
+#define SLAPI_PLUGIN_POSTSTART_FN 233
+
+
+/* extendedop plugin functions */
+#define SLAPI_PLUGIN_EXT_OP_FN 300
+#define SLAPI_PLUGIN_EXT_OP_OIDLIST 301
+#define SLAPI_PLUGIN_EXT_OP_NAMELIST 302
+
+/* preoperation plugin functions */
+#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 plugin functions */
+#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
+
+/* preoperation plugin to the backend */
+#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 plugin functions */
+#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
+#define SLAPI_PLUGIN_POST_SEARCH_FAIL_FN 513
+
+/* internal preoperation plugin functions */
+#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
+
+/* postoperation plugin to the backend */
+#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
+
+/* matching rule plugin functions */
+#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
+
+/* matching rule plugin arguments */
+#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
+
+
+/* Defined values of SLAPI_PLUGIN_MR_QUERY_OPERATOR: */
+#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
+
+/* Defined values of SLAPI_PLUGIN_MR_USAGE: */
+#define SLAPI_PLUGIN_MR_USAGE_INDEX 0
+#define SLAPI_PLUGIN_MR_USAGE_SORT 1
+
+/* Defined values for matchingRuleEntry accessor functions */
+#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
+
+/* syntax plugin functions and arguments */
+#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
+
+/* ACL plugin functions and arguments */
+#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 ACLPLUGIN_ACCESS_DEFAULT 0
+#define ACLPLUGIN_ACCESS_READ_ON_ENTRY 1
+#define ACLPLUGIN_ACCESS_READ_ON_ATTR 2
+#define ACLPLUGIN_ACCESS_READ_ON_VLV 3
+#define ACLPLUGIN_ACCESS_MODRDN 4
+#define ACLPLUGIN_ACCESS_GET_EFFECTIVE_RIGHTS 5
+
+/* Authorization types */
+#define SLAPI_BE_MAXNESTLEVEL 742
+#define SLAPI_CLIENT_DNS 745
+
+/* Password storage scheme functions and arguments */
+#define SLAPI_PLUGIN_PWD_STORAGE_SCHEME_ENC_FN 800
+#define SLAPI_PLUGIN_PWD_STORAGE_SCHEME_DEC_FN 801 /* only meaningfull for reversible encryption */
+#define SLAPI_PLUGIN_PWD_STORAGE_SCHEME_CMP_FN 802
+
+#define SLAPI_PLUGIN_PWD_STORAGE_SCHEME_NAME 810 /* name of the method: SHA, SSHA... */
+#define SLAPI_PLUGIN_PWD_STORAGE_SCHEME_USER_PWD 811 /* value sent over LDAP */
+#define SLAPI_PLUGIN_PWD_STORAGE_SCHEME_DB_PWD 812 /* value from the DB */
+
+/* entry fetch and entry store values */
+#define SLAPI_PLUGIN_ENTRY_FETCH_FUNC 813
+#define SLAPI_PLUGIN_ENTRY_STORE_FUNC 814
+
+/*
+ * Defined values of SLAPI_PLUGIN_SYNTAX_FLAGS:
+ */
+#define SLAPI_PLUGIN_SYNTAX_FLAG_ORKEYS 1
+#define SLAPI_PLUGIN_SYNTAX_FLAG_ORDERING 2
+
+/* controls we know about */
+#define SLAPI_MANAGEDSAIT 1000
+#define SLAPI_PWPOLICY 1001
+
+/* arguments that are common to all operation */
+#define SLAPI_TARGET_ADDRESS 48 /* target address (dn + uniqueid) should be normalized */
+#define SLAPI_TARGET_UNIQUEID 49 /* target uniqueid of the operation */
+#define SLAPI_TARGET_DN 50 /* target dn of the operation should be normalized */
+#define SLAPI_REQCONTROLS 51 /* request controls */
+
+/* Copies of entry before and after add, mod, mod[r]dn operations */
+#define SLAPI_ENTRY_PRE_OP 52
+#define SLAPI_ENTRY_POST_OP 53
+
+/* LDAPv3 controls to be sent with the operation result */
+#define SLAPI_RESCONTROLS 55
+#define SLAPI_ADD_RESCONTROL 56 /* add result control */
+
+/* Extra notes to be logged within access log RESULT lines */
+#define SLAPI_OPERATION_NOTES 57
+#define SLAPI_OP_NOTE_UNINDEXED 0x01
+
+/* Allows controls to be passed before operation object is created */
+#define SLAPI_CONTROLS_ARG 58
+
+/* specify whether pblock content should be destroyed when the pblock is destroyed */
+#define SLAPI_DESTROY_CONTENT 59
+
+/* add arguments */
+#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 arguments */
+#define SLAPI_BIND_TARGET SLAPI_TARGET_DN
+#define SLAPI_BIND_METHOD 70
+#define SLAPI_BIND_CREDENTIALS 71 /* v3 only */
+#define SLAPI_BIND_SASLMECHANISM 72 /* v3 only */
+/* bind return values */
+#define SLAPI_BIND_RET_SASLCREDS 73 /* v3 only */
+
+/* compare arguments */
+#define SLAPI_COMPARE_TARGET SLAPI_TARGET_DN
+#define SLAPI_COMPARE_TYPE 80
+#define SLAPI_COMPARE_VALUE 81
+
+/* delete arguments */
+#define SLAPI_DELETE_TARGET SLAPI_TARGET_DN
+#define SLAPI_DELETE_EXISTING_ENTRY SLAPI_ADD_EXISTING_DN_ENTRY
+#define SLAPI_DELETE_GLUE_PARENT_ENTRY SLAPI_ADD_PARENT_ENTRY
+
+/* modify arguments */
+#define SLAPI_MODIFY_TARGET SLAPI_TARGET_DN
+#define SLAPI_MODIFY_MODS 90
+#define SLAPI_MODIFY_EXISTING_ENTRY SLAPI_ADD_EXISTING_DN_ENTRY
+
+/* modrdn arguments */
+#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
+
+/*
+ * unnormalized dn argument (useful for MOD, MODRDN and DEL operations to carry
+ * the original non-escaped dn as introduced by the client application)
+ */
+#define SLAPI_ORIGINAL_TARGET_DN 109
+#define SLAPI_ORIGINAL_TARGET SLAPI_ORIGINAL_TARGET_DN
+
+/* search arguments */
+#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
+#define SLAPI_SEARCH_IS_AND 118
+
+/* abandon arguments */
+#define SLAPI_ABANDON_MSGID 120
+
+/* seq access arguments */
+#define SLAPI_SEQ_TYPE 150
+#define SLAPI_SEQ_ATTRNAME 151
+#define SLAPI_SEQ_VAL 152
+
+/* extended operation arguments */
+#define SLAPI_EXT_OP_REQ_OID 160 /* v3 only */
+#define SLAPI_EXT_OP_REQ_VALUE 161 /* v3 only */
+/* extended operation return values */
+#define SLAPI_EXT_OP_RET_OID 162 /* v3 only */
+#define SLAPI_EXT_OP_RET_VALUE 163 /* v3 only */
+
+/* extended filter arguments */
+#define SLAPI_MR_FILTER_ENTRY 170 /* v3 only */
+#define SLAPI_MR_FILTER_TYPE 171 /* v3 only */
+#define SLAPI_MR_FILTER_VALUE 172 /* v3 only */
+#define SLAPI_MR_FILTER_OID 173 /* v3 only */
+#define SLAPI_MR_FILTER_DNATTRS 174 /* v3 only */
+
+/* ldif2db arguments */
+/* ldif file to convert to db */
+#define SLAPI_LDIF2DB_FILE 180
+/* check for duplicate values or not */
+#define SLAPI_LDIF2DB_REMOVEDUPVALS 185
+/* index only this attribute from existing database */
+#define SLAPI_DB2INDEX_ATTRS 186
+/* do not generate attribute indexes */
+#define SLAPI_LDIF2DB_NOATTRINDEXES 187
+/* list if DNs to include */
+#define SLAPI_LDIF2DB_INCLUDE 188
+/* list of DNs to exclude */
+#define SLAPI_LDIF2DB_EXCLUDE 189
+/* generate uniqueid */
+#define SLAPI_LDIF2DB_GENERATE_UNIQUEID 175
+#define SLAPI_LDIF2DB_NAMESPACEID 177
+#define SLAPI_LDIF2DB_ENCRYPT 303
+#define SLAPI_DB2LDIF_DECRYPT 304
+/* uniqueid generation options */
+#define SLAPI_UNIQUEID_GENERATE_NONE 0 /* do not generate */
+#define SLAPI_UNIQUEID_GENERATE_TIME_BASED 1 /* generate time based id */
+#define SLAPI_UNIQUEID_GENERATE_NAME_BASED 2 /* generate name based id */
+
+/* db2ldif arguments */
+/* print keys or not in ldif */
+#define SLAPI_DB2LDIF_PRINTKEY 183
+/* filename to export */
+#define SLAPI_DB2LDIF_FILE 184
+/* dump uniqueid */
+#define SLAPI_DB2LDIF_DUMP_UNIQUEID 176
+#define SLAPI_DB2LDIF_SERVER_RUNNING 197
+
+/* db2ldif/ldif2db/bak2db/db2bak arguments */
+#define SLAPI_BACKEND_INSTANCE_NAME 178
+#define SLAPI_BACKEND_TASK 179
+#define SLAPI_TASK_FLAGS 181
+
+/* bulk import (online wire import) */
+#define SLAPI_BULK_IMPORT_ENTRY 182
+#define SLAPI_BULK_IMPORT_STATE 192
+/* the actual states (these are not pblock args) */
+#define SLAPI_BI_STATE_START 1
+#define SLAPI_BI_STATE_DONE 2
+#define SLAPI_BI_STATE_ADD 3
+/* possible error codes from a bulk import */
+#define SLAPI_BI_ERR_BUSY -23 /* backend is busy; try later */
+
+/* transaction arguments */
+#define SLAPI_PARENT_TXN 190
+#define SLAPI_TXN 191
+
+/*
+ * The following are used to pass information back and forth
+ * between the front end and the back end. The backend
+ * creates a search result set as an opaque structure and
+ * passes a reference to this back to the front end. The
+ * front end uses the backend's iterator entry point to
+ * step through the results. The entry, nentries, and
+ * referrals options, below, are set/read by both the
+ * front end and back end while stepping through the
+ * search results.
+ */
+/* Search result set */
+#define SLAPI_SEARCH_RESULT_SET 193
+/* Search result - next entry returned from search result set */
+#define SLAPI_SEARCH_RESULT_ENTRY 194
+#define SLAPI_SEARCH_RESULT_ENTRY_EXT 1944
+/* Number of entries returned from search */
+#define SLAPI_NENTRIES 195
+/* Any referrals encountered during the search */
+#define SLAPI_SEARCH_REFERRALS 196
+
+#define SLAPI_RESULT_CODE 881
+#define SLAPI_RESULT_TEXT 882
+#define SLAPI_RESULT_MATCHED 883
+
+#define SLAPI_PB_RESULT_TEXT 885
+
+/* Size of the database, in kilobytes */
+#define SLAPI_DBSIZE 199
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _SLAPIPLUGIN */
diff --git a/ldap/servers/slapd/slapi-private.h b/ldap/servers/slapd/slapi-private.h
new file mode 100644
index 00000000..506ab38d
--- /dev/null
+++ b/ldap/servers/slapd/slapi-private.h
@@ -0,0 +1,1212 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+/* slapi-private.h - external header file for some special plugins */
+
+#ifndef _SLAPISTATE
+#define _SLAPISTATE
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <time.h> /* for time_t */
+#include "nspr.h"
+#include "slapi-plugin.h"
+/*
+ * XXXmcs: we can stop including slapi-plugin-compat4.h once we stop using
+ * deprecated functions internally.
+ */
+#include "slapi-plugin-compat4.h"
+
+/*
+ * server shutdown status
+ */
+#define SLAPI_SHUTDOWN_SIGNAL 1
+#define SLAPI_SHUTDOWN_DISKFULL 2
+#define SLAPI_SHUTDOWN_EXIT 3
+
+/* filter */
+#define SLAPI_FILTER_LDAPSUBENTRY 1
+#define SLAPI_FILTER_TOMBSTONE 2
+#define SLAPI_ENTRY_LDAPSUBENTRY 2
+
+/*
+ Optimized filter path. For example the following code was lifted from int.c (syntaxes plugin):
+
+ if(ftype == LDAP_FILTER_EQUALITY_FAST) {
+ tmp=(char *)slapi_ch_calloc(1,(sizeof(Slapi_Value)+sizeof(struct berval)+len+1));
+ tmpval=(Slapi_Value *)tmp;
+ tmpbv=(struct berval *)(tmp + sizeof(Slapi_Value));
+ tmpbv->bv_val=(char *)tmp + sizeof(Slapi_Value) + (sizeof(struct berval));
+ tmpbv->bv_len=len;
+ tmpval->bvp=tmpbv;
+ b = (unsigned char *)&num;
+ memcpy(tmpbv->bv_val,b,len);
+ (*ivals)=(Slapi_Value **)tmpval;
+ }
+
+ The following diagram helps explain the strategy.
+
+ +---------------------------------------------------------------+
+ | Single contiguous allocated block |
+ +------------------------+------------------------+-------------+
+ | Slapi_Value | struct berval | octetstring |
+ +----------------+-------+------------------------+-------------+
+ | struct berval* | ... | ... | char *bv_val | <value> |
+ | v | | | v | |
+ +-------+--------+-------+---------+------+-------+-------------+
+ | ^ | ^
+ |_________________| |________|
+
+ The goal is to malloc one large chunk of memory up front and then manipulate the pointers to point
+ into this chunk. We then can free the whole block at once by calling a single slapi_ch_free (see filterindex.c).
+
+ */
+#define LDAP_FILTER_EQUALITY_FAST 0xaaL
+/*
+ * Slapi_Mods and Slapi_Mod base structures.
+ * Ideally, these would be moved to modutil.c and the structures would be
+ * completely opaque to users of the slapi_mods_...() API. But today some
+ * plugins such as replication use these directly for efficiency reasons.
+ */
+typedef struct slapi_mods
+{
+ LDAPMod **mods;
+ int num_elements;
+ int num_mods;
+ int iterator;
+ int free_mods; /* flag to indicate that the mods were dynamically allocated and needs to be freed */
+}slapi_mods;
+
+typedef struct slapi_mod
+{
+ LDAPMod *mod;
+ int num_elements;
+ int num_values;
+ int iterator;
+ int free_mod; /* flag to inidicate that the mod was dynamically allocated and needs to be freed */
+}slapi_mod;
+
+void slapi_ch_free_ref(void *ptr);
+
+/*
+ * file I/O
+ */
+PRInt32 slapi_read_buffer( PRFileDesc *fd, void *buf, PRInt32 amount );
+PRInt32 slapi_write_buffer( PRFileDesc *fd, void *buf, PRInt32 amount );
+/* rename a file, overwriting the destfilename if it exists */
+int slapi_destructive_rename( const char *srcfilename,
+ const char *destfilename );
+/* make a copy of a file */
+int slapi_copy( const char *srcfilename, const char *destfile );
+
+/* CSN */
+
+typedef struct csn CSN;
+typedef unsigned char CSNType;
+typedef struct csnset_node CSNSet;
+
+#define _CSN_TSTAMP_STRSIZE 8
+#define _CSN_SEQNUM_STRSIZE 4
+#define _CSN_REPLID_STRSIZE 4
+#define _CSN_SUBSEQNUM_STRSIZE 4
+#define _CSN_VALIDCSN_STRLEN (_CSN_TSTAMP_STRSIZE + _CSN_SEQNUM_STRSIZE + \
+ _CSN_REPLID_STRSIZE + _CSN_SUBSEQNUM_STRSIZE)
+#define CSN_STRSIZE (_CSN_VALIDCSN_STRLEN + 1)
+
+#define CSN_TYPE_UNKNOWN 0x00
+#define CSN_TYPE_NONE 0x01
+#define CSN_TYPE_ATTRIBUTE_DELETED 0x03
+#define CSN_TYPE_VALUE_UPDATED 0x04
+#define CSN_TYPE_VALUE_DELETED 0x05
+#define CSN_TYPE_VALUE_DISTINGUISHED 0x06
+
+#define VALUE_NOTFOUND 1
+#define VALUE_PRESENT 2
+#define VALUE_DELETED 3
+
+#define ATTRIBUTE_NOTFOUND 1
+#define ATTRIBUTE_PRESENT 2
+#define ATTRIBUTE_DELETED 3
+
+/*
+ * csn.c
+ */
+typedef PRUint16 ReplicaId;
+/* max 2 byte unsigned int value */
+#define MAX_REPLICA_ID 65535
+/* we will use this value for the replica ID of read only replicas */
+#define READ_ONLY_REPLICA_ID MAX_REPLICA_ID
+CSN *csn_new();
+CSN *csn_new_by_string(const char *s);
+void csn_init_by_csn(CSN *csn1,const CSN *csn2);
+void csn_init_by_string(CSN *csn, const char *s);
+CSN *csn_dup(const CSN *csn);
+void csn_free(CSN **csn);
+void csn_set_replicaid(CSN *csn, ReplicaId rid);
+void csn_set_time(CSN *csn, time_t csntime);
+void csn_set_seqnum(CSN *csn, PRUint16 seqnum);
+ReplicaId csn_get_replicaid(const CSN *csn);
+time_t csn_get_time(const CSN *csn);
+PRUint16 csn_get_seqnum(const CSN *csn);
+char *csn_as_string(const CSN *csn, PRBool replicaIdOrder, char *ss); /* WARNING: ss must be CSN_STRSIZE bytes, or NULL. */
+int csn_compare(const CSN *csn1, const CSN *csn2);
+time_t csn_time_difference(const CSN *csn1, const CSN *csn2);
+size_t csn_string_size();
+char *csn_as_attr_option_string(CSNType t,const CSN *csn,char *ss);
+const CSN *csn_max(const CSN *csn1,const CSN *csn2);
+/* this function allows to expand a csn into a set of csns.
+ The sequence is derived by adding a sequence number to the base csn
+ passed to it. This is useful when a single client operation needs to be
+ expanded into multiple operations. For instance, subtree move operation
+ is split into a sequence of adds and deletes with each add and delete assigned
+ a csn from the set.*/
+int csn_increment_subsequence (CSN *csn);
+
+/*
+ * csnset.c
+ */
+void csnset_add_csn(CSNSet **csnset, CSNType t, const CSN *csn);
+void csnset_insert_csn(CSNSet **csnset, CSNType t, const CSN *csn);
+void csnset_update_csn(CSNSet **csnset, CSNType t, const CSN *csn);
+void csnset_free(CSNSet **csnset);
+const CSN *csnset_get_csn_of_type(const CSNSet *csnset, CSNType t);
+void csnset_purge(CSNSet **csnset, const CSN *csnUpTo);
+size_t csnset_string_size(CSNSet *csnset);
+size_t csnset_size(CSNSet *csnset);
+CSNSet *csnset_dup(const CSNSet *csnset);
+void csnset_as_string(const CSNSet *csnset,char *s);
+void csnset_remove_csn(CSNSet **csnset, CSNType t);
+const CSN *csnset_get_last_csn(const CSNSet *csnset);
+int csnset_contains(const CSNSet *csnset, const CSN *csn);
+const CSN *csnset_get_previous_csn(const CSNSet *csnset, const CSN *csn);
+void* csnset_get_first_csn (const CSNSet *csnset, CSN **csn, CSNType *t);
+void* csnset_get_next_csn (const CSNSet *csnset, void *cookie, CSN **csn, CSNType *t);
+
+/*
+ * csngen.c
+ */
+
+/* error codes returned from CSN generation routines */
+enum {
+ CSN_SUCCESS = 0,
+ CSN_MEMORY_ERROR, /* memory allocation failed */
+ CSN_LIMIT_EXCEEDED, /* timestamp is way out of sync */
+ CSN_INVALID_PARAMETER, /* invalid function argument */
+ CSN_INVALID_FORMAT, /* invalid state format */
+ CSN_LDAP_ERROR, /* LDAP operation failed */
+ CSN_NSPR_ERROR /* NSPR API failure */
+};
+
+typedef struct csngen CSNGen;
+
+/* allocates new csn generator */
+CSNGen *csngen_new (ReplicaId rid, Slapi_Attr *state);
+/* frees csn generator data structure */
+void csngen_free (CSNGen **gen);
+/* generates new csn. If notify is non-zero, the generator calls
+ "generate" functions registered through csngen_register_callbacks call */
+int csngen_new_csn (CSNGen *gen, CSN **csn, PRBool notify);
+/* this function should be called for csns generated with non-zero notify
+ that were unused because the corresponding operation was aborted.
+ The function calls "abort" functions registered through
+ csngen_register_callbacks call */
+void csngen_abort_csn (CSNGen *gen, const CSN *csn);
+/* this function should be called when a remote CSN for the same part of
+ the dit becomes known to the server (for instance, as part of RUV during
+ replication session. In response, the generator would adjust its notion
+ of time so that it does not generate smaller csns */
+int csngen_adjust_time (CSNGen *gen, const CSN* csn);
+/* returns PR_TRUE if the csn was generated by this generator and
+ PR_FALSE otherwise. */
+PRBool csngen_is_local_csn(const CSNGen *gen, const CSN *csn);
+
+/* returns current state of the generator so that it can be saved in the DIT */
+int csngen_get_state (const CSNGen *gen, Slapi_Mod *state);
+
+typedef void (*GenCSNFn)(const CSN *newCsn, void *cbData);
+typedef void (*AbortCSNFn)(const CSN *delCsn, void *cbData);
+/* registers callbacks to be called when csn is created or aborted */
+void* csngen_register_callbacks(CSNGen *gen, GenCSNFn genFn, void *genArg,
+ AbortCSNFn abortFn, void *abortArg);
+/* unregisters callbacks registered via call to csngenRegisterCallbacks */
+void csngen_unregister_callbacks(CSNGen *gen, void *cookie);
+
+/* this functions is periodically called from daemon.c to
+ update time used by all generators */
+void csngen_update_time ();
+
+/* debugging function */
+void csngen_dump_state (const CSNGen *gen);
+
+/* this function tests csn generator */
+void csngen_test ();
+
+/*
+ * State storage management routines
+ *
+ *
+ */
+
+/*
+ * attr_value_find_wsi looks for a particular value (rather, the berval
+ * part of the slapi_value v) and returns it in "value". The function
+ * returns VALUE_PRESENT, VALUE_DELETED, or VALUE_NOTFOUND.
+ */
+int attr_value_find_wsi(Slapi_Attr *a, const struct berval *bval, Slapi_Value **value);
+
+/*
+ * entry_attr_find_wsi takes an entry and a type and looks for the
+ * attribute. If the attribute is found on the list of existing attributes,
+ * it is returned in "a" and the function returns ATTRIBUTE_PRESENT. If the attribute is
+ * found on the deleted list, "a" is set and the function returns ATTRIBUTE_DELETED.
+ * If the attribute is not found on either list, the function returns ATTRIBUTE_NOTFOUND.
+ */
+int entry_attr_find_wsi(Slapi_Entry *e, const char *type, Slapi_Attr **a);
+
+/*
+ * entry_add_present_attribute_wsi adds an attribute to the entry.
+ */
+int entry_add_present_attribute_wsi(Slapi_Entry *e, Slapi_Attr *a);
+
+/*
+ * entry_add_deleted_attribute_wsi adds a deleted attribute to the entry.
+ */
+int entry_add_deleted_attribute_wsi(Slapi_Entry *e, Slapi_Attr *a);
+
+/*
+ * slapi_entry_apply_mods_wsi is similar to slapi_entry_apply_mods. It also
+ * handles the state storage information. "csn" is the CSN associated with
+ * this modify operation.
+ */
+int entry_apply_mods_wsi(Slapi_Entry *e, Slapi_Mods *smods, const CSN *csn, int urp);
+int entry_first_deleted_attribute( const Slapi_Entry *e, Slapi_Attr **a);
+int entry_next_deleted_attribute( const Slapi_Entry *e, Slapi_Attr **a);
+
+/* entry.c */
+int entry_apply_mods( Slapi_Entry *e, LDAPMod **mods );
+
+int slapi_entries_diff(Slapi_Entry **old_entries, Slapi_Entry **new_entries, int testall, const char *logging_prestr, const int force_update, void *plg_id);
+
+/* entrywsi.c */
+CSN* entry_assign_operation_csn ( Slapi_PBlock *pb, Slapi_Entry *e, Slapi_Entry *parententry );
+const CSN *entry_get_maxcsn ( const Slapi_Entry *entry );
+void entry_set_maxcsn ( Slapi_Entry *entry, const CSN *csn );
+const CSN *entry_get_dncsn(const Slapi_Entry *entry);
+const CSNSet *entry_get_dncsnset(const Slapi_Entry *entry);
+int entry_add_dncsn(Slapi_Entry *entry, const CSN *csn);
+int entry_set_csn(Slapi_Entry *entry, const CSN *csn);
+void entry_purge_state_information(Slapi_Entry *entry, const CSN *csnUpto);
+void entry_add_rdn_csn(Slapi_Entry *e, const CSN *csn);
+/* this adds a csn to the entry's e_dncsnset but makes sure the set is in increasing csn order */
+#define ENTRY_DNCSN_INCREASING 0x1 /* for flags below */
+int entry_add_dncsn_ext(Slapi_Entry *entry, const CSN *csn, PRUint32 flags);
+
+/* attr.c */
+Slapi_Attr *slapi_attr_init_locking_optional(Slapi_Attr *a, const char *type, PRBool use_lock, PRBool ref_count);
+int attr_set_csn( Slapi_Attr *a, const CSN *csn);
+int attr_set_deletion_csn( Slapi_Attr *a, const CSN *csn);
+const CSN *attr_get_deletion_csn(const Slapi_Attr *a);
+int attr_first_deleted_value( Slapi_Attr *a, Slapi_Value **v );
+int attr_next_deleted_value( Slapi_Attr *a, int hint, Slapi_Value **v);
+void attr_purge_state_information(Slapi_Entry *entry, Slapi_Attr *attr, const CSN *csnUpto);
+Slapi_Value **attr_get_present_values(const Slapi_Attr *a);
+int attr_add_deleted_value(Slapi_Attr *a, const Slapi_Value *v);
+
+/* value.c */
+Slapi_Value *value_new(const struct berval *bval, CSNType t, const CSN *csn);
+Slapi_Value *value_init(Slapi_Value *v, const struct berval *bval, CSNType t, const CSN *csn);
+void value_done(Slapi_Value *v);
+Slapi_Value *value_update_csn( Slapi_Value *value, CSNType t, const CSN *csn);
+Slapi_Value *value_add_csn( Slapi_Value *value, CSNType t, const CSN *csn);
+const CSN *value_get_csn( const Slapi_Value *value, CSNType t );
+const CSNSet *value_get_csnset ( const Slapi_Value *value);
+Slapi_Value *value_remove_csn( Slapi_Value *value, CSNType t);
+int value_contains_csn( const Slapi_Value *value, CSN *csn);
+
+/* dn.c */
+/* this functions should only be used for dns allocated on the stack */
+Slapi_DN *slapi_sdn_init(Slapi_DN *sdn);
+Slapi_DN *slapi_sdn_init_dn_byref(Slapi_DN *sdn,const char *dn);
+Slapi_DN *slapi_sdn_init_dn_byval(Slapi_DN *sdn,const char *dn);
+Slapi_DN *slapi_sdn_init_dn_passin(Slapi_DN *sdn,const char *dn);
+Slapi_DN *slapi_sdn_init_ndn_byref(Slapi_DN *sdn,const char *dn);
+Slapi_DN *slapi_sdn_init_ndn_byval(Slapi_DN *sdn,const char *dn);
+Slapi_DN *slapi_sdn_init_dn_ndn_byref(Slapi_DN *sdn,const char *dn);
+
+/* filter.c */
+int filter_flag_is_set(const Slapi_Filter *f,unsigned char flag);
+char *slapi_filter_to_string(const Slapi_Filter *f, char *buffer, size_t bufsize);
+
+/* operation.c */
+
+#define OP_FLAG_PS 0x0001
+#define OP_FLAG_PS_CHANGESONLY 0x0002
+#define OP_FLAG_GET_EFFECTIVE_RIGHTS 0x0004
+#define OP_FLAG_REPLICATED 0x0008 /* A Replicated Operation */
+#define OP_FLAG_REPL_FIXUP 0x0010 /* A Fixup Operation, generated as a consequence of a Replicated Operation. */
+#define OP_FLAG_INTERNAL 0x0020 /* An operation generated by the core server or a plugin. */
+#define OP_FLAG_ACTION_LOG_ACCESS 0x0040
+#define OP_FLAG_ACTION_LOG_AUDIT 0x0080
+#define OP_FLAG_ACTION_SCHEMA_CHECK 0x0100
+#define OP_FLAG_ACTION_LOG_CHANGES 0x0200
+#define OP_FLAG_ACTION_INVOKE_FOR_REPLOP 0x0400
+#define OP_FLAG_NEVER_CHAIN SLAPI_OP_FLAG_NEVER_CHAIN /* 0x0800 */
+#define OP_FLAG_TOMBSTONE_ENTRY 0x1000
+#define OP_FLAG_RESURECT_ENTRY 0x2000
+#define OP_FLAG_LEGACY_REPLICATION_DN 0x4000 /* Operation done by legacy replication DN */
+#define OP_FLAG_ACTION_NOLOG 0x8000 /* Do not log the entry in audit log or change log */
+
+CSN *operation_get_csn(Slapi_Operation *op);
+void operation_set_csn(Slapi_Operation *op,CSN *csn);
+void operation_set_flag(Slapi_Operation *op,int flag);
+void operation_clear_flag(Slapi_Operation *op,int flag);
+int operation_is_flag_set(Slapi_Operation *op,int flag);
+unsigned long operation_get_type(Slapi_Operation *op);
+
+/*
+ * From ldap.h
+ * #define LDAP_MOD_ADD 0x00
+ * #define LDAP_MOD_DELETE 0x01
+ * #define LDAP_MOD_REPLACE 0x02
+ */
+#define LDAP_MOD_IGNORE 0x09
+
+
+/* dl.c */
+typedef struct datalist DataList;
+
+typedef int (*CMPFN) (const void *el1, const void *el2);
+typedef void (*FREEFN) (void **);
+DataList* dl_new ();
+void dl_free (DataList **dl);
+void dl_init (DataList *dl, int init_alloc);
+void dl_cleanup (DataList *dl, FREEFN freefn);
+void dl_add (DataList *dl, void *element);
+void dl_add_index(DataList *dl, void *element, int index);
+void *dl_replace(const DataList *dl, const void *elementOld, void *elementNew, CMPFN cmpfn, FREEFN freefn);
+void *dl_get_first (const DataList *dl, int *cookie);
+void *dl_get_next (const DataList *dl, int *cookie);
+void *dl_get (const DataList *dl, const void *element, CMPFN cmpfn);
+void *dl_delete (DataList *dl, const void *element, CMPFN cmpfn, FREEFN freefn);
+int dl_get_count (const DataList *dl);
+
+struct ava {
+ char *ava_type;
+ struct berval ava_value; /* JCM SLAPI_VALUE! */
+ void *ava_private; /* data private to syntax handler */
+};
+
+typedef enum{
+ FILTER_TYPE_SUBSTRING,
+ FILTER_TYPE_AVA,
+ FILTER_TYPE_PRES
+}filter_type_t;
+
+/*
+ * vattr entry routines.
+ * vattrcache private (for the moment)
+ */
+#define SLAPI_ENTRY_VATTR_NOT_RESOLVED -1
+#define SLAPI_ENTRY_VATTR_RESOLVED_ABSENT -2
+#define SLAPI_ENTRY_VATTR_RESOLVED_EXISTS 0
+
+int slapi_entry_vattrcache_merge_sv(Slapi_Entry *e, const char *type, Slapi_ValueSet *vals);
+int slapi_entry_vattrcache_find_values_and_type_ex( const Slapi_Entry *e,
+ const char *type,
+ Slapi_ValueSet ***results,
+ char ***actual_type_name);
+SLAPI_DEPRECATED int
+slapi_entry_vattrcache_find_values_and_type( const Slapi_Entry *e,
+ const char *type,
+ Slapi_ValueSet **results,
+ char **actual_type_name);
+int slapi_entry_vattrcache_findAndTest(const Slapi_Entry *e, const char *type,
+ Slapi_Filter *f,
+ filter_type_t filter_type,
+ int *rc);
+
+int slapi_vattrcache_iscacheable( const char * type );
+void slapi_vattrcache_cache_all();
+void slapi_vattrcache_cache_none();
+
+int vattr_test_filter(/* Entry we're interested in */ Slapi_Entry *e,
+ Slapi_Filter *f,
+ filter_type_t filter_type,
+ char *type);
+
+/* filter routines */
+
+int test_substring_filter( Slapi_PBlock *pb, Slapi_Entry *e,
+ struct slapi_filter *f,
+ int verify_access,int only_check_access, int *access_check_done);
+int test_ava_filter( Slapi_PBlock *pb, Slapi_Entry *e, Slapi_Attr *a,
+ struct ava *ava, int ftype, int verify_access,
+ int only_check_access, int *access_check_done);
+int test_presence_filter( Slapi_PBlock *pb, Slapi_Entry *e, char *type,
+ int verify_access, int only_check_access, int *access_check_done);
+
+/* this structure allows to address entry by dn or uniqueid */
+typedef struct entry_address
+{
+ char *dn;
+ char *udn; /* unnormalized dn */
+ char *uniqueid;
+}entry_address;
+
+/*
+ * LDAP Operation input parameters.
+ */
+typedef struct slapi_operation_parameters
+{
+ unsigned long operation_type; /* SLAPI_OPERATION_ADD, SLAPI_OPERATION_MODIFY ... */
+ entry_address target_address; /* address of target entry */
+ CSN *csn; /* The Change Sequence Number assigned to this operation. */
+
+ LDAPControl **request_controls;/* array v3 LDAPMessage controls */
+
+ union
+ {
+ struct add_parameters
+ {
+ struct slapi_entry *target_entry;
+ char *parentuniqueid;
+ } p_add;
+
+ struct bind_parameters
+ {
+ int bind_method;
+ struct berval *bind_creds;
+ char *bind_saslmechanism; /* v3 sasl mechanism name */
+ struct berval *bind_ret_saslcreds; /* v3 serverSaslCreds */
+ } p_bind;
+
+ struct compare_parameters
+ {
+ struct ava compare_ava;
+ } p_compare;
+
+ struct modify_parameters
+ {
+ LDAPMod **modify_mods;
+ } p_modify;
+
+ struct modrdn_parameters
+ {
+ char *modrdn_newrdn;
+ int modrdn_deloldrdn;
+ entry_address modrdn_newsuperior_address; /* address of the superior entry */
+ LDAPMod **modrdn_mods; /* modifiers name and timestamp */
+ } p_modrdn;
+
+ struct search_parameters
+ {
+ int search_scope;
+ int search_deref;
+ int search_sizelimit;
+ int search_timelimit;
+ struct slapi_filter *search_filter;
+ char *search_strfilter;
+ char **search_attrs;
+ int search_attrsonly;
+ int search_is_and;
+ } p_search;
+
+ struct abandon_parameters
+ {
+ int abandon_targetmsgid;
+ } p_abandon;
+
+ struct extended_parameters
+ {
+ char *exop_oid;
+ struct berval *exop_value;
+ } p_extended;
+ } p;
+} slapi_operation_parameters;
+
+struct slapi_operation_parameters *operation_parameters_new();
+struct slapi_operation_parameters *operation_parameters_dup(struct slapi_operation_parameters *sop);
+void operation_parameters_done(struct slapi_operation_parameters *sop);
+void operation_parameters_free(struct slapi_operation_parameters **sop);
+
+
+/*
+ * errormap.c
+ */
+char *slapd_pr_strerror( const PRErrorCode prerrno );
+const char *slapd_system_strerror( const int syserrno );
+const char *slapd_versatile_strerror( const PRErrorCode prerrno );
+
+
+/*
+ * localhost.c
+ */
+char* get_localhost_DNS();
+/* Return the fully-qualified DNS name of this machine.
+ The caller should _not_ free this pointer. */
+char* get_localhost_DN();
+
+/*
+ * Reference-counted objects
+ */
+typedef void (*FNFree) (void **);
+typedef struct object Object;
+Object *object_new(void *user_data, FNFree destructor);
+void object_acquire(Object *o);
+void object_release(Object *o);
+void *object_get_data(Object *o);
+
+/* Sets of reference-counted objects */
+#define OBJSET_SUCCESS 0
+#define OBJSET_ALREADY_EXISTS 1
+#define OBJSET_NO_SUCH_OBJECT 2
+typedef int (*CMPFn) (Object *set, const void *name);
+typedef struct objset Objset;
+Objset *objset_new(FNFree objset_destructor);
+void objset_delete(Objset **set);
+int objset_add_obj(Objset *set, Object *object);
+Object *objset_find(Objset *set, CMPFn compare_fn, const void *name);
+int objset_remove_obj(Objset *set, Object *object);
+Object *objset_first_obj(Objset *set);
+Object *objset_next_obj(Objset *set, Object *previous);
+int objset_is_empty(Objset *set);
+int objset_size(Objset *set);
+
+/* backend management */
+typedef struct index_config
+{
+ char *attr_name; /* attr name: dn, cn, etc. */
+ char *index_type; /* space terminated list of indexes;
+ possible types: "eq" "sub" "pres" "approx" */
+ int system; /* marks this index as system */
+}IndexConfig;
+
+int be_create_instance (const char *type, /* for now, must be "ldbm" */
+ const char *name, /* gloably unique instance name */
+ const char *root, /* backend root, i.e. o=mcom.com */
+ int cache_size, /* cache size in bytes; 0 for default */
+ IndexConfig *indexes, /* indexes in addition to standard */
+ int index_count, /* number of elements in indexes */
+ void *plugin_identity /* identity of the calling plugin */
+ );
+int be_remove_instance (const char *type, /* for now, must be "ldbm" */
+ const char *name, /* gloably unique instance name */
+ void *plugin_identity /* identity of the calling plugin */
+ );
+
+void be_set_sizelimit(Slapi_Backend * be, int sizelimit);
+void be_set_timelimit(Slapi_Backend * be, int timelimit);
+
+/* used by mapping tree to delay sending of result code when several
+ * backend are parsed
+ */
+void slapi_set_ldap_result( Slapi_PBlock *pb, int err, char *matched,
+ char *text, int nentries, struct berval **urls );
+void slapi_send_ldap_result_from_pb( Slapi_PBlock *pb);
+
+/* mapping tree utility functions */
+typedef struct mt_node mapping_tree_node;
+mapping_tree_node *slapi_get_mapping_tree_node_by_dn(const Slapi_DN *dn);
+char* slapi_get_mapping_tree_node_configdn(const Slapi_DN *root);
+const Slapi_DN* slapi_get_mapping_tree_node_root(const mapping_tree_node *node);
+const char* slapi_get_mapping_tree_config_root ();
+Slapi_Backend *slapi_mapping_tree_find_backend_for_sdn(Slapi_DN *sdn);
+/* possible flags to check for */
+#define SLAPI_MTN_LOCAL 0x1
+#define SLAPI_MTN_PRIVATE 0x2
+#define SLAPI_MTN_READONLY 0x4
+PRBool slapi_mapping_tree_node_is_set (const mapping_tree_node *node,
+ PRUint32 flag);
+Slapi_DN* slapi_mtn_get_dn(mapping_tree_node *node);
+int slapi_mapping_tree_select_and_check(Slapi_PBlock *pb,char *newdn,
+ Slapi_Backend **be, Slapi_Entry **referral, char *errorbuf);
+int slapi_mapping_tree_select_all(Slapi_PBlock *pb, Slapi_Backend **be_list,
+ Slapi_Entry **referral_list, char *errorbuf);
+void slapi_mapping_tree_free_all(Slapi_Backend **be_list,
+ Slapi_Entry **referral_list);
+
+/* Mapping Tree */
+int slapi_mapping_tree_select(Slapi_PBlock *pb, Slapi_Backend **be, Slapi_Entry **referral, char *error_string);
+char ** slapi_mtn_get_referral(const Slapi_DN *sdn);
+int slapi_mtn_set_referral(const Slapi_DN *sdn, char ** referral);
+int slapi_mtn_set_state(const Slapi_DN *sdn, char *state);
+char * slapi_mtn_get_state(const Slapi_DN *sdn);
+void slapi_mtn_be_set_readonly(Slapi_Backend *be, int readonly);
+void slapi_mtn_be_stopping(Slapi_Backend *be);
+void slapi_mtn_be_started(Slapi_Backend *be);
+void slapi_mtn_be_disable(Slapi_Backend *be);
+void slapi_mtn_be_enable(Slapi_Backend *be);
+const char *slapi_mtn_get_backend_name(const Slapi_DN *sdn);
+
+void slapi_be_stopping (Slapi_Backend *be);
+void slapi_be_free (Slapi_Backend **be);
+void slapi_be_Rlock (Slapi_Backend *be);
+void slapi_be_Wlock (Slapi_Backend *be);
+void slapi_be_Unlock (Slapi_Backend *be);
+
+/* components */
+struct slapi_componentid {
+ char * sci_magic;
+ const struct slapdplugin * sci_plugin;
+ char * sci_component_name;
+};
+
+struct slapi_componentid *
+generate_componentid ( struct slapdplugin * pp , char * name );
+void release_componentid ( struct slapi_componentid * id );
+struct slapi_componentid * plugin_get_default_component_id();
+
+/* interface for component mgmt */
+/* Well-known components DNs */
+/* Should be documented somehow for the chaining backend */
+
+#define COMPONENT_BASE_DN "cn=components,cn=config"
+#define COMPONENT_ROLES "cn=roles,"COMPONENT_BASE_DN
+#define COMPONENT_RESLIMIT "cn=resource limits,"COMPONENT_BASE_DN
+#define COMPONENT_PWPOLICY "cn=password policy,"COMPONENT_BASE_DN
+#define COMPONENT_CERT_AUTH "cn=certificate-based authentication,"COMPONENT_BASE_DN
+
+/* Component names for logging */
+#define SLAPI_COMPONENT_NAME_NSPR "Netscape runtime"
+#define SLAPI_COMPONENT_NAME_LDAPSDK "LDAP sdk"
+
+/* return the list of attr defined in the schema matching the attr flags */
+char ** slapi_schema_list_attribute_names(unsigned long flag);
+CSN *dup_global_schema_csn();
+
+/* misc function for the chaining backend */
+char * slapi_get_rootdn(); /* return the directory manager dn in use */
+
+/* plugin interface to bulk import */
+/* This function initiates bulk import. The pblock must contain
+ SLAPI_LDIF2DB_GENERATE_UNIQUEID -- currently always set to TIME_BASED
+ SLAPI_CONNECTION -- connection over which bulk import is coming
+ SLAPI_BACKEND -- the backend being imported
+ or
+ SLAPI_TARGET_DN that contains root of the imported area.
+ The function returns LDAP_SUCCESS or LDAP error code
+*/
+int slapi_start_bulk_import (Slapi_PBlock *pb);
+
+/* This function adds an entry to the bulk import. The pblock must contain
+ SLAPI_CONNECTION -- connection over which bulk import is coming
+ SLAPI_BACKEND -- optional backend pointer; if missing computed based on entry dn
+ The function returns LDAP_SUCCESS or LDAP error code
+*/
+int slapi_import_entry (Slapi_PBlock *pb, Slapi_Entry *e);
+
+/* This function stops bulk import. The pblock must contain
+ SLAPI_CONNECTION -- connection over which bulk import is coming
+ SLAPI_BACKEND -- the backend being imported
+ or
+ SLAPI_TARGET_DN that contains root of the imported area.
+ The function returns LDAP_SUCCESS or LDAP error code
+*/
+int slapi_stop_bulk_import (Slapi_PBlock *pb);
+
+/* allows plugins to close inbound connection */
+void slapi_disconnect_server(Slapi_Connection *conn);
+
+/* functions to look up instance names by suffixes (backend_manager.c) */
+int slapi_lookup_instance_name_by_suffixes(char **included,
+ char **excluded, char ***instances);
+int slapi_lookup_instance_name_by_suffix(char *suffix,
+ char ***suffixes, char ***instances, int isexact);
+
+/* begin and end the task subsystem */
+void task_init(void);
+void task_shutdown(void);
+
+/* for reversible encyrption */
+#define SLAPI_MB_CREDENTIALS "nsmultiplexorcredentials"
+#define SLAPI_REP_CREDENTIALS "nsds5ReplicaCredentials"
+int pw_rever_encode(Slapi_Value **vals, char * attr_name);
+int pw_rever_decode(char *cipher, char **plain, const char * attr_name);
+
+/* config routines */
+
+int slapi_config_get_readonly();
+
+/*
+ * charray.c
+ */
+void charray_add( char ***a, char *s );
+void charray_merge( char ***a, char **s, int copy_strs );
+void charray_free( char **array );
+int charray_inlist( char **a, char *s );
+int charray_utf8_inlist( char **a, char *s );
+char ** charray_dup( char **a );
+char ** str2charray( char *str, char *brkstr );
+int charray_remove(char **a,const char *s);
+char ** cool_charray_dup( char **a );
+void cool_charray_free( char **array );
+void charray_subtract( char **a, char **b, char ***c );
+int charray_get_index(char **array, char *s);
+
+
+/******************************************************************************
+ * value array routines.
+ *
+ * It is unclear if these should ever be public, but today they are used by
+ * some plugins. They would need to be renamed to have a slapi_ prefix at
+ * the very least before we make them public.
+ */
+void valuearray_add_value(Slapi_Value ***vals, const Slapi_Value *addval);
+void valuearray_add_value_fast(Slapi_Value ***vals, Slapi_Value *addval, int nvals, int *maxvals, int exact, int passin);
+void valuearray_add_valuearray( Slapi_Value ***vals, Slapi_Value **addvals, PRUint32 flags );
+void valuearray_add_valuearray_fast( Slapi_Value ***vals, Slapi_Value **addvals, int nvals, int naddvals, int *maxvals, int exact, int passin );
+
+
+/******************************************************************************
+ * Database plugin interface.
+ *
+ * Prior to the 5.0 release, this was a public interface that lived in
+ * slapi-plugin.h, so it is still a good idea to avoid making changes to it
+ * that are not backwards compatible.
+ */
+
+/* plugin type */
+#define SLAPI_PLUGIN_DATABASE 1
+
+/* database plugin functions */
+#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_DB_FLUSH_FN 211
+#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
+#define SLAPI_PLUGIN_DB_DB2INDEX_FN 228
+#define SLAPI_PLUGIN_DB_NEXT_SEARCH_ENTRY_EXT_FN 229
+#define SLAPI_PLUGIN_DB_ENTRY_RELEASE_FN 230
+#define SLAPI_PLUGIN_DB_INIT_INSTANCE_FN 231
+#define SLAPI_PLUGIN_DB_WIRE_IMPORT_FN 234
+#if defined(UPGRADEDB)
+#define SLAPI_PLUGIN_DB_UPGRADEDB_FN 235
+#endif
+/* database plugin-specific parameters */
+#define SLAPI_PLUGIN_DB_NO_ACL 250
+#define SLAPI_PLUGIN_DB_RMDB_FN 280
+
+/**** End of database plugin interface. **************************************/
+
+
+/******************************************************************************
+ * Interface to the UniqueID generator (uniqueid.c)
+ *
+ * This could be made public someday, although it is a large interface and
+ * not all of the elements follow the SLAPI_ naming convention.
+ */
+ /* error codes */
+#define UID_UPDATE_SHUTDOWN -1 /* update state information only during server shutdown */
+#define UID_UPDATE_INTERVAL 600000 /* 10 minutes */
+
+enum {UID_SUCCESS, /* operation was successfull */
+ UID_ERROR_BASE=10,/* start of the error codes */
+ UID_BADDATA, /* invalid parameter passed to a function */
+ UID_MEMORY_ERROR, /* memory allocation failed */
+ UID_SYSTEM_ERROR, /* I/O failed (currently, further details
+ can be obtained using PR_GetError */
+ UID_TIME_ERROR, /* UUID can't be generated because system
+ time has not been update */
+ UID_ERROR_END /* end of the error codes */
+ };
+
+/* Function: slapi_uniqueIDNew
+ Description: allocates new id
+ Paramters: none
+ Return: pointer to the newly allocated id if successful
+ NULL if the system is out of memory
+ */
+Slapi_UniqueID* slapi_uniqueIDNew( void );
+
+/* Function: slapi_uniqueIDDestroy
+ Description: destroys UniqueID object and sets its pointer to NULL
+ Parameters: uId - id to destroy
+ Return: none
+ */
+void slapi_uniqueIDDestroy(Slapi_UniqueID **uId);
+
+/* Function: slapi_uniqueIDCompare
+ Description: this function compares two ids (byte by byte).
+ Parameters: uId1, uId2 - ids to compare
+ Return: -1 if uId1 < uId2
+ 0 if uId2 == uId2
+ 1 if uId2 > uId2
+ UID_BADDATA if invalid pointer passed to the function
+*/
+int slapi_uniqueIDCompare(const Slapi_UniqueID *uId1, const Slapi_UniqueID *uId2);
+
+int slapi_uniqueIDCompareString(const char *uuid1, const char *uuid2);
+
+/* Function: slapi_uniqueIDFormat
+ Description: this function converts entryId to its string representation.
+ The id format is HH-HHHHHHHH-HHHHHHHH-HHHHHHHH-HHHHHHHH
+ where H is a hex digit.
+ Parameters: uId - unique id
+ buff - buffer in which id is returned;
+ Return: UID_SUCCESS - function was successfull
+ UID_BADDATA - invalid parameter passed to the function
+*/
+int slapi_uniqueIDFormat(const Slapi_UniqueID *uId, char **buff);
+
+/* Function: slapi_uniqueIDScan
+ Description: this function converts a string buffer into uniqueID.
+ Currently, it only supports
+ HH-HHHHHHHH-HHHHHHHH-HHHHHHHH-HHHHHHHH data format.
+ Parameters: uId - unique id to be returned
+ buff - buffer with uniqueID.
+ Return: UID_SUCCESS - function was successfull
+ UID_BADDATA - null parameter(s) or bad format
+*/
+int slapi_uniqueIDScan(Slapi_UniqueID *uId, const char *buff);
+
+
+/* Function: slapi_uniqueIDIsUUID
+ Description: tests if given entry id is of UUID type
+ Parameters: uId - unique id to test
+ Return UID_SUCCESS - function was successfull
+ UID_BADDATA - invalid data passed to the function
+ */
+int slapi_uniqueIDIsUUID(const Slapi_UniqueID *uId);
+
+/* Name: slapi_uniqueIDSize
+ Description: returns size of the string version of uniqueID in bytes
+ Parameters: none
+ Return: size of the string version of uniqueID in bytes
+ */
+int slapi_uniqueIDSize( void );
+
+/* Name: slapi_uniqueIDDup
+ Description: duplicates an UniqueID object
+ Parameters: uId - id to duplicate
+ Return: duplicate of the Id
+ */
+Slapi_UniqueID* slapi_uniqueIDDup(Slapi_UniqueID *uId);
+
+/*
+ * interface to UniqueID generator - uniqueidgen.c
+ */
+
+/* Function: slapi_uniqueIDGenerate
+ Description: this function generates uniqueid in a singlethreaded
+ environment.
+ Parameters: uId - buffer to receive the ID.
+ Return: UID_SUCCESS if function succeeds;
+ UID_BADDATA if invalid pointer passed to the function;
+ UID_SYSTEM_ERROR update to persistent storage failed.
+*/
+
+int slapi_uniqueIDGenerate(Slapi_UniqueID *uId);
+
+/* Function: slapi_uniqueIDGenerateString
+ Description: this function generates uniqueid an returns it as a string
+ in a singlethreaded environment. This function returns the
+ data in the format generated by slapi_uniqueIDFormat.
+ Parameters: uId - buffer to receive the ID. Caller is responsible for
+ freeing uId buffer.
+ Return: UID_SUCCESS if function succeeds;
+ UID_BADDATA if invalid pointer passed to the function;
+ UID_MEMORY_ERROR if malloc fails;
+ UID_SYSTEM_ERROR update to persistent storage failed.
+*/
+
+int slapi_uniqueIDGenerateString(char **uId);
+
+/* Function: slapi_uniqueIDGenerateMT
+ Description: this function generates entry id in a multithreaded
+ environment. Used in conjunction with
+ uniqueIDUpdateState function.
+ Parameters: uId - structure in which new id will be returned.
+ Return: UID_SUCCESS if function succeeds;
+ UID_BADDATA if invalid pointer passed to the function;
+ UID_TIME_ERROR uniqueIDUpdateState must be called
+ before the id can be generated.
+*/
+
+int slapi_uniqueIDGenerateMT(Slapi_UniqueID *uId);
+
+/* Function: slapi_uniqueIDGenerateMTString
+ Description: this function generates uniqueid and returns it as a
+ string in a multithreaded environment. Used in conjunction
+ with uniqueIDUpdateState function.
+ Parameters: uId - buffer in which new id will be returned. Caller is
+ responsible for freeing uId buffer.
+ Return: UID_SUCCESS if function succeeds;
+ UID_BADDATA if invalid pointer passed to the function;
+ UID_MEMORY_ERROR if malloc fails;
+ UID_TIME_ERROR uniqueIDUpdateState must be called
+ before the id can be generated.
+*/
+
+int slapi_uniqueIDGenerateMTString(char **uId);
+
+/* Function: slapi_uniqueIDGenerateFromName
+ Description: this function generates an id from a name. See uuid
+ draft for more details. This function can be used in
+ both a singlethreaded and a multithreaded environments.
+ Parameters: uId - generated id
+ uIDBase - uid used for generation to distinguish among
+ different name spaces
+ name - buffer containing name from which to generate the id
+ namelen - length of the name buffer
+ Return: UID_SUCCESS if function succeeds
+ UID_BADDATA if invalid argument is passed to the
+ function.
+*/
+
+int slapi_uniqueIDGenerateFromName(Slapi_UniqueID *uId,
+ const Slapi_UniqueID *uIdBase,
+ const void *name, int namelen);
+
+/* Function: slapi_uniqueIDGenerateFromName
+ Description: this function generates an id from a name and returns
+ it in the string format. See uuid draft for more
+ details. This function can be used in both a
+ singlethreaded and a multithreaded environments.
+ Parameters: uId - generated id in string form
+ uIDBase - uid used for generation to distinguish among
+ different name spaces in string form. NULL means to use
+ empty id as the base.
+ name - buffer containing name from which to generate the id
+ namelen - length of the name buffer
+ Return: UID_SUCCESS if function succeeds
+ UID_BADDATA if invalid argument is passed to the
+ function.
+*/
+
+int slapi_uniqueIDGenerateFromNameString(char **uId,
+ const char *uIdBase,
+ const void *name, int namelen);
+
+/**** End of UniqueID generator interface. ***********************************/
+
+
+/*****************************************************************************
+ * JCMREPL - Added for the replication plugin.
+ */
+
+/* Front end configuration */
+
+typedef int (*dseCallbackFn)(Slapi_PBlock *, Slapi_Entry *, Slapi_Entry *,
+ int *, char*, void *);
+
+/*
+ * Note: DSE callback functions MUST return one of these three values:
+ *
+ * SLAPI_DSE_CALLBACK_OK -- no errors occurred; apply changes.
+ * SLAPI_DSE_CALLBACK_ERROR -- an error occurred; don't apply changes.
+ * SLAPI_DSE_CALLBACK_DO_NOT_APPLY -- no error, but do not apply changes.
+ *
+ * SLAPI_DSE_CALLBACK_DO_NOT_APPLY should only be returned by modify
+ * callbacks (i.e., those registered with operation==SLAPI_OPERATION_MODIFY).
+ * A return value of SLAPI_DSE_CALLBACK_DO_NOT_APPLY is treated the same as
+ * SLAPI_DSE_CALLBACK_ERROR for all other operations.
+ */
+#define SLAPI_DSE_CALLBACK_OK (1)
+#define SLAPI_DSE_CALLBACK_ERROR (-1)
+#define SLAPI_DSE_CALLBACK_DO_NOT_APPLY (0)
+
+/*
+ * Flags for slapi_config_register_callback() and
+ * slapi_config_remove_callback()
+ */
+#define DSE_FLAG_PREOP 0x0001
+#define DSE_FLAG_POSTOP 0x0002
+
+int slapi_config_register_callback(int operation, int flags, const char *base, int scope, const char *filter, dseCallbackFn fn, void *fn_arg);
+int slapi_config_remove_callback(int operation, int flags, const char *base, int scope, const char *filter, dseCallbackFn fn);
+int config_is_slapd_lite( void );
+
+#define SLAPI_RTN_BIT_FETCH_EXISTING_DN_ENTRY 0
+#define SLAPI_RTN_BIT_FETCH_PARENT_ENTRY 1
+#define SLAPI_RTN_BIT_FETCH_NEWPARENT_ENTRY 2
+#define SLAPI_RTN_BIT_FETCH_TARGET_ENTRY 3
+#define SLAPI_RTN_BIT_FETCH_EXISTING_UNIQUEID_ENTRY 4
+
+/* Attribute use to mark entries that had a replication conflict on the DN */
+#define ATTR_NSDS5_REPLCONFLICT "nsds5ReplConflict"
+
+/* Time */
+#include <time.h> /* difftime, localtime_r, mktime */
+/* Duplicated: time_t read_localTime (struct berval* from); */
+time_t time_plus_sec(time_t l, long r);
+char* format_localTime(time_t from);
+time_t read_localTime(struct berval* from);
+time_t parse_localTime(char* from);
+void write_localTime(time_t from, struct berval* into);
+time_t current_time( void );
+char* format_genTime(time_t from);
+void write_genTime(time_t from, struct berval* into);
+time_t read_genTime(struct berval* from);
+time_t parse_genTime(char* from);
+
+/* Client SSL code */
+int slapd_SSL_client_init( void );
+int slapd_security_library_is_initialized( void );
+int slapd_SSL_client_bind_s(LDAP* ld, char* DN, char* pw, int use_SSL, int LDAPv);
+int slapd_sasl_ext_client_bind(LDAP* ld, int **msgid);
+int slapd_Client_auth(LDAP* ld);
+char* slapd_get_tmp_dir( void );
+
+/* Misc crrrrrrap */
+#include <stdio.h> /* GGOODREPL - For BUFSIZ, below, gak */
+const char* escape_string (const char* str, char buf[BUFSIZ]);
+const char* escape_string_with_punctuation(const char* str, char buf[BUFSIZ]);
+const char* escape_filter_value(const char* str, int len, char buf[BUFSIZ]);
+void charray_add( char ***a, char *s );
+void charray_free(char **array);
+int charray_remove(char **a,const char *s);
+int charray_inlist( char **a, char *s );
+
+char *slapi_berval_get_string_copy(const struct berval *bval);
+
+/* lenstr stuff */
+
+typedef struct _lenstr {
+ char *ls_buf;
+ size_t ls_len;
+ size_t ls_maxlen;
+} lenstr;
+#define LS_INCRSIZE 256
+
+void addlenstr( lenstr *l, const char *str );
+void lenstr_free( lenstr ** );
+lenstr *lenstr_new(void);
+
+/* event queue routines and data types */
+typedef void* Slapi_Eq_Context;
+typedef void (*slapi_eq_fn_t)(time_t when, void *arg);
+Slapi_Eq_Context slapi_eq_once(slapi_eq_fn_t fn, void *arg, time_t when);
+Slapi_Eq_Context slapi_eq_repeat(slapi_eq_fn_t fn, void *arg, time_t when, unsigned long interval);
+int slapi_eq_cancel(Slapi_Eq_Context ctx);
+void *slapi_eq_get_arg (Slapi_Eq_Context ctx);
+
+/* config DN */
+char *get_config_DN(void);
+
+/* Data Version */
+const char *get_server_dataversion( void );
+
+/* Configuration Parameters */
+int config_get_port( void );
+int config_get_secureport( void );
+
+/* Local host information */
+char* get_localhost_DN( void );
+char* get_localhost_DNS( void );
+
+int ref_array_replace(const char *dn, struct berval *referral, int write, int read);
+void ref_array_moddn(const char *dn, char *newrdn, Slapi_PBlock *pb);
+int ref_register_callback(int type, char *description,
+ void (*cb)(Slapi_PBlock *, void *), void *cbData);
+int ref_remove_callback(char *description);
+/* GGOODREPL get_data_source definition should move into repl DLL */
+struct berval **get_data_source(Slapi_PBlock *pb, const Slapi_DN *sdn, int orc, void *cf_refs);
+/* Ref_Array *send_read_referrals(Slapi_PBlock *pb, int scope, char *dn, struct berval ***urls); */
+
+/* JCMREPL - IFP and CFP should be defined centrally */
+#ifndef _IFP
+#define _IFP
+typedef int (*IFP)();
+#endif
+
+#ifndef _CFP
+#define _CFP
+typedef char*(*CFP)();
+#endif
+
+void bervalarray_add_berval_fast(struct berval ***vals, const struct berval *addval, int nvals, int *maxvals);
+
+int re_exec( char *lp );
+char *re_comp( char *pat );
+void re_lock( void );
+int re_unlock( void );
+
+
+/* this is the root configuration entry beneath which all plugin
+ configuration entries will be found */
+#define PLUGIN_BASE_DN "cn=plugins,cn=config"
+
+/***** End of items added for the replication plugin. ***********************/
+
+
+/******************************************************************************
+ * Online tasks interface (to support import, export, etc)
+ * After some cleanup, we could consider making these public.
+ */
+typedef struct _slapi_task Slapi_Task;
+typedef int (*TaskCallbackFn)(Slapi_Task *task);
+
+/* task states */
+#define SLAPI_TASK_SETUP 0
+#define SLAPI_TASK_RUNNING 1
+#define SLAPI_TASK_FINISHED 2
+#define SLAPI_TASK_CANCELLED 3
+
+/* task flags (set by the task-control code) */
+#define SLAPI_TASK_DESTROYING 0x01 /* queued event for destruction */
+
+struct _slapi_task {
+ struct _slapi_task *next;
+ char *task_dn;
+ int task_exitcode; /* for the end user */
+ int task_state; /* (see above) */
+ int task_progress; /* number between 0 and task_work */
+ int task_work; /* "units" of work to be done */
+ int task_flags; /* (see above) */
+
+ /* it is the task's responsibility to allocate this memory & free it: */
+ char *task_status; /* transient status info */
+ char *task_log; /* appended warnings, etc */
+
+ void *task_private; /* for use by backends */
+ TaskCallbackFn cancel; /* task has been cancelled by user */
+ TaskCallbackFn destructor; /* task entry is being destroyed */
+ int task_refcount;
+};
+
+int slapi_task_register_handler(const char *name, dseCallbackFn func);
+void slapi_task_status_changed(Slapi_Task *task);
+void slapi_task_log_status(Slapi_Task *task, char *format, ...);
+void slapi_task_log_notice(Slapi_Task *task, char *format, ...);
+
+/* End of interface to support online tasks **********************************/
+
+
+void DS_Sleep(PRIntervalTime ticks);
+
+#if defined(UPGRADEDB)
+/* macro to specify the behavior of upgradedb */
+#define SLAPI_UPGRADEDB_FORCE 0x1 /* reindex all (no check w/ idl switch) */
+#define SLAPI_UPGRADEDB_SKIPINIT 0x2 /* call upgradedb as part of other op */
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/ldap/servers/slapd/slapi2nspr.c b/ldap/servers/slapd/slapi2nspr.c
new file mode 100644
index 00000000..17f4896f
--- /dev/null
+++ b/ldap/servers/slapd/slapi2nspr.c
@@ -0,0 +1,274 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+/*
+ * slapi2nspr.c - expose a subset of the NSPR20/21 API to SLAPI plugin writers
+ *
+ */
+
+#include "slap.h"
+#include "snmp_collator.h"
+#include <ldap_ssl.h>
+#include <ldappr.h>
+#include <nspr.h>
+
+/*
+ * Note that Slapi_Mutex and Slapi_CondVar are defined like this in
+ * slapi-plugin.h:
+ *
+ * typedef struct slapi_mutex Slapi_Mutex;
+ * typedef struct slapi_condvar Slapi_CondVar;
+ *
+ * but there is no definition for struct slapi_mutex or struct slapi_condvar.
+ * This seems to work okay since we always use them in pointer form and cast
+ * directly to their NSPR equivalents. Clever, huh?
+ */
+
+
+/*
+ * ---------------- SLAPI API Functions --------------------------------------
+ */
+
+/*
+ * Function: slapi_new_mutex
+ * Description: behaves just like PR_NewLock().
+ * Returns: a pointer to the new mutex (NULL if a mutex can't be created).
+ */
+Slapi_Mutex *
+slapi_new_mutex( void )
+{
+ return( (Slapi_Mutex *)PR_NewLock());
+}
+
+
+/*
+ * Function: slapi_destroy_mutex
+ * Description: behaves just like PR_DestroyLock().
+ */
+void
+slapi_destroy_mutex( Slapi_Mutex *mutex )
+{
+ if ( mutex != NULL ) {
+ PR_DestroyLock( (PRLock *)mutex );
+ }
+}
+
+
+/*
+ * Function: slapi_lock_mutex
+ * Description: behaves just like PR_Lock().
+ */
+void
+slapi_lock_mutex( Slapi_Mutex *mutex )
+{
+ if ( mutex != NULL ) {
+ PR_Lock( (PRLock *)mutex );
+ }
+}
+
+
+/*
+ * Function: slapi_unlock_mutex
+ * Description: behaves just like PR_Unlock().
+ * Returns:
+ * non-zero if mutex was successfully unlocked.
+ * 0 if mutex is NULL or is not locked by the calling thread.
+ */
+int
+slapi_unlock_mutex( Slapi_Mutex *mutex )
+{
+ if ( mutex == NULL || PR_Unlock( (PRLock *)mutex ) == PR_FAILURE ) {
+ return( 0 );
+ } else {
+ return( 1 );
+ }
+}
+
+
+/*
+ * Function: slapi_new_condvar
+ * Description: behaves just like PR_NewCondVar().
+ * Returns: pointer to a new condition variable (NULL if one can't be created).
+ */
+Slapi_CondVar *
+slapi_new_condvar( Slapi_Mutex *mutex )
+{
+ if ( mutex == NULL ) {
+ return( NULL );
+ }
+
+ return( (Slapi_CondVar *)PR_NewCondVar( (PRLock *)mutex ));
+}
+
+
+/*
+ * Function: slapi_destroy_condvar
+ * Description: behaves just like PR_DestroyCondVar().
+ */
+void
+slapi_destroy_condvar( Slapi_CondVar *cvar )
+{
+ if ( cvar != NULL ) {
+ PR_DestroyCondVar( (PRCondVar *)cvar );
+ }
+}
+
+
+/*
+ * Function: slapi_wait_condvar
+ * Description: behaves just like PR_WaitCondVar() except timeout is
+ * in seconds and microseconds instead of PRIntervalTime units.
+ * If timeout is NULL, this call blocks indefinitely.
+ * Returns:
+ * non-zero is all goes well.
+ * 0 if cvar is NULL, the caller has not locked the mutex associated
+ * with cvar, or the waiting thread was interrupted.
+ */
+int
+slapi_wait_condvar( Slapi_CondVar *cvar, struct timeval *timeout )
+{
+ PRIntervalTime prit;
+
+ if ( cvar == NULL ) {
+ return( 0 );
+ }
+
+ if ( timeout == NULL ) {
+ prit = PR_INTERVAL_NO_TIMEOUT;
+ } else {
+ prit = PR_SecondsToInterval( timeout->tv_sec )
+ + PR_MicrosecondsToInterval( timeout->tv_usec );
+ }
+
+ if ( PR_WaitCondVar( (PRCondVar *)cvar, prit ) != PR_SUCCESS ) {
+ return( 0 );
+ }
+
+ return( 1 );
+}
+
+
+/*
+ * Function: slapi_notify_condvar
+ * Description: if notify_all is zero, behaves just like PR_NotifyCondVar().
+ * if notify_all is non-zero, behaves just like PR_NotifyAllCondVar().
+ * Returns:
+ * non-zero if all goes well.
+ * 0 if cvar is NULL or the caller has not locked the mutex associated
+ * with cvar.
+ */
+int
+slapi_notify_condvar( Slapi_CondVar *cvar, int notify_all )
+{
+ PRStatus prrc;
+
+ if ( cvar == NULL ) {
+ return( 0 );
+ }
+
+ if ( notify_all ) {
+ prrc = PR_NotifyAllCondVar( (PRCondVar *)cvar );
+ } else {
+ prrc = PR_NotifyCondVar( (PRCondVar *)cvar );
+ }
+
+ return( prrc == PR_SUCCESS ? 1 : 0 );
+}
+
+
+/*
+ * Function: slapi_ldap_init()
+ * Description: just like ldap_ssl_init() but also arranges for the LDAP
+ * session handle returned to be safely shareable by multiple threads
+ * if "shared" is non-zero.
+ * Returns:
+ * an LDAP session handle (NULL if some local error occurs).
+ */
+LDAP *
+slapi_ldap_init( char *ldaphost, int ldapport, int secure, int shared )
+{
+ LDAP *ld;
+ int io_timeout_ms;
+
+
+ if ( secure && slapd_SSL_client_init() != 0 ) {
+ return( NULL );
+ }
+
+ /*
+ * Leverage the libprldap layer to take care of all the NSPR integration.
+ * Note that ldapssl_init() uses libprldap implicitly.
+ */
+
+ if ( secure ) {
+ ld = ldapssl_init( ldaphost, ldapport, secure );
+ } else {
+ ld = prldap_init( ldaphost, ldapport, shared );
+ }
+
+ /* Update snmp interaction table */
+ if ( ld == NULL) {
+ set_snmp_interaction_row( ldaphost, ldapport, -1);
+ } else {
+ set_snmp_interaction_row( ldaphost, ldapport, 0);
+ }
+
+ if ( ld != NULL ) {
+ /*
+ * Set the outbound LDAP I/O timeout based on the server config.
+ */
+ io_timeout_ms = config_get_outbound_ldap_io_timeout();
+ if ( io_timeout_ms > 0 ) {
+ if ( prldap_set_session_option( ld, NULL, PRLDAP_OPT_IO_MAX_TIMEOUT,
+ io_timeout_ms ) != LDAP_SUCCESS ) {
+ slapi_log_error( SLAPI_LOG_FATAL, "slapi_ldap_init",
+ "failed: unable to set outbound I/O timeout to %dms\n",
+ io_timeout_ms );
+ slapi_ldap_unbind( ld );
+ return( NULL );
+ }
+ }
+
+ /*
+ * Set SSL strength (server certificate validity checking).
+ */
+ if ( secure ) {
+ int ssl_strength;
+
+ if ( config_get_ssl_check_hostname()) {
+ /* check hostname against name in certificate */
+ ssl_strength = LDAPSSL_AUTH_CNCHECK;
+ } else {
+ /* verify certificate only */
+ ssl_strength = LDAPSSL_AUTH_CERT;
+ }
+
+ if ( ldapssl_set_strength( ld, ssl_strength ) != 0 ) {
+ int prerr = PR_GetError();
+
+ slapi_log_error( SLAPI_LOG_FATAL, "slapi_ldap_init",
+ "failed: unable to set SSL strength to %d ("
+ SLAPI_COMPONENT_NAME_NSPR " error %d - %s)",
+ ssl_strength, prerr, slapd_pr_strerror( prerr ));
+
+ }
+ }
+ }
+
+ return( ld );
+}
+
+
+/*
+ * Function: slapi_ldap_unbind()
+ * Purpose: release an LDAP session obtained from a call to slapi_ldap_init().
+ */
+void
+slapi_ldap_unbind( LDAP *ld )
+{
+ if ( ld != NULL ) {
+ ldap_unbind( ld );
+ }
+}
diff --git a/ldap/servers/slapd/snmp_collator.c b/ldap/servers/slapd/snmp_collator.c
new file mode 100644
index 00000000..320c7fe9
--- /dev/null
+++ b/ldap/servers/slapd/snmp_collator.c
@@ -0,0 +1,617 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#ifndef _WIN32
+#include <sys/ipc.h>
+#include <sys/msg.h>
+#include <dirent.h>
+#endif
+#include <time.h>
+#include <signal.h>
+#include <string.h>
+#include <errno.h>
+
+#include "agtmmap.h"
+#include "slap.h"
+#include "prthread.h"
+#include "prlock.h"
+#include "prerror.h"
+#include "prcvar.h"
+
+#include "snmp_collator.h"
+#include "../snmp/ntagt/nslagtcom_nt.h"
+
+/* stevross: safe to assume port should be at most 5 digits ? */
+#define PORT_LEN 5
+/* strlen of url portions ie "ldap://:/" */
+#define URL_CHARS_LEN 9
+
+char *make_ds_url(char *host, int port);
+void print_snmp_interaction_table();
+int search_interaction_table(char *dsURL, int *isnew);
+
+/* snmp stats stuff */
+struct agt_stats_t *stats=NULL;
+
+/* mmap stuff */
+static int hdl;
+
+/* collator stuff */
+static char *tmpstatsfile = AGT_STATS_FILE;
+#ifdef _WIN32
+static TCHAR szStatsFile[_MAX_PATH];
+static TCHAR szTempDir[_MAX_PATH];
+static HANDLE hParentProcess = NULL;
+static HANDLE hStatSlot = NULL;
+static HANDLE hLogFile = INVALID_HANDLE_VALUE;
+static TCHAR szSpoolRootDir[_MAX_PATH];
+#else
+static char szStatsFile[_MAX_PATH];
+#endif /* _WIN32*/
+static Slapi_Eq_Context snmp_eq_ctx;
+static int snmp_collator_stopped = 0;
+
+/* lock stuff */
+static PRLock *interaction_table_mutex;
+
+
+/***********************************************************************************
+*
+* int snmp_collator_init()
+*
+* initializes the global variables used by snmp
+*
+************************************************************************************/
+
+int snmp_collator_init(){
+ int i;
+
+ /*
+ * Initialize the mmap structure
+ */
+ memset((void *) stats, 0, sizeof(*stats));
+ stats->hdr_stats.hdrVersionMjr = AGT_MJR_VERSION;
+ stats->hdr_stats.hdrVersionMnr = AGT_MNR_VERSION;
+ stats->hdr_stats.restarted = 0;
+ stats->hdr_stats.startTime = time(0); /* This is a bit off, hope it's ok */
+
+ /* point these at the mmaped data */
+ g_get_global_snmp_vars()->ops_tbl.dsAnonymousBinds = &(stats->ops_stats.dsAnonymousBinds);
+ g_get_global_snmp_vars()->ops_tbl.dsUnAuthBinds = &(stats->ops_stats.dsUnAuthBinds);
+ g_get_global_snmp_vars()->ops_tbl.dsSimpleAuthBinds = &(stats->ops_stats.dsSimpleAuthBinds);
+ g_get_global_snmp_vars()->ops_tbl.dsStrongAuthBinds = &(stats->ops_stats.dsStrongAuthBinds);
+ g_get_global_snmp_vars()->ops_tbl.dsBindSecurityErrors = &(stats->ops_stats.dsBindSecurityErrors);
+ g_get_global_snmp_vars()->ops_tbl.dsInOps = &(stats->ops_stats.dsInOps);
+ g_get_global_snmp_vars()->ops_tbl.dsReadOps = &(stats->ops_stats.dsReadOps);
+ g_get_global_snmp_vars()->ops_tbl.dsCompareOps = &(stats->ops_stats.dsCompareOps);
+ g_get_global_snmp_vars()->ops_tbl.dsAddEntryOps = &(stats->ops_stats.dsAddEntryOps);
+ g_get_global_snmp_vars()->ops_tbl.dsRemoveEntryOps = &(stats->ops_stats.dsRemoveEntryOps);
+ g_get_global_snmp_vars()->ops_tbl.dsModifyEntryOps = &(stats->ops_stats.dsModifyEntryOps);
+ g_get_global_snmp_vars()->ops_tbl.dsModifyRDNOps = &(stats->ops_stats.dsModifyRDNOps);
+ g_get_global_snmp_vars()->ops_tbl.dsListOps = &(stats->ops_stats.dsListOps);
+ g_get_global_snmp_vars()->ops_tbl.dsSearchOps = &(stats->ops_stats.dsSearchOps);
+ g_get_global_snmp_vars()->ops_tbl.dsOneLevelSearchOps = &(stats->ops_stats.dsOneLevelSearchOps);
+ g_get_global_snmp_vars()->ops_tbl.dsWholeSubtreeSearchOps = &(stats->ops_stats.dsWholeSubtreeSearchOps);
+ g_get_global_snmp_vars()->ops_tbl.dsReferrals = &(stats->ops_stats.dsReferrals);
+ g_get_global_snmp_vars()->ops_tbl.dsChainings = &(stats->ops_stats.dsChainings);
+ g_get_global_snmp_vars()->ops_tbl.dsSecurityErrors = &(stats->ops_stats.dsSecurityErrors);
+ g_get_global_snmp_vars()->ops_tbl.dsErrors = &(stats->ops_stats.dsErrors);
+ g_get_global_snmp_vars()->ops_tbl.dsConnections = &(stats->ops_stats.dsConnections);
+ g_get_global_snmp_vars()->ops_tbl.dsConnectionSeq = &(stats->ops_stats.dsConnectionSeq);
+ g_get_global_snmp_vars()->ops_tbl.dsBytesRecv = &(stats->ops_stats.dsBytesRecv);
+ g_get_global_snmp_vars()->ops_tbl.dsBytesSent = &(stats->ops_stats.dsBytesSent);
+ g_get_global_snmp_vars()->ops_tbl.dsEntriesReturned = &(stats->ops_stats.dsEntriesReturned);
+ g_get_global_snmp_vars()->ops_tbl.dsReferralsReturned = &(stats->ops_stats.dsReferralsReturned);
+
+ /* entries table */
+
+ g_get_global_snmp_vars()->entries_tbl.dsMasterEntries = &(stats->entries_stats.dsMasterEntries);
+ g_get_global_snmp_vars()->entries_tbl.dsCopyEntries = &(stats->entries_stats.dsCopyEntries);
+ g_get_global_snmp_vars()->entries_tbl.dsCacheEntries = &(stats->entries_stats.dsCacheEntries);
+ g_get_global_snmp_vars()->entries_tbl.dsCacheHits = &(stats->entries_stats.dsCacheHits);
+ g_get_global_snmp_vars()->entries_tbl.dsSlaveHits = &(stats->entries_stats.dsSlaveHits);
+
+ /* interaction table */
+
+ /* set pointers to table */
+ for(i=0; i < NUM_SNMP_INT_TBL_ROWS; i++)
+ {
+ stats->int_stats[i].dsIntIndex=i;
+ g_get_global_snmp_vars()->int_tbl[i].dsIntIndex = &(stats->int_stats[i].dsIntIndex);
+ g_get_global_snmp_vars()->int_tbl[i].dsName = stats->int_stats[i].dsName;
+ g_get_global_snmp_vars()->int_tbl[i].dsTimeOfCreation = &(stats->int_stats[i].dsTimeOfCreation);
+ g_get_global_snmp_vars()->int_tbl[i].dsTimeOfLastAttempt = &(stats->int_stats[i].dsTimeOfLastAttempt);
+ g_get_global_snmp_vars()->int_tbl[i].dsTimeOfLastSuccess = &(stats->int_stats[i].dsTimeOfLastSuccess);
+ g_get_global_snmp_vars()->int_tbl[i].dsFailuresSinceLastSuccess
+ = &(stats->int_stats[i].dsFailuresSinceLastSuccess);
+ g_get_global_snmp_vars()->int_tbl[i].dsFailures = &(stats->int_stats[i].dsFailures);
+ g_get_global_snmp_vars()->int_tbl[i].dsSuccesses = &(stats->int_stats[i].dsSuccesses);
+ g_get_global_snmp_vars()->int_tbl[i].dsURL = stats->int_stats[i].dsURL;
+ }
+
+ /* initialize table contents */
+ for(i=0; i < NUM_SNMP_INT_TBL_ROWS; i++)
+ {
+ *(g_get_global_snmp_vars()->int_tbl[i].dsIntIndex) = i + 1;
+ strcpy(g_get_global_snmp_vars()->int_tbl[i].dsName, "Not Available");
+ *(g_get_global_snmp_vars()->int_tbl[i].dsTimeOfCreation) = 0;
+ *(g_get_global_snmp_vars()->int_tbl[i].dsTimeOfLastAttempt) = 0;
+ *(g_get_global_snmp_vars()->int_tbl[i].dsTimeOfLastSuccess) = 0;
+ *(g_get_global_snmp_vars()->int_tbl[i].dsFailuresSinceLastSuccess) = 0;
+ *(g_get_global_snmp_vars()->int_tbl[i].dsFailures) = 0;
+ *(g_get_global_snmp_vars()->int_tbl[i].dsSuccesses) = 0;
+ strcpy(g_get_global_snmp_vars()->int_tbl[i].dsURL, "Not Available");
+ }
+
+ /* create lock for interaction table */
+ interaction_table_mutex = PR_NewLock();
+
+ return 0;
+}
+
+
+
+/***********************************************************************************
+ * given the name, wether or not it was successfull and the URL updates snmp
+ * interaction table appropriately
+ *
+ *
+************************************************************************************/
+
+void set_snmp_interaction_row(char *host, int port, int error)
+{
+ int index;
+ int isnew;
+ char *dsName;
+ char *dsURL;
+
+ /* stevross: our servers don't have a concept of dsName as a distinguished name
+ as specified in the MIB. Make this "Not Available" for now waiting for
+ sometime in the future when we do
+ */
+
+
+ dsName = "Not Available";
+
+ dsURL= make_ds_url(host, port);
+
+ /* lock around here to avoid race condition of two threads trying to update table at same time */
+ PR_Lock(interaction_table_mutex);
+ index = search_interaction_table(dsURL, &isnew);
+
+ if(isnew){
+ /* fillin the new row from scratch*/
+ *(g_get_global_snmp_vars()->int_tbl[index].dsIntIndex) = index;
+ strcpy(g_get_global_snmp_vars()->int_tbl[index].dsName, dsName);
+ *(g_get_global_snmp_vars()->int_tbl[index].dsTimeOfCreation) = time(0);
+ *(g_get_global_snmp_vars()->int_tbl[index].dsTimeOfLastAttempt) = time(0);
+ if(error == 0){
+ *(g_get_global_snmp_vars()->int_tbl[index].dsTimeOfLastSuccess) = time(0);
+ *(g_get_global_snmp_vars()->int_tbl[index].dsFailuresSinceLastSuccess) = 0;
+ *(g_get_global_snmp_vars()->int_tbl[index].dsFailures) = 0;
+ *(g_get_global_snmp_vars()->int_tbl[index].dsSuccesses) = 1;
+ }else{
+ *(g_get_global_snmp_vars()->int_tbl[index].dsTimeOfLastSuccess) = 0;
+ *(g_get_global_snmp_vars()->int_tbl[index].dsFailuresSinceLastSuccess) = 1;
+ *(g_get_global_snmp_vars()->int_tbl[index].dsFailures) = 1;
+ *(g_get_global_snmp_vars()->int_tbl[index].dsSuccesses) = 0;
+ }
+ strcpy(g_get_global_snmp_vars()->int_tbl[index].dsURL, dsURL);
+ }else{
+ /* just update the appropriate fields */
+ *(g_get_global_snmp_vars()->int_tbl[index].dsTimeOfLastAttempt) = time(0);
+ if(error == 0){
+ *(g_get_global_snmp_vars()->int_tbl[index].dsTimeOfLastSuccess) = time(0);
+ *(g_get_global_snmp_vars()->int_tbl[index].dsFailuresSinceLastSuccess) = 0;
+ *(g_get_global_snmp_vars()->int_tbl[index].dsSuccesses) += 1;
+ }else{
+ *(g_get_global_snmp_vars()->int_tbl[index].dsFailuresSinceLastSuccess) +=1;
+ *(g_get_global_snmp_vars()->int_tbl[index].dsFailures) +=1;
+ }
+
+ }
+ PR_Unlock(interaction_table_mutex);
+ /* free the memory allocated for dsURL in call to ds_make_url */
+ if(dsURL != NULL){
+ slapi_ch_free( (void**)&dsURL );
+ }
+}
+
+/***********************************************************************************
+ * Given: host and port
+ * Returns: ldapUrl in form of
+ * ldap://host.mcom.com:port/
+ *
+ * this should point to root DSE
+************************************************************************************/
+char *make_ds_url(char *host, int port){
+
+ char *url;
+
+ url = (char *)slapi_ch_malloc( (strlen(host) + PORT_LEN + URL_CHARS_LEN + 1) * sizeof(char));
+
+ sprintf(url,"ldap://%s:%d/",host, port);
+
+ return url;
+}
+
+
+/***********************************************************************************
+ * searches the table for the url specified
+ * If there, returns index to update stats
+ * if, not there returns index of oldest interaction, and isnew flag is set
+ * so caller can rewrite this row
+************************************************************************************/
+
+int search_interaction_table(char *dsURL, int *isnew)
+{
+ int i;
+ int index = 0;
+ time_t oldestattempt;
+ time_t currentattempt;
+
+ oldestattempt = *(g_get_global_snmp_vars()->int_tbl[0].dsTimeOfLastAttempt);
+ *isnew = 1;
+
+ for(i=0; i < NUM_SNMP_INT_TBL_ROWS; i++){
+ if(!strcmp(g_get_global_snmp_vars()->int_tbl[i].dsURL, "Not Available"))
+ {
+ /* found it -- this is new, first time for this row */
+ index = i;
+ break;
+ }else if(!strcmp(g_get_global_snmp_vars()->int_tbl[i].dsURL, dsURL)){
+ /* found it -- it was already there*/
+ *isnew = 0;
+ index = i;
+ break;
+ }else{
+ /* not found so figure out oldest row */
+ currentattempt = *(g_get_global_snmp_vars()->int_tbl[i].dsTimeOfLastAttempt);
+
+ if(currentattempt <= oldestattempt){
+ index=i;
+ oldestattempt = currentattempt;
+ }
+ }
+
+ }
+
+ return index;
+
+}
+/* for debuging until subagent part working, print contents of interaction table */
+void print_snmp_interaction_table()
+{
+ int i;
+ for(i=0; i < NUM_SNMP_INT_TBL_ROWS; i++)
+ {
+ fprintf(stderr, " dsIntIndex: %d \n", *(g_get_global_snmp_vars()->int_tbl[i].dsIntIndex));
+ fprintf(stderr, " dsName: %s \n", g_get_global_snmp_vars()->int_tbl[i].dsName);
+ fprintf(stderr, " dsTimeOfCreation: %ld \n", *(g_get_global_snmp_vars()->int_tbl[i].dsTimeOfCreation));
+ fprintf(stderr, " dsTimeOfLastAttempt: %ld \n", *(g_get_global_snmp_vars()->int_tbl[i].dsTimeOfLastAttempt));
+ fprintf(stderr, " dsTimeOfLastSuccess: %ld \n", *(g_get_global_snmp_vars()->int_tbl[i].dsTimeOfLastSuccess));
+ fprintf(stderr, "dsFailuresSinceLastSuccess: %d \n", *(g_get_global_snmp_vars()->int_tbl[i].dsFailuresSinceLastSuccess));
+ fprintf(stderr, " dsFailures: %d \n", *(g_get_global_snmp_vars()->int_tbl[i].dsFailures));
+ fprintf(stderr, " dsSuccesses: %d \n", *(g_get_global_snmp_vars()->int_tbl[i].dsSuccesses));
+ fprintf(stderr, " dsURL: %s \n", g_get_global_snmp_vars()->int_tbl[i].dsURL);
+ fprintf(stderr, "\n");
+ }
+}
+
+/*-------------------------------------------------------------------------
+ *
+ * sc_setevent: Sets the specified event (NT only). The input event has
+ * to be created by the subagent during its initialization.
+ *
+ * Returns: None
+ *
+ *-----------------------------------------------------------------------*/
+
+#ifdef _WIN32
+void sc_setevent(char *ev)
+{
+ HANDLE hTrapEvent;
+ DWORD err = NO_ERROR;
+
+ /*
+ * Set the event handle to force NT SNMP service to call the subagent
+ * DLL to generate a trap. Any error will be ignored as the subagent
+ * may not have been loaded.
+ */
+ if ((hTrapEvent = OpenEvent(EVENT_MODIFY_STATE, FALSE,
+ (LPCTSTR) ev)) != NULL)
+ {
+ if (SetEvent(hTrapEvent) == FALSE)
+ err = GetLastError();
+ }
+ else
+ err = GetLastError();
+
+ if (err != NO_ERROR)
+ {
+ fprintf(stderr, "Failed to set trap (error = %d).\n", err);
+ }
+}
+#endif
+
+
+
+/***********************************************************************************
+*
+* int snmp_collator_start()
+*
+* open the memory map and initialize the variables
+* initializes the global variables used by snmp
+*
+* starts the collator thread
+************************************************************************************/
+
+int snmp_collator_start()
+{
+
+ int err;
+ char *instancedir = config_get_instancedir();
+
+ /*
+ * Get directory for our stats file
+ */
+
+ sprintf(szStatsFile, "%s/logs/%s", instancedir,
+ AGT_STATS_FILE);
+ tmpstatsfile = szStatsFile;
+
+ slapi_ch_free((void **) &instancedir);
+
+
+ /* open the memory map */
+
+ if ((err = agt_mopen_stats(tmpstatsfile, O_RDWR, &hdl) != 0))
+ {
+ if (err != EEXIST) /* Ignore if file already exists */
+ {
+ printf("Failed to open stats file (%s) (error %d).\n",
+ AGT_STATS_FILE, err);
+
+ exit(1);
+ }
+ }
+
+
+
+/* point stats struct at mmap data */
+ stats = (struct agt_stats_t *) mmap_tbl [hdl].fp;
+
+/* initialize stats data */
+
+ snmp_collator_init();
+/*
+* now that memmap is open and things point the right way
+* an atomic set or increment anywhere in slapd should set
+* the snmp memmap vars correctly and be able to be polled by snmp
+*/
+
+ /* Arrange to be called back periodically */
+ snmp_eq_ctx = slapi_eq_repeat(snmp_collator_update, NULL, (time_t)0,
+ SLAPD_SNMP_UPDATE_INTERVAL);
+
+
+return 0;
+
+}
+
+
+/***********************************************************************************
+*
+* int snmp_collator_stop()
+*
+* stops the collator thread
+* closes the memory map
+* cleans up any needed memory
+*
+************************************************************************************/
+
+int snmp_collator_stop()
+{
+ int err;
+
+ /* Abort any pending events */
+ slapi_eq_cancel(snmp_eq_ctx);
+ snmp_collator_stopped = 1;
+
+ /* close the memory map */
+ if ((err = agt_mclose_stats(hdl)) != 0)
+ {
+ fprintf(stderr, "Failed to close stats file (%s) (error = %d).",
+ AGT_STATS_FILE, err);
+ }
+
+ if (remove(tmpstatsfile) != 0)
+ {
+ fprintf(stderr, "Failed to remove (%s) (error = %d).\n",
+ tmpstatsfile, errno);
+ }
+
+ /* delete lock */
+ PR_DestroyLock(interaction_table_mutex);
+
+#ifdef _WIN32
+ /* send the event so server down trap gets set on NT */
+ sc_setevent(MAGT_NSEV_SNMPTRAP);
+
+#endif
+ /* stevross: I probably need to free stats too... make sure to add that later */
+
+return 0;
+}
+
+
+
+/***********************************************************************************
+*
+* int snmp_collator_update()
+*
+* our architecture changed from mail server and we right to mmapped
+* area as soon as operation completed, rather than maintining the same data twice
+* and doing a polled update. However, to keep traps working correctly (as they depend)
+* on the time in the header, it is more efficient to write the header info
+* in a polled fashion (ever 1 sec)
+*
+************************************************************************************/
+
+void
+snmp_collator_update(time_t start_time, void *arg)
+{
+ Slapi_Backend *be, *be_next;
+ char *cookie = NULL;
+ Slapi_PBlock *search_result_pb = NULL;
+ Slapi_Entry **search_entries;
+ Slapi_Attr *attr = NULL;
+ Slapi_Value *sval = NULL;
+ int search_result;
+
+ if (snmp_collator_stopped) {
+ return;
+ }
+
+ /* just update the update time in the header */
+ if( stats != NULL){
+ stats->hdr_stats.updateTime = time(0);
+ }
+
+ /* set the cache hits/cache entries info */
+ be = slapi_get_first_backend(&cookie);
+ if (!be)
+ return;
+
+ be_next = slapi_get_next_backend(cookie);
+
+ slapi_ch_free ((void **) &cookie);
+
+ /* for now, only do it if there is only 1 backend, otherwise don't know
+ which backend to pick */
+ if(be_next == NULL)
+ {
+ Slapi_DN monitordn;
+ slapi_sdn_init(&monitordn);
+ be_getmonitordn(be,&monitordn);
+
+ /* do a search on the monitor dn to get info */
+ search_result_pb = slapi_search_internal( slapi_sdn_get_dn(&monitordn),
+ LDAP_SCOPE_BASE,
+ "objectclass=*",
+ NULL,
+ NULL,
+ 0);
+ slapi_sdn_done(&monitordn);
+
+ slapi_pblock_get( search_result_pb, SLAPI_PLUGIN_INTOP_RESULT, &search_result);
+
+ if(search_result == 0)
+ {
+ const struct berval *val = NULL;
+ /* get the entrycachehits */
+ slapi_pblock_get( search_result_pb,SLAPI_PLUGIN_INTOP_SEARCH_ENTRIES,
+ &search_entries);
+ if(slapi_entry_attr_find( search_entries[0], "entrycachehits", &attr) == 0 )
+ {
+ /* get the values out of the attribute */
+ val = NULL;
+ slapi_attr_first_value( attr, &sval );
+ if(NULL != sval)
+ {
+ val= slapi_value_get_berval( sval );
+ }
+ }
+
+ /* if we got a value for entrycachehits, then set it */
+ if(val != NULL)
+ {
+ PR_AtomicSet(g_get_global_snmp_vars()->entries_tbl.dsCacheHits, atoi(val->bv_val));
+
+ }
+
+ /* get the currententrycachesize */
+ attr = NULL;
+ val = NULL;
+ sval = NULL;
+ if(slapi_entry_attr_find( search_entries[0], "currententrycachesize", &attr) == 0 )
+ {
+ /* get the values out of the attribute */
+ slapi_attr_first_value( attr,&sval );
+ if(NULL != sval) {
+ val= slapi_value_get_berval( sval );
+ }
+ }
+
+ /* if we got a value for currententrycachesize, then set it */
+ if(val != NULL)
+ {
+ PR_AtomicSet(g_get_global_snmp_vars()->entries_tbl.dsCacheEntries, atoi(val->bv_val));
+
+ }
+
+ }
+
+ slapi_free_search_results_internal(search_result_pb);
+ slapi_pblock_destroy(search_result_pb);
+ }
+}
+
+static void
+add_counter_to_value(Slapi_Entry *e, const char *type, int countervalue)
+{
+ char value[40];
+ sprintf(value,"%d",countervalue);
+ slapi_entry_attr_set_charptr( e, type, value);
+}
+
+void
+snmp_as_entry(Slapi_Entry *e)
+{
+ add_counter_to_value(e,"AnonymousBinds",stats->ops_stats.dsAnonymousBinds);
+ add_counter_to_value(e,"UnAuthBinds",stats->ops_stats.dsUnAuthBinds);
+ add_counter_to_value(e,"SimpleAuthBinds",stats->ops_stats.dsSimpleAuthBinds);
+ add_counter_to_value(e,"StrongAuthBinds",stats->ops_stats.dsStrongAuthBinds);
+ add_counter_to_value(e,"BindSecurityErrors",stats->ops_stats.dsBindSecurityErrors);
+ add_counter_to_value(e,"InOps",stats->ops_stats.dsInOps);
+ add_counter_to_value(e,"ReadOps",stats->ops_stats.dsReadOps);
+ add_counter_to_value(e,"CompareOps",stats->ops_stats.dsCompareOps);
+ add_counter_to_value(e,"AddEntryOps",stats->ops_stats.dsAddEntryOps);
+ add_counter_to_value(e,"RemoveEntryOps",stats->ops_stats.dsRemoveEntryOps);
+ add_counter_to_value(e,"ModifyEntryOps",stats->ops_stats.dsModifyEntryOps);
+ add_counter_to_value(e,"ModifyRDNOps",stats->ops_stats.dsModifyRDNOps);
+ add_counter_to_value(e,"ListOps",stats->ops_stats.dsListOps);
+ add_counter_to_value(e,"SearchOps",stats->ops_stats.dsSearchOps);
+ add_counter_to_value(e,"OneLevelSearchOps",stats->ops_stats.dsOneLevelSearchOps);
+ add_counter_to_value(e,"WholeSubtreeSearchOps",stats->ops_stats.dsWholeSubtreeSearchOps);
+ add_counter_to_value(e,"Referrals",stats->ops_stats.dsReferrals);
+ add_counter_to_value(e,"Chainings",stats->ops_stats.dsChainings);
+ add_counter_to_value(e,"SecurityErrors",stats->ops_stats.dsSecurityErrors);
+ add_counter_to_value(e,"Errors",stats->ops_stats.dsErrors);
+ add_counter_to_value(e,"Connections",stats->ops_stats.dsConnections);
+ add_counter_to_value(e,"ConnectionSeq",stats->ops_stats.dsConnectionSeq);
+ add_counter_to_value(e,"BytesRecv",stats->ops_stats.dsBytesRecv);
+ add_counter_to_value(e,"BytesSent",stats->ops_stats.dsBytesSent);
+ add_counter_to_value(e,"EntriesReturned",stats->ops_stats.dsEntriesReturned);
+ add_counter_to_value(e,"ReferralsReturned",stats->ops_stats.dsReferralsReturned);
+ add_counter_to_value(e,"MasterEntries",stats->entries_stats.dsMasterEntries);
+ add_counter_to_value(e,"CopyEntries",stats->entries_stats.dsCopyEntries);
+ add_counter_to_value(e,"CacheEntries",stats->entries_stats.dsCacheEntries);
+ add_counter_to_value(e,"CacheHits",stats->entries_stats.dsCacheHits);
+ add_counter_to_value(e,"SlaveHits",stats->entries_stats.dsSlaveHits);
+}
+
+
+
+
+
+
+
+
+
diff --git a/ldap/servers/slapd/snmp_collator.h b/ldap/servers/slapd/snmp_collator.h
new file mode 100644
index 00000000..d6eec840
--- /dev/null
+++ b/ldap/servers/slapd/snmp_collator.h
@@ -0,0 +1,15 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+/******************************************************************************
+*
+* function prototypes
+*
+******************************************************************************/
+
+ int snmp_collator_start();
+ int snmp_collator_stop();
+ void set_snmp_interaction_row(char *host, int port, int error);
+ void snmp_collator_update(time_t, void *);
diff --git a/ldap/servers/slapd/snoop.c b/ldap/servers/slapd/snoop.c
new file mode 100644
index 00000000..d6841aa2
--- /dev/null
+++ b/ldap/servers/slapd/snoop.c
@@ -0,0 +1,39 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+
+/* Operation Snooping Function.
+ Used by server internal code (and plugins if they fancy)
+ to detect state changes in the server.
+ Works by snooping the operation stream (as a postop plugin)
+ and calling back all the affected registered parties.
+*/
+
+/* Insert code here ... */
+
+
+int statechange_register(callback *func, char *dns)
+{
+ int ret = -1;
+
+ /* create register cache */
+
+ return ret;
+}
+
+int statechange_unregister(callback *func, char *dns)
+{
+ int ret = -1;
+
+ return ret;
+}
+
+
+int postop()
+{
+ /* state change, evaluate who it effects and notify */
+
+}
+
diff --git a/ldap/servers/slapd/ssl.c b/ldap/servers/slapd/ssl.c
new file mode 100644
index 00000000..49eea76a
--- /dev/null
+++ b/ldap/servers/slapd/ssl.c
@@ -0,0 +1,1499 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+/* SSL-related stuff for slapd */
+
+#if defined(NET_SSL)
+
+#if defined( _WINDOWS )
+#include <windows.h>
+#include <winsock.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include "proto-ntutil.h"
+#include <string.h>
+#include <stdlib.h>
+#include <direct.h>
+#include <io.h>
+#endif
+
+#ifdef LINUX
+#include <sys/param.h>
+#endif
+
+#include <ssl.h>
+#include <nss.h>
+#include <key.h>
+#include <sslproto.h>
+#include "secmod.h"
+#include <string.h>
+#include <errno.h>
+
+#include "slap.h"
+
+#include "svrcore.h"
+#include "fe.h"
+#include <ldap_ssl.h> /* ldapssl_client_init */
+#include "certdb.h"
+
+/* For IRIX... */
+#ifndef MAXPATHLEN
+#define MAXPATHLEN 1024
+#endif
+
+extern char* slapd_SSL3ciphers;
+extern symbol_t supported_ciphers[];
+
+/* dongle_file_name is set in slapd_nss_init when we set the path for the
+ key, cert, and secmod files - the dongle file must be in the same directory
+ and use the same naming scheme
+*/
+static char* dongle_file_name = NULL;
+
+static int _security_library_initialized = 0;
+static int _ssl_listener_initialized = 0;
+
+/* Our name for the internal token, must match PKCS-11 config data below */
+static char *internalTokenName = "Internal (Software) Token";
+
+static int stimeout;
+static char *ciphers = NULL;
+static char * configDN = "cn=encryption,cn=config";
+
+/* Copied from libadmin/libadmin.h public/nsapi.h */
+#define SERVER_KEY_NAME "Server-Key"
+#define MAGNUS_ERROR_LEN 1024
+#define LOG_WARN 0
+#define LOG_FAILURE 3
+#define FILE_PATHSEP '/'
+
+/* ----------------------- Multiple cipher support ------------------------ */
+
+
+#ifdef NET_SSL
+
+static char **cipher_names = NULL;
+typedef struct {
+ char *version;
+ char *name;
+ int num;
+} cipherstruct;
+
+
+static cipherstruct _conf_ciphers[] = {
+ {"SSL3","rc4", SSL_EN_RC4_128_WITH_MD5},
+ {"SSL3","rc4export", SSL_EN_RC4_128_EXPORT40_WITH_MD5},
+ {"SSL3","rc2", SSL_EN_RC2_128_CBC_WITH_MD5},
+ {"SSL3","rc2export", SSL_EN_RC2_128_CBC_EXPORT40_WITH_MD5},
+ /*{"idea", SSL_EN_IDEA_128_CBC_WITH_MD5}, */
+ {"SSL3","des", SSL_EN_DES_64_CBC_WITH_MD5},
+ {"SSL3","desede3", SSL_EN_DES_192_EDE3_CBC_WITH_MD5},
+ {"SSL3","rsa_rc4_128_md5", SSL_RSA_WITH_RC4_128_MD5},
+ {"SSL3","rsa_3des_sha", SSL_RSA_WITH_3DES_EDE_CBC_SHA},
+ {"SSL3","rsa_des_sha", SSL_RSA_WITH_DES_CBC_SHA},
+ {"SSL3","rsa_fips_3des_sha", SSL_RSA_FIPS_WITH_3DES_EDE_CBC_SHA},
+ {"SSL3","rsa_fips_des_sha", SSL_RSA_FIPS_WITH_DES_CBC_SHA},
+ {"SSL3","rsa_rc4_40_md5", SSL_RSA_EXPORT_WITH_RC4_40_MD5},
+ {"SSL3","rsa_rc2_40_md5", SSL_RSA_EXPORT_WITH_RC2_CBC_40_MD5},
+ {"SSL3","rsa_null_md5", SSL_RSA_WITH_NULL_MD5},
+ {"TLS","tls_rsa_export1024_with_rc4_56_sha", TLS_RSA_EXPORT1024_WITH_RC4_56_SHA},
+ {"TLS","tls_rsa_export1024_with_des_cbc_sha", TLS_RSA_EXPORT1024_WITH_DES_CBC_SHA},
+ {"SSL3","fortezza", SSL_FORTEZZA_DMS_WITH_FORTEZZA_CBC_SHA},
+ {"SSL3","fortezza_rc4_128_sha", SSL_FORTEZZA_DMS_WITH_RC4_128_SHA},
+ {"SSL3","fortezza_null", SSL_FORTEZZA_DMS_WITH_NULL_SHA},
+
+
+ /*{"SSL3","dhe_dss_40_sha", SSL_DHE_DSS_EXPORT_WITH_DES40_CBC_SHA}, */
+ {"SSL3","dhe_dss_des_sha", SSL_DHE_DSS_WITH_DES_CBC_SHA},
+ {"SSL3","dhe_dss_3des_sha", SSL_DHE_DSS_WITH_3DES_EDE_CBC_SHA},
+ /*{"SSL3","dhe_rsa_40_sha", SSL_DHE_RSA_EXPORT_WITH_DES40_CBC_SHA}, */
+ {"SSL3","dhe_rsa_des_sha", SSL_DHE_RSA_WITH_DES_CBC_SHA},
+ {"SSL3","dhe_rsa_3des_sha", SSL_DHE_RSA_WITH_3DES_EDE_CBC_SHA},
+
+ {"TLS","tls_rsa_aes_128_sha", TLS_RSA_WITH_AES_128_CBC_SHA},
+ {"TLS","tls_dhe_dss_aes_128_sha", TLS_DHE_DSS_WITH_AES_128_CBC_SHA},
+ {"TLS","tls_dhe_rsa_aes_128_sha", TLS_DHE_RSA_WITH_AES_128_CBC_SHA},
+
+ {"TLS","tls_rsa_aes_256_sha", TLS_RSA_WITH_AES_256_CBC_SHA},
+ {"TLS","tls_dhe_dss_aes_256_sha", TLS_DHE_DSS_WITH_AES_256_CBC_SHA},
+ {"TLS","tls_dhe_rsa_aes_256_sha", TLS_DHE_RSA_WITH_AES_256_CBC_SHA},
+ /*{"TLS","tls_dhe_dss_1024_des_sha", TLS_DHE_DSS_EXPORT1024_WITH_DES_CBC_SHA}, */
+ {"TLS","tls_dhe_dss_1024_rc4_sha", TLS_RSA_EXPORT1024_WITH_RC4_56_SHA},
+ {"TLS","tls_dhe_dss_rc4_128_sha", TLS_DHE_DSS_WITH_RC4_128_SHA},
+ {NULL, NULL, 0}
+};
+
+char ** getSupportedCiphers()
+{
+ SSLCipherSuiteInfo info;
+ char *sep = "::";
+ int number_of_ciphers = sizeof (_conf_ciphers) /sizeof(cipherstruct);
+ int i;
+ if (cipher_names == NULL ) {
+ cipher_names = (char **) slapi_ch_calloc ((number_of_ciphers +1 ) , sizeof(char *));
+ for (i = 0 ; _conf_ciphers[i].name != NULL; i++ ) {
+ SSL_GetCipherSuiteInfo((PRUint16)_conf_ciphers[i].num,&info,sizeof(info));
+ cipher_names[i] = PR_smprintf("%s%s%s%s%s%s%s%s%d\0",_conf_ciphers[i].version,sep,_conf_ciphers[i].name,sep,info.symCipherName,sep,info.macAlgorithmName,sep,info.symKeyBits);
+ }
+ cipher_names[i] = NULL;
+ }
+ return cipher_names;
+}
+void
+_conf_setallciphers(int active)
+{
+ int x;
+
+ /* MLM - change: Because null_md5 is NOT encrypted at all, force
+ * them to activate it by name. */
+ for(x = 0; _conf_ciphers[x].name; x++) {
+ if(active && !strcmp(_conf_ciphers[x].name, "rsa_null_md5")) {
+ continue;
+ }
+#ifdef NET_SSL
+ SSL_CipherPrefSetDefault(_conf_ciphers[x].num, active ? PR_TRUE : PR_FALSE);
+#endif
+ }
+}
+
+char *
+_conf_setciphers(char *ciphers)
+{
+ char *t, err[MAGNUS_ERROR_LEN];
+ int x, active;
+ char *raw = ciphers;
+
+ /* Default is to activate all of them */
+ if(!ciphers || ciphers[0] == '\0') {
+ _conf_setallciphers(1);
+ return NULL;
+ }
+/* Enable all the ciphers by default and the following while loop would disable the user disabled ones This is needed becuase we added a new set of ciphers in the table . Right now there is no support for this from the console */
+ _conf_setallciphers(1);
+
+ t = ciphers;
+ while(t) {
+ while((*ciphers) && (isspace(*ciphers))) ++ciphers;
+
+ switch(*ciphers++) {
+ case '+':
+ active = 1; break;
+ case '-':
+ active = 0; break;
+ default:
+ sprintf(err, "invalid ciphers <%s>: format is "
+ "+cipher1,-cipher2...", raw);
+ return slapi_ch_strdup(err);
+ }
+ if( (t = strchr(ciphers, ',')) )
+ *t++ = '\0';
+
+ if(!strcasecmp(ciphers, "all"))
+ _conf_setallciphers(active);
+ else {
+ for(x = 0; _conf_ciphers[x].name; x++) {
+ if(!strcasecmp(ciphers, _conf_ciphers[x].name)) {
+ SSL_CipherPrefSetDefault(_conf_ciphers[x].num, active ? PR_TRUE : PR_FALSE);
+ break;
+ }
+ }
+ if(!_conf_ciphers[x].name) {
+ sprintf(err, "unknown cipher %s", ciphers);
+ return slapi_ch_strdup(err);
+ }
+ }
+ if(t)
+ ciphers = t;
+ }
+ return NULL;
+}
+
+/* SSL Policy stuff */
+
+/*
+ * Policy table
+ */
+static struct policy
+{
+ long ciphersuite;
+ int exportPolicy;
+} policy_table[] = {
+ { SSL_EN_RC4_128_WITH_MD5, SSL_NOT_ALLOWED },
+ { SSL_EN_RC4_128_EXPORT40_WITH_MD5, SSL_ALLOWED },
+ { SSL_EN_RC2_128_CBC_WITH_MD5, SSL_NOT_ALLOWED },
+ { SSL_EN_RC2_128_CBC_EXPORT40_WITH_MD5, SSL_ALLOWED },
+ { SSL_EN_IDEA_128_CBC_WITH_MD5, SSL_NOT_ALLOWED },
+ { SSL_EN_DES_64_CBC_WITH_MD5, SSL_NOT_ALLOWED },
+ { SSL_EN_DES_192_EDE3_CBC_WITH_MD5, SSL_NOT_ALLOWED },
+
+ /* SSL v3 Cipher Suites */
+ { SSL_RSA_WITH_NULL_MD5, SSL_ALLOWED },
+#if 0
+ { SSL_RSA_WITH_NULL_SHA, SSL_ALLOWED },
+#endif
+ { SSL_RSA_EXPORT_WITH_RC4_40_MD5, SSL_ALLOWED },
+ { SSL_RSA_WITH_RC4_128_MD5, SSL_RESTRICTED },
+#if 0
+ { SSL_RSA_WITH_RC4_128_SHA, SSL_NOT_ALLOWED },
+#endif
+ { SSL_RSA_EXPORT_WITH_RC2_CBC_40_MD5, SSL_ALLOWED },
+
+#if 0
+ { SSL_RSA_WITH_IDEA_CBC_SHA, SSL_NOT_ALLOWED },
+ { SSL_RSA_EXPORT_WITH_DES40_CBC_SHA, SSL_ALLOWED },
+#endif
+
+ { SSL_RSA_WITH_DES_CBC_SHA, SSL_NOT_ALLOWED },
+ { SSL_RSA_WITH_3DES_EDE_CBC_SHA, SSL_RESTRICTED },
+
+#if 0
+ { SSL_DH_DSS_EXPORT_WITH_DES40_CBC_SHA, SSL_ALLOWED },
+ { SSL_DH_DSS_WITH_DES_CBC_SHA, SSL_NOT_ALLOWED },
+ { SSL_DH_DSS_WITH_3DES_EDE_CBC_SHA, SSL_NOT_ALLOWED },
+ { SSL_DH_RSA_EXPORT_WITH_DES40_CBC_SHA, SSL_ALLOWED },
+ { SSL_DH_RSA_WITH_DES_CBC_SHA, SSL_NOT_ALLOWED },
+ { SSL_DH_RSA_WITH_3DES_EDE_CBC_SHA, SSL_NOT_ALLOWED },
+
+ { SSL_DHE_DSS_EXPORT_WITH_DES40_CBC_SHA, SSL_ALLOWED },
+ { SSL_DHE_DSS_WITH_DES_CBC_SHA, SSL_NOT_ALLOWED },
+ { SSL_DHE_DSS_WITH_3DES_EDE_CBC_SHA, SSL_NOT_ALLOWED },
+ { SSL_DHE_RSA_EXPORT_WITH_DES40_CBC_SHA, SSL_ALLOWED },
+ { SSL_DHE_RSA_WITH_DES_CBC_SHA, SSL_NOT_ALLOWED },
+ { SSL_DHE_RSA_WITH_3DES_EDE_CBC_SHA, SSL_NOT_ALLOWED },
+
+ { SSL_DH_ANON_EXPORT_WITH_RC4_40_MD5, SSL_ALLOWED },
+ { SSL_DH_ANON_WITH_RC4_128_MD5, SSL_NOT_ALLOWED },
+ { SSL_DH_ANON_EXPORT_WITH_DES40_CBC_SHA, SSL_ALLOWED },
+ { SSL_DH_ANON_WITH_DES_CBC_SHA, SSL_NOT_ALLOWED },
+ { SSL_DH_ANON_WITH_3DES_EDE_CBC_SHA, SSL_NOT_ALLOWED },
+#endif
+
+ { SSL_FORTEZZA_DMS_WITH_NULL_SHA, SSL_NOT_ALLOWED },
+ { SSL_FORTEZZA_DMS_WITH_FORTEZZA_CBC_SHA, SSL_NOT_ALLOWED },
+ { SSL_FORTEZZA_DMS_WITH_RC4_128_SHA, SSL_NOT_ALLOWED },
+
+ { SSL_RSA_FIPS_WITH_3DES_EDE_CBC_SHA, SSL_RESTRICTED },
+ { SSL_RSA_FIPS_WITH_DES_CBC_SHA, SSL_NOT_ALLOWED },
+};
+
+
+/*
+ * SSLPLCY_Install
+ *
+ * Call the SSL_CipherPolicySet function for each ciphersuite.
+ */
+PRStatus
+SSLPLCY_Install(void)
+{
+
+ SECStatus s = 0;
+
+
+#ifdef NS_DOMESTIC
+
+ s = NSS_SetDomesticPolicy();
+
+#else
+ s = NSS_SetExportPolicy();
+
+#endif
+
+
+ return s?PR_FAILURE:PR_SUCCESS;
+
+}
+
+
+#endif /* NET_SSL */
+
+static void
+slapd_SSL_report(int degree, char *fmt, va_list args)
+{
+ char buf[2048];
+ vsprintf( buf, fmt, args );
+ LDAPDebug( LDAP_DEBUG_ANY, "SSL %s: %s\n",
+ (degree == LOG_FAILURE) ? "failure" : "alert",
+ buf, 0 );
+}
+
+void
+slapd_SSL_error(char *fmt, ...)
+{
+ va_list args;
+ va_start(args, fmt);
+ slapd_SSL_report(LOG_FAILURE, fmt, args);
+ exit(1);
+}
+
+void
+slapd_SSL_warn(char *fmt, ...)
+{
+ va_list args;
+ va_start(args, fmt);
+ slapd_SSL_report(LOG_WARN, fmt, args);
+ va_end(args);
+}
+
+
+static void
+server_free_alias_dir(char *s)
+{
+ void *mem = s;
+
+ slapi_ch_free(&mem);
+}
+
+
+/**
+ * Get a particular entry
+ */
+static Slapi_Entry *
+getConfigEntry( const char *dn, Slapi_Entry **e2 ) {
+ Slapi_DN sdn;
+
+ slapi_sdn_init_dn_byref( &sdn, dn );
+ slapi_search_internal_get_entry( &sdn, NULL, e2,
+ plugin_get_default_component_id());
+ slapi_sdn_done( &sdn );
+ return *e2;
+}
+
+/**
+ * Free an entry
+ */
+static void
+freeConfigEntry( Slapi_Entry ** e ) {
+ if ( (e != NULL) && (*e != NULL) ) {
+ slapi_entry_free( *e );
+ *e = NULL;
+ }
+}
+
+/**
+ * Get a list of child DNs
+ */
+static char **
+getChildren( char *dn ) {
+ Slapi_PBlock *new_pb = NULL;
+ Slapi_Entry **e;
+ int search_result = 1;
+ int nEntries = 0;
+ char **list = NULL;
+
+ new_pb = slapi_search_internal ( dn, LDAP_SCOPE_ONELEVEL,
+ "(objectclass=nsEncryptionModule)",
+ NULL, NULL, 0);
+
+ slapi_pblock_get( new_pb, SLAPI_NENTRIES, &nEntries);
+ if ( nEntries > 0 ) {
+ slapi_pblock_get( new_pb, SLAPI_PLUGIN_INTOP_RESULT, &search_result);
+ slapi_pblock_get( new_pb, SLAPI_PLUGIN_INTOP_SEARCH_ENTRIES, &e);
+ if ( e != NULL ) {
+ int i;
+ list = (char **)slapi_ch_malloc( sizeof(*list) * (nEntries + 1));
+ for ( i = 0; e[i] != NULL; i++ ) {
+ list[i] = slapi_ch_strdup(slapi_entry_get_dn(e[i]));
+ }
+ list[nEntries] = NULL;
+ }
+ }
+ slapi_free_search_results_internal(new_pb);
+ slapi_pblock_destroy(new_pb );
+ return list;
+}
+
+/**
+ * Free a list of child DNs
+ */
+static void
+freeChildren( char **list ) {
+ if ( list != NULL ) {
+ int i;
+ for ( i = 0; list[i] != NULL; i++ ) {
+ slapi_ch_free( (void **)(&list[i]) );
+ }
+ slapi_ch_free( (void **)(&list) );
+ }
+}
+
+
+/*
+ * slapd_nss_init() is always called from main(), even if we do not
+ * plan to listen on a secure port. If config_available is 0, the
+ * config. entries from dse.ldif are NOT available (used only when
+ * running in referral mode).
+ * As of DS6.1, the init_ssl flag passed is ignored.
+ */
+int
+slapd_nss_init(int init_ssl, int config_available)
+{
+ SECStatus secStatus;
+ PRErrorCode errorCode;
+ char *keyfn = NULL;
+ char *certfn = NULL;
+ char *val = NULL;
+ char certPref[1024];
+ char keyPref[1024];
+ char path[1024];
+ int rv = 0;
+ int len = 0;
+ PRUint32 nssFlags = 0;
+ Slapi_Entry *ec = NULL;
+ char *instancedir;
+
+ if (config_available) {
+ getConfigEntry( configDN, &ec );
+ }
+
+ if ( ec != NULL ) {
+ certfn = slapi_entry_attr_get_charptr( ec, "nscertfile" );
+ keyfn = slapi_entry_attr_get_charptr( ec, "nskeyfile" );
+ slapi_entry_free (ec);
+ ec = NULL;
+ }
+
+ instancedir = config_get_instancedir();
+ strcpy(path, instancedir);
+ slapi_ch_free_string(&instancedir);
+
+ /* make sure path does not end in the path separator character */
+ len = strlen(path);
+ if (path[len-1] == '/' || path[len-1] == '\\') {
+ path[len-1] = '\0';
+ }
+
+ /* get the server root from the path */
+ val = strrchr(path, '/');
+ if (!val) {
+ val = strrchr(path, '\\');
+ }
+ val++;
+
+ if(keyfn && certfn) {
+ if (is_abspath(certfn)) {
+ /* first, initialize path from the certfn */
+ strcpy(path, certfn);
+ /* extract path from cert db filename */
+ val = strrchr(path, '/');
+ if (!val) {
+ val = strrchr(path, '\\');
+ }
+ *val = 0; /* path is initialized */
+ /* next, init the cert db prefix */
+ val++;
+ strcpy(certPref, val);
+ } else {
+ strcpy(val, certfn);
+ val = strrchr(path, '/');
+ if (!val) {
+ val = strrchr(path, '\\');
+ }
+ val++;
+ strcpy(certPref, val);
+ *val = '\0';
+ }
+ /* path represents now the base directory where cert, key, pin, and module db live */
+ /* richm - use strrstr to get the last occurance of -cert in the string, in case
+ the instance is named slapd-cert - the certdb name will be slapd-cert-cert7.db
+ */
+ val = PL_strrstr(certPref, "-cert");
+ val++;
+ *val = '\0';
+ /* certPref keeps the prefix added to the cert db, usually "slapd-myserver-" */
+
+ /* now find the key db prefix */
+ val = strrchr(keyfn, '/');
+ if (!val) {
+ val = strrchr(keyfn, '\\');
+ }
+ if (val != NULL) {
+ val++;
+ } else {
+ val = keyfn;
+ }
+ strcpy(keyPref, val);
+ /* richm - use strrstr to get the last occurance of -key in the string, in case
+ the instance is named slapd-key - the keydb name will be slapd-key-key3.db
+ */
+ val = PL_strrstr(keyPref, "-key");
+ val++;
+ *val = '\0';
+ /* keypref keeps the prefix added to the key db, usually "slapd-myserver-" */
+ } else {
+ if ( config_get_security() ) {
+ /* Have to have the key and cert file names to enable an SSL port */
+ errorCode = PR_GetError();
+ slapd_SSL_warn("Security Initialization: Failed to retrieve SSL "
+ "configuration information ("
+ SLAPI_COMPONENT_NAME_NSPR " error %d - %s): "
+ "nskeyfile: %s, nscertfile: %s ",
+ errorCode, slapd_pr_strerror(errorCode),
+ (keyfn ? "found" : "not found"),
+ (certfn ? "found" : "not found"));
+ }
+ sprintf(certPref, "%s-", val);
+ strcpy(keyPref, certPref);
+ strcpy(val, "alias/");
+ }
+
+ slapi_ch_free((void **) &certfn);
+ slapi_ch_free((void **) &keyfn);
+
+ /******** Initialise NSS *********/
+
+ nssFlags &= (~NSS_INIT_READONLY);
+ slapd_pk11_configurePKCS11(NULL, NULL, tokDes, ptokDes, NULL, NULL, NULL, NULL, 0, 0 );
+ secStatus = NSS_Initialize(path, certPref, keyPref, "secmod.db", nssFlags);
+
+ dongle_file_name = PR_smprintf("%s/%spin.txt", path, certPref);
+
+ if (secStatus != SECSuccess) {
+ errorCode = PR_GetError();
+ slapd_SSL_warn("Security Initialization: NSS initialization failed ("
+ SLAPI_COMPONENT_NAME_NSPR " error %d - %s): "
+ "path: %s, certdb prefix: %s, keydb prefix: %s.",
+ errorCode, slapd_pr_strerror(errorCode), path, certPref, keyPref);
+ rv = -1;
+ }
+
+ /****** end of NSS Initialization ******/
+
+ return rv;
+}
+
+
+
+
+
+/*
+ * slapd_ssl_init() is called from main() if we plan to listen
+ * on a secure port.
+ */
+int
+slapd_ssl_init() {
+ PRErrorCode errorCode;
+ char ** family_list;
+ char *val = NULL;
+ char cipher_string[1024];
+ int rv = 0;
+ PK11SlotInfo *slot;
+#ifndef _WIN32
+ SVRCOREStdPinObj *StdPinObj;
+#else
+ SVRCOREFilePinObj *FilePinObj;
+ SVRCOREAltPinObj *AltPinObj;
+ SVRCORENTUserPinObj *NTUserPinObj;
+#endif
+ Slapi_Entry *entry = NULL;
+
+ /* Get general information */
+
+ getConfigEntry( configDN, &entry );
+
+ val = slapi_entry_attr_get_charptr( entry, "nssslSessionTimeout" );
+ ciphers = slapi_entry_attr_get_charptr( entry, "nsssl3ciphers" );
+
+ /* We are currently using the value of sslSessionTimeout
+ for ssl3SessionTimeout, see SSL_ConfigServerSessionIDCache() */
+ /* Note from Tom Weinstein on the meaning of the timeout:
+
+ Timeouts are in seconds. '0' means use the default, which is
+ 24hrs for SSL3 and 100 seconds for SSL2.
+ */
+
+ if(!val) {
+ errorCode = PR_GetError();
+ slapd_SSL_warn("Security Initialization: Failed to retrieve SSL "
+ "configuration information ("
+ SLAPI_COMPONENT_NAME_NSPR " error %d - %s): "
+ "nssslSessionTimeout: %s ",
+ errorCode, slapd_pr_strerror(errorCode),
+ (val ? "found" : "not found"));
+ slapi_ch_free((void **) &val);
+ slapi_ch_free((void **) &ciphers);
+ return -1;
+ }
+
+ stimeout = atoi(val);
+ slapi_ch_free((void **) &val);
+
+#ifndef _WIN32
+ if ( SVRCORE_CreateStdPinObj(&StdPinObj, dongle_file_name, PR_TRUE) !=
+ SVRCORE_Success) {
+ errorCode = PR_GetError();
+ slapd_SSL_warn("Security Initialization: Unable to create PinObj ("
+ SLAPI_COMPONENT_NAME_NSPR " error %d - %s)",
+ errorCode, slapd_pr_strerror(errorCode));
+ return -1;
+ }
+ SVRCORE_RegisterPinObj((SVRCOREPinObj *)StdPinObj);
+#else
+ if (SVRCORE_CreateFilePinObj(&FilePinObj, dongle_file_name) !=
+ SVRCORE_Success) {
+ errorCode = PR_GetError();
+ slapd_SSL_warn("Security Initialization: Unable to create FilePinObj ("
+ SLAPI_COMPONENT_NAME_NSPR " error %d - %s)",
+ errorCode, slapd_pr_strerror(errorCode));
+ return -1;
+ }
+ if (SVRCORE_CreateNTUserPinObj(&NTUserPinObj) != SVRCORE_Success){
+ errorCode = PR_GetError();
+ slapd_SSL_warn("Security Initialization: Unable to create NTUserPinObj ("
+ SLAPI_COMPONENT_NAME_NSPR " error %d - %s)",
+ errorCode, slapd_pr_strerror(errorCode));
+ return -1;
+ }
+ if (SVRCORE_CreateAltPinObj(&AltPinObj, (SVRCOREPinObj *)FilePinObj,
+ (SVRCOREPinObj *)NTUserPinObj) != SVRCORE_Success) {
+ errorCode = PR_GetError();
+ slapd_SSL_warn("Security Initialization: Unable to create AltPinObj ("
+ SLAPI_COMPONENT_NAME_NSPR " error %d - %s)",
+ errorCode, slapd_pr_strerror(errorCode));
+ return -1;
+ }
+ SVRCORE_RegisterPinObj((SVRCOREPinObj *)AltPinObj);
+
+#endif /* _WIN32 */
+
+ if((family_list = getChildren(configDN))) {
+ char **family;
+ char *token;
+ char *activation;
+
+ for (family = family_list; *family; family++) {
+
+ token = NULL;
+ activation = NULL;
+
+ freeConfigEntry( &entry );
+
+ getConfigEntry( *family, &entry );
+ if ( entry == NULL ) {
+ continue;
+ }
+
+ activation = slapi_entry_attr_get_charptr( entry, "nssslactivation" );
+ if((!activation) || (!strcasecmp(activation, "off"))) {
+ /* this family was turned off, goto next */
+ slapi_ch_free((void **) &activation);
+ continue;
+ }
+
+ slapi_ch_free((void **) &activation);
+
+ token = slapi_entry_attr_get_charptr( entry, "nsssltoken" );
+ if( token ) {
+ if( !strcasecmp(token, "internal") ||
+ !strcasecmp(token, "internal (software)"))
+ slot = slapd_pk11_getInternalKeySlot();
+ else
+ slot = slapd_pk11_findSlotByName(token);
+ } else {
+ errorCode = PR_GetError();
+ slapd_SSL_warn("Security Initialization: Unable to get token ("
+ SLAPI_COMPONENT_NAME_NSPR " error %d - %s)",
+ errorCode, slapd_pr_strerror(errorCode));
+ return -1;
+ }
+
+ slapi_ch_free((void **) &token);
+
+ if (!slot) {
+ errorCode = PR_GetError();
+ slapd_SSL_warn("Security Initialization: Unable to find slot ("
+ SLAPI_COMPONENT_NAME_NSPR " error %d - %s)",
+ errorCode, slapd_pr_strerror(errorCode));
+ return -1;
+ }
+ /* authenticate */
+ if(slapd_pk11_authenticate(slot, PR_TRUE, NULL) != SECSuccess)
+ {
+ errorCode = PR_GetError();
+ slapd_SSL_warn("Security Initialization: Unable to authenticate ("
+ SLAPI_COMPONENT_NAME_NSPR " error %d - %s)",
+ errorCode, slapd_pr_strerror(errorCode));
+ return -1;
+ }
+ }
+ freeChildren( family_list );
+ }
+ freeConfigEntry( &entry );
+
+ if(SSLPLCY_Install() != PR_SUCCESS) {
+ errorCode = PR_GetError();
+ slapd_SSL_warn("Security Initialization: Unable to set SSL export policy ("
+ SLAPI_COMPONENT_NAME_NSPR " error %d - %s)",
+ errorCode, slapd_pr_strerror(errorCode));
+ return -1;
+ }
+
+
+ /* ugaston- Cipher preferences must be set before any sslSocket is created
+ * for such sockets to take preferences into account.
+ */
+
+ /* Step Three.5: Set SSL cipher preferences */
+ *cipher_string = 0;
+ if(ciphers && (*ciphers) && strcmp(ciphers, "blank"))
+ strcpy(cipher_string, ciphers);
+ slapi_ch_free((void **) &ciphers);
+
+ if( NULL != (val = _conf_setciphers(cipher_string)) ) {
+ errorCode = PR_GetError();
+ slapd_SSL_warn("Security Initialization: Failed to set SSL cipher "
+ "preference information: %s (" SLAPI_COMPONENT_NAME_NSPR " error %d - %s)",
+ val, errorCode, slapd_pr_strerror(errorCode));
+ rv = 3;
+ slapi_ch_free((void **) &val);
+ }
+
+ freeConfigEntry( &entry );
+
+
+ /* Introduce a way of knowing whether slapd_ssl_init has
+ * already been executed. */
+ _security_library_initialized = 1;
+
+
+ if ( rv != 0 )
+ return rv;
+
+
+ return 0;
+
+}
+
+
+int slapd_ssl_init2(PRFileDesc **fd, int startTLS)
+{
+ PRFileDesc *pr_sock, *sock = (*fd);
+ PRErrorCode errorCode;
+ SECStatus rv = SECFailure;
+ char ** family_list;
+ CERTCertificate *cert = NULL;
+ SECKEYPrivateKey *key = NULL;
+ char errorbuf[BUFSIZ];
+ char *val = NULL;
+ int nFamilies = 0;
+ SECStatus sslStatus;
+ int slapd_SSLclientAuth;
+ char* tmpDir;
+ Slapi_Entry *e = NULL;
+
+ /* turn off the PKCS11 pin interactive mode */
+#ifndef _WIN32
+ SVRCOREStdPinObj *StdPinObj;
+
+ StdPinObj = (SVRCOREStdPinObj *)SVRCORE_GetRegisteredPinObj();
+ SVRCORE_SetStdPinInteractive(StdPinObj, PR_FALSE);
+#endif
+
+ errorbuf[0] = '\0';
+
+ /* Import pr fd into SSL */
+ pr_sock = SSL_ImportFD( NULL, sock );
+ if( pr_sock == (PRFileDesc *)NULL ) {
+ errorCode = PR_GetError();
+ slapd_SSL_warn("Security Initialization: Failed to import NSPR "
+ "fd into SSL (" SLAPI_COMPONENT_NAME_NSPR " error %d - %s)",
+ errorCode, slapd_pr_strerror(errorCode));
+ return 1;
+ }
+
+ (*fd) = pr_sock;
+
+ /* Step / Three.6 /
+ * - If in FIPS mode, authenticate to the token before
+ * doing anything else
+ */
+ {
+ PK11SlotInfo *slot = slapd_pk11_getInternalSlot();
+ if (!slot) {
+ errorCode = PR_GetError();
+ slapd_SSL_warn("Security Initialization: Unable to get internal slot ("
+ SLAPI_COMPONENT_NAME_NSPR " error %d - %s)",
+ errorCode, slapd_pr_strerror(errorCode));
+ return -1;
+ }
+
+ if(slapd_pk11_isFIPS()) {
+ if(slapd_pk11_authenticate(slot, PR_TRUE, NULL) != SECSuccess) {
+ errorCode = PR_GetError();
+ slapd_SSL_warn("Security Initialization: Unable to authenticate ("
+ SLAPI_COMPONENT_NAME_NSPR " error %d - %s)",
+ errorCode, slapd_pr_strerror(errorCode));
+ return -1;
+ }
+ }
+
+ slapd_pk11_setSlotPWValues(slot, 0, 0);
+ }
+
+
+
+ /*
+ * Now, get the complete list of cipher families. Each family
+ * has a token name and personality name which we'll use to find
+ * appropriate keys and certs, and call SSL_ConfigSecureServer
+ * with.
+ */
+
+ if((family_list = getChildren(configDN))) {
+ char **family;
+ char cert_name[1024];
+ char *token;
+ char *personality;
+ char *activation;
+
+ for (family = family_list; *family; family++) {
+ token = NULL;
+ personality = NULL;
+ activation = NULL;
+
+ getConfigEntry( *family, &e );
+ if ( e == NULL ) {
+ continue;
+ }
+
+ activation = slapi_entry_attr_get_charptr( e, "nssslactivation" );
+ if((!activation) || (!strcasecmp(activation, "off"))) {
+ /* this family was turned off, goto next */
+ slapi_ch_free((void **) &activation);
+ freeConfigEntry( &e );
+ continue;
+ }
+
+ slapi_ch_free((void **) &activation);
+
+ token = slapi_entry_attr_get_charptr( e, "nsssltoken" );
+ personality = slapi_entry_attr_get_charptr( e, "nssslpersonalityssl" );
+ if( token && personality ) {
+ if( !strcasecmp(token, "internal") ||
+ !strcasecmp(token, "internal (software)") )
+ strcpy(cert_name, personality);
+ else
+ /* external PKCS #11 token - attach token name */
+ sprintf(cert_name, "%s:%s", token, personality);
+ }
+ else {
+ errorCode = PR_GetError();
+ slapd_SSL_warn("Security Initialization: Failed to get cipher "
+ "family information. Missing nsssltoken or"
+ "nssslpersonalityssl in %s ("
+ SLAPI_COMPONENT_NAME_NSPR " error %d - %s)",
+ *family, errorCode, slapd_pr_strerror(errorCode));
+ slapi_ch_free((void **) &token);
+ slapi_ch_free((void **) &personality);
+ freeConfigEntry( &e );
+ continue;
+ }
+
+ slapi_ch_free((void **) &token);
+
+ /* Step Four -- Locate the server certificate */
+ cert = slapd_pk11_findCertFromNickname(cert_name, NULL);
+
+ if (cert == NULL) {
+ errorCode = PR_GetError();
+ slapd_SSL_warn("Security Initialization: Can't find "
+ "certificate (%s) for family %s ("
+ SLAPI_COMPONENT_NAME_NSPR " error %d - %s)",
+ cert_name, *family,
+ errorCode, slapd_pr_strerror(errorCode));
+ }
+ /* Step Five -- Get the private key from cert */
+ if( cert != NULL )
+ key = slapd_pk11_findKeyByAnyCert(cert, NULL);
+
+ if (key == NULL) {
+ errorCode = PR_GetError();
+ slapd_SSL_warn("Security Initialization: Unable to retrieve "
+ "private key for cert %s of family %s ("
+ SLAPI_COMPONENT_NAME_NSPR " error %d - %s)",
+ cert_name, *family,
+ errorCode, slapd_pr_strerror(errorCode));
+ slapi_ch_free((void **) &personality);
+ CERT_DestroyCertificate(cert);
+ cert = NULL;
+ freeConfigEntry( &e );
+ continue;
+ }
+
+ /* Step Six -- Configure Secure Server Mode */
+ if(pr_sock) {
+ SECCertificateUsage returnedUsages;
+ rv = CERT_VerifyCertificateNow(
+ CERT_GetDefaultCertDB(), cert, PR_TRUE,
+ certificateUsageSSLServer,
+ SSL_RevealPinArg(pr_sock),
+ &returnedUsages);
+ if (SECSuccess == rv) {
+ if( slapd_pk11_fortezzaHasKEA(cert) == PR_TRUE ) {
+ rv = SSL_ConfigSecureServer(*fd, cert, key, kt_fortezza);
+ }
+ else {
+ rv = SSL_ConfigSecureServer(*fd, cert, key, kt_rsa);
+ }
+ if (SECSuccess != rv) {
+ errorCode = PR_GetError();
+ slapd_SSL_warn("ConfigSecureServer: "
+ "Server key/certificate is "
+ "bad for cert %s of family %s ("
+ SLAPI_COMPONENT_NAME_NSPR " error %d - %s)",
+ cert_name, *family, errorCode,
+ slapd_pr_strerror(errorCode));
+ }
+ } else {
+ /* verify certificate failed */
+ /* If the common name in the subject DN for the certificate
+ * is not identical to the domain name passed in the
+ * hostname parameter, SECFailure. */
+ errorCode = PR_GetError();
+ slapd_SSL_warn("CERT_VerifyCertificateNow: "
+ "verify certificate failed "
+ "for cert %s of family %s ("
+ SLAPI_COMPONENT_NAME_NSPR
+ " error %d - %s)",
+ cert_name, *family, errorCode,
+ slapd_pr_strerror(errorCode));
+ }
+ }
+ if (cert) {
+ CERT_DestroyCertificate(cert);
+ cert = NULL;
+ }
+ if (SECSuccess != rv) {
+ slapi_ch_free((void **) &personality);
+ freeConfigEntry( &e );
+ continue;
+ }
+ nFamilies++;
+ freeConfigEntry( &e );
+ }
+ freeChildren( family_list );
+ }
+
+
+ if ( !nFamilies ) {
+ slapd_SSL_error("None of the cipher are valid");
+ return -1;
+ }
+
+ /* Step Seven -- Configure Server Session ID Cache */
+
+ tmpDir = slapd_get_tmp_dir();
+
+ slapi_log_error(
+ SLAPI_LOG_TRACE,
+ "slapd_ssl_init2",
+ "tmp dir = %s\n", tmpDir);
+
+ rv = SSL_ConfigServerSessionIDCache(0, stimeout, stimeout, tmpDir);
+ if (rv) {
+ errorCode = PR_GetError();
+ if (errorCode == ENOSPC) {
+ slapd_SSL_error("Config of server nonce cache failed, "
+ "out of disk space! Make more room in /tmp "
+ "and try again. (" SLAPI_COMPONENT_NAME_NSPR " error %d - %s)",
+ errorCode, slapd_pr_strerror(errorCode));
+ }
+ else {
+ slapd_SSL_error("Config of server nonce cache failed (error %d - %s)",
+ errorCode, slapd_pr_strerror(errorCode));
+ }
+ return rv;
+ }
+
+ sslStatus = SSL_OptionSet(pr_sock, SSL_SECURITY, PR_TRUE);
+ if (sslStatus != SECSuccess) {
+ errorCode = PR_GetError();
+ slapd_SSL_warn("Security Initialization: Failed to enable security "
+ "on the imported socket (" SLAPI_COMPONENT_NAME_NSPR " error %d - %s)",
+ errorCode, slapd_pr_strerror(errorCode));
+ return -1;
+ }
+
+ sslStatus = SSL_OptionSet(pr_sock, SSL_ENABLE_SSL3, PR_TRUE);
+ if (sslStatus != SECSuccess) {
+ errorCode = PR_GetError();
+ slapd_SSL_warn("Security Initialization: Failed to enable SSLv3 "
+ "on the imported socket (" SLAPI_COMPONENT_NAME_NSPR " error %d - %s)",
+ errorCode, slapd_pr_strerror(errorCode));
+ }
+
+ sslStatus = SSL_OptionSet(pr_sock, SSL_ENABLE_TLS, PR_TRUE);
+ if (sslStatus != SECSuccess) {
+ errorCode = PR_GetError();
+ slapd_SSL_warn("Security Initialization: Failed to enable TLS "
+ "on the imported socket (" SLAPI_COMPONENT_NAME_NSPR " error %d - %s)",
+ errorCode, slapd_pr_strerror(errorCode));
+ }
+/* Explicitly disabling SSL2 - NGK */
+ sslStatus = SSL_OptionSet(pr_sock, SSL_ENABLE_SSL2, PR_FALSE);
+
+ /* Retrieve the SSL Client Authentication status from cn=config */
+ /* Set a default value if no value found */
+ getConfigEntry( configDN, &e );
+ val = NULL;
+ if ( e != NULL ) {
+ val = slapi_entry_attr_get_charptr( e, "nssslclientauth" );
+ }
+
+ if( !val ) {
+ errorCode = PR_GetError();
+ slapd_SSL_warn("Security Initialization: Cannot get SSL Client "
+ "Authentication status. No nsslclientauth in %s ("
+ SLAPI_COMPONENT_NAME_NSPR " error %d - %s)",
+ configDN, errorCode, slapd_pr_strerror(errorCode));
+ switch( SLAPD_SSLCLIENTAUTH_DEFAULT ) {
+ case SLAPD_SSLCLIENTAUTH_OFF:
+ val = "off";
+ break;
+ case SLAPD_SSLCLIENTAUTH_ALLOWED:
+ val = "allowed";
+ break;
+ case SLAPD_SSLCLIENTAUTH_REQUIRED:
+ val = "required";
+ break;
+ default:
+ val = "allowed";
+ break;
+ }
+ }
+ if( config_set_SSLclientAuth( "nssslclientauth", val, errorbuf,
+ CONFIG_APPLY ) != LDAP_SUCCESS ) {
+ errorCode = PR_GetError();
+ slapd_SSL_warn("Security Initialization: Cannot set SSL Client "
+ "Authentication status to \"%s\", error (%s). "
+ "Supported values are \"off\", \"allowed\" "
+ "and \"required\". (" SLAPI_COMPONENT_NAME_NSPR " error %d - %s)",
+ val, errorbuf, errorCode, slapd_pr_strerror(errorCode));
+ }
+
+ freeConfigEntry( &e );
+
+ if(( slapd_SSLclientAuth = config_get_SSLclientAuth()) != SLAPD_SSLCLIENTAUTH_OFF ) {
+ int err;
+ switch (slapd_SSLclientAuth) {
+ case SLAPD_SSLCLIENTAUTH_ALLOWED:
+#ifdef SSL_REQUIRE_CERTIFICATE /* new feature */
+ if ((err = SSL_OptionSet (pr_sock, SSL_REQUIRE_CERTIFICATE, PR_FALSE)) < 0) {
+ PRErrorCode prerr = PR_GetError();
+ LDAPDebug (LDAP_DEBUG_ANY,
+ "SSL_OptionSet(SSL_REQUIRE_CERTIFICATE,PR_FALSE) %d "
+ SLAPI_COMPONENT_NAME_NSPR " error %d (%s)\n",
+ err, prerr, slapd_pr_strerror(prerr));
+ }
+#endif
+ /* Give the client a clear opportunity to send her certificate: */
+ case SLAPD_SSLCLIENTAUTH_REQUIRED:
+ if ((err = SSL_OptionSet (pr_sock, SSL_REQUEST_CERTIFICATE, PR_TRUE)) < 0) {
+ PRErrorCode prerr = PR_GetError();
+ LDAPDebug (LDAP_DEBUG_ANY,
+ "SSL_OptionSet(SSL_REQUEST_CERTIFICATE,PR_TRUE) %d "
+ SLAPI_COMPONENT_NAME_NSPR " error %d (%s)\n",
+ err, prerr, slapd_pr_strerror(prerr));
+ }
+ default: break;
+ }
+ }
+
+ /* Introduce a way of knowing whether slapd_ssl_init2 has
+ * already been executed.
+ * The cases in which slapd_ssl_init2 is executed during an
+ * Start TLS operation are not taken into account, for it is
+ * the fact of being executed by the server's SSL listener socket
+ * that matters. */
+
+ if (!startTLS)
+ _ssl_listener_initialized = 1; /* --ugaston */
+
+ return 0;
+}
+
+/* richm 20020227
+ To do LDAP client SSL init, we need to do
+
+ static void
+ ldapssl_basic_init( void )
+ {
+ PR_Init(PR_USER_THREAD, PR_PRIORITY_NORMAL, 0);
+
+ PR_SetConcurrency( 4 );
+ }
+ NSS_Init(certdbpath);
+ SSL_OptionSetDefault(SSL_ENABLE_SSL2, PR_FALSE);
+ SSL_OptionSetDefault(SSL_ENABLE_SSLdirs
+3, PR_TRUE);
+#ifdef NS_DOMESTIC
+ s = NSS_SetDomesticPolicy();
+#elif NS_EXPORT
+ s = NSS_SetExportPolicy();
+
+We already do pr_init, we don't need pr_setconcurrency, we already do nss_init and the rest
+
+*/
+
+int
+slapd_SSL_client_init()
+{
+ return 0;
+}
+
+static int
+slapd_SSL_client_auth (LDAP* ld)
+{
+ int rc = 0;
+ PRErrorCode errorCode;
+ char* pw = NULL;
+ char ** family_list;
+ Slapi_Entry *entry = NULL;
+ char cert_name[1024];
+ char *token = NULL;
+#ifndef _WIN32
+ SVRCOREStdPinObj *StdPinObj;
+#else
+ SVRCOREAltPinObj *AltPinObj;
+#endif
+ SVRCOREError err = SVRCORE_Success;
+
+ if((family_list = getChildren(configDN))) {
+ char **family;
+ char *personality = NULL;
+ char *activation = NULL;
+ char *cipher = NULL;
+
+ for (family = family_list; *family; family++) {
+ getConfigEntry( *family, &entry );
+ if ( entry == NULL ) {
+ continue;
+ }
+
+ activation = slapi_entry_attr_get_charptr( entry, "nssslactivation" );
+ if((!activation) || (!strcasecmp(activation, "off"))) {
+ /* this family was turned off, goto next */
+ slapi_ch_free((void **) &activation);
+ freeConfigEntry( &entry );
+ continue;
+ }
+
+ slapi_ch_free((void **) &activation);
+
+ personality = slapi_entry_attr_get_charptr( entry, "nssslpersonalityssl" );
+ cipher = slapi_entry_attr_get_charptr( entry, "cn" );
+ if ( cipher && !strcasecmp(cipher, "RSA" )) {
+ char *ssltoken;
+
+ /* If there already is a token name, use it */
+ if (token) {
+ slapi_ch_free((void **) &personality);
+ slapi_ch_free((void **) &cipher);
+ freeConfigEntry( &entry );
+ continue;
+ }
+
+ ssltoken = slapi_entry_attr_get_charptr( entry, "nsssltoken" );
+ if( ssltoken && personality ) {
+ if( !strcasecmp(ssltoken, "internal") ||
+ !strcasecmp(ssltoken, "internal (software)") ) {
+
+ /* Translate config internal name to more
+ * readable form. Certificate name is just
+ * the personality for internal tokens.
+ */
+ token = slapi_ch_strdup(internalTokenName);
+ strcpy(cert_name, personality);
+ slapi_ch_free((void **) &ssltoken);
+ } else {
+ /* external PKCS #11 token - attach token name */
+ /*ssltoken was already dupped and we don't need it anymore*/
+ token = ssltoken;
+ sprintf(cert_name, "%s:%s", token, personality);
+ }
+ } else {
+ errorCode = PR_GetError();
+ slapd_SSL_warn("Security Initialization: Failed to get cipher "
+ "family information. Missing nsssltoken or"
+ "nssslpersonalityssl in %s ("
+ SLAPI_COMPONENT_NAME_NSPR " error %d - %s)",
+ *family, errorCode, slapd_pr_strerror(errorCode));
+ slapi_ch_free((void **) &ssltoken);
+ slapi_ch_free((void **) &personality);
+ slapi_ch_free((void **) &cipher);
+ freeConfigEntry( &entry );
+ continue;
+ }
+ } else { /* external PKCS #11 cipher */
+ char *ssltoken;
+
+ ssltoken = slapi_entry_attr_get_charptr( entry, "nsssltoken" );
+ if( token && personality ) {
+
+ /* free the old token and remember the new one */
+ if (token) slapi_ch_free((void **)&token);
+ token = ssltoken; /*ssltoken was already dupped and we don't need it anymore*/
+
+ /* external PKCS #11 token - attach token name */
+ sprintf(cert_name, "%s:%s", token, personality);
+ } else {
+ errorCode = PR_GetError();
+ slapd_SSL_warn("Security Initialization: Failed to get cipher "
+ "family information. Missing nsssltoken or"
+ "nssslpersonalityssl in %s ("
+ SLAPI_COMPONENT_NAME_NSPR " error %d - %s)",
+ *family, errorCode, slapd_pr_strerror(errorCode));
+ slapi_ch_free((void **) &ssltoken);
+ slapi_ch_free((void **) &personality);
+ slapi_ch_free((void **) &cipher);
+ freeConfigEntry( &entry );
+ continue;
+ }
+
+ }
+ slapi_ch_free((void **) &personality);
+ slapi_ch_free((void **) &cipher);
+ freeConfigEntry( &entry );
+ } /* end of for */
+
+ freeChildren( family_list );
+ }
+
+ /* Free config data */
+
+ /* We cannot allow NSS to cache outgoing client auth connections -
+ each client auth connection must have it's own non-shared SSL
+ connection to the peer so that it will go through the
+ entire handshake protocol every time including the use of its
+ own unique client cert - see bug 605457
+ */
+
+ ldapssl_set_option(ld, SSL_NO_CACHE, PR_TRUE);
+
+#ifndef _WIN32
+ StdPinObj = (SVRCOREStdPinObj *)SVRCORE_GetRegisteredPinObj();
+ err = SVRCORE_StdPinGetPin( &pw, StdPinObj, token );
+#else
+ AltPinObj = (SVRCOREAltPinObj *)SVRCORE_GetRegisteredPinObj();
+ pw = SVRCORE_GetPin( (SVRCOREPinObj *)AltPinObj, token, PR_FALSE);
+#endif
+ if ( err != SVRCORE_Success || pw == NULL) {
+ errorCode = PR_GetError();
+ slapd_SSL_warn("SSL client authentication cannot be used "
+ "(no password). (" SLAPI_COMPONENT_NAME_NSPR " error %d - %s)",
+ errorCode, slapd_pr_strerror(errorCode));
+ } else {
+ rc = ldapssl_enable_clientauth (ld, SERVER_KEY_NAME, pw, cert_name);
+ if (rc != 0) {
+ errorCode = PR_GetError();
+ slapd_SSL_warn("ldapssl_enable_clientauth(%s, %s) %i ("
+ SLAPI_COMPONENT_NAME_NSPR " error %d - %s)",
+ SERVER_KEY_NAME, cert_name, rc,
+ errorCode, slapd_pr_strerror(errorCode));
+ }
+ }
+
+ if (token) slapi_ch_free((void**)&token);
+ slapi_ch_free((void**)&pw);
+
+ LDAPDebug (LDAP_DEBUG_TRACE, "slapd_SSL_client_auth() %i\n", rc, 0, 0);
+ return rc;
+}
+
+int
+slapd_simple_client_bind_s(LDAP* ld, char* DN, char* pw, int LDAPv)
+{
+ int rc;
+ PRErrorCode errorCode;
+
+ ldap_set_option(ld, LDAP_OPT_PROTOCOL_VERSION, (void *) &LDAPv);
+ rc = ldap_simple_bind_s (ld, DN, pw);
+ if (rc != 0) {
+ errorCode = PR_GetError();
+ slapd_SSL_warn("ldap_simple_bind_s(%s, %s) %i (" SLAPI_COMPONENT_NAME_NSPR " error %d - %s)",
+ DN, pw, rc, errorCode, slapd_pr_strerror(errorCode));
+ }
+ LDAPDebug (LDAP_DEBUG_TRACE, "slapd_simple_client_bind_s(%s, %i) %i\n", DN, LDAPv, rc);
+ return rc;
+}
+
+int
+slapd_SSL_client_bind_s (LDAP* ld, char* DN, char* pw, int use_SSL, int LDAPv)
+{
+ int rc;
+ struct berval noCred = {0, 0};
+
+ if (!use_SSL || LDAPv == LDAP_VERSION2) {
+ rc = slapd_simple_client_bind_s(ld, DN, pw, LDAPv);
+ } else {
+
+ LDAPDebug (
+ LDAP_DEBUG_TRACE,
+ "slapd_SSL_client_bind_s: Trying SSL Client Authentication\n",
+ 0, 0, 0);
+
+ rc = slapd_SSL_client_auth(ld);
+
+ if(rc != 0)
+ {
+ LDAPDebug (
+ LDAP_DEBUG_TRACE,
+ "slapd_SSL_client_bind_s: SSL Client Auth Failed during replication Bind\n",
+ 0, 0, 0);
+ return rc;
+ }
+
+ rc = ldap_sasl_bind_s (ld, "", LDAP_SASL_EXTERNAL, &noCred,
+ NULL /* LDAPControl **serverctrls */,
+ NULL /* LDAPControl **clientctrls */,
+ NULL /* struct berval **servercredp */);
+
+ }
+ LDAPDebug (
+ LDAP_DEBUG_TRACE,
+ "slapd_SSL_client_bind_s(%i,%i) %i\n", use_SSL, LDAPv, rc);
+ return rc;
+}
+
+int
+slapd_sasl_ext_client_bind (LDAP* ld, int **msgid)
+{
+ int rc;
+ PRErrorCode errorCode;
+ struct berval noCred = {0, 0};
+
+ LDAPDebug (
+ LDAP_DEBUG_TRACE,
+ "slapd_sasl_ext_client_bind: Trying SSL Client Authentication\n",
+ 0, 0, 0);
+
+ rc = slapd_SSL_client_auth(ld);
+ if(rc != 0)
+ {
+ LDAPDebug (
+ LDAP_DEBUG_TRACE,
+ "slapd_sasl_ext_client_bind: SSL Client Auth Failed during replication Bind\n",
+ 0, 0, 0);
+ return rc;
+ }
+
+ rc = ldap_sasl_bind (ld, "", LDAP_SASL_EXTERNAL, &noCred,
+ NULL,
+ NULL,
+ *msgid);
+ if (rc != 0) {
+ errorCode = PR_GetError();
+ slapd_SSL_warn("ldap_sasl_bind(\"\",LDAP_SASL_EXTERNAL) %i (" SLAPI_COMPONENT_NAME_NSPR " error %d - %s)",
+ rc, errorCode, slapd_pr_strerror(errorCode));
+ }
+
+ LDAPDebug (
+ LDAP_DEBUG_TRACE,
+ "slapd_sasl_ext_client_bind %i\n", rc, 0, 0);
+
+ return rc;
+}
+
+
+int slapd_Client_auth(LDAP* ld)
+{
+ int rc=0;
+
+ rc = slapd_SSL_client_auth (ld);
+
+ return rc;
+}
+
+
+/* Function for keeping track of the SSL initialization status:
+ * - returns 1: when slapd_ssl_init has been executed
+ */
+int
+slapd_security_library_is_initialized()
+{
+ return _security_library_initialized;
+}
+
+
+/* Function for keeping track of the SSL listener socket initialization status:
+ * - returns 1: when slapd_ssl_init2 has been executed
+ */
+int
+slapd_ssl_listener_is_initialized()
+{
+ return _ssl_listener_initialized;
+}
+
+
+char* slapd_get_tmp_dir()
+{
+ static char tmpdir[] = "/tmp";
+ static char tmp[256];
+ char* instanceDir;
+#if defined( XP_WIN32 )
+ unsigned ilen;
+ char pch;
+#endif
+ struct stat ffinfo;
+
+ tmp[0] = '\0';
+
+ if((instanceDir = config_get_instancedir()) == NULL)
+ {
+ slapi_log_error(
+ SLAPI_LOG_FATAL,
+ "slapd_get_tmp_dir",
+ "config_get_instancedir returns NULL Setting tmp dir to default\n");
+
+#if defined( XP_WIN32 )
+ ilen = strlen(tmp);
+ GetTempPath( ilen+1, tmp );
+ /* Remove trailing slash. */
+ pch = tmp[ilen-1];
+ if( pch == '\\' || pch == '/' )
+ tmp[ilen-1] = '\0';
+ return tmp;
+#else
+ return( tmpdir );
+#endif
+ }
+
+ sprintf(tmp,"%s/tmp",instanceDir);
+
+#if defined( XP_WIN32 )
+ for(ilen=0;ilen < strlen(tmp); ilen++)
+ {
+ if(tmp[ilen]=='/')
+ tmp[ilen]='\\';
+ }
+#endif
+
+ if(stat(tmp,&ffinfo) == -1)
+#if defined( XP_WIN32 )
+ if(CreateDirectory(tmp, NULL) == 0)
+ {
+ slapi_log_error(
+ SLAPI_LOG_FATAL,
+ "slapd_get_tmp_dir",
+ "CreateDirectory(%s, NULL) Error: %s\n",
+ tmp, strerror(errno));
+ return ( tmpdir );
+ }
+#else
+ if(mkdir(tmp, 00770) == -1)
+ {
+ slapi_log_error(
+ SLAPI_LOG_FATAL,
+ "slapd_get_tmp_dir",
+ "mkdir(%s, 00770) Error: %s\n",
+ tmp, strerror(errno));
+ return ( tmpdir );
+ }
+#endif
+
+ return ( tmp );
+
+}
+
+#endif /* NET_SSL */
diff --git a/ldap/servers/slapd/sslerrstrs.h b/ldap/servers/slapd/sslerrstrs.h
new file mode 100644
index 00000000..5b63e3a7
--- /dev/null
+++ b/ldap/servers/slapd/sslerrstrs.h
@@ -0,0 +1,355 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+/*
+ * sslerrstrs.h - map SSL errors to strings (used by errormap.c)
+ *
+ */
+
+/*
+ ****************************************************************************
+ * The code below this point was provided by Nelson Bolyard <nelsonb> of the
+ * Netscape Certificate Server team on 27-March-1998.
+ * Taken from the file ns/security/cmd/lib/SSLerrs.h on NSS_1_BRANCH.
+ * Last updated from there: 24-July-1998 by Mark Smith <mcs>
+ *
+ * All of the Directory Server specific changes are enclosed inside
+ * #ifdef NS_DS.
+ ****************************************************************************
+ */
+
+/* SSL-specific security error codes */
+/* caller must include "sslerr.h" */
+
+ER3(SSL_ERROR_EXPORT_ONLY_SERVER, SSL_ERROR_BASE + 0,
+"Unable to communicate securely. Peer does not support high-grade encryption.")
+
+ER3(SSL_ERROR_US_ONLY_SERVER, SSL_ERROR_BASE + 1,
+"Unable to communicate securely. Peer requires high-grade encryption which is not supported.")
+
+ER3(SSL_ERROR_NO_CYPHER_OVERLAP, SSL_ERROR_BASE + 2,
+"Cannot communicate securely with peer: no common encryption algorithm(s).")
+
+ER3(SSL_ERROR_NO_CERTIFICATE, SSL_ERROR_BASE + 3,
+"Unable to find the certificate or key necessary for authentication.")
+
+ER3(SSL_ERROR_BAD_CERTIFICATE, SSL_ERROR_BASE + 4,
+"Unable to communicate securely with peer: peers's certificate was rejected.")
+
+/* unused (SSL_ERROR_BASE + 5),*/
+
+ER3(SSL_ERROR_BAD_CLIENT, SSL_ERROR_BASE + 6,
+"The server has encountered bad data from the client.")
+
+ER3(SSL_ERROR_BAD_SERVER, SSL_ERROR_BASE + 7,
+"The client has encountered bad data from the server.")
+
+ER3(SSL_ERROR_UNSUPPORTED_CERTIFICATE_TYPE, SSL_ERROR_BASE + 8,
+"Unsupported certificate type.")
+
+ER3(SSL_ERROR_UNSUPPORTED_VERSION, SSL_ERROR_BASE + 9,
+"Peer using unsupported version of security protocol.")
+
+/* unused (SSL_ERROR_BASE + 10),*/
+
+ER3(SSL_ERROR_WRONG_CERTIFICATE, SSL_ERROR_BASE + 11,
+"Client authentication failed: private key in key database does not match public key in certificate database.")
+
+ER3(SSL_ERROR_BAD_CERT_DOMAIN, SSL_ERROR_BASE + 12,
+"Unable to communicate securely with peer: requested domain name does not match the server's certificate.")
+
+/* SSL_ERROR_POST_WARNING (SSL_ERROR_BASE + 13),
+ defined in sslerr.h
+*/
+
+ER3(SSL_ERROR_SSL2_DISABLED, (SSL_ERROR_BASE + 14),
+"Peer only supports SSL version 2, which is locally disabled.")
+
+
+ER3(SSL_ERROR_BAD_MAC_READ, (SSL_ERROR_BASE + 15),
+"SSL received a record with an incorrect Message Authentication Code.")
+
+ER3(SSL_ERROR_BAD_MAC_ALERT, (SSL_ERROR_BASE + 16),
+"SSL peer reports incorrect Message Authentication Code.")
+
+ER3(SSL_ERROR_BAD_CERT_ALERT, (SSL_ERROR_BASE + 17),
+"SSL peer cannot verify your certificate.")
+
+ER3(SSL_ERROR_REVOKED_CERT_ALERT, (SSL_ERROR_BASE + 18),
+"SSL peer rejected your certificate as revoked.")
+
+ER3(SSL_ERROR_EXPIRED_CERT_ALERT, (SSL_ERROR_BASE + 19),
+"SSL peer rejected your certificate as expired.")
+
+ER3(SSL_ERROR_SSL_DISABLED, (SSL_ERROR_BASE + 20),
+"Cannot connect: SSL is disabled.")
+
+ER3(SSL_ERROR_FORTEZZA_PQG, (SSL_ERROR_BASE + 21),
+"Cannot connect: SSL peer is in another FORTEZZA domain.")
+
+
+ER3(SSL_ERROR_UNKNOWN_CIPHER_SUITE , (SSL_ERROR_BASE + 22),
+"An unknown SSL cipher suite has been requested.")
+
+ER3(SSL_ERROR_NO_CIPHERS_SUPPORTED , (SSL_ERROR_BASE + 23),
+"No cipher suites are present and enabled in this program.")
+
+ER3(SSL_ERROR_BAD_BLOCK_PADDING , (SSL_ERROR_BASE + 24),
+"SSL received a record with bad block padding.")
+
+ER3(SSL_ERROR_RX_RECORD_TOO_LONG , (SSL_ERROR_BASE + 25),
+"SSL received a record that exceeded the maximum permissible length.")
+
+ER3(SSL_ERROR_TX_RECORD_TOO_LONG , (SSL_ERROR_BASE + 26),
+"SSL attempted to send a record that exceeded the maximum permissible length.")
+
+/*
+ * Received a malformed (too long or short or invalid content) SSL handshake.
+ */
+ER3(SSL_ERROR_RX_MALFORMED_HELLO_REQUEST , (SSL_ERROR_BASE + 27),
+"SSL received a malformed Hello Request handshake message.")
+
+ER3(SSL_ERROR_RX_MALFORMED_CLIENT_HELLO , (SSL_ERROR_BASE + 28),
+"SSL received a malformed Client Hello handshake message.")
+
+ER3(SSL_ERROR_RX_MALFORMED_SERVER_HELLO , (SSL_ERROR_BASE + 29),
+"SSL received a malformed Server Hello handshake message.")
+
+ER3(SSL_ERROR_RX_MALFORMED_CERTIFICATE , (SSL_ERROR_BASE + 30),
+"SSL received a malformed Certificate handshake message.")
+
+ER3(SSL_ERROR_RX_MALFORMED_SERVER_KEY_EXCH , (SSL_ERROR_BASE + 31),
+"SSL received a malformed Server Key Exchange handshake message.")
+
+ER3(SSL_ERROR_RX_MALFORMED_CERT_REQUEST , (SSL_ERROR_BASE + 32),
+"SSL received a malformed Certificate Request handshake message.")
+
+ER3(SSL_ERROR_RX_MALFORMED_HELLO_DONE , (SSL_ERROR_BASE + 33),
+"SSL received a malformed Server Hello Done handshake message.")
+
+ER3(SSL_ERROR_RX_MALFORMED_CERT_VERIFY , (SSL_ERROR_BASE + 34),
+"SSL received a malformed Certificate Verify handshake message.")
+
+ER3(SSL_ERROR_RX_MALFORMED_CLIENT_KEY_EXCH , (SSL_ERROR_BASE + 35),
+"SSL received a malformed Client Key Exchange handshake message.")
+
+ER3(SSL_ERROR_RX_MALFORMED_FINISHED , (SSL_ERROR_BASE + 36),
+"SSL received a malformed Finished handshake message.")
+
+/*
+ * Received a malformed (too long or short) SSL record.
+ */
+ER3(SSL_ERROR_RX_MALFORMED_CHANGE_CIPHER , (SSL_ERROR_BASE + 37),
+"SSL received a malformed Change Cipher Spec record.")
+
+ER3(SSL_ERROR_RX_MALFORMED_ALERT , (SSL_ERROR_BASE + 38),
+"SSL received a malformed Alert record.")
+
+ER3(SSL_ERROR_RX_MALFORMED_HANDSHAKE , (SSL_ERROR_BASE + 39),
+"SSL received a malformed Handshake record.")
+
+ER3(SSL_ERROR_RX_MALFORMED_APPLICATION_DATA , (SSL_ERROR_BASE + 40),
+"SSL received a malformed Application Data record.")
+
+/*
+ * Received an SSL handshake that was inappropriate for the state we're in.
+ * E.g. Server received message from server, or wrong state in state machine.
+ */
+ER3(SSL_ERROR_RX_UNEXPECTED_HELLO_REQUEST , (SSL_ERROR_BASE + 41),
+"SSL received an unexpected Hello Request handshake message.")
+
+ER3(SSL_ERROR_RX_UNEXPECTED_CLIENT_HELLO , (SSL_ERROR_BASE + 42),
+"SSL received an unexpected Client Hello handshake message.")
+
+ER3(SSL_ERROR_RX_UNEXPECTED_SERVER_HELLO , (SSL_ERROR_BASE + 43),
+"SSL received an unexpected Server Hello handshake message.")
+
+ER3(SSL_ERROR_RX_UNEXPECTED_CERTIFICATE , (SSL_ERROR_BASE + 44),
+"SSL received an unexpected Certificate handshake message.")
+
+ER3(SSL_ERROR_RX_UNEXPECTED_SERVER_KEY_EXCH , (SSL_ERROR_BASE + 45),
+"SSL received an unexpected Server Key Exchange handshake message.")
+
+ER3(SSL_ERROR_RX_UNEXPECTED_CERT_REQUEST , (SSL_ERROR_BASE + 46),
+"SSL received an unexpected Certificate Request handshake message.")
+
+ER3(SSL_ERROR_RX_UNEXPECTED_HELLO_DONE , (SSL_ERROR_BASE + 47),
+"SSL received an unexpected Server Hello Done handshake message.")
+
+ER3(SSL_ERROR_RX_UNEXPECTED_CERT_VERIFY , (SSL_ERROR_BASE + 48),
+"SSL received an unexpected Certificate Verify handshake message.")
+
+ER3(SSL_ERROR_RX_UNEXPECTED_CLIENT_KEY_EXCH , (SSL_ERROR_BASE + 49),
+"SSL received an unexpected Cllient Key Exchange handshake message.")
+
+ER3(SSL_ERROR_RX_UNEXPECTED_FINISHED , (SSL_ERROR_BASE + 50),
+"SSL received an unexpected Finished handshake message.")
+
+/*
+ * Received an SSL record that was inappropriate for the state we're in.
+ */
+ER3(SSL_ERROR_RX_UNEXPECTED_CHANGE_CIPHER , (SSL_ERROR_BASE + 51),
+"SSL received an unexpected Change Cipher Spec record.")
+
+ER3(SSL_ERROR_RX_UNEXPECTED_ALERT , (SSL_ERROR_BASE + 52),
+"SSL received an unexpected Alert record.")
+
+ER3(SSL_ERROR_RX_UNEXPECTED_HANDSHAKE , (SSL_ERROR_BASE + 53),
+"SSL received an unexpected Handshake record.")
+
+ER3(SSL_ERROR_RX_UNEXPECTED_APPLICATION_DATA, (SSL_ERROR_BASE + 54),
+"SSL received an unexpected Application Data record.")
+
+/*
+ * Received record/message with unknown discriminant.
+ */
+ER3(SSL_ERROR_RX_UNKNOWN_RECORD_TYPE , (SSL_ERROR_BASE + 55),
+"SSL received a record with an unknown content type.")
+
+ER3(SSL_ERROR_RX_UNKNOWN_HANDSHAKE , (SSL_ERROR_BASE + 56),
+"SSL received a handshake message with an unknown message type.")
+
+ER3(SSL_ERROR_RX_UNKNOWN_ALERT , (SSL_ERROR_BASE + 57),
+"SSL received an alert record with an unknown alert description.")
+
+/*
+ * Received an alert reporting what we did wrong. (more alerts above)
+ */
+ER3(SSL_ERROR_CLOSE_NOTIFY_ALERT , (SSL_ERROR_BASE + 58),
+"SSL peer has closed this connection.")
+
+ER3(SSL_ERROR_HANDSHAKE_UNEXPECTED_ALERT , (SSL_ERROR_BASE + 59),
+"SSL peer was not expecting a handshake message it received.")
+
+ER3(SSL_ERROR_DECOMPRESSION_FAILURE_ALERT , (SSL_ERROR_BASE + 60),
+"SSL peer was unable to succesfully decompress an SSL record it received.")
+
+ER3(SSL_ERROR_HANDSHAKE_FAILURE_ALERT , (SSL_ERROR_BASE + 61),
+"SSL peer was unable to negotiate an acceptable set of security parameters.")
+
+ER3(SSL_ERROR_ILLEGAL_PARAMETER_ALERT , (SSL_ERROR_BASE + 62),
+"SSL peer rejected a handshake message for unacceptable content.")
+
+ER3(SSL_ERROR_UNSUPPORTED_CERT_ALERT , (SSL_ERROR_BASE + 63),
+"SSL peer does not support certificates of the type it received.")
+
+ER3(SSL_ERROR_CERTIFICATE_UNKNOWN_ALERT , (SSL_ERROR_BASE + 64),
+"SSL peer had some unspecified issue with the certificate it received.")
+
+
+ER3(SSL_ERROR_GENERATE_RANDOM_FAILURE , (SSL_ERROR_BASE + 65),
+"SSL experienced a failure of its random number generator.")
+
+ER3(SSL_ERROR_SIGN_HASHES_FAILURE , (SSL_ERROR_BASE + 66),
+"Unable to digitally sign data required to verify your certificate.")
+
+ER3(SSL_ERROR_EXTRACT_PUBLIC_KEY_FAILURE , (SSL_ERROR_BASE + 67),
+"SSL was unable to extract the public key from the peer's certificate.")
+
+ER3(SSL_ERROR_SERVER_KEY_EXCHANGE_FAILURE , (SSL_ERROR_BASE + 68),
+"Unspecified failure while processing SSL Server Key Exchange handshake.")
+
+ER3(SSL_ERROR_CLIENT_KEY_EXCHANGE_FAILURE , (SSL_ERROR_BASE + 69),
+"Unspecified failure while processing SSL Client Key Exchange handshake.")
+
+ER3(SSL_ERROR_ENCRYPTION_FAILURE , (SSL_ERROR_BASE + 70),
+"Bulk data encryption algorithm failed in selected cipher suite.")
+
+ER3(SSL_ERROR_DECRYPTION_FAILURE , (SSL_ERROR_BASE + 71),
+"Bulk data decryption algorithm failed in selected cipher suite.")
+
+ER3(SSL_ERROR_SOCKET_WRITE_FAILURE , (SSL_ERROR_BASE + 72),
+"Attempt to write encrypted data to underlying socket failed.")
+
+ER3(SSL_ERROR_MD5_DIGEST_FAILURE , (SSL_ERROR_BASE + 73),
+"MD5 digest function failed.")
+
+ER3(SSL_ERROR_SHA_DIGEST_FAILURE , (SSL_ERROR_BASE + 74),
+"SHA-1 digest function failed.")
+
+ER3(SSL_ERROR_MAC_COMPUTATION_FAILURE , (SSL_ERROR_BASE + 75),
+"MAC computation failed.")
+
+ER3(SSL_ERROR_SYM_KEY_CONTEXT_FAILURE , (SSL_ERROR_BASE + 76),
+"Failure to create Symmetric Key context.")
+
+ER3(SSL_ERROR_SYM_KEY_UNWRAP_FAILURE , (SSL_ERROR_BASE + 77),
+"Failure to unwrap the Symmetric key in Client Key Exchange message.")
+
+ER3(SSL_ERROR_PUB_KEY_SIZE_LIMIT_EXCEEDED , (SSL_ERROR_BASE + 78),
+"SSL Server attempted to use domestic-grade public key with export cipher suite.")
+
+ER3(SSL_ERROR_IV_PARAM_FAILURE , (SSL_ERROR_BASE + 79),
+"PKCS11 code failed to translate an IV into a param.")
+
+ER3(SSL_ERROR_INIT_CIPHER_SUITE_FAILURE , (SSL_ERROR_BASE + 80),
+"Failed to initialize the selected cipher suite.")
+
+ER3(SSL_ERROR_SESSION_KEY_GEN_FAILURE , (SSL_ERROR_BASE + 81),
+"Client failed to generate session keys for SSL session.")
+
+ER3(SSL_ERROR_NO_SERVER_KEY_FOR_ALG , (SSL_ERROR_BASE + 82),
+"Server has no key for the attempted key exchange algorithm.")
+
+ER3(SSL_ERROR_TOKEN_INSERTION_REMOVAL , (SSL_ERROR_BASE + 83),
+"PKCS#11 token was inserted or removed while operation was in progress.")
+
+ER3(SSL_ERROR_TOKEN_SLOT_NOT_FOUND , (SSL_ERROR_BASE + 84),
+"No PKCS#11 token could be found to do a required operation.")
+
+ER3(SSL_ERROR_NO_COMPRESSION_OVERLAP , (SSL_ERROR_BASE + 85),
+"Cannot communicate securely with peer: no common compression algorithm(s).")
+
+ER3(SSL_ERROR_HANDSHAKE_NOT_COMPLETED , (SSL_ERROR_BASE + 86),
+"Cannot initiate another SSL handshake until current handshake is complete.")
+
+ER3(SSL_ERROR_BAD_HANDSHAKE_HASH_VALUE , (SSL_ERROR_BASE + 87),
+"Received incorrect handshakes hash values from peer.")
+
+ER3(SSL_ERROR_CERT_KEA_MISMATCH , (SSL_ERROR_BASE + 88),
+"The certificate provided cannot be used with the selected key exchange algorithm.")
+
+ER3(SSL_ERROR_NO_TRUSTED_SSL_CLIENT_CA , (SSL_ERROR_BASE + 89),
+"No certificate authority is trusted for SSL client authentication.")
+
+ER3(SSL_ERROR_SESSION_NOT_FOUND , (SSL_ERROR_BASE + 90),
+"Client's SSL session ID not found in server's session cache.")
+
+ER3(SSL_ERROR_DECRYPTION_FAILED_ALERT , (SSL_ERROR_BASE + 91),
+"Peer was unable to decrypt an SSL record it received.")
+
+ER3(SSL_ERROR_RECORD_OVERFLOW_ALERT , (SSL_ERROR_BASE + 92),
+"Peer received an SSL record that was longer than is permitted.")
+
+ER3(SSL_ERROR_UNKNOWN_CA_ALERT , (SSL_ERROR_BASE + 93),
+"Peer does not recognize and trust the CA that issued your certificate.")
+
+ER3(SSL_ERROR_ACCESS_DENIED_ALERT , (SSL_ERROR_BASE + 94),
+"Peer received a valid certificate, but access was denied.")
+
+ER3(SSL_ERROR_DECODE_ERROR_ALERT , (SSL_ERROR_BASE + 95),
+"Peer could not decode an SSL handshake message.")
+
+ER3(SSL_ERROR_DECRYPT_ERROR_ALERT , (SSL_ERROR_BASE + 96),
+"Peer reports failure of signature verification or key exchange.")
+
+ER3(SSL_ERROR_EXPORT_RESTRICTION_ALERT , (SSL_ERROR_BASE + 97),
+"Peer reports negotiation not in compliance with export regulations.")
+
+ER3(SSL_ERROR_PROTOCOL_VERSION_ALERT , (SSL_ERROR_BASE + 98),
+"Peer reports incompatible or unsupported protocol version.")
+
+ER3(SSL_ERROR_INSUFFICIENT_SECURITY_ALERT , (SSL_ERROR_BASE + 99),
+"Server requires ciphers more secure than those supported by client.")
+
+ER3(SSL_ERROR_INTERNAL_ERROR_ALERT , (SSL_ERROR_BASE + 100),
+"Peer reports it experienced an internal error.")
+
+ER3(SSL_ERROR_USER_CANCELED_ALERT , (SSL_ERROR_BASE + 101),
+"Peer user canceled handshake.")
+
+ER3(SSL_ERROR_NO_RENEGOTIATION_ALERT , (SSL_ERROR_BASE + 102),
+"Peer does not permit renegotiation of SSL security parameters.")
+
diff --git a/ldap/servers/slapd/start_tls_extop.c b/ldap/servers/slapd/start_tls_extop.c
new file mode 100644
index 00000000..523805f0
--- /dev/null
+++ b/ldap/servers/slapd/start_tls_extop.c
@@ -0,0 +1,479 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+/*
+ * Start TLS - LDAP Extended Operation.
+ *
+ *
+ * This plugin implements the "Start TLS (Transport Layer Security)"
+ * extended operation for LDAP. The plugin function is called by
+ * the server if an LDAP client request contains the OID:
+ * "1.3.6.1.4.1.1466.20037".
+ *
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <private/pprio.h>
+
+
+#include <prio.h>
+#include <ssl.h>
+#include "slap.h"
+#include "slapi-plugin.h"
+#include "fe.h"
+
+
+
+
+/* OID of the extended operation handled by this plug-in */
+/* #define START_TLS_OID "1.3.6.1.4.1.1466.20037" */
+
+
+Slapi_PluginDesc exopdesc = { "start_tls_plugin", "Netscape", "0.1",
+ "Start TLS extended operation plugin" };
+
+
+
+
+/* Start TLS Extended operation plugin function */
+int
+start_tls( Slapi_PBlock *pb )
+{
+
+ char *oid;
+ Connection *conn;
+ PRFileDesc *oldsocket, *newsocket;
+ int secure;
+ int ns, oldnativesocket;
+ int rv;
+
+ /* Get the pb ready for sending Start TLS Extended Responses back to the client.
+ * The only requirement is to set the LDAP OID of the extended response to the START_TLS_OID. */
+
+ if ( slapi_pblock_set( pb, SLAPI_EXT_OP_RET_OID, START_TLS_OID ) != 0 ) {
+ slapi_log_error( SLAPI_LOG_PLUGIN, "start_tls",
+ "Could not set extended response oid.\n" );
+ slapi_send_ldap_result( pb, LDAP_OPERATIONS_ERROR, NULL,
+ "Could not set extended response oid.", 0, NULL );
+ return( SLAPI_PLUGIN_EXTENDED_SENT_RESULT );
+ }
+
+
+ /* Before going any further, we'll make sure that the right extended operation plugin
+ * has been called: i.e., the OID shipped whithin the extended operation request must
+ * match this very plugin's OID: START_TLS_OID. */
+
+ if ( slapi_pblock_get( pb, SLAPI_EXT_OP_REQ_OID, &oid ) != 0 ) {
+ slapi_log_error( SLAPI_LOG_PLUGIN, "start_tls",
+ "Could not get OID value from request.\n" );
+ slapi_send_ldap_result( pb, LDAP_OPERATIONS_ERROR, NULL,
+ "Could not get OID value from request.", 0, NULL );
+ return( SLAPI_PLUGIN_EXTENDED_SENT_RESULT );
+ } else {
+ slapi_log_error( SLAPI_LOG_PLUGIN, "start_tls",
+ "Received extended operation request with OID %s\n", oid );
+ }
+
+ if ( strcasecmp( oid, START_TLS_OID ) != 0) {
+ slapi_log_error( SLAPI_LOG_PLUGIN, "start_tls",
+ "Request OID does not match Start TLS OID.\n" );
+ slapi_send_ldap_result( pb, LDAP_OPERATIONS_ERROR, NULL,
+ "Request OID does not match Start TLS OID.", 0, NULL );
+ return( SLAPI_PLUGIN_EXTENDED_SENT_RESULT );
+ } else {
+ slapi_log_error( SLAPI_LOG_PLUGIN, "start_tls",
+ "Start TLS extended operation request confirmed.\n" );
+ }
+
+
+ /* At least we know that the request was indeed an Start TLS one. */
+
+ conn = pb->pb_conn;
+ PR_Lock( conn->c_mutex );
+#ifndef _WIN32
+ oldsocket = conn->c_prfd;
+ if ( oldsocket == (PRFileDesc *) NULL ) {
+ slapi_log_error( SLAPI_LOG_PLUGIN, "start_tls",
+ "Connection socket not available.\n" );
+ slapi_send_ldap_result( pb, LDAP_UNAVAILABLE, NULL,
+ "Connection socket not available.", 0, NULL );
+ goto unlock_and_return;
+ }
+#else
+ oldnativesocket = conn->c_sd;
+ oldsocket = PR_ImportTCPSocket(oldnativesocket);
+ if ( oldsocket == (PRFileDesc *) NULL ) {
+ slapi_log_error( SLAPI_LOG_PLUGIN, "start_tls",
+ "Failed to import NT native socket into NSPR.\n" );
+ slapi_send_ldap_result( pb, LDAP_UNAVAILABLE, NULL,
+ "Failed to import NT native socket into NSPR.", 0, NULL );
+ goto unlock_and_return;
+ }
+#endif
+
+ /* Check whether the Start TLS request can be accepted. */
+
+ if ( connection_operations_pending( conn, pb->pb_op,
+ 1 /* check for ops where result not yet sent */ )) {
+ slapi_log_error( SLAPI_LOG_PLUGIN, "start_tls",
+ "Other operations are still pending on the connection.\n" );
+ slapi_send_ldap_result( pb, LDAP_OPERATIONS_ERROR, NULL,
+ "Other operations are still pending on the connection.", 0, NULL );
+ goto unlock_and_return;
+ }
+
+
+ if ( !config_get_security() ) {
+ /* if any, here is where the referral to another SSL supporting server should be done: */
+ /* slapi_send_ldap_result( pb, LDAP_REFERRAL, NULL, msg, 0, url ); */
+ slapi_log_error( SLAPI_LOG_PLUGIN, "start_tls",
+ "SSL not supported by this server.\n" );
+ slapi_send_ldap_result( pb, LDAP_PROTOCOL_ERROR, NULL,
+ "SSL not supported by this server.", 0, NULL );
+ goto unlock_and_return;
+ }
+
+
+ if ( conn->c_flags & CONN_FLAG_SSL ) {
+ slapi_log_error( SLAPI_LOG_PLUGIN, "start_tls",
+ "SSL connection already established.\n" );
+ slapi_send_ldap_result( pb, LDAP_OPERATIONS_ERROR, NULL,
+ "SSL connection already established.", 0, NULL );
+ goto unlock_and_return;
+ }
+
+ if ( conn->c_flags & CONN_FLAG_SASL_CONTINUE ) {
+ slapi_log_error( SLAPI_LOG_PLUGIN, "start_tls",
+ "SASL multi-stage bind in progress.\n" );
+ slapi_send_ldap_result( pb, LDAP_OPERATIONS_ERROR, NULL,
+ "SASL multi-stage bind in progress.", 0, NULL );
+ goto unlock_and_return;
+ }
+
+
+ if ( conn->c_flags & CONN_FLAG_CLOSING ) {
+ slapi_log_error( SLAPI_LOG_PLUGIN, "start_tls",
+ "Connection being closed at this moment.\n" );
+ slapi_send_ldap_result( pb, LDAP_UNAVAILABLE, NULL,
+ "Connection being closed at this moment.", 0, NULL );
+ goto unlock_and_return;
+ }
+
+
+
+ /* At first sight, there doesn't seem to be any major impediment to start TLS.
+ * So, we may as well try initialising SSL. */
+
+ if ( slapd_security_library_is_initialized() == 0 ) {
+ slapi_log_error( SLAPI_LOG_PLUGIN, "start_tls",
+ "NSS libraries not initialised.\n" );
+ slapi_send_ldap_result( pb, LDAP_UNAVAILABLE, NULL,
+ "NSS libraries not initialised.", 0, NULL );
+ goto unlock_and_return;
+ }
+
+
+
+ /* Since no specific argument for denying the Start TLS request has been found,
+ * we send a success response back to the client. */
+
+ slapi_log_error( SLAPI_LOG_PLUGIN, "start_tls",
+ "Start TLS request accepted.Server willing to negotiate SSL.\n" );
+ slapi_send_ldap_result( pb, LDAP_SUCCESS, NULL,
+ "Start TLS request accepted.Server willing to negotiate SSL.", 0, NULL );
+
+
+ /* So far we have set up the environment for deploying SSL. It's now time to import the socket
+ * into SSL and to configure it consequently. */
+
+ if ( slapd_ssl_listener_is_initialized() != 0 ) {
+ PRFileDesc * ssl_listensocket;
+
+ ssl_listensocket = get_ssl_listener_fd();
+ if ( ssl_listensocket == (PRFileDesc *) NULL ) {
+ slapi_log_error( SLAPI_LOG_PLUGIN, "start_tls",
+ "SSL listener socket not found.\n" );
+ slapi_send_ldap_result( pb, LDAP_UNAVAILABLE, NULL,
+ "SSL listener socket not found.", 0, NULL );
+ goto unlock_and_return;
+ }
+ newsocket = slapd_ssl_importFD( ssl_listensocket, oldsocket );
+ if ( newsocket == (PRFileDesc *) NULL ) {
+ slapi_log_error( SLAPI_LOG_PLUGIN, "start_tls",
+ "SSL socket import failed.\n" );
+ slapi_send_ldap_result( pb, LDAP_UNAVAILABLE, NULL,
+ "SSL socket import failed.", 0, NULL );
+ goto unlock_and_return;
+ }
+ } else {
+ if ( slapd_ssl_init2( &oldsocket, 1 ) != 0 ) {
+ slapi_log_error( SLAPI_LOG_PLUGIN, "start_tls",
+ "SSL socket import or configuration failed.\n" );
+ slapi_send_ldap_result( pb, LDAP_UNAVAILABLE, NULL,
+ "SSL socket import or configuration failed.", 0, NULL );
+ goto unlock_and_return;
+ }
+ newsocket = oldsocket;
+ }
+
+
+ rv = slapd_ssl_resetHandshake( newsocket, 1 );
+ if ( rv != SECSuccess ) {
+ slapi_log_error( SLAPI_LOG_PLUGIN, "start_tls",
+ "Unable to set socket ready for SSL handshake.\n" );
+ slapi_send_ldap_result( pb, LDAP_UNAVAILABLE, NULL,
+ "Unable to set socket ready for SSL handshake.", 0, NULL );
+ goto unlock_and_return;
+ }
+
+
+
+ /* From here on, messages will be sent through the SSL layer, so we need to get our
+ * connection ready. */
+
+ secure = 1;
+ ns = configure_pr_socket( &newsocket, secure );
+
+ /*
+ ber_sockbuf_set_option( conn->c_sb, LBER_SOCKBUF_OPT_DESC, &newsocket );
+ ber_sockbuf_set_option( conn->c_sb, LBER_SOCKBUF_OPT_READ_FN, (void *)secure_read_function );
+ ber_sockbuf_set_option( conn->c_sb, LBER_SOCKBUF_OPT_WRITE_FN, (void *)secure_write_function );
+ */
+
+ /*changed to */
+ {
+ struct lber_x_ext_io_fns *func_pointers = malloc(LBER_X_EXTIO_FNS_SIZE);
+ func_pointers->lbextiofn_size = LBER_X_EXTIO_FNS_SIZE;
+ func_pointers->lbextiofn_read = secure_read_function;
+ func_pointers->lbextiofn_write = secure_write_function;
+ func_pointers->lbextiofn_writev = NULL;
+ func_pointers->lbextiofn_socket_arg = (struct lextiof_socket_private *) newsocket;
+ ber_sockbuf_set_option( conn->c_sb,
+ LBER_SOCKBUF_OPT_EXT_IO_FNS, func_pointers);
+ free(func_pointers);
+ }
+ conn->c_flags |= CONN_FLAG_SSL;
+ conn->c_flags |= CONN_FLAG_START_TLS;
+ conn->c_sd = ns;
+ conn->c_prfd = newsocket;
+
+
+ rv = slapd_ssl_handshakeCallback (conn->c_prfd, (void *)handle_handshake_done, conn);
+
+ if ( rv < 0 ) {
+ PRErrorCode prerr = PR_GetError();
+ slapi_log_error( SLAPI_LOG_FATAL, "start_tls",
+ "SSL_HandshakeCallback() %d " SLAPI_COMPONENT_NAME_NSPR " error %d (%s)\n",
+ rv, prerr, slapd_pr_strerror( prerr ) );
+ }
+
+ if ( config_get_SSLclientAuth() != SLAPD_SSLCLIENTAUTH_OFF ) {
+ rv = slapd_ssl_badCertHook (conn->c_prfd, (void *)handle_bad_certificate, conn);
+ if ( rv < 0 ) {
+ PRErrorCode prerr = PR_GetError();
+ slapi_log_error( SLAPI_LOG_FATAL, "start_tls",
+ "SSL_BadCertHook(%i) %i " SLAPI_COMPONENT_NAME_NSPR " error %d (%s)\n",
+ conn->c_sd, rv, prerr, slapd_pr_strerror( prerr ) );
+ }
+ }
+
+
+ /* Once agreed in starting TLS, the handshake must be carried out. */
+
+ slapi_log_error( SLAPI_LOG_PLUGIN, "start_tls",
+ "Starting SSL Handshake.\n" );
+
+ unlock_and_return:
+ PR_Unlock( conn->c_mutex );
+
+ return( SLAPI_PLUGIN_EXTENDED_SENT_RESULT );
+
+}/* start_tls */
+
+
+/* TLS Graceful Closure function.
+ * The function below kind of "resets" the connection to its state previous
+ * to receiving the Start TLS operation request. But it also sets the
+ * authorization and authentication credentials to "anonymous". Finally,
+ * it sends a closure alert message to the client.
+ *
+ * Note: start_tls_graceful_closure() must be called with c->c_mutex locked.
+ */
+int
+start_tls_graceful_closure( Connection *c, Slapi_PBlock * pb, int is_initiator )
+{
+ int ns;
+ Slapi_PBlock *pblock = pb;
+ struct slapdplugin *plugin;
+ int secure = 0;
+ PRFileDesc *ssl_fd;
+
+ if ( pblock == NULL ) {
+ pblock = slapi_pblock_new();
+ plugin = (struct slapdplugin *) slapi_ch_calloc( 1, sizeof( struct slapdplugin ) );
+ pblock->pb_plugin = plugin;
+ pblock->pb_conn = c;
+ pblock->pb_op = c->c_ops;
+ set_db_default_result_handlers( pblock );
+ if ( slapi_pblock_set( pblock, SLAPI_EXT_OP_RET_OID, START_TLS_OID ) != 0 ) {
+ slapi_log_error( SLAPI_LOG_PLUGIN, "start_tls",
+ "Could not set extended response oid.\n" );
+ slapi_send_ldap_result( pblock, LDAP_OPERATIONS_ERROR, NULL,
+ "Could not set extended response oid.", 0, NULL );
+ return( SLAPI_PLUGIN_EXTENDED_SENT_RESULT );
+ }
+ slapi_ch_free( (void **) &plugin );
+ }
+
+ /* First thing to do is to finish with whatever operation may be hanging on the
+ * encrypted session.
+ */
+
+ while ( connection_operations_pending( c, pblock->pb_op,
+ 0 /* wait for all other ops to full complete */ )) {
+ slapi_log_error( SLAPI_LOG_PLUGIN, "start_tls",
+ "Still %d operations to be completed before closing the SSL connection.\n",
+ c->c_refcnt - 1 );
+ }
+
+ slapi_log_error( SLAPI_LOG_PLUGIN, "start_tls_graceful_closure", "SSL_CLOSE_NOTIFY_ALERT\n" );
+
+ /* An SSL close_notify alert should be sent to the client. However, the NSS API
+ * doesn't provide us with anything alike.
+ */
+ slapi_send_ldap_result( pblock, LDAP_OPERATIONS_ERROR, NULL,
+ "SSL_CLOSE_NOTIFY_ALERT", 0, NULL );
+
+
+ if ( is_initiator ) {
+ /* if this call belongs to the initiator of the SSL connection closure, it must first
+ * wait for the peer to send another close_notify alert back.
+ */
+ }
+
+
+ PR_Lock( c->c_mutex );
+
+ /* "Unimport" the socket from SSL, i.e. get rid of the upper layer of the
+ * file descriptor stack, which represents SSL.
+ * The ssl socket assigned to c->c_prfd should also be closed and destroyed.
+ * Should find a way of getting that ssl socket.
+ */
+ /*
+ rc = strcasecmp( "SSL", PR_GetNameForIdentity( c->c_prfd->identity ) );
+ if ( rc == 0 ) {
+ sslSocket * ssl_socket;
+
+ ssl_socket = (sslSocket *) c->c_prfd->secret;
+ ssl_socket->fd = NULL;
+ }
+ */
+ ssl_fd = PR_PopIOLayer( c->c_prfd, PR_TOP_IO_LAYER );
+ ssl_fd->dtor( ssl_fd );
+
+
+#ifndef _WIN32
+ secure = 0;
+ ns = configure_pr_socket( &(c->c_prfd), secure );
+
+ ber_sockbuf_set_option( c->c_sb, LBER_SOCKBUF_OPT_DESC, &(c->c_prfd) );
+
+#else
+ ns = PR_FileDesc2NativeHandle( c->c_prfd );
+ c->c_prfd = NULL;
+
+ configure_ns_socket( &ns );
+
+ ber_sockbuf_set_option( c->c_sb, LBER_SOCKBUF_OPT_DESC, &ns );
+
+#endif
+
+ c->c_sd = ns;
+ c->c_flags &= ~CONN_FLAG_SSL;
+ c->c_flags &= ~CONN_FLAG_START_TLS;
+
+ ber_sockbuf_set_option( c->c_sb, LBER_SOCKBUF_OPT_READ_FN, (void *)read_function );
+ ber_sockbuf_set_option( c->c_sb, LBER_SOCKBUF_OPT_WRITE_FN, (void *)write_function );
+
+
+ /* authentication & authorization credentials must be set to "anonymous". */
+
+ bind_credentials_clear( c, PR_FALSE, PR_TRUE );
+
+ PR_Unlock( c->c_mutex );
+
+
+
+ return ( SLAPI_PLUGIN_EXTENDED_SENT_RESULT );
+}
+
+
+static char *start_tls_oid_list[] = {
+ START_TLS_OID,
+ NULL
+};
+static char *start_tls_name_list[] = {
+ "startTLS",
+ NULL
+};
+
+int start_tls_register_plugin()
+{
+ slapi_register_plugin( "extendedop", 1 /* Enabled */, "start_tls_init",
+ start_tls_init, "Start TLS extended operation",
+ start_tls_oid_list, NULL );
+
+ return 0;
+}
+
+
+/* Initialization function */
+
+int start_tls_init( Slapi_PBlock *pb )
+{
+ char **argv;
+ char *oid;
+
+ /* Get the arguments appended to the plugin extendedop directive. The first argument
+ * (after the standard arguments for the directive) should contain the OID of the
+ * extended operation.
+ */
+
+ if ( slapi_pblock_get( pb, SLAPI_PLUGIN_ARGV, &argv ) != 0 ) {
+ slapi_log_error( SLAPI_LOG_PLUGIN, "start_tls_init", "Could not get argv\n" );
+ return( -1 );
+ }
+
+ /* Compare the OID specified in the configuration file against the Start TLS OID. */
+
+ if ( argv == NULL || strcmp( argv[0], START_TLS_OID ) != 0 ) {
+ slapi_log_error( SLAPI_LOG_PLUGIN, "start_tls_init",
+ "OID is missing or is not %s\n", START_TLS_OID );
+ return( -1 );
+ } else {
+ oid = slapi_ch_strdup( argv[0] );
+ slapi_log_error( SLAPI_LOG_PLUGIN, "start_tls_init",
+ "Registering plug-in for Start TLS extended op %s.\n", oid );
+ }
+
+ /* Register the plug-in function as an extended operation
+ * plug-in function that handles the operation identified by
+ * OID 1.3.6.1.4.1.1466.20037. Also specify the version of the server
+ * plug-in */
+ if ( slapi_pblock_set( pb, SLAPI_PLUGIN_VERSION, SLAPI_PLUGIN_VERSION_01 ) != 0 ||
+ slapi_pblock_set( pb, SLAPI_PLUGIN_DESCRIPTION, (void *)&exopdesc ) != 0 ||
+ slapi_pblock_set( pb, SLAPI_PLUGIN_EXT_OP_FN, (void *) start_tls ) != 0 ||
+ slapi_pblock_set( pb, SLAPI_PLUGIN_EXT_OP_OIDLIST, start_tls_oid_list ) != 0 ||
+ slapi_pblock_set( pb, SLAPI_PLUGIN_EXT_OP_NAMELIST, start_tls_name_list ) != 0 ) {
+
+ slapi_log_error( SLAPI_LOG_PLUGIN, "start_tls_init",
+ "Failed to set plug-in version, function, and OID.\n" );
+ return( -1 );
+ }
+
+ return( 0 );
+}
+
diff --git a/ldap/servers/slapd/statechange.h b/ldap/servers/slapd/statechange.h
new file mode 100644
index 00000000..70ec3ab8
--- /dev/null
+++ b/ldap/servers/slapd/statechange.h
@@ -0,0 +1,47 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+
+#ifndef _STATE_NOTIFY_H_
+#define _STATE_NOTIFY_H_
+
+/* mechanics */
+
+typedef void (*notify_callback)(Slapi_Entry *e, char *dn, int modtype, Slapi_PBlock *pb, void *caller_data);
+typedef void (*caller_data_free_callback)(void *caller_data);
+
+typedef int (*api_statechange_register)(char *caller_id, char *dn, char *filter, void *caller_data, notify_callback func);
+/* returns pointer to caller data passed to api_statechange_register */
+typedef void *(*api_statechange_unregister)(char *dn, char *filter, notify_callback func);
+typedef void (*api_statechange_unregister_all)(char *caller_id, caller_data_free_callback callback);
+
+/* API ID for slapi_apib_get_interface */
+
+#define StateChange_v1_0_GUID "0A340151-6FB3-11d3-80D2-006008A6EFF3"
+
+/* API */
+
+/* the api broker reserves api[0] for its use */
+
+#define statechange_register(api, caller_id, dn, filter, caller, func) \
+ ((api_statechange_register*)(api))[1]( caller_id, dn, filter, caller, func)
+
+#define statechange_unregister(api, dn, filter, func) \
+ ((api_statechange_unregister*)(api))[2]( dn, filter, func)
+
+#define statechange_unregister_all(api, caller_id, callback) \
+ ((api_statechange_unregister*)(api))[3](caller_id, callback)
+
+/* Vattr state change handler to be passed to statechange_register() by va sps*/
+#define statechange_vattr_cache_invalidator_callback(api) api[4]
+
+#define STATECHANGE_VATTR_GLOBAL_INVALIDATE 1
+#define STATECHANGE_VATTR_ENTRY_INVALIDATE 2
+
+/* Vattr api caller data to be passed to statechange_register() */
+static int vattr_global_invalidate = STATECHANGE_VATTR_GLOBAL_INVALIDATE;
+static int vattr_entry_invalidate = STATECHANGE_VATTR_ENTRY_INVALIDATE;
+
+#endif /*_STATE_NOTIFY_H_*/
diff --git a/ldap/servers/slapd/str2filter.c b/ldap/servers/slapd/str2filter.c
new file mode 100644
index 00000000..c3cb2e31
--- /dev/null
+++ b/ldap/servers/slapd/str2filter.c
@@ -0,0 +1,380 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+/* slapi_str2filter.c - parse an rfc 1588 string filter */
+
+#include <stdio.h>
+#include <string.h>
+#include <sys/types.h>
+#ifndef _WIN32
+#include <sys/socket.h>
+#endif
+#include "slap.h"
+
+static struct slapi_filter *str2list();
+static int str2subvals();
+
+struct slapi_filter *
+slapi_str2filter( char *str )
+{
+ struct slapi_filter *f = NULL;
+ char *end;
+
+ LDAPDebug( LDAP_DEBUG_FILTER, "slapi_str2filter \"%s\"\n", str, 0, 0 );
+
+ if ( str == NULL || *str == '\0' ) {
+ return( NULL );
+ }
+
+ switch ( *str ) {
+ case '(':
+ if ( (end = slapi_find_matching_paren( str )) == NULL ) {
+ slapi_filter_free( f, 1 );
+ return( NULL );
+ }
+ *end = '\0';
+
+ str++;
+ switch ( *str ) {
+ case '&':
+ LDAPDebug( LDAP_DEBUG_FILTER, "slapi_str2filter: AND\n",
+ 0, 0, 0 );
+
+ str++;
+ f = str2list( str, LDAP_FILTER_AND );
+ break;
+
+ case '|':
+ LDAPDebug( LDAP_DEBUG_FILTER, "put_filter: OR\n",
+ 0, 0, 0 );
+
+ str++;
+ f = str2list( str, LDAP_FILTER_OR );
+ break;
+
+ case '!':
+ LDAPDebug( LDAP_DEBUG_FILTER, "put_filter: NOT\n",
+ 0, 0, 0 );
+
+ str++;
+ f = str2list( str, LDAP_FILTER_NOT );
+ break;
+
+ default:
+ LDAPDebug( LDAP_DEBUG_FILTER, "slapi_str2filter: simple\n",
+ 0, 0, 0 );
+
+ f = str2simple( str , 1 /* unescape_filter */);
+ break;
+ }
+ *end = ')';
+ break;
+
+ default: /* assume it's a simple type=value filter */
+ LDAPDebug( LDAP_DEBUG_FILTER, "slapi_str2filter: default\n", 0, 0,
+ 0 );
+
+ f = str2simple( str , 1 /* unescape_filter */);
+ break;
+ }
+
+ return( f );
+}
+
+/*
+ * Put a list of filters like this "(filter1)(filter2)..."
+ */
+
+static struct slapi_filter *
+str2list( char *str, unsigned long ftype )
+{
+ struct slapi_filter *f;
+ struct slapi_filter **fp;
+ char *next;
+ char save;
+
+ LDAPDebug( LDAP_DEBUG_FILTER, "str2list \"%s\"\n", str, 0, 0 );
+
+ f = (struct slapi_filter *) slapi_ch_calloc( 1, sizeof(struct slapi_filter) );
+ f->f_choice = ftype;
+ fp = &f->f_list;
+
+ while ( *str ) {
+ while ( *str && ldap_utf8isspace( str ) )
+ LDAP_UTF8INC( str );
+ if ( *str == '\0' )
+ break;
+
+ if ( (next = slapi_find_matching_paren( str )) == NULL ) {
+ slapi_filter_free( f, 1 );
+ return( NULL );
+ }
+ save = *++next;
+ *next = '\0';
+
+ /* now we have "(filter)" with str pointing to it */
+ if ( (*fp = slapi_str2filter( str )) == NULL ) {
+ slapi_filter_free( f, 1 );
+ *next = save;
+ return( NULL );
+ }
+ *next = save;
+
+ str = next;
+ f->f_flags |= ((*fp)->f_flags & SLAPI_FILTER_LDAPSUBENTRY);
+ f->f_flags |= ((*fp)->f_flags & SLAPI_FILTER_TOMBSTONE);
+ fp = &(*fp)->f_next;
+ }
+ *fp = NULL;
+ filter_compute_hash(f);
+
+ return( f );
+}
+
+static char*
+str_find_star (char* s)
+ /* Like strchr(s, '*'), except ignore "\*" */
+{
+ char* r;
+ if (s == NULL) return s;
+ r = strchr (s, '*');
+ if (r != s) while (r != NULL && r[-1] == '\\') {
+ r = strchr (r+1, '*');
+ }
+ return r;
+}
+
+
+/*
+ * unescape a string into another buffer -- note that an unescaped ldap
+ * string may contain nulls if 'binary' is set! this is sort of a mix
+ * between the LDAP SDK version and terry hayes' version. optimally it
+ * would be nice if the LDAP SDK exported something like this.
+ *
+ * if 'binary' is set, "\00" is allowed, otherwise it's not.
+ *
+ * returns: 0 on error, 1 on success
+ * (*outlen) is the actual length of the unescaped string
+ */
+static int
+filt_unescape_str(const char *instr, char *outstr, size_t outsize, size_t* outlen, int binary)
+{
+ const char *inp;
+ char *outp;
+ int ival;
+ *outlen = 0;
+
+ if (!outstr) return -1;
+ for (inp = instr, outp = outstr; *inp; inp++)
+ {
+ if (! outsize)
+ return 0; /* fail */
+ if (*inp == '\\')
+ {
+ if (! *(++inp))
+ return 0; /* fail */
+ if (((ival = hexchar2int(inp[0])) < 0) || (hexchar2int(inp[1]) < 0))
+ {
+ /* LDAPv2 (RFC1960) escape sequence */
+ *outp++ = *inp;
+ (*outlen)++, outsize--;
+ }
+ else
+ {
+ /* LDAPv3 hex escape sequence */
+ if (! *(++inp))
+ return 0; /* fail */
+ *outp = (ival << 4) | hexchar2int(*inp);
+ if ((!binary) && (!*outp))
+ return 0; /* fail: "\00" not allowed unless it's binary */
+ outp++;
+ (*outlen)++, outsize--;
+ }
+ }
+ else
+ {
+ *outp++ = *inp;
+ (*outlen)++, outsize--;
+ }
+ }
+ return 1; /* ok */
+}
+
+
+/*
+ * The caller unescapes it if unescape_filter == 0.
+ */
+struct slapi_filter *
+str2simple( char *str , int unescape_filter)
+{
+ struct slapi_filter *f;
+ char *s;
+ char *value, savechar;
+
+ LDAPDebug( LDAP_DEBUG_FILTER, "str2simple \"%s\"\n", str, 0, 0 );
+
+ if ( (s = strchr( str, '=' )) == NULL ) {
+ return( NULL );
+ }
+ value = s;
+ LDAP_UTF8INC(value);
+ LDAP_UTF8DEC(s);
+
+ f = (struct slapi_filter *) slapi_ch_calloc( 1, sizeof(struct slapi_filter) );
+
+ switch ( *s ) {
+ case '<':
+ f->f_choice = LDAP_FILTER_LE;
+ break;
+ case '>':
+ f->f_choice = LDAP_FILTER_GE;
+ break;
+ case '~':
+ f->f_choice = LDAP_FILTER_APPROX;
+ break;
+ default:
+ LDAP_UTF8INC(s);
+ if ( str_find_star( value ) == NULL ) {
+ f->f_choice = LDAP_FILTER_EQUALITY;
+ } else if ( strcmp( value, "*" ) == 0 ) {
+ f->f_choice = LDAP_FILTER_PRESENT;
+ } else {
+ f->f_choice = LDAP_FILTER_SUBSTRINGS;
+ savechar = *s;
+ *s = 0;
+ f->f_sub_type = slapi_ch_strdup( str );
+ *s = savechar;
+ if ( str2subvals( value, f , unescape_filter) != 0 ) {
+ slapi_filter_free( f, 1 );
+ return( NULL );
+ }
+ filter_compute_hash(f);
+ return( f );
+ }
+ break;
+ }
+
+ if ( f->f_choice == LDAP_FILTER_PRESENT ) {
+ savechar = *s;
+ *s = 0;
+ f->f_type = slapi_ch_strdup( str );
+ *s = savechar;
+ } else if ( unescape_filter ) {
+ int r;
+ char *unqstr;
+ size_t len = strlen(value), len2;
+
+ /* dup attr */
+ savechar = *s;
+ *s = 0;
+ f->f_avtype = slapi_ch_strdup( str );
+ *s = savechar;
+
+ /* dup value */
+ savechar = value[len];
+ value[len] = 0;
+ unqstr = slapi_ch_calloc( 1, len+1);
+ r= filt_unescape_str(value, unqstr, len, &len2, 1);
+ value[len] = savechar;
+ if (!r) {
+ slapi_filter_free(f, 1);
+ return NULL;
+ }
+ f->f_avvalue.bv_val = unqstr;
+ f->f_avvalue.bv_len = len2;
+
+ if((f->f_choice == LDAP_FILTER_EQUALITY) &&
+ (0 == strncasecmp (str,"objectclass",strlen("objectclass")))) {
+ if (0 == strcasecmp (unqstr,"ldapsubentry"))
+ f->f_flags |= SLAPI_FILTER_LDAPSUBENTRY;
+ if (0 == strcasecmp (unqstr,SLAPI_ATTR_VALUE_TOMBSTONE))
+ f->f_flags |= SLAPI_FILTER_TOMBSTONE;
+ }
+ } if ( !unescape_filter ) {
+ f->f_avtype = slapi_ch_strdup( str );
+ f->f_avvalue.bv_val = slapi_ch_strdup ( value );
+ f->f_avvalue.bv_len = strlen ( f->f_avvalue.bv_val );
+ }
+
+ filter_compute_hash(f);
+ return( f );
+}
+
+static int
+str2subvals( char *val, struct slapi_filter *f, int unescape_filter )
+{
+ char *nextstar, *unqval;
+ int gotstar;
+ size_t len, outlen;
+
+ LDAPDebug( LDAP_DEBUG_FILTER, "str2subvals \"%s\"\n", val, 0, 0 );
+
+ gotstar = 0;
+ while ( val != NULL && *val ) {
+ if ( (nextstar = str_find_star( val )) != NULL )
+ *nextstar = '\0';
+
+ if ( unescape_filter ) {
+ len = strlen(val);
+ unqval = slapi_ch_malloc(len+1);
+ if (!filt_unescape_str(val, unqval, len, &outlen, 0)) {
+ slapi_ch_free((void **)&unqval);
+ return -1;
+ }
+ unqval[outlen]= '\0';
+ } else {
+ unqval = slapi_ch_strdup ( val );
+ }
+ if (unqval && unqval[0]) {
+ if (gotstar == 0) {
+ f->f_sub_initial = unqval;
+ } else if ( nextstar == NULL ) {
+ f->f_sub_final = unqval;
+ } else {
+ charray_add( &f->f_sub_any, unqval );
+ }
+ } else {
+ slapi_ch_free((void **)&unqval);
+ }
+
+ gotstar = 1;
+ if ( nextstar != NULL )
+ *nextstar++ = '*';
+ val = nextstar;
+ }
+
+ return( 0 );
+}
+
+/*
+ * find_matching_paren - return a pointer to the right paren in s matching
+ * the left paren to which *s currently points
+ */
+
+char *
+slapi_find_matching_paren( const char *s )
+{
+ int balance, escape;
+
+ balance = 0;
+ escape = 0;
+ for ( ; *s; s++ ) {
+ if ( escape == 0 ) {
+ if ( *s == '(' )
+ balance++;
+ else if ( *s == ')' )
+ balance--;
+ }
+ if ( balance == 0 ) {
+ return( (char *)s );
+ }
+ if ( *s == '\\' && ! escape )
+ escape = 1;
+ else
+ escape = 0;
+ }
+
+ return( NULL );
+}
diff --git a/ldap/servers/slapd/strdup.c b/ldap/servers/slapd/strdup.c
new file mode 100644
index 00000000..b25d6485
--- /dev/null
+++ b/ldap/servers/slapd/strdup.c
@@ -0,0 +1,25 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+#if defined( ultrix ) || defined( nextstep )
+
+#include <string.h>
+
+
+char *strdup( char *s )
+{
+ char *p;
+
+ if ( (p = (char *) malloc( strlen( s ) + 1 )) == NULL )
+ return( NULL );
+
+ strcpy( p, s );
+
+ return( p );
+}
+
+#else
+typedef int SHUT_UP_DAMN_COMPILER;
+#endif /* ultrix || nextstep */
diff --git a/ldap/servers/slapd/stubrepl.c b/ldap/servers/slapd/stubrepl.c
new file mode 100644
index 00000000..83607bc6
--- /dev/null
+++ b/ldap/servers/slapd/stubrepl.c
@@ -0,0 +1,42 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+/* stubrepl.c - stubs of functions required for the evil stuff in the tools directory */
+
+#include "slap.h"
+
+int connection_type = -1;
+
+void
+ps_service_persistent_searches( Slapi_Entry *e, Slapi_Entry *eprev, int chgtype, int chgnum )
+{
+}
+
+void
+ps_wakeup_all( void )
+{
+}
+
+int
+slapd_ssl_init()
+{
+ return( -1 );
+}
+
+int
+slapd_ssl_init2(PRFileDesc **fd, int startTLS)
+{
+ return( -1 );
+}
+
+void
+connection_abandon_operations( Connection *conn )
+{
+}
+
+void
+disconnect_server( Connection *conn, int opconnid, int opid, PRErrorCode reason, PRInt32 error )
+{
+}
diff --git a/ldap/servers/slapd/stubs.c b/ldap/servers/slapd/stubs.c
new file mode 100644
index 00000000..0440cedf
--- /dev/null
+++ b/ldap/servers/slapd/stubs.c
@@ -0,0 +1,36 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+
+/* Needed because not all functions are currently defined for server3_branch */
+
+#include <stdio.h>
+#include <string.h>
+#include <sys/types.h>
+#include "slap.h"
+
+#if defined( XP_WIN32 ) /* PK*/
+void *dlsym(void *a, char *b)
+{
+ return 0;
+}
+#endif
+
+int type_to_ACCESS_bit( char *p )
+{
+ return 0;
+}
+
+void *PT_Lock( PRLock *x_mutex )
+{
+ return NULL;
+}
+
+int lcache_init(LDAP *ld, void *arg)
+{
+ LDAPDebug(LDAP_DEBUG_ANY,
+ "lcache_init: Shouldn't have been called\n", 0,0,0);
+ return -1;
+}
diff --git a/ldap/servers/slapd/subentry.c b/ldap/servers/slapd/subentry.c
new file mode 100644
index 00000000..b178115c
--- /dev/null
+++ b/ldap/servers/slapd/subentry.c
@@ -0,0 +1,56 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+
+/* Code to implement subentries
+*/
+
+/* This is the plan for subentries :
+
+ For updates, they're like regular entries.
+
+ For searches, we need to do special stuff:
+ We need to examine the search filter.
+ If it contains a branch of the form "objectclass=ldapsubentry",
+ then we don't need to do anything special.
+ If it does not, we need to do special stuff:
+ We need to and a filter clause "!objectclass=ldapsubentry"
+ to the filter.
+ The intention is that no entries having objectclass "ldapsubentry"
+ should be returned to the client.
+
+ Now, I feel confident that this will all work, but it poses some
+ performance problems. Looking for the filter branch could be
+ inefficient. Adding an extra filter test to every operation
+ is likely to slow the very operations we care about most.
+
+ Need to think about the best way to optimize this, perhaps using an IDL cache.
+
+ */
+
+#include "slap.h"
+
+/* Function intended to be called only from inside get_filter, so look for subentry search filters */
+int subentry_check_filter(Slapi_Filter *f)
+{
+ if ( 0 == strcasecmp ( f->f_avvalue.bv_val, "ldapsubentry")) {
+ /* Need to remember this so we avoid rewriting the filter later */
+ return 1; /* Clear the re-write flag, since we've seen the subentry filter element */
+ }
+ return 0; /* Set the rewrite flag */
+}
+
+/* Function which wraps a filter with (AND !(objectclass=ldapsubentry)) */
+void subentry_create_filter(Slapi_Filter** filter)
+{
+ Slapi_Filter *sub_filter = NULL;
+ Slapi_Filter *new_filter = NULL;
+ char *buf = slapi_ch_strdup("(!(objectclass=ldapsubentry))");
+ sub_filter = slapi_str2filter( buf );
+ new_filter = slapi_filter_join( LDAP_FILTER_AND, *filter, sub_filter );
+ *filter = new_filter;
+ slapi_ch_free((void **)&buf);
+}
+
diff --git a/ldap/servers/slapd/task.c b/ldap/servers/slapd/task.c
new file mode 100644
index 00000000..7e4bd423
--- /dev/null
+++ b/ldap/servers/slapd/task.c
@@ -0,0 +1,1703 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+/*
+ * directory online tasks (import, export, backup, restore)
+ */
+
+#include "slap.h"
+
+
+/* don't panic, this is only used when creating new tasks or removing old
+ * ones...
+ */
+static Slapi_Task *global_task_list = NULL;
+static PRLock *global_task_lock = NULL;
+static int shutting_down = 0;
+
+
+#define TASK_BASE_DN "cn=tasks, cn=config"
+#define TASK_IMPORT_DN "cn=import, cn=tasks, cn=config"
+#define TASK_EXPORT_DN "cn=export, cn=tasks, cn=config"
+#define TASK_BACKUP_DN "cn=backup, cn=tasks, cn=config"
+#define TASK_RESTORE_DN "cn=restore, cn=tasks, cn=config"
+#define TASK_INDEX_DN "cn=index, cn=tasks, cn=config"
+#if defined(UPGRADEDB)
+#define TASK_UPGRADEDB_DN "cn=upgradedb, cn=tasks, cn=config"
+#endif
+
+#define TASK_LOG_NAME "nsTaskLog"
+#define TASK_STATUS_NAME "nsTaskStatus"
+#define TASK_EXITCODE_NAME "nsTaskExitCode"
+#define TASK_PROGRESS_NAME "nsTaskCurrentItem"
+#define TASK_WORK_NAME "nsTaskTotalItems"
+
+#define DEFAULT_TTL "120" /* seconds */
+
+
+static int task_modify(Slapi_PBlock *pb, Slapi_Entry *e,
+ Slapi_Entry *eAfter, int *returncode, char *returntext, void *arg);
+static int task_deny(Slapi_PBlock *pb, Slapi_Entry *e,
+ Slapi_Entry *eAfter, int *returncode, char *returntext, void *arg);
+static int task_generic_destructor(Slapi_Task *task);
+
+/* create new task, fill in DN, and setup modify callback */
+static Slapi_Task *new_task(const char *dn)
+{
+ Slapi_Task *task = (Slapi_Task *)slapi_ch_calloc(1, sizeof(Slapi_Task));
+
+ if (task == NULL)
+ return NULL;
+ PR_Lock(global_task_lock);
+ task->next = global_task_list;
+ global_task_list = task;
+ PR_Unlock(global_task_lock);
+
+ task->task_dn = slapi_ch_strdup(dn);
+ task->destructor = task_generic_destructor;
+ slapi_config_register_callback(SLAPI_OPERATION_MODIFY, DSE_FLAG_PREOP, dn,
+ LDAP_SCOPE_BASE, "(objectclass=*)", task_modify, (void *)task);
+ slapi_config_register_callback(SLAPI_OPERATION_DELETE, DSE_FLAG_PREOP, dn,
+ LDAP_SCOPE_BASE, "(objectclass=*)", task_deny, NULL);
+ /* don't add entries under this one */
+#if 0
+ /* don't know why, but this doesn't work. it makes the current add
+ * operation fail. :(
+ */
+ slapi_config_register_callback(SLAPI_OPERATION_ADD, DSE_FLAG_PREOP, dn,
+ LDAP_SCOPE_SUBTREE, "(objectclass=*)", task_deny, NULL);
+#endif
+
+ return task;
+}
+
+/* called by the event queue to destroy a task */
+static void destroy_task(time_t when, void *arg)
+{
+ Slapi_Task *task = (Slapi_Task *)arg;
+ Slapi_Task *t1;
+ Slapi_PBlock *pb = slapi_pblock_new();
+
+ if (task->destructor != NULL)
+ (*task->destructor)(task);
+
+ /* if when == 0, we're already locked (called during shutdown) */
+ if (when != 0) {
+ PR_Lock(global_task_lock);
+ }
+ if (global_task_list == task) {
+ global_task_list = task->next;
+ } else {
+ for (t1 = global_task_list; t1; t1 = t1->next) {
+ if (t1->next == task) {
+ t1->next = task->next;
+ break;
+ }
+ }
+ }
+ if (when != 0) {
+ PR_Unlock(global_task_lock);
+ }
+
+ slapi_config_remove_callback(SLAPI_OPERATION_MODIFY, DSE_FLAG_PREOP,
+ task->task_dn, LDAP_SCOPE_BASE, "(objectclass=*)", task_modify);
+ slapi_config_remove_callback(SLAPI_OPERATION_DELETE, DSE_FLAG_PREOP,
+ task->task_dn, LDAP_SCOPE_BASE, "(objectclass=*)", task_deny);
+ slapi_delete_internal_set_pb(pb, task->task_dn, NULL, NULL,
+ (void *)plugin_get_default_component_id(), 0);
+
+ slapi_delete_internal_pb(pb);
+ slapi_pblock_destroy(pb);
+
+ slapi_ch_free((void **)&task->task_dn);
+ slapi_ch_free((void **)&task);
+}
+
+
+/********** some useful helper functions **********/
+
+
+/* extract a single value from the entry (as a string) -- if it's not in the
+ * entry, the default will be returned (which can be NULL).
+ * you do not need to free anything returned by this.
+ */
+static const char *fetch_attr(Slapi_Entry *e, const char *attrname,
+ const char *default_val)
+{
+ Slapi_Attr *attr;
+ Slapi_Value *val = NULL;
+
+ if (slapi_entry_attr_find(e, attrname, &attr) != 0)
+ return default_val;
+ slapi_attr_first_value(attr, &val);
+ return slapi_value_get_string(val);
+}
+
+/* supply the pblock, destroy it when you're done */
+static Slapi_Entry *get_internal_entry(Slapi_PBlock *pb, char *dn)
+{
+ Slapi_Entry **entries = NULL;
+ int ret = 0;
+
+ slapi_search_internal_set_pb(pb, dn, LDAP_SCOPE_BASE, "(objectclass=*)",
+ NULL, 0, NULL, NULL, (void *)plugin_get_default_component_id(), 0);
+ slapi_search_internal_pb(pb);
+ slapi_pblock_get(pb, SLAPI_PLUGIN_INTOP_RESULT, &ret);
+ if (ret != LDAP_SUCCESS) {
+ LDAPDebug(LDAP_DEBUG_ANY, "WARNING: can't find task entry '%s'\n",
+ dn, 0, 0);
+ return NULL;
+ }
+
+ slapi_pblock_get(pb, SLAPI_PLUGIN_INTOP_SEARCH_ENTRIES, &entries);
+ if ((NULL == entries) || (NULL == entries[0])) {
+ LDAPDebug(LDAP_DEBUG_ANY, "WARNING: can't find task entry '%s'\n",
+ dn, 0, 0);
+ return NULL;
+ }
+ return entries[0];
+}
+
+static void modify_internal_entry(char *dn, LDAPMod **mods)
+{
+ Slapi_PBlock pb;
+ Slapi_Operation *op;
+ int ret = 0;
+ int tries = 0;
+ int dont_write_file = 1;
+
+ do {
+
+ pblock_init(&pb);
+
+ slapi_modify_internal_set_pb(&pb, dn, mods, NULL, NULL,
+ (void *)plugin_get_default_component_id(), 0);
+
+ /* all modifications to the cn=tasks subtree are transient --
+ * we erase them all when the server starts up next time, so there's
+ * no need to save them in the dse file.
+ */
+
+ slapi_pblock_set(&pb, SLAPI_DSE_DONT_WRITE_WHEN_ADDING, &dont_write_file);
+ /* Make sure these mods are not logged in audit or changelog */
+ slapi_pblock_get(&pb, SLAPI_OPERATION, &op);
+ operation_set_flag(op, OP_FLAG_ACTION_NOLOG);
+
+ slapi_modify_internal_pb(&pb);
+ slapi_pblock_get(&pb, SLAPI_PLUGIN_INTOP_RESULT, &ret);
+ if (ret != LDAP_SUCCESS) {
+ /* could be waiting for another thread to finish adding this
+ * entry -- try at least 3 times before giving up.
+ */
+ tries++;
+ if (tries == 3) {
+ LDAPDebug(LDAP_DEBUG_ANY, "WARNING: can't modify task "
+ "entry '%s'; %s (%d)\n", dn, ldap_err2string(ret), ret);
+ pblock_done(&pb);
+ return;
+ }
+ DS_Sleep(PR_SecondsToInterval(1));
+ }
+
+ pblock_done(&pb);
+
+ } while (ret != LDAP_SUCCESS);
+}
+
+
+/********** helper functions for dealing with task logging **********/
+
+#define LOG_BUFFER 256
+/* if the cumul. log gets larger than this, it's truncated: */
+#define MAX_SCROLLBACK_BUFFER 8192
+
+/* this changes the 'nsTaskStatus' value, which is transient (anything logged
+ * here wipes out any previous status)
+ */
+void slapi_task_log_status(Slapi_Task *task, char *format, ...)
+{
+ va_list ap;
+
+ if (! task->task_status)
+ task->task_status = (char *)slapi_ch_malloc(10 * LOG_BUFFER);
+ if (! task->task_status)
+ return; /* out of memory? */
+
+ va_start(ap, format);
+ PR_vsnprintf(task->task_status, (10 * LOG_BUFFER), format, ap);
+ va_end(ap);
+ slapi_task_status_changed(task);
+}
+
+/* this adds a line to the 'nsTaskLog' value, which is cumulative (anything
+ * logged here is added to the end)
+ */
+void slapi_task_log_notice(Slapi_Task *task, char *format, ...)
+{
+ va_list ap;
+ char buffer[LOG_BUFFER];
+ size_t len;
+
+ va_start(ap, format);
+ PR_vsnprintf(buffer, LOG_BUFFER, format, ap);
+ va_end(ap);
+
+ len = 2 + strlen(buffer) + (task->task_log ? strlen(task->task_log) : 0);
+ if (len > MAX_SCROLLBACK_BUFFER) {
+ size_t i;
+ char *newbuf;
+
+ /* start from middle of buffer, and find next linefeed */
+ i = strlen(task->task_log)/2;
+ while (task->task_log[i] && (task->task_log[i] != '\n'))
+ i++;
+ if (task->task_log[i])
+ i++;
+ len = strlen(task->task_log) - i + 2 + strlen(buffer);
+ newbuf = (char *)slapi_ch_malloc(len);
+ if (! newbuf)
+ return; /* out of memory? */
+ strcpy(newbuf, task->task_log + i);
+ slapi_ch_free((void **)&task->task_log);
+ task->task_log = newbuf;
+ } else {
+ if (! task->task_log) {
+ task->task_log = (char *)slapi_ch_malloc(len);
+ task->task_log[0] = 0;
+ } else {
+ task->task_log = (char *)slapi_ch_realloc(task->task_log, len);
+ }
+ if (! task->task_log)
+ return; /* out of memory? */
+ }
+
+ if (task->task_log[0])
+ strcat(task->task_log, "\n");
+ strcat(task->task_log, buffer);
+
+ slapi_task_status_changed(task);
+}
+
+static int task_generic_destructor(Slapi_Task *task)
+{
+ if (task->task_log) {
+ slapi_ch_free((void **)&task->task_log);
+ }
+ if (task->task_status) {
+ slapi_ch_free((void **)&task->task_status);
+ }
+ task->task_log = task->task_status = NULL;
+ return 0;
+}
+
+
+/********** actual task callbacks **********/
+
+
+static int task_deny(Slapi_PBlock *pb, Slapi_Entry *e,
+ Slapi_Entry *eAfter, int *returncode, char *returntext, void *arg)
+{
+ /* internal operations (conn=NULL) are allowed to do whatever they want */
+ if (pb->pb_conn == NULL) {
+ *returncode = LDAP_SUCCESS;
+ return SLAPI_DSE_CALLBACK_OK;
+ }
+
+ *returncode = LDAP_UNWILLING_TO_PERFORM;
+ return SLAPI_DSE_CALLBACK_ERROR;
+}
+
+static int task_modify(Slapi_PBlock *pb, Slapi_Entry *e,
+ Slapi_Entry *eAfter, int *returncode, char *returntext, void *arg)
+{
+ Slapi_Task *task = (Slapi_Task *)arg;
+ LDAPMod **mods;
+ int i;
+
+ /* the connection block will be NULL for internal operations */
+ if (pb->pb_conn == NULL) {
+ *returncode = LDAP_SUCCESS;
+ return SLAPI_DSE_CALLBACK_OK;
+ }
+
+ /* ignore eAfter, just scan the mods for anything unacceptable */
+ slapi_pblock_get(pb, SLAPI_MODIFY_MODS, &mods);
+ for (i = 0; mods[i] != NULL; i++) {
+ /* for some reason, "modifiersName" and "modifyTimestamp" are
+ * stuck in by the server */
+ if ((strcasecmp(mods[i]->mod_type, "ttl") != 0) &&
+ (strcasecmp(mods[i]->mod_type, "nsTaskCancel") != 0) &&
+ (strcasecmp(mods[i]->mod_type, "modifiersName") != 0) &&
+ (strcasecmp(mods[i]->mod_type, "modifyTimestamp") != 0)) {
+ /* you aren't allowed to change this! */
+ *returncode = LDAP_UNWILLING_TO_PERFORM;
+ return SLAPI_DSE_CALLBACK_ERROR;
+ }
+ }
+
+ /* okay, we've decided to accept these changes. now look at the new
+ * entry and absorb any new values.
+ */
+ if (strcasecmp(fetch_attr(eAfter, "nsTaskCancel", "false"), "true") == 0) {
+ /* cancel this task, if not already */
+ if (task->task_state != SLAPI_TASK_CANCELLED) {
+ task->task_state = SLAPI_TASK_CANCELLED;
+ if (task->cancel) {
+ (*task->cancel)(task);
+ LDAPDebug(LDAP_DEBUG_ANY, "Cancelling task '%s'\n",
+ fetch_attr(eAfter, "cn", "?"), 0, 0);
+ }
+ }
+ }
+ /* we fetch ttl from the entry when it's needed */
+
+ *returncode = LDAP_SUCCESS;
+ return SLAPI_DSE_CALLBACK_OK;
+}
+
+static int task_import_add(Slapi_PBlock *pb, Slapi_Entry *e,
+ Slapi_Entry *eAfter, int *returncode, char *returntext, void *arg)
+{
+ Slapi_Attr *attr;
+ Slapi_Value *val = NULL;
+ Slapi_Backend *be = NULL;
+ const char *cn, *instance_name;
+ char **ldif_file = NULL, **include = NULL, **exclude = NULL;
+ int idx, rv = 0;
+ const char *do_attr_indexes, *uniqueid_kind_str;
+ int uniqueid_kind = SLAPI_UNIQUEID_GENERATE_TIME_BASED;
+ Slapi_PBlock mypb;
+ Slapi_Task *task;
+ char *nameFrombe_name = NULL;
+ const char *encrypt_on_import = NULL;
+
+ if ((cn = fetch_attr(e, "cn", NULL)) == NULL) {
+ *returncode = LDAP_OBJECT_CLASS_VIOLATION;
+ return SLAPI_DSE_CALLBACK_ERROR;
+ }
+ instance_name = fetch_attr(e, "nsInstance", NULL);
+
+ encrypt_on_import = fetch_attr(e, "nsImportEncrypt", NULL);
+
+ /* include/exclude suffixes */
+ if (slapi_entry_attr_find(e, "nsIncludeSuffix", &attr) == 0) {
+ for (idx = slapi_attr_first_value(attr, &val);
+ idx >= 0; idx = slapi_attr_next_value(attr, idx, &val)) {
+ charray_add(&include, slapi_ch_strdup(slapi_value_get_string(val)));
+ }
+ }
+ if (slapi_entry_attr_find(e, "nsExcludeSuffix", &attr) == 0) {
+ for (idx = slapi_attr_first_value(attr, &val);
+ idx >= 0; idx = slapi_attr_next_value(attr, idx, &val)) {
+ charray_add(&exclude, slapi_ch_strdup(slapi_value_get_string(val)));
+ }
+ }
+
+ /*
+ * if instance is given, just use it to get the backend.
+ * otherwise, we use included/excluded suffix list to specify a backend.
+ */
+ if (NULL == instance_name) {
+ char **instances, **ip;
+ int counter;
+
+ if (slapi_lookup_instance_name_by_suffixes(include, exclude,
+ &instances) < 0) {
+ LDAPDebug(LDAP_DEBUG_ANY,
+ "ERROR: No backend instance is specified.\n", 0, 0, 0);
+ *returncode = LDAP_OBJECT_CLASS_VIOLATION;
+ return SLAPI_DSE_CALLBACK_ERROR;
+ }
+
+ if (instances) {
+ for (ip = instances, counter = 0; ip && *ip; ip++, counter++)
+ ;
+
+ if (counter == 1){
+ instance_name = *instances;
+ nameFrombe_name = *instances;
+
+ }
+ else if (counter == 0) {
+ LDAPDebug(LDAP_DEBUG_ANY,
+ "ERROR: No backend instance is specified.\n", 0, 0, 0);
+ *returncode = LDAP_OBJECT_CLASS_VIOLATION;
+ return SLAPI_DSE_CALLBACK_ERROR;
+ } else {
+ LDAPDebug(LDAP_DEBUG_ANY,
+ "ERROR: Multiple backend instances are specified: "
+ "%s, %s, ...\n", instances[0], instances[1], 0);
+ *returncode = LDAP_OBJECT_CLASS_VIOLATION;
+ return SLAPI_DSE_CALLBACK_ERROR;
+ }
+ } else {
+ *returncode = LDAP_OBJECT_CLASS_VIOLATION;
+ return SLAPI_DSE_CALLBACK_ERROR;
+ }
+ }
+
+ /* lookup the backend */
+ be = slapi_be_select_by_instance_name(instance_name);
+ if (be == NULL) {
+ LDAPDebug(LDAP_DEBUG_ANY, "can't import to nonexistent backend %s\n",
+ instance_name, 0, 0);
+ slapi_ch_free_string(&nameFrombe_name);
+ *returncode = LDAP_NO_SUCH_OBJECT;
+ return SLAPI_DSE_CALLBACK_ERROR;
+ }
+
+ /* refuse to do an import on pre-V3 plugins. plugin api V3 is the one
+ * for DS 5.0 where the import/export stuff changed a lot.
+ */
+ if (! SLAPI_PLUGIN_IS_V3(be->be_database)) {
+ LDAPDebug(LDAP_DEBUG_ANY, "can't perform an import with pre-V3 "
+ "backend plugin %s\n", be->be_database->plg_name, 0, 0);
+ *returncode = LDAP_UNWILLING_TO_PERFORM;
+ slapi_ch_free_string(&nameFrombe_name);
+ return SLAPI_DSE_CALLBACK_ERROR;
+ }
+ if (be->be_database->plg_ldif2db == NULL) {
+ LDAPDebug(LDAP_DEBUG_ANY, "ERROR: no ldif2db function defined for "
+ "backend %s\n", be->be_database->plg_name, 0, 0);
+ *returncode = LDAP_UNWILLING_TO_PERFORM;
+ slapi_ch_free_string(&nameFrombe_name);
+ return SLAPI_DSE_CALLBACK_ERROR;
+ }
+
+ /* get ldif filenames -- from here on, memory has been allocated */
+ if (slapi_entry_attr_find(e, "nsFilename", &attr) != 0) {
+ *returncode = LDAP_OBJECT_CLASS_VIOLATION;
+ slapi_ch_free_string(&nameFrombe_name);
+ return SLAPI_DSE_CALLBACK_ERROR;
+ }
+ for (idx = slapi_attr_first_value(attr, &val);
+ idx >= 0; idx = slapi_attr_next_value(attr, idx, &val)) {
+ charray_add(&ldif_file, slapi_ch_strdup(slapi_value_get_string(val)));
+ }
+
+ do_attr_indexes = fetch_attr(e, "nsImportIndexAttrs", "true");
+ uniqueid_kind_str = fetch_attr(e, "nsUniqueIdGenerator", NULL);
+ if (uniqueid_kind_str != NULL) {
+ if (strcasecmp(uniqueid_kind_str, "none") == 0) {
+ uniqueid_kind = SLAPI_UNIQUEID_GENERATE_NONE;
+ } else if (strcasecmp(uniqueid_kind_str, "deterministic") == 0) {
+ uniqueid_kind = SLAPI_UNIQUEID_GENERATE_NAME_BASED;
+ } else {
+ /* default - time based */
+ uniqueid_kind = SLAPI_UNIQUEID_GENERATE_TIME_BASED;
+ }
+ }
+
+ /* allocate new task now */
+ task = new_task(slapi_entry_get_ndn(e));
+ if (task == NULL) {
+ LDAPDebug(LDAP_DEBUG_ANY, "unable to allocate new task!\n", 0, 0, 0);
+ rv = LDAP_OPERATIONS_ERROR;
+ goto out;
+ }
+ task->task_state = SLAPI_TASK_SETUP;
+
+ memset(&mypb, 0, sizeof(mypb));
+ mypb.pb_backend = be;
+ mypb.pb_plugin = be->be_database;
+ mypb.pb_removedupvals = atoi(fetch_attr(e, "nsImportChunkSize", "0"));
+ mypb.pb_ldif2db_noattrindexes =
+ !(strcasecmp(do_attr_indexes, "true") == 0);
+ mypb.pb_ldif_generate_uniqueid = uniqueid_kind;
+ mypb.pb_ldif_namespaceid =
+ (char *)fetch_attr(e, "nsUniqueIdGeneratorNamespace", NULL);
+ mypb.pb_instance_name = (char *)instance_name;
+ mypb.pb_ldif_files = ldif_file;
+ mypb.pb_ldif_include = include;
+ mypb.pb_ldif_exclude = exclude;
+ mypb.pb_task = task;
+ mypb.pb_task_flags = TASK_RUNNING_AS_TASK;
+ if (NULL != encrypt_on_import && 0 == strcasecmp(encrypt_on_import, "true") ) {
+ mypb.pb_ldif_encrypt = 1;
+ }
+
+ rv = (*mypb.pb_plugin->plg_ldif2db)(&mypb);
+ if (rv == 0) {
+ slapi_entry_attr_set_charptr(e, TASK_LOG_NAME, "");
+ slapi_entry_attr_set_charptr(e, TASK_STATUS_NAME, "");
+ slapi_entry_attr_set_int(e, TASK_PROGRESS_NAME, task->task_progress);
+ slapi_entry_attr_set_int(e, TASK_WORK_NAME, task->task_work);
+ }
+
+out:
+ slapi_ch_free_string(&nameFrombe_name);
+ charray_free(ldif_file);
+ charray_free(include);
+ charray_free(exclude);
+ if (rv != 0) {
+ *returncode = LDAP_OPERATIONS_ERROR;
+ destroy_task(1, task);
+ return SLAPI_DSE_CALLBACK_ERROR;
+ }
+
+ *returncode = LDAP_SUCCESS;
+ return SLAPI_DSE_CALLBACK_OK;
+}
+
+static void task_export_thread(void *arg)
+{
+ Slapi_PBlock *pb = (Slapi_PBlock *)arg;
+ char **instance_names = (char **)pb->pb_instance_name;
+ char **inp;
+ char *ldif_file = pb->pb_ldif_file;
+ char *this_ldif_file = NULL;
+ Slapi_Backend *be = NULL;
+ int rv = -1;
+ int count;
+ Slapi_Task *task = pb->pb_task;
+
+ for (count = 0, inp = instance_names; *inp; inp++, count++)
+ ;
+ task->task_work = count;
+ task->task_progress = 0;
+ task->task_state = SLAPI_TASK_RUNNING;
+ slapi_task_status_changed(task);
+
+ for (inp = instance_names; *inp; inp++) {
+ int release_me = 0;
+ /* lookup the backend */
+ be = slapi_be_select_by_instance_name((const char *)*inp);
+ if (be == NULL) {
+ /* shouldn't happen */
+ LDAPDebug(LDAP_DEBUG_ANY, "ldbm2ldif: backend '%s' is AWOL!\n",
+ (const char *)*inp, 0, 0);
+ continue;
+ }
+
+ pb->pb_backend = be;
+ pb->pb_plugin = be->be_database;
+ pb->pb_instance_name = (char *)*inp;
+
+ /* ldif_file name for each? */
+ if (pb->pb_ldif_printkey & EXPORT_APPENDMODE) {
+ if (inp == instance_names) { /* first export */
+ pb->pb_ldif_printkey |= EXPORT_APPENDMODE_1;
+ } else {
+ pb->pb_ldif_printkey &= ~EXPORT_APPENDMODE_1;
+ }
+ } else {
+ if (strcmp(ldif_file, "-")) { /* not '-' */
+ char *p;
+#if defined( _WIN32 )
+ char sep = '\\';
+ if (NULL != strchr(ldif_file, '/'))
+ sep = '/';
+#else
+ char sep = '/';
+#endif
+ this_ldif_file = (char *)slapi_ch_malloc(strlen(ldif_file) +
+ strlen(*inp) + 2);
+ p = strrchr(ldif_file, sep);
+ if (NULL == p) {
+ sprintf(this_ldif_file, "%s_%s", *inp, ldif_file);
+ } else {
+ char *q;
+
+ q = p + 1;
+ *p = '\0';
+ sprintf(this_ldif_file, "%s%c%s_%s",
+ ldif_file, sep, *inp, q);
+ *p = sep;
+ }
+ pb->pb_ldif_file = this_ldif_file;
+ release_me = 1;
+ }
+ }
+
+ slapi_task_log_notice(task, "Beginning export of '%s'", *inp);
+ LDAPDebug(LDAP_DEBUG_ANY, "Beginning export of '%s'\n", *inp, 0, 0);
+
+ rv = (*pb->pb_plugin->plg_db2ldif)(pb);
+ if (rv != 0) {
+ slapi_task_log_notice(task, "backend '%s' export failed (%d)",
+ *inp, rv);
+ LDAPDebug(LDAP_DEBUG_ANY,
+ "ldbm2ldif: backend '%s' export failed (%d)\n",
+ (const char *)*inp, rv, 0);
+ }
+
+ if (release_me) {
+ slapi_ch_free((void **)&this_ldif_file);
+ }
+
+ if (rv != 0)
+ break;
+
+ task->task_progress++;
+ slapi_task_status_changed(task);
+ }
+
+ /* free the memory now */
+ charray_free(instance_names);
+ slapi_ch_free((void **)&ldif_file);
+ charray_free(pb->pb_ldif_include);
+ charray_free(pb->pb_ldif_exclude);
+ slapi_pblock_destroy(pb);
+
+ if (rv == 0) {
+ slapi_task_log_notice(task, "Export finished.");
+ LDAPDebug(LDAP_DEBUG_ANY, "Export finished.\n", 0, 0, 0);
+ } else {
+ slapi_task_log_notice(task, "Export failed.");
+ LDAPDebug(LDAP_DEBUG_ANY, "Export failed.\n", 0, 0, 0);
+ }
+
+ task->task_exitcode = rv;
+ task->task_state = SLAPI_TASK_FINISHED;
+ slapi_task_status_changed(task);
+}
+
+static int task_export_add(Slapi_PBlock *pb, Slapi_Entry *e,
+ Slapi_Entry *eAfter, int *returncode, char *returntext, void *arg)
+{
+ Slapi_Attr *attr;
+ Slapi_Value *val = NULL;
+ Slapi_Backend *be = NULL;
+ const char *cn;
+ char *ldif_file = NULL;
+ char **instance_names = NULL, **inp;
+ char **include = NULL, **exclude = NULL;
+ int idx, rv = SLAPI_DSE_CALLBACK_OK;
+ int export_replica_flag = 0;
+ int ldif_printkey_flag = 0;
+ int dump_uniqueid_flag = 0;
+ int instance_cnt = 0;
+ const char *my_ldif_file;
+ const char *use_one_file;
+ const char *export_replica;
+ const char *ldif_printkey;
+ const char *dump_uniqueid;
+ Slapi_PBlock *mypb = NULL;
+ Slapi_Task *task = NULL;
+ PRThread *thread;
+ const char *decrypt_on_export = NULL;
+
+ *returncode = LDAP_SUCCESS;
+ if ((cn = fetch_attr(e, "cn", NULL)) == NULL) {
+ *returncode = LDAP_OBJECT_CLASS_VIOLATION;
+ rv = SLAPI_DSE_CALLBACK_ERROR;
+ goto out;
+ }
+
+ decrypt_on_export = fetch_attr(e, "nsExportDecrypt", NULL);
+
+ /* nsInstances -- from here on, memory has been allocated */
+ if (slapi_entry_attr_find(e, "nsInstance", &attr) == 0) {
+ for (idx = slapi_attr_first_value(attr, &val);
+ idx >= 0; idx = slapi_attr_next_value(attr, idx, &val)) {
+ charray_add(&instance_names,
+ slapi_ch_strdup(slapi_value_get_string(val)));
+ instance_cnt++;
+ }
+ }
+
+ /* include/exclude suffixes */
+ if (slapi_entry_attr_find(e, "nsIncludeSuffix", &attr) == 0) {
+ for (idx = slapi_attr_first_value(attr, &val);
+ idx >= 0; idx = slapi_attr_next_value(attr, idx, &val)) {
+ charray_add(&include, slapi_ch_strdup(slapi_value_get_string(val)));
+ }
+ }
+ if (slapi_entry_attr_find(e, "nsExcludeSuffix", &attr) == 0) {
+ for (idx = slapi_attr_first_value(attr, &val);
+ idx >= 0; idx = slapi_attr_next_value(attr, idx, &val)) {
+ charray_add(&exclude, slapi_ch_strdup(slapi_value_get_string(val)));
+ }
+ }
+
+ if (NULL == instance_names) {
+ char **ip;
+
+ if (slapi_lookup_instance_name_by_suffixes(include, exclude,
+ &instance_names) < 0) {
+ LDAPDebug(LDAP_DEBUG_ANY,
+ "ERROR: No backend instance is specified.\n", 0, 0, 0);
+ *returncode = LDAP_OBJECT_CLASS_VIOLATION;
+ rv = SLAPI_DSE_CALLBACK_ERROR;
+ goto out;
+ }
+
+ if (instance_names) {
+ for (ip = instance_names, instance_cnt = 0; ip && *ip;
+ ip++, instance_cnt++)
+ ;
+
+ if (instance_cnt == 0) {
+ LDAPDebug(LDAP_DEBUG_ANY,
+ "ERROR: No backend instance is specified.\n", 0, 0, 0);
+ *returncode = LDAP_OBJECT_CLASS_VIOLATION;
+ rv = SLAPI_DSE_CALLBACK_ERROR;
+ goto out;
+ }
+ } else {
+ LDAPDebug(LDAP_DEBUG_ANY,
+ "ERROR: No backend instance is specified.\n", 0, 0, 0);
+ *returncode = LDAP_OBJECT_CLASS_VIOLATION;
+ rv = SLAPI_DSE_CALLBACK_ERROR;
+ goto out;
+ }
+ }
+
+ /* ldif file name */
+ if ((my_ldif_file = fetch_attr(e, "nsFilename", NULL)) == NULL) {
+ *returncode = LDAP_OBJECT_CLASS_VIOLATION;
+ rv = SLAPI_DSE_CALLBACK_ERROR;
+ goto out;
+ }
+ ldif_file = slapi_ch_strdup(my_ldif_file);
+ /* if true, multiple backends are dumped into one ldif file */
+ use_one_file = fetch_attr(e, "nsUseOneFile", "true");
+ if (strcasecmp(use_one_file, "true") == 0) {
+ ldif_printkey_flag |= EXPORT_APPENDMODE;
+ }
+
+ /* -r: export replica */
+ export_replica = fetch_attr(e, "nsExportReplica", "false");
+ if (!strcasecmp(export_replica, "true")) /* true */
+ export_replica_flag = 1;
+
+ /* -N: eq "false" ==> does not print out key value */
+ ldif_printkey = fetch_attr(e, "nsPrintKey", "true");
+ if (!strcasecmp(ldif_printkey, "true")) /* true */
+ ldif_printkey_flag |= EXPORT_PRINTKEY;
+
+ /* -C: eq "true" ==> use only id2entry file */
+ ldif_printkey = fetch_attr(e, "nsUseId2Entry", "false");
+ if (!strcasecmp(ldif_printkey, "true")) /* true */
+ ldif_printkey_flag |= EXPORT_ID2ENTRY_ONLY;
+
+ /* if "true" ==> 8-bit strings are not base64 encoded */
+ ldif_printkey = fetch_attr(e, "nsMinimalEncoding", "false");
+ if (!strcasecmp(ldif_printkey, "true")) /* true */
+ ldif_printkey_flag |= EXPORT_MINIMAL_ENCODING;
+
+ /* -U: eq "true" ==> does not fold the output */
+ ldif_printkey = fetch_attr(e, "nsNoWrap", "false");
+ if (!strcasecmp(ldif_printkey, "true")) /* true */
+ ldif_printkey_flag |= EXPORT_NOWRAP;
+
+ /* -1: eq "true" ==> does not print version line */
+ ldif_printkey = fetch_attr(e, "nsNoVersionLine", "false");
+ if (!strcasecmp(ldif_printkey, "true")) /* true */
+ ldif_printkey_flag |= EXPORT_NOVERSION;
+
+ /* -u: eq "false" ==> does not dump unique id */
+ dump_uniqueid = fetch_attr(e, "nsDumpUniqId", "true");
+ if (!strcasecmp(dump_uniqueid, "true")) /* true */
+ dump_uniqueid_flag = 1;
+
+ /* check that all the backends are ok */
+ for (inp = instance_names; *inp; inp++) {
+ /* lookup the backend */
+ be = slapi_be_select_by_instance_name((const char *)*inp);
+ if (be == NULL) {
+ LDAPDebug(LDAP_DEBUG_ANY,
+ "can't export to nonexistent backend %s\n", *inp, 0, 0);
+ *returncode = LDAP_NO_SUCH_OBJECT;
+ rv = SLAPI_DSE_CALLBACK_ERROR;
+ goto out;
+ }
+
+ /* refuse to do an export on pre-V3 plugins. plugin api V3 is the one
+ * for DS 5.0 where the import/export stuff changed a lot.
+ */
+ if (! SLAPI_PLUGIN_IS_V3(be->be_database)) {
+ LDAPDebug(LDAP_DEBUG_ANY, "can't perform an export with pre-V3 "
+ "backend plugin %s\n", be->be_database->plg_name, 0, 0);
+ *returncode = LDAP_UNWILLING_TO_PERFORM;
+ rv = SLAPI_DSE_CALLBACK_ERROR;
+ goto out;
+ }
+ if (be->be_database->plg_db2ldif == NULL) {
+ LDAPDebug(LDAP_DEBUG_ANY, "ERROR: no db2ldif function defined for "
+ "backend %s\n", be->be_database->plg_name, 0, 0);
+ *returncode = LDAP_UNWILLING_TO_PERFORM;
+ rv = SLAPI_DSE_CALLBACK_ERROR;
+ goto out;
+ }
+ }
+
+ /* allocate new task now */
+ task = new_task(slapi_entry_get_ndn(e));
+ if (task == NULL) {
+ LDAPDebug(LDAP_DEBUG_ANY, "unable to allocate new task!\n", 0, 0, 0);
+ *returncode = LDAP_OPERATIONS_ERROR;
+ rv = SLAPI_DSE_CALLBACK_ERROR;
+ goto out;
+ }
+ task->task_state = SLAPI_TASK_SETUP;
+ task->task_work = instance_cnt;
+ task->task_progress = 0;
+
+ mypb = slapi_pblock_new();
+ if (mypb == NULL) {
+ *returncode = LDAP_OPERATIONS_ERROR;
+ rv = SLAPI_DSE_CALLBACK_ERROR;
+ goto out;
+ }
+ mypb->pb_ldif_include = include;
+ mypb->pb_ldif_exclude = exclude;
+ mypb->pb_ldif_printkey = ldif_printkey_flag;
+ mypb->pb_ldif_dump_replica = export_replica_flag;
+ mypb->pb_ldif_dump_uniqueid = dump_uniqueid_flag;
+ mypb->pb_ldif_file = ldif_file;
+ /* horrible hack */
+ mypb->pb_instance_name = (char *)instance_names;
+ mypb->pb_task = task;
+ mypb->pb_task_flags = TASK_RUNNING_AS_TASK;
+ if (NULL != decrypt_on_export && 0 == strcasecmp(decrypt_on_export, "true") ) {
+ mypb->pb_ldif_encrypt = 1;
+ }
+
+ /* start the export as a separate thread */
+ thread = PR_CreateThread(PR_USER_THREAD, task_export_thread,
+ (void *)mypb, PR_PRIORITY_NORMAL, PR_GLOBAL_THREAD,
+ PR_UNJOINABLE_THREAD, SLAPD_DEFAULT_THREAD_STACKSIZE);
+ if (thread == NULL) {
+ LDAPDebug(LDAP_DEBUG_ANY,
+ "unable to create ldbm2ldif thread!\n", 0, 0, 0);
+ *returncode = LDAP_OPERATIONS_ERROR;
+ rv = SLAPI_DSE_CALLBACK_ERROR;
+ slapi_pblock_destroy(mypb);
+ goto out;
+ }
+
+ /* thread successful -- don't free the pb, let the thread do that. */
+ return SLAPI_DSE_CALLBACK_OK;
+
+out:
+ charray_free(instance_names);
+ charray_free(include);
+ charray_free(exclude);
+ if (ldif_file != NULL) {
+ slapi_ch_free((void **)&ldif_file);
+ }
+ if (task) {
+ destroy_task(1, task);
+ }
+
+ return rv;
+}
+
+
+static void task_backup_thread(void *arg)
+{
+ Slapi_PBlock *pb = (Slapi_PBlock *)arg;
+ Slapi_Task *task = pb->pb_task;
+ int rv;
+
+ task->task_work = 1;
+ task->task_progress = 0;
+ task->task_state = SLAPI_TASK_RUNNING;
+ slapi_task_status_changed(task);
+
+ slapi_task_log_notice(task, "Beginning backup of '%s'",
+ pb->pb_plugin->plg_name);
+ LDAPDebug(LDAP_DEBUG_ANY, "Beginning backup of '%s'\n",
+ pb->pb_plugin->plg_name, 0, 0);
+
+ rv = (*pb->pb_plugin->plg_db2archive)(pb);
+ if (rv != 0) {
+ slapi_task_log_notice(task, "Backup failed (error %d)", rv);
+ slapi_task_log_status(task, "Backup failed (error %d)", rv);
+ LDAPDebug(LDAP_DEBUG_ANY, "Backup failed (error %d)\n", rv, 0, 0);
+ } else {
+ slapi_task_log_notice(task, "Backup finished.");
+ slapi_task_log_status(task, "Backup finished.");
+ LDAPDebug(LDAP_DEBUG_ANY, "Backup finished.\n", 0, 0, 0);
+ }
+
+ task->task_progress = 1;
+ task->task_exitcode = rv;
+ task->task_state = SLAPI_TASK_FINISHED;
+ slapi_task_status_changed(task);
+
+ slapi_ch_free((void **)&pb->pb_seq_val);
+ slapi_pblock_destroy(pb);
+}
+
+static int task_backup_add(Slapi_PBlock *pb, Slapi_Entry *e,
+ Slapi_Entry *eAfter, int *returncode, char *returntext, void *arg)
+{
+ Slapi_Backend *be = NULL;
+ PRThread *thread = NULL;
+ const char *cn;
+ const char *archive_dir = NULL;
+ const char *my_database_type = NULL;
+ const char *database_type = "ldbm database";
+ char *cookie = NULL;
+ int rv = SLAPI_DSE_CALLBACK_OK;
+ Slapi_PBlock *mypb = NULL;
+ Slapi_Task *task = NULL;
+
+ *returncode = LDAP_SUCCESS;
+ if ((cn = fetch_attr(e, "cn", NULL)) == NULL) {
+ *returncode = LDAP_OBJECT_CLASS_VIOLATION;
+ rv = SLAPI_DSE_CALLBACK_ERROR;
+ goto out;
+ }
+
+ /* archive dir name */
+ if ((archive_dir = fetch_attr(e, "nsArchiveDir", NULL)) == NULL) {
+ *returncode = LDAP_OBJECT_CLASS_VIOLATION;
+ rv = SLAPI_DSE_CALLBACK_ERROR;
+ goto out;
+ }
+
+ /* database type */
+ my_database_type = fetch_attr(e, "nsDatabaseType", NULL);
+ if (NULL != my_database_type)
+ database_type = my_database_type;
+
+ /* get backend that has db2archive and the database type matches. */
+ cookie = NULL;
+ be = slapi_get_first_backend(&cookie);
+ while (be) {
+ if (NULL != be->be_database->plg_db2archive &&
+ !strcasecmp(database_type, be->be_database->plg_name))
+ break;
+
+ be = (backend *)slapi_get_next_backend (cookie);
+ }
+ if (NULL == be || NULL == be->be_database->plg_db2archive) {
+ LDAPDebug(LDAP_DEBUG_ANY,
+ "ERROR: no db2archive function defined.\n", 0, 0, 0);
+ *returncode = LDAP_UNWILLING_TO_PERFORM;
+ rv = SLAPI_DSE_CALLBACK_ERROR;
+ goto out;
+ }
+
+ if (! SLAPI_PLUGIN_IS_V3(be->be_database)) {
+ LDAPDebug(LDAP_DEBUG_ANY, "can't perform an backup with pre-V3 "
+ "backend plugin %s\n", be->be_database->plg_name, 0, 0);
+ *returncode = LDAP_UNWILLING_TO_PERFORM;
+ rv = SLAPI_DSE_CALLBACK_ERROR;
+ goto out;
+ }
+
+ /* allocate new task now */
+ task = new_task(slapi_entry_get_ndn(e));
+ if (task == NULL) {
+ LDAPDebug(LDAP_DEBUG_ANY, "unable to allocate new task!\n", 0, 0, 0);
+ *returncode = LDAP_OPERATIONS_ERROR;
+ rv = SLAPI_DSE_CALLBACK_ERROR;
+ goto out;
+ }
+ task->task_state = SLAPI_TASK_SETUP;
+ task->task_work = 1;
+ task->task_progress = 0;
+
+ mypb = slapi_pblock_new();
+ if (mypb == NULL) {
+ *returncode = LDAP_OPERATIONS_ERROR;
+ rv = SLAPI_DSE_CALLBACK_ERROR;
+ goto out;
+ }
+ mypb->pb_seq_val = slapi_ch_strdup(archive_dir);
+ mypb->pb_plugin = be->be_database;
+ mypb->pb_task = task;
+ mypb->pb_task_flags = TASK_RUNNING_AS_TASK;
+
+ /* start the backup as a separate thread */
+ thread = PR_CreateThread(PR_USER_THREAD, task_backup_thread,
+ (void *)mypb, PR_PRIORITY_NORMAL, PR_GLOBAL_THREAD,
+ PR_UNJOINABLE_THREAD, SLAPD_DEFAULT_THREAD_STACKSIZE);
+ if (thread == NULL) {
+ LDAPDebug(LDAP_DEBUG_ANY,
+ "unable to create backup thread!\n", 0, 0, 0);
+ *returncode = LDAP_OPERATIONS_ERROR;
+ rv = SLAPI_DSE_CALLBACK_ERROR;
+ slapi_ch_free((void **)&mypb->pb_seq_val);
+ slapi_pblock_destroy(mypb);
+ goto out;
+ }
+
+ /* thread successful -- don't free the pb, let the thread do that. */
+ return SLAPI_DSE_CALLBACK_OK;
+
+out:
+ if (task) {
+ destroy_task(1, task);
+ }
+ return rv;
+}
+
+
+static void task_restore_thread(void *arg)
+{
+ Slapi_PBlock *pb = (Slapi_PBlock *)arg;
+ Slapi_Task *task = pb->pb_task;
+ int rv;
+
+ task->task_work = 1;
+ task->task_progress = 0;
+ task->task_state = SLAPI_TASK_RUNNING;
+ slapi_task_status_changed(task);
+
+ slapi_task_log_notice(task, "Beginning restore to '%s'",
+ pb->pb_plugin->plg_name);
+ LDAPDebug(LDAP_DEBUG_ANY, "Beginning restore to '%s'\n",
+ pb->pb_plugin->plg_name, 0, 0);
+
+ rv = (*pb->pb_plugin->plg_archive2db)(pb);
+ if (rv != 0) {
+ slapi_task_log_notice(task, "Restore failed (error %d)", rv);
+ slapi_task_log_status(task, "Restore failed (error %d)", rv);
+ LDAPDebug(LDAP_DEBUG_ANY, "Restore failed (error %d)\n", rv, 0, 0);
+ } else {
+ slapi_task_log_notice(task, "Restore finished.");
+ slapi_task_log_status(task, "Restore finished.");
+ LDAPDebug(LDAP_DEBUG_ANY, "Restore finished.\n", 0, 0, 0);
+ }
+
+ task->task_progress = 1;
+ task->task_exitcode = rv;
+ task->task_state = SLAPI_TASK_FINISHED;
+ slapi_task_status_changed(task);
+
+ slapi_ch_free((void **)&pb->pb_seq_val);
+ slapi_pblock_destroy(pb);
+}
+
+static int task_restore_add(Slapi_PBlock *pb, Slapi_Entry *e,
+ Slapi_Entry *eAfter, int *returncode, char *returntext, void *arg)
+{
+ Slapi_Backend *be = NULL;
+ const char *cn;
+ const char *archive_dir = NULL;
+ const char *my_database_type = NULL;
+ const char *database_type = "ldbm database";
+ char *cookie = NULL;
+ int rv = SLAPI_DSE_CALLBACK_OK;
+ Slapi_PBlock *mypb = NULL;
+ Slapi_Task *task = NULL;
+ PRThread *thread = NULL;
+
+ *returncode = LDAP_SUCCESS;
+ if ((cn = fetch_attr(e, "cn", NULL)) == NULL) {
+ *returncode = LDAP_OBJECT_CLASS_VIOLATION;
+ rv = SLAPI_DSE_CALLBACK_ERROR;
+ goto out;
+ }
+
+ /* archive dir name */
+ if ((archive_dir = fetch_attr(e, "nsArchiveDir", NULL)) == NULL) {
+ *returncode = LDAP_OBJECT_CLASS_VIOLATION;
+ rv = SLAPI_DSE_CALLBACK_ERROR;
+ goto out;
+ }
+
+ /* database type */
+ my_database_type = fetch_attr(e, "nsDatabaseType", NULL);
+ if (NULL != my_database_type)
+ database_type = my_database_type;
+
+ /* get backend that has archive2db and the database type matches. */
+ cookie = NULL;
+ be = slapi_get_first_backend (&cookie);
+ while (be) {
+ if (NULL != be->be_database->plg_archive2db &&
+ !strcasecmp(database_type, be->be_database->plg_name))
+ break;
+
+ be = (backend *)slapi_get_next_backend (cookie);
+ }
+ if (NULL == be || NULL == be->be_database->plg_archive2db) {
+ LDAPDebug(LDAP_DEBUG_ANY,
+ "ERROR: no db2archive function defined.\n", 0, 0, 0);
+ *returncode = LDAP_UNWILLING_TO_PERFORM;
+ rv = SLAPI_DSE_CALLBACK_ERROR;
+ goto out;
+ }
+
+ /* refuse to do an export on pre-V3 plugins. plugin api V3 is the one
+ * for DS 5.0 where the import/export stuff changed a lot.
+ */
+ if (! SLAPI_PLUGIN_IS_V3(be->be_database)) {
+ LDAPDebug(LDAP_DEBUG_ANY, "can't perform an restore with pre-V3 "
+ "backend plugin %s\n", be->be_database->plg_name, 0, 0);
+ *returncode = LDAP_UNWILLING_TO_PERFORM;
+ rv = SLAPI_DSE_CALLBACK_ERROR;
+ goto out;
+ }
+
+ /* allocate new task now */
+ task = new_task(slapi_entry_get_ndn(e));
+ if (task == NULL) {
+ LDAPDebug(LDAP_DEBUG_ANY, "unable to allocate new task!\n", 0, 0, 0);
+ *returncode = LDAP_OPERATIONS_ERROR;
+ rv = SLAPI_DSE_CALLBACK_ERROR;
+ goto out;
+ }
+ task->task_state = SLAPI_TASK_SETUP;
+ task->task_work = 1;
+ task->task_progress = 0;
+
+ mypb = slapi_pblock_new();
+ if (mypb == NULL) {
+ *returncode = LDAP_OPERATIONS_ERROR;
+ rv = SLAPI_DSE_CALLBACK_ERROR;
+ goto out;
+ }
+ mypb->pb_seq_val = slapi_ch_strdup(archive_dir);
+ mypb->pb_plugin = be->be_database;
+ mypb->pb_task = task;
+ mypb->pb_task_flags = TASK_RUNNING_AS_TASK;
+
+ /* start the restore as a separate thread */
+ thread = PR_CreateThread(PR_USER_THREAD, task_restore_thread,
+ (void *)mypb, PR_PRIORITY_NORMAL, PR_GLOBAL_THREAD,
+ PR_UNJOINABLE_THREAD, SLAPD_DEFAULT_THREAD_STACKSIZE);
+ if (thread == NULL) {
+ LDAPDebug(LDAP_DEBUG_ANY,
+ "unable to create restore thread!\n", 0, 0, 0);
+ *returncode = LDAP_OPERATIONS_ERROR;
+ rv = SLAPI_DSE_CALLBACK_ERROR;
+ slapi_ch_free((void **)&mypb->pb_seq_val);
+ slapi_pblock_destroy(mypb);
+ goto out;
+ }
+
+ /* thread successful -- don't free the pb, let the thread do that. */
+ return SLAPI_DSE_CALLBACK_OK;
+
+out:
+ if (task) {
+ destroy_task(1, task);
+ }
+ return rv;
+}
+
+
+static void task_index_thread(void *arg)
+{
+ Slapi_PBlock *pb = (Slapi_PBlock *)arg;
+ Slapi_Task *task = pb->pb_task;
+ int rv;
+
+ task->task_work = 1;
+ task->task_progress = 0;
+ task->task_state = SLAPI_TASK_RUNNING;
+ slapi_task_status_changed(task);
+
+ rv = (*pb->pb_plugin->plg_db2index)(pb);
+ if (rv != 0) {
+ slapi_task_log_notice(task, "Index failed (error %d)", rv);
+ slapi_task_log_status(task, "Index failed (error %d)", rv);
+ LDAPDebug(LDAP_DEBUG_ANY, "Index failed (error %d)\n", rv, 0, 0);
+ }
+
+ task->task_progress = task->task_work;
+ task->task_exitcode = rv;
+ task->task_state = SLAPI_TASK_FINISHED;
+ slapi_task_status_changed(task);
+
+ charray_free(pb->pb_db2index_attrs);
+ slapi_ch_free((void **)&pb->pb_instance_name);
+ slapi_pblock_destroy(pb);
+}
+
+static int task_index_add(Slapi_PBlock *pb, Slapi_Entry *e,
+ Slapi_Entry *eAfter, int *returncode, char *returntext, void *arg)
+{
+ const char *instance_name;
+ const char *cn;
+ int rv = SLAPI_DSE_CALLBACK_OK;
+ Slapi_Backend *be = NULL;
+ Slapi_Task *task = NULL;
+ Slapi_Attr *attr;
+ Slapi_Value *val = NULL;
+ char **indexlist = NULL;
+ int idx;
+ Slapi_PBlock *mypb = NULL;
+ PRThread *thread = NULL;
+
+ *returncode = LDAP_SUCCESS;
+ if ((cn = fetch_attr(e, "cn", NULL)) == NULL) {
+ *returncode = LDAP_OBJECT_CLASS_VIOLATION;
+ rv = SLAPI_DSE_CALLBACK_ERROR;
+ goto out;
+ }
+ if ((instance_name = fetch_attr(e, "nsInstance", NULL)) == NULL) {
+ *returncode = LDAP_OBJECT_CLASS_VIOLATION;
+ rv = SLAPI_DSE_CALLBACK_ERROR;
+ goto out;
+ }
+
+ /* lookup the backend */
+ be = slapi_be_select_by_instance_name(instance_name);
+ if (be == NULL) {
+ LDAPDebug(LDAP_DEBUG_ANY, "can't import to nonexistent backend %s\n",
+ instance_name, 0, 0);
+ *returncode = LDAP_NO_SUCH_OBJECT;
+ return SLAPI_DSE_CALLBACK_ERROR;
+ }
+ if (be->be_database->plg_db2index == NULL) {
+ LDAPDebug(LDAP_DEBUG_ANY, "ERROR: no db2index function defined for "
+ "backend %s\n", be->be_database->plg_name, 0, 0);
+ *returncode = LDAP_UNWILLING_TO_PERFORM;
+ return SLAPI_DSE_CALLBACK_ERROR;
+ }
+
+ /* normal indexes */
+ if (slapi_entry_attr_find(e, "nsIndexAttribute", &attr) == 0) {
+ for (idx = slapi_attr_first_value(attr, &val);
+ idx >= 0; idx = slapi_attr_next_value(attr, idx, &val)) {
+ const char *indexname = slapi_value_get_string(val);
+ char *index = (char *)slapi_ch_malloc(strlen(indexname) + 2);
+
+ if (index != NULL) {
+ sprintf(index, "t%s", indexname);
+ charray_add(&indexlist, index);
+ }
+ }
+ }
+
+ /* vlv indexes */
+ if (slapi_entry_attr_find(e, "nsIndexVlvAttribute", &attr) == 0) {
+ for (idx = slapi_attr_first_value(attr, &val);
+ idx >= 0; idx = slapi_attr_next_value(attr, idx, &val)) {
+ const char *indexname = slapi_value_get_string(val);
+ char *index = (char *)slapi_ch_malloc(strlen(indexname) + 2);
+
+ if (index != NULL) {
+ sprintf(index, "T%s", indexname);
+ charray_add(&indexlist, index);
+ }
+ }
+ }
+
+ if (NULL == indexlist) {
+ LDAPDebug(LDAP_DEBUG_ANY, "no index is specified!\n", 0, 0, 0);
+ *returncode = LDAP_OPERATIONS_ERROR;
+ rv = SLAPI_DSE_CALLBACK_OK;
+ goto out;
+ }
+
+ /* allocate new task now */
+ task = new_task(slapi_entry_get_ndn(e));
+ if (task == NULL) {
+ LDAPDebug(LDAP_DEBUG_ANY, "unable to allocate new task!\n", 0, 0, 0);
+ *returncode = LDAP_OPERATIONS_ERROR;
+ rv = SLAPI_DSE_CALLBACK_ERROR;
+ goto out;
+ }
+ task->task_state = SLAPI_TASK_SETUP;
+ task->task_work = 1;
+ task->task_progress = 0;
+
+ mypb = slapi_pblock_new();
+ if (mypb == NULL) {
+ *returncode = LDAP_OPERATIONS_ERROR;
+ rv = SLAPI_DSE_CALLBACK_ERROR;
+ goto out;
+ }
+ mypb->pb_backend = be;
+ mypb->pb_plugin = be->be_database;
+ mypb->pb_instance_name = slapi_ch_strdup(instance_name);
+ mypb->pb_db2index_attrs = indexlist;
+ mypb->pb_task = task;
+ mypb->pb_task_flags = TASK_RUNNING_AS_TASK;
+
+ /* start the db2index as a separate thread */
+ thread = PR_CreateThread(PR_USER_THREAD, task_index_thread,
+ (void *)mypb, PR_PRIORITY_NORMAL, PR_GLOBAL_THREAD,
+ PR_UNJOINABLE_THREAD, SLAPD_DEFAULT_THREAD_STACKSIZE);
+ if (thread == NULL) {
+ LDAPDebug(LDAP_DEBUG_ANY,
+ "unable to create index thread!\n", 0, 0, 0);
+ rv = SLAPI_DSE_CALLBACK_ERROR;
+ slapi_ch_free((void **)&mypb->pb_instance_name);
+ slapi_pblock_destroy(mypb);
+ goto out;
+ }
+
+ /* thread successful -- don't free the pb, let the thread do that. */
+ return SLAPI_DSE_CALLBACK_OK;
+
+out:
+ if (task) {
+ destroy_task(1, task);
+ }
+ if (indexlist) {
+ charray_free(indexlist);
+ }
+ return rv;
+}
+
+#if defined(UPGRADEDB)
+static int
+task_upgradedb_add(Slapi_PBlock *pb, Slapi_Entry *e, Slapi_Entry *eAfter,
+ int *returncode, char *returntext, void *arg)
+{
+ const char *cn;
+ int rv = SLAPI_DSE_CALLBACK_OK;
+ Slapi_Backend *be = NULL;
+ Slapi_Task *task = NULL;
+ Slapi_PBlock mypb;
+ PRThread *thread = NULL;
+ const char *archive_dir = NULL;
+ const char *force = NULL;
+ const char *database_type = "ldbm database";
+ const char *my_database_type = NULL;
+ char *cookie = NULL;
+
+ *returncode = LDAP_SUCCESS;
+ if ((cn = fetch_attr(e, "cn", NULL)) == NULL) {
+ *returncode = LDAP_OBJECT_CLASS_VIOLATION;
+ rv = SLAPI_DSE_CALLBACK_ERROR;
+ goto out;
+ }
+
+ /* archive dir name */
+ if ((archive_dir = fetch_attr(e, "nsArchiveDir", NULL)) == NULL) {
+ *returncode = LDAP_OBJECT_CLASS_VIOLATION;
+ rv = SLAPI_DSE_CALLBACK_ERROR;
+ goto out;
+ }
+
+ /* database type */
+ my_database_type = fetch_attr(e, "nsDatabaseType", NULL);
+ if (NULL != my_database_type)
+ database_type = my_database_type;
+
+ /* force to reindex? */
+ force = fetch_attr(e, "nsForceToReindex", NULL);
+
+ /* get backend that has db2archive and the database type matches. */
+ cookie = NULL;
+ be = slapi_get_first_backend(&cookie);
+ while (be) {
+ if (NULL != be->be_database->plg_upgradedb)
+ break;
+
+ be = (backend *)slapi_get_next_backend (cookie);
+ }
+ if (NULL == be || NULL == be->be_database->plg_upgradedb ||
+ strcasecmp(database_type, be->be_database->plg_name)) {
+ LDAPDebug(LDAP_DEBUG_ANY,
+ "ERROR: no upgradedb is defined in %s.\n",
+ be->be_database->plg_name, 0, 0);
+ *returncode = LDAP_UNWILLING_TO_PERFORM;
+ rv = SLAPI_DSE_CALLBACK_ERROR;
+ goto out;
+ }
+
+ /* allocate new task now */
+ task = new_task(slapi_entry_get_ndn(e));
+ if (task == NULL) {
+ LDAPDebug(LDAP_DEBUG_ANY, "unable to allocate new task!\n", 0, 0, 0);
+ *returncode = LDAP_OPERATIONS_ERROR;
+ rv = SLAPI_DSE_CALLBACK_ERROR;
+ goto out;
+ }
+ task->task_state = SLAPI_TASK_SETUP;
+ task->task_work = 1;
+ task->task_progress = 0;
+
+ memset(&mypb, 0, sizeof(mypb));
+ mypb.pb_backend = be;
+ mypb.pb_plugin = be->be_database;
+ if (force && 0 == strcasecmp(force, "true"))
+ mypb.pb_seq_type = SLAPI_UPGRADEDB_FORCE; /* force; reindex all regardless the dbversion */
+ mypb.pb_seq_val = slapi_ch_strdup(archive_dir);
+ mypb.pb_task = task;
+ mypb.pb_task_flags = TASK_RUNNING_AS_TASK;
+
+ rv = (mypb.pb_plugin->plg_upgradedb)(&mypb);
+ if (rv == 0) {
+ slapi_entry_attr_set_charptr(e, TASK_LOG_NAME, "");
+ slapi_entry_attr_set_charptr(e, TASK_STATUS_NAME, "");
+ slapi_entry_attr_set_int(e, TASK_PROGRESS_NAME, task->task_progress);
+ slapi_entry_attr_set_int(e, TASK_WORK_NAME, task->task_work);
+ }
+
+out:
+ if (rv != 0) {
+ if (task)
+ destroy_task(1, task);
+
+ *returncode = LDAP_OPERATIONS_ERROR;
+ return SLAPI_DSE_CALLBACK_ERROR;
+ }
+
+ *returncode = LDAP_SUCCESS;
+ return SLAPI_DSE_CALLBACK_OK;
+}
+#endif
+
+/* update attributes in the entry under "cn=tasks" to match the current
+ * status of the task.
+ */
+#define NEXTMOD(_type, _val) do { \
+ modlist[cur].mod_op = LDAP_MOD_REPLACE; \
+ modlist[cur].mod_type = (_type); \
+ modlist[cur].mod_values = (char **)slapi_ch_malloc(2*sizeof(char *)); \
+ modlist[cur].mod_values[0] = (_val); \
+ modlist[cur].mod_values[1] = NULL; \
+ mod[cur] = &modlist[cur]; \
+ cur++; \
+} while (0)
+void slapi_task_status_changed(Slapi_Task *task)
+{
+ LDAPMod modlist[20];
+ LDAPMod *mod[20];
+ int cur = 0, i;
+ char s1[20], s2[20], s3[20];
+
+ if (shutting_down) {
+ /* don't care about task status updates anymore */
+ return;
+ }
+
+ NEXTMOD(TASK_LOG_NAME, task->task_log);
+ NEXTMOD(TASK_STATUS_NAME, task->task_status);
+ sprintf(s1, "%d", task->task_exitcode);
+ sprintf(s2, "%d", task->task_progress);
+ sprintf(s3, "%d", task->task_work);
+ NEXTMOD(TASK_PROGRESS_NAME, s2);
+ NEXTMOD(TASK_WORK_NAME, s3);
+ /* only add the exit code when the job is done */
+ if ((task->task_state == SLAPI_TASK_FINISHED) ||
+ (task->task_state == SLAPI_TASK_CANCELLED)) {
+ NEXTMOD(TASK_EXITCODE_NAME, s1);
+ /* make sure the console can tell the task has ended */
+ if (task->task_progress != task->task_work) {
+ task->task_progress = task->task_work;
+ }
+ }
+
+ mod[cur] = NULL;
+ modify_internal_entry(task->task_dn, mod);
+
+ for (i = 0; i < cur; i++)
+ slapi_ch_free((void **)&modlist[i].mod_values);
+
+ if ((task->task_state == SLAPI_TASK_FINISHED) &&
+ !(task->task_flags & SLAPI_TASK_DESTROYING)) {
+ /* queue an event to destroy the state info */
+ Slapi_Eq_Context event;
+ Slapi_PBlock *pb = slapi_pblock_new();
+ Slapi_Entry *e;
+ int ttl;
+ time_t expire;
+
+ e = get_internal_entry(pb, task->task_dn);
+ if (e == NULL)
+ return;
+ ttl = atoi(fetch_attr(e, "ttl", DEFAULT_TTL));
+ if (ttl > 3600)
+ ttl = 3600; /* be reasonable. */
+ expire = time(NULL) + ttl;
+ task->task_flags |= SLAPI_TASK_DESTROYING;
+ event = slapi_eq_once(destroy_task, (void *)task, expire);
+
+ slapi_free_search_results_internal(pb);
+ slapi_pblock_destroy(pb);
+ }
+}
+
+
+/* cleanup old tasks that may still be in the DSE from a previous session
+ * (this can happen if the server crashes [no matter how unlikely we like
+ * to think that is].)
+ */
+static void cleanup_old_tasks(void)
+{
+ Slapi_PBlock *pb = slapi_pblock_new();
+ Slapi_Entry **entries = NULL;
+ int ret = 0, i, x;
+ Slapi_DN *rootDN;
+
+ slapi_search_internal_set_pb(pb, TASK_BASE_DN, LDAP_SCOPE_SUBTREE,
+ "(objectclass=*)", NULL, 0, NULL, NULL,
+ (void *)plugin_get_default_component_id(), 0);
+ slapi_search_internal_pb(pb);
+ slapi_pblock_get(pb, SLAPI_PLUGIN_INTOP_RESULT, &ret);
+ if (ret != LDAP_SUCCESS) {
+ LDAPDebug(LDAP_DEBUG_ANY, "WARNING: entire cn=tasks tree seems to "
+ "be AWOL!\n", 0, 0, 0);
+ slapi_pblock_destroy(pb);
+ return;
+ }
+
+ slapi_pblock_get(pb, SLAPI_PLUGIN_INTOP_SEARCH_ENTRIES, &entries);
+ if (NULL == entries) {
+ LDAPDebug(LDAP_DEBUG_ANY, "WARNING: entire cn=tasks tree seems to "
+ "be AWOL!\n", 0, 0, 0);
+ slapi_pblock_destroy(pb);
+ return;
+ }
+
+ rootDN = slapi_sdn_new_dn_byval(TASK_BASE_DN);
+
+ /* rotate through entries, skipping the base dn */
+ for (i = 0; entries[i] != NULL; i++) {
+ const Slapi_DN *sdn = slapi_entry_get_sdn_const(entries[i]);
+ Slapi_PBlock *mypb;
+ Slapi_Operation *op;
+
+ if (slapi_sdn_compare(sdn, rootDN) == 0)
+ continue;
+
+ mypb = slapi_pblock_new();
+ if (mypb == NULL) {
+ continue;
+ }
+ slapi_delete_internal_set_pb(mypb, slapi_sdn_get_dn(sdn), NULL, NULL,
+ plugin_get_default_component_id(), 0);
+
+ /* Make sure these deletes don't appear in the audit and change logs */
+ slapi_pblock_get(mypb, SLAPI_OPERATION, &op);
+ operation_set_flag(op, OP_FLAG_ACTION_NOLOG);
+
+ x = 1;
+ slapi_pblock_set(mypb, SLAPI_DSE_DONT_WRITE_WHEN_ADDING, &x);
+ slapi_delete_internal_pb(mypb);
+ slapi_pblock_destroy(mypb);
+ }
+
+ slapi_sdn_free(&rootDN);
+ slapi_free_search_results_internal(pb);
+ slapi_pblock_destroy(pb);
+}
+
+/* name is, for exmaple, "import" */
+int slapi_task_register_handler(const char *name, dseCallbackFn func)
+{
+ char *dn = NULL;
+ Slapi_PBlock *pb = NULL;
+ Slapi_Operation *op;
+ LDAPMod *mods[3];
+ LDAPMod mod[3];
+ const char *objectclass[3];
+ const char *cnvals[2];
+ int ret = -1;
+ int x;
+
+ dn = slapi_ch_malloc(strlen(name) + strlen(TASK_BASE_DN) + 20);
+ if (dn == NULL) {
+ goto out;
+ }
+ sprintf(dn, "cn=%s, %s", name, TASK_BASE_DN);
+
+ pb = slapi_pblock_new();
+ if (pb == NULL) {
+ goto out;
+ }
+
+ /* this is painful :( */
+ mods[0] = &mod[0];
+ mod[0].mod_op = LDAP_MOD_ADD;
+ mod[0].mod_type = "objectClass";
+ mod[0].mod_values = (char **)objectclass;
+ objectclass[0] = "top";
+ objectclass[1] = "extensibleObject";
+ objectclass[2] = NULL;
+ mods[1] = &mod[1];
+ mod[1].mod_op = LDAP_MOD_ADD;
+ mod[1].mod_type = "cn";
+ mod[1].mod_values = (char **)cnvals;
+ cnvals[0] = name;
+ cnvals[1] = NULL;
+ mods[2] = NULL;
+ slapi_add_internal_set_pb(pb, dn, mods, NULL,
+ plugin_get_default_component_id(), 0);
+ x = 1;
+ slapi_pblock_set(pb, SLAPI_DSE_DONT_WRITE_WHEN_ADDING, &x);
+ /* Make sure these adds don't appear in the audit and change logs */
+ slapi_pblock_get(pb, SLAPI_OPERATION, &op);
+ operation_set_flag(op, OP_FLAG_ACTION_NOLOG);
+
+ slapi_add_internal_pb(pb);
+ slapi_pblock_get(pb, SLAPI_PLUGIN_INTOP_RESULT, &x);
+ if ((x != LDAP_SUCCESS) && (x != LDAP_ALREADY_EXISTS)) {
+ LDAPDebug(LDAP_DEBUG_ANY,
+ "Can't create task node '%s' (error %d)\n",
+ name, x, 0);
+ ret = x;
+ goto out;
+ }
+
+ /* register add callback */
+ slapi_config_register_callback(SLAPI_OPERATION_ADD, DSE_FLAG_PREOP,
+ dn, LDAP_SCOPE_SUBTREE, "(objectclass=*)", func, NULL);
+ /* deny modify/delete of the root task entry */
+ slapi_config_register_callback(SLAPI_OPERATION_MODIFY, DSE_FLAG_PREOP,
+ dn, LDAP_SCOPE_BASE, "(objectclass=*)", task_deny, NULL);
+ slapi_config_register_callback(SLAPI_OPERATION_DELETE, DSE_FLAG_PREOP,
+ dn, LDAP_SCOPE_BASE, "(objectclass=*)", task_deny, NULL);
+
+ ret = 0;
+
+out:
+ if (dn) {
+ slapi_ch_free((void **)&dn);
+ }
+ if (pb) {
+ slapi_pblock_destroy(pb);
+ }
+ return ret;
+}
+
+
+void task_init(void)
+{
+ global_task_lock = PR_NewLock();
+ if (global_task_lock == NULL) {
+ LDAPDebug(LDAP_DEBUG_ANY, "unable to create global tasks lock! "
+ "(that's bad)\n", 0, 0, 0);
+ return;
+ }
+
+ cleanup_old_tasks();
+
+ slapi_task_register_handler("import", task_import_add);
+ slapi_task_register_handler("export", task_export_add);
+ slapi_task_register_handler("backup", task_backup_add);
+ slapi_task_register_handler("restore", task_restore_add);
+ slapi_task_register_handler("index", task_index_add);
+#if defined(UPGRADEDB)
+ slapi_task_register_handler("upgradedb", task_upgradedb_add);
+#endif
+}
+
+/* called when the server is shutting down -- abort all existing tasks */
+void task_shutdown(void)
+{
+ Slapi_Task *task;
+ int found_any = 0;
+
+ /* first, cancel all tasks */
+ PR_Lock(global_task_lock);
+ shutting_down = 1;
+ for (task = global_task_list; task; task = task->next) {
+ if ((task->task_state != SLAPI_TASK_CANCELLED) &&
+ (task->task_state != SLAPI_TASK_FINISHED)) {
+ task->task_state = SLAPI_TASK_CANCELLED;
+ if (task->cancel) {
+ LDAPDebug(LDAP_DEBUG_ANY, "Cancelling task '%s'\n",
+ task->task_dn, 0, 0);
+ (*task->cancel)(task);
+ found_any = 1;
+ }
+ }
+ }
+
+ if (found_any) {
+ /* give any tasks 1 second to say their last rites */
+ DS_Sleep(PR_SecondsToInterval( 1 ));
+ }
+
+ while (global_task_list) {
+ destroy_task(0, global_task_list);
+ }
+ PR_Unlock(global_task_lock);
+}
diff --git a/ldap/servers/slapd/tempnam.c b/ldap/servers/slapd/tempnam.c
new file mode 100644
index 00000000..00c7c2fc
--- /dev/null
+++ b/ldap/servers/slapd/tempnam.c
@@ -0,0 +1,44 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+#if defined( nextstep )
+
+#include <string.h>
+
+char *tempnam( char *dir, char *pfx );
+
+char *tempnam( char *dir, char *pfx )
+{
+ char *s;
+
+ if ( dir == NULL ) {
+ dir = "/tmp";
+ }
+
+/*
+ * allocate space for dir + '/' + pfx (up to 5 chars) + 6 trailing 'X's + 0 byte
+ */
+ if (( s = (char *)slapi_ch_malloc( strlen( dir ) + 14 )) == NULL ) {
+ return( NULL );
+ }
+
+ strcpy( s, dir );
+ strcat( s, "/" );
+ if ( pfx != NULL ) {
+ strcat( s, pfx );
+ }
+ strcat( s, "XXXXXX" );
+ mktemp( s );
+
+ if ( *s == '\0' ) {
+ slapi_ch_free( (void**)&s );
+ }
+
+ return( s );
+}
+
+#else /* nextstep */
+typedef int SHUT_UP_DAMN_COMPILER;
+#endif /* nextstep */
diff --git a/ldap/servers/slapd/test-plugins/Makefile b/ldap/servers/slapd/test-plugins/Makefile
new file mode 100644
index 00000000..8b78aada
--- /dev/null
+++ b/ldap/servers/slapd/test-plugins/Makefile
@@ -0,0 +1,51 @@
+#
+# BEGIN COPYRIGHT BLOCK
+# Copyright 2001 Sun Microsystems, Inc.
+# Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+# All rights reserved.
+# END COPYRIGHT BLOCK
+#
+#
+# GNU Makefile for Directory Server test-plugin
+#
+
+LDAP_SRC = ../../..
+MCOM_ROOT = ../../../../..
+
+NOSTDCLEAN=true # don't let nsconfig.mk define target clean
+NOSTDSTRIP=true # don't let nsconfig.mk define target strip
+NSPR20=true # probably should be defined somewhere else (not sure where)
+
+OBJDEST = $(OBJDIR)/servers/obj
+BINDIR = $(OBJDIR)/bin
+LIBDIR = $(OBJDIR)/lib
+
+include $(MCOM_ROOT)/ldapserver/nsdefs.mk
+include $(MCOM_ROOT)/ldapserver/nsconfig.mk
+include $(LDAP_SRC)/nsldap.mk
+include $(MCOM_ROOT)/ldapserver/ns_usepurify.mk
+include $(MCOM_ROOT)/ldapserver/ns_usedb.mk
+
+
+EXTRA_INCLUDES=-I.. -I../../../include $(NSPR_INCLUDE) -I$(LDAP_INCLUDE) -I$(DB_INCLUDE) -I../../../servers/slapd -g
+INCLUDE_FLAGS=-I../include $(EXTRA_INCLUDES)
+EXTRA_LIBS = $(DB_LIB)
+
+ifdef USE_64
+MAKEFILE_FILE=Makefile.$(BUILD_ARCH)64
+else
+MAKEFILE_FILE=Makefile.$(BUILD_ARCH)
+endif
+
+all:
+ $(MAKE) -f $(MAKEFILE_FILE) all INCLUDE_FLAGS="$(INCLUDE_FLAGS)" EXTRA_LIBS="$(EXTRA_LIBS)"
+
+libtest-plugin.so:
+ $(MAKE) -f $(MAKEFILE_FILE) libtest-plugin.so \
+ INCLUDE_FLAGS="$(INCLUDE_FLAGS)"
+
+clean:
+ $(MAKE) -f $(MAKEFILE_FILE) clean INCLUDE_FLAGS="$(INCLUDE_FLAGS)"
+
+veryclean: clean
+
diff --git a/ldap/servers/slapd/test-plugins/Makefile.AIX b/ldap/servers/slapd/test-plugins/Makefile.AIX
new file mode 100644
index 00000000..31a64146
--- /dev/null
+++ b/ldap/servers/slapd/test-plugins/Makefile.AIX
@@ -0,0 +1,36 @@
+#
+# BEGIN COPYRIGHT BLOCK
+# Copyright 2001 Sun Microsystems, Inc.
+# Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+# All rights reserved.
+# END COPYRIGHT BLOCK
+#
+# AIX Makefile for Directory Server plug-in examples
+
+INCLUDE_FLAGS= -I../include
+CFLAGS= $(INCLUDE_FLAGS) -D_THREAD_SAFE -qansialias -langlvl=ansi -qro -qroconst -qarch=com
+LIBPATH=/usr/lib/threads:/usr/lpp/xlC/lib:/usr/lib:/lib:..:../../../../lib
+EXTRA_LIBS= -bI:/usr/lib/lowsys.exp -lC_r -lC -lpthreads -lc_r -lm \
+ /usr/lib/libc.a
+LDFLAGS= -bE:libtest-plugin_shr.exp -G -bnoentry -blibpath:$(LIBPATH) \
+ $(EXTRA_LIBS)
+
+OBJS = testsaslbind.o testextendedop.o testpreop.o testpostop.o testentry.o testbind.o testgetip.o testdatainterop.o testdbinterop.o
+
+all: libtest-plugin.so
+
+
+libtest-plugin.so: $(OBJS)
+ rm -f libtest-plugin_shr.exp
+ echo "#!" > libtest-plugin_shr.exp
+ nm -B -C -g $(OBJS) | \
+ awk '/ [B,T,D] / {print $$3}' | \
+ sed -e 's/^\.//' | sort -u >> libtest-plugin_shr.exp
+ $(LD) $(LDFLAGS) -o $@ $(OBJS)
+
+.c.o:
+ $(CC) $(CFLAGS) -c $<
+
+clean:
+ -rm -f $(OBJS) libtest-plugin.so libtest-plugin_shr.exp
+
diff --git a/ldap/servers/slapd/test-plugins/Makefile.BSDI b/ldap/servers/slapd/test-plugins/Makefile.BSDI
new file mode 100644
index 00000000..0433e752
--- /dev/null
+++ b/ldap/servers/slapd/test-plugins/Makefile.BSDI
@@ -0,0 +1,31 @@
+#
+# BEGIN COPYRIGHT BLOCK
+# Copyright 2001 Sun Microsystems, Inc.
+# Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+# All rights reserved.
+# END COPYRIGHT BLOCK
+#
+# SOLARIS Makefile for Directory Server plug-in examples
+#
+
+CC = cc
+LD = ld
+
+INCLUDE_FLAGS = -I../include
+CFLAGS = $(INCLUDE_FLAGS) -D_REENTRANT -KPIC
+LDFLAGS = -G
+
+OBJS = testsaslbind.o testextendedop.o testpreop.o testpostop.o testentry.o testbind.o testgetip.o testdatainterop.o testdbinterop.o
+
+all: libtest-plugin.so
+
+
+libtest-plugin.so: $(OBJS)
+ $(LD) $(LDFLAGS) -o $@ $(OBJS)
+
+.c.o:
+ $(CC) $(CFLAGS) -c $<
+
+clean:
+ -rm -f $(OBJS) libtest-plugin.so
+
diff --git a/ldap/servers/slapd/test-plugins/Makefile.HPUX b/ldap/servers/slapd/test-plugins/Makefile.HPUX
new file mode 100644
index 00000000..06df5a21
--- /dev/null
+++ b/ldap/servers/slapd/test-plugins/Makefile.HPUX
@@ -0,0 +1,25 @@
+#
+# BEGIN COPYRIGHT BLOCK
+# Copyright 2001 Sun Microsystems, Inc.
+# Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+# All rights reserved.
+# END COPYRIGHT BLOCK
+#
+# HPUX Makefile for Directory Server plug-in examples
+
+INCLUDE = -I../include
+CFLAGS=$(INCLUDE) -D_HPUX_SOURCE -Aa +z +DAportable -Ae
+LDFLAGS = -b
+
+OBJS = testsaslbind.o testpreop.o testpostop.o testextendedop.o testentry.o testbind.o testgetip.o testdatainterop.o testdbinterop.o
+
+all: libtest-plugin.sl
+
+libtest-plugin.sl: $(OBJS)
+ $(LD) $(LDFLAGS) -o $@ $(OBJS)
+
+.c.o:
+ $(CC) $(CFLAGS) -c $<
+
+clean:
+ -rm -f $(OBJS) libtest-plugin.sl
diff --git a/ldap/servers/slapd/test-plugins/Makefile.HPUX64 b/ldap/servers/slapd/test-plugins/Makefile.HPUX64
new file mode 100644
index 00000000..2e9fc329
--- /dev/null
+++ b/ldap/servers/slapd/test-plugins/Makefile.HPUX64
@@ -0,0 +1,24 @@
+#
+# BEGIN COPYRIGHT BLOCK
+# Copyright 2001 Sun Microsystems, Inc.
+# Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+# All rights reserved.
+# END COPYRIGHT BLOCK
+#
+# HPUX Makefile for Directory Server plug-in examples
+
+CFLAGS=$(INCLUDE_FLAGS) -D_HPUX_SOURCE -Aa +z +DA2.0W -Ae
+LDFLAGS = +k -b +s $(EXTRA_LIBS)
+
+OBJS = testsaslbind.o testpreop.o testpostop.o testextendedop.o testentry.o testbind.o testgetip.o testdatainterop.o testdbinterop.o
+
+all: libtest-plugin.sl
+
+libtest-plugin.sl: $(OBJS)
+ $(LD) $(LDFLAGS) -o $@ $(OBJS)
+
+.c.o:
+ $(CC) $(CFLAGS) -c $<
+
+clean:
+ -rm -f $(OBJS) libtest-plugin.sl
diff --git a/ldap/servers/slapd/test-plugins/Makefile.IRIX b/ldap/servers/slapd/test-plugins/Makefile.IRIX
new file mode 100644
index 00000000..820e5d1b
--- /dev/null
+++ b/ldap/servers/slapd/test-plugins/Makefile.IRIX
@@ -0,0 +1,31 @@
+#
+# BEGIN COPYRIGHT BLOCK
+# Copyright 2001 Sun Microsystems, Inc.
+# Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+# All rights reserved.
+# END COPYRIGHT BLOCK
+#
+# IRIX Makefile for Directory Server plug-in examples
+#
+
+CC = cc
+LD = ld
+
+INCLUDE_FLAGS = -I../include
+CFLAGS = $(INCLUDE_FLAGS) -D_SGI_MP_SOURCE -fullwarn
+LDFLAGS = -32 -shared
+
+OBJS = testsaslbind.o testextendedop.o testpreop.o testpostop.o testentry.o testbind.o testgetip.o testdatainterop.o testdbinterop.o
+
+all: libtest-plugin.so
+
+
+libtest-plugin.so: $(OBJS)
+ $(LD) $(LDFLAGS) -o $@ $(OBJS)
+
+.c.o:
+ $(CC) $(CFLAGS) -c $<
+
+clean:
+ -rm -f $(OBJS) libtest-plugin.so
+
diff --git a/ldap/servers/slapd/test-plugins/Makefile.Linux b/ldap/servers/slapd/test-plugins/Makefile.Linux
new file mode 100644
index 00000000..1e3de457
--- /dev/null
+++ b/ldap/servers/slapd/test-plugins/Makefile.Linux
@@ -0,0 +1,31 @@
+#
+# BEGIN COPYRIGHT BLOCK
+# Copyright 2001 Sun Microsystems, Inc.
+# Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+# All rights reserved.
+# END COPYRIGHT BLOCK
+#
+# Linux Makefile for Directory Server plug-in examples
+#
+
+CC = gcc
+LD = ld
+
+INCLUDE_FLAGS = -I../include
+CFLAGS = $(INCLUDE_FLAGS) -D_REENTRANT -fPIC
+LDFLAGS = -G
+
+OBJS = testsaslbind.o testextendedop.o testpreop.o testpostop.o testentry.o testbind.o testgetip.o testdatainterop.o testdbinterop.o
+
+all: libtest-plugin.so
+
+
+libtest-plugin.so: $(OBJS)
+ $(LD) $(LDFLAGS) -o $@ $(OBJS)
+
+.c.o:
+ $(CC) $(CFLAGS) -c $<
+
+clean:
+ -rm -f $(OBJS) libtest-plugin.so
+
diff --git a/ldap/servers/slapd/test-plugins/Makefile.OSF1 b/ldap/servers/slapd/test-plugins/Makefile.OSF1
new file mode 100644
index 00000000..a1765d2e
--- /dev/null
+++ b/ldap/servers/slapd/test-plugins/Makefile.OSF1
@@ -0,0 +1,30 @@
+#
+# BEGIN COPYRIGHT BLOCK
+# Copyright 2001 Sun Microsystems, Inc.
+# Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+# All rights reserved.
+# END COPYRIGHT BLOCK
+#
+# OSF1 Makefile for Directory Server plug-in examples
+
+CC = cc
+LD = ld
+
+INCLUDE = -I../include
+CFLAGS = $(INCLUDE) -DIS_64 -ieee_with_inexact -pthread -DOSF1
+LDFLAGS = -shared -all -expect_unresolved "*" -taso
+
+
+OBJS = testsaslbind.o testpreop.o testpostop.o testextendedop.o testentry.o testbind.o testgetip.o testdatainterop.o testdbinterop.o
+
+all: libtest-plugin.so
+
+libtest-plugin.so: $(OBJS)
+ $(LD) $(LDFLAGS) -o $@ $(OBJS)
+
+.c.o:
+ $(CC) $(CFLAGS) -c $<
+
+clean:
+ -rm -f $(OBJS) libtest-plugin.so
+
diff --git a/ldap/servers/slapd/test-plugins/Makefile.ReliantUNIX b/ldap/servers/slapd/test-plugins/Makefile.ReliantUNIX
new file mode 100644
index 00000000..0433e752
--- /dev/null
+++ b/ldap/servers/slapd/test-plugins/Makefile.ReliantUNIX
@@ -0,0 +1,31 @@
+#
+# BEGIN COPYRIGHT BLOCK
+# Copyright 2001 Sun Microsystems, Inc.
+# Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+# All rights reserved.
+# END COPYRIGHT BLOCK
+#
+# SOLARIS Makefile for Directory Server plug-in examples
+#
+
+CC = cc
+LD = ld
+
+INCLUDE_FLAGS = -I../include
+CFLAGS = $(INCLUDE_FLAGS) -D_REENTRANT -KPIC
+LDFLAGS = -G
+
+OBJS = testsaslbind.o testextendedop.o testpreop.o testpostop.o testentry.o testbind.o testgetip.o testdatainterop.o testdbinterop.o
+
+all: libtest-plugin.so
+
+
+libtest-plugin.so: $(OBJS)
+ $(LD) $(LDFLAGS) -o $@ $(OBJS)
+
+.c.o:
+ $(CC) $(CFLAGS) -c $<
+
+clean:
+ -rm -f $(OBJS) libtest-plugin.so
+
diff --git a/ldap/servers/slapd/test-plugins/Makefile.SOLARIS b/ldap/servers/slapd/test-plugins/Makefile.SOLARIS
new file mode 100644
index 00000000..1914f3b2
--- /dev/null
+++ b/ldap/servers/slapd/test-plugins/Makefile.SOLARIS
@@ -0,0 +1,28 @@
+#
+# BEGIN COPYRIGHT BLOCK
+# Copyright 2001 Sun Microsystems, Inc.
+# Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+# All rights reserved.
+# END COPYRIGHT BLOCK
+#
+# SOLARIS Makefile for Directory Server plug-in examples
+#
+
+INCLUDE_FLAGS = -I../include
+CFLAGS = $(INCLUDE_FLAGS) -D_REENTRANT -KPIC
+LDFLAGS = -G
+
+OBJS = testsaslbind.o testextendedop.o testpreop.o testpostop.o testentry.o testbind.o testgetip.o testdatainterop.o testdbinterop.o
+
+all: libtest-plugin.so
+
+
+libtest-plugin.so: $(OBJS)
+ $(LD) $(LDFLAGS) -o $@ $(OBJS)
+
+.c.o:
+ $(CC) $(CFLAGS) -c $<
+
+clean:
+ -rm -f $(OBJS) libtest-plugin.so
+
diff --git a/ldap/servers/slapd/test-plugins/Makefile.SOLARIS64 b/ldap/servers/slapd/test-plugins/Makefile.SOLARIS64
new file mode 100644
index 00000000..151ea5b1
--- /dev/null
+++ b/ldap/servers/slapd/test-plugins/Makefile.SOLARIS64
@@ -0,0 +1,28 @@
+#
+# BEGIN COPYRIGHT BLOCK
+# Copyright 2001 Sun Microsystems, Inc.
+# Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+# All rights reserved.
+# END COPYRIGHT BLOCK
+#
+# SOLARIS Makefile for Directory Server plug-in examples
+#
+
+INCLUDE_FLAGS = -I../include
+CFLAGS = $(INCLUDE_FLAGS) -xarch=v9 -D_REENTRANT -KPIC
+LDFLAGS = -G -xarch=v9
+
+OBJS = testsaslbind.o testextendedop.o testpreop.o testpostop.o testentry.o testbind.o testgetip.o testdatainterop.o testdbinterop.o
+
+all: libtest-plugin.so
+
+
+libtest-plugin.so: $(OBJS)
+ $(LD) $(LDFLAGS) -o $@ $(OBJS)
+
+.c.o:
+ $(CC) $(CFLAGS) -c $<
+
+clean:
+ -rm -f $(OBJS) libtest-plugin.so
+
diff --git a/ldap/servers/slapd/test-plugins/Makefile.SOLARISx86 b/ldap/servers/slapd/test-plugins/Makefile.SOLARISx86
new file mode 100644
index 00000000..0433e752
--- /dev/null
+++ b/ldap/servers/slapd/test-plugins/Makefile.SOLARISx86
@@ -0,0 +1,31 @@
+#
+# BEGIN COPYRIGHT BLOCK
+# Copyright 2001 Sun Microsystems, Inc.
+# Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+# All rights reserved.
+# END COPYRIGHT BLOCK
+#
+# SOLARIS Makefile for Directory Server plug-in examples
+#
+
+CC = cc
+LD = ld
+
+INCLUDE_FLAGS = -I../include
+CFLAGS = $(INCLUDE_FLAGS) -D_REENTRANT -KPIC
+LDFLAGS = -G
+
+OBJS = testsaslbind.o testextendedop.o testpreop.o testpostop.o testentry.o testbind.o testgetip.o testdatainterop.o testdbinterop.o
+
+all: libtest-plugin.so
+
+
+libtest-plugin.so: $(OBJS)
+ $(LD) $(LDFLAGS) -o $@ $(OBJS)
+
+.c.o:
+ $(CC) $(CFLAGS) -c $<
+
+clean:
+ -rm -f $(OBJS) libtest-plugin.so
+
diff --git a/ldap/servers/slapd/test-plugins/Makefile.UnixWare b/ldap/servers/slapd/test-plugins/Makefile.UnixWare
new file mode 100644
index 00000000..0433e752
--- /dev/null
+++ b/ldap/servers/slapd/test-plugins/Makefile.UnixWare
@@ -0,0 +1,31 @@
+#
+# BEGIN COPYRIGHT BLOCK
+# Copyright 2001 Sun Microsystems, Inc.
+# Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+# All rights reserved.
+# END COPYRIGHT BLOCK
+#
+# SOLARIS Makefile for Directory Server plug-in examples
+#
+
+CC = cc
+LD = ld
+
+INCLUDE_FLAGS = -I../include
+CFLAGS = $(INCLUDE_FLAGS) -D_REENTRANT -KPIC
+LDFLAGS = -G
+
+OBJS = testsaslbind.o testextendedop.o testpreop.o testpostop.o testentry.o testbind.o testgetip.o testdatainterop.o testdbinterop.o
+
+all: libtest-plugin.so
+
+
+libtest-plugin.so: $(OBJS)
+ $(LD) $(LDFLAGS) -o $@ $(OBJS)
+
+.c.o:
+ $(CC) $(CFLAGS) -c $<
+
+clean:
+ -rm -f $(OBJS) libtest-plugin.so
+
diff --git a/ldap/servers/slapd/test-plugins/Makefile.UnixWareUDK b/ldap/servers/slapd/test-plugins/Makefile.UnixWareUDK
new file mode 100644
index 00000000..0433e752
--- /dev/null
+++ b/ldap/servers/slapd/test-plugins/Makefile.UnixWareUDK
@@ -0,0 +1,31 @@
+#
+# BEGIN COPYRIGHT BLOCK
+# Copyright 2001 Sun Microsystems, Inc.
+# Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+# All rights reserved.
+# END COPYRIGHT BLOCK
+#
+# SOLARIS Makefile for Directory Server plug-in examples
+#
+
+CC = cc
+LD = ld
+
+INCLUDE_FLAGS = -I../include
+CFLAGS = $(INCLUDE_FLAGS) -D_REENTRANT -KPIC
+LDFLAGS = -G
+
+OBJS = testsaslbind.o testextendedop.o testpreop.o testpostop.o testentry.o testbind.o testgetip.o testdatainterop.o testdbinterop.o
+
+all: libtest-plugin.so
+
+
+libtest-plugin.so: $(OBJS)
+ $(LD) $(LDFLAGS) -o $@ $(OBJS)
+
+.c.o:
+ $(CC) $(CFLAGS) -c $<
+
+clean:
+ -rm -f $(OBJS) libtest-plugin.so
+
diff --git a/ldap/servers/slapd/test-plugins/Makefile.WINNT b/ldap/servers/slapd/test-plugins/Makefile.WINNT
new file mode 100644
index 00000000..d18d7ea7
--- /dev/null
+++ b/ldap/servers/slapd/test-plugins/Makefile.WINNT
@@ -0,0 +1,45 @@
+#
+# BEGIN COPYRIGHT BLOCK
+# Copyright 2001 Sun Microsystems, Inc.
+# Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+# All rights reserved.
+# END COPYRIGHT BLOCK
+#
+# Makefile for Directory Server plug-in
+#
+
+CC = cl
+LD = link
+
+
+TARGET=testplugin
+
+OBJS=testsaslbind.obj testextendedop.obj testpreop.obj testpostop.obj testentry.obj testbind.obj testgetip.obj testdatainterop.obj testdbinterop.obj
+
+
+INC = ../include
+CFLAGS = /nologo -I $(INC) /c
+LDFLAGS = /dll /nologo
+LIBS=/DEFAULTLIB:kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib wsock32.lib ../lib/libslapd.lib ../lib/libnspr4.lib ./lib/libdb42.lib
+
+
+all: \
+ init \
+ $(TARGET).dll
+
+init:
+ "c:\program files\microsoft visual studio\vc98\bin\vcvars32.bat"
+
+
+$(TARGET).dll: $(OBJS)
+ $(LD) $(LDFLAGS) /def:$(TARGET).def /out:$(TARGET).dll $(EXTRA_LIBS) $(LIBS) $(OBJS)
+ -rm -f $(OBJS2) *~
+
+%.obj:%.c
+ $(CC) $(CFLAGS) $<
+
+clean:
+ del -f $(OBJS) $(TARGET).dll *~
+
+
+
diff --git a/ldap/servers/slapd/test-plugins/Makefile.server b/ldap/servers/slapd/test-plugins/Makefile.server
new file mode 100644
index 00000000..4923aa38
--- /dev/null
+++ b/ldap/servers/slapd/test-plugins/Makefile.server
@@ -0,0 +1,107 @@
+#
+# PROPRIETARY/CONFIDENTIAL. Use of this product is subject to
+# license terms. Copyright 2001 Sun Microsystems, Inc.
+# Some preexisting portions Copyright 2001 Netscape Communications Corp.
+# All rights reserved.
+#
+#
+# GNU Makefile for Directory Server distribution plugin
+#
+
+LDAP_SRC = ../../..
+MCOM_ROOT = ../../../../..
+
+NOSTDCLEAN=true # don't let nsconfig.mk define target clean
+NOSTDSTRIP=true # don't let nsconfig.mk define target strip
+NSPR20=true # probably should be defined somewhere else (not sure where)
+
+OBJDEST = $(OBJDIR)/lib/libtestplug
+LIBDIR = $(LIB_RELDIR)
+
+include $(MCOM_ROOT)/ldapserver/nsconfig.mk
+include $(LDAP_SRC)/nsldap.mk
+include $(MCOM_ROOT)/ldapserver/ns_usedb.mk
+
+ifeq ($(ARCH), WINNT)
+DEF_FILE:=./libdistrib.def
+endif
+
+CFLAGS+=$(SLCFLAGS)
+
+INCLUDES += -I$(LDAP_SRC)/servers/slapd -I$(DB_INCLUDE)
+
+DIS_OBJS= \
+ testsaslbind.o testpreop.o testpostop.o testextendedop.o testentry.o testbind.o testgetip.o testdatainterop.o testdbinterop.o
+
+OBJS = $(addprefix $(OBJDEST)/, $(DIS_OBJS))
+
+ifeq ($(ARCH), WINNT)
+LIBDIS_DLL_OBJ = $(addprefix $(OBJDEST)/, dllmain.o)
+endif
+
+# The sample distribution plugin is not part of DS.
+# So we generate the shared library outside of $(LIBDIR)
+# so that it's not retreived by the packaging makefiles.
+#LIBDIS = $(addprefix $(LIBDIR)/, $(DIS_DLL).$(DLL_SUFFIX))
+LIBDIS = $(addprefix $(OBJDEST)/, $(TEST_PLUGIN_DLL).$(DLL_SUFFIX))
+
+ifeq ($(ARCH), WINNT)
+EXTRA_LIBS_DEP += \
+ $(LIBSLAPD_DEP) \
+ $(LDAP_LIBUTIL_DEP) \
+ $(LDAP_COMMON_LIBS_DEP)
+EXTRA_LIBS_DEP += \
+ $(LDAPSDK_DEP) \
+ $(SECURITY_DEP)
+EXTRA_LIBS += \
+ $(LIBSLAPD) \
+ $(LDAP_SDK_LIBLDAP_DLL) \
+ $(LIBUTIL) \
+ $(NSPRLINK) \
+ $(LDAP_COMMON_LIBS)
+endif
+ifeq ($(ARCH), AIX)
+EXTRA_LIBS_DEP += \
+ $(LIBSLAPD_DEP) \
+ $(LDAP_LIBUTIL_DEP) \
+ $(LDAP_COMMON_LIBS_DEP)
+EXTRA_LIBS_DEP += \
+ $(LDAPSDK_DEP)
+EXTRA_LIBS += \
+ $(LIBSLAPDLINK) \
+ $(LDAP_SDK_LIBLDAP_DLL) \
+ $(LIBUTIL) \
+ $(NSPRLINK) \
+ $(LDAP_COMMON_LIBS)
+endif
+
+EXTRA_LIBS_DEP += $(LIBSLAPD_DEP) $(LDAPSDK_DEP) $(NSPR_DEP) $(DB_LIB_DEP)
+EXTRA_LIBS += $(DYN_NSHTTPD) $(ADMINUTIL_LINK) $(LDAPLINK) $(NSPRLINK) $(ICULINK) $(DB_LIB)
+
+ifeq ($(ARCH), WINNT)
+DLL_LDFLAGS += -def:"./libdistrib.def"
+CFLAGS+= /WX
+endif # WINNT
+
+ifeq ($(ARCH), AIX)
+LD=ld
+endif
+
+clientSDK:
+
+all: $(OBJDEST) $(LIBDIR) $(LIBDIS)
+
+$(LIBDIS): $(OBJS) $(LIBDIS_DLL_OBJ) $(DEF_FILE)
+ $(LINK_DLL) $(LIBDIS_DLL_OBJ) $(EXTRA_LIBS)
+
+veryclean: clean
+
+clean:
+ $(RM) $(OBJS)
+ifeq ($(ARCH), WINNT)
+ $(RM) $(LIBDIS_DLL_OBJ)
+endif
+ $(RM) $(LIBDIS)
+
+$(OBJDEST):
+ $(MKDIR) $(OBJDEST)
diff --git a/ldap/servers/slapd/test-plugins/README b/ldap/servers/slapd/test-plugins/README
new file mode 100644
index 00000000..ecdcac58
--- /dev/null
+++ b/ldap/servers/slapd/test-plugins/README
@@ -0,0 +1,149 @@
+# BEGIN COPYRIGHT BLOCK
+# Copyright 2001 Sun Microsystems, Inc.
+# Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+# All rights reserved.
+# END COPYRIGHT BLOCK
+#
+
+ ----------------------------
+ Sample Server Plug-Ins
+ for Directory Server 7
+ ----------------------------
+
+This directory contains code for some sample server plug-ins intended for
+use with the Netscape Directory Server 7.
+
+ NOTE: Before you compile and run these examples, make sure
+ to change any server-specific data in the examples to
+ values applicable to your Directory Server.
+
+testbind.c
+----------
+This is an example of a pre-operation bind plug-in function that
+handles authentication. When processing an LDAP bind request, the
+server calls this plug-in function before calling the database bind
+function.
+
+testentry.c
+-----------
+This is an example of an entry store plug-in function and an entry fetch
+plug-in function. You must be using the default database (not your own
+back-end database) in order for these plug-in functions to work.
+
+testextendedop.c
+----------------
+This is an example of an extended operation plug-in function that
+handles requests for the extended operation with the OID 1.2.3.4.
+The example should be used in conjunction with the reqextop.c and
+ReqExtOp.java clients (the source code for these clients is located
+in the clients subdirectory). These clients are capable of requesting
+the extended operation with the OID 1.2.3.4.
+
+testpostop.c
+------------
+This contains examples of post-operation plug-in functions. These
+functions are called after the server processes LDAP operations.
+The functions log changes to the directory in a change log file.
+
+testpreop.c
+-----------
+This contains examples of pre-operation plug-in functions. These
+functions are called before the server processes LDAP operations.
+
+testsaslbind.c
+--------------
+This is an example of a pre-operation plug-in function that
+implements a SASL mechanism.
+
+clients
+-------
+This directory contains the C and Java source code for clients
+that you can use to test the server plug-ins. See the README
+file in that directory for details.
+
+
+ ----------------------------
+ How To Create
+ A Server Plug-In
+ ----------------------------
+
+Text between brackets ([]) should be replaced with values specific to
+your situation.
+
+
+Creating the Plug-In Library
+----------------------------
+Server plug-ins are built as libraries available to the server.
+
+1. Include the Plug-In API. For example:
+
+ #include "[serverRoot]/plugins/slapd/slapi/include/slapi-plugin.h"
+
+2. Write your plug-in, including a top level initialization function
+ used by the server to start the plug-in. For example:
+
+ /* Plug-in functions defined here */
+
+ int my_plugin_init( Slapi_PBlock *pb ) /* initialize param. block */
+ {
+ /* Set or get the parameters in pb */
+ slapi_pblock_set();
+ slapi_pblock_get();
+
+ /* Plug-in functions registered here */
+
+ if (error)
+ {
+ slapi_log_error();
+ return error_code;
+ }
+ else return 0;
+
+ } /* my_plugin_init() */
+
+ See the Parameter Block Reference in the Netscape Directory Server
+ Plug-In Programmer's Guide for hints on plug-in types.
+
+3. Build the plug-in as a library.
+
+ We recommend you copy and adapt the Makefile in
+ [serverRoot]/plugins/slapd/slapi/examples.
+
+
+Plugging the Library Into the Server
+------------------------------------
+When started, the server loads plug-ins.
+
+1. Stop the server.
+
+ Console: Select the server; Object > Stop Server
+ Command Line: cd [serverRoot]/slapd-[serverID] ; ./stop-slapd
+
+2. Add the entry for the server plug-in to
+ [serverRoot]/slapd-[serverID]/config/dse.ldif. For example:
+
+ dn: cn=[My Server Plugin],cn=plugins,cn=config
+ objectClass: top
+ objectClass: nsSlapdPlugin
+ objectClass: extensibleObject
+ cn: [My Server Plugin]
+ nsslapd-pluginPath: [[serverRoot]/myPlugins/myveryown-plugin.so]
+ nsslapd-pluginInitfunc: [my_plugin_init]
+ nsslapd-pluginType: [myPluginType]
+ nsslapd-pluginEnabled: on
+ nsslapd-pluginarg0: [uid]
+ nsslapd-pluginarg1: [mail]
+ nsslapd-pluginarg2: [...]
+ nsslapd-plugin-depends-on-type: [anotherPluginType]
+ nsslapd-pluginId: [MyFirstServerPlugin]
+ nsslapd-pluginVersion: [0.1]
+ nsslapd-pluginVendor: [Fictional Software Company Incorporated]
+ nsslapd-pluginDescription: [Add lots of cool functionality]
+
+ See the Parameter Block Reference in the Netscape Directory Server
+ Plug-In Programmer's Guide for hints on plug-in types.
+
+3. Restart the server.
+
+ Console: Object > Start Server
+ Command Line: cd [serverRoot]/slapd-[serverID] ; ./restart-slapd
diff --git a/ldap/servers/slapd/test-plugins/clients/README b/ldap/servers/slapd/test-plugins/clients/README
new file mode 100644
index 00000000..a2aac49d
--- /dev/null
+++ b/ldap/servers/slapd/test-plugins/clients/README
@@ -0,0 +1,45 @@
+# BEGIN COPYRIGHT BLOCK
+# Copyright 2001 Sun Microsystems, Inc.
+# Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+# All rights reserved.
+# END COPYRIGHT BLOCK
+#
+ ---------------------------
+ Sample LDAP Clients That
+ Work with Server Plug-Ins
+ ---------------------------
+
+This directory contains code for some sample LDAP clients intended for
+use with the sample server plug-ins. For example, one of the server
+plug-ins handles requests for an extended operation with the OID 1.2.3.4.
+In order to test this sample plug-in, you need an LDAP v3 client that is
+capable of requesting this extended operation.
+
+ NOTE: Before you compile and run these examples, make sure
+ to change the server name, port number, root DN, and root DN
+ password to values applicable to your Directory Server.
+
+reqextop.c
+----------
+This is an example of an LDAP client that requests extended operations.
+The example should be used in conjunction with the testexop.c server
+plug-in, which handles the extended operation with the OID 1.2.3.4.
+
+This example requires a version of the Netscape Directory SDK that supports
+the LDAP v3 protocol. The 3.0 Beta version of the Directory SDK for C
+supports LDAP v3 and is available at the following location:
+
+ http://developer.netscape.com/tech/directory/
+
+ReqExtOp.java
+-------------
+This is an example of an LDAP client that requests extended operations.
+The example should be used in conjunction with the testexop.c server
+plug-in, which handles the extended operation with the OID 1.2.3.4.
+
+This example requires a version of the Netscape Directory SDK that supports
+the LDAP v3 protocol. The Netscape Directory SDK for Java 3.0 supports
+LDAP v3 and is available at the following location:
+
+ http://developer.netscape.com/tech/directory/
+
diff --git a/ldap/servers/slapd/test-plugins/clients/ReqExtOp.java b/ldap/servers/slapd/test-plugins/clients/ReqExtOp.java
new file mode 100644
index 00000000..cf29ea21
--- /dev/null
+++ b/ldap/servers/slapd/test-plugins/clients/ReqExtOp.java
@@ -0,0 +1,77 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+/*
+ *
+ * Requests an extended operation with the OID 1.2.3.4.
+ * Use this client in conjunction with a server that can process
+ * this extended operation.
+ *
+ */
+
+import netscape.ldap.*;
+import java.util.*;
+import java.io.*;
+
+public class ReqExtOp {
+ public static void main( String[] args )
+ {
+ LDAPConnection ld = null;
+ int status = -1;
+ try {
+ ld = new LDAPConnection();
+
+ /* Connect to server */
+ String MY_HOST = "localhost";
+ int MY_PORT = 389;
+ ld.connect( MY_HOST, MY_PORT );
+ System.out.println( "Connected to server." );
+
+ /* Authenticate to the server as directory manager */
+ String MGR_DN = "cn=Directory Manager";
+ String MGR_PW = "23skidoo";
+ if ( ld.LDAP_VERSION < 3 ) {
+ ld.authenticate( 3, MGR_DN, MGR_PW );
+ } else {
+ System.out.println( "Specified LDAP server does not support v3 of the LDAP protocol." );
+ ld.disconnect();
+ System.exit(1);
+ }
+ System.out.println( "Authenticated to directory." );
+
+ /* Create an extended operation object */
+ String myval = "My Value";
+ byte vals[] = myval.getBytes( "UTF8" );
+ LDAPExtendedOperation exop = new LDAPExtendedOperation( "1.2.3.4", vals );
+ System.out.println( "Created LDAPExtendedOperation object." );
+
+ /* Request the extended operation from the server. */
+ LDAPExtendedOperation exres = ld.extendedOperation( exop );
+
+ System.out.println( "Performed extended operation." );
+
+ /* Get data from the response sent by the server. */
+ System.out.println( "OID: " + exres.getID() );
+ String retValue = new String( exres.getValue(), "UTF8" );
+ System.out.println( "Value: " + retValue );
+ }
+ catch( LDAPException e ) {
+ System.out.println( "Error: " + e.toString() );
+ }
+ catch( UnsupportedEncodingException e ) {
+ System.out.println( "Error: UTF8 not supported" );
+ }
+
+ /* Done, so disconnect */
+ if ( (ld != null) && ld.isConnected() ) {
+ try {
+ ld.disconnect();
+ } catch ( LDAPException e ) {
+ System.out.println( "Error: " + e.toString() );
+ }
+ }
+ System.exit(status);
+ }
+}
diff --git a/ldap/servers/slapd/test-plugins/clients/reqextop.c b/ldap/servers/slapd/test-plugins/clients/reqextop.c
new file mode 100644
index 00000000..f23bcf38
--- /dev/null
+++ b/ldap/servers/slapd/test-plugins/clients/reqextop.c
@@ -0,0 +1,85 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+/*
+ * Requests an extended operation with the OID 1.2.3.4.
+ * Use this client in conjunction with a server that can process
+ * this extended operation.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+
+#include <ldap.h>
+
+/* Name and port of the LDAP server you want to connect to. */
+#define MY_HOST "localhost"
+#define MY_PORT 389
+
+/* DN of user (and password of user) who you want to authenticate as */
+#define MGR_DN "cn=Directory Manager"
+#define MGR_PW "23skidoo"
+
+int
+main( int argc, char **argv )
+{
+
+ /* OID of the extended operation that you are requesting */
+ const char *oidrequest = "1.2.3.4";
+ char *oidresult;
+ struct berval valrequest;
+ struct berval *valresult;
+ LDAP *ld;
+ int rc, version;
+
+ /* Set up the value that you want to pass to the server */
+ printf( "Setting up value to pass to server...\n" );
+ valrequest.bv_val = "My Value";
+ valrequest.bv_len = strlen( "My Value" );
+
+ /* Get a handle to an LDAP connection */
+ printf( "Getting the handle to the LDAP connection...\n" );
+ if ( (ld = ldap_init( MY_HOST, MY_PORT )) == NULL ) {
+ perror( "ldap_init" );
+ ldap_unbind( ld );
+ return( 1 );
+ }
+
+ /* Set the LDAP protocol version supported by the client
+ to 3. (By default, this is set to 2. Extended operations
+ are part of version 3 of the LDAP protocol.) */
+ ldap_get_option( ld, LDAP_OPT_PROTOCOL_VERSION, &version );
+ printf( "Resetting version %d to 3.0...\n", version );
+ version = LDAP_VERSION3;
+ ldap_set_option( ld, LDAP_OPT_PROTOCOL_VERSION, &version );
+
+ /* Authenticate to the directory as the Directory Manager */
+ printf( "Binding to the directory...\n" );
+ if ( ldap_simple_bind_s( ld, MGR_DN, MGR_PW ) != LDAP_SUCCESS ) {
+ ldap_perror( ld, "ldap_simple_bind_s" );
+ ldap_unbind( ld );
+ return( 1 );
+ }
+
+ /* Initiate the extended operation */
+ printf( "Initiating the extended operation...\n" );
+ if ( ( rc = ldap_extended_operation_s( ld, oidrequest, &valrequest, NULL, NULL, &oidresult, &valresult ) ) != LDAP_SUCCESS ) {
+ ldap_perror( ld, "ldap_extended failed: " );
+ ldap_unbind( ld );
+ return( 1 );
+ }
+
+ /* Get the OID and the value from the result returned by the server. */
+ printf( "Operation successful.\n" );
+ printf( "\tReturned OID: %s\n", oidresult );
+ printf( "\tReturned value: %s\n", valresult->bv_val );
+
+ /* Disconnect from the server. */
+ ldap_unbind( ld );
+ return 0;
+}
+
diff --git a/ldap/servers/slapd/test-plugins/dllmain.c b/ldap/servers/slapd/test-plugins/dllmain.c
new file mode 100644
index 00000000..f50f3595
--- /dev/null
+++ b/ldap/servers/slapd/test-plugins/dllmain.c
@@ -0,0 +1,109 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+/*
+ * Microsoft Windows specifics for sample plug-ins DLL
+ */
+#include "ldap.h"
+#include "lber.h"
+
+#ifdef _WIN32
+/* Lifted from Q125688
+ * How to Port a 16-bit DLL to a Win32 DLL
+ * on the MSVC 4.0 CD
+ */
+BOOL WINAPI DllMain (HANDLE hModule, DWORD fdwReason, LPVOID lpReserved)
+{
+ WSADATA wsadata;
+
+ switch (fdwReason)
+ {
+ case DLL_PROCESS_ATTACH:
+ /* Code from LibMain inserted here. Return TRUE to keep the
+ DLL loaded or return FALSE to fail loading the DLL.
+
+ You may have to modify the code in your original LibMain to
+ account for the fact that it may be called more than once.
+ You will get one DLL_PROCESS_ATTACH for each process that
+ loads the DLL. This is different from LibMain which gets
+ called only once when the DLL is loaded. The only time this
+ is critical is when you are using shared data sections.
+ If you are using shared data sections for statically
+ allocated data, you will need to be careful to initialize it
+ only once. Check your code carefully.
+
+ Certain one-time initializations may now need to be done for
+ each process that attaches. You may also not need code from
+ your original LibMain because the operating system may now
+ be doing it for you.
+ */
+ /*
+ * 16 bit code calls UnlockData()
+ * which is mapped to UnlockSegment in windows.h
+ * in 32 bit world UnlockData is not defined anywhere
+ * UnlockSegment is mapped to GlobalUnfix in winbase.h
+ * and the docs for both UnlockSegment and GlobalUnfix say
+ * ".. function is oboslete. Segments have no meaning
+ * in the 32-bit environment". So we do nothing here.
+ */
+
+ if( errno = WSAStartup(0x0101, &wsadata ) != 0 )
+ return FALSE;
+
+ break;
+
+ case DLL_THREAD_ATTACH:
+ /* Called each time a thread is created in a process that has
+ already loaded (attached to) this DLL. Does not get called
+ for each thread that exists in the process before it loaded
+ the DLL.
+
+ Do thread-specific initialization here.
+ */
+ break;
+
+ case DLL_THREAD_DETACH:
+ /* Same as above, but called when a thread in the process
+ exits.
+
+ Do thread-specific cleanup here.
+ */
+ break;
+
+ case DLL_PROCESS_DETACH:
+ /* Code from _WEP inserted here. This code may (like the
+ LibMain) not be necessary. Check to make certain that the
+ operating system is not doing it for you.
+ */
+ WSACleanup();
+
+ break;
+ }
+ /* The return value is only used for DLL_PROCESS_ATTACH; all other
+ conditions are ignored.
+ */
+ return TRUE; // successful DLL_PROCESS_ATTACH
+}
+#else
+int CALLBACK
+LibMain( HINSTANCE hinst, WORD wDataSeg, WORD cbHeapSize, LPSTR lpszCmdLine )
+{
+ /*UnlockData( 0 );*/
+ return( 1 );
+}
+#endif
+
+#ifndef _WIN32
+/* The 16-bit version of the RTL does not implement perror() */
+#include <stdio.h>
+
+void perror( const char *msg )
+{
+ char buf[128];
+ wsprintf( buf, "%s: error %d\n", msg, WSAGetLastError()) ;
+ OutputDebugString( buf );
+}
+
+#endif
diff --git a/ldap/servers/slapd/test-plugins/installDse.pl b/ldap/servers/slapd/test-plugins/installDse.pl
new file mode 100755
index 00000000..6b827ab4
--- /dev/null
+++ b/ldap/servers/slapd/test-plugins/installDse.pl
@@ -0,0 +1,129 @@
+# The script is used to load the config mapping tree node for the null suffix
+# and to load the test sample plugin node into the Directory Server used to
+# demonstrate the Data Interoperability feature for Verisign.
+
+# The dse.ldif configuration file used as the default configuration by the
+# Server gets edited for adding the above mentioned two config nodes and the
+# server is restarted to load the plugin.
+
+# The loading of the DataInterop test plugin is only to demostrate the use
+# of the test plugin and can be replaced with Verisign's database plugin for use.
+
+# Written by binduk@netscape.com
+
+# Edits to be done for use
+# $host = Hostname of the Directory Server
+# $port = port used by the Server
+# $mgrdn = Bind Dn
+# $mgrpass = Password
+# $installDir = Installation root of the Server <server-root>
+
+
+###### Begin Edits ##################################
+
+$host = "trika.nscp.aoltw.net";
+$instance = "trika2";
+$port = "7775";
+$mgrdn = "cn=directory manager";
+$mgrpass = "password";
+$installDir = "/export/msyBuild/install"; # Installation root of the Server <server-root>
+
+###### End of Edits ##################################
+
+
+$dseOrgFile = "$installDir/slapd-$instance/config/dse.ldif"; # default configuration file to be edited
+$dseFile = "$installDir/slapd-$instance/config/load_dse.ldif"; # additional configuration file to be added
+$pidFile = "$installDir/slapd-$instance/logs/pid"; # pid file for the running server
+my $editedNode = 0;
+my $editedPlugin = 0;
+my $serverStatus = 1;
+
+
+ if(!(-e $pidFile)){
+ open( START_SERVER, "| $installDir/slapd-$instance/start-slapd ") || die "Can't Start the Server \n";
+ close(START_SERVER);
+
+ if(-e $pidFile){
+ print " ######## Started the Server \n";
+ }
+ else {
+ print " ######## Unable to Start the Server \n";
+ $serverStatus = 0;
+ }
+ }
+
+
+open(DSE_ORG, "$dseOrgFile") || die "Can't open $dseOrgFile for checks \n";
+ while(<DSE_ORG>){
+ $isEditedNode = 1 if (/^dn: cn=\"\",cn=mapping tree,cn=config/);
+ $isEditedPlugin = 1 if (/^dn: cn=datainterop,cn=plugins,cn=config/);
+ }
+close(DSE_ORG);
+
+open(DSE, ">$dseFile") || die "Can't open $dseFile for editing \n";
+
+ my $changesMade = 0;
+ unless($isEditedNode){
+ print DSE "dn: cn=\"\",cn=mapping tree,cn=config\n";
+ print DSE "objectClass: top\n";
+ print DSE "objectClass: extensibleObject\n";
+ print DSE "objectClass: nsMappingTree\n";
+ print DSE "cn: \"\"\n";
+ print DSE "nsslapd-state: container\n";
+ print DSE "\n";
+ $changesMade = 1;
+ }
+
+ unless($isEditedPlugin){
+ print DSE "dn: cn=datainterop,cn=plugins,cn=config\n";
+ print DSE "objectClass: top\n";
+ print DSE "objectClass: nsSlapdPlugin\n";
+ print DSE "cn: datainterop\n";
+ print DSE "nsslapd-pluginPath: $installDir/plugins/slapd/slapi/examples/libtest-plugin.so\n";
+ print DSE "nsslapd-pluginInitfunc: nullsuffix_init\n";
+ print DSE "nsslapd-pluginType: preoperation\n";
+ print DSE "nsslapd-pluginEnabled: on\n";
+ print DSE "nsslapd-pluginId: nullsuffix-preop\n";
+ print DSE "nsslapd-pluginVersion: 6.2\n";
+ print DSE "nsslapd-pluginVendor: Netscape\n";
+ print DSE "nsslapd-pluginDescription: sample pre-operation null suffix search plugin\n";
+ $changesMade = 1;
+ }
+close(DSE);
+
+
+if($changesMade){
+ chdir "$installDir/shared/bin" or die "cannot cd over error=$! \n";
+
+ open(LDAPMODIFY, "|ldapmodify -p \"${port}\" -h \"${host}\" -D \"${mgrdn}\" -w \"${mgrpass}\" -v -c -a -f $dseFile " ) || die "Can't modify the configuration of the Server \n";
+
+ close(LDAPMODIFY);
+
+ print " Modifications to the dse.ldif file have been done....restarting the server to load plugin \n";
+
+ open( STOP_SERVER, "| $installDir/slapd-$instance/stop-slapd ") || die "Can't Stop the Server \n";;
+ close(STOP_SERVER);
+
+ print " Now stopped the Server to load the plugin \n";
+
+ open( START_SERVER, "| $installDir/slapd-$instance/start-slapd ") || die "Can't Start the Server \n";
+ close(START_SERVER);
+
+ if(-e $pidFile){
+ print " Started the Server Successfully\n";
+ }
+ else{
+ $serverStatus = 0;
+ print "Failure in starting the Server - Check to see if the sample plugin has been compiled \n";
+ }
+
+}
+else {
+ if($serverStatus){
+ print " Nothing needs to be done \n";
+ }
+ else {
+ print " The Server did not Start Successfully \n";
+ }
+
+}
diff --git a/ldap/servers/slapd/test-plugins/nicknames b/ldap/servers/slapd/test-plugins/nicknames
new file mode 100644
index 00000000..1d7a2515
--- /dev/null
+++ b/ldap/servers/slapd/test-plugins/nicknames
@@ -0,0 +1,10 @@
+#
+# BEGIN COPYRIGHT BLOCK
+# Copyright 2001 Sun Microsystems, Inc.
+# Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+# All rights reserved.
+# END COPYRIGHT BLOCK
+#
+Timothy Tim Timbo Timmy
+Barbara Babs Buddah
+Robert Rob Bob Rob-bob Robby Bobby
diff --git a/ldap/servers/slapd/test-plugins/testbind.c b/ldap/servers/slapd/test-plugins/testbind.c
new file mode 100644
index 00000000..4d080a0f
--- /dev/null
+++ b/ldap/servers/slapd/test-plugins/testbind.c
@@ -0,0 +1,252 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+
+/************************************************************
+
+ testbind.c
+
+ This source file provides an example of a pre-operation plug-in
+ function that handles authentication.
+
+ Note that the Directory Server front-end handles bind
+ operations requested by the root DN. The server does not
+ invoke your plug-in function if the client is authenicating
+ as the root DN.
+
+ To test this plug-in function, stop the server, edit the dse.ldif file
+ (in the <server_root>/slapd-<server_id>/config directory)
+ and add the following lines before restarting the server :
+
+ dn: cn=Test Bind,cn=plugins,cn=config
+ objectClass: top
+ objectClass: nsSlapdPlugin
+ objectClass: extensibleObject
+ cn: Test Bind
+ nsslapd-pluginPath: <server_root>/plugins/slapd/slapi/examples/libtest-plugin.so
+ nsslapd-pluginInitfunc: testbind_init
+ nsslapd-pluginType: preoperation
+ nsslapd-pluginEnabled: on
+ nsslapd-plugin-depends-on-type: database
+ nsslapd-pluginId: test-bind
+
+ ************************************************************/
+#include <stdio.h>
+#include <string.h>
+#include "slapi-plugin.h"
+
+Slapi_PluginDesc bindpdesc = { "test-bind", "Netscape", "0.5",
+ "sample bind pre-operation plugin" };
+
+static Slapi_ComponentId *plugin_id = NULL;
+
+
+
+
+/* Pre-operation plug-in function */
+int
+test_bind( Slapi_PBlock *pb )
+{
+ char *dn, *attrs[2] = { SLAPI_USERPWD_ATTR, NULL };
+ int method, rc = LDAP_SUCCESS;
+ struct berval *credentials;
+ struct berval **pwvals;
+ Slapi_DN *sdn = NULL;
+ Slapi_Entry *e = NULL;
+ Slapi_Attr *attr = NULL;
+
+ /* Log a message to the server error log. */
+ slapi_log_error( SLAPI_LOG_PLUGIN, "test_bind",
+ "Pre-operation bind function called.\n" );
+
+ /* Gets parameters available when processing an LDAP bind
+ operation. */
+ if ( slapi_pblock_get( pb, SLAPI_BIND_TARGET, &dn ) != 0 ||
+ slapi_pblock_get( pb, SLAPI_BIND_METHOD, &method ) != 0 ||
+ slapi_pblock_get( pb, SLAPI_BIND_CREDENTIALS, &credentials ) != 0 ) {
+
+ slapi_log_error( SLAPI_LOG_PLUGIN, "test_bind",
+ "Could not get parameters for bind operation\n" );
+ slapi_send_ldap_result( pb, LDAP_OPERATIONS_ERROR,
+ NULL, NULL, 0, NULL );
+ return( 1 );
+ }
+
+ /* Check the authentication method */
+ switch( method ) {
+ case LDAP_AUTH_SIMPLE:
+ /* First, get the entry specified by the DN. */
+ sdn = slapi_sdn_new_dn_byref( dn );
+ rc = slapi_search_internal_get_entry( sdn, attrs, &e,
+ plugin_id );
+ slapi_sdn_free( &sdn );
+
+ if ( rc != LDAP_SUCCESS ) {
+ slapi_log_error( SLAPI_LOG_PLUGIN, "test_bind",
+ "Could not find entry %s (error %d)\n",
+ dn, rc );
+ break;
+ }
+
+ /* Next, check credentials against the userpassword attribute
+ of that entry. */
+ if ( e != NULL ) {
+ Slapi_Value *credval, **pwvals;
+ int i, hint, valcount;
+
+
+ if ( slapi_entry_attr_find( e, SLAPI_USERPWD_ATTR,
+ &attr ) != 0 || slapi_attr_get_numvalues( attr,
+ &valcount ) != 0 ) {
+ slapi_log_error( SLAPI_LOG_PLUGIN, "test_bind",
+ "Entry has no %s attribute values\n",
+ SLAPI_USERPWD_ATTR );
+ rc = LDAP_INAPPROPRIATE_AUTH;
+ break;
+ }
+
+ credval = slapi_value_new_berval( credentials );
+ pwvals = (Slapi_Value **)slapi_ch_calloc( valcount,
+ sizeof( Slapi_Value * ));
+ i = 0;
+ for ( hint = slapi_attr_first_value( attr, &pwvals[i] );
+ hint != -1; hint = slapi_attr_next_value( attr,
+ hint, &pwvals[i] )) {
+ ++i;
+ }
+
+ if ( slapi_pw_find_sv( pwvals, credval ) != 0 ) {
+ slapi_log_error( SLAPI_LOG_PLUGIN, "test_bind",
+ "Credentials are not correct\n" );
+ rc = LDAP_INVALID_CREDENTIALS;
+ }
+
+ slapi_value_free( &credval );
+ slapi_ch_free( (void **)&pwvals );
+
+ if ( LDAP_SUCCESS != rc ) {
+ break;
+ }
+ } else {
+ /* This should not happen. The previous section of code
+ already checks for this case. */
+ slapi_log_error( SLAPI_LOG_PLUGIN, "test_bind",
+ "Could find entry for %s\n", dn );
+ rc = LDAP_NO_SUCH_OBJECT;
+ break;
+ }
+
+ /* Set the DN and authentication method for the connection. */
+ if ( slapi_pblock_set( pb, SLAPI_CONN_DN,
+ slapi_ch_strdup( dn ) ) != 0 ||
+ slapi_pblock_set( pb, SLAPI_CONN_AUTHMETHOD,
+ SLAPD_AUTH_SIMPLE ) != 0 ) {
+
+ slapi_log_error( SLAPI_LOG_PLUGIN, "test_bind",
+ "Failed to set DN and method for connection\n" );
+ rc = LDAP_OPERATIONS_ERROR;
+ break;
+ }
+
+ /* Send a "success" result code back to the client. */
+ slapi_log_error( SLAPI_LOG_PLUGIN, "test_bind",
+ "Authenticated: %s\n", dn );
+ rc = LDAP_SUCCESS;
+ break;
+
+ /* If NONE is specified, the client is requesting to bind anonymously.
+ Normally, this case should be handled by the server's front-end
+ before it calls this plug-in function. Just in case this does
+ get through to the plug-in function, you can handle this by
+ sending a successful result code back to the client and returning
+ 1.
+ */
+ case LDAP_AUTH_NONE:
+ slapi_log_error( SLAPI_LOG_PLUGIN, "test_bind",
+ "Authenticating anonymously\n" );
+ rc = LDAP_SUCCESS;
+ break;
+
+ /* This plug-in does not support any other method of authentication */
+ case LDAP_AUTH_SASL:
+ default:
+ slapi_log_error( SLAPI_LOG_PLUGIN, "test_bind",
+ "Unsupported authentication method requested: %d\n",
+ method );
+ rc = LDAP_AUTH_METHOD_NOT_SUPPORTED;
+ break;
+ }
+
+ slapi_send_ldap_result( pb, rc, NULL, NULL, 0, NULL );
+ return( 1 );
+}
+
+/* Pre-operation plug-in function */
+int
+test_search( Slapi_PBlock *pb )
+{
+ char *reqdn;
+
+ /* Log a message to the server error log. */
+ slapi_log_error( SLAPI_LOG_PLUGIN, "test_search",
+ "Pre-operation search function called.\n" );
+
+ /* Get requestor of search operation. This is not critical
+ to performing the search (this plug-in just serves as
+ confirmation that the bind plug-in works), so return 0
+ if this fails. */
+ if ( slapi_pblock_get( pb, SLAPI_REQUESTOR_DN, &reqdn ) != 0 ) {
+
+ slapi_log_error( SLAPI_LOG_PLUGIN, "test_search",
+ "Could not get requestor parameter for search operation\n" );
+ return( 0 );
+ }
+
+ /* Indicate who is requesting the search */
+ if ( reqdn != NULL && *reqdn != '\0' ) {
+ slapi_log_error( SLAPI_LOG_PLUGIN, "test_search",
+ "Search requested by %s\n", reqdn );
+ } else {
+ slapi_log_error( SLAPI_LOG_PLUGIN, "test_search",
+ "Search requested by anonymous client\n" );
+ }
+ return( 0 );
+}
+
+/* Initialization function */
+#ifdef _WIN32
+__declspec(dllexport)
+#endif
+int
+testbind_init( Slapi_PBlock *pb )
+{
+
+ /* Retrieve and save the plugin identity to later pass to
+ internal operations */
+ if ( slapi_pblock_get( pb, SLAPI_PLUGIN_IDENTITY, &plugin_id ) != 0 ) {
+ slapi_log_error( SLAPI_LOG_PLUGIN, "testbind_init",
+ "Failed to retrieve SLAPI_PLUGIN_IDENTITY\n" );
+ return( -1 );
+ }
+
+ /* Register the pre-operation bind function and specify
+ the server plug-in version. */
+ if ( slapi_pblock_set( pb, SLAPI_PLUGIN_VERSION,
+ SLAPI_PLUGIN_VERSION_01 ) != 0 ||
+ slapi_pblock_set( pb, SLAPI_PLUGIN_DESCRIPTION,
+ (void *)&bindpdesc ) != 0 ||
+ slapi_pblock_set( pb, SLAPI_PLUGIN_PRE_BIND_FN,
+ (void *) test_bind ) != 0 ||
+ slapi_pblock_set( pb, SLAPI_PLUGIN_PRE_SEARCH_FN,
+ (void *) test_search ) != 0 ) {
+
+ slapi_log_error( SLAPI_LOG_PLUGIN, "testbind_init",
+ "Failed to set version and functions\n" );
+ return( -1 );
+ }
+
+ return( 0 );
+}
+
diff --git a/ldap/servers/slapd/test-plugins/testdatainterop.c b/ldap/servers/slapd/test-plugins/testdatainterop.c
new file mode 100644
index 00000000..19b171c1
--- /dev/null
+++ b/ldap/servers/slapd/test-plugins/testdatainterop.c
@@ -0,0 +1,312 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2002 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+
+/******** testdatainterop.c *******************
+
+ This source file provides an example of a plug-in function
+ that implements an datainteroprability functionality.
+ The plug-in function is called by the server
+ when the server is configured to use the null dn.
+ meaning dn:
+ The server uses the null dn or the root suffix opnly when
+ the configuration for the server has the following
+ node in the dse.ldif of the server instance
+ (in the <server_root>/slapd-<server_id>/config directory).
+
+ dn: cn="",cn=mapping tree,cn=config
+ objectClass: top
+ objectClass: extensibleObject
+ objectClass: nsMappingTree
+ cn: ""
+ nsslapd-state: container
+
+ The plugin below is a pre-operation plugin which
+ provides alternate functionality for the LDAP operations
+ of search, modify, add etc. that are targeted at the root-suffix
+ or the null-dn to be serviced by an alternate data source or
+ alternate access methods allowing datainteroperability.
+
+ The example below creates a berkely db and modifies or adds data
+ to the db demonstarting the use of an alternate data source seperate
+ from the Directory Server. Also, the results of a search operation
+ are completely in the control of the pre-operation plugin. In this
+ example a fake entry is returned to express the functionality
+
+
+ To test this plug-in function, stop the server, edit the dse.ldif file
+ (in the <server_root>/slapd-<server_id>/config directory)
+ and add the following lines before restarting the server :
+
+ dn: cn="",cn=mapping tree,cn=config
+ objectClass: top
+ objectClass: extensibleObject
+ objectClass: nsMappingTree
+ cn: ""
+ nsslapd-state: container
+
+
+ dn: cn=datainterop,cn=plugins,cn=config
+ objectClass: top
+ objectClass: nsSlapdPlugin
+ cn: datainterop
+ nsslapd-pluginPath: <server-root>/plugins/slapd/slapi/examples/libtest-plugin.so
+ nsslapd-pluginInitfunc: nullsuffix_init
+ nsslapd-pluginType: preoperation
+ nsslapd-pluginEnabled: on
+ nsslapd-pluginId: nullsuffix-preop
+ nsslapd-pluginVersion: 6.2
+ nsslapd-pluginVendor: Netscape
+ nsslapd-pluginDescription: sample pre-operation null suffix plugin
+
+ ******************************************/
+
+#include <stdio.h>
+#include <string.h>
+#include "slapi-plugin.h"
+
+/*
+ * Macros.
+ */
+#define PLUGIN_NAME "nullsuffix-preop"
+
+#define PLUGIN_OPERATION_HANDLED 1
+#define PLUGIN_OPERATION_IGNORED 0
+
+#define SEARCH_SCOPE_ANY (-1)
+
+
+
+/*
+ * Static variables.
+ */
+static Slapi_PluginDesc plugindesc = { PLUGIN_NAME, "Netscape", "0.5",
+ "sample pre-operation null suffix plugin" };
+
+static Slapi_ComponentId *plugin_id = NULL;
+
+
+/*
+ * Function prototypes.
+ */
+static int nullsuffix_search( Slapi_PBlock *pb );
+static int nullsuffix_add( Slapi_PBlock *pb );
+static int nullsuffix_close( Slapi_PBlock *pb );
+static int nullsuffix_modify( Slapi_PBlock *pb );
+static int nullsuffix_delete( Slapi_PBlock *pb );
+static int nullsuffix_modrdn( Slapi_PBlock *pb );
+static int nullsuffix_bind( Slapi_PBlock *pb );
+
+
+/*
+ * Initialization function.
+ */
+#ifdef _WIN32
+__declspec(dllexport)
+#endif
+int
+nullsuffix_init( Slapi_PBlock *pb )
+{
+ int i;
+
+ slapi_log_error( SLAPI_LOG_PLUGIN, PLUGIN_NAME, "nullsuffix_init\n" );
+
+ /* retrieve plugin identity to later pass to internal operations */
+ if ( slapi_pblock_get( pb, SLAPI_PLUGIN_IDENTITY, &plugin_id ) != 0 ) {
+ slapi_log_error( SLAPI_LOG_PLUGIN, PLUGIN_NAME,
+ "unable to get SLAPI_PLUGIN_IDENTITY\n" );
+ return -1;
+ }
+
+ /* register the pre-operation search function, etc. */
+ if ( slapi_pblock_set( pb, SLAPI_PLUGIN_VERSION, SLAPI_PLUGIN_VERSION_01
+ ) != 0
+ || slapi_pblock_set( pb, SLAPI_PLUGIN_DESCRIPTION,
+ (void *)&plugindesc ) != 0
+ || slapi_pblock_set( pb, SLAPI_PLUGIN_PRE_SEARCH_FN,
+ (void *)nullsuffix_search ) != 0
+ || slapi_pblock_set( pb, SLAPI_PLUGIN_CLOSE_FN,
+ (void *)nullsuffix_close ) != 0
+ || slapi_pblock_set( pb, SLAPI_PLUGIN_PRE_ADD_FN,
+ (void *)nullsuffix_add) != 0
+ || slapi_pblock_set( pb, SLAPI_PLUGIN_PRE_MODIFY_FN,
+ (void *)nullsuffix_modify) != 0
+ || slapi_pblock_set( pb, SLAPI_PLUGIN_PRE_DELETE_FN,
+ (void *)nullsuffix_delete) != 0
+ || slapi_pblock_set( pb, SLAPI_PLUGIN_PRE_BIND_FN,
+ (void *)nullsuffix_bind) != 0
+ || slapi_pblock_set( pb, SLAPI_PLUGIN_PRE_MODRDN_FN,
+ (void *)nullsuffix_modrdn) != 0) {
+ slapi_log_error( SLAPI_LOG_PLUGIN, PLUGIN_NAME,
+ "failed to set version and function\n" );
+ return -1;
+ }
+
+
+
+ return 0;
+}
+
+static int
+nullsuffix_bind( Slapi_PBlock *pb )
+{
+ if( slapi_op_reserved(pb) ){
+ return PLUGIN_OPERATION_IGNORED;
+ }
+ slapi_log_error( SLAPI_LOG_PLUGIN, PLUGIN_NAME, "nullsuffix_bind\n" );
+ send_ldap_result( pb, LDAP_SUCCESS, NULL, NULL, 0, NULL );
+ return PLUGIN_OPERATION_HANDLED;
+
+}
+
+static int
+nullsuffix_add( Slapi_PBlock *pb )
+{
+ char *dn;
+ if( slapi_op_reserved(pb) ){
+ return PLUGIN_OPERATION_IGNORED;
+ }
+ slapi_log_error( SLAPI_LOG_PLUGIN, PLUGIN_NAME, "nullsuffix_add\n" );
+ slapi_pblock_get( pb, SLAPI_ADD_TARGET, &dn );
+ db_put_dn(dn);
+ send_ldap_result( pb, LDAP_SUCCESS, NULL, NULL, 0, NULL );
+ return PLUGIN_OPERATION_HANDLED;
+}
+
+static int
+nullsuffix_modify( Slapi_PBlock *pb )
+{
+ Slapi_Entry *entry;
+ int i;
+ int j;
+ char *dn;
+ if( slapi_op_reserved(pb) ){
+ return PLUGIN_OPERATION_IGNORED;
+ }
+ slapi_pblock_get( pb, SLAPI_MODIFY_TARGET, &dn );
+ slapi_pblock_get( pb, SLAPI_ENTRY_PRE_OP, &entry);
+ db_put_dn(dn);
+ send_ldap_result( pb, LDAP_SUCCESS, NULL, NULL, 0, NULL );
+ slapi_log_error( SLAPI_LOG_PLUGIN, PLUGIN_NAME, "nullsuffix_modify\n" );
+ return PLUGIN_OPERATION_HANDLED;
+
+}
+
+static int
+nullsuffix_delete( Slapi_PBlock *pb )
+{
+ if( slapi_op_reserved(pb) ){
+ return PLUGIN_OPERATION_IGNORED;
+ }
+ slapi_log_error( SLAPI_LOG_PLUGIN, PLUGIN_NAME, "nullsuffix_delete\n" );
+ send_ldap_result( pb, LDAP_SUCCESS, NULL, NULL, 0, NULL );
+ return PLUGIN_OPERATION_HANDLED;
+}
+
+static int
+nullsuffix_modrdn( Slapi_PBlock *pb )
+{
+ if( slapi_op_reserved(pb) ){
+ return PLUGIN_OPERATION_IGNORED;
+ }
+ slapi_log_error( SLAPI_LOG_PLUGIN, PLUGIN_NAME, "nullsuffix_modrdn\n" );
+ send_ldap_result( pb, LDAP_SUCCESS, NULL, NULL, 0, NULL );
+ return PLUGIN_OPERATION_HANDLED;
+}
+
+static int
+nullsuffix_search( Slapi_PBlock *pb )
+{
+ char *dn_base, **attrs, *newStr;
+ int scope, sizelimit, timelimit, deref, attrsonly;
+ Slapi_Filter *filter;
+ Slapi_DN *sdn_base;
+ int ldaperr = LDAP_SUCCESS; /* optimistic */
+ int nentries = 0; /* entry count */
+ int i;
+ Slapi_Operation *op;
+ Slapi_Entry *e;
+
+ const char *entrystr =
+ "dn:cn=Joe Smith,o=Example\n"
+ "objectClass: top\n"
+ "objectClass: person\n"
+ "objectClass: organizationalPerson\n"
+ "objectClass: inetOrgPerson\n"
+ "cn:Joe Smith\n"
+ "sn:Smith\n"
+ "uid:jsmith\n"
+ "mail:jsmith@example.com\n";
+
+ slapi_log_error( SLAPI_LOG_PLUGIN, PLUGIN_NAME, "nullsuffix_search\n" );
+ if( slapi_op_reserved(pb) ){
+ return PLUGIN_OPERATION_IGNORED;
+ }
+
+ /* get essential search parameters */
+ if ( slapi_pblock_get( pb, SLAPI_SEARCH_TARGET, &dn_base ) != 0 ||
+ slapi_pblock_get( pb, SLAPI_SEARCH_SCOPE, &scope ) != 0 ) {
+ slapi_log_error( SLAPI_LOG_PLUGIN, PLUGIN_NAME,
+ "could not get base DN and scope search parameters\n" );
+ }
+ if ( dn_base == NULL ) {
+ dn_base = "";
+ }
+ sdn_base = slapi_sdn_new_dn_byval( dn_base );
+ slapi_pblock_get(pb, SLAPI_OPERATION, &op);
+
+ /* get remaining search parameters */
+ if ( slapi_pblock_get( pb, SLAPI_SEARCH_DEREF, &deref ) != 0 ||
+ slapi_pblock_get( pb, SLAPI_SEARCH_SIZELIMIT, &sizelimit ) != 0 ||
+ slapi_pblock_get( pb, SLAPI_SEARCH_TIMELIMIT, &timelimit ) != 0 ||
+ slapi_pblock_get( pb, SLAPI_SEARCH_FILTER, &filter ) != 0 ||
+ slapi_pblock_get( pb, SLAPI_SEARCH_ATTRS, &attrs ) != 0 ||
+ slapi_pblock_get( pb, SLAPI_SEARCH_ATTRSONLY, &attrsonly ) != 0 ) {
+ slapi_log_error( SLAPI_LOG_PLUGIN, PLUGIN_NAME,
+ "could not get remaining search parameters\n" );
+ }
+
+ if ( slapi_pblock_get( pb, SLAPI_OPERATION, &op ) != 0 ) {
+ slapi_log_error( SLAPI_LOG_PLUGIN, PLUGIN_NAME,
+ "could not get operation\n" );
+ } else {
+ slapi_operation_set_flag(op, SLAPI_OP_FLAG_NO_ACCESS_CHECK );
+ }
+
+ /* create a fake entry and send it along */
+ newStr = slapi_ch_strdup( entrystr );
+ if ( NULL == ( e = slapi_str2entry( newStr,
+ SLAPI_STR2ENTRY_ADDRDNVALS
+ | SLAPI_STR2ENTRY_EXPAND_OBJECTCLASSES ))) {
+ slapi_log_error( SLAPI_LOG_PLUGIN, PLUGIN_NAME,
+ "nullsuffix_search: slapi_str2entry() failed\n" );
+ } else {
+ slapi_send_ldap_search_entry( pb, e, NULL /* controls */,
+ attrs, attrsonly );
+ ++nentries;
+ slapi_entry_free( e );
+ }
+
+ slapi_send_ldap_result( pb, ldaperr, NULL, "kilroy was here",
+ nentries, NULL );
+ slapi_log_error( SLAPI_LOG_PLUGIN, PLUGIN_NAME, "nullsuffix_search:"
+ " handled search based at %s with scope %d; ldaperr=%d\n",
+ dn_base, scope, ldaperr );
+
+ slapi_ch_free_string(&newStr);
+ slapi_sdn_free(&sdn_base);
+
+ return PLUGIN_OPERATION_HANDLED;
+}
+
+
+/*
+ * Shutdown function.
+ */
+static int
+nullsuffix_close( Slapi_PBlock *pb )
+{
+ slapi_log_error( SLAPI_LOG_PLUGIN, PLUGIN_NAME, "nullsuffix_close\n" );
+ return 0;
+}
diff --git a/ldap/servers/slapd/test-plugins/testdbinterop.c b/ldap/servers/slapd/test-plugins/testdbinterop.c
new file mode 100644
index 00000000..7da3e72f
--- /dev/null
+++ b/ldap/servers/slapd/test-plugins/testdbinterop.c
@@ -0,0 +1,102 @@
+#include <sys/types.h>
+#include <stdio.h>
+#include "db.h"
+#include "testdbinterop.h"
+#include "slapi-plugin.h"
+
+#define DATABASE "access.db"
+static int number_of_keys=100;
+static int key_buffer_size = 8000;
+
+#define DB_PLUGIN_NAME "nullsuffix-preop"
+
+static PRLock *db_lock=NULL;
+
+#if 1000*DB_VERSION_MAJOR + 100*DB_VERSION_MINOR >= 4100
+#define DB_OPEN(db, txnid, file, database, type, flags, mode) \
+ (db)->open((db), (txnid), (file), (database), (type), (flags), (mode))
+#else
+ (db)->open((db), (file), (database), (type), (flags), (mode))
+#endif
+
+
+DB *dbp=NULL;
+
+void
+create_db()
+{
+ int ret;
+ DBT key, data;
+
+ if ((ret = db_create(&dbp, NULL, 0)) != 0) {
+ fprintf(stderr, "db_create: %s\n", db_strerror(ret));
+ exit (1);
+ }
+
+}
+
+void make_key(DBT *key)
+{
+ char *key_string = (char*)(key->data);
+ unsigned int seed = (unsigned int)time( (time_t*) 0);
+ long int key_long = slapi_rand_r(&seed) % number_of_keys;
+ sprintf(key_string,"key%ld",key_long);
+ slapi_log_error(SLAPI_LOG_PLUGIN, DB_PLUGIN_NAME,"generated key: %s\n", key_string);
+ key->size = strlen(key_string);
+}
+
+
+void
+db_put_dn(char *data_dn)
+{
+ int ret;
+ DBT key = {0};
+ DBT data = {0};
+
+ if(db_lock == NULL){
+ db_lock = PR_NewLock();
+ }
+ PR_Lock(db_lock);
+ create_db();
+
+ if ((ret = DB_OPEN(dbp, NULL, DATABASE, NULL, DB_BTREE, DB_CREATE, 0664)) != 0) {
+ dbp->err(dbp, ret, "%s", DATABASE);
+ }
+ memset(&key, 0, sizeof(key));
+ memset(&data, 0, sizeof(data));
+
+
+ key.data = (char *)malloc(key_buffer_size);
+ /* make_key will set up the key and the data */
+ make_key(&key);
+
+ data.data = slapi_ch_strdup(data_dn);
+ data.size = strlen(data_dn);
+
+
+ switch (ret =
+ dbp->put(dbp, NULL, &key, &data, DB_NOOVERWRITE)) {
+ case 0:
+ slapi_log_error(SLAPI_LOG_PLUGIN, DB_PLUGIN_NAME, "db: %s: key stored.\n", (char *)key.data);
+ break;
+ case DB_KEYEXIST:
+ slapi_log_error(SLAPI_LOG_PLUGIN, DB_PLUGIN_NAME, "db: %s: key previously stored.\n",
+ (char *)key.data);
+ break;
+ default:
+ dbp->err(dbp, ret, "DB->put");
+ goto err;
+ }
+
+ err:
+ if(ret){
+ slapi_log_error(SLAPI_LOG_PLUGIN, DB_PLUGIN_NAME, "db: Error detected in db_put \n");
+ }
+ free(key.data);
+ if (dbp){
+ dbp->close(dbp,0);
+ dbp=NULL;
+ }
+ PR_Unlock(db_lock);
+
+}
diff --git a/ldap/servers/slapd/test-plugins/testdbinterop.h b/ldap/servers/slapd/test-plugins/testdbinterop.h
new file mode 100644
index 00000000..4fe46347
--- /dev/null
+++ b/ldap/servers/slapd/test-plugins/testdbinterop.h
@@ -0,0 +1,22 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2002 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+
+/******** testdbinterop.h *******************
+
+ The header file is for access to a Berkeley DB
+ that is being created by the testdbinterop.c
+ and used by testdatainterop.c ( plugin ); to allow
+ creation of a DB and adding DN's to the DB.
+ A simple example to show how external databases can
+ be accessed through the datainterop plugin of
+ testdatainterop.c
+
+**********************************************/
+
+#include "nspr.h"
+
+void create_db();
+void db_put_dn(char *data);
+
diff --git a/ldap/servers/slapd/test-plugins/testentry.c b/ldap/servers/slapd/test-plugins/testentry.c
new file mode 100644
index 00000000..893c6955
--- /dev/null
+++ b/ldap/servers/slapd/test-plugins/testentry.c
@@ -0,0 +1,133 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+/************************************************************
+
+ testentry.c
+
+ This source file provides examples of entry store and
+ entry fetch plug-in functions. These plug-in functions are
+ called by the server before writing an entry to disk and
+ after reading an entry from disk.
+
+ Entry store and entry fetch plug-in functions are passed
+ the string representation (in LDIF -- LDAP Data Interchange
+ Format) of the entry.
+
+ In this example, the entry store function performs a bitwise
+ exclusive-OR operation on each character in the entry
+ against the value 0xaa (10101010). The entry fetch
+ function performs this again to revert each character
+ back to its initial value.
+
+ NOTE: The Directory Server caches recently added and retrieved
+ entries in memory. The entry fetch plug-in function is called
+ only when reading the entry from the disk, *not* when reading
+ the entry from the cache.
+
+ For example, if you add an entry and search for it, you will
+ not see a message in the server error log indicating that
+ the entry fetch plug-in function was called. In the process
+ of adding the entry to the directory, the server also added
+ the entry to the cache; the server then reads the entry from
+ the cache instead of from the disk and does not need to call
+ the entry fetch plug-in function.
+
+ You can flush the cache by shutting down the server.
+
+ To test this plug-in function, stop the server, edit the dse.ldif file
+ (in the <server_root>/slapd-<server_id>/config directory)
+ and add the following lines before restarting the server :
+
+dn: cn=Test entry,cn=plugins,cn=config
+objectClass: top
+objectClass: nsSlapdPlugin
+objectClass: extensibleObject
+cn: Test entry
+nsslapd-pluginPath: <server_root>/plugins/slapd/slapi/examples/libtest-plugin.so
+nsslapd-pluginInitfunc: testentry_init
+nsslapd-pluginType: ldbmentryfetchstore
+nsslapd-pluginEnabled: on
+nsslapd-pluginId: test-entry
+
+ ************************************************************/
+
+#include <stdio.h>
+#include <string.h>
+#include "slapi-plugin.h"
+
+Slapi_PluginDesc entrypdesc = { "test-entry", "Netscape", "0.5",
+ "sample entry modification plugin" };
+
+/* Entry store plug-in function */
+#ifdef _WIN32
+__declspec(dllexport)
+#endif
+int
+testentry_scramble( char **entry, unsigned long *len )
+{
+ unsigned long i;
+
+ /* Log an entry to the server's error log file whenever
+ this function is called. */
+ slapi_log_error( SLAPI_LOG_PLUGIN, "testentry_scramble",
+ "Entry data scrambled.\n" );
+
+ /* Perform a bitwise exclusive-OR operation on each
+ character in the entry. */
+ for ( i = 0; i < *len - 1; i++ ) {
+ (*entry)[i] ^= 0xaa;
+ }
+
+ return( 0 );
+}
+
+/* Entry fetch plug-in function */
+#ifdef _WIN32
+__declspec(dllexport)
+#endif
+int
+testentry_unscramble( char **entry, unsigned long *len )
+{
+ unsigned long i;
+
+ /* Some entries will not be scrambled, so check if the entry is
+ scrambled before attempting to unscramble the entry. */
+ if ( !strncmp( *entry, "dn:", 3 ) ) {
+ return( 0 );
+ }
+
+ /* Perform a bitwise exclusive-OR operation on each
+ character in the entry. */
+ for ( i = 0; i < *len - 1; i++ ) {
+ (*entry)[i] ^= 0xaa;
+ }
+
+ slapi_log_error( SLAPI_LOG_PLUGIN, "testentry_unscramble",
+ "Entry data unscrambled.\n");
+ return( 0 );
+}
+
+int
+testentry_init(Slapi_PBlock *pb)
+{
+ /* Register the store/fetch functions and specify
+ the server plug-in version. */
+ if ( slapi_pblock_set( pb, SLAPI_PLUGIN_VERSION,
+ SLAPI_PLUGIN_VERSION_01 ) != 0 ||
+ slapi_pblock_set( pb, SLAPI_PLUGIN_DESCRIPTION,
+ (void *)&entrypdesc ) != 0 ||
+ slapi_pblock_set( pb, SLAPI_PLUGIN_ENTRY_FETCH_FUNC,
+ (void *) testentry_unscramble ) != 0 ||
+ slapi_pblock_set( pb, SLAPI_PLUGIN_ENTRY_STORE_FUNC,
+ (void *) testentry_scramble ) != 0 ) {
+
+ slapi_log_error( SLAPI_LOG_PLUGIN, "testentry_init",
+ "Failed to set version and functions\n" );
+ return( -1 );
+ }
+
+ return 0;
+}
diff --git a/ldap/servers/slapd/test-plugins/testextendedop.c b/ldap/servers/slapd/test-plugins/testextendedop.c
new file mode 100644
index 00000000..fad462a7
--- /dev/null
+++ b/ldap/servers/slapd/test-plugins/testextendedop.c
@@ -0,0 +1,188 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+
+/************************************************************
+
+ testextendedop.c
+
+ This source file provides an example of a plug-in function
+ that implements an extended operation. The plug-in function
+ is called by the server if an LDAP client request contains
+ the OID "1.2.3.4" (which identifies this operation).
+
+ To test this plug-in function, you need to write an LDAP
+ v3 client that can send requests for extended operations.
+ You can use the Netscape Directory SDK for C 3.0 or the
+ Netscape Directory SDK for Java 3.0 to build these clients.
+ (These SDKs are available from DevEdge Online at
+ http://developer.netscape.com/tech/directory/)
+
+ The LDAP client should send an extended operation request
+ with the OID 1.2.3.4. To verify that the operation completed
+ successfully, your client should check the OID and value
+ returned in the LDAP response.
+
+ To test this plug-in function, stop the server, edit the dse.ldif file
+ (in the <server_root>/slapd-<server_id>/config directory)
+ and add the following lines before restarting the server :
+
+ dn: cn=Test ExtendedOp,cn=plugins,cn=config
+ objectClass: top
+ objectClass: nsSlapdPlugin
+ objectClass: extensibleObject
+ cn: Test ExtendedOp
+ nsslapd-pluginPath: <server_root>/plugins/slapd/slapi/examples/libtest-plugin.so
+ nsslapd-pluginInitfunc: testexop_init
+ nsslapd-pluginType: extendedop
+ nsslapd-pluginEnabled: on
+ nsslapd-plugin-depends-on-type: database
+ nsslapd-pluginId: test-extendedop
+ nsslapd-pluginarg0: 1.2.3.4
+
+ ************************************************************/
+
+#include <stdio.h>
+#include <string.h>
+#include "slapi-plugin.h"
+
+/* OID of the extended operation handled by this plug-in */
+#define MY_OID "1.2.3.4"
+
+Slapi_PluginDesc expdesc = { "test-extendedop", "Netscape", "0.5",
+ "sample extended operation plugin" };
+
+
+/* Extended operation plug-in */
+int
+testexop_babs( Slapi_PBlock *pb )
+{
+ char *oid;
+ struct berval *bval;
+ char *retval, *msg;
+ struct berval retbval;
+
+ /* Get the OID and the value included in the request */
+ if ( slapi_pblock_get( pb, SLAPI_EXT_OP_REQ_OID, &oid ) != 0 ||
+ slapi_pblock_get( pb, SLAPI_EXT_OP_REQ_VALUE, &bval ) != 0 ) {
+ msg = "Could not get OID and value from request.";
+ slapi_log_error( SLAPI_LOG_PLUGIN, "testexop_babs", "%s\n",
+ msg );
+ slapi_send_ldap_result( pb, LDAP_OPERATIONS_ERROR, NULL,
+ msg, 0, NULL );
+ return( SLAPI_PLUGIN_EXTENDED_SENT_RESULT );
+ } else {
+ slapi_log_error( SLAPI_LOG_PLUGIN, "testexop_babs",
+ "Received extended operation request with OID %s\n",
+ oid );
+ slapi_log_error( SLAPI_LOG_PLUGIN, "testexop_babs",
+ "Value from client: %s\n", bval->bv_val );
+ }
+
+ /* Set up the value that you want returned to the client.
+ In this case, it's just the value sent from the client,
+ preceded by the string "Value from client: " */
+
+ msg = "Value from client: ";
+ retval = ( char * )slapi_ch_malloc( bval->bv_len + strlen( msg ) + 1 );
+ sprintf( retval, "%s%s", msg, bval->bv_val );
+ retbval.bv_val = retval;
+ retbval.bv_len = strlen( retbval.bv_val );
+
+ /* Prepare to return the OID and value back to the client.
+ Note that if you want, you can return a different OID to
+ the client (for example, if you want to use the OID as
+ an indicator of something). */
+ if ( slapi_pblock_set( pb, SLAPI_EXT_OP_RET_OID, "5.6.7.8" ) != 0 ||
+ slapi_pblock_set( pb, SLAPI_EXT_OP_RET_VALUE, &retbval ) != 0 ) {
+ slapi_ch_free( ( void ** ) &retval );
+ msg = "Could not set return values";
+ slapi_log_error( SLAPI_LOG_PLUGIN, "testexop_babs", "%s\n",
+ msg );
+ slapi_send_ldap_result( pb, LDAP_OPERATIONS_ERROR, NULL,
+ msg, 0, NULL );
+ return( SLAPI_PLUGIN_EXTENDED_SENT_RESULT );
+ }
+
+ /* Send the response (containing the OID and value you set)
+ back to the client. */
+ slapi_send_ldap_result( pb, LDAP_SUCCESS, NULL,
+ "operation babs successful!", 0, NULL );
+ slapi_log_error( SLAPI_LOG_PLUGIN, "testexop_babs",
+ "OID sent to client: %s\n", "5.6.7.8" );
+ slapi_log_error( SLAPI_LOG_PLUGIN, "testexop_babs",
+ "Value sent to client: %s\n", retval );
+
+ /* Free any memory allocated by this plug-in. */
+ slapi_ch_free( ( void ** ) &retval );
+
+ /* Let front end know we sent the result */
+ return( SLAPI_PLUGIN_EXTENDED_SENT_RESULT );
+}
+
+/* Initialization function */
+#ifdef _WIN32
+__declspec(dllexport)
+#endif
+int
+testexop_init( Slapi_PBlock *pb )
+{
+ char **argv;
+ char *oid;
+ char **oidlist, **namelist;
+
+ /* Get the arguments appended to the plugin extendedop directive
+ in the plugin entry. The first argument
+ (after the standard arguments for the directive) should
+ contain the OID of the extended op.
+ */
+
+ if ( slapi_pblock_get( pb, SLAPI_PLUGIN_ARGV, &argv ) != 0 ) {
+ slapi_log_error( SLAPI_LOG_PLUGIN,
+ "testexop_init", "Could not get argv\n" );
+ return( -1 );
+ }
+
+ /* Compare the OID specified in the configuration file
+ against the OID supported by this plug-in function. */
+
+ if ( argv == NULL || strcmp( argv[0], MY_OID ) != 0 ) {
+ slapi_log_error( SLAPI_LOG_PLUGIN,
+ "testexop_init", "OID is missing or is not %s\n", MY_OID );
+ return( -1 );
+ } else {
+ oid = slapi_ch_strdup( argv[0] );
+ slapi_log_error( SLAPI_LOG_PLUGIN, "testexop_init",
+ "Registering plug-in for extended op %s.\n", oid );
+ }
+
+ oidlist = (char **) slapi_ch_malloc( 2 * sizeof( char * ) );
+ oidlist[0] = oid;
+ oidlist[1] = NULL;
+ namelist = (char **) slapi_ch_malloc( 2 * sizeof( char * ) );
+ namelist[0] = "test extended op";
+ namelist[1] = NULL;
+
+ /* Register the plug-in function as an extended operation
+ plug-in function that handles the operation identified by
+ OID 1.2.3.4. Also specify the version of the server
+ plug-in */
+ if ( slapi_pblock_set( pb, SLAPI_PLUGIN_VERSION,
+ SLAPI_PLUGIN_VERSION_01 ) != 0 ||
+ slapi_pblock_set( pb, SLAPI_PLUGIN_DESCRIPTION,
+ (void *)&expdesc ) != 0 ||
+ slapi_pblock_set( pb, SLAPI_PLUGIN_EXT_OP_FN,
+ (void *) testexop_babs ) != 0 ||
+ slapi_pblock_set( pb, SLAPI_PLUGIN_EXT_OP_OIDLIST, oidlist ) ||
+ slapi_pblock_set( pb, SLAPI_PLUGIN_EXT_OP_NAMELIST, namelist ) != 0 ) {
+
+ slapi_log_error( SLAPI_LOG_PLUGIN, "testexop_init",
+ "Failed to set plug-in version, function, and OID.\n" );
+ return( -1 );
+ }
+
+ return( 0 );
+}
+
diff --git a/ldap/servers/slapd/test-plugins/testgetip.c b/ldap/servers/slapd/test-plugins/testgetip.c
new file mode 100644
index 00000000..b8d08a52
--- /dev/null
+++ b/ldap/servers/slapd/test-plugins/testgetip.c
@@ -0,0 +1,141 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+/************************************************************
+
+ testgetip.c
+
+ This source file provides an example of a pre-operation plug-in
+ function that gets the IP address of the client and the IP
+ address of the server.
+
+ testgetip logs this information to the server error log.
+
+ To test this plug-in function, stop the server, edit the dse.ldif file
+ (in the <server_root>/slapd-<server_id>/config directory)
+ and add the following lines before restarting the server :
+
+ dn: cn=Test GetIP,cn=plugins,cn=config
+ objectClass: top
+ objectClass: nsSlapdPlugin
+ objectClass: extensibleObject
+ cn: Test GetIP
+ nsslapd-pluginPath: <server_root>/plugins/slapd/slapi/examples/libtest-plugin.so
+ nsslapd-pluginInitfunc: testgetip_init
+ nsslapd-pluginType: preoperation
+ nsslapd-pluginEnabled: on
+ nsslapd-plugin-depends-on-type: database
+ nsslapd-pluginId: test-getip
+
+ ************************************************************/
+
+#include <stdio.h>
+#include <string.h>
+#include <sys/types.h>
+#ifndef _WIN32
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#endif
+#include "slapi-plugin.h"
+#include "nspr.h"
+
+Slapi_PluginDesc getippdesc = { "test-getip", "Netscape", "0.5",
+ "sample pre-operation plugin" };
+
+static char *netaddr2str( PRNetAddr *addrp, char *buf, size_t buflen );
+
+int
+testgetip( Slapi_PBlock *pb )
+{
+ void *conn;
+ PRNetAddr client_addr, server_addr;
+ char addrbuf[ 512 ], *addrstr;
+
+ /*
+ * Don't do anything for internal operations (NULL connection).
+ */
+ if ( slapi_pblock_get( pb, SLAPI_CONNECTION, &conn ) != 0 ||
+ ( conn == NULL )) {
+ return( 0 );
+ }
+
+ /*
+ * Get the client's IP address and log it
+ */
+ if ( slapi_pblock_get( pb, SLAPI_CONN_CLIENTNETADDR, &client_addr )
+ != 0 || ( client_addr.raw.family == 0 )) {
+ slapi_log_error( SLAPI_LOG_PLUGIN, "testgetip",
+ "Could not get client IP.\n" );
+ } else if (( addrstr = netaddr2str( &client_addr, addrbuf,
+ sizeof(addrbuf))) != NULL ) {
+ slapi_log_error( SLAPI_LOG_PLUGIN, "testgetip",
+ "Client's IP address is %s\n", addrstr );
+ }
+
+ /*
+ * Get the destination (server) IP address and log it
+ */
+ if ( slapi_pblock_get( pb, SLAPI_CONN_SERVERNETADDR, &server_addr )
+ != 0 || ( server_addr.raw.family == 0 )) {
+ slapi_log_error( SLAPI_LOG_PLUGIN, "testgetip",
+ "Could not get server IP.\n" );
+ } else if (( addrstr = netaddr2str( &server_addr, addrbuf,
+ sizeof(addrbuf))) != NULL ) {
+ slapi_log_error( SLAPI_LOG_PLUGIN, "testgetip",
+ "Client sent request to server IP %s\n", addrstr );
+ }
+
+ return( 0 );
+}
+
+#ifdef _WIN32
+__declspec(dllexport)
+#endif
+int
+testgetip_init( Slapi_PBlock *pb )
+{
+ /* Register the pre-operation plug-in function. */
+ if ( slapi_pblock_set( pb, SLAPI_PLUGIN_VERSION,
+ SLAPI_PLUGIN_VERSION_01 ) != 0 ||
+ slapi_pblock_set( pb, SLAPI_PLUGIN_DESCRIPTION,
+ (void *)&getippdesc ) != 0 ||
+ slapi_pblock_set( pb, SLAPI_PLUGIN_PRE_SEARCH_FN,
+ (void *) testgetip ) != 0 ) {
+ slapi_log_error( SLAPI_LOG_FATAL, "testgetip_init",
+ "Failed to set version and functions.\n" );
+ return( -1 );
+ }
+
+ slapi_log_error( SLAPI_LOG_PLUGIN, "testgetip_init",
+ "Registered preop plugins.\n" );
+ return( 0 );
+}
+
+
+/*
+ * Utility function to convert a PRNetAddr to a human readable string.
+ */
+static char *
+netaddr2str( PRNetAddr *addrp, char *buf, size_t buflen )
+{
+ char *addrstr;
+
+ *buf = '\0';
+ if ( PR_NetAddrToString( addrp, buf, buflen ) != PR_SUCCESS ) {
+ slapi_log_error( SLAPI_LOG_PLUGIN, "testgetip",
+ "PR_NetAddrToString failed.\n" );
+ return( NULL );
+ }
+
+ /* skip past leading ::ffff: if IPv4 address */
+ if ( strlen( buf ) > 7 && strncmp( buf, "::ffff:", 7 ) == 0 ) {
+ addrstr = buf + 7;
+ } else {
+ addrstr = buf;
+ }
+
+ return( addrstr );
+}
diff --git a/ldap/servers/slapd/test-plugins/testplugin.def b/ldap/servers/slapd/test-plugins/testplugin.def
new file mode 100644
index 00000000..b14c082e
--- /dev/null
+++ b/ldap/servers/slapd/test-plugins/testplugin.def
@@ -0,0 +1,16 @@
+; BEGIN COPYRIGHT BLOCK
+; Copyright 2001 Sun Microsystems, Inc.
+; Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+; All rights reserved.
+; END COPYRIGHT BLOCK
+;
+DESCRIPTION 'Netscape Directory Server 7 test plugin'
+EXPORTS
+ testentry_scramble @2
+ testentry_unscramble @3
+ testexop_init @4
+ testpostop_init @5
+ testpreop_init @6
+ testsasl_init @7
+ testbind_init @8
+ nullsuffix_init @9
diff --git a/ldap/servers/slapd/test-plugins/testplugin.dsp b/ldap/servers/slapd/test-plugins/testplugin.dsp
new file mode 100644
index 00000000..d956bcd3
--- /dev/null
+++ b/ldap/servers/slapd/test-plugins/testplugin.dsp
@@ -0,0 +1,143 @@
+#
+# BEGIN COPYRIGHT BLOCK
+# Copyright 2001 Sun Microsystems, Inc.
+# Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+# All rights reserved.
+# END COPYRIGHT BLOCK
+#
+# Microsoft Developer Studio Project File - Name="testplugin" - Package Owner=<4>
+# Microsoft Developer Studio Generated Build File, Format Version 5.00
+# ** DO NOT EDIT **
+
+# TARGTYPE "Win32 (x86) Dynamic-Link Library" 0x0102
+
+CFG=testplugin - Win32 Release
+!MESSAGE This is not a valid makefile. To build this project using NMAKE,
+!MESSAGE use the Export Makefile command and run
+!MESSAGE
+!MESSAGE NMAKE /f "testplugin.mak".
+!MESSAGE
+!MESSAGE You can specify a configuration when running NMAKE
+!MESSAGE by defining the macro CFG on the command line. For example:
+!MESSAGE
+!MESSAGE NMAKE /f "testplugin.mak" CFG="testplugin - Win32 Release"
+!MESSAGE
+!MESSAGE Possible choices for configuration are:
+!MESSAGE
+!MESSAGE "testplugin - Win32 Release" (based on\
+ "Win32 (x86) Dynamic-Link Library")
+!MESSAGE "testplugin - Win32 Debug" (based on\
+ "Win32 (x86) Dynamic-Link Library")
+!MESSAGE
+
+# Begin Project
+# PROP Scc_ProjName ""
+# PROP Scc_LocalPath ""
+CPP=cl.exe
+MTL=midl.exe
+RSC=rc.exe
+
+!IF "$(CFG)" == "testplugin - Win32 Release"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 0
+# PROP BASE Output_Dir ".\Release"
+# PROP BASE Intermediate_Dir ".\Release"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 0
+# PROP Output_Dir ".\Release"
+# PROP Intermediate_Dir ".\Release"
+# PROP Ignore_Export_Lib 0
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /MT /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /YX /c
+# ADD CPP /nologo /MD /W3 /GX /O2 /I "..\include" /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_WIN32" /YX /FD /c
+# ADD BASE MTL /nologo /D "NDEBUG" /win32
+# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32
+# ADD BASE RSC /l 0x409 /d "NDEBUG"
+# ADD RSC /l 0x409 /d "NDEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LINK32=link.exe
+# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /dll /machine:I386
+# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib wsock32.lib ..\lib\libslapd.lib /nologo /subsystem:windows /dll /machine:I386
+
+!ELSEIF "$(CFG)" == "testplugin - Win32 Debug"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 1
+# PROP BASE Output_Dir ".\Debug"
+# PROP BASE Intermediate_Dir ".\Debug"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 1
+# PROP Output_Dir ".\Debug"
+# PROP Intermediate_Dir ".\Debug"
+# PROP Ignore_Export_Lib 0
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /MTd /W3 /Gm /GX /Zi /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /YX /c
+# ADD CPP /nologo /MD /W3 /Gm /GX /Zi /Od /I "..\include" /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_WIN32" /YX /FD /c
+# ADD BASE MTL /nologo /D "_DEBUG" /win32
+# ADD MTL /nologo /D "_DEBUG" /mktyplib203 /win32
+# ADD BASE RSC /l 0x409 /d "_DEBUG"
+# ADD RSC /l 0x409 /d "_DEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LINK32=link.exe
+# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /dll /debug /machine:I386
+# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib wsock32.lib ..\lib\libslapd.lib /nologo /subsystem:windows /dll /debug /machine:I386
+
+!ENDIF
+
+# Begin Target
+
+# Name "testplugin - Win32 Release"
+# Name "testplugin - Win32 Debug"
+# Begin Group "Source Files"
+
+# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;hpj;bat;for;f90"
+# Begin Source File
+
+SOURCE=.\dllmain.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\testbind.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\testentry.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\testextendedop.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\testplugin.def
+# End Source File
+# Begin Source File
+
+SOURCE=.\testpostop.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\testpreop.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\testsaslbind.c
+# End Source File
+# End Group
+# Begin Group "Header Files"
+
+# PROP Default_Filter "h;hpp;hxx;hm;inl;fi;fd"
+# End Group
+# Begin Group "Resource Files"
+
+# PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;cnt;rtf;gif;jpg;jpeg;jpe"
+# End Group
+# End Target
+# End Project
diff --git a/ldap/servers/slapd/test-plugins/testplugin.mak b/ldap/servers/slapd/test-plugins/testplugin.mak
new file mode 100644
index 00000000..ca48774c
--- /dev/null
+++ b/ldap/servers/slapd/test-plugins/testplugin.mak
@@ -0,0 +1,431 @@
+#
+# BEGIN COPYRIGHT BLOCK
+# Copyright 2001 Sun Microsystems, Inc.
+# Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+# All rights reserved.
+# END COPYRIGHT BLOCK
+#
+# Microsoft Developer Studio Generated NMAKE File, Based on testplugin.dsp
+!IF "$(CFG)" == ""
+CFG=testplugin - Win32 Release
+!MESSAGE No configuration specified. Defaulting to testplugin - Win32 Release.
+!ENDIF
+
+!IF "$(CFG)" != "testplugin - Win32 Release" && "$(CFG)" !=\
+ "testplugin - Win32 Debug"
+!MESSAGE Invalid configuration "$(CFG)" specified.
+!MESSAGE You can specify a configuration when running NMAKE
+!MESSAGE by defining the macro CFG on the command line. For example:
+!MESSAGE
+!MESSAGE NMAKE /f "testplugin.mak" CFG="testplugin - Win32 Release"
+!MESSAGE
+!MESSAGE Possible choices for configuration are:
+!MESSAGE
+!MESSAGE "testplugin - Win32 Release" (based on\
+ "Win32 (x86) Dynamic-Link Library")
+!MESSAGE "testplugin - Win32 Debug" (based on\
+ "Win32 (x86) Dynamic-Link Library")
+!MESSAGE
+!ERROR An invalid configuration is specified.
+!ENDIF
+
+!IF "$(OS)" == "Windows_NT"
+NULL=
+!ELSE
+NULL=nul
+!ENDIF
+
+CPP=cl.exe
+MTL=midl.exe
+RSC=rc.exe
+
+!IF "$(CFG)" == "testplugin - Win32 Release"
+
+OUTDIR=.\Release
+INTDIR=.\Release
+# Begin Custom Macros
+OutDir=.\.\Release
+# End Custom Macros
+
+!IF "$(RECURSE)" == "0"
+
+ALL : "$(OUTDIR)\testplugin.dll"
+
+!ELSE
+
+ALL : "$(OUTDIR)\testplugin.dll"
+
+!ENDIF
+
+CLEAN :
+ -@erase "$(INTDIR)\dllmain.obj"
+ -@erase "$(INTDIR)\testbind.obj"
+ -@erase "$(INTDIR)\testentry.obj"
+ -@erase "$(INTDIR)\testextendedop.obj"
+ -@erase "$(INTDIR)\testpostop.obj"
+ -@erase "$(INTDIR)\testpreop.obj"
+ -@erase "$(INTDIR)\testsaslbind.obj"
+ -@erase "$(INTDIR)\vc50.idb"
+ -@erase "$(OUTDIR)\testplugin.dll"
+ -@erase "$(OUTDIR)\testplugin.exp"
+ -@erase "$(OUTDIR)\testplugin.lib"
+
+"$(OUTDIR)" :
+ if not exist "$(OUTDIR)/$(NULL)" mkdir "$(OUTDIR)"
+
+CPP_PROJ=/nologo /MD /W3 /GX /O2 /I "..\include" /D "WIN32" /D "NDEBUG" /D\
+ "_WINDOWS" /D "_WIN32" /Fp"$(INTDIR)\testplugin.pch" /YX /Fo"$(INTDIR)\\"\
+ /Fd"$(INTDIR)\\" /FD /c
+CPP_OBJS=.\Release/
+CPP_SBRS=.
+MTL_PROJ=/nologo /D "NDEBUG" /mktyplib203 /win32
+BSC32=bscmake.exe
+BSC32_FLAGS=/nologo /o"$(OUTDIR)\testplugin.bsc"
+BSC32_SBRS= \
+
+LINK32=link.exe
+LINK32_FLAGS=kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib\
+ advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib\
+ odbccp32.lib wsock32.lib ..\lib\libslapd.lib /nologo /subsystem:windows /dll\
+ /incremental:no /pdb:"$(OUTDIR)\testplugin.pdb" /machine:I386\
+ /def:".\testplugin.def" /out:"$(OUTDIR)\testplugin.dll"\
+ /implib:"$(OUTDIR)\testplugin.lib"
+DEF_FILE= \
+ ".\testplugin.def"
+LINK32_OBJS= \
+ "$(INTDIR)\dllmain.obj" \
+ "$(INTDIR)\testbind.obj" \
+ "$(INTDIR)\testentry.obj" \
+ "$(INTDIR)\testextendedop.obj" \
+ "$(INTDIR)\testpostop.obj" \
+ "$(INTDIR)\testpreop.obj" \
+ "$(INTDIR)\testsaslbind.obj"
+
+"$(OUTDIR)\testplugin.dll" : "$(OUTDIR)" $(DEF_FILE) $(LINK32_OBJS)
+ $(LINK32) @<<
+ $(LINK32_FLAGS) $(LINK32_OBJS)
+<<
+
+!ELSEIF "$(CFG)" == "testplugin - Win32 Debug"
+
+OUTDIR=.\Debug
+INTDIR=.\Debug
+# Begin Custom Macros
+OutDir=.\.\Debug
+# End Custom Macros
+
+!IF "$(RECURSE)" == "0"
+
+ALL : "$(OUTDIR)\testplugin.dll"
+
+!ELSE
+
+ALL : "$(OUTDIR)\testplugin.dll"
+
+!ENDIF
+
+CLEAN :
+ -@erase "$(INTDIR)\dllmain.obj"
+ -@erase "$(INTDIR)\testbind.obj"
+ -@erase "$(INTDIR)\testentry.obj"
+ -@erase "$(INTDIR)\testextendedop.obj"
+ -@erase "$(INTDIR)\testpostop.obj"
+ -@erase "$(INTDIR)\testpreop.obj"
+ -@erase "$(INTDIR)\testsaslbind.obj"
+ -@erase "$(INTDIR)\vc50.idb"
+ -@erase "$(INTDIR)\vc50.pdb"
+ -@erase "$(OUTDIR)\testplugin.dll"
+ -@erase "$(OUTDIR)\testplugin.exp"
+ -@erase "$(OUTDIR)\testplugin.ilk"
+ -@erase "$(OUTDIR)\testplugin.lib"
+ -@erase "$(OUTDIR)\testplugin.pdb"
+
+"$(OUTDIR)" :
+ if not exist "$(OUTDIR)/$(NULL)" mkdir "$(OUTDIR)"
+
+CPP_PROJ=/nologo /MD /W3 /Gm /GX /Zi /Od /I "..\include" /D "WIN32" /D "_DEBUG"\
+ /D "_WINDOWS" /D "_WIN32" /Fp"$(INTDIR)\testplugin.pch" /YX /Fo"$(INTDIR)\\"\
+ /Fd"$(INTDIR)\\" /FD /c
+CPP_OBJS=.\Debug/
+CPP_SBRS=.
+MTL_PROJ=/nologo /D "_DEBUG" /mktyplib203 /win32
+BSC32=bscmake.exe
+BSC32_FLAGS=/nologo /o"$(OUTDIR)\testplugin.bsc"
+BSC32_SBRS= \
+
+LINK32=link.exe
+LINK32_FLAGS=kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib\
+ advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib\
+ odbccp32.lib wsock32.lib ..\lib\libslapd.lib /nologo /subsystem:windows /dll\
+ /incremental:yes /pdb:"$(OUTDIR)\testplugin.pdb" /debug /machine:I386\
+ /def:".\testplugin.def" /out:"$(OUTDIR)\testplugin.dll"\
+ /implib:"$(OUTDIR)\testplugin.lib"
+DEF_FILE= \
+ ".\testplugin.def"
+LINK32_OBJS= \
+ "$(INTDIR)\dllmain.obj" \
+ "$(INTDIR)\testbind.obj" \
+ "$(INTDIR)\testentry.obj" \
+ "$(INTDIR)\testextendedop.obj" \
+ "$(INTDIR)\testpostop.obj" \
+ "$(INTDIR)\testpreop.obj" \
+ "$(INTDIR)\testsaslbind.obj"
+
+"$(OUTDIR)\testplugin.dll" : "$(OUTDIR)" $(DEF_FILE) $(LINK32_OBJS)
+ $(LINK32) @<<
+ $(LINK32_FLAGS) $(LINK32_OBJS)
+<<
+
+!ENDIF
+
+.c{$(CPP_OBJS)}.obj::
+ $(CPP) @<<
+ $(CPP_PROJ) $<
+<<
+
+.cpp{$(CPP_OBJS)}.obj::
+ $(CPP) @<<
+ $(CPP_PROJ) $<
+<<
+
+.cxx{$(CPP_OBJS)}.obj::
+ $(CPP) @<<
+ $(CPP_PROJ) $<
+<<
+
+.c{$(CPP_SBRS)}.sbr::
+ $(CPP) @<<
+ $(CPP_PROJ) $<
+<<
+
+.cpp{$(CPP_SBRS)}.sbr::
+ $(CPP) @<<
+ $(CPP_PROJ) $<
+<<
+
+.cxx{$(CPP_SBRS)}.sbr::
+ $(CPP) @<<
+ $(CPP_PROJ) $<
+<<
+
+
+!IF "$(CFG)" == "testplugin - Win32 Release" || "$(CFG)" ==\
+ "testplugin - Win32 Debug"
+SOURCE=.\dllmain.c
+
+!IF "$(CFG)" == "testplugin - Win32 Release"
+
+DEP_CPP_DLLMA=\
+ {$(INCLUDE)}"lber.h"\
+ {$(INCLUDE)}"ldap.h"\
+
+
+"$(INTDIR)\dllmain.obj" : $(SOURCE) $(DEP_CPP_DLLMA) "$(INTDIR)"
+
+
+!ELSEIF "$(CFG)" == "testplugin - Win32 Debug"
+
+DEP_CPP_DLLMA=\
+ {$(INCLUDE)}"lber.h"\
+ {$(INCLUDE)}"ldap.h"\
+ {$(INCLUDE)}"sys\types.h"\
+
+NODEP_CPP_DLLMA=\
+ "..\include\macsock.h"\
+ "..\include\os2sock.h"\
+
+
+"$(INTDIR)\dllmain.obj" : $(SOURCE) $(DEP_CPP_DLLMA) "$(INTDIR)"
+
+
+!ENDIF
+
+SOURCE=.\testbind.c
+
+!IF "$(CFG)" == "testplugin - Win32 Release"
+
+DEP_CPP_TESTB=\
+ "..\include\slapi-plugin.h"\
+ {$(INCLUDE)}"lber.h"\
+ {$(INCLUDE)}"ldap.h"\
+
+
+"$(INTDIR)\testbind.obj" : $(SOURCE) $(DEP_CPP_TESTB) "$(INTDIR)"
+
+
+!ELSEIF "$(CFG)" == "testplugin - Win32 Debug"
+
+DEP_CPP_TESTB=\
+ "..\include\slapi-plugin.h"\
+ {$(INCLUDE)}"lber.h"\
+ {$(INCLUDE)}"ldap.h"\
+ {$(INCLUDE)}"sys\types.h"\
+
+NODEP_CPP_TESTB=\
+ "..\include\macsock.h"\
+ "..\include\os2sock.h"\
+
+
+"$(INTDIR)\testbind.obj" : $(SOURCE) $(DEP_CPP_TESTB) "$(INTDIR)"
+
+
+!ENDIF
+
+SOURCE=.\testentry.c
+
+!IF "$(CFG)" == "testplugin - Win32 Release"
+
+DEP_CPP_TESTE=\
+ "..\include\slapi-plugin.h"\
+ {$(INCLUDE)}"lber.h"\
+ {$(INCLUDE)}"ldap.h"\
+
+
+"$(INTDIR)\testentry.obj" : $(SOURCE) $(DEP_CPP_TESTE) "$(INTDIR)"
+
+
+!ELSEIF "$(CFG)" == "testplugin - Win32 Debug"
+
+DEP_CPP_TESTE=\
+ "..\include\slapi-plugin.h"\
+ {$(INCLUDE)}"lber.h"\
+ {$(INCLUDE)}"ldap.h"\
+ {$(INCLUDE)}"sys\types.h"\
+
+NODEP_CPP_TESTE=\
+ "..\include\macsock.h"\
+ "..\include\os2sock.h"\
+
+
+"$(INTDIR)\testentry.obj" : $(SOURCE) $(DEP_CPP_TESTE) "$(INTDIR)"
+
+
+!ENDIF
+
+SOURCE=.\testextendedop.c
+
+!IF "$(CFG)" == "testplugin - Win32 Release"
+
+DEP_CPP_TESTEX=\
+ "..\include\slapi-plugin.h"\
+ {$(INCLUDE)}"lber.h"\
+ {$(INCLUDE)}"ldap.h"\
+
+
+"$(INTDIR)\testextendedop.obj" : $(SOURCE) $(DEP_CPP_TESTEX) "$(INTDIR)"
+
+
+!ELSEIF "$(CFG)" == "testplugin - Win32 Debug"
+
+DEP_CPP_TESTEX=\
+ "..\include\slapi-plugin.h"\
+ {$(INCLUDE)}"lber.h"\
+ {$(INCLUDE)}"ldap.h"\
+ {$(INCLUDE)}"sys\types.h"\
+
+NODEP_CPP_TESTEX=\
+ "..\include\macsock.h"\
+ "..\include\os2sock.h"\
+
+
+"$(INTDIR)\testextendedop.obj" : $(SOURCE) $(DEP_CPP_TESTEX) "$(INTDIR)"
+
+
+!ENDIF
+
+SOURCE=.\testpostop.c
+
+!IF "$(CFG)" == "testplugin - Win32 Release"
+
+DEP_CPP_TESTP=\
+ "..\include\slapi-plugin.h"\
+ {$(INCLUDE)}"lber.h"\
+ {$(INCLUDE)}"ldap.h"\
+
+
+"$(INTDIR)\testpostop.obj" : $(SOURCE) $(DEP_CPP_TESTP) "$(INTDIR)"
+
+
+!ELSEIF "$(CFG)" == "testplugin - Win32 Debug"
+
+DEP_CPP_TESTP=\
+ "..\include\slapi-plugin.h"\
+ {$(INCLUDE)}"lber.h"\
+ {$(INCLUDE)}"ldap.h"\
+ {$(INCLUDE)}"sys\types.h"\
+
+NODEP_CPP_TESTP=\
+ "..\include\macsock.h"\
+ "..\include\os2sock.h"\
+
+
+"$(INTDIR)\testpostop.obj" : $(SOURCE) $(DEP_CPP_TESTP) "$(INTDIR)"
+
+
+!ENDIF
+
+SOURCE=.\testpreop.c
+
+!IF "$(CFG)" == "testplugin - Win32 Release"
+
+DEP_CPP_TESTPR=\
+ "..\include\slapi-plugin.h"\
+ {$(INCLUDE)}"lber.h"\
+ {$(INCLUDE)}"ldap.h"\
+
+
+"$(INTDIR)\testpreop.obj" : $(SOURCE) $(DEP_CPP_TESTPR) "$(INTDIR)"
+
+
+!ELSEIF "$(CFG)" == "testplugin - Win32 Debug"
+
+DEP_CPP_TESTPR=\
+ "..\include\slapi-plugin.h"\
+ {$(INCLUDE)}"lber.h"\
+ {$(INCLUDE)}"ldap.h"\
+ {$(INCLUDE)}"sys\types.h"\
+
+NODEP_CPP_TESTPR=\
+ "..\include\macsock.h"\
+ "..\include\os2sock.h"\
+
+
+"$(INTDIR)\testpreop.obj" : $(SOURCE) $(DEP_CPP_TESTPR) "$(INTDIR)"
+
+
+!ENDIF
+
+SOURCE=.\testsaslbind.c
+
+!IF "$(CFG)" == "testplugin - Win32 Release"
+
+DEP_CPP_TESTS=\
+ "..\include\slapi-plugin.h"\
+ {$(INCLUDE)}"lber.h"\
+ {$(INCLUDE)}"ldap.h"\
+
+
+"$(INTDIR)\testsaslbind.obj" : $(SOURCE) $(DEP_CPP_TESTS) "$(INTDIR)"
+
+
+!ELSEIF "$(CFG)" == "testplugin - Win32 Debug"
+
+DEP_CPP_TESTS=\
+ "..\include\slapi-plugin.h"\
+ {$(INCLUDE)}"lber.h"\
+ {$(INCLUDE)}"ldap.h"\
+ {$(INCLUDE)}"sys\types.h"\
+
+NODEP_CPP_TESTS=\
+ "..\include\macsock.h"\
+ "..\include\os2sock.h"\
+
+
+"$(INTDIR)\testsaslbind.obj" : $(SOURCE) $(DEP_CPP_TESTS) "$(INTDIR)"
+
+
+!ENDIF
+
+
+!ENDIF
+
diff --git a/ldap/servers/slapd/test-plugins/testpostop.c b/ldap/servers/slapd/test-plugins/testpostop.c
new file mode 100644
index 00000000..5dbf18d1
--- /dev/null
+++ b/ldap/servers/slapd/test-plugins/testpostop.c
@@ -0,0 +1,386 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+
+/************************************************************
+
+ testpostop.c
+
+ This source file provides examples of post-operation plug-in
+ functions. The server calls these plug-in functions after
+ executing certain LDAP operations:
+
+ * testpostop_add (called after an LDAP add operation)
+ * testpostop_mod (called after an LDAP modify operation)
+ * testpostop_del (called after an LDAP delete operation)
+ * testpostop_modrdn (called after an LDAP modify RDN operation)
+ * testpostop_abandon (called after an LDAP abandon operation)
+
+ After the server processes an LDAP add, modify, delete, or
+ modify RDN operation, these post-operation plug-in functions
+ log information about the operation to a change log file.
+
+ The post-abandon plugin simply logs some information to the error
+ log to demonstrate that it was called.
+
+ To test this plug-in function, stop the server, edit the dse.ldif file
+ (in the <server_root>/slapd-<server_id>/config directory)
+ and add the following lines before restarting the server :
+
+ dn: cn=Test PostOp,cn=plugins,cn=config
+ objectClass: top
+ objectClass: nsSlapdPlugin
+ objectClass: extensibleObject
+ cn: Test PostOp
+ nsslapd-pluginPath: <server_root>/plugins/slapd/slapi/examples/libtest-plugin.so
+ nsslapd-pluginInitfunc: testpostop_init
+ nsslapd-pluginType: postoperation
+ nsslapd-pluginEnabled: on
+ nsslapd-plugin-depends-on-type: database
+ nsslapd-pluginId: test-postop
+
+ ************************************************************/
+
+#include <stdio.h>
+#include <string.h>
+#include <ctype.h>
+#include <time.h>
+#include "slapi-plugin.h"
+
+#define SLAPD_LOGGING 1
+#define _ADD 0
+#define _MOD 1
+#define _DEL 2
+#define _MODRDN 3
+
+#ifdef _WIN32
+static char changelogfile[MAX_PATH+1];
+#else
+static char *changelogfile = "/tmp/changelog";
+#endif
+
+Slapi_PluginDesc postoppdesc = { "test-postop", "Netscape", "0.5",
+ "sample post-operation plugin" };
+
+static void write_changelog( int optype, char *dn, void *change, int flag );
+
+/* Current time is a function defined in the server. */
+time_t current_time( void );
+
+/* Post-operation plug-in function */
+int
+testpostop_add( Slapi_PBlock *pb )
+{
+ Slapi_Entry *e;
+ char *dn;
+
+ /* Get the entry that has been added and the DN of
+ that entry. */
+ if ( slapi_pblock_get( pb, SLAPI_ADD_ENTRY, &e ) != 0 ||
+ slapi_pblock_get( pb, SLAPI_ADD_TARGET, &dn ) != 0 ) {
+ slapi_log_error( SLAPI_LOG_PLUGIN,
+ "testpostop_add", "Could not get parameters\n" );
+ return( -1 );
+ }
+
+ /* Log the DN of the newly added entry in the server error log. */
+ slapi_log_error( SLAPI_LOG_PLUGIN, "testpostop_add",
+ "Added entry (%s)\n", dn );
+
+ /* Log the DN and the entry to the change log file. */
+ write_changelog( _ADD, dn, (void *) e, 0 );
+
+ return( 0 ); /* allow the operation to continue */
+}
+
+/* Post-operation plug-in function. */
+int
+testpostop_mod( Slapi_PBlock *pb )
+{
+ char *dn;
+ LDAPMod **mods;
+
+ /* Get the DN of the modified entry and the modifications made. */
+ if ( slapi_pblock_get( pb, SLAPI_MODIFY_TARGET, &dn ) != 0 ||
+ slapi_pblock_get( pb, SLAPI_MODIFY_MODS, &mods ) != 0 ) {
+ slapi_log_error( SLAPI_LOG_PLUGIN,
+ "testpostop_mod", "Could not get parameters\n" );
+ return( -1 );
+ }
+
+ /* Log the DN of the modified entry to the server error log. */
+ slapi_log_error( SLAPI_LOG_PLUGIN, "testpostop_mod",
+ "Modified entry (%s)\n", dn );
+
+ /* Log the DN and the modifications made to the change log file. */
+ write_changelog( _MOD, dn, (void *) mods, 0 );
+
+ return( 0 ); /* allow the operation to continue */
+}
+
+/* Post-operation plug-in function */
+int
+testpostop_del( Slapi_PBlock *pb )
+{
+ char *dn;
+
+ /* Get the DN of the entry that was removed from the directory. */
+ if ( slapi_pblock_get( pb, SLAPI_DELETE_TARGET, &dn ) != 0 ) {
+ slapi_log_error( SLAPI_LOG_PLUGIN,
+ "testpostop_del", "Could not get parameters\n" );
+ return( -1 );
+ }
+
+ /* Log the DN of the deleted entry to the server error log. */
+ slapi_log_error( SLAPI_LOG_PLUGIN, "testpostop_del",
+ "Deleted entry (%s)\n", dn );
+
+ /* Log the DN of the deleted entry to the change log. */
+ write_changelog( _DEL, dn, NULL, 0 );
+
+ return( 0 ); /* allow the operation to continue */
+}
+
+/* Post-operation plug-in function */
+int
+testpostop_modrdn( Slapi_PBlock *pb )
+{
+ char *dn;
+ char *newrdn;
+ int dflag;
+
+ /* Get the DN of the renamed entry, the new RDN of the entry,
+ and the flag indicating whether or not the old RDN was
+ removed from the entry. */
+ if ( slapi_pblock_get( pb, SLAPI_MODRDN_TARGET, &dn ) != 0 ||
+ slapi_pblock_get( pb, SLAPI_MODRDN_NEWRDN, &newrdn ) != 0 ||
+ slapi_pblock_get( pb, SLAPI_MODRDN_DELOLDRDN, &dflag ) != 0 ) {
+ slapi_log_error( SLAPI_LOG_PLUGIN,
+ "testpostop_modrdn", "Could not get parameters\n" );
+ return( -1 );
+ }
+
+ /* Log the DN of the renamed entry to the server error log. */
+ slapi_log_error( SLAPI_LOG_PLUGIN, "testpostop_modrdn",
+ "modrdn entry (%s)\n", dn );
+
+ /* Log the DN of the renamed entry, its new RDN, and the
+ flag (the one indicating whether or not the old RDN was
+ removed from the entry) to the change log. */
+ write_changelog( _MODRDN, dn, (void *) newrdn, dflag );
+
+ return( 0 ); /* allow the operation to continue */
+}
+
+/* Post-operation plug-in function */
+int
+testpostop_abandon( Slapi_PBlock *pb )
+{
+ int msgid;
+
+ /* Get the LDAP message ID of the abandoned operation */
+ if ( slapi_pblock_get( pb, SLAPI_ABANDON_MSGID, &msgid ) != 0 ) {
+ slapi_log_error( SLAPI_LOG_PLUGIN,
+ "testpostop_abandon", "Could not get parameters\n" );
+ return( -1 );
+ }
+
+ /* Log information about the abandon operation to the
+ server error log. */
+ slapi_log_error( SLAPI_LOG_PLUGIN, "testpostop_abandon",
+ "Postoperation abandon function called.\n"
+ "\tTarget MsgID: %d\n",
+ msgid );
+
+ return( 0 ); /* allow the operation to continue */
+}
+
+/* Initialization function */
+#ifdef _WIN32
+__declspec(dllexport)
+#endif
+int
+testpostop_init( Slapi_PBlock *pb )
+{
+ /* Register the four post-operation plug-in functions,
+ and specify the server plug-in version. */
+ if ( slapi_pblock_set( pb, SLAPI_PLUGIN_VERSION,
+ SLAPI_PLUGIN_VERSION_01 ) != 0 ||
+ slapi_pblock_set( pb, SLAPI_PLUGIN_DESCRIPTION,
+ (void *)&postoppdesc ) != 0 ||
+ slapi_pblock_set( pb, SLAPI_PLUGIN_POST_ADD_FN,
+ (void *) testpostop_add ) != 0 ||
+ slapi_pblock_set( pb, SLAPI_PLUGIN_POST_MODIFY_FN,
+ (void *) testpostop_mod ) != 0 ||
+ slapi_pblock_set( pb, SLAPI_PLUGIN_POST_DELETE_FN,
+ (void *) testpostop_del ) != 0 ||
+ slapi_pblock_set( pb, SLAPI_PLUGIN_POST_MODRDN_FN,
+ (void *) testpostop_modrdn ) != 0 ||
+ slapi_pblock_set( pb, SLAPI_PLUGIN_POST_ABANDON_FN,
+ (void *) testpostop_abandon ) != 0 ) {
+ slapi_log_error( SLAPI_LOG_PLUGIN, "testpostop_init",
+ "Failed to set version and functions\n" );
+ return( -1 );
+ }
+ return( 0 );
+}
+
+/* Function for generating a newly allocated string that contains the
+ specified time. The time is expressed as generalizedTime, except
+ without the time zone. */
+static char*
+format_localTime( time_t timeval )
+{
+ char* into;
+ struct tm t;
+#ifdef _WIN32
+ memcpy (&t, localtime (&timeval), sizeof(t));
+#else
+ localtime_r (&timeval, &t);
+#endif
+
+ /* Allocate memory for the formatted string. (slapi_ch_malloc()
+ should be used in server plug-ins instead of malloc().)
+ This string is freed by the calling function write_changelog(). */
+ into = slapi_ch_malloc(15);
+ sprintf (into, "%.4li%.2i%.2i%.2i%.2i%.2i",
+ 1900L + t.tm_year, 1 + t.tm_mon, t.tm_mday, t.tm_hour, t.tm_min, t.tm_sec);
+ return into;
+}
+
+/* Logs information on an operation to a change log file.
+ Parameters;
+ - optype is type of LDAP operation to record:
+ - _ADD for LDAP add operations
+ - _MOD for LDAP modify operations
+ - _DEL for LDAP delete operations
+ - _MODRDN for LDAP modify RDN operations
+ - dn is DN of the entry affected by the operation.
+ - change is information about the operation performed.
+ The type of information depends on the value of optype:
+ - For _ADD, it is the newly added entry (Slapi_Entry).
+ - For _MOD, it is the list of modifications made (array of
+ LDAPMod).
+ - For _DEL, it is NULL.
+ - For _MODRDN, it is the new RDN of the entry.
+ - flag is only used for LDAP modify RDN operations.
+ It represents the flag that indicates whether or not
+ the old RDN has been removed.
+*/
+static void
+write_changelog(
+ int optype,
+ char *dn,
+ void *change,
+ int flag
+)
+{
+ LDAPMod **mods;
+ Slapi_Entry *e;
+ char *newrdn, *tmp, *tmpsave;
+ FILE *fp;
+ int len, i, j;
+ char* timestr;
+#ifdef _WIN32
+ char szTmpPath[MAX_PATH+1];
+#endif
+
+ /* Open the change log file */
+#ifdef _WIN32
+ GetTempPath( MAX_PATH, szTmpPath );
+ strcpy( changelogfile, szTmpPath );
+ strcat( changelogfile, "\\" );
+ strcat( changelogfile, "changelog.txt" );
+#endif
+ if ( changelogfile == NULL ) {
+ return;
+ }
+ if ( (fp = fopen( changelogfile, "ab" )) == NULL ) {
+ slapi_log_error( SLAPI_LOG_PLUGIN, "write_changelog",
+ "Could not open log file %s\n", changelogfile );
+ return;
+ }
+
+ /* Log the current time of the operation in generalizedTime form */
+ timestr = format_localTime( current_time() );
+ fprintf( fp, "time: %s\n", timestr );
+ slapi_ch_free( ( void ** ) &timestr);
+ timestr = NULL;
+
+ /* Print the DN of the entry affected by the operation. */
+ fprintf( fp, "dn: %s\n", dn );
+
+ /* Log information about the operation */
+ switch ( optype ) {
+ case _MOD:
+ /* For modify operations, log the attribute type
+ that has been added, replaced, or deleted. */
+ fprintf( fp, "changetype: modify\n" );
+ mods = (LDAPMod **)change;
+ for ( j = 0; mods[j] != NULL; j++ ) {
+ switch ( mods[j]->mod_op & ~LDAP_MOD_BVALUES ) {
+ case LDAP_MOD_ADD:
+ fprintf( fp, "add: %s\n", mods[j]->mod_type );
+ break;
+
+ case LDAP_MOD_DELETE:
+ fprintf( fp, "delete: %s\n", mods[j]->mod_type );
+ break;
+
+ case LDAP_MOD_REPLACE:
+ fprintf( fp, "replace: %s\n", mods[j]->mod_type );
+ break;
+ }
+
+ for ( i = 0; mods[j]->mod_bvalues != NULL &&
+ mods[j]->mod_bvalues[i] != NULL; i++ ) {
+ /* XXX should handle binary values XXX */
+ fprintf( fp, "%s: %s\n", mods[j]->mod_type,
+ mods[j]->mod_bvalues[i]->bv_val );
+ }
+ fprintf( fp, "-\n" );
+ }
+ break;
+
+ case _ADD:
+ /* For LDAP add operations, log the newly added entry. */
+ e = (Slapi_Entry *)change;
+ fprintf( fp, "changetype: add\n" );
+ /* Get the LDIF string representation of the entry. */
+ tmp = slapi_entry2str( e, &len );
+ tmpsave = tmp;
+ /* Skip the first line, which is the dn: line */
+ while (( tmp = strchr( tmp, '\n' )) != NULL ) {
+ tmp++;
+ if ( !isspace( *tmp )) {
+ break;
+ }
+ }
+ fprintf( fp, "%s", tmp );
+ slapi_ch_free( (void **)&tmpsave );
+ break;
+
+ case _DEL:
+ /* For the LDAP delete operation, log the DN of the
+ removed entry. (Since this is already done earlier,
+ the plug-in just needs to note the type of operation
+ performed. */
+ fprintf( fp, "changetype: delete\n" );
+ break;
+
+ case _MODRDN:
+ /* For the LDAP modify RDN operation, log the new RDN
+ and the flag indicating whether or not the old RDN
+ was removed. */
+ newrdn = (char *)change;
+ fprintf( fp, "changetype: modrdn\n" );
+ fprintf( fp, "newrdn: %s\n", newrdn );
+ fprintf( fp, "deleteoldrdn: %d\n", flag ? 1 : 0 );
+ }
+ fprintf( fp, "\n" );
+
+ fclose( fp );
+}
+
diff --git a/ldap/servers/slapd/test-plugins/testpreop.c b/ldap/servers/slapd/test-plugins/testpreop.c
new file mode 100644
index 00000000..54a08b53
--- /dev/null
+++ b/ldap/servers/slapd/test-plugins/testpreop.c
@@ -0,0 +1,215 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+/************************************************************
+
+ testpreop.c
+
+ This source file provides examples of pre-operation plug-in
+ functions. The server calls these plug-in functions before
+ executing certain LDAP operations:
+
+ * testpreop_bind (called before an LDAP bind operation)
+ * testpreop_add (called before an LDAP add operation)
+ * testpreop_abandon (called before an LDAP abandon operation)
+
+ testpreop_bind logs information about the LDAP bind operation
+ to the server error log. testpreop_add prepends the name "BOB"
+ to the value of the cn attribute before an entry is added.
+
+ Note that the Directory Server front-end handles bind
+ operations requested by the root DN. The server does not
+ invoke your plug-in function if the client is authenticating
+ as the root DN.
+
+ To test this plug-in function, stop the server, edit the dse.ldif file
+ (in the <server_root>/slapd-<server_id>/config directory)
+ and add the following lines before restarting the server :
+
+ dn: cn=Test Preop,cn=plugins,cn=config
+ objectClass: top
+ objectClass: nsSlapdPlugin
+ objectClass: extensibleObject
+ cn: Test Preop
+ nsslapd-pluginPath: <server_root>/plugins/slapd/slapi/examples/libtest-plugin.so
+ nsslapd-pluginInitfunc: testpreop_init
+ nsslapd-pluginType: preoperation
+ nsslapd-pluginEnabled: on
+ nsslapd-plugin-depends-on-type: database
+ nsslapd-pluginId: test-preop
+
+ ************************************************************/
+
+#include <stdio.h>
+#include <string.h>
+#include "slapi-plugin.h"
+
+Slapi_PluginDesc preoppdesc = { "test-preop", "Netscape", "0.5",
+ "sample pre-operation plugin" };
+
+/* Pre-operation plug-in function */
+int
+testpreop_bind( Slapi_PBlock *pb )
+{
+ char *dn;
+ int method;
+ char *auth;
+
+ /* Get the DN that the client is binding as and the method
+ of authentication used. */
+ if ( slapi_pblock_get( pb, SLAPI_BIND_TARGET, &dn ) != 0 ||
+ slapi_pblock_get( pb, SLAPI_BIND_METHOD, &method ) != 0 ) {
+ slapi_log_error( SLAPI_LOG_PLUGIN,
+ "testpreop_bind", "Could not get parameters\n" );
+ return( -1 );
+ }
+
+ switch( method ) {
+ case LDAP_AUTH_NONE:
+ auth = "No authentication";
+ break;
+ case LDAP_AUTH_SIMPLE:
+ auth = "Simple authentication";
+ break;
+ case LDAP_AUTH_SASL:
+ auth = "SASL authentication";
+ break;
+ default:
+ auth = "Unknown method of authentication";
+ break;
+ }
+
+ /* Log information about the bind operation to the
+ server error log. */
+ slapi_log_error( SLAPI_LOG_PLUGIN, "testpreop_bind",
+ "Preoperation bind function called.\n"
+ "\tTarget DN: %s\n\tAuthentication method: %s\n",
+ dn, auth );
+
+ return( 0 ); /* allow the operation to continue */
+}
+
+/* Pre-operation plug-in function */
+int
+testpreop_add( Slapi_PBlock *pb )
+{
+ Slapi_Entry *e;
+ Slapi_Attr *a;
+ Slapi_Value *v;
+ struct berval **bvals;
+ int i, hint;
+ char *tmp;
+ const char *s;
+
+ /* Get the entry that is about to be added. */
+ if ( slapi_pblock_get( pb, SLAPI_ADD_ENTRY, &e ) != 0 ) {
+ slapi_log_error( SLAPI_LOG_PLUGIN,
+ "testpreop_add", "Could not get entry\n" );
+ return( -1 );
+ }
+
+ /* Prepend the name "BOB" to the value of the cn attribute
+ in the entry. */
+ if ( slapi_entry_attr_find( e, "cn", &a ) == 0 ) {
+ for ( hint = slapi_attr_first_value( a, &v ); hint != -1;
+ hint = slapi_attr_next_value( a, hint, &v )) {
+ s = slapi_value_get_string( v );
+ tmp = (char *) malloc( 5 + strlen( s ));
+ strcpy( tmp, "BOB " );
+ strcat( tmp + 4, s );
+ slapi_value_set_string( v, tmp );
+ free( tmp );
+ }
+ }
+
+ return( 0 ); /* allow the operation to continue */
+}
+
+
+/* Pre-operation plug-in function */
+int
+testpreop_abandon( Slapi_PBlock *pb )
+{
+ int msgid;
+
+ /* Get the LDAP message ID of the abandon target */
+ if ( slapi_pblock_get( pb, SLAPI_ABANDON_MSGID, &msgid ) != 0 ) {
+ slapi_log_error( SLAPI_LOG_PLUGIN,
+ "testpreop_abandon", "Could not get parameters\n" );
+ return( -1 );
+ }
+
+ /* Log information about the abandon operation to the
+ server error log. */
+ slapi_log_error( SLAPI_LOG_PLUGIN, "testpreop_bind",
+ "Preoperation abandon function called.\n"
+ "\tTarget MsgID: %d\n",
+ msgid );
+
+ return( 0 ); /* allow the operation to continue */
+}
+
+
+static void
+get_plugin_config_dn_and_entry( char *msg, Slapi_PBlock *pb )
+{
+ char *dn = NULL;
+ Slapi_Entry *e = NULL;
+ int loglevel = SLAPI_LOG_PLUGIN;
+
+ if ( slapi_pblock_get( pb, SLAPI_TARGET_DN, &dn ) != 0 || dn == NULL ) {
+ slapi_log_error( loglevel, msg, "failed to get plugin config DN\n" );
+ } else {
+ slapi_log_error( loglevel, msg, "this plugin's config DN is \"%s\"\n",
+ dn );
+ }
+
+ if ( slapi_pblock_get( pb, SLAPI_ADD_ENTRY, &e ) != 0 || e == NULL ) {
+ slapi_log_error( loglevel, msg, "failed to get plugin config entry\n" );
+ } else {
+ char *ldif;
+
+ ldif = slapi_entry2str_with_options( e, NULL, 0 );
+ slapi_log_error( loglevel, msg,
+ "this plugin's config entry is \"\n%s\"\n", ldif );
+ slapi_ch_free_string( &ldif );
+ }
+}
+
+static int
+testpreop_start( Slapi_PBlock *pb )
+{
+ get_plugin_config_dn_and_entry( "testpreop_start", pb );
+ return( 0 );
+}
+
+/* Initialization function */
+#ifdef _WIN32
+__declspec(dllexport)
+#endif
+int
+testpreop_init( Slapi_PBlock *pb )
+{
+ /* Register the two pre-operation plug-in functions,
+ and specify the server plug-in version. */
+ if ( slapi_pblock_set( pb, SLAPI_PLUGIN_VERSION,
+ SLAPI_PLUGIN_VERSION_01 ) != 0 ||
+ slapi_pblock_set( pb, SLAPI_PLUGIN_DESCRIPTION,
+ (void *)&preoppdesc ) != 0 ||
+ slapi_pblock_set( pb, SLAPI_PLUGIN_START_FN,
+ (void *) testpreop_start ) != 0 ||
+ slapi_pblock_set( pb, SLAPI_PLUGIN_PRE_BIND_FN,
+ (void *) testpreop_bind ) != 0 ||
+ slapi_pblock_set( pb, SLAPI_PLUGIN_PRE_ADD_FN,
+ (void *) testpreop_add ) != 0 ||
+ slapi_pblock_set( pb, SLAPI_PLUGIN_PRE_ABANDON_FN,
+ (void *) testpreop_abandon ) != 0 ) {
+ slapi_log_error( SLAPI_LOG_PLUGIN, "testpreop_init",
+ "Failed to set version and function\n" );
+ return( -1 );
+ }
+
+ return( 0 );
+}
diff --git a/ldap/servers/slapd/test-plugins/testsaslbind.c b/ldap/servers/slapd/test-plugins/testsaslbind.c
new file mode 100644
index 00000000..3d265f4d
--- /dev/null
+++ b/ldap/servers/slapd/test-plugins/testsaslbind.c
@@ -0,0 +1,145 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+
+/************************************************************
+
+ testsaslbind.c
+
+ This source file provides an example of a pre-operation plug-in
+ function for SASL authentication with LDAP bind operations.
+ The function demonstrates how to send credentials back to the
+ client in cases where mutual authentication is required.
+
+ This plugin responds to SASL bind requests with a mechanism
+ name of "babsmechanism". Simple binds and other SASL mechanisms
+ should not be affected by the presence of this plugin.
+
+ Binds with our mechanism always succeed (which is not very secure!)
+
+ Note that the Directory Server front-end handles bind
+ operations requested by the root DN. The server does not
+ invoke your plug-in function if the client is authenticating
+ as the root DN.
+
+ To test this plug-in function, stop the server, edit the dse.ldif file
+ (in the <server_root>/slapd-<server_id>/config directory)
+ and add the following lines before restarting the server :
+
+dn: cn=test-saslbind,cn=plugins,cn=config
+objectclass: top
+objectclass: nsSlapdPlugin
+objectclass: extensibleObject
+cn: test-saslbind
+nsslapd-pluginpath: /usr/netscape/servers/plugins/slapd/slapi/examples/libtest-plugin.so
+nsslapd-plugininitfunc: testsasl_init
+nsslapd-plugintype: preoperation
+nsslapd-pluginenabled: on
+nsslapd-pluginid: test-saslbind
+nsslapd-pluginversion: 5.0
+nsslapd-pluginvendor: Netscape Communications Corp.
+nsslapd-plugindescription: sample SASL bind pre-operation plugin
+
+
+ ************************************************************/
+#include <stdio.h>
+#include <string.h>
+#include "slapi-plugin.h"
+
+Slapi_PluginDesc saslpdesc = { "test-saslbind", "Netscape Communications Corp.", "7.0",
+ "sample SASL bind pre-operation plugin" };
+
+
+#define TEST_SASL_MECHANISM "babsmechanism"
+#define TEST_SASL_AUTHMETHOD SLAPD_AUTH_SASL TEST_SASL_MECHANISM
+
+/* Pre-operation plug-in function */
+int
+testsasl_bind( Slapi_PBlock *pb )
+{
+ char *target;
+ int method;
+ char *mechanism;
+ struct berval *credentials;
+ struct berval svrcreds;
+
+ /* Log a message to the server error log. */
+ slapi_log_error( SLAPI_LOG_PLUGIN, "testsasl_bind",
+ "Pre-operation bind function called.\n" );
+
+ /* Gets parameters available when processing an LDAP bind
+ operation. */
+ if ( slapi_pblock_get( pb, SLAPI_BIND_TARGET, &target ) != 0 ||
+ slapi_pblock_get( pb, SLAPI_BIND_METHOD, &method ) != 0 ||
+ slapi_pblock_get( pb, SLAPI_BIND_CREDENTIALS, &credentials ) != 0 ||
+ slapi_pblock_get( pb, SLAPI_BIND_SASLMECHANISM, &mechanism )
+ != 0 ) {
+ slapi_log_error( SLAPI_LOG_FATAL, "testsasl_bind",
+ "Could not get parameters for bind operation\n" );
+ return( 0 ); /* let the server try other mechanisms */
+ }
+
+ /* Check to see if the mechanism being used is ours. */
+ if ( mechanism == NULL
+ || strcmp( mechanism, TEST_SASL_MECHANISM ) != 0 ) {
+ return( 0 ); /* let the server try other mechanisms */
+ }
+
+ /*
+ * Set the DN and authentication method for the connection.
+ * Binds with our mechanism always succeed (which is not very secure!)
+ */
+ if ( slapi_pblock_set( pb, SLAPI_CONN_DN, slapi_ch_strdup( target ) )
+ != 0 || slapi_pblock_set( pb, SLAPI_CONN_AUTHMETHOD,
+ TEST_SASL_AUTHMETHOD ) != 0 ) {
+ slapi_log_error( SLAPI_LOG_FATAL, "testsasl_bind",
+ "Failed to set DN and method for connection\n" );
+ slapi_send_ldap_result( pb, LDAP_OPERATIONS_ERROR, NULL,
+ NULL, 0, NULL );
+ return( 1 ); /* done -- sent result to client */
+ }
+
+ /* Set the credentials that should be returned to the client. */
+ svrcreds.bv_val = "my credentials";
+ svrcreds.bv_len = sizeof("my credentials") - 1;
+ if ( slapi_pblock_set( pb, SLAPI_BIND_RET_SASLCREDS, &svrcreds )
+ != 0 ) {
+ slapi_log_error( SLAPI_LOG_PLUGIN, "testsasl_bind",
+ "Could not set credentials to return to client\n" );
+ slapi_pblock_set( pb, SLAPI_CONN_DN, NULL );
+ slapi_pblock_set( pb, SLAPI_CONN_AUTHMETHOD, SLAPD_AUTH_NONE );
+ return( 0 ); /* let the server try other mechanisms */
+ }
+
+ /* Send the credentials back to the client. */
+ slapi_log_error( SLAPI_LOG_PLUGIN, "testsasl_bind",
+ "Authenticated: %s\n", target );
+ slapi_send_ldap_result( pb, LDAP_SUCCESS, NULL, NULL, 0, NULL );
+
+ return( 1 ); /* done -- sent result to client */
+}
+
+/* Initialization function */
+#ifdef _WIN32
+__declspec(dllexport)
+#endif
+int
+testsasl_init( Slapi_PBlock *pb )
+{
+ /* Register the pre-operation bind function and specify
+ the server plug-in version. */
+ if ( slapi_pblock_set( pb, SLAPI_PLUGIN_VERSION, SLAPI_PLUGIN_VERSION_01 ) != 0 ||
+ slapi_pblock_set( pb, SLAPI_PLUGIN_DESCRIPTION, (void *)&saslpdesc ) != 0 ||
+ slapi_pblock_set( pb, SLAPI_PLUGIN_PRE_BIND_FN, (void *) testsasl_bind ) != 0 ) {
+ slapi_log_error( SLAPI_LOG_PLUGIN, "testsasl_init",
+ "Failed to set version and function\n" );
+ return( -1 );
+ }
+
+ /* Register the SASL mechanism. */
+ slapi_register_supported_saslmechanism( TEST_SASL_MECHANISM );
+ return( 0 );
+}
+
diff --git a/ldap/servers/slapd/time.c b/ldap/servers/slapd/time.c
new file mode 100644
index 00000000..ebb279e8
--- /dev/null
+++ b/ldap/servers/slapd/time.c
@@ -0,0 +1,367 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+/* time.c - various time routines */
+
+#include <stdio.h>
+#include <sys/types.h>
+#ifndef _WIN32
+#ifdef AIX
+#include <time.h>
+#else
+#include <sys/time.h>
+#endif
+#endif /* _WIN32 */
+
+#include "slap.h"
+#include "fe.h"
+
+LDAP_API(unsigned long) strntoul( char *from, size_t len, int base );
+#define mktime_r(from) mktime (from) /* possible bug: is this thread-safe? */
+
+static time_t currenttime;
+static int currenttime_set = 0;
+/* XXX currenttime and currenttime_set are used by multiple threads,
+ * concurrently (one thread sets them, many threads read them),
+ * WITHOUT SYNCHRONIZATION. If assignment to currenttime especially
+ * is not atomic, current_time() will return bogus values, and
+ * bogus behavior may ensue. We think this isn't a problem, because
+ * currenttime is a static variable, and defined first in this module;
+ * consequently it's aligned, and doesn't cross cache lines or
+ * otherwise run afoul of multiprocessor weirdness that might make
+ * assignment to it non-atomic.
+ */
+
+#ifndef HAVE_TIME_R
+PRLock *time_func_mutex;
+
+int gmtime_r(
+ const time_t *timer,
+ struct tm *result
+)
+{
+ if ( result == NULL ) {
+ return -1;
+ }
+ PR_Lock( time_func_mutex );
+ memcpy( (void *) result, (const void *) gmtime( timer ),
+ sizeof( struct tm ));
+ PR_Unlock( time_func_mutex );
+ return 0;
+}
+
+int localtime_r(
+ const time_t *timer,
+ struct tm *result
+)
+{
+ if ( result == NULL ) {
+ return -1;
+ }
+ PR_Lock( time_func_mutex );
+ memcpy( (void *) result, (const void *) localtime( timer ),
+ sizeof( struct tm ));
+ PR_Unlock( time_func_mutex );
+ return 0;
+}
+
+
+int ctime_r(
+ const time_t *timer,
+ char *buffer,
+ int buflen
+)
+{
+ if (( buffer == NULL ) || ( buflen < 26)) {
+ return -1;
+ }
+ PR_Lock( time_func_mutex );
+ memset( buffer, 0, buflen );
+ memcpy( buffer, ctime( timer ), 26 );
+ PR_Unlock( time_func_mutex );
+ return 0;
+}
+#endif /* HAVE_TIME_R */
+
+
+char *
+get_timestring(time_t *t)
+{
+ char *timebuf;
+
+#if defined( _WIN32 )
+ timebuf = ctime( t );
+#else
+ if ( (timebuf = slapi_ch_malloc(32)) == NULL )
+ return("No memory for get_timestring");
+ CTIME(t, timebuf, 32);
+#endif
+ timebuf[strlen(timebuf) - 1] = '\0'; /* strip out return */
+ return(timebuf);
+}
+
+void
+free_timestring(char *timestr)
+{
+#if defined( _WIN32 )
+ return;
+#else
+ if ( timestr != NULL )
+ slapi_ch_free((void**)&timestr);
+#endif
+}
+
+/*
+ * poll_current_time() is called at least once every second by the time
+ * thread (see daemon.c:time_thread()). current_time() returns the time
+ * that poll_current_time() last stored. This approach is used to avoid
+ * calling the time() system call many times per second.
+ *
+ * Note: during server startup, poll_current_time() is not called at all so
+ * current_time() just calls through to time() until poll_current_time() starts
+ * to be called.
+ */
+time_t
+poll_current_time()
+{
+ if ( !currenttime_set ) {
+ currenttime_set = 1;
+ }
+
+ time( &currenttime );
+ return( currenttime );
+}
+
+time_t
+current_time( void )
+{
+ if ( currenttime_set ) {
+ return( currenttime );
+ } else {
+ return( time( (time_t *)0 ));
+ }
+}
+
+time_t
+time_plus_sec (time_t l, long r)
+ /* return the point in time 'r' seconds after 'l'. */
+{
+ /* On many (but not all) platforms this is simply l + r;
+ perhaps it would be better to implement it that way. */
+ struct tm t;
+ if (r == 0) return l; /* performance optimization */
+#ifdef _WIN32
+ {
+ struct tm *pt = localtime( &l );
+ memcpy(&t, pt, sizeof(struct tm) );
+ }
+#else
+ localtime_r (&l, &t);
+#endif
+ /* Conceptually, we want to do: t.tm_sec += r;
+ but to avoid overflowing fields: */
+ r += t.tm_sec; t.tm_sec = r % 60; r /= 60;
+ r += t.tm_min; t.tm_min = r % 60; r /= 60;
+ r += t.tm_hour; t.tm_hour = r % 24; r /= 24;
+ t.tm_mday += r; /* may be > 31; mktime_r() must handle this */
+
+ /* These constants are chosen to work when the maximum
+ field values are 127 (the worst case) or more.
+ Perhaps this is excessively conservative. */
+ return mktime_r (&t);
+}
+
+char*
+format_localTime (time_t from)
+ /* return a newly-allocated string containing the given time, expressed
+ in the syntax of a generalizedTime, except without the time zone. */
+{
+ char* into;
+ struct tm t;
+#ifdef _WIN32
+ {
+ struct tm *pt = localtime( &from );
+ memcpy(&t, pt, sizeof(struct tm) );
+ }
+#else
+ localtime_r (&from, &t);
+#endif
+ into = slapi_ch_malloc (15);
+ sprintf (into, "%.4li%.2i%.2i%.2i%.2i%.2i",
+ 1900L + t.tm_year, t.tm_mon + 1, t.tm_mday, t.tm_hour, t.tm_min, t.
+tm_sec);
+ return into;
+}
+
+time_t
+read_localTime (struct berval* from)
+/* the inverse of write_localTime */
+{
+ return (read_genTime(from));
+}
+
+
+time_t
+parse_localTime (char* from)
+ /* the inverse of format_localTime */
+{
+ return (parse_genTime(from));
+}
+
+void
+write_localTime (time_t from, struct berval* into)
+ /* like format_localTime, except it returns a berval */
+{
+ into->bv_val = format_localTime (from);
+ into->bv_len = 14; /* strlen (into->bv_val) */
+}
+
+/*
+ * Function: strntoul
+ *
+ * Returns: the value of "from", converted to an unsigned long integer.
+ *
+ * Arguments: from - a pointer to the string to be converted.
+ * len - the maximum number of characters to process.
+ * base - the base to use for conversion. See strtoul(3).
+ *
+ * Returns: See strtoul(3).
+ */
+LDAP_API(unsigned long) strntoul( char *from, size_t len, int base )
+{
+ unsigned long result;
+ char c = from[ len ];
+
+ from[ len ] = '\0';
+ result = strtoul( from, NULL, base );
+ from[ len ] = c;
+ return result;
+}
+
+/*
+ * New time functions.
+ * The format and write functions, express time as
+ * generalizedTime (in the Z form).
+ * The parse and read functions, can read either
+ * localTime or generalizedTime.
+ */
+
+char *
+format_genTime (time_t from)
+ /* return a newly-allocated string containing the given time, expressed
+ in the syntax of a generalizedTime. */
+{
+ char* into;
+ struct tm t;
+#ifdef _WIN32
+ {
+ struct tm *pt = gmtime( &from );
+ memcpy(&t, pt, sizeof(struct tm) );
+ }
+#else
+ gmtime_r (&from, &t);
+#endif
+ into = slapi_ch_malloc (20);
+ strftime(into, 20, "%Y%m%d%H%M%SZ", &t);
+ return into;
+}
+
+void
+write_genTime (time_t from, struct berval* into)
+ /* like format_localTime, except it returns a berval */
+{
+ into->bv_val = format_genTime (from);
+ into->bv_len = strlen (into->bv_val);
+}
+
+time_t
+read_genTime(struct berval*from)
+{
+ struct tm t;
+ time_t retTime;
+ time_t diffsec = 0;
+ int i, gflag = 0, havesec = 0;
+
+ memset (&t, 0, sizeof(t));
+ t.tm_isdst = -1;
+ t.tm_year = strntoul (from->bv_val , 4, 10) - 1900L;
+ t.tm_mon = strntoul (from->bv_val + 4, 2, 10) - 1;
+ t.tm_mday = strntoul (from->bv_val + 6, 2, 10);
+ t.tm_hour = strntoul (from->bv_val + 8, 2, 10);
+ t.tm_min = strntoul (from->bv_val + 10, 2, 10);
+ i =12;
+ /*
+ * the string can end with Z or -xxxx or +xxxx
+ * or before to have the Z/+/- we may have two digits for the seconds.
+ * If there's no Z/+/-, it means it's been expressed as local time
+ * (not standard).
+ */
+ while (from->bv_val[i]) {
+ switch (from->bv_val[i]) {
+ case 'Z':
+ case 'z':
+ gflag = 1;
+ ++i;
+ break;
+ case '+': /* Offset from GMT is on 4 digits */
+ i++;
+ diffsec -= strntoul (from->bv_val + i, 4, 10);
+ gflag = 1;
+ i += 4;
+ break;
+ case '-': /* Offset from GMT is on 4 digits */
+ i++;
+ diffsec += strntoul (from->bv_val + i, 4, 10);
+ gflag = 1;
+ i += 4;
+ break;
+ default:
+ if (havesec){
+ /* Ignore milliseconds */
+ i++;
+ } else {
+ t.tm_sec = strntoul (from->bv_val + i, 2, 10);
+ havesec = 1;
+ i += 2;
+ }
+ } /* end switch */
+ }
+ if (gflag){
+ PRTime pt;
+ PRExplodedTime expt = {0};
+ unsigned long year = strntoul (from->bv_val , 4, 10);
+
+ expt.tm_year = (PRInt16)year;
+ expt.tm_month = t.tm_mon;
+ expt.tm_mday = t.tm_mday;
+ expt.tm_hour = t.tm_hour;
+ expt.tm_min = t.tm_min;
+ expt.tm_sec = t.tm_sec;
+ /* This is a GMT time */
+ expt.tm_params.tp_gmt_offset = 0;
+ expt.tm_params.tp_dst_offset = 0;
+ /* PRTime is expressed in microseconds */
+ pt = PR_ImplodeTime(&expt) / 1000000L;
+ retTime = (time_t)pt;
+ return (retTime + diffsec);
+ } else {
+ return mktime_r (&t);
+ }
+}
+
+time_t
+parse_genTime (char* from)
+/* the inverse of format_genTime
+ * Because read_localTime has been rewriten to take into
+ * account generalizedTime, parse_time is similar to parse_localTime.
+ * The new call is
+ */
+{
+ struct berval tbv;
+ tbv.bv_val = from;
+ tbv.bv_len = strlen (from);
+
+ return read_genTime(&tbv);
+}
diff --git a/ldap/servers/slapd/tools/Makefile b/ldap/servers/slapd/tools/Makefile
new file mode 100644
index 00000000..abd9fda1
--- /dev/null
+++ b/ldap/servers/slapd/tools/Makefile
@@ -0,0 +1,168 @@
+#
+# BEGIN COPYRIGHT BLOCK
+# Copyright 2001 Sun Microsystems, Inc.
+# Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+# All rights reserved.
+# END COPYRIGHT BLOCK
+#
+#
+# gnu makefile for LDAP Server tools.
+#
+
+LDAP_SRC = ../../..
+MCOM_ROOT = ../../../../..
+
+NOSTDCLEAN=true # don't let nsconfig.mk define target clean
+NOSTDSTRIP=true # don't let nsconfig.mk define target strip
+NSPR20=true # probably should be defined somewhere else (not sure where)
+
+OBJDEST = $(OBJDIR)/servers/tools/obj
+BINDIR = $(LDAP_SERVER_RELDIR)
+
+SLAPD_OBJDIR = $(LDAP_OBJDIR)
+
+include $(MCOM_ROOT)/ldapserver/nsdefs.mk
+include $(MCOM_ROOT)/ldapserver/nsconfig.mk
+include $(LDAP_SRC)/nsldap.mk
+ifndef LDAP_USE_OLD_DB
+include $(MCOM_ROOT)/ldapserver/ns_usedb.mk
+INCLUDES+=-I$(DB_INCLUDE)
+else
+CFLAGS+=-DLDAP_USE_DB185
+endif
+
+SLAPDHDIR = ../
+
+ifeq ($(ARCH), OSF1)
+PLATFORM_SPECIFIC_EXTRA_LIBRARY = -lcxx
+else # OSF1
+# oems might need to edit this for their platform
+PLATFORM_SPECIFIC_EXTRA_LIBRARY =
+endif # OSF1
+
+INCLUDES += $(SSLINCLUDE)
+DEFS += $(SSL)
+
+OBJS1 += $(OBJDEST)/pwenc.o
+
+ifeq ($(USE_64), 1)
+ifeq ($(ARCH), HPUX)
+LDFLAGS += -lpthread +DA2.0W +DS2.0 +Z
+endif
+ifeq ($(ARCH), SOLARIS)
+LDFLAGS += -xarch=v9
+endif
+endif
+
+
+INCLUDES += -I$(SLAPDHDIR) -I$(LDAP_ADMINCDIR)
+INCLUDES += -I$(ACLINC)
+INCLUDES += -I ../../plugins/rever
+LDFLAGS += $(EXLDFLAGS) $(SSLLIBFLAG)
+
+ifeq ($(ARCH), WINNT)
+SUBSYSTEM=console
+endif
+
+DEPLIBS=
+
+EXTRA_LIBS_DEP = $(LDAPSDK_DEP) \
+ $(LDAP_LIBLDIF_DEP) \
+ $(LDAP_SLIBLCACHE_DEP) $(DB_LIB_DEP) $(LIBSLAPD_DEP) \
+ $(LDAP_COMMON_LIBS_DEP)
+
+EXTRA_LIBS = $(LDAPLINK) \
+ $(LDAP_SLIBLCACHE) $(DB_LIB) \
+ $(PLATFORM_SPECIFIC_EXTRA_LIBRARY) $(LIBSLAPD) $(LDAP_LIBLITEKEY) \
+ $(ALIBS) \
+ $(SECURITYLINK) $(DBMLINK) \
+ $(THREADSLIB) $(LDAP_COMMON_LIBS) $(NSPRLINK) $(SVRCORELINK)
+
+ifeq ($(ARCH), Linux)
+EXTRA_LIBS += -lcrypt
+endif
+
+
+KEYUPG_LIBS_DEP=
+KEYUPG_LIBS=$(LDAP_LIBLITEKEY)
+
+ifeq ($(ARCH), WINNT)
+KEYUPG_LIBS_DEP=$(LDAP_LIBUTIL_DEP)
+KEYUPG_LIBS += $(LDAP_LIBUTIL)
+endif
+
+ifdef HEAPAGENT
+CFLAGS+=-DPURIFYING
+LDAP_DONT_USE_SMARTHEAP=1
+endif
+
+# It looks like all of the latest versions of Unix that we ship on
+# have a good enough heap implementations that they don't need
+# SmartHeap. We still need it on NT.
+ifneq ($(ARCH), WINNT)
+LDAP_DONT_USE_SMARTHEAP=1
+endif
+
+# Don't use smartheap for debug builds on NT
+ifeq ($(ARCH), WINNT)
+ifeq ($(DEBUG), full)
+LDAP_DONT_USE_SMARTHEAP=1
+endif
+endif
+
+ifndef LDAP_DONT_USE_SMARTHEAP
+include $(MCOM_ROOT)/ldapserver/ns_usesh.mk
+_smartheap_depend = $(SH_LIB_DEP)
+else
+CFLAGS+=-DLDAP_DONT_USE_SMARTHEAP
+endif
+
+
+TOOL_OBJS = ldif.o keyupg.o pwenc.o mmldif.o migratecred.o eggencode.o
+ALL_OBJS = $(addprefix $(OBJDEST)/, $(TOOL_OBJS))
+
+LDIF = $(addsuffix $(EXE_SUFFIX), \
+ $(addprefix $(BINDIR)/, ldif))
+PWDHASH = $(addsuffix $(EXE_SUFFIX), \
+ $(addprefix $(BINDIR)/, pwdhash))
+MIGRATECRED = $(addsuffix $(EXE_SUFFIX), \
+ $(addprefix $(BINDIR)/, migratecred))
+KEYUPG = $(addsuffix $(EXE_SUFFIX), \
+ $(addprefix $(BINDIR)/, keyupg))
+MMLDIF = $(addsuffix $(EXE_SUFFIX), \
+ $(addprefix $(BINDIR)/, mmldif))
+EGGENCODE = $(addsuffix $(EXE_SUFFIX), \
+ $(addprefix $(BINDIR)/, eggencode))
+
+BINS= $(LDIF) $(PWDHASH) $(KEYUPG) $(MMLDIF) $(MIGRATECRED)
+EXTRABINS= $(EGGENCODE)
+
+all: $(OBJDEST) $(BINDIR) $(LDAP_ADMIN_BIN_RELDIR) $(BINS)
+
+extras: $(OBJDEST) $(BINDIR) $(EGGENCODE)
+
+$(LDIF): $(OBJDEST)/ldif.o $(LDAP_LIBLDIF_DEP)
+ $(LINK_EXE) $< $(LDAP_LIBLDIF)
+
+$(PWDHASH): $(OBJS1) $(EXTRA_LIBS_DEP)
+ $(LINK_EXE) $(OBJS1) $(EXTRA_LIBS)
+
+$(MIGRATECRED): $(OBJDEST)/migratecred.o $(EXTRA_LIBS_DEP)
+ $(LINK_EXE) $(OBJDEST)/migratecred.o $(EXTRA_LIBS)
+
+$(KEYUPG): $(OBJDEST)/keyupg.o $(KEYUPG_LIBS_DEP)
+ $(LINK_EXE_NOLIBSOBJS) $< $(KEYUPG_LIBS)
+
+$(MMLDIF): $(OBJDEST)/mmldif.o $(EXTRA_LIBS_DEP)
+ $(LINK_EXE_NOLIBSOBJS) $(OBJDEST)/mmldif.o $(EXTRA_LIBS)
+
+$(EGGENCODE): $(OBJDEST)/eggencode.o
+ $(LINK_EXE_NOLIBSOBJS) $(OBJDEST)/eggencode.o
+
+$(OBJDEST):
+ $(MKDIR) $(OBJDEST)
+
+clean:
+ -$(RM) $(ALL_OBJS)
+ -$(RM) $(BINS) $(EXTRABINS)
+
diff --git a/ldap/servers/slapd/tools/eggencode.c b/ldap/servers/slapd/tools/eggencode.c
new file mode 100644
index 00000000..92b098ca
--- /dev/null
+++ b/ldap/servers/slapd/tools/eggencode.c
@@ -0,0 +1,57 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+
+/*
+ * Easter egg encoder. See ../fedse.c:egg_decode() for the mirror image.
+ */
+#include <stdio.h>
+
+static unsigned char egg_nibble2char( int nibble );
+
+int
+main( int argc, char *argv[] )
+{
+ int c, colcount;
+ char outc;
+
+ if ( argc > 1 ) {
+ fprintf( stderr, "usage: %s < in > out\n", argv[0] );
+ return 2;
+ }
+
+ colcount = 0;
+ while (( c = getchar()) != EOF ) {
+ if ( 0 == colcount ) {
+ putchar( '"' );
+ }
+ c ^= 122;
+ outc = egg_nibble2char( (c & 0xF0) >> 4 );
+ putchar( outc );
+ ++colcount;
+ outc = egg_nibble2char( c & 0x0F );
+ putchar( outc );
+ ++colcount;
+ if ( colcount > 72 ) {
+ colcount = 0;
+ putchar( '"' );
+ putchar( '\n' );
+ }
+ }
+
+ if ( colcount > 0 ) {
+ putchar( '"' );
+ putchar( '\n' );
+ }
+
+ return 0;
+}
+
+
+static unsigned char
+egg_nibble2char( int nibble )
+{
+ return ( nibble < 10 ) ? nibble + '0' : ( nibble - 10 ) + 'A';
+}
diff --git a/ldap/servers/slapd/tools/keyupg.c b/ldap/servers/slapd/tools/keyupg.c
new file mode 100644
index 00000000..f1d9adc1
--- /dev/null
+++ b/ldap/servers/slapd/tools/keyupg.c
@@ -0,0 +1,85 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+/*
+ *
+ * keyupg.c
+ *
+ * Upgrade the Key from Lite to normal ( only one way )
+ *
+ */
+
+
+#include <stdio.h>
+#include <string.h>
+#ifdef hpux
+#include <strings.h>
+#endif /* hpux */
+#ifdef LINUX
+#include <unistd.h> /* needed for getopt */
+#endif
+#if defined( _WIN32 )
+#include <windows.h>
+#include "proto-ntutil.h"
+#endif
+#include <stdlib.h>
+#include "litekey.h"
+
+
+#define BUFSIZE 800
+#define FILE_PATHSEP '/'
+
+int
+main (int argc, char **argv )
+{
+
+
+ char *keyfile = NULL;
+ int i, ikey, nkey;
+ FILE *fp = NULL;
+ int debug =0;
+
+
+ while ( (i = getopt( argc, argv, "k:f:dh" )) != EOF ) {
+ switch (i){
+ case 'f':
+ keyfile = strdup( optarg );
+ break;
+ case 'k':
+ ikey = atoi ( optarg );
+ break;
+ case 'd':
+ debug = 1;
+ break;
+ }
+ }
+
+ if ( (NULL == keyfile ) || (!ikey)) {
+ fprintf (stderr, "usage:%s -k key -f key_file_path\n", argv[0]);
+ exit(1);
+
+ }
+
+ if (debug) printf ( "Key is :%d and file is in :%s\n", ikey, keyfile);
+
+ if ( ! is_key_validNormalKey ( ikey )) {
+ printf ( "Sorry. The input key is invalid\n" );
+ exit (1);
+ }
+
+
+ nkey = generate_directory_key ( DS_NORMAL_TYPE );
+
+ if ( (fp = fopen ( keyfile, "r+b")) == NULL ) {
+ printf ("KEYUPG Error: Could not open the the key file:%s\n", keyfile );
+ exit ( 1 );
+ }
+ fprintf (fp, "key: %d\n", nkey );
+ fclose ( fp );
+
+ printf ("Success: Your Directory Servers have been upgraded to the full version.\n");
+
+ return 0;
+}
diff --git a/ldap/servers/slapd/tools/ldif.c b/ldap/servers/slapd/tools/ldif.c
new file mode 100644
index 00000000..aa90a178
--- /dev/null
+++ b/ldap/servers/slapd/tools/ldif.c
@@ -0,0 +1,128 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+#include <stdio.h>
+#include <string.h>
+#include <memory.h>
+#include <sys/types.h>
+#if defined( _WINDOWS ) || defined( _WIN32 )
+#include <windows.h>
+#include <io.h>
+#include <fcntl.h>
+#else
+#include <unistd.h> /* for read() */
+#include <sys/socket.h>
+#endif
+#include "ldap.h"
+#include "ldif.h"
+
+int ldap_syslog;
+int ldap_syslog_level;
+
+
+static void
+display_usage( char *name )
+{
+ fprintf( stderr, "usage: %s [-b] <attrtype>\n", name );
+}
+
+int main( int argc, char **argv )
+{
+ char *type, *out;
+ int binary = 0;
+
+ if (argc < 2 || argc > 3 ) {
+ display_usage( argv[0] );
+ return( 1 );
+ }
+ if ( argc == 3 ) {
+ if ( strcmp( argv[1], "-b" ) != 0 ) {
+ display_usage( argv[0] );
+ return( 1 );
+ } else {
+ binary = 1;
+ type = argv[2];
+ }
+ } else {
+ if ( strcmp( argv[1], "-b" ) == 0 ) {
+ display_usage( argv[0] );
+ return( 1 );
+ }
+ type = argv[1];
+ }
+
+ /* if the -b flag was used, read single binary value from stdin */
+ if ( binary ) {
+ char buf[BUFSIZ];
+ char *val;
+ int nread, max, cur;
+#if defined( _WINDOWS ) || defined( _WIN32 )
+ _setmode( _fileno( stdin ), _O_BINARY );
+#endif
+
+ if (( val = (char *) malloc( BUFSIZ )) == NULL ) {
+ perror( "malloc" );
+ return( 1 );
+ }
+ max = BUFSIZ;
+ cur = 0;
+ while ( (nread = read( 0, buf, BUFSIZ )) != 0 ) {
+ if ( nread + cur > max ) {
+ max += BUFSIZ;
+ if (( val = (char *) realloc( val, max )) ==
+ NULL ) {
+ perror( "realloc" );
+ return( 1 );
+ }
+ }
+ memcpy( val + cur, buf, nread );
+ cur += nread;
+ }
+
+ if (( out = ldif_type_and_value( type, val, cur )) == NULL ) {
+ perror( "ldif_type_and_value" );
+ return( 1 );
+ }
+
+ fputs( out, stdout );
+ free( out );
+ free( val );
+ return( 0 );
+ } else {
+ /* not binary: one value per line... */
+ char *buf;
+ int curlen, maxlen = BUFSIZ;
+
+ if( (buf = malloc(BUFSIZ)) == NULL ) {
+ perror( "malloc" );
+ return( 1 );
+ }
+ while ( buf = fgets(buf, maxlen, stdin) ) {
+ /* if buffer was filled, expand and keep reading unless last char
+ is linefeed, in which case it is OK for buffer to be full */
+ while( ((curlen = strlen(buf)) == (maxlen - 1)) && buf[curlen-1] != '\n' ) {
+ maxlen *= 2;
+ if( (buf = (char *)realloc(buf, maxlen)) == NULL ) {
+ perror( "realloc" );
+ return( 1 );
+ }
+ fgets(buf+curlen, maxlen/2 + 1, stdin);
+ }
+ /* we have a full line, chop potential newline and turn into ldif */
+ if( buf[curlen-1] == '\n' )
+ buf[curlen-1]='\0';
+ if (( out = ldif_type_and_value( type, buf, strlen( buf ) ))
+ == NULL ) {
+ perror( "ldif_type_and_value" );
+ return( 1 );
+ }
+ fputs( out, stdout );
+ free( out );
+
+ }
+ free( buf );
+ }
+ return( 0 );
+}
diff --git a/ldap/servers/slapd/tools/migratecred.c b/ldap/servers/slapd/tools/migratecred.c
new file mode 100644
index 00000000..9d0d2c66
--- /dev/null
+++ b/ldap/servers/slapd/tools/migratecred.c
@@ -0,0 +1,154 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+
+#include <stdio.h>
+#include <string.h>
+#include <limits.h>
+#include <stdarg.h>
+#include <stdlib.h>
+
+#ifndef _WIN32
+#include <sys/param.h> /* MAXPATHLEN */
+#endif
+
+#include "../plugins/rever/rever.h"
+#include "getopt_ext.h"
+
+static void usage(char *name)
+{
+ fprintf(stderr, "usage: %s -o 5.0InstancePath -n 5.1InstancePath -c 5.0Credential\n", name);
+ exit(1);
+}
+
+#ifdef _WIN32
+/* converts '\' chars to '/' */
+static void dostounixpath(char *szText)
+{
+ if(szText)
+ {
+ while(*szText)
+ {
+ if( *szText == '\\' )
+ *szText = '/';
+ szText++;
+ }
+ }
+}
+#endif
+
+/* Script used during 5.0 to 5.1 migration: replication and
+ chaining backend credentials must be converted.
+
+ Assumption: the built-in des-plugin.so lib has been used
+ in 5.0 and is used in 5.1
+
+ Usage: migrateCred
+ -o <5.0 instance path>
+ -n <5.1 instance path>
+ -c <5.0 credential, with prefix>
+
+ Return 5.1 credential with prefix
+*/
+
+int
+main( int argc, char **argv)
+{
+ char *cmd = argv[0];
+ char *oldpath = NULL;
+ char *newpath = NULL;
+ char *prefixCred = NULL;
+ char *cred = NULL;
+
+ char *newcred = NULL;
+ migrate_fn_type fct = NULL;
+ char libpath[MAXPATHLEN];
+ char *shared_lib;
+
+ char *opts = "o:n:c:";
+ int i;
+
+ while (( i = getopt( argc, argv, opts )) != EOF )
+ {
+ switch (i)
+ {
+ case 'o':
+ oldpath = strdup(optarg);
+#ifdef _WIN32
+ dostounixpath(oldpath);
+#endif /* _WIN32 */
+
+ break;
+ case 'n':
+ newpath = strdup(optarg);
+#ifdef _WIN32
+ dostounixpath(newpath);
+#endif /* _WIN32 */
+ break;
+ case 'c':
+ {
+ char *end = NULL;
+ int namelen;
+
+ /* cred has the prefix, remove it before decoding */
+ prefixCred = strdup(optarg);
+
+ if ((*prefixCred == PWD_HASH_PREFIX_START) &&
+ ((end = strchr(prefixCred, PWD_HASH_PREFIX_END)) != NULL) &&
+ ((namelen = end - prefixCred - 1 ) <= (3*PWD_MAX_NAME_LEN)) )
+ {
+ cred = prefixCred + namelen + 2;
+ }
+ else
+ {
+ fprintf(stderr, "Invalid -c argument: %s (wrong prefix?).\n", prefixCred);
+ }
+ }
+ break;
+ default:
+ usage(cmd);
+ }
+ }
+
+ if ( !oldpath || !newpath || !cred )
+ {
+ usage(cmd);
+ }
+
+
+#if defined( XP_WIN32 )
+ shared_lib = ".dll";
+#else
+#ifdef HPUX
+ shared_lib = ".sl";
+#else
+#ifdef AIX
+#if OSVERSION >= 4200
+ shared_lib = ".so";
+#else
+ shared_lib = "_shr.a";
+#endif
+#else
+ shared_lib = ".so";
+#endif
+#endif
+#endif
+
+ sprintf(libpath, "%s/../lib/des-plugin%s", newpath, shared_lib);
+
+ fct = (migrate_fn_type)sym_load(libpath, "migrateCredentials",
+ "DES Plugin", 1 /* report errors */ );
+ if ( fct == NULL )
+ {
+ return(1);
+ }
+
+ newcred = (fct)(oldpath, newpath, cred);
+
+ fprintf(stdout, "%s", newcred);
+
+ return(0);
+
+}
diff --git a/ldap/servers/slapd/tools/mkdep.c b/ldap/servers/slapd/tools/mkdep.c
new file mode 100644
index 00000000..469194fe
--- /dev/null
+++ b/ldap/servers/slapd/tools/mkdep.c
@@ -0,0 +1,335 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+/*
+ * Originally by Linus Torvalds.
+ * Smart CONFIG_* processing by Werner Almesberger, Michael Chastain.
+ * Lobotomized by Robey Pointer.
+ *
+ * Usage: mkdep file ...
+ *
+ * Read source files and output makefile dependency lines for them.
+ * I make simple dependency lines for #include "*.h".
+ */
+
+#include <ctype.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#ifdef WINNT
+#include <windows.h>
+#include <winbase.h>
+#include <io.h>
+#else
+#include <unistd.h>
+#include <fcntl.h>
+#include <sys/mman.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#endif
+
+
+char __depname[512] = "\n\t@touch ";
+#define depname (__depname+9)
+int hasdep;
+
+char *outdir = ".";
+
+struct path_struct {
+ int len;
+ char buffer[256-sizeof(int)];
+} path = { 0, "" };
+
+
+#ifdef WINNT
+#define EXISTS(_fn) _access(_fn, 00)
+#else
+#define EXISTS(_fn) access(_fn, F_OK)
+#endif
+
+/*
+ * Handle an #include line.
+ */
+void handle_include(const char * name, int len)
+{
+ memcpy(path.buffer+path.len, name, len);
+ path.buffer[path.len+len] = '\0';
+ if (EXISTS(path.buffer))
+ return;
+
+ if (!hasdep) {
+ hasdep = 1;
+ /* don't use outdir if it's a .h file */
+ if ((strlen(depname) > 2) &&
+ (strcmp(depname + strlen(depname) - 2, ".h") == 0)) {
+ /* skip using the outdir */
+ } else {
+ if (outdir)
+ printf("%s/", outdir);
+ }
+ printf("%s:", depname);
+ }
+ printf(" \\\n %s", path.buffer);
+}
+
+
+/* --- removed weird functions to try to emulate asm ---
+ * (turns out it's faster just to scan thru a char*)
+ */
+
+#define GETNEXT { current = *next++; if (next >= end) break; }
+
+/*
+ * State machine macros.
+ */
+#define CASE(c,label) if (current == c) goto label
+#define NOTCASE(c,label) if (current != c) goto label
+
+/*
+ * Yet another state machine speedup.
+ */
+#define MAX2(a,b) ((a)>(b)?(a):(b))
+#define MIN2(a,b) ((a)<(b)?(a):(b))
+#define MAX4(a,b,c,d) (MAX2(a,MAX2(b,MAX2(c,d))))
+#define MIN4(a,b,c,d) (MIN2(a,MIN2(b,MIN2(c,d))))
+
+
+
+/*
+ * The state machine looks for (approximately) these Perl regular expressions:
+ *
+ * m|\/\*.*?\*\/|
+ * m|'.*?'|
+ * m|".*?"|
+ * m|#\s*include\s*"(.*?)"|
+ *
+ * About 98% of the CPU time is spent here, and most of that is in
+ * the 'start' paragraph. Because the current characters are
+ * in a register, the start loop usually eats 4 or 8 characters
+ * per memory read. The MAX6 and MIN6 tests dispose of most
+ * input characters with 1 or 2 comparisons.
+ */
+void state_machine(const char * map, const char * end)
+{
+ register const char * next = map;
+ register const char * map_dot;
+ register unsigned char current;
+
+ for (;;) {
+start:
+ GETNEXT
+__start:
+ if (current > MAX4('/','\'','"','#')) goto start;
+ if (current < MIN4('/','\'','"','#')) goto start;
+ CASE('/', slash);
+ CASE('\'', squote);
+ CASE('"', dquote);
+ CASE('#', pound);
+ goto start;
+
+/* / */
+slash:
+ GETNEXT
+ NOTCASE('*', __start);
+slash_star_dot_star:
+ GETNEXT
+__slash_star_dot_star:
+ NOTCASE('*', slash_star_dot_star);
+ GETNEXT
+ NOTCASE('/', __slash_star_dot_star);
+ goto start;
+
+/* '.*?' */
+squote:
+ GETNEXT
+ CASE('\'', start);
+ NOTCASE('\\', squote);
+ GETNEXT
+ goto squote;
+
+/* ".*?" */
+dquote:
+ GETNEXT
+ CASE('"', start);
+ NOTCASE('\\', dquote);
+ GETNEXT
+ goto dquote;
+
+/* #\s* */
+pound:
+ GETNEXT
+ CASE(' ', pound);
+ CASE('\t', pound);
+ CASE('i', pound_i);
+ goto __start;
+
+/* #\s*i */
+pound_i:
+ GETNEXT NOTCASE('n', __start);
+ GETNEXT NOTCASE('c', __start);
+ GETNEXT NOTCASE('l', __start);
+ GETNEXT NOTCASE('u', __start);
+ GETNEXT NOTCASE('d', __start);
+ GETNEXT NOTCASE('e', __start);
+ goto pound_include;
+
+/* #\s*include\s* */
+pound_include:
+ GETNEXT
+ CASE(' ', pound_include);
+ CASE('\t', pound_include);
+ map_dot = next;
+ CASE('"', pound_include_dquote);
+ goto __start;
+
+/* #\s*include\s*"(.*)" */
+pound_include_dquote:
+ GETNEXT
+ CASE('\n', start);
+ NOTCASE('"', pound_include_dquote);
+ handle_include(map_dot, next - map_dot - 1);
+ goto start;
+
+ }
+}
+
+
+#ifdef WINNT
+
+/*
+ * Alternate implementation of do_depend() for NT
+ * (NT has its own wacky versions of open/mmap/close)
+ */
+void do_depend(const char *filename, const char *command)
+{
+ HANDLE fd, mapfd;
+ BY_HANDLE_FILE_INFORMATION st;
+ char *map;
+
+ fd = CreateFile(filename, GENERIC_READ, FILE_SHARE_READ, NULL,
+ OPEN_EXISTING, FILE_FLAG_SEQUENTIAL_SCAN, NULL);
+ if (fd == INVALID_HANDLE_VALUE) {
+ fprintf(stderr, "NT error opening '%s'\n", filename);
+ return;
+ }
+ if (! GetFileInformationByHandle(fd, &st)) {
+ fprintf(stderr, "NT error getting stat on '%s'\n", filename);
+ CloseHandle(fd);
+ return;
+ }
+ if (st.nFileSizeLow == 0) {
+ fprintf(stderr, "%s is empty\n", filename);
+ CloseHandle(fd);
+ return;
+ }
+
+ mapfd = CreateFileMapping(fd, NULL, PAGE_READONLY, st.nFileSizeHigh,
+ st.nFileSizeLow, NULL);
+ if (mapfd == NULL) {
+ fprintf(stderr, "NT error creating file mapping of '%s'\n",
+ filename);
+ CloseHandle(fd);
+ return;
+ }
+ map = MapViewOfFile(mapfd, FILE_MAP_READ, 0, 0, 0);
+ if (map == NULL) {
+ fprintf(stderr, "NT error creating mapped view of '%s'\n",
+ filename);
+ CloseHandle(mapfd);
+ CloseHandle(fd);
+ return;
+ }
+
+ hasdep = 0;
+ state_machine(map, map+st.nFileSizeLow);
+ if (hasdep)
+ puts(command);
+
+ UnmapViewOfFile(map);
+ CloseHandle(mapfd);
+ CloseHandle(fd);
+}
+
+#else
+
+/*
+ * Generate dependencies for one file.
+ */
+void do_depend(const char * filename, const char * command)
+{
+ int mapsize;
+ int pagesizem1 = getpagesize()-1;
+ int fd;
+ struct stat st;
+ char * map;
+
+ fd = open(filename, O_RDONLY);
+ if (fd < 0) {
+ perror(filename);
+ return;
+ }
+
+ fstat(fd, &st);
+ if (st.st_size == 0) {
+ fprintf(stderr,"%s is empty\n",filename);
+ close(fd);
+ return;
+ }
+
+ mapsize = st.st_size;
+ mapsize = (mapsize+pagesizem1) & ~pagesizem1;
+ map = mmap(NULL, mapsize, PROT_READ, MAP_PRIVATE, fd, 0);
+ if ((long) map == -1) {
+ perror("mkdep: mmap");
+ close(fd);
+ return;
+ }
+ if ((unsigned long) map % sizeof(unsigned long) != 0)
+ {
+ fprintf(stderr, "do_depend: map not aligned\n");
+ exit(1);
+ }
+
+ hasdep = 0;
+ state_machine(map, map+st.st_size);
+ if (hasdep)
+ puts(command);
+
+ munmap(map, mapsize);
+ close(fd);
+}
+
+#endif
+
+
+/*
+ * Generate dependencies for all files.
+ */
+int main(int argc, char **argv)
+{
+ int len;
+
+ while (--argc > 0) {
+ const char *filename = *++argv;
+ const char *command = __depname;
+
+ if (strcmp(filename, "-o") == 0) {
+ outdir = *++argv;
+ argc--;
+ continue;
+ }
+ len = strlen(filename);
+ memcpy(depname, filename, len+1);
+ if (len > 2 && filename[len-2] == '.') {
+ if (filename[len-1] == 'c' || filename[len-1] == 'S') {
+ depname[len-1] = 'o';
+ command = "";
+ }
+ }
+ do_depend(filename, command);
+ }
+ return 0;
+}
diff --git a/ldap/servers/slapd/tools/mmldif.c b/ldap/servers/slapd/tools/mmldif.c
new file mode 100644
index 00000000..413c1b7d
--- /dev/null
+++ b/ldap/servers/slapd/tools/mmldif.c
@@ -0,0 +1,1742 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+
+#include <stdio.h>
+#include <ctype.h>
+#include <string.h>
+#include <time.h>
+#include <stdlib.h>
+#include <ldap.h>
+#ifndef _WIN32
+# define stricmp strcasecmp
+#else
+# include <io.h>
+#endif
+
+#include <pk11func.h>
+
+#include <slap.h>
+#include <getopt_ext.h>
+#include <ldaplog.h>
+
+#ifndef TRUE
+#define TRUE 1
+#endif
+
+#ifndef FALSE
+#define FALSE 0
+#endif
+
+#ifdef _WIN32
+int slapd_ldap_debug = 0;
+int *module_ldap_debug;
+#endif
+
+/*
+ * VSTRING was defined in PMDF headers.
+ */
+typedef struct vstring {
+ int length;
+ char body[252];
+} MM_VSTRING;
+
+/*
+ * Base64 declarations.
+ */
+
+typedef struct Enc64_s {
+ struct Enc64_s * next;
+ unsigned char * source;
+ int slen;
+} Enc64_t;
+
+typedef struct Dec64_s {
+ struct Dec64_s * next;
+ unsigned char * dest;
+ int maxlen;
+ int curlen;
+ int nextra;
+ unsigned char extra[3];
+} Dec64_t;
+
+Enc64_t * initEnc64(unsigned char * source, int slen);
+int Enc64(Enc64_t * this, unsigned char *dest, int maxlen, int *len);
+void freeEnc64(Enc64_t *this);
+Dec64_t * initDec64(unsigned char * dest, int maxlen);
+int freeDec64(Dec64_t *this);
+int Dec64(Dec64_t * this, unsigned char *source);
+
+/*
+ * License check declarations.
+ */
+
+int license_limit = -1;
+int license_count;
+
+/*
+ * Public declarations.(potentially).
+ */
+
+#define IDDS_MM_OK 0
+#define IDDS_MM_EOF -1
+#define IDDS_MM_ABSENT -2
+#define IDDS_MM_FIO -3
+#define IDDS_MM_BAD -4
+
+/* attrib_t is used to hold each record in memory. The emphasis here is
+ * on size, although compromising simplicity rather than speed. In reality
+ * the way this is used is that there is a block of bytes defined. within
+ * that block are a sequence of records each alligned on whatever is needed
+ * to read shorts happilly. each record consists of a name, a value and
+ * their lengths. The value length is first, because it has to be aligned
+ * then the name value, because we need it first, then the name, null
+ * terminated, then the value, null terminated. Thus if "thing" is a pointer
+ * to one of these things,
+ * thing->data is the name
+ * (thing->data + namelen + 1) is the value,
+ * (thing->data + ((namelen + 1 + valuelen + 1 + 3) & ~3) is the next one
+ * (if we're aligned on 4 byte boundaries)
+ */
+typedef int Boolean;
+typedef struct {
+ int valuelen;
+ char namelen;
+ char data[1];
+} attrib_t;
+
+#define attribname(thing) (thing)->data
+#define attribvalue(thing) ((thing)->data + (thing)->namelen + 1)
+#define attribalign 4
+#define attribsize(thing) (((thing)->namelen + (thing)->valuelen + 1 \
+ + attribalign) & ~(attribalign-1))
+#define attribnext(thing) (attrib_t *)(((char *)thing) \
+ + (((thing)->namelen + (thing)->valuelen \
+ + sizeof(int) + 2 + attribalign) & ~(attribalign-1)))
+
+/* record_t is used to hold a record once it had been squeezed
+ * obviously it has to be allocated carefully so that it is the right size
+ */
+typedef struct {
+ short nattrs;
+ attrib_t data;
+} record_t;
+
+/* attrib1_t is used to read in and sort a record */
+typedef struct attrib1_s {
+ struct attrib1_s *next;
+ char namelen;
+ char name[64];
+ int valuelen;
+ char value[0x20000];
+} attrib1_t;
+
+typedef struct ignore_s {
+ struct ignore_s *next;
+ char name[65];
+} ignore_t;
+
+/* entry_t is the structure used to carry the fingerprint in the hash table */
+typedef struct entry_s {
+ struct entry_s *overflow; /* we hash into buckets. This
+ * is the chain of entries */
+ char key[20]; /* this is the hash of the DN */
+ int present[4]; /* actually treated as a 128 bit array*/
+ /* this is the bitmap of which
+ * directories contain this DN */
+ int db; /* this is the directory number which
+ * provided the data for this entry */
+ record_t * first; /* this it the actual data */
+ char fingerprint[20];/* this is the hash of the data */
+ char flags; /* the status of this entry */
+#define EMPTY 0
+#define IDENTITY 1
+#define MODIFIED 2
+#define LOADED 0x10
+} entry_t;
+
+typedef struct {
+ time_t start_time;
+ int records;
+ int records_away;
+ time_t end_time;
+} cookstats_t;
+
+typedef struct {
+ cookstats_t cook;
+
+ time_t comb_start_time;
+ int authoritative_records;
+ time_t comb_end_time;
+
+ time_t diff_start_time;
+ int num_identities;
+ int num_unchanged;
+ int num_deletes;
+ int num_adds;
+ int num_modifies;
+ time_t diff_end_time;
+
+ cookstats_t uncook;
+} stats_t;
+
+extern int mm_init(int argc, char * argv[]);
+extern int mm_diff(stats_t *statsp);
+
+extern int mm_getvalue(
+ record_t *first,
+ attrib1_t *a,
+ int directory,
+ char *name,
+ char **value,
+ int *length
+);
+
+extern int mm_is_deleted(
+ record_t *first,
+ attrib1_t *a,
+ int directory
+);
+
+extern int mm_get_winner(record_t *first, attrib1_t *a);
+extern void mm_init_winner(void);
+extern void mm_fin_winner(void);
+
+/*
+ * Private declarations.
+ */
+
+#define log_write_error() fprintf(stderr, "error writing record\n")
+
+/*
+ * We need to maintain the order of entries read from input, so that
+ * we can maintain hierarchical ordering. The entryblock structure
+ * is used for that purpose. Memory for blocks of entries are allocated
+ * and strung in a linked list.
+ */
+struct entryblock {
+ entry_t *eb_block;
+ unsigned n;
+ struct entryblock *next;
+};
+
+static struct entryblock *eb_head = NULL, *eb_cur = NULL;
+
+entry_t *entryalloc(void)
+{
+ if (eb_head == NULL || eb_cur->n == 0x1000) {
+ struct entryblock *newblock;
+ newblock =
+ (struct entryblock *)calloc(1, sizeof(struct entryblock));
+ newblock->eb_block = (entry_t*)calloc(0x1000, sizeof(entry_t));
+ if (eb_head == NULL) {
+ eb_cur = eb_head = newblock;
+ } else {
+ eb_cur = eb_cur->next = newblock;
+ }
+ }
+ return &eb_cur->eb_block[eb_cur->n++];
+}
+
+typedef struct {
+ FILE * fp;
+ int end;
+} edfFILE;
+
+static int ndirectories;
+static edfFILE edfin[128];
+static FILE * edfout[128];
+static FILE * ofp;
+static char line[2048];
+static char seed;
+static int hashmask;
+static entry_t **hashtable;
+static int maxcount;
+static int emitchanges;
+
+static int readrec(edfFILE * edf1, attrib1_t ** attrib);
+static void freefreelist(attrib1_t * freelist);
+static void hashname(char seed, attrib1_t * attrib, char * hashkey);
+static void hashvalue(char seed, attrib1_t * attrib, char * fingerprint);
+static record_t * newrecord(attrib1_t * big);
+static int adddelete(FILE * edf3, attrib1_t * attrib);
+static int addnew(FILE * edf3, const char *changetype, record_t * first);
+static int addmodified(FILE * edf3, attrib1_t * attrib, record_t * first);
+static int simpletext(unsigned char * body, int length);
+static int simpletextbody(unsigned char * body, int length);
+static int putvalue(
+ FILE * fh,
+ const char *tag,
+ char * name,
+ int namelen,
+ char * value,
+ int valuelen
+);
+static int signedmemcmp(
+ unsigned char * a,
+ int lena,
+ unsigned char * b,
+ int lenb
+);
+static void makeupper(MM_VSTRING * v, char * body, int len);
+
+static void commententry(FILE *fp, attrib1_t *attrib);
+
+int mm_diff(stats_t *statsp)
+{
+ unsigned int h;
+ entry_t * overflow;
+ int i;
+ int pindex;
+ int pmask;
+ attrib1_t * attrib = 0;
+ entry_t * hashentry;
+ entry_t * hashentry2;
+ char fingerprint[16];
+ int stat;
+ int count;
+ int firsttime = TRUE;
+ int records = 0;
+ int added;
+ struct entryblock *block, *next;
+
+ union {
+ unsigned int key;
+ char data[16];
+ } hashkey;
+
+ unsigned int key;
+
+ time(&statsp->diff_start_time);
+ license_count = 0;
+
+/*
+ * read all entries from all directories hashing name and value, and make
+ * a bitmaps of who has each entry. Flag those entries where at least
+ * one directory differs from any other.
+ */
+ for (i = 0; i < ndirectories; i++) {
+ pindex = i / 32;
+ pmask = 1 << (i % 32);
+ LDAPDebug(LDAP_DEBUG_TRACE, "finger printing directory %d\n", i, 0, 0);
+ while (TRUE) {
+ stat = readrec(&edfin[i], &attrib);
+ if (stat == IDDS_MM_ABSENT) {
+ LDAPDebug(LDAP_DEBUG_TRACE, "ignored: %s: %s\n",
+ attrib->name, attrib->value, 0);
+ continue;
+ }
+ if (stat == IDDS_MM_EOF)
+ break;
+ if (stat != IDDS_MM_OK) {
+ free(hashtable);
+ return stat;
+ }
+ records++;
+ LDAPDebug(LDAP_DEBUG_TRACE, "db%d: %s: %s\n",
+ i, attrib->name, attrib->value);
+ hashname(seed, attrib, hashkey.data);
+ key = hashkey.key & hashmask;
+ if (!hashtable[key]) {
+ hashentry = hashtable[key] = entryalloc();
+ } else {
+ hashentry = hashtable[key];
+ while (hashentry &&
+ memcmp(hashkey.data, hashentry->key, 16))
+ hashentry = hashentry->overflow;
+ if (hashentry != NULL) {
+ if (hashentry->present[pindex] & pmask) {
+ LDAPDebug(LDAP_DEBUG_TRACE,
+ "duplicate DN <%s=%s> (ignored)\n",
+ attrib->name, attrib->value, 0);
+ if (emitchanges) {
+ fprintf(edfout[i], "\n# Duplicate DN:\n");
+ commententry(edfout[i], attrib);
+ }
+ if (ofp != NULL) {
+ fprintf(ofp, "\n# Duplicate DN (in database %d):\n",
+ i);
+ commententry(ofp, attrib);
+ }
+ } else {
+ hashentry->present[pindex] |= pmask;
+ hashvalue(seed, attrib, fingerprint);
+ if (memcmp(fingerprint, hashentry->fingerprint, 16)) {
+ LDAPDebug(LDAP_DEBUG_TRACE,
+ "...data modified\n", key, 0, 0);
+ hashentry->flags = MODIFIED;
+ }
+ }
+ continue;
+ }
+ LDAPDebug(LDAP_DEBUG_TRACE, "overflow in key %u\n", key, 0, 0);
+ hashentry2 = entryalloc();
+ hashentry2->overflow = hashtable[key];
+ hashentry = hashtable[key] = hashentry2;
+ }
+ hashentry->present[pindex] |= pmask;
+ memcpy(hashentry->key, hashkey.data, 16);
+ hashentry->flags = IDENTITY;
+ statsp->num_identities++;
+ hashvalue(seed, attrib, hashentry->fingerprint);
+ }
+ if ((license_limit > 0) && (records > license_limit)) {
+ fprintf(stderr, "license exceeded\n");
+ free(hashtable);
+ return IDDS_MM_BAD;
+ }
+ if (records > license_count)
+ license_count = records;
+ records = 0;
+ }
+
+/*
+ * read all the directories again. This time we load the data into memory
+ * We use a fairly tight (and ugly) structure to hold the data.
+ * There are three possibilities to consider:
+ * 1. no data has yet been loaded for this entry (load it)
+ * 2. data is present, and the data is marked as an identity
+ * (skip it)
+ * 3. data is present, and the data differs in at least one
+ * directory. call out to see who wins.
+ */
+ for (i = 0; i < ndirectories; i++) {
+ rewind(edfin[i].fp);
+ edfin[i].end = FALSE;
+ pindex = i / 32;
+ pmask = 1 << (i % 32);
+
+ LDAPDebug(LDAP_DEBUG_TRACE,
+ "loading authoritative data from directory %d\n", i, 0, 0);
+ count = 0;
+ while (TRUE) {
+ stat = readrec(&edfin[i], &attrib);
+ if (stat == IDDS_MM_ABSENT) {
+ LDAPDebug(LDAP_DEBUG_TRACE, "ignored: %s: %s\n",
+ attrib->name, attrib->value, 0);
+ continue;
+ }
+ if (stat == IDDS_MM_EOF)
+ break;
+ if (stat != IDDS_MM_OK) {
+ free(hashtable);
+ return stat;
+ }
+ LDAPDebug(LDAP_DEBUG_TRACE, "db%d: %s: %s\n",
+ i, attrib->name, attrib->value);
+ hashname(seed, attrib, hashkey.data);
+ key = hashkey.key & hashmask;
+ hashentry = hashtable[key];
+ while (hashentry &&
+ memcmp(hashentry->key, hashkey.data, 16))
+ hashentry = hashentry->overflow;
+ if (hashentry == NULL) {
+ LDAPDebug(LDAP_DEBUG_TRACE, "...hash entry not found\n", 0, 0, 0);
+ continue;
+ }
+ if (!(hashentry->flags & LOADED))
+ {
+ count++;
+ hashentry->first = newrecord(attrib);
+ hashentry->flags |= LOADED;
+ LDAPDebug(LDAP_DEBUG_TRACE, " ...data loaded\n", 0, 0, 0);
+ hashentry->db = i;
+ continue;
+ }
+ if (hashentry->flags & IDENTITY)
+ continue;
+ if (mm_get_winner(hashentry->first, attrib)) {
+ hashentry->flags |= LOADED;
+ LDAPDebug(LDAP_DEBUG_TRACE, " ...winner data loaded\n", 0, 0, 0);
+ hashentry->db = i;
+ free(hashentry->first);
+ hashentry->first = newrecord(attrib);
+ hashvalue(seed, attrib, hashentry->fingerprint);
+ /* must take new fingerprint */
+ continue;
+ }
+ }
+ }
+
+ if (!emitchanges) goto afterchanges;
+
+/*
+ * Now we have the "authoritative" data in memory. Hey, that's what
+ * VM is for. Now we are able to go through each directory (again)
+ * and generate the differences. There are a number of possibilities
+ * 1. the entry is marked as an identity. skip it
+ * 2. the entry is marked as originating from this directory
+ * skip it
+ * 3. the entry's finger print is unchanged. skip it
+ * 4. the entry has isDeleted set. emit a delete
+ * 5. otherwise emit a change record.
+ */
+ for (i = 0; i < ndirectories; i++) {
+ rewind(edfin[i].fp);
+ edfin[i].end = FALSE;
+ pindex = i / 32;
+ pmask = 1 << (i % 32);
+
+ LDAPDebug(LDAP_DEBUG_TRACE,
+ "generating differences for directory %d\n", i, 0, 0);
+ count = 0;
+ while (TRUE) {
+ stat = readrec(&edfin[i], &attrib);
+ if (stat == IDDS_MM_ABSENT) {
+ LDAPDebug(LDAP_DEBUG_TRACE, "ignored: %s: %s\n",
+ attrib->name, attrib->value, 0);
+ continue;
+ }
+ if (stat == IDDS_MM_EOF)
+ break;
+ if (stat != IDDS_MM_OK) {
+ free(hashtable);
+ return stat;
+ }
+ LDAPDebug(LDAP_DEBUG_TRACE, "db%d: %s: %s\n",
+ i, attrib->name, attrib->value);
+ hashname(seed, attrib, hashkey.data);
+ key = hashkey.key & hashmask;
+ hashentry = hashtable[key];
+ while (hashentry &&
+ memcmp(hashentry->key, hashkey.data, 16))
+ hashentry = hashentry->overflow;
+ if (hashentry == NULL) {
+ LDAPDebug(LDAP_DEBUG_TRACE, "...hash entry not found\n", 0, 0, 0);
+ continue;
+ }
+ if (hashentry->flags & IDENTITY)
+ continue;
+ if (hashentry->db == i)
+ continue;
+ hashvalue(seed, attrib, fingerprint);
+ if (memcmp(fingerprint, hashentry->fingerprint, 16)) {
+ if (mm_is_deleted(hashentry->first, attrib, 0)) {
+ LDAPDebug(LDAP_DEBUG_TRACE, " ...deleted\n", 0, 0, 0);
+ adddelete(edfout[i], attrib);
+ } else {
+ LDAPDebug(LDAP_DEBUG_TRACE, " ...modified\n", 0, 0, 0);
+ addmodified(edfout[i], attrib, hashentry->first);
+ }
+ }
+ }
+ }
+
+ afterchanges:
+
+/*
+ * Nearly done. Now we need to go through each entry in the hash table
+ * and for each directory check the "present" bit. If this is set
+ * no action is needed here. Otherwise we emit an add.
+ * we take this opportunity to free the memory.
+ */
+ LDAPDebug(LDAP_DEBUG_TRACE, "scanning db for new entries\n", 0, 0, 0);
+ for (h = 0; h < 0x1000; h++) {
+ for (hashentry = hashtable[h]; hashentry; hashentry = overflow) {
+ if (!hashentry->flags)
+ break;
+
+ for (i = 0, added = 0; i < ndirectories; i++) {
+ pindex = i / 32;
+ pmask = 1 << (i % 32);
+ if (hashentry->present[pindex] & pmask)
+ continue;
+ if (mm_is_deleted(hashentry->first, NULL, 0)) continue;
+ added++;
+ if (!emitchanges) continue;
+ LDAPDebug(LDAP_DEBUG_TRACE, " ...add new\n", 0, 0, 0);
+ addnew(edfout[i], "add", hashentry->first);
+ }
+
+ if (added) {
+ statsp->num_adds++;
+ } else if (hashentry->flags & MODIFIED) {
+ statsp->num_modifies++;
+ } else {
+ statsp->num_unchanged++;
+ }
+
+ overflow = hashentry->overflow;
+ }
+ }
+
+ /* output authoritative data and free data */
+ for (block = eb_head; block != NULL; block = next) {
+ entry_t *entry;
+ for (h = 0; h < block->n; h++) {
+ entry = &block->eb_block[h];
+ if (ofp != NULL) {
+ if (!mm_is_deleted(entry->first, NULL, 0)) {
+ addnew(ofp, NULL, entry->first);
+ }
+ }
+ free(entry->first);
+ }
+ next = block->next;
+ free(block->eb_block);
+ free(block);
+ }
+
+ free(hashtable);
+ time(&statsp->diff_end_time);
+ return IDDS_MM_OK;
+}
+
+static void usage(char *m)
+{
+ fprintf(stderr,"usage: mmldif [-c] [-D] [-o out.ldif] in1.ldif in2.ldif ...\n\n", m);
+ fprintf(stderr,"-c\tWrite a change file (.delta) for each input file\n");
+ fprintf(stderr,"-D\tPrint debugging information\n");
+ fprintf(stderr,"-o\tWrite authoritative data to this file\n");
+ fprintf(stderr,"\n");
+ exit(1);
+}
+
+int mm_init(int argc, char * argv[])
+{
+ char deltaname[255];
+ time_t tl;
+ int c;
+ char *ofn = NULL;
+ char *prog = argv[0];
+ char *tailorfile = NULL;
+
+ time(&tl);
+ seed = (char)tl;
+ maxcount = 10;
+ ndirectories = 0;
+ emitchanges = 0;
+ ofp = NULL;
+
+ mm_init_winner();
+
+ slapd_ldap_debug = 0;
+
+ while ((c = getopt(argc, argv, "cDho:")) != EOF) {
+ switch (c) {
+ case 'c':
+ emitchanges = 1;
+ break;
+ case 'D':
+ slapd_ldap_debug = 65535;
+ break;
+ case 'o':
+ ofn = strdup(optarg);
+ break;
+ case 'h':
+ default:
+ usage(prog);
+ break;
+ }
+ }
+
+#ifdef _WIN32
+ module_ldap_debug = &slapd_ldap_debug;
+ libldap_init_debug_level(&slapd_ldap_debug);
+#endif
+
+ if (ofn != NULL) {
+ ofp = fopen(ofn, "w");
+ if (ofp == NULL) {
+ perror(ofn);
+ return -1;
+ }
+ free(ofn);
+ ofn = NULL;
+ }
+
+ for (argv += optind; optind < argc; optind++, argv++) {
+ edfin[ndirectories].fp = fopen(*argv, "r");
+ if (edfin[ndirectories].fp == NULL) {
+ perror(*argv);
+ return -1;
+ }
+ edfin[ndirectories].end = FALSE;
+
+ if (emitchanges) {
+ strcpy(deltaname, *argv);
+ strcat(deltaname, ".delta");
+ edfout[ndirectories] = fopen(deltaname, "w");
+ if (edfout[ndirectories] == NULL) {
+ perror(deltaname);
+ return -1;
+ }
+ }
+ ndirectories++;
+ }
+
+ if (ndirectories == 0) {
+ fprintf(stderr, "\nno input files\n\n");
+ usage(prog);
+ return 0;
+ }
+
+ hashmask = 0xfff;
+ hashtable = (entry_t **)calloc(0x1000, sizeof(entry_t*));
+ if (tailorfile) free(tailorfile);
+ return 0;
+}
+
+/* this clears the attrib structure if there is one, and reads in the data
+ * sorting lines 2 to n by name, and eliminating comments
+ */
+static int
+readrec(edfFILE * edf1, attrib1_t ** attrib)
+{
+ Dec64_t * b64;
+ char * vptr;
+ char * lptr;
+ char * ptr;
+ int len;
+ int lookahead = 0;
+ int toolong = FALSE;
+ int rc;
+ int cmp;
+ attrib1_t * att;
+ attrib1_t ** prev;
+ attrib1_t * freelist = *attrib;
+ attrib1_t * newlist = NULL;
+ attrib1_t * a;
+ int ignore_rec = FALSE;
+
+ *attrib = NULL;
+ if (edf1->end) {
+ freefreelist(freelist);
+ return IDDS_MM_EOF;
+ }
+
+ while (TRUE) {
+ if (lookahead) {
+ if (lookahead == '\n') {
+ break; /* return */
+ }
+ line[0] = lookahead;
+ lptr = line+1;
+ lookahead = 0;
+ }
+ else
+ lptr = line;
+ if (!fgets(lptr, sizeof(line)-1, edf1->fp)) {
+ edf1->end = TRUE;
+ if (!newlist) {
+ /* that's for the case where the file */
+ /* has a trailing blank line */
+ freefreelist(freelist);
+ return IDDS_MM_EOF;
+ }
+ break; /* return */
+ }
+ if (line[0] == '\n') {
+ /* ignore empty lines at head of LDIF file */
+ if (newlist == NULL) {
+ continue;
+ }
+ break; /* an empty line terminates a record */
+ }
+ if (line[0] == '#')
+ continue; /* skip comment lines */
+
+ len = strlen(line);
+ for (lptr = line+len-1; len; len--, lptr--) {
+ if ((*lptr != '\n') && (*lptr != '\r'))
+ break;
+ *lptr = 0;
+ }
+ vptr = strchr(line, ':');
+ if (!vptr) {
+ LDAPDebug(LDAP_DEBUG_TRACE, "%s\n invalid input line\n",
+ line, 0, 0);
+ continue; /* invalid line, but we'll just skip it */
+ }
+ *vptr = 0;
+ if (!stricmp(line, "authoritative"))
+ continue;
+ if (!freelist) {
+ att = (attrib1_t *)malloc(sizeof(attrib1_t));
+ } else {
+ att = freelist;
+ freelist = freelist->next;
+ }
+ att->namelen = vptr-line;
+
+ if (att->namelen > 63) {
+ att->namelen = 63;
+ *(line+64) = 0;
+ }
+
+ memcpy(att->name, line, att->namelen+1);
+ vptr++;
+ if (*vptr == ':') {
+ vptr++;
+ while (*vptr == ' ') vptr++; /* skip optional spaces */
+ b64 = initDec64((unsigned char *)att->value, 0x20000);
+ if (Dec64(b64, (unsigned char *) vptr)) {
+ LDAPDebug(LDAP_DEBUG_TRACE, "%s\n invalid input line\n",
+ line, 0, 0);
+ continue; /* invalid line, but we'll just skip it */
+ }
+ toolong = FALSE;
+ while (TRUE) {
+ lookahead = fgetc(edf1->fp);
+ if (lookahead != ' ')
+ break;
+ fgets(line, sizeof(line), edf1->fp);
+ len = strlen(line);
+ for (lptr = line+len-1; len; len--, lptr--) {
+ if ((*lptr != '\n') && (*lptr != '\r'))
+ break;
+ *lptr = 0;
+ }
+ rc = Dec64(b64, (unsigned char *)line);
+ if (rc == -1)
+ {
+ LDAPDebug(LDAP_DEBUG_TRACE,
+ "%s\n invalid input line\n", line, 0, 0);
+ continue; /* invalid line, but we'll just skip it */
+ }
+
+ if (rc) {
+ if (!toolong) {
+ toolong = TRUE;
+ LDAPDebug(LDAP_DEBUG_TRACE,
+ "%s\n line too long\n", line, 0, 0);
+ }
+ continue;
+ }
+ }
+ att->valuelen = freeDec64(b64);
+ } else {
+ if (!*vptr) {
+ att->valuelen = 0;
+ }
+ while (*vptr == ' ') vptr++; /* skip optional spaces */
+
+ att->valuelen = strlen(vptr);
+ memcpy(att->value, vptr, att->valuelen);
+ ptr = att->value + att->valuelen;
+ while (TRUE) {
+ lookahead = fgetc(edf1->fp);
+ if (lookahead != ' ')
+ break;
+ fgets(line, sizeof(line), edf1->fp);
+ len = strlen(line);
+ for (lptr = line+len-1; len; len--, lptr--) {
+ if ((*lptr != '\n') && (*lptr != '\r'))
+ break;
+ *lptr = 0;
+ }
+ memcpy(ptr, line, len);
+ att->valuelen += len;
+ ptr += len;
+ }
+ *ptr = 0;
+ }
+
+ if (newlist) {
+ if (newlist->next) {
+ for (a = newlist->next, prev = &(newlist->next);
+ a; prev=&(a->next), a = a->next) {
+ cmp = stricmp(a->name, att->name);
+ if (cmp > 0) {
+ att->next = *prev;
+ *prev = att;
+ goto f1;
+ }
+ if (cmp == 0) {
+ cmp = signedmemcmp((unsigned char *)a->value,
+ a->valuelen,
+ (unsigned char *)att->value,
+ att->valuelen);
+ if (cmp > 0) {
+ att->next = *prev;
+ *prev = att;
+ goto f1;
+ }
+ }
+ }
+ *prev = att;
+ att->next = NULL;
+ f1: ;
+ } else {
+ newlist->next = att;
+ att->next = NULL;
+ }
+ } else {
+ newlist = att;
+ att->next = NULL;
+ }
+ }
+ *attrib = newlist;
+ freefreelist(freelist);
+ if (ignore_rec)
+ return IDDS_MM_ABSENT;
+ return IDDS_MM_OK;
+}
+
+static void
+freefreelist(attrib1_t * freelist)
+{
+ attrib1_t * next;
+ for (;freelist; freelist = next) {
+ next = freelist->next;
+ free(freelist);
+ }
+}
+
+static void
+hashname(char seed, attrib1_t * attrib, char * hashkey)
+{
+ MM_VSTRING upper;
+ PK11Context *context;
+ unsigned int hashLen;
+
+/* we want the name to be case insensitive, and if the name DN, we want
+ * the value to be case insensitive. */
+/* this creates a hash key based on the first line in attrib */
+ makeupper(&upper, attrib->name, attrib->namelen);
+ context = PK11_CreateDigestContext(SEC_OID_MD5);
+ if (context != NULL) {
+ PK11_DigestBegin(context);
+ PK11_DigestOp(context, (unsigned char *)&seed, 1);
+ PK11_DigestOp(context, (unsigned char *)upper.body, upper.length);
+ PK11_DigestOp(context, (unsigned char *)"=", 1);
+ if (!memcmp(upper.body, "DN", 2)) {
+ makeupper(&upper, attrib->value, attrib->valuelen);
+ PK11_DigestOp(context, (unsigned char *)upper.body, upper.length);
+ } else
+ PK11_DigestOp(context, (unsigned char *)attrib->value, attrib->valuelen);
+ PK11_DigestFinal(context, (unsigned char *)hashkey, &hashLen, 16);
+ PK11_DestroyContext(context, PR_TRUE);
+ }
+ else { /* Probably desesperate but at least deterministic... */
+ memset(hashkey, 0, 16);
+ }
+}
+
+/* this creates a hash key base on all but the first line in attrib */
+static void
+hashvalue(char seed, attrib1_t * attrib, char * fingerprint)
+{
+ MM_VSTRING upper;
+ attrib1_t * a;
+ PK11Context *context;
+ unsigned int fgLen;
+
+ context = PK11_CreateDigestContext(SEC_OID_MD5);
+ if (context != NULL) {
+ PK11_DigestBegin(context);
+ PK11_DigestOp(context, (unsigned char *)&seed, 1);
+ for (a = attrib->next; a; a = a->next) {
+ if (!stricmp(a->name, "authoritative"))
+ continue;
+ /* we want the name to be case insensitive, and if the name DN, we want
+ * the value to be case insensitive. */
+ makeupper(&upper, a->name, a->namelen);
+ PK11_DigestOp(context, (unsigned char *)upper.body, upper.length);
+ PK11_DigestOp(context, (unsigned char *)"=", 1);
+ if (!memcmp(upper.body, "DN", 2)) {
+ makeupper(&upper, a->value, a->valuelen);
+ PK11_DigestOp(context, (unsigned char *)upper.body, upper.length);
+ } else
+ PK11_DigestOp(context, (unsigned char *)a->value, a->valuelen);
+ PK11_DigestOp(context, (unsigned char *)";", 1);
+ }
+ PK11_DigestFinal(context, (unsigned char *)fingerprint, &fgLen, 16);
+ PK11_DestroyContext(context, PR_TRUE);
+ }
+ else { /* Probably desesperate but at least deterministic... */
+ memset(fingerprint, 0, 16);
+ }
+}
+
+/* this writes a record deletion record based on the first line in attrib */
+static int
+adddelete(FILE * edf3, attrib1_t * attrib)
+{
+ if (!putvalue(edf3, NULL, attrib->name, attrib->namelen,
+ attrib->value, attrib->valuelen)) {
+ log_write_error();
+ return IDDS_MM_FIO;
+ }
+ fprintf(edf3, "changetype: delete\n\n");
+ return IDDS_MM_OK;
+}
+
+/* this writes a record addition record based on attrib */
+static int
+addnew(FILE * edf3, const char *changetype, record_t * first)
+{
+ attrib_t * att;
+ int attnum;
+
+ for (attnum = 1, att = &first->data;
+ attnum <= first->nattrs;
+ attnum++, att = attribnext(att)) {
+ if (!stricmp(attribname(att), "modifytimestamp"))
+ continue;
+ if (!stricmp(attribname(att), "modifiersname"))
+ continue;
+ if (!putvalue(edf3, NULL, attribname(att), att->namelen,
+ attribvalue(att), att->valuelen)) {
+ log_write_error();
+ return IDDS_MM_FIO;
+ }
+ if (attnum == 1 && changetype != NULL) {
+ fprintf(edf3, "changetype: %s\n", changetype);
+ }
+ }
+ if (fputs("\n", edf3) < 0) {
+ log_write_error();
+ return IDDS_MM_FIO;
+ }
+ return IDDS_MM_OK;
+}
+
+/* this writes a record modification record based on the information in
+ * first and attrib
+ */
+static int
+addmodified(FILE * edf3, attrib1_t * attrib, record_t * first)
+{
+ attrib_t *b;
+ attrib1_t *a;
+ int num_b;
+ int tot_b;
+ int cmp;
+ char *attrname;
+
+ if (!putvalue(edf3, NULL, attrib->name, attrib->namelen,
+ attrib->value, attrib->valuelen)) {
+ log_write_error();
+ return IDDS_MM_FIO;
+ }
+ if (fputs("changetype: modify\n", edf3) < 0) {
+ log_write_error();
+ return IDDS_MM_FIO;
+ }
+
+ tot_b = first->nattrs;
+ num_b = 1;
+ b = &first->data;
+
+ /* advance past dn attrs */
+ a = attrib->next;
+ b = attribnext(b); num_b++;
+
+ /*
+ * Lock-step through the two attr lists while there are still
+ * attrs remaining in either.
+ */
+ while (a != NULL || num_b <= tot_b) {
+ /* ignore operational attrs */
+ if (num_b <= tot_b &&
+ (stricmp(attribname(b), "modifytimestamp") == 0 ||
+ stricmp(attribname(b), "modifiersname") == 0)) {
+ b = attribnext(b); num_b++;
+ continue;
+ }
+ if (a != NULL &&
+ (stricmp(a->name, "modifytimestamp") == 0 ||
+ stricmp(a->name, "modifiersname") == 0)) {
+ a = a->next;
+ continue;
+ }
+
+ if (num_b > tot_b) {
+ cmp = -1;
+ } else if (a == NULL) {
+ cmp = 1;
+ } else {
+ cmp = stricmp(a->name, attribname(b));
+ }
+ if (cmp < 0) {
+ /* a < b: a is deleted */
+ attrname = a->name;
+ fprintf(edf3, "delete: %s\n-\n", attrname);
+ do {
+ a = a->next;
+ } while (a != NULL && stricmp(a->name, attrname) == 0);
+ continue;
+ } else if (cmp > 0) {
+ /* a > b: b is added */
+ attrname = attribname(b);
+ fprintf(edf3, "add: %s\n", attrname);
+ do {
+ if (!putvalue(edf3, NULL, attribname(b), b->namelen,
+ attribvalue(b), b->valuelen)) {
+ log_write_error();
+ return IDDS_MM_FIO;
+ }
+ b = attribnext(b); num_b++;
+ } while (num_b <= tot_b && stricmp(attribname(b), attrname) == 0);
+ fprintf(edf3, "-\n");
+ continue;
+ } else {
+ /* a == b */
+ int nmods = 0;
+ attrib_t *begin_b = b;
+ attrib1_t *v_del = NULL;
+ attrib_t *v_add = NULL;
+ int begin_num_b = num_b;
+
+ /*
+ * Lock-step through the ordered values.
+ * Remember a maximum of one changed value.
+ * If we encounter more than one change then
+ * just issue a replace of the whole value.
+ */
+ attrname = a->name;
+ do {
+ if (num_b > tot_b || stricmp(attribname(b), attrname) != 0) {
+ cmp = -1;
+ } else if (a == NULL || stricmp(a->name, attrname) != 0) {
+ cmp = 1;
+ } else {
+ cmp = signedmemcmp((unsigned char *)a->value,
+ a->valuelen,
+ (unsigned char *)attribvalue(b),
+ b->valuelen);
+ }
+ if (cmp < 0) {
+ nmods++;
+ v_del = a;
+ a = a->next;
+ } else if (cmp > 0) {
+ nmods++;
+ v_add = b;
+ b = attribnext(b); num_b++;
+ } else {
+ a = a->next;
+ b = attribnext(b); num_b++;
+ }
+ } while ((a != NULL &&
+ stricmp(a->name, attrname) == 0) ||
+ (num_b <= tot_b &&
+ stricmp(attribname(b), attrname) == 0));
+ if (nmods == 1) {
+ if (v_add != NULL) {
+ if (!putvalue(edf3, "add",
+ attribname(v_add), v_add->namelen,
+ attribvalue(v_add), v_add->valuelen)) {
+ log_write_error();
+ return IDDS_MM_FIO;
+ }
+ } else {
+ if (!putvalue(edf3, "delete",
+ v_del->name, v_del->namelen,
+ v_del->value, v_del->valuelen)) {
+ log_write_error();
+ return IDDS_MM_FIO;
+ }
+ }
+ } else if (nmods > 1) {
+ fprintf(edf3, "replace: %s\n", attrname);
+ do {
+ if (!putvalue(edf3, NULL,
+ attribname(begin_b), begin_b->namelen,
+ attribvalue(begin_b), begin_b->valuelen)) {
+ log_write_error();
+ return IDDS_MM_FIO;
+ }
+ begin_b = attribnext(begin_b); begin_num_b++;
+ } while (begin_num_b <= tot_b && begin_b != b);
+ fprintf(edf3, "-\n");
+ }
+ }
+ }
+
+ if (fputs("\n", edf3) < 0) {
+ log_write_error();
+ return IDDS_MM_FIO;
+ }
+ return IDDS_MM_OK;
+}
+
+static record_t * newrecord(attrib1_t * big)
+{
+ record_t * smll;
+ attrib_t * b;
+ attrib1_t * a;
+ int len = 0;
+ int count = 0;
+
+ for (a=big; a; a = a->next) {
+ count++;
+ len += (a->namelen + a->valuelen + sizeof(attrib_t) + attribalign) &
+ ~ (attribalign-1);
+ }
+ len += sizeof(short);
+ smll = (record_t *)malloc(len);
+
+ for (a=big, b=&smll->data; a; a = a->next, b = attribnext(b)) {
+ b->valuelen = a->valuelen;
+ b->namelen = a->namelen;
+ memcpy(attribname(b), a->name, a->namelen+1);
+ memcpy(attribvalue(b), a->value, a->valuelen+1);
+ }
+ smll->nattrs = count;
+ return smll;
+}
+
+static int
+simpletextbody(unsigned char * body, int length)
+{
+ int i;
+ for (i = length; --i >= 0; body++) {
+ if ((*body < ' ') || (*body >= 0x7f))
+ return FALSE;
+ }
+ return TRUE;
+}
+
+static int
+simpletext(unsigned char * body, int length)
+{
+ if ((*body == ':') || (*body == '<') || (*body == ' '))
+ return FALSE;
+ return simpletextbody(body, length);
+}
+
+/* output a string value */
+static int
+putvalue(
+ FILE * fh,
+ const char * tag,
+ char * name,
+ int namelen,
+ char * value,
+ int valuelen
+)
+{
+ Enc64_t * b64;
+ char * lptr;
+ char line[255];
+ int return_code;
+ int len;
+ char * sptr;
+ int rc;
+ int simpletxt = TRUE;
+
+ lptr = line;
+ if (tag != NULL) {
+ sprintf(lptr, "%s: ", tag);
+ lptr += strlen(lptr);
+ memcpy(lptr, name, namelen);
+ lptr += namelen;
+ *lptr++ = '\n';
+ }
+
+ memcpy(lptr, name, namelen);
+ lptr += namelen;
+ *lptr++ = ':';
+
+ if (!valuelen) {
+ *lptr = '\n';
+ *(lptr+1) = 0;
+ return_code = fputs(line, fh);
+ goto return_bit;
+ }
+
+ if (simpletext((unsigned char *)value, valuelen)) {
+ *lptr = ' ';
+ if (valuelen + (lptr+1 - line) < 80) {
+ strcpy(lptr+1, value);
+ strcpy(lptr+1 + valuelen, "\n");
+ return_code = fputs(line, fh);
+ goto return_bit;
+ }
+ len = 80 - (lptr+1 - line);
+ memcpy(lptr+1, value, len);
+ line[80] = '\n';
+ line[81] = 0;
+ return_code = fputs(line, fh);
+ if (return_code < 0)
+ goto return_bit;
+ sptr = value + len;
+ len = valuelen - len;
+ line[0] = ' ';
+ while (len > 79) {
+ memcpy(line+1, sptr, 79);
+ return_code = fputs(line, fh);
+ if (return_code < 0)
+ goto return_bit;
+ sptr += 79;
+ len -= 79;
+ }
+ if (len) {
+ memcpy(line+1, sptr, len);
+ line[len+1] = '\n';
+ line[len+2] = 0;
+ return_code = fputs(line, fh);
+ }
+ goto return_bit;
+ }
+
+ b64 = initEnc64((unsigned char *)value, valuelen);
+ *lptr = ':';
+ *(lptr+1) = ' ';
+ rc = Enc64(b64, (unsigned char *)(lptr+2), 80-(lptr-line), &len);
+ *(lptr +len+2) = '\n';
+ *(lptr + len +3) = 0;
+ return_code = fputs(line, fh);
+ if (return_code < 0)
+ goto return_bit;
+ while (TRUE) {
+ line[0] = ' ';
+ rc = Enc64(b64, (unsigned char *)line+1, 79, &len);
+ if (rc)
+ break;
+ line[len+1] = '\n';
+ line[len+2] = 0;
+ return_code = fputs(line, fh);
+ if (return_code < 0)
+ goto return_bit;
+ }
+
+ return_bit:
+ if (tag != NULL) {
+ fputs("-\n", fh);
+ }
+ if (return_code < 0)
+ return FALSE;
+ return TRUE;
+}
+
+static int
+signedmemcmp(unsigned char * a, int lena, unsigned char * b, int lenb)
+{
+ int c;
+
+ for (;; a++, b++) {
+ if (!lenb)
+ return lena;
+ if (!lena)
+ return -1;
+ if (c=(int)*a - (int)*b)
+ return c;
+ lena--;
+ lenb--;
+ }
+}
+
+static void
+makeupper(MM_VSTRING * v, char * body, int len)
+{
+ char * vp;
+ v->length = len;
+ for (vp = v->body; len > 0; len--, vp++, body++)
+ *vp = toupper(*body);
+}
+
+int
+mm_getvalue(
+ record_t *first,
+ attrib1_t *a,
+ int directory,
+ char *name,
+ char **value,
+ int *length
+)
+{
+ int attnum;
+ attrib_t * att;
+ if (directory) {
+ for ( ; a; a = a->next) {
+ if (!stricmp(a->name, name)) {
+ if (!*value) {
+ *value = a->value;
+ *length = a->valuelen;
+ return TRUE;
+ } else {
+ if (*value == a->value)
+ *value = NULL;
+ }
+ }
+ }
+ return FALSE;
+ }
+
+ att = &first->data;
+
+ for (attnum = 1, att = &first->data;
+ attnum <= first->nattrs;
+ attnum++, att = attribnext(att)) {
+ if (!stricmp(attribname(att), name)) {
+ if (!*value) {
+ *value = attribvalue(att);
+ *length = att->valuelen;
+ return TRUE;
+ } else {
+ if (*value == attribvalue(att))
+ *value = NULL;
+ }
+ }
+ }
+ return FALSE;
+}
+
+int mm_is_deleted(
+ record_t *first,
+ attrib1_t *attrib,
+ int directory
+)
+{
+ char * value = NULL;
+ int len;
+
+ while (mm_getvalue(first, attrib, directory,
+ "objectclass",
+ &value, &len)) {
+ if (stricmp(value, "nsTombstone") == 0) {
+ return 1;
+ }
+ }
+
+ if (mm_getvalue(first, attrib, directory, "isdeleted", &value, &len)) {
+ if ((len == 1 && *value == '1') ||
+ (len == 4 && stricmp(value, "true") == 0)) {
+ return 1;
+ }
+ }
+
+ if (mm_getvalue(first, attrib, directory, "zombi", &value, &len)) {
+ return 1;
+ }
+
+ return 0;
+}
+
+static void commententry(FILE *fp, attrib1_t *attrib)
+{
+ attrib1_t *a;
+
+ if (attrib == NULL) return;
+
+ fprintf(fp, "# %s: %s\n", attrib->name, attrib->value);
+ for (a = attrib->next; a; a = a->next) {
+ if (simpletext((unsigned char *)a->value,
+ a->valuelen)) {
+ fprintf(fp, "# %*.*s: %*.*s\n",
+ a->namelen, a->namelen,
+ a->name,
+ a->valuelen, a->valuelen,
+ a->value);
+ }
+ }
+ fprintf(fp, "\n");
+}
+
+int main(int argc, char *argv[])
+{
+ stats_t stats;
+ int rc;
+ float difftime;
+
+ memset(&stats, 0, sizeof(stats));
+
+ if (rc = mm_init(argc, argv))
+ return rc;
+
+ if ((mm_diff(&stats) == IDDS_MM_OK)
+ && (license_limit > 0)) {
+ if (license_count > license_limit * 98.0 / 100)
+ fprintf(stderr, "That was over 98% of your license limit.\n");
+ else if (license_count > license_limit * 95.0 / 100)
+ fprintf(stderr, "That was over 95% of your license limit.\n");
+ else if (license_count > license_limit * 90.0 / 100)
+ fprintf(stderr, "That was over 90% of your license limit.\n");
+ }
+ mm_fin_winner();
+ printf("start time %s", ctime(&stats.diff_start_time));
+ printf("\nentry counts: unchanged=%d changed=%d new=%d total=%d\n\n",
+ stats.num_unchanged,
+ stats.num_modifies,
+ stats.num_adds,
+ stats.num_identities);
+ printf("end time %s", ctime(&stats.diff_end_time));
+
+ difftime = stats.diff_end_time - stats.diff_start_time;
+ if (difftime <= 1)
+ printf("differencing took <= 1 second\n");
+ else
+ printf("differencing took %u seconds, %u records per second\n",
+ (unsigned int)difftime,
+ (unsigned int)(stats.num_identities / difftime));
+ exit(0);
+}
+
+/*
+ * Conflict resolution.
+ */
+
+void mm_init_winner()
+{
+}
+
+void mm_fin_winner()
+{
+}
+
+int mm_get_winner(record_t * first, attrib1_t * a)
+{
+ int len;
+ char * modified0 = NULL;
+ char * modified1 = NULL;
+
+ mm_getvalue(first, a, 0, "modifytimestamp", &modified0, &len);
+ mm_getvalue(first, a, 1, "modifytimestamp", &modified1, &len);
+
+ if (!modified0) {
+ mm_getvalue(first, a, 0, "createtimestamp", &modified0, &len);
+ }
+
+ if (!modified1) {
+ mm_getvalue(first, a, 1, "createtimestamp", &modified1, &len);
+ }
+
+ if (!modified0) {
+ mm_getvalue(first, a, 0, "deletetimestamp", &modified0, &len);
+ }
+
+ if (!modified1) {
+ mm_getvalue(first, a, 1, "deletetimestamp", &modified1, &len);
+ }
+
+ if (!modified0)
+ return 1;
+ if (!modified1)
+ return 0;
+ return strcmp(modified0, modified1) <= 0;
+}
+
+/*
+ * Base64 Implementation.
+ */
+
+ /* 0123456789ABCDEF */
+static unsigned char b64[] = "ABCDEFGHIJKLMNOP"
+ "QRSTUVWXYZabcdef"
+ "ghijklmnopqrstuv"
+ "wxyz0123456789+/";
+
+static unsigned char ub64[] = {
+/* 0 1 2 3 4 5 6 7
+ * 8 9 A B C C E F
+ */
+/*0-*/ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+/*0-*/ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+/*1-*/ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+/*1-*/ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+/*2-*/ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+/*2-*/ 0xFF, 0xFF, 0xFF, 62, 0xFF, 0xFF, 0xFF, 63,
+/*3-*/ 52, 53, 54, 55, 56, 57, 58, 59,
+/*3-*/ 60, 61, 0xFF, 0xFF, 0xFF, 64, 0xFF, 0xFF,
+/*4-*/ 0xFF, 0, 1, 2, 3, 4, 5, 6,
+/*4-*/ 7, 8, 9, 10, 11, 12, 13, 14,
+/*5-*/ 15, 16, 17, 18, 19, 20, 21, 22,
+/*5-*/ 23, 24, 25, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+/*6-*/ 0xFF, 26, 27, 28, 29, 30, 31, 32,
+/*6-*/ 33, 34, 35, 36, 37, 38, 39, 40,
+/*7-*/ 41, 42, 43, 44, 45, 46, 47, 48,
+/*7-*/ 49, 50, 51, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+/*8-*/ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+/*8-*/ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+/*9-*/ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+/*9-*/ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+/*A-*/ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+/*A-*/ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+/*B-*/ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+/*B-*/ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+/*C-*/ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+/*C-*/ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+/*D-*/ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+/*D-*/ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+/*E-*/ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+/*E-*/ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+/*F-*/ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+/*F-*/ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF};
+
+
+static Enc64_t * freeEnc64List = NULL;
+static Dec64_t * freeDec64List = NULL;
+
+Enc64_t *
+initEnc64(unsigned char * source, int slen)
+{
+ Enc64_t * this = freeEnc64List;
+ if (this)
+ freeEnc64List = freeEnc64List->next;
+ else
+ this = (Enc64_t *)malloc(sizeof(Enc64_t));
+ this->source = source;
+ this->slen = slen;
+ return this;
+ }
+
+int
+Enc64(Enc64_t * this, unsigned char *dest, int maxlen, int *len)
+{
+ /* returns 0 normally
+ * +1 on end of string
+ * -1 on badi
+ */
+ int l;
+ unsigned char * s;
+ unsigned char * d;
+ int reml;
+ int i;
+
+ if (!this->slen)
+ return 1;
+ l = this->slen / 3;
+ s = this->source;
+ if (l > maxlen / 4)
+ {
+ l = maxlen / 4;
+ this->slen -= l*3;
+ reml = 0;
+ this->source += l*3;
+ }
+ else
+ {
+ reml = this->slen % 3;
+ this->slen = 0;
+ }
+ for (d = dest, i = 0; i < l; i++)
+ {
+ *d++ = b64[(*s >> 2) & 0x3f];
+ *d++ = b64[((*s << 4) & 0x30) + ((*(s+1) >> 4) & 0x0f)];
+ s++;
+ *d++ = b64[((*s << 2) & 0x3c) + ((*(s+1) >> 6) & 0x03)];
+ s++;
+ *d++ = b64[*s & 0x3f];
+ s++;
+ }
+ if (reml--)
+ *d++ = b64[(*s >> 2) & 0x3f];
+ else
+ {
+ *d = 0;
+ *len = l*4;
+ return 0;
+ }
+ if (reml)
+ {
+ *d++ = b64[((*s << 4) & 0x30) + ((*(s+1) >> 4) & 0x0f)];
+ s++;
+ *d++ = b64[((*s << 2) & 0x3c)];
+ }
+ else
+ {
+ *d++ = b64[((*s << 4) & 0x30) + ((*(s+1) >> 4) & 0x0f)];
+ *d++ = '=';
+ }
+ *d++ = '=';
+ *d = 0;
+ *len = (l+1)*4;
+ return 0;
+ }
+
+void
+freeEnc64(Enc64_t *this)
+{
+ this->next = freeEnc64List;
+ freeEnc64List = this;
+ }
+
+Dec64_t *
+initDec64(unsigned char * dest, int maxlen)
+{
+ Dec64_t * this = freeDec64List;
+ if (this)
+ freeDec64List = freeDec64List->next;
+ else
+ this = (Dec64_t *)malloc(sizeof(Dec64_t));
+ this->dest = dest;
+ this->maxlen = maxlen;
+ this->curlen = 0;
+ this->nextra = 0;
+ return this;
+ }
+
+int
+freeDec64(Dec64_t *this)
+{
+ this->next = freeDec64List;
+ freeDec64List = this;
+ return this->curlen;
+ }
+
+int
+Dec64(Dec64_t * this, unsigned char *source)
+{
+ /* returns 0 normally
+ * -1 on badi
+ * 1 on too long
+ */
+ unsigned char * s;
+ unsigned char * d;
+ unsigned char * e;
+ unsigned char s1, s2, s3, s4;
+ int i;
+ int slen;
+ int len;
+ int nextra;
+ int newnextra;
+ unsigned char newextra[3];
+
+ nextra = this->nextra;
+ slen = strlen((char *)source);
+ len = (slen + nextra) / 4;
+ newnextra = (slen + nextra) - len * 4;
+ for (i = 0; i < newnextra; i++)
+ {
+ newextra[i] = source[slen-newnextra+i];
+ }
+
+ if (len * 3 > this->maxlen - this->curlen)
+ return 1;
+ for (d = this->dest + this->curlen, s = source, e = this->extra, i = 0;
+ i < len; i++)
+ {
+ if (nextra)
+ {
+ nextra--;
+ s1 = ub64[*e++];
+ }
+ else
+ s1 = ub64[*s++];
+ if (nextra)
+ {
+ nextra--;
+ s2 = ub64[*e++];
+ }
+ else
+ s2 = ub64[*s++];
+ if (nextra)
+ {
+ nextra--;
+ s3 = ub64[*e++];
+ }
+ else
+ s3 = ub64[*s++];
+ s4 = ub64[*s++];
+ if ((s1 | s2 | s3 | s4) & 0x80)
+ return -1;
+ *d++ = (s1 << 2) + (s2 >> 4);
+ this->curlen++;
+ if (s3 == 64)
+ break;
+ *d++ = (s2 << 4) + (s3 >> 2);
+ this->curlen++;
+ if (s4 == 64)
+ break;
+ *d++ = (s3 << 6) + s4;
+ this->curlen++;
+ }
+ this->nextra = newnextra;
+ memcpy(this->extra, newextra, 3);
+ return 0;
+ }
diff --git a/ldap/servers/slapd/tools/pwenc.c b/ldap/servers/slapd/tools/pwenc.c
new file mode 100644
index 00000000..7a150860
--- /dev/null
+++ b/ldap/servers/slapd/tools/pwenc.c
@@ -0,0 +1,400 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+#if defined( _WIN32 )
+#include <sys/stat.h> /* for S_IREAD and S_IWRITE */
+#include <windows.h>
+#include <time.h>
+#include "proto-ntutil.h"
+#else
+#include <sys/socket.h>
+#include <sys/errno.h>
+#include <sys/param.h>
+#include <sys/types.h>
+#if defined(LINUX) /* I bet other Unix would like
+ * this flag. But don't want to
+ * break other builds so far */
+#include <unistd.h>
+#endif
+#endif
+#include <stdio.h>
+#include <string.h>
+#include <ctype.h>
+#include <stdlib.h>
+#include "ldap.h"
+#include "ldif.h"
+#include "../slapi-plugin.h"
+#include "../slap.h"
+#include <nspr.h>
+#include <nss.h>
+#include "../../plugins/pwdstorage/pwdstorage.h"
+
+int ldap_syslog;
+int ldap_syslog_level;
+int slapd_ldap_debug = LDAP_DEBUG_ANY;
+#ifdef _WIN32
+int *module_ldap_debug;
+#endif
+int detached;
+FILE *error_logfp;
+FILE *access_logfp;
+struct pw_scheme *pwdhashscheme;
+int heflag = 0;
+
+static int slapd_config(const char *configdir);
+static int entry_has_attr_and_value(Slapi_Entry *e, const char *attrname, char *value);
+
+static void
+usage( name )
+char *name;
+{
+ fprintf( stderr, "usage: %s -D instance-dir [-H] [-s scheme | -c comparepwd ] password...\n", name );
+ exit( 1 );
+}
+
+
+/*
+ * If global "heflag" is non-zero, un-hex-encode the string
+ * and return a decoded copy. Otherwise return a copy of the
+ * string.
+ */
+static char *
+decode( char *orig )
+{
+ char *r;
+
+ if ( NULL == orig ) {
+ return NULL;
+ }
+ r = calloc( 1, strlen( orig ) + 2 );
+ strcpy( r, orig );
+
+ if ( heflag ) {
+ char *s;
+
+ for ( s = r; *s != '\0'; ++s ) {
+ if ( *s == '%' && ldap_utf8isxdigit( s+1 ) && ldap_utf8isxdigit( s+2 )) {
+ memmove( s, s + 1, 2 );
+ s[ 2 ] = '\0';
+ *s = strtoul( s, NULL, 16 );
+ memmove( s + 1, s + 3, strlen( s + 3 ) + 1 );
+ }
+ }
+ }
+ return r;
+}
+
+
+int
+main( argc, argv )
+ int argc;
+ char *argv[];
+{
+ int i, rc;
+ char *enc, *cmp, *name;
+ struct pw_scheme *pwsp, *cmppwsp;
+ extern int optind;
+ char *cpwd = NULL; /* candidate password for comparison */
+ char errorbuf[BUFSIZ];
+ slapdFrontendConfig_t *slapdFrontendConfig = NULL;
+
+ char *opts = "Hs:c:D:";
+ char *instancedir = NULL;
+ name = argv[ 0 ];
+ pwsp = cmppwsp = NULL;
+
+#ifdef _WIN32
+ module_ldap_debug = &slapd_ldap_debug;
+ libldap_init_debug_level(&slapd_ldap_debug);
+#endif
+
+ PR_Init( PR_USER_THREAD, PR_PRIORITY_NORMAL, 0 );
+
+ /* Initialize NSS to make ds_salted_sha1_pw_enc() work */
+ if (NSS_NoDB_Init(NULL) != SECSuccess) {
+ fprintf( stderr, "Fatal error: unable to initialize the NSS subcomponent." );
+ return( 1 );
+ }
+
+
+ while (( i = getopt( argc, argv, opts )) != EOF ) {
+ switch ( i ) {
+ case 'D':
+ /* kexcoff: quite the same as slapd_bootstrap_config */
+ FrontendConfig_init();
+
+ instancedir = rel2abspath( optarg );
+ if ( config_set_instancedir( "configdir (-D)", instancedir,
+ errorbuf, 1) != LDAP_SUCCESS ) {
+ fprintf( stderr, "%s\n", errorbuf );
+ return( 1 );
+ }
+ slapi_ch_free((void **)&instancedir);
+
+
+ slapdFrontendConfig = getFrontendConfig();
+ if (0 == slapd_config(slapdFrontendConfig->configdir)) {
+ fprintf(stderr,
+ "The configuration files in directory %s could not be read or were not found. Please refer to the error log or output for more information.\n",
+ slapdFrontendConfig->configdir);
+ return(1);
+ }
+ break;
+
+ case 's': /* set hash scheme */
+ if (!slapdFrontendConfig) {
+ usage( name );
+ return( 1 );
+ }
+ if (( pwsp = pw_name2scheme( optarg )) == NULL ) {
+ fprintf( stderr, "%s: unknown hash scheme \"%s\"\n", name,
+ optarg );
+ return( 1 );
+ }
+ break;
+
+ case 'c': /* compare encoded password to password */
+ if (!slapdFrontendConfig) {
+ usage( name );
+ return( 1 );
+ }
+ cpwd = optarg;
+ break;
+
+ case 'H': /* password(s) is(are) hex-encoded */
+ if (!slapdFrontendConfig) {
+ usage( name );
+ return( 1 );
+ }
+ heflag = 1;
+ break;
+
+ default:
+ usage( name );
+ }
+ }
+
+ if (!slapdFrontendConfig) {
+ usage( name );
+ return( 1 );
+ }
+
+ if ( cpwd != NULL ) {
+ cmppwsp = pw_val2scheme( decode( cpwd ), &cmp, 1 );
+ }
+
+ if ( cmppwsp != NULL && pwsp != NULL ) {
+ fprintf( stderr, "%s: do not use -s with -c\n", name );
+ usage( name );
+ }
+
+ if ( cmppwsp == NULL && pwsp == NULL ) {
+ pwsp = pw_name2scheme( SALTED_SHA1_SCHEME_NAME );
+ }
+
+ if ( argc <= optind ) {
+ usage( name );
+ }
+
+ if ( cmppwsp == NULL && pwsp->pws_enc == NULL ) {
+ fprintf( stderr,
+ "The scheme \"%s\" does not support password encoding.\n",
+ pwsp->pws_name );
+ return( 1 );
+ }
+
+ srand((int)time(NULL)); /* schemes such as crypt use random salt */
+
+ for ( rc = 0; optind < argc && rc == 0; ++optind ) {
+ if ( cmppwsp == NULL ) { /* encode passwords */
+ if (( enc = (*pwsp->pws_enc)( decode( argv[ optind ] ))) == NULL ) {
+ perror( name );
+ return( 1 );
+ }
+
+ puts( enc );
+ free( enc );
+ } else { /* compare passwords */
+ if (( rc = (*(cmppwsp->pws_cmp))( decode( argv[ optind ]), cmp )) == 0 ) {
+ printf( "%s: password ok.\n", name );
+ } else {
+ printf( "%s: password does not match.\n", name );
+ }
+ }
+ }
+
+ return( rc == 0 ? 0 : 1 );
+}
+
+/* -------------------------------------------------------------- */
+
+/*
+ kexcoff: quite similar to slapd_bootstrap_config() from the server,
+ but it only loads password storage scheme plugins
+ */
+static int
+slapd_config(const char *configdir)
+{
+ char configfile[MAXPATHLEN+1];
+ PRFileInfo prfinfo;
+ int rc = 0; /* Fail */
+ int done = 0;
+ PRInt32 nr = 0;
+ PRFileDesc *prfd = 0;
+ char *buf = 0;
+ char *lastp = 0;
+ char *entrystr = 0;
+
+ sprintf(configfile, "%s/%s", configdir, CONFIG_FILENAME);
+ if ( (rc = PR_GetFileInfo( configfile, &prfinfo )) != PR_SUCCESS )
+ {
+ fprintf(stderr,
+ "The given config file %s could not be accessed, error %d\n",
+ configfile, rc);
+ exit( 1 );
+ }
+ else if (( prfd = PR_Open( configfile, PR_RDONLY,
+ SLAPD_DEFAULT_FILE_MODE )) == NULL )
+ {
+ fprintf(stderr,
+ "The given config file %s could not be read\n",
+ configfile);
+ exit( 1 );
+ }
+ else
+ {
+ /* read the entire file into core */
+ buf = slapi_ch_malloc( prfinfo.size + 1 );
+ if (( nr = slapi_read_buffer( prfd, buf, prfinfo.size )) < 0 )
+ {
+ fprintf(stderr,
+ "Could only read %d of %d bytes from config file %s\n",
+ nr, prfinfo.size, configfile);
+ exit( 1 );
+ }
+
+ (void)PR_Close(prfd);
+ buf[ nr ] = '\0';
+
+ if(!done)
+ {
+ /* Convert LDIF to entry structures */
+ Slapi_DN plug_dn;
+ slapi_sdn_init_dn_byref(&plug_dn, PLUGIN_BASE_DN);
+ while ((entrystr = dse_read_next_entry(buf, &lastp)) != NULL)
+ {
+ /*
+ * XXXmcs: it would be better to also pass
+ * SLAPI_STR2ENTRY_REMOVEDUPVALS in the flags, but
+ * duplicate value checking requires that the syntax
+ * and schema subsystems be initialized... and they
+ * are not yet.
+ */
+ Slapi_Entry *e = slapi_str2entry(entrystr,
+ SLAPI_STR2ENTRY_NOT_WELL_FORMED_LDIF);
+ if (e == NULL)
+ {
+ fprintf(stderr,
+ "The entry [%s] in the configfile %s was empty or could not be parsed\n",
+ entrystr, configfile);
+ continue;
+ }
+
+ /* see if the entry is a child of the plugin base dn */
+ if (slapi_sdn_isgrandparent(&plug_dn,
+ slapi_entry_get_sdn_const(e)))
+ {
+ if ( entry_has_attr_and_value(e, ATTR_PLUGIN_TYPE, "pwdstoragescheme"))
+ {
+ /* add the syntax/matching/pwd storage scheme rule plugin */
+ if (plugin_setup(e, 0, 0, 1))
+ {
+ fprintf(stderr,
+ "The plugin entry [%s] in the configfile %s was invalid\n",
+ slapi_entry_get_dn(e), configfile);
+ exit(1); /* yes this sucks, but who knows what else would go on if I did the right thing */
+ }
+ else
+ {
+ e = 0; /* successful plugin_setup consumes entry */
+ }
+ }
+ }
+
+ if (e)
+ slapi_entry_free(e);
+ }
+
+ /* kexcoff: initialize rootpwstoragescheme and pw_storagescheme
+ * if not explicilty set in the config file
+ */
+ config_set_storagescheme();
+
+ slapi_sdn_done(&plug_dn);
+ rc= 1; /* OK */
+ }
+
+ slapi_ch_free((void **)&buf);
+ }
+
+ return rc;
+}
+
+/*
+ kexcoff: direclty copied fron the server code
+ See if the given entry has an attribute with the given name and the
+ given value; if value is NULL, just test for the presence of the given
+ attribute; if value is an empty string (i.e. value[0] == 0),
+ the first value in the attribute will be copied into the given buffer
+ and returned
+*/
+static int
+entry_has_attr_and_value(Slapi_Entry *e, const char *attrname,
+ char *value)
+{
+ int retval = 0;
+ Slapi_Attr *attr = 0;
+ if (!e || !attrname)
+ return retval;
+
+ /* see if the entry has the specified attribute name */
+ if (!slapi_entry_attr_find(e, attrname, &attr) && attr)
+ {
+ /* if value is not null, see if the attribute has that
+ value */
+ if (!value)
+ {
+ retval = 1;
+ }
+ else
+ {
+ Slapi_Value *v = 0;
+ int index = 0;
+ for (index = slapi_attr_first_value(attr, &v);
+ v && (index != -1);
+ index = slapi_attr_next_value(attr, index, &v))
+ {
+ const char *s = slapi_value_get_string(v);
+ if (!s)
+ continue;
+
+ if (!*value)
+ {
+ strcpy(value, s);
+ retval = 1;
+ break;
+ }
+ else if (!strcasecmp(s, value))
+ {
+ retval = 1;
+ break;
+ }
+ }
+ }
+ }
+
+ return retval;
+}
+
diff --git a/ldap/servers/slapd/unbind.c b/ldap/servers/slapd/unbind.c
new file mode 100644
index 00000000..5f7d9057
--- /dev/null
+++ b/ldap/servers/slapd/unbind.c
@@ -0,0 +1,83 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+/* unbind.c - decode an ldap unbind operation and pass it to a backend db */
+
+/*
+ * 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 <stdio.h>
+#include <sys/types.h>
+#ifndef _WIN32
+#include <sys/socket.h>
+#endif
+#include "slap.h"
+
+void
+do_unbind( Slapi_PBlock *pb )
+{
+ Slapi_Operation *operation;
+ BerElement *ber;
+ int err;
+
+ LDAPDebug( LDAP_DEBUG_TRACE, "do_unbind\n", 0, 0, 0 );
+
+ slapi_pblock_get( pb, SLAPI_OPERATION, &operation);
+ ber = operation->o_ber;
+ /*
+ * Parse the unbind request. It looks like this:
+ *
+ * UnBindRequest ::= NULL
+ */
+ if ( ber_get_null( ber ) == LBER_ERROR ) {
+ slapi_log_access( LDAP_DEBUG_STATS, "conn=%d op=%d UNBIND,"
+ " decoding error: UnBindRequest not null\n",
+ pb->pb_conn->c_connid, operation->o_opid );
+ /* LDAPv3 does not allow a response to an unbind... so just return. */
+ goto free_and_return;
+ }
+
+ /*
+ * in LDAPv3 there can be optional control extensions on
+ * the end of an LDAPMessage. we need to read them in and
+ * pass them to the backend.
+ */
+ if ( (err = get_ldapmessage_controls( pb, ber, NULL )) != 0 ) {
+ slapi_log_access( LDAP_DEBUG_STATS, "conn=%d op=%d UNBIND,"
+ " error processing controls - error %d (%s)\n",
+ pb->pb_conn->c_connid, operation->o_opid,
+ err, ldap_err2string( err ));
+ /* LDAPv3 does not allow a response to an unbind... so just return. */
+ goto free_and_return;
+ }
+
+ /* target spec is used to decide which plugins are applicable for the operation */
+ PR_Lock( pb->pb_conn->c_mutex );
+ operation_set_target_spec_str (operation, pb->pb_conn->c_dn);
+ PR_Unlock( pb->pb_conn->c_mutex );
+
+ /* ONREPL - plugins should be called and passed bind dn and, possibly, other data */
+
+ slapi_log_access( LDAP_DEBUG_STATS, "conn=%d op=%d UNBIND\n",
+ pb->pb_conn->c_connid, operation->o_opid );
+
+ /* pass the unbind to all backends */
+ be_unbindall( pb->pb_conn, operation );
+
+ /* close the connection to the client */
+ disconnect_server( pb->pb_conn, operation->o_connid, operation->o_opid, SLAPD_DISCONNECT_UNBIND, 0);
+
+free_and_return:;
+}
diff --git a/ldap/servers/slapd/uniqueid.c b/ldap/servers/slapd/uniqueid.c
new file mode 100644
index 00000000..0c7439f0
--- /dev/null
+++ b/ldap/servers/slapd/uniqueid.c
@@ -0,0 +1,295 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+/* uniqueid.c implementation of entryid functionality */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <ctype.h>
+
+#include "slap.h"
+
+#define UIDSTR_SIZE 35 /* size of the string representation of the id */
+#define MODULE "uniqueid" /* for logging */
+
+static int isValidFormat (const char * buff);
+static PRUint8 str2Byte (const char *str);
+
+/* All functions that strat with slapi_ are exposed to the plugins */
+
+/* Function: slapi_uniqueIDNew
+ Description: creates new Slapi_UniqueID object
+ Parameters: none
+ Return: pointer to the new uId object if successful
+ NULL if memory allocation failed
+ */
+
+Slapi_UniqueID *slapi_uniqueIDNew ()
+{
+ Slapi_UniqueID *uId;
+ uId = (Slapi_UniqueID*)slapi_ch_malloc (sizeof (Slapi_UniqueID));
+
+ if (uId == NULL)
+ {
+ slapi_log_error (SLAPI_LOG_FATAL, MODULE, "uniqueIDNew: "
+ "failed to allocate new id.\n");
+ return NULL;
+ }
+
+ memset (uId, 0, sizeof (Slapi_UniqueID));
+
+ return uId;
+}
+
+/* Function: slapi_uniqueIDDestroy
+ Description: destroys Slapi_UniqueID objects and sets the pointer to NULL
+ Parameters: uId - id to destroy
+ Return: none
+ */
+
+void slapi_uniqueIDDestroy (Slapi_UniqueID **uId)
+{
+ if (uId && *uId)
+ {
+ slapi_ch_free ((void**)uId);
+ *uId = NULL;
+ }
+}
+
+/* Function: slapi_uniqueIDCompare
+ Description: this function lexically compares two entry ids.
+ both Ids must have UUID type.
+ Parameters: uId1, uId2 - ids to compare
+ Return: -1 if uId1 < uId2
+ 0 if uId2 == uId2
+ 1 if uId2 > uId2
+ UID_BADDATA if invalid pointer passed to the function
+*/
+int slapi_uniqueIDCompare (const Slapi_UniqueID *uId1, const Slapi_UniqueID *uId2){
+ if (uId1 == NULL || uId2 == NULL)
+ {
+ slapi_log_error (SLAPI_LOG_FATAL, MODULE, "uniqueIDCompare: "
+ "NULL argument passed to the function.\n");
+ return UID_BADDATA;
+ }
+
+ return(uuid_compare(uId1, uId2));
+}
+
+/* Function: slapi_uniqueIDCompareString
+ Description: this function compares two uniqueids, represented as strings
+ Parameters: uuid1, uuid2 - ids to compare
+ Return: 0 if uuid1 == uuid2
+ non-zero if uuid1 != uuid2 or uuid1 == NULL or uuid2 == NULL
+*/
+int slapi_uniqueIDCompareString(const char *uuid1, const char *uuid2)
+{
+ int return_value = 0; /* assume not equal */
+ if (NULL != uuid1)
+ {
+ if (NULL != uuid2)
+ {
+ if (strcmp(uuid1, uuid2) == 0)
+ {
+ return_value = 1;
+ }
+ }
+ }
+ return return_value;
+}
+
+/* Function: slapi_uniqueIDFormat
+ Description: this function converts Slapi_UniqueID to its string representation.
+ The id format is HHHHHHHH-HHHHHHHH-HHHHHHHH-HHHHHHHH
+ where H is a hex digit. The data will be outputed in the
+ network byte order.
+ Parameters: uId - entry id
+ buff - buffer in which id is returned; caller must free this
+ buffer
+ Return: UID_SUCCESS - function was successfull
+ UID_BADDATA - invalid parameter passed to the function
+ UID_MEMORY_ERROR - failed to allocate the buffer
+*/
+int slapi_uniqueIDFormat (const Slapi_UniqueID *uId, char **buff){
+ guid_t uuid_tmp;
+
+ if (uId == NULL || buff == NULL)
+ {
+ slapi_log_error (SLAPI_LOG_FATAL, MODULE, "uniqueIDFormat: "
+ "NULL argument passed to the function.\n");
+ return UID_BADDATA;
+ }
+
+ *buff = (char*)slapi_ch_malloc (UIDSTR_SIZE + 1);
+ if (*buff == NULL)
+ {
+ slapi_log_error (SLAPI_LOG_FATAL, MODULE, "uniqueIDFormat: "
+ "failed to allocate buffer.\n");
+ return UID_MEMORY_ERROR;
+ }
+
+ uuid_tmp = *uId;
+ uuid_tmp.time_low = htonl(uuid_tmp.time_low);
+ uuid_tmp.time_mid = htons(uuid_tmp.time_mid);
+ uuid_tmp.time_hi_and_version = htons(uuid_tmp.time_hi_and_version);
+
+ sprintf (*buff, "%2.2x%2.2x%2.2x%2.2x-%2.2x%2.2x%2.2x%2.2x-"
+ "%2.2x%2.2x%2.2x%2.2x-%2.2x%2.2x%2.2x%2.2x",
+ ((PRUint8 *) &uuid_tmp.time_low)[0], ((PRUint8 *) &uuid_tmp.time_low)[1],
+ ((PRUint8 *) &uuid_tmp.time_low)[2], ((PRUint8 *) &uuid_tmp.time_low)[3],
+ ((PRUint8 *) &uuid_tmp.time_mid)[0], ((PRUint8 *) &uuid_tmp.time_mid)[1],
+ ((PRUint8 *) &uuid_tmp.time_hi_and_version)[0],
+ ((PRUint8 *) &uuid_tmp.time_hi_and_version)[1],
+ uuid_tmp.clock_seq_hi_and_reserved, uuid_tmp.clock_seq_low,
+ uuid_tmp.node[0], uuid_tmp.node[1], uuid_tmp.node[2],
+ uuid_tmp.node[3], uuid_tmp.node[4], uuid_tmp.node[5]);
+
+ return UID_SUCCESS;
+}
+
+/* Function: slapi_uniqueIDScan
+ Description: this function converts a string buffer into uniqueID.
+ Parameters: uId - unique id to be returned
+ buff - buffer with uniqueID in the format returned by
+ uniqueIDFormat function
+ Return: UID_SUCCESS - function was successfull
+ UID_BADDATA - null parameter(s) or bad format
+*/
+int slapi_uniqueIDScan (Slapi_UniqueID *uId, const char *buff){
+ if (uId == NULL || buff == NULL)
+ {
+ slapi_log_error (SLAPI_LOG_FATAL, MODULE, "uniqueIDScan: "
+ "NULL argument passed to the function.\n");
+ return UID_BADDATA;
+ }
+
+ if (!isValidFormat (buff))
+ {
+ slapi_log_error (SLAPI_LOG_FATAL, MODULE, "uniqueIDScan: "
+ "invalid data format.\n");
+ return UID_BADDATA;
+ }
+
+ ((PRUint8 *) &uId->time_low)[0] = str2Byte (&(buff[0]));
+ ((PRUint8 *) &uId->time_low)[1] = str2Byte (&(buff[2]));
+ ((PRUint8 *) &uId->time_low)[2] = str2Byte (&(buff[4]));
+ ((PRUint8 *) &uId->time_low)[3] = str2Byte (&(buff[6]));
+ /* next field is at 9 because we skip the - */
+ ((PRUint8 *) &uId->time_mid)[0] = str2Byte (&(buff[9]));
+ ((PRUint8 *) &uId->time_mid)[1] = str2Byte (&(buff[11]));
+ ((PRUint8 *) &uId->time_hi_and_version)[0] = str2Byte (&(buff[13]));
+ ((PRUint8 *) &uId->time_hi_and_version)[1] = str2Byte (&(buff[15]));
+ /* next field is at 18 because we skip the - */
+ uId->clock_seq_hi_and_reserved = str2Byte (&(buff[18]));
+ uId->clock_seq_low = str2Byte (&(buff[20]));
+ uId->node[0] = str2Byte (&(buff[22]));
+ uId->node[1] = str2Byte (&(buff[24]));
+ /* next field is at 27 because we skip the - */
+ uId->node[2] = str2Byte (&(buff[27]));
+ uId->node[3] = str2Byte (&(buff[29]));
+ uId->node[4] = str2Byte (&(buff[31]));
+ uId->node[5] = str2Byte (&(buff[33]));
+
+ uId->time_low = ntohl(uId->time_low);
+ uId->time_mid = ntohs(uId->time_mid);
+ uId->time_hi_and_version = ntohs(uId->time_hi_and_version);
+
+ return UID_SUCCESS;
+}
+
+/* Function: slapi_uniqueIDIsUUID
+ Description: tests if given entry id is in UUID format
+ Parameters: uId - id to test
+ Return 0 - it is UUID
+ 1 - it is not UUID
+ UID_BADDATA - invalid data passed to the function
+ Note: LPXXX - This call is not used currently. Keep it ???
+ */
+int slapi_uniqueIDIsUUID (const Slapi_UniqueID *uId){
+ if (uId == NULL)
+ return UID_BADDATA;
+ /* Shortening Slapi_UniqueID: This call does nothing */
+ return (0);
+}
+
+/* Name: slapi_uniqueIDSize
+ Description: returns size of the string version of uniqueID in bytes
+ Parameters: none
+ Return: size of the string version of uniqueID in bytes
+ */
+int slapi_uniqueIDSize ()
+{
+ return (UIDSTR_SIZE);
+}
+
+/* Name: slapi_uniqueIDDup
+ Description: duplicates an UniqueID object
+ Parameters: uId - id to duplicate
+ Return: duplicate of the Id
+ */
+Slapi_UniqueID* slapi_uniqueIDDup (Slapi_UniqueID *uId)
+{
+ Slapi_UniqueID *uIdDup = slapi_uniqueIDNew ();
+ memcpy (uIdDup, uId, sizeof (Slapi_UniqueID));
+
+ return uIdDup;
+}
+
+/* helper functions */
+
+static char hexDigits[] = {'0', '1', '2', '3', '4', '5', '6', '7',
+ '8', '9', 'A', 'B', 'C', 'D', 'E', 'F', '\0'};
+/* this function converts a string representation of a byte in hex into
+ an actual byte. For instance: "AB" -> 171 */
+static PRUint8 str2Byte (const char *str)
+{
+ char letter1 = str[0];
+ char letter2 = str[1];
+ PRUint8 num = 0;
+ int i = 0;
+
+ while (hexDigits[i] != '\0')
+ {
+ if (letter1 == hexDigits[i] || toupper (letter1) == hexDigits[i])
+ {
+ num |= (i << 4);
+ }
+
+ if (letter2 == hexDigits[i] || toupper (letter2) == hexDigits[i])
+ {
+ num |= i;
+ }
+
+ i++;
+ }
+
+ return num;
+}
+
+static char* format = "XXXXXXXX-XXXXXXXX-XXXXXXXX-XXXXXXXX";
+/* This function verifies that buff contains data in the correct
+ format (specified above). */
+static int isValidFormat (const char * buff)
+{
+ int len;
+ int i;
+
+ if (strlen (buff) != strlen (format))
+ return UID_BADDATA;
+
+ len = strlen (format);
+
+ for (i = 0; i < len; i++)
+ {
+ if (format[i] == '-' && buff [i] != '-')
+ return 0;
+ else if (format[i] == 'X' && ! isxdigit (buff[i]))
+ return 0;
+ }
+
+ return 1;
+}
+
diff --git a/ldap/servers/slapd/uniqueidgen.c b/ldap/servers/slapd/uniqueidgen.c
new file mode 100644
index 00000000..1765b78d
--- /dev/null
+++ b/ldap/servers/slapd/uniqueidgen.c
@@ -0,0 +1,219 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+/* uniqueidgen.c - implementation for uniqueID generator */
+
+#include <string.h>
+
+#ifndef _WIN32 /* for ntoh* functions */
+#include <sys/types.h>
+#include <sys/time.h>
+#include <sys/sysinfo.h>
+#include <sys/utsname.h>
+#endif
+
+#include "nspr.h"
+#include "slap.h"
+#include "uuid.h"
+
+#define MODULE "uniqueid generator"
+
+/* converts from guid -> UniqueID */
+/* static void uuid2UniqueID (const guid_t *uuid, Slapi_UniqueID *uId); */
+/* converts from UniqueID -> guid */
+/* static void uniqueID2uuid (const Slapi_UniqueID *uId, guid_t *uuid); */
+/* validates directory */
+static int validDir (const char *configDir);
+
+/* Function: uniqueIDGenInit
+ Description: this function initializes the generator
+ Parameters: configDir - directory in which generators state is stored
+ configDN - DIT entry with state information
+ mtGen - indicates whether multiple threads will use generator
+ Return: UID_SUCCESS if function succeeds
+ UID_BADDATA if invalif directory is passed
+ UID_SYSTEM_ERROR if any other failure occurs
+*/
+int uniqueIDGenInit (const char *configDir, const Slapi_DN *configDN, PRBool mtGen)
+{
+ int rt;
+ if ((configDN == NULL && (configDir == NULL || !validDir(configDir))) ||
+ (configDN && configDir))
+ {
+ slapi_log_error (SLAPI_LOG_FATAL, MODULE, "uniqueIDGenInit: invalid arguments\n");
+
+ return UID_BADDATA;
+ }
+
+ rt = uuid_init (configDir, configDN, mtGen);
+
+ if (rt == UUID_SUCCESS)
+ return UID_SUCCESS;
+ else
+ {
+ slapi_log_error (SLAPI_LOG_FATAL, MODULE, "uniqueIDGenInit: "
+ "generator initialization failed\n");
+ return UID_SYSTEM_ERROR;
+ }
+}
+
+/* Function: uniqueIDGenCleanup
+ Description: cleanup
+ Parameters: none
+ Return: none
+*/
+void uniqueIDGenCleanup (){
+ uuid_cleanup ();
+}
+
+/* Function: slapi_uniqueIDGenerate
+ Description: this function generates UniqueID; exposed to the plugins.
+ Parameters: uId - structure in which new id will be return
+ Return: UID_SUCCESS, if operation is successful
+ UID_BADDATA, if null pointer is passed to the function
+ UID_SYSTEM_ERROR, if update to persistent storage failed
+*/
+
+int slapi_uniqueIDGenerate (Slapi_UniqueID *uId){
+ int rt;
+
+ if (uId == NULL)
+ {
+ slapi_log_error (SLAPI_LOG_FATAL, MODULE, "uniqueIDGenerate: "
+ "NULL paramter is passed to the function.\n");
+ return UID_BADDATA;
+ }
+
+ rt = uuid_create(uId);
+ if (rt != UUID_SUCCESS)
+ {
+ slapi_log_error (SLAPI_LOG_FATAL, MODULE, "uniqueIDGenerate: "
+ "id generation failed.\n");
+ return UID_SYSTEM_ERROR;
+ }
+ return UID_SUCCESS;
+}
+
+/* Function: slapi_uniqueIDGenerateString
+ Description: this function generates uniqueid an returns it as a string
+ This function returns the data in the format generated by
+ slapi_uniqueIDFormat.
+ Parameters: uId - buffer to receive the ID. Caller is responsible for
+ freeing uId buffer.
+ Return: UID_SUCCESS if function succeeds;
+ UID_BADDATA if invalid pointer passed to the function;
+ UID_MEMORY_ERROR if malloc fails;
+ UID_SYSTEM_ERROR update to persistent storage failed.
+*/
+
+int slapi_uniqueIDGenerateString (char **uId)
+{
+ Slapi_UniqueID uIdTemp;
+ int rc;
+
+ rc = slapi_uniqueIDGenerate (&uIdTemp);
+
+ if (rc != UID_SUCCESS)
+ return rc;
+
+ rc = slapi_uniqueIDFormat (&uIdTemp, uId);
+
+ return rc;
+}
+
+/* Function: slapi_uniqueIDGenerateFromName
+ Description: this function generates an id from name. See uuid
+ draft for more details. This function is thread safe.
+ Parameters: uId - generated id
+ uIDBase - uid used for generation to distinguish different
+ name - buffer containing name from which to generate the id
+ namelen - length of the name buffer
+ name spaces
+ Return: UID_SUCCESS if function succeeds
+ UID_BADDATA if invalid argument is passed to the
+ function.
+*/
+
+int slapi_uniqueIDGenerateFromName (Slapi_UniqueID *uId, const Slapi_UniqueID *uIdBase,
+ const void *name, int namelen)
+{
+ if (uId == NULL || uIdBase == NULL || name == NULL || namelen <= 0)
+ {
+ slapi_log_error (SLAPI_LOG_FATAL, MODULE, "uniqueIDGenerateMT: "
+ "invalid paramter is passed to the function.\n");
+ return UID_BADDATA;
+ }
+
+ uuid_create_from_name(uId, *uIdBase, name, namelen);
+
+ return UID_SUCCESS;
+}
+
+/* Function: slapi_uniqueIDGenerateFromName
+ Description: this function generates an id from a name and returns
+ it in the string format. See uuid draft for more
+ details. This function can be used in both a
+ singlethreaded and a multithreaded environments.
+ Parameters: uId - generated id in string form
+ uIDBase - uid used for generation to distinguish among
+ different name spaces in string form; NULL means to use
+ empty id as the base.
+ name - buffer containing name from which to generate the id
+ namelen - length of the name buffer
+ Return: UID_SUCCESS if function succeeds
+ UID_BADDATA if invalid argument is passed to the
+ function.
+*/
+
+int slapi_uniqueIDGenerateFromNameString (char **uId,
+ const char *uIdBase,
+ const void *name, int namelen)
+{
+ int rc;
+ Slapi_UniqueID idBase;
+ Slapi_UniqueID idGen;
+
+ /* just use Id of all 0 as base id */
+ if (uIdBase == NULL)
+ {
+ memset (&idBase, 0, sizeof (idBase));
+ memset (&idGen, 0, sizeof (idGen));
+ }
+ else
+ {
+ rc = slapi_uniqueIDScan (&idBase, uIdBase);
+ if (rc != UID_SUCCESS)
+ {
+ return rc;
+ }
+ }
+
+ rc = slapi_uniqueIDGenerateFromName (&idGen, &idBase, name, namelen);
+ if (rc != UID_SUCCESS)
+ {
+ return rc;
+ }
+
+ rc = slapi_uniqueIDFormat (&idGen, uId);
+
+ return rc;
+}
+
+/* helper fumctions */
+
+static int validDir(const char *configDir){
+ PRDir* dir;
+
+ /* empty string means this directory */
+ if (strlen(configDir) == 0)
+ return 1;
+ dir = PR_OpenDir(configDir);
+ if (dir){
+ PR_CloseDir (dir);
+ return 1;
+ }
+
+ return 0;
+}
diff --git a/ldap/servers/slapd/utf8compare.c b/ldap/servers/slapd/utf8compare.c
new file mode 100644
index 00000000..f011472a
--- /dev/null
+++ b/ldap/servers/slapd/utf8compare.c
@@ -0,0 +1,2287 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+#include <stdio.h>
+#include <string.h>
+#include <ctype.h>
+#include "ldap.h"
+#include "slap.h"
+#include "slapi-plugin.h"
+
+typedef struct sUpperLowerTbl {
+ char *upper, *lower;
+ int tsz; /* target size */
+} UpperLowerTbl_t;
+
+/*
+ * slapi_has8thBit: check the input string
+ * return 1 if the string contains 8-bit character
+ * return 0 otherwise
+ */
+int
+slapi_has8thBit(unsigned char *s)
+{
+ unsigned char *p, *tail;
+ tail = s + strlen((char *)s);
+ for (p = s; p < tail; p++) {
+ if (0x80 & *p) {
+ return 1;
+ }
+ }
+ return 0;
+}
+
+/*
+ * UpperToLower Tables: sorted by upper characters
+ */
+UpperLowerTbl_t Upper2LowerTbl20[] = {
+ /* upper, lower */
+ {"\303\200", "\303\240", 2},
+ {"\303\201", "\303\241", 2},
+ {"\303\202", "\303\242", 2},
+ {"\303\203", "\303\243", 2},
+ {"\303\204", "\303\244", 2},
+ {"\303\205", "\303\245", 2},
+ {"\303\206", "\303\246", 2},
+ {"\303\207", "\303\247", 2},
+ {"\303\210", "\303\250", 2},
+ {"\303\211", "\303\251", 2},
+ {"\303\212", "\303\252", 2},
+ {"\303\213", "\303\253", 2},
+ {"\303\214", "\303\254", 2},
+ {"\303\215", "\303\255", 2},
+ {"\303\216", "\303\256", 2},
+ {"\303\217", "\303\257", 2},
+ {"\303\220", "\303\260", 2},
+ {"\303\221", "\303\261", 2},
+ {"\303\222", "\303\262", 2},
+ {"\303\223", "\303\263", 2},
+ {"\303\224", "\303\264", 2},
+ {"\303\225", "\303\265", 2},
+ {"\303\226", "\303\266", 2},
+ {"\303\230", "\303\270", 2},
+ {"\303\231", "\303\271", 2},
+ {"\303\232", "\303\272", 2},
+ {"\303\233", "\303\273", 2},
+ {"\303\234", "\303\274", 2},
+ {"\303\235", "\303\275", 2},
+ {"\303\236", "\303\276", 2},
+ {NULL, NULL, 0}
+};
+
+UpperLowerTbl_t Upper2LowerTbl21[] = {
+ {"\304\200", "\304\201", 2},
+ {"\304\202", "\304\203", 2},
+ {"\304\204", "\304\205", 2},
+ {"\304\206", "\304\207", 2},
+ {"\304\210", "\304\211", 2},
+ {"\304\212", "\304\213", 2},
+ {"\304\214", "\304\215", 2},
+ {"\304\216", "\304\217", 2},
+ {"\304\220", "\304\221", 2},
+ {"\304\222", "\304\223", 2},
+ {"\304\224", "\304\225", 2},
+ {"\304\226", "\304\227", 2},
+ {"\304\230", "\304\231", 2},
+ {"\304\232", "\304\233", 2},
+ {"\304\234", "\304\235", 2},
+ {"\304\236", "\304\237", 2},
+ {"\304\240", "\304\241", 2},
+ {"\304\242", "\304\243", 2},
+ {"\304\244", "\304\245", 2},
+ {"\304\246", "\304\247", 2},
+ {"\304\250", "\304\251", 2},
+ {"\304\252", "\304\253", 2},
+ {"\304\254", "\304\255", 2},
+ {"\304\256", "\304\257", 2},
+ {"\304\260", "\151", 1},
+ {"\304\262", "\304\263", 2},
+ {"\304\264", "\304\265", 2},
+ {"\304\266", "\304\267", 2},
+ {"\304\271", "\304\272", 2},
+ {"\304\273", "\304\274", 2},
+ {"\304\275", "\304\276", 2},
+ {"\304\277", "\305\200", 2},
+ {NULL, NULL, 0}
+};
+
+UpperLowerTbl_t Upper2LowerTbl22[] = {
+ {"\305\201", "\305\202", 2},
+ {"\305\203", "\305\204", 2},
+ {"\305\205", "\305\206", 2},
+ {"\305\207", "\305\210", 2},
+ {"\305\212", "\305\213", 2},
+ {"\305\214", "\305\215", 2},
+ {"\305\216", "\305\217", 2},
+ {"\305\220", "\305\221", 2},
+ {"\305\222", "\305\223", 2},
+ {"\305\224", "\305\225", 2},
+ {"\305\226", "\305\227", 2},
+ {"\305\230", "\305\231", 2},
+ {"\305\232", "\305\233", 2},
+ {"\305\234", "\305\235", 2},
+ {"\305\236", "\305\237", 2},
+ {"\305\240", "\305\241", 2},
+ {"\305\242", "\305\243", 2},
+ {"\305\244", "\305\245", 2},
+ {"\305\246", "\305\247", 2},
+ {"\305\250", "\305\251", 2},
+ {"\305\252", "\305\253", 2},
+ {"\305\254", "\305\255", 2},
+ {"\305\256", "\305\257", 2},
+ {"\305\260", "\305\261", 2},
+ {"\305\262", "\305\263", 2},
+ {"\305\264", "\305\265", 2},
+ {"\305\266", "\305\267", 2},
+ {"\305\270", "\303\277", 2},
+ {"\305\271", "\305\272", 2},
+ {"\305\273", "\305\274", 2},
+ {"\305\275", "\305\276", 2},
+ {NULL, NULL, 0}
+};
+
+UpperLowerTbl_t Upper2LowerTbl23[] = {
+ {"\306\201", "\311\223", 2},
+ {"\306\202", "\306\203", 2},
+ {"\306\204", "\306\205", 2},
+ {"\306\206", "\311\224", 2},
+ {"\306\207", "\306\210", 2},
+ {"\306\211", "\311\226", 2},
+ {"\306\212", "\311\227", 2},
+ {"\306\213", "\306\214", 2},
+ {"\306\216", "\311\230", 2},
+ {"\306\217", "\311\231", 2},
+ {"\306\220", "\311\233", 2},
+ {"\306\221", "\306\222", 2},
+ {"\306\223", "\311\240", 2},
+ {"\306\224", "\311\243", 2},
+ {"\306\226", "\311\251", 2},
+ {"\306\227", "\311\250", 2},
+ {"\306\230", "\306\231", 2},
+ {"\306\234", "\311\257", 2},
+ {"\306\235", "\311\262", 2},
+ {"\306\237", "\306\237", 2},
+ {"\306\240", "\306\241", 2},
+ {"\306\242", "\306\243", 2},
+ {"\306\244", "\306\245", 2},
+ {"\306\246", "\306\246", 2},
+ {"\306\247", "\306\250", 2},
+ {"\306\251", "\312\203", 2},
+ {"\306\254", "\306\255", 2},
+ {"\306\256", "\312\210", 2},
+ {"\306\257", "\306\260", 2},
+ {"\306\261", "\312\212", 2},
+ {"\306\262", "\312\213", 2},
+ {"\306\263", "\306\264", 2},
+ {"\306\265", "\306\266", 2},
+ {"\306\267", "\312\222", 2},
+ {"\306\270", "\306\271", 2},
+ {"\306\274", "\306\275", 2},
+ {NULL, NULL, 0}
+};
+
+UpperLowerTbl_t Upper2LowerTbl24[] = {
+ {"\307\204", "\307\205", 2},
+ {"\307\205", "\307\204", 2},
+ {"\307\207", "\307\210", 2},
+ {"\307\210", "\307\207", 2},
+ {"\307\212", "\307\213", 2},
+ {"\307\213", "\307\212", 2},
+ {"\307\215", "\307\216", 2},
+ {"\307\217", "\307\220", 2},
+ {"\307\221", "\307\222", 2},
+ {"\307\223", "\307\224", 2},
+ {"\307\225", "\307\226", 2},
+ {"\307\227", "\307\230", 2},
+ {"\307\231", "\307\232", 2},
+ {"\307\233", "\307\234", 2},
+ {"\307\236", "\307\237", 2},
+ {"\307\240", "\307\241", 2},
+ {"\307\242", "\307\243", 2},
+ {"\307\244", "\307\245", 2},
+ {"\307\246", "\307\247", 2},
+ {"\307\250", "\307\251", 2},
+ {"\307\252", "\307\253", 2},
+ {"\307\254", "\307\255", 2},
+ {"\307\256", "\307\257", 2},
+ {"\307\261", "\307\262", 2},
+ {"\307\262", "\307\261", 2},
+ {"\307\264", "\307\265", 2},
+ {"\307\272", "\307\273", 2},
+ {"\307\274", "\307\275", 2},
+ {"\307\276", "\307\277", 2},
+ {NULL, NULL, 0}
+};
+
+UpperLowerTbl_t Upper2LowerTbl25[] = {
+ {"\310\200", "\310\201", 2},
+ {"\310\202", "\310\203", 2},
+ {"\310\204", "\310\205", 2},
+ {"\310\206", "\310\207", 2},
+ {"\310\210", "\310\211", 2},
+ {"\310\212", "\310\213", 2},
+ {"\310\214", "\310\215", 2},
+ {"\310\216", "\310\217", 2},
+ {"\310\220", "\310\221", 2},
+ {"\310\222", "\310\223", 2},
+ {"\310\224", "\310\225", 2},
+ {"\310\226", "\310\227", 2},
+ {NULL, NULL, 0}
+};
+
+UpperLowerTbl_t Upper2LowerTbl26[] = {
+ {"\316\206", "\316\254", 2},
+ {"\316\210", "\316\255", 2},
+ {"\316\211", "\316\256", 2},
+ {"\316\212", "\316\257", 2},
+ {"\316\214", "\317\214", 2},
+ {"\316\216", "\317\215", 2},
+ {"\316\217", "\317\216", 2},
+ {"\316\221", "\316\261", 2},
+ {"\316\222", "\316\262", 2},
+ {"\316\223", "\316\263", 2},
+ {"\316\224", "\316\264", 2},
+ {"\316\225", "\316\265", 2},
+ {"\316\226", "\316\266", 2},
+ {"\316\227", "\316\267", 2},
+ {"\316\230", "\316\270", 2},
+ {"\316\231", "\316\271", 2},
+ {"\316\232", "\316\272", 2},
+ {"\316\233", "\316\273", 2},
+ {"\316\234", "\316\274", 2},
+ {"\316\235", "\316\275", 2},
+ {"\316\236", "\316\276", 2},
+ {"\316\237", "\316\277", 2},
+ {"\316\240", "\317\200", 2},
+ {"\316\241", "\317\201", 2},
+ {"\316\243", "\317\203", 2},
+ {"\316\244", "\317\204", 2},
+ {"\316\245", "\317\205", 2},
+ {"\316\246", "\317\206", 2},
+ {"\316\247", "\317\207", 2},
+ {"\316\250", "\317\210", 2},
+ {"\316\251", "\317\211", 2},
+ {"\316\252", "\317\212", 2},
+ {"\316\253", "\317\213", 2},
+ {NULL, NULL, 0}
+};
+
+UpperLowerTbl_t Upper2LowerTbl27[] = {
+ {"\317\222", "\317\222", 2},
+ {"\317\223", "\317\223", 2},
+ {"\317\224", "\317\224", 2},
+ {"\317\232", "\317\232", 2},
+ {"\317\234", "\317\234", 2},
+ {"\317\236", "\317\236", 2},
+ {"\317\240", "\317\240", 2},
+ {"\317\242", "\317\243", 2},
+ {"\317\244", "\317\245", 2},
+ {"\317\246", "\317\247", 2},
+ {"\317\250", "\317\251", 2},
+ {"\317\252", "\317\253", 2},
+ {"\317\254", "\317\255", 2},
+ {"\317\256", "\317\257", 2},
+ {NULL, NULL, 0}
+};
+
+UpperLowerTbl_t Upper2LowerTbl28[] = {
+ {"\320\201", "\321\221", 2},
+ {"\320\202", "\321\222", 2},
+ {"\320\203", "\321\223", 2},
+ {"\320\204", "\321\224", 2},
+ {"\320\205", "\321\225", 2},
+ {"\320\206", "\321\226", 2},
+ {"\320\207", "\321\227", 2},
+ {"\320\210", "\321\230", 2},
+ {"\320\211", "\321\231", 2},
+ {"\320\212", "\321\232", 2},
+ {"\320\213", "\321\233", 2},
+ {"\320\214", "\321\234", 2},
+ {"\320\216", "\321\236", 2},
+ {"\320\217", "\321\237", 2},
+ {"\320\220", "\320\260", 2},
+ {"\320\221", "\320\261", 2},
+ {"\320\222", "\320\262", 2},
+ {"\320\223", "\320\263", 2},
+ {"\320\224", "\320\264", 2},
+ {"\320\225", "\320\265", 2},
+ {"\320\226", "\320\266", 2},
+ {"\320\227", "\320\267", 2},
+ {"\320\230", "\320\270", 2},
+ {"\320\231", "\320\271", 2},
+ {"\320\232", "\320\272", 2},
+ {"\320\233", "\320\273", 2},
+ {"\320\234", "\320\274", 2},
+ {"\320\235", "\320\275", 2},
+ {"\320\236", "\320\276", 2},
+ {"\320\237", "\320\277", 2},
+ {"\320\240", "\321\200", 2},
+ {"\320\241", "\321\201", 2},
+ {"\320\242", "\321\202", 2},
+ {"\320\243", "\321\203", 2},
+ {"\320\244", "\321\204", 2},
+ {"\320\245", "\321\205", 2},
+ {"\320\246", "\321\206", 2},
+ {"\320\247", "\321\207", 2},
+ {"\320\250", "\321\210", 2},
+ {"\320\251", "\321\211", 2},
+ {"\320\252", "\321\212", 2},
+ {"\320\253", "\321\213", 2},
+ {"\320\254", "\321\214", 2},
+ {"\320\255", "\321\215", 2},
+ {"\320\256", "\321\216", 2},
+ {"\320\257", "\321\217", 2},
+ {NULL, NULL, 0}
+};
+
+UpperLowerTbl_t Upper2LowerTbl29[] = {
+ {"\321\240", "\321\241", 2},
+ {"\321\242", "\321\243", 2},
+ {"\321\244", "\321\245", 2},
+ {"\321\246", "\321\247", 2},
+ {"\321\250", "\321\251", 2},
+ {"\321\252", "\321\253", 2},
+ {"\321\254", "\321\255", 2},
+ {"\321\256", "\321\257", 2},
+ {"\321\260", "\321\261", 2},
+ {"\321\262", "\321\263", 2},
+ {"\321\264", "\321\265", 2},
+ {"\321\266", "\321\267", 2},
+ {"\321\270", "\321\271", 2},
+ {"\321\272", "\321\273", 2},
+ {"\321\274", "\321\275", 2},
+ {"\321\276", "\321\277", 2},
+ {NULL, NULL, 0}
+};
+
+UpperLowerTbl_t Upper2LowerTbl2a[] = {
+ {"\322\200", "\322\201", 2},
+ {"\322\220", "\322\221", 2},
+ {"\322\222", "\322\223", 2},
+ {"\322\224", "\322\225", 2},
+ {"\322\226", "\322\227", 2},
+ {"\322\230", "\322\231", 2},
+ {"\322\232", "\322\233", 2},
+ {"\322\234", "\322\235", 2},
+ {"\322\236", "\322\237", 2},
+ {"\322\240", "\322\241", 2},
+ {"\322\242", "\322\243", 2},
+ {"\322\244", "\322\245", 2},
+ {"\322\246", "\322\247", 2},
+ {"\322\250", "\322\251", 2},
+ {"\322\252", "\322\253", 2},
+ {"\322\254", "\322\255", 2},
+ {"\322\256", "\322\257", 2},
+ {"\322\260", "\322\261", 2},
+ {"\322\262", "\322\263", 2},
+ {"\322\264", "\322\265", 2},
+ {"\322\266", "\322\267", 2},
+ {"\322\270", "\322\271", 2},
+ {"\322\272", "\322\273", 2},
+ {"\322\274", "\322\275", 2},
+ {"\322\276", "\322\277", 2},
+ {NULL, NULL, 0}
+};
+
+UpperLowerTbl_t Upper2LowerTbl2b[] = {
+ {"\323\201", "\323\202", 2},
+ {"\323\203", "\323\204", 2},
+ {"\323\207", "\323\210", 2},
+ {"\323\213", "\323\214", 2},
+ {"\323\220", "\323\221", 2},
+ {"\323\222", "\323\223", 2},
+ {"\323\224", "\323\225", 2},
+ {"\323\226", "\323\227", 2},
+ {"\323\230", "\323\231", 2},
+ {"\323\232", "\323\233", 2},
+ {"\323\234", "\323\235", 2},
+ {"\323\236", "\323\237", 2},
+ {"\323\240", "\323\241", 2},
+ {"\323\242", "\323\243", 2},
+ {"\323\244", "\323\245", 2},
+ {"\323\246", "\323\247", 2},
+ {"\323\250", "\323\251", 2},
+ {"\323\252", "\323\253", 2},
+ {"\323\256", "\323\257", 2},
+ {"\323\260", "\323\261", 2},
+ {"\323\262", "\323\263", 2},
+ {"\323\264", "\323\265", 2},
+ {"\323\270", "\323\271", 2},
+ {NULL, NULL, 0}
+};
+
+UpperLowerTbl_t Upper2LowerTbl2c[] = {
+ {"\324\261", "\325\241", 2},
+ {"\324\262", "\325\242", 2},
+ {"\324\263", "\325\243", 2},
+ {"\324\264", "\325\244", 2},
+ {"\324\265", "\325\245", 2},
+ {"\324\266", "\325\246", 2},
+ {"\324\267", "\325\247", 2},
+ {"\324\270", "\325\250", 2},
+ {"\324\271", "\325\251", 2},
+ {"\324\272", "\325\252", 2},
+ {"\324\273", "\325\253", 2},
+ {"\324\274", "\325\254", 2},
+ {"\324\275", "\325\255", 2},
+ {"\324\276", "\325\256", 2},
+ {"\324\277", "\325\257", 2},
+ {NULL, NULL, 0}
+};
+
+UpperLowerTbl_t Upper2LowerTbl2d[] = {
+ {"\325\200", "\325\260", 2},
+ {"\325\201", "\325\261", 2},
+ {"\325\202", "\325\262", 2},
+ {"\325\203", "\325\263", 2},
+ {"\325\204", "\325\264", 2},
+ {"\325\205", "\325\265", 2},
+ {"\325\206", "\325\266", 2},
+ {"\325\207", "\325\267", 2},
+ {"\325\210", "\325\270", 2},
+ {"\325\211", "\325\271", 2},
+ {"\325\212", "\325\272", 2},
+ {"\325\213", "\325\273", 2},
+ {"\325\214", "\325\274", 2},
+ {"\325\215", "\325\275", 2},
+ {"\325\216", "\325\276", 2},
+ {"\325\217", "\325\277", 2},
+ {"\325\220", "\326\200", 2},
+ {"\325\221", "\326\201", 2},
+ {"\325\222", "\326\202", 2},
+ {"\325\223", "\326\203", 2},
+ {"\325\224", "\326\204", 2},
+ {"\325\225", "\326\205", 2},
+ {"\325\226", "\326\206", 2},
+ {NULL, NULL, 0}
+ /* upper, lower */
+};
+
+UpperLowerTbl_t Upper2LowerTbl30[] = {
+ /* upper, lower */
+ {"\341\202\240", "\341\203\220", 3},
+ {"\341\202\241", "\341\203\221", 3},
+ {"\341\202\242", "\341\203\222", 3},
+ {"\341\202\243", "\341\203\223", 3},
+ {"\341\202\244", "\341\203\224", 3},
+ {"\341\202\245", "\341\203\225", 3},
+ {"\341\202\246", "\341\203\226", 3},
+ {"\341\202\247", "\341\203\227", 3},
+ {"\341\202\250", "\341\203\230", 3},
+ {"\341\202\251", "\341\203\231", 3},
+ {"\341\202\252", "\341\203\232", 3},
+ {"\341\202\253", "\341\203\233", 3},
+ {"\341\202\254", "\341\203\234", 3},
+ {"\341\202\255", "\341\203\235", 3},
+ {"\341\202\256", "\341\203\236", 3},
+ {"\341\202\257", "\341\203\237", 3},
+ {"\341\202\260", "\341\203\240", 3},
+ {"\341\202\261", "\341\203\241", 3},
+ {"\341\202\262", "\341\203\242", 3},
+ {"\341\202\263", "\341\203\243", 3},
+ {"\341\202\264", "\341\203\244", 3},
+ {"\341\202\265", "\341\203\245", 3},
+ {"\341\202\266", "\341\203\246", 3},
+ {"\341\202\267", "\341\203\247", 3},
+ {"\341\202\270", "\341\203\250", 3},
+ {"\341\202\271", "\341\203\251", 3},
+ {"\341\202\272", "\341\203\252", 3},
+ {"\341\202\273", "\341\203\253", 3},
+ {"\341\202\274", "\341\203\254", 3},
+ {"\341\202\275", "\341\203\255", 3},
+ {"\341\202\276", "\341\203\256", 3},
+ {"\341\202\277", "\341\203\257", 3},
+ {"\341\203\200", "\341\203\260", 3},
+ {"\341\203\201", "\341\203\261", 3},
+ {"\341\203\202", "\341\203\262", 3},
+ {"\341\203\203", "\341\203\263", 3},
+ {"\341\203\204", "\341\203\264", 3},
+ {"\341\203\205", "\341\203\265", 3},
+ {"\341\270\200", "\341\270\201", 3},
+ {"\341\270\202", "\341\270\203", 3},
+ {"\341\270\204", "\341\270\205", 3},
+ {"\341\270\206", "\341\270\207", 3},
+ {"\341\270\210", "\341\270\211", 3},
+ {"\341\270\212", "\341\270\213", 3},
+ {"\341\270\214", "\341\270\215", 3},
+ {"\341\270\216", "\341\270\217", 3},
+ {"\341\270\220", "\341\270\221", 3},
+ {"\341\270\222", "\341\270\223", 3},
+ {"\341\270\224", "\341\270\225", 3},
+ {"\341\270\226", "\341\270\227", 3},
+ {"\341\270\230", "\341\270\231", 3},
+ {"\341\270\232", "\341\270\233", 3},
+ {"\341\270\234", "\341\270\235", 3},
+ {"\341\270\236", "\341\270\237", 3},
+ {"\341\270\240", "\341\270\241", 3},
+ {"\341\270\242", "\341\270\243", 3},
+ {"\341\270\244", "\341\270\245", 3},
+ {"\341\270\246", "\341\270\247", 3},
+ {"\341\270\250", "\341\270\251", 3},
+ {"\341\270\252", "\341\270\253", 3},
+ {"\341\270\254", "\341\270\255", 3},
+ {"\341\270\256", "\341\270\257", 3},
+ {"\341\270\260", "\341\270\261", 3},
+ {"\341\270\262", "\341\270\263", 3},
+ {"\341\270\264", "\341\270\265", 3},
+ {"\341\270\266", "\341\270\267", 3},
+ {"\341\270\270", "\341\270\271", 3},
+ {"\341\270\272", "\341\270\273", 3},
+ {"\341\270\274", "\341\270\275", 3},
+ {"\341\270\276", "\341\270\277", 3},
+ {"\341\271\200", "\341\271\201", 3},
+ {"\341\271\202", "\341\271\203", 3},
+ {"\341\271\204", "\341\271\205", 3},
+ {"\341\271\206", "\341\271\207", 3},
+ {"\341\271\210", "\341\271\211", 3},
+ {"\341\271\212", "\341\271\213", 3},
+ {"\341\271\214", "\341\271\215", 3},
+ {"\341\271\216", "\341\271\217", 3},
+ {"\341\271\220", "\341\271\221", 3},
+ {"\341\271\222", "\341\271\223", 3},
+ {"\341\271\224", "\341\271\225", 3},
+ {"\341\271\226", "\341\271\227", 3},
+ {"\341\271\230", "\341\271\231", 3},
+ {"\341\271\232", "\341\271\233", 3},
+ {"\341\271\234", "\341\271\235", 3},
+ {"\341\271\236", "\341\271\237", 3},
+ {"\341\271\240", "\341\271\241", 3},
+ {"\341\271\242", "\341\271\243", 3},
+ {"\341\271\244", "\341\271\245", 3},
+ {"\341\271\246", "\341\271\247", 3},
+ {"\341\271\250", "\341\271\251", 3},
+ {"\341\271\252", "\341\271\253", 3},
+ {"\341\271\254", "\341\271\255", 3},
+ {"\341\271\256", "\341\271\257", 3},
+ {"\341\271\260", "\341\271\261", 3},
+ {"\341\271\262", "\341\271\263", 3},
+ {"\341\271\264", "\341\271\265", 3},
+ {"\341\271\266", "\341\271\267", 3},
+ {"\341\271\270", "\341\271\271", 3},
+ {"\341\271\272", "\341\271\273", 3},
+ {"\341\271\274", "\341\271\275", 3},
+ {"\341\271\276", "\341\271\277", 3},
+ {"\341\272\200", "\341\272\201", 3},
+ {"\341\272\202", "\341\272\203", 3},
+ {"\341\272\204", "\341\272\205", 3},
+ {"\341\272\206", "\341\272\207", 3},
+ {"\341\272\210", "\341\272\211", 3},
+ {"\341\272\212", "\341\272\213", 3},
+ {"\341\272\214", "\341\272\215", 3},
+ {"\341\272\216", "\341\272\217", 3},
+ {"\341\272\220", "\341\272\221", 3},
+ {"\341\272\222", "\341\272\223", 3},
+ {"\341\272\224", "\341\272\225", 3},
+ {"\341\272\240", "\341\272\241", 3},
+ {"\341\272\242", "\341\272\243", 3},
+ {"\341\272\244", "\341\272\245", 3},
+ {"\341\272\246", "\341\272\247", 3},
+ {"\341\272\250", "\341\272\251", 3},
+ {"\341\272\252", "\341\272\253", 3},
+ {"\341\272\254", "\341\272\255", 3},
+ {"\341\272\256", "\341\272\257", 3},
+ {"\341\272\260", "\341\272\261", 3},
+ {"\341\272\262", "\341\272\263", 3},
+ {"\341\272\264", "\341\272\265", 3},
+ {"\341\272\266", "\341\272\267", 3},
+ {"\341\272\270", "\341\272\271", 3},
+ {"\341\272\272", "\341\272\273", 3},
+ {"\341\272\274", "\341\272\275", 3},
+ {"\341\272\276", "\341\272\277", 3},
+ {"\341\273\200", "\341\273\201", 3},
+ {"\341\273\202", "\341\273\203", 3},
+ {"\341\273\204", "\341\273\205", 3},
+ {"\341\273\206", "\341\273\207", 3},
+ {"\341\273\210", "\341\273\211", 3},
+ {"\341\273\212", "\341\273\213", 3},
+ {"\341\273\214", "\341\273\215", 3},
+ {"\341\273\216", "\341\273\217", 3},
+ {"\341\273\220", "\341\273\221", 3},
+ {"\341\273\222", "\341\273\223", 3},
+ {"\341\273\224", "\341\273\225", 3},
+ {"\341\273\226", "\341\273\227", 3},
+ {"\341\273\230", "\341\273\231", 3},
+ {"\341\273\232", "\341\273\233", 3},
+ {"\341\273\234", "\341\273\235", 3},
+ {"\341\273\236", "\341\273\237", 3},
+ {"\341\273\240", "\341\273\241", 3},
+ {"\341\273\242", "\341\273\243", 3},
+ {"\341\273\244", "\341\273\245", 3},
+ {"\341\273\246", "\341\273\247", 3},
+ {"\341\273\250", "\341\273\251", 3},
+ {"\341\273\252", "\341\273\253", 3},
+ {"\341\273\254", "\341\273\255", 3},
+ {"\341\273\256", "\341\273\257", 3},
+ {"\341\273\260", "\341\273\261", 3},
+ {"\341\273\262", "\341\273\263", 3},
+ {"\341\273\264", "\341\273\265", 3},
+ {"\341\273\266", "\341\273\267", 3},
+ {"\341\273\270", "\341\273\271", 3},
+ {"\341\274\210", "\341\274\200", 3},
+ {"\341\274\211", "\341\274\201", 3},
+ {"\341\274\212", "\341\274\202", 3},
+ {"\341\274\213", "\341\274\203", 3},
+ {"\341\274\214", "\341\274\204", 3},
+ {"\341\274\215", "\341\274\205", 3},
+ {"\341\274\216", "\341\274\206", 3},
+ {"\341\274\217", "\341\274\207", 3},
+ {"\341\274\230", "\341\274\220", 3},
+ {"\341\274\231", "\341\274\221", 3},
+ {"\341\274\232", "\341\274\222", 3},
+ {"\341\274\233", "\341\274\223", 3},
+ {"\341\274\234", "\341\274\224", 3},
+ {"\341\274\235", "\341\274\225", 3},
+ {"\341\274\250", "\341\274\240", 3},
+ {"\341\274\251", "\341\274\241", 3},
+ {"\341\274\252", "\341\274\242", 3},
+ {"\341\274\253", "\341\274\243", 3},
+ {"\341\274\254", "\341\274\244", 3},
+ {"\341\274\255", "\341\274\245", 3},
+ {"\341\274\256", "\341\274\246", 3},
+ {"\341\274\257", "\341\274\247", 3},
+ {"\341\274\270", "\341\274\260", 3},
+ {"\341\274\271", "\341\274\261", 3},
+ {"\341\274\272", "\341\274\262", 3},
+ {"\341\274\273", "\341\274\263", 3},
+ {"\341\274\274", "\341\274\264", 3},
+ {"\341\274\275", "\341\274\265", 3},
+ {"\341\274\276", "\341\274\266", 3},
+ {"\341\274\277", "\341\274\267", 3},
+ {"\341\275\210", "\341\275\200", 3},
+ {"\341\275\211", "\341\275\201", 3},
+ {"\341\275\212", "\341\275\202", 3},
+ {"\341\275\213", "\341\275\203", 3},
+ {"\341\275\214", "\341\275\204", 3},
+ {"\341\275\215", "\341\275\205", 3},
+ {"\341\275\231", "\341\275\221", 3},
+ {"\341\275\233", "\341\275\223", 3},
+ {"\341\275\235", "\341\275\225", 3},
+ {"\341\275\237", "\341\275\227", 3},
+ {"\341\275\250", "\341\275\240", 3},
+ {"\341\275\251", "\341\275\241", 3},
+ {"\341\275\252", "\341\275\242", 3},
+ {"\341\275\253", "\341\275\243", 3},
+ {"\341\275\254", "\341\275\244", 3},
+ {"\341\275\255", "\341\275\245", 3},
+ {"\341\275\256", "\341\275\246", 3},
+ {"\341\275\257", "\341\275\247", 3},
+ {"\341\276\210", "\341\276\200", 3},
+ {"\341\276\211", "\341\276\201", 3},
+ {"\341\276\212", "\341\276\202", 3},
+ {"\341\276\213", "\341\276\203", 3},
+ {"\341\276\214", "\341\276\204", 3},
+ {"\341\276\215", "\341\276\205", 3},
+ {"\341\276\216", "\341\276\206", 3},
+ {"\341\276\217", "\341\276\207", 3},
+ {"\341\276\230", "\341\276\220", 3},
+ {"\341\276\231", "\341\276\221", 3},
+ {"\341\276\232", "\341\276\222", 3},
+ {"\341\276\233", "\341\276\223", 3},
+ {"\341\276\234", "\341\276\224", 3},
+ {"\341\276\235", "\341\276\225", 3},
+ {"\341\276\236", "\341\276\226", 3},
+ {"\341\276\237", "\341\276\227", 3},
+ {"\341\276\250", "\341\276\240", 3},
+ {"\341\276\251", "\341\276\241", 3},
+ {"\341\276\252", "\341\276\242", 3},
+ {"\341\276\253", "\341\276\243", 3},
+ {"\341\276\254", "\341\276\244", 3},
+ {"\341\276\255", "\341\276\245", 3},
+ {"\341\276\256", "\341\276\246", 3},
+ {"\341\276\257", "\341\276\247", 3},
+ {"\341\276\270", "\341\276\260", 3},
+ {"\341\276\271", "\341\276\261", 3},
+ {"\341\276\272", "\341\275\260", 3},
+ {"\341\276\273", "\341\275\261", 3},
+ {"\341\276\274", "\341\276\263", 3},
+ {"\341\276\276", "\341\276\276", 3},
+ {"\341\277\210", "\341\275\262", 3},
+ {"\341\277\211", "\341\275\263", 3},
+ {"\341\277\212", "\341\275\264", 3},
+ {"\341\277\213", "\341\275\265", 3},
+ {"\341\277\214", "\341\277\203", 3},
+ {"\341\277\230", "\341\277\220", 3},
+ {"\341\277\231", "\341\277\221", 3},
+ {"\341\277\232", "\341\275\266", 3},
+ {"\341\277\233", "\341\275\267", 3},
+ {"\341\277\250", "\341\277\240", 3},
+ {"\341\277\251", "\341\277\241", 3},
+ {"\341\277\252", "\341\275\272", 3},
+ {"\341\277\253", "\341\275\273", 3},
+ {"\341\277\254", "\341\277\245", 3},
+ {"\341\277\270", "\341\275\270", 3},
+ {"\341\277\271", "\341\275\271", 3},
+ {"\341\277\272", "\341\275\274", 3},
+ {"\341\277\273", "\341\275\275", 3},
+ {"\341\277\274", "\341\277\263", 3},
+ {NULL, NULL, 0}
+};
+
+UpperLowerTbl_t Upper2LowerTbl31[] = {
+ {"\357\274\241", "\357\275\201", 3},
+ {"\357\274\242", "\357\275\202", 3},
+ {"\357\274\243", "\357\275\203", 3},
+ {"\357\274\244", "\357\275\204", 3},
+ {"\357\274\245", "\357\275\205", 3},
+ {"\357\274\246", "\357\275\206", 3},
+ {"\357\274\247", "\357\275\207", 3},
+ {"\357\274\250", "\357\275\210", 3},
+ {"\357\274\251", "\357\275\211", 3},
+ {"\357\274\252", "\357\275\212", 3},
+ {"\357\274\253", "\357\275\213", 3},
+ {"\357\274\254", "\357\275\214", 3},
+ {"\357\274\255", "\357\275\215", 3},
+ {"\357\274\256", "\357\275\216", 3},
+ {"\357\274\257", "\357\275\217", 3},
+ {"\357\274\260", "\357\275\220", 3},
+ {"\357\274\261", "\357\275\221", 3},
+ {"\357\274\262", "\357\275\222", 3},
+ {"\357\274\263", "\357\275\223", 3},
+ {"\357\274\264", "\357\275\224", 3},
+ {"\357\274\265", "\357\275\225", 3},
+ {"\357\274\266", "\357\275\226", 3},
+ {"\357\274\267", "\357\275\227", 3},
+ {"\357\274\270", "\357\275\230", 3},
+ {"\357\274\271", "\357\275\231", 3},
+ {"\357\274\272", "\357\275\232", 3},
+ {NULL, NULL, 0}
+ /* upper, lower */
+};
+
+UpperLowerTbl_t *Upper2LowerTbl2[] = {
+ Upper2LowerTbl20, /* \303 */
+ Upper2LowerTbl21, /* \304 */
+ Upper2LowerTbl22, /* \305 */
+ Upper2LowerTbl23, /* \306 */
+ Upper2LowerTbl24, /* \307 */
+ Upper2LowerTbl25, /* \310 */
+ NULL, /* \311 */
+ NULL, /* \312 */
+ NULL, /* \313 */
+ NULL, /* \314 */
+ NULL, /* \315 */
+ Upper2LowerTbl26, /* \316 */
+ Upper2LowerTbl27, /* \317 */
+ Upper2LowerTbl28, /* \320 */
+ Upper2LowerTbl29, /* \321 */
+ Upper2LowerTbl2a, /* \322 */
+ Upper2LowerTbl2b, /* \323 */
+ Upper2LowerTbl2c, /* \324 */
+ Upper2LowerTbl2d /* \325 */
+};
+
+UpperLowerTbl_t *Upper2LowerTbl3[] = {
+ Upper2LowerTbl30, /* \341 */
+ NULL, /* \342 */
+ NULL, /* \343 */
+ NULL, /* \344 */
+ NULL, /* \345 */
+ NULL, /* \346 */
+ NULL, /* \347 */
+ NULL, /* \350 */
+ NULL, /* \351 */
+ NULL, /* \352 */
+ NULL, /* \353 */
+ NULL, /* \354 */
+ NULL, /* \355 */
+ NULL, /* \356 */
+ Upper2LowerTbl31 /* \357 */
+};
+
+#define UL2S (unsigned char)'\303'
+#define UL2E (unsigned char)'\325'
+#define UL3S (unsigned char)'\341'
+#define UL3E (unsigned char)'\357'
+
+/*
+ * slapi_utf8StrToLower: translate upper-case string to lower-case
+ *
+ * input: a null terminated UTF-8 string
+ * output: a null terminated UTF-8 string which characters are
+ * converted to lower-case; characters which are not
+ * upper-case are copied as is. If it's not considered
+ * a UTF-8 string, NULL is returned.
+ *
+ * Notes: This function takes a string (made of multiple UTF-8 characters)
+ * for the input (not one character as in "tolower").
+ * Output string is allocated in this function, which needs to be
+ * released when it's not needed any more.
+ */
+unsigned char *
+slapi_UTF8STRTOLOWER(char *s)
+{
+ return slapi_utf8StrToLower((unsigned char *)s);
+}
+
+unsigned char *
+slapi_utf8StrToLower(unsigned char *s)
+{
+ UpperLowerTbl_t *ultp;
+ unsigned char *p, *np, *tail;
+ unsigned char *lp, *lphead;
+ int len, sz;
+
+ if (s == NULL || *s == '\0') {
+ return s;
+ }
+ len = strlen((char *)s);
+ tail = s + len;
+ lphead = lp = (unsigned char *)slapi_ch_malloc(len + 1);
+ p = s;
+ while ((np = (unsigned char *)ldap_utf8next((char *)p)) <= tail) {
+ switch(sz = np - p) {
+ case 1:
+ sprintf((char *)lp, "%c", tolower(*p));
+ break;
+ case 2:
+ if (*p < UL2S || *p > UL2E) { /* out of range */
+ memcpy(lp, p, sz);
+ break;
+ }
+ for (ultp = Upper2LowerTbl2[*p - UL2S];
+ ultp && ultp->upper && memcmp(p, ultp->upper, sz);
+ ultp++)
+ ;
+ if (!ultp) { /* out of range */
+ memcpy(lp, p, sz);
+ } else if (ultp->upper) { /* matched */
+ memcpy(lp, ultp->lower, ultp->tsz);
+ sz = ultp->tsz;
+ } else {
+ memcpy(lp, p, sz);
+ }
+ break;
+ case 3:
+ if (*p != UL3S && *p != UL3E) { /* out of range */
+ memcpy(lp, p, sz);
+ break;
+ }
+ for (ultp = Upper2LowerTbl3[*p - UL3S];
+ ultp && ultp->upper && memcmp(p, ultp->upper, sz);
+ ultp++)
+ ;
+ if (!ultp) { /* out of range */
+ memcpy(lp, p, sz);
+ } else if (ultp->upper) { /* matched */
+ memcpy(lp, ultp->lower, sz);
+ } else {
+ memcpy(lp, p, sz);
+ }
+ break;
+ case 4:
+ memcpy(lp, p, sz);
+ break;
+ default: /* not UTF-8 */
+ slapi_ch_free((void **)&lphead);
+ return NULL;
+ }
+ lp += sz;
+ p = np;
+ if (p == tail) {
+ break;
+ }
+ }
+ *lp = '\0';
+ return lphead;
+}
+
+/*
+ * slapi_utf8ToLower: translate upper-case character to lower-case
+ *
+ * input: a UTF-8 character (s)
+ * output: a UTF-8 character which is converted to lower-case (d)
+ * length (in bytes) of input character (ssz) and
+ * output character (dsz)
+ *
+ * Notes: This function takes a UTF-8 character (could be multiple bytes)
+ * for the input. Memory for the output character is NOT allocated
+ * in this function, caller should have allocated it (d).
+ * "memmove" is used since (s) and (d) are overlapped.
+ */
+void
+slapi_UTF8TOLOWER(char *s, char *d, int *ssz, int *dsz)
+{
+ slapi_utf8ToLower((unsigned char *)s, (unsigned char *)d, ssz, dsz);
+ return;
+}
+
+void
+slapi_utf8ToLower(unsigned char *s, unsigned char *d, int *ssz, int *dsz)
+{
+ UpperLowerTbl_t *ultp;
+ unsigned char *tail;
+
+ if (s == NULL || *s == '\0') {
+ *ssz = *dsz = 0;
+ return;
+ }
+ if (!(*s & 0x80)) { /* ASCII */
+ *dsz = *ssz = 1;
+ *d = tolower(*s);
+ return;
+ }
+ tail = (unsigned char *)ldap_utf8next((char *)s);
+ *dsz = *ssz = tail - s;
+ switch(*ssz) {
+ case 1: /* ASCII */
+ *d = tolower(*s);
+ break;
+ case 2: /* 2 bytes */
+ if (*s < UL2S || *s > UL2E) { /* out of range */
+ memmove(d, s, *ssz);
+ break;
+ }
+ for (ultp = Upper2LowerTbl2[*s - UL2S];
+ ultp && ultp->upper && memcmp(s, ultp->upper, *ssz);
+ ultp++)
+ ;
+ if (!ultp) { /* out of range */
+ memmove(d, s, *ssz);
+ } else if (ultp->upper) { /* matched */
+ memmove(d, ultp->lower, ultp->tsz);
+ *dsz = ultp->tsz;
+ } else {
+ memmove(d, s, *ssz);
+ }
+ break;
+ case 3: /* 3 bytes */
+ if (*s != UL3S && *s != UL3E) { /* out of range */
+ memmove(d, s, *ssz);
+ break;
+ }
+ for (ultp = Upper2LowerTbl3[*s - UL3S];
+ ultp && ultp->upper && memcmp(s, ultp->upper, *ssz);
+ ultp++)
+ ;
+ if (!ultp) { /* out of range */
+ memmove(d, s, *ssz);
+ } else if (ultp->upper) { /* matched */
+ memmove(d, ultp->lower, *ssz);
+ } else {
+ memmove(d, s, *ssz);
+ }
+ break;
+ }
+ return;
+}
+
+/*
+ * slapi_utf8isUpper: tests for a character that is a upper-case letter in
+ * UTF-8
+ *
+ * input: a UTF-8 character (could be multi-byte)
+ * output: 1 if the character is a upper-case letter
+ * 0 if the character is not a upper-case letter
+ */
+int
+slapi_UTF8ISUPPER(char *s)
+{
+ return slapi_utf8isUpper((unsigned char *)s);
+}
+
+int
+slapi_utf8isUpper(unsigned char *s)
+{
+ UpperLowerTbl_t *ultp;
+ unsigned char *next;
+ int sz;
+
+ if (s == NULL || *s == '\0') {
+ return 0;
+ }
+ if (!(*s & 0x80)) { /* ASCII */
+ return isupper(*s);
+ }
+ next = (unsigned char *)ldap_utf8next((char *)s);
+ switch(sz = next - s) {
+ case 1: /* ASCII */
+ return isupper(*s);
+ case 2:
+ if (*s < UL2S || *s > UL2E) { /* out of range */
+ return 0;
+ }
+ for (ultp = Upper2LowerTbl2[*s - UL2S];
+ ultp && ultp->upper && memcmp(s, ultp->upper, sz);
+ ultp++)
+ ;
+ if (!ultp) { /* out of range */
+ return 0;
+ } else if (ultp->upper) { /* matched */
+ return 1;
+ } else {
+ return 0;
+ }
+ case 3:
+ if (*s < UL3S || *s > UL3E) { /* out of range */
+ return 0;
+ }
+ for (ultp = Upper2LowerTbl3[*s - UL3S];
+ ultp && ultp->upper && memcmp(s, ultp->upper, sz);
+ ultp++)
+ ;
+ if (!ultp) { /* out of range */
+ return 0;
+ } else if (ultp->upper) { /* matched */
+ return 1;
+ } else {
+ return 0;
+ }
+ default:
+ return 0;
+ }
+}
+
+/*
+ * Lower2Upper Tables: sorted by lower characters
+ */
+UpperLowerTbl_t Lower2UpperTbl20[] = {
+ /* upper, lower */
+ {"\303\200", "\303\240", 2},
+ {"\303\201", "\303\241", 2},
+ {"\303\202", "\303\242", 2},
+ {"\303\203", "\303\243", 2},
+ {"\303\204", "\303\244", 2},
+ {"\303\205", "\303\245", 2},
+ {"\303\206", "\303\246", 2},
+ {"\303\207", "\303\247", 2},
+ {"\303\210", "\303\250", 2},
+ {"\303\211", "\303\251", 2},
+ {"\303\212", "\303\252", 2},
+ {"\303\213", "\303\253", 2},
+ {"\303\214", "\303\254", 2},
+ {"\303\215", "\303\255", 2},
+ {"\303\216", "\303\256", 2},
+ {"\303\217", "\303\257", 2},
+ {"\303\220", "\303\260", 2},
+ {"\303\221", "\303\261", 2},
+ {"\303\222", "\303\262", 2},
+ {"\303\223", "\303\263", 2},
+ {"\303\224", "\303\264", 2},
+ {"\303\225", "\303\265", 2},
+ {"\303\226", "\303\266", 2},
+ {"\303\230", "\303\270", 2},
+ {"\303\231", "\303\271", 2},
+ {"\303\232", "\303\272", 2},
+ {"\303\233", "\303\273", 2},
+ {"\303\234", "\303\274", 2},
+ {"\303\235", "\303\275", 2},
+ {"\303\236", "\303\276", 2},
+ {"\305\270", "\303\277", 2},
+ {NULL, NULL, 0}
+};
+
+UpperLowerTbl_t Lower2UpperTbl21[] = {
+ {"\304\200", "\304\201", 2},
+ {"\304\202", "\304\203", 2},
+ {"\304\204", "\304\205", 2},
+ {"\304\206", "\304\207", 2},
+ {"\304\210", "\304\211", 2},
+ {"\304\212", "\304\213", 2},
+ {"\304\214", "\304\215", 2},
+ {"\304\216", "\304\217", 2},
+ {"\304\220", "\304\221", 2},
+ {"\304\222", "\304\223", 2},
+ {"\304\224", "\304\225", 2},
+ {"\304\226", "\304\227", 2},
+ {"\304\230", "\304\231", 2},
+ {"\304\232", "\304\233", 2},
+ {"\304\234", "\304\235", 2},
+ {"\304\236", "\304\237", 2},
+ {"\304\240", "\304\241", 2},
+ {"\304\242", "\304\243", 2},
+ {"\304\244", "\304\245", 2},
+ {"\304\246", "\304\247", 2},
+ {"\304\250", "\304\251", 2},
+ {"\304\252", "\304\253", 2},
+ {"\304\254", "\304\255", 2},
+ {"\304\256", "\304\257", 2},
+ {"\111", "\304\261", 1},
+ {"\304\262", "\304\263", 2},
+ {"\304\264", "\304\265", 2},
+ {"\304\266", "\304\267", 2},
+ {"\304\271", "\304\272", 2},
+ {"\304\273", "\304\274", 2},
+ {"\304\275", "\304\276", 2},
+ {NULL, NULL}
+};
+
+UpperLowerTbl_t Lower2UpperTbl22[] = {
+ {"\304\277", "\305\200", 2},
+ {"\305\201", "\305\202", 2},
+ {"\305\203", "\305\204", 2},
+ {"\305\205", "\305\206", 2},
+ {"\305\207", "\305\210", 2},
+ {"\305\212", "\305\213", 2},
+ {"\305\214", "\305\215", 2},
+ {"\305\216", "\305\217", 2},
+ {"\305\220", "\305\221", 2},
+ {"\305\222", "\305\223", 2},
+ {"\305\224", "\305\225", 2},
+ {"\305\226", "\305\227", 2},
+ {"\305\230", "\305\231", 2},
+ {"\305\232", "\305\233", 2},
+ {"\305\234", "\305\235", 2},
+ {"\305\236", "\305\237", 2},
+ {"\305\240", "\305\241", 2},
+ {"\305\242", "\305\243", 2},
+ {"\305\244", "\305\245", 2},
+ {"\305\246", "\305\247", 2},
+ {"\305\250", "\305\251", 2},
+ {"\305\252", "\305\253", 2},
+ {"\305\254", "\305\255", 2},
+ {"\305\256", "\305\257", 2},
+ {"\305\260", "\305\261", 2},
+ {"\305\262", "\305\263", 2},
+ {"\305\264", "\305\265", 2},
+ {"\305\266", "\305\267", 2},
+ {"\305\271", "\305\272", 2},
+ {"\305\273", "\305\274", 2},
+ {"\305\275", "\305\276", 2},
+ {"\123", "\305\277", 1},
+ {NULL, NULL, 0}
+};
+
+UpperLowerTbl_t Lower2UpperTbl23[] = {
+ {"\306\202", "\306\203", 2},
+ {"\306\204", "\306\205", 2},
+ {"\306\207", "\306\210", 2},
+ {"\306\213", "\306\214", 2},
+ {"\306\221", "\306\222", 2},
+ {"\306\230", "\306\231", 2},
+ {"\306\240", "\306\241", 2},
+ {"\306\242", "\306\243", 2},
+ {"\306\244", "\306\245", 2},
+ {"\306\247", "\306\250", 2},
+ {"\306\254", "\306\255", 2},
+ {"\306\257", "\306\260", 2},
+ {"\306\263", "\306\264", 2},
+ {"\306\265", "\306\266", 2},
+ {"\306\270", "\306\271", 2},
+ {"\306\274", "\306\275", 2},
+ {NULL, NULL, 0}
+};
+
+UpperLowerTbl_t Lower2UpperTbl24[] = {
+ {"\307\204", "\307\206", 2},
+ {"\307\207", "\307\211", 2},
+ {"\307\212", "\307\214", 2},
+ {"\307\215", "\307\216", 2},
+ {"\307\217", "\307\220", 2},
+ {"\307\221", "\307\222", 2},
+ {"\307\223", "\307\224", 2},
+ {"\307\225", "\307\226", 2},
+ {"\307\227", "\307\230", 2},
+ {"\307\231", "\307\232", 2},
+ {"\307\233", "\307\234", 2},
+ {"\307\236", "\307\237", 2},
+ {"\307\240", "\307\241", 2},
+ {"\307\242", "\307\243", 2},
+ {"\307\244", "\307\245", 2},
+ {"\307\246", "\307\247", 2},
+ {"\307\250", "\307\251", 2},
+ {"\307\252", "\307\253", 2},
+ {"\307\254", "\307\255", 2},
+ {"\307\256", "\307\257", 2},
+ {"\307\261", "\307\263", 2},
+ {"\307\264", "\307\265", 2},
+ {"\307\272", "\307\273", 2},
+ {"\307\274", "\307\275", 2},
+ {"\307\276", "\307\277", 2},
+ {NULL, NULL, 0}
+};
+
+UpperLowerTbl_t Lower2UpperTbl25[] = {
+ {"\310\200", "\310\201", 2},
+ {"\310\202", "\310\203", 2},
+ {"\310\204", "\310\205", 2},
+ {"\310\206", "\310\207", 2},
+ {"\310\210", "\310\211", 2},
+ {"\310\212", "\310\213", 2},
+ {"\310\214", "\310\215", 2},
+ {"\310\216", "\310\217", 2},
+ {"\310\220", "\310\221", 2},
+ {"\310\222", "\310\223", 2},
+ {"\310\224", "\310\225", 2},
+ {"\310\226", "\310\227", 2},
+ {NULL, NULL, 0}
+};
+
+UpperLowerTbl_t Lower2UpperTbl26[] = {
+ {"\306\201", "\311\223", 2},
+ {"\306\206", "\311\224", 2},
+ {"\306\211", "\311\226", 2},
+ {"\306\212", "\311\227", 2},
+ {"\306\216", "\311\230", 2},
+ {"\306\217", "\311\231", 2},
+ {"\306\220", "\311\233", 2},
+ {"\306\223", "\311\240", 2},
+ {"\306\224", "\311\243", 2},
+ {"\306\227", "\311\250", 2},
+ {"\306\226", "\311\251", 2},
+ {"\306\234", "\311\257", 2},
+ {"\306\235", "\311\262", 2},
+ {NULL, NULL, 0}
+};
+
+UpperLowerTbl_t Lower2UpperTbl27[] = {
+ {"\306\251", "\312\203", 2},
+ {"\306\256", "\312\210", 2},
+ {"\306\261", "\312\212", 2},
+ {"\306\262", "\312\213", 2},
+ {"\306\267", "\312\222", 2},
+ {NULL, NULL, 0}
+};
+
+UpperLowerTbl_t Lower2UpperTbl28[] = {
+ {"\316\206", "\316\254", 2},
+ {"\316\210", "\316\255", 2},
+ {"\316\211", "\316\256", 2},
+ {"\316\212", "\316\257", 2},
+ {"\316\221", "\316\261", 2},
+ {"\316\222", "\316\262", 2},
+ {"\316\223", "\316\263", 2},
+ {"\316\224", "\316\264", 2},
+ {"\316\225", "\316\265", 2},
+ {"\316\226", "\316\266", 2},
+ {"\316\227", "\316\267", 2},
+ {"\316\230", "\316\270", 2},
+ {"\316\231", "\316\271", 2},
+ {"\316\232", "\316\272", 2},
+ {"\316\233", "\316\273", 2},
+ {"\316\234", "\316\274", 2},
+ {"\316\235", "\316\275", 2},
+ {"\316\236", "\316\276", 2},
+ {"\316\237", "\316\277", 2},
+ {NULL, NULL, 0}
+};
+
+UpperLowerTbl_t Lower2UpperTbl29[] = {
+ {"\316\240", "\317\200", 2},
+ {"\316\241", "\317\201", 2},
+ {"\316\243", "\317\202", 2},
+ {"\316\243", "\317\203", 2},
+ {"\316\244", "\317\204", 2},
+ {"\316\245", "\317\205", 2},
+ {"\316\246", "\317\206", 2},
+ {"\316\247", "\317\207", 2},
+ {"\316\250", "\317\210", 2},
+ {"\316\251", "\317\211", 2},
+ {"\316\252", "\317\212", 2},
+ {"\316\253", "\317\213", 2},
+ {"\316\214", "\317\214", 2},
+ {"\316\216", "\317\215", 2},
+ {"\316\217", "\317\216", 2},
+ {"\316\222", "\317\220", 2},
+ {"\316\230", "\317\221", 2},
+ {"\316\246", "\317\225", 2},
+ {"\316\240", "\317\226", 2},
+ {"\317\242", "\317\243", 2},
+ {"\317\244", "\317\245", 2},
+ {"\317\246", "\317\247", 2},
+ {"\317\250", "\317\251", 2},
+ {"\317\252", "\317\253", 2},
+ {"\317\254", "\317\255", 2},
+ {"\317\256", "\317\257", 2},
+ {"\316\232", "\317\260", 2},
+ {"\316\241", "\317\261", 2},
+ {NULL, NULL, 0}
+};
+
+UpperLowerTbl_t Lower2UpperTbl2a[] = {
+ {"\320\220", "\320\260", 2},
+ {"\320\221", "\320\261", 2},
+ {"\320\222", "\320\262", 2},
+ {"\320\223", "\320\263", 2},
+ {"\320\224", "\320\264", 2},
+ {"\320\225", "\320\265", 2},
+ {"\320\226", "\320\266", 2},
+ {"\320\227", "\320\267", 2},
+ {"\320\230", "\320\270", 2},
+ {"\320\231", "\320\271", 2},
+ {"\320\232", "\320\272", 2},
+ {"\320\233", "\320\273", 2},
+ {"\320\234", "\320\274", 2},
+ {"\320\235", "\320\275", 2},
+ {"\320\236", "\320\276", 2},
+ {"\320\237", "\320\277", 2},
+ {NULL, NULL, 0}
+};
+
+UpperLowerTbl_t Lower2UpperTbl2b[] = {
+ {"\320\240", "\321\200", 2},
+ {"\320\241", "\321\201", 2},
+ {"\320\242", "\321\202", 2},
+ {"\320\243", "\321\203", 2},
+ {"\320\244", "\321\204", 2},
+ {"\320\245", "\321\205", 2},
+ {"\320\246", "\321\206", 2},
+ {"\320\247", "\321\207", 2},
+ {"\320\250", "\321\210", 2},
+ {"\320\251", "\321\211", 2},
+ {"\320\252", "\321\212", 2},
+ {"\320\253", "\321\213", 2},
+ {"\320\254", "\321\214", 2},
+ {"\320\255", "\321\215", 2},
+ {"\320\256", "\321\216", 2},
+ {"\320\257", "\321\217", 2},
+ {"\320\201", "\321\221", 2},
+ {"\320\202", "\321\222", 2},
+ {"\320\203", "\321\223", 2},
+ {"\320\204", "\321\224", 2},
+ {"\320\205", "\321\225", 2},
+ {"\320\206", "\321\226", 2},
+ {"\320\207", "\321\227", 2},
+ {"\320\210", "\321\230", 2},
+ {"\320\211", "\321\231", 2},
+ {"\320\212", "\321\232", 2},
+ {"\320\213", "\321\233", 2},
+ {"\320\214", "\321\234", 2},
+ {"\320\216", "\321\236", 2},
+ {"\320\217", "\321\237", 2},
+ {"\321\240", "\321\241", 2},
+ {"\321\242", "\321\243", 2},
+ {"\321\244", "\321\245", 2},
+ {"\321\246", "\321\247", 2},
+ {"\321\250", "\321\251", 2},
+ {"\321\252", "\321\253", 2},
+ {"\321\254", "\321\255", 2},
+ {"\321\256", "\321\257", 2},
+ {"\321\260", "\321\261", 2},
+ {"\321\262", "\321\263", 2},
+ {"\321\264", "\321\265", 2},
+ {"\321\266", "\321\267", 2},
+ {"\321\270", "\321\271", 2},
+ {"\321\272", "\321\273", 2},
+ {"\321\274", "\321\275", 2},
+ {"\321\276", "\321\277", 2},
+ {NULL, NULL, 0}
+};
+
+UpperLowerTbl_t Lower2UpperTbl2c[] = {
+ {"\322\200", "\322\201", 2},
+ {"\322\220", "\322\221", 2},
+ {"\322\222", "\322\223", 2},
+ {"\322\224", "\322\225", 2},
+ {"\322\226", "\322\227", 2},
+ {"\322\230", "\322\231", 2},
+ {"\322\232", "\322\233", 2},
+ {"\322\234", "\322\235", 2},
+ {"\322\236", "\322\237", 2},
+ {"\322\240", "\322\241", 2},
+ {"\322\242", "\322\243", 2},
+ {"\322\244", "\322\245", 2},
+ {"\322\246", "\322\247", 2},
+ {"\322\250", "\322\251", 2},
+ {"\322\252", "\322\253", 2},
+ {"\322\254", "\322\255", 2},
+ {"\322\256", "\322\257", 2},
+ {"\322\260", "\322\261", 2},
+ {"\322\262", "\322\263", 2},
+ {"\322\264", "\322\265", 2},
+ {"\322\266", "\322\267", 2},
+ {"\322\270", "\322\271", 2},
+ {"\322\272", "\322\273", 2},
+ {"\322\274", "\322\275", 2},
+ {"\322\276", "\322\277", 2},
+ {NULL, NULL, 0}
+};
+
+UpperLowerTbl_t Lower2UpperTbl2d[] = {
+ {"\323\201", "\323\202", 2},
+ {"\323\203", "\323\204", 2},
+ {"\323\207", "\323\210", 2},
+ {"\323\213", "\323\214", 2},
+ {"\323\220", "\323\221", 2},
+ {"\323\222", "\323\223", 2},
+ {"\323\224", "\323\225", 2},
+ {"\323\226", "\323\227", 2},
+ {"\323\230", "\323\231", 2},
+ {"\323\232", "\323\233", 2},
+ {"\323\234", "\323\235", 2},
+ {"\323\236", "\323\237", 2},
+ {"\323\240", "\323\241", 2},
+ {"\323\242", "\323\243", 2},
+ {"\323\244", "\323\245", 2},
+ {"\323\246", "\323\247", 2},
+ {"\323\250", "\323\251", 2},
+ {"\323\252", "\323\253", 2},
+ {"\323\256", "\323\257", 2},
+ {"\323\260", "\323\261", 2},
+ {"\323\262", "\323\263", 2},
+ {"\323\264", "\323\265", 2},
+ {"\323\270", "\323\271", 2},
+ {NULL, NULL, 0}
+};
+
+UpperLowerTbl_t Lower2UpperTbl2e[] = {
+ {"\324\261", "\325\241", 2},
+ {"\324\262", "\325\242", 2},
+ {"\324\263", "\325\243", 2},
+ {"\324\264", "\325\244", 2},
+ {"\324\265", "\325\245", 2},
+ {"\324\266", "\325\246", 2},
+ {"\324\267", "\325\247", 2},
+ {"\324\270", "\325\250", 2},
+ {"\324\271", "\325\251", 2},
+ {"\324\272", "\325\252", 2},
+ {"\324\273", "\325\253", 2},
+ {"\324\274", "\325\254", 2},
+ {"\324\275", "\325\255", 2},
+ {"\324\276", "\325\256", 2},
+ {"\324\277", "\325\257", 2},
+ {"\325\200", "\325\260", 2},
+ {"\325\201", "\325\261", 2},
+ {"\325\202", "\325\262", 2},
+ {"\325\203", "\325\263", 2},
+ {"\325\204", "\325\264", 2},
+ {"\325\205", "\325\265", 2},
+ {"\325\206", "\325\266", 2},
+ {"\325\207", "\325\267", 2},
+ {"\325\210", "\325\270", 2},
+ {"\325\211", "\325\271", 2},
+ {"\325\212", "\325\272", 2},
+ {"\325\213", "\325\273", 2},
+ {"\325\214", "\325\274", 2},
+ {"\325\215", "\325\275", 2},
+ {"\325\216", "\325\276", 2},
+ {"\325\217", "\325\277", 2},
+ {NULL, NULL, 0}
+};
+
+UpperLowerTbl_t Lower2UpperTbl2f[] = {
+ {"\325\220", "\326\200", 2},
+ {"\325\221", "\326\201", 2},
+ {"\325\222", "\326\202", 2},
+ {"\325\223", "\326\203", 2},
+ {"\325\224", "\326\204", 2},
+ {"\325\225", "\326\205", 2},
+ {"\325\226", "\326\206", 2},
+ {NULL, NULL, 0}
+};
+
+UpperLowerTbl_t Lower2UpperTbl30[] = {
+ {"\341\202\240", "\341\203\220", 3},
+ {"\341\202\241", "\341\203\221", 3},
+ {"\341\202\242", "\341\203\222", 3},
+ {"\341\202\243", "\341\203\223", 3},
+ {"\341\202\244", "\341\203\224", 3},
+ {"\341\202\245", "\341\203\225", 3},
+ {"\341\202\246", "\341\203\226", 3},
+ {"\341\202\247", "\341\203\227", 3},
+ {"\341\202\250", "\341\203\230", 3},
+ {"\341\202\251", "\341\203\231", 3},
+ {"\341\202\252", "\341\203\232", 3},
+ {"\341\202\253", "\341\203\233", 3},
+ {"\341\202\254", "\341\203\234", 3},
+ {"\341\202\255", "\341\203\235", 3},
+ {"\341\202\256", "\341\203\236", 3},
+ {"\341\202\257", "\341\203\237", 3},
+ {"\341\202\260", "\341\203\240", 3},
+ {"\341\202\261", "\341\203\241", 3},
+ {"\341\202\262", "\341\203\242", 3},
+ {"\341\202\263", "\341\203\243", 3},
+ {"\341\202\264", "\341\203\244", 3},
+ {"\341\202\265", "\341\203\245", 3},
+ {"\341\202\266", "\341\203\246", 3},
+ {"\341\202\267", "\341\203\247", 3},
+ {"\341\202\270", "\341\203\250", 3},
+ {"\341\202\271", "\341\203\251", 3},
+ {"\341\202\272", "\341\203\252", 3},
+ {"\341\202\273", "\341\203\253", 3},
+ {"\341\202\274", "\341\203\254", 3},
+ {"\341\202\275", "\341\203\255", 3},
+ {"\341\202\276", "\341\203\256", 3},
+ {"\341\202\277", "\341\203\257", 3},
+ {"\341\203\200", "\341\203\260", 3},
+ {"\341\203\201", "\341\203\261", 3},
+ {"\341\203\202", "\341\203\262", 3},
+ {"\341\203\203", "\341\203\263", 3},
+ {"\341\203\204", "\341\203\264", 3},
+ {"\341\203\205", "\341\203\265", 3},
+ {"\341\270\200", "\341\270\201", 3},
+ {"\341\270\202", "\341\270\203", 3},
+ {"\341\270\204", "\341\270\205", 3},
+ {"\341\270\206", "\341\270\207", 3},
+ {"\341\270\210", "\341\270\211", 3},
+ {"\341\270\212", "\341\270\213", 3},
+ {"\341\270\214", "\341\270\215", 3},
+ {"\341\270\216", "\341\270\217", 3},
+ {"\341\270\220", "\341\270\221", 3},
+ {"\341\270\222", "\341\270\223", 3},
+ {"\341\270\224", "\341\270\225", 3},
+ {"\341\270\226", "\341\270\227", 3},
+ {"\341\270\230", "\341\270\231", 3},
+ {"\341\270\232", "\341\270\233", 3},
+ {"\341\270\234", "\341\270\235", 3},
+ {"\341\270\236", "\341\270\237", 3},
+ {"\341\270\240", "\341\270\241", 3},
+ {"\341\270\242", "\341\270\243", 3},
+ {"\341\270\244", "\341\270\245", 3},
+ {"\341\270\246", "\341\270\247", 3},
+ {"\341\270\250", "\341\270\251", 3},
+ {"\341\270\252", "\341\270\253", 3},
+ {"\341\270\254", "\341\270\255", 3},
+ {"\341\270\256", "\341\270\257", 3},
+ {"\341\270\260", "\341\270\261", 3},
+ {"\341\270\262", "\341\270\263", 3},
+ {"\341\270\264", "\341\270\265", 3},
+ {"\341\270\266", "\341\270\267", 3},
+ {"\341\270\270", "\341\270\271", 3},
+ {"\341\270\272", "\341\270\273", 3},
+ {"\341\270\274", "\341\270\275", 3},
+ {"\341\270\276", "\341\270\277", 3},
+ {"\341\271\200", "\341\271\201", 3},
+ {"\341\271\202", "\341\271\203", 3},
+ {"\341\271\204", "\341\271\205", 3},
+ {"\341\271\206", "\341\271\207", 3},
+ {"\341\271\210", "\341\271\211", 3},
+ {"\341\271\212", "\341\271\213", 3},
+ {"\341\271\214", "\341\271\215", 3},
+ {"\341\271\216", "\341\271\217", 3},
+ {"\341\271\220", "\341\271\221", 3},
+ {"\341\271\222", "\341\271\223", 3},
+ {"\341\271\224", "\341\271\225", 3},
+ {"\341\271\226", "\341\271\227", 3},
+ {"\341\271\230", "\341\271\231", 3},
+ {"\341\271\232", "\341\271\233", 3},
+ {"\341\271\234", "\341\271\235", 3},
+ {"\341\271\236", "\341\271\237", 3},
+ {"\341\271\240", "\341\271\241", 3},
+ {"\341\271\242", "\341\271\243", 3},
+ {"\341\271\244", "\341\271\245", 3},
+ {"\341\271\246", "\341\271\247", 3},
+ {"\341\271\250", "\341\271\251", 3},
+ {"\341\271\252", "\341\271\253", 3},
+ {"\341\271\254", "\341\271\255", 3},
+ {"\341\271\256", "\341\271\257", 3},
+ {"\341\271\260", "\341\271\261", 3},
+ {"\341\271\262", "\341\271\263", 3},
+ {"\341\271\264", "\341\271\265", 3},
+ {"\341\271\266", "\341\271\267", 3},
+ {"\341\271\270", "\341\271\271", 3},
+ {"\341\271\272", "\341\271\273", 3},
+ {"\341\271\274", "\341\271\275", 3},
+ {"\341\271\276", "\341\271\277", 3},
+ {"\341\272\200", "\341\272\201", 3},
+ {"\341\272\202", "\341\272\203", 3},
+ {"\341\272\204", "\341\272\205", 3},
+ {"\341\272\206", "\341\272\207", 3},
+ {"\341\272\210", "\341\272\211", 3},
+ {"\341\272\212", "\341\272\213", 3},
+ {"\341\272\214", "\341\272\215", 3},
+ {"\341\272\216", "\341\272\217", 3},
+ {"\341\272\220", "\341\272\221", 3},
+ {"\341\272\222", "\341\272\223", 3},
+ {"\341\272\224", "\341\272\225", 3},
+ {"\341\272\240", "\341\272\241", 3},
+ {"\341\272\242", "\341\272\243", 3},
+ {"\341\272\244", "\341\272\245", 3},
+ {"\341\272\246", "\341\272\247", 3},
+ {"\341\272\250", "\341\272\251", 3},
+ {"\341\272\252", "\341\272\253", 3},
+ {"\341\272\254", "\341\272\255", 3},
+ {"\341\272\256", "\341\272\257", 3},
+ {"\341\272\260", "\341\272\261", 3},
+ {"\341\272\262", "\341\272\263", 3},
+ {"\341\272\264", "\341\272\265", 3},
+ {"\341\272\266", "\341\272\267", 3},
+ {"\341\272\270", "\341\272\271", 3},
+ {"\341\272\272", "\341\272\273", 3},
+ {"\341\272\274", "\341\272\275", 3},
+ {"\341\272\276", "\341\272\277", 3},
+ {"\341\273\200", "\341\273\201", 3},
+ {"\341\273\202", "\341\273\203", 3},
+ {"\341\273\204", "\341\273\205", 3},
+ {"\341\273\206", "\341\273\207", 3},
+ {"\341\273\210", "\341\273\211", 3},
+ {"\341\273\212", "\341\273\213", 3},
+ {"\341\273\214", "\341\273\215", 3},
+ {"\341\273\216", "\341\273\217", 3},
+ {"\341\273\220", "\341\273\221", 3},
+ {"\341\273\222", "\341\273\223", 3},
+ {"\341\273\224", "\341\273\225", 3},
+ {"\341\273\226", "\341\273\227", 3},
+ {"\341\273\230", "\341\273\231", 3},
+ {"\341\273\232", "\341\273\233", 3},
+ {"\341\273\234", "\341\273\235", 3},
+ {"\341\273\236", "\341\273\237", 3},
+ {"\341\273\240", "\341\273\241", 3},
+ {"\341\273\242", "\341\273\243", 3},
+ {"\341\273\244", "\341\273\245", 3},
+ {"\341\273\246", "\341\273\247", 3},
+ {"\341\273\250", "\341\273\251", 3},
+ {"\341\273\252", "\341\273\253", 3},
+ {"\341\273\254", "\341\273\255", 3},
+ {"\341\273\256", "\341\273\257", 3},
+ {"\341\273\260", "\341\273\261", 3},
+ {"\341\273\262", "\341\273\263", 3},
+ {"\341\273\264", "\341\273\265", 3},
+ {"\341\273\266", "\341\273\267", 3},
+ {"\341\273\270", "\341\273\271", 3},
+ {"\341\274\210", "\341\274\200", 3},
+ {"\341\274\211", "\341\274\201", 3},
+ {"\341\274\212", "\341\274\202", 3},
+ {"\341\274\213", "\341\274\203", 3},
+ {"\341\274\214", "\341\274\204", 3},
+ {"\341\274\215", "\341\274\205", 3},
+ {"\341\274\216", "\341\274\206", 3},
+ {"\341\274\217", "\341\274\207", 3},
+ {"\341\274\230", "\341\274\220", 3},
+ {"\341\274\231", "\341\274\221", 3},
+ {"\341\274\232", "\341\274\222", 3},
+ {"\341\274\233", "\341\274\223", 3},
+ {"\341\274\234", "\341\274\224", 3},
+ {"\341\274\235", "\341\274\225", 3},
+ {"\341\274\250", "\341\274\240", 3},
+ {"\341\274\251", "\341\274\241", 3},
+ {"\341\274\252", "\341\274\242", 3},
+ {"\341\274\253", "\341\274\243", 3},
+ {"\341\274\254", "\341\274\244", 3},
+ {"\341\274\255", "\341\274\245", 3},
+ {"\341\274\256", "\341\274\246", 3},
+ {"\341\274\257", "\341\274\247", 3},
+ {"\341\274\270", "\341\274\260", 3},
+ {"\341\274\271", "\341\274\261", 3},
+ {"\341\274\272", "\341\274\262", 3},
+ {"\341\274\273", "\341\274\263", 3},
+ {"\341\274\274", "\341\274\264", 3},
+ {"\341\274\275", "\341\274\265", 3},
+ {"\341\274\276", "\341\274\266", 3},
+ {"\341\274\277", "\341\274\267", 3},
+ {"\341\275\210", "\341\275\200", 3},
+ {"\341\275\211", "\341\275\201", 3},
+ {"\341\275\212", "\341\275\202", 3},
+ {"\341\275\213", "\341\275\203", 3},
+ {"\341\275\214", "\341\275\204", 3},
+ {"\341\275\215", "\341\275\205", 3},
+ {"\341\275\231", "\341\275\221", 3},
+ {"\341\275\233", "\341\275\223", 3},
+ {"\341\275\235", "\341\275\225", 3},
+ {"\341\275\237", "\341\275\227", 3},
+ {"\341\275\250", "\341\275\240", 3},
+ {"\341\275\251", "\341\275\241", 3},
+ {"\341\275\252", "\341\275\242", 3},
+ {"\341\275\253", "\341\275\243", 3},
+ {"\341\275\254", "\341\275\244", 3},
+ {"\341\275\255", "\341\275\245", 3},
+ {"\341\275\256", "\341\275\246", 3},
+ {"\341\275\257", "\341\275\247", 3},
+ {"\341\276\272", "\341\275\260", 3},
+ {"\341\276\273", "\341\275\261", 3},
+ {"\341\277\210", "\341\275\262", 3},
+ {"\341\277\211", "\341\275\263", 3},
+ {"\341\277\212", "\341\275\264", 3},
+ {"\341\277\213", "\341\275\265", 3},
+ {"\341\277\232", "\341\275\266", 3},
+ {"\341\277\233", "\341\275\267", 3},
+ {"\341\277\270", "\341\275\270", 3},
+ {"\341\277\271", "\341\275\271", 3},
+ {"\341\277\252", "\341\275\272", 3},
+ {"\341\277\253", "\341\275\273", 3},
+ {"\341\277\272", "\341\275\274", 3},
+ {"\341\277\273", "\341\275\275", 3},
+ {"\341\276\210", "\341\276\200", 3},
+ {"\341\276\211", "\341\276\201", 3},
+ {"\341\276\212", "\341\276\202", 3},
+ {"\341\276\213", "\341\276\203", 3},
+ {"\341\276\214", "\341\276\204", 3},
+ {"\341\276\215", "\341\276\205", 3},
+ {"\341\276\216", "\341\276\206", 3},
+ {"\341\276\217", "\341\276\207", 3},
+ {"\341\276\230", "\341\276\220", 3},
+ {"\341\276\231", "\341\276\221", 3},
+ {"\341\276\232", "\341\276\222", 3},
+ {"\341\276\233", "\341\276\223", 3},
+ {"\341\276\234", "\341\276\224", 3},
+ {"\341\276\235", "\341\276\225", 3},
+ {"\341\276\236", "\341\276\226", 3},
+ {"\341\276\237", "\341\276\227", 3},
+ {"\341\276\250", "\341\276\240", 3},
+ {"\341\276\251", "\341\276\241", 3},
+ {"\341\276\252", "\341\276\242", 3},
+ {"\341\276\253", "\341\276\243", 3},
+ {"\341\276\254", "\341\276\244", 3},
+ {"\341\276\255", "\341\276\245", 3},
+ {"\341\276\256", "\341\276\246", 3},
+ {"\341\276\257", "\341\276\247", 3},
+ {"\341\276\270", "\341\276\260", 3},
+ {"\341\276\271", "\341\276\261", 3},
+ {"\341\276\274", "\341\276\263", 3},
+ {"\341\277\214", "\341\277\203", 3},
+ {"\341\277\230", "\341\277\220", 3},
+ {"\341\277\231", "\341\277\221", 3},
+ {"\341\277\250", "\341\277\240", 3},
+ {"\341\277\251", "\341\277\241", 3},
+ {"\341\277\254", "\341\277\245", 3},
+ {"\341\277\274", "\341\277\263", 3},
+ {NULL, NULL, 0}
+};
+
+UpperLowerTbl_t Lower2UpperTbl31[] = {
+ {"\357\274\241", "\357\275\201", 3},
+ {"\357\274\242", "\357\275\202", 3},
+ {"\357\274\243", "\357\275\203", 3},
+ {"\357\274\244", "\357\275\204", 3},
+ {"\357\274\245", "\357\275\205", 3},
+ {"\357\274\246", "\357\275\206", 3},
+ {"\357\274\247", "\357\275\207", 3},
+ {"\357\274\250", "\357\275\210", 3},
+ {"\357\274\251", "\357\275\211", 3},
+ {"\357\274\252", "\357\275\212", 3},
+ {"\357\274\253", "\357\275\213", 3},
+ {"\357\274\254", "\357\275\214", 3},
+ {"\357\274\255", "\357\275\215", 3},
+ {"\357\274\256", "\357\275\216", 3},
+ {"\357\274\257", "\357\275\217", 3},
+ {"\357\274\260", "\357\275\220", 3},
+ {"\357\274\261", "\357\275\221", 3},
+ {"\357\274\262", "\357\275\222", 3},
+ {"\357\274\263", "\357\275\223", 3},
+ {"\357\274\264", "\357\275\224", 3},
+ {"\357\274\265", "\357\275\225", 3},
+ {"\357\274\266", "\357\275\226", 3},
+ {"\357\274\267", "\357\275\227", 3},
+ {"\357\274\270", "\357\275\230", 3},
+ {"\357\274\271", "\357\275\231", 3},
+ {"\357\274\272", "\357\275\232", 3},
+ {NULL, NULL, 0}
+ /* upper, lower */
+};
+
+UpperLowerTbl_t *Lower2UpperTbl2[] = {
+ Lower2UpperTbl20, /* \303 */
+ Lower2UpperTbl21, /* \304 */
+ Lower2UpperTbl22, /* \305 */
+ Lower2UpperTbl23, /* \306 */
+ Lower2UpperTbl24, /* \307 */
+ Lower2UpperTbl25, /* \310 */
+ Lower2UpperTbl26, /* \311 */
+ Lower2UpperTbl27, /* \312 */
+ NULL, /* \313 */
+ NULL, /* \314 */
+ NULL, /* \315 */
+ Lower2UpperTbl28, /* \316 */
+ Lower2UpperTbl29, /* \317 */
+ Lower2UpperTbl2a, /* \320 */
+ Lower2UpperTbl2b, /* \321 */
+ Lower2UpperTbl2c, /* \322 */
+ Lower2UpperTbl2d, /* \323 */
+ NULL, /* \324 */
+ Lower2UpperTbl2e, /* \325 */
+ Lower2UpperTbl2f /* \326 */
+};
+
+UpperLowerTbl_t *Lower2UpperTbl3[] = {
+ Lower2UpperTbl30, /* \341 */
+ NULL, /* \342 */
+ NULL, /* \343 */
+ NULL, /* \344 */
+ NULL, /* \345 */
+ NULL, /* \346 */
+ NULL, /* \347 */
+ NULL, /* \350 */
+ NULL, /* \351 */
+ NULL, /* \352 */
+ NULL, /* \353 */
+ NULL, /* \354 */
+ NULL, /* \355 */
+ NULL, /* \356 */
+ Lower2UpperTbl31 /* \357 */
+};
+
+#define LU2S (unsigned char)'\303'
+#define LU2E (unsigned char)'\326'
+#define LU3S (unsigned char)'\341'
+#define LU3E (unsigned char)'\357'
+
+/*
+ * slapi_utf8StrToUpper: translate lower-case string to upper-case
+ *
+ * input: a null terminated UTF-8 string
+ * output: a null terminated UTF-8 string which characters are
+ * converted to upper-case; characters which are not
+ * lower-case are copied as is. If it's not considered
+ * a UTF-8 string, NULL is returned.
+ *
+ * Notes: This function takes a string (made of multiple UTF-8 characters)
+ * for the input (not one character as in "toupper").
+ * Output string is allocated in this function, which needs to be
+ * released when it's not needed any more.
+ */
+unsigned char *
+slapi_UTF8STRTOUPPER(char *s)
+{
+ return slapi_utf8StrToUpper((unsigned char *)s);
+}
+
+unsigned char *
+slapi_utf8StrToUpper(unsigned char *s)
+{
+ UpperLowerTbl_t *ultp;
+ unsigned char *p, *np, *tail;
+ unsigned char *up, *uphead;
+ int len, sz;
+
+ if (s == NULL || *s == '\0') {
+ return s;
+ }
+ len = strlen((char *)s);
+ tail = s + len;
+ uphead = up = (unsigned char *)slapi_ch_malloc(len + 1);
+ p = s;
+ while ((np = (unsigned char *)ldap_utf8next((char *)p)) <= tail) {
+ switch(sz = np - p) {
+ case 1: /* ASCII */
+ sprintf((char *)up, "%c", toupper(*p));
+ break;
+ case 2: /* 2 bytes */
+ if (*p < LU2S || *p > LU2E) { /* out of range */
+ memcpy(up, p, sz);
+ break;
+ }
+ for (ultp = Lower2UpperTbl2[*p - LU2S];
+ ultp && ultp->lower && memcmp(p, ultp->lower, sz);
+ ultp++)
+ ;
+ if (!ultp) { /* out of range */
+ memcpy(up, p, sz);
+ } else if (ultp->lower) { /* matched */
+ memcpy(up, ultp->upper, ultp->tsz);
+ sz = ultp->tsz;
+ } else {
+ memcpy(up, p, sz);
+ }
+ break;
+ case 3: /* 3 bytes */
+ if (*p != LU3S && *p != LU3E) { /* out of range */
+ memcpy(up, p, sz);
+ break;
+ }
+ for (ultp = Lower2UpperTbl3[*p - LU3S];
+ ultp && ultp->lower && memcmp(p, ultp->lower, sz);
+ ultp++)
+ ;
+ if (!ultp) { /* out of range */
+ memcpy(up, p, sz);
+ } else if (ultp->lower) { /* matched */
+ memcpy(up, ultp->upper, sz);
+ } else {
+ memcpy(up, p, sz);
+ }
+ break;
+ case 4:
+ memcpy(up, p, sz);
+ break;
+ default: /* not UTF-8 */
+ slapi_ch_free((void **)&uphead);
+ return NULL;
+ }
+ up += sz;
+ p = np;
+ if (p == tail) {
+ break;
+ }
+ }
+ *up = '\0';
+ return uphead;
+}
+
+/*
+ * slapi_utf8ToUpper: translate lower-case character to upper-case
+ *
+ * input: a UTF-8 character (s)
+ * output: a UTF-8 character which is converted to upper-case (d)
+ * length (in bytes) of input character (ssz) and
+ * output character (dsz)
+ *
+ * Notes: This function takes a UTF-8 character (could be multiple bytes)
+ * for the input. Memory for the output character is NOT allocated
+ * in this function, caller should have allocated it (d).
+ * "memmove" is used since (s) and (d) are overlapped.
+ */
+void
+slapi_UTF8TOUPPER(char *s, char *d, int *ssz, int *dsz)
+{
+ slapi_utf8ToUpper((unsigned char *)s, (unsigned char *)d, ssz, dsz);
+ return;
+}
+
+void
+slapi_utf8ToUpper(unsigned char *s, unsigned char *d, int *ssz, int *dsz)
+{
+ UpperLowerTbl_t *ultp;
+ unsigned char *tail;
+
+ if (s == NULL || *s == '\0') {
+ *ssz = *dsz = 0;
+ return;
+ }
+ if (!(*s & 0x80)) { /* ASCII */
+ *dsz = *ssz = 1;
+ *d = toupper(*s);
+ return;
+ }
+ tail = (unsigned char *)ldap_utf8next((char *)s);
+ *dsz = *ssz = tail - s;
+ switch(*ssz) {
+ case 1: /* ASCII */
+ *d = toupper(*s);
+ break;
+ case 2: /* 2 bytes */
+ if (*s < LU2S || *s > LU2E) { /* out of range */
+ memmove(d, s, *ssz);
+ break;
+ }
+ for (ultp = Lower2UpperTbl2[*s - LU2S];
+ ultp && ultp->lower && memcmp(s, ultp->lower, *ssz);
+ ultp++)
+ ;
+ if (!ultp) { /* out of range */
+ memmove(d, s, *ssz);
+ } else if (ultp->lower) { /* matched */
+ memmove(d, ultp->upper, ultp->tsz);
+ *dsz = ultp->tsz;
+ } else {
+ memmove(d, s, *ssz);
+ }
+ break;
+ case 3: /* 3 bytes */
+ if (*s != LU3S && *s != LU3E) { /* out of range */
+ memmove(d, s, *ssz);
+ break;
+ }
+ for (ultp = Lower2UpperTbl3[*s - LU3S];
+ ultp && ultp->lower && memcmp(s, ultp->lower, *ssz);
+ ultp++)
+ ;
+ if (!ultp) { /* out of range */
+ memmove(d, s, *ssz);
+ } else if (ultp->lower) { /* matched */
+ memmove(d, ultp->upper, *ssz);
+ } else {
+ memmove(d, s, *ssz);
+ }
+ break;
+ }
+ return;
+}
+
+/*
+ * slapi_utf8isLower: tests for a character that is a lower-case letter in
+ * UTF-8
+ *
+ * input: a UTF-8 character (could be multi-byte)
+ * output: 1 if the character is a lower-case letter
+ * 0 if the character is not a lower-case letter
+ */
+int
+slapi_UTF8ISLOWER(char *s)
+{
+ return slapi_utf8isLower((unsigned char *)s);
+}
+
+int
+slapi_utf8isLower(unsigned char *s)
+{
+ UpperLowerTbl_t *ultp;
+ unsigned char *next;
+ int sz;
+
+ if (s == NULL || *s == '\0') {
+ return 0;
+ }
+ if (!(*s & 0x80)) { /* ASCII */
+ return islower(*s);
+ }
+ next = (unsigned char *)ldap_utf8next((char *)s);
+ switch(sz = next - s) {
+ case 1: /* ASCII */
+ return islower(*s);
+ case 2:
+ if (*s < LU2S || *s > LU2E) { /* out of range */
+ return 0;
+ }
+ for (ultp = Lower2UpperTbl2[*s - LU2S];
+ ultp && ultp->lower && memcmp(s, ultp->lower, sz);
+ ultp++)
+ ;
+ if (!ultp) { /* out of range */
+ return 0;
+ } else if (ultp->lower) { /* matched */
+ return 1;
+ } else {
+ return 0;
+ }
+ case 3:
+ if (*s < LU3S || *s > LU3E) { /* out of range */
+ return 0;
+ }
+ for (ultp = Lower2UpperTbl3[*s - LU3S];
+ ultp && ultp->lower && memcmp(s, ultp->lower, sz);
+ ultp++)
+ ;
+ if (!ultp) { /* out of range */
+ return 0;
+ } else if (ultp->lower) { /* matched */
+ return 1;
+ } else {
+ return 0;
+ }
+ default:
+ return 0;
+ }
+}
+
+/*
+ * slapi_utf8casecmp: case-insensitive string compare for UTF-8 strings
+ *
+ * input: two UTF-8 strings (s0, s1) to be compared
+ * output: positive number, if s0 is after s1
+ * 0, if the two strings are identical ignoring the case
+ * negative number, if s1 is after s0
+ *
+ * Rules: If both UTF-8 strings are NULL or 0-length, 0 is returned.
+ * If one of the strings is NULL or 0-length, the NULL/0-length
+ * string is smaller.
+ * If one or both of the strings are not UTF-8, system provided
+ * strcasecmp is used.
+ * If one of the two strings contains no 8-bit characters,
+ * strcasecmp is used.
+ * The strings are compared after converted to lower-case UTF-8.
+ * Each character is compared from the beginning.
+ * Evaluation goes in this order:
+ * If the length of one character is shorter then the other,
+ * the difference of the two lengths is returned.
+ * If the length of the corresponsing characters is same,
+ * each byte in the characters is compared.
+ * If there's a difference between two bytes,
+ * the diff is returned.
+ * If one string is shorter then the other, the diff is returned.
+ *
+ * Notes: Don't use this function for collation
+ * 1) there's no notion of locale in this function.
+ * 2) it's UTF-8 code order, which is different from the locale
+ * based collation.
+ */
+int
+slapi_UTF8CASECMP(char *s0, char *s1)
+{
+ return slapi_utf8casecmp((unsigned char *)s0, (unsigned char *)s1);
+}
+
+int
+slapi_utf8casecmp(unsigned char *s0, unsigned char *s1)
+{
+ unsigned char *d0, *d1; /* store lower-case strings */
+ unsigned char *p0, *p1; /* current UTF-8 char */
+ unsigned char *n0, *n1; /* next UTF-8 char */
+ unsigned char *t0, *t1; /* tail of the strings */
+ unsigned char *x0, *x1; /* current byte in a char */
+ int i0, i1; /* length of characters */
+ int l0, l1; /* length of leftover */
+ int rval;
+ int has8_s0;
+ int has8_s1;
+
+ d0 = d1 = NULL;
+ if (s0 == NULL || *s0 == '\0') {
+ if (s1 == NULL || *s1 == '\0') {
+ rval = 0;
+ } else {
+ rval = -1; /* regardless s1, s0 < s1 */
+ }
+ goto end;
+ } else if (s1 == NULL || *s1 == '\0') {
+ rval = 1; /* regardless s0, s0 > s1 */
+ goto end;
+ }
+
+ has8_s0 = slapi_has8thBit(s0);
+ has8_s1 = slapi_has8thBit(s1);
+ if (has8_s0 == has8_s1) { /* both has-8th-bit or both do not */
+ if (has8_s0 == 0) { /* neither has-8th-bit */
+ rval = strcasecmp((char *)s0, (char *)s1);
+ goto end;
+ }
+ } else { /* one has and the other do not */
+ rval = strcasecmp((char *)s0, (char *)s1);
+ goto end;
+ }
+
+ d0 = slapi_utf8StrToLower(s0);
+ d1 = slapi_utf8StrToLower(s1);
+ if (d0 == NULL || d1 == NULL || /* either is not a UTF-8 string */
+ (d0 && *d0 == '\0') || (d1 && *d1 == '\0')) {
+ rval = strcasecmp((char *)s0, (char *)s1);
+ goto end;
+ }
+
+ p0 = d0;
+ p1 = d1;
+
+ t0 = d0 + strlen((char *)d0);
+ t1 = d1 + strlen((char *)d1);
+
+ rval = 0;
+ while (1) {
+ n0 = (unsigned char *)ldap_utf8next((char *)p0);
+ n1 = (unsigned char *)ldap_utf8next((char *)p1);
+ if (n0 > t0 || n1 > t1) {
+ break;
+ }
+
+ i0 = n0 - p0;
+ i1 = n1 - p1;
+ rval = i0 - i1;
+ if (rval) { /* length is different */
+ goto end;
+ }
+
+ /* i0 == i1: same length */
+ for (x0 = p0, x1 = p1; x0 < n0; x0++, x1++) {
+ rval = *x0 - *x1;
+ if (rval) {
+ goto end;
+ }
+ }
+
+ p0 = n0; p1 = n1; /* goto next */
+ }
+ /* finished scanning the shared part and check the leftover */
+ l0 = t0 - n0;
+ l1 = t1 - n1;
+ rval = l0 - l1;
+
+end:
+ if (d0)
+ slapi_ch_free((void **)&d0);
+ if (d1)
+ slapi_ch_free((void **)&d1);
+
+ return rval;
+}
+
+/*
+ * slapi_utf8ncasecmp: case-insensitive string compare (n chars) for UTF-8
+ * strings
+ *
+ * input: two UTF-8 strings (s0, s1) to be compared
+ * number or characters
+ * output: positive number, if s0 is after s1
+ * 0, if the two strings are identical ignoring the case
+ * negative number, if s1 is after s0
+ *
+ * Rules: Same as slapi_utf8casecmp except the n characters limit.
+ *
+ * Notes: Don't use this function for collation
+ * 1) there's no notion of locale in this function.
+ * 2) it's UTF-8 code order, which is different from the locale
+ * based collation.
+ * n characters, NOT n bytes
+ */
+int
+slapi_UTF8NCASECMP(char *s0, char *s1, int n)
+{
+ return slapi_utf8ncasecmp((unsigned char *)s0, (unsigned char *)s1, n);
+}
+
+int
+slapi_utf8ncasecmp(unsigned char *s0, unsigned char *s1, int n)
+{
+ unsigned char *d0, *d1; /* store lower-case strings */
+ unsigned char *p0, *p1; /* current UTF-8 char */
+ unsigned char *n0, *n1; /* next UTF-8 char */
+ unsigned char *t0, *t1; /* tail of the strings */
+ unsigned char *x0, *x1; /* current byte in a char */
+ int i0, i1; /* length of characters */
+ int l0, l1; /* length of leftover */
+ int cnt;
+ int rval;
+ int has8_s0;
+ int has8_s1;
+
+ d0 = d1 = NULL;
+ if (s0 == NULL || *s0 == '\0') {
+ if (s1 == NULL || *s1 == '\0') {
+ rval = 0;
+ } else {
+ rval = -1; /* regardless s1, s0 < s1 */
+ }
+ goto end;
+ } else if (s1 == NULL || *s1 == '\0') {
+ rval = 1; /* regardless s0, s0 > s1 */
+ goto end;
+ }
+
+ has8_s0 = slapi_has8thBit(s0);
+ has8_s1 = slapi_has8thBit(s1);
+ if (has8_s0 == has8_s1) { /* both has-8th-bit or both do not */
+ if (has8_s0 == 0) { /* neither has-8th-bit */
+ rval = strncasecmp((char *)s0, (char *)s1, n);
+ goto end;
+ }
+ } else { /* one has and the other do not */
+ rval = strncasecmp((char *)s0, (char *)s1, n);
+ goto end;
+ }
+
+ d0 = slapi_utf8StrToLower(s0);
+ d1 = slapi_utf8StrToLower(s1);
+
+ if (d0 == NULL || d1 == NULL || /* either is not a UTF-8 string */
+ (d0 && *d0 == '\0') || (d1 && *d1 == '\0')) {
+ rval = strncasecmp((char *)s0, (char *)s1, n);
+ goto end;
+ }
+
+ p0 = d0;
+ p1 = d1;
+
+ t0 = d0 + strlen((char *)d0);
+ t1 = d1 + strlen((char *)d1);
+
+ rval = 0;
+ cnt = 0;
+ while (1) {
+ n0 = (unsigned char *)ldap_utf8next((char *)p0);
+ n1 = (unsigned char *)ldap_utf8next((char *)p1);
+ if (n0 > t0 || n1 > t1 || cnt == n) {
+ break;
+ }
+
+ i0 = n0 - p0;
+ i1 = n1 - p1;
+ rval = i0 - i1;
+ if (rval) /* length is different */
+ goto end;
+
+ /* i0 == i1: same length */
+ for (x0 = p0, x1 = p1; x0 < n0; x0++, x1++) {
+ rval = *x0 - *x1;
+ if (rval)
+ goto end;
+ }
+
+ p0 = n0; p1 = n1; /* goto next */
+ cnt++;
+ }
+ if (cnt == n)
+ rval = 0;
+ else {
+ /* finished scanning the shared part and check the leftover */
+ l0 = t0 - n0;
+ l1 = t1 - n1;
+ rval = l0 - l1;
+ }
+
+end:
+ if (d0)
+ slapi_ch_free((void **)&d0);
+ if (d1)
+ slapi_ch_free((void **)&d1);
+
+ return rval;
+}
+
diff --git a/ldap/servers/slapd/util.c b/ldap/servers/slapd/util.c
new file mode 100644
index 00000000..6b6a5b0c
--- /dev/null
+++ b/ldap/servers/slapd/util.c
@@ -0,0 +1,601 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+/* util.c -- utility functions -- functions available form libslapd */
+#ifdef _WIN32
+#include <direct.h> /* for getcwd */
+#else
+#include <sys/socket.h>
+#include <sys/param.h>
+#include <unistd.h>
+#include <pwd.h>
+#endif
+#include <pk11func.h>
+#include "slap.h"
+#include "prtime.h"
+#include "prinrval.h"
+
+#define UTIL_ESCAPE_NONE 0
+#define UTIL_ESCAPE_HEX 1
+#define UTIL_ESCAPE_BACKSLASH 2
+
+static int special_np(unsigned char c)
+{
+ if(c < 32 || c > 126) {
+ return UTIL_ESCAPE_HEX;
+ } else if ((c== '"') || (c=='\\'))
+ {
+ return UTIL_ESCAPE_HEX;
+ }
+ return UTIL_ESCAPE_NONE;
+}
+
+static int special_np_and_punct(unsigned char c)
+{
+ if (c < 32 || c > 126 || c == '*') return UTIL_ESCAPE_HEX;
+ if (c == '\\' || c == '"') return UTIL_ESCAPE_BACKSLASH;
+ return UTIL_ESCAPE_NONE;
+}
+
+static int special_filter(unsigned char c)
+{
+ /*
+ * Escape all non-printing chars and double-quotes in addition
+ * to those required by RFC 2254 so that we can use the string
+ * in log files.
+ */
+ return (c < 32 ||
+ c > 126 ||
+ c == '*' ||
+ c == '(' ||
+ c == ')' ||
+ c == '\\' ||
+ c == '"') ? UTIL_ESCAPE_HEX : UTIL_ESCAPE_NONE;
+}
+
+static const char*
+do_escape_string (
+ const char* str,
+ int len, /* -1 means str is nul-terminated */
+ char buf[BUFSIZ],
+ int (*special)(unsigned char)
+)
+{
+ const char* s;
+ const char* last;
+ int esc;
+
+ if (str == NULL) {
+ *buf = '\0';
+ return buf;
+ }
+
+ if (len == -1) len = strlen (str);
+ if (len == 0) return str;
+
+ last = str + len - 1;
+ for (s = str; s <= last; ++s) {
+ if ( esc = (*special)((unsigned char)*s)) {
+ const char* first = str;
+ char* bufNext = buf;
+ int bufSpace = BUFSIZ - 4;
+ while (1) {
+ if (bufSpace < (s - first)) s = first + bufSpace - 1;
+ if (s > first) {
+ memcpy (bufNext, first, s - first);
+ bufNext += (s - first);
+ bufSpace -= (s - first);
+ }
+ if (s > last) {
+ break;
+ }
+ do {
+ *bufNext++ = '\\'; --bufSpace;
+ if (bufSpace < 2) {
+ memcpy (bufNext, "..", 2);
+ bufNext += 2;
+ goto bail;
+ }
+ if (esc == UTIL_ESCAPE_BACKSLASH) {
+ *bufNext++ = *s; --bufSpace;
+ } else { /* UTIL_ESCAPE_HEX */
+ sprintf (bufNext, "%02x", (unsigned)*(unsigned char*)s);
+ bufNext += 2; bufSpace -= 2;
+ }
+ } while (++s <= last &&
+ (esc = (*special)((unsigned char)*s)));
+ if (s > last) break;
+ first = s;
+ while ( (esc = (*special)((unsigned char)*s)) == UTIL_ESCAPE_NONE && s <= last) ++s;
+ }
+ bail:
+ *bufNext = '\0';
+ return buf;
+ }
+ }
+ return str;
+}
+
+/*
+ * Function: escape_string
+ * Arguments: str: string
+ * buf: a char array of BUFSIZ length, in which the escaped string will
+ * be returned.
+ * Returns: a pointer to buf, if str==NULL or it needed to be escaped, or
+ * str itself otherwise.
+ *
+ * This function should only be used for generating loggable strings.
+ */
+const char*
+escape_string (const char* str, char buf[BUFSIZ])
+{
+ return do_escape_string(str,-1,buf,special_np);
+}
+
+const char*
+escape_string_with_punctuation(const char* str, char buf[BUFSIZ])
+{
+ return do_escape_string(str,-1,buf,special_np_and_punct);
+}
+
+const char*
+escape_filter_value(const char* str, int len, char buf[BUFSIZ])
+{
+ return do_escape_string(str,len,buf,special_filter);
+}
+
+/* functions to convert between an entry and a set of mods */
+int slapi_mods2entry (Slapi_Entry **e, const char *idn, LDAPMod **iattrs)
+{
+ int i, rc = LDAP_SUCCESS;
+ LDAPMod **attrs= NULL;
+
+ PR_ASSERT (idn);
+ PR_ASSERT (iattrs);
+ PR_ASSERT (e);
+
+ attrs = normalize_mods2bvals((const LDAPMod **)iattrs);
+ PR_ASSERT (attrs);
+
+ /* Construct an entry */
+ *e = slapi_entry_alloc();
+ PR_ASSERT (*e);
+ slapi_entry_init(*e, slapi_ch_strdup(idn), NULL);
+
+ for (i = 0; rc==LDAP_SUCCESS && attrs[ i ]!=NULL; i++)
+ {
+ char *normtype;
+ Slapi_Value **vals;
+
+ normtype = slapi_attr_syntax_normalize(attrs[ i ]->mod_type);
+ valuearray_init_bervalarray(attrs[ i ]->mod_bvalues, &vals);
+ if (strcasecmp(normtype, SLAPI_USERPWD_ATTR) == 0)
+ {
+ pw_encodevals(vals);
+ }
+
+ /* set entry uniqueid - also adds attribute to the list */
+ if (strcasecmp(normtype, SLAPI_ATTR_UNIQUEID) == 0)
+ slapi_entry_set_uniqueid (*e, slapi_ch_strdup (slapi_value_get_string(vals[0])));
+ else
+ rc = slapi_entry_add_values_sv(*e, normtype, vals);
+
+ valuearray_free(&vals);
+ if (rc != LDAP_SUCCESS)
+ {
+ LDAPDebug(LDAP_DEBUG_ANY, "slapi_add_internal: add_values for type %s failed\n", normtype, 0, 0 );
+ slapi_entry_free (*e);
+ *e = NULL;
+ }
+ slapi_ch_free((void **) &normtype);
+ }
+ freepmods(attrs);
+
+ return rc;
+}
+
+int slapi_entry2mods (const Slapi_Entry *e, char **dn, LDAPMod ***attrs)
+{
+ Slapi_Mods smods;
+ Slapi_Attr *attr;
+ Slapi_Value **va;
+ char *type;
+ int rc;
+
+ PR_ASSERT (e && attrs);
+
+ if (dn)
+ *dn = slapi_ch_strdup (slapi_entry_get_dn ((Slapi_Entry *)e));
+ slapi_mods_init (&smods, 0);
+
+ rc = slapi_entry_first_attr(e, &attr);
+ while (rc == 0)
+ {
+ if ( NULL != ( va = attr_get_present_values( attr ))) {
+ slapi_attr_get_type(attr, &type);
+ slapi_mods_add_mod_values(&smods, LDAP_MOD_ADD, type, va );
+ }
+ rc = slapi_entry_next_attr(e, attr, &attr);
+ }
+
+ *attrs = slapi_mods_get_ldapmods_passout (&smods);
+ slapi_mods_done (&smods);
+
+ return 0;
+}
+
+/******************************************************************************
+*
+* normalize_mods2bvals
+*
+*
+*
+*
+*******************************************************************************/
+
+LDAPMod **
+normalize_mods2bvals(const LDAPMod **mods)
+{
+ int w, x, vlen, num_values, num_mods;
+ LDAPMod **normalized_mods;
+
+ if (mods == NULL)
+ {
+ return NULL;
+ }
+
+ /* first normalize the mods so they are bvalues */
+ /* count the number of mods -- sucks but should be small */
+ num_mods = 1;
+ for (w=0; mods[w] != NULL; w++) num_mods++;
+
+ normalized_mods = (LDAPMod **) slapi_ch_calloc(num_mods, sizeof(LDAPMod *));
+
+ for (w = 0; mods[w] != NULL; w++)
+ {
+ /* copy each mod into a normalized modbvalue */
+ normalized_mods[w] = (LDAPMod *) slapi_ch_calloc(1, sizeof(LDAPMod));
+ normalized_mods[w]->mod_op = mods[w]->mod_op | LDAP_MOD_BVALUES;
+
+ normalized_mods[w]->mod_type = slapi_ch_strdup(mods[w]->mod_type);
+
+ /*
+ * count the number of values -- kinda sucks but probably
+ * less expensive then reallocing, and num_values
+ * should typically be very small
+ */
+ num_values = 0;
+ if (mods[w]->mod_op & LDAP_MOD_BVALUES)
+ {
+ for (x = 0; mods[w]->mod_bvalues != NULL &&
+ mods[w]->mod_bvalues[x] != NULL; x++)
+ {
+ num_values++;
+ }
+ } else {
+ for (x = 0; mods[w]->mod_values[x] != NULL &&
+ mods[w]->mod_values[x] != NULL; x++)
+ {
+ num_values++;
+ }
+ }
+
+ if (num_values > 0)
+ {
+ normalized_mods[w]->mod_bvalues = (struct berval **)
+ slapi_ch_calloc(num_values + 1, sizeof(struct berval *));
+ } else {
+ normalized_mods[w]->mod_bvalues = NULL;
+ }
+
+ if (mods[w]->mod_op & LDAP_MOD_BVALUES)
+ {
+ for (x = 0; mods[w]->mod_bvalues != NULL &&
+ mods[w]->mod_bvalues[x] != NULL; x++)
+ {
+ normalized_mods[w]->mod_bvalues[x] = ber_bvdup(mods[w]->mod_bvalues[x]);
+ }
+ } else {
+ for (x = 0; mods[w]->mod_values != NULL &&
+ mods[w]->mod_values[x] != NULL; x++)
+ {
+ normalized_mods[w]->mod_bvalues[ x ] = (struct berval *)
+ slapi_ch_calloc(1, sizeof(struct berval));
+
+ vlen = strlen(mods[w]->mod_values[x]);
+ normalized_mods[w]->mod_bvalues[ x ]->bv_val =
+ slapi_ch_calloc(vlen + 1, sizeof(char));
+ memcpy(normalized_mods[w]->mod_bvalues[ x ]->bv_val,
+ mods[w]->mod_values[x], vlen);
+ normalized_mods[w]->mod_bvalues[ x ]->bv_val[vlen] = '\0';
+ normalized_mods[w]->mod_bvalues[ x ]->bv_len = vlen;
+ }
+ }
+
+ /* don't forget to null terminate it */
+ if (num_values > 0)
+ {
+ normalized_mods[w]->mod_bvalues[ x ] = NULL;
+ }
+ }
+
+ /* don't forget to null terminate the normalize list of mods */
+ normalized_mods[w] = NULL;
+
+ return(normalized_mods);
+}
+
+/*
+ * Return true if the given path is an absolute path, false otherwise
+ */
+int
+is_abspath(const char *path)
+{
+ if (path == NULL || *path == '\0') {
+ return 0; /* empty path is not absolute? */
+ }
+
+#if defined( XP_WIN32 )
+ if (path[0] == '/' || path[0] == '\\' ||
+ (isalpha(path[0]) && (path[1] == ':'))) {
+ return 1; /* Windows abs path */
+ }
+#else
+ if (path[0] == '/') {
+ return 1; /* unix abs path */
+ }
+#endif
+
+ return 0; /* not an abs path */
+}
+
+
+/*
+ * Take "relpath" and prepend the current working directory to it
+ * if it isn't an absolute pathname already. The caller is responsible
+ * for freeing the returned string.
+ */
+char *
+rel2abspath( char *relpath )
+{
+ char abspath[ MAXPATHLEN + 1 ];
+
+#if defined( _WIN32 )
+ CHAR szDrive[_MAX_DRIVE];
+ CHAR szDir[_MAX_DIR];
+ CHAR szFname[_MAX_FNAME];
+ CHAR szExt[_MAX_EXT];
+#define _PSEP "\\"
+#else
+#define _PSEP "/"
+#endif
+
+ if ( relpath == NULL ) {
+ return NULL;
+ }
+
+#if defined( _WIN32 )
+ memset (&szDrive, 0, sizeof (szDrive));
+ memset (&szDir, 0, sizeof (szDir));
+ memset (&szFname, 0, sizeof (szFname));
+ memset (&szExt, 0, sizeof (szExt));
+ _splitpath( relpath, szDrive, szDir, szFname, szExt );
+ if( szDrive[0] && szDir[0] )
+ return( slapi_ch_strdup( relpath ));
+ if ( relpath[ 0 ] == '/' || relpath[ 0 ] == '\\' ) {
+ return( slapi_ch_strdup( relpath ));
+#else
+ if ( relpath[ 0 ] == '/' ) {
+ return( slapi_ch_strdup( relpath ));
+ }
+#endif
+
+ if ( getcwd( abspath, MAXPATHLEN ) == NULL ) {
+ perror( "getcwd" );
+ LDAPDebug( LDAP_DEBUG_ANY, "Cannot determine current directory\n",
+ 0, 0, 0 );
+ exit( 1 );
+ }
+
+ if ( strlen( relpath ) + strlen( abspath ) + 1 > MAXPATHLEN ) {
+ LDAPDebug( LDAP_DEBUG_ANY, "Pathname \"%s" _PSEP "%s\" too long\n",
+ abspath, relpath, 0 );
+ exit( 1 );
+ }
+
+ if ( strcmp( relpath, "." )) {
+#if defined( _WIN32 )
+ if ( abspath[ 0 ] != '\0' && abspath[ strlen( abspath ) - 1 ] != '/' && abspath[ strlen( abspath ) - 1 ] != '\\' )
+#else
+ if ( abspath[ 0 ] != '\0' && abspath[ strlen( abspath ) - 1 ] != '/' ) {
+#endif
+ strcat( abspath, "/" );
+ }
+ strcat( abspath, relpath );
+ }
+ return( slapi_ch_strdup( abspath ));
+}
+
+
+/*
+ * Allocate a buffer large enough to hold a berval's
+ * value and a terminating null byte. The returned buffer
+ * is null-terminated. Returns NULL if bval is NULL or if
+ * bval->bv_val is NULL.
+ */
+char *
+slapi_berval_get_string_copy(const struct berval *bval)
+{
+ char *return_value = NULL;
+ if (NULL != bval && NULL != bval->bv_val)
+ {
+ return_value = slapi_ch_malloc(bval->bv_len + 1);
+ memcpy(return_value, bval->bv_val, bval->bv_len);
+ return_value[bval->bv_len] = '\0';
+ }
+ return return_value;
+}
+
+
+ /* Takes a return code supposed to be errno or from a plugin
+ which we don't expect to see and prints a handy log message */
+void slapd_nasty(char* str, int c, int err)
+{
+ char *msg = NULL;
+ char buffer[100];
+ sprintf(buffer,"%s BAD %d",str,c);
+ LDAPDebug(LDAP_DEBUG_ANY,"%s, err=%d %s\n",buffer,err,(msg = strerror( err )) ? msg : "");
+}
+
+/* ***************************************************
+ Random function (very similar to rand_r())
+ *************************************************** */
+int
+slapi_rand_r(unsigned int *randx)
+{
+ if (*randx)
+ {
+ PK11_RandomUpdate(randx, sizeof(*randx));
+ }
+ PK11_GenerateRandom((unsigned char *)randx, (int)sizeof(*randx));
+ return (int)(*randx & 0x7FFFFFFF);
+}
+
+/* ***************************************************
+ Random function (very similar to rand_r() but takes and returns an array)
+ Note: there is an identical function in plugins/pwdstorage/ssha_pwd.c.
+ That module can't use a libslapd function because the module is included
+ in libds_admin, which doesn't link to libslapd. Eventually, shared
+ functions should be moved to a shared library.
+ *************************************************** */
+void
+slapi_rand_array(void *randx, size_t len)
+{
+ PK11_RandomUpdate(randx, len);
+ PK11_GenerateRandom((unsigned char *)randx, (int)len);
+}
+
+/* ***************************************************
+ Random function (very similar to rand()...)
+ *************************************************** */
+int
+slapi_rand()
+{
+ unsigned int randx = 0;
+ return slapi_rand_r(&randx);
+}
+
+
+
+/************************************************************************
+Function: DS_Sleep(PRIntervalTime ticks)
+
+Purpose: To replace PR_Sleep()
+
+Author: Scott Hopson <shopson@netscape.com>
+
+Description:
+ Causes the current thread to wait for ticks number of intervals.
+
+ In UNIX this is accomplished by using select()
+ which should be supported across all UNIX platforms.
+
+ In WIN32 we simply use the Sleep() function which yields
+ for the number of milliseconds specified.
+
+************************************************************************/
+
+
+#if defined(_WIN32)
+
+#include "windows.h"
+
+
+void DS_Sleep(PRIntervalTime ticks)
+{
+DWORD mSecs = PR_IntervalToMilliseconds(ticks);
+
+ Sleep(mSecs);
+}
+
+#else /*** UNIX ***/
+
+
+#include <sys/time.h>
+
+
+void DS_Sleep(PRIntervalTime ticks)
+{
+long mSecs = PR_IntervalToMilliseconds(ticks);
+struct timeval tm;
+
+ tm.tv_sec = mSecs / 1000;
+ tm.tv_usec = (mSecs % 1000) * 1000;
+
+ select(0,NULL,NULL,NULL,&tm);
+}
+
+#endif
+
+
+/*****************************************************************************
+ * strarray2str(): convert the array of strings in "a" into a single
+ * space-separated string like:
+ * str1 str2 str3
+ * If buf is too small, the result will be truncated and end with "...".
+ * If include_quotes is non-zero, double quote marks are included around
+ * the string, e.g.,
+ * "str2 str2 str3"
+ *
+ * Returns: 0 if completely successful and -1 if result is truncated.
+ */
+int
+strarray2str( char **a, char *buf, size_t buflen, int include_quotes )
+{
+ int rc = 0; /* optimistic */
+ char *p = buf;
+ size_t totlen = 0;
+
+
+ if ( include_quotes ) {
+ if ( buflen < 3 ) {
+ return -1; /* not enough room for the quote marks! */
+ }
+ *p++ = '"';
+ ++totlen;
+ }
+
+ if ( NULL != a ) {
+ int ii;
+ size_t len = 0;
+ for ( ii = 0; a[ ii ] != NULL; ii++ ) {
+ if ( ii > 0 ) {
+ *p++ = ' ';
+ totlen++;
+ }
+ len = strlen( a[ ii ]);
+ if ( totlen + len > buflen - 5 ) {
+ strcpy ( p, "..." );
+ p += 3;
+ totlen += 3;
+ rc = -1;
+ break; /* result truncated */
+ } else {
+ strcpy( p, a[ ii ]);
+ p += len;
+ totlen += len;
+ }
+ }
+ }
+
+ if ( include_quotes ) {
+ *p++ = '"';
+ ++totlen;
+ }
+ buf[ totlen ] = '\0';
+
+ return( rc );
+}
+/*****************************************************************************/
diff --git a/ldap/servers/slapd/uuid.c b/ldap/servers/slapd/uuid.c
new file mode 100644
index 00000000..47576559
--- /dev/null
+++ b/ldap/servers/slapd/uuid.c
@@ -0,0 +1,893 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+/* uuid.c */
+
+/*
+** Copyright (c) 1990- 1993, 1996 Open Software Foundation, Inc.
+** Copyright (c) 1989 by Hewlett-Packard Company, Palo Alto, Ca. &
+** Digital Equipment Corporation, Maynard, Mass.
+** Copyright (c) 1998 Microsoft.
+** To anyone who acknowledges that this file is provided "AS IS"
+** without any express or implied warranty: permission to use, copy,
+** modify, and distribute this file for any purpose is hereby
+** granted without fee, provided that the above copyright notices and
+** this notice appears in all source code copies, and that none of
+** the names of Open Software Foundation, Inc., Hewlett-Packard
+** Company, or Digital Equipment Corporation be used in advertising
+** or publicity pertaining to distribution of the software without
+** specific, written prior permission. Neither Open Software
+** Foundation, Inc., Hewlett-Packard Company, Microsoft, nor Digital Equipment
+* Corporation makes any representations about the suitability of
+** this software for any purpose.
+*/
+
+
+#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <time.h>
+#include <pk11func.h>
+
+#ifdef _WIN32
+#include <sys/stat.h> /* for S_IREAD and S_IWRITE */
+#else
+#include <sys/types.h>
+#include <sys/time.h>
+#include <sys/sysinfo.h>
+#include <sys/utsname.h>
+#include <unistd.h> /* gethostname() */
+#endif
+
+
+#include "slap.h"
+#include "uuid.h"
+#include "sechash.h"
+
+#define SEQ_PER_SEC 10000000 /* number of 100ns intervals in a sec */
+#define STATE_FILE "state" /* file that contains generator's state */
+#define STATE_ATTR "nsState" /* attribute that stores state info */
+#define MODULE "uuid" /* for logging */
+#define UPDATE_INTERVAL 60000 /* 1 minute */
+#define NEED_TIME_UPDATE -1
+
+/* generates uuid in singlethreaded environment */
+static int uuid_create_st(guid_t *uuid);
+/* generates uuid in multithreaded environment */
+static int uuid_create_mt(guid_t *uuid);
+/* periodically called to update generator's state - mt case only */
+static void uuid_update_state (time_t when, void *arg);
+/* creates uuid in v1 format using current state info */
+static void format_uuid_v1(guid_t *uuid, uuid_time_t timestamp, unsigned16 clock_seq);
+/* generates uuid in v3 format */
+static void format_uuid_v3(guid_t *uuid, unsigned char hash[16]);
+/* reads state from a file or DIT entry */
+static int read_state (const char *configDir, const Slapi_DN *configDN, PRBool *newState);
+/* reads state from a file */
+static int read_state_from_file (const char *configDir);
+/* read state information from DIT */
+static int read_state_from_entry (const Slapi_DN *configDN);
+/* writes state to persistent store: file or dit */
+static int write_state(PRBool newState);
+/* writes state to a file */
+static int write_state_to_file();
+/* writes state to dit */
+static int write_state_to_entry(PRBool newState);
+/* add state entry to the dit */
+static int add_state_entry ();
+/* modify state entry in the dit */
+static int modify_state_entry ();
+/* generates timestamp for the next uuid - single threaded */
+static uuid_time_t update_time();
+/* generates timestamp for the next uuid - multithreaded threaded */
+static int update_time_mt(uuid_time_t *timestamp, unsigned16 *clock_seq);
+/* retrieves or generates nodeid */
+static int get_node_identifier(uuid_node_t *node);
+/* returns current time in the UTC format */
+static void get_system_time(uuid_time_t *uuid_time);
+/* generates random value - used to set clock sequence */
+static unsigned16 true_random(void);
+/* generate random info buffer to generate nodeid */
+static void get_random_info(unsigned char seed[16]);
+
+/* UUID generator state stored persistently */
+typedef struct
+{
+ uuid_time_t timestamp; /* saved timestamp */
+ uuid_node_t node; /* saved node ID */
+ unsigned16 clockseq; /* saved clock sequence */
+ unsigned8 last_update;/* flags the update made during server sutdown */
+} uuid_gen_state;
+
+/* generator state plus data to support it */
+typedef struct
+{
+ uuid_gen_state genstate; /* generator state */
+ int time_seq; /* sequence number to account for clock
+ granularity; not written to disk */
+ PRBool initialized; /* uniqueid successfully initialized */
+ PRBool mtGen; /* multithreaded generation */
+ PRLock *lock; /* lock to protect state */
+ PRFileDesc *fd; /* fd for the state file */
+ Slapi_DN *configDN; /* db entry that contains state info */
+} uuid_state;
+
+static unsigned int uuid_seed = 0; /* seed for the random generator */
+
+ uuid_state _state; /* generator's state */
+
+/* uuid_init -- initializes uuid layer */
+int uuid_init (const char *configDir, const Slapi_DN *configDN, PRBool mtGen)
+{
+ int rt;
+ PRBool newState = PR_FALSE;
+
+ if (_state.initialized)
+ {
+ slapi_log_error (SLAPI_LOG_FATAL, MODULE,
+ "uuid_init: generator is already initialized\n");
+ return UUID_SUCCESS;
+ }
+
+ memset (&_state, 0, sizeof (_state));
+
+ /* get saved state */
+ rt = read_state(configDir, configDN, &newState);
+ if (rt != UUID_SUCCESS)
+ {
+ slapi_log_error (SLAPI_LOG_FATAL, MODULE,
+ "uuid_init: failed to get generator's state\n");
+ uuid_cleanup ();
+ return rt;
+ }
+
+ _state.mtGen = mtGen;
+
+ /* this is multithreaded generation - create lock */
+ if (_state.mtGen)
+ {
+ _state.lock = PR_NewLock();
+ if (!_state.lock)
+ {
+ PRErrorCode prerr = PR_GetError();
+ slapi_log_error (SLAPI_LOG_FATAL, MODULE, "uuid_init: "
+ "failed to create state lock; " SLAPI_COMPONENT_NAME_NSPR " error %d (%s).\n",
+ prerr, slapd_pr_strerror(prerr));
+ uuid_cleanup ();
+ return UUID_LOCK_ERROR;
+ }
+ }
+
+ /* save the state */
+ rt = write_state(newState);
+ /* can't proceede if the state can't be written */
+ if (rt != UUID_SUCCESS)
+ {
+ if (slapi_config_get_readonly() &&
+ (rt == UUID_LDAP_ERROR)) {
+ /*
+ * If server is readonly and error is UUID_LDAP_ERROR
+ * we can continue.
+ */
+ slapi_log_error (SLAPI_LOG_FATAL, MODULE, "Warning: "
+ "The server is in read-only mode, therefore the unique ID generator cannot be used. "
+ "Do not use this server in any replication agreement\n");
+ }
+ else {
+ slapi_log_error (SLAPI_LOG_FATAL, MODULE, "uuid_init: "
+ "failed to save generator's state.\n");
+ uuid_cleanup ();
+ return rt;
+ }
+ }
+
+ /* schedule update task for multithreaded generation */
+ if (_state.mtGen)
+ slapi_eq_repeat(uuid_update_state, NULL, (time_t)0, UPDATE_INTERVAL);
+
+ _state.initialized = PR_TRUE;
+ return UUID_SUCCESS;
+}
+
+/* uuid_cleanup -- saves state, destroys generator data */
+void uuid_cleanup ()
+{
+ if (_state.initialized)
+ {
+ _state.genstate.last_update = 1;
+ write_state (PR_FALSE);
+ }
+
+ if (_state.lock)
+ PR_DestroyLock (_state.lock);
+
+ if (_state.fd)
+ PR_Close (_state.fd);
+
+ if (_state.configDN)
+ slapi_sdn_free(&_state.configDN);
+
+ memset (&_state, 0, sizeof (_state));
+}
+
+/* uuid_create - main generating function */
+int uuid_create(guid_t *uuid)
+{
+ if (_state.mtGen)
+ return uuid_create_mt(uuid);
+ else
+ return uuid_create_st(uuid);
+}
+
+/* uuid_compare -- Compare two UUID's "lexically" and return
+ -1 u1 is lexically before u2
+ 0 u1 is equal to u2
+ 1 u1 is lexically after u2
+
+ Note: lexical ordering is not temporal ordering!
+*/
+#define CHECK(f1, f2) if (f1 != f2) return f1 < f2 ? -1 : 1;
+int uuid_compare(const guid_t *u1, const guid_t *u2)
+{
+ int i;
+
+ CHECK(u1->time_low, u2->time_low);
+ CHECK(u1->time_mid, u2->time_mid);
+ CHECK(u1->time_hi_and_version, u2->time_hi_and_version);
+ CHECK(u1->clock_seq_hi_and_reserved, u2->clock_seq_hi_and_reserved);
+ CHECK(u1->clock_seq_low, u2->clock_seq_low)
+ for (i = 0; i < 6; i++)
+ {
+ if (u1->node[i] < u2->node[i])
+ return -1;
+ if (u1->node[i] > u2->node[i])
+ return 1;
+ }
+ return 0;
+}
+
+/* uuid_format -- converts UUID to its string representation
+ buffer should be at list 40 bytes long
+*/
+
+void uuid_format(const guid_t *u, char *buff)
+{
+ sprintf(buff, "%8.8lx-%4.4x-%4.4x-%2.2x%2.2x-%2.2x%2.2x%2.2x%2.2x%2.2x%2.2x",
+ u->time_low, u->time_mid, u->time_hi_and_version,
+ u->clock_seq_hi_and_reserved, u->clock_seq_low, u->node[0],
+ u->node[1], u->node[2], u->node[3], u->node[4], u->node[5]);
+}
+
+/* uuid_create_from_name -- create a UUID using a "name" from a "name space"
+ */
+void uuid_create_from_name(guid_t * uuid, /* resulting UUID */
+ const guid_t nsid, /* UUID to serve as context, so identical
+ names from different name spaces generate
+ different UUIDs */
+ const void * name, /* the name from which to generate a UUID */
+ int namelen) /* the length of the name */
+{
+ PK11Context *c = NULL;
+
+ unsigned char hash[16];
+ unsigned int hashLen;
+ guid_t net_nsid; /* context UUID in network byte order */
+
+ memset(hash, 0, 16);
+
+ /* put name space ID in network byte order so it hashes the same
+ no matter what endian machine we're on */
+
+ memset(&net_nsid, 0, sizeof(guid_t));
+ net_nsid.time_low = PR_htonl(nsid.time_low);
+ net_nsid.time_mid = PR_htons(nsid.time_mid);
+ net_nsid.time_hi_and_version = PR_htons(nsid.time_hi_and_version);
+ net_nsid.clock_seq_hi_and_reserved=nsid.clock_seq_hi_and_reserved;
+ net_nsid.clock_seq_low=nsid.clock_seq_low;
+ strncpy((char *)net_nsid.node, (char *)nsid.node, 6);
+
+ c = PK11_CreateDigestContext(SEC_OID_MD5);
+ if (c != NULL) {
+ PK11_DigestBegin(c);
+ PK11_DigestOp(c, (unsigned char *)&net_nsid, sizeof(net_nsid));
+ PK11_DigestOp(c, (unsigned char *)name, namelen);
+ PK11_DigestFinal(c, hash, &hashLen, 16);
+
+ /* the hash is in network byte order at this point */
+ format_uuid_v3(uuid, hash);
+
+ PK11_DestroyContext(c, PR_TRUE);
+ }
+ else { /* Probably desesperate but at least deterministic... */
+ memset(&uuid, 0, sizeof(uuid));
+ }
+}
+
+/* Helper Functions */
+
+/* uuid_create_st -- singlethreaded generation */
+static int uuid_create_st(guid_t *uuid)
+{
+ uuid_time_t timestamp;
+
+ /* generate new time and save it in the state */
+ timestamp = update_time ();
+
+ /* stuff fields into the UUID */
+ format_uuid_v1(uuid, timestamp, _state.genstate.clockseq);
+
+ return UUID_SUCCESS;
+}
+
+/* uuid_create -- multithreaded generation */
+static int uuid_create_mt(guid_t *uuid)
+{
+ uuid_time_t timestamp;
+ unsigned16 clock_seq;
+
+ /* just bumps time sequence number. the actual
+ time calls are made by a uuid_update_state */
+ update_time_mt (&timestamp, &clock_seq);
+
+ if (timestamp == NEED_TIME_UPDATE)
+ {
+ slapi_log_error (SLAPI_LOG_FATAL, MODULE, "uuid_create_mt: generator ran "
+ "out of sequence numbers.\n");
+ return UUID_TIME_ERROR;
+ }
+
+ /* stuff fields into UUID */
+ format_uuid_v1(uuid, timestamp, clock_seq);
+
+ return UUID_SUCCESS;
+}
+
+/* uuid_update_state -- called periodically to update generator's state
+ (multithreaded case only)
+ */
+static void uuid_update_state (time_t when, void *arg)
+{
+ uuid_time_t timestamp;
+
+ get_system_time (&timestamp);
+
+ /* time has not changed since last call - return */
+ if (timestamp == _state.genstate.timestamp)
+ return;
+
+ PR_Lock (_state.lock);
+ /* clock was set backward - insure uuid uniquness by incrementing clock sequence */
+ if (timestamp < _state.genstate.timestamp)
+ _state.genstate.clockseq ++;
+
+ _state.genstate.timestamp = timestamp;
+ _state.time_seq = 0;
+
+ PR_Unlock (_state.lock);
+}
+
+/* read_state -- read UUID generator state from non-volatile store.
+*/
+static int read_state(const char *configDir, const Slapi_DN *configDN, PRBool *newState)
+{
+ uuid_time_t timestamp;
+ int rt;
+
+ if (configDN)
+ rt = read_state_from_entry (configDN);
+ else
+ rt = read_state_from_file (configDir);
+
+ if (rt == UUID_NOTFOUND)
+ *newState = PR_TRUE;
+ else
+ *newState = PR_FALSE;
+
+ if (rt != UUID_SUCCESS && rt != UUID_NOTFOUND) /* fatal error - bail out */
+ {
+ slapi_log_error (SLAPI_LOG_FATAL, MODULE,
+ "read_state: failed to get generator's state\n");
+ return rt;
+ }
+
+ /* get current time and nodeid */
+ get_system_time(&timestamp);
+ if (*newState) /* state info is missing - generate */
+ {
+ get_node_identifier (&_state.genstate.node);
+ _state.genstate.clockseq = true_random();
+ }
+ else if(_state.genstate.last_update != 1)
+ {
+ /* clock sequence should be randomized and not just incremented
+ because server's clock could have been set back before the
+ server crashed in which case clock sequence was incremented */
+ _state.genstate.clockseq = true_random();
+ }
+ else if (timestamp <= _state.genstate.timestamp)
+ {
+ _state.genstate.clockseq ++;
+ }
+
+ _state.genstate.timestamp = timestamp;
+ _state.time_seq = 0;
+ /* need to clear this field so that we know if the state information
+ is written during shutdown (in which case this flag is set to 1 */
+ _state.genstate.last_update = 0;
+
+ return UUID_SUCCESS;
+}
+
+/* read_state_from_file -- read generator state from file.
+*/
+static int read_state_from_file (const char *configDir)
+{
+ char *path;
+ int rt;
+
+ if (configDir == NULL || configDir[0] == '\0')
+ { /* this directory */
+ path = (char*)slapi_ch_malloc(strlen (STATE_FILE) + 1);
+ if (path == NULL)
+ {
+ slapi_log_error (SLAPI_LOG_FATAL, MODULE, "read_state: "
+ "memory allocation failed.\n");
+ return (UUID_MEMORY_ERROR);
+ }
+
+ strcpy (path, STATE_FILE);
+ }
+ else
+ {
+ path = (char*)slapi_ch_malloc(strlen(configDir) + strlen(STATE_FILE) + 2);
+ if (path == NULL)
+ {
+ slapi_log_error (SLAPI_LOG_FATAL, MODULE, "read_state: "
+ "memory allocation failed.\n");
+ return (UUID_MEMORY_ERROR);
+ }
+ sprintf (path, "%s/%s", configDir, STATE_FILE);
+ }
+
+ /* open or create state file for read/write and keep it in sync */
+ _state.fd = PR_Open(path, PR_RDWR | PR_CREATE_FILE | PR_SYNC,
+ SLAPD_DEFAULT_FILE_MODE);
+ slapi_ch_free ((void**)&path);
+ if (!_state.fd)
+ {
+ PRErrorCode prerr = PR_GetError();
+ slapi_log_error (SLAPI_LOG_FATAL, MODULE, "read_state: "
+ "failed to open state file - %s; " SLAPI_COMPONENT_NAME_NSPR " error %d (%s).\n",
+ path, prerr, slapd_pr_strerror(prerr));
+ return (UUID_IO_ERROR);
+ }
+
+ rt = PR_Read (_state.fd, &_state.genstate, sizeof(uuid_gen_state));
+ if (rt == 0) /* new state */
+ {
+ return UUID_NOTFOUND;
+ }
+
+ if (rt == -1)
+ {
+ PRErrorCode prerr = PR_GetError();
+ slapi_log_error (SLAPI_LOG_FATAL, MODULE, "read_state: "
+ "failed to read state information; " SLAPI_COMPONENT_NAME_NSPR " error %d (%s).\n",
+ prerr, slapd_pr_strerror(prerr));
+ return (UUID_IO_ERROR);
+ }
+
+ return(UUID_SUCCESS);
+}
+
+/* read_state_from_entry -- read generator state from DIT.
+*/
+static int read_state_from_entry (const Slapi_DN *configDN)
+{
+ Slapi_PBlock *pb;
+ int res, rt;
+ Slapi_Entry **entries;
+ Slapi_Attr *attr;
+ Slapi_Value *value;
+ const struct berval *bv;
+
+ _state.configDN = slapi_sdn_dup (configDN);
+ pb = slapi_search_internal(slapi_sdn_get_ndn (configDN), LDAP_SCOPE_BASE,
+ "objectclass=*", NULL, NULL, 0);
+
+ if (pb == NULL)
+ {
+ /* the only time NULL pb is returned is when memory allocation fails */
+ slapi_log_error (SLAPI_LOG_FATAL, MODULE, "read_state_from_entry: "
+ "NULL pblock returned from search\n");
+ return UUID_MEMORY_ERROR;
+ }
+
+ slapi_pblock_get(pb, SLAPI_PLUGIN_INTOP_RESULT, &res);
+
+ if (res == LDAP_NO_SUCH_OBJECT)
+ {
+ rt = UUID_NOTFOUND;
+ goto done;
+ }
+
+ if (res != LDAP_SUCCESS)
+ {
+ slapi_log_error (SLAPI_LOG_FATAL, MODULE, "read_state_from_entry: "
+ "search operation failed; LDAP error - %d\n", res);
+ rt = UUID_LDAP_ERROR;
+ goto done;
+ }
+
+ slapi_pblock_get(pb, SLAPI_PLUGIN_INTOP_SEARCH_ENTRIES, &entries);
+ if (entries == NULL || entries[0] == NULL)
+ {
+ rt = UUID_UNKNOWN_ERROR;
+ goto done;
+ }
+
+ /* get state info */
+ rt = slapi_entry_attr_find (entries[0], STATE_ATTR, &attr);
+ if (rt != LDAP_SUCCESS)
+ {
+ rt = UUID_FORMAT_ERROR;
+ goto done;
+ }
+
+ slapi_attr_first_value(attr,&value);
+ if (value == NULL)
+ {
+ rt = UUID_FORMAT_ERROR;
+ goto done;
+ }
+
+ bv = slapi_value_get_berval(value);
+ if (bv == NULL || bv->bv_val == NULL || bv->bv_len != sizeof (_state.genstate))
+ {
+ rt = UUID_FORMAT_ERROR;
+ goto done;
+ }
+
+ memcpy (&(_state.genstate), bv->bv_val, bv->bv_len);
+
+done:;
+ if (pb)
+ {
+ slapi_free_search_results_internal(pb);
+ slapi_pblock_destroy(pb);
+ }
+
+ return rt;
+}
+
+/* write_state -- save UUID generator state back to non-volatile
+ storage. Writes immediately to the disk
+*/
+
+static int write_state (PRBool newState)
+{
+ if (_state.configDN) /* write to DIT */
+ return write_state_to_entry (newState);
+ else /* write to a file */
+ return write_state_to_file ();
+}
+
+/* write_state_to_file -- stores state to state file
+*/
+static int write_state_to_file()
+{
+ int rt;
+
+ rt = PR_Seek (_state.fd, 0, PR_SEEK_SET);
+ if (rt == -1)
+ {
+ PRErrorCode prerr = PR_GetError();
+ slapi_log_error (SLAPI_LOG_FATAL, MODULE, "write_state: "
+ "failed to rewind state file; " SLAPI_COMPONENT_NAME_NSPR " error %d (%s).\n",
+ prerr, slapd_pr_strerror(prerr));
+ return UUID_IO_ERROR;
+ }
+
+ rt = PR_Write (_state.fd, &_state.genstate, sizeof (uuid_gen_state));
+ if (rt == -1)
+ {
+ PRErrorCode prerr = PR_GetError();
+ slapi_log_error (SLAPI_LOG_FATAL, MODULE, "write_state: "
+ "failed to update state file; " SLAPI_COMPONENT_NAME_NSPR " error %d (%s).\n",
+ prerr, slapd_pr_strerror(prerr));
+
+ return (UUID_IO_ERROR);
+ }
+
+ return (UUID_SUCCESS);
+}
+
+/* write_state_to_entry -- stores state to state file
+*/
+static int write_state_to_entry(PRBool newState) {
+ if (newState)
+ return add_state_entry ();
+ else
+ return modify_state_entry ();
+}
+
+/* add_state_entry -- add state entry to the dit */
+static int add_state_entry ()
+{
+ struct berval *vals[2];
+ struct berval val;
+ Slapi_Entry *e;
+ Slapi_PBlock *pb = NULL;
+ const char *dn = slapi_sdn_get_ndn (_state.configDN);
+ int rt;
+
+ vals[0] = &val;
+ vals[1] = NULL;
+
+ e = slapi_entry_alloc();
+ slapi_entry_set_dn(e, slapi_ch_strdup(dn));
+
+ /* Set the objectclass attribute */
+ val.bv_val = "top";
+ val.bv_len = strlen (val.bv_val);
+ slapi_entry_add_values(e, "objectclass", vals);
+
+ val.bv_val = "extensibleObject";
+ val.bv_len = strlen (val.bv_val);
+ slapi_entry_add_values(e, "objectclass", vals);
+
+ /* Set state attribute */
+ val.bv_val = (char*)&(_state.genstate);
+ val.bv_len = sizeof (_state.genstate);
+ slapi_entry_add_values(e, STATE_ATTR, vals);
+
+ /* this operation frees the entry */
+ pb = slapi_add_entry_internal(e, 0, 0 /* log_change */);
+ if (pb == NULL)
+ {
+ /* the only time NULL pb is returned is when memory allocation fails */
+ slapi_log_error (SLAPI_LOG_FATAL, MODULE, "add_state_entry: "
+ "NULL pblock returned from search\n");
+ return UUID_MEMORY_ERROR;
+ }
+ else
+ {
+ slapi_pblock_get( pb, SLAPI_PLUGIN_INTOP_RESULT, &rt);
+ slapi_ch_free((void **) &pb);
+ }
+
+ if (rt != LDAP_SUCCESS)
+ {
+ slapi_log_error (SLAPI_LOG_FATAL, MODULE, "add_state_entry: "
+ "add operation failed; LDAP error - %d.\n", rt);
+ return UUID_LDAP_ERROR;
+ }
+
+ slapi_log_error (SLAPI_LOG_HOUSE, MODULE, "add_state_entry: "
+ "successfully added generator's state entry");
+
+ return UUID_SUCCESS;
+}
+
+/* modify_state_entry -- modify state entry in the dit */
+static int modify_state_entry ()
+{
+ int res;
+ Slapi_Mods mods;
+ struct berval *vals[2];
+ struct berval val;
+ Slapi_PBlock *pb;
+
+ val.bv_val = (char*)&(_state.genstate);
+ val.bv_len = sizeof (_state.genstate);
+ vals[0] = &val;
+ vals[1] = NULL;
+
+ slapi_mods_init (&mods, 1);
+ slapi_mods_add_modbvps(&mods, LDAP_MOD_REPLACE | LDAP_MOD_BVALUES, STATE_ATTR, vals);
+ pb = slapi_modify_internal(slapi_sdn_get_ndn (_state.configDN),
+ slapi_mods_get_ldapmods_byref(&mods), NULL, 0);
+ slapi_mods_done(&mods);
+
+ if (pb == NULL)
+ {
+ /* the only time NULL pb is returned is when memory allocation fails */
+ slapi_log_error (SLAPI_LOG_FATAL, MODULE, "modify_state_entry: "
+ "NULL pblock returned from search\n");
+ return UUID_MEMORY_ERROR;
+ }
+
+ slapi_pblock_get(pb, SLAPI_PLUGIN_INTOP_RESULT, &res);
+ slapi_pblock_destroy(pb);
+ if (res != LDAP_SUCCESS)
+ {
+ slapi_log_error (SLAPI_LOG_FATAL, MODULE, "modify_state_entry: "
+ "update operation failed; LDAP error - %d.\n", res);
+
+ return UUID_LDAP_ERROR;
+ }
+
+ slapi_log_error (SLAPI_LOG_HOUSE, MODULE, "modify_state_entry: "
+ "successfully updated generator's state entry");
+ return UUID_SUCCESS;
+}
+
+/* update_time -- updates time portion of the generators state
+ for singlethreaded generation
+*/
+static uuid_time_t update_time()
+{
+ uuid_time_t time_now;
+
+ get_system_time(&time_now);
+
+ /* time was turned back - need to change clocksequence */
+ if (time_now < _state.genstate.timestamp)
+ {
+ _state.genstate.clockseq ++;
+ _state.genstate.timestamp = time_now;
+ _state.time_seq = 0;
+ return _state.genstate.timestamp;
+ }
+
+ /* go into loop if the time has not changed since last call */
+ while (time_now == _state.genstate.timestamp)
+ {
+ /* if we still have sequence numbers to give to the
+ timestamp, use it and get out of the loop */
+ if (_state.time_seq < SEQ_PER_SEC - 1)
+ {
+ _state.time_seq ++;
+ break;
+ }
+
+ /* this should never happen because we don't generate more that 10 mln ids/sec */
+ DS_Sleep (PR_MillisecondsToInterval(500));
+ get_system_time(&time_now);
+ }
+
+ /* system time has changed - clear sequence number and
+ update last time */
+ if (time_now > _state.genstate.timestamp)
+ {
+ _state.time_seq = 0;
+ _state.genstate.timestamp = time_now;
+ }
+
+ return _state.genstate.timestamp + _state.time_seq;
+}
+
+/* update_time_mt -- this function updates time sequence part of generators state.
+ This function should be used in the multithreaded environment
+ only.
+*/
+static int update_time_mt (uuid_time_t *timestamp, unsigned16 *clock_seq)
+{
+
+ PR_Lock (_state.lock);
+
+ /* we ran out time sequence numbers because
+ uuid_update_state function is not called
+ frequently enough */
+ if (_state.time_seq >= SEQ_PER_SEC - 1)
+ {
+ _state.time_seq = NEED_TIME_UPDATE;
+ slapi_log_error (SLAPI_LOG_FATAL, MODULE, "update_time_mt: "
+ "ran out of time sequence numbers; "
+ "uuid_update_state must be called\n");
+
+ PR_Unlock (_state.lock);
+ return (UUID_TIME_ERROR);
+ }
+
+ _state.time_seq++;
+
+ *timestamp = _state.genstate.timestamp + _state.time_seq;
+ *clock_seq = _state.genstate.clockseq;
+
+ PR_Unlock (_state.lock);
+
+ return UUID_SUCCESS;
+}
+
+/* format_uuid_v1 -- make a UUID from the timestamp, clockseq,
+ and node ID
+*/
+static void format_uuid_v1(guid_t * uuid, uuid_time_t timestamp, unsigned16 clock_seq)
+{
+ /* Construct a version 1 uuid with the information we've gathered
+ * plus a few constants. */
+
+ uuid->time_low = (unsigned32)(timestamp & 0xFFFFFFFF);
+ uuid->time_mid = (unsigned16)((timestamp >> 32) & 0xFFFF);
+ uuid->time_hi_and_version = (unsigned16)
+ ((timestamp >> 48) & 0x0FFF);
+ uuid->time_hi_and_version |= (1 << 12);
+ uuid->clock_seq_low = clock_seq & 0xFF;
+ uuid->clock_seq_hi_and_reserved = (unsigned8)(clock_seq & 0x3F00) >> 8;
+ uuid->clock_seq_hi_and_reserved |= 0x80;
+ memcpy(&uuid->node, &_state.genstate.node, sizeof (uuid->node));
+}
+
+/* format_uuid_v3 -- make a UUID from a (pseudo)random 128 bit number
+*/
+static void format_uuid_v3(guid_t * uuid, unsigned char hash[16])
+{
+ /* Construct a version 3 uuid with the (pseudo-)random number
+ * plus a few constants. */
+
+ memcpy(uuid, hash, sizeof(guid_t));
+
+ /* convert UUID to local byte order */
+ ntohl(uuid->time_low);
+ ntohs(uuid->time_mid);
+ ntohs(uuid->time_hi_and_version);
+
+ /* put in the variant and version bits */
+ uuid->time_hi_and_version &= 0x0FFF;
+ uuid->time_hi_and_version |= (3 << 12);
+ uuid->clock_seq_hi_and_reserved &= 0x3F;
+ uuid->clock_seq_hi_and_reserved |= 0x80;
+}
+
+/* system dependent call to get IEEE node ID.
+ This sample implementation generates a random node ID
+ Assumes that configDir was tested for validity by
+ the higher layer
+*/
+static int get_node_identifier (uuid_node_t *node)
+{
+ unsigned char seed[16]= {0};
+
+#ifdef USE_NIC
+ /* ONREPL - code to use NIC address would go here; Currently, we use
+ cryptographic random number to avoid state sharing among
+ servers running on the same host. See UniqueID Generator
+ docs for more info.
+ */
+#endif
+ get_random_info(seed);
+ seed[0] |= 0x80;
+ memcpy (node, seed, sizeof (uuid_node_t));
+
+ return UUID_SUCCESS;
+}
+
+/* call to get the current system time. Returned as 100ns ticks
+ since Oct 15, 1582, but resolution may be less than 100ns.
+*/
+
+static void get_system_time(uuid_time_t *uuid_time)
+{
+ time_t cur_time;
+
+ cur_time = current_time ();
+
+ /* Offset between UUID formatted times and time() formatted times.
+ UUID UTC base time is October 15, 1582. time() base time is January 1, 1970.*/
+ *uuid_time = cur_time * SEQ_PER_SEC + I64(0x01B21DD213814000);
+}
+
+/* ONREPL */
+
+/* true_random -- generate a crypto-quality random number.
+*/
+static unsigned16 true_random(void)
+{
+ static int inited = 0;
+
+ if (!inited)
+ {
+ uuid_seed = slapi_rand();
+ inited = 1;
+ }
+
+ return (slapi_rand_r(&uuid_seed));
+}
+
+static void get_random_info(unsigned char seed[16])
+{
+ slapi_rand_array(seed, sizeof(seed));
+}
diff --git a/ldap/servers/slapd/uuid.h b/ldap/servers/slapd/uuid.h
new file mode 100644
index 00000000..403b8ed7
--- /dev/null
+++ b/ldap/servers/slapd/uuid.h
@@ -0,0 +1,116 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+/* uuid.h - interface to uuid layer. UUID is generated in accordance
+ with UUIDs and GUIDs IETF draft
+ */
+
+/*
+** Copyright (c) 1990- 1993, 1996 Open Software Foundation, Inc.
+** Copyright (c) 1989 by Hewlett-Packard Company, Palo Alto, Ca. &
+** Digital Equipment Corporation, Maynard, Mass.
+** Copyright (c) 1998 Microsoft.
+** To anyone who acknowledges that this file is provided "AS IS"
+** without any express or implied warranty: permission to use, copy,
+** modify, and distribute this file for any purpose is hereby
+** granted without fee, provided that the above copyright notices and
+** this notice appears in all source code copies, and that none of
+** the names of Open Software Foundation, Inc., Hewlett-Packard
+** Company, or Digital Equipment Corporation be used in advertising
+** or publicity pertaining to distribution of the software without
+** specific, written prior permission. Neither Open Software
+** Foundation, Inc., Hewlett-Packard Company, Microsoft, nor Digital Equipment
+** Corporation makes any representations about the suitability of
+** this software for any purpose.
+*/
+
+
+#ifndef UUID_H
+#define UUID_H
+
+/* Set this to what your compiler uses for 64 bit data type */
+#ifdef _WIN32
+#define unsigned64_t unsigned __int64
+#define I64(C) C
+#else
+#define unsigned64_t unsigned long long
+#define I64(C) C##LL
+#endif
+
+/***** uuid related data types *****/
+/*
+ * DBDB These types were broken on 64-bit architectures
+ * This file has been modified to fix that problem.
+ */
+#if defined(__LP64__) || defined (_LP64)
+typedef unsigned int unsigned32;
+#else
+typedef unsigned long unsigned32;
+#endif
+typedef unsigned short unsigned16;
+typedef unsigned char unsigned8;
+typedef unsigned64_t uuid_time_t;
+typedef struct
+{
+ char nodeID[6];
+} uuid_node_t;
+
+typedef struct _guid_t
+{
+ unsigned32 time_low;
+ unsigned16 time_mid;
+ unsigned16 time_hi_and_version;
+ unsigned8 clock_seq_hi_and_reserved;
+ unsigned8 clock_seq_low;
+ PRUint8 node[6];
+} guid_t;
+
+enum
+{
+ UUID_SUCCESS, /* operation succeded */
+ UUID_IO_ERROR, /* file I/O failed */
+ UUID_LOCK_ERROR, /* lock creation failed */
+ UUID_TIME_ERROR, /* ran out of time sequence numbers, need
+ time update to generate the id */
+ UUID_BAD_FORMAT, /* data in a string buffer is not in UUID format */
+ UUID_MEMORY_ERROR, /* memory allocation failed */
+ UUID_LDAP_ERROR, /* LDAP operation failed */
+ UUID_NOTFOUND, /* generator state is missing */
+ UUID_FORMAT_ERROR, /* state entry does not contain right data */
+ UUID_UNKNOWN_ERROR
+};
+
+/***** uuid interface *****/
+
+/* uuid_init -- initialize uuid layer */
+int uuid_init (const char *configDir, const Slapi_DN *configDN, PRBool mtGen);
+
+/* uuid_cleanup -- cleanup of uuid layer */
+void uuid_cleanup ();
+
+/* uuid_create -- generate a UUID */
+int uuid_create(guid_t *uuid);
+
+/* uuid_compare -- Compare two UUID's "lexically" and return
+ -1 u1 is lexically before u2
+ 0 u1 is equal to u2
+ 1 u1 is lexically after u2
+ Note: lexical ordering is not temporal ordering!
+*/
+int uuid_compare(const guid_t *u1, const guid_t *u2);
+
+/* uuid_format -- converts UUID to its string representation and stores it
+ in the buff. buff must be at list 40 bytes long
+*/
+void uuid_format(const guid_t *u, char *buff);
+
+/* uuid_create_from_name -- create a UUID using a "name" from a "name space" */
+void uuid_create_from_name(guid_t * uuid, /* resulting UUID */
+ const guid_t nsid, /* UUID to serve as context, so identical
+ names from different name spaces generate
+ different UUIDs */
+ const void * name, /* the name from which to generate a UUID */
+ int namelen); /* the length of the name */
+#endif
diff --git a/ldap/servers/slapd/value.c b/ldap/servers/slapd/value.c
new file mode 100644
index 00000000..8e2a31ef
--- /dev/null
+++ b/ldap/servers/slapd/value.c
@@ -0,0 +1,460 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+
+/* value.c - routines for dealing with values */
+
+#undef DEBUG /* disable counters */
+#include <prcountr.h>
+#include "slap.h"
+#include "slapi-private.h"
+
+/*
+ * Functions needed when a berval is embedded in a struct or
+ * allocated on the stack rather than the heap.
+ */
+
+static void ber_bvdone(struct berval *bvp)
+{
+ if (bvp == NULL) return;
+
+ slapi_ch_free_string(&bvp->bv_val);
+ bvp->bv_len = 0;
+
+ return;
+}
+
+static void ber_bvcpy(struct berval *bvd, const struct berval *bvs)
+{
+ size_t len;
+
+ if (bvd == NULL || bvs == NULL) return;
+
+ len = bvs->bv_len;
+ bvd->bv_val = slapi_ch_malloc(len+1);
+ bvd->bv_len = len;
+ memcpy(bvd->bv_val, bvs->bv_val, len);
+ bvd->bv_val[len] = '\0';
+
+ return;
+}
+
+/* <=========================== Slapi_Value ==========================> */
+
+#ifdef VALUE_DEBUG
+static void value_dump( const Slapi_Value *value, const char *text);
+#define VALUE_DUMP(value,name) value_dump(value,name)
+#else
+#define VALUE_DUMP(value,name) ((void)0)
+#endif
+
+static int counters_created= 0;
+PR_DEFINE_COUNTER(slapi_value_counter_created);
+PR_DEFINE_COUNTER(slapi_value_counter_deleted);
+PR_DEFINE_COUNTER(slapi_value_counter_exist);
+
+Slapi_Value *
+slapi_value_new()
+{
+ return value_new(NULL,CSN_TYPE_NONE,NULL);
+}
+
+Slapi_Value *
+slapi_value_new_berval(const struct berval *bval)
+{
+ return value_new(bval,CSN_TYPE_NONE,NULL);
+}
+
+Slapi_Value *
+slapi_value_new_value(const Slapi_Value *v)
+{
+ return slapi_value_dup(v);
+}
+
+Slapi_Value *
+slapi_value_new_string(const char *s)
+{
+ Slapi_Value *v= value_new(NULL,CSN_TYPE_UNKNOWN,NULL);
+ slapi_value_set_string(v, s);
+ return v;
+}
+
+Slapi_Value *
+slapi_value_new_string_passin(char *s)
+{
+ Slapi_Value *v= value_new(NULL,CSN_TYPE_UNKNOWN,NULL);
+ slapi_value_set_string_passin(v, s);
+ return v;
+}
+
+Slapi_Value *
+slapi_value_init(Slapi_Value *v)
+{
+ return value_init(v,NULL,CSN_TYPE_NONE,NULL);
+}
+
+Slapi_Value *
+slapi_value_init_berval(Slapi_Value *v, struct berval *bval)
+{
+ return value_init(v,bval,CSN_TYPE_NONE,NULL);
+}
+
+Slapi_Value *
+slapi_value_init_string(Slapi_Value *v,const char *s)
+{
+ value_init(v,NULL,CSN_TYPE_UNKNOWN,NULL);
+ slapi_value_set_string(v,s);
+ return v;
+}
+
+Slapi_Value *
+slapi_value_init_string_passin(Slapi_Value *v, char *s)
+{
+ value_init(v,NULL,CSN_TYPE_UNKNOWN,NULL);
+ slapi_value_set_string_passin(v,s);
+ return v;
+}
+
+Slapi_Value *
+slapi_value_dup(const Slapi_Value *v)
+{
+ Slapi_Value *newvalue= value_new(&v->bv,CSN_TYPE_UNKNOWN,NULL);
+ newvalue->v_csnset= csnset_dup(v->v_csnset);
+ return newvalue;
+
+}
+
+Slapi_Value *
+value_new(const struct berval *bval,CSNType t,const CSN *csn)
+{
+ Slapi_Value *v;
+ v = (Slapi_Value *)slapi_ch_malloc(sizeof(Slapi_Value));
+ value_init(v, bval, t, csn);
+ if(!counters_created)
+ {
+ PR_CREATE_COUNTER(slapi_value_counter_created,"Slapi_Value","created","");
+ PR_CREATE_COUNTER(slapi_value_counter_deleted,"Slapi_Value","deleted","");
+ PR_CREATE_COUNTER(slapi_value_counter_exist,"Slapi_Value","exist","");
+ counters_created= 1;
+ }
+ PR_INCREMENT_COUNTER(slapi_value_counter_created);
+ PR_INCREMENT_COUNTER(slapi_value_counter_exist);
+ VALUE_DUMP(v,"value_new");
+ return v;
+}
+
+Slapi_Value *
+value_init(Slapi_Value *v, const struct berval *bval,CSNType t,const CSN *csn)
+{
+ PR_ASSERT(v!=NULL);
+ memset(v,0,sizeof(Slapi_Value));
+ if(csn!=NULL)
+ {
+ value_update_csn(v,t,csn);
+ }
+ slapi_value_set_berval(v,bval);
+ return v;
+}
+
+void
+slapi_value_free(Slapi_Value **v)
+{
+ if(v!=NULL && *v!=NULL)
+ {
+ VALUE_DUMP(*v,"value_free");
+ value_done(*v);
+ slapi_ch_free((void **)v);
+ *v= NULL;
+ PR_INCREMENT_COUNTER(slapi_value_counter_deleted);
+ PR_DECREMENT_COUNTER(slapi_value_counter_exist);
+ }
+}
+
+void
+value_done(Slapi_Value *v)
+{
+ if(v!=NULL)
+ {
+ if(NULL != v->v_csnset)
+ {
+ csnset_free(&(v->v_csnset));
+ }
+ ber_bvdone(&v->bv);
+ }
+}
+
+const CSNSet *
+value_get_csnset ( const Slapi_Value *value)
+{
+ if (value)
+ return value->v_csnset;
+ else
+ return NULL;
+}
+
+const CSN *
+value_get_csn( const Slapi_Value *value, CSNType t)
+{
+ const CSN *csn= NULL;
+ if(NULL!=value)
+ {
+ csn= csnset_get_csn_of_type(value->v_csnset, t);
+ }
+ return csn;
+}
+
+int
+value_contains_csn( const Slapi_Value *value, CSN *csn)
+{
+ int r= 0;
+ if(NULL!=value)
+ {
+ r= csnset_contains(value->v_csnset, csn);
+ }
+ return r;
+}
+
+Slapi_Value *
+value_update_csn( Slapi_Value *value, CSNType t, const CSN *csn)
+{
+ if(value!=NULL)
+ {
+ csnset_update_csn(&value->v_csnset,t,csn);
+ }
+ return value;
+}
+
+Slapi_Value *
+value_add_csn( Slapi_Value *value, CSNType t, const CSN *csn)
+{
+ if(value!=NULL)
+ {
+ csnset_add_csn(&value->v_csnset,t,csn);
+ }
+ return value;
+}
+
+const struct berval *
+slapi_value_get_berval( const Slapi_Value *value )
+{
+ const struct berval *bval= NULL;
+ if(NULL != value)
+ {
+ bval = &value->bv;
+ }
+ return bval;
+}
+
+Slapi_Value *
+slapi_value_set( Slapi_Value *value, void *val, unsigned long len)
+{
+ struct berval bv;
+ bv.bv_len= len;
+ bv.bv_val= (void*)val; /* We cast away the const, but we're not going to change anything */
+ slapi_value_set_berval( value, &bv);
+ return value;
+}
+
+Slapi_Value *
+slapi_value_set_value( Slapi_Value *value, const Slapi_Value *vfrom)
+{
+ slapi_value_set_berval( value, &vfrom->bv );
+ csnset_free(&value->v_csnset);
+ value->v_csnset= csnset_dup(vfrom->v_csnset);
+ return value;
+}
+
+Slapi_Value *
+value_remove_csn( Slapi_Value *value, CSNType t)
+{
+ if(value!=NULL)
+ {
+ csnset_remove_csn(&value->v_csnset,t);
+ }
+ return value;
+}
+
+Slapi_Value *
+slapi_value_set_berval( Slapi_Value *value, const struct berval *bval )
+{
+ if(value!=NULL)
+ {
+ ber_bvdone(&value->bv);
+ if(bval!=NULL)
+ {
+ ber_bvcpy(&value->bv, bval);
+ }
+ }
+ return value;
+}
+
+int
+slapi_value_set_string(Slapi_Value *value, const char *strVal)
+{
+ return slapi_value_set_string_passin(value, slapi_ch_strdup(strVal));
+}
+
+int
+slapi_value_set_string_passin(Slapi_Value *value, char *strVal)
+{
+ int rc= -1;
+ if(NULL != value)
+ {
+ ber_bvdone(&value->bv);
+ value->bv.bv_val = strVal;
+ value->bv.bv_len = strlen(strVal);
+ rc= 0;
+ }
+ return rc;
+}
+
+int
+slapi_value_set_int(Slapi_Value *value, int intVal)
+{
+ int rc= -1;
+ if(NULL != value)
+ {
+ char valueBuf[80];
+ ber_bvdone(&value->bv);
+ sprintf(valueBuf,"%d",intVal);
+ value->bv.bv_val = slapi_ch_strdup(valueBuf);
+ value->bv.bv_len = strlen(value->bv.bv_val);
+ rc= 0;
+ }
+ return rc;
+}
+
+/*
+ * Warning: The value may not be '\0' terminated!
+ * Make sure that you know this is a C string.
+ */
+const char *
+slapi_value_get_string(const Slapi_Value *value)
+{
+ const char *r= NULL;
+ if(value!=NULL)
+ {
+ r= (const char*)value->bv.bv_val;
+ }
+ return r;
+}
+
+size_t
+slapi_value_get_length(const Slapi_Value *value)
+{
+ size_t r= 0;
+ if(NULL!=value)
+ {
+ r= value->bv.bv_len;
+ }
+ return r;
+}
+
+int
+slapi_value_get_int(const Slapi_Value *value)
+{
+ int r= 0;
+ if(NULL!=value)
+ {
+ char *p;
+ p = slapi_ch_malloc(value->bv.bv_len + 1);
+ memcpy (p, value->bv.bv_val, value->bv.bv_len);
+ p [value->bv.bv_len] = '\0';
+ r= atoi(p);
+ slapi_ch_free((void **)&p);
+ }
+ return r;
+}
+
+unsigned int
+slapi_value_get_uint(const Slapi_Value *value)
+{
+ unsigned int r= 0;
+ if(NULL!=value)
+ {
+ char *p;
+ p = slapi_ch_malloc(value->bv.bv_len + 1);
+ memcpy (p, value->bv.bv_val, value->bv.bv_len);
+ p [value->bv.bv_len] = '\0';
+ r= (unsigned int)atoi(p);
+ slapi_ch_free((void **)&p);
+ }
+ return r;
+}
+
+long
+slapi_value_get_long(const Slapi_Value *value)
+{
+ long r= 0;
+ if(NULL!=value)
+ {
+ char *p;
+ p = slapi_ch_malloc(value->bv.bv_len + 1);
+ memcpy (p, value->bv.bv_val, value->bv.bv_len);
+ p [value->bv.bv_len] = '\0';
+ r = atol(p);
+ slapi_ch_free((void **)&p);
+ }
+ return r;
+}
+
+unsigned long
+slapi_value_get_ulong(const Slapi_Value *value)
+{
+ unsigned long r= 0;
+ if(NULL!=value)
+ {
+ char *p;
+ p = slapi_ch_malloc(value->bv.bv_len + 1);
+ memcpy (p, value->bv.bv_val, value->bv.bv_len);
+ p [value->bv.bv_len] = '\0';
+ r = (unsigned long)atol(p);
+ slapi_ch_free((void **)&p);
+ }
+ return r;
+}
+
+int
+slapi_value_compare(const Slapi_Attr *a,const Slapi_Value *v1,const Slapi_Value *v2)
+{
+ int r= 0;
+ if(v1!=NULL && v2!=NULL)
+ {
+ r= slapi_attr_value_cmp( a, &v1->bv, &v2->bv);
+ }
+ else if(v1!=NULL && v2==NULL)
+ {
+ r= 1; /* v1>v2 */
+ }
+ else if (v1==NULL && v2!=NULL)
+ {
+ r= -1; /* v1<v2 */
+ }
+ else /* (v1==NULL && v2==NULL) */
+ {
+ r= 0; /* The same */
+ }
+ return r;
+}
+
+size_t
+value_size(const Slapi_Value *v)
+{
+ size_t s= v->bv.bv_len;
+ s += csnset_size(v->v_csnset);
+ s += sizeof(Slapi_Value);
+ return s;
+}
+
+
+#ifdef VALUE_DEBUG
+static void
+value_dump( const Slapi_Value *value, const char *text)
+{
+ LDAPDebug( LDAP_DEBUG_ANY, "Slapi_Value %s ptr=%lx\n", text, value, 0);
+ /* JCM - Dump value contents... */
+}
+#endif
+
diff --git a/ldap/servers/slapd/valueset.c b/ldap/servers/slapd/valueset.c
new file mode 100644
index 00000000..a388fc38
--- /dev/null
+++ b/ldap/servers/slapd/valueset.c
@@ -0,0 +1,1303 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+/* valueset.c - routines for dealing with value sets */
+
+#include "slap.h"
+#include "slapi-private.h"
+
+/* <=========================== Berval Array ==========================> */
+
+/*
+ * vals - The existing values.
+ * addval - The value to add.
+ * nvals - The number of existing values.
+ * maxvals - The number of elements in the existing values array.
+ */
+void
+bervalarray_add_berval_fast(
+ struct berval ***vals,
+ const struct berval *addval,
+ int nvals,
+ int *maxvals
+)
+{
+ int need = nvals + 2;
+ if(need>*maxvals)
+ {
+ if (*maxvals==0)
+ {
+ *maxvals = 2;
+ }
+ while ( *maxvals < need )
+ {
+ *maxvals *= 2;
+ }
+ if(*vals==NULL)
+ {
+ *vals = (struct berval **) slapi_ch_malloc( *maxvals * sizeof(struct berval *));
+ }
+ else
+ {
+ *vals = (struct berval **) slapi_ch_realloc( (char *) *vals, *maxvals * sizeof(struct berval *));
+ }
+ }
+ (*vals)[nvals] = ber_bvdup( (struct berval *)addval );
+ (*vals)[nvals+1] = NULL;
+}
+
+/* <=========================== Value Array ==========================> */
+
+/*
+ * vals - The existing values.
+ * addval - The value to add.
+ * nvals - The number of existing values.
+ * maxvals - The number of elements in the existing values array.
+ */
+void
+valuearray_add_value_fast(Slapi_Value ***vals,
+ Slapi_Value *addval,
+ int nvals,
+ int *maxvals,
+ int exact, /* Don't create an array bigger than needed */
+ int passin) /* The values are being passed in */
+{
+ Slapi_Value *oneval[2];
+ oneval[0]= addval;
+ oneval[1]= NULL;
+ valuearray_add_valuearray_fast(vals,oneval,nvals,1,maxvals,exact,passin);
+}
+
+void
+valuearray_add_valuearray_fast(Slapi_Value ***vals,
+ Slapi_Value **addvals,
+ int nvals,
+ int naddvals,
+ int *maxvals,
+ int exact, /* Don't create an array bigger than needed */
+ int passin) /* The values are being passed in */
+{
+ int i, j;
+ int allocate= 0;
+ int need = nvals + naddvals + 1;
+ if(exact)
+ {
+ /* Create an array exactly the right size. */
+ if(need>*maxvals)
+ {
+ allocate= need;
+ }
+ }
+ else
+ {
+ if (*maxvals==0) /* empty; create with 4 by default */
+ {
+ allocate= 4;
+ }
+ else if (need > *maxvals)
+ {
+ /* Exponentially expand the array */
+ allocate= *maxvals;
+
+ while ( allocate < need )
+ {
+ allocate *= 2;
+ }
+ }
+ }
+ if(allocate>0)
+ {
+ if(*vals==NULL)
+ {
+ *vals = (Slapi_Value **) slapi_ch_malloc( allocate * sizeof(Slapi_Value *));
+ }
+ else
+ {
+ *vals = (Slapi_Value **) slapi_ch_realloc( (char *) *vals, allocate * sizeof(Slapi_Value *));
+ }
+ *maxvals= allocate;
+ }
+ for ( i = 0, j = 0; i < naddvals; i++)
+ {
+ if ( addvals[i]!=NULL )
+ {
+ if(passin)
+ {
+ /* We consume the values */
+ (*vals)[nvals + j] = addvals[i];
+ }
+ else
+ {
+ /* We copy the values */
+ (*vals)[nvals + j] = slapi_value_dup(addvals[i]);
+ }
+ j++;
+ }
+ }
+ (*vals)[nvals + j] = NULL;
+}
+
+void
+valuearray_add_value(Slapi_Value ***vals, const Slapi_Value *addval)
+{
+ Slapi_Value *oneval[2];
+ oneval[0]= (Slapi_Value*)addval;
+ oneval[1]= NULL;
+ valuearray_add_valuearray(vals,oneval,0);
+}
+
+void
+valuearray_add_valuearray(Slapi_Value ***vals, Slapi_Value **addvals, PRUint32 flags)
+{
+ int valslen;
+ int addvalslen;
+ int maxvals;
+
+ addvalslen= valuearray_count(addvals);
+ if(*vals == NULL)
+ {
+ valslen= 0;
+ maxvals= 0;
+ }
+ else
+ {
+ valslen= valuearray_count(*vals);
+ maxvals= valslen+1;
+ }
+ valuearray_add_valuearray_fast(vals,addvals,valslen,addvalslen,&maxvals,1/*Exact*/,flags & SLAPI_VALUE_FLAG_PASSIN);
+}
+
+int
+valuearray_count( Slapi_Value **va)
+{
+ int i=0;
+ if(va!=NULL)
+ {
+ while(NULL != va[i]) i++;
+ }
+ return(i);
+}
+
+int
+valuearray_isempty( Slapi_Value **va)
+{
+ return va==NULL || va[0]==NULL;
+}
+
+/*
+ * JCM SLOW FUNCTION
+ *
+ * WARNING: Use only if you absolutley need to...
+ * This function mostly exists to map from the old slapi berval
+ * based interface to the new Slapi_Value based interfaces.
+ */
+int
+valuearray_init_bervalarray(struct berval **bvals, Slapi_Value ***cvals)
+{
+ int n;
+ for(n=0; bvals != NULL && bvals[n] != NULL; n++);
+ if(n==0)
+ {
+ *cvals = NULL;
+ }
+ else
+ {
+ int i;
+ *cvals = (Slapi_Value **) slapi_ch_malloc((n + 1) * sizeof(Slapi_Value *));
+ for(i=0;i<n;i++)
+ {
+ (*cvals)[i] = slapi_value_new_berval(bvals[i]);
+ }
+ (*cvals)[i] = NULL;
+ }
+ return n;
+}
+
+/*
+ * JCM SLOW FUNCTION
+ *
+ * Use only if you absolutley need to...
+ * This function mostly exists to map from the old slapi berval
+ * based interface to the new Slapi_Value based interfaces.
+ */
+int
+valuearray_get_bervalarray(Slapi_Value **cvals,struct berval ***bvals)
+{
+ int i,n;
+ n= valuearray_count(cvals);
+ if (0 == n)
+ {
+ *bvals = NULL;
+ }
+ else
+ {
+ *bvals = (struct berval **)slapi_ch_malloc((n + 1) * sizeof(struct berval *));
+ for(i=0;i<n;i++)
+ {
+ (*bvals)[i] = ber_bvdup(slapi_value_get_berval(cvals[i]));
+ }
+ (*bvals)[i] = NULL;
+ }
+ return(0);
+}
+
+void
+valuearray_free(Slapi_Value ***va)
+{
+ if(va!=NULL && *va!=NULL)
+ {
+ int i;
+ for(i=0; (*va)[i]!=NULL; i++)
+ {
+ slapi_value_free(&(*va)[i]);
+ }
+ slapi_ch_free((void **)va);
+ *va = NULL;
+ }
+}
+
+int
+valuearray_next_value( Slapi_Value **va, int index, Slapi_Value **v)
+{
+ int r= -1;
+ if(va!=NULL && va[0]!=NULL)
+ {
+ r= index+1;
+ *v= va[r];
+ if(*v==NULL)
+ {
+ r= -1;
+ }
+ }
+ else
+ {
+ *v= NULL;
+ }
+ return r;
+}
+
+int
+valuearray_first_value( Slapi_Value **va, Slapi_Value **v )
+{
+ return valuearray_next_value( va, -1, v);
+}
+
+/*
+ * Find the value and return an index number to it.
+ */
+int
+valuearray_find(const Slapi_Attr *a, Slapi_Value **va, const Slapi_Value *v)
+{
+ int i= 0;
+ int found= -1;
+ while(found==-1 && va!=NULL && va[i]!=NULL)
+ {
+ if(slapi_value_compare( a, v, va[i])==0)
+ {
+ found= i;
+ }
+ else
+ {
+ i++;
+ }
+ }
+ return found;
+}
+
+/*
+ * Shunt up the array to cover the value to remove.
+ */
+void
+valuearray_remove_value_atindex(Slapi_Value **va, int index)
+{
+ if(va!=NULL && va[0]!=NULL)
+ {
+ int k;
+ for ( k = index + 1; va[k] != NULL; k++ )
+ {
+ va[k - 1] = va[k];
+ }
+ va[k - 1] = NULL;
+ }
+}
+
+/*
+ * Find the value in the array,
+ * shunt up the array to cover it,
+ * return a ptr to the value.
+ */
+Slapi_Value *
+valuearray_remove_value(const Slapi_Attr *a, Slapi_Value **va, const Slapi_Value *v)
+{
+ Slapi_Value *r= NULL;
+ int i= 0;
+ i= valuearray_find(a, va, v);
+ if(i!=-1)
+ {
+ r= va[i];
+ valuearray_remove_value_atindex(va,i);
+ }
+ return r;
+}
+
+/*
+ * Remove any values older than the CSN.
+ */
+int
+valuearray_purge(Slapi_Value ***va, const CSN *csn)
+{
+ int numValues=0;
+ int i=0;
+ int nextValue=0;
+
+ PR_ASSERT(va!=NULL && *va!=NULL);
+
+ /* Loop over all the values freeing the old ones. */
+ for(i=0; (*va)[i]; i++)
+ {
+ csnset_purge(&((*va)[i]->v_csnset),csn);
+ if ((*va)[i]->v_csnset == NULL)
+ {
+ slapi_value_free(&(*va)[i]);
+ (*va)[i] = NULL;
+ }
+ }
+ /* Now compact the value list. */
+ numValues=i;
+ nextValue = 0;
+ i = 0;
+ for(i=0;i<numValues;i++)
+ {
+ while((nextValue < numValues) && (NULL == (*va)[nextValue]))
+ {
+ nextValue++;
+ }
+ if(nextValue < numValues)
+ {
+ (*va)[i] = (*va)[nextValue];
+ nextValue++;
+ }
+ else
+ {
+ break;
+ }
+ }
+ (*va)[i] = NULL;
+
+ /* All the values were deleted, we can discard the whole array. */
+ if(NULL == (*va)[0])
+ {
+ slapi_ch_free((void**)va);
+ *va= NULL;
+ }
+
+ return(0);
+}
+
+size_t
+valuearray_size(Slapi_Value **va)
+{
+ size_t s= 0;
+ if(va!=NULL && va[0]!=NULL)
+ {
+ int i;
+ for (i = 0; va[i]; i++)
+ {
+ s += value_size(va[i]);
+ }
+ s += (i + 1) * sizeof(Slapi_Value*);
+ }
+ return s;
+}
+
+void
+valuearray_update_csn(Slapi_Value **va, CSNType t, const CSN *csn)
+{
+ int i;
+ for (i = 0; va!=NULL && va[i]; i++)
+ {
+ value_update_csn(va[i],t,csn);
+ }
+}
+
+/*
+ * Shunt up the values to cover the empty slots.
+ *
+ * "compressed" means "contains no NULL's"
+ *
+ * Invariant for the outer loop:
+ * va[0..i] is compressed &&
+ * va[n..numvalues] contains just NULL's
+ *
+ * Invariant for the inner loop:
+ * i<j<=k<=n && va[j..k] has been shifted left by (j-i) places &&
+ * va[k..n] remains to be shifted left by (j-i) places
+ *
+ */
+void
+valuearray_compress(Slapi_Value **va,int numvalues)
+{
+ int i = 0;
+ int n= numvalues;
+ while(i<n)
+ {
+ if ( va[i] != NULL ) {
+ i++;
+ } else {
+ int k,j;
+ j = i + 1;
+ /* Find the length of the next run of NULL's */
+ while( j<n && va[j] == NULL) { j++; }
+ /* va[i..j] is all NULL && j<= n */
+ for ( k = j; k<n; k++ )
+ {
+ va[k - (j-i)] = va[k];
+ va[k] = NULL;
+ }
+ /* va[i..n] has been shifted down by j-i places */
+ n = n - (j-i);
+ /*
+ * If va[i] in now non null, then bump i,
+ * if not then we are done anyway (j==n) so can bump it.
+ */
+ i++;
+ }
+ }
+}
+
+/* <=========================== Value Array Fast ==========================> */
+
+void
+valuearrayfast_init(struct valuearrayfast *vaf,Slapi_Value **va)
+{
+ vaf->num= valuearray_count(va);
+ vaf->max= vaf->num;
+ vaf->va= va;
+}
+
+void
+valuearrayfast_done(struct valuearrayfast *vaf)
+{
+ if(vaf->va!=NULL)
+ {
+ int i;
+ for(i=0; i<vaf->num; i++)
+ {
+ slapi_value_free(&vaf->va[i]);
+ }
+ slapi_ch_free((void **)&vaf->va);
+ vaf->num= 0;
+ vaf->max= 0;
+ }
+}
+
+void
+valuearrayfast_add_value(struct valuearrayfast *vaf,const Slapi_Value *v)
+{
+ valuearray_add_value_fast(&vaf->va,(Slapi_Value *)v,vaf->num,&vaf->max,0/*Exact*/,0/*!PassIn*/);
+ vaf->num++;
+}
+
+void
+valuearrayfast_add_value_passin(struct valuearrayfast *vaf,Slapi_Value *v)
+{
+ valuearray_add_value_fast(&vaf->va,v,vaf->num,&vaf->max,0/*Exact*/,1/*PassIn*/);
+ vaf->num++;
+}
+
+void
+valuearrayfast_add_valuearrayfast(struct valuearrayfast *vaf,const struct valuearrayfast *vaf_add)
+{
+ valuearray_add_valuearray_fast(&vaf->va,vaf_add->va,vaf->num,vaf_add->num,&vaf->max,0/*Exact*/,0/*!PassIn*/);
+ vaf->num+= vaf_add->num;
+}
+
+/* <=========================== ValueArrayIndexTree =======================> */
+
+static int valuetree_dupvalue_disallow( caddr_t d1, caddr_t d2 );
+static int valuetree_node_cmp( caddr_t d1, caddr_t d2 );
+static int valuetree_node_free( caddr_t data );
+
+/*
+ * structure used within AVL value trees.
+ */
+typedef struct valuetree_node
+{
+ int index; /* index into the value array */
+ Slapi_Value *sval; /* the actual value */
+} valuetree_node;
+
+/*
+ * Create or update an AVL tree of values that can be used to speed up value
+ * lookups. We store the index keys for the values in the AVL tree so
+ * we can use a trivial comparison function.
+ *
+ * Returns:
+ * LDAP_SUCCESS on success,
+ * LDAP_TYPE_OR_VALUE_EXISTS if the value already exists,
+ * LDAP_OPERATIONS_ERROR for some unexpected failure.
+ *
+ * Sets *valuetreep to the root of the AVL tree that was created. If a
+ * non-zero value is returned, the tree is freed if free_on_error is non-zero
+ * and *valuetreep is set to NULL.
+ */
+int
+valuetree_add_valuearray( const char *type, struct slapdplugin *pi, Slapi_Value **va, Avlnode **valuetreep, int *duplicate_index )
+{
+ int rc= LDAP_SUCCESS;
+
+ PR_ASSERT(type!=NULL);
+ PR_ASSERT(pi!=NULL);
+ PR_ASSERT(valuetreep!=NULL);
+
+ if ( duplicate_index ) {
+ *duplicate_index = -1;
+ }
+
+ if ( !valuearray_isempty(va) )
+ {
+ Slapi_Value **keyvals;
+ /* Convert the value array into key values */
+ if ( slapi_call_syntax_values2keys_sv( pi, (Slapi_Value**)va, &keyvals, LDAP_FILTER_EQUALITY ) != 0 ) /* jcm cast */
+ {
+ LDAPDebug( LDAP_DEBUG_ANY,"slapi_call_syntax_values2keys for attribute %s failed\n", type, 0, 0 );
+ rc= LDAP_OPERATIONS_ERROR;
+ }
+ else
+ {
+ int i;
+ valuetree_node *vaip;
+ for ( i = 0; rc==LDAP_SUCCESS && va[i] != NULL; ++i )
+ {
+ if ( keyvals[i] == NULL )
+ {
+ LDAPDebug( LDAP_DEBUG_ANY,"slapi_call_syntax_values2keys for attribute %s did not return enough key values\n", type, 0, 0 );
+ rc= LDAP_OPERATIONS_ERROR;
+ }
+ else
+ {
+ vaip = (valuetree_node *)slapi_ch_malloc( sizeof( valuetree_node ));
+ vaip->index = i;
+ vaip->sval = keyvals[i];
+ if (( rc = avl_insert( valuetreep, vaip, valuetree_node_cmp, valuetree_dupvalue_disallow )) != 0 )
+ {
+ slapi_ch_free( (void **)&vaip );
+ /* Value must already be in there */
+ rc= LDAP_TYPE_OR_VALUE_EXISTS;
+ if ( duplicate_index ) {
+ *duplicate_index = i;
+ }
+ }
+ else
+ {
+ keyvals[i]= NULL;
+ }
+ }
+ }
+ valuearray_free( &keyvals );
+ }
+ }
+ if(rc!=0)
+ {
+ valuetree_free( valuetreep );
+ }
+
+ return rc;
+}
+
+int
+valuetree_add_value( const char *type, struct slapdplugin *pi, const Slapi_Value *v, Avlnode **valuetreep)
+{
+ Slapi_Value *va[2];
+ va[0]= (Slapi_Value*)v;
+ va[1]= NULL;
+ return valuetree_add_valuearray( type, pi, va, valuetreep, NULL);
+}
+
+
+/*
+ *
+ * Find value "v" using AVL tree "valuetree"
+ *
+ * returns LDAP_SUCCESS if "v" was found, LDAP_NO_SUCH_ATTRIBUTE
+ * if "v" was not found and LDAP_OPERATIONS_ERROR if some unexpected error occurs.
+ */
+static int
+valuetree_find( const struct slapi_attr *a, const Slapi_Value *v, Avlnode *valuetree, int *index)
+{
+ const Slapi_Value *oneval[2];
+ Slapi_Value **keyvals;
+ valuetree_node *vaip, tmpvain;
+
+ PR_ASSERT(a!=NULL);
+ PR_ASSERT(a->a_plugin!=NULL);
+ PR_ASSERT(v!=NULL);
+ PR_ASSERT(valuetree!=NULL);
+ PR_ASSERT(index!=NULL);
+
+ if ( a == NULL || a->a_plugin == NULL || v == NULL || valuetree == NULL )
+ {
+ return( LDAP_OPERATIONS_ERROR );
+ }
+
+ keyvals = NULL;
+ oneval[0] = v;
+ oneval[1] = NULL;
+ if ( slapi_call_syntax_values2keys_sv( a->a_plugin, (Slapi_Value**)oneval, &keyvals, LDAP_FILTER_EQUALITY ) != 0 /* jcm cast */
+ || keyvals == NULL
+ || keyvals[0] == NULL )
+ {
+ LDAPDebug( LDAP_DEBUG_ANY, "valuetree_find_and_replace: "
+ "slapi_call_syntax_values2keys failed for type %s\n",
+ a->a_type, 0, 0 );
+ return( LDAP_OPERATIONS_ERROR );
+ }
+
+ tmpvain.index = 0;
+ tmpvain.sval = keyvals[0];
+ vaip = (valuetree_node *)avl_find( valuetree, &tmpvain, valuetree_node_cmp );
+
+ if ( keyvals != NULL )
+ {
+ valuearray_free( &keyvals );
+ }
+
+ if (vaip == NULL)
+ {
+ return( LDAP_NO_SUCH_ATTRIBUTE );
+ }
+ else
+ {
+ *index= vaip->index;
+ }
+
+ return( LDAP_SUCCESS );
+}
+
+static int
+valuetree_dupvalue_disallow( caddr_t d1, caddr_t d2 )
+{
+ return( 1 );
+}
+
+
+void
+valuetree_free( Avlnode **valuetreep )
+{
+ if ( valuetreep != NULL && *valuetreep != NULL )
+ {
+ avl_free( *valuetreep, valuetree_node_free );
+ *valuetreep = NULL;
+ }
+}
+
+
+static int
+valuetree_node_free( caddr_t data )
+{
+ if ( data!=NULL )
+ {
+ valuetree_node *vaip = (valuetree_node *)data;
+
+ slapi_value_free(&vaip->sval);
+ slapi_ch_free( (void **)&data );
+ }
+ return( 0 );
+}
+
+
+static int
+valuetree_node_cmp( caddr_t d1, caddr_t d2 )
+{
+ const struct berval *bv1, *bv2;
+ int rc;
+
+ bv1 = slapi_value_get_berval(((valuetree_node *)d1)->sval);
+ bv2 = slapi_value_get_berval(((valuetree_node *)d2)->sval);
+
+ if ( bv1->bv_len < bv2->bv_len ) {
+ rc = -1;
+ } else if ( bv1->bv_len > bv2->bv_len ) {
+ rc = 1;
+ } else {
+ rc = memcmp( bv1->bv_val, bv2->bv_val, bv1->bv_len );
+ }
+
+ return( rc );
+}
+
+/* <=========================== Value Set =======================> */
+
+/*
+ * JCM: All of these valueset functions are just forwarded to the
+ * JCM: valuearray functions... waste of time. Inline them!
+ */
+
+Slapi_ValueSet *
+slapi_valueset_new()
+{
+ Slapi_ValueSet *vs = (Slapi_ValueSet *)slapi_ch_calloc(1,sizeof(Slapi_ValueSet));
+
+ if(vs)
+ slapi_valueset_init(vs);
+
+ return vs;
+}
+
+void
+slapi_valueset_init(Slapi_ValueSet *vs)
+{
+ if(vs!=NULL)
+ {
+ vs->va= NULL;
+ }
+}
+
+void
+slapi_valueset_done(Slapi_ValueSet *vs)
+{
+ if(vs!=NULL)
+ {
+ if(vs->va!=NULL)
+ {
+ valuearray_free(&vs->va);
+ vs->va= NULL;
+ }
+ }
+}
+
+void
+slapi_valueset_free(Slapi_ValueSet *vs)
+{
+ if(vs!=NULL)
+ {
+ slapi_valueset_done(vs);
+ slapi_ch_free((void **)&vs);
+ }
+}
+
+void
+slapi_valueset_set_from_smod(Slapi_ValueSet *vs, Slapi_Mod *smod)
+{
+ Slapi_Value **va= NULL;
+ valuearray_init_bervalarray(slapi_mod_get_ldapmod_byref(smod)->mod_bvalues, &va);
+ valueset_set_valuearray_passin(vs, va);
+}
+
+void
+valueset_set_valuearray_byval(Slapi_ValueSet *vs, Slapi_Value **addvals)
+{
+ slapi_valueset_init(vs);
+ valueset_add_valuearray(vs,addvals);
+}
+
+void
+valueset_set_valuearray_passin(Slapi_ValueSet *vs, Slapi_Value **addvals)
+{
+ slapi_valueset_init(vs);
+ vs->va= addvals;
+}
+
+void
+slapi_valueset_set_valueset(Slapi_ValueSet *vs1, const Slapi_ValueSet *vs2)
+{
+ slapi_valueset_init(vs1);
+ valueset_add_valueset(vs1,vs2);
+}
+
+int
+slapi_valueset_first_value( Slapi_ValueSet *vs, Slapi_Value **v )
+{
+ return valuearray_first_value(vs->va,v);
+}
+
+int
+slapi_valueset_next_value( Slapi_ValueSet *vs, int index, Slapi_Value **v)
+{
+ return valuearray_next_value(vs->va,index,v);
+}
+
+int
+slapi_valueset_count( const Slapi_ValueSet *vs)
+{
+ int r=0;
+ if (NULL != vs)
+ {
+ if(!valuearray_isempty(vs->va))
+ {
+ r= valuearray_count(vs->va);
+ }
+ }
+ return r;
+}
+
+int
+valueset_isempty( const Slapi_ValueSet *vs)
+{
+ return valuearray_isempty(vs->va);
+}
+
+
+Slapi_Value *
+slapi_valueset_find(const Slapi_Attr *a, const Slapi_ValueSet *vs, const Slapi_Value *v)
+{
+ Slapi_Value *r= NULL;
+ if(!valuearray_isempty(vs->va))
+ {
+ int i= valuearray_find(a,vs->va,v);
+ if(i!=-1)
+ {
+ r= vs->va[i];
+ }
+ }
+ return r;
+}
+
+/*
+ * The value is found in the set, removed and returned.
+ * The caller is responsible for freeing the value.
+ */
+Slapi_Value *
+valueset_remove_value(const Slapi_Attr *a, Slapi_ValueSet *vs, const Slapi_Value *v)
+{
+ Slapi_Value *r= NULL;
+ if(!valuearray_isempty(vs->va))
+ {
+ r= valuearray_remove_value(a, vs->va, v);
+ }
+ return r;
+}
+
+/*
+ * Remove any values older than the CSN.
+ */
+int
+valueset_purge(Slapi_ValueSet *vs, const CSN *csn)
+{
+ int r= 0;
+ if(!valuearray_isempty(vs->va))
+ {
+ r= valuearray_purge(&vs->va, csn);
+ }
+ return r;
+}
+
+Slapi_Value **
+valueset_get_valuearray(const Slapi_ValueSet *vs)
+{
+ return (Slapi_Value**)vs->va;
+}
+
+size_t
+valueset_size(const Slapi_ValueSet *vs)
+{
+ size_t s= 0;
+ if(!valuearray_isempty(vs->va))
+ {
+ s= valuearray_size(vs->va);
+ }
+ return s;
+}
+
+/*
+ * The value array is passed in by value.
+ */
+void
+valueset_add_valuearray(Slapi_ValueSet *vs, Slapi_Value **addvals)
+{
+ if(!valuearray_isempty(addvals))
+ {
+ valuearray_add_valuearray(&vs->va, addvals, 0);
+ }
+}
+
+void
+valueset_add_valuearray_ext(Slapi_ValueSet *vs, Slapi_Value **addvals, PRUint32 flags)
+{
+ if(!valuearray_isempty(addvals))
+ {
+ valuearray_add_valuearray(&vs->va, addvals, flags);
+ }
+}
+
+/*
+ * The value is passed in by value.
+ */
+void
+slapi_valueset_add_value(Slapi_ValueSet *vs, const Slapi_Value *addval)
+{
+ valuearray_add_value(&vs->va,addval);
+}
+
+void
+slapi_valueset_add_value_ext(Slapi_ValueSet *vs, Slapi_Value *addval, unsigned long flags)
+{
+ Slapi_Value *oneval[2];
+ oneval[0]= (Slapi_Value*)addval;
+ oneval[1]= NULL;
+ valuearray_add_valuearray(&vs->va, oneval, flags);
+}
+
+/*
+ * The string is passed in by value.
+ */
+void
+valueset_add_string(Slapi_ValueSet *vs, const char *s, CSNType t, const CSN *csn)
+{
+ Slapi_Value v;
+ value_init(&v,NULL,t,csn);
+ slapi_value_set_string(&v,s);
+ valuearray_add_value(&vs->va,&v);
+ value_done(&v);
+}
+
+/*
+ * The value set is passed in by value.
+ */
+void
+valueset_add_valueset(Slapi_ValueSet *vs1, const Slapi_ValueSet *vs2)
+{
+ if (vs1 && vs2)
+ valueset_add_valuearray(vs1, vs2->va);
+}
+
+void
+valueset_remove_string(const Slapi_Attr *a, Slapi_ValueSet *vs, const char *s)
+{
+ Slapi_Value v;
+ value_init(&v,NULL,CSN_TYPE_NONE,NULL);
+ slapi_value_set_string(&v,s);
+ valuearray_remove_value(a,vs->va,&v);
+ value_done(&v);
+}
+
+void
+valueset_update_csn(Slapi_ValueSet *vs, CSNType t, const CSN *csn)
+{
+ if(!valuearray_isempty(vs->va))
+ {
+ valuearray_update_csn(vs->va,t,csn);
+ }
+}
+
+/*
+ * If we are adding or deleting SLAPD_MODUTIL_TREE_THRESHHOLD or more
+ * entries, we use an AVL tree to speed up searching for duplicates or
+ * values we are trying to delete. This threshhold is somewhat arbitrary;
+ * we should really take some measurements to determine an optimal number.
+ */
+#define SLAPD_MODUTIL_TREE_THRESHHOLD 5
+/*
+ * Remove an array of values from a value set.
+ * The removed values are passed back in an array.
+ *
+ * Returns
+ * LDAP_SUCCESS - OK.
+ * LDAP_NO_SUCH_ATTRIBUTE - A value to be deleted was not in the value set.
+ * LDAP_OPERATIONS_ERROR - Something very bad happened.
+ */
+int
+valueset_remove_valuearray(Slapi_ValueSet *vs, const Slapi_Attr *a, Slapi_Value **valuestodelete, int flags, Slapi_Value ***va_out)
+{
+ int rc= LDAP_SUCCESS;
+ if(!valuearray_isempty(vs->va))
+ {
+ int numberofvaluestodelete= valuearray_count(valuestodelete);
+ struct valuearrayfast vaf_out;
+ if ( va_out )
+ {
+ valuearrayfast_init(&vaf_out,*va_out);
+ }
+
+ /*
+ * determine whether we should use an AVL tree of values or not
+ */
+ if ( numberofvaluestodelete >= SLAPD_MODUTIL_TREE_THRESHHOLD)
+ {
+ /*
+ * Several values to delete: first build an AVL tree that
+ * holds all of the existing values and use that to find
+ * the values we want to delete.
+ */
+ Avlnode *vtree = NULL;
+ int numberofexistingvalues= slapi_valueset_count(vs);
+ rc= valuetree_add_valuearray( a->a_type, a->a_plugin, vs->va, &vtree, NULL );
+ if ( rc!=LDAP_SUCCESS )
+ {
+ /*
+ * failed while constructing AVL tree of existing
+ * values... something bad happened.
+ */
+ rc= LDAP_OPERATIONS_ERROR;
+ }
+ else
+ {
+ int i;
+ /*
+ * find and mark all the values that are to be deleted
+ */
+ for ( i = 0; rc == LDAP_SUCCESS && valuestodelete[i] != NULL; ++i )
+ {
+ int index= 0;
+ rc = valuetree_find( a, valuestodelete[i], vtree, &index );
+ if(rc==LDAP_SUCCESS)
+ {
+ if(vs->va[index]!=NULL)
+ {
+ /* Move the value to be removed to the out array */
+ if ( va_out )
+ {
+ if (vs->va[index]->v_csnset && (flags & SLAPI_VALUE_FLAG_PRESERVECSNSET))
+ {
+ valuestodelete[i]->v_csnset = csnset_dup (vs->va[index]->v_csnset);
+ }
+ valuearrayfast_add_value_passin(&vaf_out,vs->va[index]);
+ vs->va[index] = NULL;
+ }
+ else
+ {
+ if (flags & SLAPI_VALUE_FLAG_PRESERVECSNSET)
+ {
+ valuestodelete[i]->v_csnset = vs->va[index]->v_csnset;
+ vs->va[index]->v_csnset = NULL;
+ }
+ slapi_value_free ( & vs->va[index] );
+ }
+ }
+ else
+ {
+ /* We already deleted this value... */
+ if((flags & SLAPI_VALUE_FLAG_IGNOREERROR) == 0)
+ {
+ /* ...that's an error. */
+ rc= LDAP_NO_SUCH_ATTRIBUTE;
+ }
+ }
+ }
+ else
+ {
+ /* Couldn't find the value to be deleted */
+ if(rc==LDAP_NO_SUCH_ATTRIBUTE && (flags & SLAPI_VALUE_FLAG_IGNOREERROR ))
+ {
+ rc= LDAP_SUCCESS;
+ }
+ }
+ }
+ valuetree_free( &vtree );
+
+ if ( rc != LDAP_SUCCESS )
+ {
+ LDAPDebug( LDAP_DEBUG_ANY,"could not find value %d for attr %s (%s)\n", i-1, a->a_type, ldap_err2string( rc ));
+ }
+ else
+ {
+ /* Shunt up all the remaining values to cover the deleted ones. */
+ valuearray_compress(vs->va,numberofexistingvalues);
+ }
+ }
+ }
+ else
+ {
+ /* We don't have to delete very many values, so we use brute force. */
+ int i;
+ for ( i = 0; rc==LDAP_SUCCESS && valuestodelete[i] != NULL; ++i )
+ {
+ Slapi_Value *found= valueset_remove_value(a, vs, valuestodelete[i]);
+ if(found!=NULL)
+ {
+ if ( va_out )
+ {
+ if (found->v_csnset && (flags & SLAPI_VALUE_FLAG_PRESERVECSNSET))
+ {
+ valuestodelete[i]->v_csnset = csnset_dup (found->v_csnset);
+ }
+ valuearrayfast_add_value_passin(&vaf_out,found);
+ }
+ else
+ {
+ if (flags & SLAPI_VALUE_FLAG_PRESERVECSNSET)
+ {
+ valuestodelete[i]->v_csnset = found->v_csnset;
+ found->v_csnset = NULL;
+ }
+ slapi_value_free ( & found );
+ }
+ }
+ else
+ {
+ if((flags & SLAPI_VALUE_FLAG_IGNOREERROR) == 0)
+ {
+ LDAPDebug( LDAP_DEBUG_ARGS,"could not find value %d for attr %s\n", i-1, a->a_type, 0 );
+ rc= LDAP_NO_SUCH_ATTRIBUTE;
+ }
+ }
+ }
+ }
+ if ( va_out )
+ {
+ *va_out= vaf_out.va;
+ if(rc!=LDAP_SUCCESS)
+ {
+ valuearray_free(va_out);
+ }
+ }
+ }
+ return rc;
+}
+
+/*
+ * Check if the set of values in the valueset and the valuearray intersect.
+ *
+ * Returns
+ * LDAP_SUCCESS - No intersection.
+ * LDAP_NO_SUCH_ATTRIBUTE - There is an intersection.
+ * LDAP_OPERATIONS_ERROR - There are duplicate values in the value set already.
+ */
+int
+valueset_intersectswith_valuearray(Slapi_ValueSet *vs, const Slapi_Attr *a, Slapi_Value **values, int *duplicate_index )
+{
+ int rc= LDAP_SUCCESS;
+
+ if ( duplicate_index ) {
+ *duplicate_index = -1;
+ }
+
+ if(valuearray_isempty(vs->va))
+ {
+ /* No intersection */
+ }
+ else
+ {
+ int numberofvalues= valuearray_count(values);
+ /*
+ * determine whether we should use an AVL tree of values or not
+ */
+ if (numberofvalues==0)
+ {
+ /* No intersection */
+ }
+ else if ( numberofvalues >= SLAPD_MODUTIL_TREE_THRESHHOLD)
+ {
+ /*
+ * Several values to add: use an AVL tree to detect duplicates.
+ */
+ Avlnode *vtree = NULL;
+ rc= valuetree_add_valuearray( a->a_type, a->a_plugin, vs->va, &vtree, duplicate_index );
+ if(rc==LDAP_OPERATIONS_ERROR)
+ {
+ /* There were already duplicate values in the value set */
+ }
+ else
+ {
+ rc= valuetree_add_valuearray( a->a_type, a->a_plugin, values, &vtree, duplicate_index );
+ /*
+ * Returns LDAP_OPERATIONS_ERROR if something very bad happens.
+ * Or LDAP_TYPE_OR_VALUE_EXISTS if a value already exists.
+ */
+ }
+ valuetree_free( &vtree );
+ }
+ else
+ {
+ /*
+ * Small number of values to add: don't bother constructing
+ * an AVL tree, etc. since it probably isn't worth the time.
+ *
+ * JCM - This is actually quite slow because the comparison function is looked up many times.
+ */
+ int i;
+ for ( i = 0; rc == LDAP_SUCCESS && values[i] != NULL; ++i )
+ {
+ if(valuearray_find(a, vs->va, values[i])!=-1)
+ {
+ rc = LDAP_TYPE_OR_VALUE_EXISTS;
+ *duplicate_index = i;
+ break;
+ }
+ }
+ }
+ }
+ return rc;
+}
+
+Slapi_ValueSet *
+valueset_dup(const Slapi_ValueSet *dupee)
+{
+ Slapi_ValueSet *duped= (Slapi_ValueSet *)slapi_ch_calloc(1,sizeof(Slapi_ValueSet));
+ if (NULL!=duped)
+ {
+ valueset_add_valuearray( duped, dupee->va );
+ }
+ return duped;
+}
+
+/* quickly throw away any old contents of this valueset, and stick in the
+ * new ones.
+ */
+void
+valueset_replace(Slapi_ValueSet *vs, Slapi_Value **vals)
+{
+ if(!valuearray_isempty(vs->va))
+ {
+ slapi_valueset_done(vs);
+ }
+ vs->va = vals;
+}
+
+/*
+ * Search the value set for each value to be update,
+ * and update the value with the CSN provided.
+ * Updated values are moved from the valuestoupdate
+ * array to the valueupdated array.
+ */
+void
+valueset_update_csn_for_valuearray(Slapi_ValueSet *vs, const Slapi_Attr *a, Slapi_Value **valuestoupdate, CSNType t, const CSN *csn, Slapi_Value ***valuesupdated)
+{
+ if(!valuearray_isempty(valuestoupdate) &&
+ !valuearray_isempty(vs->va))
+ {
+ /*
+ * determine whether we should use an AVL tree of values or not
+ */
+ struct valuearrayfast vaf_valuesupdated;
+ int numberofvaluestoupdate= valuearray_count(valuestoupdate);
+ valuearrayfast_init(&vaf_valuesupdated,*valuesupdated);
+ if (numberofvaluestoupdate>=SLAPD_MODUTIL_TREE_THRESHHOLD)
+ {
+ int i;
+ Avlnode *vtree = NULL;
+ int rc= valuetree_add_valuearray( a->a_type, a->a_plugin, vs->va, &vtree, NULL );
+ PR_ASSERT(rc==LDAP_SUCCESS);
+ for (i=0;valuestoupdate[i]!=NULL;++i)
+ {
+ int index= 0;
+ rc = valuetree_find( a, valuestoupdate[i], vtree, &index );
+ if(rc==LDAP_SUCCESS)
+ {
+ value_update_csn(vs->va[index],t,csn);
+ valuearrayfast_add_value_passin(&vaf_valuesupdated,valuestoupdate[i]);
+ valuestoupdate[i] = NULL;
+ }
+ }
+ valuetree_free(&vtree);
+ }
+ else
+ {
+ int i;
+ for (i=0;valuestoupdate[i]!=NULL;++i)
+ {
+ int index= valuearray_find(a, vs->va, valuestoupdate[i]);
+ if(index!=-1)
+ {
+ value_update_csn(vs->va[index],t,csn);
+ valuearrayfast_add_value_passin(&vaf_valuesupdated,valuestoupdate[i]);
+ valuestoupdate[i]= NULL;
+ }
+ }
+ }
+ valuearray_compress(valuestoupdate,numberofvaluestoupdate);
+ *valuesupdated= vaf_valuesupdated.va;
+ }
+}
diff --git a/ldap/servers/slapd/vattr.c b/ldap/servers/slapd/vattr.c
new file mode 100644
index 00000000..ca360b08
--- /dev/null
+++ b/ldap/servers/slapd/vattr.c
@@ -0,0 +1,2387 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+
+/*
+ Virtual Attributes
+
+ This file implements the "virtual attribute API", which is the interface
+ by which any joe servercode gets the values of attributes associated with an
+ entry which are _not_ stored with the entry. Attributes which _are_ stored
+ with the entry will also be returned by this interface, unless the caller
+ specifies the SLAPI_REALATTRS_ONLY flag. This means that the casual caller
+ looking for attribute values should probably call the virtual attribute interface
+ rather than the regular Slapi_Entry_Attr_Find... and so on calls.
+ This interface is different from that one in that the data returned is
+ a copy, to be freed by the caller. Details on how to free the returned data is
+ given individually for each function.
+
+ The thing is implemented with a service provider model. The code in here
+ does NOT know how to generate computed attr values etc, it just calls the
+ registered providers of such information. It also takes care of any crusty
+ special cases. Everything above the line here is crispy clean.
+
+ Implicit in this interface is the assumption that the value of an attribute
+ can only depend on the entry and some independent stored state. For example,
+ the value can't depend upon who is asking, since we don't pass that information
+ over the interface.
+
+ More design: we'd like to have the regular result returning code in result.c call this
+ interface. However, as it stands, his would incur a malloc, a copy and a free for every
+ value. Too expensive. So, it would be good to modify the interface such that when we
+ retrieve values from inside the entry, we just fetch a pointer like before. When we
+ retrieve values which are generated, we get copies of the data (or can we get pointers
+ there too ?? --- no).
+
+ One way to achieve this: allow the caller to say that they perfer to receive pointers
+ and not copies. They are then informed by the function whether they did receive pointers
+ or copies. They then call the free function only when needed. The implicit lifetime of
+ the returned pointers is the lifetime of the entry object passed into the function.
+ Nasty, but one has to do these things in the name of the god performance.
+
+ DBDB: remember to rename the structures mark complained weren't compliant with the slapi naming scheme.
+
+*/
+
+#include "slap.h"
+
+#include "vattr_spi.h"
+#include "statechange.h"
+
+#ifdef SOURCEFILE
+#undef SOURCEFILE
+#endif
+#define SOURCEFILE "vattr.c"
+static char *sourcefile = SOURCEFILE;
+
+/* Define only for module test code */
+/* #define VATTR_TEST_CODE */
+
+/* Loop context structure */
+struct _vattr_context {
+ Slapi_PBlock *pb;
+ unsigned int vattr_context_loop_count;
+ unsigned int error_displayed;
+};
+#define VATTR_LOOP_COUNT_MAX 50
+
+typedef vattr_sp_handle vattr_sp_handle_list;
+
+/* Local prototypes */
+static int vattr_map_create();
+static void vattr_map_destroy();
+int vattr_map_sp_insert(char *type_to_add, vattr_sp_handle *sp, void *hint);
+vattr_sp_handle_list *vattr_map_sp_getlist(char *type_to_find);
+vattr_sp_handle_list *vattr_map_namespace_sp_getlist(Slapi_DN *dn, const char *type_to_find);
+vattr_sp_handle_list *vattr_map_sp_get_complete_list();
+vattr_sp_handle *vattr_map_sp_first(vattr_sp_handle_list *list,void **hint);
+vattr_sp_handle *vattr_map_sp_next(vattr_sp_handle_list *list,void **hint);
+vattr_sp_handle *vattr_list_sp_first(vattr_sp_handle_list *list);
+vattr_sp_handle *vattr_list_sp_next(vattr_sp_handle_list *list);
+int vattr_call_sp_get_value(vattr_sp_handle *handle, vattr_context *c, Slapi_Entry *e, vattr_get_thang *my_get, char *type, Slapi_ValueSet** results,int *type_name_disposition, char** actual_type_name, int flags, int *buffer_flags, void *hint);
+int vattr_call_sp_get_batch_values(vattr_sp_handle *handle, vattr_context *c, Slapi_Entry *e, vattr_get_thang *my_get, char **type, Slapi_ValueSet*** results,int **type_name_disposition, char*** actual_type_name, int flags, int *buffer_flags, void** hint);
+int vattr_call_sp_compare_value(vattr_sp_handle *handle, vattr_context *c, Slapi_Entry *e, vattr_get_thang *my_get, const char *type, Slapi_Value* test_this,int *result, int flags, void* hint);
+int vattr_call_sp_get_types(vattr_sp_handle *handle,Slapi_Entry *e,vattr_type_list_context *type_context,int flags);
+void schema_changed_callback(Slapi_Entry *e, char *dn, int modtype, Slapi_PBlock *pb, void *caller_data);
+int slapi_vattrspi_register_internal(vattr_sp_handle **h, vattr_get_fn_type get_fn, vattr_get_ex_fn_type get_ex_fn, vattr_compare_fn_type compare_fn, vattr_types_fn_type types_fn, void *options);
+
+#ifdef VATTR_TEST_CODE
+int vattr_basic_sp_init();
+#endif
+
+void **statechange_api;
+
+/* Housekeeping Functions, called by server startup/shutdown code */
+
+/* Called on server startup, init all structures etc */
+void vattr_init()
+{
+ statechange_api = 0;
+ vattr_map_create();
+
+#ifdef VATTR_TEST_CODE
+ vattr_basic_sp_init();
+#endif
+}
+
+/* Called on server shutdown, free all structures, inform service providers that we're going down etc */
+void vattr_cleanup()
+{
+ vattr_map_destroy();
+}
+
+/* The public interface functions start here */
+
+/* Function which returns the value(s) of an attribute, given an entry and the attribute type name */
+/* Return 0 if OK, ?? if attr doesn't exist, ?? if some error condition ?? if result doesn't need to be free'ed */
+
+int slapi_vattr_values_get(/* Entry we're interested in */ Slapi_Entry *e, /* attr type name */ char *type, /* pointer to result set */ Slapi_ValueSet** results,int *type_name_disposition, char** actual_type_name, int flags, int *buffer_flags)
+{
+ vattr_context *c = NULL;
+ return slapi_vattr_values_get_sp(c,e,type,results,type_name_disposition,actual_type_name,flags, buffer_flags);
+}
+
+int slapi_vattr_values_get_ex(/* Entry we're interested in */ Slapi_Entry *e,/* attr type name */ char *type, /* pointer to result set */ Slapi_ValueSet*** results,int **type_name_disposition, char*** actual_type_name, int flags, int *buffer_flags, int *subtype_count)
+{
+ vattr_context *c = NULL;
+ return slapi_vattr_values_get_sp_ex(c,e,type,results,type_name_disposition,actual_type_name,flags, buffer_flags, subtype_count);
+}
+
+int slapi_vattr_namespace_values_get(/* Entry we're interested in */ Slapi_Entry *e, /* backend namespace dn*/ Slapi_DN *namespace_dn, /* attr type name */ char *type, /* pointer to result set */ Slapi_ValueSet*** results,int **type_name_disposition, char*** actual_type_name, int flags, int *buffer_flags, int *subtype_count)
+{
+ vattr_context *c = NULL;
+ return slapi_vattr_namespace_values_get_sp(c,e,namespace_dn,type,results,type_name_disposition,actual_type_name,flags, buffer_flags, subtype_count);
+}
+
+
+/*
+ * If pointers into the entry were requested then we might have
+ * a stashed pointer to the entry values, otherwise will be null.
+ */
+Slapi_ValueSet *vattr_typethang_get_values(vattr_type_thang *t)
+{
+ return t->type_values;
+}
+
+/*
+ * This can be much faster than using slapi_vattr_values_get()
+ * when you have a vattr_type_thang list returned from slapi_vattr_list_types().
+ *
+ * We call the vattr SPs to get values for an attribute type in the list only
+ * if the results field for that attribute type is null.
+ * If the type list comes from slapi_vattr_list_types() then the value is null
+ * only for attributes for which an SP wishes to provide a value, so fo the other
+ * ones don't bother calling the SPs again--just use the real value we picked up
+ * from the entry.
+ *
+ */
+int slapi_vattr_values_type_thang_get(
+ Slapi_Entry *e,
+ vattr_type_thang *type_thang,
+ Slapi_ValueSet **results,
+ int *type_name_disposition,
+ char **actual_type_name,
+ int flags,
+ int *buffer_flags
+)
+{
+ int rc = 0;
+ char *type = NULL;
+
+ type = vattr_typethang_get_name(type_thang);
+ *results = vattr_typethang_get_values(type_thang);
+
+ if (*results != NULL) {
+ /* we already have a pointer directly into the entry */
+ *actual_type_name = type;
+ *type_name_disposition =
+ SLAPI_VIRTUALATTRS_TYPE_NAME_MATCHED_EXACTLY_OR_ALIAS;
+ *buffer_flags = SLAPI_VIRTUALATTRS_RETURNED_POINTERS;
+ } else {
+ /* fall back to usual implementation */
+ rc = slapi_vattr_values_get(e,type,results,
+ type_name_disposition,
+ actual_type_name,
+ flags,buffer_flags);
+ }
+
+ return rc;
+}
+
+static void vattr_helper_get_entry_conts(Slapi_Entry *e,char *type, vattr_get_thang *my_get)
+{
+ Slapi_Attr *a = NULL;
+ void *dummy = 0;
+
+ a = attrlist_find_ex(e->e_attrs,type,&(my_get->get_name_disposition), &(my_get->get_type_name), &dummy);
+ if (a) {
+ my_get->get_present = 1;
+ my_get->get_present_values = &(a->a_present_values);
+ my_get->get_attr = a;
+ }
+}
+
+static int vattr_helper_get_entry_conts_with_subtypes(Slapi_Entry *e,const char *type, vattr_get_thang **my_get)
+{
+ Slapi_Attr *a = NULL;
+ void *hint = 0;
+ int counter = 0;
+ int attr_count = attrlist_count_subtypes(e->e_attrs,type);
+
+ if(attr_count > 0)
+ {
+ *my_get = (vattr_get_thang *)slapi_ch_calloc(attr_count, sizeof(vattr_get_thang));
+
+ /* pick up attributes with sub-types and slip into the get_thang list */
+ for(counter = 0; counter < attr_count; counter++)
+ {
+ a = attrlist_find_ex(e->e_attrs,type,&((*my_get)[counter].get_name_disposition), &((*my_get)[counter].get_type_name), &hint);
+ if (a) {
+ (*my_get)[counter].get_present = 1;
+ (*my_get)[counter].get_present_values = &(a->a_present_values);
+ (*my_get)[counter].get_attr = a;
+ }
+ }
+ }
+
+ return attr_count;
+}
+
+static int vattr_helper_get_entry_conts_no_subtypes(Slapi_Entry *e,const char *type, vattr_get_thang **my_get)
+{
+ int attr_count = 0;
+ Slapi_Attr *a = attrlist_find(e->e_attrs,type);
+
+ if (a) {
+ attr_count = 1;
+ *my_get = (vattr_get_thang *)slapi_ch_calloc(1, sizeof(vattr_get_thang));
+ (*my_get)[0].get_present = 1;
+ (*my_get)[0].get_name_disposition = SLAPI_VIRTUALATTRS_TYPE_NAME_MATCHED_EXACTLY_OR_ALIAS;
+ (*my_get)[0].get_type_name = a->a_type;
+ (*my_get)[0].get_present_values = &(a->a_present_values);
+ (*my_get)[0].get_attr = a;
+ }
+
+ return attr_count;
+}
+
+static int vattr_helper_get_entry_conts_ex(Slapi_Entry *e,const char *type, vattr_get_thang **my_get, int suppress_subtypes)
+{
+ int rc;
+
+ if ( suppress_subtypes ) {
+ rc = vattr_helper_get_entry_conts_no_subtypes(e, type, my_get);
+ } else {
+ rc = vattr_helper_get_entry_conts_with_subtypes(e, type, my_get);
+ }
+
+ return rc;
+}
+
+
+vattr_context *vattr_context_new( Slapi_PBlock *pb )
+{
+ vattr_context *c = (vattr_context *)slapi_ch_calloc(1, sizeof(vattr_context));
+ /* The payload is zero, which is what we want */
+ if ( c ) {
+ c->pb = pb;
+ }
+
+ return c;
+}
+
+static int vattr_context_check(vattr_context *c)
+{
+ /* Observe the loop count and see if it's higher than is allowed */
+ if (c->vattr_context_loop_count > VATTR_LOOP_COUNT_MAX) {
+ return SLAPI_VIRTUALATTRS_LOOP_DETECTED;
+ } else return 0;
+}
+
+static void vattr_context_mark(vattr_context *c)
+{
+ c->vattr_context_loop_count += 1;
+}
+
+static int vattr_context_unmark(vattr_context *c)
+{
+ return (c->vattr_context_loop_count -= 1);
+}
+
+/* modify the context structure on exit from a vattr sp function */
+static void vattr_context_ungrok(vattr_context **c)
+{
+ /* Decrement the loop count */
+ if (0 == vattr_context_unmark(*c)) {
+ /* If necessary, delete the structure */
+ slapi_ch_free((void **)c);
+ }
+}
+
+/* Check and mess with the context structure on entry to a vattr sp function */
+static int vattr_context_grok(vattr_context **c)
+{
+ int rc = 0;
+ /* First check that we've not got into an infinite loop.
+ We do so by means of the vattr_context structure.
+ */
+
+ /* Do we have a context at all ?? */
+ if (NULL == *c) {
+ /* No, so let's make one */
+ *c = vattr_context_new( NULL );
+ if (NULL == *c) {
+ return ENOMEM;
+ }
+ } else {
+ /* Yes, so let's check its contents */
+ rc = vattr_context_check(*c);
+ }
+ /* mark the context as having been used once */
+ vattr_context_mark(*c);
+ return rc;
+}
+
+
+
+/* keep track of error messages so we don't spam the error log */
+static void vattr_context_set_loop_msg_displayed(vattr_context **c)
+{
+ (*c)->error_displayed = 1;
+}
+
+static int vattr_context_is_loop_msg_displayed(vattr_context **c)
+{
+ return (*c)->error_displayed;
+}
+
+/*
+ * vattr_test_filter:
+ *
+ * . tests an ava, presence or substring filter against e.
+ * . group these filter types together to avoid having to duplicate the
+ * . vattr specific code in three seperate routines.
+ * . handles virtual attrs in the filter.
+ * . does update the vattrcache if a call to this calculates vattrs
+ *
+ * returns: 0 filter matched
+ * -1 filter did not match
+ * >0 an ldap error code
+ *
+*/
+int vattr_test_filter( /* Entry we're interested in */ Slapi_Entry *e,
+ Slapi_Filter *f,
+ filter_type_t filter_type,
+ char * type) {
+ int rc = -1;
+ int sp_bit = 0; /* Set if an SP supplied an answer */
+ vattr_sp_handle_list *list = NULL;
+ Slapi_DN *sdn;
+ Slapi_Backend *be;
+ Slapi_DN *namespace_dn;
+
+ /* get the namespace this entry belongs to */
+ sdn = slapi_entry_get_sdn( e );
+ be = slapi_be_select( sdn );
+ namespace_dn = (Slapi_DN*)slapi_be_getsuffix(be, 0);
+
+ /* Look for attribute in the map */
+
+ if(namespace_dn)
+ {
+ list = vattr_map_namespace_sp_getlist(namespace_dn, type);
+ }
+ else
+ {
+ list = vattr_map_namespace_sp_getlist(NULL, type);
+ }
+
+ if (list) {
+ vattr_sp_handle *current_handle = NULL;
+ Slapi_Attr *cache_attr = NULL;
+ /* first lets consult the cache to save work */
+ int cache_status;
+
+ cache_status = slapi_entry_vattrcache_findAndTest(e, type,
+ f,
+ filter_type,
+ &rc);
+ switch(cache_status)
+ {
+ case SLAPI_ENTRY_VATTR_RESOLVED_EXISTS: /* cached vattr */
+ {
+ sp_bit = 1;
+ break;
+ }
+
+ case SLAPI_ENTRY_VATTR_RESOLVED_ABSENT: /* does not exist */
+ break; /* look in entry */
+
+ case SLAPI_ENTRY_VATTR_NOT_RESOLVED: /* not resolved */
+ default: /* any other result, resolve */
+ {
+ int flags = SLAPI_VIRTUALATTRS_REQUEST_POINTERS;
+ void *hint = NULL;
+ Slapi_ValueSet **results = NULL; /* pointer to result set */
+ int *type_name_disposition;
+ char **actual_type_name;
+ int buffer_flags;
+ vattr_get_thang my_get = {0};
+ vattr_context ctx;
+ /* bit cacky, but need to make a null terminated lists for now
+ * for the (unimplemented and so fake) batch attribute request
+ */
+ char *types[2];
+ void *hint_list[2];
+
+ types[0] = type;
+ types[1] = 0;
+ hint_list[1] = 0;
+
+ /* set up some local context */
+ ctx.vattr_context_loop_count=1;
+ ctx.error_displayed = 0;
+
+ for (current_handle = vattr_map_sp_first(list,&hint); current_handle; current_handle = vattr_map_sp_next(current_handle,&hint))
+ {
+ hint_list[0] = hint;
+
+ rc = vattr_call_sp_get_batch_values(current_handle,&ctx,e,
+ &my_get,types,&results,&type_name_disposition,
+ &actual_type_name,flags,&buffer_flags, hint_list);
+
+ if (0 == rc)
+ {
+ sp_bit = 1;
+ break;
+ }
+ }
+
+ if(!sp_bit)
+ {
+ /*
+ * No vattr sp supplied an answer so will look in the
+ * entry itself.
+ * but first lets cache the no result
+ */
+ slapi_entry_vattrcache_merge_sv(e, type, NULL );
+
+ }
+ else
+ {
+ /*
+ * A vattr sp supplied an answer.
+ * so turn the value into a Slapi_Attr, pass
+ * to the syntax plugin for comparison.
+ */
+
+ if ( filter_type == FILTER_TYPE_AVA ||
+ filter_type == FILTER_TYPE_SUBSTRING ) {
+ Slapi_Attr *a = NULL;
+ int i=0;
+ /* may need this when we have a passin interface for set_valueset */
+ /*Slapi_ValueSet null_valueset = {0};*/
+
+ rc=-1;
+
+ if(results && actual_type_name && type_name_disposition)
+ {
+ while(results[i] && rc)
+ {
+ a = slapi_attr_new();
+ slapi_attr_init(a, type);
+ /* a now contains a *copy* of results */
+ slapi_attr_set_valueset( a, results[i]);
+
+ if ( filter_type == FILTER_TYPE_AVA ) {
+ rc = plugin_call_syntax_filter_ava( a,
+ f->f_choice, &f->f_ava );
+ } else if ( filter_type == FILTER_TYPE_SUBSTRING) {
+ rc = plugin_call_syntax_filter_sub( a,
+ &f->f_sub);
+ }
+
+ /*
+ * Cache stuff: dups results
+ */
+ slapi_entry_vattrcache_merge_sv(e, actual_type_name[i],
+ results[i] );
+ /*
+ * Free stuff, just in case we did not
+ * get pointers.
+ */
+ slapi_vattr_values_free( &(results[i]),
+ &(actual_type_name[i]),
+ buffer_flags);
+ /* may need this when we support a passin set_valueset */
+ /*slapi_attr_set_valueset( a, &null_valueset);*/
+ /* since a contains a copy of results, we must free it */
+ slapi_attr_free(&a);
+ i++;
+ }
+ }
+
+ slapi_ch_free((void**)&results);
+ slapi_ch_free((void**)&actual_type_name);
+ slapi_ch_free((void**)&type_name_disposition);
+
+ } else if ( filter_type == FILTER_TYPE_PRES ) {
+ /*
+ * Cache stuff: dups results
+ */
+ int i=0;
+
+ while(results[i])
+ {
+ slapi_entry_vattrcache_merge_sv(e, actual_type_name[i],
+ results[i] );
+ /*
+ * Free stuff, just in case we did not
+ * get pointers.
+ */
+ slapi_vattr_values_free( &results[i],
+ &actual_type_name[i],
+ buffer_flags);
+ }
+ slapi_ch_free((void**)&results);
+ slapi_ch_free((void**)&actual_type_name);
+ slapi_ch_free((void**)&type_name_disposition);
+ }
+ }
+
+ break;
+ }
+ }/* switch */
+ }
+ /* If no SP supplied the answer, take it from the entry */
+ if (!sp_bit)
+ {
+ int acl_test_done;
+
+ if ( filter_type == FILTER_TYPE_AVA ) {
+
+ rc = test_ava_filter( NULL /* pb not needed */,
+ e, e->e_attrs, &f->f_ava,
+ f->f_choice,
+ 0 /* no access check */,
+ 0 /* do test filter */,
+ &acl_test_done);
+
+ } else if ( filter_type == FILTER_TYPE_SUBSTRING ) {
+
+ rc = test_substring_filter( NULL, e, f, 0 /* no access check */,
+ 0 /* do test filter */, &acl_test_done);
+
+ } else if ( filter_type == FILTER_TYPE_PRES ) {
+
+ rc = test_presence_filter( NULL, e, f->f_type,
+ 0 /* no access check */,
+ 0 /* do test filter */,
+ &acl_test_done);
+ }
+ }
+ return rc;
+}
+/*
+ * deprecated in favour of slapi_vattr_values_get_sp_ex() which
+ * returns subtypes too.
+*/
+SLAPI_DEPRECATED int
+slapi_vattr_values_get_sp(vattr_context *c,
+ /* Entry we're interested in */ Slapi_Entry *e,
+ /* attr type name */ char *type,
+ /* pointer to result set */ Slapi_ValueSet** results,
+ int *type_name_disposition,
+ char** actual_type_name, int flags,
+ int *buffer_flags)
+{
+
+ PRBool use_local_ctx=PR_FALSE;
+ vattr_context ctx;
+ int rc = 0;
+ int sp_bit = 0; /* Set if an SP supplied an answer */
+ vattr_sp_handle_list *list = NULL;
+
+ vattr_get_thang my_get = {0};
+
+ if (c != NULL) {
+ rc = vattr_context_grok(&c);
+ if (0 != rc) {
+ if(!vattr_context_is_loop_msg_displayed(&c))
+ {
+ /* Print a handy error log message */
+ LDAPDebug(LDAP_DEBUG_ANY,"Detected virtual attribute loop in get on entry %s, attribute %s\n", slapi_entry_get_dn_const(e), type, 0);
+ vattr_context_set_loop_msg_displayed(&c);
+ }
+ return rc;
+ }
+ } else {
+ use_local_ctx=PR_TRUE;
+ ctx.vattr_context_loop_count=1;
+ ctx.error_displayed = 0;
+ }
+
+ /* For attributes which are in the entry, we just need to get to the Slapi_Attr structure and yank out the slapi_value_set
+ structure. We either return a pointer directly to it, or we copy it, depending upon whether the caller asked us to try to
+ avoid copying.
+ */
+
+ /* First grok the entry, and remember what we saw. This call does no more than walk down the entry attribute list, do some string compares and copy pointers. */
+ vattr_helper_get_entry_conts(e,type, &my_get);
+ /* Having done that, we now consult the attribute map to find service providers who are interested */
+ /* Look for attribute in the map */
+ if(!(flags & SLAPI_REALATTRS_ONLY))
+ {
+ list = vattr_map_sp_getlist(type);
+ if (list) {
+ vattr_sp_handle *current_handle = NULL;
+ void *hint = NULL;
+ Slapi_Attr* cache_attr = 0;
+ char *vattr_type = NULL;
+ /* first lets consult the cache to save work */
+ int cache_status;
+
+ cache_status =
+ slapi_entry_vattrcache_find_values_and_type(e, type,
+ results,
+ actual_type_name);
+ switch(cache_status)
+ {
+ case SLAPI_ENTRY_VATTR_RESOLVED_EXISTS: /* cached vattr */
+ {
+ sp_bit = 1;
+
+ /* Complete analysis of type matching */
+ if ( 0 == slapi_attr_type_cmp( type , *actual_type_name, SLAPI_TYPE_CMP_EXACT) )
+ {
+ *type_name_disposition = SLAPI_VIRTUALATTRS_TYPE_NAME_MATCHED_EXACTLY_OR_ALIAS;
+ } else {
+ *type_name_disposition = SLAPI_VIRTUALATTRS_TYPE_NAME_MATCHED_SUBTYPE;
+ }
+
+ break;
+ }
+
+ case SLAPI_ENTRY_VATTR_RESOLVED_ABSENT: /* does not exist */
+ break; /* look in entry */
+
+ case SLAPI_ENTRY_VATTR_NOT_RESOLVED: /* not resolved */
+ default: /* any other result, resolve */
+ {
+ for (current_handle = vattr_map_sp_first(list,&hint); current_handle; current_handle = vattr_map_sp_next(current_handle,&hint))
+ {
+ if (use_local_ctx)
+ {
+ rc = vattr_call_sp_get_value(current_handle,&ctx,e,&my_get,type,results,type_name_disposition,actual_type_name,flags,buffer_flags, hint);
+ }
+ else
+ {
+ /* call this SP */
+ rc = vattr_call_sp_get_value(current_handle,c,e,&my_get,type,results,type_name_disposition,actual_type_name,flags,buffer_flags, hint);
+ }
+
+ if (0 == rc)
+ {
+ sp_bit = 1;
+ break;
+ }
+ }
+
+ if(!sp_bit)
+ {
+ /* clean up, we have failed and must now examine the
+ * entry itself
+ * But first lets cache the no result
+ * Creates the type (if necessary).
+ */
+ slapi_entry_vattrcache_merge_sv(e, type, NULL );
+
+ }
+ else
+ {
+ /*
+ * we need to cache the virtual attribute
+ * creates the type (if necessary) and dups
+ * results.
+ */
+ slapi_entry_vattrcache_merge_sv(e, *actual_type_name,
+ *results );
+ }
+
+ break;
+ }
+ }
+ }
+ }
+ /* If no SP supplied the answer, take it from the entry */
+ if (!sp_bit && !(flags & SLAPI_VIRTUALATTRS_ONLY))
+ {
+ rc = 0; /* reset return code (cause an sp must have failed) */
+ *type_name_disposition = my_get.get_name_disposition;
+
+ if (my_get.get_present) {
+ if (flags & SLAPI_VIRTUALATTRS_REQUEST_POINTERS) {
+ *results = my_get.get_present_values;
+ *actual_type_name = my_get.get_type_name;
+ } else {
+ *results = valueset_dup(my_get.get_present_values);
+ if (NULL == *results) {
+ rc = ENOMEM;
+ } else {
+ *actual_type_name = slapi_ch_strdup(my_get.get_type_name);
+ if (NULL == *actual_type_name) {
+ rc = ENOMEM;
+ }
+ }
+ }
+ if (flags & SLAPI_VIRTUALATTRS_REQUEST_POINTERS) {
+ *buffer_flags = SLAPI_VIRTUALATTRS_RETURNED_POINTERS;
+ } else {
+ *buffer_flags = SLAPI_VIRTUALATTRS_RETURNED_COPIES;
+ }
+ } else {
+ rc = SLAPI_VIRTUALATTRS_NOT_FOUND;
+ }
+ }
+ if (!use_local_ctx) {
+ vattr_context_ungrok(&c);
+ }
+ return rc;
+}
+
+/*
+ *
+ * returns 0: (no_error && type found ) in which case:
+ * results: contains the current values for type and
+ * all it's subtypes in e
+ * type_name_disposition: how each type was matched
+ * SLAPI_VIRTUALATTRS_TYPE_NAME_MATCHED_EXACTLY_OR_ALIAS
+ * SLAPI_VIRTUALATTRS_TYPE_NAME_MATCHED_SUBTYPE
+ * actual_type_name: type name as found
+ * flags: bit mask of options:
+ * SLAPI_REALATTRS_ONLY
+ * SLAPI_VIRTUALATTRS_ONLY
+ * SLAPI_VIRTUALATTRS_REQUEST_POINTERS
+ * SLAPI_VIRTUALATTRS_LIST_OPERATIONAL_ATTRS
+ * buffer_flags: bit mask to be used as input flags for
+ * slapi_values_free()
+ * SLAPI_VIRTUALATTRS_RETURNED_POINTERS
+ * SLAPI_VIRTUALATTRS_RETURNED_COPIES
+ * SLAPI_VIRTUALATTRS_REALATTRS_ONLY
+ * item_count: number of subtypes matched
+ * otherwise:
+ * SLAPI_VIRTUALATTRS_LOOP_DETECTED (failed to eval a vattr)
+ * SLAPI_VIRTUALATTRS_NOT_FOUND (type not recognised by any vattr
+ * sp && not a real attr in entry )
+ * ENOMEM (memory error)
+ *
+ * Note:
+ * . modifes the virtual cache in the entry.
+ * . for cached vattrs you always get a copy, so it will need to be
+ * freed via slapi_values_free().
+ *
+ */
+
+int slapi_vattr_values_get_sp_ex(vattr_context *c,
+ /* Entry we're interested in */ Slapi_Entry *e,
+ /* attr type name */ char *type,
+ /* pointer to result set */ Slapi_ValueSet*** results,
+ int **type_name_disposition,
+ char*** actual_type_name, int flags,
+ int *buffer_flags, int *item_count)
+{
+ return slapi_vattr_namespace_values_get_sp(
+ c, e, NULL, type, results, type_name_disposition,
+ actual_type_name, flags, buffer_flags, item_count
+ );
+}
+
+int slapi_vattr_namespace_values_get_sp(vattr_context *c,
+ /* Entry we're interested in */ Slapi_Entry *e,
+ /* DN denoting backend namespace */ Slapi_DN *namespace_dn,
+ /* attr type name */ char *type,
+ /* pointer to result set */ Slapi_ValueSet*** results,
+ int **type_name_disposition,
+ char*** actual_type_name, int flags,
+ int *buffer_flags, int *item_count)
+{
+
+ int rc = 0;
+ int sp_bit = 0; /* Set if an SP supplied an answer */
+ vattr_sp_handle_list *list = NULL;
+ vattr_get_thang *my_get = NULL;
+ int attr_count = 0;
+
+ rc = vattr_context_grok(&c);
+ if (0 != rc) {
+ /* Print a handy error log message */
+ if(!vattr_context_is_loop_msg_displayed(&c))
+ {
+ LDAPDebug(LDAP_DEBUG_ANY,"Detected virtual attribute loop in get on entry %s, attribute %s\n", slapi_entry_get_dn_const(e), type, 0);
+ vattr_context_set_loop_msg_displayed(&c);
+ }
+ return rc;
+ }
+
+ /* Having done that, we now consult the attribute map to find service providers who are interested */
+ /* Look for attribute in the map */
+ if(!(flags & SLAPI_REALATTRS_ONLY))
+ {
+ /* we use the vattr namespace aware version of this */
+ list = vattr_map_namespace_sp_getlist(namespace_dn, type);
+ if (list) {
+ vattr_sp_handle *current_handle = NULL;
+ void *hint = NULL;
+ Slapi_Attr* cache_attr = 0;
+ char *vattr_type=NULL;
+ /* first lets consult the cache to save work */
+ int cache_status;
+
+ cache_status =
+ slapi_entry_vattrcache_find_values_and_type_ex(e, type,
+ results,
+ actual_type_name);
+ switch(cache_status)
+ {
+ case SLAPI_ENTRY_VATTR_RESOLVED_EXISTS: /* cached vattr */
+ {
+ sp_bit = 1;
+
+ /* Complete analysis of type matching */
+ *type_name_disposition =
+ (int *)slapi_ch_malloc(sizeof(*type_name_disposition));
+ if ( 0 == slapi_attr_type_cmp( type , **actual_type_name, SLAPI_TYPE_CMP_EXACT) )
+ {
+ **type_name_disposition =
+ SLAPI_VIRTUALATTRS_TYPE_NAME_MATCHED_EXACTLY_OR_ALIAS;
+ } else {
+ **type_name_disposition =
+ SLAPI_VIRTUALATTRS_TYPE_NAME_MATCHED_SUBTYPE;
+ }
+
+ *item_count = 1;
+
+ break;
+ }
+
+ case SLAPI_ENTRY_VATTR_RESOLVED_ABSENT: /* does not exist */
+ break; /* look in entry */
+
+ case SLAPI_ENTRY_VATTR_NOT_RESOLVED: /* not resolved */
+ default: /* any other result, resolve */
+ {
+ /* bit cacky, but need to make a null terminated lists for now
+ * for the (unimplemented and so fake) batch attribute request
+ */
+ char **type_list = (char**)slapi_ch_calloc(2,sizeof(char*));
+ void **hint_list = (void**)slapi_ch_calloc(2,sizeof(void*));
+
+ type_list[0] = type;
+
+ for (current_handle = vattr_map_sp_first(list,&hint); current_handle; current_handle = vattr_map_sp_next(current_handle,&hint)) {
+ /* call this SP */
+
+ hint_list[0] = hint;
+
+ rc = vattr_call_sp_get_batch_values(current_handle,c,e,my_get,
+ type_list,results,type_name_disposition,
+ actual_type_name, flags,buffer_flags, hint_list);
+ if (0 == rc) {
+ sp_bit = 1; /* this sp provided an answer, we are done */
+
+ /* count the items in the (null terminated) result list */
+ *item_count = 0;
+
+ while((*results)[*item_count])
+ {
+ (*item_count)++;
+ }
+
+ break;
+ }
+ }
+
+ slapi_ch_free((void**)&type_list);
+ slapi_ch_free((void**)&hint_list);
+
+ if(!sp_bit)
+ {
+ /* we have failed and must now examine the entry itself
+ *
+ * But first lets cache the no result
+ * dups the type (if necessary).
+ */
+ slapi_entry_vattrcache_merge_sv(e, type, NULL );
+
+ }
+ else
+ {
+ /*
+ * we need to cache the virtual attribute
+ * dups the type (if necessary) and results.
+ */
+ /*
+ * this (and above) will of course need to get updated
+ * when we do real batched attributes
+ */
+ slapi_entry_vattrcache_merge_sv(e, **actual_type_name,
+ **results );
+ }
+ }
+ }
+ }
+ }
+
+ /* If no SP supplied the answer, take it from the entry */
+ if (!sp_bit && !(flags & SLAPI_VIRTUALATTRS_ONLY))
+ {
+ int counter;
+
+ /* First grok the entry - allocates memory for list */
+ attr_count = vattr_helper_get_entry_conts_ex(e,type, &my_get,
+ (0 != (flags & SLAPI_VIRTUALATTRS_SUPPRESS_SUBTYPES)));
+ *item_count = attr_count;
+ rc = 0; /* reset return code (cause an sp must have failed) */
+
+ if(attr_count > 0)
+ {
+
+ *results = (Slapi_ValueSet**)slapi_ch_calloc(1, sizeof(*results) * attr_count);
+ *type_name_disposition = (int *)slapi_ch_malloc(sizeof(*type_name_disposition) * attr_count);
+ *actual_type_name = (char**)slapi_ch_malloc(sizeof(*actual_type_name) * attr_count);
+
+ /* For attributes which are in the entry, we just need to get to the Slapi_Attr structure and yank out the slapi_value_set
+ structure. We either return a pointer directly to it, or we copy it, depending upon whether the caller asked us to try to
+ avoid copying.
+ */
+ for(counter = 0; counter < attr_count; counter++)
+ {
+ (*type_name_disposition)[counter] = my_get[counter].get_name_disposition;
+
+ if (my_get[counter].get_present) {
+ if (flags & SLAPI_VIRTUALATTRS_REQUEST_POINTERS) {
+ /* pointers will do, good */
+ (*results)[counter] = my_get[counter].get_present_values;
+ (*actual_type_name)[counter] = my_get[counter].get_type_name;
+ } else {
+ /* need to copy the values */
+ (*results)[counter] = valueset_dup(my_get[counter].get_present_values);
+ if (NULL == (*results)[counter]) {
+ rc = ENOMEM;
+ } else {
+ (*actual_type_name)[counter] = slapi_ch_strdup(my_get[counter].get_type_name);
+ if (NULL == (*actual_type_name)[counter]) {
+ rc = ENOMEM;
+ }
+ }
+ }
+ if (flags & SLAPI_VIRTUALATTRS_REQUEST_POINTERS) {
+ *buffer_flags = SLAPI_VIRTUALATTRS_RETURNED_POINTERS;
+ } else {
+ *buffer_flags = SLAPI_VIRTUALATTRS_RETURNED_COPIES;
+ }
+ } else {
+ rc = SLAPI_VIRTUALATTRS_NOT_FOUND;
+ }
+ }
+
+ slapi_ch_free((void **)&my_get);
+ }
+ }
+ vattr_context_ungrok(&c);
+ return rc;
+}
+
+/* Do we need a call to free the results from the above ? */
+
+void slapi_vattr_values_free(Slapi_ValueSet **value, char** actual_type_name,
+ int flags)
+{
+ /* Check whether we need to free the strings */
+ if (flags & SLAPI_VIRTUALATTRS_RETURNED_POINTERS) {
+ /* We don't need to free the values */
+ } else {
+ /* Free the valueset */
+ if (*value) {
+ slapi_valueset_free(*value);
+ }
+ if (*actual_type_name) {
+ slapi_ch_free((void **)actual_type_name);
+ }
+ }
+ *actual_type_name = NULL;
+ *value = NULL;
+}
+
+/* Function like the one above but doing a compare operation */
+/* Same return codes as above. Compare result value returned in "result": 0 if compare true, 1 if compare false */
+
+int slapi_vattr_value_compare(Slapi_Entry *e, char *type, Slapi_Value *test_this, int *result, int flags)
+{
+ return slapi_vattr_namespace_value_compare_sp(NULL,e,NULL,type,test_this,result,flags);
+}
+
+/* namespace version of above */
+int slapi_vattr_namespace_value_compare(Slapi_Entry *e, Slapi_DN *namespace_dn, const char *type, Slapi_Value *test_this, int *result, int flags)
+{
+ return slapi_vattr_namespace_value_compare_sp(NULL,e,namespace_dn,type,test_this,result,flags);
+}
+
+int slapi_vattr_value_compare_sp(vattr_context *c,/* Entry we're interested in */ Slapi_Entry *e,/* attr type name */ char *type, Slapi_Value *test_this,/* pointer to result */ int *result, int flags)
+{
+ return slapi_vattr_namespace_value_compare_sp(c,e,NULL,type,test_this,result,flags);
+}
+
+int slapi_vattr_namespace_value_compare_sp(vattr_context *c,/* Entry we're interested in */ Slapi_Entry *e, /* backend namespace dn*/Slapi_DN *namespace_dn, /* attr type name */ const char *type, Slapi_Value *test_this,/* pointer to result */ int *result, int flags)
+{
+ int rc = 0;
+
+ int sp_bit = 0; /* Set if an SP supplied an answer */
+ vattr_sp_handle_list *list = NULL;
+
+ vattr_get_thang *my_get = 0;
+
+ *result = 0; /* return "compare false" by default */
+
+ rc = vattr_context_grok(&c);
+ if (0 != rc) {
+ /* Print a handy error log message */
+ LDAPDebug(LDAP_DEBUG_ANY,"Detected virtual attribute loop in compare on entry %s, attribute %s\n", slapi_entry_get_dn_const(e), type, 0);
+ return rc;
+ }
+
+ /* Having done that, we now consult the attribute map to find service providers who are interested */
+ /* Look for attribute in the map */
+ list = vattr_map_namespace_sp_getlist(namespace_dn, type);
+ if (list) {
+ vattr_sp_handle *current_handle = NULL;
+ void *hint = NULL;
+ for (current_handle = vattr_map_sp_first(list,&hint); current_handle; current_handle = vattr_map_sp_next(list,&hint)) {
+ /* call this SP */
+ rc = vattr_call_sp_compare_value(current_handle,c,e,my_get,type,test_this,result,flags, hint);
+ if (0 == rc) {
+ sp_bit = 1;
+ break;
+ }
+ }
+ }
+ /* If no SP supplied the answer, take it from the entry */
+ if (!sp_bit) {
+ /* Grok the entry, and remember what we saw. This call does no more than walk down the entry attribute list, do some string compares and copy pointers. */
+ int attr_count = vattr_helper_get_entry_conts_ex(e,type, &my_get,
+ (0 != (flags & SLAPI_VIRTUALATTRS_SUPPRESS_SUBTYPES)));
+
+ if (my_get && my_get->get_present) {
+ int i;
+ Slapi_Value *Dummy_value = NULL;
+
+ /* Put the required stuff in the fake attr */
+
+ for(i=0;i<attr_count;i++)
+ {
+ Dummy_value = slapi_valueset_find( my_get[i].get_attr, my_get[i].get_present_values, test_this );
+
+ if (Dummy_value) {
+ *result = 1; /* return "compare true" */
+
+ break;
+ }
+ }
+ } else {
+ rc = SLAPI_VIRTUALATTRS_NOT_FOUND;
+ }
+ }
+ vattr_context_ungrok(&c);
+ slapi_ch_free((void **)&my_get);
+ return rc;
+}
+
+/* Function to obtain the list of attribute types which are available on this entry */
+
+/*
+ Need to call service providers here:
+ We know only the entry's DN and so we could restrict our choice of SPs based on that.
+ However, we don't yet implement such fancyness.
+ For now, we just crawl through all the SPs, asking them in turn to contribute.
+ This is pretty inefficient because we will trawl the list for each wildcard attribute type search operation.
+ Service providers should therefore handle the call as fast as they can.
+ */
+
+struct _vattr_type_list_context {
+ unsigned int vattr_context_loop_count;
+ vattr_type_thang *types;
+ int realattrs_only; /* TRUE implies list contains only real attrs */
+ int list_is_copies;
+ int flags;
+ size_t list_length;
+ size_t block_length;
+};
+
+/* Helper function which converts a type list from pointers to copies */
+static int vattr_convert_type_list(vattr_type_list_context *thang)
+{
+ vattr_type_thang *old_list = NULL;
+ vattr_type_thang *new_list = NULL;
+ size_t index = 0;
+
+ old_list = thang->types;
+ /* Make a new list */
+ new_list = (vattr_type_thang*)slapi_ch_calloc(thang->block_length, sizeof(vattr_type_thang));
+ if (NULL == new_list) {
+ return ENOMEM;
+ }
+ /* Walk the list strdup'ing the type name and copying the rest of the structure contents */
+ for (index = 0; index < thang->list_length; index++) {
+ new_list[index].type_flags = old_list[index].type_flags;
+ new_list[index].type_name = slapi_ch_strdup(old_list[index].type_name);
+ /*
+ * list_is_copies does not affect type_values as it
+ * is always a pointer never a copy.
+ */
+ new_list[index].type_values = old_list[index].type_values;
+ }
+ /* Mark it a copy list */
+ thang->list_is_copies = 1;
+ /* Free the original list */
+ slapi_ch_free((void **)&(thang->types));
+ /* swap lists */
+ thang->types = new_list;
+
+ return 0;
+}
+
+/*
+ * Returns all the attribute types from e, both real and virtual.
+ *
+ * Any of the attributes found in the entry to start with, for
+ * whom no vattr SP volunteered, will also have a pointer to the
+ * original valueset in the entry--this fact can be used by
+ * calling slapi_vattr_values_type_thang_get(), which will
+ * take the values present in the vattr_type_thang list
+ * rather than calling slapi_vattr_values_get() to retrieve the value.
+*/
+
+#define TYPE_LIST_EXTRA_SPACE 5 /* Number of extra slots we allow for SP's to insert types */
+
+int slapi_vattr_list_attrs(/* Entry we're interested in */ Slapi_Entry *e,
+ /* pointer to receive the list */ vattr_type_thang **types,
+ int flags, int *buffer_flags)
+{
+ int rc = 0;
+ size_t attr_count = 0;
+ vattr_type_thang *result_array = NULL ;
+ Slapi_Attr *current_attr = NULL;
+ size_t i = 0;
+ int list_is_copies = 0;
+ vattr_sp_handle_list *list = NULL;
+ size_t list_length = 0;
+ size_t block_length = 0;
+ vattr_type_list_context type_context = {0};
+
+ block_length = 1 + TYPE_LIST_EXTRA_SPACE;
+
+ if(!(flags & SLAPI_VIRTUALATTRS_ONLY))
+ {
+ /* First find what's in the entry itself*/
+ /* Count the attributes */
+ for (current_attr = e->e_attrs; current_attr != NULL; current_attr = current_attr->a_next, attr_count++) ;
+ block_length += attr_count;
+ /* Allocate the pointer array */
+ result_array = (vattr_type_thang*)slapi_ch_calloc(block_length,sizeof(vattr_type_thang));
+ if (NULL == result_array) {
+ return ENOMEM;
+ }
+ list_length = attr_count;
+
+ /* Now copy the pointers into the array */
+ i = 0;
+ for (current_attr = e->e_attrs; current_attr != NULL; current_attr = current_attr->a_next) {
+ char *mypointer = NULL;
+ if (flags & SLAPI_VIRTUALATTRS_REQUEST_POINTERS) {
+ mypointer = current_attr->a_type;
+ result_array[i].type_values = &(current_attr->a_present_values);
+ } else {
+ mypointer = slapi_ch_strdup(current_attr->a_type);
+ list_is_copies = 1;
+ }
+ result_array[i].type_flags = current_attr->a_flags;
+ result_array[i++].type_name = mypointer;
+ }
+ PR_ASSERT(i == attr_count);
+ }
+
+ /* if we didnt send real attrs we need to allocate result array */
+ if(NULL == result_array)
+ {
+ result_array = (vattr_type_thang*)slapi_ch_calloc(block_length,sizeof(vattr_type_thang));
+ if (NULL == result_array) {
+ return ENOMEM;
+ }
+ }
+
+ /* Then ask the service providers for their input */
+ type_context.flags = flags;
+ type_context.realattrs_only = 1; /* until we know otherwise */
+ type_context.list_is_copies = list_is_copies;
+ type_context.types = result_array;
+ type_context.list_length = list_length;
+ type_context.block_length = block_length;
+
+ /*
+ * At this point type_context.types is a list of all
+ * the attributetypes (copies or pointers) and values (copies, if present)
+ * found in the entry.
+ */
+
+ if(!(flags & SLAPI_REALATTRS_ONLY))
+ {
+ list = vattr_map_sp_get_complete_list();
+ if (list) {
+ vattr_sp_handle *current_handle = NULL;
+
+ for (current_handle = vattr_list_sp_first(list); current_handle; current_handle = vattr_list_sp_next((vattr_sp_handle_list *)current_handle)) {
+ /* call this SP */
+ rc = vattr_call_sp_get_types(current_handle,e,&type_context,
+ flags);
+ if (0 != rc) {
+ /* DBDB do what on error condition ? */
+ }
+ }
+ /* assert enough space for the null terminator */
+ PR_ASSERT( type_context.list_length < type_context.block_length);
+ i = type_context.list_length;
+ }
+ }
+
+ /*
+ * Now type_context.types is a list of all the types in this entry--
+ * real and virtual. For real ones, the values field is filled in.
+ * For virtual ones (including virtual ones which will overwrite a real
+ * value) the values field is null--this fact may used
+ * subsequently by callers of slapi_vattr_list_attrs()
+ * by calling slapi_vattr_values_type_thang_get() which
+ * will only calculate the values for attributetypes with
+ * non-null values.
+ */
+
+ flags = type_context.flags;
+ list_is_copies = type_context.list_is_copies;
+ result_array = type_context.types;
+ list_length = type_context.list_length;
+ block_length = type_context.block_length;
+
+ if (list_is_copies) {
+ *buffer_flags = SLAPI_VIRTUALATTRS_RETURNED_COPIES;
+ } else {
+ *buffer_flags = SLAPI_VIRTUALATTRS_RETURNED_POINTERS;
+ }
+
+ /* Let the caller know if the list contains only real attrs */
+ if (type_context.realattrs_only) {
+ *buffer_flags |= SLAPI_VIRTUALATTRS_REALATTRS_ONLY;
+ }
+
+ result_array[i].type_name = NULL;
+
+ if (types && list_length) {
+ *types = result_array;
+ }
+ else
+ *types = 0;
+
+ return rc;
+}
+
+static int vattr_add_thang_to_list(vattr_type_list_context *c, vattr_type_thang *thang)
+{
+ vattr_type_thang *new_list = NULL;
+
+ /* Will it fit in the current block ? */
+ if (c->list_length == c->block_length - 1) {
+ c->block_length *= 2;
+
+ new_list = (vattr_type_thang*)slapi_ch_realloc((char *)c->types,
+ c->block_length * (int)sizeof(vattr_type_thang));
+ if (NULL == new_list) {
+ return ENOMEM;
+ }
+
+ c->types = new_list;
+ }
+ /* Add to the end of the list */
+ c->types[c->list_length ] = *thang;
+ c->list_length += 1;
+ return 0;
+}
+
+/* Called by SP's during their get_types call, to have the server add a
+ * type to the type list.
+ *
+ * c: the vattr_type_list_context passed from the original caller of
+ * slapi_vattr_list_attrs()
+ * thang: the new type to be added to the type list.
+ * flags: tells the routine whether to copy the thang or not.
+ * SLAPI_VIRTUALATTRS_REQUEST_POINTERS--no need to copy it.
+ * !(SLAPI_VIRTUALATTRS_REQUEST_POINTERS)--need to copy it.
+ *
+ * Checks to see if the requested type is already in the list,
+ * and if it is, if the value of the attribute is
+ * non-null, it resets the value to null. This means that when used
+ * subsequently, the type list indicates whether, for a given attribute type,
+ * the SP's need to be called to retrieve the value.
+ *
+*/
+int slapi_vattrspi_add_type(vattr_type_list_context *c,
+ vattr_type_thang *thang, int flags)
+{
+ int rc = 0;
+ unsigned int index = 0;
+ vattr_type_thang thang_to_add = {0};
+
+ int found_it = 0;
+
+ PR_ASSERT(c);
+
+ /* We are no longer sure that the list contains only real attrs */
+ c->realattrs_only = 0;
+
+ /* Check to see if the type is in the list already */
+ for (index = 0; index < c->list_length; index++) {
+ if (0 == slapi_UTF8CASECMP(c->types[index].type_name,thang->type_name)) {
+ found_it = 1;
+ break;
+ }
+ }
+ /*
+ * If found and there are values specified in the vattr_type_thang, then
+ * that means it's a real attribute--because SP's do not add values
+ * when called via slapi_vattr_list_attrs()/vattr_call_sp_get_types().
+ * However, an SP is willing to claim responsibility for this attribute
+ * type, so to ensure that that virtual value will get calculated, set the
+ * original real value to NULL here.
+ * The guiding rule here is "if an SP is prepared
+ * to provide a value for an attribute type, then that is the one
+ * that must be returned to the user". Note, this does not prevent
+ * the SP implementing a "merge real with virtual policy", if they
+ * wish.
+ */
+
+ if (found_it) {
+
+ if ( c->types[index].type_values != NULL ) {
+ /*
+ * Any values in this list are always pointers.
+ * See slapi_vattr_list_types() and vattr_convert_type_list()
+ */
+ c->types[index].type_values = NULL;
+ }
+
+ return 0;
+ }
+ /*
+ * If it's not in the list then we need to add it.
+ * If the list is already copies, then we need to make a copy of what the
+ * SP passed us.
+ * If the SP indicated that the type name it passed us is volatile,
+ * !(flags & SLAPI_VIRTUALATTRS_REQUEST_POINTERS)
+ * then we need to copy it anyway.
+ */
+
+ /* rbyrneXXX optimisation for COS: if each type_thang could have a
+ * free_flag
+ * then we would not have to convert all the real attrs to copies
+ * just because an SP returned a copy (note: only COS rrequires copies;
+ * roles returns a pointer to a static string "nsRole").
+ */
+ if (!c->list_is_copies &&
+ (0 == (flags & SLAPI_VIRTUALATTRS_REQUEST_POINTERS))) {
+ /* Means we need to turn the existing list into a list of copies */
+ vattr_convert_type_list(c);
+ }
+ if (c->list_is_copies) {
+ thang_to_add.type_flags = thang->type_flags;
+ thang_to_add.type_name = slapi_ch_strdup(thang->type_name);
+ } else {
+ thang_to_add = *thang;
+ }
+ /* Lastly, we add to the list, using a function which hides the complexity of extending the list if it fills up */
+ vattr_add_thang_to_list(c, &thang_to_add);
+
+ return rc;
+}
+
+/* Function to free the list returned in the function above */
+/* Written, reviewed, debug stepped */
+
+void slapi_vattr_attrs_free(vattr_type_thang **types, int flags)
+{
+ if (NULL == *types) {
+ return;
+ }
+ /* Check whether we need to free the strings */
+ if (flags & SLAPI_VIRTUALATTRS_RETURNED_POINTERS) {
+ /* We don't need to free the values */
+ } else {
+ char *attr_name_to_free = NULL;
+ size_t i = 0;
+ /* Walk down the set of values, fr eeing each one in turn */
+ for (; (attr_name_to_free = (*types)[i].type_name) != NULL; i++) {
+ slapi_ch_free((void **)&attr_name_to_free);
+ }
+ }
+ /* We always need to free the pointer block */
+ slapi_ch_free((void **)types);
+}
+
+char *vattr_typethang_get_name(vattr_type_thang *t)
+{
+ return t->type_name;
+}
+
+unsigned long vattr_typethang_get_flags(vattr_type_thang *t)
+{
+ return t->type_flags;
+}
+
+vattr_type_thang *vattr_typethang_next(vattr_type_thang *t)
+{
+ t++;
+ if (t->type_name) {
+ return t;
+ } else {
+ return NULL;
+ }
+}
+
+vattr_type_thang *vattr_typethang_first(vattr_type_thang *t)
+{
+ return t;
+}
+
+vattr_get_thang *slapi_vattr_getthang_first(vattr_get_thang *t)
+{
+ return t;
+}
+
+vattr_get_thang *slapi_vattr_getthang_next(vattr_get_thang *t)
+{
+ t++;
+ if (t->get_present) {
+ return t;
+ } else {
+ return NULL;
+ }
+}
+
+
+
+
+/* End of the public interface functions */
+
+/* Everything below here is the SPI interface, only callable by vattr service providers.
+ Currently this interface is not public. Interface definitions are in vattr_spi.h
+ */
+
+/* Structures used in the SPI interface */
+
+/* Service provider object */
+
+
+struct _vattr_sp {
+ /* vtbl */
+ vattr_get_fn_type sp_get_fn;
+ vattr_get_ex_fn_type sp_get_ex_fn;
+ vattr_compare_fn_type sp_compare_fn;
+ vattr_types_fn_type sp_types_fn;
+ void *sp_data; /* Pointer for use by the Service Provider */
+};
+typedef struct _vattr_sp vattr_sp;
+
+/* Service provider handle */
+struct _vattr_sp_handle {
+ vattr_sp *sp;
+ struct _vattr_sp_handle *next; /* So we can link them together in the map */
+ void *hint; /* Hint to the SP */
+};
+
+/* Calls made by Service Providers */
+/* Call to register as a service provider */
+/* This function needs to do the following:
+ o Let the provider say "hey, I'm here". It should probably get some handle back which it can use in future calls.
+ o Say whether it wants to be called to resolve every single query, or whether it will say in advance what attrs it services.
+ */
+
+static vattr_sp_handle *vattr_sp_list = NULL;
+
+int slapi_vattrspi_register(vattr_sp_handle **h, vattr_get_fn_type get_fn, vattr_compare_fn_type compare_fn, vattr_types_fn_type types_fn)
+{
+ return slapi_vattrspi_register_internal(h, get_fn, 0, compare_fn, types_fn, 0);
+}
+
+int slapi_vattrspi_register_ex(vattr_sp_handle **h, vattr_get_ex_fn_type get_fn, vattr_compare_fn_type compare_fn, vattr_types_fn_type types_fn, void *options)
+{
+ return slapi_vattrspi_register_internal(h, 0, get_fn, compare_fn, types_fn, options);
+}
+
+/* options not used yet - for future expansion */
+int slapi_vattrspi_register_internal(vattr_sp_handle **h, vattr_get_fn_type get_fn, vattr_get_ex_fn_type get_ex_fn, vattr_compare_fn_type compare_fn, vattr_types_fn_type types_fn, void *options)
+{
+ vattr_sp_handle *return_to_caller = NULL;
+ vattr_sp_handle *list_handle = NULL;
+ vattr_sp *new_sp = NULL;
+ /* Make a service provider handle */
+ new_sp = (vattr_sp*)slapi_ch_calloc(1,sizeof(vattr_sp));
+ if (NULL == new_sp) {
+ slapd_nasty(sourcefile,7,0);
+ return ENOMEM;
+ }
+ return_to_caller = (vattr_sp_handle*)slapi_ch_calloc(1,sizeof(vattr_sp_handle));
+ if (NULL == return_to_caller) {
+ slapd_nasty(sourcefile,8,0);
+ return ENOMEM;
+ }
+ new_sp->sp_get_fn = get_fn;
+ new_sp->sp_get_ex_fn = get_ex_fn;
+ new_sp->sp_compare_fn = compare_fn;
+ new_sp->sp_types_fn = types_fn;
+ return_to_caller->sp = new_sp;
+ /* Add to the service provider list */
+ /* Make a handle for the list */
+ list_handle = (vattr_sp_handle*)slapi_ch_calloc(1, sizeof (vattr_sp_handle));
+ if (NULL == list_handle) {
+ return ENOMEM;
+ }
+ *list_handle = *return_to_caller;
+ list_handle->next = vattr_sp_list;
+ vattr_sp_list = list_handle;
+ /* Return the handle to the caller */
+ *h = return_to_caller;
+ return 0;
+}
+
+vattr_sp_handle_list *vattr_map_sp_get_complete_list()
+{
+ return vattr_sp_list;
+}
+
+int slapi_vattrspi_regattr(vattr_sp_handle *h,char *type_name_to_register, char* DN , void *hint)
+{
+ int ret = 0;
+ char *type_to_add;
+ int free_type_to_add = 0;
+
+ /* Supplying a DN means that the plugin requires to be called
+ * only when the considering attributes in relevant entries - almost
+ *
+ * Actually the smallest namespace is the backend.
+ *
+ * atttributes that are tied to backends have the format DN::attr
+ * this essentially makes the attribute have split namespaces
+ * that are treated as separate attributes in the vattr code
+ */
+ if(DN)
+ {
+ /* as a coutesy we accept any old DN and will convert
+ * to a namespace DN, this helps to hide details
+ * (that we might decide to change) anyway
+ */
+ Slapi_DN original_dn;
+ Slapi_Backend *be;
+ Slapi_DN *namespace_dn;
+
+ slapi_sdn_init(&original_dn);
+ slapi_sdn_set_dn_byref(&original_dn,DN);
+ be = slapi_be_select( &original_dn );
+ namespace_dn = (Slapi_DN*)slapi_be_getsuffix(be, 0);
+
+ if(namespace_dn && be != defbackend_get_backend()) /* just in case someone thinks "" is a good namespace */
+ {
+ type_to_add = (char*)PR_smprintf("%s::%s",
+ (char*)slapi_sdn_get_dn(namespace_dn),
+ type_name_to_register);
+
+ if(!type_to_add)
+ {
+ ret = -1;
+ }
+
+ free_type_to_add = 1;
+ }
+ else
+ {
+ type_to_add = type_name_to_register;
+ }
+
+ slapi_sdn_done(&original_dn);
+ }
+ else
+ {
+ type_to_add = type_name_to_register;
+ }
+
+ ret = vattr_map_sp_insert(type_to_add,h,hint);
+
+ if(free_type_to_add)
+ {
+ PR_smprintf_free(type_to_add);
+ }
+
+ return ret;
+}
+
+
+/* Call to advertise or refute that you generate the value for some attribute, and to signal that the definition for this attribute has changed */
+/* This call needs to do the following:
+ o Let the SP say "you know, I will generate the value of attribute "foo", within subtree "bar". (we might ignore the subtree for now to keep things simple
+ o Same as above, but say "you know, I'm not doing that any more". (we'll do that with a addordel flag).
+ o Say, "you know that definition I said I did, well it's changed".
+*/
+
+/* Functions to handle the context stucture */
+
+int slapi_vattr_context_create(vattr_context **c)
+{
+ return 0;
+}
+
+void slapi_vattr_context_destroy(vattr_context *c)
+{
+}
+
+int vattr_call_sp_get_value(vattr_sp_handle *handle, vattr_context *c, Slapi_Entry *e, vattr_get_thang *my_get, char *type, Slapi_ValueSet** results,int *type_name_disposition, char** actual_type_name, int flags, int *buffer_flags, void* hint)
+{
+ int ret = -1;
+
+ if(handle->sp->sp_get_fn)
+ {
+ ret = ((handle->sp->sp_get_fn)(handle,c,e,type,results,type_name_disposition,actual_type_name,flags,buffer_flags, hint));
+ }
+
+ return ret;
+}
+
+int vattr_call_sp_get_batch_values(vattr_sp_handle *handle, vattr_context *c, Slapi_Entry *e, vattr_get_thang *my_get, char **type, Slapi_ValueSet*** results,int **type_name_disposition, char*** actual_type_name, int flags, int *buffer_flags, void** hint)
+{
+ int ret = 0;
+
+ if(handle->sp->sp_get_ex_fn)
+ {
+ ret = ((handle->sp->sp_get_ex_fn)(handle,c,e,type,results,type_name_disposition,actual_type_name,flags,buffer_flags, hint));
+ }
+ else
+ {
+ /* make our args look like the simple non-batched case */
+ *results = (Slapi_ValueSet**)slapi_ch_calloc(2, sizeof(*results)); /* 2 for null terminated list */
+ *type_name_disposition = (int *)slapi_ch_calloc(2, sizeof(*type_name_disposition));
+ *actual_type_name = (char**)slapi_ch_calloc(2, sizeof(*actual_type_name));
+
+ ret =((handle->sp->sp_get_fn)(handle,c,e,*type,*results,*type_name_disposition,*actual_type_name,flags,buffer_flags, hint));
+ if(ret)
+ {
+ slapi_ch_free((void**)results );
+ slapi_ch_free((void**)type_name_disposition );
+ slapi_ch_free((void**)actual_type_name );
+ }
+ }
+
+ return ret;
+}
+
+int vattr_call_sp_compare_value(vattr_sp_handle *handle, vattr_context *c, Slapi_Entry *e, vattr_get_thang *my_get, const char *type, Slapi_Value* test_this,int *result, int flags, void* hint)
+{
+ return ((handle->sp->sp_compare_fn)(handle,c,e,(char*)type,test_this,result,flags, hint));
+}
+
+int vattr_call_sp_get_types(vattr_sp_handle *handle,Slapi_Entry *e,vattr_type_list_context *type_context,int flags)
+{
+ return ((handle->sp->sp_types_fn)(handle,e,type_context,flags));
+}
+
+/* Service provider entry point prototypes */
+
+/* Call which allows a SP to say what attributes can be used to define a given attribute, used for loop detection */
+/* Not implementing this yet */
+/* typedef int (*vattrspi_explain_definition) (); */
+
+/* Function called to indicate to the SP that the game is over, we free the handle, not the SP */
+typedef int (*vattrspi_deregister) (vattr_sp_handle *h);
+
+/* End of the SPI functions */
+
+/* Implementation functions only after here */
+
+/* The attribute map */
+
+/* We need a structure which can map attribute type names to SP's which might tell us
+something useful. We need a function which, given an attr type name, can return
+a set of SP's to ask about said attr. SP's which don't tell us which attrs they are
+handling always get asked. The first SP which tells us the answer wins, we don't
+attempt to arbitrate between them. One might imaging doing this stuff in a unified
+way with other stuff pertaining to types (schema, syntax). Someday. For now this
+is a separate thing in the insterests of stability.
+*/
+
+/* Virtual Attribute map implementation */
+
+/* The virtual attribute type map is implemented as a hash table.
+ The single key is the attribute type base name (not an alias)
+ (what about subtypes??). The hash table takes care of translating
+ this key to a list of probable service providers for the type.
+
+ Other than the obvious lookup function, we need functions to
+ allow entries to be added and removed from the map.
+
+ Because the map is likely to be consulted at least once for every search
+ operation performed by the server, it's important that it is free from
+ performance deficiencies such as lock contention (is the NSPR hash table
+ implementation even thread safe ?
+
+ */
+
+#define VARRT_MAP_HASHTABLE_SIZE 10
+
+/* Attribute map oject */
+/* Needs to contain: a linked list of pointers to provider handles handles,
+ The type name,
+ other stuff ?
+
+ Access to the entire map will be controlled via a single RW lock.
+ */
+
+struct _objAttrValue
+{
+ struct _objAttrValue *pNext;
+ Slapi_Value *val;
+};
+typedef struct _objAttrValue objAttrValue;
+
+struct _vattr_map_entry {
+ char * /* currect ? */ type_name;
+ vattr_sp_handle *sp_list;
+ objAttrValue *objectclasses; /* objectclasses for this type to check schema with*/
+};
+typedef struct _vattr_map_entry vattr_map_entry;
+
+
+vattr_map_entry test_entry = {NULL};
+
+struct _vattr_map {
+ PRRWLock *lock;
+ PLHashTable *hashtable; /* Hash table */
+};
+typedef struct _vattr_map vattr_map;
+
+static vattr_map *the_map = NULL;
+
+static PRIntn vattr_hash_compare_keys(const void *v1, const void *v2)
+{
+ /* Should this be subtype aware, etc ? */
+ return ( (0 == strcasecmp(v1,v2)) ? 1 : 0) ;
+}
+
+static PRIntn vattr_hash_compare_values(const void *v1, const void *v2)
+{
+ return( ((char *)v1 == (char *)v2 ) ? 1 : 0);
+}
+
+static PLHashNumber vattr_hash_fn(const void *type_name)
+{
+ /* need a hash function here */
+ /* Sum the bytes for now */
+ PLHashNumber result = 0;
+ char * current_position = NULL;
+ char current_char = 0;
+ for (current_position = (char*) type_name; *current_position; current_position++) {
+ current_char = tolower(*current_position);
+ result += current_char;
+ }
+ return result;
+}
+
+static int vattr_map_create()
+{
+ the_map = (vattr_map*)slapi_ch_calloc(1, sizeof(vattr_map));
+ if (NULL == the_map) {
+ slapd_nasty(sourcefile,1,0);
+ return ENOMEM;
+ }
+
+ the_map->hashtable = PL_NewHashTable(VARRT_MAP_HASHTABLE_SIZE,
+ vattr_hash_fn, vattr_hash_compare_keys,
+ vattr_hash_compare_values, NULL, NULL);
+
+ if (NULL == the_map->hashtable) {
+ slapd_nasty(sourcefile,2,0);
+ return ENOMEM;
+ }
+
+ the_map->lock = PR_NewRWLock(0,SOURCEFILE "1");
+ if (NULL == the_map) {
+ slapd_nasty(sourcefile,3,0);
+ return ENOMEM;
+ }
+ return 0;
+}
+
+static void vattr_map_destroy()
+{
+ if (the_map) {
+ if (the_map->hashtable) {
+ PL_HashTableDestroy(the_map->hashtable);
+ }
+ if (the_map->lock) {
+ PR_DestroyRWLock(the_map->lock);
+ }
+ }
+ slapi_ch_free ((void**)&the_map);
+}
+
+/* Returns 0 if present, entry returned in result. Returns SLAPI_VIRTUALATTRS_NOT_FOUND if not found */
+static int vattr_map_lookup(const char *type_to_find, vattr_map_entry **result)
+{
+ char *basetype = 0;
+ char *tmp = 0;
+ char buf[SLAPD_TYPICAL_ATTRIBUTE_NAME_MAX_LENGTH];
+
+ *result = NULL;
+ PR_ASSERT(the_map);
+
+ /* vattrs need to support subtypes
+ * we insist that one service provider
+ * will support all subtypes for a
+ * given superior, hence we look for
+ * superiors only here
+ */
+
+ tmp = slapi_attr_basetype( type_to_find, buf, sizeof(buf));
+ if(tmp)
+ {
+ basetype = tmp;
+ }
+ else
+ {
+ basetype = buf;
+ }
+
+ /* Get the reader lock */
+ PR_RWLock_Rlock(the_map->lock);
+ *result = (vattr_map_entry*)PL_HashTableLookupConst(the_map->hashtable,
+ (void*)basetype);
+ /* Release ze lock */
+ PR_RWLock_Unlock(the_map->lock);
+
+ if(tmp)
+ {
+ slapi_ch_free_string(&tmp);
+ }
+
+ if (*result) {
+ return 0;
+ } else {
+ return SLAPI_VIRTUALATTRS_NOT_FOUND;
+ }
+}
+
+/* Insert an entry into the attribute map */
+int vattr_map_insert(vattr_map_entry *vae)
+{
+ char *copy_of_type_name = NULL;
+ PR_ASSERT(the_map);
+ copy_of_type_name = slapi_ch_strdup(vae->type_name);
+ if (NULL == copy_of_type_name) {
+ slapd_nasty(sourcefile,6,0);
+ return ENOMEM;
+ }
+ /* Get the writer lock */
+ PR_RWLock_Wlock(the_map->lock);
+ /* Insert the thing */
+ /* It's illegal to call this function if the entry is already there */
+ PR_ASSERT(NULL == PL_HashTableLookupConst(the_map->hashtable,(void*)copy_of_type_name));
+ PL_HashTableAdd(the_map->hashtable,(void*)copy_of_type_name,(void*)vae);
+ /* Unlock and we're done */
+ PR_RWLock_Unlock(the_map->lock);
+ return 0;
+}
+
+/*
+ vattr_delete_attrvals
+ ---------------------
+ deletes a value list
+*/
+void vattr_delete_attrvals(objAttrValue **attrval)
+{
+ objAttrValue *val = *attrval;
+
+ while(val)
+ {
+ objAttrValue *next = val->pNext;
+ slapi_value_free(&val->val);
+ slapi_ch_free((void**)&val);
+ val = next;
+ }
+}
+
+/*
+ vattr_add_attrval
+ -----------------
+ adds a value to an attribute value list
+*/
+int vattr_add_attrval(objAttrValue **attrval, char *val)
+{
+ int ret = 0;
+ objAttrValue *theVal;
+
+ LDAPDebug( LDAP_DEBUG_TRACE, "--> vattr_add_attrval\n",0,0,0);
+
+ /* create the attrvalue */
+ theVal = (objAttrValue*) slapi_ch_malloc(sizeof(objAttrValue));
+ if(theVal)
+ {
+ theVal->val = slapi_value_new_string(val);
+ if(theVal->val)
+ {
+ theVal->pNext = *attrval;
+ *attrval = theVal;
+ }
+ else
+ {
+ slapi_ch_free((void**)&theVal);
+ LDAPDebug( LDAP_DEBUG_ANY, "vattr_add_attrval: failed to allocate memory\n",0,0,0);
+ ret = -1;
+ }
+ }
+ else
+ {
+ LDAPDebug( LDAP_DEBUG_ANY, "vattr_add_attrval: failed to allocate memory\n",0,0,0);
+ ret = -1;
+ }
+
+ LDAPDebug( LDAP_DEBUG_TRACE, "<-- vattr_add_attrval\n",0,0,0);
+ return ret;
+}
+
+
+objAttrValue *vattr_map_entry_build_schema(char *type_name)
+{
+ objAttrValue *ret = 0;
+ struct objclass *oc;
+ int attr_index = 0;
+
+ LDAPDebug( LDAP_DEBUG_TRACE, "--> vattr_map_entry_build_schema\n",0,0,0);
+
+ /* this is the first opportunity to register
+ * with the statechange api, our init function
+ * gets called prior to loading plugins, so it
+ * was not available then
+ */
+ if(!statechange_api)
+ {
+ /* grab statechange api - we never release this */
+ if(!slapi_apib_get_interface(StateChange_v1_0_GUID, &statechange_api))
+ {
+ /* register for schema changes via dn */
+ statechange_register(statechange_api, "vattr", "cn=schema", NULL, NULL, (notify_callback) schema_changed_callback);
+ }
+ }
+
+ if(!config_get_schemacheck())
+ {
+ LDAPDebug( LDAP_DEBUG_TRACE, "<-- vattr_map_entry_build_schema - schema off\n",0,0,0);
+ return 0;
+ }
+
+ oc_lock_read();
+ for ( oc = g_get_global_oc_nolock(); oc != NULL; oc = oc->oc_next )
+ {
+ char **pppAttrs[2];
+ int index;
+ int attrType = 0;
+ int oc_added = 0;
+
+ pppAttrs[0] = oc->oc_required;
+ pppAttrs[1] = oc->oc_allowed;
+
+ /* we need to check both required and allowed attributes */
+ while(!oc_added && attrType < 2)
+ {
+ if(pppAttrs[attrType])
+ {
+ index = 0;
+
+ while(pppAttrs[attrType][index])
+ {
+ if(!PL_strcasecmp(pppAttrs[attrType][index], type_name))
+ {
+ vattr_add_attrval(&ret, oc->oc_name);
+ oc_added = 1;
+ break;
+ }
+ index++;
+ }
+ }
+ attrType++;
+ }
+ }
+ oc_unlock();
+
+ LDAPDebug( LDAP_DEBUG_TRACE, "<-- vattr_map_entry_build_schema\n",0,0,0);
+ return ret;
+}
+
+static PRIntn vattr_map_entry_rebuild_schema(PLHashEntry *he, PRIntn i, void *arg)
+{
+ vattr_map_entry *entry = (vattr_map_entry *)(he->value);
+
+ if(entry->objectclasses)
+ vattr_delete_attrvals(&(entry->objectclasses));
+
+ entry->objectclasses = vattr_map_entry_build_schema(entry->type_name);
+
+ return HT_ENUMERATE_NEXT;
+}
+
+void schema_changed_callback(Slapi_Entry *e, char *dn, int modtype, Slapi_PBlock *pb, void *caller_data)
+{
+ /* Get the writer lock */
+ PR_RWLock_Wlock(the_map->lock);
+
+ /* go through the list */
+ PL_HashTableEnumerateEntries(the_map->hashtable, vattr_map_entry_rebuild_schema, 0);
+
+ /* Unlock and we're done */
+ PR_RWLock_Unlock(the_map->lock);
+}
+
+
+int slapi_vattr_schema_check_type(Slapi_Entry *e, char *type)
+{
+ int ret = 0;
+ vattr_map_entry *map_entry;
+
+ if(config_get_schemacheck())
+ {
+ Slapi_Attr *attr;
+
+ if(0 == slapi_entry_attr_find(e, "objectclass", &attr))
+ {
+ Slapi_ValueSet *vs;
+
+ if(0 == slapi_attr_get_valueset(attr, &vs))
+ {
+ objAttrValue *obj;
+
+ if(0 == vattr_map_lookup(type, &map_entry))
+ {
+ PR_RWLock_Rlock(the_map->lock);
+
+ obj = map_entry->objectclasses;
+
+ while(obj)
+ {
+ if(slapi_valueset_find(attr, vs, obj->val))
+ {
+ /* this entry has an objectclass
+ * that allows or requires the
+ * attribute type
+ */
+ ret = -1;
+ break;
+ }
+
+ obj = obj->pNext;
+ }
+
+ PR_RWLock_Unlock(the_map->lock);
+ }
+
+ slapi_valueset_free(vs);
+ }
+ }
+
+ }
+ else
+ ret = -1;
+
+ return ret;
+}
+
+vattr_map_entry *vattr_map_entry_new(char *type_name, vattr_sp_handle *sph, void* hint)
+{
+ vattr_map_entry *result = NULL;
+ vattr_sp_handle *sp_copy = NULL;
+ sp_copy = (vattr_sp_handle*)slapi_ch_calloc(1, sizeof (vattr_sp_handle));
+ if (!sp_copy) {
+ return NULL;
+ }
+ sp_copy->sp = sph->sp;
+ sp_copy->hint = hint;
+ result = (vattr_map_entry*)slapi_ch_calloc(1, sizeof (vattr_map_entry));
+ if (result) {
+ result->type_name = slapi_ch_strdup(type_name);
+ result->sp_list = sp_copy;
+ }
+
+ /* go get schema */
+ result->objectclasses = vattr_map_entry_build_schema(type_name);
+
+ return result;
+}
+
+/* On top of the map, we need functions to manipulate the SP handles from within */
+
+/* Function to get the SP list from the map, given a type name */
+/* The resulting list is in the map, but is safe to read regardless of concurrent updates */
+vattr_sp_handle_list *vattr_map_sp_getlist(char *type_to_find)
+{
+ int ret = 0;
+ vattr_map_entry *result = NULL;
+ ret = vattr_map_lookup(type_to_find,&result);
+ if (0 == ret) {
+ return (vattr_sp_handle_list*) result->sp_list;
+ } else {
+ return NULL;
+ }
+}
+
+/* same as above, but filters the list based on the supplied backend dn
+ * when we stored these dn based attributes, we concatenated them with
+ * the dn like this dn::attribute, so we need to do two checks for the
+ * attribute, one with, and one without the dn
+ */
+vattr_sp_handle_list *vattr_map_namespace_sp_getlist(Slapi_DN *dn, const char *type_to_find)
+{
+ int ret = 0;
+ vattr_map_entry *result = NULL;
+ vattr_sp_handle_list* return_list = 0;
+
+ ret = vattr_map_lookup(type_to_find,&result);
+ if (0 == ret) {
+ return_list = (vattr_sp_handle_list*) result->sp_list;
+ } else {
+ /* we have allowed the global namespace provider a shot
+ * now it is time to query for split namespace providers
+ */
+ if(dn) {
+ char *split_dn = (char*)slapi_sdn_get_dn(dn);
+ char *split_type_to_find =
+ (char*)PR_smprintf("%s::%s",split_dn, type_to_find);
+
+ if(split_type_to_find)
+ {
+ ret = vattr_map_lookup(split_type_to_find,&result);
+ if (0 == ret) {
+ return_list = (vattr_sp_handle_list*) result->sp_list;
+ }
+
+ PR_smprintf_free(split_type_to_find);
+ }
+ }
+ }
+
+ return return_list;
+}
+
+
+/* Iterator function for the list */
+vattr_sp_handle *vattr_map_sp_next(vattr_sp_handle_list *list, void **hint)
+{
+ vattr_sp_handle *result = NULL;
+ vattr_sp_handle *current = (vattr_sp_handle*) list;
+ PR_ASSERT(list);
+ PR_ASSERT(hint);
+ result = current->next;
+ *hint = current->hint;
+ return result;
+}
+
+/* Iterator function for the list */
+vattr_sp_handle *vattr_map_sp_first(vattr_sp_handle_list *list, void **hint)
+{
+ vattr_sp_handle *result = NULL;
+ PR_ASSERT(list);
+ PR_ASSERT(hint);
+ result = (vattr_sp_handle*)list;
+ *hint = result->hint;
+ return result;
+}
+
+/* Iterator function for the list */
+vattr_sp_handle *vattr_list_sp_next(vattr_sp_handle_list *list)
+{
+ vattr_sp_handle *result = NULL;
+ vattr_sp_handle *current = (vattr_sp_handle*) list;
+ PR_ASSERT(list);
+ result = current->next;
+ return result;
+}
+
+/* Iterator function for the list */
+vattr_sp_handle *vattr_list_sp_first(vattr_sp_handle_list *list)
+{
+ vattr_sp_handle *result = NULL;
+ PR_ASSERT(list);
+ result = (vattr_sp_handle*)list;
+ return result;
+}
+
+/* Function to insert an SP into the map entry for a given type name */
+/* Note that SP's can't ever remmove themselves from the map---if they could
+we'd need to hold a lock on the read path, which we don't want to do.
+So any SP which relinquishes its need to handle a type needs to continue
+to handle the calls on it, but return nothing */
+/* DBDB need to sort out memory ownership here, it's not quite right */
+
+int vattr_map_sp_insert(char *type_to_add, vattr_sp_handle *sp, void *hint)
+{
+ int result = 0;
+ vattr_map_entry *map_entry = NULL;
+ /* Is this type already there ? */
+ result = vattr_map_lookup(type_to_add,&map_entry);
+ /* If it is, add this SP to the list, safely even if readers are traversing the list at the same time */
+ if (0 == result) {
+ int found = 0;
+ vattr_sp_handle *list_entry = NULL;
+ /* Walk the list checking that the daft SP isn't already here */
+ for (list_entry = map_entry->sp_list ; list_entry; list_entry = list_entry->next) {
+ if (list_entry == sp) {
+ found = 1;
+ break;
+ }
+ }
+ /* If it is, we do nothing */
+ if(found) {
+ return 0;
+ }
+ /* We insert the SP handle into the linked list at the head */
+ sp->next = map_entry->sp_list;
+ map_entry->sp_list = sp;
+ } else {
+ /* If not, add it */
+ map_entry = vattr_map_entry_new(type_to_add,sp,hint);
+ if (NULL == map_entry) {
+ return ENOMEM;
+ }
+ return vattr_map_insert(map_entry);
+ }
+ return 0;
+}
+
+/*
+ * returns non-zero if type is a cachable virtual attr, zero otherwise.
+ *
+ * Decision point for caching vattrs...to be expanded to be configurable,
+ * allow sp's to have a say etc.
+*/
+
+static int cache_all = 0;
+
+int slapi_vattrcache_iscacheable( const char * type ) {
+
+ int rc = 0;
+
+ if(/*cache_all ||*/ !slapi_UTF8CASECMP((char *)type, "nsrole")) {
+ rc = 1;
+ }
+
+ return(rc);
+}
+
+/*
+ * slapi_vattrcache_cache_all and slapi_vattrcache_cache_none
+ * ----------------------------------------------------------
+ * limited control for deciding whether to
+ * cache anything, in reality controls whether
+ * to cache cos attributes right now
+ */
+void slapi_vattrcache_cache_all()
+{
+ cache_all = -1;
+}
+
+void slapi_vattrcache_cache_none()
+{
+ cache_all = 0;
+}
+
+void vattrcache_entry_READ_LOCK(const Slapi_Entry *e){
+ PR_RWLock_Rlock(e->e_virtual_lock);
+}
+
+void vattrcache_entry_READ_UNLOCK(const Slapi_Entry *e) {
+ PR_RWLock_Unlock(e->e_virtual_lock);
+
+}
+void vattrcache_entry_WRITE_LOCK(const Slapi_Entry *e){
+ PR_RWLock_Wlock(e->e_virtual_lock);
+
+}
+void vattrcache_entry_WRITE_UNLOCK(const Slapi_Entry *e){
+ PR_RWLock_Unlock(e->e_virtual_lock);
+}
+
+#ifdef VATTR_TEST_CODE
+
+/* Prototype SP begins here */
+
+/* Get value function */
+int vattr_basic_sp_get_value(vattr_sp_handle *handle, vattr_context *c, Slapi_Entry *e, char *type, Slapi_ValueSet** results,int *type_name_disposition, char** actual_type_name, int flags, int *buffer_flags, void *hint)
+{
+ Slapi_Value *value = NULL;
+ *buffer_flags = 0;
+ *actual_type_name = slapi_ch_strdup("dbtestattr");
+ value = slapi_value_new_string("Hello Client");
+ *results = slapi_ch_calloc(1,sizeof(Slapi_ValueSet));
+ slapi_valueset_init(*results);
+ slapi_valueset_add_value_ext(*results,value,SLAPI_VALUE_FLAG_PASSIN);
+ return 0;
+}
+
+/* Compare value function */
+int vattr_basic_sp_compare_value(vattr_sp_handle *handle, vattr_context *c, Slapi_Entry *e, char *type, Slapi_Value *test_this, int* result,int flags, void *hint)
+{
+ *result = 0;
+ return 0;
+}
+
+int vattr_basic_sp_list_types(vattr_sp_handle *handle,Slapi_Entry *e,vattr_type_list_context *type_context,int flags)
+{
+ static char* test_type_name = "dbtestattr";
+ vattr_type_thang thang = {0};
+
+ thang.type_name = test_type_name;
+ thang.type_flags = 0;
+
+ slapi_vattrspi_add_type(type_context,&thang,SLAPI_VIRTUALATTRS_REQUEST_POINTERS);
+ return 0;
+}
+
+int vattr_basic_sp_init()
+{
+ int ret = 0;
+ vattr_sp_handle *my_handle = NULL;
+ /* Register SP */
+ ret = slapi_vattrspi_register(&my_handle,vattr_basic_sp_get_value, vattr_basic_sp_compare_value, vattr_basic_sp_list_types);
+ if (ret) {
+ slapd_nasty(sourcefile,4,0);
+ return ret;
+ }
+ /* Register interest in some attribute over the entire tree */
+ ret = slapi_vattrspi_regattr(my_handle,"dbtestattr","", NULL);
+ if (ret) {
+ slapd_nasty(sourcefile,5,0);
+ return ret;
+ }
+ /* Register interest in some attribute over the entire tree */
+ ret = slapi_vattrspi_regattr(my_handle,"dbtestattr1","", NULL);
+ if (ret) {
+ slapd_nasty(sourcefile,5,0);
+ return ret;
+ }
+ /* Register interest in some attribute over the entire tree */
+ ret = slapi_vattrspi_regattr(my_handle,"dbtestattr2","", NULL);
+ if (ret) {
+ slapd_nasty(sourcefile,5,0);
+ return ret;
+ }
+ /* Register interest in some attribute over the entire tree */
+ ret = slapi_vattrspi_regattr(my_handle,"dbtestatt3r","", NULL);
+ if (ret) {
+ slapd_nasty(sourcefile,5,0);
+ return ret;
+ }
+ return ret;
+}
+
+/* What do we do on shutdown ? */
+int vattr_basic_sp_cleanup()
+{
+ return 0;
+}
+
+#endif
+
+
+
+
diff --git a/ldap/servers/slapd/vattr_spi.h b/ldap/servers/slapd/vattr_spi.h
new file mode 100644
index 00000000..c9de3988
--- /dev/null
+++ b/ldap/servers/slapd/vattr_spi.h
@@ -0,0 +1,54 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+
+/* Defines the vattr SPI interface, used by COS and Roles at present */
+/* Also needs to be included by any code which participates in the vattr
+ loop detection scheme (e.g. filter test code)
+ */
+
+/* Loop context structure */
+typedef struct _vattr_context vattr_context;
+typedef struct _vattr_sp_handle vattr_sp_handle;
+typedef struct _vattr_type_list_context vattr_type_list_context;
+
+typedef int (*vattr_get_fn_type)(vattr_sp_handle *handle, vattr_context *c, Slapi_Entry *e, char *type, Slapi_ValueSet** results,int *type_name_disposition, char** actual_type_name, int flags, int *free_flags, void *hint);
+typedef int (*vattr_get_ex_fn_type)(vattr_sp_handle *handle, vattr_context *c, Slapi_Entry *e, char **type, Slapi_ValueSet*** results,int **type_name_disposition, char*** actual_type_name, int flags, int *free_flags, void **hint);
+typedef int (*vattr_compare_fn_type)(vattr_sp_handle *handle, vattr_context *c, Slapi_Entry *e, char *type, Slapi_Value *test_this, int* result, int flags, void *hint);
+typedef int (*vattr_types_fn_type)(vattr_sp_handle *handle,Slapi_Entry *e,vattr_type_list_context *type_context,int flags);
+
+vattr_context *vattr_context_new( Slapi_PBlock *pb );
+
+int slapi_vattrspi_register(vattr_sp_handle **h, vattr_get_fn_type get_fn, vattr_compare_fn_type compare_fn, vattr_types_fn_type types_fn);
+
+/* options must be set to null */
+int slapi_vattrspi_register_ex(vattr_sp_handle **h, vattr_get_ex_fn_type get_fn, vattr_compare_fn_type compare_fn, vattr_types_fn_type types_fn, void *options);
+int slapi_vattrspi_regattr(vattr_sp_handle *h,char *type_name_to_register, char* DN /* Is there a DN type ?? */, void *hint);
+
+/* Type thang structure used by slapi_vattrspi_add_type() */
+struct _vattr_type_thang {
+ char *type_name;
+ unsigned long type_flags; /* Same values as Slapi_Attr->a_flags */
+ Slapi_ValueSet *type_values; /* for slapi_vattr_list_attrs() use only */
+};
+
+int slapi_vattrspi_add_type(vattr_type_list_context *c, vattr_type_thang *thang, int flags);
+
+/* get thang structure used by slapi_vattr_values_get_sp() */
+struct _vattr_get_thang {
+ int get_present;
+ char *get_type_name;
+ int get_name_disposition;
+ Slapi_ValueSet *get_present_values;
+ Slapi_Attr *get_attr;
+};
+
+/* Loop-detection-aware versions of the functions, to be called by service providers and their ilk */
+SLAPI_DEPRECATED int slapi_vattr_values_get_sp(vattr_context *c, /* Entry we're interested in */ Slapi_Entry *e, /* attr type name */ char *type, /* pointer to result set */ Slapi_ValueSet** results,int *type_name_disposition, char **actual_type_name, int flags, int *free_flags);
+int slapi_vattr_values_get_sp_ex(vattr_context *c, /* Entry we're interested in */ Slapi_Entry *e, /* attr type name */ char *type, /* pointer to result set */ Slapi_ValueSet*** results,int **type_name_disposition, char ***actual_type_name, int flags, int *free_flags, int *subtype_count);
+int slapi_vattr_namespace_values_get_sp(vattr_context *c, /* Entry we're interested in */ Slapi_Entry *e, /* backend namespace dn */ Slapi_DN *namespace_dn, /* attr type name */ char *type, /* pointer to result set */ Slapi_ValueSet*** results,int **type_name_disposition, char ***actual_type_name, int flags, int *free_flags, int *subtype_count);
+int slapi_vattr_value_compare_sp(vattr_context *c, Slapi_Entry *e,char *type, Slapi_Value *test_this, int *result, int flags);
+int slapi_vattr_namespace_value_compare_sp(vattr_context *c,/* Entry we're interested in */ Slapi_Entry *e, /* backend namespace dn*/Slapi_DN *namespace_dn, /* attr type name */ const char *type, Slapi_Value *test_this,/* pointer to result */ int *result, int flags);
+
diff --git a/ldap/servers/slapd/views.h b/ldap/servers/slapd/views.h
new file mode 100644
index 00000000..10a7b135
--- /dev/null
+++ b/ldap/servers/slapd/views.h
@@ -0,0 +1,29 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+
+#ifndef _VIEWS_H_
+#define _VIEWS_H_
+
+/* mechanics */
+
+typedef int (*api_views_entry_exists)(char *view_dn, Slapi_Entry *e);
+typedef int (*api_views_entry_dn_exists)(char *view_dn, char *e_dn);
+
+/* API ID for slapi_apib_get_interface */
+
+#define Views_v1_0_GUID "000e5b1e-9958-41da-a573-db8064a3894e"
+
+/* API */
+
+/* the api broker reserves api[0] for its use */
+
+#define views_entry_exists(api, dn, entry) \
+ ((api_views_entry_exists*)(api))[1]( dn, entry )
+
+#define views_entry_dn_exists(api, dn, entry_dn) \
+ ((api_views_entry_dn_exists*)(api))[2]( dn, entry_dn )
+
+#endif /*_VIEWS_H_*/
diff --git a/ldap/servers/snmp/Makefile b/ldap/servers/snmp/Makefile
new file mode 100644
index 00000000..4e40d8c0
--- /dev/null
+++ b/ldap/servers/snmp/Makefile
@@ -0,0 +1,91 @@
+#
+# BEGIN COPYRIGHT BLOCK
+# Copyright 2001 Sun Microsystems, Inc.
+# Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+# All rights reserved.
+# END COPYRIGHT BLOCK
+#
+#
+# Make file SNMP subagent for Netscape Directory Server
+#
+#
+# Revision History:
+#
+# 07/31/97 Created by stevross
+#
+#
+
+MCOM_ROOT = ../../../..
+LDAP_SRC = ../..
+
+NOSTDCLEAN=true # don't let nsconfig.mk define target clean
+NOSTDSTRIP=true # don't let nsconfig.mk define target strip
+NSPR20=true # probably should be defined somewhere else (not sure where)
+
+OBJDEST = $(OBJDIR)/ns-ldapagt/obj
+INCLDEST = $(OBJDIR)/ns-ldapagt/include
+BINDIR = $(LDAP_SERVER_RELDIR)
+EXTDEST = $(BINDIR)
+
+include $(MCOM_ROOT)/ldapserver/nsconfig.mk
+include $(LDAP_SRC)/nsldap.mk
+
+MCC_SERVER=
+
+ARCH := $(shell uname -s)
+ifneq ($(ARCH), WINNT)
+ARCH := $(shell $(MCOM_ROOT)/ldapserver/nsarch)
+endif
+
+
+SNMPMODULE = ns-ldapagt
+
+# the netscape-ldap.mib goes in the plugins/snmp directory, and the other mib like
+# files go in the plugins/snmp/mibs directory
+NSMIB_DEST_DIR = $(RELDIR)/plugins/snmp
+NSMIB_SRC_FILES = netscape-ldap.mib
+MIBS_DEST_DIR = $(NSMIB_DEST_DIR)/mibs
+MIBS_SRC_FILES = NETWORK-SERVICES-MIB.txt \
+ RFC1155-SMI.txt \
+ RFC-1215.txt \
+ SNMPv2-CONF.txt \
+ SNMPv2-SMI.txt \
+ SNMPv2-TC.txt
+MIB_DEST_FILES = $(addprefix $(MIBS_DEST_DIR)/,$(notdir $(MIBS_SRC_FILES))) \
+ $(addprefix $(NSMIB_DEST_DIR)/,$(NSMIB_SRC_FILES))
+
+default: all
+
+ifneq ($(ARCH), WINNT)
+all: $(EXTDEST)/$(SNMPMODULE) $(NSMIB_DEST_DIR)/$(NSMIB_SRC_FILES) $(MIB_DEST_FILES)
+else
+OBJ_SUFFIX=obj
+all: $(MIB_DEST_FILES)
+ cd ntagt; $(MAKE) $(MFLAGS) all
+endif
+
+# Rule to create destination directories
+$(MIBS_DEST_DIR) $(NSMIB_DEST_DIR):
+ $(MKDIR) $@
+
+# Rule to copy subagent binary to release area
+$(EXTDEST)/$(SNMPMODULE): $(EXTDEST)
+ifneq ($(ARCH), WINNT)
+ $(CP) $(NSCP_DISTDIR)/peer/obj/$(SNMPMODULE) $(EXTDEST)/$(SNMPMODULE)
+endif
+
+# this rule is for mib files in the local directory that go in the nsmib directory
+$(NSMIB_DEST_DIR)/$(NSMIB_SRC_FILES): $(NSMIB_DEST_DIR)
+ $(CP) ./$(NSMIB_SRC_FILES) $@
+
+# this rule is for mib files which go in the mibs subdir
+$(MIBS_DEST_DIR)/%: % $(MIBS_DEST_DIR)
+ $(CP) $< $@
+
+clean: localclean
+
+localclean:
+ifneq ($(ARCH), WINNT)
+ $(RM) $(EXTDEST)/$(SNMPMODULE)$(EXE_SUFFIX)
+endif
+
diff --git a/ldap/servers/snmp/NETWORK-SERVICES-MIB.txt b/ldap/servers/snmp/NETWORK-SERVICES-MIB.txt
new file mode 100644
index 00000000..cadc5504
--- /dev/null
+++ b/ldap/servers/snmp/NETWORK-SERVICES-MIB.txt
@@ -0,0 +1,650 @@
+-- extracted from rfc2788.txt
+-- at Fri Mar 24 07:07:18 2000
+
+ NETWORK-SERVICES-MIB DEFINITIONS ::= BEGIN
+
+ IMPORTS
+ OBJECT-TYPE, Counter32, Gauge32, MODULE-IDENTITY, mib-2
+ FROM SNMPv2-SMI
+ TimeStamp, TEXTUAL-CONVENTION
+ FROM SNMPv2-TC
+ MODULE-COMPLIANCE, OBJECT-GROUP
+ FROM SNMPv2-CONF
+ SnmpAdminString
+ FROM SNMP-FRAMEWORK-MIB;
+
+ application MODULE-IDENTITY
+ LAST-UPDATED "200003030000Z"
+ ORGANIZATION "IETF Mail and Directory Management Working Group"
+ CONTACT-INFO
+ " Ned Freed
+
+ Postal: Innosoft International, Inc.
+ 1050 Lakes Drive
+ West Covina, CA 91790
+ US
+
+ Tel: +1 626 919 3600
+ Fax: +1 626 919 3614
+
+ E-Mail: ned.freed@innosoft.com"
+ DESCRIPTION
+ "The MIB module describing network service applications"
+ REVISION "200003030000Z"
+ DESCRIPTION
+ "This revision, published in RFC 2788, changes a number of
+ DisplayStrings to SnmpAdminStrings. Note that this change
+ is not strictly supported by SMIv2. However, the alternative
+ of deprecating the old objects and defining new objects
+ would have a more adverse impact on backward compatibility
+ and interoperability, given the particular semantics of
+ these objects. The defining reference for distinguished
+ names has also been updated from RFC 1779 to RFC 2253."
+ REVISION "199905120000Z"
+ DESCRIPTION
+ "This revision fixes a few small technical problems found
+ in previous versions, mostly in regards to the conformance
+ groups for different versions of this MIB. No changes have
+ been made to the objects this MIB defines since RFC 2248."
+ REVISION "199708170000Z"
+ DESCRIPTION
+ "This revision, published in RFC 2248, adds the
+ applDescription and applURL objects, adds the quiescing
+ state to the applOperStatus object and renames the MIB
+ from the APPLICATION-MIB to the NETWORK-SERVICE-MIB."
+ REVISION "199311280000Z"
+ DESCRIPTION
+ "The original version of this MIB was published in RFC 1565"
+ ::= {mib-2 27}
+
+ -- Textual conventions
+
+ -- DistinguishedName is used to refer to objects in the
+ -- directory.
+
+ DistinguishedName ::= TEXTUAL-CONVENTION
+ DISPLAY-HINT "255a"
+ STATUS current
+ DESCRIPTION
+ "A Distinguished Name represented in accordance with
+ RFC 2253, presented in the UTF-8 charset defined in
+ RFC 2279."
+ SYNTAX OCTET STRING (SIZE (0..255))
+
+ -- Uniform Resource Locators are stored in URLStrings.
+
+ URLString ::= TEXTUAL-CONVENTION
+ DISPLAY-HINT "255a"
+ STATUS current
+ DESCRIPTION
+ "A Uniform Resource Locator represented in accordance
+ with RFCs 1738 and 2368, presented in the NVT ASCII
+ charset defined in RFC 854."
+ SYNTAX OCTET STRING (SIZE (0..255))
+
+ -- The basic applTable contains a list of the application
+ -- entities.
+
+ applTable OBJECT-TYPE
+ SYNTAX SEQUENCE OF ApplEntry
+ MAX-ACCESS not-accessible
+ STATUS current
+ DESCRIPTION
+ "The table holding objects which apply to all different
+ kinds of applications providing network services.
+ Each network service application capable of being
+ monitored should have a single entry in this table."
+ ::= {application 1}
+
+ applEntry OBJECT-TYPE
+ SYNTAX ApplEntry
+ MAX-ACCESS not-accessible
+ STATUS current
+ DESCRIPTION
+ "An entry associated with a single network service
+ application."
+ INDEX {applIndex}
+ ::= {applTable 1}
+
+ ApplEntry ::= SEQUENCE {
+ applIndex
+ INTEGER,
+ applName
+ SnmpAdminString,
+ applDirectoryName
+ DistinguishedName,
+ applVersion
+ SnmpAdminString,
+ applUptime
+ TimeStamp,
+ applOperStatus
+ INTEGER,
+ applLastChange
+ TimeStamp,
+ applInboundAssociations
+ Gauge32,
+ applOutboundAssociations
+ Gauge32,
+ applAccumulatedInboundAssociations
+ Counter32,
+ applAccumulatedOutboundAssociations
+ Counter32,
+ applLastInboundActivity
+ TimeStamp,
+ applLastOutboundActivity
+ TimeStamp,
+ applRejectedInboundAssociations
+ Counter32,
+ applFailedOutboundAssociations
+ Counter32,
+ applDescription
+ SnmpAdminString,
+ applURL
+ URLString
+ }
+
+ applIndex OBJECT-TYPE
+ SYNTAX INTEGER (1..2147483647)
+ MAX-ACCESS not-accessible
+ STATUS current
+ DESCRIPTION
+ "An index to uniquely identify the network service
+ application. This attribute is the index used for
+ lexicographic ordering of the table."
+ ::= {applEntry 1}
+
+ applName OBJECT-TYPE
+ SYNTAX SnmpAdminString
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "The name the network service application chooses to be
+ known by."
+ ::= {applEntry 2}
+
+ applDirectoryName OBJECT-TYPE
+ SYNTAX DistinguishedName
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "The Distinguished Name of the directory entry where
+ static information about this application is stored.
+ An empty string indicates that no information about
+ the application is available in the directory."
+ ::= {applEntry 3}
+
+ applVersion OBJECT-TYPE
+ SYNTAX SnmpAdminString
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "The version of network service application software.
+ This field is usually defined by the vendor of the
+ network service application software."
+ ::= {applEntry 4}
+ applUptime OBJECT-TYPE
+ SYNTAX TimeStamp
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "The value of sysUpTime at the time the network service
+ application was last initialized. If the application was
+ last initialized prior to the last initialization of the
+ network management subsystem, then this object contains
+ a zero value."
+ ::= {applEntry 5}
+
+ applOperStatus OBJECT-TYPE
+ SYNTAX INTEGER {
+ up(1),
+ down(2),
+ halted(3),
+ congested(4),
+ restarting(5),
+ quiescing(6)
+ }
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "Indicates the operational status of the network service
+ application. 'down' indicates that the network service is
+ not available. 'up' indicates that the network service
+ is operational and available. 'halted' indicates that the
+ service is operational but not available. 'congested'
+ indicates that the service is operational but no additional
+ inbound associations can be accommodated. 'restarting'
+ indicates that the service is currently unavailable but is
+ in the process of restarting and will be available soon.
+ 'quiescing' indicates that service is currently operational
+ but is in the process of shutting down. Additional inbound
+ associations may be rejected by applications in the
+ 'quiescing' state."
+ ::= {applEntry 6}
+
+ applLastChange OBJECT-TYPE
+ SYNTAX TimeStamp
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "The value of sysUpTime at the time the network service
+ application entered its current operational state. If
+ the current state was entered prior to the last
+ initialization of the local network management subsystem,
+ then this object contains a zero value."
+ ::= {applEntry 7}
+
+ applInboundAssociations OBJECT-TYPE
+ SYNTAX Gauge32
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "The number of current associations to the network service
+ application, where it is the responder. An inbound
+ association occurs when another application successfully
+ connects to this one."
+ ::= {applEntry 8}
+
+ applOutboundAssociations OBJECT-TYPE
+ SYNTAX Gauge32
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "The number of current associations to the network service
+ application, where it is the initiator. An outbound
+ association occurs when this application successfully
+ connects to another one."
+ ::= {applEntry 9}
+
+ applAccumulatedInboundAssociations OBJECT-TYPE
+ SYNTAX Counter32
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "The total number of associations to the application entity
+ since application initialization, where it was the responder."
+ ::= {applEntry 10}
+
+ applAccumulatedOutboundAssociations OBJECT-TYPE
+ SYNTAX Counter32
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "The total number of associations to the application entity
+ since application initialization, where it was the initiator."
+ ::= {applEntry 11}
+
+ applLastInboundActivity OBJECT-TYPE
+ SYNTAX TimeStamp
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "The value of sysUpTime at the time this application last
+ had an inbound association. If the last association
+ occurred prior to the last initialization of the network
+ subsystem, then this object contains a zero value."
+ ::= {applEntry 12}
+
+ applLastOutboundActivity OBJECT-TYPE
+ SYNTAX TimeStamp
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "The value of sysUpTime at the time this application last
+ had an outbound association. If the last association
+ occurred prior to the last initialization of the network
+ subsystem, then this object contains a zero value."
+ ::= {applEntry 13}
+
+ applRejectedInboundAssociations OBJECT-TYPE
+ SYNTAX Counter32
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "The total number of inbound associations the application
+ entity has rejected, since application initialization.
+ Rejected associations are not counted in the accumulated
+ association totals. Note that this only counts
+ associations the application entity has rejected itself;
+ it does not count rejections that occur at lower layers
+ of the network. Thus, this counter may not reflect the
+ true number of failed inbound associations."
+ ::= {applEntry 14}
+
+ applFailedOutboundAssociations OBJECT-TYPE
+ SYNTAX Counter32
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "The total number associations where the application entity
+ is initiator and association establishment has failed,
+ since application initialization. Failed associations are
+ not counted in the accumulated association totals."
+ ::= {applEntry 15}
+
+ applDescription OBJECT-TYPE
+ SYNTAX SnmpAdminString
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "A text description of the application. This information
+ is intended to identify and briefly describe the
+ application in a status display."
+ ::= {applEntry 16}
+
+ applURL OBJECT-TYPE
+ SYNTAX URLString
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "A URL pointing to a description of the application.
+ This information is intended to identify and describe
+ the application in a status display."
+ ::= {applEntry 17}
+
+ -- The assocTable augments the information in the applTable
+ -- with information about associations. Note that two levels
+ -- of compliance are specified below, depending on whether
+ -- association monitoring is mandated.
+
+ assocTable OBJECT-TYPE
+ SYNTAX SEQUENCE OF AssocEntry
+ MAX-ACCESS not-accessible
+ STATUS current
+ DESCRIPTION
+ "The table holding a set of all active application
+ associations."
+ ::= {application 2}
+
+ assocEntry OBJECT-TYPE
+ SYNTAX AssocEntry
+ MAX-ACCESS not-accessible
+ STATUS current
+ DESCRIPTION
+ "An entry associated with an association for a network
+ service application."
+ INDEX {applIndex, assocIndex}
+ ::= {assocTable 1}
+
+ AssocEntry ::= SEQUENCE {
+ assocIndex
+ INTEGER,
+ assocRemoteApplication
+ SnmpAdminString,
+ assocApplicationProtocol
+ OBJECT IDENTIFIER,
+ assocApplicationType
+ INTEGER,
+ assocDuration
+ TimeStamp
+ }
+
+ assocIndex OBJECT-TYPE
+ SYNTAX INTEGER (1..2147483647)
+ MAX-ACCESS not-accessible
+ STATUS current
+ DESCRIPTION
+ "An index to uniquely identify each association for a network
+ service application. This attribute is the index that is
+ used for lexicographic ordering of the table. Note that the
+ table is also indexed by the applIndex."
+ ::= {assocEntry 1}
+
+ assocRemoteApplication OBJECT-TYPE
+ SYNTAX SnmpAdminString
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "The name of the system running remote network service
+ application. For an IP-based application this should be
+ either a domain name or IP address. For an OSI application
+ it should be the string encoded distinguished name of the
+ managed object. For X.400(1984) MTAs which do not have a
+ Distinguished Name, the RFC 2156 syntax 'mta in
+ globalid' used in X400-Received: fields can be used. Note,
+ however, that not all connections an MTA makes are
+ necessarily to another MTA."
+ ::= {assocEntry 2}
+
+ assocApplicationProtocol OBJECT-TYPE
+ SYNTAX OBJECT IDENTIFIER
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "An identification of the protocol being used for the
+ application. For an OSI Application, this will be the
+ Application Context. For Internet applications, OID
+ values of the form {applTCPProtoID port} or {applUDPProtoID
+ port} are used for TCP-based and UDP-based protocols,
+ respectively. In either case 'port' corresponds to the
+ primary port number being used by the protocol. The
+ usual IANA procedures may be used to register ports for
+ new protocols."
+ ::= {assocEntry 3}
+
+ assocApplicationType OBJECT-TYPE
+ SYNTAX INTEGER {
+ uainitiator(1),
+ uaresponder(2),
+ peerinitiator(3),
+ peerresponder(4)}
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "This indicates whether the remote application is some type of
+ client making use of this network service (e.g., a Mail User
+ Agent) or a server acting as a peer. Also indicated is whether
+ the remote end initiated an incoming connection to the network
+ service or responded to an outgoing connection made by the
+ local application. MTAs and messaging gateways are
+ considered to be peers for the purposes of this variable."
+ ::= {assocEntry 4}
+
+ assocDuration OBJECT-TYPE
+ SYNTAX TimeStamp
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "The value of sysUpTime at the time this association was
+ started. If this association started prior to the last
+ initialization of the network subsystem, then this
+ object contains a zero value."
+ ::= {assocEntry 5}
+
+
+ -- Conformance information
+
+ applConformance OBJECT IDENTIFIER ::= {application 3}
+
+ applGroups OBJECT IDENTIFIER ::= {applConformance 1}
+ applCompliances OBJECT IDENTIFIER ::= {applConformance 2}
+
+ -- Compliance statements
+
+ applCompliance MODULE-COMPLIANCE
+ STATUS obsolete
+ DESCRIPTION
+ "The compliance statement for RFC 1565 implementations
+ which support the Network Services Monitoring MIB
+ for basic monitoring of network service applications.
+ This is the basic compliance statement for RFC 1565."
+ MODULE
+ MANDATORY-GROUPS {applRFC1565Group}
+ ::= {applCompliances 1}
+
+ assocCompliance MODULE-COMPLIANCE
+ STATUS obsolete
+ DESCRIPTION
+ "The compliance statement for RFC 1565 implementations
+ which support the Network Services Monitoring MIB
+ for basic monitoring of network service applications
+ and their associations."
+ MODULE
+ MANDATORY-GROUPS {applRFC1565Group, assocRFC1565Group}
+ ::= {applCompliances 2}
+
+ applRFC2248Compliance MODULE-COMPLIANCE
+ STATUS deprecated
+ DESCRIPTION
+ "The compliance statement for RFC 2248 implementations
+ which support the Network Services Monitoring MIB
+ for basic monitoring of network service applications."
+ MODULE
+ MANDATORY-GROUPS {applRFC2248Group}
+ ::= {applCompliances 3}
+
+ assocRFC2248Compliance MODULE-COMPLIANCE
+ STATUS deprecated
+ DESCRIPTION
+ "The compliance statement for RFC 2248 implementations
+ which support the Network Services Monitoring MIB for
+ basic monitoring of network service applications and
+ their associations."
+ MODULE
+ MANDATORY-GROUPS {applRFC2248Group, assocRFC2248Group}
+ ::= {applCompliances 4}
+
+ applRFC2788Compliance MODULE-COMPLIANCE
+ STATUS current
+ DESCRIPTION
+ "The compliance statement for RFC 2788 implementations
+ which support the Network Services Monitoring MIB
+ for basic monitoring of network service applications."
+ MODULE
+ MANDATORY-GROUPS {applRFC2788Group}
+ ::= {applCompliances 5}
+
+ assocRFC2788Compliance MODULE-COMPLIANCE
+ STATUS current
+ DESCRIPTION
+ "The compliance statement for RFC 2788 implementations
+ which support the Network Services Monitoring MIB for
+ basic monitoring of network service applications and
+ their associations."
+ MODULE
+ MANDATORY-GROUPS {applRFC2788Group, assocRFC2788Group}
+ ::= {applCompliances 6}
+
+
+ -- Units of conformance
+
+ applRFC1565Group OBJECT-GROUP
+ OBJECTS {
+ applName, applVersion, applUptime, applOperStatus,
+ applLastChange, applInboundAssociations,
+ applOutboundAssociations, applAccumulatedInboundAssociations,
+ applAccumulatedOutboundAssociations, applLastInboundActivity,
+ applLastOutboundActivity, applRejectedInboundAssociations,
+ applFailedOutboundAssociations}
+ STATUS obsolete
+ DESCRIPTION
+ "A collection of objects providing basic monitoring of
+ network service applications. This is the original set
+ of such objects defined in RFC 1565."
+ ::= {applGroups 7}
+
+ assocRFC1565Group OBJECT-GROUP
+ OBJECTS {
+ assocRemoteApplication, assocApplicationProtocol,
+ assocApplicationType, assocDuration}
+ STATUS obsolete
+ DESCRIPTION
+ "A collection of objects providing basic monitoring of
+ network service applications' associations. This is the
+ original set of such objects defined in RFC 1565."
+ ::= {applGroups 2}
+
+ applRFC2248Group OBJECT-GROUP
+ OBJECTS {
+ applName, applVersion, applUptime, applOperStatus,
+ applLastChange, applInboundAssociations,
+ applOutboundAssociations, applAccumulatedInboundAssociations,
+ applAccumulatedOutboundAssociations, applLastInboundActivity,
+ applLastOutboundActivity, applRejectedInboundAssociations,
+ applFailedOutboundAssociations, applDescription, applURL}
+ STATUS deprecated
+ DESCRIPTION
+ "A collection of objects providing basic monitoring of
+ network service applications. This group was originally
+ defined in RFC 2248; note that applDirectoryName is
+ missing."
+ ::= {applGroups 3}
+
+ assocRFC2248Group OBJECT-GROUP
+ OBJECTS {
+ assocRemoteApplication, assocApplicationProtocol,
+ assocApplicationType, assocDuration}
+ STATUS deprecated
+ DESCRIPTION
+ "A collection of objects providing basic monitoring of
+ network service applications' associations. This group
+ was originally defined by RFC 2248."
+ ::= {applGroups 4}
+
+ applRFC2788Group OBJECT-GROUP
+ OBJECTS {
+ applName, applDirectoryName, applVersion, applUptime,
+ applOperStatus, applLastChange, applInboundAssociations,
+ applOutboundAssociations, applAccumulatedInboundAssociations,
+ applAccumulatedOutboundAssociations, applLastInboundActivity,
+ applLastOutboundActivity, applRejectedInboundAssociations,
+ applFailedOutboundAssociations, applDescription, applURL}
+ STATUS current
+ DESCRIPTION
+ "A collection of objects providing basic monitoring of
+ network service applications. This is the appropriate
+ group for RFC 2788 -- it adds the applDirectoryName object
+ missing in RFC 2248."
+ ::= {applGroups 5}
+
+ assocRFC2788Group OBJECT-GROUP
+ OBJECTS {
+ assocRemoteApplication, assocApplicationProtocol,
+ assocApplicationType, assocDuration}
+ STATUS current
+ DESCRIPTION
+ "A collection of objects providing basic monitoring of
+ network service applications' associations. This is
+ the appropriate group for RFC 2788."
+ ::= {applGroups 6}
+
+ -- OIDs of the form {applTCPProtoID port} are intended to be used
+ -- for TCP-based protocols that don't have OIDs assigned by other
+ -- means. {applUDPProtoID port} serves the same purpose for
+ -- UDP-based protocols. In either case 'port' corresponds to
+ -- the primary port number being used by the protocol. For example,
+ -- assuming no other OID is assigned for SMTP, an OID of
+ -- {applTCPProtoID 25} could be used, since SMTP is a TCP-based
+ -- protocol that uses port 25 as its primary port.
+
+ applTCPProtoID OBJECT IDENTIFIER ::= {application 4}
+ applUDPProtoID OBJECT IDENTIFIER ::= {application 5}
+
+ END
+
+--
+-- Copyright (C) The Internet Society (2000). 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 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.
+--
diff --git a/ldap/servers/snmp/RFC-1215.txt b/ldap/servers/snmp/RFC-1215.txt
new file mode 100644
index 00000000..64327233
--- /dev/null
+++ b/ldap/servers/snmp/RFC-1215.txt
@@ -0,0 +1,38 @@
+
+RFC-1215 DEFINITIONS ::= BEGIN
+
+-- This module is a empty module. It has been created solely for the
+-- purpose of allowing other modules to correctly import the TRAP-TYPE
+-- clause from RFC-1215 where it should be imported from. It's a
+-- built in type in the UCD-SNMP code, and in fact RFC-1215 doesn't
+-- actually define a mib at all; it only defines macros. However,
+-- importing the TRAP-TYPE is conventionally done from an import
+-- clause pointing to RFC-1215.
+--
+-- Wes 7/17/98
+
+TRAP-TYPE MACRO ::=
+BEGIN
+ TYPE NOTATION ::= "ENTERPRISE" value
+ (enterprise OBJECT IDENTIFIER)
+ VarPart
+ DescrPart
+ ReferPart
+ VALUE NOTATION ::= value (VALUE INTEGER)
+ VarPart ::=
+ "VARIABLES" "{" VarTypes "}"
+ | empty
+ VarTypes ::=
+ VarType | VarTypes "," VarType
+ VarType ::=
+ value (vartype ObjectName)
+ DescrPart ::=
+ "DESCRIPTION" value (description DisplayString)
+ | empty
+ ReferPart ::=
+ "REFERENCE" value (reference DisplayString)
+ | empty
+END
+
+
+END
diff --git a/ldap/servers/snmp/RFC1155-SMI.txt b/ldap/servers/snmp/RFC1155-SMI.txt
new file mode 100644
index 00000000..3abc7ffb
--- /dev/null
+++ b/ldap/servers/snmp/RFC1155-SMI.txt
@@ -0,0 +1,119 @@
+RFC1155-SMI DEFINITIONS ::= BEGIN
+
+EXPORTS -- EVERYTHING
+ internet, directory, mgmt,
+ experimental, private, enterprises,
+ OBJECT-TYPE, ObjectName, ObjectSyntax, SimpleSyntax,
+ ApplicationSyntax, NetworkAddress, IpAddress,
+ Counter, Gauge, TimeTicks, Opaque;
+
+ -- the path to the root
+
+ internet OBJECT IDENTIFIER ::= { iso org(3) dod(6) 1 }
+
+ directory OBJECT IDENTIFIER ::= { internet 1 }
+
+ mgmt OBJECT IDENTIFIER ::= { internet 2 }
+
+ experimental OBJECT IDENTIFIER ::= { internet 3 }
+
+ private OBJECT IDENTIFIER ::= { internet 4 }
+ enterprises OBJECT IDENTIFIER ::= { private 1 }
+
+ -- definition of object types
+
+ OBJECT-TYPE MACRO ::=
+ BEGIN
+ TYPE NOTATION ::= "SYNTAX" type (TYPE ObjectSyntax)
+ "ACCESS" Access
+ "STATUS" Status
+ VALUE NOTATION ::= value (VALUE ObjectName)
+
+ Access ::= "read-only"
+ | "read-write"
+ | "write-only"
+ | "not-accessible"
+ Status ::= "mandatory"
+ | "optional"
+ | "obsolete"
+ END
+
+ -- names of objects in the MIB
+
+ ObjectName ::=
+ OBJECT IDENTIFIER
+
+ -- syntax of objects in the MIB
+
+ ObjectSyntax ::=
+ CHOICE {
+ simple
+ SimpleSyntax,
+ -- note that simple SEQUENCEs are not directly
+ -- mentioned here to keep things simple (i.e.,
+ -- prevent mis-use). However, application-wide
+ -- types which are IMPLICITly encoded simple
+ -- SEQUENCEs may appear in the following CHOICE
+
+ application-wide
+ ApplicationSyntax
+ }
+
+ SimpleSyntax ::=
+ CHOICE {
+ number
+ INTEGER,
+ string
+ OCTET STRING,
+ object
+ OBJECT IDENTIFIER,
+ empty
+ NULL
+ }
+
+ ApplicationSyntax ::=
+ CHOICE {
+ address
+ NetworkAddress,
+ counter
+ Counter,
+ gauge
+ Gauge,
+ ticks
+ TimeTicks,
+ arbitrary
+ Opaque
+
+ -- other application-wide types, as they are
+ -- defined, will be added here
+ }
+
+ -- application-wide types
+
+ NetworkAddress ::=
+ CHOICE {
+ internet
+ IpAddress
+ }
+
+ IpAddress ::=
+ [APPLICATION 0] -- in network-byte order
+ IMPLICIT OCTET STRING (SIZE (4))
+
+ Counter ::=
+ [APPLICATION 1]
+ IMPLICIT INTEGER (0..4294967295)
+
+ Gauge ::=
+ [APPLICATION 2]
+ IMPLICIT INTEGER (0..4294967295)
+
+ TimeTicks ::=
+ [APPLICATION 3]
+ IMPLICIT INTEGER (0..4294967295)
+
+ Opaque ::=
+ [APPLICATION 4] -- arbitrary ASN.1 value,
+ IMPLICIT OCTET STRING -- "double-wrapped"
+
+ END
diff --git a/ldap/servers/snmp/SNMPv2-CONF.txt b/ldap/servers/snmp/SNMPv2-CONF.txt
new file mode 100644
index 00000000..24a1eed9
--- /dev/null
+++ b/ldap/servers/snmp/SNMPv2-CONF.txt
@@ -0,0 +1,322 @@
+SNMPv2-CONF DEFINITIONS ::= BEGIN
+
+IMPORTS ObjectName, NotificationName, ObjectSyntax
+ FROM SNMPv2-SMI;
+
+-- definitions for conformance groups
+
+OBJECT-GROUP MACRO ::=
+BEGIN
+ TYPE NOTATION ::=
+ ObjectsPart
+ "STATUS" Status
+ "DESCRIPTION" Text
+ ReferPart
+
+ VALUE NOTATION ::=
+ value(VALUE OBJECT IDENTIFIER)
+
+ ObjectsPart ::=
+ "OBJECTS" "{" Objects "}"
+ Objects ::=
+ Object
+ | Objects "," Object
+ Object ::=
+
+ value(ObjectName)
+
+ Status ::=
+ "current"
+ | "deprecated"
+ | "obsolete"
+
+ ReferPart ::=
+ "REFERENCE" Text
+ | empty
+
+ -- a character string as defined in [2]
+ Text ::= value(IA5String)
+END
+
+-- more definitions for conformance groups
+
+NOTIFICATION-GROUP MACRO ::=
+BEGIN
+ TYPE NOTATION ::=
+ NotificationsPart
+ "STATUS" Status
+ "DESCRIPTION" Text
+ ReferPart
+
+ VALUE NOTATION ::=
+ value(VALUE OBJECT IDENTIFIER)
+
+ NotificationsPart ::=
+ "NOTIFICATIONS" "{" Notifications "}"
+ Notifications ::=
+ Notification
+ | Notifications "," Notification
+ Notification ::=
+ value(NotificationName)
+
+ Status ::=
+ "current"
+ | "deprecated"
+ | "obsolete"
+
+ ReferPart ::=
+ "REFERENCE" Text
+ | empty
+
+ -- a character string as defined in [2]
+ Text ::= value(IA5String)
+END
+
+-- definitions for compliance statements
+
+MODULE-COMPLIANCE MACRO ::=
+BEGIN
+ TYPE NOTATION ::=
+ "STATUS" Status
+ "DESCRIPTION" Text
+ ReferPart
+ ModulePart
+
+ VALUE NOTATION ::=
+ value(VALUE OBJECT IDENTIFIER)
+
+ Status ::=
+ "current"
+ | "deprecated"
+ | "obsolete"
+
+ ReferPart ::=
+ "REFERENCE" Text
+ | empty
+
+ ModulePart ::=
+ Modules
+ Modules ::=
+ Module
+ | Modules Module
+ Module ::=
+ -- name of module --
+ "MODULE" ModuleName
+ MandatoryPart
+ CompliancePart
+
+ ModuleName ::=
+ -- identifier must start with uppercase letter
+ identifier ModuleIdentifier
+ -- must not be empty unless contained
+ -- in MIB Module
+ | empty
+ ModuleIdentifier ::=
+ value(OBJECT IDENTIFIER)
+ | empty
+
+ MandatoryPart ::=
+ "MANDATORY-GROUPS" "{" Groups "}"
+ | empty
+
+ Groups ::=
+
+ Group
+ | Groups "," Group
+ Group ::=
+ value(OBJECT IDENTIFIER)
+
+ CompliancePart ::=
+ Compliances
+ | empty
+
+ Compliances ::=
+ Compliance
+ | Compliances Compliance
+ Compliance ::=
+ ComplianceGroup
+ | Object
+
+ ComplianceGroup ::=
+ "GROUP" value(OBJECT IDENTIFIER)
+ "DESCRIPTION" Text
+
+ Object ::=
+ "OBJECT" value(ObjectName)
+ SyntaxPart
+ WriteSyntaxPart
+ AccessPart
+ "DESCRIPTION" Text
+
+ -- must be a refinement for object's SYNTAX clause
+ SyntaxPart ::= "SYNTAX" Syntax
+ | empty
+
+ -- must be a refinement for object's SYNTAX clause
+ WriteSyntaxPart ::= "WRITE-SYNTAX" Syntax
+ | empty
+
+ Syntax ::= -- Must be one of the following:
+ -- a base type (or its refinement),
+ -- a textual convention (or its refinement), or
+ -- a BITS pseudo-type
+ type
+ | "BITS" "{" NamedBits "}"
+
+ NamedBits ::= NamedBit
+ | NamedBits "," NamedBit
+
+ NamedBit ::= identifier "(" number ")" -- number is nonnegative
+
+ AccessPart ::=
+ "MIN-ACCESS" Access
+ | empty
+ Access ::=
+ "not-accessible"
+ | "accessible-for-notify"
+ | "read-only"
+ | "read-write"
+ | "read-create"
+
+ -- a character string as defined in [2]
+ Text ::= value(IA5String)
+END
+
+-- definitions for capabilities statements
+
+AGENT-CAPABILITIES MACRO ::=
+BEGIN
+ TYPE NOTATION ::=
+ "PRODUCT-RELEASE" Text
+ "STATUS" Status
+ "DESCRIPTION" Text
+ ReferPart
+ ModulePart
+
+ VALUE NOTATION ::=
+ value(VALUE OBJECT IDENTIFIER)
+
+ Status ::=
+ "current"
+ | "obsolete"
+
+ ReferPart ::=
+ "REFERENCE" Text
+ | empty
+
+ ModulePart ::=
+ Modules
+ | empty
+ Modules ::=
+ Module
+ | Modules Module
+ Module ::=
+ -- name of module --
+ "SUPPORTS" ModuleName
+ "INCLUDES" "{" Groups "}"
+ VariationPart
+
+ ModuleName ::=
+
+ -- identifier must start with uppercase letter
+ identifier ModuleIdentifier
+ ModuleIdentifier ::=
+ value(OBJECT IDENTIFIER)
+ | empty
+
+ Groups ::=
+ Group
+ | Groups "," Group
+ Group ::=
+ value(OBJECT IDENTIFIER)
+
+ VariationPart ::=
+ Variations
+ | empty
+ Variations ::=
+ Variation
+ | Variations Variation
+
+ Variation ::=
+ ObjectVariation
+ | NotificationVariation
+
+ NotificationVariation ::=
+ "VARIATION" value(NotificationName)
+ AccessPart
+ "DESCRIPTION" Text
+
+ ObjectVariation ::=
+ "VARIATION" value(ObjectName)
+ SyntaxPart
+ WriteSyntaxPart
+ AccessPart
+ CreationPart
+ DefValPart
+ "DESCRIPTION" Text
+
+ -- must be a refinement for object's SYNTAX clause
+ SyntaxPart ::= "SYNTAX" Syntax
+ | empty
+
+ WriteSyntaxPart ::= "WRITE-SYNTAX" Syntax
+ | empty
+
+ Syntax ::= -- Must be one of the following:
+ -- a base type (or its refinement),
+ -- a textual convention (or its refinement), or
+ -- a BITS pseudo-type
+
+ type
+ | "BITS" "{" NamedBits "}"
+
+ NamedBits ::= NamedBit
+ | NamedBits "," NamedBit
+
+ NamedBit ::= identifier "(" number ")" -- number is nonnegative
+
+ AccessPart ::=
+ "ACCESS" Access
+ | empty
+
+ Access ::=
+ "not-implemented"
+ -- only "not-implemented" for notifications
+ | "accessible-for-notify"
+ | "read-only"
+ | "read-write"
+ | "read-create"
+ -- following is for backward-compatibility only
+ | "write-only"
+
+ CreationPart ::=
+ "CREATION-REQUIRES" "{" Cells "}"
+ | empty
+ Cells ::=
+ Cell
+ | Cells "," Cell
+ Cell ::=
+ value(ObjectName)
+
+ DefValPart ::= "DEFVAL" "{" Defvalue "}"
+ | empty
+
+ Defvalue ::= -- must be valid for the object's syntax
+ -- in this macro's SYNTAX clause, if present,
+ -- or if not, in object's OBJECT-TYPE macro
+ value(ObjectSyntax)
+ | "{" BitsValue "}"
+
+ BitsValue ::= BitNames
+ | empty
+
+ BitNames ::= BitName
+ | BitNames "," BitName
+
+ BitName ::= identifier
+
+ -- a character string as defined in [2]
+ Text ::= value(IA5String)
+END
+
+END
diff --git a/ldap/servers/snmp/SNMPv2-SMI.txt b/ldap/servers/snmp/SNMPv2-SMI.txt
new file mode 100644
index 00000000..1c01e1df
--- /dev/null
+++ b/ldap/servers/snmp/SNMPv2-SMI.txt
@@ -0,0 +1,344 @@
+SNMPv2-SMI DEFINITIONS ::= BEGIN
+
+-- the path to the root
+
+org OBJECT IDENTIFIER ::= { iso 3 } -- "iso" = 1
+dod OBJECT IDENTIFIER ::= { org 6 }
+internet OBJECT IDENTIFIER ::= { dod 1 }
+
+directory OBJECT IDENTIFIER ::= { internet 1 }
+
+mgmt OBJECT IDENTIFIER ::= { internet 2 }
+mib-2 OBJECT IDENTIFIER ::= { mgmt 1 }
+transmission OBJECT IDENTIFIER ::= { mib-2 10 }
+
+experimental OBJECT IDENTIFIER ::= { internet 3 }
+
+private OBJECT IDENTIFIER ::= { internet 4 }
+enterprises OBJECT IDENTIFIER ::= { private 1 }
+
+security OBJECT IDENTIFIER ::= { internet 5 }
+
+snmpV2 OBJECT IDENTIFIER ::= { internet 6 }
+
+-- transport domains
+snmpDomains OBJECT IDENTIFIER ::= { snmpV2 1 }
+
+-- transport proxies
+snmpProxys OBJECT IDENTIFIER ::= { snmpV2 2 }
+
+-- module identities
+snmpModules OBJECT IDENTIFIER ::= { snmpV2 3 }
+
+-- Extended UTCTime, to allow dates with four-digit years
+-- (Note that this definition of ExtUTCTime is not to be IMPORTed
+-- by MIB modules.)
+ExtUTCTime ::= OCTET STRING(SIZE(11 | 13))
+ -- format is YYMMDDHHMMZ or YYYYMMDDHHMMZ
+
+ -- where: YY - last two digits of year (only years
+ -- between 1900-1999)
+ -- YYYY - last four digits of the year (any year)
+ -- MM - month (01 through 12)
+ -- DD - day of month (01 through 31)
+ -- HH - hours (00 through 23)
+ -- MM - minutes (00 through 59)
+ -- Z - denotes GMT (the ASCII character Z)
+ --
+ -- For example, "9502192015Z" and "199502192015Z" represent
+ -- 8:15pm GMT on 19 February 1995. Years after 1999 must use
+ -- the four digit year format. Years 1900-1999 may use the
+ -- two or four digit format.
+
+-- definitions for information modules
+
+MODULE-IDENTITY MACRO ::=
+BEGIN
+ TYPE NOTATION ::=
+ "LAST-UPDATED" value(Update ExtUTCTime)
+ "ORGANIZATION" Text
+ "CONTACT-INFO" Text
+ "DESCRIPTION" Text
+ RevisionPart
+
+ VALUE NOTATION ::=
+ value(VALUE OBJECT IDENTIFIER)
+
+ RevisionPart ::=
+ Revisions
+ | empty
+ Revisions ::=
+ Revision
+ | Revisions Revision
+ Revision ::=
+ "REVISION" value(Update ExtUTCTime)
+ "DESCRIPTION" Text
+
+ -- a character string as defined in section 3.1.1
+ Text ::= value(IA5String)
+END
+
+OBJECT-IDENTITY MACRO ::=
+BEGIN
+ TYPE NOTATION ::=
+ "STATUS" Status
+ "DESCRIPTION" Text
+
+ ReferPart
+
+ VALUE NOTATION ::=
+ value(VALUE OBJECT IDENTIFIER)
+
+ Status ::=
+ "current"
+ | "deprecated"
+ | "obsolete"
+
+ ReferPart ::=
+ "REFERENCE" Text
+ | empty
+
+ -- a character string as defined in section 3.1.1
+ Text ::= value(IA5String)
+END
+
+-- names of objects
+-- (Note that these definitions of ObjectName and NotificationName
+-- are not to be IMPORTed by MIB modules.)
+
+ObjectName ::=
+ OBJECT IDENTIFIER
+
+NotificationName ::=
+ OBJECT IDENTIFIER
+
+-- syntax of objects
+
+-- the "base types" defined here are:
+-- 3 built-in ASN.1 types: INTEGER, OCTET STRING, OBJECT IDENTIFIER
+-- 8 application-defined types: Integer32, IpAddress, Counter32,
+-- Gauge32, Unsigned32, TimeTicks, Opaque, and Counter64
+
+ObjectSyntax ::=
+ CHOICE {
+ simple
+ SimpleSyntax,
+ -- note that SEQUENCEs for conceptual tables and
+ -- rows are not mentioned here...
+
+ application-wide
+ ApplicationSyntax
+ }
+
+-- built-in ASN.1 types
+
+SimpleSyntax ::=
+ CHOICE {
+ -- INTEGERs with a more restrictive range
+ -- may also be used
+ integer-value -- includes Integer32
+ INTEGER (-2147483648..2147483647),
+ -- OCTET STRINGs with a more restrictive size
+ -- may also be used
+ string-value
+ OCTET STRING (SIZE (0..65535)),
+ objectID-value
+ OBJECT IDENTIFIER
+ }
+
+-- indistinguishable from INTEGER, but never needs more than
+-- 32-bits for a two's complement representation
+Integer32 ::=
+ INTEGER (-2147483648..2147483647)
+
+-- application-wide types
+
+ApplicationSyntax ::=
+ CHOICE {
+ ipAddress-value
+ IpAddress,
+ counter-value
+ Counter32,
+ timeticks-value
+ TimeTicks,
+ arbitrary-value
+ Opaque,
+ big-counter-value
+ Counter64,
+ unsigned-integer-value -- includes Gauge32
+ Unsigned32
+ }
+
+-- in network-byte order
+
+-- (this is a tagged type for historical reasons)
+IpAddress ::=
+ [APPLICATION 0]
+ IMPLICIT OCTET STRING (SIZE (4))
+
+-- this wraps
+Counter32 ::=
+ [APPLICATION 1]
+ IMPLICIT INTEGER (0..4294967295)
+
+-- this doesn't wrap
+Gauge32 ::=
+ [APPLICATION 2]
+ IMPLICIT INTEGER (0..4294967295)
+
+-- an unsigned 32-bit quantity
+-- indistinguishable from Gauge32
+Unsigned32 ::=
+ [APPLICATION 2]
+ IMPLICIT INTEGER (0..4294967295)
+
+-- hundredths of seconds since an epoch
+TimeTicks ::=
+ [APPLICATION 3]
+ IMPLICIT INTEGER (0..4294967295)
+
+-- for backward-compatibility only
+Opaque ::=
+ [APPLICATION 4]
+ IMPLICIT OCTET STRING
+
+-- for counters that wrap in less than one hour with only 32 bits
+Counter64 ::=
+ [APPLICATION 6]
+ IMPLICIT INTEGER (0..18446744073709551615)
+
+-- definition for objects
+
+OBJECT-TYPE MACRO ::=
+BEGIN
+ TYPE NOTATION ::=
+ "SYNTAX" Syntax
+ UnitsPart
+ "MAX-ACCESS" Access
+ "STATUS" Status
+ "DESCRIPTION" Text
+ ReferPart
+
+ IndexPart
+ DefValPart
+
+ VALUE NOTATION ::=
+ value(VALUE ObjectName)
+
+ Syntax ::= -- Must be one of the following:
+ -- a base type (or its refinement),
+ -- a textual convention (or its refinement), or
+ -- a BITS pseudo-type
+ type
+ | "BITS" "{" NamedBits "}"
+
+ NamedBits ::= NamedBit
+ | NamedBits "," NamedBit
+
+ NamedBit ::= identifier "(" number ")" -- number is nonnegative
+
+ UnitsPart ::=
+ "UNITS" Text
+ | empty
+
+ Access ::=
+ "not-accessible"
+ | "accessible-for-notify"
+ | "read-only"
+ | "read-write"
+ | "read-create"
+
+ Status ::=
+ "current"
+ | "deprecated"
+ | "obsolete"
+
+ ReferPart ::=
+ "REFERENCE" Text
+ | empty
+
+ IndexPart ::=
+ "INDEX" "{" IndexTypes "}"
+ | "AUGMENTS" "{" Entry "}"
+ | empty
+ IndexTypes ::=
+ IndexType
+ | IndexTypes "," IndexType
+ IndexType ::=
+ "IMPLIED" Index
+ | Index
+
+ Index ::=
+ -- use the SYNTAX value of the
+ -- correspondent OBJECT-TYPE invocation
+ value(ObjectName)
+ Entry ::=
+ -- use the INDEX value of the
+ -- correspondent OBJECT-TYPE invocation
+ value(ObjectName)
+
+ DefValPart ::= "DEFVAL" "{" Defvalue "}"
+ | empty
+
+ Defvalue ::= -- must be valid for the type specified in
+ -- SYNTAX clause of same OBJECT-TYPE macro
+ value(ObjectSyntax)
+ | "{" BitsValue "}"
+
+ BitsValue ::= BitNames
+ | empty
+
+ BitNames ::= BitName
+ | BitNames "," BitName
+
+ BitName ::= identifier
+
+ -- a character string as defined in section 3.1.1
+ Text ::= value(IA5String)
+END
+
+-- definitions for notifications
+
+NOTIFICATION-TYPE MACRO ::=
+BEGIN
+ TYPE NOTATION ::=
+ ObjectsPart
+ "STATUS" Status
+ "DESCRIPTION" Text
+ ReferPart
+
+ VALUE NOTATION ::=
+ value(VALUE NotificationName)
+
+ ObjectsPart ::=
+ "OBJECTS" "{" Objects "}"
+ | empty
+ Objects ::=
+ Object
+
+ | Objects "," Object
+ Object ::=
+ value(ObjectName)
+
+ Status ::=
+ "current"
+ | "deprecated"
+ | "obsolete"
+
+ ReferPart ::=
+ "REFERENCE" Text
+ | empty
+
+ -- a character string as defined in section 3.1.1
+ Text ::= value(IA5String)
+END
+
+-- definitions of administrative identifiers
+
+zeroDotZero OBJECT-IDENTITY
+ STATUS current
+ DESCRIPTION
+ "A value used for null identifiers."
+ ::= { 0 0 }
+
+END
diff --git a/ldap/servers/snmp/SNMPv2-TC.txt b/ldap/servers/snmp/SNMPv2-TC.txt
new file mode 100644
index 00000000..860bf71e
--- /dev/null
+++ b/ldap/servers/snmp/SNMPv2-TC.txt
@@ -0,0 +1,772 @@
+SNMPv2-TC DEFINITIONS ::= BEGIN
+
+IMPORTS
+ TimeTicks FROM SNMPv2-SMI;
+
+-- definition of textual conventions
+
+TEXTUAL-CONVENTION MACRO ::=
+
+BEGIN
+ TYPE NOTATION ::=
+ DisplayPart
+ "STATUS" Status
+ "DESCRIPTION" Text
+ ReferPart
+ "SYNTAX" Syntax
+
+ VALUE NOTATION ::=
+ value(VALUE Syntax) -- adapted ASN.1
+
+ DisplayPart ::=
+ "DISPLAY-HINT" Text
+ | empty
+
+ Status ::=
+ "current"
+ | "deprecated"
+ | "obsolete"
+
+ ReferPart ::=
+ "REFERENCE" Text
+ | empty
+
+ -- a character string as defined in [2]
+ Text ::= value(IA5String)
+
+ Syntax ::= -- Must be one of the following:
+ -- a base type (or its refinement), or
+ -- a BITS pseudo-type
+ type
+ | "BITS" "{" NamedBits "}"
+
+ NamedBits ::= NamedBit
+ | NamedBits "," NamedBit
+
+ NamedBit ::= identifier "(" number ")" -- number is nonnegative
+
+END
+
+DisplayString ::= TEXTUAL-CONVENTION
+ DISPLAY-HINT "255a"
+ STATUS current
+ DESCRIPTION
+ "Represents textual information taken from the NVT ASCII
+
+ character set, as defined in pages 4, 10-11 of RFC 854.
+
+ To summarize RFC 854, the NVT ASCII repertoire specifies:
+
+ - the use of character codes 0-127 (decimal)
+
+ - the graphics characters (32-126) are interpreted as
+ US ASCII
+
+ - NUL, LF, CR, BEL, BS, HT, VT and FF have the special
+ meanings specified in RFC 854
+
+ - the other 25 codes have no standard interpretation
+
+ - the sequence 'CR LF' means newline
+
+ - the sequence 'CR NUL' means carriage-return
+
+ - an 'LF' not preceded by a 'CR' means moving to the
+ same column on the next line.
+
+ - the sequence 'CR x' for any x other than LF or NUL is
+ illegal. (Note that this also means that a string may
+ end with either 'CR LF' or 'CR NUL', but not with CR.)
+
+ Any object defined using this syntax may not exceed 255
+ characters in length."
+ SYNTAX OCTET STRING (SIZE (0..255))
+
+PhysAddress ::= TEXTUAL-CONVENTION
+ DISPLAY-HINT "1x:"
+ STATUS current
+ DESCRIPTION
+ "Represents media- or physical-level addresses."
+ SYNTAX OCTET STRING
+
+MacAddress ::= TEXTUAL-CONVENTION
+ DISPLAY-HINT "1x:"
+ STATUS current
+ DESCRIPTION
+ "Represents an 802 MAC address represented in the
+ `canonical' order defined by IEEE 802.1a, i.e., as if it
+ were transmitted least significant bit first, even though
+ 802.5 (in contrast to other 802.x protocols) requires MAC
+ addresses to be transmitted most significant bit first."
+ SYNTAX OCTET STRING (SIZE (6))
+
+TruthValue ::= TEXTUAL-CONVENTION
+ STATUS current
+ DESCRIPTION
+ "Represents a boolean value."
+ SYNTAX INTEGER { true(1), false(2) }
+
+TestAndIncr ::= TEXTUAL-CONVENTION
+ STATUS current
+ DESCRIPTION
+ "Represents integer-valued information used for atomic
+ operations. When the management protocol is used to specify
+ that an object instance having this syntax is to be
+ modified, the new value supplied via the management protocol
+ must precisely match the value presently held by the
+ instance. If not, the management protocol set operation
+ fails with an error of `inconsistentValue'. Otherwise, if
+ the current value is the maximum value of 2^31-1 (2147483647
+ decimal), then the value held by the instance is wrapped to
+ zero; otherwise, the value held by the instance is
+ incremented by one. (Note that regardless of whether the
+ management protocol set operation succeeds, the variable-
+ binding in the request and response PDUs are identical.)
+
+ The value of the ACCESS clause for objects having this
+ syntax is either `read-write' or `read-create'. When an
+ instance of a columnar object having this syntax is created,
+ any value may be supplied via the management protocol.
+
+ When the network management portion of the system is re-
+ initialized, the value of every object instance having this
+ syntax must either be incremented from its value prior to
+ the re-initialization, or (if the value prior to the re-
+ initialization is unknown) be set to a pseudo-randomly
+ generated value."
+ SYNTAX INTEGER (0..2147483647)
+
+AutonomousType ::= TEXTUAL-CONVENTION
+ STATUS current
+ DESCRIPTION
+ "Represents an independently extensible type identification
+ value. It may, for example, indicate a particular sub-tree
+ with further MIB definitions, or define a particular type of
+ protocol or hardware."
+ SYNTAX OBJECT IDENTIFIER
+
+InstancePointer ::= TEXTUAL-CONVENTION
+ STATUS obsolete
+ DESCRIPTION
+ "A pointer to either a specific instance of a MIB object or
+ a conceptual row of a MIB table in the managed device. In
+ the latter case, by convention, it is the name of the
+ particular instance of the first accessible columnar object
+ in the conceptual row.
+
+ The two uses of this textual convention are replaced by
+ VariablePointer and RowPointer, respectively."
+ SYNTAX OBJECT IDENTIFIER
+
+VariablePointer ::= TEXTUAL-CONVENTION
+ STATUS current
+ DESCRIPTION
+ "A pointer to a specific object instance. For example,
+ sysContact.0 or ifInOctets.3."
+ SYNTAX OBJECT IDENTIFIER
+
+RowPointer ::= TEXTUAL-CONVENTION
+ STATUS current
+ DESCRIPTION
+ "Represents a pointer to a conceptual row. The value is the
+ name of the instance of the first accessible columnar object
+ in the conceptual row.
+
+ For example, ifIndex.3 would point to the 3rd row in the
+ ifTable (note that if ifIndex were not-accessible, then
+ ifDescr.3 would be used instead)."
+ SYNTAX OBJECT IDENTIFIER
+
+RowStatus ::= TEXTUAL-CONVENTION
+ STATUS current
+ DESCRIPTION
+ "The RowStatus textual convention is used to manage the
+ creation and deletion of conceptual rows, and is used as the
+ value of the SYNTAX clause for the status column of a
+ conceptual row (as described in Section 7.7.1 of [2].)
+
+ The status column has six defined values:
+
+ - `active', which indicates that the conceptual row is
+ available for use by the managed device;
+
+ - `notInService', which indicates that the conceptual
+ row exists in the agent, but is unavailable for use by
+ the managed device (see NOTE below); 'notInService' has
+ no implication regarding the internal consistency of
+ the row, availability of resources, or consistency with
+ the current state of the managed device;
+
+ - `notReady', which indicates that the conceptual row
+ exists in the agent, but is missing information
+ necessary in order to be available for use by the
+ managed device (i.e., one or more required columns in
+ the conceptual row have not been instanciated);
+
+ - `createAndGo', which is supplied by a management
+ station wishing to create a new instance of a
+ conceptual row and to have its status automatically set
+ to active, making it available for use by the managed
+ device;
+
+ - `createAndWait', which is supplied by a management
+ station wishing to create a new instance of a
+ conceptual row (but not make it available for use by
+ the managed device); and,
+ - `destroy', which is supplied by a management station
+ wishing to delete all of the instances associated with
+ an existing conceptual row.
+
+ Whereas five of the six values (all except `notReady') may
+ be specified in a management protocol set operation, only
+ three values will be returned in response to a management
+ protocol retrieval operation: `notReady', `notInService' or
+ `active'. That is, when queried, an existing conceptual row
+ has only three states: it is either available for use by
+ the managed device (the status column has value `active');
+ it is not available for use by the managed device, though
+ the agent has sufficient information to attempt to make it
+ so (the status column has value `notInService'); or, it is
+ not available for use by the managed device, and an attempt
+ to make it so would fail because the agent has insufficient
+ information (the state column has value `notReady').
+
+ NOTE WELL
+
+ This textual convention may be used for a MIB table,
+ irrespective of whether the values of that table's
+ conceptual rows are able to be modified while it is
+ active, or whether its conceptual rows must be taken
+ out of service in order to be modified. That is, it is
+ the responsibility of the DESCRIPTION clause of the
+ status column to specify whether the status column must
+ not be `active' in order for the value of some other
+ column of the same conceptual row to be modified. If
+ such a specification is made, affected columns may be
+ changed by an SNMP set PDU if the RowStatus would not
+ be equal to `active' either immediately before or after
+ processing the PDU. In other words, if the PDU also
+ contained a varbind that would change the RowStatus
+ value, the column in question may be changed if the
+ RowStatus was not equal to `active' as the PDU was
+ received, or if the varbind sets the status to a value
+ other than 'active'.
+
+ Also note that whenever any elements of a row exist, the
+ RowStatus column must also exist.
+
+ To summarize the effect of having a conceptual row with a
+ status column having a SYNTAX clause value of RowStatus,
+ consider the following state diagram:
+
+ STATE
+ +--------------+-----------+-------------+-------------
+ | A | B | C | D
+ | |status col.|status column|
+ |status column | is | is |status column
+ ACTION |does not exist| notReady | notInService| is active
+--------------+--------------+-----------+-------------+-------------
+set status |noError ->D|inconsist- |inconsistent-|inconsistent-
+column to | or | entValue| Value| Value
+createAndGo |inconsistent- | | |
+ | Value| | |
+--------------+--------------+-----------+-------------+-------------
+set status |noError see 1|inconsist- |inconsistent-|inconsistent-
+column to | or | entValue| Value| Value
+createAndWait |wrongValue | | |
+--------------+--------------+-----------+-------------+-------------
+set status |inconsistent- |inconsist- |noError |noError
+column to | Value| entValue| |
+active | | | |
+ | | or | |
+ | | | |
+ | |see 2 ->D|see 8 ->D| ->D
+--------------+--------------+-----------+-------------+-------------
+set status |inconsistent- |inconsist- |noError |noError ->C
+column to | Value| entValue| |
+notInService | | | |
+ | | or | | or
+ | | | |
+ | |see 3 ->C| ->C|see 6
+--------------+--------------+-----------+-------------+-------------
+set status |noError |noError |noError |noError ->A
+column to | | | | or
+destroy | ->A| ->A| ->A|see 7
+--------------+--------------+-----------+-------------+-------------
+set any other |see 4 |noError |noError |see 5
+column to some| | | |
+value | | see 1| ->C| ->D
+--------------+--------------+-----------+-------------+-------------
+
+ (1) goto B or C, depending on information available to the
+ agent.
+
+ (2) if other variable bindings included in the same PDU,
+ provide values for all columns which are missing but
+ required, and all columns have acceptable values, then
+ return noError and goto D.
+
+ (3) if other variable bindings included in the same PDU,
+ provide legal values for all columns which are missing but
+ required, then return noError and goto C.
+
+ (4) at the discretion of the agent, the return value may be
+ either:
+
+ inconsistentName: because the agent does not choose to
+ create such an instance when the corresponding
+ RowStatus instance does not exist, or
+
+ inconsistentValue: if the supplied value is
+ inconsistent with the state of some other MIB object's
+ value, or
+
+ noError: because the agent chooses to create the
+ instance.
+
+ If noError is returned, then the instance of the status
+ column must also be created, and the new state is B or C,
+ depending on the information available to the agent. If
+ inconsistentName or inconsistentValue is returned, the row
+ remains in state A.
+
+ (5) depending on the MIB definition for the column/table,
+ either noError or inconsistentValue may be returned.
+
+ (6) the return value can indicate one of the following
+ errors:
+
+ wrongValue: because the agent does not support
+ notInService (e.g., an agent which does not support
+ createAndWait), or
+
+ inconsistentValue: because the agent is unable to take
+ the row out of service at this time, perhaps because it
+ is in use and cannot be de-activated.
+
+ (7) the return value can indicate the following error:
+
+ inconsistentValue: because the agent is unable to
+ remove the row at this time, perhaps because it is in
+ use and cannot be de-activated.
+
+ (8) the transition to D can fail, e.g., if the values of the
+ conceptual row are inconsistent, then the error code would
+ be inconsistentValue.
+
+ NOTE: Other processing of (this and other varbinds of) the
+ set request may result in a response other than noError
+ being returned, e.g., wrongValue, noCreation, etc.
+
+ Conceptual Row Creation
+
+ There are four potential interactions when creating a
+ conceptual row: selecting an instance-identifier which is
+ not in use; creating the conceptual row; initializing any
+ objects for which the agent does not supply a default; and,
+ making the conceptual row available for use by the managed
+ device.
+
+ Interaction 1: Selecting an Instance-Identifier
+
+ The algorithm used to select an instance-identifier varies
+ for each conceptual row. In some cases, the instance-
+ identifier is semantically significant, e.g., the
+ destination address of a route, and a management station
+ selects the instance-identifier according to the semantics.
+
+ In other cases, the instance-identifier is used solely to
+ distinguish conceptual rows, and a management station
+ without specific knowledge of the conceptual row might
+ examine the instances present in order to determine an
+ unused instance-identifier. (This approach may be used, but
+ it is often highly sub-optimal; however, it is also a
+ questionable practice for a naive management station to
+ attempt conceptual row creation.)
+
+ Alternately, the MIB module which defines the conceptual row
+ might provide one or more objects which provide assistance
+ in determining an unused instance-identifier. For example,
+ if the conceptual row is indexed by an integer-value, then
+ an object having an integer-valued SYNTAX clause might be
+ defined for such a purpose, allowing a management station to
+ issue a management protocol retrieval operation. In order
+ to avoid unnecessary collisions between competing management
+ stations, `adjacent' retrievals of this object should be
+ different.
+
+ Finally, the management station could select a pseudo-random
+ number to use as the index. In the event that this index
+
+ was already in use and an inconsistentValue was returned in
+ response to the management protocol set operation, the
+ management station should simply select a new pseudo-random
+ number and retry the operation.
+
+ A MIB designer should choose between the two latter
+ algorithms based on the size of the table (and therefore the
+ efficiency of each algorithm). For tables in which a large
+ number of entries are expected, it is recommended that a MIB
+ object be defined that returns an acceptable index for
+ creation. For tables with small numbers of entries, it is
+ recommended that the latter pseudo-random index mechanism be
+ used.
+
+ Interaction 2: Creating the Conceptual Row
+
+ Once an unused instance-identifier has been selected, the
+ management station determines if it wishes to create and
+ activate the conceptual row in one transaction or in a
+ negotiated set of interactions.
+
+ Interaction 2a: Creating and Activating the Conceptual Row
+
+ The management station must first determine the column
+ requirements, i.e., it must determine those columns for
+ which it must or must not provide values. Depending on the
+ complexity of the table and the management station's
+ knowledge of the agent's capabilities, this determination
+ can be made locally by the management station. Alternately,
+ the management station issues a management protocol get
+ operation to examine all columns in the conceptual row that
+ it wishes to create. In response, for each column, there
+ are three possible outcomes:
+
+ - a value is returned, indicating that some other
+ management station has already created this conceptual
+ row. We return to interaction 1.
+
+ - the exception `noSuchInstance' is returned,
+ indicating that the agent implements the object-type
+ associated with this column, and that this column in at
+ least one conceptual row would be accessible in the MIB
+ view used by the retrieval were it to exist. For those
+ columns to which the agent provides read-create access,
+ the `noSuchInstance' exception tells the management
+ station that it should supply a value for this column
+ when the conceptual row is to be created.
+
+ - the exception `noSuchObject' is returned, indicating
+ that the agent does not implement the object-type
+ associated with this column or that there is no
+ conceptual row for which this column would be
+ accessible in the MIB view used by the retrieval. As
+ such, the management station can not issue any
+ management protocol set operations to create an
+ instance of this column.
+
+ Once the column requirements have been determined, a
+ management protocol set operation is accordingly issued.
+ This operation also sets the new instance of the status
+ column to `createAndGo'.
+
+ When the agent processes the set operation, it verifies that
+ it has sufficient information to make the conceptual row
+ available for use by the managed device. The information
+ available to the agent is provided by two sources: the
+ management protocol set operation which creates the
+ conceptual row, and, implementation-specific defaults
+ supplied by the agent (note that an agent must provide
+ implementation-specific defaults for at least those objects
+ which it implements as read-only). If there is sufficient
+ information available, then the conceptual row is created, a
+ `noError' response is returned, the status column is set to
+ `active', and no further interactions are necessary (i.e.,
+ interactions 3 and 4 are skipped). If there is insufficient
+ information, then the conceptual row is not created, and the
+ set operation fails with an error of `inconsistentValue'.
+ On this error, the management station can issue a management
+ protocol retrieval operation to determine if this was
+ because it failed to specify a value for a required column,
+ or, because the selected instance of the status column
+ already existed. In the latter case, we return to
+ interaction 1. In the former case, the management station
+ can re-issue the set operation with the additional
+ information, or begin interaction 2 again using
+ `createAndWait' in order to negotiate creation of the
+ conceptual row.
+
+ NOTE WELL
+
+ Regardless of the method used to determine the column
+ requirements, it is possible that the management
+ station might deem a column necessary when, in fact,
+ the agent will not allow that particular columnar
+ instance to be created or written. In this case, the
+ management protocol set operation will fail with an
+ error such as `noCreation' or `notWritable'. In this
+ case, the management station decides whether it needs
+ to be able to set a value for that particular columnar
+ instance. If not, the management station re-issues the
+ management protocol set operation, but without setting
+ a value for that particular columnar instance;
+ otherwise, the management station aborts the row
+ creation algorithm.
+
+ Interaction 2b: Negotiating the Creation of the Conceptual
+ Row
+
+ The management station issues a management protocol set
+ operation which sets the desired instance of the status
+ column to `createAndWait'. If the agent is unwilling to
+ process a request of this sort, the set operation fails with
+ an error of `wrongValue'. (As a consequence, such an agent
+ must be prepared to accept a single management protocol set
+ operation, i.e., interaction 2a above, containing all of the
+ columns indicated by its column requirements.) Otherwise,
+ the conceptual row is created, a `noError' response is
+ returned, and the status column is immediately set to either
+ `notInService' or `notReady', depending on whether it has
+ sufficient information to (attempt to) make the conceptual
+ row available for use by the managed device. If there is
+ sufficient information available, then the status column is
+ set to `notInService'; otherwise, if there is insufficient
+ information, then the status column is set to `notReady'.
+ Regardless, we proceed to interaction 3.
+
+ Interaction 3: Initializing non-defaulted Objects
+
+ The management station must now determine the column
+ requirements. It issues a management protocol get operation
+ to examine all columns in the created conceptual row. In
+ the response, for each column, there are three possible
+ outcomes:
+
+ - a value is returned, indicating that the agent
+ implements the object-type associated with this column
+ and had sufficient information to provide a value. For
+ those columns to which the agent provides read-create
+ access (and for which the agent allows their values to
+ be changed after their creation), a value return tells
+ the management station that it may issue additional
+ management protocol set operations, if it desires, in
+ order to change the value associated with this column.
+
+ - the exception `noSuchInstance' is returned,
+ indicating that the agent implements the object-type
+ associated with this column, and that this column in at
+ least one conceptual row would be accessible in the MIB
+ view used by the retrieval were it to exist. However,
+ the agent does not have sufficient information to
+ provide a value, and until a value is provided, the
+ conceptual row may not be made available for use by the
+ managed device. For those columns to which the agent
+ provides read-create access, the `noSuchInstance'
+ exception tells the management station that it must
+ issue additional management protocol set operations, in
+ order to provide a value associated with this column.
+
+ - the exception `noSuchObject' is returned, indicating
+ that the agent does not implement the object-type
+ associated with this column or that there is no
+ conceptual row for which this column would be
+ accessible in the MIB view used by the retrieval. As
+ such, the management station can not issue any
+ management protocol set operations to create an
+ instance of this column.
+
+ If the value associated with the status column is
+ `notReady', then the management station must first deal with
+ all `noSuchInstance' columns, if any. Having done so, the
+ value of the status column becomes `notInService', and we
+ proceed to interaction 4.
+
+ Interaction 4: Making the Conceptual Row Available
+
+ Once the management station is satisfied with the values
+ associated with the columns of the conceptual row, it issues
+ a management protocol set operation to set the status column
+ to `active'. If the agent has sufficient information to
+ make the conceptual row available for use by the managed
+ device, the management protocol set operation succeeds (a
+ `noError' response is returned). Otherwise, the management
+ protocol set operation fails with an error of
+ `inconsistentValue'.
+
+ NOTE WELL
+
+ A conceptual row having a status column with value
+ `notInService' or `notReady' is unavailable to the
+ managed device. As such, it is possible for the
+ managed device to create its own instances during the
+ time between the management protocol set operation
+ which sets the status column to `createAndWait' and the
+ management protocol set operation which sets the status
+ column to `active'. In this case, when the management
+ protocol set operation is issued to set the status
+ column to `active', the values held in the agent
+ supersede those used by the managed device.
+
+ If the management station is prevented from setting the
+ status column to `active' (e.g., due to management station
+ or network failure) the conceptual row will be left in the
+ `notInService' or `notReady' state, consuming resources
+ indefinitely. The agent must detect conceptual rows that
+ have been in either state for an abnormally long period of
+ time and remove them. It is the responsibility of the
+ DESCRIPTION clause of the status column to indicate what an
+ abnormally long period of time would be. This period of
+ time should be long enough to allow for human response time
+ (including `think time') between the creation of the
+ conceptual row and the setting of the status to `active'.
+ In the absence of such information in the DESCRIPTION
+ clause, it is suggested that this period be approximately 5
+ minutes in length. This removal action applies not only to
+ newly-created rows, but also to previously active rows which
+ are set to, and left in, the notInService state for a
+ prolonged period exceeding that which is considered normal
+ for such a conceptual row.
+
+ Conceptual Row Suspension
+
+ When a conceptual row is `active', the management station
+ may issue a management protocol set operation which sets the
+ instance of the status column to `notInService'. If the
+ agent is unwilling to do so, the set operation fails with an
+ error of `wrongValue' or `inconsistentValue'. Otherwise,
+ the conceptual row is taken out of service, and a `noError'
+ response is returned. It is the responsibility of the
+ DESCRIPTION clause of the status column to indicate under
+ what circumstances the status column should be taken out of
+ service (e.g., in order for the value of some other column
+ of the same conceptual row to be modified).
+
+ Conceptual Row Deletion
+
+ For deletion of conceptual rows, a management protocol set
+ operation is issued which sets the instance of the status
+ column to `destroy'. This request may be made regardless of
+ the current value of the status column (e.g., it is possible
+ to delete conceptual rows which are either `notReady',
+ `notInService' or `active'.) If the operation succeeds,
+ then all instances associated with the conceptual row are
+ immediately removed."
+ SYNTAX INTEGER {
+ -- the following two values are states:
+ -- these values may be read or written
+ active(1),
+ notInService(2),
+ -- the following value is a state:
+ -- this value may be read, but not written
+ notReady(3),
+ -- the following three values are
+ -- actions: these values may be written,
+ -- but are never read
+ createAndGo(4),
+ createAndWait(5),
+ destroy(6)
+ }
+
+TimeStamp ::= TEXTUAL-CONVENTION
+ STATUS current
+ DESCRIPTION
+ "The value of the sysUpTime object at which a specific
+ occurrence happened. The specific occurrence must be
+
+ defined in the description of any object defined using this
+ type.
+
+ If sysUpTime is reset to zero as a result of a re-
+ initialization of the network management (sub)system, then
+ the values of all TimeStamp objects are also reset.
+ However, after approximately 497 days without a re-
+ initialization, the sysUpTime object will reach 2^^32-1 and
+ then increment around to zero; in this case, existing values
+ of TimeStamp objects do not change. This can lead to
+ ambiguities in the value of TimeStamp objects."
+ SYNTAX TimeTicks
+
+TimeInterval ::= TEXTUAL-CONVENTION
+ STATUS current
+ DESCRIPTION
+ "A period of time, measured in units of 0.01 seconds."
+ SYNTAX INTEGER (0..2147483647)
+
+DateAndTime ::= TEXTUAL-CONVENTION
+ DISPLAY-HINT "2d-1d-1d,1d:1d:1d.1d,1a1d:1d"
+ STATUS current
+ DESCRIPTION
+ "A date-time specification.
+
+ field octets contents range
+ ----- ------ -------- -----
+ 1 1-2 year* 0..65536
+ 2 3 month 1..12
+ 3 4 day 1..31
+ 4 5 hour 0..23
+ 5 6 minutes 0..59
+ 6 7 seconds 0..60
+ (use 60 for leap-second)
+ 7 8 deci-seconds 0..9
+ 8 9 direction from UTC '+' / '-'
+ 9 10 hours from UTC* 0..13
+ 10 11 minutes from UTC 0..59
+
+ * Notes:
+ - the value of year is in network-byte order
+ - daylight saving time in New Zealand is +13
+
+ For example, Tuesday May 26, 1992 at 1:30:15 PM EDT would be
+ displayed as:
+
+ 1992-5-26,13:30:15.0,-4:0
+
+ Note that if only local time is known, then timezone
+ information (fields 8-10) is not present."
+ SYNTAX OCTET STRING (SIZE (8 | 11))
+
+StorageType ::= TEXTUAL-CONVENTION
+ STATUS current
+ DESCRIPTION
+ "Describes the memory realization of a conceptual row. A
+ row which is volatile(2) is lost upon reboot. A row which
+ is either nonVolatile(3), permanent(4) or readOnly(5), is
+ backed up by stable storage. A row which is permanent(4)
+ can be changed but not deleted. A row which is readOnly(5)
+ cannot be changed nor deleted.
+
+ If the value of an object with this syntax is either
+ permanent(4) or readOnly(5), it cannot be written.
+ Conversely, if the value is either other(1), volatile(2) or
+ nonVolatile(3), it cannot be modified to be permanent(4) or
+ readOnly(5). (All illegal modifications result in a
+ 'wrongValue' error.)
+
+ Every usage of this textual convention is required to
+ specify the columnar objects which a permanent(4) row must
+ at a minimum allow to be writable."
+ SYNTAX INTEGER {
+ other(1), -- eh?
+ volatile(2), -- e.g., in RAM
+ nonVolatile(3), -- e.g., in NVRAM
+ permanent(4), -- e.g., partially in ROM
+ readOnly(5) -- e.g., completely in ROM
+ }
+
+TDomain ::= TEXTUAL-CONVENTION
+ STATUS current
+ DESCRIPTION
+ "Denotes a kind of transport service.
+
+ Some possible values, such as snmpUDPDomain, are defined in
+ the SNMPv2-TM MIB module. Other possible values are defined
+ in other MIB modules."
+ REFERENCE "The SNMPv2-TM MIB module is defined in RFC 1906."
+ SYNTAX OBJECT IDENTIFIER
+
+TAddress ::= TEXTUAL-CONVENTION
+ STATUS current
+ DESCRIPTION
+ "Denotes a transport service address.
+
+ A TAddress value is always interpreted within the context of a
+ TDomain value. Thus, each definition of a TDomain value must
+ be accompanied by a definition of a textual convention for use
+ with that TDomain. Some possible textual conventions, such as
+ SnmpUDPAddress for snmpUDPDomain, are defined in the SNMPv2-TM
+ MIB module. Other possible textual conventions are defined in
+ other MIB modules."
+ REFERENCE "The SNMPv2-TM MIB module is defined in RFC 1906."
+ SYNTAX OCTET STRING (SIZE (1..255))
+
+END
diff --git a/ldap/servers/snmp/netscape-ldap.mib b/ldap/servers/snmp/netscape-ldap.mib
new file mode 100644
index 00000000..3e51839a
--- /dev/null
+++ b/ldap/servers/snmp/netscape-ldap.mib
@@ -0,0 +1,727 @@
+-- BEGIN COPYRIGHT BLOCK
+-- Copyright 2001 Sun Microsystems, Inc.
+-- Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+-- All rights reserved.
+-- END COPYRIGHT BLOCK
+--
+--
+-- MIB for Netscape Directory Server 7
+--
+-- This is an implementation of the MADMAN mib for monitoring LDAP/CLDAP and X.500
+-- directories described in RFC 2788 and 2789
+-- with the addition of traps for server up and down events
+
+NSLDAP-MIB DEFINITIONS ::= BEGIN
+
+IMPORTS
+ MODULE-IDENTITY, Counter32, Gauge32, OBJECT-TYPE
+ FROM SNMPv2-SMI
+ DisplayString, TimeStamp, TEXTUAL-CONVENTION
+ FROM SNMPv2-TC
+ MODULE-COMPLIANCE, OBJECT-GROUP
+ FROM SNMPv2-CONF
+ applIndex, DistinguishedName, URLString
+ FROM NETWORK-SERVICES-MIB
+ enterprises
+ FROM RFC1155-SMI
+ TRAP-TYPE
+ FROM RFC-1215;
+
+ netscape OBJECT IDENTIFIER ::= { enterprises 1450 }
+
+ URLString ::= TEXTUAL-CONVENTION
+ STATUS current
+ DESCRIPTION
+ "I couldn't find it but madman said it should be here, guessing DisplayString"
+ SYNTAX DisplayString
+
+ nsldap MODULE-IDENTITY
+ LAST-UPDATED "200207160000Z"
+ ORGANIZATION "Netscape Communications Corp"
+ CONTACT-INFO
+ " AOL Strategic Business Solutions
+ Postal: 22000 AOL Way
+ Dulles, VA 20166
+
+ Website: http://enterprise.netscape.com"
+ DESCRIPTION
+ " An implementation of the MADMAN mib for monitoring LDAP/CLDAP and X.500
+ directories described in RFC 2788 and 2789
+ used for Netscape Directory Server 7"
+ ::= { netscape 7}
+
+ dsOpsTable OBJECT-TYPE
+ SYNTAX SEQUENCE OF DsOpsEntry
+ MAX-ACCESS not-accessible
+ STATUS current
+ DESCRIPTION
+ " The table holding information related to the
+ DS operations."
+ ::= {nsldap 1}
+
+ dsOpsEntry OBJECT-TYPE
+ SYNTAX DsOpsEntry
+ MAX-ACCESS not-accessible
+ STATUS current
+ DESCRIPTION
+ " Entry containing operations related statistics
+ for a DS."
+ INDEX { applIndex}
+ ::= {dsOpsTable 1}
+
+ DsOpsEntry ::= SEQUENCE {
+
+ -- Bindings
+
+ dsAnonymousBinds
+ Counter32,
+ dsUnAuthBinds
+ Counter32,
+ dsSimpleAuthBinds
+ Counter32,
+ dsStrongAuthBinds
+ Counter32,
+ dsBindSecurityErrors
+ Counter32,
+
+ -- In-coming operations
+
+ dsInOps
+ Counter32,
+ dsReadOps
+ Counter32,
+ dsCompareOps
+ Counter32,
+ dsAddEntryOps
+ Counter32,
+ dsRemoveEntryOps
+ Counter32,
+ dsModifyEntryOps
+ Counter32,
+ dsModifyRDNOps
+ Counter32,
+ dsListOps
+ Counter32,
+ dsSearchOps
+ Counter32,
+ dsOneLevelSearchOps
+ Counter32,
+ dsWholeSubtreeSearchOps
+ Counter32,
+
+ -- Out going operations
+
+ dsReferrals
+ Counter32,
+ dsChainings
+ Counter32,
+
+ -- Errors
+
+ dsSecurityErrors
+ Counter32,
+ dsErrors
+ Counter32
+ }
+
+ -- CLDAP does not use binds; for A CLDAP DS the bind
+ -- related counters will be inaccessible.
+ --
+ -- CLDAP and LDAP implement "Read" and "List" operations
+ -- indirectly via the "search" operation; the following
+ -- counters will be inaccessible for CLDAP and LDAP DSs:
+ -- dsReadOps, dsListOps
+ --
+ -- CLDAP does not implement "Compare", "Add", "Remove",
+ -- "Modify", "ModifyRDN"; the following counters will be
+ -- inaccessible for CLDAP DSs:
+ -- dsCompareOps, dsAddEntryOps, dsRemoveEntryOps,
+ -- dsModifyEntryOps, dsModifyRDNOps.
+ --
+ -- CLDAP and LDAP DS's do not return Referrals
+ -- the following fields will remain inaccessible for
+ -- CLDAP and LDAP DSs: dsReferrals.
+
+ dsAnonymousBinds OBJECT-TYPE
+ SYNTAX Counter32
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ " Number of anonymous binds to this DS from UAs
+ since application start."
+ ::= {dsOpsEntry 1}
+
+ dsUnAuthBinds OBJECT-TYPE
+ SYNTAX Counter32
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ " Number of un-authenticated binds to this DS since
+ application start."
+ ::= {dsOpsEntry 2}
+
+ dsSimpleAuthBinds OBJECT-TYPE
+ SYNTAX Counter32
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ " Number of binds to this DS that were authenticated
+ using simple authentication procedures since
+ application start."
+ REFERENCE
+ " CCITT Blue Book Fascicle VIII.8 - Rec. X.511, 1988:
+ Section 8.1.2.1.1. and, RFC1777 Section 4.1"
+ ::= {dsOpsEntry 3}
+
+ dsStrongAuthBinds OBJECT-TYPE
+ SYNTAX Counter32
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ " Number of binds to this DS that were authenticated
+ using the strong authentication procedures since
+ application start. This includes the binds that were
+ authenticated using external authentication procedures."
+ REFERENCE
+ " CCITT Blue Book Fascicle VIII.8 - Rec. X.511, 1988:
+ Sections 8.1.2.1.2 & 8.1.2.1.3. and, RFC1777 Section 4.1."
+ ::= {dsOpsEntry 4}
+
+ dsBindSecurityErrors OBJECT-TYPE
+ SYNTAX Counter32
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ " Number of bind operations that have been rejected
+ by this DS due to inappropriateAuthentication or
+ invalidCredentials."
+ REFERENCE
+ " CCITT Blue Book Fascicle VIII.8 - Rec. X.511, 1988:
+ Section 12.7.2 and, RFC1777 Section 4."
+ ::= {dsOpsEntry 5}
+
+ dsInOps OBJECT-TYPE
+ SYNTAX Counter32
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ " Number of operations forwarded to this DS
+ from UAs or other DSs since application
+ start up."
+ ::= {dsOpsEntry 6}
+
+ dsReadOps OBJECT-TYPE
+ SYNTAX Counter32
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ " Number of read operations serviced by
+ this DS since application startup."
+ REFERENCE
+ " CCITT Blue Book Fascicle VIII.8 - Rec. X.511, 1988:
+ Section 9.1."
+ ::= {dsOpsEntry 7}
+
+ dsCompareOps OBJECT-TYPE
+ SYNTAX Counter32
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ " Number of compare operations serviced by
+ this DS since application startup."
+ REFERENCE
+ " CCITT Blue Book Fascicle VIII.8 - Rec. X.511, 1988:
+ Section 9.2. and, RFC1777 section 4.8"
+ ::= {dsOpsEntry 8}
+
+ dsAddEntryOps OBJECT-TYPE
+ SYNTAX Counter32
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ " Number of addEntry operations serviced by
+ this DS since application startup."
+ REFERENCE
+ " CCITT Blue Book Fascicle VIII.8 - Rec. X.511, 1988:
+ Section 11.1. and, RFC1777 Section 4.5."
+ ::= {dsOpsEntry 9}
+
+ dsRemoveEntryOps OBJECT-TYPE
+ SYNTAX Counter32
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ " Number of removeEntry operations serviced by
+ this DS since application startup."
+ REFERENCE
+ " CCITT Blue Book Fascicle VIII.8 - Rec. X.511, 1988:
+ Section 11.2. and, RFC1777 Section 4.6."
+ ::= {dsOpsEntry 10}
+
+ dsModifyEntryOps OBJECT-TYPE
+ SYNTAX Counter32
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ " Number of modifyEntry operations serviced by
+ this DS since application startup."
+ REFERENCE
+ " CCITT Blue Book Fascicle VIII.8 - Rec. X.511, 1988:
+ Section 11.3. and, RFC1777 Section 4.4."
+ ::= {dsOpsEntry 11}
+
+ dsModifyRDNOps OBJECT-TYPE
+ SYNTAX Counter32
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ " Number of modifyRDN operations serviced by
+ this DS since application startup."
+ REFERENCE
+ " CCITT Blue Book Fascicle VIII.8 - Rec. X.511, 1988:
+ Section 11.4.and, RFC1777 Section 4.7"
+ ::= {dsOpsEntry 12}
+
+ dsListOps OBJECT-TYPE
+ SYNTAX Counter32
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ " Number of list operations serviced by
+ this DS since application startup."
+ REFERENCE
+ " CCITT Blue Book Fascicle VIII.8 - Rec. X.511, 1988:
+ Section 10.1."
+ ::= {dsOpsEntry 13}
+
+ dsSearchOps OBJECT-TYPE
+ SYNTAX Counter32
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ " Number of search operations- baseObject searches,
+ oneLevel searches and wholeSubtree searches,
+ serviced by this DS since application startup."
+ REFERENCE
+ " CCITT Blue Book Fascicle VIII.8 - Rec. X.511, 1988:
+ Section 10.2. and, RFC1777 Section 4.3."
+ ::= {dsOpsEntry 14}
+
+ dsOneLevelSearchOps OBJECT-TYPE
+ SYNTAX Counter32
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ " Number of oneLevel search operations serviced
+ by this DS since application startup."
+ REFERENCE
+ " CCITT Blue Book Fascicle VIII.8 - Rec. X.511, 1988:
+ Section 10.2.2.2. and, RFC1777 Section 4.3."
+ ::= {dsOpsEntry 15}
+
+ dsWholeSubtreeSearchOps OBJECT-TYPE
+ SYNTAX Counter32
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ " Number of wholeSubtree search operations serviced
+ by this DS since application startup."
+ REFERENCE
+ " CCITT Blue Book Fascicle VIII.8 - Rec. X.511, 1988:
+ Section 10.2.2.2. and, RFC1777 Section 4.3."
+ ::= {dsOpsEntry 16}
+
+ dsReferrals OBJECT-TYPE
+ SYNTAX Counter32
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ " Number of referrals returned by this DS in response
+ to requests for operations since application startup."
+ REFERENCE
+ " CCITT Blue Book Fascicle VIII.8 - Rec. X.511, 1988:
+ Section 12.6."
+ ::= {dsOpsEntry 17}
+
+ dsChainings OBJECT-TYPE
+ SYNTAX Counter32
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ " Number of operations forwarded by this DS
+ to other DSs since application startup."
+ REFERENCE
+ " CCITT Blue Book Fascicle VIII.8 - Rec. X.518, 1988:
+ Section 14."
+ ::= {dsOpsEntry 18}
+
+ dsSecurityErrors OBJECT-TYPE
+ SYNTAX Counter32
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ " Number of operations forwarded to this DS
+ which did not meet the security requirements. "
+ REFERENCE
+ " CCITT Blue Book Fascicle VIII.8 - Rec. X.511, 1988:
+ Section 12.7. and, RFC1777 Section 4."
+ ::= {dsOpsEntry 19}
+
+ dsErrors OBJECT-TYPE
+ SYNTAX Counter32
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ " Number of operations that could not be serviced
+ due to errors other than security errors, and
+ referrals.
+ A partially serviced operation will not be counted
+ as an error.
+ The errors include NameErrors, UpdateErrors, Attribute
+ errors and ServiceErrors."
+ REFERENCE
+ " CCITT Blue Book Fascicle VIII.8 - Rec. X.511, 1988:
+ Sections 12.4, 12.5, 12.8 & 12.9. and, RFC1777 Section 4."
+ ::= {dsOpsEntry 20}
+
+ -- Entry statistics/Cache performance
+ dsEntriesTable OBJECT-TYPE
+ SYNTAX SEQUENCE OF DsEntriesEntry
+ MAX-ACCESS not-accessible
+ STATUS current
+ DESCRIPTION
+ " The table holding information related to the
+ entry statistics and cache performance of the DSs."
+ ::= {nsldap 2}
+
+ dsEntriesEntry OBJECT-TYPE
+ SYNTAX DsEntriesEntry
+ MAX-ACCESS not-accessible
+ STATUS current
+ DESCRIPTION
+ " Entry containing statistics pertaining to entries
+ held by a DS."
+ INDEX { applIndex }
+ ::= {dsEntriesTable 1}
+
+
+ DsEntriesEntry ::= SEQUENCE {
+
+ dsMasterEntries
+ Gauge32,
+ dsCopyEntries
+ Gauge32,
+ dsCacheEntries
+ Gauge32,
+ dsCacheHits
+ Counter32,
+ dsSlaveHits
+ Counter32
+ }
+
+ -- A (C)LDAP frontend to the X.500 Directory will not have
+ -- MasterEntries, CopyEntries; the following counters will
+ -- be inaccessible for LDAP/CLDAP frontends to the X.500
+ -- directory: dsMasterEntries, dsCopyEntries, dsSlaveHits.
+
+ dsMasterEntries OBJECT-TYPE
+ SYNTAX Gauge32
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ " Number of entries mastered in the DS."
+ ::= {dsEntriesEntry 1}
+
+ dsCopyEntries OBJECT-TYPE
+ SYNTAX Gauge32
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ " Number of entries for which systematic (slave)
+ copies are maintained in the DS."
+ ::= {dsEntriesEntry 2}
+
+ dsCacheEntries OBJECT-TYPE
+ SYNTAX Gauge32
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ " Number of entries cached (non-systematic copies) in
+ the DS. This will include the entries that are
+ cached partially. The negative cache is not counted."
+ ::= {dsEntriesEntry 3}
+
+ dsCacheHits OBJECT-TYPE
+ SYNTAX Counter32
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ " Number of operations that were serviced from
+ the locally held cache since application
+ startup."
+ ::= {dsEntriesEntry 4}
+
+ dsSlaveHits OBJECT-TYPE
+ SYNTAX Counter32
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ " Number of operations that were serviced from
+ the locally held object replications ( shadow
+ entries) since application startup."
+ ::= {dsEntriesEntry 5}
+
+ -- The dsIntTable contains statistical data on the peer DSs
+ -- with which the monitored DSs (attempt to) interact. This
+ -- table will provide a useful insight into the effect of
+ -- neighbours on the DS performance.
+ -- The table keeps track of the last "N" DSs with which the
+ -- monitored DSs has interacted (attempted to interact),
+ -- where "N" is a locally-defined constant.
+
+ dsIntTable OBJECT-TYPE
+ SYNTAX SEQUENCE OF DsIntEntry
+ MAX-ACCESS not-accessible
+ STATUS current
+ DESCRIPTION
+ " Each row of this table contains some details
+ related to the history of the interaction
+ of the monitored DSs with their respective
+ peer DSs."
+ ::= { nsldap 3 }
+
+ dsIntEntry OBJECT-TYPE
+ SYNTAX DsIntEntry
+ MAX-ACCESS not-accessible
+ STATUS current
+ DESCRIPTION
+ " Entry containing interaction details of a DS
+ with a peer DS."
+ INDEX { applIndex, dsIntIndex }
+ ::= { dsIntTable 1 }
+
+ DsIntEntry ::= SEQUENCE {
+
+ dsIntIndex
+ INTEGER,
+ dsName
+ DistinguishedName,
+ dsTimeOfCreation
+ TimeStamp,
+ dsTimeOfLastAttempt
+ TimeStamp,
+ dsTimeOfLastSuccess
+ TimeStamp,
+ dsFailuresSinceLastSuccess
+ Counter32,
+ dsFailures
+ Counter32,
+ dsSuccesses
+ Counter32,
+ dsURL
+ URLString
+
+ }
+
+ dsIntIndex OBJECT-TYPE
+ SYNTAX INTEGER (1..2147483647)
+ MAX-ACCESS not-accessible
+ STATUS current
+ DESCRIPTION
+ " Together with applIndex it forms the unique key to
+ identify the conceptual row which contains useful info
+ on the (attempted) interaction between the DS (referred
+ to by applIndex) and a peer DS."
+ ::= {dsIntEntry 1}
+
+ dsName OBJECT-TYPE
+ SYNTAX DistinguishedName
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ " Distinguished Name of the peer DS to which this
+ entry pertains."
+ ::= {dsIntEntry 2}
+
+ dsTimeOfCreation OBJECT-TYPE
+ SYNTAX TimeStamp
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ " The value of sysUpTime when this row was created.
+ If the entry was created before the network management
+ subsystem was initialized, this object will contain
+ a value of zero."
+ ::= {dsIntEntry 3}
+
+ dsTimeOfLastAttempt OBJECT-TYPE
+ SYNTAX TimeStamp
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ " The value of sysUpTime when the last attempt was made
+ to contact this DS. If the last attempt was made before
+ the network management subsystem was initialized, this
+ object will contain a value of zero."
+ ::= {dsIntEntry 4}
+
+ dsTimeOfLastSuccess OBJECT-TYPE
+ SYNTAX TimeStamp
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ " The value of sysUpTime when the last attempt made to
+ contact this DS was successful. If there have
+ been no successful attempts this entry will have a value
+ of zero. If the last successful attempt was made before
+ the network management subsystem was initialized, this
+ object will contain a value of zero."
+ ::= {dsIntEntry 5}
+
+ dsFailuresSinceLastSuccess OBJECT-TYPE
+ SYNTAX Counter32
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ " The number of failures since the last time an
+ attempt to contact this DS was successful. If
+ there has been no successful attempts, this counter
+ will contain the number of failures since this entry
+ was created."
+ ::= {dsIntEntry 6}
+
+ dsFailures OBJECT-TYPE
+ SYNTAX Counter32
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ " Cumulative failures since the creation of
+ this entry."
+ ::= {dsIntEntry 7}
+
+ dsSuccesses OBJECT-TYPE
+ SYNTAX Counter32
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ " Cumulative successes since the creation of
+ this entry."
+ ::= {dsIntEntry 8}
+
+ dsURL OBJECT-TYPE
+ SYNTAX URLString
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ " URL of the DS application."
+ ::= {dsIntEntry 9}
+
+--
+-- Information about this installation of the directory server
+--
+
+ dsEntityTable OBJECT-TYPE
+ SYNTAX SEQUENCE OF DsEntityEntry
+ MAX-ACCESS not-accessible
+ STATUS current
+ DESCRIPTION
+ "This table holds general information related to an installed
+ instance of a directory server"
+ ::= {nsldap 5}
+
+ dsEntityEntry OBJECT-TYPE
+ SYNTAX DsEntityEntry
+ MAX-ACCESS not-accessible
+ STATUS current
+ DESCRIPTION
+ "Entry of general information about an installed instance
+ of a directory server"
+ INDEX { applIndex}
+ ::= {dsEntityTable 1}
+
+ DsEntityEntry ::= SEQUENCE {
+ dsEntityDescr
+ DisplayString,
+ dsEntityVers
+ DisplayString,
+ dsEntityOrg
+ DisplayString,
+ dsEntityLocation
+ DisplayString,
+ dsEntityContact
+ DisplayString,
+ dsEntityName
+ DisplayString
+ }
+
+ dsEntityDescr OBJECT-TYPE
+ SYNTAX DisplayString(SIZE (0..255))
+ ACCESS read-only
+ STATUS mandatory
+ DESCRIPTION
+ "A general textual description of this directory server."
+ ::= {dsEntityEntry 1}
+
+ dsEntityVers OBJECT-TYPE
+ SYNTAX DisplayString(SIZE (0..255))
+ ACCESS read-only
+ STATUS mandatory
+ DESCRIPTION
+ "The version of this directory server"
+ ::={dsEntityEntry 2}
+
+ dsEntityOrg OBJECT-TYPE
+ SYNTAX DisplayString(SIZE (0..255))
+ ACCESS read-only
+ STATUS mandatory
+ DESCRIPTION
+ "Organization responsible for directory server at this installation"
+ ::={dsEntityEntry 3}
+
+ dsEntityLocation OBJECT-TYPE
+ SYNTAX DisplayString(SIZE (0..255))
+ ACCESS read-only
+ STATUS mandatory
+ DESCRIPTION
+ "Physical location of this entity (directory server).
+ For example: hostname, building number, lab number, etc."
+ ::={dsEntityEntry 4}
+
+ dsEntityContact OBJECT-TYPE
+ SYNTAX DisplayString(SIZE (0..255))
+ ACCESS read-only
+ STATUS mandatory
+ DESCRIPTION
+ "Contact person(s)responsible for the directory server at this
+ installation, together with information on how to conact."
+ ::={dsEntityEntry 5}
+
+ dsEntityName OBJECT-TYPE
+ SYNTAX DisplayString(SIZE (0..255))
+ ACCESS read-only
+ STATUS mandatory
+ DESCRIPTION
+ "Name assigned to this entity at the installation site"
+ ::={dsEntityEntry 6}
+
+--
+-- Traps
+--
+--
+ nsDirectoryServerDown TRAP-TYPE
+ ENTERPRISE netscape
+ VARIABLES { dsEntityDescr, dsEntityVers, dsEntityLocation,
+ dsEntityContact }
+ DESCRIPTION "This trap is generated whenever the agent detects the
+ directory server to be (potentially) Down."
+ ::= 7001
+
+ nsDirectoryServerStart TRAP-TYPE
+ ENTERPRISE netscape
+ VARIABLES { dsEntityDescr, dsEntityVers, dsEntityLocation }
+ DESCRIPTION "This trap is generated whenever the agent detects the
+ directory server to have (re)started."
+ ::= 7002
+
+ END
+
diff --git a/ldap/servers/snmp/ntagt/Makefile b/ldap/servers/snmp/ntagt/Makefile
new file mode 100644
index 00000000..22605b2f
--- /dev/null
+++ b/ldap/servers/snmp/ntagt/Makefile
@@ -0,0 +1,103 @@
+#
+# BEGIN COPYRIGHT BLOCK
+# Copyright 2001 Sun Microsystems, Inc.
+# Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+# All rights reserved.
+# END COPYRIGHT BLOCK
+#
+#############################################################################
+# SNMP NT Subagent Common Rulesets #
+#############################################################################
+
+MCOM_ROOT = ../../../../..
+MSRV_ROOT = ../../..
+SLAPD_ROOT = $(MSRV_ROOT)/servers/slapd
+SNMP_ROOT = $(MSRV_ROOT)/servers/snmp
+LIBDEST = $(OBJDIR)/obj
+BINDIR = $(LDAP_SERVER_RELDIR)
+
+NOSTDCLEAN = true # don't let nsconfig.mk define target clean
+NOSTDSTRIP = true # don't let nsconfig.mk define target strip
+NSPR20 = true
+
+include $(MCOM_ROOT)/ldapserver/nsconfig.mk
+include $(MSRV_ROOT)/nsldap.mk
+
+OBJ_SUFFIX = obj
+
+INCLUDES += -I../../slapd -I$(MCOM_ROOT)/ldapserver/ldap/include
+
+CCOPTS += $(CFLAGS) $(DLL_CFLAGS) $(MCC_INCLUDE) $(INCLUDES) -I$(SNMP_ROOT)
+CXXOPTS += $(CFLAGS) $(DLL_CXXFLAGS) $(MCC_INCLUDE) $(INCLUDES) -I$(SNMP_ROOT)
+
+EXTRA_LIBS = advapi32.lib user32.lib snmpapi.lib mgmtapi.lib $(LDAP_SDK_LIBLDAP_DLL)
+
+#############################################################################
+# SNMP NT Subagent Core Components #
+#############################################################################
+
+LOCAL_SRCS = agtmmap.c \
+ nsldapagt_nt.c \
+ nsldapmib_nt.c
+
+SNMP_SRCS = $(LOCAL_SRCS)
+
+LOCAL_INCS = agtmmap.h \
+ nsldapagt_nt.h \
+ nsldapmib_nt.h
+
+SNMP_INCS = $(LOCAL_INCS)
+
+SNMP_OBJS = $(addprefix $(LIBDEST)/, \
+ agtmmap.$(OBJ_SUFFIX) \
+ nsldapagt_nt.$(OBJ_SUFFIX) \
+ nsldapmib_nt.$(OBJ_SUFFIX))
+
+
+SNMP_LIB = ns-ldapagt.$(DLL_SUFFIX)
+SNMP_IMPLIB = ns-ldapagt.$(LIB_SUFFIX)
+
+#############################################################################
+# SNMP NT Subagent Build Rules #
+#############################################################################
+
+# Now we get into the actual build rules
+AGTMMAP = agtmmap
+
+all: $(LDAP_SERVER_RELDIR) $(AGTMMAP) $(BINDIR)/$(SNMP_LIB)
+
+over: clean all
+
+$(AGTMMAP): $(SLAPD_ROOT)/$(AGTMMAP).c $(SLAPD_ROOT)/$(AGTMMAP).h
+ $(CP) $(SLAPD_ROOT)/$(AGTMMAP).c .
+ $(CP) $(SLAPD_ROOT)/$(AGTMMAP).h .
+
+$(SNMP_OBJS): $(SNMP_SRCS) $(SNMP_INCS)
+
+$(BINDIR)/$(SNMP_LIB): $(SNMP_OBJS) $(SNMP_RES) Makefile
+ $(LINK_DLL) $(LD_EXTRAS) /DEF:"nsldapagt_nt.def" /VERSION:"7" \
+ $(SNMP_OBJS) $(EXTRA_LIBS)
+
+$(LIBDEST)/%.$(OBJ_SUFFIX): %.cxx
+ $(CCP) -c $(CXXOPTS) $< -Fo$(LIBDEST)/$*.$(OBJ_SUFFIX)
+
+$(LIBDEST)/%.$(OBJ_SUFFIX): %.c
+ $(CC) -c $(CCOPTS) $< -Fo$(LIBDEST)/$*.$(OBJ_SUFFIX)
+
+$(LIBDEST)/%.$(OBJ_SUFFIX): $(SNMP_ROOT)/%.cxx
+ $(CCP) -c $(CXXOPTS) $< -Fo$(LIBDEST)/$*.$(OBJ_SUFFIX)
+
+$(LIBDEST)/%.$(OBJ_SUFFIX): $(SNMP_ROOT)/%.c
+ $(CC) -c $(CCOPTS) $< -Fo$(LIBDEST)/$*.$(OBJ_SUFFIX)
+
+clean: localclean
+
+localclean:
+ $(RM) $(SNMP_OBJS) $(LIBDEST)/$(SNMP_LIB) $(LIBDEST)/$(SNMP_IMPLIB) $(AGTMMAP).c $(AGTMMAP).h
+
+#############################################################################
+# Depend Area #
+#############################################################################
+
+# DO NOT DELETE THIS LINE -- make depend depends on it.
+
diff --git a/ldap/servers/snmp/ntagt/msrvdefs.mak b/ldap/servers/snmp/ntagt/msrvdefs.mak
new file mode 100644
index 00000000..ac877d9c
--- /dev/null
+++ b/ldap/servers/snmp/ntagt/msrvdefs.mak
@@ -0,0 +1,492 @@
+#
+# BEGIN COPYRIGHT BLOCK
+# Copyright 2001 Sun Microsystems, Inc.
+# Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+# All rights reserved.
+# END COPYRIGHT BLOCK
+#
+#############################################################################
+# Mail Server Common Rulesets #
+#############################################################################
+
+MSRV_RELEASE = 4.0
+
+ifeq ($(DEBUG), optimize)
+MSRV_DEBUG = no
+else
+MSRV_DEBUG = yes
+endif
+
+ifeq ($(MSRV_DEBUG), yes)
+DEBUG_DEST = DBG-
+else
+DEBUG_DEST =
+endif
+
+# this allows mixing DBG objects with non DBG libs, avoiding
+# a complete autobuild or other linking hacks. Any DBG libs
+# (under mailserv2) will get picked up first, followed by non-DBG
+# libs. In any event, no DBG libs will be linked if MSRV_DEBUG=no.
+NDBGDEST = $(MSRV_ROOT)/built/$(ARCH)
+
+# This is where we are generally supposed to put built stuff
+BLDDEST = $(MSRV_ROOT)/built/$(DEBUG_DEST)$(ARCH)
+
+# module specific locations for build stuff
+LIBDEST = $(BLDDEST)/lib
+NETDEST = $(BLDDEST)/network
+LOCDEST = $(BLDDEST)/local
+EXTDEST = $(BLDDEST)/extras
+BINDEST = $(BLDDEST)/bin
+OBJDEST = $(BLDDEST)/obj
+
+IMPORTS_DIR = $(MSRV_ROOT)/code/import
+
+
+# Extensions for generated files.
+
+SHLIB_SUFFIX = so
+ARCHIVE_SUFFIX = a
+OBJ_SUFFIX = o
+EXE_SUFFIX =
+
+export SHLIB_EXT ARCHIVE_EXT OBJ_EXT EXE_EXT
+
+RM = rm -f
+AR = ar cr
+MD = mkdir -p
+MAKE = gmake
+STRIP = strip
+CP = cp
+ECHO = echo
+
+# For reasons you really don't want to know, we put all the C++ core
+# modules into a static lib on Unix platforms. If the system supports
+# shared libs, we use it only for C-derived object modules.
+
+ifneq ($(ARCH), WINNT)
+STAT_LIB = libNSmObj.$(ARCHIVE_SUFFIX)
+endif
+
+ifeq ($(ARCH), WINNT)
+
+ifeq ($(DEBUG), full)
+ML_DEBUG = /DEBUG
+ifeq ($(NTDEBUGENV),ON)
+MC_DEBUGENV= /D__NTDebugEnv__
+else
+MC_DEBUGENV=
+endif
+ifeq ($(NTDEBUGSLEEP),ON)
+MC_DEBUG=/D__NTDebug__ /D__NTDebugSleep__ $(MC_DEBUGENV)
+else
+MC_DEBUG = /D__NTDebug__ $(MC_DEBUGENV)
+endif
+else
+ML_DEBUG =
+MC_DEBUG =
+endif
+
+endif
+### Solaris #############################################################
+
+ifeq ($(ARCH), SOLARIS)
+ BUILD_SHARED = TRUE
+ BUILDAPI_SHARED = FALSE
+ PLUGIN_SHARED = TRUE
+ CC = gcc
+ CXX = g++
+
+ifeq ($(PURIFY), yes)
+ CC = /stuff/iasrc3/xtern/purify/purify gcc
+ CXX = /stuff/iasrc3/xtern/purify/purify g++
+endif
+
+ifeq ($(QUANTIFY), yes)
+ CC = /stuff/iasrc3/xtern/quantify/quantify gcc
+ CXX = /stuff/iasrc3/xtern/quantify/quantify g++
+endif
+
+ LD = ld
+ DLL_CFLAGS = -fpic
+ DLL_CXXFLAGS = -fpic
+ DLL_LDFLAGS = -G
+ OPTIMIZE_FLAGS = -O2
+ CFLAGS = $(DLL_CFLAGS) -DSVR4 -D__svr4__ -DSOLARIS \
+ -DHAVE_NIS -DHAS_GETSPNAM -DHAS_FGETPWENT \
+ -DXP_UNIX -DSTATOBJS \
+ -DMSRV_RELEASE=\"$(MSRV_RELEASE)\" \
+ -DOSVERSION=$(OSVERSION) -D_REENTRANT
+ LD_EXTRAS = -L/tools/ns/lib -lthread -lsocket -lnsl -lgen -ldl
+ MADM_LDLIBS = -ldl
+endif
+
+### IRIX notes ##########################################################
+## Suppress warnings about statement unreachable (3203) and
+## unused parameters (3262). Increase Olimit
+## Suppress REALLY ANNOYING link warnings unless a debug build
+## 84 is unsed libs. 85 is duplicate symbol preemption.
+## This hides duplicate symbols in the link - so watch it
+##########################################################################
+ifeq ($(ARCH), IRIX)
+ BUILD_SHARED = TRUE
+ BUILDAPI_SHARED = FALSE
+ PLUGIN_SHARED = TRUE
+ CC = cc
+ifneq ($(MSRV_DEBUG),yes)
+ CXX = CC -woff 3203,3262 -Wl,-woff,84 -Wl,-woff,85
+else
+ CXX = CC
+endif
+ LD = CC
+ DLL_CFLAGS =
+ DLL_CXXFLAGS =
+ DLL_LDFLAGS = -shared
+ OPTIMIZE_FLAGS = -O -Olimit 4096
+ CFLAGS = $(DLL_CFLAGS) -DIRIX -DSTATOBJS \
+ -DHAVE_NIS -DHAS_GETSPNAM -DHAS_FGETPWENT \
+ -DXP_UNIX \
+ -DMSRV_RELEASE=\"$(MSRV_RELEASE)\" \
+ -DOSVERSION=$(OSVERSION)
+
+ LD_EXTRAS =
+endif
+
+### HPUX notes ###########################################################
+## g++ does not support pic on a300 architecture
+## -Wl,+b,/usr/lib,+s is needed so that runtime can find shared lib
+## correctly
+## Also, gcc is used to compile and link modules with c-based main
+## to overcome a global constructor problem. Change in
+## admin/src/Makefile and code/network/IMAP4-Server/unix/Makefile
+## and code/tools/Makefile
+#########################################################################
+
+ifeq ($(ARCH), HPUX)
+ SHLIB_SUFFIX = sl
+ BUILD_SHARED = TRUE
+ BUILDAPI_SHARED = FALSE
+ PLUGIN_SHARED = TRUE
+ CC = cc
+ CXX = g++
+ LD = ld
+ NATIVE_CFLAGS = -Aa -D_HPUX_SOURCE
+ DLL_CFLAGS = +z
+ DLL_CXXFLAGS =
+ DLL_LDFLAGS = -b
+ OPTIMIZE_FLAGS = -O
+ CFLAGS = -DHPUX -DSTATOBJS -DHAVE_NIS \
+ -DXP_UNIX -DHAS_FGETPWENT \
+ -DMSRV_RELEASE=\"$(MSRV_RELEASE)\" \
+ -DOSVERSION=$(OSVERSION)
+ LD_EXTRAS = -Wl,+b,/usr/lib,+s \
+ -L/tools/ns/lib -liostream -ldld -lm
+ #-L/tools/ns/lib -liostream -ldld -lm -lpthread
+ MSRV_INCLUDES = -I/tools/ns/lib/g++-include
+endif
+
+
+### OSF1 notes ############################################################
+## -taso options and -DIS_64 are needed to get 32 bit behavior on the alpha
+## ...otherwise libdbm will not work
+###########################################################################
+
+ifeq ($(ARCH), OSF1)
+ BUILD_SHARED = TRUE
+ BUILDAPI_SHARED = FALSE
+ PLUGIN_SHARED = TRUE
+ CC = cc -taso -DIS_64 -Olimit 4000
+ CXX = g++
+ LD = ld
+ DLL_CFLAGS =
+ DLL_CXXFLAGS =
+ DLL_LDFLAGS =
+ OPTIMIZE_FLAGS = -O
+ CFLAGS = -DOSF1 -DSTATOBJS -DHAVE_NIS -DXP_UNIX \
+ -DHAS_FGETPWENT \
+ -DMSRV_RELEASE=\"$(MSRV_RELEASE)\" \
+ -DOSVERSION=$(OSVERSION)
+ LD_EXTRAS = -lc_r -L/tools/ns/lib -liostream -Wl,-taso
+ MSRV_INCLUDES = -I/tools/ns/lib/g++-include
+endif
+
+### AIX notes #############################################################
+## Yah, right. In your dreams...
+## What a horrid c++ platform. We need to supply our own g++ config file
+## because the systems supplied one is broken. Lots of surprises in store.
+## Needs a separate build tree on 3.2.5 because automount is broken.
+## Also, like HP - gcc is used to compile and link modules with c-based main
+## to overcome a global constructor problem. Change in
+## admin/src/Makefile and code/network/IMAP4-Server/unix/Makefile
+## and code/tools/Makefile
+##
+## DLL_CFLAGS type of stuff is set specifically in plugins/Makefile
+## and also local/SMTP-Router/Makefile to use a specific export list
+## (for the NSmatch plugin in this case). Also see further below for the
+## linker override option to use the import list instead of a named lib
+##########################################################################
+
+## AIX 4
+ifeq ($(ARCH), AIX)
+ BUILD_SHARED = FALSE
+ BUILDAPI_SHARED = FALSE
+ PLUGIN_SHARED = TRUE
+ SHLIB_SUFFIX = _shr.a
+ CC = cc
+ CXX = g++
+# CXX = g++ -I$(MSRV_ROOT)/code/include/aix
+ LD = ld
+ DLL_CLAGS =
+ DLL_CXXFLAGS =
+ OPTIMIZE_FLAGS = -O
+ CFLAGS = -DAIX -DAIXV3 -DAIXV4 -DSTATOBJS \
+ -DHAVE_NIS -DXP_UNIX -mcpu=common\
+ -DMSRV_RELEASE=\"$(MSRV_RELEASE)\" \
+ -DOSVERSION=$(OSVERSION)
+ LD_EXTRAS = -L/gnu/install/lib -L/gnu/install/lib/gcc-lib/powerpc-ibm-aix4.1.4.0/2.7.2.1/common -lstdc++ -ls -lsvld -lgcc -lc_r
+# LD_EXTRAS = -L/usr/gnu/lib -liostream -ls -ldl
+ MSRV_INCLUDES = -I/gnu/install/lib/g++-include
+# MSRV_INCLUDES = -I/usr/gnu/lib/g++-include
+endif
+
+## AIX 3
+#ifeq ($(ARCH), AIX)
+# BUILD_SHARED = FALSE
+# BUILDAPI_SHARED = FALSE
+# PLUGIN_SHARED = TRUE
+# CC = svcc
+# CXX = g++ -I$(MSRV_ROOT)/code/include/aix
+# LD = ld
+# DLL_CLAGS =
+# DLL_CXXFLAGS =
+# OPTIMIZE_FLAGS = -O
+# CFLAGS = -DAIX -DAIXV3 -DSTATOBJS \
+# -DHAVE_NIS -DXP_UNIX \
+# -DMSRV_RELEASE=\"$(MSRV_RELEASE)\"
+# LD_EXTRAS = -L/tools/ns/lib -liostream -ls
+# MSRV_INCLUDES = -I/tools/ns/lib/g++-include
+#endif
+
+
+ifeq ($(ARCH), WINNT)
+
+ SHLIB_SUFFIX = dll
+ ARCHIVE_SUFFIX = lib
+ RM = del /q
+ EXE_SUFFIX = .exe
+ OBJ_SUFFIX = obj
+
+ SHARED_LIB = NetscapeMTA30.$(SHLIB_SUFFIX)
+ SHARED_IMPLIB = NetscapeMTA30.$(ARCHIVE_SUFFIX)
+ SHARED_BASE_LIB = NetscapeMTAX30.$(SHLIB_SUFFIX)
+ SHARED_BASE_IMPLIB = NetscapeMTAX30.$(ARCHIVE_SUFFIX)
+ MATCH_LIB = NSMatch30.$(SHLIB_SUFFIX)
+ MATCH_IMPLIB = NSMatch30.$(ARCHIVE_SUFFIX)
+ POSEC_LIB = nsSupport30.$(SHLIB_SUFFIX)
+ POSEC_IMPLIB = nsSupport30.$(ARCHIVE_SUFFIX)
+ MDBAPI_LIB = NetscapeMDB30.$(SHLIB_SUFFIX)
+ MDBAPI_IMPLIB = NetscapeMDB30.$(ARCHIVE_SUFFIX)
+ BUILD_SHARED = TRUE
+ BUILDAPI_SHARED = TRUE
+ PLUGIN_SHARED = TRUE
+ POSEC_SHARED = FALSE
+ CFLAGS = /D__NT__ $(MC_DEBUG) \
+ /DMSRV_RELEASE=\"$(MSRV_RELEASE)\" \
+ /D$(NS_PRODUCT) $(XP_FLAG) \
+ /D__USE_THREAD_HEAPS__ -Gy
+ OPTIMIZE_FLAGS = -Ob1 -O2
+ DLL_CFLAGS = /D__Lib__
+ DLL_CXXFLAGS = /D__Lib__
+ LD_EXTRAS = /INCREMENTAL:NO $(ML_DEBUG)
+ LINK_CONSOLE = link /NOLOGO /SUBSYSTEM:CONSOLE $(ML_DEBUG) \
+ /OUT:$@ /OPT:REF /MAP
+endif
+
+
+.SUFFIXES: .cxx
+
+# In Debug mode, always build archive libraries
+
+ifeq ($(MSRV_DEBUG), yes)
+
+ifneq ($(ARCH), WINNT)
+MSRV_DBG_DEFINES = -g -DMSRV_DEBUG $(DBG_FLAGS)
+BUILD_SHARED = FALSE
+PLUGIN_SHARED = FALSE
+else
+MSRV_DBG_DEFINES = /Od /Zi $(DBG_FLAGS)
+endif
+
+else
+MSRV_DBG_DEFINES = $(OPTIMIZE_FLAGS)
+endif
+
+
+ifneq ($(ARCH), WINNT)
+
+ifeq ($(BUILD_SHARED), TRUE)
+ SHARED_LIB = libNSmail.$(SHLIB_SUFFIX)
+else
+ SHARED_LIB = libNSmail.$(ARCHIVE_SUFFIX)
+endif
+
+ifeq ($(BUILDAPI_SHARED), TRUE)
+ MDBAPI_LIB = libNSmdb.$(SHLIB_SUFFIX)
+else
+ MDBAPI_LIB = libNSmdb.$(ARCHIVE_SUFFIX)
+endif
+
+
+ifeq ($(PLUGIN_SHARED), TRUE)
+ifeq ($(ARCH), AIX)
+ MATCH_LIB = libNSmatch$(SHLIB_SUFFIX)
+ HDR_LIB = libNShdr$(SHLIB_SUFFIX)
+else
+ MATCH_LIB = libNSmatch.$(SHLIB_SUFFIX)
+ HDR_LIB = libNShdr.$(SHLIB_SUFFIX)
+endif
+else
+ MATCH_LIB = libNSmatch.$(ARCHIVE_SUFFIX)
+ HDR_LIB = libNShdr.$(ARCHIVE_SUFFIX)
+endif
+
+
+ifeq ($(ARCH), HPUX)
+ifeq ($(MSRV_HPUX_CURSES_STATIC), TRUE)
+CURSES=/usr/lib/libHcurses.a
+else
+CURSES=/usr/lib/libcurses.sl
+endif
+else
+CURSES=-lcurses -ltermcap
+endif
+
+ifeq ($(ARCH), AIX)
+CURSES=-lcurses
+endif
+
+# Before merge stuff - nirmal 4/24
+#LD_LINKLIB = -L$(LIBDEST) -L/usr/lib -L$(NDBGDEST)/lib \
+# -lNSmdb -lNSmail -lNSmObj \
+# -lNSmail -lNSmdb -L$(NSCP_DISTDIR)/lib \
+# -llcache10 -lldap10 $(CURSES)
+#
+
+ifeq ($(ARCH), HPUX)
+LD_LINKLIB = -L$(LIBDEST) -L/usr/lib -L$(NDBGDEST)/lib \
+ -lNSmdb -lNSmail -lNSmObj -lNSmail -lNSmdb \
+ -L$(NSCP_DISTDIR)/lib -llcache10 \
+ -lldap10 \
+ $(CURSES)
+else
+LD_LINKLIB = -L$(LIBDEST) -L/usr/lib -L$(NDBGDEST)/lib \
+ -lNSmdb -lNSmail -lNSmObj -lNSmail -lNSmdb \
+ $(NSCP_DISTDIR)/lib/liblcache10.$(SHLIB_SUFFIX) \
+ $(NSCP_DISTDIR)/lib/libldap10.$(SHLIB_SUFFIX) \
+ $(CURSES)
+endif
+
+ifeq ($(ARCH), AIX)
+CURSES=-lcurses
+LD_LINKLIB = -L$(LIBDEST) -L/usr/lib -L$(NDBGDEST)/lib \
+ -lNSmdb -lNSmail -lNSmObj \
+ $(NSCP_DISTDIR)/lib/liblcache10$(SHLIB_SUFFIX) \
+ $(NSCP_DISTDIR)/lib/libldap10$(SHLIB_SUFFIX) \
+ $(NSCP_DISTDIR)/lib/libssldap10.a \
+ $(CURSES)
+
+endif
+
+
+LD_MATCHLIB = -lNSmatch
+else # ARCH = WINNT section
+LD_POSECLIB = $(addprefix $(LIBDEST)/, $(POSEC_IMPLIB))
+LD_LINKLIB = $(addprefix $(LIBDEST)/, $(SHARED_IMPLIB))
+LD_MATCHLIB = $(addprefix $(LIBDEST)/, $(MATCH_IMPLIB))
+endif
+
+ifeq ($(ARCH), WINNT)
+LD_LINKLIB += $(NSCP_DISTDIR)/lib/nsldap32v10.lib $(NSCP_DISTDIR)/lib/nslch32v10.lib
+LDX_LINKLIB = $(LD_LINKLIB) $(LIBDEST)/$(SHARED_BASE_IMPLIB)
+
+I18NLIBS=$(addsuffix .$(LIB_SUFFIX),\
+ $(addprefix $(OBJDIR)/lib/lib, \
+ ldapu $(LIBADMIN) $(FRAME) $(CRYPT) $(LIBACCESS))) \
+ $(LIBDBM) $(LIBXP) $(LIBNSPR) $(LIBARES) $(LIBSEC)
+else
+
+# The way I18LIBS was defined in server3_mail_branch, is quite misleading.
+# (see modules.mk for details). After this, LD_LINKLIB gets redefined
+# and that adds unwanted lines to the link of bunch of things. Please
+# add I18LIBS to the individual makefiles, where necessary. See
+# mailserv2/admin/src/Makefile -> DEPLIBS for an example.
+# I am changing this definition for the merge. Retain these changes in
+# future merges. I haved added posix4 to keep what was before merge.
+# - Nirmal 4/24/97.
+#I18NLIBS=$(addsuffix .$(LIB_SUFFIX),\
+# $(addprefix $(OBJDIR)/lib/lib, \
+# ldapu $(LIBADMIN) $(FRAME) $(CRYPT) $(LIBACCESS))) \
+# $(LIBDBM) $(LIBXP) $(LIBNSPR) $(LIBARES) \
+# -L$(MCOM_ROOT)/components/ldapsdk/$(NSOBJDIR_NAME)/lib \
+# -lssldap10 $(LIBSEC)
+I18NLIBS=$(addsuffix .$(LIB_SUFFIX),\
+ $(addprefix $(OBJDIR)/lib/lib, \
+ ldapu $(LIBADMIN) $(FRAME) $(CRYPT) $(LIBACCESS))) \
+ $(LIBDBM) $(LIBXP) $(LIBNSPR) $(LIBARES) \
+ -L$(NSCP_DISTDIR)/lib \
+ -lssldap10 $(LIBSEC)
+
+endif
+
+ifeq ($(ARCH), SOLARIS) # IRIX and HPUX have no posix4 lib
+ I18NLIBS += -lposix4
+endif
+
+LD_LINKLIB += $(I18NLIBS)
+
+ifeq ($(ARCH), IRIX)
+LD_LINKLIB += $(NSCP_DISTDIR)/lib/libldap10.so
+endif
+
+##########################################################################
+## AIX override to make dynamic linking work correctly
+## instead of linking directly with the shared object, we use an
+## import list. Note this syntax only works with g++ or gcc as the compiler
+##########################################################################
+
+ifeq ($(ARCH), AIX)
+LD_MATCHLIB = -Wl,-bI:$(MSRV_ROOT)/code/plugins/NSMatch.exp
+endif
+
+
+
+DB_DEFINES = -DMEMMOVE -D__DBINTERFACE_PRIVATE -DPOSIX_MISTAKE
+
+INCLUDES = -I$(MSRV_ROOT)/code/include \
+ -I$(MCOM_ROOT)/lib/libdbm \
+ -I$(MSRV_ROOT)/contrib/regex \
+ $(MSRV_INCLUDES)
+
+CCOPTS = $(MSRV_DBG_DEFINES) $(CFLAGS) $(INCLUDES) \
+ $(MCC_INCLUDE) $(NSPR_DEFINES)
+CXXOPTS = $(MSRV_DBG_DEFINES) $(CFLAGS) $(INCLUDES) \
+ $(MCC_INCLUDE) $(NSPR_DEFINES)
+
+
+MSRVDESTS = $(BLDDEST) $(LIBDEST) $(NETDEST) $(LOCDEST) $(EXTDEST) \
+ $(BINDEST) $(OBJDEST)
+
+$(MSRVDESTS):
+ $(MD) $@
+
+default: all
+
+all: $(MSRVDESTS)
+
+depend: localdepend
+
+clean: localclean
+
+spotless: clean
+ $(RM) -r $(BLDDEST)
+
diff --git a/ldap/servers/snmp/ntagt/nslagtcom_nt.h b/ldap/servers/snmp/ntagt/nslagtcom_nt.h
new file mode 100644
index 00000000..0a9f7eb5
--- /dev/null
+++ b/ldap/servers/snmp/ntagt/nslagtcom_nt.h
@@ -0,0 +1,32 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+
+/*-------------------------------------------------------------------------
+ *
+ * nslagtcom_nt.h - Common definitions for NS Directory Server's SNMP
+ * subagent on NT.
+ * The definitions in here should be kept so that modules
+ * other than the subagent can share this file.
+ *
+ * Revision History:
+ * 07/27/1997 Steve Ross Created
+ *
+ *
+ *
+ *-----------------------------------------------------------------------*/
+
+#ifndef __NSLAGTCOM_NT_H_
+#define __NSLMAGTCOM_NT_H_
+
+/*-------------------------------------------------------------------------
+ *
+ * Defines
+ *
+ *-----------------------------------------------------------------------*/
+
+#define MAGT_NSEV_SNMPTRAP "NSEV_SNMPTRAP_LDAP"
+
+#endif /* __NSLAGTCOM_NT_H_ */
diff --git a/ldap/servers/snmp/ntagt/nsldapagt_nt.c b/ldap/servers/snmp/ntagt/nsldapagt_nt.c
new file mode 100644
index 00000000..268ee27e
--- /dev/null
+++ b/ldap/servers/snmp/ntagt/nsldapagt_nt.c
@@ -0,0 +1,1738 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+
+/*-------------------------------------------------------------------------
+ *
+ * nsldapagt_nt.c - SNMP Extension Agent for Directory Server on NT.
+ * Provides SNMP data to NT SNMP Service on behalf of the
+ * Directory Server installed on the current system. SNMP
+ * data is collected from the following sources:
+ * 1. config file (static data)
+ * 2. daemonstats file (dynamic data)
+ *
+ * Revision History:
+ * 07/25/1997 Steve Ross Created
+ *
+ *
+ *-----------------------------------------------------------------------*/
+
+
+#include <ctype.h>
+#include <string.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <io.h>
+#include <windows.h>
+#include <winsock.h>
+#include <time.h>
+#include <snmp.h>
+#include "nt/regparms.h"
+#include "agtmmap.h"
+#include "nslagtcom_nt.h"
+#include "nsldapmib_nt.h"
+#include "nsldapagt_nt.h"
+#include "ldap.h"
+
+/*-------------------------------------------------------------------------
+ *
+ * Defines
+ *
+ *-----------------------------------------------------------------------*/
+
+#define REPLACE(x, y) do {if ((x)) SNMP_free((x));\
+ (x) = SNMP_malloc(strlen((y)) + 1);\
+ if ((x)) strcpy((x), (y));} while(0)
+
+#define export extern "C"
+
+/*-------------------------------------------------------------------------
+ *
+ * Globals
+ *
+ *-----------------------------------------------------------------------*/
+
+instance_list_t *pInstanceList = NULL;
+
+/*
+ * Extension Agent DLLs need access to elapsed time agent has been active.
+ * This is implemented by initializing the Extension Agent with a time zero
+ * reference, and allowing the agent to compute elapsed time by subtracting
+ * the time zero reference from the current system time. This Extension
+ * Agent implements this reference with dwTimeZero.
+ */
+DWORD dwTimeZero = 0;
+
+/*-------------------------------------------------------------------------
+ *
+ * Externs
+ *
+ *-----------------------------------------------------------------------*/
+
+extern AsnObjectIdentifier MIB_OidPrefix;
+extern UINT MIB_num_vars;
+
+/*------------------------ prototypes -----------------------------------*/
+// char *getRootDirFromConfFile(char *filename, char *szLogPath);
+char *getRootDirFromConfFile(char *filename);
+
+/*-------------------------------------------------------------------------
+ *
+ * MagtInitInstance: initializes entry in instance list
+ *
+ *
+ *
+ *
+ *
+ *-----------------------------------------------------------------------*/
+
+ int MagtInitInstance(instance_list_t *pInstance)
+ {
+
+
+ pInstance->ghTrapEvent = NULL;
+ pInstance->pOpsStatInfo = NULL;
+ pInstance->pEntriesStatInfo = NULL;
+ pInstance->ppIntStatInfo = NULL;
+ pInstance->pMibInfo = NULL;
+ pInstance->oldUpdateTime = 0;
+ pInstance->oldStartTime = 0;
+ pInstance->graceCycles = MAGT_TIME_QUANTUM;
+ pInstance->mmapStale = MAGT_FALSE;
+ pInstance->mmapOk = MAGT_FALSE;
+ pInstance->serverUp = MAGT_FALSE;
+ pInstance->downTrapSent = MAGT_FALSE;
+ pInstance->trapType = MAGT_TRAP_NONE;
+
+ return 0;
+ }
+
+ int MagtInitStats(MagtOpsTblInfo_t *OpsTblInfo,
+ MagtEntriesTblInfo_t *EntriesTblInfo,
+ MagtIntTblInfo_t **IntTblInfo )
+ {
+ int i;
+
+ if (OpsTblInfo != NULL)
+ {
+ OpsTblInfo->AnonymousBinds = 0;
+ OpsTblInfo->UnAuthBinds = 0;
+ OpsTblInfo->SimpleAuthBinds = 0;
+ OpsTblInfo->StrongAuthBinds = 0;
+ OpsTblInfo->BindSecurityErrors = 0;
+ OpsTblInfo->InOps = 0;
+ OpsTblInfo->ReadOps = 0;
+ OpsTblInfo->CompareOps = 0;
+ OpsTblInfo->AddEntryOps = 0;
+ OpsTblInfo->RemoveEntryOps = 0;
+ OpsTblInfo->ModifyEntryOps = 0;
+ OpsTblInfo->ModifyRDNOps = 0;
+ OpsTblInfo->ListOps = 0;
+ OpsTblInfo->SearchOps = 0;
+ OpsTblInfo->OneLevelSearchOps = 0;
+ OpsTblInfo->WholeSubtreeSearchOps = 0;
+ OpsTblInfo->Referrals = 0;
+ OpsTblInfo->Chainings = 0;
+ OpsTblInfo->SecurityErrors = 0;
+ OpsTblInfo->Errors = 0;
+ }
+
+ if(EntriesTblInfo != NULL)
+ {
+ EntriesTblInfo->MasterEntries = 0;
+ EntriesTblInfo->CopyEntries = 0;
+ EntriesTblInfo->CacheEntries = 0;
+ EntriesTblInfo->CacheHits = 0;
+ EntriesTblInfo->SlaveHits = 0;
+ }
+
+ if(IntTblInfo != NULL)
+ {
+ for(i=0; i < NUM_SNMP_INT_TBL_ROWS; i++)
+ {
+ strcpy(IntTblInfo[i]->DsName.val, "Not Available");
+ IntTblInfo[i]->DsName.len = strlen("Not Available");
+
+ IntTblInfo[i]->TimeOfCreation = 0;
+ IntTblInfo[i]->TimeOfLastAttempt = 0;
+ IntTblInfo[i]->TimeOfLastSuccess = 0;
+ IntTblInfo[i]->FailuresSinceLastSuccess = 0;
+ IntTblInfo[i]->Failures = 0;
+ IntTblInfo[i]->Successes = 0;
+
+ strcpy(IntTblInfo[i]->URL.val, "Not Available");
+ IntTblInfo[i]->URL.len = strlen("Not Available");
+ }
+ }
+
+ return 0;
+ }
+
+/*-------------------------------------------------------------------------
+ *
+ * MagtCheckServer: Checks the Server's status and indicates
+ * which trap is to be generated if necessary.
+ *
+ * Returns: MAGT_TRAP_NONE - No trap to be generated
+ * Trap # - Id of trap to be generated
+ *
+ *-----------------------------------------------------------------------*/
+
+int MagtCheckServer(instance_list_t *pInstance)
+{
+ int err;
+
+ if (pInstance->mmapStale == MAGT_TRUE)
+ pInstance->mmapOk = MAGT_FALSE; /* Ensure open of mmap */
+
+ err = MagtReadStats(&(pInstance->hdrInfo),
+ NULL,
+ NULL,
+ NULL,
+ pInstance->szStatsPath,
+ pInstance->szLogPath); /* Find times info in hdr */
+
+ if (pInstance->mmapOk == MAGT_FALSE)
+ {
+ if (err != 0) /* Cannot open mmap file */
+ {
+ if ((pInstance->serverUp == MAGT_TRUE) || /* Server status changes */
+ (pInstance->downTrapSent == MAGT_FALSE)) /* Down trap was not sent */
+ {
+ pInstance->serverUp = MAGT_FALSE;
+ pInstance->downTrapSent = MAGT_TRUE;
+ pInstance->trapType = MAGT_TRAP_SERVER_DOWN;
+ }
+ }
+ else
+ {
+ pInstance->mmapOk = MAGT_TRUE;
+
+ /*
+ * Since mmapOk was false, it means the mmap file couldn't be
+ * opened before. Now it is opened ok, so it will be assumed
+ * that the server has gone down and up and a start trap may need
+ * to be sent.
+ */
+ if (pInstance->mmapStale == MAGT_FALSE)
+ pInstance->serverUp = MAGT_FALSE;
+ else
+ pInstance->mmapStale = MAGT_FALSE; /* Not stale anymore */
+ }
+ }
+
+ if (pInstance->trapType == MAGT_TRAP_NONE)
+ {
+ if (err != 0)
+ {
+ pInstance->mmapOk = MAGT_FALSE;
+
+ /*
+ * If the mmap file does not exist, assume server has gone down.
+ */
+ if (err == ENOENT)
+ {
+ if((pInstance->serverUp == MAGT_TRUE) || /* Server status changes */
+ (pInstance->downTrapSent == MAGT_FALSE)) /* Down trap was not sent */
+ {
+ pInstance->serverUp = MAGT_FALSE;
+ pInstance->downTrapSent = MAGT_TRUE;
+ pInstance->trapType = MAGT_TRAP_SERVER_DOWN;
+ }
+ }
+ }
+ else /* Got hdr info ok */
+ {
+
+ /*
+ * The fact that header info can be read will be taken as the
+ * server is up. If it was not up before, a server start trap
+ * will need to be sent.
+ */
+ if (((pInstance->hdrInfo.restarted) || (pInstance->hdrInfo.startTime > pInstance->oldStartTime))
+ && (pInstance->hdrInfo.updateTime > pInstance->oldUpdateTime))
+ {
+ pInstance->oldUpdateTime = pInstance->hdrInfo.updateTime;
+ pInstance->oldStartTime = pInstance->hdrInfo.startTime;
+ pInstance->graceCycles = MAGT_TIME_QUANTUM;
+ pInstance->serverUp = MAGT_TRUE;
+ pInstance->downTrapSent = MAGT_FALSE;
+ pInstance->trapType = MAGT_TRAP_SERVER_START;
+ }
+ else
+ {
+ if (pInstance->hdrInfo.updateTime > pInstance->oldUpdateTime)
+ {
+ pInstance->oldUpdateTime = pInstance->hdrInfo.updateTime;
+ if (pInstance->graceCycles == 0)
+ {
+
+ /*
+ * The server has probably been stuck and has been restarted.
+ */
+ pInstance->serverUp = MAGT_TRUE;
+ pInstance->downTrapSent = MAGT_FALSE;
+ pInstance->trapType = MAGT_TRAP_SERVER_START;
+ }
+
+ /*
+ * Reset grace cycles in either case because server is healthy.
+ */
+ pInstance->graceCycles = MAGT_TIME_QUANTUM;
+ }
+ else /* Mmap file not updated */
+ {
+ pInstance->mmapStale = MAGT_TRUE;
+
+ /*
+ * The server is not responding, send trap if one has not
+ * been sent yet.
+ */
+ if (pInstance->graceCycles > 0)
+ {
+ pInstance->graceCycles--;
+ if (pInstance->graceCycles == 0)
+ {
+ pInstance->trapType = MAGT_TRAP_SERVER_DOWN;
+ }
+ }
+ }
+ }
+ }
+ }
+
+ return pInstance->trapType;
+}
+
+
+/*-------------------------------------------------------------------------
+ *
+ * MagtCleanUp: Cleans up any allocated global resources.
+ *
+ * Returns: None
+ *
+ *-----------------------------------------------------------------------*/
+
+void MagtCleanUp()
+{
+ instance_list_t *pInstance;
+
+ for (pInstance = pInstanceList; pInstance; pInstance = pInstance->pNext)
+ {
+ if (pInstance->pCfgInfo != NULL)
+ GlobalFree(pInstance->pCfgInfo);
+
+ if (pInstance->pMibInfo != NULL)
+ GlobalFree(pInstance->pMibInfo);
+
+ if (pInstance->szRootDir != NULL)
+ GlobalFree(pInstance->szRootDir);
+
+ if (pInstance->ghTrapEvent != NULL)
+ CloseHandle(pInstance->ghTrapEvent);
+ }
+
+}
+
+/*-------------------------------------------------------------------------
+ *
+ * MagtConfProcess: Processes a configuration entry and updates the
+ * corresponding static info field.
+ *
+ * Returns: None
+ *
+ *-----------------------------------------------------------------------*/
+
+void MagtConfProcess(char *line, int lineLen,
+ MagtLDAPInfo_t *info)
+
+{
+ char keyWord[MAGT_MAX_LINELEN + 1];
+ char *val, *p;
+
+ if (line == NULL) /* Shouldn't happen */
+ return;
+
+ if ((*line) == '#') /* Comment - Ignore */
+ return;
+
+ keyWord[0] = '\0';
+
+ if (sscanf(line, "%s", keyWord) != 1) /* Partial entry */
+ return;
+
+ val = line;
+
+ /*
+ * Go past any spaces preceding the keyword.
+ */
+ while ((*val) && (isspace(*val)))
+ ++val;
+
+ if (!(*val))
+ return;
+
+ /*
+ * Go past the keyword.
+ */
+ for (; (*val) && !(isspace(*val)); ++val);
+
+ if (!(*val))
+ return;
+
+ /*
+ * Go past the spaces that follow the key word.
+ */
+ while ((*val) && (isspace(*val)))
+ ++val;
+
+ if (!(*val))
+ return;
+
+ /*
+ * Strip CRLF characters.
+ */
+ if ((p = strchr(val, '\r')) != NULL)
+ *p = '\0';
+ if ((p = strchr(val, '\n')) != NULL)
+ *p = '\0';
+
+ /*
+ * Now val points to the value and keyWord points to the key word.
+ */
+
+ if (!stricmp(keyWord, "nsslapd-port:"))
+ {
+ info->port = atoi(val);
+ return;
+ }
+
+ if (!stricmp(keyWord, "nsslapd-localhost:"))
+ {
+ REPLACE(info->host, val);
+ info->host[strlen(info->host)] = '\0';
+ return;
+ }
+
+ if (!stricmp(keyWord, "nsslapd-rootdn:"))
+ {
+ REPLACE(info->rootdn, val);
+ info->rootdn[strlen(info->rootdn)] = '\0';
+ return;
+ }
+
+ if (!stricmp(keyWord, "nsslapd-rootpw:"))
+ {
+ REPLACE(info->rootpw, val);
+ info->rootpw[strlen(info->rootpw)] = '\0';
+ return;
+ }
+
+ /*
+ * None of the above? Invalid keyword. Just return.
+ */
+ return;
+}
+
+
+char *getRootDirFromConfFile(char *confpath)
+{
+ char *rootDir = NULL;
+ const char *config = "\\config\0" ;
+ char instanceDir[MAGT_MAX_LINELEN + 1] = "";
+ size_t len ;
+
+ if (confpath) {
+ len = strlen(confpath) - strlen(config) ;
+ strncpy(instanceDir, confpath, len);
+ rootDir = _strdup(instanceDir) ; // allocate memory for rootDir and set up to value pointed by instanceDir
+ return rootDir ;
+ }
+ else return NULL ;
+}
+
+
+/*-------------------------------------------------------------------------
+ *
+ * MagtInitMibStorage: Initializes the storage pointers of MIB variables.
+ *
+ * Returns: None
+ *
+ *-----------------------------------------------------------------------*/
+
+void MagtInitMibStorage(MagtMibEntry_t * MibInfo,
+ MagtOpsTblInfo_t * pOpsStatInfo,
+ MagtEntriesTblInfo_t * pEntriesStatInfo,
+ MagtIntTblInfo_t ** ppIntStatInfo,
+ MagtStaticInfo_t * pCfgInfo)
+{
+ switch(MibInfo->uId)
+ {
+ case MAGT_ID_DESC:
+ MibInfo->Storage = pCfgInfo->entityDescr.val;
+ break;
+ case MAGT_ID_VERS:
+ MibInfo->Storage = pCfgInfo->entityVers.val;
+ break;
+ case MAGT_ID_ORG:
+ MibInfo->Storage = pCfgInfo->entityOrg.val;
+ break;
+ case MAGT_ID_LOC:
+ MibInfo->Storage = pCfgInfo->entityLocation.val;
+ break;
+ case MAGT_ID_CONTACT:
+ MibInfo->Storage = pCfgInfo->entityContact.val;
+ break;
+ case MAGT_ID_NAME:
+ MibInfo->Storage = pCfgInfo->entityName.val;
+ break;
+ case MAGT_ID_APPLINDEX:
+ MibInfo->Storage = &pCfgInfo->ApplIndex;
+ break;
+
+ /* operations table attrs */
+ case MAGT_ID_ANONYMOUS_BINDS:
+ MibInfo->Storage = &pOpsStatInfo->AnonymousBinds;
+ break;
+ case MAGT_ID_UNAUTH_BINDS:
+ MibInfo->Storage = &pOpsStatInfo->UnAuthBinds;
+ break;
+ case MAGT_ID_SIMPLE_AUTH_BINDS:
+ MibInfo->Storage = &pOpsStatInfo->SimpleAuthBinds;
+ break;
+ case MAGT_ID_STRONG_AUTH_BINDS:
+ MibInfo->Storage = &pOpsStatInfo->StrongAuthBinds;
+ break;
+ case MAGT_ID_BIND_SECURITY_ERRORS:
+ MibInfo->Storage = &pOpsStatInfo->BindSecurityErrors;
+ break;
+ case MAGT_ID_IN_OPS:
+ MibInfo->Storage = &pOpsStatInfo->InOps;
+ break;
+ case MAGT_ID_READ_OPS:
+ MibInfo->Storage = &pOpsStatInfo->ReadOps;
+ break;
+ case MAGT_ID_COMPARE_OPS:
+ MibInfo->Storage = &pOpsStatInfo->CompareOps;
+ break;
+ case MAGT_ID_ADD_ENTRY_OPS:
+ MibInfo->Storage = &pOpsStatInfo->AddEntryOps;
+ break;
+ case MAGT_ID_REMOVE_ENTRY_OPS:
+ MibInfo->Storage = &pOpsStatInfo->RemoveEntryOps;
+ break;
+ case MAGT_ID_MODIFY_ENTRY_OPS:
+ MibInfo->Storage = &pOpsStatInfo->ModifyEntryOps;
+ break;
+ case MAGT_ID_MODIFY_RDN_OPS:
+ MibInfo->Storage = &pOpsStatInfo->ModifyRDNOps;
+ break;
+ case MAGT_ID_LIST_OPS:
+ MibInfo->Storage = &pOpsStatInfo->ListOps;
+ break;
+ case MAGT_ID_SEARCH_OPS:
+ MibInfo->Storage = &pOpsStatInfo->SearchOps;
+ break;
+ case MAGT_ID_ONE_LEVEL_SEARCH_OPS:
+ MibInfo->Storage = &pOpsStatInfo->OneLevelSearchOps;
+ break;
+ case MAGT_ID_WHOLE_SUBTREE_SEARCH_OPS:
+ MibInfo->Storage = &pOpsStatInfo->WholeSubtreeSearchOps;
+ break;
+ case MAGT_ID_REFERRALS:
+ MibInfo->Storage = &pOpsStatInfo->Referrals;
+ break;
+ case MAGT_ID_CHAININGS:
+ MibInfo->Storage = &pOpsStatInfo->Chainings;
+ break;
+ case MAGT_ID_SECURITY_ERRORS:
+ MibInfo->Storage = &pOpsStatInfo->SecurityErrors;
+ break;
+ case MAGT_ID_ERRORS:
+ MibInfo->Storage = &pOpsStatInfo->Errors;
+ break;
+ /* entries table attrs */
+ case MAGT_ID_MASTER_ENTRIES:
+ MibInfo->Storage = &pEntriesStatInfo->MasterEntries;
+ break;
+ case MAGT_ID_COPY_ENTRIES:
+ MibInfo->Storage = &pEntriesStatInfo->CopyEntries;
+ break;
+ case MAGT_ID_CACHE_ENTRIES:
+ MibInfo->Storage = &pEntriesStatInfo->CacheEntries;
+ break;
+ case MAGT_ID_CACHE_HITS:
+ MibInfo->Storage = &pEntriesStatInfo->CacheHits;
+ break;
+ case MAGT_ID_SLAVE_HITS:
+ MibInfo->Storage = &pEntriesStatInfo->SlaveHits;
+ break;
+ /* interaction table entries
+ *---------------------------------
+ * a little different because table of N entries, we can get current value of n
+ * from MibInfo->Oid.ids[MibInfo->Oid.idLength] because dsIntIndex is last,
+ * subtract 1 from it because oids go from 1 to NUM_SNMP_INT_TBL_ROWS array goes
+ * from 0 to NUM_SNMP_INT_TBL_ROWS - 1
+ * if this ever changes this logic will have to change to get it from
+ * appropriate spot
+ */
+ case MAGT_ID_DS_NAME:
+ MibInfo->Storage = ppIntStatInfo[MibInfo->Oid.ids[MibInfo->Oid.idLength - 1] - 1]->DsName.val;
+ break;
+ case MAGT_ID_TIME_OF_CREATION:
+ MibInfo->Storage = &(ppIntStatInfo[MibInfo->Oid.ids[MibInfo->Oid.idLength - 1] - 1]->TimeOfCreation);
+ break;
+ case MAGT_ID_TIME_OF_LAST_ATTEMPT:
+ MibInfo->Storage = &(ppIntStatInfo[MibInfo->Oid.ids[MibInfo->Oid.idLength - 1] - 1]->TimeOfLastAttempt);
+ break;
+ case MAGT_ID_TIME_OF_LAST_SUCCESS:
+ MibInfo->Storage = &(ppIntStatInfo[MibInfo->Oid.ids[MibInfo->Oid.idLength - 1] - 1]->TimeOfLastSuccess);
+ break;
+ case MAGT_ID_FAILURES_SINCE_LAST_SUCCESS:
+ MibInfo->Storage = &(ppIntStatInfo[MibInfo->Oid.ids[MibInfo->Oid.idLength - 1] - 1]->FailuresSinceLastSuccess);
+ break;
+ case MAGT_ID_FAILURES:
+ MibInfo->Storage = &(ppIntStatInfo[MibInfo->Oid.ids[MibInfo->Oid.idLength - 1] - 1]->Failures);
+ break;
+ case MAGT_ID_SUCCESSES:
+ MibInfo->Storage = &(ppIntStatInfo[MibInfo->Oid.ids[MibInfo->Oid.idLength - 1] - 1]->Successes);
+ break;
+ case MAGT_ID_URL:
+ MibInfo->Storage = ppIntStatInfo[MibInfo->Oid.ids[MibInfo->Oid.idLength - 1] - 1]->URL.val;
+ break;
+
+
+ default:
+ break;
+ }
+}
+
+/*-------------------------------------------------------------------------
+ *
+ * ReadStaticSettingsOverLdap: Reads static information from the directory server
+ *
+ *
+ * Returns: 0 - No error
+ * -1 - Errors
+ *
+ *-----------------------------------------------------------------------*/
+
+int ReadStaticSettingsOverLdap(MagtLDAPInfo_t ldapInfo, MagtStaticInfo_t *staticInfo, int *SNMPoff)
+{
+ LDAP *ld;
+ LDAPMessage *result, *e;
+ BerElement *ber;
+ char *a;
+ char **vals;
+ char *attrs[]={LDAP_ATTR_SNMP_ENABLED,
+ LDAP_ATTR_SNMP_DESCRIPTION,
+ LDAP_ATTR_SNMP_ORGANIZATION,
+ LDAP_ATTR_SNMP_LOCATION,
+ LDAP_ATTR_SNMP_CONTACT,
+ NULL};
+ /* set the applIndex to the ldap port */
+ staticInfo->ApplIndex = ldapInfo.port;
+
+ /* get rest of static settings from the Directory Server */
+ if ( ( ld = ldap_init( ldapInfo.host, ldapInfo.port ) ) == NULL )
+ {
+ return -1;
+ }
+
+ if ( ldap_simple_bind_s( ld, NULL, NULL) != LDAP_SUCCESS )
+ {
+ return -1;
+ }
+
+
+ if ( ldap_search_s( ld, LDAP_CONFIG_DN, LDAP_SCOPE_BASE, BASE_OBJECTCLASS_SEARCH,
+ attrs, 0, &result ) != LDAP_SUCCESS )
+ {
+ return -1;
+
+ }else{
+
+ e = ldap_first_entry( ld, result );
+
+ if(e != NULL)
+ {
+ for ( a = ldap_first_attribute( ld, e, &ber );
+ a != NULL; a = ldap_next_attribute( ld, e, ber ) )
+ {
+ if ((vals = ldap_get_values( ld, e, a)) != NULL )
+ {
+ MagtDispStr_t *pStaticAttr=NULL;
+ /* we only want the first value, ignore any others */
+ if( 0 == strcmp(LDAP_ATTR_SNMP_ENABLED, a) )
+ {
+ if(0 == stricmp(SNMP_ON, vals[0]) )
+ {
+ *SNMPoff = 0;
+ }else{
+ *SNMPoff = 1;
+ }
+ }else if( 0 == strcmp(LDAP_ATTR_SNMP_DESCRIPTION, a) ){
+ pStaticAttr = &(staticInfo->entityDescr);
+ }else if( 0 == strcmp(LDAP_ATTR_SNMP_ORGANIZATION, a) ){
+ pStaticAttr = &(staticInfo->entityOrg);
+ }else if( 0 == strcmp(LDAP_ATTR_SNMP_LOCATION, a) ){
+ pStaticAttr = &(staticInfo->entityLocation);
+ }else if( 0 == strcmp(LDAP_ATTR_SNMP_CONTACT, a) ){
+ pStaticAttr = &(staticInfo->entityContact);
+ }
+ /* stevross: missing the following for NT
+ version
+ DSName
+ */
+
+ /* for Unix also missing
+ MasterHost, MasterPort
+ */
+ if(pStaticAttr != NULL && vals[0] != NULL)
+ {
+ REPLACE(pStaticAttr->val, vals[0]);
+ pStaticAttr->len = strlen(pStaticAttr->val);
+ }
+
+ ldap_value_free( vals );
+
+ }
+
+ ldap_memfree( a );
+
+ }
+
+ if ( ber != NULL )
+ {
+ ldap_ber_free( ber, 0 );
+ }
+ }
+ }
+
+ ldap_msgfree( result );
+ ldap_unbind( ld );
+
+ return 0;
+}
+
+/*-------------------------------------------------------------------------
+ *
+ * MagtLoadStaticInfo: Loads static information from the configuration
+ * file.
+ *
+ * Returns: 0 - No error
+ * -1 - Errors
+ *
+ *-----------------------------------------------------------------------*/
+
+int MagtLoadStaticInfo(MagtStaticInfo_t *staticInfo, char *pszRootDir, int *SNMPOff, char *pszLogPath)
+{
+ char confpath[MAX_PATH];
+ FILE *fp;
+ char linebuf[MAGT_MAX_LINELEN + 1];
+ int lineLen;
+ char logMsg[1024];
+ MagtLDAPInfo_t ldapInfo;
+
+ /*
+ * Set-up default values first.
+ */
+
+ staticInfo->entityDescr.val = NULL;
+ staticInfo->entityVers.val = NULL;
+ staticInfo->entityOrg.val = NULL;
+ staticInfo->entityLocation.val = NULL;
+ staticInfo->entityContact.val = NULL;
+ staticInfo->entityName.val = NULL;
+
+ staticInfo->ApplIndex = 0;
+
+ REPLACE(staticInfo->entityDescr.val, "Netscape Directory Server");
+ staticInfo->entityDescr.len = strlen(staticInfo->entityDescr.val);
+
+ REPLACE(staticInfo->entityVers.val, "7");
+ staticInfo->entityVers.len = strlen(staticInfo->entityVers.val);
+
+ REPLACE(staticInfo->entityOrg.val, "Not Available");
+ staticInfo->entityOrg.len = strlen(staticInfo->entityOrg.val);
+
+ REPLACE(staticInfo->entityLocation.val, "Not Available");
+ staticInfo->entityLocation.len = strlen(staticInfo->entityLocation.val);
+
+ REPLACE(staticInfo->entityContact.val, "Not Available");
+ staticInfo->entityContact.len = strlen(staticInfo->entityContact.val);
+
+ REPLACE(staticInfo->entityName.val, "Not Available");
+ staticInfo->entityName.len = strlen(staticInfo->entityName.val);
+
+ /*
+ * Read any config info from dse.ldif (for now its just port used as
+ * applIndex
+ */
+
+ wsprintf(confpath, "%s/%s/%s", pszRootDir, MAGT_CONFDIR, DSE_LDIF);
+
+ if ((fp = fopen(confpath, "r")) == (FILE *) NULL)
+ {
+ wsprintf(logMsg,
+ "Failed to open dse.ldif (error = %d)\n",
+ errno);
+ MagtLog(logMsg, pszLogPath);
+ return (-1);
+ }
+
+
+ while ((lineLen = MagtReadLine(linebuf, MAGT_MAX_LINELEN, fp)) > 0)
+ {
+ /*
+ * Update the configured entries.
+ */
+ MagtConfProcess(linebuf, lineLen, &ldapInfo);
+ }
+ fclose (fp);
+
+ if( 0 != ReadStaticSettingsOverLdap(ldapInfo, staticInfo, SNMPOff) < 0 )
+ {
+ wsprintf(logMsg,
+ "Failed to read SNMP configuration over ldap\n");
+ MagtLog(logMsg, pszLogPath);
+ return (-1);
+ }
+
+ return (0);
+}
+
+/*-------------------------------------------------------------------------
+ *
+ * MagtLog: Logs the specified message into the log file.
+ * Notes: Log file is opened and closed each time.
+ *
+ * Returns: None
+ *
+ *-----------------------------------------------------------------------*/
+
+void MagtLog(char *logMsg, char *pszLogPath)
+{
+ FILE *f;
+ char *szTime;
+
+ f = fopen(pszLogPath, "a");
+ if (!f)
+ return;
+ szTime = MagtLogTime();
+ if (szTime != NULL)
+ {
+ fprintf(f, "%s %s", szTime, logMsg);
+ SNMP_free(szTime);
+ }
+ else /* No time string returned */
+ {
+ fprintf(f, "%s %s", "00000000000000", logMsg);
+ }
+ fclose(f);
+}
+
+/*-------------------------------------------------------------------------
+ *
+ * MagtLogTime: Returns time for logging purpose.
+ *
+ * Returns: Formatted time string - No error
+ * "00000000000000" - Errors
+ *
+ *-----------------------------------------------------------------------*/
+
+char *MagtLogTime()
+{
+ time_t timeNow;
+ struct tm tmLocal;
+ char dateBuf[64];
+ char *timeStr = NULL;
+ static timeZoneSet = MAGT_FALSE;
+
+ timeNow = time(0);
+ memcpy(&tmLocal, localtime(&timeNow), sizeof(tmLocal));
+
+ /*
+ * Set up the timezone information.
+ */
+ if (!timeZoneSet)
+ {
+ tzset();
+ timeZoneSet = MAGT_TRUE;
+ }
+
+ /*
+ * Create the date string.
+ */
+ if (!strftime(dateBuf, 64, "%Y%m%d%H%M%S", &tmLocal))
+ {
+ strcpy(dateBuf, "00000000000000");
+ }
+ REPLACE(timeStr, dateBuf);
+
+ return timeStr;
+}
+
+/*-------------------------------------------------------------------------
+ *
+ * MagtOpenLog: Creates and opens the log file.
+ * Backs up any old log file.
+ *
+ * Returns: None
+ *
+ *-----------------------------------------------------------------------*/
+
+void MagtOpenLog(char *pszRootDir, char *pszLogPath)
+{
+ char logDir[MAX_PATH];
+ char oldPath[MAX_PATH];
+ int fd;
+
+ wsprintf(logDir, "%s\\%s", pszRootDir, "logs");
+ if (mkdir(logDir) != 0)
+ {
+ if (errno != EEXIST)
+ return;
+ }
+
+ wsprintf(pszLogPath, "%s\\%s", logDir, MAGT_LOGFILE);
+ wsprintf(oldPath, "%s\\%s%s", logDir, MAGT_LOGFILE, ".old");
+
+ /*
+ * Rename old log file to keep a back up.
+ */
+ unlink(oldPath);
+ rename(pszLogPath, oldPath);
+
+ /*
+ * Create and open new log file.
+ */
+ fd = open(pszLogPath,
+ _O_WRONLY | _O_CREAT | _O_TRUNC,
+ _S_IWRITE);
+ if (fd != -1)
+ close(fd);
+}
+
+/*-------------------------------------------------------------------------
+ *
+ * MagtReadLine: Reads one line of text (up to n chars) from specified
+ * file.
+ *
+ * Returns: Len read - No error
+ * -1 - Errors
+ *
+ *-----------------------------------------------------------------------*/
+
+int MagtReadLine(char *buf, int n, FILE *fp)
+{
+ if (fgets(buf, n, fp) != NULL)
+ {
+ return(strlen(buf));
+ }
+ else
+ {
+ return(-1);
+ }
+}
+
+
+/*-------------------------------------------------------------------------
+ *
+ * MagtReadStats: Reads statistics from stats file. The hdr and tbl data
+ * buffers will be filled in if they are not NULL.
+ *
+ * Returns: 0 - No errors
+ * errno - Errors
+ *
+ *-----------------------------------------------------------------------*/
+
+int MagtReadStats(MagtHdrInfo_t *hdrInfo,
+ MagtOpsTblInfo_t *OpsTblInfo,
+ MagtEntriesTblInfo_t *EntriesTblInfo,
+ MagtIntTblInfo_t **IntTblInfo,
+ char * pszStatsPath,
+ char * pszLogPath)
+{
+ int hdl;
+ int err;
+ int i;
+ struct agt_stats_t *pfile_stats;
+
+ if ((err = agt_mopen_stats(pszStatsPath, O_RDONLY, &hdl)) != 0)
+ {
+
+ /*
+ now with multiple instances this function gets called
+ on every snmprequest. Hence
+ logging here became too expensive, now let caller interpret
+ results and figure out if it should log something or not
+ */
+
+
+ return err;
+ }
+
+
+
+ if ( (hdl > 1) || (hdl < 0) )
+ {
+ return (EINVAL); /* Inavlid handle */
+ }
+
+ if ((mmap_tbl [hdl].maptype != AGT_MAP_READ) && (mmap_tbl [hdl].maptype != AGT_MAP_RDWR))
+ {
+ return (EINVAL); /* Inavlid handle */
+ }
+
+ if (mmap_tbl [hdl].fp <= (caddr_t) 0)
+ {
+ return (EFAULT); /* Something got corrupted */
+ }
+
+#if (0)
+ fprintf (stderr, "%s@%d> fp = %d\n", __FILE__, __LINE__, mmap_tbl [hdl].fp);
+#endif
+
+ pfile_stats = (struct agt_stats_t *) (mmap_tbl [hdl].fp);
+
+ /*
+ * Only fill in buffers if they are not NULL. This way, one can choose
+ * to get only the hdr info or only the tbl info.
+ */
+ if (hdrInfo != NULL)
+ {
+ hdrInfo->versMajor = pfile_stats->hdr_stats.hdrVersionMjr;
+ hdrInfo->versMinor = pfile_stats->hdr_stats.hdrVersionMnr;
+ hdrInfo->restarted = pfile_stats->hdr_stats.restarted;
+ hdrInfo->startTime = pfile_stats->hdr_stats.startTime;
+ hdrInfo->updateTime = pfile_stats->hdr_stats.updateTime;
+ }
+ if (OpsTblInfo != NULL){
+ OpsTblInfo->AnonymousBinds = pfile_stats->ops_stats.dsAnonymousBinds;
+ OpsTblInfo->UnAuthBinds = pfile_stats->ops_stats.dsUnAuthBinds;
+ OpsTblInfo->SimpleAuthBinds = pfile_stats->ops_stats.dsSimpleAuthBinds;
+ OpsTblInfo->StrongAuthBinds = pfile_stats->ops_stats.dsStrongAuthBinds;
+ OpsTblInfo->BindSecurityErrors = pfile_stats->ops_stats.dsBindSecurityErrors;
+ OpsTblInfo->InOps = pfile_stats->ops_stats.dsInOps;
+ OpsTblInfo->ReadOps = pfile_stats->ops_stats.dsReadOps;
+ OpsTblInfo->CompareOps = pfile_stats->ops_stats.dsCompareOps;
+ OpsTblInfo->AddEntryOps = pfile_stats->ops_stats.dsAddEntryOps;
+ OpsTblInfo->RemoveEntryOps = pfile_stats->ops_stats.dsRemoveEntryOps;
+ OpsTblInfo->ModifyEntryOps = pfile_stats->ops_stats.dsModifyEntryOps;
+ OpsTblInfo->ModifyRDNOps = pfile_stats->ops_stats.dsModifyRDNOps;
+ OpsTblInfo->ListOps = pfile_stats->ops_stats.dsListOps;
+ OpsTblInfo->SearchOps = pfile_stats->ops_stats.dsSearchOps;
+ OpsTblInfo->OneLevelSearchOps = pfile_stats->ops_stats.dsOneLevelSearchOps;
+ OpsTblInfo->WholeSubtreeSearchOps = pfile_stats->ops_stats.dsWholeSubtreeSearchOps;
+ OpsTblInfo->Referrals = pfile_stats->ops_stats.dsReferrals;
+ OpsTblInfo->Chainings = pfile_stats->ops_stats.dsChainings;
+ OpsTblInfo->SecurityErrors = pfile_stats->ops_stats.dsSecurityErrors;
+ OpsTblInfo->Errors = pfile_stats->ops_stats.dsErrors;
+ }
+ if(EntriesTblInfo != NULL){
+ EntriesTblInfo->MasterEntries = pfile_stats->entries_stats.dsMasterEntries;
+ EntriesTblInfo->CopyEntries = pfile_stats->entries_stats.dsCopyEntries;
+ EntriesTblInfo->CacheEntries = pfile_stats->entries_stats.dsCacheEntries;
+ EntriesTblInfo->CacheHits = pfile_stats->entries_stats.dsCacheHits;
+ EntriesTblInfo->SlaveHits = pfile_stats->entries_stats.dsSlaveHits;
+ }
+
+ if(IntTblInfo != NULL){
+ for(i=0; i < NUM_SNMP_INT_TBL_ROWS; i++)
+ {
+
+ strcpy(IntTblInfo[i]->DsName.val, pfile_stats->int_stats[i].dsName);
+ IntTblInfo[i]->DsName.len = strlen(pfile_stats->int_stats[i].dsName);
+
+ IntTblInfo[i]->TimeOfCreation = pfile_stats->int_stats[i].dsTimeOfCreation;
+ IntTblInfo[i]->TimeOfLastAttempt = pfile_stats->int_stats[i].dsTimeOfLastAttempt;
+ IntTblInfo[i]->TimeOfLastSuccess = pfile_stats->int_stats[i].dsTimeOfLastSuccess;
+ IntTblInfo[i]->FailuresSinceLastSuccess = pfile_stats->int_stats[i].dsFailuresSinceLastSuccess;
+ IntTblInfo[i]->Failures = pfile_stats->int_stats[i].dsFailures;
+ IntTblInfo[i]->Successes = pfile_stats->int_stats[i].dsSuccesses;
+ strcpy(IntTblInfo[i]->URL.val, pfile_stats->int_stats[i].dsURL);
+ IntTblInfo[i]->URL.len = strlen(pfile_stats->int_stats[i].dsURL);
+
+ }
+ }
+
+ agt_mclose_stats(hdl);
+ return 0;
+}
+
+/*-------------------------------------------------------------------------
+ *
+ * DllMain: Standard WIN32 DLL entry point.
+ *
+ * Returns: TRUE
+ *
+ *-----------------------------------------------------------------------*/
+
+BOOL WINAPI DllMain(HANDLE hDll, DWORD dwReason, LPVOID lpReserved)
+{
+
+ switch(dwReason)
+ {
+ case DLL_PROCESS_ATTACH:
+ case DLL_THREAD_ATTACH:
+ case DLL_THREAD_DETACH:
+ break;
+ case DLL_PROCESS_DETACH:
+ MagtCleanUp();
+ break;
+ default:
+ break;
+ }
+
+ return TRUE;
+}
+
+/*-------------------------------------------------------------------------
+ *
+ * SnmpExtensionInit: Entry point to coordinate the initializations of the
+ * Extension Agent and the Extendible Agent. The
+ * Extendible Agent provides the Extension Agent with a
+ * time zero reference; and the Extension Agent
+ * provides the Extendible Agent with an Event handle
+ * for communicating occurence of traps, and an object
+ * identifier representing the root of the MIB subtree
+ * that the Extension Agent supports.
+ *
+ * Returns: TRUE - No error
+ * FALSE - Errors
+ *
+ *-----------------------------------------------------------------------*/
+
+BOOL WINAPI SnmpExtensionInit(IN DWORD dwTimeZeroReference,
+ OUT HANDLE *hPollForTrapEvent,
+ OUT AsnObjectIdentifier *supportedView)
+{
+ int nMibIndex = 0;
+ SECURITY_ATTRIBUTES sa;
+ PSECURITY_ATTRIBUTES psa = NULL;
+ SECURITY_DESCRIPTOR sd;
+ char logMsg[1024];
+ int i;
+
+ instance_list_t *pInstance;
+
+ /*
+ * Record the time reference provided by the Extendible Agent.
+ */
+
+ dwTimeZero = dwTimeZeroReference;
+
+ /*
+ * Create a security descriptor that gives everyone access to the
+ * trap event. This is so that the SNMP process can set the event
+ * when it detects that the server is up or down. Without this
+ * relaxed ACL, the SNMP process which usually runs as the Netscape
+ * DS user can not set the trap event created by this DLL which is
+ * loaded by the Extendible Agent, which usually runs as LocalSystem.
+ */
+ if (InitializeSecurityDescriptor(&sd,
+ SECURITY_DESCRIPTOR_REVISION) == TRUE)
+ {
+ if (SetSecurityDescriptorDacl(&sd, TRUE, NULL, FALSE) == TRUE)
+ {
+ sa.nLength = sizeof(SECURITY_ATTRIBUTES);
+ sa.bInheritHandle = TRUE;
+ sa.lpSecurityDescriptor = &sd;
+ psa = &sa;
+ }
+ }
+
+ /*
+ * Create an event that will be used to communicate the occurence of
+ * traps to the Extendible Agent.
+ * The event will have a signaled initial state so that the status of
+ * the server can be checked as soon as the subagent is loaded and
+ * the necessary trap will be generated.
+ */
+ if ((*hPollForTrapEvent = CreateEvent(psa,
+ FALSE,
+ FALSE,
+ MAGT_NSEV_SNMPTRAP)) == NULL)
+ {
+ return FALSE;
+ }
+
+ /*
+ * Indicate the MIB view supported by this Extension Agent, an object
+ * identifier representing the sub root of the MIB that is supported.
+ */
+ *supportedView = MIB_OidPrefix;
+
+
+ /*
+ * Initialize globals.
+ */
+
+ if ( !_FindNetscapeServers() )
+ {
+ return FALSE;
+ }
+
+ for (pInstance = pInstanceList; pInstance; pInstance = pInstance->pNext)
+ {
+ MagtInitInstance(pInstance);
+ }
+
+ for (pInstance = pInstanceList; pInstance; pInstance = pInstance->pNext)
+ {
+ /* build the mib */
+
+ if ((pInstance->pOpsStatInfo = (MagtOpsTblInfo_t *) GlobalAlloc(GPTR,
+ sizeof(MagtOpsTblInfo_t))) == NULL)
+ {
+ wsprintf(logMsg, "Failed to allocate ops stats structure (error = %d)\n",
+ GetLastError());
+ MagtLog(logMsg, pInstance->szLogPath);
+ return FALSE;
+ }
+
+ if ((pInstance->pEntriesStatInfo = (MagtEntriesTblInfo_t *) GlobalAlloc(GPTR,
+ sizeof(MagtEntriesTblInfo_t))) == NULL)
+ {
+ wsprintf(logMsg, "Failed to allocate entries stat structure (error = %d)\n",
+ GetLastError());
+ MagtLog(logMsg, pInstance->szLogPath);
+ return FALSE;
+ }
+
+ if ((pInstance->ppIntStatInfo = (MagtIntTblInfo_t **) GlobalAlloc(GPTR,
+ NUM_SNMP_INT_TBL_ROWS * sizeof(MagtIntTblInfo_t *))) == NULL)
+ {
+ wsprintf(logMsg, "Failed to allocate interaction stat structure (error = %d)\n",
+ GetLastError());
+ MagtLog(logMsg, pInstance->szLogPath);
+ return FALSE;
+ }
+
+ for(i =0; i < NUM_SNMP_INT_TBL_ROWS; i++)
+ {
+ pInstance->ppIntStatInfo[i] = (MagtIntTblInfo_t *) GlobalAlloc(GPTR,
+ sizeof(MagtIntTblInfo_t));
+
+ /* make the static char for name and url so they have one address for later use */
+ pInstance->ppIntStatInfo[i]->DsName.val = (char *) GlobalAlloc(GPTR,
+ 100 * sizeof(char));
+ pInstance->ppIntStatInfo[i]->URL.val = (char *) GlobalAlloc(GPTR,
+ 100 * sizeof(char));
+ }
+
+ /* initialize the stats we just allocated */
+ MagtInitStats(pInstance->pOpsStatInfo,
+ pInstance->pEntriesStatInfo,
+ pInstance->ppIntStatInfo);
+
+
+ if( Mib_init(&(pInstance->pMibInfo), pInstance->pCfgInfo->ApplIndex) == -1)
+ {
+ wsprintf(logMsg, "Failed to create Mib structure (error = %d)\n",
+ GetLastError());
+ MagtLog(logMsg, pInstance->szLogPath);
+ return FALSE;
+ }
+
+ for (nMibIndex = 0; nMibIndex < (int) MIB_num_vars; nMibIndex++)
+ {
+ MagtInitMibStorage(&(pInstance->pMibInfo[nMibIndex]),
+ pInstance->pOpsStatInfo,
+ pInstance->pEntriesStatInfo,
+ pInstance->ppIntStatInfo,
+ pInstance->pCfgInfo);
+ }
+
+
+ /*
+ * Construct the path to stats file.
+ */
+ wsprintfA(pInstance->szStatsPath, "%s/logs/%s", pInstance->szRootDir,
+ AGT_STATS_FILE);
+
+ if (MagtReadStats(NULL, pInstance->pOpsStatInfo,
+ pInstance->pEntriesStatInfo,
+ pInstance->ppIntStatInfo,
+ pInstance->szStatsPath,
+ pInstance->szLogPath) != 0)
+ {
+ wsprintf(logMsg,
+ "Failed to open Memory Mapped Stats File. Make sure ns-slapd is running\n",
+ GetLastError());
+ MagtLog(logMsg, pInstance->szLogPath);
+ }
+
+
+ }
+
+ /* now that all mib's set up set next pointer from last entry to first
+ entry of next instance */
+
+ for (nMibIndex = 0; nMibIndex < (int) MIB_num_vars; nMibIndex++)
+ {
+
+ for (pInstance = pInstanceList; pInstance; pInstance = pInstance->pNext)
+ {
+ if(pInstance->pNext != NULL)
+ {
+ pInstance->pMibInfo[nMibIndex].MibNext = &(pInstance->pNext->pMibInfo[nMibIndex]);
+ }else{
+ if (nMibIndex + 1 != (int) MIB_num_vars)
+ {
+ pInstance->pMibInfo[nMibIndex].MibNext = &(pInstanceList->pMibInfo[nMibIndex + 1]);
+ }
+ }
+ }
+
+
+ }
+
+ /*
+ * Set event to have SnmpExtensionTrap invoked for initial check of
+ * Server status.
+ */
+ if (SetEvent(*hPollForTrapEvent) == FALSE)
+ {
+ /* don't have a specific instance to log it to, find something better to do later */
+ }
+
+ return TRUE;
+}
+
+/*-------------------------------------------------------------------------
+ *
+ * SnmpExtensionTrap: Entry point to communicate traps to the Extendible
+ * Agent. The Extendible Agent will query this entry
+ * point when the trap event (supplied at initialization
+ * time) is asserted, which indicates that zero or more
+ * traps may have occured. The Extendible Agent will
+ * repeatedly call this entry point until FALSE is
+ * returned, indicating that all outstanding traps have
+ * been processed.
+ *
+ * Returns: TRUE - Valid trap data
+ * FALSE - No trap data
+ *
+ *-----------------------------------------------------------------------*/
+
+BOOL WINAPI SnmpExtensionTrap(OUT AsnObjectIdentifier *enterprise,
+ OUT AsnInteger *genericTrap,
+ OUT AsnInteger *specificTrap,
+ OUT AsnTimeticks *timeStamp,
+ OUT RFC1157VarBindList *variableBindings)
+{
+ static UINT oidList[] = {1, 3, 6, 1, 4, 1, 1450};
+ static UINT oidListLen = MAGT_OID_SIZEOF(oidList);
+ static RFC1157VarBind *trapVars = NULL;
+ static MagtTrapTask_t trapTask = MAGT_TRAP_GENERATION;
+ int nVarLen;
+ char logMsg[1024];
+ instance_list_t *pInstance;
+
+
+ if (trapTask == MAGT_TRAP_CLEANUP)
+ {
+ if (variableBindings->list != NULL)
+ SNMP_FreeVarBind(variableBindings->list);
+
+ trapTask = MAGT_TRAP_GENERATION;
+ }
+
+
+ if (trapTask == MAGT_TRAP_GENERATION)
+ {
+
+ for (pInstance = pInstanceList; pInstance; pInstance = pInstance->pNext)
+ {
+ MagtCheckServer(pInstance);
+
+ /*
+ * If there is no trap to be generated for this instance keep looking at other
+ * instances.
+ */
+ if (pInstance->trapType == MAGT_TRAP_NONE)
+ {
+ continue;
+ }
+
+ enterprise->ids = (UINT *) SNMP_malloc(sizeof(UINT) * oidListLen);
+ if (enterprise->ids == NULL)
+ {
+ wsprintf(logMsg,
+ "Failed to allocate enterprise id\n");
+ MagtLog(logMsg, pInstance->szLogPath);
+ return FALSE;
+ }
+ enterprise->idLength = oidListLen;
+ memcpy(enterprise->ids, oidList, sizeof(UINT) * oidListLen);
+
+ *genericTrap = SNMP_GENERICTRAP_ENTERSPECIFIC;
+ *specificTrap = pInstance->trapType;
+ *timeStamp = GetCurrentTime() - dwTimeZero;
+
+ /*
+ * Set up the variable binding list with variables specified in the MIB
+ * for each trap.
+ */
+ if ((trapVars = SNMP_malloc(sizeof(RFC1157VarBind) * 4)) == NULL)
+ {
+ wsprintf(logMsg,
+ "Failed to allocate trap variables\n");
+ MagtLog(logMsg, pInstance->szLogPath);
+ SNMP_oidfree(enterprise);
+ return FALSE;
+ }
+
+ if ((nVarLen = MagtFillTrapVars(pInstance->trapType, trapVars, pInstance->pCfgInfo)) == 0)
+ {
+ wsprintf(logMsg,
+ "Failed to fill trap variables\n");
+ MagtLog(logMsg, pInstance->szLogPath);
+ SNMP_free(trapVars);
+ SNMP_oidfree(enterprise);
+ return FALSE;
+ }
+
+ variableBindings->list = trapVars;
+ variableBindings->len = nVarLen;
+
+ trapTask = MAGT_TRAP_CLEANUP;
+
+ wsprintf(logMsg,
+ "Sending trap %d\n",
+ pInstance->trapType);
+ MagtLog(logMsg, pInstance->szLogPath);
+
+ /* reset the trap type for this instance */
+ pInstance->trapType = MAGT_TRAP_NONE;
+
+ /*
+ * Indicate that a trap should be sent and parameters contain valid
+ * data.
+ */
+ return TRUE;
+ }
+
+
+ }
+
+ /*
+ * Indicate that no more traps are available and parameters do not
+ * refer to any valid data.
+ */
+
+ return FALSE;
+}
+
+/*-------------------------------------------------------------------------
+ *
+ * SnmpExtensionQuery: Entry point to resolve queries for MIB variables in
+ * their supported MIB view (supplied at
+ * initialization time). The supported requestType is
+ * Get/GetNext.
+ *
+ * Returns: TRUE - No error
+ * FALSE - Errors
+ *
+ *-----------------------------------------------------------------------*/
+
+BOOL WINAPI SnmpExtensionQuery(IN BYTE requestType,
+ IN OUT RFC1157VarBindList *variableBindings,
+ OUT AsnInteger *errorStatus,
+ OUT AsnInteger *errorIndex)
+{
+ static time_t lastChkTime = 0;
+ UINT i;
+ HANDLE hTrapEvent;
+ time_t currTime;
+
+ /*
+ * Check for valid input.
+ */
+
+ if (variableBindings == NULL ||
+ errorStatus == NULL ||
+ errorIndex == NULL)
+ {
+ return FALSE;
+ }
+
+ /*
+ * Iterate through the variable bindings list to resolve individual
+ * variable bindings.
+ */
+
+ for (i = 0; i < variableBindings->len; i++)
+ {
+ *errorStatus = MagtResolveVarBind(&variableBindings->list[i],
+ requestType);
+
+ /*
+ * Test and handle case where GetNext past end of MIB view supported by
+ * this Extension Agent occurs. Special processing is required to
+ * communicate this situation to the Extendible Agent so it can take
+ * appropriate action.
+ */
+ if (*errorStatus == SNMP_ERRORSTATUS_NOSUCHNAME &&
+ requestType == MAGT_MIB_ACTION_GETNEXT)
+ {
+ *errorStatus = SNMP_ERRORSTATUS_NOERROR;
+
+ /*
+ * Modify variable binding of such variables so the OID points just
+ * outside the MIB view supported by this Extension Agent. The
+ * Extendible Agent tests for this, and takes appropriate action.
+ */
+ SNMP_oidfree(&variableBindings->list[i].name);
+ SNMP_oidcpy(&variableBindings->list[i].name, &MIB_OidPrefix);
+ variableBindings->list[i].name.ids[MAGT_MIB_PREFIX_LEN - 1]++;
+ }
+
+ /*
+ * If an error was indicated, communicate error status and error index
+ * to the Extendible Agent. The Extendible Agent will ensure that the
+ * original variable bindings are returned in the response packet.
+ */
+ if (*errorStatus != SNMP_ERRORSTATUS_NOERROR)
+ {
+ *errorIndex = i + 1;
+ return FALSE;
+ }
+ }
+
+ /*
+ * Before going back, set the trap event so server status can be checked
+ * to see if a trap needs to be generated. This is to cover the case the
+ * SNMP process is unable to set the trap event because it is stuck.
+ */
+ currTime = time(0);
+
+ /*
+ * If just check status, do not generate event again.
+ */
+ if ((currTime - lastChkTime) >= MAGT_TIME_QUANTUM * 3)
+ {
+ if ((hTrapEvent = OpenEvent(EVENT_MODIFY_STATE, FALSE,
+ (LPCTSTR)MAGT_NSEV_SNMPTRAP)) != NULL)
+ SetEvent(hTrapEvent);
+
+ lastChkTime = currTime;
+ }
+
+ return TRUE;
+}
+
+
+/* --- Open Function --------------------------------------------------------------------- */
+
+
+/* _FindNetscapeServers()
+ * Function to loop through registry looking for netscape servers
+ * Stores them into pInstanceList as it finds them.
+ */
+
+#define MAX_KEY_SIZE 128
+DWORD
+_FindNetscapeServers()
+{
+ LONG regStatus,
+ status;
+ HKEY hKeyNetscape = NULL,
+ hKeyNetscapeConf;
+ DWORD dwKey,
+ type,
+ dwServerKeySize,
+ size,
+ dwServerCount = 0;
+ WCHAR szServerKeyName[MAX_KEY_SIZE],
+ szPath[MAX_KEY_SIZE];
+ FILETIME fileTime;
+ instance_list_t *pNew;
+ instance_list_t *pCurrent;
+ DWORD iUniqueID = 0;
+ char logMsg[1024];
+ regStatus = RegOpenKeyEx(
+ HKEY_LOCAL_MACHINE,
+ TEXT(KEY_SOFTWARE_NETSCAPE) TEXT("\\") TEXT(DS_KEY_ROOT),
+ 0L,
+ KEY_ALL_ACCESS,
+ &hKeyNetscape);
+
+ if (regStatus != ERROR_SUCCESS) {
+ goto ExitPoint;
+ }
+
+ dwKey = 0;
+ do {
+ dwServerKeySize = MAX_KEY_SIZE;
+ regStatus = RegEnumKeyEx(
+ hKeyNetscape,
+ dwKey,
+ (char *) szServerKeyName,
+ &dwServerKeySize,
+ NULL,
+ 0,
+ 0,
+ &fileTime);
+ dwKey++;
+
+ if (regStatus == ERROR_SUCCESS) {
+
+ regStatus = RegOpenKeyEx(
+ hKeyNetscape,
+ (char *) szServerKeyName,
+ 0L,
+ KEY_ALL_ACCESS,
+ &hKeyNetscapeConf);
+
+ if (regStatus != ERROR_SUCCESS) {
+ continue;
+ }
+
+ /* Now look for "ConfigurationPath" to find 3.0 netscape servers */
+ size = MAX_KEY_SIZE;
+ status = RegQueryValueEx(
+ hKeyNetscapeConf,
+ TEXT(VALUE_CONFIG_PATH),
+ 0L,
+ &type,
+ (LPBYTE)szPath,
+ &size);
+ if ( status == ERROR_SUCCESS ) {
+ /* this is a netscape server */
+ if ( (pNew = (instance_list_t *)malloc(sizeof(instance_list_t))) == NULL) {
+ status = (unsigned long)-1;
+ RegCloseKey(hKeyNetscapeConf);
+ goto ExitPoint;
+ }
+ if ( (pNew->pInstanceName = (PWCH)malloc(sizeof(WCHAR) *(dwServerKeySize+1))) == NULL) {
+ status = (unsigned long)-1;
+ RegCloseKey(hKeyNetscapeConf);
+ goto ExitPoint;
+ }
+ if ( (pNew->pConfPath = (PWCH)malloc(sizeof(WCHAR) *(size+1))) == NULL) {
+ status = (unsigned long)-1;
+ RegCloseKey(hKeyNetscapeConf);
+ goto ExitPoint;
+ }
+
+
+ pNew->Handle = 0;
+ pNew->pData = NULL;
+
+ pNew->instance.ParentObjectTitleIndex = 0;
+ pNew->instance.ParentObjectInstance = 0;
+ pNew->instance.UniqueID = -1;
+ pNew->instance.NameOffset = sizeof(PERF_INSTANCE_DEFINITION);
+ lstrcpy((char *) pNew->pInstanceName, (char *) szServerKeyName);
+
+ lstrcpy((char *) pNew->pConfPath, (char *) szPath);
+
+ pNew->instance.NameLength = (dwServerKeySize+1) * sizeof(WCHAR);
+ pNew->instance.ByteLength = sizeof(PERF_INSTANCE_DEFINITION) +
+ (((pNew->instance.NameLength + sizeof(DWORD)-1)/sizeof(DWORD))*sizeof(DWORD));
+ pNew->instance.UniqueID = iUniqueID++;
+
+ wsprintf(pNew->szLogPath, "\\%s", MAGT_LOGFILE);
+ if( ((char *) pNew->szRootDir = getRootDirFromConfFile(pNew->pConfPath) ) != NULL)
+ {
+ /* can only check if getRootDir */
+
+ /* open the log */
+
+ MagtOpenLog(pNew->szRootDir, pNew->szLogPath);
+
+ if ((pNew->pCfgInfo = (MagtStaticInfo_t *) GlobalAlloc(GPTR,
+ sizeof(MagtStaticInfo_t))) == NULL)
+ {
+ /* something fatal happened but try to free this
+ node that won't be used anyway
+ */
+ if(pNew != NULL)
+ {
+ free(pNew);
+ }
+ status = (unsigned long)-1;
+ goto ExitPoint;
+ }
+
+ MagtLoadStaticInfo(pNew->pCfgInfo, pNew->szRootDir, &pNew->SNMPOff, pNew->szLogPath);
+
+
+ if ( pNew->SNMPOff )
+ {
+ wsprintf(logMsg,
+ "SNMP subagent is not configured to be on\n");
+ MagtLog(logMsg, pNew->szLogPath);
+
+ /* since not adding this to list free it */
+ if(pNew != NULL)
+ {
+ free(pNew);
+ }
+ }else{
+ /* new instance that is on to add to list */
+
+ /* if first element null or less than first element add to beginning */
+ if( (pInstanceList == NULL)
+ || (pNew->pCfgInfo->ApplIndex < pInstanceList->pCfgInfo->ApplIndex) )
+ {
+ pNew->pNext = pInstanceList;
+ pInstanceList = pNew;
+ }else{
+ /* must be after first element */
+ for(pCurrent= pInstanceList; pCurrent; pCurrent=pCurrent->pNext)
+ {
+ if(pNew->pCfgInfo->ApplIndex == pCurrent->pCfgInfo->ApplIndex)
+ {
+ /* ApplIndex must be unique, another instance on this host
+ is already configured to be on using this applIndex,
+ so I can't monitor this one. Log the error and
+ don't add this instance to the list */
+
+ wsprintf(logMsg,
+ "Another server instance with this ApplIndex: %d is already being"
+ " monitored. ApplIndex must be unique. Turn off"
+ " SNMP monitoring of the other server instance or change"
+ " the ApplIndex of one of the server instances.\n",
+ pNew->pCfgInfo->ApplIndex);
+ MagtLog(logMsg, pNew->szLogPath);
+
+ /* since not adding this to list free it */
+ if(pNew != NULL)
+ {
+ free(pNew);
+ }
+ }else if( (pCurrent->pNext == NULL)
+ || ( (pNew->pCfgInfo->ApplIndex > pCurrent->pCfgInfo->ApplIndex)
+ && (pNew->pCfgInfo->ApplIndex < pCurrent->pNext->pCfgInfo->ApplIndex)) )
+ {
+ /* if next is null or if greater this element and less then next one
+ add it inbetween */
+ pNew->pNext=pCurrent->pNext;
+ pCurrent->pNext=pNew;
+ break;
+ }
+ }
+ }
+ }
+ }
+ dwServerCount++;
+ }
+
+ RegCloseKey(hKeyNetscapeConf);
+ }
+
+ } while ( regStatus != ERROR_NO_MORE_ITEMS );
+
+ExitPoint:
+ if (hKeyNetscape)
+ RegCloseKey (hKeyNetscape);
+
+ return dwServerCount;
+}
+
diff --git a/ldap/servers/snmp/ntagt/nsldapagt_nt.def b/ldap/servers/snmp/ntagt/nsldapagt_nt.def
new file mode 100644
index 00000000..9968af40
--- /dev/null
+++ b/ldap/servers/snmp/ntagt/nsldapagt_nt.def
@@ -0,0 +1,24 @@
+; BEGIN COPYRIGHT BLOCK
+; Copyright 2001 Sun Microsystems, Inc.
+; Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+; All rights reserved.
+; END COPYRIGHT BLOCK
+;
+LIBRARY ns-ldapagt
+
+DESCRIPTION 'Netscape Directory Server SNMP Agent'
+
+CODE LOADONCALL MOVEABLE DISCARDABLE
+DATA PRELOAD MOVEABLE SINGLE
+
+SEGMENTS
+ _TEXT PRELOAD
+ INIT_TEXT PRELOAD
+
+HEAPSIZE 1024
+
+EXPORTS
+ SnmpExtensionInit
+ SnmpExtensionTrap
+ SnmpExtensionQuery
+
diff --git a/ldap/servers/snmp/ntagt/nsldapagt_nt.h b/ldap/servers/snmp/ntagt/nsldapagt_nt.h
new file mode 100644
index 00000000..16e472db
--- /dev/null
+++ b/ldap/servers/snmp/ntagt/nsldapagt_nt.h
@@ -0,0 +1,228 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+
+/*-------------------------------------------------------------------------
+ *
+ * nsldapagt_nt.h - Definitions for NS Directory Server's SNMP subagent on
+ * NT.
+ *
+ * Revision History:
+ * 07/25/1997 Steve Ross Created
+ *
+ *
+ *-----------------------------------------------------------------------*/
+
+#ifndef __NSLDAPAGT_NT_H_
+#define __NSLDAPAGT_NT_H_
+
+/*-------------------------------------------------------------------------
+ *
+ * Defines
+ *
+ *-----------------------------------------------------------------------*/
+
+#define MAGT_MAX_LINELEN 255
+#define MAGT_CONFFILE "snmp.conf"
+#define MAGT_CONFDIR "config"
+#define DSE_LDIF "dse.ldif"
+#define MAGT_LOGFILE "nsldapagt.log"
+#define MAGT_TIME_QUANTUM 10
+
+#define MAGT_TRAP_NONE 0
+#define MAGT_TRAP_SERVER_DOWN 7001
+#define MAGT_TRAP_SERVER_START 7002
+
+#define LDAP_CONFIG_DN "cn=SNMP,cn=config"
+#define BASE_OBJECTCLASS_SEARCH "objectclass=*"
+
+#define LDAP_ATTR_SNMP_ENABLED "nssnmpenabled"
+#define LDAP_ATTR_SNMP_DESCRIPTION "nssnmpdescription"
+#define LDAP_ATTR_SNMP_ORGANIZATION "nssnmporganization"
+#define LDAP_ATTR_SNMP_LOCATION "nssnmplocation"
+#define LDAP_ATTR_SNMP_CONTACT "nssnmpcontact"
+
+#define SNMP_ON "ON"
+
+/*-------------------------------------------------------------------------
+ *
+ * Types
+ *
+ *-----------------------------------------------------------------------*/
+
+typedef enum
+{
+ MAGT_FALSE = 0,
+ MAGT_TRUE
+} MagtBool_t;
+
+typedef enum
+{
+ MAGT_TRAP_GENERATION,
+ MAGT_TRAP_CLEANUP
+} MagtTrapTask_t;
+
+typedef struct MagtDispStr
+{
+ int len;
+ unsigned char *val;
+} MagtDispStr_t;
+
+typedef struct MagtStaticInfo
+{
+ MagtDispStr_t entityDescr;
+ MagtDispStr_t entityVers;
+ MagtDispStr_t entityOrg;
+ MagtDispStr_t entityLocation;
+ MagtDispStr_t entityContact;
+ MagtDispStr_t entityName;
+ int ApplIndex;
+
+} MagtStaticInfo_t;
+
+typedef struct MagtLDAPInfo
+{
+ char *host;
+ int port;
+ char *rootdn;
+ char *rootpw;
+
+
+} MagtLDAPInfo_t;
+
+typedef struct MagtHdrInfo
+{
+ int versMajor;
+ int versMinor;
+ int restarted;
+ time_t startTime;
+ time_t updateTime;
+} MagtHdrInfo_t;
+
+typedef struct MagtOpsTblInfo
+{
+ int AnonymousBinds;
+ int UnAuthBinds;
+ int SimpleAuthBinds;
+ int StrongAuthBinds;
+ int BindSecurityErrors;
+ int InOps;
+ int ReadOps;
+ int CompareOps;
+ int AddEntryOps;
+ int RemoveEntryOps;
+ int ModifyEntryOps;
+ int ModifyRDNOps;
+ int ListOps;
+ int SearchOps;
+ int OneLevelSearchOps;
+ int WholeSubtreeSearchOps;
+ int Referrals;
+ int Chainings;
+ int SecurityErrors;
+ int Errors;
+} MagtOpsTblInfo_t;
+
+typedef struct MagtEntriesTblInfo
+{
+ int MasterEntries;
+ int CopyEntries;
+ int CacheEntries;
+ int CacheHits;
+ int SlaveHits;
+} MagtEntriesTblInfo_t;
+
+typedef struct MagtIntTblInfo
+{
+ MagtDispStr_t DsName;
+ time_t TimeOfCreation;
+ time_t TimeOfLastAttempt;
+ time_t TimeOfLastSuccess;
+ int FailuresSinceLastSuccess;
+ int Failures;
+ int Successes;
+ MagtDispStr_t URL;
+}MagtIntTblInfo_t;
+
+typedef struct instance_list_t {
+ PERF_INSTANCE_DEFINITION instance;
+ PWSTR pInstanceName;
+ PWSTR pConfPath;
+ int Handle;
+ struct agt_stats_t * pData;
+ HANDLE ghTrapEvent;
+ MagtOpsTblInfo_t * pOpsStatInfo;
+ MagtEntriesTblInfo_t * pEntriesStatInfo;
+ MagtIntTblInfo_t ** ppIntStatInfo;
+ MagtStaticInfo_t * pCfgInfo;
+ MagtMibEntry_t * pMibInfo;
+ char * szRootDir;
+ char szLogPath[MAX_PATH];
+ char szStatsPath[MAX_PATH];
+ int SNMPOff;
+
+
+ /* trap stuff */
+ time_t oldUpdateTime;
+ time_t oldStartTime;
+ int graceCycles;
+ MagtBool_t mmapStale;
+ MagtBool_t mmapOk;
+ MagtBool_t serverUp;
+ MagtBool_t downTrapSent;
+ int trapType;
+ MagtHdrInfo_t hdrInfo;
+
+ struct instance_list_t * pNext;
+} instance_list_t;
+
+/*-------------------------------------------------------------------------
+ *
+ * Prototypes
+ *
+ *-----------------------------------------------------------------------*/
+
+int MagtCheckServer(instance_list_t *pInstance);
+
+void MagtCleanUp();
+
+void MagtConfProcess(char *line, int lineLen,
+ MagtLDAPInfo_t *info);
+
+char *MagtGetRootDir(void);
+
+
+void MagtInitMibStorage(MagtMibEntry_t * MibInfo,
+ MagtOpsTblInfo_t * pOpsStatInfo,
+ MagtEntriesTblInfo_t * pEntriesStatInfo,
+ MagtIntTblInfo_t ** ppIntStatInfo,
+ MagtStaticInfo_t * pCfgInfo);
+
+
+int MagtLoadStaticInfo(MagtStaticInfo_t *staticInfo, char *pszRootDir, int *SNMPOff, char *pszLogPath);
+
+void MagtLog(char *logMsg, char *pszLogPath);
+
+char *MagtLogTime();
+
+void MagtOpenLog(char *pszRootDir, char *pszLogPath);
+
+int MagtReadLine(char *buf,
+ int n,
+ FILE *fp);
+
+int MagtReadStats(MagtHdrInfo_t *hdrInfo,
+ MagtOpsTblInfo_t *OpsTblInfo,
+ MagtEntriesTblInfo_t *EntriesTblInfo,
+ MagtIntTblInfo_t **IntTblInfo,
+ char * pszStatsPath,
+ char * pszLogPath);
+
+DWORD _FindNetscapeServers();
+
+extern instance_list_t *pInstanceList;
+
+
+#endif /* __NSLDAPAGT_NT_H_ */
diff --git a/ldap/servers/snmp/ntagt/nsldapmib_nt.c b/ldap/servers/snmp/ntagt/nsldapmib_nt.c
new file mode 100644
index 00000000..af977a23
--- /dev/null
+++ b/ldap/servers/snmp/ntagt/nsldapmib_nt.c
@@ -0,0 +1,1041 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+
+/*-------------------------------------------------------------------------
+ *
+ * nsldapmib_nt.c - NS Directory Server's MIB for extended SNMP agent
+ * on NT.
+ *
+ * Revision History:
+ * 07/25/1997 Steve Ross Created
+ *
+ *
+ *-----------------------------------------------------------------------*/
+
+#include <stdio.h>
+#include <malloc.h>
+#include <time.h>
+#include <windows.h>
+#include <malloc.h>
+#include <snmp.h>
+#include <mgmtapi.h>
+#include "nsldapmib_nt.h"
+#include "nsldapagt_nt.h"
+#include "agtmmap.h"
+
+/*-------------------------------------------------------------------------
+ *
+ * Globals
+ *
+ *-----------------------------------------------------------------------*/
+
+/*
+ * For ldap, the prefix to all MIB variables is 1.3.6.1.4.1.1450.7
+ */
+UINT OID_Prefix[] = {1, 3, 6, 1, 4, 1, 1450, 7};
+AsnObjectIdentifier MIB_OidPrefix = {MAGT_OID_SIZEOF(OID_Prefix),
+ OID_Prefix};
+
+/*
+ * OID of each MIB variable.
+ * For example, the OID for mtaReceivedMessages is:
+ * 1.3.6.1.4.1.1450.7
+ *
+ * - nsldap = netscape 7
+ * -dsOpsTable = nsldap 1
+ * -dsEntriesTable = nsldap 2
+ * -dsIntTable = nsldap 3
+ * -dsEntityINfo = nsldap 5
+ *
+ */
+
+
+UINT OID_ApplIndex[] = {389};
+/* setup the parts of the OID we know in advance */
+
+/* ops table */
+UINT OID_AnonymousBinds[] = {1, 1, 1};
+UINT OID_UnAuthBinds[] = {1, 1, 2};
+UINT OID_SimpleAuthBinds[] = {1, 1, 3};
+UINT OID_StrongAuthBinds[] = {1, 1, 4};
+UINT OID_BindSecurityErrors[] = {1, 1, 5};
+UINT OID_InOps[] = {1, 1, 6};
+UINT OID_ReadOps[] = {1, 1, 7};
+UINT OID_CompareOps[] = {1, 1, 8};
+UINT OID_AddEntryOps[] = {1, 1, 9};
+UINT OID_RemoveEntryOps[] = {1, 1, 10};
+UINT OID_ModifyEntryOps[] = {1, 1, 11};
+UINT OID_ModifyRDNOps[] = {1, 1, 12};
+UINT OID_ListOps[] = {1, 1, 13};
+UINT OID_SearchOps[] = {1, 1, 14};
+UINT OID_OneLevelSearchOps[] = {1, 1, 15};
+UINT OID_WholeSubtreeSearchOps[] = {1, 1, 16};
+UINT OID_Referrals[] = {1, 1, 17};
+UINT OID_Chainings[] = {1, 1, 18};
+UINT OID_SecurityErrors[] = {1, 1, 19};
+UINT OID_Errors[] = {1, 1, 20};
+
+/* entries table */
+UINT OID_MasterEntries[] = {2, 1, 1};
+UINT OID_CopyEntries[] = {2, 1, 2};
+UINT OID_CacheEntries[] = {2, 1, 3};
+UINT OID_CacheHits[] = {2, 1, 4};
+UINT OID_SlaveHits[] = {2, 1, 5};
+
+/* interaction table */
+UINT OID_DsIntIndex[] = {3, 1, 1};
+UINT OID_DsName[] = {3, 1, 2};
+UINT OID_TimeOfCreation[] = {3, 1, 3};
+UINT OID_TimeOfLastAttempt[] = {3, 1, 4};
+UINT OID_TimeOfLastSuccess[] = {3, 1, 5};
+UINT OID_FailuresSinceLastSuccess[] = {3, 1, 6};
+UINT OID_Failures[] = {3, 1, 7};
+UINT OID_Successes[] = {3, 1, 8};
+UINT OID_URL[] = {3, 1, 9};
+
+/* entity table */
+UINT OID_EntityDescr[] = {5, 1, 1};
+UINT OID_EntityVers[] = {5, 1, 2};
+UINT OID_EntityOrg[] = {5, 1, 3};
+UINT OID_EntityLocation[] = {5, 1, 4};
+UINT OID_EntityContact[] = {5, 1, 5};
+UINT OID_EntityName[] = {5, 1, 6};
+
+
+/* make AsnObjectIdentifiers so can use snmputilOidcpy for each server instance, and append to put on indexes later */
+/* ops table */
+AsnObjectIdentifier ASN_AnonymousBinds = {MAGT_OID_SIZEOF(OID_AnonymousBinds) ,OID_AnonymousBinds};
+AsnObjectIdentifier ASN_UnAuthBinds = {MAGT_OID_SIZEOF(OID_UnAuthBinds) ,OID_UnAuthBinds};
+AsnObjectIdentifier ASN_SimpleAuthBinds = {MAGT_OID_SIZEOF(OID_SimpleAuthBinds) ,OID_SimpleAuthBinds};
+AsnObjectIdentifier ASN_StrongAuthBinds = {MAGT_OID_SIZEOF(OID_StrongAuthBinds) ,OID_StrongAuthBinds};
+AsnObjectIdentifier ASN_BindSecurityErrors = {MAGT_OID_SIZEOF(OID_BindSecurityErrors) ,OID_BindSecurityErrors};
+AsnObjectIdentifier ASN_InOps = {MAGT_OID_SIZEOF(OID_InOps) ,OID_InOps};
+AsnObjectIdentifier ASN_ReadOps = {MAGT_OID_SIZEOF(OID_ReadOps) ,OID_ReadOps};
+AsnObjectIdentifier ASN_CompareOps = {MAGT_OID_SIZEOF(OID_CompareOps) ,OID_CompareOps};
+AsnObjectIdentifier ASN_AddEntryOps = {MAGT_OID_SIZEOF(OID_AddEntryOps) ,OID_AddEntryOps};
+AsnObjectIdentifier ASN_RemoveEntryOps = {MAGT_OID_SIZEOF(OID_RemoveEntryOps) ,OID_RemoveEntryOps};
+AsnObjectIdentifier ASN_ModifyEntryOps = {MAGT_OID_SIZEOF(OID_ModifyEntryOps) ,OID_ModifyEntryOps};
+AsnObjectIdentifier ASN_ModifyRDNOps = {MAGT_OID_SIZEOF(OID_ModifyRDNOps) ,OID_ModifyRDNOps};
+AsnObjectIdentifier ASN_ListOps = {MAGT_OID_SIZEOF(OID_ListOps) ,OID_ListOps};
+AsnObjectIdentifier ASN_SearchOps = {MAGT_OID_SIZEOF(OID_SearchOps) ,OID_SearchOps};
+AsnObjectIdentifier ASN_OneLevelSearchOps = {MAGT_OID_SIZEOF(OID_OneLevelSearchOps) ,OID_OneLevelSearchOps};
+AsnObjectIdentifier ASN_WholeSubtreeSearchOps = {MAGT_OID_SIZEOF(OID_WholeSubtreeSearchOps),OID_WholeSubtreeSearchOps};
+AsnObjectIdentifier ASN_Referrals = {MAGT_OID_SIZEOF(OID_Referrals) ,OID_Referrals};
+AsnObjectIdentifier ASN_Chainings = {MAGT_OID_SIZEOF(OID_Chainings) ,OID_Chainings};
+AsnObjectIdentifier ASN_SecurityErrors = {MAGT_OID_SIZEOF(OID_SecurityErrors) ,OID_SecurityErrors};
+AsnObjectIdentifier ASN_Errors = {MAGT_OID_SIZEOF(OID_Errors) ,OID_Errors};
+
+/* entries table */
+AsnObjectIdentifier ASN_MasterEntries = {MAGT_OID_SIZEOF(OID_MasterEntries) ,OID_MasterEntries};
+AsnObjectIdentifier ASN_CopyEntries = {MAGT_OID_SIZEOF(OID_CopyEntries) ,OID_CopyEntries};
+AsnObjectIdentifier ASN_CacheEntries = {MAGT_OID_SIZEOF(OID_CacheEntries) ,OID_CacheEntries};
+AsnObjectIdentifier ASN_CacheHits = {MAGT_OID_SIZEOF(OID_CacheHits) ,OID_CacheHits};
+AsnObjectIdentifier ASN_SlaveHits = {MAGT_OID_SIZEOF(OID_SlaveHits) ,OID_SlaveHits};
+
+/* interaction table */
+AsnObjectIdentifier ASN_DsName = {MAGT_OID_SIZEOF(OID_DsName) ,OID_DsName};
+AsnObjectIdentifier ASN_TimeOfCreation = {MAGT_OID_SIZEOF(OID_TimeOfCreation) ,OID_TimeOfCreation};
+AsnObjectIdentifier ASN_TimeOfLastAttempt = {MAGT_OID_SIZEOF(OID_TimeOfLastAttempt) ,OID_TimeOfLastAttempt};
+AsnObjectIdentifier ASN_TimeOfLastSuccess = {MAGT_OID_SIZEOF(OID_TimeOfLastSuccess) ,OID_TimeOfLastSuccess};
+AsnObjectIdentifier ASN_FailuresSinceLastSuccess = {MAGT_OID_SIZEOF(OID_FailuresSinceLastSuccess) ,OID_FailuresSinceLastSuccess};
+AsnObjectIdentifier ASN_Failures = {MAGT_OID_SIZEOF(OID_Failures) ,OID_Failures};
+AsnObjectIdentifier ASN_Successes = {MAGT_OID_SIZEOF(OID_Successes) ,OID_Successes};
+AsnObjectIdentifier ASN_URL = {MAGT_OID_SIZEOF(OID_URL) ,OID_URL};
+
+/* entity table */
+AsnObjectIdentifier ASN_EntityDescr = {MAGT_OID_SIZEOF(OID_EntityDescr) ,OID_EntityDescr};
+AsnObjectIdentifier ASN_EntityVers = {MAGT_OID_SIZEOF(OID_EntityVers) ,OID_EntityVers};
+AsnObjectIdentifier ASN_EntityOrg = {MAGT_OID_SIZEOF(OID_EntityOrg) ,OID_EntityOrg};
+AsnObjectIdentifier ASN_EntityLocation = {MAGT_OID_SIZEOF(OID_EntityLocation) ,OID_EntityLocation};
+AsnObjectIdentifier ASN_EntityContact = {MAGT_OID_SIZEOF(OID_EntityContact) ,OID_EntityContact};
+AsnObjectIdentifier ASN_EntityName = {MAGT_OID_SIZEOF(OID_EntityName) ,OID_EntityName};
+
+/*
+ * Storage definitions for MIB.
+ */
+char szPlaceHolder[] = "Not Available";
+int nPlaceHolder = 0;
+
+#define NUM_ENTITY_COLUMNS 6
+#define NUM_OPS_COLUMNS 20
+#define NUM_ENTRIES_COLUMNS 5
+#define NUM_INT_COLUMNS 8
+#define NUM_INT_ROWS 5
+
+
+UINT MIB_num_vars;
+
+void OidAppendIndex(AsnObjectIdentifier *Oid, int Index);
+
+int Mib_init(MagtMibEntry_t **Mib, int ApplIndex)
+{
+ int i;
+ int j;
+
+ MIB_num_vars = (UINT) 71;
+
+ /* allocate the memory for this Mib Instance */
+ if( (*Mib = (MagtMibEntry_t *) GlobalAlloc(GPTR, MIB_num_vars *
+ sizeof(MagtMibEntry_t ) )) == NULL)
+ {
+ return -1;
+ }
+
+ /**************************
+ * Ops Table Stuff
+ * --------------
+ * AnonymousBinds
+ * UnAuthBinds
+ * SimpleAuthBinds
+ * StrongAuthBinds
+ * BindSecurityErrors
+ * InOps
+ * ReadOps
+ * CompareOps
+ * AddEntryOps
+ * RemoveEntryOps
+ * ModifyEntryOps
+ * ModifyRDNOps
+ * ListOps
+ * SearchOps
+ * OneLevelSearchOps
+ * WholeSubtreeSearchOps
+ * Referrals
+ * Chainings
+ * SecurityErrors
+ * Errors
+ **************************/
+
+
+ i=0;
+ SnmpUtilOidCpy(&((*Mib)[i].Oid), &ASN_AnonymousBinds);
+ OidAppendIndex(&((*Mib)[i].Oid), ApplIndex);
+ (*Mib)[i].Storage = &nPlaceHolder;
+ (*Mib)[i].Type = ASN_RFC1155_COUNTER;
+ (*Mib)[i].Access = MAGT_MIB_ACCESS_READ;
+ (*Mib)[i].MibFunc = MagtMIBLeafFunc;
+ (*Mib)[i].MibNext = NULL;
+ (*Mib)[i].uId = MAGT_ID_ANONYMOUS_BINDS;
+
+ i++;
+ SnmpUtilOidCpy(&((*Mib)[i].Oid), &ASN_UnAuthBinds);
+ OidAppendIndex(&((*Mib)[i].Oid), ApplIndex);
+ (*Mib)[i].Storage = &nPlaceHolder;
+ (*Mib)[i].Type = ASN_RFC1155_COUNTER;
+ (*Mib)[i].Access = MAGT_MIB_ACCESS_READ;
+ (*Mib)[i].MibFunc = MagtMIBLeafFunc;
+ (*Mib)[i].MibNext = NULL;
+ (*Mib)[i].uId = MAGT_ID_UNAUTH_BINDS;
+
+ i++;
+ SnmpUtilOidCpy(&((*Mib)[i].Oid), &ASN_SimpleAuthBinds);
+ OidAppendIndex(&((*Mib)[i].Oid), ApplIndex);
+ (*Mib)[i].Storage = &nPlaceHolder;
+ (*Mib)[i].Type = ASN_RFC1155_COUNTER;
+ (*Mib)[i].Access = MAGT_MIB_ACCESS_READ;
+ (*Mib)[i].MibFunc = MagtMIBLeafFunc;
+ (*Mib)[i].MibNext = NULL;
+ (*Mib)[i].uId = MAGT_ID_SIMPLE_AUTH_BINDS;
+
+ i++;
+ SnmpUtilOidCpy(&((*Mib)[i].Oid), &ASN_StrongAuthBinds);
+ OidAppendIndex(&((*Mib)[i].Oid), ApplIndex);
+ (*Mib)[i].Storage = &nPlaceHolder;
+ (*Mib)[i].Type = ASN_RFC1155_COUNTER;
+ (*Mib)[i].Access = MAGT_MIB_ACCESS_READ;
+ (*Mib)[i].MibFunc = MagtMIBLeafFunc;
+ (*Mib)[i].MibNext = NULL;
+ (*Mib)[i].uId = MAGT_ID_STRONG_AUTH_BINDS;
+
+ i++;
+ SnmpUtilOidCpy(&((*Mib)[i].Oid), &ASN_BindSecurityErrors);
+ OidAppendIndex(&((*Mib)[i].Oid), ApplIndex);
+ (*Mib)[i].Storage = &nPlaceHolder;
+ (*Mib)[i].Type = ASN_RFC1155_COUNTER;
+ (*Mib)[i].Access = MAGT_MIB_ACCESS_READ;
+ (*Mib)[i].MibFunc = MagtMIBLeafFunc;
+ (*Mib)[i].MibNext = NULL;
+ (*Mib)[i].uId = MAGT_ID_BIND_SECURITY_ERRORS;
+
+ i++;
+ SnmpUtilOidCpy(&((*Mib)[i].Oid), &ASN_InOps);
+ OidAppendIndex(&((*Mib)[i].Oid), ApplIndex);
+ (*Mib)[i].Storage = &nPlaceHolder;
+ (*Mib)[i].Type = ASN_RFC1155_COUNTER;
+ (*Mib)[i].Access = MAGT_MIB_ACCESS_READ;
+ (*Mib)[i].MibFunc = MagtMIBLeafFunc;
+ (*Mib)[i].MibNext = NULL;
+ (*Mib)[i].uId = MAGT_ID_IN_OPS;
+
+ i++;
+ SnmpUtilOidCpy(&((*Mib)[i].Oid), &ASN_ReadOps);
+ OidAppendIndex(&((*Mib)[i].Oid), ApplIndex);
+ (*Mib)[i].Storage = &nPlaceHolder;
+ (*Mib)[i].Type = ASN_RFC1155_COUNTER;
+ (*Mib)[i].Access = MAGT_MIB_ACCESS_READ;
+ (*Mib)[i].MibFunc = MagtMIBLeafFunc;
+ (*Mib)[i].MibNext = NULL;
+ (*Mib)[i].uId = MAGT_ID_READ_OPS;
+
+ i++;
+ SnmpUtilOidCpy(&((*Mib)[i].Oid), &ASN_CompareOps);
+ OidAppendIndex(&((*Mib)[i].Oid), ApplIndex);
+ (*Mib)[i].Storage = &nPlaceHolder;
+ (*Mib)[i].Type = ASN_RFC1155_COUNTER;
+ (*Mib)[i].Access = MAGT_MIB_ACCESS_READ;
+ (*Mib)[i].MibFunc = MagtMIBLeafFunc;
+ (*Mib)[i].MibNext = NULL;
+ (*Mib)[i].uId = MAGT_ID_COMPARE_OPS;
+
+ i++;
+ SnmpUtilOidCpy(&((*Mib)[i].Oid), &ASN_AddEntryOps);
+ OidAppendIndex(&((*Mib)[i].Oid), ApplIndex);
+ (*Mib)[i].Storage = &nPlaceHolder;
+ (*Mib)[i].Type = ASN_RFC1155_COUNTER;
+ (*Mib)[i].Access = MAGT_MIB_ACCESS_READ;
+ (*Mib)[i].MibFunc = MagtMIBLeafFunc;
+ (*Mib)[i].MibNext = NULL;
+ (*Mib)[i].uId = MAGT_ID_ADD_ENTRY_OPS;
+
+ i++;
+ SnmpUtilOidCpy(&((*Mib)[i].Oid), &ASN_RemoveEntryOps);
+ OidAppendIndex(&((*Mib)[i].Oid), ApplIndex);
+ (*Mib)[i].Storage = &nPlaceHolder;
+ (*Mib)[i].Type = ASN_RFC1155_COUNTER;
+ (*Mib)[i].Access = MAGT_MIB_ACCESS_READ;
+ (*Mib)[i].MibFunc = MagtMIBLeafFunc;
+ (*Mib)[i].MibNext = NULL;
+ (*Mib)[i].uId = MAGT_ID_REMOVE_ENTRY_OPS;
+
+ i++;
+ SnmpUtilOidCpy(&((*Mib)[i].Oid), &ASN_ModifyEntryOps);
+ OidAppendIndex(&((*Mib)[i].Oid), ApplIndex);
+ (*Mib)[i].Storage = &nPlaceHolder;
+ (*Mib)[i].Type = ASN_RFC1155_COUNTER;
+ (*Mib)[i].Access = MAGT_MIB_ACCESS_READ;
+ (*Mib)[i].MibFunc = MagtMIBLeafFunc;
+ (*Mib)[i].MibNext = NULL;
+ (*Mib)[i].uId = MAGT_ID_MODIFY_ENTRY_OPS;
+
+ i++;
+ SnmpUtilOidCpy(&((*Mib)[i].Oid), &ASN_ModifyRDNOps);
+ OidAppendIndex(&((*Mib)[i].Oid), ApplIndex);
+ (*Mib)[i].Storage = &nPlaceHolder;
+ (*Mib)[i].Type = ASN_RFC1155_COUNTER;
+ (*Mib)[i].Access = MAGT_MIB_ACCESS_READ;
+ (*Mib)[i].MibFunc = MagtMIBLeafFunc;
+ (*Mib)[i].MibNext = NULL;
+ (*Mib)[i].uId = MAGT_ID_MODIFY_RDN_OPS;
+
+ i++;
+ SnmpUtilOidCpy(&((*Mib)[i].Oid), &ASN_ListOps);
+ OidAppendIndex(&((*Mib)[i].Oid), ApplIndex);
+ (*Mib)[i].Storage = &nPlaceHolder;
+ (*Mib)[i].Type = ASN_RFC1155_COUNTER;
+ (*Mib)[i].Access = MAGT_MIB_ACCESS_READ;
+ (*Mib)[i].MibFunc = MagtMIBLeafFunc;
+ (*Mib)[i].MibNext = NULL;
+ (*Mib)[i].uId = MAGT_ID_LIST_OPS;
+
+ i++;
+ SnmpUtilOidCpy(&((*Mib)[i].Oid), &ASN_SearchOps);
+ OidAppendIndex(&((*Mib)[i].Oid), ApplIndex);
+ (*Mib)[i].Storage = &nPlaceHolder;
+ (*Mib)[i].Type = ASN_RFC1155_COUNTER;
+ (*Mib)[i].Access = MAGT_MIB_ACCESS_READ;
+ (*Mib)[i].MibFunc = MagtMIBLeafFunc;
+ (*Mib)[i].MibNext = NULL;
+ (*Mib)[i].uId = MAGT_ID_SEARCH_OPS;
+
+ i++;
+ SnmpUtilOidCpy(&((*Mib)[i].Oid), &ASN_OneLevelSearchOps);
+ OidAppendIndex(&((*Mib)[i].Oid), ApplIndex);
+ (*Mib)[i].Storage = &nPlaceHolder;
+ (*Mib)[i].Type = ASN_RFC1155_COUNTER;
+ (*Mib)[i].Access = MAGT_MIB_ACCESS_READ;
+ (*Mib)[i].MibFunc = MagtMIBLeafFunc;
+ (*Mib)[i].MibNext = NULL;
+ (*Mib)[i].uId = MAGT_ID_ONE_LEVEL_SEARCH_OPS;
+
+ i++;
+ SnmpUtilOidCpy(&((*Mib)[i].Oid), &ASN_WholeSubtreeSearchOps);
+ OidAppendIndex(&((*Mib)[i].Oid), ApplIndex);
+ (*Mib)[i].Storage = &nPlaceHolder;
+ (*Mib)[i].Type = ASN_RFC1155_COUNTER;
+ (*Mib)[i].Access = MAGT_MIB_ACCESS_READ;
+ (*Mib)[i].MibFunc = MagtMIBLeafFunc;
+ (*Mib)[i].MibNext = NULL;
+ (*Mib)[i].uId = MAGT_ID_WHOLE_SUBTREE_SEARCH_OPS;
+
+ i++;
+ SnmpUtilOidCpy(&((*Mib)[i].Oid), &ASN_Referrals);
+ OidAppendIndex(&((*Mib)[i].Oid), ApplIndex);
+ (*Mib)[i].Storage = &nPlaceHolder;
+ (*Mib)[i].Type = ASN_RFC1155_COUNTER;
+ (*Mib)[i].Access = MAGT_MIB_ACCESS_READ;
+ (*Mib)[i].MibFunc = MagtMIBLeafFunc;
+ (*Mib)[i].MibNext = NULL;
+ (*Mib)[i].uId = MAGT_ID_REFERRALS;
+
+ i++;
+ SnmpUtilOidCpy(&((*Mib)[i].Oid), &ASN_Chainings);
+ OidAppendIndex(&((*Mib)[i].Oid), ApplIndex);
+ (*Mib)[i].Storage = &nPlaceHolder;
+ (*Mib)[i].Type = ASN_RFC1155_COUNTER;
+ (*Mib)[i].Access = MAGT_MIB_ACCESS_READ;
+ (*Mib)[i].MibFunc = MagtMIBLeafFunc;
+ (*Mib)[i].MibNext = NULL;
+ (*Mib)[i].uId = MAGT_ID_CHAININGS;
+
+ i++;
+ SnmpUtilOidCpy(&((*Mib)[i].Oid), &ASN_SecurityErrors);
+ OidAppendIndex(&((*Mib)[i].Oid), ApplIndex);
+ (*Mib)[i].Storage = &nPlaceHolder;
+ (*Mib)[i].Type = ASN_RFC1155_COUNTER;
+ (*Mib)[i].Access = MAGT_MIB_ACCESS_READ;
+ (*Mib)[i].MibFunc = MagtMIBLeafFunc;
+ (*Mib)[i].MibNext = NULL;
+ (*Mib)[i].uId = MAGT_ID_SECURITY_ERRORS;
+
+ i++;
+ SnmpUtilOidCpy(&((*Mib)[i].Oid), &ASN_Errors);
+ OidAppendIndex(&((*Mib)[i].Oid), ApplIndex);
+ (*Mib)[i].Storage = &nPlaceHolder;
+ (*Mib)[i].Type = ASN_RFC1155_COUNTER;
+ (*Mib)[i].Access = MAGT_MIB_ACCESS_READ;
+ (*Mib)[i].MibFunc = MagtMIBLeafFunc;
+ (*Mib)[i].MibNext = NULL;
+ (*Mib)[i].uId = MAGT_ID_ERRORS;
+
+ /**************************
+ * Entries Table Stuff
+ * --------------
+ * MasterEntries
+ * CopyEntries
+ * CacheEntries
+ * CacheHits
+ * SlaveHits
+ **************************/
+
+ i++;
+ SnmpUtilOidCpy(&((*Mib)[i].Oid), &ASN_MasterEntries);
+ OidAppendIndex(&((*Mib)[i].Oid), ApplIndex);
+ (*Mib)[i].Storage = &nPlaceHolder;
+ (*Mib)[i].Type = ASN_RFC1155_COUNTER;
+ (*Mib)[i].Access = MAGT_MIB_ACCESS_READ;
+ (*Mib)[i].MibFunc = MagtMIBLeafFunc;
+ (*Mib)[i].MibNext = NULL;
+ (*Mib)[i].uId = MAGT_ID_MASTER_ENTRIES;
+
+ i++;
+ SnmpUtilOidCpy(&((*Mib)[i].Oid), &ASN_CopyEntries);
+ OidAppendIndex(&((*Mib)[i].Oid), ApplIndex);
+ (*Mib)[i].Storage = &nPlaceHolder;
+ (*Mib)[i].Type = ASN_RFC1155_COUNTER;
+ (*Mib)[i].Access = MAGT_MIB_ACCESS_READ;
+ (*Mib)[i].MibFunc = MagtMIBLeafFunc;
+ (*Mib)[i].MibNext = NULL;
+ (*Mib)[i].uId = MAGT_ID_COPY_ENTRIES;
+
+ i++;
+ SnmpUtilOidCpy(&((*Mib)[i].Oid), &ASN_CacheEntries);
+ OidAppendIndex(&((*Mib)[i].Oid), ApplIndex);
+ (*Mib)[i].Storage = &nPlaceHolder;
+ (*Mib)[i].Type = ASN_RFC1155_COUNTER;
+ (*Mib)[i].Access = MAGT_MIB_ACCESS_READ;
+ (*Mib)[i].MibFunc = MagtMIBLeafFunc;
+ (*Mib)[i].MibNext = NULL;
+ (*Mib)[i].uId = MAGT_ID_CACHE_ENTRIES;
+
+ i++;
+ SnmpUtilOidCpy(&((*Mib)[i].Oid), &ASN_CacheHits);
+ OidAppendIndex(&((*Mib)[i].Oid), ApplIndex);
+ (*Mib)[i].Storage = &nPlaceHolder;
+ (*Mib)[i].Type = ASN_RFC1155_COUNTER;
+ (*Mib)[i].Access = MAGT_MIB_ACCESS_READ;
+ (*Mib)[i].MibFunc = MagtMIBLeafFunc;
+ (*Mib)[i].MibNext = NULL;
+ (*Mib)[i].uId = MAGT_ID_CACHE_HITS;
+
+ i++;
+ SnmpUtilOidCpy(&((*Mib)[i].Oid), &ASN_SlaveHits);
+ OidAppendIndex(&((*Mib)[i].Oid), ApplIndex);
+ (*Mib)[i].Storage = &nPlaceHolder;
+ (*Mib)[i].Type = ASN_RFC1155_COUNTER;
+ (*Mib)[i].Access = MAGT_MIB_ACCESS_READ;
+ (*Mib)[i].MibFunc = MagtMIBLeafFunc;
+ (*Mib)[i].MibNext = NULL;
+ (*Mib)[i].uId = MAGT_ID_SLAVE_HITS;
+
+
+ /**************************
+ * Interaction Table Stuff
+ * --------------
+ * DsName
+ * TimeOfCreation
+ * TimeOfLastAttempt
+ * TimeOfLastSuccess
+ * FailuresSinceLastSuccess
+ * Failures
+ * Successes
+ * URL
+ **************************/
+
+ for(j=1; j <= NUM_SNMP_INT_TBL_ROWS; j++)
+ {
+
+ i++;
+ SnmpUtilOidCpy(&((*Mib)[i].Oid), &ASN_DsName);
+ OidAppendIndex(&((*Mib)[i].Oid), ApplIndex);
+ OidAppendIndex(&((*Mib)[i].Oid), j);
+ (*Mib)[i].Storage = szPlaceHolder;
+ (*Mib)[i].Type = ASN_RFC1213_DISPSTRING;
+ (*Mib)[i].Access = MAGT_MIB_ACCESS_READ;
+ (*Mib)[i].MibFunc = MagtMIBLeafFunc;
+ (*Mib)[i].MibNext = NULL;
+ (*Mib)[i].uId = MAGT_ID_DS_NAME;
+
+ }
+
+ for(j=1; j <= NUM_SNMP_INT_TBL_ROWS; j++)
+ {
+
+ i++;
+ SnmpUtilOidCpy(&((*Mib)[i].Oid), &ASN_TimeOfCreation);
+ OidAppendIndex(&((*Mib)[i].Oid), ApplIndex);
+ OidAppendIndex(&((*Mib)[i].Oid), j);
+ (*Mib)[i].Storage = &nPlaceHolder;
+ (*Mib)[i].Type = ASN_RFC1155_TIMETICKS;
+ (*Mib)[i].Access = MAGT_MIB_ACCESS_READ;
+ (*Mib)[i].MibFunc = MagtMIBLeafFunc;
+ (*Mib)[i].MibNext = NULL;
+ (*Mib)[i].uId = MAGT_ID_TIME_OF_CREATION;
+ }
+
+ for(j=1; j <= NUM_SNMP_INT_TBL_ROWS; j++)
+ {
+
+ i++;
+ SnmpUtilOidCpy(&((*Mib)[i].Oid), &ASN_TimeOfLastAttempt);
+ OidAppendIndex(&((*Mib)[i].Oid), ApplIndex);
+ OidAppendIndex(&((*Mib)[i].Oid), j);
+ (*Mib)[i].Storage = &nPlaceHolder;
+ (*Mib)[i].Type = ASN_RFC1155_TIMETICKS;
+ (*Mib)[i].Access = MAGT_MIB_ACCESS_READ;
+ (*Mib)[i].MibFunc = MagtMIBLeafFunc;
+ (*Mib)[i].MibNext = NULL;
+ (*Mib)[i].uId = MAGT_ID_TIME_OF_LAST_ATTEMPT;
+ }
+
+ for(j=1; j <= NUM_SNMP_INT_TBL_ROWS; j++)
+ {
+
+ i++;
+ SnmpUtilOidCpy(&((*Mib)[i].Oid), &ASN_TimeOfLastSuccess);
+ OidAppendIndex(&((*Mib)[i].Oid), ApplIndex);
+ OidAppendIndex(&((*Mib)[i].Oid), j);
+ (*Mib)[i].Storage = &nPlaceHolder;
+ (*Mib)[i].Type = ASN_RFC1155_TIMETICKS;
+ (*Mib)[i].Access = MAGT_MIB_ACCESS_READ;
+ (*Mib)[i].MibFunc = MagtMIBLeafFunc;
+ (*Mib)[i].MibNext = NULL;
+ (*Mib)[i].uId = MAGT_ID_TIME_OF_LAST_SUCCESS;
+ }
+
+ for(j=1; j <= NUM_SNMP_INT_TBL_ROWS; j++)
+ {
+
+ i++;
+ SnmpUtilOidCpy(&((*Mib)[i].Oid), &ASN_FailuresSinceLastSuccess);
+ OidAppendIndex(&((*Mib)[i].Oid), ApplIndex);
+ OidAppendIndex(&((*Mib)[i].Oid), j);
+ (*Mib)[i].Storage = &nPlaceHolder;
+ (*Mib)[i].Type = ASN_RFC1155_COUNTER;
+ (*Mib)[i].Access = MAGT_MIB_ACCESS_READ;
+ (*Mib)[i].MibFunc = MagtMIBLeafFunc;
+ (*Mib)[i].MibNext = NULL;
+ (*Mib)[i].uId = MAGT_ID_FAILURES_SINCE_LAST_SUCCESS;
+ }
+
+ for(j=1; j <= NUM_SNMP_INT_TBL_ROWS; j++)
+ {
+ i++;
+ SnmpUtilOidCpy(&((*Mib)[i].Oid), &ASN_Failures);
+ OidAppendIndex(&((*Mib)[i].Oid), ApplIndex);
+ OidAppendIndex(&((*Mib)[i].Oid), j);
+ (*Mib)[i].Storage = &nPlaceHolder;
+ (*Mib)[i].Type = ASN_RFC1155_COUNTER;
+ (*Mib)[i].Access = MAGT_MIB_ACCESS_READ;
+ (*Mib)[i].MibFunc = MagtMIBLeafFunc;
+ (*Mib)[i].MibNext = NULL;
+ (*Mib)[i].uId = MAGT_ID_FAILURES;
+ }
+
+ for(j=1; j <= NUM_SNMP_INT_TBL_ROWS; j++)
+ {
+ i++;
+ SnmpUtilOidCpy(&((*Mib)[i].Oid), &ASN_Successes);
+ OidAppendIndex(&((*Mib)[i].Oid), ApplIndex);
+ OidAppendIndex(&((*Mib)[i].Oid), j);
+ (*Mib)[i].Storage = &nPlaceHolder;
+ (*Mib)[i].Type = ASN_RFC1155_COUNTER;
+ (*Mib)[i].Access = MAGT_MIB_ACCESS_READ;
+ (*Mib)[i].MibFunc = MagtMIBLeafFunc;
+ (*Mib)[i].MibNext = NULL;
+ (*Mib)[i].uId = MAGT_ID_SUCCESSES;
+ }
+
+ for(j=1; j <= NUM_SNMP_INT_TBL_ROWS; j++)
+ {
+ i++;
+ SnmpUtilOidCpy(&((*Mib)[i].Oid), &ASN_URL);
+ OidAppendIndex(&((*Mib)[i].Oid), ApplIndex);
+ OidAppendIndex(&((*Mib)[i].Oid), j);
+ (*Mib)[i].Storage = szPlaceHolder;
+ (*Mib)[i].Type = ASN_RFC1213_DISPSTRING;
+ (*Mib)[i].Access = MAGT_MIB_ACCESS_READ;
+ (*Mib)[i].MibFunc = MagtMIBLeafFunc;
+ (*Mib)[i].MibNext = NULL;
+ (*Mib)[i].uId = MAGT_ID_URL;
+ }
+
+ /**************************
+ * Entity Stuff
+ * --------------
+ * EntityDescr
+ * EntityVers
+ * EntityOrg
+ * EntityLocation
+ * EntityContact
+ * EntityName
+ **************************/
+
+ i++;
+ SnmpUtilOidCpy(&((*Mib)[i].Oid), &ASN_EntityDescr);
+ OidAppendIndex(&((*Mib)[i].Oid), ApplIndex);
+ (*Mib)[i].Storage = szPlaceHolder;
+ (*Mib)[i].Type = ASN_RFC1213_DISPSTRING;
+ (*Mib)[i].Access = MAGT_MIB_ACCESS_READ;
+ (*Mib)[i].MibFunc = MagtMIBLeafFunc;
+ (*Mib)[i].MibNext = NULL;
+ (*Mib)[i].uId = MAGT_ID_DESC;
+
+ i++;
+ SnmpUtilOidCpy(&((*Mib)[i].Oid), &ASN_EntityVers);
+ OidAppendIndex(&((*Mib)[i].Oid), ApplIndex);
+ (*Mib)[i].Storage = szPlaceHolder;
+ (*Mib)[i].Type = ASN_RFC1213_DISPSTRING;
+ (*Mib)[i].Access = MAGT_MIB_ACCESS_READ;
+ (*Mib)[i].MibFunc = MagtMIBLeafFunc;
+ (*Mib)[i].MibNext = NULL;
+ (*Mib)[i].uId = MAGT_ID_VERS;
+
+ i++;
+ SnmpUtilOidCpy(&((*Mib)[i].Oid), &ASN_EntityOrg);
+ OidAppendIndex(&((*Mib)[i].Oid), ApplIndex);
+ (*Mib)[i].Storage = szPlaceHolder;
+ (*Mib)[i].Type = ASN_RFC1213_DISPSTRING;
+ (*Mib)[i].Access = MAGT_MIB_ACCESS_READ;
+ (*Mib)[i].MibFunc = MagtMIBLeafFunc;
+ (*Mib)[i].MibNext = NULL;
+ (*Mib)[i].uId = MAGT_ID_ORG;
+
+ i++;
+ SnmpUtilOidCpy(&((*Mib)[i].Oid), &ASN_EntityLocation);
+ OidAppendIndex(&((*Mib)[i].Oid), ApplIndex);
+ (*Mib)[i].Storage = szPlaceHolder;
+ (*Mib)[i].Type = ASN_RFC1213_DISPSTRING;
+ (*Mib)[i].Access = MAGT_MIB_ACCESS_READ;
+ (*Mib)[i].MibFunc = MagtMIBLeafFunc;
+ (*Mib)[i].MibNext = NULL;
+ (*Mib)[i].uId = MAGT_ID_LOC;
+
+ i++;
+ SnmpUtilOidCpy(&((*Mib)[i].Oid), &ASN_EntityContact);
+ OidAppendIndex(&((*Mib)[i].Oid), ApplIndex);
+ (*Mib)[i].Storage = szPlaceHolder;
+ (*Mib)[i].Type = ASN_RFC1213_DISPSTRING;
+ (*Mib)[i].Access = MAGT_MIB_ACCESS_READ;
+ (*Mib)[i].MibFunc = MagtMIBLeafFunc;
+ (*Mib)[i].MibNext = NULL;
+ (*Mib)[i].uId = MAGT_ID_CONTACT;
+
+ i++;
+ SnmpUtilOidCpy(&((*Mib)[i].Oid), &ASN_EntityName);
+ OidAppendIndex(&((*Mib)[i].Oid), ApplIndex);
+ (*Mib)[i].Storage = szPlaceHolder;
+ (*Mib)[i].Type = ASN_RFC1213_DISPSTRING;
+ (*Mib)[i].Access = MAGT_MIB_ACCESS_READ;
+ (*Mib)[i].MibFunc = MagtMIBLeafFunc;
+ (*Mib)[i].MibNext = NULL;
+ (*Mib)[i].uId = MAGT_ID_NAME;
+
+ return 0;
+}
+
+void OidAppendIndex(AsnObjectIdentifier *Oid, int Index)
+{
+ UINT OID_Index[1];
+ AsnObjectIdentifier ASN_Index;
+
+ OID_Index[0] = Index;
+ ASN_Index.ids = OID_Index;
+ ASN_Index.idLength = 1;
+
+ SnmpUtilOidAppend(Oid, &ASN_Index);
+
+}
+
+/*-------------------------------------------------------------------------
+ *
+ * MagtFillTrapVars: Fills in the variable list for the specified trap.
+ *
+ * Returns: 0 - No variable filled
+ * n - Number of variables filled
+ *
+ *-----------------------------------------------------------------------*/
+
+int MagtFillTrapVars(int trapType, RFC1157VarBind *trapVars, MagtStaticInfo_t *pCfgInfo)
+{
+ MagtDispStr_t varVals[4];
+ int nVarLen = 0;
+ int i, j;
+ static AsnObjectIdentifier varOid[4] = {{MAGT_OID_SIZEOF(OID_EntityDescr),
+ OID_EntityDescr},
+ {MAGT_OID_SIZEOF(OID_EntityVers),
+ OID_EntityVers},
+ {MAGT_OID_SIZEOF(OID_EntityLocation),
+ OID_EntityLocation},
+ {MAGT_OID_SIZEOF(OID_EntityContact),
+ OID_EntityContact}};
+
+ /*
+ * Get the variable values from the static info which has been obtained
+ * from the snmp config file at initialization time.
+ */
+ varVals[0].len = pCfgInfo->entityDescr.len;
+ varVals[0].val = pCfgInfo->entityDescr.val;
+ varVals[1].len = pCfgInfo->entityVers.len;
+ varVals[1].val = pCfgInfo->entityVers.val;
+ varVals[2].len = pCfgInfo->entityLocation.len;
+ varVals[2].val = pCfgInfo->entityLocation.val;
+ varVals[3].len = pCfgInfo->entityContact.len;
+ varVals[3].val = pCfgInfo->entityContact.val;
+
+ for (i = 0; i < 4; i++)
+ {
+ SNMP_oidcpy(&trapVars[i].name, &MIB_OidPrefix);
+ SNMP_oidappend(&trapVars[i].name, &varOid[i]);
+ trapVars[i].value.asnType = ASN_OCTETSTRING;
+ trapVars[i].value.asnValue.string.length = varVals[i].len;
+ trapVars[i].value.asnValue.string.stream =
+ SNMP_malloc((trapVars[i].value.asnValue.string.length) *
+ sizeof(char));
+ if (trapVars[i].value.asnValue.string.stream == NULL)
+ {
+
+ /*
+ * Clean up any allocated variable binding allocated up until now.
+ */
+ for (j = 0; j < i; j++)
+ {
+ SNMP_FreeVarBind(&trapVars[j]);
+ return nVarLen;
+ }
+ }
+ memcpy(trapVars[i].value.asnValue.string.stream, varVals[i].val,
+ trapVars[i].value.asnValue.string.length);
+ trapVars[i].value.asnValue.string.dynamic = TRUE;
+ }
+
+ switch (trapType)
+ {
+ case MAGT_TRAP_SERVER_DOWN:
+ nVarLen = 4;
+ break;
+ case MAGT_TRAP_SERVER_START:
+ nVarLen = 3;
+ break;
+ default:
+ break;
+ }
+
+ return nVarLen;
+}
+
+/*-------------------------------------------------------------------------
+ *
+ * MagtMIBLeafFunc: Performs generic actions on leaf variables in the MIB.
+ * Note that SET action is not supported.
+ *
+ * Returns: SNMP_ERRORSTATUS_NOERROR - No error
+ * PDU error codes - Errors
+ *
+ *-----------------------------------------------------------------------*/
+
+UINT MagtMIBLeafFunc(IN UINT Action, IN MagtMibEntry_t *mibPtr,
+ IN RFC1157VarBind *VarBind)
+{
+ UINT ErrStat = SNMP_ERRORSTATUS_NOERROR;
+ static AsnObjectIdentifier ApplIndexOid = {MAGT_OID_SIZEOF(OID_ApplIndex),
+ OID_ApplIndex};
+ instance_list_t *pInstance;
+ char logMsg[1024];
+
+ switch(Action)
+ {
+ case MAGT_MIB_ACTION_GETNEXT:
+ /*
+ * If there is no next pointer, this is the end of the MIB tree.
+ */
+ if (mibPtr->MibNext == NULL)
+ {
+ ErrStat = SNMP_ERRORSTATUS_NOSUCHNAME;
+ return ErrStat;
+ }
+
+ /*
+ * Set up VarBind to contain the OID of the next variable.
+ */
+ SNMP_oidfree(&VarBind->name);
+ SNMP_oidcpy(&VarBind->name, &MIB_OidPrefix);
+ SNMP_oidappend(&VarBind->name, &mibPtr->MibNext->Oid);
+
+ /*
+ * Call function to process the GET request.
+ */
+ ErrStat = (*mibPtr->MibNext->MibFunc)(MAGT_MIB_ACTION_GET, mibPtr->MibNext,
+ VarBind);
+ break;
+ case MAGT_MIB_ACTION_GET:
+
+ /*
+ * Make sure that the variable's access right allows GET.
+ */
+ if (mibPtr->Access != MAGT_MIB_ACCESS_READ &&
+ mibPtr->Access != MAGT_MIB_ACCESS_READWRITE)
+ {
+ ErrStat = SNMP_ERRORSTATUS_NOSUCHNAME;
+ return ErrStat;
+ }
+
+ if (mibPtr->Storage == NULL) /* Counter not supported */
+ {
+ ErrStat = SNMP_ERRORSTATUS_GENERR;
+ return ErrStat;
+ }
+
+ if ((VarBind->name.ids[MAGT_MIB_PREFIX_LEN] > 1) &&
+ (SNMP_oidcmp(&mibPtr->Oid, &ApplIndexOid) != 0))
+ {
+
+ /*
+ * Read stats file to update counter statistics.
+ */
+
+ /* need to update all of them because don't know which instance resulted
+ into call into this function */
+ for (pInstance = pInstanceList; pInstance; pInstance = pInstance->pNext)
+ {
+ if (MagtReadStats(NULL, pInstance->pOpsStatInfo,
+ pInstance->pEntriesStatInfo,
+ pInstance->ppIntStatInfo,
+ pInstance->szStatsPath,
+ pInstance->szLogPath) != 0)
+ {
+
+ /* this server is off/or went down since we
+ started up snmp. The snmp agent will
+ return last values it was set to until
+ server starts back up. If server was not
+ started will return null for strings and
+ 0 for values */
+
+ /* to log for each snmp request is to expensive
+ for now, just silently acknowledge the fact
+ and think about something better for the future
+ */
+
+
+ }
+ }
+ }
+
+ /*
+ * Set up VarBind's return value.
+ */
+ VarBind->value.asnType = mibPtr->Type;
+ switch (VarBind->value.asnType)
+ {
+ case ASN_RFC1155_TIMETICKS:
+ case ASN_RFC1155_COUNTER:
+ case ASN_RFC1155_GAUGE:
+ case ASN_INTEGER:
+ VarBind->value.asnValue.number = *(AsnInteger *)(mibPtr->Storage);
+ break;
+ case ASN_RFC1155_IPADDRESS:
+ case ASN_OCTETSTRING: /* = ASN_RFC1213_DISPSTRING */
+ VarBind->value.asnValue.string.length =
+ strlen((LPSTR)mibPtr->Storage);
+ VarBind->value.asnValue.string.stream =
+ SNMP_malloc((VarBind->value.asnValue.string.length + 2) *
+ sizeof(char));
+ if (VarBind->value.asnValue.string.stream == NULL)
+ {
+ ErrStat = SNMP_ERRORSTATUS_GENERR;
+ return ErrStat;
+ }
+ memcpy(VarBind->value.asnValue.string.stream,
+ (LPSTR)mibPtr->Storage,
+ VarBind->value.asnValue.string.length);
+ VarBind->value.asnValue.string.dynamic = TRUE;
+ break;
+ case ASN_OBJECTIDENTIFIER:
+ VarBind->value.asnValue.object =
+ *(AsnObjectIdentifier *)(mibPtr->Storage);
+ break;
+ default:
+ ErrStat = SNMP_ERRORSTATUS_GENERR;
+ break;
+ } /* Switch */
+
+ break;
+ default:
+ ErrStat = SNMP_ERRORSTATUS_GENERR;
+ break;
+ } /* Switch */
+
+ return ErrStat;
+}
+
+/*-------------------------------------------------------------------------
+ *
+ * MagtResolveVarBind: Resolves a single variable binding. Modifies the
+ * variable on a GET or a GETNEXT.
+ *
+ * Returns: SNMP_ERRORSTATUS_NOERROR - No error
+ * PDU error codes - Errors
+ *
+ *-----------------------------------------------------------------------*/
+
+UINT MagtResolveVarBind(IN OUT RFC1157VarBind *VarBind, IN UINT PduAction)
+{
+ MagtMibEntry_t *mibPtr = NULL;
+ AsnObjectIdentifier TempOid;
+ int CompResult;
+ UINT i = 0;
+ UINT nResult;
+ instance_list_t *pInstance;
+
+ pInstance = pInstanceList;
+
+ while (mibPtr == NULL && pInstance !=NULL)
+ {
+ /*
+ * Construct OID with complete prefix for comparison purpose
+ */
+ SNMP_oidcpy(&TempOid, &MIB_OidPrefix);
+ SNMP_oidappend(&TempOid, &(pInstance->pMibInfo[i].Oid));
+
+ /*
+ * Check for OID in MIB. On a GET-NEXT, the OID does not have to match
+ * exactly a variable in the MIB, it must only fall under the MIB root.
+ */
+ CompResult = SNMP_oidcmp(&VarBind->name, &TempOid);
+
+ if (CompResult < 0) /* Not an exact match */
+ {
+ if (PduAction != MAGT_MIB_ACTION_GETNEXT) /* Only GET-NEXT is valid */
+ {
+
+ pInstance=pInstance->pNext;
+ i=0;
+ if(pInstance == NULL)
+ {
+ nResult = SNMP_ERRORSTATUS_NOSUCHNAME;
+ return nResult;
+ }else{
+ continue;
+ }
+
+ }
+
+ /*
+ * Since the match was not exact, but var bind name is within MIB,
+ * we are at the next MIB variable down from the one specified.
+ */
+ PduAction = MAGT_MIB_ACTION_GET;
+ mibPtr = &(pInstance->pMibInfo[i]);
+
+ /*
+ * Replace var bind name with new name.
+ */
+ SNMP_oidfree(&VarBind->name);
+ SNMP_oidcpy(&VarBind->name, &MIB_OidPrefix);
+ SNMP_oidappend(&VarBind->name, &mibPtr->Oid);
+ }
+ else
+ {
+ if (CompResult == 0) /* Found an exact match */
+ {
+ mibPtr = &(pInstance->pMibInfo[i]);
+ }else{
+ /* see if it is one of the other ApplIndex */
+ instance_list_t *pApplIndex;
+ for(pApplIndex = pInstance; pApplIndex; pApplIndex=pApplIndex->pNext)
+ {
+ SNMP_oidfree(&TempOid);
+
+ SNMP_oidcpy(&TempOid, &MIB_OidPrefix);
+ SNMP_oidappend(&TempOid, &(pApplIndex->pMibInfo[i].Oid));
+
+ CompResult = SNMP_oidcmp(&VarBind->name, &TempOid);
+ if(CompResult == 0)
+ {
+ mibPtr = &(pApplIndex->pMibInfo[i]);
+ }
+ }
+ }
+
+
+ }
+
+ /*
+ * Free OID memory before checking another variable.
+ */
+ SNMP_oidfree(&TempOid);
+ i++;
+
+ if(i == MIB_num_vars)
+ {
+ pInstance=pInstance->pNext;
+ i=0;
+ }
+ } /* While */
+
+ if (mibPtr == NULL) /* OID not within MIB's scope */
+ {
+ nResult = SNMP_ERRORSTATUS_NOSUCHNAME;
+ return nResult;
+ }
+
+ if (*mibPtr->MibFunc == NULL)
+ {
+ nResult = SNMP_ERRORSTATUS_NOSUCHNAME;
+ return nResult;
+ }
+
+ /*
+ * Call function to process request. Each MIB entry has a function pointer
+ * that knows how to process its MIB variable.
+ */
+ nResult = (*mibPtr->MibFunc)(PduAction, mibPtr, VarBind);
+
+ SNMP_oidfree(&TempOid); /* Free temp memory */
+
+ return nResult;
+
+}
diff --git a/ldap/servers/snmp/ntagt/nsldapmib_nt.h b/ldap/servers/snmp/ntagt/nsldapmib_nt.h
new file mode 100644
index 00000000..ac53dad7
--- /dev/null
+++ b/ldap/servers/snmp/ntagt/nsldapmib_nt.h
@@ -0,0 +1,130 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+
+/*-------------------------------------------------------------------------
+ *
+ * nsldapmib_nt.h - Definitions for NS Directory Server's MIB on NT.
+ *
+ * Revision History:
+ * 07/25/1997 Steve Ross Created
+ *
+ *
+ *
+ *-----------------------------------------------------------------------*/
+
+#ifndef __NSLDAPMIB_NT_H_
+#define __NSLDAPMIB_NT_H_
+
+/*-------------------------------------------------------------------------
+ *
+ * Defines
+ *
+ *-----------------------------------------------------------------------*/
+
+#define MAGT_MIB_PREFIX_LEN MIB_OidPrefix.idLength
+#define MAGT_MAX_STRING_LEN 255
+
+#define MAGT_MIB_ACCESS_READ 0
+#define MAGT_MIB_ACCESS_WRITE 1
+#define MAGT_MIB_ACCESS_READWRITE 2
+
+#define MAGT_MIB_ACTION_GET ASN_RFC1157_GETREQUEST
+#define MAGT_MIB_ACTION_SET ASN_RFC1157_SETREQUEST
+#define MAGT_MIB_ACTION_GETNEXT ASN_RFC1157_GETNEXTREQUEST
+
+/*
+ * Macro to determine number of sub-oids in array.
+ */
+#define MAGT_OID_SIZEOF(Oid) (sizeof Oid / sizeof(UINT))
+
+/*
+ * Unique ID for each entry in the MIB.
+ */
+enum
+{
+ MAGT_ID_DESC = 0,
+ MAGT_ID_VERS,
+ MAGT_ID_ORG,
+ MAGT_ID_LOC,
+ MAGT_ID_CONTACT,
+ MAGT_ID_NAME,
+ /* operations table attrs */
+ MAGT_ID_ANONYMOUS_BINDS,
+ MAGT_ID_UNAUTH_BINDS,
+ MAGT_ID_SIMPLE_AUTH_BINDS,
+ MAGT_ID_STRONG_AUTH_BINDS ,
+ MAGT_ID_BIND_SECURITY_ERRORS,
+ MAGT_ID_IN_OPS,
+ MAGT_ID_READ_OPS,
+ MAGT_ID_COMPARE_OPS,
+ MAGT_ID_ADD_ENTRY_OPS,
+ MAGT_ID_REMOVE_ENTRY_OPS,
+ MAGT_ID_MODIFY_ENTRY_OPS,
+ MAGT_ID_MODIFY_RDN_OPS,
+ MAGT_ID_LIST_OPS,
+ MAGT_ID_SEARCH_OPS,
+ MAGT_ID_ONE_LEVEL_SEARCH_OPS,
+ MAGT_ID_WHOLE_SUBTREE_SEARCH_OPS,
+ MAGT_ID_REFERRALS,
+ MAGT_ID_CHAININGS,
+ MAGT_ID_SECURITY_ERRORS,
+ MAGT_ID_ERRORS,
+ /* entries table attrs */
+ MAGT_ID_MASTER_ENTRIES,
+ MAGT_ID_COPY_ENTRIES,
+ MAGT_ID_CACHE_ENTRIES,
+ MAGT_ID_CACHE_HITS,
+ MAGT_ID_SLAVE_HITS,
+ /* interaction table entries */
+ MAGT_ID_DS_NAME,
+ MAGT_ID_TIME_OF_CREATION,
+ MAGT_ID_TIME_OF_LAST_ATTEMPT,
+ MAGT_ID_TIME_OF_LAST_SUCCESS,
+ MAGT_ID_FAILURES_SINCE_LAST_SUCCESS,
+ MAGT_ID_FAILURES,
+ MAGT_ID_SUCCESSES,
+ MAGT_ID_URL,
+ /* applIndex */
+ MAGT_ID_APPLINDEX
+};
+
+/*-------------------------------------------------------------------------
+ *
+ * Types
+ *
+ *-----------------------------------------------------------------------*/
+
+typedef struct MagtMibEntry
+{
+ AsnObjectIdentifier Oid;
+ void *Storage;
+ BYTE Type;
+ UINT Access;
+ UINT (*MibFunc)(UINT, struct MagtMibEntry *, RFC1157VarBind *);
+ struct MagtMibEntry *MibNext;
+ UINT uId;
+} MagtMibEntry_t;
+
+#include "nsldapagt_nt.h"
+/*-------------------------------------------------------------------------
+ *
+ * Prototypes
+ *
+ *-----------------------------------------------------------------------*/
+
+int MagtFillTrapVars(int trapType,
+ RFC1157VarBind * trapVars,
+ MagtStaticInfo_t * pCfgInfo);
+
+UINT MagtMIBLeafFunc(IN UINT Action,
+ IN MagtMibEntry_t *MibPtr,
+ IN RFC1157VarBind *VarBind);
+
+UINT MagtResolveVarBind(IN OUT RFC1157VarBind *VarBind,
+ IN UINT PduAction);
+
+int Mib_init(MagtMibEntry_t **Mib, int ApplIndex);
+#endif /* __NSLDAPMIB_NT_H_ */