#!/usr/local/cpanel/3rdparty/bin/perl #WHMADDON:addonupdates:ConfigServer Mail Manage #ACLS:configserver ############################################################################### # Copyright (C) 2006-2025 Jonathan Michaelson # # https://github.com/waytotheweb/scripts # # This program is free software; you can redistribute it and/or modify it under # the terms of the GNU General Public License as published by the Free Software # Foundation; either version 3 of the License, or (at your option) any later # version. # # This program is distributed in the hope that it will be useful, but WITHOUT # ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS # FOR A PARTICULAR PURPOSE. See the GNU General Public License for more # details. # # You should have received a copy of the GNU General Public License along with # this program; if not, see . ############################################################################### ## no critic (RequireUseWarnings, ProhibitExplicitReturnUndef, ProhibitMixedBooleanOperators, RequireBriefOpen) use strict; use CGI::Carp qw(fatalsToBrowser); use Sys::Hostname qw(hostname); use IPC::Open3; use File::Basename; use Fcntl qw(:DEFAULT :flock); use lib '/usr/local/cpanel'; require Cpanel::Form; require Cpanel::Config; require Whostmgr::ACLS; require Cpanel::Rlimit; require Cpanel::Template; require Cpanel::Version::Tiny; ############################################################################### # start main our ($class, $day, $hrs, $images, $min, $month, $myv, $script, $subdir, $used, $user, $versionfile, $x, $year, @localdomains, %FORM, $downloadserver); %FORM = Cpanel::Form::parseform(); Whostmgr::ACLS::init_acls(); if (!Whostmgr::ACLS::hasroot()) { print "Content-type: text/html\r\n\r\n"; print "You do not have access to this option.\n"; exit(); } Cpanel::Rlimit::set_rlimit_to_infinity(); $script = "cmm.cgi"; $images = "cmm"; $versionfile = "/usr/local/cpanel/whostmgr/docroot/cgi/configserver/cmm/cmmversion.txt"; open (my $IN, "<", $versionfile) or die $!; flock ($IN, LOCK_SH); $myv = <$IN>; close ($IN); chomp $myv; $downloadserver = &getdownloadserver; my $thisapp = "cmm"; my $reregister; my $modalstyle; if ($Cpanel::Version::Tiny::major_version >= 65) { if (-e "/usr/local/cpanel/whostmgr/docroot/cgi/configserver/${thisapp}/${thisapp}.conf") { sysopen (my $CONF, "/usr/local/cpanel/whostmgr/docroot/cgi/configserver/${thisapp}/${thisapp}.conf", O_RDWR | O_CREAT); flock ($CONF, LOCK_EX); my @confdata = <$CONF>; chomp @confdata; for (0..scalar(@confdata)) { if ($confdata[$_] =~ /^target=mainFrame/) { $confdata[$_] = "target=_self"; $reregister = 1; } } if ($reregister) { seek ($CONF, 0, 0); truncate ($CONF, 0); foreach (@confdata) { print $CONF "$_\n"; } &printcmd("/usr/local/cpanel/bin/register_appconfig","/usr/local/cpanel/whostmgr/docroot/cgi/configserver/${thisapp}/${thisapp}.conf"); $reregister = "

Updated application. The next time you login to WHM this will open within the native WHM main window instead of launching a separate window

\n"; } close ($CONF); } } print "Content-type: text/html\r\n\r\n"; if ($Cpanel::Version::Tiny::major_version < 65) { $modalstyle = "style='top:120px'"; } my $bootstrapcss = ""; my $jqueryjs = ""; my $bootstrapjs = ""; my $templatehtml; unless ($FORM{action} eq "viewmail") { open SCRIPTOUT, '>', \$templatehtml; select SCRIPTOUT; print < $jqueryjs $bootstrapjs EOF } else { print < $bootstrapcss $jqueryjs $bootstrapjs
EOF } print <

ConfigServer Mail Manage - cmm v$myv

EOF if ($reregister ne "") {print $reregister} $| = 1; ## no critic my $mailscanner = 0; if (-e "/usr/mscpanel/version.txt") {$mailscanner = 1} if ($FORM{domain} ne "" and $FORM{domain} =~ /[^\w\-\.]/) { print "Invalid domain name [$FORM{domain}]"; } elsif ($FORM{account} ne "" and $FORM{account} =~ /[^a-zA-Z0-9\-\_\.\@\+]/) { print "Invalid account name [$FORM{account}]"; } elsif ($FORM{action} eq "Manage Mail Forwarders") { my %userdomains; open (my $IN, "<","/etc/userdomains"); flock ($IN, LOCK_SH); my @localusers = <$IN>; close ($IN); chomp @localusers; foreach my $line (@localusers) { my ($domain,$user) = split(/\:\s*/,$line,2); $userdomains{$domain} = $user; } print "

    

\n"; print "\n"; print ""; print ""; my $total = 0; open (my $VALIASES, "<", "/etc/valiases/$FORM{domain}"); flock ($VALIASES, LOCK_SH); my @forwarders = <$VALIASES>; close ($VALIASES); chomp @forwarders; foreach my $aliases (@forwarders) { my ($alias,$recipient) = split(/: /,$aliases,2); if ($alias eq "*") {$alias .= " (Default Address)"} print "\n"; $total++; } unless ($total) {print "\n"} print "\n"; print "
Mail Forwarders for $FORM{domain}
ForwarderRecipient
$alias$recipient
No entries found
"; my ($childin, $childout); my $cmdpid = open3($childin, $childout, $childout, "/usr/bin/quota", "-qlu", $userdomains{$FORM{domain}}); my @data = <$childout>; waitpid ($cmdpid, 0); chomp @data; if ($data[0] =~ /Block limit reached/) { print "

cPanel account over quota - editing disabled

"; } else { print "
"; } print "
\n"; print "

Total Accounts: $total

\n"; print "

\n"; } elsif ($FORM{action} eq "Edit Mail Forwarders") { open (my $IN,"<","/etc/valiases/$FORM{domain}"); flock ($IN, LOCK_SH); my @confdata = <$IN>; close ($IN); chomp @confdata; my $max = 80; foreach my $line (@confdata) {if (length($line) > $max) {$max = length($line) + 1}} print "
\n"; print "\n"; print "Edit /etc/valiases/$FORM{domain}\n"; print "\n"; print "
\n"; print "

\n"; print "
\n"; print "

