| line | stmt | bran | cond | sub | pod | time | code | 
| 1 | 13 |  |  | 13 |  | 526369 | use 5.006; | 
|  | 13 |  |  |  |  | 39 |  | 
|  | 13 |  |  |  |  | 443 |  | 
| 2 | 13 |  |  | 13 |  | 59 | use strict; | 
|  | 13 |  |  |  |  | 16 |  | 
|  | 13 |  |  |  |  | 417 |  | 
| 3 | 13 |  |  | 13 |  | 55 | use warnings; | 
|  | 13 |  |  |  |  | 15 |  | 
|  | 13 |  |  |  |  | 597 |  | 
| 4 |  |  |  |  |  |  |  | 
| 5 |  |  |  |  |  |  | package Test::Net::LDAP::Mock::Data; | 
| 6 | 13 |  |  | 13 |  | 63 | use base qw(Test::Net::LDAP::Mixin); | 
|  | 13 |  |  |  |  | 23 |  | 
|  | 13 |  |  |  |  | 3439 |  | 
| 7 |  |  |  |  |  |  |  | 
| 8 | 13 |  |  | 13 |  | 69 | use Net::LDAP; | 
|  | 13 |  |  |  |  | 17 |  | 
|  | 13 |  |  |  |  | 67 |  | 
| 9 | 13 |  |  |  |  | 1006 | use Net::LDAP::Constant qw( | 
| 10 |  |  |  |  |  |  | LDAP_SUCCESS | 
| 11 |  |  |  |  |  |  | LDAP_COMPARE_TRUE LDAP_COMPARE_FALSE | 
| 12 |  |  |  |  |  |  | LDAP_NO_SUCH_OBJECT LDAP_ALREADY_EXISTS | 
| 13 |  |  |  |  |  |  | LDAP_INVALID_DN_SYNTAX LDAP_PARAM_ERROR | 
| 14 |  |  |  |  |  |  | LDAP_INVALID_CREDENTIALS LDAP_INAPPROPRIATE_AUTH | 
| 15 | 13 |  |  | 13 |  | 768 | ); | 
|  | 13 |  |  |  |  | 22 |  | 
| 16 | 13 |  |  | 13 |  | 4495 | use Net::LDAP::Entry; | 
|  | 13 |  |  |  |  | 20248 |  | 
|  | 13 |  |  |  |  | 348 |  | 
| 17 | 13 |  |  | 13 |  | 6548 | use Net::LDAP::Filter; | 
|  | 13 |  |  |  |  | 26281 |  | 
|  | 13 |  |  |  |  | 381 |  | 
| 18 | 13 |  |  | 13 |  | 6285 | use Net::LDAP::FilterMatch; | 
|  | 13 |  |  |  |  | 62233 |  | 
|  | 13 |  |  |  |  | 100 |  | 
| 19 | 13 |  |  |  |  | 916 | use Net::LDAP::Util qw( | 
| 20 |  |  |  |  |  |  | canonical_dn escape_dn_value ldap_explode_dn | 
| 21 | 13 |  |  | 13 |  | 43835 | ); | 
|  | 13 |  |  |  |  | 24 |  | 
| 22 | 13 |  |  | 13 |  | 70 | use Scalar::Util qw(blessed); | 
|  | 13 |  |  |  |  | 20 |  | 
|  | 13 |  |  |  |  | 1055 |  | 
| 23 | 13 |  |  | 13 |  | 219 | use Test::Net::LDAP::Util; | 
|  | 13 |  |  |  |  | 19 |  | 
|  | 13 |  |  |  |  | 34762 |  | 
| 24 |  |  |  |  |  |  |  | 
| 25 |  |  |  |  |  |  | my %scope = qw(base  0 one    1 single 1 sub    2 subtree 2); | 
| 26 |  |  |  |  |  |  | my %deref = qw(never 0 search 1 find   2 always 3); | 
| 27 |  |  |  |  |  |  | %scope = (%scope, map {$_ => $_} values %scope); | 
| 28 |  |  |  |  |  |  | %deref = (%deref, map {$_ => $_} values %deref); | 
| 29 |  |  |  |  |  |  |  | 
| 30 |  |  |  |  |  |  | sub new { | 
| 31 | 28 |  |  | 28 | 0 | 139 | my ($class, $ldap) = @_; | 
| 32 | 28 |  |  |  |  | 6449 | require Test::Net::LDAP::Mock::Node; | 
| 33 |  |  |  |  |  |  |  | 
| 34 | 28 |  |  |  |  | 181 | my $self = bless { | 
| 35 |  |  |  |  |  |  | root => Test::Net::LDAP::Mock::Node->new, | 
| 36 |  |  |  |  |  |  | ldap => $ldap, | 
| 37 |  |  |  |  |  |  | schema => undef, | 
| 38 |  |  |  |  |  |  | bind_success => 0, | 
| 39 |  |  |  |  |  |  | password_mocked => 0, | 
| 40 |  |  |  |  |  |  | mock_bind_code => LDAP_SUCCESS, | 
| 41 |  |  |  |  |  |  | mock_bind_message => '', | 
| 42 |  |  |  |  |  |  | }, $class; | 
| 43 |  |  |  |  |  |  |  | 
| 44 | 28 |  | 66 |  |  | 318 | $self->{ldap} ||= do { | 
| 45 | 8 |  |  |  |  | 3447 | require Test::Net::LDAP::Mock; | 
| 46 | 8 |  |  |  |  | 62 | my $ldap = Test::Net::LDAP::Mock->new; | 
| 47 | 8 |  |  |  |  | 17 | $ldap->{mock_data} = $self; | 
| 48 | 8 |  |  |  |  | 11 | $ldap; | 
| 49 |  |  |  |  |  |  | }; | 
| 50 |  |  |  |  |  |  |  | 
| 51 | 28 |  |  |  |  | 117 | return $self; | 
| 52 |  |  |  |  |  |  | } | 
| 53 |  |  |  |  |  |  |  | 
| 54 |  |  |  |  |  |  | sub root { | 
| 55 | 133 |  |  | 133 | 0 | 443 | shift->{root}; | 
| 56 |  |  |  |  |  |  | } | 
| 57 |  |  |  |  |  |  |  | 
| 58 |  |  |  |  |  |  | sub schema { | 
| 59 | 159 |  |  | 159 | 0 | 135 | my $self = shift; | 
| 60 |  |  |  |  |  |  |  | 
| 61 | 159 | 50 |  |  |  | 224 | if (@_) { | 
| 62 | 0 |  |  |  |  | 0 | my $schema = $self->{schema}; | 
| 63 | 0 |  |  |  |  | 0 | $self->{schema} = $_[0]; | 
| 64 | 0 |  |  |  |  | 0 | return $schema; | 
| 65 |  |  |  |  |  |  | } else { | 
| 66 | 159 |  |  |  |  | 229 | return $self->{schema}; | 
| 67 |  |  |  |  |  |  | } | 
| 68 |  |  |  |  |  |  | } | 
| 69 |  |  |  |  |  |  |  | 
| 70 |  |  |  |  |  |  | sub ldap { | 
| 71 | 215 |  |  | 215 | 0 | 190 | my $self = shift; | 
| 72 |  |  |  |  |  |  |  | 
| 73 | 215 | 50 |  |  |  | 365 | if (@_) { | 
| 74 | 0 |  |  |  |  | 0 | my $ldap = $self->{ldap}; | 
| 75 | 0 |  |  |  |  | 0 | $self->{ldap} = $_[0]; | 
| 76 | 0 |  |  |  |  | 0 | return $ldap; | 
| 77 |  |  |  |  |  |  | } else { | 
| 78 | 215 |  |  |  |  | 858 | return $self->{ldap}; | 
| 79 |  |  |  |  |  |  | } | 
| 80 |  |  |  |  |  |  | } | 
| 81 |  |  |  |  |  |  |  | 
| 82 |  |  |  |  |  |  | sub root_dse { | 
| 83 | 1 |  |  | 1 | 0 | 6 | my $self = shift; | 
| 84 | 1 |  |  |  |  | 4 | $self->ldap->root_dse(@_); | 
| 85 |  |  |  |  |  |  | } | 
| 86 |  |  |  |  |  |  |  | 
| 87 |  |  |  |  |  |  | sub mock_root_dse { | 
| 88 | 1 |  |  | 1 | 0 | 6 | my $self = shift; | 
| 89 | 1 |  |  |  |  | 3 | my $root_node = $self->root; | 
| 90 |  |  |  |  |  |  |  | 
| 91 | 1 | 50 |  |  |  | 9 | if (@_) { | 
| 92 | 1 |  |  |  |  | 450 | require Net::LDAP::RootDSE; | 
| 93 | 1 |  |  |  |  | 206 | my $old_entry = $root_node->entry; | 
| 94 | 1 |  |  |  |  | 1 | my $new_entry; | 
| 95 |  |  |  |  |  |  |  | 
| 96 | 1 | 50 | 33 |  |  | 18 | if ($_[0] && blessed($_[0]) && $_[0]->isa('Net::LDAP::Entry')) { | 
|  |  |  | 33 |  |  |  |  | 
| 97 | 0 |  |  |  |  | 0 | $new_entry = $_[0]->clone; | 
| 98 | 0 |  |  |  |  | 0 | $new_entry->dn(''); | 
| 99 |  |  |  |  |  |  |  | 
| 100 | 0 | 0 |  |  |  | 0 | unless ($new_entry->isa('Net::LDAP::RootDSE')) { | 
| 101 | 0 |  |  |  |  | 0 | bless $new_entry, 'Net::LDAP::RootDSE'; | 
| 102 |  |  |  |  |  |  | } | 
| 103 |  |  |  |  |  |  | } else { | 
| 104 | 1 |  |  |  |  | 9 | $new_entry = Net::LDAP::RootDSE->new('', @_); | 
| 105 |  |  |  |  |  |  | } | 
| 106 |  |  |  |  |  |  |  | 
| 107 | 1 | 50 |  |  |  | 92 | unless ($new_entry->get_value('objectClass')) { | 
| 108 | 1 |  |  |  |  | 18 | $new_entry->add(objectClass => 'top'); | 
| 109 |  |  |  |  |  |  | # Net::LDAP::root_dse uses the filter '(objectclass=*)' to search | 
| 110 |  |  |  |  |  |  | # for the root DSE. | 
| 111 |  |  |  |  |  |  | } | 
| 112 |  |  |  |  |  |  |  | 
| 113 | 1 |  |  |  |  | 25 | $root_node->entry($new_entry); | 
| 114 | 1 |  |  |  |  | 3 | return $old_entry; | 
| 115 |  |  |  |  |  |  | } else { | 
| 116 | 0 |  |  |  |  | 0 | return $root_node->entry; | 
| 117 |  |  |  |  |  |  | } | 
| 118 |  |  |  |  |  |  | } | 
| 119 |  |  |  |  |  |  |  | 
| 120 |  |  |  |  |  |  | sub mock_bind { | 
| 121 | 20 |  |  | 20 | 0 | 4583 | my $self = shift; | 
| 122 | 20 |  |  |  |  | 48 | my @values = ($self->{mock_bind_code}, $self->{mock_bind_message}); | 
| 123 |  |  |  |  |  |  |  | 
| 124 | 20 | 100 |  |  |  | 37 | if (@_) { | 
| 125 | 18 |  |  |  |  | 22 | $self->{mock_bind_code} = shift; | 
| 126 | 18 |  |  |  |  | 25 | $self->{mock_bind_message} = shift; | 
| 127 |  |  |  |  |  |  | } | 
| 128 |  |  |  |  |  |  |  | 
| 129 | 20 | 50 |  |  |  | 61 | return wantarray ? @values : $values[0]; | 
| 130 |  |  |  |  |  |  | } | 
| 131 |  |  |  |  |  |  |  | 
| 132 |  |  |  |  |  |  | sub mock_password { | 
| 133 | 6 |  |  | 6 | 0 | 11 | my $self = shift; | 
| 134 | 6 | 50 |  |  |  | 14 | my $dn = shift or return; | 
| 135 |  |  |  |  |  |  |  | 
| 136 | 6 | 100 |  |  |  | 12 | if (@_) { | 
| 137 | 2 |  |  |  |  | 3 | my $password = shift; | 
| 138 | 2 |  |  |  |  | 4 | $self->{password_mocked} = 1; | 
| 139 | 2 |  |  |  |  | 4 | my $node = $self->root->make_node($dn); | 
| 140 | 2 |  |  |  |  | 10 | return $node->password($password); | 
| 141 |  |  |  |  |  |  | } else { | 
| 142 | 4 | 100 |  |  |  | 7 | my $node = $self->root->get_node($dn) or return; | 
| 143 | 2 |  |  |  |  | 10 | return $node->password(); | 
| 144 |  |  |  |  |  |  | } | 
| 145 |  |  |  |  |  |  | } | 
| 146 |  |  |  |  |  |  |  | 
| 147 |  |  |  |  |  |  | sub _result_entry { | 
| 148 | 56 |  |  | 56 |  | 83 | my ($self, $input_entry, $arg) = @_; | 
| 149 | 56 |  | 100 |  |  | 169 | my $attrs = $arg->{attrs} || []; | 
| 150 | 56 |  |  |  |  | 63 | my $output_entry; | 
| 151 |  |  |  |  |  |  |  | 
| 152 | 56 | 100 |  |  |  | 102 | if (@$attrs) { | 
| 153 | 29 |  |  |  |  | 96 | $output_entry = Net::LDAP::Entry->new; | 
| 154 | 29 |  |  |  |  | 320 | $output_entry->dn($input_entry->dn); | 
| 155 |  |  |  |  |  |  |  | 
| 156 | 69 |  |  |  |  | 576 | $output_entry->add( | 
| 157 | 29 |  |  |  |  | 218 | map {$_ => [$input_entry->get_value($_)]} @$attrs | 
| 158 |  |  |  |  |  |  | ); | 
| 159 |  |  |  |  |  |  | } else { | 
| 160 | 27 |  |  |  |  | 73 | $output_entry = $input_entry->clone; | 
| 161 |  |  |  |  |  |  | } | 
| 162 |  |  |  |  |  |  |  | 
| 163 | 56 |  |  |  |  | 3554 | $output_entry->changetype('modify'); | 
| 164 | 56 |  |  |  |  | 343 | return $output_entry; | 
| 165 |  |  |  |  |  |  | } | 
| 166 |  |  |  |  |  |  |  | 
| 167 |  |  |  |  |  |  | sub _error { | 
| 168 | 44 |  |  | 44 |  | 53 | my $self = shift; | 
| 169 | 44 |  |  |  |  | 65 | $self->ldap->_error(@_); | 
| 170 |  |  |  |  |  |  | } | 
| 171 |  |  |  |  |  |  |  | 
| 172 |  |  |  |  |  |  | sub _mock_message { | 
| 173 | 170 |  |  | 170 |  | 182 | my $self = shift; | 
| 174 | 170 |  |  |  |  | 312 | $self->ldap->_mock_message(@_); | 
| 175 |  |  |  |  |  |  | } | 
| 176 |  |  |  |  |  |  |  | 
| 177 |  |  |  |  |  |  | sub bind { | 
| 178 | 30 |  |  | 30 | 0 | 42 | my $self = shift; | 
| 179 | 30 |  |  |  |  | 63 | my $arg = &Net::LDAP::_dn_options; | 
| 180 | 30 |  |  |  |  | 1359 | require Net::LDAP::Bind; | 
| 181 | 30 |  |  |  |  | 867 | my $mesg = $self->_mock_message('Net::LDAP::Bind' => $arg); | 
| 182 |  |  |  |  |  |  |  | 
| 183 | 30 | 100 | 100 |  |  | 93 | if ($self->{password_mocked} && exists $arg->{password}) { | 
| 184 | 4 |  |  |  |  | 7 | my $dn = $arg->{dn}; | 
| 185 |  |  |  |  |  |  |  | 
| 186 | 4 | 100 |  |  |  | 8 | if (!defined $dn) { | 
| 187 | 1 |  |  |  |  | 3 | return $self->_error($mesg, LDAP_INAPPROPRIATE_AUTH, 'No password, did you mean noauth or anonymous ?'); | 
| 188 |  |  |  |  |  |  | } | 
| 189 |  |  |  |  |  |  |  | 
| 190 | 3 | 50 |  |  |  | 9 | $dn = ldap_explode_dn($dn, casefold => 'lower') | 
| 191 |  |  |  |  |  |  | or return $self->_error($mesg, LDAP_INVALID_DN_SYNTAX, 'invalid DN'); | 
| 192 |  |  |  |  |  |  |  | 
| 193 | 3 | 100 |  |  |  | 233 | my $node = $self->root->get_node($dn) | 
| 194 |  |  |  |  |  |  | or return $self->_error($mesg, LDAP_NO_SUCH_OBJECT, ''); | 
| 195 |  |  |  |  |  |  |  | 
| 196 | 2 | 100 | 33 |  |  | 10 | unless (defined $node->password && defined $arg->{password} | 
|  |  |  | 66 |  |  |  |  | 
| 197 |  |  |  |  |  |  | && $node->password eq $arg->{password}) { | 
| 198 | 1 |  |  |  |  | 4 | return $self->_error($mesg, LDAP_INVALID_CREDENTIALS, ''); | 
| 199 |  |  |  |  |  |  | } | 
| 200 |  |  |  |  |  |  | } | 
| 201 |  |  |  |  |  |  |  | 
| 202 | 27 | 100 |  |  |  | 61 | if (my $code = $self->{mock_bind_code}) { | 
| 203 | 9 |  | 100 |  |  | 25 | my $message = $self->{mock_bind_message} || ''; | 
| 204 |  |  |  |  |  |  |  | 
| 205 | 9 | 100 |  |  |  | 21 | if (ref $code eq 'CODE') { | 
| 206 |  |  |  |  |  |  | # Callback | 
| 207 | 3 |  |  |  |  | 8 | my @result = $code->($arg); | 
| 208 | 3 |  | 100 |  |  | 1541 | ($code, $message) = ($result[0] || LDAP_SUCCESS, $result[1] || $message); | 
|  |  |  | 66 |  |  |  |  | 
| 209 |  |  |  |  |  |  | } | 
| 210 |  |  |  |  |  |  |  | 
| 211 | 9 | 100 |  |  |  | 29 | if (blessed $code) { | 
| 212 |  |  |  |  |  |  | # Assume $code is a LDAP::Message | 
| 213 | 4 |  | 66 |  |  | 7 | ($code, $message) = ($code->code, $message || $code->error); | 
| 214 |  |  |  |  |  |  | } | 
| 215 |  |  |  |  |  |  |  | 
| 216 | 9 | 100 |  |  |  | 78 | if ($code != LDAP_SUCCESS) { | 
| 217 | 8 |  |  |  |  | 16 | return $self->_error($mesg, $code, $message); | 
| 218 |  |  |  |  |  |  | } | 
| 219 |  |  |  |  |  |  | } | 
| 220 |  |  |  |  |  |  |  | 
| 221 | 19 | 100 |  |  |  | 40 | if (my $callback = $arg->{callback}) { | 
| 222 | 1 |  |  |  |  | 3 | $callback->($mesg); | 
| 223 |  |  |  |  |  |  | } | 
| 224 |  |  |  |  |  |  |  | 
| 225 | 19 |  |  |  |  | 53 | return $mesg; | 
| 226 |  |  |  |  |  |  | } | 
| 227 |  |  |  |  |  |  |  | 
| 228 |  |  |  |  |  |  | sub unbind { | 
| 229 | 2 |  |  | 2 | 0 | 2 | my $self = shift; | 
| 230 | 2 |  |  |  |  | 5 | my $arg = &Net::LDAP::_dn_options; | 
| 231 | 2 |  |  |  |  | 25 | my $mesg =  $self->_mock_message('Net::LDAP::Unbind' => $arg); | 
| 232 |  |  |  |  |  |  |  | 
| 233 | 2 | 100 |  |  |  | 5 | if (my $callback = $arg->{callback}) { | 
| 234 | 1 |  |  |  |  | 2 | $callback->($mesg); | 
| 235 |  |  |  |  |  |  | } | 
| 236 |  |  |  |  |  |  |  | 
| 237 | 2 |  |  |  |  | 8 | return $mesg; | 
| 238 |  |  |  |  |  |  | } | 
| 239 |  |  |  |  |  |  |  | 
| 240 |  |  |  |  |  |  | sub abandon { | 
| 241 | 2 |  |  | 2 | 0 | 3 | my $self = shift; | 
| 242 | 2 |  |  |  |  | 4 | my $arg = &Net::LDAP::_dn_options; | 
| 243 | 2 |  |  |  |  | 26 | my $mesg =  $self->_mock_message('Net::LDAP::Abandon' => $arg); | 
| 244 |  |  |  |  |  |  |  | 
| 245 | 2 | 100 |  |  |  | 6 | if (my $callback = $arg->{callback}) { | 
| 246 | 1 |  |  |  |  | 3 | $callback->($mesg); | 
| 247 |  |  |  |  |  |  | } | 
| 248 |  |  |  |  |  |  |  | 
| 249 | 2 |  |  |  |  | 8 | return $mesg; | 
| 250 |  |  |  |  |  |  | } | 
| 251 |  |  |  |  |  |  |  | 
| 252 |  |  |  |  |  |  | sub search { | 
| 253 | 50 |  |  | 50 | 0 | 61 | my $self = shift; | 
| 254 | 50 |  |  |  |  | 111 | my $arg = &Net::LDAP::_dn_options; | 
| 255 |  |  |  |  |  |  |  | 
| 256 | 50 |  |  |  |  | 5388 | require Net::LDAP::Search; | 
| 257 | 50 |  |  |  |  | 16376 | my $mesg = $self->_mock_message('Net::LDAP::Search' => $arg); | 
| 258 |  |  |  |  |  |  |  | 
| 259 |  |  |  |  |  |  | # Configure params | 
| 260 | 50 |  | 100 |  |  | 226 | my $base = $arg->{base} || ''; | 
| 261 | 50 |  |  |  |  | 132 | $base = ldap_explode_dn($base, casefold => 'lower'); | 
| 262 |  |  |  |  |  |  |  | 
| 263 | 50 | 100 |  |  |  | 2450 | unless ($base) { | 
| 264 | 1 |  |  |  |  | 5 | return $self->_error($mesg, LDAP_INVALID_DN_SYNTAX, 'invalid DN'); | 
| 265 |  |  |  |  |  |  | } | 
| 266 |  |  |  |  |  |  |  | 
| 267 | 49 |  |  |  |  | 84 | my $filter = $arg->{filter}; | 
| 268 |  |  |  |  |  |  |  | 
| 269 | 49 | 100 | 66 |  |  | 284 | if (defined $filter && !ref($filter) && $filter ne '') { | 
|  |  |  | 100 |  |  |  |  | 
| 270 | 37 |  |  |  |  | 168 | my $f = Net::LDAP::Filter->new; | 
| 271 |  |  |  |  |  |  |  | 
| 272 | 37 | 100 |  |  |  | 379 | unless ($f->parse($filter)) { | 
| 273 | 1 |  |  |  |  | 44 | return $self->_error($mesg, LDAP_PARAM_ERROR, 'Bad filter'); | 
| 274 |  |  |  |  |  |  | } | 
| 275 |  |  |  |  |  |  |  | 
| 276 | 36 |  |  |  |  | 2429 | $filter = $f; | 
| 277 |  |  |  |  |  |  | } else { | 
| 278 | 12 |  |  |  |  | 13 | $filter = undef; | 
| 279 |  |  |  |  |  |  | } | 
| 280 |  |  |  |  |  |  |  | 
| 281 | 48 |  | 100 |  |  | 163 | my $scope = $scope{$arg->{scope} || 0}; | 
| 282 |  |  |  |  |  |  |  | 
| 283 | 48 | 100 |  |  |  | 97 | unless (defined $scope) { | 
| 284 | 2 |  |  |  |  | 5 | return $self->_error($mesg, LDAP_PARAM_ERROR, 'invalid scope'); | 
| 285 |  |  |  |  |  |  | } | 
| 286 |  |  |  |  |  |  |  | 
| 287 | 46 |  |  |  |  | 62 | my $callback = $arg->{callback}; | 
| 288 |  |  |  |  |  |  |  | 
| 289 |  |  |  |  |  |  | # Traverse tree | 
| 290 | 46 |  |  |  |  | 75 | $mesg->{entries} = []; | 
| 291 | 46 | 50 |  |  |  | 142 | my $base_node = $base ? $self->root->get_node($base) : $self->root; | 
| 292 |  |  |  |  |  |  |  | 
| 293 | 46 | 100 |  |  |  | 182 | unless ($base_node) { | 
| 294 | 3 |  |  |  |  | 10 | return $self->_error($mesg, LDAP_NO_SUCH_OBJECT, ''); | 
| 295 |  |  |  |  |  |  | } | 
| 296 |  |  |  |  |  |  |  | 
| 297 | 43 | 100 |  |  |  | 111 | $callback->($mesg) if $callback; | 
| 298 |  |  |  |  |  |  |  | 
| 299 |  |  |  |  |  |  | $base_node->traverse(sub { | 
| 300 | 154 |  |  | 154 |  | 144 | my ($node) = @_; | 
| 301 | 154 |  |  |  |  | 280 | my $entry = $node->entry; | 
| 302 | 154 |  |  |  |  | 244 | my $schema = $self->schema; | 
| 303 |  |  |  |  |  |  |  | 
| 304 | 154 | 100 | 100 |  |  | 580 | if ($entry && (!$filter || $filter->match($entry, $schema))) { | 
|  |  |  | 66 |  |  |  |  | 
| 305 | 56 |  |  |  |  | 3668 | my $result_entry = $self->_result_entry($entry, $arg); | 
| 306 | 56 |  |  |  |  | 62 | push @{$mesg->{entries}}, $result_entry; | 
|  | 56 |  |  |  |  | 139 |  | 
| 307 | 56 | 100 |  |  |  | 188 | $callback->($mesg, $result_entry) if $callback; | 
| 308 |  |  |  |  |  |  | } | 
| 309 | 43 |  |  |  |  | 239 | }, $scope); | 
| 310 |  |  |  |  |  |  |  | 
| 311 | 43 |  |  |  |  | 227 | return $mesg; | 
| 312 |  |  |  |  |  |  | } | 
| 313 |  |  |  |  |  |  |  | 
| 314 |  |  |  |  |  |  | sub compare { | 
| 315 | 8 |  |  | 8 | 0 | 11 | my $self = shift; | 
| 316 | 8 |  |  |  |  | 15 | my $arg = &Net::LDAP::_dn_options; | 
| 317 | 8 |  |  |  |  | 131 | my $mesg = $self->_mock_message('Net::LDAP::Compare' => $arg); | 
| 318 |  |  |  |  |  |  |  | 
| 319 | 8 | 50 |  |  |  | 18 | my $dn = (ref $arg->{dn} ? $arg->{dn}->dn : $arg->{dn}); | 
| 320 |  |  |  |  |  |  |  | 
| 321 | 8 | 100 |  |  |  | 13 | unless ($dn) { | 
| 322 | 1 |  |  |  |  | 6 | return $self->_error($mesg, LDAP_PARAM_ERROR, 'No DN specified'); | 
| 323 |  |  |  |  |  |  | } | 
| 324 |  |  |  |  |  |  |  | 
| 325 | 7 |  |  |  |  | 27 | my $dn_list = ldap_explode_dn($dn, casefold => 'lower'); | 
| 326 |  |  |  |  |  |  |  | 
| 327 | 7 | 100 |  |  |  | 500 | unless ($dn_list) { | 
| 328 | 2 |  |  |  |  | 4 | return $self->_error($mesg, LDAP_INVALID_DN_SYNTAX, 'invalid DN'); | 
| 329 |  |  |  |  |  |  | } | 
| 330 |  |  |  |  |  |  |  | 
| 331 | 5 | 0 |  |  |  | 13 | my $attr = exists $arg->{attr} | 
|  |  | 50 |  |  |  |  |  | 
| 332 |  |  |  |  |  |  | ? $arg->{attr} | 
| 333 |  |  |  |  |  |  | : exists $arg->{attrs} #compat | 
| 334 |  |  |  |  |  |  | ? $arg->{attrs}[0] | 
| 335 |  |  |  |  |  |  | : ""; | 
| 336 |  |  |  |  |  |  |  | 
| 337 | 5 | 0 |  |  |  | 12 | my $value = exists $arg->{value} | 
|  |  | 50 |  |  |  |  |  | 
| 338 |  |  |  |  |  |  | ? $arg->{value} | 
| 339 |  |  |  |  |  |  | : exists $arg->{attrs} #compat | 
| 340 |  |  |  |  |  |  | ? $arg->{attrs}[1] | 
| 341 |  |  |  |  |  |  | : ""; | 
| 342 |  |  |  |  |  |  |  | 
| 343 | 5 |  |  |  |  | 11 | my $node = $self->root->get_node($dn_list); | 
| 344 |  |  |  |  |  |  |  | 
| 345 | 5 | 50 | 33 |  |  | 27 | unless ($node && $node->entry) { | 
| 346 | 0 |  |  |  |  | 0 | return $self->_error($mesg, LDAP_NO_SUCH_OBJECT, ''); | 
| 347 |  |  |  |  |  |  | } | 
| 348 |  |  |  |  |  |  |  | 
| 349 | 5 |  |  |  |  | 12 | my $entry = $node->entry; | 
| 350 |  |  |  |  |  |  |  | 
| 351 | 5 |  |  |  |  | 23 | my $filter = bless { | 
| 352 |  |  |  |  |  |  | equalityMatch => { | 
| 353 |  |  |  |  |  |  | attributeDesc => $attr, | 
| 354 |  |  |  |  |  |  | assertionValue => $value, | 
| 355 |  |  |  |  |  |  | } | 
| 356 |  |  |  |  |  |  | }, 'Net::LDAP::Filter'; | 
| 357 |  |  |  |  |  |  |  | 
| 358 | 5 | 100 |  |  |  | 11 | $mesg->{resultCode} = $filter->match($entry, $self->schema) | 
| 359 |  |  |  |  |  |  | ? LDAP_COMPARE_TRUE : LDAP_COMPARE_FALSE; | 
| 360 |  |  |  |  |  |  |  | 
| 361 | 5 | 100 |  |  |  | 753 | if (my $callback = $arg->{callback}) { | 
| 362 | 1 |  |  |  |  | 4 | $callback->($mesg); | 
| 363 |  |  |  |  |  |  | } | 
| 364 |  |  |  |  |  |  |  | 
| 365 | 5 |  |  |  |  | 46 | return $mesg; | 
| 366 |  |  |  |  |  |  | } | 
| 367 |  |  |  |  |  |  |  | 
| 368 |  |  |  |  |  |  | sub add { | 
| 369 | 39 |  |  | 39 | 0 | 48 | my $self = shift; | 
| 370 | 39 |  |  |  |  | 101 | my $arg = &Net::LDAP::_dn_options; | 
| 371 | 39 |  |  |  |  | 742 | my $mesg = $self->_mock_message('Net::LDAP::Add' => $arg); | 
| 372 |  |  |  |  |  |  |  | 
| 373 | 39 | 50 |  |  |  | 108 | my $dn = ref $arg->{dn} ? $arg->{dn}->dn : $arg->{dn}; | 
| 374 |  |  |  |  |  |  |  | 
| 375 | 39 | 100 |  |  |  | 90 | unless ($dn) { | 
| 376 | 1 |  |  |  |  | 4 | return $self->_error($mesg, LDAP_PARAM_ERROR, 'No DN specified'); | 
| 377 |  |  |  |  |  |  | } | 
| 378 |  |  |  |  |  |  |  | 
| 379 | 38 |  |  |  |  | 108 | my $dn_list = ldap_explode_dn($dn, casefold => 'lower'); | 
| 380 |  |  |  |  |  |  |  | 
| 381 | 38 | 100 |  |  |  | 4829 | unless ($dn_list) { | 
| 382 | 2 |  |  |  |  | 4 | return $self->_error($mesg, LDAP_INVALID_DN_SYNTAX, 'invalid DN'); | 
| 383 |  |  |  |  |  |  | } | 
| 384 |  |  |  |  |  |  |  | 
| 385 | 36 |  |  |  |  | 99 | my $node = $self->root->make_node($dn); | 
| 386 |  |  |  |  |  |  |  | 
| 387 | 36 | 100 |  |  |  | 174 | if ($node->entry) { | 
| 388 | 2 |  |  |  |  | 6 | return $self->_error($mesg, LDAP_ALREADY_EXISTS, ''); | 
| 389 |  |  |  |  |  |  | } | 
| 390 |  |  |  |  |  |  |  | 
| 391 | 34 |  |  |  |  | 45 | my $entry; | 
| 392 |  |  |  |  |  |  |  | 
| 393 | 34 | 50 |  |  |  | 70 | if (ref $arg->{dn}) { | 
| 394 | 0 |  |  |  |  | 0 | $entry = $arg->{dn}->clone; | 
| 395 |  |  |  |  |  |  | } else { | 
| 396 | 34 | 100 | 66 |  |  | 342 | $entry = Net::LDAP::Entry->new( | 
| 397 |  |  |  |  |  |  | $arg->{dn}, | 
| 398 | 34 |  |  |  |  | 49 | @{$arg->{attrs} || $arg->{attr} || []} | 
| 399 |  |  |  |  |  |  | ); | 
| 400 |  |  |  |  |  |  | } | 
| 401 |  |  |  |  |  |  |  | 
| 402 | 34 | 50 |  |  |  | 890 | if (my $rdn = $dn_list->[0]) { | 
| 403 | 34 |  |  |  |  | 134 | $entry->delete(%$rdn); | 
| 404 | 34 |  |  |  |  | 1174 | $entry->add(%$rdn); | 
| 405 |  |  |  |  |  |  | } | 
| 406 |  |  |  |  |  |  |  | 
| 407 | 34 |  |  |  |  | 645 | $entry->changetype('add'); | 
| 408 | 34 |  |  |  |  | 250 | $node->entry($entry); | 
| 409 |  |  |  |  |  |  |  | 
| 410 | 34 | 100 |  |  |  | 90 | if (my $callback = $arg->{callback}) { | 
| 411 | 1 |  |  |  |  | 3 | $callback->($mesg); | 
| 412 |  |  |  |  |  |  | } | 
| 413 |  |  |  |  |  |  |  | 
| 414 | 34 |  |  |  |  | 216 | return $mesg; | 
| 415 |  |  |  |  |  |  | } | 
| 416 |  |  |  |  |  |  |  | 
| 417 |  |  |  |  |  |  | my %opcode = (add => 0, delete => 1, replace => 2, increment => 3); | 
| 418 |  |  |  |  |  |  |  | 
| 419 |  |  |  |  |  |  | sub modify { | 
| 420 | 19 |  |  | 19 | 0 | 23 | my $self = shift; | 
| 421 | 19 |  |  |  |  | 41 | my $arg = &Net::LDAP::_dn_options; | 
| 422 | 19 |  |  |  |  | 304 | my $mesg = $self->_mock_message('Net::LDAP::Modify' => $arg); | 
| 423 |  |  |  |  |  |  |  | 
| 424 | 19 | 50 |  |  |  | 49 | my $dn = (ref $arg->{dn} ? $arg->{dn}->dn : $arg->{dn}); | 
| 425 |  |  |  |  |  |  |  | 
| 426 | 19 | 100 |  |  |  | 40 | unless ($dn) { | 
| 427 | 1 |  |  |  |  | 4 | return $self->_error($mesg, LDAP_PARAM_ERROR, 'No DN specified'); | 
| 428 |  |  |  |  |  |  | } | 
| 429 |  |  |  |  |  |  |  | 
| 430 | 18 |  |  |  |  | 42 | my $dn_list = ldap_explode_dn($dn, casefold => 'lower'); | 
| 431 |  |  |  |  |  |  |  | 
| 432 | 18 | 100 |  |  |  | 1300 | unless ($dn_list) { | 
| 433 | 2 |  |  |  |  | 5 | return $self->_error($mesg, LDAP_INVALID_DN_SYNTAX, 'invalid DN'); | 
| 434 |  |  |  |  |  |  | } | 
| 435 |  |  |  |  |  |  |  | 
| 436 | 16 |  |  |  |  | 36 | my $node = $self->root->get_node($dn_list); | 
| 437 |  |  |  |  |  |  |  | 
| 438 | 16 | 100 | 66 |  |  | 92 | unless ($node && $node->entry) { | 
| 439 | 2 |  |  |  |  | 7 | return $self->_error($mesg, LDAP_NO_SUCH_OBJECT, ''); | 
| 440 |  |  |  |  |  |  | } | 
| 441 |  |  |  |  |  |  |  | 
| 442 | 14 |  |  |  |  | 30 | my $entry = $node->entry; | 
| 443 |  |  |  |  |  |  |  | 
| 444 | 14 | 100 |  |  |  | 28 | if (exists $arg->{changes}) { | 
| 445 | 2 |  |  |  |  | 5 | for (my $j = 0; $j < @{$arg->{changes}}; $j += 2) { | 
|  | 5 |  |  |  |  | 112 |  | 
| 446 | 4 |  |  |  |  | 5 | my $op = $arg->{changes}[$j]; | 
| 447 | 4 |  |  |  |  | 5 | my $chg = $arg->{changes}[$j + 1]; | 
| 448 |  |  |  |  |  |  |  | 
| 449 | 4 | 100 |  |  |  | 21 | unless (defined $opcode{$op}) { | 
| 450 | 1 |  |  |  |  | 5 | return $self->_error($mesg, LDAP_PARAM_ERROR, "Bad change type '$op'"); | 
| 451 |  |  |  |  |  |  | } | 
| 452 |  |  |  |  |  |  |  | 
| 453 | 3 |  |  |  |  | 9 | $entry->$op(@$chg); | 
| 454 |  |  |  |  |  |  | } | 
| 455 |  |  |  |  |  |  | } else { | 
| 456 | 12 |  |  |  |  | 34 | for my $op (keys %opcode) { | 
| 457 | 48 | 100 |  |  |  | 298 | my $chg = $arg->{$op} or next; | 
| 458 | 11 |  |  |  |  | 12 | my $opcode = $opcode{$op}; | 
| 459 | 11 |  |  |  |  | 13 | my $ref_chg = ref $chg; | 
| 460 |  |  |  |  |  |  |  | 
| 461 | 11 | 100 |  |  |  | 33 | if ($opcode == 3) { | 
|  |  | 100 |  |  |  |  |  | 
|  |  | 50 |  |  |  |  |  | 
|  |  | 0 |  |  |  |  |  | 
| 462 |  |  |  |  |  |  | # $op eq 'increment' | 
| 463 | 2 | 100 |  |  |  | 8 | if ($ref_chg eq 'HASH') { | 
|  |  | 50 |  |  |  |  |  | 
|  |  | 0 |  |  |  |  |  | 
| 464 | 1 |  |  |  |  | 3 | for my $attr (keys %$chg) { | 
| 465 | 2 |  |  |  |  | 23 | my $incr = $chg->{$attr}; | 
| 466 |  |  |  |  |  |  |  | 
| 467 | 3 |  |  |  |  | 24 | $entry->replace( | 
| 468 | 2 |  |  |  |  | 5 | $attr => [map {$_ + $incr} $entry->get_value($attr)] | 
| 469 |  |  |  |  |  |  | ); | 
| 470 |  |  |  |  |  |  | } | 
| 471 |  |  |  |  |  |  | } elsif ($ref_chg eq 'ARRAY') { | 
| 472 | 1 |  |  |  |  | 5 | for (my $i = 0; $i < @$chg; $i += 2) { | 
| 473 | 2 |  |  |  |  | 21 | my ($attr, $incr) = ($chg->[$i], $chg->[$i + 1]); | 
| 474 | 2 | 50 |  |  |  | 4 | next unless defined $incr; | 
| 475 |  |  |  |  |  |  |  | 
| 476 | 3 |  |  |  |  | 25 | $entry->replace( | 
| 477 | 2 |  |  |  |  | 6 | $attr => [map {$_ + $incr} $entry->get_value($attr)] | 
| 478 |  |  |  |  |  |  | ); | 
| 479 |  |  |  |  |  |  | } | 
| 480 |  |  |  |  |  |  | } elsif (!$ref_chg) { | 
| 481 | 0 |  |  |  |  | 0 | $entry->replace( | 
| 482 | 0 |  |  |  |  | 0 | $chg => [map {$_ + 1} $entry->get_value($chg)] | 
| 483 |  |  |  |  |  |  | ); | 
| 484 |  |  |  |  |  |  | } | 
| 485 |  |  |  |  |  |  | } elsif ($ref_chg eq 'HASH') { | 
| 486 | 5 |  |  |  |  | 28 | $entry->$op(%$chg); | 
| 487 |  |  |  |  |  |  | } elsif ($ref_chg eq 'ARRAY') { | 
| 488 | 4 | 100 |  |  |  | 9 | if ($opcode == 1) { | 
| 489 |  |  |  |  |  |  | # $op eq 'delete' | 
| 490 | 1 |  |  |  |  | 2 | $entry->$op(map {$_ => []} @$chg); | 
|  | 2 |  |  |  |  | 8 |  | 
| 491 |  |  |  |  |  |  | } else { | 
| 492 | 3 |  |  |  |  | 10 | $entry->$op(@$chg); | 
| 493 |  |  |  |  |  |  | } | 
| 494 |  |  |  |  |  |  | } elsif (!$ref_chg) { | 
| 495 | 0 |  |  |  |  | 0 | $entry->$op($chg => []); | 
| 496 |  |  |  |  |  |  | } | 
| 497 |  |  |  |  |  |  | } | 
| 498 |  |  |  |  |  |  | } | 
| 499 |  |  |  |  |  |  |  | 
| 500 | 13 | 100 |  |  |  | 157 | if (my $callback = $arg->{callback}) { | 
| 501 | 2 |  |  |  |  | 14 | $callback->($mesg); | 
| 502 |  |  |  |  |  |  | } | 
| 503 |  |  |  |  |  |  |  | 
| 504 | 13 |  |  |  |  | 72 | return $mesg; | 
| 505 |  |  |  |  |  |  | } | 
| 506 |  |  |  |  |  |  |  | 
| 507 |  |  |  |  |  |  | sub delete { | 
| 508 | 8 |  |  | 8 | 0 | 9 | my $self = shift; | 
| 509 | 8 |  |  |  |  | 15 | my $arg = &Net::LDAP::_dn_options; | 
| 510 | 8 |  |  |  |  | 116 | my $mesg = $self->_mock_message('Net::LDAP::Delete' => $arg); | 
| 511 |  |  |  |  |  |  |  | 
| 512 | 8 | 50 |  |  |  | 21 | my $dn = (ref $arg->{dn} ? $arg->{dn}->dn : $arg->{dn}); | 
| 513 |  |  |  |  |  |  |  | 
| 514 | 8 | 100 |  |  |  | 17 | unless ($dn) { | 
| 515 | 1 |  |  |  |  | 4 | return $self->_error($mesg, LDAP_PARAM_ERROR, 'No DN specified'); | 
| 516 |  |  |  |  |  |  | } | 
| 517 |  |  |  |  |  |  |  | 
| 518 | 7 |  |  |  |  | 17 | my $dn_list = ldap_explode_dn($dn, casefold => 'lower'); | 
| 519 |  |  |  |  |  |  |  | 
| 520 | 7 | 100 |  |  |  | 448 | unless ($dn_list) { | 
| 521 | 2 |  |  |  |  | 5 | return $self->_error($mesg, LDAP_INVALID_DN_SYNTAX, 'invalid DN'); | 
| 522 |  |  |  |  |  |  | } | 
| 523 |  |  |  |  |  |  |  | 
| 524 | 5 |  |  |  |  | 16 | my $node = $self->root->get_node($dn_list); | 
| 525 |  |  |  |  |  |  |  | 
| 526 | 5 | 100 | 66 |  |  | 28 | unless ($node && $node->entry) { | 
| 527 | 1 |  |  |  |  | 4 | return $self->_error($mesg, LDAP_NO_SUCH_OBJECT, ''); | 
| 528 |  |  |  |  |  |  | } | 
| 529 |  |  |  |  |  |  |  | 
| 530 | 4 |  |  |  |  | 9 | $node->entry(undef); | 
| 531 |  |  |  |  |  |  |  | 
| 532 | 4 | 100 |  |  |  | 10 | if (my $callback = $arg->{callback}) { | 
| 533 | 1 |  |  |  |  | 3 | $callback->($mesg); | 
| 534 |  |  |  |  |  |  | } | 
| 535 |  |  |  |  |  |  |  | 
| 536 | 4 |  |  |  |  | 21 | return $mesg; | 
| 537 |  |  |  |  |  |  | } | 
| 538 |  |  |  |  |  |  |  | 
| 539 |  |  |  |  |  |  | sub moddn { | 
| 540 | 12 |  |  | 12 | 0 | 13 | my $self = shift; | 
| 541 | 12 |  |  |  |  | 23 | my $arg = &Net::LDAP::_dn_options; | 
| 542 | 12 |  |  |  |  | 200 | my $mesg = $self->_mock_message('Net::LDAP::ModDN' => $arg); | 
| 543 |  |  |  |  |  |  |  | 
| 544 | 12 | 50 |  |  |  | 28 | my $dn = (ref $arg->{dn} ? $arg->{dn}->dn : $arg->{dn}); | 
| 545 |  |  |  |  |  |  |  | 
| 546 | 12 | 100 |  |  |  | 25 | unless ($dn) { | 
| 547 | 1 |  |  |  |  | 4 | return $self->_error($mesg, LDAP_PARAM_ERROR, 'No DN specified'); | 
| 548 |  |  |  |  |  |  | } | 
| 549 |  |  |  |  |  |  |  | 
| 550 | 11 |  |  |  |  | 27 | my $dn_list = ldap_explode_dn($dn, casefold => 'lower'); | 
| 551 |  |  |  |  |  |  |  | 
| 552 | 11 | 100 |  |  |  | 740 | unless ($dn_list) { | 
| 553 | 2 |  |  |  |  | 4 | return $self->_error($mesg, LDAP_INVALID_DN_SYNTAX, 'invalid DN'); | 
| 554 |  |  |  |  |  |  | } | 
| 555 |  |  |  |  |  |  |  | 
| 556 | 9 |  |  |  |  | 12 | my $old_rdn = $dn_list->[0]; | 
| 557 | 9 |  |  |  |  | 18 | my $old_node = $self->root->get_node($dn_list); | 
| 558 |  |  |  |  |  |  |  | 
| 559 | 9 | 100 | 66 |  |  | 48 | unless ($old_node && $old_node->entry) { | 
| 560 | 1 |  |  |  |  | 2 | return $self->_error($mesg, LDAP_NO_SUCH_OBJECT, ''); | 
| 561 |  |  |  |  |  |  | } | 
| 562 |  |  |  |  |  |  |  | 
| 563 |  |  |  |  |  |  | # Configure new RDN | 
| 564 | 8 |  |  |  |  | 9 | my $new_rdn; | 
| 565 | 8 |  |  |  |  | 7 | my $rdn_changed = 0; | 
| 566 |  |  |  |  |  |  |  | 
| 567 | 8 | 100 |  |  |  | 17 | if (defined(my $new_rdn_value = $arg->{newrdn})) { | 
| 568 | 7 |  |  |  |  | 14 | my $new_rdn_list = ldap_explode_dn($new_rdn_value, casefold => 'lower'); | 
| 569 |  |  |  |  |  |  |  | 
| 570 | 7 | 100 |  |  |  | 221 | unless ($new_rdn_list) { | 
| 571 | 1 |  |  |  |  | 4 | return $self->_error($mesg, LDAP_INVALID_DN_SYNTAX, 'invalid new RDN'); | 
| 572 |  |  |  |  |  |  | } | 
| 573 |  |  |  |  |  |  |  | 
| 574 | 6 |  |  |  |  | 10 | $new_rdn = $new_rdn_list->[0]; | 
| 575 | 6 |  |  |  |  | 11 | $rdn_changed = 1; | 
| 576 |  |  |  |  |  |  | } else { | 
| 577 | 1 |  |  |  |  | 2 | $new_rdn = $dn_list->[0]; | 
| 578 |  |  |  |  |  |  | } | 
| 579 |  |  |  |  |  |  |  | 
| 580 |  |  |  |  |  |  | # Configure new DN | 
| 581 | 7 | 100 |  |  |  | 17 | if (defined(my $new_superior = $arg->{newsuperior})) { | 
| 582 | 4 |  |  |  |  | 10 | $dn_list = ldap_explode_dn($new_superior, casefold => 'lower'); | 
| 583 |  |  |  |  |  |  |  | 
| 584 | 4 | 100 |  |  |  | 176 | unless ($dn_list) { | 
| 585 | 1 |  |  |  |  | 4 | return $self->_error($mesg, LDAP_INVALID_DN_SYNTAX, 'invalid newSuperior'); | 
| 586 |  |  |  |  |  |  | } | 
| 587 |  |  |  |  |  |  |  | 
| 588 | 3 |  |  |  |  | 6 | unshift @$dn_list, $new_rdn; | 
| 589 |  |  |  |  |  |  | } else { | 
| 590 | 3 |  |  |  |  | 5 | $dn_list->[0] = $new_rdn; | 
| 591 |  |  |  |  |  |  | } | 
| 592 |  |  |  |  |  |  |  | 
| 593 | 6 |  |  |  |  | 12 | my $new_dn = canonical_dn($dn_list, casefold => 'lower'); | 
| 594 |  |  |  |  |  |  |  | 
| 595 |  |  |  |  |  |  | # Create new node | 
| 596 | 6 |  |  |  |  | 282 | my $new_node = $self->root->make_node($dn_list); | 
| 597 |  |  |  |  |  |  |  | 
| 598 | 6 | 100 |  |  |  | 25 | if ($new_node->entry) { | 
| 599 | 2 |  |  |  |  | 4 | return $self->_error($mesg, LDAP_ALREADY_EXISTS, ''); | 
| 600 |  |  |  |  |  |  | } | 
| 601 |  |  |  |  |  |  |  | 
| 602 |  |  |  |  |  |  | # Set up new entry | 
| 603 | 4 |  |  |  |  | 10 | my $new_entry = $old_node->entry; | 
| 604 | 4 |  |  |  |  | 9 | $old_node->entry(undef); | 
| 605 |  |  |  |  |  |  |  | 
| 606 | 4 |  |  |  |  | 13 | $new_entry->dn($new_dn); | 
| 607 |  |  |  |  |  |  |  | 
| 608 | 4 | 100 |  |  |  | 18 | if ($rdn_changed) { | 
| 609 | 3 | 100 |  |  |  | 9 | if ($arg->{deleteoldrdn}) { | 
| 610 | 2 |  |  |  |  | 7 | $new_entry->delete(%$old_rdn); | 
| 611 |  |  |  |  |  |  | } | 
| 612 |  |  |  |  |  |  |  | 
| 613 | 3 |  |  |  |  | 78 | $new_entry->delete(%$new_rdn); | 
| 614 | 3 |  |  |  |  | 97 | $new_entry->add(%$new_rdn); | 
| 615 |  |  |  |  |  |  | } | 
| 616 |  |  |  |  |  |  |  | 
| 617 | 4 |  |  |  |  | 43 | $new_node->entry($new_entry); | 
| 618 |  |  |  |  |  |  |  | 
| 619 | 4 | 50 |  |  |  | 10 | if (my $callback = $arg->{callback}) { | 
| 620 | 0 |  |  |  |  | 0 | $callback->($mesg); | 
| 621 |  |  |  |  |  |  | } | 
| 622 |  |  |  |  |  |  |  | 
| 623 | 4 |  |  |  |  | 23 | return $mesg; | 
| 624 |  |  |  |  |  |  | } | 
| 625 |  |  |  |  |  |  |  | 
| 626 |  |  |  |  |  |  | 1; |