| 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__ |