\n"; } elsif ($FORM{action} eq "saveforwarders") { $FORM{formdata} =~ s/\r//g; sysopen(my $OUT,"/etc/valiases/$FORM{domain}", O_WRONLY | O_CREAT | O_TRUNC); flock ($OUT, LOCK_EX); print $OUT $FORM{formdata}; close ($OUT); my %userdomains; open (my $IN, "<","/etc/userdomains"); flock ($IN, LOCK_SH); my @localusers = <$IN>; close ($IN); chomp @localusers; foreach my $line (@localusers) { my ($domain,$user) = split(/\: /,$line,2); $userdomains{$domain} = $user; } chown ((getpwnam($userdomains{$FORM{domain}}))[2],(getgrnam("mail"))[2],"/etc/valiases/$FORM{domain}"); print "

Changes saved.

\n"; print "

\n"; } elsif ($FORM{action} eq "Manage Mail Filters") { my %userdomains; open (my $IN,"<","/etc/userdomains"); flock ($IN, LOCK_SH); my @localusers = <$IN>; close ($IN); chomp @localusers; foreach my $line (@localusers) { my ($domain,$user) = split(/\: /,$line,2); $userdomains{$domain} = $user; } print "

    

\n"; print "\n"; print ""; my $class = "tdshade2_noborder"; if (-z "/etc/vfilters/$FORM{domain}") { print "\n"; } else { print "\n"; print "\n"; print "
Mail Filters for $FORM{domain}
No entries found
\n";
		open (my $IN, "<", "/etc/vfilters/$FORM{domain}");
		flock ($IN, LOCK_SH);
		my @data = <$IN>;
		close ($IN);
		foreach my $line (@data) {
			$line =~ s/&/&/g;
			$line =~ s/>/>/g;
			$line =~ s/
\n"; my ($childin, $childout); my $cmdpid = open3($childin, $childout, $childout, "/usr/bin/quota", "-qlu", $userdomains{$FORM{domain}}); my @data = <$childout>; waitpid ($cmdpid, 0); chomp @data; if ($data[0] =~ /Block limit reached/) { print "

cPanel account over quota - editing disabled

"; } else { print "
\n"; } print "
\n"; print "

\n"; } elsif ($FORM{action} eq "Edit Mail Filters") { open (my $IN, "<","/etc/vfilters/$FORM{domain}"); flock ($IN, LOCK_SH); my @confdata = <$IN>; close ($IN); chomp @confdata; my $max = 80; foreach my $line (@confdata) {if (length($line) > $max) {$max = length($line) + 1}} print "
\n"; print "\n"; print "
\n"; print "
Edit /etc/vfilters/$FORM{domain}
\n"; print "
\n"; print "
\n"; print "
\n"; print "
\n"; print "

\n"; } elsif ($FORM{action} eq "savefilters") { $FORM{formdata} =~ s/\r//g; open(my $OUT,">","/etc/vfilters/$FORM{domain}"); print $OUT $FORM{formdata}; close($OUT); my %userdomains; open(my $IN,"<","/etc/userdomains"); my @localusers = <$IN>; close($IN); chomp @localusers; foreach my $line (@localusers) { my ($domain,$user) = split(/\: /,$line,2); $userdomains{$domain} = $user; } chown ((getpwnam($userdomains{$FORM{domain}}))[2],(getgrnam("mail"))[2],"/etc/vfilters/$FORM{domain}"); print "

Changes saved.

\n"; print "

\n"; } elsif ($FORM{action} eq "Manage Mail Hourly Limits") { if ($FORM{domain} ne "") { my $cpconf = Cpanel::Config::loadcpconf(); my $maxemails = $cpconf->{maxemailsperhour}; my $account; my %userdomains; my $usermaxemails; my $domainmaxemails; open(my $IN,"<","/etc/userdomains"); my @localusers = <$IN>; close($IN); chomp @localusers; foreach my $line (@localusers) { my ($domain,$user) = split(/\: /,$line,2); $userdomains{$domain} = $user; } $account = $userdomains{$FORM{domain}}; open(my $DOMAIN,"<","/var/cpanel/users/$userdomains{$FORM{domain}}"); my @confdata = <$DOMAIN>; close($DOMAIN); chomp @confdata; foreach my $line (@confdata) { if ($line =~ /^MAX_EMAIL_PER_HOUR=(\d+)/) {$usermaxemails = $1} if ($line =~ /^MAX_EMAIL_PER_HOUR-$FORM{domain}=(\d+)/) {$domainmaxemails = $1} } if ($usermaxemails > 0) {$maxemails = $usermaxemails} if ($domainmaxemails > 0) {$maxemails = $domainmaxemails} if ($maxemails eq "") {$maxemails = 0} print "

    

\n"; print "
\n"; print "\n"; print "
\n"; print "
Edit Mail Hourly limits for $FORM{domain}
\n"; print "
\n"; print "\n"; print "
$FORM{domain} can send a maximum of per hour [0 = unlimited]
\n"; print "
\n"; print "
\n"; } else { print "
You must select a domain first
\n"; } print "

\n"; } elsif ($FORM{action} eq "savelimits") { my %userdomains; open (my $IN,"<","/etc/userdomains"); flock ($IN, LOCK_SH); my @localusers = <$IN>; close ($IN); chomp @localusers; foreach my $line (@localusers) { my ($domain,$user) = split(/\: /,$line,2); $userdomains{$domain} = $user; } open (my $USERS, "<","/var/cpanel/users/$userdomains{$FORM{domain}}"); flock ($USERS, LOCK_SH); my @confdata = <$USERS>; close ($USERS); chomp @confdata; my @newconfdata; foreach my $line (@confdata) { if ($line =~ /^MAX_EMAIL_PER_HOUR-$FORM{domain}=(\d+)/) {next} push @newconfdata,$line; } push @newconfdata, "MAX_EMAIL_PER_HOUR-$FORM{domain}=$FORM{maxemails}"; sysopen (my $OUT, "/var/cpanel/users/$userdomains{$FORM{domain}}", O_WRONLY | O_CREAT | O_TRUNC); flock ($OUT, LOCK_EX); foreach my $line (@newconfdata) {print $OUT "$line\n"} close ($OUT); my $cmd = "/scripts/updateuserdomains"; my ($childin, $childout); my $cmdpid = open3($childin, $childout, $childout, $cmd); my @data = <$childout>; waitpid ($cmdpid, 0); chomp @data; print "

$cmd

\n"; foreach my $line (@data) { print "$line
\n"; } print "

Changes saved.

\n"; print "

\n"; } elsif ($FORM{action} eq "viewmail") { if (-f $FORM{file}) { my %userdomains; open (my $IN,"<","/etc/userdomains"); flock ($IN, LOCK_SH); my @localusers = <$IN>; close ($IN); chomp @localusers; foreach my $line (@localusers) { my ($domain,$user) = split(/\: /,$line,2); $userdomains{$domain} = $user; } my ($file, $filedir) = fileparse($FORM{file}); my $homedir = ( getpwnam($userdomains{$FORM{domain}}) )[7]; if ($homedir eq "" or $filedir !~ /^$homedir/) { print "Invalid file [$FORM{file}]"; } else { print "\n"; print ""; print "\n"; print "
View Email
\n";
			my @mail;
			open (my $IN, "<", $FORM{file});
			unless ($FORM{f}) {
				my $x;
				for ($x = 0; $x < 500;$x++) {
					my $line  = <$IN>;
					$line =~ s/&/&/g;
					$line =~ s/>/>/g;
					$line =~ s/\n";
					print "...[truncated to 500 lines] View full ($size KB) email\n";
				} else {
					print "
"; } } else { while (my $line = <$IN>) { $line =~ s/&/&/g; $line =~ s/>/>/g; $line =~ s/"; } close ($IN); print "
\n"; } } else { print "File [$FORM{file}] not found"; } } elsif ($FORM{action} eq "empty") { print "
\n"; print "\n"; print ""; print "\n"; print "\n"; print "
Empty Mailbox $FORM{account}\@$FORM{domain}
Are you sure that you want to irretrievably delete all email and associated files within this mailbox?
\n"; print "
\n"; print "

\n"; } elsif ($FORM{action} eq "Empty Mailbox") { my %userdomains; open (my $IN,"<","/etc/userdomains"); flock ($IN, LOCK_SH); my @localusers = <$IN>; close ($IN); chomp @localusers; foreach my $line (@localusers) { my ($domain,$user) = split(/\: /,$line,2); $userdomains{$domain} = $user; } my $dir; my $title; if ($FORM{top}) { my $homedir = ( getpwnam($FORM{account}) )[7]; $dir = "$homedir/mail"; $title = "$FORM{account} (cPanel user)"; } else { my $homedir = ( getpwnam($userdomains{$FORM{domain}}) )[7]; $dir = "$homedir/mail/$FORM{domain}/$FORM{account}"; $title = "$FORM{account}\@$FORM{domain}"; } print "\n"; print ""; print "\n"; print "
$title
\n"; my @maildirs; push @maildirs, $dir; opendir (my $DIR, $dir); while (my $file = readdir($DIR)) { if ($file eq ".") {next} if ($file eq "..") {next} if (readlink "$dir/$file") {next} if ((-d "$dir/$file") and (-d "$dir/$file/cur") and (-d "$dir/$file/new") and (-d "$dir/$file/tmp")) {push @maildirs, "$dir/$file"} } closedir ($DIR); my $total = 0; foreach my $line (@maildirs) { foreach my $subdir ("/","/cur","/new","/tmp") { opendir (my $DIR, "$line$subdir"); while (my $file = readdir($DIR)) { if ((-f "$line$subdir/$file") and ($file =~ /^\d+\./)) { print ". "; unlink ("$line$subdir/$file"); $total++; } } closedir ($DIR); } if (-e "$line$subdir/maildirsize") {unlink "$line$subdir/maildirsize"} } print "

Total emails removed: $total

\n"; print "

\n"; } elsif ($FORM{action} eq "view") { my %userdomains; open (my $IN,"<","/etc/userdomains"); flock ($IN, LOCK_SH); my @localusers = <$IN>; close ($IN); chomp @localusers; foreach my $line (@localusers) { my ($domain,$user) = split(/\: /,$line,2); $userdomains{$domain} = $user; } my $dir; my $title; if ($FORM{top}) { my $homedir = ( getpwnam($FORM{account}) )[7]; $dir = "$homedir/mail"; $title = "$FORM{account} (cPanel user)"; } else { my $homedir = ( getpwnam($userdomains{$FORM{domain}}) )[7]; $dir = "$homedir/mail/$FORM{domain}/$FORM{account}"; $title = "$FORM{account}\@$FORM{domain}"; } my $topdir = $FORM{topdir}; if ($topdir eq "") {$topdir = "/"} print "\n"; print "
\n"; if ($topdir eq "/") { print "\n"; print ""; print "\n"; } else { my $showdir = "$topdir"; $showdir =~ s/\/+/\//g; print "\n"; print "\n"; print "
$title
 DirectoryMail CountSize
\n"; print ""; print "\n"; print "\n"; } my @maildirs; push @maildirs, "/"; opendir (my $DIR, $dir); while (my $file = readdir($DIR)) { if ($file eq ".") {next} if ($file eq "..") {next} if (readlink "$dir/$file") {next} if ((-d "$dir/$file") and (-d "$dir/$file/cur") and (-d "$dir/$file/new") and (-d "$dir/$file/tmp")) {push @maildirs, "/$file"} } closedir ($DIR); my $total = 0; my $class = "tdshade2_noborder"; foreach my $line (@maildirs) { foreach my $subdir ("/cur","/new","/tmp") { opendir (my $DIR, "$dir$line$subdir"); my @files = readdir($DIR); closedir ($DIR); my $dirtot = 0; my $dirsize = 0; foreach my $file (sort @files) { if ((-f "$dir$line$subdir/$file") and ($file =~ /^(\d+)\./)) { if ("$line$subdir" ne $topdir) { $dirtot++; $dirsize += (stat("$dir$line$subdir/$file"))[7]; next; } my $date = $1; my @mail; open (my $IN, "<", "$dir$line$subdir/$file"); flock ($IN, LOCK_SH); for (my $x = 0; $x < 200;$x++) { my $line = <$IN>; if ($line eq "\n") {last;} push (@mail, $line); } close ($IN); chomp @mail; my @tmp = grep {$_ =~ /^subject:/i} @mail; my $subject; if ($tmp[0]) {$subject = $tmp[0]} if (length($subject) > 50) {$subject = substr($subject,0,47)."..."} $subject =~ s/subject://ig; $subject =~ s/>/>/g; $subject =~ s/"; print ""; print "\n"; print "\n"; } } if (($topdir eq "/") and ($dirtot > 0)) { my $showdir = "$line/$subdir"; $showdir =~ s/\/+/\//g; my $emptydir = ""; if ($mailscanner) {$emptydir .= " "} $used = $dirsize / (1024 * 1024); if ($used == 0) {$emptydir = " "} $used = sprintf("%.02f",$used); print "\n"; } } } if ($topdir ne "/") { print "\n"; print "\n"; print "
$title
$showdir "; if ($mailscanner) {print " "} print "
\#DelSubjectDateSize 
$total$subject$date$size KB
$emptydir$showdirView $dirtot emails$used MB
 
All
  
\n"; print ""; } else { print "\n"; } print "
\n"; print "

\n"; print "\n"; print "\n"; } elsif ($FORM{action} eq "deletemail") { if (-f $FORM{file}) { my %userdomains; open (my $IN,"<","/etc/userdomains"); flock ($IN, LOCK_SH); my @localusers = <$IN>; close ($IN); chomp @localusers; foreach my $line (@localusers) { my ($domain,$user) = split(/\: /,$line,2); $userdomains{$domain} = $user; } my ($file, $filedir) = fileparse($FORM{file}); my $homedir = ( getpwnam($userdomains{$FORM{domain}}) )[7]; if ($homedir eq "" or $filedir !~ /^$homedir/) { print "Invalid file [$FORM{file}]"; } else { print "\n"; print ""; unlink $FORM{file}; my ($file, $filedir) = fileparse($FORM{file}); if (-e "$filedir/../maildirsize") {unlink "$filedir/../maildirsize"} print "\n"; print "
Delete Email
Email deleted
\n"; } } else { print "File [$FORM{file}] not found"; } } elsif ($FORM{action} eq "emptydir") { if (-d $FORM{file}) { my %userdomains; open (my $IN,"<","/etc/userdomains"); flock ($IN, LOCK_SH); my @localusers = <$IN>; close ($IN); chomp @localusers; chomp @localusers; foreach my $line (@localusers) { my ($domain,$user) = split(/\: /,$line,2); $userdomains{$domain} = $user; } my ($file, $filedir) = fileparse($FORM{file}); my $homedir = ( getpwnam($userdomains{$FORM{domain}}) )[7]; if ($homedir eq "" or $filedir !~ /^$homedir/) { print "Invalid directory [$FORM{file}]"; } else { my $total = 0; print "\n"; print ""; print "\n"; print "\n"; print "
Empty Directory [$FORM{file}]
"; opendir (my $DIR, $FORM{file}); while (my $file = readdir($DIR)) { if (readlink "$FORM{file}/$file") {next} if ((-f "$FORM{file}/$file") and ($file =~ /^\d+\./)) { print ". "; unlink ("$FORM{file}/$file"); $total++; } } if (-e "$FORM{file}/../maildirsize") {unlink "$FORM{file}/../maildirsize"} closedir ($DIR); print "

Total emails removed: $total

Directory emptied
\n"; } } else { print "Directory [$FORM{file}] not found"; } } elsif ($FORM{action} eq "salearn") { if (-d $FORM{file}) { my %userdomains; open (my $IN,"<","/etc/userdomains"); flock ($IN, LOCK_SH); my @localusers = <$IN>; close ($IN); chomp @localusers; foreach my $line (@localusers) { my ($domain,$user) = split(/\: /,$line,2); $userdomains{$domain} = $user; } my ($file, $filedir) = fileparse($FORM{file}); my $homedir = ( getpwnam($userdomains{$FORM{domain}}) )[7]; if ($homedir eq "" or $filedir !~ /^$homedir/) { print "Invalid file [$FORM{file}]"; } else { $| = 1; ## no critic print "\n"; print ""; print "\n"; print "
Running sa-learn for spam against [$FORM{file}]

This may take some time depending on the number of emails and the speed of SpamAssassin:

\n
\n\# /usr/local/cpanel/3rdparty/bin/sa-learn --spam --showdots $FORM{file}\n";
			my ($childin, $childout);
			my $cmdpid = open3($childin, $childout, $childout, "/usr/local/cpanel/3rdparty/bin/sa-learn", "--spam", "--showdots", $FORM{file});
			while (<$childout>) {print $_}
			waitpid ($cmdpid, 0);
			print "
\n
\n"; print "\n"; } } else { print "File [$FORM{file}] not found"; } } elsif ($FORM{action} eq "bulkdelete") { my $total = 0; my $anyfile; my %userdomains; open (my $IN,"<","/etc/userdomains"); flock ($IN, LOCK_SH); my @localusers = <$IN>; close ($IN); chomp @localusers; foreach my $line (@localusers) { my ($domain,$user) = split(/\: /,$line,2); $userdomains{$domain} = $user; } print "\n"; print ""; print "\n"; print "
Delete Selected Mails
"; for ($x = 1; $x <= $FORM{total} ;$x++) { my $delfile = $FORM{"cmmdel_$x"}; if (-f $delfile) { my ($file, $filedir) = fileparse($delfile); my $homedir = ( getpwnam($userdomains{$FORM{domain}}) )[7]; if ($homedir eq "" or $filedir !~ /^$homedir/) { print "Invalid file [$delfile]"; } else { unlink ($delfile); $total++; $anyfile = $delfile; } } } my ($file, $filedir) = fileparse($anyfile); if (-d $filedir) { my $homedir = ( getpwnam($userdomains{$FORM{domain}}) )[7]; if ($homedir eq "" or $filedir !~ /^$homedir/) { print "Invalid directory [$filedir]"; } else { if (-e "$filedir/../maildirsize") {unlink "$filedir/../maildirsize"} } } print "

Total emails removed: $total

\n"; print "

\n"; } elsif ($FORM{action} eq "changequota") { my %userdomains; open (my $IN,"<","/etc/userdomains"); flock ($IN, LOCK_SH); my @localusers = <$IN>; close ($IN); chomp @localusers; foreach my $line (@localusers) { my ($domain,$user) = split(/\: /,$line,2); $userdomains{$domain} = $user; } if (($FORM{quota} =~ /[^\d\.]/) or ($FORM{quota} == 0)) {$FORM{quota} = "unlimited"} print "\n"; print ""; print "\n"; print "
Change Quota for $FORM{account}\@$FORM{domain}
"; eval { local $) = local $(; ## no critic local $> = local $<; ## no critic local $ENV{'REMOTE_USER'} = $user; &drop($userdomains{$FORM{domain}}); my ($childin, $childout); my $cmdpid = open3($childin, $childout, $childout, "/usr/local/cpanel/cpanel-email", "editquota", $FORM{account}, $FORM{domain}, $FORM{quota}); my @data = <$childout>; waitpid ($cmdpid, 0); chomp @data; my $cnt = 0; foreach my $line (@data) { if ($line =~ /^stdin: is not a tty/) {next} if ($line =~ /^[\r\n]/) {next} if ($line =~ /^
/) {next} print "
$line
\n"; $cnt++; } unless ($cnt) {print "

