File Coverage

blib/lib/Net/LDAP/Entry.pm
Criterion Covered Total %
statement 120 221 54.3
branch 55 118 46.6
condition 12 37 32.4
subroutine 19 28 67.8
pod 13 19 68.4
total 219 423 51.7


line stmt bran cond sub pod time code
1             # Copyright (c) 1997-2004 Graham Barr . All rights reserved.
2             # This program is free software; you can redistribute it and/or
3             # modify it under the same terms as Perl itself.
4              
5             package Net::LDAP::Entry;
6              
7 22     22   67569 use strict;
  22         58  
  22         710  
8 22     22   1310 use Net::LDAP::ASN qw(LDAPEntry);
  22         53  
  22         165  
9 22     22   4485 use Net::LDAP::Constant qw(LDAP_LOCAL_ERROR LDAP_OTHER);
  22         46  
  22         1867  
10              
11 22     22   136 use constant CHECK_UTF8 => $] > 5.007;
  22         39  
  22         1361  
12              
13             BEGIN {
14             require Encode
15 22     22   41459 if (CHECK_UTF8);
16             }
17              
18             our $VERSION = '0.28';
19              
20             sub new {
21 15     15 1 1190 my $self = shift;
22 15   33     52 my $type = ref($self) || $self;
23              
24 15         53 my $entry = bless { changetype => 'add', changes => [] }, $type;
25              
26 15 100       63 @_ and $entry->dn( shift );
27 15 100       78 @_ and $entry->add( @_ );
28              
29 15         36 return $entry;
30             }
31              
32             sub clone {
33 0     0 1 0 my $self = shift;
34 0         0 my $clone = $self->new();
35              
36 0         0 $clone->dn($self->dn());
37 0         0 foreach ($self->attributes()) {
38 0         0 $clone->add($_ => [$self->get_value($_)]);
39             }
40              
41 0         0 $clone->{changetype} = $self->{changetype};
42 0         0 my @changes = @{$self->{changes}};
  0         0  
43 0         0 while (my($action, $cmd) = splice(@changes, 0, 2)) {
44 0         0 my @new_cmd;
45 0         0 my @cmd = @$cmd;
46 0         0 while (my($type, $val) = splice(@cmd, 0, 2)) {
47 0         0 push @new_cmd, $type, [ @$val ];
48             }
49 0         0 push @{$clone->{changes}}, $action, \@new_cmd;
  0         0  
50             }
51              
52 0         0 $clone;
53             }
54              
55             # Build attrs cache, created when needed
56              
57             sub _build_attrs {
58 15     15   22 +{ map { (lc($_->{type}), $_->{vals}) } @{$_[0]->{asn}{attributes}} };
  0         0  
  15         77  
59             }
60              
61             # If we are passed an ASN structure we really do nothing
62              
63             sub decode {
64 0     0 0 0 my $self = shift;
65 0 0       0 my $result = ref($_[0]) ? shift : $LDAPEntry->decode(shift)
    0          
66             or return;
67 0         0 my %arg = @_;
68              
69 0         0 %{$self} = ( asn => $result, changetype => 'modify', changes => []);
  0         0  
70              
71 0 0       0 if (CHECK_UTF8 && $arg{raw}) {
72             $result->{objectName} = Encode::decode_utf8($result->{objectName})
73 0 0       0 if ('dn' !~ /$arg{raw}/);
74              
75 0         0 foreach my $elem (@{$self->{asn}{attributes}}) {
  0         0  
76 0         0 map { $_ = Encode::decode_utf8($_) } @{$elem->{vals}}
  0         0  
77 0 0       0 if ($elem->{type} !~ /$arg{raw}/);
78             }
79             }
80              
81 0         0 $self;
82             }
83              
84              
85              
86             sub encode {
87 0     0 0 0 $LDAPEntry->encode( shift->{asn} );
88             }
89              
90              
91             sub dn {
92 43     43 1 71 my $self = shift;
93 43 100       161 @_ ? ($self->{asn}{objectName} = shift) : $self->{asn}{objectName};
94             }
95              
96             sub get_attribute {
97 0     0 0 0 require Carp;
98 0 0       0 Carp::carp('->get_attribute deprecated, use ->get_value') if $^W;
99 0         0 shift->get_value(@_, asref => !wantarray);
100             }
101              
102             sub get {
103 0     0 0 0 require Carp;
104 0 0       0 Carp::carp('->get deprecated, use ->get_value') if $^W;
105 0         0 shift->get_value(@_, asref => !wantarray);
106             }
107              
108              
109             sub exists {
110 2     2 1 3 my $self = shift;
111 2         3 my $type = lc(shift);
112 2   33     5 my $attrs = $self->{attrs} ||= _build_attrs($self);
113              
114 2         6 exists $attrs->{$type};
115             }
116              
117             sub get_value {
118 372     372 1 1126 my $self = shift;
119 372         507 my $type = lc(shift);
120 372         540 my %opt = @_;
121              
122 372 100       620 if ($opt{alloptions}) {
123             my %ret = map {
124 15 100       73 $_->{type} =~ /^\Q$type\E((?:;.*)?)$/i ? (lc($1), $_->{vals}) : ()
125 1         2 } @{$self->{asn}{attributes}};
  1         4  
126 1 50       7 return %ret ? \%ret : undef;
127             }
128              
129 371   33     634 my $attrs = $self->{attrs} ||= _build_attrs($self);
130 371 100       686 my $attr = $attrs->{$type} or return;
131              
132             return $opt{asref}
133             ? $attr
134             : wantarray
135 249 50       580 ? @{$attr}
  99 100       287  
136             : $attr->[0];
137             }
138              
139              
140             sub changetype {
141              
142 16     16 1 22 my $self = shift;
143 16 100       46 return $self->{changetype} unless @_;
144 7         12 $self->{changes} = [];
145 7         13 $self->{changetype} = shift;
146 7         21 return $self;
147             }
148              
149              
150              
151             sub add {
152 75     75 1 784 my $self = shift;
153 75 100       145 my $cmd = $self->{changetype} eq 'modify' ? [] : undef;
154 75   66     161 my $attrs = $self->{attrs} ||= _build_attrs($self);
155              
156 75         206 while (my($type, $val) = splice(@_, 0, 2)) {
157 97         155 my $lc_type = lc $type;
158              
159 97         386 push @{$self->{asn}{attributes}}, { type => $type, vals => ($attrs->{$lc_type}=[])}
160 97 50       183 unless exists $attrs->{$lc_type};
161              
162 97 100       135 push @{$attrs->{$lc_type}}, ref($val) ? @$val : $val;
  97         434  
163              
164 97 100       329 push @$cmd, $type, [ ref($val) ? @$val : $val ]
    100          
165             if $cmd;
166              
167             }
168              
169 75 100       135 push(@{$self->{changes}}, 'add', $cmd) if $cmd;
  4         9  
170              
171 75         127 return $self;
172             }
173              
174              
175             sub replace {
176 1     1 1 2 my $self = shift;
177 1 50       5 my $cmd = $self->{changetype} eq 'modify' ? [] : undef;
178 1   33     3 my $attrs = $self->{attrs} ||= _build_attrs($self);
179              
180 1         5 while (my($type, $val) = splice(@_, 0, 2)) {
181 1         3 my $lc_type = lc $type;
182              
183 1 50 33     8 if (defined($val) and (!ref($val) or @$val)) {
      33        
184              
185 0         0 push @{$self->{asn}{attributes}}, { type => $type, vals => ($attrs->{$lc_type}=[])}
186 1 50       4 unless exists $attrs->{$lc_type};
187              
188 1 50       4 @{$attrs->{$lc_type}} = ref($val) ? @$val : ($val);
  1         4  
189              
190 1 50       8 push @$cmd, $type, [ ref($val) ? @$val : $val ]
    50          
191             if $cmd;
192              
193             }
194             else {
195 0         0 delete $attrs->{$lc_type};
196              
197 0         0 @{$self->{asn}{attributes}}
198 0         0 = grep { $lc_type ne lc($_->{type}) } @{$self->{asn}{attributes}};
  0         0  
  0         0  
199              
200 0 0       0 push @$cmd, $type, []
201             if $cmd;
202              
203             }
204             }
205              
206 1 50       3 push(@{$self->{changes}}, 'replace', $cmd) if $cmd;
  1         3  
207              
208 1         2 return $self;
209             }
210              
211              
212             sub delete {
213 2     2 1 5 my $self = shift;
214              
215 2 50       7 unless (@_) {
216 0         0 $self->changetype('delete');
217 0         0 return $self;
218             }
219              
220 2 50       12 my $cmd = $self->{changetype} eq 'modify' ? [] : undef;
221 2   33     8 my $attrs = $self->{attrs} ||= _build_attrs($self);
222              
223 2         9 while (my($type, $val) = splice(@_, 0, 2)) {
224 2         5 my $lc_type = lc $type;
225              
226 2 100 33     13 if (defined($val) and (!ref($val) or @$val)) {
      66        
227 1         36 my %values;
228 1 50       9 @values{(ref($val) ? @$val : $val)} = ();
229              
230 1 50       3 unless (@{$attrs->{$lc_type}}
  1         1118  
231 5         11 = grep { !exists $values{$_} } @{$attrs->{$lc_type}})
  1         4  
232             {
233 0         0 delete $attrs->{$lc_type};
234 0         0 @{$self->{asn}{attributes}}
235 0         0 = grep { $lc_type ne lc($_->{type}) } @{$self->{asn}{attributes}};
  0         0  
  0         0  
236             }
237              
238 1 50       15 push @$cmd, $type, [ ref($val) ? @$val : $val ]
    50          
239             if $cmd;
240             }
241             else {
242 1         3 delete $attrs->{$lc_type};
243              
244 1         6 @{$self->{asn}{attributes}}
245 1         2 = grep { $lc_type ne lc($_->{type}) } @{$self->{asn}{attributes}};
  11         24  
  1         16  
246              
247 1 50       7 push @$cmd, $type, [] if $cmd;
248             }
249             }
250              
251 2 50       7 push(@{$self->{changes}}, 'delete', $cmd) if $cmd;
  2         5  
252              
253 2         63 return $self;
254             }
255              
256              
257             sub update {
258 0     0 1 0 my $self = shift;
259 0         0 my $target = shift; # a Net::LDAP or a Net::LDAP::LDIF object
260 0         0 my %opt = @_;
261 0         0 my $mesg;
262 0         0 my $user_cb = delete $opt{callback};
263 0 0   0   0 my $cb = sub { $self->changetype('modify') unless $_[0]->code;
264 0 0       0 $user_cb->(@_) if $user_cb };
  0         0  
265              
266 0 0       0 if (eval { $target->isa('Net::LDAP') }) {
  0 0       0  
267 0 0       0 if ($self->{changetype} eq 'add') {
    0          
    0          
    0          
268 0         0 $mesg = $target->add($self, callback => $cb, %opt);
269             }
270             elsif ($self->{changetype} eq 'delete') {
271 0         0 $mesg = $target->delete($self, callback => $cb, %opt);
272             }
273             elsif ($self->{changetype} =~ /modr?dn/o) {
274 0   0     0 my @args = (newrdn => $self->get_value('newrdn') || undef,
      0        
275             deleteoldrdn => $self->get_value('deleteoldrdn') || undef);
276 0         0 my $newsuperior = $self->get_value('newsuperior');
277 0 0       0 push(@args, newsuperior => $newsuperior) if $newsuperior;
278 0         0 $mesg = $target->moddn($self, @args, callback => $cb, %opt);
279             }
280 0         0 elsif (@{$self->{changes}}) {
281 0         0 $mesg = $target->modify($self, changes => $self->{changes}, callback => $cb, %opt);
282             }
283             else {
284 0         0 require Net::LDAP::Message;
285 0         0 $mesg = Net::LDAP::Message->new( $target );
286 0         0 $mesg->set_error(LDAP_LOCAL_ERROR, 'No attributes to update');
287             }
288             }
289 0         0 elsif (eval { $target->isa('Net::LDAP::LDIF') }) {
290 0         0 require Net::LDAP::Message;
291 0         0 $target->write_entry($self, %opt);
292 0         0 $mesg = Net::LDAP::Message::Dummy->new();
293 0 0       0 $mesg->set_error(LDAP_OTHER, $target->error())
294             if ($target->error());
295             }
296             else {
297 0         0 $mesg = Net::LDAP::Message::Dummy->new();
298 0         0 $mesg->set_error(LDAP_OTHER, 'illegal update target');
299             }
300              
301 0         0 return $mesg;
302             }
303              
304             sub ldif {
305 4     4 1 1665 my $self = shift;
306 4         11 my %opt = @_;
307              
308 4         27 require Net::LDAP::LDIF;
309 1     1   6 open(my $fh, '>', \my $buffer);
  1         2  
  1         7  
  4         69  
310 4 100       961 my $change = exists $opt{change} ? $opt{change} : $self->changes ? 1 : 0;
    100          
311 4         27 my $ldif = Net::LDAP::LDIF->new($fh, 'w', %opt, version => 0, change => $change);
312 4         14 $ldif->write_entry($self);
313 4         17 return $buffer;
314             }
315              
316             # Just for debugging
317              
318             sub dump {
319 0     0 1 0 my $self = shift;
320 22     22   142 no strict 'refs'; # select may return a GLOB name
  22         42  
  22         9110  
321 0 0       0 my $fh = @_ ? shift : select;
322              
323 0         0 my $asn = $self->{asn};
324 0         0 print $fh '-' x 72, "\n";
325 0 0       0 print $fh 'dn:', $asn->{objectName}, "\n\n" if $asn->{objectName};
326              
327 0         0 my $l = 0;
328              
329 0   0     0 for (keys %{ $self->{attrs} ||= _build_attrs($self) }) {
  0         0  
330 0 0       0 $l = length if length > $l;
331             }
332              
333 0         0 my $spc = "\n " . ' ' x $l;
334              
335 0         0 foreach my $attr (@{$asn->{attributes}}) {
  0         0  
336 0         0 my $val = $attr->{vals};
337 0         0 printf $fh "%${l}s: ", $attr->{type};
338 0         0 my $i = 0;
339 0         0 foreach my $v (@$val) {
340 0 0       0 print $fh $spc if $i++;
341 0         0 print $fh $v;
342             }
343 0         0 print $fh "\n";
344             }
345             }
346              
347             sub attributes {
348 24     24 1 38 my $self = shift;
349 24         40 my %opt = @_;
350              
351 24 100       43 if ($opt{nooptions}) {
352 1         3 my %done;
353             return map {
354 15         34 $_->{type} =~ /^([^;]+)/;
355 15 100       62 $done{lc $1}++ ? () : ($1);
356 1         1 } @{$self->{asn}{attributes}};
  1         4  
357             }
358             else {
359 23         36 return map { $_->{type} } @{$self->{asn}{attributes}};
  153         295  
  23         52  
360             }
361             }
362              
363             sub asn {
364             shift->{asn}
365 0     0 0 0 }
366              
367             sub changes {
368 11     11 0 19 my $ref = shift->{changes};
369 11 50       33 $ref ? @$ref : ();
370             }
371              
372             1;