File Coverage

blib/lib/DBD/LDAP.pm
Criterion Covered Total %
statement 33 427 7.7
branch 0 152 0.0
condition 0 80 0.0
subroutine 11 36 30.5
pod 0 1 0.0
total 44 696 6.3


line stmt bran cond sub pod time code
1             =head1 NAME
2              
3             DBD::LDAP - Provides an SQL/Perl DBI interface to LDAP
4              
5             =head1 AUTHOR
6              
7             This module is Copyright (C) 2000-2019 by
8              
9             Jim Turner
10              
11             Email: turnerjw784 .att. yahoo dot com
12              
13             All rights reserved.
14              
15             You may distribute this module under the same terms as Perl itself.
16              
17             =head1 PREREQUISITES
18              
19             Convert::ANS1 (required by Net::LDAP)
20             Net::LDAP
21             DBI
22             - an LDAP database to connect to.
23              
24             =head1 SYNOPSIS
25              
26             use DBI;
27             $dbh = DBI->connect("DBI:LDAP:ldapdb",'user','password') #USER LOGIN.
28             or die "Cannot connect as user: " . $DBI::errstr;
29              
30             $dbh = DBI->connect("DBI:LDAP:ldapdb") #ANONYMOUS LOGIN (Read-only).
31             or die "Cannot connect as guest (readonly): " . $DBI::errstr;
32              
33             $sth = $dbh->prepare("select * from people where (cn like 'Smith%')")
34             or die "Cannot prepare: " . $dbh->errstr();
35             $sth->execute() or die "Cannot execute: " . $sth->errstr();
36             while ((@results) = $sth->fetchrow_array)
37             {
38             print "--------------------------------------------------------\n";
39             ++$cnt;
40             while (@results)
41             {
42             print "------>".join('|',split(/\0/, shift(@results)))."\n";
43             }
44             }
45             $sth->finish();
46             $dbh->disconnect();
47              
48             =head1 DESCRIPTION
49              
50             LDAP stands for the "Lightweight Directory Access Protocol". For more information, see: http://www.ogre.com/ldap/docs.html
51              
52             DBD::LDAP is a DBI extension module adding an SQL database interface to
53             standard LDAP databases to Perl's database-independent database interface.
54             You will need access to an existing LDAP database or set up your own using
55             an LDAP server, ie. "OpenLDAP", see (http://www.openldap.org).
56              
57             The main advantage of DBD::LDAP is the ability to query LDAP databases via
58             standard SQL queries in leu of cryptic LDAP "filters". LDAP is optimized for
59             quick lookup of existing data, but DBD::LDAP does support entry inserts,
60             updates, and deletes with commit/rollback via the standard SQL commands!
61              
62             LDAP databases are "heirarchical" in structure, whereas other DBD-supported
63             databases are "relational" and there is no LDAP-equivalent to SQL "tables", so
64             DBD::LDAP maps a "table" interface over the LDAP "tree" via a configuration
65             file you must set up. Each "table" is mapped to a common "base DN". For
66             example, consider a typical LDAP database of employees within different
67             departments within a company. You might have a "company" names "Acme" and
68             the root "dn" of "dc=Acme, dc=com" (Acme.com). Below the company level, are
69             divisions, ie. "Widgets", and "Blivets". Each division would have an entry
70             with a "dn" of "ou=Widgets, dc=Acme, dc=com". Employees within each division
71             could have a "dn" of "cn=John Doe, ou=Widgets, dc=Acme, dc=com".
72             With DBD::LDAP, we could create tables to access these different levels,
73             ie. "top", which would have a "DN" of "dc=Acme, dc=com", "WidgetDivision" for
74             "dc=Acme, dc=com". Tables can also be constained by additional
75             attribute specifications (filters), ie constraining by "objectclass", ie.
76             "(objectclass=person)". Then, doing a "select * from WidgetDivision" would
77             display all "person"s with a "dn" containing ""ou=Widgets, dc=Acme, dc=com".
78              
79             =head1 INSTALLATION
80              
81             Installing this module (and the prerequisites from above) is quite simple. You just fetch the archive, extract it with
82              
83             gzip -cd DBD-LDAP-####.tar.gz | tar xf -
84              
85             -or-
86              
87             tar -xzvf DBD-LDAP-####.tar.gz
88              
89             (this is for Unix users, Windows users would prefer WinZip or something similar) and then enter the following:
90              
91             cd DBD-LDAP-#.###
92             perl Makefile.PL
93             make
94             make test
95              
96             If any tests fail, let me know. Otherwise go on with
97              
98             make install
99              
100             Note that you almost definitely need root or administrator permissions. If you don't have them, read the ExtUtils::MakeMaker man page for details on installing in your own directories.
101              
102             =head1 GETTING STARTED:
103              
104             1) Create a "database", ie. "foo" by creating a text file "foo.ldb". The general format of this file is:
105              
106             ----------------------------------------------------------
107             hostname[;port][:[root-dn][:[loginrule]]]
108             tablename1:[basedn]:[basefilter]:dnattrs:[visableattrs]:[insertattrs]:[ldap_options]
109             tablename2:[basedn]:[basefilter]:dnattrs:[visableattrs]:[insertattrs]:[ldap_options]
110             ...
111             ----------------------------------------------------------
112              
113             represents the ldap server host name.
114             represents the server's port, default is 389.
115             if specified, is appended to the end of each tablename's
116             base-dn.
117             if specified, converts single word "usernames" to the
118             appropriate DN, ie:
119              
120             "cn=*," would convert user name "foo" to "cn=foo, " and
121             append the "" onto that. The asterisk is converted to
122             the user-name specified in the "connect" method. If not specified,
123             the username specified in the "connect" method must be a full DN.
124             If the "" is not specified, then the "" would
125             need to be a full DN.
126              
127             tablename - represents the name to be used in SQL statements for a given
128             set of entries which make up a virtual "table".
129             basedn - if specified, is appended to the "" to make up the
130             common base DN for all entries in this table. If "" is
131             not specified, then a full DN must be specified; otherwise, the
132             default is the root-dn.
133             basefilter - if specified, specifies a filter to be used if no "where"-
134             clause is specified in SQL queries. If a "where"-clause is
135             specified, the resulting filter is "and"-ed with this one. The
136             default is "(objectclass=*)".
137             dnattrs - specifies which attributes that values for which are to be
138             appended to the left of the basedn to create DNs for new entries
139             being inserted into the table.
140             visableattrs - if specified, one or more attributes separated by commas
141             which will be sought when the SQL statement does not specify
142             attributes, ie. "select * from tablename". If not specified, the
143             attributes of the first matching entry are returned and used for
144             all entries matching a given query.
145             insertattrs - if specified, one or more attribute/value combinations to be
146             added to any new entry inserted into the table, usually needed for
147             objectclass values. The attributes and values usually correspond
148             to those specivied in the "". The general format is:
149             attr1=value1[|value2...],attr2=value1...,...
150             These attributes and values will be joined with any user-specified
151             values for these attributes.
152             ldap_options - if specified, can be any one or more of the following:
153              
154             ldap_sizelimit - Limit the number of entries fetch by a query to this
155             number (0 = no limit) - default: 0.
156             ldap_timelimit - Limit the search to this number of seconds per query.
157             (0 = no limit) - default: 0.
158             ldap_scope - specify the "scope" of the search. Values are: "base",
159             "one", and "sub", see Net::LDAP docs. Default is "one",
160             meaning the set of records one level below the basedn. "base"
161             means search only the basedn, and "sub" means the union
162             of entries at the "base" level and "one" level below.
163             ldap_inseparator - specify the separator character/string to be used
164             in queries to separate multiple values being specified for
165             a given attribute. Default is "|".
166             ldap_outseparator - specify the separator character/string to be used
167             in queryies to separate multiple values displayed as a result
168             of a query. Default is "|".
169             ldap_firstonly - only display the 1st value fetched for each attribute
170             per entry. This makes "ldap_outseparator" unnecessary.
171              
172             2) write your script to use DBI, ie:
173              
174             #!/usr/bin/perl
175             use DBI;
176             $dbh = DBI->connect('DBD:LDAP:mydb','me','mypassword') ||
177             die "Could not connect (".$DBI->err.':'.$DBI->errstr.")!";
178             ...
179             #CREATE A TABLE, INSERT SOME RECORDS, HAVE SOME FUN!
180              
181             3) get your application working.
182              
183             =head1 INSERTING, FETCHING AND MODIFYING DATA
184              
185             1st, we'll create a database called "ldapdb" with the tables previously mentioned in the example in the DESCRIPTION section:
186              
187             ----------------- file "ldapdb.ldb" ----------------
188             ldapserver:dc=Acme, dc=com:cn=*,
189             top:::dc
190             WidgetDivision:ou=Widgets, :&(objectclass=top)(objectclass=person):cn:cn,sn,ou,title,telephonenumber,description,objectclass,dn:objectclass=top|person|organizationalPerson:ldap_outseparator => ":"
191             ----------------------------------------------------
192              
193             The following examples insert some data in a table and fetch it back: First all data in the string:
194              
195             $dbh->do(q{
196             INSERT INTO top (ou, cn, objectclass)
197             VALUES ('Widgets', 'WidgetDivision', 'top|organizationalUnit')
198             };
199              
200             Next an example using parameters:
201              
202             $dbh->do("INSERT INTO WidgetDivision (cn,sn,title,telephonenumber) VALUES (?, ?, ?, ?)",
203             'John Doe','DoeJ','Manager','123-1111');
204             $dbh->commit;
205              
206             NOTE: Unlike most other DBD modules which support transactions, changes made do NOT show up until the "commit" function is called, unless "AutoCommit" is set. This is due to the fact that fetches are done from the LDAP server and changes do not take effect there until the Net::LDAP "update" function is called, which is called by "commit".
207              
208             NOTE: The "dn" field is generated automatically from the base "dn" and the dn component fields specified by "dnattrs", If you try to insert a value directly into it, it will be ignored. Also, if not specified, any attribute/value combinations specified in the "insertattrs" option will be added automatically.
209              
210             To retrieve data, you can use the following:
211              
212             my($query) = "SELECT * FROM WidgetDivision WHERE cn like 'John%' ORDER BY cn";
213             my($sth) = $dbh->prepare($query);
214             $sth->execute();
215             while (my $entry = $sth->fetchrow_hashref) {
216             print("Found result record: cn = ", $entry->{'cn'},
217             ", phone = ", $row->{'telephonenumber'});
218             }
219             $sth->finish();
220              
221             The SQL "SELECT" statement above (combined with the table information in the "ldapdb.ldb" database file would generate and execute the following equivalent LDAP Search:
222              
223             base => 'ou=Widgets, dc=Acme, dc=com',
224             filter => '(&(&(objectclass=top)(objectclass=person))(cn=John*))',
225             scope => 'one',
226             attrs => 'cn,sn,ou,title,telephonenumber,description,objectclass,dn'
227              
228             See the L manpage for details on these methods. See the Data rows are modified with the UPDATE statement:
229              
230             $dbh->do("UPDATE WidgetDivision SET description = 'Outstanding Employee' WHERE cn = 'John Doe'");
231              
232             NOTE: You can NOT change the "dn" field directly - direct changes will be ignored. You change the "rdn" component of the "dn" field by changing the value of the other field(s) which are appended to the base "dn". Also, if not specified, any attribute/value combinations specified in the "insertattrs" option will be added automatically.
233              
234             Likewise you use the DELETE statement for removing entries:
235              
236             $dbh->do("DELETE FROM WidgetDivision WHERE description = 'Outstanding Employee'");
237              
238             =head1 METADATA
239              
240             The following attributes are handled by DBI itself and not by DBD::LDAP, thus they should all work as expected.
241              
242             PrintError
243             RaiseError
244             Warn
245              
246             The following DBI attributes are handled by DBD::LDAP:
247              
248             AutoCommit
249             Works
250              
251             NUM_OF_FIELDS
252             Valid after '$sth->execute'
253              
254             NUM_OF_PARAMS
255             Valid after '$sth->prepare'
256              
257             NAME
258             Valid after '$sth->execute'; undef for Non-Select statements.
259              
260             NULLABLE
261             Not really working. Always returns an array ref of one's, as
262             DBD::LDAP always allows NULL (handled as an empty string).
263             Valid after `$sth->execute'.
264              
265             LongReadLen
266             Should work
267              
268             LongTruncOk
269             Should work
270              
271             These attributes and methods are not supported:
272              
273             bind_param_inout
274             CursorName
275              
276             In addition to the DBI attributes, you can use the following dbh attributes. These attributes are read-only after "connect".
277              
278             ldap_dbuser
279             Current database user.
280              
281             ldap_HOME
282             Environment variable specifying a path to search for LDAP
283             databases (*.ldb) files.
284              
285              
286             =head1 DRIVER PRIVATE METHODS
287              
288             DBI->data_sources()
289             The `data_sources' method returns a list of "databases" (.ldb files)
290             found in the current directory and, if specified, the path in
291             the ldap_HOME environment variable.
292              
293             $dbh->tables()
294             This method returns a list of table names specified in the current
295             database.
296             Example:
297              
298             my($dbh) = DBI->connect("DBI:LDAP:mydatabase",'me','mypswd');
299             my(@list) = $dbh->func('tables');
300              
301             =head1 OTHER SUPPORTING UTILITIES
302              
303             =head1 RESTRICTIONS
304              
305             DBD::LDAP currently treats all data as strings and all fields as VARCHAR(255).
306              
307             Currently, you must define tables manually in the ".ldb" file using your favorite text editor. I hope to add support for the SQL "Create Table", "Alter Table", and "Drop Table" functions to handle this eventually.
308              
309             =head1 TODO
310              
311             "Create Table", "Alter Table", and "Drop Table" SQL functions for creating, altering, and deleting the tables defined in the ".ldb" file.
312              
313             Some kind of datatype support, ie. numeric (for sorting), CHAR for padding, Long/Blob - for >255 chars per field, etc.
314              
315             =head1 KNOWN BUGS
316              
317             none - (yet).
318              
319             =head1 SEE ALSO
320              
321             L, L
322              
323             =cut
324              
325             require DBI;
326              
327             package DBD::LDAP;
328              
329 1     1   703 use strict;
  1         2  
  1         43  
330             #use vars qw($VERSION @ISA @EXPORT @EXPORT_OK);
331 1     1   6 use vars qw($VERSION $err $errstr $state $sqlstate $drh $i $j $dbcnt);
  1         2  
  1         100  
332 1     1   6 no warnings qw (uninitialized);
  1         2  
  1         182  
333              
334             #require Exporter;
335              
336             #@ISA = qw(Exporter AutoLoader);
337             # Items to export into callers namespace by default. Note: do not export
338             # names by default without a very good reason. Use EXPORT_OK instead.
339             # Do not simply export all your public functions/methods/constants.
340             #@EXPORT = qw(
341            
342             #);
343             $VERSION = '0.24';
344              
345             # Preloaded methods go here.
346              
347             $err = 0; # holds error code for DBI::err
348             $errstr = ''; # holds error string for DBI::errstr
349             $sqlstate = '';
350             $drh = undef; # holds driver handle once initialised
351              
352             sub driver{
353 0 0   0 0   return $drh if $drh;
354 0           my($class, $attr) = @_;
355              
356 0           $class .= "::dr";
357              
358             # not a 'my' since we use it above to prevent multiple drivers
359 0           $drh = DBI::_new_drh($class, { 'Name' => 'LDAP',
360             'Version' => $VERSION,
361             'Err' => \$DBD::LDAP::err,
362             'Errstr' => \$DBD::LDAP::errstr,
363             'State' => \$DBD::LDAP::state,
364             'Attribution' => 'DBD::LDAP by Shishir Gurdavaram & Jim Turner',
365             });
366 0           $drh;
367             }
368              
369             #sub AUTOLOAD {
370             # print "***** AUTOLOAD CALLED! *****\n";
371             #}
372              
373             1;
374              
375              
376             package DBD::LDAP::dr; # ====== DRIVER ======
377 1     1   7 use strict;
  1         2  
  1         27  
378 1     1   5 use vars qw($imp_data_size);
  1         1  
  1         1752  
379              
380             $DBD::LDAP::dr::imp_data_size = 0;
381              
382             sub connect
383             {
384 0     0     my($drh, $dbname, $dbuser, $dbpswd, $attr, $old_driver, $connect_meth) = @_;
385 0           my($i, $j);
386              
387             # Avoid warnings for undefined values
388              
389 0   0       $dbuser ||= '';
390 0   0       $dbpswd ||= '';
391              
392 0   0       $ENV{LDAP_HOME} ||= '';
393 0 0         unless (open(DBFILE, "<$ENV{LDAP_HOME}/${dbname}.ldb"))
394             {
395 0 0         unless (open(DBFILE, "<${dbname}.ldb"))
396             {
397 0 0         unless (open(DBFILE, "<$ENV{HOME}/${dbname}.ldb"))
398             {
399 0           $_ = "No such database ($dbname)!";
400 0           DBI::set_err($drh, -1, $_);
401 0 0         warn $_ if ($attr->{PrintError});
402 0           $_ = '-1:'.$_;
403 0           return undef;
404             }
405             }
406             }
407             do
408 0           {
409 0           $_ = ;
410 0           chomp;
411             }
412             while (/^\#/o);
413              
414 0           s#^(\w+)\:\/\/#$1\x02\/\/#o; #PROTECT COLON IN PROTOCOLS (ADDED ON)
415 0           s#\:(\d+)#\x02$1#go; #PROTECT COLON BEFORE PORT#S (ADDED ON)
416 0           my ($ldap_hostname, $ldap_root, $ldap_loginrule) = split(/\:/o);
417 0           $ldap_hostname =~ s/\x02/\:/go;
418              
419 0           my %ldap_tables;
420             my %ldap_ops;
421 0           my ($tablename,$basedn,$dnattbs,$inseparator,$outseparator);
422              
423 0           while ()
424             {
425 0           chomp;
426 0 0         next if (/^\#/o);
427 0           my ($tablename,$basedn,$objclass,$dnattbs,$allattbs,$alwaysinsert,$dbdattbs) = split(/\:/o, $_,7);
428 0 0 0       if ($ldap_root && $basedn !~ /\i/o)
429             {
430 0 0 0       $basedn .= ',' unless ($basedn !~ /\S/o || $basedn =~ /\,\s*$/o);
431 0           $basedn .= $ldap_root;
432             }
433 0 0         $ldap_tables{$tablename} = "$basedn:$objclass:$dnattbs:$allattbs:$alwaysinsert" if ($tablename);
434 0           $ldap_tables{$tablename} =~ s/\/$ldap_root/i;
435 0           eval "\$ldap_ops{$tablename} = \{$dbdattbs\};";
436             }
437              
438             #CREATE A 'BLANK' DBH
439              
440 0 0 0       if ($dbuser && $ldap_loginrule =~ /\*/o)
441             {
442 0           $ldap_loginrule =~ s/\/$ldap_root/gi;
443 0           $_ = $dbuser;
444 0           $dbuser = $ldap_loginrule;
445 0           $dbuser =~ s/\*/$_/g;
446             }
447 0           my ($privateattr) =
448             {
449             'Name' => $ldap_hostname,
450             'user' => $dbuser,
451             'dbpswd' => $dbpswd
452             };
453              
454 0           my $this = DBI::_new_dbh($drh,
455             {
456             'Name' => $ldap_hostname, #LDAP URL!
457             'USER' => $dbuser, #OPTIONAL, '' = ANONYMOUS!
458             'CURRENT_USER' => $dbuser,
459             }
460             );
461 0 0         unless ($this)
462             {
463 0           $_ = "Could not get new dbh handle on \"$ldap_hostname\" (".$@.")!";
464 0           DBI::set_err($drh, -1, $_);
465 0 0         warn $_ if ($attr->{PrintError});
466 0           $_ = '-1:'.$_;
467 0           return undef;
468             }
469              
470              
471 0           my $ldap_hostport = 389;
472 0 0         $ldap_hostport = $1 if ($ldap_hostname =~ s/\;(.*)$//o);
473 0           my $ldap;
474 0           my @connectArgs = ($ldap_hostname);
475 0 0         push (@connectArgs, 'port', $ldap_hostport) unless ($ldap_hostname =~ /\:\d+$/o);
476 0 0         if ($ldap_hostname =~ /^ldaps/o)
477             {
478 0 0 0       unless (defined($attr->{ldaps_capath}) && -d $attr->{ldaps_capath})
479             {
480 0           $_ = "Must specify valid path for \"ldaps_capath\" attribute when using ldaps!";
481 0           DBI::set_err($drh, -1, $_);
482 0 0         warn $_ if ($attr->{PrintError});
483 0           $_ = '-1:'.$_;
484 0           return undef;
485             }
486 0           push (@connectArgs, 'verify', 'require', 'capath', $attr->{ldaps_capath});
487             }
488 0           $ldap = Net::LDAP->new(@connectArgs);
489 0 0         unless ($ldap)
490             {
491 0           $_ = "Could not connect to \"$ldap_hostname\" (".$@.")!";
492 0           DBI::set_err($drh, -1, $_);
493 0 0         warn $_ if ($attr->{PrintError});
494 0           $_ = '-1:'.$_;
495 0           return undef;
496             }
497              
498 0           my $mesg;
499 0 0         if ($dbpswd)
    0          
500             {
501 0           $mesg = $ldap->bind($dbuser, password => $dbpswd);
502             }
503             elsif ($dbuser)
504             {
505 0           $mesg = $ldap->bind($dbuser);
506             }
507             else
508             {
509 0           $mesg = $ldap->bind();
510             }
511 0 0         unless ($mesg)
512             {
513 0           $_ = "Could not bind - \"$ldap_hostname\" (".$mesg->code().':'.$mesg->error().")!";
514 0   0       DBI::set_err($drh, ($mesg->code()||-1), $_);
515 0 0         warn $_ if ($attr->{PrintError});
516 0           $_ = $mesg->code().':'.$_;
517 0           return undef;
518             }
519 0 0         if ($mesg->code())
520             {
521 0           $_ = "Could not bind to \"$ldap_hostname\" (".$mesg->code().':'.$mesg->error().")!";
522 0   0       DBI::set_err($drh, ($mesg->code()||-1), $_);
523 0 0         warn $_ if ($attr->{PrintError});
524 0           $_ = $mesg->code().':'.$_;
525 0           return undef;
526             }
527              
528             #POPULATE INTERNAL HANDLE DATA.
529              
530 0           ++$DBD::LDAP::dbcnt;
531 0           my (@commitqueue) = ();
532 0           $this->STORE('ldap_commitqueue', \@commitqueue);
533 0           $this->STORE('ldap_ldap', $ldap);
534 0           $this->STORE('ldap_mesg', $mesg);
535 0           $this->STORE('ldap_dbname',$dbname);
536 0           $this->STORE('ldap_dbuser',$dbuser);
537 0           $this->STORE('ldap_dbpswd',$dbpswd);
538 0           $this->STORE('ldap_autocommit', 0);
539 0           $this->STORE('ldap_attrhref', $attr);
540 0           $this->STORE('ldap_hostname', $ldap_hostname);
541 0           $this->STORE('ldap_tables', \%ldap_tables);
542 0           $this->STORE('ldap_tablenames', [keys(%ldap_tables)]);
543 0           $this->STORE('ldap_ops', \%ldap_ops);
544 0   0       $this->STORE('AutoCommit', ($attr->{AutoCommit} || 0));
545 0           return $this;
546             }
547              
548             sub data_sources
549             {
550 0     0     my ($self) = shift;
551              
552 0           my (@dsources) = ();
553              
554 0           my $path;
555 0 0         if (defined $ENV{LDAP_HOME})
556             {
557 0           $path = "$ENV{LDAP_HOME}/*.ldb";
558 0           my $code = "while (my \$i = <$path>)\n";
559 0           $code .= <<'END_CODE';
560             {
561             chomp ($i);
562             push (@dsources,"DBI:LDAP:$1") if ($i =~ m#([^\/\.]+)\.ldb$#);
563             }
564             END_CODE
565 0           eval $code;
566 0           $code =~ s/\.ldb([\>\$])/\.LDB$1/g; #HANDLE WINDOWSEY FILENAMES :(
567 0           eval $code;
568             }
569 0           $path = '*.ldb';
570 0           my $code = "while (my \$i = <$path>)\n";
571 0           $code .= <<'END_CODE';
572             {
573             chomp ($i);
574             push (@dsources,"DBI:LDAP:$1") if ($i =~ m#([^\/\.]+)\.ldb$#);
575             }
576             END_CODE
577 0           eval $code;
578 0           $code =~ s/\.ldb([\>\$])/\.LDB$1/g; #HANDLE WINDOWSEY FILENAMES :(
579 0           eval $code;
580 0 0         unless (@dsources)
581             {
582 0 0         if (defined $ENV{HOME})
583             {
584 0           $path = "$ENV{HOME}/*.ldb";
585 0           my $code = "while (my \$i = <$path>)\n";
586 0           $code .= <<'END_CODE';
587             {
588             chomp ($i);
589             push (@dsources,"DBI:LDAP:$1") if ($i =~ m#([^\/\.]+)\.ldb$#);
590             }
591             END_CODE
592 0           eval $code;
593 0           $code =~ s/\.ldb([\>\$])/\.LDB$1/g; #HANDLE WINDOWSEY FILENAMES :(
594 0           eval $code;
595             }
596             }
597 0           return (@dsources);
598             }
599              
600             sub DESTROY
601             {
602 0     0     my($drh) = shift;
603 0           $drh = undef;
604 0           return undef;
605             }
606              
607             sub disconnect_all
608       0     {
609             }
610              
611             sub admin { #I HAVE NO IDEA WHAT THIS DOES!
612 0     0     my($drh) = shift;
613 0           my($command) = shift;
614              
615 0 0 0       my($dbname) = ($command eq 'createdb' || $command eq 'dropdb') ?
616             shift : '';
617 0   0       my($host, $port) = DBD::LDAP->_OdbcParseHost(shift(@_) || '');
618 0   0       my($user) = shift || '';
619 0   0       my($password) = shift || '';
620              
621 0   0       $drh->func(undef, $command,
      0        
      0        
622             $dbname || '',
623             $host || '',
624             $port || '',
625             $user, $password, '_admin_internal');
626             }
627              
628             1;
629              
630              
631             package DBD::LDAP::db; # ====== DATABASE ======
632 1     1   14 use strict;
  1         3  
  1         36  
633 1     1   657 use Net::LDAP;
  1         189503  
  1         5  
634 1     1   580 use JLdap;
  1         3  
  1         50  
635              
636             $DBD::LDAP::db::imp_data_size = 0;
637 1     1   6 use vars qw($imp_data_size);
  1         1  
  1         1607  
638              
639             sub prepare
640             {
641 0     0     my ($resptr, $sqlstr, $attribs) = @_;
642              
643 0           local ($_);
644              
645 0           $sqlstr =~ s/\n/ /go;
646            
647 0           DBI::set_err($resptr, undef);
648 0           my $csr = DBI::_new_sth($resptr, {
649             'Statement' => $sqlstr,
650             });
651              
652 0           my $myldapref = new JLdap;
653 0           $csr->STORE('ldap_ldapdb', $myldapref);
654 0           $csr->STORE('ldap_fetchcnt', 0);
655 0           $csr->STORE('ldap_reslinev','');
656              
657             #NEXT 4 LINES ADDED 20010829!
658              
659 0           $myldapref->{CaseTableNames} = $resptr->{ldap_attrhref}->{ldap_CaseTableNames};
660 0           $myldapref->{ldap_firstonly} = $resptr->{ldap_attrhref}->{ldap_firstonly};
661             $myldapref->{ldap_inseparator} = $resptr->{ldap_attrhref}->{ldap_inseparator}
662 0 0         if ($resptr->{ldap_attrhref}->{ldap_inseparator});
663             $myldapref->{ldap_outseparator} = $resptr->{ldap_attrhref}->{ldap_outseparator}
664 0 0         if ($resptr->{ldap_attrhref}->{ldap_outseparator});
665             $myldapref->{ldap_appendbase2ins} = $resptr->{ldap_attrhref}->{ldap_appendbase2ins}
666 0 0         ? $resptr->{ldap_attrhref}->{ldap_appendbase2ins} : 0;
667              
668 0           $sqlstr =~ /(into|from|update|table|primary_key_info)\s+(\w+)/gio;
669 0           my ($tablename) = $2;
670 0           $csr->STORE('ldap_base', $tablename);
671              
672             #NEXT 5 LINES ADDED 20091105 TO MAKE primary_key_info() FUNCTION WORK:
673 0           my $tablehash = $resptr->FETCH('ldap_tables');
674 0           my $keyfields;
675 0           (undef, undef, $keyfields) = split(/\:/o, $tablehash->{$tablename});
676 0           $myldapref->{'table'} = $tablename;
677 0           $myldapref->{'key_fields'} = $keyfields;
678              
679 0           $myldapref->{ldap_dbh} = $resptr;
680 0           my ($ldap_ops) = $resptr->FETCH('ldap_ops');
681 0           foreach my $i (keys %{$ldap_ops->{$tablename}})
  0            
682             {
683 0 0         $myldapref->{$i} = $ldap_ops->{$tablename}->{$i} if ($i =~ /^ldap_/o);
684             }
685 0           foreach my $i (qw(ldap_sizelimit ldap_timelimit ldap_scope deref typesonly
686             callback))
687             {
688 0 0         $myldapref->{$i} = $attribs->{$i} if (defined $attribs->{$i});
689             }
690              
691             #SET UP STMT. PARAMETERS.
692              
693 0 0         unless (defined $tablehash->{$tablename})
694             {
695 0           DBI::set_err($resptr, -1,
696             "..Could not prepare query - no such table ($tablename)!");
697 0           return undef;
698             }
699 0           my ($ldap) = $resptr->FETCH('ldap_ldap');
700 0           $csr->STORE('ldap_ldap', $ldap);
701 0           $csr->STORE('ldap_params', []);
702 0           $sqlstr =~ s/([\'\"])([^$1]*?)\?([^$1]*?$1)/$1$2\x02$3/g; #PROTECT ? IN QUOTES (DATA)!
703              
704 0           my $num_of_params = ($sqlstr =~ tr/?//);
705 0           $sqlstr =~ s/\x02/\?/go;
706 0           $csr->STORE('NUM_OF_PARAMS', $num_of_params);
707 0           $csr->STORE('ldap_dbh', $resptr);
708 0           return ($csr);
709             }
710              
711             sub commit
712             {
713 0     0     my ($dB) = shift;
714              
715 0           my ($status, $res);
716 0 0         if ($dB->FETCH('AutoCommit'))
717             {
718 0 0         if ($dB->FETCH('Warn'))
719             {
720 0           warn ('Commit ineffective while AutoCommit is ON!');
721 0           return 0;
722             }
723             }
724             else
725             {
726 0           my ($commitqueue) = $dB->FETCH('ldap_commitqueue');
727 0           my ($entry, $ldap);
728 0           while (@{$commitqueue})
  0            
729             {
730 0           $entry = shift(@{$commitqueue});
  0            
731 0           $ldap = shift(@{$commitqueue});
  0            
732 0           $res = ${$entry}->update($$ldap);
  0            
733 0 0         if ($res->is_error)
734             {
735 0   0       DBI::set_err($dB, ($res->code||-1),
736             ("Could not commit - " . $res->code . ': '
737             . $res->error . '!'));
738 0           return (undef);
739             }
740 0 0         if ($commitqueue->[0] =~ /^dn\=(.+)/o)
741             {
742 0           my $newdn = $1;
743 0           shift(@{$commitqueue});
  0            
744 0           $res = ${$ldap}->moddn($$entry, newrdn => $newdn);
  0            
745 0 0         if ($res->is_error)
746             {
747 0   0       DBI::set_err($dB, ($res->code||-1),
748             ("Could not commit new dn - " . $res->code . ': '
749             . $res->error . '!'));
750 0           return (undef);
751             }
752             }
753             }
754             }
755 0           return 1;
756             }
757              
758             sub rollback
759             {
760 0     0     my ($dB) = shift;
761              
762 0 0         if ($dB->FETCH('AutoCommit'))
763             {
764 0 0 0       if ($dB->FETCH('AutoCommit') && $dB->FETCH('Warn'))
765             {
766 0           warn ('Rollback ineffective while AutoCommit is ON!');
767 0           return 0;
768             }
769             }
770             else
771             {
772 0           my ($commitqueue) = $dB->FETCH('ldap_commitqueue');
773 0           @{$commitqueue} = ();
  0            
774             }
775 0           return 1;
776             }
777              
778             sub STORE
779             {
780 0     0     my($dbh, $attr, $val) = @_;
781 0 0         if ($attr eq 'AutoCommit')
782             {
783             # AutoCommit is currently the only standard attribute we have
784             # to consider.
785              
786 0 0 0       $dbh->commit() if ($val == 1 && !$dbh->FETCH('AutoCommit'));
787 0           $dbh->{AutoCommit} = $val;
788 0           return 1;
789             }
790 0 0         if ($attr =~ /^ldap/o)
791             {
792             # Handle only our private attributes here
793             # Note that we could trigger arbitrary actions.
794             # Ideally we should catch unknown attributes.
795              
796 0           $dbh->{$attr} = $val; # Yes, we are allowed to do this,
797 0           return 1; # but only for our private attributes
798             }
799             # Else pass up to DBI to handle for us
800 0           $dbh->SUPER::STORE($attr, $val);
801             }
802              
803             sub FETCH
804             {
805 0     0     my($dbh, $attr) = @_;
806 0 0         if ($attr eq 'AutoCommit') { return $dbh->{AutoCommit}; }
  0            
807 0 0         if ($attr =~ /^ldap_/o)
808             {
809             # Handle only our private attributes here
810             # Note that we could trigger arbitrary actions.
811              
812 0           return $dbh->{$attr}; # Yes, we are allowed to do this,
813             # but only for our private attributes
814 0           return $dbh->{$attr};
815             }
816             # Else pass up to DBI to handle
817 0           $dbh->SUPER::FETCH($attr);
818             }
819              
820             sub disconnect
821             {
822 0     0     my ($db) = shift;
823 0           my ($ldap) = $db->FETCH('ldap_ldap');
824              
825 0 0         $ldap->unbind() if ($ldap);
826 0           DBI::set_err($db, undef);
827 0           return (1); #20000114: MAKE WORK LIKE DBI!
828             }
829              
830             sub do
831             {
832 0     0     my ($dB, $sqlstr, $attr, @bind_values) = @_;
833 0 0         my ($csr) = $dB->prepare($sqlstr, $attr) or return undef;
834              
835 0           DBI::set_err($dB, undef);
836            
837 0   0       return ($csr->execute(@bind_values) or undef);
838             }
839              
840             sub table_info
841             {
842 0     0     my($dbh) = @_; # XXX add qualification
843 0 0         my $sth = $dbh->prepare('select tables')
844             or return undef;
845 0 0         $sth->execute or return undef;
846 0           $sth;
847             }
848              
849             sub primary_key_info #ADDED 20091105 TO SUPPORT DBI primary_key/primary_key_info FUNCTIONS!
850             {
851 0     0     my ($dbh, $cat, $schema, $tablename) = @_;
852 0 0         my $sth = $dbh->prepare("PRIMARY_KEY_INFO $tablename")
853             or return undef;
854 0 0         $sth->execute() or return undef;
855 0           return $sth;
856             }
857              
858             sub type_info_all #ADDED 20010312, BORROWED FROM "Oracle.pm".
859             {
860 0     0     my ($dbh) = @_;
861 0           my $names =
862             {
863             TYPE_NAME => 0,
864             DATA_TYPE => 1,
865             COLUMN_SIZE => 2,
866             LITERAL_PREFIX => 3,
867             LITERAL_SUFFIX => 4,
868             CREATE_PARAMS => 5,
869             NULLABLE => 6,
870             CASE_SENSITIVE => 7,
871             SEARCHABLE => 8,
872             UNSIGNED_ATTRIBUTE => 9,
873             FIXED_PREC_SCALE =>10,
874             AUTO_UNIQUE_VALUE =>11,
875             LOCAL_TYPE_NAME =>12,
876             MINIMUM_SCALE =>13,
877             MAXIMUM_SCALE =>14,
878             }
879             ;
880             # Based on the values from Oracle 8.0.4 ODBC driver
881 0           my $ti = [
882             $names,
883             [ 'VARCHAR', 12, 255, '\'', '\'', 'max length', 1, '0', 3,
884             undef, '0', '0', undef, undef, undef
885             ]
886             ];
887 0           return $ti;
888             }
889             sub tables #CONVENIENCE METHOD FOR FETCHING LIST OF TABLES IN THE DATABASE.
890             {
891 0     0     my($dbh) = @_; # XXX add qualification
892              
893 0           my ($tables) = $dbh->FETCH('ldap_tablenames');
894 0           my (@tables) = @{$tables};
  0            
895 0 0         return undef unless ($#tables >= 0);
896 0           return (@tables);
897             }
898              
899             sub rows
900             {
901 0     0     return $DBI::rows;
902             }
903              
904             sub DESTROY
905             {
906 0     0     my($drh) = shift;
907 0           my ($ldap) = $drh->FETCH('ldap_ldap');
908 0 0         if ($drh->FETCH('AutoCommit') != 1)
909             {
910 0           $drh->rollback(); #ROLL BACK ANYTHING UNCOMMITTED IF AUTOCOMMIT OFF!
911             }
912              
913 0           $drh->disconnect();
914 0           $drh = undef;
915 0           return undef;
916             }
917              
918             1;
919              
920              
921             package DBD::LDAP::st; # ====== STATEMENT ======
922 1     1   8 use strict;
  1         1  
  1         68  
923              
924             my (%typehash) = (
925             'LONG RAW' => -4,
926             'RAW' => -3,
927             'LONG' => -1,
928             'CHAR' => 1,
929             'NUMBER' => 3,
930             'DOUBLE' => 8,
931             'DATE' => 11,
932             'VARCHAR' => 12,
933             'BOOLEAN' => -7, #ADDED 20000308!
934             );
935              
936             $DBD::LDAP::st::imp_data_size = 0;
937 1     1   6 use vars qw($imp_data_size *fetch);
  1         2  
  1         1586  
938              
939             sub bind_param
940             {
941 0     0     my($sth, $pNum, $val, $attr) = @_;
942 0 0         my $type = (ref $attr) ? $attr->{TYPE} : $attr;
943              
944 0 0         if ($type)
945             {
946 0           my $dbh = $sth->{Database};
947 0           $val = $dbh->quote($sth, $type);
948             }
949 0           my $params = $sth->FETCH('ldap_params');
950 0           $params->[$pNum-1] = $val;
951              
952 0           ${$sth->{bindvars}}[($pNum-1)] = $val; #FOR LDAP.
  0            
953              
954 0           $sth->STORE('ldap_params', $params);
955 0           return 1;
956             }
957              
958             sub execute
959             {
960 0     0     my ($sth, @bind_values) = @_;
961             #print STDERR "-execute1($sth,".join(',',@bind_values).")\n";
962 0 0         my $params = (@bind_values) ?
963             \@bind_values : $sth->FETCH('ldap_params');
964              
965 0           for (my $i=0;$i<=$#{$params};$i++) #ADDED 20000303 FIX QUOTE PROBLEM WITH BINDS.
  0            
966             {
967 0           $params->[$i] =~ s/\'/\'\'/go;
968             }
969              
970 0           my $numParam = $sth->FETCH('NUM_OF_PARAMS');
971              
972 0 0 0       if ($params && scalar(@$params) != $numParam) #CHECK FOR RIGHT # PARAMS.
973             {
974 0           DBI::set_err($sth, (scalar(@$params)-$numParam),
975             "..execute: Wrong number of bind variables (".(scalar(@$params)-$numParam)." too many!)");
976 0           return undef;
977             }
978 0           my $sqlstr = $sth->{'Statement'};
979              
980             #NEXT 8 LINES ADDED 20010911 TO FIX BUG WHEN QUOTED VALUES CONTAIN "?"s.
981 0           $sqlstr =~ s/\\\'/\x03/go; #PROTECT ESCAPED DOUBLE-QUOTES.
982 0           $sqlstr =~ s/\'\'/\x04/go; #PROTECT DOUBLED DOUBLE-QUOTES.
983 0           $sqlstr =~ s/\'([^\']*?)\'/
984 0           my ($str) = $1;
985 0           $str =~ s|\?|\x02|go; #PROTECT QUESTION-MARKS WITHIN QUOTES.
986 0           "'$str'"/eg;
987 0           $sqlstr =~ s/\x04/\'\'/go; #UNPROTECT DOUBLED DOUBLE-QUOTES.
988 0           $sqlstr =~ s/\x03/\\\'/go; #UNPROTECT ESCAPED DOUBLE-QUOTES.
989              
990             #CONVERT REMAINING QUESTION-MARKS TO BOUND VALUES.
991              
992 0           for (my $i = 0; $i < $numParam; $i++)
993             {
994 0           $params->[$i] =~ s/\?/\x02\^2jSpR1tE\x02/gs; #ADDED 20091030 TO FIX BUG WHEN PARAMETER OTHER THAN LAST CONTAINS A "?"!
995 0           $sqlstr =~ s/\?/"'".$params->[$i]."'"/e;
  0            
996             }
997 0           $sqlstr =~ s/\x02\^2jSpR1tE\x02/\?/gs; #ADDED 20091030! - UNPROTECT PROTECTED "?"s.
998 0           my ($ldapref) = $sth->FETCH('ldap_ldapdb');
999              
1000             #CALL JLDAP TO DO THE SQL!
1001              
1002 0           my (@resv) = $ldapref->sql($sth, $sqlstr);
1003 0           my $saveAT = $@;
1004             #print STDERR "-execute4 at=$@\n";
1005              
1006             #!!! HANDLE LDAP ERRORS HERE (SEE LDAP.PM)!!!
1007            
1008 0           my ($retval) = undef;
1009 0 0         if ($#resv < 0) #GENERAL ERROR!
    0          
1010             {
1011             DBI::set_err($sth, ($ldapref->{lasterror} || -601),
1012 0   0       ($ldapref->{lastmsg} || 'Unknown Error!'));
      0        
1013 0   0       $@ ||= $saveAT;
1014 0           return $retval;
1015             }
1016             elsif ($resv[0]) #NORMAL ACTION IF NON SELECT OR >0 ROWS SELECTED.
1017             {
1018 0           $retval = $resv[0];
1019 0           my $dB = $sth->{Database};
1020              
1021             # if ($dB->FETCH('AutoCommit') == 1 && $sth->FETCH('Statement') !~ /^\s*select/i) #CHGD. TO NEXT 20091105:
1022 0 0 0       if ($dB->FETCH('AutoCommit') == 1 && $sth->FETCH('Statement') !~ /^\s*(?:select|primary_key_info)/io)
1023             {
1024             #print STDERR "!!!!!!!!!!!! clearing Autocommit drh=$dB= !!!!!!!!!!!!!\n";
1025 0           $dB->STORE('AutoCommit',0); #ADDED 20010911 AS PER SPRITE TO MAKE AUTOCOMMIT WORK.
1026 0           $dB->commit(); #ADDED 20010911 AS PER SPRITE TO MAKE AUTOCOMMIT WORK.
1027 0           $dB->STORE('AutoCommit',1); #COMMIT DONE HERE!
1028             }
1029            
1030             }
1031             else #SELECT SELECTED ZERO RECORDS.
1032             {
1033 0 0         if ($ldapref->{lasterror})
1034             {
1035 0           DBI::set_err($sth, $ldapref->{lasterror}, $ldapref->{lastmsg});
1036 0   0       $@ ||= $saveAT;
1037 0           $retval = undef;
1038             }
1039 0           $retval = '0E0';
1040             # $resv[0] = $ldapref->{lastmsg};
1041             # DBI::set_err($sth, ($ldapref->{lasterror} || -402),
1042             # ($ldapref->{lastmsg} || 'No matching records found/modified!'));
1043             # $retval = '0E0';
1044             }
1045            
1046             #EVERYTHING WORKED, SO SAVE LDAP RESULT (# ROWS) AND FETCH FIELD INFO.
1047            
1048 0           $sth->{'driver_rows'} = $resv[0]; # number of rows
1049 0           $sth->{'ldap_rows'} = $resv[0]; # number of rows #ADDED 20050416 PER PACH BY jmorano
1050              
1051             #### NOTE #### IF THIS FAILS, IT PROBABLY NEEDS TO BE "ldap_rows"?
1052            
1053 0           shift @resv; #REMOVE 1ST COLUMN FROM DATA RETURNED (THE LDAP RESULT).
1054              
1055 0           my @l = split(/,/o, $ldapref->{use_fields});
1056 0           $sth->STORE('NUM_OF_FIELDS',($#l+1));
1057 0 0         unless ($ldapref->{TYPE})
1058             {
1059 0           @{$ldapref->{NAME}} = @l;
  0            
1060 0           for my $i (0..$#l)
1061             {
1062 0           ${$ldapref->{TYPE}}[$i] = $typehash{${$ldapref->{types}}{$l[$i]}}
1063 0   0       || $typehash{'VARCHAR'};
1064 0   0       ${$ldapref->{PRECISION}}[$i] = ${$ldapref->{lengths}}{$l[$i]}
  0            
1065             || 255;
1066 0   0       ${$ldapref->{SCALE}}[$i] = ${$ldapref->{scales}}{$l[$i]} || 0;
  0            
1067 0           ${$ldapref->{NULLABLE}}[$i] = 1;
  0            
1068             #${$ldapref->{TYPE}}[$i] = 12; #VARCHAR
1069             ##${$ldapref->{TYPE}}[$i] = -1; #VARCHAR #NEXT 4 REPLACED BY 1ST 4 PER REQUEST BY jmorano.
1070             ##${$ldapref->{PRECISION}}[$i] = 255;
1071             ##${$ldapref->{SCALE}}[$i] = 0;
1072             ##${$ldapref->{NULLABLE}}[$i] = 1;
1073             }
1074             }
1075              
1076             #TRANSFER LDAP'S FIELD DATA TO DBI.
1077              
1078 0           $sth->{'driver_data'} = \@resv;
1079 0           $sth->STORE('ldap_data', \@resv);
1080 0           $sth->STORE('ldap_rows', ($#resv+1)); # number of rows
1081 0           $sth->{'TYPE'} = \@{$ldapref->{TYPE}};
  0            
1082 0           $sth->{'NAME'} = \@{$ldapref->{NAME}};
  0            
1083 0           $sth->{'PRECISION'} = \@{$ldapref->{PRECISION}};
  0            
1084 0           $sth->{'SCALE'} = \@{$ldapref->{SCALE}};
  0            
1085 0           $sth->{'NULLABLE'} = \@{$ldapref->{NULLABLE}};
  0            
1086 0           $sth->STORE('ldap_resv',\@resv);
1087 0   0       $@ ||= $saveAT;
1088 0 0         return $retval if ($retval);
1089 0 0         return '0E0' if (defined $retval);
1090 0           return undef;
1091             }
1092              
1093             sub fetchrow_arrayref
1094             {
1095 0     0     my($sth) = @_;
1096 0           my $data = $sth->FETCH('driver_data');
1097 0           my $row = shift @$data;
1098              
1099 0 0         return undef if (!$row);
1100 0           my ($longreadlen) = $sth->{Database}->FETCH('LongReadLen');
1101 0 0         if ($longreadlen > 0)
1102             {
1103 0 0         if ($sth->FETCH('ChopBlanks'))
1104             {
1105 0           for (my $i=0;$i<=$#{$row};$i++)
  0            
1106             {
1107 0 0         if (${$sth->{TYPE}}[$i] < 0) #LONG, LONG RAW, etc.
  0            
1108             {
1109 0           my ($t) = substr($row->[$i],0,$longreadlen);
1110 0 0 0       return undef unless (($row->[$i] eq $t) || $sth->{Database}->FETCH('LongTruncOk'));
1111 0           $row->[$i] = $t;
1112             }
1113             }
1114 0           map { $_ =~ s/\s+$//o; } @$row;
  0            
1115             }
1116             }
1117             else
1118             {
1119 0 0         if ($sth->FETCH('ChopBlanks'))
1120             {
1121 0           map { $_ =~ s/\s+$//o; } @$row;
  0            
1122             }
1123             }
1124              
1125 0           return $sth->_set_fbav($row);
1126             }
1127              
1128             *fetch = \&fetchrow_arrayref; # required alias for fetchrow_arrayref
1129             sub rows
1130             {
1131 0     0     my($sth) = @_;
1132 0           $sth->FETCH('driver_rows');
1133             }
1134              
1135             #### NOTE #### IF THIS FAILS, IT PROBABLY NEEDS TO BE "ldap_rows"?
1136              
1137              
1138             sub STORE
1139             {
1140 0     0     my($dbh, $attr, $val) = @_;
1141 0 0         if ($attr eq 'AutoCommit')
1142             {
1143             # AutoCommit is currently the only standard attribute we have
1144             # to consider.
1145             #if (!$val) { die "Can't disable AutoCommit"; }
1146              
1147 0           $dbh->{AutoCommit} = $val;
1148 0           return 1;
1149             }
1150 0 0         if ($attr =~ /^ldap/o)
1151             {
1152             # Handle only our private attributes here
1153             # Note that we could trigger arbitrary actions.
1154             # Ideally we should catch unknown attributes.
1155              
1156 0           $dbh->{$attr} = $val; # Yes, we are allowed to do this,
1157 0           return 1; # but only for our private attributes
1158             }
1159             # Else pass up to DBI to handle for us
1160 0           eval {$dbh->SUPER::STORE($attr, $val);};
  0            
1161             }
1162              
1163             sub FETCH
1164             {
1165 0     0     my($dbh, $attr) = @_;
1166 0 0         if ($attr eq 'AutoCommit') { return $dbh->{AutoCommit}; }
  0            
1167 0 0         if ($attr =~ /^ldap_/o)
1168             {
1169             # Handle only our private attributes here
1170             # Note that we could trigger arbitrary actions.
1171              
1172 0           return $dbh->{$attr}; # Yes, we are allowed to do this,
1173             # but only for our private attributes
1174 0           return $dbh->{$attr};
1175             }
1176             # Else pass up to DBI to handle
1177 0           $dbh->SUPER::FETCH($attr);
1178             }
1179              
1180              
1181             1;
1182              
1183             package DBD::LDAP; # ====== HAD TO HAVE TO PREVENT MAKE ERROR! ======
1184              
1185             1;
1186              
1187             __END__