};
$txt .= qq{
$statusdata...};
$method eq 'print' ? print $txt : return $txt;
}
else {
$method eq 'print' ? print qq{$statusdata...} : return qq{$statusdata...};
}
return '';
}
sub setstatusdone {
Cpanel::LoadModule::load_perl_module('Cpanel::MagicRevision') if !$INC{'Cpanel/MagicRevision.pm'};
return _end_status_block( "Done", Cpanel::MagicRevision::calculate_magic_url( '/cjt/images/icons/success.png', $ENV{'REQUEST_URI'}, '/usr/local/cpanel/whostmgr/docroot' ) );
}
sub setstatuserror {
Cpanel::LoadModule::load_perl_module('Cpanel::MagicRevision') if !$INC{'Cpanel/MagicRevision.pm'};
return _end_status_block( "Failed", Cpanel::MagicRevision::calculate_magic_url( '/cjt/images/icons/error.png', $ENV{'REQUEST_URI'}, '/usr/local/cpanel/whostmgr/docroot' ) );
}
sub _end_status_block {
my ( $msg, $img ) = @_;
local $| = 1;
$status_block_depth--;
if ( !$nohtml && !( -t STDOUT || !defined $ENV{'GATEWAY_INTERFACE'} || $ENV{'GATEWAY_INTERFACE'} !~ /CGI/i ) ) {
my $txt = qq{...$msg
\n};
$method eq 'print' ? print $txt : return $txt;
}
else {
$method eq 'print' ? print qq{...$msg\n} : return qq{...$msg\n};
}
return '';
}
sub clearstatus {
local $| = 1;
if ( !$nohtml
&& !( -t STDOUT || !defined $ENV{'GATEWAY_INTERFACE'} || $ENV{'GATEWAY_INTERFACE'} !~ /CGI/i ) ) {
if ( $method ne 'hide' ) { print "\n"; }
}
return '';
}
1;
} # --- END Whostmgr/UI.pm
{ # --- BEGIN Whostmgr/Transfers/State.pm
package Whostmgr::Transfers::State;
use strict;
my $is_transfer = 0;
sub start_transfer {
$is_transfer = 1;
return 1;
}
sub is_transfer {
return ( $is_transfer eq 1 ) ? 1 : 0;
}
sub end_transfer {
$is_transfer = 0;
return 1;
}
1;
} # --- END Whostmgr/Transfers/State.pm
{ # --- BEGIN Cpanel/Validate/Username/Mode.pm
package Cpanel::Validate::Username::Mode;
use strict;
use warnings;
# use Whostmgr::Transfers::State ();
my $_allows_leading_digits;
sub allows_leading_digits {
return ( $_allows_leading_digits //= ( -e '/etc/allowstupidstuff' ? 1 : 0 ) );
}
*in_transfer_mode = *Whostmgr::Transfers::State::is_transfer;
sub additional_reserved_usernames {
return ();
}
sub _clear_cache {
return undef $_allows_leading_digits;
}
1;
} # --- END Cpanel/Validate/Username/Mode.pm
{ # --- BEGIN Cpanel/Validate/Username/Core.pm
package Cpanel::Validate::Username::Core;
use strict;
use warnings;
# use Cpanel::Validate::Username::Mode ();
# use Cpanel::ArrayFunc::Uniq ();
# use Cpanel::LoadFile ();
our $MAX_LENGTH = 16; #Constrained by MySQL
our $MAX_SYSTEM_USERNAME_LENGTH = 31;
our $ETC_ALIASES_PATH = '/etc/aliases';
our $ETC_LOCALALIASES_PATH = '/etc/localaliases';
our @_list_reserved_usernames;
sub list_reserved_usernames {
return @_list_reserved_usernames if @_list_reserved_usernames;
my @names = qw(
abrt
admin
all
clamav
cpanel-ccs
cpeasyapache
cpses
cptkt
cphulkd
cpwebcalls
dbus
dirs
dnsadmin
dovecot
eximstats
files
ftpxferlog
haldaemon
information_schema
mailman
modsec
munin
mydns
nobody
performance_schema
postgres
postmaster
proftpd
root
roundcube
shadow
spamassassin
ssl
sudo
sys
system
tcpdump
tmp
tomcat
toor
virtfs
virtual
webmaster
wheel
whmxfer
_lock
agent360
);
push @names, aliases();
push @names, Cpanel::Validate::Username::Mode::additional_reserved_usernames();
push @names, _get_sudoers_entries();
return ( @_list_reserved_usernames = Cpanel::ArrayFunc::Uniq::uniq(@names) );
}
our $SUDOERS = "/etc/sudoers";
our $SUDOERS_D = "/etc/sudoers.d";
sub _get_sudoers_entries {
my %sudoers;
for my $file ( $SUDOERS, _sudoersd() ) {
for my $line ( split m/\n+/, Cpanel::LoadFile::load($file) ) {
if ( $line =~ m/^\s*\%(\S+)/ ) {
$sudoers{"$1"}++; # Copy $1 since it can be weird in binaries
}
}
}
return keys %sudoers;
}
sub _sudoersd {
my $dir = shift // $SUDOERS_D;
return if !-d $dir; # not TOCTOU, just want to facilitate system w/out /etc/sudoers.d
opendir( my $dh, $dir ) || die "Can't open $dir: $!\n";
my @results;
while ( my $name = readdir $dh ) {
next if $name eq "." || $name eq "..";
my $path = "$dir/$name";
if ( -d $path ) { # symlink or not
push @results, _sudoersd($path);
}
else {
push @results, $path; # symlink or not
}
}
closedir $dh;
return @results;
}
sub reserved_username_prefixes {
return qw(test
passwd.
cpmydns
cpanel
mydns
pg_toast
pg_temp
cpses
cptkt
cpbackup);
}
sub reserved_username_suffixes {
return qw(
.lock
.cache
.yaml
.json
.db
assword
);
}
sub list_reserved_username_patterns {
return (
list_reserved_username_regexes(),
( map { '^' . quotemeta($_) } reserved_username_prefixes() ),
( map { quotemeta($_) . '$' } reserved_username_suffixes() )
);
}
sub list_reserved_username_regexes {
return qw(
^[-._0-9]+$
);
}
sub get_system_username_regexp_str {
return '^' . _regexp_lead() . "[-._A-Za-z0-9]{1,$MAX_SYSTEM_USERNAME_LENGTH}\$";
}
sub get_regexp_str {
return '^' . _regexp_lead() . "[-._a-z0-9]{1,$MAX_LENGTH}\$";
}
sub _regexp_lead {
return Cpanel::Validate::Username::Mode::allows_leading_digits() ? '(?![-.])' : '(?![-.0-9])';
}
sub is_valid_system_username {
my ($user) = @_;
return if !defined $user;
my $regexp = get_system_username_regexp_str();
$regexp = _apply_perl_boundary($regexp);
return $user =~ m{$regexp}o;
}
sub is_valid {
my ($user) = @_;
return if !defined $user;
my $regexp = get_regexp_str();
$regexp = _apply_perl_boundary($regexp);
return $user =~ m{$regexp}o;
}
sub normalize {
my ($name) = @_;
return unless defined $name;
$name =~ s/^\s+//;
$name =~ s/\s+$//;
$name =~ tr/A-Z/a-z/;
return $name;
}
sub scrub {
my ($name) = @_;
return unless defined $name;
$name = normalize($name);
if ( Cpanel::Validate::Username::Mode::in_transfer_mode() ) {
$name =~ tr/-a-z0-9._//cd;
}
else {
$name =~ tr/a-z0-9._//cd;
}
substr( $name, 0, 1, '' ) while index( $name, '.' ) == 0;
$name =~ s/^\d+// unless Cpanel::Validate::Username::Mode::allows_leading_digits();
if ( length $name > $MAX_LENGTH ) {
substr( $name, $MAX_LENGTH ) = q{};
}
return $name;
}
sub reserved_username_check {
my ($user) = @_;
return if !$user;
return 1 if grep { $_ eq $user } list_reserved_usernames();
return 1 if grep { rindex( $user, $_, 0 ) == 0 } reserved_username_prefixes();
return 1 if grep { index( $user, $_, length($user) - length($_) ) != -1 } reserved_username_suffixes();
return 1 if grep { $user =~ m{$_} } list_reserved_username_regexes();
return;
}
sub aliases {
my @reserved_names;
foreach my $file ( $ETC_ALIASES_PATH, $ETC_LOCALALIASES_PATH ) {
next if ( !-f $file || !-r _ );
my ( $name, $line );
for $line ( split m<\n+>, Cpanel::LoadFile::load($file) ) {
$name = ( split /:/, $line )[0];
next if !length $name || $name =~ tr< :#\t><>;
push @reserved_names, $name;
}
}
return @reserved_names;
}
sub _apply_perl_boundary {
$_[0] =~ s{^\^}{\\A};
$_[0] =~ s{\$$}{\\z};
return $_[0];
}
1;
} # --- END Cpanel/Validate/Username/Core.pm
{ # --- BEGIN Cpanel/AcctUtils/Domain.pm
package Cpanel::AcctUtils::Domain;
use strict;
use warnings;
# use Cpanel::Config::LoadCpUserFile ();
# use Cpanel::Config::HasCpUserFile ();
sub getdomain {
my ($user) = @_;
return unless Cpanel::Config::HasCpUserFile::has_cpuser_file($user);
return Cpanel::Config::LoadCpUserFile::loadcpuserfile($user)->{'DOMAIN'};
}
1;
} # --- END Cpanel/AcctUtils/Domain.pm
{ # --- BEGIN Cpanel/Config/userdata/Constants.pm
package Cpanel::Config::userdata::Constants;
use strict;
use warnings;
our $USERDATA_DIR = '/var/cpanel/userdata';
1;
} # --- END Cpanel/Config/userdata/Constants.pm
{ # --- BEGIN Cpanel/AcctUtils/Account.pm
package Cpanel::AcctUtils::Account;
use strict;
use warnings;
# use Cpanel::PwCache ();
our $USERS_DIR = '/var/cpanel/users';
sub accountexists {
return 1 if $_[0] && $_[0] eq 'root';
return length( scalar Cpanel::PwCache::getpwnam_noshadow( $_[0] ) ) ? 1 : 0;
}
sub get_existing_account_parts {
my ($user) = @_;
my @existing_parts = ();
push( @existing_parts, "$USERS_DIR/$user" ) if -e "$USERS_DIR/$user";
push( @existing_parts, "system user “$user”" ) if accountexists($user);
require Cpanel::Config::userdata::Constants;
push( @existing_parts, "$Cpanel::Config::userdata::Constants::USERDATA_DIR/$user" ) if -e "$Cpanel::Config::userdata::Constants::USERDATA_DIR/$user";
return \@existing_parts;
}
sub accountexists_or_die {
accountexists( $_[0] ) or do {
require Cpanel::Exception;
die Cpanel::Exception::create( 'UserNotFound', [ name => $_[0] ] );
};
return 1;
}
1;
} # --- END Cpanel/AcctUtils/Account.pm
{ # --- BEGIN Cpanel/AcctUtils/Lookup/Webmail.pm
package Cpanel::AcctUtils::Lookup::Webmail;
use strict;
use warnings;
sub is_webmail_user {
return (
(
defined $_[0] # must have some value
&& length( $_[0] ) > 4 # must be at least 5 characters long (1 localpart, 3 domain, 1 domain separator)
&& 1 == $_[0] =~ tr{+%:@}{} # must have 1 domain separator character
&& 1 == $_[0] =~ tr{a-zA-Z0-9._-}{}c # the domain separator is the only character present that is not allowed in a localpart or domain
&& substr( $_[0], 0, 1 ) !~ tr{+%:@.}{} # first character could be a localpart ( no domain separator or . )
&& substr( $_[0], -3, 2 ) !~ tr{+%:@_}{} # first two of the last three characters could be a domain
&& substr( $_[0], -1, 1 ) !~ tr{+%:@._-}{} # last character could be a tld
) ? 1 : 0
);
}
sub is_strict_webmail_user {
return (
(
defined $_[0] # must have some value
&& length( $_[0] ) > 4 # must be at least 5 characters long (1 localpart, 3 domain, 1 domain separator)
&& 1 == $_[0] =~ tr{@}{} # must contain 1 domain separator character
&& 1 == $_[0] =~ tr{a-z0-9._-}{}c # the domain separator is the only character that is not allowed in a localpart or domain
&& substr( $_[0], 0, 1 ) !~ tr{@.}{} # first character could be a localpart ( no domain separator or . )
&& substr( $_[0], -3, 2 ) !~ tr{@_}{} # first two of the last three characters could be a domain
&& substr( $_[0], -1, 1 ) !~ tr{@._-}{} # last character could be a tld
) ? 1 : 0
);
}
sub normalize_webmail_user {
my ($user) = @_;
$user =~ tr/A-Z+%:/a-z@/;
return ( wantarray() ? split( '@', $user, 2 ) : $user );
}
1;
} # --- END Cpanel/AcctUtils/Lookup/Webmail.pm
{ # --- BEGIN Cpanel/AcctUtils/Lookup.pm
package Cpanel::AcctUtils::Lookup;
use strict;
use warnings;
# use Cpanel::AcctUtils::DomainOwner::Tiny ();
# use Cpanel::AcctUtils::Account ();
# use Cpanel::AcctUtils::Lookup::Webmail ();
# use Cpanel::Exception ();
sub get_system_user {
my $sysuser = get_system_user_without_existence_validation( $_[0] );
if ( !Cpanel::AcctUtils::Account::accountexists($sysuser) ) {
die Cpanel::Exception::create( 'UserNotFound', [ name => $sysuser ] );
}
return $sysuser;
}
sub get_system_user_without_existence_validation { ##no critic qw(RequireArgUnpacking)
die Cpanel::Exception::create( 'UserNotFound', [ name => '' ] ) unless defined $_[0] && length $_[0];
if ( $_[0] =~ tr{/}{} ) {
die Cpanel::Exception::create( 'InvalidParameter', '“[_1]” is not a valid username because it contains a “[_2]” character.', [ $_[0], '/' ] );
}
return $_[0] if !Cpanel::AcctUtils::Lookup::Webmail::is_strict_webmail_user( $_[0] );
my ($domain) = ( split( m{@}, $_[0], 2 ) )[1];
my $sysuser = Cpanel::AcctUtils::DomainOwner::Tiny::getdomainowner( $domain, { 'skiptruelookup' => 1, 'default' => '' } );
if ( !length $sysuser ) {
die Cpanel::Exception::create( 'DomainDoesNotExist', 'The domain “[_1]” does not exist.', [$domain] );
}
return $sysuser;
}
1;
} # --- END Cpanel/AcctUtils/Lookup.pm
{ # --- BEGIN Cpanel/Sereal/Decoder.pm
package Cpanel::Sereal::Decoder;
use cPstrict;
use Sereal::Decoder ();
my %REQUIRED_OPTIONS = (
refuse_objects => 1,
no_bless_objects => 1,
refuse_snappy => 1,
validate_utf8 => 1,
max_recursion_depth => 100,
);
sub create {
return Sereal::Decoder->new( \%REQUIRED_OPTIONS );
}
1;
} # --- END Cpanel/Sereal/Decoder.pm
{ # --- BEGIN Cpanel/Sereal/Encoder.pm
package Cpanel::Sereal::Encoder;
use cPstrict;
use Sereal::Encoder ();
my %REQUIRED_OPTIONS = (
croak_on_bless => 1,
);
sub create {
return Sereal::Encoder->new( \%REQUIRED_OPTIONS );
}
1;
} # --- END Cpanel/Sereal/Encoder.pm
{ # --- BEGIN Cpanel/JSON/Sanitize.pm
package Cpanel::JSON::Sanitize;
use cPstrict;
sub sanitize_for_dumping {
my $item = shift;
my $reftype = ref $item;
if ( !defined $item || $reftype eq "" ) {
return $item;
}
elsif ( ( $reftype eq 'SCALAR' || $reftype eq 'JSON::XS::Boolean' || $reftype eq 'JSON::PP::Boolean' || $reftype eq 'Types::Serialiser::Boolean' ) && ( $$item eq '1' || $$item eq '0' ) ) {
return $item;
}
elsif ( $reftype eq "ARRAY" ) {
return [ map { sanitize_for_dumping($_) } grep { _suitable_for_dumping($_) } @$item ];
}
elsif ( $reftype eq "HASH" ) {
return {
map { $_ => sanitize_for_dumping( $item->{$_} ) }
grep { _suitable_for_dumping( $item->{$_} ) }
keys %$item
};
}
else {
die "That data structure isn't suitable for dumping.";
}
}
sub filter_to_json ($item) {
my $ref = ref $item;
return $item if !$ref;
if ( 'ARRAY' eq $ref ) {
return [ map { filter_to_json($_) } @$item ];
}
elsif ( 'HASH' eq $ref ) {
my %dupe = map { ( $_ => filter_to_json( $item->{$_} ) ) } keys %$item;
return \%dupe;
}
elsif ( UNIVERSAL::can( $item, 'TO_JSON' ) ) {
return filter_to_json( $item->TO_JSON() );
}
require Carp;
Carp::croak("$item is not JSON-compatible!");
}
sub uxxxx_to_bytes {
my $str_r = \$_[0];
$$str_r =~ s/
(\\+)u( [a-fA-F0-9]{4} )
/
if ( length($1) % 2 ) {
my $c = pack('U', hex $2);
utf8::encode($c);
substr($1, 1) . $c;
}
else {
"$1u$2";
}
/exg;
return $$str_r;
}
sub _suitable_for_dumping {
my $item = shift;
my $reftype = ref $item;
return 1 if !defined $item;
return 1 if $reftype eq "" || $reftype eq 'ARRAY' || $reftype eq 'HASH';
return 1 if ( $reftype eq 'SCALAR' || $reftype eq 'JSON::XS::Boolean' || $reftype eq 'JSON::PP::Boolean' || $reftype eq 'Types::Serialiser::Boolean' ) && ( $$item eq '1' || $$item eq '0' );
return 0;
}
1;
} # --- END Cpanel/JSON/Sanitize.pm
{ # --- BEGIN Cpanel/UntrustedException.pm
package Cpanel::UntrustedException;
use strict;
use warnings;
use overload ( '""' => \&stringify, fallback => 1 );
sub new {
my ( $package, %params ) = @_;
my $self = {
string => $params{string},
class => $params{class},
longmess => $params{longmess},
metadata => $params{metadata},
};
return bless $self, $package;
}
sub class {
my ($self) = @_;
return $self->{class};
}
sub string {
my ($self) = @_;
return $self->{string};
}
sub longmess {
my ($self) = @_;
return $self->{longmess};
}
sub get {
my ( $self, $attr ) = @_;
return $self->{metadata}{$attr};
}
sub stringify {
my ($self) = @_;
if ( $self->{class} ) {
return $self->{class} . '/' . join "\n", $self->{string} || '