File Coverage

blib/lib/Net/LDAP/LDIF.pm
Criterion Covered Total %
statement 275 362 75.9
branch 124 222 55.8
condition 57 147 38.7
subroutine 26 36 72.2
pod 13 17 76.4
total 495 784 63.1


line stmt bran cond sub pod time code
1             # Copyright (c) 1997-2008 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::LDIF;
6              
7 22     22   69757 use strict;
  22         52  
  22         893  
8             require Net::LDAP::Entry;
9              
10 22     22   106 use constant CHECK_UTF8 => $] > 5.007;
  22         39  
  22         1362  
11              
12             BEGIN {
13             require Encode
14 22     22   47048 if (CHECK_UTF8);
15             }
16              
17             our $VERSION = '0.27';
18              
19             # allow the letters r,w,a as mode letters
20             my %modes = qw(r < r+ +< w > w+ +> a >> a+ +>>);
21              
22             sub new {
23 12     12 1 4844 my $pkg = shift;
24 12   50     35 my $file = shift || '-';
25 12 50 50     84 my $mode = @_ % 2 ? shift || 'r' : 'r';
26 12         29 my %opt = @_;
27 12         15 my $fh;
28 12         16 my $opened_fh = 0;
29              
30             # harmonize mode
31             $mode = $modes{$mode}
32 12 50       38 if (defined($modes{$mode}));
33              
34 12 100       27 if (ref($file)) {
35 4         6 $fh = $file;
36             }
37             else {
38 8 50       20 if ($file eq '-') {
39 0 0       0 ($file,$fh) = ($mode eq '<')
40             ? ('STDIN', \*STDIN)
41             : ('STDOUT',\*STDOUT);
42              
43 0 0       0 if ($mode =~ /(:.*$)/) {
44 0         0 my $layer = $1;
45 0         0 binmode($file, $layer);
46             }
47             }
48             else {
49 8 50       449 $opened_fh = ($file =~ /^\| | \|$/x)
50             ? open($fh, $file)
51             : open($fh, $mode, $file);
52 8 50       49 return unless ($opened_fh);
53             }
54             }
55              
56             # Default the encoding of DNs to 'none' unless the user specifies
57 12 50       43 $opt{encode} = 'none' unless (exists $opt{encode});
58              
59             # Default the error handling to die
60 12 50       31 $opt{onerror} = 'die' unless (exists $opt{onerror});
61              
62             # sanitize options
63 12   50     55 $opt{lowercase} ||= 0;
64 12   100     40 $opt{change} ||= 0;
65 12   50     42 $opt{sort} ||= 0;
66 12   100     67 $opt{version} ||= 0;
67              
68 12 50 33     120 my $self = {
69             changetype => 'modify',
70             modify => 'add',
71             wrap => 78,
72             %opt,
73             fh => $fh,
74             file => "$file",
75             opened_fh => $opened_fh,
76             _eof => 0,
77             write_count => ($mode =~ /^\s*\+?>>/ and tell($fh) > 0) ? 1 : 0,
78             };
79              
80 12         71 bless $self, $pkg;
81             }
82              
83             sub _read_lines {
84 17     17   22 my $self = shift;
85 17         23 my $fh = $self->{fh};
86 17         24 my @ldif = ();
87 17         21 my $entry = '';
88 17         23 my $in_comment = 0;
89 17         19 my $entry_completed = 0;
90 17         18 my $ln;
91              
92 17 100       27 return @ldif if ($self->eof());
93              
94 15   100     156 while (defined($ln = $self->{_buffered_line} || scalar <$fh>)) {
95 1021         1124 delete($self->{_buffered_line});
96 1021 50       1282 if ($ln =~ /^#/o) { # ignore 1st line of comments
97 0         0 $in_comment = 1;
98             }
99             else {
100 1021 100       1475 if ($ln =~ /^[ \t]/o) { # append wrapped line (if not in a comment)
101 25 50       89 $entry .= $ln if (!$in_comment);
102             }
103             else {
104 996         962 $in_comment = 0;
105 996 100       1205 if ($ln =~ /^\r?\n$/o) {
106             # ignore empty line on start of entry
107             # empty line at non-empty entry indicate entry completion
108 12 100       50 $entry_completed++ if (length($entry));
109             }
110             else {
111 984 100       1073 if ($entry_completed) {
112 11         20 $self->{_buffered_line} = $ln;
113 11         15 last;
114             }
115             else {
116             # append non-empty line
117 973         3192 $entry .= $ln;
118             }
119             }
120             }
121             }
122             }
123 15 100       43 $self->eof(1) if (!defined($ln));
124 15         161 $self->{_current_lines} = $entry;
125 15         100 $entry =~ s/\r?\n //sgo; # un-wrap wrapped lines
126 15         31 $entry =~ s/\r?\n\t/ /sgo; # OpenLDAP extension !!!
127 15         408 @ldif = split(/^/, $entry);
128 15         38 map { s/\r?\n$//; } @ldif;
  973         2441  
129              
130 15         227 @ldif;
131             }
132              
133              
134             # read attribute value from URL
135             sub _read_url_attribute {
136 0     0   0 my $self = shift;
137 0         0 my $url = shift;
138 0         0 my @ldif = @_;
139 0         0 my $line;
140              
141 0 0 0     0 if ($url =~ s/^file:(?:\/\/)?//) {
    0          
142 0 0       0 open(my $fh, '<', $url)
143             or return $self->_error("can't open $url: $!", @ldif);
144              
145 0         0 binmode($fh);
146             { # slurp in whole file at once
147 0         0 local $/;
  0         0  
148 0         0 $line = <$fh>;
149             }
150 0         0 close($fh);
151             }
152             elsif ($url =~ /^(https?|ftp|gopher|news:)/ and
153 0         0 eval { require LWP::UserAgent; }) {
154 0         0 my $ua = LWP::UserAgent->new();
155 0         0 my $response = $ua->get($url);
156              
157 0 0       0 return $self->_error("can't get data from $url: $!", @ldif)
158             if (!$response->is_success);
159              
160 0         0 $line = $response->decoded_content();
161              
162 0 0       0 return $self->error("decoding data from $url failed: $@", @ldif)
163             if (!defined($line));
164             }
165             else {
166 0         0 return $self->_error('unsupported URL type', @ldif);
167             }
168              
169 0         0 $line;
170             }
171              
172              
173             # read attribute value (decode it based in its type)
174             sub _read_attribute_value {
175 15     15   40 my $self = shift;
176 15         35 my $type = shift;
177 15         20 my $value = shift;
178 15         100 my @ldif = @_;
179              
180             # Base64-encoded value: decode it
181 15 100 66     62 if ($type && $type eq ':') {
    50 33        
      33        
182 1         471 require MIME::Base64;
183 1         554 $value = MIME::Base64::decode($value);
184             }
185             # URL value: read from URL
186             elsif ($type && $type eq '<' and $value =~ s/^(.*?)\s*$/$1/) {
187 0         0 $value = $self->_read_url_attribute($value, @ldif);
188 0 0       0 return if (!defined($value));
189             }
190              
191 15         48 $value;
192             }
193              
194              
195             # _read_one() is deprecated and will be removed
196             # in a future version
197             *_read_one = \&_read_entry;
198              
199             sub _read_entry {
200 17     17   24 my $self = shift;
201 17         19 my @ldif;
202 17         37 $self->_clear_error();
203              
204 17         34 @ldif = $self->_read_lines;
205              
206 17 100       43 unless (@ldif) { # empty records are errors if not at eof
207 2 50       9 $self->_error('illegal empty LDIF entry') if (!$self->eof());
208 2         6 return;
209             }
210              
211 15 100 66     61 if (@ldif and $ldif[0] =~ /^version:\s+(\d+)/) {
212 1         3 $self->{version} = $1;
213 1         2 shift @ldif;
214 1 50       17 return $self->_read_entry
215             unless (@ldif);
216             }
217              
218 14 50       63 if (@ldif < 1) {
    50          
219 0         0 return $self->_error('LDIF entry is not valid', @ldif);
220             }
221             elsif ($ldif[0] !~ /^dn::? */) {
222 0         0 return $self->_error('First line of LDIF entry does not begin with "dn:"', @ldif);
223             }
224              
225 14         27 my $dn = shift @ldif;
226 14 50       74 my $xattr = $1 if ($dn =~ s/^dn:(:?) *//);
227              
228 14         45 $dn = $self->_read_attribute_value($xattr, $dn, @ldif);
229              
230 14         69 my $entry = Net::LDAP::Entry->new;
231             $dn = Encode::decode_utf8($dn)
232 14 50 33     35 if (CHECK_UTF8 && $self->{raw} && ('dn' !~ /$self->{raw}/));
233 14         46 $entry->dn($dn);
234              
235 14         21 my @controls = ();
236              
237             # optional control: line => change record
238 14   66     59 while (@ldif && ($ldif[0] =~ /^control:\s*/)) {
239 4         7 my $control = shift(@ldif);
240              
241 4 50       32 if ($control =~ /^control:\s*(\d+(?:\.\d+)*)(?:\s+(?i)(true|false))?(?:\s*:([:<])?\s*(.*))?$/) {
242 4         17 my($oid,$critical, $type, $value) = ($1,$2,$3, $4);
243              
244 4 50 33     20 $critical = ($critical && $critical =~ /true/i) ? 1 : 0;
245              
246 4 100       7 if (defined($value)) {
247 2 100       4 if ($type) {
248 1         3 $value = $self->_read_attribute_value($type, $value, @ldif);
249 1 50       4 return $self->_error('Illegal value in control line given', @ldif)
250             if !defined($value);
251             }
252             }
253              
254 4         19 require Net::LDAP::Control;
255 4         15 my $ctrl = Net::LDAP::Control->new(type => $oid,
256             value => $value,
257             critical => $critical);
258              
259 4         8 push(@controls, $ctrl);
260              
261 4 50       24 return $self->_error('Illegally formatted control line given', @ldif)
262             if (!@ldif);
263             }
264             else {
265 0         0 return $self->_error('Illegally formatted control line given', @ldif);
266             }
267             }
268              
269             # LDIF change record
270 14 100 66     50 if ((scalar @ldif) && ($ldif[0] =~ /^changetype:\s*/)) {
271             my $changetype = $ldif[0] =~ s/^changetype:\s*//
272 6 50       22 ? shift(@ldif) : $self->{changetype};
273 6         17 $entry->changetype($changetype);
274              
275 6 50       11 if ($changetype eq 'delete') {
276 0 0       0 return $self->_error('LDIF "delete" entry is not valid', @ldif)
277             if (@ldif);
278 0 0       0 return wantarray ? ($entry, @controls) : $entry;
279             }
280              
281 6 50       10 return $self->_error('LDAP entry is not valid', @ldif)
282             unless (@ldif);
283              
284 6         9 while (@ldif) {
285 6         8 my $action = $self->{modify};
286 6         12 my $modattr;
287             my $lastattr;
288 6         0 my @values;
289              
290 6 50       8 if ($changetype eq 'modify') {
291 0 0       0 unless ((my $tmp = shift @ldif) =~ s/^(add|delete|replace|increment):\s*([-;\w]+)//) {
292 0         0 return $self->_error('LDAP entry is not valid', @ldif);
293             }
294 0         0 $lastattr = $modattr = $2;
295 0         0 $action = $1;
296             }
297              
298 6         9 while (@ldif) {
299 60         68 my $line = shift @ldif;
300              
301 60 50       80 if ($line eq '-') {
302 0 0 0     0 return $self->_error('LDAP entry is not valid', @ldif)
303             if (!defined($modattr) || !defined($lastattr));
304              
305 0         0 last;
306             }
307              
308 60 50       180 if ($line =~ /^([-;\w]+):([\<\:]?)\s*(.*)$/o) {
309 60         125 my ($attr,$xattr,$val) = ($1,$2,$3);
310              
311 60 50 33     122 return $self->_error('LDAP entry is not valid', @ldif)
312             if (defined($modattr) && $attr ne $modattr);
313              
314 60 50       79 $val = $self->_read_attribute_value($xattr, $val, $line)
315             if ($xattr);
316 60 50       87 return if !defined($val);
317              
318             $val = Encode::decode_utf8($val)
319 60 50 33     87 if (CHECK_UTF8 && $self->{raw} && ($attr !~ /$self->{raw}/));
320              
321 60 100 100     129 if (!defined($lastattr) || $lastattr ne $attr) {
322 30 100       73 $entry->$action($lastattr => \@values)
323             if (defined $lastattr);
324              
325 30         41 $lastattr = $attr;
326 30         34 @values = ();
327             }
328 60         108 push(@values, $val);
329             }
330             else {
331 0         0 return $self->_error('LDAP entry is not valid', @ldif);
332             }
333             }
334 6 50       14 $entry->$action($lastattr => \@values)
335             if (defined $lastattr);
336             }
337             }
338             # content record (i.e. no 'changetype' line; implicitly treated as 'add')
339             else {
340 8         14 my $last = '';
341 8         10 my @values;
342              
343 8 50       17 return $self->_error('Controls only allowed with LDIF change entries', @ldif)
344             if (@controls);
345              
346 8         14 foreach my $line (@ldif) {
347 888 50       2440 if ($line =~ /^([-;\w]+):([\<\:]?)\s*(.*)$/o) {
348 888         2108 my($attr,$xattr,$val) = ($1,$2,$3);
349              
350 888 100       1440 $last = $attr if (!$last);
351              
352 888 50       1131 $val = $self->_read_attribute_value($xattr, $val, $line)
353             if ($xattr);
354 888 50       1072 return if !defined($val);
355              
356             $val = Encode::decode_utf8($val)
357 888 50 33     1304 if (CHECK_UTF8 && $self->{raw} && ($attr !~ /$self->{raw}/));
358              
359 888 100       1129 if ($attr ne $last) {
360 32         91 $entry->add($last => \@values);
361 32         101 @values = ();
362 32         47 $last = $attr;
363             }
364 888         1388 push(@values, $val);
365             }
366             else {
367 0         0 return $self->_error("illegal LDIF line '$line'", @ldif);
368             }
369             }
370 8         29 $entry->add($last => \@values);
371             }
372              
373 14         30 $self->{_current_entry} = $entry;
374              
375 14 100       121 return wantarray ? ($entry, @controls) : $entry;
376             }
377              
378             sub read_entry {
379 16     16 1 435 my $self = shift;
380              
381             return $self->_error('LDIF file handle not valid')
382 16 50       51 unless ($self->{fh});
383              
384 16         31 $self->_read_entry();
385             }
386              
387             # read() is deprecated and will be removed
388             # in a future version
389             sub read {
390 3     3 0 764 my $self = shift;
391              
392 3 100       14 return $self->read_entry() unless wantarray;
393              
394 1         2 my($entry, @entries);
395 1         2 push(@entries, $entry) while ($entry = $self->read_entry);
396              
397 1         4 @entries;
398             }
399              
400             sub eof {
401 23     23 1 32 my $self = shift;
402 23         38 my $eof = shift;
403              
404 23 100       40 $self->{_eof} = $eof
405             if ($eof);
406              
407 23         71 $self->{_eof};
408             }
409              
410             sub _wrap {
411 312     312   390 my $len = int($_[1]); # needs to be >= 2 to avoid division by zero
412 312 100 66     957 return $_[0] if (length($_[0]) <= $len or $len <= 40);
413 22     22   32101 use integer;
  22         295  
  22         102  
414 41         48 my $l2 = $len - 1;
415 41         45 my $x = (length($_[0]) - $len) / $l2;
416 41 50       76 my $extra = (length($_[0]) == ($l2 * $x + $len)) ? '' : 'a*';
417 41         196 join("\n ", unpack("a$len" . "a$l2" x $x . $extra, $_[0]));
418             }
419              
420             sub _write_attr {
421 150     150   225 my($self, $attr, $val) = @_;
422 150         163 my $lower = $self->{lowercase};
423 150         155 my $fh = $self->{fh};
424 150         151 my $res = 1; # result value
425              
426 150         204 foreach my $v (@$val) {
427 284 50       373 my $ln = $lower ? lc $attr : $attr;
428              
429 284 50       459 $v = Encode::encode_utf8($v)
430             if (CHECK_UTF8 and Encode::is_utf8($v));
431              
432 284 50       1351 if ($v =~ /(^[ :<]|[\x00-\x1f\x7f-\xff]| $)/) {
433 0         0 require MIME::Base64;
434 0         0 $ln .= ':: ' . MIME::Base64::encode($v, '');
435             }
436             else {
437 284         378 $ln .= ': ' . $v;
438             }
439 284   33     468 $res &&= print $fh _wrap($ln, $self->{wrap}), "\n";
440             }
441 150         335 $res;
442             }
443              
444             # helper function to compare attribute names (sort objectClass first)
445             sub _cmpAttrs {
446 0 0   0   0 ($a =~ /^objectclass$/io)
    0          
447             ? -1 : (($b =~ /^objectclass$/io) ? 1 : ($a cmp $b));
448             }
449              
450             sub _write_attrs {
451 22     22   28 my($self, $entry) = @_;
452 22         45 my @attributes = $entry->attributes();
453 22         25 my $res = 1; # result value
454              
455 22 50       42 @attributes = sort _cmpAttrs @attributes if ($self->{sort});
456              
457 22         38 foreach my $attr (@attributes) {
458 138         226 my $val = $entry->get_value($attr, asref => 1);
459 138   33     247 $res &&= $self->_write_attr($attr, $val);
460             }
461 22         53 $res;
462             }
463              
464             sub _write_controls {
465 3     3   8 my($self, @ctrls) = @_;
466 3         4 my $res = 1;
467 3         6 my $fh = $self->{fh};
468              
469 3         12 require Net::LDAP::Control;
470              
471 3         6 foreach my $ctrl (@ctrls) {
472 4 50       16 my $ln = 'control: ' . $ctrl->type . ($ctrl->critical ? ' true' : ' false');
473 4         10 my $v = $ctrl->value;
474              
475 4 100       8 if (defined($v)) {
476 2 50       6 $v = Encode::encode_utf8($v)
477             if (CHECK_UTF8 and Encode::is_utf8($v));
478              
479 2 100       12 if ($v =~ /(^[ :<]|[\x00-\x1f\x7f-\xff]| $)/) {
480 1         4 require MIME::Base64;
481 1         5 $v = MIME::Base64::encode($v, '');
482 1         10 $ln .= ':'; # indicate Base64-encoding of $v
483             }
484              
485 2         5 $ln .= ': ' . $v;
486             }
487 4   33     10 $res &&= print $fh _wrap($ln, $self->{wrap}), "\n";
488             }
489 3         8 $res;
490             }
491              
492             sub _write_dn {
493 24     24   39 my($self, $dn) = @_;
494 24         32 my $encode = $self->{encode};
495 24         32 my $fh = $self->{fh};
496              
497 24 50       61 $dn = Encode::encode_utf8($dn)
498             if (CHECK_UTF8 and Encode::is_utf8($dn));
499              
500 24 50       140 if ($dn =~ /^[ :<]|[\x00-\x1f\x7f-\xff]/) {
501 0 0       0 if ($encode =~ /canonical/i) {
    0          
502 0         0 require Net::LDAP::Util;
503 0         0 $dn = Net::LDAP::Util::canonical_dn($dn, mbcescape => 1);
504             # Canonicalizer won't fix leading spaces, colons or less-thans, which
505             # are special in LDIF, so we fix those up here.
506 0         0 $dn =~ s/^([ :<])/\\$1/;
507 0         0 $dn = "dn: $dn";
508             }
509             elsif ($encode =~ /base64/i) {
510 0         0 require MIME::Base64;
511 0         0 $dn = 'dn:: ' . MIME::Base64::encode($dn, '');
512             }
513             else {
514 0         0 $dn = "dn: $dn";
515             }
516             }
517             else {
518 24         46 $dn = "dn: $dn";
519             }
520 24         50 print $fh _wrap($dn, $self->{wrap}), "\n";
521             }
522              
523             # write() is deprecated and will be removed
524             # in a future version
525             sub write {
526 0     0 0 0 my $self = shift;
527              
528 0         0 $self->_write_entry(0, @_);
529             }
530              
531             sub write_entry {
532 8     8 1 307 my $self = shift;
533              
534 8         20 $self->_write_entry($self->{change}, @_);
535             }
536              
537             sub write_version {
538 8     8 1 11 my $self = shift;
539 8         11 my $fh = $self->{fh};
540 8         10 my $res = 1;
541              
542             $res &&= print $fh "version: $self->{version}\n"
543 8 100 33     44 if ($self->{version} && !$self->{version_written}++);
      66        
544              
545 8         22 return $res;
546             }
547              
548             # internal helper: write entry in different format depending on 1st arg
549             sub _write_entry {
550 9     9   13 my $self = shift;
551 9         11 my $change = shift;
552 9         9 my $res = 1; # result value
553 9         14 my @args = ();
554              
555             return $self->_error('LDIF file handle not valid')
556 9 50       19 unless ($self->{fh});
557              
558             # parse list of entries optionally interspersed with lists of option pairs
559             # each option-pair list belongs to the preceding entry
560             # e.g. $entry1, control => $ctrl1, $entry2, $entry3, control => [ $ctrl3a, $ctrl3b ], ...
561 9         16 foreach my $elem (@_) {
562 30 100       53 if (ref($elem)) {
    50          
563 27 100       48 if (scalar(@args) % 2) { # odd number of args: $entry + optional args
564 15   33     50 $res &&= $self->_write_one($change, @args);
565 15         22 @args = ();
566             }
567             }
568             elsif (!@args) { # 1st arg needs to be an N:L:E object
569 0         0 $self->_error("Entry '$elem' is not a valid Net::LDAP::Entry object.");
570 0         0 $res = 0;
571 0         0 @args = ();
572 0         0 next; # try to re-sync
573             }
574              
575 30         45 push(@args, $elem);
576             }
577              
578 9 50       14 if (scalar(@args) % 2) {
    0          
579 9   33     24 $res &&= $self->_write_one($change, @args);
580             }
581             elsif (@args) {
582 0         0 $self->error("Illegal argument list passed");
583 0         0 $res = 0;
584             }
585              
586 9 0 33     36 $self->_error($!) if (!$res && $!);
587              
588 9         19 $res;
589             }
590              
591             # internal helper to write exactly one entry
592             sub _write_one
593             {
594 24     24   31 my $self = shift;
595 24         27 my $change = shift;
596 24         26 my $entry = shift;
597 24         34 my %opt = @_;
598 24         29 my $fh = $self->{fh};
599 24         24 my $res = 1; # result value
600 24         51 local($\, $,); # output field and record separators
601              
602 24 100       33 if ($change) {
603 9         19 my @changes = $entry->changes;
604 9         18 my $type = $entry->changetype;
605              
606             # Skip entry if there is nothing to write
607 9 50 66     25 return $res if ($type eq 'modify' and !@changes);
608              
609 9 100 33     938 $res &&= $self->write_version() unless ($self->{write_count}++);
610 9   33     4486 $res &&= print $fh "\n";
611 9   33     30 $res &&= $self->_write_dn($entry->dn);
612              
613             $res &&= $self->_write_controls(ref($opt{control}) eq 'ARRAY'
614 3         35 ? @{$opt{control}}
615             : ( $opt{control} ))
616 9 50 33     24 if ($opt{control});
    100          
617              
618 9   33     46 $res &&= print $fh "changetype: $type\n";
619              
620 9 50       28 if ($type eq 'delete') {
    100          
    50          
621 0         0 return $res;
622             }
623             elsif ($type eq 'add') {
624 7   33     22 $res &&= $self->_write_attrs($entry);
625 7         27 return $res;
626             }
627             elsif ($type =~ /modr?dn/o) {
628 0   0     0 my $deleteoldrdn = $entry->get_value('deleteoldrdn') || 0;
629 0   0     0 $res &&= $self->_write_attr('newrdn', $entry->get_value('newrdn', asref => 1));
630 0   0     0 $res &&= print $fh 'deleteoldrdn: ', $deleteoldrdn, "\n";
631 0         0 my $ns = $entry->get_value('newsuperior', asref => 1);
632 0 0 0     0 $res &&= $self->_write_attr('newsuperior', $ns) if (defined $ns);
633 0         0 return $res;
634             }
635              
636 2         3 my $dash = 0;
637             # changetype: modify
638 2         6 while (my($action,$attrs) = splice(@changes, 0, 2)) {
639 10         16 my @attrs = @$attrs;
640              
641 10         18 while (my($attr,$val) = splice(@attrs, 0, 2)) {
642 12 100 33     47 $res &&= print $fh "-\n" if (!$self->{version} && $dash++);
      66        
643 12   33     41 $res &&= print $fh "$action: $attr\n";
644 12   33     28 $res &&= $self->_write_attr($attr, $val);
645 12 50 0     44 $res &&= print $fh "-\n" if ($self->{version});
646             }
647             }
648             }
649             else {
650 15 100 33     33 $res &&= $self->write_version() unless ($self->{write_count}++);
651 15   33     62 $res &&= print $fh "\n";
652 15   33     49 $res &&= $self->_write_dn($entry->dn);
653 15   33     34 $res &&= $self->_write_attrs($entry);
654             }
655              
656 17         53 $res;
657             }
658              
659             # read_cmd() is deprecated in favor of read_entry()
660             # and will be removed in a future version
661             sub read_cmd {
662 0     0 0 0 my $self = shift;
663              
664 0 0       0 return $self->read_entry() unless wantarray;
665              
666 0         0 my($entry, @entries);
667 0         0 push(@entries, $entry) while ($entry = $self->read_entry);
668              
669 0         0 @entries;
670             }
671              
672             # _read_one_cmd() is deprecated in favor of _read_one()
673             # and will be removed in a future version
674             *_read_one_cmd = \&_read_entry;
675              
676             # write_cmd() is deprecated in favor of write_entry()
677             # and will be removed in a future version
678             sub write_cmd {
679 1     1 0 5 my $self = shift;
680              
681 1         4 $self->_write_entry(1, @_);
682             }
683              
684             sub done {
685 15     15 1 865 my $self = shift;
686 15         22 my $res = 1; # result value
687              
688 15 100       33 if ($self->{fh}) {
689 12 100       24 if ($self->{opened_fh}) {
690 8         199 $res = close($self->{fh});
691 8         24 undef $self->{opened_fh};
692             }
693 12         36 delete $self->{fh};
694             }
695 15         344 $res;
696             }
697              
698             sub handle {
699 0     0 1 0 my $self = shift;
700              
701 0         0 return $self->{fh};
702             }
703              
704             my %onerror = (
705             die => sub {
706             my $self = shift;
707             require Carp;
708             $self->done;
709             Carp::croak($self->error(@_));
710             },
711             warn => sub {
712             my $self = shift;
713             require Carp;
714             Carp::carp($self->error(@_));
715             },
716             undef => sub {
717             my $self = shift;
718             require Carp;
719             Carp::carp($self->error(@_)) if ($^W);
720             },
721             );
722              
723             sub _error {
724 0     0   0 my ($self, $errmsg, @errlines) = @_;
725 0         0 $self->{_err_msg} = $errmsg;
726 0         0 $self->{_err_lines} = join("\n", @errlines);
727              
728 0         0 scalar &{ $onerror{ $self->{onerror} } }($self, $self->{_err_msg})
729 0 0       0 if ($self->{onerror});
730              
731 0         0 return;
732             }
733              
734             sub _clear_error {
735 17     17   22 my $self = shift;
736              
737 17         22 undef $self->{_err_msg};
738 17         25 undef $self->{_err_lines};
739             }
740              
741             sub error {
742 0     0 1 0 my $self = shift;
743 0         0 $self->{_err_msg};
744             }
745              
746             sub error_lines {
747 0     0 1 0 my $self = shift;
748 0         0 $self->{_err_lines};
749             }
750              
751             sub current_entry {
752 0     0 1 0 my $self = shift;
753 0         0 $self->{_current_entry};
754             }
755              
756             sub current_lines {
757 1     1 1 5 my $self = shift;
758 1         14 $self->{_current_lines};
759             }
760              
761             sub version {
762 1     1 1 4 my $self = shift;
763 1 50       15 return $self->{version} unless (@_);
764 0   0     0 $self->{version} = shift || 0;
765             }
766              
767             sub next_lines {
768 0     0 1 0 my $self = shift;
769 0         0 $self->{_next_lines};
770             }
771              
772             sub DESTROY {
773 12     12   977 my $self = shift;
774 12         35 $self->done();
775             }
776              
777             1;