Quota changed to $FORM{quota} MB

\n"} }; print "
\n"; print "

\n"; } elsif ($FORM{action} eq "password") { print "
\n"; print "\n"; print ""; print "\n"; print "\n"; print "\n"; print "
Change Password for $FORM{account}\@$FORM{domain}
New password
Confirm password
\n"; print "
\n"; print "

\n"; } elsif ($FORM{action} eq "Change Password") { my %userdomains; open (my $IN,"<","/etc/userdomains"); flock ($IN, LOCK_SH); my @localusers = <$IN>; close ($IN); chomp @localusers; foreach my $line (@localusers) { my ($domain,$user) = split(/\: /,$line,2); $userdomains{$domain} = $user; } print "\n"; print ""; if ($FORM{password} eq "") { print "\n"; } elsif ($FORM{password} ne $FORM{confirmpassword}) { print "\n"; } elsif ($FORM{password} =~ /\"/) { print "\n"; } else { print "
Change Password for $FORM{account}\@$FORM{domain}
Failed: Empty password field
Failed: Passwords do not match
Failed: password must not contain quotes
\n"; eval { local $) = local $(; ## no critic local $> = local $<; ## no critic local $ENV{'REMOTE_USER'} = $user; &drop($userdomains{$FORM{domain}}); my ($childin, $childout); my $cmdpid = open3($childin, $childout, $childout, "/usr/local/cpanel/cpanel-email", "passwdpop", $FORM{account}, "$FORM{password}", "0", $FORM{domain}); my @data = <$childout>; waitpid ($cmdpid, 0); chomp @data; my $cnt = 0; foreach my $line (@data) { if ($line =~ /^stdin: is not a tty/) {next} print "
$line
\n"; $cnt++; } unless ($cnt) {print "

