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