Skip Menu |

This queue is for tickets about the perl-ldap CPAN distribution.

Report information
The Basics
Id: 46111
Status: resolved
Priority: 0/
Queue: perl-ldap

People
Owner: Nobody in particular
Requestors: yair.lenga [...] citi.com
Cc:
AdminCc:

Bug Information
Severity: Important
Broken in: (no value)
Fixed in: (no value)



Subject: LWP interface does not uspport LDIF
This is a mixed bug/request. The LWP::Protocol::ldap module will only produce HTML data. While this is useful for certain application, it make it hard to get the actual data. Will it be possible to use LDIF for the output ? Either as a default, or by specifying an extension (mode=ldif, following the mode=a for FTP), or by passing an special header in the LWP request object.
Subject: Re: [rt.cpan.org #46111] LWP interface does not uspport LDIF
Date: Fri, 15 May 2009 08:57:36 -0500
To: bug-perl-ldap [...] rt.cpan.org
From: Graham Barr <gbarr [...] pobox.com>
Attached is an UNTESTED attempt to make it support ldif, I would appreciate any feedback. Currently it uses the extensions part of the url to specify format, you need to pass x-format=ldif extensions are set after the 4th ? eg ldap:///??sub??x-format=ldif see http://www.faqs.org/rfcs/rfc2255.html Although I am tempted to set ldif as the default as it does seem more useful. Graham.

Message body is not shown because sender requested not to inline it.

Graham, Thanks for the prompt response. I tested with RH4 (perl-LDAP 0.31-5). On RH4, I switched the code to use the 5.8 open (and skip the optional IO::Scalar). I made my best effort to implement a fallback for old Perl (before 5.8), but I really do not have any way to test. Also, I removed the call to write_version. The LDIF seems to put the version automatically). 100a101 Show quoted text
> require IO::Scalar;
102,110c103 < my $fh ; < if ( $] < 5.008 ) { < require IO::Scalar; < my $fh = IO::Scalar->new(\$content); < } else { < $fh = new IO::Handle ; < open $fh, '>', \$content or die ; < } ; < --- Show quoted text
> my $fh = IO::Scalar->new(\$content);
112c105 < #REMOVED $ldif->write_version; --- Show quoted text
> $ldif->write_version;
While testing, I'm noticed TWO problems with error handling: if ($mesg->code) { my $res = new HTTP::Response &HTTP::Status::RC_BAD_REQUEST, "LDAP return code " . $ldap->code; $res->content_type("text/plain"); # BAD $res->content($ldap->error); $res->content($mesg->error); return $res; } the '$ldap->error' should be '$mesg->error'. Do you want a separate ticket for this item ? Without it, any error will result little information to the user about the real problem. Most common error for me was bad password/username, or bad permission. Attached is my final version, but it's built from 1.10. Yair
# Copyright (c) 1998-2004 Graham Barr <gbarr@pobox.com>. All rights reserved. # This program is free software; you can redistribute it and/or # modify it under the same terms as Perl itself. package LWP::Protocol::ldap; use Carp (); use HTTP::Status (); use HTTP::Negotiate (); use HTTP::Response (); use LWP::MediaTypes (); require LWP::Protocol; @ISA = qw(LWP::Protocol); $VERSION = "1.11"; use strict; eval { require Net::LDAP; }; my $init_failed = $@ ? $@ : undef; sub request { my($self, $request, $proxy, $arg, $size, $timeout) = @_; $size = 4096 unless $size; LWP::Debug::trace('()'); # check proxy if (defined $proxy) { return new HTTP::Response &HTTP::Status::RC_BAD_REQUEST, 'You can not proxy through the ldap'; } my $url = $request->url; if ($url->scheme ne 'ldap') { my $scheme = $url->scheme; return new HTTP::Response &HTTP::Status::RC_INTERNAL_SERVER_ERROR, "LWP::Protocol::ldap::request called for '$scheme'"; } # check method my $method = $request->method; unless ($method eq 'GET') { return new HTTP::Response &HTTP::Status::RC_BAD_REQUEST, 'Library does not allow method ' . "$method for 'ldap:' URLs"; } if ($init_failed) { return new HTTP::Response &HTTP::Status::RC_INTERNAL_SERVER_ERROR, $init_failed; } my $host = $url->host; my $port = $url->port; my ($user, $password) = split(":", $url->userinfo, 2); # Create an initial response object my $response = new HTTP::Response &HTTP::Status::RC_OK, "Document follows"; $response->request($request); my $ldap = new Net::LDAP($host, port => $port); my $mesg = $ldap->bind($user, password => $password); if ($mesg->code) { my $res = new HTTP::Response &HTTP::Status::RC_BAD_REQUEST, "LDAP return code " . $mesg->code; $res->content_type("text/plain"); $res->content($mesg->error); return $res; } my $dn = $url->dn; my @attrs = $url->attributes; my $scope = $url->scope || "base"; my $filter = $url->filter; my @opts = (scope => $scope); my %extn = $url->extensions; my $format = lc($extn{'x-format'} || 'html'); push @opts, "base" => $dn if $dn; push @opts, "filter" => $filter if $filter; push @opts, "attrs" => \@attrs if @attrs; $mesg = $ldap->search(@opts); if ($mesg->code) { my $res = new HTTP::Response &HTTP::Status::RC_BAD_REQUEST, "LDAP return code " . $mesg->code; $res->content_type("text/plain"); $res->content($mesg->error); return $res; } elsif ($format eq 'ldif') { require Net::LDAP::LDIF; require IO::Scalar; my $content = ''; my $fh = IO::Scalar->new(\$content); my $ldif = Net::LDAP::LDIF->new($fh,"w", version => 1); $ldif->write_version; while(my $entry = $mesg->shift_entry) { $ldif->write_entry($entry); } $ldif->done; close($fh); $response->header('Content-Type' => 'text/ldif'); $response->header('Content-Length', length($content)); $response = $self->collect_once($arg, $response, $content) if ($method ne 'HEAD'); } else { my $content = "<head><title>Directory Search Results</title></head>\n<body>"; my $entry; my $index; for($index = 0 ; $entry = $mesg->entry($index) ; $index++ ) { my $attr; $content .= $index ? qq{<tr><th colspan="2"><hr>&nbsp</tr>\n} : "<table>"; $content .= qq{<tr><th colspan="2">} . $entry->dn . "</th></tr>\n"; foreach $attr ($entry->attributes) { my $vals = $entry->get_value($attr, asref => 1); my $val; $content .= q{<tr><td align="right" valign="top"}; $content .= q{ rowspan="} . scalar(@$vals) . q{"} if (@$vals > 1); $content .= ">" . $attr . "&nbsp</td>\n"; my $j = 0; foreach $val (@$vals) { $val = qq!<a href="$val">$val</a>! if $val =~ /^https?:/; $val = qq!<a href="mailto:$val">$val</a>! if $val =~ /^[-\w]+\@[-.\w]+$/; $content .= "<tr>" if $j++; $content .= "<td>" . $val . "</td></tr>\n"; } } } $content .= "</table>" if $index; $content .= "<hr>"; $content .= $index ? sprintf("%s Match%s found",$index, $index>1 ? "es" : "") : "<b>No Matches found</b>"; $content .= "</body>\n"; $response->header('Content-Type' => 'text/html'); $response->header('Content-Length', length($content)); $response = $self->collect_once($arg, $response, $content) if ($method ne 'HEAD'); } $ldap->unbind; $response; } 1;
Subject: Re: [rt.cpan.org #46111] LWP interface does not uspport LDIF
Date: Fri, 15 May 2009 12:56:59 -0500
To: bug-perl-ldap [...] rt.cpan.org
From: Graham Barr <gbarr [...] pobox.com>
On May 15, 2009, at 12:47 PM, yair via RT wrote: Show quoted text
> Queue: perl-ldap > Ticket <URL: https://rt.cpan.org/Ticket/Display.html?id=46111 > > > Graham, > > Thanks for the prompt response. I tested with RH4 (perl-LDAP 0.31-5). > > On RH4, I switched the code to use the 5.8 open (and skip the optional > IO::Scalar). I made my best effort to implement a fallback for old > Perl > (before 5.8), but I really do not have any way to test. Also, I > removed > the call to write_version. The LDIF seems to put the version > automatically). > > 100a101
>> require IO::Scalar;
> 102,110c103 > < my $fh ; > < if ( $] < 5.008 ) { > < require IO::Scalar; > < my $fh = IO::Scalar->new(\$content); > < } else { > < $fh = new IO::Handle ; > < open $fh, '>', \$content or die ;
That is actually a syntax error in some versions of perl, which was why I opted for IO::Scalar. Of course is it really worth trying to support 5.4 anymore though Show quoted text
> > < } ; > < > ---
>> my $fh = IO::Scalar->new(\$content);
> 112c105 > < #REMOVED $ldif->write_version;
This was there for the case when the search returns no results. Show quoted text
> > ---
>> $ldif->write_version;
> > While testing, I'm noticed TWO problems with error handling: > > if ($mesg->code) { > my $res = new HTTP::Response &HTTP::Status::RC_BAD_REQUEST, > "LDAP return code " . $ldap->code; > $res->content_type("text/plain"); > # BAD $res->content($ldap->error); > $res->content($mesg->error);
as this was copy/paste from the html section I can conclude that nobody uses this module and that we can default to ldif output :) Show quoted text
> > return $res; > } > > the '$ldap->error' should be '$mesg->error'. > > Do you want a separate ticket for this item ? Without it, any error > will result little information to the user about the real problem. > Most > common error for me was bad password/username, or bad permission. > > Attached is my final version, but it's built from 1.10.
Thanks Graham.
Graham, Given the choice of working "out of the box" in perl5.8 (RedHat 4 and RedHat 5), or supporting 5.5, I would vote for making the code work in 5.8 without having to install the (legacy ?) IO::Scalar. Also, I've opened another ticker for adding ldaps support - with some suggestions. If you send me the final version, I'll be happy to test.