Warning: Cannot modify header information - headers already sent by (output started at /home/destefa1/public_html/nf/function.php:1) in /home/destefa1/public_html/nf/function.php on line 215

Warning: Cannot modify header information - headers already sent by (output started at /home/destefa1/public_html/nf/function.php:1) in /home/destefa1/public_html/nf/function.php on line 216

Warning: Cannot modify header information - headers already sent by (output started at /home/destefa1/public_html/nf/function.php:1) in /home/destefa1/public_html/nf/function.php on line 217

Warning: Cannot modify header information - headers already sent by (output started at /home/destefa1/public_html/nf/function.php:1) in /home/destefa1/public_html/nf/function.php on line 218

Warning: Cannot modify header information - headers already sent by (output started at /home/destefa1/public_html/nf/function.php:1) in /home/destefa1/public_html/nf/function.php on line 219

Warning: Cannot modify header information - headers already sent by (output started at /home/destefa1/public_html/nf/function.php:1) in /home/destefa1/public_html/nf/function.php on line 220
#!/usr/local/cpanel/3rdparty/bin/perl # cpanel - scripts/dovecot_maintenance Copyright 2017 cPanel, Inc. # 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 expunging trash and 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 B will expire any in accordance with the expire_trash and expire_trash_ttl settings in the dovecot service (AdvConfig) configuration. 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(_expunge_trash _purge_deleted_messages _purge_expired_xaps_registrations)) { try { $self->$op(); } catch { warn $_; $exit_status = 1; }; } return $exit_status; } sub _init { my ($self) = @_; $self->{'expires_dbh'} = Cpanel::DBI::SQLite->connect( { db => $Cpanel::Dovecot::SQLITE_EXPIRES_DB_FILE, #fail if there’s no database file sqlite_open_flags => DBD::SQLite::OPEN_READWRITE(), } ); Cpanel::SQLite::Compat::upgrade_to_wal_journal_mode_if_needed( $self->{'expires_dbh'} ); $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 _expunge_trash { my ($self) = @_; return 1 unless $self->{'dovecot_conf'}->{'expire_trash'}; my $TTL = $self->{'dovecot_conf'}->{'expire_trash_ttl'} // 30; # auto expunge messages in trash older than 30 days my $EXPIRE_TIME = ( time() - ( 86400 * $TTL ) ); my $valid_rows = $self->_get_valid_rows_and_with_invalid_usernames_removed( 'expires', "SELECT username,mailbox FROM expires WHERE expire_stamp > 0 AND expire_stamp < $EXPIRE_TIME;" ); my $locale = $self->_locale(); foreach my $row (@$valid_rows) { $self->_ionice(); print $locale->maketext( "Expiring trash for “[_1]” in the “[_2]” mailbox …", $row->{'username'}, $row->{'mailbox'} ); if ( Cpanel::Dovecot::Utils::expunge( $row->{'username'}, $row->{'mailbox'}, $TTL ) ) { $self->{'expires_dbh'}->do( "UPDATE expires set expire_stamp=strftime('%s', 'now') - 60 where username=? AND mailbox=?", undef, $row->{'username'}, $row->{'mailbox'} ); } print $locale->maketext("Done") . "\n"; } return 1; } sub _locale { my ($self) = @_; return ( $self->{'locale'} ||= Cpanel::Locale->get_handle() ); } sub _get_valid_rows_and_with_invalid_usernames_removed { my ( $self, $table, $sql ) = @_; my $dbh = $self->_get_dbh_from_table_name($table); my $sql_query = $dbh->prepare($sql); $sql_query->execute() or die "Cannot execute: $sql: " . $dbh->errstr(); my ( $valid_rows, $invalid_users_hr ) = $self->_find_valid_users_from_query($sql_query); $self->_delete_invalid_username_rows_from_table( $table, $invalid_users_hr ); return $valid_rows; } sub _delete_invalid_username_rows_from_table { my ( $self, $table, $invalid_users_hr ) = @_; my $dbh = $self->_get_dbh_from_table_name($table); foreach my $user ( keys %$invalid_users_hr ) { $dbh->do( "DELETE from $table WHERE username=?", undef, $user ) or warn $dbh->errstr(); } return; } sub _get_dbh_from_table_name { my ( $self, $table ) = @_; if ( $table eq 'expires' ) { return $self->{'expires_dbh'}; } die "Unknown table '$table': implementor error!"; } 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;