Compare commits

...

9 Commits

7 changed files with 700 additions and 19 deletions
+1 -1
View File
@@ -623,7 +623,7 @@ fi
# - Install Postfix from debian packages system # - Install Postfix from debian packages system
# - # -
echononl " Install Postfix from debian packages system" echononl " Install Postfix from debian packages system"
_needed_packages="postfix postfix-pgsql postfix-mysql postfix-pcre libsasl2-modules bsd-mailx haveged" _needed_packages="postfix postfix-pgsql postfix-mysql postfix-pcre libsasl2-modules libsasl2-modules-db bsd-mailx haveged"
if [[ "$SASL_AUTH_ENABLED" = "yes" ]]; then if [[ "$SASL_AUTH_ENABLED" = "yes" ]]; then
_needed_packages="$_needed_packages sasl2-bin" _needed_packages="$_needed_packages sasl2-bin"
fi fi
+1 -1
View File
@@ -575,7 +575,7 @@ fi
# - Install Postfix from debian packages system # - Install Postfix from debian packages system
# - # -
echononl " Install Postfix from debian packages system" echononl " Install Postfix from debian packages system"
_needed_packages="postfix postfix-pcre libsasl2-modules bsd-mailx haveged" _needed_packages="postfix postfix-pcre libsasl2-modules libsasl2-modules-db bsd-mailx haveged"
for _pkg in $_needed_packages ; do for _pkg in $_needed_packages ; do
if `dpkg -l | grep $_pkg | grep -e "^i" > /dev/null 2>&1` ; then if `dpkg -l | grep $_pkg | grep -e "^i" > /dev/null 2>&1` ; then
continue continue
+2 -2
View File
@@ -1046,11 +1046,11 @@ fi
# - # -
echononl "\tInstall a cronjob for cleaning up database" echononl "\tInstall a cronjob for cleaning up database"
crontab -l > /tmp/tmp_crontab crontab -l > /tmp/tmp_crontab
if ! grep -q -E "${WEBSITE_BASEDIR}/htdocs/bin/cleandb.sh" /tmp/tmp_crontab 2> /dev/null ; then if ! grep -q -E "/root/bin/admin-stuff/roundcube-cleandb.sh" /tmp/tmp_crontab 2> /dev/null ; then
echo "" >> /tmp/tmp_crontab echo "" >> /tmp/tmp_crontab
echo "# - Keep roundcube database slick and clean" >> /tmp/tmp_crontab echo "# - Keep roundcube database slick and clean" >> /tmp/tmp_crontab
echo "# -" >> /tmp/tmp_crontab echo "# -" >> /tmp/tmp_crontab
echo "37 3 * * * su - www-data -c \"${WEBSITE_BASEDIR}/htdocs/bin/cleandb.sh > /dev/null\" -s /bin/bash" >> /tmp/tmp_crontab echo "37 3 * * * /root/bin/admin-stuff/roundcube-cleandb.sh -w ${WEBSITE_BASEDIR}" >> /tmp/tmp_crontab
crontab /tmp/tmp_crontab > $log_file 2>&1 crontab /tmp/tmp_crontab > $log_file 2>&1
if [[ $? -eq 0 ]]; then if [[ $? -eq 0 ]]; then
echo_ok echo_ok
+160
View File
@@ -0,0 +1,160 @@
#!/bin/bash
# =============================================================================
# migrate_markasjunk2_to_markasjunk.sh
#
# Migrates the external roundcube-markasjunk2 plugin to the built-in
# markasjunk plugin included in Roundcube 1.7.x+
#
# Usage: migrate_markasjunk2_to_markasjunk.sh <roundcube-install-dir>
#
# Example:
# migrate_markasjunk2_to_markasjunk.sh /var/www/webmail/roundcubemail-1.7.1
# =============================================================================
RC_DIR="${1}"
# -----------------------------------------------------------------------------
# Colors / output helpers
# -----------------------------------------------------------------------------
red='\033[0;31m'
green='\033[0;32m'
yellow='\033[1;33m'
cyan='\033[0;36m'
reset='\033[0m'
info() { echo -e "${cyan}[INFO]${reset} $*"; }
ok() { echo -e "${green}[OK]${reset} $*"; }
skip() { echo -e "\033[0;37m[SKIP] $*${reset}"; }
warn() { echo -e "${yellow}[WARN]${reset} $*"; }
error() { echo -e "${red}[ERROR]${reset} $*"; }
#fatal() { echo -e "${red}[FATAL]${reset} $*"; exit 1; }
fatal() { echo -e "\n ${red}[FATAL]${reset} $*\n"; exit 1; }
# -----------------------------------------------------------------------------
# Sanity checks
# -----------------------------------------------------------------------------
[[ -z "$RC_DIR" ]] && fatal "Usage: $0 <roundcube-install-dir>"
[[ -d "$RC_DIR" ]] || fatal "Directory not found: $RC_DIR"
RC_CONFIG="${RC_DIR}/config/config.inc.php"
PLUGIN_DIR_NEW="${RC_DIR}/plugins/markasjunk"
PLUGIN_CONFIG_NEW="${PLUGIN_DIR_NEW}/config.inc.php"
[[ -f "$RC_CONFIG" ]] || fatal "Roundcube config not found: $RC_CONFIG"
[[ -d "$PLUGIN_DIR_NEW" ]] || fatal "Built-in markasjunk plugin not found: ${PLUGIN_DIR_NEW}"
echo ""
echo "================================================================="
echo " Migrate markasjunk2 -> markasjunk"
echo " Roundcube: ${RC_DIR}"
echo "================================================================="
echo ""
# -----------------------------------------------------------------------------
# Find markasjunk2 plugin directory (may have different names)
# -----------------------------------------------------------------------------
PLUGIN_DIR_OLD=""
for candidate in \
"${RC_DIR}/plugins/markasjunk2" \
"${RC_DIR}/plugins/roundcube-markasjunk2-master" \
"${RC_DIR}/plugins/roundcube-markasjunk2" ; do
if [[ -d "$candidate" ]]; then
PLUGIN_DIR_OLD="$candidate"
break
fi
done
PLUGIN_CONFIG_OLD=""
if [[ -n "$PLUGIN_DIR_OLD" ]]; then
if [[ -f "${PLUGIN_DIR_OLD}/config.inc.php" ]]; then
PLUGIN_CONFIG_OLD="${PLUGIN_DIR_OLD}/config.inc.php"
elif [[ -f "${PLUGIN_DIR_OLD}/config.inc.php.dist" ]]; then
PLUGIN_CONFIG_OLD="${PLUGIN_DIR_OLD}/config.inc.php.dist"
warn "No config.inc.php found in markasjunk2 plugin, using .dist file"
fi
fi
# -----------------------------------------------------------------------------
# Step 1: Replace plugin name in Roundcube config
# -----------------------------------------------------------------------------
info "Step 1: Replacing 'markasjunk2' with 'markasjunk' in config.inc.php.."
if grep -q "'markasjunk2'" "$RC_CONFIG" ; then
sed -i "s/'markasjunk2'/'markasjunk'/g" "$RC_CONFIG"
if [[ $? -eq 0 ]]; then
ok "Plugin name updated in ${RC_CONFIG}"
else
fatal "Failed to update plugin name in ${RC_CONFIG}"
fi
elif grep -q "'markasjunk'" "$RC_CONFIG" ; then
skip "Plugin 'markasjunk' already active in config"
else
warn "Neither 'markasjunk2' nor 'markasjunk' found in config please check manually"
fi
# -----------------------------------------------------------------------------
# Step 2: Migrate plugin config (markasjunk2_ -> markasjunk_)
# -----------------------------------------------------------------------------
info "Step 2: Migrating plugin config.."
if [[ -f "$PLUGIN_CONFIG_NEW" ]]; then
skip "markasjunk config already exists: ${PLUGIN_CONFIG_NEW}"
elif [[ -n "$PLUGIN_CONFIG_OLD" ]]; then
info "Source config: ${PLUGIN_CONFIG_OLD}"
# Copy and rename all markasjunk2_ keys to markasjunk_
sed "s/markasjunk2_/markasjunk_/g" "$PLUGIN_CONFIG_OLD" > "$PLUGIN_CONFIG_NEW"
if [[ $? -eq 0 ]]; then
ok "Config migrated to ${PLUGIN_CONFIG_NEW}"
info "Key changes applied: markasjunk2_* -> markasjunk_*"
else
fatal "Failed to write ${PLUGIN_CONFIG_NEW}"
fi
else
warn "No markasjunk2 config found using built-in defaults for markasjunk"
info "Defaults are fine if your markasjunk2 config was unmodified"
fi
# -----------------------------------------------------------------------------
# Step 3: Optionally remove old markasjunk2 plugin directory
# -----------------------------------------------------------------------------
if [[ -n "$PLUGIN_DIR_OLD" ]]; then
echo ""
echo -n "Remove old markasjunk2 plugin directory '${PLUGIN_DIR_OLD}'? [yes/no]: "
read REMOVE
REMOVE="$(echo "$REMOVE" | tr '[:upper:]' '[:lower:]')"
while [[ "$REMOVE" != "yes" ]] && [[ "$REMOVE" != "no" ]] ; do
echo -n "Wrong entry! - repeat [yes/no]: "
read REMOVE
done
if [[ "$REMOVE" = "yes" ]]; then
rm -rf "$PLUGIN_DIR_OLD"
if [[ $? -eq 0 ]]; then
ok "Removed: ${PLUGIN_DIR_OLD}"
else
error "Failed to remove: ${PLUGIN_DIR_OLD}"
fi
else
info "Keeping old plugin directory: ${PLUGIN_DIR_OLD}"
fi
else
skip "No markasjunk2 plugin directory found nothing to remove"
fi
# -----------------------------------------------------------------------------
# Summary
# -----------------------------------------------------------------------------
echo ""
echo "================================================================="
echo " Summary"
echo "================================================================="
grep -n "markasjunk" "$RC_CONFIG" | while read line ; do
info "config.inc.php line: $line"
done
[[ -f "$PLUGIN_CONFIG_NEW" ]] && ok "Plugin config: ${PLUGIN_CONFIG_NEW}" \
|| warn "No plugin config using built-in defaults"
echo ""
ok "Migration complete."
echo ""
+367
View File
@@ -0,0 +1,367 @@
<?php
/**
* rebuild_roundcube_config.php
*
* Regenerates a Roundcube config/config.inc.php file:
* - parameter order and comments follow config/defaults.inc.php
* - only parameters whose effective value differs from the default are kept
* - parameters that now equal the default, or that no longer exist in this
* Roundcube version, are written to a "*.removed-params.txt" file together
* with the reason and their last known value
* - parameters listed in FORCE_KEEP_PARAMS are kept even if their value
* matches the default; this does NOT resurrect a parameter that no
* longer exists in Roundcube at all (defaults.inc.php / core source)
*
* Usage:
* php rebuild_roundcube_config.php <path-to-roundcube-config-dir> [--apply]
*
* Without --apply (dry run, default):
* writes config.inc.php.new and config.inc.php.removed-params.<timestamp>.txt
* next to the existing files, does NOT touch config.inc.php itself.
*
* With --apply:
* additionally backs up the existing config.inc.php to
* config.inc.php.bak.<timestamp> and replaces it with the regenerated file.
*/
// ---------------------------------------------------------------------------
// Parameters that must always end up in the regenerated config.inc.php, even
// if their value equals the default (they are still kept explicit/visible
// per site policy). Add further keys here as needed. Note: this only
// suppresses the "equals default" removal - a parameter that no longer
// exists in Roundcube at all is still dropped, see UNDOCUMENTED_CORE_PARAMS
// below for parameters that need to be recognized as still valid.
// ---------------------------------------------------------------------------
const FORCE_KEEP_PARAMS = [
'imap_host',
'imap_auth_type',
'smtp_host',
'smtp_port',
'smtp_user',
'smtp_pass',
];
// ---------------------------------------------------------------------------
// Parameters that exist in Roundcube 1.7.1 core (verified against program/
// source via `->config->get('key', ...)` calls) but are NOT documented in
// the shipped config/defaults.inc.php. Keys found here are kept (not treated
// as "removed"), using this one-line note as their comment since
// defaults.inc.php has none. Verified 2026-07-03 against roundcubemail-1.7.1.
// ---------------------------------------------------------------------------
const UNDOCUMENTED_CORE_PARAMS = [
'additional_logo_types' => 'Additional file extensions accepted as skin logo/favicon images.',
'dark_mode_support' => 'Tell the skin/UI that dark mode is supported (exposed to the client as env var).',
'devel_mode' => 'Enable developer/debug mode (verbose errors, disables some internal caching).',
'editor_css_location' => 'Path to a custom CSS file loaded into the HTML message editor (TinyMCE).',
'embed_css_location' => 'Path to a custom CSS file loaded for embedded/quoted HTML messages.',
'enable_caching' => 'Enable IMAP/message caching in the database.',
'lock_special_folders' => "Prevent users from unsubscribing/renaming special folders (Inbox, Sent, ...).",
'media_browser_css_location' => 'Path to a custom CSS file for the TinyMCE media browser.',
'namespace_fixed' => 'Treat the configured IMAP namespace as fixed instead of auto-detecting it.',
'pagesize' => 'Legacy fallback for addressbook_pagesize (see mail_pagesize/addressbook_pagesize).',
'performance_stats' => 'Log performance/timing statistics (like devel_mode).',
'shared_cache_ttl' => 'TTL for the shared memcache/redis object cache.',
'spellcheck_atd_key' => 'API key for the After-the-Deadline spell checking service.',
'storage_driver' => "Storage backend driver; currently only 'imap' is implemented by core.",
'tnef_decode' => 'Enable/disable decoding of winmail.dat (TNEF) attachments.',
];
function fail(string $msg): void
{
fwrite(STDERR, "ERROR: $msg\n");
exit(1);
}
function read_lines(string $file): array
{
$lines = file($file);
if ($lines === false) {
fail("Cannot read $file");
}
return $lines;
}
/**
* Parse a Roundcube config source file into an ordered list of parameter
* blocks: key => ['comment' => <preceding comment/blank lines verbatim>,
* 'value_src' => <raw PHP source of the right-hand side>]
*
* Returns [orderedKeys (first-seen order), paramsByKey (last occurrence wins,
* matching PHP's own "last assignment wins" semantics for duplicate keys)]
*/
function parse_config_source(string $file): array
{
$lines = read_lines($file);
$n = count($lines);
$order = [];
$params = [];
$commentBuf = [];
$i = 0;
while ($i < $n) {
$line = $lines[$i];
// $config = []; / $config = array(); is the file's own initializer,
// not a parameter - discard any accumulated header/license comments.
if (preg_match('/^\$config\s*=\s*(\[\]|array\(\))\s*;/', trim($line))) {
$commentBuf = [];
$i++;
continue;
}
if (preg_match("/^\\\$config\\['([a-zA-Z0-9_]+)'\\]\\s*=/", $line, $m)) {
$key = $m[1];
$stmtLines = [$line];
$depth = substr_count($line, '(') - substr_count($line, ')')
+ substr_count($line, '[') - substr_count($line, ']');
$j = $i;
while (!(preg_match('/;\s*$/', rtrim($line)) && $depth <= 0)) {
$j++;
if ($j >= $n) {
fail("Unterminated statement for \$config['$key'] in $file at line " . ($i + 1));
}
$line = $lines[$j];
$stmtLines[] = $line;
$depth += substr_count($line, '(') - substr_count($line, ')')
+ substr_count($line, '[') - substr_count($line, ']');
}
$raw = implode('', $stmtLines);
if (!preg_match("/^\\\$config\\['" . preg_quote($key, '/') . "'\\]\\s*=\\s*(.*);\\s*$/s", $raw, $vm)) {
fail("Could not extract value for \$config['$key'] in $file");
}
$comment = rtrim(implode('', $commentBuf));
$params[$key] = [
'comment' => $comment,
'value_src' => rtrim($vm[1]),
];
if (!in_array($key, $order, true)) {
$order[] = $key;
}
$commentBuf = [];
$i = $j + 1;
continue;
}
$commentBuf[] = $line;
$i++;
}
return [$order, $params];
}
/**
* Evaluate a raw PHP expression (right-hand side of a $config[...] = ...;
* assignment) in isolation. These expressions in defaults.inc.php /
* config.inc.php are self-contained literals (strings, bools, numbers,
* arrays) with no external references, so isolated eval() is safe here.
*/
function eval_value_src(string $key, string $src, string $originFile)
{
try {
return eval("return $src;");
} catch (\Throwable $e) {
fail("Failed to evaluate \$config['$key'] from $originFile: " . $e->getMessage());
}
}
function values_equal($a, $b): bool
{
return var_export($a, true) === var_export($b, true);
}
function format_value(string $key, $val): string
{
// Prefer var_export for the (rare) fallback path where we don't have
// the original source text available.
$s = var_export($val, true);
return $s;
}
// ---------------------------------------------------------------------------
// Main
// ---------------------------------------------------------------------------
$configDir = isset($argv[1]) ? rtrim($argv[1], '/') : null;
$apply = in_array('--apply', $argv, true);
if (!$configDir || !is_dir($configDir)) {
fail("Usage: php rebuild_roundcube_config.php <path-to-roundcube-config-dir> [--apply]");
}
$defaultsFile = "$configDir/defaults.inc.php";
$localFile = "$configDir/config.inc.php";
if (!is_file($defaultsFile)) {
fail("Not found: $defaultsFile");
}
if (!is_file($localFile)) {
fail("Not found: $localFile");
}
// defaults.inc.php references RCUBE_INSTALL_PATH (e.g. for log_dir/temp_dir
// defaults). Define it exactly as Roundcube's own bootstrap does: the
// installation root (parent of config/) with a trailing slash.
if (!defined('RCUBE_INSTALL_PATH')) {
define('RCUBE_INSTALL_PATH', dirname($configDir) . '/');
}
[$defaultsOrder, $defaultsParams] = parse_config_source($defaultsFile);
[$localOrder, $localParams] = parse_config_source($localFile);
// Detect duplicate keys in the local file, and report which value wins
// (informational - matches actual PHP "last wins" behaviour).
$rawLocalLines = read_lines($localFile);
$assignCounts = [];
foreach ($rawLocalLines as $line) {
if (preg_match("/^\\\$config\\['([a-zA-Z0-9_]+)'\\]\\s*=/", $line, $m)) {
$assignCounts[$m[1]] = ($assignCounts[$m[1]] ?? 0) + 1;
}
}
$duplicates = array_filter($assignCounts, fn($c) => $c > 1);
$keep = []; // key => value_src (raw source text to emit)
$removed = []; // key => reason string
foreach ($localOrder as $key) {
$localSrc = $localParams[$key]['value_src'];
$localVal = eval_value_src($key, $localSrc, $localFile);
$forceKeep = in_array($key, FORCE_KEEP_PARAMS, true);
if (array_key_exists($key, $defaultsParams)) {
$defaultVal = eval_value_src($key, $defaultsParams[$key]['value_src'], $defaultsFile);
if (!$forceKeep && values_equal($localVal, $defaultVal)) {
$removed[$key] = [
'reason' => 'entspricht dem Default-Wert',
'value' => format_value($key, $localVal),
];
continue;
}
$keep[$key] = $localSrc;
} elseif (array_key_exists($key, UNDOCUMENTED_CORE_PARAMS)) {
// Known to still be a valid core parameter, just undocumented.
$keep[$key] = $localSrc;
} else {
// Parameter no longer exists at all (neither in defaults.inc.php nor
// in UNDOCUMENTED_CORE_PARAMS) - FORCE_KEEP_PARAMS cannot resurrect
// it, there is nothing left to keep it in sync with.
$removed[$key] = [
'reason' => 'Parameter existiert in dieser Roundcube-Version nicht (mehr) / ist unbekannt',
'value' => format_value($key, $localVal),
];
}
}
// Force-keep parameters that were not set in config.inc.php at all: fall
// back to the default value so the parameter still shows up explicitly.
// Only applies to parameters that still exist in defaults.inc.php.
foreach (FORCE_KEEP_PARAMS as $key) {
if (array_key_exists($key, $keep) || array_key_exists($key, $localParams)) {
continue;
}
if (array_key_exists($key, $defaultsParams)) {
$keep[$key] = $defaultsParams[$key]['value_src'];
}
}
// ---------------------------------------------------------------------------
// Build new config.inc.php content
// ---------------------------------------------------------------------------
$timestamp = date('Y-m-d-Hi');
$totalKept = count($keep);
$out = [];
$out[] = '<?php';
$out[] = '';
$out[] = '/* Local configuration for Roundcube Webmail';
$out[] = ' *';
$out[] = " * Regenerated by rebuild_roundcube_config.php on " . date('Y-m-d H:i');
$out[] = ' * Parameter order and comments follow config/defaults.inc.php.';
$out[] = ' * Parameters dropped during regeneration are listed in the';
$out[] = ' * accompanying *.removed-params.*.txt file.';
$out[] = ' */';
$out[] = '';
$out[] = '$config = [];';
$out[] = '';
foreach ($defaultsOrder as $key) {
if (!array_key_exists($key, $keep)) {
continue;
}
$comment = $defaultsParams[$key]['comment'];
if ($comment !== '') {
$out[] = $comment;
}
$out[] = "\$config['$key'] = " . $keep[$key] . ';';
$out[] = '';
unset($keep[$key]);
}
// Remaining kept keys are the "undocumented but valid core parameter" ones -
// defaults.inc.php has no position/comment for them.
if (!empty($keep)) {
$out[] = '// ----------------------------------------------------------------';
$out[] = '// Parameters valid in this Roundcube core version but not documented';
$out[] = '// in config/defaults.inc.php (see rebuild_roundcube_config.php).';
$out[] = '// ----------------------------------------------------------------';
$out[] = '';
foreach ($keep as $key => $src) {
$out[] = '// ' . (UNDOCUMENTED_CORE_PARAMS[$key] ?? '');
$out[] = "\$config['$key'] = " . $src . ';';
$out[] = '';
}
}
$newContent = implode("\n", $out) . "\n";
// ---------------------------------------------------------------------------
// Build removed-params report
// ---------------------------------------------------------------------------
$report = [];
$report[] = "# Parameter removed from config.inc.php on regeneration ($timestamp)";
$report[] = "# Format: key = last effective value // reason";
$report[] = "";
foreach ($removed as $key => $info) {
$report[] = "$key = {$info['value']} // {$info['reason']}";
}
$reportContent = implode("\n", $report) . "\n";
// ---------------------------------------------------------------------------
// Write output / apply
// ---------------------------------------------------------------------------
$newFile = "$localFile.new";
$reportFile = "$configDir/config.inc.php.removed-params.$timestamp.txt";
file_put_contents($newFile, $newContent);
file_put_contents($reportFile, $reportContent);
echo "Wrote: $newFile\n";
echo "Wrote: $reportFile\n";
echo "\n";
echo "Kept parameters : $totalKept\n";
echo "Removed parameters: " . count($removed) . "\n";
if (!empty($duplicates)) {
echo "\nWARNING: the following keys were assigned more than once in config.inc.php\n";
echo "(PHP uses the LAST assignment - already reflected above, listed here for review):\n";
foreach ($duplicates as $key => $count) {
echo " - $key ($count assignments)\n";
}
}
if ($apply) {
$backupFile = "$localFile.bak.$timestamp";
if (!copy($localFile, $backupFile)) {
fail("Could not create backup $backupFile");
}
if (!rename($newFile, $localFile)) {
fail("Could not move $newFile to $localFile");
}
echo "\nApplied: backed up original to $backupFile and replaced $localFile\n";
} else {
echo "\nDry run only. Review $newFile and $reportFile, then re-run with --apply.\n";
}
+169 -15
View File
@@ -296,6 +296,12 @@ DEFAULT_WEBSITE_BASEDIR="/var/www/${WEBSITE_NAME}"
[[ -n "$WEBSITE_BASEDIR" ]] || WEBSITE_BASEDIR=$DEFAULT_WEBSITE_BASEDIR [[ -n "$WEBSITE_BASEDIR" ]] || WEBSITE_BASEDIR=$DEFAULT_WEBSITE_BASEDIR
CUR_INSTALL_DIR="$(realpath "${WEBSITE_BASEDIR}/htdocs")" CUR_INSTALL_DIR="$(realpath "${WEBSITE_BASEDIR}/htdocs")"
# For Roundcube 1.7.x the symlink points to roundcubemail-x.y.z/public_html/
# We need the parent roundcubemail directory, not public_html
if [[ "$(basename "$CUR_INSTALL_DIR")" = "public_html" ]]; then
CUR_INSTALL_DIR="$(dirname "$CUR_INSTALL_DIR")"
fi
if [[ ! -d "$CUR_INSTALL_DIR" ]] ; then if [[ ! -d "$CUR_INSTALL_DIR" ]] ; then
fatal "No current installation of roundcube found!" fatal "No current installation of roundcube found!"
fi fi
@@ -949,25 +955,75 @@ else
fi fi
echo ""
echo ""
echo "You might want to deactivate some plugins in the configuration file, e.g. the plugin 'carddav'.."
echo ""
echononl "continue [yes/no]: "
read OK
OK="$(echo "$OK" | tr '[:upper:]' '[:lower:]')"
while [[ "$OK" != "yes" ]] && [[ "$OK" != "no" ]] ; do
echononl "Wrong entry! - repeat [yes/no]: "
read OK
done
[[ $OK = "yes" ]] || fatal "Abbruch durch User"
# Root-Warning unterdrücken # Root-Warning unterdrücken
export COMPOSER_ALLOW_SUPERUSER=1 export COMPOSER_ALLOW_SUPERUSER=1
export COMPOSER_ROOT_VERSION=${ROUNDCUBE_VERSION} export COMPOSER_ROOT_VERSION=${ROUNDCUBE_VERSION}
echononl " Deactivate spellcheck_engine (pspell not available in PHP 8.4+).."
if grep -q "spellcheck_engine.*pspell" "${WEBSITE_BASEDIR}/roundcubemail-${ROUNDCUBE_VERSION}/config/config.inc.php" 2>/dev/null ; then
sed -i "s/\\\$config\['spellcheck_engine'\].*=.*'pspell'.*/\$config['spellcheck_engine'] = '';/" \
"${WEBSITE_BASEDIR}/roundcubemail-${ROUNDCUBE_VERSION}/config/config.inc.php" > $log_file 2>&1
if [[ $? -eq 0 ]]; then
echo_ok
else
echo_failed
error "$(cat $log_file)"
fi
else
echo_skipped
fi
echononl " Replace deprecated markasjunk2 plugin with built-in markasjunk.."
if grep -q "'markasjunk2'" "${WEBSITE_BASEDIR}/roundcubemail-${ROUNDCUBE_VERSION}/config/config.inc.php" 2>/dev/null ; then
sed -i "s/'markasjunk2'/'markasjunk'/" \
"${WEBSITE_BASEDIR}/roundcubemail-${ROUNDCUBE_VERSION}/config/config.inc.php" > $log_file 2>&1
if [[ $? -eq 0 ]]; then
echo_ok
else
echo_failed
error "$(cat $log_file)"
fi
else
echo_skipped
fi
echononl " Copy composer.json from source archive to installation directory.."
cp "${script_dir}/roundcubemail-${ROUNDCUBE_VERSION}/composer.json" \
"${WEBSITE_BASEDIR}/roundcubemail-${ROUNDCUBE_VERSION}/composer.json" > $log_file 2>&1
if [[ $? -eq 0 ]]; then
echo_ok
else
echo_failed
error "$(cat $log_file)"
echononl "continue anyway [yes/no]: "
read OK
OK="$(echo "$OK" | tr '[:upper:]' '[:lower:]')"
while [[ "$OK" != "yes" ]] && [[ "$OK" != "no" ]] ; do
echononl "Wrong entry! - repeat [yes/no]: "
read OK
done
[[ $OK = "yes" ]] || fatal "Abbruch durch User"
fi
echononl " Remove old composer.lock (force fresh dependency resolution).."
if [[ -f "${WEBSITE_BASEDIR}/roundcubemail-${ROUNDCUBE_VERSION}/composer.lock" ]]; then
rm "${WEBSITE_BASEDIR}/roundcubemail-${ROUNDCUBE_VERSION}/composer.lock" > $log_file 2>&1
if [[ $? -eq 0 ]]; then
echo_ok
else
echo_failed
error "$(cat $log_file)"
fi
else
echo_skipped
fi
echononl " Allow Plugins .." echononl " Allow Plugins .."
php composer.phar --no-interaction config allow-plugins.roundcube/plugin-installer true > $log_file 2>&1 php composer.phar --no-interaction config allow-plugins.roundcube/plugin-installer true > $log_file 2>&1
if [[ $? -eq 0 ]]; then if [[ $? -eq 0 ]]; then
@@ -1044,6 +1100,105 @@ else
fi fi
echononl " Regenerate Composer autoloader (remove stale files).."
_autoload_dir="${WEBSITE_BASEDIR}/roundcubemail-${ROUNDCUBE_VERSION}/vendor/composer"
rm -f "${_autoload_dir}/autoload_real.php" \
"${_autoload_dir}/ClassLoader.php" \
"${_autoload_dir}/InstalledVersions.php" \
"${WEBSITE_BASEDIR}/roundcubemail-${ROUNDCUBE_VERSION}/vendor/autoload.php"
php composer.phar --no-interaction dump-autoload > $log_file 2>&1
if [[ $? -eq 0 ]]; then
echo_ok
else
echo_failed
error "$(cat $log_file)"
echononl "continue anyway [yes/no]: "
read OK
OK="$(echo "$OK" | tr '[:upper:]' '[:lower:]')"
while [[ "$OK" != "yes" ]] && [[ "$OK" != "no" ]] ; do
echononl "Wrong entry! - repeat [yes/no]: "
read OK
done
[[ $OK = "yes" ]] || fatal "Abbruch durch User"
fi
echononl " Run database schema migration (bin/updatedb.sh).."
php ${WEBSITE_BASEDIR}/roundcubemail-${ROUNDCUBE_VERSION}/bin/updatedb.sh \
--package=roundcube \
--dir=${WEBSITE_BASEDIR}/roundcubemail-${ROUNDCUBE_VERSION}/SQL \
> $log_file 2>&1
if [[ $? -eq 0 ]]; then
echo_ok
else
# Check if failure is due to a partially applied migration ("already exists")
if grep -q "already exists\|existiert bereits" $log_file ; then
warn "DB migration had 'already exists' errors - attempting to skip failed migration and retry.."
# Extract the failed migration ID from the error output
_failed_migration=$(grep -oE "\([0-9]{10}\)" $log_file | head -1 | tr -d '()')
if [[ -n "$_failed_migration" ]]; then
info "Setting DB schema version to '${_failed_migration}' and retrying.."
if [[ "$DB_TYPE" = "pgsql" ]]; then
su - postgres -c "${psql_command} -q -d ${DB_NAME} -c \
\"UPDATE system SET value='${_failed_migration}' WHERE name='roundcube-version';\"" > $log_file 2>&1
elif [[ "$DB_TYPE" = "mysql" ]]; then
${mysql_command} ${MYSQL_CREDENTIALS} ${DB_NAME} -e \
"UPDATE system SET value='${_failed_migration}' WHERE name='roundcube-version';" > $log_file 2>&1
fi
# Retry migration
php ${WEBSITE_BASEDIR}/roundcubemail-${ROUNDCUBE_VERSION}/bin/updatedb.sh \
--package=roundcube \
--dir=${WEBSITE_BASEDIR}/roundcubemail-${ROUNDCUBE_VERSION}/SQL \
>> $log_file 2>&1
if [[ $? -eq 0 ]]; then
echo_ok
else
echo_failed
error "Retry also failed:\n$(cat $log_file)"
echononl "continue anyway [yes/no]: "
read OK
OK="$(echo "$OK" | tr '[:upper:]' '[:lower:]')"
while [[ "$OK" != "yes" ]] && [[ "$OK" != "no" ]] ; do
echononl "Wrong entry! - repeat [yes/no]: "
read OK
done
[[ $OK = "yes" ]] || fatal "Abbruch durch User"
fi
else
echo_failed
error "Could not extract failed migration ID.\ncommand was:\n\t# php ${WEBSITE_BASEDIR}/roundcubemail-${ROUNDCUBE_VERSION}/bin/updatedb.sh --package=roundcube --dir=${WEBSITE_BASEDIR}/roundcubemail-${ROUNDCUBE_VERSION}/SQL\n\n$(cat $log_file)"
echononl "continue anyway [yes/no]: "
read OK
OK="$(echo "$OK" | tr '[:upper:]' '[:lower:]')"
while [[ "$OK" != "yes" ]] && [[ "$OK" != "no" ]] ; do
echononl "Wrong entry! - repeat [yes/no]: "
read OK
done
[[ $OK = "yes" ]] || fatal "Abbruch durch User"
fi
else
echo_failed
error "command was:\n\t# php ${WEBSITE_BASEDIR}/roundcubemail-${ROUNDCUBE_VERSION}/bin/updatedb.sh --package=roundcube --dir=${WEBSITE_BASEDIR}/roundcubemail-${ROUNDCUBE_VERSION}/SQL\n\n$(cat $log_file)"
echononl "continue anyway [yes/no]: "
read OK
OK="$(echo "$OK" | tr '[:upper:]' '[:lower:]')"
while [[ "$OK" != "yes" ]] && [[ "$OK" != "no" ]] ; do
echononl "Wrong entry! - repeat [yes/no]: "
read OK
done
[[ $OK = "yes" ]] || fatal "Abbruch durch User"
fi
fi
echononl " Index build-in addressbook" echononl " Index build-in addressbook"
${WEBSITE_BASEDIR}/roundcubemail-${ROUNDCUBE_VERSION}/bin/indexcontacts.sh > $log_file 2>&1 ${WEBSITE_BASEDIR}/roundcubemail-${ROUNDCUBE_VERSION}/bin/indexcontacts.sh > $log_file 2>&1
if [[ $? -eq 0 ]]; then if [[ $? -eq 0 ]]; then
@@ -1180,4 +1335,3 @@ fi
echo "" echo ""
clean_up 0 clean_up 0