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