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