package Cpanel::BoxTrapper;

# cPanel12 - Cpanel/Boxtrapper.pm            Copyright(c) 2004-2007 cPanel, Inc.
#                                                           All rights Reserved.
# copyright@cpanel.net                                         http://cpanel.net
# This code is subject to the cPanel license. Unauthorized copying is prohibited

use lib '/usr/local/cpanel';
use strict;
use Cwd qw( abs_path );
use Time::HiRes qw( gettimeofday );
use Carp               ();
use SafeFile           ();
use Cpanel::Encoder    ();
use IO::Handle         ();
use Cpanel             ();
use Cpanel::Lang       ();
use Cpanel::Config     ();
use Cpanel::Sys        ();
use Cpanel::FileUtils  ();
use Cpanel::SafeDir    ();
use Cpanel::StringFunc ();
use Cpanel::PwCache    ();
use Cpanel::Regex      ();

use vars qw( @ISA @EXPORT $VERSION );

require Exporter;
require DynaLoader;
@ISA = qw( Exporter DynaLoader );

@EXPORT = qw(
  BoxTrapper_init            BoxTrapper_gettransportmethod
  BoxTrapper_findreturnaddy  BoxTrapper_checkdeadq
  BoxTrapper_clog            BoxTrapper_loadconf
  BoxTrapper_getqueueid      BoxTrapper_queuemessage
  BoxTrapper_sendformmessage BoxTrapper_checklist
  BoxTrapper_delivermessage  BoxTrapper_extractaddresses
  BoxTrapper_splitaddresses  BoxTrapper_extractaddress
  BoxTrapper_getheader       BoxTrapper_getsender
  BoxTrapper_getheaders      BoxTrapper_getdomainowner
  BoxTrapper_beginmatch      BoxTrapper_gethomedir
  BoxTrapper_getranddata     BoxTrapper_getourid
  BoxTrapper_addaddytolist   BoxTrapper_isfromself
  BoxTrapper_isinarray       BoxTrapper_loopprotect
  BoxTrapper_getemaildirs    BoxTrapper_getaccountinfo
  BoxTrapper_getwebdomain    BoxTrapper_accountmanagelist
  BoxTrapper_listpops        BoxTrapper_isenabled
  BoxTrapper_status          BoxTrapper_statusbutton
  BoxTrapper_changestatus    BoxTrapper_logdate
  BoxTrapper_logcontrols     BoxTrapper_showlog
  BoxTrapper_nicedate        BoxTrapper_cleanfield
  BoxTrapper_showemails      BoxTrapper_showqueuetime
  BoxTrapper_saveconf        BoxTrapper_getboxconfdir
  BoxTrapper_showqueue       BoxTrapper_extractfrom
  BoxTrapper_showmessage     BoxTrapper_messageaction
  BoxTrapper_listmsgs        BoxTrapper_editmsg
  BoxTrapper_loadfwdlist     BoxTrapper_resetmsg
  BoxTrapper_logmatch        BoxTrapper_showqueuesearch
  BoxTrapper_extractbody     BoxTrapper_multimessageaction
  BoxTrapper_cleanlist       BoxTrapper_getheadersfromfile
  BoxTrapper_getmailuser     BoxTrapper_showautowhitelist
  BoxTrapper_maildircheck    BoxTrapper_fetchcfgfile
  BoxTrapper_savecfgfile	 BoxTrapper_cleancfgfilelist
  BoxTrapper_updatesearchdb  BoxTrapper_extractheaders
  BoxTrapper_extractall      BoxTrapper_rebuildsearchdb
  BoxTrapper_removefromsearchdb
  BoxTrapper_showfromname    BoxTrapper_getrecievedfrom
);

$VERSION = '1.6';

my $rCPCONF;
my $maildir;
my %mailuser = BoxTrapper_getmailuser();
my $lang     = 'english';

sub BoxTrapper_init {
    $lang = $Cpanel::CPDATA{'LANG'};
    return 1;
}


sub BoxTrapper_cleanfield     { goto &Cpanel::Encoder::html_encode_str; }
sub BoxTrapper_beginmatch     { goto &Cpanel::StringFunc::ibeginmatch; }
sub BoxTrapper_extractheaders { goto &BoxTrapper_getheadersfromfile; }
sub BoxTrapper_endmatch       { goto &Cpanel::StringFunc::iendmatch; }
sub BoxTrapper_extractfrom    { goto &BoxTrapper_extractaddress; }

sub BoxTrapper_accountmanagelist {
    my $link = shift;
    my $POPS = BoxTrapper_listpops();

    unshift @{$POPS}, $Cpanel::user;
    my $bg = 'even';
    foreach my $pop ( @{$POPS} ) {
        next if ( $Cpanel::appname eq 'webmail' && $pop ne $Cpanel::authuser );

        my $enabled = BoxTrapper_isenabled($pop);
        my $status;
        if ($enabled) {
            $status = qq{<font class="redtext">$Cpanel::Lang::LANG{$lang}{'enabled-BoxTrapper'}</font>};
        }
        else {
            $status = qq{<font class="blacktext">$Cpanel::Lang::LANG{$lang}{'disabled-BoxTrapper'}</font>};
        }
        print <<"EOM";
<tr class="info-$bg">
    <td>$pop</td>
    <td>$status</td>
    <td><a href="$link?account=$pop">$Cpanel::Lang::LANG{$lang}{'manage-BoxTrapper'}</a></td>
</tr>
EOM
        $bg eq 'even' ? $bg = 'odd' : $bg = 'even';
    }
}

sub BoxTrapper_addaddytolist {
    my $list = shift;
    my $addy = shift;
    my $dir  = shift;
    $addy =~ s/$Cpanel::Regex::regex{'singledot'}/\\\./g;
    $addy =~ s/$Cpanel::Regex::regex{'commercialat'}/\\\@/g;
    $addy =~ s/$Cpanel::Regex::regex{'newline'}//g;
    $addy =~ s/^\s*|\s*$//g;

    $dir  =~ s/$Cpanel::Regex::regex{'doubledot'}//g;
    $list =~ s/$Cpanel::Regex::regex{'doubledot'}//g;

    return if ( $addy eq '' );

    if ( -e $dir . '/.boxtrapper/' . $list . '-list.txt' ) {
        my $listlock = SafeFile::safeopen( \*MYLST, $dir . '/.boxtrapper/' . $list . '-list.txt' );
        while (<MYLST>) {
            chomp();
            if ( $_ eq 'from ' . $addy ) {
                SafeFile::safeclose( \*MYLST, $listlock );
                return;
            }
        }
        SafeFile::safeclose( \*MYLST, $listlock );
    }

    my $listlock = SafeFile::safeopen( \*MYLST, '>>', $dir . '/.boxtrapper/' . $list . '-list.txt' );
    print MYLST "from $addy\n";
    SafeFile::safeclose( \*MYLST, $listlock );
}

sub BoxTrapper_savecfgfile {
    my $account  = shift;
    my $filetype = shift;
    my $cfgfile  = shift;
    my $page = shift;
    $cfgfile =~ s/\///g;
    $cfgfile =~ s/\.\.//g;
    
    if ( $Cpanel::appname eq 'webmail' ) {
        $account = $Cpanel::authuser;
    }

    my $file;
    if ( $filetype eq 'list' ) {
        $file = BoxTrapper_getboxconfdir( $account, 1 ) . '/' . $cfgfile;
    }
    elsif ( $filetype eq 'msg' ) {
        $file = BoxTrapper_getboxconfdir( $account, 1 ) . '/forms/' . $cfgfile;
    }

    if ( open my $write_fh, '>', $file ) {
        $page =~ s/\r//g;
        print {$write_fh} $page;
        close $write_fh;
    }
    else {
        Cpanel::Logger::cplog( "Failed to write $file: $!", 'warn', __PACKAGE__, 1 );
    }
    return;
}

sub BoxTrapper_fetchcfgfile {
    my $account  = shift;
    my $filetype = shift;
    my $cfgfile  = shift;
    $cfgfile =~ s/\///g;
    $cfgfile =~ s/\.\.//g;

    if ( $Cpanel::appname eq 'webmail' ) {
        $account = $Cpanel::authuser;
    }

    my $file;
    if ( $filetype eq 'list' ) {
        $file = BoxTrapper_getboxconfdir( $account, 1 ) . '/' . $cfgfile;
    }
    elsif ( $filetype eq 'msg' ) {
        $file = BoxTrapper_getboxconfdir( $account, 1 ) . '/forms/' . $cfgfile;
    }

    return if !-e $file;

    if ( open my $read_fh, '<', $file ) {
        while (<$read_fh>) {
            print Cpanel::Form::escapehtml($_);
        }
        close $read_fh;
    }
    else {
        Cpanel::Logger::cplog( "Failed to read $file: $!", 'warn', __PACKAGE__, 1 );
    }
}

sub BoxTrapper_changestatus {
    my $account = shift;
    if ( $Cpanel::appname eq 'webmail' ) {
        $account = $Cpanel::authuser;
    }
    my $action = shift;
    my ( $homedir, $domain ) = BoxTrapper_getaccountinfo($account);
    if ( !$homedir ) {
        print $Cpanel::Lang::LANG{$lang}{'unablelocate-BoxTrapper'} . " $account\n";
        return;
    }

    my ( $emaildir, $emaildeliverdir ) = BoxTrapper_getemaildirs( $account, $homedir );

    if ( $action =~ m/enable/i ) {
        print $Cpanel::Lang::LANG{$lang}{'enabled-BoxTrapper'};
        my $filelock = SafeFile::safeopen( \*BX, '>>', $emaildir . '/.boxtrapperenable' );
        SafeFile::safeclose( \*BX, $filelock );
        return 1;
    }
    else {
        print $Cpanel::Lang::LANG{$lang}{'disabled-BoxTrapper'};
        unlink $emaildir . '/.boxtrapperenable';
        return 1;
    }
}

sub BoxTrapper_checkdeadq {
    my $emaildir = shift;
    my $rconf    = shift;
    my $now      = time();
    my $killtime = $rconf->{'stale-queue-time'};
    $killtime = ( $killtime * 86400 );    #time in seconds

    my $mtime = ( stat("$emaildir/boxtrapper/last_dead_check") )[9];

    return if ( ( $mtime + $killtime ) > $now && $mtime < $now );    #time warp safe

    BoxTrapper_clog( 2, $emaildir, "Searching for stale data files ($killtime seconds old)" );
    my @DIRS = qw( boxtrapper/queue boxtrapper/verifications boxtrapper/log );

    my @removed_queue_files;
    foreach my $dir (@DIRS) {
        if ( opendir my $qf_fh, $emaildir . '/' . $dir ) {
            my @QFS = readdir $qf_fh;
            closedir $qf_fh;
            foreach my $qf (@QFS) {
                if ( -f $emaildir . '/' . $dir . '/' . $qf && ( stat(_) )[9] + $killtime < $now ) {
                    if ( unlink $emaildir . '/' . $dir . '/' . $qf ) {
                        push @removed_queue_files, $qf;
                        BoxTrapper_clog( 2, $emaildir, "Unlinking ${emaildir}/${dir}/${qf} age exceeds $killtime seconds" );
                    }
                    else {
                        BoxTrapper_clog( 2, $emaildir, "Failed to unlink ${emaildir}/${dir}/${qf}: $!" );
                    }
                }
            }
        }
    }
    Cpanel::FileUtils::touchfile("$emaildir/boxtrapper/last_dead_check");
    if ( @removed_queue_files ) {
        BoxTrapper_removefromsearchdb( $emaildir, \@removed_queue_files );
    }
}

