Upgrade to Locale::Maketext 1.07.
[p5sagit/p5-mst-13.2.git] / lib / Net / Cmd.pm
CommitLineData
dea4d7df 1# Net::Cmd.pm $Id: //depot/libnet/Net/Cmd.pm#33 $
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;
12df23ee 15use Symbol 'gensym';
406c51ee 16
686337f3 17BEGIN {
18 if ($^O eq 'os390') {
19 require Convert::EBCDIC;
20# Convert::EBCDIC->import;
21 }
22}
23
dea4d7df 24$VERSION = "2.24";
406c51ee 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 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 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
406c51ee 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};
edd55068 87 $cmd->debug_print(1,"${spc}${pkg}${v}\n");
406c51ee 88
89 if(@{"${pkg}::ISA"})
90 {
91 @spc{@{"${pkg}::ISA"}} = (" " . $spc{$pkg}) x @{"${pkg}::ISA"};
92 unshift(@do, @{"${pkg}::ISA"});
93 }
94 }
406c51ee 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
686337f3 193 unless (defined fileno($cmd))
194 {
195 $cmd->set_status("599", "Connection closed");
196 return $cmd;
197 }
198
199
406c51ee 200 $cmd->dataend()
dea4d7df 201 if(exists ${*$cmd}{'net_cmd_need_crlf'});
406c51ee 202
203 if (scalar(@_))
204 {
686337f3 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";
406c51ee 210
406c51ee 211 my $len = length $str;
212 my $swlen;
686337f3 213
406c51ee 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);
686337f3 256
406c51ee 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
686337f3 297 if ($tr)
298 {
299 foreach my $ln (@{${*$cmd}{'net_cmd_lines'}})
300 {
301 $ln = $cmd->toebcdic($ln);
302 }
303 }
304
406c51ee 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
dea4d7df 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 }
406c51ee 402
edd55068 403 if($cmd->debug) {
404 foreach my $b (split(/\n/,$line)) {
405 $cmd->debug_print(1, "$b\n");
406 }
406c51ee 407 }
408
dea4d7df 409 $line =~ s/\r?\n/\r\n/sg;
410 $line =~ tr/\r\n/\015\012/ unless "\r" eq "\015";
406c51ee 411
412 $line =~ s/(\012\.)/$1./sog;
dea4d7df 413 $line =~ s/^\./../ unless ${*$cmd}{'net_cmd_need_crlf'};
406c51ee 414
dea4d7df 415 ${*$cmd}{'net_cmd_need_crlf'} = substr($line,-1,1) ne "\012";
406c51ee 416
dea4d7df 417 my $len = length($line);
418 my $offset = 0;
406c51ee 419 my $win = "";
420 vec($win,fileno($cmd),1) = 1;
421 my $timeout = $cmd->timeout || undef;
422
dea4d7df 423 local $SIG{PIPE} = 'IGNORE' unless $^O eq 'MacOS';
424
406c51ee 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
dea4d7df 449sub rawdatasend
406c51ee 450{
451 my $cmd = shift;
dea4d7df 452 my $arr = @_ == 1 && ref($_[0]) ? $_[0] : \@_;
453 my $line = join("" ,@$arr);
406c51ee 454
455 return 0 unless defined(fileno($cmd));
456
457 return 1
dea4d7df 458 unless length($line);
406c51ee 459
dea4d7df 460 if($cmd->debug)
406c51ee 461 {
dea4d7df 462 my $b = "$cmd>>> ";
463 print STDERR $b,join("\n$b",split(/\n/,$line)),"\n";
406c51ee 464 }
dea4d7df 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)
406c51ee 474 {
dea4d7df 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 }
406c51ee 492 }
493
dea4d7df 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
edd55068 510 $cmd->debug_print(1, ".\n")
406c51ee 511 if($cmd->debug);
512
513 syswrite($cmd,".\015\012",3);
514
dea4d7df 515 delete ${*$cmd}{'net_cmd_need_crlf'};
406c51ee 516
517 $cmd->response() == CMD_OK;
518}
519
12df23ee 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;
edd55068 540 my ($len,$offset) = @_[1,2];
12df23ee 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
406c51ee 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;
686337f3 596
406c51ee 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
510179aa 617Different packages
618may implement different levels of debug but a non-zero value results in
406c51ee 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
dea4d7df 720=item rawdatasend ( DATA )
721
722Send data to the remote server without performing any conversions. C<DATA>
723is a scalar.
724
406c51ee 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
12df23ee 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
406c51ee 742=back
743
744=head1 EXPORTS
745
746C<Net::Cmd> exports six subroutines, five of these, C<CMD_INFO>, C<CMD_OK>,
510179aa 747C<CMD_MORE>, C<CMD_REJECT> and C<CMD_ERROR>, correspond to possible results
406c51ee 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
686337f3 760=for html <hr>
761
dea4d7df 762I<$Id: //depot/libnet/Net/Cmd.pm#33 $>
686337f3 763
406c51ee 764=cut