Password changed

\n"} }; } print "
\n"; print "

\n"; } elsif ($FORM{action} eq "delete") { print "
\n"; print "\n"; print ""; print "\n"; print "\n"; print "
Delete Mailbox $FORM{account}\@$FORM{domain}
Are you sure that you want to irretrievably delete all email and associated files within this mailbox?
\n"; print "
\n"; print "

\n"; } elsif ($FORM{action} eq "Delete Mailbox") { my %userdomains; open (my $IN,"<","/etc/userdomains"); flock ($IN, LOCK_SH); my @localusers = <$IN>; close ($IN); chomp @localusers; foreach my $line (@localusers) { my ($domain,$user) = split(/\: /,$line,2); $userdomains{$domain} = $user; } print "\n"; print ""; print "
Delete Mailbox $FORM{account}\@$FORM{domain}
\n"; eval { local $) = local $(; ## no critic local $> = local $<; ## no critic local $ENV{'REMOTE_USER'} = $user; &drop($userdomains{$FORM{domain}}); my ($childin, $childout); my $cmdpid = open3($childin, $childout, $childout, "/usr/local/cpanel/cpanel-email", "delpop", $FORM{account}, "0", $FORM{domain}); my @data = <$childout>; waitpid ($cmdpid, 0); chomp @data; my $cnt = 0; foreach my $line (@data) { if ($line =~ /^stdin: is not a tty/) {next} print "
$line
\n"; $cnt++; } print "

