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   84195 use strict;
  22         56  
  22         1094  
8             require Net::LDAP::Entry;
9              
10 22     22   139 use constant CHECK_UTF8 => $] > 5.007;
  22         44  
  22         1658  
11              
12             BEGIN {
13             require Encode
14 22     22   57474 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 6088 my $pkg = shift;
24 12   50     41 my $file = shift || '-';
25 12 50 50     58 my $mode = @_ % 2 ? shift || 'r' : 'r';
26 12         33 my %opt = @_;
27 12         21 my $fh;
28 12         21 my $opened_fh = 0;
29              
30             # harmonize mode
31             $mode = $modes{$mode}
32 12 50       47 if (defined($modes{$mode}));
33              
34 12 100       32 if (ref($file)) {
35 4         9 $fh = $file;
36             }
37             else {
38 8 50       22 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       579 $opened_fh = ($file =~ /^\| | \|$/x)
50             ? open($fh, $file)
51             : open($fh, $mode, $file);
52 8 50       61 return unless ($opened_fh);
53             }
54             }
55              
56             # Default the encoding of DNs to 'none' unless the user specifies
57 12 50       52 $opt{encode} = 'none' unless (exists $opt{encode});
58              
59             # Default the error handling to die
60 12 50       38 $opt{onerror} = 'die' unless (exists $opt{onerror});
61              
62             # sanitize options
63 12   50     104 $opt{lowercase} ||= 0;
64 12   100     49 $opt{change} ||= 0;
65 12   50     50 $opt{sort} ||= 0;
66 12   100     54 $opt{version} ||= 0;
67              
68 12 50 33     142 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         93 bless $self, $pkg;
81             }
82              
83             sub _read_lines {
84 17     17   26 my $self = shift;
85 17         28 my $fh = $self->{fh};
86 17         28 my @ldif = ();
87 17         27 my $entry = '';
88 17         25 my $in_comment = 0;
89 17         24 my $entry_completed = 0;
90 17         22 my $ln;
91              
92 17 100       32 return @ldif if ($self->eof());
93              
94 15   100     169 while (defined($ln = $self->{_buffered_line} || scalar <$fh>)) {
95 1021         1438 delete($self->{_buffered_line});
96 1021 50       1645 if ($ln =~ /^#/o) { # ignore 1st line of comments
97 0         0 $in_comment = 1;
98             }
99             else {
100 1021 100       1708 if ($ln =~ /^[ \t]/o) { # append wrapped line (if not in a comment)
101 25 50       100 $entry .= $ln if (!$in_comment);
102             }
103             else {
104 996         1159 $in_comment = 0;
105 996 100       1461 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       63 $entry_completed++ if (length($entry));
109             }
110             else {
111 984 100       1295 if ($entry_completed) {
112 11         20 $self->{_buffered_line} = $ln;
113 11         20 last;
114             }
115             else {
116             # append non-empty line
117 973         4144 $entry .= $ln;
118             }
119             }
120             }
121             }
122             }
123 15 100       51 $self->eof(1) if (!defined($ln));
124 15         220 $self->{_current_lines} = $entry;
125 15         143 $entry =~ s/\r?\n //sgo; # un-wrap wrapped lines
126 15         41 $entry =~ s/\r?\n\t/ /sgo; # OpenLDAP extension !!!
127 15         531 @ldif = split(/^/, $entry);
128 15         40 map { s/\r?\n$//; } @ldif;
  973         3038  
129              
130 15         292 @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   27 my $self = shift;
176 15         26 my $type = shift;
177 15         22 my $value = shift;
178 15         117 my @ldif = @_;
179              
180             # Base64-encoded value: decode it
181 15 100 66     79 if ($type && $type eq ':') {
    50 33        
      33        
182 1         603 require MIME::Base64;
183 1         755 $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         60 $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   28 my $self = shift;
201 17         27 my @ldif;
202 17         45 $self->_clear_error();
203              
204 17         45 @ldif = $self->_read_lines;
205              
206 17 100       57 unless (@ldif) { # empty records are errors if not at eof
207 2 50       14 $self->_error('illegal empty LDIF entry') if (!$self->eof());
208 2         7 return;
209             }
210              
211 15 100 66     76 if (@ldif and $ldif[0] =~ /^version:\s+(\d+)/) {
212 1         4 $self->{version} = $1;
213 1         2 shift @ldif;
214 1 50       20 return $self->_read_entry
215             unless (@ldif);
216             }
217              
218 14 50       75 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         29 my $dn = shift @ldif;
226 14 50       78 my $xattr = $1 if ($dn =~ s/^dn:(:?) *//);
227              
228 14         59 $dn = $self->_read_attribute_value($xattr, $dn, @ldif);
229              
230 14         71 my $entry = Net::LDAP::Entry->new;
231             $dn = Encode::decode_utf8($dn)
232 14 50 33     60 if (CHECK_UTF8 && $self->{raw} && ('dn' !~ /$self->{raw}/));
233 14         50 $entry->dn($dn);
234              
235 14         27 my @controls = ();
236              
237             # optional control: line => change record
238 14   66     76 while (@ldif && ($ldif[0] =~ /^control:\s*/)) {
239 4         9 my $control = shift(@ldif);
240              
241 4 50       32 if ($control =~ /^control:\s*(\d+(?:\.\d+)*)(?:\s+(?i)(true|false))?(?:\s*:([:<])?\s*(.*))?$/) {
242 4         20 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       9 if (defined($value)) {
247 2 100       5 if ($type) {
248 1         5 $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         24 require Net::LDAP::Control;
255 4         22 my $ctrl = Net::LDAP::Control->new(type => $oid,
256             value => $value,
257             critical => $critical);
258              
259 4         10 push(@controls, $ctrl);
260              
261 4 50       28 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     87 if ((scalar @ldif) && ($ldif[0] =~ /^changetype:\s*/)) {
271             my $changetype = $ldif[0] =~ s/^changetype:\s*//
272 6 50       25 ? shift(@ldif) : $self->{changetype};
273 6         21 $entry->changetype($changetype);
274              
275 6 50       13 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       11 return $self->_error('LDAP entry is not valid', @ldif)
282             unless (@ldif);
283              
284 6         15 while (@ldif) {
285 6         12 my $action = $self->{modify};
286 6         13 my $modattr;
287             my $lastattr;
288 6         0 my @values;
289              
290 6 50       13 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         14 while (@ldif) {
299 60         91 my $line = shift @ldif;
300              
301 60 50       97 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       235 if ($line =~ /^([-;\w]+):([\<\:]?)\s*(.*)$/o) {
309 60         165 my ($attr,$xattr,$val) = ($1,$2,$3);
310              
311 60 50 33     107 return $self->_error('LDAP entry is not valid', @ldif)
312             if (defined($modattr) && $attr ne $modattr);
313              
314 60 50       91 $val = $self->_read_attribute_value($xattr, $val, $line)
315             if ($xattr);
316 60 50       91 return if !defined($val);
317              
318             $val = Encode::decode_utf8($val)
319 60 50 33     106 if (CHECK_UTF8 && $self->{raw} && ($attr !~ /$self->{raw}/));
320              
321 60 100 100     178 if (!defined($lastattr) || $lastattr ne $attr) {
322 30 100       102 $entry->$action($lastattr => \@values)
323             if (defined $lastattr);
324              
325 30         50 $lastattr = $attr;
326 30         43 @values = ();
327             }
328 60         142 push(@values, $val);
329             }
330             else {
331 0         0 return $self->_error('LDAP entry is not valid', @ldif);
332             }
333             }
334 6 50       33 $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         18 my $last = '';
341 8         12 my @values;
342              
343 8 50       19 return $self->_error('Controls only allowed with LDIF change entries', @ldif)
344             if (@controls);
345              
346 8         19 foreach my $line (@ldif) {
347 888 50       3085 if ($line =~ /^([-;\w]+):([\<\:]?)\s*(.*)$/o) {
348 888         2539 my($attr,$xattr,$val) = ($1,$2,$3);
349              
350 888 100       1531 $last = $attr if (!$last);
351              
352 888 50       1298 $val = $self->_read_attribute_value($xattr, $val, $line)
353             if ($xattr);
354 888 50       1334 return if !defined($val);
355              
356             $val = Encode::decode_utf8($val)
357 888 50 33     1959 if (CHECK_UTF8 && $self->{raw} && ($attr !~ /$self->{raw}/));
358              
359 888 100       1422 if ($attr ne $last) {
360 32         141 $entry->add($last => \@values);
361 32         75 @values = ();
362 32         55 $last = $attr;
363             }
364 888         1659 push(@values, $val);
365             }
366             else {
367 0         0 return $self->_error("illegal LDIF line '$line'", @ldif);
368             }
369             }
370 8         37 $entry->add($last => \@values);
371             }
372              
373 14         50 $self->{_current_entry} = $entry;
374              
375 14 100       147 return wantarray ? ($entry, @controls) : $entry;
376             }
377              
378             sub read_entry {
379 16     16 1 594 my $self = shift;
380              
381             return $self->_error('LDIF file handle not valid')
382 16 50       79 unless ($self->{fh});
383              
384 16         102 $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 859 my $self = shift;
391              
392 3 100       15 return $self->read_entry() unless wantarray;
393              
394 1         2 my($entry, @entries);
395 1         4 push(@entries, $entry) while ($entry = $self->read_entry);
396              
397 1         5 @entries;
398             }
399              
400             sub eof {
401 23     23 1 37 my $self = shift;
402 23         31 my $eof = shift;
403              
404 23 100       50 $self->{_eof} = $eof
405             if ($eof);
406              
407 23         57 $self->{_eof};
408             }
409              
410             sub _wrap {
411 312     312   451 my $len = int($_[1]); # needs to be >= 2 to avoid division by zero
412 312 100 66     1322 return $_[0] if (length($_[0]) <= $len or $len <= 40);
413 22     22   40668 use integer;
  22         347  
  22         125  
414 41         61 my $l2 = $len - 1;
415 41         53 my $x = (length($_[0]) - $len) / $l2;
416 41 50       85 my $extra = (length($_[0]) == ($l2 * $x + $len)) ? '' : 'a*';
417 41         258 join("\n ", unpack("a$len" . "a$l2" x $x . $extra, $_[0]));
418             }
419              
420             sub _write_attr {
421 150     150   255 my($self, $attr, $val) = @_;
422 150         340 my $lower = $self->{lowercase};
423 150         198 my $fh = $self->{fh};
424 150         184 my $res = 1; # result value
425              
426 150         212 foreach my $v (@$val) {
427 284 50       495 my $ln = $lower ? lc $attr : $attr;
428              
429 284 50       557 $v = Encode::encode_utf8($v)
430             if (CHECK_UTF8 and Encode::is_utf8($v));
431              
432 284 50       1667 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         459 $ln .= ': ' . $v;
438             }
439 284   33     691 $res &&= print $fh _wrap($ln, $self->{wrap}), "\n";
440             }
441 150         421 $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   44 my($self, $entry) = @_;
452 22         55 my @attributes = $entry->attributes();
453 22         36 my $res = 1; # result value
454              
455 22 50       44 @attributes = sort _cmpAttrs @attributes if ($self->{sort});
456              
457 22         38 foreach my $attr (@attributes) {
458 138         327 my $val = $entry->get_value($attr, asref => 1);
459 138   33     321 $res &&= $self->_write_attr($attr, $val);
460             }
461 22         67 $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         16 require Net::LDAP::Control;
470              
471 3         8 foreach my $ctrl (@ctrls) {
472 4 50       23 my $ln = 'control: ' . $ctrl->type . ($ctrl->critical ? ' true' : ' false');
473 4         10 my $v = $ctrl->value;
474              
475 4 100       10 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       14 if ($v =~ /(^[ :<]|[\x00-\x1f\x7f-\xff]| $)/) {
480 1         6 require MIME::Base64;
481 1         4 $v = MIME::Base64::encode($v, '');
482 1         3 $ln .= ':'; # indicate Base64-encoding of $v
483             }
484              
485 2         5 $ln .= ': ' . $v;
486             }
487 4   33     14 $res &&= print $fh _wrap($ln, $self->{wrap}), "\n";
488             }
489 3         31 $res;
490             }
491              
492             sub _write_dn {
493 24     24   44 my($self, $dn) = @_;
494 24         39 my $encode = $self->{encode};
495 24         37 my $fh = $self->{fh};
496              
497 24 50       62 $dn = Encode::encode_utf8($dn)
498             if (CHECK_UTF8 and Encode::is_utf8($dn));
499              
500 24 50       194 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         66 $dn = "dn: $dn";
519             }
520 24         61 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 420 my $self = shift;
533              
534 8         30 $self->_write_entry($self->{change}, @_);
535             }
536              
537             sub write_version {
538 8     8 1 12 my $self = shift;
539 8         13 my $fh = $self->{fh};
540 8         13 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         38 return $res;
546             }
547              
548             # internal helper: write entry in different format depending on 1st arg
549             sub _write_entry {
550 9     9   12 my $self = shift;
551 9         18 my $change = shift;
552 9         11 my $res = 1; # result value
553 9         20 my @args = ();
554              
555             return $self->_error('LDIF file handle not valid')
556 9 50       23 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         22 foreach my $elem (@_) {
562 30 100       66 if (ref($elem)) {
    50          
563 27 100       62 if (scalar(@args) % 2) { # odd number of args: $entry + optional args
564 15   33     54 $res &&= $self->_write_one($change, @args);
565 15         27 @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         56 push(@args, $elem);
576             }
577              
578 9 50       29 if (scalar(@args) % 2) {
    0          
579 9   33     30 $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     27 $self->_error($!) if (!$res && $!);
587              
588 9         25 $res;
589             }
590              
591             # internal helper to write exactly one entry
592             sub _write_one
593             {
594 24     24   32 my $self = shift;
595 24         34 my $change = shift;
596 24         35 my $entry = shift;
597 24         998 my %opt = @_;
598 24         43 my $fh = $self->{fh};
599 24         27 my $res = 1; # result value
600 24         68 local($\, $,); # output field and record separators
601              
602 24 100       47 if ($change) {
603 9         24 my @changes = $entry->changes;
604 9         23 my $type = $entry->changetype;
605              
606             # Skip entry if there is nothing to write
607 9 50 66     30 return $res if ($type eq 'modify' and !@changes);
608              
609 9 100 33     33 $res &&= $self->write_version() unless ($self->{write_count}++);
610 9   33     47 $res &&= print $fh "\n";
611 9   33     34 $res &&= $self->_write_dn($entry->dn);
612              
613             $res &&= $self->_write_controls(ref($opt{control}) eq 'ARRAY'
614 3         9 ? @{$opt{control}}
615             : ( $opt{control} ))
616 9 50 33     31 if ($opt{control});
    100          
617              
618 9   33     45 $res &&= print $fh "changetype: $type\n";
619              
620 9 50       36 if ($type eq 'delete') {
    100          
    50          
621 0         0 return $res;
622             }
623             elsif ($type eq 'add') {
624 7   33     25 $res &&= $self->_write_attrs($entry);
625 7         34 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         9 while (my($action,$attrs) = splice(@changes, 0, 2)) {
639 10         22 my @attrs = @$attrs;
640              
641 10         22 while (my($attr,$val) = splice(@attrs, 0, 2)) {
642 12 100 33     64 $res &&= print $fh "-\n" if (!$self->{version} && $dash++);
      66        
643 12   33     107 $res &&= print $fh "$action: $attr\n";
644 12   33     72 $res &&= $self->_write_attr($attr, $val);
645 12 50 0     53 $res &&= print $fh "-\n" if ($self->{version});
646             }
647             }
648             }
649             else {
650 15 100 33     41 $res &&= $self->write_version() unless ($self->{write_count}++);
651 15   33     78 $res &&= print $fh "\n";
652 15   33     59 $res &&= $self->_write_dn($entry->dn);
653 15   33     41 $res &&= $self->_write_attrs($entry);
654             }
655              
656 17         68 $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 1024 my $self = shift;
686 15         23 my $res = 1; # result value
687              
688 15 100       43 if ($self->{fh}) {
689 12 100       42 if ($self->{opened_fh}) {
690 8         256 $res = close($self->{fh});
691 8         29 undef $self->{opened_fh};
692             }
693 12         44 delete $self->{fh};
694             }
695 15         430 $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         30 undef $self->{_err_msg};
738 17         33 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         4 $self->{_current_lines};
759             }
760              
761             sub version {
762 1     1 1 7 my $self = shift;
763 1 50       8 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   1245 my $self = shift;
774 12         35 $self->done();
775             }
776              
777             1;