xref: /openbsd-src/gnu/usr.bin/perl/cpan/libnet/lib/Net/Cmd.pm (revision e068048151d29f2562a32185e21a8ba885482260)
1b8851fccSafresh1# Net::Cmd.pm
2b8851fccSafresh1#
35759b3d2Safresh1# Copyright (C) 1995-2006 Graham Barr.  All rights reserved.
4*e0680481Safresh1# Copyright (C) 2013-2016, 2020, 2022 Steve Hay.  All rights reserved.
5b8851fccSafresh1# This module is free software; you can redistribute it and/or modify it under
6b8851fccSafresh1# the same terms as Perl itself, i.e. under the terms of either the GNU General
7b8851fccSafresh1# Public License or the Artistic License, as specified in the F<LICENCE> file.
8b8851fccSafresh1
9b8851fccSafresh1package Net::Cmd;
10b8851fccSafresh1
11b8851fccSafresh1use 5.008001;
12b8851fccSafresh1
13b8851fccSafresh1use strict;
14b8851fccSafresh1use warnings;
15b8851fccSafresh1
16b8851fccSafresh1use Carp;
17b8851fccSafresh1use Exporter;
18b8851fccSafresh1use Symbol 'gensym';
19b8851fccSafresh1use Errno 'EINTR';
20b8851fccSafresh1
21b8851fccSafresh1BEGIN {
22eac174f2Safresh1  if (ord "A" == 193) {
23b8851fccSafresh1    require Convert::EBCDIC;
24b8851fccSafresh1
25b8851fccSafresh1    #    Convert::EBCDIC->import;
26b8851fccSafresh1  }
27b8851fccSafresh1}
28b8851fccSafresh1
29*e0680481Safresh1our $VERSION = "3.15";
30b8851fccSafresh1our @ISA     = qw(Exporter);
31b8851fccSafresh1our @EXPORT  = qw(CMD_INFO CMD_OK CMD_MORE CMD_REJECT CMD_ERROR CMD_PENDING);
32b8851fccSafresh1
33b8851fccSafresh1use constant CMD_INFO    => 1;
34b8851fccSafresh1use constant CMD_OK      => 2;
35b8851fccSafresh1use constant CMD_MORE    => 3;
36b8851fccSafresh1use constant CMD_REJECT  => 4;
37b8851fccSafresh1use constant CMD_ERROR   => 5;
38b8851fccSafresh1use constant CMD_PENDING => 0;
39b8851fccSafresh1
40b8851fccSafresh1use constant DEF_REPLY_CODE => 421;
41b8851fccSafresh1
42b8851fccSafresh1my %debug = ();
43b8851fccSafresh1
44eac174f2Safresh1my $tr = ord "A" == 193 ? Convert::EBCDIC->new() : undef;
45b8851fccSafresh1
46b8851fccSafresh1sub toebcdic {
47b8851fccSafresh1  my $cmd = shift;
48b8851fccSafresh1
49b8851fccSafresh1  unless (exists ${*$cmd}{'net_cmd_asciipeer'}) {
50b8851fccSafresh1    my $string    = $_[0];
51b8851fccSafresh1    my $ebcdicstr = $tr->toebcdic($string);
52b8851fccSafresh1    ${*$cmd}{'net_cmd_asciipeer'} = $string !~ /^\d+/ && $ebcdicstr =~ /^\d+/;
53b8851fccSafresh1  }
54b8851fccSafresh1
55b8851fccSafresh1  ${*$cmd}{'net_cmd_asciipeer'}
56b8851fccSafresh1    ? $tr->toebcdic($_[0])
57b8851fccSafresh1    : $_[0];
58b8851fccSafresh1}
59b8851fccSafresh1
60b8851fccSafresh1
61b8851fccSafresh1sub toascii {
62b8851fccSafresh1  my $cmd = shift;
63b8851fccSafresh1  ${*$cmd}{'net_cmd_asciipeer'}
64b8851fccSafresh1    ? $tr->toascii($_[0])
65b8851fccSafresh1    : $_[0];
66b8851fccSafresh1}
67b8851fccSafresh1
68b8851fccSafresh1
69b8851fccSafresh1sub _print_isa {
70b8851fccSafresh1  no strict 'refs'; ## no critic (TestingAndDebugging::ProhibitNoStrict)
71b8851fccSafresh1
72b8851fccSafresh1  my $pkg = shift;
73b8851fccSafresh1  my $cmd = $pkg;
74b8851fccSafresh1
75b8851fccSafresh1  $debug{$pkg} ||= 0;
76b8851fccSafresh1
77b8851fccSafresh1  my %done = ();
78b8851fccSafresh1  my @do   = ($pkg);
79b8851fccSafresh1  my %spc  = ($pkg, "");
80b8851fccSafresh1
81b8851fccSafresh1  while ($pkg = shift @do) {
82b8851fccSafresh1    next if defined $done{$pkg};
83b8851fccSafresh1
84b8851fccSafresh1    $done{$pkg} = 1;
85b8851fccSafresh1
86b8851fccSafresh1    my $v =
87b8851fccSafresh1      defined ${"${pkg}::VERSION"}
88b8851fccSafresh1      ? "(" . ${"${pkg}::VERSION"} . ")"
89b8851fccSafresh1      : "";
90b8851fccSafresh1
91b8851fccSafresh1    my $spc = $spc{$pkg};
92b8851fccSafresh1    $cmd->debug_print(1, "${spc}${pkg}${v}\n");
93b8851fccSafresh1
94b8851fccSafresh1    if (@{"${pkg}::ISA"}) {
95b8851fccSafresh1      @spc{@{"${pkg}::ISA"}} = ("  " . $spc{$pkg}) x @{"${pkg}::ISA"};
96b8851fccSafresh1      unshift(@do, @{"${pkg}::ISA"});
97b8851fccSafresh1    }
98b8851fccSafresh1  }
99b8851fccSafresh1}
100b8851fccSafresh1
101b8851fccSafresh1
102b8851fccSafresh1sub debug {
103eac174f2Safresh1  @_ == 1 or @_ == 2 or croak 'usage: $obj->debug([$level])';
104b8851fccSafresh1
105b8851fccSafresh1  my ($cmd, $level) = @_;
106b8851fccSafresh1  my $pkg    = ref($cmd) || $cmd;
107b8851fccSafresh1  my $oldval = 0;
108b8851fccSafresh1
109b8851fccSafresh1  if (ref($cmd)) {
110b8851fccSafresh1    $oldval = ${*$cmd}{'net_cmd_debug'} || 0;
111b8851fccSafresh1  }
112b8851fccSafresh1  else {
113b8851fccSafresh1    $oldval = $debug{$pkg} || 0;
114b8851fccSafresh1  }
115b8851fccSafresh1
116b8851fccSafresh1  return $oldval
117b8851fccSafresh1    unless @_ == 2;
118b8851fccSafresh1
119b8851fccSafresh1  $level = $debug{$pkg} || 0
120b8851fccSafresh1    unless defined $level;
121b8851fccSafresh1
122b8851fccSafresh1  _print_isa($pkg)
123b8851fccSafresh1    if ($level && !exists $debug{$pkg});
124b8851fccSafresh1
125b8851fccSafresh1  if (ref($cmd)) {
126b8851fccSafresh1    ${*$cmd}{'net_cmd_debug'} = $level;
127b8851fccSafresh1  }
128b8851fccSafresh1  else {
129b8851fccSafresh1    $debug{$pkg} = $level;
130b8851fccSafresh1  }
131b8851fccSafresh1
132b8851fccSafresh1  $oldval;
133b8851fccSafresh1}
134b8851fccSafresh1
135b8851fccSafresh1
136b8851fccSafresh1sub message {
137b8851fccSafresh1  @_ == 1 or croak 'usage: $obj->message()';
138b8851fccSafresh1
139b8851fccSafresh1  my $cmd = shift;
140b8851fccSafresh1
141b8851fccSafresh1  wantarray
142b8851fccSafresh1    ? @{${*$cmd}{'net_cmd_resp'}}
143b8851fccSafresh1    : join("", @{${*$cmd}{'net_cmd_resp'}});
144b8851fccSafresh1}
145b8851fccSafresh1
146b8851fccSafresh1
147b8851fccSafresh1sub debug_text { $_[2] }
148b8851fccSafresh1
149b8851fccSafresh1
150b8851fccSafresh1sub debug_print {
151b8851fccSafresh1  my ($cmd, $out, $text) = @_;
152b8851fccSafresh1  print STDERR $cmd, ($out ? '>>> ' : '<<< '), $cmd->debug_text($out, $text);
153b8851fccSafresh1}
154b8851fccSafresh1
155b8851fccSafresh1
156b8851fccSafresh1sub code {
157b8851fccSafresh1  @_ == 1 or croak 'usage: $obj->code()';
158b8851fccSafresh1
159b8851fccSafresh1  my $cmd = shift;
160b8851fccSafresh1
161b8851fccSafresh1  ${*$cmd}{'net_cmd_code'} = $cmd->DEF_REPLY_CODE
162b8851fccSafresh1    unless exists ${*$cmd}{'net_cmd_code'};
163b8851fccSafresh1
164b8851fccSafresh1  ${*$cmd}{'net_cmd_code'};
165b8851fccSafresh1}
166b8851fccSafresh1
167b8851fccSafresh1
168b8851fccSafresh1sub status {
169b8851fccSafresh1  @_ == 1 or croak 'usage: $obj->status()';
170b8851fccSafresh1
171b8851fccSafresh1  my $cmd = shift;
172b8851fccSafresh1
173b8851fccSafresh1  substr(${*$cmd}{'net_cmd_code'}, 0, 1);
174b8851fccSafresh1}
175b8851fccSafresh1
176b8851fccSafresh1
177b8851fccSafresh1sub set_status {
178eac174f2Safresh1  @_ == 3 or croak 'usage: $obj->set_status($code, $resp)';
179b8851fccSafresh1
180b8851fccSafresh1  my $cmd = shift;
181b8851fccSafresh1  my ($code, $resp) = @_;
182b8851fccSafresh1
183b8851fccSafresh1  $resp = defined $resp ? [$resp] : []
184b8851fccSafresh1    unless ref($resp);
185b8851fccSafresh1
186b8851fccSafresh1  (${*$cmd}{'net_cmd_code'}, ${*$cmd}{'net_cmd_resp'}) = ($code, $resp);
187b8851fccSafresh1
188b8851fccSafresh1  1;
189b8851fccSafresh1}
190b8851fccSafresh1
191b8851fccSafresh1sub _syswrite_with_timeout {
192b8851fccSafresh1  my $cmd = shift;
193b8851fccSafresh1  my $line = shift;
194b8851fccSafresh1
195b8851fccSafresh1  my $len    = length($line);
196b8851fccSafresh1  my $offset = 0;
197b8851fccSafresh1  my $win    = "";
198b8851fccSafresh1  vec($win, fileno($cmd), 1) = 1;
199b8851fccSafresh1  my $timeout = $cmd->timeout || undef;
200b8851fccSafresh1  my $initial = time;
201b8851fccSafresh1  my $pending = $timeout;
202b8851fccSafresh1
203b8851fccSafresh1  local $SIG{PIPE} = 'IGNORE' unless $^O eq 'MacOS';
204b8851fccSafresh1
205b8851fccSafresh1  while ($len) {
206b8851fccSafresh1    my $wout;
207b8851fccSafresh1    my $nfound = select(undef, $wout = $win, undef, $pending);
208b8851fccSafresh1    if ((defined $nfound and $nfound > 0) or -f $cmd)    # -f for testing on win32
209b8851fccSafresh1    {
210b8851fccSafresh1      my $w = syswrite($cmd, $line, $len, $offset);
211b8851fccSafresh1      if (! defined($w) ) {
212b8851fccSafresh1        my $err = $!;
213b8851fccSafresh1        $cmd->close;
214b8851fccSafresh1        $cmd->_set_status_closed($err);
215b8851fccSafresh1        return;
216b8851fccSafresh1      }
217b8851fccSafresh1      $len -= $w;
218b8851fccSafresh1      $offset += $w;
219b8851fccSafresh1    }
220b8851fccSafresh1    elsif ($nfound == -1) {
221b8851fccSafresh1      if ( $! == EINTR ) {
222b8851fccSafresh1        if ( defined($timeout) ) {
223b8851fccSafresh1          redo if ($pending = $timeout - ( time - $initial ) ) > 0;
224b8851fccSafresh1          $cmd->_set_status_timeout;
225b8851fccSafresh1          return;
226b8851fccSafresh1        }
227b8851fccSafresh1        redo;
228b8851fccSafresh1      }
229b8851fccSafresh1      my $err = $!;
230b8851fccSafresh1      $cmd->close;
231b8851fccSafresh1      $cmd->_set_status_closed($err);
232b8851fccSafresh1      return;
233b8851fccSafresh1    }
234b8851fccSafresh1    else {
235b8851fccSafresh1      $cmd->_set_status_timeout;
236b8851fccSafresh1      return;
237b8851fccSafresh1    }
238b8851fccSafresh1  }
239b8851fccSafresh1
240b8851fccSafresh1  return 1;
241b8851fccSafresh1}
242b8851fccSafresh1
243b8851fccSafresh1sub _set_status_timeout {
244b8851fccSafresh1  my $cmd = shift;
245b8851fccSafresh1  my $pkg = ref($cmd) || $cmd;
246b8851fccSafresh1
247b8851fccSafresh1  $cmd->set_status($cmd->DEF_REPLY_CODE, "[$pkg] Timeout");
248b8851fccSafresh1  carp(ref($cmd) . ": " . (caller(1))[3] . "(): timeout") if $cmd->debug;
249b8851fccSafresh1}
250b8851fccSafresh1
251b8851fccSafresh1sub _set_status_closed {
252b8851fccSafresh1  my $cmd = shift;
253b8851fccSafresh1  my $err = shift;
254b8851fccSafresh1  my $pkg = ref($cmd) || $cmd;
255b8851fccSafresh1
256b8851fccSafresh1  $cmd->set_status($cmd->DEF_REPLY_CODE, "[$pkg] Connection closed");
257b8851fccSafresh1  carp(ref($cmd) . ": " . (caller(1))[3]
258b8851fccSafresh1    . "(): unexpected EOF on command channel: $err") if $cmd->debug;
259b8851fccSafresh1}
260b8851fccSafresh1
261b8851fccSafresh1sub _is_closed {
262b8851fccSafresh1  my $cmd = shift;
263b8851fccSafresh1  if (!defined fileno($cmd)) {
264b8851fccSafresh1     $cmd->_set_status_closed($!);
265b8851fccSafresh1     return 1;
266b8851fccSafresh1  }
267b8851fccSafresh1  return 0;
268b8851fccSafresh1}
269b8851fccSafresh1
270b8851fccSafresh1sub command {
271b8851fccSafresh1  my $cmd = shift;
272b8851fccSafresh1
273b8851fccSafresh1  return $cmd
274b8851fccSafresh1    if $cmd->_is_closed;
275b8851fccSafresh1
276b8851fccSafresh1  $cmd->dataend()
277b8851fccSafresh1    if (exists ${*$cmd}{'net_cmd_last_ch'});
278b8851fccSafresh1
279b8851fccSafresh1  if (scalar(@_)) {
280b8851fccSafresh1    my $str = join(
281b8851fccSafresh1      " ",
282b8851fccSafresh1      map {
283b8851fccSafresh1        /\n/
284b8851fccSafresh1          ? do { my $n = $_; $n =~ tr/\n/ /; $n }
285b8851fccSafresh1          : $_;
286b8851fccSafresh1        } @_
287b8851fccSafresh1    );
288b8851fccSafresh1    $str = $cmd->toascii($str) if $tr;
289b8851fccSafresh1    $str .= "\015\012";
290b8851fccSafresh1
291b8851fccSafresh1    $cmd->debug_print(1, $str)
292b8851fccSafresh1      if ($cmd->debug);
293b8851fccSafresh1
294b8851fccSafresh1    # though documented to return undef on failure, the legacy behavior
295b8851fccSafresh1    # was to return $cmd even on failure, so this odd construct does that
296b8851fccSafresh1    $cmd->_syswrite_with_timeout($str)
297b8851fccSafresh1      or return $cmd;
298b8851fccSafresh1  }
299b8851fccSafresh1
300b8851fccSafresh1  $cmd;
301b8851fccSafresh1}
302b8851fccSafresh1
303b8851fccSafresh1
304b8851fccSafresh1sub ok {
305b8851fccSafresh1  @_ == 1 or croak 'usage: $obj->ok()';
306b8851fccSafresh1
307b8851fccSafresh1  my $code = $_[0]->code;
308b8851fccSafresh1  0 < $code && $code < 400;
309b8851fccSafresh1}
310b8851fccSafresh1
311b8851fccSafresh1
312b8851fccSafresh1sub unsupported {
313b8851fccSafresh1  my $cmd = shift;
314b8851fccSafresh1
315b8851fccSafresh1  $cmd->set_status(580, 'Unsupported command');
316b8851fccSafresh1
317b8851fccSafresh1  0;
318b8851fccSafresh1}
319b8851fccSafresh1
320b8851fccSafresh1
321b8851fccSafresh1sub getline {
322b8851fccSafresh1  my $cmd = shift;
323b8851fccSafresh1
324b8851fccSafresh1  ${*$cmd}{'net_cmd_lines'} ||= [];
325b8851fccSafresh1
326b8851fccSafresh1  return shift @{${*$cmd}{'net_cmd_lines'}}
327b8851fccSafresh1    if scalar(@{${*$cmd}{'net_cmd_lines'}});
328b8851fccSafresh1
329b8851fccSafresh1  my $partial = defined(${*$cmd}{'net_cmd_partial'}) ? ${*$cmd}{'net_cmd_partial'} : "";
330b8851fccSafresh1
331b8851fccSafresh1  return
332b8851fccSafresh1    if $cmd->_is_closed;
333b8851fccSafresh1
334b8851fccSafresh1  my $fd = fileno($cmd);
335b8851fccSafresh1  my $rin = "";
336b8851fccSafresh1  vec($rin, $fd, 1) = 1;
337b8851fccSafresh1
338b8851fccSafresh1  my $buf;
339b8851fccSafresh1
340b8851fccSafresh1  until (scalar(@{${*$cmd}{'net_cmd_lines'}})) {
341b8851fccSafresh1    my $timeout = $cmd->timeout || undef;
342b8851fccSafresh1    my $rout;
343b8851fccSafresh1
344b8851fccSafresh1    my $select_ret = select($rout = $rin, undef, undef, $timeout);
345b8851fccSafresh1    if ($select_ret > 0) {
346b8851fccSafresh1      unless (sysread($cmd, $buf = "", 1024)) {
347b8851fccSafresh1        my $err = $!;
348b8851fccSafresh1        $cmd->close;
349b8851fccSafresh1        $cmd->_set_status_closed($err);
350b8851fccSafresh1        return;
351b8851fccSafresh1      }
352b8851fccSafresh1
353b8851fccSafresh1      substr($buf, 0, 0) = $partial;    ## prepend from last sysread
354b8851fccSafresh1
355b8851fccSafresh1      my @buf = split(/\015?\012/, $buf, -1);    ## break into lines
356b8851fccSafresh1
357b8851fccSafresh1      $partial = pop @buf;
358b8851fccSafresh1
359b8851fccSafresh1      push(@{${*$cmd}{'net_cmd_lines'}}, map {"$_\n"} @buf);
360b8851fccSafresh1
361b8851fccSafresh1    }
362b8851fccSafresh1    else {
363b8851fccSafresh1      $cmd->_set_status_timeout;
364b8851fccSafresh1      return;
365b8851fccSafresh1    }
366b8851fccSafresh1  }
367b8851fccSafresh1
368b8851fccSafresh1  ${*$cmd}{'net_cmd_partial'} = $partial;
369b8851fccSafresh1
370b8851fccSafresh1  if ($tr) {
371b8851fccSafresh1    foreach my $ln (@{${*$cmd}{'net_cmd_lines'}}) {
372b8851fccSafresh1      $ln = $cmd->toebcdic($ln);
373b8851fccSafresh1    }
374b8851fccSafresh1  }
375b8851fccSafresh1
376b8851fccSafresh1  shift @{${*$cmd}{'net_cmd_lines'}};
377b8851fccSafresh1}
378b8851fccSafresh1
379b8851fccSafresh1
380b8851fccSafresh1sub ungetline {
381b8851fccSafresh1  my ($cmd, $str) = @_;
382b8851fccSafresh1
383b8851fccSafresh1  ${*$cmd}{'net_cmd_lines'} ||= [];
384b8851fccSafresh1  unshift(@{${*$cmd}{'net_cmd_lines'}}, $str);
385b8851fccSafresh1}
386b8851fccSafresh1
387b8851fccSafresh1
388b8851fccSafresh1sub parse_response {
389b8851fccSafresh1  return ()
390b8851fccSafresh1    unless $_[1] =~ s/^(\d\d\d)(.?)//o;
391b8851fccSafresh1  ($1, $2 eq "-");
392b8851fccSafresh1}
393b8851fccSafresh1
394b8851fccSafresh1
395b8851fccSafresh1sub response {
396b8851fccSafresh1  my $cmd = shift;
397b8851fccSafresh1  my ($code, $more) = (undef) x 2;
398b8851fccSafresh1
399b8851fccSafresh1  $cmd->set_status($cmd->DEF_REPLY_CODE, undef); # initialize the response
400b8851fccSafresh1
401b8851fccSafresh1  while (1) {
402b8851fccSafresh1    my $str = $cmd->getline();
403b8851fccSafresh1
404b8851fccSafresh1    return CMD_ERROR
405b8851fccSafresh1      unless defined($str);
406b8851fccSafresh1
407b8851fccSafresh1    $cmd->debug_print(0, $str)
408b8851fccSafresh1      if ($cmd->debug);
409b8851fccSafresh1
410b8851fccSafresh1    ($code, $more) = $cmd->parse_response($str);
411b8851fccSafresh1    unless (defined $code) {
412b8851fccSafresh1      carp("$cmd: response(): parse error in '$str'") if ($cmd->debug);
413b8851fccSafresh1      $cmd->ungetline($str);
414b8851fccSafresh1      $@ = $str;   # $@ used as tunneling hack
415b8851fccSafresh1      return CMD_ERROR;
416b8851fccSafresh1    }
417b8851fccSafresh1
418b8851fccSafresh1    ${*$cmd}{'net_cmd_code'} = $code;
419b8851fccSafresh1
420b8851fccSafresh1    push(@{${*$cmd}{'net_cmd_resp'}}, $str);
421b8851fccSafresh1
422b8851fccSafresh1    last unless ($more);
423b8851fccSafresh1  }
424b8851fccSafresh1
425b8851fccSafresh1  return unless defined $code;
426b8851fccSafresh1  substr($code, 0, 1);
427b8851fccSafresh1}
428b8851fccSafresh1
429b8851fccSafresh1
430b8851fccSafresh1sub read_until_dot {
431b8851fccSafresh1  my $cmd = shift;
432b8851fccSafresh1  my $fh  = shift;
433b8851fccSafresh1  my $arr = [];
434b8851fccSafresh1
435b8851fccSafresh1  while (1) {
436b8851fccSafresh1    my $str = $cmd->getline() or return;
437b8851fccSafresh1
438b8851fccSafresh1    $cmd->debug_print(0, $str)
439b8851fccSafresh1      if ($cmd->debug & 4);
440b8851fccSafresh1
441b8851fccSafresh1    last if ($str =~ /^\.\r?\n/o);
442b8851fccSafresh1
443b8851fccSafresh1    $str =~ s/^\.\././o;
444b8851fccSafresh1
445b8851fccSafresh1    if (defined $fh) {
446b8851fccSafresh1      print $fh $str;
447b8851fccSafresh1    }
448b8851fccSafresh1    else {
449b8851fccSafresh1      push(@$arr, $str);
450b8851fccSafresh1    }
451b8851fccSafresh1  }
452b8851fccSafresh1
453b8851fccSafresh1  $arr;
454b8851fccSafresh1}
455b8851fccSafresh1
456b8851fccSafresh1
457b8851fccSafresh1sub datasend {
458b8851fccSafresh1  my $cmd  = shift;
459b8851fccSafresh1  my $arr  = @_ == 1 && ref($_[0]) ? $_[0] : \@_;
460b8851fccSafresh1  my $line = join("", @$arr);
461b8851fccSafresh1
462b8851fccSafresh1  # Perls < 5.10.1 (with the exception of 5.8.9) have a performance problem with
463b8851fccSafresh1  # the substitutions below when dealing with strings stored internally in
464b8851fccSafresh1  # UTF-8, so downgrade them (if possible).
465b8851fccSafresh1  # Data passed to datasend() should be encoded to octets upstream already so
466b8851fccSafresh1  # shouldn't even have the UTF-8 flag on to start with, but if it so happens
467b8851fccSafresh1  # that the octets are stored in an upgraded string (as can sometimes occur)
468b8851fccSafresh1  # then they would still downgrade without fail anyway.
469b8851fccSafresh1  # Only Unicode codepoints > 0xFF stored in an upgraded string will fail to
470b8851fccSafresh1  # downgrade. We fail silently in that case, and a "Wide character in print"
471b8851fccSafresh1  # warning will be emitted later by syswrite().
472b8851fccSafresh1  utf8::downgrade($line, 1) if $] < 5.010001 && $] != 5.008009;
473b8851fccSafresh1
474b8851fccSafresh1  return 0
475b8851fccSafresh1    if $cmd->_is_closed;
476b8851fccSafresh1
477b8851fccSafresh1  my $last_ch = ${*$cmd}{'net_cmd_last_ch'};
478b8851fccSafresh1
479b8851fccSafresh1  # We have not send anything yet, so last_ch = "\012" means we are at the start of a line
480b8851fccSafresh1  $last_ch = ${*$cmd}{'net_cmd_last_ch'} = "\012" unless defined $last_ch;
481b8851fccSafresh1
482b8851fccSafresh1  return 1 unless length $line;
483b8851fccSafresh1
484b8851fccSafresh1  if ($cmd->debug) {
485b8851fccSafresh1    foreach my $b (split(/\n/, $line)) {
486b8851fccSafresh1      $cmd->debug_print(1, "$b\n");
487b8851fccSafresh1    }
488b8851fccSafresh1  }
489b8851fccSafresh1
490b8851fccSafresh1  $line =~ tr/\r\n/\015\012/ unless "\r" eq "\015";
491b8851fccSafresh1
492b8851fccSafresh1  my $first_ch = '';
493b8851fccSafresh1
494b8851fccSafresh1  if ($last_ch eq "\015") {
495b8851fccSafresh1    # Remove \012 so it does not get prefixed with another \015 below
496b8851fccSafresh1    # and escape the . if there is one following it because the fixup
497b8851fccSafresh1    # below will not find it
498b8851fccSafresh1    $first_ch = "\012" if $line =~ s/^\012(\.?)/$1$1/;
499b8851fccSafresh1  }
500b8851fccSafresh1  elsif ($last_ch eq "\012") {
501b8851fccSafresh1    # Fixup below will not find the . as the first character of the buffer
502b8851fccSafresh1    $first_ch = "." if $line =~ /^\./;
503b8851fccSafresh1  }
504b8851fccSafresh1
505b8851fccSafresh1  $line =~ s/\015?\012(\.?)/\015\012$1$1/sg;
506b8851fccSafresh1
507b8851fccSafresh1  substr($line, 0, 0) = $first_ch;
508b8851fccSafresh1
509b8851fccSafresh1  ${*$cmd}{'net_cmd_last_ch'} = substr($line, -1, 1);
510b8851fccSafresh1
511b8851fccSafresh1  $cmd->_syswrite_with_timeout($line)
512b8851fccSafresh1    or return;
513b8851fccSafresh1
514b8851fccSafresh1  1;
515b8851fccSafresh1}
516b8851fccSafresh1
517b8851fccSafresh1
518b8851fccSafresh1sub rawdatasend {
519b8851fccSafresh1  my $cmd  = shift;
520b8851fccSafresh1  my $arr  = @_ == 1 && ref($_[0]) ? $_[0] : \@_;
521b8851fccSafresh1  my $line = join("", @$arr);
522b8851fccSafresh1
523b8851fccSafresh1  return 0
524b8851fccSafresh1    if $cmd->_is_closed;
525b8851fccSafresh1
526b8851fccSafresh1  return 1
527b8851fccSafresh1    unless length($line);
528b8851fccSafresh1
529b8851fccSafresh1  if ($cmd->debug) {
530b8851fccSafresh1    my $b = "$cmd>>> ";
531b8851fccSafresh1    print STDERR $b, join("\n$b", split(/\n/, $line)), "\n";
532b8851fccSafresh1  }
533b8851fccSafresh1
534b8851fccSafresh1  $cmd->_syswrite_with_timeout($line)
535b8851fccSafresh1    or return;
536b8851fccSafresh1
537b8851fccSafresh1  1;
538b8851fccSafresh1}
539b8851fccSafresh1
540b8851fccSafresh1
541b8851fccSafresh1sub dataend {
542b8851fccSafresh1  my $cmd = shift;
543b8851fccSafresh1
544b8851fccSafresh1  return 0
545b8851fccSafresh1    if $cmd->_is_closed;
546b8851fccSafresh1
547b8851fccSafresh1  my $ch = ${*$cmd}{'net_cmd_last_ch'};
548b8851fccSafresh1  my $tosend;
549b8851fccSafresh1
550b8851fccSafresh1  if (!defined $ch) {
551b8851fccSafresh1    return 1;
552b8851fccSafresh1  }
553b8851fccSafresh1  elsif ($ch ne "\012") {
554b8851fccSafresh1    $tosend = "\015\012";
555b8851fccSafresh1  }
556b8851fccSafresh1
557b8851fccSafresh1  $tosend .= ".\015\012";
558b8851fccSafresh1
559b8851fccSafresh1  $cmd->debug_print(1, ".\n")
560b8851fccSafresh1    if ($cmd->debug);
561b8851fccSafresh1
562b8851fccSafresh1  $cmd->_syswrite_with_timeout($tosend)
563b8851fccSafresh1    or return 0;
564b8851fccSafresh1
565b8851fccSafresh1  delete ${*$cmd}{'net_cmd_last_ch'};
566b8851fccSafresh1
567b8851fccSafresh1  $cmd->response() == CMD_OK;
568b8851fccSafresh1}
569b8851fccSafresh1
570b8851fccSafresh1# read and write to tied filehandle
571b8851fccSafresh1sub tied_fh {
572b8851fccSafresh1  my $cmd = shift;
573b8851fccSafresh1  ${*$cmd}{'net_cmd_readbuf'} = '';
574b8851fccSafresh1  my $fh = gensym();
575b8851fccSafresh1  tie *$fh, ref($cmd), $cmd;
576b8851fccSafresh1  return $fh;
577b8851fccSafresh1}
578b8851fccSafresh1
579b8851fccSafresh1# tie to myself
580b8851fccSafresh1sub TIEHANDLE {
581b8851fccSafresh1  my $class = shift;
582b8851fccSafresh1  my $cmd   = shift;
583b8851fccSafresh1  return $cmd;
584b8851fccSafresh1}
585b8851fccSafresh1
586b8851fccSafresh1# Tied filehandle read.  Reads requested data length, returning
587b8851fccSafresh1# end-of-file when the dot is encountered.
588b8851fccSafresh1sub READ {
589b8851fccSafresh1  my $cmd = shift;
590b8851fccSafresh1  my ($len, $offset) = @_[1, 2];
591b8851fccSafresh1  return unless exists ${*$cmd}{'net_cmd_readbuf'};
592b8851fccSafresh1  my $done = 0;
593b8851fccSafresh1  while (!$done and length(${*$cmd}{'net_cmd_readbuf'}) < $len) {
594b8851fccSafresh1    ${*$cmd}{'net_cmd_readbuf'} .= $cmd->getline() or return;
595b8851fccSafresh1    $done++ if ${*$cmd}{'net_cmd_readbuf'} =~ s/^\.\r?\n\Z//m;
596b8851fccSafresh1  }
597b8851fccSafresh1
598b8851fccSafresh1  $_[0] = '';
599b8851fccSafresh1  substr($_[0], $offset + 0) = substr(${*$cmd}{'net_cmd_readbuf'}, 0, $len);
600b8851fccSafresh1  substr(${*$cmd}{'net_cmd_readbuf'}, 0, $len) = '';
601b8851fccSafresh1  delete ${*$cmd}{'net_cmd_readbuf'} if $done;
602b8851fccSafresh1
603b8851fccSafresh1  return length $_[0];
604b8851fccSafresh1}
605b8851fccSafresh1
606b8851fccSafresh1
607b8851fccSafresh1sub READLINE {
608b8851fccSafresh1  my $cmd = shift;
609b8851fccSafresh1
610b8851fccSafresh1  # in this context, we use the presence of readbuf to
611b8851fccSafresh1  # indicate that we have not yet reached the eof
612b8851fccSafresh1  return unless exists ${*$cmd}{'net_cmd_readbuf'};
613b8851fccSafresh1  my $line = $cmd->getline;
614b8851fccSafresh1  return if $line =~ /^\.\r?\n/;
615b8851fccSafresh1  $line;
616b8851fccSafresh1}
617b8851fccSafresh1
618b8851fccSafresh1
619b8851fccSafresh1sub PRINT {
620b8851fccSafresh1  my $cmd = shift;
621b8851fccSafresh1  my ($buf, $len, $offset) = @_;
622b8851fccSafresh1  $len ||= length($buf);
623b8851fccSafresh1  $offset += 0;
624b8851fccSafresh1  return unless $cmd->datasend(substr($buf, $offset, $len));
625b8851fccSafresh1  ${*$cmd}{'net_cmd_sending'}++;    # flag that we should call dataend()
626b8851fccSafresh1  return $len;
627b8851fccSafresh1}
628b8851fccSafresh1
629b8851fccSafresh1
630b8851fccSafresh1sub CLOSE {
631b8851fccSafresh1  my $cmd = shift;
632b8851fccSafresh1  my $r = exists(${*$cmd}{'net_cmd_sending'}) ? $cmd->dataend : 1;
633b8851fccSafresh1  delete ${*$cmd}{'net_cmd_readbuf'};
634b8851fccSafresh1  delete ${*$cmd}{'net_cmd_sending'};
635b8851fccSafresh1  $r;
636b8851fccSafresh1}
637b8851fccSafresh1
638b8851fccSafresh11;
639b8851fccSafresh1
640b8851fccSafresh1__END__
641b8851fccSafresh1
642b8851fccSafresh1
643b8851fccSafresh1=head1 NAME
644b8851fccSafresh1
645b8851fccSafresh1Net::Cmd - Network Command class (as used by FTP, SMTP etc)
646b8851fccSafresh1
647b8851fccSafresh1=head1 SYNOPSIS
648b8851fccSafresh1
649b8851fccSafresh1    use Net::Cmd;
650b8851fccSafresh1
651b8851fccSafresh1    @ISA = qw(Net::Cmd);
652b8851fccSafresh1
653b8851fccSafresh1=head1 DESCRIPTION
654b8851fccSafresh1
6555759b3d2Safresh1C<Net::Cmd> is a collection of methods that can be inherited by a sub-class
6565759b3d2Safresh1of C<IO::Socket::INET>. These methods implement the functionality required for a
657b8851fccSafresh1command based protocol, for example FTP and SMTP.
658b8851fccSafresh1
6595759b3d2Safresh1If your sub-class does not also derive from C<IO::Socket::INET> or similar (e.g.
6605759b3d2Safresh1C<IO::Socket::IP>, C<IO::Socket::INET6> or C<IO::Socket::SSL>) then you must
6615759b3d2Safresh1provide the following methods by other means yourself: C<close()> and
6625759b3d2Safresh1C<timeout()>.
6635759b3d2Safresh1
664eac174f2Safresh1=head2 Public Methods
665b8851fccSafresh1
666b8851fccSafresh1These methods provide a user interface to the C<Net::Cmd> object.
667b8851fccSafresh1
668b8851fccSafresh1=over 4
669b8851fccSafresh1
670eac174f2Safresh1=item C<debug($level)>
671b8851fccSafresh1
672eac174f2Safresh1Set the level of debug information for this object. If C<$level> is not given
673b8851fccSafresh1then the current state is returned. Otherwise the state is changed to
674eac174f2Safresh1C<$level> and the previous state returned.
675b8851fccSafresh1
676b8851fccSafresh1Different packages
677b8851fccSafresh1may implement different levels of debug but a non-zero value results in
678b8851fccSafresh1copies of all commands and responses also being sent to STDERR.
679b8851fccSafresh1
680eac174f2Safresh1If C<$level> is C<undef> then the debug level will be set to the default
681b8851fccSafresh1debug level for the class.
682b8851fccSafresh1
683b8851fccSafresh1This method can also be called as a I<static> method to set/get the default
684b8851fccSafresh1debug level for a given class.
685b8851fccSafresh1
686eac174f2Safresh1=item C<message()>
687b8851fccSafresh1
688b8851fccSafresh1Returns the text message returned from the last command. In a scalar
689b8851fccSafresh1context it returns a single string, in a list context it will return
690b8851fccSafresh1each line as a separate element. (See L<PSEUDO RESPONSES> below.)
691b8851fccSafresh1
692eac174f2Safresh1=item C<code()>
693b8851fccSafresh1
694b8851fccSafresh1Returns the 3-digit code from the last command. If a command is pending
695b8851fccSafresh1then the value 0 is returned. (See L<PSEUDO RESPONSES> below.)
696b8851fccSafresh1
697eac174f2Safresh1=item C<ok()>
698b8851fccSafresh1
699b8851fccSafresh1Returns non-zero if the last code value was greater than zero and
700b8851fccSafresh1less than 400. This holds true for most command servers. Servers
701b8851fccSafresh1where this does not hold may override this method.
702b8851fccSafresh1
703eac174f2Safresh1=item C<status()>
704b8851fccSafresh1
705b8851fccSafresh1Returns the most significant digit of the current status code. If a command
706b8851fccSafresh1is pending then C<CMD_PENDING> is returned.
707b8851fccSafresh1
708eac174f2Safresh1=item C<datasend($data)>
709b8851fccSafresh1
710b8851fccSafresh1Send data to the remote server, converting LF to CRLF. Any line starting
711b8851fccSafresh1with a '.' will be prefixed with another '.'.
712eac174f2Safresh1C<$data> may be an array or a reference to an array.
713eac174f2Safresh1The C<$data> passed in must be encoded by the caller to octets of whatever
714b8851fccSafresh1encoding is required, e.g. by using the Encode module's C<encode()> function.
715b8851fccSafresh1
716eac174f2Safresh1=item C<dataend()>
717b8851fccSafresh1
718b8851fccSafresh1End the sending of data to the remote server. This is done by ensuring that
719b8851fccSafresh1the data already sent ends with CRLF then sending '.CRLF' to end the
720b8851fccSafresh1transmission. Once this data has been sent C<dataend> calls C<response> and
721b8851fccSafresh1returns true if C<response> returns CMD_OK.
722b8851fccSafresh1
723b8851fccSafresh1=back
724b8851fccSafresh1
725eac174f2Safresh1=head2 Protected Methods
726b8851fccSafresh1
727b8851fccSafresh1These methods are not intended to be called by the user, but used or
728b8851fccSafresh1over-ridden by a sub-class of C<Net::Cmd>
729b8851fccSafresh1
730b8851fccSafresh1=over 4
731b8851fccSafresh1
732eac174f2Safresh1=item C<debug_print($dir, $text)>
733b8851fccSafresh1
734eac174f2Safresh1Print debugging information. C<$dir> denotes the direction I<true> being
735b8851fccSafresh1data being sent to the server. Calls C<debug_text> before printing to
736b8851fccSafresh1STDERR.
737b8851fccSafresh1
738eac174f2Safresh1=item C<debug_text($dir, $text)>
739b8851fccSafresh1
740eac174f2Safresh1This method is called to print debugging information. C<$text> is
741b8851fccSafresh1the text being sent. The method should return the text to be printed.
742b8851fccSafresh1
743b8851fccSafresh1This is primarily meant for the use of modules such as FTP where passwords
744b8851fccSafresh1are sent, but we do not want to display them in the debugging information.
745b8851fccSafresh1
746eac174f2Safresh1=item C<command($cmd[, $args, ... ])>
747b8851fccSafresh1
748b8851fccSafresh1Send a command to the command server. All arguments are first joined with
749b8851fccSafresh1a space character and CRLF is appended, this string is then sent to the
750b8851fccSafresh1command server.
751b8851fccSafresh1
752b8851fccSafresh1Returns undef upon failure.
753b8851fccSafresh1
754eac174f2Safresh1=item C<unsupported()>
755b8851fccSafresh1
756b8851fccSafresh1Sets the status code to 580 and the response text to 'Unsupported command'.
757b8851fccSafresh1Returns zero.
758b8851fccSafresh1
759eac174f2Safresh1=item C<response()>
760b8851fccSafresh1
761b8851fccSafresh1Obtain a response from the server. Upon success the most significant digit
762b8851fccSafresh1of the status code is returned. Upon failure, timeout etc., I<CMD_ERROR> is
763b8851fccSafresh1returned.
764b8851fccSafresh1
765eac174f2Safresh1=item C<parse_response($text)>
766b8851fccSafresh1
767b8851fccSafresh1This method is called by C<response> as a method with one argument. It should
768b8851fccSafresh1return an array of 2 values, the 3-digit status code and a flag which is true
769b8851fccSafresh1when this is part of a multi-line response and this line is not the last.
770b8851fccSafresh1
771eac174f2Safresh1=item C<getline()>
772b8851fccSafresh1
773b8851fccSafresh1Retrieve one line, delimited by CRLF, from the remote server. Returns I<undef>
774b8851fccSafresh1upon failure.
775b8851fccSafresh1
776b8851fccSafresh1B<NOTE>: If you do use this method for any reason, please remember to add
777b8851fccSafresh1some C<debug_print> calls into your method.
778b8851fccSafresh1
779eac174f2Safresh1=item C<ungetline($text)>
780b8851fccSafresh1
781b8851fccSafresh1Unget a line of text from the server.
782b8851fccSafresh1
783eac174f2Safresh1=item C<rawdatasend($data)>
784b8851fccSafresh1
785eac174f2Safresh1Send data to the remote server without performing any conversions. C<$data>
786b8851fccSafresh1is a scalar.
787eac174f2Safresh1As with C<datasend()>, the C<$data> passed in must be encoded by the caller
788b8851fccSafresh1to octets of whatever encoding is required, e.g. by using the Encode module's
789b8851fccSafresh1C<encode()> function.
790b8851fccSafresh1
791eac174f2Safresh1=item C<read_until_dot()>
792b8851fccSafresh1
793b8851fccSafresh1Read data from the remote server until a line consisting of a single '.'.
794b8851fccSafresh1Any lines starting with '..' will have one of the '.'s removed.
795b8851fccSafresh1
796b8851fccSafresh1Returns a reference to a list containing the lines, or I<undef> upon failure.
797b8851fccSafresh1
798eac174f2Safresh1=item C<tied_fh()>
799b8851fccSafresh1
800b8851fccSafresh1Returns a filehandle tied to the Net::Cmd object.  After issuing a
801b8851fccSafresh1command, you may read from this filehandle using read() or <>.  The
802b8851fccSafresh1filehandle will return EOF when the final dot is encountered.
803b8851fccSafresh1Similarly, you may write to the filehandle in order to send data to
804b8851fccSafresh1the server after issuing a command that expects data to be written.
805b8851fccSafresh1
806b8851fccSafresh1See the Net::POP3 and Net::SMTP modules for examples of this.
807b8851fccSafresh1
808b8851fccSafresh1=back
809b8851fccSafresh1
810eac174f2Safresh1=head2 Pseudo Responses
811b8851fccSafresh1
812b8851fccSafresh1Normally the values returned by C<message()> and C<code()> are
813b8851fccSafresh1obtained from the remote server, but in a few circumstances, as
814b8851fccSafresh1detailed below, C<Net::Cmd> will return values that it sets. You
815b8851fccSafresh1can alter this behavior by overriding DEF_REPLY_CODE() to specify
816b8851fccSafresh1a different default reply code, or overriding one of the specific
817b8851fccSafresh1error handling methods below.
818b8851fccSafresh1
819b8851fccSafresh1=over 4
820b8851fccSafresh1
821b8851fccSafresh1=item Initial value
822b8851fccSafresh1
823b8851fccSafresh1Before any command has executed or if an unexpected error occurs
824b8851fccSafresh1C<code()> will return "421" (temporary connection failure) and
825b8851fccSafresh1C<message()> will return undef.
826b8851fccSafresh1
827b8851fccSafresh1=item Connection closed
828b8851fccSafresh1
829b8851fccSafresh1If the underlying C<IO::Handle> is closed, or if there are
830b8851fccSafresh1any read or write failures, the file handle will be forced closed,
831b8851fccSafresh1and C<code()> will return "421" (temporary connection failure)
832b8851fccSafresh1and C<message()> will return "[$pkg] Connection closed"
833b8851fccSafresh1(where $pkg is the name of the class that subclassed C<Net::Cmd>).
834b8851fccSafresh1The _set_status_closed() method can be overridden to set a different
835b8851fccSafresh1message (by calling set_status()) or otherwise trap this error.
836b8851fccSafresh1
837b8851fccSafresh1=item Timeout
838b8851fccSafresh1
839b8851fccSafresh1If there is a read or write timeout C<code()> will return "421"
840b8851fccSafresh1(temporary connection failure) and C<message()> will return
841b8851fccSafresh1"[$pkg] Timeout" (where $pkg is the name of the class
842b8851fccSafresh1that subclassed C<Net::Cmd>). The _set_status_timeout() method
843b8851fccSafresh1can be overridden to set a different message (by calling set_status())
844b8851fccSafresh1or otherwise trap this error.
845b8851fccSafresh1
846b8851fccSafresh1=back
847b8851fccSafresh1
848b8851fccSafresh1=head1 EXPORTS
849b8851fccSafresh1
850eac174f2Safresh1The following symbols are, or can be, exported by this module:
851eac174f2Safresh1
852eac174f2Safresh1=over 4
853eac174f2Safresh1
854eac174f2Safresh1=item Default Exports
855eac174f2Safresh1
856eac174f2Safresh1C<CMD_INFO>,
857eac174f2Safresh1C<CMD_OK>,
858eac174f2Safresh1C<CMD_MORE>,
859eac174f2Safresh1C<CMD_REJECT>,
860eac174f2Safresh1C<CMD_ERROR>,
861eac174f2Safresh1C<CMD_PENDING>.
862eac174f2Safresh1
863eac174f2Safresh1(These correspond to possible results of C<response()> and C<status()>.)
864eac174f2Safresh1
865eac174f2Safresh1=item Optional Exports
866eac174f2Safresh1
867eac174f2Safresh1I<None>.
868eac174f2Safresh1
869eac174f2Safresh1=item Export Tags
870eac174f2Safresh1
871eac174f2Safresh1I<None>.
872eac174f2Safresh1
873eac174f2Safresh1=back
874eac174f2Safresh1
875eac174f2Safresh1=head1 KNOWN BUGS
876eac174f2Safresh1
877eac174f2Safresh1See L<https://rt.cpan.org/Dist/Display.html?Status=Active&Queue=libnet>.
878b8851fccSafresh1
879b8851fccSafresh1=head1 AUTHOR
880b8851fccSafresh1
881eac174f2Safresh1Graham Barr E<lt>L<gbarr@pobox.com|mailto:gbarr@pobox.com>E<gt>.
882b8851fccSafresh1
883eac174f2Safresh1Steve Hay E<lt>L<shay@cpan.org|mailto:shay@cpan.org>E<gt> is now maintaining
884eac174f2Safresh1libnet as of version 1.22_02.
885b8851fccSafresh1
886b8851fccSafresh1=head1 COPYRIGHT
887b8851fccSafresh1
8885759b3d2Safresh1Copyright (C) 1995-2006 Graham Barr.  All rights reserved.
8895759b3d2Safresh1
890*e0680481Safresh1Copyright (C) 2013-2016, 2020, 2022 Steve Hay.  All rights reserved.
8915759b3d2Safresh1
8925759b3d2Safresh1=head1 LICENCE
893b8851fccSafresh1
894b8851fccSafresh1This module is free software; you can redistribute it and/or modify it under the
895b8851fccSafresh1same terms as Perl itself, i.e. under the terms of either the GNU General Public
896b8851fccSafresh1License or the Artistic License, as specified in the F<LICENCE> file.
897b8851fccSafresh1
898eac174f2Safresh1=head1 VERSION
899eac174f2Safresh1
900*e0680481Safresh1Version 3.15
901eac174f2Safresh1
902eac174f2Safresh1=head1 DATE
903eac174f2Safresh1
904*e0680481Safresh120 March 2023
905eac174f2Safresh1
906eac174f2Safresh1=head1 HISTORY
907eac174f2Safresh1
908eac174f2Safresh1See the F<Changes> file.
909eac174f2Safresh1
910b8851fccSafresh1=cut
911