#!/usr/local/cpanel/3rdparty/bin/perl
# cpanel - bin/update-roundcube-sqlite-db 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
## cpdev: installs to /usr/local/cpanel/base/3rdparty/roundcube
package bin::update_roundcube_sqlite_db;
use strict;
use warnings;
use parent qw( Cpanel::HelpfulScript );
use constant _OPTIONS => (
'foreground',
'user=s@',
);
use Cpanel::Config::HasCpUserFile ();
use Cpanel::Config::LoadCpConf ();
use Cpanel::Config::Users ();
use Cpanel::Email::RoundCube ();
use Cpanel::Email::RoundCube::DBI ();
use Cpanel::PwCache ();
use Whostmgr::Email ();
use Try::Tiny;
use Fcntl ();
use IO::Handle ();
unless ( caller() ) {
_redirect_to_mysql_script(@ARGV);
exit __PACKAGE__->new(@ARGV)->script();
}
my $cpconf;
sub script {
my ($self) = @_;
my $foreground = $self->getopt('foreground');
my $opt_users = $self->getopt('user');
my @users;
if ( ref $opt_users eq 'ARRAY' && scalar @{$opt_users} ) {
for my $user ( @{$opt_users} ) {
if ( !Cpanel::Config::HasCpUserFile::has_cpuser_file($user) ) {
print "User “$user” does not exist.\n";
next;
}
push @users, $user;
}
if ( !@users ) {
print "No valid users given.\n";
return 1; # script exit code
}
}
else {
@users = Cpanel::Config::Users::getcpusers();
}
my $latest_bundled_version = Cpanel::Email::RoundCube::get_active_version() // '';
my $version_before_update = Cpanel::Email::RoundCube::get_stored_version() // 'none'; # This will be undef during initial install
_daemonize() unless $foreground;
Cpanel::Email::RoundCube::init_roundcube_data_dir();
print "Roundcube update from $version_before_update to $latest_bundled_version in progress.\n";
Cpanel::Email::RoundCube::lock_roundcube_for_update();
my $dest_dir = $Cpanel::Email::RoundCube::ROUNDCUBE_DEST_DIR;
##############################
## DATABASE
## return value not used currently, but keep this for now; we do not know
## the extent or type of problems we might encounter
my $rv = handle_database( $latest_bundled_version, \@users );
##############################
# If it is 'mysql_plus_sqlite' we already generated the config in such a
# way as to be able to load from either DB.
# Default is sqlite for this, so also do it if it's just falsey.
if ( !$cpconf->{'roundcube_db'} || ( length $cpconf->{'roundcube_db'} && $cpconf->{'roundcube_db'} eq 'sqlite' ) ) {
Cpanel::Email::RoundCube::generate_roundcube_config_sqlite($dest_dir);
}
Cpanel::Email::RoundCube::write_version_file();
if ( Cpanel::Email::RoundCube::process_custom_roundcube_install_file($dest_dir) ) {
Cpanel::Email::RoundCube::unlock_roundcube_after_update();
return 0; # script exit code
}
Cpanel::Email::RoundCube::unlock_roundcube_after_update();
print "Roundcube updated to $latest_bundled_version\n";
return 0; # script exit code
}
##############################
# Determine whether our config override is in place. If we don't see the include in the primary RC config, return 0
# so the rest of the script will process the update to eventually call _generate_roundcube_config() in Cpanel/Email/RoundCube.pm
sub config_override_in_place {
if ( open( my $config_fh, '<', '/usr/local/cpanel/base/3rdparty/roundcube/config/config.inc.php' ) ) {
my $include_found = 0;
while ( my $line = readline($config_fh) ) {
if ( $line =~ m/include.+cpanel_config\.php/ ) {
$include_found = 1;
last;
}
}
close($config_fh);
return $include_found;
}
else {
# No config in place, need to run an update to trigger the config build
return 0;
}
}
##############################
## case 48029: removed conditional that would have invoked the rcube/sqlite convert
## script, if this script were invoked on a MySQL machine
sub handle_database {
my ( $RCUBE_VERSION, $users_ar ) = @_;
for my $sysuser ( @{$users_ar} ) {
my $homedir = Cpanel::PwCache::gethomedir($sysuser);
my @dbs;
my ( $base_dir, $db_fname ) = ( "$homedir/etc", "$sysuser.rcube.db" );
if ( -e "$base_dir/$db_fname" ) {
push(
@dbs,
{
sysuser => $sysuser,
user => $sysuser,
domain => undef,
base_dir => $base_dir,
db_fname => $db_fname,
}
);
}
try {
my $email_accts = Whostmgr::Email::list_pops_for($sysuser);
for my $email_address (@$email_accts) {
my ( $email_user, $domain ) = split( '@', $email_address );
my ( $base_dir, $db_fname ) = ( "$homedir/etc/$domain", "$email_user.rcube.db" );
if ( -e "$base_dir/$db_fname" ) {
push(
@dbs,
{
sysuser => $sysuser,
user => $email_user,
domain => $domain,
base_dir => $base_dir,
db_fname => $db_fname,
}
);
}
}
}
catch {
local $@ = $_;
warn;
};
next unless ( scalar @dbs );
my $rv = Cpanel::Email::RoundCube::DBI::sqlite_schema_updates_for_user( $sysuser, $RCUBE_VERSION, \@dbs );
## note: can't really do anything with the return value, as we need to determine
## the types of problems we might encounter
}
return 1;
}
sub _daemonize {
print STDERR "$0: running in the background.\n";
require Cpanel::Daemonizer::Simple;
Cpanel::Daemonizer::Simple::daemonize();
return;
}
sub _redirect_to_mysql_script {
my @argv = @_;
$cpconf = Cpanel::Config::LoadCpConf::loadcpconf();
## case 48029: Redirect to MySQL script unless Roundcube is configured to use SQLite
unless ( exists $cpconf->{'roundcube_db'} && grep { $cpconf->{'roundcube_db'} eq $_ } qw{sqlite mysql_plus_sqlite} ) {
( my $mysql_version = $0 ) =~ s/(.+?)\-sqlite(\-db)?$/$1$2/;
$mysql_version .= '-db' if $mysql_version !~ m/\-db$/;
print "Roundcube is not configured to use SQLite.\n";
print "This update is for Roundcube using SQLite. Running $mysql_version.\n";
exec $mysql_version, @argv;
die "Failed to exec $mysql_version";
}
return;
}
1;
__END__
=encoding utf-8
=head1 NAME
update-roundcube-sqlite-db
=head1 SYNOPSIS
update-roundcube-sqlite-db [--foreground] [--user <user1>] ... [--user <userN>]
update-roundcube-sqlite-db --help
=head1 OPTIONS
=over 4
=item --foreground
Run the script in the foreground.
The default behavior is to run in the background.
=item --user C<username>
Run the script for the specified system user.
Can be used repeatedly to specify multiple system users.
The user must be a cPanel account.
The default behavior is to run for all users.
=item --help
Provides this help text.
=back
=head1 DESCRIPTION
This script updates Roundcube SQLite database schema for one or more users.