source: for-distributions/trunk/bin/windows/perl/lib/Net/Cmd.pm@ 14489

Last change on this file since 14489 was 14489, checked in by oranfry, 17 years ago

upgrading to perl 5.8

File size: 15.4 KB
Line 
1# Net::Cmd.pm $Id: //depot/libnet/Net/Cmd.pm#34 $
2#
3# Copyright (c) 1995-1997 Graham Barr <[email protected]>. 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.26";
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_last_ch'});
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 my $last_ch = ${*$cmd}{'net_cmd_last_ch'};
396 $last_ch = ${*$cmd}{'net_cmd_last_ch'} = "\012" unless defined $last_ch;
397
398 return 1 unless length $line;
399
400 if($cmd->debug) {
401 foreach my $b (split(/\n/,$line)) {
402 $cmd->debug_print(1, "$b\n");
403 }
404 }
405
406 $line =~ tr/\r\n/\015\012/ unless "\r" eq "\015";
407
408 my $first_ch = '';
409
410 if ($last_ch eq "\015") {
411 $first_ch = "\012" if $line =~ s/^\012//;
412 }
413 elsif ($last_ch eq "\012") {
414 $first_ch = "." if $line =~ /^\./;
415 }
416
417 $line =~ s/\015?\012(\.?)/\015\012$1$1/sg;
418
419 substr($line,0,0) = $first_ch;
420
421 ${*$cmd}{'net_cmd_last_ch'} = substr($line,-1,1);
422
423 my $len = length($line);
424 my $offset = 0;
425 my $win = "";
426 vec($win,fileno($cmd),1) = 1;
427 my $timeout = $cmd->timeout || undef;
428
429 local $SIG{PIPE} = 'IGNORE' unless $^O eq 'MacOS';
430
431 while($len)
432 {
433 my $wout;
434 if (select(undef,$wout=$win, undef, $timeout) > 0 or -f $cmd) # -f for testing on win32
435 {
436 my $w = syswrite($cmd, $line, $len, $offset);
437 unless (defined($w))
438 {
439 carp("$cmd: $!") if $cmd->debug;
440 return undef;
441 }
442 $len -= $w;
443 $offset += $w;
444 }
445 else
446 {
447 carp("$cmd: Timeout") if($cmd->debug);
448 return undef;
449 }
450 }
451
452 1;
453}
454
455sub rawdatasend
456{
457 my $cmd = shift;
458 my $arr = @_ == 1 && ref($_[0]) ? $_[0] : \@_;
459 my $line = join("" ,@$arr);
460
461 return 0 unless defined(fileno($cmd));
462
463 return 1
464 unless length($line);
465
466 if($cmd->debug)
467 {
468 my $b = "$cmd>>> ";
469 print STDERR $b,join("\n$b",split(/\n/,$line)),"\n";
470 }
471
472 my $len = length($line);
473 my $offset = 0;
474 my $win = "";
475 vec($win,fileno($cmd),1) = 1;
476 my $timeout = $cmd->timeout || undef;
477
478 local $SIG{PIPE} = 'IGNORE' unless $^O eq 'MacOS';
479 while($len)
480 {
481 my $wout;
482 if (select(undef,$wout=$win, undef, $timeout) > 0)
483 {
484 my $w = syswrite($cmd, $line, $len, $offset);
485 unless (defined($w))
486 {
487 carp("$cmd: $!") if $cmd->debug;
488 return undef;
489 }
490 $len -= $w;
491 $offset += $w;
492 }
493 else
494 {
495 carp("$cmd: Timeout") if($cmd->debug);
496 return undef;
497 }
498 }
499
500 1;
501}
502
503sub dataend
504{
505 my $cmd = shift;
506
507 return 0 unless defined(fileno($cmd));
508
509 my $ch = ${*$cmd}{'net_cmd_last_ch'};
510 my $tosend;
511
512 if (!defined $ch) {
513 return 1;
514 }
515 elsif ($ch ne "\012") {
516 $tosend = "\015\012";
517 }
518
519 $tosend .= ".\015\012";
520
521 local $SIG{PIPE} = 'IGNORE' unless $^O eq 'MacOS';
522
523 $cmd->debug_print(1, ".\n")
524 if($cmd->debug);
525
526 syswrite($cmd,$tosend, length $tosend);
527
528 delete ${*$cmd}{'net_cmd_last_ch'};
529
530 $cmd->response() == CMD_OK;
531}
532
533# read and write to tied filehandle
534sub tied_fh {
535 my $cmd = shift;
536 ${*$cmd}{'net_cmd_readbuf'} = '';
537 my $fh = gensym();
538 tie *$fh,ref($cmd),$cmd;
539 return $fh;
540}
541
542# tie to myself
543sub TIEHANDLE {
544 my $class = shift;
545 my $cmd = shift;
546 return $cmd;
547}
548
549# Tied filehandle read. Reads requested data length, returning
550# end-of-file when the dot is encountered.
551sub READ {
552 my $cmd = shift;
553 my ($len,$offset) = @_[1,2];
554 return unless exists ${*$cmd}{'net_cmd_readbuf'};
555 my $done = 0;
556 while (!$done and length(${*$cmd}{'net_cmd_readbuf'}) < $len) {
557 ${*$cmd}{'net_cmd_readbuf'} .= $cmd->getline() or return;
558 $done++ if ${*$cmd}{'net_cmd_readbuf'} =~ s/^\.\r?\n\Z//m;
559 }
560
561 $_[0] = '';
562 substr($_[0],$offset+0) = substr(${*$cmd}{'net_cmd_readbuf'},0,$len);
563 substr(${*$cmd}{'net_cmd_readbuf'},0,$len) = '';
564 delete ${*$cmd}{'net_cmd_readbuf'} if $done;
565
566 return length $_[0];
567}
568
569sub READLINE {
570 my $cmd = shift;
571 # in this context, we use the presence of readbuf to
572 # indicate that we have not yet reached the eof
573 return unless exists ${*$cmd}{'net_cmd_readbuf'};
574 my $line = $cmd->getline;
575 return if $line =~ /^\.\r?\n/;
576 $line;
577}
578
579sub PRINT {
580 my $cmd = shift;
581 my ($buf,$len,$offset) = @_;
582 $len ||= length ($buf);
583 $offset += 0;
584 return unless $cmd->datasend(substr($buf,$offset,$len));
585 ${*$cmd}{'net_cmd_sending'}++; # flag that we should call dataend()
586 return $len;
587}
588
589sub CLOSE {
590 my $cmd = shift;
591 my $r = exists(${*$cmd}{'net_cmd_sending'}) ? $cmd->dataend : 1;
592 delete ${*$cmd}{'net_cmd_readbuf'};
593 delete ${*$cmd}{'net_cmd_sending'};
594 $r;
595}
596
5971;
598
599__END__
600
601
602=head1 NAME
603
604Net::Cmd - Network Command class (as used by FTP, SMTP etc)
605
606=head1 SYNOPSIS
607
608 use Net::Cmd;
609
610 @ISA = qw(Net::Cmd);
611
612=head1 DESCRIPTION
613
614C<Net::Cmd> is a collection of methods that can be inherited by a sub class
615of C<IO::Handle>. These methods implement the functionality required for a
616command based protocol, for example FTP and SMTP.
617
618=head1 USER METHODS
619
620These methods provide a user interface to the C<Net::Cmd> object.
621
622=over 4
623
624=item debug ( VALUE )
625
626Set the level of debug information for this object. If C<VALUE> is not given
627then the current state is returned. Otherwise the state is changed to
628C<VALUE> and the previous state returned.
629
630Different packages
631may implement different levels of debug but a non-zero value results in
632copies of all commands and responses also being sent to STDERR.
633
634If C<VALUE> is C<undef> then the debug level will be set to the default
635debug level for the class.
636
637This method can also be called as a I<static> method to set/get the default
638debug level for a given class.
639
640=item message ()
641
642Returns the text message returned from the last command
643
644=item code ()
645
646Returns the 3-digit code from the last command. If a command is pending
647then the value 0 is returned
648
649=item ok ()
650
651Returns non-zero if the last code value was greater than zero and
652less than 400. This holds true for most command servers. Servers
653where this does not hold may override this method.
654
655=item status ()
656
657Returns the most significant digit of the current status code. If a command
658is pending then C<CMD_PENDING> is returned.
659
660=item datasend ( DATA )
661
662Send data to the remote server, converting LF to CRLF. Any line starting
663with a '.' will be prefixed with another '.'.
664C<DATA> may be an array or a reference to an array.
665
666=item dataend ()
667
668End the sending of data to the remote server. This is done by ensuring that
669the data already sent ends with CRLF then sending '.CRLF' to end the
670transmission. Once this data has been sent C<dataend> calls C<response> and
671returns true if C<response> returns CMD_OK.
672
673=back
674
675=head1 CLASS METHODS
676
677These methods are not intended to be called by the user, but used or
678over-ridden by a sub-class of C<Net::Cmd>
679
680=over 4
681
682=item debug_print ( DIR, TEXT )
683
684Print debugging information. C<DIR> denotes the direction I<true> being
685data being sent to the server. Calls C<debug_text> before printing to
686STDERR.
687
688=item debug_text ( TEXT )
689
690This method is called to print debugging information. TEXT is
691the text being sent. The method should return the text to be printed
692
693This is primarily meant for the use of modules such as FTP where passwords
694are sent, but we do not want to display them in the debugging information.
695
696=item command ( CMD [, ARGS, ... ])
697
698Send a command to the command server. All arguments a first joined with
699a space character and CRLF is appended, this string is then sent to the
700command server.
701
702Returns undef upon failure
703
704=item unsupported ()
705
706Sets the status code to 580 and the response text to 'Unsupported command'.
707Returns zero.
708
709=item response ()
710
711Obtain a response from the server. Upon success the most significant digit
712of the status code is returned. Upon failure, timeout etc., I<undef> is
713returned.
714
715=item parse_response ( TEXT )
716
717This method is called by C<response> as a method with one argument. It should
718return an array of 2 values, the 3-digit status code and a flag which is true
719when this is part of a multi-line response and this line is not the list.
720
721=item getline ()
722
723Retrieve one line, delimited by CRLF, from the remote server. Returns I<undef>
724upon failure.
725
726B<NOTE>: If you do use this method for any reason, please remember to add
727some C<debug_print> calls into your method.
728
729=item ungetline ( TEXT )
730
731Unget a line of text from the server.
732
733=item rawdatasend ( DATA )
734
735Send data to the remote server without performing any conversions. C<DATA>
736is a scalar.
737
738=item read_until_dot ()
739
740Read data from the remote server until a line consisting of a single '.'.
741Any lines starting with '..' will have one of the '.'s removed.
742
743Returns a reference to a list containing the lines, or I<undef> upon failure.
744
745=item tied_fh ()
746
747Returns a filehandle tied to the Net::Cmd object. After issuing a
748command, you may read from this filehandle using read() or <>. The
749filehandle will return EOF when the final dot is encountered.
750Similarly, you may write to the filehandle in order to send data to
751the server after issuing a commmand that expects data to be written.
752
753See the Net::POP3 and Net::SMTP modules for examples of this.
754
755=back
756
757=head1 EXPORTS
758
759C<Net::Cmd> exports six subroutines, five of these, C<CMD_INFO>, C<CMD_OK>,
760C<CMD_MORE>, C<CMD_REJECT> and C<CMD_ERROR>, correspond to possible results
761of C<response> and C<status>. The sixth is C<CMD_PENDING>.
762
763=head1 AUTHOR
764
765Graham Barr <[email protected]>
766
767=head1 COPYRIGHT
768
769Copyright (c) 1995-1997 Graham Barr. All rights reserved.
770This program is free software; you can redistribute it and/or modify
771it under the same terms as Perl itself.
772
773=for html <hr>
774
775I<$Id: //depot/libnet/Net/Cmd.pm#34 $>
776
777=cut
Note: See TracBrowser for help on using the repository browser.