File Coverage

blib/lib/Net/SFTP/Foreign.pm
Criterion Covered Total %
statement 94 1861 5.0
branch 13 1352 0.9
condition 7 721 0.9
subroutine 28 153 18.3
pod 48 49 97.9
total 190 4136 4.5


line stmt bran cond sub pod time code
1             package Net::SFTP::Foreign;
2              
3             our $VERSION = '1.92_02';
4              
5 3     3   143179 use strict;
  3         24  
  3         92  
6 3     3   17 use warnings;
  3         5  
  3         76  
7 3     3   15 use warnings::register;
  3         8  
  3         495  
8              
9 3     3   23 use Carp qw(carp croak);
  3         4  
  3         161  
10              
11 3     3   1591 use Symbol ();
  3         2569  
  3         70  
12 3     3   1403 use Errno ();
  3         4219  
  3         78  
13 3     3   21 use Fcntl;
  3         5  
  3         764  
14 3     3   34 use File::Spec ();
  3         7  
  3         57  
15 3     3   1687 use Time::HiRes ();
  3         4540  
  3         75  
16 3     3   1570 use POSIX ();
  3         19466  
  3         333  
17              
18             BEGIN {
19 3 50   3   21 if ($] >= 5.008) {
20 3         1812 require Encode;
21             }
22             else {
23             # Work around for incomplete Unicode handling in perl 5.6.x
24 0         0 require bytes;
25 0         0 bytes->import();
26 0         0 *Encode::encode = sub { $_[1] };
  0         0  
27 0         0 *Encode::decode = sub { $_[1] };
  0         0  
28 0         0 *utf8::downgrade = sub { 1 };
  0         0  
29             }
30             }
31              
32             # we make $Net::SFTP::Foreign::Helpers::debug an alias for
33             # $Net::SFTP::Foreign::debug so that the user can set it without
34             # knowing anything about the Helpers package!
35             our $debug;
36 3     3   32229 BEGIN { *Net::SFTP::Foreign::Helpers::debug = \$debug };
37 3         337 use Net::SFTP::Foreign::Helpers qw(_is_reg _is_lnk _is_dir _debug
38             _sort_entries _gen_wanted
39             _gen_converter _hexdump
40             _ensure_list _catch_tainted_args
41             _file_part _umask_save_and_set
42 3     3   1504 _untaint);
  3         8  
43 3         1655 use Net::SFTP::Foreign::Constants qw( :fxp :flags :att
44             :status :error
45 3     3   1474 SSH2_FILEXFER_VERSION );
  3         9  
46 3     3   1543 use Net::SFTP::Foreign::Attributes;
  3         7  
  3         99  
47 3     3   21 use Net::SFTP::Foreign::Buffer;
  3         6  
  3         341  
48             require Net::SFTP::Foreign::Common;
49             our @ISA = qw(Net::SFTP::Foreign::Common);
50              
51             our $dirty_cleanup;
52             my $windows;
53              
54             BEGIN {
55 3     3   19 $windows = $^O =~ /Win(?:32|64)/;
56              
57 3 50       410 if ($^O =~ /solaris/i) {
58 0 0       0 $dirty_cleanup = 1 unless defined $dirty_cleanup;
59             }
60             }
61              
62             my $thread_generation = 1;
63 0     0   0 sub CLONE { $thread_generation++ }
64              
65             sub _deprecated {
66 0 0 0 0   0 if (warnings::enabled('deprecated') and warnings::enabled(__PACKAGE__)) {
67 0         0 Carp::carp(join('', @_));
68             }
69             }
70              
71 0     0   0 sub _next_msg_id { shift->{_msg_id}++ }
72              
73 3     3   33 use constant _empty_attributes => Net::SFTP::Foreign::Attributes->new;
  3         7  
  3         15  
74              
75             sub _queue_new_msg {
76 0     0   0 my $sftp = shift;
77 0         0 my $code = shift;
78 0         0 my $id = $sftp->_next_msg_id;
79 0         0 $sftp->{incomming}{$id} = undef;
80 0         0 my $msg = Net::SFTP::Foreign::Buffer->new(int8 => $code, int32 => $id, @_);
81 0         0 $sftp->_queue_msg($msg);
82 0         0 return $id;
83             }
84              
85             sub _queue_msg {
86 0     0   0 my ($sftp, $buf) = @_;
87              
88 0         0 my $bytes = $buf->bytes;
89 0         0 my $len = length $bytes;
90              
91 0 0 0     0 if ($debug and $debug & 1) {
92 0         0 $sftp->{_queued}++;
93 0         0 _debug(sprintf("queueing msg len: %i, code:%i, id:%i ... [$sftp->{_queued}]",
94             $len, unpack(CN => $bytes)));
95              
96 0 0       0 $debug & 16 and _hexdump(pack('N', length($bytes)) . $bytes);
97             }
98              
99 0         0 $sftp->{_bout} .= pack('N', length($bytes));
100 0         0 $sftp->{_bout} .= $bytes;
101             }
102              
103              
104 0     0   0 sub _do_io { $_[0]->{_backend}->_do_io(@_) }
105              
106             sub _conn_lost {
107 0     0   0 my ($sftp, $status, $err, @str) = @_;
108              
109 0 0 0     0 $debug and $debug & 32 and _debug("_conn_lost");
110              
111             $sftp->{_status} or
112 0 0       0 $sftp->_set_status(defined $status ? $status : SSH2_FX_CONNECTION_LOST);
    0          
113              
114             $sftp->{_error} or
115 0 0       0 $sftp->_set_error((defined $err ? $err : SFTP_ERR_CONNECTION_BROKEN),
    0          
    0          
116             (@str ? @str : "Connection to remote server is broken"));
117              
118 0         0 undef $sftp->{_connected};
119             }
120              
121             sub _conn_failed {
122 0     0   0 my $sftp = shift;
123             $sftp->_conn_lost(SSH2_FX_NO_CONNECTION,
124             SFTP_ERR_CONNECTION_BROKEN,
125             @_)
126 0 0       0 unless $sftp->{_error};
127             }
128              
129             sub _get_msg {
130 0     0   0 my $sftp = shift;
131              
132 0 0 0     0 $debug and $debug & 1 and _debug("waiting for message... [$sftp->{_queued}]");
133              
134 0 0       0 unless ($sftp->_do_io($sftp->{_timeout})) {
135 0         0 $sftp->_conn_lost(undef, undef, "Connection to remote server stalled");
136 0         0 return undef;
137             }
138              
139 0         0 my $bin = \$sftp->{_bin};
140 0         0 my $len = unpack N => substr($$bin, 0, 4, '');
141 0         0 my $msg = Net::SFTP::Foreign::Buffer->make(substr($$bin, 0, $len, ''));
142              
143 0 0 0     0 if ($debug and $debug & 1) {
144 0         0 $sftp->{_queued}--;
145 0         0 my ($code, $id, $status) = unpack( CNN => $$msg);
146 0 0       0 $id = '-' if $code == SSH2_FXP_VERSION;
147 0 0       0 $status = '-' unless $code == SSH2_FXP_STATUS;
148 0         0 _debug(sprintf("got it!, len:%i, code:%i, id:%s, status: %s",
149             $len, $code, $id, $status));
150 0 0       0 $debug & 8 and _hexdump($$msg);
151             }
152              
153 0         0 return $msg;
154             }
155              
156             sub _croak_bad_options {
157 0 0   0   0 if (@_) {
158 0 0       0 my $s = (@_ > 1 ? 's' : '');
159 0         0 croak "Invalid option$s '" . CORE::join("', '", @_) . "' or bad combination of options";
160             }
161             }
162              
163             sub _fs_encode {
164 0     0   0 my ($sftp, $path) = @_;
165 0         0 Encode::encode($sftp->{_fs_encoding}, $path);
166             }
167              
168             sub _fs_decode {
169 0     0   0 my ($sftp, $path) = @_;
170 0         0 Encode::decode($sftp->{_fs_encoding}, $path);
171             }
172              
173             sub new {
174 0 0   0 1 0 ${^TAINT} and &_catch_tainted_args;
175              
176 0         0 my $class = shift;
177 0 0       0 unshift @_, 'host' if @_ & 1;
178 0         0 my %opts = @_;
179              
180 0         0 my $sftp = { _msg_id => 0,
181             _bout => '',
182             _bin => '',
183             _connected => 1,
184             _queued => 0,
185             _error => 0,
186             _status => 0,
187             _incomming => {} };
188              
189 0         0 bless $sftp, $class;
190              
191 0 0       0 if ($debug) {
192 0         0 _debug "This is Net::SFTP::Foreign $Net::SFTP::Foreign::VERSION";
193 0         0 _debug "Loaded from $INC{'Net/SFTP/Foreign.pm'}";
194 0         0 _debug "Running on Perl $^V for $^O";
195 0         0 _debug "debug set to $debug";
196 0         0 _debug "~0 is " . ~0;
197             }
198              
199 0         0 $sftp->_clear_error_and_status;
200              
201 0         0 my $backend = delete $opts{backend};
202 0 0       0 unless (ref $backend) {
203 0 0       0 $backend = ($windows ? 'Windows' : 'Unix')
    0          
204             unless (defined $backend);
205 0 0       0 $backend =~ /^\w+$/
206             or croak "Bad backend name $backend";
207 0         0 my $backend_class = "Net::SFTP::Foreign::Backend::$backend";
208 0 0       0 eval "require $backend_class; 1"
209             or croak "Unable to load backend $backend: $@";
210 0         0 $backend = $backend_class->_new($sftp, \%opts);
211             }
212 0         0 $sftp->{_backend} = $backend;
213              
214 0 0       0 if ($debug) {
215 0   0     0 my $class = ref($backend) || $backend;
216 3     3   26 no strict 'refs';
  3         7  
  3         17378  
217 0   0     0 my $version = ${$class .'::VERSION'} || 0;
218 0         0 _debug "Using backend $class $version";
219             }
220              
221 0         0 my %defs = $backend->_defaults;
222              
223 0         0 $sftp->{_autodie} = delete $opts{autodie};
224 0   0     0 $sftp->{_block_size} = delete $opts{block_size} || $defs{block_size} || 32*1024;
225 0   0     0 $sftp->{_min_block_size} = delete $opts{min_block_size} || $defs{min_block_size} || 512;
226 0   0     0 $sftp->{_queue_size} = delete $opts{queue_size} || $defs{queue_size} || 32;
227 0   0     0 $sftp->{_read_ahead} = $defs{read_ahead} || $sftp->{_block_size} * 4;
228 0   0     0 $sftp->{_write_delay} = $defs{write_delay} || $sftp->{_block_size} * 8;
229 0         0 $sftp->{_autoflush} = delete $opts{autoflush};
230 0         0 $sftp->{_late_set_perm} = delete $opts{late_set_perm};
231 0         0 $sftp->{_dirty_cleanup} = delete $opts{dirty_cleanup};
232 0         0 $sftp->{_remote_has_volumes} = delete $opts{remote_has_volumes};
233              
234 0         0 $sftp->{_timeout} = delete $opts{timeout};
235 0 0 0     0 defined $sftp->{_timeout} and $sftp->{_timeout} <= 0 and croak "invalid timeout";
236              
237 0         0 $sftp->{_fs_encoding} = delete $opts{fs_encoding};
238 0 0       0 if (defined $sftp->{_fs_encoding}) {
239 0 0       0 $] < 5.008
240             and carp "fs_encoding feature is not supported in this perl version $]";
241             }
242             else {
243 0         0 $sftp->{_fs_encoding} = 'utf8';
244             }
245              
246 0         0 $sftp->autodisconnect(delete $opts{autodisconnect});
247              
248 0         0 $backend->_init_transport($sftp, \%opts);
249 0 0       0 %opts and _croak_bad_options(keys %opts);
250              
251 0 0       0 $sftp->_init unless $sftp->{_error};
252 0         0 $backend->_after_init($sftp);
253 0         0 $sftp
254             }
255              
256             sub autodisconnect {
257 0     0 1 0 my ($sftp, $ad) = @_;
258 0 0 0     0 if (not defined $ad or $ad == 2) {
259 0 0 0     0 $debug and $debug & 4 and _debug "setting disconnecting pid to $$ and thread to $thread_generation";
260 0         0 $sftp->{_disconnect_by_pid} = $$;
261 0         0 $sftp->{_disconnect_by_thread} = $thread_generation;
262             }
263             else {
264 0         0 delete $sftp->{_disconnect_by_thread};
265 0 0       0 if ($ad == 0) {
    0          
266 0         0 $sftp->{_disconnect_by_pid} = -1;
267             }
268             elsif ($ad == 1) {
269 0         0 delete $sftp->{_disconnect_by_pid};
270             }
271             else {
272 0         0 croak "bad value '$ad' for autodisconnect";
273             }
274             }
275 0         0 1;
276             }
277              
278             sub disconnect {
279 0     0 1 0 my $sftp = shift;
280 0         0 my $pid = delete $sftp->{pid};
281              
282 0 0 0     0 $debug and $debug & 4 and _debug("$sftp->disconnect called (ssh pid: ".($pid||'').")");
      0        
283              
284 0         0 local $sftp->{_autodie};
285 0         0 $sftp->_conn_lost;
286              
287 0 0       0 if (defined $pid) {
288 0 0 0     0 close $sftp->{ssh_out} if (defined $sftp->{ssh_out} and not $sftp->{_ssh_out_is_not_dupped});
289 0 0       0 close $sftp->{ssh_in} if defined $sftp->{ssh_in};
290 0 0       0 if ($windows) {
291 0 0       0 kill KILL => $pid
292             and waitpid($pid, 0);
293 0 0 0     0 $debug and $debug & 4 and _debug "process $pid reaped";
294             }
295             else {
296             my $dirty = ( defined $sftp->{_dirty_cleanup}
297             ? $sftp->{_dirty_cleanup}
298 0 0       0 : $dirty_cleanup );
299              
300 0 0 0     0 if ($dirty or not defined $dirty) {
301 0 0 0     0 $debug and $debug & 4 and _debug("starting dirty cleanup of process $pid");
302 0 0       0 OUT: for my $sig (($dirty ? () : 0), qw(TERM TERM KILL KILL)) {
303 0 0 0     0 $debug and $debug & 4 and _debug("killing process $pid with signal $sig");
304 0 0       0 $sig and kill $sig, $pid;
305              
306 0         0 local ($@, $SIG{__DIE__}, $SIG{__WARN__});
307 0         0 my $deadline = Time::HiRes::time + 8;
308 0         0 my $dt = 0.01;
309 0         0 while (Time::HiRes::time < $deadline) {
310 0         0 my $wpr = waitpid($pid, POSIX::WNOHANG());
311 0 0 0     0 $debug and $debug & 4 and _debug("waitpid returned ", $wpr);
312 0 0 0     0 last OUT if $wpr or $! == Errno::ECHILD();
313 0         0 Time::HiRes::sleep($dt);
314 0         0 $dt *= 1.2;
315             }
316             }
317             }
318             else {
319 0         0 while (1) {
320 0 0       0 last if waitpid($pid, 0) > 0;
321 0 0       0 if ($! != Errno::EINTR()) {
322 0 0       0 warn "internal error: unexpected error in waitpid($pid): $!"
323             if $! != Errno::ECHILD();
324 0         0 last;
325             }
326             }
327             }
328 0 0 0     0 $debug and $debug & 4 and _debug "process $pid reaped";
329             }
330             }
331 0 0       0 close $sftp->{_pty} if defined $sftp->{_pty};
332 0         0 1
333             }
334              
335             sub DESTROY {
336 0     0   0 local ($?, $!, $@);
337              
338 0         0 my $sftp = shift;
339 0         0 my $dbpid = $sftp->{_disconnect_by_pid};
340 0         0 my $dbthread = $sftp->{_disconnect_by_thread};
341              
342 0 0 0     0 $debug and $debug & 4 and _debug("$sftp->DESTROY called (current pid: $$, disconnect_by_pid: " .
      0        
      0        
343             ($dbpid || '') .
344             "), current thread generation: $thread_generation, disconnect_by_thread: " .
345             ($dbthread || '') . ")");
346              
347 0 0 0     0 if (!defined $dbpid or ($dbpid == $$ and $dbthread == $thread_generation)) {
      0        
348 0         0 $sftp->disconnect
349             }
350             else {
351 0 0 0     0 $debug and $debug & 4 and _debug "skipping disconnection because pid and/or thread generation don't match";
352             }
353             }
354              
355             sub _init {
356 0     0   0 my $sftp = shift;
357 0         0 $sftp->_queue_msg( Net::SFTP::Foreign::Buffer->new(int8 => SSH2_FXP_INIT,
358             int32 => SSH2_FILEXFER_VERSION));
359              
360 0 0 0     0 if (my $msg = $sftp->_get_msg) {
    0 0        
361 0         0 my $type = $msg->get_int8;
362 0 0       0 if ($type == SSH2_FXP_VERSION) {
363 0         0 my $version = $msg->get_int32;
364              
365 0         0 $sftp->{server_version} = $version;
366 0         0 $sftp->{server_extensions} = {};
367 0         0 while (length $$msg) {
368 0         0 my $key = $msg->get_str;
369 0         0 my $value = $msg->get_str;
370 0         0 $sftp->{server_extensions}{$key} = $value;
371              
372 0 0       0 if ($key eq 'vendor-id') {
    0          
373 0         0 my $vid = Net::SFTP::Foreign::Buffer->make("$value");
374 0         0 $sftp->{_ext__vendor_id} = [ Encode::decode(utf8 => $vid->get_str),
375             Encode::decode(utf8 => $vid->get_str),
376             Encode::decode(utf8 => $vid->get_str),
377             $vid->get_int64 ];
378             }
379             elsif ($key eq 'supported2') {
380 0         0 my $s2 = Net::SFTP::Foreign::Buffer->make("$value");
381 0         0 $sftp->{_ext__supported2} = [ $s2->get_int32,
382             $s2->get_int32,
383             $s2->get_int32,
384             $s2->get_int32,
385             $s2->get_int32,
386             $s2->get_int16,
387             $s2->get_int16,
388             [map Encode::decode(utf8 => $_), $s2->get_str_list],
389             [map Encode::decode(utf8 => $_), $s2->get_str_list] ];
390             }
391             }
392              
393 0         0 return $version;
394             }
395              
396 0         0 $sftp->_conn_lost(SSH2_FX_BAD_MESSAGE,
397             SFTP_ERR_REMOTE_BAD_MESSAGE,
398             "bad packet type, expecting SSH2_FXP_VERSION, got $type");
399             }
400             elsif ($sftp->{_status} == SSH2_FX_CONNECTION_LOST
401             and $sftp->{_password_authentication}
402             and $sftp->{_password_sent}) {
403 0         0 $sftp->_set_error(SFTP_ERR_PASSWORD_AUTHENTICATION_FAILED,
404             "Password authentication failed or connection lost");
405             }
406 0         0 return undef;
407             }
408              
409 0     0 0 0 sub server_extensions { %{shift->{server_extensions}} }
  0         0  