sub BoxTrapper_checklist {
    my ( $list, $dir, $addy, $raddyto, $raddycc, $subject ) = @_;

    $dir  =~ s/$Cpanel::Regex::regex{'doubledot'}//g;
    $list =~ s/$Cpanel::Regex::regex{'doubledot'}//g;
    my $linenum = 0;
    
    return if !-e $dir . '/.boxtrapper/' . $list . '-list.txt';
    if ( open my $list_fh, '<', $dir . '/.boxtrapper/' . $list . '-list.txt' ) {
        while ( readline $list_fh ) {
            $linenum++;
            next if Cpanel::StringFunc::ibeginmatch( $_, '#' );
            s/\r?\n$//g;
            
            my ( $header, $match ) = split( / /, $_, 2 );
            my $matchrgx = $match;
            next if ( !defined $match || $match =~ m/^\s*$/ );
            $header = lc $header;
            
            if ( Cpanel::StringFunc::ibeginmatch( $matchrgx, q{'} ) && Cpanel::StringFunc::iendmatch( $matchrgx, q{'} ) ) {
                $matchrgx = Cpanel::StringFunc::begintrim( $matchrgx, q{'} );
                $matchrgx = Cpanel::StringFunc::endtrim( $matchrgx, q{'} );
                $matchrgx = '\Q' . $match . '\E';
            }
            elsif ( Cpanel::StringFunc::ibeginmatch( $matchrgx, q{"} ) && Cpanel::StringFunc::iendmatch( $matchrgx, q{"} ) ) {
                $matchrgx = Cpanel::StringFunc::begintrim( $matchrgx, q{"} );
                $matchrgx = Cpanel::StringFunc::endtrim( $matchrgx, q{"} );
                $matchrgx = '\Q' . $match . '\E';
            }

            if ( $header eq 'from' && $addy =~ m/$matchrgx/i ) {
                BoxTrapper_logmatch( $dir, $list, $header, $match, $linenum );
                close $list_fh;
                return wantarray ? ( 1, $match, $linenum ) : 1;
            }
            elsif ( $header eq 'subject' && $subject =~ m/$matchrgx/i ) {
                BoxTrapper_logmatch( $dir, $list, $header, $match, $linenum );
                close $list_fh;
                return wantarray ? ( 1, $match, $linenum ) : 1;
            }
            elsif ( $header eq 'to' ) {
                foreach my $addyto ( @{$raddyto} ) {
                    if ( $addyto =~ m/$matchrgx/i ) {
                        close $list_fh;
                        BoxTrapper_logmatch( $dir, $list, $header, $match, $linenum );
                        return wantarray ? ( 1, $match, $linenum ) : 1;
                    }
                }
            }
            elsif ( $header eq 'cc' ) {
                foreach my $addycc ( @{$raddycc} ) {
                    if ( $addycc =~ m/${matchrgx}/i ) {
                        close $list_fh;
                        BoxTrapper_logmatch( $dir, $list, $header, $match, $linenum );
                        return wantarray ? ( 1, $match, $linenum ) : 1;
                    }
                }
            }
        }
        close $list_fh;
        return;
    }
    else {
        Cpanel::Logger::cplog( "Failed to open $dir/.boxtrapper/${list}-list.txt: $!", 'warn', __PACKAGE__, 1 );
        return;
    }
}

sub BoxTrapper_cleancfgfilelist {
    my $account  = shift;
    my $filetype = shift;
    my $cfgfile  = shift;
    $cfgfile =~ s/\///g;
    $cfgfile =~ s/\.\.//g;

    if ( $Cpanel::appname eq 'webmail' ) {
        $account = $Cpanel::authuser;
    }

    my $file;
    if ( $filetype eq 'list' ) {
        $file = BoxTrapper_getboxconfdir( $account, 1 ) . '/' . $cfgfile;
    }
    else {
        return;
    }

    return BoxTrapper_cleanlist($file);
}

sub BoxTrapper_cleanlist {
    my $file = shift;
    my @LST;
    my $listlock = SafeFile::safeopen( \*LST, '+<', $file );
    my %DUPLIST;
    while (<LST>) {
        s/\r?\n//g;
        if (/^[\s\t]*(from|to|cc)[\s\t]+/io) {
            my ( $match, $address ) = split( /[\s\t]+/, $_ );
            if (Cpanel::StringFunc::beginmatch($address,'*') || Cpanel::StringFunc::beginmatch($address,'+') || Cpanel::StringFunc::beginmatch($address,'?')) {
                $address = '.' . $address; 
# people like to put things like *@cpanel.net  when its a perl regex and they really mean .*@cpanel.net
            }
            $match   = lc($match);
            $address = lc($address);
            next if ( $DUPLIST{$match}{$address} );
            $DUPLIST{$match}{$address} = 1;
            push( @LST, $match . ' ' . $address );
        }
        else {
            push( @LST, $_ );
        }
    }
    seek( LST, 0, 0 );
    print LST join( "\n", @LST );
    truncate( LST, tell(LST) );
    SafeFile::safeclose( \*LST, $listlock );
    return 0;
}

sub BoxTrapper_clog {
    my ( $loglevel, $emaildir, $log )  = @_;
    my ( $mon,      $mday,     $year ) = BoxTrapper_nicedate( time() );
    chomp $log;

    if ( !-e $emaildir . '/boxtrapper' ) {
        Cpanel::SafeDir::safemkdir( $emaildir . '/boxtrapper', 0700 );
    }
    if ( !-e $emaildir . '/boxtrapper/log' ) {
        Cpanel::SafeDir::safemkdir( $emaildir . '/boxtrapper/log', 0700 );
    }
    my $loglock = SafeFile::safeopen( \*CLOG, '>>', $emaildir . '/boxtrapper/log/' . $mon . '-' . $mday . '-' . $year . '.log' );
    print CLOG $log . "\n";
    SafeFile::safeclose( \*CLOG, $loglock );
    return;
}

sub BoxTrapper_delivermessage {
    my ( $clean, $emaildir, $file, $hdref, $bdref ) = @_;
    my $fwd = 0;

    $file =~ s/$Cpanel::Regex::regex{doubledot}//g;

    if ($clean) {
        my @FWDLIST = BoxTrapper_loadfwdlist($emaildir);
        if ( $#FWDLIST > -1 && -x '/usr/sbin/sendmail' ) {
            open( FWD, '|-' ) || exec( '/usr/sbin/sendmail', '-oi', @FWDLIST );
            $fwd = 1;
        }
    }

    my $msglock;
    my $maildirfile;

    if ( BoxTrapper_maildircheck() && $clean ) {
        my $hostname = Cpanel::Sys::gethostname() || 'localhost';
        
        my ( $stime, $mtime );
        
        ( $stime, $mtime ) = Time::HiRes::gettimeofday();
        
        
        $maildirfile = $stime . '.H' . $mtime . 'P' . $$ . '.' . $hostname;

        while ( -e $file . '/tmp/' . $maildirfile ) {
            ( $stime, $mtime ) = Time::HiRes::gettimeofday();
            $maildirfile = $stime . '.H' . $mtime . 'P' . $$ . '.' . $hostname;
        }
        unless ( open( MBOX, '>', $file . '/tmp/' . $maildirfile ) ) {
            if ($fwd) {
                close(FWD);
            }
            warn "Unable to write message: $!";
            return;
        }
    }
    else {
        $msglock = SafeFile::safeopen( \*MBOX, '>>', $file );
    }
    my $converted = 0;

    foreach ( @{$hdref} ) {
        print MBOX;
        if ($fwd) {
            if ( !$converted && /^From /o ) {
                $converted = 1;
                my $id = BoxTrapper_getourid($emaildir);
                print FWD "X-Boxtrapper: $id\n";
            }
            else {
                print FWD;
            }
        }
    }
    print MBOX "\n";
    if ($fwd) {
        print FWD "\n";
    }

    if ( $#{$bdref} == -1 ) {
        while (<STDIN>) {
            print MBOX;
            if ($fwd) {
                print FWD;
            }
        }
    }
    else {
        foreach ( @{$bdref} ) {
            print MBOX;
            if ($fwd) {
                print FWD;
            }
        }
    }

    if ($fwd) {
        close(FWD);
    }
    if ( BoxTrapper_maildircheck() && $clean ) {
        close(MBOX);
        if ( -e $file . '/maildirsize' ) {
            my $mdsize = SafeFile::safeopen( \*MDSIZE, '>>', $file . '/maildirsize' );
            if ($mdsize) {
                my $size = ( stat( $file . '/tmp/' . $maildirfile ) )[7];
                if ($size) {
                    print MDSIZE $size . " 1\n";
                }
                SafeFile::safeclose( \*MDSIZE, $mdsize );
            }
        }
        rename( $file . '/tmp/' . $maildirfile, $file . '/new/' . $maildirfile );
    }
    else {
        SafeFile::safeclose( \*MBOX, $msglock );
    }
}

sub BoxTrapper_editmsg {
    my $account = shift;
    if ( $Cpanel::appname eq 'webmail' ) {
        $account = $Cpanel::authuser;
    }
    my $message = shift;
    my ( $homedir, $domain ) = BoxTrapper_getaccountinfo($account);
    if ( !$homedir ) {
        print $Cpanel::Lang::LANG{$lang}{'unablelocate-BoxTrapper'} . " $account\n";
        return;
    }

    $message =~ s/$Cpanel::Regex::regex{'forwardslash'}//g;
    $message =~ s/$Cpanel::Regex::regex{'doubledot'}//g;
    my ( $emaildir, $emaildeliverdir ) = BoxTrapper_getemaildirs( $account, $homedir );
    if ( !-e $emaildir . '/.boxtrapper/forms/' . $message ) {
        system 'cp', '-f', '/usr/local/cpanel/etc/boxtrapper/forms/' . $message, $emaildir . '/.boxtrapper/forms/' . $message;
    }
}

sub BoxTrapper_extractaddress {
    my $email = shift;
    $email = lc $email;
    $email =~ s/$Cpanel::Regex::regex{'multipledot'}/$Cpanel::Regex::regex{'singledot'}/g;
    my ($ea) = $email =~ m/\<($Cpanel::Regex::regex{'emailaddr'})\>/;
    if ( !$ea ) {
        ($ea) = $email =~ m/($Cpanel::Regex::regex{'emailaddr'})/;
    }
    return $ea;
}

sub BoxTrapper_extractaddresses {
    my @ADDRESSES = @_;
    my @EADDRESSES;
    foreach my $addr (@ADDRESSES) {
        push @EADDRESSES, BoxTrapper_extractaddress($addr);
    }
    return @EADDRESSES;
}

# Warning, this extracts binary attachments as well.
sub BoxTrapper_extractall {
    my $filename = shift;
    return if !$filename;
    my @body;
    my @headers;
    my $nextline = 0;
    if ( open my $msg_fh, '<', $filename ) {
        while (<$msg_fh>) {
            if ( !$nextline ) {
                if ( m/$Cpanel::Regex::regex{'lineofnonprintingchars'}/ ) {
                    $nextline = 1;
                    next;
                }
                push @headers, $_;
            }
            elsif ( $nextline ) {
                push @body, $_;
            }
        }
        close $msg_fh;
    }
    else {
        Cpanel::Logger::cplog( "Failed to open $filename: $!", 'warn', __PACKAGE__, 1 );
        return;
    }
    return wantarray ? ( \@headers, \@body ) : \@body;
}

sub BoxTrapper_extractbody {
    my $body_aref = BoxTrapper_extractall(@_);
    if ( $body_aref && ref $body_aref eq 'ARRAY' ) {
        return wantarray ? @{ $body_aref } : join( '', @{ $body_aref } );
    }
    return;
}

sub BoxTrapper_findreturnaddy {
    my $account  = shift;
    my $okaddys  = shift;
    my @ALLADDYS = @_;
    my @OKADDYS  = split( /\,/, $okaddys );

    foreach my $okaddy (@OKADDYS) {
        foreach my $emailaddy (@ALLADDYS) {
            if ( $okaddy eq $emailaddy ) {
                return $okaddy;
            }
        }
    }
    return $account;
}

sub BoxTrapper_getaccountinfo {
    my $account = shift;
    my $domain;

    if ( $account =~ $Cpanel::Regex::regex{'commercialat'} ) {
        $domain = ( split( /\@/, $account ) )[1];
    }
    elsif ( $account =~ $Cpanel::Regex::regex{'plussign'} ) {
        $domain = ( split( /\+/, $account ) )[1];
    }
    elsif ( Cpanel::PwCache::getpwnam($account) ) {
        return ( Cwd::abs_path( ( Cpanel::PwCache::getpwnam($account) )[7] ), '' );
    }
    else {
        return;
    }

    if ( !$domain ) {
        print "Unable to locate $account information.\n";
        return;
    }
    my $homedir = BoxTrapper_gethomedir($domain);
    return ( $homedir, $domain );
}

sub BoxTrapper_getboxconfdir {
    my $account = shift;
    my $ret     = shift;
    if ( $Cpanel::appname eq 'webmail' ) {
        $account = $Cpanel::authuser;
    }
    my ( $homedir, $domain ) = BoxTrapper_getaccountinfo($account);
    if ( !$homedir ) {
        print $Cpanel::Lang::LANG{$lang}{'unablelocate-BoxTrapper'} . " $account\n";
        return;
    }
    my ( $emaildir, $emaildeliverdir ) = BoxTrapper_getemaildirs( $account, $homedir );
    if ( $emaildir eq '' || $emaildeliverdir eq '' ) {
        print $Cpanel::Lang::LANG{$lang}{'noemaildirbegin-BoxTrapper'} . ' ' . $account . ' ' . $Cpanel::Lang::LANG{$lang}{'noemaildirend-BoxTrapper'} . "\n";
        return;
    }
    if ( !-e $emaildir . '/.boxtrapper' ) {
        Cpanel::SafeDir::safemkdir( $emaildir . '/.boxtrapper', 0700 );
    }
    if ($ret) { return $emaildir . '/.boxtrapper'; }
    print $emaildir . '/.boxtrapper';
}

sub BoxTrapper_getdomainowner {
    my $domain = shift;
    my $domainowner;
    if ( $> == $mailuser{'uid'} ) {
        if ( -r '/etc/userdomains' ) {
            if ( open my $ud_fh, '<', '/etc/userdomains' ) {
                while ( readline $ud_fh ) {
                    if ( m/^\Q$domain\E\s*:\s*(\S+)/ ) {
                        $domainowner = $1;
                        last;
                    }
                }
                close $ud_fh;
            }            
        }
        elsif ( -e _ ) {
            Cpanel::Logger::cplog( "System file /etc/userdomains does not exist. Run /scripts/updateuserdomains", 'warn', __PACKAGE__, 1 );
        }
        elsif ( !-r _ ) {
            Cpanel::Logger::cplog( "/etc/userdomains is not readable by $mailuser{'name'}", 'warn', __PACKAGE__, 1 );
        }
    }
    else {
        $domainowner = ( Cpanel::PwCache::getpwuid($>) )[0];
    }
    if ( !$domainowner ) {
        Cpanel::Logger::cplog( "Unable to determine owner of domain $domain", 'warn', __PACKAGE__, 1 );
    }
    return $domainowner;
}

sub BoxTrapper_getemaildirs {
    my ( $account, $homedir ) = @_;
    my $error_message = qq{$Cpanel::Lang::LANG{$lang}{'noemaildirbegin-BoxTrapper'} $account $Cpanel::Lang::LANG{$lang}{'noemaildirend-BoxTrapper'}\n};
    if ( !$account ) {
        Cpanel::Logger::cplog( 'Missing argument(s)', 'warn', __PACKAGE__, 1 );
        print $error_message;
        return;
    }
    elsif ( !$homedir ) {
        $homedir = ( Cpanel::PwCache::getpwuid($>) )[7];
    }
    
    $account =~ s/$Cpanel::Regex::regex{'multipledot'}/\./g;
    $homedir =~ s/$Cpanel::Regex::regex{'multipledot'}/\./g;
    
    if ( !-e $homedir ) {
        Cpanel::Logger::cplog( "HOMEDIR: $homedir", 'info', __PACKAGE__, 1 );
        return;
    }

    my ( $emaildir, $emaildeliverdir );

    if ( $account =~ $Cpanel::Regex::regex{'commercialat'} ) {
        my ( $user, $domain ) = split /\@/, $account;
        $emaildir        = $homedir . '/etc/' . $domain . '/' . $user;
        $emaildeliverdir = $homedir . '/mail/' . $domain . '/' . $user;
        
        # Validate domain
        if ( !-e $emaildir && !-e $homedir . '/etc/' . $domain ) {
            if ( !$Cpanel::user ) {
                $Cpanel::user = ( Cpanel::PwCache::getpwuid($>) )[0];
            }
            if ( !-e '/var/cpanel/users/' . $Cpanel::user ) {
                Cpanel::Logger::cplog( "Unable to resolve cPanel user for $account", 'warn', __PACKAGE__, 1 );
                print $error_message;
                return;
            }
        }
    }
    else {
        $emaildir        = $homedir . '/etc';
        $emaildeliverdir = $homedir . '/mail';
    }

    foreach my $dir ( $emaildir, $emaildeliverdir, $emaildir . '/.boxtrapper', $emaildir . '/.boxtrapper/forms' ) {
        if ( !-e $dir ) {
            Cpanel::SafeDir::safemkdir( $dir, '0711' );
        }
        if ( !-d $dir ) {
            Cpanel::Logger::cplog( "Missing directory $dir, BoxTrapper will not function properly", 'warn', __PACKAGE__, 1 );
            if ( $dir eq $homedir . '/mail' ) {
                Cpanel::Logger::cplog( "Mail directory $dir must be present for mail delivery!", 'warn', __PACKAGE__, 1 );
                print $error_message;
                return;
            }
        }
    }
    return wantarray ? ( $emaildir, $emaildeliverdir ) : \@{ $emaildir, $emaildeliverdir };
}

sub BoxTrapper_getheader {
    my $header   = shift;
    my $hdref    = shift;
    my $whichone = shift;

    my $hresult;
    my $nextline   = 0;
    my $matchcount = 0;

    foreach ( @{$hdref} ) {
        if (   $nextline == 1
            && $hresult ne ''
            && m/$Cpanel::Regex::regex{'beginswithspaceortabs'}/ ) {
            s/$Cpanel::Regex::regex{'beginswithspaceortabs'}/ /g;
            $hresult .= $_;
        }
        elsif ( $nextline == 0 && /^${header}: (.*)/i ) {
            if ( $hresult ne '' ) { last; }
            $hresult  = $1;
            $nextline = 1;
            $matchcount++;
        }
        elsif ( $nextline == 1 ) {
            if ( $whichone && $whichone != $matchcount ) {
                if (/^${header}: (.*)/i) {
                    $hresult = $1;
                    $matchcount++;
                }
                else {
                    $nextline = 0;
                    $hresult  = '';
                }
                next;
            }
            last; # we didn't match the nextline again so bail
        }
    }
    return $hresult;
}

sub BoxTrapper_getheaders {
    my @HEADERS;
    while (<STDIN>) {
        last if (/$Cpanel::Regex::regex{'emailheaderterminator'}/);
        push( @HEADERS, $_ );
    }
    return wantarray ? @HEADERS : \@HEADERS;
}

sub BoxTrapper_getheadersfromfile {
    my $file = shift;
    my @HEADERS;

    if ( -e $file ) {
        open( HFILE, '<', $file );
        while (<HFILE>) {
            last if (m/$Cpanel::Regex::regex{'emailheaderterminator'}/);
            push( @HEADERS, $_ );
        }
        close(HFILE);
    }
    return wantarray ? @HEADERS : \@HEADERS;
}

sub BoxTrapper_gethomedir {
    my $domain  = shift;
    my $homedir;
    if ( $> != $mailuser{'uid'} ) {
        $homedir = Cwd::abs_path( ( Cpanel::PwCache::getpwuid($>) )[7] );
    }
    else {
        $homedir = $mailuser{'dir'};
    }

    if ( !$homedir ) {
        Cpanel::Logger::cplog( "Unable to determine home directory for domain $domain", 'warn', __PACKAGE__, 1 );
    }
    return $homedir;
}

sub BoxTrapper_getmailuser {
    my ( $muid, $mname, $mdir ) = ( Cpanel::PwCache::getpwnam('mailnull') )[0,2,7];
    if ( !$muid ) {
        ( $muid, $mname, $mdir ) = ( Cpanel::PwCache::getpwnam('mail') )[0,2,7];
    }
    if ( !$muid ) {
        Cpanel::Logger::cplog( 'Cannot determine mail user uid, please create mail user', 'die', __PACKAGE__ );
        return; # Just in case ?
    }
    return wantarray ? ( 'uid'  => $muid, 'name' => $mname, 'dir' => $mdir ) : $mname;
}

sub BoxTrapper_getourid {
    my $emaildir = shift;
    $emaildir =~ s/$Cpanel::Regex::regex{doubledot}//g;
    my $id;
    if ( -e $emaildir . '/.boxtrapper/id' ) {
        open( ID, '<', $emaildir . '/.boxtrapper/id' );
        chomp( $id = <ID> );
        close(ID);
    }

    if ( !$id || $id eq '' ) {
        $id = BoxTrapper_getranddata( 32, 1 );
        open( ID, '>', $emaildir . '/.boxtrapper/id' );
        print ID $id;
        close(ID);
    }
    return $id;
}

sub BoxTrapper_getqueueid {
    my $dir      = shift;
    my $queuedir = $dir . '/boxtrapper/queue/';
    my $randdata;
    my $rndfile = BoxTrapper_getranddata( 32, 1 );
    alarm(50);
    while ( -e $queuedir . '/' . $rndfile . '.msg' ) {
        $rndfile = BoxTrapper_getranddata( 32, 1 );
    }
    alarm(0);
    return $rndfile . '.msg';
}

sub BoxTrapper_getranddata {
    my $size       = shift;
    my $sendheader = shift;
    if ( $size eq '' ) { $size = 10; }
    my $readsize = ( $size * 16 );

    my $rndpass = '';
    open( URAND, '/dev/urandom' ) || do {
        print "Fatal Error: Unable to read data from /dev/urandom ($!).  Please contact your system admin to have them repair the problem.\n";
        exit 1;
    };
    while ( length($rndpass) < $size ) {
        read URAND, $rndpass, $readsize;
        $rndpass =~ s/\W//g;
    }
    $rndpass = substr( $rndpass, 0, $size );
    return $rndpass;
}

sub BoxTrapper_getsender {
    my $hdref = shift;
    return BoxTrapper_extractaddress( BoxTrapper_getheader( 'from', $hdref ) );
}

sub BoxTrapper_getrecievedfrom {
    my $header = shift;
    $header =~ $Cpanel::Regex::regex{'getrecievedfrom'};
    return $1;
}

sub BoxTrapper_gettransportmethod {
    my $header = shift;
    $header =~ $Cpanel::Regex::regex{'getemailtransport'};
    return $1;
}

sub BoxTrapper_getwebdomain {
    my $webdomain = shift;

    if ( $webdomain eq '' ) {
        $webdomain = Cpanel::Sys::gethostname();
    }
    else {
        $webdomain = 'mail.' . $webdomain;
    }
    return $webdomain;
}

sub BoxTrapper_isenabled {
    my $account = shift;
    if ( !$account ) {
        Cpanel::Logger::cplog( 'Missing argument', 'warn', __PACKAGE__, 1 );
        return;
    }
    my $emaildir = ( BoxTrapper_getemaildirs( $account, ( BoxTrapper_getaccountinfo($account) )[0] ) )[0];
    return if !$emaildir;
    return -e $emaildir . '/.boxtrapperenable' ? 1 : 0;
}

sub BoxTrapper_isfromself {
    my ( $email, $account, $froms ) = @_;
    foreach my $from ( $account, split( /\s*\,\s*/, $froms ) ) {
        return 1 if lc $email eq lc $from;
    }
    return;
}

sub BoxTrapper_isinarray {
    my ( $item, @ARRAY ) = @_;
    foreach my $key (@ARRAY) {
        return 1 if $key eq $item;
    }
    return;
}

sub BoxTrapper_listmsgs {
    my ( $account, $editfile, $resetfile ) = @_;
    if ( $Cpanel::appname eq 'webmail' ) {
        $account = $Cpanel::authuser;
    }
    if ( !$account ) {
        Cpanel::Logger::cplog( 'Missing arguments', 'warn', __PACKAGE__, 1 );
        return;
    }
    if ( !$editfile ) {
        $editfile = 'editmsg.html';
    }
    if ( !$resetfile ) {
        $resetfile = 'resetmsg.html';
    }

    my ( $homedir, $domain ) = BoxTrapper_getaccountinfo($account);
    if ( !$homedir ) {
        print $Cpanel::Lang::LANG{$lang}{'unablelocate-BoxTrapper'} . " $account\n";
        return;
    }
    my ( $emaildir, $emaildeliverdir ) = BoxTrapper_getemaildirs( $account, $homedir );
    return if !$emaildir;

    if ( opendir my $fm_dh, '/usr/local/cpanel/etc/boxtrapper/forms' ) {
        while ( my $form = readdir $fm_dh ) {
            next if ( $form =~ m/^\./ || $form !~ m/(.+)\.txt$/ );
            $form = $1;
            print <<"EOM";
<tr>
    <td>$form</td>
    <td>
        <form action="$editfile">
            <input type="hidden" name="account" value="$account">
            <input type="hidden" name="form" value="${form}.txt">
            <input type="hidden" name="emaildir" value="${emaildir}/.boxtrapper/forms">
            <input type="submit" class="input-button" value="$Cpanel::Lang::LANG{$lang}{'editbtn-BoxTrapper'}">
        </form>
    </td>
    <td>
        <form action="$resetfile">
            <input type="hidden" name="account" value="$account">
            <input type="hidden" name="form" value="${form}.txt">
            <input type="hidden" name="emaildir" value="$emaildir/.boxtrapper/forms">
            <input type="submit" class="input-button" value="$Cpanel::Lang::LANG{$lang}{'resetdefault-BoxTrapper'}">
        </form>
    </td>
</tr>
EOM
        }
        closedir $fm_dh;
    }
    else {
        Cpanel::Logger::cplog("Unable to open BoxTrapper forms directory: $!", 'warn', __PACKAGE__, 1 );
    }
}

sub BoxTrapper_listpops {
    my @ARR;
    my $handoff = $Cpanel::root . '/cpanel-email';
    if ( -e $Cpanel::root . '/cpanel-email.pl' ) {
        $handoff = $Cpanel::root . '/cpanel-email.pl';
    }
    open( HAND, '-|' ) || exec( $handoff, 'listpops' );
    while (<HAND>) {
        chomp();
        push( @ARR, $_ );
    }
    close(HAND);
    return wantarray ? @ARR : \@ARR;
}

sub BoxTrapper_logdate {
    my $logdate = shift;
    if ( $logdate eq '' ) { $logdate = time(); }
    my ( $mon, $mday, $year ) = BoxTrapper_nicedate($logdate);
    print $mon . '-' . $mday . '-' . $year;
}

sub BoxTrapper_loadconf {
    my $emaildir = shift;
    my $account  = shift;
    my %CNF;
    open( CF, '<', $emaildir . '/boxtrapper.conf' );
    while (<CF>) {
        chomp();
        my ( $name, $value ) = split( /=/, $_, 2 );
        if ( $name ne '' ) {
            $CNF{$name} = $value;
        }
    }
    close(CF);

    if ( $CNF{'stale-queue-time'} eq '' ) { $CNF{'stale-queue-time'} = 15; }
    if ( $CNF{'froms'}            eq '' ) { $CNF{'froms'}            = $account; }

    return %CNF;
}

sub BoxTrapper_loadfwdlist {
    my $dir = shift;
    my @FWDLIST;
    open( FWDLIST, '<', $dir . '/.boxtrapper/forward-list.txt' );
    while (<FWDLIST>) {
        s/\r?\n$//g;
        next if ( $_ eq '' );
        push( @FWDLIST, $_ );
    }
    return @FWDLIST;
}

sub BoxTrapper_logcontrols {
    my $logdate = shift;
    my $account = shift;
    if ( $Cpanel::appname eq 'webmail' ) {
        $account = $Cpanel::authuser;
    }
    my $bxaction = shift;
    if ( $logdate eq '' ) { $logdate = time; }
    print '<td align="left">';
    my $nd = ( $logdate - 86400 );
    my ( $mon, $mday, $year ) = BoxTrapper_nicedate($nd);
    print "<a href=\"/$ENV{'SCRIPT_URI'}?bxaction=${bxaction}&account=${account}&logdate=${nd}\">&lt;&lt; ${mon}-${mday}-${year}</a>\n";
    print "</td><td align=\"center\">\n";
    ( $mon, $mday, $year ) = BoxTrapper_nicedate($logdate);
    print "${mon}-${mday}-${year}";
    print "</td><td align=\"right\">\n";
    $nd = ( $logdate + 86400 );
    ( $mon, $mday, $year ) = BoxTrapper_nicedate($nd);
    print "<a href=\"/$ENV{'SCRIPT_URI'}?bxaction=${bxaction}&account=${account}&logdate=${nd}\">${mon}-${mday}-${year} &gt;&gt;</a>\n";
    print '</td>';
}

sub BoxTrapper_logmatch {
    my $dir     = shift;
    my $list    = shift;
    my $header  = shift;
    my $match   = shift;
    my $linenum = shift;
    BoxTrapper_clog( 3, $dir, "Email matches rule \"$header $match\" Line $linenum in ${list}list" );
}

sub BoxTrapper_loopprotect {
    my $from     = shift;
    my $emaildir = shift;
    my %LOOPTIMES;
    $emaildir =~ s/$Cpanel::Regex::regex{doubledot}//g;
    my $loopprofile = $emaildir . '/.boxtrapper/loopprotect';
    my $looplock = SafeFile::safeopen( \*LOOPCONTROL, '<', $loopprofile );
    while (<LOOPCONTROL>) {
        chomp();
        my ( $email, $time ) = split( /=/, $_, 2 );
        $LOOPTIMES{$email} = $time;
    }
    SafeFile::safeclose( \*LOOPCONTROL, $looplock );

    my ( $lastrespond, $respondcount ) = split( /=/, $LOOPTIMES{$from} );
    my $now = time();

    if ( ( $lastrespond + ( 60 * 30 ) ) < $now ) {
        $respondcount = 0;
    }
    if ( $lastrespond > $now ) {
        $lastrespond = $now;
    }
    $respondcount++;

    $looplock = SafeFile::safeopen( \*LOOPCONTROL, '>', $loopprofile );
    $LOOPTIMES{$from} = $now . '=' . $respondcount;

    foreach my $email ( keys %LOOPTIMES ) {
        my ( $rtime, $rcount ) = split( /=/, $LOOPTIMES{$email} );
        if ( ( $rtime + ( 60 * 60 * 24 ) ) < $now ) {
            next;
        }
        print LOOPCONTROL $email . '=' . $rtime . '=' . $rcount . "\n";
    }
    SafeFile::safeclose( \*LOOPCONTROL, $looplock );

    if ( ( $lastrespond + ( 30 * 60 ) ) > $now && $respondcount > 5 ) {    # loopcontrol
        BoxTrapper_clog( 2, $emaildir, "Exiting for loop protection ($from) respondcount=$respondcount" );
        exit 0;
    }
}

sub BoxTrapper_maildircheck {
    if ( defined($maildir) ) {
        return $maildir;
    }
    if ( !$rCPCONF || ref $rCPCONF ne 'HASH' ) { $rCPCONF = Cpanel::Config::loadcpconf() }
    if ( $$rCPCONF{'maildir'} eq '1' ) { return 1; }
    return 0;
}

sub BoxTrapper_messageaction {
    my ( $account, $logdate, $queuefile, $action ) = @_;
    if ( $Cpanel::appname eq 'webmail' ) {
        $account = $Cpanel::authuser;
    }
    my ( $homedir, $domain ) = BoxTrapper_getaccountinfo($account);
    if ( !$homedir ) {
        print $Cpanel::Lang::LANG{$lang}{'unablelocate-BoxTrapper'} . " $account\n";
        return;
    }

    my ( $emaildir, $emaildeliverdir ) = BoxTrapper_getemaildirs( $account, $homedir );
    if ( !$emaildir ) {
        print $Cpanel::Lang::LANG{$lang}{'unablelocate-BoxTrapper'} . " $account\n";
        return;
    }

    my @ACTIONS = split( /\,/, $action );
    if ( !@ACTIONS ) {
        Cpanel::Logger::cplog( 'Missing arguments', 'warn', __PACKAGE__, 1 );
        return;
    }

    my ( $headersref, $email );
    if ( $queuefile ) {
        $queuefile =~ s/$Cpanel::Regex::regex{'doubledot'}//g;
        $headersref = BoxTrapper_getheadersfromfile( $emaildir . '/boxtrapper/queue/' . $queuefile ); 
        $email = BoxTrapper_extractaddress( BoxTrapper_getheader( 'from', $headersref ) );
        $email = Cpanel::Encoder::html_encode_str($email);
        $email     =~ s/$Cpanel::Regex::regex{'doubledot'}//g;
    }

    foreach my $action (@ACTIONS) {
        if ( $action eq 'deliverall' && !-e $emaildir . '/boxtrapper/verifications/' . $email ) {
            $action = 'deliver';
        }

        if ( $action eq 'whitelist' ) {
            print $email . ' ' . $Cpanel::Lang::LANG{$lang}{'whtlistadd-BoxTrapper'} . "<br/>\n";
            BoxTrapper_addaddytolist( 'white', $email, $emaildir );
        }
        elsif ( $action eq 'blacklist' ) {
            print $email . ' ' . $Cpanel::Lang::LANG{$lang}{'blklistadd-BoxTrapper'} . "<br/>\n";
            BoxTrapper_addaddytolist( 'black', $email, $emaildir );
        }
        elsif ( $action eq 'ignorelist' ) {
            print $email . ' ' . $Cpanel::Lang::LANG{$lang}{'ignlistadd-BoxTrapper'} . "<br/>\n";
            BoxTrapper_addaddytolist( 'ignore', $email, $emaildir );
        }
        elsif ( $action eq 'deliverall' ) {
            my $verflock = SafeFile::safeopen( \*MSGIDS, '<', $emaildir . '/boxtrapper/verifications/' . $email );
            my @failed_removal;
            my @msgids;
            while ( my $msgidr = <MSGIDS> ) {
                chomp $msgidr;
                next if !-e $emaildir . '/boxtrapper/queue/' . $msgidr . '.msg';
                my ( $headersref, $rbody ) = BoxTrapper_extractall( $emaildir . '/boxtrapper/queue/' . $msgidr . '.msg' );
                if ( !@{$headersref} ) {
                    BoxTrapper_clog( 2, $emaildir, "Skipping deliverall of message $msgidr as it is not in the queue (from a deliverall)" );
                    next;
                }
                push @{$headersref}, "X-BoxTrapper-Queue: released via web action: deliverall\n";

                if ( BoxTrapper_maildircheck() ) {
                    BoxTrapper_delivermessage( 1, $emaildir, $emaildeliverdir, $headersref, $rbody );
                }
                else {
                    BoxTrapper_delivermessage( 1, $emaildir, $emaildeliverdir . '/inbox', $headersref, $rbody );
                }
                if ( unlink $emaildir . '/boxtrapper/queue/' . $msgidr . '.msg' ) {
                    push @msgids, $msgidr;
                }
                else {
                    push @failed_removal, $msgidr;
                    BoxTrapper_clog( 2, $emaildir, "Unable to remove delivered message ${msgidr}.msg from queue: $!" );
                    Cpanel::Logger::cplog( "Unable to unlink $emaildir/boxtrapper/queue/${msgidr}.msg: $!", 'warn', __PACKAGE__, 1 );
                }
            }
            SafeFile::safeclose( \*MSGIDS, $verflock );
            if ( @msgids ) {
                BoxTrapper_removefromsearchdb( $emaildir, \@msgids );
            }
            if ( @failed_removal ) {
                print "Unable to remove some queued messages. Check the BoxTrapper log for more details.<br />\n";
                my $verflock = SafeFile::safeopen( \*MSGIDS, '>', $emaildir . '/boxtrapper/verifications/' . $email );
                print MSGIDS join( "\n", @failed_removal ) . "\n";
                SafeFile::safeclose( \*MSGIDS, $verflock );
            }
            else {
                if ( unlink $emaildir . '/boxtrapper/verifications/' . $email ) {
                    print "Queued messages from $email delivered.<br />\n";
                }
                else {
                    Cpanel::Logger::cplog( "Failed to unlink $emaildir/boxtrapper/verifications/$email: $!", 'warn', __PACKAGE__, 1 );
                    print "Queued messages from $email delivered, but there was a problem.<br />\n";
                }
            }
        }
        elsif ( $action eq 'deliver' ) {
            if (!-e $emaildir . '/boxtrapper/queue/' . $queuefile) {
                print "No message found to deliver.<br />\n";
                return;
            }
            print $Cpanel::Lang::LANG{$lang}{'queuedmsgbegin-BoxTrapper'} . " $email " . $Cpanel::Lang::LANG{$lang}{'queuedmsgdlvend-BoxTrapper'} . "\n";

            my @body = BoxTrapper_extractbody( $emaildir . '/boxtrapper/queue/' . $queuefile );
            push @{$headersref}, "X-BoxTrapper-Queue: released via web action: deliver\n";

            if ( BoxTrapper_maildircheck() ) {
                BoxTrapper_delivermessage( 1, $emaildir, $emaildeliverdir, $headersref, \@body );
            }
            else {
                BoxTrapper_delivermessage( 1, $emaildir, $emaildeliverdir . '/inbox', $headersref, \@body );
            }
            if ( unlink $emaildir . '/boxtrapper/queue/' . $queuefile ) {
                BoxTrapper_removefromsearchdb( $emaildir, $queuefile );
            }
            else {
                BoxTrapper_clog( 2, $emaildir, "Unable to remove delivered message $emaildir/boxtrapper/queue/$queuefile from queue: $!" );
                Cpanel::Logger::cplog( "Unable to unlink $emaildir/boxtrapper/queue/$queuefile: $!", 'warn', __PACKAGE__, 1 );
            }
        }
        elsif ( $action eq 'delete' ) {
            if ( !-e $emaildir . '/boxtrapper/queue/' . $queuefile ) {
                print "No message found to delete.<br />\n";
                return;
            }
            if ( unlink $emaildir . '/boxtrapper/queue/' . $queuefile ) {
                print $Cpanel::Lang::LANG{$lang}{'queuedmsgbegin-BoxTrapper'} . " ${email} " . $Cpanel::Lang::LANG{$lang}{'queuedmsgdeleteend-BoxTrapper'} . "\n";
                BoxTrapper_clog( 2, $emaildir, "Deleted $queuefile from $email" );
                BoxTrapper_removefromsearchdb( $emaildir, $queuefile );
            }
            else {
                print "Unable to delete message.<br />\n";
                Cpanel::Logger::cplog( "Failed to unlink $emaildir/boxtrapper/queue/$queuefile: $!", 'warn', __PACKAGE__, 1 );
                BoxTrapper_clog( 2, $emaildir, "Unable to delete ${emaildir}/boxtrapper/queue/${queuefile}: $!" );
            }
        }
        elsif ( $action eq 'deleteall' ) {
            if ( !-e $emaildir . '/boxtrapper/verifications/' . $email ) {
                print "No verification list found for $email.<br />\n";
                return;
            }
            my $verflock = SafeFile::safeopen( \*MSGIDS, $emaildir . '/boxtrapper/verifications/' . $email );
            my @msgids;
            while ( my $msgidr = <MSGIDS> ) {
                chomp $msgidr;
                if ( -e $emaildir . '/boxtrapper/queue/' . $msgidr . '.msg' ) {
                    if ( unlink $emaildir . '/boxtrapper/queue/' . $msgidr . '.msg' ) {
                        BoxTrapper_clog( 2, $emaildir, "Deleted ${msgidr}.msg from $email" );
                        push @msgids, $msgidr;
                    }
                    else {
                        Cpanel::Logger::cplog( "Failed to unlink $emaildir/boxtrapper/queue/${msgidr}.msg: $!", 'warn', __PACKAGE__, 1 );
                        BoxTrapper_clog( 2, $emaildir, "Unable to delete ${emaildir}/boxtrapper/queue/${msgidr}.msg: $!" );
                    }
                }
            }
            SafeFile::safeclose( \*MSGIDS, $verflock );
            unlink $emaildir . '/boxtrapper/verifications/' . $email;
            print "Queued messages from $email removed.<br />\n";
            BoxTrapper_removefromsearchdb( $emaildir, \@msgids );
        }
    }
}

sub BoxTrapper_multimessageaction {
    my $account = $Cpanel::FORM{'account'};
    my $action = $Cpanel::FORM{'multimsg'};

    if ( $Cpanel::appname eq 'webmail' ) {
        $account = $Cpanel::authuser;
    }
    if ( !$account ) {
        Cpanel::Logger::cplog( 'Missing arguments', 'warn', __PACKAGE__, 1 );
        return;
    }
    
    my ( $homedir, $domain ) = BoxTrapper_getaccountinfo($account);
    if ( !$homedir ) {
        print $Cpanel::Lang::LANG{$lang}{'unablelocate-BoxTrapper'} . " $account\n";
        return;
    }

    my ( $emaildir, $emaildeliverdir ) = BoxTrapper_getemaildirs( $account, $homedir );

    my @msgids;

    foreach my $msg_id ( sort keys %Cpanel::FORM ) {
        next if ( $msg_id !~ m/^msgid\d+/ || !$Cpanel::FORM{$msg_id} );
        my $queuefile = $Cpanel::FORM{$msg_id};
        $queuefile =~ s/\///g;
        next if ( !$queuefile || !-e $emaildir . '/boxtrapper/queue/' . $queuefile );

        my ( $HEADERS, $rbody ) = BoxTrapper_extractall( $emaildir . '/boxtrapper/queue/' . $queuefile );
        my $email = BoxTrapper_extractaddress( BoxTrapper_getheader( 'from', $HEADERS ) );

        if ( $action eq 'whitelistall' ) {
            BoxTrapper_addaddytolist( 'white', $email, $emaildir );

            push @{$HEADERS}, "X-BoxTrapper-Queue: released via web multiaction: whitelistall\n";
            if ( BoxTrapper_maildircheck() ) {
                BoxTrapper_delivermessage( 1, $emaildir, $emaildeliverdir, $HEADERS, $rbody );
            }
            else {
                BoxTrapper_delivermessage( 1, $emaildir, $emaildeliverdir . '/inbox', $HEADERS, $rbody );
            }

            if ( unlink $emaildir . '/boxtrapper/queue/' . $queuefile ) {
                push @msgids, $queuefile;
                print $Cpanel::Lang::LANG{$lang}{'queuedmsgbegin-BoxTrapper'} . " $email " . $Cpanel::Lang::LANG{$lang}{'queuedmsgdlvend-BoxTrapper'} . "<br />\n";
            }
        }
        elsif ( $action eq 'deleteall' ) {
            if ( unlink $emaildir . '/boxtrapper/queue/' . $queuefile ) {
                push @msgids, $queuefile;
                print $Cpanel::Lang::LANG{$lang}{'queuedmsgbegin-BoxTrapper'} . " $email " . $Cpanel::Lang::LANG{$lang}{'queuedmsgdeleteend-BoxTrapper'} . "<br />\n";
                BoxTrapper_clog( 2, $emaildir, "Deleted $queuefile from $email" );
            }
        }
    }
    BoxTrapper_removefromsearchdb( $emaildir, \@msgids );
}

sub BoxTrapper_nicedate {
    my $date = shift;
    my ( $mday, $mon, $year, $hour, $min, $sec );
    ( $sec, $min, $hour, $mday, $mon, $year, undef ) = localtime($date);
    $year += 1900;
    $mon  += 1;
    $mon  = sprintf( "%02d", $mon );
    $mday = sprintf( "%02d", $mday );
    $sec  = sprintf( "%02d", $sec );
    $min  = sprintf( "%02d", $min );
    $hour = sprintf( "%02d", $hour );

    return ( $mon, $mday, $year, $hour, $min, $sec );
}

sub BoxTrapper_removefromsearchdb {
    my ( $dir, $msgidlist ) = @_;
    my $sdblock = SafeFile::safeopen( \*SDB, '+<', $dir . '/boxtrapper/search.db' );
    my $sdb;
    eval { $sdb = _loadsearchdb( \*SDB ); };
    if ( ref $msgidlist eq 'ARRAY' ) {
        foreach my $msgid ( @{$msgidlist} ) {
            delete $sdb->{ Cpanel::StringFunc::endtrim( $msgid, '.msg' ) };
        }
    }
    else {
        delete $sdb->{ Cpanel::StringFunc::endtrim( $msgidlist, '.msg' ) };
    }
    seek( SDB, 0, 0 );
    _writesearchdb( $sdb, \*SDB );
    truncate( SDB, tell(SDB) );
    SafeFile::safeclose( \*SDB, $sdblock );
}

sub BoxTrapper_updatesearchdb {
    my ( $dir, $msgid, $headers ) = @_;
    my $from    = BoxTrapper_getheader( 'from',    $headers );
    my $subject = BoxTrapper_getheader( 'subject', $headers );
    $subject =~ s/[\r\n]//g;
    $from    =~ s/[\r\n]//g;
    $msgid = Cpanel::StringFunc::endtrim( $msgid, '.msg' );

    if ( !-e $dir . '/boxtrapper/search.db' ) {
        BoxTrapper_rebuildsearchdb($dir);
        return;
    }

    my $sdblock = SafeFile::safeopen( \*SDB, '>>', $dir . '/boxtrapper/search.db' );
    my $sdb;
    $sdb->{$msgid}->{'from'}    = [$from];
    $sdb->{$msgid}->{'subject'} = [$subject];
    _writesearchdb( $sdb, \*SDB, 1 );
    SafeFile::safeclose( \*SDB, $sdblock );
}

sub BoxTrapper_rebuildsearchdb {
    my ($emaildir) = @_;

    my $sdb;
    opendir( QDIR, $emaildir . '/boxtrapper/queue' );
    while ( my $queuefile = readdir(QDIR) ) {
        next if ( $queuefile =~ /^\./ || $queuefile =~ /\.lock$/ );
        my $msgid   = Cpanel::StringFunc::endtrim( $queuefile, '.msg' );
        my $headers = BoxTrapper_getheadersfromfile( $emaildir . '/boxtrapper/queue/' . $queuefile );
        my $from    = BoxTrapper_getheader( 'from', $headers );
        my $subject = BoxTrapper_getheader( 'subject', $headers );
        $subject =~ s/[\r\n]//g;
        $from    =~ s/[\r\n]//g;

        $sdb->{$msgid}->{'from'}    = [$from];
        $sdb->{$msgid}->{'subject'} = [$subject];
    }
    closedir(QDIR);

    my $sdblock = SafeFile::safeopen( \*SDB, '>', $emaildir . '/boxtrapper/search.db' );
    _writesearchdb( $sdb, \*SDB, 1 );
    SafeFile::safeclose( \*SDB, $sdblock );
}

sub BoxTrapper_queuemessage {
    my ( $dir, $email, $msgid ) = @_;
    $email =~ s/$Cpanel::Regex::regex{multipledot}/$Cpanel::Regex::regex{singledot}/g;
    $email =~ s/$Cpanel::Regex::regex{forwardslash}//g;
    $msgid = Cpanel::StringFunc::endtrim( $msgid, '.msg' );
    my $mboxlock = SafeFile::safeopen( \*MBOX, '>>', $dir . '/boxtrapper/verifications/' . $email );
    print MBOX $msgid . "\n";
    SafeFile::safeclose( \*MBOX, $mboxlock );
}

sub BoxTrapper_resetmsg {
    my $account = shift;
    if ( $Cpanel::appname eq 'webmail' ) {
        $account = $Cpanel::authuser;
    }
    my $message = shift;
    my ( $homedir, $domain ) = BoxTrapper_getaccountinfo($account);
    if ( !$homedir ) {
        print $Cpanel::Lang::LANG{$lang}{'unablelocate-BoxTrapper'} . " $account\n";
        return;
    }
    $message =~ s/$Cpanel::Regex::regex{'forwardslash'}//g;
    $message =~ s/$Cpanel::Regex::regex{'doubledot'}//g;
    my ( $emaildir, $emaildeliverdir ) = BoxTrapper_getemaildirs( $account, $homedir );
    if ( -e $emaildir . '/.boxtrapper/forms/' . $message ) {
        unlink( $emaildir . '/.boxtrapper/forms/' . $message );
    }
}

sub BoxTrapper_saveconf {
    my ( $account, $froms, $queuetime, $autowhitelist, $fromname ) = @_;
    if ( $Cpanel::appname eq 'webmail' ) {
        $account = $Cpanel::authuser;
    }
    my ( $homedir, $domain ) = BoxTrapper_getaccountinfo($account);
    if ( !$homedir ) {
        print $Cpanel::Lang::LANG{$lang}{'unablelocate-BoxTrapper'} . ' ' . $account . "\n";
        return;
    }
    my ( $emaildir, $emaildeliverdir ) = BoxTrapper_getemaildirs( $account, $homedir );
    if ( $emaildir eq "" || $emaildeliverdir eq "" ) {
        print $Cpanel::Lang::LANG{$lang}{'noemaildirbegin-BoxTrapper'} . ' ' . $account . ' ' . $Cpanel::Lang::LANG{$lang}{'noemaildirend-BoxTrapper'} . "\n";
        return;
    }

    $froms     =~ s/\n//g;
    $queuetime =~ s/\n//g;
    $fromname  =~ s/\n//g;
    $fromname  =~ s/\"//g;

    my $conflock = SafeFile::safeopen( \*CF, '>', $emaildir . '/boxtrapper.conf' );
    print CF "froms=${froms}\n";
    print CF "stale-queue-time=${queuetime}\n";
    print CF "fromname=${fromname}\n";
    SafeFile::safeclose( \*CF, $conflock );

    if ( $autowhitelist && -e $emaildir . '/.boxtrapperautowhitelistdisable' ) {
        unlink( $emaildir . '/.boxtrapperautowhitelistdisable' );
    }
    elsif ( !$autowhitelist ) {
        open( AW, '>', $emaildir . '/.boxtrapperautowhitelistdisable' );
        close(AW);
    }
}

sub BoxTrapper_sendformmessage {
    my ( $message, $emaildir, $email, $subject, $msgid, $rheaders, $webdomain, $acct, $id, $returnaddy, $rconf ) = @_;
    $emaildir =~ s/$Cpanel::Regex::regex{'doubledot'}//g;
    $message  =~ s/$Cpanel::Regex::regex{'doubledot'}//g;
    $msgid = Cpanel::StringFunc::endtrim( $msgid, '.msg' );
    my $headers = join( '', @{$rheaders} );

    if ( -e $emaildir . '/.boxtrapper/forms/' . $message . '.txt' ) {
        open( FL, '<', $emaildir . '/.boxtrapper/forms/' . $message . '.txt' );
    }
    else {
        open( FL, '<', '/usr/local/cpanel/etc/boxtrapper/forms/' . $message . '.txt' );
    }
    open( SM, "|/usr/sbin/sendmail -ti" );
    print SM "X-Boxtrapper: $id\n";
    my $fromname;
    if ( $rconf->{'fromname'} && $rconf->{'fromname'} ne '' ) {
        $fromname = $rconf->{'fromname'};
        print SM "From: \"" . $rconf->{'fromname'} . "\" <$returnaddy>\n";
    }
    else {
        $fromname = $returnaddy;
        print SM "From: $returnaddy\n";
    }
    while (<FL>) {
        s/%acct%/$acct/g;
        s/%msgid%/$msgid/g;
        s/%subject%/$subject/g;
        s/%email%/$email/g;
        s/%headers%/$headers/g;
        s/%fromname%/$fromname/g;
        s/%webdomain%/$webdomain/g;
        print SM;
    }
    close(SM);
    close(FL);
}

sub BoxTrapper_showautowhitelist {
    my $account = shift;
    if ( $Cpanel::appname eq 'webmail' ) {
        $account = $Cpanel::authuser;
    }
    my ( $homedir, $domain ) = BoxTrapper_getaccountinfo($account);
    if ( !$homedir ) {
        print $Cpanel::Lang::LANG{$lang}{'unablelocate-BoxTrapper'} . " $account\n";
        return;
    }
    my ( $emaildir, $emaildeliverdir ) = BoxTrapper_getemaildirs( $account, $homedir );
    if ( $emaildir eq '' || $emaildeliverdir eq '' ) {
        print $Cpanel::Lang::LANG{$lang}{'noemaildirbegin-BoxTrapper'} . " ${account} " . $Cpanel::Lang::LANG{$lang}{'noemaildirend-BoxTrapper'} . "\n";
        return;
    }
    if ( !-e $emaildir . '/.boxtrapperautowhitelistdisable' ) {
        print 'checked="checked"';
    }
}

sub BoxTrapper_showemails {
    my $account = shift;
    if ( $Cpanel::appname eq 'webmail' ) {
        $account = $Cpanel::authuser;
    }
    my ( $homedir, $domain ) = BoxTrapper_getaccountinfo($account);
    if ( !$homedir ) {
        print $Cpanel::Lang::LANG{$lang}{'unablelocate-BoxTrapper'} . ' ' . $account . "\n";
        return;
    }
    my ( $emaildir, $emaildeliverdir ) = BoxTrapper_getemaildirs( $account, $homedir );
    if ( $emaildir eq '' || $emaildeliverdir eq '' ) {
        print $Cpanel::Lang::LANG{$lang}{'noemaildirbegin-BoxTrapper'} . ' ' . $account . ' ' . $Cpanel::Lang::LANG{$lang}{'noemaildirend-BoxTrapper'} . "\n";
        return;
    }
    my %CONF = BoxTrapper_loadconf( $emaildir, $account );
    print $CONF{'froms'};
}

sub BoxTrapper_showlog {
    my $logdate = shift || '';
    my $account = shift;
    if ( $Cpanel::appname eq 'webmail' ) {
        $account = $Cpanel::authuser;
    }
    if ( $logdate eq '' ) { $logdate = time(); }
    my ( $mon, $mday, $year ) = BoxTrapper_nicedate($logdate);
    my ( $homedir, $domain ) = BoxTrapper_getaccountinfo($account);
    if ( !$homedir ) {
        print $Cpanel::Lang::LANG{$lang}{'unablelocate-BoxTrapper'} . " ${account}\n";
        return;
    }

    my ( $emaildir, $emaildeliverdir ) = BoxTrapper_getemaildirs( $account, $homedir );

    if ( $emaildir eq '' || $emaildeliverdir eq '' ) {
        print $Cpanel::Lang::LANG{$lang}{'noemaildirbegin-BoxTrapper'} . " ${account} " . $Cpanel::Lang::LANG{$lang}{'noemaildirend-BoxTrapper'} . "\n";
        return;
    }

    open( CLOG, '<', $emaildir . '/boxtrapper/log/' . $mon . '-' . $mday . '-' . $year . '.log' );
    while (<CLOG>) {
        print Cpanel::Encoder::html_encode_str($_);
    }
    close(CLOG);
}

sub BoxTrapper_showmessage {
    my ( $account, $logdate, $queuefile ) = @_;
    if ( $Cpanel::appname eq 'webmail' ) {
        $account = $Cpanel::authuser;
    }
    $queuefile =~ s/\.\.//g;
    my ( $homedir, $domain ) = BoxTrapper_getaccountinfo($account);
    if ( !$homedir ) {
        print $Cpanel::Lang::LANG{$lang}{'unablelocate-BoxTrapper'} . " ${account}\n";
        return;
    }

    my ( $emaildir, $emaildeliverdir ) = BoxTrapper_getemaildirs( $account, $homedir );

    if ( $emaildir eq '' || $emaildeliverdir eq '' ) {
        print $Cpanel::Lang::LANG{$lang}{'noemaildirbegin-BoxTrapper'} . " ${account} " . $Cpanel::Lang::LANG{$lang}{'noemaildirend-BoxTrapper'} . "\n";
        return;
    }

    my $lines = 0;

    open( QF, '<', $emaildir . '/boxtrapper/queue/' . $queuefile );
    while (<QF>) {
        $lines++;
        last if ( $lines > 200 );
        chomp();
        $_ = Cpanel::Encoder::html_encode_str($_);
        print;
        print "\n";
    }
    close(QF);
}

sub BoxTrapper_showqueue {
    my ( $logdate, $account, $showfile, $bxaction, $skipcontrols ) = @_;
    if ( $Cpanel::appname eq 'webmail' ) {
        $account = $Cpanel::authuser;
    }
    if ( $logdate eq '' ) { $logdate = time(); }
    my ( $mon, $mday, $year ) = BoxTrapper_nicedate($logdate);
    my ( $homedir, $domain ) = BoxTrapper_getaccountinfo($account);
    if ( !$homedir ) {
        print $Cpanel::Lang::LANG{$lang}{'unablelocate-BoxTrapper'} . " $account\n";
        return;
    }

    my ( $emaildir, $emaildeliverdir ) = BoxTrapper_getemaildirs( $account, $homedir );

    if ( $emaildir eq '' || $emaildeliverdir eq '' ) {
        print $Cpanel::Lang::LANG{$lang}{'noemaildirbegin-BoxTrapper'} . " ${account} " . $Cpanel::Lang::LANG{$lang}{'noemaildirend-BoxTrapper'} . "\n";
        return;
    }

    my (%BXMSG);
    opendir( QDIR, "${emaildir}/boxtrapper/queue" );
    my @QDIR = readdir(QDIR);
    closedir(QDIR);
    @QDIR = grep( !/^\./,     @QDIR );
    @QDIR = grep( !/\.lock$/, @QDIR );
    my $i = 0;
    foreach my $queuefile (@QDIR) {
        my $tt = ( stat("${emaildir}/boxtrapper/queue/${queuefile}") )[9];
        my ( $qmon, $qmday, $qyear, $qhour, $qmin, $qsec ) = BoxTrapper_nicedate($tt);
        if ( $qmday == $mday && $qmon == $mon && $qyear == $year ) {
            $i++;
            my @HEADERS;
            open( QF, "<", "${emaildir}/boxtrapper/queue/${queuefile}" );
            while (<QF>) {
                if (/^[\r\n]*$/) { last; }
                push( @HEADERS, $_ );
            }
            close(QF);
            my $email = BoxTrapper_extractaddress( BoxTrapper_getheader( 'from', \@HEADERS ) );
            my $subject = BoxTrapper_getheader( 'subject', \@HEADERS );
            $subject = Cpanel::Encoder::html_encode_str($subject);
            $email   = Cpanel::Encoder::html_encode_str($email);

            $BXMSG{$i}{'time'}      = $tt;
            $BXMSG{$i}{'queuefile'} = $queuefile;
            $BXMSG{$i}{'email'}     = $email;
            $BXMSG{$i}{'subject'}   = $subject;
            $BXMSG{$i}{'nicetime'}  = "$qmon/$qmday ${qhour}:${qmin}:${qsec}";
        }
    }

    my $showfiledelete = $showfile;
    my $multimsgaction = $showfile;
    if ( $showfile eq 'showmsg.html' ) {
        $showfiledelete = 'msgaction.html';
        $multimsgaction = 'multimsgaction.html';
    }

    my $bg = '2';
    if ( !$skipcontrols ) {
        print qq{<form name="input" action="$multimsgaction" method="GET">\n};
    }
    print qq{<input type="hidden" name="account" value="$account">\n};
    print qq{<input type="hidden" name="bxaction" value="multimsgaction">\n};
    my $i = 0;
    foreach my $msg ( sort { $BXMSG{$a}{'time'} <=> $BXMSG{$b}{'time'} } keys %BXMSG ) {
        my $new_subject = Cpanel::StringFunc::textbreak( $BXMSG{$msg}{'subject'} );
        print <<"EOM";
        <tr class="tdshade${bg}">
            <td width="5%"><input type="checkbox" name="msgid${i}" value="$BXMSG{$msg}{'queuefile'}"></td>
            <td class="truncate" truncate="25" width="15%"><a href="${showfile}?account=${account}&q=$BXMSG{$msg}{'queuefile'}&bxaction=${bxaction}">$BXMSG{$msg}{'email'}</a></td>
            <td class="truncate" truncate="75"><a href="${showfile}?account=${account}&q=$BXMSG{$msg}{'queuefile'}&bxaction=${bxaction}">$new_subject</a></td>
            <td width="15%"><a href="${showfile}?account=${account}&q=$BXMSG{$msg}{'queuefile'}&bxaction=${bxaction}\">$BXMSG{$msg}{'nicetime'}</a></td>
        </tr>
EOM
        if ( $bg eq '2' ) { $bg = '1'; }
        else { $bg = '2'; }
        $i++;
    }
    if ( !$skipcontrols ) {
        print <<"EOM";
    <tr>
        <td colspan="2">
            <input type="radio" name="multimsg" value="deleteall">Delete</input><br />
            <input type="radio" name="multimsg" value="whitelistall">Whitelist & Deliver</input><br />
            <input type="submit" class="input-button" value="$Cpanel::Lang::LANG{$lang}{'submit-BoxTrapper'}">
        </td>
        <td colspan="3">&nbsp</td>
    </tr>
    </form>
EOM
    }
}

sub BoxTrapper_showqueuesearch {
    my ( $field, $string, $account, $showfile, $bxaction, $skipcontrols ) = @_;
    if ( $Cpanel::appname eq 'webmail' ) {
        $account = $Cpanel::authuser;
    }
    if ( !defined $string || $string eq '' ) {
        print <<"EOM";
<tr>
    <td>$Cpanel::Lang::LANG{$lang}{'nosrchresults-BoxTrapper'}</td>
    <td>$Cpanel::Lang::LANG{$lang}{'notavailable-BoxTrapper'}</td>
    <td>$Cpanel::Lang::LANG{$lang}{'notavailable-BoxTrapper'}</td>
    <td>$Cpanel::Lang::LANG{$lang}{'notavailable-BoxTrapper'}</td>
</tr>
EOM
        return;
    }

    my ( $homedir, $domain ) = BoxTrapper_getaccountinfo($account);
    if ( !$homedir ) {
        print $Cpanel::Lang::LANG{$lang}{'unablelocate-BoxTrapper'} . " $account\n";
        return;
    }

    my ( $emaildir, $emaildeliverdir ) = BoxTrapper_getemaildirs( $account, $homedir );

    if ( $emaildir eq '' || $emaildeliverdir eq '' ) {
        print $Cpanel::Lang::LANG{$lang}{'noemaildirbegin-BoxTrapper'} . " ${account} " . $Cpanel::Lang::LANG{$lang}{'noemaildirend-BoxTrapper'} . "\n";
        return;
    }

    my %BXMSG;
    my @invalid_search_db_entries;
    my $i = 0;
    if ( $field eq 'sender' || $field eq 'subject' ) {
        my $sdblock = SafeFile::safeopen( \*SDB, '+<', $emaildir . '/boxtrapper/search.db' );
        my $sdb;
        eval { $sdb = _loadsearchdb( \*SDB ); };
        SafeFile::safeclose( \*SDB, $sdblock );

        my $searchkey = 'from';
        if ( $field eq 'subject' ) { $searchkey = 'subject'; }
        foreach my $msg ( keys %{$sdb} ) {
            my $searchadd = 0;
            next if ref $sdb->{$msg}{$searchkey} ne 'ARRAY';    # Don't search field that doesn't exist
            foreach my $key ( @{ $sdb->{$msg}{$searchkey} } ) {
                if ( Cpanel::Encoder::html_decode_str($key) =~ m/$string/im ) {
                    $searchadd = 1;
                    last;
                }
            }

            next if !$searchadd;

            if ( -e $emaildir . '/boxtrapper/queue/' . $msg . '.msg' ) {    # Don't add message that doesn't exist to results
                my $tt = ( stat(_) )[9];
                my ( $qmon, $qmday, $qyear, $qhour, $qmin, $qsec ) = BoxTrapper_nicedate($tt);

                my $rHEADERS = BoxTrapper_getheadersfromfile( $emaildir . '/boxtrapper/queue/' . $msg . '.msg' );
                my $email    = BoxTrapper_extractaddress( BoxTrapper_getheader( 'from', $rHEADERS ) );
                my $subject  = BoxTrapper_getheader( 'subject', $rHEADERS );
                $subject = Cpanel::Encoder::html_encode_str($subject);
                $email   = Cpanel::Encoder::html_encode_str($email);

                $BXMSG{$i}{'time'}      = $tt;
                $BXMSG{$i}{'queuefile'} = $msg . '.msg';
                $BXMSG{$i}{'email'}     = $email;
                $BXMSG{$i}{'subject'}   = $subject;
                $BXMSG{$i}{'nicetime'}  = "$qmon/$qmday ${qhour}:${qmin}:${qsec}";
                $i++;
            }
            else {

                # Cpanel::Logger::cplog( "Missing queue message '$emaildir/boxtrapper/queue/$msg.msg'", 'info', __PACKAGE__, 1 );
                push @invalid_search_db_entries, $msg;
            }
        }
    }
    elsif ( $field eq 'body' ) {
        opendir( QDIR, $emaildir . '/boxtrapper/queue' );
        while ( my $queuefile = readdir(QDIR) ) {
            next if ( $queuefile =~ /^\./ || $queuefile =~ /\.lock$/ );

            my $searchadd = 0;
            my ( $rHEADERS, $rBODY ) = BoxTrapper_extractall("${emaildir}/boxtrapper/queue/${queuefile}");

            if ( grep( m/$string/im, @$rBODY ) ) {
                my $tt = ( stat("${emaildir}/boxtrapper/queue/${queuefile}") )[9];
                my ( $qmon, $qmday, $qyear, $qhour, $qmin, $qsec ) = BoxTrapper_nicedate($tt);

                my $email = BoxTrapper_extractaddress( BoxTrapper_getheader( 'from', $rHEADERS ) );
                my $subject = BoxTrapper_getheader( 'subject', $rHEADERS );
                $subject = Cpanel::Encoder::html_encode_str($subject);
                $email   = Cpanel::Encoder::html_encode_str($email);

                $BXMSG{$i}{'time'}      = $tt;
                $BXMSG{$i}{'queuefile'} = $queuefile;
                $BXMSG{$i}{'email'}     = $email;
                $BXMSG{$i}{'subject'}   = $subject;
                $BXMSG{$i}{'nicetime'}  = "$qmon/$qmday ${qhour}:${qmin}:${qsec}";
            }
            $i++;
        }
    }
    closedir(QDIR);

    # Delete invalid entries from search.db
    if (@invalid_search_db_entries) {
        BoxTrapper_removefromsearchdb( $emaildir, \@invalid_search_db_entries );
    }

    my $showfiledelete = $showfile;
    my $multimsgaction = $showfile;
    if ( $showfile eq 'queuesearch.html' ) {
        $showfiledelete = 'queuesearch.html';
        $multimsgaction = 'multimsgaction.html';
        $showfile       = 'showmsg.html';
    }

    if ( !$skipcontrols ) {
        print qq{<form name="input" action="$multimsgaction" method="GET">\n};
    }
    print qq{<input type="hidden" name="account" value="$account">\n};
    print qq{<input type="hidden" name="bxaction" value="multimsgaction">\n};

    if ( keys(%BXMSG) == 0 ) {
        print <<"EOM";
<tr>
    <td>$Cpanel::Lang::LANG{$lang}{'nosrchresults-BoxTrapper'}</td>
    <td>$Cpanel::Lang::LANG{$lang}{'notavailable-BoxTrapper'}</td>
    <td>$Cpanel::Lang::LANG{$lang}{'notavailable-BoxTrapper'}</td>
    <td>$Cpanel::Lang::LANG{$lang}{'notavailable-BoxTrapper'}</td>
</tr>
EOM
        return;
    }

    my $i = 0;
    my $bg = '2';
    foreach my $msg ( sort { $BXMSG{$a}{'time'} <=> $BXMSG{$b}{'time'} } keys %BXMSG ) {
        if ( !exists $BXMSG{$msg}{'email'} || $BXMSG{$msg}{'email'} eq '' ) {
            Cpanel::Logger::cplog( "BoxTrapper queued message $BXMSG{$msg}{'queuefile'} for user $Cpanel::user invalid", 'info', __PACKAGE__, 1 );
            next;
        }
        my $new_subject = Cpanel::StringFunc::textbreak( $BXMSG{$msg}{'subject'} );

        print <<"EOM";
        <tr class="tdshade${bg}">
            <td width="5%"><input type="checkbox" name="msgid${i}" value="$BXMSG{$msg}{'queuefile'}"></td>
            <td class="truncate" truncate="25" width="15%"><a href="${showfile}?account=${account}&q=$BXMSG{$msg}{'queuefile'}&bxaction=${bxaction}">$BXMSG{$msg}{'email'}</a></td>
            <td class="truncate" truncate="75"><a href="${showfile}?account=${account}&q=$BXMSG{$msg}{'queuefile'}&bxaction=${bxaction}">$new_subject</a></td>
            <td width=\"15%\"><a href="${showfile}?account=${account}&q=$BXMSG{$msg}{'queuefile'}&bxaction=${bxaction}\">$BXMSG{$msg}{'nicetime'}</a></td>
        </tr>
EOM
        if ( $bg eq '2' ) { $bg = '1'; }
        else { $bg = '2'; }
        $i++;
    }
    if ( !$skipcontrols ) {
        print <<"EOM";
    <tr>
        <td colspan="2">
            <input type="radio" name="multimsg" value="deleteall">Delete</input>
            <br />
            <input type="radio" name="multimsg" value="whitelistall">Whitelist & Deliver</input><br />
            <input type="submit" class="input-button" value="$Cpanel::Lang::LANG{$lang}{'submit-BoxTrapper'}">
        </td>
        <td colspan="3">&nbsp</td>
    </tr>
    </form>
EOM
    }
}

sub BoxTrapper_showfromname {
    my $account = shift;
    if ( $Cpanel::appname eq 'webmail' ) {
        $account = $Cpanel::authuser;
    }
    my ( $homedir, $domain ) = BoxTrapper_getaccountinfo($account);
    if ( !$homedir ) {
        print $Cpanel::Lang::LANG{$lang}{'unablelocate-BoxTrapper'} . " $account\n";
        return;
    }

    my ( $emaildir, $emaildeliverdir ) = BoxTrapper_getemaildirs( $account, $homedir );
    if ( $emaildir eq "" || $emaildeliverdir eq "" ) {
        print $Cpanel::Lang::LANG{$lang}{'noemaildirbegin-BoxTrapper'} . ' ' . $account . ' ' . $Cpanel::Lang::LANG{$lang}{'noemaildirend-BoxTrapper'} . "\n";
        return;
    }
    my %CONF = BoxTrapper_loadconf( $emaildir, $account );
    print $CONF{'fromname'};
}

sub BoxTrapper_showqueuetime {
    my $account = shift;
    if ( $Cpanel::appname eq 'webmail' ) {
        $account = $Cpanel::authuser;
    }
    my ( $homedir, $domain ) = BoxTrapper_getaccountinfo($account);
    if ( !$homedir ) {
        print $Cpanel::Lang::LANG{$lang}{'unablelocate-BoxTrapper'} . " $account\n";
        return;
    }

    my ( $emaildir, $emaildeliverdir ) = BoxTrapper_getemaildirs( $account, $homedir );
    if ( $emaildir eq "" || $emaildeliverdir eq "" ) {
        print $Cpanel::Lang::LANG{$lang}{'noemaildirbegin-BoxTrapper'} . ' ' . $account . ' ' . $Cpanel::Lang::LANG{$lang}{'noemaildirend-BoxTrapper'} . "\n";
        return;
    }
    my %CONF = BoxTrapper_loadconf( $emaildir, $account );
    print $CONF{'stale-queue-time'};
}

sub BoxTrapper_splitaddresses {
    my $addresses = shift;
    $addresses =~ s/$Cpanel::Regex::regex{allspacetabchars}//g;
    return split( /[\;\,]+/, $addresses );
}

sub BoxTrapper_status {
    my $account = shift;
    if ( $Cpanel::appname eq 'webmail' ) {
        $account = $Cpanel::authuser;
    }

    my $enabled = BoxTrapper_isenabled($account);
    my $status;
    if ($enabled) {
        $status = "<font class=\"redtext\">" . $Cpanel::Lang::LANG{$lang}{'enabled-BoxTrapper'} . "</font>\n";
    }
    else {
        $status = "<font class=\"blacktext\">" . $Cpanel::Lang::LANG{$lang}{'disabled-BoxTrapper'} . "</font>\n";
    }
    print $status;
}

sub BoxTrapper_statusbutton {
    my $account = shift;
    if ( $Cpanel::appname eq 'webmail' ) {
        $account = $Cpanel::authuser;
    }
    my $enabled = BoxTrapper_isenabled($account);
    my $status;
    if ($enabled) {
        print "<input type=\"hidden\" name=\"action\" value=\"Disable\">\n";
        print "<input type=\"submit\" class=\"input-button\" name=\"submitaction\" value=\"" . $Cpanel::Lang::LANG{$lang}{'disable-BoxTrapper'} . "\">\n";
    }
    else {
        print "<input type=\"hidden\" name=\"action\" value=\"Enable\">\n";
        print "<input type=\"submit\" class=\"input-button\" name=\"submitaction\" value=\"" . $Cpanel::Lang::LANG{$lang}{'enable-BoxTrapper'} . "\">\n";
    }
}

sub _loadsearchdb {
    my ( $fh, $encode ) = @_;
    my $sdb;
    my $namekey = 'id';
    my $inname;
    my $name;
    my $ckey;
    my $key;
    while ( my $rline = readline($fh) ) {
        $name   = '';
        $inname = 0;
        while ( $rline =~ m/(\<[^\>\<]+\>*)/g ) {
            my $startpos = ( pos($rline) - length($1) );
            if ($inname) {
                $name = substr( $rline, 0, $startpos );
                $inname = 0;
            }
            elsif ( $name ne '' && $startpos > 0 ) {
                push( @{ $sdb->{$name}->{$ckey} }, ( $encode ? Cpanel::Encoder::html_decode_str( substr( $rline, 0, $startpos ) ) : substr( $rline, 0, $startpos ) ) );
            }
            $key = $1;
            if ( $key eq '<' . $namekey . '>' ) {
                $inname = 1;
            }
            else {
                $ckey = $key;
                $ckey =~ s/\<|\>//g;
            }
            $rline = substr( $rline, pos($rline) );
        }
    }
    return $sdb;
}

sub _writesearchdb {
    my ( $db, $fh, $encode ) = @_;
    foreach my $entry ( keys %{$db} ) {
        print {$fh} "<msg><id>$entry</id>";
        foreach my $key ( keys %{ $db->{$entry} } ) {
            print {$fh} map { "<$key>" . ( $encode ? Cpanel::Encoder::html_encode_str($_) : $_ ) . "</$key>" } @{ $db->{$entry}->{$key} };
        }
        print {$fh} "</msg>\n";
    }
}

1;
