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