line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
# Net::FTP.pm |
2
|
|
|
|
|
|
|
# |
3
|
|
|
|
|
|
|
# Copyright (C) 1995-2004 Graham Barr. All rights reserved. |
4
|
|
|
|
|
|
|
# Copyright (C) 2013-2017, 2020, 2022 Steve Hay. All rights reserved. |
5
|
|
|
|
|
|
|
# This module is free software; you can redistribute it and/or modify it under |
6
|
|
|
|
|
|
|
# the same terms as Perl itself, i.e. under the terms of either the GNU General |
7
|
|
|
|
|
|
|
# Public License or the Artistic License, as specified in the F file. |
8
|
|
|
|
|
|
|
# |
9
|
|
|
|
|
|
|
# Documentation (at end) improved 1996 by Nathan Torkington . |
10
|
|
|
|
|
|
|
|
11
|
|
|
|
|
|
|
package Net::FTP; |
12
|
|
|
|
|
|
|
|
13
|
2
|
|
|
2
|
|
1111
|
use 5.008001; |
|
2
|
|
|
|
|
7
|
|
14
|
|
|
|
|
|
|
|
15
|
2
|
|
|
2
|
|
8
|
use strict; |
|
2
|
|
|
|
|
4
|
|
|
2
|
|
|
|
|
33
|
|
16
|
2
|
|
|
2
|
|
8
|
use warnings; |
|
2
|
|
|
|
|
3
|
|
|
2
|
|
|
|
|
37
|
|
17
|
|
|
|
|
|
|
|
18
|
2
|
|
|
2
|
|
8
|
use Carp; |
|
2
|
|
|
|
|
3
|
|
|
2
|
|
|
|
|
100
|
|
19
|
2
|
|
|
2
|
|
9
|
use Fcntl qw(O_WRONLY O_RDONLY O_APPEND O_CREAT O_TRUNC); |
|
2
|
|
|
|
|
3
|
|
|
2
|
|
|
|
|
80
|
|
20
|
2
|
|
|
2
|
|
831
|
use IO::Socket; |
|
2
|
|
|
|
|
21829
|
|
|
2
|
|
|
|
|
46
|
|
21
|
2
|
|
|
2
|
|
1179
|
use Net::Cmd; |
|
2
|
|
|
|
|
3
|
|
|
2
|
|
|
|
|
98
|
|
22
|
2
|
|
|
2
|
|
12
|
use Net::Config; |
|
2
|
|
|
|
|
11
|
|
|
2
|
|
|
|
|
132
|
|
23
|
2
|
|
|
2
|
|
10
|
use Socket; |
|
2
|
|
|
|
|
3
|
|
|
2
|
|
|
|
|
811
|
|
24
|
2
|
|
|
2
|
|
853
|
use Time::Local; |
|
2
|
|
|
|
|
3741
|
|
|
2
|
|
|
|
|
157
|
|
25
|
|
|
|
|
|
|
|
26
|
|
|
|
|
|
|
our $VERSION = '3.14'; |
27
|
|
|
|
|
|
|
|
28
|
|
|
|
|
|
|
our $IOCLASS; |
29
|
|
|
|
|
|
|
my $family_key; |
30
|
|
|
|
|
|
|
BEGIN { |
31
|
|
|
|
|
|
|
# Code for detecting if we can use SSL |
32
|
2
|
|
50
|
2
|
|
8
|
my $ssl_class = eval { |
33
|
|
|
|
|
|
|
require IO::Socket::SSL; |
34
|
|
|
|
|
|
|
# first version with default CA on most platforms |
35
|
2
|
|
|
2
|
|
12
|
no warnings 'numeric'; |
|
2
|
|
|
|
|
2
|
|
|
2
|
|
|
|
|
152
|
|
36
|
|
|
|
|
|
|
IO::Socket::SSL->VERSION(2.007); |
37
|
|
|
|
|
|
|
} && 'IO::Socket::SSL'; |
38
|
|
|
|
|
|
|
|
39
|
2
|
|
50
|
|
|
11
|
my $nossl_warn = !$ssl_class && |
40
|
|
|
|
|
|
|
'To use SSL please install IO::Socket::SSL with version>=2.007'; |
41
|
|
|
|
|
|
|
|
42
|
|
|
|
|
|
|
# Code for detecting if we can use IPv6 |
43
|
|
|
|
|
|
|
my $inet6_class = eval { |
44
|
|
|
|
|
|
|
require IO::Socket::IP; |
45
|
2
|
|
|
2
|
|
11
|
no warnings 'numeric'; |
|
2
|
|
|
|
|
3
|
|
|
2
|
|
|
|
|
105
|
|
46
|
|
|
|
|
|
|
IO::Socket::IP->VERSION(0.25); |
47
|
2
|
|
33
|
|
|
6
|
} && 'IO::Socket::IP' || eval { |
48
|
|
|
|
|
|
|
require IO::Socket::INET6; |
49
|
2
|
|
|
2
|
|
11
|
no warnings 'numeric'; |
|
2
|
|
|
|
|
4
|
|
|
2
|
|
|
|
|
226
|
|
50
|
|
|
|
|
|
|
IO::Socket::INET6->VERSION(2.62); |
51
|
|
|
|
|
|
|
} && 'IO::Socket::INET6'; |
52
|
|
|
|
|
|
|
|
53
|
0
|
|
|
0
|
1
|
|
sub can_ssl { $ssl_class }; |
54
|
0
|
|
|
0
|
1
|
|
sub can_inet6 { $inet6_class }; |
55
|
|
|
|
|
|
|
|
56
|
2
|
|
50
|
|
|
10
|
$IOCLASS = $ssl_class || $inet6_class || 'IO::Socket::INET'; |
57
|
2
|
50
|
0
|
|
|
14
|
$family_key = |
|
|
50
|
|
|
|
|
|
58
|
|
|
|
|
|
|
( $ssl_class ? $ssl_class->can_ipv6 : $inet6_class || '' ) |
59
|
|
|
|
|
|
|
eq 'IO::Socket::IP' |
60
|
|
|
|
|
|
|
? 'Family' : 'Domain'; |
61
|
|
|
|
|
|
|
} |
62
|
|
|
|
|
|
|
|
63
|
|
|
|
|
|
|
our @ISA = ('Exporter','Net::Cmd',$IOCLASS); |
64
|
|
|
|
|
|
|
|
65
|
2
|
|
|
2
|
|
114
|
use constant TELNET_IAC => 255; |
|
2
|
|
|
|
|
3
|
|
|
2
|
|
|
|
|
98
|
|
66
|
2
|
|
|
2
|
|
10
|
use constant TELNET_IP => 244; |
|
2
|
|
|
|
|
4
|
|
|
2
|
|
|
|
|
73
|
|
67
|
2
|
|
|
2
|
|
9
|
use constant TELNET_DM => 242; |
|
2
|
|
|
|
|
4
|
|
|
2
|
|
|
|
|
79
|
|
68
|
|
|
|
|
|
|
|
69
|
2
|
|
|
2
|
|
10
|
use constant EBCDIC => ord 'A' == 193; |
|
2
|
|
|
|
|
3
|
|
|
2
|
|
|
|
|
15728
|
|
70
|
|
|
|
|
|
|
|
71
|
|
|
|
|
|
|
sub new { |
72
|
0
|
|
|
0
|
1
|
|
my $pkg = shift; |
73
|
0
|
|
|
|
|
|
my ($peer, %arg); |
74
|
0
|
0
|
|
|
|
|
if (@_ % 2) { |
75
|
0
|
|
|
|
|
|
$peer = shift; |
76
|
0
|
|
|
|
|
|
%arg = @_; |
77
|
|
|
|
|
|
|
} |
78
|
|
|
|
|
|
|
else { |
79
|
0
|
|
|
|
|
|
%arg = @_; |
80
|
0
|
|
|
|
|
|
$peer = delete $arg{Host}; |
81
|
|
|
|
|
|
|
} |
82
|
|
|
|
|
|
|
|
83
|
0
|
|
|
|
|
|
my $host = $peer; |
84
|
0
|
|
|
|
|
|
my $fire = undef; |
85
|
0
|
|
|
|
|
|
my $fire_type = undef; |
86
|
|
|
|
|
|
|
|
87
|
0
|
0
|
0
|
|
|
|
if (exists($arg{Firewall}) || Net::Config->requires_firewall($peer)) { |
88
|
|
|
|
|
|
|
$fire = $arg{Firewall} |
89
|
|
|
|
|
|
|
|| $ENV{FTP_FIREWALL} |
90
|
|
|
|
|
|
|
|| $NetConfig{ftp_firewall} |
91
|
0
|
|
0
|
|
|
|
|| undef; |
92
|
|
|
|
|
|
|
|
93
|
0
|
0
|
|
|
|
|
if (defined $fire) { |
94
|
0
|
|
|
|
|
|
$peer = $fire; |
95
|
0
|
|
|
|
|
|
delete $arg{Port}; |
96
|
|
|
|
|
|
|
$fire_type = $arg{FirewallType} |
97
|
|
|
|
|
|
|
|| $ENV{FTP_FIREWALL_TYPE} |
98
|
|
|
|
|
|
|
|| $NetConfig{firewall_type} |
99
|
0
|
|
0
|
|
|
|
|| undef; |
100
|
|
|
|
|
|
|
} |
101
|
|
|
|
|
|
|
} |
102
|
|
|
|
|
|
|
|
103
|
0
|
|
|
|
|
|
my %tlsargs; |
104
|
0
|
0
|
|
|
|
|
if (can_ssl()) { |
|
|
0
|
|
|
|
|
|
105
|
|
|
|
|
|
|
# for name verification strip port from domain:port, ipv4:port, [ipv6]:port |
106
|
0
|
|
|
|
|
|
(my $hostname = $host) =~s{(?
|
107
|
0
|
0
|
|
|
|
|
%tlsargs = ( |
108
|
|
|
|
|
|
|
SSL_verifycn_scheme => 'ftp', |
109
|
|
|
|
|
|
|
SSL_verifycn_name => $hostname, |
110
|
|
|
|
|
|
|
# use SNI if supported by IO::Socket::SSL |
111
|
|
|
|
|
|
|
$pkg->can_client_sni ? (SSL_hostname => $hostname):(), |
112
|
|
|
|
|
|
|
# reuse SSL session of control connection in data connections |
113
|
|
|
|
|
|
|
SSL_session_cache_size => 10, |
114
|
|
|
|
|
|
|
SSL_session_key => $hostname, |
115
|
|
|
|
|
|
|
); |
116
|
|
|
|
|
|
|
# user defined SSL arg |
117
|
0
|
|
|
|
|
|
$tlsargs{$_} = $arg{$_} for(grep { m{^SSL_} } keys %arg); |
|
0
|
|
|
|
|
|
|
118
|
0
|
0
|
|
|
|
|
$tlsargs{SSL_reuse_ctx} = IO::Socket::SSL::SSL_Context->new(%tlsargs) |
119
|
|
|
|
|
|
|
or return; |
120
|
|
|
|
|
|
|
|
121
|
|
|
|
|
|
|
} elsif ($arg{SSL}) { |
122
|
0
|
|
|
|
|
|
croak("IO::Socket::SSL >= 2.007 needed for SSL support"); |
123
|
|
|
|
|
|
|
} |
124
|
|
|
|
|
|
|
|
125
|
|
|
|
|
|
|
my $ftp = $pkg->SUPER::new( |
126
|
|
|
|
|
|
|
PeerAddr => $peer, |
127
|
|
|
|
|
|
|
PeerPort => $arg{Port} || ($arg{SSL} ? 'ftps(990)' : 'ftp(21)'), |
128
|
|
|
|
|
|
|
LocalAddr => $arg{'LocalAddr'}, |
129
|
|
|
|
|
|
|
$family_key => $arg{Domain} || $arg{Family}, |
130
|
|
|
|
|
|
|
Proto => 'tcp', |
131
|
|
|
|
|
|
|
Timeout => defined $arg{Timeout} ? $arg{Timeout} : 120, |
132
|
|
|
|
|
|
|
%tlsargs, |
133
|
0
|
0
|
0
|
|
|
|
$arg{SSL} ? ():( SSL_startHandshake => 0 ), |
|
|
0
|
0
|
|
|
|
|
|
|
0
|
|
|
|
|
|
134
|
|
|
|
|
|
|
) or return; |
135
|
|
|
|
|
|
|
|
136
|
0
|
|
|
|
|
|
${*$ftp}{'net_ftp_host'} = $host; # Remote hostname |
|
0
|
|
|
|
|
|
|
137
|
0
|
|
|
|
|
|
${*$ftp}{'net_ftp_type'} = 'A'; # ASCII/binary/etc mode |
|
0
|
|
|
|
|
|
|
138
|
0
|
|
0
|
|
|
|
${*$ftp}{'net_ftp_blksize'} = abs($arg{'BlockSize'} || 10240); |
|
0
|
|
|
|
|
|
|
139
|
|
|
|
|
|
|
|
140
|
0
|
|
|
|
|
|
${*$ftp}{'net_ftp_localaddr'} = $arg{'LocalAddr'}; |
|
0
|
|
|
|
|
|
|
141
|
0
|
|
0
|
|
|
|
${*$ftp}{'net_ftp_domain'} = $arg{Domain} || $arg{Family}; |
|
0
|
|
|
|
|
|
|
142
|
|
|
|
|
|
|
|
143
|
0
|
0
|
|
|
|
|
${*$ftp}{'net_ftp_firewall'} = $fire |
|
0
|
|
|
|
|
|
|
144
|
|
|
|
|
|
|
if (defined $fire); |
145
|
0
|
0
|
|
|
|
|
${*$ftp}{'net_ftp_firewall_type'} = $fire_type |
|
0
|
|
|
|
|
|
|
146
|
|
|
|
|
|
|
if (defined $fire_type); |
147
|
|
|
|
|
|
|
|
148
|
0
|
|
|
|
|
|
${*$ftp}{'net_ftp_passive'} = |
149
|
|
|
|
|
|
|
int exists $arg{Passive} ? $arg{Passive} |
150
|
|
|
|
|
|
|
: exists $ENV{FTP_PASSIVE} ? $ENV{FTP_PASSIVE} |
151
|
|
|
|
|
|
|
: defined $fire ? $NetConfig{ftp_ext_passive} |
152
|
0
|
0
|
|
|
|
|
: $NetConfig{ftp_int_passive}; # Whew! :-) |
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
153
|
|
|
|
|
|
|
|
154
|
0
|
0
|
|
|
|
|
${*$ftp}{net_ftp_tlsargs} = \%tlsargs if %tlsargs; |
|
0
|
|
|
|
|
|
|
155
|
0
|
0
|
|
|
|
|
if ($arg{SSL}) { |
156
|
0
|
|
|
|
|
|
${*$ftp}{net_ftp_tlsprot} = 'P'; |
|
0
|
|
|
|
|
|
|
157
|
0
|
|
|
|
|
|
${*$ftp}{net_ftp_tlsdirect} = 1; |
|
0
|
|
|
|
|
|
|
158
|
|
|
|
|
|
|
} |
159
|
|
|
|
|
|
|
|
160
|
0
|
0
|
|
|
|
|
$ftp->hash(exists $arg{Hash} ? $arg{Hash} : 0, 1024); |
161
|
|
|
|
|
|
|
|
162
|
0
|
|
|
|
|
|
$ftp->autoflush(1); |
163
|
|
|
|
|
|
|
|
164
|
0
|
0
|
|
|
|
|
$ftp->debug(exists $arg{Debug} ? $arg{Debug} : undef); |
165
|
|
|
|
|
|
|
|
166
|
0
|
0
|
|
|
|
|
unless ($ftp->response() == CMD_OK) { |
167
|
0
|
|
|
|
|
|
$ftp->close(); |
168
|
|
|
|
|
|
|
# keep @$ if no message. Happens, when response did not start with a code. |
169
|
0
|
|
0
|
|
|
|
$@ = $ftp->message || $@; |
170
|
0
|
|
|
|
|
|
undef $ftp; |
171
|
|
|
|
|
|
|
} |
172
|
|
|
|
|
|
|
|
173
|
0
|
|
|
|
|
|
$ftp; |
174
|
|
|
|
|
|
|
} |
175
|
|
|
|
|
|
|
|
176
|
|
|
|
|
|
|
## |
177
|
|
|
|
|
|
|
## User interface methods |
178
|
|
|
|
|
|
|
## |
179
|
|
|
|
|
|
|
|
180
|
|
|
|
|
|
|
|
181
|
|
|
|
|
|
|
sub host { |
182
|
0
|
|
|
0
|
1
|
|
my $me = shift; |
183
|
0
|
|
|
|
|
|
${*$me}{'net_ftp_host'}; |
|
0
|
|
|
|
|
|
|
184
|
|
|
|
|
|
|
} |
185
|
|
|
|
|
|
|
|
186
|
|
|
|
|
|
|
sub passive { |
187
|
0
|
|
|
0
|
1
|
|
my $ftp = shift; |
188
|
0
|
0
|
|
|
|
|
return ${*$ftp}{'net_ftp_passive'} unless @_; |
|
0
|
|
|
|
|
|
|
189
|
0
|
|
|
|
|
|
${*$ftp}{'net_ftp_passive'} = shift; |
|
0
|
|
|
|
|
|
|
190
|
|
|
|
|
|
|
} |
191
|
|
|
|
|
|
|
|
192
|
|
|
|
|
|
|
|
193
|
|
|
|
|
|
|
sub hash { |
194
|
0
|
|
|
0
|
1
|
|
my $ftp = shift; # self |
195
|
|
|
|
|
|
|
|
196
|
0
|
|
|
|
|
|
my ($h, $b) = @_; |
197
|
0
|
0
|
|
|
|
|
unless ($h) { |
198
|
0
|
|
|
|
|
|
delete ${*$ftp}{'net_ftp_hash'}; |
|
0
|
|
|
|
|
|
|
199
|
0
|
|
|
|
|
|
return [\*STDERR, 0]; |
200
|
|
|
|
|
|
|
} |
201
|
0
|
0
|
0
|
|
|
|
($h, $b) = (ref($h) ? $h : \*STDERR, $b || 1024); |
202
|
0
|
|
|
|
|
|
select((select($h), $| = 1)[0]); |
203
|
0
|
0
|
|
|
|
|
$b = 512 if $b < 512; |
204
|
0
|
|
|
|
|
|
${*$ftp}{'net_ftp_hash'} = [$h, $b]; |
|
0
|
|
|
|
|
|
|
205
|
|
|
|
|
|
|
} |
206
|
|
|
|
|
|
|
|
207
|
|
|
|
|
|
|
|
208
|
|
|
|
|
|
|
sub quit { |
209
|
0
|
|
|
0
|
1
|
|
my $ftp = shift; |
210
|
|
|
|
|
|
|
|
211
|
0
|
|
|
|
|
|
$ftp->_QUIT; |
212
|
0
|
|
|
|
|
|
$ftp->close; |
213
|
|
|
|
|
|
|
} |
214
|
|
|
|
|
|
|
|
215
|
|
|
|
|
|
|
|
216
|
|
|
|
0
|
|
|
sub DESTROY { } |
217
|
|
|
|
|
|
|
|
218
|
|
|
|
|
|
|
|
219
|
0
|
|
|
0
|
1
|
|
sub ascii { shift->type('A', @_); } |
220
|
0
|
|
|
0
|
1
|
|
sub binary { shift->type('I', @_); } |
221
|
|
|
|
|
|
|
|
222
|
|
|
|
|
|
|
|
223
|
|
|
|
|
|
|
sub ebcdic { |
224
|
0
|
|
|
0
|
0
|
|
carp "TYPE E is unsupported, shall default to I"; |
225
|
0
|
|
|
|
|
|
shift->type('E', @_); |
226
|
|
|
|
|
|
|
} |
227
|
|
|
|
|
|
|
|
228
|
|
|
|
|
|
|
|
229
|
|
|
|
|
|
|
sub byte { |
230
|
0
|
|
|
0
|
0
|
|
carp "TYPE L is unsupported, shall default to I"; |
231
|
0
|
|
|
|
|
|
shift->type('L', @_); |
232
|
|
|
|
|
|
|
} |
233
|
|
|
|
|
|
|
|
234
|
|
|
|
|
|
|
# Allow the user to send a command directly, BE CAREFUL !! |
235
|
|
|
|
|
|
|
|
236
|
|
|
|
|
|
|
|
237
|
|
|
|
|
|
|
sub quot { |
238
|
0
|
|
|
0
|
1
|
|
my $ftp = shift; |
239
|
0
|
|
|
|
|
|
my $cmd = shift; |
240
|
|
|
|
|
|
|
|
241
|
0
|
|
|
|
|
|
$ftp->command(uc $cmd, @_); |
242
|
0
|
|
|
|
|
|
$ftp->response(); |
243
|
|
|
|
|
|
|
} |
244
|
|
|
|
|
|
|
|
245
|
|
|
|
|
|
|
|
246
|
|
|
|
|
|
|
sub site { |
247
|
0
|
|
|
0
|
1
|
|
my $ftp = shift; |
248
|
|
|
|
|
|
|
|
249
|
0
|
|
|
|
|
|
$ftp->command("SITE", @_); |
250
|
0
|
|
|
|
|
|
$ftp->response(); |
251
|
|
|
|
|
|
|
} |
252
|
|
|
|
|
|
|
|
253
|
|
|
|
|
|
|
|
254
|
|
|
|
|
|
|
sub mdtm { |
255
|
0
|
|
|
0
|
1
|
|
my $ftp = shift; |
256
|
0
|
|
|
|
|
|
my $file = shift; |
257
|
|
|
|
|
|
|
|
258
|
|
|
|
|
|
|
# Server Y2K bug workaround |
259
|
|
|
|
|
|
|
# |
260
|
|
|
|
|
|
|
# sigh; some idiotic FTP servers use ("19%d",tm.tm_year) instead of |
261
|
|
|
|
|
|
|
# ("%d",tm.tm_year+1900). This results in an extra digit in the |
262
|
|
|
|
|
|
|
# string returned. To account for this we allow an optional extra |
263
|
|
|
|
|
|
|
# digit in the year. Then if the first two digits are 19 we use the |
264
|
|
|
|
|
|
|
# remainder, otherwise we subtract 1900 from the whole year. |
265
|
|
|
|
|
|
|
|
266
|
0
|
0
|
0
|
|
|
|
$ftp->_MDTM($file) |
|
|
0
|
|
|
|
|
|
267
|
|
|
|
|
|
|
&& $ftp->message =~ /((\d\d)(\d\d\d?))(\d\d)(\d\d)(\d\d)(\d\d)(\d\d)/ |
268
|
|
|
|
|
|
|
? timegm($8, $7, $6, $5, $4 - 1, $2 eq '19' ? ($3 + 1900) : $1) |
269
|
|
|
|
|
|
|
: undef; |
270
|
|
|
|
|
|
|
} |
271
|
|
|
|
|
|
|
|
272
|
|
|
|
|
|
|
|
273
|
|
|
|
|
|
|
sub size { |
274
|
0
|
|
|
0
|
1
|
|
my $ftp = shift; |
275
|
0
|
|
|
|
|
|
my $file = shift; |
276
|
0
|
|
|
|
|
|
my $io; |
277
|
0
|
0
|
|
|
|
|
if ($ftp->supported("SIZE")) { |
|
|
0
|
|
|
|
|
|
278
|
0
|
0
|
|
|
|
|
return $ftp->_SIZE($file) |
279
|
|
|
|
|
|
|
? ($ftp->message =~ /(\d+)\s*(bytes?\s*)?$/)[0] |
280
|
|
|
|
|
|
|
: undef; |
281
|
|
|
|
|
|
|
} |
282
|
|
|
|
|
|
|
elsif ($ftp->supported("STAT")) { |
283
|
0
|
|
|
|
|
|
my @msg; |
284
|
|
|
|
|
|
|
return |
285
|
0
|
0
|
0
|
|
|
|
unless $ftp->_STAT($file) && (@msg = $ftp->message) == 3; |
286
|
0
|
|
|
|
|
|
foreach my $line (@msg) { |
287
|
0
|
0
|
|
|
|
|
return (split(/\s+/, $line))[4] |
288
|
|
|
|
|
|
|
if $line =~ /^[-rwxSsTt]{10}/; |
289
|
|
|
|
|
|
|
} |
290
|
|
|
|
|
|
|
} |
291
|
|
|
|
|
|
|
else { |
292
|
0
|
|
|
|
|
|
my @files = $ftp->dir($file); |
293
|
0
|
0
|
|
|
|
|
if (@files) { |
294
|
0
|
0
|
|
|
|
|
return (split(/\s+/, $1))[4] |
295
|
|
|
|
|
|
|
if $files[0] =~ /^([-rwxSsTt]{10}.*)$/; |
296
|
|
|
|
|
|
|
} |
297
|
|
|
|
|
|
|
} |
298
|
0
|
|
|
|
|
|
undef; |
299
|
|
|
|
|
|
|
} |
300
|
|
|
|
|
|
|
|
301
|
|
|
|
|
|
|
|
302
|
|
|
|
|
|
|
sub starttls { |
303
|
0
|
|
|
0
|
1
|
|
my $ftp = shift; |
304
|
0
|
0
|
|
|
|
|
can_ssl() or croak("IO::Socket::SSL >= 2.007 needed for SSL support"); |
305
|
0
|
0
|
|
|
|
|
$ftp->is_SSL and croak("called starttls within SSL session"); |
306
|
0
|
0
|
|
|
|
|
$ftp->_AUTH('TLS') == CMD_OK or return; |
307
|
|
|
|
|
|
|
|
308
|
0
|
0
|
|
|
|
|
$ftp->connect_SSL or return; |
309
|
0
|
|
|
|
|
|
$ftp->prot('P'); |
310
|
0
|
|
|
|
|
|
return 1; |
311
|
|
|
|
|
|
|
} |
312
|
|
|
|
|
|
|
|
313
|
|
|
|
|
|
|
sub prot { |
314
|
0
|
|
|
0
|
1
|
|
my ($ftp,$prot) = @_; |
315
|
0
|
0
|
0
|
|
|
|
$prot eq 'C' or $prot eq 'P' or croak("prot must by C or P"); |
316
|
0
|
0
|
|
|
|
|
$ftp->_PBSZ(0) or return; |
317
|
0
|
0
|
|
|
|
|
$ftp->_PROT($prot) or return; |
318
|
0
|
|
|
|
|
|
${*$ftp}{net_ftp_tlsprot} = $prot; |
|
0
|
|
|
|
|
|
|
319
|
0
|
|
|
|
|
|
return 1; |
320
|
|
|
|
|
|
|
} |
321
|
|
|
|
|
|
|
|
322
|
|
|
|
|
|
|
sub stoptls { |
323
|
0
|
|
|
0
|
1
|
|
my $ftp = shift; |
324
|
0
|
0
|
|
|
|
|
$ftp->is_SSL or croak("called stoptls outside SSL session"); |
325
|
0
|
0
|
|
|
|
|
${*$ftp}{net_ftp_tlsdirect} and croak("cannot stoptls direct SSL session"); |
|
0
|
|
|
|
|
|
|
326
|
0
|
0
|
|
|
|
|
$ftp->_CCC() or return; |
327
|
0
|
|
|
|
|
|
$ftp->stop_SSL(); |
328
|
0
|
|
|
|
|
|
return 1; |
329
|
|
|
|
|
|
|
} |
330
|
|
|
|
|
|
|
|
331
|
|
|
|
|
|
|
sub login { |
332
|
0
|
|
|
0
|
1
|
|
my ($ftp, $user, $pass, $acct) = @_; |
333
|
0
|
|
|
|
|
|
my ($ok, $ruser, $fwtype); |
334
|
|
|
|
|
|
|
|
335
|
0
|
0
|
|
|
|
|
unless (defined $user) { |
336
|
0
|
|
|
|
|
|
require Net::Netrc; |
337
|
|
|
|
|
|
|
|
338
|
0
|
|
|
|
|
|
my $rc = Net::Netrc->lookup(${*$ftp}{'net_ftp_host'}); |
|
0
|
|
|
|
|
|
|
339
|
|
|
|
|
|
|
|
340
|
0
|
0
|
|
|
|
|
($user, $pass, $acct) = $rc->lpa() |
341
|
|
|
|
|
|
|
if ($rc); |
342
|
|
|
|
|
|
|
} |
343
|
|
|
|
|
|
|
|
344
|
0
|
|
0
|
|
|
|
$user ||= "anonymous"; |
345
|
0
|
|
|
|
|
|
$ruser = $user; |
346
|
|
|
|
|
|
|
|
347
|
|
|
|
|
|
|
$fwtype = ${*$ftp}{'net_ftp_firewall_type'} |
348
|
0
|
|
0
|
|
|
|
|| $NetConfig{'ftp_firewall_type'} |
349
|
|
|
|
|
|
|
|| 0; |
350
|
|
|
|
|
|
|
|
351
|
0
|
0
|
0
|
|
|
|
if ($fwtype && defined ${*$ftp}{'net_ftp_firewall'}) { |
|
0
|
|
|
|
|
|
|
352
|
0
|
0
|
0
|
|
|
|
if ($fwtype == 1 || $fwtype == 7) { |
353
|
0
|
|
|
|
|
|
$user .= '@' . ${*$ftp}{'net_ftp_host'}; |
|
0
|
|
|
|
|
|
|
354
|
|
|
|
|
|
|
} |
355
|
|
|
|
|
|
|
else { |
356
|
0
|
|
|
|
|
|
require Net::Netrc; |
357
|
|
|
|
|
|
|
|
358
|
0
|
|
|
|
|
|
my $rc = Net::Netrc->lookup(${*$ftp}{'net_ftp_firewall'}); |
|
0
|
|
|
|
|
|
|
359
|
|
|
|
|
|
|
|
360
|
0
|
0
|
|
|
|
|
my ($fwuser, $fwpass, $fwacct) = $rc ? $rc->lpa() : (); |
361
|
|
|
|
|
|
|
|
362
|
0
|
0
|
|
|
|
|
if ($fwtype == 5) { |
363
|
0
|
|
|
|
|
|
$user = join('@', $user, $fwuser, ${*$ftp}{'net_ftp_host'}); |
|
0
|
|
|
|
|
|
|
364
|
0
|
|
|
|
|
|
$pass = $pass . '@' . $fwpass; |
365
|
|
|
|
|
|
|
} |
366
|
|
|
|
|
|
|
else { |
367
|
0
|
0
|
|
|
|
|
if ($fwtype == 2) { |
|
|
0
|
|
|
|
|
|
368
|
0
|
|
|
|
|
|
$user .= '@' . ${*$ftp}{'net_ftp_host'}; |
|
0
|
|
|
|
|
|
|
369
|
|
|
|
|
|
|
} |
370
|
|
|
|
|
|
|
elsif ($fwtype == 6) { |
371
|
0
|
|
|
|
|
|
$fwuser .= '@' . ${*$ftp}{'net_ftp_host'}; |
|
0
|
|
|
|
|
|
|
372
|
|
|
|
|
|
|
} |
373
|
|
|
|
|
|
|
|
374
|
0
|
|
|
|
|
|
$ok = $ftp->_USER($fwuser); |
375
|
|
|
|
|
|
|
|
376
|
0
|
0
|
0
|
|
|
|
return 0 unless $ok == CMD_OK || $ok == CMD_MORE; |
377
|
|
|
|
|
|
|
|
378
|
0
|
|
0
|
|
|
|
$ok = $ftp->_PASS($fwpass || ""); |
379
|
|
|
|
|
|
|
|
380
|
0
|
0
|
0
|
|
|
|
return 0 unless $ok == CMD_OK || $ok == CMD_MORE; |
381
|
|
|
|
|
|
|
|
382
|
0
|
0
|
|
|
|
|
$ok = $ftp->_ACCT($fwacct) |
383
|
|
|
|
|
|
|
if defined($fwacct); |
384
|
|
|
|
|
|
|
|
385
|
0
|
0
|
|
|
|
|
if ($fwtype == 3) { |
|
|
0
|
|
|
|
|
|
386
|
0
|
|
|
|
|
|
$ok = $ftp->command("SITE", ${*$ftp}{'net_ftp_host'})->response; |
|
0
|
|
|
|
|
|
|
387
|
|
|
|
|
|
|
} |
388
|
|
|
|
|
|
|
elsif ($fwtype == 4) { |
389
|
0
|
|
|
|
|
|
$ok = $ftp->command("OPEN", ${*$ftp}{'net_ftp_host'})->response; |
|
0
|
|
|
|
|
|
|
390
|
|
|
|
|
|
|
} |
391
|
|
|
|
|
|
|
|
392
|
0
|
0
|
0
|
|
|
|
return 0 unless $ok == CMD_OK || $ok == CMD_MORE; |
393
|
|
|
|
|
|
|
} |
394
|
|
|
|
|
|
|
} |
395
|
|
|
|
|
|
|
} |
396
|
|
|
|
|
|
|
|
397
|
0
|
|
|
|
|
|
$ok = $ftp->_USER($user); |
398
|
|
|
|
|
|
|
|
399
|
|
|
|
|
|
|
# Some dumb firewalls don't prefix the connection messages |
400
|
0
|
0
|
0
|
|
|
|
$ok = $ftp->response() |
|
|
|
0
|
|
|
|
|
401
|
|
|
|
|
|
|
if ($ok == CMD_OK && $ftp->code == 220 && $user =~ /\@/); |
402
|
|
|
|
|
|
|
|
403
|
0
|
0
|
|
|
|
|
if ($ok == CMD_MORE) { |
404
|
0
|
0
|
|
|
|
|
unless (defined $pass) { |
405
|
0
|
|
|
|
|
|
require Net::Netrc; |
406
|
|
|
|
|
|
|
|
407
|
0
|
|
|
|
|
|
my $rc = Net::Netrc->lookup(${*$ftp}{'net_ftp_host'}, $ruser); |
|
0
|
|
|
|
|
|
|
408
|
|
|
|
|
|
|
|
409
|
0
|
0
|
|
|
|
|
($ruser, $pass, $acct) = $rc->lpa() |
410
|
|
|
|
|
|
|
if ($rc); |
411
|
|
|
|
|
|
|
|
412
|
0
|
0
|
0
|
|
|
|
$pass = '-anonymous@' |
|
|
|
0
|
|
|
|
|
413
|
|
|
|
|
|
|
if (!defined $pass && (!defined($ruser) || $ruser =~ /^anonymous/o)); |
414
|
|
|
|
|
|
|
} |
415
|
|
|
|
|
|
|
|
416
|
0
|
|
0
|
|
|
|
$ok = $ftp->_PASS($pass || ""); |
417
|
|
|
|
|
|
|
} |
418
|
|
|
|
|
|
|
|
419
|
0
|
0
|
0
|
|
|
|
$ok = $ftp->_ACCT($acct) |
|
|
|
0
|
|
|
|
|
420
|
|
|
|
|
|
|
if (defined($acct) && ($ok == CMD_MORE || $ok == CMD_OK)); |
421
|
|
|
|
|
|
|
|
422
|
0
|
0
|
0
|
|
|
|
if ($fwtype == 7 && $ok == CMD_OK && defined ${*$ftp}{'net_ftp_firewall'}) { |
|
0
|
|
0
|
|
|
|
|
423
|
0
|
|
|
|
|
|
my ($f, $auth, $resp) = _auth_id($ftp); |
424
|
0
|
0
|
|
|
|
|
$ftp->authorize($auth, $resp) if defined($resp); |
425
|
|
|
|
|
|
|
} |
426
|
|
|
|
|
|
|
|
427
|
0
|
|
|
|
|
|
$ok == CMD_OK; |
428
|
|
|
|
|
|
|
} |
429
|
|
|
|
|
|
|
|
430
|
|
|
|
|
|
|
|
431
|
|
|
|
|
|
|
sub account { |
432
|
0
|
0
|
|
0
|
1
|
|
@_ == 2 or croak 'usage: $ftp->account($acct)'; |
433
|
0
|
|
|
|
|
|
my $ftp = shift; |
434
|
0
|
|
|
|
|
|
my $acct = shift; |
435
|
0
|
|
|
|
|
|
$ftp->_ACCT($acct) == CMD_OK; |
436
|
|
|
|
|
|
|
} |
437
|
|
|
|
|
|
|
|
438
|
|
|
|
|
|
|
|
439
|
|
|
|
|
|
|
sub _auth_id { |
440
|
0
|
|
|
0
|
|
|
my ($ftp, $auth, $resp) = @_; |
441
|
|
|
|
|
|
|
|
442
|
0
|
0
|
|
|
|
|
unless (defined $resp) { |
443
|
0
|
|
|
|
|
|
require Net::Netrc; |
444
|
|
|
|
|
|
|
|
445
|
0
|
|
0
|
|
|
|
$auth ||= eval { (getpwuid($>))[0] } || $ENV{NAME}; |
|
|
|
0
|
|
|
|
|
446
|
|
|
|
|
|
|
|
447
|
|
|
|
|
|
|
my $rc = Net::Netrc->lookup(${*$ftp}{'net_ftp_firewall'}, $auth) |
448
|
0
|
|
0
|
|
|
|
|| Net::Netrc->lookup(${*$ftp}{'net_ftp_firewall'}); |
449
|
|
|
|
|
|
|
|
450
|
0
|
0
|
|
|
|
|
($auth, $resp) = $rc->lpa() |
451
|
|
|
|
|
|
|
if ($rc); |
452
|
|
|
|
|
|
|
} |
453
|
0
|
|
|
|
|
|
($ftp, $auth, $resp); |
454
|
|
|
|
|
|
|
} |
455
|
|
|
|
|
|
|
|
456
|
|
|
|
|
|
|
|
457
|
|
|
|
|
|
|
sub authorize { |
458
|
0
|
0
|
0
|
0
|
1
|
|
@_ >= 1 || @_ <= 3 or croak 'usage: $ftp->authorize([$auth[, $resp]])'; |
459
|
|
|
|
|
|
|
|
460
|
0
|
|
|
|
|
|
my ($ftp, $auth, $resp) = &_auth_id; |
461
|
|
|
|
|
|
|
|
462
|
0
|
|
0
|
|
|
|
my $ok = $ftp->_AUTH($auth || ""); |
463
|
|
|
|
|
|
|
|
464
|
0
|
0
|
0
|
|
|
|
return $ftp->_RESP($resp || "") |
465
|
|
|
|
|
|
|
if ($ok == CMD_MORE); |
466
|
|
|
|
|
|
|
|
467
|
0
|
|
|
|
|
|
$ok == CMD_OK; |
468
|
|
|
|
|
|
|
} |
469
|
|
|
|
|
|
|
|
470
|
|
|
|
|
|
|
|
471
|
|
|
|
|
|
|
sub rename { |
472
|
0
|
0
|
|
0
|
1
|
|
@_ == 3 or croak 'usage: $ftp->rename($oldname, $newname)'; |
473
|
|
|
|
|
|
|
|
474
|
0
|
|
|
|
|
|
my ($ftp, $oldname, $newname) = @_; |
475
|
|
|
|
|
|
|
|
476
|
0
|
0
|
|
|
|
|
$ftp->_RNFR($oldname) |
477
|
|
|
|
|
|
|
&& $ftp->_RNTO($newname); |
478
|
|
|
|
|
|
|
} |
479
|
|
|
|
|
|
|
|
480
|
|
|
|
|
|
|
|
481
|
|
|
|
|
|
|
sub type { |
482
|
0
|
|
|
0
|
1
|
|
my $ftp = shift; |
483
|
0
|
|
|
|
|
|
my $type = shift; |
484
|
0
|
|
|
|
|
|
my $oldval = ${*$ftp}{'net_ftp_type'}; |
|
0
|
|
|
|
|
|
|
485
|
|
|
|
|
|
|
|
486
|
0
|
0
|
|
|
|
|
return $oldval |
487
|
|
|
|
|
|
|
unless (defined $type); |
488
|
|
|
|
|
|
|
|
489
|
|
|
|
|
|
|
return |
490
|
0
|
0
|
|
|
|
|
unless ($ftp->_TYPE($type, @_)); |
491
|
|
|
|
|
|
|
|
492
|
0
|
|
|
|
|
|
${*$ftp}{'net_ftp_type'} = join(" ", $type, @_); |
|
0
|
|
|
|
|
|
|
493
|
|
|
|
|
|
|
|
494
|
0
|
|
|
|
|
|
$oldval; |
495
|
|
|
|
|
|
|
} |
496
|
|
|
|
|
|
|
|
497
|
|
|
|
|
|
|
|
498
|
|
|
|
|
|
|
sub alloc { |
499
|
0
|
|
|
0
|
1
|
|
my $ftp = shift; |
500
|
0
|
|
|
|
|
|
my $size = shift; |
501
|
0
|
|
|
|
|
|
my $oldval = ${*$ftp}{'net_ftp_allo'}; |
|
0
|
|
|
|
|
|
|
502
|
|
|
|
|
|
|
|
503
|
0
|
0
|
|
|
|
|
return $oldval |
504
|
|
|
|
|
|
|
unless (defined $size); |
505
|
|
|
|
|
|
|
|
506
|
|
|
|
|
|
|
return |
507
|
0
|
0
|
0
|
|
|
|
unless ($ftp->supported("ALLO") and $ftp->_ALLO($size, @_)); |
508
|
|
|
|
|
|
|
|
509
|
0
|
|
|
|
|
|
${*$ftp}{'net_ftp_allo'} = join(" ", $size, @_); |
|
0
|
|
|
|
|
|
|
510
|
|
|
|
|
|
|
|
511
|
0
|
|
|
|
|
|
$oldval; |
512
|
|
|
|
|
|
|
} |
513
|
|
|
|
|
|
|
|
514
|
|
|
|
|
|
|
|
515
|
|
|
|
|
|
|
sub abort { |
516
|
0
|
|
|
0
|
1
|
|
my $ftp = shift; |
517
|
|
|
|
|
|
|
|
518
|
0
|
|
|
|
|
|
send($ftp, pack("CCC", TELNET_IAC, TELNET_IP, TELNET_IAC), MSG_OOB); |
519
|
|
|
|
|
|
|
|
520
|
0
|
|
|
|
|
|
$ftp->command(pack("C", TELNET_DM) . "ABOR"); |
521
|
|
|
|
|
|
|
|
522
|
0
|
|
|
|
|
|
${*$ftp}{'net_ftp_dataconn'}->close() |
523
|
0
|
0
|
|
|
|
|
if defined ${*$ftp}{'net_ftp_dataconn'}; |
|
0
|
|
|
|
|
|
|
524
|
|
|
|
|
|
|
|
525
|
0
|
|
|
|
|
|
$ftp->response(); |
526
|
|
|
|
|
|
|
|
527
|
0
|
|
|
|
|
|
$ftp->status == CMD_OK; |
528
|
|
|
|
|
|
|
} |
529
|
|
|
|
|
|
|
|
530
|
|
|
|
|
|
|
|
531
|
|
|
|
|
|
|
sub get { |
532
|
0
|
|
|
0
|
1
|
|
my ($ftp, $remote, $local, $where) = @_; |
533
|
|
|
|
|
|
|
|
534
|
0
|
|
|
|
|
|
my ($loc, $len, $buf, $resp, $data); |
535
|
0
|
|
|
|
|
|
local *FD; |
536
|
|
|
|
|
|
|
|
537
|
0
|
|
0
|
|
|
|
my $localfd = ref($local) || ref(\$local) eq "GLOB"; |
538
|
|
|
|
|
|
|
|
539
|
0
|
0
|
|
|
|
|
($local = $remote) =~ s#^.*/## |
540
|
|
|
|
|
|
|
unless (defined $local); |
541
|
|
|
|
|
|
|
|
542
|
0
|
0
|
|
|
|
|
croak("Bad remote filename '$remote'\n") |
543
|
|
|
|
|
|
|
if $remote =~ /[\r\n]/s; |
544
|
|
|
|
|
|
|
|
545
|
0
|
0
|
|
|
|
|
${*$ftp}{'net_ftp_rest'} = $where if defined $where; |
|
0
|
|
|
|
|
|
|
546
|
0
|
|
|
|
|
|
my $rest = ${*$ftp}{'net_ftp_rest'}; |
|
0
|
|
|
|
|
|
|
547
|
|
|
|
|
|
|
|
548
|
0
|
|
|
|
|
|
delete ${*$ftp}{'net_ftp_port'}; |
|
0
|
|
|
|
|
|
|
549
|
0
|
|
|
|
|
|
delete ${*$ftp}{'net_ftp_pasv'}; |
|
0
|
|
|
|
|
|
|
550
|
|
|
|
|
|
|
|
551
|
0
|
0
|
|
|
|
|
$data = $ftp->retr($remote) |
552
|
|
|
|
|
|
|
or return; |
553
|
|
|
|
|
|
|
|
554
|
0
|
0
|
|
|
|
|
if ($localfd) { |
555
|
0
|
|
|
|
|
|
$loc = $local; |
556
|
|
|
|
|
|
|
} |
557
|
|
|
|
|
|
|
else { |
558
|
0
|
|
|
|
|
|
$loc = \*FD; |
559
|
|
|
|
|
|
|
|
560
|
0
|
0
|
|
|
|
|
unless (sysopen($loc, $local, O_CREAT | O_WRONLY | ($rest ? O_APPEND: O_TRUNC))) { |
|
|
0
|
|
|
|
|
|
561
|
0
|
|
|
|
|
|
carp "Cannot open Local file $local: $!\n"; |
562
|
0
|
|
|
|
|
|
$data->abort; |
563
|
0
|
|
|
|
|
|
return; |
564
|
|
|
|
|
|
|
} |
565
|
|
|
|
|
|
|
} |
566
|
|
|
|
|
|
|
|
567
|
0
|
0
|
0
|
|
|
|
if ($ftp->type eq 'I' && !binmode($loc)) { |
568
|
0
|
|
|
|
|
|
carp "Cannot binmode Local file $local: $!\n"; |
569
|
0
|
|
|
|
|
|
$data->abort; |
570
|
0
|
0
|
|
|
|
|
close($loc) unless $localfd; |
571
|
0
|
|
|
|
|
|
return; |
572
|
|
|
|
|
|
|
} |
573
|
|
|
|
|
|
|
|
574
|
0
|
|
|
|
|
|
$buf = ''; |
575
|
0
|
|
|
|
|
|
my ($count, $hashh, $hashb, $ref) = (0); |
576
|
|
|
|
|
|
|
|
577
|
|
|
|
|
|
|
($hashh, $hashb) = @$ref |
578
|
0
|
0
|
|
|
|
|
if ($ref = ${*$ftp}{'net_ftp_hash'}); |
|
0
|
|
|
|
|
|
|
579
|
|
|
|
|
|
|
|
580
|
0
|
|
|
|
|
|
my $blksize = ${*$ftp}{'net_ftp_blksize'}; |
|
0
|
|
|
|
|
|
|
581
|
0
|
|
|
|
|
|
local $\; # Just in case |
582
|
|
|
|
|
|
|
|
583
|
0
|
|
|
|
|
|
while (1) { |
584
|
0
|
0
|
|
|
|
|
last unless $len = $data->read($buf, $blksize); |
585
|
|
|
|
|
|
|
|
586
|
0
|
|
|
|
|
|
if (EBCDIC && $ftp->type ne 'I') { |
587
|
|
|
|
|
|
|
$buf = $ftp->toebcdic($buf); |
588
|
|
|
|
|
|
|
$len = length($buf); |
589
|
|
|
|
|
|
|
} |
590
|
|
|
|
|
|
|
|
591
|
0
|
0
|
|
|
|
|
if ($hashh) { |
592
|
0
|
|
|
|
|
|
$count += $len; |
593
|
0
|
|
|
|
|
|
print $hashh "#" x (int($count / $hashb)); |
594
|
0
|
|
|
|
|
|
$count %= $hashb; |
595
|
|
|
|
|
|
|
} |
596
|
0
|
0
|
|
|
|
|
unless (print $loc $buf) { |
597
|
0
|
|
|
|
|
|
carp "Cannot write to Local file $local: $!\n"; |
598
|
0
|
|
|
|
|
|
$data->abort; |
599
|
0
|
0
|
|
|
|
|
close($loc) |
600
|
|
|
|
|
|
|
unless $localfd; |
601
|
0
|
|
|
|
|
|
return; |
602
|
|
|
|
|
|
|
} |
603
|
|
|
|
|
|
|
} |
604
|
|
|
|
|
|
|
|
605
|
0
|
0
|
|
|
|
|
print $hashh "\n" if $hashh; |
606
|
|
|
|
|
|
|
|
607
|
0
|
0
|
|
|
|
|
unless ($localfd) { |
608
|
0
|
0
|
|
|
|
|
unless (close($loc)) { |
609
|
0
|
|
|
|
|
|
carp "Cannot close file $local (perhaps disk space) $!\n"; |
610
|
0
|
|
|
|
|
|
return; |
611
|
|
|
|
|
|
|
} |
612
|
|
|
|
|
|
|
} |
613
|
|
|
|
|
|
|
|
614
|
0
|
0
|
|
|
|
|
unless ($data->close()) # implied $ftp->response |
615
|
|
|
|
|
|
|
{ |
616
|
0
|
|
|
|
|
|
carp "Unable to close datastream"; |
617
|
0
|
|
|
|
|
|
return; |
618
|
|
|
|
|
|
|
} |
619
|
|
|
|
|
|
|
|
620
|
0
|
|
|
|
|
|
return $local; |
621
|
|
|
|
|
|
|
} |
622
|
|
|
|
|
|
|
|
623
|
|
|
|
|
|
|
|
624
|
|
|
|
|
|
|
sub cwd { |
625
|
0
|
0
|
0
|
0
|
1
|
|
@_ == 1 || @_ == 2 or croak 'usage: $ftp->cwd([$dir])'; |
626
|
|
|
|
|
|
|
|
627
|
0
|
|
|
|
|
|
my ($ftp, $dir) = @_; |
628
|
|
|
|
|
|
|
|
629
|
0
|
0
|
0
|
|
|
|
$dir = "/" unless defined($dir) && $dir =~ /\S/; |
630
|
|
|
|
|
|
|
|
631
|
0
|
0
|
|
|
|
|
$dir eq ".." |
632
|
|
|
|
|
|
|
? $ftp->_CDUP() |
633
|
|
|
|
|
|
|
: $ftp->_CWD($dir); |
634
|
|
|
|
|
|
|
} |
635
|
|
|
|
|
|
|
|
636
|
|
|
|
|
|
|
|
637
|
|
|
|
|
|
|
sub cdup { |
638
|
0
|
0
|
|
0
|
1
|
|
@_ == 1 or croak 'usage: $ftp->cdup()'; |
639
|
0
|
|
|
|
|
|
$_[0]->_CDUP; |
640
|
|
|
|
|
|
|
} |
641
|
|
|
|
|
|
|
|
642
|
|
|
|
|
|
|
|
643
|
|
|
|
|
|
|
sub pwd { |
644
|
0
|
0
|
|
0
|
1
|
|
@_ == 1 || croak 'usage: $ftp->pwd()'; |
645
|
0
|
|
|
|
|
|
my $ftp = shift; |
646
|
|
|
|
|
|
|
|
647
|
0
|
|
|
|
|
|
$ftp->_PWD(); |
648
|
0
|
|
|
|
|
|
$ftp->_extract_path; |
649
|
|
|
|
|
|
|
} |
650
|
|
|
|
|
|
|
|
651
|
|
|
|
|
|
|
# rmdir( $ftp, $dir, [ $recurse ] ) |
652
|
|
|
|
|
|
|
# |
653
|
|
|
|
|
|
|
# Removes $dir on remote host via FTP. |
654
|
|
|
|
|
|
|
# $ftp is handle for remote host |
655
|
|
|
|
|
|
|
# |
656
|
|
|
|
|
|
|
# If $recurse is TRUE, the directory and deleted recursively. |
657
|
|
|
|
|
|
|
# This means all of its contents and subdirectories. |
658
|
|
|
|
|
|
|
# |
659
|
|
|
|
|
|
|
# Initial version contributed by Dinkum Software |
660
|
|
|
|
|
|
|
# |
661
|
|
|
|
|
|
|
sub rmdir { |
662
|
0
|
0
|
0
|
0
|
1
|
|
@_ == 2 || @_ == 3 or croak('usage: $ftp->rmdir($dir[, $recurse])'); |
663
|
|
|
|
|
|
|
|
664
|
|
|
|
|
|
|
# Pick off the args |
665
|
0
|
|
|
|
|
|
my ($ftp, $dir, $recurse) = @_; |
666
|
0
|
|
|
|
|
|
my $ok; |
667
|
|
|
|
|
|
|
|
668
|
0
|
0
|
0
|
|
|
|
return $ok |
669
|
|
|
|
|
|
|
if $ok = $ftp->_RMD($dir) |
670
|
|
|
|
|
|
|
or !$recurse; |
671
|
|
|
|
|
|
|
|
672
|
|
|
|
|
|
|
# Try to delete the contents |
673
|
|
|
|
|
|
|
# Get a list of all the files in the directory, excluding the current and parent directories |
674
|
0
|
0
|
|
|
|
|
my @filelist = map { /^(?:\S+;)+ (.+)$/ ? ($1) : () } grep { !/^(?:\S+;)*type=[cp]dir;/i } $ftp->_list_cmd("MLSD", $dir); |
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
675
|
|
|
|
|
|
|
|
676
|
|
|
|
|
|
|
# Fallback to using the less well-defined NLST command if MLSD fails |
677
|
0
|
0
|
|
|
|
|
@filelist = grep { !/^\.{1,2}$/ } $ftp->ls($dir) |
|
0
|
|
|
|
|
|
|
678
|
|
|
|
|
|
|
unless @filelist; |
679
|
|
|
|
|
|
|
|
680
|
|
|
|
|
|
|
return |
681
|
0
|
0
|
|
|
|
|
unless @filelist; # failed, it is probably not a directory |
682
|
|
|
|
|
|
|
|
683
|
0
|
0
|
0
|
|
|
|
return $ftp->delete($dir) |
684
|
|
|
|
|
|
|
if @filelist == 1 and $dir eq $filelist[0]; |
685
|
|
|
|
|
|
|
|
686
|
|
|
|
|
|
|
# Go thru and delete each file or the directory |
687
|
0
|
0
|
|
|
|
|
foreach my $file (map { m,/, ? $_ : "$dir/$_" } @filelist) { |
|
0
|
|
|
|
|
|
|
688
|
|
|
|
|
|
|
next # successfully deleted the file |
689
|
0
|
0
|
|
|
|
|
if $ftp->delete($file); |
690
|
|
|
|
|
|
|
|
691
|
|
|
|
|
|
|
# Failed to delete it, assume its a directory |
692
|
|
|
|
|
|
|
# Recurse and ignore errors, the final rmdir() will |
693
|
|
|
|
|
|
|
# fail on any errors here |
694
|
0
|
0
|
|
|
|
|
return $ok |
695
|
|
|
|
|
|
|
unless $ok = $ftp->rmdir($file, 1); |
696
|
|
|
|
|
|
|
} |
697
|
|
|
|
|
|
|
|
698
|
|
|
|
|
|
|
# Directory should be empty |
699
|
|
|
|
|
|
|
# Try to remove the directory again |
700
|
|
|
|
|
|
|
# Pass results directly to caller |
701
|
|
|
|
|
|
|
# If any of the prior deletes failed, this |
702
|
|
|
|
|
|
|
# rmdir() will fail because directory is not empty |
703
|
0
|
|
|
|
|
|
return $ftp->_RMD($dir); |
704
|
|
|
|
|
|
|
} |
705
|
|
|
|
|
|
|
|
706
|
|
|
|
|
|
|
|
707
|
|
|
|
|
|
|
sub restart { |
708
|
0
|
0
|
|
0
|
1
|
|
@_ == 2 || croak 'usage: $ftp->restart($where)'; |
709
|
|
|
|
|
|
|
|
710
|
0
|
|
|
|
|
|
my ($ftp, $where) = @_; |
711
|
|
|
|
|
|
|
|
712
|
0
|
|
|
|
|
|
${*$ftp}{'net_ftp_rest'} = $where; |
|
0
|
|
|
|
|
|
|
713
|
|
|
|
|
|
|
|
714
|
0
|
|
|
|
|
|
return; |
715
|
|
|
|
|
|
|
} |
716
|
|
|
|
|
|
|
|
717
|
|
|
|
|
|
|
|
718
|
|
|
|
|
|
|
sub mkdir { |
719
|
0
|
0
|
0
|
0
|
1
|
|
@_ == 2 || @_ == 3 or croak 'usage: $ftp->mkdir($dir[, $recurse])'; |
720
|
|
|
|
|
|
|
|
721
|
0
|
|
|
|
|
|
my ($ftp, $dir, $recurse) = @_; |
722
|
|
|
|
|
|
|
|
723
|
0
|
0
|
0
|
|
|
|
$ftp->_MKD($dir) || $recurse |
724
|
|
|
|
|
|
|
or return; |
725
|
|
|
|
|
|
|
|
726
|
0
|
|
|
|
|
|
my $path = $dir; |
727
|
|
|
|
|
|
|
|
728
|
0
|
0
|
|
|
|
|
unless ($ftp->ok) { |
729
|
0
|
|
|
|
|
|
my @path = split(m#(?=/+)#, $dir); |
730
|
|
|
|
|
|
|
|
731
|
0
|
|
|
|
|
|
$path = ""; |
732
|
|
|
|
|
|
|
|
733
|
0
|
|
|
|
|
|
while (@path) { |
734
|
0
|
|
|
|
|
|
$path .= shift @path; |
735
|
|
|
|
|
|
|
|
736
|
0
|
|
|
|
|
|
$ftp->_MKD($path); |
737
|
|
|
|
|
|
|
|
738
|
0
|
|
|
|
|
|
$path = $ftp->_extract_path($path); |
739
|
|
|
|
|
|
|
} |
740
|
|
|
|
|
|
|
|
741
|
|
|
|
|
|
|
# If the creation of the last element was not successful, see if we |
742
|
|
|
|
|
|
|
# can cd to it, if so then return path |
743
|
|
|
|
|
|
|
|
744
|
0
|
0
|
|
|
|
|
unless ($ftp->ok) { |
745
|
0
|
|
|
|
|
|
my ($status, $message) = ($ftp->status, $ftp->message); |
746
|
0
|
|
|
|
|
|
my $pwd = $ftp->pwd; |
747
|
|
|
|
|
|
|
|
748
|
0
|
0
|
0
|
|
|
|
if ($pwd && $ftp->cwd($dir)) { |
749
|
0
|
|
|
|
|
|
$path = $dir; |
750
|
0
|
|
|
|
|
|
$ftp->cwd($pwd); |
751
|
|
|
|
|
|
|
} |
752
|
|
|
|
|
|
|
else { |
753
|
0
|
|
|
|
|
|
undef $path; |
754
|
|
|
|
|
|
|
} |
755
|
0
|
|
|
|
|
|
$ftp->set_status($status, $message); |
756
|
|
|
|
|
|
|
} |
757
|
|
|
|
|
|
|
} |
758
|
|
|
|
|
|
|
|
759
|
0
|
|
|
|
|
|
$path; |
760
|
|
|
|
|
|
|
} |
761
|
|
|
|
|
|
|
|
762
|
|
|
|
|
|
|
|
763
|
|
|
|
|
|
|
sub delete { |
764
|
0
|
0
|
|
0
|
1
|
|
@_ == 2 || croak 'usage: $ftp->delete($filename)'; |
765
|
|
|
|
|
|
|
|
766
|
0
|
|
|
|
|
|
$_[0]->_DELE($_[1]); |
767
|
|
|
|
|
|
|
} |
768
|
|
|
|
|
|
|
|
769
|
|
|
|
|
|
|
|
770
|
0
|
|
|
0
|
1
|
|
sub put { shift->_store_cmd("stor", @_) } |
771
|
0
|
|
|
0
|
1
|
|
sub put_unique { shift->_store_cmd("stou", @_) } |
772
|
0
|
|
|
0
|
1
|
|
sub append { shift->_store_cmd("appe", @_) } |
773
|
|
|
|
|
|
|
|
774
|
|
|
|
|
|
|
|
775
|
0
|
|
|
0
|
1
|
|
sub nlst { shift->_data_cmd("NLST", @_) } |
776
|
0
|
|
|
0
|
1
|
|
sub list { shift->_data_cmd("LIST", @_) } |
777
|
0
|
|
|
0
|
1
|
|
sub retr { shift->_data_cmd("RETR", @_) } |
778
|
0
|
|
|
0
|
1
|
|
sub stor { shift->_data_cmd("STOR", @_) } |
779
|
0
|
|
|
0
|
1
|
|
sub stou { shift->_data_cmd("STOU", @_) } |
780
|
0
|
|
|
0
|
1
|
|
sub appe { shift->_data_cmd("APPE", @_) } |
781
|
|
|
|
|
|
|
|
782
|
|
|
|
|
|
|
|
783
|
|
|
|
|
|
|
sub _store_cmd { |
784
|
0
|
|
|
0
|
|
|
my ($ftp, $cmd, $local, $remote) = @_; |
785
|
0
|
|
|
|
|
|
my ($loc, $sock, $len, $buf); |
786
|
0
|
|
|
|
|
|
local *FD; |
787
|
|
|
|
|
|
|
|
788
|
0
|
|
0
|
|
|
|
my $localfd = ref($local) || ref(\$local) eq "GLOB"; |
789
|
|
|
|
|
|
|
|
790
|
0
|
0
|
0
|
|
|
|
if (!defined($remote) and 'STOU' ne uc($cmd)) { |
791
|
0
|
0
|
|
|
|
|
croak 'Must specify remote filename with stream input' |
792
|
|
|
|
|
|
|
if $localfd; |
793
|
|
|
|
|
|
|
|
794
|
0
|
|
|
|
|
|
require File::Basename; |
795
|
0
|
|
|
|
|
|
$remote = File::Basename::basename($local); |
796
|
|
|
|
|
|
|
} |
797
|
0
|
0
|
|
|
|
|
if (defined ${*$ftp}{'net_ftp_allo'}) { |
|
0
|
|
|
|
|
|
|
798
|
0
|
|
|
|
|
|
delete ${*$ftp}{'net_ftp_allo'}; |
|
0
|
|
|
|
|
|
|
799
|
|
|
|
|
|
|
} |
800
|
|
|
|
|
|
|
else { |
801
|
|
|
|
|
|
|
|
802
|
|
|
|
|
|
|
# if the user hasn't already invoked the alloc method since the last |
803
|
|
|
|
|
|
|
# _store_cmd call, figure out if the local file is a regular file(not |
804
|
|
|
|
|
|
|
# a pipe, or device) and if so get the file size from stat, and send |
805
|
|
|
|
|
|
|
# an ALLO command before sending the STOR, STOU, or APPE command. |
806
|
0
|
0
|
|
|
|
|
my $size = do { local $^W; -f $local && -s _ }; # no ALLO if sending data from a pipe |
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
807
|
0
|
0
|
|
|
|
|
${*$ftp}{'net_ftp_allo'} = $size if $size; |
|
0
|
|
|
|
|
|
|
808
|
|
|
|
|
|
|
} |
809
|
0
|
0
|
0
|
|
|
|
croak("Bad remote filename '$remote'\n") |
810
|
|
|
|
|
|
|
if defined($remote) and $remote =~ /[\r\n]/s; |
811
|
|
|
|
|
|
|
|
812
|
0
|
0
|
|
|
|
|
if ($localfd) { |
813
|
0
|
|
|
|
|
|
$loc = $local; |
814
|
|
|
|
|
|
|
} |
815
|
|
|
|
|
|
|
else { |
816
|
0
|
|
|
|
|
|
$loc = \*FD; |
817
|
|
|
|
|
|
|
|
818
|
0
|
0
|
|
|
|
|
unless (sysopen($loc, $local, O_RDONLY)) { |
819
|
0
|
|
|
|
|
|
carp "Cannot open Local file $local: $!\n"; |
820
|
0
|
|
|
|
|
|
return; |
821
|
|
|
|
|
|
|
} |
822
|
|
|
|
|
|
|
} |
823
|
|
|
|
|
|
|
|
824
|
0
|
0
|
0
|
|
|
|
if ($ftp->type eq 'I' && !binmode($loc)) { |
825
|
0
|
|
|
|
|
|
carp "Cannot binmode Local file $local: $!\n"; |
826
|
0
|
|
|
|
|
|
return; |
827
|
|
|
|
|
|
|
} |
828
|
|
|
|
|
|
|
|
829
|
0
|
|
|
|
|
|
delete ${*$ftp}{'net_ftp_port'}; |
|
0
|
|
|
|
|
|
|
830
|
0
|
|
|
|
|
|
delete ${*$ftp}{'net_ftp_pasv'}; |
|
0
|
|
|
|
|
|
|
831
|
|
|
|
|
|
|
|
832
|
0
|
0
|
|
|
|
|
$sock = $ftp->_data_cmd($cmd, grep { defined } $remote) |
|
0
|
|
|
|
|
|
|
833
|
|
|
|
|
|
|
or return; |
834
|
|
|
|
|
|
|
|
835
|
0
|
0
|
|
|
|
|
$remote = ($ftp->message =~ /\w+\s*:\s*(.*)/)[0] |
836
|
|
|
|
|
|
|
if 'STOU' eq uc $cmd; |
837
|
|
|
|
|
|
|
|
838
|
0
|
|
|
|
|
|
my $blksize = ${*$ftp}{'net_ftp_blksize'}; |
|
0
|
|
|
|
|
|
|
839
|
|
|
|
|
|
|
|
840
|
0
|
|
|
|
|
|
my ($count, $hashh, $hashb, $ref) = (0); |
841
|
|
|
|
|
|
|
|
842
|
|
|
|
|
|
|
($hashh, $hashb) = @$ref |
843
|
0
|
0
|
|
|
|
|
if ($ref = ${*$ftp}{'net_ftp_hash'}); |
|
0
|
|
|
|
|
|
|
844
|
|
|
|
|
|
|
|
845
|
0
|
|
|
|
|
|
while (1) { |
846
|
0
|
0
|
|
|
|
|
last unless $len = read($loc, $buf = "", $blksize); |
847
|
|
|
|
|
|
|
|
848
|
0
|
|
|
|
|
|
if (EBCDIC && $ftp->type ne 'I') { |
849
|
|
|
|
|
|
|
$buf = $ftp->toascii($buf); |
850
|
|
|
|
|
|
|
$len = length($buf); |
851
|
|
|
|
|
|
|
} |
852
|
|
|
|
|
|
|
|
853
|
0
|
0
|
|
|
|
|
if ($hashh) { |
854
|
0
|
|
|
|
|
|
$count += $len; |
855
|
0
|
|
|
|
|
|
print $hashh "#" x (int($count / $hashb)); |
856
|
0
|
|
|
|
|
|
$count %= $hashb; |
857
|
|
|
|
|
|
|
} |
858
|
|
|
|
|
|
|
|
859
|
0
|
|
|
|
|
|
my $wlen; |
860
|
0
|
0
|
0
|
|
|
|
unless (defined($wlen = $sock->write($buf, $len)) && $wlen == $len) { |
861
|
0
|
|
|
|
|
|
$sock->abort; |
862
|
0
|
0
|
|
|
|
|
close($loc) |
863
|
|
|
|
|
|
|
unless $localfd; |
864
|
0
|
0
|
|
|
|
|
print $hashh "\n" if $hashh; |
865
|
0
|
|
|
|
|
|
return; |
866
|
|
|
|
|
|
|
} |
867
|
|
|
|
|
|
|
} |
868
|
|
|
|
|
|
|
|
869
|
0
|
0
|
|
|
|
|
print $hashh "\n" if $hashh; |
870
|
|
|
|
|
|
|
|
871
|
0
|
0
|
|
|
|
|
close($loc) |
872
|
|
|
|
|
|
|
unless $localfd; |
873
|
|
|
|
|
|
|
|
874
|
0
|
0
|
|
|
|
|
$sock->close() |
875
|
|
|
|
|
|
|
or return; |
876
|
|
|
|
|
|
|
|
877
|
0
|
0
|
0
|
|
|
|
if ('STOU' eq uc $cmd and $ftp->message =~ m/unique\s+file\s*name\s*:\s*(.*)\)|"(.*)"/) { |
878
|
0
|
|
|
|
|
|
require File::Basename; |
879
|
0
|
|
|
|
|
|
$remote = File::Basename::basename($+); |
880
|
|
|
|
|
|
|
} |
881
|
|
|
|
|
|
|
|
882
|
0
|
|
|
|
|
|
return $remote; |
883
|
|
|
|
|
|
|
} |
884
|
|
|
|
|
|
|
|
885
|
|
|
|
|
|
|
|
886
|
|
|
|
|
|
|
sub port { |
887
|
0
|
0
|
0
|
0
|
1
|
|
@_ == 1 || @_ == 2 or croak 'usage: $self->port([$port])'; |
888
|
0
|
|
|
|
|
|
return _eprt('PORT',@_); |
889
|
|
|
|
|
|
|
} |
890
|
|
|
|
|
|
|
|
891
|
|
|
|
|
|
|
sub eprt { |
892
|
0
|
0
|
0
|
0
|
1
|
|
@_ == 1 || @_ == 2 or croak 'usage: $self->eprt([$port])'; |
893
|
0
|
|
|
|
|
|
return _eprt('EPRT',@_); |
894
|
|
|
|
|
|
|
} |
895
|
|
|
|
|
|
|
|
896
|
|
|
|
|
|
|
sub _eprt { |
897
|
0
|
|
|
0
|
|
|
my ($cmd,$ftp,$port) = @_; |
898
|
0
|
|
|
|
|
|
delete ${*$ftp}{net_ftp_intern_port}; |
|
0
|
|
|
|
|
|
|
899
|
0
|
0
|
0
|
|
|
|
unless ($port) { |
900
|
0
|
|
|
|
|
|
my $listen = ${*$ftp}{net_ftp_listen} ||= $IOCLASS->new( |
901
|
|
|
|
|
|
|
Listen => 1, |
902
|
|
|
|
|
|
|
Timeout => $ftp->timeout, |
903
|
|
|
|
|
|
|
LocalAddr => $ftp->sockhost, |
904
|
|
|
|
|
|
|
$family_key => $ftp->sockdomain, |
905
|
|
|
|
|
|
|
can_ssl() ? ( |
906
|
0
|
0
|
0
|
|
|
|
%{ ${*$ftp}{net_ftp_tlsargs} }, |
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
907
|
|
|
|
|
|
|
SSL_startHandshake => 0, |
908
|
|
|
|
|
|
|
):(), |
909
|
|
|
|
|
|
|
); |
910
|
0
|
|
|
|
|
|
${*$ftp}{net_ftp_intern_port} = 1; |
|
0
|
|
|
|
|
|
|
911
|
0
|
0
|
|
|
|
|
my $fam = ($listen->sockdomain == AF_INET) ? 1:2; |
912
|
0
|
0
|
0
|
|
|
|
if ( $cmd eq 'EPRT' || $fam == 2 ) { |
913
|
0
|
|
|
|
|
|
$port = "|$fam|".$listen->sockhost."|".$listen->sockport."|"; |
914
|
0
|
|
|
|
|
|
$cmd = 'EPRT'; |
915
|
|
|
|
|
|
|
} else { |
916
|
0
|
|
|
|
|
|
my $p = $listen->sockport; |
917
|
0
|
|
|
|
|
|
$port = join(',',split(m{\.},$listen->sockhost),$p >> 8,$p & 0xff); |
918
|
|
|
|
|
|
|
} |
919
|
|
|
|
|
|
|
} elsif (ref($port) eq 'ARRAY') { |
920
|
|
|
|
|
|
|
$port = join(',',split(m{\.},@$port[0]),@$port[1] >> 8,@$port[1] & 0xff); |
921
|
|
|
|
|
|
|
} |
922
|
0
|
0
|
|
|
|
|
my $ok = $cmd eq 'EPRT' ? $ftp->_EPRT($port) : $ftp->_PORT($port); |
923
|
0
|
0
|
|
|
|
|
${*$ftp}{net_ftp_port} = $port if $ok; |
|
0
|
|
|
|
|
|
|
924
|
0
|
|
|
|
|
|
return $ok; |
925
|
|
|
|
|
|
|
} |
926
|
|
|
|
|
|
|
|
927
|
|
|
|
|
|
|
|
928
|
0
|
|
|
0
|
1
|
|
sub ls { shift->_list_cmd("NLST", @_); } |
929
|
0
|
|
|
0
|
1
|
|
sub dir { shift->_list_cmd("LIST", @_); } |
930
|
|
|
|
|
|
|
|
931
|
|
|
|
|
|
|
|
932
|
|
|
|
|
|
|
sub pasv { |
933
|
0
|
|
|
0
|
1
|
|
my $ftp = shift; |
934
|
0
|
0
|
|
|
|
|
@_ and croak 'usage: $ftp->port()'; |
935
|
0
|
0
|
|
|
|
|
return $ftp->epsv if $ftp->sockdomain != AF_INET; |
936
|
0
|
|
|
|
|
|
delete ${*$ftp}{net_ftp_intern_port}; |
|
0
|
|
|
|
|
|
|
937
|
|
|
|
|
|
|
|
938
|
0
|
0
|
0
|
|
|
|
if ( $ftp->_PASV && |
939
|
|
|
|
|
|
|
$ftp->message =~ m{(\d+,\d+,\d+,\d+),(\d+),(\d+)} ) { |
940
|
0
|
|
|
|
|
|
my $port = 256 * $2 + $3; |
941
|
0
|
|
|
|
|
|
( my $ip = $1 ) =~s{,}{.}g; |
942
|
0
|
|
|
|
|
|
return ${*$ftp}{net_ftp_pasv} = [ $ip,$port ]; |
|
0
|
|
|
|
|
|
|
943
|
|
|
|
|
|
|
} |
944
|
0
|
|
|
|
|
|
return; |
945
|
|
|
|
|
|
|
} |
946
|
|
|
|
|
|
|
|
947
|
|
|
|
|
|
|
sub epsv { |
948
|
0
|
|
|
0
|
1
|
|
my $ftp = shift; |
949
|
0
|
0
|
|
|
|
|
@_ and croak 'usage: $ftp->epsv()'; |
950
|
0
|
|
|
|
|
|
delete ${*$ftp}{net_ftp_intern_port}; |
|
0
|
|
|
|
|
|
|
951
|
|
|
|
|
|
|
|
952
|
|
|
|
|
|
|
$ftp->_EPSV && $ftp->message =~ m{\(([\x33-\x7e])\1\1(\d+)\1\)} |
953
|
0
|
0
|
0
|
|
|
|
? ${*$ftp}{net_ftp_pasv} = [ $ftp->peerhost, $2 ] |
|
0
|
|
|
|
|
|
|
954
|
|
|
|
|
|
|
: undef; |
955
|
|
|
|
|
|
|
} |
956
|
|
|
|
|
|
|
|
957
|
|
|
|
|
|
|
|
958
|
|
|
|
|
|
|
sub unique_name { |
959
|
0
|
|
|
0
|
1
|
|
my $ftp = shift; |
960
|
0
|
0
|
|
|
|
|
${*$ftp}{'net_ftp_unique'} || undef; |
|
0
|
|
|
|
|
|
|
961
|
|
|
|
|
|
|
} |
962
|
|
|
|
|
|
|
|
963
|
|
|
|
|
|
|
|
964
|
|
|
|
|
|
|
sub supported { |
965
|
0
|
0
|
|
0
|
1
|
|
@_ == 2 or croak 'usage: $ftp->supported($cmd)'; |
966
|
0
|
|
|
|
|
|
my $ftp = shift; |
967
|
0
|
|
|
|
|
|
my $cmd = uc shift; |
968
|
0
|
|
0
|
|
|
|
my $hash = ${*$ftp}{'net_ftp_supported'} ||= {}; |
|
0
|
|
|
|
|
|
|
969
|
|
|
|
|
|
|
|
970
|
|
|
|
|
|
|
return $hash->{$cmd} |
971
|
0
|
0
|
|
|
|
|
if exists $hash->{$cmd}; |
972
|
|
|
|
|
|
|
|
973
|
0
|
0
|
|
|
|
|
return $hash->{$cmd} = 1 |
974
|
|
|
|
|
|
|
if $ftp->feature($cmd); |
975
|
|
|
|
|
|
|
|
976
|
0
|
0
|
|
|
|
|
return $hash->{$cmd} = 0 |
977
|
|
|
|
|
|
|
unless $ftp->_HELP($cmd); |
978
|
|
|
|
|
|
|
|
979
|
0
|
|
|
|
|
|
my $text = $ftp->message; |
980
|
0
|
0
|
|
|
|
|
if ($text =~ /following.+commands/i) { |
981
|
0
|
|
|
|
|
|
$text =~ s/^.*\n//; |
982
|
0
|
|
|
|
|
|
while ($text =~ /(\*?)(\w+)(\*?)/sg) { |
983
|
0
|
|
|
|
|
|
$hash->{"\U$2"} = !length("$1$3"); |
984
|
|
|
|
|
|
|
} |
985
|
|
|
|
|
|
|
} |
986
|
|
|
|
|
|
|
else { |
987
|
0
|
|
|
|
|
|
$hash->{$cmd} = $text !~ /unimplemented/i; |
988
|
|
|
|
|
|
|
} |
989
|
|
|
|
|
|
|
|
990
|
0
|
|
0
|
|
|
|
$hash->{$cmd} ||= 0; |
991
|
|
|
|
|
|
|
} |
992
|
|
|
|
|
|
|
|
993
|
|
|
|
|
|
|
## |
994
|
|
|
|
|
|
|
## Deprecated methods |
995
|
|
|
|
|
|
|
## |
996
|
|
|
|
|
|
|
|
997
|
|
|
|
|
|
|
|
998
|
|
|
|
|
|
|
sub lsl { |
999
|
0
|
0
|
|
0
|
0
|
|
carp "Use of Net::FTP::lsl deprecated, use 'dir'" |
1000
|
|
|
|
|
|
|
if $^W; |
1001
|
0
|
|
|
|
|
|
goto &dir; |
1002
|
|
|
|
|
|
|
} |
1003
|
|
|
|
|
|
|
|
1004
|
|
|
|
|
|
|
|
1005
|
|
|
|
|
|
|
sub authorise { |
1006
|
0
|
0
|
|
0
|
0
|
|
carp "Use of Net::FTP::authorise deprecated, use 'authorize'" |
1007
|
|
|
|
|
|
|
if $^W; |
1008
|
0
|
|
|
|
|
|
goto &authorize; |
1009
|
|
|
|
|
|
|
} |
1010
|
|
|
|
|
|
|
|
1011
|
|
|
|
|
|
|
|
1012
|
|
|
|
|
|
|
## |
1013
|
|
|
|
|
|
|
## Private methods |
1014
|
|
|
|
|
|
|
## |
1015
|
|
|
|
|
|
|
|
1016
|
|
|
|
|
|
|
|
1017
|
|
|
|
|
|
|
sub _extract_path { |
1018
|
0
|
|
|
0
|
|
|
my ($ftp, $path) = @_; |
1019
|
|
|
|
|
|
|
|
1020
|
|
|
|
|
|
|
# This tries to work both with and without the quote doubling |
1021
|
|
|
|
|
|
|
# convention (RFC 959 requires it, but the first 3 servers I checked |
1022
|
|
|
|
|
|
|
# didn't implement it). It will fail on a server which uses a quote in |
1023
|
|
|
|
|
|
|
# the message which isn't a part of or surrounding the path. |
1024
|
0
|
0
|
0
|
|
|
|
$ftp->ok |
1025
|
|
|
|
|
|
|
&& $ftp->message =~ /(?:^|\s)\"(.*)\"(?:$|\s)/ |
1026
|
|
|
|
|
|
|
&& ($path = $1) =~ s/\"\"/\"/g; |
1027
|
|
|
|
|
|
|
|
1028
|
0
|
|
|
|
|
|
$path; |
1029
|
|
|
|
|
|
|
} |
1030
|
|
|
|
|
|
|
|
1031
|
|
|
|
|
|
|
## |
1032
|
|
|
|
|
|
|
## Communication methods |
1033
|
|
|
|
|
|
|
## |
1034
|
|
|
|
|
|
|
|
1035
|
|
|
|
|
|
|
|
1036
|
|
|
|
|
|
|
sub _dataconn { |
1037
|
0
|
|
|
0
|
|
|
my $ftp = shift; |
1038
|
0
|
|
|
|
|
|
my $pkg = "Net::FTP::" . $ftp->type; |
1039
|
0
|
0
|
|
|
|
|
eval "require " . $pkg ## no critic (BuiltinFunctions::ProhibitStringyEval) |
1040
|
|
|
|
|
|
|
or croak("cannot load $pkg required for type ".$ftp->type); |
1041
|
0
|
|
|
|
|
|
$pkg =~ s/ /_/g; |
1042
|
0
|
|
|
|
|
|
delete ${*$ftp}{net_ftp_dataconn}; |
|
0
|
|
|
|
|
|
|
1043
|
|
|
|
|
|
|
|
1044
|
0
|
|
|
|
|
|
my $conn; |
1045
|
0
|
|
|
|
|
|
my $pasv = ${*$ftp}{net_ftp_pasv}; |
|
0
|
|
|
|
|
|
|
1046
|
0
|
0
|
|
|
|
|
if ($pasv) { |
|
|
0
|
|
|
|
|
|
1047
|
|
|
|
|
|
|
$conn = $pkg->new( |
1048
|
|
|
|
|
|
|
PeerAddr => $pasv->[0], |
1049
|
|
|
|
|
|
|
PeerPort => $pasv->[1], |
1050
|
0
|
|
|
|
|
|
LocalAddr => ${*$ftp}{net_ftp_localaddr}, |
1051
|
0
|
|
|
|
|
|
$family_key => ${*$ftp}{net_ftp_domain}, |
1052
|
|
|
|
|
|
|
Timeout => $ftp->timeout, |
1053
|
|
|
|
|
|
|
can_ssl() ? ( |
1054
|
|
|
|
|
|
|
SSL_startHandshake => 0, |
1055
|
0
|
0
|
|
|
|
|
%{${*$ftp}{net_ftp_tlsargs}}, |
|
0
|
0
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
1056
|
|
|
|
|
|
|
):(), |
1057
|
|
|
|
|
|
|
) or return; |
1058
|
0
|
|
|
|
|
|
} elsif (my $listen = delete ${*$ftp}{net_ftp_listen}) { |
1059
|
0
|
0
|
|
|
|
|
$conn = $listen->accept($pkg) or return; |
1060
|
0
|
|
|
|
|
|
$conn->timeout($ftp->timeout); |
1061
|
0
|
|
|
|
|
|
close($listen); |
1062
|
|
|
|
|
|
|
} else { |
1063
|
0
|
|
|
|
|
|
croak("no listener in active mode"); |
1064
|
|
|
|
|
|
|
} |
1065
|
|
|
|
|
|
|
|
1066
|
0
|
0
|
0
|
|
|
|
if (( ${*$ftp}{net_ftp_tlsprot} || '') eq 'P') { |
1067
|
0
|
0
|
|
|
|
|
if ($conn->connect_SSL) { |
1068
|
|
|
|
|
|
|
# SSL handshake ok |
1069
|
|
|
|
|
|
|
} else { |
1070
|
0
|
|
|
|
|
|
carp("failed to ssl upgrade dataconn: $IO::Socket::SSL::SSL_ERROR"); |
1071
|
0
|
|
|
|
|
|
return; |
1072
|
|
|
|
|
|
|
} |
1073
|
|
|
|
|
|
|
} |
1074
|
|
|
|
|
|
|
|
1075
|
0
|
|
|
|
|
|
${*$ftp}{net_ftp_dataconn} = $conn; |
|
0
|
|
|
|
|
|
|
1076
|
0
|
|
|
|
|
|
${*$conn} = ""; |
|
0
|
|
|
|
|
|
|
1077
|
0
|
|
|
|
|
|
${*$conn}{net_ftp_cmd} = $ftp; |
|
0
|
|
|
|
|
|
|
1078
|
0
|
|
|
|
|
|
${*$conn}{net_ftp_blksize} = ${*$ftp}{net_ftp_blksize}; |
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
1079
|
0
|
|
|
|
|
|
return $conn; |
1080
|
|
|
|
|
|
|
} |
1081
|
|
|
|
|
|
|
|
1082
|
|
|
|
|
|
|
|
1083
|
|
|
|
|
|
|
sub _list_cmd { |
1084
|
0
|
|
|
0
|
|
|
my $ftp = shift; |
1085
|
0
|
|
|
|
|
|
my $cmd = uc shift; |
1086
|
|
|
|
|
|
|
|
1087
|
0
|
|
|
|
|
|
delete ${*$ftp}{'net_ftp_port'}; |
|
0
|
|
|
|
|
|
|
1088
|
0
|
|
|
|
|
|
delete ${*$ftp}{'net_ftp_pasv'}; |
|
0
|
|
|
|
|
|
|
1089
|
|
|
|
|
|
|
|
1090
|
0
|
|
|
|
|
|
my $data = $ftp->_data_cmd($cmd, @_); |
1091
|
|
|
|
|
|
|
|
1092
|
|
|
|
|
|
|
return |
1093
|
0
|
0
|
|
|
|
|
unless (defined $data); |
1094
|
|
|
|
|
|
|
|
1095
|
0
|
|
|
|
|
|
require Net::FTP::A; |
1096
|
0
|
|
|
|
|
|
bless $data, "Net::FTP::A"; # Force ASCII mode |
1097
|
|
|
|
|
|
|
|
1098
|
0
|
|
|
|
|
|
my $databuf = ''; |
1099
|
0
|
|
|
|
|
|
my $buf = ''; |
1100
|
0
|
|
|
|
|
|
my $blksize = ${*$ftp}{'net_ftp_blksize'}; |
|
0
|
|
|
|
|
|
|
1101
|
|
|
|
|
|
|
|
1102
|
0
|
|
|
|
|
|
while ($data->read($databuf, $blksize)) { |
1103
|
0
|
|
|
|
|
|
$buf .= $databuf; |
1104
|
|
|
|
|
|
|
} |
1105
|
|
|
|
|
|
|
|
1106
|
0
|
|
|
|
|
|
my $list = [split(/\n/, $buf)]; |
1107
|
|
|
|
|
|
|
|
1108
|
0
|
|
|
|
|
|
$data->close(); |
1109
|
|
|
|
|
|
|
|
1110
|
0
|
|
|
|
|
|
if (EBCDIC) { |
1111
|
|
|
|
|
|
|
for (@$list) { $_ = $ftp->toebcdic($_) } |
1112
|
|
|
|
|
|
|
} |
1113
|
|
|
|
|
|
|
|
1114
|
|
|
|
|
|
|
wantarray |
1115
|
0
|
0
|
|
|
|
|
? @{$list} |
|
0
|
|
|
|
|
|
|
1116
|
|
|
|
|
|
|
: $list; |
1117
|
|
|
|
|
|
|
} |
1118
|
|
|
|
|
|
|
|
1119
|
|
|
|
|
|
|
|
1120
|
|
|
|
|
|
|
sub _data_cmd { |
1121
|
0
|
|
|
0
|
|
|
my $ftp = shift; |
1122
|
0
|
|
|
|
|
|
my $cmd = uc shift; |
1123
|
0
|
|
|
|
|
|
my $ok = 1; |
1124
|
0
|
|
0
|
|
|
|
my $where = delete ${*$ftp}{'net_ftp_rest'} || 0; |
1125
|
0
|
|
|
|
|
|
my $arg; |
1126
|
|
|
|
|
|
|
|
1127
|
0
|
|
|
|
|
|
for my $arg (@_) { |
1128
|
0
|
0
|
|
|
|
|
croak("Bad argument '$arg'\n") |
1129
|
|
|
|
|
|
|
if $arg =~ /[\r\n]/s; |
1130
|
|
|
|
|
|
|
} |
1131
|
|
|
|
|
|
|
|
1132
|
0
|
0
|
0
|
|
|
|
if ( ${*$ftp}{'net_ftp_passive'} |
|
0
|
|
0
|
|
|
|
|
1133
|
0
|
|
|
|
|
|
&& !defined ${*$ftp}{'net_ftp_pasv'} |
1134
|
0
|
|
|
|
|
|
&& !defined ${*$ftp}{'net_ftp_port'}) |
1135
|
|
|
|
|
|
|
{ |
1136
|
0
|
0
|
|
|
|
|
return unless defined $ftp->pasv; |
1137
|
|
|
|
|
|
|
|
1138
|
0
|
0
|
0
|
|
|
|
if ($where and !$ftp->_REST($where)) { |
1139
|
0
|
|
|
|
|
|
my ($status, $message) = ($ftp->status, $ftp->message); |
1140
|
0
|
|
|
|
|
|
$ftp->abort; |
1141
|
0
|
|
|
|
|
|
$ftp->set_status($status, $message); |
1142
|
0
|
|
|
|
|
|
return; |
1143
|
|
|
|
|
|
|
} |
1144
|
|
|
|
|
|
|
|
1145
|
|
|
|
|
|
|
# first send command, then open data connection |
1146
|
|
|
|
|
|
|
# otherwise the peer might not do a full accept (with SSL |
1147
|
|
|
|
|
|
|
# handshake if PROT P) |
1148
|
0
|
|
|
|
|
|
$ftp->command($cmd, @_); |
1149
|
0
|
|
|
|
|
|
my $data = $ftp->_dataconn(); |
1150
|
0
|
0
|
|
|
|
|
if (CMD_INFO == $ftp->response()) { |
1151
|
0
|
0
|
0
|
|
|
|
$data->reading |
1152
|
|
|
|
|
|
|
if $data && $cmd =~ /RETR|LIST|NLST|MLSD/; |
1153
|
0
|
|
|
|
|
|
return $data; |
1154
|
|
|
|
|
|
|
} |
1155
|
0
|
0
|
|
|
|
|
$data->_close if $data; |
1156
|
|
|
|
|
|
|
|
1157
|
0
|
|
|
|
|
|
return; |
1158
|
|
|
|
|
|
|
} |
1159
|
|
|
|
|
|
|
|
1160
|
|
|
|
|
|
|
$ok = $ftp->port |
1161
|
0
|
|
|
|
|
|
unless (defined ${*$ftp}{'net_ftp_port'} |
1162
|
0
|
0
|
0
|
|
|
|
|| defined ${*$ftp}{'net_ftp_pasv'}); |
|
0
|
|
|
|
|
|
|
1163
|
|
|
|
|
|
|
|
1164
|
0
|
0
|
0
|
|
|
|
$ok = $ftp->_REST($where) |
1165
|
|
|
|
|
|
|
if $ok && $where; |
1166
|
|
|
|
|
|
|
|
1167
|
|
|
|
|
|
|
return |
1168
|
0
|
0
|
|
|
|
|
unless $ok; |
1169
|
|
|
|
|
|
|
|
1170
|
0
|
0
|
0
|
|
|
|
if ($cmd =~ /(STOR|APPE|STOU)/ and exists ${*$ftp}{net_ftp_allo} and |
|
0
|
|
0
|
|
|
|
|
1171
|
|
|
|
|
|
|
$ftp->supported("ALLO")) |
1172
|
|
|
|
|
|
|
{ |
1173
|
0
|
|
|
|
|
|
$ftp->_ALLO(delete ${*$ftp}{net_ftp_allo}) |
1174
|
0
|
0
|
|
|
|
|
or return; |
1175
|
|
|
|
|
|
|
} |
1176
|
|
|
|
|
|
|
|
1177
|
0
|
|
|
|
|
|
$ftp->command($cmd, @_); |
1178
|
|
|
|
|
|
|
|
1179
|
|
|
|
|
|
|
return 1 |
1180
|
0
|
0
|
|
|
|
|
if (defined ${*$ftp}{'net_ftp_pasv'}); |
|
0
|
|
|
|
|
|
|
1181
|
|
|
|
|
|
|
|
1182
|
0
|
|
|
|
|
|
$ok = CMD_INFO == $ftp->response(); |
1183
|
|
|
|
|
|
|
|
1184
|
|
|
|
|
|
|
return $ok |
1185
|
0
|
0
|
|
|
|
|
unless exists ${*$ftp}{'net_ftp_intern_port'}; |
|
0
|
|
|
|
|
|
|
1186
|
|
|
|
|
|
|
|
1187
|
0
|
0
|
|
|
|
|
if ($ok) { |
1188
|
0
|
|
|
|
|
|
my $data = $ftp->_dataconn(); |
1189
|
|
|
|
|
|
|
|
1190
|
0
|
0
|
0
|
|
|
|
$data->reading |
1191
|
|
|
|
|
|
|
if $data && $cmd =~ /RETR|LIST|NLST|MLSD/; |
1192
|
|
|
|
|
|
|
|
1193
|
0
|
|
|
|
|
|
return $data; |
1194
|
|
|
|
|
|
|
} |
1195
|
|
|
|
|
|
|
|
1196
|
|
|
|
|
|
|
|
1197
|
0
|
|
|
|
|
|
close(delete ${*$ftp}{'net_ftp_listen'}); |
|
0
|
|
|
|
|
|
|
1198
|
|
|
|
|
|
|
|
1199
|
0
|
|
|
|
|
|
return; |
1200
|
|
|
|
|
|
|
} |
1201
|
|
|
|
|
|
|
|
1202
|
|
|
|
|
|
|
## |
1203
|
|
|
|
|
|
|
## Over-ride methods (Net::Cmd) |
1204
|
|
|
|
|
|
|
## |
1205
|
|
|
|
|
|
|
|
1206
|
|
|
|
|
|
|
|
1207
|
0
|
0
|
|
0
|
1
|
|
sub debug_text { $_[2] =~ /^(pass|resp|acct)/i ? "$1 ....\n" : $_[2]; } |
1208
|
|
|
|
|
|
|
|
1209
|
|
|
|
|
|
|
|
1210
|
|
|
|
|
|
|
sub command { |
1211
|
0
|
|
|
0
|
1
|
|
my $ftp = shift; |
1212
|
|
|
|
|
|
|
|
1213
|
0
|
|
|
|
|
|
delete ${*$ftp}{'net_ftp_port'}; |
|
0
|
|
|
|
|
|
|
1214
|
0
|
|
|
|
|
|
$ftp->SUPER::command(@_); |
1215
|
|
|
|
|
|
|
} |
1216
|
|
|
|
|
|
|
|
1217
|
|
|
|
|
|
|
|
1218
|
|
|
|
|
|
|
sub response { |
1219
|
0
|
|
|
0
|
1
|
|
my $ftp = shift; |
1220
|
0
|
|
0
|
|
|
|
my $code = $ftp->SUPER::response() || 5; # assume 500 if undef |
1221
|
|
|
|
|
|
|
|
1222
|
0
|
0
|
0
|
|
|
|
delete ${*$ftp}{'net_ftp_pasv'} |
|
0
|
|
|
|
|
|
|
1223
|
|
|
|
|
|
|
if ($code != CMD_MORE && $code != CMD_INFO); |
1224
|
|
|
|
|
|
|
|
1225
|
0
|
|
|
|
|
|
$code; |
1226
|
|
|
|
|
|
|
} |
1227
|
|
|
|
|
|
|
|
1228
|
|
|
|
|
|
|
|
1229
|
|
|
|
|
|
|
sub parse_response { |
1230
|
0
|
0
|
|
0
|
1
|
|
return ($1, $2 eq "-") |
1231
|
|
|
|
|
|
|
if $_[1] =~ s/^(\d\d\d)([- ]?)//o; |
1232
|
|
|
|
|
|
|
|
1233
|
0
|
|
|
|
|
|
my $ftp = shift; |
1234
|
|
|
|
|
|
|
|
1235
|
|
|
|
|
|
|
# Darn MS FTP server is a load of CRAP !!!! |
1236
|
|
|
|
|
|
|
# Expect to see undef here. |
1237
|
|
|
|
|
|
|
return () |
1238
|
0
|
0
|
0
|
|
|
|
unless 0 + (${*$ftp}{'net_cmd_code'} || 0); |
1239
|
|
|
|
|
|
|
|
1240
|
0
|
|
|
|
|
|
(${*$ftp}{'net_cmd_code'}, 1); |
|
0
|
|
|
|
|
|
|
1241
|
|
|
|
|
|
|
} |
1242
|
|
|
|
|
|
|
|
1243
|
|
|
|
|
|
|
## |
1244
|
|
|
|
|
|
|
## Allow 2 servers to talk directly |
1245
|
|
|
|
|
|
|
## |
1246
|
|
|
|
|
|
|
|
1247
|
|
|
|
|
|
|
|
1248
|
|
|
|
|
|
|
sub pasv_xfer_unique { |
1249
|
0
|
|
|
0
|
1
|
|
my ($sftp, $sfile, $dftp, $dfile) = @_; |
1250
|
0
|
|
|
|
|
|
$sftp->pasv_xfer($sfile, $dftp, $dfile, 1); |
1251
|
|
|
|
|
|
|
} |
1252
|
|
|
|
|
|
|
|
1253
|
|
|
|
|
|
|
|
1254
|
|
|
|
|
|
|
sub pasv_xfer { |
1255
|
0
|
|
|
0
|
1
|
|
my ($sftp, $sfile, $dftp, $dfile, $unique) = @_; |
1256
|
|
|
|
|
|
|
|
1257
|
0
|
0
|
|
|
|
|
($dfile = $sfile) =~ s#.*/## |
1258
|
|
|
|
|
|
|
unless (defined $dfile); |
1259
|
|
|
|
|
|
|
|
1260
|
0
|
0
|
|
|
|
|
my $port = $sftp->pasv |
1261
|
|
|
|
|
|
|
or return; |
1262
|
|
|
|
|
|
|
|
1263
|
0
|
0
|
|
|
|
|
$dftp->port($port) |
1264
|
|
|
|
|
|
|
or return; |
1265
|
|
|
|
|
|
|
|
1266
|
|
|
|
|
|
|
return |
1267
|
0
|
0
|
|
|
|
|
unless ($unique ? $dftp->stou($dfile) : $dftp->stor($dfile)); |
|
|
0
|
|
|
|
|
|
1268
|
|
|
|
|
|
|
|
1269
|
0
|
0
|
0
|
|
|
|
unless ($sftp->retr($sfile) && $sftp->response == CMD_INFO) { |
1270
|
0
|
|
|
|
|
|
$sftp->retr($sfile); |
1271
|
0
|
|
|
|
|
|
$dftp->abort; |
1272
|
0
|
|
|
|
|
|
$dftp->response(); |
1273
|
0
|
|
|
|
|
|
return; |
1274
|
|
|
|
|
|
|
} |
1275
|
|
|
|
|
|
|
|
1276
|
0
|
|
|
|
|
|
$dftp->pasv_wait($sftp); |
1277
|
|
|
|
|
|
|
} |
1278
|
|
|
|
|
|
|
|
1279
|
|
|
|
|
|
|
|
1280
|
|
|
|
|
|
|
sub pasv_wait { |
1281
|
0
|
0
|
|
0
|
1
|
|
@_ == 2 or croak 'usage: $ftp->pasv_wait($non_pasv_server)'; |
1282
|
|
|
|
|
|
|
|
1283
|
0
|
|
|
|
|
|
my ($ftp, $non_pasv_server) = @_; |
1284
|
0
|
|
|
|
|
|
my ($file, $rin, $rout); |
1285
|
|
|
|
|
|
|
|
1286
|
0
|
|
|
|
|
|
vec($rin = '', fileno($ftp), 1) = 1; |
1287
|
0
|
|
|
|
|
|
select($rout = $rin, undef, undef, undef); |
1288
|
|
|
|
|
|
|
|
1289
|
0
|
|
|
|
|
|
my $dres = $ftp->response(); |
1290
|
0
|
|
|
|
|
|
my $sres = $non_pasv_server->response(); |
1291
|
|
|
|
|
|
|
|
1292
|
|
|
|
|
|
|
return |
1293
|
0
|
0
|
0
|
|
|
|
unless $dres == CMD_OK && $sres == CMD_OK; |
1294
|
|
|
|
|
|
|
|
1295
|
|
|
|
|
|
|
return |
1296
|
0
|
0
|
0
|
|
|
|
unless $ftp->ok() && $non_pasv_server->ok(); |
1297
|
|
|
|
|
|
|
|
1298
|
0
|
0
|
|
|
|
|
return $1 |
1299
|
|
|
|
|
|
|
if $ftp->message =~ /unique file name:\s*(\S*)\s*\)/; |
1300
|
|
|
|
|
|
|
|
1301
|
0
|
0
|
|
|
|
|
return $1 |
1302
|
|
|
|
|
|
|
if $non_pasv_server->message =~ /unique file name:\s*(\S*)\s*\)/; |
1303
|
|
|
|
|
|
|
|
1304
|
0
|
|
|
|
|
|
return 1; |
1305
|
|
|
|
|
|
|
} |
1306
|
|
|
|
|
|
|
|
1307
|
|
|
|
|
|
|
|
1308
|
|
|
|
|
|
|
sub feature { |
1309
|
0
|
0
|
|
0
|
1
|
|
@_ == 2 or croak 'usage: $ftp->feature($name)'; |
1310
|
0
|
|
|
|
|
|
my ($ftp, $name) = @_; |
1311
|
|
|
|
|
|
|
|
1312
|
0
|
|
0
|
|
|
|
my $feature = ${*$ftp}{net_ftp_feature} ||= do { |
|
0
|
|
|
|
|
|
|
1313
|
0
|
|
|
|
|
|
my @feat; |
1314
|
|
|
|
|
|
|
|
1315
|
|
|
|
|
|
|
# Example response |
1316
|
|
|
|
|
|
|
# 211-Features: |
1317
|
|
|
|
|
|
|
# MDTM |
1318
|
|
|
|
|
|
|
# REST STREAM |
1319
|
|
|
|
|
|
|
# SIZE |
1320
|
|
|
|
|
|
|
# 211 End |
1321
|
|
|
|
|
|
|
|
1322
|
0
|
0
|
|
|
|
|
@feat = map { /^\s+(.*\S)/ } $ftp->message |
|
0
|
|
|
|
|
|
|
1323
|
|
|
|
|
|
|
if $ftp->_FEAT; |
1324
|
|
|
|
|
|
|
|
1325
|
0
|
|
|
|
|
|
\@feat; |
1326
|
|
|
|
|
|
|
}; |
1327
|
|
|
|
|
|
|
|
1328
|
0
|
|
|
|
|
|
return grep { /^\Q$name\E\b/i } @$feature; |
|
0
|
|
|
|
|
|
|
1329
|
|
|
|
|
|
|
} |
1330
|
|
|
|
|
|
|
|
1331
|
|
|
|
|
|
|
|
1332
|
0
|
|
|
0
|
0
|
|
sub cmd { shift->command(@_)->response() } |
1333
|
|
|
|
|
|
|
|
1334
|
|
|
|
|
|
|
######################################## |
1335
|
|
|
|
|
|
|
# |
1336
|
|
|
|
|
|
|
# RFC959 + RFC2428 + RFC4217 commands |
1337
|
|
|
|
|
|
|
# |
1338
|
|
|
|
|
|
|
|
1339
|
|
|
|
|
|
|
|
1340
|
0
|
|
|
0
|
|
|
sub _ABOR { shift->command("ABOR")->response() == CMD_OK } |
1341
|
0
|
|
|
0
|
|
|
sub _ALLO { shift->command("ALLO", @_)->response() == CMD_OK } |
1342
|
0
|
|
|
0
|
|
|
sub _CDUP { shift->command("CDUP")->response() == CMD_OK } |
1343
|
0
|
|
|
0
|
|
|
sub _NOOP { shift->command("NOOP")->response() == CMD_OK } |
1344
|
0
|
|
|
0
|
|
|
sub _PASV { shift->command("PASV")->response() == CMD_OK } |
1345
|
0
|
|
|
0
|
|
|
sub _QUIT { shift->command("QUIT")->response() == CMD_OK } |
1346
|
0
|
|
|
0
|
|
|
sub _DELE { shift->command("DELE", @_)->response() == CMD_OK } |
1347
|
0
|
|
|
0
|
|
|
sub _CWD { shift->command("CWD", @_)->response() == CMD_OK } |
1348
|
0
|
|
|
0
|
|
|
sub _PORT { shift->command("PORT", @_)->response() == CMD_OK } |
1349
|
0
|
|
|
0
|
|
|
sub _RMD { shift->command("RMD", @_)->response() == CMD_OK } |
1350
|
0
|
|
|
0
|
|
|
sub _MKD { shift->command("MKD", @_)->response() == CMD_OK } |
1351
|
0
|
|
|
0
|
|
|
sub _PWD { shift->command("PWD", @_)->response() == CMD_OK } |
1352
|
0
|
|
|
0
|
|
|
sub _TYPE { shift->command("TYPE", @_)->response() == CMD_OK } |
1353
|
0
|
|
|
0
|
|
|
sub _RNTO { shift->command("RNTO", @_)->response() == CMD_OK } |
1354
|
0
|
|
|
0
|
|
|
sub _RESP { shift->command("RESP", @_)->response() == CMD_OK } |
1355
|
0
|
|
|
0
|
|
|
sub _MDTM { shift->command("MDTM", @_)->response() == CMD_OK } |
1356
|
0
|
|
|
0
|
|
|
sub _SIZE { shift->command("SIZE", @_)->response() == CMD_OK } |
1357
|
0
|
|
|
0
|
|
|
sub _HELP { shift->command("HELP", @_)->response() == CMD_OK } |
1358
|
0
|
|
|
0
|
|
|
sub _STAT { shift->command("STAT", @_)->response() == CMD_OK } |
1359
|
0
|
|
|
0
|
|
|
sub _FEAT { shift->command("FEAT", @_)->response() == CMD_OK } |
1360
|
0
|
|
|
0
|
|
|
sub _PBSZ { shift->command("PBSZ", @_)->response() == CMD_OK } |
1361
|
0
|
|
|
0
|
|
|
sub _PROT { shift->command("PROT", @_)->response() == CMD_OK } |
1362
|
0
|
|
|
0
|
|
|
sub _CCC { shift->command("CCC", @_)->response() == CMD_OK } |
1363
|
0
|
|
|
0
|
|
|
sub _EPRT { shift->command("EPRT", @_)->response() == CMD_OK } |
1364
|
0
|
|
|
0
|
|
|
sub _EPSV { shift->command("EPSV", @_)->response() == CMD_OK } |
1365
|
0
|
|
|
0
|
|
|
sub _APPE { shift->command("APPE", @_)->response() == CMD_INFO } |
1366
|
0
|
|
|
0
|
|
|
sub _LIST { shift->command("LIST", @_)->response() == CMD_INFO } |
1367
|
0
|
|
|
0
|
|
|
sub _NLST { shift->command("NLST", @_)->response() == CMD_INFO } |
1368
|
0
|
|
|
0
|
|
|
sub _RETR { shift->command("RETR", @_)->response() == CMD_INFO } |
1369
|
0
|
|
|
0
|
|
|
sub _STOR { shift->command("STOR", @_)->response() == CMD_INFO } |
1370
|
0
|
|
|
0
|
|
|
sub _STOU { shift->command("STOU", @_)->response() == CMD_INFO } |
1371
|
0
|
|
|
0
|
|
|
sub _RNFR { shift->command("RNFR", @_)->response() == CMD_MORE } |
1372
|
0
|
|
|
0
|
|
|
sub _REST { shift->command("REST", @_)->response() == CMD_MORE } |
1373
|
0
|
|
|
0
|
|
|
sub _PASS { shift->command("PASS", @_)->response() } |
1374
|
0
|
|
|
0
|
|
|
sub _ACCT { shift->command("ACCT", @_)->response() } |
1375
|
0
|
|
|
0
|
|
|
sub _AUTH { shift->command("AUTH", @_)->response() } |
1376
|
|
|
|
|
|
|
|
1377
|
|
|
|
|
|
|
|
1378
|
|
|
|
|
|
|
sub _USER { |
1379
|
0
|
|
|
0
|
|
|
my $ftp = shift; |
1380
|
0
|
|
|
|
|
|
my $ok = $ftp->command("USER", @_)->response(); |
1381
|
|
|
|
|
|
|
|
1382
|
|
|
|
|
|
|
# A certain brain dead firewall :-) |
1383
|
0
|
0
|
0
|
|
|
|
$ok = $ftp->command("user", @_)->response() |
1384
|
|
|
|
|
|
|
unless $ok == CMD_MORE or $ok == CMD_OK; |
1385
|
|
|
|
|
|
|
|
1386
|
0
|
|
|
|
|
|
$ok; |
1387
|
|
|
|
|
|
|
} |
1388
|
|
|
|
|
|
|
|
1389
|
|
|
|
|
|
|
|
1390
|
0
|
|
|
0
|
|
|
sub _SMNT { shift->unsupported(@_) } |
1391
|
0
|
|
|
0
|
|
|
sub _MODE { shift->unsupported(@_) } |
1392
|
0
|
|
|
0
|
|
|
sub _SYST { shift->unsupported(@_) } |
1393
|
0
|
|
|
0
|
|
|
sub _STRU { shift->unsupported(@_) } |
1394
|
0
|
|
|
0
|
|
|
sub _REIN { shift->unsupported(@_) } |
1395
|
|
|
|
|
|
|
|
1396
|
|
|
|
|
|
|
|
1397
|
|
|
|
|
|
|
1; |
1398
|
|
|
|
|
|
|
|
1399
|
|
|
|
|
|
|
__END__ |