| line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
|
1
|
|
|
|
|
|
|
package Net::SSH::Any::_Base; |
|
2
|
|
|
|
|
|
|
|
|
3
|
1
|
|
|
1
|
|
3
|
use strict; |
|
|
1
|
|
|
|
|
1
|
|
|
|
1
|
|
|
|
|
20
|
|
|
4
|
1
|
|
|
1
|
|
2
|
use warnings; |
|
|
1
|
|
|
|
|
1
|
|
|
|
1
|
|
|
|
|
15
|
|
|
5
|
1
|
|
|
1
|
|
2
|
use Carp; |
|
|
1
|
|
|
|
|
1
|
|
|
|
1
|
|
|
|
|
43
|
|
|
6
|
|
|
|
|
|
|
|
|
7
|
1
|
|
|
1
|
|
3
|
use File::Spec; |
|
|
1
|
|
|
|
|
1
|
|
|
|
1
|
|
|
|
|
14
|
|
|
8
|
1
|
|
|
1
|
|
3
|
use Scalar::Util (); |
|
|
1
|
|
|
|
|
1
|
|
|
|
1
|
|
|
|
|
8
|
|
|
9
|
1
|
|
|
1
|
|
2
|
use Encode (); |
|
|
1
|
|
|
|
|
1
|
|
|
|
1
|
|
|
|
|
16
|
|
|
10
|
|
|
|
|
|
|
|
|
11
|
1
|
|
|
1
|
|
2
|
use Net::SSH::Any::Constants qw(SSHA_BACKEND_ERROR SSHA_LOCAL_IO_ERROR SSHA_UNIMPLEMENTED_ERROR SSHA_ENCODING_ERROR); |
|
|
1
|
|
|
|
|
1
|
|
|
|
1
|
|
|
|
|
42
|
|
|
12
|
1
|
|
|
1
|
|
3
|
use Net::SSH::Any::Util; |
|
|
1
|
|
|
|
|
1
|
|
|
|
1
|
|
|
|
|
1955
|
|
|
13
|
|
|
|
|
|
|
our @CARP_NOT = qw(Net::SSH::Any::Util); |
|
14
|
|
|
|
|
|
|
|
|
15
|
|
|
|
|
|
|
sub _new { |
|
16
|
0
|
|
|
0
|
|
|
my ($class, $opts) = @_; |
|
17
|
0
|
|
|
|
|
|
my $os = delete $opts->{os}; |
|
18
|
|
|
|
|
|
|
|
|
19
|
0
|
|
|
|
|
|
my (%remote_cmd, %local_cmd, %remote_extra_args, %local_extra_args); |
|
20
|
0
|
|
|
|
|
|
for (keys %$opts) { |
|
21
|
0
|
0
|
|
|
|
|
/^remote_(.*)_cmd$/ and $remote_cmd{$1} = $opts->{$_}; |
|
22
|
0
|
0
|
|
|
|
|
/^local_(.*)_cmd$/ and $local_cmd{$1} = $opts->{$_}; |
|
23
|
0
|
0
|
|
|
|
|
/^remote_(.*)_extra_args$/ and $remote_extra_args{$1} = $opts->{$_}; |
|
24
|
0
|
0
|
|
|
|
|
/^local_(.*)_extra_args$/ and $local_extra_args{$1} = $opts->{$_}; |
|
25
|
|
|
|
|
|
|
} |
|
26
|
|
|
|
|
|
|
|
|
27
|
0
|
|
|
|
|
|
my $self = { os => $os, |
|
28
|
|
|
|
|
|
|
error => 0, |
|
29
|
|
|
|
|
|
|
error_prefix => [], |
|
30
|
|
|
|
|
|
|
backend_log => [], |
|
31
|
|
|
|
|
|
|
remote_cmd => \%remote_cmd, |
|
32
|
|
|
|
|
|
|
local_cmd => \%local_cmd, |
|
33
|
|
|
|
|
|
|
remote_extra_args => \%remote_extra_args, |
|
34
|
|
|
|
|
|
|
local_extra_args => \%local_extra_args, |
|
35
|
|
|
|
|
|
|
}; |
|
36
|
|
|
|
|
|
|
|
|
37
|
0
|
|
0
|
|
|
|
my $encoding = $self->{encoding} = delete $opts->{encoding} // 'utf8'; |
|
38
|
0
|
|
0
|
|
|
|
$self->{stream_encoding} = delete $opts->{stream_encoding} // $encoding; |
|
39
|
0
|
|
0
|
|
|
|
$self->{argument_encoding} = delete $opts->{argument_encoding} // $encoding; |
|
40
|
|
|
|
|
|
|
|
|
41
|
0
|
|
|
|
|
|
bless $self, $class; |
|
42
|
0
|
|
|
|
|
|
$self; |
|
43
|
|
|
|
|
|
|
} |
|
44
|
|
|
|
|
|
|
|
|
45
|
|
|
|
|
|
|
sub _log_error_and_reset_backend { |
|
46
|
0
|
|
|
0
|
|
|
my $self = shift; |
|
47
|
0
|
|
|
|
|
|
push @{$self->{backend_log}}, "$self->{backend}: [".($self->{error}+0)."] $self->{error}"; |
|
|
0
|
|
|
|
|
|
|
|
48
|
0
|
|
|
|
|
|
$self->{error} = 0; |
|
49
|
0
|
|
|
|
|
|
delete $self->{backend}; |
|
50
|
0
|
|
|
|
|
|
delete $self->{backend_module}; |
|
51
|
|
|
|
|
|
|
() |
|
52
|
0
|
|
|
|
|
|
} |
|
53
|
|
|
|
|
|
|
|
|
54
|
|
|
|
|
|
|
sub _load_backend_module { |
|
55
|
0
|
|
|
0
|
|
|
my ($self, $class, $backend, $required_version) = @_; |
|
56
|
0
|
0
|
|
|
|
|
$backend =~ /^\w+$/ or croak "Bad backend name '$backend' for class '$class'"; |
|
57
|
0
|
|
|
|
|
|
$self->{backend} = $backend; |
|
58
|
0
|
|
|
|
|
|
my $module = $self->{backend_module} = "${class}::Backend::${backend}"; |
|
59
|
|
|
|
|
|
|
|
|
60
|
0
|
|
|
|
|
|
local ($@, $SIG{__DIE__}); |
|
61
|
0
|
|
|
|
|
|
my $ok = eval <
|
|
62
|
|
|
|
|
|
|
no strict; |
|
63
|
|
|
|
|
|
|
no warnings; |
|
64
|
|
|
|
|
|
|
require $module; |
|
65
|
|
|
|
|
|
|
1; |
|
66
|
|
|
|
|
|
|
EOE |
|
67
|
0
|
0
|
|
|
|
|
if ($ok) { |
|
68
|
0
|
0
|
|
|
|
|
if ($required_version) { |
|
69
|
0
|
0
|
|
|
|
|
if ($module->can('_backend_api_version')) { |
|
70
|
0
|
|
|
|
|
|
my $version = $module->_backend_api_version; |
|
71
|
0
|
0
|
|
|
|
|
if ($version >= $required_version) { |
|
72
|
0
|
|
|
|
|
|
return 1; |
|
73
|
|
|
|
|
|
|
} |
|
74
|
|
|
|
|
|
|
else { |
|
75
|
0
|
|
|
|
|
|
$self->_set_error(SSHA_BACKEND_ERROR, |
|
76
|
|
|
|
|
|
|
"backend API version $version is too old ($required_version required)"); |
|
77
|
|
|
|
|
|
|
} |
|
78
|
|
|
|
|
|
|
} |
|
79
|
|
|
|
|
|
|
else { |
|
80
|
0
|
|
|
|
|
|
$self->_set_error(SSHA_BACKEND_ERROR, 'method _backend_api_version missing'); |
|
81
|
|
|
|
|
|
|
} |
|
82
|
|
|
|
|
|
|
} |
|
83
|
|
|
|
|
|
|
else { |
|
84
|
0
|
|
|
|
|
|
return 1; |
|
85
|
|
|
|
|
|
|
} |
|
86
|
|
|
|
|
|
|
} |
|
87
|
|
|
|
|
|
|
else { |
|
88
|
0
|
|
|
|
|
|
$self->_set_error(SSHA_BACKEND_ERROR, "unable to load module '$module'", $@); |
|
89
|
|
|
|
|
|
|
} |
|
90
|
|
|
|
|
|
|
|
|
91
|
0
|
|
|
|
|
|
$self->_log_error_and_reset_backend; |
|
92
|
|
|
|
|
|
|
() |
|
93
|
0
|
|
|
|
|
|
} |
|
94
|
|
|
|
|
|
|
|
|
95
|
0
|
|
|
0
|
0
|
|
sub error { shift->{error} } |
|
96
|
|
|
|
|
|
|
|
|
97
|
|
|
|
|
|
|
sub die_on_error { |
|
98
|
0
|
|
|
0
|
0
|
|
my $self = shift; |
|
99
|
0
|
0
|
|
|
|
|
$self->{error} and croak(join(': ', @_, "$self->{error}")); |
|
100
|
0
|
|
|
|
|
|
1; |
|
101
|
|
|
|
|
|
|
} |
|
102
|
|
|
|
|
|
|
|
|
103
|
|
|
|
|
|
|
sub _set_error { |
|
104
|
0
|
|
|
0
|
|
|
my $self = shift; |
|
105
|
0
|
|
0
|
|
|
|
my $code = shift || 0; |
|
106
|
0
|
0
|
|
|
|
|
my @msg = grep { defined && length } @_; |
|
|
0
|
|
|
|
|
|
|
|
107
|
0
|
0
|
|
|
|
|
@msg = "Unknown error $code" unless @msg; |
|
108
|
|
|
|
|
|
|
my $error = $self->{error} = ( $code |
|
109
|
0
|
0
|
|
|
|
|
? Scalar::Util::dualvar($code, join(': ', @{$self->{error_prefix}}, @msg)) |
|
|
0
|
|
|
|
|
|
|
|
110
|
|
|
|
|
|
|
: 0 ); |
|
111
|
0
|
0
|
0
|
|
|
|
$debug and $debug & 1 and _debug "set_error($code - $error)"; |
|
112
|
0
|
|
|
|
|
|
return $error |
|
113
|
|
|
|
|
|
|
} |
|
114
|
|
|
|
|
|
|
|
|
115
|
|
|
|
|
|
|
sub _or_set_error { |
|
116
|
0
|
|
|
0
|
|
|
my $self = shift; |
|
117
|
0
|
0
|
|
|
|
|
$self->{error} or $self->_set_error(@_); |
|
118
|
|
|
|
|
|
|
} |
|
119
|
|
|
|
|
|
|
|
|
120
|
|
|
|
|
|
|
sub _or_check_error_after_eval { |
|
121
|
0
|
0
|
|
0
|
|
|
if ($@) { |
|
122
|
0
|
|
|
|
|
|
my ($any, $code) = @_; |
|
123
|
0
|
0
|
|
|
|
|
unless ($any->{error}) { |
|
124
|
0
|
|
|
|
|
|
my $err = $@; |
|
125
|
0
|
|
|
|
|
|
$err =~ s/(.*) at .* line \d+.$/$1/; |
|
126
|
0
|
|
|
|
|
|
$any->_set_error($code, $err); |
|
127
|
|
|
|
|
|
|
} |
|
128
|
0
|
|
|
|
|
|
return 0; |
|
129
|
|
|
|
|
|
|
} |
|
130
|
|
|
|
|
|
|
1 |
|
131
|
0
|
|
|
|
|
|
} |
|
132
|
|
|
|
|
|
|
|
|
133
|
|
|
|
|
|
|
sub _open_file { |
|
134
|
0
|
|
|
0
|
|
|
my ($self, $def_mode, $name_or_args) = @_; |
|
135
|
0
|
0
|
|
|
|
|
my ($mode, @args) = (ref $name_or_args |
|
136
|
|
|
|
|
|
|
? @$name_or_args |
|
137
|
|
|
|
|
|
|
: ($def_mode, $name_or_args)); |
|
138
|
0
|
0
|
|
|
|
|
if (open my $fh, $mode, @args) { |
|
139
|
0
|
|
|
|
|
|
return $fh; |
|
140
|
|
|
|
|
|
|
} |
|
141
|
0
|
|
|
|
|
|
$self->_set_error(SSHA_LOCAL_IO_ERROR, "Unable to open file '@args': $!"); |
|
142
|
0
|
|
|
|
|
|
return undef; |
|
143
|
|
|
|
|
|
|
} |
|
144
|
|
|
|
|
|
|
|
|
145
|
|
|
|
|
|
|
my %loaded; |
|
146
|
|
|
|
|
|
|
sub _load_module { |
|
147
|
0
|
|
|
0
|
|
|
my ($self, $module) = @_; |
|
148
|
0
|
0
|
0
|
|
|
|
$loaded{$module} ||= eval "require $module; 1" and return 1; |
|
149
|
0
|
|
|
|
|
|
$self->_set_error(SSHA_UNIMPLEMENTED_ERROR, "Unable to load perl module $module"); |
|
150
|
0
|
|
|
|
|
|
return; |
|
151
|
|
|
|
|
|
|
} |
|
152
|
|
|
|
|
|
|
|
|
153
|
|
|
|
|
|
|
sub _load_os { |
|
154
|
0
|
|
|
0
|
|
|
my $self = shift; |
|
155
|
0
|
0
|
0
|
|
|
|
my $os = $self->{os} //= ($^O =~ /^mswin/i ? 'MSWin' : 'POSIX'); |
|
156
|
0
|
|
|
|
|
|
my $os_module = "Net::SSH::Any::OS::$os"; |
|
157
|
0
|
0
|
|
|
|
|
$self->_load_module($os_module) or return; |
|
158
|
0
|
|
|
|
|
|
$self->{os_module} = $os_module; |
|
159
|
|
|
|
|
|
|
} |
|
160
|
|
|
|
|
|
|
|
|
161
|
|
|
|
|
|
|
sub _find_cmd_by_friend { |
|
162
|
0
|
|
|
0
|
|
|
my ($any, $name, $friend) = @_; |
|
163
|
0
|
0
|
|
|
|
|
if (defined $friend) { |
|
164
|
0
|
|
|
|
|
|
my $up = File::Spec->updir; |
|
165
|
0
|
|
|
|
|
|
my ($drive, $dir) = File::Spec->splitpath($friend); |
|
166
|
0
|
|
|
|
|
|
my $base = File::Spec->catpath($drive, $dir); |
|
167
|
0
|
|
|
|
|
|
for my $path (File::Spec->join($base, $name), |
|
168
|
|
|
|
|
|
|
map File::Spec->join($base, $up, $_, $name), qw(bin sbin libexec) ) { |
|
169
|
0
|
|
|
|
|
|
my $cmd = $any->_os_validate_cmd($path); |
|
170
|
0
|
0
|
|
|
|
|
return $cmd if defined $cmd; |
|
171
|
|
|
|
|
|
|
} |
|
172
|
|
|
|
|
|
|
} |
|
173
|
|
|
|
|
|
|
() |
|
174
|
0
|
|
|
|
|
|
} |
|
175
|
|
|
|
|
|
|
|
|
176
|
|
|
|
|
|
|
sub _find_cmd_in_path { |
|
177
|
0
|
|
|
0
|
|
|
my ($any, $name) = @_; |
|
178
|
0
|
|
|
|
|
|
for my $path (File::Spec->path) { |
|
179
|
0
|
|
|
|
|
|
my $cmd = $any->_os_validate_cmd(File::Spec->join($path, $name)); |
|
180
|
0
|
0
|
|
|
|
|
return $cmd if defined $cmd; |
|
181
|
|
|
|
|
|
|
} |
|
182
|
|
|
|
|
|
|
() |
|
183
|
0
|
|
|
|
|
|
} |
|
184
|
|
|
|
|
|
|
|
|
185
|
|
|
|
|
|
|
sub _find_cmd { |
|
186
|
0
|
|
|
0
|
|
|
my ($any, $name, $friend, $app, $default) = @_; |
|
187
|
0
|
|
|
|
|
|
my $safe_name = $name; |
|
188
|
0
|
|
|
|
|
|
$safe_name =~ s/\W/_/g; |
|
189
|
0
|
|
0
|
|
|
|
return ( $any->{local_cmd}{$safe_name} // |
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
190
|
|
|
|
|
|
|
$any->_find_cmd_by_friend($name, $friend) // |
|
191
|
|
|
|
|
|
|
$any->_find_cmd_in_path($name) // |
|
192
|
|
|
|
|
|
|
$any->_find_helper_cmd($name) // |
|
193
|
|
|
|
|
|
|
$any->_os_find_cmd_by_app($name, $app) // |
|
194
|
|
|
|
|
|
|
$any->_os_validate_cmd($default) // |
|
195
|
|
|
|
|
|
|
$name ); |
|
196
|
|
|
|
|
|
|
} |
|
197
|
|
|
|
|
|
|
|
|
198
|
|
|
|
|
|
|
sub _find_helper_cmd { |
|
199
|
0
|
|
|
0
|
|
|
my ($any, $name) = @_; |
|
200
|
0
|
0
|
0
|
|
|
|
$debug and $debug & 1024 and _debug "looking for helper $name"; |
|
201
|
0
|
|
0
|
|
|
|
my $module = my $last = $any->{backend_module} // return; |
|
202
|
0
|
0
|
|
|
|
|
$last =~ s/.*::// or return; |
|
203
|
0
|
|
|
|
|
|
$module =~ s{::}{/}g; |
|
204
|
0
|
0
|
0
|
|
|
|
$debug and $debug & 1024 and _debug "module as \$INC key is ", $module, ".pm"; |
|
205
|
0
|
|
0
|
|
|
|
my $file_pm = $INC{"$module.pm"} // return; |
|
206
|
0
|
|
|
|
|
|
my ($drive, $dir) = File::Spec->splitpath(File::Spec->rel2abs($file_pm)); |
|
207
|
0
|
|
|
|
|
|
my $path = File::Spec->catpath($drive, $dir, $last, 'Helpers', $name); |
|
208
|
0
|
|
|
|
|
|
$any->_os_validate_cmd($path); |
|
209
|
|
|
|
|
|
|
} |
|
210
|
|
|
|
|
|
|
|
|
211
|
|
|
|
|
|
|
sub _find_local_extra_args { |
|
212
|
0
|
|
|
0
|
|
|
my ($any, $name, $opts, @default) = @_; |
|
213
|
0
|
|
|
|
|
|
my $safe_name = $name; |
|
214
|
0
|
|
|
|
|
|
$safe_name =~ s/\W/_/g; |
|
215
|
|
|
|
|
|
|
my $extra = ( $opts->{"local_${safe_name}_extra_args"} // |
|
216
|
0
|
|
0
|
|
|
|
$any->{local_extra_args}{$safe_name} // |
|
|
|
|
0
|
|
|
|
|
|
217
|
|
|
|
|
|
|
\@default ); |
|
218
|
0
|
|
|
|
|
|
[_array_or_scalar_to_list $extra] |
|
219
|
|
|
|
|
|
|
} |
|
220
|
|
|
|
|
|
|
|
|
221
|
|
|
|
|
|
|
my %posix_shell = map { $_ => 1 } qw(POSIX bash sh ksh ash dash pdksh mksh lksh zsh fizsh posh); |
|
222
|
|
|
|
|
|
|
|
|
223
|
|
|
|
|
|
|
sub _new_quoter { |
|
224
|
0
|
|
|
0
|
|
|
my ($any, $shell) = @_; |
|
225
|
0
|
0
|
|
|
|
|
if ($posix_shell{$shell}) { |
|
226
|
0
|
0
|
|
|
|
|
$any->_load_module('Net::SSH::Any::POSIXShellQuoter') or return; |
|
227
|
0
|
|
|
|
|
|
return 'Net::SSH::Any::POSIXShellQuoter'; |
|
228
|
|
|
|
|
|
|
} |
|
229
|
|
|
|
|
|
|
else { |
|
230
|
0
|
0
|
|
|
|
|
$any->_load_module('Net::OpenSSH::ShellQuoter') or return; |
|
231
|
0
|
|
|
|
|
|
return Net::OpenSSH::ShellQuoter->quoter($shell); |
|
232
|
|
|
|
|
|
|
} |
|
233
|
|
|
|
|
|
|
} |
|
234
|
|
|
|
|
|
|
|
|
235
|
|
|
|
|
|
|
sub _quoter { |
|
236
|
0
|
|
|
0
|
|
|
my ($any, $shell) = @_; |
|
237
|
0
|
0
|
|
|
|
|
defined $shell or croak "shell argument is undef"; |
|
238
|
0
|
|
|
|
|
|
return $any->_new_quoter($shell); |
|
239
|
|
|
|
|
|
|
} |
|
240
|
|
|
|
|
|
|
|
|
241
|
|
|
|
|
|
|
sub _quote_args { |
|
242
|
0
|
|
|
0
|
|
|
my $any = shift; |
|
243
|
0
|
|
|
|
|
|
my $opts = shift; |
|
244
|
0
|
0
|
|
|
|
|
ref $opts eq 'HASH' or die "internal error"; |
|
245
|
0
|
|
|
|
|
|
my $quote = delete $opts->{quote_args}; |
|
246
|
0
|
|
|
|
|
|
my $glob_quoting = delete $opts->{glob_quoting}; |
|
247
|
0
|
|
|
|
|
|
my $argument_encoding = $any->_delete_argument_encoding($opts); |
|
248
|
0
|
0
|
|
|
|
|
$quote = (@_ > 1) unless defined $quote; |
|
249
|
|
|
|
|
|
|
|
|
250
|
0
|
|
|
|
|
|
my @quoted; |
|
251
|
0
|
0
|
|
|
|
|
if ($quote) { |
|
252
|
0
|
|
0
|
|
|
|
my $shell = delete $opts->{remote_shell} // delete $opts->{shell}; |
|
253
|
0
|
0
|
|
|
|
|
my $quoter = $any->_quoter($shell) or return; |
|
254
|
0
|
0
|
|
|
|
|
my $quote_method = ($glob_quoting ? 'quote_glob' : 'quote'); |
|
255
|
|
|
|
|
|
|
|
|
256
|
|
|
|
|
|
|
# foo => $quoter |
|
257
|
|
|
|
|
|
|
# \foo => $quoter_glob |
|
258
|
|
|
|
|
|
|
# \\foo => no quoting at all and disable extended quoting as it is not safe |
|
259
|
0
|
|
|
|
|
|
for (@_) { |
|
260
|
0
|
0
|
|
|
|
|
if (ref $_) { |
|
261
|
0
|
0
|
0
|
|
|
|
if (ref $_ eq 'SCALAR') { |
|
|
|
0
|
|
|
|
|
|
|
262
|
0
|
|
|
|
|
|
push @quoted, $quoter->quote_glob($$_); |
|
263
|
|
|
|
|
|
|
} |
|
264
|
|
|
|
|
|
|
elsif (ref $_ eq 'REF' and ref $$_ eq 'SCALAR') { |
|
265
|
0
|
|
|
|
|
|
push @quoted, $$$_; |
|
266
|
|
|
|
|
|
|
} |
|
267
|
|
|
|
|
|
|
else { |
|
268
|
0
|
|
|
|
|
|
croak "invalid reference in remote command argument list" |
|
269
|
|
|
|
|
|
|
} |
|
270
|
|
|
|
|
|
|
} |
|
271
|
|
|
|
|
|
|
else { |
|
272
|
0
|
|
|
|
|
|
push @quoted, $quoter->$quote_method($_); |
|
273
|
|
|
|
|
|
|
} |
|
274
|
|
|
|
|
|
|
} |
|
275
|
|
|
|
|
|
|
} |
|
276
|
|
|
|
|
|
|
else { |
|
277
|
0
|
0
|
|
|
|
|
croak "reference found in argument list when argument quoting is disabled" if (grep ref, @_); |
|
278
|
0
|
|
|
|
|
|
@quoted = @_; |
|
279
|
|
|
|
|
|
|
} |
|
280
|
0
|
|
|
|
|
|
$any->_encode_args($argument_encoding, @quoted); |
|
281
|
0
|
0
|
0
|
|
|
|
$debug and $debug & 1024 and _debug("command+args: @quoted"); |
|
282
|
0
|
0
|
|
|
|
|
wantarray ? @quoted : join(" ", @quoted); |
|
283
|
|
|
|
|
|
|
} |
|
284
|
|
|
|
|
|
|
|
|
285
|
|
|
|
|
|
|
sub _delete_argument_encoding { |
|
286
|
0
|
|
|
0
|
|
|
my ($any, $opts) = @_; |
|
287
|
|
|
|
|
|
|
_first_defined(delete $opts->{argument_encoding}, |
|
288
|
|
|
|
|
|
|
delete $opts->{encoding}, |
|
289
|
|
|
|
|
|
|
$any->{argument_encoding}) |
|
290
|
0
|
|
|
|
|
|
} |
|
291
|
|
|
|
|
|
|
|
|
292
|
|
|
|
|
|
|
sub _delete_stream_encoding { |
|
293
|
0
|
|
|
0
|
|
|
my ($any, $opts) = @_; |
|
294
|
|
|
|
|
|
|
_first_defined(delete $opts->{stream_encoding}, |
|
295
|
|
|
|
|
|
|
$opts->{encoding}, |
|
296
|
|
|
|
|
|
|
$any->{stream_encoding}) |
|
297
|
0
|
|
|
|
|
|
} |
|
298
|
|
|
|
|
|
|
|
|
299
|
|
|
|
|
|
|
sub _find_encoding { |
|
300
|
0
|
|
|
0
|
|
|
my ($any, $encoding, $data) = @_; |
|
301
|
0
|
0
|
|
|
|
|
my $enc = Encode::find_encoding($encoding) |
|
302
|
|
|
|
|
|
|
or $any->_or_set_error(SSHA_ENCODING_ERROR, "bad encoding '$encoding'"); |
|
303
|
0
|
|
|
|
|
|
return $enc |
|
304
|
|
|
|
|
|
|
} |
|
305
|
|
|
|
|
|
|
|
|
306
|
|
|
|
|
|
|
sub _encode_data { |
|
307
|
0
|
|
|
0
|
|
|
my $any = shift; |
|
308
|
0
|
|
|
|
|
|
my $encoding = shift; |
|
309
|
0
|
0
|
|
|
|
|
if (@_) { |
|
310
|
0
|
0
|
|
|
|
|
my $enc = $any->_find_encoding($encoding) or return; |
|
311
|
0
|
|
|
|
|
|
local $any->{error_prefix} = [@{$any->{error_prefix}}, "data encoding failed"]; |
|
|
0
|
|
|
|
|
|
|
|
312
|
0
|
|
|
|
|
|
local ($@, $SIG{__DIE__}); |
|
313
|
0
|
|
0
|
|
|
|
eval { defined and $_ = $enc->encode($_, Encode::FB_CROAK()) for @_ }; |
|
|
0
|
|
|
|
|
|
|
|
314
|
0
|
0
|
|
|
|
|
$any->_or_check_error_after_eval(SSHA_ENCODING_ERROR) or return; |
|
315
|
|
|
|
|
|
|
} |
|
316
|
|
|
|
|
|
|
1 |
|
317
|
0
|
|
|
|
|
|
} |
|
318
|
|
|
|
|
|
|
|
|
319
|
|
|
|
|
|
|
sub _decode_data { |
|
320
|
0
|
|
|
0
|
|
|
my $any = shift; |
|
321
|
0
|
|
|
|
|
|
my $encoding = shift; |
|
322
|
0
|
0
|
|
|
|
|
my $enc = $any->_find_encoding($encoding) or return; |
|
323
|
0
|
0
|
|
|
|
|
if (@_) { |
|
324
|
0
|
|
|
|
|
|
local ($@, $SIG{__DIE__}); |
|
325
|
0
|
|
0
|
|
|
|
eval { defined and $_ = $enc->decode($_, Encode::FB_CROAK()) for @_ }; |
|
|
0
|
|
|
|
|
|
|
|
326
|
0
|
0
|
|
|
|
|
$any->_or_check_error_after_eval(SSHA_ENCODING_ERROR) or return; |
|
327
|
|
|
|
|
|
|
} |
|
328
|
0
|
|
|
|
|
|
1; |
|
329
|
|
|
|
|
|
|
} |
|
330
|
|
|
|
|
|
|
|
|
331
|
|
|
|
|
|
|
sub _encode_args { |
|
332
|
0
|
0
|
|
0
|
|
|
if (@_ > 2) { |
|
333
|
0
|
|
|
|
|
|
my $any = shift; |
|
334
|
0
|
|
|
|
|
|
my $encoding = shift; |
|
335
|
0
|
|
|
|
|
|
local $any->{error_prefix} = [@{$any->{error_prefix}}, "argument encoding failed"]; |
|
|
0
|
|
|
|
|
|
|
|
336
|
0
|
0
|
|
|
|
|
if (my $enc = $any->_find_encoding($encoding)) { |
|
337
|
0
|
|
|
|
|
|
$any->_encode_data($enc, @_); |
|
338
|
|
|
|
|
|
|
} |
|
339
|
0
|
|
|
|
|
|
return !$any->{_error}; |
|
340
|
|
|
|
|
|
|
} |
|
341
|
0
|
|
|
|
|
|
1; |
|
342
|
|
|
|
|
|
|
} |
|
343
|
|
|
|
|
|
|
|
|
344
|
|
|
|
|
|
|
# transparently delegate method calls to backend and os packages: |
|
345
|
|
|
|
|
|
|
sub AUTOLOAD { |
|
346
|
0
|
|
|
0
|
|
|
our $AUTOLOAD; |
|
347
|
0
|
|
|
|
|
|
my ($name) = $AUTOLOAD =~ /([^:]*)$/; |
|
348
|
0
|
|
|
|
|
|
my $sub; |
|
349
|
1
|
|
|
1
|
|
5
|
no strict 'refs'; |
|
|
1
|
|
|
|
|
1
|
|
|
|
1
|
|
|
|
|
208
|
|
|
350
|
0
|
0
|
|
|
|
|
if (my ($os_name) = $name =~ /^_os_(.*)/) { |
|
351
|
|
|
|
|
|
|
$sub = sub { |
|
352
|
0
|
0
|
0
|
0
|
|
|
my $os = $_[0]->{os_module} //= $_[0]->_load_os or return; |
|
353
|
0
|
0
|
|
|
|
|
my $method = $os->can($os_name) |
|
354
|
|
|
|
|
|
|
or croak "method '$os_name' not defined in OS '$os'"; |
|
355
|
0
|
|
|
|
|
|
goto &$method; |
|
356
|
0
|
|
|
|
|
|
}; |
|
357
|
|
|
|
|
|
|
} |
|
358
|
|
|
|
|
|
|
else { |
|
359
|
|
|
|
|
|
|
$sub = sub { |
|
360
|
0
|
0
|
|
0
|
|
|
my $module = $_[0]->{backend_module} or return; |
|
361
|
0
|
0
|
|
|
|
|
my $method = $module->can($name) |
|
362
|
|
|
|
|
|
|
or croak "method '$name' not defined in backend '$module'"; |
|
363
|
0
|
|
|
|
|
|
goto &$method; |
|
364
|
0
|
|
|
|
|
|
}; |
|
365
|
|
|
|
|
|
|
} |
|
366
|
0
|
|
|
|
|
|
*{$AUTOLOAD} = $sub; |
|
|
0
|
|
|
|
|
|
|
|
367
|
0
|
|
|
|
|
|
goto &$sub; |
|
368
|
|
|
|
|
|
|
} |
|
369
|
|
|
|
|
|
|
|
|
370
|
|
|
|
|
|
|
sub DESTROY { |
|
371
|
0
|
|
|
0
|
|
|
my $self = shift; |
|
372
|
0
|
|
|
|
|
|
my $module = $self->{backend_module}; |
|
373
|
0
|
0
|
|
|
|
|
if (defined $module) { |
|
374
|
0
|
|
|
|
|
|
my $sub = $module->can('DESTROY'); |
|
375
|
0
|
0
|
|
|
|
|
$sub->($self) if $sub; |
|
376
|
|
|
|
|
|
|
} |
|
377
|
|
|
|
|
|
|
} |
|
378
|
|
|
|
|
|
|
|
|
379
|
|
|
|
|
|
|
1; |