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
|
|
3
|
use warnings; |
|
1
|
|
|
|
|
1
|
|
|
1
|
|
|
|
|
15
|
|
5
|
1
|
|
|
1
|
|
2
|
use Carp; |
|
1
|
|
|
|
|
1
|
|
|
1
|
|
|
|
|
36
|
|
6
|
|
|
|
|
|
|
|
7
|
1
|
|
|
1
|
|
3
|
use File::Spec; |
|
1
|
|
|
|
|
0
|
|
|
1
|
|
|
|
|
16
|
|
8
|
1
|
|
|
1
|
|
3
|
use Scalar::Util (); |
|
1
|
|
|
|
|
0
|
|
|
1
|
|
|
|
|
12
|
|
9
|
1
|
|
|
1
|
|
2
|
use Encode (); |
|
1
|
|
|
|
|
1
|
|
|
1
|
|
|
|
|
19
|
|
10
|
|
|
|
|
|
|
|
11
|
1
|
|
|
1
|
|
3
|
use Net::SSH::Any::Constants qw(SSHA_BACKEND_ERROR SSHA_LOCAL_IO_ERROR SSHA_UNIMPLEMENTED_ERROR SSHA_ENCODING_ERROR); |
|
1
|
|
|
|
|
1
|
|
|
1
|
|
|
|
|
43
|
|
12
|
1
|
|
|
1
|
|
3
|
use Net::SSH::Any::Util; |
|
1
|
|
|
|
|
1
|
|
|
1
|
|
|
|
|
2034
|
|
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
|
|
|
|
|
217
|
|
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; |