Edit file File name : CloudLinux.pm Content :# Copyright © Cloud Linux GmbH & Cloud Linux Software, Inc 2010-2019 All Rights Reserved # # Licensed under CLOUD LINUX LICENSE AGREEMENT # http://cloudlinux.com/docs/LICENSE.TXT package CloudLinux; use strict; use warnings; use JSON::XS; use Text::Trim qw(trim); use Cpanel::SafeRun::Object(); use Whostmgr::HTMLInterface (); use Whostmgr::ACLS (); use MIME::Base64; use constant ASSETS_PATH => "/3rdparty/cloudlinux/assets"; use constant OWNER_ADMIN => 'admin'; use constant OWNER_USER => 'user'; use constant OWNER_RESELLER => 'reseller'; use constant APP_MODE => 'PRODUCTION_MODE'; use constant DEFAULT_LANGUAGE => 'en'; use constant DOC_ROOT => "/usr/local/cpanel/whostmgr/docroot"; use constant CLOUDLINUX_CLI => '/usr/share/l.v.e-manager/utils/cloudlinux-cli.py'; use constant CLOUDLINUX_CLI_USER => '/usr/share/l.v.e-manager/utils/cloudlinux-cli-user.py'; my $CURRENT_USER = $ENV{'TEAM_OWNER'} ? $ENV{'TEAM_OWNER'} : $ENV{'REMOTE_USER'}; my $current_locale; my $user_type; sub detectLocale { $current_locale = _getCurrentLocale($_[0]); } sub parseForm { my (%DATA) = @_; my %result; foreach my $key (keys %DATA) { if ($key =~ /^file\-/) { if ($key =~ /^file-(.+)-key$/) { my $fileName = $1; my $filePath = $DATA{"file-$1"}; if ($filePath =~ /\/Cpanel_Form_file\.upload\.[a-z0-9]{8,10}$/) { unshift (@{$result{$DATA{$key}}}, {'name' => $fileName, 'file' => $filePath}); } } } elsif ($key =~ /^([^\[\]]+)(\[.+\])$/) { my $name_of_param = $1; my $path = $2; my @parts = $path=~/\[([^\[\]]+)\]/g; unshift(@parts, $name_of_param); creatBranch(\@parts, \%result, $DATA{$key}); } else { $result{$key} = $DATA{$key}; } } return %result; } sub creatBranch { my ($parts, $post, $value) = @_; my $first = shift(@$parts); if (ref($_[1]) eq 'HASH') { if (exists $_[1]{$first}) { creatBranch(\@$parts, $_[1]{$first}, $value); } else { $_[1]{$first} = @$parts ? \%{getInnerValues($value, @$parts)} : $value; } } } sub getInnerValues { my ($value, @parts) = @_; my $first = shift(@parts); if (@parts) { return {$first => getInnerValues($value, @parts)} ; } else { return {$first => $value}; } } sub _getApplicationMode { my $modeFile = '/usr/share/l.v.e-manager/spa/app_mode.status'; if (-e $modeFile) { return trim(safeRun('cat '.$modeFile)); } return APP_MODE; } sub safeRun { my $command; if(ref($_[0]) eq 'ARRAY'){ $command = join ' ', $_[0]; } else { $command = join ' ', @_; } my $proc = Cpanel::SafeRun::Object->new( 'program' => '/bin/bash', 'args' => [ '-c', $command ], 'keep_env' => 1 ); my $stdout = trim($proc->stdout()); my $stderr = $proc->stderr(); if($stdout eq '') { return $stderr; } return $stdout; } sub _getUserIdByName { my ($user_name) = @_; return trim(safeRun( sprintf('id -u %s', $user_name) )); } sub setJsonHeader { my ($content) = @_; responseCustomHeaders("Content-type: application/json\n\n", $content); } sub responseFile { my ($filename) = @_; my $filesize; sendError("File download error", 0, 0, "File $filename not available for panel user") if !-e $filename; open FILE, "< $filename" or sendError("File download error", 0, 0, "File $filename is not available for reading"); binmode FILE; $filesize = -s $filename; print "Content-Type:application/x-download\n"; print "Content-Length: $filesize\n\n"; local $/ = \10240; while (<FILE>){ print $_; } exit; } sub responseCustomHeaders { my ($headers, $content) = @_; print "HTTP/1.1 200 OK\n"; print $headers; print $content; } sub knockKnock { setJsonHeader('{"result":"success"}'); } sub sendError { my ($errorMessage, $isJSON, $logoutSignal, $details) = @_; print "HTTP/1.1 503 Service Unavailable\n"; print "Content-type: application/json\n\n"; if ($isJSON) { print $errorMessage; } else { my %res = ( 'result' => $errorMessage, 'logoutSignal' => $logoutSignal ? $logoutSignal : 0, 'details' => $details || '' ); print encode_json \%res; } exit; } sub sendUnavailableError { my ($pluginName) = @_; print "HTTP/1.1 503 Service Unavailable\n"; print "Content-type: application/json\n\n"; my %res = ( 'result' => '', 'code' => 503, 'error_id' => 'ERROR.not_available_plugin', 'context' => { 'pluginName' => $pluginName, }, 'icon' => 'disabled' ); print encode_json \%res; exit; } sub checkMethod { if(($ENV{REQUEST_METHOD} ne $_[0]) && (!defined($_[1]) || $ENV{REQUEST_METHOD} ne $_[1])) { print "HTTP/1.1 405 Method Not Allowed\n"; print "Content-type: text/html\n\n"; print "Method Not Allowed"; exit; } } sub getPluginVersion { return safeRun('cat /usr/share/l.v.e-manager/version'); } sub _getCurrentLocale { my $cgi = $_[0]; my $locale = _getLocaleFromCookie($cgi) || _getSystemLocale(); return $locale; } sub _getLocaleFromCookie { my $cgi = $_[0]; return $cgi->cookie('session_locale'); } sub _getSystemLocale { my $userArgument = $user_type eq OWNER_USER ? '' : sprintf('--user=%s', $CURRENT_USER); my $responseInJson = safeRun( sprintf('cpapi2 %s Locale get_user_locale --output=json 2>/dev/null', $userArgument) ); my %response; eval { %response = %{decode_json($responseInJson)}; }; # If decode_json is catched an exeption or specified key in result doesn't exist # set default language if ($@ || !exists $response{'cpanelresult'}{'data'}[0]{'locale'}) { return DEFAULT_LANGUAGE; } else { return $response{'cpanelresult'}{'data'}[0]{'locale'}; } } sub loadAssets { my ($assetsPath, $mainBundle, $config, $assetsStaticPath) = @_; Whostmgr::HTMLInterface::load_css($assetsPath.'/css/bootstrap.min.css'); Whostmgr::HTMLInterface::load_css($assetsPath.'/css/lvemanager.css'); Whostmgr::HTMLInterface::load_css($assetsPath.'/static/common-styles.css'); loadGlobalVariables($assetsStaticPath); Whostmgr::HTMLInterface::load_js($assetsPath.'/js/jquery.min.js'); Whostmgr::HTMLInterface::load_js($assetsPath.'/js/bootstrap.min.js'); Whostmgr::HTMLInterface::load_js($assetsPath.'/js/'.$config.'.js'); Whostmgr::HTMLInterface::load_js($assetsPath.'/js/common.js'); # For integration tests, don't remove comment in production in line below #Whostmgr::HTMLInterface::load_js($assetsPath.'/js/interceptor.js'); #for integration tests my $pluginVersion = getPluginVersion(); Whostmgr::HTMLInterface::load_js( sprintf('%s/static/common.bundle.min.js?v=%s', $assetsPath, $pluginVersion) ); Whostmgr::HTMLInterface::load_js( sprintf('%s/static/polyfills.bundle.min.js?v=%s', $assetsPath, $pluginVersion) ); Whostmgr::HTMLInterface::load_js( sprintf('%s/static/vendor.bundle.min.js?v=%s', $assetsPath, $pluginVersion) ); Whostmgr::HTMLInterface::load_js( sprintf('%s/static/%s.bundle.min.js?v=%s', $assetsPath, $mainBundle, $pluginVersion) ); } sub getDataContent { my ($folder, $file_name, $print) = @_; my $file = DOC_ROOT.ASSETS_PATH."/$folder/$file_name"; my $content = ''; if (-e $file) { open(FH, $file); while (<FH>){ $content .= $_; } close(FH); } else { $content = qq{<div class="error_block"> The specified file does not exist</div>}; } if ($print) { print $content; } else { return $content; } } sub jsonHandler { my %data; my %REQUEST = %{$_[0]}; my $requestBody = $_[1]; my @ALLOWED_COMMANDS = qw(lvectl cloudlinux-awp-admin cloudlinux-limits); $data{'owner'} = $user_type; $data{'command'} = 'lvectl'; $data{'plugin_name'} = 'jsonhandler'; foreach my $param (keys %REQUEST) { if ($param eq 'handler') { $data{'method'} = $REQUEST{'handler'}; } elsif ($param eq 'command') { if (grep {$REQUEST{'command'} eq $_} @ALLOWED_COMMANDS) { $data{'command'} = $REQUEST{'command'}; } else { sendError('COMMAND NOT ALLOWED'); } } elsif (ref($param) ne 'HASH' && $param ne 'cgiaction' ) { if (exists $REQUEST{'command'} && $REQUEST{'command'} eq 'cloudlinux-limits' && $param eq 'lveid') { $data{'params'}{'lve-id'} = $REQUEST{$param}; } else { $data{'params'}{$param} = $REQUEST{$param}; } } } if(defined $requestBody) { $data{'params'}{'stdin'} = $requestBody; } my $fullCommandStr; $fullCommandStr = "ulimit -m unlimited -v unlimited && " . CLOUDLINUX_CLI; $fullCommandStr = sprintf( "%s --data=%s", $fullCommandStr, encode_base64(encode_json(\%data), '') ); my $responseInJson = safeRun($fullCommandStr); setJsonHeader($responseInJson); } sub lvemanagerHandler { my ($REQUEST_REF, $plugin_name) = @_; my %REQUEST = %$REQUEST_REF; unless (exists $REQUEST{'command'}) { sendError('COMMAND NOT SPECIFIED'); } my %data; $data{'owner'} = $user_type; $data{'command'} = $REQUEST{'command'}; $data{'plugin_name'} = $plugin_name; if (exists $REQUEST{'method'}) { $data{'method'} = $REQUEST{'method'}; } if (exists $REQUEST{'params'}) { $data{'params'} = $REQUEST{'params'}; } if (exists $REQUEST{'attachments[]'}) { $data{'attachments'} = []; foreach my $file ( @{$REQUEST{'attachments[]'}} ) { unshift (@{$data{'attachments'}}, $file); } } if ($data{'owner'} ne OWNER_ADMIN) { $data{'user_info'} = { 'username' => $CURRENT_USER, 'lve-id' => _getUserIdByName($CURRENT_USER) }; } if (exists $REQUEST{'mockJson'} && $REQUEST{'mockJson'}) { $data{'mockJson'} = $REQUEST{'mockJson'}; } if (exists $REQUEST{'lang'} && $REQUEST{'lang'}) { $data{'lang'} = $REQUEST{'lang'}; } my $fullCommandStr; if ($data{'owner'} eq OWNER_ADMIN) { $fullCommandStr = "ulimit -m unlimited -v unlimited && " . CLOUDLINUX_CLI; } elsif ($data{'owner'} eq OWNER_RESELLER) { $fullCommandStr = CLOUDLINUX_CLI } elsif ($data{'owner'} eq OWNER_USER) { $fullCommandStr = CLOUDLINUX_CLI_USER; } $fullCommandStr = sprintf( "%s --data=%s", $fullCommandStr, encode_base64(JSON::XS->new->encode(\%data), '') ); my $responseInJson = safeRun($fullCommandStr); my %response; eval { %response = %{decode_json($responseInJson)}; }; # If decode_json is catched an exeption, send error header with backtrace if ($@ && $responseInJson ne '') { sendError('ERROR.wrong_received_data', 0, 0, $responseInJson); } if (exists $response{'result'} && $response{'result'} eq 'file') { responseFile($response{'filepath'}) } if (exists $response{'result'} && $response{'result'} ne 'success' && $response{'result'} ne 'rollback') { sendError($responseInJson, 1); } if ($responseInJson eq '') { sendError('RESPONSE OF COMMAND IS EMPTY'); } setJsonHeader($responseInJson); } sub detectOwner { if (_isAdmin()) { return setOwner(OWNER_ADMIN); } if (_isReseller()) { return setOwner(OWNER_RESELLER); } return setOwner(OWNER_USER); } sub setOwner { my ($owner) = @_; $user_type = $owner; return $owner; } sub _isAdmin { if (Whostmgr::ACLS::hasroot()) { return 1; } return 0; } sub _isReseller { my $RESELLER_LIST_FILE = '/var/cpanel/resellers'; my $result = 0; if (-e $RESELLER_LIST_FILE) { open my $f, $RESELLER_LIST_FILE or die "Could not open $RESELLER_LIST_FILE: $!"; while( my $line = <$f>) { my @data = split /:/, $line; if ($CURRENT_USER eq $data[0]) { $result = 1; last; } } close $f; } return $result; } sub loadGlobalVariables { my $appMode = _getApplicationMode(); my $pluginVersion = getPluginVersion(); my ($assetsStaticPath) = @_; printf( '<script type="text/javascript">' . 'var userType = "%s";'. 'var userName = "%s";'. 'var currentLanguage = "%s";'. 'var APP_MODE = "%s";'. 'var localePath = "%s";'. 'var assetsStaticPath = "%s";'. 'var pluginVersion = "%s";'. '</script>', $user_type, $CURRENT_USER, $current_locale, $appMode, $assetsStaticPath.'/i18n/', $assetsStaticPath.'/', trim($pluginVersion) ); } # Should be present for require 1; Save