perldelta.pod fixes
[p5sagit/p5-mst-13.2.git] / lib / Net / Cmd.pm
CommitLineData
302c2e6b 1# Net::Cmd.pm $Id: //depot/libnet/Net/Cmd.pm#26 $
406c51ee 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 16BEGIN {
17 if ($^O eq 'os390') {
18 require Convert::EBCDIC;
19# Convert::EBCDIC->import;
20 }
21}
22
302c2e6b 23$VERSION = "2.20";
406c51ee 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 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 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 195 unless (defined fileno($cmd))
196 {
197 $cmd->set_status("599", "Connection closed");
198 return $cmd;
199 }
200
201
406c51ee 202 $cmd->dataend()
203 if(exists ${*$cmd}{'net_cmd_lastch'});
204
205 if (scalar(@_))
206 {
686337f3 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 213 my $len = length $str;
214 my $swlen;
686337f3 215
406c51ee 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 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 299 if ($tr)
300 {
301 foreach my $ln (@{${*$cmd}{'net_cmd_lines'}})
302 {
303 $ln = $cmd->toebcdic($ln);
304 }
305 }
306
406c51ee 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 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 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 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
510179aa 512Different packages
513may implement different levels of debug but a non-zero value results in
406c51ee 514copies of all commands and responses also being sent to STDERR.
515
516If C<VALUE> is C<undef> then the debug level will be set to the default
517debug level for the class.
518
519This method can also be called as a I<static> method to set/get the default
520debug level for a given class.
521
522=item message ()
523
524Returns the text message returned from the last command
525
526=item code ()
527
528Returns the 3-digit code from the last command. If a command is pending
529then the value 0 is returned
530
531=item ok ()
532
533Returns non-zero if the last code value was greater than zero and
534less than 400. This holds true for most command servers. Servers
535where this does not hold may override this method.
536
537=item status ()
538
539Returns the most significant digit of the current status code. If a command
540is pending then C<CMD_PENDING> is returned.
541
542=item datasend ( DATA )
543
544Send data to the remote server, converting LF to CRLF. Any line starting
545with a '.' will be prefixed with another '.'.
546C<DATA> may be an array or a reference to an array.
547
548=item dataend ()
549
550End the sending of data to the remote server. This is done by ensuring that
551the data already sent ends with CRLF then sending '.CRLF' to end the
552transmission. Once this data has been sent C<dataend> calls C<response> and
553returns true if C<response> returns CMD_OK.
554
555=back
556
557=head1 CLASS METHODS
558
559These methods are not intended to be called by the user, but used or
560over-ridden by a sub-class of C<Net::Cmd>
561
562=over 4
563
564=item debug_print ( DIR, TEXT )
565
566Print debugging information. C<DIR> denotes the direction I<true> being
567data being sent to the server. Calls C<debug_text> before printing to
568STDERR.
569
570=item debug_text ( TEXT )
571
572This method is called to print debugging information. TEXT is
573the text being sent. The method should return the text to be printed
574
575This is primarily meant for the use of modules such as FTP where passwords
576are sent, but we do not want to display them in the debugging information.
577
578=item command ( CMD [, ARGS, ... ])
579
580Send a command to the command server. All arguments a first joined with
581a space character and CRLF is appended, this string is then sent to the
582command server.
583
584Returns undef upon failure
585
586=item unsupported ()
587
588Sets the status code to 580 and the response text to 'Unsupported command'.
589Returns zero.
590
591=item response ()
592
593Obtain a response from the server. Upon success the most significant digit
594of the status code is returned. Upon failure, timeout etc., I<undef> is
595returned.
596
597=item parse_response ( TEXT )
598
599This method is called by C<response> as a method with one argument. It should
600return an array of 2 values, the 3-digit status code and a flag which is true
601when this is part of a multi-line response and this line is not the list.
602
603=item getline ()
604
605Retrieve one line, delimited by CRLF, from the remote server. Returns I<undef>
606upon failure.
607
608B<NOTE>: If you do use this method for any reason, please remember to add
609some C<debug_print> calls into your method.
610
611=item ungetline ( TEXT )
612
613Unget a line of text from the server.
614
615=item read_until_dot ()
616
617Read data from the remote server until a line consisting of a single '.'.
618Any lines starting with '..' will have one of the '.'s removed.
619
620Returns a reference to a list containing the lines, or I<undef> upon failure.
621
622=back
623
624=head1 EXPORTS
625
626C<Net::Cmd> exports six subroutines, five of these, C<CMD_INFO>, C<CMD_OK>,
510179aa 627C<CMD_MORE>, C<CMD_REJECT> and C<CMD_ERROR>, correspond to possible results
406c51ee 628of C<response> and C<status>. The sixth is C<CMD_PENDING>.
629
630=head1 AUTHOR
631
632Graham Barr <gbarr@pobox.com>
633
634=head1 COPYRIGHT
635
636Copyright (c) 1995-1997 Graham Barr. All rights reserved.
637This program is free software; you can redistribute it and/or modify
638it under the same terms as Perl itself.
639
686337f3 640=for html <hr>
641
302c2e6b 642I<$Id: //depot/libnet/Net/Cmd.pm#26 $>
686337f3 643
406c51ee 644=cut