summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRichard Bos <richard@radoeka.nl>2008-10-16 18:58:23 (GMT)
committerRichard Bos <richard@radoeka.nl>2008-10-16 18:58:23 (GMT)
commit645bd715ab4b9d627d5022fd40653ea72701fe7a (patch)
treef50291bcfed95b607ba43fbcda01ce0d517a69dd
parentb27b1d92c80d2e5977b620e57fdcf05129ebd2c2 (diff)
downloadperl-Kolab-645bd715ab4b9d627d5022fd40653ea72701fe7a.tar.gz
Updated with changes provided by Mathieu Parent. See kolab/issue1755
-rw-r--r--ChangeLog6
-rw-r--r--lib/Kolab/LDAP/Backend/syncrepl.pm243
2 files changed, 113 insertions, 136 deletions
diff --git a/ChangeLog b/ChangeLog
index 640a2a2..ac47314 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,9 @@
+2008-10-16 Richard Bos <richard@radoeka.nl>
+
+ * lib/Kolab/LDAP/Backend/syncrepl.pm: updated. Updates provided by
+ Mathieu Parent
+
+
2008-10-10 Gunnar Wrobel <p@rdus.de>
* lib/Kolab.pm: Allow returning only the global information when
diff --git a/lib/Kolab/LDAP/Backend/syncrepl.pm b/lib/Kolab/LDAP/Backend/syncrepl.pm
index 872d60e..0a78c48 100644
--- a/lib/Kolab/LDAP/Backend/syncrepl.pm
+++ b/lib/Kolab/LDAP/Backend/syncrepl.pm
@@ -21,8 +21,13 @@ use strict;
use warnings;
use Kolab;
use Kolab::LDAP;
-use Net::LDAP;
+use Net::LDAP qw(
+ LDAP_USER_CANCELED
+ LDAP_SYNC_REFRESH_ONLY
+ LDAP_SYNC_REFRESH_AND_PERSIST
+);
use Net::LDAP::Control;
+use Net::LDAP::Control::SyncRequest;
use Net::LDAP::Entry;
use vars qw($ldap $cookie $disconnected);
my $cookie = '';
@@ -45,97 +50,13 @@ our @EXPORT = qw(
);
-our $VERSION = '0.1';
-
-# LDAP Content Synchronization Operation -- RFC 4533
-use constant LDAP_SYNC_OID => "1.3.6.1.4.1.4203.1.9.1";
-use constant {
- LDAP_CONTROL_SYNC => LDAP_SYNC_OID.".1",
- LDAP_CONTROL_SYNC_STATE => LDAP_SYNC_OID.".2",
- LDAP_CONTROL_SYNC_DONE => LDAP_SYNC_OID.".3",
- LDAP_SYNC_INFO => LDAP_SYNC_OID.".4",
-
- LDAP_SYNC_NONE => 0x00,
- LDAP_SYNC_REFRESH_ONLY => 0x01,
- LDAP_SYNC_RESERVED => 0x02,
- LDAP_SYNC_REFRESH_AND_PERSIST => 0x03,
-
- LDAP_SYNC_REFRESH_PRESENTS => 0,
- LDAP_SYNC_REFRESH_DELETES => 1,
-
- LDAP_TAG_SYNC_NEW_COOKIE => 0x80,
- LDAP_TAG_SYNC_REFRESH_DELETE => 0xa1,
- LDAP_TAG_SYNC_REFRESH_PRESENT => 0xa2,
- LDAP_TAG_SYNC_ID_SET => 0xa3,
-
- LDAP_TAG_SYNC_COOKIE => 0x04,
- LDAP_TAG_REFRESHDELETES => 0x01,
- LDAP_TAG_REFRESHDONE => 0x01,
- LDAP_TAG_RELOAD_HINT => 0x01,
+our $VERSION = '0.2';
- LDAP_SYNC_PRESENT => 0,
- LDAP_SYNC_ADD => 1,
- LDAP_SYNC_MODIFY => 2,
- LDAP_SYNC_DELETE => 3,
-};
-
-use Convert::ASN1;
-use Data::Dumper;
-
-my $asn = Convert::ASN1->new;
-
-$asn->prepare(<<'LDAP_ASN') or die $asn->error;
-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
+sub mode {
+ LDAP_SYNC_REFRESH_ONLY;
+ #LDAP_SYNC_REFRESH_AND_PERSIST;
}
-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
-
-
sub startup { 1; }
sub shutdown
@@ -184,22 +105,17 @@ sub run {
Kolab::LDAP::ensureAsync($ldap);
Kolab::log('SYNCREPL', 'Async checked', KOLAB_DEBUG);
- Kolab::log('SYNCREPL', "Cookie: $cookie", KOLAB_DEBUG);
-
while($ldap and not $disconnected) {
- #sync control
- my $asn_syncRequestValue = $asn->find('syncRequestValue');
- my $ctrl = Net::LDAP::Control->new(type => LDAP_CONTROL_SYNC,
- value => $asn_syncRequestValue->encode(mode => LDAP_SYNC_REFRESH_ONLY,
- cookie => $cookie,
- reloadHint => 0
- ),
- critical => 0
- );
- Kolab::log('SYNCREPL', 'Control created', KOLAB_DEBUG);
-
- #search
- my $mesg = $ldap->search(base => $Kolab::config{'base_dn'},
+ my $ctrl = Net::LDAP::Control::SyncRequest->new(
+ mode => Kolab::LDAP::Backend::syncrepl::mode,
+ cookie => $cookie,
+ reloadHint => 0);
+ Kolab::log('SYNCREPL', 'Control created: mode='.$ctrl->mode().
+ '; cookie='.$ctrl->cookie().
+ '; reloadHint='.$ctrl->reloadHint(), KOLAB_DEBUG);
+
+ #search
+ my $mesg = $ldap->search(base => $Kolab::config{'base_dn'},
scope => 'sub',
control => [ $ctrl ],
callback => \&searchCallback, # call for each entry
@@ -210,7 +126,7 @@ sub run {
$Kolab::config{'user_field_quota'},
$Kolab::config{'user_field_deleted'},
],
- );
+ );
Kolab::log('SYNCREPL', 'Search created', KOLAB_DEBUG);
$mesg->sync;
Kolab::log('SYNCREPL', "Finished Net::LDAP::Search::sync sleeping 10s", KOLAB_DEBUG);
@@ -223,37 +139,53 @@ sub run {
#search callback
sub searchCallback {
my $mesg = shift;
- my $entry = shift;
- my $issearch = $mesg->isa("Net::LDAP::Search");
+ my $param2 = shift; # might be entry or intermediate
my @controls = $mesg->control;
- if(not $issearch) {
- Kolab::log('SYNCREPL', 'mesg is not a search object, testing code...', KOLAB_DEBUG);
- if ($mesg->code == 88) {
- Kolab::log('SYNCREPL', 'searchCallback() -> Exit code received, returning', KOLAB_DEBUG);
- return;
- } elsif ($mesg->code) {
- Kolab::log('SYNCREPL', "Not a search: mesg->code = `" . $mesg->code . "', mesg->msg = `" . $mesg->error . "'", KOLAB_DEBUG);
- &abort;
- }
- } elsif(@controls == 0) {
- if ($mesg->code == 1) {
- Kolab::log('SYNCREPL', 'No control: Communications Error: disconnecting', KOLAB_DEBUG);
- $disconnected = 1;
- return;
- } elsif ($mesg->code) {
- Kolab::log('SYNCREPL', "No control: mesg->code = `" . $mesg->code . "', mesg->msg = `" . $mesg->error . "'", KOLAB_DEBUG);
- &abort;
- }
- } elsif($controls[0]->type eq LDAP_CONTROL_SYNC_STATE) {
- Kolab::log('SYNCREPL', 'Received Sync State Control', KOLAB_DEBUG);
- Kolab::log('SYNCREPL', "Entry (".$entry->changetype."): ".$entry->dn(), KOLAB_DEBUG);
- } elsif($controls[0]->type eq LDAP_CONTROL_SYNC_DONE) {
- Kolab::log('SYNCREPL', 'Received Sync Done Control', KOLAB_DEBUG);
- my $asn_syncDoneValue = $asn->find('syncDoneValue');
- my $out = $asn_syncDoneValue->decode($controls[0]->value);
+ my @sync_controls = ();
+ if($param2 && $param2->isa("Net::LDAP::Entry")) {
+ Kolab::log('SYNCREPL', 'Received Search Entry', KOLAB_DEBUG);
+ #retrieve Sync State Control
+ foreach my $ctrl (@controls) {
+ push(@sync_controls, $ctrl)
+ if $ctrl->isa('Net::LDAP::Control::SyncState');
+ }
+ if(@sync_controls>1) {
+ Kolab::log('SYNCREPL', 'Got search entry with multiple Sync State controls',
+ KOLAB_DEBUG);
+ return;
+ }
+ if(!@sync_controls) {
+ Kolab::log('SYNCREPL', 'Got search entry without Sync State control',
+ KOLAB_DEBUG);
+ return;
+ }
+ if(!$sync_controls[0]->entryUUID) {
+ Kolab::log('SYNCREPL', 'Got empty entryUUID',
+ KOLAB_DEBUG);
+ return;
+ }
+ Kolab::log('SYNCREPL', 'Search Entry has Sync State Control: '.
+ 'state='.$sync_controls[0]->state().
+ '; entryUUID='.unpack("H*",$sync_controls[0]->entryUUID()).
+ '; cookie='.(defined($sync_controls[0]->cookie()) ? $sync_controls[0]->cookie() : 'UNDEF')
+ , KOLAB_DEBUG);
+ if(defined($sync_controls[0]->cookie)) {
+ $cookie = $sync_controls[0]->cookie;
+ Kolab::log('SYNCREPL',"New cookie: $cookie", KOLAB_DEBUG);
+ }
+ Kolab::log('SYNCREPL', "Entry (".$param2->changetype."): ".$param2->dn(), KOLAB_DEBUG);
+ } elsif($param2 && $param2->isa("Net::LDAP::Reference")) {
+ Kolab::log('SYNCREPL', 'Received Search Reference', KOLAB_DEBUG);
+ return;
+ #if it not first control?
+ } elsif($controls[0] and $controls[0]->isa('Net::LDAP::Control::SyncDone')) {
+ Kolab::log('SYNCREPL', 'Received Sync Done Control: '.
+ 'cookie='.(defined($controls[0]->cookie()) ? $controls[0]->cookie() : 'UNDEF').
+ '; refreshDeletes='.$controls[0]->refreshDeletes()
+ , KOLAB_DEBUG);
#we have a new cookie
- if(defined($out->{cookie}) and not $out->{cookie} eq '' and not $out->{cookie} eq $cookie) {
- $cookie = $out->{cookie};
+ if(defined($controls[0]->cookie()) and not $controls[0]->cookie() eq '' and not $controls[0]->cookie() eq $cookie) {
+ $cookie = $controls[0]->cookie();
Kolab::log('SYNCREPL', "New cookie: $cookie", KOLAB_DEBUG);
Kolab::log('SYNCREPL', "Calling Kolab::LDAP::sync", KOLAB_DEBUG);
Kolab::LDAP::sync;
@@ -261,8 +193,47 @@ sub searchCallback {
Kolab::log('SYNCREPL', "Finished Kolab::LDAP::sync sleeping 1s", KOLAB_DEBUG);
sleep 1; # we get too many bogus change notifications!
}
+ } elsif($param2 && $param2->isa("Net::LDAP::Intermediate")) {
+ Kolab::log('SYNCREPL', 'Received Intermediate Message', KOLAB_DEBUG);
+ my $attrs = $param2->{asn};
+ if($attrs->{newcookie}) {
+ $cookie = $attrs->{newcookie};
+ Kolab::log('SYNCREPL', "New cookie: $cookie", KOLAB_DEBUG);
+ } elsif(my $refreshInfos = ($attrs->{refreshDelete} || $attrs->{refreshPresent})) {
+ $cookie = $refreshInfos->{cookie} if defined($refreshInfos->{cookie});
+ Kolab::log('SYNCREPL',
+ (defined($refreshInfos->{cookie}) ? 'New ' : 'Empty ').
+ "cookie from ".
+ ($attrs->{refreshDelete} ? 'refreshDelete' : 'refreshPresent').
+ " (refreshDone=".$refreshInfos->{refreshDone}."): $cookie", KOLAB_DEBUG);
+ } elsif(my $syncIdSetInfos = $attrs->{syncIdSet}) {
+ $cookie = $syncIdSetInfos->{cookie} if defined($syncIdSetInfos->{cookie});
+ Kolab::log('SYNCREPL',
+ (defined($syncIdSetInfos->{cookie}) ? 'Empty ' : 'New ').
+ "cookie from syncIdSet".
+ " (refreshDeletes=".$syncIdSetInfos->{refreshDeletes}."): $cookie", KOLAB_DEBUG);
+ foreach my $syncUUID ($syncIdSetInfos->{syncUUIDs}) {
+ Kolab::log('SYNCREPL', 'entryUUID='.
+ unpack("H*",$syncUUID), KOLAB_DEBUG);
+ }
+ }
+ } elsif($mesg->code) {
+ if ($mesg->code == 1) {
+ Kolab::log('SYNCREPL', 'Communication Error: disconnecting', KOLAB_DEBUG);
+ $disconnected = 1;
+ return 0;
+ } elsif ($mesg->code == LDAP_USER_CANCELED) {
+ Kolab::log('SYNCREPL', 'searchCallback() -> Exit code received, returning', KOLAB_DEBUG);
+ return;
+ } elsif ($mesg->code == 4096) {
+ Kolab::log('SYNCREPL', 'Refresh required', KOLAB_DEBUG);
+ $cookie = '';
+ } else {
+ Kolab::log('SYNCREPL', "searchCallback: mesg->code = `" . $mesg->code . "', mesg->msg = `" . $mesg->error . "'", KOLAB_DEBUG);
+ &abort;
+ }
} else {
- Kolab::log('SYNCREPL', 'Received unknown control: '.$controls[0]->type, KOLAB_DEBUG);
+ Kolab::log('SYNCREPL', 'Received something else', KOLAB_DEBUG);
}
return 0;
}
@@ -315,4 +286,4 @@ However (quoting from RFC, page 21):
-=cut
+=cut \ No newline at end of file