line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
# File : Net::FTPSSL |
2
|
|
|
|
|
|
|
# Author : cleach |
3
|
|
|
|
|
|
|
# Created : 01 March 2005 |
4
|
|
|
|
|
|
|
# Version : 0.40 |
5
|
|
|
|
|
|
|
# Revision: -Id: FTPSSL.pm,v 1.24 2005/10/23 14:37:12 cleach Exp - |
6
|
|
|
|
|
|
|
|
7
|
|
|
|
|
|
|
package Net::FTPSSL; |
8
|
|
|
|
|
|
|
|
9
|
4
|
|
|
4
|
|
296472
|
use strict; |
|
4
|
|
|
|
|
33
|
|
|
4
|
|
|
|
|
111
|
|
10
|
4
|
|
|
4
|
|
22
|
use warnings; |
|
4
|
|
|
|
|
7
|
|
|
4
|
|
|
|
|
125
|
|
11
|
|
|
|
|
|
|
|
12
|
|
|
|
|
|
|
# Enforce a minimum version of this module or Net::FTPSSL hangs! |
13
|
|
|
|
|
|
|
# v1.08 works, v1.18 added ccc() support. |
14
|
|
|
|
|
|
|
# Don't use v1.79 to v1.85 due to misleading warnings. |
15
|
4
|
|
|
4
|
|
2941
|
use IO::Socket::SSL 1.26; |
|
4
|
|
|
|
|
289132
|
|
|
4
|
|
|
|
|
33
|
|
16
|
|
|
|
|
|
|
|
17
|
4
|
|
|
4
|
|
741
|
use vars qw( $VERSION @EXPORT $ERRSTR ); |
|
4
|
|
|
|
|
7
|
|
|
4
|
|
|
|
|
274
|
|
18
|
4
|
|
|
4
|
|
24
|
use base ( 'Exporter', 'IO::Socket::SSL' ); |
|
4
|
|
|
|
|
7
|
|
|
4
|
|
|
|
|
630
|
|
19
|
|
|
|
|
|
|
|
20
|
|
|
|
|
|
|
# Only supports IPv4 (to also get IPv6 must use IO::Socket::IP instead. v0.20) |
21
|
4
|
|
|
4
|
|
23
|
use IO::Socket::INET; |
|
4
|
|
|
|
|
8
|
|
|
4
|
|
|
|
|
46
|
|
22
|
|
|
|
|
|
|
|
23
|
4
|
|
|
4
|
|
3641
|
use Net::SSLeay::Handle; |
|
4
|
|
|
|
|
7394
|
|
|
4
|
|
|
|
|
165
|
|
24
|
4
|
|
|
4
|
|
27
|
use File::Basename; |
|
4
|
|
|
|
|
7
|
|
|
4
|
|
|
|
|
302
|
|
25
|
4
|
|
|
4
|
|
452
|
use File::Copy; |
|
4
|
|
|
|
|
4842
|
|
|
4
|
|
|
|
|
173
|
|
26
|
4
|
|
|
4
|
|
1418
|
use Time::Local; |
|
4
|
|
|
|
|
6070
|
|
|
4
|
|
|
|
|
203
|
|
27
|
4
|
|
|
4
|
|
1815
|
use Sys::Hostname; |
|
4
|
|
|
|
|
4133
|
|
|
4
|
|
|
|
|
223
|
|
28
|
4
|
|
|
4
|
|
27
|
use Carp qw( carp croak ); |
|
4
|
|
|
|
|
7
|
|
|
4
|
|
|
|
|
161
|
|
29
|
4
|
|
|
4
|
|
21
|
use Errno qw/ EINTR /; |
|
4
|
|
|
|
|
6
|
|
|
4
|
|
|
|
|
220
|
|
30
|
|
|
|
|
|
|
|
31
|
|
|
|
|
|
|
@EXPORT = qw( IMP_CRYPT EXP_CRYPT CLR_CRYPT |
32
|
|
|
|
|
|
|
DATA_PROT_CLEAR DATA_PROT_PRIVATE |
33
|
|
|
|
|
|
|
DATA_PROT_SAFE DATA_PROT_CONFIDENTIAL |
34
|
|
|
|
|
|
|
CMD_INFO CMD_OK CMD_MORE CMD_REJECT |
35
|
|
|
|
|
|
|
CMD_ERROR CMD_PROTECT CMD_PENDING ); |
36
|
|
|
|
|
|
|
$ERRSTR = "No Errors Detected Yet."; |
37
|
|
|
|
|
|
|
|
38
|
|
|
|
|
|
|
# Command Channel Protection Levels |
39
|
4
|
|
|
4
|
|
22
|
use constant IMP_CRYPT => "I"; |
|
4
|
|
|
|
|
7
|
|
|
4
|
|
|
|
|
206
|
|
40
|
4
|
|
|
4
|
|
21
|
use constant EXP_CRYPT => "E"; # Default |
|
4
|
|
|
|
|
6
|
|
|
4
|
|
|
|
|
153
|
|
41
|
4
|
|
|
4
|
|
42
|
use constant CLR_CRYPT => "C"; |
|
4
|
|
|
|
|
14
|
|
|
4
|
|
|
|
|
198
|
|
42
|
|
|
|
|
|
|
|
43
|
|
|
|
|
|
|
# Data Channel Protection Levels |
44
|
4
|
|
|
4
|
|
24
|
use constant DATA_PROT_CLEAR => "C"; # Least secure! |
|
4
|
|
|
|
|
8
|
|
|
4
|
|
|
|
|
197
|
|
45
|
4
|
|
|
4
|
|
24
|
use constant DATA_PROT_SAFE => "S"; |
|
4
|
|
|
|
|
6
|
|
|
4
|
|
|
|
|
180
|
|
46
|
4
|
|
|
4
|
|
21
|
use constant DATA_PROT_CONFIDENTIAL => "E"; |
|
4
|
|
|
|
|
44
|
|
|
4
|
|
|
|
|
171
|
|
47
|
4
|
|
|
4
|
|
23
|
use constant DATA_PROT_PRIVATE => "P"; # Default & most secure! |
|
4
|
|
|
|
|
5
|
|
|
4
|
|
|
|
|
183
|
|
48
|
|
|
|
|
|
|
|
49
|
|
|
|
|
|
|
# Valid FTP Result codes |
50
|
4
|
|
|
4
|
|
20
|
use constant CMD_INFO => 1; |
|
4
|
|
|
|
|
8
|
|
|
4
|
|
|
|
|
197
|
|
51
|
4
|
|
|
4
|
|
23
|
use constant CMD_OK => 2; |
|
4
|
|
|
|
|
5
|
|
|
4
|
|
|
|
|
269
|
|
52
|
4
|
|
|
4
|
|
25
|
use constant CMD_MORE => 3; |
|
4
|
|
|
|
|
7
|
|
|
4
|
|
|
|
|
196
|
|
53
|
4
|
|
|
4
|
|
20
|
use constant CMD_REJECT => 4; |
|
4
|
|
|
|
|
8
|
|
|
4
|
|
|
|
|
187
|
|
54
|
4
|
|
|
4
|
|
21
|
use constant CMD_ERROR => 5; |
|
4
|
|
|
|
|
6
|
|
|
4
|
|
|
|
|
180
|
|
55
|
4
|
|
|
4
|
|
22
|
use constant CMD_PROTECT => 6; |
|
4
|
|
|
|
|
19
|
|
|
4
|
|
|
|
|
194
|
|
56
|
4
|
|
|
4
|
|
27
|
use constant CMD_PENDING => 0; |
|
4
|
|
|
|
|
13
|
|
|
4
|
|
|
|
|
200
|
|
57
|
|
|
|
|
|
|
|
58
|
|
|
|
|
|
|
# -------- Above Exported ---- Below don't bother to export -------- |
59
|
|
|
|
|
|
|
|
60
|
|
|
|
|
|
|
# File transfer modes (the mixed modes have no code) |
61
|
4
|
|
|
4
|
|
41
|
use constant MODE_BINARY => "I"; |
|
4
|
|
|
|
|
9
|
|
|
4
|
|
|
|
|
190
|
|
62
|
4
|
|
|
4
|
|
20
|
use constant MODE_ASCII => "A"; # Default |
|
4
|
|
|
|
|
6
|
|
|
4
|
|
|
|
|
165
|
|
63
|
|
|
|
|
|
|
|
64
|
|
|
|
|
|
|
# The Data Connection Setup Commands ... |
65
|
|
|
|
|
|
|
# Passive Options ... (All pasive modes are currently supported) |
66
|
4
|
|
|
4
|
|
19
|
use constant FTPS_PASV => 1; # Default mode ... |
|
4
|
|
|
|
|
8
|
|
|
4
|
|
|
|
|
192
|
|
67
|
4
|
|
|
4
|
|
22
|
use constant FTPS_EPSV_1 => 2; # EPSV 1 - Internet Protocol Version 4 |
|
4
|
|
|
|
|
28
|
|
|
4
|
|
|
|
|
188
|
|
68
|
4
|
|
|
4
|
|
24
|
use constant FTPS_EPSV_2 => 3; # EPSV 2 - Internet Protocol Version 6 |
|
4
|
|
|
|
|
6
|
|
|
4
|
|
|
|
|
211
|
|
69
|
|
|
|
|
|
|
# Active Options ... (No active modes are currently supported) |
70
|
4
|
|
|
4
|
|
22
|
use constant FTPS_PORT => 4; |
|
4
|
|
|
|
|
8
|
|
|
4
|
|
|
|
|
170
|
|
71
|
4
|
|
|
4
|
|
21
|
use constant FTPS_EPRT_1 => 5; # EPRT 1 - Internet Protocol Version 4 |
|
4
|
|
|
|
|
28
|
|
|
4
|
|
|
|
|
189
|
|
72
|
4
|
|
|
4
|
|
23
|
use constant FTPS_EPRT_2 => 6; # EPRT 2 - Internet Protocol Version 6 |
|
4
|
|
|
|
|
7
|
|
|
4
|
|
|
|
|
152
|
|
73
|
|
|
|
|
|
|
|
74
|
|
|
|
|
|
|
# Misc constants |
75
|
4
|
|
|
4
|
|
21
|
use constant TRACE_MOD => 10; # How many iterations between ".". Must be >= 5. |
|
4
|
|
|
|
|
14
|
|
|
4
|
|
|
|
|
1397
|
|
76
|
|
|
|
|
|
|
|
77
|
|
|
|
|
|
|
# Primarily used while the call to new() is still in scope! |
78
|
|
|
|
|
|
|
my $FTPS_ERROR; |
79
|
|
|
|
|
|
|
|
80
|
|
|
|
|
|
|
# Used to handle trapping all warnings accross class instances |
81
|
|
|
|
|
|
|
my %warn_list; |
82
|
|
|
|
|
|
|
|
83
|
|
|
|
|
|
|
# Tells if possible to use IPv6 in connections. |
84
|
|
|
|
|
|
|
my $ipv6; |
85
|
|
|
|
|
|
|
my $IOCLASS; |
86
|
|
|
|
|
|
|
my $family_key; # Domain or Family |
87
|
|
|
|
|
|
|
my $debug_log_msg; # Used if Debug is turned on |
88
|
|
|
|
|
|
|
|
89
|
|
|
|
|
|
|
|
90
|
|
|
|
|
|
|
BEGIN { |
91
|
4
|
|
|
4
|
|
15
|
$VERSION = "0.40"; # The version of this module! |
92
|
|
|
|
|
|
|
|
93
|
4
|
|
|
|
|
8
|
my $type = "IO::Socket::SSL"; |
94
|
4
|
|
|
|
|
6
|
$ipv6 = 0; # Assume IPv4 only ... |
95
|
4
|
|
|
|
|
7
|
$IOCLASS = "IO::Socket::INET"; # Assume IPv4 only ... |
96
|
4
|
|
|
|
|
6
|
$family_key = "Domain"; # Traditional ... |
97
|
4
|
|
|
|
|
6
|
my $msg; |
98
|
|
|
|
|
|
|
|
99
|
4
|
|
|
|
|
8
|
my $ioOrig = $IOCLASS; |
100
|
|
|
|
|
|
|
|
101
|
|
|
|
|
|
|
# Can we use IPv6 vs IPv4? Let IO::Socket::SSL make the decision for us! |
102
|
|
|
|
|
|
|
# The logic gets real messy otherwise. |
103
|
4
|
50
|
|
|
|
86
|
if ( ! $type->can ("can_ipv6") ) { |
|
|
50
|
|
|
|
|
|
104
|
0
|
|
|
|
|
0
|
$msg = "No IPv6 support available. You must 1st upgrade $type to support it!"; |
105
|
|
|
|
|
|
|
|
106
|
|
|
|
|
|
|
} elsif ( $type->can_ipv6 () ) { |
107
|
4
|
|
|
|
|
21
|
$ipv6 = 1; # Yes! IPv6 can be suporteed! |
108
|
4
|
|
|
|
|
8
|
$IOCLASS = $type->can_ipv6 (); # Get which IPv6 module SSL uses. |
109
|
4
|
50
|
|
|
|
22
|
$family_key = "Family" if ( $IOCLASS eq "IO::Socket::IP" ); |
110
|
4
|
|
|
|
|
32
|
my $ver = $IOCLASS->VERSION; |
111
|
4
|
|
|
|
|
20
|
$msg = "IPv6 support available via $IOCLASS ($ver) Key: ($family_key)"; |
112
|
|
|
|
|
|
|
|
113
|
|
|
|
|
|
|
} else { |
114
|
0
|
|
|
|
|
0
|
$msg = "No IPv6 support available. Missing required modules!"; |
115
|
|
|
|
|
|
|
} |
116
|
|
|
|
|
|
|
|
117
|
|
|
|
|
|
|
# Now let's provide the logfile header information ... |
118
|
|
|
|
|
|
|
# Done here so only have to generate this information one time! |
119
|
4
|
|
|
|
|
40
|
my $pv = sprintf ("%s [%vd]", $], $^V); # The version of perl! |
120
|
|
|
|
|
|
|
|
121
|
|
|
|
|
|
|
# Required info when opening a CPAN ticket against this module ... |
122
|
4
|
|
|
|
|
13
|
$debug_log_msg = "\n" |
123
|
|
|
|
|
|
|
. "Net-FTPSSL Version: $VERSION\n"; |
124
|
|
|
|
|
|
|
|
125
|
|
|
|
|
|
|
# Print out versions of critical modules we depend on ... |
126
|
4
|
|
|
|
|
10
|
foreach ( "IO-Socket-SSL", "Net-SSLeay", |
127
|
|
|
|
|
|
|
"IO-Socket-INET", "IO-Socket-INET6", |
128
|
|
|
|
|
|
|
"IO-Socket-IP", "IO", |
129
|
|
|
|
|
|
|
"Socket" ) { |
130
|
28
|
|
|
|
|
36
|
my $mod = $_; |
131
|
28
|
|
|
|
|
67
|
$mod =~ s/-/::/g; |
132
|
28
|
|
|
|
|
220
|
my $ver = $mod->VERSION; |
133
|
28
|
100
|
|
|
|
72
|
if ( defined $ver ) { |
134
|
24
|
|
|
|
|
50
|
$debug_log_msg .= "$_ Version: $ver\n"; |
135
|
|
|
|
|
|
|
} else { |
136
|
4
|
|
|
|
|
12
|
$debug_log_msg .= "$_ might not be installed.\n"; |
137
|
|
|
|
|
|
|
} |
138
|
|
|
|
|
|
|
} |
139
|
|
|
|
|
|
|
|
140
|
4
|
|
|
|
|
35
|
$debug_log_msg .= "\n${msg}\n\n" |
141
|
|
|
|
|
|
|
. "Perl: $pv, OS: $^O\n\n"; |
142
|
|
|
|
|
|
|
|
143
|
|
|
|
|
|
|
# Comment out/remove when ready to implement iIPv6 ... |
144
|
4
|
|
|
|
|
10
|
$IOCLASS = $ioOrig; $family_key = "Domain"; |
|
4
|
|
|
|
|
5
|
|
145
|
4
|
|
|
|
|
95133
|
$debug_log_msg .= "***** IPv6 not yet supported in Net::FTPSSL! *****\n\n"; |
146
|
|
|
|
|
|
|
} |
147
|
|
|
|
|
|
|
|
148
|
|
|
|
|
|
|
# ============================================================================ |
149
|
|
|
|
|
|
|
|
150
|
|
|
|
|
|
|
sub new { |
151
|
0
|
|
|
0
|
1
|
|
my $self = shift; |
152
|
0
|
|
0
|
|
|
|
my $type = ref($self) || $self; |
153
|
0
|
|
|
|
|
|
my $host = shift; |
154
|
0
|
0
|
|
|
|
|
my $arg = (ref ($_[0]) eq "HASH") ? $_[0] : {@_}; |
155
|
|
|
|
|
|
|
|
156
|
|
|
|
|
|
|
|
157
|
|
|
|
|
|
|
# Make sure to reset in case previous call to new generated an error! |
158
|
0
|
|
|
|
|
|
$ERRSTR = "No Errors Detected Yet."; |
159
|
|
|
|
|
|
|
|
160
|
|
|
|
|
|
|
# Used as a quick way to detect if the caller passed us SSL_* arguments ... |
161
|
0
|
|
|
|
|
|
my $found_ssl_args = 0; |
162
|
|
|
|
|
|
|
|
163
|
|
|
|
|
|
|
# The hash to pass to start_ssl() ... |
164
|
0
|
|
|
|
|
|
my %ssl_args; |
165
|
|
|
|
|
|
|
|
166
|
|
|
|
|
|
|
# Depreciated in v0.18 (in 2011) and fatal in v0.26 (2015) |
167
|
|
|
|
|
|
|
# Will remove this test sometime in 2017/2018! |
168
|
0
|
0
|
|
|
|
|
if (ref ($arg->{SSL_Advanced}) eq "HASH") { |
169
|
0
|
|
|
|
|
|
%ssl_args = %{$arg->{SSL_Advanced}}; |
|
0
|
|
|
|
|
|
|
170
|
0
|
|
|
|
|
|
$ERRSTR = "SSL_Advanced is no longer supported! Using a separate hash is no longer required for adding SSL options!"; |
171
|
0
|
|
|
|
|
|
croak ("\n" . $ERRSTR . "\n"); |
172
|
|
|
|
|
|
|
} |
173
|
|
|
|
|
|
|
|
174
|
|
|
|
|
|
|
# May depreciate in the near future in favor of the "grep" loop below! |
175
|
|
|
|
|
|
|
# Debating the merrits of having two ways to do this. |
176
|
0
|
0
|
|
|
|
|
if (ref ($arg->{SSL_Client_Certificate}) eq "HASH") { |
177
|
|
|
|
|
|
|
# The main purpose of this option was to allow users to specify |
178
|
|
|
|
|
|
|
# client certificates when their FTPS server requires them. |
179
|
|
|
|
|
|
|
# This hash applies to both the command & data channels. |
180
|
|
|
|
|
|
|
# Tags specified here overrided normal options if any tags |
181
|
|
|
|
|
|
|
# conflict. |
182
|
|
|
|
|
|
|
# See IO::Socket::SSL for supported options. |
183
|
0
|
|
|
|
|
|
%ssl_args = %{$arg->{SSL_Client_Certificate}}; |
|
0
|
|
|
|
|
|
|
184
|
0
|
|
|
|
|
|
$found_ssl_args = 1; |
185
|
|
|
|
|
|
|
} |
186
|
|
|
|
|
|
|
|
187
|
|
|
|
|
|
|
# See IO::Socket::SSL for supported options. |
188
|
|
|
|
|
|
|
# Provides a way to directly pass needed SSL_* arguments to this module. |
189
|
|
|
|
|
|
|
# There is only one Net::FTPSSL option that starts with SSL_, so skipping it! |
190
|
0
|
|
|
|
|
|
for (grep { m{^SSL_} } keys %{$arg}) { |
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
191
|
0
|
0
|
|
|
|
|
next if ( $_ eq "SSL_Client_Certificate" ); # The FTPSSL opt to skip! |
192
|
0
|
|
|
|
|
|
$ssl_args{$_} = $arg->{$_}; |
193
|
0
|
|
|
|
|
|
$found_ssl_args = 1; |
194
|
|
|
|
|
|
|
} |
195
|
|
|
|
|
|
|
|
196
|
|
|
|
|
|
|
# Only add if not using certificates & the caller didn't provide a value ... |
197
|
0
|
0
|
0
|
|
|
|
unless ( $ssl_args{SSL_use_cert} || $ssl_args{SSL_verify_mode} ) { |
198
|
|
|
|
|
|
|
# Stops the Man-In-The-Middle (MITM) security warning from start_ssl() |
199
|
|
|
|
|
|
|
# when it calls configure_SSL() in IO::Socket::SSL. |
200
|
|
|
|
|
|
|
# To plug that MITM security hole requires the use of certificates, |
201
|
|
|
|
|
|
|
# so all that's being done here is supressing the warning. The MITM |
202
|
|
|
|
|
|
|
# security hole is still open! |
203
|
|
|
|
|
|
|
# That warning is now a fatal error in newer versions of IO::Socket::SSL. |
204
|
|
|
|
|
|
|
# warn "WARNING: Your connection is vunerable to the MITM attacks\n"; |
205
|
0
|
|
|
|
|
|
$ssl_args{SSL_verify_mode} = Net::SSLeay::VERIFY_NONE(); |
206
|
|
|
|
|
|
|
} |
207
|
|
|
|
|
|
|
|
208
|
|
|
|
|
|
|
# -------------------------------------------------------------------------- |
209
|
|
|
|
|
|
|
# Will hold all the control options to this class |
210
|
|
|
|
|
|
|
# Similar in use as _SSL_arguments ... |
211
|
0
|
|
|
|
|
|
my %ftpssl_args; |
212
|
|
|
|
|
|
|
|
213
|
|
|
|
|
|
|
# Now onto processing the regular hash of arguments provided ... |
214
|
0
|
|
0
|
|
|
|
my $encrypt_mode = $arg->{Encryption} || EXP_CRYPT; |
215
|
0
|
|
0
|
|
|
|
my $port = $arg->{Port} || (($encrypt_mode eq IMP_CRYPT) ? 990 : 21); |
216
|
0
|
|
0
|
|
|
|
my $debug = $arg->{Debug} || 0; |
217
|
0
|
|
0
|
|
|
|
my $trace = $arg->{Trace} || 0; |
218
|
0
|
|
0
|
|
|
|
my $timeout = $ssl_args{Timeout} || $arg->{Timeout} || 120; |
219
|
0
|
|
0
|
|
|
|
my $buf_size = $arg->{Buffer} || 10240; |
220
|
|
|
|
|
|
|
my $data_prot = ($encrypt_mode eq CLR_CRYPT) ? DATA_PROT_CLEAR |
221
|
0
|
0
|
0
|
|
|
|
: ($arg->{DataProtLevel} || DATA_PROT_PRIVATE); |
222
|
0
|
|
0
|
|
|
|
my $die = $arg->{Croak} || $arg->{Die}; |
223
|
0
|
|
0
|
|
|
|
my $pres_ts = $arg->{PreserveTimestamp} || 0; |
224
|
0
|
|
0
|
|
|
|
my $use_ssl = $arg->{useSSL} || 0; # Being depreciated. |
225
|
|
|
|
|
|
|
|
226
|
0
|
|
|
|
|
|
my ($use_logfile, $use_glob) = (0, 0); |
227
|
0
|
0
|
0
|
|
|
|
if ( $debug && defined $arg->{DebugLogFile} ) { |
228
|
|
|
|
|
|
|
$use_logfile = (ref ($arg->{DebugLogFile}) eq "" && |
229
|
0
|
|
0
|
|
|
|
$arg->{DebugLogFile} ne ""); |
230
|
0
|
|
|
|
|
|
$use_glob = _isa_glob (undef, $arg->{DebugLogFile}); |
231
|
|
|
|
|
|
|
} |
232
|
0
|
|
0
|
|
|
|
my $localaddr = $ssl_args{LocalAddr} || $arg->{LocalAddr}; |
233
|
0
|
|
0
|
|
|
|
my $pret = $arg->{Pret} || 0; |
234
|
0
|
|
0
|
|
|
|
my $domain = $arg->{Domain} || $arg->{Family}; |
235
|
0
|
|
0
|
|
|
|
my $xWait = $arg->{xWait} || 0; |
236
|
|
|
|
|
|
|
|
237
|
0
|
|
0
|
|
|
|
my $reuseSession = $arg->{ReuseSession} || 0; |
238
|
|
|
|
|
|
|
|
239
|
|
|
|
|
|
|
# Default to true unless you request to disable it or no encryption used ... |
240
|
0
|
0
|
0
|
|
|
|
my $enableCtx = ($arg->{DisableContext} || $encrypt_mode eq CLR_CRYPT) ? 0 : 1; |
241
|
|
|
|
|
|
|
|
242
|
|
|
|
|
|
|
# Used to work arround some FTPS servers behaving badly! |
243
|
0
|
|
|
|
|
|
my $pasvHost = $arg->{OverridePASV}; |
244
|
0
|
|
|
|
|
|
my $fixHelp = $arg->{OverrideHELP}; |
245
|
|
|
|
|
|
|
|
246
|
|
|
|
|
|
|
# -------------------------------------------------------------------------- |
247
|
|
|
|
|
|
|
# if ( $debug && ! exists $arg->{DebugLogFile} ) { |
248
|
|
|
|
|
|
|
# # So will write any debug comments to STDERR ... |
249
|
|
|
|
|
|
|
# $IO::Socket::SSL::DEBUG = 3; |
250
|
|
|
|
|
|
|
# } |
251
|
|
|
|
|
|
|
|
252
|
|
|
|
|
|
|
# A special case used for further debugging the response! |
253
|
|
|
|
|
|
|
# This special value is undocumented in the POD on purpose! |
254
|
0
|
0
|
|
|
|
|
my $debug_extra = ($debug == 99) ? 1 : 0; |
255
|
|
|
|
|
|
|
|
256
|
|
|
|
|
|
|
# Special case for eliminating listing help text during login! |
257
|
0
|
0
|
|
|
|
|
my $no_login_help = ($debug == 90) ? 1 : 0; |
258
|
|
|
|
|
|
|
|
259
|
0
|
|
|
|
|
|
my $f_exists = 0; |
260
|
|
|
|
|
|
|
|
261
|
|
|
|
|
|
|
# Determine where to write the Debug info to ... |
262
|
0
|
0
|
|
|
|
|
if ( $use_logfile ) { |
|
|
0
|
|
|
|
|
|
263
|
0
|
0
|
|
|
|
|
my $open_mode = ( $debug == 2 ) ? ">>" : ">"; |
264
|
0
|
|
|
|
|
|
my $f = $arg->{DebugLogFile}; |
265
|
0
|
0
|
0
|
|
|
|
unlink ( $f ) if ( -f $f && $open_mode ne ">>" ); |
266
|
0
|
|
|
|
|
|
$f_exists = (-f $f); |
267
|
|
|
|
|
|
|
|
268
|
|
|
|
|
|
|
# Always calls die on failure to open the requested log file ... |
269
|
0
|
0
|
|
|
|
|
open ( $FTPS_ERROR, "$open_mode $f" ) or |
270
|
|
|
|
|
|
|
_croak_or_return (undef, 1, 0, |
271
|
|
|
|
|
|
|
"Can't create debug logfile: $f ($!)"); |
272
|
|
|
|
|
|
|
|
273
|
0
|
|
|
|
|
|
$debug = 2; # Save the file handle & later close it ... |
274
|
|
|
|
|
|
|
|
275
|
|
|
|
|
|
|
} elsif ( $use_glob ) { |
276
|
0
|
|
|
|
|
|
$FTPS_ERROR = $arg->{DebugLogFile}; |
277
|
0
|
|
|
|
|
|
$debug = 3; # Save the file handle, but never close it ... |
278
|
|
|
|
|
|
|
} |
279
|
|
|
|
|
|
|
|
280
|
0
|
0
|
0
|
|
|
|
if ( $use_logfile || $use_glob ) { |
|
|
0
|
|
|
|
|
|
281
|
0
|
0
|
|
|
|
|
unless ( $f_exists ) { |
282
|
0
|
|
|
|
|
|
print $FTPS_ERROR $debug_log_msg; |
283
|
|
|
|
|
|
|
} else { |
284
|
0
|
|
|
|
|
|
print $FTPS_ERROR "\n\n"; |
285
|
|
|
|
|
|
|
} |
286
|
|
|
|
|
|
|
|
287
|
|
|
|
|
|
|
} elsif ( $debug ) { |
288
|
0
|
|
|
|
|
|
$debug = 1; # No file handle to save ... |
289
|
|
|
|
|
|
|
|
290
|
|
|
|
|
|
|
# open ( $FTPS_ERROR, ">&STDERR" ) or |
291
|
|
|
|
|
|
|
# _croak_or_return (undef, 1, 0, |
292
|
|
|
|
|
|
|
# "Can't attach the debug logfile to STDERR. ($!)"); |
293
|
|
|
|
|
|
|
# $FTPS_ERROR->autoflush (1); |
294
|
|
|
|
|
|
|
|
295
|
0
|
|
|
|
|
|
print STDERR $debug_log_msg; |
296
|
|
|
|
|
|
|
} |
297
|
|
|
|
|
|
|
|
298
|
0
|
0
|
|
|
|
|
if ( $debug ) { |
299
|
0
|
|
|
|
|
|
_print_LOG (undef, "Server (port): $host ($port)\n\n"); |
300
|
0
|
|
|
|
|
|
_print_LOG (undef, "Keys: (", join ("), (", keys %${arg}), ")\n"); |
301
|
0
|
|
|
|
|
|
_print_LOG (undef, "Values: (", join ("), (", values %${arg}), ")\n\n"); |
302
|
|
|
|
|
|
|
} |
303
|
|
|
|
|
|
|
|
304
|
|
|
|
|
|
|
# Determines if we die if we will also need to write to the error log file ... |
305
|
0
|
0
|
|
|
|
|
my $dbg_flg = $die ? ( $debug >= 2 ? 1 : 0 ) : $debug; |
|
|
0
|
|
|
|
|
|
306
|
|
|
|
|
|
|
|
307
|
0
|
0
|
|
|
|
|
return _croak_or_return (undef, $die, $dbg_flg, "Host undefined") unless $host; |
308
|
|
|
|
|
|
|
|
309
|
0
|
0
|
0
|
|
|
|
return _croak_or_return (undef, $die, $dbg_flg, |
|
|
|
0
|
|
|
|
|
310
|
|
|
|
|
|
|
"Encryption mode unknown! ($encrypt_mode)") |
311
|
|
|
|
|
|
|
if ( $encrypt_mode ne IMP_CRYPT && $encrypt_mode ne EXP_CRYPT && |
312
|
|
|
|
|
|
|
$encrypt_mode ne CLR_CRYPT ); |
313
|
|
|
|
|
|
|
|
314
|
0
|
0
|
0
|
|
|
|
return _croak_or_return (undef, $die, $dbg_flg, |
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
315
|
|
|
|
|
|
|
"Data Channel mode unknown! ($data_prot)") |
316
|
|
|
|
|
|
|
if ( $data_prot ne DATA_PROT_CLEAR && |
317
|
|
|
|
|
|
|
$data_prot ne DATA_PROT_SAFE && |
318
|
|
|
|
|
|
|
$data_prot ne DATA_PROT_CONFIDENTIAL && |
319
|
|
|
|
|
|
|
$data_prot ne DATA_PROT_PRIVATE ); |
320
|
|
|
|
|
|
|
|
321
|
0
|
0
|
0
|
|
|
|
if ( $ipv6 && defined $domain ) { |
322
|
0
|
0
|
|
|
|
|
my $fmly = (exists $arg->{Domain}) ? "Domain" : "Family"; |
323
|
0
|
|
|
|
|
|
$domain = _validate_domain ( $type, $fmly, $domain, $dbg_flg, $die ); |
324
|
0
|
0
|
|
|
|
|
return ( undef ) unless (defined $domain); |
325
|
|
|
|
|
|
|
} |
326
|
|
|
|
|
|
|
|
327
|
|
|
|
|
|
|
# We start with a clear connection, because I don't know if the |
328
|
|
|
|
|
|
|
# connection will be implicit or explicit or remain clear after all. |
329
|
0
|
|
|
|
|
|
my $socket; |
330
|
|
|
|
|
|
|
|
331
|
0
|
0
|
|
|
|
|
if (exists $arg->{ProxyArgs}) { |
332
|
|
|
|
|
|
|
# Establishing a Proxy Connection ... |
333
|
0
|
|
|
|
|
|
my %proxyArgs = %{$arg->{ProxyArgs}}; |
|
0
|
|
|
|
|
|
|
334
|
|
|
|
|
|
|
|
335
|
0
|
|
|
|
|
|
$proxyArgs{'remote-host'} = $host; |
336
|
0
|
|
|
|
|
|
$proxyArgs{'remote-port'} = $port; |
337
|
|
|
|
|
|
|
|
338
|
0
|
|
|
|
|
|
eval { |
339
|
0
|
|
|
|
|
|
require Net::HTTPTunnel; # So not everyone has to install this module ... |
340
|
|
|
|
|
|
|
|
341
|
0
|
|
|
|
|
|
$socket = Net::HTTPTunnel->new ( %proxyArgs ); |
342
|
|
|
|
|
|
|
}; |
343
|
0
|
0
|
|
|
|
|
if ($@) { |
344
|
0
|
|
|
|
|
|
return _croak_or_return (undef, $die, $dbg_flg, "Missing Perl Module Error:\n" . $@); |
345
|
|
|
|
|
|
|
} |
346
|
0
|
0
|
|
|
|
|
unless ( defined $socket ) { |
347
|
0
|
|
0
|
|
|
|
my $pmsg = ($proxyArgs{'proxy-host'} || "undef") . ":" . ($proxyArgs{'proxy-port'} || "undef"); |
|
|
|
0
|
|
|
|
|
348
|
0
|
|
|
|
|
|
return _croak_or_return (undef, $die, $dbg_flg, |
349
|
|
|
|
|
|
|
"Can't open HTTPTunnel proxy connection! ($pmsg) to ($host:$port)"); |
350
|
|
|
|
|
|
|
} |
351
|
0
|
|
|
|
|
|
$ftpssl_args{myProxyArgs} = \%proxyArgs; |
352
|
|
|
|
|
|
|
|
353
|
|
|
|
|
|
|
} else { |
354
|
|
|
|
|
|
|
# Establishing a Direct Connection ... |
355
|
0
|
|
|
|
|
|
my %socketArgs = ( PeerAddr => $host, |
356
|
|
|
|
|
|
|
PeerPort => $port, |
357
|
|
|
|
|
|
|
Proto => 'tcp', |
358
|
|
|
|
|
|
|
Timeout => $timeout |
359
|
|
|
|
|
|
|
); |
360
|
0
|
0
|
|
|
|
|
$socketArgs{LocalAddr} = $localaddr if (defined $localaddr); |
361
|
0
|
0
|
0
|
|
|
|
$socketArgs{$family_key} = $domain if ($ipv6 && defined $domain); |
362
|
|
|
|
|
|
|
|
363
|
0
|
0
|
|
|
|
|
$socket = $IOCLASS->new ( %socketArgs ) |
364
|
|
|
|
|
|
|
# $socket = IO::Socket::INET->new ( %socketArgs ) |
365
|
|
|
|
|
|
|
or |
366
|
|
|
|
|
|
|
return _croak_or_return (undef, $die, $dbg_flg, |
367
|
|
|
|
|
|
|
"Can't open tcp connection! ($host:$port)"); |
368
|
0
|
|
|
|
|
|
$ftpssl_args{mySocketOpts} = \%socketArgs; |
369
|
|
|
|
|
|
|
} |
370
|
|
|
|
|
|
|
|
371
|
0
|
|
|
|
|
|
_my_autoflush ( $socket ); |
372
|
|
|
|
|
|
|
|
373
|
|
|
|
|
|
|
# Save so we can log socket activity if needed ... |
374
|
0
|
|
|
|
|
|
$ftpssl_args{debug} = $debug; |
375
|
0
|
|
|
|
|
|
$ftpssl_args{debug_extra} = $debug_extra; |
376
|
0
|
|
|
|
|
|
$ftpssl_args{Croak} = $die; |
377
|
0
|
|
|
|
|
|
$ftpssl_args{Timeout} = $timeout; |
378
|
|
|
|
|
|
|
|
379
|
|
|
|
|
|
|
# Bug Id: 120341 says this will be removed from socket by start_SSL() call. |
380
|
0
|
|
|
|
|
|
${*$socket}{_FTPSSL_arguments} = \%ftpssl_args; |
|
0
|
|
|
|
|
|
|
381
|
|
|
|
|
|
|
|
382
|
0
|
|
|
|
|
|
my $obj; |
383
|
|
|
|
|
|
|
|
384
|
0
|
0
|
|
|
|
|
if ( $encrypt_mode eq CLR_CRYPT ) { |
385
|
|
|
|
|
|
|
# Catch the banner from the connection request ... |
386
|
0
|
0
|
|
|
|
|
return _croak_or_return ($socket) unless ( response($socket) == CMD_OK ); |
387
|
|
|
|
|
|
|
|
388
|
|
|
|
|
|
|
# Leave the command channel clear for regular FTP. |
389
|
0
|
|
|
|
|
|
$obj = $socket; |
390
|
0
|
|
|
|
|
|
bless ( $obj, $type ); |
391
|
0
|
|
|
|
|
|
${*$obj}{_SSL_opened} = 0; # To get rid of SSL warning on quit ... |
|
0
|
|
|
|
|
|
|
392
|
|
|
|
|
|
|
|
393
|
|
|
|
|
|
|
} else { |
394
|
|
|
|
|
|
|
# Determine the options to use in start_SSL() ... |
395
|
|
|
|
|
|
|
# ------------------------------------------------------------------------ |
396
|
|
|
|
|
|
|
# Reset SSL_version & Timeout in %ssl_args so these options can be |
397
|
|
|
|
|
|
|
# applied to the SSL_Client_Certificate functionality. |
398
|
|
|
|
|
|
|
# ------------------------------------------------------------------------ |
399
|
0
|
|
|
|
|
|
my $mode; |
400
|
0
|
0
|
|
|
|
|
if (defined $ssl_args{SSL_version}) { |
|
|
0
|
|
|
|
|
|
401
|
0
|
|
|
|
|
|
$mode = $ssl_args{SSL_version}; # Mode was overridden. |
402
|
|
|
|
|
|
|
# Reset use_ssl flag in case it conflicts ... |
403
|
0
|
|
|
|
|
|
$use_ssl = ( $mode =~ m/^SSLv/i ); # Bug ID 115296 |
404
|
|
|
|
|
|
|
} elsif ( $use_ssl ) { |
405
|
0
|
|
|
|
|
|
$mode = $ssl_args{SSL_version} = "SSLv23"; # SSL per override |
406
|
0
|
|
|
|
|
|
warn ("Option useSSL has been depreciated. Use option SSL_version instead.\n"); |
407
|
|
|
|
|
|
|
} else { |
408
|
0
|
|
|
|
|
|
$mode = $ssl_args{SSL_version} = "TLSv12"; # TLS v1.2 per defaults |
409
|
|
|
|
|
|
|
} |
410
|
0
|
0
|
|
|
|
|
$ssl_args{Timeout} = $timeout unless (exists $ssl_args{Timeout}); |
411
|
|
|
|
|
|
|
|
412
|
|
|
|
|
|
|
# ------------------------------------------------------------------------ |
413
|
|
|
|
|
|
|
# The options for Reusing the Session ... |
414
|
|
|
|
|
|
|
# ------------------------------------------------------------------------ |
415
|
0
|
0
|
|
|
|
|
if ( $reuseSession ) { |
416
|
0
|
|
|
|
|
|
$ssl_args{SSL_session_cache} = IO::Socket::SSL::Session_Cache->new (4 + $reuseSession); |
417
|
0
|
|
|
|
|
|
$ssl_args{SSL_session_key} = "Net-FTPSSL-${VERSION}-$$:${port}"; |
418
|
|
|
|
|
|
|
} |
419
|
|
|
|
|
|
|
|
420
|
|
|
|
|
|
|
# _debug_print_hash (undef, "Socket call", "initialization", "?", $socket); |
421
|
|
|
|
|
|
|
# _debug_print_hash (undef, "Before start_SSL() call", "initialization", "?", \%ssl_args); |
422
|
|
|
|
|
|
|
# ------------------------------------------------------------------------ |
423
|
|
|
|
|
|
|
|
424
|
|
|
|
|
|
|
# Can we use SNI? |
425
|
0
|
0
|
0
|
|
|
|
if ( $type->can ("can_client_sni") && $type->can_client_sni () ) { |
426
|
0
|
0
|
|
|
|
|
$ssl_args{SSL_hostname} = $host if (! exists $ssl_args{SSL_hostname}); |
427
|
|
|
|
|
|
|
} |
428
|
|
|
|
|
|
|
|
429
|
0
|
0
|
|
|
|
|
if ( $encrypt_mode eq EXP_CRYPT ) { |
430
|
|
|
|
|
|
|
# Catch the banner from the connection request ... |
431
|
0
|
0
|
|
|
|
|
return _croak_or_return ($socket) unless (response ($socket) == CMD_OK); |
432
|
|
|
|
|
|
|
|
433
|
|
|
|
|
|
|
# In explicit mode FTPSSL sends an AUTH TLS/SSL command, catch the msgs |
434
|
0
|
0
|
|
|
|
|
command( $socket, "AUTH", ($use_ssl ? "SSL" : "TLS") ); |
435
|
0
|
0
|
|
|
|
|
return _croak_or_return ($socket) unless (response ($socket) == CMD_OK); |
436
|
|
|
|
|
|
|
} |
437
|
|
|
|
|
|
|
|
438
|
|
|
|
|
|
|
# ------------------------------------------------------------------------ |
439
|
|
|
|
|
|
|
# Now transform the clear connection into a SSL one on our end. |
440
|
|
|
|
|
|
|
# Messy since newer IO::Socket::SSL modules remove {_FTPSSL_arguments}! |
441
|
|
|
|
|
|
|
# Bug Id: 120341. |
442
|
|
|
|
|
|
|
# ------------------------------------------------------------------------ |
443
|
0
|
|
|
|
|
|
$obj = $type->start_SSL( $socket, %ssl_args ); |
444
|
0
|
0
|
|
|
|
|
unless ( $obj ) { |
445
|
0
|
0
|
|
|
|
|
unless ( exists ${*$socket}{_FTPSSL_arguments} ) { |
|
0
|
|
|
|
|
|
|
446
|
0
|
|
|
|
|
|
${*$socket}{_FTPSSL_arguments} = \%ftpssl_args; |
|
0
|
|
|
|
|
|
|
447
|
0
|
0
|
|
|
|
|
_print_LOG (undef, "Restoring _FTPSSL_arguments to \$socket.\n") if ( $debug ); |
448
|
|
|
|
|
|
|
} |
449
|
0
|
|
|
|
|
|
return _croak_or_return ( $socket, undef, |
450
|
|
|
|
|
|
|
"$mode: " . IO::Socket::SSL::errstr () ); |
451
|
|
|
|
|
|
|
} |
452
|
|
|
|
|
|
|
|
453
|
0
|
0
|
|
|
|
|
unless ( exists ${*$obj}{_FTPSSL_arguments} ) { |
|
0
|
|
|
|
|
|
|
454
|
0
|
|
|
|
|
|
${*$obj}{_FTPSSL_arguments} = \%ftpssl_args; |
|
0
|
|
|
|
|
|
|
455
|
0
|
0
|
|
|
|
|
$obj->_print_LOG ("Restoring _FTPSSL_arguments to \$obj.\n") if ( $debug ); |
456
|
|
|
|
|
|
|
} |
457
|
|
|
|
|
|
|
# ------------------------------------------------------------------------ |
458
|
|
|
|
|
|
|
|
459
|
0
|
0
|
|
|
|
|
if ( $encrypt_mode eq IMP_CRYPT ) { |
460
|
|
|
|
|
|
|
# Catch the banner from the implicit connection request ... |
461
|
0
|
0
|
|
|
|
|
return $obj->_croak_or_return () unless ( $obj->response() == CMD_OK ); |
462
|
|
|
|
|
|
|
} |
463
|
|
|
|
|
|
|
|
464
|
0
|
|
|
|
|
|
$ftpssl_args{start_SSL_opts} = \%ssl_args; |
465
|
|
|
|
|
|
|
} |
466
|
|
|
|
|
|
|
|
467
|
|
|
|
|
|
|
|
468
|
|
|
|
|
|
|
# -------------------------------------- |
469
|
|
|
|
|
|
|
# Check if overriding "_help()" ... |
470
|
|
|
|
|
|
|
# -------------------------------------- |
471
|
0
|
0
|
|
|
|
|
if ( defined $fixHelp ) { |
472
|
0
|
|
|
|
|
|
my %helpHash; |
473
|
|
|
|
|
|
|
|
474
|
0
|
|
|
|
|
|
$ftpssl_args{OverrideHELP} = 0; # So we know OverrideHELP was used ... |
475
|
0
|
0
|
|
|
|
|
if ( ref ($fixHelp) eq "ARRAY" ) { |
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
476
|
0
|
|
|
|
|
|
foreach (@{$fixHelp}) { |
|
0
|
|
|
|
|
|
|
477
|
0
|
|
|
|
|
|
my $k = uc ($_); |
478
|
0
|
0
|
|
|
|
|
$helpHash{$k} = 1 if ( $k ne "HELP" ); |
479
|
|
|
|
|
|
|
} |
480
|
|
|
|
|
|
|
} elsif ( $fixHelp == -1 ) { |
481
|
0
|
|
|
|
|
|
$ftpssl_args{removeHELP} = 1; # Uses FEAT to list commands supported! |
482
|
0
|
|
|
|
|
|
delete $ftpssl_args{OverrideHELP}; |
483
|
|
|
|
|
|
|
} elsif ( $fixHelp ) { |
484
|
0
|
|
|
|
|
|
$ftpssl_args{OverrideHELP} = 1; # All FTP commands supported ... |
485
|
|
|
|
|
|
|
} |
486
|
|
|
|
|
|
|
|
487
|
|
|
|
|
|
|
# Set the "cache" tags used by "_help()" so that it can still be called! |
488
|
0
|
|
|
|
|
|
$ftpssl_args{help_cmds_found} = \%helpHash; |
489
|
0
|
|
|
|
|
|
$ftpssl_args{help_cmds_msg} = "214 HELP Command Overridden by request."; |
490
|
|
|
|
|
|
|
|
491
|
|
|
|
|
|
|
# Causes direct calls to _help($cmd) to skip the server hit. |
492
|
0
|
|
|
|
|
|
$ftpssl_args{help_cmds_no_syntax_available} = 1; |
493
|
|
|
|
|
|
|
} |
494
|
|
|
|
|
|
|
# -------------------------------------- |
495
|
|
|
|
|
|
|
# End overriding "_help()" ... |
496
|
|
|
|
|
|
|
# -------------------------------------- |
497
|
|
|
|
|
|
|
|
498
|
|
|
|
|
|
|
# These options control the behaviour of the Net::FTPSSL class ... |
499
|
0
|
|
|
|
|
|
$ftpssl_args{Host} = $host; |
500
|
0
|
|
|
|
|
|
$ftpssl_args{Crypt} = $encrypt_mode; |
501
|
0
|
|
|
|
|
|
$ftpssl_args{debug} = $debug; |
502
|
0
|
|
|
|
|
|
$ftpssl_args{debug_extra} = $debug_extra; |
503
|
0
|
|
|
|
|
|
$ftpssl_args{debug_no_help} = $no_login_help; |
504
|
0
|
|
|
|
|
|
$ftpssl_args{trace} = $trace; |
505
|
0
|
|
|
|
|
|
$ftpssl_args{buf_size} = $buf_size; |
506
|
0
|
|
|
|
|
|
$ftpssl_args{type} = MODE_ASCII; |
507
|
0
|
|
|
|
|
|
$ftpssl_args{data_prot} = $data_prot; |
508
|
0
|
|
|
|
|
|
$ftpssl_args{Croak} = $die; |
509
|
0
|
|
|
|
|
|
$ftpssl_args{FixPutTs} = $ftpssl_args{FixGetTs} = $pres_ts; |
510
|
0
|
0
|
|
|
|
|
$ftpssl_args{OverridePASV} = $pasvHost if (defined $pasvHost); |
511
|
0
|
|
|
|
|
|
$ftpssl_args{dcsc_mode} = FTPS_PASV; |
512
|
0
|
|
|
|
|
|
$ftpssl_args{Pret} = $pret; |
513
|
0
|
|
|
|
|
|
$ftpssl_args{Timeout} = $timeout; |
514
|
0
|
0
|
|
|
|
|
$ftpssl_args{xWait} = $xWait if ( $xWait ); |
515
|
|
|
|
|
|
|
|
516
|
0
|
0
|
|
|
|
|
$ftpssl_args{ftpssl_filehandle} = $FTPS_ERROR if ( $debug >= 2 ); |
517
|
0
|
|
|
|
|
|
$FTPS_ERROR = undef; |
518
|
|
|
|
|
|
|
|
519
|
|
|
|
|
|
|
# Must be last for certificates to work correctly ... |
520
|
0
|
0
|
0
|
|
|
|
if ( $reuseSession || $enableCtx || |
|
|
|
0
|
|
|
|
|
521
|
|
|
|
|
|
|
ref ($arg->{SSL_Client_Certificate}) eq "HASH" ) { |
522
|
|
|
|
|
|
|
# Reuse the command channel context ... |
523
|
0
|
|
|
|
|
|
my %ssl_reuse = ( SSL_reuse_ctx => ${*$obj}{_SSL_ctx} ); |
|
0
|
|
|
|
|
|
|
524
|
|
|
|
|
|
|
|
525
|
|
|
|
|
|
|
# Added to fix CPAN Bug Id: 101388 ... |
526
|
0
|
|
|
|
|
|
my $key = "SSL_ca_file"; |
527
|
0
|
0
|
|
|
|
|
if ( exists ${*$obj}{_SSL_arguments}->{$key} ) { |
|
0
|
|
|
|
|
|
|
528
|
0
|
|
|
|
|
|
$ssl_reuse{$key} = ${*$obj}{_SSL_arguments}->{$key}; |
|
0
|
|
|
|
|
|
|
529
|
|
|
|
|
|
|
} |
530
|
0
|
|
|
|
|
|
$key = "SSL_verifycn_name"; |
531
|
0
|
0
|
|
|
|
|
if ( exists ${*$obj}{_SSL_arguments}->{$key} ) { |
|
0
|
|
|
|
|
|
|
532
|
0
|
|
|
|
|
|
$ssl_reuse{$key} = ${*$obj}{_SSL_arguments}->{$key}; |
|
0
|
|
|
|
|
|
|
533
|
|
|
|
|
|
|
} |
534
|
0
|
|
|
|
|
|
$key = "SSL_verifycn_scheme"; |
535
|
0
|
0
|
|
|
|
|
if ( exists ${*$obj}{_SSL_arguments}->{$key} ) { |
|
0
|
0
|
|
|
|
|
|
536
|
0
|
|
|
|
|
|
$ssl_reuse{$key} = ${*$obj}{_SSL_arguments}->{$key}; |
|
0
|
|
|
|
|
|
|
537
|
|
|
|
|
|
|
} elsif ( exists $ssl_args{$key} ) { |
538
|
0
|
|
|
|
|
|
$ssl_reuse{$key} = $ssl_args{$key}; |
539
|
|
|
|
|
|
|
} |
540
|
|
|
|
|
|
|
|
541
|
|
|
|
|
|
|
# Fix for Bug Ids # 104407 & 76108. (Session Reuse!) |
542
|
0
|
|
|
|
|
|
$key = "SSL_session_key"; |
543
|
0
|
0
|
0
|
|
|
|
if ( exists ${*$obj}{_SSL_arguments}->{$key} && ! exists $ssl_reuse{$key} ) { |
|
0
|
|
|
|
|
|
|
544
|
0
|
|
|
|
|
|
$ssl_reuse{$key} = ${*$obj}{_SSL_arguments}->{$key}; |
|
0
|
|
|
|
|
|
|
545
|
|
|
|
|
|
|
# $obj->_print_LOG ("\n *** Adding: $key --> $ssl_reuse{$key} ***\n"); |
546
|
|
|
|
|
|
|
} |
547
|
|
|
|
|
|
|
|
548
|
0
|
|
|
|
|
|
$ftpssl_args{myContext} = \%ssl_reuse; |
549
|
|
|
|
|
|
|
} |
550
|
|
|
|
|
|
|
|
551
|
|
|
|
|
|
|
# ------------------------------------------------------------------------- |
552
|
|
|
|
|
|
|
# Print out the details of the SSL object. It's TRUE only for debugging! |
553
|
|
|
|
|
|
|
# ------------------------------------------------------------------------- |
554
|
0
|
0
|
|
|
|
|
if ( $debug ) { |
555
|
0
|
0
|
|
|
|
|
if ( ref ($arg->{SSL_Client_Certificate}) eq "HASH" ) { |
556
|
|
|
|
|
|
|
$obj->_debug_print_hash ( "SSL_Client_Certificate", "options", |
557
|
0
|
|
|
|
|
|
$encrypt_mode, $arg->{SSL_Client_Certificate} ); |
558
|
|
|
|
|
|
|
} |
559
|
0
|
|
|
|
|
|
$obj->_debug_print_hash ( "SSL", "arguments", $encrypt_mode, \%ssl_args ); |
560
|
0
|
|
|
|
|
|
$obj->_debug_print_hash ( $host, $port, $encrypt_mode, undef, "*" ); |
561
|
|
|
|
|
|
|
} |
562
|
|
|
|
|
|
|
|
563
|
0
|
|
|
|
|
|
return $obj; |
564
|
|
|
|
|
|
|
} |
565
|
|
|
|
|
|
|
|
566
|
|
|
|
|
|
|
#----------------------------------------------------------------------- |
567
|
|
|
|
|
|
|
# TODO: Adding ACCT (Account) support (response 332 [CMD_MORE] on password) |
568
|
|
|
|
|
|
|
|
569
|
|
|
|
|
|
|
sub login { |
570
|
0
|
|
|
0
|
1
|
|
my ( $self, $user, $pass ) = @_; |
571
|
|
|
|
|
|
|
|
572
|
0
|
|
|
|
|
|
my $arg = ${*$self}{_FTPSSL_arguments}; |
|
0
|
|
|
|
|
|
|
573
|
|
|
|
|
|
|
|
574
|
0
|
0
|
0
|
|
|
|
if ( defined $user && $user ne "" ) { |
575
|
0
|
|
|
|
|
|
$arg->{_hide_value_in_response_} = $user; |
576
|
0
|
|
|
|
|
|
$arg->{_mask_value_in_response_} = "++++++"; |
577
|
|
|
|
|
|
|
} |
578
|
|
|
|
|
|
|
|
579
|
0
|
|
0
|
|
|
|
my $logged_on = $self->_test_croak ( $self->_user ($user) && |
580
|
|
|
|
|
|
|
$self->_passwd ($pass) ); |
581
|
|
|
|
|
|
|
|
582
|
0
|
|
|
|
|
|
delete ( $arg->{_hide_value_in_response_} ); |
583
|
0
|
|
|
|
|
|
delete ( $arg->{_mask_value_in_response_} ); |
584
|
|
|
|
|
|
|
|
585
|
0
|
0
|
|
|
|
|
if ( $logged_on ) { |
586
|
|
|
|
|
|
|
# Check if we want to supress the help logging ... |
587
|
0
|
|
|
|
|
|
my $save = $arg->{debug}; |
588
|
0
|
0
|
|
|
|
|
if ( $arg->{debug_no_help} ) { |
589
|
0
|
|
|
|
|
|
delete $arg->{debug}; |
590
|
|
|
|
|
|
|
} |
591
|
|
|
|
|
|
|
|
592
|
|
|
|
|
|
|
# So _help is always called early instead of later. |
593
|
0
|
|
|
|
|
|
$self->supported ("HELP"); |
594
|
|
|
|
|
|
|
|
595
|
0
|
|
|
|
|
|
$arg->{debug} = $save; # Re-enabled again! |
596
|
|
|
|
|
|
|
|
597
|
0
|
0
|
0
|
|
|
|
if ( $arg->{debug} && $arg->{debug_extra} ) { |
598
|
0
|
|
|
|
|
|
my $hlp = join ("), (", sort keys %{$self->_help ()}); |
|
0
|
|
|
|
|
|
|
599
|
0
|
0
|
|
|
|
|
if ( $hlp eq "" ) { |
600
|
0
|
0
|
|
|
|
|
my $msg = ( $arg->{OverrideHELP} ) ? "All" : "No"; |
601
|
0
|
|
|
|
|
|
$self->_print_LOG ("HELP: () --> $msg FTP Commands.\n"); |
602
|
|
|
|
|
|
|
} else { |
603
|
0
|
|
|
|
|
|
$self->_print_LOG ("HELP: ($hlp)\n"); |
604
|
|
|
|
|
|
|
} |
605
|
|
|
|
|
|
|
} |
606
|
|
|
|
|
|
|
|
607
|
|
|
|
|
|
|
# Check if these commands are not supported by this server after all! |
608
|
0
|
0
|
0
|
|
|
|
if ( $arg->{FixPutTs} && ! $self->supported ("MFMT") ) { |
609
|
0
|
|
|
|
|
|
$arg->{FixPutTs} = 0; |
610
|
|
|
|
|
|
|
} |
611
|
0
|
0
|
0
|
|
|
|
if ( $arg->{FixGetTs} && ! $self->supported ("MDTM") ) { |
612
|
0
|
|
|
|
|
|
$arg->{FixGetTs} = 0; |
613
|
|
|
|
|
|
|
} |
614
|
|
|
|
|
|
|
} |
615
|
|
|
|
|
|
|
|
616
|
0
|
|
|
|
|
|
return ( $logged_on ); |
617
|
|
|
|
|
|
|
} |
618
|
|
|
|
|
|
|
|
619
|
|
|
|
|
|
|
#----------------------------------------------------------------------- |
620
|
|
|
|
|
|
|
|
621
|
|
|
|
|
|
|
sub quit { |
622
|
0
|
|
|
0
|
1
|
|
my $self = shift; |
623
|
0
|
0
|
|
|
|
|
$self->_quit() or return 0; # Don't do a croak here, since who tests? |
624
|
0
|
|
|
|
|
|
_my_close ($self); # Old way $self->close(); |
625
|
0
|
|
|
|
|
|
return 1; |
626
|
|
|
|
|
|
|
} |
627
|
|
|
|
|
|
|
|
628
|
|
|
|
|
|
|
#----------------------------------------------------------------------- |
629
|
|
|
|
|
|
|
|
630
|
|
|
|
|
|
|
sub force_epsv { |
631
|
0
|
|
|
0
|
1
|
|
my $self = shift; |
632
|
0
|
|
0
|
|
|
|
my $epsv_mode = shift || "1"; |
633
|
|
|
|
|
|
|
|
634
|
0
|
0
|
0
|
|
|
|
unless ($epsv_mode eq "1" || $epsv_mode eq "2") { |
635
|
0
|
|
|
|
|
|
return $self->croak_or_return (0, "Invalid IP Protocol Flag ($epsv_mode)"); |
636
|
|
|
|
|
|
|
} |
637
|
|
|
|
|
|
|
|
638
|
|
|
|
|
|
|
# Don't resend the command to the FTPS server if it was sent before! |
639
|
0
|
0
|
0
|
|
|
|
if ( ${*$self}{_FTPSSL_arguments}->{dcsc_mode} != FTPS_EPSV_1 && |
|
0
|
|
|
|
|
|
|
640
|
0
|
|
|
|
|
|
${*$self}{_FTPSSL_arguments}->{dcsc_mode} != FTPS_EPSV_2 ) { |
641
|
0
|
0
|
|
|
|
|
unless ($self->command ("EPSV", "ALL")->response () == CMD_OK) { |
642
|
0
|
|
|
|
|
|
return $self->_croak_or_return (); |
643
|
|
|
|
|
|
|
} |
644
|
|
|
|
|
|
|
} |
645
|
|
|
|
|
|
|
|
646
|
|
|
|
|
|
|
# Now that only EPSV is supported, remember which one was requested ... |
647
|
|
|
|
|
|
|
# You can no longer swap back to PASV, PORT or EPRT. |
648
|
0
|
0
|
|
|
|
|
${*$self}{_FTPSSL_arguments}->{dcsc_mode} = ($epsv_mode eq "1") ? FTPS_EPSV_1 : FTPS_EPSV_2; |
|
0
|
|
|
|
|
|
|
649
|
|
|
|
|
|
|
|
650
|
|
|
|
|
|
|
# Now check out if the requested EPSV mode was actually supported ... |
651
|
0
|
0
|
|
|
|
|
unless ($self->command ("EPSV", $epsv_mode)->response () == CMD_OK) { |
652
|
0
|
|
|
|
|
|
return $self->_croak_or_return (); |
653
|
|
|
|
|
|
|
} |
654
|
|
|
|
|
|
|
|
655
|
|
|
|
|
|
|
# So the server will release the returned port! |
656
|
0
|
|
|
|
|
|
$self->_abort(); |
657
|
|
|
|
|
|
|
|
658
|
0
|
|
|
|
|
|
return (1); # Success! |
659
|
|
|
|
|
|
|
} |
660
|
|
|
|
|
|
|
|
661
|
|
|
|
|
|
|
sub _pasv { |
662
|
0
|
|
|
0
|
|
|
my $self = shift; |
663
|
|
|
|
|
|
|
# Leaving the other arguments on the stack (for use by PRET if called) |
664
|
|
|
|
|
|
|
|
665
|
0
|
|
|
|
|
|
my ($host, $port) = ("", ""); |
666
|
|
|
|
|
|
|
|
667
|
0
|
0
|
|
|
|
|
if ( ${*$self}{_FTPSSL_arguments}->{Pret} ) { |
|
0
|
|
|
|
|
|
|
668
|
0
|
0
|
|
|
|
|
unless ( $self->command ("PRET", @_)->response () == CMD_OK ) { |
669
|
0
|
|
|
|
|
|
$self->_croak_or_return (); |
670
|
0
|
|
|
|
|
|
return ($host, $port); |
671
|
|
|
|
|
|
|
} |
672
|
|
|
|
|
|
|
} |
673
|
|
|
|
|
|
|
|
674
|
0
|
0
|
|
|
|
|
unless ( $self->command ("PASV")->response () == CMD_OK ) { |
675
|
0
|
0
|
|
|
|
|
if ( ${*$self}{_FTPSSL_arguments}->{Pret} ) { |
|
0
|
0
|
|
|
|
|
|
676
|
|
|
|
|
|
|
# Prevents infinite recursion on failure if PRET is already set ... |
677
|
0
|
|
|
|
|
|
$self->_croak_or_return (); |
678
|
|
|
|
|
|
|
|
679
|
|
|
|
|
|
|
} elsif ( $self->last_message () =~ m/(^|\s)PRET($|[\s.!?])/i ) { |
680
|
|
|
|
|
|
|
# Turns PRET on for all future calls to _pasv()! |
681
|
|
|
|
|
|
|
# Stays on even if it still doesn't work with PRET! |
682
|
0
|
|
|
|
|
|
${*$self}{_FTPSSL_arguments}->{Pret} = 1; |
|
0
|
|
|
|
|
|
|
683
|
0
|
|
|
|
|
|
$self->_print_DBG ("<<+ Auto-adding PRET option!\n"); |
684
|
0
|
|
|
|
|
|
($host, $port) = $self->_pasv (@_); |
685
|
|
|
|
|
|
|
|
686
|
|
|
|
|
|
|
} else { |
687
|
0
|
|
|
|
|
|
$self->_croak_or_return (); |
688
|
|
|
|
|
|
|
} |
689
|
|
|
|
|
|
|
|
690
|
0
|
|
|
|
|
|
return ($host, $port); |
691
|
|
|
|
|
|
|
} |
692
|
|
|
|
|
|
|
|
693
|
|
|
|
|
|
|
# [227] [Entering Passive Mode] ([h1,h2,h3,h4,p1,p2]). |
694
|
0
|
|
|
|
|
|
my $msg = $self->last_message (); |
695
|
0
|
0
|
|
|
|
|
unless ($msg =~ m/(\d+)\s(.*)\(((\d+,?)+)\)\.?/) { |
696
|
0
|
|
|
|
|
|
$self->_croak_or_return (0, "Can't parse the PASV response."); |
697
|
0
|
|
|
|
|
|
return ($host, $port); |
698
|
|
|
|
|
|
|
} |
699
|
|
|
|
|
|
|
|
700
|
0
|
|
|
|
|
|
my @address = split( /,/, $3 ); |
701
|
|
|
|
|
|
|
|
702
|
0
|
|
|
|
|
|
$host = join( '.', @address[ 0 .. 3 ] ); |
703
|
0
|
|
|
|
|
|
$port = $address[4] * 256 + $address[5]; |
704
|
|
|
|
|
|
|
|
705
|
0
|
0
|
|
|
|
|
if ( ${*$self}{_FTPSSL_arguments}->{OverridePASV} ) { |
|
0
|
|
|
|
|
|
|
706
|
0
|
|
|
|
|
|
my $ip = $host; |
707
|
0
|
|
|
|
|
|
$host = ${*$self}{_FTPSSL_arguments}->{OverridePASV}; |
|
0
|
|
|
|
|
|
|
708
|
0
|
|
|
|
|
|
$self->_print_DBG ( "--- Overriding PASV IP Address $ip with $host\n" ); |
709
|
|
|
|
|
|
|
} |
710
|
|
|
|
|
|
|
|
711
|
0
|
|
|
|
|
|
return ($host, $port); |
712
|
|
|
|
|
|
|
} |
713
|
|
|
|
|
|
|
|
714
|
|
|
|
|
|
|
sub _epsv { |
715
|
0
|
|
|
0
|
|
|
my $self = shift; |
716
|
0
|
|
|
|
|
|
my $ipver = shift; |
717
|
|
|
|
|
|
|
|
718
|
0
|
0
|
|
|
|
|
$self->command ("EPSV", ($ipver == FTPS_EPSV_1) ? "1" : "2"); |
719
|
0
|
0
|
|
|
|
|
unless ( $self->response () == CMD_OK ) { |
720
|
0
|
|
|
|
|
|
$self->_croak_or_return (); |
721
|
0
|
|
|
|
|
|
return ("", ""); |
722
|
|
|
|
|
|
|
} |
723
|
|
|
|
|
|
|
|
724
|
|
|
|
|
|
|
# [227] [Entering Extended Passive Mode] (||||). |
725
|
0
|
|
|
|
|
|
my $msg = $self->last_message (); |
726
|
0
|
0
|
|
|
|
|
unless ($msg =~ m/[(](.)(.)(.)(\d+)(.)[)]/) { |
727
|
0
|
|
|
|
|
|
$self->_croak_or_return (0, "Can't parse the EPSV response."); |
728
|
0
|
|
|
|
|
|
return ("", ""); |
729
|
|
|
|
|
|
|
} |
730
|
|
|
|
|
|
|
|
731
|
0
|
|
|
|
|
|
my ($s1, $s2, $s3, $port, $s4) = ($1, $2, $3, $4, $5); |
732
|
|
|
|
|
|
|
|
733
|
|
|
|
|
|
|
# By definition, EPSV must use the same host for the DC as the CC. |
734
|
0
|
|
|
|
|
|
return (${*$self}{_FTPSSL_arguments}->{Host}, $port); |
|
0
|
|
|
|
|
|
|
735
|
|
|
|
|
|
|
} |
736
|
|
|
|
|
|
|
|
737
|
|
|
|
|
|
|
sub prep_data_channel { |
738
|
0
|
|
|
0
|
0
|
|
my $self = shift; |
739
|
|
|
|
|
|
|
# Leaving other arguments on the stack (for use by PRET if called via PASV) |
740
|
|
|
|
|
|
|
|
741
|
|
|
|
|
|
|
# Should only do this for encrypted Command Channels. |
742
|
0
|
0
|
|
|
|
|
if ( ${*$self}{_FTPSSL_arguments}->{Crypt} ne CLR_CRYPT ) { |
|
0
|
|
|
|
|
|
|
743
|
0
|
|
|
|
|
|
$self->_pbsz(); |
744
|
0
|
0
|
|
|
|
|
unless ($self->_prot()) { return $self->_croak_or_return (); } |
|
0
|
|
|
|
|
|
|
745
|
|
|
|
|
|
|
} |
746
|
|
|
|
|
|
|
|
747
|
|
|
|
|
|
|
# Determine what host/port pairs to use for the data channel ... |
748
|
0
|
|
|
|
|
|
my $mode = ${*$self}{_FTPSSL_arguments}->{dcsc_mode}; |
|
0
|
|
|
|
|
|
|
749
|
0
|
|
|
|
|
|
my ($host, $port); |
750
|
0
|
0
|
0
|
|
|
|
if ( $mode == FTPS_PASV ) { |
|
|
0
|
|
|
|
|
|
751
|
0
|
|
|
|
|
|
($host, $port) = $self->_pasv (@_); |
752
|
|
|
|
|
|
|
} elsif ( $mode == FTPS_EPSV_1 || $mode == FTPS_EPSV_2 ) { |
753
|
0
|
|
|
|
|
|
($host, $port) = $self->_epsv ($mode); |
754
|
|
|
|
|
|
|
} else { |
755
|
0
|
0
|
0
|
|
|
|
my $err = ($mode == FTPS_PORT || |
756
|
|
|
|
|
|
|
$mode == FTPS_EPRT_1 || $mode == FTPS_EPRT_2) |
757
|
|
|
|
|
|
|
? "Active FTP mode ($mode)" |
758
|
|
|
|
|
|
|
: "Unknown FTP mode ($mode)"; |
759
|
0
|
|
|
|
|
|
return $self->_croak_or_return (0, "Currently doesn't support $err when requesting the data channel port to use!"); |
760
|
|
|
|
|
|
|
} |
761
|
|
|
|
|
|
|
|
762
|
0
|
|
|
|
|
|
$self->_print_DBG ("--- Host ($host) Port ($port)\n"); |
763
|
|
|
|
|
|
|
|
764
|
|
|
|
|
|
|
# Already decided not to call croak earlier if this happens. |
765
|
0
|
0
|
0
|
|
|
|
return (0) if ($host eq "" || $port eq ""); |
766
|
|
|
|
|
|
|
|
767
|
|
|
|
|
|
|
# Returns if the data channel was established or not ... |
768
|
0
|
|
|
|
|
|
return ( $self->_open_data_channel ($host, $port) ); |
769
|
|
|
|
|
|
|
} |
770
|
|
|
|
|
|
|
|
771
|
|
|
|
|
|
|
sub _open_data_channel { |
772
|
0
|
|
|
0
|
|
|
my $self = shift; |
773
|
0
|
|
|
|
|
|
my $host = shift; |
774
|
0
|
|
|
|
|
|
my $port = shift; |
775
|
|
|
|
|
|
|
|
776
|
|
|
|
|
|
|
# Warning: also called by t/10-complex.t func check_for_pasv_issue(), |
777
|
|
|
|
|
|
|
# so verify still works there if any significant changes are made here. |
778
|
|
|
|
|
|
|
|
779
|
|
|
|
|
|
|
# We don't care about any context features here, only in _get_data_channel(). |
780
|
|
|
|
|
|
|
# You can't apply these features until after the command using the data |
781
|
|
|
|
|
|
|
# channel has been sent to the FTPS server and the FTPS server responds |
782
|
|
|
|
|
|
|
# to the socket you are creating below! |
783
|
|
|
|
|
|
|
|
784
|
|
|
|
|
|
|
# Makes it easier to refrence all those pesky values over & over again. |
785
|
0
|
|
|
|
|
|
my $ftps_ref = ${*$self}{_FTPSSL_arguments}; |
|
0
|
|
|
|
|
|
|
786
|
|
|
|
|
|
|
|
787
|
0
|
|
|
|
|
|
my $msg = ""; |
788
|
0
|
|
|
|
|
|
my %proxyArgs; |
789
|
0
|
0
|
|
|
|
|
if (exists $ftps_ref->{myProxyArgs} ) { |
790
|
0
|
|
|
|
|
|
%proxyArgs = %{$ftps_ref->{myProxyArgs}}; |
|
0
|
|
|
|
|
|
|
791
|
0
|
|
0
|
|
|
|
$msg = ($proxyArgs{'proxy-host'} || "undef") . ":" . ($proxyArgs{'proxy-port'} || "undef"); |
|
|
|
0
|
|
|
|
|
792
|
|
|
|
|
|
|
|
793
|
|
|
|
|
|
|
# Update the host & port to connect to through the proxy server ... |
794
|
0
|
|
|
|
|
|
$proxyArgs{'remote-host'} = $host; |
795
|
0
|
|
|
|
|
|
$proxyArgs{'remote-port'} = $port; |
796
|
|
|
|
|
|
|
} |
797
|
|
|
|
|
|
|
|
798
|
0
|
|
|
|
|
|
my $socket; |
799
|
|
|
|
|
|
|
|
800
|
0
|
0
|
0
|
|
|
|
if ( $ftps_ref->{data_prot} eq DATA_PROT_PRIVATE ) { |
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
801
|
0
|
0
|
|
|
|
|
if (exists $ftps_ref->{myProxyArgs} ) { |
802
|
|
|
|
|
|
|
# Set the proxy parameters for all future data connections ... |
803
|
|
|
|
|
|
|
Net::SSLeay::set_proxy ( $proxyArgs{'proxy-host'}, $proxyArgs{'proxy-port'}, |
804
|
0
|
|
|
|
|
|
$proxyArgs{'proxy-user'}, $proxyArgs{'proxy-pass'} ); |
805
|
0
|
|
|
|
|
|
$msg = " (via proxy $msg)"; |
806
|
|
|
|
|
|
|
} |
807
|
|
|
|
|
|
|
|
808
|
|
|
|
|
|
|
# carp "MSG=($msg)\n" . "proxyhost=($Net::SSLeay::proxyhost--$Net::SSLeay::proxyport)\n" . "auth=($Net::SSLeay::proxyauth--$Net::SSLeay::CRLF)\n"; |
809
|
|
|
|
|
|
|
|
810
|
0
|
0
|
|
|
|
|
$socket = Net::SSLeay::Handle->make_socket( $host, $port ) |
811
|
|
|
|
|
|
|
or return $self->_croak_or_return (0, |
812
|
|
|
|
|
|
|
"Can't open private data connection to $host:$port $msg"); |
813
|
|
|
|
|
|
|
|
814
|
|
|
|
|
|
|
} elsif ( $ftps_ref->{data_prot} eq DATA_PROT_CLEAR && exists $ftps_ref->{myProxyArgs} ) { |
815
|
0
|
0
|
|
|
|
|
$socket = Net::HTTPTunnel->new ( %proxyArgs ) or |
816
|
|
|
|
|
|
|
return $self->_croak_or_return (0, |
817
|
|
|
|
|
|
|
"Can't open HTTP Proxy data connection tunnel from $msg to $host:$port"); |
818
|
|
|
|
|
|
|
|
819
|
|
|
|
|
|
|
} elsif ( $ftps_ref->{data_prot} eq DATA_PROT_CLEAR ) { |
820
|
0
|
|
|
|
|
|
my %socketArgs = %{$ftps_ref->{mySocketOpts}}; |
|
0
|
|
|
|
|
|
|
821
|
0
|
|
|
|
|
|
$socketArgs{PeerAddr} = $host; |
822
|
0
|
|
|
|
|
|
$socketArgs{PeerPort} = $port; |
823
|
|
|
|
|
|
|
|
824
|
0
|
0
|
|
|
|
|
$socket = $IOCLASS->new ( %socketArgs ) or |
825
|
|
|
|
|
|
|
# $socket = IO::Socket::INET->new( %socketArgs ) or |
826
|
|
|
|
|
|
|
return $self->_croak_or_return (0, |
827
|
|
|
|
|
|
|
"Can't open clear data connection to $host:$port"); |
828
|
|
|
|
|
|
|
|
829
|
|
|
|
|
|
|
} else { |
830
|
|
|
|
|
|
|
# TODO: Fix so DATA_PROT_SAFE & DATA_PROT_CONFIDENTIAL work. |
831
|
0
|
|
|
|
|
|
return $self->_croak_or_return (0, "Currently doesn't support mode $ftps_ref->{data_prot} for data channels to $host:$port"); |
832
|
|
|
|
|
|
|
} |
833
|
|
|
|
|
|
|
|
834
|
0
|
|
|
|
|
|
$ftps_ref->{data_ch} = \*$socket; # Must call _get_data_channel() before using. |
835
|
0
|
|
|
|
|
|
$ftps_ref->{data_host} = $host; # Save the IP Address used ... |
836
|
|
|
|
|
|
|
|
837
|
0
|
|
|
|
|
|
return 1; # Data Channel was established! |
838
|
|
|
|
|
|
|
} |
839
|
|
|
|
|
|
|
|
840
|
|
|
|
|
|
|
sub _get_data_channel { |
841
|
0
|
|
|
0
|
|
|
my $self = shift; |
842
|
|
|
|
|
|
|
|
843
|
|
|
|
|
|
|
# Makes it easier to refrence all those pesky values over & over again. |
844
|
0
|
|
|
|
|
|
my $ftps_ref = ${*$self}{_FTPSSL_arguments}; |
|
0
|
|
|
|
|
|
|
845
|
|
|
|
|
|
|
|
846
|
|
|
|
|
|
|
# $self->_debug_print_hash ("host", "port", $ftps_ref->{data_prot}, $ftps_ref->{data_ch}); |
847
|
|
|
|
|
|
|
|
848
|
0
|
|
|
|
|
|
my $io; |
849
|
0
|
0
|
0
|
|
|
|
if ( $ftps_ref->{data_prot} eq DATA_PROT_PRIVATE && exists ($ftps_ref->{myContext}) ) { |
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
850
|
0
|
|
|
|
|
|
my %ssl_opts = %{$ftps_ref->{myContext}}; |
|
0
|
|
|
|
|
|
|
851
|
0
|
|
|
|
|
|
my $mode = ${*$self}{_SSL_arguments}->{SSL_version}; |
|
0
|
|
|
|
|
|
|
852
|
|
|
|
|
|
|
|
853
|
|
|
|
|
|
|
# Can we use SNI? |
854
|
0
|
0
|
0
|
|
|
|
if ( $self->can ("can_client_sni") && $self->can_client_sni () ) { |
855
|
0
|
|
|
|
|
|
$ssl_opts{SSL_hostname} = $ftps_ref->{data_host}; |
856
|
|
|
|
|
|
|
} |
857
|
|
|
|
|
|
|
|
858
|
0
|
0
|
|
|
|
|
$io = IO::Socket::SSL->start_SSL ( $ftps_ref->{data_ch}, \%ssl_opts ) |
859
|
|
|
|
|
|
|
or return $self->_croak_or_return ( 0, |
860
|
|
|
|
|
|
|
"$mode: " . IO::Socket::SSL::errstr () ); |
861
|
|
|
|
|
|
|
|
862
|
|
|
|
|
|
|
} elsif ( $ftps_ref->{data_prot} eq DATA_PROT_PRIVATE ) { |
863
|
0
|
|
|
|
|
|
$io = IO::Handle->new (); |
864
|
0
|
|
|
|
|
|
tie ( *$io, "Net::SSLeay::Handle", $ftps_ref->{data_ch} ); |
865
|
|
|
|
|
|
|
|
866
|
|
|
|
|
|
|
} elsif ( $ftps_ref->{data_prot} eq DATA_PROT_CLEAR ) { |
867
|
0
|
|
|
|
|
|
$io = $ftps_ref->{data_ch}; |
868
|
|
|
|
|
|
|
|
869
|
|
|
|
|
|
|
} else { |
870
|
|
|
|
|
|
|
# TODO: Fix so DATA_PROT_SAFE & DATA_PROT_CONFIDENTIAL work. |
871
|
0
|
|
|
|
|
|
return $self->_croak_or_return (0, "Currently doesn't support mode $ftps_ref->{data_prot} for data channels."); |
872
|
|
|
|
|
|
|
} |
873
|
|
|
|
|
|
|
|
874
|
0
|
|
|
|
|
|
_my_autoflush ( $io ); |
875
|
|
|
|
|
|
|
|
876
|
|
|
|
|
|
|
# $self->_debug_print_hash ("host", "port", $ftps_ref->{data_prot}, $io, "="); |
877
|
|
|
|
|
|
|
|
878
|
0
|
|
|
|
|
|
return ( $io ); |
879
|
|
|
|
|
|
|
} |
880
|
|
|
|
|
|
|
|
881
|
|
|
|
|
|
|
# Note: This doesn't reference $self on purpose! (so not a bug!) |
882
|
|
|
|
|
|
|
# See Bug Id 82094 |
883
|
|
|
|
|
|
|
sub _my_autoflush { |
884
|
0
|
|
|
0
|
|
|
my $skt = shift; |
885
|
|
|
|
|
|
|
|
886
|
0
|
0
|
|
|
|
|
if ( $skt->can ('autoflush') ) { |
887
|
0
|
|
|
|
|
|
$skt->autoflush (1); |
888
|
|
|
|
|
|
|
} else { |
889
|
|
|
|
|
|
|
# So turn it on manually instead ... |
890
|
0
|
|
|
|
|
|
my $oldFh = select $skt; |
891
|
0
|
|
|
|
|
|
$| = 1; |
892
|
0
|
|
|
|
|
|
select $oldFh; |
893
|
|
|
|
|
|
|
} |
894
|
|
|
|
|
|
|
|
895
|
0
|
|
|
|
|
|
return; |
896
|
|
|
|
|
|
|
} |
897
|
|
|
|
|
|
|
|
898
|
|
|
|
|
|
|
# Note: This doesn't reference $self on purpose! (so not a bug!) |
899
|
|
|
|
|
|
|
# See Bug Id 82094 |
900
|
|
|
|
|
|
|
sub _my_close { |
901
|
0
|
|
|
0
|
|
|
my $io = shift; |
902
|
|
|
|
|
|
|
|
903
|
0
|
0
|
|
|
|
|
if ( $io->can ('close') ) { |
904
|
0
|
|
|
|
|
|
$io->close (); |
905
|
|
|
|
|
|
|
} else { |
906
|
0
|
|
|
|
|
|
close ($io); |
907
|
|
|
|
|
|
|
} |
908
|
|
|
|
|
|
|
|
909
|
0
|
|
|
|
|
|
return; |
910
|
|
|
|
|
|
|
} |
911
|
|
|
|
|
|
|
|
912
|
|
|
|
|
|
|
sub nlst { |
913
|
0
|
|
|
0
|
1
|
|
my $self = shift; |
914
|
|
|
|
|
|
|
|
915
|
0
|
|
|
|
|
|
return ( $self->list (@_) ); |
916
|
|
|
|
|
|
|
} |
917
|
|
|
|
|
|
|
|
918
|
|
|
|
|
|
|
# Returns an empty array on failure ... |
919
|
|
|
|
|
|
|
|
920
|
|
|
|
|
|
|
sub list { |
921
|
0
|
|
|
0
|
1
|
|
my $self = shift; |
922
|
0
|
|
0
|
|
|
|
my $path = shift || undef; # Causes "" to be treated as "."! |
923
|
0
|
|
0
|
|
|
|
my $pattern = shift || undef; # Only wild cards are * and ? (same as ls cmd) |
924
|
|
|
|
|
|
|
|
925
|
0
|
|
|
|
|
|
my $dati = ""; |
926
|
|
|
|
|
|
|
|
927
|
|
|
|
|
|
|
# "(caller(1))[3]" returns undef if not called by another Net::FTPSSL method! |
928
|
0
|
|
|
|
|
|
my $c = (caller(1))[3]; |
929
|
0
|
|
0
|
|
|
|
my $nlst_flg = ( defined $c && $c eq "Net::FTPSSL::nlst" ); |
930
|
|
|
|
|
|
|
|
931
|
0
|
0
|
|
|
|
|
unless ( $self->prep_data_channel( $nlst_flg ? "NLST" : "LIST" ) ) { |
|
|
0
|
|
|
|
|
|
932
|
0
|
|
|
|
|
|
return (); # Already decided not to call croak if you get here! |
933
|
|
|
|
|
|
|
} |
934
|
|
|
|
|
|
|
|
935
|
0
|
0
|
|
|
|
|
unless ( $nlst_flg ? $self->_nlst($path) : $self->_list($path) ) { |
|
|
0
|
|
|
|
|
|
936
|
0
|
|
|
|
|
|
$self->_croak_or_return (); |
937
|
0
|
|
|
|
|
|
return (); |
938
|
|
|
|
|
|
|
} |
939
|
|
|
|
|
|
|
|
940
|
0
|
|
|
|
|
|
my ( $tmp, $io, $size ); |
941
|
|
|
|
|
|
|
|
942
|
0
|
|
|
|
|
|
$size = ${*$self}{_FTPSSL_arguments}->{buf_size}; |
|
0
|
|
|
|
|
|
|
943
|
|
|
|
|
|
|
|
944
|
0
|
|
|
|
|
|
$io = $self->_get_data_channel (); |
945
|
0
|
0
|
|
|
|
|
unless ( defined $io ) { |
946
|
0
|
|
|
|
|
|
return (); # Already decided not to call croak if you get here! |
947
|
|
|
|
|
|
|
} |
948
|
|
|
|
|
|
|
|
949
|
0
|
|
|
|
|
|
while ( my $len = sysread $io, $tmp, $size ) { |
950
|
0
|
0
|
|
|
|
|
unless ( defined $len ) { |
951
|
0
|
0
|
|
|
|
|
next if $! == EINTR; |
952
|
0
|
0
|
|
|
|
|
my $type = $nlst_flg ? 'nlst()' : 'list()'; |
953
|
0
|
|
|
|
|
|
$self->_croak_or_return (0, "System read error on read while $type: $!"); |
954
|
0
|
|
|
|
|
|
_my_close ($io); # Old way $io->close(); |
955
|
0
|
|
|
|
|
|
return (); |
956
|
|
|
|
|
|
|
} |
957
|
0
|
|
|
|
|
|
$dati .= $tmp; |
958
|
|
|
|
|
|
|
} |
959
|
|
|
|
|
|
|
|
960
|
0
|
|
|
|
|
|
_my_close ($io); # Old way $io->close(); |
961
|
|
|
|
|
|
|
|
962
|
|
|
|
|
|
|
# To catch the expected "226 Closing data connection." |
963
|
0
|
0
|
|
|
|
|
if ( $self->response() != CMD_OK ) { |
964
|
0
|
|
|
|
|
|
$self->_croak_or_return (); |
965
|
0
|
|
|
|
|
|
return (); |
966
|
|
|
|
|
|
|
} |
967
|
|
|
|
|
|
|
|
968
|
|
|
|
|
|
|
# Convert to use local separators ... |
969
|
|
|
|
|
|
|
# Required for callback functionality ... |
970
|
0
|
|
|
|
|
|
$dati =~ s/\015\012/\n/g; |
971
|
|
|
|
|
|
|
|
972
|
|
|
|
|
|
|
# Remove that pesky total that isn't returned from all FTPS servers. |
973
|
|
|
|
|
|
|
# This way we are consistant for everyone! |
974
|
|
|
|
|
|
|
# Another reason to strip it out is that it's the total block size, |
975
|
|
|
|
|
|
|
# not the total number of files. Which gets confusing. |
976
|
|
|
|
|
|
|
# Works no matter where the total is in the string ... |
977
|
0
|
0
|
|
|
|
|
unless ( $nlst_flg ) { |
978
|
0
|
0
|
|
|
|
|
$dati =~ s/^\n//s if ( $dati =~ s/^\s*total\s+\d+\s*$//mi ); |
979
|
0
|
|
|
|
|
|
$dati =~ s/\n\n/\n/s; # In case total not 1st line ... |
980
|
|
|
|
|
|
|
} |
981
|
|
|
|
|
|
|
|
982
|
|
|
|
|
|
|
# What if we asked to use patterns to limit the listing returned ? |
983
|
0
|
0
|
|
|
|
|
if ( defined $pattern ) { |
984
|
0
|
|
|
|
|
|
my $p = $pattern; # So can display original pattern later on. |
985
|
|
|
|
|
|
|
|
986
|
|
|
|
|
|
|
# Convert from shell wild cards into a perl regular expression ... |
987
|
0
|
|
|
|
|
|
$pattern =~ s/([.+])/\\$1/g; |
988
|
0
|
|
|
|
|
|
$pattern =~ s/[?]/./g; |
989
|
|
|
|
|
|
|
|
990
|
0
|
0
|
|
|
|
|
if ( $nlst_flg ) { |
991
|
0
|
0
|
|
|
|
|
if ( $pattern =~ m/[*]/ ) { |
992
|
|
|
|
|
|
|
# Don't allow path separators in the string ... |
993
|
|
|
|
|
|
|
# Can't do this with regular expressions ... |
994
|
0
|
|
|
|
|
|
$pattern = join ( "[^\\\\/]*", split (/\*/, $pattern . "XXX") ); |
995
|
0
|
|
|
|
|
|
$pattern =~ s/XXX$//; |
996
|
|
|
|
|
|
|
} |
997
|
0
|
|
|
|
|
|
$pattern = '(^|[\\\\/])' . $pattern . '$'; |
998
|
|
|
|
|
|
|
|
999
|
|
|
|
|
|
|
} else { |
1000
|
0
|
|
|
|
|
|
$pattern =~ s/[*]/\\S*/g; # No spaces in file's name is allowed! |
1001
|
0
|
|
|
|
|
|
$pattern = '\s+(' . $pattern . ')($|\s+->\s+)'; |
1002
|
|
|
|
|
|
|
} |
1003
|
|
|
|
|
|
|
|
1004
|
0
|
|
|
|
|
|
$self->_print_DBG ( "PATTERN: <- $p => $pattern ->\n" ); |
1005
|
|
|
|
|
|
|
|
1006
|
|
|
|
|
|
|
# Now only keep those files that match the pattern. |
1007
|
0
|
|
|
|
|
|
my @res; |
1008
|
0
|
|
|
|
|
|
foreach ( split ( /\n/, $dati ) ) { |
1009
|
0
|
0
|
|
|
|
|
push (@res, $_) if ( $_ =~ m/$pattern/i ); |
1010
|
|
|
|
|
|
|
} |
1011
|
0
|
|
|
|
|
|
$dati = join ("\n", @res); |
1012
|
|
|
|
|
|
|
} |
1013
|
|
|
|
|
|
|
|
1014
|
0
|
|
|
|
|
|
my $len = length ($dati); |
1015
|
0
|
0
|
|
|
|
|
my $lvl = $nlst_flg ? 2 : 1; |
1016
|
0
|
|
|
|
|
|
my $total = 0; |
1017
|
|
|
|
|
|
|
|
1018
|
0
|
0
|
|
|
|
|
if ( $len > 0 ) { |
1019
|
0
|
|
|
|
|
|
$total = $self->_call_callback ($lvl, \$dati, \$len, 0); |
1020
|
|
|
|
|
|
|
} |
1021
|
|
|
|
|
|
|
|
1022
|
|
|
|
|
|
|
# Process trailing call back info if present. |
1023
|
0
|
|
|
|
|
|
my $trail; |
1024
|
0
|
|
|
|
|
|
($trail, $len, $total) = $self->_end_callback ($lvl, $total); |
1025
|
0
|
0
|
|
|
|
|
if ( $trail ) { |
1026
|
0
|
|
|
|
|
|
$dati .= $trail; |
1027
|
|
|
|
|
|
|
} |
1028
|
|
|
|
|
|
|
|
1029
|
0
|
0
|
|
|
|
|
return $dati ? split( /\n/, $dati ) : (); |
1030
|
|
|
|
|
|
|
} |
1031
|
|
|
|
|
|
|
|
1032
|
|
|
|
|
|
|
sub _get_local_file_size { |
1033
|
0
|
|
|
0
|
|
|
my $self = shift; |
1034
|
0
|
|
|
|
|
|
my $file_name = shift; |
1035
|
|
|
|
|
|
|
|
1036
|
|
|
|
|
|
|
# Return the trivial cases ... |
1037
|
0
|
0
|
|
|
|
|
return (0) unless ( -f $file_name); |
1038
|
0
|
0
|
|
|
|
|
return (-s $file_name) if ( ${*$self}{_FTPSSL_arguments}->{type} eq MODE_BINARY ); |
|
0
|
|
|
|
|
|
|
1039
|
|
|
|
|
|
|
|
1040
|
|
|
|
|
|
|
# If we get here, we know we are transfering the file in ASCII mode ... |
1041
|
0
|
|
|
|
|
|
my $fd; |
1042
|
0
|
0
|
|
|
|
|
unless ( open( $fd, "< $file_name" ) ) { |
1043
|
0
|
|
|
|
|
|
return $self->_croak_or_return(0, |
1044
|
|
|
|
|
|
|
"Can't open file in ASCII mode! ($file_name) $!"); |
1045
|
|
|
|
|
|
|
} |
1046
|
|
|
|
|
|
|
|
1047
|
0
|
|
|
|
|
|
my ($len, $offset) = (0, 0); |
1048
|
0
|
|
|
|
|
|
my $data; |
1049
|
0
|
|
0
|
|
|
|
my $size = ${*$self}{_FTPSSL_arguments}->{buf_size} || 2048; |
1050
|
|
|
|
|
|
|
|
1051
|
0
|
|
|
|
|
|
while ( $len = sysread ( $fd, $data, $size ) ) { |
1052
|
|
|
|
|
|
|
# print STDERR "Line: ($len, $data)\n"; |
1053
|
0
|
|
|
|
|
|
$data =~ s/\n/\015\012/g; |
1054
|
0
|
|
|
|
|
|
$len = length ($data); |
1055
|
0
|
|
|
|
|
|
$offset += $len; |
1056
|
|
|
|
|
|
|
} |
1057
|
|
|
|
|
|
|
|
1058
|
0
|
0
|
|
|
|
|
unless ( defined $len ) { |
1059
|
0
|
0
|
|
|
|
|
unless ( $! == EINTR ) { |
1060
|
0
|
|
|
|
|
|
return $self->_croak_or_return (0, |
1061
|
|
|
|
|
|
|
"System read error on calculating OFFSET: $!"); |
1062
|
|
|
|
|
|
|
} |
1063
|
|
|
|
|
|
|
} |
1064
|
|
|
|
|
|
|
|
1065
|
0
|
|
|
|
|
|
close ($fd); |
1066
|
|
|
|
|
|
|
|
1067
|
0
|
|
|
|
|
|
return ($offset); |
1068
|
|
|
|
|
|
|
} |
1069
|
|
|
|
|
|
|
|
1070
|
|
|
|
|
|
|
sub _get_local_file_truncate { |
1071
|
0
|
|
|
0
|
|
|
my $self = shift; |
1072
|
0
|
|
|
|
|
|
my $file_name = shift; |
1073
|
0
|
|
|
|
|
|
my $offset = shift; # Value > 0. |
1074
|
|
|
|
|
|
|
|
1075
|
0
|
|
|
|
|
|
my $max_offset = $self->_get_local_file_size ( $file_name ); |
1076
|
0
|
0
|
|
|
|
|
return (undef) unless ( defined $offset ); |
1077
|
|
|
|
|
|
|
|
1078
|
0
|
0
|
|
|
|
|
if ( $offset > $max_offset ) { |
1079
|
0
|
|
|
|
|
|
return $self->_croak_or_return (0, |
1080
|
|
|
|
|
|
|
"OFFSET ($offset) is larger than the local file ($max_offset)"); |
1081
|
|
|
|
|
|
|
} |
1082
|
|
|
|
|
|
|
|
1083
|
|
|
|
|
|
|
# Exactly the size of the file ... |
1084
|
0
|
0
|
|
|
|
|
return ( $offset ) if ( $offset == $max_offset ); |
1085
|
|
|
|
|
|
|
|
1086
|
|
|
|
|
|
|
# It's smaller & non-zero, so now we must truncate the local file ... |
1087
|
0
|
|
|
|
|
|
my $fd; |
1088
|
0
|
0
|
|
|
|
|
unless ( open( $fd, "+< $file_name" ) ) { |
1089
|
0
|
|
|
|
|
|
return $self->_croak_or_return(0, |
1090
|
|
|
|
|
|
|
"Can't open file in read/write mode! ($file_name): $!"); |
1091
|
|
|
|
|
|
|
} |
1092
|
|
|
|
|
|
|
|
1093
|
0
|
|
|
|
|
|
my $pos = 0; |
1094
|
0
|
0
|
|
|
|
|
if ( ${*$self}{_FTPSSL_arguments}->{type} eq MODE_BINARY ) { |
|
0
|
|
|
|
|
|
|
1095
|
0
|
0
|
|
|
|
|
unless ( binmode $fd ) { |
1096
|
0
|
|
|
|
|
|
return $self->_croak_or_return(0, "Can't set binary mode to local file!"); |
1097
|
|
|
|
|
|
|
} |
1098
|
0
|
|
|
|
|
|
$pos = $offset; |
1099
|
|
|
|
|
|
|
|
1100
|
|
|
|
|
|
|
} else { |
1101
|
|
|
|
|
|
|
# ASCII Mode ... |
1102
|
|
|
|
|
|
|
# For some OS, $off & $pos are always the same, |
1103
|
|
|
|
|
|
|
# while for other OS they differ once the 1st |
1104
|
|
|
|
|
|
|
# was hit! |
1105
|
0
|
|
|
|
|
|
my ($len, $off) = (0, 0); |
1106
|
0
|
|
|
|
|
|
my $data; |
1107
|
0
|
|
0
|
|
|
|
my $size = ${*$self}{_FTPSSL_arguments}->{buf_size} || 2048; |
1108
|
|
|
|
|
|
|
|
1109
|
0
|
0
|
|
|
|
|
$size = $offset if ( $size > $offset ); |
1110
|
|
|
|
|
|
|
|
1111
|
0
|
|
|
|
|
|
while ( $len = sysread ( $fd, $data, $size ) ) { |
1112
|
|
|
|
|
|
|
# print STDERR "Line: ($len, $data)\n"; |
1113
|
0
|
|
|
|
|
|
my $cr_only = ($data eq "\n"); |
1114
|
0
|
|
|
|
|
|
$data =~ s/\n/\015\012/g; |
1115
|
0
|
|
|
|
|
|
$off += length ($data); |
1116
|
0
|
|
|
|
|
|
my $diff = $offset - $off; |
1117
|
|
|
|
|
|
|
|
1118
|
|
|
|
|
|
|
# The offset was between the \015 & \012 |
1119
|
|
|
|
|
|
|
# (Bogus for a lot of OS, so must fix offset one char smaller.) |
1120
|
0
|
0
|
0
|
|
|
|
if ( $diff == -1 && $cr_only ) { |
1121
|
0
|
|
|
|
|
|
my $old = $offset--; |
1122
|
0
|
|
|
|
|
|
$self->_print_DBG ("<<+ 222 HOT FIX ==> Offset ($old ==> $offset) ", |
1123
|
|
|
|
|
|
|
"Since can't truncate between \\015 & \\012 ", |
1124
|
|
|
|
|
|
|
"in ASCII mode!\n"); |
1125
|
|
|
|
|
|
|
# Use the last $pos value, no need to recalculate it ... |
1126
|
0
|
|
|
|
|
|
last; |
1127
|
|
|
|
|
|
|
} |
1128
|
|
|
|
|
|
|
|
1129
|
|
|
|
|
|
|
# Found the requested offset ... |
1130
|
0
|
0
|
|
|
|
|
if ( $diff == 0 ) { |
1131
|
0
|
|
|
|
|
|
$pos = sysseek ( $fd, 0, 1 ); # Current position in the file |
1132
|
0
|
|
|
|
|
|
last; |
1133
|
|
|
|
|
|
|
} |
1134
|
|
|
|
|
|
|
|
1135
|
|
|
|
|
|
|
# Still more data to read ... |
1136
|
0
|
0
|
|
|
|
|
if ( $diff > 0 ) { |
1137
|
0
|
|
|
|
|
|
$pos = sysseek ( $fd, 0, 1 ); # Current position in the file |
1138
|
0
|
0
|
|
|
|
|
$size = $diff if ( $size > $diff ); |
1139
|
|
|
|
|
|
|
|
1140
|
|
|
|
|
|
|
# Read past my offset value ... So re-read the last line again |
1141
|
|
|
|
|
|
|
# with a smaller buffer size! |
1142
|
|
|
|
|
|
|
} else { |
1143
|
0
|
|
|
|
|
|
$pos = sysseek ( $fd, $pos, 0 ); # The previous position in the file |
1144
|
0
|
|
|
|
|
|
$off -= length ($data); |
1145
|
0
|
|
|
|
|
|
$size += $diff; # Diff is negative here ... |
1146
|
|
|
|
|
|
|
} |
1147
|
|
|
|
|
|
|
|
1148
|
0
|
0
|
|
|
|
|
last unless ($pos); |
1149
|
|
|
|
|
|
|
} # End while ... |
1150
|
|
|
|
|
|
|
|
1151
|
0
|
0
|
|
|
|
|
unless ( defined $len ) { |
1152
|
0
|
0
|
|
|
|
|
unless ( $! == EINTR ) { |
1153
|
0
|
|
|
|
|
|
return $self->_croak_or_return (0, |
1154
|
|
|
|
|
|
|
"System read error on calculating OFFSET: $!"); |
1155
|
|
|
|
|
|
|
} |
1156
|
|
|
|
|
|
|
} |
1157
|
|
|
|
|
|
|
} # End else ASCII ... |
1158
|
|
|
|
|
|
|
|
1159
|
0
|
0
|
|
|
|
|
unless ($pos) { |
1160
|
0
|
|
|
|
|
|
return $self->_croak_or_return (0, |
1161
|
|
|
|
|
|
|
"System seek error before Truncation: $!"); |
1162
|
|
|
|
|
|
|
} |
1163
|
|
|
|
|
|
|
|
1164
|
0
|
0
|
|
|
|
|
unless ( truncate ( $fd, $pos ) ) { |
1165
|
0
|
|
|
|
|
|
return $self->_croak_or_return (0, "Truncate File Error: $!"); |
1166
|
|
|
|
|
|
|
} |
1167
|
|
|
|
|
|
|
|
1168
|
0
|
|
|
|
|
|
close ( $fd ); |
1169
|
|
|
|
|
|
|
|
1170
|
0
|
|
|
|
|
|
return ( $offset ); |
1171
|
|
|
|
|
|
|
} |
1172
|
|
|
|
|
|
|
|
1173
|
|
|
|
|
|
|
sub get { |
1174
|
0
|
|
|
0
|
1
|
|
my $self = shift; |
1175
|
0
|
|
|
|
|
|
my $file_rem = shift; |
1176
|
0
|
|
|
|
|
|
my $file_loc = shift; |
1177
|
0
|
|
0
|
|
|
|
my $offset = shift || ${*$self}{_FTPSSL_arguments}->{net_ftpssl_rest_offset} || 0; |
1178
|
|
|
|
|
|
|
|
1179
|
|
|
|
|
|
|
# Clear out this messy restart() cluge for next time ... |
1180
|
0
|
|
|
|
|
|
delete ( ${*$self}{_FTPSSL_arguments}->{net_ftpssl_rest_offset} ); |
|
0
|
|
|
|
|
|
|
1181
|
|
|
|
|
|
|
|
1182
|
0
|
0
|
|
|
|
|
if ( $offset < -1 ) { |
1183
|
0
|
|
|
|
|
|
return $self->_croak_or_return(0, "Invalid file offset ($offset)!"); |
1184
|
|
|
|
|
|
|
} |
1185
|
|
|
|
|
|
|
|
1186
|
0
|
|
|
|
|
|
my ( $size, $localfd ); |
1187
|
0
|
|
|
|
|
|
my $close_file = 0; |
1188
|
|
|
|
|
|
|
|
1189
|
0
|
0
|
|
|
|
|
unless ($file_loc) { |
1190
|
0
|
|
|
|
|
|
$file_loc = basename($file_rem); |
1191
|
|
|
|
|
|
|
} |
1192
|
|
|
|
|
|
|
|
1193
|
0
|
|
0
|
|
|
|
$size = ${*$self}{_FTPSSL_arguments}->{buf_size} || 2048; |
1194
|
|
|
|
|
|
|
|
1195
|
0
|
0
|
|
|
|
|
if ( $self->_isa_glob ($file_loc) ) { |
1196
|
0
|
0
|
|
|
|
|
if ( $offset == -1 ) { |
1197
|
0
|
|
|
|
|
|
return $self->_croak_or_return(0, |
1198
|
|
|
|
|
|
|
"Invalid file offset ($offset) for a file handle!"); |
1199
|
|
|
|
|
|
|
} |
1200
|
0
|
|
|
|
|
|
$localfd = \*$file_loc; |
1201
|
|
|
|
|
|
|
|
1202
|
|
|
|
|
|
|
} else { |
1203
|
|
|
|
|
|
|
# Calculate the file offset to send to the FTPS server via REST ... |
1204
|
0
|
0
|
|
|
|
|
if ($offset == -1) { |
|
|
0
|
|
|
|
|
|
1205
|
0
|
|
|
|
|
|
$offset = $self->_get_local_file_size ($file_loc); |
1206
|
0
|
0
|
|
|
|
|
return (undef) unless (defined $offset); |
1207
|
|
|
|
|
|
|
} elsif ($offset) { |
1208
|
0
|
|
|
|
|
|
$offset = $self->_get_local_file_truncate ($file_loc, $offset); |
1209
|
0
|
0
|
|
|
|
|
return (undef) unless (defined $offset); |
1210
|
|
|
|
|
|
|
} |
1211
|
|
|
|
|
|
|
|
1212
|
|
|
|
|
|
|
# Now we can open the file we need to write to ... |
1213
|
0
|
0
|
|
|
|
|
my $mode = ($offset) ? ">>" : ">"; |
1214
|
0
|
0
|
|
|
|
|
unless ( open( $localfd, "$mode $file_loc" ) ) { |
1215
|
0
|
|
|
|
|
|
return $self->_croak_or_return(0, |
1216
|
|
|
|
|
|
|
"Can't create/open local file! ($mode $file_loc)"); |
1217
|
|
|
|
|
|
|
} |
1218
|
0
|
|
|
|
|
|
$close_file = 1; |
1219
|
|
|
|
|
|
|
} |
1220
|
|
|
|
|
|
|
|
1221
|
0
|
|
|
|
|
|
my $fix_cr_issue = 1; |
1222
|
0
|
0
|
|
|
|
|
if ( ${*$self}{_FTPSSL_arguments}->{type} eq MODE_BINARY ) { |
|
0
|
|
|
|
|
|
|
1223
|
0
|
0
|
|
|
|
|
unless ( binmode $localfd ) { |
1224
|
0
|
0
|
|
|
|
|
if ( $close_file ) { |
1225
|
0
|
|
|
|
|
|
close ($localfd); |
1226
|
0
|
0
|
|
|
|
|
unlink ($file_loc) unless ($offset); |
1227
|
|
|
|
|
|
|
} |
1228
|
0
|
|
|
|
|
|
return $self->_croak_or_return(0, "Can't set binary mode to local file!"); |
1229
|
|
|
|
|
|
|
} |
1230
|
0
|
|
|
|
|
|
$fix_cr_issue = 0; |
1231
|
|
|
|
|
|
|
} |
1232
|
|
|
|
|
|
|
|
1233
|
0
|
0
|
|
|
|
|
unless ( $self->prep_data_channel( "RETR", $file_rem ) ) { |
1234
|
0
|
0
|
|
|
|
|
if ( $close_file ) { |
1235
|
0
|
|
|
|
|
|
close ($localfd); |
1236
|
0
|
0
|
|
|
|
|
unlink ($file_loc) unless ($offset); |
1237
|
|
|
|
|
|
|
} |
1238
|
0
|
|
|
|
|
|
return undef; # Already decided not to call croak if you get here! |
1239
|
|
|
|
|
|
|
} |
1240
|
|
|
|
|
|
|
|
1241
|
|
|
|
|
|
|
# "(caller(1))[3]" returns undef if not called by another Net::FTPSSL method! |
1242
|
0
|
|
|
|
|
|
my $c = (caller(1))[3]; |
1243
|
0
|
0
|
0
|
|
|
|
my $cb_idx = ( defined $c && $c eq "Net::FTPSSL::xget" ) ? 2 : 1; |
1244
|
0
|
0
|
|
|
|
|
my $func = ( $cb_idx == 1 ) ? "get" : "xget"; |
1245
|
|
|
|
|
|
|
|
1246
|
|
|
|
|
|
|
|
1247
|
|
|
|
|
|
|
# Check if the "get" failed ... |
1248
|
0
|
0
|
|
|
|
|
my $rest = ($offset) ? $self->_rest ($offset) : 1; |
1249
|
0
|
0
|
0
|
|
|
|
unless ( $rest && $self->_retr($file_rem) ) { |
1250
|
0
|
0
|
|
|
|
|
if ($close_file) { |
1251
|
0
|
|
|
|
|
|
close ($localfd); |
1252
|
0
|
0
|
|
|
|
|
unlink ($file_loc) unless ($offset); |
1253
|
|
|
|
|
|
|
} |
1254
|
|
|
|
|
|
|
|
1255
|
0
|
0
|
0
|
|
|
|
if ( $offset && $rest ) { |
1256
|
0
|
|
|
|
|
|
my $msg = $self->last_message (); |
1257
|
0
|
|
|
|
|
|
$self->_rest (0); # Must clear out on failure! |
1258
|
0
|
|
|
|
|
|
${*$self}{_FTPSSL_arguments}->{last_ftp_msg} = $msg; # Restore original error message! |
|
0
|
|
|
|
|
|
|
1259
|
|
|
|
|
|
|
} |
1260
|
|
|
|
|
|
|
|
1261
|
0
|
|
|
|
|
|
return $self->_croak_or_return (); |
1262
|
|
|
|
|
|
|
} |
1263
|
|
|
|
|
|
|
|
1264
|
0
|
|
|
|
|
|
my ( $data, $written, $io ); |
1265
|
|
|
|
|
|
|
|
1266
|
0
|
|
|
|
|
|
$io = $self->_get_data_channel (); |
1267
|
0
|
0
|
|
|
|
|
unless ( defined $io ) { |
1268
|
0
|
0
|
|
|
|
|
if ( $close_file ) { |
1269
|
0
|
|
|
|
|
|
close ($localfd); |
1270
|
0
|
0
|
|
|
|
|
unlink ($file_loc) unless ($offset); |
1271
|
|
|
|
|
|
|
} |
1272
|
0
|
|
|
|
|
|
return undef; # Already decided not to call croak if you get here! |
1273
|
|
|
|
|
|
|
} |
1274
|
|
|
|
|
|
|
|
1275
|
0
|
|
|
|
|
|
my $trace_flag = ${*$self}{_FTPSSL_arguments}->{trace}; |
|
0
|
|
|
|
|
|
|
1276
|
0
|
0
|
|
|
|
|
print STDERR "$func() trace ." if ($trace_flag); |
1277
|
0
|
|
|
|
|
|
my $cnt = 0; |
1278
|
0
|
|
|
|
|
|
my $prev = ""; |
1279
|
0
|
|
|
|
|
|
my $total = 0; |
1280
|
0
|
|
|
|
|
|
my $len; |
1281
|
|
|
|
|
|
|
|
1282
|
0
|
|
|
|
|
|
while ( ( $len = sysread $io, $data, $size ) ) { |
1283
|
0
|
0
|
|
|
|
|
unless ( defined $len ) { |
1284
|
0
|
0
|
|
|
|
|
next if $! == EINTR; |
1285
|
0
|
0
|
|
|
|
|
close ($localfd) if ( $close_file ); |
1286
|
0
|
|
|
|
|
|
return $self->_croak_or_return (0, "System read error on $func(): $!"); |
1287
|
|
|
|
|
|
|
} |
1288
|
|
|
|
|
|
|
|
1289
|
0
|
0
|
|
|
|
|
if ( $fix_cr_issue ) { |
1290
|
|
|
|
|
|
|
# What if the line only contained \015 ? (^M) |
1291
|
0
|
0
|
|
|
|
|
if ( $data eq "\015" ) { |
1292
|
0
|
|
|
|
|
|
$prev .= "\015"; |
1293
|
0
|
|
|
|
|
|
next; |
1294
|
|
|
|
|
|
|
} |
1295
|
|
|
|
|
|
|
|
1296
|
|
|
|
|
|
|
# What if this line was truncated? (Ends with \015 instead of \015\012) |
1297
|
|
|
|
|
|
|
# Can't test with reg expr since m/(\015)$/s & m/(\015\012)$/s same! |
1298
|
|
|
|
|
|
|
# Don't care if it was truncated anywhere else! |
1299
|
0
|
|
|
|
|
|
my $last_char = substr ($data, -1); |
1300
|
0
|
0
|
|
|
|
|
if ( $last_char eq "\015" ) { |
|
|
0
|
|
|
|
|
|
1301
|
0
|
|
|
|
|
|
$data =~ s/^(.+).$/$prev$1/s; |
1302
|
0
|
|
|
|
|
|
$prev = $last_char; |
1303
|
|
|
|
|
|
|
} |
1304
|
|
|
|
|
|
|
|
1305
|
|
|
|
|
|
|
# What if the previous line was truncated? But not this one. |
1306
|
|
|
|
|
|
|
elsif ( $prev ne "" ) { |
1307
|
0
|
|
|
|
|
|
$data = $prev . $data; |
1308
|
0
|
|
|
|
|
|
$prev = ""; |
1309
|
|
|
|
|
|
|
} |
1310
|
|
|
|
|
|
|
|
1311
|
0
|
|
|
|
|
|
$data =~ s/\015\012/\n/g; |
1312
|
0
|
|
|
|
|
|
$len = length ($data); |
1313
|
|
|
|
|
|
|
} |
1314
|
|
|
|
|
|
|
|
1315
|
0
|
0
|
0
|
|
|
|
print STDERR "." if ($trace_flag && ($cnt % TRACE_MOD) == 0); |
1316
|
0
|
|
|
|
|
|
++$cnt; |
1317
|
|
|
|
|
|
|
|
1318
|
0
|
|
|
|
|
|
$total = $self->_call_callback ($cb_idx, \$data, \$len, $total); |
1319
|
|
|
|
|
|
|
|
1320
|
0
|
0
|
|
|
|
|
if ( $len > 0 ) { |
1321
|
0
|
|
|
|
|
|
$written = syswrite $localfd, $data, $len; |
1322
|
0
|
0
|
|
|
|
|
return $self->_croak_or_return (0, "System write error on $func(): $!") |
1323
|
|
|
|
|
|
|
unless (defined $written); |
1324
|
|
|
|
|
|
|
} |
1325
|
|
|
|
|
|
|
} |
1326
|
|
|
|
|
|
|
|
1327
|
|
|
|
|
|
|
# Potentially write a last ASCII char to the file ... |
1328
|
0
|
0
|
|
|
|
|
if ($prev ne "") { |
1329
|
0
|
|
|
|
|
|
$len = length ($prev); |
1330
|
0
|
|
|
|
|
|
$total = $self->_call_callback ($cb_idx, \$prev, \$len, $total); |
1331
|
0
|
0
|
|
|
|
|
if ( $len > 0 ) { |
1332
|
0
|
|
|
|
|
|
$written = syswrite $localfd, $prev, $len; |
1333
|
0
|
0
|
|
|
|
|
return $self->_croak_or_return (0, "System write error on $func(prev): $!") |
1334
|
|
|
|
|
|
|
unless (defined $written); |
1335
|
|
|
|
|
|
|
} |
1336
|
|
|
|
|
|
|
} |
1337
|
|
|
|
|
|
|
|
1338
|
|
|
|
|
|
|
# Process trailing "callback" info if returned. |
1339
|
0
|
|
|
|
|
|
my $trail; |
1340
|
0
|
|
|
|
|
|
($trail, $len, $total) = $self->_end_callback ($cb_idx, $total); |
1341
|
0
|
0
|
|
|
|
|
if ( $trail ) { |
1342
|
0
|
|
|
|
|
|
$written = syswrite $localfd, $trail, $len; |
1343
|
0
|
0
|
|
|
|
|
return $self->_croak_or_return (0, "System write error on $func(trail): $!") |
1344
|
|
|
|
|
|
|
unless (defined $written); |
1345
|
|
|
|
|
|
|
} |
1346
|
|
|
|
|
|
|
|
1347
|
0
|
0
|
|
|
|
|
print STDERR ". done! (" . $self->_fmt_num ($total) . " byte(s))\n" if ($trace_flag); |
1348
|
|
|
|
|
|
|
|
1349
|
0
|
|
|
|
|
|
_my_close ($io); # Old way $io->close(); |
1350
|
|
|
|
|
|
|
|
1351
|
|
|
|
|
|
|
# To catch the expected "226 Closing data connection." |
1352
|
0
|
0
|
|
|
|
|
if ( $self->response() != CMD_OK ) { |
1353
|
0
|
0
|
|
|
|
|
close ($localfd) if ( $close_file ); |
1354
|
0
|
|
|
|
|
|
return $self->_croak_or_return (); |
1355
|
|
|
|
|
|
|
} |
1356
|
|
|
|
|
|
|
|
1357
|
0
|
0
|
|
|
|
|
if ( $close_file ) { |
1358
|
0
|
|
|
|
|
|
close ($localfd); |
1359
|
0
|
0
|
|
|
|
|
if ( ${*$self}{_FTPSSL_arguments}->{FixGetTs} ) { |
|
0
|
|
|
|
|
|
|
1360
|
0
|
|
|
|
|
|
my $tm = $self->_mdtm ( $file_rem ); |
1361
|
0
|
0
|
|
|
|
|
utime ( $tm, $tm, $file_loc ) if ( $tm ); |
1362
|
|
|
|
|
|
|
} |
1363
|
|
|
|
|
|
|
} |
1364
|
|
|
|
|
|
|
|
1365
|
0
|
|
|
|
|
|
return 1; |
1366
|
|
|
|
|
|
|
} |
1367
|
|
|
|
|
|
|
|
1368
|
|
|
|
|
|
|
|
1369
|
|
|
|
|
|
|
sub put { # Regular put (STOR command) |
1370
|
0
|
|
|
0
|
1
|
|
my $self = shift; |
1371
|
0
|
|
|
|
|
|
my ($resp, $msg1, $msg2, $requested_file_name, $tm) = $self->_common_put (@_); |
1372
|
|
|
|
|
|
|
|
1373
|
0
|
0
|
0
|
|
|
|
if ( $resp && ${*$self}{_FTPSSL_arguments}->{FixPutTs} && defined $tm ) { |
|
0
|
|
0
|
|
|
|
|
1374
|
0
|
|
|
|
|
|
$self->_mfmt ($tm, $requested_file_name); |
1375
|
|
|
|
|
|
|
} |
1376
|
|
|
|
|
|
|
|
1377
|
0
|
|
|
|
|
|
return ( $resp ); |
1378
|
|
|
|
|
|
|
} |
1379
|
|
|
|
|
|
|
|
1380
|
|
|
|
|
|
|
sub append { # Append put (APPE command) |
1381
|
0
|
|
|
0
|
1
|
|
my $self = shift; |
1382
|
0
|
|
|
|
|
|
my ($resp, $msg1, $msg2, $requested_file_name, $tm) = $self->_common_put (@_); |
1383
|
|
|
|
|
|
|
|
1384
|
0
|
0
|
0
|
|
|
|
if ( $resp && ${*$self}{_FTPSSL_arguments}->{FixPutTs} && defined $tm ) { |
|
0
|
|
0
|
|
|
|
|
1385
|
0
|
|
|
|
|
|
$self->_mfmt ($tm, $requested_file_name); |
1386
|
|
|
|
|
|
|
} |
1387
|
|
|
|
|
|
|
|
1388
|
0
|
|
|
|
|
|
return ( $resp ); |
1389
|
|
|
|
|
|
|
} |
1390
|
|
|
|
|
|
|
|
1391
|
|
|
|
|
|
|
sub uput { # Unique put (STOU command) |
1392
|
0
|
|
|
0
|
1
|
|
my $self = shift; |
1393
|
0
|
|
|
|
|
|
my ($resp, $msg1, $msg2, $requested_file_name, $tm) = $self->_common_put (@_); |
1394
|
|
|
|
|
|
|
|
1395
|
|
|
|
|
|
|
# Now lets get the real name of the file uploaded! |
1396
|
0
|
0
|
|
|
|
|
if ( $resp ) { |
1397
|
|
|
|
|
|
|
# The file name may appear in either message returned. (The 150 or 226 msg) |
1398
|
|
|
|
|
|
|
# So lets check both messages merged together! |
1399
|
|
|
|
|
|
|
# Assumes no spaces are in the new file's name! |
1400
|
0
|
|
|
|
|
|
my $msg = $msg1 . "\n" . $msg2; |
1401
|
|
|
|
|
|
|
|
1402
|
0
|
0
|
|
|
|
|
if ( $msg =~ m/(FILE|name):\s*([^\s)]+)($|[\s)])/im ) { |
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
1403
|
0
|
|
|
|
|
|
$requested_file_name = $2; # We found an actual name to use ... |
1404
|
|
|
|
|
|
|
|
1405
|
|
|
|
|
|
|
} elsif ( $msg =~ m/Transfer starting for\s+([^\s]+)($|\s)/im ) { |
1406
|
0
|
|
|
|
|
|
$requested_file_name = $1; # We found an actual name to use ... |
1407
|
0
|
|
|
|
|
|
$requested_file_name =~ s/[.]$//; # Remove optional trailing ".". |
1408
|
|
|
|
|
|
|
|
1409
|
0
|
|
|
|
|
|
} elsif ( ${*$self}{_FTPSSL_arguments}->{uput} == 1 ) { |
1410
|
|
|
|
|
|
|
# The alternate STOU command format was used where the remote |
1411
|
|
|
|
|
|
|
# ftps server won't allow us to recomend any hints! |
1412
|
|
|
|
|
|
|
# So we don't know what the remote server used for a filename |
1413
|
|
|
|
|
|
|
# if it didn't appear in either of the message formats! |
1414
|
0
|
|
|
|
|
|
$requested_file_name = "?"; |
1415
|
|
|
|
|
|
|
} |
1416
|
|
|
|
|
|
|
|
1417
|
|
|
|
|
|
|
# TODO: Figure out other uput variants to check for besides the ones above. |
1418
|
|
|
|
|
|
|
|
1419
|
|
|
|
|
|
|
# Until then, if we can't find the file name used in the messages, |
1420
|
|
|
|
|
|
|
# we'll just have to assume that the default file name was used if |
1421
|
|
|
|
|
|
|
# we were not explicitly told it wasn't being used! |
1422
|
|
|
|
|
|
|
|
1423
|
0
|
0
|
|
|
|
|
if ( $requested_file_name ne "?" ) { |
1424
|
|
|
|
|
|
|
# Now lets update the timestamp for that file on the server ... |
1425
|
|
|
|
|
|
|
# It's allowed to fail since we are not 100% sure of the remote name used! |
1426
|
0
|
0
|
0
|
|
|
|
if ( ${*$self}{_FTPSSL_arguments}->{FixPutTs} && defined $tm ) { |
|
0
|
|
|
|
|
|
|
1427
|
0
|
|
|
|
|
|
$self->_mfmt ($tm, $requested_file_name); |
1428
|
|
|
|
|
|
|
} |
1429
|
|
|
|
|
|
|
|
1430
|
|
|
|
|
|
|
# Fix done in v0.25 |
1431
|
|
|
|
|
|
|
# Some servers returned the full path to the file. But that sometimes |
1432
|
|
|
|
|
|
|
# causes issues. So always strip off the path information. If there |
1433
|
|
|
|
|
|
|
# was a path in the source file, then the caller knows where it was! |
1434
|
0
|
|
|
|
|
|
$requested_file_name = basename ($requested_file_name); |
1435
|
|
|
|
|
|
|
} |
1436
|
|
|
|
|
|
|
|
1437
|
0
|
|
|
|
|
|
return ( $requested_file_name ); |
1438
|
|
|
|
|
|
|
} |
1439
|
|
|
|
|
|
|
|
1440
|
0
|
|
|
|
|
|
return ( undef ); # Fatal error & Croak is turned off. |
1441
|
|
|
|
|
|
|
} |
1442
|
|
|
|
|
|
|
|
1443
|
|
|
|
|
|
|
# Makes sure the scratch file name generated appears in the same directory as |
1444
|
|
|
|
|
|
|
# the real file unless you provide a prefix with a directory as part of it. |
1445
|
|
|
|
|
|
|
sub _get_scratch_file { |
1446
|
0
|
|
|
0
|
|
|
my $self = shift; |
1447
|
0
|
|
|
|
|
|
my $prefix = shift; # May include a path |
1448
|
0
|
|
|
|
|
|
my $body = shift; |
1449
|
0
|
|
|
|
|
|
my $postfix = shift; |
1450
|
0
|
|
|
|
|
|
my $file = shift; # The final file name to use (may include a path) |
1451
|
|
|
|
|
|
|
|
1452
|
|
|
|
|
|
|
# So we don't override "", which is OK for these 2 parts. |
1453
|
0
|
0
|
|
|
|
|
$prefix = "_tmp." unless ( defined $prefix ); |
1454
|
0
|
0
|
|
|
|
|
$postfix = ".tmp" unless ( defined $postfix ); |
1455
|
|
|
|
|
|
|
|
1456
|
|
|
|
|
|
|
# Determine if we need to parse by OS or FTP path rules ... (get vs put) |
1457
|
|
|
|
|
|
|
# And get default body to use if none was supplied or it's ""! |
1458
|
0
|
|
|
|
|
|
my $c = (caller(1))[3]; |
1459
|
0
|
|
|
|
|
|
my $os; |
1460
|
0
|
0
|
0
|
|
|
|
if ( defined $c && |
|
|
|
0
|
|
|
|
|
1461
|
|
|
|
|
|
|
( $c eq "Net::FTPSSL::xput" || $c eq "Net::FTPSSL::xtransfer" ) ) { |
1462
|
0
|
|
|
|
|
|
$os = fileparse_set_fstype ("FTP"); # Follow Unix instead of OS rules. |
1463
|
|
|
|
|
|
|
# Client Name + process PID ... Unique on remote server ... |
1464
|
0
|
|
0
|
|
|
|
$body = $body || (hostname () . ".$$"); |
1465
|
|
|
|
|
|
|
} else { |
1466
|
0
|
|
|
|
|
|
$os = fileparse_set_fstype (); # Follow local OS rules. |
1467
|
|
|
|
|
|
|
# reverse(Client Name) + process PID ... Unique on local server ... |
1468
|
0
|
|
0
|
|
|
|
$body = $body || (reverse (hostname ()) . ".$$"); |
1469
|
|
|
|
|
|
|
} |
1470
|
|
|
|
|
|
|
|
1471
|
|
|
|
|
|
|
# Makes sure the scratch file and the final file will appear in the same |
1472
|
|
|
|
|
|
|
# directory unless the user overrides the directory as part of the prefix! |
1473
|
0
|
|
|
|
|
|
my ($base, $dir, $type) = fileparse ($file); |
1474
|
0
|
0
|
|
|
|
|
if ( $base ne $file ) { |
1475
|
|
|
|
|
|
|
# The file is not in the current direcory ... |
1476
|
0
|
|
|
|
|
|
my ($pbase, $pdir, $ptype) = fileparse ($prefix); |
1477
|
0
|
0
|
|
|
|
|
if ( $pbase eq $prefix ) { |
1478
|
|
|
|
|
|
|
# Prefix has no path, so put it in the file's directory! |
1479
|
0
|
|
|
|
|
|
$prefix = $dir . $prefix; |
1480
|
|
|
|
|
|
|
} |
1481
|
|
|
|
|
|
|
} |
1482
|
|
|
|
|
|
|
|
1483
|
|
|
|
|
|
|
# Return to the previously remembered OS rules again! (Avoids side affects!) |
1484
|
0
|
|
|
|
|
|
fileparse_set_fstype ($os); |
1485
|
|
|
|
|
|
|
|
1486
|
0
|
|
|
|
|
|
my $scratch_name = $prefix . $body . $postfix; |
1487
|
|
|
|
|
|
|
|
1488
|
0
|
0
|
|
|
|
|
if ( $scratch_name eq $file ) { |
1489
|
0
|
|
|
|
|
|
return $self->_croak_or_return (0, "The scratch name and final name are the same! ($file) It's required that they must be different!" ); |
1490
|
|
|
|
|
|
|
} |
1491
|
|
|
|
|
|
|
|
1492
|
0
|
|
|
|
|
|
return ( $scratch_name ); |
1493
|
|
|
|
|
|
|
} |
1494
|
|
|
|
|
|
|
|
1495
|
|
|
|
|
|
|
sub xput { # A variant of the regular put (STOR command) |
1496
|
0
|
|
|
0
|
1
|
|
my $self = shift; |
1497
|
0
|
|
|
|
|
|
my $file_loc = shift; |
1498
|
0
|
|
|
|
|
|
my $file_rem = shift; |
1499
|
|
|
|
|
|
|
|
1500
|
|
|
|
|
|
|
# See _get_scratch_file() for the default values if undef! |
1501
|
0
|
|
|
|
|
|
my ($prefix, $postfix, $body) = (shift, shift, shift); |
1502
|
|
|
|
|
|
|
|
1503
|
0
|
0
|
|
|
|
|
unless ($file_rem) { |
1504
|
0
|
0
|
|
|
|
|
if ( $self->_isa_glob ($file_loc) ) { |
1505
|
0
|
|
|
|
|
|
return $self->_croak_or_return (0, "When you pass a stream, you must specify the remote filename."); |
1506
|
|
|
|
|
|
|
} |
1507
|
|
|
|
|
|
|
|
1508
|
0
|
|
|
|
|
|
$file_rem = basename ($file_loc); |
1509
|
|
|
|
|
|
|
} |
1510
|
|
|
|
|
|
|
|
1511
|
0
|
|
|
|
|
|
my $scratch_name = $self->_get_scratch_file ($prefix, $body, $postfix, |
1512
|
|
|
|
|
|
|
$file_rem); |
1513
|
0
|
0
|
|
|
|
|
return undef unless ($scratch_name); |
1514
|
|
|
|
|
|
|
|
1515
|
0
|
0
|
|
|
|
|
unless ( $self->all_supported ( "STOR", "DELE", "RNFR", "RNTO" ) ) { |
1516
|
0
|
|
|
|
|
|
return $self->_croak_or_return (0, "Function xput is not supported by this server."); |
1517
|
|
|
|
|
|
|
} |
1518
|
|
|
|
|
|
|
|
1519
|
|
|
|
|
|
|
# Now lets send the file. Make sure we can't die during this process ... |
1520
|
0
|
|
|
|
|
|
my $die = ${*$self}{_FTPSSL_arguments}->{Croak}; |
|
0
|
|
|
|
|
|
|
1521
|
0
|
|
|
|
|
|
${*$self}{_FTPSSL_arguments}->{Croak} = 0; |
|
0
|
|
|
|
|
|
|
1522
|
|
|
|
|
|
|
|
1523
|
0
|
|
|
|
|
|
my ($resp, $msg1, $msg2, $requested_file_name, $tm) = |
1524
|
|
|
|
|
|
|
$self->_common_put ($file_loc, $scratch_name); |
1525
|
|
|
|
|
|
|
|
1526
|
0
|
0
|
|
|
|
|
if ( $resp ) { |
1527
|
0
|
|
|
|
|
|
$self->_xWait (); # Some servers require a wait before you may move on! |
1528
|
|
|
|
|
|
|
|
1529
|
|
|
|
|
|
|
# Delete any file sitting on the server with the final name we want to use |
1530
|
|
|
|
|
|
|
# to avoid file permission issues. Usually the file won't exist so the |
1531
|
|
|
|
|
|
|
# delete will fail ... |
1532
|
0
|
|
|
|
|
|
$self->delete ( $file_rem ); |
1533
|
|
|
|
|
|
|
|
1534
|
|
|
|
|
|
|
# Now lets make it visible to the file recognizer ... |
1535
|
0
|
|
|
|
|
|
$resp = $self->rename ( $requested_file_name, $file_rem ); |
1536
|
|
|
|
|
|
|
|
1537
|
|
|
|
|
|
|
# Now lets update the timestamp for the file on the server ... |
1538
|
|
|
|
|
|
|
# It's not an error if the file recognizer grabs it before the |
1539
|
|
|
|
|
|
|
# timestamp is reset ... |
1540
|
0
|
0
|
0
|
|
|
|
if ( $resp && ${*$self}{_FTPSSL_arguments}->{FixPutTs} && defined $tm ) { |
|
0
|
|
0
|
|
|
|
|
1541
|
0
|
|
|
|
|
|
$self->_mfmt ($tm, $file_rem); |
1542
|
|
|
|
|
|
|
} |
1543
|
|
|
|
|
|
|
} |
1544
|
|
|
|
|
|
|
|
1545
|
|
|
|
|
|
|
# Delete the scratch file on error, but don't return this as the error msg. |
1546
|
|
|
|
|
|
|
# We want the actual error encounterd from the put or rename commands! |
1547
|
0
|
0
|
|
|
|
|
unless ($resp) { |
1548
|
0
|
|
|
|
|
|
$msg1 = ${*$self}{_FTPSSL_arguments}->{last_ftp_msg}; |
|
0
|
|
|
|
|
|
|
1549
|
0
|
|
|
|
|
|
$self->delete ( $scratch_name ); |
1550
|
0
|
|
|
|
|
|
${*$self}{_FTPSSL_arguments}->{last_ftp_msg} = $msg1; |
|
0
|
|
|
|
|
|
|
1551
|
|
|
|
|
|
|
} |
1552
|
|
|
|
|
|
|
|
1553
|
|
|
|
|
|
|
# Now allow us to die again if we must ... |
1554
|
0
|
|
|
|
|
|
${*$self}{_FTPSSL_arguments}->{Croak} = $die; |
|
0
|
|
|
|
|
|
|
1555
|
|
|
|
|
|
|
|
1556
|
0
|
|
|
|
|
|
return ( $self->_test_croak ( $resp ) ); |
1557
|
|
|
|
|
|
|
} |
1558
|
|
|
|
|
|
|
|
1559
|
|
|
|
|
|
|
sub xget { # A variant of the regular get (RETR command) |
1560
|
0
|
|
|
0
|
1
|
|
my $self = shift; |
1561
|
0
|
|
|
|
|
|
my $file_rem = shift; |
1562
|
0
|
|
|
|
|
|
my $file_loc = shift; |
1563
|
|
|
|
|
|
|
|
1564
|
|
|
|
|
|
|
# See _get_scratch_file() for the default values if undef! |
1565
|
0
|
|
|
|
|
|
my ($prefix, $postfix, $body) = (shift, shift, shift); |
1566
|
|
|
|
|
|
|
|
1567
|
0
|
0
|
|
|
|
|
unless ( $file_loc ) { |
1568
|
0
|
|
|
|
|
|
$file_loc = basename ($file_rem); |
1569
|
|
|
|
|
|
|
} |
1570
|
|
|
|
|
|
|
|
1571
|
0
|
0
|
|
|
|
|
if ( $self->_isa_glob ($file_loc) ) { |
1572
|
0
|
|
|
|
|
|
return $self->_croak_or_return (0, "xget doesn't support file_loc being an open file handle."); |
1573
|
|
|
|
|
|
|
} |
1574
|
|
|
|
|
|
|
|
1575
|
0
|
|
|
|
|
|
my $scratch_name = $self->_get_scratch_file ( $prefix, $body, $postfix, |
1576
|
|
|
|
|
|
|
$file_loc ); |
1577
|
0
|
0
|
|
|
|
|
return undef unless ($scratch_name); |
1578
|
|
|
|
|
|
|
|
1579
|
0
|
0
|
|
|
|
|
if (defined ${*$self}{_FTPSSL_arguments}->{net_ftpssl_rest_offset}) { |
|
0
|
|
|
|
|
|
|
1580
|
0
|
|
|
|
|
|
return $self->_croak_or_return (0, "Can't call restart() before xget()!"); |
1581
|
|
|
|
|
|
|
} |
1582
|
|
|
|
|
|
|
|
1583
|
|
|
|
|
|
|
# In this case, we can die if we must, no required post work here ... |
1584
|
0
|
|
|
|
|
|
my $resp = $self->get ( $file_rem, $scratch_name, undef ); |
1585
|
|
|
|
|
|
|
|
1586
|
|
|
|
|
|
|
# Make it visisble to the local file recognizer on success ... |
1587
|
0
|
0
|
|
|
|
|
if ( $resp ) { |
1588
|
0
|
|
|
|
|
|
$self->_print_DBG ( "<<+ renamed $scratch_name to $file_loc\n" ); |
1589
|
0
|
|
|
|
|
|
unlink ( $file_loc ); # To avoid potential permission issues ... |
1590
|
0
|
0
|
|
|
|
|
move ( $scratch_name, $file_loc ) or |
1591
|
|
|
|
|
|
|
return $self->_croak_or_return (0, "Can't rename the local scratch file!"); |
1592
|
|
|
|
|
|
|
} |
1593
|
|
|
|
|
|
|
|
1594
|
0
|
|
|
|
|
|
return ( $self->_test_croak ( $resp ) ); |
1595
|
|
|
|
|
|
|
} |
1596
|
|
|
|
|
|
|
|
1597
|
|
|
|
|
|
|
# Doesn't do the CF/LF transformation. |
1598
|
|
|
|
|
|
|
# It lets the source & dest servers do it if it's necessary! |
1599
|
|
|
|
|
|
|
# Please note that $self & $dest_ftp will write to different log files! |
1600
|
|
|
|
|
|
|
sub transfer { |
1601
|
0
|
|
|
0
|
1
|
|
my $self = shift; |
1602
|
0
|
|
|
|
|
|
my $dest_ftp = shift; # A Net::FTPSSL object. |
1603
|
0
|
|
0
|
|
|
|
my $remote_file = shift || ""; |
1604
|
0
|
|
0
|
|
|
|
my $dest_file = shift || $remote_file; |
1605
|
0
|
|
0
|
|
|
|
my $offset = shift || ${*$self}{_FTPSSL_arguments}->{net_ftpssl_rest_offset} || 0; |
1606
|
|
|
|
|
|
|
|
1607
|
|
|
|
|
|
|
# Verify we are dealing with a Net::FTPSSL object ... |
1608
|
0
|
0
|
0
|
|
|
|
if ( ref($dest_ftp) eq "" || ref($dest_ftp) ne __PACKAGE__ ) { |
1609
|
0
|
|
|
|
|
|
return $self->_croak_or_return(0, "The destination server must be a valid Net::FTPSSL object! (" . ref($dest_ftp) . ")"); |
1610
|
|
|
|
|
|
|
} |
1611
|
|
|
|
|
|
|
|
1612
|
0
|
|
|
|
|
|
my $sArg = ${*$self}{_FTPSSL_arguments}; |
|
0
|
|
|
|
|
|
|
1613
|
0
|
|
|
|
|
|
my $dArg = ${*$dest_ftp}{_FTPSSL_arguments}; |
|
0
|
|
|
|
|
|
|
1614
|
|
|
|
|
|
|
|
1615
|
|
|
|
|
|
|
# Clear out this messy restart() cluge for next time ... |
1616
|
0
|
|
|
|
|
|
delete ( $sArg->{net_ftpssl_rest_offset} ); |
1617
|
|
|
|
|
|
|
|
1618
|
|
|
|
|
|
|
# Don't care if this value was set or not. Just remove it! |
1619
|
|
|
|
|
|
|
# We just use any offset from ${*$self} instead ... |
1620
|
0
|
|
|
|
|
|
delete ( $dArg->{net_ftpssl_rest_offset} ); |
1621
|
|
|
|
|
|
|
|
1622
|
0
|
|
0
|
|
|
|
my ($stmp, $dtmp) = ($sArg->{Croak} || 0, $dArg->{Croak} || 0); |
|
|
|
0
|
|
|
|
|
1623
|
0
|
0
|
|
|
|
|
if ( $stmp != $dtmp ) { |
1624
|
0
|
|
|
|
|
|
my $msg = "Both connections must use the same Croak Settings for the transfer!"; |
1625
|
0
|
|
|
|
|
|
$msg .= " (${stmp} vs ${dtmp})"; |
1626
|
0
|
|
|
|
|
|
$dest_ftp->_print_DBG ("<<+ 555 $msg\n"); |
1627
|
0
|
|
|
|
|
|
return $self->_croak_or_return (0, $msg); |
1628
|
|
|
|
|
|
|
} |
1629
|
|
|
|
|
|
|
|
1630
|
0
|
|
|
|
|
|
($stmp, $dtmp) = ($sArg->{type}, $dArg->{type}); |
1631
|
0
|
0
|
|
|
|
|
if ( $stmp ne $dtmp ) { |
1632
|
0
|
|
|
|
|
|
my $msg = "Both connections must use ASCII or BIN for the transfer!"; |
1633
|
0
|
|
|
|
|
|
$msg .= " (${stmp} vs ${dtmp})"; |
1634
|
0
|
|
|
|
|
|
$dest_ftp->_print_DBG ("<<+ 555 $msg\n"); |
1635
|
0
|
|
|
|
|
|
return $self->_croak_or_return(0, $msg); |
1636
|
|
|
|
|
|
|
} |
1637
|
|
|
|
|
|
|
|
1638
|
0
|
|
0
|
|
|
|
my $size = $sArg->{buf_size} || 2048; |
1639
|
|
|
|
|
|
|
|
1640
|
|
|
|
|
|
|
# Validate the remaining arguments ... |
1641
|
0
|
0
|
0
|
|
|
|
if ( ref($remote_file) || $remote_file eq "" ) { |
1642
|
0
|
|
|
|
|
|
return $self->_croak_or_return(0, "The remote file must be a file name!"); |
1643
|
|
|
|
|
|
|
} |
1644
|
0
|
0
|
0
|
|
|
|
if ( ref($dest_file) || $dest_file eq "" ) { |
1645
|
0
|
|
|
|
|
|
return $self->_croak_or_return(0, "The destination file must be a file name!"); |
1646
|
|
|
|
|
|
|
} |
1647
|
0
|
0
|
|
|
|
|
if ( $offset < -1 ) { |
1648
|
0
|
|
|
|
|
|
return $self->_croak_or_return(0, "Invalid file offset ($offset)!"); |
1649
|
|
|
|
|
|
|
} |
1650
|
|
|
|
|
|
|
|
1651
|
|
|
|
|
|
|
# "(caller(1))[3]" returns undef if not called by another Net::FTPSSL method! |
1652
|
0
|
|
|
|
|
|
my $c = (caller(1))[3]; |
1653
|
0
|
0
|
0
|
|
|
|
my $cb_idx = ( defined $c && $c eq "Net::FTPSSL::xtransfer" ) ? 2 : 1; |
1654
|
0
|
0
|
|
|
|
|
my $func = ( $cb_idx == 1 ) ? "transfer" : "xtransfer"; |
1655
|
0
|
0
|
|
|
|
|
my $func2 = ( $cb_idx == 1 ) ? "Transfer" : "xTransfer"; |
1656
|
|
|
|
|
|
|
|
1657
|
0
|
|
|
|
|
|
$self->_print_DBG ( "+++ Starting $func2 Between Servers +++\n"); |
1658
|
0
|
|
|
|
|
|
$dest_ftp->_print_DBG ( "--- Starting $func2 Between Servers ---\n"); |
1659
|
|
|
|
|
|
|
|
1660
|
|
|
|
|
|
|
# Calculate the file offset to send to the FTPS source server via REST ... |
1661
|
0
|
0
|
|
|
|
|
if ($offset == -1) { |
1662
|
0
|
|
|
|
|
|
$offset = $dest_ftp->size ($dest_file); |
1663
|
0
|
0
|
|
|
|
|
return (undef) unless (defined $offset); |
1664
|
|
|
|
|
|
|
} |
1665
|
|
|
|
|
|
|
|
1666
|
|
|
|
|
|
|
# ------------------------------------------------- |
1667
|
|
|
|
|
|
|
# Set up the transfer destination server ... (put) |
1668
|
|
|
|
|
|
|
# ------------------------------------------------- |
1669
|
0
|
0
|
|
|
|
|
return (undef) unless ( $dest_ftp->prep_data_channel ("STOR", $dest_file) ); |
1670
|
0
|
0
|
|
|
|
|
my $restart = ($offset) ? $dest_ftp->_rest ($offset) : 1; |
1671
|
0
|
|
|
|
|
|
my $response = $dest_ftp->_stor ($dest_file); |
1672
|
0
|
0
|
0
|
|
|
|
unless ($restart && $response) { |
1673
|
0
|
0
|
0
|
|
|
|
$dest_ftp->_rest (0) if ($restart && $offset); |
1674
|
0
|
|
|
|
|
|
return ($dest_ftp->_croak_or_return (), undef, undef, $dest_file, undef); |
1675
|
|
|
|
|
|
|
} |
1676
|
|
|
|
|
|
|
# my $put_msg = $dest_ftp->last_message (); |
1677
|
0
|
|
|
|
|
|
my $dio = $dest_ftp->_get_data_channel (); |
1678
|
0
|
0
|
|
|
|
|
return (undef) unless (defined $dio); |
1679
|
|
|
|
|
|
|
|
1680
|
|
|
|
|
|
|
# ------------------------------------------------- |
1681
|
|
|
|
|
|
|
# Set up the transfer source server ... (get) |
1682
|
|
|
|
|
|
|
# ------------------------------------------------- |
1683
|
0
|
0
|
|
|
|
|
unless ( $self->prep_data_channel( "RETR", $remote_file ) ) { |
1684
|
0
|
|
|
|
|
|
_my_close ($dio); |
1685
|
0
|
|
|
|
|
|
$dest_ftp->response (); |
1686
|
0
|
|
|
|
|
|
return (undef); # Already decided not to call croak if you get here! |
1687
|
|
|
|
|
|
|
} |
1688
|
0
|
0
|
|
|
|
|
my $rest = ($offset) ? $self->_rest ($offset) : 1; |
1689
|
0
|
0
|
0
|
|
|
|
unless ( $rest && $self->_retr ($remote_file) ) { |
1690
|
0
|
0
|
0
|
|
|
|
if ( $offset && $rest ) { |
1691
|
0
|
|
|
|
|
|
my $msg = $self->last_message (); |
1692
|
0
|
|
|
|
|
|
$self->_rest (0); # Must clear out on failure! |
1693
|
0
|
|
|
|
|
|
$sArg->{last_ftp_msg} = $msg; # Restore original error message! |
1694
|
|
|
|
|
|
|
} |
1695
|
0
|
|
|
|
|
|
_my_close ($dio); |
1696
|
0
|
|
|
|
|
|
$dest_ftp->response (); |
1697
|
0
|
|
|
|
|
|
return ($self->_croak_or_return ()); |
1698
|
|
|
|
|
|
|
} |
1699
|
|
|
|
|
|
|
|
1700
|
0
|
|
|
|
|
|
my $sio = $self->_get_data_channel (); |
1701
|
0
|
0
|
|
|
|
|
unless (defined $sio) { |
1702
|
0
|
|
|
|
|
|
_my_close ($dio); |
1703
|
0
|
|
|
|
|
|
$dest_ftp->response (); |
1704
|
|
|
|
|
|
|
return (undef) |
1705
|
0
|
|
|
|
|
|
} |
1706
|
|
|
|
|
|
|
|
1707
|
0
|
|
|
|
|
|
my $trace_flag = $sArg->{trace}; |
1708
|
0
|
0
|
|
|
|
|
print STDERR "$func() trace ." if ($trace_flag); |
1709
|
|
|
|
|
|
|
|
1710
|
0
|
|
|
|
|
|
my ($cnt, $total, $len) = (0, 0, 0); |
1711
|
0
|
|
|
|
|
|
my $data; |
1712
|
|
|
|
|
|
|
my $written; |
1713
|
|
|
|
|
|
|
|
1714
|
|
|
|
|
|
|
# So simple without CR/LF transformations ... |
1715
|
0
|
|
|
|
|
|
while ( $len = sysread ($sio, $data, $size) ) { |
1716
|
0
|
0
|
|
|
|
|
unless ( defined $len ) { |
1717
|
0
|
0
|
|
|
|
|
next if ( $! == EINTR ); |
1718
|
0
|
|
|
|
|
|
_my_close ($dio); |
1719
|
0
|
|
|
|
|
|
$dest_ftp->response (); |
1720
|
0
|
|
|
|
|
|
return $self->_croak_or_return (0, "System read error on $func(): $!"); |
1721
|
|
|
|
|
|
|
} |
1722
|
|
|
|
|
|
|
|
1723
|
0
|
0
|
0
|
|
|
|
print STDERR "." if ($trace_flag && ($cnt % TRACE_MOD) == 0); |
1724
|
0
|
|
|
|
|
|
++$cnt; |
1725
|
|
|
|
|
|
|
|
1726
|
0
|
|
|
|
|
|
$total = $self->_call_callback ($cb_idx, \$data, \$len, $total); |
1727
|
|
|
|
|
|
|
|
1728
|
|
|
|
|
|
|
# Write to the destination server ... |
1729
|
0
|
0
|
|
|
|
|
if ($len > 0) { |
1730
|
0
|
|
|
|
|
|
$written = syswrite ($dio, $data, $len); |
1731
|
0
|
0
|
|
|
|
|
unless (defined $written) { |
1732
|
0
|
|
|
|
|
|
_my_close ($sio); |
1733
|
0
|
|
|
|
|
|
$self->response (); |
1734
|
0
|
|
|
|
|
|
return ($dest_ftp->_croak_or_return (0, "System write error on $func(): $!")); |
1735
|
|
|
|
|
|
|
} |
1736
|
|
|
|
|
|
|
} |
1737
|
|
|
|
|
|
|
} # End while reading from the source server ... |
1738
|
|
|
|
|
|
|
|
1739
|
|
|
|
|
|
|
|
1740
|
|
|
|
|
|
|
# Process trailing "callback" info if returned. |
1741
|
0
|
|
|
|
|
|
my $trail; |
1742
|
0
|
|
|
|
|
|
($trail, $len, $total) = $self->_end_callback ($cb_idx, $total); |
1743
|
|
|
|
|
|
|
|
1744
|
|
|
|
|
|
|
# Write to the destination server ... |
1745
|
0
|
0
|
0
|
|
|
|
if ($trail && $len > 0) { |
1746
|
0
|
|
|
|
|
|
$written = syswrite ($dio, $trail, $len); |
1747
|
0
|
0
|
|
|
|
|
unless (defined $written) { |
1748
|
0
|
|
|
|
|
|
_my_close ($sio); |
1749
|
0
|
|
|
|
|
|
$self->response (); |
1750
|
0
|
|
|
|
|
|
return ($dest_ftp->_croak_or_return (0, "System write error on $func(): $!")); |
1751
|
|
|
|
|
|
|
} |
1752
|
|
|
|
|
|
|
} |
1753
|
|
|
|
|
|
|
|
1754
|
0
|
0
|
|
|
|
|
print STDERR ". done!", $self->_fmt_num ($total) . " byte(s)\n" if ($trace_flag); |
1755
|
|
|
|
|
|
|
|
1756
|
|
|
|
|
|
|
# Lets finish off both connections ... |
1757
|
0
|
|
|
|
|
|
_my_close ($sio); |
1758
|
0
|
|
|
|
|
|
_my_close ($dio); |
1759
|
0
|
|
|
|
|
|
my $resp1 = $self->response (); |
1760
|
0
|
|
|
|
|
|
my $resp2 = $dest_ftp->response (); |
1761
|
|
|
|
|
|
|
|
1762
|
0
|
0
|
0
|
|
|
|
if ($resp1 != CMD_OK || $resp2 != CMD_OK) { |
1763
|
0
|
|
|
|
|
|
return ($self->_croak_or_return ()); |
1764
|
|
|
|
|
|
|
} |
1765
|
|
|
|
|
|
|
|
1766
|
|
|
|
|
|
|
# Preserve the timestamp on the transfered file ... |
1767
|
0
|
0
|
0
|
|
|
|
if ($cb_idx == 1 && $sArg->{FixGetTs} && $dArg->{FixPutTs}) { |
|
|
|
0
|
|
|
|
|
1768
|
0
|
|
|
|
|
|
my $tm = $self->_mdtm ($remote_file); |
1769
|
0
|
|
|
|
|
|
$dest_ftp->_mfmt ($tm, $dest_file); |
1770
|
|
|
|
|
|
|
} |
1771
|
|
|
|
|
|
|
|
1772
|
0
|
|
|
|
|
|
$self->_print_DBG ( "+++ $func2 Between Servers Completed +++\n"); |
1773
|
0
|
|
|
|
|
|
$dest_ftp->_print_DBG ( "--- $func2 Between Servers Completed ---\n"); |
1774
|
|
|
|
|
|
|
|
1775
|
0
|
|
|
|
|
|
return (1); |
1776
|
|
|
|
|
|
|
} |
1777
|
|
|
|
|
|
|
|
1778
|
|
|
|
|
|
|
sub xtransfer { |
1779
|
0
|
|
|
0
|
1
|
|
my $self = shift; |
1780
|
0
|
|
|
|
|
|
my $dest_ftp = shift; # A Net::FTPSSL object. |
1781
|
0
|
|
0
|
|
|
|
my $remote_file = shift || ""; |
1782
|
0
|
|
0
|
|
|
|
my $dest_file = shift || $remote_file; |
1783
|
|
|
|
|
|
|
|
1784
|
|
|
|
|
|
|
# See _get_scratch_file() for default valuies if undef! |
1785
|
0
|
|
|
|
|
|
my ($prefix, $postfix, $body) = (shift, shift, shift); |
1786
|
|
|
|
|
|
|
|
1787
|
0
|
0
|
0
|
|
|
|
if ( ref($dest_ftp) eq "" || ref($dest_ftp) ne __PACKAGE__ ) { |
1788
|
0
|
|
|
|
|
|
return $self->_croak_or_return(0, "The destination server must be a valid Net::FTPSSL object! (" . ref($dest_ftp) . ")"); |
1789
|
|
|
|
|
|
|
} |
1790
|
|
|
|
|
|
|
|
1791
|
0
|
|
|
|
|
|
my $sArg = ${*$self}{_FTPSSL_arguments}; |
|
0
|
|
|
|
|
|
|
1792
|
0
|
|
|
|
|
|
my $dArg = ${*$dest_ftp}{_FTPSSL_arguments}; |
|
0
|
|
|
|
|
|
|
1793
|
|
|
|
|
|
|
|
1794
|
0
|
0
|
|
|
|
|
if (defined $sArg->{net_ftpssl_rest_offset}) { |
1795
|
0
|
|
|
|
|
|
return $self->_croak_or_return (0, "Can't call restart() before xtransfer()!"); |
1796
|
|
|
|
|
|
|
} |
1797
|
0
|
0
|
|
|
|
|
if (defined $dArg->{net_ftpssl_rest_offset}) { |
1798
|
0
|
|
|
|
|
|
return $dest_ftp->_croak_or_return (0, "Can't call restart() before xtransfer()!"); |
1799
|
|
|
|
|
|
|
} |
1800
|
|
|
|
|
|
|
|
1801
|
0
|
0
|
|
|
|
|
if ( $self->_isa_glob ($remote_file) ) { |
1802
|
0
|
|
|
|
|
|
return $self->_croak_or_return (0, "xtransfer doesn't support REMOTE_FILE being an open file handle."); |
1803
|
|
|
|
|
|
|
} |
1804
|
0
|
0
|
|
|
|
|
if ( $self->_isa_glob ($dest_file) ) { |
1805
|
0
|
|
|
|
|
|
return $self->_croak_or_return (0, "xtransfer doesn't support DEST_FILE being an open file handle."); |
1806
|
|
|
|
|
|
|
} |
1807
|
|
|
|
|
|
|
|
1808
|
|
|
|
|
|
|
# Check if allowed on the destination server ... |
1809
|
0
|
0
|
|
|
|
|
unless ( $dest_ftp->all_supported ( "STOR", "DELE", "RNFR", "RNTO" ) ) { |
1810
|
0
|
|
|
|
|
|
return $dest_ftp->_croak_or_return (0, "Function xtransfer is not supported by the destination server."); |
1811
|
|
|
|
|
|
|
} |
1812
|
|
|
|
|
|
|
|
1813
|
0
|
|
|
|
|
|
my $scratch_name = $self->_get_scratch_file ( $prefix, $body, $postfix, |
1814
|
|
|
|
|
|
|
$dest_file ); |
1815
|
0
|
0
|
|
|
|
|
return undef unless ($scratch_name); |
1816
|
|
|
|
|
|
|
|
1817
|
|
|
|
|
|
|
# Save the current die settings for both servers ... |
1818
|
0
|
|
0
|
|
|
|
my ($sdie, $ddie) = ($sArg->{Croak} || 0, $dArg->{Croak} || 0); |
|
|
|
0
|
|
|
|
|
1819
|
0
|
0
|
|
|
|
|
if ( $sdie != $ddie ) { |
1820
|
0
|
|
|
|
|
|
return $self->_croak_or_return (0, "xtransfer requires the Croak setting to be the same on both servers (${sdie} vs ${ddie})"); |
1821
|
|
|
|
|
|
|
} |
1822
|
|
|
|
|
|
|
|
1823
|
|
|
|
|
|
|
# Disable calling "die" on errors ... (save the current Croak setting again) |
1824
|
0
|
|
|
|
|
|
($sdie, $ddie) = ($sArg->{Croak}, $dArg->{Croak}); |
1825
|
0
|
|
|
|
|
|
(${*$self}{_FTPSSL_arguments}->{Croak}, ${*$dest_ftp}{_FTPSSL_arguments}->{Croak}) = (0, 0); |
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
1826
|
|
|
|
|
|
|
|
1827
|
|
|
|
|
|
|
# Now lets send the file, we can no longer die during this process ... |
1828
|
0
|
|
|
|
|
|
my $resp = $self->transfer ($dest_ftp, $remote_file, $scratch_name, undef); |
1829
|
|
|
|
|
|
|
|
1830
|
0
|
0
|
|
|
|
|
if ( $resp ) { |
1831
|
0
|
|
|
|
|
|
$dest_ftp->_xWait (); # Some servers require a wait before moving on! |
1832
|
|
|
|
|
|
|
|
1833
|
|
|
|
|
|
|
# Delete any file sitting on the server with the final name we want to use |
1834
|
|
|
|
|
|
|
# to avoid file permission issues. Usually the file won't exist so the |
1835
|
|
|
|
|
|
|
# delete will fail ... |
1836
|
0
|
|
|
|
|
|
$dest_ftp->delete ( $dest_file ); |
1837
|
|
|
|
|
|
|
|
1838
|
|
|
|
|
|
|
# Now lets make it visible to the file recognizer ... |
1839
|
0
|
|
|
|
|
|
$resp = $dest_ftp->rename ( $scratch_name, $dest_file ); |
1840
|
|
|
|
|
|
|
|
1841
|
|
|
|
|
|
|
# Now lets update the timestamp for the file on the dest server ... |
1842
|
|
|
|
|
|
|
# It's not an error if the file recognizer grabs it before the |
1843
|
|
|
|
|
|
|
# timestamp is reset ... |
1844
|
0
|
0
|
0
|
|
|
|
if ($resp && $sArg->{FixGetTs} && $dArg->{FixPutTs}) { |
|
|
|
0
|
|
|
|
|
1845
|
0
|
|
|
|
|
|
my $tm = $self->_mdtm ($remote_file); |
1846
|
0
|
|
|
|
|
|
$dest_ftp->_mfmt ($tm, $dest_file); |
1847
|
|
|
|
|
|
|
} |
1848
|
|
|
|
|
|
|
} |
1849
|
|
|
|
|
|
|
|
1850
|
|
|
|
|
|
|
# Delete the scratch file on error, but don't return this as the error msg. |
1851
|
|
|
|
|
|
|
# We want the actual error encounterd from the put or rename commands! |
1852
|
0
|
0
|
|
|
|
|
unless ($resp) { |
1853
|
0
|
|
|
|
|
|
my $msg1 = $dArg->{last_ftp_msg}; |
1854
|
0
|
|
|
|
|
|
$dest_ftp->delete ( $scratch_name ); |
1855
|
0
|
|
|
|
|
|
$dArg->{last_ftp_msg} = $msg1; |
1856
|
|
|
|
|
|
|
} |
1857
|
|
|
|
|
|
|
|
1858
|
|
|
|
|
|
|
# Now allow us to die again if we must ... |
1859
|
0
|
|
|
|
|
|
($sArg->{Croak}, $dArg->{Croak}) = ($sdie, $ddie); |
1860
|
|
|
|
|
|
|
|
1861
|
0
|
|
|
|
|
|
return ( $self->_test_croak ( $resp ) ); |
1862
|
|
|
|
|
|
|
} |
1863
|
|
|
|
|
|
|
|
1864
|
|
|
|
|
|
|
sub _put_offset_fix { |
1865
|
0
|
|
|
0
|
|
|
my $self = shift; |
1866
|
0
|
|
|
|
|
|
my $offset = shift; |
1867
|
0
|
|
|
|
|
|
my $len = shift; |
1868
|
0
|
|
|
|
|
|
my $data = shift; |
1869
|
|
|
|
|
|
|
|
1870
|
|
|
|
|
|
|
# Determine if we can send any of this data to the server ... |
1871
|
0
|
0
|
|
|
|
|
if ( $offset >= $len ) { |
|
|
0
|
|
|
|
|
|
1872
|
|
|
|
|
|
|
# Can't send anything form the data buffer this time ... |
1873
|
0
|
|
|
|
|
|
$offset -= $len; # Result is >= 0 |
1874
|
0
|
|
|
|
|
|
$len = 0; |
1875
|
0
|
|
|
|
|
|
$data = ""; |
1876
|
|
|
|
|
|
|
|
1877
|
|
|
|
|
|
|
} elsif ( $offset ) { |
1878
|
|
|
|
|
|
|
# Sending a partial data buffer, stripping off leading chars ... |
1879
|
0
|
|
|
|
|
|
my $p = "." x $offset; |
1880
|
0
|
|
|
|
|
|
$data =~ s/^$p//s; # Use option "s" since $data has "\n" in it. |
1881
|
0
|
|
|
|
|
|
$len -= $offset; # Result is >= 0 |
1882
|
0
|
|
|
|
|
|
$offset = 0; |
1883
|
|
|
|
|
|
|
} |
1884
|
|
|
|
|
|
|
|
1885
|
0
|
|
|
|
|
|
return ($offset, $len, $data); |
1886
|
|
|
|
|
|
|
} |
1887
|
|
|
|
|
|
|
|
1888
|
|
|
|
|
|
|
sub _common_put { |
1889
|
0
|
|
|
0
|
|
|
my $self = shift; |
1890
|
0
|
|
|
|
|
|
my $file_loc = shift; |
1891
|
0
|
|
|
|
|
|
my $file_rem = shift; |
1892
|
0
|
|
0
|
|
|
|
my $offset = shift || ${*$self}{_FTPSSL_arguments}->{net_ftpssl_rest_offset} || 0; |
1893
|
|
|
|
|
|
|
|
1894
|
|
|
|
|
|
|
# Clear out this messy restart() cluge for next time ... |
1895
|
0
|
|
|
|
|
|
delete ( ${*$self}{_FTPSSL_arguments}->{net_ftpssl_rest_offset} ); |
|
0
|
|
|
|
|
|
|
1896
|
|
|
|
|
|
|
|
1897
|
0
|
0
|
0
|
|
|
|
if ( $self->_isa_glob ($file_loc) && ! $file_rem ) { |
1898
|
0
|
|
|
|
|
|
return $self->_croak_or_return (0, "When you pass a stream, you must specify the remote filename."); |
1899
|
|
|
|
|
|
|
} |
1900
|
|
|
|
|
|
|
|
1901
|
0
|
0
|
|
|
|
|
unless ($file_rem) { |
1902
|
0
|
|
|
|
|
|
$file_rem = basename ($file_loc); |
1903
|
|
|
|
|
|
|
} |
1904
|
|
|
|
|
|
|
|
1905
|
0
|
0
|
|
|
|
|
if ( $offset < -1 ) { |
1906
|
0
|
|
|
|
|
|
return $self->_croak_or_return(0, "Invalid file offset ($offset)!"); |
1907
|
|
|
|
|
|
|
} |
1908
|
|
|
|
|
|
|
|
1909
|
|
|
|
|
|
|
# Find out which of 4 "put" functions called me ... |
1910
|
0
|
|
0
|
|
|
|
my $func = (caller(1))[3] || ":unknown"; |
1911
|
0
|
|
|
|
|
|
$func =~ m/:([^:]+)$/; |
1912
|
0
|
|
|
|
|
|
$func = $1; |
1913
|
|
|
|
|
|
|
|
1914
|
0
|
0
|
0
|
|
|
|
if ( $offset && $func ne "put" && $func ne "append" ) { |
|
|
|
0
|
|
|
|
|
1915
|
0
|
|
|
|
|
|
return $self->_croak_or_return(0, "Function $func() doesn't support RESTart."); |
1916
|
|
|
|
|
|
|
} |
1917
|
|
|
|
|
|
|
|
1918
|
0
|
0
|
|
|
|
|
if ( $offset == -1 ) { |
1919
|
0
|
|
|
|
|
|
$offset = $self->size ($file_rem); |
1920
|
0
|
0
|
|
|
|
|
unless ( defined $offset ) { |
1921
|
0
|
|
|
|
|
|
return (undef); # Already did croak test in size(). |
1922
|
|
|
|
|
|
|
} |
1923
|
|
|
|
|
|
|
} |
1924
|
|
|
|
|
|
|
|
1925
|
0
|
|
|
|
|
|
my ( $size, $localfd ); |
1926
|
0
|
|
|
|
|
|
my $close_file = 0; |
1927
|
|
|
|
|
|
|
|
1928
|
0
|
|
0
|
|
|
|
$size = ${*$self}{_FTPSSL_arguments}->{buf_size} || 2048; |
1929
|
|
|
|
|
|
|
|
1930
|
0
|
0
|
|
|
|
|
if ( $self->_isa_glob ($file_loc) ) { |
1931
|
0
|
|
|
|
|
|
$localfd = \*$file_loc; |
1932
|
|
|
|
|
|
|
|
1933
|
|
|
|
|
|
|
} else { |
1934
|
0
|
0
|
|
|
|
|
unless ( open( $localfd, "< $file_loc" ) ) { |
1935
|
0
|
|
|
|
|
|
return $self->_croak_or_return (0, "Can't open local file! ($file_loc)"); |
1936
|
|
|
|
|
|
|
} |
1937
|
0
|
|
|
|
|
|
$close_file = 1; |
1938
|
|
|
|
|
|
|
} |
1939
|
|
|
|
|
|
|
|
1940
|
0
|
|
|
|
|
|
my $fix_cr_issue = 1; |
1941
|
0
|
0
|
|
|
|
|
if ( ${*$self}{_FTPSSL_arguments}->{type} eq MODE_BINARY ) { |
|
0
|
|
|
|
|
|
|
1942
|
0
|
0
|
|
|
|
|
unless ( binmode $localfd ) { |
1943
|
0
|
|
|
|
|
|
return $self->_croak_or_return(0, "Can't set binary mode to local file!"); |
1944
|
|
|
|
|
|
|
} |
1945
|
0
|
|
|
|
|
|
$fix_cr_issue = 0; |
1946
|
|
|
|
|
|
|
} |
1947
|
|
|
|
|
|
|
|
1948
|
|
|
|
|
|
|
# Set in case we require the use of the PRET command ... |
1949
|
0
|
|
|
|
|
|
my $cmd = ""; |
1950
|
0
|
0
|
|
|
|
|
if ( $func eq "uput" ) { |
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
1951
|
0
|
|
|
|
|
|
$cmd = "STOU"; |
1952
|
|
|
|
|
|
|
} elsif ( $func eq "xput" ) { |
1953
|
0
|
|
|
|
|
|
$cmd = "STOR"; |
1954
|
|
|
|
|
|
|
} elsif ( $func eq "put" ) { |
1955
|
0
|
|
|
|
|
|
$cmd = "STOR"; |
1956
|
|
|
|
|
|
|
} elsif ( $func eq "append" ) { |
1957
|
0
|
|
|
|
|
|
$cmd = "APPE"; |
1958
|
|
|
|
|
|
|
} |
1959
|
|
|
|
|
|
|
|
1960
|
0
|
0
|
|
|
|
|
unless ( $self->prep_data_channel( $cmd, $file_rem ) ) { |
1961
|
0
|
0
|
|
|
|
|
close ($localfd) if ($close_file); |
1962
|
0
|
|
|
|
|
|
return undef; # Already decided not to call croak if you get here! |
1963
|
|
|
|
|
|
|
} |
1964
|
|
|
|
|
|
|
|
1965
|
|
|
|
|
|
|
# If alloc_size is already set, I skip this part |
1966
|
0
|
0
|
|
|
|
|
unless ( defined ${*$self}{_FTPSSL_arguments}->{alloc_size} ) { |
|
0
|
|
|
|
|
|
|
1967
|
0
|
0
|
0
|
|
|
|
if ( $close_file && -f $file_loc ) { |
1968
|
0
|
|
|
|
|
|
my $size = -s $file_loc; |
1969
|
0
|
|
|
|
|
|
$self->alloc($size); |
1970
|
|
|
|
|
|
|
} |
1971
|
|
|
|
|
|
|
} |
1972
|
|
|
|
|
|
|
|
1973
|
0
|
|
|
|
|
|
delete ${*$self}{_FTPSSL_arguments}->{alloc_size}; |
|
0
|
|
|
|
|
|
|
1974
|
|
|
|
|
|
|
|
1975
|
|
|
|
|
|
|
# Issue the correct "put" request ... |
1976
|
0
|
|
|
|
|
|
my ($response, $restart) = (0, 1); |
1977
|
0
|
0
|
|
|
|
|
if ( $func eq "uput" ) { |
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
1978
|
0
|
|
|
|
|
|
$response = $self->_stou ($file_rem); |
1979
|
|
|
|
|
|
|
} elsif ( $func eq "xput" ) { |
1980
|
0
|
|
|
|
|
|
$response = $self->_stor ($file_rem); |
1981
|
|
|
|
|
|
|
} elsif ( $func eq "put" ) { |
1982
|
0
|
0
|
|
|
|
|
$restart = ($offset) ? $self->_rest ($offset) : 1; |
1983
|
0
|
|
|
|
|
|
$response = $self->_stor ($file_rem); |
1984
|
|
|
|
|
|
|
} elsif ( $func eq "append" ) { |
1985
|
|
|
|
|
|
|
# Just uses OFFSET, doesn't send REST out. |
1986
|
0
|
|
|
|
|
|
$response = $self->_appe ($file_rem); |
1987
|
|
|
|
|
|
|
} |
1988
|
|
|
|
|
|
|
|
1989
|
|
|
|
|
|
|
# If the "put" request fails ... |
1990
|
0
|
0
|
0
|
|
|
|
unless ($restart && $response) { |
1991
|
0
|
0
|
|
|
|
|
close ($localfd) if ($close_file); |
1992
|
0
|
0
|
0
|
|
|
|
if ( $restart && $offset && $func eq "get" ) { |
|
|
|
0
|
|
|
|
|
1993
|
0
|
|
|
|
|
|
$self->_rest (0); |
1994
|
|
|
|
|
|
|
} |
1995
|
0
|
|
|
|
|
|
return ( $self->_croak_or_return (), undef, undef, $file_rem, undef ); |
1996
|
|
|
|
|
|
|
} |
1997
|
|
|
|
|
|
|
|
1998
|
|
|
|
|
|
|
# The "REST" command doesn't affect file streams ... |
1999
|
0
|
0
|
|
|
|
|
$offset = 0 unless ($close_file); |
2000
|
|
|
|
|
|
|
|
2001
|
0
|
|
|
|
|
|
my $put_msg = $self->last_message (); |
2002
|
|
|
|
|
|
|
|
2003
|
0
|
|
|
|
|
|
my ( $data, $written, $io ); |
2004
|
|
|
|
|
|
|
|
2005
|
0
|
|
|
|
|
|
$io = $self->_get_data_channel (); |
2006
|
0
|
0
|
|
|
|
|
unless ( defined $io ) { |
2007
|
0
|
0
|
|
|
|
|
close ($localfd) if ($close_file); |
2008
|
0
|
|
|
|
|
|
return undef; # Already decided not to call croak if you get here! |
2009
|
|
|
|
|
|
|
} |
2010
|
|
|
|
|
|
|
|
2011
|
0
|
|
|
|
|
|
my $trace_flag = ${*$self}{_FTPSSL_arguments}->{trace}; |
|
0
|
|
|
|
|
|
|
2012
|
0
|
0
|
|
|
|
|
print STDERR "$func() trace ." if ($trace_flag); |
2013
|
0
|
|
|
|
|
|
my $cnt = 0; |
2014
|
0
|
|
|
|
|
|
my $total = 0; |
2015
|
0
|
|
|
|
|
|
my $len; |
2016
|
|
|
|
|
|
|
|
2017
|
0
|
|
|
|
|
|
while ( ( $len = sysread $localfd, $data, $size ) ) { |
2018
|
0
|
0
|
|
|
|
|
unless ( defined $len ) { |
2019
|
0
|
0
|
|
|
|
|
next if $! == EINTR; |
2020
|
0
|
|
|
|
|
|
return $self->_croak_or_return (0, "System read error on $func(): $!"); |
2021
|
|
|
|
|
|
|
} |
2022
|
|
|
|
|
|
|
|
2023
|
0
|
|
|
|
|
|
$total = $self->_call_callback (2, \$data, \$len, $total); |
2024
|
|
|
|
|
|
|
|
2025
|
0
|
0
|
|
|
|
|
if ($fix_cr_issue) { |
2026
|
0
|
|
|
|
|
|
$data =~ s/\n/\015\012/g; |
2027
|
0
|
|
|
|
|
|
$len = length ($data); |
2028
|
|
|
|
|
|
|
} |
2029
|
|
|
|
|
|
|
|
2030
|
|
|
|
|
|
|
# Determine if we can send any of this data to the server ... |
2031
|
0
|
0
|
|
|
|
|
if ( $offset ) { |
2032
|
0
|
|
|
|
|
|
($offset, $len, $data) = $self->_put_offset_fix ( $offset, $len, $data ); |
2033
|
|
|
|
|
|
|
} |
2034
|
|
|
|
|
|
|
|
2035
|
0
|
0
|
0
|
|
|
|
print STDERR "." if ($trace_flag && ($cnt % TRACE_MOD) == 0); |
2036
|
0
|
|
|
|
|
|
++$cnt; |
2037
|
|
|
|
|
|
|
|
2038
|
0
|
0
|
|
|
|
|
if ( $len > 0 ) { |
2039
|
0
|
|
|
|
|
|
$written = syswrite $io, $data, $len; |
2040
|
0
|
0
|
|
|
|
|
return $self->_croak_or_return (0, "System write error on $func(): $!") |
2041
|
|
|
|
|
|
|
unless (defined $written); |
2042
|
|
|
|
|
|
|
} |
2043
|
|
|
|
|
|
|
} # End while sysread() loop! |
2044
|
|
|
|
|
|
|
|
2045
|
|
|
|
|
|
|
|
2046
|
|
|
|
|
|
|
# Process trailing call back info if present. |
2047
|
0
|
|
|
|
|
|
my $trail; |
2048
|
0
|
|
|
|
|
|
($trail, $len, $total) = $self->_end_callback (2, $total); |
2049
|
0
|
0
|
|
|
|
|
if ( $trail ) { |
2050
|
0
|
0
|
|
|
|
|
if ($fix_cr_issue) { |
2051
|
0
|
|
|
|
|
|
$trail =~ s/\n/\015\012/g; |
2052
|
0
|
|
|
|
|
|
$len = length ($trail); |
2053
|
|
|
|
|
|
|
} |
2054
|
|
|
|
|
|
|
|
2055
|
|
|
|
|
|
|
# Determine if we can send any of this data to the server ... |
2056
|
0
|
0
|
|
|
|
|
if ( $offset ) { |
2057
|
0
|
|
|
|
|
|
($offset, $len, $data) = $self->_put_offset_fix ( $offset, $len, $data ); |
2058
|
|
|
|
|
|
|
} |
2059
|
|
|
|
|
|
|
|
2060
|
0
|
0
|
|
|
|
|
if ( $len > 0 ) { |
2061
|
0
|
|
|
|
|
|
$written = syswrite $io, $trail, $len; |
2062
|
0
|
0
|
|
|
|
|
return $self->_croak_or_return (0, "System write error on $func(): $!") |
2063
|
|
|
|
|
|
|
unless (defined $written); |
2064
|
|
|
|
|
|
|
} |
2065
|
|
|
|
|
|
|
} |
2066
|
|
|
|
|
|
|
|
2067
|
0
|
0
|
|
|
|
|
print STDERR ". done! (" . $self->_fmt_num ($total) . " byte(s))\n" if ($trace_flag); |
2068
|
|
|
|
|
|
|
|
2069
|
0
|
|
|
|
|
|
my $tm; |
2070
|
0
|
0
|
|
|
|
|
if ($close_file) { |
2071
|
0
|
|
|
|
|
|
close ($localfd); |
2072
|
0
|
0
|
|
|
|
|
if ( ${*$self}{_FTPSSL_arguments}->{FixPutTs} ) { |
|
0
|
|
|
|
|
|
|
2073
|
0
|
|
|
|
|
|
$tm = (stat ($file_loc))[9]; # Get's the local file's timestamp! |
2074
|
|
|
|
|
|
|
} |
2075
|
|
|
|
|
|
|
} |
2076
|
|
|
|
|
|
|
|
2077
|
0
|
|
|
|
|
|
_my_close ($io); # Old way $io->close(); |
2078
|
|
|
|
|
|
|
|
2079
|
|
|
|
|
|
|
# To catch the expected "226 Closing data connection." |
2080
|
0
|
0
|
|
|
|
|
if ( $self->response() != CMD_OK ) { |
2081
|
0
|
|
|
|
|
|
return $self->_croak_or_return (); |
2082
|
|
|
|
|
|
|
} |
2083
|
|
|
|
|
|
|
|
2084
|
0
|
|
|
|
|
|
return ( 1, $put_msg, $self->last_message (), $file_rem, $tm ); |
2085
|
|
|
|
|
|
|
} |
2086
|
|
|
|
|
|
|
|
2087
|
|
|
|
|
|
|
|
2088
|
|
|
|
|
|
|
# On some servers this command always fails! So no croak test! |
2089
|
|
|
|
|
|
|
# It's also why supported gets called. |
2090
|
|
|
|
|
|
|
# Just be aware of HELP issue (OverrideHELP option) |
2091
|
|
|
|
|
|
|
sub alloc { |
2092
|
0
|
|
|
0
|
0
|
|
my $self = shift; |
2093
|
0
|
|
|
|
|
|
my $size = shift; |
2094
|
|
|
|
|
|
|
|
2095
|
0
|
0
|
0
|
|
|
|
if ( $self->supported ("ALLO") && |
2096
|
|
|
|
|
|
|
$self->_alloc($size) ) { |
2097
|
0
|
|
|
|
|
|
${*$self}{_FTPSSL_arguments}->{alloc_size} = $size; |
|
0
|
|
|
|
|
|
|
2098
|
|
|
|
|
|
|
} |
2099
|
|
|
|
|
|
|
else { |
2100
|
0
|
|
|
|
|
|
return 0; |
2101
|
|
|
|
|
|
|
} |
2102
|
|
|
|
|
|
|
|
2103
|
0
|
|
|
|
|
|
return 1; |
2104
|
|
|
|
|
|
|
} |
2105
|
|
|
|
|
|
|
|
2106
|
|
|
|
|
|
|
sub delete { |
2107
|
0
|
|
|
0
|
1
|
|
my $self = shift; |
2108
|
0
|
|
|
|
|
|
return ($self->_test_croak ($self->command("DELE", @_)->response() == CMD_OK)); |
2109
|
|
|
|
|
|
|
} |
2110
|
|
|
|
|
|
|
|
2111
|
|
|
|
|
|
|
sub auth { |
2112
|
0
|
|
|
0
|
0
|
|
my $self = shift; |
2113
|
0
|
|
|
|
|
|
return ($self->_test_croak ($self->command("AUTH", "TLS")->response() == CMD_OK)); |
2114
|
|
|
|
|
|
|
} |
2115
|
|
|
|
|
|
|
|
2116
|
|
|
|
|
|
|
sub pwd { |
2117
|
0
|
|
|
0
|
1
|
|
my $self = shift; |
2118
|
0
|
|
|
|
|
|
my $path; |
2119
|
|
|
|
|
|
|
|
2120
|
0
|
|
|
|
|
|
$self->command("PWD")->response(); |
2121
|
|
|
|
|
|
|
|
2122
|
0
|
0
|
|
|
|
|
if ( ${*$self}{_FTPSSL_arguments}->{last_ftp_msg} =~ /\"(.*)\".*/ ) |
|
0
|
|
|
|
|
|
|
2123
|
|
|
|
|
|
|
{ |
2124
|
|
|
|
|
|
|
# 257 "//" is current directory. |
2125
|
|
|
|
|
|
|
# "Quote-doubling" convention - RFC 959, Appendix II |
2126
|
0
|
|
|
|
|
|
( $path = $1 ) =~ s/\"\"/\"/g; |
2127
|
0
|
|
|
|
|
|
return $path; |
2128
|
|
|
|
|
|
|
} |
2129
|
|
|
|
|
|
|
else { |
2130
|
0
|
|
|
|
|
|
return $self->_croak_or_return (); |
2131
|
|
|
|
|
|
|
} |
2132
|
|
|
|
|
|
|
} |
2133
|
|
|
|
|
|
|
|
2134
|
|
|
|
|
|
|
sub cwd { |
2135
|
0
|
|
|
0
|
1
|
|
my $self = shift; |
2136
|
0
|
|
|
|
|
|
return ( $self->_test_croak ($self->command("CWD", @_)->response() == CMD_OK) ); |
2137
|
|
|
|
|
|
|
} |
2138
|
|
|
|
|
|
|
|
2139
|
|
|
|
|
|
|
sub noop { |
2140
|
0
|
|
|
0
|
1
|
|
my $self = shift; |
2141
|
0
|
|
|
|
|
|
return ( $self->_test_croak ($self->command("NOOP")->response() == CMD_OK) ); |
2142
|
|
|
|
|
|
|
} |
2143
|
|
|
|
|
|
|
|
2144
|
|
|
|
|
|
|
sub rename { |
2145
|
0
|
|
|
0
|
1
|
|
my $self = shift; |
2146
|
0
|
|
|
|
|
|
my $old_name = shift; |
2147
|
0
|
|
|
|
|
|
my $new_name = shift; |
2148
|
|
|
|
|
|
|
|
2149
|
0
|
|
0
|
|
|
|
return ( $self->_test_croak ( $self->_rnfr ($old_name) && |
2150
|
|
|
|
|
|
|
$self->_rnto ($new_name) ) ); |
2151
|
|
|
|
|
|
|
} |
2152
|
|
|
|
|
|
|
|
2153
|
|
|
|
|
|
|
sub cdup { |
2154
|
0
|
|
|
0
|
1
|
|
my $self = shift; |
2155
|
0
|
|
|
|
|
|
return ( $self->_test_croak ($self->command("CDUP")->response() == CMD_OK) ); |
2156
|
|
|
|
|
|
|
} |
2157
|
|
|
|
|
|
|
|
2158
|
|
|
|
|
|
|
# TODO: Make mkdir() working with recursion. |
2159
|
|
|
|
|
|
|
sub mkdir { |
2160
|
0
|
|
|
0
|
1
|
|
my $self = shift; |
2161
|
0
|
|
|
|
|
|
my $dir = shift; |
2162
|
0
|
|
|
|
|
|
$self->command("MKD", $dir); |
2163
|
0
|
|
|
|
|
|
return ( $self->_test_croak ($self->response() == CMD_OK) ); |
2164
|
|
|
|
|
|
|
} |
2165
|
|
|
|
|
|
|
|
2166
|
|
|
|
|
|
|
# TODO: Make rmdir() working with recursion. |
2167
|
|
|
|
|
|
|
sub rmdir { |
2168
|
0
|
|
|
0
|
1
|
|
my $self = shift; |
2169
|
0
|
|
|
|
|
|
my $dir = shift; |
2170
|
0
|
|
|
|
|
|
$self->command("RMD", $dir); |
2171
|
0
|
|
|
|
|
|
return ( $self->_test_croak ($self->response() == CMD_OK) ); |
2172
|
|
|
|
|
|
|
} |
2173
|
|
|
|
|
|
|
|
2174
|
|
|
|
|
|
|
sub site { |
2175
|
0
|
|
|
0
|
1
|
|
my $self = shift; |
2176
|
|
|
|
|
|
|
|
2177
|
0
|
|
|
|
|
|
return ($self->_test_croak ($self->command("SITE", @_)->response() == CMD_OK)); |
2178
|
|
|
|
|
|
|
} |
2179
|
|
|
|
|
|
|
|
2180
|
|
|
|
|
|
|
# A true boolean func, should never call croak! |
2181
|
|
|
|
|
|
|
sub supported { |
2182
|
0
|
|
|
0
|
1
|
|
my $self = shift; |
2183
|
0
|
|
0
|
|
|
|
my $cmd = uc (shift || ""); |
2184
|
0
|
|
0
|
|
|
|
my $sub_cmd = uc (shift || ""); |
2185
|
|
|
|
|
|
|
|
2186
|
0
|
|
|
|
|
|
my $result = 0; # Assume invalid FTP command |
2187
|
0
|
|
|
|
|
|
my $arg = ${*$self}{_FTPSSL_arguments}; |
|
0
|
|
|
|
|
|
|
2188
|
|
|
|
|
|
|
|
2189
|
|
|
|
|
|
|
# It will cache the result so OK to call multiple times. |
2190
|
0
|
|
|
|
|
|
my $help = $self->_help (); |
2191
|
|
|
|
|
|
|
|
2192
|
|
|
|
|
|
|
# Only finds exact matches, no abbreviations like some FTP servers allow. |
2193
|
0
|
0
|
0
|
|
|
|
if ( $arg->{OverrideHELP} || exists $help->{$cmd} ) { |
2194
|
0
|
|
|
|
|
|
$result = 1; # Was a valid FTP command |
2195
|
0
|
|
|
|
|
|
$arg->{last_ftp_msg} = "214 The $cmd command is supported."; |
2196
|
|
|
|
|
|
|
} else { |
2197
|
0
|
|
|
|
|
|
$arg->{last_ftp_msg} = "502 Unknown command $cmd."; |
2198
|
|
|
|
|
|
|
} |
2199
|
|
|
|
|
|
|
|
2200
|
|
|
|
|
|
|
# Are we validating a SITE sub-command? |
2201
|
0
|
0
|
0
|
|
|
|
if ($result && $cmd eq "SITE" && $sub_cmd ne "") { |
|
|
|
0
|
|
|
|
|
2202
|
0
|
|
|
|
|
|
my $help2 = $self->_help ($cmd); |
2203
|
0
|
0
|
|
|
|
|
if ( exists $help2->{$sub_cmd} ) { |
|
|
0
|
|
|
|
|
|
2204
|
0
|
|
|
|
|
|
$arg->{last_ftp_msg} = "214 The SITE sub-command $sub_cmd is supported."; |
2205
|
0
|
|
|
|
|
|
} elsif ( scalar (keys %{$help2}) > 0 ) { |
2206
|
0
|
|
|
|
|
|
$arg->{last_ftp_msg} = "502 Unknown SITE sub-command - $sub_cmd."; |
2207
|
0
|
|
|
|
|
|
$result = 0; # It failed after all! |
2208
|
|
|
|
|
|
|
} else { |
2209
|
0
|
|
|
|
|
|
$arg->{last_ftp_msg} = "503 Can't validate SITE sub-commands - $sub_cmd."; |
2210
|
0
|
|
|
|
|
|
$result = -1; # Maybe/mabye not supported! |
2211
|
|
|
|
|
|
|
} |
2212
|
|
|
|
|
|
|
} |
2213
|
|
|
|
|
|
|
|
2214
|
|
|
|
|
|
|
# Are we validating a FEAT sub-command? |
2215
|
0
|
0
|
0
|
|
|
|
if ($result && $cmd eq "FEAT" && $sub_cmd ne "") { |
|
|
|
0
|
|
|
|
|
2216
|
0
|
|
|
|
|
|
my $feat2 = $self->_feat (); |
2217
|
0
|
0
|
|
|
|
|
if ( exists $feat2->{$sub_cmd} ) { |
2218
|
0
|
|
|
|
|
|
$arg->{last_ftp_msg} = "214 The FEAT sub-command $sub_cmd is supported."; |
2219
|
0
|
0
|
0
|
|
|
|
if ( exists $feat2->{OPTS} && exists $feat2->{OPTS}->{$sub_cmd} ) { |
2220
|
0
|
|
|
|
|
|
$arg->{last_ftp_msg} .= " And its behaviour may be modified by OPTS."; |
2221
|
|
|
|
|
|
|
} |
2222
|
|
|
|
|
|
|
} else { |
2223
|
0
|
|
|
|
|
|
$arg->{last_ftp_msg} = "502 Unknown FEAT sub-command - $sub_cmd."; |
2224
|
0
|
|
|
|
|
|
$result = 0; # It failed after all! |
2225
|
|
|
|
|
|
|
} |
2226
|
|
|
|
|
|
|
} |
2227
|
|
|
|
|
|
|
|
2228
|
|
|
|
|
|
|
# Are we validating a OPTS sub-command? |
2229
|
0
|
0
|
0
|
|
|
|
if ($result && $cmd eq "OPTS" && $sub_cmd ne "") { |
|
|
|
0
|
|
|
|
|
2230
|
0
|
|
|
|
|
|
my $feat3 = $self->_feat (); |
2231
|
0
|
0
|
0
|
|
|
|
if ( exists $feat3->{OPTS} && exists $feat3->{OPTS}->{$sub_cmd} ) { |
|
|
0
|
|
|
|
|
|
2232
|
0
|
|
|
|
|
|
$arg->{last_ftp_msg} = "214 The FEAT sub-command $sub_cmd may be modified by the OPTS command."; |
2233
|
|
|
|
|
|
|
} elsif ( exists $feat3->{sub_cmd} ) { |
2234
|
0
|
|
|
|
|
|
$arg->{last_ftp_msg} = "504 The FEAT sub-command $sub_cmd may not be modified by the OPTS command."; |
2235
|
|
|
|
|
|
|
} else { |
2236
|
0
|
|
|
|
|
|
$arg->{last_ftp_msg} = "505 The FEAT sub-command $sub_cmd doesn't exist and so can't be modified by the OPTS command."; |
2237
|
|
|
|
|
|
|
} |
2238
|
|
|
|
|
|
|
} |
2239
|
|
|
|
|
|
|
|
2240
|
0
|
|
|
|
|
|
$self->_print_DBG ( "<<+ ", $self->last_message (), "\n" ); |
2241
|
|
|
|
|
|
|
|
2242
|
0
|
|
|
|
|
|
return ($result); |
2243
|
|
|
|
|
|
|
} |
2244
|
|
|
|
|
|
|
|
2245
|
|
|
|
|
|
|
|
2246
|
|
|
|
|
|
|
# A true boolean func, should never call croak! |
2247
|
|
|
|
|
|
|
sub all_supported { |
2248
|
0
|
|
|
0
|
1
|
|
my $self = shift; |
2249
|
|
|
|
|
|
|
# Leave the rest of the options on the @_ array ... |
2250
|
|
|
|
|
|
|
|
2251
|
0
|
|
|
|
|
|
my $cnt = 0; |
2252
|
0
|
|
|
|
|
|
foreach ( @_ ) { |
2253
|
0
|
0
|
0
|
|
|
|
next unless (defined $_ && $_ ne ""); |
2254
|
0
|
|
|
|
|
|
++$cnt; |
2255
|
0
|
0
|
|
|
|
|
next if ($self->supported ($_)); |
2256
|
0
|
|
|
|
|
|
return (0); # Something wasn't supported! |
2257
|
|
|
|
|
|
|
} |
2258
|
|
|
|
|
|
|
|
2259
|
0
|
0
|
|
|
|
|
return ( ($cnt >= 1) ? 1 : 0 ); |
2260
|
|
|
|
|
|
|
} |
2261
|
|
|
|
|
|
|
|
2262
|
|
|
|
|
|
|
|
2263
|
|
|
|
|
|
|
# Hacks the _help() cache, otherwise can't modify the supported logic! |
2264
|
|
|
|
|
|
|
sub fix_supported { |
2265
|
0
|
|
|
0
|
1
|
|
my $self = shift; |
2266
|
0
|
|
|
|
|
|
my $mode = shift; # True Add/False Remove |
2267
|
|
|
|
|
|
|
# Leave the rest of the options on the @_ array ... |
2268
|
|
|
|
|
|
|
|
2269
|
|
|
|
|
|
|
# Can't update supported() if using OverrideHELP => 1. (Everything supported) |
2270
|
0
|
0
|
|
|
|
|
return (0) if ( ${*$self}{_FTPSSL_arguments}->{OverrideHELP} ); |
|
0
|
|
|
|
|
|
|
2271
|
|
|
|
|
|
|
|
2272
|
|
|
|
|
|
|
# Holds the real cached help values ... |
2273
|
0
|
|
|
|
|
|
my $help = ${*$self}{_FTPSSL_arguments}->{help_cmds_found}; |
|
0
|
|
|
|
|
|
|
2274
|
0
|
0
|
|
|
|
|
return (0) unless ( defined $help ); |
2275
|
|
|
|
|
|
|
|
2276
|
|
|
|
|
|
|
# Flag to tell if you can add/remove the HELP command from supported! |
2277
|
|
|
|
|
|
|
my $help_flag = ( exists ${*$self}{_FTPSSL_arguments}->{OverrideHELP} || |
2278
|
0
|
|
0
|
|
|
|
exists ${*$self}{_FTPSSL_arguments}->{removeHELP} ); |
2279
|
|
|
|
|
|
|
|
2280
|
0
|
|
|
|
|
|
my $cnt = 0; |
2281
|
0
|
|
|
|
|
|
foreach ( @_ ) { |
2282
|
0
|
|
|
|
|
|
my $key = uc ($_); |
2283
|
|
|
|
|
|
|
|
2284
|
0
|
0
|
0
|
|
|
|
next if ( $key eq "HELP" && $help_flag ); |
2285
|
|
|
|
|
|
|
|
2286
|
0
|
0
|
0
|
|
|
|
if ( $mode && ! exists $help->{$key} ) { |
|
|
0
|
0
|
|
|
|
|
2287
|
0
|
|
|
|
|
|
$help->{$key} = 3; # Add the command as supported. |
2288
|
0
|
|
|
|
|
|
++$cnt; |
2289
|
|
|
|
|
|
|
|
2290
|
|
|
|
|
|
|
} elsif ( ! $mode && exists $help->{$key} ) { |
2291
|
0
|
|
|
|
|
|
delete $help->{$key}; # Remove the command as supported. |
2292
|
0
|
|
|
|
|
|
++$cnt; |
2293
|
|
|
|
|
|
|
} |
2294
|
|
|
|
|
|
|
} |
2295
|
|
|
|
|
|
|
|
2296
|
0
|
|
|
|
|
|
return ( $cnt ); |
2297
|
|
|
|
|
|
|
} |
2298
|
|
|
|
|
|
|
|
2299
|
|
|
|
|
|
|
|
2300
|
|
|
|
|
|
|
# The Clear Command Channel func is only valid after login. |
2301
|
|
|
|
|
|
|
sub ccc { |
2302
|
0
|
|
|
0
|
1
|
|
my $self = shift; |
2303
|
0
|
|
0
|
|
|
|
my $prot = shift || ${*$self}{_FTPSSL_arguments}->{data_prot}; |
2304
|
|
|
|
|
|
|
|
2305
|
0
|
0
|
|
|
|
|
if ( ${*$self}{_FTPSSL_arguments}->{Crypt} eq CLR_CRYPT ) { |
|
0
|
|
|
|
|
|
|
2306
|
0
|
|
|
|
|
|
return $self->_croak_or_return (undef, "Command Channel already clear!"); |
2307
|
|
|
|
|
|
|
} |
2308
|
|
|
|
|
|
|
|
2309
|
|
|
|
|
|
|
# Set the data channel to the requested security level ... |
2310
|
|
|
|
|
|
|
# This command is no longer supported after the CCC command executes. |
2311
|
0
|
0
|
0
|
|
|
|
unless ($self->_pbsz() && $self->_prot ($prot)) { |
2312
|
0
|
|
|
|
|
|
return $self->_croak_or_return (); |
2313
|
|
|
|
|
|
|
} |
2314
|
|
|
|
|
|
|
|
2315
|
|
|
|
|
|
|
# Do before the CCC command so we know which command is available to clear |
2316
|
|
|
|
|
|
|
# out the command channel with. All servers should support one or the other. |
2317
|
|
|
|
|
|
|
# We also want commands that return just one line! [To make it less likely |
2318
|
|
|
|
|
|
|
# that the hack will cause response() to hang or get out of sync when |
2319
|
|
|
|
|
|
|
# unrecognizable junk is returned for the hack.] |
2320
|
0
|
0
|
|
|
|
|
my $ccc_fix_cmd = $self->supported ("NOOP") ? "NOOP" : "PWD"; |
2321
|
|
|
|
|
|
|
|
2322
|
|
|
|
|
|
|
# Request that just the commnad channel go clear ... |
2323
|
0
|
0
|
|
|
|
|
unless ( $self->command ("CCC")->response () == CMD_OK ) { |
2324
|
0
|
|
|
|
|
|
return $self->_croak_or_return (); |
2325
|
|
|
|
|
|
|
} |
2326
|
0
|
|
|
|
|
|
${*$self}{_FTPSSL_arguments}->{Crypt} = CLR_CRYPT; |
|
0
|
|
|
|
|
|
|
2327
|
|
|
|
|
|
|
|
2328
|
|
|
|
|
|
|
# Save before stop_SSL() removes the bless. |
2329
|
0
|
|
|
|
|
|
my $bless_type = ref ($self); |
2330
|
|
|
|
|
|
|
|
2331
|
|
|
|
|
|
|
# ------------------------------------------------------------------------- |
2332
|
|
|
|
|
|
|
# Stop SSL, but leave the socket open! |
2333
|
|
|
|
|
|
|
# Converts $self to IO::Socket::INET object instead of Net::FTPSSL |
2334
|
|
|
|
|
|
|
# NOTE: SSL_no_shutdown => 1 doesn't work on some boxes, and when 0, |
2335
|
|
|
|
|
|
|
# it hangs on others without the SSL_fast_shutdown => 1 option. |
2336
|
|
|
|
|
|
|
# ------------------------------------------------------------------------- |
2337
|
0
|
0
|
|
|
|
|
unless ( $self->stop_SSL ( SSL_no_shutdown => 0, SSL_fast_shutdown => 1 ) ) { |
2338
|
0
|
|
|
|
|
|
return $self->_croak_or_return (undef, "Command Channel downgrade failed!"); |
2339
|
|
|
|
|
|
|
} |
2340
|
|
|
|
|
|
|
|
2341
|
|
|
|
|
|
|
# Bless back to Net::FTPSSL from IO::Socket::INET ... |
2342
|
0
|
|
|
|
|
|
bless ( $self, $bless_type ); |
2343
|
0
|
|
|
|
|
|
${*$self}{_SSL_opened} = 0; # To get rid of warning on quit ... |
|
0
|
|
|
|
|
|
|
2344
|
|
|
|
|
|
|
|
2345
|
|
|
|
|
|
|
# ------------------------------------------------------------------------- |
2346
|
|
|
|
|
|
|
# This is a hack, but it seems to resolve the command channel corruption |
2347
|
|
|
|
|
|
|
# problem where the 1st command or two afer CCC may fail or look strange ... |
2348
|
|
|
|
|
|
|
# I've even caught it a few times sending back 2 independant OK responses |
2349
|
|
|
|
|
|
|
# to a single command! |
2350
|
|
|
|
|
|
|
# ------------------------------------------------------------------------ |
2351
|
0
|
|
|
|
|
|
my $ok = CMD_ERROR; |
2352
|
0
|
|
|
|
|
|
foreach ( 1..4 ) { |
2353
|
0
|
|
|
|
|
|
$ok = $self->command ($ccc_fix_cmd)->response (1); # This "1" is a hack! |
2354
|
0
|
0
|
|
|
|
|
last if ( $ok eq CMD_OK ); # Do char compare since not always a number. |
2355
|
|
|
|
|
|
|
} |
2356
|
|
|
|
|
|
|
|
2357
|
0
|
0
|
|
|
|
|
if ( $ok == CMD_OK ) { |
2358
|
|
|
|
|
|
|
# Complete the hack, now force a failure response! |
2359
|
|
|
|
|
|
|
# And if the server was still confused ? |
2360
|
|
|
|
|
|
|
# Keep asking for responses until we get our error! |
2361
|
0
|
|
|
|
|
|
$self->command ("xxxxNOOP"); |
2362
|
0
|
|
|
|
|
|
while ( $self->response () == CMD_OK ) { |
2363
|
0
|
|
|
|
|
|
my $tmp = CMD_ERROR; # A no-op command for loop body ... |
2364
|
|
|
|
|
|
|
} |
2365
|
|
|
|
|
|
|
} |
2366
|
|
|
|
|
|
|
# ------------------------------------------------------------------------- |
2367
|
|
|
|
|
|
|
# End hack of CCC command recovery. |
2368
|
|
|
|
|
|
|
# ------------------------------------------------------------------------- |
2369
|
|
|
|
|
|
|
|
2370
|
0
|
|
|
|
|
|
return ( $self->_test_croak ( $ok == CMD_OK ) ); |
2371
|
|
|
|
|
|
|
} |
2372
|
|
|
|
|
|
|
|
2373
|
|
|
|
|
|
|
|
2374
|
|
|
|
|
|
|
#----------------------------------------------------------------------- |
2375
|
|
|
|
|
|
|
# Allow the user to send a random FTP command directly, BE CAREFUL !! |
2376
|
|
|
|
|
|
|
# Since doing unsupported stuff, we can never call croak! |
2377
|
|
|
|
|
|
|
# Also not all unsupported stuff will show up via supported(). |
2378
|
|
|
|
|
|
|
# So all we can do is try to prevent commands known to have side affects. |
2379
|
|
|
|
|
|
|
#----------------------------------------------------------------------- |
2380
|
|
|
|
|
|
|
sub quot { |
2381
|
0
|
|
|
0
|
1
|
|
my $self = shift; |
2382
|
0
|
|
|
|
|
|
my $cmd = shift; |
2383
|
|
|
|
|
|
|
|
2384
|
|
|
|
|
|
|
# Format the command for testing ... |
2385
|
0
|
0
|
|
|
|
|
my $cmd2 = (defined $cmd) ? uc ($cmd) : ""; |
2386
|
0
|
0
|
|
|
|
|
$cmd2 = $1 if ( $cmd2 =~ m/^\s*(\S+)(\s|$)/ ); |
2387
|
|
|
|
|
|
|
|
2388
|
0
|
|
|
|
|
|
my $msg = ""; # Assume all is OK ... |
2389
|
|
|
|
|
|
|
|
2390
|
|
|
|
|
|
|
# The following FTP commands are known to open a data channel |
2391
|
0
|
0
|
0
|
|
|
|
if ( $cmd2 eq "STOR" || $cmd2 eq "RETR" || |
|
|
0
|
0
|
|
|
|
|
|
|
0
|
0
|
|
|
|
|
|
|
0
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
2392
|
|
|
|
|
|
|
$cmd2 eq "NLST" || $cmd2 eq "LIST" || |
2393
|
|
|
|
|
|
|
$cmd2 eq "STOU" || $cmd2 eq "APPE" || |
2394
|
|
|
|
|
|
|
$cmd2 eq "MLSD" ) { |
2395
|
0
|
|
|
|
|
|
$msg = "x23 Data Connections are not supported via quot(). [$cmd2]"; |
2396
|
|
|
|
|
|
|
|
2397
|
|
|
|
|
|
|
} elsif ( $cmd2 eq "CCC" ) { |
2398
|
0
|
|
|
|
|
|
$msg = "x22 Why didn't you call CCC directly via it's interface?"; |
2399
|
|
|
|
|
|
|
|
2400
|
|
|
|
|
|
|
} elsif ( $cmd2 eq "" ) { |
2401
|
0
|
|
|
|
|
|
$msg = "x21 Where is the needed command?"; |
2402
|
0
|
|
|
|
|
|
$cmd = ""; # Making sure it isn't undefined. |
2403
|
|
|
|
|
|
|
|
2404
|
|
|
|
|
|
|
} elsif ( $cmd2 eq "HELP" && |
2405
|
|
|
|
|
|
|
( exists ${*$self}{_FTPSSL_arguments}->{OverrideHELP} || |
2406
|
|
|
|
|
|
|
exists ${*$self}{_FTPSSL_arguments}->{removeHELP} ) ) { |
2407
|
0
|
|
|
|
|
|
$msg = "x20 Why did you try to call HELP after you overrode all calls to it?"; |
2408
|
|
|
|
|
|
|
|
2409
|
|
|
|
|
|
|
} else { |
2410
|
|
|
|
|
|
|
# Strip off leading spaces, some servers choak on them! |
2411
|
0
|
|
|
|
|
|
$cmd =~ s/^\s+//; |
2412
|
|
|
|
|
|
|
} |
2413
|
|
|
|
|
|
|
|
2414
|
0
|
0
|
|
|
|
|
if ( $msg ne "" ) { |
2415
|
0
|
|
|
|
|
|
my $cmd_str = join (" ", $cmd, @_); |
2416
|
0
|
|
|
|
|
|
${*$self}{_FTPSSL_arguments}->{last_ftp_msg} = $msg; |
|
0
|
|
|
|
|
|
|
2417
|
0
|
|
|
|
|
|
$self->_change_status_code (CMD_REJECT); |
2418
|
0
|
|
|
|
|
|
$self->_print_DBG ( ">>+ ", $cmd_str, "\n" ); |
2419
|
0
|
|
|
|
|
|
$self->_print_DBG ( "<<+ ", $self->last_message (), "\n" ); |
2420
|
0
|
|
|
|
|
|
return (CMD_REJECT); |
2421
|
|
|
|
|
|
|
} |
2422
|
|
|
|
|
|
|
|
2423
|
0
|
|
|
|
|
|
return ( $self->command ($cmd, @_)->response () ); |
2424
|
|
|
|
|
|
|
} |
2425
|
|
|
|
|
|
|
|
2426
|
|
|
|
|
|
|
#----------------------------------------------------------------------- |
2427
|
|
|
|
|
|
|
# Type setting function |
2428
|
|
|
|
|
|
|
#----------------------------------------------------------------------- |
2429
|
|
|
|
|
|
|
|
2430
|
|
|
|
|
|
|
sub ascii { |
2431
|
0
|
|
|
0
|
1
|
|
my $self = shift; |
2432
|
0
|
|
|
|
|
|
${*$self}{_FTPSSL_arguments}->{type} = MODE_ASCII; |
|
0
|
|
|
|
|
|
|
2433
|
0
|
|
|
|
|
|
return $self->_test_croak ($self->_type(MODE_ASCII)); |
2434
|
|
|
|
|
|
|
} |
2435
|
|
|
|
|
|
|
|
2436
|
|
|
|
|
|
|
sub binary { |
2437
|
0
|
|
|
0
|
1
|
|
my $self = shift; |
2438
|
0
|
|
|
|
|
|
${*$self}{_FTPSSL_arguments}->{type} = MODE_BINARY; |
|
0
|
|
|
|
|
|
|
2439
|
0
|
|
|
|
|
|
return $self->_test_croak ($self->_type(MODE_BINARY)); |
2440
|
|
|
|
|
|
|
} |
2441
|
|
|
|
|
|
|
|
2442
|
|
|
|
|
|
|
# Server thinks it's ASCII & Client thinks it's BINARY |
2443
|
|
|
|
|
|
|
sub mixedModeAI { |
2444
|
0
|
|
|
0
|
1
|
|
my $self = shift; |
2445
|
0
|
|
|
|
|
|
${*$self}{_FTPSSL_arguments}->{type} = MODE_BINARY; |
|
0
|
|
|
|
|
|
|
2446
|
0
|
|
|
|
|
|
return $self->_test_croak ($self->_type(MODE_ASCII)); |
2447
|
|
|
|
|
|
|
} |
2448
|
|
|
|
|
|
|
|
2449
|
|
|
|
|
|
|
# Server thinks it's BINARY & Client thinks it's ASCII |
2450
|
|
|
|
|
|
|
sub mixedModeIA { |
2451
|
0
|
|
|
0
|
1
|
|
my $self = shift; |
2452
|
0
|
|
|
|
|
|
${*$self}{_FTPSSL_arguments}->{type} = MODE_ASCII; |
|
0
|
|
|
|
|
|
|
2453
|
0
|
|
|
|
|
|
return $self->_test_croak ($self->_type(MODE_BINARY)); |
2454
|
|
|
|
|
|
|
} |
2455
|
|
|
|
|
|
|
|
2456
|
|
|
|
|
|
|
#----------------------------------------------------------------------- |
2457
|
|
|
|
|
|
|
# Internal functions |
2458
|
|
|
|
|
|
|
#----------------------------------------------------------------------- |
2459
|
|
|
|
|
|
|
|
2460
|
|
|
|
|
|
|
sub _xWait { |
2461
|
0
|
|
|
0
|
|
|
my $self = shift; |
2462
|
|
|
|
|
|
|
|
2463
|
0
|
|
|
|
|
|
my $slp = ${*$self}{_FTPSSL_arguments}->{xWait}; |
|
0
|
|
|
|
|
|
|
2464
|
|
|
|
|
|
|
|
2465
|
0
|
0
|
|
|
|
|
if ( $slp ) { |
2466
|
0
|
|
|
|
|
|
$self->_print_DBG ("---- ", "Sleeping ${slp} second(s)\n"); |
2467
|
0
|
|
|
|
|
|
sleep ( $slp ); |
2468
|
|
|
|
|
|
|
} |
2469
|
|
|
|
|
|
|
} |
2470
|
|
|
|
|
|
|
|
2471
|
|
|
|
|
|
|
sub _user { |
2472
|
0
|
|
|
0
|
|
|
my $self = shift; |
2473
|
0
|
|
|
|
|
|
my $resp = $self->command ( "USER", @_ )->response (); |
2474
|
0
|
|
0
|
|
|
|
return ( $resp == CMD_OK || $resp == CMD_MORE ); |
2475
|
|
|
|
|
|
|
} |
2476
|
|
|
|
|
|
|
|
2477
|
|
|
|
|
|
|
sub _passwd { |
2478
|
0
|
|
|
0
|
|
|
my $self = shift; |
2479
|
0
|
|
|
|
|
|
my $resp = $self->command ( "PASS", @_ )->response (); |
2480
|
0
|
|
0
|
|
|
|
return ( $resp == CMD_OK || $resp == CMD_MORE ); |
2481
|
|
|
|
|
|
|
} |
2482
|
|
|
|
|
|
|
|
2483
|
|
|
|
|
|
|
sub _quit { |
2484
|
0
|
|
|
0
|
|
|
my $self = shift; |
2485
|
0
|
|
|
|
|
|
return ( $self->command ("QUIT")->response () == CMD_OK ); |
2486
|
|
|
|
|
|
|
} |
2487
|
|
|
|
|
|
|
|
2488
|
|
|
|
|
|
|
sub _prot { |
2489
|
0
|
|
|
0
|
|
|
my $self = shift; |
2490
|
0
|
|
0
|
|
|
|
my $opt = shift || ${*$self}{_FTPSSL_arguments}->{data_prot}; |
2491
|
|
|
|
|
|
|
|
2492
|
|
|
|
|
|
|
# C, S, E or P. |
2493
|
0
|
|
|
|
|
|
my $resp = ( $self->command ( "PROT", $opt )->response () == CMD_OK ); |
2494
|
|
|
|
|
|
|
|
2495
|
|
|
|
|
|
|
# Check if someone changed the data channel protection mode ... |
2496
|
0
|
0
|
0
|
|
|
|
if ($resp && $opt ne ${*$self}{_FTPSSL_arguments}->{data_prot}) { |
|
0
|
|
|
|
|
|
|
2497
|
0
|
|
|
|
|
|
${*$self}{_FTPSSL_arguments}->{data_prot} = $opt; # They did change it! |
|
0
|
|
|
|
|
|
|
2498
|
|
|
|
|
|
|
} |
2499
|
|
|
|
|
|
|
|
2500
|
0
|
|
|
|
|
|
return ( $resp ); |
2501
|
|
|
|
|
|
|
} |
2502
|
|
|
|
|
|
|
|
2503
|
|
|
|
|
|
|
# Depreciated, only present to make backwards compatible with v0.05 & earlier. |
2504
|
|
|
|
|
|
|
sub _protp { |
2505
|
0
|
|
|
0
|
|
|
my $self = shift; |
2506
|
0
|
|
|
|
|
|
return ($self->_prot (DATA_PROT_PRIVATE)); |
2507
|
|
|
|
|
|
|
} |
2508
|
|
|
|
|
|
|
|
2509
|
|
|
|
|
|
|
sub _pbsz { |
2510
|
0
|
|
|
0
|
|
|
my $self = shift; |
2511
|
0
|
|
|
|
|
|
return ( $self->command ( "PBSZ", "0" )->response () == CMD_OK ); |
2512
|
|
|
|
|
|
|
} |
2513
|
|
|
|
|
|
|
|
2514
|
|
|
|
|
|
|
sub _nlst { |
2515
|
0
|
|
|
0
|
|
|
my $self = shift; |
2516
|
0
|
|
|
|
|
|
return ( $self->command ( "NLST", @_ )->response () == CMD_INFO ); |
2517
|
|
|
|
|
|
|
} |
2518
|
|
|
|
|
|
|
|
2519
|
|
|
|
|
|
|
sub _list { |
2520
|
0
|
|
|
0
|
|
|
my $self = shift; |
2521
|
0
|
|
|
|
|
|
return ( $self->command ( "LIST", @_ )->response () == CMD_INFO ); |
2522
|
|
|
|
|
|
|
} |
2523
|
|
|
|
|
|
|
|
2524
|
|
|
|
|
|
|
sub _type { |
2525
|
0
|
|
|
0
|
|
|
my $self = shift; |
2526
|
0
|
|
|
|
|
|
return ( $self->command ( "TYPE", @_ )->response () == CMD_OK ); |
2527
|
|
|
|
|
|
|
} |
2528
|
|
|
|
|
|
|
|
2529
|
|
|
|
|
|
|
sub _rest { |
2530
|
0
|
|
|
0
|
|
|
my $self = shift; |
2531
|
0
|
|
|
|
|
|
return ( $self->command ( "REST", @_ )->response () == CMD_MORE ); |
2532
|
|
|
|
|
|
|
} |
2533
|
|
|
|
|
|
|
|
2534
|
|
|
|
|
|
|
sub _retr { |
2535
|
0
|
|
|
0
|
|
|
my $self = shift; |
2536
|
0
|
|
|
|
|
|
return ( $self->command ( "RETR", @_ )->response () == CMD_INFO ); |
2537
|
|
|
|
|
|
|
} |
2538
|
|
|
|
|
|
|
|
2539
|
|
|
|
|
|
|
sub _stor { |
2540
|
0
|
|
|
0
|
|
|
my $self = shift; |
2541
|
0
|
|
|
|
|
|
return ( $self->command ( "STOR", @_ )->response () == CMD_INFO ); |
2542
|
|
|
|
|
|
|
} |
2543
|
|
|
|
|
|
|
|
2544
|
|
|
|
|
|
|
sub _appe { |
2545
|
0
|
|
|
0
|
|
|
my $self = shift; |
2546
|
0
|
|
|
|
|
|
return ( $self->command ( "APPE", @_ )->response () == CMD_INFO ); |
2547
|
|
|
|
|
|
|
} |
2548
|
|
|
|
|
|
|
|
2549
|
|
|
|
|
|
|
sub _stou { |
2550
|
0
|
|
|
0
|
|
|
my $self = shift; |
2551
|
|
|
|
|
|
|
|
2552
|
|
|
|
|
|
|
# Works for most non-windows FTPS servers ... |
2553
|
0
|
|
|
|
|
|
${*$self}{_FTPSSL_arguments}->{uput} = 0; # Conditionally uses scratch name. |
|
0
|
|
|
|
|
|
|
2554
|
0
|
|
|
|
|
|
my $res = $self->command ( "STOU", @_ )->response (); |
2555
|
0
|
0
|
|
|
|
|
return ( 1 ) if ( $res == CMD_INFO ); |
2556
|
|
|
|
|
|
|
|
2557
|
|
|
|
|
|
|
# Some windows servers won't allow any arguments ... |
2558
|
|
|
|
|
|
|
# They always use a scratch name! (But don't always return what it is.) |
2559
|
0
|
|
|
|
|
|
my $msg = $self->last_message (); |
2560
|
0
|
0
|
0
|
|
|
|
if ( $res == CMD_ERROR && $msg =~ m/Invalid number of parameters/ ) { |
2561
|
0
|
|
|
|
|
|
${*$self}{_FTPSSL_arguments}->{uput} = 1; # Will always use scratch name. |
|
0
|
|
|
|
|
|
|
2562
|
0
|
|
|
|
|
|
$res = $self->command ( "STOU" )->response (); |
2563
|
|
|
|
|
|
|
} |
2564
|
|
|
|
|
|
|
|
2565
|
0
|
|
|
|
|
|
return ( $res == CMD_INFO ); |
2566
|
|
|
|
|
|
|
} |
2567
|
|
|
|
|
|
|
|
2568
|
|
|
|
|
|
|
sub _abort { |
2569
|
0
|
|
|
0
|
|
|
my $self = shift; |
2570
|
0
|
|
|
|
|
|
return ( $self->command ("ABOR")->response () == CMD_OK ); |
2571
|
|
|
|
|
|
|
} |
2572
|
|
|
|
|
|
|
|
2573
|
|
|
|
|
|
|
sub _alloc { |
2574
|
0
|
|
|
0
|
|
|
my $self = shift; |
2575
|
0
|
|
|
|
|
|
return ( $self->command ( "ALLO", @_ )->response () == CMD_OK ); |
2576
|
|
|
|
|
|
|
} |
2577
|
|
|
|
|
|
|
|
2578
|
|
|
|
|
|
|
sub _rnfr { |
2579
|
0
|
|
|
0
|
|
|
my $self = shift; |
2580
|
0
|
|
|
|
|
|
return ( $self->command ( "RNFR", @_ )->response () == CMD_MORE ); |
2581
|
|
|
|
|
|
|
} |
2582
|
|
|
|
|
|
|
|
2583
|
|
|
|
|
|
|
sub _rnto { |
2584
|
0
|
|
|
0
|
|
|
my $self = shift; |
2585
|
0
|
|
|
|
|
|
return ( $self->command ( "RNTO", @_ )->response () == CMD_OK ); |
2586
|
|
|
|
|
|
|
} |
2587
|
|
|
|
|
|
|
|
2588
|
|
|
|
|
|
|
sub mfmt { |
2589
|
0
|
|
|
0
|
1
|
|
my $self = shift; |
2590
|
0
|
|
|
|
|
|
$self->command( "MFMT", @_ ); |
2591
|
0
|
|
|
|
|
|
return ( $self->_test_croak ($self->response () == CMD_OK) ); |
2592
|
|
|
|
|
|
|
} |
2593
|
|
|
|
|
|
|
|
2594
|
|
|
|
|
|
|
# Uses the PreserveTimestamp kludge! |
2595
|
|
|
|
|
|
|
# If not preserving timestamps, assumes GMT! |
2596
|
|
|
|
|
|
|
sub _mfmt { |
2597
|
0
|
|
|
0
|
|
|
my $self = shift; |
2598
|
0
|
|
|
|
|
|
my $timestamp = shift; # (stat ($loc_file))[9] - The local file's timestamp! |
2599
|
0
|
|
|
|
|
|
my $remote_file = shift; |
2600
|
0
|
|
|
|
|
|
my $local = shift; # True Local / False GMT / Undef use PreserveTimestamp |
2601
|
|
|
|
|
|
|
|
2602
|
|
|
|
|
|
|
# Asks if the FTPS server is using GMT or Local time for the returned timestamp. |
2603
|
0
|
|
|
|
|
|
my $GMT_flag = 1; # Assume GMT ... |
2604
|
0
|
0
|
|
|
|
|
if ( defined $local ) { |
|
|
0
|
|
|
|
|
|
2605
|
0
|
0
|
|
|
|
|
$GMT_flag = $local ? 0 : 1; # Override PreserveTimestamp option ... |
2606
|
0
|
|
|
|
|
|
} elsif ( ${*$self}{_FTPSSL_arguments}->{FixPutTs} < 0 ) { |
2607
|
0
|
|
|
|
|
|
$GMT_flag = 0; # PreserveTimestamp said to use Local Time ... |
2608
|
|
|
|
|
|
|
} |
2609
|
|
|
|
|
|
|
|
2610
|
|
|
|
|
|
|
# Convert it into YYYYMMDDHHMMSS format (GM Time) [ gmtime() vs localtime() ] |
2611
|
0
|
|
|
|
|
|
my ($sec, $min, $hr, $day, $mon, $yr, $wday, $yday, $isdst); |
2612
|
|
|
|
|
|
|
|
2613
|
|
|
|
|
|
|
# Using perl's built-in functions here. (years offset of 1900.) |
2614
|
0
|
0
|
|
|
|
|
if ( $GMT_flag ) { |
2615
|
|
|
|
|
|
|
# Use GMT Time [ gmtime() vs timegm() ] |
2616
|
0
|
|
|
|
|
|
($sec, $min, $hr, $day, $mon, $yr, $wday, $yday, $isdst) = |
2617
|
|
|
|
|
|
|
gmtime ( $timestamp ); |
2618
|
|
|
|
|
|
|
} else { |
2619
|
|
|
|
|
|
|
# Use Local Time [ localtime() vs timelocal() ] |
2620
|
0
|
|
|
|
|
|
($sec, $min, $hr, $day, $mon, $yr, $wday, $yday, $isdst) = |
2621
|
|
|
|
|
|
|
localtime ( $timestamp ); |
2622
|
|
|
|
|
|
|
} |
2623
|
|
|
|
|
|
|
|
2624
|
0
|
|
|
|
|
|
my $time = sprintf ("%04d%02d%02d%02d%02d%02d", |
2625
|
|
|
|
|
|
|
$yr + 1900, $mon + 1, $day, $hr, $min, $sec); |
2626
|
|
|
|
|
|
|
|
2627
|
|
|
|
|
|
|
# Upload the file's new timestamp ... |
2628
|
0
|
|
|
|
|
|
return ( $self->command ( "MFMT", $time, $remote_file )->response () == CMD_OK ); |
2629
|
|
|
|
|
|
|
} |
2630
|
|
|
|
|
|
|
|
2631
|
|
|
|
|
|
|
|
2632
|
|
|
|
|
|
|
# Parses the remote FTPS server's response for the file's timestamp! |
2633
|
|
|
|
|
|
|
# Now in a separate function due to server bug reported by Net::FTP! |
2634
|
|
|
|
|
|
|
# Returns: undef or the timestamp in YYYYMMDDHHMMSS (len 14) format! |
2635
|
|
|
|
|
|
|
sub _internal_mdtm_parse { |
2636
|
0
|
|
|
0
|
|
|
my $self = shift; |
2637
|
|
|
|
|
|
|
|
2638
|
|
|
|
|
|
|
# Get the message returned by the FTPS server for the MDTM command ... |
2639
|
0
|
|
|
|
|
|
my $msg = ${*$self}{_FTPSSL_arguments}->{last_ftp_msg}; |
|
0
|
|
|
|
|
|
|
2640
|
|
|
|
|
|
|
|
2641
|
0
|
|
|
|
|
|
my $gmt_time_str; |
2642
|
|
|
|
|
|
|
|
2643
|
|
|
|
|
|
|
# Check for the expected timestamp format ... |
2644
|
0
|
0
|
|
|
|
|
if ( $msg =~ m/(^|\D)(\d{14})($|\D)/ ) { |
|
|
0
|
|
|
|
|
|
2645
|
0
|
|
|
|
|
|
$gmt_time_str = $2; # The timestamp on the remote server: YYYYMMDDHHMMSS. |
2646
|
|
|
|
|
|
|
|
2647
|
|
|
|
|
|
|
# According to Net::FTP, some idiotic FTP server bug used |
2648
|
|
|
|
|
|
|
# ("19%d", tm.tm_year") instead of ("%d", tm.tm_year+1900) |
2649
|
|
|
|
|
|
|
# to format the year. So converting it into the expected format! |
2650
|
|
|
|
|
|
|
# This way this bug isn't propagated outside this function! |
2651
|
|
|
|
|
|
|
# Fix doesn't work for dates before 1-1-1910, which should never happen! |
2652
|
|
|
|
|
|
|
} elsif ( $msg =~ m/(^|\D)19(\d{3})(\d{10})($|\D)/ ) { |
2653
|
0
|
|
|
|
|
|
my ( $yr, $rest ) = ( $2, $3 ); |
2654
|
|
|
|
|
|
|
|
2655
|
|
|
|
|
|
|
# The corrected date ... |
2656
|
0
|
|
|
|
|
|
$gmt_time_str = sprintf ("%04d%s", 1900 + $yr, $rest); |
2657
|
|
|
|
|
|
|
|
2658
|
0
|
|
|
|
|
|
$self->_print_DBG ("---- ", "Bad Year: 19${yr}${rest}! ", |
2659
|
|
|
|
|
|
|
"Converting to ${gmt_time_str}\n"); |
2660
|
|
|
|
|
|
|
} |
2661
|
|
|
|
|
|
|
|
2662
|
0
|
|
|
|
|
|
return ( $gmt_time_str ); |
2663
|
|
|
|
|
|
|
} |
2664
|
|
|
|
|
|
|
|
2665
|
|
|
|
|
|
|
sub mdtm { |
2666
|
0
|
|
|
0
|
1
|
|
my $self = shift; |
2667
|
|
|
|
|
|
|
|
2668
|
0
|
|
|
|
|
|
my $gmt_time_str; |
2669
|
|
|
|
|
|
|
|
2670
|
0
|
0
|
|
|
|
|
if ( $self->command( "MDTM", @_ )-> response () == CMD_OK ) { |
2671
|
0
|
|
|
|
|
|
$gmt_time_str = $self->_internal_mdtm_parse (); |
2672
|
|
|
|
|
|
|
} |
2673
|
|
|
|
|
|
|
|
2674
|
0
|
|
|
|
|
|
return ( $self->_test_croak ($gmt_time_str) ); # In GMT time ... |
2675
|
|
|
|
|
|
|
} |
2676
|
|
|
|
|
|
|
|
2677
|
|
|
|
|
|
|
# Uses the PreserveTimestamp kludge! |
2678
|
|
|
|
|
|
|
# If not preserving timestamps, assumes GMT! |
2679
|
|
|
|
|
|
|
sub _mdtm { |
2680
|
0
|
|
|
0
|
|
|
my $self = shift; |
2681
|
0
|
|
|
|
|
|
my $remote_file = shift; |
2682
|
0
|
|
|
|
|
|
my $local = shift; # True Local / False GMT / Undef use PreserveTimestamp |
2683
|
|
|
|
|
|
|
|
2684
|
0
|
|
|
|
|
|
my $timestamp; # The return value ... |
2685
|
|
|
|
|
|
|
|
2686
|
|
|
|
|
|
|
# Asks if the FTPS server is using GMT or Local time for the returned timestamp. |
2687
|
0
|
|
|
|
|
|
my $GMT_flag = 1; # Assume GMT ... |
2688
|
0
|
0
|
|
|
|
|
if ( defined $local ) { |
|
|
0
|
|
|
|
|
|
2689
|
0
|
0
|
|
|
|
|
$GMT_flag = $local ? 0 : 1; # Override PreserveTimestamp option ... |
2690
|
0
|
|
|
|
|
|
} elsif ( ${*$self}{_FTPSSL_arguments}->{FixGetTs} < 0 ) { |
2691
|
0
|
|
|
|
|
|
$GMT_flag = 0; # PreserveTimestamp said to use Local Time ... |
2692
|
|
|
|
|
|
|
} |
2693
|
|
|
|
|
|
|
|
2694
|
|
|
|
|
|
|
# Collect the timestamp from the FTPS server ... |
2695
|
0
|
0
|
|
|
|
|
if ( $self->command ("MDTM", $remote_file)->response () == CMD_OK ) { |
2696
|
0
|
|
|
|
|
|
my $time_str = $self->_internal_mdtm_parse (); |
2697
|
|
|
|
|
|
|
|
2698
|
|
|
|
|
|
|
# Now convert it into the internal format used by Perl ... |
2699
|
0
|
0
|
0
|
|
|
|
if ( defined $time_str && |
2700
|
|
|
|
|
|
|
$time_str =~ m/^(\d{4})(\d{2})(\d{2})(\d{2})(\d{2})(\d{2})$/ ) { |
2701
|
0
|
|
|
|
|
|
my ($yr, $mon, $day, $hr, $min, $sec) = ($1, $2, $3, $4, $5, $6); |
2702
|
|
|
|
|
|
|
|
2703
|
|
|
|
|
|
|
# Using Time::Local functions here. |
2704
|
|
|
|
|
|
|
# (Not a true inverse of the built-in funcs with regards to the year.) |
2705
|
0
|
0
|
|
|
|
|
if ( $GMT_flag ) { |
2706
|
|
|
|
|
|
|
# Use GMT Time [ timegm() vs gmtime() ] |
2707
|
0
|
|
|
|
|
|
$timestamp = timegm ( $sec, $min, $hr, $day, $mon - 1, $yr ); |
2708
|
|
|
|
|
|
|
} else { |
2709
|
|
|
|
|
|
|
# Use Local Time [ timelocal() vs localtime() ] |
2710
|
0
|
|
|
|
|
|
$timestamp = timelocal ( $sec, $min, $hr, $day, $mon - 1, $yr ); |
2711
|
|
|
|
|
|
|
} |
2712
|
|
|
|
|
|
|
} |
2713
|
|
|
|
|
|
|
} |
2714
|
|
|
|
|
|
|
|
2715
|
0
|
|
|
|
|
|
return ( $timestamp ); |
2716
|
|
|
|
|
|
|
} |
2717
|
|
|
|
|
|
|
|
2718
|
|
|
|
|
|
|
sub size { |
2719
|
0
|
|
|
0
|
1
|
|
my $self = shift; |
2720
|
0
|
|
|
|
|
|
my $file = shift; |
2721
|
|
|
|
|
|
|
|
2722
|
0
|
0
|
|
|
|
|
if ( $self->supported ("SIZE") ) { |
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
2723
|
0
|
0
|
0
|
|
|
|
if ( $self->command ("SIZE", $file, @_)->response () == CMD_OK && |
2724
|
|
|
|
|
|
|
$self->message () =~ m/\d+\s+(\d+)($|\D)/ ) { |
2725
|
0
|
|
|
|
|
|
return ( $1 ); # The size in bytes! May be zero! |
2726
|
|
|
|
|
|
|
} |
2727
|
|
|
|
|
|
|
|
2728
|
|
|
|
|
|
|
# Note: If $file is in fact a directory, STAT will return the directory's |
2729
|
|
|
|
|
|
|
# contents! Which can be very slow if there are tons of files in the dir! |
2730
|
|
|
|
|
|
|
} elsif ( $self->supported ("STAT") ) { |
2731
|
0
|
0
|
|
|
|
|
if ( $self->command ("STAT", $file, @_)->response () == CMD_OK ) { |
2732
|
0
|
|
|
|
|
|
my @msg = split ("\n", $self->message ()); |
2733
|
0
|
|
|
|
|
|
my $cnt = @msg; |
2734
|
0
|
|
|
|
|
|
my $rFile = $self->_mask_regex_chars ( basename ($file) ); |
2735
|
|
|
|
|
|
|
|
2736
|
|
|
|
|
|
|
# ... Size Filename |
2737
|
0
|
0
|
0
|
|
|
|
if ( $cnt == 3 && $msg[1] =~ m/\s(\d+)\s+${rFile}/ ) { |
2738
|
0
|
|
|
|
|
|
return ( $1 ); # The size in bytes! May be zero! |
2739
|
|
|
|
|
|
|
} |
2740
|
|
|
|
|
|
|
# ... Size Month Day HH:MM Filename |
2741
|
0
|
0
|
0
|
|
|
|
if ( $cnt == 3 && $msg[1] =~ m/\s(\d+)\s+(\S+)\s+(\d+)\s+(\d+:\d+)\s+${rFile}/ ) { |
2742
|
0
|
|
|
|
|
|
return ( $1 ); # The size in bytes! May be zero! |
2743
|
|
|
|
|
|
|
} |
2744
|
|
|
|
|
|
|
} |
2745
|
|
|
|
|
|
|
|
2746
|
|
|
|
|
|
|
# Can be less accurate than the other two methods ... |
2747
|
|
|
|
|
|
|
} elsif ( $self->all_supported ("MLST", "OPTS") ) { |
2748
|
|
|
|
|
|
|
# On the todo list ... |
2749
|
|
|
|
|
|
|
# Must use "OPTS MLST SIZE" if the size fact is currently disabled. |
2750
|
|
|
|
|
|
|
} |
2751
|
|
|
|
|
|
|
|
2752
|
0
|
|
|
|
|
|
return ( $self->_test_croak (undef) ); # It's not a regular file! |
2753
|
|
|
|
|
|
|
} |
2754
|
|
|
|
|
|
|
|
2755
|
|
|
|
|
|
|
sub is_file { |
2756
|
0
|
|
|
0
|
1
|
|
my $self = shift; |
2757
|
0
|
|
|
|
|
|
my $file = shift; |
2758
|
|
|
|
|
|
|
|
2759
|
|
|
|
|
|
|
# Now let's disable Croak so we can't die during this test ... |
2760
|
0
|
|
|
|
|
|
my $die = $self->set_croak (0); |
2761
|
|
|
|
|
|
|
|
2762
|
0
|
|
|
|
|
|
my $size = $self->size ( $file ); |
2763
|
|
|
|
|
|
|
|
2764
|
0
|
|
|
|
|
|
$self->set_croak ( $die ); # Restore the croak settings! |
2765
|
|
|
|
|
|
|
|
2766
|
0
|
0
|
0
|
|
|
|
if ( defined $size && $size >= 0 ) { |
2767
|
0
|
|
|
|
|
|
return ( 1 ); # It's a plain file! We successfully got it's size! |
2768
|
|
|
|
|
|
|
} |
2769
|
|
|
|
|
|
|
|
2770
|
0
|
|
|
|
|
|
return ( 0 ); # It's not a plain file or it doesn't exist! |
2771
|
|
|
|
|
|
|
} |
2772
|
|
|
|
|
|
|
|
2773
|
|
|
|
|
|
|
sub is_dir { |
2774
|
0
|
|
|
0
|
1
|
|
my $self = shift; |
2775
|
0
|
|
|
|
|
|
my $dir = shift; |
2776
|
|
|
|
|
|
|
|
2777
|
0
|
|
|
|
|
|
my $isDir = 0; # Assume not a directory ... |
2778
|
|
|
|
|
|
|
|
2779
|
|
|
|
|
|
|
# The current direcory! |
2780
|
0
|
|
|
|
|
|
my $curDir = $self->pwd (); |
2781
|
|
|
|
|
|
|
|
2782
|
|
|
|
|
|
|
# Now let's disable Croak so we can't die during this test ... |
2783
|
0
|
|
|
|
|
|
my $die = $self->set_croak (0); |
2784
|
|
|
|
|
|
|
|
2785
|
|
|
|
|
|
|
# Check if it's a directory we have access to ... |
2786
|
0
|
0
|
|
|
|
|
if ( $self->cwd ( $dir ) ) { |
2787
|
0
|
|
|
|
|
|
$self->cwd ( $curDir ); |
2788
|
0
|
|
|
|
|
|
$isDir = 1; |
2789
|
|
|
|
|
|
|
|
2790
|
|
|
|
|
|
|
} else { |
2791
|
|
|
|
|
|
|
# At this point if it's really a directory, we don't have access to it. |
2792
|
|
|
|
|
|
|
# And parsing error messages really isn't an option. |
2793
|
|
|
|
|
|
|
|
2794
|
|
|
|
|
|
|
# So what if we now assume it it might be a directory if "is_file()" |
2795
|
|
|
|
|
|
|
# returns false and we can see that the file does exists via "nlst()"? |
2796
|
|
|
|
|
|
|
|
2797
|
|
|
|
|
|
|
# I don't really like that no-access test, too many chances for false |
2798
|
|
|
|
|
|
|
# positives, so I'm open to better ideas! I'll leave this code disabled |
2799
|
|
|
|
|
|
|
# until I can mull this over some more. |
2800
|
|
|
|
|
|
|
|
2801
|
|
|
|
|
|
|
# Currently disabled ... |
2802
|
0
|
|
|
|
|
|
if ( 1 != 1 ) { |
2803
|
|
|
|
|
|
|
# If it isn't a regular file, then it might yet still be a directory! |
2804
|
|
|
|
|
|
|
unless ( $self->is_file ( $dir ) ) { |
2805
|
|
|
|
|
|
|
# Now check if we can see a file of this name ... |
2806
|
|
|
|
|
|
|
my @lst = $self->nlst (dirname ($dir), basename ($dir)); |
2807
|
|
|
|
|
|
|
if ( scalar (@lst) ) { |
2808
|
|
|
|
|
|
|
# It may or may not be a directory ... |
2809
|
|
|
|
|
|
|
$self->_print_DBG ("--- Found match: ", $lst[0], "\n"); |
2810
|
|
|
|
|
|
|
$isDir = 1; |
2811
|
|
|
|
|
|
|
} |
2812
|
|
|
|
|
|
|
} |
2813
|
|
|
|
|
|
|
} |
2814
|
|
|
|
|
|
|
} |
2815
|
|
|
|
|
|
|
|
2816
|
0
|
|
|
|
|
|
$self->set_croak ( $die ); # Restore the croak settings! |
2817
|
|
|
|
|
|
|
|
2818
|
0
|
|
|
|
|
|
return ( $isDir ); |
2819
|
|
|
|
|
|
|
} |
2820
|
|
|
|
|
|
|
|
2821
|
|
|
|
|
|
|
sub copy_cc_to_dc { |
2822
|
0
|
|
|
0
|
1
|
|
my $self = shift; |
2823
|
0
|
0
|
|
|
|
|
my $args = (ref ($_[0]) eq "ARRAY") ? $_[0] : \@_; |
2824
|
|
|
|
|
|
|
|
2825
|
0
|
|
|
|
|
|
my %dcValues; |
2826
|
0
|
0
|
|
|
|
|
if ( exists ${*$self}{_FTPSSL_arguments}->{myContext} ) { |
|
0
|
|
|
|
|
|
|
2827
|
0
|
|
|
|
|
|
%dcValues = %{${*$self}{_FTPSSL_arguments}->{myContext}}; |
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
2828
|
|
|
|
|
|
|
} |
2829
|
|
|
|
|
|
|
|
2830
|
0
|
|
|
|
|
|
my $cnt = 0; |
2831
|
0
|
|
|
|
|
|
foreach ( @{$args} ) { |
|
0
|
|
|
|
|
|
|
2832
|
0
|
|
|
|
|
|
my $val; |
2833
|
0
|
0
|
|
|
|
|
if ( exists ${*$self}{_SSL_arguments}->{$_} ) { |
|
0
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
2834
|
0
|
|
|
|
|
|
$val = ${*$self}{_SSL_arguments}->{$_}; |
|
0
|
|
|
|
|
|
|
2835
|
|
|
|
|
|
|
|
2836
|
0
|
|
|
|
|
|
} elsif ( exists ${*$self}{_FTPSSL_arguments}->{start_SSL_opts}->{$_} ) { |
2837
|
0
|
|
|
|
|
|
$val = ${*$self}{_FTPSSL_arguments}->{start_SSL_opts}->{$_}; |
|
0
|
|
|
|
|
|
|
2838
|
|
|
|
|
|
|
|
2839
|
0
|
|
|
|
|
|
} elsif ( exists ${*$self}{$_} ) { |
2840
|
0
|
|
|
|
|
|
$val = ${*$self}{$_}; |
|
0
|
|
|
|
|
|
|
2841
|
|
|
|
|
|
|
|
2842
|
|
|
|
|
|
|
} else { |
2843
|
0
|
|
|
|
|
|
$self->_print_DBG ("No such Key defined for the CC: ", $_, "\n"); |
2844
|
0
|
|
|
|
|
|
next; |
2845
|
|
|
|
|
|
|
} |
2846
|
|
|
|
|
|
|
|
2847
|
0
|
|
|
|
|
|
$dcValues{$_} = $val; |
2848
|
0
|
|
|
|
|
|
++$cnt; |
2849
|
|
|
|
|
|
|
} |
2850
|
|
|
|
|
|
|
|
2851
|
|
|
|
|
|
|
# Update with the new Data Channel options ... |
2852
|
0
|
0
|
|
|
|
|
if ( $cnt > 0 ) { |
2853
|
0
|
|
|
|
|
|
${*$self}{_FTPSSL_arguments}->{myContext} = \%dcValues; |
|
0
|
|
|
|
|
|
|
2854
|
|
|
|
|
|
|
} |
2855
|
|
|
|
|
|
|
|
2856
|
0
|
0
|
|
|
|
|
if ( ${*$self}{_FTPSSL_arguments}->{debug} ) { |
|
0
|
|
|
|
|
|
|
2857
|
0
|
|
|
|
|
|
$self->_debug_print_hash ( "DC Hash", "options", "cc2dc($cnt)", \%dcValues, "#" ); |
2858
|
|
|
|
|
|
|
} |
2859
|
|
|
|
|
|
|
|
2860
|
0
|
|
|
|
|
|
return ( $cnt ); |
2861
|
|
|
|
|
|
|
} |
2862
|
|
|
|
|
|
|
|
2863
|
|
|
|
|
|
|
sub set_dc_from_hash { |
2864
|
0
|
|
|
0
|
1
|
|
my $self = shift; |
2865
|
0
|
0
|
|
|
|
|
my $args = (ref ($_[0]) eq "HASH") ? $_[0] : {@_}; |
2866
|
|
|
|
|
|
|
|
2867
|
0
|
|
|
|
|
|
my %dcValues; |
2868
|
0
|
0
|
|
|
|
|
if ( exists ${*$self}{_FTPSSL_arguments}->{myContext} ) { |
|
0
|
|
|
|
|
|
|
2869
|
0
|
|
|
|
|
|
%dcValues = %{${*$self}{_FTPSSL_arguments}->{myContext}}; |
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
2870
|
|
|
|
|
|
|
} |
2871
|
|
|
|
|
|
|
|
2872
|
0
|
|
|
|
|
|
my $cnt = 0; |
2873
|
0
|
|
|
|
|
|
foreach my $key ( keys %{$args} ) { |
|
0
|
|
|
|
|
|
|
2874
|
0
|
|
|
|
|
|
my $val = $args->{$key}; |
2875
|
|
|
|
|
|
|
|
2876
|
0
|
0
|
|
|
|
|
if ( defined $val ) { |
|
|
0
|
|
|
|
|
|
2877
|
|
|
|
|
|
|
# Add the requested value to the DC hash ... |
2878
|
0
|
|
|
|
|
|
$dcValues{$key} = $val; |
2879
|
0
|
|
|
|
|
|
++$cnt; |
2880
|
|
|
|
|
|
|
|
2881
|
|
|
|
|
|
|
} elsif ( exists $dcValues{$key} ) { |
2882
|
|
|
|
|
|
|
# Delete the requested value from the DC hash ... |
2883
|
0
|
|
|
|
|
|
delete $dcValues{$key}; |
2884
|
0
|
|
|
|
|
|
++$cnt; |
2885
|
|
|
|
|
|
|
} |
2886
|
|
|
|
|
|
|
} |
2887
|
|
|
|
|
|
|
|
2888
|
|
|
|
|
|
|
# Update with the new Data Channel options ... |
2889
|
0
|
0
|
|
|
|
|
if ( $cnt > 0 ) { |
2890
|
0
|
|
|
|
|
|
${*$self}{_FTPSSL_arguments}->{myContext} = \%dcValues; |
|
0
|
|
|
|
|
|
|
2891
|
|
|
|
|
|
|
} |
2892
|
|
|
|
|
|
|
|
2893
|
0
|
0
|
|
|
|
|
if ( ${*$self}{_FTPSSL_arguments}->{debug} ) { |
|
0
|
|
|
|
|
|
|
2894
|
0
|
|
|
|
|
|
$self->_debug_print_hash ( "DC Hash", "options", "setdc($cnt)", \%dcValues, "%" ); |
2895
|
|
|
|
|
|
|
} |
2896
|
|
|
|
|
|
|
|
2897
|
0
|
|
|
|
|
|
return ( $cnt ); |
2898
|
|
|
|
|
|
|
} |
2899
|
|
|
|
|
|
|
|
2900
|
|
|
|
|
|
|
#----------------------------------------------------------------------- |
2901
|
|
|
|
|
|
|
# Checks what commands are available on the remote server |
2902
|
|
|
|
|
|
|
# If a "*" follows a command, it's unimplemented! |
2903
|
|
|
|
|
|
|
# The caller is free to modify the returned hash refrence. |
2904
|
|
|
|
|
|
|
# It's just a copy of what's been cached, not the original! |
2905
|
|
|
|
|
|
|
#----------------------------------------------------------------------- |
2906
|
|
|
|
|
|
|
# Please remember that when OverrideHELP=>1 is used, it will always |
2907
|
|
|
|
|
|
|
# return the empty hash!!! |
2908
|
|
|
|
|
|
|
#----------------------------------------------------------------------- |
2909
|
|
|
|
|
|
|
|
2910
|
|
|
|
|
|
|
sub _help { |
2911
|
|
|
|
|
|
|
# Only shift off self, bug otherwise! |
2912
|
0
|
|
|
0
|
|
|
my $self = shift; |
2913
|
0
|
|
0
|
|
|
|
my $cmd = uc ($_[0] || ""); # Converts undef to "". (Do not do a shift!) |
2914
|
|
|
|
|
|
|
|
2915
|
|
|
|
|
|
|
# Check if requesting a list of all commands or details on specific commands. |
2916
|
0
|
|
|
|
|
|
my $all_cmds = ($cmd eq ""); |
2917
|
0
|
|
|
|
|
|
my $site_cmd = ($cmd eq "SITE"); |
2918
|
|
|
|
|
|
|
|
2919
|
0
|
|
|
|
|
|
my %help; |
2920
|
0
|
|
|
|
|
|
my $arg = ${*$self}{_FTPSSL_arguments}; |
|
0
|
|
|
|
|
|
|
2921
|
|
|
|
|
|
|
|
2922
|
|
|
|
|
|
|
# Only possible if _help() is called before 1st call to supported()! |
2923
|
0
|
0
|
0
|
|
|
|
unless ( $all_cmds || exists $arg->{help_cmds_msg} ) { |
2924
|
0
|
|
|
|
|
|
$self->_help (); |
2925
|
|
|
|
|
|
|
} |
2926
|
|
|
|
|
|
|
|
2927
|
|
|
|
|
|
|
# Use FEAT instead of HELP to populate the supported hash! |
2928
|
|
|
|
|
|
|
# Assuming the HELP command itself is broken! "via OverrideHELP=>-1" |
2929
|
0
|
0
|
0
|
|
|
|
if ( exists $arg->{removeHELP} && $arg->{removeHELP} == 1 ) { |
|
|
0
|
|
|
|
|
|
2930
|
0
|
|
|
|
|
|
$arg->{help_cmds_no_syntax_available} = 1; |
2931
|
|
|
|
|
|
|
|
2932
|
0
|
|
|
|
|
|
my $ft = $self->_feat (); |
2933
|
0
|
0
|
|
|
|
|
$ft->{FEAT} = 2 if (scalar (keys %{$ft}) > 0); |
|
0
|
|
|
|
|
|
|
2934
|
0
|
|
|
|
|
|
foreach ( keys %{$ft} ) { $ft->{$_} = 2; } # So always TRUE |
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
2935
|
|
|
|
|
|
|
|
2936
|
0
|
|
|
|
|
|
$arg->{help_cmds_found} = $ft; |
2937
|
0
|
|
|
|
|
|
$arg->{help_cmds_msg} = $self->last_message (); |
2938
|
0
|
|
|
|
|
|
$self->_site_help ( $arg->{help_cmds_found} ); |
2939
|
0
|
|
|
|
|
|
$arg->{removeHELP} = 2; # So won't execute again ... |
2940
|
|
|
|
|
|
|
|
2941
|
|
|
|
|
|
|
# For the other supported variants to OverrideHELP! |
2942
|
|
|
|
|
|
|
} elsif ( exists $arg->{OverrideHELP} ) { |
2943
|
0
|
|
|
|
|
|
$arg->{help_cmds_no_syntax_available} = 1; |
2944
|
|
|
|
|
|
|
} |
2945
|
|
|
|
|
|
|
|
2946
|
|
|
|
|
|
|
# Now see if we've cached any results previously ... |
2947
|
0
|
|
|
|
|
|
my $key; |
2948
|
0
|
0
|
0
|
|
|
|
if ($all_cmds && exists $arg->{help_cmds_msg}) { |
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
2949
|
0
|
|
|
|
|
|
$arg->{last_ftp_msg} = $arg->{help_cmds_msg}; |
2950
|
0
|
|
|
|
|
|
$key = "help_cmds_found"; |
2951
|
0
|
0
|
|
|
|
|
%help = %{$arg->{$key}} if ( exists $arg->{$key} ); |
|
0
|
|
|
|
|
|
|
2952
|
0
|
|
|
|
|
|
return ( \%help ); |
2953
|
|
|
|
|
|
|
|
2954
|
|
|
|
|
|
|
} elsif (exists $arg->{"help_${cmd}_msg"}) { |
2955
|
0
|
|
|
|
|
|
$arg->{last_ftp_msg} = $arg->{"help_${cmd}_msg"}; |
2956
|
0
|
|
|
|
|
|
$key = "help_${cmd}_found"; |
2957
|
0
|
0
|
|
|
|
|
%help = %{$arg->{$key}} if ( exists $arg->{$key} ); |
|
0
|
|
|
|
|
|
|
2958
|
0
|
|
|
|
|
|
return ( \%help ); |
2959
|
|
|
|
|
|
|
|
2960
|
|
|
|
|
|
|
} elsif ( exists $arg->{help_cmds_no_syntax_available} ) { |
2961
|
0
|
0
|
0
|
|
|
|
if ( exists $arg->{help_cmds_found}->{$cmd} || $arg->{OverrideHELP} ) { |
2962
|
0
|
|
|
|
|
|
$arg->{last_ftp_msg} = "503 Syntax for ${cmd} is not available."; |
2963
|
|
|
|
|
|
|
} else { |
2964
|
0
|
|
|
|
|
|
$arg->{last_ftp_msg} = "501 Unknown command ${cmd}."; |
2965
|
|
|
|
|
|
|
} |
2966
|
|
|
|
|
|
|
# $self->_print_DBG ( "<<+ ", $self->last_message (), "\n" ); |
2967
|
0
|
|
|
|
|
|
return ( \%help ); # The empty hash ... |
2968
|
|
|
|
|
|
|
} |
2969
|
|
|
|
|
|
|
|
2970
|
|
|
|
|
|
|
# From here on out, we will get at least one server hit ... |
2971
|
|
|
|
|
|
|
|
2972
|
0
|
|
|
|
|
|
my $sts; |
2973
|
0
|
0
|
|
|
|
|
if ($all_cmds) { |
2974
|
0
|
|
|
|
|
|
$sts = $self->command ("HELP")->response (); |
2975
|
0
|
|
|
|
|
|
$arg->{help_cmds_msg} = $self->last_message (); |
2976
|
0
|
0
|
|
|
|
|
$arg->{help_cmds_no_syntax_available} = 1 if ( $sts != CMD_OK ); |
2977
|
|
|
|
|
|
|
} else { |
2978
|
0
|
|
|
|
|
|
$sts = $self->command ("HELP", @_)->response (); |
2979
|
0
|
|
|
|
|
|
$arg->{"help_${cmd}_msg"} = $self->last_message (); |
2980
|
|
|
|
|
|
|
} |
2981
|
|
|
|
|
|
|
|
2982
|
|
|
|
|
|
|
# If failure, return empty hash ... |
2983
|
0
|
0
|
|
|
|
|
return (\%help) if ( $sts != CMD_OK ); |
2984
|
|
|
|
|
|
|
|
2985
|
|
|
|
|
|
|
# Check if "HELP" & "HELP CMD" return the same thing ... |
2986
|
0
|
0
|
0
|
|
|
|
if ( (! $all_cmds) && $arg->{help_cmds_msg} eq $self->last_message () ) { |
2987
|
0
|
|
|
|
|
|
$arg->{help_cmds_no_syntax_available} = 1; |
2988
|
0
|
|
|
|
|
|
delete $arg->{"help_${cmd}_msg"}; # Delete this wrong message ... |
2989
|
0
|
|
|
|
|
|
return ( $self->_help ($cmd) ); # Recursive to get the right error msg! |
2990
|
|
|
|
|
|
|
} |
2991
|
|
|
|
|
|
|
|
2992
|
|
|
|
|
|
|
# HELP ... |
2993
|
0
|
0
|
|
|
|
|
if ( $all_cmds ) { |
|
|
0
|
|
|
|
|
|
2994
|
0
|
|
|
|
|
|
%help = %{$self->_help_parse ()}; |
|
0
|
|
|
|
|
|
|
2995
|
|
|
|
|
|
|
|
2996
|
|
|
|
|
|
|
# If we don't find anything, it's a problem. So don't cache if false ... |
2997
|
0
|
0
|
|
|
|
|
if (scalar (keys %help) > 0) { |
2998
|
0
|
0
|
|
|
|
|
if ($help{FEAT}) { |
2999
|
|
|
|
|
|
|
# Now put any features into the help response as well ... |
3000
|
0
|
|
|
|
|
|
my $feat = $self->_feat (); |
3001
|
0
|
|
|
|
|
|
foreach (keys %{$feat}) { |
|
0
|
|
|
|
|
|
|
3002
|
0
|
0
|
|
|
|
|
$help{$_} = 2 unless ($help{$_}); |
3003
|
|
|
|
|
|
|
} |
3004
|
|
|
|
|
|
|
} |
3005
|
|
|
|
|
|
|
|
3006
|
0
|
|
|
|
|
|
my %siteHelp; |
3007
|
|
|
|
|
|
|
my $msg; |
3008
|
0
|
0
|
|
|
|
|
if ($help{SITE}) { |
3009
|
|
|
|
|
|
|
# See if this returns a usage statement or a list of SITE commands! |
3010
|
0
|
|
|
|
|
|
%siteHelp = %{$self->_help ("SITE")}; |
|
0
|
|
|
|
|
|
|
3011
|
0
|
0
|
|
|
|
|
$msg = $self->message () if ( $self->last_status_code() == CMD_OK ); |
3012
|
|
|
|
|
|
|
} |
3013
|
|
|
|
|
|
|
|
3014
|
|
|
|
|
|
|
# Do only if no SITE details yet ... |
3015
|
0
|
0
|
|
|
|
|
if (scalar (keys %siteHelp) == 0) { |
3016
|
0
|
|
|
|
|
|
$self->_site_help (\%help, $msg); |
3017
|
|
|
|
|
|
|
} |
3018
|
|
|
|
|
|
|
|
3019
|
0
|
|
|
|
|
|
my %lclHelp = %help; |
3020
|
0
|
|
|
|
|
|
$arg->{help_cmds_found} = \%lclHelp; |
3021
|
|
|
|
|
|
|
} |
3022
|
|
|
|
|
|
|
|
3023
|
|
|
|
|
|
|
# HELP SITE ... |
3024
|
|
|
|
|
|
|
} elsif ( $site_cmd ) { |
3025
|
0
|
|
|
|
|
|
%help = %{$self->_help_parse (1)}; |
|
0
|
|
|
|
|
|
|
3026
|
|
|
|
|
|
|
|
3027
|
|
|
|
|
|
|
# If we find something, it means it's returning the list of SITE commands. |
3028
|
|
|
|
|
|
|
# Some servers do this rather than returning a syntax statement. |
3029
|
0
|
0
|
|
|
|
|
if (scalar (keys %help) > 0) { |
3030
|
0
|
|
|
|
|
|
my %siteHelp = %help; |
3031
|
0
|
|
|
|
|
|
$arg->{help_SITE_found} = \%siteHelp; |
3032
|
|
|
|
|
|
|
} |
3033
|
|
|
|
|
|
|
|
3034
|
|
|
|
|
|
|
# HELP some_other_command ... |
3035
|
|
|
|
|
|
|
} else { |
3036
|
|
|
|
|
|
|
# Nothing really to do here ... |
3037
|
|
|
|
|
|
|
} |
3038
|
|
|
|
|
|
|
|
3039
|
0
|
|
|
|
|
|
return (\%help); |
3040
|
|
|
|
|
|
|
} |
3041
|
|
|
|
|
|
|
|
3042
|
|
|
|
|
|
|
#--------------------------------------------------------------------------- |
3043
|
|
|
|
|
|
|
# Try to get a list of SITE commands supported. |
3044
|
|
|
|
|
|
|
#--------------------------------------------------------------------------- |
3045
|
|
|
|
|
|
|
sub _site_help |
3046
|
|
|
|
|
|
|
{ |
3047
|
0
|
|
|
0
|
|
|
my $self = shift; |
3048
|
0
|
|
|
|
|
|
my $help = shift; |
3049
|
0
|
|
|
|
|
|
my $msg = shift; # Optional override message. |
3050
|
|
|
|
|
|
|
|
3051
|
0
|
|
|
|
|
|
my $arg = ${*$self}{_FTPSSL_arguments}; |
|
0
|
|
|
|
|
|
|
3052
|
|
|
|
|
|
|
|
3053
|
|
|
|
|
|
|
# Not calling site() in case Croak is turned on. |
3054
|
|
|
|
|
|
|
# It's not a fatal error if this call fails ... |
3055
|
|
|
|
|
|
|
# my $ok = $self->site ("HELP"); |
3056
|
0
|
|
|
|
|
|
my $ok = ($self->command("SITE", "HELP")->response() == CMD_OK); |
3057
|
|
|
|
|
|
|
|
3058
|
0
|
|
|
|
|
|
$arg->{help_SITE_msg} = $self->last_message (); |
3059
|
|
|
|
|
|
|
|
3060
|
0
|
0
|
|
|
|
|
if ( $ok ) { |
3061
|
0
|
|
|
|
|
|
my $siteHelp = $self->_help_parse (1); |
3062
|
|
|
|
|
|
|
|
3063
|
0
|
0
|
|
|
|
|
if (scalar (keys %{$siteHelp}) > 0) { |
|
0
|
|
|
|
|
|
|
3064
|
0
|
0
|
|
|
|
|
if ( defined $help ) { |
3065
|
0
|
0
|
|
|
|
|
$help->{SITE} = -1 unless ( exists $help->{SITE} ); |
3066
|
|
|
|
|
|
|
} |
3067
|
0
|
0
|
|
|
|
|
$siteHelp->{HELP} = -1 unless ( exists $siteHelp->{HELP} ); |
3068
|
0
|
|
|
|
|
|
$arg->{help_SITE_found} = $siteHelp; |
3069
|
|
|
|
|
|
|
|
3070
|
|
|
|
|
|
|
# Only do optional override of the cached message on success! |
3071
|
0
|
0
|
|
|
|
|
$arg->{help_SITE_msg} = $msg if ( $msg ); |
3072
|
|
|
|
|
|
|
} |
3073
|
|
|
|
|
|
|
} |
3074
|
|
|
|
|
|
|
|
3075
|
0
|
|
|
|
|
|
return; |
3076
|
|
|
|
|
|
|
} |
3077
|
|
|
|
|
|
|
|
3078
|
|
|
|
|
|
|
#--------------------------------------------------------------------------- |
3079
|
|
|
|
|
|
|
# Handles the parsing of the "HELP", "HELP SITE" & "SITE HELP" commands ... |
3080
|
|
|
|
|
|
|
#--------------------------------------------------------------------------- |
3081
|
|
|
|
|
|
|
sub _help_parse { |
3082
|
0
|
|
|
0
|
|
|
my $self = shift; |
3083
|
0
|
|
|
|
|
|
my $site_cmd = shift; |
3084
|
|
|
|
|
|
|
|
3085
|
0
|
|
|
|
|
|
my $helpmsg = $self->last_message (); |
3086
|
0
|
|
|
|
|
|
my @lines = split (/\n/, $helpmsg); |
3087
|
|
|
|
|
|
|
|
3088
|
0
|
|
|
|
|
|
my %help; |
3089
|
|
|
|
|
|
|
|
3090
|
0
|
|
|
|
|
|
foreach my $line (@lines) { |
3091
|
|
|
|
|
|
|
# Strip off the code & separator or leading blanks if multi line. |
3092
|
0
|
|
|
|
|
|
$line =~ s/((^[0-9]+[\s-]?)|(^\s*))//; |
3093
|
0
|
|
|
|
|
|
my $lead = $1; |
3094
|
|
|
|
|
|
|
|
3095
|
0
|
0
|
|
|
|
|
next if ($line eq ""); |
3096
|
|
|
|
|
|
|
|
3097
|
|
|
|
|
|
|
# Skip over the start/end part of the response ... |
3098
|
|
|
|
|
|
|
# Doesn't work for all servers! |
3099
|
|
|
|
|
|
|
# next if ( defined $lead && $lead =~ m/^\d+[\s-]?$/ ); |
3100
|
|
|
|
|
|
|
|
3101
|
|
|
|
|
|
|
# Make sure no space between command & the * that marks it unsupported! |
3102
|
|
|
|
|
|
|
# May be more than one hit per line ... |
3103
|
0
|
|
|
|
|
|
$line =~ s/(\S)\s+[*]($|\s|,)/$1*$2/g; |
3104
|
|
|
|
|
|
|
|
3105
|
0
|
|
|
|
|
|
my @lst = split (/[\s,.]+/, $line); # Break into individual commands |
3106
|
|
|
|
|
|
|
|
3107
|
0
|
0
|
0
|
|
|
|
if ( $site_cmd && $lst[0] eq "SITE" && $lst[1] =~ m/^[A-Z]+$/ ) { |
|
|
0
|
0
|
|
|
|
|
3108
|
0
|
|
|
|
|
|
$help{$lst[1]} = 1; # Each line: "SITE CMD mixed-case-usage" |
3109
|
|
|
|
|
|
|
} |
3110
|
|
|
|
|
|
|
# Now only process if nothing is in lower case (ie: its a comment) |
3111
|
|
|
|
|
|
|
# All commands must be in upper case, some special chars not allowed. |
3112
|
|
|
|
|
|
|
# Commands ending in "*" are currently turned off. |
3113
|
|
|
|
|
|
|
elsif ( $line !~ m/[a-z()]/ ) { |
3114
|
0
|
|
|
|
|
|
foreach (@lst) { |
3115
|
0
|
0
|
|
|
|
|
$help{$_} = 1 if ($_ !~ m/[*]$/); |
3116
|
|
|
|
|
|
|
} |
3117
|
|
|
|
|
|
|
} |
3118
|
|
|
|
|
|
|
} |
3119
|
|
|
|
|
|
|
|
3120
|
0
|
|
|
|
|
|
return (\%help); |
3121
|
|
|
|
|
|
|
} |
3122
|
|
|
|
|
|
|
|
3123
|
|
|
|
|
|
|
#----------------------------------------------------------------------- |
3124
|
|
|
|
|
|
|
# Returns a hash of features supported by this server ... |
3125
|
|
|
|
|
|
|
# It's always uses the cache after the 1st call ... this list never changes! |
3126
|
|
|
|
|
|
|
# Making this a static list! |
3127
|
|
|
|
|
|
|
# This is the version used internally by _help & supported! |
3128
|
|
|
|
|
|
|
#----------------------------------------------------------------------- |
3129
|
|
|
|
|
|
|
|
3130
|
|
|
|
|
|
|
sub _feat { |
3131
|
0
|
|
|
0
|
|
|
my $self = shift; |
3132
|
|
|
|
|
|
|
|
3133
|
0
|
|
|
|
|
|
my $arg = ${*$self}{_FTPSSL_arguments}; |
|
0
|
|
|
|
|
|
|
3134
|
|
|
|
|
|
|
|
3135
|
|
|
|
|
|
|
# Check to see if we've cached the result previously ... |
3136
|
|
|
|
|
|
|
# Must use slightly different naming convenion than used |
3137
|
|
|
|
|
|
|
# in _help() to avoid conflicts. [set in feat()] |
3138
|
0
|
0
|
|
|
|
|
if (exists $arg->{help_FEAT_msg2}) { |
3139
|
0
|
|
|
|
|
|
$arg->{last_ftp_msg} = $arg->{help_FEAT_msg2}; |
3140
|
0
|
|
|
|
|
|
my %hlp = %{$arg->{help_FEAT_found2}}; |
|
0
|
|
|
|
|
|
|
3141
|
0
|
|
|
|
|
|
return ( \%hlp ); |
3142
|
|
|
|
|
|
|
} |
3143
|
|
|
|
|
|
|
|
3144
|
0
|
|
|
|
|
|
my $res = $self->feat (1); # Undocumented opt to disable Croak if on! |
3145
|
|
|
|
|
|
|
|
3146
|
0
|
|
|
|
|
|
return ($res); # Feel free to modify it if you wish! Won't harm anything! |
3147
|
|
|
|
|
|
|
} |
3148
|
|
|
|
|
|
|
|
3149
|
|
|
|
|
|
|
|
3150
|
|
|
|
|
|
|
#----------------------------------------------------------------------- |
3151
|
|
|
|
|
|
|
# Returns a hash of features supported by this server ... |
3152
|
|
|
|
|
|
|
# It's conditionally cached based on the results of the 1st call to FEAT! |
3153
|
|
|
|
|
|
|
# So on some servers this list will be static, while on others dynamic! |
3154
|
|
|
|
|
|
|
#----------------------------------------------------------------------- |
3155
|
|
|
|
|
|
|
# The FEAT command returns one line per command, with optional behaviors. |
3156
|
|
|
|
|
|
|
# If the command ends in "*", the command isn't supported by FEAT! |
3157
|
|
|
|
|
|
|
# Format: CMD [behavior] |
3158
|
|
|
|
|
|
|
#----------------------------------------------------------------------- |
3159
|
|
|
|
|
|
|
# If one or more commands have behaviors, then it's possible for the |
3160
|
|
|
|
|
|
|
# results of the FEAT command to change based on calls to |
3161
|
|
|
|
|
|
|
# "OPTS CMD behavior" |
3162
|
|
|
|
|
|
|
# So if even one command has a behavior, there will be a server hit |
3163
|
|
|
|
|
|
|
# to see if the FEAT results changed. It will also add OPTS to the hash! |
3164
|
|
|
|
|
|
|
# Otherwise the results are cached! |
3165
|
|
|
|
|
|
|
#----------------------------------------------------------------------- |
3166
|
|
|
|
|
|
|
sub feat { |
3167
|
0
|
|
|
0
|
1
|
|
my $self = shift; |
3168
|
0
|
|
|
|
|
|
my $disable_croak = shift; # Undocumented option in POD on purpose! |
3169
|
|
|
|
|
|
|
# Only used when called from _feat()! |
3170
|
|
|
|
|
|
|
|
3171
|
0
|
|
|
|
|
|
my $arg = ${*$self}{_FTPSSL_arguments}; |
|
0
|
|
|
|
|
|
|
3172
|
|
|
|
|
|
|
|
3173
|
0
|
|
|
|
|
|
my %res; |
3174
|
|
|
|
|
|
|
|
3175
|
|
|
|
|
|
|
# Conditionally use the cache if the server will always return a static list! |
3176
|
|
|
|
|
|
|
# It will be static if the OPTS command isn't supported! |
3177
|
0
|
0
|
0
|
|
|
|
if ( exists $arg->{help_FEAT_found2} && |
3178
|
|
|
|
|
|
|
! exists $arg->{help_FEAT_found2}->{OPTS} ) { |
3179
|
0
|
|
|
|
|
|
$arg->{last_ftp_msg} = $arg->{help_FEAT_msg2}; |
3180
|
0
|
|
|
|
|
|
%res = %{$arg->{help_FEAT_found2}}; |
|
0
|
|
|
|
|
|
|
3181
|
0
|
|
|
|
|
|
return ( \%res ); |
3182
|
|
|
|
|
|
|
} |
3183
|
|
|
|
|
|
|
|
3184
|
|
|
|
|
|
|
# Check if a request has been made to not honor HELP if in the FEAT list. |
3185
|
0
|
|
|
|
|
|
my $remove = ""; |
3186
|
0
|
0
|
|
|
|
|
if ( $arg->{removeHELP} ) { |
|
|
0
|
|
|
|
|
|
3187
|
0
|
|
|
|
|
|
$remove = "-1"; |
3188
|
|
|
|
|
|
|
} elsif ( exists $arg->{OverrideHELP} ) { |
3189
|
0
|
|
|
|
|
|
$remove = $arg->{OverrideHELP}; |
3190
|
0
|
0
|
|
|
|
|
if ( $remove == 0 ) { |
3191
|
0
|
|
|
|
|
|
my @lst = keys %{$arg->{help_cmds_found}}; |
|
0
|
|
|
|
|
|
|
3192
|
0
|
0
|
|
|
|
|
$remove = "[array]" if ($#lst != -1); |
3193
|
|
|
|
|
|
|
} |
3194
|
|
|
|
|
|
|
} |
3195
|
0
|
|
|
|
|
|
my $mask = "HELP * OverrideHELP=>${remove} says to remove this command."; |
3196
|
|
|
|
|
|
|
|
3197
|
|
|
|
|
|
|
# Check if a request has been made to not honor HELP if in the FEAT list. |
3198
|
0
|
0
|
|
|
|
|
if ( $remove ne "" ) { |
3199
|
0
|
|
|
|
|
|
$arg->{_hide_value_in_response_} = "HELP"; |
3200
|
0
|
|
|
|
|
|
$arg->{_mask_value_in_response_} = $mask; |
3201
|
|
|
|
|
|
|
} |
3202
|
|
|
|
|
|
|
|
3203
|
0
|
|
|
|
|
|
my $status = $self->command ("FEAT")->response (); |
3204
|
|
|
|
|
|
|
|
3205
|
0
|
0
|
|
|
|
|
if ( $remove ne "" ) { |
3206
|
0
|
|
|
|
|
|
$arg->{last_ftp_msg} =~ s/HELP/<${mask}>/i; |
3207
|
0
|
|
|
|
|
|
delete $arg->{_hide_value_in_response_}; |
3208
|
0
|
|
|
|
|
|
delete $arg->{_mask_value_in_response_}; |
3209
|
|
|
|
|
|
|
} |
3210
|
|
|
|
|
|
|
|
3211
|
0
|
0
|
|
|
|
|
if ( $status == CMD_OK ) { |
3212
|
0
|
|
|
|
|
|
my @lines = split (/\n/, $self->last_message ()); |
3213
|
0
|
|
|
|
|
|
my %behave; |
3214
|
|
|
|
|
|
|
|
3215
|
0
|
|
|
|
|
|
my $may_change = 0; # Assume always returns an unchanging list ... |
3216
|
0
|
|
|
|
|
|
foreach my $line (@lines) { |
3217
|
|
|
|
|
|
|
# Strip off the code & separator or leading blanks if multi line. |
3218
|
0
|
|
|
|
|
|
$line =~ s/((^[0-9]+[\s-]?)|(^\s*))//; |
3219
|
0
|
|
|
|
|
|
my $lead = $1; |
3220
|
|
|
|
|
|
|
|
3221
|
|
|
|
|
|
|
# Skip over the start/end part of the response ... |
3222
|
0
|
0
|
0
|
|
|
|
next if ( defined $lead && $lead =~ m/^\d+[\s-]?$/ ); |
3223
|
|
|
|
|
|
|
|
3224
|
0
|
0
|
|
|
|
|
next if ( $line eq "" ); # Skip over all blank lines |
3225
|
|
|
|
|
|
|
|
3226
|
0
|
|
|
|
|
|
my @part = split (/\s+/, $line); |
3227
|
|
|
|
|
|
|
|
3228
|
|
|
|
|
|
|
# Command ends in "*" or the next part is "*" ??? |
3229
|
|
|
|
|
|
|
# Used to conditionally remove the HELP cmd if necessary ... |
3230
|
|
|
|
|
|
|
# Otherwise not sure if this test ever really happens ... |
3231
|
0
|
0
|
0
|
|
|
|
next if ($part[0] =~ m/[*]$/ || (defined $part[1] && $part[1] eq "*")); |
|
|
|
0
|
|
|
|
|
3232
|
|
|
|
|
|
|
|
3233
|
|
|
|
|
|
|
# The value is the rest of the command ... |
3234
|
0
|
0
|
|
|
|
|
if ( $#part == 0 ) { |
3235
|
0
|
|
|
|
|
|
$res{$part[0]} = ""; # No behavior defined. |
3236
|
|
|
|
|
|
|
} else { |
3237
|
|
|
|
|
|
|
# Save the behavior! |
3238
|
0
|
|
|
|
|
|
$behave{$part[0]} = $res{$part[0]} = (split (/\s+/, $line, 2))[1]; |
3239
|
0
|
|
|
|
|
|
$may_change = 1; |
3240
|
|
|
|
|
|
|
} |
3241
|
|
|
|
|
|
|
} |
3242
|
|
|
|
|
|
|
|
3243
|
0
|
0
|
|
|
|
|
if ( $may_change ) { |
3244
|
|
|
|
|
|
|
# Added per RFC 2389: It says OPTS is an assumed command if FEAT is |
3245
|
|
|
|
|
|
|
# supported. But some servers fail to implement OPTS if there are |
3246
|
|
|
|
|
|
|
# no features it can modify. So adding OPTS to the hash only if at |
3247
|
|
|
|
|
|
|
# least one FEAT command has a behavior string defined! |
3248
|
|
|
|
|
|
|
# If no behaviors are defined it will assume the OPTS command isn't |
3249
|
|
|
|
|
|
|
# supported after all! |
3250
|
0
|
0
|
|
|
|
|
my $msg = (exists $res{OPTS}) ? "Updating OPTS Command!" |
3251
|
|
|
|
|
|
|
: "Auto-adding OPTS Command!"; |
3252
|
0
|
|
|
|
|
|
$self->_print_DBG ("<<+ ", CMD_INFO, "11 ", $msg, "\n"); |
3253
|
|
|
|
|
|
|
|
3254
|
|
|
|
|
|
|
# Adding hash reference to list all valid OPTS commands ... |
3255
|
0
|
|
|
|
|
|
$res{OPTS} = \%behave; |
3256
|
|
|
|
|
|
|
} |
3257
|
|
|
|
|
|
|
} |
3258
|
|
|
|
|
|
|
|
3259
|
|
|
|
|
|
|
# Only cache the results if the 1st time called! This cache is only used by |
3260
|
|
|
|
|
|
|
# this method if OPTS is not supported! But its always used by _feat()! |
3261
|
0
|
0
|
|
|
|
|
unless ( exists $arg->{help_FEAT_msg2} ) { |
3262
|
0
|
|
|
|
|
|
my %res2 = %res; |
3263
|
0
|
|
|
|
|
|
$arg->{help_FEAT_found2} = \%res2; |
3264
|
0
|
|
|
|
|
|
$arg->{help_FEAT_msg2} = $self->last_message (); |
3265
|
|
|
|
|
|
|
} |
3266
|
|
|
|
|
|
|
|
3267
|
0
|
0
|
0
|
|
|
|
unless ( $status == CMD_OK || $disable_croak ) { |
3268
|
0
|
|
|
|
|
|
$self->_croak_or_return (); |
3269
|
|
|
|
|
|
|
} |
3270
|
|
|
|
|
|
|
|
3271
|
0
|
|
|
|
|
|
return (\%res); # The caller is free to modify the hash if they wish! |
3272
|
|
|
|
|
|
|
} |
3273
|
|
|
|
|
|
|
|
3274
|
|
|
|
|
|
|
#----------------------------------------------------------------------- |
3275
|
|
|
|
|
|
|
# Enable/Disable the Croak logic! |
3276
|
|
|
|
|
|
|
# Returns the previous Croak setting! |
3277
|
|
|
|
|
|
|
#----------------------------------------------------------------------- |
3278
|
|
|
|
|
|
|
|
3279
|
|
|
|
|
|
|
sub set_croak { |
3280
|
0
|
|
|
0
|
1
|
|
my $self = shift; |
3281
|
0
|
|
|
|
|
|
my $turn_on = shift; |
3282
|
|
|
|
|
|
|
|
3283
|
0
|
|
0
|
|
|
|
my $res = ${*$self}{_FTPSSL_arguments}->{Croak} || 0; |
3284
|
|
|
|
|
|
|
|
3285
|
0
|
0
|
|
|
|
|
if ( defined $turn_on ) { |
3286
|
0
|
0
|
|
|
|
|
if ( $turn_on ) { |
|
|
0
|
|
|
|
|
|
3287
|
0
|
|
|
|
|
|
${*$self}{_FTPSSL_arguments}->{Croak} = 1; |
|
0
|
|
|
|
|
|
|
3288
|
0
|
|
|
|
|
|
} elsif ( exists ( ${*$self}{_FTPSSL_arguments}->{Croak} ) ) { |
3289
|
0
|
|
|
|
|
|
delete ( ${*$self}{_FTPSSL_arguments}->{Croak} ); |
|
0
|
|
|
|
|
|
|
3290
|
|
|
|
|
|
|
} |
3291
|
|
|
|
|
|
|
} |
3292
|
|
|
|
|
|
|
|
3293
|
0
|
|
|
|
|
|
return ( $res ); |
3294
|
|
|
|
|
|
|
} |
3295
|
|
|
|
|
|
|
|
3296
|
|
|
|
|
|
|
#----------------------------------------------------------------------- |
3297
|
|
|
|
|
|
|
# Boolean check for croak! |
3298
|
|
|
|
|
|
|
# Uses the current message as the croak message on error! |
3299
|
|
|
|
|
|
|
#----------------------------------------------------------------------- |
3300
|
|
|
|
|
|
|
|
3301
|
|
|
|
|
|
|
sub _test_croak { |
3302
|
0
|
|
|
0
|
|
|
my $self = shift; |
3303
|
0
|
|
|
|
|
|
my $true = shift; |
3304
|
|
|
|
|
|
|
|
3305
|
0
|
0
|
|
|
|
|
unless ( $true ) { |
3306
|
0
|
|
|
|
|
|
$ERRSTR = ${*$self}{_FTPSSL_arguments}->{last_ftp_msg}; |
|
0
|
|
|
|
|
|
|
3307
|
0
|
0
|
|
|
|
|
if ( ${*$self}{_FTPSSL_arguments}->{Croak} ) { |
|
0
|
|
|
|
|
|
|
3308
|
0
|
|
|
|
|
|
my $c = (caller(1))[3]; |
3309
|
0
|
0
|
0
|
|
|
|
if ( defined $c && $c ne "Net::FTPSSL::login" ) { |
3310
|
0
|
|
|
|
|
|
$self->_abort (); |
3311
|
0
|
|
|
|
|
|
$self->quit (); |
3312
|
0
|
|
|
|
|
|
${*$self}{_FTPSSL_arguments}->{last_ftp_msg} = $ERRSTR; |
|
0
|
|
|
|
|
|
|
3313
|
|
|
|
|
|
|
} |
3314
|
|
|
|
|
|
|
|
3315
|
0
|
|
|
|
|
|
croak ( $ERRSTR . "\n" ); |
3316
|
|
|
|
|
|
|
} |
3317
|
|
|
|
|
|
|
} |
3318
|
|
|
|
|
|
|
|
3319
|
0
|
|
|
|
|
|
return ( $true ); |
3320
|
|
|
|
|
|
|
} |
3321
|
|
|
|
|
|
|
|
3322
|
|
|
|
|
|
|
#----------------------------------------------------------------------- |
3323
|
|
|
|
|
|
|
# Error handling - Decides if to Croak or return undef ... |
3324
|
|
|
|
|
|
|
# Has 2 modes, a regular member func & when not a member func ... |
3325
|
|
|
|
|
|
|
#----------------------------------------------------------------------- |
3326
|
|
|
|
|
|
|
|
3327
|
|
|
|
|
|
|
sub _croak_or_return { |
3328
|
0
|
|
|
0
|
|
|
my $self = shift; |
3329
|
|
|
|
|
|
|
|
3330
|
|
|
|
|
|
|
# The error code to use if we update the last message! |
3331
|
|
|
|
|
|
|
# Or if we print it to FTPS_ERROR & we don't croak! |
3332
|
0
|
|
|
|
|
|
my $err = CMD_ERROR . CMD_ERROR . CMD_ERROR; |
3333
|
|
|
|
|
|
|
|
3334
|
0
|
0
|
|
|
|
|
unless (defined $self) { |
3335
|
|
|
|
|
|
|
# Called this way only by new() before $self is created ... |
3336
|
0
|
|
|
|
|
|
my $should_we_die = shift; |
3337
|
0
|
|
|
|
|
|
my $should_we_print = shift; |
3338
|
0
|
|
0
|
|
|
|
$ERRSTR = shift || "Unknown Error"; |
3339
|
|
|
|
|
|
|
|
3340
|
0
|
0
|
|
|
|
|
_print_LOG ( undef, "<<+ $err ", $ERRSTR, "\n" ) if ( $should_we_print ); |
3341
|
0
|
0
|
|
|
|
|
croak ( $ERRSTR . "\n" ) if ( $should_we_die ); |
3342
|
|
|
|
|
|
|
|
3343
|
|
|
|
|
|
|
} else { |
3344
|
|
|
|
|
|
|
# Called this way as a memeber func by everyone else ... |
3345
|
0
|
|
|
|
|
|
my $replace_mode = shift; # 1 - append, 0 - replace, |
3346
|
|
|
|
|
|
|
# undef - leave last_message() unchanged |
3347
|
0
|
|
|
|
|
|
my $msg = shift; |
3348
|
0
|
|
0
|
|
|
|
$ERRSTR = $msg || ${*$self}{_FTPSSL_arguments}->{last_ftp_msg}; |
3349
|
|
|
|
|
|
|
|
3350
|
|
|
|
|
|
|
# Do 1st so updated if caller trapped the Croak! |
3351
|
0
|
0
|
0
|
|
|
|
if ( defined $replace_mode && uc ($msg || "") ne "" ) { |
|
|
|
0
|
|
|
|
|
3352
|
0
|
0
|
0
|
|
|
|
if ($replace_mode && uc (${*$self}{_FTPSSL_arguments}->{last_ftp_msg} || "") ne "" ) { |
|
|
|
0
|
|
|
|
|
3353
|
0
|
|
|
|
|
|
${*$self}{_FTPSSL_arguments}->{last_ftp_msg} .= "\n" . $err . " " . $msg; |
|
0
|
|
|
|
|
|
|
3354
|
|
|
|
|
|
|
} else { |
3355
|
0
|
|
|
|
|
|
${*$self}{_FTPSSL_arguments}->{last_ftp_msg} = $err . " " . $msg; |
|
0
|
|
|
|
|
|
|
3356
|
|
|
|
|
|
|
} |
3357
|
|
|
|
|
|
|
} |
3358
|
|
|
|
|
|
|
|
3359
|
0
|
0
|
|
|
|
|
if ( ${*$self}{_FTPSSL_arguments}->{Croak} ) { |
|
0
|
|
|
|
|
|
|
3360
|
0
|
|
0
|
|
|
|
my $c = (caller(1))[3] || ""; |
3361
|
|
|
|
|
|
|
|
3362
|
|
|
|
|
|
|
# Trying to prevent infinite recursion ... |
3363
|
|
|
|
|
|
|
# Also reseting the PIPE Signal in case catastrophic failure detected! |
3364
|
0
|
0
|
0
|
|
|
|
if ( ref($self) eq __PACKAGE__ && |
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
3365
|
0
|
|
|
|
|
|
(! exists ${*$self}{_FTPSSL_arguments}->{_command_failed_}) && |
3366
|
0
|
|
|
|
|
|
(! exists ${*$self}{_FTPSSL_arguments}->{recursion}) && |
3367
|
|
|
|
|
|
|
$c ne "Net::FTPSSL::command" && |
3368
|
|
|
|
|
|
|
$c ne "Net::FTPSSL::response" ) { |
3369
|
0
|
|
|
|
|
|
${*$self}{_FTPSSL_arguments}->{recursion} = "TRUE"; |
|
0
|
|
|
|
|
|
|
3370
|
0
|
|
|
|
|
|
my $tmp = ${*$self}{_FTPSSL_arguments}->{last_ftp_msg}; |
|
0
|
|
|
|
|
|
|
3371
|
0
|
|
|
|
|
|
local $SIG{PIPE} = "IGNORE"; # Limits scope to just current block! |
3372
|
0
|
|
|
|
|
|
$self->_abort (); |
3373
|
0
|
|
|
|
|
|
$self->quit (); |
3374
|
0
|
|
|
|
|
|
${*$self}{_FTPSSL_arguments}->{last_ftp_msg} = $tmp; |
|
0
|
|
|
|
|
|
|
3375
|
|
|
|
|
|
|
} |
3376
|
|
|
|
|
|
|
|
3377
|
|
|
|
|
|
|
# Only do if writing the message to the error log file ... |
3378
|
0
|
0
|
0
|
|
|
|
if ( defined $replace_mode && uc ($msg || "") ne "" && |
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
3379
|
0
|
|
|
|
|
|
${*$self}{_FTPSSL_arguments}->{debug} == 2 ) { |
3380
|
0
|
|
|
|
|
|
_print_LOG ( $self, "<<+ $err ", $msg, "\n" ); |
3381
|
|
|
|
|
|
|
} |
3382
|
|
|
|
|
|
|
|
3383
|
0
|
|
|
|
|
|
croak ( $ERRSTR . "\n" ); |
3384
|
|
|
|
|
|
|
} |
3385
|
|
|
|
|
|
|
|
3386
|
|
|
|
|
|
|
# Handles both cases of writing to STDERR or the error log file ... |
3387
|
0
|
0
|
0
|
|
|
|
if ( defined $replace_mode && uc ($msg || "") ne "" && ${*$self}{_FTPSSL_arguments}->{debug} ) { |
|
0
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
3388
|
0
|
|
|
|
|
|
_print_LOG ( $self, "<<+ $err " . $msg . "\n" ); |
3389
|
|
|
|
|
|
|
} |
3390
|
|
|
|
|
|
|
} |
3391
|
|
|
|
|
|
|
|
3392
|
0
|
|
|
|
|
|
return ( undef ); |
3393
|
|
|
|
|
|
|
} |
3394
|
|
|
|
|
|
|
|
3395
|
|
|
|
|
|
|
#----------------------------------------------------------------------- |
3396
|
|
|
|
|
|
|
# Messages handler |
3397
|
|
|
|
|
|
|
# ---------------------------------------------------------------------- |
3398
|
|
|
|
|
|
|
# Called by both Net::FTPSSL and IO::Socket::INET classes. |
3399
|
|
|
|
|
|
|
#----------------------------------------------------------------------- |
3400
|
|
|
|
|
|
|
|
3401
|
|
|
|
|
|
|
sub command { |
3402
|
0
|
|
|
0
|
0
|
|
my $self = shift; # Remaining arg(s) accessed directly. |
3403
|
|
|
|
|
|
|
|
3404
|
0
|
|
|
|
|
|
my @args; |
3405
|
|
|
|
|
|
|
my $data; |
3406
|
|
|
|
|
|
|
|
3407
|
|
|
|
|
|
|
# Remove any previous failure ... |
3408
|
0
|
|
|
|
|
|
delete ( ${*$self}{_FTPSSL_arguments}->{_command_failed_} ); |
|
0
|
|
|
|
|
|
|
3409
|
|
|
|
|
|
|
|
3410
|
|
|
|
|
|
|
# remove undef values from the list. |
3411
|
|
|
|
|
|
|
# Maybe I have to find out why those undef were passed. |
3412
|
0
|
|
|
|
|
|
@args = grep ( defined($_), @_ ); |
3413
|
|
|
|
|
|
|
|
3414
|
|
|
|
|
|
|
$data = join( " ", |
3415
|
0
|
|
|
|
|
|
map { /\n/ |
3416
|
0
|
0
|
|
|
|
|
? do { my $n = $_; $n =~ tr/\n/ /; $n } |
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
3417
|
|
|
|
|
|
|
: $_; |
3418
|
|
|
|
|
|
|
} @args |
3419
|
|
|
|
|
|
|
); |
3420
|
|
|
|
|
|
|
|
3421
|
|
|
|
|
|
|
# Log the command being executed ... |
3422
|
0
|
0
|
|
|
|
|
if ( ${*$self}{_FTPSSL_arguments}->{debug} ) { |
|
0
|
|
|
|
|
|
|
3423
|
0
|
0
|
|
|
|
|
my $prefix = ( ref($self) eq __PACKAGE__ ) ? ">>> " : "SKT >>> "; |
3424
|
0
|
0
|
|
|
|
|
if ( $data =~ m/^PASS\s/ ) { |
|
|
0
|
|
|
|
|
|
3425
|
0
|
|
|
|
|
|
_print_LOG ( $self, $prefix, "PASS *******\n" ); # Don't echo passwords |
3426
|
|
|
|
|
|
|
} elsif ( $data =~ m/^USER\s/ ) { |
3427
|
0
|
|
|
|
|
|
_print_LOG ( $self, $prefix, "USER +++++++\n" ); # Don't echo user names |
3428
|
|
|
|
|
|
|
} else { |
3429
|
0
|
|
|
|
|
|
_print_LOG ( $self, $prefix, $data, "\n" ); # Echo everything else |
3430
|
|
|
|
|
|
|
} |
3431
|
|
|
|
|
|
|
} |
3432
|
|
|
|
|
|
|
|
3433
|
0
|
|
|
|
|
|
$data .= "\015\012"; |
3434
|
|
|
|
|
|
|
|
3435
|
0
|
|
|
|
|
|
my $len = length $data; |
3436
|
0
|
|
|
|
|
|
my $written = syswrite( $self, $data, $len ); |
3437
|
0
|
0
|
|
|
|
|
unless ( defined $written ) { |
3438
|
0
|
|
|
|
|
|
${*$self}{_FTPSSL_arguments}->{_command_failed_} = "ERROR"; |
|
0
|
|
|
|
|
|
|
3439
|
0
|
|
|
|
|
|
my $err_msg = "Can't write command on socket: $!"; |
3440
|
0
|
|
|
|
|
|
carp "$err_msg"; # This prints a warning. |
3441
|
|
|
|
|
|
|
# Not called as an object member in case $self not a Net::FTPSSL obj. |
3442
|
0
|
|
|
|
|
|
_my_close ($self); # Old way $self->close(); |
3443
|
0
|
|
|
|
|
|
_croak_or_return ($self, 0, $err_msg); |
3444
|
0
|
|
|
|
|
|
return $self; # Included here due to non-standard _croak_or_return() usage. |
3445
|
|
|
|
|
|
|
} |
3446
|
|
|
|
|
|
|
|
3447
|
0
|
|
|
|
|
|
return $self; # So can directly call response()! |
3448
|
|
|
|
|
|
|
} |
3449
|
|
|
|
|
|
|
|
3450
|
|
|
|
|
|
|
# ----------------------------------------------------------------------------- |
3451
|
|
|
|
|
|
|
# Some responses take multiple lines to finish. ("211-" [more] vs "211 " [done]) |
3452
|
|
|
|
|
|
|
# Some responses have CR's embeded in them. (ie: no code in the next line) |
3453
|
|
|
|
|
|
|
# Sometimes the data channel response comes with the open data connection msg. |
3454
|
|
|
|
|
|
|
# (Especially if the data channel is not encrypted or the file is small.) |
3455
|
|
|
|
|
|
|
# So be careful, you will be blocked if you read past the last row of the |
3456
|
|
|
|
|
|
|
# current response or return the wrong code if you get into the next response! |
3457
|
|
|
|
|
|
|
# (And will probably hang the next time response() is called.) |
3458
|
|
|
|
|
|
|
# So far the only thing I haven't seen is a call to sysread() returning a |
3459
|
|
|
|
|
|
|
# partial line response! (Drat, that just happened! See 0.20 Change notes.) |
3460
|
|
|
|
|
|
|
# ----------------------------------------------------------------------------- |
3461
|
|
|
|
|
|
|
# Called by both Net::FTPSSL and IO::Socket::INET classes. |
3462
|
|
|
|
|
|
|
# Hence using func($self, ...) instead of $self->func(...) |
3463
|
|
|
|
|
|
|
# ----------------------------------------------------------------------------- |
3464
|
|
|
|
|
|
|
# Returns a single digit response code! (The CMD_* constants!) |
3465
|
|
|
|
|
|
|
# ----------------------------------------------------------------------------- |
3466
|
|
|
|
|
|
|
sub response { |
3467
|
0
|
|
|
0
|
0
|
|
my $self = shift; |
3468
|
0
|
|
0
|
|
|
|
my $ccc_mess = shift || 0; # Only set by the CCC command! Hangs if not used. |
3469
|
|
|
|
|
|
|
|
3470
|
|
|
|
|
|
|
# The buffer size to use during the sysread() call on the command channel. |
3471
|
0
|
|
|
|
|
|
my $buffer_size = 4096; |
3472
|
|
|
|
|
|
|
|
3473
|
|
|
|
|
|
|
# Uncomment to experiment with variable buffer sizes. |
3474
|
|
|
|
|
|
|
# Very usefull in debugging _response_details () & simulating server issues. |
3475
|
|
|
|
|
|
|
# Supports any value >= 1. |
3476
|
|
|
|
|
|
|
# $buffer_size = 10; |
3477
|
|
|
|
|
|
|
|
3478
|
|
|
|
|
|
|
# The warning to use when printing past the end of the current response! |
3479
|
|
|
|
|
|
|
# Used in place of $prefix in certain conditions. |
3480
|
0
|
|
|
|
|
|
my $warn = "Warning: Attempted to read past end of response! "; |
3481
|
|
|
|
|
|
|
|
3482
|
|
|
|
|
|
|
# Only continue if the command() call worked! |
3483
|
|
|
|
|
|
|
# Otherwise on failure this method will hang! |
3484
|
|
|
|
|
|
|
# We already printed out the failure message in command() if not croaking! |
3485
|
0
|
0
|
|
|
|
|
return (CMD_ERROR) if ( exists ${*$self}{_FTPSSL_arguments}->{_command_failed_} ); |
|
0
|
|
|
|
|
|
|
3486
|
|
|
|
|
|
|
|
3487
|
0
|
|
|
|
|
|
${*$self}{_FTPSSL_arguments}->{last_ftp_msg} = ""; # Clear out the message |
|
0
|
|
|
|
|
|
|
3488
|
0
|
0
|
|
|
|
|
my $prefix = ( ref($self) eq __PACKAGE__ ) ? "<<< " : "SKT <<< "; |
3489
|
|
|
|
|
|
|
|
3490
|
0
|
|
|
|
|
|
my $timeout = ${*$self}{_FTPSSL_arguments}->{Timeout}; |
|
0
|
|
|
|
|
|
|
3491
|
|
|
|
|
|
|
|
3492
|
0
|
0
|
0
|
|
|
|
my $sep = ( ${*$self}{_FTPSSL_arguments}->{debug} && ${*$self}{_FTPSSL_arguments}->{debug_extra} ) ? "===============" : undef; |
3493
|
|
|
|
|
|
|
|
3494
|
|
|
|
|
|
|
# Starting a new message ... |
3495
|
0
|
|
|
|
|
|
${*$self}{_FTPSSL_arguments}->{last_ftp_msg} = ""; |
|
0
|
|
|
|
|
|
|
3496
|
0
|
|
|
|
|
|
my $data = ""; |
3497
|
0
|
|
|
|
|
|
my ($done, $complete) = (0, 1); |
3498
|
|
|
|
|
|
|
|
3499
|
|
|
|
|
|
|
# Check if we need to process anything read in past the previous command. |
3500
|
|
|
|
|
|
|
# Hopefully under normal conditions we'll find nothing to process. |
3501
|
0
|
0
|
|
|
|
|
if ( exists ${*$self}{_FTPSSL_arguments}->{next_ftp_msg} ) { |
|
0
|
|
|
|
|
|
|
3502
|
0
|
0
|
|
|
|
|
_print_LOG ( $self, "Info: Response found from previous read ...\n") if ( ${*$self}{_FTPSSL_arguments}->{debug} ); |
|
0
|
|
|
|
|
|
|
3503
|
0
|
|
|
|
|
|
$data = ${*$self}{_FTPSSL_arguments}->{next_ftp_msg}; |
|
0
|
|
|
|
|
|
|
3504
|
0
|
|
|
|
|
|
delete ( ${*$self}{_FTPSSL_arguments}->{next_ftp_msg} ); |
|
0
|
|
|
|
|
|
|
3505
|
0
|
|
|
|
|
|
($done, $complete) = _response_details ($self, $prefix, \$data, 0, $ccc_mess); |
3506
|
0
|
0
|
0
|
|
|
|
if ( $done && $complete ) { |
3507
|
0
|
|
|
|
|
|
_print_edited_response ( $self, $prefix, ${*$self}{_FTPSSL_arguments}->{last_ftp_msg}, $sep, 0 ); |
|
0
|
|
|
|
|
|
|
3508
|
0
|
|
|
|
|
|
_print_edited_response ( $self, $warn, ${*$self}{_FTPSSL_arguments}->{next_ftp_msg}, $sep, 2 ); |
|
0
|
|
|
|
|
|
|
3509
|
0
|
|
|
|
|
|
return last_status_code ( $self ); |
3510
|
|
|
|
|
|
|
} |
3511
|
|
|
|
|
|
|
|
3512
|
|
|
|
|
|
|
# Should never happen, but using very short timeout on continued commands. |
3513
|
0
|
|
|
|
|
|
$timeout = 2; |
3514
|
|
|
|
|
|
|
} |
3515
|
|
|
|
|
|
|
|
3516
|
|
|
|
|
|
|
# Check if there is data still pending on the command channel ... |
3517
|
0
|
|
|
|
|
|
my $rin = ""; |
3518
|
0
|
|
|
|
|
|
vec ($rin, fileno($self), 1) = 1; |
3519
|
0
|
|
|
|
|
|
my $res = select ( $rin, undef, undef, $timeout ); |
3520
|
0
|
0
|
|
|
|
|
if ( $res > 0 ) { |
|
|
0
|
|
|
|
|
|
3521
|
|
|
|
|
|
|
# Now lets read the response from the command channel itself. |
3522
|
0
|
|
|
|
|
|
my $cnt = 0; |
3523
|
0
|
|
|
|
|
|
while ( sysread( $self, $data, $buffer_size ) ) { |
3524
|
0
|
|
|
|
|
|
($done, $complete) = _response_details ($self, $prefix, \$data, $done, $ccc_mess); |
3525
|
0
|
|
|
|
|
|
++$cnt; |
3526
|
0
|
0
|
0
|
|
|
|
last if ($done && $complete); |
3527
|
|
|
|
|
|
|
} |
3528
|
|
|
|
|
|
|
|
3529
|
|
|
|
|
|
|
# Check for errors ... |
3530
|
0
|
0
|
0
|
|
|
|
if ( $done && $complete ) { |
|
|
0
|
0
|
|
|
|
|
3531
|
|
|
|
|
|
|
# A no-op to protect against random setting of "$!" on no real error! |
3532
|
0
|
|
|
|
|
|
my $nothing = ""; |
3533
|
|
|
|
|
|
|
|
3534
|
|
|
|
|
|
|
} elsif ( $cnt == 0 || $! ne "" ) { |
3535
|
0
|
0
|
|
|
|
|
if ($cnt > 0) { |
3536
|
|
|
|
|
|
|
# Will put brackes arround the error reponse! |
3537
|
0
|
|
|
|
|
|
_print_edited_response ( $self, $prefix, ${*$self}{_FTPSSL_arguments}->{last_ftp_msg}, $sep, 1 ); |
|
0
|
|
|
|
|
|
|
3538
|
0
|
|
|
|
|
|
_print_edited_response ( $self, $warn, ${*$self}{_FTPSSL_arguments}->{next_ftp_msg}, $sep, 2 ); |
|
0
|
|
|
|
|
|
|
3539
|
|
|
|
|
|
|
} |
3540
|
0
|
|
|
|
|
|
_croak_or_return ($self, 0, "Unexpected EOF on Command Channel [$cnt] ($done, $complete) ($!)"); |
3541
|
0
|
|
|
|
|
|
return (CMD_ERROR); |
3542
|
|
|
|
|
|
|
} |
3543
|
|
|
|
|
|
|
|
3544
|
0
|
|
|
|
|
|
} elsif ( ${*$self}{_FTPSSL_arguments}->{last_ftp_msg} ne "" ) { |
3545
|
|
|
|
|
|
|
# A Timeout here is OK, it meant the previous command was complete. |
3546
|
0
|
|
|
|
|
|
my $nothing = ""; |
3547
|
|
|
|
|
|
|
|
3548
|
|
|
|
|
|
|
} else { |
3549
|
|
|
|
|
|
|
# Will put brackes arround the error reponse! |
3550
|
0
|
|
|
|
|
|
_print_edited_response ( $self, $prefix, ${*$self}{_FTPSSL_arguments}->{last_ftp_msg}, $sep, 1 ); |
|
0
|
|
|
|
|
|
|
3551
|
0
|
|
|
|
|
|
_print_edited_response ( $self, $warn, ${*$self}{_FTPSSL_arguments}->{next_ftp_msg}, $sep, 2 ); |
|
0
|
|
|
|
|
|
|
3552
|
0
|
|
|
|
|
|
_croak_or_return ($self, 0, "Timed out waiting for a response! [$res] ($!)"); |
3553
|
0
|
|
|
|
|
|
return (CMD_ERROR); |
3554
|
|
|
|
|
|
|
} |
3555
|
|
|
|
|
|
|
|
3556
|
|
|
|
|
|
|
# Now print out the final patched together responses ... |
3557
|
0
|
|
|
|
|
|
_print_edited_response ( $self, $prefix, ${*$self}{_FTPSSL_arguments}->{last_ftp_msg}, $sep, 0 ); |
|
0
|
|
|
|
|
|
|
3558
|
0
|
|
|
|
|
|
_print_edited_response ( $self, $warn, ${*$self}{_FTPSSL_arguments}->{next_ftp_msg}, $sep, 2 ); |
|
0
|
|
|
|
|
|
|
3559
|
|
|
|
|
|
|
|
3560
|
|
|
|
|
|
|
# Returns the 1st digit of the 3 digit status code! |
3561
|
0
|
|
|
|
|
|
return last_status_code ( $self ); |
3562
|
|
|
|
|
|
|
} |
3563
|
|
|
|
|
|
|
|
3564
|
|
|
|
|
|
|
#----------------------------------------------------------------------- |
3565
|
|
|
|
|
|
|
# Mask sensitive information before it's written to the log file. |
3566
|
|
|
|
|
|
|
# Separated out since done in multiple places. |
3567
|
|
|
|
|
|
|
#----------------------------------------------------------------------- |
3568
|
|
|
|
|
|
|
sub _print_edited_response { |
3569
|
0
|
|
|
0
|
|
|
my $self = shift; |
3570
|
0
|
|
|
|
|
|
my $prefix = shift; # "<<< " vs "SKT <<< ". |
3571
|
0
|
|
|
|
|
|
my $msg = shift; # The response to print. (may be undef) |
3572
|
0
|
|
|
|
|
|
my $sep = shift; # An optional separator string. |
3573
|
0
|
|
|
|
|
|
my $bracket = shift; # 0 or 1 or 2. |
3574
|
|
|
|
|
|
|
|
3575
|
|
|
|
|
|
|
# Tells which separator to use to break up lines in $msg! |
3576
|
0
|
0
|
|
|
|
|
my $breakStr = ($bracket == 2) ? "\015\012" : "\n"; |
3577
|
|
|
|
|
|
|
|
3578
|
|
|
|
|
|
|
# A safety check to simplify when calling with undefined {next_ftp_msg}. |
3579
|
0
|
0
|
|
|
|
|
unless (defined $msg) { |
3580
|
0
|
|
|
|
|
|
return; |
3581
|
|
|
|
|
|
|
} |
3582
|
|
|
|
|
|
|
|
3583
|
0
|
0
|
|
|
|
|
if ( ${*$self}{_FTPSSL_arguments}->{debug} ) { |
|
0
|
|
|
|
|
|
|
3584
|
|
|
|
|
|
|
# Do we need to hide a value in the logged response ??? |
3585
|
0
|
0
|
|
|
|
|
if ( exists ${*$self}{_FTPSSL_arguments}->{_hide_value_in_response_} ) { |
|
0
|
|
|
|
|
|
|
3586
|
0
|
|
|
|
|
|
my $val = _mask_regex_chars ($self, ${*$self}{_FTPSSL_arguments}->{_hide_value_in_response_}); |
|
0
|
|
|
|
|
|
|
3587
|
0
|
|
0
|
|
|
|
my $mask = ${*$self}{_FTPSSL_arguments}->{_mask_value_in_response_} || "????"; |
3588
|
0
|
|
|
|
|
|
$msg =~ s/\s${val}($|[\s.!,])/ <$mask>${1}/g; |
3589
|
|
|
|
|
|
|
} |
3590
|
|
|
|
|
|
|
|
3591
|
0
|
0
|
|
|
|
|
if ($bracket) { |
3592
|
0
|
|
|
|
|
|
$msg = $prefix . "[" . join ("]\n${prefix}[", split ($breakStr, $msg)) . "]"; |
3593
|
|
|
|
|
|
|
} else { |
3594
|
0
|
|
|
|
|
|
$msg = $prefix . join ("\n$prefix", split ($breakStr, $msg)); |
3595
|
|
|
|
|
|
|
} |
3596
|
|
|
|
|
|
|
|
3597
|
0
|
0
|
0
|
|
|
|
if ( defined $sep && $sep !~ m/^\s*$/ ) { |
3598
|
0
|
|
|
|
|
|
$msg = "Start: " . $sep . "\n" . $msg . "\nEnd::: " . $sep; |
3599
|
|
|
|
|
|
|
} |
3600
|
0
|
|
|
|
|
|
_print_LOG ( $self, $msg, "\n"); |
3601
|
|
|
|
|
|
|
} |
3602
|
|
|
|
|
|
|
|
3603
|
0
|
|
|
|
|
|
return; |
3604
|
|
|
|
|
|
|
} |
3605
|
|
|
|
|
|
|
|
3606
|
|
|
|
|
|
|
#----------------------------------------------------------------------- |
3607
|
|
|
|
|
|
|
# Broken out from response() in order to simplify the logic. |
3608
|
|
|
|
|
|
|
# The previous version was getting way too convoluted to support. |
3609
|
|
|
|
|
|
|
# Any bugs in this function easily causes things to hang or insert |
3610
|
|
|
|
|
|
|
# random into the returned messages! |
3611
|
|
|
|
|
|
|
#----------------------------------------------------------------------- |
3612
|
|
|
|
|
|
|
# If you need to turn on the logging for this method use "Debug => 99" |
3613
|
|
|
|
|
|
|
# in the constructor! |
3614
|
|
|
|
|
|
|
#----------------------------------------------------------------------- |
3615
|
|
|
|
|
|
|
# What a line should look like |
3616
|
|
|
|
|
|
|
# - --- Continuation line(s) [repeateable] |
3617
|
|
|
|
|
|
|
# --- Response completed line |
3618
|
|
|
|
|
|
|
# Anything else means it's a Continuation line with embedded 's. |
3619
|
|
|
|
|
|
|
# I think its safe to say the response completed line dosn't have |
3620
|
|
|
|
|
|
|
# any extra 's embeded in it. Otherwise it's kind of difficult |
3621
|
|
|
|
|
|
|
# to know when to stop reading from the socket & risk hangs. |
3622
|
|
|
|
|
|
|
#----------------------------------------------------------------------- |
3623
|
|
|
|
|
|
|
# But what I actually saw in many cases: (list not complete) |
3624
|
|
|
|
|
|
|
# 2 |
3625
|
|
|
|
|
|
|
# 13-First Line |
3626
|
|
|
|
|
|
|
# 213 |
3627
|
|
|
|
|
|
|
# -Second Line |
3628
|
|
|
|
|
|
|
# 213- |
3629
|
|
|
|
|
|
|
# Third Line |
3630
|
|
|
|
|
|
|
# 213-Fourth |
3631
|
|
|
|
|
|
|
# Line |
3632
|
|
|
|
|
|
|
# Turns out sysread() isn't generous. It returns as little as possible |
3633
|
|
|
|
|
|
|
# sometimes. Even when there is plenty of space left in the buffer. |
3634
|
|
|
|
|
|
|
# Hence the strange behaviour above. But once all the pieces are put |
3635
|
|
|
|
|
|
|
# together properly, you see what you expected in the 1st place. |
3636
|
|
|
|
|
|
|
#----------------------------------------------------------------------- |
3637
|
|
|
|
|
|
|
# Returns if it thinks the current response is done & complete or not. |
3638
|
|
|
|
|
|
|
# end_respnose - (passed as "$status" next time called) |
3639
|
|
|
|
|
|
|
# 0 - Response isn't complete yet. |
3640
|
|
|
|
|
|
|
# 1 - Response was done, but may or may not be truncated in . |
3641
|
|
|
|
|
|
|
# response_complete - Tells if the final line is complete or truncated. |
3642
|
|
|
|
|
|
|
# 0 - Line was truncated! |
3643
|
|
|
|
|
|
|
# 1 - Last line was complete! |
3644
|
|
|
|
|
|
|
# Both must be true to stop reading from the socket. |
3645
|
|
|
|
|
|
|
# If we've read past the response into the next one, we don't stop |
3646
|
|
|
|
|
|
|
# reading until the overflow response is complete as well. Otherwise |
3647
|
|
|
|
|
|
|
# the Timeout logic might not work properly later on. |
3648
|
|
|
|
|
|
|
#----------------------------------------------------------------------- |
3649
|
|
|
|
|
|
|
# The data buffer. I've seen the following: |
3650
|
|
|
|
|
|
|
# 1) A line begining with: \012 (The \015 ended the pevious buffer) |
3651
|
|
|
|
|
|
|
# 2) A line ending with: \015 (The \012 started the next buffer) |
3652
|
|
|
|
|
|
|
# 3) Lines not ending with: \015\012 |
3653
|
|
|
|
|
|
|
# 4) A line only containing: \015\012 |
3654
|
|
|
|
|
|
|
# 5) A line only containing: \012 |
3655
|
|
|
|
|
|
|
# 6) Lines ending with: \015\012 |
3656
|
|
|
|
|
|
|
# If you see the 1st three items, you know there is more to read |
3657
|
|
|
|
|
|
|
# from the socket. If you see the last 3 items, it's possible |
3658
|
|
|
|
|
|
|
# that the next read from the socket will hang if you've already |
3659
|
|
|
|
|
|
|
# seen the response complete message. So be careful here! |
3660
|
|
|
|
|
|
|
#----------------------------------------------------------------------- |
3661
|
|
|
|
|
|
|
sub _response_details { |
3662
|
0
|
|
|
0
|
|
|
my $self = shift; |
3663
|
0
|
|
|
|
|
|
my $prefix = shift; # "<<< " vs "SKT <<< ". |
3664
|
0
|
|
|
|
|
|
my $data_ref = shift; # The data buffer to parse ... |
3665
|
0
|
|
|
|
|
|
my $status = shift; # 0 or 1 (the returned status from previous call) |
3666
|
|
|
|
|
|
|
|
3667
|
0
|
|
|
|
|
|
my $ccc_kludge = shift; # Tells us if we are dealing with a corrupted CC |
3668
|
|
|
|
|
|
|
# due to the aftermath of a CCC command! |
3669
|
|
|
|
|
|
|
# 1st hit terminates the command in this case! |
3670
|
|
|
|
|
|
|
|
3671
|
|
|
|
|
|
|
# The return values ... |
3672
|
0
|
|
|
|
|
|
my ($end_response, $response_complete) = (0, 0); |
3673
|
|
|
|
|
|
|
|
3674
|
|
|
|
|
|
|
# A more restrictive option for turning on logging is needed in this method. |
3675
|
|
|
|
|
|
|
# Otherwise too much info is written to the logs and it is very confusing. |
3676
|
|
|
|
|
|
|
# (Debug => 99 turns this extra logging on!) |
3677
|
|
|
|
|
|
|
# So only use this special option if we need to debug this one method! |
3678
|
0
|
|
0
|
|
|
|
my $debug = ${*$self}{_FTPSSL_arguments}->{debug} && ${*$self}{_FTPSSL_arguments}->{debug_extra}; |
3679
|
|
|
|
|
|
|
|
3680
|
|
|
|
|
|
|
# Assuming that if the line doesn't end in a , the response is truncated |
3681
|
|
|
|
|
|
|
# and we'll need the next sysread() to continue with the response. |
3682
|
|
|
|
|
|
|
# Split drops trailing , so need this flag to detect this. |
3683
|
0
|
0
|
|
|
|
|
my $end_with_cr = (substr (${$data_ref}, -2) eq "\015\012") ? 1 : 0; |
|
0
|
|
|
|
|
|
|
3684
|
|
|
|
|
|
|
|
3685
|
0
|
0
|
|
|
|
|
if ( $debug ) { |
3686
|
0
|
0
|
|
|
|
|
my $type = ( exists ${*$self}{_FTPSSL_arguments}->{next_ftp_msg} ) ? "Overflow" : "Current"; |
|
0
|
|
|
|
|
|
|
3687
|
0
|
0
|
|
|
|
|
my $k = $ccc_kludge ? ", Kludge: $ccc_kludge" : ""; |
3688
|
0
|
|
|
|
|
|
_print_LOG ($self, "In _response_details ($type, Status: $status, len = ", length (${$data_ref}), ", End: ${end_with_cr}${k})\n"); |
|
0
|
|
|
|
|
|
|
3689
|
|
|
|
|
|
|
} |
3690
|
|
|
|
|
|
|
|
3691
|
0
|
|
|
|
|
|
my ($ref, $splt); |
3692
|
0
|
0
|
|
|
|
|
if ( exists ${*$self}{_FTPSSL_arguments}->{next_ftp_msg} ) { |
|
0
|
|
|
|
|
|
|
3693
|
0
|
|
|
|
|
|
$ref = \${*$self}{_FTPSSL_arguments}->{next_ftp_msg}; |
|
0
|
|
|
|
|
|
|
3694
|
0
|
|
|
|
|
|
$splt = "\015\012"; |
3695
|
|
|
|
|
|
|
} else { |
3696
|
0
|
|
|
|
|
|
$ref = \${*$self}{_FTPSSL_arguments}->{last_ftp_msg}; |
|
0
|
|
|
|
|
|
|
3697
|
0
|
|
|
|
|
|
$splt = "\n"; |
3698
|
|
|
|
|
|
|
} |
3699
|
|
|
|
|
|
|
|
3700
|
|
|
|
|
|
|
# Sysread() does split the \015 & \012 to seperate lines, so test for it! |
3701
|
|
|
|
|
|
|
# And fix the problem as well if it's found! |
3702
|
0
|
|
|
|
|
|
my $index = 0; |
3703
|
0
|
0
|
|
|
|
|
if ( substr (${$data_ref}, 0, 1) eq "\012" ) { |
|
0
|
|
|
|
|
|
|
3704
|
|
|
|
|
|
|
# It hangs if I strip off from $data_ref, so handle later! (via $index) |
3705
|
0
|
0
|
|
|
|
|
if ( substr (${$ref}, -1) eq "\015" ) { |
|
0
|
|
|
|
|
|
|
3706
|
0
|
|
|
|
|
|
substr (${$ref}, -1) = $splt; # Replace with proper terminator. |
|
0
|
|
|
|
|
|
|
3707
|
0
|
|
|
|
|
|
$index = 1; |
3708
|
0
|
0
|
|
|
|
|
_print_LOG ($self, "Fixed 015/012 split!\n") if ( $debug ); |
3709
|
0
|
0
|
|
|
|
|
if ( ${$data_ref} eq "\012" ) { |
|
0
|
|
|
|
|
|
|
3710
|
0
|
|
0
|
|
|
|
return ($status || $ccc_kludge, 1); # Only thing on the line. |
3711
|
|
|
|
|
|
|
} |
3712
|
|
|
|
|
|
|
} |
3713
|
|
|
|
|
|
|
} |
3714
|
|
|
|
|
|
|
|
3715
|
|
|
|
|
|
|
# Check if the last line from the previous call was trucated ... |
3716
|
0
|
|
|
|
|
|
my $trunc = ""; |
3717
|
0
|
0
|
0
|
|
|
|
if ( ${$ref} ne "" && substr (${$ref}, -length($splt)) ne $splt ) { |
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
3718
|
0
|
|
|
|
|
|
$trunc = (split ($splt, ${$ref}))[-1]; |
|
0
|
|
|
|
|
|
|
3719
|
|
|
|
|
|
|
} |
3720
|
|
|
|
|
|
|
|
3721
|
0
|
|
|
|
|
|
my @term; |
3722
|
|
|
|
|
|
|
my @data; |
3723
|
0
|
0
|
|
|
|
|
if ( $end_with_cr ) { |
3724
|
|
|
|
|
|
|
# Protects from split throwing away trailing empty lines ... |
3725
|
0
|
|
|
|
|
|
@data = split( "\015\012", substr ( ${$data_ref}, $index ) . "|" ); |
|
0
|
|
|
|
|
|
|
3726
|
0
|
|
|
|
|
|
pop (@data); |
3727
|
|
|
|
|
|
|
} else { |
3728
|
|
|
|
|
|
|
# Last line was truncated ... |
3729
|
0
|
|
|
|
|
|
@data = split( "\015\012", substr ( ${$data_ref}, $index ) ); |
|
0
|
|
|
|
|
|
|
3730
|
|
|
|
|
|
|
} |
3731
|
|
|
|
|
|
|
|
3732
|
|
|
|
|
|
|
# Tag which lines are complete! (Only the last one can be truncated) |
3733
|
0
|
|
|
|
|
|
foreach (0..$#data) { |
3734
|
0
|
|
|
|
|
|
$term[$_] = 1; |
3735
|
|
|
|
|
|
|
} |
3736
|
0
|
|
|
|
|
|
$term[-1] = $end_with_cr; |
3737
|
|
|
|
|
|
|
|
3738
|
|
|
|
|
|
|
# Current command or rolled over to the next command ??? |
3739
|
0
|
|
|
|
|
|
my (@lines, @next, @line_term, @next_term); |
3740
|
0
|
0
|
|
|
|
|
if ( exists ${*$self}{_FTPSSL_arguments}->{next_ftp_msg} ) { |
|
0
|
|
|
|
|
|
|
3741
|
0
|
|
|
|
|
|
@next = @data; |
3742
|
0
|
|
|
|
|
|
@next_term = @term; |
3743
|
0
|
|
|
|
|
|
@data = @term = @lines; # All are now empty. |
3744
|
|
|
|
|
|
|
} else { |
3745
|
0
|
|
|
|
|
|
@lines = @data; |
3746
|
0
|
|
|
|
|
|
@line_term = @term; |
3747
|
0
|
|
|
|
|
|
@data = @term = @next; # All are now empty. |
3748
|
|
|
|
|
|
|
} |
3749
|
|
|
|
|
|
|
|
3750
|
|
|
|
|
|
|
# ------------------------------------------------------------------------ |
3751
|
|
|
|
|
|
|
# Now lets process the response messages we've read in. See the comments |
3752
|
|
|
|
|
|
|
# above response() on why this code is such a mess. |
3753
|
|
|
|
|
|
|
# But it's much cleaner than it used to be. |
3754
|
|
|
|
|
|
|
# ------------------------------------------------------------------------ |
3755
|
0
|
|
|
|
|
|
my ( $code, $sep, $desc, $done ) = ( CMD_ERROR, "-", "", 0 ); |
3756
|
0
|
|
|
|
|
|
my ( $line, $term ); |
3757
|
|
|
|
|
|
|
|
3758
|
0
|
|
|
|
|
|
foreach ( 0..$#lines ) { |
3759
|
0
|
|
|
|
|
|
$line = $lines[$_]; |
3760
|
0
|
|
|
|
|
|
$term = $line_term[$_]; |
3761
|
|
|
|
|
|
|
|
3762
|
|
|
|
|
|
|
# If the previous line was the end of the response ... |
3763
|
|
|
|
|
|
|
# There can be no in that line! |
3764
|
|
|
|
|
|
|
# So if true, it means we've read past the end of the response! |
3765
|
0
|
0
|
|
|
|
|
if ( $done ) { |
3766
|
0
|
|
|
|
|
|
push (@next, $line); |
3767
|
0
|
|
|
|
|
|
push (@next_term, $term); |
3768
|
0
|
|
|
|
|
|
next; |
3769
|
|
|
|
|
|
|
} |
3770
|
|
|
|
|
|
|
|
3771
|
|
|
|
|
|
|
# Always represents the start of a new line ... |
3772
|
0
|
|
|
|
|
|
my $test = $trunc . $line; |
3773
|
0
|
|
|
|
|
|
$trunc = ""; # No longer possible for previous line to be truncated. |
3774
|
|
|
|
|
|
|
|
3775
|
|
|
|
|
|
|
# Check if this line marks the response complete! (If sep is a space) |
3776
|
0
|
0
|
|
|
|
|
if ( $test =~ m/^(\d{3})([-\s])(.*)$/s ) { |
3777
|
0
|
|
|
|
|
|
($code, $sep, $desc) = ($1, $2, $3); |
3778
|
0
|
0
|
|
|
|
|
$done = ($sep eq " ") ? $term : 0; |
3779
|
|
|
|
|
|
|
|
3780
|
|
|
|
|
|
|
# Update the return status ... |
3781
|
0
|
0
|
|
|
|
|
$end_response = ($sep eq " ") ? 1: 0; |
3782
|
0
|
|
|
|
|
|
$response_complete = $term; |
3783
|
|
|
|
|
|
|
} |
3784
|
|
|
|
|
|
|
|
3785
|
|
|
|
|
|
|
# The CCC command messes up the Command Channel for a while! |
3786
|
|
|
|
|
|
|
# So we need this work arround to immediately stop processing |
3787
|
|
|
|
|
|
|
# to avoid breaking the command channel or hanging things. |
3788
|
0
|
0
|
0
|
|
|
|
if ( $ccc_kludge && $term && ! $done ) { |
|
|
|
0
|
|
|
|
|
3789
|
0
|
0
|
|
|
|
|
_print_LOG ( $self, "Kludge: 1st CCC work around detected ...\n") if ( $debug ); |
3790
|
0
|
|
|
|
|
|
$end_response = $response_complete = $done = 1; |
3791
|
|
|
|
|
|
|
} |
3792
|
|
|
|
|
|
|
|
3793
|
|
|
|
|
|
|
# Save the unedited message ... |
3794
|
0
|
|
|
|
|
|
${*$self}{_FTPSSL_arguments}->{last_ftp_msg} .= $line; |
|
0
|
|
|
|
|
|
|
3795
|
|
|
|
|
|
|
|
3796
|
|
|
|
|
|
|
# Write to the log file if requested ... |
3797
|
|
|
|
|
|
|
# But due to random splits, it risks not masking properly! |
3798
|
0
|
0
|
|
|
|
|
_print_edited_response ( $self, $prefix, $line, undef, 1 ) if ( $debug ); |
3799
|
|
|
|
|
|
|
|
3800
|
|
|
|
|
|
|
# Finish the current line ... |
3801
|
0
|
0
|
0
|
|
|
|
if ($sep eq "-" && $term) { |
3802
|
0
|
|
|
|
|
|
${*$self}{_FTPSSL_arguments}->{last_ftp_msg} .= "\n"; # Restore the internal . |
|
0
|
|
|
|
|
|
|
3803
|
|
|
|
|
|
|
} |
3804
|
|
|
|
|
|
|
} |
3805
|
|
|
|
|
|
|
|
3806
|
|
|
|
|
|
|
# ------------------------------------------------------------------------ |
3807
|
|
|
|
|
|
|
# Process the response to the next command ... (read in with this one) |
3808
|
|
|
|
|
|
|
# Shouldn't happen, but it sometimes does ... |
3809
|
|
|
|
|
|
|
# ------------------------------------------------------------------------ |
3810
|
0
|
|
|
|
|
|
my $warn = "Warning: Attempting to read past end of response! "; |
3811
|
0
|
|
|
|
|
|
my $next_kludge = 0; |
3812
|
0
|
|
|
|
|
|
$done = 0; |
3813
|
0
|
|
|
|
|
|
foreach ( 0..$#next ) { |
3814
|
0
|
|
|
|
|
|
$next_kludge = 1; |
3815
|
0
|
|
|
|
|
|
$line = $next[$_]; |
3816
|
0
|
|
|
|
|
|
$term = $next_term[$_]; |
3817
|
|
|
|
|
|
|
|
3818
|
|
|
|
|
|
|
# We've read past the end of the current response into the next one ... |
3819
|
0
|
0
|
|
|
|
|
_print_edited_response ( $self, $warn, $line, undef, 2 ) if ( $debug ); |
3820
|
|
|
|
|
|
|
|
3821
|
0
|
0
|
|
|
|
|
if ( ! exists ${*$self}{_FTPSSL_arguments}->{next_ftp_msg} ) { |
|
0
|
0
|
|
|
|
|
|
3822
|
0
|
|
|
|
|
|
${*$self}{_FTPSSL_arguments}->{next_ftp_msg} = $line; |
|
0
|
|
|
|
|
|
|
3823
|
|
|
|
|
|
|
} elsif ( $trunc ne "" ) { |
3824
|
0
|
|
|
|
|
|
${*$self}{_FTPSSL_arguments}->{next_ftp_msg} .= $line; |
|
0
|
|
|
|
|
|
|
3825
|
|
|
|
|
|
|
} else { |
3826
|
0
|
|
|
|
|
|
${*$self}{_FTPSSL_arguments}->{next_ftp_msg} .= "\015\012" . $line; |
|
0
|
|
|
|
|
|
|
3827
|
|
|
|
|
|
|
} |
3828
|
|
|
|
|
|
|
|
3829
|
|
|
|
|
|
|
# Always represents the start of a new line ... |
3830
|
0
|
|
|
|
|
|
my $test = $trunc . $line; |
3831
|
0
|
|
|
|
|
|
$trunc = ""; # No longer possible for previous line to be truncated. |
3832
|
|
|
|
|
|
|
|
3833
|
|
|
|
|
|
|
# Check if this line marks the response complete! (If sep is a space) |
3834
|
0
|
0
|
|
|
|
|
if ( $test =~ m/^(\d{3})([-\s])(.*)$/s ) { |
3835
|
0
|
|
|
|
|
|
($code, $sep, $desc) = ($1, $2, $3); |
3836
|
0
|
0
|
|
|
|
|
$done = ($sep eq " ") ? $term : 0; |
3837
|
|
|
|
|
|
|
|
3838
|
|
|
|
|
|
|
# Update the return status ... |
3839
|
0
|
0
|
|
|
|
|
$end_response = ($sep eq " ") ? 1: 0; |
3840
|
0
|
|
|
|
|
|
$response_complete = $term; |
3841
|
|
|
|
|
|
|
} |
3842
|
|
|
|
|
|
|
} |
3843
|
|
|
|
|
|
|
|
3844
|
0
|
0
|
0
|
|
|
|
if ( $end_with_cr && exists ${*$self}{_FTPSSL_arguments}->{next_ftp_msg} ) { |
|
0
|
|
|
|
|
|
|
3845
|
0
|
|
|
|
|
|
${*$self}{_FTPSSL_arguments}->{next_ftp_msg} .= "\015\012"; |
|
0
|
|
|
|
|
|
|
3846
|
|
|
|
|
|
|
} |
3847
|
|
|
|
|
|
|
|
3848
|
|
|
|
|
|
|
# Complete the Kludge! (Only needed if entered the @next loop!) |
3849
|
0
|
0
|
0
|
|
|
|
if ( $ccc_kludge && $next_kludge && ! ($end_response && $response_complete) ) { |
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
3850
|
0
|
0
|
|
|
|
|
_print_LOG ( $self, "Kludge: 2nd CCC work around detected ...\n") if ( $debug ); |
3851
|
0
|
|
|
|
|
|
$end_response = $response_complete = 1; |
3852
|
|
|
|
|
|
|
} |
3853
|
|
|
|
|
|
|
|
3854
|
0
|
|
|
|
|
|
return ($end_response, $response_complete); |
3855
|
|
|
|
|
|
|
} |
3856
|
|
|
|
|
|
|
|
3857
|
|
|
|
|
|
|
#----------------------------------------------------------------------- |
3858
|
|
|
|
|
|
|
|
3859
|
|
|
|
|
|
|
sub last_message { |
3860
|
0
|
|
|
0
|
1
|
|
my $self = shift; |
3861
|
0
|
|
|
|
|
|
return ${*$self}{_FTPSSL_arguments}->{last_ftp_msg}; |
|
0
|
|
|
|
|
|
|
3862
|
|
|
|
|
|
|
} |
3863
|
|
|
|
|
|
|
|
3864
|
|
|
|
|
|
|
#----------------------------------------------------------------------- |
3865
|
|
|
|
|
|
|
# This method sets up a trap so that warnings can be written to my logs. |
3866
|
|
|
|
|
|
|
# Always call like: $ftps->trapWarn(). |
3867
|
|
|
|
|
|
|
#----------------------------------------------------------------------- |
3868
|
|
|
|
|
|
|
sub trapWarn { |
3869
|
0
|
|
|
0
|
1
|
|
my $self = shift; |
3870
|
0
|
|
0
|
|
|
|
my $force = shift || 0; # Only used by t/10-compelx.t & t/20-certificate.t |
3871
|
|
|
|
|
|
|
# Do not use the $force parameter otherwise! |
3872
|
|
|
|
|
|
|
# You've been warned! |
3873
|
|
|
|
|
|
|
|
3874
|
0
|
|
|
|
|
|
my $res = 0; # Warnings are not yet trapped ... |
3875
|
|
|
|
|
|
|
|
3876
|
|
|
|
|
|
|
# Only trap warnings if a debug log is turned on to write to ... |
3877
|
0
|
0
|
0
|
|
|
|
if ( defined $self && ${*$self}{_FTPSSL_arguments}->{debug} && |
|
0
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
3878
|
|
|
|
|
|
|
($force || exists ${*$self}{_FTPSSL_arguments}->{ftpssl_filehandle}) ) { |
3879
|
0
|
|
|
|
|
|
my $tmp = $SIG{__WARN__}; |
3880
|
|
|
|
|
|
|
|
3881
|
|
|
|
|
|
|
# Must do as an inline function call so things will go to |
3882
|
|
|
|
|
|
|
# the proper log file. |
3883
|
0
|
|
|
0
|
|
|
my $func_ref = sub { $self->_print_LOG ("WARNING: ", $_[0]); }; |
|
0
|
|
|
|
|
|
|
3884
|
|
|
|
|
|
|
|
3885
|
0
|
|
|
|
|
|
$warn_list{$self} = $func_ref; |
3886
|
|
|
|
|
|
|
|
3887
|
|
|
|
|
|
|
# This test prevents a recursive trap ... |
3888
|
0
|
0
|
|
|
|
|
if (! exists $warn_list{OTHER}) { |
3889
|
0
|
|
|
|
|
|
$warn_list{OTHER} = $tmp; |
3890
|
0
|
|
|
|
|
|
$SIG{__WARN__} = __PACKAGE__ . "::_handleWarn"; |
3891
|
|
|
|
|
|
|
} |
3892
|
|
|
|
|
|
|
|
3893
|
0
|
|
|
|
|
|
$res = 1; # The warnings are trapped now ... |
3894
|
|
|
|
|
|
|
} |
3895
|
|
|
|
|
|
|
|
3896
|
0
|
|
|
|
|
|
return ($res); # Whether trapped or not! |
3897
|
|
|
|
|
|
|
} |
3898
|
|
|
|
|
|
|
|
3899
|
|
|
|
|
|
|
# Warning, this method cannot be called as a member function. |
3900
|
|
|
|
|
|
|
# So it will never reference $self! It's also not documented in the POD! |
3901
|
|
|
|
|
|
|
# See trapWarn() instead! |
3902
|
|
|
|
|
|
|
sub _handleWarn { |
3903
|
0
|
|
|
0
|
|
|
my $warn = shift; # The warning being processed ... |
3904
|
|
|
|
|
|
|
|
3905
|
|
|
|
|
|
|
# Print warning to each of the registered log files. |
3906
|
|
|
|
|
|
|
# Will always be a reference to the function to call! |
3907
|
0
|
|
|
|
|
|
my $func_ref; |
3908
|
0
|
|
|
|
|
|
foreach ( keys %warn_list ) { |
3909
|
0
|
0
|
|
|
|
|
next if ($_ eq "OTHER"); |
3910
|
0
|
|
|
|
|
|
$func_ref = $warn_list{$_}; |
3911
|
0
|
|
|
|
|
|
$func_ref->( $warn ); # Prints to an open Net::FTPSSL log file ... |
3912
|
|
|
|
|
|
|
} |
3913
|
|
|
|
|
|
|
|
3914
|
|
|
|
|
|
|
# Was there any parent we replaced to chain the warning to? |
3915
|
0
|
0
|
0
|
|
|
|
if (exists $warn_list{OTHER} && defined $warn_list{OTHER}) { |
3916
|
0
|
|
|
|
|
|
$func_ref = $warn_list{OTHER}; |
3917
|
0
|
0
|
0
|
|
|
|
if (ref ($func_ref) eq "CODE") { |
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
3918
|
0
|
|
|
|
|
|
$func_ref->( $warn ); |
3919
|
|
|
|
|
|
|
} elsif ( $func_ref eq "" || $func_ref eq "DEFAULT" ) { |
3920
|
0
|
|
|
|
|
|
print STDERR "$warn\n"; |
3921
|
|
|
|
|
|
|
} elsif ( $func_ref ne "IGNORE" ) { |
3922
|
0
|
|
|
|
|
|
&{\&{$func_ref}}($warn); # Will throw exception if doesn't exist! |
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
3923
|
|
|
|
|
|
|
} |
3924
|
|
|
|
|
|
|
} |
3925
|
|
|
|
|
|
|
} |
3926
|
|
|
|
|
|
|
|
3927
|
|
|
|
|
|
|
# Called automatically when an instance of Net::FTPSSL goes out of scope! |
3928
|
|
|
|
|
|
|
# Only called if new() was successfull! Used so we could remove all this |
3929
|
|
|
|
|
|
|
# termination logic from quit()! |
3930
|
|
|
|
|
|
|
sub DESTROY { |
3931
|
0
|
|
|
0
|
|
|
my $self = shift; |
3932
|
|
|
|
|
|
|
|
3933
|
0
|
0
|
|
|
|
|
if ( ${*$self}{_FTPSSL_arguments}->{debug} ) { |
|
0
|
|
|
|
|
|
|
3934
|
|
|
|
|
|
|
# Disable optional trapping of the warnings written to the log file |
3935
|
|
|
|
|
|
|
# now that we're going out of scope! |
3936
|
0
|
0
|
|
|
|
|
if ( exists $warn_list{$self} ) { |
3937
|
0
|
|
|
|
|
|
delete ($warn_list{$self}); |
3938
|
|
|
|
|
|
|
} |
3939
|
|
|
|
|
|
|
|
3940
|
|
|
|
|
|
|
# Now let's close the log file itself ... |
3941
|
0
|
|
|
|
|
|
$self->_close_LOG (); |
3942
|
|
|
|
|
|
|
|
3943
|
|
|
|
|
|
|
# Comment out this Debug Statement when no longer needed! |
3944
|
|
|
|
|
|
|
# print STDERR "Good Bye FTPSSL instance! (", ref($self), ") [$self]\n"; |
3945
|
|
|
|
|
|
|
} |
3946
|
|
|
|
|
|
|
} |
3947
|
|
|
|
|
|
|
|
3948
|
|
|
|
|
|
|
# Called automatically when this module is removed from memory. |
3949
|
|
|
|
|
|
|
# NOTE: Due to how Perl's garbage collector works, in many cases END may be |
3950
|
|
|
|
|
|
|
# called before DESTROY is called! Not what you'd expect! |
3951
|
|
|
|
|
|
|
sub END { |
3952
|
|
|
|
|
|
|
# Restore to original setting when the module gets unloaded from memory! |
3953
|
|
|
|
|
|
|
# If this entry wasn't created, then we never redirected any warnings! |
3954
|
4
|
50
|
|
4
|
|
3051365
|
if ( exists $warn_list{OTHER} ) { |
3955
|
0
|
|
|
|
|
|
$SIG{__WARN__} = $warn_list{OTHER}; |
3956
|
0
|
|
|
|
|
|
delete ( $warn_list{OTHER} ); |
3957
|
|
|
|
|
|
|
# print STDERR "Good Bye FTPSSL! (", $SIG{__WARN__}, ")\n"; |
3958
|
|
|
|
|
|
|
} |
3959
|
|
|
|
|
|
|
} |
3960
|
|
|
|
|
|
|
|
3961
|
|
|
|
|
|
|
#----------------------------------------------------------------------- |
3962
|
|
|
|
|
|
|
# Not in POD on purpose. It's an internal work arround for a debug issue. |
3963
|
|
|
|
|
|
|
# Replace all chars known to cause issues with RegExp by putting |
3964
|
|
|
|
|
|
|
# a "\" in front of it to remove the chars special meaning. |
3965
|
|
|
|
|
|
|
# (less messy than putting it into square brackets ...) |
3966
|
|
|
|
|
|
|
#----------------------------------------------------------------------- |
3967
|
|
|
|
|
|
|
sub _mask_regex_chars { |
3968
|
0
|
|
|
0
|
|
|
my $self = shift; |
3969
|
0
|
|
|
|
|
|
my $mask = shift; |
3970
|
|
|
|
|
|
|
|
3971
|
0
|
|
|
|
|
|
$mask =~ s/([([?+*\\^$).])/\\$1/g; |
3972
|
|
|
|
|
|
|
|
3973
|
0
|
|
|
|
|
|
return ($mask); |
3974
|
|
|
|
|
|
|
} |
3975
|
|
|
|
|
|
|
|
3976
|
|
|
|
|
|
|
#----------------------------------------------------------------------- |
3977
|
|
|
|
|
|
|
# Added to make backwards compatible with Net::FTP |
3978
|
|
|
|
|
|
|
#----------------------------------------------------------------------- |
3979
|
|
|
|
|
|
|
sub message { |
3980
|
0
|
|
|
0
|
1
|
|
my $self = shift; |
3981
|
0
|
|
|
|
|
|
return ${*$self}{_FTPSSL_arguments}->{last_ftp_msg}; |
|
0
|
|
|
|
|
|
|
3982
|
|
|
|
|
|
|
} |
3983
|
|
|
|
|
|
|
|
3984
|
|
|
|
|
|
|
sub last_status_code { |
3985
|
0
|
|
|
0
|
1
|
|
my $self = shift; |
3986
|
|
|
|
|
|
|
|
3987
|
0
|
|
|
|
|
|
my $code = CMD_ERROR; |
3988
|
0
|
0
|
|
|
|
|
if ( defined ${*$self}{_FTPSSL_arguments}->{last_ftp_msg} ) { |
|
0
|
|
|
|
|
|
|
3989
|
0
|
|
|
|
|
|
$code = substr (${*$self}{_FTPSSL_arguments}->{last_ftp_msg}, 0, 1); |
|
0
|
|
|
|
|
|
|
3990
|
|
|
|
|
|
|
} |
3991
|
|
|
|
|
|
|
|
3992
|
0
|
|
|
|
|
|
return ($code); |
3993
|
|
|
|
|
|
|
} |
3994
|
|
|
|
|
|
|
|
3995
|
|
|
|
|
|
|
sub _change_status_code { |
3996
|
0
|
|
|
0
|
|
|
my $self = shift; |
3997
|
0
|
|
|
|
|
|
my $code = shift; # Should be a single digit. Strange behaviour otherwise! |
3998
|
|
|
|
|
|
|
|
3999
|
0
|
0
|
|
|
|
|
if ( defined ${*$self}{_FTPSSL_arguments}->{last_ftp_msg} ) { |
|
0
|
|
|
|
|
|
|
4000
|
0
|
|
|
|
|
|
substr (${*$self}{_FTPSSL_arguments}->{last_ftp_msg}, 0, 1) = $code; |
|
0
|
|
|
|
|
|
|
4001
|
|
|
|
|
|
|
} |
4002
|
|
|
|
|
|
|
|
4003
|
0
|
|
|
|
|
|
return; |
4004
|
|
|
|
|
|
|
} |
4005
|
|
|
|
|
|
|
|
4006
|
|
|
|
|
|
|
sub restart { |
4007
|
0
|
|
|
0
|
1
|
|
my $self = shift; |
4008
|
0
|
|
|
|
|
|
my $offset = shift; |
4009
|
0
|
|
|
|
|
|
${*$self}{_FTPSSL_arguments}->{net_ftpssl_rest_offset} = $offset; |
|
0
|
|
|
|
|
|
|
4010
|
0
|
|
|
|
|
|
return (1); |
4011
|
|
|
|
|
|
|
} |
4012
|
|
|
|
|
|
|
|
4013
|
|
|
|
|
|
|
#----------------------------------------------------------------------- |
4014
|
|
|
|
|
|
|
# Implements data channel call back functionality ... |
4015
|
|
|
|
|
|
|
#----------------------------------------------------------------------- |
4016
|
|
|
|
|
|
|
sub set_callback { |
4017
|
0
|
|
|
0
|
1
|
|
my $self = shift; |
4018
|
0
|
|
|
|
|
|
my $func_ref = shift; # The callback function to call. |
4019
|
0
|
|
|
|
|
|
my $end_func_ref = shift; # The end callback function to call. |
4020
|
0
|
|
|
|
|
|
my $cb_work_area_ref = shift; # Optional ref to the callback work area! |
4021
|
|
|
|
|
|
|
|
4022
|
0
|
0
|
0
|
|
|
|
if ( defined $func_ref && defined $end_func_ref ) { |
4023
|
0
|
|
|
|
|
|
${*$self}{_FTPSSL_arguments}->{callback_func} = $func_ref; |
|
0
|
|
|
|
|
|
|
4024
|
0
|
|
|
|
|
|
${*$self}{_FTPSSL_arguments}->{callback_end_func} = $end_func_ref; |
|
0
|
|
|
|
|
|
|
4025
|
0
|
|
|
|
|
|
${*$self}{_FTPSSL_arguments}->{callback_data} = $cb_work_area_ref; |
|
0
|
|
|
|
|
|
|
4026
|
|
|
|
|
|
|
} else { |
4027
|
0
|
|
|
|
|
|
delete ( ${*$self}{_FTPSSL_arguments}->{callback_func} ); |
|
0
|
|
|
|
|
|
|
4028
|
0
|
|
|
|
|
|
delete ( ${*$self}{_FTPSSL_arguments}->{callback_end_func} ); |
|
0
|
|
|
|
|
|
|
4029
|
0
|
|
|
|
|
|
delete ( ${*$self}{_FTPSSL_arguments}->{callback_data} ); |
|
0
|
|
|
|
|
|
|
4030
|
|
|
|
|
|
|
} |
4031
|
|
|
|
|
|
|
|
4032
|
0
|
|
|
|
|
|
return; |
4033
|
|
|
|
|
|
|
} |
4034
|
|
|
|
|
|
|
|
4035
|
|
|
|
|
|
|
sub _end_callback { |
4036
|
0
|
|
|
0
|
|
|
my $self = shift; |
4037
|
0
|
|
|
|
|
|
my $offset = shift; # Always >= 1. Index to original function called. |
4038
|
0
|
|
|
|
|
|
my $total = shift; |
4039
|
|
|
|
|
|
|
|
4040
|
0
|
|
|
|
|
|
my $res; |
4041
|
0
|
|
|
|
|
|
my $len = 0; |
4042
|
|
|
|
|
|
|
|
4043
|
|
|
|
|
|
|
# Is there an end callback function to use ? |
4044
|
0
|
0
|
|
|
|
|
if ( defined ${*$self}{_FTPSSL_arguments}->{callback_end_func} ) { |
|
0
|
|
|
|
|
|
|
4045
|
0
|
|
|
|
|
|
$res = &{${*$self}{_FTPSSL_arguments}->{callback_end_func}} ( (caller($offset))[3], $total, |
|
0
|
|
|
|
|
|
|
4046
|
0
|
|
|
|
|
|
${*$self}{_FTPSSL_arguments}->{callback_data} ); |
|
0
|
|
|
|
|
|
|
4047
|
|
|
|
|
|
|
|
4048
|
|
|
|
|
|
|
# Now check the results for terminating the call back. |
4049
|
0
|
0
|
|
|
|
|
if (defined $res) { |
4050
|
0
|
0
|
|
|
|
|
if ($res eq "") { |
4051
|
0
|
|
|
|
|
|
$res = undef; # Make it easier to work with. |
4052
|
|
|
|
|
|
|
} else { |
4053
|
0
|
|
|
|
|
|
$len = length ($res); |
4054
|
0
|
|
|
|
|
|
$total += $len; |
4055
|
|
|
|
|
|
|
} |
4056
|
|
|
|
|
|
|
} |
4057
|
|
|
|
|
|
|
} |
4058
|
|
|
|
|
|
|
|
4059
|
0
|
|
|
|
|
|
return ($res, $len, $total); |
4060
|
|
|
|
|
|
|
} |
4061
|
|
|
|
|
|
|
|
4062
|
|
|
|
|
|
|
sub _call_callback { |
4063
|
0
|
|
|
0
|
|
|
my $self = shift; |
4064
|
0
|
|
|
|
|
|
my $offset = shift; # Always >= 1. Index to original function called. |
4065
|
|
|
|
|
|
|
|
4066
|
0
|
|
|
|
|
|
my $data_ref = shift; |
4067
|
0
|
|
|
|
|
|
my $data_len_ref = shift; |
4068
|
0
|
|
|
|
|
|
my $total_len = shift; |
4069
|
|
|
|
|
|
|
|
4070
|
|
|
|
|
|
|
# Is there is a callback function to use ? |
4071
|
0
|
0
|
|
|
|
|
if ( defined ${*$self}{_FTPSSL_arguments}->{callback_func} ) { |
|
0
|
|
|
|
|
|
|
4072
|
|
|
|
|
|
|
|
4073
|
|
|
|
|
|
|
# Allowed to modify contents of $data_ref & $data_len_ref ... |
4074
|
0
|
|
|
|
|
|
&{${*$self}{_FTPSSL_arguments}->{callback_func}} ( (caller($offset))[3], |
|
0
|
|
|
|
|
|
|
4075
|
|
|
|
|
|
|
$data_ref, $data_len_ref, $total_len, |
4076
|
0
|
|
|
|
|
|
${*$self}{_FTPSSL_arguments}->{callback_data} ); |
|
0
|
|
|
|
|
|
|
4077
|
|
|
|
|
|
|
} |
4078
|
|
|
|
|
|
|
|
4079
|
|
|
|
|
|
|
# Calculate the new total length to use for next time ... |
4080
|
0
|
0
|
|
|
|
|
$total_len += (defined $data_len_ref ? ${$data_len_ref} : 0); |
|
0
|
|
|
|
|
|
|
4081
|
|
|
|
|
|
|
|
4082
|
0
|
|
|
|
|
|
return ($total_len); |
4083
|
|
|
|
|
|
|
} |
4084
|
|
|
|
|
|
|
|
4085
|
|
|
|
|
|
|
sub _fmt_num { |
4086
|
0
|
|
|
0
|
|
|
my $self = shift; |
4087
|
0
|
|
|
|
|
|
my $num = shift; |
4088
|
|
|
|
|
|
|
|
4089
|
|
|
|
|
|
|
# Change: 1234567890 --> 1,234,567,890 |
4090
|
0
|
|
|
|
|
|
while ( $num =~ s/(\d)(\d{3}(\D|$))/$1,$2/ ) { } |
4091
|
|
|
|
|
|
|
|
4092
|
0
|
|
|
|
|
|
return ( $num ); |
4093
|
|
|
|
|
|
|
} |
4094
|
|
|
|
|
|
|
|
4095
|
|
|
|
|
|
|
#----------------------------------------------------------------------- |
4096
|
|
|
|
|
|
|
# To assist in debugging the flags being used by this module ... |
4097
|
|
|
|
|
|
|
#----------------------------------------------------------------------- |
4098
|
|
|
|
|
|
|
|
4099
|
|
|
|
|
|
|
sub _debug_print_hash |
4100
|
|
|
|
|
|
|
{ |
4101
|
0
|
|
|
0
|
|
|
my $self = shift; |
4102
|
0
|
|
|
|
|
|
my $host = shift; |
4103
|
0
|
|
|
|
|
|
my $port = shift; |
4104
|
0
|
|
|
|
|
|
my $mode = shift; |
4105
|
0
|
|
0
|
|
|
|
my $obj = shift || $self; # So can log most GLOB object types ... |
4106
|
0
|
|
|
|
|
|
my $sep = shift; # The optional separator char to print out. |
4107
|
|
|
|
|
|
|
|
4108
|
0
|
|
|
|
|
|
_print_LOG ( $self, "\nObject ", ref($obj), " Details ..." ); |
4109
|
0
|
0
|
|
|
|
|
_print_LOG ( $self, " ($host:$port - $mode)" ) if (defined $host); |
4110
|
0
|
|
|
|
|
|
_print_LOG ( $self, "\n" ); |
4111
|
|
|
|
|
|
|
|
4112
|
|
|
|
|
|
|
# Fix to support non-GLOB object types ... |
4113
|
0
|
|
|
|
|
|
my @lst; |
4114
|
0
|
|
|
|
|
|
my $hash = 0; |
4115
|
0
|
0
|
|
|
|
|
if ( ref ($obj) eq "HASH" ) { |
4116
|
0
|
|
|
|
|
|
@lst = sort keys %{$obj}; |
|
0
|
|
|
|
|
|
|
4117
|
0
|
|
|
|
|
|
$hash = 1; |
4118
|
|
|
|
|
|
|
} else { |
4119
|
|
|
|
|
|
|
# It's a GLOB reference ... |
4120
|
0
|
|
|
|
|
|
@lst = sort keys %{*$obj}; |
|
0
|
|
|
|
|
|
|
4121
|
|
|
|
|
|
|
} |
4122
|
|
|
|
|
|
|
|
4123
|
|
|
|
|
|
|
# The separators to use ... |
4124
|
0
|
|
|
|
|
|
my @seps = ( "==>", "===>", |
4125
|
|
|
|
|
|
|
"---->", "++++>", "====>", |
4126
|
|
|
|
|
|
|
"----->", "+++++>", "=====>", |
4127
|
|
|
|
|
|
|
"------>", "++++++>", "======>" ); |
4128
|
|
|
|
|
|
|
|
4129
|
|
|
|
|
|
|
# To help detect infinite recursive loops ... |
4130
|
0
|
|
|
|
|
|
my %loop; |
4131
|
|
|
|
|
|
|
my %empty; |
4132
|
|
|
|
|
|
|
|
4133
|
0
|
|
|
|
|
|
foreach (@lst) { |
4134
|
0
|
0
|
|
|
|
|
unless ( defined $host ) { |
4135
|
0
|
0
|
|
|
|
|
next unless ( m/^(io_|_SSL|SSL)/ ); |
4136
|
|
|
|
|
|
|
} |
4137
|
0
|
0
|
|
|
|
|
my $val = ($hash) ? $obj->{$_} : ${*$obj}{$_}; |
|
0
|
|
|
|
|
|
|
4138
|
|
|
|
|
|
|
|
4139
|
0
|
|
|
|
|
|
%loop = %empty; # Empty out the hash again ... |
4140
|
0
|
|
|
|
|
|
_print_hash_tree ( $self, " ", 0, $_, $val, \@seps, \%loop ); |
4141
|
|
|
|
|
|
|
} |
4142
|
|
|
|
|
|
|
|
4143
|
0
|
0
|
0
|
|
|
|
if (defined $sep && $sep !~ m/^\s*$/) { |
4144
|
0
|
|
|
|
|
|
_print_LOG ( $self, $sep x 60, "\n"); |
4145
|
|
|
|
|
|
|
} else { |
4146
|
0
|
|
|
|
|
|
_print_LOG ( $self, "\n" ); |
4147
|
|
|
|
|
|
|
} |
4148
|
|
|
|
|
|
|
|
4149
|
0
|
|
|
|
|
|
return; |
4150
|
|
|
|
|
|
|
} |
4151
|
|
|
|
|
|
|
|
4152
|
|
|
|
|
|
|
# Recursive so can handle unlimited depth of hash trees ... |
4153
|
|
|
|
|
|
|
sub _print_hash_tree |
4154
|
|
|
|
|
|
|
{ |
4155
|
0
|
|
|
0
|
|
|
my $self = shift; |
4156
|
0
|
|
|
|
|
|
my $indent = shift; |
4157
|
0
|
|
|
|
|
|
my $lvl = shift; # Index to the $sep_ref array reference. |
4158
|
0
|
|
|
|
|
|
my $lbl = shift; |
4159
|
0
|
|
|
|
|
|
my $val = shift; |
4160
|
0
|
|
|
|
|
|
my $sep_ref = shift; # An array reference. |
4161
|
0
|
|
|
|
|
|
my $loop_ref = shift; # A hash ref to detect infinit recursion with. |
4162
|
|
|
|
|
|
|
|
4163
|
0
|
0
|
|
|
|
|
my $prefix = ($lvl == 0) ? "" : "-- "; |
4164
|
0
|
0
|
|
|
|
|
my $sep = (defined $sep_ref->[$lvl]) ? $sep_ref->[$lvl] : ".....>"; |
4165
|
|
|
|
|
|
|
|
4166
|
|
|
|
|
|
|
# Make sure it always has a value ... |
4167
|
0
|
0
|
|
|
|
|
$val = "(undef)" unless (defined $val); |
4168
|
|
|
|
|
|
|
|
4169
|
|
|
|
|
|
|
# Fix indentation in case "\n" appears in the value ... |
4170
|
0
|
0
|
|
|
|
|
$val = join ("\n${indent} ", split (/\n/, $val)) unless (ref($val)); |
4171
|
|
|
|
|
|
|
|
4172
|
|
|
|
|
|
|
# Fix in case it's a scalar reference ... |
4173
|
0
|
0
|
|
|
|
|
$val .= " [" . ${$val} . "]" if ($val =~ m/SCALAR\(0/); |
|
0
|
|
|
|
|
|
|
4174
|
|
|
|
|
|
|
|
4175
|
0
|
|
|
|
|
|
my $msg = "${indent}${prefix}${lbl} ${sep} ${val}"; |
4176
|
|
|
|
|
|
|
|
4177
|
|
|
|
|
|
|
# How deep to indent for the next level ... (add 4 spaces) |
4178
|
0
|
|
|
|
|
|
$indent .= " "; |
4179
|
|
|
|
|
|
|
|
4180
|
0
|
0
|
|
|
|
|
if ( $val =~ m/ARRAY\(0/ ) { |
|
|
0
|
|
|
|
|
|
4181
|
0
|
|
|
|
|
|
my $lst = join (", ", @{$val}); |
|
0
|
|
|
|
|
|
|
4182
|
0
|
|
|
|
|
|
_print_LOG ( $self, $msg, "\n" ); |
4183
|
0
|
|
|
|
|
|
_print_LOG ( $self, "${indent}[", $lst, "]\n" ); |
4184
|
|
|
|
|
|
|
|
4185
|
|
|
|
|
|
|
} elsif ( $val =~ m/HASH\((0x[\da-zA-Z]+)\)/ ) { |
4186
|
0
|
|
|
|
|
|
my $key = $1; # The Hash address ... |
4187
|
0
|
|
|
|
|
|
my %start = %{$loop_ref}; |
|
0
|
|
|
|
|
|
|
4188
|
|
|
|
|
|
|
|
4189
|
0
|
|
|
|
|
|
_print_LOG ( $self, $msg ); |
4190
|
0
|
0
|
|
|
|
|
if ( exists $loop_ref->{$key} ) { |
4191
|
0
|
|
|
|
|
|
_print_LOG ($self, " ... Infinite Hash Loop Detected!\n"); |
4192
|
|
|
|
|
|
|
} else { |
4193
|
0
|
|
|
|
|
|
$start{$key} = $loop_ref->{$key} = $val; |
4194
|
0
|
|
|
|
|
|
_print_LOG ( $self, "\n" ); |
4195
|
0
|
|
|
|
|
|
foreach (sort keys %{$val}) { |
|
0
|
|
|
|
|
|
|
4196
|
0
|
|
|
|
|
|
%{$loop_ref} = %start; |
|
0
|
|
|
|
|
|
|
4197
|
0
|
|
|
|
|
|
_print_hash_tree ( $self, $indent, $lvl + 1, $_, $val->{$_}, |
4198
|
|
|
|
|
|
|
$sep_ref, $loop_ref ); |
4199
|
|
|
|
|
|
|
} |
4200
|
|
|
|
|
|
|
} |
4201
|
|
|
|
|
|
|
|
4202
|
|
|
|
|
|
|
# Else not an ARRAY or HASH ... |
4203
|
|
|
|
|
|
|
} else { |
4204
|
0
|
|
|
|
|
|
_print_LOG ( $self, $msg, "\n" ); |
4205
|
|
|
|
|
|
|
} |
4206
|
|
|
|
|
|
|
} |
4207
|
|
|
|
|
|
|
|
4208
|
|
|
|
|
|
|
#----------------------------------------------------------------------- |
4209
|
|
|
|
|
|
|
# Provided so each class instance gets its own log file to write to. |
4210
|
|
|
|
|
|
|
#----------------------------------------------------------------------- |
4211
|
|
|
|
|
|
|
# Always writes to the log when called ... |
4212
|
|
|
|
|
|
|
sub _print_LOG |
4213
|
|
|
|
|
|
|
{ |
4214
|
0
|
|
|
0
|
|
|
my $self = shift; |
4215
|
0
|
|
|
|
|
|
my $msg = shift; |
4216
|
|
|
|
|
|
|
|
4217
|
0
|
|
|
|
|
|
my $FILE; |
4218
|
|
|
|
|
|
|
|
4219
|
|
|
|
|
|
|
# Determine where to write the log message to ... |
4220
|
0
|
0
|
0
|
|
|
|
if ( defined $self && exists ${*$self}{_FTPSSL_arguments}->{ftpssl_filehandle} ) { |
|
0
|
0
|
|
|
|
|
|
4221
|
0
|
|
|
|
|
|
$FILE = ${*$self}{_FTPSSL_arguments}->{ftpssl_filehandle}; # A custom log file ... |
|
0
|
|
|
|
|
|
|
4222
|
|
|
|
|
|
|
} elsif ( defined $FTPS_ERROR ) { |
4223
|
0
|
|
|
|
|
|
$FILE = $FTPS_ERROR; # Write to file when called during new() ... |
4224
|
|
|
|
|
|
|
} else { |
4225
|
0
|
|
|
|
|
|
$FILE = \*STDERR; # Write to screen anyone ? |
4226
|
|
|
|
|
|
|
} |
4227
|
|
|
|
|
|
|
|
4228
|
0
|
|
|
|
|
|
while ( defined $msg ) { |
4229
|
0
|
|
|
|
|
|
print $FILE $msg; # Write to the log file ... |
4230
|
0
|
|
|
|
|
|
$msg = shift; |
4231
|
|
|
|
|
|
|
} |
4232
|
|
|
|
|
|
|
} |
4233
|
|
|
|
|
|
|
|
4234
|
|
|
|
|
|
|
# Only write to the log if debug is turned on ... |
4235
|
|
|
|
|
|
|
# So we don't have to test everywhere ... |
4236
|
|
|
|
|
|
|
# Done this way so can be called in new() on a socket as well. |
4237
|
|
|
|
|
|
|
sub _print_DBG |
4238
|
|
|
|
|
|
|
{ |
4239
|
0
|
|
|
0
|
|
|
my $self = shift; |
4240
|
0
|
0
|
0
|
|
|
|
if ( defined $self && ${*$self}{_FTPSSL_arguments}->{debug} ) { |
|
0
|
|
|
|
|
|
|
4241
|
0
|
|
|
|
|
|
_print_LOG ( $self, @_ ); # Only if debug is turned on ... |
4242
|
|
|
|
|
|
|
} |
4243
|
|
|
|
|
|
|
} |
4244
|
|
|
|
|
|
|
|
4245
|
|
|
|
|
|
|
sub get_log_filehandle |
4246
|
|
|
|
|
|
|
{ |
4247
|
0
|
|
|
0
|
1
|
|
my $self = shift; |
4248
|
|
|
|
|
|
|
|
4249
|
0
|
|
|
|
|
|
my $FILE; |
4250
|
0
|
0
|
0
|
|
|
|
if ( defined $self && exists ${*$self}{_FTPSSL_arguments}->{ftpssl_filehandle} ) { |
|
0
|
|
|
|
|
|
|
4251
|
0
|
|
|
|
|
|
$FILE = ${*$self}{_FTPSSL_arguments}->{ftpssl_filehandle}; |
|
0
|
|
|
|
|
|
|
4252
|
|
|
|
|
|
|
} |
4253
|
|
|
|
|
|
|
|
4254
|
0
|
|
|
|
|
|
return ($FILE); |
4255
|
|
|
|
|
|
|
} |
4256
|
|
|
|
|
|
|
|
4257
|
|
|
|
|
|
|
sub _close_LOG |
4258
|
|
|
|
|
|
|
{ |
4259
|
0
|
|
|
0
|
|
|
my $self = shift; |
4260
|
|
|
|
|
|
|
|
4261
|
0
|
0
|
0
|
|
|
|
if ( defined $self && exists ${*$self}{_FTPSSL_arguments}->{ftpssl_filehandle} ) { |
|
0
|
|
|
|
|
|
|
4262
|
0
|
|
|
|
|
|
my $FILE = ${*$self}{_FTPSSL_arguments}->{ftpssl_filehandle}; |
|
0
|
|
|
|
|
|
|
4263
|
0
|
0
|
|
|
|
|
close ($FILE) if ( ${*$self}{_FTPSSL_arguments}->{debug} == 2 ); |
|
0
|
|
|
|
|
|
|
4264
|
0
|
|
|
|
|
|
delete ( ${*$self}{_FTPSSL_arguments}->{ftpssl_filehandle} ); |
|
0
|
|
|
|
|
|
|
4265
|
0
|
|
|
|
|
|
${*$self}{_FTPSSL_arguments}->{debug} = 1; # Back to using STDERR again ... |
|
0
|
|
|
|
|
|
|
4266
|
|
|
|
|
|
|
} |
4267
|
|
|
|
|
|
|
} |
4268
|
|
|
|
|
|
|
|
4269
|
|
|
|
|
|
|
# A helper method to tell if it can be counted as a GLOB ... |
4270
|
|
|
|
|
|
|
sub _isa_glob |
4271
|
|
|
|
|
|
|
{ |
4272
|
0
|
|
|
0
|
|
|
my $self = shift; |
4273
|
0
|
|
|
|
|
|
my $fh = shift; |
4274
|
|
|
|
|
|
|
|
4275
|
0
|
|
|
|
|
|
my $res = 0; # Assume not a file handle/GLOB ... |
4276
|
|
|
|
|
|
|
|
4277
|
0
|
0
|
|
|
|
|
if ( defined $fh ) { |
4278
|
0
|
|
|
|
|
|
my $tmp = ref ( $fh ); |
4279
|
0
|
0
|
|
|
|
|
if ( $tmp ) { |
4280
|
0
|
0
|
0
|
|
|
|
$res = 1 if ( $tmp eq "GLOB" || $fh->isa ("IO::Handle") ); |
4281
|
|
|
|
|
|
|
} |
4282
|
|
|
|
|
|
|
} |
4283
|
|
|
|
|
|
|
|
4284
|
0
|
|
|
|
|
|
return ( $res ); |
4285
|
|
|
|
|
|
|
} |
4286
|
|
|
|
|
|
|
|
4287
|
|
|
|
|
|
|
#----------------------------------------------------------------------- |
4288
|
|
|
|
|
|
|
# If the Domain/Family is passed as a string, this function will convert |
4289
|
|
|
|
|
|
|
# it into the needed numerical value. [Only called by new().] |
4290
|
|
|
|
|
|
|
sub _validate_domain { |
4291
|
0
|
|
|
0
|
|
|
my $type = shift; # It's a string, not an Net::FTPSSL object! |
4292
|
0
|
|
|
|
|
|
my $family = shift; # The tag used for this value. |
4293
|
0
|
|
|
|
|
|
my $domain = shift; # Should never be undef when called. |
4294
|
0
|
|
|
|
|
|
my $debug = shift; |
4295
|
0
|
|
|
|
|
|
my $die = shift; |
4296
|
|
|
|
|
|
|
|
4297
|
0
|
|
|
|
|
|
my $ret; |
4298
|
|
|
|
|
|
|
|
4299
|
0
|
0
|
|
|
|
|
if ( $domain =~ m/^\d+$/ ) { |
|
|
0
|
|
|
|
|
|
4300
|
0
|
|
|
|
|
|
$ret = $domain; # Already a numeric value, so just return it ... |
4301
|
|
|
|
|
|
|
|
4302
|
|
|
|
|
|
|
# Valid domains are inherited functions named after the value! |
4303
|
|
|
|
|
|
|
} elsif ( $domain =~ m/^AF_/i ) { |
4304
|
0
|
0
|
|
|
|
|
if ( $type->can ( uc ($domain) ) ) { |
4305
|
0
|
|
|
|
|
|
my $func = $type . "::" . uc ($domain) . "()"; |
4306
|
0
|
|
|
|
|
|
$ret = eval $func; # Call the function to convert it to an integer! |
4307
|
|
|
|
|
|
|
} |
4308
|
|
|
|
|
|
|
} |
4309
|
|
|
|
|
|
|
|
4310
|
0
|
0
|
|
|
|
|
unless ( defined $ret ) { |
4311
|
0
|
|
|
|
|
|
_croak_or_return ( undef, $die, $debug, |
4312
|
|
|
|
|
|
|
"Unknown value \"${domain}\" for option ${family}." ); |
4313
|
|
|
|
|
|
|
} |
4314
|
|
|
|
|
|
|
|
4315
|
|
|
|
|
|
|
# Return the domain/family as a numeric value. |
4316
|
|
|
|
|
|
|
# Can be undef if invalid & Croak is turned off. |
4317
|
0
|
|
|
|
|
|
return ( $ret ); |
4318
|
|
|
|
|
|
|
} |
4319
|
|
|
|
|
|
|
|
4320
|
|
|
|
|
|
|
|
4321
|
|
|
|
|
|
|
#----------------------------------------------------------------------- |
4322
|
|
|
|
|
|
|
|
4323
|
|
|
|
|
|
|
1; |
4324
|
|
|
|
|
|
|
|
4325
|
|
|
|
|
|
|
__END__ |