On Tue, Jun 17, 2008 at 5:16 PM, Graham Barr <[EMAIL PROTECTED]> wrote: > On Jun 14, 2008, at 2:01 PM, Mathieu PARENT wrote: >> >> Hi, >> >> I have implemented LDAP Content Synchronization Operation (rfc4533) in >> perl-ldap (See the attached patch). This is used by the OpenLDAP >> server and provide features similar to Persistent Searches in a more >> consistent way. >> >> This is my first try in this code, any feedback is welcome. > > It looks good. However the makefile is generated so we do not need a patch > for that. And you should add your new files to the MANIFEST file.
Thanks. I've corrected this.
>> I don't know how to handle the Sync Info Message which is an LDAP
>> Intermediate Response Message (see 2.5 in RFC). perl-ldap doesn't
>> seems to support intermediate responses in response to LDAP::Search.
>> What is the way to correct this ?
>
> I have not read the RFC, but I assume this does not signal the end of the
> search
> operation, so the callback should be called.
Yes
> What is the content of the
> Intermediate Response Message ? There should probably be a class added to
> represent
> this message and that gets passed to the callback in the same way we do for
> entries and references.
I've added a Net::LDAP::Intermediate class (inspired by
net::LDAP::Control) with the ability to expand (for example
Net::LDAP::Intermediate::SyncInfo), see attached patch (work in
progress). This class is used from Net::LDAP::Search->decode().
The problem is that Convert::ASN1 doesn't understand this quite
complex BER script :
syncInfoValue ::= CHOICE {
newcookie [0] syncCookie,
refreshDelete [1] SEQUENCE {
refreshDeleteCookie syncCookie OPTIONAL,
refreshDeleteDone BOOLEAN -- DEFAULT TRUE
}
refreshPresent [2] SEQUENCE {
refreshDeletecookie syncCookie OPTIONAL,
refreshDeleteDone BOOLEAN -- DEFAULT TRUE
}
syncIdSet [3] SEQUENCE {
cookie syncCookie OPTIONAL,
refreshDeletes BOOLEAN, -- DEFAULT FALSE
syncUUIDs SET OF syncUUID
}
}
with the attached "out" (which is working with enber/unber). Any idea
of where the problem comes from?
>
> Before I add this, I want to make the switch to Git from SVN. This will
> allow
> me to give better attribution to those who make contributions and also for
> people
> to contribute using git's mail patch mechanism which makes things easier.
I'll use git soon.
> I have converted the current SVN repository which you can find here
>
> http://git.goingon.net/gitweb?p=perl-ldap.git;a=summary
>
> Graham.
>
>
Mathieu Parent
out
Description: Binary data
diff -urN --exclude=blib --exclude=debian --exclude '*~' --exclude Makefile libnet-ldap-perl-0.36-old/lib/Net/LDAP/ASN.pm libnet-ldap-perl-0.36/lib/Net/LDAP/ASN.pm
--- libnet-ldap-perl-0.36-old/lib/Net/LDAP/ASN.pm 2008-04-21 17:11:06.000000000 +0200
+++ libnet-ldap-perl-0.36/lib/Net/LDAP/ASN.pm 2008-06-14 14:33:55.000000000 +0200
@@ -1,7 +1,7 @@
package Net::LDAP::ASN;
-$VERSION = "0.06";
+$VERSION = "0.07";
use Convert::ASN1;
@@ -440,6 +440,56 @@
objectName LDAPDN,
attributes PartialAttributeList }
+ -- RFC-4533 LDAP Content Synchronization Operation
+
+ syncUUID ::= OCTET STRING -- (SIZE(16))
+
+ syncCookie ::= OCTET STRING
+
+ syncRequestValue ::= SEQUENCE {
+ mode ENUMERATED {
+ -- 0 unused
+ refreshOnly (1),
+ -- 2 reserved
+ refreshAndPersist (3)
+ }
+ cookie syncCookie OPTIONAL,
+ reloadHint BOOLEAN -- DEFAULT FALSE
+ }
+
+ syncStateValue ::= SEQUENCE {
+ state ENUMERATED {
+ present (0),
+ add (1),
+ modify (2),
+ delete (3)
+ }
+ entryUUID syncUUID,
+ cookie syncCookie OPTIONAL
+ }
+
+ syncDoneValue ::= SEQUENCE {
+ cookie syncCookie OPTIONAL,
+ refreshDeletes BOOLEAN -- DEFAULT FALSE
+ }
+
+ syncInfoValue ::= CHOICE {
+ newcookie [0] syncCookie,
+ refreshDelete [1] SEQUENCE {
+ refreshDeleteCookie syncCookie OPTIONAL,
+ refreshDeleteDone BOOLEAN -- DEFAULT TRUE
+ }
+ refreshPresent [2] SEQUENCE {
+ refreshDeletecookie syncCookie OPTIONAL,
+ refreshDeleteDone BOOLEAN -- DEFAULT TRUE
+ }
+ syncIdSet [3] SEQUENCE {
+ cookie syncCookie OPTIONAL,
+ refreshDeletes BOOLEAN, -- DEFAULT FALSE
+ syncUUIDs SET OF syncUUID
+ }
+ }
+
LDAP_ASN
1;
diff -urN --exclude=blib --exclude=debian --exclude '*~' --exclude Makefile libnet-ldap-perl-0.36-old/lib/Net/LDAP/Constant.pm libnet-ldap-perl-0.36/lib/Net/LDAP/Constant.pm
--- libnet-ldap-perl-0.36-old/lib/Net/LDAP/Constant.pm 2008-04-21 17:11:06.000000000 +0200
+++ libnet-ldap-perl-0.36/lib/Net/LDAP/Constant.pm 2008-06-14 15:57:15.000000000 +0200
@@ -4,7 +4,7 @@
package Net::LDAP::Constant;
-$VERSION = "0.06";
+$VERSION = "0.07";
use Carp;
@@ -481,6 +481,14 @@
=item LDAP_CONTROL_ASSERTION (1.3.6.1.1.12)
+=item LDAP_CONTROL_SYNC (1.3.6.1.4.1.4203.1.9.1.1)
+
+=item LDAP_CONTROL_SYNC_STATE (1.3.6.1.4.1.4203.1.9.1.2)
+
+=item LDAP_CONTROL_SYNC_DONE (1.3.6.1.4.1.4203.1.9.1.3)
+
+=item LDAP_SYNC_INFO (1.3.6.1.4.1.4203.1.9.1.4)
+
=back
=head2 Control constants
@@ -523,6 +531,42 @@
The new password was used too recently.
+=item LDAP_SYNC_NONE (0) [LDAP_CONTROL_SYNC]
+
+=item LDAP_SYNC_REFRESH_ONLY (1) [LDAP_CONTROL_SYNC]
+
+=item LDAP_SYNC_RESERVED (2) [LDAP_CONTROL_SYNC]
+
+=item LDAP_SYNC_REFRESH_AND_PERSIST (3) [LDAP_CONTROL_SYNC]
+
+=item LDAP_SYNC_REFRESH_PRESENTS (0) [LDAP_SYNC_INFO]
+
+=item LDAP_SYNC_REFRESH_DELETES (1) [LDAP_SYNC_INFO]
+
+=item LDAP_TAG_SYNC_NEW_COOKIE (0x80) [LDAP_SYNC_INFO]
+
+=item LDAP_TAG_SYNC_REFRESH_DELETE (0xa1) [LDAP_SYNC_INFO]
+
+=item LDAP_TAG_SYNC_REFRESH_PRESENT (0xa2) [LDAP_SYNC_INFO]
+
+=item LDAP_TAG_SYNC_ID_SET (0xa3) [LDAP_SYNC_INFO]
+
+=item LDAP_TAG_SYNC_COOKIE (0x04) [LDAP_SYNC_INFO]
+
+=item LDAP_TAG_REFRESHDELETES (0x01) [LDAP_SYNC_INFO]
+
+=item LDAP_TAG_REFRESHDONE (0x01) [LDAP_SYNC_INFO]
+
+=item LDAP_TAG_RELOAD_HINT (0x01) [LDAP_CONTROL_SYNC]
+
+=item LDAP_SYNC_PRESENT (0) [LDAP_CONTROL_SYNC_STATE]
+
+=item LDAP_SYNC_ADD (1) [LDAP_CONTROL_SYNC_STATE]
+
+=item LDAP_SYNC_MODIFY (2) [LDAP_CONTROL_SYNC_STATE]
+
+=item LDAP_SYNC_DELETE (3) [LDAP_CONTROL_SYNC_STATE]
+
=back
=head2 Extension OIDs
diff -urN --exclude=blib --exclude=debian --exclude '*~' --exclude Makefile libnet-ldap-perl-0.36-old/lib/Net/LDAP/Control/SyncDone.pm libnet-ldap-perl-0.36/lib/Net/LDAP/Control/SyncDone.pm
--- libnet-ldap-perl-0.36-old/lib/Net/LDAP/Control/SyncDone.pm 1970-01-01 01:00:00.000000000 +0100
+++ libnet-ldap-perl-0.36/lib/Net/LDAP/Control/SyncDone.pm 2008-06-14 17:50:59.000000000 +0200
@@ -0,0 +1,152 @@
+# Copyright (c) 2008 Mathieu Parent <[EMAIL PROTECTED]>. All rights reserved.
+# This program is free software; you can redistribute it and/or
+# modify it under the same terms as Perl itself.
+
+package Net::LDAP::Control::SyncDone;
+
+use vars qw(@ISA $VERSION);
+use Net::LDAP::Control;
+
[EMAIL PROTECTED] = qw(Net::LDAP::Control);
+$VERSION = "0.01";
+
+use Net::LDAP::ASN qw(syncDoneValue);
+use strict;
+
+# use some kind of hack here:
+# - calling the control without args means: response,
+# - giving an argument: means: request
+sub init {
+ my($self) = @_;
+
+ delete $self->{asn};
+
+ unless (exists $self->{value}) {
+ $self->{asn} = {
+ cookie => $self->{cookie} || '',
+ refreshDeletes => $self->{refreshDeletes} || '0',
+ };
+ }
+
+ $self;
+}
+
+sub cookie {
+ my $self = shift;
+ $self->{asn} ||= $syncDoneValue->decode($self->{value});
+ if (@_) {
+ delete $self->{value};
+ return $self->{asn}{cookie} = shift || 0;
+ }
+ $self->{asn}{cookie};
+}
+
+sub refreshDeletes {
+ my $self = shift;
+ $self->{asn} ||= $syncDoneValue->decode($self->{value});
+ if (@_) {
+ delete $self->{value};
+ return $self->{asn}{refreshDeletes} = shift || 0;
+ }
+ $self->{asn}{refreshDeletes};
+}
+
+sub value {
+ my $self = shift;
+
+ exists $self->{value}
+ ? $self->{value}
+ : $self->{value} = $syncDoneValue->encode($self->{asn});
+}
+
+1;
+
+
+__END__
+
+=head1 NAME
+
+Net::LDAP::Control::SyncDone - LDAPv3 Sync Done control object
+
+=head1 SYNOPSIS
+
+ use Net::LDAP;
+ use Net::LDAP::Control::SyncRequest;
+ use Net::LDAP::Constant qw(
+ LDAP_SYNC_REFRESH_ONLY
+ LDAP_SYNC_REFRESH_AND_PERSIST
+ LDAP_SUCCESS );
+
+ $ldap = Net::LDAP->new( "ldap.mydomain.eg" );
+
+ $req = Net::LDAP::Control::SyncRequest->new( mode => LDAP_SYNC_REFRESH_ONLY );
+ my $mesg = $ldap->search(base=> 'dc=mydomain,dc='eg',
+ scope => 'sub',
+ control => [ $req ],
+ callback => \&searchCallback, # call for each entry
+ filter => "(objectClass=*)",
+ attrs => [ '*']);
+ sub searchCallback {
+ my $message = shift;
+ my $entry = shift;
+ my @controls = $message->control;
+
+ if($controls[0]->isa('Net::LDAP::Control::SyncState')) {
+ print "Received Sync State Control\n";
+ print $entry->dn()."\n";
+ print 'State: '.$controls[0]->state."\n".', entryUUID: '.$controls[0]->entryUUID.', cookie: '.$controls[0]->cookie;
+ } elsif($controls[0]->isa('Net::LDAP::Control::SyncDone')) {
+ print "Received Sync Done Control\n";
+ print 'Cookie: '.$controls[0]->cookie.', refreshDeletes: '.$controls[0]->refreshDeletes;
+ }
+ }
+
+=head1 DESCRIPTION
+
+C<Net::LDAP::Control::SyncDone> provides an interface for the creation and
+manipulation of objects that represent the C<Sync Request Control> as described
+by RFC 4533.
+
+=head1 CONSTRUCTOR ARGUMENTS
+
+In addition to the constructor arguments described in
+L<Net::LDAP::Control> the following are provided.
+
+=over 4
+
+=item cookie
+
+=item refreshDeletes
+
+=back
+
+=head1 METHODS
+
+As with L<Net::LDAP::Control> each constructor argument
+described above is also avaliable as a method on the object which will
+return the current value for the attribute if called without an argument,
+and set a new value for the attribute if called with an argument.
+
+=head1 SEE ALSO
+
+L<Net::LDAP>,
+L<Net::LDAP::Control>,
+L<Net::LDAP::Control::SyncRequest>,
+L<Net::LDAP::Control::SyncState>,
+http://www.ietf.org/rfc/rfc4533.txt
+
+=head1 AUTHOR
+
+Mathieu Parent E<lt>[EMAIL PROTECTED]<gt>
+
+Please report any bugs, or post any suggestions, to the perl-ldap mailing list
+E<lt>[EMAIL PROTECTED]<gt>
+
+=head1 COPYRIGHT
+
+Copyright (c) 2008 Mathieu Parent. All rights reserved. This program is
+free software; you can redistribute it and/or modify it under the same
+terms as Perl itself.
+
+=cut
+
diff -urN --exclude=blib --exclude=debian --exclude '*~' --exclude Makefile libnet-ldap-perl-0.36-old/lib/Net/LDAP/Control/SyncRequest.pm libnet-ldap-perl-0.36/lib/Net/LDAP/Control/SyncRequest.pm
--- libnet-ldap-perl-0.36-old/lib/Net/LDAP/Control/SyncRequest.pm 1970-01-01 01:00:00.000000000 +0100
+++ libnet-ldap-perl-0.36/lib/Net/LDAP/Control/SyncRequest.pm 2008-06-14 17:50:52.000000000 +0200
@@ -0,0 +1,165 @@
+# Copyright (c) 2008 Mathieu Parent <[EMAIL PROTECTED]>. All rights reserved.
+# This program is free software; you can redistribute it and/or
+# modify it under the same terms as Perl itself.
+
+package Net::LDAP::Control::SyncRequest;
+
+use vars qw(@ISA $VERSION);
+use Net::LDAP::Control;
+
[EMAIL PROTECTED] = qw(Net::LDAP::Control);
+$VERSION = "0.01";
+
+use Net::LDAP::ASN qw(syncRequestValue);
+use strict;
+
+# use some kind of hack here:
+# - calling the control without args means: response,
+# - giving an argument: means: request
+sub init {
+ my($self) = @_;
+
+ delete $self->{asn};
+
+ unless (exists $self->{value}) {
+ $self->{asn} = {
+ mode => $self->{mode} || '1',
+ cookie => $self->{cookie} || '',
+ reloadHint => $self->{reloadHint} || '0',
+ };
+ }
+
+ $self;
+}
+
+sub mode {
+ my $self = shift;
+ $self->{asn} ||= $syncRequestValue->decode($self->{value});
+ if (@_) {
+ delete $self->{value};
+ return $self->{asn}{mode} = shift || 0;
+ }
+ $self->{asn}{mode};
+}
+
+sub cookie {
+ my $self = shift;
+ $self->{asn} ||= $syncRequestValue->decode($self->{value});
+ if (@_) {
+ delete $self->{value};
+ return $self->{asn}{cookie} = shift || 0;
+ }
+ $self->{asn}{cookie};
+}
+
+sub reloadHint {
+ my $self = shift;
+ $self->{asn} ||= $syncRequestValue->decode($self->{value});
+ if (@_) {
+ delete $self->{value};
+ return $self->{asn}{reloadHint} = shift || 0;
+ }
+ $self->{asn}{reloadHint};
+}
+
+sub value {
+ my $self = shift;
+
+ exists $self->{value}
+ ? $self->{value}
+ : $self->{value} = $syncRequestValue->encode($self->{asn});
+}
+
+1;
+
+
+__END__
+
+=head1 NAME
+
+Net::LDAP::Control::SyncRequest - LDAPv3 Sync Request control object
+
+=head1 SYNOPSIS
+
+ use Net::LDAP;
+ use Net::LDAP::Control::SyncRequest;
+ use Net::LDAP::Constant qw(
+ LDAP_SYNC_REFRESH_ONLY
+ LDAP_SYNC_REFRESH_AND_PERSIST
+ LDAP_SUCCESS );
+
+ $ldap = Net::LDAP->new( "ldap.mydomain.eg" );
+
+ $req = Net::LDAP::Control::SyncRequest->new( mode => LDAP_SYNC_REFRESH_ONLY );
+ my $mesg = $ldap->search(base=> 'dc=mydomain,dc='eg',
+ scope => 'sub',
+ control => [ $req ],
+ callback => \&searchCallback, # call for each entry
+ filter => "(objectClass=*)",
+ attrs => [ '*']);
+ sub searchCallback {
+ my $message = shift;
+ my $entry = shift;
+ my @controls = $message->control;
+
+ if($controls[0]->isa('Net::LDAP::Control::SyncState')) {
+ print "Received Sync State Control\n";
+ print $entry->dn()."\n";
+ print 'State: '.$controls[0]->state."\n".', entryUUID: '.$controls[0]->entryUUID.', cookie: '.$controls[0]->cookie;
+ } elsif($controls[0]->isa('Net::LDAP::Control::SyncDone')) {
+ print "Received Sync Done Control\n";
+ print 'Cookie: '.$controls[0]->cookie.', refreshDeletes: '.$controls[0]->refreshDeletes;
+ }
+ }
+
+=head1 DESCRIPTION
+
+C<Net::LDAP::Control::SyncRequest> provides an interface for the creation and
+manipulation of objects that represent the C<Sync Request Control> as described
+by RFC 4533.
+
+=head1 CONSTRUCTOR ARGUMENTS
+
+In addition to the constructor arguments described in
+L<Net::LDAP::Control> the following are provided.
+
+=over 4
+
+=item mode
+
+=item cookie
+
+=item reloadHint
+
+=back
+
+=head1 METHODS
+
+As with L<Net::LDAP::Control> each constructor argument
+described above is also avaliable as a method on the object which will
+return the current value for the attribute if called without an argument,
+and set a new value for the attribute if called with an argument.
+
+=head1 SEE ALSO
+
+L<Net::LDAP>,
+L<Net::LDAP::Control>,
+L<Net::LDAP::Control::SyncState>,
+L<Net::LDAP::Control::SyncDone>,
+http://www.ietf.org/rfc/rfc4533.txt
+
+=head1 AUTHOR
+
+Mathieu Parent E<lt>[EMAIL PROTECTED]<gt>
+
+Please report any bugs, or post any suggestions, to the perl-ldap mailing list
+E<lt>[EMAIL PROTECTED]<gt>
+
+=head1 COPYRIGHT
+
+Copyright (c) 2008 Mathieu Parent. All rights reserved. This program is
+free software; you can redistribute it and/or modify it under the same
+terms as Perl itself.
+
+=cut
+
diff -urN --exclude=blib --exclude=debian --exclude '*~' --exclude Makefile libnet-ldap-perl-0.36-old/lib/Net/LDAP/Control/SyncState.pm libnet-ldap-perl-0.36/lib/Net/LDAP/Control/SyncState.pm
--- libnet-ldap-perl-0.36-old/lib/Net/LDAP/Control/SyncState.pm 1970-01-01 01:00:00.000000000 +0100
+++ libnet-ldap-perl-0.36/lib/Net/LDAP/Control/SyncState.pm 2008-06-14 17:50:49.000000000 +0200
@@ -0,0 +1,165 @@
+# Copyright (c) 2008 Mathieu Parent <[EMAIL PROTECTED]>. All rights reserved.
+# This program is free software; you can redistribute it and/or
+# modify it under the same terms as Perl itself.
+
+package Net::LDAP::Control::SyncState;
+
+use vars qw(@ISA $VERSION);
+use Net::LDAP::Control;
+
[EMAIL PROTECTED] = qw(Net::LDAP::Control);
+$VERSION = "0.01";
+
+use Net::LDAP::ASN qw(syncStateValue);
+use strict;
+
+# use some kind of hack here:
+# - calling the control without args means: response,
+# - giving an argument: means: request
+sub init {
+ my($self) = @_;
+
+ delete $self->{asn};
+
+ unless (exists $self->{value}) {
+ $self->{asn} = {
+ state => $self->{state} || '',
+ entryUUID => $self->{entryUUID} || '',
+ cookie => $self->{cookie} || '',
+ };
+ }
+
+ $self;
+}
+
+sub state {
+ my $self = shift;
+ $self->{asn} ||= $syncStateValue->decode($self->{value});
+ if (@_) {
+ delete $self->{value};
+ return $self->{asn}{state} = shift || 0;
+ }
+ $self->{asn}{state};
+}
+
+sub entryUUID {
+ my $self = shift;
+ $self->{asn} ||= $syncStateValue->decode($self->{value});
+ if (@_) {
+ delete $self->{value};
+ return $self->{asn}{entryUUID} = shift || 0;
+ }
+ $self->{asn}{entryUUID};
+}
+
+sub cookie {
+ my $self = shift;
+ $self->{asn} ||= $syncStateValue->decode($self->{value});
+ if (@_) {
+ delete $self->{value};
+ return $self->{asn}{cookie} = shift || 0;
+ }
+ $self->{asn}{cookie};
+}
+
+sub value {
+ my $self = shift;
+
+ exists $self->{value}
+ ? $self->{value}
+ : $self->{value} = $syncStateValue->encode($self->{asn});
+}
+
+1;
+
+
+__END__
+
+=head1 NAME
+
+Net::LDAP::Control::SyncState - LDAPv3 Sync State control object
+
+=head1 SYNOPSIS
+
+ use Net::LDAP;
+ use Net::LDAP::Control::SyncRequest;
+ use Net::LDAP::Constant qw(
+ LDAP_SYNC_REFRESH_ONLY
+ LDAP_SYNC_REFRESH_AND_PERSIST
+ LDAP_SUCCESS );
+
+ $ldap = Net::LDAP->new( "ldap.mydomain.eg" );
+
+ $req = Net::LDAP::Control::SyncRequest->new( mode => LDAP_SYNC_REFRESH_ONLY );
+ my $mesg = $ldap->search(base=> 'dc=mydomain,dc='eg',
+ scope => 'sub',
+ control => [ $req ],
+ callback => \&searchCallback, # call for each entry
+ filter => "(objectClass=*)",
+ attrs => [ '*']);
+ sub searchCallback {
+ my $message = shift;
+ my $entry = shift;
+ my @controls = $message->control;
+
+ if($controls[0]->isa('Net::LDAP::Control::SyncState')) {
+ print "Received Sync State Control\n";
+ print $entry->dn()."\n";
+ print 'State: '.$controls[0]->state."\n".', entryUUID: '.$controls[0]->entryUUID.', cookie: '.$controls[0]->cookie;
+ } elsif($controls[0]->isa('Net::LDAP::Control::SyncDone')) {
+ print "Received Sync Done Control\n";
+ print 'Cookie: '.$controls[0]->cookie.', refreshDeletes: '.$controls[0]->refreshDeletes;
+ }
+ }
+
+=head1 DESCRIPTION
+
+C<Net::LDAP::Control::SyncState> provides an interface for the creation and
+manipulation of objects that represent the C<Sync State Control> as described
+by RFC 4533.
+
+=head1 CONSTRUCTOR ARGUMENTS
+
+In addition to the constructor arguments described in
+L<Net::LDAP::Control> the following are provided.
+
+=over 4
+
+=item state
+
+=item entryUIID
+
+=item cookie
+
+=back
+
+=head1 METHODS
+
+As with L<Net::LDAP::Control> each constructor argument
+described above is also avaliable as a method on the object which will
+return the current value for the attribute if called without an argument,
+and set a new value for the attribute if called with an argument.
+
+=head1 SEE ALSO
+
+L<Net::LDAP>,
+L<Net::LDAP::Control>,
+L<Net::LDAP::Control::SyncRequest>,
+L<Net::LDAP::Control::SyncDone>,
+http://www.ietf.org/rfc/rfc4533.txt
+
+=head1 AUTHOR
+
+Mathieu Parent E<lt>[EMAIL PROTECTED]<gt>
+
+Please report any bugs, or post any suggestions, to the perl-ldap mailing list
+E<lt>[EMAIL PROTECTED]<gt>
+
+=head1 COPYRIGHT
+
+Copyright (c) 2008 Mathieu Parent. All rights reserved. This program is
+free software; you can redistribute it and/or modify it under the same
+terms as Perl itself.
+
+=cut
+
diff -urN --exclude=blib --exclude=debian --exclude '*~' --exclude Makefile libnet-ldap-perl-0.36-old/lib/Net/LDAP/Control.pm libnet-ldap-perl-0.36/lib/Net/LDAP/Control.pm
--- libnet-ldap-perl-0.36-old/lib/Net/LDAP/Control.pm 2008-04-21 17:11:06.000000000 +0200
+++ libnet-ldap-perl-0.36/lib/Net/LDAP/Control.pm 2008-06-17 19:25:48.000000000 +0200
@@ -21,9 +21,12 @@
LDAP_CONTROL_PASSWORDPOLICY
LDAP_CONTROL_PREREAD
LDAP_CONTROL_POSTREAD
+ LDAP_CONTROL_SYNC
+ LDAP_CONTROL_SYNC_STATE
+ LDAP_CONTROL_SYNC_DONE
);
-$VERSION = "0.08";
+$VERSION = "0.09";
my %Pkg2Type = (
@@ -49,6 +52,10 @@
'Net::LDAP::Control::PreRead' => LDAP_CONTROL_PREREAD,
'Net::LDAP::Control::PostRead' => LDAP_CONTROL_POSTREAD,
+
+ 'Net::LDAP::Control::SyncRequest' => LDAP_CONTROL_SYNC,
+ 'Net::LDAP::Control::SyncState' => LDAP_CONTROL_SYNC_STATE,
+ 'Net::LDAP::Control::SyncDone' => LDAP_CONTROL_SYNC_DONE,
#
#LDAP_CONTROL_PWEXPIRED
#LDAP_CONTROL_PWEXPIRING
@@ -294,10 +301,16 @@
L<Net::LDAP::Control::EntryChange>
L<Net::LDAP::Control::ManageDsaIT>
L<Net::LDAP::Control::Paged>
+L<Net::LDAP::Control::PasswordPolicy>
L<Net::LDAP::Control::PersistentSearch>
+L<Net::LDAP::Control::PostRead>
+L<Net::LDAP::Control::PreRead>
L<Net::LDAP::Control::ProxyAuth>
L<Net::LDAP::Control::Sort>
L<Net::LDAP::Control::SortResult>
+L<Net::LDAP::Control::SyncDone>
+L<Net::LDAP::Control::SyncRequest>
+L<Net::LDAP::Control::SyncState>
L<Net::LDAP::Control::VLV>
L<Net::LDAP::Control::VLVResponse>
diff -urN --exclude=blib --exclude=debian --exclude '*~' --exclude Makefile libnet-ldap-perl-0.36-old/lib/Net/LDAP/Intermediate/SyncInfo.pm libnet-ldap-perl-0.36/lib/Net/LDAP/Intermediate/SyncInfo.pm
--- libnet-ldap-perl-0.36-old/lib/Net/LDAP/Intermediate/SyncInfo.pm 1970-01-01 01:00:00.000000000 +0100
+++ libnet-ldap-perl-0.36/lib/Net/LDAP/Intermediate/SyncInfo.pm 2008-06-17 22:35:08.000000000 +0200
@@ -0,0 +1,140 @@
+# Copyright (c) 2008 Mathieu Parent <[EMAIL PROTECTED]>. All rights reserved.
+# This program is free software; you can redistribute it and/or
+# modify it under the same terms as Perl itself.
+
+package Net::LDAP::Intermediate::SyncInfo;
+
+use vars qw(@ISA $VERSION);
+use Net::LDAP::Intermediate;
+
[EMAIL PROTECTED] = qw(Net::LDAP::Intermediate);
+$VERSION = "0.01";
+
+use Net::LDAP::ASN qw(syncInfoValue);
+use strict;
+
+# use some kind of hack here:
+# - calling the control without args means: response,
+# - giving an argument: means: request
+sub init {
+ my($self) = @_;
+
+ delete $self->{asn};
+
+ unless (exists $self->{responseValue}) {
+ $self->{asn} = {
+ newcookie => $self->{newcookie} || '',
+ };
+ }
+
+ $self;
+}
+
+sub newcookie {
+ my $self = shift;
+ $self->{asn} ||= $syncInfoValue->decode($self->{responseValue});
+ if (@_) {
+ delete $self->{responseValue};
+ return $self->{asn}{newcookie} = shift || 0;
+ }
+ $self->{asn}{cookie};
+}
+
+sub responseValue {
+ my $self = shift;
+
+ exists $self->{responseValue}
+ ? $self->{responseValue}
+ : $self->{responseValue} = $syncInfoValue->encode($self->{asn});
+}
+
+1;
+
+
+__END__
+
+=head1 NAME
+
+Net::LDAP::Intermediate::SyncInfo - LDAPv3 Sync Info Message object
+
+=head1 SYNOPSIS
+
+ use Net::LDAP;
+ use Net::LDAP::Control::SyncRequest;
+ use Net::LDAP::Constant qw(
+ LDAP_SYNC_REFRESH_ONLY
+ LDAP_SYNC_REFRESH_AND_PERSIST
+ LDAP_SUCCESS );
+
+ $ldap = Net::LDAP->new( "ldap.mydomain.eg" );
+
+ $req = Net::LDAP::Control::SyncRequest->new( mode => LDAP_SYNC_REFRESH_ONLY );
+ my $mesg = $ldap->search(base=> 'dc=mydomain,dc='eg',
+ scope => 'sub',
+ control => [ $req ],
+ callback => \&searchCallback, # call for each entry
+ filter => "(objectClass=*)",
+ attrs => [ '*']);
+ sub searchCallback {
+ my $message = shift;
+ my $entry = shift;
+ my @controls = $message->control;
+
+ if($controls[0]->isa('Net::LDAP::Control::SyncState')) {
+ print "Received Sync State Control\n";
+ print $entry->dn()."\n";
+ print 'State: '.$controls[0]->state."\n".', entryUUID: '.$controls[0]->entryUUID.', cookie: '.$controls[0]->cookie;
+ } elsif($controls[0]->isa('Net::LDAP::Control::SyncDone')) {
+ print "Received Sync Done Control\n";
+ print 'Cookie: '.$controls[0]->cookie.', refreshDeletes: '.$controls[0]->refreshDeletes;
+ }
+ }
+
+=head1 DESCRIPTION
+
+C<Net::LDAP::Intermediate::SyncInfo> provides an interface for the creation and
+manipulation of objects that represent the C<Sync Info Message> as described
+by RFC 4533.
+
+=head1 CONSTRUCTOR ARGUMENTS
+
+In addition to the constructor arguments described in
+L<Net::LDAP::Intermediate> the following are provided.
+
+=over 4
+
+=item TODO
+
+=back
+
+=head1 METHODS
+
+As with L<Net::LDAP::Intermediate> each constructor argument
+described above is also avaliable as a method on the object which will
+return the current value for the attribute if called without an argument,
+and set a new value for the attribute if called with an argument.
+
+=head1 SEE ALSO
+
+L<Net::LDAP>,
+L<Net::LDAP::Intermediate>,
+L<Net::LDAP::Control>,
+L<Net::LDAP::Control::SyncRequest>,
+L<Net::LDAP::Control::SyncState>,
+http://www.ietf.org/rfc/rfc4533.txt
+
+=head1 AUTHOR
+
+Mathieu Parent E<lt>[EMAIL PROTECTED]<gt>
+
+Please report any bugs, or post any suggestions, to the perl-ldap mailing list
+E<lt>[EMAIL PROTECTED]<gt>
+
+=head1 COPYRIGHT
+
+Copyright (c) 2008 Mathieu Parent. All rights reserved. This program is
+free software; you can redistribute it and/or modify it under the same
+terms as Perl itself.
+
+=cut
+
diff -urN --exclude=blib --exclude=debian --exclude '*~' --exclude Makefile libnet-ldap-perl-0.36-old/lib/Net/LDAP/Intermediate.pm libnet-ldap-perl-0.36/lib/Net/LDAP/Intermediate.pm
--- libnet-ldap-perl-0.36-old/lib/Net/LDAP/Intermediate.pm 1970-01-01 01:00:00.000000000 +0100
+++ libnet-ldap-perl-0.36/lib/Net/LDAP/Intermediate.pm 2008-06-17 22:16:00.000000000 +0200
@@ -0,0 +1,236 @@
+# Copyright (c) 2008 Mathieu Parent <[EMAIL PROTECTED]>. All rights reserved.
+# This program is free software; you can redistribute it and/or
+# modify it under the same terms as Perl itself.
+
+package Net::LDAP::Intermediate;
+
+use vars qw($VERSION);
+use strict;
+
+use Net::LDAP::Constant qw(
+ LDAP_SYNC_INFO
+);
+
+$VERSION = "0.01";
+
+my %Class2ResponseName = (
+
+ #'Net::LDAP::Intermediate::SyncInfo' => LDAP_SYNC_INFO, #disabled as decoding doesn't work
+);
+
+my %ResponseName2Class = reverse %Class2ResponseName;
+
+sub register {
+ my($class,$responseName) = @_;
+
+ require Carp and Carp::croak("$responseName is already registered to $ResponseName2Class{$responseName}")
+ if exists $ResponseName2Class{$responseName} and $ResponseName2Class{$responseName} ne $class;
+
+ require Carp and Carp::croak("$class is already registered to $Class2ResponseName{$class}")
+ if exists $Class2ResponseName{$class} and $Class2ResponseName{$class} ne $responseName;
+
+ $ResponseName2Class{$responseName} = $class;
+ $Class2ResponseName{$class} = $responseName;
+}
+
+sub new {
+ my $self = shift;
+ my $class = ref($self) || $self;
+ my $responseName = (@_ & 1) ? shift : undef;
+ my %args = @_;
+
+ $args{'responseName'} ||= $responseName || $Class2ResponseName{$class} || '';
+
+ unless ($args{responseName} =~ /^\d+(?:\.\d+)+$/) {
+ $args{error} = 'Invalid responseName';
+ return bless \%args;
+ }
+
+ if ($class eq __PACKAGE__ and exists $ResponseName2Class{$args{responseName}}) {
+ $class = $ResponseName2Class{$args{responseName}};
+ eval "require $class" or die $@;
+ }
+
+ delete $args{error};
+
+ bless(\%args, $class)->init;
+}
+
+
+sub from_asn {
+ my $self = shift;
+ my $asn = shift;
+ my $class = ref($self) || $self;
+
+ if ($class eq __PACKAGE__ and exists $ResponseName2Class{$asn->{responseName}}) {
+ $class = $ResponseName2Class{$asn->{responseName}};
+ eval "require $class" or die $@;
+ }
+
+ delete $asn->{error};
+
+ bless($asn, $class)->init;
+}
+
+sub to_asn {
+ my $self = shift;
+ $self->responseValue; # Ensure value is there
+ $self;
+}
+
+sub responseName { shift->{responseName} }
+
+sub responseValue {
+ my $self = shift;
+ $self->{responseValue} = shift if @_;
+ $self->{responseValue} || undef
+}
+
+sub valid { ! exists shift->{error} }
+sub error { shift->{error} }
+sub init { shift }
+
+1;
+
+__END__
+
+
+=head1 NAME
+
+Net::LDAP::Intermediate - LDAPv3 intermediate response object base class
+
+=head1 SYNOPSIS
+
+ use Net::LDAP::Intermediate;
+
+=head1 DESCRIPTION
+
+C<Net::LDAP::Intermediate> is a base-class for LDAPv3 intermediate response objects.
+
+=cut
+
+##
+## Need more blurb in here about intermediate responses
+##
+
+=head1 CONSTRUCTORS
+
+=over 4
+
+=item new ( ARGS )
+
+ARGS is a list of name/value pairs, valid arguments are:
+
+=over 4
+
+=item responseName
+
+A dotted-decimal representation of an OBJECT IDENTIFIER which
+uniquely identifies the intermediate response. This prevents conflicts between
+intermediate response names.
+
+=item responseValue
+
+Optional information associated with the intermediate response. It's format is specific
+to the particular intermediate response.
+
+=back
+
+=item from_asn ( ASN )
+
+ASN is a HASH reference, normally extracted from a PDU. It will contain
+a C<responseName> element and optionally C<responseValue> element. On
+return ASN will be blessed into a package. If C<responseName> is a registered
+OID, then ASN will be blessed into the registered package, if not then ASN
+will be blessed into Net::LDAP::Intermediate.
+
+This constructor is used internally by Net::LDAP and assumes that HASH
+passed contains a valid intermediate response. It should be used with B<caution>.
+
+=back
+
+=head1 METHODS
+
+In addition to the methods listed below, each of the named parameters
+to C<new> is also avaliable as a method. C<responseName> will return the OID of
+the intermediate response object. C<responseValue> is set/get methods and will
+return the current value for each attribute if called without arguments,
+but may also be called with arguments to set new values.
+
+=over 4
+
+=item error ()
+
+If there has been an error returns a description of the error, otherwise it will
+return C<undef>
+
+=item init ()
+
+C<init> will be called as the last step in both contructors. What it does will depend
+on the sub-class. It must always return the object.
+
+=item register ( OID )
+
+C<register> is provided for sub-class implementors. It should be called as a class method
+on a sub-class of Net::LDAP::Intermediate with the OID that the class will handle. Net::LDAP::Intermediate
+will remember this class and OID pair and use it in the following
+situations.
+
+=over 4
+
+=item *
+
+C<new> is called as a class method on the Net::LDAP::Intermediate package and OID is passed
+as the responseName. The returned object will be blessed into the package that registered
+the OID.
+
+=item *
+
+C<new> is called as a class method on a registered package and the C<responseName> is not
+specified. The C<responseName> will be set to the OID registered by that package.
+
+=item *
+
+C<from_asn> is called to construct an object from ASN. The returned object will be
+blessed into the package which was registered to handle the OID in the ASN.
+
+=back
+
+=item ( to_asn )
+
+Returns a structure suitable for passing to Convert::ASN1 for
+encoding. This method will be called by L<Net::LDAP> when the
+intermediate response is used.
+
+The base class implementation of this method will call the C<responseValue> method
+without arguments to allow a sub-class to encode it's value. Sub-classes
+should not need to override this method.
+
+=item valid ()
+
+Returns true if the object is valid and can be encoded. The default implementation
+for this method is to return TRUE if there is no error, but sub-classes may override that.
+
+=back
+
+=head1 SEE ALSO
+
+L<Net::LDAP>
+L<Net::LDAP::Extension>
+L<Net::LDAP::Search>
+L<Net::LDAP::Intermediate::SyncInfo>
+
+=head1 AUTHOR
+
+Mathieu Parent E<lt>[EMAIL PROTECTED]<gt>
+
+Please report any bugs, or post any suggestions, to the perl-ldap mailing list
+E<lt>[EMAIL PROTECTED]<gt>
+
+=head1 COPYRIGHT
+
+Copyright (c) 2008 Mathieu Parent. All rights reserved. This program is
+free software; you can redistribute it and/or modify it under the same
+terms as Perl itself.
+
+=cut
diff -urN --exclude=blib --exclude=debian --exclude '*~' --exclude Makefile libnet-ldap-perl-0.36-old/lib/Net/LDAP/Search.pm libnet-ldap-perl-0.36/lib/Net/LDAP/Search.pm
--- libnet-ldap-perl-0.36-old/lib/Net/LDAP/Search.pm 2008-04-21 17:11:06.000000000 +0200
+++ libnet-ldap-perl-0.36/lib/Net/LDAP/Search.pm 2008-06-17 21:30:46.000000000 +0200
@@ -8,11 +8,12 @@
use vars qw(@ISA $VERSION);
use Net::LDAP::Message;
use Net::LDAP::Entry;
+use Net::LDAP::Intermediate;
use Net::LDAP::Filter;
use Net::LDAP::Constant qw(LDAP_SUCCESS LDAP_DECODING_ERROR);
@ISA = qw(Net::LDAP::Message);
-$VERSION = "0.11";
+$VERSION = "0.12";
sub first_entry { # compat
@@ -63,6 +64,17 @@
return $self;
}
+ elsif ($data = delete $result->{protocolOp}{intermediateResponse}) {
+
+ my $intermediate = Net::LDAP::Intermediate->from_asn($data);
+
+ push(@{$self->{'intermediate'} ||= []}, [$intermediate]);
+
+ $self->{callback}->($self, $intermediate)
+ if (defined $self->{callback});
+
+ return $self;
+ }
$self->set_error(LDAP_DECODING_ERROR, "LDAP decode error");
return;
diff -urN --exclude=blib --exclude=debian --exclude '*~' --exclude Makefile libnet-ldap-perl-0.36-old/MANIFEST libnet-ldap-perl-0.36/MANIFEST
--- libnet-ldap-perl-0.36-old/MANIFEST 2008-04-21 17:14:14.000000000 +0200
+++ libnet-ldap-perl-0.36/MANIFEST 2008-06-17 19:20:23.000000000 +0200
@@ -60,6 +60,9 @@
lib/Net/LDAP/Control/ProxyAuth.pm
lib/Net/LDAP/Control/Sort.pm
lib/Net/LDAP/Control/SortResult.pm
+lib/Net/LDAP/Control/SyncDone.pm
+lib/Net/LDAP/Control/SyncRequest.pm
+lib/Net/LDAP/Control/SyncState.pm
lib/Net/LDAP/Control/VLV.pm
lib/Net/LDAP/Control/VLVResponse.pm
lib/Net/LDAP/DSML.pm
@@ -74,6 +77,8 @@
lib/Net/LDAP/Filter.pm
lib/Net/LDAP/Filter.pod
lib/Net/LDAP/FilterMatch.pm
+lib/Net/LDAP/Intermediate.pm
+lib/Net/LDAP/Intermediate/SyncInfo.pm
lib/Net/LDAP/LDIF.pm
lib/Net/LDAP/LDIF.pod
lib/Net/LDAP/Message.pm