Account deleted

\n"; }; print "
\n"; print "

\n"; } elsif ($FORM{action} eq "Add Mailbox") { my %userdomains; open (my $IN,"<","/etc/userdomains"); flock ($IN, LOCK_SH); my @localusers = <$IN>; close ($IN); chomp @localusers; foreach my $line (@localusers) { my ($domain,$user) = split(/\: /,$line,2); $userdomains{$domain} = $user; } if (($FORM{quota} =~ /[^\d\.]/) or ($FORM{quota} == 0)) {$FORM{quota} = "unlimited"} print "\n"; print ""; if ($FORM{password} eq "") { print "\n"; } elsif ($FORM{password} ne $FORM{confirmpassword}) { print "\n"; } elsif ($FORM{password} =~ /\"/) { print "\n"; } else { print "\n"; } print "
Add Mailbox $FORM{account}\@$FORM{domain}
Failed: Empty password field
Failed: Passwords do not match
Failed: password must not contain quotes
\n"; eval { local $) = local $(; ## no critic local $> = local $<; ## no critic local $ENV{'REMOTE_USER'} = $user; &drop($userdomains{$FORM{domain}}); my ($childin, $childout); my $cmdpid = open3($childin, $childout, $childout, "/usr/local/cpanel/cpanel-email", "addpop", $FORM{account}, "$FORM{password}", $FORM{quota}, $FORM{domain}); my @data = <$childout>; waitpid ($cmdpid, 0); chomp @data; my $cnt = 0; foreach my $line (@data) { if ($line =~ /^stdin: is not a tty/) {next} print "
$line
\n"; $cnt++; } print "

Account created

\n"; }; print "
\n"; print "

\n"; } elsif (($FORM{action} =~ /^Mail Quota Report \((.*)\)/) or ($FORM{action} eq "Manage Mail Accounts")) { my $report = $1; my $total = 0; my $colspan = 5; my $extracol = " "; my ($tot_accounts, $tot_mails, $tot_space); if ($FORM{action} eq "Manage Mail Accounts") { $FORM{dospace} = 0; $FORM{dopercent} = 1; $FORM{percent} = 99; $FORM{doall} = 1; $report = "Selected"; $colspan = 5; $extracol = "Action"; print "

    

