summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBill MacAllister <whm@stanford.edu>2016-08-14 00:54:20 -0700
committerBill MacAllister <whm@stanford.edu>2016-08-14 00:54:20 -0700
commitfc4d04018c3a527bb9e43f2fdec959f85270fc74 (patch)
treed1669d785360c3c3ffffb6f957bb2c5f3cf563d7
Imported Upstream version 3.0.3
-rw-r--r--Changes190
-rw-r--r--Credits42
-rw-r--r--LDAPapi.pm2917
-rw-r--r--LDAPapi.xs1650
-rw-r--r--MANIFEST20
-rw-r--r--META.yml11
-rw-r--r--Makefile.PL131
-rw-r--r--README222
-rw-r--r--Todo15
-rw-r--r--constant.gen379
-rwxr-xr-xexamples/ldap_mod_attr.pl257
-rwxr-xr-xexamples/ldapwalk.pl175
-rwxr-xr-xexamples/ldapwalk2.pl113
-rwxr-xr-xexamples/testwrite.pl80
-rwxr-xr-xexamples/updatepw.pl95
-rwxr-xr-xexamples/web500.pl1158
-rwxr-xr-xexamples/www-ldap.pl362
-rw-r--r--ldap_compat.h22
-rw-r--r--test.pl130
-rw-r--r--typemap25
20 files changed, 7994 insertions, 0 deletions
diff --git a/Changes b/Changes
new file mode 100644
index 0000000..ea4adfb
--- /dev/null
+++ b/Changes
@@ -0,0 +1,190 @@
+Revision history for Perl5 Net::LDAPapi Module.
+3.0.3 Wed Aug 20 12:23:00 PST 2008
+ - Add Convert::ASN1 requirement.
+ - Fix error code handling (CPAN bug#35910)
+3.0.2 Wed Jan 02 12:23:00 PST 2008
+ - Fix various variable initializations and scope issues
+ - Fix initialization of sasl parm
+ - Fix test unbind
+ - Add ldap_perror function
+3.0.1 Thu Aug 09 17:22:15 PDT 2007
+ - New 3.0.1 release, fixes sasl headers
+3.0.0 Thu Jul 12 12:13:00 PDT 2007
+ - New 3.0 release, using the LDAP V3 api
+2.01 Fri Feb 09 08:41:46 PST 2007
+ - Fix library linking
+2.00 Tue Feb 06 18:46:38 PST 2007
+ - Stripped out ISODE, UMich bits
+ - Updated to refer to Mozilla C SDK
+1.43 Sat Jun 13 02:39:15 CDT 1998
+ - Added replacement for strcasecmp that was more portable
+ - Fixed a few memory leaks caused by changes in 1.42 and
+ tested these changes on both Netscape and UMICH SDKs.
+ - Changed the test.pl to be more intuitive and no longer require
+ modifications.
+ - Made a few cosmetic changes to Makefile.PL
+1.42 Sat Apr 25 22:27:22 CDT 1998
+ - Added named arguments to all methods
+ - Got rid of a few old ber_free's that were causing problems
+ - Fixed the sort functions (Thanks to Miguel Marques)
+ - Added the ldap_url functions
+ - I was destroying error messages before I printed them in a few
+ of the ldapwalk examples. Whoops. Fixed.
+ - Fixed problem with Out of Memory errors on some platforms with
+ the get_all_values function.
+ - Fixed a multitude of problems with rebinding
+ - Resolved issue resulting in dumped core on modify where NULL
+ values were passed.
+ - Much more extensive testing by the author against both Netscape
+ and University of Michigan libraries. Many small corrections.
+ - Added Makefile.PL options for Netscape LDAPv3 SDK. No v3 calls
+ added yet.
+1.40a Wed Jan 28 11:20:51 CST 1998
+ - sort_entries and multisort_entries have been added with the single
+ arguments $attr and \@attr respectively. Neither seem to work
+ properly on my system when sorting anything besides DN (attr =
+ non-null). Please try this function on your system and let me
+ know if it you can get it to work. If so, I will finish adding
+ this function with another argument to specify a perl based compare
+ function for the sort.
+ - Lots of changes from Hallvard B. Furuseth <h.b.furuseth@usit.uio.no>
+ Including:
+ o Got rid of extra checks in LDAPapi.pm where checks exist in
+ XS or Perl.
+ o Cleaned up ldap_<add/modify>[_s] with a few simple changes to
+ hash2mod() function. Also fixed bug in that function that
+ causes problems when you supply an empty hash.
+ o Small typos and bug fixes in test.pl fixed.
+ o Error string is now retrieved when requested, rather than after
+ every call (What was I thinking when I did this? Who knows....)
+ o Fixed some reverse logic in ldap_<set/get>_option in the XS file.
+ - Changed memory related calls to use 'New' and 'Safefree' macros,
+ which seems to be what the perlguts document suggests.
+ - Got rid of the ber_free define in ldap_compat.h when using UMICH.
+ This was causing core dumps on some platforms.
+ - Added ldap_mod_attr.pl to the examples. A wonderful script from
+ Andrew J Cosgriff <ajc@bing.wattle.id.au>.
+
+1.40 Wed Jan 21 11:12:24 CST 1998
+ - I never did include the new version of web500.pl that supports
+ Add functionality. It did exist, and I've included it with this
+ release.
+ - There was a bug when adding or modifying a record where the value
+ was set to be an integer. The bug has probably existed since a very
+ old version of the module. It has now been fixed.
+ - Fix to internal rebind process for Netscape API users provided by
+ Rob Weltman <rweltman@netscape.com> of Netscape.
+
+1.40beta Tue Jan 13 14:54:13 CST 1998
+ - Wow, what hasn't changed in this version...
+ - Removed the extra 'free' in ldap_get_dn that was submitted as
+ a fix to a memory leak. The fix seems to give inconsistent results
+ on different platforms and APIs.
+ - Added Perl-OO methods for virtually all API calls. The C style
+ API calls still work, and will work in all future versions.
+ Note that I haven't added named arguments yet, so argument order
+ matters. This should be in the next version.
+ - Manpages completely rewritten. Please send me any errors or typos.
+ - All examples except the two web ones have been rewritten to run under
+ 'use Strict'.
+ - Test program and all examples except web500.pl have been rewritten
+ to use Perl-OO methods. Might need to clean-up comments and code
+ after so many rewrites.
+ - Minor bugfixes to web500.pl
+
+1.39a Tue Nov 25 16:37:35 CST 1997
+ - Version 1.39 "overimproved" memory handling. It seemed to dump
+ core, as it occasionally freed memory that Perl felt very inclined
+ to freeing on its own. Whoops! This is only a bugfix release
+ put out while I work on v1.40.
+ - In the process of debugging, also changed several scripts to run
+ under 'use strict' and 'perl -w'.
+
+1.39 Mon Oct 27 15:33:12 CST 1997
+ - Added Al Grimstead's memory leak fix for ldap_get_all_entries.
+ - Added Windows NT 4.0 Support (using Perl5.004, not ActiveState Perl)
+ - Added finished web500.pl Gateway
+ - Improved memory handling in Add/Modify Routine
+ - Included updatepw.pl in the examples directory. This is a script
+ that can syncronize unix passwords into directories supporting
+ crypted passwords.
+
+1.36 Fri Aug 29 14:54:38 CDT 1997
+ - Added ldap_get_all_entries, which is not a C API command, but
+ useful anyway. Thanks to Andreas Beck for some of the code to
+ make this possible.
+ - Included a few new examples (ldapwalk2.pl and web500.pl)
+
+1.35 Tue Aug 26 15:44:25 CDT 1997
+ - Added SSL Functions when using Netscape SDK
+ - Added the following functions for non-Netscape SDKs (and ensured
+ that they now work properly for Netscape SDK users):
+ ldap_get_lderrno (to get errno from LDAP struct)
+ ldap_set_lderrno (to set errno from LDAP struct)
+ ldap_msgid (to obtain msgid from an LDAPMessage *)
+ ldap_msgtype (to obtain msgtype from an LDAPMessage *)
+ ldap_set_option (to set some LDAP * options)
+ ldap_get_option (to get some LDAP * options)
+ - Added support for a rebind process. Mostly untested, so let me know
+ how well this works for you. You can set this to a PERL routine
+ using ldap_set_rebind_proc($ld,\&perl_function_name), then make
+ sure that this function returns DN, PASSWORD, and AUTHTYPE.
+ - Added more code and documentation fixes from Hallvard Furuseth
+ - Fixed documentation/code errors noted by Al Grimstad <alg@att.com>
+ - Added ldap_compat.h for non-Netscape SDK users.
+ - Added Credits file containing people who have provided feedback
+ and fixes.
+ - Added Todo file containing list of things I'm working on (or
+ would like to be working on).
+
+1.31 Tue Jul 29 17:25:54 CDT 1997
+ - Added function prototypes for internal functions so that some C
+ compilers don't complain.
+ - Fixed the ldap_*_option functions for Netscape SDK users.
+ - Implemented fixes from Hallvard Furuseth <h.b.furuseth@usit.uio.no>
+ for ISODE support and lots of code cleanup, including a very nice
+ script to autogenerate a constant.h file. THANKS!
+
+1.30 Tue Jul 22 17:19:36 CDT 1997
+ - Fixes Makefile.PL to add Kerberos libraries if needed. Also added
+ LDAP_AUTH_KRB* defines to LDAPapi.xs file.
+ - Changed all malloc to PERL5 safe versions.
+ - Now properly free all LDAPMod structures after ldap_add & ldap_modify
+ - Completely rewrote add and modify routines and added binary value
+ support to add and modify command
+ - Added ldap_get_values_len function to support retrieving binary
+ attributes.
+
+1.21 Fri Jul 18 08:53:11 CDT 1997
+ - Fixed the compile warnings with University of Michigan
+ - Changed www-ldap.pl to only submit changed items.
+ - Updated all files changing module name from LDAP to Net::LDAPapi
+
+1.20 Thu Jul 17 01:20:08 CDT 1997
+ - Changed LDAP.xs - added hash2add and hash2mod internal routines
+ and seriously cleaned up the add and modify routines.
+ - References to Arrays (\%) are now required as the third parameter
+ to ldap_add* and ldap_modify* functions.
+ - Updated README, Man Page in LDAP.pm, and other documentation
+ changes.
+
+1.11 Wed Jul 16 10:48:17 CDT 1997
+ - Changed LDAP.xs and Makefile.PL to make this module work with
+ University of Michigan LDAP SDK. Tested on Solaris.
+ - 'ldap_init' call does not seem to work properly if compiled with
+ University of Michigan LDAP SDK. Otherwise all OK. Use ldap_open
+ as a temporary work-around.
+
+1.1 Tue Jul 15 17:54:27 CDT 1997
+ - Fixed a few simple bugs in ldap_modify* and ldap_add* commands.
+ - Included new www-ldap.pl CGI script
+
+1.0 Thu Jun 26 13:35:14 CDT 1997
+ - All Async and Sync Add, Modify, and Delete commands have been
+ added. As have commands to manipulate DNs and other data.
+ - FIRST RELEASE VERSION
+ - See README for supported LDAP API calls
+
+0.5 Tue May 6 09:00:31 CDT 1997
+ - original version; Original name: LDAP. Modify Routines Not Yet
+ Available
diff --git a/Credits b/Credits
new file mode 100644
index 0000000..309889a
--- /dev/null
+++ b/Credits
@@ -0,0 +1,42 @@
+###############################################
+# Net::LDAPapi - Credits List #
+###############################################
+
+Special thanks to the following people
+for providing useful fixes and suggestions.
+
+Hallvard B. Furuseth <h.b.furuseth@usit.uio.no> (Various Fixes/Suggestions)
+ - Most of the fixes and suggestions implemeted in 1.40a were from Hallvard.
+ - Many of fixes made in 1.21 were also suggested by Hallvard.
+Andreas Beck <beck@sdm.de>
+ - Supplied much of the code and the idea for ldap_get_all_entries
+Douglas Gray Stephens <gray@austin.sar.slb.com>
+ - Various fixes, both to the CGIs and other things...
+Al Grimstad <alg@att.com>
+ - Helped debug lderrno/etc and helped fix a number of memory leaks
+Christian Murphy <r28874@ismuc.sps.mot.com>
+ - Small fix for AIX Library Name in Makefile
+Jauder Ho <jauderho@transmeta.com>
+ - Redhat Linux Makefile Fix and useful bug reports
+Bill Dixon <billd@firstsolutions.com>
+ - His example isolated the bug w/ integer modifys
+Rob Weltman <rweltman@netscape.com>
+ - Supplied fix for rebinding when using Netscape SDK
+Andrew J Cosgriff <ajc@bing.wattle.id.au>
+ - The wonderful ldap_mod_attr.pl script
+ - Various Useful Bug Reports
+Dmitri Priimak <priimak@stanford.edu>
+ - For doing the majority of the Net::LDAPapi 3.0 work
+Howard Chu <hyc@symas.com>
+ - For getting this project started again
+Marcus Watts
+ - For submitting patches to the new code line
+--
+Quanah Gibson-Mount
+mishikal@yahoo.com
+CPAN: /by-authors/id/M/MI/MISHIKAL/
+--
+Clayton Donley
+donley@wwa.com
+http://www.wwa.com/~donley/
+CPAN: /authors/id/CDONLEY
diff --git a/LDAPapi.pm b/LDAPapi.pm
new file mode 100644
index 0000000..113d922
--- /dev/null
+++ b/LDAPapi.pm
@@ -0,0 +1,2917 @@
+package Net::LDAPapi;
+
+use strict;
+use Carp;
+use Convert::ASN1;
+use vars qw($VERSION @ISA @EXPORT $AUTOLOAD);
+
+require Exporter;
+require DynaLoader;
+require AutoLoader;
+
+@ISA = qw(Exporter DynaLoader);
+# Items to export into callers namespace by default. Note: do not export
+# names by default without a very good reason. Use EXPORT_OK instead.
+# Do not simply export all your public functions/methods/constants.
+@EXPORT =
+ qw(
+ ldap_create ldap_set_option ldap_get_option ldap_unbind_ext
+ ldap_unbind_ext_s ldap_version ldap_abandon_ext ldap_add_ext ldap_add_ext_s
+ ldap_set_rebind_proc
+ ldap_rename ldap_rename_s
+ ldap_compare_ext ldap_compare_ext_s ldap_delete_ext
+ ldap_delete_ext_s ldap_search_ext ldap_search_ext_s ldap_result
+ ldap_msgfree ldap_msg_free ldap_msgid ldap_msgtype
+ ldap_get_lderrno ldap_set_lderrno ldap_parse_result ldap_err2string
+ ldap_count_entries ldap_first_entry ldap_next_entry ldap_get_dn
+ ldap_err2string ldap_dn2ufn ldap_str2dn ldap_str2rdn ldap_explode_rdn
+ ldap_explode_dns ldap_first_attribute ldap_next_attribute
+ ldap_get_values ldap_get_values_len ldap_sasl_bind ldap_sasl_bind_s
+ ldapssl_client_init ldapssl_init ldapssl_install_routines
+ ldap_get_all_entries ldap_multisort_entries
+ ldap_is_ldap_url ldap_url_parse ldap_url_search ldap_url_search_s
+ ldap_url_search_st ber_free ldap_init ldap_initialize ldap_start_tls_s
+ ldap_sasl_interactive_bind_s
+ ldap_create_control ldap_control_berval
+ LDAP_RES_BIND
+ LDAP_RES_SEARCH_ENTRY
+ LDAP_RES_SEARCH_REFERENCE
+ LDAP_RES_SEARCH_RESULT
+ LDAP_RES_MODIFY
+ LDAP_RES_ADD
+ LDAP_RES_DELETE
+ LDAP_RES_MODDN
+ LDAP_RES_COMPARE
+ LDAP_RES_EXTENDED
+ LDAP_RES_INTERMEDIATE
+ LDAP_RES_ANY
+ LDAP_RES_UNSOLICITED
+ LDAPS_PORT
+ LDAP_ADMIN_LIMIT_EXCEEDED
+ LDAP_AFFECTS_MULTIPLE_DSAS
+ LDAP_ALIAS_DEREF_PROBLEM
+ LDAP_ALIAS_PROBLEM
+ LDAP_ALREADY_EXISTS
+ LDAP_AUTH_KRBV4
+ LDAP_AUTH_KRBV41
+ LDAP_AUTH_KRBV41_30
+ LDAP_AUTH_KRBV42
+ LDAP_AUTH_KRBV42_30
+ LDAP_AUTH_NONE
+ LDAP_AUTH_SASL
+ LDAP_AUTH_SIMPLE
+ LDAP_AUTH_UNKNOWN
+ LDAP_BUSY
+ LDAP_CACHE_CHECK
+ LDAP_CACHE_LOCALDB
+ LDAP_CACHE_POPULATE
+ LDAP_CALLBACK
+ LDAP_COMPARE_FALSE
+ LDAP_COMPARE_TRUE
+ LDAP_CONNECT_ERROR
+ LDAP_CONSTRAINT_VIOLATION
+ LDAP_CONTROL_ASSERT
+ LDAP_CONTROL_DUPENT
+ LDAP_CONTROL_DUPENT_ENTRY
+ LDAP_CONTROL_DUPENT_REQUEST
+ LDAP_CONTROL_DUPENT_RESPONSE
+ LDAP_CONTROL_GROUPING
+ LDAP_CONTROL_MANAGEDIT
+ LDAP_CONTROL_MANAGEDSAIT
+ LDAP_CONTROL_NOOP
+ LDAP_CONTROL_NO_SUBORDINATES
+ LDAP_CONTROL_PAGEDRESULTS
+ LDAP_CONTROL_PASSWORDPOLICYREQUEST
+ LDAP_CONTROL_PASSWORDPOLICYRESPONSE
+ LDAP_CONTROL_PERSIST_ENTRY_CHANGE_NOTICE
+ LDAP_CONTROL_PERSIST_REQUEST
+ LDAP_CONTROL_POST_READ
+ LDAP_CONTROL_PRE_READ
+ LDAP_CONTROL_PROXY_AUTHZ
+ LDAP_CONTROL_SLURP
+ LDAP_CONTROL_SORTREQUEST
+ LDAP_CONTROL_SORTRESPONSE
+ LDAP_CONTROL_SUBENTRIES
+ LDAP_CONTROL_SYNC
+ LDAP_CONTROL_SYNC_DONE
+ LDAP_CONTROL_SYNC_STATE
+ LDAP_CONTROL_VALSORT
+ LDAP_CONTROL_VALUESRETURNFILTER
+ LDAP_CONTROL_VLVREQUEST
+ LDAP_CONTROL_VLVRESPONSE
+ LDAP_CONTROL_X_CHAINING_BEHAVIOR
+ LDAP_CONTROL_X_DOMAIN_SCOPE
+ LDAP_CONTROL_X_EXTENDED_DN
+ LDAP_CONTROL_X_INCREMENTAL_VALUES
+ LDAP_CONTROL_X_PERMISSIVE_MODIFY
+ LDAP_CONTROL_X_SEARCH_OPTIONS
+ LDAP_CONTROL_X_TREE_DELETE
+ LDAP_CONTROL_X_VALUESRETURNFILTER
+ LDAP_CUP_INVALID_DATA
+ LDAP_DECODING_ERROR
+ LDAP_DEREF_ALWAYS
+ LDAP_DEREF_FINDING
+ LDAP_DEREF_NEVER
+ LDAP_DEREF_SEARCHING
+ LDAP_ENCODING_ERROR
+ LDAP_FILTER_ERROR
+ LDAP_FILT_MAXSIZ
+ LDAP_INAPPROPRIATE_AUTH
+ LDAP_INAPPROPRIATE_MATCHING
+ LDAP_INSUFFICIENT_ACCESS
+ LDAP_INVALID_CREDENTIALS
+ LDAP_INVALID_DN_SYNTAX
+ LDAP_INVALID_SYNTAX
+ LDAP_IS_LEAF
+ LDAP_LOCAL_ERROR
+ LDAP_LOOP_DETECT
+ LDAP_MOD_ADD
+ LDAP_MOD_BVALUES
+ LDAP_MOD_DELETE
+ LDAP_MOD_REPLACE
+ LDAP_NAMING_VIOLATION
+ LDAP_NOT_ALLOWED_ON_NONLEAF
+ LDAP_NOT_ALLOWED_ON_RDN
+ LDAP_NO_LIMIT
+ LDAP_NO_MEMORY
+ LDAP_NO_OBJECT_CLASS_MODS
+ LDAP_NO_SUCH_ATTRIBUTE
+ LDAP_NO_SUCH_OBJECT
+ LDAP_OBJECT_CLASS_VIOLATION
+ LDAP_OPERATIONS_ERROR
+ LDAP_OPT_CACHE_ENABLE
+ LDAP_OPT_CACHE_FN_PTRS
+ LDAP_OPT_CACHE_STRATEGY
+ LDAP_OPT_DEBUG_LEVEL
+ LDAP_OPT_DEREF
+ LDAP_OPT_DESC
+ LDAP_OPT_DNS
+ LDAP_OPT_IO_FN_PTRS
+ LDAP_OPT_OFF
+ LDAP_OPT_ON
+ LDAP_OPT_PROTOCOL_VERSION
+ LDAP_OPT_REBIND_ARG
+ LDAP_OPT_REBIND_FN
+ LDAP_OPT_REFERRALS
+ LDAP_OPT_REFERRAL_HOP_LIMIT
+ LDAP_OPT_RESTART
+ LDAP_OPT_SIZELIMIT
+ LDAP_OPT_SSL
+ LDAP_OPT_THREAD_FN_PTRS
+ LDAP_OPT_TIMELIMIT
+ LDAP_OTHER
+ LDAP_PARAM_ERROR
+ LDAP_PARTIAL_RESULTS
+ LDAP_PORT
+ LDAP_PORT_MAX
+ LDAP_PROTOCOL_ERROR
+ LDAP_REFERRAL
+ LDAP_RESULTS_TOO_LARGE
+ LDAP_SASL_AUTOMATIC
+ LDAP_SASL_INTERACTIVE
+ LDAP_SASL_NULL
+ LDAP_SASL_QUIET
+ LDAP_SASL_SIMPLE
+ LDAP_SCOPE_BASE
+ LDAP_SCOPE_ONELEVEL
+ LDAP_SCOPE_SUBTREE
+ LDAP_SECURITY_NONE
+ LDAP_SERVER_DOWN
+ LDAP_SIZELIMIT_EXCEEDED
+ LDAP_STRONG_AUTH_NOT_SUPPORTED
+ LDAP_STRONG_AUTH_REQUIRED
+ LDAP_SUCCESS
+ LDAP_SYNC_INFO
+ LDAP_TIMELIMIT_EXCEEDED
+ LDAP_TIMEOUT
+ LDAP_TYPE_OR_VALUE_EXISTS
+ LDAP_UNAVAILABLE
+ LDAP_UNAVAILABLE_CRITICAL_EXTN
+ LDAP_UNDEFINED_TYPE
+ LDAP_UNWILLING_TO_PERFORM
+ LDAP_URL_ERR_BADSCOPE
+ LDAP_URL_ERR_MEM
+ LDAP_URL_ERR_NODN
+ LDAP_URL_ERR_NOTLDAP
+ LDAP_URL_ERR_PARAM
+ LDAP_URL_OPT_SECURE
+ LDAP_USER_CANCELLED
+ LDAP_VERSION
+ LDAP_VERSION1
+ LDAP_VERSION2
+ LDAP_VERSION3
+ LDAP_TAG_SYNC_NEW_COOKIE
+ LDAP_TAG_SYNC_REFRESH_DELETE
+ LDAP_TAG_SYNC_REFRESH_PRESENT
+ LDAP_TAG_SYNC_ID_SET
+ LDAP_TAG_SYNC_COOKIE
+ LDAP_TAG_REFRESHDELETES
+ LDAP_TAG_REFRESHDONE
+ LDAP_TAG_RELOAD_HINT
+ LDAP_TAG_EXOP_MODIFY_PASSWD_ID
+ LDAP_TAG_EXOP_MODIFY_PASSWD_OLD
+ LDAP_TAG_EXOP_MODIFY_PASSWD_NEW
+ LDAP_TAG_EXOP_MODIFY_PASSWD_GEN
+ LDAP_TAG_MESSAGE
+ LDAP_TAG_MSGID
+ LDAP_TAG_LDAPDN
+ LDAP_TAG_LDAPCRED
+ LDAP_TAG_CONTROLS
+ LDAP_TAG_REFERRAL
+ LDAP_TAG_NEWSUPERIOR
+ LDAP_TAG_EXOP_REQ_OID
+ LDAP_TAG_EXOP_REQ_VALUE
+ LDAP_TAG_EXOP_RES_OID
+ LDAP_TAG_EXOP_RES_VALUE
+ LDAP_TAG_IM_RES_OID
+ LDAP_TAG_IM_RES_VALUE
+ LDAP_TAG_SASL_RES_CREDS
+ );
+$VERSION = '3.0.3';
+
+sub AUTOLOAD {
+ # This AUTOLOAD is used to 'autoload' constants from the constant()
+ # XS function. If a constant is not found then control is passed
+ # to the AUTOLOAD in AutoLoader.
+
+ my $constname;
+ ($constname = $AUTOLOAD) =~ s/.*:://;
+ my $val = constant($constname, @_ ? $_[0] : 0);
+ if ($! != 0) {
+ if ($! =~ /Invalid/) {
+
+ # try constants_h
+ $val = '"'.constant_s($constname).'"';
+ goto SUBDEF if ($! == 0);
+
+ $AutoLoader::AUTOLOAD = $AUTOLOAD;
+ goto &AutoLoader::AUTOLOAD;
+ } else {
+ croak "Your vendor has not defined LDAP macro $constname";
+ }
+ }
+SUBDEF:
+ eval "sub $AUTOLOAD { $val }";
+ goto &$AUTOLOAD;
+}
+
+bootstrap Net::LDAPapi $VERSION;
+
+
+# creats blessed ldap object.
+# accepts following arguments '-host', '-port', '-url', '-debug'
+# if '-url' is given then then '-host' and '-port' are not used
+sub new
+{
+ my ($this, @args) = @_;
+ my $class = ref($this) || $this;
+ my $self = {};
+ my $ld;
+ bless $self, $class;
+
+ my ($host, $port, $url, $debug) =
+ $self->rearrange(['HOST','PORT','URL', 'DEBUG'],@args);
+
+ if ( defined($url) ) {
+ return -1 unless (ldap_initialize($ld, $url) == $self->LDAP_SUCCESS );
+
+ } else {
+ $host = "localhost" unless $host;
+ $port = $self->LDAP_PORT unless $port;
+
+ return -1 unless ( ldap_initialize($ld, "ldap://$host:$port") == $self-> LDAP_SUCCESS);
+ }
+
+ # Following ASN.1 contains definitions for synrepl API
+ my $asn = Convert::ASN1->new;
+ $asn->prepare(<<ASN) or die "prepare: ", $asn->error;
+
+ syncUUID ::= OCTET STRING
+
+ syncCookie ::= OCTET STRING
+
+ syncRequestValue ::= SEQUENCE {
+ mode ENUMERATED,
+ cookie syncCookie OPTIONAL,
+ reloadHint BOOLEAN
+ }
+
+ syncStateValue ::= SEQUENCE {
+ state ENUMERATED,
+ entryUUID syncUUID,
+ cookie syncCookie OPTIONAL
+ }
+
+ refresh_Delete ::= SEQUENCE {
+ cookie syncCookie OPTIONAL,
+ refreshDone BOOLEAN OPTIONAL
+ }
+
+ refresh_Present ::= SEQUENCE {
+ cookie syncCookie OPTIONAL,
+ refreshDone BOOLEAN OPTIONAL
+ }
+
+ syncId_Set ::= SEQUENCE {
+ cookie syncCookie OPTIONAL,
+ refreshDeletes BOOLEAN OPTIONAL,
+ syncUUIDs SET OF syncUUID
+ }
+
+ syncInfoValue ::= CHOICE {
+ newcookie [0] syncCookie,
+ refreshDelete [1] refresh_Delete,
+ refreshPresent [2] refresh_Present,
+ syncIdSet [3] syncId_Set
+ }
+ASN
+
+ $self->{"asn"} = $asn;
+ $self->{"ld"} = $ld;
+ $self->{"errno"} = 0;
+ $self->{"errstring"} = undef;
+ $self->{"debug"} = $debug;
+ ldap_set_option($ld, $self->LDAP_OPT_PROTOCOL_VERSION, $self->LDAP_VERSION3);
+ return $self;
+} # end of new
+
+
+sub DESTROY {};
+
+
+sub abandon
+{
+ my ($self, @args) = @_;
+
+ my ($status, $sctrls, $cctrls);
+
+ my ($msgid, $serverctrls, $clientctrls) =
+ $self->rearrange(['MSGID', 'SCTRLS', 'CCTRLS'], @args);
+
+ croak("Invalid MSGID") if ($msgid < 0);
+
+ $sctrls = $self->create_controls_array(@$serverctrls) if $serverctrls;
+ $cctrls = $self->create_controls_array(@$clientctrls) if $clientctrls;
+
+ $status = ldap_abandon_ext($self->{"ld"}, $msgid, $sctrls, $cctrls);
+
+ $self->errorize($status);
+
+ ldap_controls_array_free($sctrls) if $sctrls;
+ ldap_controls_array_free($cctrls) if $cctrls;
+
+ return $status;
+} # end of abandon
+
+
+# synonim for abandon(...)
+sub abandon_ext {
+ my ($self, @args) = @_;
+
+ return $self->abandon(@args);
+} # end of abandon_ext
+
+
+sub add
+{
+ my ($self,@args) = @_;
+
+ my ($msgid, $sctrls, $cctrls, $status);
+
+ my ($dn, $mod, $serverctrls, $clientctrls) =
+ $self->rearrange(['DN', 'MOD', 'SCTRLS', 'CCTRLS'], @args);
+
+ croak("No DN Specified") if ($dn eq "");
+ croak("LDAPMod structure is not a hash reference.") if( ref($mod) ne "HASH" );
+
+ $sctrls = $self->create_controls_array(@$serverctrls) if $serverctrls;
+ $cctrls = $self->create_controls_array(@$clientctrls) if $clientctrls;
+
+ $status = ldap_add_ext($self->{"ld"}, $dn, $mod, $sctrls, $cctrls, $msgid);
+
+ ldap_controls_array_free($sctrls) if $sctrls;
+ ldap_controls_array_free($cctrls) if $cctrls;
+
+ $self->errorize($status);
+ if( $status != $self->LDAP_SUCCESS ) {
+ return undef;
+ }
+
+ return $msgid;
+} # end of add
+
+# synonym for add
+sub add_ext
+{
+ my ($self, @args) = @_;
+
+ return $self->add(@args);
+} # end of add_ext
+
+
+sub add_s
+{
+ my ($self,@args) = @_;
+
+ my ($sctrls, $cctrls, $status);
+
+ my ($dn, $mod, $serverctrls, $clientctrls) =
+ $self->rearrange(['DN', 'MOD', 'SCTRLS', 'CCTRLS'], @args);
+
+ croak("No DN Specified") if ($dn eq "");
+ croak("LDAP Modify Structure Not a HASH Reference") if (ref($mod) ne "HASH");
+
+ $sctrls = $self->create_controls_array(@$serverctrls) if $serverctrls;
+ $cctrls = $self->create_controls_array(@$clientctrls) if $clientctrls;
+
+ $status = ldap_add_ext_s($self->{"ld"}, $dn, $mod, $sctrls, $cctrls);
+
+ ldap_controls_array_free($sctrls) if $sctrls;
+ ldap_controls_array_free($cctrls) if $cctrls;
+
+ $self->errorize($status);
+
+ return $status;
+} # end of add_s
+
+
+# synonym for add_s
+sub add_ext_s
+{
+ my ($self, @args) = @_;
+
+ return $self->add_s(@args);
+} # end of add_ext_s
+
+
+sub bind
+{
+ my ($self,@args) = @_;
+
+ my ($msgid, $sctrls, $cctrls, $status);
+
+ my ($dn, $pass, $authtype, $serverctrls, $clientctrls) =
+ $self->rearrange(['DN', 'PASSWORD', 'TYPE', 'SCTRLS', 'CCTRLS'],@args);
+
+ $dn = "" unless $dn;
+ $pass = "" unless $pass;
+ $authtype = $authtype || $self->LDAP_AUTH_SIMPLE;
+
+ croak("bind supports only LDAP_AUTH_SIMPLE auth type")
+ unless $authtype == $self->LDAP_AUTH_SIMPLE;
+
+ $sctrls = $self->create_controls_array(@$serverctrls) if $serverctrls;
+ $cctrls = $self->create_controls_array(@$clientctrls) if $clientctrls;
+
+ $status = ldap_sasl_bind($self->{"ld"}, $dn, $pass,
+ $sctrls, $cctrls, $msgid);
+
+ ldap_controls_array_free($sctrls) if $sctrls;
+ ldap_controls_array_free($cctrls) if $cctrls;
+
+ $self->errorize($status);
+ if( $status != $self->LDAP_SUCCESS ) {
+ return undef;
+ }
+
+ return $msgid;
+} # end of bind
+
+
+sub bind_s
+{
+ my ($self, @args) = @_;
+
+ my ($saslmech, $status, $servercredp, $sctrls, $cctrls);
+
+ my ($dn, $pass, $authtype, $serverctrls, $clientctrls) =
+ $self->rearrange(['DN', 'PASSWORD', 'TYPE', 'SCTRLS', 'CCTRLS'], @args);
+
+ $dn = "" unless $dn;
+ $pass = "" unless $pass;
+ $sctrls = 0 unless $sctrls;
+ $cctrls = 0 unless $cctrls;
+ $authtype = $authtype || $self->LDAP_AUTH_SIMPLE;
+
+ $sctrls = $self->create_controls_array(@$serverctrls) if $serverctrls;
+ $cctrls = $self->create_controls_array(@$clientctrls) if $clientctrls;
+
+ if ($authtype == $self->LDAP_AUTH_SASL) {
+ $status =
+ ldap_sasl_interactive_bind_s($self->{"ld"}, $dn, $pass,
+ $sctrls, $cctrls, $saslmech,
+ $self->{"saslrealm"},
+ $self->{"saslauthzid"},
+ $self->{"saslsecprops"},
+ $self->{"saslflags"});
+
+ } else {
+ # not sure here what to do with $servercredp
+ $status = ldap_sasl_bind_s($self->{"ld"}, $dn, $pass,
+ $sctrls, $cctrls, \$servercredp);
+ }
+
+ ldap_controls_array_free($sctrls) if $sctrls;
+ ldap_controls_array_free($cctrls) if $cctrls;
+
+ $self->errorize($status);
+
+ return $status;
+} # end of bind_s
+
+
+sub sasl_parms
+{
+ my ($self,@args) = @_;
+ my ($mech, $realm, $authzid, $secprops, $flags) =
+ $self->rearrange(['MECH', 'REALM', 'AUTHZID', 'SECPROPS', 'FLAGS'],
+ @args);
+
+ $mech = "" unless $mech;
+ $realm = "" unless $realm;
+ $authzid = "" unless $authzid;
+ $secprops = "" unless $secprops;
+ $flags = $self->LDAP_SASL_QUIET unless defined($flags);
+
+ $self->{"saslmech"} = $mech;
+ $self->{"saslrealm"} = $realm;
+ $self->{"saslauthzid"} = $authzid;
+ $self->{"saslsecprops"} = $secprops;
+ $self->{"saslflags"} = $flags;
+} # end of sasl_parms
+
+
+sub compare
+{
+ my ($self, @args) = @_;
+
+ my ($status, $msgid, $sctrls, $cctrls);
+
+ my ($dn, $attr, $value, $serverctrls, $clientctrls) =
+ $self->rearrange(['DN','ATTR', ['VALUE', 'VALUES'], 'SCTRLS', 'CCTRLS'], @args);
+
+ croak("No DN Specified") if ($dn eq "");
+ $value = "" unless $value;
+
+ $sctrls = $self->create_controls_array(@$serverctrls) if $serverctrls;
+ $cctrls = $self->create_controls_array(@$clientctrls) if $clientctrls;
+
+ $status =
+ ldap_compare_ext($self->{"ld"}, $dn, $attr, $value, $sctrls, $cctrls, $msgid);
+
+ ldap_controls_array_free($sctrls) if $sctrls;
+ ldap_controls_array_free($cctrls) if $cctrls;
+
+ $self->errorize($status);
+ if( $status != $self->LDAP_SUCCESS ) {
+ return undef;
+ }
+
+ return $msgid;
+} # end of compare
+
+
+# synonym for compare
+sub compare_ext {
+ my ($self, @args) = @_;
+
+ return $self->compare(@args);
+} # end of compare_ext
+
+
+# needs to use ldap_compare_ext_s
+sub compare_s
+{
+ my ($self, @args) = @_;
+
+ my ($status, $sctrls, $cctrls);
+
+ my ($dn, $attr, $value, $serverctrls, $clientctrls) =
+ $self->rearrange(['DN', 'ATTR' , ['VALUE', 'VALUES'], 'SCTRLS', 'CCTRLS'], @args);
+
+ croak("No DN Specified") if ($dn eq "");
+ $value = "" unless $value;
+
+ $sctrls = $self->create_controls_array(@$serverctrls) if $serverctrls;
+ $cctrls = $self->create_controls_array(@$clientctrls) if $clientctrls;
+
+ $status = ldap_compare_ext_s($self->{"ld"}, $dn, $attr, $value, $sctrls, $cctrls);
+
+ ldap_controls_array_free($sctrls) if $sctrls;
+ ldap_controls_array_free($cctrls) if $cctrls;
+
+ $self->errorize($status);
+ return $status;
+} # end of compare_s
+
+
+# synonym for compare
+sub compare_ext_s {
+ my ($self, @args) = @_;
+
+ return $self->compare_s(@args);
+} # end of compare_ext
+
+
+# needs DOC in POD bellow. XXX
+sub start_tls
+{
+ my ($self, @args) = @_;
+
+ my ($msgid, $status, $sctrls, $cctrls);
+
+ my ($serverctrls, $clientctrls) =
+ $self->rearrange(['SCTRLS', 'CCTRLS'], @args);
+
+ $sctrls = $self->create_controls_array(@$serverctrls) if $serverctrls;
+ $cctrls = $self->create_controls_array(@$clientctrls) if $clientctrls;
+
+ $status = ldap_start_tls($self->{"ld"}, $sctrls, $cctrls, $msgid);
+
+ ldap_controls_array_free($sctrls) if $sctrls;
+ ldap_controls_array_free($cctrls) if $cctrls;
+
+ $self->errorize($status);
+ if( $status != $self->LDAP_SUCCESS ) {
+ return undef;
+ }
+
+ return $msgid;
+} # end of start_tls
+
+
+# needs DOC in POD bellow. XXX
+sub start_tls_s
+{
+ my ($self, @args) = @_;
+
+ my ($status, $sctrls, $cctrls);
+ $sctrls=0;
+ $cctrls=0;
+
+ my ($serverctrls, $clientctrls) = $self->rearrange(['SCTRLS', 'CCTRLS'], @args);
+
+ $sctrls = $self->create_controls_array(@$serverctrls) if $serverctrls;
+ $cctrls = $self->create_controls_array(@$clientctrls) if $clientctrls;
+
+ $status = ldap_start_tls_s($self->{"ld"}, $sctrls, $cctrls);
+
+ ldap_controls_array_free($sctrls) if $sctrls;
+ ldap_controls_array_free($cctrls) if $cctrls;
+
+ $self->errorize($status);
+
+ return $status;
+} # end of start_tls_s
+
+
+sub count_entries
+{
+ my ($self, @args) = @_;
+
+ my ($result) = $self->rearrange(['RESULT'], @args);
+
+ $result = $self->{"result"} unless $result;
+
+ croak("No result is given") unless $result;
+
+ return ldap_count_entries($self->{"ld"}, $result);
+} # end of count_entries
+
+
+sub delete
+{
+ my ($self,@args) = @_;
+
+ my ($msgid, $status, $sctrls, $cctrls);
+
+ my ($dn, $serverctrls, $clientctrls) =
+ $self->rearrange(['DN', 'SCTRLS', 'CCTRLS'], @args);
+
+ croak("No DN Specified") if ($dn eq "");
+
+ $sctrls = 0;
+ $cctrls = 0;
+
+ $sctrls = $self->create_controls_array(@$serverctrls) if $serverctrls;
+ $cctrls = $self->create_controls_array(@$clientctrls) if $clientctrls;
+
+ $status = ldap_delete_ext($self->{"ld"}, $dn, $sctrls, $cctrls, $msgid);
+
+ ldap_controls_array_free($sctrls) if $sctrls;
+ ldap_controls_array_free($cctrls) if $cctrls;
+
+ $self->errorize($status);
+ if( $status != $self->LDAP_SUCCESS ) {
+ return undef;
+ }
+
+ return $msgid;
+} # end of delete
+
+sub delete_s
+{
+ my ($self,@args) = @_;
+
+ my ($status, $sctrls, $cctrls);
+
+ my ($dn, $serverctrls, $clientctrls) =
+ $self->rearrange(['DN', 'SCTRLS', 'CCTRLS'], @args);
+
+ croak("No DN Specified") if ($dn eq "");
+
+ $sctrls = $self->create_controls_array(@$serverctrls) if $serverctrls;
+ $cctrls = $self->create_controls_array(@$clientctrls) if $clientctrls;
+
+ $status = ldap_delete_ext_s($self->{"ld"}, $dn, $sctrls, $cctrls);
+
+ ldap_controls_array_free($sctrls) if $sctrls;
+ ldap_controls_array_free($cctrls) if $cctrls;
+
+ $self->errorize($status);
+
+ return $status;
+} # end of delete_s
+
+sub dn2ufn
+{
+ my ($self, @args) = @_;
+
+ my ($dn) = $self->rearrange(['DN'], @args);
+
+ return ldap_dn2ufn($dn);
+} # end of dn2ufn
+
+
+sub explode_dn
+{
+ my ($self, @args) = @_;
+
+ my ($dn, $notypes) = $self->rearrange(['DN', 'NOTYPES'],@args);
+
+ return ldap_explode_dn($dn, $notypes);
+} # end of explode_dn
+
+
+sub explode_rdn
+{
+ my ($self, @args) = @_;
+
+ my (@components);
+
+ my ($rdn, $notypes) = $self->rearrange(['RDN', 'NOTYPES'], @args);
+
+ return ldap_explode_rdn($rdn, $notypes);
+} # end of explode_rdn
+
+
+sub first_message
+{
+ my ($self, @args) = @_;
+
+ my ($result) = $self->rearrange(['RESULT'], @args);
+
+ $result = $self->{"result"} unless $result;
+
+ croak("No Current Result") unless $result;
+
+ $self->{"msg"} = ldap_first_message($self->{"ld"}, $self->{"result"});
+
+ return $self->{"msg"};
+} # end of first_message
+
+
+sub next_message
+{
+ my ($self, @args) = @_;
+
+ my ($msg) = $self->rearrange(['MSG'], @args);
+
+ $msg = $self->{"msg"} unless $msg;
+
+ croak("No Current Message") unless $msg;
+
+ $self->{"msg"} = ldap_next_message($self->{"ld"}, $msg);
+
+ return $self->{"msg"};
+} # end of next_message
+
+
+# using this function you don't have to call fist_message and next_message
+# here is an example:
+#
+# print "message = $message\n" while( $msg = $ld->result_message );
+#
+sub result_message
+{
+ my ($self, @args) = @_;
+
+ my ($result) = $self->rearrange(['RESULT'], @args);
+
+ $result = $self->{"result"} unless $result;
+
+ croak("No Current Result") unless $result;
+
+ if( $self->{"msg"} == 0 ) {
+ $self->{"msg"} = ldap_first_message($self->{"ld"}, $self->{"result"});
+ } else {
+ $self->{"msg"} = ldap_next_message($self->{"ld"}, $self->{"msg"});
+ }
+
+ return $self->{"msg"};
+} # end of result_message
+
+
+sub next_changed_entries {
+ my ($self, @args) = @_;
+
+ my ($msgid, $allnone, $timeout) =
+ $self->rearrange(['MSGID', 'ALL', 'TIMEOUT'], @args);
+
+ my ($rc, $msg, $msgtype, $asn, $syncInfoValue,
+ $syncInfoValues, $refreshPresent, $ctrl, $oid, %parsed,
+ $retdatap, $retoidp, @entries, $syncStateValue, $syncStateValues,
+ $state, $berval, $cookie);
+
+ $rc = $self->result($msgid, $allnone, $timeout);
+
+ @entries = ();
+
+ $asn = $self->{"asn"};
+
+ while( $msg = $self->result_message ) {
+ $msgtype = $self->msgtype($msg);
+
+ if( $msgtype eq $self->LDAP_RES_SEARCH_ENTRY ) {
+ my %entr = ('entry' => $msg);
+ push(@entries, \%entr);
+ $self->{"entry"} = $msg;
+
+ # extract controls if any
+ my @sctrls = $self->get_entry_controls($msg);
+ foreach $ctrl (@sctrls) {
+ $oid = $self->get_control_oid($ctrl);
+ if( $oid eq $self->LDAP_CONTROL_SYNC_STATE ) {
+ $berval = $self->get_control_berval($ctrl);
+ $syncStateValue = $asn->find('syncStateValue');
+ $syncStateValues = $syncStateValue->decode($berval);
+ $state = $syncStateValues->{'state'};
+ if( $state == 0 ) {
+ $entr{'state'} = "present";
+ } elsif( $state == 1 ) {
+ $entr{'state'} = "add";
+ } elsif( $state == 2 ) {
+ $entr{'state'} = "modify";
+ } elsif( $state == 3 ) {
+ $entr{'state'} = "delete";
+ } else {
+ $entr{'state'} = "unknown";
+ }
+ }
+
+ $cookie = $syncStateValues->{'cookie'};
+ if( $cookie ) {
+ # save the cookie
+ open(COOKIE_FILE,">".$self->{"cookie"}) ||
+ die("Cannot open file '".$self->{"cookie"}."' for writing.");
+ print COOKIE_FILE $cookie;
+ close(COOKIE_FILE);
+ }
+ }
+
+ } elsif( $msgtype eq $self->LDAP_RES_INTERMEDIATE ) {
+ %parsed = $self->parse_intermediate($msg);
+ $retdatap = $parsed{'retdatap'};
+ $retoidp = $parsed{'retoidp'};
+
+ if( $retoidp eq $self->LDAP_SYNC_INFO ) {
+ my $cookie;
+
+ $asn->configure(encoding => "DER");
+ $syncInfoValue = $asn->find('syncInfoValue');
+ $syncInfoValues = $syncInfoValue->decode($retdatap);
+
+ # trying to get the cookie from one of the foolowing choices.
+ $cookie = $syncInfoValues->{'newcookie'};
+
+ my $refreshPresent = $syncInfoValues->{'refreshPresent'};
+ $cookie = $refreshPresent->{'cookie'} if( $refreshPresent );
+
+ my $refreshDelete = $syncInfoValues->{'refreshDelete'};
+ $cookie = $refreshDelete->{'cookie'} if( $refreshDelete );
+
+ my $syncIdSet = $syncInfoValues->{'syncIdSet'};
+ $cookie = $syncIdSet->{'cookie'} if( $syncIdSet );
+
+ $asn->configure(encoding => "BER");
+
+ # see if we got any and save it.
+ if( $cookie ) {
+ open(COOKIE_FILE,">".$self->{"cookie"}) ||
+ die("Cannot open file '".$self->{"cookie"}."' for writing.");
+ print COOKIE_FILE $cookie;
+ close(COOKIE_FILE);
+ }
+ }
+ }
+ }
+
+ return @entries;
+} # next_changed_entries
+
+
+sub first_entry
+{
+ my ($self) = @_;
+
+ croak("No Current Result") if ($self->{"result"} == 0);
+
+ $self->{"entry"} = ldap_first_entry($self->{"ld"}, $self->{"result"});
+
+ return $self->{"entry"};
+} # end of first_entry
+
+sub next_entry
+{
+ my ($self) = @_;
+
+ croak("No Current Entry") if ($self->{"entry"} == 0);
+
+ $self->{"entry"} = ldap_next_entry($self->{"ld"}, $self->{"entry"});
+
+ return $self->{"entry"};
+} # end of next_entry
+
+
+# using this function you don't have to call fist_entry and next_entry
+# here is an example:
+#
+# print "entry = $entry\n" while( $entry = $ld->result_entry );
+#
+sub result_entry
+{
+ my ($self) = @_;
+
+ croak("No Current Result") if ($self->{"result"} == 0);
+
+ if( $self->{"entry"} == 0 ) {
+ $self->{"entry"} = ldap_first_entry($self->{"ld"}, $self->{"result"});
+ } else {
+ $self->{"entry"} = ldap_next_entry($self->{"ld"}, $self->{"entry"});
+ }
+
+ return $self->{"entry"};
+} # end of result_entry
+
+
+sub get_entry_controls
+{
+ my ($self, @args) = @_;
+
+ my ($msg) = $self->rearrange(['MSG'], @args);
+
+ $msg = $self->{"msg"} unless $msg;
+
+ croak("No Current Message/Entry") unless $msg;
+
+ my @serverctrls = ();
+ my $serverctrls_ref = \@serverctrls;
+
+ ldap_get_entry_controls($self->{"ld"}, $msg, $serverctrls_ref);
+
+ return @serverctrls;
+} # end of get_entry_controls
+
+
+sub get_control_oid {
+ my ($self, @args) = @_;
+
+ my ($ctrl) = $self->rearrange(['CTRL'], @args);
+
+ return ldap_control_oid($ctrl);
+} # end of get_control_oid
+
+
+sub get_control_berval {
+ my ($self, @args) = @_;
+
+ my ($ctrl) = $self->rearrange(['CTRL'], @args);
+
+ return ldap_control_berval($ctrl);
+} # end of get_control_berval
+
+
+sub get_control_critical {
+ my ($self, @args) = @_;
+
+ my ($ctrl) = $self->rearrange(['CTRL'], @args);
+
+ return ldap_control_critical($ctrl);
+} # end of get_control_critical
+
+
+sub first_attribute
+{
+ my ($self) = @_;
+
+ my ($attr, $ber);
+
+ croak("No Current Entry") if ($self->{"entry"} == 0);
+
+ $attr = ldap_first_attribute($self->{"ld"}, $self->{"entry"}, $ber);
+
+ $self->{"ber"} = $ber;
+
+ return $attr;
+} # end of first_attribute
+
+
+sub next_attribute
+{
+ my ($self) = @_;
+
+ my ($attr);
+
+ croak("No Current Entry") if ($self->{"entry"} == 0);
+ croak("Empty Ber Value") if ($self->{"ber"} == 0);
+
+ $attr = ldap_next_attribute($self->{"ld"}, $self->{"entry"}, $self->{"ber"});
+
+ ber_free($self->{"ber"}, 0) if (!$attr);
+
+ return $attr;
+} # end of next_attribute
+
+
+# using this function you don't have to call fist_attribute and next_attribute
+# as in the following example:
+#
+# print "<$attr>\n" while( $attr = $ld->entry_attribute );
+#
+sub entry_attribute {
+
+ my ($self, @args) = @_;
+
+ my ($msg) = $self->rearrange(['MSG'], @args);
+
+ my ($attr, $ber);
+
+ $msg = $self->{"entry"} unless $msg;
+
+ croak("No Current Entry") unless $msg;
+
+ if ($self->{"ber"} == 0) {
+ $attr = ldap_first_attribute($self->{"ld"}, $msg, $ber);
+ $self->{"ber"} = $ber;
+
+ } else {
+ croak("Empty Ber Value") if ($self->{"ber"} == 0);
+ $attr = ldap_next_attribute($self->{"ld"}, $msg, $self->{"ber"});
+ if (!$attr) {
+ ber_free($self->{"ber"}, 0);
+ $self->{"ber"} = undef;
+ }
+ }
+
+ return $attr;
+} # end of entry_attribute
+
+
+sub parse_result {
+ my ($self, @args) = @_;
+
+ my ($msg, $freeMsg) = $self->rearrange(['MSG', 'FREEMSG'], @args);
+
+ my ($status, %result);
+
+ $freeMsg = 0 unless $freeMsg;
+ $msg = $self->{"entry"} unless $msg;
+
+ my ($errcode, $matcheddn, $errmsg, @referrals, @serverctrls);
+
+ @serverctrls = ();
+ my $serverctrls_ref = \@serverctrls;
+
+ @referrals = ();
+ my $referrals_ref = \@referrals;
+
+ $status =
+ ldap_parse_result($self->{"ld"}, $msg, $errcode, $matcheddn,
+ $errmsg, $referrals_ref, $serverctrls_ref, $freeMsg);
+
+
+ $self->errorize($status);
+ if( $status != $self->LDAP_SUCCESS ) {
+ return undef;
+ }
+
+ $result{"errcode"} = $errcode;
+ $result{"matcheddn"} = $matcheddn;
+ $result{"errmsg"} = $errmsg;
+ $result{"referrals"} = $referrals_ref;
+ $result{"serverctrls"} = $serverctrls_ref;
+
+ return %result;
+} # end of parse_result(...)
+
+
+# needs docs bellow in POD. XXX
+sub parse_intermediate {
+ my ($self, @args) = @_;
+
+ my ($msg, $freeMsg) = $self->rearrange(['MSG', 'FREEMSG'], @args);
+
+ my ($status, %result);
+
+ $freeMsg = 0 unless $freeMsg;
+ $msg = $self->{"msg"} unless $msg;
+
+ my ($retoidp, $retdatap, @serverctrls);
+
+ @serverctrls = ();
+ my $serverctrls_ref = \@serverctrls;
+
+ $status =
+ ldap_parse_intermediate($self->{"ld"}, $msg, $retoidp,
+ $retdatap, $serverctrls_ref, $freeMsg);
+
+ $self->errorize($status);
+ if( $status != $self->LDAP_SUCCESS ) {
+ return undef;
+ }
+
+ $result{"retoidp"} = $retoidp;
+ $result{"retdatap"} = $retdatap;
+ $result{"serverctrls"} = $serverctrls_ref;
+
+ return %result;
+} # end of parse_result(...)
+
+
+sub perror
+{
+ my ($self, @args) = @_;
+
+ my ($msg) = $self->rearrange(['MSG'], @args);
+
+ ldap_perror($self->{"ld"}, $msg);
+}
+
+# get dn for current entry
+sub get_dn
+{
+ my ($self, @args) = @_;
+
+ my ($entry) = $self->rearrange(['MSG'], @args);
+
+ $entry = $self->{"entry"} unless $entry;
+
+ croak("No Current Entry") unless $entry;
+
+ my $dn = ldap_get_dn($self->{"ld"}, $entry);
+
+ return $dn;
+} # end of get_dn
+
+
+# get array of values for current entry and a given attribute
+sub get_values
+{
+ my ($self, @args) = @_;
+
+ my ($attr) = $self->rearrange(['ATTR'], @args);
+
+ croak("No Current Entry") if ($self->{"entry"} == 0);
+ croak("No Attribute Specified") if ($attr eq "");
+
+ my @vals = ldap_get_values_len($self->{"ld"}, $self->{"entry"}, $attr);
+
+ return @vals;
+} # end of get_values
+
+
+# synonym for get_values(...)
+sub get_values_len {
+ my ($self, @args) = @_;
+
+ return $self->get_values(@args);
+} # end of get_values_len
+
+
+sub msgfree
+{
+ my ($self, @args) = @_;
+
+ my ($result) = $self->rearrange(['RESULT'], @args);
+
+ $result = $self->{"result"} unless $result;
+
+ return ldap_msgfree($self->{"result"});
+} # end of msgfree
+
+
+sub modify
+{
+ my ($self, @args) = @_;
+
+ my ($msgid, $sctrls, $cctrls, $status);
+
+ my ($dn, $mod, $serverctrls, $clientctrls) =
+ $self->rearrange(['DN', 'MOD', 'SCTRLS', 'CCTRLS'], @args);
+
+ croak("No DN Specified") if ($dn eq "");
+ croak("LDAP Modify Structure Not a Reference") if (ref($mod) ne "HASH");
+
+ $sctrls = $self->create_controls_array(@$serverctrls) if $serverctrls;
+ $cctrls = $self->create_controls_array(@$clientctrls) if $clientctrls;
+
+ $status = ldap_modify_ext($self->{"ld"}, $dn, $mod, $sctrls, $cctrls, $msgid);
+
+ $self->errorize($status);
+ if( $status != $self->LDAP_SUCCESS ) {
+ return undef;
+ }
+
+ ldap_controls_array_free($sctrls) if $sctrls;
+ ldap_controls_array_free($cctrls) if $cctrls;
+
+ return $msgid;
+} # end of modify
+
+
+# synonym for modify
+sub modify_ext
+{
+ my ($self, @args) = @_;
+
+ return $self->modify(@args);
+} # end of modify_ext
+
+
+sub modify_s
+{
+ my ($self,@args) = @_;
+
+ my ($status, $sctrls, $cctrls);
+
+ my ($dn, $mod, $serverctrls, $clientctrls) =
+ $self->rearrange(['DN', 'MOD', 'SCTRLS', 'CCTRLS'], @args);
+
+ croak("No DN Specified") if ($dn eq "");
+ croak("LDAP Modify Structure Not a Reference") if (ref($mod) ne "HASH");
+
+ $sctrls = $self->create_controls_array(@$serverctrls) if $serverctrls;
+ $cctrls = $self->create_controls_array(@$clientctrls) if $clientctrls;
+
+ $status = ldap_modify_ext_s($self->{"ld"}, $dn, $mod, $sctrls, $cctrls);
+
+ ldap_controls_array_free($sctrls) if $sctrls;
+ ldap_controls_array_free($cctrls) if $cctrls;
+
+ $self->errorize($status);
+ return $status;
+} # end of modify_s
+
+
+# synonym for modify
+sub modify_ext_s
+{
+ my ($self, @args) = @_;
+
+ return $self->modify_s(@args);
+} # end of modify_ext
+
+
+# needs updated docs in POD bellow
+sub rename {
+ my ($self, @args) = @_;
+
+ my ($sctrls, $cctrls, $msgid, $status);
+
+ my ($dn, $newrdn, $newsuper, $delete, $serverctrls, $clientctrls) =
+ $self->rearrange(['DN', 'NEWRDN', 'NEWSUPER', 'DELETE', 'SCTRLS', 'CCTRLS'],
+ @args);
+
+ $sctrls = $self->create_controls_array(@$serverctrls) if $serverctrls;
+ $cctrls = $self->create_controls_array(@$clientctrls) if $clientctrls;
+
+ $status =
+ ldap_rename($self->{"ld"}, $dn, $newrdn, $newsuper,
+ $delete, $sctrls, $cctrls, $msgid);
+
+ ldap_controls_array_free($sctrls) if $sctrls;
+ ldap_controls_array_free($cctrls) if $cctrls;
+
+ $self->errorize($status);
+ if( $status != $self->LDAP_SUCCESS ) {
+ return undef;
+ }
+
+ return $msgid;
+} # end of rename
+
+
+# needs updated docs in POD bellow
+sub rename_s {
+ my ($self, @args) = @_;
+
+ my ($sctrls, $cctrls, $status);
+
+ my ($dn, $newrdn, $newsuper, $delete, $serverctrls, $clientctrls) =
+ $self->rearrange(['DN', 'NEWRDN', 'NEWSUPER', 'DELETE', 'SCTRLS', 'CCTRLS'],
+ @args);
+
+ $sctrls = $self->create_controls_array(@$serverctrls) if $serverctrls;
+ $cctrls = $self->create_controls_array(@$clientctrls) if $clientctrls;
+
+ $status =
+ ldap_rename_s($self->{"ld"}, $dn, $newrdn, $newsuper,
+ $delete, $sctrls, $cctrls);
+
+ ldap_controls_array_free($sctrls) if $sctrls;
+ ldap_controls_array_free($cctrls) if $cctrls;
+
+ $self->errorize($status);
+
+ return $status;
+} # end of rename_s
+
+
+# this function is used to retrieve results of asynchronous search operation
+# it returns LDAPMesage which is to be processed by functions first_entry,
+# result_entry, first_message, result_message. To find message type one
+# should use function msgtype(...)
+sub result
+{
+ my ($self, @args) = @_;
+ my ($result, $status, $err) = (undef, undef, undef);
+
+ my ($msgid, $allnone, $timeout) =
+ $self->rearrange(['MSGID', 'ALL', 'TIMEOUT'], @args);
+
+ croak("Invalid MSGID") if ($msgid < 0);
+
+ $status = ldap_result($self->{"ld"}, $msgid, $allnone, $timeout, $result);
+ $self->{"result"} = $result;
+ $self->{"status"} = $status;
+
+ $self->errorize($status);
+ if( $status == -1 || $status == 0 ) {
+ return undef;
+ }
+
+ return $result;
+} # end of result
+
+
+sub is_ldap_url
+{
+ my ($self,@args) = @_;
+
+ my ($url) = $self->rearrange(['URL'],@args);
+
+ return ldap_is_ldap_url($url);
+} # end of is_ldap_url
+
+
+sub url_parse
+{
+ my ($self,@args) = @_;
+ my ($url) = $self->rearrange(['URL'],@args);
+
+ return ldap_url_parse($url);
+} # end of url_parse
+
+
+# needs testing XXX. present only in Mozilla SDK
+sub url_search
+{
+ my ($self,@args) = @_;
+ my ($msgid,$errdn,$extramsg);
+
+ my ($url,$attrsonly) = $self->rearrange(['URL','ATTRSONLY'],@args);
+
+ if (($msgid = ldap_url_search($self->{"ld"},$url,$attrsonly)) < 0)
+ {
+ $self->{"errno"} = ldap_get_lderrno($self->{"ld"},$errdn,$extramsg);
+ $self->{"extramsg"} = undef;
+ } else {
+ $self->{"errno"} = 0;
+ $self->{"extramsg"} = "";
+ }
+ return $msgid;
+} # end of url_search
+
+
+# needs testing XXX. present only in Mozilla SDK
+sub url_search_s
+{
+ my ($self, @args) = @_;
+ my ($result, $status, $errdn, $extramsg);
+
+ my ($url,$attrsonly) = $self->rearrange(['URL', 'ATTRSONLY'], @args);
+
+ if( ($status = ldap_url_search_s($self->{"ld"}, $url, $attrsonly, $result)) !=
+ $self->LDAP_SUCCESS )
+ {
+ $self->{"errno"} = ldap_get_lderrno($self->{"ld"},$errdn,$extramsg);
+ $self->{"extramsg"} = $extramsg;
+ } else {
+ $self->{"errno"} = 0;
+ $self->{"extramsg"} = undef;
+ }
+ $self->{"result"} = $result;
+ return $status;
+} # end of url_search_s
+
+
+# needs testing XXX. present only in Mozilla SDK
+sub url_search_st
+{
+ my ($self,@args) = @_;
+ my ($result,$status,$errdn,$extramsg);
+
+ my ($url,$attrsonly,$timeout) = $self->rearrange(['URL','ATTRSONLY',
+ 'TIMEOUT'],@args);
+
+ if (($status = ldap_url_search_st($self->{"ld"},$url,$attrsonly,$timeout,
+ $result)) != $self->LDAP_SUCCESS)
+ {
+ $self->{"errno"} = ldap_get_lderrno($self->{"ld"},$errdn,$extramsg);
+ $self->{"extramsg"} = $extramsg;
+ } else {
+ $self->{"errno"} = 0;
+ $self->{"extramsg"} = undef;
+ }
+ $self->{"result"} = $result;
+ return $status;
+} # end of url_search_st
+
+
+# needs testing XXX. present only in Mozilla SDK
+sub multisort_entries
+{
+ my ($self,@args) = @_;
+ my ($status,$errdn,$extramsg);
+
+ my ($attr) = $self->rearrange(['ATTR'],@args);
+
+ if (!$self->{"result"})
+ {
+ croak("No Current Result");
+ }
+
+ $status = ldap_multisort_entries($self->{"ld"},$self->{"result"},$attr);
+ $self->errorize($status);
+ return $status;
+} # end of multisort_entries
+
+
+sub listen_for_changes
+{
+ my ($self, @args) = @_;
+
+ my ($msgid, $status, $sctrls, $the_cookie, $syncRequestBerval);
+
+ my ($basedn, $scope, $filter, $attrs,
+ $attrsonly, $timeout, $sizelimit, $cookie) =
+ $self->rearrange(['BASEDN', 'SCOPE', 'FILTER', 'ATTRS',
+ 'ATTRSONLY', 'TIMEOUT', 'SIZELIMIT', 'COOKIE'], @args);
+
+ croak("No Filter Specified") if ($filter eq "");
+ croak("No cookie file specified") unless $cookie;
+
+ $self->{"cookie"} = $cookie;
+
+ if( $attrs == undef ) {
+ my @null_array = ();
+ $attrs = \@null_array;
+ }
+
+ # load cookie from the file
+ if( open(COOKIE, $cookie) ) {
+ read COOKIE, $the_cookie, 1024, 0;
+ } else {
+ warn "Failed to open file '".$cookie."' for reading.\n";
+ }
+
+ my $asn = $self->{"asn"};
+ my $syncRequestValue = $asn->find('syncRequestValue');
+
+ # refreshAndPersist mode
+ if( $the_cookie ) { # we have the cookie
+ $syncRequestBerval = $asn->encode(mode => 3, cookie => $the_cookie, reloadHint => 1);
+ } else {
+ $syncRequestBerval = $asn->encode(mode => 3, reloadHint => 1);
+ }
+
+ my $ctrl_persistent =
+ $self->create_control(-oid => $self->LDAP_CONTROL_SYNC,
+ -berval => $syncRequestBerval,
+ -critical => $self->CRITICAL);
+
+ my @controls = ($ctrl_persistent);
+ $sctrls = $self->create_controls_array(@controls);
+
+ $status =
+ ldap_search_ext($self->{"ld"}, $basedn, $scope, $filter,
+ $attrs, $attrsonly, $sctrls, undef,
+ $timeout, $sizelimit, $msgid);
+
+ ldap_controls_array_free($sctrls);
+ ldap_control_free($ctrl_persistent);
+
+ $self->errorize($status);
+ if( $status != $self->LDAP_SUCCESS ) {
+ return undef;
+ }
+
+ return $msgid;
+} # listen_for_changes
+
+
+sub search
+{
+ my ($self, @args) = @_;
+ my ($msgid, $status, $sctrls, $cctrls);
+
+ my ($basedn, $scope, $filter, $attrs, $attrsonly,
+ $serverctrls, $clientctrls, $timeout, $sizelimit) =
+ $self->rearrange(['BASEDN', 'SCOPE', 'FILTER', 'ATTRS',
+ 'ATTRSONLY', 'SCTRLS', 'CCTRLS', 'TIMEOUT',
+ 'SIZELIMIT'],
+ @args);
+
+ croak("No Filter Specified") if ($filter eq "");
+
+ if( $attrs == undef ) {
+ my @null_array = ();
+ $attrs = \@null_array;
+ }
+
+ $sctrls = $self->create_controls_array(@$serverctrls) if $serverctrls;
+ $cctrls = $self->create_controls_array(@$clientctrls) if $clientctrls;
+
+ $status =
+ ldap_search_ext($self->{"ld"}, $basedn, $scope, $filter,
+ $attrs, $attrsonly, $sctrls, $cctrls,
+ $timeout, $sizelimit, $msgid);
+
+ ldap_controls_array_free($sctrls) if $sctrls;
+ ldap_controls_array_free($cctrls) if $cctrls;
+
+ $self->errorize($status);
+ if( $status != $self->LDAP_SUCCESS ) {
+ return undef;
+ }
+
+ return $msgid;
+} # end of search
+
+
+# synonym for search
+sub search_ext
+{
+ my ($self, @args) = @_;
+
+ return $self->search(@args);
+} # end of search_ext
+
+
+sub search_s
+{
+ my ($self, @args) = @_;
+
+ my ($result, $status, $sctrls, $cctrls);
+
+ my ($basedn, $scope, $filter, $attrs, $attrsonly,
+ $serverctrls, $clientctrls, $timeout, $sizelimit) =
+ $self->rearrange(['BASEDN', 'SCOPE', 'FILTER', 'ATTRS',
+ 'ATTRSONLY', 'SCTRLS', 'CCTRLS', 'TIMEOUT',
+ 'SIZELIMIT' ], @args);
+
+ croak("No Filter Passed as Argument 3") if ($filter eq "");
+
+ if( $attrs == undef ) {
+ my @null_array = ();
+ $attrs = \@null_array;
+ }
+
+ $sctrls = $self->create_controls_array(@$serverctrls) if $serverctrls;
+ $cctrls = $self->create_controls_array(@$clientctrls) if $clientctrls;
+
+ $status =
+ ldap_search_ext_s($self->{"ld"}, $basedn, $scope, $filter,
+ $attrs, $attrsonly, $sctrls, $cctrls,
+ $timeout, $sizelimit, $result);
+
+
+ ldap_controls_array_free($sctrls) if $sctrls;
+ ldap_controls_array_free($cctrls) if $cctrls;
+
+ $self->errorize($status);
+ $self->{"result"} = $result;
+ return $status;
+} # end of search_s
+
+
+# synonym for search_s(...)
+sub search_ext_s
+{
+ my ($self, @args) = @_;
+
+ return $self->search_s(@args);
+} # end of search_ext_s
+
+
+sub count_references
+{
+ my ($self, @args) = @_;
+
+ my ($msg) = $self->rearrange(['MSG'], @args);
+
+ $msg = $self->{"entry"} unless $msg;
+
+ return ldap_count_references($self->{"ld"}, $msg);
+} # end of count_references
+
+
+sub get_option
+{
+ my ($self, @args) = @_;
+ my ($status);
+
+ my ($option, $optdata) = $self->rearrange(['OPTION', 'OPTDATA'], @args);
+
+ $status = ldap_get_option($self->{"ld"}, $option, $optdata);
+
+ return $status;
+} # end of get_option
+
+
+sub set_option
+{
+ my ($self,@args) = @_;
+ my ($status);
+
+ my ($option,$optdata) = $self->rearrange(['OPTION','OPTDATA'],@args);
+
+ $status = ldap_set_option($self->{"ld"},$option,$optdata);
+
+ return $status;
+} # end of set_option
+
+
+# needs testing more XXX
+sub set_rebind_proc
+{
+ my ($self, @args) = @_;
+ my ($status);
+
+ my ($rebindproc) = $self->rearrange(['REBINDPROC'], @args);
+
+ if( ref($rebindproc) eq "CODE" ) {
+ $status = ldap_set_rebind_proc($self->{"ld"}, $rebindproc);
+ } else {
+ croak("REBINDPROC is not a CODE Reference");
+ }
+
+ return $status;
+} # end of set_rebind_proc
+
+
+# needs docs in a POD bellow. XXX
+sub get_all_entries
+{
+ my ($self, @args) = shift;
+
+ my ($result) = $self->rearrange(['RESULT'], @args);
+
+ $result = $self->{"result"} unless $result;
+
+ croak("NULL Result") unless $result;
+
+ return ldap_get_all_entries($self->{"ld"}, $result);
+} # end of get_all_entries
+
+
+sub unbind
+{
+ my ($self, @args) = @_;
+
+ my ($status, $sctrls, $cctrls);
+
+ my ($serverctrls, $clientctrls) =
+ $self->rearrange(['SCTRLS', 'CCTRLS'], @args);
+
+ $sctrls = 0;
+ $cctrls = 0;
+
+ $sctrls = $self->create_controls_array(@$serverctrls) if $serverctrls;
+ $cctrls = $self->create_controls_array(@$clientctrls) if $clientctrls;
+
+ $status = ldap_unbind_ext_s($self->{"ld"}, $sctrls, $cctrls);
+
+ ldap_controls_array_free($sctrls) if $sctrls;
+ ldap_controls_array_free($cctrls) if $cctrls;
+
+ $self->errorize($status);
+ return $status;
+} # end of unbind
+
+
+# do we need these ssl function
+sub ssl_client_init
+{
+ my ($self,@args) = @_;
+ my ($status);
+
+ my ($certdbpath,$certdbhandle) = $self->rearrange(['DBPATH','DBHANDLE'],
+ @args);
+
+ $status = ldapssl_client_init($certdbpath,$certdbhandle);
+ return($status);
+} # end of ssl_client_init
+
+
+# do we need these ssl function
+sub ssl
+{
+ my ($self) = @_;
+ my ($status);
+
+ $status = ldapssl_install_routines($self->{"ld"});
+ return $status;
+} # end of ssl
+
+
+sub entry
+{
+ my ($self) = @_;
+ return $self->{"entry"};
+} # end of entry
+
+
+sub err
+{
+ my ($self) = @_;
+ return $self->{"errno"};
+} # end of err
+
+
+sub errno
+{
+ my ($self) = @_;
+ return $self->{"errno"};
+} # end of errno
+
+
+sub errstring
+{
+ my ($self) = @_;
+ return ldap_err2string($self->{"errno"});
+} # end of errstring
+
+
+sub extramsg
+{
+ my ($self) = @_;
+ return $self->{"extramsg"};
+} # end of extramsg
+
+
+sub ld
+{
+ my ($self) = @_;
+ return $self->{"ld"};
+} # end of ld
+
+
+sub msgtype
+{
+ my ($self, @args) = @_;
+
+ my ($msg) = $self->rearrange(['MSG'], @args);
+
+ $msg = $self->{"msg"} unless $msg;
+
+ return ldap_msgtype($msg);
+} # end of msgtype
+
+sub msgtype2str
+{
+ my ($self, @args) = @_;
+
+ my ($type) = $self->rearrange(['TYPE'], @args);
+
+ if( $type == $self->LDAP_RES_BIND ) {
+ return "LDAP_RES_BIND";
+ } elsif( $type == $self->LDAP_RES_SEARCH_ENTRY ) {
+ return "LDAP_RES_SEARCH_ENTRY";
+ } elsif( $type == $self->LDAP_RES_SEARCH_REFERENCE ) {
+ return "LDAP_RES_SEARCH_REFERENCE";
+ } elsif( $type == $self->LDAP_RES_SEARCH_RESULT ) {
+ return "LDAP_RES_SEARCH_RESULT";
+ } elsif( $type == $self->LDAP_RES_MODIFY ) {
+ return "LDAP_RES_MODIFY";
+ } elsif( $type == $self->LDAP_RES_ADD ) {
+ return "LDAP_RES_ADD";
+ } elsif( $type == $self->LDAP_RES_DELETE ) {
+ return "LDAP_RES_DELETE";
+ } elsif( $type == $self->LDAP_RES_MODDN ) {
+ return "LDAP_RES_MODDN";
+ } elsif( $type == $self->LDAP_RES_COMPARE ) {
+ return "LDAP_RES_COMPARE";
+ } elsif( $type == $self->LDAP_RES_EXTENDED ) {
+ return "LDAP_RES_EXTENDED";
+ } elsif( $type == $self->LDAP_RES_INTERMEDIATE ) {
+ return "LDAP_RES_INTERMEDIATE";
+ } elsif( $type == $self->LDAP_RES_ANY ) {
+ return "LDAP_RES_ANY";
+ } elsif( $type == $self->LDAP_RES_UNSOLICITED ) {
+ return "LDAP_RES_UNSOLICITED";
+ } else {
+ return "UNKNOWN";
+ }
+} # end of msgtype2str
+
+
+sub msgid
+{
+ my ($self, @args) = @_;
+
+ my ($result) = $self->rearrange(['RESULT'], @args);
+
+ $result = $self->{"result"} unless $result;
+
+ return ldap_msgid($self->{"ld"}, $result);
+} # end of msgid
+
+# Given array of elements of type Net::LDAP::Control
+# array of controls sutable for passing to C-calls is created.
+# It is to be freed by calling ldap_controls_array_free(...)
+# Note that this method is *NOT* to be used by the end user of
+# this library.
+sub create_controls_array
+{
+ my ($self, @args) = @_;
+
+ my ($location, $status, $ctrlp);
+
+ my $ctrls = ldap_controls_array_init($#args + 2);
+ for( $location = 0; $location < $#args + 1; $location++ ) {
+ ldap_control_set($ctrls, $args[$location], $location);
+ }
+ ldap_control_set($ctrls, undef, $#args + 1);
+
+ return $ctrls;
+} # create_controls_array
+
+
+# Creates control given its OID and berval. Default value of criticality is true.
+sub create_control
+{
+ my ($self, @args) = @_;
+
+ my ($oid, $berval, $critical) = $self->rearrange(['OID', 'BERVAL', 'CRITICAL'], @args);
+
+ croak("No OID of controls is passed") unless $oid;
+ croak("No BerVal is passed") unless $berval;
+ $critical = 1 if $critical == undef;
+
+ my ($ctrl) = undef;
+ my $status = ldap_create_control($oid, $berval, length($berval), $critical, $ctrl);
+
+ $self->errorize($status);
+ return $ctrl;
+} # end of create_control
+
+
+sub free_control
+{
+ my ($self, @args) = @_;
+
+ my ($control) = $self->rearrange(['CONTROL'], @args);
+
+ ldap_control_free($control);
+} # end of free_control
+
+
+# This subroutine was borrowed from CGI.pm. It does a wonderful job and
+# is much better than anything I created in my first attempt at named
+# arguments. I may replace it later.
+sub make_attributes
+{
+ my $attr = shift;
+ return () unless $attr && ref($attr) && ref($attr) eq 'HASH';
+ my $escape = shift || 0;
+ my(@att);
+ foreach (keys %{$attr}) {
+ my($key) = $_;
+ $key=~s/^\-//; # get rid of initial - if present
+
+ # old way: breaks EBCDIC!
+ # $key=~tr/A-Z_/a-z-/; # parameters are lower case, use dashes
+
+ ($key="\L$key") =~ tr/_/-/; # parameters are lower case, use dashes
+
+ my $value = $escape ? simple_escape($attr->{$_}) : $attr->{$_};
+ push(@att, defined($attr->{$_}) ? qq/$key="$value"/ : qq/$key/);
+ }
+ return @att;
+} # end of make_attributes
+
+
+sub rearrange
+{
+ my($self, $order, @param) = @_;
+ return () unless @param;
+
+ return @param unless (defined($param[0]) && substr($param[0],0,1) eq '-');
+
+ my $i;
+ for ($i=0;$i<@param;$i+=2) {
+ $param[$i]=~s/^\-//; # get rid of initial - if present
+ $param[$i]=~tr/a-z/A-Z/; # parameters are upper case
+ }
+
+ my(%param) = @param; # convert into associative array
+ my(@return_array);
+
+ my($key)='';
+ foreach $key (@$order) {
+ my($value);
+ # this is an awful hack to fix spurious warnings when the
+ # -w switch is set.
+ if (ref($key) && ref($key) eq 'ARRAY') {
+ foreach (@$key) {
+ last if defined($value);
+ $value = $param{$_};
+ delete $param{$_};
+ }
+ } else {
+ $value = $param{$key};
+ delete $param{$key};
+ }
+ push(@return_array,$value);
+ }
+ push (@return_array, $self->make_attributes(\%param)) if %param;
+ return (@return_array);
+} # end of rearrange
+
+
+# places internal ldap errors into $self under keys "errno" and "extramsg"
+sub errorize {
+ my ($self, $status) = @_;
+
+ my ($errdn, $extramsg);
+
+ if ($status != $self->LDAP_SUCCESS) {
+ $self->{"errno"} = ldap_get_lderrno($self->{"ld"}, $errdn, $extramsg);
+ $self->{"extramsg"} = $extramsg;
+
+ if( $self->{"debug"} ) {
+ print "LDAP ERROR STATUS: $status ".ldap_err2string($status)."\n";
+ printf("LDAP ERROR CODE: %x\n", $self->{"errno"});
+ print "LDAP ERROR MESSAGE: $extramsg\n";
+ }
+ } else {
+ $self->{"errno"}=0;
+ $self->{"errstring"}=undef;
+ }
+} # end of errorize
+
+
+sub CRITICAL {
+ 1;
+}
+
+
+sub NONCRITICAL {
+ 0;
+}
+
+# Preloaded methods go here.
+
+# Autoload methods go after =cut, and are processed by the autosplit program.
+
+1;
+__END__
+
+=head1 NAME
+
+Net::LDAPapi - Perl5 Module Supporting LDAP API
+
+=head1 SYNOPSIS
+
+ use Net::LDAPapi;
+
+ See individual items and Example Programs for Usage
+
+=head1 DESCRIPTION
+
+ This module allows Perl programmers to access and manipulate an LDAP
+ based Directory.
+
+ Versions beginning with 1.40 support both the original "C API" and
+ new "Perl OO" style interface methods. With version 1.42, I've added
+ named arguments.
+
+=head1 THE INTIAL CONNECTION
+
+ All connections to the LDAP server are started by creating a new
+ "blessed object" in the Net::LDAPapi class. This can be done quite
+ easily by the following type of statement.
+
+ $ld = new Net::LDAPapi($hostname);
+
+ Where $hostname is the name of your LDAP server. If you are not using
+ the standard LDAP port (389), you will also need to supply the
+ portnumber.
+
+ $ld = new Net::LDAPapi($hostname, 15555);
+
+ The new method can also be called with named arguments.
+
+ $ld = new Net::LDAPapi(-host=>$hostname, -port=>15389);
+
+ Instead of the above mentioned argumens -url can be used in the
+ following form
+
+ $ld = new Net::LDAPapi(-url=>"ldap://host:port");
+
+ Setting -debug=>"TRUE" will enable more verbose error messages.
+
+ Note that with named arguments, the order of the arguments is
+ insignificant.
+
+=head1 CONTROLS
+
+ In LDAP v3 controls are an additional piece of data, which can be
+ submitted with most of the requests to the server and returned back
+ attached to the result. Controls, passed to the call, are separated
+ in two types. The client side controls, which are not passed to the
+ server and are of not much use. They are denoted by -cctrls named
+ parameter. The server side controls, denoted by -sctrls named
+ parameter are actually passed to the server and may affect its
+ operation or returned results. Each entry of the result may have
+ controls attached to it as well ( see parse_entry(...) call ).
+
+ -cctrls and -sctrls must be reference to array of controls.
+
+ To create control call create_control(...) method. Bellow is an
+ example of creating valsort control.
+
+ my $asn = Convert::ASN1->new;
+ $asn->prepare('SEQUENCE { b BOOLEAN }');
+ my $berval = $asn->encode(b=>1); # or 1
+
+ my $ctrl =
+ $ld->create_control(-oid=>Net::LDAPapi::LDAP_CONTROL_VALSORT,
+ -berval=>$berval,
+ -critical=>Net::LDAPapi::CRITICAL);
+
+ The control is to be freed by calling free_control($ctrl).
+
+ If contol is attached to results entry, it can be retrieved by
+ calling parse_result($entry). If no entry is passed to
+ parse_result(...) then current entry is used. It returns hash
+ with following keys
+
+ Key Value
+ -------------------
+ matcheddn string
+ errmsg string
+ referrals array reference
+ serverctrls array reference
+
+ You can look into content of the control by using get_contol_XXX
+ functions like this:
+
+ local %parsed = $ld->parse_result($entry);
+ local $serverctrls = $parsed{"serverctrls"};
+ local @sctrls = @$serverctrls;
+ if( scalar(@sctrls) > 0 ) {
+ foreach $ctrl (@sctrls) {
+ print "\nreceived control\n";
+ print "oid = ".$ld->get_control_oid($ctrl)."\n";
+ print "berval = ".$ld->get_control_berval($ctrl)."\n";
+ print "critical = ".$ld->get_control_critical($ctrl)."\n";
+ }
+ }
+
+=head1 BINDING
+
+ After creating a connection to the LDAP server, you may need to
+ bind to the server prior to performing any LDAP related functions.
+ This can be done with the 'bind' methods.
+
+ An anonymous bind can be performed without arguments:
+
+ $status = $ld->bind_s;
+
+ A simple bind can be performed by specifying the DN and PASSWORD of
+ the user you are authenticating as:
+
+ $status = $ld->bind_s($dn, $password);
+
+ Note that if $password above was "", you would be doing a reference
+ bind, which would return success even if the password in the
+ directory was non-null. Thus if you were using the bind to check a
+ password entered with one in the directory, you should first check
+ to see if $password was NULL.
+
+ To perform SASL bind fill in appropriate parameters calling
+ sasl_params(...) and call
+
+ $status = $ld->bind_s(-type=>LDAP_AUTH_SASL)
+
+ Bellow is an example of GSSAPI K5 bind parameters.
+
+ $ld->sasl_parms(-mech=>"GSSAPI", -realm=>"domain.name.com",
+ -authzid=>"", -secprops=>"",
+ -flags=>LDAP_SASL_QUIET);
+
+ For all of the above operations, you could compare $status to
+ LDAP_SUCCESS to see if the operation was successful.
+
+ Additionally, you could use 'bind' rather than 'bind_s' if you wanted
+ to use the Asynchronous LDAP routines. The asynchronous routines
+ would return a MSGID rather than a status. To find the status of an
+ Asynchronous bind, you would need to first obtain the result with a
+ call to $ld->result. See the entry for result later in the man page,
+ as well as the 'ldapwalk.pl' example for further information on
+ obtaining results from Asynchronous operations.
+
+ The bind operations can also accept named arguments.
+
+ $status = $ld->bind_s(-dn=>$dn,
+ -password=>$password,
+ -type=>LDAP_AUTH_SIMPLE);
+
+ As with all other commands that support named arguments, the order of
+ the arguments makes no difference.
+
+=head1 GENERATING AN ADD/MODIFY HASH
+
+ For the add and modify routines you will need to generate
+ a list of attributes and values.
+
+ You will do this by creating a HASH table. Each attribute in the
+ hash contains associated values. These values can be one of three
+ things.
+
+ - SCALAR VALUE (ex. "Clayton Donley")
+ - ARRAY REFERENCE (ex. ["Clayton Donley","Clay Donley"])
+ - HASH REFERENCE (ex. {"r",["Clayton Donley"]}
+ note: the value inside the HASH REFERENCE must currently
+ be an ARRAY REFERENCE.
+
+ The key inside the HASH REFERENCE must be one of the following for a
+ modify operation:
+ - "a" for LDAP_MOD_ADD (Add these values to the attribute)
+ - "r" for LDAP_MOD_REPLACE (Replace these values in the attribute)
+ - "d" for LDAP_MOD_DELETE (Delete these values from the attribute)
+
+ Additionally, in add and modify operations, you may specify "b" if the
+ attributes you are adding are BINARY (ex. "rb" to replace binary).
+
+ Currently, it is only possible to do one operation per add/modify
+ operation, meaning you can't do something like:
+
+ {"d",["Clayton"],"a",["Clay"]} <-- WRONG!
+
+ Using any combination of the above value types, you can do things like:
+
+ %ldap_modifications = (
+ "cn", "Clayton Donley", # Replace 'cn' values
+ "givenname", ["Clayton","Clay"], # Replace 'givenname' values
+ "mail", {"a",["donley\@cig.mcel.mot.com"], #Add 'mail' values
+ "jpegphoto", {"rb",[$jpegphotodata]}, # Replace Binary jpegPhoto
+ );
+
+ Then remember to call the add or modify operations with a REFERENCE to
+ this HASH. Something like:
+
+ $ld->modify_s($modify_dn,\%ldap_modifications);
+
+=head1 GETTING/SETTING LDAP INTERNAL VALUES
+
+ The following methods exist to obtain internal values within a
+ Net::LDAPapi object:
+
+ o errno - The last error-number returned by the LDAP library for this
+ connection.
+ ex: print "Error Number: " . $ld->errno . "\n";
+
+ o errstring - The string equivalent of 'errno'.
+ ex: print "Error: " . $ld->errstring . "\n";
+
+ o ld - Reference to the actual internal LDAP structure. Only useful if
+ you needed to obtain this pointer for use in non-OO routines.
+ ex: $ldptr = $ld->ld;
+
+ o entry - Reference to the current entry. Not typically needed, but method
+ supplied, just in case.
+ ex: $entry = $ld->entry;
+
+ o msgid - Get msgid from an LDAP Result.
+ ex: $msgid = $ld->msgid; # msgid of current result
+ ex: $msgid = $ld->msgid($result) # msgid of $result
+
+ o msgtype - Get msgtype from an LDAP Result.
+ ex: $msgtype = $ld->msgtype; # msgtype of current result
+ ex: $msgtype = $ld->msgtype($result) # msgtype of $result
+
+ These methods are only useful for GETTING internal information, not setting
+ it. No methods are currently available for SETTING these internal values.
+
+=head1 GETTING AND SETTING LDAP SESSION OPTIONS
+
+ The get_option and set_option methods can be used to get and set LDAP
+ session options.
+
+ The following LDAP options can be set or gotten with these methods:
+ LDAP_OPT_DEREF - Dereference
+ LDAP_OPT_SIZELIMIT - Maximum Number of Entries to Return
+ LDAP_OPT_TIMELIMIT - Timeout for LDAP Operations
+ LDAP_OPT_REFERRALS - Follow Referrals
+
+ For both get and set operations, the first argument is the relivant
+ option. In get, the second argument is a reference to a scalar variable
+ that will contain the current value of the option. In set, the second
+ argument is the value at which to set this option.
+
+ Examples:
+ $ld->set_option(LDAP_OPT_SIZELIMIT,50);
+ $ld->get_option(LDAP_OPT_SIZELIMIT,\$size);
+
+ When setting LDAP_OPT_REFERRALS, the second argument is either LDAP_OPT_ON
+ or LDAP_OPT_OFF. Other options require a number.
+
+ Both get_option and set_option return 0 on success and non-zero otherwise.
+
+=head1 SSL SUPPORT
+
+ When compiled with the Mozilla SDK, this module now supports SSL.
+ I do not have an SSL capable server, but I'm told this works. The
+ functions available are:
+
+ o ssl - Turn on SSL for this connection.
+ Install I/O routines to make SSL over LDAP possible
+ o ssl_client_init($certdbpath,$certdbhandle)
+ Initialize the secure parts (called only once)
+
+ Example:
+ $ld = new Net::LDAPapi("host",LDAPS_PORT);
+ $ld->ssl_client_init($certdbpath,$certdbhandle);
+ $ld->ssl;
+
+=head1 SETTING REBIND PROCESS
+
+ As of version 1.42, rebinding now works properly.
+
+ The set_rebind_proc method is used to set a PERL function to supply DN,
+ PASSWORD, and AUTHTYPE for use when the server rebinds (for referals,
+ etc...).
+
+ Usage should be something like:
+ $rebind_ref = \&my_rebind_proc;
+ $ld->set_rebind_proc($rebind_ref);
+
+ You can then create the procedure specified. It should return 3 values.
+
+ Example:
+ sub my_rebind_proc
+ {
+ return($dn,$pass,LDAP_AUTH_SIMPLE);
+ }
+
+
+=head1 SUPPORTED METHODS
+
+=item abandon MSGID SCTRLS CCTRLS
+
+ This cancels an asynchronous LDAP operation that has not completed. It
+ returns an LDAP STATUS code upon completion.
+
+ Example:
+
+ $status = ldap_abandon($ld, $msgid); # XXX fix this
+
+=item add DN ATTR SCTRLS CCTRLS
+
+ Begins an an asynchronous LDAP Add operation. It returns a MSGID or undef
+ upon completion.
+
+ Example:
+
+ %attributes = (
+ "cn", ["Clayton Donley","Clay Donley"] #Add Multivalue cn
+ "sn", "Donley", #Add sn
+ "telephoneNumber", "+86-10-65551234", #Add telephoneNumber
+ "objectClass", ["person","organizationalPerson"],
+ # Add Multivalue objectClass
+ "jpegphoto", {"b",[$jpegphoto]}, # Add Binary jpegphoto
+ );
+
+ $entrydn = "cn=Clayton Donley, o=Motorola, c=US";
+
+ $msgid = $ld->add($entrydn, \%attributes);
+
+ Note that in most cases, you will need to be bound to the LDAP server
+ as an administrator in order to add users.
+
+=item add_s DN ATTR SCTRLS CCTRLS
+
+ Synchronous version of the 'add' method. Arguments are identical
+ to the 'add' method, but this operation returns an LDAP STATUS,
+ not a MSGID.
+
+ Example:
+
+ $ld->add_s($entrydn, \%attributes);
+
+ See the section on creating the modify structure for more information
+ on populating the ATTRIBUTES field for Add and Modify operations.
+
+=item bind DN PASSWORD TYPE SCTRLS CCTRLS
+
+ Asynchronous method for binding to the LDAP server. It returns a
+ MSGID.
+
+ Examples:
+
+ $msgid = $ld->bind;
+ $msgid = $ld->bind("cn=Clayton Donley, o=Motorola, c=US", "abc123");
+
+
+=item bind_s DN PASSWORD TYPE SCTRLS CCTRLS
+
+ Synchronous method for binding to the LDAP server. It returns
+ an LDAP STATUS.
+
+ Examples:
+
+ $status = $ld->bind_s;
+ $status = $ld->bind_s("cn=Clayton Donley, o=Motorola, c=US", "abc123");
+
+
+=item compare DN ATTR VALUE SCTRLS CCTRLS
+
+ Asynchronous method for comparing a value with the value contained
+ within DN. Returns a MSGID or undef.
+
+ Example:
+
+ $msgid = $ld->compare("cn=Clayton Donley, o=Motorola, c=US", \
+ $type, $value);
+
+=item compare_s DN ATTR VALUE SCTRLS CCTRLS
+
+ Synchronous method for comparing a value with the value contained
+ within DN. Returns an LDAP_COMPARE_TRUE, LDAP_COMPARE_FALSE or an error code.
+
+ Example:
+
+ $status = $ld->compare_s("cn=Clayton Donley, o=Motorola, c=US", \
+ $type, $value);
+
+=item count_entries
+
+ Returns the number of entries in an LDAP result chain.
+
+ Example:
+
+ $number = $ld->count_entries;
+
+=item count_references MSG
+
+ Return number of references in a given/current message.
+
+ Example:
+
+ $number = $ld->count_references
+
+=item delete DN
+
+ Asynchronous method to delete DN. Returns a MSGID or -1 if error.
+
+ Example:
+
+ $msgid = $ld->delete("cn=Clayton Donley, o=Motorola, c=US");
+
+=item delete_s DN
+
+ Synchronous method to delete DN. Returns an LDAP STATUS.
+
+ Example:
+
+ $status = $ld->delete_s("cn=Clayton Donley, o=Motorola, c=US");
+
+=item dn2ufn DN
+
+ Converts a Distinguished Name (DN) to a User Friendly Name (UFN).
+ Returns a string with the UFN.
+
+ Since this operation doesn't require an LDAP object to work, you
+ could technically access the function directly as 'ldap_dn2ufn' rather
+ that the object oriented form.
+
+ Example:
+
+ $ufn = $ld->dn2ufn("cn=Clayton Donley, o=Motorola, c=US");
+
+=item explode_dn DN NOTYPES
+
+ Splits the DN into an array comtaining the separate components of
+ the DN. Returns an Array. NOTYPES is a 1 to remove attribute
+ types and 0 to retain attribute types.
+
+ Can also be accessed directly as 'ldap_explode_dn' if no session is
+ initialized and you don't want the object oriented form.
+
+ In OpenLDAP this call is depricated.
+
+ Example:
+
+ @components = $ld->explode_dn($dn, 0);
+
+=item explode_rdn RDN NOTYPES
+
+ Same as explode_dn, except that the first argument is a
+ Relative Distinguished Name. NOTYPES is a 1 to remove attribute
+ types and 0 to retain attribute types. Returns an array with
+ each component.
+
+ Can also be accessed directly as 'ldap_explode_rdn' if no session is
+ initialized and you don't want the object oriented form.
+
+ In OpenLDAP this call is depricated.
+
+ Example:
+
+ @components = $ld->explode_rdn($rdn, 0);
+
+=item first_attribute
+
+ Returns pointer to first attribute name found in the current entry.
+ Note that this only returning attribute names (ex: cn, mail, etc...).
+ Returns a string with the attribute name.
+
+ Returns an empty string when no attributes are available.
+
+ Example:
+
+ $attr = $ld->first_attribute;
+
+=item first_entry
+
+ Sets internal pointer to the first entry in a chain of results. Returns
+ an empty string when no entries are available.
+
+ Example:
+
+ $entry = $ld->first_entry;
+
+=item first_message
+
+ Return the first message in a chain of result returned by the search
+ operation. LDAP search operations return LDAPMessage, which is a head
+ in chain of messages accessable to the user. Not all all of them are
+ entries though. Type of the message can be obtained by calling
+ msgtype(...) function.
+
+=item get_all_entries RESULT
+
+ Returns result of the search operation in the following format
+ (HASH)
+ dn -> (HASH)
+ key -> (ARRAY)
+
+ Example:
+ my $all_entries_ref = $ld->get_all_entries;
+ my %all_entries = %$all_entries_ref;
+
+ foreach (keys %all_entries) {
+ print "<$_> -> <".$all_entries{$_}.">\n";
+ $entry = $all_entries{$_};
+
+ local %entry_h = %$entry;
+ foreach $k (keys %entry_h) {
+ $values = $entry_h{$k};
+
+ print " <$k> ->\n";
+ foreach $val (@$values) {
+ print " <$val>\n";
+ }
+ }
+ }
+
+=item get_dn MSG
+
+ Returns a string containing the DN for the specified message or an
+ empty string if an error occurs. If no message is specified then
+ then default entry is used.
+
+ Example:
+
+ $dn = $ld->get_dn;
+
+=item get_entry_controls MSG
+
+ Returns an array of controls returned with the given entry. If not MSG
+ is given as a paramater then current message/entry is used.
+
+ Example:
+
+ my @sctrls = $ld->get_entry_controls($msg);
+ foreach $ctrl (@sctrls) {
+ print "control oid is ".$self->get_control_oid($ctrl)."\n";
+ }
+
+=item get_values ATTR
+
+ Obtain a list of all values associated with a given attribute.
+ Returns an empty list if none are available.
+
+ Example:
+
+ @values = $ld->get_values("cn");
+
+ This would put all the 'cn' values for $entry into the array @values.
+
+=item get_values_len ATTR
+
+ Retrieves a set of binary values for the specified attribute.
+
+ Example:
+
+ @values = $ld->get_values_len("jpegphoto");
+
+ This would put all the 'jpegphoto' values for $entry into the array @values.
+ These could then be written to a file, or further processed.
+
+=item is_ldap_url URL
+
+ Checks to see if a specified URL is a valid LDAP Url. Returns 0 on false
+ and 1 on true.
+
+ Example:
+
+ $isurl = $ld->is_ldap_url("ldap://x500.my.org/o=Org,c=US");
+
+=item listen_for_changes BASEDN SCOPE FILTER ATTRS ATTRSONLY TIMEOUT SIZELIMIT COOKIE
+
+ Experimental function which implements syncrepl API in
+ refreshAndPersist mode. All but one arguments are the same as in search
+ function. Argument 'cookie' is the special one here. It must be specified
+ and is a file name in which cookie is to be stored. On a subsequent
+ restart of the seach only the newer results will be returned than those
+ indicated by the stored cookie. To refresh all entries, one would have to
+ remove that file.
+
+ This function is to be used in conjunction with next_changed_entries(...),
+ there you will also find example of its usage.
+
+=item msgfree
+
+ Frees the current LDAP result. Returns the type of message freed.
+
+ Example:
+
+ $type = $ld->msgfree;
+
+=item msgtype MSG
+
+ Returns the numeric id of a given message. If no MSG is given as a parameter
+ then current message is used. Following types are recognized: LDAP_RES_BIND,
+ LDAP_RES_SEARCH_ENTRY, LDAP_RES_SEARCH_REFERENCE, LDAP_RES_SEARCH_RESULT,
+ LDAP_RES_MODIFY, LDAP_RES_ADD, LDAP_RES_DELETE, LDAP_RES_MODDN,
+ LDAP_RES_COMPARE, LDAP_RES_EXTENDED, LDAP_RES_INTERMEDIATE, LDAP_RES_ANY,
+ LDAP_RES_UNSOLICITED.
+
+ Example:
+
+ $type = $ld->msgtype
+
+=item msgtype2str TYPE
+
+ Returns string representation of a given numeric message type.
+
+ Example:
+ print "type = ".$ld->msgtype2str($ld->msgtype)."\n";
+
+=item modify DN MOD
+
+ Asynchronous method to modify an LDAP entry. DN is the DN to
+ modify and MOD contains a hash-table of attributes and values. If
+ multiple values need to be passed for a specific attribute, a
+ reference to an array must be passed.
+
+ Returns the MSGID of the modify operation.
+
+ Example:
+
+ %mods = (
+ "telephoneNumber", "", #remove telephoneNumber
+ "sn", "Test", #set SN to TEST
+ "mail", ["me\@abc123.com","me\@second-home.com"], #set multivalue 'mail'
+ "pager", {"a",["1234567"]}, #Add a Pager Value
+ "jpegphoto", {"rb",[$jpegphoto]}, # Replace Binary jpegphoto
+ );
+
+ $msgid = $ld->modify($entrydn,\%mods);
+
+ The above would remove the telephoneNumber attribute from the entry
+ and replace the "sn" attribute with "Test". The value in the "mail"
+ attribute for this entry would be replaced with both addresses
+ specified in @mail. The "jpegphoto" attribute would be replaced with
+ the binary data in $jpegphoto.
+
+=item modify_s DN MOD
+
+ Synchronous version of modify method. Returns an LDAP STATUS. See the
+ modify method for notes and examples of populating the MOD
+ parameter.
+
+ Example:
+
+ $status = $ld->modify_s($entrydn,\%mods);
+
+=item modrdn2 DN NEWRDN DELETE
+
+ No longer available. Use function 'rename'.
+
+=item modrdn2_s DN NEWRDN DELETE
+
+ No longer available. Use function 'rename_s'.
+
+=item next_attribute
+
+ Similar to first_attribute, but obtains next attribute.
+ Returns a string comtaining the attribute name. An empty string
+ is returned when no further attributes exist.
+
+ Example:
+
+ $attr = $ld->next_attribute;
+
+=item next_changed_entries MSGID ALL TIMEOUT
+
+ This function is too be used together with listen_for_changes(...) (see above).
+ It returns an array of Entries, which has just changed. Each element in this
+ array is a hash reference with two key value pairs, 'entry' which contains usual
+ entry and 'state' which contain one of the following strings 'present', 'add',
+ 'modify' or 'delete'.
+
+ Example:
+
+ my $msgid = $ld->listen_for_changes('', LDAP_SCOPE_SUBTREE, "(cn=Dm*)", NULL, NULL,
+ NULL, NULL, $cookie);
+
+ while(1) {
+ while( @entries = $ld->next_changed_entries($msgid, 0, -1) ) {
+ foreach $entry (@entries) {
+ print "entry dn is <".$ld->get_dn($entry->{'entry'})."> ".
+ $entry->{'state'}."\n";
+ }
+ }
+ }
+
+=item next_entry
+
+ Moves internal pointer to the next entry in a chain of search results.
+
+ Example:
+
+ $entry = $ld->next_entry;
+
+=item next_message
+
+ Moves internal pointer to the next message in a chain of search results.
+
+ Example:
+
+ $msg = $ld->next_message;
+
+=item parse_result MSG FREEMSG
+
+ This function is used to retrieve auxiliary data associated with the
+ message. The return value is a hashtable containing following kevalue
+ pairs.
+ 'errcode' -> numeric
+ 'matcheddn' -> string
+ 'errmsg' -> string
+ 'referrals' -> array reference
+ 'serverctrls' -> array reference
+
+ The FREEMSG parameter determines whether the parsed message is freed
+ or not after the extraction. Any non-zero value will make it free the
+ message. The msgfree() routine can also be used to free the message
+ later.
+
+=item perror MSG
+
+ If an error occurs while performing an LDAP function, this procedure
+ will display it. You can also use the err and errstring methods to
+ manipulate the error number and error string in other ways.
+
+ Note that this function does NOT terminate your program. You would
+ need to do any cleanup work on your own.
+
+ Example:
+
+ $ld->perror("add_s");
+
+=item rename DN NEWRDN NEWSUPER DELETE SCTRLS CCTRLS
+
+ Asynchronous method to change the name of an entry. NEWSUPER is a new
+ parent (superior entry). If set to NULL then only the RDN is changed.
+ Set DELETE to non-zero if you wish to remove the attribute values from the
+ old name. Returns a MSGID.
+
+ Example:
+
+ $msgid = $ld->rename("cn=Clayton Donley, o=Motorola, c=US", \
+ "cn=Clay Donley", NULL, 0);
+
+=item rename_s DN NEWRDN NEWSUPER DELETE SCTRLS CCTRLS
+
+ Synchronous method to change the name of an entry. NEWSUPER is a new
+ parent (superior entry). If set to NULL then only the RDN is changed.
+ Set DELETE to non-zero if you wish to remove the attribute values from the
+ old name. Returns a LDAP STATUS.
+
+ Example:
+
+ $status = $ld->rename("cn=Clayton Donley, o=Motorola, c=US", \
+ "cn=Clay Donley", NULL, 0);
+
+=item result MSGID ALL TIMEOUT
+
+ Retrieves the result of an operation initiated using an asynchronous LDAP
+ call. It calls internally ldap_result function. Returns LDAP message or
+ undef if error. Return value of ldap_result call stored in $ld->{"status"}
+ and is set -1 if something wrong happened, 0 if specified timeout was
+ exceeded or type of the returned message.
+
+ MSGID is the MSGID returned by the Asynchronous LDAP call. Set ALL to
+ 0 to receive entries as they arrive, or non-zero to receive all entries
+ before returning. Set TIMEOUT to the number of seconds to wait for the
+ result, or -1 for no timeout.
+
+ Example:
+
+ $entry = $ld->result($msgid, 0, 1);
+ print "msgtype = ".$ld->msgtype2str($ld->{"status"})."\n";
+
+=item result_entry
+
+ This function is a shortcut for moving pointer along the chain of entries
+ in the result. It is used instead of first_entry and next_entry functions.
+
+ Example
+ while( $entry = $ld->result_entry ) {
+ print "dn = ".$ld->get_dn($entry)."\n";
+ }
+
+=item result_message
+
+ This function is a shortcut for moving pointer along the chain of messages
+ in the result. It is used instead of first_message and next_message functions.
+
+ Example
+ while( $msg = $ld->result_message ) {
+ $msgtype = $self->msgtype($msg);
+ }
+
+=item search BASE SCOPE FILTER ATTRS ATTRSONLY
+
+ Begins an asynchronous LDAP search. Returns a MSGID or -1 if an
+ error occurs. BASE is the base object for the search operation.
+ FILTER is a string containing an LDAP search filter. ATTRS is a
+ reference to an array containing the attributes to return. An
+ empty array would return all attributes. ATTRSONLY set to non-zero
+ will only obtain the attribute types without values.
+
+ SCOPE is one of the following:
+ LDAP_SCOPE_BASE
+ LDAP_SCOPE_ONELEVEL
+ LDAP_SCOPE_SUBTREE
+
+ Example:
+
+ @attrs = ("cn","sn"); # Return specific attributes
+ @attrs = (); # Return all Attributes
+
+ $msgid = $ld->search("o=Motorola, c=US", LDAP_SCOPE_SUBTREE, \
+ "(sn=Donley), \@attrs, 0);
+
+=item search_s BASE SCOPE FILTER ATTRS ATTRSONLY (rewrite XXX)
+
+ Performs a synchronous LDAP search. Returns an LDAP STATUS. BASE
+ is the base object for the search operation. FILTER is a string
+ containing an LDAP search filter. ATTRS is a reference to an array
+ containing the attributes to return. An empty array would return all
+ attributes. ATTRSONLY set to non-zero will only obtain the attribute
+ types without values.
+
+ SCOPE is one of the following:
+ LDAP_SCOPE_BASE
+ LDAP_SCOPE_ONELEVEL
+ LDAP_SCOPE_SUBTREE
+
+ Example:
+
+ @attrs = ("cn","sn"); # Return specific attributes
+ @attrs = (); # Return all attributes
+
+ $status = $ld->search_s("o=Motorola, c=US",LDAP_SCOPE_SUBTREE, \
+ "(sn=Donley)",\@attrs,0);
+
+=item search_st BASE SCOPE FILTER ATTRS ATTRSONLY TIMEOUT (rewrite/remove XXX)
+
+ Performs a synchronous LDAP search with a TIMEOUT. See search_s
+ for a description of parameters. Returns an LDAP STATUS. Results are
+ put into RESULTS. TIMEOUT is a number of seconds to wait before giving
+ up, or -1 for no timeout.
+
+ Example:
+
+ $status = $ld->search_st("o=Motorola, c=US",LDAP_SCOPE_SUBTREE, \
+ "(sn=Donley),[],0,3);
+
+=item unbind SCTRLS CCTRLS
+
+ Unbind LDAP connection with specified SESSION handler.
+
+ Example:
+
+ $ld->unbind;
+
+=item url_parse URL
+
+ Parses an LDAP URL into separate components. Returns a HASH reference
+ with the following keys, if they exist in the URL:
+
+ host - LDAP Host
+ port - LDAP Port
+ dn - LDAP Base DN
+ attr - LDAP Attributes to Return (ARRAY Reference)
+ filter - LDAP Search Filter
+ scope - LDAP Search Scope
+ options - Mozilla key specifying LDAP over SSL
+
+ Example:
+
+ $urlref = $ld->url_parse("ldap://ldap.my.org/o=My,c=US");
+
+=item url_search URL ATTRSONLY
+
+ Perform an asynchronous search using an LDAP URL. URL is the LDAP
+ URL to search on. ATTRSONLY determines whether we are returning
+ the values for each attribute (0) or only returning the attribute
+ names (1). Results are retrieved and parsed identically to a call
+ to the search method.
+
+ Returns a non-negative MSGID upon success.
+
+ Example:
+
+ $msgid = $ld->url_search($my_ldap_url, 0);
+
+=item url_search_s URL ATTRSONLY
+
+ Synchronous version of the url_search method. Results are retrieved
+ and parsed identically to a call to the search_s method.
+
+ Returns LDAP_SUCCESS upon success.
+
+ Example:
+
+ $status = $ld->url_search_s($my_ldap_url, 0);
+
+=item url_search_st URL ATTRSONLY TIMEOUT
+
+ Similar to the url_search_s method, except that it allows a timeout
+ to be specified. The timeout is specified as seconds. A timeout of
+ 0 specifies an unlimited timeout. Results are retrieved and parsed
+ identically to a call to the search_st method.
+
+ Returns LDAP_SUCCESS upon success.
+
+ Example:
+
+ $status = $ld->url_search_s($my_ldap_url,0,2);
+
+=head1 AUTHOR
+
+Clayton Donley, donley@wwa.com
+http://miso.wwa.com/~donley/
+
+=head1 SEE ALSO
+
+perl(1).
+
+=cut
diff --git a/LDAPapi.xs b/LDAPapi.xs
new file mode 100644
index 0000000..a40c641
--- /dev/null
+++ b/LDAPapi.xs
@@ -0,0 +1,1650 @@
+/* This file was modified by Howard Chu, hyc@symas.com, 2000-2003.
+ * Most changes are #if OPENLDAP, some are not marked.
+ */
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include "EXTERN.h"
+#include "perl.h"
+#include "XSUB.h"
+
+#ifdef __cplusplus
+}
+#endif
+
+#include <lber.h>
+#include <ldap.h>
+
+#include <sasl/sasl.h>
+
+/* Mozilla prototypes declare things as "const char *" while */
+/* OpenLDAP uses "char *" */
+
+#ifdef MOZILLA_LDAP
+ #define LDAP_CHAR const char
+ #include <ldap_ssl.h>
+#else
+
+#ifndef OPENLDAP
+ #include "ldap_compat.h"
+#endif
+
+ #define LDAP_CHAR char
+#endif
+
+#ifndef LDAP_RES_INTERMEDIATE
+ #define LDAP_RES_INTERMEDIATE 0x79U /* 121 */
+#endif
+
+/* Function Prototypes for Internal Functions */
+
+static char **av2modvals(AV *ldap_value_array_av, int ldap_isa_ber);
+static LDAPMod *parse1mod(SV *ldap_value_ref,char *ldap_current_attribute,
+ int ldap_add_func,int cont);
+static LDAPMod **hash2mod(SV *ldap_change,int ldap_add_func, const char *func);
+
+#ifdef OPENLDAP
+ static int internal_rebind_proc(LDAP *ld, LDAP_CONST char *url,
+ ber_tag_t request, ber_int_t msgid,
+ void *params);
+#endif
+
+/* The Name of the PERL function to return DN, PASSWD, AUTHTYPE on Rebind */
+/* Set using 'set_rebind_proc()' */
+SV *ldap_perl_rebindproc = NULL;
+
+
+/* Use constant.h generated from constant.gen */
+/* Courtesy of h.b.furuseth@usit.uio.no */
+
+#include "constant.h"
+
+
+/* Strcasecmp - Some operating systems don't have this, including NT */
+
+int StrCaseCmp(const char *s, const char *t)
+{
+ while (*s && *t && toupper(*s) == toupper(*t))
+ {
+ s++; t++;
+ }
+ return(toupper(*s) - toupper(*t));
+}
+
+/* av2modvals - Takes a single Array Reference (AV *) and returns */
+/* a null terminated list of char pointers. */
+
+static
+char **av2modvals(AV *ldap_value_array_av, int ldap_isa_ber)
+{
+ I32 ldap_arraylen;
+ char **ldap_ch_modvalues = NULL;
+ char *ldap_current_value_char = NULL;
+ struct berval **ldap_bv_modvalues = NULL;
+ struct berval *ldap_current_bval = NULL;
+ SV **ldap_current_value_sv;
+ int ldap_value_count = 0,ldap_pvlen,ldap_real_valuecount = 0;
+
+ ldap_arraylen = av_len(ldap_value_array_av);
+ if (ldap_arraylen < 0)
+ return(NULL);
+
+ if (ldap_isa_ber == 1)
+ {
+ New(1,ldap_bv_modvalues,2+ldap_arraylen,struct berval *);
+ } else {
+ New(1,ldap_ch_modvalues,2+ldap_arraylen,char *);
+ }
+
+ for (ldap_value_count = 0; ldap_value_count <=ldap_arraylen;
+ ldap_value_count++)
+ {
+ ldap_current_value_sv = av_fetch(ldap_value_array_av,ldap_value_count,0);
+ ldap_current_value_char = SvPV(*ldap_current_value_sv,PL_na);
+ ldap_pvlen = SvCUR(*ldap_current_value_sv);
+ if (strcmp(ldap_current_value_char,"") != 0)
+ {
+ if (ldap_isa_ber == 1)
+ {
+ New(1,ldap_current_bval,1,struct berval);
+ ldap_current_bval->bv_len = ldap_pvlen;
+ ldap_current_bval->bv_val = ldap_current_value_char;
+ ldap_bv_modvalues[ldap_real_valuecount] = ldap_current_bval;
+ } else {
+ ldap_ch_modvalues[ldap_real_valuecount] = ldap_current_value_char;
+ }
+ ldap_real_valuecount++;
+ }
+ }
+ if (ldap_isa_ber == 1)
+ {
+ ldap_bv_modvalues[ldap_real_valuecount] = NULL;
+ return ((char **)ldap_bv_modvalues);
+ } else {
+ ldap_ch_modvalues[ldap_real_valuecount] = NULL;
+ return (ldap_ch_modvalues);
+ }
+}
+
+
+/* parse1mod - Take a single reference, figure out if it is a HASH, */
+/* ARRAY, or SCALAR, then extract the values and attributes and */
+/* return a single LDAPMod pointer to this data. */
+
+static
+LDAPMod *parse1mod(SV *ldap_value_ref,char *ldap_current_attribute,
+ int ldap_add_func,int cont)
+{
+ LDAPMod *ldap_current_mod;
+ static HV *ldap_current_values_hv;
+ HE *ldap_change_element;
+ char *ldap_current_modop;
+ SV *ldap_current_value_sv;
+ I32 keylen;
+ int ldap_isa_ber = 0;
+
+ if (ldap_current_attribute == NULL)
+ return(NULL);
+ New(1,ldap_current_mod,1,LDAPMod);
+ ldap_current_mod->mod_type = ldap_current_attribute;
+ if (SvROK(ldap_value_ref))
+ {
+ if (SvTYPE(SvRV(ldap_value_ref)) == SVt_PVHV)
+ {
+ if (!cont)
+ {
+ ldap_current_values_hv = (HV *) SvRV(ldap_value_ref);
+ hv_iterinit(ldap_current_values_hv);
+ }
+ if ((ldap_change_element = hv_iternext(ldap_current_values_hv)) == NULL)
+ return(NULL);
+ ldap_current_modop = hv_iterkey(ldap_change_element,&keylen);
+ ldap_current_value_sv = hv_iterval(ldap_current_values_hv,
+ ldap_change_element);
+ if (ldap_add_func == 1)
+ {
+ ldap_current_mod->mod_op = 0;
+ } else {
+ if (strchr(ldap_current_modop,'a') != NULL)
+ {
+ ldap_current_mod->mod_op = LDAP_MOD_ADD;
+ } else if (strchr(ldap_current_modop,'r') != NULL)
+ {
+ ldap_current_mod->mod_op = LDAP_MOD_REPLACE;
+ } else if (strchr(ldap_current_modop,'d') != NULL) {
+ ldap_current_mod->mod_op = LDAP_MOD_DELETE;
+ } else {
+ return(NULL);
+ }
+ }
+ if (strchr(ldap_current_modop,'b') != NULL)
+ {
+ ldap_isa_ber = 1;
+ ldap_current_mod->mod_op = ldap_current_mod->mod_op | LDAP_MOD_BVALUES;
+ }
+ if (SvTYPE(SvRV(ldap_current_value_sv)) == SVt_PVAV)
+ {
+ if (ldap_isa_ber == 1)
+ {
+ ldap_current_mod->mod_values =
+ av2modvals((AV *)SvRV(ldap_current_value_sv),ldap_isa_ber);
+ } else {
+ ldap_current_mod->mod_values =
+ av2modvals((AV *)SvRV(ldap_current_value_sv),ldap_isa_ber);
+ }
+ }
+ } else if (SvTYPE(SvRV(ldap_value_ref)) == SVt_PVAV) {
+ if (cont)
+ return NULL;
+ if (ldap_add_func == 1)
+ ldap_current_mod->mod_op = 0;
+ else
+ ldap_current_mod->mod_op = LDAP_MOD_REPLACE;
+ ldap_current_mod->mod_values = av2modvals((AV *)SvRV(ldap_value_ref),0);
+ if (ldap_current_mod->mod_values == NULL)
+ {
+ ldap_current_mod->mod_op = LDAP_MOD_DELETE;
+ }
+ }
+ } else {
+ if (cont)
+ return NULL;
+ if (strcmp(SvPV(ldap_value_ref,PL_na),"") == 0)
+ {
+ if (ldap_add_func != 1)
+ {
+ ldap_current_mod->mod_op = LDAP_MOD_DELETE;
+ ldap_current_mod->mod_values = NULL;
+ } else {
+ return(NULL);
+ }
+ } else {
+ if (ldap_add_func == 1)
+ {
+ ldap_current_mod->mod_op = 0;
+ } else {
+ ldap_current_mod->mod_op = LDAP_MOD_REPLACE;
+ }
+ New(1,ldap_current_mod->mod_values,2,char *);
+ ldap_current_mod->mod_values[0] = SvPV(ldap_value_ref,PL_na);
+ ldap_current_mod->mod_values[1] = NULL;
+ }
+ }
+ return(ldap_current_mod);
+}
+
+
+/* hash2mod - Cycle through all the keys in the hash and properly call */
+/* the appropriate functions to build a NULL terminated list of */
+/* LDAPMod pointers. */
+
+static
+LDAPMod ** hash2mod(SV *ldap_change_ref, int ldap_add_func, const char *func)
+{
+ LDAPMod **ldapmod = NULL;
+ LDAPMod *ldap_current_mod;
+ int ldap_attribute_count = 0;
+ HE *ldap_change_element;
+ char *ldap_current_attribute;
+ SV *ldap_current_value_sv;
+ I32 keylen;
+ HV *ldap_change;
+
+ if (!SvROK(ldap_change_ref) || SvTYPE(SvRV(ldap_change_ref)) != SVt_PVHV)
+ croak("Net::LDAPapi::%s needs Hash reference as argument 3.",func);
+
+ ldap_change = (HV *)SvRV(ldap_change_ref);
+
+ hv_iterinit(ldap_change);
+ while((ldap_change_element = hv_iternext(ldap_change)) != NULL)
+ {
+ ldap_current_attribute = hv_iterkey(ldap_change_element,&keylen);
+ ldap_current_value_sv = hv_iterval(ldap_change,ldap_change_element);
+ ldap_current_mod = parse1mod(ldap_current_value_sv,
+ ldap_current_attribute,ldap_add_func,0);
+ while (ldap_current_mod != NULL)
+ {
+ ldap_attribute_count++;
+ (ldapmod
+ ? Renew(ldapmod,1+ldap_attribute_count,LDAPMod *)
+ : New(1,ldapmod,1+ldap_attribute_count,LDAPMod *));
+ New(1,ldapmod[ldap_attribute_count -1],sizeof(LDAPMod),LDAPMod);
+ Copy(ldap_current_mod,ldapmod[ldap_attribute_count-1],
+ sizeof(LDAPMod),LDAPMod *);
+ ldap_current_mod = parse1mod(ldap_current_value_sv,
+ ldap_current_attribute,ldap_add_func,1);
+
+ }
+ }
+ ldapmod[ldap_attribute_count] = NULL;
+ return ldapmod;
+}
+
+/* internal_rebind_proc - Wrapper to call a PERL rebind process */
+/* ldap_set_rebind_proc is slightly different between Mozilla and OpenLDAP */
+
+int
+#ifdef OPENLDAP
+internal_rebind_proc(LDAP *ld, LDAP_CONST char *url,
+ ber_tag_t request, ber_int_t msgid,
+ void *params)
+#endif
+{
+ return(LDAP_SUCCESS);
+}
+
+typedef struct bictx {
+ char *authcid;
+ char *passwd;
+ char *realm;
+ char *authzid;
+} bictx;
+
+static int
+ldap_b2_interact(LDAP *ld, unsigned flags, void *def, void *inter)
+{
+ sasl_interact_t *in = inter;
+ const char *p;
+ bictx *ctx = def;
+ for (;in->id != SASL_CB_LIST_END;in++)
+ {
+ p = NULL;
+ switch(in->id)
+ {
+ case SASL_CB_GETREALM:
+ p = ctx->realm;
+ break;
+ case SASL_CB_AUTHNAME:
+ p = ctx->authcid;
+ break;
+ case SASL_CB_USER:
+ p = ctx->authzid;
+ break;
+ case SASL_CB_PASS:
+ p = ctx->passwd;
+ break;
+ }
+ if (p)
+ {
+ in->len = strlen(p);
+ in->result = p;
+ }
+ }
+ return LDAP_SUCCESS;
+}
+
+
+MODULE = Net::LDAPapi PACKAGE = Net::LDAPapi
+
+PROTOTYPES: ENABLE
+
+double
+constant(name,arg)
+ char * name
+ int arg
+
+
+char *
+constant_s(name)
+ char * name
+
+
+int
+ldap_initialize(ldp, url)
+ LDAP * ldp = NO_INIT
+ LDAP_CHAR * url
+ CODE:
+ {
+ RETVAL = ldap_initialize(&ldp, url);
+ }
+ OUTPUT:
+ RETVAL
+ ldp
+
+int
+ldap_create(ldp)
+ LDAP ** ldp = NO_INIT
+ CODE:
+ {
+ RETVAL = ldap_create(ldp);
+ }
+ OUTPUT:
+ RETVAL
+ ldp
+
+int
+ldap_bind_s(ldp, dn, passwd, authmethod)
+ LDAP * ldp
+ LDAP_CHAR * dn
+ LDAP_CHAR * passwd
+ int authmethod
+
+int
+ldap_set_option(ld,option,optdata)
+ LDAP * ld
+ int option
+ int optdata
+ CODE:
+ {
+ RETVAL = ldap_set_option(ld,option,&optdata);
+ }
+ OUTPUT:
+ RETVAL
+
+int
+ldap_get_option(ld,option,optdata)
+ LDAP * ld
+ int option
+ int optdata = NO_INIT
+ CODE:
+ {
+ RETVAL = ldap_get_option(ld, option, &optdata);
+ }
+ OUTPUT:
+ RETVAL
+ optdata
+
+int
+ldap_unbind_ext_s(ld,sctrls,cctrls)
+ LDAP * ld
+ LDAPControl ** sctrls
+ LDAPControl ** cctrls
+
+int
+ldap_search_s(ldp, base, scope, filter, attrs, attrsonly, res)
+ LDAP * ldp
+ LDAP_CHAR * base
+ int scope
+ LDAP_CHAR * filter
+ LDAP_CHAR ** attrs
+ int attrsonly
+ LDAPMessage * res = NO_INIT
+ CODE:
+ {
+ RETVAL = ldap_search_s(ldp, base, scope, filter, attrs, attrsonly, &res);
+ }
+ OUTPUT:
+ RETVAL
+ res
+
+#ifdef MOZILLA_LDAP
+
+int
+ldap_version(ver)
+ LDAPVersion *ver
+
+#endif
+
+int
+ldap_abandon_ext(ld,msgid,sctrls,cctrls)
+ LDAP * ld
+ int msgid
+ LDAPControl ** sctrls
+ LDAPControl ** cctrls
+
+int
+ldap_add_ext(ld, dn, ldap_change_ref, sctrls, cctrls, msgidp)
+ LDAP * ld
+ LDAP_CHAR * dn
+ SV * ldap_change_ref
+ LDAPControl ** sctrls
+ LDAPControl ** cctrls
+ int msgidp = NO_INIT
+ CODE:
+ {
+ LDAPMod ** attrs = hash2mod(ldap_change_ref, 1, "ldap_add_ext");
+ RETVAL = ldap_add_ext(ld, dn, attrs, sctrls, cctrls, &msgidp);
+ Safefree(attrs);
+ }
+ OUTPUT:
+ RETVAL
+ msgidp
+
+int
+ldap_add_ext_s(ld,dn,ldap_change_ref,sctrls,cctrls)
+ LDAP * ld
+ LDAP_CHAR * dn
+ LDAPMod ** ldap_change_ref = hash2mod($arg, 1, "ldap_add_ext_s");
+ LDAPControl ** sctrls
+ LDAPControl ** cctrls
+ CLEANUP:
+ Safefree(ldap_change_ref);
+
+int
+ldap_sasl_bind(ld, dn, passwd, sctrls, serverctrls, clientctrls, msgidp)
+ LDAP * ld
+ LDAP_CHAR * dn
+ LDAP_CHAR * passwd
+ LDAPControl ** serverctrls
+ LDAPControl ** clientctrls
+ int msgidp = NO_INIT
+ CODE:
+ {
+ struct berval cred;
+
+ if( passwd == NULL )
+ cred.bv_val = "";
+ else
+ cred.bv_val = passwd;
+
+ cred.bv_len = strlen(cred.bv_val);
+
+ RETVAL = ldap_sasl_bind(ld, dn, LDAP_SASL_SIMPLE, &cred,
+ serverctrls, clientctrls, &msgidp);
+ }
+ OUTPUT:
+ RETVAL
+ msgidp
+
+int
+ldap_modify_ext(ld, dn, ldap_change_ref, sctrls, cctrls, msgidp)
+ LDAP * ld
+ LDAP_CHAR * dn
+ SV * ldap_change_ref
+ LDAPControl ** sctrls
+ LDAPControl ** cctrls
+ int msgidp = NO_INIT
+ CODE:
+ {
+ LDAPMod ** mods = hash2mod(ldap_change_ref, 0, "ldap_modify_ext");
+ RETVAL = ldap_modify_ext(ld, dn, mods, sctrls, cctrls, &msgidp);
+ Safefree(mods);
+ }
+ OUTPUT:
+ RETVAL
+ msgidp
+
+int
+ldap_modify_ext_s(ld,dn,ldap_change_ref,sctrl,cctrl)
+ LDAP * ld
+ LDAP_CHAR * dn
+ LDAPMod ** ldap_change_ref = hash2mod($arg, 0, "$func_name");
+ LDAPControl ** sctrl
+ LDAPControl ** cctrl
+
+int
+ldap_rename(ld, dn, newrdn, newSuperior, deleteoldrdn, sctrls, cctrls, msgidp)
+ LDAP * ld
+ LDAP_CHAR * dn
+ LDAP_CHAR * newrdn
+ LDAP_CHAR * newSuperior
+ int deleteoldrdn
+ LDAPControl ** sctrls
+ LDAPControl ** cctrls
+ int msgidp = NO_INIT
+ CODE:
+ {
+ RETVAL = ldap_rename(ld, dn, newrdn, newSuperior,
+ deleteoldrdn, sctrls, cctrls, &msgidp);
+ }
+ OUTPUT:
+ RETVAL
+
+int
+ldap_rename_s(ld, dn, newrdn, newSuperior, deleteoldrdn, sctrls, cctrls)
+ LDAP * ld
+ LDAP_CHAR * dn
+ LDAP_CHAR * newrdn
+ LDAP_CHAR * newSuperior
+ int deleteoldrdn
+ LDAPControl ** sctrls
+ LDAPControl ** cctrls
+
+int
+ldap_compare_ext(ld,dn,attr,value,sctrls,cctrls,msgidp)
+ LDAP * ld
+ LDAP_CHAR * dn
+ LDAP_CHAR * attr
+ LDAP_CHAR * value
+ LDAPControl ** sctrls
+ LDAPControl ** cctrls
+ int msgidp = NO_INIT
+ CODE:
+ {
+ struct berval bvalue;
+ bvalue.bv_len = strlen(value);
+ bvalue.bv_val = value;
+ RETVAL = ldap_compare_ext(ld, dn, attr, &bvalue, sctrls, cctrls, &msgidp);
+ }
+ OUTPUT:
+ RETVAL
+ msgidp
+
+int
+ldap_compare_ext_s(ld, dn, attr, value, sctrls, cctrls)
+ LDAP * ld
+ LDAP_CHAR * dn
+ LDAP_CHAR * attr
+ LDAP_CHAR * value
+ LDAPControl ** sctrls
+ LDAPControl ** cctrls
+ CODE:
+ {
+ struct berval bvalue;
+ bvalue.bv_len = strlen(value);
+ bvalue.bv_val = value;
+ RETVAL = ldap_compare_ext_s(ld, dn, attr, &bvalue, sctrls, cctrls);
+ }
+ OUTPUT:
+ RETVAL
+
+int
+ldap_delete_ext(ld,dn,sctrls,cctrls,msgidp)
+ LDAP * ld
+ LDAP_CHAR * dn
+ LDAPControl ** sctrls
+ LDAPControl ** cctrls
+ int msgidp = NO_INIT
+ CODE:
+ {
+ RETVAL = ldap_delete_ext(ld, dn, sctrls, cctrls, &msgidp);
+ }
+ OUTPUT:
+ RETVAL
+ msgidp
+
+int
+ldap_delete_ext_s(ld,dn,sctrls,cctrls)
+ LDAP * ld
+ LDAP_CHAR * dn
+ LDAPControl ** sctrls
+ LDAPControl ** cctrls
+
+int
+ldap_search_ext(ld, base, scope, filter, attrs, attrsonly, sctrls, cctrls, timeout, sizelimit, msgidp)
+ LDAP * ld
+ LDAP_CHAR * base
+ int scope
+ LDAP_CHAR * filter
+ SV * attrs
+ int attrsonly
+ LDAPControl ** sctrls
+ LDAPControl ** cctrls
+ struct timeval * timeout
+ int sizelimit
+ int msgidp = NO_INIT
+
+ CODE:
+ {
+ char **attrs_char;
+ SV **current;
+ int arraylen,count;
+
+ if (SvTYPE(SvRV(attrs)) != SVt_PVAV)
+ {
+ croak("Net::LDAPapi::ldap_search_ext needs ARRAY reference as argument 5.");
+ XSRETURN(1);
+ }
+
+ if ((arraylen = av_len((AV *)SvRV(attrs))) < 0)
+ {
+ New(1,attrs_char,2,char *);
+ attrs_char[0] = NULL;
+ } else {
+ New(1,attrs_char,arraylen+2,char *);
+ for (count=0;count <= arraylen; count++)
+ {
+ current = av_fetch((AV *)SvRV(attrs),count,0);
+ attrs_char[count] = SvPV(*current,PL_na);
+ }
+ attrs_char[arraylen+1] = NULL;
+ }
+ RETVAL = ldap_search_ext(ld, base, scope, filter, attrs_char,
+ attrsonly, sctrls, cctrls, timeout, sizelimit,
+ &msgidp);
+ Safefree(attrs_char);
+ }
+ OUTPUT:
+ RETVAL
+ msgidp
+
+int
+ldap_search_ext_s(ld, base, scope, filter, attrs, attrsonly, sctrls, cctrls, timeout, sizelimit, res)
+ LDAP * ld
+ LDAP_CHAR * base
+ int scope
+ LDAP_CHAR * filter
+ SV * attrs
+ int attrsonly
+ LDAPControl ** sctrls
+ LDAPControl ** cctrls
+ struct timeval * timeout
+ int sizelimit
+ LDAPMessage * res = NO_INIT
+ CODE:
+ {
+ char **attrs_char;
+ SV **current;
+ int arraylen,count;
+
+ if (SvTYPE(SvRV(attrs)) == SVt_PVAV)
+ {
+ if ((arraylen = av_len((AV *)SvRV(attrs))) < 0)
+ {
+ New(1, attrs_char, 2, char *);
+ attrs_char[0] = NULL;
+ } else {
+ New(1, attrs_char, arraylen+2, char *);
+ for (count=0;count <= arraylen; count++)
+ {
+ current = av_fetch((AV *)SvRV(attrs),count,0);
+ attrs_char[count] = SvPV(*current,PL_na);
+ }
+ attrs_char[arraylen+1] = NULL;
+ }
+ } else {
+ croak("Net::LDAPapi::ldap_search_ext_s needs ARRAY reference as argument 5.");
+ XSRETURN(1);
+ }
+ RETVAL = ldap_search_ext_s(ld,base,scope,filter,attrs_char,attrsonly,sctrls,cctrls,timeout,sizelimit,&res);
+ Safefree(attrs_char);
+ }
+ OUTPUT:
+ RETVAL
+ res
+
+
+int
+ldap_result(ld, msgid, all, timeout, result)
+ LDAP * ld
+ int msgid
+ int all
+ LDAP_CHAR * timeout
+ LDAPMessage * result = NO_INIT
+ CODE:
+ {
+ struct timeval *tv_timeout = NULL, timeoutbuf;
+ if (atof(timeout) > 0 && timeout && *timeout)
+ {
+ tv_timeout = &timeoutbuf;
+ tv_timeout->tv_sec = atof(timeout);
+ tv_timeout->tv_usec = 0;
+ }
+ RETVAL = ldap_result(ld, msgid, all, NULL, &result);
+ }
+ OUTPUT:
+ RETVAL
+ result
+
+
+int
+ldap_msgfree(lm)
+ LDAPMessage * lm
+
+void
+ber_free(ber, freebuf)
+ BerElement * ber
+ int freebuf
+
+#if defined(MOZILLA_LDAP) || defined(OPENLDAP)
+
+int
+ldap_msgid(lm)
+ LDAPMessage * lm
+
+int
+ldap_msgtype(lm)
+ LDAPMessage * lm
+
+#else
+
+int
+ldap_msgid(lm)
+ LDAPMessage * lm
+ CODE:
+ {
+ RETVAL = lm->lm_msgid;
+ }
+ OUTPUT:
+ RETVAL
+
+int
+ldap_msgtype(lm)
+ LDAPMessage * lm
+ CODE:
+ {
+ RETVAL = lm->lm_msgtype;
+ }
+ OUTPUT:
+ RETVAL
+
+#endif
+
+#if defined(MOZILLA_LDAP)
+
+int
+ldap_get_lderrno(ld,m,s)
+ LDAP * ld
+ char * m = NO_INIT
+ char * s = NO_INIT
+ CODE:
+ {
+ RETVAL = ldap_get_lderrno(ld,&m,&s);
+ }
+ OUTPUT:
+ RETVAL
+ m
+ s
+
+int
+ldap_set_lderrno(ld,e,m,s)
+ LDAP * ld
+ int e
+ char * m
+ char * s
+
+#else
+
+int
+ldap_get_lderrno(ld,m,s)
+ LDAP * ld
+ char * m = NO_INIT
+ char * s = NO_INIT
+ CODE:
+ {
+#ifdef OPENLDAP
+ ldap_get_option(ld, LDAP_OPT_ERROR_NUMBER, &RETVAL);
+ ldap_get_option(ld, LDAP_OPT_ERROR_STRING, &s);
+ ldap_get_option(ld, LDAP_OPT_MATCHED_DN, &m);
+#else
+ RETVAL = ld->ld_errno;
+ m = ld->ld_matched;
+ s = ld->ld_error;
+#endif
+ }
+ OUTPUT:
+ RETVAL
+ m
+ s
+
+int
+ldap_set_lderrno(ld,e,m,s)
+ LDAP * ld
+ int e
+ char * m
+ char * s
+ CODE:
+ {
+ RETVAL = 0;
+#ifdef OPENLDAP
+ ldap_set_option(ld, LDAP_OPT_ERROR_NUMBER, &e);
+ ldap_set_option(ld, LDAP_OPT_ERROR_STRING, s);
+ ldap_set_option(ld, LDAP_OPT_MATCHED_DN, m);
+#else
+ ld->ld_errno = e;
+ ld->ld_matched = m;
+ ld->ld_error = s;
+#endif
+ }
+ OUTPUT:
+ RETVAL
+
+#endif
+
+int
+ldap_get_entry_controls(ld, entry, serverctrls_ref)
+ LDAP * ld
+ LDAPMessage * entry
+ SV * serverctrls_ref
+ CODE:
+ {
+ int i;
+
+ if (SvTYPE(SvRV(serverctrls_ref)) != SVt_PVAV)
+ {
+ croak("Net::LDAPapi::ldap_get_entry_controls needs ARRAY reference as argument 3.");
+ XSRETURN(-1);
+ }
+
+ AV *serverctrls_av = (AV *)SvRV(serverctrls_ref);
+
+ LDAPControl **serverctrls = malloc(sizeof(LDAPControl **));
+ if( serverctrls == NULL ) {
+ croak("In ldap_parse_result(...) failed to allocate memory for serverctrls.");
+ XSRETURN(-1);
+ }
+
+ RETVAL = ldap_get_entry_controls( ld, entry, &serverctrls);
+
+ // transfer returned controls to the perl code
+ if( serverctrls != NULL ) {
+ for( i = 0; serverctrls[i] != NULL; i++ )
+ av_push(serverctrls_av, newSViv((IV)serverctrls[i]));
+ }
+
+ free(serverctrls);
+
+ SvRV( serverctrls_ref ) = (SV *)serverctrls_av;
+ }
+ OUTPUT:
+ RETVAL
+
+int
+ldap_parse_result(ld, msg, errorcodep, matcheddnp, errmsgp, referrals_ref, serverctrls_ref, freeit)
+ LDAP * ld
+ LDAPMessage * msg
+ int errorcodep = NO_INIT
+ char * matcheddnp = NO_INIT
+ char * errmsgp = NO_INIT
+ SV * referrals_ref
+ SV * serverctrls_ref
+ int freeit
+ CODE:
+ {
+ int i;
+
+ if (SvTYPE(SvRV(referrals_ref)) != SVt_PVAV)
+ {
+ croak("Net::LDAPapi::ldap_parse_result needs ARRAY reference as argument 6.");
+ XSRETURN(-1);
+ }
+
+ if (SvTYPE(SvRV(serverctrls_ref)) != SVt_PVAV)
+ {
+ croak("Net::LDAPapi::ldap_parse_result needs ARRAY reference as argument 7.");
+ XSRETURN(-1);
+ }
+
+ AV *serverctrls_av = (AV *)SvRV(serverctrls_ref);
+ AV *referrals_av = (AV *)SvRV(referrals_ref);
+
+ LDAPControl **serverctrls = malloc(sizeof(LDAPControl **));
+ if( serverctrls == NULL ) {
+ croak("In ldap_parse_result(...) failed to allocate memory for serverctrls.");
+ XSRETURN(-1);
+ }
+
+ char **referrals = malloc(sizeof(char **));
+ if( referrals == NULL ) {
+ croak("In ldap_parse_result(...) failed to allocate memory for referrals.");
+ free(referrals);
+ XSRETURN(-1);
+ }
+
+ RETVAL =
+ ldap_parse_result(ld, msg, &errorcodep, &matcheddnp,
+ &errmsgp, &referrals, &serverctrls, freeit);
+
+ // transfer returned referrals to the perl code
+ if( referrals != NULL ) {
+ for( i = 0; referrals[i] != NULL; i++ )
+ av_push(referrals_av, newSViv((IV)referrals[i]));
+ }
+
+ // transfer returned controls to the perl code
+ if( serverctrls != NULL ) {
+ for( i = 0; serverctrls[i] != NULL; i++ )
+ av_push(serverctrls_av, newSViv((IV)serverctrls[i]));
+ }
+
+ free(serverctrls);
+ free(referrals);
+
+ SvRV( referrals_ref ) = (SV *)referrals_av;
+ SvRV( serverctrls_ref ) = (SV *)serverctrls_av;
+ }
+ OUTPUT:
+ RETVAL
+ errorcodep
+ matcheddnp
+ errmsgp
+
+int
+ldap_parse_intermediate(ld, msg, retoidp, retdatap, serverctrls_ref, freeit)
+ LDAP * ld
+ LDAPMessage * msg
+ char * retoidp = NO_INIT
+ char * retdatap = NO_INIT
+ SV * serverctrls_ref
+ int freeit
+ CODE:
+ {
+ int i;
+ struct berval *retdata;
+
+ if (SvTYPE(SvRV(serverctrls_ref)) != SVt_PVAV)
+ {
+ croak("Net::LDAPapi::ldap_parse_intermediate needs ARRAY reference as argument 5.");
+ XSRETURN(-1);
+ }
+
+ AV *serverctrls_av = (AV *)SvRV(serverctrls_ref);
+
+ LDAPControl **serverctrls = malloc(sizeof(LDAPControl **));
+ if( serverctrls == NULL ) {
+ croak("In ldap_parse_intermediate(...) failed to allocate memory for serverctrls.");
+ XSRETURN(-1);
+ }
+
+ retdata = malloc(sizeof(struct berval *));
+
+ RETVAL =
+ ldap_parse_intermediate(ld, msg, &retoidp,
+ &retdata, &serverctrls, freeit);
+
+ if( retdata != NULL )
+ retdatap = ldap_strdup(retdata->bv_val);
+
+ // transfer returned controls to the perl code
+ if( serverctrls != NULL ) {
+ for( i = 0; serverctrls[i] != NULL; i++ )
+ av_push(serverctrls_av, newSViv((IV)serverctrls[i]));
+ }
+
+ free(serverctrls);
+ free(retdata);
+
+ SvRV( serverctrls_ref ) = (SV *)serverctrls_av;
+ }
+ OUTPUT:
+ RETVAL
+ retoidp
+ retdatap
+
+
+char *
+ldap_control_oid(control)
+ LDAPControl * control
+ CODE:
+ {
+ RETVAL = control->ldctl_oid;
+ }
+ OUTPUT:
+ RETVAL
+
+
+char *
+ldap_control_berval(control)
+ LDAPControl * control
+ CODE:
+ {
+ RETVAL = control->ldctl_value.bv_val;
+ }
+ OUTPUT:
+ RETVAL
+
+
+int
+ldap_control_critical(control)
+ LDAPControl * control
+ CODE:
+ {
+ RETVAL = control->ldctl_iscritical;
+ }
+ OUTPUT:
+ RETVAL
+
+
+char *
+ldap_err2string(err)
+ int err
+
+
+int
+ldap_count_references(ld, result)
+ LDAP *ld
+ LDAPMessage *result
+
+
+int
+ldap_count_entries(ld,result)
+ LDAP * ld
+ LDAPMessage * result
+
+
+LDAPMessage *
+ldap_first_entry(ld,result)
+ LDAP * ld
+ LDAPMessage * result
+
+
+LDAPMessage *
+ldap_next_entry(ld,preventry)
+ LDAP * ld
+ LDAPMessage * preventry
+
+LDAPMessage *
+ldap_first_message(ld, chain)
+ LDAP *ld
+ LDAPMessage *chain
+
+LDAPMessage *
+ldap_next_message(ld, chain)
+ LDAP *ld
+ LDAPMessage *chain
+
+SV *
+ldap_get_dn(ld,entry)
+ LDAP * ld
+ LDAPMessage * entry
+ PREINIT:
+ char * dn;
+ CODE:
+ {
+ dn = ldap_get_dn(ld, entry);
+ if (dn)
+ {
+ RETVAL = newSVpv(dn,0);
+ ldap_memfree(dn);
+ } else {
+ RETVAL = &PL_sv_undef;
+ }
+ }
+ OUTPUT:
+ RETVAL
+
+void
+ldap_perror(ld,s)
+ LDAP * ld
+ LDAP_CHAR * s
+
+char *
+ldap_dn2ufn(dn)
+ LDAP_CHAR * dn
+
+#if defined(OPENLDAP)
+int
+ldap_str2dn(str,dn,flags)
+ LDAP_CHAR * str
+ LDAPDN * dn
+ unsigned flags
+
+int ldap_str2rdn(str,rdn,n_in,flags)
+ LDAP_CHAR * str
+ LDAPRDN * rdn
+ char ** n_in
+ unsigned flags
+
+#endif
+
+void
+ldap_explode_dn(dn,notypes)
+ char * dn
+ int notypes
+ PPCODE:
+ {
+ char ** LDAPGETVAL;
+ int i;
+
+ if ((LDAPGETVAL = ldap_explode_dn(dn,notypes)) != NULL)
+ {
+ for (i = 0; LDAPGETVAL[i] != NULL; i++)
+ {
+ EXTEND(sp,1);
+ PUSHs(sv_2mortal(newSVpv(LDAPGETVAL[i],strlen(LDAPGETVAL[i]))));
+ }
+ ldap_value_free(LDAPGETVAL);
+ }
+ }
+
+void
+ldap_explode_rdn(dn,notypes)
+ char * dn
+ int notypes
+ PPCODE:
+ {
+ char ** LDAPGETVAL;
+ int i;
+
+ if ((LDAPGETVAL = ldap_explode_rdn(dn,notypes)) != NULL)
+ {
+ for (i = 0; LDAPGETVAL[i] != NULL; i++)
+ {
+ EXTEND(sp,1);
+ PUSHs(sv_2mortal(newSVpv(LDAPGETVAL[i],strlen(LDAPGETVAL[i]))));
+ }
+ ldap_value_free(LDAPGETVAL);
+ }
+ }
+
+SV *
+ldap_first_attribute(ld,entry,ber)
+ LDAP * ld
+ LDAPMessage * entry
+ BerElement * ber = NO_INIT
+ PREINIT:
+ char * attr;
+ CODE:
+ {
+ attr = ldap_first_attribute(ld, entry, &ber);
+ if (attr)
+ {
+ RETVAL = newSVpv(attr,0);
+ ldap_memfree(attr);
+ } else {
+ RETVAL = &PL_sv_undef;
+ }
+ }
+ OUTPUT:
+ RETVAL
+ ber
+
+SV *
+ldap_next_attribute(ld,entry,ber)
+ LDAP * ld
+ LDAPMessage * entry
+ BerElement * ber
+ PREINIT:
+ char * attr;
+ CODE:
+ {
+ attr = ldap_next_attribute(ld, entry, ber);
+ if (attr)
+ {
+ RETVAL = newSVpv(attr,0);
+ ldap_memfree(attr);
+ } else {
+ RETVAL = &PL_sv_undef;
+ }
+ }
+ OUTPUT:
+ RETVAL
+ ber
+
+
+void
+ldap_get_values_len(ld,entry,target)
+ LDAP * ld
+ LDAPMessage * entry
+ char * target
+ PPCODE:
+ {
+ struct berval ** LDAPGETVAL;
+ int i;
+
+ if ((LDAPGETVAL = ldap_get_values_len(ld,entry,target)) != NULL)
+ {
+ for (i = 0; LDAPGETVAL[i] != NULL; i++)
+ {
+ EXTEND(sp,1);
+ PUSHs(sv_2mortal(newSVpv(LDAPGETVAL[i]->bv_val,LDAPGETVAL[i]->bv_len)));
+ }
+ }
+ }
+
+#ifdef MOZILLA_LDAP
+
+int
+ldapssl_client_init(certdbpath,certdbhandle)
+ char * certdbpath
+ void * certdbhandle
+
+LDAP *
+ldapssl_init(defhost,defport,defsecure)
+ char * defhost
+ int defport
+ int defsecure
+
+int
+ldapssl_install_routines(ld)
+ LDAP * ld
+
+#endif
+
+void
+ldap_set_rebind_proc(ld,rebind_function,args)
+ LDAP * ld
+ SV * rebind_function
+ void * args
+ CODE:
+ {
+ if (SvTYPE(SvRV(rebind_function)) != SVt_PVCV)
+ {
+ // rebind_function is not actually a function
+ // and we set rebind function to NULL
+#if defined(MOZILLA_LDAP) || defined(OPENLDAP)
+ ldap_set_rebind_proc(ld,NULL,NULL);
+#else
+ ldap_set_rebind_proc(ld,NULL);
+#endif
+ } else {
+ if (ldap_perl_rebindproc == (SV*)NULL)
+ ldap_perl_rebindproc = newSVsv(rebind_function);
+ else
+ SvSetSV(ldap_perl_rebindproc, rebind_function);
+#if defined(OPENLDAP)
+ ldap_set_rebind_proc(ld, internal_rebind_proc, args);
+#endif
+ }
+ }
+
+HV *
+ldap_get_all_entries(ld,result)
+ LDAP * ld
+ LDAPMessage * result
+ CODE:
+ {
+ LDAPMessage *entry = NULL;
+ char *dn = NULL, *attr = NULL;
+ struct berval **vals = NULL;
+ BerElement *ber = NULL;
+ int count = 0;
+ HV* FullHash = newHV();
+
+ for ( entry = ldap_first_entry(ld, result); entry != NULL;
+ entry = ldap_next_entry(ld, entry) )
+ {
+ HV* ResultHash = newHV();
+ SV* HashRef = newRV((SV*) ResultHash);
+
+ if ((dn = ldap_get_dn(ld, entry)) == NULL)
+ continue;
+
+ for ( attr = ldap_first_attribute(ld, entry, &ber);
+ attr != NULL;
+ attr = ldap_next_attribute(ld, entry, ber) )
+ {
+ AV* AttributeValsArray = newAV();
+ SV* ArrayRef = newRV((SV*) AttributeValsArray);
+ if ((vals = ldap_get_values_len(ld, entry, attr)) != NULL)
+ {
+ for (count=0; vals[count] != NULL; count++)
+ {
+ SV* SVval = newSVpvn(vals[count]->bv_val, vals[count]->bv_len);
+ av_push(AttributeValsArray, SVval);
+ }
+ }
+ hv_store(ResultHash, attr, strlen(attr), ArrayRef, 0);
+ if (vals != NULL)
+ ldap_value_free_len(vals);
+ }
+ if (attr != NULL)
+ ldap_memfree(attr);
+ hv_store(FullHash, dn, strlen(dn), HashRef, 0);
+ if (dn != NULL)
+ ldap_memfree(dn);
+#if defined(MOZILLA_LDAP) || defined(OPENLDAP)
+ if (ber != NULL)
+ ber_free(ber,0);
+#endif
+ }
+ RETVAL = FullHash;
+ }
+ OUTPUT:
+ RETVAL
+
+int
+ldap_is_ldap_url(url)
+ LDAP_CHAR * url
+
+SV *
+ldap_url_parse(url)
+ LDAP_CHAR * url
+ CODE:
+ {
+ LDAPURLDesc *realcomp;
+ int count,ret;
+
+ HV* FullHash = newHV();
+ RETVAL = newRV((SV*)FullHash);
+
+ ret = ldap_url_parse(url,&realcomp);
+ if (ret == 0)
+ {
+ static char *host_key = "host";
+ static char *port_key = "port";
+ static char *dn_key = "dn";
+ static char *attr_key = "attr";
+ static char *scope_key = "scope";
+ static char *filter_key = "filter";
+#ifdef MOZILLA_LDAP
+ static char *options_key = "options";
+ SV* options = newSViv(realcomp->lud_options);
+#endif
+#ifdef OPENLDAP
+ static char *scheme_key = "scheme";
+ static char *exts_key = "exts";
+ AV* extsarray = newAV();
+ SV* extsibref = newRV((SV*) extsarray);
+ SV* scheme = newSVpv(realcomp->lud_scheme,0);
+#endif
+ SV* host = newSVpv(realcomp->lud_host,0);
+ SV* port = newSViv(realcomp->lud_port);
+ SV* dn; /* = newSVpv(realcomp->lud_dn,0); */
+ SV* scope = newSViv(realcomp->lud_scope);
+ SV* filter = newSVpv(realcomp->lud_filter,0);
+ AV* attrarray = newAV();
+ SV* attribref = newRV((SV*) attrarray);
+
+ if (realcomp->lud_dn)
+ dn = newSVpv(realcomp->lud_dn,0);
+ else
+ dn = newSVpv("",0);
+
+ if (realcomp->lud_attrs != NULL)
+ {
+ for (count=0; realcomp->lud_attrs[count] != NULL; count++)
+ {
+ SV* SVval = newSVpv(realcomp->lud_attrs[count],0);
+ av_push(attrarray, SVval);
+ }
+ }
+#ifdef OPENLDAP
+ if (realcomp->lud_exts != NULL)
+ {
+ for (count=0; realcomp->lud_exts[count] != NULL; count++)
+ {
+ SV* SVval = newSVpv(realcomp->lud_exts[count],0);
+ av_push(extsarray, SVval);
+ }
+ }
+ hv_store(FullHash,exts_key,strlen(exts_key),extsibref,0);
+ hv_store(FullHash,scheme_key,strlen(scheme_key),scheme,0);
+#endif
+ hv_store(FullHash,host_key,strlen(host_key),host,0);
+ hv_store(FullHash,port_key,strlen(port_key),port,0);
+ hv_store(FullHash,dn_key,strlen(dn_key),dn,0);
+ hv_store(FullHash,attr_key,strlen(attr_key),attribref,0);
+ hv_store(FullHash,scope_key,strlen(scope_key),scope,0);
+ hv_store(FullHash,filter_key,strlen(filter_key),filter,0);
+#ifdef MOZILLA_LDAP
+ hv_store(FullHash,options_key,strlen(options_key),options,0);
+#endif
+ ldap_free_urldesc(realcomp);
+ } else {
+ RETVAL = &PL_sv_undef;
+ }
+ }
+ OUTPUT:
+ RETVAL
+
+#ifndef OPENLDAP
+
+int
+ldap_url_search(ld,url,attrsonly)
+ LDAP * ld
+ char * url
+ int attrsonly
+
+int
+ldap_url_search_s(ld,url,attrsonly,result)
+ LDAP * ld
+ char * url
+ int attrsonly
+ LDAPMessage * result = NO_INIT
+ CODE:
+ {
+ RETVAL = ldap_url_search_s(ld,url,attrsonly,&result);
+ }
+ OUTPUT:
+ RETVAL
+ result
+
+int
+ldap_url_search_st(ld,url,attrsonly,timeout,result)
+ LDAP * ld
+ char * url
+ int attrsonly
+ LDAP_CHAR * timeout
+ LDAPMessage * result = NO_INIT
+ CODE:
+ {
+ struct timeval *tv_timeout = NULL, timeoutbuf;
+ if (timeout && *timeout)
+ {
+ tv_timeout = &timeoutbuf;
+ tv_timeout->tv_sec = atof(timeout);
+ tv_timeout->tv_usec = 0;
+ }
+ RETVAL = ldap_url_search_st(ld,url,attrsonly,tv_timeout,&result);
+ }
+ OUTPUT:
+ RETVAL
+ result
+
+#endif
+
+int
+ldap_sort_entries(ld,chain,attr)
+ LDAP * ld
+ LDAPMessage * chain
+ char * attr
+ CODE:
+ {
+ RETVAL = ldap_sort_entries(ld,&chain,attr,StrCaseCmp);
+ }
+ OUTPUT:
+ RETVAL
+ chain
+
+#ifdef MOZILLA_LDAP
+
+int
+ldap_multisort_entries(ld,chain,attrs)
+ LDAP * ld
+ LDAPMessage * chain
+ SV * attrs
+ CODE:
+ {
+ char **attrs_char;
+ SV ** current;
+ int count,arraylen;
+ if (SvTYPE(SvRV(attrs)) == SVt_PVAV)
+ {
+ if ((arraylen = av_len((AV *)SvRV(attrs))) < 0)
+ {
+ New(1,attrs_char,2,char *);
+ attrs_char[0] = NULL;
+ } else {
+ New(1,attrs_char,arraylen+2,char *);
+ for (count=0;count <= arraylen; count++)
+ {
+ current = av_fetch((AV *)SvRV(attrs),count,0);
+ attrs_char[count] = SvPV(*current,PL_na);
+ }
+ attrs_char[arraylen+1] = NULL;
+ }
+ } else {
+ croak("Net::LDAPapi::ldap_multisort_entries needs ARRAY reference as argument 3.");
+ XSRETURN(1);
+ }
+ RETVAL = ldap_multisort_entries(ld,&chain,attrs_char,StrCaseCmp);
+ }
+ OUTPUT:
+ RETVAL
+ chain
+
+#endif
+
+#ifdef OPENLDAP
+
+int
+ldap_start_tls(ld,serverctrls,clientctrls,msgidp)
+ LDAP * ld
+ LDAPControl ** serverctrls
+ LDAPControl ** clientctrls
+ int msgidp = NO_INIT
+ CODE:
+ {
+ RETVAL = ldap_start_tls(ld, serverctrls, clientctrls, &msgidp);
+ }
+ OUTPUT:
+ RETVAL
+ msgidp
+
+int
+ldap_start_tls_s(ld,serverctrls,clientctrls)
+ LDAP * ld
+ LDAPControl ** serverctrls
+ LDAPControl ** clientctrls
+
+
+int
+ldap_sasl_interactive_bind_s(ld, who, passwd, serverctrls, clientctrls, mech, realm, authzid, props, flags)
+ LDAP * ld
+ LDAP_CHAR * who
+ LDAP_CHAR * passwd
+ LDAPControl ** serverctrls
+ LDAPControl ** clientctrls
+ LDAP_CHAR * mech
+ LDAP_CHAR * realm
+ LDAP_CHAR * authzid
+ LDAP_CHAR * props
+ unsigned flags
+ CODE:
+ {
+ bictx ctx = {who, passwd, realm, authzid};
+ if (props)
+ ldap_set_option(ld, LDAP_OPT_X_SASL_SECPROPS, props);
+ RETVAL = ldap_sasl_interactive_bind_s( ld, NULL, mech, serverctrls, clientctrls,
+ flags, ldap_b2_interact, &ctx );
+ }
+ OUTPUT:
+ RETVAL
+
+int
+ldap_sasl_bind_s(ld, dn, passwd, serverctrls, clientctrls, servercredp)
+ LDAP * ld
+ LDAP_CHAR * dn
+ LDAP_CHAR * passwd
+ LDAPControl ** serverctrls
+ LDAPControl ** clientctrls
+ struct berval ** servercredp = NO_INIT
+ CODE:
+ {
+ struct berval cred;
+
+ if( passwd == NULL )
+ cred.bv_val = "";
+ else
+ cred.bv_val = passwd;
+
+ cred.bv_len = strlen(cred.bv_val);
+
+ servercredp = 0; /* mdw 20070918 */
+ RETVAL = ldap_sasl_bind_s(ld, dn, LDAP_SASL_SIMPLE, &cred,
+ serverctrls, clientctrls, servercredp);
+ }
+ OUTPUT:
+ RETVAL
+ servercredp
+
+#endif
+
+LDAPControl **
+ldap_controls_array_init(total)
+ int total
+ CODE:
+ {
+ LDAPControl ** array;
+ array = malloc(total * sizeof(LDAPControl *));
+ RETVAL = array;
+ }
+ OUTPUT:
+ RETVAL
+
+void
+ldap_controls_array_free(ctrls)
+ LDAPControl ** ctrls
+ CODE:
+ {
+ //int i;
+ //for( i = 0; ctrls[i] != NULL; i++ )
+ // free((LDAPControl *)ctrls[i]);
+
+ free(ctrls);
+ }
+
+
+void
+ldap_control_set(array, ctrl, location)
+ LDAPControl **array
+ LDAPControl *ctrl
+ int location
+ CODE:
+ {
+ array[location] = ctrl;
+ }
+
+int
+ldap_create_control(oid, bv_val, bv_len, iscritical, ctrlp)
+ LDAP_CHAR * oid
+ LDAP_CHAR * bv_val
+ int bv_len
+ int iscritical
+ LDAPControl * ctrlp = NO_INIT
+ CODE:
+ {
+ LDAPControl *ctrl = malloc(sizeof(LDAPControl));
+
+ ctrl->ldctl_oid = ber_strdup(oid);
+ ctrl->ldctl_value.bv_val = ber_strdup(bv_val);
+ ctrl->ldctl_value.bv_len = bv_len;
+ ctrl->ldctl_iscritical = iscritical;
+
+ ctrlp = ctrl;
+
+ RETVAL = 0;
+ }
+ OUTPUT:
+ RETVAL
+ ctrlp
+
+void
+ldap_control_free (ctrl)
+ LDAPControl *ctrl
+
+BerElement *
+ber_alloc_t(options);
+ int options
diff --git a/MANIFEST b/MANIFEST
new file mode 100644
index 0000000..4addb2c
--- /dev/null
+++ b/MANIFEST
@@ -0,0 +1,20 @@
+Changes
+Credits
+LDAPapi.pm
+LDAPapi.xs
+MANIFEST
+Makefile.PL
+README
+Todo
+constant.gen
+ldap_compat.h
+test.pl
+typemap
+examples/ldap_mod_attr.pl
+examples/ldapwalk.pl
+examples/ldapwalk2.pl
+examples/testwrite.pl
+examples/updatepw.pl
+examples/web500.pl
+examples/www-ldap.pl
+META.yml Module meta-data (added by MakeMaker)
diff --git a/META.yml b/META.yml
new file mode 100644
index 0000000..6526d4b
--- /dev/null
+++ b/META.yml
@@ -0,0 +1,11 @@
+# http://module-build.sourceforge.net/META-spec.html
+#XXXXXXX This is a prototype!!! It will change in the future!!! XXXXX#
+name: Net-LDAPapi
+version: 3.0.3
+version_from: LDAPapi.pm
+installdirs: site
+requires:
+ Convert::ASN1: 0.19
+
+distribution_type: module
+generated_by: ExtUtils::MakeMaker version 6.17
diff --git a/Makefile.PL b/Makefile.PL
new file mode 100644
index 0000000..fa36724
--- /dev/null
+++ b/Makefile.PL
@@ -0,0 +1,131 @@
+use ExtUtils::MakeMaker qw(prompt WriteMakefile);
+use Config;
+
+print "\n\nNet::LDAPapi Perl5 Module - by Quanah Gibson-Mount <mishikal\@yahoo.com>\n\n";
+print "OpenLDAP support by Symas Corporation -- http://www.symas.com\n";
+print "Updated by Quanah Gibson-Mount to match modern products and vendors.\n";
+print "Updated by Dmitri Priimak to use the v3 OpenLDAP API.\n";
+print "\nOriginally by Clayton Donley <donley\@cig.mcel.mot.com>\n\n";
+
+$pl_path = $Config{'perlpath'};
+
+unless (@ARGV) {
+warn <<END;
+NOTICE: This module requires the OpenLDAP C API or Mozilla C SDK.
+ It will NOT work with ISODE or the UMich LDAP api.
+
+ Type perl Makefile.PL -h for command-line option summary.
+END
+}
+
+#############################################################################################
+# Build options passed in to script to support reproducible builds via Makefiles
+#############################################################################################
+use Getopt::Long;
+my $result = GetOptions("sdk=s" => \$sdk,
+ "lib_path=s" => \$lib_ldap,
+ "include_path=s" => \$include_ldap,
+ "sasl_include_path=s" => \$include_sasl,
+ );
+unless ($result) {
+ print STDERR <<END;
+Usage: perl Makefile.PL [options]
+
+Configure Net::LDAPapi module.
+
+ Options:
+ -sdk SDK which SDK to use(openldap or mozilla)
+ -lib_path path path to the LDAP libraries
+ -include_path path path to the LDAP includes
+ -sasl_include_path path path to the SASL includes (optional)
+
+
+If no options are passed on the command line will prompt for these
+values interactively.
+END
+}
+
+if ($sdk eq "mozilla" || $sdk eq "MOZILLA") {
+ $sdk = 2;
+}
+
+unless ($sdk)
+{
+ print "\nSelect your Development Kit:\n";
+ print " 1. OpenLDAP (default)\n";
+ print " 2. Mozilla\n";
+ $sdk=prompt("Choose:",1);
+}
+
+$version = ($sdk == 2 ? 'MOZILLA' :
+ 'OPENLDAP');
+
+if ($include_ldap eq "")
+{
+ $include_ldap=prompt("Location of LDAP include files:","/usr/include");
+ chomp($include_ldap);
+ $include_ldap = "/usr/include" unless $include_ldap =~ /\S/;
+}
+
+if ($lib_ldap eq "")
+{
+ $df_lib = ($include_ldap =~ m%^(\S+)/include% ? "$1/lib" : "/usr/lib");
+ $lib_ldap=prompt("Location of LDAP library files:",$df_lib);
+ chomp($lib_ldap);
+ $lib_ldap = $df_lib unless $lib_ldap =~ /\S/;
+}
+
+if ( $sdk == 1 && $include_sasl eq "") {
+ $include_sasl=prompt("Location of SASL include files if needed:");
+ chomp($include_sasl);
+}
+
+if ($version eq "MOZILLA")
+{
+ if ($Config::Config{'osname'} eq 'MSWin32')
+ {
+ $ldap_lib = 'nsldapssl32v30';
+ } else {
+ $ldap_lib = 'ldapssl30';
+ }
+}
+
+if ($version eq "OPENLDAP")
+{
+ if ($^O eq "MSWin32")
+ {
+ $ldap_lib = 'ldap.lib lber.lib';
+ }
+ else
+ {
+ $ldap_lib = '-lldap -llber';
+ }
+}
+
+sub MY::postamble
+{
+ "
+constant.h: constant.gen
+ $pl_path constant.gen >constant.h
+";
+}
+
+WriteMakefile(
+ 'NAME' => 'Net::LDAPapi',
+ 'VERSION_FROM' => 'LDAPapi.pm',
+ 'PREREQ_PM' => { 'Convert::ASN1' => '0.19'},
+ ($include_sasl ne "" ? (
+ 'INC' => "-I$include_ldap -I$include_sasl -I/usr/include",
+ ) : (
+ 'INC' => "-I$include_ldap -I/usr/include",
+ )),
+ ($version eq "MOZILLA" ? (
+ 'LIBS' => ["-L$lib_ldap -l$ldap_lib"],
+ 'DEFINE' => '-DMOZILLA_LDAP',
+ ) : (
+ 'LIBS' => ["-L$lib_ldap $ldap_lib"],
+ 'DEFINE' => '-DOPENLDAP',
+ )),
+ 'depend' => { 'LDAPapi.c' => 'constant.h' },
+ 'clean' => { 'FILES' => 'constant.h' },
+);
diff --git a/README b/README
new file mode 100644
index 0000000..f644df4
--- /dev/null
+++ b/README
@@ -0,0 +1,222 @@
+=======================================================================
+ Net::LDAPapi Module v3.0.x for Perl5
+ by Quanah Gibson-Mount @ Zimbra, Inc.
+ based on version 2.00 by Quanah Gibson-Mount @ Stanford University
+ based on version 1.50 by Howard Chu @ Symas Corporation
+ based on version 1.43 by Clayton Donley, <donley@wwa.com>
+=======================================================================
+
+COPYRIGHT
+=========
+
+ Copyright (c) 2007 Quanah Gibson-Mount. All rights reserved
+ Copyright (c) 2007 Board of Trustees, Leland Stanford Jr. University
+ Copyright (c) 2003 Howard Chu. All rights reserved.
+ Copyright (c) 1998 Clayton Donley. All rights reserved.
+ This program is free software; you can redistribute it and/or
+ modify it under the same terms as Perl itself.
+
+
+INTRODUCTION
+============
+
+This module acts as a Perl5 interface to the LDAP C Development Kits from
+OpenLDAP and Mozilla. The module itself is written completely
+in C. Full documentation is included, as are commented example scripts.
+The current version supports the LDAPv3 API as implemented in OpenLDAP.
+
+REQUIREMENTS
+============
+
+This module requires Perl5, a C compiler, and the LDAP libraries and include
+files from one of the following:
+
+OpenLDAP C SDK (support added by Symas):
+ http://www.openldap.org
+Mozilla LDAP C SDK
+ http://wiki.mozilla.org/LDAP_C_SDK
+
+A C Compiler is not required for the NT version if you download Perl5
+and module binaries and the Mozilla SDK (you must copy the DLL into your
+system directory).
+
+
+PLATFORMS SUPPORTED
+===================
+
+This version should be easily compiled on various platforms. It has been tested
+extensively under Solaris and Linux.
+
+As of 3.00, the module has not been tested on Windows.
+
+Please let me know if you compile it successfully on a new platform or have
+any problems. For problems, please include the compilation errors.
+
+Support for the Mozilla SDK has not been tested with the v3 rewrites. Testing
+welcomed.
+
+
+NEW PERL-OO INTERFACE
+=====================
+
+As of version 1.40, a new Perl-OO layer has been added to allow more
+object oriented access into the API. All of the original commands are
+unchanged, but are no longer documented. You are encouraged to use the
+new OO style routines, which are documented in the man-page.
+
+Version 1.42 and above support passing named arguments to all methods.
+
+
+UNIX INSTALLATION
+=================
+
+ After extracting the archive, simply type the following:
+
+ $ perl5 Makefile.PL
+ $ make
+
+ Edit the file test.pl to contain your LDAP server, BASEDN, and filter
+string. Next type:
+
+ $ make test
+
+ It should complete all of its tests with no problems. To install the
+module you will likely need to become root and type:
+
+ # make install
+
+ That's it. You can now 'use Net::LDAPapi' and make LDAP calls within your
+PERL5 scripts with ease.
+
+NT SOURCE INSTALLATION
+======================
+
+ I use Microsoft Visual C++ 5.0. I am not certain what procedures you will
+need to follow with other compilers.
+
+ C:\> perl Makefile.PL
+
+ Answer the questions appropriately. Supply the path to PERL.EXE and the
+Mozilla library and include files from their SDK.
+
+ C:\> nmake
+
+ The module will now be built.
+
+ c:\> edit test.pl
+
+ Change the LDAP server name and other attributes as necessary.
+
+ C:\> nmake test
+
+ If it passes most of the tests, it probably works, so you'll want to
+install. For this, just type:
+
+ C:\> nmake install
+
+ You can now do a 'use Net::LDAPapi' from your NT Perl modules. You will
+also want be be sure to copy the nsldap32v11.dll file into your system32
+folder.
+
+
+NT BINARY INSTALLATION
+======================
+
+ First, obtain Perl 5.004 binaries for Windows NT. You can get it at the
+following URL: http://www.perl.com/CPAN-local/authors/id/GSAR/. Simply get
+one of the files starting with 'perl5.004'.
+
+ You will also need the Mozilla LDAP SDK mentioned previously. Copy the
+nsldap32v11.dll file from that archive into your system32 folder.
+
+ Finally, unzip the archive into the C:\ directory if you installed
+PERL in C:\PERL. Otherwise, extract to the parent directory of your PERL
+installation directory.
+
+
+EXAMPLES
+========
+
+ There exists six examples in the 'examples' directory. The current
+examples are:
+
+ o ldapwalk.pl - This script demonstrates the use of Asynchronous LDAP
+ calls to return all the attributes and values for entries matching
+ a filter specified on the command line. Simply change the variables
+ at the start of the file to match your environment.
+
+ o ldapwalk2.pl - This is the same as ldapwalk.pl, only it shows how to
+ replace the portion that retrieves results and packages them in
+ the hash of hashes with the new get_all_entries function.
+
+ o testwrite.pl - This script demonstrates using synchronous Add, Modify,
+ and Delete calls to manage an LDAP server. You need update access
+ to an LDAP server to run this example. Once again, simply change
+ the variables at the top of the file.
+
+ o www-ldap.pl - This script uses both this module and the CGI.pm
+ module available on the Internet (and included in 5.004). It
+ allows users to authenticate and modify their own information.
+ You can easily customize this program, or learn from it and
+ write your own CGIs. The BIND and WEB_AUTHENTICATE subroutines
+ would be especially useful in incorporating LDAP authentication
+ into your own CGI scripts, even if the script has nothing to
+ do with LDAP otherwise.
+
+ o web500.pl - This script is a complete Web->LDAP gateway. It uses
+ CGI.pm and this module. It can be easily customized, supports
+ searching, modifications, and even things like jpegphoto uploads
+ and displaying. The original design used frames, but switched
+ to a WebPH style interface for the search part on the advice of
+ Douglas Gray Stephens.
+
+ o updatepw.pl - Updates a password entry for a user from Unix into
+ the LDAP server. You'll likely have to tinker with this program
+ to get it to do exactly what you want, but it exists as an idea
+ as to how you can do this.
+
+ o ldap_mod_attr.pl - Matches a filter or UID and makes specified changes
+ to each entry. Contributed by Andrew J Cosgriff.
+
+ In addition, the test.pl program in the top level directory shows some
+of the basic synchronous search functionality.
+
+
+LDAP C API SUPPORT
+==================
+
+ This module supports direct perl access to all C API calls with the
+exception of filter generation calls (since you can do this better in Perl
+anyway).
+
+ While direct access is available, it is suggested that you use the
+Perl-OO style interface if you have never used the C API.
+
+
+FEEDBACK
+========
+ Any feedback should be directed to mishikal@yahoo.com
+
+BUGS
+====
+
+The non-OO stuff should work well. Please let me know if I've introduced
+any bugs in the OO stuff or the changed examples.
+
+--
+Clayton Donley
+Rolling Meadows, IL, USA
+email: donley@wwa.com
+web: http://www.wwa.com/~donley
+CPAN: /authors/id/CDONLEY
+
+Howard Chu
+Chief Architect, Symas Corporation http://www.symas.com
+Core Team, OpenLDAP Project http://www.openldap.org
+
+Quanah Gibson-Mount
+email: mishikal@yahoo.com
+CPAN: /by-authors/id/M/MI/MISHIKAL
+Principal Software Engineer
+Zimbra, Inc http://www.zimbra.com
+Core Team, OpenLDAP Project http://www.openldap.org
diff --git a/Todo b/Todo
new file mode 100644
index 0000000..3e0e119
--- /dev/null
+++ b/Todo
@@ -0,0 +1,15 @@
+#############################################
+# Net::LDAPapi - TODO List #
+# #
+# Last Modified #
+# Date: Wed Aug 20 15:27:19 PST 2007 #
+#############################################
+
+Platforms:
+- Test on windows
+
+LDAPv3 API Support:
+- Test against Mozilla C SDK
+
+Package:
+- Update examples to be compliant with version 3.0 builds
diff --git a/constant.gen b/constant.gen
new file mode 100644
index 0000000..dafb0e7
--- /dev/null
+++ b/constant.gen
@@ -0,0 +1,379 @@
+#!/usr/misc/bin/perl5 -w
+#
+# constants.gen - h.b.furuseth@usit.uio.no
+# some OpenLDAP constants added by hyc@symas.com
+# some OpenLDAP constants added by priimak@stanford.edu
+
+sub SYM
+{
+ my($name,$ret) = @_;
+ $ret = $name unless defined $ret;
+
+ return qq(if (strEQ(name, "$name"))
+#ifdef $name
+ return $ret;
+#else
+ goto not_there;
+#endif);
+}
+
+while (<DATA>)
+{
+ s%\b(SYM)\(([^()]*)\)%&SYM(split(/,\s*/,$2))%ge;
+ print;
+}
+__END__
+
+/* This file is generated from constants.gen. Changes here will be lost! */
+
+#ifndef LDAP_CONTROL_SYNC
+#define LDAP_CONTROL_SYNC "1.3.6.1.4.1.4203.1.9.1.1"
+#endif
+
+#ifndef LDAP_CONTROL_SYNC_STATE
+#define LDAP_CONTROL_SYNC_STATE "1.3.6.1.4.1.4203.1.9.1.2"
+#endif
+
+#ifndef LDAP_CONTROL_SYNC_DONE
+#define LDAP_CONTROL_SYNC_DONE "1.3.6.1.4.1.4203.1.9.1.3"
+#endif
+
+#ifndef LDAP_SYNC_INFO
+#define LDAP_SYNC_INFO "1.3.6.1.4.1.4203.1.9.1.2"
+#endif
+
+#ifndef LDAP_RES_INTERMEDIATE
+#define LDAP_RES_INTERMEDIATE ((ber_tag_t) 0x79U) /* V3+: application + constructed */
+#endif
+
+static char *
+constant_s(name)
+char *name;
+{
+ errno = 0;
+
+ if (name[0] == 'L' &&
+ name[1] == 'D' &&
+ name[2] == 'A' &&
+ name[3] == 'P' &&
+ name[4] == '_' )
+ switch (name[5])
+ {
+ case 'S':
+ SYM(LDAP_SASL_NULL)
+ SYM(LDAP_SASL_SIMPLE)
+ SYM(LDAP_SYNC_INFO)
+ break;
+
+ case 'C':
+ SYM(LDAP_CONTROL_MANAGEDSAIT)
+ SYM(LDAP_CONTROL_PROXY_AUTHZ)
+ SYM(LDAP_CONTROL_SUBENTRIES)
+ SYM(LDAP_CONTROL_VALUESRETURNFILTER)
+ SYM(LDAP_CONTROL_X_VALUESRETURNFILTER)
+ SYM(LDAP_CONTROL_ASSERT)
+ SYM(LDAP_CONTROL_PRE_READ)
+ SYM(LDAP_CONTROL_POST_READ)
+ SYM(LDAP_CONTROL_SORTREQUEST)
+ SYM(LDAP_CONTROL_SORTRESPONSE)
+ SYM(LDAP_CONTROL_PAGEDRESULTS)
+ SYM(LDAP_CONTROL_PASSWORDPOLICYREQUEST)
+ SYM(LDAP_CONTROL_PASSWORDPOLICYRESPONSE)
+ SYM(LDAP_CONTROL_NOOP)
+ SYM(LDAP_CONTROL_NO_SUBORDINATES)
+ SYM(LDAP_CONTROL_MANAGEDIT)
+ SYM(LDAP_CONTROL_SLURP)
+ SYM(LDAP_CONTROL_VALSORT)
+ SYM(LDAP_CONTROL_SYNC)
+ SYM(LDAP_CONTROL_SYNC_STATE)
+ SYM(LDAP_CONTROL_SYNC_DONE)
+ SYM(LDAP_CONTROL_X_CHAINING_BEHAVIOR)
+ SYM(LDAP_CONTROL_X_INCREMENTAL_VALUES)
+ SYM(LDAP_CONTROL_X_DOMAIN_SCOPE)
+ SYM(LDAP_CONTROL_X_PERMISSIVE_MODIFY)
+ SYM(LDAP_CONTROL_X_SEARCH_OPTIONS)
+ SYM(LDAP_CONTROL_X_TREE_DELETE)
+ SYM(LDAP_CONTROL_X_EXTENDED_DN)
+ SYM(LDAP_CONTROL_DUPENT_REQUEST)
+ SYM(LDAP_CONTROL_DUPENT_RESPONSE)
+ SYM(LDAP_CONTROL_DUPENT_ENTRY)
+ SYM(LDAP_CONTROL_DUPENT)
+ SYM(LDAP_CONTROL_PERSIST_REQUEST)
+ SYM(LDAP_CONTROL_PERSIST_ENTRY_CHANGE_NOTICE)
+ SYM(LDAP_CONTROL_VLVREQUEST)
+ SYM(LDAP_CONTROL_VLVRESPONSE)
+ SYM(LDAP_CONTROL_GROUPING)
+ break;
+ }
+ errno = EINVAL;
+ return NULL;
+
+not_there:
+ errno = ENOENT;
+ return NULL;
+
+}
+
+static double
+constant(name, arg)
+char *name;
+int arg;
+{
+ errno = 0;
+
+ if (name[0] != 'L')
+ {
+ SYM(FD_SETSIZE)
+ SYM(NBBY)
+ SYM(NFDBITS)
+ }
+ else if (name[1] && name[2] && name[3] && name[4]) switch (name[5])
+ {
+ case 'A':
+ SYM(LDAP_ADMIN_LIMIT_EXCEEDED)
+ SYM(LDAP_AFFECTS_MULTIPLE_DSAS)
+ SYM(LDAP_ALIAS_DEREF_PROBLEM)
+ SYM(LDAP_ALIAS_PROBLEM)
+ SYM(LDAP_ALREADY_EXISTS)
+ SYM(LDAP_AUTH_KRBV4)
+ SYM(LDAP_AUTH_KRBV41)
+ SYM(LDAP_AUTH_KRBV42)
+ SYM(LDAP_AUTH_KRBV41_30)
+ SYM(LDAP_AUTH_KRBV42_30)
+ SYM(LDAP_AUTH_NONE)
+ SYM(LDAP_AUTH_SASL)
+ SYM(LDAP_AUTH_SIMPLE)
+ SYM(LDAP_AUTH_UNKNOWN)
+ break;
+
+ case 'B':
+ SYM(LDAP_BUSY)
+ break;
+
+ case 'C':
+ SYM(LDAP_CACHE_CHECK)
+ SYM(LDAP_CACHE_LOCALDB)
+ SYM(LDAP_CACHE_POPULATE)
+ SYM(LDAP_COMPARE_FALSE)
+ SYM(LDAP_COMPARE_TRUE)
+ SYM(LDAP_CONNECT_ERROR)
+ SYM(LDAP_CONSTRAINT_VIOLATION)
+ break;
+
+ case 'D':
+ SYM(LDAP_DECODING_ERROR)
+ SYM(LDAP_DEREF_ALWAYS)
+ SYM(LDAP_DEREF_FINDING)
+ SYM(LDAP_DEREF_NEVER)
+ SYM(LDAP_DEREF_SEARCHING)
+ break;
+
+ case 'E':
+ SYM(LDAP_ENCODING_ERROR)
+ break;
+
+ case 'F':
+ SYM(LDAP_FILTER_ERROR)
+ SYM(LDAP_FILT_MAXSIZ)
+ break;
+
+ case 'I':
+ SYM(LDAP_INAPPROPRIATE_AUTH)
+ SYM(LDAP_INAPPROPRIATE_MATCHING)
+ SYM(LDAP_INSUFFICIENT_ACCESS)
+ SYM(LDAP_INVALID_CREDENTIALS)
+ SYM(LDAP_INVALID_DN_SYNTAX)
+ SYM(LDAP_INVALID_SYNTAX)
+ SYM(LDAP_IS_LEAF)
+ break;
+
+ case 'L':
+ SYM(LDAP_LOCAL_ERROR)
+ SYM(LDAP_LOOP_DETECT)
+ break;
+
+ case 'M':
+ SYM(LDAP_MOD_ADD)
+ SYM(LDAP_MOD_BVALUES)
+ SYM(LDAP_MOD_DELETE)
+ SYM(LDAP_MOD_REPLACE)
+ break;
+
+ case 'N':
+ SYM(LDAP_NAMING_VIOLATION)
+ SYM(LDAP_NOT_ALLOWED_ON_NONLEAF)
+ SYM(LDAP_NOT_ALLOWED_ON_RDN)
+ SYM(LDAP_NO_LIMIT)
+ SYM(LDAP_NO_MEMORY)
+ SYM(LDAP_NO_OBJECT_CLASS_MODS)
+ SYM(LDAP_NO_SUCH_ATTRIBUTE)
+ SYM(LDAP_NO_SUCH_OBJECT)
+ break;
+
+ case 'O':
+ SYM(LDAP_OBJECT_CLASS_VIOLATION)
+ SYM(LDAP_OPERATIONS_ERROR)
+ SYM(LDAP_OPT_API_INFO)
+ SYM(LDAP_OPT_API_FEATURE_INFO)
+ SYM(LDAP_OPT_CACHE_ENABLE)
+ SYM(LDAP_OPT_CACHE_FN_PTRS)
+ SYM(LDAP_OPT_CACHE_STRATEGY)
+ SYM(LDAP_OPT_CLIENT_CONTROLS)
+ SYM(LDAP_OPT_DEBUG_LEVEL)
+ SYM(LDAP_OPT_DEREF)
+ SYM(LDAP_OPT_DESC)
+ SYM(LDAP_OPT_DNS)
+ SYM(LDAP_OPT_HOST_NAME)
+ SYM(LDAP_OPT_IO_FN_PTRS)
+ SYM(LDAP_OPT_NETWORK_TIMEOUT)
+ SYM(LDAP_OPT_OFF, 0)
+ SYM(LDAP_OPT_ON, 1)
+ SYM(LDAP_OPT_PROTOCOL_VERSION)
+ SYM(LDAP_OPT_REBIND_ARG)
+ SYM(LDAP_OPT_REBIND_FN)
+ SYM(LDAP_OPT_REFERRALS)
+ SYM(LDAP_OPT_REFERRAL_HOP_LIMIT)
+ SYM(LDAP_OPT_REFERRAL_URLS)
+ SYM(LDAP_OPT_REFHOPLIMIT)
+ SYM(LDAP_OPT_RESTART)
+ SYM(LDAP_OPT_SIZELIMIT)
+ SYM(LDAP_OPT_SERVER_CONTROLS)
+ SYM(LDAP_OPT_SSL)
+ SYM(LDAP_OPT_THREAD_FN_PTRS)
+ SYM(LDAP_OPT_TIMELIMIT)
+ SYM(LDAP_OPT_TIMEOUT)
+ SYM(LDAP_OPT_URI)
+ SYM(LDAP_OPT_X_SASL)
+ SYM(LDAP_OPT_X_SASL_AUTHCID)
+ SYM(LDAP_OPT_X_SASL_AUTHZID)
+ SYM(LDAP_OPT_X_SASL_MAXBUFSIZE)
+ SYM(LDAP_OPT_X_SASL_MECH)
+ SYM(LDAP_OPT_X_SASL_REALM)
+ SYM(LDAP_OPT_X_SASL_SECPROPS)
+ SYM(LDAP_OPT_X_SASL_SSF)
+ SYM(LDAP_OPT_X_SASL_SSF_EXTERNAL)
+ SYM(LDAP_OPT_X_SASL_SSF_MIN)
+ SYM(LDAP_OPT_X_SASL_SSF_MAX)
+ SYM(LDAP_OPT_X_TLS)
+ SYM(LDAP_OPT_X_TLS_CTX)
+ SYM(LDAP_OPT_X_TLS_CACERTFILE)
+ SYM(LDAP_OPT_X_TLS_CACERTDIR)
+ SYM(LDAP_OPT_X_TLS_CERTFILE)
+ SYM(LDAP_OPT_X_TLS_KEYFILE)
+ SYM(LDAP_OPT_X_TLS_REQUIRE_CERT)
+ SYM(LDAP_OPT_X_TLS_CIPHER_SUITE)
+ SYM(LDAP_OPT_X_TLS_RANDOM_FILE)
+ SYM(LDAP_OPT_X_TLS_SSL_CTX)
+ SYM(LDAP_OPT_X_TLS_NEVER)
+ SYM(LDAP_OPT_X_TLS_HARD)
+ SYM(LDAP_OPT_X_TLS_DEMAND)
+ SYM(LDAP_OPT_X_TLS_ALLOW)
+ SYM(LDAP_OPT_X_TLS_TRY)
+ SYM(LDAP_OTHER)
+ break;
+
+ case 'P':
+ SYM(LDAP_PARAM_ERROR)
+ SYM(LDAP_PARTIAL_RESULTS)
+ SYM(LDAP_PORT)
+ SYM(LDAP_PORT_MAX)
+ SYM(LDAP_PROTOCOL_ERROR)
+ break;
+
+ case 'R':
+ SYM(LDAP_REFERRAL)
+ SYM(LDAP_RESULTS_TOO_LARGE)
+ SYM(LDAP_RES_BIND)
+ SYM(LDAP_RES_SEARCH_ENTRY)
+ SYM(LDAP_RES_SEARCH_REFERENCE)
+ SYM(LDAP_RES_SEARCH_RESULT)
+ SYM(LDAP_RES_MODIFY)
+ SYM(LDAP_RES_ADD)
+ SYM(LDAP_RES_DELETE)
+ SYM(LDAP_RES_MODDN)
+ SYM(LDAP_RES_COMPARE)
+ SYM(LDAP_RES_EXTENDED)
+ SYM(LDAP_RES_INTERMEDIATE)
+ SYM(LDAP_RES_ANY)
+ SYM(LDAP_RES_UNSOLICITED)
+ break;
+
+ case 'S':
+ SYM(LDAP_SASL_AUTOMATIC)
+ SYM(LDAP_SASL_INTERACTIVE)
+ SYM(LDAP_SASL_QUIET)
+ SYM(LDAP_SCOPE_BASE)
+ SYM(LDAP_SCOPE_ONELEVEL)
+ SYM(LDAP_SCOPE_SUBTREE)
+ SYM(LDAP_SECURITY_NONE)
+ SYM(LDAP_SERVER_DOWN)
+ SYM(LDAP_SIZELIMIT_EXCEEDED)
+ SYM(LDAP_STRONG_AUTH_NOT_SUPPORTED)
+ SYM(LDAP_STRONG_AUTH_REQUIRED)
+ SYM(LDAP_SUCCESS)
+ break;
+
+ case 'T':
+ SYM(LDAP_TAG_SYNC_NEW_COOKIE)
+ SYM(LDAP_TAG_SYNC_REFRESH_DELETE)
+ SYM(LDAP_TAG_SYNC_REFRESH_PRESENT)
+ SYM(LDAP_TAG_SYNC_ID_SET)
+ SYM(LDAP_TAG_SYNC_COOKIE)
+ SYM(LDAP_TAG_REFRESHDELETES)
+ SYM(LDAP_TAG_REFRESHDONE)
+ SYM(LDAP_TAG_RELOAD_HINT)
+ SYM(LDAP_TAG_EXOP_MODIFY_PASSWD_ID)
+ SYM(LDAP_TAG_EXOP_MODIFY_PASSWD_OLD)
+ SYM(LDAP_TAG_EXOP_MODIFY_PASSWD_NEW)
+ SYM(LDAP_TAG_EXOP_MODIFY_PASSWD_GEN)
+ SYM(LDAP_TAG_MESSAGE)
+ SYM(LDAP_TAG_MSGID)
+ SYM(LDAP_TAG_LDAPDN)
+ SYM(LDAP_TAG_LDAPCRED)
+ SYM(LDAP_TAG_CONTROLS)
+ SYM(LDAP_TAG_REFERRAL)
+ SYM(LDAP_TAG_NEWSUPERIOR)
+ SYM(LDAP_TAG_EXOP_REQ_OID)
+ SYM(LDAP_TAG_EXOP_REQ_VALUE)
+ SYM(LDAP_TAG_EXOP_RES_OID)
+ SYM(LDAP_TAG_EXOP_RES_VALUE)
+ SYM(LDAP_TAG_IM_RES_OID)
+ SYM(LDAP_TAG_IM_RES_VALUE)
+ SYM(LDAP_TAG_SASL_RES_CREDS)
+ SYM(LDAP_TIMELIMIT_EXCEEDED)
+ SYM(LDAP_TIMEOUT)
+ SYM(LDAP_TYPE_OR_VALUE_EXISTS)
+ break;
+
+ case 'U':
+ SYM(LDAP_UNAVAILABLE)
+ SYM(LDAP_UNAVAILABLE_CRITICAL_EXTN)
+ SYM(LDAP_UNDEFINED_TYPE)
+ SYM(LDAP_UNWILLING_TO_PERFORM)
+ SYM(LDAP_URL_ERR_BADSCOPE)
+ SYM(LDAP_URL_ERR_MEM)
+ SYM(LDAP_URL_ERR_NODN)
+ SYM(LDAP_URL_ERR_NOTLDAP)
+ SYM(LDAP_URL_ERR_PARAM)
+ SYM(LDAP_URL_OPT_SECURE)
+ SYM(LDAP_USER_CANCELLED)
+ break;
+
+ case 'V':
+ SYM(LDAP_VERSION)
+ SYM(LDAP_VERSION1)
+ SYM(LDAP_VERSION2)
+ SYM(LDAP_VERSION3)
+ break;
+
+ case '_':
+ SYM(LDAPS_PORT)
+ break;
+ }
+ errno = EINVAL;
+ return 0;
+
+not_there:
+ errno = ENOENT;
+ return 0;
+}
diff --git a/examples/ldap_mod_attr.pl b/examples/ldap_mod_attr.pl
new file mode 100755
index 0000000..d0fcd56
--- /dev/null
+++ b/examples/ldap_mod_attr.pl
@@ -0,0 +1,257 @@
+#! /usr/bin/perl -w
+#
+# ldap_mod_attr - change an attribute in someone's LDAP entry
+#
+# Author: Andrew J Cosgriff <ajc@bing.wattle.id.au>
+# Created: Thu Dec 4 19:48:03 1997
+# Version: $Id: ldap_mod_attr.pl,v 1.1.1.1 1998/01/30 19:10:06 jonl Exp $
+# Keywords: ldap modify add remove attribute commmand-line useful really
+#
+########################################
+#
+### Commentary:
+#
+# Sick of typing in lines of ldapmodify stuff just to change one or
+# two attributes ? This is for you...
+#
+### TO DO:
+#
+# - take note when dealing with multiple values for an attribute
+#
+########################################
+#
+### Code:
+#
+use Net::LDAPapi;
+use Getopt::Std;
+use File::Basename;
+my $version = substr q$Revision: 1.1.1.1 $, 10;
+chop $version;
+##########
+#
+# Defaults
+#
+$ldap_server = "ldap.org.au";
+$BASEDN = $ENV{'LDAP_BASEDN'} || "o=Org, c=AU";
+$ROOTDN = "cn=admin, o=Org, c=AU";
+$ROOTPW = "";
+$batchmode = 0;
+$verbosemode = 0;
+$modify_all = 0;
+$do_nothing = 0;
+$UIDATTR = "uid";
+#
+# Parse command line options, explained here :
+#
+$usage_msg = "ldap_mod_attr version " . $version . " by Andrew J Cosgriff <ajc\@bing.wattle.id.au>
+Usage : " . basename($0) . " [options] <search filter> <attr=value> <attr=value> ...
+
+[options] being one or more of :
+-a : modify all matching entries (rather than prompting for one)
+-b <dn> : base DN for searches
+ [ default - $BASEDN ]
+-D <dn> : bind as this DN to do the modifications
+ [ default - $ROOTDN ]
+-h <host> : ldap server to talk to
+ [ default - $ldap_server ]
+-n : do nothing, just show what would happen (implies -v)
+-q : batch/quiet mode - no prompting for password
+ - no prompting if there are multiple matches
+-v : verbose mode - print \"<attr> changed from <old> to <new>\"
+-w <pwd> : the password for the DN we bind as with -D
+
+<search filter> being either :
+- a uid, eg. \"nate\"
+- an RFC 1558-style LDAP search filter, eg. \"cn=Nathan Bailey\"
+
+exitcodes are :
+1 - general error
+2 - no matches returned by ldap_search_s
+3 - too many matches (for -q)
+";
+
+if (getopts('ab:D:h:nqvw:?', \%opt) == 0) {
+ print $usage_msg;
+ exit 1;
+}
+
+$modify_all = 1 if (defined $opt{'a'});
+$BASEDN = $opt{'b'} if (defined $opt{'b'});
+$ROOTDN = $opt{'D'} if (defined $opt{'D'});
+$ldap_server = $opt{'h'} if (defined $opt{'h'});
+$batchmode = 1 if (defined $opt{'q'});
+$verbosemode = 1 if (defined $opt{'v'});
+$do_nothing = 1 if (defined $opt{'n'});
+$verbosemode = $do_nothing || $verbosemode;
+$ROOTPW = $opt{'w'} if (defined $opt{'w'});
+#
+# Print help if they want/need it
+#
+if ($opt{'?'}) {
+ print $usage_msg;
+ exit 1;
+}
+
+if ($#ARGV == -1) {
+ print "Need to specify a search filter and attr=value pairs\n";
+ print $usage_msg;
+ exit 1;
+}
+
+if ($#ARGV <= 0) {
+ print "Need to specify attr=value pairs as well\n";
+ print $usage_msg;
+ exit 1;
+}
+
+print "Well hey, we\'re in DoNothing mode...\n" if $do_nothing;
+#
+# Ask for the Root DN's password if they didn't specify it
+#
+if ($ROOTPW eq "") {
+ print "Attempting to bind as $ROOTDN\nPassword : ";
+ system "stty -echo";
+ $ROOTPW = <STDIN>;
+ chomp $ROOTPW;
+ system "stty echo";
+ print "\n";
+}
+#
+# Initialize Connection to LDAP Server
+#
+if (($ld = ldap_open($ldap_server,LDAP_PORT)) eq "")
+{
+ die "ldap_init failed";
+}
+#
+# Bind as the specified DN
+#
+if ((ldap_simple_bind_s($ld,$ROOTDN,$ROOTPW)) != LDAP_SUCCESS)
+{
+ ldap_perror($ld,"ldap_simple_bind_s");
+ die "Failed to bind as $ROOTDN";
+}
+#
+# Perform search
+#
+$filter = shift @ARGV;
+
+if ($filter !~ /[\(\)\&\|=]/) {
+ $filter = "($UIDATTR=$filter)";
+}
+print "\nSearching for $filter\n" if ($verbosemode);
+@attrs = ();
+if (ldap_search_s($ld,$BASEDN,LDAP_SCOPE_SUBTREE,$filter,\@attrs,0,$result)
+ != LDAP_SUCCESS)
+ {
+ $err = ldap_get_lderrno($ld,$errdn,$extramsg);
+ print &ldap_err2string($err),"\n";
+ print "DN $errdn\n" if defined $errdn;
+ print "extramsg $extramsg\n" if defined $extramsg;
+
+ ldap_unbind($ld);
+ die "Search for $filter failed\n";
+ }
+
+$num_entries = ldap_count_entries($ld,$result);
+#
+# Die if we got no matches, or if we're in batch mode and got more
+# than one match
+#
+exit 2 if ($num_entries == 0);
+exit 3 if ($batchmode && ($num_entries > 1));
+
+print "$num_entries matches\n" if ($verbosemode && ($num_entries > 1));
+
+$entry = ldap_first_entry($ld, $result);
+if ($num_entries == 1) {
+ #
+ # If we got just one match, just do it.
+ #
+ &do_mod_entry($entry);
+} else {
+ #
+ # If we're modifying all entries, loop through and do each one in
+ # turn. Otherwise, make a list of entries so we can present a menu
+ # and ask the user which entry to modify.
+ #
+ while ($entry != 0) {
+ if ($modify_all) {
+ &do_mod_entry($entry);
+ } else {
+ push @entries, $entry;
+ }
+ $entry = ldap_next_entry($ld, $entry);
+ }
+ #
+ # Present a menu of matching entries, and ask which of them the user
+ # wants to modify.
+ #
+ if (! $modify_all) {
+ for $cnt (0 .. $#entries) {
+ print "$cnt : ", ldap_get_dn($ld, $entries[$cnt]), "\n";
+ }
+ $num = -1;
+ while (($num < 0) || ($num > $#entries)) {
+ print "Which entry ? : ";
+ $num = <STDIN>;
+ chomp $num;
+ }
+ &do_mod_entry($entries[$num]);
+ }
+}
+########################################
+#
+# do_mod_entry - Given an entry (as returned by
+# ldap_first_entry/ldap_next_entry), apply all the modifications as
+# specified in @ARGV
+#
+sub do_mod_entry {
+ my $entry = shift @_;
+ my $dn = ldap_get_dn($ld, $entry);
+
+ print "\nModifying ", ldap_get_dn($ld, $entry), " :\n" if $verbosemode;
+ foreach $mod (@ARGV) {
+ my ($attr, $val) = split('=',$mod);
+ @values = ldap_get_values($ld,$entry,$attr);
+ my (%mods) = ( $attr, $val );
+ if (($#values > -1) && ($val eq $values[0])) {
+ print "* (no change) $attr=$val\n" if $verbosemode;
+ next;
+ } elsif (($#values == -1) && ($val eq "")) {
+ print "* (no change) $attr not present\n";
+ next;
+ }
+ #
+ # Print out nice verbose info on what's going on
+ #
+ if ($verbosemode) {
+ if ($val eq "") {
+ if ($#values > -1) {
+ print "- $attr=", $values[0], "\n";
+ }
+ } elsif ($#values > -1) {
+ print "- $attr=", $values[0], "\n";
+ print "+ $attr=", $val, "\n";
+ } else {
+ print "+ $attr=$val\n";
+ }
+ }
+ #
+ # Apply this modification - it'd be groovier to assemble a list of
+ # modifications so we only call ldap_modify_s once per entry, but
+ # it's a bit fiddly to assemble said list properly, so i'm being
+ # lazy :)
+ #
+ if (! $do_nothing) {
+ if (ldap_modify_s($ld,$dn,\%mods) != LDAP_SUCCESS) {
+ ldap_perror($ld,"ldap_modify_s");
+ die "Failed to modify $dn\n";
+ } else {
+ print "* modifications successful.\n" if $verbosemode;
+ }
+ }
+ }
+}
+
+### ldap_mod_attr ends here
diff --git a/examples/ldapwalk.pl b/examples/ldapwalk.pl
new file mode 100755
index 0000000..3cfbbbe
--- /dev/null
+++ b/examples/ldapwalk.pl
@@ -0,0 +1,175 @@
+#!/usr/bin/perl
+#
+#
+# ldapwalk.pl - Walks through Records Matching a Given Filter
+# Author: Clayton Donley, Motorola, <donley@cig.mot.com>
+#
+# Demonstration of Synchronous Searching in PERL5.
+#
+# Rather than printing attribute and values directly, they are
+# stored in a Hash, where further manipulation would be very simple.
+# The output could then be printed to a file or standard output, or
+# simply run through the modify or add commands.
+#
+# Usage: ldapwalk.pl FILTER
+# Example: ldapwalk.pl "sn=Donley"
+#
+
+use strict;
+use Net::LDAPapi;
+
+# Define these values
+
+my $ldap_server = "localhost";
+my $BASEDN = "o=Org, c=US";
+my $sizelimit = 100; # Set to Maximum Number of Entries to Return
+# Can set small to test error routines
+
+# Various Variable Declarations...
+my $ld;
+my $dn;
+my $attr;
+my $ent;
+my $ber;
+my @vals;
+my %record;
+my $result;
+
+#
+# Initialize Connection to LDAP Server
+
+if (($ld = new Net::LDAPapi($ldap_server)) == -1)
+{
+ die "Connection Failed!";
+}
+
+#ldap_set_option(0,LDAP_OPT_DEBUG_LEVEL,-1);
+
+#
+# Bind as NULL User to LDAP connection $ld
+
+#$ld->sasl_parms(-mech=>"CRAM-MD5",-flags=>LDAP_SASL_AUTOMATIC);
+
+#if ($ld->bind_s("tester","tester",LDAP_AUTH_SASL) != LDAP_SUCCESS)
+if ($ld->bind_s != LDAP_SUCCESS)
+{
+ $ld->unbind;
+ die "bind: ", $ld->errstring, ": ", $ld->extramsg;
+}
+
+# This will set the size limit to $sizelimit from above. The command
+# is a Netscape addition, but I've programmed replacement versions for
+# other APIs.
+$ld->set_option(LDAP_OPT_SIZELIMIT,$sizelimit);
+
+# This routine is COMPLETELY unnecessary in this application, since
+# the rebind procedure at the end of this program simply rebinds as
+# a NULL user.
+#$ld->set_rebind_proc(&rebindproc);
+
+#
+# Specify Search Filter and List of Attributes to Return
+
+my $filter = $ARGV[0];
+my @attrs = ();
+
+#
+# Perform Search
+my $msgid = $ld->search($BASEDN,LDAP_SCOPE_SUBTREE,$filter,\@attrs,0);
+
+if ($msgid < 0)
+{
+ $ld->unbind;
+ die "search: ", $ld->errstring, ": ", $ld->extramsg;
+}
+
+# Reset Number of Entries Counter
+my $nentries = 0;
+
+# Set no timeout.
+my $timeout = -1;
+
+#
+# Cycle Through Entries
+while (1)
+{
+ $result = $ld->result($msgid, 0, $timeout);
+
+ last unless $result;
+ last if( $ld->{"status"} == $ld->LDAP_RES_SEARCH_RESULT );
+ next if( $ld->{"status"} != $ld->LDAP_RES_SEARCH_ENTRY );
+
+ $nentries++;
+
+ for ($ent = $ld->first_entry; $ent != 0; $ent = $ld->next_entry)
+ {
+
+ #
+ # Get Full DN
+ if (($dn = $ld->get_dn) eq "")
+ {
+ $ld->unbind;
+ die "get_dn: ", $ld->errstring, ": ", $ld->extramsg;
+ }
+
+ #
+ # Cycle Through Each Attribute
+ for ($attr = $ld->first_attribute; $attr ne ""; $attr = $ld->next_attribute)
+ {
+
+ #
+ # Notice that we're using get_values_len. This will retrieve binary
+ # as well as text data. You can change to get_values to only get text
+ # data.
+ #
+ @vals = $ld->get_values_len($attr);
+ $record{$dn}->{$attr} = [@vals];
+ }
+ }
+ $ld->msgfree;
+
+}
+if ( $result == undef && $ld->err != LDAP_SUCCESS)
+{
+ $ld->unbind;
+ die "result: ", $ld->errstring, ": ", $ld->extramsg;
+}
+
+print "Found $nentries records\n";
+
+$ld->unbind;
+
+foreach $dn (keys %record)
+{
+ my $item;
+ print "dn: $dn\n";
+ foreach $attr (keys %{$record{$dn}})
+ {
+ for $item ( @{$record{$dn}{$attr}})
+ {
+ if ($attr =~ /binary/ )
+ {
+ print "$attr: <binary>\n";
+ } elsif ($attr eq "jpegphoto") {
+#
+# Notice how easy it is to take a binary attribute and dump it to a file
+# or such. Gotta love PERL.
+#
+ print "$attr: JpegPhoto (length: " . length($item). ")\n";
+ open (TEST,">$dn.jpg");
+ print TEST $item;
+ close (TEST);
+ } else {
+ print "$attr: $item\n";
+ }
+ }
+ }
+}
+
+exit;
+
+sub rebindproc
+{
+
+ return("","",LDAP_AUTH_SIMPLE);
+}
diff --git a/examples/ldapwalk2.pl b/examples/ldapwalk2.pl
new file mode 100755
index 0000000..759e31d
--- /dev/null
+++ b/examples/ldapwalk2.pl
@@ -0,0 +1,113 @@
+#!/usr/bin/perl -w
+#
+# testwalk.pl - Walks through Records Matching a Given Filter
+# Author: Clayton Donley, Motorola, <donley@cig.mot.com>
+#
+# Demonstration of OO Style LDAP Calls Using Net::LDAPapi
+#
+# Similar to ldapwalk2.pl, only it uses the OO versions of the synchronous
+# functions to retrieve a hash containing the matching entries.
+#
+# Usage: testwalk.pl FILTER
+# Example: testwalk.pl "sn=Donley"
+#
+
+use strict;
+use Net::LDAPapi;
+
+# Define these values
+
+my $ldap_server = "localhost";
+my $BASEDN = "o=Org, c=US";
+my $sizelimit = 100; # Set to Maximum Number of Entries to Return
+ # Can set small to test error routines
+
+# Various Variable Declarations
+my $ldcon;
+my $ld;
+my $filter;
+my $result;
+my %record;
+my $dn;
+my $item;
+my $attr;
+
+# Initialize Connection to LDAP Server
+
+if (($ldcon = new Net::LDAPapi($ldap_server)) == -1)
+{
+ die "Unable to Open LDAP Connection";
+}
+
+if ($ldcon->bind_s != LDAP_SUCCESS)
+{
+ die $ldcon->errstring;
+}
+
+$ldcon->set_option(LDAP_OPT_SIZELIMIT,$sizelimit);
+
+$ldcon->set_rebind_proc(\&rebindproc);
+
+# Specify what to Search For
+
+$filter = $ARGV[0];
+
+# Perform Search
+
+if ($ldcon->search_s($BASEDN,LDAP_SCOPE_SUBTREE,$filter,[],0) != LDAP_SUCCESS)
+{
+ print $ldcon->errstring . "\n";
+ die;
+}
+
+# Here we get a HASH of HASHes... All entries, keyed by DN and ATTRIBUTE.
+#
+# Since a reference is returned, we simply make %record contain the HASH
+# that the reference points to.
+
+%record = %{$ldcon->get_all_entries};
+
+$ldcon->unbind;
+
+# We can sort our resulting DNs quite easily...
+my @dns = (sort keys %record);
+
+# Print the number of entries returned.
+print $#dns+1 . " entries returned.\n";
+
+foreach $dn (@dns)
+{
+ print "dn: $dn\n";
+ foreach $attr (keys %{$record{$dn}})
+ {
+ for $item ( @{$record{$dn}{$attr}})
+ {
+ if ($attr =~ /binary/)
+ {
+ print "$attr: binary - length=" . length($item) . "\n";
+ }
+ elsif ($attr eq "jpegphoto")
+ {
+#
+# Notice how easy it is to take a binary attribute and dump it to a file
+# or such. Gotta love PERL.
+#
+ print "$attr: binary - length=" . length($item). "\n";
+ open (TEST,">$dn.jpg");
+ print TEST $item;
+ close (TEST);
+ } else {
+ print "$attr: $item\n";
+ }
+ }
+ }
+}
+
+exit;
+
+sub rebindproc
+{
+
+ return("","",LDAP_AUTH_SIMPLE);
+}
+
diff --git a/examples/testwrite.pl b/examples/testwrite.pl
new file mode 100755
index 0000000..8e0e8dd
--- /dev/null
+++ b/examples/testwrite.pl
@@ -0,0 +1,80 @@
+#!/usr/bin/perl -w
+#
+# testwrite.pl - Test of LDAP Modify Operations in Perl5
+# Author: Clayton Donley <donley@cig.mot.com>
+#
+# This utility is mostly to demonstrate all the write operations
+# that can be done with LDAP through this PERL5 module.
+#
+
+
+use strict;
+use Net::LDAPapi;
+
+
+# This is the entry we will be adding. Do not use a pre-existing entry.
+my $ENTRYDN = "cn=New Guy, o=Org, c=US";
+
+# This is the DN and password for an Administrator
+my $ROOTDN = "cn=root, o=Org, c=US";
+my $ROOTPW = "abc123";
+
+my $ldap_server = "localhost";
+
+my $ld = new Net::LDAPapi($ldap_server);
+
+if ($ld == -1)
+{
+ die "Connection to LDAP Server Failed";
+}
+
+if ($ld->bind_s($ROOTDN,$ROOTPW) != LDAP_SUCCESS)
+{
+ die $ld->errstring;
+}
+
+my %testwrite = (
+ "cn" => "Test User",
+ "sn" => "User",
+ "givenName" => "Test",
+ "telephoneNumber" => "8475551212",
+ "objectClass" => ["top","person","organizationalPerson",
+ "inetOrgPerson"],
+ "mail" => "tuser\@my.org",
+);
+
+if ($ld->add_s($ENTRYDN,\%testwrite) != LDAP_SUCCESS)
+{
+ die $ld->errstring;
+}
+
+print "Entry Added.\n";
+
+
+%testwrite = (
+ "telephoneNumber" => "7085551212",
+ "mail" => {"a",["Test_User\@my.org"]},
+);
+
+if ($ld->modify_s($ENTRYDN,\%testwrite) != LDAP_SUCCESS)
+{
+ die $ld->errstring;
+}
+
+print "Entry Modified.\n";
+
+exit;
+#
+# Delete the entry for $ENTRYDN
+#
+if ($ld->delete_s($ENTRYDN) != LDAP_SUCCESS)
+{
+ die $ld->errstring;
+}
+
+print "Entry Deleted.\n";
+
+# Unbind to LDAP server
+$ld->unbind;
+
+exit;
diff --git a/examples/updatepw.pl b/examples/updatepw.pl
new file mode 100755
index 0000000..9cfdae3
--- /dev/null
+++ b/examples/updatepw.pl
@@ -0,0 +1,95 @@
+#!/usr/bin/perl
+#
+#
+# updatepw.pl - Synchronize Passwords from Unix to LDAP
+# Author: Clayton Donley, Motorola, <donley@cig.mot.com>
+#
+# Reads in a password file, checks for existing entries matching
+# username@domain.com in the mail attribute and populates the CRYPTed
+# password from Unix into the userPassword attribute for that DN.
+#
+# Usage: updatepw.pl username username ... username
+# Example: updatepw.pl "donley"
+#
+
+use Net::LDAPapi;
+
+# Define these values
+
+$ldap_server = "localhost";
+$PWFILE = "/etc/passwd";
+$BASEDN = "o=Org, c=US";
+$ROOTDN = "cn=Directory Manager, o=Org, c=US";
+$ROOTPW = "abc123";
+$MAILATTR = "mail";
+$MYDOMAIN = "mycompany.com";
+
+open(PASSWD,$PWFILE);
+while($line = <PASSWD>)
+{
+ chop $line;
+
+ ($user,$pass) = split(/:/,$line);
+ $pwuser{$user} = $pass;
+}
+close(PASSWD);
+
+# Initialize Connection to LDAP Server
+
+if (($ld = new Net::LDAPapi($ldap_server)) == -1)
+{
+ die "Cannot Open Connection to Server!";
+}
+
+# Bind as the ROOT DIRECTORY USER to LDAP connection $ld
+
+if ($ld->bind_s($ROOTDN,$ROOTPW) != LDAP_SUCCESS)
+{
+ die $ld->errstring;
+}
+
+
+# Specify what to Search For
+
+foreach $username (@ARGV)
+{
+
+# Perform Search
+ $filter = "($MAILATTR=$username\@$MYDOMAIN)";
+ if ($ld->search_s($BASEDN,LDAP_SCOPE_SUBTREE,$filter,["uid","userpassword","mail"],0)
+ != LDAP_SUCCESS)
+ {
+ $ld->unbind;
+ die $ld->errstring;
+ }
+
+# Here we get a HASH of HASHes... All entries, keyed by DN and ATTRIBUTE.
+#
+# Since a reference is returned, we simply make %record contain the HASH
+# that the reference points to.
+
+ if ($ld->first_entry == 0)
+ {
+ print "Not Found: $username\@$MYDOMAIN\n";
+ } else {
+ $dn = $ld->get_dn;
+ @pass = $ld->get_values('userpassword');
+ if ($pass[0] ne "{CRYPT}$pwuser{$username}")
+ {
+ $modifyrec{"userpassword"} = [ "{CRYPT}$pwuser{$username}" ];
+ if ($ld->modify_s($dn,\%modifyrec) != LDAP_SUCCESS)
+ {
+ print "Error: $dn Unsuccessful.\n";
+ print "modify_s: $ld->errstring\n";
+ }
+ print "Updated: $username\@$MYDOMAIN\n";
+ } else {
+ print "Matched: $username\@$MYDOMAIN\n";
+ }
+ }
+}
+
+$ld->unbind;
+
+exit;
+
diff --git a/examples/web500.pl b/examples/web500.pl
new file mode 100755
index 0000000..83a547b
--- /dev/null
+++ b/examples/web500.pl
@@ -0,0 +1,1158 @@
+#!/usr/bin/perl
+#
+# web500.pl - Full featured LDAP directory SEARCH, MODIFY, DELETE, ADD
+# Web Interface, with Authentication.
+#
+# Author: Clayton Donley, Motorola <donley@cig.mot.com>
+#
+# Other Credits:
+# - textarea feature - Douglas Gray Stevens <gray@austin.apc.slb.com>
+
+
+# Requires the CGI and Net::LDAPapi Modules
+
+use CGI qw(:standard :html3);
+use Net::LDAPapi;
+
+# Set to Local LDAP Server and Base DN
+
+$LDAP_SERVER = "localhost";
+$LDAP_BASEDN = "o=Org,c=US";
+
+# Set this to the name of the CGI on your web server
+$LDAPCGI_NAME = "/cgi-bin/web500.pl";
+
+# This is the displayed title...
+$LDAPCGI_TITLE = "Directory Search and Update";
+
+# If set to 0, new passwords will be stored PLAIN TEXT
+$LDAPCGI_CRYPT_PASS = 1;
+
+# This is the address that supports your LDAP server
+$LDAPCGI_HELP_MAIL = "help\@myorg.com";
+
+# Do you allow users to change their own password?
+$LDAPCGI_ALLOW_CHPASS = 1;
+
+# Do you allow users to upload JPEG photos?
+$LDAPCGI_ALLOW_JPEGUL = 1;
+
+# Do you want to display Netscape VCARD Entries?
+$LDAPCGI_DISPLAY_VCARD = 0;
+
+# This is the default DN and PASSWORD to bind to the LDAP server when
+# a user hasn't authenticated.
+
+%ldap_default_auth = (
+ "dn", "",
+ "pass", "",
+);
+
+# %fields - Attribute Table used by all forms
+# Format: "Field Name" => ["Description",length,max_length,multiple,rows]
+# Field Name - Lower Case Attribute Name
+# Description - Name to display to the User
+# length - Length of Field on Screen
+# max_length - Maximum Length of Input
+# multiple - 1 to allow multiple values, 0 for single value
+# rows - Number of Rows to allow for entry.
+
+%fields = (
+ "cn" => ["Name",20,40,1,1],
+ "givenname" => ["First Name",20,40,0,1],
+ "sn" => ["Last Name",20,40,0,1],
+ "uid" => ["UniqueID",10,15,0,1],
+ "departmentnumber" => ["Department Number",10,25,0,1],
+ "telephonenumber" => ["Telephone Number",30,50,1,1],
+ "facsimiletelephonenumber"
+ => ["Fax Number", 30,50,0,1],
+ "pager" => ["Pager Number", 30,50,0,1],
+ "mobile" => ["Mobile Number", 30,50,0,1],
+ "labeleduri" => ["WWW Home Page", 40,100,0,1],
+ "title" => ["Title", 40,100,0,1],
+ "employeenumber" => ["Employee Number",10,25,0,1],
+ "l" => ["City",30,50,0,1],
+ "mail" => ["Email Address", 25,70,0,1],
+ "postaladdress" => ["Postal Address", 30,200,0,5],
+);
+
+# When searching for users, only obtain the following fields
+@searchuser_attributes = ("cn","givenname","sn","uid","telephonenumber",
+ "facsimiletelephonenumber","mobile","pager","labeleduri","title","mail",
+ "postaladdress","employeenumber","l","departmentnumber","jpegphoto");
+
+@searchuser_onattr = ("cn","telephonenumber","uid", "postaladdress","mail",
+ "title","departmentnumber","l");
+
+# When adding users, the following attributes may be specified
+@adduser_attributes = ("givenname","sn","uid","departmentnumber","mail",
+ "telephonenumber","facsimiletelephonenumber","pager","mobile","labeleduri",
+ "title","employeenumber");
+
+# When adding users, the following attributes MUST be given
+@adduser_required = ("sn","mail");
+
+# When modifying users, the following attributes can be modified.
+@modifyuser_attributes = ("departmentnumber","telephonenumber","mail",
+ "facsimiletelephonenumber","pager","mobile","labeleduri","title",
+ "employeenumber","l","postaladdress");
+
+# When displaying Organizations and Localities below the current point, use
+# this search filter.
+$DOWN_FILTER = "(|(objectclass=organization)(objectclass=organizationalunit)(objectclass=locality))";
+
+# A List of Location or Organization Names that can be used to map people to
+# Certain parts of the Directory Tree.
+# Also used by the 'assign_next_uid' routine to assign UserIDs when doing
+# directory additions. You can replace that function with your own method
+# of assigning UIDs.
+%location = (
+ "Finance" => ["ou=Finance,o=Org,c=US","a","/usr/web/logs/nextid.fin"],
+ "HR" => ["ou=HR, o=Org, c=US","b","/usr/web/logs/nextid.hr"],
+ "IS" => ["ou=IS, o=Org, c=US","c","/usr/web/logs/nextid.is"],
+);
+
+# @default_person_objectclass is the objectclasses assigned to new users
+@default_person_objectclass = ("top","person","organizationalperson","inetorgperson");
+
+# $op will contain our current operation
+$op = param('op');
+
+# $searchfor will contain YES if we've performed a search...
+$searchfor = param('searchfor');
+
+#
+# Operations Requiring no LDAP Access, Binding, or Access Control
+#
+
+# Show the Authentication Screen
+&authenticate if $op =~ /authenticate/;
+
+
+#
+# Retrieve our Authentication Cookie and put it into our ldap_auth
+# hash. If there is no cookie, we use the default.
+#
+
+if (!cookie('ldap_auth_cookie'))
+{
+ %ldap_auth = %ldap_default_auth;
+} else {
+ %ldap_auth = cookie('ldap_auth_cookie');
+}
+
+#
+# Open Our Connection to the LDAP Server...Only place in the whole program.
+# We use this $ld as the handle for all LDAP access.
+#
+
+$ld = ldap_open($LDAP_SERVER,LDAP_PORT);
+
+#
+# If these were passed, we have sent new authorization credentials.
+# Put these into our ldap_auth structure, or reset the structure to
+# the defaults if the word CLEAR is the UID.
+#
+
+if (param('ldap_myuid') && param('ldap_mypass'))
+{
+ $ldap_myuid = param('ldap_myuid');
+ if ($ldap_myuid eq "CLEAR")
+ {
+ $ldap_auth{'pass'} = $ldap_default_auth{'pass'};
+ $ldap_auth{'dn'} = $ldap_default_auth{'dn'};
+ } else {
+ $ldap_auth{'pass'} = param('ldap_mypass');
+
+# Since the person supplied a UID, not the DN, we lookup the DN
+ $ldap_auth{'dn'} = &get_my_dn($ldap_myuid);
+ }
+}
+
+#
+# We now bind to the server using the specified DN and Password
+#
+
+if (ldap_simple_bind_s($ld,$ldap_auth{'dn'},$ldap_auth{'pass'})
+ != LDAP_SUCCESS)
+{
+ &print_bad_auth;
+ ldap_unbind($ld);
+ exit;
+}
+
+#
+# Lets now build a cookie with our authentication information. The
+# cookie will expire if the browser does not reconnect (and thus resubmit
+# a new cookie) within the hour.
+#
+
+$ldap_auth_cookie = cookie( -name => 'ldap_auth_cookie',
+ -value => \%ldap_auth,
+ -path => $LDAPCGI_NAME,
+ -expires => '+4h');
+
+#
+# These two functions return NON-HTML mime-types, so we will go there
+# directly if necessary rather than send headers and such.
+#
+
+&view_jpegphoto if $op =~ /viewjpeg/;
+&view_vcard if $op =~ /viewvcard/;
+
+#
+# Print the headers and jump to the necessary operation
+#
+
+&print_html_headers;
+&print_options;
+&adduser_entry if $op =~ /adduser/;
+&moduser_entry if $op =~ /moduser/;
+&deluser_entry if $op =~ /deluser/;
+&viewuser_entry if $op =~ /viewuser/ || $op =~ /View Selected/;
+&searchuser_results if $op =~ /searchresult/ || $searchfor =~ /yes/;
+&help_screen if $op =~ /help/;
+
+# By default, display the search screen...
+&searchuser_entry;
+
+# We should NEVER get here, but I've left an unbind and an exit just
+# in case. All of the above subroutines should do EXITs, not RETURNs.
+
+ldap_unbind($ld);
+exit;
+
+
+####
+# get_my_dn - Takes UID as argument and returns a matching DN
+####
+
+sub get_my_dn
+{
+ my ($uid) = @_;
+ my $dn;
+ if (ldap_simple_bind_s($ld,"","") != LDAP_SUCCESS)
+ {
+ &print_bad_auth;
+ ldap_unbind($ld);
+ exit;
+ }
+ $filter = "(uid=$uid)";
+ if (ldap_search_s($ld,$LDAP_BASEDN,LDAP_SCOPE_SUBTREE,$filter,
+ ["uid"],1,$result) != LDAP_SUCCESS)
+ {
+ &print_error;
+ ldap_unbind($ld);
+ exit;
+ }
+ $ent = ldap_first_entry($ld,$result);
+ if ($ent == 0)
+ {
+ &print_bad_auth;
+ ldap_unbind($ld);
+ exit;
+ }
+ $dn = ldap_get_dn($ld,$ent);
+}
+
+
+####
+# print_bottom - Print bottom information
+####
+
+sub print_bottom
+{
+ print "Comments and Suggestions to:",
+ "<ADDRESS><A HREF=mailto:$LDAPCGI_HELP_MAIL>$LDAPCGI_HELP_MAIL</A></ADDRESS>\n",p;
+ print "<h6><strong>$LDAPCGI_TITLE\n",br,
+ "Written by Clayton Donley &lt;<a href=mailto:donley\@cig.mot.com>",
+ "donley\@cig.mot.com</a>&gt;\n",br,
+ "Copyright &copy 1998 by <a href=http://miso.wwa.com/~donley/>Clayton Donley</a>\n",br,
+ "All Rights Reserved.</strong></h6>\n";
+ return;
+}
+
+
+####
+# print_options - Print Top Options
+####
+
+sub print_options
+{
+ local $Flag;
+ $Flag=0;
+ print "<center><a href=$LDAPCGI_NAME?op=search>[SEARCH]</a>";
+ if ($ldap_auth{'dn'} eq $ldap_default_auth{'dn'})
+ {
+ print "<a href=$LDAPCGI_NAME?op=authenticate>[LOGIN]</a> ";
+ $Flag=1;
+ } else {
+ print "<a href=$LDAPCGI_NAME?op=searchuser&ldap_myuid=CLEAR&ldap_mypass=CLEAR>[LOGOUT]</a> ";
+ print "<a href=$LDAPCGI_NAME?op=moduser>[CHANGE PASSWORD/INFO]</a> ";
+ print "<a href=$LDAPCGI_NAME?op=adduser>[ADD]</a> ";
+ }
+ print "<a href=$LDAPCGI_NAME?op=help>[HELP]</a></center>",p;
+ if ($Flag) {
+ print "NOTE: Please LOGIN before you change password and other information.<br><br>";
+ }
+ return;
+}
+
+
+####
+# searchuser_entry - The main search screen
+####
+
+sub searchuser_entry
+{
+
+# If 'my_base_dn' is passed, use it, otherwise use the default
+
+ if (param('my_base_dn'))
+ {
+ $my_base_dn = param('my_base_dn');
+ } else {
+ $my_base_dn = $LDAP_BASEDN;
+ }
+
+# Get rid of extra spaces after commas. This probably isn't the
+# safest way to do this, but should be okay for now.
+
+ $my_base_dn =~ s/,\s/,/g;
+
+# Now make sure that anything passed contains our default BASEDN, otherwise
+# 'my_base_dn' may not be useful.
+
+ if ($my_base_dn !~ /$LDAP_BASEDN$/)
+ {
+ $my_base_dn = $LDAP_BASEDN;
+ }
+
+# Splits the DN into segments. Netscape makes this easy with ldap_explode_dn,
+# but I have to do it manually because none of the other SDKs support it.
+# We're building a hash with all the levels above our own for use in the
+# popup_menu.
+
+ @splitbase = split(/,/,$my_base_dn);
+ @splitdefault = split(/,/,$LDAP_BASEDN);
+
+ for ($count = 0; $count <= $#splitbase; $count++)
+ {
+ for ($base_count = $count; $base_count <= $#splitbase; $base_count++)
+ {
+ if ($count != $base_count)
+ {
+ $base_vals[$count] = $base_vals[$count] . ",";
+ }
+ $base_vals[$count] = $base_vals[$count] . $splitbase[$base_count];
+ }
+ $shortname = $splitbase[$count];
+ $shortname =~ s/^.*=//;
+ $basename{$base_vals[$count]} = $shortname;
+ }
+
+# We don't want people to be able to go higher than the default level.
+ $#base_vals = $#base_vals - $#splitdefault;
+
+# Now print the form with the query and the popup containing higher
+# levels within the LDAP tree.
+
+ print "<b>Current Search Base:</b> $my_base_dn",
+ start_form,
+ hidden('op','searchresult'),
+ hidden('searchfor','yes'),
+ "Move Up To: ",popup_menu('my_base_dn',\@base_vals,$base_vals[0],\%basename),p;
+ foreach $searchattr (@searchuser_onattr)
+ {
+ print textfield(-name=>"searchfor_$searchattr",-size=>50),
+ $fields{$searchattr}[0],"\n",br;
+ }
+ print p,submit('Search'),reset('Reset'),
+ end_form,p,"\n";
+
+# This search will find all the organizations and localities one level below
+# our current level. This allows people to navigate downwards.
+
+ if (ldap_search_s($ld,$my_base_dn,LDAP_SCOPE_ONELEVEL,$DOWN_FILTER,[],1,$result) != LDAP_SUCCESS)
+ {
+ &print_error;
+ ldap_unbind($ld);
+ exit;
+ }
+
+ print h3("Move Down To:\n");
+ print "<ul>\n";
+ $entrycount = 0;
+ for ($ent=ldap_first_entry($ld,$result);$ent!=0;$ent=ldap_next_entry($ld,$ent))
+ {
+ $entrycount = $entrycount + 1;
+ $newbase = ldap_get_dn($ld,$ent);
+ $subbase = $newbase;
+
+# We need to escape certain special characters. I'm sure there are more
+# than these, but this was all I could think of for now.
+ $subbase =~ s/ /%20/g;
+ $subbase =~ s/=/%3D/g;
+
+# We simply pass parameters that would change my_base_dn and continue searching
+ print "<li><a href='$LDAPCGI_NAME?op=searchuser&my_base_dn=$subbase'>$newbase</a>\n";
+ }
+ if ($entrycount == 0)
+ {
+ print "<li>Nothing Below\n";
+ }
+ print "</ul>\n",hr;
+
+ &print_bottom;
+ ldap_unbind($ld);
+ exit;
+}
+
+sub searchuser_results
+{
+ $filter = "";
+ $noattrs = 0;
+
+ foreach $searchattr (@searchuser_onattr)
+ {
+ if (param("searchfor_$searchattr"))
+ {
+ $noattrs++;
+ $filter = $filter . "($searchattr=*" . param("searchfor_$searchattr") . "*)";
+ }
+ }
+
+ if ($filter ne "")
+ {
+ if ($noattrs > 1)
+ {
+ $fullfilter = "(&$filter)";
+ } else {
+ $fullfilter = $filter;
+ }
+ } else {
+ &searchuser_entry;
+ }
+
+ $my_base_dn = param('my_base_dn');
+
+ if (ldap_search_s($ld,$my_base_dn,LDAP_SCOPE_SUBTREE,$fullfilter,[],0,$result) != LDAP_SUCCESS)
+ {
+ ldap_perror($ld,"Search");
+ &print_error;
+ ldap_unbind($ld);
+ exit;
+ }
+ print h3("Results: Search of $my_base_dn for $fullfilter");
+
+# We're going to display the results in a table so that they line-up
+# nicely.
+
+ print start_form,"<table border width=500>\n";
+ print "<TR><TD>No.</TD><TD>Name</TD><TD>Location</TD><TD>Email</TD></TR>\n";
+ $entrycount = 0;
+
+# This for loop cycles through all the entries.
+
+ for ($ent = ldap_first_entry($ld,$result); $ent != 0;
+ $ent = ldap_next_entry($ld,$ent))
+ {
+ $entrycount = $entrycount + 1;
+ $fulldn = ldap_get_dn($ld,$ent);
+ $realdn = $fulldn;
+
+# Once again, we're going to escape special characters
+ $fulldn =~ s/ /%20/g;
+ $fulldn =~ s/=/%3D/g;
+
+# In a later version I'll make these defined at the beginning, but
+# these are the fields for the short listing.
+ @cn = ldap_get_values($ld,$ent,"cn");
+ @l = ldap_get_values($ld,$ent,"l");
+ @mail = ldap_get_values($ld,$ent,"mail");
+ @labeleduri = ldap_get_values($ld,$ent,"labeleduri");
+ @jpegphoto = ldap_get_values($ld,$ent,"jpegphoto");
+
+# Each listing has a checkbox with the value of the person's DN
+ print "<TR><TD>",checkbox('selectdn',0,$realdn,$entrycount),"</TD>";
+
+# If the person has a 'labeleduri' field, make the person's CN a hyperlink
+# to their WWW page.
+
+ if ($#labeleduri >= 0)
+ {
+ print "<TD><a href=$labeleduri[0]>$cn[0]</a></TD>";
+ } else {
+ print "<TD>$cn[0]</TD>";
+ }
+ print "<TD>$l[0]</TD>";
+
+# If the person has a registered EMAIL address, display it and make it
+# a 'mailto' URL.
+
+ if ($#mail >= 0)
+ {
+ print "<TD><a href=mailto:$mail[0]>$mail[0]</a></TD>";
+ } else {
+ print "<TD></TD>";
+ }
+
+# Allow full details of the user to be viewed.
+ print "<TD><a href='$LDAPCGI_NAME?op=viewuser&selectdn=$fulldn'>View All</a></TD>";
+
+# Only display Modify and Delete options if we have authenticated.
+ if ($ldap_auth{'dn'} ne $ldap_default_auth{'dn'})
+ {
+ print "<TD><a href='$LDAPCGI_NAME?op=moduser&selectdn=$fulldn'>Modify</a></TD>";
+ print "<TD><a href='$LDAPCGI_NAME?op=deluser&selectdn=$fulldn'>Delete</a></TD>";
+ }
+
+# If we are displaying Netscape VCARDs, display that option.
+ if ($LDAPCGI_DISPLAY_VCARD)
+ {
+ print "<TD><a href='$LDAPCGI_NAME?op=viewvcard&selectdn=$fulldn'>View Vcard</a></TD>";
+ }
+
+# If the person has a Jpeg Photo, give an option to display it.
+ if ($#jpegphoto >= 0)
+ {
+ print "<TD><a href='$LDAPCGI_NAME?op=viewjpeg&selectdn=$fulldn'>View Photo</a></TD>";
+ }
+ print "</TR>\n";
+ }
+ print "</TABLE>\n",p;
+ if ($entrycount == 0)
+ {
+ print "No Matches\n",end_form,hr;
+ } else {
+ print submit("op","View Selected"),end_form,hr;
+ }
+
+ &print_bottom;
+ ldap_unbind($ld);
+ exit;
+}
+
+####
+# Modify User
+####
+
+sub moduser_entry
+{
+
+ if ($ldap_auth{'dn'} eq $ldap_default_auth{'dn'})
+ {
+ print "Please <a href=$LDAPCGI_NAME?op=authenticate>Authenticate</a>.";
+ ldap_unbind($ld);
+ exit;
+ }
+
+ if (param('selectdn'))
+ {
+ $selectdn = param('selectdn');
+ } else {
+ $selectdn = $ldap_auth{'dn'};
+ }
+
+ print "<b>Modifying:</b> $selectdn",br;
+
+ if (param('gomodifyit'))
+ {
+ &gomodifyit;
+ }
+
+ if (ldap_search_s($ld,$selectdn,LDAP_SCOPE_BASE,"objectclass=*",[],0,$result)
+ != LDAP_SUCCESS)
+ {
+ &print_error;
+ ldap_unbind($ld);
+ exit;
+ }
+
+ $ent = ldap_first_entry($ld,$result);
+
+ if ($ent == 0)
+ {
+ print "User Not Found...\n",p;
+ ldap_unbind($ld);
+ exit;
+ }
+
+# Cycle through all attributes and place their values in a hash.
+
+ for ($attr = ldap_first_attribute($ld,$ent,$ber); $attr; $attr =
+ldap_next_attribute($ld,$ent,$ber))
+ {
+ @vals = ldap_get_values($ld,$ent,$attr);
+ $record{$attr} = [ @vals ];
+ }
+
+# Draw up the Web Form
+
+ print start_multipart_form,
+ hidden('op','moduser'),
+ hidden('selectdn',$selectdn),
+ hidden('gomodifyit','yes');
+
+ print hr,"<TABLE>";
+
+# Password is a Special Case. Since it needs special processing, we
+# do not include it in %fields and thus request it separately.
+
+ print " <TR><TD VALIGN=TOP>New Password:</TD><TD VALIGN=TOP>",password_field('pass'),"</TD></TR>\n";
+ print " <TR><TD VALIGN=TOP>New Password (again):</TD><TD VALIGN=TOP>",password_field('pass2'),"</TD></TR>\n";
+ print "</TABLE><hr><TABLE>\n";
+
+# Now cycle through all keys in %fields and construct the form for each
+# attribute to be modified through this page.
+
+ foreach $key (@modifyuser_attributes)
+ {
+ $count = 0;
+ for $value ( @{$record{$key}} )
+ {
+ if ($count == 0)
+ {
+ print " <TR><TD VALIGN=TOP>" . $fields{$key}[0] . ":</TD>";
+ } else {
+ print " <TR><TD VALIGN=TOP></TD>";
+ }
+ # Sun 24-Aug-1997; Douglas Gray Stephens
+ # Add option for textarea
+ if ($fields{$key}[4]>1)
+ {
+ print " <TD VALIGN=TOP>",textarea("$key.$count",$value,$fields{$key}[4],$fields{$key}[1],"","wrap=virtual"),"</TD></TR>\n";
+ } else {
+ print " <TD VALIGN=TOP>",textfield("$key.$count",$value,$fields{$key}[1],$fields{$key}[2]),"</TD></TR>\n";
+ }
+ print hidden("$key.$count.orig",$value);
+ $count++;
+ }
+ if ($fields{$key}[3] == 1 || $count == 0)
+ {
+ # Sun 24-Aug-1997; Douglas Gray Stephens
+ # Add option for textarea
+ if ($count == 0)
+ {
+ print " <TR><TD VALIGN=TOP>" . $fields{$key}[0] . ":</TD>";
+ } else {
+ print " <TR><TD VALIGN=TOP></TD>";
+ }
+ if ($fields{$key}[4]>1)
+ {
+ print " <TD VALIGN=TOP>",textarea("$key.$count",$value,$fields{$key}[4],$fields{$key}[1],"","wrap=virtual"),"</TD></TR>\n";
+ } else {
+ print " <TD VALIGN=TOP>",textfield("$key.$count",$value,$fields{$key}[1],$fields{$key}[2]),"</TD></TR>\n";
+ }
+ }
+ print hidden($key,$count);
+ }
+ if ($LDAPCGI_ALLOW_JPEGUL)
+ {
+ print " <TR><TD VALIGN=TOP>Upload Photo (JPEG)</TD><TD VALIGN=TOP>",
+ filefield('jpegphoto','',35,256),"</TD></TR>\n";
+ print " <TR><TD VALIGN=TOP></TD><TD VALIGN=TOP>(Enter 'REMOVE' to delete current image)</TD></TR>";
+ }
+ print "</TABLE>",hr,submit('Modify Entry'), end_form,hr;
+
+ &print_bottom;
+ ldap_unbind($ld);
+ exit;
+}
+
+####
+# gomodifyit - Routine to actually do the User Modification
+####
+
+sub gomodifyit
+{
+ print hr;
+
+ foreach $key (@modifyuser_attributes)
+ {
+ $change = 0;
+ @vals = ();
+ $realcount = 0;
+ for ($count = 0; $count <= param($key); $count++)
+ {
+ if (param("$key.$count") ne "")
+ {
+ $vals[$realcount] = param("$key.$count");
+ $realcount++;
+ }
+ if (param("$key.$count.orig") ne param("$key.$count"))
+ {
+ $change = 1;
+ }
+ }
+
+ if ($change == 1)
+ {
+ if ($#vals < 0)
+ {
+ $ldapmod{$key} = "";
+ } else {
+ $ldapmod{$key} = [ @vals ];
+ }
+ }
+ }
+
+ if ($LDAPCGI_ALLOW_CHPASS)
+ {
+ $pass = param("pass");
+ $pass2 = param("pass2");
+
+ if ($pass ne "")
+ {
+ if ($pass eq $pass2)
+ {
+ if ($LDAPCGI_CRYPT_PASS)
+ {
+ $chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
+ srand( time() ^ ($$ + ($$ << 15)));
+ $salt = "";
+ for ($i = 0; $i <2; $i++)
+ {
+ $saltno = int rand length($chars);
+ $mychar = substr($chars,$saltno,1);
+ $salt = $salt . $mychar;
+ }
+ $encpass = crypt $pass, $salt;
+ $ldapmod{'userpassword'} = "{CRYPT}" . $encpass;
+ } else {
+ $ldapmod{'userpassword'} = $pass;
+ }
+ if ($ldap_auth{'dn'} eq $selectdn)
+ {
+ print "<b>NOTICE:</b> You must <a href=",
+ "$LDAPCGI_NAME?op=authenticate>Re-Authenticate</a> to",
+ " make other modifications.",p;
+ }
+ } else {
+ print "<b>WARNING:</b> Passwords did NOT match (not changed)!",p;
+ }
+ }
+ }
+
+ if ($LDAPCGI_ALLOW_JPEGUL)
+ {
+ if (($filename = param('jpegphoto')))
+ {
+ if ($filename =~ /^remove$/i)
+ {
+ $ldapmod{'jpegphoto'} = "";
+ } else {
+ $jpegimg = "";
+ while (read($filename,$buffer,1024))
+ {
+ $jpegimg = $jpegimg . $buffer;
+ }
+ $ldapmod{'jpegphoto'} = {"rb",[$jpegimg]};
+ }
+ }
+ }
+
+ if (ldap_modify_s($ld,$selectdn,\%ldapmod) != LDAP_SUCCESS)
+ {
+ &print_error;
+ ldap_unbind($ld);
+ exit;
+ }
+
+ &post_modify_routine;
+
+ print "<b>Entry Modified...</b>\n";
+ return;
+}
+
+
+####
+# Add User
+####
+
+sub adduser_entry
+{
+ if ($ldap_auth{'dn'} eq $ldap_default_auth{'dn'})
+ {
+ print "Please <a href=$LDAPCGI_NAME?op=authenticate>Authenticate</a>.";
+ ldap_unbind($ld);
+ exit;
+ }
+
+ if (param('addit'))
+ {
+ &add_one_user;
+ ldap_unbind($ld);
+ exit;
+ }
+
+ @locations = sort keys %location;
+
+ print start_form,
+ hidden('op','adduser'),
+ hidden('addit','yes'),
+ hr,
+ "<table>",
+ "<TR><TD VALIGN=TOP>Password:</TD><TD>",password_field('pass'),"</TD></TR>\n",
+ "<TR><TD VALIGN=TOP>Password (again):</TD><TD>",password_field('pass2'),"</TD></TR>\n",
+ "</TABLE>",hr,"<TABLE>";
+
+ foreach $key (@adduser_attributes)
+ {
+ print " <TR><TD VALIGN=TOP>" . $fields{$key}[0] . ":</TD>";
+ if ($fields{$key}[4] > 1)
+ {
+ print " <TD VALIGN=TOP>",textarea("$key","",$fields{$key}[4],$fields{$key}[1],"","wrap=virtual"),"</TD></TR>\n";
+ } else {
+ print " <TD VALIGN=TOP>",textfield("$key","",$fields{$key}[1],$fields{$key}[2]),"</TD></TR>\n";
+ }
+ }
+ print " <TR><TD VALIGN=TOP>Location:</TD><TD VALIGN=TOP>",radio_group('l',[@locations],$locations[0],'true');
+ print "</TABLE>",hr,submit('Add'),reset('Reset'),end_form,hr;
+ ldap_unbind($ld);
+ exit;
+}
+
+####
+# Routine to Add One User
+####
+
+sub add_one_user
+{
+ if (length(param('pass')) < 6)
+ {
+ print "Password must be at least 6 characters in length.\n";
+ return;
+ }
+ if (param('pass') ne param('pass2'))
+ {
+ print "Passwords did not match, please try again.\n";
+ return;
+ }
+
+ $pass = param('pass');
+ if ($LDAPCGI_CRYPT_PASS)
+ {
+ $chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
+ srand(time()^($$+($$<<15)));
+ $salt = "";
+ for ($i = 0; $i <2; $i++)
+ {
+ $saltno = int rand length($chars);
+ $mychar = substr($chars,$saltno,1);
+ $salt = $salt . $mychar;
+ }
+ $encpass = crypt $pass, $salt;
+ $ldapmod{'userpassword'} = "{CRYPT}" . $encpass;
+ } else {
+ $ldapmod{'userpassword'} = $pass;
+ }
+
+ foreach $key (@adduser_attributes)
+ {
+ if (param($key) ne "")
+ {
+ $ldapmod{$key} = param($key);
+ }
+ }
+
+ foreach $key (@adduser_required)
+ {
+ if ($ldapmod{$key} eq "")
+ {
+ print "Missing Required Field: $key\n",p;
+ ldap_unbind($ld);
+ exit;
+ }
+ }
+
+ $ldapmod{'objectclass'} = [ @default_person_objectclass ];
+
+ $l = param('l');
+ $ldapmod{'l'} = $l;
+
+ if ($ldapmod{'uid'} eq "")
+ {
+ $ldapmod{'uid'} = &assign_next_uid($l);
+ }
+
+ &verify_unique;
+
+ $cn = $ldapmod{'givenname'} . " " . $ldapmod{'sn'};
+ $uid = $ldapmod{'uid'};
+ $long_cn = $cn . "-" . $uid;
+
+ $ldapmod{'cn'} = [ ($long_cn, $cn) ];
+
+ $add_dn = "cn=" . $long_cn . "," . $location{$l}[0];
+
+ if (ldap_add_s($ld,$add_dn,\%ldapmod) != LDAP_SUCCESS)
+ {
+ &print_error;
+ ldap_unbind($ld);
+ exit;
+ }
+
+ &post_add_routine;
+
+ print "<b>Entry Added...</b>\n",p;
+ print "DN: $add_dn\n",br;
+ print "UID: $uid\n",p;
+
+ return;
+}
+
+sub assign_next_uid
+{
+ my ($loc) = $_;
+
+ open(READNEXTID,$location{$l}[2]);
+ $nextid = <READNEXTID>;
+ close (READNEXTID);
+ chop $nextid;
+ $uid = $location{$l}[1] . $nextid;
+
+ open(WRITENEXTID,">$location{$l}[2]");
+ print WRITENEXTID $nextid+1 . "\n";
+ close(WRITENEXTID);
+
+ return $uid;
+}
+
+####
+# Delete User
+####
+
+sub deluser_entry
+{
+ $selectdn = param('selectdn');
+ if ($selectdn eq "")
+ {
+ print "Nothing to Delete.\n";
+ return;
+ }
+ print h3("Delete User: $selectdn");
+
+ if (!param('confirm'))
+ {
+ print start_form,
+ hidden('op','deluser'),
+ hidden('confirm','yes'),
+ hidden('selectdn',$selectdn),
+ "WARNING! This will PERMANENTLY remove the entry for:\n",p,
+ $selectdn,p,
+ "Please confirm or click BACK on your browser to cancel.\n",p,
+ submit('Confirm'),
+ end_form;
+ ldap_unbind($ld);
+ exit;
+ }
+
+ if (ldap_delete_s($ld,$selectdn) != LDAP_SUCCESS)
+ {
+ &print_error;
+ ldap_unbind($ld);
+ exit;
+ }
+
+ &post_delete_routine;
+
+ print "DELETED!\n",p,hr;
+ &print_bottom;
+ ldap_unbind($ld);
+ exit;
+}
+
+####
+# Display Full User Entry
+####
+
+sub viewuser_entry
+{
+ @selectdn = param('selectdn');
+ foreach $currentdn (@selectdn)
+ {
+ if (ldap_search_s($ld,$currentdn,LDAP_SCOPE_BASE,"(objectclass=*)",
+ \@searchuser_attributes,0,$results) != LDAP_SUCCESS)
+ {
+ &print_error;
+ ldap_unbind($ld);
+ exit;
+ }
+ $ent = ldap_first_entry($ld,$results);
+ print h3("$currentdn");
+
+ print "<TABLE>\n";
+ for ($attr = ldap_first_attribute($ld,$ent,$ber); $attr ne "";
+ $attr = ldap_next_attribute($ld,$ent,$ber))
+ {
+ if ($attr eq "jpegphoto")
+ {
+ $fulldn = $currentdn;
+ $fulldn =~ s/ /%20/g;
+ $fulldn =~ s/=/%3D/g;
+ print "<TR><img src='$LDAPCGI_NAME?op=viewjpeg&selectdn=$fulldn'></TR>\n";
+ } else {
+ @vals = ldap_get_values($ld,$ent,$attr);
+ print "<TR>";
+ if ($fields{$attr})
+ {
+ print "<TD>$fields{$attr}[0]:</TD>";
+ } else {
+ print "<TD>$attr:</TD>";
+ }
+ for ($count = 0; $count <= $#vals; $count++)
+ {
+ if ($attr eq "mail")
+ {
+ print "<TD><a href=mailto:$vals[$count]>$vals[$count]</a></TD>";
+ } elsif ($attr eq "labeleduri") {
+ print "<TD><a href=$vals[$count]>$vals[$count]</a></TD>";
+ } else {
+ $vals[$count] =~ s/\n/<br>/g;
+ print "<TD>$vals[$count]</TD>";
+ }
+ }
+ print "</TR>\n";
+ }
+ }
+ print "</TABLE>\n",hr;
+ }
+ &print_bottom;
+ ldap_unbind($ld);
+ exit;
+}
+
+
+####
+# Display jpegPhoto
+####
+
+sub view_jpegphoto
+{
+
+# Print the image/jpeg Header
+ print header('image/jpeg');
+
+# $selectdn is our currently selected DN.
+ $selectdn = param('selectdn');
+
+# We perform a search for the 'jpegphoto' attribute.
+ if (ldap_search_s($ld,$selectdn,LDAP_SCOPE_BASE,"objectclass=*",
+ ['jpegphoto'],0,$result) != LDAP_SUCCESS)
+ {
+ &print_error;
+ ldap_unbind($ld);
+ exit;
+ }
+
+# Only one entry should match.
+ $ent = ldap_first_entry($ld,$result);
+
+# We use ldap_get_values_len, since jpegphoto is binary.
+ @pics = ldap_get_values_len($ld,$ent,"jpegphoto");
+
+# Print the picture data to STDOUT if it exists.
+ if ($#pics >= 0)
+ {
+ print $pics[0];
+ }
+
+ ldap_unbind($ld);
+ exit;
+}
+
+####
+# Print Authentication Form
+####
+
+sub authenticate
+{
+ print header;
+ print start_html;
+ print h2("Directory Authentication");
+
+# Print the Authentication Form
+ print start_form(-action=>"$LDAPCGI_NAME"),
+ "Login: ",textfield('ldap_myuid'),p,
+ "Password: ",password_field('ldap_mypass'),p,
+ submit('Login'),
+ end_form,hr;
+ &print_bottom;
+ exit;
+}
+
+
+####
+# Print Basic HTML Headers, including Authentication Cookie
+####
+
+sub print_html_headers
+{
+
+# Notice that we print the Cookie containing the authentication information.
+ print header(-cookie=> $ldap_auth_cookie);
+ print start_html($LDAPCGI_TITLE),h1($LDAPCGI_TITLE);
+
+# If the person has authenticated, let them know we know who they are.
+ if ($ldap_auth{'dn'} ne $ldap_default_auth{'dn'})
+ {
+ @splitdn = split(/,/,$ldap_auth{'dn'});
+ $name = $splitdn[0];
+ $name =~ s/.*=//;
+ print "<b>Welcome, $name!</b>",hr;
+ }
+}
+
+####
+# Print the LDAP Error Message
+####
+
+sub print_error
+{
+# ldap_get_lderrno is a Netscape SDK call, but I've made a dummy version
+# for the PERL module, as we need some way to get the numerical error code.
+ $lderr = ldap_get_lderrno($ld,$blah1,$blah2);
+ $errmsg = ldap_err2string($lderr);
+ print p,"\nError: $errmsg\n",p,hr;
+ &print_bottom;
+ return;
+}
+
+sub help_screen
+{
+ print "Online Help is Net Yet Implemented.",p,hr;
+ &print_bottom;
+ exit;
+}
+
+sub print_bad_auth
+{
+ print header;
+ print start_html("Login/Password Incorrect");
+ print h1("Login/Password Incorrect");
+ print "Please <a href=$LDAPCGI_NAME?op=authenticate>Authenticate</a> again.\n",p,hr;
+ &print_bottom;
+ return;
+}
+
+######
+# post_*_routine is used for any actions you want to perform after doing
+# any of these functions. Useful for email/logging and synchronization
+# purposes that you may have.
+######
+
+sub post_add_routine
+{
+ return;
+}
+
+sub post_modify_routine
+{
+ return;
+}
+
+sub post_delete_routine
+{
+ return;
+}
diff --git a/examples/www-ldap.pl b/examples/www-ldap.pl
new file mode 100755
index 0000000..66ebd3f
--- /dev/null
+++ b/examples/www-ldap.pl
@@ -0,0 +1,362 @@
+#!/usr/bin/perl
+#
+# www-ldap.pl - CGI script to allow users with passwords to authenticate
+# and modify their own accounts on an LDAP server.
+#
+# Requires: PERL5 LDAP Module
+# CGI.pm Module
+#
+# Author: Clayton Donley <donley@cig.mot.com>
+#
+
+
+use CGI qw(:standard);
+use Net::LDAPapi;
+
+#
+# These are the only lines you should need to change for normal
+# operation. You'll need to change part of the &bind subroutine if
+# you don't use 'uid' as your unique identifier.
+#
+
+$BASEDN = "o=Org, c=US"; # Set to your top level
+$ldap_server = "localhost"; # Set to your LDAP server
+$problem_mail = "root\@localhost"; # Set to a help desk mail address
+$program_url = "/cgi-bin/www-ldap.pl"; # URL for this program
+
+# The layout for the %field hash is as followed:
+#
+# "attribute",["Description", display_length, max_length, multiple],
+#
+# attribute -> Lower Case Attribute Name
+# Description -> Description of Field for End User
+# display_length -> Number of Columns to Display for Attribute
+# max_length -> Most Characters to Accept for Attribute
+# multiple -> 1 = Multiple Value Attribute, 0 = Single Value Attributes
+
+%field = (
+ "departmentnumber",["Department Number", 10,25,0],
+ "telephonenumber",["Telephone Number", 30, 50,1],
+ "facsimiletelephonenumber",["Fax Number", 30, 50,0],
+ "pager",["Pager Number", 30, 50,0],
+ "mobile",["Mobile Number", 30, 50,0],
+ "labeleduri",["WWW Home Page", 50, 100,1],
+ "title",["Title",50,100,0],
+ "employeenumber",["Employee Number",10,25,0],
+ "l",["City",30,50,0],
+);
+
+# END OF SUGGESTED MODIFICATION AREA
+
+print header;
+
+if (!param())
+{
+ &web_authenticate;
+ &byline;
+ exit;
+} else {
+ $ldap_bind_uid = param('login');
+ $ldap_bind_password = param('password');
+ if ($ldap_bind_uid ne "" && $ldap_bind_password ne "")
+ {
+ if (&bind < 0)
+ {
+ &incorrect_login;
+ }
+ } else {
+ &incorrect_login;
+ }
+ &modify_screen;
+ $ld->unbind;
+ &byline;
+ exit;
+}
+
+sub byline
+{
+ print hr,"LDAP Account Management Tool by <em><a href=mailto:donley\@cig.mot.com>Clayton Donley</a></em>\n",p;
+ return;
+}
+
+sub incorrect_login
+{
+ print start_html('Invalid Username or Password'),
+ h1('Invalid Username or Password'),
+ "The Login or Password you supplied was incorrect. Please ",
+ "click <a href=" . $program_url . ">HERE</a> and try again.\n";
+ exit;
+}
+
+sub web_authenticate
+{
+ print start_html('LDAP Account Maintenance'),
+ h1('LDAP Account Maintenance'),
+ "For Problems with this service, please email <a href=$problem_email>$problem_email</a>.",hr,
+ start_form,
+ "Login: ",textfield('login'),
+ p,
+ "Password: ",password_field('password'),
+ p,
+ submit('Login'),
+ end_form;
+}
+
+sub bind
+{
+
+# First initialize our connection to the LDAP Server and bind anonymously.
+
+ $ld = new Net::LDAPapi($ldap_server);
+
+ if ($ld->bind_s != LDAP_SUCCESS)
+ {
+ print "Error: Unable to Bind Anonymously to the Directory.",p;
+ print "bind_s: $ld->errstring\n";
+ $ld->unbind;
+ return;
+ }
+
+# Since we've entered our UID, not our CN, we must first find the DN of a
+# person who matches the UID in $ldap_bind_uid
+
+ @attrs = ("cn");
+ $filter = "(uid=$ldap_bind_uid)";
+ if ($ld->search_s($BASEDN,LDAP_SCOPE_SUBTREE,$filter,\@attrs,1)
+ != LDAP_SUCCESS)
+ {
+ print "Error: Unable to Search Directory.",p;
+ print "search_s: $ld->errstring\n";
+ $ld->unbind;
+ exit -1;
+ }
+
+# Obtain a pointer to the first entry matching our query. We are making the
+# assumption that since UID means Unique ID that this is the only time we
+# need to do this.
+
+ $ld->first_entry;
+
+ if ($ent != 0)
+ {
+
+# We only need the DN from the entry we matched.
+
+ $dn = $ld->get_dn;
+
+# Attempt to bind with the DN and Password supplied previously.
+
+ if ($ld->bind_s($dn,$ldap_bind_password) != LDAP_SUCCESS)
+ {
+ $ld->unbind;
+ return -1; # Return Failure
+ }
+ return 0; # Return Success
+ }
+ $ld->unbind;
+ return -1; # Return Failure
+}
+
+sub modify_screen
+{
+
+# Print WWW Header
+
+ print start_html("LDAP Account Management for '$ldap_bind_uid'"),
+ h1("LDAP Account Management for: '$ldap_bind_uid'");
+
+# If we've just made changes, jump to the Modify routine.
+
+ if (param('gomodifyit'))
+ {
+ &gomodifyit;
+ }
+
+# Find values for all attributes. Should probably change this.
+
+ @attrs = ();
+
+# Set the query filter to be the userid specified previously
+
+ $filter = "(uid=$ldap_bind_uid)";
+
+# Perform Synchronous LDAP Search
+
+ if ($ld->search_s($dn,LDAP_SCOPE_BASE,$filter,\@attrs,0)
+ != LDAP_SUCCESS)
+ {
+ print "search_s: $ld->errstring\n",p;
+ print "Error: Unable to Search.\n";
+ exit;
+ }
+
+# Since we queried within a specific DN, we will get only 1 match...Put
+# a pointer to that match in $ent
+
+ $ld->first_entry;
+
+# This should never happen in normal use...
+
+ if ($ent == 0)
+ {
+ print "User Not Found...\n",p;
+ return;
+ }
+
+# Cycle through all attributes and place their values in a hash.
+
+ for ($attr = $ld->first_attribute; $attr; $attr = $ld->next_attribute)
+ {
+ @vals = $ld->get_values($attr);
+ $record{$attr} = [ @vals ];
+ }
+
+# Draw up the Web Form
+
+ print start_form,
+ hidden('login',param('login')),
+ hidden('password',param('password')),
+ hidden('gomodifyit','yes');
+
+ print hr,"<TABLE>";
+
+# Password is a Special Case. Since it needs special processing, we
+# do not include it in %fields and thus request it separately.
+
+ print " <TR><TD>New Password:</TD><TD>",password_field('pass'),"</TD></TR>\n";
+ print " <TR><TD>New Password (again):</TD><TD>",password_field('pass2'),"</TD></TR>\n";
+ print "</TABLE><hr><TABLE>\n";
+
+# Now cycle through all keys in %field and construct the form for each
+# attribute to be modified through this page.
+
+ foreach $key (sort keys %field)
+ {
+ $count = 0;
+ for $value ( @{$record{$key}} )
+ {
+ if ($count == 0)
+ {
+ print " <TR><TD>" . $field{$key}[0] . ":</TD>";
+ } else {
+ print " <TR><TD></TD>";
+ }
+ print " <TD>",textfield("$key.$count",$value,$field{$key}[1],$field{$key}[2]),"</TD></TR>\n";
+ print hidden("$key.$count.orig",$value);
+ $count++;
+ }
+ if ($field{$key}[3] == 1 || $count == 0)
+ {
+ if ($count == 0)
+ {
+ print " <TR><TD>" . $field{$key}[0] . ":</TD>";
+ } else {
+ print " <TR><TD></TD>";
+ }
+ print "<TD>",textfield("$key.$count","",$field{$key}[1],$field{$key}[2]),"</TD></TR>\n";
+ }
+ print hidden($key,$count);
+ }
+ print "</TABLE>",hr, submit('Modify Entry'),
+ end_form;
+ return;
+}
+
+
+#
+# Routine to actually modify an LDAP entry. Must have already used the
+# &bind subroutine to bind to the server.
+#
+
+sub gomodifyit
+{
+
+ print hr;
+
+# Build a hash of arrays for the LDAP Modification
+
+ foreach $key (sort keys %field)
+ {
+ $change = 0;
+ @vals = ();
+ $realcount = 0;
+ for ($count = 0; $count <= param($key); $count++)
+ {
+ if (param("$key.$count") ne "")
+ {
+ $vals[$realcount] = param("$key.$count");
+ $realcount++;
+ }
+ if (param("$key.$count.orig") ne param("$key.$count"))
+ {
+ $change = 1;
+ }
+
+ }
+
+# If there is no values, pass an empty scalar.
+
+ if ($change == 1)
+ {
+ if ($#vals < 0)
+ {
+ $ldapmod{$key} = "";
+ } else {
+ $ldapmod{$key} = [ @vals ];
+ }
+ }
+ }
+
+# Lets Check the Password... If non-empty, encrypt and add to %ldapmod
+
+ $pass = param("pass");
+ $pass2 = param("pass2");
+
+ if ($pass eq "")
+ {
+ } else {
+ if ($pass eq $pass2)
+ {
+# Encrypt as necessary...
+ if ($ENCRYPT_PASS == 1)
+ {
+ $chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
+
+# Seeding with time and proccess id is not normally recommended, but we're
+# only generating the salt, not the password.
+
+ srand( time() ^ ($$ + ($$ << 15)) );
+ $salt = "";
+ for ($i = 0; $i <2; $i++)
+ {
+ $saltno = int rand length($chars);
+ $mychar = substr($chars,$saltno,1);
+ $salt = $salt . $mychar;
+ }
+ $pass = crypt $pass, $salt;
+ }
+ $ldapmod{'userPassword'} = "{CRYPT}" . $pass;
+ print "<b>Warning:</b> Click <a href=" . $program_url . ">HERE</a> and login using your new password if you plan to make other changes...\n",p;
+ } else {
+ print "<b>Warning:</b> Passwords Did Not Match...Not Changed...\n",p;
+ }
+ }
+
+# Perform a synchronous MODIFY operation on our $dn
+
+ @change_keys = keys %ldapmod;
+ if ($#change_keys >= 0)
+ {
+ if ($ld->modify_s($dn,\%ldapmod) != LDAP_SUCCESS)
+ {
+ print "\n",p,"Error: Unable to Modify Entry...\n",p;
+ print "modify_s: $ld->errstring\n";
+ exit;
+ }
+# Success!
+ print "<b>Entry Modified...</b>\n";
+ return;
+ } else {
+ print "<b>No Changes Made...</b>\n";
+ }
+}
diff --git a/ldap_compat.h b/ldap_compat.h
new file mode 100644
index 0000000..0a28ead
--- /dev/null
+++ b/ldap_compat.h
@@ -0,0 +1,22 @@
+/****************************************************************************/
+/* ldap_compat.h - Header file to add ldap_*_option support and other */
+/* Functions to NON-Mozilla Development Kits. */
+/* Author: Clayton Donley - donley@wwa.com */
+/* Date: Tue Aug 26 13:13:32 CDT 1997 */
+/****************************************************************************/
+
+#define ldap_memfree(x) Safefree(x)
+
+/*
+ * OpenLDAP already defines these macros
+ */
+
+#ifndef OPENLDAP
+#define LDAP_OPT_DEREF 2
+#define LDAP_OPT_SIZELIMIT 3
+#define LDAP_OPT_TIMELIMIT 4
+#define LDAP_OPT_REFERRALS 8
+
+#define LDAP_OPT_ON 1
+#define LDAP_OPT_OFF 0
+#endif
diff --git a/test.pl b/test.pl
new file mode 100644
index 0000000..637e978
--- /dev/null
+++ b/test.pl
@@ -0,0 +1,130 @@
+# Before `make install' is performed this script should be runnable with
+# `make test'. After `make install' it should work as `perl test.pl'
+
+######################### We start with some black magic to print on failure.
+
+# Change 1..1 below to 1..last_test_to_print .
+# (It may become useful if the test is moved to ./t subdirectory.)
+
+BEGIN { $| = 1; print "1..8\n"; }
+END {print "modinit - not ok\n" unless $loaded;}
+use Net::LDAPapi;
+$loaded = 1;
+print "modinit - ok\n";
+
+######################### End of black magic.
+
+##
+## Change these values for test to work...
+##
+
+print "\nEnter LDAP Server: ";
+chomp($ldap_host = <>);
+print "Enter port: ";
+chomp($ldap_port = <>);
+print "Enter Search Filter (ex. uid=abc123): ";
+chomp($filter = <>);
+print "Enter LDAP Search Base (ex. o=Org, c=US): ";
+chomp($BASEDN = <>);
+print "\n";
+
+if (!$ldap_host)
+{
+ die "Please edit \$BASEDN, \$filter and \$ldap_host in test.pl.\n";
+}
+
+##
+## Initialize LDAP Connection
+##
+
+if (($ld = new Net::LDAPapi(-host=>$ldap_host,-port=>$ldap_port)) == -1)
+{
+ print "open - not ok\n";
+ exit -1;
+}
+print "open - ok\n";
+
+##
+## Bind as DN, PASSWORD (NULL,NULL) on LDAP connection $ld
+##
+
+if ($ld->bind_s != LDAP_SUCCESS)
+{
+ $ld->perror("bind_s");
+ print "bind - not ok\n";
+ exit -1;
+}
+print "bind - ok\n";
+
+##
+## ldap_search_s - Synchronous Search
+##
+
+@attrs = ();
+
+if ($ld->search_s($BASEDN,LDAP_SCOPE_SUBTREE,$filter,\@attrs,0) != LDAP_SUCCESS)
+{
+ $ld->perror("search_s");
+ print "search - not ok\n";
+}
+print "search - ok\n";
+
+##
+## ldap_count_entries - Count Matched Entries
+##
+
+if ($ld->count_entries == -1)
+{
+ ldap_perror($ld,"count_entry");
+ print "count - not ok\n";
+}
+print "count - ok\n";
+
+##
+## first_entry - Get First Matched Entry
+## next_entry - Get Next Matched Entry
+##
+
+ for ($ent = $ld->first_entry; $ent; $ent = $ld->next_entry)
+ {
+
+##
+## ldap_get_dn - Get DN for Matched Entries
+##
+
+ if ($ld->get_dn ne "")
+ {
+ print "getdn - ok\n";
+ } else {
+ $ld->perror("get_dn");
+ print "getdn - not ok\n";
+ }
+
+ if (($attr = $ld->first_attribute) ne "")
+ {
+ print "firstatt - ok\n";
+
+##
+## ldap_get_values
+##
+
+ @vals = $ld->get_values($attr);
+ if ($#vals >= 0)
+ {
+ print "getvals - ok\n";
+ } else {
+ print "getvals - not ok\n";
+ }
+ } else {
+ print "firstattr - not ok\n";
+ }
+
+
+ }
+
+##
+## Unbind LDAP Connection
+##
+
+$ld->unbind();
+
diff --git a/typemap b/typemap
new file mode 100644
index 0000000..4be4277
--- /dev/null
+++ b/typemap
@@ -0,0 +1,25 @@
+int * T_PTR
+char * T_PV
+char ** T_PTR
+char *** T_PTR
+const char * T_PV
+LDAP_CHAR * T_PV
+LDAP_CHAR ** T_PTR
+LDAP * T_PTR
+LDAP ** T_PTR
+LDAPControl * T_PTR
+LDAPControl ** T_PTR
+LDAPControl *** T_PTR
+LDAPMessage * T_PTR
+LDAPMessage ** T_PTR
+BerElement * T_PTR
+BerElement ** T_PTR
+LDAPVersion * T_PTR
+LDAPMod * T_PTR
+struct berval * T_PTR
+struct berval ** T_PTR
+struct timeval * T_PTR
+LDAPDN * T_PTR
+LDAPRDN * T_PTR
+LDAPSortKey ** T_PTR
+LDAPSortKey *** T_PTR