410              
411             sub _check_extension {
412 0     0   0 my ($sftp, $name, $version, $error, $errstr) = @_;
413 0         0 my $ext = $sftp->{server_extensions}{$name};
414 0 0 0     0 return 1 if (defined $ext and $ext == $version);
415              
416 0         0 $sftp->_set_status(SSH2_FX_OP_UNSUPPORTED);
417 0         0 $sftp->_set_error($error, "$errstr: extended operation not supported by server");
418 0         0 return undef;
419             }
420              
421             # helper methods:
422              
423             sub _get_msg_by_id {
424 0     0   0 my ($sftp, $eid) = @_;
425 0         0 while (1) {
426 0   0     0 my $msg = delete($sftp->{incomming}{$eid}) || $sftp->_get_msg || return undef;
427 0         0 my $id = unpack xN => $$msg;
428 0 0       0 return $msg if $id == $eid;
429 0 0       0 unless (exists $sftp->{incomming}{$id}) {
430 0         0 $sftp->_conn_lost(SSH2_FX_BAD_MESSAGE,
431             SFTP_ERR_REMOTE_BAD_MESSAGE,
432             $_[2], "bad packet sequence, expected $eid, got $id");
433 0         0 return undef;
434             }
435 0         0 $sftp->{incomming}{$id} = $msg
436             }
437             }
438              
439             sub _get_msg_and_check {
440 0     0   0 my ($sftp, $etype, $eid, $err, $errstr) = @_;
441 0         0 my $msg = $sftp->_get_msg_by_id($eid, $errstr);
442 0 0       0 if ($msg) {
443 0         0 my $type = $msg->get_int8;
444 0         0 $msg->get_int32; # discard id, it has already been checked at _get_msg_by_id
445              
446 0         0 $sftp->_clear_error_and_status;
447              
448 0 0       0 if ($type != $etype) {
449 0 0       0 if ($type == SSH2_FXP_STATUS) {
450 0         0 my $code = $msg->get_int32;
451 0         0 my $str = Encode::decode(utf8 => $msg->get_str);
452 0 0       0 my $status = $sftp->_set_status($code, (defined $str ? $str : ()));
453 0         0 $sftp->_set_error($err, $errstr, $status);
454             }
455             else {
456 0         0 $sftp->_conn_lost(SSH2_FX_BAD_MESSAGE,
457             SFTP_ERR_REMOTE_BAD_MESSAGE,
458             $errstr, "bad packet type, expected $etype packet, got $type");
459             }
460 0         0 return undef;
461             }
462             }
463 0         0 $msg;
464             }
465              
466             # reads SSH2_FXP_HANDLE packet and returns handle, or undef on failure
467             sub _get_handle {
468 0     0   0 my ($sftp, $eid, $error, $errstr) = @_;
469 0 0       0 if (my $msg = $sftp->_get_msg_and_check(SSH2_FXP_HANDLE, $eid,
470             $error, $errstr)) {
471 0         0 return $msg->get_str;
472             }
473 0         0 return undef;
474             }
475              
476             sub _rid {
477 0     0   0 my ($sftp, $rfh) = @_;
478 0         0 my $rid = $rfh->_rid;
479 0 0       0 unless (defined $rid) {
480 0         0 $sftp->_set_error(SFTP_ERR_REMOTE_ACCESING_CLOSED_FILE,
481             "Couldn't access a file that has been previosly closed");
482             }
483             $rid
484 0         0 }
485              
486             sub _rfid {
487 0     0   0 $_[1]->_check_is_file;
488 0         0 &_rid;
489             }
490              
491             sub _rdid {
492 0     0   0 $_[1]->_check_is_dir;
493 0         0 &_rid;
494             }
495              
496             sub _queue_rid_request {
497 0     0   0 my ($sftp, $code, $fh, $attrs) = @_;
498 0         0 my $rid = $sftp->_rid($fh);
499 0 0       0 return undef unless defined $rid;
500              
501 0 0       0 $sftp->_queue_new_msg($code, str => $rid,
502             (defined $attrs ? (attr => $attrs) : ()));
503             }
504              
505             sub _queue_rfid_request {
506 0     0   0 $_[2]->_check_is_file;
507 0         0 &_queue_rid_request;
508             }
509              
510             sub _queue_rdid_request {
511 0     0   0 $_[2]->_check_is_dir;
512 0         0 &_queue_rid_request;
513             }
514              
515             sub _queue_str_request {
516 0     0   0 my($sftp, $code, $str, $attrs) = @_;
517 0 0       0 $sftp->_queue_new_msg($code, str => $str,
518             (defined $attrs ? (attr => $attrs) : ()));
519             }
520              
521             sub _check_status_ok {
522 0     0   0 my ($sftp, $eid, $error, $errstr) = @_;
523 0 0       0 if (defined $eid) {
524 0 0       0 if (my $msg = $sftp->_get_msg_and_check(SSH2_FXP_STATUS, $eid,
525             $error, $errstr)) {
526 0         0 my $status = $sftp->_set_status($msg->get_int32, $msg->get_str);
527 0 0       0 return 1 if $status == SSH2_FX_OK;
528              
529 0         0 $sftp->_set_error($error, $errstr, $status);
530             }
531             }
532 0         0 return undef;
533             }
534              
535             sub setcwd {
536 0 0   0 1 0 ${^TAINT} and &_catch_tainted_args;
537              
538 0         0 my ($sftp, $cwd, %opts) = @_;
539 0         0 $sftp->_clear_error_and_status;
540              
541 0         0 my $check = delete $opts{check};
542 0 0       0 $check = 1 unless defined $check;
543              
544 0 0       0 %opts and _croak_bad_options(keys %opts);
545              
546 0 0       0 if (defined $cwd) {
547 0 0       0 if ($check) {
548 0         0 $cwd = $sftp->realpath($cwd);
549 0 0       0 return undef unless defined $cwd;
550 0         0 _untaint($cwd);
551 0 0       0 my $a = $sftp->stat($cwd)
552             or return undef;
553 0 0       0 unless (_is_dir($a->perm)) {
554 0         0 $sftp->_set_error(SFTP_ERR_REMOTE_BAD_OBJECT,
555             "Remote object '$cwd' is not a directory");
556 0         0 return undef;
557             }
558             }
559             else {
560 0         0 $cwd = $sftp->_rel2abs($cwd);
561             }
562 0         0 return $sftp->{cwd} = $cwd;
563             }
564             else {
565 0         0 delete $sftp->{cwd};
566 0 0       0 return $sftp->cwd if defined wantarray;
567             }
568             }
569              
570             sub cwd {
571 0 0   0 1 0 @_ == 1 or croak 'Usage: $sftp->cwd()';
572              
573 0         0 my $sftp = shift;
574 0 0       0 return defined $sftp->{cwd} ? $sftp->{cwd} : $sftp->realpath('');
575             }
576              
577             ## SSH2_FXP_OPEN (3)
578             # returns handle on success, undef on failure
579             sub open {
580 0 0 0 0 1 0 (@_ >= 2 and @_ <= 4)
581             or croak 'Usage: $sftp->open($path [, $flags [, $attrs]])';
582 0 0       0 ${^TAINT} and &_catch_tainted_args;
583              
584 0         0 my ($sftp, $path, $flags, $a) = @_;
585 0         0 $path = $sftp->_rel2abs($path);
586 0 0       0 defined $flags or $flags = SSH2_FXF_READ;
587 0 0       0 defined $a or $a = Net::SFTP::Foreign::Attributes->new;
588 0         0 my $id = $sftp->_queue_new_msg(SSH2_FXP_OPEN,
589             str => $sftp->_fs_encode($path),
590             int32 => $flags, attr => $a);
591              
592 0         0 my $rid = $sftp->_get_handle($id,
593             SFTP_ERR_REMOTE_OPEN_FAILED,
594             "Couldn't open remote file '$path'");
595              
596 0 0 0     0 if ($debug and $debug & 2) {
597 0 0       0 if (defined $rid) {
598 0         0 _debug("new remote file '$path' open, rid:");
599 0         0 _hexdump($rid);
600             }
601             else {
602 0         0 _debug("open failed: $sftp->{_status}");
603             }
604             }
605              
606 0 0       0 defined $rid or return undef;
607              
608 0         0 my $fh = Net::SFTP::Foreign::FileHandle->_new_from_rid($sftp, $rid);
609 0 0       0 $fh->_flag(append => 1) if ($flags & SSH2_FXF_APPEND);
610              
611 0         0 $fh;
612             }
613              
614             sub _open_mkpath {
615 0     0   0 my ($sftp, $filename, $mkpath, $flags, $attrs) = @_;
616 0   0     0 $flags = ($flags || 0) | SSH2_FXF_WRITE|SSH2_FXF_CREAT;
617 0         0 my $fh = do {
618 0         0 local $sftp->{_autodie};
619 0         0 $sftp->open($filename, $flags, $attrs);
620             };
621 0 0       0 unless ($fh) {
622 0 0 0     0 if ($mkpath and $sftp->status == SSH2_FX_NO_SUCH_FILE) {
623 0         0 my $da = $attrs->clone;
624 0   0     0 $da->set_perm(($da->perm || 0) | 0700);
625 0 0       0 $sftp->mkpath($filename, $da, 1) or return;
626 0         0 $fh = $sftp->open($filename, $flags, $attrs);
627             }
628             else {
629 0         0 $sftp->_ok_or_autodie;
630             }
631             }
632 0         0 $fh;
633             }
634              
635             ## SSH2_FXP_OPENDIR (11)
636             sub opendir {
637 0 0   0 1 0 @_ <= 2 or croak 'Usage: $sftp->opendir($path)';
638 0 0       0 ${^TAINT} and &_catch_tainted_args;
639              
640 0         0 my $sftp = shift;
641 0         0 my $path = shift;
642 0 0       0 $path = '.' unless defined $path;
643 0         0 $path = $sftp->_rel2abs($path);
644 0         0 my $id = $sftp->_queue_str_request(SSH2_FXP_OPENDIR, $sftp->_fs_encode($path), @_);
645 0         0 my $rid = $sftp->_get_handle($id, SFTP_ERR_REMOTE_OPENDIR_FAILED,
646             "Couldn't open remote dir '$path'");
647              
648 0 0 0     0 if ($debug and $debug & 2) {
649 0         0 _debug("new remote dir '$path' open, rid:");
650 0         0 _hexdump($rid);
651             }
652              
653 0 0       0 defined $rid
654             or return undef;
655              
656 0         0 Net::SFTP::Foreign::DirHandle->_new_from_rid($sftp, $rid, 0)
657             }
658              
659             ## SSH2_FXP_READ (4)
660             # returns data on success undef on failure
661             sub sftpread {
662 0 0 0 0 1 0 (@_ >= 3 and @_ <= 4)
663             or croak 'Usage: $sftp->sftpread($fh, $offset [, $size])';
664              
665 0         0 my ($sftp, $rfh, $offset, $size) = @_;
666              
667 0 0       0 unless ($size) {
668 0 0       0 return '' if defined $size;
669 0         0 $size = $sftp->{_block_size};
670             }
671              
672 0         0 my $rfid = $sftp->_rfid($rfh);
673 0 0       0 defined $rfid or return undef;
674              
675 0         0 my $id = $sftp->_queue_new_msg(SSH2_FXP_READ, str=> $rfid,
676             int64 => $offset, int32 => $size);
677              
678 0 0       0 if (my $msg = $sftp->_get_msg_and_check(SSH2_FXP_DATA, $id,
679             SFTP_ERR_REMOTE_READ_FAILED,
680             "Couldn't read from remote file")) {
681 0         0 return $msg->get_str;
682             }
683 0         0 return undef;
684             }
685              
686             ## SSH2_FXP_WRITE (6)
687             # returns true on success, undef on failure
688             sub sftpwrite {
689 0 0   0 1 0 @_ == 4 or croak 'Usage: $sftp->sftpwrite($fh, $offset, $data)';
690              
691 0         0 my ($sftp, $rfh, $offset) = @_;
692 0         0 my $rfid = $sftp->_rfid($rfh);
693 0 0       0 defined $rfid or return undef;
694 0 0       0 utf8::downgrade($_[3], 1) or croak "wide characters found in data";
695              
696 0         0 my $id = $sftp->_queue_new_msg(SSH2_FXP_WRITE, str => $rfid,
697             int64 => $offset, str => $_[3]);
698              
699 0 0       0 if ($sftp->_check_status_ok($id,
700             SFTP_ERR_REMOTE_WRITE_FAILED,
701             "Couldn't write to remote file")) {
702 0         0 return 1;
703             }
704 0         0 return undef;
705             }
706              
707             sub seek {
708 0 0 0 0 1 0 (@_ >= 3 and @_ <= 4)
709             or croak 'Usage: $sftp->seek($fh, $pos [, $whence])';
710              
711 0         0 my ($sftp, $rfh, $pos, $whence) = @_;
712 0 0       0 $sftp->flush($rfh) or return undef;
713              
714 0 0       0 if (!$whence) {
    0          
    0          
715 0         0 $rfh->_pos($pos)
716             }
717             elsif ($whence == 1) {
718 0         0 $rfh->_inc_pos($pos)
719             }
720             elsif ($whence == 2) {
721 0 0       0 my $a = $sftp->stat($rfh) or return undef;
722 0         0 $rfh->_pos($pos + $a->size);
723             }
724             else {
725 0         0 croak "invalid value for whence argument ('$whence')";
726             }
727 0         0 1;
728             }
729              
730             sub tell {
731 0 0   0 1 0 @_ == 2 or croak 'Usage: $sftp->tell($fh)';
732              
733 0         0 my ($sftp, $rfh) = @_;
734 0         0 return $rfh->_pos + length ${$rfh->_bout};
  0         0  
735             }
736              
737             sub eof {
738 0 0   0 1 0 @_ == 2 or croak 'Usage: $sftp->eof($fh)';
739              
740 0         0 my ($sftp, $rfh) = @_;
741 0         0 $sftp->_fill_read_cache($rfh, 1);
742 0         0 return length(${$rfh->_bin}) == 0
  0         0  
743             }
744              
745             sub _write {
746 0     0   0 my ($sftp, $rfh, $off, $cb) = @_;
747              
748 0         0 $sftp->_clear_error_and_status;
749              
750 0         0 my $rfid = $sftp->_rfid($rfh);
751 0 0       0 defined $rfid or return undef;
752              
753 0         0 my $qsize = $sftp->{_queue_size};
754              
755 0         0 my @msgid;
756             my @written;
757 0         0 my $written = 0;
758 0         0 my $end;
759              
760 0   0     0 while (!$end or @msgid) {
761 0   0     0 while (!$end and @msgid < $qsize) {
762 0         0 my $data = $cb->();
763 0 0 0     0 if (defined $data and length $data) {
764 0         0 my $id = $sftp->_queue_new_msg(SSH2_FXP_WRITE, str => $rfid,
765             int64 => $off + $written, str => $data);
766 0         0 push @written, $written;
767 0         0 $written += length $data;
768 0         0 push @msgid, $id;
769             }
770             else {
771 0         0 $end = 1;
772             }
773             }
774              
775 0         0 my $eid = shift @msgid;
776 0         0 my $last = shift @written;
777 0 0       0 unless ($sftp->_check_status_ok($eid,
778             SFTP_ERR_REMOTE_WRITE_FAILED,
779             "Couldn't write to remote file")) {
780              
781             # discard responses to queued requests:
782 0         0 $sftp->_get_msg_by_id($_) for @msgid;
783 0         0 return $last;
784             }
785             }
786              
787 0         0 return $written;
788             }
789              
790             sub write {
791 0 0   0 1 0 @_ == 3 or croak 'Usage: $sftp->write($fh, $data)';
792              
793 0         0 my ($sftp, $rfh) = @_;
794 0 0       0 $sftp->flush($rfh, 'in') or return undef;
795 0 0       0 utf8::downgrade($_[2], 1) or croak "wide characters found in data";
796 0         0 my $datalen = length $_[2];
797 0         0 my $bout = $rfh->_bout;
798 0         0 $$bout .= $_[2];
799 0         0 my $len = length $$bout;
800              
801             $sftp->flush($rfh, 'out')
802 0 0 0     0 if ($len >= $sftp->{_write_delay} or ($len and $sftp->{_autoflush} ));
      0        
803              
804 0         0 return $datalen;
805             }
806              
807             sub flush {
808 0 0 0 0 1 0 (@_ >= 2 and @_ <= 3)
809             or croak 'Usage: $sftp->flush($fh [, $direction])';
810              
811 0         0 my ($sftp, $rfh, $dir) = @_;
812 0   0     0 $dir ||= '';
813              
814 0 0       0 defined $sftp->_rfid($rfh) or return;
815              
816 0 0       0 if ($dir ne 'out') { # flush in!
817 0         0 ${$rfh->_bin} = '';
  0         0  
818             }
819              
820 0 0       0 if ($dir ne 'in') { # flush out!
821 0         0 my $bout = $rfh->_bout;
822 0         0 my $len = length $$bout;
823 0 0       0 if ($len) {
824 0         0 my $start;
825 0         0 my $append = $rfh->_flag('append');
826 0 0       0 if ($append) {
827 0 0       0 my $attr = $sftp->stat($rfh)
828             or return undef;
829 0         0 $start = $attr->size;
830             }
831             else {
832 0         0 $start = $rfh->_pos;
833 0         0 ${$rfh->_bin} = '';
  0         0  
834             }
835 0         0 my $off = 0;
836             my $written = $sftp->_write($rfh, $start,
837             sub {
838 0     0   0 my $data = substr($$bout, $off, $sftp->{_block_size});
839 0         0 $off += length $data;
840 0         0 $data;
841 0         0 } );
842 0 0       0 $rfh->_inc_pos($written)
843             unless $append;
844              
845 0         0 substr($$bout, 0, $written, '');
846 0 0       0 $written == $len or return undef;
847             }
848             }
849 0         0 1;
850             }
851              
852             sub _fill_read_cache {
853 0     0   0 my ($sftp, $rfh, $len) = @_;
854              
855 0         0 $sftp->_clear_error_and_status;
856              
857 0 0       0 $sftp->flush($rfh, 'out')
858             or return undef;
859              
860 0         0 my $rfid = $sftp->_rfid($rfh);
861 0 0       0 defined $rfid or return undef;
862              
863 0         0 my $bin = $rfh->_bin;
864              
865 0 0       0 if (defined $len) {
866 0 0       0 return 1 if ($len < length $$bin);
867              
868 0         0 my $read_ahead = $sftp->{_read_ahead};
869 0 0       0 $len = length($$bin) + $read_ahead
870             if $len - length($$bin) < $read_ahead;
871             }
872              
873 0         0 my $pos = $rfh->_pos;
874              
875 0         0 my $qsize = $sftp->{_queue_size};
876 0         0 my $bsize = $sftp->{_block_size};
877              
878 0         0 do {
879 0         0 local $sftp->{_autodie};
880              
881 0         0 my @msgid;
882 0         0 my $askoff = length $$bin;
883 0         0 my $ensure_eof;
884              
885 0   0     0 while (!defined $len or length $$bin < $len) {
886 0   0     0 while ((!defined $len or $askoff < $len) and @msgid < $qsize) {
      0        
887 0         0 my $id = $sftp->_queue_new_msg(SSH2_FXP_READ, str=> $rfid,
888             int64 => $pos + $askoff, int32 => $bsize);
889 0         0 push @msgid, $id;
890 0         0 $askoff += $bsize;
891             }
892              
893 0         0 my $eid = shift @msgid;
894 0 0       0 my $msg = $sftp->_get_msg_and_check(SSH2_FXP_DATA, $eid,
895             SFTP_ERR_REMOTE_READ_FAILED,
896             "Couldn't read from remote file")
897             or last;
898              
899 0         0 my $data = $msg->get_str;
900 0         0 $$bin .= $data;
901 0 0       0 if (length $data < $bsize) {
902 0 0       0 unless (defined $len) {
903 0         0 $ensure_eof = $sftp->_queue_new_msg(SSH2_FXP_READ, str=> $rfid,
904             int64 => $pos + length $$bin, int32 => 1);
905             }
906 0         0 last;
907             }
908             }
909              
910 0         0 $sftp->_get_msg_by_id($_) for @msgid;
911              
912 0 0 0     0 if ($ensure_eof and
913             $sftp->_get_msg_and_check(SSH2_FXP_DATA, $ensure_eof,
914             SFTP_ERR_REMOTE_READ_FAILED,
915             "Couldn't read from remote file")) {
916              
917 0         0 $sftp->_set_error(SFTP_ERR_REMOTE_BLOCK_TOO_SMALL,
918             "Received block was too small");
919             }
920              
921 0 0       0 if ($sftp->{_status} == SSH2_FX_EOF) {
922 0         0 $sftp->_set_error;
923 0 0       0 $sftp->_set_status if length $$bin
924             }
925             };
926              
927 0 0       0 $sftp->_ok_or_autodie and length $$bin;
928             }
929              
930             sub read {
931 0 0   0 1 0 @_ == 3 or croak 'Usage: $sftp->read($fh, $len)';
932              
933 0         0 my ($sftp, $rfh, $len) = @_;
934 0 0       0 if ($sftp->_fill_read_cache($rfh, $len)) {
935 0         0 my $bin = $rfh->_bin;
936 0         0 my $data = substr($$bin, 0, $len, '');
937 0         0 $rfh->_inc_pos(length $data);
938 0         0 return $data;
939             }
940 0         0 return undef;
941             }
942              
943             sub _readline {
944 0     0   0 my ($sftp, $rfh, $sep) = @_;
945              
946 0 0       0 $sep = "\n" if @_ < 3;
947              
948 0         0 my $sl = length $sep;
949              
950 0         0 my $bin = $rfh->_bin;
951 0         0 my $last = 0;
952              
953 0         0 while(1) {
954 0         0 my $ix = index $$bin, $sep, $last + 1 - $sl ;
955 0 0       0 if ($ix >= 0) {
956 0         0 $ix += $sl;
957 0         0 $rfh->_inc_pos($ix);
958 0         0 return substr($$bin, 0, $ix, '');
959             }
960              
961 0         0 $last = length $$bin;
962 0         0 $sftp->_fill_read_cache($rfh, length($$bin) + 1);
963              
964 0 0       0 unless (length $$bin > $last) {
965             $sftp->{_error}
966 0 0       0 and return undef;
967              
968 0         0 my $line = $$bin;
969 0         0 $rfh->_inc_pos(length $line);
970 0         0 $$bin = '';
971 0 0       0 return (length $line ? $line : undef);
972             }
973             }
974             }
975              
976             sub readline {
977 0 0 0 0 1 0 (@_ >= 2 and @_ <= 3)
978             or croak 'Usage: $sftp->readline($fh [, $sep])';
979              
980 0         0 my ($sftp, $rfh, $sep) = @_;
981 0 0       0 $sep = "\n" if @_ < 3;
982 0 0 0     0 if (!defined $sep or $sep eq '') {
983 0         0 $sftp->_fill_read_cache($rfh);
984             $sftp->{_error}
985 0 0       0 and return undef;
986 0         0 my $bin = $rfh->_bin;
987 0         0 my $line = $$bin;
988 0         0 $rfh->_inc_pos(length $line);
989 0         0 $$bin = '';
990 0         0 return $line;
991             }
992 0 0       0 if (wantarray) {
993 0         0 my @lines;
994 0         0 while (defined (my $line = $sftp->_readline($rfh, $sep))) {
995 0         0 push @lines, $line;
996             }
997 0         0 return @lines;
998             }
999 0         0 return $sftp->_readline($rfh, $sep);
1000             }
1001              
1002             sub getc {
1003 0 0   0 1 0 @_ == 2 or croak 'Usage: $sftp->getc($fh)';
1004              
1005 0         0 my ($sftp, $rfh) = @_;
1006              
1007 0         0 $sftp->_fill_read_cache($rfh, 1);
1008 0         0 my $bin = $rfh->_bin;
1009 0 0       0 if (length $bin) {
1010 0         0 $rfh->_inc_pos(1);
1011 0         0 return substr $$bin, 0, 1, '';
1012             }
1013 0         0 return undef;
1014             }
1015              
1016             ## SSH2_FXP_LSTAT (7), SSH2_FXP_FSTAT (8), SSH2_FXP_STAT (17)
1017             # these all return a Net::SFTP::Foreign::Attributes object on success, undef on failure
1018              
1019             sub lstat {
1020 0 0   0 1 0 @_ <= 2 or croak 'Usage: $sftp->lstat($path)';
1021 0 0       0 ${^TAINT} and &_catch_tainted_args;
1022              
1023 0         0 my ($sftp, $path) = @_;
1024 0 0       0 $path = '.' unless defined $path;
1025 0         0 $path = $sftp->_rel2abs($path);
1026 0         0 my $id = $sftp->_queue_str_request(SSH2_FXP_LSTAT, $sftp->_fs_encode($path));
1027 0 0       0 if (my $msg = $sftp->_get_msg_and_check(SSH2_FXP_ATTRS, $id,
1028             SFTP_ERR_REMOTE_LSTAT_FAILED, "Couldn't stat remote link")) {
1029 0         0 return $msg->get_attributes;
1030             }
1031 0         0 return undef;
1032             }
1033              
1034             sub stat {
1035 0 0   0 1 0 @_ <= 2 or croak 'Usage: $sftp->stat($path_or_fh)';
1036 0 0       0 ${^TAINT} and &_catch_tainted_args;
1037              
1038 0         0 my ($sftp, $pofh) = @_;
1039 0 0       0 $pofh = '.' unless defined $pofh;
1040 0 0 0     0 my $id = $sftp->_queue_new_msg( (ref $pofh and UNIVERSAL::isa($pofh, 'Net::SFTP::Foreign::FileHandle'))
1041             ? ( SSH2_FXP_FSTAT, str => $sftp->_rid($pofh))
1042             : ( SSH2_FXP_STAT, str => $sftp->_fs_encode($sftp->_rel2abs($pofh))) );
1043 0 0       0 if (my $msg = $sftp->_get_msg_and_check(SSH2_FXP_ATTRS, $id,
1044             SFTP_ERR_REMOTE_STAT_FAILED, "Couldn't stat remote file")) {
1045 0         0 return $msg->get_attributes;
1046             }
1047 0         0 return undef;
1048             }
1049              
1050             sub fstat {
1051 0     0 1 0 _deprecated "fstat is deprecated and will be removed on the upcoming 2.xx series, "
1052             . "stat method accepts now both file handlers and paths";
1053 0         0 goto &stat;
1054             }
1055              
1056             ## SSH2_FXP_RMDIR (15), SSH2_FXP_REMOVE (13)
1057             # these return true on success, undef on failure
1058              
1059             sub _gen_remove_method {
1060 6     6   19 my($name, $code, $error, $errstr) = @_;
1061             my $sub = sub {
1062 0 0   0   0 @_ == 2 or croak "Usage: \$sftp->$name(\$path)";
1063 0 0       0 ${^TAINT} and &_catch_tainted_args;
1064              
1065 0         0 my ($sftp, $path) = @_;
1066 0         0 $path = $sftp->_rel2abs($path);
1067 0         0 my $id = $sftp->_queue_str_request($code, $sftp->_fs_encode($path));
1068 0         0 $sftp->_check_status_ok($id, $error, $errstr);
1069 6         32 };
1070 3     3   29 no strict 'refs';
  3         7  
  3         5625  
1071 6         42 *$name = $sub;
1072             }
1073              
1074             _gen_remove_method(remove => SSH2_FXP_REMOVE,
1075             SFTP_ERR_REMOTE_REMOVE_FAILED, "Couldn't delete remote file");
1076             _gen_remove_method(rmdir => SSH2_FXP_RMDIR,
1077             SFTP_ERR_REMOTE_RMDIR_FAILED, "Couldn't remove remote directory");
1078              
1079             ## SSH2_FXP_MKDIR (14), SSH2_FXP_SETSTAT (9)
1080             # these return true on success, undef on failure
1081              
1082             sub mkdir {
1083 0 0 0 0 1 0 (@_ >= 2 and @_ <= 3)
1084             or croak 'Usage: $sftp->mkdir($path [, $attrs])';
1085 0 0       0 ${^TAINT} and &_catch_tainted_args;
1086              
1087 0         0 my ($sftp, $path, $attrs) = @_;
1088 0 0       0 $attrs = _empty_attributes unless defined $attrs;
1089 0         0 $path = $sftp->_rel2abs($path);
1090 0         0 my $id = $sftp->_queue_str_request(SSH2_FXP_MKDIR,
1091             $sftp->_fs_encode($path),
1092             $attrs);
1093 0         0 $sftp->_check_status_ok($id,
1094             SFTP_ERR_REMOTE_MKDIR_FAILED,
1095             "Couldn't create remote directory");
1096             }
1097              
1098             sub join {
1099 17     17 1 1012 my $sftp = shift;
1100 17         28 my $vol = '';
1101 17         26 my $a = '.';
1102 17         47 while (@_) {
1103 34         53 my $b = shift;
1104 34 50       62 if (defined $b) {
1105 34 0 33     63 if (ref $sftp and # this method can also be used as a static one
      0        
1106             $sftp->{_remote_has_volumes} and $b =~ /^([a-z]\:)(.*)/i) {
1107 0         0 $vol = $1;
1108 0         0 $a = '.';
1109 0         0 $b = $2;
1110             }
1111 34         98 $b =~ s|^(?:\./+)+||;
1112 34 100 100     130 if (length $b and $b ne '.') {
1113 19 100 100     85 if ($b !~ m|^/| and $a ne '.' ) {
1114 2 50       8 $a = ($a =~ m|/$| ? "$a$b" : "$a/$b");
1115             }
1116             else {
1117 17         32 $a = $b
1118             }
1119 19         65 $a =~ s|(?:/+\.)+/?$|/|;
1120 19         54 $a =~ s|(?<=[^/])/+$||;
1121 19 50       54 $a = '.' unless length $a;
1122             }
1123             }
1124             }
1125 17         78 "$vol$a";
1126             }
1127              
1128             sub _rel2abs {
1129 0     0   0 my ($sftp, $path) = @_;
1130 0         0 my $old = $path;
1131 0         0 my $cwd = $sftp->{cwd};
1132 0         0 $path = $sftp->join($sftp->{cwd}, $path);
1133 0 0 0     0 $debug and $debug & 4096 and _debug("'$old' --> '$path'");
1134 0         0 return $path
1135             }
1136              
1137             sub mkpath {
1138 0 0 0 0 1 0 (@_ >= 2 and @_ <= 4)
1139             or croak 'Usage: $sftp->mkpath($path [, $attrs [, $parent]])';
1140 0 0       0 ${^TAINT} and &_catch_tainted_args;
1141              
1142 0         0 my ($sftp, $path, $attrs, $parent) = @_;
1143 0         0 $sftp->_clear_error_and_status;
1144 0         0 my $first = !$parent; # skips file name
1145 0         0 $path =~ s{^(/*)}{};
1146 0         0 my $start = $1;
1147 0         0 $path =~ s{/+$}{};
1148 0         0 my @path;
1149 0         0 while (1) {
1150 0 0       0 if ($first) {
1151 0         0 $first = 0
1152             }
1153             else {
1154 0         0 $path =~ s{/*[^/]*$}{}
1155             }
1156 0         0 my $p = "$start$path";
1157 0 0 0     0 $debug and $debug & 8192 and _debug "checking $p";
1158 0 0       0 if ($sftp->test_d($p)) {
1159 0 0 0     0 $debug and $debug & 8192 and _debug "$p is a dir";
1160 0         0 last;
1161             }
1162 0 0       0 unless (length $path) {
1163 0         0 $sftp->_set_error(SFTP_ERR_REMOTE_MKDIR_FAILED,
1164             "Unable to make path, bad root");
1165 0         0 return undef;
1166             }
1167 0         0 unshift @path, $p;
1168              
1169             }
1170 0         0 for my $p (@path) {
1171 0 0 0     0 $debug and $debug & 8192 and _debug "mkdir $p";
1172 0 0 0     0 if ($p =~ m{^(?:.*/)?\.{1,2}$} or $p =~ m{/$}) {
1173 0 0 0     0 $debug and $debug & 8192 and _debug "$p is a symbolic dir, skipping";
1174 0 0       0 unless ($sftp->test_d($p)) {
1175 0 0 0     0 $debug and $debug & 8192 and _debug "symbolic dir $p can not be checked";
1176             $sftp->{_error} or
1177 0 0       0 $sftp->_set_error(SFTP_ERR_REMOTE_MKDIR_FAILED,
1178             "Unable to make path, bad name");
1179 0         0 return undef;
1180             }
1181             }
1182             else {
1183 0 0       0 $sftp->mkdir($p, $attrs)
1184             or return undef;
1185             }
1186             }
1187 0         0 1;
1188             }
1189              
1190             sub _mkpath_local {
1191 0     0   0 my ($sftp, $path, $perm, $parent) = @_;
1192             # When parent is set, the last path part is removed and the parent
1193             # directory of the path given created.
1194              
1195 0         0 my @parts = File::Spec->splitdir($path);
1196 0 0 0     0 $debug and $debug & 32768 and _debug "_mkpath_local($path, $perm, ".($parent||0).")";
      0        
1197              
1198 0 0       0 if ($parent) {
1199 0   0     0 pop @parts while @parts and not length $parts[-1];
1200 0 0       0 unless (@parts) {
1201 0         0 $sftp->_set_error(SFTP_ERR_LOCAL_MKDIR_FAILED,
1202             "mkpath failed, top dir reached");
1203 0         0 return;
1204             }
1205 0         0 pop @parts;
1206             }
1207              
1208 0         0 my @tail;
1209 0         0 while (@parts) {
1210 0         0 my $target = File::Spec->catdir(@parts);
1211 0 0       0 if (-e $target) {
1212 0 0       0 unless (-d $target) {
1213 0         0 $sftp->_set_error(SFTP_ERR_LOCAL_BAD_OBJECT,
1214             "Local file '$target' is not a directory");
1215 0         0 return;
1216             }
1217             last
1218 0         0 }
1219 0         0 unshift @tail, pop @parts;
1220             }
1221 0         0 while (@tail) {
1222 0         0 push @parts, shift @tail;
1223 0         0 my $target = File::Spec->catdir(@parts);
1224 0 0 0     0 $debug and $debug and 32768 and _debug "creating local directory '$target'";
      0        
1225 0 0       0 unless (CORE::mkdir $target, $perm) {
1226 0 0       0 unless (do { local $!; -d $target}) {
  0         0  
  0         0  
1227 0         0 $sftp->_set_error(SFTP_ERR_LOCAL_MKDIR_FAILED,
1228             "mkdir '$target' failed", $!);
1229 0         0 return;
1230             }
1231             }
1232             }
1233 0 0 0     0 $debug and $debug & 32768 and _debug "_mkpath_local succeeded";
1234 0         0 return 1;
1235             }
1236              
1237             sub setstat {
1238 0 0   0 1 0 @_ == 3 or croak 'Usage: $sftp->setstat($path_or_fh, $attrs)';
1239 0 0       0 ${^TAINT} and &_catch_tainted_args;
1240              
1241 0         0 my ($sftp, $pofh, $attrs) = @_;
1242 0 0 0     0 my $id = $sftp->_queue_new_msg( ( (ref $pofh and UNIVERSAL::isa($pofh, 'Net::SFTP::Foreign::FileHandle') )
1243             ? ( SSH2_FXP_FSETSTAT, str => $sftp->_rid($pofh) )
1244             : ( SSH2_FXP_SETSTAT, str => $sftp->_fs_encode($sftp->_rel2abs($pofh)) ) ),
1245             attr => $attrs );
1246 0         0 return $sftp->_check_status_ok($id,
1247             SFTP_ERR_REMOTE_SETSTAT_FAILED,
1248             "Couldn't setstat remote file");
1249             }
1250              
1251             ## SSH2_FXP_CLOSE (4), SSH2_FXP_FSETSTAT (10)
1252             # these return true on success, undef on failure
1253              
1254             sub fsetstat {
1255 0     0 1 0 _deprecated "fsetstat is deprecated and will be removed on the upcoming 2.xx series, "
1256             . "setstat method accepts now both file handlers and paths";
1257 0         0 goto &setstat;
1258             }
1259              
1260             sub _gen_setstat_shortcut {
1261 12     12   37 my ($name, $rid_type, $attrs_flag, @arg_types) = @_;
1262 12         24 my $nargs = 2 + @arg_types;
1263 12         74 my $usage = ("\$sftp->$name("
1264             . CORE::join(', ', '$path_or_fh', map "arg$_", 1..@arg_types)
1265             . ')');
1266 12 50       51 my $rid_method = ($rid_type eq 'file' ? '_rfid' :
    50          
    100          
1267             $rid_type eq 'dir' ? '_rdid' :
1268             $rid_type eq 'any' ? '_rid' :
1269             croak "bad rid type $rid_type");
1270             my $sub = sub {
1271 0 0   0   0 @_ == $nargs or croak $usage;
1272 0         0 my $sftp = shift;
1273 0         0 my $pofh = shift;
1274             my $id = $sftp->_queue_new_msg( ( (ref $pofh and UNIVERSAL::isa($pofh, 'Net::SFTP::Foreign::FileHandle') )
1275             ? ( SSH2_FXP_FSETSTAT, str => $sftp->$rid_method($pofh) )
1276             : ( SSH2_FXP_SETSTAT, str => $sftp->_fs_encode($sftp->_rel2abs($pofh)) ) ),
1277             int32 => $attrs_flag,
1278 0 0 0     0 map { $arg_types[$_] => $_[$_] } 0..$#arg_types );
  0         0  
1279 0         0 $sftp->_check_status_ok($id,
1280             SFTP_ERR_REMOTE_SETSTAT_FAILED,
1281             "Couldn't setstat remote file ($name)");
1282 12         42 };
1283 3     3   28 no strict 'refs';
  3         7  
  3         14212  
1284 12         52 *$name = $sub;
1285             }
1286              
1287             _gen_setstat_shortcut(truncate => 'file', SSH2_FILEXFER_ATTR_SIZE, 'int64');
1288             _gen_setstat_shortcut(chown => 'any' , SSH2_FILEXFER_ATTR_UIDGID, 'int32', 'int32');
1289             _gen_setstat_shortcut(chmod => 'any' , SSH2_FILEXFER_ATTR_PERMISSIONS, 'int32');
1290             _gen_setstat_shortcut(utime => 'any' , SSH2_FILEXFER_ATTR_ACMODTIME, 'int32', 'int32');
1291              
1292             sub _close {
1293 0 0   0   0 @_ == 2 or croak 'Usage: $sftp->close($fh, $attrs)';
1294              
1295 0         0 my $sftp = shift;
1296 0         0 my $id = $sftp->_queue_rid_request(SSH2_FXP_CLOSE, @_);
1297 0 0       0 defined $id or return undef;
1298              
1299 0         0 my $ok = $sftp->_check_status_ok($id,
1300             SFTP_ERR_REMOTE_CLOSE_FAILED,
1301             "Couldn't close remote file");
1302              
1303 0 0 0     0 if ($debug and $debug & 2) {
1304 0 0       0 _debug sprintf("closing file handle, return: %s, rid:", (defined $ok ? $ok : '-'));
1305 0         0 _hexdump($sftp->_rid($_[0]));
1306             }
1307              
1308 0         0 return $ok;
1309             }
1310              
1311             sub close {
1312 0 0   0 1 0 @_ == 2 or croak 'Usage: $sftp->close($fh)';
1313 0 0       0 ${^TAINT} and &_catch_tainted_args;
1314              
1315 0         0 my ($sftp, $rfh) = @_;
1316             # defined $sftp->_rfid($rfh) or return undef;
1317             # ^--- commented out because flush already checks it is an open file
1318 0 0       0 $sftp->flush($rfh)
1319             or return undef;
1320              
1321 0 0       0 if ($sftp->_close($rfh)) {
1322 0         0 $rfh->_close;
1323 0         0 return 1
1324             }
1325             undef
1326 0         0 }
1327              
1328             sub closedir {
1329 0 0   0 1 0 @_ == 2 or croak 'Usage: $sftp->closedir($dh)';
1330 0 0       0 ${^TAINT} and &_catch_tainted_args;
1331              
1332 0         0 my ($sftp, $rdh) = @_;
1333 0         0 $rdh->_check_is_dir;
1334              
1335 0 0       0 if ($sftp->_close($rdh)) {
1336 0         0 $rdh->_close;
1337 0         0 return 1;
1338             }
1339             undef
1340 0         0 }
1341              
1342             sub readdir {
1343 0 0   0 1 0 @_ == 2 or croak 'Usage: $sftp->readdir($dh)';
1344 0 0       0 ${^TAINT} and &_catch_tainted_args;
1345              
1346 0         0 my ($sftp, $rdh) = @_;
1347              
1348 0         0 my $rdid = $sftp->_rdid($rdh);
1349 0 0       0 defined $rdid or return undef;
1350              
1351 0         0 my $cache = $rdh->_cache;
1352              
1353 0   0     0 while (!@$cache or wantarray) {
1354 0         0 my $id = $sftp->_queue_str_request(SSH2_FXP_READDIR, $rdid);
1355 0 0       0 if (my $msg = $sftp->_get_msg_and_check(SSH2_FXP_NAME, $id,
1356             SFTP_ERR_REMOTE_READDIR_FAILED,
1357             "Couldn't read remote directory" )) {
1358 0 0       0 my $count = $msg->get_int32 or last;
1359              
1360 0         0 for (1..$count) {
1361 0         0 push @$cache, { filename => $sftp->_fs_decode($msg->get_str),
1362             longname => $sftp->_fs_decode($msg->get_str),
1363             a => $msg->get_attributes };
1364             }
1365             }
1366             else {
1367 0 0       0 $sftp->_set_error if $sftp->{_status} == SSH2_FX_EOF;
1368 0         0 last;
1369             }
1370             }
1371              
1372 0 0       0 if (wantarray) {
1373 0         0 my $old = $cache;
1374 0         0 $cache = [];
1375 0         0 return @$old;
1376             }
1377 0         0 shift @$cache;
1378             }
1379              
1380             sub _readdir {
1381 0     0   0 my ($sftp, $rdh);
1382 0 0       0 if (wantarray) {
1383 0         0 my $line = $sftp->readdir($rdh);
1384 0 0       0 if (defined $line) {
1385 0         0 return $line->{filename};
1386             }
1387             }
1388             else {
1389 0         0 return map { $_->{filename} } $sftp->readdir($rdh);
  0         0  
1390             }
1391             }
1392              
1393             sub _gen_getpath_method {
1394 6     6   16 my ($code, $error, $name) = @_;
1395             return sub {
1396 0 0   0   0 @_ == 2 or croak 'Usage: $sftp->some_method($path)';
1397 0 0       0 ${^TAINT} and &_catch_tainted_args;
1398              
1399 0         0 my ($sftp, $path) = @_;
1400 0         0 $path = $sftp->_rel2abs($path);
1401 0         0 my $id = $sftp->_queue_str_request($code, $sftp->_fs_encode($path));
1402              
1403 0 0       0 if (my $msg = $sftp->_get_msg_and_check(SSH2_FXP_NAME, $id,
1404             $error,
1405             "Couldn't get $name for remote '$path'")) {
1406 0 0       0 $msg->get_int32 > 0
1407             and return $sftp->_fs_decode($msg->get_str);
1408              
1409 0         0 $sftp->_set_error($error,
1410             "Couldn't get $name for remote '$path', no names on reply")
1411             }
1412 0         0 return undef;
1413 6         36 };
1414             }
1415              
1416             ## SSH2_FXP_REALPATH (16)
1417             ## SSH2_FXP_READLINK (19)
1418             # return path on success, undef on failure
1419             *realpath = _gen_getpath_method(SSH2_FXP_REALPATH,
1420             SFTP_ERR_REMOTE_REALPATH_FAILED,
1421             "realpath");
1422             *readlink = _gen_getpath_method(SSH2_FXP_READLINK,
1423             SFTP_ERR_REMOTE_READLINK_FAILED,
1424             "link target");
1425              
1426             ## SSH2_FXP_RENAME (18)
1427             # true on success, undef on failure
1428              
1429             sub _rename {
1430 0     0   0 my ($sftp, $old, $new) = @_;
1431              
1432 0         0 $old = $sftp->_rel2abs($old);
1433 0         0 $new = $sftp->_rel2abs($new);
1434              
1435 0         0 my $id = $sftp->_queue_new_msg(SSH2_FXP_RENAME,
1436             str => $sftp->_fs_encode($old),
1437             str => $sftp->_fs_encode($new));
1438              
1439 0         0 $sftp->_check_status_ok($id, SFTP_ERR_REMOTE_RENAME_FAILED,
1440             "Couldn't rename remote file '$old' to '$new'");
1441             }
1442              
1443             sub rename {
1444 0 0   0 1 0 (@_ & 1) or croak 'Usage: $sftp->rename($old, $new, %opts)';
1445 0 0       0 ${^TAINT} and &_catch_tainted_args;
1446              
1447 0         0 my ($sftp, $old, $new, %opts) = @_;
1448              
1449 0         0 my $overwrite = delete $opts{overwrite};
1450 0         0 my $numbered = delete $opts{numbered};
1451 0 0 0     0 croak "'overwrite' and 'numbered' options can not be used together"
1452             if ($overwrite and $numbered);
1453 0 0       0 %opts and _croak_bad_options(keys %opts);
1454              
1455 0 0       0 if ($overwrite) {
1456 0 0       0 $sftp->atomic_rename($old, $new) and return 1;
1457 0 0       0 $sftp->{_status} != SSH2_FX_OP_UNSUPPORTED and return undef;
1458             }
1459              
1460 0         0 for (1) {
1461 0         0 local $sftp->{_autodie};
1462             # we are optimistic here and try to rename it without testing
1463             # if a file of the same name already exists first
1464 0 0 0     0 if (!$sftp->_rename($old, $new) and
1465             $sftp->{_status} == SSH2_FX_FAILURE) {
1466 0 0 0     0 if ($numbered and $sftp->test_e($new)) {
    0          
1467 0         0 _inc_numbered($new);
1468 0         0 redo;
1469             }
1470             elsif ($overwrite) {
1471 0         0 my $rp_old = $sftp->realpath($old);
1472 0         0 my $rp_new = $sftp->realpath($new);
1473 0 0 0     0 if (defined $rp_old and defined $rp_new and $rp_old eq $rp_new) {
    0 0        
1474 0         0 $sftp->_clear_error_and_status;
1475             }
1476             elsif ($sftp->remove($new)) {
1477 0         0 $overwrite = 0;
1478 0         0 redo;
1479             }
1480             }
1481             }
1482             }
1483 0         0 $sftp->_ok_or_autodie;
1484             }
1485              
1486             sub atomic_rename {
1487 0 0   0 1 0 @_ == 3 or croak 'Usage: $sftp->atomic_rename($old, $new)';
1488 0 0       0 ${^TAINT} and &_catch_tainted_args;
1489              
1490 0         0 my ($sftp, $old, $new) = @_;
1491              
1492 0 0       0 $sftp->_check_extension('posix-rename@openssh.com' => 1,
1493             SFTP_ERR_REMOTE_RENAME_FAILED,
1494             "atomic rename failed")
1495             or return undef;
1496              
1497 0         0 $old = $sftp->_rel2abs($old);
1498 0         0 $new = $sftp->_rel2abs($new);
1499              
1500 0         0 my $id = $sftp->_queue_new_msg(SSH2_FXP_EXTENDED,
1501             str => 'posix-rename@openssh.com',
1502             str => $sftp->_fs_encode($old),
1503             str => $sftp->_fs_encode($new));
1504              
1505 0         0 $sftp->_check_status_ok($id, SFTP_ERR_REMOTE_RENAME_FAILED,
1506             "Couldn't rename remote file '$old' to '$new'");
1507             }
1508              
1509             ## SSH2_FXP_SYMLINK (20)
1510             # true on success, undef on failure
1511             sub symlink {
1512 0 0   0 1 0 @_ == 3 or croak 'Usage: $sftp->symlink($sl, $target)';
1513 0 0       0 ${^TAINT} and &_catch_tainted_args;
1514              
1515 0         0 my ($sftp, $sl, $target) = @_;
1516 0         0 $sl = $sftp->_rel2abs($sl);
1517 0         0 my $id = $sftp->_queue_new_msg(SSH2_FXP_SYMLINK,
1518             str => $sftp->_fs_encode($target),
1519             str => $sftp->_fs_encode($sl));
1520              
1521 0         0 $sftp->_check_status_ok($id, SFTP_ERR_REMOTE_SYMLINK_FAILED,
1522             "Couldn't create symlink '$sl' pointing to '$target'");
1523             }
1524              
1525             sub hardlink {
1526 0 0   0 1 0 @_ == 3 or croak 'Usage: $sftp->hardlink($hl, $target)';
1527 0 0       0 ${^TAINT} and &_catch_tainted_args;
1528              
1529 0         0 my ($sftp, $hl, $target) = @_;
1530              
1531 0 0       0 $sftp->_check_extension('hardlink@openssh.com' => 1,
1532             SFTP_ERR_REMOTE_HARDLINK_FAILED,
1533             "hardlink failed")
1534             or return undef;
1535 0         0 $hl = $sftp->_rel2abs($hl);
1536 0         0 $target = $sftp->_rel2abs($target);
1537              
1538 0         0 my $id = $sftp->_queue_new_msg(SSH2_FXP_EXTENDED,
1539             str => 'hardlink@openssh.com',
1540             str => $sftp->_fs_encode($target),
1541             str => $sftp->_fs_encode($hl));
1542 0         0 $sftp->_check_status_ok($id, SFTP_ERR_REMOTE_HARDLINK_FAILED,
1543             "Couldn't create hardlink '$hl' pointing to '$target'");
1544             }
1545              
1546             sub _gen_save_status_method {
1547 9     9   21 my $method = shift;
1548             sub {
1549 0     0     my $sftp = shift;
1550 0 0         local ($sftp->{_error}, $sftp->{_status}) if $sftp->{_error};
1551 0           $sftp->$method(@_);
1552             }
1553 9         39 }
1554              
1555              
1556             *_close_save_status = _gen_save_status_method('close');
1557             *_closedir_save_status = _gen_save_status_method('closedir');
1558             *_remove_save_status = _gen_save_status_method('remove');
1559              
1560             sub _inc_numbered {
1561 0 0   0     $_[0] =~ s{^(.*)\((\d+)\)((?:\.[^\.]*)?)$}{"$1(" . ($2+1) . ")$3"}e or
  0            
1562             $_[0] =~ s{((?:\.[^\.]*)?)$}{(1)$1};
1563 0 0 0       $debug and $debug & 128 and _debug("numbering to: $_[0]");
1564             }
1565              
1566             ## High-level client -> server methods.
1567              
1568             sub abort {
1569 0     0 1   my $sftp = shift;
1570 0 0         $sftp->_set_error(SFTP_ERR_ABORTED, ($@ ? $_[0] : "Aborted"));
1571             }
1572              
1573             # returns true on success, undef on failure
1574             sub get {
1575 0 0   0 1   @_ >= 2 or croak 'Usage: $sftp->get($remote, $local, %opts)';
1576 0 0         ${^TAINT} and &_catch_tainted_args;
1577              
1578 0           my ($sftp, $remote, $local, %opts) = @_;
1579 0 0         defined $remote or croak "remote file path is undefined";
1580              
1581 0           $sftp->_clear_error_and_status;
1582              
1583 0           $remote = $sftp->_rel2abs($remote);
1584 0 0         $local = _file_part($remote) unless defined $local;
1585 0   0       my $local_is_fh = (ref $local and $local->isa('GLOB'));
1586              
1587 0           my $cb = delete $opts{callback};
1588 0           my $umask = delete $opts{umask};
1589 0           my $perm = delete $opts{perm};
1590 0 0         my $copy_perm = delete $opts{exists $opts{copy_perm} ? 'copy_perm' : 'copy_perms'};
1591 0           my $copy_time = delete $opts{copy_time};
1592 0           my $overwrite = delete $opts{overwrite};
1593 0           my $resume = delete $opts{resume};
1594 0           my $append = delete $opts{append};
1595 0   0       my $block_size = delete $opts{block_size} || $sftp->{_block_size};
1596 0   0       my $queue_size = delete $opts{queue_size} || $sftp->{_queue_size};
1597 0           my $dont_save = delete $opts{dont_save};
1598 0           my $conversion = delete $opts{conversion};
1599 0           my $numbered = delete $opts{numbered};
1600 0           my $cleanup = delete $opts{cleanup};
1601 0           my $atomic = delete $opts{atomic};
1602 0           my $best_effort = delete $opts{best_effort};
1603 0           my $mkpath = delete $opts{mkpath};
1604              
1605 0 0 0       croak "'perm' and 'copy_perm' options can not be used simultaneously"
1606             if (defined $perm and defined $copy_perm);
1607 0 0 0       croak "'numbered' can not be used with 'overwrite', 'resume' or 'append'"
      0        
1608             if ($numbered and ($overwrite or $resume or $append));
1609 0 0 0       if ($resume or $append) {
1610 0 0 0       $resume and $append and croak "'resume' and 'append' options can not be used simultaneously";
1611 0 0         $atomic and croak "'atomic' can not be used with 'resume' or 'append'";
1612 0 0         $overwrite and croak "'overwrite' can not be used with 'resume' or 'append'";
1613             }
1614              
1615 0 0         if ($local_is_fh) {
1616 0           my $tail = 'option can not be used when target is a file handle';
1617 0 0         $resume and croak "'resume' $tail";
1618 0 0         $overwrite and croak "'overwrite' $tail";
1619 0 0         $numbered and croak "'numbered' $tail";
1620 0 0         $dont_save and croak "'dont_save' $tail";
1621 0 0         $atomic and croak "'croak' $tail";
1622             }
1623 0 0         %opts and _croak_bad_options(keys %opts);
1624              
1625 0 0 0       if ($resume and $conversion) {
1626 0           carp "resume option is useless when data conversion has also been requested";
1627 0           undef $resume;
1628             }
1629              
1630 0 0 0       $overwrite = 1 unless (defined $overwrite or $local_is_fh or $numbered or $append);
      0        
      0        
1631 0 0 0       $copy_perm = 1 unless (defined $perm or defined $copy_perm or $local_is_fh);
      0        
1632 0 0 0       $copy_time = 1 unless (defined $copy_time or $local_is_fh);
1633 0 0         $mkpath = 1 unless defined $mkpath;
1634 0 0 0       $cleanup = ($atomic || $numbered) unless defined $cleanup;
1635              
1636 0           my $a = do {
1637 0           local $sftp->{_autodie};
1638 0           $sftp->stat($remote);
1639             };
1640 0 0         my ($rperm, $size, $atime, $mtime) = ($a ? ($a->perm, $a->size, $a->atime, $a->mtime) : ());
1641 0 0         $size = -1 unless defined $size;
1642              
1643 0 0 0       if ($copy_time and not defined $atime) {
1644 0 0         if ($best_effort) {
1645 0           undef $copy_time;
1646             }
1647             else {
1648 0 0         $sftp->_ok_or_autodie and $sftp->_set_error(SFTP_ERR_REMOTE_STAT_FAILED,
1649             "Not enough information on stat, amtime not included");
1650 0           return undef;
1651             }
1652             }
1653              
1654 0 0         $umask = (defined $perm ? 0 : umask) unless defined $umask;
    0          
1655 0 0         if ($copy_perm) {
1656 0 0         if (defined $rperm) {
    0          
1657 0           $perm = $rperm;
1658             }
1659             elsif ($best_effort) {
1660 0           undef $copy_perm
1661             }
1662             else {
1663 0 0         $sftp->_ok_or_autodie and $sftp->_set_error(SFTP_ERR_REMOTE_STAT_FAILED,
1664             "Not enough information on stat, mode not included");
1665             return undef
1666 0           }
1667             }
1668 0 0         $perm &= ~$umask if defined $perm;
1669              
1670 0           $sftp->_clear_error_and_status;
1671              
1672 0 0 0       if ($resume and $resume eq 'auto') {
1673 0           undef $resume;
1674 0 0         if (defined $mtime) {
1675 0 0         if (my @lstat = CORE::stat $local) {
1676 0           $resume = ($mtime <= $lstat[9]);
1677             }
1678             }
1679             }
1680              
1681 0           my ($atomic_numbered, $atomic_local, $atomic_cleanup);
1682              
1683 0           my ($rfh, $fh);
1684 0           my $askoff = 0;
1685 0           my $lstart = 0;
1686              
1687 0 0         if ($dont_save) {
1688 0           $rfh = $sftp->open($remote, SSH2_FXF_READ);
1689 0 0         defined $rfh or return undef;
1690             }
1691             else {
1692 0 0 0       unless ($local_is_fh or $overwrite or $append or $resume or $numbered) {
      0        
      0        
      0        
1693 0 0         if (-e $local) {
1694 0           $sftp->_set_error(SFTP_ERR_LOCAL_ALREADY_EXISTS,
1695             "local file $local already exists");
1696             return undef
1697 0           }
1698             }
1699              
1700 0 0         if ($atomic) {
1701 0           $atomic_local = $local;
1702 0           $local .= sprintf("(%d).tmp", rand(10000));
1703 0           $atomic_numbered = $numbered;
1704 0           $numbered = 1;
1705 0 0 0       $debug and $debug & 128 and _debug("temporal local file name: $local");
1706             }
1707              
1708 0 0         if ($resume) {
1709 0 0         if (CORE::open $fh, '+<', $local) {
1710 0           binmode $fh;
1711 0           CORE::seek($fh, 0, 2);
1712 0           $askoff = CORE::tell $fh;
1713 0 0         if ($askoff < 0) {
1714             # something is going really wrong here, fall
1715             # back to non-resuming mode...
1716 0           $askoff = 0;
1717 0           undef $fh;
1718             }
1719             else {
1720 0 0 0       if ($size >=0 and $askoff > $size) {
1721 0           $sftp->_set_error(SFTP_ERR_LOCAL_BIGGER_THAN_REMOTE,
1722             "Couldn't resume transfer, local file is bigger than remote");
1723 0           return undef;
1724             }
1725 0 0         $size == $askoff and return 1;
1726             }
1727             }
1728             }
1729              
1730             # we open the remote file so late in order to skip it when
1731             # resuming an already completed transfer:
1732 0           $rfh = $sftp->open($remote, SSH2_FXF_READ);
1733 0 0         defined $rfh or return undef;
1734              
1735 0 0         unless (defined $fh) {
1736 0 0         if ($local_is_fh) {
1737 0           $fh = $local;
1738 0           local ($@, $SIG{__DIE__}, $SIG{__WARN__});
1739 0           eval { $lstart = CORE::tell($fh) };
  0            
1740 0 0 0       $lstart = 0 unless ($lstart and $lstart > 0);
1741             }
1742             else {
1743 0           my $flags = Fcntl::O_CREAT|Fcntl::O_WRONLY;
1744 0 0         $flags |= Fcntl::O_APPEND if $append;
1745 0 0 0       $flags |= Fcntl::O_EXCL if ($numbered or (!$overwrite and !$append));
      0        
1746 0 0         unlink $local if $overwrite;
1747 0 0         my $open_perm = (defined $perm ? $perm : 0666);
1748 0           my $save = _umask_save_and_set($umask);
1749 0 0         $sftp->_mkpath_local($local, $open_perm|0700, 1) if $mkpath;
1750 0           while (1) {
1751 0 0         sysopen ($fh, $local, $flags, $open_perm) and last;
1752 0 0 0       unless ($numbered and -e $local) {
1753 0           $sftp->_set_error(SFTP_ERR_LOCAL_OPEN_FAILED,
1754             "Can't open $local", $!);
1755 0           return undef;
1756             }
1757 0           _inc_numbered($local);
1758             }
1759 0 0         $$numbered = $local if ref $numbered;
1760 0           binmode $fh;
1761 0 0         $lstart = sysseek($fh, 0, 2) if $append;
1762             }
1763             }
1764              
1765 0 0         if (defined $perm) {
1766 0           my $error;
1767 0           do {
1768 0           local ($@, $SIG{__DIE__}, $SIG{__WARN__});
1769 0 0         unless (eval { CORE::chmod($perm, $local) > 0 }) {
  0            
1770 0 0         $error = ($@ ? $@ : $!);
1771             }
1772             };
1773 0 0 0       if ($error and !$best_effort) {
1774 0 0 0       unlink $local unless $resume or $append;
1775 0           $sftp->_set_error(SFTP_ERR_LOCAL_CHMOD_FAILED,
1776             "Can't chmod $local", $error);
1777             return undef
1778 0           }
1779             }
1780             }
1781              
1782 0           my $converter = _gen_converter $conversion;
1783              
1784 0           my $rfid = $sftp->_rfid($rfh);
1785 0 0         defined $rfid or die "internal error: rfid not defined";
1786              
1787 0           my @msgid;
1788             my @askoff;
1789 0           my $loff = $askoff;
1790 0           my $adjustment = 0;
1791 0           local $\;
1792              
1793 0 0         my $slow_start = ($size == -1 ? $queue_size - 1 : 0);
1794              
1795 0           my $safe_block_size = $sftp->{_min_block_size} >= $block_size;
1796              
1797 0           do {
1798             # Disable autodie here in order to do not leave unhandled
1799             # responses queued on the connection in case of failure.
1800 0           local $sftp->{_autodie};
1801              
1802             # Again, once this point is reached, all code paths should end
1803             # through the CLEANUP block.
1804              
1805 0           while (1) {
1806             # request a new block if queue is not full
1807 0   0       while (!@msgid or ( ($size == -1 or $size + $block_size > $askoff) and
      0        
      0        
      0        
1808             @msgid < $queue_size - $slow_start and
1809             $safe_block_size ) ) {
1810 0           my $id = $sftp->_queue_new_msg(SSH2_FXP_READ, str=> $rfid,
1811             int64 => $askoff, int32 => $block_size);
1812 0           push @msgid, $id;
1813 0           push @askoff, $askoff;
1814 0           $askoff += $block_size;
1815             }
1816              
1817 0 0         $slow_start-- if $slow_start;
1818              
1819 0           my $eid = shift @msgid;
1820 0           my $roff = shift @askoff;
1821              
1822 0           my $msg = $sftp->_get_msg_and_check(SSH2_FXP_DATA, $eid,
1823             SFTP_ERR_REMOTE_READ_FAILED,
1824             "Couldn't read from remote file");
1825              
1826 0 0         unless ($msg) {
1827 0 0         $sftp->_set_error if $sftp->{_status} == SSH2_FX_EOF;
1828 0           last;
1829             }
1830              
1831 0           my $data = $msg->get_str;
1832 0           my $len = length $data;
1833              
1834 0 0 0       if ($roff != $loff or !$len) {
1835 0           $sftp->_set_error(SFTP_ERR_REMOTE_BLOCK_TOO_SMALL,
1836             "remote packet received is too small" );
1837 0           last;
1838             }
1839              
1840 0           $loff += $len;
1841 0 0         unless ($safe_block_size) {
1842 0 0         if ($len > $sftp->{_min_block_size}) {
1843 0           $sftp->{min_block_size} = $len;
1844 0 0         if ($len < $block_size) {
1845             # auto-adjust block size
1846 0           $block_size = $len;
1847 0           $askoff = $loff;
1848             }
1849             }
1850 0           $safe_block_size = 1;
1851             }
1852              
1853 0           my $adjustment_before = $adjustment;
1854 0 0         $adjustment += $converter->($data) if $converter;
1855              
1856 0 0 0       if (length($data) and defined $cb) {
1857             # $size = $loff if ($loff > $size and $size != -1);
1858 0           local $\;
1859 0           $cb->($sftp, $data,
1860             $lstart + $roff + $adjustment_before,
1861             $lstart + $size + $adjustment);
1862              
1863 0 0         last if $sftp->{_error};
1864             }
1865              
1866 0 0 0       if (length($data) and !$dont_save) {
1867 0 0         unless (print $fh $data) {
1868 0           $sftp->_set_error(SFTP_ERR_LOCAL_WRITE_FAILED,
1869             "unable to write data to local file $local", $!);
1870 0           last;
1871             }
1872             }
1873             }
1874              
1875 0           $sftp->_get_msg_by_id($_) for @msgid;
1876              
1877 0 0         goto CLEANUP if $sftp->{_error};
1878              
1879             # if a converter is in place, and aditional call has to be
1880             # performed in order to flush any pending buffered data
1881 0 0         if ($converter) {
1882 0           my $data = '';
1883 0           my $adjustment_before = $adjustment;
1884 0           $adjustment += $converter->($data);
1885              
1886 0 0 0       if (length($data) and defined $cb) {
1887             # $size = $loff if ($loff > $size and $size != -1);
1888 0           local $\;
1889 0           $cb->($sftp, $data, $askoff + $adjustment_before, $size + $adjustment);
1890 0 0         goto CLEANUP if $sftp->{_error};
1891             }
1892              
1893 0 0 0       if (length($data) and !$dont_save) {
1894 0 0         unless (print $fh $data) {
1895 0           $sftp->_set_error(SFTP_ERR_LOCAL_WRITE_FAILED,
1896             "unable to write data to local file $local", $!);
1897 0           goto CLEANUP;
1898             }
1899             }
1900             }
1901              
1902             # we call the callback one last time with an empty string;
1903 0 0         if (defined $cb) {
1904 0           my $data = '';
1905 0           do {
1906 0           local $\;
1907 0           $cb->($sftp, $data, $askoff + $adjustment, $size + $adjustment);
1908             };
1909 0 0         return undef if $sftp->{_error};
1910 0 0 0       if (length($data) and !$dont_save) {
1911 0 0         unless (print $fh $data) {
1912 0           $sftp->_set_error(SFTP_ERR_LOCAL_WRITE_FAILED,
1913             "unable to write data to local file $local", $!);
1914 0           goto CLEANUP;
1915             }
1916             }
1917             }
1918              
1919 0 0         unless ($dont_save) {
1920 0 0 0       unless ($local_is_fh or CORE::close $fh) {
1921 0           $sftp->_set_error(SFTP_ERR_LOCAL_WRITE_FAILED,
1922             "unable to write data to local file $local", $!);
1923 0           goto CLEANUP;
1924             }
1925              
1926             # we can be running on taint mode, so some checks are
1927             # performed to untaint data from the remote side.
1928              
1929 0 0         if ($copy_time) {
1930 0 0 0       unless (utime($atime, $mtime, $local) or $best_effort) {
1931 0           $sftp->_set_error(SFTP_ERR_LOCAL_UTIME_FAILED,
1932             "Can't utime $local", $!);
1933 0           goto CLEANUP;
1934             }
1935             }
1936              
1937 0 0         if ($atomic) {
1938 0 0         if (!$overwrite) {
1939 0           while (1) {
1940             # performing a non-overwriting atomic rename is
1941             # quite burdensome: first, link is tried, if that
1942             # fails, non-overwriting is favoured over
1943             # atomicity and an empty file is used to lock the
1944             # path before atempting an overwriting rename.
1945 0 0         if (link $local, $atomic_local) {
1946 0           unlink $local;
1947 0           last;
1948             }
1949 0           my $err = $!;
1950 0 0         unless (-e $atomic_local) {
1951 0 0         if (sysopen my $lock, $atomic_local,
1952             Fcntl::O_CREAT|Fcntl::O_EXCL|Fcntl::O_WRONLY,
1953             0600) {
1954 0           $atomic_cleanup = 1;
1955 0           goto OVERWRITE;
1956             }
1957 0           $err = $!;
1958 0 0         unless (-e $atomic_local) {
1959 0           $sftp->_set_error(SFTP_ERR_LOCAL_OPEN_FAILED,
1960             "Can't open $local", $err);
1961 0           goto CLEANUP;
1962             }
1963             }
1964 0 0         unless ($numbered) {
1965 0           $sftp->_set_error(SFTP_ERR_LOCAL_ALREADY_EXISTS,
1966             "local file $atomic_local already exists");
1967 0           goto CLEANUP;
1968             }
1969 0           _inc_numbered($atomic_local);
1970             }
1971             }
1972             else {
1973             OVERWRITE:
1974 0 0         unless (CORE::rename $local, $atomic_local) {
1975 0           $sftp->_set_error(SFTP_ERR_LOCAL_RENAME_FAILED,
1976             "Unable to rename temporal file to its final position '$atomic_local'", $!);
1977 0           goto CLEANUP;
1978             }
1979             }
1980 0 0         $$atomic_numbered = $local if ref $atomic_numbered;
1981             }
1982             }
1983             CLEANUP:
1984 0 0 0       if ($cleanup and $sftp->{_error}) {
1985 0           unlink $local;
1986 0 0         unlink $atomic_local if $atomic_cleanup;
1987             }
1988             }; # autodie flag is restored here!
1989              
1990 0           $sftp->_ok_or_autodie;
1991             }
1992              
1993             # return file contents on success, undef on failure
1994             sub get_content {
1995 0 0   0 1   @_ == 2 or croak 'Usage: $sftp->get_content($remote)';
1996 0 0         ${^TAINT} and &_catch_tainted_args;
1997              
1998 0           my ($sftp, $name) = @_;
1999             #$name = $sftp->_rel2abs($name);
2000 0           my @data;
2001              
2002 0 0         my $rfh = $sftp->open($name)
2003             or return undef;
2004              
2005 0           scalar $sftp->readline($rfh, undef);
2006             }
2007              
2008             sub put {
2009 0 0   0 1   @_ >= 2 or croak 'Usage: $sftp->put($local, $remote, %opts)';
2010 0 0         ${^TAINT} and &_catch_tainted_args;
2011              
2012 0           my ($sftp, $local, $remote, %opts) = @_;
2013 0 0         defined $local or croak "local file path is undefined";
2014              
2015 0           $sftp->_clear_error_and_status;
2016              
2017 0   0       my $local_is_fh = (ref $local and $local->isa('GLOB'));
2018 0 0         unless (defined $remote) {
2019 0 0         $local_is_fh and croak "unable to infer remote file name when a file handler is passed as local";
2020 0           $remote = (File::Spec->splitpath($local))[2];
2021             }
2022             # $remote = $sftp->_rel2abs($remote);
2023              
2024 0           my $cb = delete $opts{callback};
2025 0           my $umask = delete $opts{umask};
2026 0           my $perm = delete $opts{perm};
2027 0           my $copy_perm = delete $opts{copy_perm};
2028 0 0         $copy_perm = delete $opts{copy_perms} unless defined $copy_perm;
2029 0           my $copy_time = delete $opts{copy_time};
2030 0           my $overwrite = delete $opts{overwrite};
2031 0           my $resume = delete $opts{resume};
2032 0           my $append = delete $opts{append};
2033 0   0       my $block_size = delete $opts{block_size} || $sftp->{_block_size};
2034 0   0       my $queue_size = delete $opts{queue_size} || $sftp->{_queue_size};
2035 0           my $conversion = delete $opts{conversion};
2036 0           my $late_set_perm = delete $opts{late_set_perm};
2037 0           my $numbered = delete $opts{numbered};
2038 0           my $atomic = delete $opts{atomic};
2039 0           my $cleanup = delete $opts{cleanup};
2040 0           my $best_effort = delete $opts{best_effort};
2041 0           my $sparse = delete $opts{sparse};
2042 0           my $mkpath = delete $opts{mkpath};
2043              
2044 0 0 0       croak "'perm' and 'umask' options can not be used simultaneously"
2045             if (defined $perm and defined $umask);
2046 0 0 0       croak "'perm' and 'copy_perm' options can not be used simultaneously"
2047             if (defined $perm and $copy_perm);
2048 0 0 0       croak "'resume' and 'append' options can not be used simultaneously"
2049             if ($resume and $append);
2050 0 0 0       croak "'resume' and 'overwrite' options can not be used simultaneously"
2051             if ($resume and $overwrite);
2052 0 0 0       croak "'numbered' can not be used with 'overwrite', 'resume' or 'append'"
      0        
2053             if ($numbered and ($overwrite or $resume or $append));
2054 0 0 0       croak "'atomic' can not be used with 'resume' or 'append'"
      0        
2055             if ($atomic and ($resume or $append));
2056              
2057 0 0         %opts and _croak_bad_options(keys %opts);
2058              
2059 0 0 0       $overwrite = 1 unless (defined $overwrite or $numbered);
2060 0 0 0       $copy_perm = 1 unless (defined $perm or defined $copy_perm or $local_is_fh);
      0        
2061 0 0 0       $copy_time = 1 unless (defined $copy_time or $local_is_fh);
2062 0 0         $late_set_perm = $sftp->{_late_set_perm} unless defined $late_set_perm;
2063 0 0 0       $cleanup = ($atomic || $numbered) unless defined $cleanup;
2064 0 0         $mkpath = 1 unless defined $mkpath;
2065              
2066 0           my $neg_umask;
2067 0 0         if (defined $perm) {
2068 0           $neg_umask = $perm;
2069             }
2070             else {
2071 0 0         $umask = umask unless defined $umask;
2072 0           $neg_umask = 0777 & ~$umask;
2073             }
2074              
2075 0           my ($fh, $lmode, $lsize, $latime, $lmtime);
2076 0 0         if ($local_is_fh) {
2077 0           $fh = $local;
2078             # we don't set binmode for the passed file handle on purpose
2079             }
2080             else {
2081 0 0         unless (CORE::open $fh, '<', $local) {
2082 0           $sftp->_set_error(SFTP_ERR_LOCAL_OPEN_FAILED,
2083             "Unable to open local file '$local'", $!);
2084 0           return undef;
2085             }
2086 0           binmode $fh;
2087             }
2088              
2089             {
2090             # as $fh can come from the outside, it may be a tied object
2091             # lacking support for some methods, so we call them wrapped
2092             # inside eval blocks
2093 0           local ($@, $SIG{__DIE__}, $SIG{__WARN__});
  0            
2094 0 0 0       if ((undef, undef, $lmode, undef, undef,
    0 0        
    0          
2095             undef, undef, $lsize, $latime, $lmtime) =
2096             eval {
2097 3     3   73 no warnings; # Calling stat on a tied handler
  3         9  
  3         22103  
2098             # generates a warning because the op is
2099             # not supported by the tie API.
2100 0           CORE::stat $fh;
2101             }
2102             ) {
2103 0 0 0       $debug and $debug & 16384 and _debug "local file size is " . (defined $lsize ? $lsize : '');
    0          
2104              
2105             # $fh can point at some place inside the file, not just at the
2106             # begining
2107 0 0 0       if ($local_is_fh and defined $lsize) {
2108 0           my $tell = eval { CORE::tell $fh };
  0            
2109 0 0 0       $lsize -= $tell if $tell and $tell > 0;
2110             }
2111             }
2112             elsif ($copy_perm or $copy_time) {
2113 0           $sftp->_set_error(SFTP_ERR_LOCAL_STAT_FAILED,
2114             "Couldn't stat local file '$local'", $!);
2115 0           return undef;
2116             }
2117             elsif ($resume and $resume eq 'auto') {
2118 0 0 0       $debug and $debug & 16384 and _debug "not resuming because stat'ing the local file failed";
2119 0           undef $resume
2120             }
2121             }
2122              
2123 0 0         $perm = $lmode & $neg_umask if $copy_perm;
2124 0           my $attrs = Net::SFTP::Foreign::Attributes->new;
2125 0 0         $attrs->set_perm($perm) if defined $perm;
2126              
2127 0           my $rfh;
2128 0           my $writeoff = 0;
2129 0           my $converter = _gen_converter $conversion;
2130 0           my $converted_input = '';
2131 0           my $rattrs;
2132              
2133 0 0 0       if ($resume or $append) {
2134 0           $rattrs = do {
2135 0           local $sftp->{_autodie};
2136 0           $sftp->stat($remote);
2137             };
2138 0 0         if ($rattrs) {
2139 0 0 0       if ($resume and $resume eq 'auto' and $rattrs->mtime <= $lmtime) {
      0        
2140 0 0 0       $debug and $debug & 16384 and
2141             _debug "not resuming because local file is newer, r: ".$rattrs->mtime." l: $lmtime";
2142 0           undef $resume;
2143             }
2144             else {
2145 0           $writeoff = $rattrs->size;
2146 0 0 0       $debug and $debug & 16384 and _debug "resuming from $writeoff";
2147             }
2148             }
2149             else {
2150 0 0         if ($append) {
2151 0 0 0       $sftp->{_status} == SSH2_FX_NO_SUCH_FILE
2152             or $sftp->_ok_or_autodie or return undef;
2153             # no such file, no append
2154 0           undef $append;
2155             }
2156 0           $sftp->_clear_error_and_status;
2157             }
2158             }
2159              
2160 0           my ($atomic_numbered, $atomic_remote);
2161 0 0         if ($writeoff) {
2162             # one of $resume or $append is set
2163 0 0         if ($resume) {
2164 0 0 0       $debug and $debug & 16384 and _debug "resuming file transfer from $writeoff";
2165 0 0         if ($converter) {
    0          
2166             # as size could change, we have to read and convert
2167             # data until we reach the given position on the local
2168             # file:
2169 0           my $off = 0;
2170 0           my $eof_t;
2171 0           while (1) {
2172 0           my $len = length $converted_input;
2173 0           my $delta = $writeoff - $off;
2174 0 0         if ($delta <= $len) {
2175 0 0 0       $debug and $debug & 16384 and _debug "discarding $delta converted bytes";
2176 0           substr $converted_input, 0, $delta, '';
2177 0           last;
2178             }
2179             else {
2180 0           $off += $len;
2181 0 0         if ($eof_t) {
2182 0           $sftp->_set_error(SFTP_ERR_REMOTE_BIGGER_THAN_LOCAL,
2183             "Couldn't resume transfer, remote file is bigger than local");
2184 0           return undef;
2185             }
2186 0           my $read = CORE::read($fh, $converted_input, $block_size * 4);
2187 0 0         unless (defined $read) {
2188 0           $sftp->_set_error(SFTP_ERR_LOCAL_READ_ERROR,
2189             "Couldn't read from local file '$local' to the resume point $writeoff", $!);
2190 0           return undef;
2191             }
2192 0 0         $lsize += $converter->($converted_input) if defined $lsize;
2193 0 0         utf8::downgrade($converted_input, 1)
2194             or croak "converter introduced wide characters in data";
2195 0 0         $read or $eof_t = 1;
2196             }
2197             }
2198             }
2199             elsif ($local_is_fh) {
2200             # as some PerlIO layer could be installed on the $fh,
2201             # just seeking to the resume position will not be
2202             # enough. We have to read and discard data until the
2203             # desired offset is reached
2204 0           my $off = $writeoff;
2205 0           while ($off) {
2206 0 0         my $read = CORE::read($fh, my($buf), ($off < 16384 ? $off : 16384));
2207 0 0         if ($read) {
2208 0 0 0       $debug and $debug & 16384 and _debug "discarding $read bytes";
2209 0           $off -= $read;
2210             }
2211             else {
2212 0 0         $sftp->_set_error(defined $read
2213             ? ( SFTP_ERR_REMOTE_BIGGER_THAN_LOCAL,
2214             "Couldn't resume transfer, remote file is bigger than local")
2215             : ( SFTP_ERR_LOCAL_READ_ERROR,
2216             "Couldn't read from local file handler '$local' to the resume point $writeoff", $!));
2217             }
2218             }
2219             }
2220             else {
2221 0 0 0       if (defined $lsize and $writeoff > $lsize) {
2222 0           $sftp->_set_error(SFTP_ERR_REMOTE_BIGGER_THAN_LOCAL,
2223             "Couldn't resume transfer, remote file is bigger than local");
2224 0           return undef;
2225             }
2226 0 0         unless (CORE::seek($fh, $writeoff, 0)) {
2227 0           $sftp->_set_error(SFTP_ERR_LOCAL_SEEK_FAILED,
2228             "seek operation on local file failed: $!");
2229 0           return undef;
2230             }
2231             }
2232 0 0 0       if (defined $lsize and $writeoff == $lsize) {
2233 0 0 0       if (defined $perm and $rattrs->perm != $perm) {
2234             # FIXME: do copy_time here if required
2235 0           return $sftp->_best_effort($best_effort, setstat => $remote, $attrs);
2236             }
2237 0           return 1;
2238             }
2239             }
2240 0 0         $rfh = $sftp->open($remote, SSH2_FXF_WRITE)
2241             or return undef;
2242             }
2243             else {
2244 0 0         if ($atomic) {
2245             # check that does not exist a file of the same name that
2246             # would block the rename operation at the end
2247 0 0 0       if (!($numbered or $overwrite) and
      0        
2248             $sftp->test_e($remote)) {
2249 0           $sftp->_set_status(SSH2_FX_FAILURE);
2250 0           $sftp->_set_error(SFTP_ERR_REMOTE_ALREADY_EXISTS,
2251             "Remote file '$remote' already exists");
2252 0           return undef;
2253             }
2254 0           $atomic_remote = $remote;
2255 0           $remote .= sprintf("(%d).tmp", rand(10000));
2256 0           $atomic_numbered = $numbered;
2257 0           $numbered = 1;
2258 0 0 0       $debug and $debug & 128 and _debug("temporal remote file name: $remote");
2259             }
2260 0           local $sftp->{_autodie};
2261 0 0         if ($numbered) {
2262 0           while (1) {
2263 0           $rfh = $sftp->_open_mkpath($remote,
2264             $mkpath,
2265             SSH2_FXF_WRITE | SSH2_FXF_CREAT | SSH2_FXF_EXCL,
2266             $attrs);
2267             last if ($rfh or
2268 0 0 0       $sftp->{_status} != SSH2_FX_FAILURE or
      0        
2269             !$sftp->test_e($remote));
2270 0           _inc_numbered($remote);
2271             }
2272 0 0 0       $$numbered = $remote if $rfh and ref $numbered;
2273             }
2274             else {
2275             # open can fail due to a remote file with the wrong
2276             # permissions being already there. We are optimistic here,
2277             # first we try to open the remote file and if it fails due
2278             # to a permissions error then we remove it and try again.
2279 0           for my $rep (0, 1) {
2280 0 0         $rfh = $sftp->_open_mkpath($remote,
2281             $mkpath,
2282             SSH2_FXF_WRITE | SSH2_FXF_CREAT |
2283             ($overwrite ? SSH2_FXF_TRUNC : SSH2_FXF_EXCL),
2284             $attrs);
2285              
2286 0 0 0       last if $rfh or $rep or !$overwrite or $sftp->{_status} != SSH2_FX_PERMISSION_DENIED;
      0        
      0        
2287              
2288 0 0 0       $debug and $debug & 2 and _debug("retrying open after removing remote file");
2289 0           local ($sftp->{_status}, $sftp->{_error});
2290 0           $sftp->remove($remote);
2291             }
2292             }
2293             }
2294              
2295 0 0         $sftp->_ok_or_autodie or return undef;
2296             # Once this point is reached and for the remaining of the sub,
2297             # code should never return but jump into the CLEANUP block.
2298              
2299 0           my $last_block_was_zeros;
2300              
2301 0           do {
2302 0           local $sftp->{autodie};
2303              
2304             # In some SFTP server implementations, open does not set the
2305             # attributes for existent files so we do it again. The
2306             # $late_set_perm work around is for some servers that do not
2307             # support changing the permissions of open files
2308 0 0 0       if (defined $perm and !$late_set_perm) {
2309 0 0         $sftp->_best_effort($best_effort, setstat => $rfh, $attrs) or goto CLEANUP;
2310             }
2311              
2312 0           my $rfid = $sftp->_rfid($rfh);
2313 0 0         defined $rfid or die "internal error: rfid is undef";
2314              
2315             # In append mode we add the size of the remote file in
2316             # writeoff, if lsize is undef, we initialize it to $writeoff:
2317 0 0 0       $lsize += $writeoff if ($append or not defined $lsize);
2318              
2319             # when a converter is used, the EOF can become delayed by the
2320             # buffering introduced, we use $eof_t to account for that.
2321 0           my ($eof, $eof_t);
2322 0           my @msgid;
2323 0           OK: while (1) {
2324 0 0 0       if (!$eof and @msgid < $queue_size) {
2325 0           my ($data, $len);
2326 0 0         if ($converter) {
2327 0   0       while (!$eof_t and length $converted_input < $block_size) {
2328 0           my $read = CORE::read($fh, my $input, $block_size * 4);
2329 0 0         unless ($read) {
2330 0 0         unless (defined $read) {
2331 0           $sftp->_set_error(SFTP_ERR_LOCAL_READ_ERROR,
2332             "Couldn't read from local file '$local'", $!);
2333 0           last OK;
2334             }
2335 0           $eof_t = 1;
2336             }
2337              
2338             # note that the $converter is called a last time
2339             # with an empty string
2340 0           $lsize += $converter->($input);
2341 0 0         utf8::downgrade($input, 1)
2342             or croak "converter introduced wide characters in data";
2343 0           $converted_input .= $input;
2344             }
2345 0           $data = substr($converted_input, 0, $block_size, '');
2346 0           $len = length $data;
2347 0 0 0       $eof = 1 if ($eof_t and !$len);
2348             }
2349             else {
2350 0 0 0       $debug and $debug & 16384 and
2351             _debug "reading block at offset ".CORE::tell($fh)." block_size: $block_size";
2352              
2353 0           $len = CORE::read($fh, $data, $block_size);
2354              
2355 0 0         if ($len) {
2356 0 0 0       $debug and $debug & 16384 and _debug "block read, size: $len";
2357              
2358 0 0         utf8::downgrade($data, 1)
2359             or croak "wide characters unexpectedly read from file";
2360              
2361 0 0 0       $debug and $debug & 16384 and length $data != $len and
      0        
2362             _debug "read data changed size on downgrade to " . length($data);
2363             }
2364             else {
2365 0 0         unless (defined $len) {
2366 0           $sftp->_set_error(SFTP_ERR_LOCAL_READ_ERROR,
2367             "Couldn't read from local file '$local'", $!);
2368 0           last OK;
2369             }
2370 0           $eof = 1;
2371             }
2372             }
2373              
2374 0           my $nextoff = $writeoff + $len;
2375              
2376 0 0         if (defined $cb) {
2377 0 0         $lsize = $nextoff if $nextoff > $lsize;
2378 0           $cb->($sftp, $data, $writeoff, $lsize);
2379              
2380 0 0         last OK if $sftp->{_error};
2381              
2382 0 0         utf8::downgrade($data, 1) or croak "callback introduced wide characters in data";
2383              
2384 0           $len = length $data;
2385 0           $nextoff = $writeoff + $len;
2386             }
2387              
2388 0 0         if ($len) {
2389 0 0 0       if ($sparse and $data =~ /^\x{00}*$/s) {
2390 0           $last_block_was_zeros = 1;
2391 0 0 0       $debug and $debug & 16384 and _debug "skipping zeros block at offset $writeoff, length $len";
2392             }
2393             else {
2394 0 0 0       $debug and $debug & 16384 and _debug "writing block at offset $writeoff, length $len";
2395              
2396 0           my $id = $sftp->_queue_new_msg(SSH2_FXP_WRITE, str => $rfid,
2397             int64 => $writeoff, str => $data);
2398 0           push @msgid, $id;
2399 0           $last_block_was_zeros = 0;
2400             }
2401 0           $writeoff = $nextoff;
2402             }
2403             }
2404              
2405 0 0 0       last if ($eof and !@msgid);
2406              
2407 0 0 0       next unless ($eof
      0        
2408             or @msgid >= $queue_size
2409             or $sftp->_do_io(0));
2410              
2411 0           my $id = shift @msgid;
2412 0 0         unless ($sftp->_check_status_ok($id,
2413             SFTP_ERR_REMOTE_WRITE_FAILED,
2414             "Couldn't write to remote file")) {
2415 0           last OK;
2416             }
2417             }
2418              
2419 0 0         CORE::close $fh unless $local_is_fh;
2420              
2421 0           $sftp->_get_msg_by_id($_) for @msgid;
2422              
2423             $sftp->truncate($rfh, $writeoff)
2424 0 0 0       if $last_block_was_zeros and not $sftp->{_error};
2425              
2426 0           $sftp->_close_save_status($rfh);
2427              
2428 0 0         goto CLEANUP if $sftp->{_error};
2429              
2430             # set perm for servers that does not support setting
2431             # permissions on open files and also atime and mtime:
2432 0 0 0       if ($copy_time or ($late_set_perm and defined $perm)) {
      0        
2433 0 0 0       $attrs->set_perm unless $late_set_perm and defined $perm;
2434 0 0         $attrs->set_amtime($latime, $lmtime) if $copy_time;
2435 0 0         $sftp->_best_effort($best_effort, setstat => $remote, $attrs) or goto CLEANUP
2436             }
2437              
2438 0 0         if ($atomic) {
2439 0 0         $sftp->rename($remote, $atomic_remote,
2440             overwrite => $overwrite,
2441             numbered => $atomic_numbered) or goto CLEANUP;
2442             }
2443              
2444             CLEANUP:
2445 0 0 0       if ($cleanup and $sftp->{_error}) {
2446 0           warn "cleanup $remote";
2447 0           $sftp->_remove_save_status($remote);
2448             }
2449             };
2450 0           $sftp->_ok_or_autodie;
2451             }
2452              
2453             sub put_content {
2454 0 0   0 1   @_ >= 3 or croak 'Usage: $sftp->put_content($content, $remote, %opts)';
2455 0 0         ${^TAINT} and &_catch_tainted_args;
2456              
2457 0           my ($sftp, undef, $remote, %opts) = @_;
2458 0           my %put_opts = ( map { $_ => delete $opts{$_} }
  0            
2459             qw(perm umask block_size queue_size overwrite conversion resume
2460             numbered late_set_perm atomic best_effort mkpath));
2461 0 0         %opts and _croak_bad_options(keys %opts);
2462              
2463 0           my $fh;
2464 0 0         unless (CORE::open $fh, '<', \$_[1]) {
2465 0           $sftp->_set_error(SFTP_ERR_LOCAL_OPEN_FAILED, "Can't open scalar as file handle", $!);
2466 0           return undef;
2467             }
2468 0           $sftp->put($fh, $remote, %opts);
2469             }
2470              
2471             sub ls {
2472 0 0   0 1   @_ >= 1 or croak 'Usage: $sftp->ls($remote_dir, %opts)';
2473 0 0         ${^TAINT} and &_catch_tainted_args;
2474              
2475 0           my $sftp = shift;
2476 0 0         my %opts = @_ & 1 ? (dir => @_) : @_;
2477              
2478 0           my $dir = delete $opts{dir};
2479 0           my $ordered = delete $opts{ordered};
2480 0           my $follow_links = delete $opts{follow_links};
2481 0           my $atomic_readdir = delete $opts{atomic_readdir};
2482 0           my $names_only = delete $opts{names_only};
2483 0           my $realpath = delete $opts{realpath};
2484 0           my $queue_size = delete $opts{queue_size};
2485 0   0       my $cheap = ($names_only and !$realpath);
2486 0           my ($cheap_wanted, $wanted);
2487 0 0 0       if ($cheap and
      0        
2488             ref $opts{wanted} eq 'Regexp' and
2489             not defined $opts{no_wanted}) {
2490             $cheap_wanted = delete $opts{wanted}
2491 0           }
2492             else {
2493             $wanted = (delete $opts{_wanted} ||
2494             _gen_wanted(delete $opts{wanted},
2495 0   0       delete $opts{no_wanted}));
2496 0 0         undef $cheap if defined $wanted;
2497             }
2498              
2499 0 0         %opts and _croak_bad_options(keys %opts);
2500              
2501 0   0       my $delayed_wanted = ($atomic_readdir and $wanted);
2502 0 0 0       $queue_size = 1 if ($follow_links or $realpath or
      0        
      0        
2503             ($wanted and not $delayed_wanted));
2504 0   0       my $max_queue_size = $queue_size || $sftp->{_queue_size};
2505 0 0 0       $queue_size ||= ($max_queue_size < 2 ? $max_queue_size : 2);
2506              
2507 0 0         $dir = '.' unless defined $dir;
2508 0           $dir = $sftp->_rel2abs($dir);
2509              
2510 0           my $rdh = $sftp->opendir($dir);
2511 0 0         return unless defined $rdh;
2512              
2513 0           my $rdid = $sftp->_rdid($rdh);
2514 0 0         defined $rdid or return undef;
2515              
2516 0           my @dir;
2517             my @msgid;
2518              
2519 0           do {
2520 0           local $sftp->{_autodie};
2521 0           OK: while (1) {
2522 0           push @msgid, $sftp->_queue_str_request(SSH2_FXP_READDIR, $rdid)
2523             while (@msgid < $queue_size);
2524              
2525 0           my $id = shift @msgid;
2526 0 0         my $msg = $sftp->_get_msg_and_check(SSH2_FXP_NAME, $id,
2527             SFTP_ERR_REMOTE_READDIR_FAILED,
2528             "Couldn't read directory '$dir'" ) or last;
2529 0 0         my $count = $msg->get_int32 or last;
2530              
2531 0 0         if ($cheap) {
2532 0           for (1..$count) {
2533 0           my $fn = $sftp->_fs_decode($msg->get_str);
2534 0 0 0       push @dir, $fn if (!defined $cheap_wanted or $fn =~ $cheap_wanted);
2535 0           $msg->skip_str;
2536 0           Net::SFTP::Foreign::Attributes->skip_from_buffer($msg);
2537             }
2538             }
2539             else {
2540 0           for (1..$count) {
2541 0           my $fn = $sftp->_fs_decode($msg->get_str);
2542 0           my $ln = $sftp->_fs_decode($msg->get_str);
2543             # my $a = $msg->get_attributes;
2544 0           my $a = Net::SFTP::Foreign::Attributes->new_from_buffer($msg);
2545              
2546 0           my $entry = { filename => $fn,
2547             longname => $ln,
2548             a => $a };
2549              
2550 0 0 0       if ($follow_links and _is_lnk($a->perm)) {
2551              
2552 0 0         if ($a = $sftp->stat($sftp->join($dir, $fn))) {
2553 0           $entry->{a} = $a;
2554             }
2555             else {
2556 0           $sftp->_clear_error_and_status;
2557             }
2558             }
2559              
2560 0 0         if ($realpath) {
2561 0           my $rp = $sftp->realpath($sftp->join($dir, $fn));
2562 0 0         if (defined $rp) {
2563 0           $fn = $entry->{realpath} = $rp;
2564             }
2565             else {
2566 0           $sftp->_clear_error_and_status;
2567             }
2568             }
2569              
2570 0 0 0       if (!$wanted or $delayed_wanted or $wanted->($sftp, $entry)) {
      0        
2571 0 0 0       push @dir, (($names_only and !$delayed_wanted) ? $fn : $entry);
2572             }
2573             }
2574             }
2575 0 0         $queue_size++ if $queue_size < $max_queue_size;
2576             }
2577 0 0         $sftp->_set_error if $sftp->{_status} == SSH2_FX_EOF;
2578 0           $sftp->_get_msg_by_id($_) for @msgid;
2579 0 0         $sftp->_closedir_save_status($rdh) if $rdh;
2580             };
2581 0 0         unless ($sftp->{_error}) {
2582 0 0         if ($delayed_wanted) {
2583 0           @dir = grep { $wanted->($sftp, $_) } @dir;
  0            
2584 0 0         @dir = map { defined $_->{realpath}
2585             ? $_->{realpath}
2586 0 0         : $_->{filename} } @dir
2587             if $names_only;
2588             }
2589 0 0         if ($ordered) {
2590 0 0         if ($names_only) {
2591 0           @dir = sort @dir;
2592             }
2593             else {
2594 0           _sort_entries \@dir;
2595             }
2596             }
2597 0           return \@dir;
2598             }
2599 0 0         croak $sftp->{_error} if $sftp->{_autodie};
2600 0           return undef;
2601             }
2602              
2603             sub rremove {
2604 0 0   0 1   @_ >= 2 or croak 'Usage: $sftp->rremove($dirs, %opts)';
2605 0 0         ${^TAINT} and &_catch_tainted_args;
2606              
2607 0           my ($sftp, $dirs, %opts) = @_;
2608              
2609 0           my $on_error = delete $opts{on_error};
2610 0 0         local $sftp->{_autodie} if $on_error;
2611             my $wanted = _gen_wanted( delete $opts{wanted},
2612 0           delete $opts{no_wanted});
2613              
2614 0 0         %opts and _croak_bad_options(keys %opts);
2615              
2616 0           my $count = 0;
2617              
2618 0           my @dirs;
2619             $sftp->find( $dirs,
2620             on_error => $on_error,
2621             atomic_readdir => 1,
2622             wanted => sub {
2623 0     0     my $e = $_[1];
2624 0           my $fn = $e->{filename};
2625 0 0         if (_is_dir($e->{a}->perm)) {
2626 0           push @dirs, $e;
2627             }
2628             else {
2629 0 0 0       if (!$wanted or $wanted->($sftp, $e)) {
2630 0 0         if ($sftp->remove($fn)) {
2631 0           $count++;
2632             }
2633             else {
2634 0           $sftp->_call_on_error($on_error, $e);
2635             }
2636             }
2637             }
2638 0           } );
2639              
2640 0           _sort_entries(\@dirs);
2641              
2642 0           while (@dirs) {
2643 0           my $e = pop @dirs;
2644 0 0 0       if (!$wanted or $wanted->($sftp, $e)) {
2645 0 0         if ($sftp->rmdir($e->{filename})) {
2646 0           $count++;
2647             }
2648             else {
2649 0           $sftp->_call_on_error($on_error, $e);
2650             }
2651             }
2652             }
2653              
2654 0           return $count;
2655             }
2656              
2657             sub get_symlink {
2658 0 0   0 1   @_ >= 3 or croak 'Usage: $sftp->get_symlink($remote, $local, %opts)';
2659 0           my ($sftp, $remote, $local, %opts) = @_;
2660 0           my $overwrite = delete $opts{overwrite};
2661 0           my $numbered = delete $opts{numbered};
2662              
2663 0 0 0       croak "'overwrite' and 'numbered' can not be used together"
2664             if ($overwrite and $numbered);
2665 0 0         %opts and _croak_bad_options(keys %opts);
2666              
2667 0 0 0       $overwrite = 1 unless (defined $overwrite or $numbered);
2668              
2669 0 0         my $a = $sftp->lstat($remote) or return undef;
2670 0 0         unless (_is_lnk($a->perm)) {
2671 0           $sftp->_set_error(SFTP_ERR_REMOTE_BAD_OBJECT,
2672             "Remote object '$remote' is not a symlink");
2673 0           return undef;
2674             }
2675              
2676 0 0         my $link = $sftp->readlink($remote) or return undef;
2677              
2678             # TODO: this is too weak, may contain race conditions.
2679 0 0         if ($numbered) {
    0          
2680 0           _inc_numbered($local) while -e $local;
2681             }
2682             elsif (-e $local) {
2683 0 0         if ($overwrite) {
2684 0           unlink $local;
2685             }
2686             else {
2687 0           $sftp->_set_error(SFTP_ERR_LOCAL_ALREADY_EXISTS,
2688             "local file $local already exists");
2689             return undef
2690 0           }
2691             }
2692              
2693 0 0         unless (eval { CORE::symlink $link, $local }) {
  0            
2694 0           $sftp->_set_error(SFTP_ERR_LOCAL_SYMLINK_FAILED,
2695             "creation of symlink '$local' failed", $!);
2696 0           return undef;
2697             }
2698 0 0         $$numbered = $local if ref $numbered;
2699              
2700 0           1;
2701             }
2702              
2703             sub put_symlink {
2704 0 0   0 1   @_ >= 3 or croak 'Usage: $sftp->put_symlink($local, $remote, %opts)';
2705 0           my ($sftp, $local, $remote, %opts) = @_;
2706 0           my $overwrite = delete $opts{overwrite};
2707 0           my $numbered = delete $opts{numbered};
2708              
2709 0 0 0       croak "'overwrite' and 'numbered' can not be used together"
2710             if ($overwrite and $numbered);
2711 0 0         %opts and _croak_bad_options(keys %opts);
2712              
2713 0 0 0       $overwrite = 1 unless (defined $overwrite or $numbered);
2714 0           my $perm = (CORE::lstat $local)[2];
2715 0 0         unless (defined $perm) {
2716 0           $sftp->_set_error(SFTP_ERR_LOCAL_STAT_FAILED,
2717             "Couldn't stat local file '$local'", $!);
2718 0           return undef;
2719             }
2720 0 0         unless (_is_lnk($perm)) {
2721 0           $sftp->_set_error(SFTP_ERR_LOCAL_BAD_OBJECT,
2722             "Local file $local is not a symlink");
2723 0           return undef;
2724             }
2725 0           my $target = readlink $local;
2726 0 0         unless (defined $target) {
2727 0           $sftp->_set_error(SFTP_ERR_LOCAL_READLINK_FAILED,
2728             "Couldn't read link '$local'", $!);
2729 0           return undef;
2730             }
2731              
2732 0           while (1) {
2733 0           local $sftp->{_autodie};
2734 0           $sftp->symlink($remote, $target);
2735 0 0 0       if ($sftp->{_error} and
2736             $sftp->{_status} == SSH2_FX_FAILURE) {
2737 0 0 0       if ($numbered and $sftp->test_e($remote)) {
    0 0        
2738 0           _inc_numbered($remote);
2739 0           redo;
2740             }
2741             elsif ($overwrite and $sftp->_remove_save_status($remote)) {
2742 0           $overwrite = 0;
2743 0           redo;
2744             }
2745             }
2746             last
2747 0           }
2748 0 0         $$numbered = $remote if ref $numbered;
2749 0           $sftp->_ok_or_autodie;
2750             }
2751              
2752             sub rget {
2753 0 0   0 1   @_ >= 2 or croak 'Usage: $sftp->rget($remote, $local, %opts)';
2754 0 0         ${^TAINT} and &_catch_tainted_args;
2755 0           my ($sftp, $remote, $local, %opts) = @_;
2756              
2757 0 0         defined $remote or croak "remote file path is undefined";
2758 0 0         $local = File::Spec->curdir unless defined $local;
2759              
2760             # my $cb = delete $opts{callback};
2761 0           my $umask = delete $opts{umask};
2762 0 0         my $copy_perm = delete $opts{exists $opts{copy_perm} ? 'copy_perm' : 'copy_perms'};
2763 0           my $copy_time = delete $opts{copy_time};
2764 0           my $newer_only = delete $opts{newer_only};
2765 0           my $on_error = delete $opts{on_error};
2766 0 0         local $sftp->{_autodie} if $on_error;
2767 0           my $ignore_links = delete $opts{ignore_links};
2768 0           my $mkpath = delete $opts{mkpath};
2769              
2770             # my $relative_links = delete $opts{relative_links};
2771              
2772             my $wanted = _gen_wanted( delete $opts{wanted},
2773 0           delete $opts{no_wanted} );
2774              
2775 0           my %get_opts = (map { $_ => delete $opts{$_} }
  0            
2776             qw(block_size queue_size overwrite conversion
2777             resume numbered atomic best_effort));
2778              
2779 0 0 0       if ($get_opts{resume} and $get_opts{conversion}) {
2780 0           carp "resume option is useless when data conversion has also been requested";
2781 0           delete $get_opts{resume};
2782             }
2783              
2784 0           my %get_symlink_opts = (map { $_ => $get_opts{$_} }
  0            
2785             qw(overwrite numbered));
2786              
2787 0 0         %opts and _croak_bad_options(keys %opts);
2788              
2789 0           $remote = $sftp->join($remote, './');
2790 0           my $qremote = quotemeta $remote;
2791 0           my $reremote = qr/^$qremote(.*)$/i;
2792              
2793 0           my $save = _umask_save_and_set $umask;
2794              
2795 0 0         $copy_perm = 1 unless defined $copy_perm;
2796 0 0         $copy_time = 1 unless defined $copy_time;
2797 0 0         $mkpath = 1 unless defined $mkpath;
2798              
2799 0           my $count = 0;
2800             $sftp->find( [$remote],
2801             descend => sub {
2802 0     0     my $e = $_[1];
2803             # print "descend: $e->{filename}\n";
2804 0 0 0       if (!$wanted or $wanted->($sftp, $e)) {
2805 0           my $fn = $e->{filename};
2806 0 0         if ($fn =~ $reremote) {
2807 0           my $lpath = File::Spec->catdir($local, $1);
2808 0 0         ($lpath) = $lpath =~ /(.*)/ if ${^TAINT};
2809 0 0         if (-d $lpath) {
2810 0           $sftp->_set_error(SFTP_ERR_LOCAL_ALREADY_EXISTS,
2811             "directory '$lpath' already exists");
2812 0           $sftp->_call_on_error($on_error, $e);
2813 0           return 1;
2814             }
2815             else {
2816 0 0         my $perm = ($copy_perm ? $e->{a}->perm & 0777 : 0777);
2817 0 0 0       if (CORE::mkdir($lpath, $perm) or
      0        
2818             ($mkpath and $sftp->_mkpath_local($lpath, $perm))) {
2819 0           $count++;
2820 0           return 1;
2821             }
2822 0           $sftp->_set_error(SFTP_ERR_LOCAL_MKDIR_FAILED,
2823             "mkdir '$lpath' failed", $!);
2824             }
2825             }
2826             else {
2827 0           $sftp->_set_error(SFTP_ERR_REMOTE_BAD_PATH,
2828             "bad remote path '$fn'");
2829             }
2830 0           $sftp->_call_on_error($on_error, $e);
2831             }
2832 0           return undef;
2833             },
2834             wanted => sub {
2835 0     0     my $e = $_[1];
2836 0 0         unless (_is_dir($e->{a}->perm)) {
2837 0 0 0       if (!$wanted or $wanted->($sftp, $e)) {
2838 0           my $fn = $e->{filename};
2839 0 0         if ($fn =~ $reremote) {
2840 0 0         my $lpath = ((length $1) ? File::Spec->catfile($local, $1) : $local);
2841             # print "file fn:$e->{filename}, lpath:$lpath, re:$reremote\n";
2842 0 0         ($lpath) = $lpath =~ /(.*)/ if ${^TAINT};
2843 0 0 0       if (_is_lnk($e->{a}->perm) and !$ignore_links) {
    0          
2844 0 0         if ($sftp->get_symlink($fn, $lpath,
2845             # copy_time => $copy_time,
2846             %get_symlink_opts)) {
2847 0           $count++;
2848 0           return undef;
2849             }
2850             }
2851             elsif (_is_reg($e->{a}->perm)) {
2852 0 0 0       if ($newer_only and -e $lpath
      0        
2853             and (CORE::stat _)[9] >= $e->{a}->mtime) {
2854 0           $sftp->_set_error(SFTP_ERR_LOCAL_ALREADY_EXISTS,
2855             "newer local file '$lpath' already exists");
2856             }
2857             else {
2858 0 0         if ($sftp->get($fn, $lpath,
2859             copy_perm => $copy_perm,
2860             copy_time => $copy_time,
2861             %get_opts)) {
2862 0           $count++;
2863 0           return undef;
2864             }
2865             }
2866             }
2867             else {
2868 0 0         $sftp->_set_error(SFTP_ERR_REMOTE_BAD_OBJECT,
2869             ( $ignore_links
2870             ? "remote file '$fn' is not regular file or directory"
2871             : "remote file '$fn' is not regular file, directory or link"));
2872             }
2873             }
2874             else {
2875 0           $sftp->_set_error(SFTP_ERR_REMOTE_BAD_PATH,
2876             "bad remote path '$fn'");
2877             }
2878 0           $sftp->_call_on_error($on_error, $e);
2879             }
2880             }
2881 0           return undef;
2882 0           } );
2883              
2884 0           return $count;
2885             }
2886              
2887             sub rput {
2888 0 0   0 1   @_ >= 2 or croak 'Usage: $sftp->rput($local, $remote, %opts)';
2889 0 0         ${^TAINT} and &_catch_tainted_args;
2890              
2891 0           my ($sftp, $local, $remote, %opts) = @_;
2892              
2893 0 0         defined $local or croak "local path is undefined";
2894 0 0         $remote = '.' unless defined $remote;
2895              
2896             # my $cb = delete $opts{callback};
2897 0           my $umask = delete $opts{umask};
2898 0           my $perm = delete $opts{perm};
2899 0 0         my $copy_perm = delete $opts{exists $opts{copy_perm} ? 'copy_perm' : 'copy_perms'};
2900 0           my $copy_time = delete $opts{copy_time};
2901              
2902 0           my $newer_only = delete $opts{newer_only};
2903 0           my $on_error = delete $opts{on_error};
2904 0 0         local $sftp->{_autodie} if $on_error;
2905 0           my $ignore_links = delete $opts{ignore_links};
2906 0           my $mkpath = delete $opts{mkpath};
2907              
2908             my $wanted = _gen_wanted( delete $opts{wanted},
2909 0           delete $opts{no_wanted} );
2910              
2911 0           my %put_opts = (map { $_ => delete $opts{$_} }
  0            
2912             qw(block_size queue_size overwrite
2913             conversion resume numbered
2914             late_set_perm atomic best_effort
2915             sparse));
2916              
2917 0           my %put_symlink_opts = (map { $_ => $put_opts{$_} }
  0            
2918             qw(overwrite numbered));
2919              
2920 0 0 0       croak "'perm' and 'umask' options can not be used simultaneously"
2921             if (defined $perm and defined $umask);
2922 0 0 0       croak "'perm' and 'copy_perm' options can not be used simultaneously"
2923             if (defined $perm and $copy_perm);
2924              
2925 0 0         %opts and _croak_bad_options(keys %opts);
2926              
2927 0           require Net::SFTP::Foreign::Local;
2928 0           my $lfs = Net::SFTP::Foreign::Local->new;
2929              
2930 0           $local = $lfs->join($local, './');
2931 0           my $relocal;
2932 0 0         if ($local =~ m|^\./?$|) {
2933 0           $relocal = qr/^(.*)$/;
2934             }
2935             else {
2936 0           my $qlocal = quotemeta $local;
2937 0           $relocal = qr/^$qlocal(.*)$/i;
2938             }
2939              
2940 0 0         $copy_perm = 1 unless defined $copy_perm;
2941 0 0         $copy_time = 1 unless defined $copy_time;
2942 0 0         $mkpath = 1 unless defined $mkpath;
2943              
2944 0           my $mask;
2945 0 0         if (defined $perm) {
2946 0           $mask = $perm & 0777;
2947             }
2948             else {
2949 0 0         $umask = umask unless defined $umask;
2950 0           $mask = 0777 & ~$umask;
2951             }
2952              
2953 0 0         if ($on_error) {
2954 0           my $on_error1 = $on_error;
2955             $on_error = sub {
2956 0     0     my $lfs = shift;
2957 0           $sftp->_copy_error($lfs);
2958 0           $sftp->_call_on_error($on_error1, @_);
2959             }
2960 0           }
2961              
2962 0           my $count = 0;
2963             $lfs->find( [$local],
2964             descend => sub {
2965 0     0     my $e = $_[1];
2966             # print "descend: $e->{filename}\n";
2967 0 0 0       if (!$wanted or $wanted->($lfs, $e)) {
2968 0           my $fn = $e->{filename};
2969 0 0 0       $debug and $debug & 32768 and _debug "rput handling $fn";
2970 0 0         if ($fn =~ $relocal) {
2971 0           my $rpath = $sftp->join($remote, File::Spec->splitdir($1));
2972 0 0 0       $debug and $debug & 32768 and _debug "rpath: $rpath";
2973 0           my $a = Net::SFTP::Foreign::Attributes->new;
2974 0 0         if (defined $perm) {
    0          
2975 0           $a->set_perm($mask | 0300);
2976             }
2977             elsif ($copy_perm) {
2978 0           $a->set_perm($e->{a}->perm & $mask);
2979             }
2980 0 0         if ($sftp->mkdir($rpath, $a)) {
2981 0           $count++;
2982 0           return 1;
2983             }
2984 0 0 0       if ($mkpath and
2985             $sftp->status == SSH2_FX_NO_SUCH_FILE) {
2986 0           $sftp->_clear_error_and_status;
2987 0 0         if ($sftp->mkpath($rpath, $a)) {
2988 0           $count++;
2989 0           return 1;
2990             }
2991             }
2992 0           $lfs->_copy_error($sftp);
2993 0 0         if ($sftp->test_d($rpath)) {
2994 0           $lfs->_set_error(SFTP_ERR_REMOTE_ALREADY_EXISTS,
2995             "Remote directory '$rpath' already exists");
2996 0           $lfs->_call_on_error($on_error, $e);
2997 0           return 1;
2998             }
2999             }
3000             else {
3001 0           $lfs->_set_error(SFTP_ERR_LOCAL_BAD_PATH,
3002             "Bad local path '$fn'");
3003             }
3004 0           $lfs->_call_on_error($on_error, $e);
3005             }
3006 0           return undef;
3007             },
3008             wanted => sub {
3009 0     0     my $e = $_[1];
3010             # print "file fn:$e->{filename}, a:$e->{a}\n";
3011 0 0         unless (_is_dir($e->{a}->perm)) {
3012 0 0 0       if (!$wanted or $wanted->($lfs, $e)) {
3013 0           my $fn = $e->{filename};
3014 0 0 0       $debug and $debug & 32768 and _debug "rput handling $fn";
3015 0 0         if ($fn =~ $relocal) {
3016 0           my (undef, $d, $f) = File::Spec->splitpath($1);
3017 0           my $rpath = $sftp->join($remote, File::Spec->splitdir($d), $f);
3018 0 0 0       if (_is_lnk($e->{a}->perm) and !$ignore_links) {
    0          
3019 0 0         if ($sftp->put_symlink($fn, $rpath,
3020             %put_symlink_opts)) {
3021 0           $count++;
3022 0           return undef;
3023             }
3024 0           $lfs->_copy_error($sftp);
3025             }
3026             elsif (_is_reg($e->{a}->perm)) {
3027 0           my $ra;
3028 0 0 0       if ( $newer_only and
      0        
3029             $ra = $sftp->stat($rpath) and
3030             $ra->mtime >= $e->{a}->mtime) {
3031 0           $lfs->_set_error(SFTP_ERR_REMOTE_ALREADY_EXISTS,
3032             "Newer remote file '$rpath' already exists");
3033             }
3034             else {
3035 0 0         if ($sftp->put($fn, $rpath,
    0          
    0          
3036             ( defined($perm) ? (perm => $perm)
3037             : $copy_perm ? (perm => $e->{a}->perm & $mask)
3038             : (copy_perm => 0, umask => $umask) ),
3039             copy_time => $copy_time,
3040             %put_opts)) {
3041 0           $count++;
3042 0           return undef;
3043             }
3044 0           $lfs->_copy_error($sftp);
3045             }
3046             }
3047             else {
3048 0 0         $lfs->_set_error(SFTP_ERR_LOCAL_BAD_OBJECT,
3049             ( $ignore_links
3050             ? "Local file '$fn' is not regular file or directory"
3051             : "Local file '$fn' is not regular file, directory or link"));
3052             }
3053             }
3054             else {
3055 0           $lfs->_set_error(SFTP_ERR_LOCAL_BAD_PATH,
3056             "Bad local path '$fn'");
3057             }
3058 0           $lfs->_call_on_error($on_error, $e);
3059             }
3060             }
3061 0           return undef;
3062 0           } );
3063              
3064 0           return $count;
3065             }
3066              
3067             sub mget {
3068 0 0   0 1   @_ >= 2 or croak 'Usage: $sftp->mget($remote, $localdir, %opts)';
3069 0 0         ${^TAINT} and &_catch_tainted_args;
3070              
3071 0           my ($sftp, $remote, $localdir, %opts) = @_;
3072              
3073 0 0         defined $remote or croak "remote pattern is undefined";
3074              
3075 0           my $on_error = $opts{on_error};
3076 0 0         local $sftp->{_autodie} if $on_error;
3077 0           my $ignore_links = delete $opts{ignore_links};
3078              
3079 0           my %glob_opts = (map { $_ => delete $opts{$_} }
  0            
3080             qw(on_error follow_links ignore_case
3081             wanted no_wanted strict_leading_dot));
3082              
3083 0           my %get_symlink_opts = (map { $_ => $opts{$_} }
  0            
3084             qw(overwrite numbered));
3085              
3086 0           my %get_opts = (map { $_ => delete $opts{$_} }
  0            
3087             qw(umask perm copy_perm copy_time block_size queue_size
3088             overwrite conversion resume numbered atomic best_effort mkpath));
3089              
3090 0 0         %opts and _croak_bad_options(keys %opts);
3091              
3092 0           my @remote = map $sftp->glob($_, %glob_opts), _ensure_list $remote;
3093              
3094 0           my $count = 0;
3095              
3096 0           require File::Spec;
3097 0           for my $e (@remote) {
3098 0           my $perm = $e->{a}->perm;
3099 0 0         if (_is_dir($perm)) {
3100 0           $sftp->_set_error(SFTP_ERR_REMOTE_BAD_OBJECT,
3101             "Remote object '$e->{filename}' is a directory");
3102             }
3103             else {
3104 0           my $fn = $e->{filename};
3105 0           my ($local) = $fn =~ m{([^\\/]*)$};
3106              
3107 0 0         $local = File::Spec->catfile($localdir, $local)
3108             if defined $localdir;
3109              
3110 0 0         if (_is_lnk($perm)) {
3111 0 0         next if $ignore_links;
3112 0           $sftp->get_symlink($fn, $local, %get_symlink_opts);
3113             }
3114             else {
3115 0           $sftp->get($fn, $local, %get_opts);
3116             }
3117             }
3118 0 0         $count++ unless $sftp->{_error};
3119 0           $sftp->_call_on_error($on_error, $e);
3120             }
3121 0           $count;
3122             }
3123              
3124             sub mput {
3125 0 0   0 1   @_ >= 2 or croak 'Usage: $sftp->mput($local, $remotedir, %opts)';
3126              
3127 0           my ($sftp, $local, $remotedir, %opts) = @_;
3128              
3129 0 0         defined $local or die "local pattern is undefined";
3130              
3131 0           my $on_error = $opts{on_error};
3132 0 0         local $sftp->{_autodie} if $on_error;
3133 0           my $ignore_links = delete $opts{ignore_links};
3134              
3135 0           my %glob_opts = (map { $_ => delete $opts{$_} }
  0            
3136             qw(on_error follow_links ignore_case
3137             wanted no_wanted strict_leading_dot));
3138 0           my %put_symlink_opts = (map { $_ => $opts{$_} }
  0            
3139             qw(overwrite numbered));
3140              
3141 0           my %put_opts = (map { $_ => delete $opts{$_} }
  0            
3142             qw(umask perm copy_perm copy_time block_size queue_size
3143             overwrite conversion resume numbered late_set_perm
3144             atomic best_effort sparse mkpath));
3145              
3146 0 0         %opts and _croak_bad_options(keys %opts);
3147              
3148 0           require Net::SFTP::Foreign::Local;
3149 0           my $lfs = Net::SFTP::Foreign::Local->new;
3150 0           my @local = map $lfs->glob($_, %glob_opts), _ensure_list $local;
3151              
3152 0           my $count = 0;
3153 0           require File::Spec;
3154 0           for my $e (@local) {
3155 0           my $perm = $e->{a}->perm;
3156 0 0         if (_is_dir($perm)) {
3157 0           $sftp->_set_error(SFTP_ERR_REMOTE_BAD_OBJECT,
3158             "Remote object '$e->{filename}' is a directory");
3159             }
3160             else {
3161 0           my $fn = $e->{filename};
3162 0           my $remote = (File::Spec->splitpath($fn))[2];
3163 0 0         $remote = $sftp->join($remotedir, $remote)
3164             if defined $remotedir;
3165              
3166 0 0         if (_is_lnk($perm)) {
3167 0 0         next if $ignore_links;
3168 0           $sftp->put_symlink($fn, $remote, %put_symlink_opts);
3169             }
3170             else {
3171 0           $sftp->put($fn, $remote, %put_opts);
3172             }
3173             }
3174 0 0         $count++ unless $sftp->{_error};
3175 0           $sftp->_call_on_error($on_error, $e);
3176             }
3177 0           $count;
3178             }
3179              
3180             sub fsync {
3181 0 0   0 1   @_ == 2 or croak 'Usage: $sftp->fsync($fh)';
3182 0 0         ${^TAINT} and &_catch_tainted_args;
3183              
3184 0           my ($sftp, $fh) = @_;
3185              
3186 0           $sftp->flush($fh, "out");
3187 0 0         $sftp->_check_extension('fsync@openssh.com' => 1,
3188             SFTP_ERR_REMOTE_FSYNC_FAILED,
3189             "fsync failed, not implemented")
3190             or return undef;
3191              
3192 0           my $id = $sftp->_queue_new_msg(SSH2_FXP_EXTENDED,
3193             str => 'fsync@openssh.com',
3194             str => $sftp->_rid($fh));
3195 0 0         if ($sftp->_check_status_ok($id,
3196             SFTP_ERR_REMOTE_FSYNC_FAILED,
3197             "Couldn't fsync remote file")) {
3198 0           return 1;
3199             }
3200 0           return undef;
3201             }
3202              
3203             sub statvfs {
3204 0 0   0 1   @_ == 2 or croak 'Usage: $sftp->statvfs($path_or_fh)';
3205 0 0         ${^TAINT} and &_catch_tainted_args;
3206              
3207 0           my ($sftp, $pofh) = @_;
3208 0 0 0       my ($extension, $arg) = ( (ref $pofh and UNIVERSAL::isa($pofh, 'Net::SFTP::Foreign::FileHandle'))
3209             ? ('fstatvfs@openssh.com', $sftp->_rid($pofh) )
3210             : ('statvfs@openssh.com' , $sftp->_fs_encode($sftp->_rel2abs($pofh)) ) );
3211              
3212 0 0         $sftp->_check_extension($extension => 2,
3213             SFTP_ERR_REMOTE_STATVFS_FAILED,
3214             "statvfs failed, not implemented")
3215             or return undef;
3216              
3217 0           my $id = $sftp->_queue_new_msg(SSH2_FXP_EXTENDED,
3218             str => $extension,
3219             str => $arg);
3220              
3221 0 0         if (my $msg = $sftp->_get_msg_and_check(SSH2_FXP_EXTENDED_REPLY, $id,
3222             SFTP_ERR_REMOTE_STATVFS_FAILED,
3223             "Couldn't stat remote file system")) {
3224 0           my %statvfs = map { $_ => $msg->get_int64 } qw(bsize frsize blocks
  0            
3225             bfree bavail files ffree
3226             favail fsid flag namemax);
3227 0           return \%statvfs;
3228             }
3229 0           return undef;
3230             }
3231              
3232             sub fstatvfs {
3233 0     0 1   _deprecated "fstatvfs is deprecated and will be removed on the upcoming 2.xx series, "
3234             . "statvfs method accepts now both file handlers and paths";
3235 0           goto &statvfs;
3236             }
3237              
3238             package Net::SFTP::Foreign::Handle;
3239              
3240 3     3   1945 use Tie::Handle;
  3         5969  
  3         6015  
3241             our @ISA = qw(Tie::Handle);
3242             our @CARP_NOT = qw(Net::SFTP::Foreign Tie::Handle);
3243              
3244             my $gen_accessor = sub {
3245             my $ix = shift;
3246             sub {
3247 0     0     my $st = *{shift()}{ARRAY};
  0            
3248 0 0         if (@_) {
3249 0           $st->[$ix] = shift;
3250             }
3251             else {
3252 0           $st->[$ix]
3253             }
3254             }
3255             };
3256              
3257             my $gen_proxy_method = sub {
3258             my $method = shift;
3259             sub {
3260 0     0     my $self = $_[0];
3261 0 0         $self->_check
3262             or return undef;
3263              
3264 0           my $sftp = $self->_sftp;
3265 0 0         if (wantarray) {
3266 0           my @ret = $sftp->$method(@_);
3267 0 0         $sftp->_set_errno unless @ret;
3268 0           return @ret;
3269             }
3270             else {
3271 0           my $ret = $sftp->$method(@_);
3272 0 0         $sftp->_set_errno unless defined $ret;
3273 0           return $ret;
3274             }
3275             }
3276             };
3277              
3278             my $gen_not_supported = sub {
3279             sub {
3280 0     0     $! = Errno::ENOTSUP();
3281             undef
3282 0           }
3283             };
3284              
3285 0     0     sub TIEHANDLE { return shift }
3286              
3287             # sub UNTIE {}
3288              
3289             sub _new_from_rid {
3290 0     0     my $class = shift;
3291 0           my $sftp = shift;
3292 0           my $rid = shift;
3293 0   0       my $flags = shift || 0;
3294              
3295 0           my $self = Symbol::gensym;
3296 0           bless $self, $class;
3297 0           *$self = [ $sftp, $rid, 0, $flags, @_];
3298 0           tie *$self, $self;
3299              
3300 0           $self;
3301             }
3302              
3303             sub _close {
3304 0     0     my $self = shift;
3305 0           @{*{$self}{ARRAY}} = ();
  0            
  0            
3306             }
3307              
3308             sub _check {
3309 0 0   0     return 1 if defined(*{shift()}{ARRAY}[0]);
  0            
3310 0           $! = Errno::EBADF();
3311 0           undef;
3312             }
3313              
3314             sub FILENO {
3315 0     0     my $self = shift;
3316 0 0         $self->_check
3317             or return undef;
3318              
3319 0           my $hrid = unpack 'H*' => $self->_rid;
3320 0           "-1:sftp(0x$hrid)"
3321             }
3322              
3323 0     0     sub _sftp { *{shift()}{ARRAY}[0] }
  0            
3324 0     0     sub _rid { *{shift()}{ARRAY}[1] }
  0            
3325              
3326             * _pos = $gen_accessor->(2);
3327              
3328             sub _inc_pos {
3329 0     0     my ($self, $inc) = @_;
3330 0           *{shift()}{ARRAY}[2] += $inc;
  0            
3331             }
3332              
3333              
3334             my %flag_bit = (append => 0x1);
3335              
3336             sub _flag {
3337 0     0     my $st = *{shift()}{ARRAY};
  0            
3338 0           my $fn = shift;
3339 0           my $flag = $flag_bit{$fn};
3340 0 0         Carp::croak("unknown flag $fn") unless defined $flag;
3341 0 0         if (@_) {
3342 0 0         if (shift) {
3343 0           $st->[3] |= $flag;
3344             }
3345             else {
3346 0           $st->[3] &= ~$flag;
3347             }
3348             }
3349 0 0         $st->[3] & $flag ? 1 : 0
3350             }
3351              
3352             sub _check_is_file {
3353 0     0     Carp::croak("expecting remote file handler, got directory handler");
3354             }
3355             sub _check_is_dir {
3356 0     0     Carp::croak("expecting remote directory handler, got file handler");
3357             }
3358              
3359             my $autoloaded;
3360             sub AUTOLOAD {
3361 0     0     my $self = shift;
3362 0           our $AUTOLOAD;
3363 0 0         if ($autoloaded) {
3364 0   0       my $class = ref $self || $self;
3365 0           Carp::croak qq|Can't locate object method "$AUTOLOAD" via package "$class|;
3366             }
3367             else {
3368 0           $autoloaded = 1;
3369 0           require IO::File;
3370 0           require IO::Dir;
3371 0           my ($method) = $AUTOLOAD =~ /^.*::(.*)$/;
3372 0           $self->$method(@_);
3373             }
3374             }
3375              
3376             package Net::SFTP::Foreign::FileHandle;
3377             our @ISA = qw(Net::SFTP::Foreign::Handle IO::File);
3378              
3379             sub _new_from_rid {
3380 0     0     my $class = shift;
3381 0           my $sftp = shift;
3382 0           my $rid = shift;
3383 0           my $flags = shift;
3384              
3385 0           my $self = $class->SUPER::_new_from_rid($sftp, $rid, $flags, '', '');
3386             }
3387              
3388       0     sub _check_is_file {}
3389              
3390 0     0     sub _bin { \(*{shift()}{ARRAY}[4]) }
  0            
3391 0     0     sub _bout { \(*{shift()}{ARRAY}[5]) }
  0            
3392              
3393             sub WRITE {
3394 0     0     my ($self, undef, $length, $offset) = @_;
3395 0 0         $self->_check
3396             or return undef;
3397              
3398 0 0         $offset = 0 unless defined $offset;
3399 0 0         $offset = length $_[1] + $offset if $offset < 0;
3400 0 0         $length = length $_[1] unless defined $length;
3401              
3402 0           my $sftp = $self->_sftp;
3403              
3404 0           my $ret = $sftp->write($self, substr($_[1], $offset, $length));
3405 0 0         $sftp->_set_errno unless defined $ret;
3406 0           $ret;
3407             }
3408              
3409             sub READ {
3410 0     0     my ($self, undef, $len, $offset) = @_;
3411 0 0         $self->_check
3412             or return undef;
3413              
3414 0 0         $_[1] = '' unless defined $_[1];
3415 0   0       $offset ||= 0;
3416 0 0         if ($offset > length $_[1]) {
3417 0           $_[1] .= "\0" x ($offset - length $_[1])
3418             }
3419              
3420 0 0         if ($len == 0) {
3421 0           substr($_[1], $offset) = '';
3422 0           return 0;
3423             }
3424              
3425 0           my $sftp = $self->_sftp;
3426 0           $sftp->_fill_read_cache($self, $len);
3427              
3428 0           my $bin = $self->_bin;
3429 0 0         if (length $$bin) {
3430 0           my $data = substr($$bin, 0, $len, '');
3431 0           $self->_inc_pos($len);
3432 0           substr($_[1], $offset) = $data;
3433 0           return length $data;
3434             }
3435 0 0         return 0 if $sftp->{_status} == $sftp->SSH2_FX_EOF;
3436 0           $sftp->_set_errno;
3437 0           undef;
3438             }
3439              
3440             sub EOF {
3441 0     0     my $self = $_[0];
3442 0 0         $self->_check or return undef;
3443 0           my $sftp = $self->_sftp;
3444 0           my $ret = $sftp->eof($self);
3445 0 0         $sftp->_set_errno unless defined $ret;
3446 0           $ret;
3447             }
3448              
3449             *GETC = $gen_proxy_method->('getc');
3450             *TELL = $gen_proxy_method->('tell');
3451             *SEEK = $gen_proxy_method->('seek');
3452             *CLOSE = $gen_proxy_method->('close');
3453              
3454             my $readline = $gen_proxy_method->('readline');
3455 0     0     sub READLINE { $readline->($_[0], $/) }
3456              
3457             sub OPEN {
3458 0     0     shift->CLOSE;
3459 0           undef;
3460             }
3461              
3462             sub DESTROY {
3463 0     0     local ($@, $!, $?);
3464 0           my $self = shift;
3465 0           my $sftp = $self->_sftp;
3466 0 0 0       $debug and $debug & 4 and Net::SFTP::Foreign::_debug("$self->DESTROY called (sftp: ".($sftp||'').")");
      0        
3467 0 0 0       if ($self->_check and $sftp) {
3468 0           local $sftp->{_autodie};
3469 0           $sftp->_close_save_status($self)
3470             }
3471             }
3472              
3473             package Net::SFTP::Foreign::DirHandle;
3474             our @ISA = qw(Net::SFTP::Foreign::Handle IO::Dir);
3475              
3476             sub _new_from_rid {
3477 0     0     my $class = shift;
3478 0           my $sftp = shift;
3479 0           my $rid = shift;
3480 0           my $flags = shift;
3481              
3482 0           my $self = $class->SUPER::_new_from_rid($sftp, $rid, $flags, []);
3483             }
3484              
3485              
3486       0     sub _check_is_dir {}
3487              
3488 0     0     sub _cache { *{shift()}{ARRAY}[4] }
  0            
3489              
3490             *CLOSEDIR = $gen_proxy_method->('closedir');
3491             *READDIR = $gen_proxy_method->('_readdir');
3492              
3493             sub OPENDIR {
3494 0     0     shift->CLOSEDIR;
3495 0           undef;
3496             }
3497              
3498             *REWINDDIR = $gen_not_supported->();
3499             *TELLDIR = $gen_not_supported->();
3500             *SEEKDIR = $gen_not_supported->();
3501              
3502             sub DESTROY {
3503 0     0     local ($@, $!, $?);
3504 0           my $self = shift;
3505 0           my $sftp = $self->_sftp;
3506              
3507 0 0 0       $debug and $debug & 4 and Net::SFTP::Foreign::_debug("$self->DESTROY called (sftp: ".($sftp||'').")");
      0        
3508              
3509 0 0 0       if ($self->_check and $sftp) {
3510 0           local $sftp->{_autodie};
3511 0           $sftp->_closedir_save_status($self)
3512             }
3513             }
3514              
3515             1;
3516             __END__