\n"; } print "\n"; print ""; print "$extracol"; my @users; my %userdomains; open (my $IN,"<","/etc/userdomains"); flock ($IN, LOCK_SH); my @localusers = <$IN>; close ($IN); chomp @localusers; foreach my $line (@localusers) { my ($domain,$user) = split(/\: /,$line,2); $userdomains{$domain} = $user; } my @domains; if ($report eq "Selected") { push @domains, $FORM{domain}; } else { open (my $IN,"<","/etc/userdomains"); flock ($IN, LOCK_SH); while (my $entry = <$IN>) { chomp $entry; my ($domain,$user) = split(/:\s*/,$entry); if ($user eq "nobody") {next} push @domains, $domain; } close ($IN); @domains = sort @domains; } foreach my $domain (@domains) { unless ($domain) {next} if ($domain =~ /^\#/) {next} my $homedir = ( getpwnam($userdomains{$domain}) )[7]; unless (-e "$homedir/etc/$domain/passwd") {next} # unless (-e "$homedir/etc/$domain/quota") {next} my @accounts; open (my $IN,"<","$homedir/etc/$domain/passwd"); flock ($IN, LOCK_SH); my @localusers = <$IN>; close ($IN); chomp @localusers; foreach my $line (@localusers) { ($user,undef) = split(/\:/,$line,2); if ($user) {push @accounts,$user} } @accounts = sort @accounts; unshift @accounts,$userdomains{$domain}; open (my $QUOTA,"<","$homedir/etc/$domain/quota"); flock ($QUOTA, LOCK_SH); my @localquota = <$QUOTA>; close ($QUOTA); chomp @localquota; my %quotas; foreach my $line (@localquota) { my ($user,$quota) = split(/\:/,$line,2); $quotas{$user} = $quota; } my $first = 1; my $lines = 0; foreach my $key (@accounts) { my $dir; my $topdomain = "\@$domain"; my $account = "${key}\@${domain}"; my $quota = $quotas{$key}; my $used = 0; my $files = 0; my $mdbox = 0; if ($first == -1) {$first = 0} if ($first) { $first = -1; open (my $IN,"<","/var/cpanel/users/$userdomains{$domain}"); my @userdata = <$IN>; close ($IN); chomp @userdata; my $maindomain; foreach my $line (@userdata) { if ($line =~ /^DNS=(.*)/) {$maindomain = $1} } if ($maindomain ne $domain) {next} $dir = "$homedir/mail"; $topdomain = " ($domain cPanel user)"; $account = "${key}"; $quota = 0; } else { $dir = "$homedir/mail/$domain/$key"; } if (-e "$dir/storage") { $mdbox = 1; $topdomain .= " mdbox"; my ($childin, $childout); my $cmdpid = open3($childin, $childout, $childout, "/usr/bin/doveadm", "mailbox", "status", "-u", "$account", "-t", "messages vsize", "*"); my @data = <$childout>; waitpid ($cmdpid, 0); chomp @data; if ($data[0] =~ /messages=(\d+)/) {$files = $1} if ($data[0] =~ /vsize=(\d+)/) {$used = $1} } else { my @maildirs; push @maildirs, $dir; opendir (my $DIR, $dir); while (my $file = readdir($DIR)) { if ($file eq ".") {next} if ($file eq "..") {next} if (readlink "$dir/$file") {next} if ((-d "$dir/$file") and (-d "$dir/$file/cur") and (-d "$dir/$file/new") and (-d "$dir/$file/tmp")) {push @maildirs, "$dir/$file"} } closedir ($DIR); if (-e "$dir/storage") { $mdbox = 1; } else { foreach my $line (@maildirs) { foreach my $subdir ("/","/cur","/new","/tmp") { opendir (my $DIR, "$line$subdir"); while (my $file = readdir($DIR)) { if ((-f "$line$subdir/$file") and ($file =~ /^\d+\./)) { $used += (stat("$line$subdir/$file"))[7]; $files++; } } closedir ($DIR); } } } } my $uclass = ""; if (($FORM{dospace}) and ($used > $FORM{space} * 1024 * 1024)) {$uclass = "warning"} if (($FORM{dopercent}) and (($used >= $quota) or ($used / $quota >= ($FORM{percent} / 100))) and ($quota > 0)) {$uclass = "danger"} unless ($FORM{doall}) { if ($uclass eq "") {next} if ($uclass eq "") {next} } my $uquota = 0; if ($quota > 0) {$uquota = int(($used / $quota) * 100)} $used = $used / (1024 * 1024); my $tused = $used; $used = sprintf("%.02f",$used); $used .= " ($uquota\%)"; if ($quota < 1) { $quota = "unlimited"; } else { $quota = int($quota / (1024 * 1024)); } if ($FORM{action} eq "Manage Mail Accounts") { $tot_accounts++; $tot_mails+=$files; $tot_space+=$tused; print "\n"; } else { print "\n"; } if ($class eq "tdshade2_noborder") {$class = "tdshade1_noborder"} else {$class = "tdshade2_noborder"} $total++; $lines++; } my $info = ""; $tot_space = sprintf("%.02f",$tot_space); if ($FORM{action} eq "Manage Mail Accounts") {$info = ""} if ($FORM{doall} and $lines) {print "$info\n"} } print "
Mail Account Quotas
AccountMailsUsed (MB)Quota (MB)
$key$topdomain$files$used"; if ($first == -1) { print "$quota"; if ($files > 0 and !$mdbox) { print " "; print " "; } } else { print ""; print " "; if ($files > 0 and !$mdbox) { print " "; print " "; } else {print "";} print " "; } print "
$key$topdomain$files$used$quota\n"; print "
 $tot_accounts account(s)$tot_mails$tot_space 
\n"; if ($FORM{action} eq "Manage Mail Accounts") { print "
\n"; print "\n"; print "\n"; print ""; my ($childin, $childout); my $cmdpid = open3($childin, $childout, $childout, "/usr/bin/quota", "-qlu", $userdomains{$FORM{domain}}); my @data = <$childout>; waitpid ($cmdpid, 0); chomp @data; if ($data[0] =~ /Block limit reached/) { print "\n"; } else { print "\n"; print "\n"; print "\n"; print "\n"; print "\n"; } print "
Add Mailbox

cPanel account over quota - add mailbox disabled

