#!/usr/local/cpanel/3rdparty/bin/perl
# cpanel - scripts/dovecot_maintenance Copyright 2022 cPanel, L.L.C.
# All rights reserved.
# copyright@cpanel.net http://cpanel.net
# This code is subject to the cPanel license. Unauthorized copying is prohibited
package scripts::dovecot_maintenance;
=pod
=head1 NAME
dovecot_maintenance - Run nightly maintenance for dovecot which includes
purging deleted messages from mdbox.
=head1 SYNOPSIS
/usr/local/cpanel/scripts/dovecot_maintenance [options]
Options:
--help This help message
--background Run in the background
=head1 DESCRIPTION
All deleted email will be purged from mdbox users
who have logged in since this script was last run.
This program will also purge all expired APNs
registrations
=cut
use strict;
use Cpanel::IONice ();
use Cpanel::PwCache ();
use Cpanel::PwCache::Build ();
use Cpanel::Config::LoadCpConf ();
use Cpanel::Config::LoadConfig ();
use Cpanel::ConfigFiles ();
use Cpanel::Dovecot ();
use Cpanel::Dovecot::Utils ();
use Cpanel::AdvConfig ();
use Cpanel::Locale ();
use Cpanel::AcctUtils::DomainOwner::Tiny ();
use Cpanel::AcctUtils::Lookup ();
use Cpanel::FileUtils::Open ();
use Cpanel::Email::Exists ();
use Cpanel::FileUtils::Dir ();
use Cpanel::SQLite::Compat ();
use DBD::SQLite ();
use Cpanel::DBI::SQLite ();
use Cpanel::APNS::Mail::DB ();
use File::Path ();
use Getopt::Long ();
use Pod::Usage ();
use Umask::Local ();
use Try::Tiny;
our $DAYS_TO_KEEP_APNS_REGISTRATIONS = 7;
my $background = 0;
my $help = 0;
unless ( caller() ) {
Getopt::Long::GetOptions( 'background' => \$background, 'help' => \$help );
Pod::Usage::pod2usage( -verbose => 2 ) if $help;
if ($background) {
require Cpanel::Daemonizer::Tiny;
my $pid = Cpanel::Daemonizer::Tiny::run_as_daemon(
sub {
####
# The next two calls are unchecked because it cannot be captured when running as a daemon
Cpanel::FileUtils::Open::sysopen_with_real_perms( \*STDERR, $Cpanel::ConfigFiles::CPANEL_ROOT . '/logs/error_log', 'O_WRONLY|O_APPEND|O_CREAT', 0600 );
open( STDOUT, '>&', \*STDERR ) || warn "Failed to redirect STDOUT to STDERR";
exit( __PACKAGE__->script() );
}
);
}
else {
exit( __PACKAGE__->script() );
}
}
our $DEFAULT_IO_NICE = 7;
sub script {
my ($class) = @_;
my $self = bless {}, $class;
$self->_init();
local $| = 1;
my $exit_status = 0;
# Order matters since for mdbox expunge will only mark it for purge
foreach my $op (qw(_purge_deleted_messages _purge_expired_xaps_registrations)) {
try {
$self->$op();
}
catch {
warn $_;
$exit_status = 1;
};
}
return $exit_status;
}
sub _init {
my ($self) = @_;
$self->{'mailbox_formats'} = scalar Cpanel::Config::LoadConfig::loadConfig( "/etc/mailbox_formats", undef, ": " );
$self->{'dovecot_conf'} = Cpanel::AdvConfig::load_app_conf('dovecot');
Cpanel::AcctUtils::DomainOwner::Tiny::build_domain_cache();
Cpanel::PwCache::Build::init_passwdless_pwcache();
return;
}
sub _ionice {
my ($self) = @_;
return if $self->{'did_ionice'};
$self->{'did_ionice'} = 1;
my $cpconf_ref = Cpanel::Config::LoadCpConf::loadcpconf();
if ( Cpanel::IONice::ionice( 'best-effort', exists $cpconf_ref->{'ionice_dovecot_maintenance'} ? $cpconf_ref->{'ionice_dovecot_maintenance'} : $$DEFAULT_IO_NICE ) ) {
print "[dovecot_maintenance] Setting I/O priority to reduce system load: " . Cpanel::IONice::get_ionice() . "\n";
}
return 1;
}
sub _purge_deleted_messages {
my ($self) = @_;
return if !-d $Cpanel::Dovecot::LASTLOGIN_DIR; # may not be created yet
my $nodes_ar = Cpanel::FileUtils::Dir::get_directory_nodes($Cpanel::Dovecot::LASTLOGIN_DIR);
my $locale = $self->_locale();
foreach my $username (@$nodes_ar) {
if ( index( $username, q{__cpanel__service__auth__} ) == -1 && $self->_has_mdbox($username) ) {
$self->_ionice();
print $locale->maketext( "Purging deleted messages for “[_1]” …", $username );
Cpanel::Dovecot::Utils::purge($username);
print $locale->maketext("Done") . "\n";
}
if ( -d "$Cpanel::Dovecot::LASTLOGIN_DIR/$username" ) {
# Handle user/sent logins
try {
File::Path::rmtree("$Cpanel::Dovecot::LASTLOGIN_DIR/$username");
}
catch {
local $@ = $_;
warn;
};
}
else {
# Handle normal logins
unlink("$Cpanel::Dovecot::LASTLOGIN_DIR/$username");
}
}
return 1;
}
sub _locale {
my ($self) = @_;
return ( $self->{'locale'} ||= Cpanel::Locale->get_handle() );
}
sub _has_mdbox {
my ( $self, $username ) = @_;
my $system_user;
# get_system_user generates an exception when the user or the
# domain does not exist. UserNotFound/DomainDoesNotExist.
#
# anything else is a fail
try {
$system_user = Cpanel::AcctUtils::Lookup::get_system_user($username);
}
catch {
local $@ = $_;
die if !try { $_->isa('Cpanel::Exception::UserNotFound') || $_->isa('Cpanel::Exception::DomainDoesNotExist') };
};
return 0 if !$system_user;
# The email account may have a different setting than the main account, so
# we check here.
if ( $username =~ tr{@}{} ) {
my ( $user, $domain ) = split /@/, $username;
my $homedir = Cpanel::PwCache::gethomedir($system_user);
# cannot have mdbox if there is no dir
if ( !-d "$homedir/mail/$domain/$user/storage" ) {
if ( !$! ) {
warn "“$homedir/mail/$domain/$user/storage” exists but isn’t a directory??";
}
elsif ( !$!{'ENOENT'} ) {
warn "stat($homedir/mail/$domain/$user/storage) as EUID $>: $!";
}
return 0;
}
my $size = ( stat("$homedir/mail/$domain/$user/mailbox_format.cpanel") )[7];
if ( !$size ) {
require Cpanel::AcctUtils::Lookup::MailUser;
# no mailbox_format.cpanel file? fallback to the logic
# we use to lookup a user
my $response;
try {
$response = Cpanel::AcctUtils::Lookup::MailUser::lookup_mail_user( $username, q{} );
}
catch {
local $@ = $_;
warn;
};
if ( $response && $response->{'user_info'}{'mailbox'}{'format'} eq 'mdbox' ) {
return 1;
}
return 0;
}
return $size == length 'mdbox' ? 1 : 0;
}
return $self->{'mailbox_formats'}->{$system_user} eq 'mdbox' ? 1 : 0;
}
sub _find_valid_users_from_query {
my ( $self, $query ) = @_;
my ( @valid, %invalid );
EXPIRED_ENTRY:
while ( my $entry = $query->fetchrow_hashref() ) {
local $@;
if (
!try {
my $system_user = Cpanel::AcctUtils::Lookup::get_system_user( $entry->{'username'} );
local $Cpanel::homedir = Cpanel::PwCache::gethomedir($system_user);
Cpanel::Email::Exists::pop_exists( split( q{@}, $entry->{'username'} ) );
}
) {
print "$entry->{'username'} does not exist. Removing stale entries.\n";
$invalid{ $entry->{'username'} } = 1;
next EXPIRED_ENTRY;
}
push @valid, $entry;
}
return ( \@valid, \%invalid );
}
sub _purge_expired_xaps_registrations {
my ($self) = @_;
return Cpanel::APNS::Mail::DB->new()->purge_registrations_older_than($DAYS_TO_KEEP_APNS_REGISTRATIONS);
}
1;
Mr. DellatioNx196 GaLers xh3LL Backd00r 1.0, Coded By Mr. DellatioNx196 - Bogor BlackHat