File Coverage

blib/lib/DBD/LDAP.pm
Criterion Covered Total %
statement 33 436 7.5
branch 0 166 0.0
condition 0 86 0.0
subroutine 11 36 30.5
pod 0 1 0.0
total 44 725 6.0


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