Account\@$FORM{domain}
Password
Confirm
QuotaMB
\n"; print "
\n"; print ""; print "\n"; print "\n"; print "\n"; print "\n"; print "\n"; if ($mailscanner) {print "\n"} print "
Button Key
Change mailbox quota
Change mailbox password
View mailbox contents
Empty mailbox/Directory
Delete mailbox/Mail
sa-learn spam Directory
\n"; print "
\n"; } print "
Total Accounts: $total
\n"; print "
mdbox - Mailboxes using the cPanel v11.58+ mdbox format have limited support in this script
\n"; print "

\n"; } elsif ($FORM{action} eq "Bulk Enable GreyListing") { require Cpanel::GreyList::Client; print "\n"; print "\n"; print "
Enabled GreyListing on all domains
\n"; my @localdomains; open (my $IN,"<","/etc/userdomains"); flock ($IN, LOCK_SH); while (my $entry = <$IN>) { chomp $entry; my ($domain,$user) = split(/:\s*/,$entry); if ($user eq "nobody") {next} if ($domain eq "") {next} if ($domain eq "*") {next} push @localdomains, $domain; } close($IN); my $client = Cpanel::GreyList::Client->new(); $client->disable_opt_out_for_domains(\@localdomains); print "

\n"; } elsif ($FORM{action} eq "Bulk Show GreyListing") { require Cpanel::GreyList::Client; print "\n"; my @localdomains; open (my $IN,"<","/etc/userdomains"); flock ($IN, LOCK_SH); my @entries = <$IN>; close ($IN); chomp @entries; foreach my $entry (sort @entries) { chomp $entry; my ($domain,$user) = split(/:\s*/,$entry); if ($user eq "nobody") {next} if ($domain eq "") {next} if ($domain eq "*") {next} my $client = Cpanel::GreyList::Client->new(); if ($client->is_greylisting_enabled($domain)) { print "\n"; } else { print "\n"; } } print "
GreyListing is enabled for $domain
GreyListing is disabled for $domain
\n"; print "

\n"; } elsif ($FORM{action} eq "Bulk Disable GreyListing") { require Cpanel::GreyList::Client; print "\n"; print "\n"; print "
Disabled GreyListing on all domains
\n"; my @localdomains; open (my $IN,"<","/etc/userdomains"); flock ($IN, LOCK_SH); while (my $entry = <$IN>) { chomp $entry; my ($domain,$user) = split(/:\s*/,$entry); if ($user eq "nobody") {next} if ($domain eq "") {next} if ($domain eq "*") {next} push @localdomains, $domain; } close ($IN); my $client = Cpanel::GreyList::Client->new(); $client->enable_opt_out_for_domains(\@localdomains); print "

\n"; } elsif ($FORM{action} eq "Show GreyListing") { unless ($FORM{domain}) { print "

You must select a domain!

\n"; } else { require Cpanel::GreyList::Client; print "\n"; my $client = Cpanel::GreyList::Client->new(); if ($client->is_greylisting_enabled($FORM{domain})) { print "\n"; } else { print "\n"; } print "
GreyListing is enabled for $FORM{domain}
GreyListing is disabled for $FORM{domain}
\n"; } print "

\n"; } elsif ($FORM{action} eq "Enable GreyListing") { unless ($FORM{domain}) { print "

You must select a domain!

\n"; } else { require Cpanel::GreyList::Client; print "\n"; print "\n"; print "
Enabled GreyListing on $FORM{domain}
\n"; my @domains; my $client = Cpanel::GreyList::Client->new(); @domains = ($FORM{domain}); $client->disable_opt_out_for_domains(\@domains); } print "

\n"; } elsif ($FORM{action} eq "Disable GreyListing") { unless ($FORM{domain}) { print "

You must select a domain!

\n"; } else { require Cpanel::GreyList::Client; print "\n"; print "\n"; print "
Disabled GreyListing on $FORM{domain}
\n"; my @domains; my $client = Cpanel::GreyList::Client->new(); @domains = ($FORM{domain}); $client->enable_opt_out_for_domains(\@domains); } print "

