| line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
|
1
|
|
|
|
|
|
|
# You may distribute under the terms of either the GNU General Public License |
|
2
|
|
|
|
|
|
|
# or the Artistic License (the same terms as Perl itself) |
|
3
|
|
|
|
|
|
|
# |
|
4
|
|
|
|
|
|
|
# (C) Paul Evans, 2008-2013 -- leonerd@leonerd.org.uk |
|
5
|
|
|
|
|
|
|
|
|
6
|
|
|
|
|
|
|
package Net::Async::FTP; |
|
7
|
|
|
|
|
|
|
|
|
8
|
8
|
|
|
8
|
|
1059673
|
use strict; |
|
|
8
|
|
|
|
|
19
|
|
|
|
8
|
|
|
|
|
479
|
|
|
9
|
8
|
|
|
8
|
|
48
|
use warnings; |
|
|
8
|
|
|
|
|
14
|
|
|
|
8
|
|
|
|
|
273
|
|
|
10
|
8
|
|
|
8
|
|
52
|
use base qw( IO::Async::Stream ); |
|
|
8
|
|
|
|
|
15
|
|
|
|
8
|
|
|
|
|
2027
|
|
|
11
|
|
|
|
|
|
|
IO::Async::Stream->VERSION( '0.59' ); |
|
12
|
|
|
|
|
|
|
|
|
13
|
8
|
|
|
8
|
|
120919
|
use Carp; |
|
|
8
|
|
|
|
|
17
|
|
|
|
8
|
|
|
|
|
872
|
|
|
14
|
|
|
|
|
|
|
|
|
15
|
|
|
|
|
|
|
our $VERSION = '0.08'; |
|
16
|
|
|
|
|
|
|
|
|
17
|
8
|
|
|
8
|
|
43
|
use Socket qw( AF_INET SOCK_STREAM inet_aton pack_sockaddr_in ); |
|
|
8
|
|
|
|
|
15
|
|
|
|
8
|
|
|
|
|
42193
|
|
|
18
|
|
|
|
|
|
|
|
|
19
|
|
|
|
|
|
|
my $CRLF = "\x0d\x0a"; |
|
20
|
|
|
|
|
|
|
|
|
21
|
|
|
|
|
|
|
=head1 NAME |
|
22
|
|
|
|
|
|
|
|
|
23
|
|
|
|
|
|
|
C - use FTP with C |
|
24
|
|
|
|
|
|
|
|
|
25
|
|
|
|
|
|
|
=head1 SYNOPSIS |
|
26
|
|
|
|
|
|
|
|
|
27
|
|
|
|
|
|
|
use IO::Async::Loop; |
|
28
|
|
|
|
|
|
|
use Net::Async::FTP; |
|
29
|
|
|
|
|
|
|
|
|
30
|
|
|
|
|
|
|
my $loop = IO::Async::Loop->new(); |
|
31
|
|
|
|
|
|
|
|
|
32
|
|
|
|
|
|
|
my $ftp = Net::Async::FTP->new(); |
|
33
|
|
|
|
|
|
|
$loop->add( $ftp ); |
|
34
|
|
|
|
|
|
|
|
|
35
|
|
|
|
|
|
|
$ftp->connect( |
|
36
|
|
|
|
|
|
|
host => "ftp.example.com", |
|
37
|
|
|
|
|
|
|
)->then( sub { |
|
38
|
|
|
|
|
|
|
$ftp->login( |
|
39
|
|
|
|
|
|
|
user => "username", |
|
40
|
|
|
|
|
|
|
pass => "password", |
|
41
|
|
|
|
|
|
|
) |
|
42
|
|
|
|
|
|
|
})->then( sub { |
|
43
|
|
|
|
|
|
|
$ftp->retr( |
|
44
|
|
|
|
|
|
|
path => "README.txt", |
|
45
|
|
|
|
|
|
|
) |
|
46
|
|
|
|
|
|
|
})->then( sub { |
|
47
|
|
|
|
|
|
|
my ( $data ) = @_; |
|
48
|
|
|
|
|
|
|
print "README.txt says:\n"; |
|
49
|
|
|
|
|
|
|
print $data; |
|
50
|
|
|
|
|
|
|
})->get; |
|
51
|
|
|
|
|
|
|
|
|
52
|
|
|
|
|
|
|
=head1 DESCRIPTION |
|
53
|
|
|
|
|
|
|
|
|
54
|
|
|
|
|
|
|
This object class implements an asynchronous FTP client, for use in |
|
55
|
|
|
|
|
|
|
L-based programs. |
|
56
|
|
|
|
|
|
|
|
|
57
|
|
|
|
|
|
|
The code in this module is not particularly complete. It contains a minimal |
|
58
|
|
|
|
|
|
|
implementation of a few FTP commands, not even the full minimal set the RFC |
|
59
|
|
|
|
|
|
|
suggests all clients should support. I am releasing it anyway, because it is |
|
60
|
|
|
|
|
|
|
still useful as it stands, and could easily support extra commands being added |
|
61
|
|
|
|
|
|
|
if anyone would find it useful. |
|
62
|
|
|
|
|
|
|
|
|
63
|
|
|
|
|
|
|
The (undocumented) C method provides a generic base for the |
|
64
|
|
|
|
|
|
|
currently-implemented commands, and would be the basis for new commands. |
|
65
|
|
|
|
|
|
|
|
|
66
|
|
|
|
|
|
|
As they say so often in the open-source world; Patches Welcome. |
|
67
|
|
|
|
|
|
|
|
|
68
|
|
|
|
|
|
|
=cut |
|
69
|
|
|
|
|
|
|
|
|
70
|
|
|
|
|
|
|
=head1 CONSTRUCTOR |
|
71
|
|
|
|
|
|
|
|
|
72
|
|
|
|
|
|
|
=cut |
|
73
|
|
|
|
|
|
|
|
|
74
|
|
|
|
|
|
|
=head2 $ftp = Net::Async::FTP->new( %args ) |
|
75
|
|
|
|
|
|
|
|
|
76
|
|
|
|
|
|
|
This function returns a new instance of a C object. As it is |
|
77
|
|
|
|
|
|
|
a subclass of C its constructor takes any arguments for |
|
78
|
|
|
|
|
|
|
that class. |
|
79
|
|
|
|
|
|
|
|
|
80
|
|
|
|
|
|
|
=cut |
|
81
|
|
|
|
|
|
|
|
|
82
|
|
|
|
|
|
|
sub new |
|
83
|
|
|
|
|
|
|
{ |
|
84
|
8
|
|
|
8
|
1
|
53277
|
my $class = shift; |
|
85
|
8
|
|
|
|
|
29
|
my %args = @_; |
|
86
|
|
|
|
|
|
|
|
|
87
|
8
|
|
|
|
|
127
|
my $self = $class->SUPER::new( %args ); |
|
88
|
|
|
|
|
|
|
|
|
89
|
8
|
|
|
|
|
2200
|
$self->{req_queue} = []; |
|
90
|
|
|
|
|
|
|
|
|
91
|
8
|
|
|
|
|
30
|
return $self; |
|
92
|
|
|
|
|
|
|
} |
|
93
|
|
|
|
|
|
|
|
|
94
|
|
|
|
|
|
|
sub on_read |
|
95
|
|
|
|
|
|
|
{ |
|
96
|
59
|
|
|
59
|
1
|
82722
|
my $self = shift; |
|
97
|
59
|
|
|
|
|
124
|
my ( $buffref, $closed ) = @_; |
|
98
|
|
|
|
|
|
|
|
|
99
|
59
|
|
|
|
|
183
|
$self->_do_req_queue; |
|
100
|
|
|
|
|
|
|
|
|
101
|
59
|
100
|
|
|
|
103
|
if( my $item = shift @{ $self->{req_queue} } ) { |
|
|
59
|
|
|
|
|
212
|
|
|
102
|
32
|
|
|
|
|
204
|
return $item->{on_read}; |
|
103
|
|
|
|
|
|
|
} |
|
104
|
|
|
|
|
|
|
|
|
105
|
27
|
50
|
|
|
|
341
|
return 0 unless $$buffref =~ s/^(.*)$CRLF//; |
|
106
|
0
|
|
|
|
|
0
|
print STDERR "Unexpected incoming line $1\n"; |
|
107
|
0
|
|
|
|
|
0
|
return 1; |
|
108
|
|
|
|
|
|
|
} |
|
109
|
|
|
|
|
|
|
|
|
110
|
|
|
|
|
|
|
=head1 METHODS |
|
111
|
|
|
|
|
|
|
|
|
112
|
|
|
|
|
|
|
=cut |
|
113
|
|
|
|
|
|
|
|
|
114
|
|
|
|
|
|
|
=head2 $ftp->connect( %args ) ==> () |
|
115
|
|
|
|
|
|
|
|
|
116
|
|
|
|
|
|
|
Connects to the FTP server. Takes the following arguments: |
|
117
|
|
|
|
|
|
|
|
|
118
|
|
|
|
|
|
|
=over 8 |
|
119
|
|
|
|
|
|
|
|
|
120
|
|
|
|
|
|
|
=item host => STRING |
|
121
|
|
|
|
|
|
|
|
|
122
|
|
|
|
|
|
|
Hostname of the server |
|
123
|
|
|
|
|
|
|
|
|
124
|
|
|
|
|
|
|
=item service => STRING or INT |
|
125
|
|
|
|
|
|
|
|
|
126
|
|
|
|
|
|
|
Optional. Service name or port number to connect to. If not supplied, will use |
|
127
|
|
|
|
|
|
|
C. |
|
128
|
|
|
|
|
|
|
|
|
129
|
|
|
|
|
|
|
=item family => INT |
|
130
|
|
|
|
|
|
|
|
|
131
|
|
|
|
|
|
|
Optional. Socket family to use. Will default to whatever C |
|
132
|
|
|
|
|
|
|
returns if not supplied. |
|
133
|
|
|
|
|
|
|
|
|
134
|
|
|
|
|
|
|
=item on_connected => CODE |
|
135
|
|
|
|
|
|
|
|
|
136
|
|
|
|
|
|
|
Optional when returning a Future. Continuation to call when connection is |
|
137
|
|
|
|
|
|
|
successful. |
|
138
|
|
|
|
|
|
|
|
|
139
|
|
|
|
|
|
|
$on_connected->() |
|
140
|
|
|
|
|
|
|
|
|
141
|
|
|
|
|
|
|
=item on_error => CODE |
|
142
|
|
|
|
|
|
|
|
|
143
|
|
|
|
|
|
|
Optional when returning a Future. Continuation to call on an error. |
|
144
|
|
|
|
|
|
|
|
|
145
|
|
|
|
|
|
|
$on_error->( $message ) |
|
146
|
|
|
|
|
|
|
|
|
147
|
|
|
|
|
|
|
=back |
|
148
|
|
|
|
|
|
|
|
|
149
|
|
|
|
|
|
|
=cut |
|
150
|
|
|
|
|
|
|
|
|
151
|
|
|
|
|
|
|
sub connect |
|
152
|
|
|
|
|
|
|
{ |
|
153
|
2
|
|
|
2
|
1
|
348
|
my $self = shift; |
|
154
|
2
|
|
|
|
|
20
|
my %args = @_; |
|
155
|
|
|
|
|
|
|
|
|
156
|
2
|
50
|
66
|
|
|
17
|
my $on_connected = $args{on_connected} or defined wantarray or croak "Expected 'on_connected'"; |
|
157
|
2
|
50
|
66
|
|
|
17
|
my $on_error = $args{on_error} or defined wantarray or croak "Expected 'on_error'"; |
|
158
|
|
|
|
|
|
|
|
|
159
|
|
|
|
|
|
|
my $f = $self->SUPER::connect( |
|
160
|
|
|
|
|
|
|
service => "ftp", |
|
161
|
|
|
|
|
|
|
%args, |
|
162
|
|
|
|
|
|
|
)->then( sub { |
|
163
|
|
|
|
|
|
|
# TODO: This is a bit messy. Install an initial on_read handler for |
|
164
|
|
|
|
|
|
|
# the connect messages, by sending an "empty string" command |
|
165
|
2
|
|
|
2
|
|
2116496
|
$self->do_command( undef, [ 220 ] ); |
|
166
|
2
|
|
|
|
|
21
|
}); |
|
167
|
|
|
|
|
|
|
|
|
168
|
2
|
100
|
|
|
|
59248
|
$f->on_done( $on_connected ) if $on_connected; |
|
169
|
2
|
100
|
|
|
|
17
|
$f->on_fail( $on_error ) if $on_error; |
|
170
|
|
|
|
|
|
|
|
|
171
|
2
|
100
|
|
|
|
34
|
return $f if defined wantarray; |
|
172
|
1
|
|
|
0
|
|
7
|
$f->on_ready( sub { undef $f } ); # Intentional cycle |
|
|
0
|
|
|
|
|
0
|
|
|
173
|
|
|
|
|
|
|
} |
|
174
|
|
|
|
|
|
|
|
|
175
|
|
|
|
|
|
|
my %NUMTYPES = ( |
|
176
|
|
|
|
|
|
|
1 => "info", |
|
177
|
|
|
|
|
|
|
2 => "ok", |
|
178
|
|
|
|
|
|
|
3 => "more", |
|
179
|
|
|
|
|
|
|
4 => "err", |
|
180
|
|
|
|
|
|
|
5 => "err", |
|
181
|
|
|
|
|
|
|
); |
|
182
|
|
|
|
|
|
|
|
|
183
|
|
|
|
|
|
|
sub _build_future_onread |
|
184
|
|
|
|
|
|
|
{ |
|
185
|
45
|
|
|
45
|
|
88
|
my $self = shift; |
|
186
|
45
|
|
|
|
|
106
|
my ( $command, $f, $accept, %continue ) = @_; |
|
187
|
|
|
|
|
|
|
|
|
188
|
45
|
|
|
|
|
62
|
my @extralines; |
|
189
|
45
|
|
|
|
|
110
|
my %accept = map { $_ => 1 } @$accept; |
|
|
33
|
|
|
|
|
177
|
|
|
190
|
|
|
|
|
|
|
|
|
191
|
|
|
|
|
|
|
sub { |
|
192
|
83
|
|
|
83
|
|
28771
|
my ( $self, $buffref, $closed ) = @_; |
|
193
|
|
|
|
|
|
|
|
|
194
|
83
|
100
|
|
|
|
913
|
return 0 unless $$buffref =~ s/^(.*)$CRLF//; |
|
195
|
66
|
|
|
|
|
243
|
my $line = $1; |
|
196
|
|
|
|
|
|
|
|
|
197
|
66
|
100
|
|
|
|
368
|
if( $line =~ m/^(\d{3}) +(.*)$/ ) { |
|
|
|
50
|
|
|
|
|
|
|
198
|
46
|
|
|
|
|
149
|
my ( $number, $message ) = ( $1, $2 ); |
|
199
|
46
|
|
|
|
|
155
|
my $numtype = $NUMTYPES{substr($number, 0, 1)}; |
|
200
|
|
|
|
|
|
|
|
|
201
|
46
|
100
|
100
|
|
|
260
|
if( $accept{$number} || $accept{$numtype} ) { |
|
|
|
50
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
202
|
32
|
50
|
|
|
|
295
|
if( $numtype eq "info" ) { |
|
203
|
0
|
|
|
|
|
0
|
print STDERR "TODO: info $number\n"; |
|
204
|
|
|
|
|
|
|
} |
|
205
|
|
|
|
|
|
|
else { |
|
206
|
32
|
|
|
|
|
155
|
$f->done( $number, $message, @extralines ); |
|
207
|
32
|
|
|
|
|
2007
|
return undef; |
|
208
|
|
|
|
|
|
|
} |
|
209
|
|
|
|
|
|
|
} |
|
210
|
|
|
|
|
|
|
elsif( my $cb = $continue{$number} ) { |
|
211
|
14
|
|
|
|
|
44
|
return $cb->( $f, $number, $message ); |
|
212
|
|
|
|
|
|
|
} |
|
213
|
|
|
|
|
|
|
elsif( $numtype eq "err" ) { |
|
214
|
0
|
|
|
|
|
0
|
$f->fail( $message, ftp => $number ); |
|
215
|
|
|
|
|
|
|
} |
|
216
|
|
|
|
|
|
|
else { |
|
217
|
0
|
|
|
|
|
0
|
print STDERR "Unexpected incoming $number\n"; |
|
218
|
|
|
|
|
|
|
} |
|
219
|
|
|
|
|
|
|
} |
|
220
|
|
|
|
|
|
|
elsif( $line =~ m/^(\d{3})-(.*)$/ ) { |
|
221
|
20
|
|
|
|
|
52
|
push @extralines, $2; |
|
222
|
|
|
|
|
|
|
} |
|
223
|
|
|
|
|
|
|
else { |
|
224
|
0
|
|
|
|
|
0
|
print STDERR "Unparseable incoming line $line\n"; |
|
225
|
|
|
|
|
|
|
} |
|
226
|
|
|
|
|
|
|
|
|
227
|
20
|
|
|
|
|
60
|
return 1; |
|
228
|
45
|
|
|
|
|
346
|
}; |
|
229
|
|
|
|
|
|
|
} |
|
230
|
|
|
|
|
|
|
|
|
231
|
|
|
|
|
|
|
sub do_command |
|
232
|
|
|
|
|
|
|
{ |
|
233
|
33
|
|
|
33
|
0
|
57
|
my $self = shift; |
|
234
|
33
|
|
|
|
|
108
|
my ( $command, $accept, %continue ) = @_; |
|
235
|
|
|
|
|
|
|
|
|
236
|
33
|
|
|
|
|
142
|
my $f = $self->loop->new_future; |
|
237
|
33
|
|
|
|
|
10616
|
my $on_read = $self->_build_future_onread( $command, $f, $accept, %continue ); |
|
238
|
|
|
|
|
|
|
|
|
239
|
33
|
|
|
|
|
69
|
my $queue = $self->{req_queue}; |
|
240
|
33
|
|
|
|
|
133
|
push @$queue, { command => $command, on_read => $on_read }; |
|
241
|
|
|
|
|
|
|
|
|
242
|
33
|
|
|
|
|
96
|
$self->_do_req_queue; |
|
243
|
|
|
|
|
|
|
|
|
244
|
33
|
|
|
|
|
220
|
return $f; |
|
245
|
|
|
|
|
|
|
} |
|
246
|
|
|
|
|
|
|
|
|
247
|
|
|
|
|
|
|
sub _do_req_queue |
|
248
|
|
|
|
|
|
|
{ |
|
249
|
92
|
|
|
92
|
|
135
|
my $self = shift; |
|
250
|
|
|
|
|
|
|
|
|
251
|
92
|
|
|
|
|
149
|
my $queue = $self->{req_queue}; |
|
252
|
92
|
100
|
|
|
|
247
|
return unless @$queue; |
|
253
|
|
|
|
|
|
|
|
|
254
|
65
|
|
|
|
|
242
|
my $item = $queue->[0]; |
|
255
|
|
|
|
|
|
|
|
|
256
|
65
|
100
|
|
|
|
198
|
if( defined $item->{command} ) { |
|
257
|
31
|
|
|
|
|
223
|
$self->write( "$item->{command}$CRLF" ); |
|
258
|
31
|
|
|
|
|
4634
|
undef $item->{command}; |
|
259
|
|
|
|
|
|
|
} |
|
260
|
|
|
|
|
|
|
} |
|
261
|
|
|
|
|
|
|
|
|
262
|
|
|
|
|
|
|
sub _connect_dataconn |
|
263
|
|
|
|
|
|
|
{ |
|
264
|
12
|
|
|
12
|
|
32
|
my $self = shift; |
|
265
|
12
|
|
|
|
|
30
|
my ( $on_conn ) = @_; |
|
266
|
|
|
|
|
|
|
|
|
267
|
|
|
|
|
|
|
$self->do_command( "PASV", [], |
|
268
|
|
|
|
|
|
|
227 => sub { |
|
269
|
12
|
|
|
12
|
|
30
|
my ( $f, $num, $message ) = @_; |
|
270
|
12
|
50
|
|
|
|
80
|
$message =~ m/\((\d+,\d+,\d+,\d+,\d+,\d+)\)/ or |
|
271
|
|
|
|
|
|
|
return $f->fail( "Did not find (ip,port) in message $message", ftp => $num, $message ); |
|
272
|
|
|
|
|
|
|
|
|
273
|
12
|
|
|
|
|
77
|
my ( $ipA, $ipB, $ipC, $ipD, $portHI, $portLO ) = split( m/,/, $1 ); |
|
274
|
12
|
|
|
|
|
56
|
my $ip = "$ipA.$ipB.$ipC.$ipD"; |
|
275
|
12
|
|
|
|
|
40
|
my $port = $portHI*256 + $portLO; |
|
276
|
|
|
|
|
|
|
|
|
277
|
12
|
|
|
|
|
66
|
my $sinaddr = pack_sockaddr_in( $port, inet_aton( $ip ) ); |
|
278
|
|
|
|
|
|
|
|
|
279
|
12
|
|
|
|
|
72
|
my $loop = $self->get_loop; |
|
280
|
12
|
|
|
|
|
133
|
my $connect_f = $loop->connect( |
|
281
|
|
|
|
|
|
|
addr => [ AF_INET, SOCK_STREAM, 0, $sinaddr ], |
|
282
|
|
|
|
|
|
|
); |
|
283
|
12
|
|
|
|
|
59810
|
$connect_f->on_fail( $f ); |
|
284
|
|
|
|
|
|
|
|
|
285
|
12
|
|
|
|
|
115
|
return $on_conn->( $f, $connect_f ); |
|
286
|
|
|
|
|
|
|
}, |
|
287
|
12
|
|
|
|
|
110
|
); |
|
288
|
|
|
|
|
|
|
} |
|
289
|
|
|
|
|
|
|
|
|
290
|
|
|
|
|
|
|
# Now some convenient wrappers for classes of command |
|
291
|
|
|
|
|
|
|
|
|
292
|
|
|
|
|
|
|
sub _do_command_collect_dataconn |
|
293
|
|
|
|
|
|
|
{ |
|
294
|
10
|
|
|
10
|
|
18
|
my $self = shift; |
|
295
|
10
|
|
|
|
|
16
|
my ( $command ) = @_; |
|
296
|
|
|
|
|
|
|
|
|
297
|
|
|
|
|
|
|
$self->_connect_dataconn( |
|
298
|
|
|
|
|
|
|
sub { |
|
299
|
10
|
|
|
10
|
|
18
|
my ( $f, $connect_f ) = @_; |
|
300
|
|
|
|
|
|
|
|
|
301
|
10
|
|
|
|
|
15
|
my $data; |
|
302
|
|
|
|
|
|
|
my $dataconn = IO::Async::Stream->new( |
|
303
|
|
|
|
|
|
|
on_read => sub { |
|
304
|
20
|
|
|
|
|
20843
|
my ( $self, $buffref, $closed ) = @_; |
|
305
|
20
|
100
|
|
|
|
91
|
return 0 unless $closed; |
|
306
|
10
|
|
|
|
|
23
|
$data = $$buffref; |
|
307
|
10
|
|
|
|
|
37
|
$self->close; |
|
308
|
10
|
|
|
|
|
5683
|
return 0; |
|
309
|
|
|
|
|
|
|
}, |
|
310
|
10
|
|
|
|
|
2163
|
); |
|
311
|
10
|
|
|
|
|
1067
|
$self->add_child( $dataconn ); |
|
312
|
|
|
|
|
|
|
|
|
313
|
|
|
|
|
|
|
$connect_f->on_done( sub { |
|
314
|
10
|
|
|
|
|
11272
|
$dataconn->configure( read_handle => $_[0] ); |
|
315
|
10
|
|
|
|
|
1045
|
}); |
|
316
|
10
|
|
|
|
|
131
|
$connect_f->on_ready( sub { undef $connect_f } ); # Intentional cycle |
|
|
10
|
|
|
|
|
5666
|
|
|
317
|
|
|
|
|
|
|
|
|
318
|
10
|
|
|
|
|
117
|
$self->write( "$command$CRLF" ); |
|
319
|
|
|
|
|
|
|
|
|
320
|
10
|
|
|
|
|
1213
|
my $cmd_f = $f->new; |
|
321
|
10
|
|
|
|
|
177
|
my $on_read = $self->_build_future_onread( $command, $cmd_f, [ 226 ] ); |
|
322
|
|
|
|
|
|
|
|
|
323
|
|
|
|
|
|
|
my $done_f = Future->needs_all( $dataconn->new_close_future, $cmd_f ) |
|
324
|
10
|
|
|
|
|
3180
|
->on_done( sub { $f->done( $data ) }) |
|
325
|
10
|
|
|
|
|
58
|
->on_fail( $f ); |
|
326
|
10
|
|
|
|
|
1698
|
$f->on_cancel( $done_f ); |
|
327
|
|
|
|
|
|
|
|
|
328
|
10
|
|
|
|
|
121
|
return $on_read; |
|
329
|
|
|
|
|
|
|
}, |
|
330
|
10
|
|
|
|
|
87
|
); |
|
331
|
|
|
|
|
|
|
} |
|
332
|
|
|
|
|
|
|
|
|
333
|
|
|
|
|
|
|
sub _do_command_send_dataconn |
|
334
|
|
|
|
|
|
|
{ |
|
335
|
2
|
|
|
2
|
|
5
|
my $self = shift; |
|
336
|
2
|
|
|
|
|
12
|
my ( $command, $data ) = @_; |
|
337
|
|
|
|
|
|
|
|
|
338
|
|
|
|
|
|
|
$self->_connect_dataconn( |
|
339
|
|
|
|
|
|
|
sub { |
|
340
|
2
|
|
|
2
|
|
4
|
my ( $f, $connect_f ) = @_; |
|
341
|
|
|
|
|
|
|
|
|
342
|
2
|
|
|
|
|
25
|
my $dataconn = IO::Async::Stream->new; |
|
343
|
2
|
|
|
|
|
394
|
$self->add_child( $dataconn ); |
|
344
|
|
|
|
|
|
|
|
|
345
|
|
|
|
|
|
|
$connect_f->on_done( sub { |
|
346
|
2
|
|
|
|
|
4528
|
$dataconn->configure( write_handle => $_[0] ); |
|
347
|
2
|
|
|
|
|
266
|
}); |
|
348
|
2
|
|
|
|
|
28
|
$connect_f->on_ready( sub { undef $connect_f } ); # Intentional cycle |
|
|
2
|
|
|
|
|
186
|
|
|
349
|
|
|
|
|
|
|
|
|
350
|
2
|
|
|
|
|
27
|
$self->write( "$command$CRLF" ); |
|
351
|
|
|
|
|
|
|
|
|
352
|
2
|
|
|
|
|
422
|
my $cmd_f = $f->new; |
|
353
|
|
|
|
|
|
|
my $on_read = $self->_build_future_onread( $command, $cmd_f, [ 226 ], |
|
354
|
|
|
|
|
|
|
150 => sub { |
|
355
|
2
|
|
|
|
|
12
|
$dataconn->write( $data ); |
|
356
|
2
|
|
|
|
|
349
|
$dataconn->close_when_empty; |
|
357
|
2
|
|
|
|
|
27
|
return 1; |
|
358
|
|
|
|
|
|
|
}, |
|
359
|
2
|
|
|
|
|
45
|
); |
|
360
|
|
|
|
|
|
|
|
|
361
|
|
|
|
|
|
|
my $done_f = Future->needs_all( $dataconn->new_close_future, $cmd_f ) |
|
362
|
2
|
|
|
|
|
1002
|
->on_done( sub { $f->done } ) |
|
363
|
2
|
|
|
|
|
16
|
->on_fail( $f ); |
|
364
|
2
|
|
|
|
|
323
|
$f->on_cancel( $done_f ); |
|
365
|
|
|
|
|
|
|
|
|
366
|
2
|
|
|
|
|
28
|
return $on_read; |
|
367
|
|
|
|
|
|
|
}, |
|
368
|
2
|
|
|
|
|
19
|
); |
|
369
|
|
|
|
|
|
|
} |
|
370
|
|
|
|
|
|
|
|
|
371
|
|
|
|
|
|
|
=head2 $ftp->login( %args ) ==> () |
|
372
|
|
|
|
|
|
|
|
|
373
|
|
|
|
|
|
|
Sends a C and optionally C command. Takes the following arguments: |
|
374
|
|
|
|
|
|
|
|
|
375
|
|
|
|
|
|
|
=over 8 |
|
376
|
|
|
|
|
|
|
|
|
377
|
|
|
|
|
|
|
=item user => STRING |
|
378
|
|
|
|
|
|
|
|
|
379
|
|
|
|
|
|
|
Username for the C command |
|
380
|
|
|
|
|
|
|
|
|
381
|
|
|
|
|
|
|
=item pass => STRING |
|
382
|
|
|
|
|
|
|
|
|
383
|
|
|
|
|
|
|
Password for the C command if required |
|
384
|
|
|
|
|
|
|
|
|
385
|
|
|
|
|
|
|
=item on_login => CODE |
|
386
|
|
|
|
|
|
|
|
|
387
|
|
|
|
|
|
|
Optional when returning a future. Continuation to invoke on successful login. |
|
388
|
|
|
|
|
|
|
|
|
389
|
|
|
|
|
|
|
$on_login->() |
|
390
|
|
|
|
|
|
|
|
|
391
|
|
|
|
|
|
|
=item on_error => CODE |
|
392
|
|
|
|
|
|
|
|
|
393
|
|
|
|
|
|
|
Optional when returning a future. Continuation to invoke on an error. |
|
394
|
|
|
|
|
|
|
|
|
395
|
|
|
|
|
|
|
$on_error->( $message ) |
|
396
|
|
|
|
|
|
|
|
|
397
|
|
|
|
|
|
|
=back |
|
398
|
|
|
|
|
|
|
|
|
399
|
|
|
|
|
|
|
=cut |
|
400
|
|
|
|
|
|
|
|
|
401
|
|
|
|
|
|
|
sub login |
|
402
|
|
|
|
|
|
|
{ |
|
403
|
3
|
|
|
3
|
1
|
1404
|
my $self = shift; |
|
404
|
3
|
|
|
|
|
23
|
my %args = @_; |
|
405
|
|
|
|
|
|
|
|
|
406
|
3
|
50
|
|
|
|
15
|
my $user = $args{user} or croak "Expected 'user'"; |
|
407
|
|
|
|
|
|
|
|
|
408
|
3
|
50
|
66
|
|
|
35
|
my $on_login = $args{on_login} or defined wantarray or croak "Expected 'on_login'"; |
|
409
|
3
|
50
|
66
|
|
|
35
|
my $on_error = $args{on_error} or defined wantarray or croak "Expected 'on_error'"; |
|
410
|
|
|
|
|
|
|
|
|
411
|
|
|
|
|
|
|
my $f = $self->do_command( "USER $user", [ 331 ] ) |
|
412
|
|
|
|
|
|
|
->then( sub { |
|
413
|
3
|
50
|
|
3
|
|
162
|
exists $args{pass} or return $on_error->( "No password" ); |
|
414
|
3
|
|
|
|
|
18
|
$self->do_command( "PASS $args{pass}", [ 230 ] ) |
|
415
|
3
|
|
|
|
|
21
|
}); |
|
416
|
|
|
|
|
|
|
|
|
417
|
3
|
100
|
|
|
|
213
|
$f->on_done( $on_login ) if $on_login; |
|
418
|
3
|
100
|
|
|
|
22
|
$f->on_fail( $on_error ) if $on_error; |
|
419
|
|
|
|
|
|
|
|
|
420
|
3
|
100
|
|
|
|
21
|
return $f if defined wantarray; |
|
421
|
1
|
|
|
1
|
|
10
|
$f->on_ready( sub { undef $f } ); # Intentional cycle |
|
|
1
|
|
|
|
|
70
|
|
|
422
|
|
|
|
|
|
|
} |
|
423
|
|
|
|
|
|
|
|
|
424
|
|
|
|
|
|
|
=head2 $ftp->rename( %args ) ==> () |
|
425
|
|
|
|
|
|
|
|
|
426
|
|
|
|
|
|
|
Renames a file on the remote server. Takes the following arguments |
|
427
|
|
|
|
|
|
|
|
|
428
|
|
|
|
|
|
|
=over 8 |
|
429
|
|
|
|
|
|
|
|
|
430
|
|
|
|
|
|
|
=item oldpath => STRING |
|
431
|
|
|
|
|
|
|
|
|
432
|
|
|
|
|
|
|
Path to file to rename |
|
433
|
|
|
|
|
|
|
|
|
434
|
|
|
|
|
|
|
=item newpath => STRING |
|
435
|
|
|
|
|
|
|
|
|
436
|
|
|
|
|
|
|
Desired new path for the file |
|
437
|
|
|
|
|
|
|
|
|
438
|
|
|
|
|
|
|
=item on_done => CODE |
|
439
|
|
|
|
|
|
|
|
|
440
|
|
|
|
|
|
|
Optional when returning a future. Continuation to invoke on success. |
|
441
|
|
|
|
|
|
|
|
|
442
|
|
|
|
|
|
|
$on_done->() |
|
443
|
|
|
|
|
|
|
|
|
444
|
|
|
|
|
|
|
=item on_error => CODE |
|
445
|
|
|
|
|
|
|
|
|
446
|
|
|
|
|
|
|
Optional. Continuation to invoke on an error. |
|
447
|
|
|
|
|
|
|
|
|
448
|
|
|
|
|
|
|
$on_error->( $message ) |
|
449
|
|
|
|
|
|
|
|
|
450
|
|
|
|
|
|
|
=back |
|
451
|
|
|
|
|
|
|
|
|
452
|
|
|
|
|
|
|
=cut |
|
453
|
|
|
|
|
|
|
|
|
454
|
|
|
|
|
|
|
sub rename |
|
455
|
|
|
|
|
|
|
{ |
|
456
|
2
|
|
|
2
|
1
|
1081
|
my $self = shift; |
|
457
|
2
|
|
|
|
|
8
|
my %args = @_; |
|
458
|
|
|
|
|
|
|
|
|
459
|
2
|
|
|
|
|
4
|
my $oldpath = $args{oldpath}; |
|
460
|
2
|
50
|
|
|
|
6
|
defined $oldpath or croak "Expected 'oldpath'"; |
|
461
|
|
|
|
|
|
|
|
|
462
|
2
|
|
|
|
|
5
|
my $newpath = $args{newpath}; |
|
463
|
2
|
50
|
|
|
|
12
|
defined $newpath or croak "Expected 'newpath'"; |
|
464
|
|
|
|
|
|
|
|
|
465
|
2
|
50
|
66
|
|
|
11
|
my $on_done = $args{on_done} or defined wantarray or croak "Expected 'on_done'"; |
|
466
|
|
|
|
|
|
|
|
|
467
|
2
|
|
|
|
|
3
|
my $on_error = $args{on_error}; |
|
468
|
2
|
100
|
50
|
0
|
|
14
|
$on_error ||= sub { die "Error $_[0] during rename" } if !defined wantarray; |
|
|
0
|
|
|
|
|
0
|
|
|
469
|
|
|
|
|
|
|
|
|
470
|
|
|
|
|
|
|
my $f = $self->do_command( "RNFR $oldpath", [ 350 ] ) |
|
471
|
|
|
|
|
|
|
->then( sub { |
|
472
|
2
|
|
|
2
|
|
113
|
$self->do_command( "RNTO $newpath", [ 'ok' ] ) |
|
473
|
2
|
|
|
|
|
10
|
}); |
|
474
|
|
|
|
|
|
|
|
|
475
|
2
|
100
|
|
|
|
156
|
$f->on_done( $on_done ) if $on_done; |
|
476
|
2
|
100
|
|
|
|
19
|
$f->on_fail( $on_error ) if $on_error; |
|
477
|
|
|
|
|
|
|
|
|
478
|
2
|
100
|
|
|
|
15
|
return $f if defined wantarray; |
|
479
|
1
|
|
|
1
|
|
10
|
$f->on_ready( sub { undef $f } ); # Intentional cycle |
|
|
1
|
|
|
|
|
90
|
|
|
480
|
|
|
|
|
|
|
} |
|
481
|
|
|
|
|
|
|
|
|
482
|
|
|
|
|
|
|
=head2 $ftp->dele( %args ) ==> () |
|
483
|
|
|
|
|
|
|
|
|
484
|
|
|
|
|
|
|
Deletes a file on the remote server. Takes the following arguments |
|
485
|
|
|
|
|
|
|
|
|
486
|
|
|
|
|
|
|
=over 8 |
|
487
|
|
|
|
|
|
|
|
|
488
|
|
|
|
|
|
|
=item path => STRING |
|
489
|
|
|
|
|
|
|
|
|
490
|
|
|
|
|
|
|
Path to file to delete |
|
491
|
|
|
|
|
|
|
|
|
492
|
|
|
|
|
|
|
=item on_done => CODE |
|
493
|
|
|
|
|
|
|
|
|
494
|
|
|
|
|
|
|
Optional when returning a future. Continuation to invoke on success. |
|
495
|
|
|
|
|
|
|
|
|
496
|
|
|
|
|
|
|
$on_done->() |
|
497
|
|
|
|
|
|
|
|
|
498
|
|
|
|
|
|
|
=item on_error => CODE |
|
499
|
|
|
|
|
|
|
|
|
500
|
|
|
|
|
|
|
Optional. Continuation to invoke on an error. |
|
501
|
|
|
|
|
|
|
|
|
502
|
|
|
|
|
|
|
$on_error->( $message ) |
|
503
|
|
|
|
|
|
|
|
|
504
|
|
|
|
|
|
|
=back |
|
505
|
|
|
|
|
|
|
|
|
506
|
|
|
|
|
|
|
=cut |
|
507
|
|
|
|
|
|
|
|
|
508
|
|
|
|
|
|
|
sub dele |
|
509
|
|
|
|
|
|
|
{ |
|
510
|
2
|
|
|
2
|
1
|
792
|
my $self = shift; |
|
511
|
2
|
|
|
|
|
9
|
my %args = @_; |
|
512
|
|
|
|
|
|
|
|
|
513
|
2
|
|
|
|
|
5
|
my $path = $args{path}; |
|
514
|
2
|
50
|
|
|
|
7
|
defined $path or croak "Expected 'path'"; |
|
515
|
|
|
|
|
|
|
|
|
516
|
2
|
50
|
66
|
|
|
16
|
my $on_done = $args{on_done} or defined wantarray or croak "Expected 'on_done'"; |
|
517
|
|
|
|
|
|
|
|
|
518
|
2
|
|
|
|
|
5
|
my $on_error = $args{on_error}; |
|
519
|
2
|
100
|
50
|
0
|
|
13
|
$on_error ||= sub { die "Error $_[0] during RETR" } if !defined wantarray; |
|
|
0
|
|
|
|
|
0
|
|
|
520
|
|
|
|
|
|
|
|
|
521
|
2
|
|
|
|
|
13
|
my $f = $self->do_command( "DELE $path", [ 'ok' ] ); |
|
522
|
|
|
|
|
|
|
|
|
523
|
2
|
100
|
|
|
|
15
|
$f->on_done( $on_done ) if $on_done; |
|
524
|
2
|
100
|
|
|
|
24
|
$f->on_fail( $on_error ) if $on_error; |
|
525
|
|
|
|
|
|
|
|
|
526
|
2
|
|
|
|
|
18
|
return $f; |
|
527
|
|
|
|
|
|
|
} |
|
528
|
|
|
|
|
|
|
|
|
529
|
|
|
|
|
|
|
=head2 $ftp->list( %args ) ==> $list |
|
530
|
|
|
|
|
|
|
|
|
531
|
|
|
|
|
|
|
Runs a C command on a path on the remote server; which requests details |
|
532
|
|
|
|
|
|
|
on the file, or contents of the directory. Takes the following arguments |
|
533
|
|
|
|
|
|
|
|
|
534
|
|
|
|
|
|
|
=over 8 |
|
535
|
|
|
|
|
|
|
|
|
536
|
|
|
|
|
|
|
=item path => STRING |
|
537
|
|
|
|
|
|
|
|
|
538
|
|
|
|
|
|
|
Path to C |
|
539
|
|
|
|
|
|
|
|
|
540
|
|
|
|
|
|
|
=item on_list => CODE |
|
541
|
|
|
|
|
|
|
|
|
542
|
|
|
|
|
|
|
Optional when returning a future. Continuation to invoke on success. Is passed |
|
543
|
|
|
|
|
|
|
a list of lines from the C result in a single string. |
|
544
|
|
|
|
|
|
|
|
|
545
|
|
|
|
|
|
|
$on_list->( $list ) |
|
546
|
|
|
|
|
|
|
|
|
547
|
|
|
|
|
|
|
=item on_error => CODE |
|
548
|
|
|
|
|
|
|
|
|
549
|
|
|
|
|
|
|
Optional. Continuation to invoke on an error. |
|
550
|
|
|
|
|
|
|
|
|
551
|
|
|
|
|
|
|
$on_error->( $message ) |
|
552
|
|
|
|
|
|
|
|
|
553
|
|
|
|
|
|
|
=back |
|
554
|
|
|
|
|
|
|
|
|
555
|
|
|
|
|
|
|
The C method may be easier to use as it parses the lines. |
|
556
|
|
|
|
|
|
|
|
|
557
|
|
|
|
|
|
|
=cut |
|
558
|
|
|
|
|
|
|
|
|
559
|
|
|
|
|
|
|
sub list |
|
560
|
|
|
|
|
|
|
{ |
|
561
|
4
|
|
|
4
|
1
|
1107
|
my $self = shift; |
|
562
|
4
|
|
|
|
|
22
|
my %args = @_; |
|
563
|
|
|
|
|
|
|
|
|
564
|
4
|
|
|
|
|
8
|
my $path = $args{path}; |
|
565
|
|
|
|
|
|
|
|
|
566
|
4
|
50
|
66
|
|
|
26
|
my $on_list = $args{on_list} or defined wantarray or croak "Expected 'on_list'"; |
|
567
|
|
|
|
|
|
|
|
|
568
|
4
|
|
|
|
|
8
|
my $on_error = $args{on_error}; |
|
569
|
4
|
100
|
50
|
0
|
|
26
|
$on_error ||= sub { die "Error $_[0] during LIST" } if !defined wantarray; |
|
|
0
|
|
|
|
|
0
|
|
|
570
|
|
|
|
|
|
|
|
|
571
|
4
|
50
|
|
|
|
25
|
my $f = $self->_do_command_collect_dataconn( |
|
572
|
|
|
|
|
|
|
"LIST" . ( defined $path ? " $path" : "" ), |
|
573
|
|
|
|
|
|
|
); |
|
574
|
|
|
|
|
|
|
|
|
575
|
4
|
100
|
|
|
|
35
|
$f->on_done( $on_list ) if $on_list; |
|
576
|
4
|
100
|
|
|
|
19
|
$f->on_fail( $on_error ) if $on_error; |
|
577
|
|
|
|
|
|
|
|
|
578
|
4
|
|
|
|
|
29
|
return $f; |
|
579
|
|
|
|
|
|
|
} |
|
580
|
|
|
|
|
|
|
|
|
581
|
|
|
|
|
|
|
=head2 $ftp->list_parsed( %args ) ==> @list |
|
582
|
|
|
|
|
|
|
|
|
583
|
|
|
|
|
|
|
Runs a C command on a path on the remote server; and parse the result |
|
584
|
|
|
|
|
|
|
lines. Takes the following arguments |
|
585
|
|
|
|
|
|
|
|
|
586
|
|
|
|
|
|
|
=over 8 |
|
587
|
|
|
|
|
|
|
|
|
588
|
|
|
|
|
|
|
=item path => STRING |
|
589
|
|
|
|
|
|
|
|
|
590
|
|
|
|
|
|
|
Path to C |
|
591
|
|
|
|
|
|
|
|
|
592
|
|
|
|
|
|
|
=item on_list => CODE |
|
593
|
|
|
|
|
|
|
|
|
594
|
|
|
|
|
|
|
Optional when returning a future. Continuation to invoke on success. Is passed |
|
595
|
|
|
|
|
|
|
a list of files from the C result, one line per element. |
|
596
|
|
|
|
|
|
|
|
|
597
|
|
|
|
|
|
|
$on_list->( @list ) |
|
598
|
|
|
|
|
|
|
|
|
599
|
|
|
|
|
|
|
=item on_error => CODE |
|
600
|
|
|
|
|
|
|
|
|
601
|
|
|
|
|
|
|
Optional. Continuation to invoke on an error. |
|
602
|
|
|
|
|
|
|
|
|
603
|
|
|
|
|
|
|
$on_error->( $message ) |
|
604
|
|
|
|
|
|
|
|
|
605
|
|
|
|
|
|
|
=back |
|
606
|
|
|
|
|
|
|
|
|
607
|
|
|
|
|
|
|
The C<@list> array will be passed a list of C references, each formed |
|
608
|
|
|
|
|
|
|
like |
|
609
|
|
|
|
|
|
|
|
|
610
|
|
|
|
|
|
|
=over 8 |
|
611
|
|
|
|
|
|
|
|
|
612
|
|
|
|
|
|
|
=item name => STRING |
|
613
|
|
|
|
|
|
|
|
|
614
|
|
|
|
|
|
|
The filename |
|
615
|
|
|
|
|
|
|
|
|
616
|
|
|
|
|
|
|
=item type => STRING |
|
617
|
|
|
|
|
|
|
|
|
618
|
|
|
|
|
|
|
A single character; C for files, C for directories |
|
619
|
|
|
|
|
|
|
|
|
620
|
|
|
|
|
|
|
=item size => INT |
|
621
|
|
|
|
|
|
|
|
|
622
|
|
|
|
|
|
|
The size in bytes |
|
623
|
|
|
|
|
|
|
|
|
624
|
|
|
|
|
|
|
=item mtime => INT |
|
625
|
|
|
|
|
|
|
|
|
626
|
|
|
|
|
|
|
The item's last modify timestamp, as a UNIX epoch time |
|
627
|
|
|
|
|
|
|
|
|
628
|
|
|
|
|
|
|
=item mode => INT |
|
629
|
|
|
|
|
|
|
|
|
630
|
|
|
|
|
|
|
The access mode, as a number |
|
631
|
|
|
|
|
|
|
|
|
632
|
|
|
|
|
|
|
=back |
|
633
|
|
|
|
|
|
|
|
|
634
|
|
|
|
|
|
|
=cut |
|
635
|
|
|
|
|
|
|
|
|
636
|
|
|
|
|
|
|
sub list_parsed |
|
637
|
|
|
|
|
|
|
{ |
|
638
|
2
|
|
|
2
|
1
|
1967
|
my $self = shift; |
|
639
|
2
|
|
|
|
|
7
|
my %args = @_; |
|
640
|
|
|
|
|
|
|
|
|
641
|
2
|
50
|
66
|
|
|
16
|
my $on_list = $args{on_list} or defined wantarray or croak "Expected 'on_list'"; |
|
642
|
|
|
|
|
|
|
|
|
643
|
2
|
|
|
|
|
4
|
my $on_error = $args{on_error}; |
|
644
|
2
|
100
|
50
|
0
|
|
14
|
$on_error ||= sub { die "Error $_[0] during LIST" } if !defined wantarray; |
|
|
0
|
|
|
|
|
0
|
|
|
645
|
|
|
|
|
|
|
|
|
646
|
2
|
|
|
|
|
1149
|
require File::Listing; |
|
647
|
|
|
|
|
|
|
|
|
648
|
|
|
|
|
|
|
my $f = $self->list( |
|
649
|
|
|
|
|
|
|
path => $args{path}, |
|
650
|
|
|
|
|
|
|
)->then( sub { |
|
651
|
2
|
|
|
2
|
|
84
|
my ( $list ) = @_; |
|
652
|
2
|
|
|
|
|
10
|
my @files = File::Listing::parse_dir( $list ); |
|
653
|
|
|
|
|
|
|
|
|
654
|
|
|
|
|
|
|
# We want to present a list of HASH refs, as they're nicer to work with |
|
655
|
2
|
|
|
|
|
844
|
@files = map { my %h; @h{qw( name type size mtime mode )} = @$_; \%h } @files; |
|
|
4
|
|
|
|
|
6
|
|
|
|
4
|
|
|
|
|
20
|
|
|
|
4
|
|
|
|
|
13
|
|
|
656
|
|
|
|
|
|
|
|
|
657
|
2
|
|
|
|
|
14
|
return Future->new->done( @files ); |
|
658
|
2
|
|
|
|
|
7976
|
}); |
|
659
|
|
|
|
|
|
|
|
|
660
|
2
|
100
|
|
|
|
133
|
$f->on_done( $on_list ) if $on_list; |
|
661
|
2
|
100
|
|
|
|
14
|
$f->on_fail( $on_error ) if $on_error; |
|
662
|
|
|
|
|
|
|
|
|
663
|
2
|
100
|
|
|
|
20
|
return $f if defined wantarray; |
|
664
|
1
|
|
|
1
|
|
8
|
$f->on_ready( sub { undef $f } ); # Intentional cycle |
|
|
1
|
|
|
|
|
132
|
|
|
665
|
|
|
|
|
|
|
} |
|
666
|
|
|
|
|
|
|
|
|
667
|
|
|
|
|
|
|
=head2 $ftp->nlist( %args ) ==> $list |
|
668
|
|
|
|
|
|
|
|
|
669
|
|
|
|
|
|
|
Runs a C command on a path on the remote server; which requests a list |
|
670
|
|
|
|
|
|
|
of filenames in a directory. Takes the following arguments |
|
671
|
|
|
|
|
|
|
|
|
672
|
|
|
|
|
|
|
=over 8 |
|
673
|
|
|
|
|
|
|
|
|
674
|
|
|
|
|
|
|
=item path => STRING |
|
675
|
|
|
|
|
|
|
|
|
676
|
|
|
|
|
|
|
Path to C |
|
677
|
|
|
|
|
|
|
|
|
678
|
|
|
|
|
|
|
=item on_list => CODE |
|
679
|
|
|
|
|
|
|
|
|
680
|
|
|
|
|
|
|
Optional when returning a future. Continuation to invoke on success. Is passed |
|
681
|
|
|
|
|
|
|
a list of names from the C result in a single string. |
|
682
|
|
|
|
|
|
|
|
|
683
|
|
|
|
|
|
|
$on_list->( $list ) |
|
684
|
|
|
|
|
|
|
|
|
685
|
|
|
|
|
|
|
=item on_error => CODE |
|
686
|
|
|
|
|
|
|
|
|
687
|
|
|
|
|
|
|
Optional. Continuation to invoke on an error. |
|
688
|
|
|
|
|
|
|
|
|
689
|
|
|
|
|
|
|
$on_error->( $message ) |
|
690
|
|
|
|
|
|
|
|
|
691
|
|
|
|
|
|
|
=back |
|
692
|
|
|
|
|
|
|
|
|
693
|
|
|
|
|
|
|
The C method may be easier to use as it splits the lines. |
|
694
|
|
|
|
|
|
|
|
|
695
|
|
|
|
|
|
|
=cut |
|
696
|
|
|
|
|
|
|
|
|
697
|
|
|
|
|
|
|
sub nlst |
|
698
|
|
|
|
|
|
|
{ |
|
699
|
4
|
|
|
4
|
0
|
2930
|
my $self = shift; |
|
700
|
4
|
|
|
|
|
11
|
my %args = @_; |
|
701
|
|
|
|
|
|
|
|
|
702
|
4
|
|
|
|
|
10
|
my $path = $args{path}; |
|
703
|
|
|
|
|
|
|
|
|
704
|
4
|
50
|
66
|
|
|
24
|
my $on_list = $args{on_list} or defined wantarray or croak "Expected 'on_list'"; |
|
705
|
|
|
|
|
|
|
|
|
706
|
4
|
|
|
|
|
9
|
my $on_error = $args{on_error}; |
|
707
|
4
|
100
|
50
|
0
|
|
19
|
$on_error ||= sub { die "Error $_[0] during NLST" } if !defined wantarray; |
|
|
0
|
|
|
|
|
0
|
|
|
708
|
|
|
|
|
|
|
|
|
709
|
4
|
50
|
|
|
|
24
|
my $f = $self->_do_command_collect_dataconn( |
|
710
|
|
|
|
|
|
|
"NLST" . ( defined $path ? " $path" : "" ), |
|
711
|
|
|
|
|
|
|
); |
|
712
|
|
|
|
|
|
|
|
|
713
|
4
|
100
|
|
|
|
19
|
$f->on_done( $on_list ) if $on_list; |
|
714
|
4
|
100
|
|
|
|
18
|
$f->on_fail( $on_error ) if $on_error; |
|
715
|
|
|
|
|
|
|
|
|
716
|
4
|
|
|
|
|
30
|
return $f; |
|
717
|
|
|
|
|
|
|
} |
|
718
|
|
|
|
|
|
|
|
|
719
|
|
|
|
|
|
|
=head2 $ftp->namelist( %args ) ==> @names |
|
720
|
|
|
|
|
|
|
|
|
721
|
|
|
|
|
|
|
Runs a C command on a path on the remote server; which requests a list |
|
722
|
|
|
|
|
|
|
of filenames in a directory. Takes the following arguments |
|
723
|
|
|
|
|
|
|
|
|
724
|
|
|
|
|
|
|
=over 8 |
|
725
|
|
|
|
|
|
|
|
|
726
|
|
|
|
|
|
|
=item path => STRING |
|
727
|
|
|
|
|
|
|
|
|
728
|
|
|
|
|
|
|
Path to C |
|
729
|
|
|
|
|
|
|
|
|
730
|
|
|
|
|
|
|
=item on_names => CODE |
|
731
|
|
|
|
|
|
|
|
|
732
|
|
|
|
|
|
|
Optional when returning a future. Continuation to invoke on success. Is passed |
|
733
|
|
|
|
|
|
|
a list of names from the C result in a list, one name per entry |
|
734
|
|
|
|
|
|
|
|
|
735
|
|
|
|
|
|
|
$on_name->( @names ) |
|
736
|
|
|
|
|
|
|
|
|
737
|
|
|
|
|
|
|
=item on_error => CODE |
|
738
|
|
|
|
|
|
|
|
|
739
|
|
|
|
|
|
|
Optional. Continuation to invoke on an error. |
|
740
|
|
|
|
|
|
|
|
|
741
|
|
|
|
|
|
|
$on_error->( $message ) |
|
742
|
|
|
|
|
|
|
|
|
743
|
|
|
|
|
|
|
=back |
|
744
|
|
|
|
|
|
|
|
|
745
|
|
|
|
|
|
|
=cut |
|
746
|
|
|
|
|
|
|
|
|
747
|
|
|
|
|
|
|
sub namelist |
|
748
|
|
|
|
|
|
|
{ |
|
749
|
2
|
|
|
2
|
1
|
2065
|
my $self = shift; |
|
750
|
2
|
|
|
|
|
36
|
my %args = @_; |
|
751
|
|
|
|
|
|
|
|
|
752
|
2
|
50
|
66
|
|
|
15
|
my $on_names = $args{on_names} or defined wantarray or croak "Expected 'on_names'"; |
|
753
|
|
|
|
|
|
|
|
|
754
|
2
|
|
|
|
|
3
|
my $on_error = $args{on_error}; |
|
755
|
2
|
100
|
50
|
0
|
|
16
|
$on_error ||= sub { die "Error $_[0] during NLST" } if !defined wantarray; |
|
|
0
|
|
|
|
|
0
|
|
|
756
|
|
|
|
|
|
|
|
|
757
|
|
|
|
|
|
|
my $f = $self->nlst( |
|
758
|
|
|
|
|
|
|
path => $args{path}, |
|
759
|
|
|
|
|
|
|
)->then( sub { |
|
760
|
2
|
|
|
2
|
|
91
|
my ( $list ) = @_; |
|
761
|
2
|
|
|
|
|
13
|
return Future->new->done( split( m/\r?\n/, $list ) ); |
|
762
|
2
|
|
|
|
|
12
|
}); |
|
763
|
|
|
|
|
|
|
|
|
764
|
2
|
100
|
|
|
|
132
|
$f->on_done( $on_names ) if $on_names; |
|
765
|
2
|
100
|
|
|
|
14
|
$f->on_fail( $on_error ) if $on_error; |
|
766
|
|
|
|
|
|
|
|
|
767
|
2
|
100
|
|
|
|
15
|
return $f if defined wantarray; |
|
768
|
1
|
|
|
1
|
|
7
|
$f->on_ready( sub { undef $f } ); # Intentional cycle |
|
|
1
|
|
|
|
|
122
|
|
|
769
|
|
|
|
|
|
|
} |
|
770
|
|
|
|
|
|
|
|
|
771
|
|
|
|
|
|
|
=head2 $ftp->retr( %args ) ==> $content |
|
772
|
|
|
|
|
|
|
|
|
773
|
|
|
|
|
|
|
Retrieves a file on the remote server. Takes the following arguments |
|
774
|
|
|
|
|
|
|
|
|
775
|
|
|
|
|
|
|
=over 8 |
|
776
|
|
|
|
|
|
|
|
|
777
|
|
|
|
|
|
|
=item path => STRING |
|
778
|
|
|
|
|
|
|
|
|
779
|
|
|
|
|
|
|
Path to file to retrieve |
|
780
|
|
|
|
|
|
|
|
|
781
|
|
|
|
|
|
|
=item on_data => CODE |
|
782
|
|
|
|
|
|
|
|
|
783
|
|
|
|
|
|
|
Optional when returning a future. Continuation to invoke on success. Is |
|
784
|
|
|
|
|
|
|
passed the contents of the file as a single string. |
|
785
|
|
|
|
|
|
|
|
|
786
|
|
|
|
|
|
|
$on_data->( $content ) |
|
787
|
|
|
|
|
|
|
|
|
788
|
|
|
|
|
|
|
=item on_error => CODE |
|
789
|
|
|
|
|
|
|
|
|
790
|
|
|
|
|
|
|
Optional. Continuation to invoke on an error. |
|
791
|
|
|
|
|
|
|
|
|
792
|
|
|
|
|
|
|
$on_error->( $message ) |
|
793
|
|
|
|
|
|
|
|
|
794
|
|
|
|
|
|
|
=back |
|
795
|
|
|
|
|
|
|
|
|
796
|
|
|
|
|
|
|
=cut |
|
797
|
|
|
|
|
|
|
|
|
798
|
|
|
|
|
|
|
sub retr |
|
799
|
|
|
|
|
|
|
{ |
|
800
|
2
|
|
|
2
|
1
|
4006
|
my $self = shift; |
|
801
|
2
|
|
|
|
|
11
|
my %args = @_; |
|
802
|
|
|
|
|
|
|
|
|
803
|
2
|
|
|
|
|
4
|
my $path = $args{path}; |
|
804
|
2
|
50
|
|
|
|
12
|
defined $path or croak "Expected 'path'"; |
|
805
|
|
|
|
|
|
|
|
|
806
|
2
|
50
|
66
|
|
|
16
|
my $on_data = $args{on_data} or defined wantarray or croak "Expected 'on_data' as CODE reference"; |
|
807
|
|
|
|
|
|
|
|
|
808
|
2
|
|
|
|
|
5
|
my $on_error = $args{on_error}; |
|
809
|
2
|
100
|
50
|
0
|
|
16
|
$on_error ||= sub { die "Error $_[0] during RETR" } if !defined wantarray; |
|
|
0
|
|
|
|
|
0
|
|
|
810
|
|
|
|
|
|
|
|
|
811
|
2
|
|
|
|
|
12
|
my $f = $self->_do_command_collect_dataconn( |
|
812
|
|
|
|
|
|
|
"RETR $path", |
|
813
|
|
|
|
|
|
|
); |
|
814
|
|
|
|
|
|
|
|
|
815
|
2
|
100
|
|
|
|
13
|
$f->on_done( $on_data ) if $on_data; |
|
816
|
2
|
100
|
|
|
|
18
|
$f->on_fail( $on_error ) if $on_error; |
|
817
|
|
|
|
|
|
|
|
|
818
|
2
|
|
|
|
|
15
|
return $f; |
|
819
|
|
|
|
|
|
|
} |
|
820
|
|
|
|
|
|
|
|
|
821
|
|
|
|
|
|
|
=head2 $ftp->stat( %args ) ==> @stat |
|
822
|
|
|
|
|
|
|
|
|
823
|
|
|
|
|
|
|
Runs a C command on a path on the remote server; which requests details |
|
824
|
|
|
|
|
|
|
on the file, or contents of the directory. Takes the following arguments |
|
825
|
|
|
|
|
|
|
|
|
826
|
|
|
|
|
|
|
=over 8 |
|
827
|
|
|
|
|
|
|
|
|
828
|
|
|
|
|
|
|
=item path => STRING |
|
829
|
|
|
|
|
|
|
|
|
830
|
|
|
|
|
|
|
Path to C |
|
831
|
|
|
|
|
|
|
|
|
832
|
|
|
|
|
|
|
=item on_stat => CODE |
|
833
|
|
|
|
|
|
|
|
|
834
|
|
|
|
|
|
|
Optional when not returning a future. Continuation to invoke on success. Is |
|
835
|
|
|
|
|
|
|
passed a list of lines from the C result, one line per element. |
|
836
|
|
|
|
|
|
|
|
|
837
|
|
|
|
|
|
|
$on_stat->( @stat ) |
|
838
|
|
|
|
|
|
|
|
|
839
|
|
|
|
|
|
|
=item on_error => CODE |
|
840
|
|
|
|
|
|
|
|
|
841
|
|
|
|
|
|
|
Optional. Continuation to invoke on an error. |
|
842
|
|
|
|
|
|
|
|
|
843
|
|
|
|
|
|
|
$on_error->( $message ) |
|
844
|
|
|
|
|
|
|
|
|
845
|
|
|
|
|
|
|
=back |
|
846
|
|
|
|
|
|
|
|
|
847
|
|
|
|
|
|
|
The C method may be easier to use as it parses the lines. |
|
848
|
|
|
|
|
|
|
|
|
849
|
|
|
|
|
|
|
=cut |
|
850
|
|
|
|
|
|
|
|
|
851
|
|
|
|
|
|
|
sub stat |
|
852
|
|
|
|
|
|
|
{ |
|
853
|
7
|
|
|
7
|
1
|
2848
|
my $self = shift; |
|
854
|
7
|
|
|
|
|
22
|
my %args = @_; |
|
855
|
|
|
|
|
|
|
|
|
856
|
7
|
|
|
|
|
16
|
my $path = $args{path}; # optional |
|
857
|
|
|
|
|
|
|
|
|
858
|
7
|
50
|
66
|
|
|
45
|
my $on_stat = $args{on_stat} or defined wantarray or croak "Expected 'on_stat'"; |
|
859
|
|
|
|
|
|
|
|
|
860
|
7
|
|
|
|
|
14
|
my $on_error = $args{on_error}; |
|
861
|
7
|
100
|
50
|
0
|
|
30
|
$on_error ||= sub { die "Error $_[0] during STAT" } if !defined wantarray; |
|
|
0
|
|
|
|
|
0
|
|
|
862
|
|
|
|
|
|
|
|
|
863
|
|
|
|
|
|
|
my $f = $self->do_command( defined $path ? "STAT $path" : "STAT", [ 211 ] ) |
|
864
|
|
|
|
|
|
|
->then( sub { |
|
865
|
7
|
|
|
7
|
|
443
|
my ( $num, $message, $headline, @statlines ) = @_; |
|
866
|
7
|
|
|
|
|
36
|
return Future->new->done( @statlines ); |
|
867
|
7
|
50
|
|
|
|
54
|
}); |
|
868
|
|
|
|
|
|
|
|
|
869
|
7
|
100
|
|
|
|
482
|
$f->on_done( $on_stat ) if $on_stat; |
|
870
|
7
|
100
|
|
|
|
169
|
$f->on_fail( $on_error ) if $on_error; |
|
871
|
|
|
|
|
|
|
|
|
872
|
7
|
100
|
|
|
|
67
|
return $f if defined wantarray; |
|
873
|
2
|
|
|
2
|
|
14
|
$f->on_ready( sub { undef $f } ); # Intentional cycle |
|
|
2
|
|
|
|
|
203
|
|
|
874
|
|
|
|
|
|
|
} |
|
875
|
|
|
|
|
|
|
|
|
876
|
|
|
|
|
|
|
=head2 $ftp->stat_parsed( %args ) ==> @stat |
|
877
|
|
|
|
|
|
|
|
|
878
|
|
|
|
|
|
|
Runs a C command on a path on the remote server; and parse the result |
|
879
|
|
|
|
|
|
|
lines. Takes the following arguments |
|
880
|
|
|
|
|
|
|
|
|
881
|
|
|
|
|
|
|
=over 8 |
|
882
|
|
|
|
|
|
|
|
|
883
|
|
|
|
|
|
|
=item path => STRING |
|
884
|
|
|
|
|
|
|
|
|
885
|
|
|
|
|
|
|
Path to C |
|
886
|
|
|
|
|
|
|
|
|
887
|
|
|
|
|
|
|
=item on_stat => CODE |
|
888
|
|
|
|
|
|
|
|
|
889
|
|
|
|
|
|
|
Optional when returning a future. Continuation to invoke on success. Is passed |
|
890
|
|
|
|
|
|
|
a list of lines from the C result, one line per element. |
|
891
|
|
|
|
|
|
|
|
|
892
|
|
|
|
|
|
|
$on_stat->( @stat ) |
|
893
|
|
|
|
|
|
|
|
|
894
|
|
|
|
|
|
|
=item on_error => CODE |
|
895
|
|
|
|
|
|
|
|
|
896
|
|
|
|
|
|
|
Optional. Continuation to invoke on an error. |
|
897
|
|
|
|
|
|
|
|
|
898
|
|
|
|
|
|
|
$on_error->( $message ) |
|
899
|
|
|
|
|
|
|
|
|
900
|
|
|
|
|
|
|
=back |
|
901
|
|
|
|
|
|
|
|
|
902
|
|
|
|
|
|
|
The C<@stat> array will be passed a list of C references, each formed |
|
903
|
|
|
|
|
|
|
like |
|
904
|
|
|
|
|
|
|
|
|
905
|
|
|
|
|
|
|
=over 8 |
|
906
|
|
|
|
|
|
|
|
|
907
|
|
|
|
|
|
|
=item name => STRING |
|
908
|
|
|
|
|
|
|
|
|
909
|
|
|
|
|
|
|
The filename |
|
910
|
|
|
|
|
|
|
|
|
911
|
|
|
|
|
|
|
=item type => STRING |
|
912
|
|
|
|
|
|
|
|
|
913
|
|
|
|
|
|
|
A single character; C for files, C for directories |
|
914
|
|
|
|
|
|
|
|
|
915
|
|
|
|
|
|
|
=item size => INT |
|
916
|
|
|
|
|
|
|
|
|
917
|
|
|
|
|
|
|
The size in bytes |
|
918
|
|
|
|
|
|
|
|
|
919
|
|
|
|
|
|
|
=item mtime => INT |
|
920
|
|
|
|
|
|
|
|
|
921
|
|
|
|
|
|
|
The item's last modify timestamp, as a UNIX epoch time |
|
922
|
|
|
|
|
|
|
|
|
923
|
|
|
|
|
|
|
=item mode => INT |
|
924
|
|
|
|
|
|
|
|
|
925
|
|
|
|
|
|
|
The access mode, as a number |
|
926
|
|
|
|
|
|
|
|
|
927
|
|
|
|
|
|
|
=back |
|
928
|
|
|
|
|
|
|
|
|
929
|
|
|
|
|
|
|
If C is invoked on a file, then C<@stat> will contain a single reference |
|
930
|
|
|
|
|
|
|
to represent it. If invoked on a directory, the C<@stat> will start with a |
|
931
|
|
|
|
|
|
|
reference about the directory itself (whose name will be C<.>), then one per |
|
932
|
|
|
|
|
|
|
item in the directory, in the order the server returned the lines. |
|
933
|
|
|
|
|
|
|
|
|
934
|
|
|
|
|
|
|
=cut |
|
935
|
|
|
|
|
|
|
|
|
936
|
|
|
|
|
|
|
sub stat_parsed |
|
937
|
|
|
|
|
|
|
{ |
|
938
|
4
|
|
|
4
|
1
|
12711
|
my $self = shift; |
|
939
|
4
|
|
|
|
|
17
|
my %args = @_; |
|
940
|
|
|
|
|
|
|
|
|
941
|
4
|
50
|
|
|
|
16
|
defined $args{path} or croak "Expected 'path'"; |
|
942
|
|
|
|
|
|
|
|
|
943
|
4
|
50
|
66
|
|
|
22
|
my $on_stat = $args{on_stat} or defined wantarray or croak "Expected 'on_stat'"; |
|
944
|
|
|
|
|
|
|
|
|
945
|
4
|
|
|
|
|
838
|
require File::Listing; |
|
946
|
|
|
|
|
|
|
|
|
947
|
4
|
|
|
|
|
12233
|
my $on_error = $args{on_error}; |
|
948
|
4
|
100
|
50
|
0
|
|
29
|
$on_error ||= sub { die "Error $_[0] during stat_parsed" } if !defined wantarray; |
|
|
0
|
|
|
|
|
0
|
|
|
949
|
|
|
|
|
|
|
|
|
950
|
|
|
|
|
|
|
my $f = $self->stat( |
|
951
|
|
|
|
|
|
|
path => $args{path}, |
|
952
|
|
|
|
|
|
|
)->then( sub { |
|
953
|
4
|
|
|
4
|
|
463
|
my @statlines = @_; |
|
954
|
|
|
|
|
|
|
|
|
955
|
4
|
|
|
|
|
9
|
my @pstats; |
|
956
|
|
|
|
|
|
|
|
|
957
|
4
|
100
|
|
|
|
12
|
if( @statlines > 1 ) { |
|
958
|
|
|
|
|
|
|
# path is a directory. In that case, look for the . item |
|
959
|
|
|
|
|
|
|
# This would be easy only File::Listing::parse_dir WILL |
|
960
|
|
|
|
|
|
|
# ignore it and we don't get a say in the matter. |
|
961
|
|
|
|
|
|
|
# In this case, we'll do a bit of cheating. We'll look for the |
|
962
|
|
|
|
|
|
|
# "." line ourselves, mangle its name to "DIR", and mangle it |
|
963
|
|
|
|
|
|
|
# back on the other end. |
|
964
|
|
|
|
|
|
|
|
|
965
|
2
|
|
|
|
|
4
|
my @lines_with_cwd; |
|
966
|
|
|
|
|
|
|
my @lines_without_cwd; |
|
967
|
|
|
|
|
|
|
|
|
968
|
2
|
|
|
|
|
4
|
foreach ( @statlines ) { |
|
969
|
6
|
100
|
|
|
|
26
|
m/ \.$/ ? ( push @lines_with_cwd, $_ ) : ( push @lines_without_cwd, $_ ); |
|
970
|
|
|
|
|
|
|
} |
|
971
|
|
|
|
|
|
|
|
|
972
|
2
|
50
|
|
|
|
7
|
@lines_with_cwd == 1 or |
|
973
|
|
|
|
|
|
|
return $on_error->( "Did not find '.' in LIST output on directory $args{path}" ); |
|
974
|
|
|
|
|
|
|
|
|
975
|
2
|
|
|
|
|
5
|
my $l = $lines_with_cwd[0]; |
|
976
|
2
|
|
|
|
|
8
|
$l =~ s/ \.$/ DIR/; |
|
977
|
|
|
|
|
|
|
|
|
978
|
2
|
|
|
|
|
12
|
( my $cwdstat ) = File::Listing::parse_dir( $l ); |
|
979
|
|
|
|
|
|
|
|
|
980
|
2
|
50
|
|
|
|
499
|
$cwdstat->[0] eq "DIR" or |
|
981
|
|
|
|
|
|
|
return $on_error->( "Parsed listing did not contain DIR as the name like we expected for $args{path}" ); |
|
982
|
|
|
|
|
|
|
|
|
983
|
2
|
|
|
|
|
5
|
$cwdstat->[0] = "."; |
|
984
|
|
|
|
|
|
|
|
|
985
|
2
|
|
|
|
|
9
|
@pstats = ( $cwdstat, File::Listing::parse_dir( \@lines_without_cwd ) ); |
|
986
|
|
|
|
|
|
|
} |
|
987
|
|
|
|
|
|
|
else { |
|
988
|
2
|
|
|
|
|
16
|
@pstats = File::Listing::parse_dir( $statlines[0] ); |
|
989
|
|
|
|
|
|
|
} |
|
990
|
|
|
|
|
|
|
|
|
991
|
|
|
|
|
|
|
# We want to present a HASH refs, as they're nicer to work with |
|
992
|
4
|
|
|
|
|
1177
|
foreach ( @pstats ) { |
|
993
|
8
|
|
|
|
|
12
|
my %h; |
|
994
|
8
|
|
|
|
|
44
|
@h{qw( name type size mtime mode )} = @$_; |
|
995
|
8
|
|
|
|
|
27
|
$_ = \%h; |
|
996
|
|
|
|
|
|
|
} |
|
997
|
|
|
|
|
|
|
|
|
998
|
4
|
|
|
|
|
19
|
return Future->new->done( @pstats ); |
|
999
|
4
|
|
|
|
|
20
|
}); |
|
1000
|
|
|
|
|
|
|
|
|
1001
|
4
|
100
|
|
|
|
224
|
$f->on_done( $on_stat ) if $on_stat; |
|
1002
|
4
|
100
|
|
|
|
32
|
$f->on_fail( $on_error ) if $on_error; |
|
1003
|
|
|
|
|
|
|
|
|
1004
|
4
|
100
|
|
|
|
38
|
return $f if defined wantarray; |
|
1005
|
2
|
|
|
2
|
|
13
|
$f->on_ready( sub { undef $f } ); # Intentional cycle |
|
|
2
|
|
|
|
|
242
|
|
|
1006
|
|
|
|
|
|
|
} |
|
1007
|
|
|
|
|
|
|
|
|
1008
|
|
|
|
|
|
|
=head2 $ftp->stor( %args ) ==> () |
|
1009
|
|
|
|
|
|
|
|
|
1010
|
|
|
|
|
|
|
Stores a file on the remote server. Takes the following arguments |
|
1011
|
|
|
|
|
|
|
|
|
1012
|
|
|
|
|
|
|
=over 8 |
|
1013
|
|
|
|
|
|
|
|
|
1014
|
|
|
|
|
|
|
=item path => STRING |
|
1015
|
|
|
|
|
|
|
|
|
1016
|
|
|
|
|
|
|
Path to file to store |
|
1017
|
|
|
|
|
|
|
|
|
1018
|
|
|
|
|
|
|
=item data => STRING |
|
1019
|
|
|
|
|
|
|
|
|
1020
|
|
|
|
|
|
|
New contents for the file |
|
1021
|
|
|
|
|
|
|
|
|
1022
|
|
|
|
|
|
|
=item on_stored => CODE |
|
1023
|
|
|
|
|
|
|
|
|
1024
|
|
|
|
|
|
|
Optional when returning a future. Continuation to invoke on success. |
|
1025
|
|
|
|
|
|
|
|
|
1026
|
|
|
|
|
|
|
$on_stored->() |
|
1027
|
|
|
|
|
|
|
|
|
1028
|
|
|
|
|
|
|
=item on_error => CODE |
|
1029
|
|
|
|
|
|
|
|
|
1030
|
|
|
|
|
|
|
Optional. Continuation to invoke on an error. |
|
1031
|
|
|
|
|
|
|
|
|
1032
|
|
|
|
|
|
|
$on_error->( $message ) |
|
1033
|
|
|
|
|
|
|
|
|
1034
|
|
|
|
|
|
|
=back |
|
1035
|
|
|
|
|
|
|
|
|
1036
|
|
|
|
|
|
|
=cut |
|
1037
|
|
|
|
|
|
|
|
|
1038
|
|
|
|
|
|
|
sub stor |
|
1039
|
|
|
|
|
|
|
{ |
|
1040
|
2
|
|
|
2
|
1
|
856
|
my $self = shift; |
|
1041
|
2
|
|
|
|
|
11
|
my %args = @_; |
|
1042
|
|
|
|
|
|
|
|
|
1043
|
2
|
|
|
|
|
6
|
my $path = $args{path}; |
|
1044
|
2
|
50
|
|
|
|
9
|
defined $path or croak "Expected 'path'"; |
|
1045
|
|
|
|
|
|
|
|
|
1046
|
2
|
|
|
|
|
5
|
my $data = $args{data}; |
|
1047
|
2
|
50
|
|
|
|
7
|
defined $data or croak "Expected 'data'"; |
|
1048
|
|
|
|
|
|
|
|
|
1049
|
2
|
50
|
66
|
|
|
23
|
my $on_stored = $args{on_stored} or defined wantarray or croak "Expected 'on_stored'"; |
|
1050
|
|
|
|
|
|
|
|
|
1051
|
2
|
|
|
|
|
5
|
my $on_error = $args{on_error}; |
|
1052
|
2
|
100
|
50
|
0
|
|
15
|
$on_error ||= sub { die "Error $_[0] during STOR" } if !defined wantarray; |
|
|
0
|
|
|
|
|
0
|
|
|
1053
|
|
|
|
|
|
|
|
|
1054
|
2
|
|
|
|
|
12
|
my $f = $self->_do_command_send_dataconn( |
|
1055
|
|
|
|
|
|
|
"STOR $path", |
|
1056
|
|
|
|
|
|
|
$data, |
|
1057
|
|
|
|
|
|
|
); |
|
1058
|
|
|
|
|
|
|
|
|
1059
|
2
|
100
|
|
|
|
11
|
$f->on_done( $on_stored ) if $on_stored; |
|
1060
|
2
|
100
|
|
|
|
18
|
$f->on_fail( $on_error ) if $on_error; |
|
1061
|
|
|
|
|
|
|
|
|
1062
|
2
|
|
|
|
|
14
|
return $f; |
|
1063
|
|
|
|
|
|
|
} |
|
1064
|
|
|
|
|
|
|
|
|
1065
|
|
|
|
|
|
|
=head1 SEE ALSO |
|
1066
|
|
|
|
|
|
|
|
|
1067
|
|
|
|
|
|
|
=over 4 |
|
1068
|
|
|
|
|
|
|
|
|
1069
|
|
|
|
|
|
|
=item * |
|
1070
|
|
|
|
|
|
|
|
|
1071
|
|
|
|
|
|
|
L - FILE TRANSFER PROTOCOL (FTP) |
|
1072
|
|
|
|
|
|
|
|
|
1073
|
|
|
|
|
|
|
=back |
|
1074
|
|
|
|
|
|
|
|
|
1075
|
|
|
|
|
|
|
=head1 AUTHOR |
|
1076
|
|
|
|
|
|
|
|
|
1077
|
|
|
|
|
|
|
Paul Evans |
|
1078
|
|
|
|
|
|
|
|
|
1079
|
|
|
|
|
|
|
=cut |
|
1080
|
|
|
|
|
|
|
|
|
1081
|
|
|
|
|
|
|
0x55AA; |