| line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
|
1
|
|
|
|
|
|
|
package App::cpanel; |
|
2
|
|
|
|
|
|
|
|
|
3
|
1
|
|
|
1
|
|
68944
|
use Exporter 'import'; |
|
|
1
|
|
|
|
|
8
|
|
|
|
1
|
|
|
|
|
71
|
|
|
4
|
|
|
|
|
|
|
|
|
5
|
|
|
|
|
|
|
our $VERSION = '0.005'; |
|
6
|
|
|
|
|
|
|
our @EXPORT_OK = qw(dispatch_cmd_print dispatch_cmd_raw_p dir_walk_p); |
|
7
|
|
|
|
|
|
|
|
|
8
|
|
|
|
|
|
|
=head1 NAME |
|
9
|
|
|
|
|
|
|
|
|
10
|
|
|
|
|
|
|
App::cpanel - CLI for cPanel UAPI and API 2 |
|
11
|
|
|
|
|
|
|
|
|
12
|
|
|
|
|
|
|
=begin markdown |
|
13
|
|
|
|
|
|
|
|
|
14
|
|
|
|
|
|
|
# PROJECT STATUS |
|
15
|
|
|
|
|
|
|
|
|
16
|
|
|
|
|
|
|
[](https://metacpan.org/pod/App::cpanel) |
|
17
|
|
|
|
|
|
|
|
|
18
|
|
|
|
|
|
|
=end markdown |
|
19
|
|
|
|
|
|
|
|
|
20
|
|
|
|
|
|
|
=head1 SYNOPSIS |
|
21
|
|
|
|
|
|
|
|
|
22
|
|
|
|
|
|
|
$ cpanel uapi Notifications get_notifications_count |
|
23
|
|
|
|
|
|
|
$ cpanel uapi ResourceUsage get_usages |
|
24
|
|
|
|
|
|
|
$ cpanel uapi Fileman list_files dir=public_html |
|
25
|
|
|
|
|
|
|
$ cpanel uapi Fileman get_file_content dir=public_html file=index.html |
|
26
|
|
|
|
|
|
|
$ cpanel download public_html/index.html |
|
27
|
|
|
|
|
|
|
$ cpanel api2 Fileman fileop op=chmod metadata=0755 sourcefiles=public_html/cgi-bin/hello-world |
|
28
|
|
|
|
|
|
|
$ cpanel api2 Fileman fileop op=unlink sourcefiles=public_html/cgi-bin/hello-world |
|
29
|
|
|
|
|
|
|
$ cpanel api2 Fileman mkdir path= name=new-dir-at-top |
|
30
|
|
|
|
|
|
|
|
|
31
|
|
|
|
|
|
|
# this one is one at a time but can overwrite files |
|
32
|
|
|
|
|
|
|
$ cpanel api2 Fileman savefile dir=public_html/cgi-bin filename=hello-world content="$(cat public_html/cgi-bin/hello-world)" |
|
33
|
|
|
|
|
|
|
# this is multiple files but refuses to overwrite |
|
34
|
|
|
|
|
|
|
$ cpanel upload public_html/cgi-bin hello-world |
|
35
|
|
|
|
|
|
|
|
|
36
|
|
|
|
|
|
|
# download |
|
37
|
|
|
|
|
|
|
$ cpanel mirror public_html public_html cpanel localfs |
|
38
|
|
|
|
|
|
|
# upload |
|
39
|
|
|
|
|
|
|
$ cpanel mirror public_html public_html localfs cpanel |
|
40
|
|
|
|
|
|
|
|
|
41
|
|
|
|
|
|
|
=head1 DESCRIPTION |
|
42
|
|
|
|
|
|
|
|
|
43
|
|
|
|
|
|
|
CLI for cPanel UAPI and also API 2, due to missing functionality in UAPI. |
|
44
|
|
|
|
|
|
|
|
|
45
|
|
|
|
|
|
|
Stores session token in F<~/.cpanel-token>, a two-line file. First line |
|
46
|
|
|
|
|
|
|
is the URL component that goes after C. Second is the C |
|
47
|
|
|
|
|
|
|
cookie, which you can get from your browser's DevTools. |
|
48
|
|
|
|
|
|
|
|
|
49
|
|
|
|
|
|
|
Stores relevant domain name in F<~/.cpanel-domain>. |
|
50
|
|
|
|
|
|
|
|
|
51
|
|
|
|
|
|
|
=head1 FUNCTIONS |
|
52
|
|
|
|
|
|
|
|
|
53
|
|
|
|
|
|
|
Exportable: |
|
54
|
|
|
|
|
|
|
|
|
55
|
|
|
|
|
|
|
=head2 dispatch_cmd_print |
|
56
|
|
|
|
|
|
|
|
|
57
|
|
|
|
|
|
|
Will print the return value, using L except for |
|
58
|
|
|
|
|
|
|
C. |
|
59
|
|
|
|
|
|
|
|
|
60
|
|
|
|
|
|
|
=head2 dispatch_cmd_raw_p |
|
61
|
|
|
|
|
|
|
|
|
62
|
|
|
|
|
|
|
Returns a promise of the decoded JSON value or Ced content. |
|
63
|
|
|
|
|
|
|
|
|
64
|
|
|
|
|
|
|
=head2 dir_walk_p |
|
65
|
|
|
|
|
|
|
|
|
66
|
|
|
|
|
|
|
Takes C<$from_dir>, C<$to_dir>, C<$from_map>, C<$to_map>. Copies the |
|
67
|
|
|
|
|
|
|
information in the first directory to the second, using the respective |
|
68
|
|
|
|
|
|
|
maps. Assumes UNIX-like semantics in filenames, i.e. C<$dir/$file>. |
|
69
|
|
|
|
|
|
|
|
|
70
|
|
|
|
|
|
|
Returns a promise of completion. |
|
71
|
|
|
|
|
|
|
|
|
72
|
|
|
|
|
|
|
The maps are hash-refs whose values are functions, and the keys are: |
|
73
|
|
|
|
|
|
|
|
|
74
|
|
|
|
|
|
|
=head3 ls |
|
75
|
|
|
|
|
|
|
|
|
76
|
|
|
|
|
|
|
Takes C<$dir>. Returns a promise of two hash-refs, of directories and of |
|
77
|
|
|
|
|
|
|
files. Each has keys of relative filename, values are an array-ref |
|
78
|
|
|
|
|
|
|
containing a string octal number representing UNIX permissions, and a |
|
79
|
|
|
|
|
|
|
number giving the C. Must reject if does not exist. |
|
80
|
|
|
|
|
|
|
|
|
81
|
|
|
|
|
|
|
=head3 mkdir |
|
82
|
|
|
|
|
|
|
|
|
83
|
|
|
|
|
|
|
Takes C<$dir>. Returns a promise of having created the directory. |
|
84
|
|
|
|
|
|
|
|
|
85
|
|
|
|
|
|
|
=head3 read |
|
86
|
|
|
|
|
|
|
|
|
87
|
|
|
|
|
|
|
Takes C<$dir>, C<$file>. Returns a promise of the file contents. |
|
88
|
|
|
|
|
|
|
|
|
89
|
|
|
|
|
|
|
=head3 write |
|
90
|
|
|
|
|
|
|
|
|
91
|
|
|
|
|
|
|
Takes C<$dir>, C<$file>. Returns a promise of having written the file |
|
92
|
|
|
|
|
|
|
contents. |
|
93
|
|
|
|
|
|
|
|
|
94
|
|
|
|
|
|
|
=head3 chmod |
|
95
|
|
|
|
|
|
|
|
|
96
|
|
|
|
|
|
|
Takes C<$path>, C<$perms>. Returns a promise of having changed the |
|
97
|
|
|
|
|
|
|
permissions. |
|
98
|
|
|
|
|
|
|
|
|
99
|
|
|
|
|
|
|
=head1 SEE ALSO |
|
100
|
|
|
|
|
|
|
|
|
101
|
|
|
|
|
|
|
L |
|
102
|
|
|
|
|
|
|
|
|
103
|
|
|
|
|
|
|
L |
|
104
|
|
|
|
|
|
|
|
|
105
|
|
|
|
|
|
|
=head1 AUTHOR |
|
106
|
|
|
|
|
|
|
|
|
107
|
|
|
|
|
|
|
Ed J |
|
108
|
|
|
|
|
|
|
|
|
109
|
|
|
|
|
|
|
=head1 COPYRIGHT AND LICENSE |
|
110
|
|
|
|
|
|
|
|
|
111
|
|
|
|
|
|
|
This is free software; you can redistribute it and/or modify it under |
|
112
|
|
|
|
|
|
|
the same terms as the Perl 5 programming language system itself. |
|
113
|
|
|
|
|
|
|
|
|
114
|
|
|
|
|
|
|
=cut |
|
115
|
|
|
|
|
|
|
|
|
116
|
1
|
|
|
1
|
|
5
|
use strict; |
|
|
1
|
|
|
|
|
2
|
|
|
|
1
|
|
|
|
|
19
|
|
|
117
|
1
|
|
|
1
|
|
5
|
use warnings; |
|
|
1
|
|
|
|
|
1
|
|
|
|
1
|
|
|
|
|
36
|
|
|
118
|
1
|
|
|
1
|
|
618
|
use Mojo::URL; |
|
|
1
|
|
|
|
|
203344
|
|
|
|
1
|
|
|
|
|
9
|
|
|
119
|
1
|
|
|
1
|
|
606
|
use Mojo::UserAgent; |
|
|
1
|
|
|
|
|
264774
|
|
|
|
1
|
|
|
|
|
10
|
|
|
120
|
1
|
|
|
1
|
|
50
|
use Mojo::File qw(path); |
|
|
1
|
|
|
|
|
2
|
|
|
|
1
|
|
|
|
|
53
|
|
|
121
|
1
|
|
|
1
|
|
8
|
use Mojo::Util qw(dumper encode); |
|
|
1
|
|
|
|
|
2
|
|
|
|
1
|
|
|
|
|
3284
|
|
|
122
|
|
|
|
|
|
|
|
|
123
|
|
|
|
|
|
|
my %cmd2func = ( |
|
124
|
|
|
|
|
|
|
uapi => [ \&uapi_p, 1 ], |
|
125
|
|
|
|
|
|
|
download => [ \&download_p, 0 ], |
|
126
|
|
|
|
|
|
|
upload => [ \&upload_p, 1 ], |
|
127
|
|
|
|
|
|
|
api2 => [ \&api2_p, 1 ], |
|
128
|
|
|
|
|
|
|
mirror => [ \&mirror_p, 1 ], |
|
129
|
|
|
|
|
|
|
); |
|
130
|
|
|
|
|
|
|
my $token_file = "$ENV{HOME}/.cpanel-token"; |
|
131
|
|
|
|
|
|
|
my $domain_file = "$ENV{HOME}/.cpanel-domain"; |
|
132
|
|
|
|
|
|
|
my %localfs_map = ( |
|
133
|
|
|
|
|
|
|
ls => \&localfs_ls, |
|
134
|
|
|
|
|
|
|
mkdir => \&localfs_mkdir, |
|
135
|
|
|
|
|
|
|
read => \&localfs_read, |
|
136
|
|
|
|
|
|
|
write => \&localfs_write, |
|
137
|
|
|
|
|
|
|
chmod => \&localfs_chmod, |
|
138
|
|
|
|
|
|
|
); |
|
139
|
|
|
|
|
|
|
my %cpanel_map = ( |
|
140
|
|
|
|
|
|
|
ls => \&cpanel_ls, |
|
141
|
|
|
|
|
|
|
mkdir => \&cpanel_mkdir, |
|
142
|
|
|
|
|
|
|
read => \&cpanel_read, |
|
143
|
|
|
|
|
|
|
write => \&cpanel_write, |
|
144
|
|
|
|
|
|
|
chmod => \&cpanel_chmod, |
|
145
|
|
|
|
|
|
|
); |
|
146
|
|
|
|
|
|
|
our %MAP2HASH = ( |
|
147
|
|
|
|
|
|
|
localfs => \%localfs_map, |
|
148
|
|
|
|
|
|
|
cpanel => \%cpanel_map, |
|
149
|
|
|
|
|
|
|
); |
|
150
|
|
|
|
|
|
|
|
|
151
|
|
|
|
|
|
|
sub dispatch_cmd_print { |
|
152
|
0
|
|
|
0
|
1
|
0
|
my $cmd = shift; |
|
153
|
0
|
0
|
|
|
|
0
|
die "No command\n" unless $cmd; |
|
154
|
0
|
0
|
|
|
|
0
|
die "Unknown command '$cmd'\n" unless my $info = $cmd2func{$cmd}; |
|
155
|
0
|
|
|
|
|
0
|
my $p = dispatch_cmd_raw_p($cmd, @_); |
|
156
|
0
|
0
|
|
|
|
0
|
$p = $p->then(\&dumper) if $info->[1]; |
|
157
|
0
|
|
|
0
|
|
0
|
$p->then(sub { print @_ }, sub { warn encode 'UTF-8', join '', @_ })->wait; |
|
|
0
|
|
|
|
|
0
|
|
|
|
0
|
|
|
|
|
0
|
|
|
158
|
|
|
|
|
|
|
} |
|
159
|
|
|
|
|
|
|
|
|
160
|
|
|
|
|
|
|
sub dispatch_cmd_raw_p { |
|
161
|
0
|
|
|
0
|
1
|
0
|
my $cmd = shift; |
|
162
|
0
|
0
|
|
|
|
0
|
die "No command\n" unless $cmd; |
|
163
|
0
|
0
|
|
|
|
0
|
die "Unknown command '$cmd'\n" unless my $info = $cmd2func{$cmd}; |
|
164
|
0
|
|
|
|
|
0
|
goto &{$info->[0]}; |
|
|
0
|
|
|
|
|
0
|
|
|
165
|
|
|
|
|
|
|
} |
|
166
|
|
|
|
|
|
|
|
|
167
|
|
|
|
|
|
|
sub api_request { |
|
168
|
0
|
|
|
0
|
0
|
0
|
my ($method, $domain, $token, $parts, $args, @extra_args) = @_; |
|
169
|
0
|
|
|
|
|
0
|
my ($url_token, $cookie_token) = split /\s+/, $token; |
|
170
|
0
|
|
|
|
|
0
|
my $url = Mojo::URL->new("https://$domain:2083"); |
|
171
|
0
|
|
|
|
|
0
|
$url->path(join '/', '', "cpsess$url_token", @$parts); |
|
172
|
0
|
0
|
|
|
|
0
|
$url->query(%$args) if $args; |
|
173
|
0
|
|
|
|
|
0
|
CORE::state $ua = Mojo::UserAgent->new; # state as needs to live long enough to complete request |
|
174
|
0
|
|
|
|
|
0
|
$ua->$method( |
|
175
|
|
|
|
|
|
|
$url->to_abs . "", |
|
176
|
|
|
|
|
|
|
{ Cookie => "cpsession=$cookie_token" }, |
|
177
|
|
|
|
|
|
|
@extra_args, |
|
178
|
|
|
|
|
|
|
); |
|
179
|
|
|
|
|
|
|
} |
|
180
|
|
|
|
|
|
|
|
|
181
|
|
|
|
|
|
|
sub read_file { |
|
182
|
0
|
|
|
0
|
0
|
0
|
my ($file) = @_; |
|
183
|
0
|
0
|
|
|
|
0
|
die "$file: $!\n" unless -f $file; |
|
184
|
0
|
0
|
|
|
|
0
|
die "$file: too readable\n" if (stat $file)[2] & 0044; |
|
185
|
0
|
|
|
|
|
0
|
local $/; |
|
186
|
0
|
0
|
|
|
|
0
|
open my $fh, $file or die "$file: $!\n"; |
|
187
|
0
|
|
|
|
|
0
|
my $content = <$fh>; |
|
188
|
0
|
0
|
|
|
|
0
|
die "No content in '$file'\n" unless $content; |
|
189
|
0
|
|
|
|
|
0
|
$content =~ s/^\s*(.*?)\s*$/$1/g; |
|
190
|
0
|
|
|
|
|
0
|
$content; |
|
191
|
|
|
|
|
|
|
} |
|
192
|
|
|
|
|
|
|
|
|
193
|
0
|
|
|
0
|
0
|
0
|
sub read_token { read_file($token_file) } |
|
194
|
0
|
|
|
0
|
0
|
0
|
sub read_domain { read_file($domain_file) } |
|
195
|
|
|
|
|
|
|
|
|
196
|
|
|
|
|
|
|
sub _error_or_json { |
|
197
|
0
|
|
|
0
|
|
0
|
my $res = $_[0]->res; |
|
198
|
0
|
0
|
|
|
|
0
|
die $res->code . ": " . $res->message . "\n" if $res->code != 200; |
|
199
|
0
|
|
|
|
|
0
|
$res->json; |
|
200
|
|
|
|
|
|
|
} |
|
201
|
|
|
|
|
|
|
|
|
202
|
|
|
|
|
|
|
sub _uapi_error_or_json { |
|
203
|
0
|
|
|
0
|
|
0
|
my $json = $_[0]; |
|
204
|
0
|
0
|
|
|
|
0
|
if (!$json->{status}) { |
|
205
|
|
|
|
|
|
|
die join '', "Failed:\n", |
|
206
|
0
|
0
|
|
|
|
0
|
map "$_\n", map @{ $json->{$_} || [] }, qw(errors warnings); |
|
|
0
|
|
|
|
|
0
|
|
|
207
|
|
|
|
|
|
|
} |
|
208
|
0
|
|
|
|
|
0
|
$json; |
|
209
|
|
|
|
|
|
|
} |
|
210
|
|
|
|
|
|
|
|
|
211
|
|
|
|
|
|
|
sub uapi_p { |
|
212
|
0
|
|
|
0
|
0
|
0
|
my ($module, $function, @args) = @_; |
|
213
|
0
|
0
|
|
|
|
0
|
die "No module\n" unless $module; |
|
214
|
0
|
0
|
|
|
|
0
|
die "No function\n" unless $function; |
|
215
|
0
|
|
|
|
|
0
|
my ($token, $domain) = (read_token(), read_domain()); |
|
216
|
0
|
0
|
|
|
|
0
|
my $args_hash = ref($args[0]) eq 'HASH' |
|
|
|
0
|
|
|
|
|
|
|
217
|
|
|
|
|
|
|
? $args[0] |
|
218
|
|
|
|
|
|
|
: { map split('=', $_, 2), @args } |
|
219
|
|
|
|
|
|
|
if @args; |
|
220
|
0
|
|
|
|
|
0
|
my $tx_p = api_request 'get_p', $domain, $token, |
|
221
|
|
|
|
|
|
|
[ 'execute', $module, $function ], |
|
222
|
|
|
|
|
|
|
$args_hash; |
|
223
|
0
|
|
|
|
|
0
|
$tx_p->then(\&_error_or_json)->then(\&_uapi_error_or_json); |
|
224
|
|
|
|
|
|
|
} |
|
225
|
|
|
|
|
|
|
|
|
226
|
|
|
|
|
|
|
sub download_p { |
|
227
|
0
|
|
|
0
|
0
|
0
|
my ($file) = @_; |
|
228
|
0
|
0
|
|
|
|
0
|
die "No file\n" unless $file; |
|
229
|
0
|
|
|
|
|
0
|
my ($token, $domain) = (read_token(), read_domain()); |
|
230
|
0
|
|
|
|
|
0
|
my $tx_p = api_request 'get_p', $domain, $token, |
|
231
|
|
|
|
|
|
|
[ 'download' ], |
|
232
|
|
|
|
|
|
|
{ skipencode => 1, file => $file }; |
|
233
|
|
|
|
|
|
|
$tx_p->then(sub { |
|
234
|
0
|
|
|
0
|
|
0
|
my $res = $_[0]->res; |
|
235
|
0
|
0
|
|
|
|
0
|
die $res->code . ": " . $res->message . "\n" if $res->code != 200; |
|
236
|
0
|
|
|
|
|
0
|
$res->body; |
|
237
|
0
|
|
|
|
|
0
|
}); |
|
238
|
|
|
|
|
|
|
} |
|
239
|
|
|
|
|
|
|
|
|
240
|
|
|
|
|
|
|
sub make_upload_form { |
|
241
|
0
|
|
|
0
|
0
|
0
|
my $dir = shift; |
|
242
|
0
|
|
|
|
|
0
|
my $counter = 0; |
|
243
|
|
|
|
|
|
|
+{ |
|
244
|
|
|
|
|
|
|
dir => $dir, |
|
245
|
|
|
|
|
|
|
map { |
|
246
|
0
|
|
|
|
|
0
|
my $p = path $_; |
|
|
0
|
|
|
|
|
0
|
|
|
247
|
0
|
|
|
|
|
0
|
('file-' . ++$counter => { |
|
248
|
|
|
|
|
|
|
filename => $p->basename, |
|
249
|
|
|
|
|
|
|
content => $p->slurp, |
|
250
|
|
|
|
|
|
|
}); |
|
251
|
|
|
|
|
|
|
} @_, |
|
252
|
|
|
|
|
|
|
}; |
|
253
|
|
|
|
|
|
|
} |
|
254
|
|
|
|
|
|
|
|
|
255
|
|
|
|
|
|
|
sub upload_p { |
|
256
|
0
|
|
|
0
|
0
|
0
|
my ($dir, @files) = @_; |
|
257
|
0
|
0
|
|
|
|
0
|
die "No dir\n" unless $dir; |
|
258
|
0
|
0
|
|
|
|
0
|
die "No files\n" unless @files; |
|
259
|
0
|
|
|
|
|
0
|
my ($token, $domain) = (read_token(), read_domain()); |
|
260
|
0
|
|
|
|
|
0
|
my $tx_p = api_request 'post_p', $domain, $token, |
|
261
|
|
|
|
|
|
|
[ 'execute', 'Fileman', 'upload_files' ], |
|
262
|
|
|
|
|
|
|
undef, |
|
263
|
|
|
|
|
|
|
form => make_upload_form($dir, @files), |
|
264
|
|
|
|
|
|
|
; |
|
265
|
0
|
|
|
|
|
0
|
$tx_p->then(\&_error_or_json)->then(\&_uapi_error_or_json); |
|
266
|
|
|
|
|
|
|
} |
|
267
|
|
|
|
|
|
|
|
|
268
|
|
|
|
|
|
|
sub _api2_error_or_json { |
|
269
|
0
|
|
|
0
|
|
0
|
my $json = $_[0]; |
|
270
|
0
|
|
|
|
|
0
|
my $result = $json->{cpanelresult}; |
|
271
|
0
|
0
|
0
|
|
|
0
|
if (!$result or !$result->{event}{result} or $result->{error}) { |
|
|
|
|
0
|
|
|
|
|
|
272
|
|
|
|
|
|
|
die join '', "Failed:\n", |
|
273
|
|
|
|
|
|
|
map "$_\n", |
|
274
|
|
|
|
|
|
|
($result->{error} ? $result->{error} : ()), |
|
275
|
0
|
0
|
|
|
|
0
|
(map "$_->{src}: $_->{err}", grep !$_->{result}, @{$result->{data} || []}), |
|
|
0
|
0
|
|
|
|
0
|
|
|
276
|
|
|
|
|
|
|
; |
|
277
|
|
|
|
|
|
|
} |
|
278
|
0
|
|
|
|
|
0
|
$json; |
|
279
|
|
|
|
|
|
|
} |
|
280
|
|
|
|
|
|
|
|
|
281
|
|
|
|
|
|
|
sub api2_p { |
|
282
|
0
|
|
|
0
|
0
|
0
|
my ($module, $function, @args) = @_; |
|
283
|
0
|
0
|
|
|
|
0
|
die "No module\n" unless $module; |
|
284
|
0
|
0
|
|
|
|
0
|
die "No function\n" unless $function; |
|
285
|
0
|
|
|
|
|
0
|
my ($token, $domain) = (read_token(), read_domain()); |
|
286
|
0
|
0
|
|
|
|
0
|
my $args_hash = ref($args[0]) eq 'HASH' |
|
|
|
0
|
|
|
|
|
|
|
287
|
|
|
|
|
|
|
? $args[0] |
|
288
|
|
|
|
|
|
|
: { map split('=', $_, 2), @args } |
|
289
|
|
|
|
|
|
|
if @args; |
|
290
|
|
|
|
|
|
|
my $tx_p = api_request 'post_p', $domain, $token, |
|
291
|
|
|
|
|
|
|
[ qw(json-api cpanel) ], |
|
292
|
|
|
|
|
|
|
{ |
|
293
|
|
|
|
|
|
|
cpanel_jsonapi_module => $module, |
|
294
|
|
|
|
|
|
|
cpanel_jsonapi_func => $function, |
|
295
|
|
|
|
|
|
|
cpanel_jsonapi_apiversion => 2, |
|
296
|
0
|
0
|
|
|
|
0
|
%{ $args_hash || {} }, |
|
|
0
|
|
|
|
|
0
|
|
|
297
|
|
|
|
|
|
|
}; |
|
298
|
0
|
|
|
|
|
0
|
$tx_p->then(\&_error_or_json)->then(\&_api2_error_or_json); |
|
299
|
|
|
|
|
|
|
} |
|
300
|
|
|
|
|
|
|
|
|
301
|
|
|
|
|
|
|
sub dir_walk_p { |
|
302
|
2
|
|
|
2
|
1
|
149
|
my ($from_dir, $to_dir, $from_map, $to_map, $to_dir_created) = @_; |
|
303
|
2
|
|
|
|
|
3
|
my $to_dir_create_p; |
|
304
|
2
|
100
|
|
|
|
6
|
if ($to_dir_created) { |
|
305
|
1
|
|
|
|
|
4
|
$to_dir_create_p = Mojo::Promise->resolve(1); |
|
306
|
|
|
|
|
|
|
} else { |
|
307
|
1
|
|
|
|
|
2
|
my $from_dir_perms; |
|
308
|
|
|
|
|
|
|
$to_dir_create_p = $to_map->{ls}->($to_dir)->catch(sub { |
|
309
|
|
|
|
|
|
|
# only create if ls fails |
|
310
|
1
|
|
|
1
|
|
439
|
$to_map->{mkdir}->($to_dir) |
|
311
|
|
|
|
|
|
|
})->then(sub { |
|
312
|
1
|
|
|
1
|
|
419
|
$from_map->{ls}->(path($from_dir)->dirname) |
|
313
|
|
|
|
|
|
|
})->then(sub { |
|
314
|
1
|
|
|
1
|
|
535
|
my ($dirs, $files) = @_; |
|
315
|
1
|
|
50
|
|
|
5
|
$from_dir_perms = $dirs->{path($from_dir)->basename}[0] || '0755'; |
|
316
|
|
|
|
|
|
|
})->then(sub { |
|
317
|
1
|
|
|
1
|
|
156
|
$to_map->{chmod}->($to_dir, $from_dir_perms) |
|
318
|
1
|
|
|
|
|
8
|
}); |
|
319
|
|
|
|
|
|
|
} |
|
320
|
|
|
|
|
|
|
$to_dir_create_p->then(sub { |
|
321
|
2
|
|
|
2
|
|
524
|
$from_map->{ls}->($from_dir) |
|
322
|
|
|
|
|
|
|
})->then(sub { |
|
323
|
2
|
|
|
2
|
|
1089
|
my ($dirs, $files) = @_; |
|
324
|
|
|
|
|
|
|
my @dir_create_p = map { |
|
325
|
2
|
|
|
|
|
9
|
my $this_dir = $_; |
|
|
1
|
|
|
|
|
3
|
|
|
326
|
|
|
|
|
|
|
$to_map->{mkdir}->("$to_dir/$this_dir") |
|
327
|
1
|
|
|
|
|
299
|
->then(sub { $to_map->{chmod}->("$to_dir/$this_dir", $dirs->{$this_dir}[0]) }) |
|
328
|
1
|
|
|
|
|
540
|
->then(sub { dir_walk_p("$from_dir/$this_dir", "$to_dir/$this_dir", $from_map, $to_map, 1) }) |
|
329
|
1
|
|
|
|
|
5
|
} sort keys %$dirs; |
|
330
|
|
|
|
|
|
|
my @file_create_p = map { |
|
331
|
2
|
|
|
|
|
222
|
my $this_file = $_; |
|
|
3
|
|
|
|
|
204
|
|
|
332
|
|
|
|
|
|
|
$from_map->{read}->($from_dir, $this_file) |
|
333
|
3
|
|
|
|
|
597
|
->then(sub { $to_map->{write}->($to_dir, $this_file, $_[0]) }) |
|
334
|
3
|
|
|
|
|
789
|
->then(sub { $to_map->{chmod}->("$to_dir/$this_file", $files->{$this_file}[0]) }) |
|
335
|
3
|
|
|
|
|
13
|
} sort keys %$files; |
|
336
|
2
|
|
|
|
|
418
|
Mojo::Promise->all(@dir_create_p, @file_create_p); |
|
337
|
2
|
|
|
|
|
622
|
}); |
|
338
|
|
|
|
|
|
|
} |
|
339
|
|
|
|
|
|
|
|
|
340
|
|
|
|
|
|
|
sub mirror_p { |
|
341
|
0
|
|
|
0
|
0
|
|
my ($from_dir, $to_dir, $from_map, $to_map) = @_; |
|
342
|
0
|
0
|
|
|
|
|
die "No from_dir\n" unless $from_dir; |
|
343
|
0
|
0
|
|
|
|
|
die "No to_dir\n" unless $to_dir; |
|
344
|
0
|
0
|
|
|
|
|
die "No from_map\n" unless $from_map; |
|
345
|
0
|
0
|
|
|
|
|
die "No to_map\n" unless $to_map; |
|
346
|
0
|
0
|
|
|
|
|
die "Invalid from_map\n" unless $from_map = $MAP2HASH{$from_map}; |
|
347
|
0
|
0
|
|
|
|
|
die "Invalid to_map\n" unless $to_map = $MAP2HASH{$to_map}; |
|
348
|
0
|
|
|
|
|
|
dir_walk_p $from_dir, $to_dir, $from_map, $to_map; |
|
349
|
|
|
|
|
|
|
} |
|
350
|
|
|
|
|
|
|
|
|
351
|
|
|
|
|
|
|
sub localfs_ls { |
|
352
|
0
|
|
|
0
|
0
|
|
my ($dir) = @_; |
|
353
|
0
|
|
|
|
|
|
my $dir_path = path($dir); |
|
354
|
|
|
|
|
|
|
my %files = map { |
|
355
|
0
|
|
|
|
|
|
($_->basename => [ sprintf("%04o", $_->lstat->mode & 07777), $_->lstat->mtime ]) |
|
|
0
|
|
|
|
|
|
|
|
356
|
|
|
|
|
|
|
} $dir_path->list({hidden => 1})->each; |
|
357
|
|
|
|
|
|
|
my %dirs = map { |
|
358
|
0
|
|
|
|
|
|
($_->basename => [ sprintf("%04o", $_->lstat->mode & 07777), $_->lstat->mtime ]) |
|
359
|
0
|
|
|
0
|
|
|
} $dir_path->list({dir => 1, hidden => 1})->grep(sub { !$files{$_->basename} })->each; |
|
|
0
|
|
|
|
|
|
|
|
360
|
0
|
|
|
|
|
|
Mojo::Promise->resolve(\%dirs, \%files); |
|
361
|
|
|
|
|
|
|
} |
|
362
|
|
|
|
|
|
|
|
|
363
|
|
|
|
|
|
|
sub localfs_mkdir { |
|
364
|
0
|
|
|
0
|
0
|
|
my ($dir) = @_; |
|
365
|
0
|
|
|
|
|
|
my $dir_path = path($dir); |
|
366
|
0
|
|
|
|
|
|
$dir_path->make_path; |
|
367
|
0
|
|
|
|
|
|
Mojo::Promise->resolve(1); |
|
368
|
|
|
|
|
|
|
} |
|
369
|
|
|
|
|
|
|
|
|
370
|
|
|
|
|
|
|
sub localfs_read { |
|
371
|
0
|
|
|
0
|
0
|
|
my ($dir, $file) = @_; |
|
372
|
0
|
|
|
|
|
|
my $path = path($dir)->child($file); |
|
373
|
0
|
|
|
|
|
|
Mojo::Promise->resolve($path->slurp); |
|
374
|
|
|
|
|
|
|
} |
|
375
|
|
|
|
|
|
|
|
|
376
|
|
|
|
|
|
|
sub localfs_write { |
|
377
|
0
|
|
|
0
|
0
|
|
my ($dir, $file, $content) = @_; |
|
378
|
0
|
|
|
|
|
|
my $path = path($dir)->child($file); |
|
379
|
0
|
|
|
|
|
|
$path->spurt($content); |
|
380
|
0
|
|
|
|
|
|
Mojo::Promise->resolve(1); |
|
381
|
|
|
|
|
|
|
} |
|
382
|
|
|
|
|
|
|
|
|
383
|
|
|
|
|
|
|
sub localfs_chmod { |
|
384
|
0
|
|
|
0
|
0
|
|
my ($path, $perms) = @_; |
|
385
|
0
|
|
|
|
|
|
$path = path($path); |
|
386
|
0
|
|
|
|
|
|
$path->chmod(oct $perms); |
|
387
|
0
|
|
|
|
|
|
Mojo::Promise->resolve(1); |
|
388
|
|
|
|
|
|
|
} |
|
389
|
|
|
|
|
|
|
|
|
390
|
|
|
|
|
|
|
sub cpanel_ls { |
|
391
|
0
|
|
|
0
|
0
|
|
my ($dir) = @_; |
|
392
|
|
|
|
|
|
|
uapi_p(qw(Fileman list_files), { dir => $dir })->then(sub { |
|
393
|
0
|
|
|
0
|
|
|
my (%dirs, %files); |
|
394
|
|
|
|
|
|
|
($_->{type} eq 'dir' ? \%dirs : \%files)->{$_->{file}} = |
|
395
|
|
|
|
|
|
|
[ $_->{nicemode}, $_->{mtime} ] |
|
396
|
0
|
0
|
|
|
|
|
for @{ $_[0]->{data} }; |
|
|
0
|
|
|
|
|
|
|
|
397
|
0
|
|
|
|
|
|
(\%dirs, \%files); |
|
398
|
0
|
|
|
|
|
|
}); |
|
399
|
|
|
|
|
|
|
} |
|
400
|
|
|
|
|
|
|
|
|
401
|
|
|
|
|
|
|
sub cpanel_read { |
|
402
|
0
|
|
|
0
|
0
|
|
my ($dir, $file) = @_; |
|
403
|
0
|
|
|
|
|
|
download_p "$dir/$file"; |
|
404
|
|
|
|
|
|
|
} |
|
405
|
|
|
|
|
|
|
|
|
406
|
|
|
|
|
|
|
sub cpanel_mkdir { |
|
407
|
0
|
|
|
0
|
0
|
|
my ($dir) = @_; |
|
408
|
0
|
|
|
|
|
|
$dir = path $dir; |
|
409
|
0
|
|
|
|
|
|
api2_p qw(Fileman mkdir), { path => $dir->dirname, name => $dir->basename }; |
|
410
|
|
|
|
|
|
|
} |
|
411
|
|
|
|
|
|
|
|
|
412
|
|
|
|
|
|
|
sub cpanel_write { |
|
413
|
0
|
|
|
0
|
0
|
|
my ($dir, $file, $content) = @_; |
|
414
|
0
|
|
|
|
|
|
api2_p qw(Fileman savefile), { |
|
415
|
|
|
|
|
|
|
dir => $dir, filename => $file, content => $content, |
|
416
|
|
|
|
|
|
|
}; |
|
417
|
|
|
|
|
|
|
} |
|
418
|
|
|
|
|
|
|
|
|
419
|
|
|
|
|
|
|
sub cpanel_chmod { |
|
420
|
0
|
|
|
0
|
0
|
|
my ($path, $perms) = @_; |
|
421
|
0
|
|
|
|
|
|
api2_p qw(Fileman fileop), { |
|
422
|
|
|
|
|
|
|
op => 'chmod', metadata => $perms, sourcefiles => $path, |
|
423
|
|
|
|
|
|
|
}; |
|
424
|
|
|
|
|
|
|
} |
|
425
|
|
|
|
|
|
|
|
|
426
|
|
|
|
|
|
|
1; |