1*0Sstevel@tonic-gate# Net::Cmd.pm $Id: //depot/libnet/Net/Cmd.pm#33 $ 2*0Sstevel@tonic-gate# 3*0Sstevel@tonic-gate# Copyright (c) 1995-1997 Graham Barr <gbarr@pobox.com>. All rights reserved. 4*0Sstevel@tonic-gate# This program is free software; you can redistribute it and/or 5*0Sstevel@tonic-gate# modify it under the same terms as Perl itself. 6*0Sstevel@tonic-gate 7*0Sstevel@tonic-gatepackage Net::Cmd; 8*0Sstevel@tonic-gate 9*0Sstevel@tonic-gaterequire 5.001; 10*0Sstevel@tonic-gaterequire Exporter; 11*0Sstevel@tonic-gate 12*0Sstevel@tonic-gateuse strict; 13*0Sstevel@tonic-gateuse vars qw(@ISA @EXPORT $VERSION); 14*0Sstevel@tonic-gateuse Carp; 15*0Sstevel@tonic-gateuse Symbol 'gensym'; 16*0Sstevel@tonic-gate 17*0Sstevel@tonic-gateBEGIN { 18*0Sstevel@tonic-gate if ($^O eq 'os390') { 19*0Sstevel@tonic-gate require Convert::EBCDIC; 20*0Sstevel@tonic-gate# Convert::EBCDIC->import; 21*0Sstevel@tonic-gate } 22*0Sstevel@tonic-gate} 23*0Sstevel@tonic-gate 24*0Sstevel@tonic-gate$VERSION = "2.24"; 25*0Sstevel@tonic-gate@ISA = qw(Exporter); 26*0Sstevel@tonic-gate@EXPORT = qw(CMD_INFO CMD_OK CMD_MORE CMD_REJECT CMD_ERROR CMD_PENDING); 27*0Sstevel@tonic-gate 28*0Sstevel@tonic-gatesub CMD_INFO { 1 } 29*0Sstevel@tonic-gatesub CMD_OK { 2 } 30*0Sstevel@tonic-gatesub CMD_MORE { 3 } 31*0Sstevel@tonic-gatesub CMD_REJECT { 4 } 32*0Sstevel@tonic-gatesub CMD_ERROR { 5 } 33*0Sstevel@tonic-gatesub CMD_PENDING { 0 } 34*0Sstevel@tonic-gate 35*0Sstevel@tonic-gatemy %debug = (); 36*0Sstevel@tonic-gate 37*0Sstevel@tonic-gatemy $tr = $^O eq 'os390' ? Convert::EBCDIC->new() : undef; 38*0Sstevel@tonic-gate 39*0Sstevel@tonic-gatesub toebcdic 40*0Sstevel@tonic-gate{ 41*0Sstevel@tonic-gate my $cmd = shift; 42*0Sstevel@tonic-gate 43*0Sstevel@tonic-gate unless (exists ${*$cmd}{'net_cmd_asciipeer'}) 44*0Sstevel@tonic-gate { 45*0Sstevel@tonic-gate my $string = $_[0]; 46*0Sstevel@tonic-gate my $ebcdicstr = $tr->toebcdic($string); 47*0Sstevel@tonic-gate ${*$cmd}{'net_cmd_asciipeer'} = $string !~ /^\d+/ && $ebcdicstr =~ /^\d+/; 48*0Sstevel@tonic-gate } 49*0Sstevel@tonic-gate 50*0Sstevel@tonic-gate ${*$cmd}{'net_cmd_asciipeer'} 51*0Sstevel@tonic-gate ? $tr->toebcdic($_[0]) 52*0Sstevel@tonic-gate : $_[0]; 53*0Sstevel@tonic-gate} 54*0Sstevel@tonic-gate 55*0Sstevel@tonic-gatesub toascii 56*0Sstevel@tonic-gate{ 57*0Sstevel@tonic-gate my $cmd = shift; 58*0Sstevel@tonic-gate ${*$cmd}{'net_cmd_asciipeer'} 59*0Sstevel@tonic-gate ? $tr->toascii($_[0]) 60*0Sstevel@tonic-gate : $_[0]; 61*0Sstevel@tonic-gate} 62*0Sstevel@tonic-gate 63*0Sstevel@tonic-gatesub _print_isa 64*0Sstevel@tonic-gate{ 65*0Sstevel@tonic-gate no strict qw(refs); 66*0Sstevel@tonic-gate 67*0Sstevel@tonic-gate my $pkg = shift; 68*0Sstevel@tonic-gate my $cmd = $pkg; 69*0Sstevel@tonic-gate 70*0Sstevel@tonic-gate $debug{$pkg} ||= 0; 71*0Sstevel@tonic-gate 72*0Sstevel@tonic-gate my %done = (); 73*0Sstevel@tonic-gate my @do = ($pkg); 74*0Sstevel@tonic-gate my %spc = ( $pkg , ""); 75*0Sstevel@tonic-gate 76*0Sstevel@tonic-gate while ($pkg = shift @do) 77*0Sstevel@tonic-gate { 78*0Sstevel@tonic-gate next if defined $done{$pkg}; 79*0Sstevel@tonic-gate 80*0Sstevel@tonic-gate $done{$pkg} = 1; 81*0Sstevel@tonic-gate 82*0Sstevel@tonic-gate my $v = defined ${"${pkg}::VERSION"} 83*0Sstevel@tonic-gate ? "(" . ${"${pkg}::VERSION"} . ")" 84*0Sstevel@tonic-gate : ""; 85*0Sstevel@tonic-gate 86*0Sstevel@tonic-gate my $spc = $spc{$pkg}; 87*0Sstevel@tonic-gate $cmd->debug_print(1,"${spc}${pkg}${v}\n"); 88*0Sstevel@tonic-gate 89*0Sstevel@tonic-gate if(@{"${pkg}::ISA"}) 90*0Sstevel@tonic-gate { 91*0Sstevel@tonic-gate @spc{@{"${pkg}::ISA"}} = (" " . $spc{$pkg}) x @{"${pkg}::ISA"}; 92*0Sstevel@tonic-gate unshift(@do, @{"${pkg}::ISA"}); 93*0Sstevel@tonic-gate } 94*0Sstevel@tonic-gate } 95*0Sstevel@tonic-gate} 96*0Sstevel@tonic-gate 97*0Sstevel@tonic-gatesub debug 98*0Sstevel@tonic-gate{ 99*0Sstevel@tonic-gate @_ == 1 or @_ == 2 or croak 'usage: $obj->debug([LEVEL])'; 100*0Sstevel@tonic-gate 101*0Sstevel@tonic-gate my($cmd,$level) = @_; 102*0Sstevel@tonic-gate my $pkg = ref($cmd) || $cmd; 103*0Sstevel@tonic-gate my $oldval = 0; 104*0Sstevel@tonic-gate 105*0Sstevel@tonic-gate if(ref($cmd)) 106*0Sstevel@tonic-gate { 107*0Sstevel@tonic-gate $oldval = ${*$cmd}{'net_cmd_debug'} || 0; 108*0Sstevel@tonic-gate } 109*0Sstevel@tonic-gate else 110*0Sstevel@tonic-gate { 111*0Sstevel@tonic-gate $oldval = $debug{$pkg} || 0; 112*0Sstevel@tonic-gate } 113*0Sstevel@tonic-gate 114*0Sstevel@tonic-gate return $oldval 115*0Sstevel@tonic-gate unless @_ == 2; 116*0Sstevel@tonic-gate 117*0Sstevel@tonic-gate $level = $debug{$pkg} || 0 118*0Sstevel@tonic-gate unless defined $level; 119*0Sstevel@tonic-gate 120*0Sstevel@tonic-gate _print_isa($pkg) 121*0Sstevel@tonic-gate if($level && !exists $debug{$pkg}); 122*0Sstevel@tonic-gate 123*0Sstevel@tonic-gate if(ref($cmd)) 124*0Sstevel@tonic-gate { 125*0Sstevel@tonic-gate ${*$cmd}{'net_cmd_debug'} = $level; 126*0Sstevel@tonic-gate } 127*0Sstevel@tonic-gate else 128*0Sstevel@tonic-gate { 129*0Sstevel@tonic-gate $debug{$pkg} = $level; 130*0Sstevel@tonic-gate } 131*0Sstevel@tonic-gate 132*0Sstevel@tonic-gate $oldval; 133*0Sstevel@tonic-gate} 134*0Sstevel@tonic-gate 135*0Sstevel@tonic-gatesub message 136*0Sstevel@tonic-gate{ 137*0Sstevel@tonic-gate @_ == 1 or croak 'usage: $obj->message()'; 138*0Sstevel@tonic-gate 139*0Sstevel@tonic-gate my $cmd = shift; 140*0Sstevel@tonic-gate 141*0Sstevel@tonic-gate wantarray ? @{${*$cmd}{'net_cmd_resp'}} 142*0Sstevel@tonic-gate : join("", @{${*$cmd}{'net_cmd_resp'}}); 143*0Sstevel@tonic-gate} 144*0Sstevel@tonic-gate 145*0Sstevel@tonic-gatesub debug_text { $_[2] } 146*0Sstevel@tonic-gate 147*0Sstevel@tonic-gatesub debug_print 148*0Sstevel@tonic-gate{ 149*0Sstevel@tonic-gate my($cmd,$out,$text) = @_; 150*0Sstevel@tonic-gate print STDERR $cmd,($out ? '>>> ' : '<<< '), $cmd->debug_text($out,$text); 151*0Sstevel@tonic-gate} 152*0Sstevel@tonic-gate 153*0Sstevel@tonic-gatesub code 154*0Sstevel@tonic-gate{ 155*0Sstevel@tonic-gate @_ == 1 or croak 'usage: $obj->code()'; 156*0Sstevel@tonic-gate 157*0Sstevel@tonic-gate my $cmd = shift; 158*0Sstevel@tonic-gate 159*0Sstevel@tonic-gate ${*$cmd}{'net_cmd_code'} = "000" 160*0Sstevel@tonic-gate unless exists ${*$cmd}{'net_cmd_code'}; 161*0Sstevel@tonic-gate 162*0Sstevel@tonic-gate ${*$cmd}{'net_cmd_code'}; 163*0Sstevel@tonic-gate} 164*0Sstevel@tonic-gate 165*0Sstevel@tonic-gatesub status 166*0Sstevel@tonic-gate{ 167*0Sstevel@tonic-gate @_ == 1 or croak 'usage: $obj->status()'; 168*0Sstevel@tonic-gate 169*0Sstevel@tonic-gate my $cmd = shift; 170*0Sstevel@tonic-gate 171*0Sstevel@tonic-gate substr(${*$cmd}{'net_cmd_code'},0,1); 172*0Sstevel@tonic-gate} 173*0Sstevel@tonic-gate 174*0Sstevel@tonic-gatesub set_status 175*0Sstevel@tonic-gate{ 176*0Sstevel@tonic-gate @_ == 3 or croak 'usage: $obj->set_status(CODE, MESSAGE)'; 177*0Sstevel@tonic-gate 178*0Sstevel@tonic-gate my $cmd = shift; 179*0Sstevel@tonic-gate my($code,$resp) = @_; 180*0Sstevel@tonic-gate 181*0Sstevel@tonic-gate $resp = [ $resp ] 182*0Sstevel@tonic-gate unless ref($resp); 183*0Sstevel@tonic-gate 184*0Sstevel@tonic-gate (${*$cmd}{'net_cmd_code'},${*$cmd}{'net_cmd_resp'}) = ($code, $resp); 185*0Sstevel@tonic-gate 186*0Sstevel@tonic-gate 1; 187*0Sstevel@tonic-gate} 188*0Sstevel@tonic-gate 189*0Sstevel@tonic-gatesub command 190*0Sstevel@tonic-gate{ 191*0Sstevel@tonic-gate my $cmd = shift; 192*0Sstevel@tonic-gate 193*0Sstevel@tonic-gate unless (defined fileno($cmd)) 194*0Sstevel@tonic-gate { 195*0Sstevel@tonic-gate $cmd->set_status("599", "Connection closed"); 196*0Sstevel@tonic-gate return $cmd; 197*0Sstevel@tonic-gate } 198*0Sstevel@tonic-gate 199*0Sstevel@tonic-gate 200*0Sstevel@tonic-gate $cmd->dataend() 201*0Sstevel@tonic-gate if(exists ${*$cmd}{'net_cmd_need_crlf'}); 202*0Sstevel@tonic-gate 203*0Sstevel@tonic-gate if (scalar(@_)) 204*0Sstevel@tonic-gate { 205*0Sstevel@tonic-gate local $SIG{PIPE} = 'IGNORE' unless $^O eq 'MacOS'; 206*0Sstevel@tonic-gate 207*0Sstevel@tonic-gate my $str = join(" ", map { /\n/ ? do { my $n = $_; $n =~ tr/\n/ /; $n } : $_; } @_); 208*0Sstevel@tonic-gate $str = $cmd->toascii($str) if $tr; 209*0Sstevel@tonic-gate $str .= "\015\012"; 210*0Sstevel@tonic-gate 211*0Sstevel@tonic-gate my $len = length $str; 212*0Sstevel@tonic-gate my $swlen; 213*0Sstevel@tonic-gate 214*0Sstevel@tonic-gate $cmd->close 215*0Sstevel@tonic-gate unless (defined($swlen = syswrite($cmd,$str,$len)) && $swlen == $len); 216*0Sstevel@tonic-gate 217*0Sstevel@tonic-gate $cmd->debug_print(1,$str) 218*0Sstevel@tonic-gate if($cmd->debug); 219*0Sstevel@tonic-gate 220*0Sstevel@tonic-gate ${*$cmd}{'net_cmd_resp'} = []; # the response 221*0Sstevel@tonic-gate ${*$cmd}{'net_cmd_code'} = "000"; # Made this one up :-) 222*0Sstevel@tonic-gate } 223*0Sstevel@tonic-gate 224*0Sstevel@tonic-gate $cmd; 225*0Sstevel@tonic-gate} 226*0Sstevel@tonic-gate 227*0Sstevel@tonic-gatesub ok 228*0Sstevel@tonic-gate{ 229*0Sstevel@tonic-gate @_ == 1 or croak 'usage: $obj->ok()'; 230*0Sstevel@tonic-gate 231*0Sstevel@tonic-gate my $code = $_[0]->code; 232*0Sstevel@tonic-gate 0 < $code && $code < 400; 233*0Sstevel@tonic-gate} 234*0Sstevel@tonic-gate 235*0Sstevel@tonic-gatesub unsupported 236*0Sstevel@tonic-gate{ 237*0Sstevel@tonic-gate my $cmd = shift; 238*0Sstevel@tonic-gate 239*0Sstevel@tonic-gate ${*$cmd}{'net_cmd_resp'} = [ 'Unsupported command' ]; 240*0Sstevel@tonic-gate ${*$cmd}{'net_cmd_code'} = 580; 241*0Sstevel@tonic-gate 0; 242*0Sstevel@tonic-gate} 243*0Sstevel@tonic-gate 244*0Sstevel@tonic-gatesub getline 245*0Sstevel@tonic-gate{ 246*0Sstevel@tonic-gate my $cmd = shift; 247*0Sstevel@tonic-gate 248*0Sstevel@tonic-gate ${*$cmd}{'net_cmd_lines'} ||= []; 249*0Sstevel@tonic-gate 250*0Sstevel@tonic-gate return shift @{${*$cmd}{'net_cmd_lines'}} 251*0Sstevel@tonic-gate if scalar(@{${*$cmd}{'net_cmd_lines'}}); 252*0Sstevel@tonic-gate 253*0Sstevel@tonic-gate my $partial = defined(${*$cmd}{'net_cmd_partial'}) 254*0Sstevel@tonic-gate ? ${*$cmd}{'net_cmd_partial'} : ""; 255*0Sstevel@tonic-gate my $fd = fileno($cmd); 256*0Sstevel@tonic-gate 257*0Sstevel@tonic-gate return undef 258*0Sstevel@tonic-gate unless defined $fd; 259*0Sstevel@tonic-gate 260*0Sstevel@tonic-gate my $rin = ""; 261*0Sstevel@tonic-gate vec($rin,$fd,1) = 1; 262*0Sstevel@tonic-gate 263*0Sstevel@tonic-gate my $buf; 264*0Sstevel@tonic-gate 265*0Sstevel@tonic-gate until(scalar(@{${*$cmd}{'net_cmd_lines'}})) 266*0Sstevel@tonic-gate { 267*0Sstevel@tonic-gate my $timeout = $cmd->timeout || undef; 268*0Sstevel@tonic-gate my $rout; 269*0Sstevel@tonic-gate if (select($rout=$rin, undef, undef, $timeout)) 270*0Sstevel@tonic-gate { 271*0Sstevel@tonic-gate unless (sysread($cmd, $buf="", 1024)) 272*0Sstevel@tonic-gate { 273*0Sstevel@tonic-gate carp(ref($cmd) . ": Unexpected EOF on command channel") 274*0Sstevel@tonic-gate if $cmd->debug; 275*0Sstevel@tonic-gate $cmd->close; 276*0Sstevel@tonic-gate return undef; 277*0Sstevel@tonic-gate } 278*0Sstevel@tonic-gate 279*0Sstevel@tonic-gate substr($buf,0,0) = $partial; ## prepend from last sysread 280*0Sstevel@tonic-gate 281*0Sstevel@tonic-gate my @buf = split(/\015?\012/, $buf, -1); ## break into lines 282*0Sstevel@tonic-gate 283*0Sstevel@tonic-gate $partial = pop @buf; 284*0Sstevel@tonic-gate 285*0Sstevel@tonic-gate push(@{${*$cmd}{'net_cmd_lines'}}, map { "$_\n" } @buf); 286*0Sstevel@tonic-gate 287*0Sstevel@tonic-gate } 288*0Sstevel@tonic-gate else 289*0Sstevel@tonic-gate { 290*0Sstevel@tonic-gate carp("$cmd: Timeout") if($cmd->debug); 291*0Sstevel@tonic-gate return undef; 292*0Sstevel@tonic-gate } 293*0Sstevel@tonic-gate } 294*0Sstevel@tonic-gate 295*0Sstevel@tonic-gate ${*$cmd}{'net_cmd_partial'} = $partial; 296*0Sstevel@tonic-gate 297*0Sstevel@tonic-gate if ($tr) 298*0Sstevel@tonic-gate { 299*0Sstevel@tonic-gate foreach my $ln (@{${*$cmd}{'net_cmd_lines'}}) 300*0Sstevel@tonic-gate { 301*0Sstevel@tonic-gate $ln = $cmd->toebcdic($ln); 302*0Sstevel@tonic-gate } 303*0Sstevel@tonic-gate } 304*0Sstevel@tonic-gate 305*0Sstevel@tonic-gate shift @{${*$cmd}{'net_cmd_lines'}}; 306*0Sstevel@tonic-gate} 307*0Sstevel@tonic-gate 308*0Sstevel@tonic-gatesub ungetline 309*0Sstevel@tonic-gate{ 310*0Sstevel@tonic-gate my($cmd,$str) = @_; 311*0Sstevel@tonic-gate 312*0Sstevel@tonic-gate ${*$cmd}{'net_cmd_lines'} ||= []; 313*0Sstevel@tonic-gate unshift(@{${*$cmd}{'net_cmd_lines'}}, $str); 314*0Sstevel@tonic-gate} 315*0Sstevel@tonic-gate 316*0Sstevel@tonic-gatesub parse_response 317*0Sstevel@tonic-gate{ 318*0Sstevel@tonic-gate return () 319*0Sstevel@tonic-gate unless $_[1] =~ s/^(\d\d\d)(.?)//o; 320*0Sstevel@tonic-gate ($1, $2 eq "-"); 321*0Sstevel@tonic-gate} 322*0Sstevel@tonic-gate 323*0Sstevel@tonic-gatesub response 324*0Sstevel@tonic-gate{ 325*0Sstevel@tonic-gate my $cmd = shift; 326*0Sstevel@tonic-gate my($code,$more) = (undef) x 2; 327*0Sstevel@tonic-gate 328*0Sstevel@tonic-gate ${*$cmd}{'net_cmd_resp'} ||= []; 329*0Sstevel@tonic-gate 330*0Sstevel@tonic-gate while(1) 331*0Sstevel@tonic-gate { 332*0Sstevel@tonic-gate my $str = $cmd->getline(); 333*0Sstevel@tonic-gate 334*0Sstevel@tonic-gate return CMD_ERROR 335*0Sstevel@tonic-gate unless defined($str); 336*0Sstevel@tonic-gate 337*0Sstevel@tonic-gate $cmd->debug_print(0,$str) 338*0Sstevel@tonic-gate if ($cmd->debug); 339*0Sstevel@tonic-gate 340*0Sstevel@tonic-gate ($code,$more) = $cmd->parse_response($str); 341*0Sstevel@tonic-gate unless(defined $code) 342*0Sstevel@tonic-gate { 343*0Sstevel@tonic-gate $cmd->ungetline($str); 344*0Sstevel@tonic-gate last; 345*0Sstevel@tonic-gate } 346*0Sstevel@tonic-gate 347*0Sstevel@tonic-gate ${*$cmd}{'net_cmd_code'} = $code; 348*0Sstevel@tonic-gate 349*0Sstevel@tonic-gate push(@{${*$cmd}{'net_cmd_resp'}},$str); 350*0Sstevel@tonic-gate 351*0Sstevel@tonic-gate last unless($more); 352*0Sstevel@tonic-gate } 353*0Sstevel@tonic-gate 354*0Sstevel@tonic-gate substr($code,0,1); 355*0Sstevel@tonic-gate} 356*0Sstevel@tonic-gate 357*0Sstevel@tonic-gatesub read_until_dot 358*0Sstevel@tonic-gate{ 359*0Sstevel@tonic-gate my $cmd = shift; 360*0Sstevel@tonic-gate my $fh = shift; 361*0Sstevel@tonic-gate my $arr = []; 362*0Sstevel@tonic-gate 363*0Sstevel@tonic-gate while(1) 364*0Sstevel@tonic-gate { 365*0Sstevel@tonic-gate my $str = $cmd->getline() or return undef; 366*0Sstevel@tonic-gate 367*0Sstevel@tonic-gate $cmd->debug_print(0,$str) 368*0Sstevel@tonic-gate if ($cmd->debug & 4); 369*0Sstevel@tonic-gate 370*0Sstevel@tonic-gate last if($str =~ /^\.\r?\n/o); 371*0Sstevel@tonic-gate 372*0Sstevel@tonic-gate $str =~ s/^\.\././o; 373*0Sstevel@tonic-gate 374*0Sstevel@tonic-gate if (defined $fh) 375*0Sstevel@tonic-gate { 376*0Sstevel@tonic-gate print $fh $str; 377*0Sstevel@tonic-gate } 378*0Sstevel@tonic-gate else 379*0Sstevel@tonic-gate { 380*0Sstevel@tonic-gate push(@$arr,$str); 381*0Sstevel@tonic-gate } 382*0Sstevel@tonic-gate } 383*0Sstevel@tonic-gate 384*0Sstevel@tonic-gate $arr; 385*0Sstevel@tonic-gate} 386*0Sstevel@tonic-gate 387*0Sstevel@tonic-gatesub datasend 388*0Sstevel@tonic-gate{ 389*0Sstevel@tonic-gate my $cmd = shift; 390*0Sstevel@tonic-gate my $arr = @_ == 1 && ref($_[0]) ? $_[0] : \@_; 391*0Sstevel@tonic-gate my $line = join("" ,@$arr); 392*0Sstevel@tonic-gate 393*0Sstevel@tonic-gate return 0 unless defined(fileno($cmd)); 394*0Sstevel@tonic-gate 395*0Sstevel@tonic-gate unless (length $line) { 396*0Sstevel@tonic-gate # Even though we are not sending anything, the fact we were 397*0Sstevel@tonic-gate # called means that dataend needs to be called before the next 398*0Sstevel@tonic-gate # command, which happens of net_cmd_need_crlf exists 399*0Sstevel@tonic-gate ${*$cmd}{'net_cmd_need_crlf'} ||= 0; 400*0Sstevel@tonic-gate return 1; 401*0Sstevel@tonic-gate } 402*0Sstevel@tonic-gate 403*0Sstevel@tonic-gate if($cmd->debug) { 404*0Sstevel@tonic-gate foreach my $b (split(/\n/,$line)) { 405*0Sstevel@tonic-gate $cmd->debug_print(1, "$b\n"); 406*0Sstevel@tonic-gate } 407*0Sstevel@tonic-gate } 408*0Sstevel@tonic-gate 409*0Sstevel@tonic-gate $line =~ s/\r?\n/\r\n/sg; 410*0Sstevel@tonic-gate $line =~ tr/\r\n/\015\012/ unless "\r" eq "\015"; 411*0Sstevel@tonic-gate 412*0Sstevel@tonic-gate $line =~ s/(\012\.)/$1./sog; 413*0Sstevel@tonic-gate $line =~ s/^\./../ unless ${*$cmd}{'net_cmd_need_crlf'}; 414*0Sstevel@tonic-gate 415*0Sstevel@tonic-gate ${*$cmd}{'net_cmd_need_crlf'} = substr($line,-1,1) ne "\012"; 416*0Sstevel@tonic-gate 417*0Sstevel@tonic-gate my $len = length($line); 418*0Sstevel@tonic-gate my $offset = 0; 419*0Sstevel@tonic-gate my $win = ""; 420*0Sstevel@tonic-gate vec($win,fileno($cmd),1) = 1; 421*0Sstevel@tonic-gate my $timeout = $cmd->timeout || undef; 422*0Sstevel@tonic-gate 423*0Sstevel@tonic-gate local $SIG{PIPE} = 'IGNORE' unless $^O eq 'MacOS'; 424*0Sstevel@tonic-gate 425*0Sstevel@tonic-gate while($len) 426*0Sstevel@tonic-gate { 427*0Sstevel@tonic-gate my $wout; 428*0Sstevel@tonic-gate if (select(undef,$wout=$win, undef, $timeout) > 0) 429*0Sstevel@tonic-gate { 430*0Sstevel@tonic-gate my $w = syswrite($cmd, $line, $len, $offset); 431*0Sstevel@tonic-gate unless (defined($w)) 432*0Sstevel@tonic-gate { 433*0Sstevel@tonic-gate carp("$cmd: $!") if $cmd->debug; 434*0Sstevel@tonic-gate return undef; 435*0Sstevel@tonic-gate } 436*0Sstevel@tonic-gate $len -= $w; 437*0Sstevel@tonic-gate $offset += $w; 438*0Sstevel@tonic-gate } 439*0Sstevel@tonic-gate else 440*0Sstevel@tonic-gate { 441*0Sstevel@tonic-gate carp("$cmd: Timeout") if($cmd->debug); 442*0Sstevel@tonic-gate return undef; 443*0Sstevel@tonic-gate } 444*0Sstevel@tonic-gate } 445*0Sstevel@tonic-gate 446*0Sstevel@tonic-gate 1; 447*0Sstevel@tonic-gate} 448*0Sstevel@tonic-gate 449*0Sstevel@tonic-gatesub rawdatasend 450*0Sstevel@tonic-gate{ 451*0Sstevel@tonic-gate my $cmd = shift; 452*0Sstevel@tonic-gate my $arr = @_ == 1 && ref($_[0]) ? $_[0] : \@_; 453*0Sstevel@tonic-gate my $line = join("" ,@$arr); 454*0Sstevel@tonic-gate 455*0Sstevel@tonic-gate return 0 unless defined(fileno($cmd)); 456*0Sstevel@tonic-gate 457*0Sstevel@tonic-gate return 1 458*0Sstevel@tonic-gate unless length($line); 459*0Sstevel@tonic-gate 460*0Sstevel@tonic-gate if($cmd->debug) 461*0Sstevel@tonic-gate { 462*0Sstevel@tonic-gate my $b = "$cmd>>> "; 463*0Sstevel@tonic-gate print STDERR $b,join("\n$b",split(/\n/,$line)),"\n"; 464*0Sstevel@tonic-gate } 465*0Sstevel@tonic-gate 466*0Sstevel@tonic-gate my $len = length($line); 467*0Sstevel@tonic-gate my $offset = 0; 468*0Sstevel@tonic-gate my $win = ""; 469*0Sstevel@tonic-gate vec($win,fileno($cmd),1) = 1; 470*0Sstevel@tonic-gate my $timeout = $cmd->timeout || undef; 471*0Sstevel@tonic-gate 472*0Sstevel@tonic-gate local $SIG{PIPE} = 'IGNORE' unless $^O eq 'MacOS'; 473*0Sstevel@tonic-gate while($len) 474*0Sstevel@tonic-gate { 475*0Sstevel@tonic-gate my $wout; 476*0Sstevel@tonic-gate if (select(undef,$wout=$win, undef, $timeout) > 0) 477*0Sstevel@tonic-gate { 478*0Sstevel@tonic-gate my $w = syswrite($cmd, $line, $len, $offset); 479*0Sstevel@tonic-gate unless (defined($w)) 480*0Sstevel@tonic-gate { 481*0Sstevel@tonic-gate carp("$cmd: $!") if $cmd->debug; 482*0Sstevel@tonic-gate return undef; 483*0Sstevel@tonic-gate } 484*0Sstevel@tonic-gate $len -= $w; 485*0Sstevel@tonic-gate $offset += $w; 486*0Sstevel@tonic-gate } 487*0Sstevel@tonic-gate else 488*0Sstevel@tonic-gate { 489*0Sstevel@tonic-gate carp("$cmd: Timeout") if($cmd->debug); 490*0Sstevel@tonic-gate return undef; 491*0Sstevel@tonic-gate } 492*0Sstevel@tonic-gate } 493*0Sstevel@tonic-gate 494*0Sstevel@tonic-gate 1; 495*0Sstevel@tonic-gate} 496*0Sstevel@tonic-gate 497*0Sstevel@tonic-gatesub dataend 498*0Sstevel@tonic-gate{ 499*0Sstevel@tonic-gate my $cmd = shift; 500*0Sstevel@tonic-gate 501*0Sstevel@tonic-gate return 0 unless defined(fileno($cmd)); 502*0Sstevel@tonic-gate 503*0Sstevel@tonic-gate return 1 504*0Sstevel@tonic-gate unless(exists ${*$cmd}{'net_cmd_need_crlf'}); 505*0Sstevel@tonic-gate 506*0Sstevel@tonic-gate local $SIG{PIPE} = 'IGNORE' unless $^O eq 'MacOS'; 507*0Sstevel@tonic-gate syswrite($cmd,"\015\012",2) 508*0Sstevel@tonic-gate if ${*$cmd}{'net_cmd_need_crlf'}; 509*0Sstevel@tonic-gate 510*0Sstevel@tonic-gate $cmd->debug_print(1, ".\n") 511*0Sstevel@tonic-gate if($cmd->debug); 512*0Sstevel@tonic-gate 513*0Sstevel@tonic-gate syswrite($cmd,".\015\012",3); 514*0Sstevel@tonic-gate 515*0Sstevel@tonic-gate delete ${*$cmd}{'net_cmd_need_crlf'}; 516*0Sstevel@tonic-gate 517*0Sstevel@tonic-gate $cmd->response() == CMD_OK; 518*0Sstevel@tonic-gate} 519*0Sstevel@tonic-gate 520*0Sstevel@tonic-gate# read and write to tied filehandle 521*0Sstevel@tonic-gatesub tied_fh { 522*0Sstevel@tonic-gate my $cmd = shift; 523*0Sstevel@tonic-gate ${*$cmd}{'net_cmd_readbuf'} = ''; 524*0Sstevel@tonic-gate my $fh = gensym(); 525*0Sstevel@tonic-gate tie *$fh,ref($cmd),$cmd; 526*0Sstevel@tonic-gate return $fh; 527*0Sstevel@tonic-gate} 528*0Sstevel@tonic-gate 529*0Sstevel@tonic-gate# tie to myself 530*0Sstevel@tonic-gatesub TIEHANDLE { 531*0Sstevel@tonic-gate my $class = shift; 532*0Sstevel@tonic-gate my $cmd = shift; 533*0Sstevel@tonic-gate return $cmd; 534*0Sstevel@tonic-gate} 535*0Sstevel@tonic-gate 536*0Sstevel@tonic-gate# Tied filehandle read. Reads requested data length, returning 537*0Sstevel@tonic-gate# end-of-file when the dot is encountered. 538*0Sstevel@tonic-gatesub READ { 539*0Sstevel@tonic-gate my $cmd = shift; 540*0Sstevel@tonic-gate my ($len,$offset) = @_[1,2]; 541*0Sstevel@tonic-gate return unless exists ${*$cmd}{'net_cmd_readbuf'}; 542*0Sstevel@tonic-gate my $done = 0; 543*0Sstevel@tonic-gate while (!$done and length(${*$cmd}{'net_cmd_readbuf'}) < $len) { 544*0Sstevel@tonic-gate ${*$cmd}{'net_cmd_readbuf'} .= $cmd->getline() or return; 545*0Sstevel@tonic-gate $done++ if ${*$cmd}{'net_cmd_readbuf'} =~ s/^\.\r?\n\Z//m; 546*0Sstevel@tonic-gate } 547*0Sstevel@tonic-gate 548*0Sstevel@tonic-gate $_[0] = ''; 549*0Sstevel@tonic-gate substr($_[0],$offset+0) = substr(${*$cmd}{'net_cmd_readbuf'},0,$len); 550*0Sstevel@tonic-gate substr(${*$cmd}{'net_cmd_readbuf'},0,$len) = ''; 551*0Sstevel@tonic-gate delete ${*$cmd}{'net_cmd_readbuf'} if $done; 552*0Sstevel@tonic-gate 553*0Sstevel@tonic-gate return length $_[0]; 554*0Sstevel@tonic-gate} 555*0Sstevel@tonic-gate 556*0Sstevel@tonic-gatesub READLINE { 557*0Sstevel@tonic-gate my $cmd = shift; 558*0Sstevel@tonic-gate # in this context, we use the presence of readbuf to 559*0Sstevel@tonic-gate # indicate that we have not yet reached the eof 560*0Sstevel@tonic-gate return unless exists ${*$cmd}{'net_cmd_readbuf'}; 561*0Sstevel@tonic-gate my $line = $cmd->getline; 562*0Sstevel@tonic-gate return if $line =~ /^\.\r?\n/; 563*0Sstevel@tonic-gate $line; 564*0Sstevel@tonic-gate} 565*0Sstevel@tonic-gate 566*0Sstevel@tonic-gatesub PRINT { 567*0Sstevel@tonic-gate my $cmd = shift; 568*0Sstevel@tonic-gate my ($buf,$len,$offset) = @_; 569*0Sstevel@tonic-gate $len ||= length ($buf); 570*0Sstevel@tonic-gate $offset += 0; 571*0Sstevel@tonic-gate return unless $cmd->datasend(substr($buf,$offset,$len)); 572*0Sstevel@tonic-gate ${*$cmd}{'net_cmd_sending'}++; # flag that we should call dataend() 573*0Sstevel@tonic-gate return $len; 574*0Sstevel@tonic-gate} 575*0Sstevel@tonic-gate 576*0Sstevel@tonic-gatesub CLOSE { 577*0Sstevel@tonic-gate my $cmd = shift; 578*0Sstevel@tonic-gate my $r = exists(${*$cmd}{'net_cmd_sending'}) ? $cmd->dataend : 1; 579*0Sstevel@tonic-gate delete ${*$cmd}{'net_cmd_readbuf'}; 580*0Sstevel@tonic-gate delete ${*$cmd}{'net_cmd_sending'}; 581*0Sstevel@tonic-gate $r; 582*0Sstevel@tonic-gate} 583*0Sstevel@tonic-gate 584*0Sstevel@tonic-gate1; 585*0Sstevel@tonic-gate 586*0Sstevel@tonic-gate__END__ 587*0Sstevel@tonic-gate 588*0Sstevel@tonic-gate 589*0Sstevel@tonic-gate=head1 NAME 590*0Sstevel@tonic-gate 591*0Sstevel@tonic-gateNet::Cmd - Network Command class (as used by FTP, SMTP etc) 592*0Sstevel@tonic-gate 593*0Sstevel@tonic-gate=head1 SYNOPSIS 594*0Sstevel@tonic-gate 595*0Sstevel@tonic-gate use Net::Cmd; 596*0Sstevel@tonic-gate 597*0Sstevel@tonic-gate @ISA = qw(Net::Cmd); 598*0Sstevel@tonic-gate 599*0Sstevel@tonic-gate=head1 DESCRIPTION 600*0Sstevel@tonic-gate 601*0Sstevel@tonic-gateC<Net::Cmd> is a collection of methods that can be inherited by a sub class 602*0Sstevel@tonic-gateof C<IO::Handle>. These methods implement the functionality required for a 603*0Sstevel@tonic-gatecommand based protocol, for example FTP and SMTP. 604*0Sstevel@tonic-gate 605*0Sstevel@tonic-gate=head1 USER METHODS 606*0Sstevel@tonic-gate 607*0Sstevel@tonic-gateThese methods provide a user interface to the C<Net::Cmd> object. 608*0Sstevel@tonic-gate 609*0Sstevel@tonic-gate=over 4 610*0Sstevel@tonic-gate 611*0Sstevel@tonic-gate=item debug ( VALUE ) 612*0Sstevel@tonic-gate 613*0Sstevel@tonic-gateSet the level of debug information for this object. If C<VALUE> is not given 614*0Sstevel@tonic-gatethen the current state is returned. Otherwise the state is changed to 615*0Sstevel@tonic-gateC<VALUE> and the previous state returned. 616*0Sstevel@tonic-gate 617*0Sstevel@tonic-gateDifferent packages 618*0Sstevel@tonic-gatemay implement different levels of debug but a non-zero value results in 619*0Sstevel@tonic-gatecopies of all commands and responses also being sent to STDERR. 620*0Sstevel@tonic-gate 621*0Sstevel@tonic-gateIf C<VALUE> is C<undef> then the debug level will be set to the default 622*0Sstevel@tonic-gatedebug level for the class. 623*0Sstevel@tonic-gate 624*0Sstevel@tonic-gateThis method can also be called as a I<static> method to set/get the default 625*0Sstevel@tonic-gatedebug level for a given class. 626*0Sstevel@tonic-gate 627*0Sstevel@tonic-gate=item message () 628*0Sstevel@tonic-gate 629*0Sstevel@tonic-gateReturns the text message returned from the last command 630*0Sstevel@tonic-gate 631*0Sstevel@tonic-gate=item code () 632*0Sstevel@tonic-gate 633*0Sstevel@tonic-gateReturns the 3-digit code from the last command. If a command is pending 634*0Sstevel@tonic-gatethen the value 0 is returned 635*0Sstevel@tonic-gate 636*0Sstevel@tonic-gate=item ok () 637*0Sstevel@tonic-gate 638*0Sstevel@tonic-gateReturns non-zero if the last code value was greater than zero and 639*0Sstevel@tonic-gateless than 400. This holds true for most command servers. Servers 640*0Sstevel@tonic-gatewhere this does not hold may override this method. 641*0Sstevel@tonic-gate 642*0Sstevel@tonic-gate=item status () 643*0Sstevel@tonic-gate 644*0Sstevel@tonic-gateReturns the most significant digit of the current status code. If a command 645*0Sstevel@tonic-gateis pending then C<CMD_PENDING> is returned. 646*0Sstevel@tonic-gate 647*0Sstevel@tonic-gate=item datasend ( DATA ) 648*0Sstevel@tonic-gate 649*0Sstevel@tonic-gateSend data to the remote server, converting LF to CRLF. Any line starting 650*0Sstevel@tonic-gatewith a '.' will be prefixed with another '.'. 651*0Sstevel@tonic-gateC<DATA> may be an array or a reference to an array. 652*0Sstevel@tonic-gate 653*0Sstevel@tonic-gate=item dataend () 654*0Sstevel@tonic-gate 655*0Sstevel@tonic-gateEnd the sending of data to the remote server. This is done by ensuring that 656*0Sstevel@tonic-gatethe data already sent ends with CRLF then sending '.CRLF' to end the 657*0Sstevel@tonic-gatetransmission. Once this data has been sent C<dataend> calls C<response> and 658*0Sstevel@tonic-gatereturns true if C<response> returns CMD_OK. 659*0Sstevel@tonic-gate 660*0Sstevel@tonic-gate=back 661*0Sstevel@tonic-gate 662*0Sstevel@tonic-gate=head1 CLASS METHODS 663*0Sstevel@tonic-gate 664*0Sstevel@tonic-gateThese methods are not intended to be called by the user, but used or 665*0Sstevel@tonic-gateover-ridden by a sub-class of C<Net::Cmd> 666*0Sstevel@tonic-gate 667*0Sstevel@tonic-gate=over 4 668*0Sstevel@tonic-gate 669*0Sstevel@tonic-gate=item debug_print ( DIR, TEXT ) 670*0Sstevel@tonic-gate 671*0Sstevel@tonic-gatePrint debugging information. C<DIR> denotes the direction I<true> being 672*0Sstevel@tonic-gatedata being sent to the server. Calls C<debug_text> before printing to 673*0Sstevel@tonic-gateSTDERR. 674*0Sstevel@tonic-gate 675*0Sstevel@tonic-gate=item debug_text ( TEXT ) 676*0Sstevel@tonic-gate 677*0Sstevel@tonic-gateThis method is called to print debugging information. TEXT is 678*0Sstevel@tonic-gatethe text being sent. The method should return the text to be printed 679*0Sstevel@tonic-gate 680*0Sstevel@tonic-gateThis is primarily meant for the use of modules such as FTP where passwords 681*0Sstevel@tonic-gateare sent, but we do not want to display them in the debugging information. 682*0Sstevel@tonic-gate 683*0Sstevel@tonic-gate=item command ( CMD [, ARGS, ... ]) 684*0Sstevel@tonic-gate 685*0Sstevel@tonic-gateSend a command to the command server. All arguments a first joined with 686*0Sstevel@tonic-gatea space character and CRLF is appended, this string is then sent to the 687*0Sstevel@tonic-gatecommand server. 688*0Sstevel@tonic-gate 689*0Sstevel@tonic-gateReturns undef upon failure 690*0Sstevel@tonic-gate 691*0Sstevel@tonic-gate=item unsupported () 692*0Sstevel@tonic-gate 693*0Sstevel@tonic-gateSets the status code to 580 and the response text to 'Unsupported command'. 694*0Sstevel@tonic-gateReturns zero. 695*0Sstevel@tonic-gate 696*0Sstevel@tonic-gate=item response () 697*0Sstevel@tonic-gate 698*0Sstevel@tonic-gateObtain a response from the server. Upon success the most significant digit 699*0Sstevel@tonic-gateof the status code is returned. Upon failure, timeout etc., I<undef> is 700*0Sstevel@tonic-gatereturned. 701*0Sstevel@tonic-gate 702*0Sstevel@tonic-gate=item parse_response ( TEXT ) 703*0Sstevel@tonic-gate 704*0Sstevel@tonic-gateThis method is called by C<response> as a method with one argument. It should 705*0Sstevel@tonic-gatereturn an array of 2 values, the 3-digit status code and a flag which is true 706*0Sstevel@tonic-gatewhen this is part of a multi-line response and this line is not the list. 707*0Sstevel@tonic-gate 708*0Sstevel@tonic-gate=item getline () 709*0Sstevel@tonic-gate 710*0Sstevel@tonic-gateRetrieve one line, delimited by CRLF, from the remote server. Returns I<undef> 711*0Sstevel@tonic-gateupon failure. 712*0Sstevel@tonic-gate 713*0Sstevel@tonic-gateB<NOTE>: If you do use this method for any reason, please remember to add 714*0Sstevel@tonic-gatesome C<debug_print> calls into your method. 715*0Sstevel@tonic-gate 716*0Sstevel@tonic-gate=item ungetline ( TEXT ) 717*0Sstevel@tonic-gate 718*0Sstevel@tonic-gateUnget a line of text from the server. 719*0Sstevel@tonic-gate 720*0Sstevel@tonic-gate=item rawdatasend ( DATA ) 721*0Sstevel@tonic-gate 722*0Sstevel@tonic-gateSend data to the remote server without performing any conversions. C<DATA> 723*0Sstevel@tonic-gateis a scalar. 724*0Sstevel@tonic-gate 725*0Sstevel@tonic-gate=item read_until_dot () 726*0Sstevel@tonic-gate 727*0Sstevel@tonic-gateRead data from the remote server until a line consisting of a single '.'. 728*0Sstevel@tonic-gateAny lines starting with '..' will have one of the '.'s removed. 729*0Sstevel@tonic-gate 730*0Sstevel@tonic-gateReturns a reference to a list containing the lines, or I<undef> upon failure. 731*0Sstevel@tonic-gate 732*0Sstevel@tonic-gate=item tied_fh () 733*0Sstevel@tonic-gate 734*0Sstevel@tonic-gateReturns a filehandle tied to the Net::Cmd object. After issuing a 735*0Sstevel@tonic-gatecommand, you may read from this filehandle using read() or <>. The 736*0Sstevel@tonic-gatefilehandle will return EOF when the final dot is encountered. 737*0Sstevel@tonic-gateSimilarly, you may write to the filehandle in order to send data to 738*0Sstevel@tonic-gatethe server after issuing a commmand that expects data to be written. 739*0Sstevel@tonic-gate 740*0Sstevel@tonic-gateSee the Net::POP3 and Net::SMTP modules for examples of this. 741*0Sstevel@tonic-gate 742*0Sstevel@tonic-gate=back 743*0Sstevel@tonic-gate 744*0Sstevel@tonic-gate=head1 EXPORTS 745*0Sstevel@tonic-gate 746*0Sstevel@tonic-gateC<Net::Cmd> exports six subroutines, five of these, C<CMD_INFO>, C<CMD_OK>, 747*0Sstevel@tonic-gateC<CMD_MORE>, C<CMD_REJECT> and C<CMD_ERROR>, correspond to possible results 748*0Sstevel@tonic-gateof C<response> and C<status>. The sixth is C<CMD_PENDING>. 749*0Sstevel@tonic-gate 750*0Sstevel@tonic-gate=head1 AUTHOR 751*0Sstevel@tonic-gate 752*0Sstevel@tonic-gateGraham Barr <gbarr@pobox.com> 753*0Sstevel@tonic-gate 754*0Sstevel@tonic-gate=head1 COPYRIGHT 755*0Sstevel@tonic-gate 756*0Sstevel@tonic-gateCopyright (c) 1995-1997 Graham Barr. All rights reserved. 757*0Sstevel@tonic-gateThis program is free software; you can redistribute it and/or modify 758*0Sstevel@tonic-gateit under the same terms as Perl itself. 759*0Sstevel@tonic-gate 760*0Sstevel@tonic-gate=for html <hr> 761*0Sstevel@tonic-gate 762*0Sstevel@tonic-gateI<$Id: //depot/libnet/Net/Cmd.pm#33 $> 763*0Sstevel@tonic-gate 764*0Sstevel@tonic-gate=cut 765