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