\n"; } elsif ($FORM{action} eq "upgrade") { $| = 1; ## no critic print "
";

	if (-e "/usr/src/cmm.tgz") {unlink ("/usr/src/cmm.tgz") or die $!}
	print "Retrieving new cmm package...\n";

	my ($status, $text) = &urlget("https://$downloadserver/cmm.tgz","/usr/src/cmm.tgz");
	if ($status) {print "Oops: $text\n"}

	if (! -z "/usr/src/cmm.tgz") {
		print "Unpacking new cmm package...\n";
		print "
";
		&printcmd("cd /usr/src ; tar -xzf cmm.tgz ; cd cmm ; sh install.sh 2>&1");
		print "
"; print "Tidying up...\n"; print "
";
		&printcmd("rm -Rfv /usr/src/cmm*");
		print "
"; print "...All done.\n"; } print "
"; open (my $IN, "<",$versionfile) or die $!; flock ($IN, LOCK_SH); $myv = <$IN>; close ($IN); chomp $myv; print "

\n"; } else { open (my $IN, "<","/etc/userdomains"); flock ($IN, LOCK_SH); while (my $entry = <$IN>) { chomp $entry; my ($domain,$user) = split(/:\s*/,$entry); if ($user eq "nobody") {next} push @localdomains, $domain; } close ($IN); @localdomains = sort @localdomains; my $domainlist; my $domaincnt; foreach my $domain (@localdomains) { $domainlist .= "\n"; $domaincnt ++; } print "
\n"; print "\n"; print ""; print "\n"; print ""; print "\n"; print "\n"; print "\n"; print "\n"; print "
Mail Manage
\n"; print "

\n"; print "

\n"; print "

\n"; print "

\n"; if (-e "/var/cpanel/greylist/enabled") { print "

\n"; print "

\n"; print "

WARNING: If you disable GreyListing on a main domain cPanel forces all subdomains to be disabled until the main domain is enabled

\n"; } print "

Mail Domains: $domaincnt

\n"; print "
Mail Reports
\n"; print " Identify accounts using over MB of mailbox space
\n"; print " Identify accounts using within or over % of the mailbox quota
\n"; print " Show all accounts\n"; print "

Note: These reports may take some time to run

View email account usage for the domain selected above
View email account usage for all domains
 
\n"; print "
mdbox - Mailboxes using the cPanel v11.58+ mdbox format have limited support in this script
\n"; if (-e "/var/cpanel/greylist/enabled") { print "\n"; print ""; print "\n"; print "\n"; print "\n"; print "
GreyListing
Display GreyListing for all domains
Configure GreyListing so that all domains are enabled
Configure GreyListing so that all domains are disabled
\n"; print "

WARNING: Using GreyListing can and will lead to lost legitimate emails. It can also cause significant problems with Password Verification systems. See this article for more information

\n"; } my $retry = 0; my $retrytime = 300; print "\n"; print ""; if (-e "/usr/local/cpanel/whostmgr/docroot/cgi/cmmnocheck") { open (my $IN, "<", "/usr/local/cpanel/whostmgr/docroot/cgi/cmmnocheck"); flock ($IN, LOCK_SH); my $time = <$IN>; close ($IN); chomp $time; $retry = time - $time; if ($retry > $retrytime) {unlink ("/usr/local/cpanel/whostmgr/docroot/cgi/cmmnocheck")} } unless (-e "/usr/local/cpanel/whostmgr/docroot/cgi/cmmnocheck") { my ($status, $text) = &urlget("https://$downloadserver/cmm/cmmversion.txt"); my $actv = $text; my $up = 0; if ($actv ne "") { if ($actv =~ /^[\d\.]*$/) { if ($actv > $myv) { print "\n"; } else { print "\n"; } $up = 1; } } unless ($up) { sysopen (my $OUT, "/usr/local/cpanel/whostmgr/docroot/cgi/cmmnocheck", O_WRONLY | O_CREAT); flock ($OUT, LOCK_EX); print $OUT time; close ($OUT); print "\n"; } } else { print "\n"; } print "
Upgrade
A new version of cmm (v$actv) is available. Upgrading will retain your settings
View ChangeLog
You are running the latest version of cmm.
An Upgrade button will appear here if a new version becomes available
Unable to connect to http://www.configserver.com, retry in $retrytime seconds.
An Upgrade button will appear here if new version is detected
Unable to connect to http://www.configserver.com, retry in ".($retrytime - $retry)." seconds.
An Upgrade button will appear here if new version is detected
\n"; } print "
cmm: v$myv
"; print "

©2006-2019, ConfigServer Services (Jonathan Michaelson)

\n"; print < \$("#loader").hide(); \$("#docs-link").hide(); EOF unless ($FORM{action} eq "viewmail") { close SCRIPTOUT; select STDOUT; Cpanel::Template::process_template( 'whostmgr', { "template_file" => "${thisapp}.tmpl", "${thisapp}_output" => $templatehtml, "print" => 1, } ); } else { print "\n"; print "\n"; print "\n"; } # end main ############################################################################### # start drop sub drop { my $user = shift; my (undef,undef,$uid,$gid,undef,undef,undef,$home) = getpwnam($user); if ($> == 0) { chdir($home); $) = $( = $gid; ## no critic $> = $< = $uid; ## no critic if (($) != $gid) or ($> != $uid) or ($( != $gid) or ($< != $uid)) {print "Failed to drop privileges ($uid:$gid)\n";exit} ## no critic $ENV{'REMOTE_USER'} = $user; ## no critic } return } # end drop ############################################################################### sub uri_escape { my $string = shift; $string =~ s/([^^A-Za-z0-9\-_.!~*'()])/ sprintf "%%%0x", ord $1 /eg; return $string; } ############################################################################### # start urlget (v1.3) # # Examples: #my ($status, $text) = &urlget("http://prdownloads.sourceforge.net/clamav/clamav-0.92.tar.gz","/tmp/clam.tgz"); #if ($status) {print "Oops: $text\n"} # #my ($status, $text) = &urlget("http://www.configserver.com/free/msfeversion.txt"); #if ($status) {print "Oops: $text\n"} else {print "Version: $text\n"} # sub urlget { my $url = shift; my $file = shift; my $status = 0; my $timeout = 1200; local $SIG{PIPE} = 'IGNORE'; use LWP::UserAgent; my $ua = LWP::UserAgent->new; $ua->timeout(30); my $req = HTTP::Request->new(GET => $url); my $res; my $text; ($status, $text) = eval { local $SIG{__DIE__} = undef; local $SIG{'ALRM'} = sub {die "Download timeout after $timeout seconds"}; alarm($timeout); if ($file) { $|=1; ## no critic my $expected_length; my $bytes_received = 0; my $per = 0; my $oldper = 0; open (my $OUT, ">", "$file\.tmp") or return (1, "Unable to open $file\.tmp: $!"); binmode ($OUT); print "...0\%\n"; $res = $ua->request($req, sub { my($chunk, $res) = @_; $bytes_received += length($chunk); unless (defined $expected_length) {$expected_length = $res->content_length || 0} if ($expected_length) { my $per = int(100 * $bytes_received / $expected_length); if ((int($per / 5) == $per / 5) and ($per != $oldper)) { print "...$per\%\n"; $oldper = $per; } } else { print "."; } print $OUT $chunk; }); close ($OUT); print "\n"; } else { $res = $ua->request($req); } alarm(0); if ($res->is_success) { if ($file) { rename ("$file\.tmp","$file") or return (1, "Unable to rename $file\.tmp to $file: $!"); return (0, $file); } else { return (0, $res->content); } } else { return (1, "Unable to download: ".$res->message); } }; alarm(0); if ($@) { return (1, $@); } if ($text) { return ($status,$text); } else { return (1, "Download timeout after $timeout seconds"); } } # end urlget ############################################################################### ## start printcmd sub printcmd { my @command = @_; my ($childin, $childout); my $pid = open3($childin, $childout, $childout, @command); while (<$childout>) {print $_} waitpid ($pid, 0); return; } ## end printcmd ############################################################################### ## start getdownloadserver sub getdownloadserver { my @servers; my $downloadservers = "/usr/local/cpanel/whostmgr/docroot/cgi/configserver/cmm/downloadservers"; my $chosen; if (-e $downloadservers) { open (my $DOWNLOAD, "<", $downloadservers); flock ($DOWNLOAD, LOCK_SH); my @data = <$DOWNLOAD>; close ($DOWNLOAD); chomp @data; foreach my $line (@data) { if ($line =~ /^download/) {push @servers, $line} } ## foreach my $line (slurp($downloadservers)) { ## $line =~ s/$cleanreg//g; ## if ($line =~ /^download/) {push @servers, $line} ## } $chosen = $servers[rand @servers]; } if ($chosen eq "") {$chosen = "download.configserver.com"} return $chosen; } ## end getdownloadserver ############################################################################### 1;