File Coverage

blib/lib/Net/LDAP/Entry.pm
Criterion Covered Total %
statement 128 229 55.9
branch 61 124 49.1
condition 12 37 32.4
subroutine 19 28 67.8
pod 13 19 68.4
total 233 437 53.3


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   74136 use strict;
  22         71  
  22         797  
8 22     22   1668 use Net::LDAP::ASN qw(LDAPEntry);
  22         59  
  22         183  
9 22     22   2787 use Net::LDAP::Constant qw(LDAP_LOCAL_ERROR LDAP_OTHER);
  22         55  
  22         1705  
10              
11 22     22   146 use constant CHECK_UTF8 => $] > 5.007;
  22         47  
  22         1520  
12              
13             BEGIN {
14             require Encode
15 22     22   52368 if (CHECK_UTF8);
16             }
17              
18             our $VERSION = '0.29';
19              
20             sub new {
21 15     15 1 1286 my $self = shift;
22 15   33     56 my $type = ref($self) || $self;
23              
24 15         63 my $entry = bless { changetype => 'add', changes => [] }, $type;
25              
26 15 100       73 @_ and $entry->dn( shift );
27 15 100       53 @_ and $entry->add( @_ );
28              
29 15         38 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   27 +{ map { (lc($_->{type}), $_->{vals}) } @{$_[0]->{asn}{attributes}} };
  0         0  
  15         71  
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 73 my $self = shift;
93 43 100       203 @_ ? ($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 5 my $self = shift;
111 2         5 my $type = lc(shift);
112 2   33     6 my $attrs = $self->{attrs} ||= _build_attrs($self);
113              
114 2         9 exists $attrs->{$type};
115             }
116              
117             sub get_value {
118 375     375 1 2603 my $self = shift;
119 375         605 my $type = lc(shift);
120 375         673 my %opt = @_;
121              
122 375 100       676 if ($opt{alloptions}) {
123             my %ret = map {
124 15 100       80 $_->{type} =~ /^\Q$type\E((?:;.*)?)$/i ? (lc($1), $_->{vals}) : ()
125 1         2 } @{$self->{asn}{attributes}};
  1         5  
126 1 50       7 return %ret ? \%ret : undef;
127             }
128              
129 374   33     732 my $attrs = $self->{attrs} ||= _build_attrs($self);
130 374         463 my $attr;
131              
132 374 100       608 if ($opt{nooptions}) {
133             my @vals = map {
134 45 100       152 $_->{type} =~ /^\Q$type\E((?:;.*)?)$/i ? @{$_->{vals}} : ()
  6         21  
135 3         5 } @{$self->{asn}{attributes}};
  3         8  
136              
137 3 50       8 return unless @vals;
138              
139 3         7 $attr = \@vals;
140             }
141             else {
142 371 100       848 $attr = $attrs->{$type} or return;
143             }
144              
145             return $opt{asref}
146             ? $attr
147             : wantarray
148 252 100       614 ? @{$attr}
  100 100       350  
149             : $attr->[0];
150             }
151              
152              
153             sub changetype {
154              
155 16     16 1 26 my $self = shift;
156 16 100       51 return $self->{changetype} unless @_;
157 7         18 $self->{changes} = [];
158 7         14 $self->{changetype} = shift;
159 7         14 return $self;
160             }
161              
162              
163              
164             sub add {
165 75     75 1 776 my $self = shift;
166 75 100       159 my $cmd = $self->{changetype} eq 'modify' ? [] : undef;
167 75   66     173 my $attrs = $self->{attrs} ||= _build_attrs($self);
168              
169 75         226 while (my($type, $val) = splice(@_, 0, 2)) {
170 97         177 my $lc_type = lc $type;
171              
172 97         464 push @{$self->{asn}{attributes}}, { type => $type, vals => ($attrs->{$lc_type}=[])}
173 97 50       196 unless exists $attrs->{$lc_type};
174              
175 97 100       174 push @{$attrs->{$lc_type}}, ref($val) ? @$val : $val;
  97         432  
176              
177 97 100       338 push @$cmd, $type, [ ref($val) ? @$val : $val ]
    100          
178             if $cmd;
179              
180             }
181              
182 75 100       142 push(@{$self->{changes}}, 'add', $cmd) if $cmd;
  4         10  
183              
184 75         158 return $self;
185             }
186              
187              
188             sub replace {
189 1     1 1 3 my $self = shift;
190 1 50       4 my $cmd = $self->{changetype} eq 'modify' ? [] : undef;
191 1   33     4 my $attrs = $self->{attrs} ||= _build_attrs($self);
192              
193 1         4 while (my($type, $val) = splice(@_, 0, 2)) {
194 1         3 my $lc_type = lc $type;
195              
196 1 50 33     8 if (defined($val) and (!ref($val) or @$val)) {
      33        
197              
198 0         0 push @{$self->{asn}{attributes}}, { type => $type, vals => ($attrs->{$lc_type}=[])}
199 1 50       4 unless exists $attrs->{$lc_type};
200              
201 1 50       4 @{$attrs->{$lc_type}} = ref($val) ? @$val : ($val);
  1         3  
202              
203 1 50       7 push @$cmd, $type, [ ref($val) ? @$val : $val ]
    50          
204             if $cmd;
205              
206             }
207             else {
208 0         0 delete $attrs->{$lc_type};
209              
210 0         0 @{$self->{asn}{attributes}}
211 0         0 = grep { $lc_type ne lc($_->{type}) } @{$self->{asn}{attributes}};
  0         0  
  0         0  
212              
213 0 0       0 push @$cmd, $type, []
214             if $cmd;
215              
216             }
217             }
218              
219 1 50       4 push(@{$self->{changes}}, 'replace', $cmd) if $cmd;
  1         4  
220              
221 1         2 return $self;
222             }
223              
224              
225             sub delete {
226 2     2 1 4 my $self = shift;
227              
228 2 50       6 unless (@_) {
229 0         0 $self->changetype('delete');
230 0         0 return $self;
231             }
232              
233 2 50       8 my $cmd = $self->{changetype} eq 'modify' ? [] : undef;
234 2   33     7 my $attrs = $self->{attrs} ||= _build_attrs($self);
235              
236 2         10 while (my($type, $val) = splice(@_, 0, 2)) {
237 2         6 my $lc_type = lc $type;
238              
239 2 100 33     12 if (defined($val) and (!ref($val) or @$val)) {
      66        
240 1         2 my %values;
241 1 50       984 @values{(ref($val) ? @$val : $val)} = ();
242              
243 1 50       6 unless (@{$attrs->{$lc_type}}
  1         8  
244 5         12 = grep { !exists $values{$_} } @{$attrs->{$lc_type}})
  1         4  
245             {
246 0         0 delete $attrs->{$lc_type};
247 0         0 @{$self->{asn}{attributes}}
248 0         0 = grep { $lc_type ne lc($_->{type}) } @{$self->{asn}{attributes}};
  0         0  
  0         0  
249             }
250              
251 1 50       10 push @$cmd, $type, [ ref($val) ? @$val : $val ]
    50          
252             if $cmd;
253             }
254             else {
255 1         3 delete $attrs->{$lc_type};
256              
257 1         6 @{$self->{asn}{attributes}}
258 1         2 = grep { $lc_type ne lc($_->{type}) } @{$self->{asn}{attributes}};
  11         22  
  1         4  
259              
260 1 50       8 push @$cmd, $type, [] if $cmd;
261             }
262             }
263              
264 2 50       64 push(@{$self->{changes}}, 'delete', $cmd) if $cmd;
  2         8  
265              
266 2         39 return $self;
267             }
268              
269              
270             sub update {
271 0     0 1 0 my $self = shift;
272 0         0 my $target = shift; # a Net::LDAP or a Net::LDAP::LDIF object
273 0         0 my %opt = @_;
274 0         0 my $mesg;
275 0         0 my $user_cb = delete $opt{callback};
276 0 0   0   0 my $cb = sub { $self->changetype('modify') unless $_[0]->code;
277 0 0       0 $user_cb->(@_) if $user_cb };
  0         0  
278              
279 0 0       0 if (eval { $target->isa('Net::LDAP') }) {
  0 0       0  
280 0 0       0 if ($self->{changetype} eq 'add') {
    0          
    0          
    0          
281 0         0 $mesg = $target->add($self, callback => $cb, %opt);
282             }
283             elsif ($self->{changetype} eq 'delete') {
284 0         0 $mesg = $target->delete($self, callback => $cb, %opt);
285             }
286             elsif ($self->{changetype} =~ /modr?dn/o) {
287 0   0     0 my @args = (newrdn => $self->get_value('newrdn') || undef,
      0        
288             deleteoldrdn => $self->get_value('deleteoldrdn') || undef);
289 0         0 my $newsuperior = $self->get_value('newsuperior');
290 0 0       0 push(@args, newsuperior => $newsuperior) if $newsuperior;
291 0         0 $mesg = $target->moddn($self, @args, callback => $cb, %opt);
292             }
293 0         0 elsif (@{$self->{changes}}) {
294 0         0 $mesg = $target->modify($self, changes => $self->{changes}, callback => $cb, %opt);
295             }
296             else {
297 0         0 require Net::LDAP::Message;
298 0         0 $mesg = Net::LDAP::Message->new( $target );
299 0         0 $mesg->set_error(LDAP_LOCAL_ERROR, 'No attributes to update');
300             }
301             }
302 0         0 elsif (eval { $target->isa('Net::LDAP::LDIF') }) {
303 0         0 require Net::LDAP::Message;
304 0         0 $target->write_entry($self, %opt);
305 0         0 $mesg = Net::LDAP::Message::Dummy->new();
306 0 0       0 $mesg->set_error(LDAP_OTHER, $target->error())
307             if ($target->error());
308             }
309             else {
310 0         0 $mesg = Net::LDAP::Message::Dummy->new();
311 0         0 $mesg->set_error(LDAP_OTHER, 'illegal update target');
312             }
313              
314 0         0 return $mesg;
315             }
316              
317             sub ldif {
318 4     4 1 1638 my $self = shift;
319 4         11 my %opt = @_;
320              
321 4         25 require Net::LDAP::LDIF;
322 1     1   7 open(my $fh, '>', \my $buffer);
  1         3  
  1         6  
  4         71  
323 4 100       836 my $change = exists $opt{change} ? $opt{change} : $self->changes ? 1 : 0;
    100          
324 4         29 my $ldif = Net::LDAP::LDIF->new($fh, 'w', %opt, version => 0, change => $change);
325 4         15 $ldif->write_entry($self);
326 4         14 return $buffer;
327             }
328              
329             # Just for debugging
330              
331             sub dump {
332 0     0 1 0 my $self = shift;
333 22     22   175 no strict 'refs'; # select may return a GLOB name
  22         71  
  22         10815  
334 0 0       0 my $fh = @_ ? shift : select;
335              
336 0         0 my $asn = $self->{asn};
337 0         0 print $fh '-' x 72, "\n";
338 0 0       0 print $fh 'dn:', $asn->{objectName}, "\n\n" if $asn->{objectName};
339              
340 0         0 my $l = 0;
341              
342 0   0     0 for (keys %{ $self->{attrs} ||= _build_attrs($self) }) {
  0         0  
343 0 0       0 $l = length if length > $l;
344             }
345              
346 0         0 my $spc = "\n " . ' ' x $l;
347              
348 0         0 foreach my $attr (@{$asn->{attributes}}) {
  0         0  
349 0         0 my $val = $attr->{vals};
350 0         0 printf $fh "%${l}s: ", $attr->{type};
351 0         0 my $i = 0;
352 0         0 foreach my $v (@$val) {
353 0 0       0 print $fh $spc if $i++;
354 0         0 print $fh $v;
355             }
356 0         0 print $fh "\n";
357             }
358             }
359              
360             sub attributes {
361 24     24 1 35 my $self = shift;
362 24         41 my %opt = @_;
363              
364 24 100       49 if ($opt{nooptions}) {
365 1         2 my %done;
366             return map {
367 15         39 $_->{type} =~ /^([^;]+)/;
368 15 100       75 $done{lc $1}++ ? () : ($1);
369 1         2 } @{$self->{asn}{attributes}};
  1         3  
370             }
371             else {
372 23         31 return map { $_->{type} } @{$self->{asn}{attributes}};
  153         298  
  23         50  
373             }
374             }
375              
376             sub asn {
377             shift->{asn}
378 0     0 0 0 }
379              
380             sub changes {
381 11     11 0 23 my $ref = shift->{changes};
382 11 50       35 $ref ? @$ref : ();
383             }
384              
385             1;