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