Compare commits
14 Commits
17e858d51b
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
| b73fa92d43 | |||
| bb8cd0c2cc | |||
| 2d288c6705 | |||
| a96c92524e | |||
| dd105ad5d9 | |||
| 7fb1ac4511 | |||
| cb3079a7ae | |||
| 5a7e79d8c4 | |||
| d60cb62d8f | |||
| 5b9e632fe4 | |||
| 7be12b675c | |||
| d385627251 | |||
| 8df8b715c7 | |||
| b90c692af1 |
@@ -623,7 +623,7 @@ fi
|
||||
# - 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
|
||||
_needed_packages="$_needed_packages sasl2-bin"
|
||||
fi
|
||||
|
||||
@@ -575,7 +575,7 @@ fi
|
||||
# - 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
|
||||
if `dpkg -l | grep $_pkg | grep -e "^i" > /dev/null 2>&1` ; then
|
||||
continue
|
||||
|
||||
+95
-2
@@ -1182,14 +1182,89 @@ else
|
||||
echo_ok
|
||||
fi
|
||||
|
||||
echononl "\tMake file composer.json writable.."
|
||||
chown ${HTTP_USER} ${WEBSITE_BASEDIR}/postfixadmin-${PF_ADMIN_VERSION}/composer.json > $log_file 2>&1
|
||||
if [[ $? -eq 0 ]]; then
|
||||
echo_ok
|
||||
else
|
||||
echo_failed
|
||||
error "$(cat $log_file)"
|
||||
|
||||
echononl "\tcontinue anyway [yes/no]: "
|
||||
read OK
|
||||
OK="$(echo "$OK" | tr '[:upper:]' '[:lower:]')"
|
||||
while [[ "$OK" != "yes" ]] && [[ "$OK" != "no" ]] ; do
|
||||
echononl "Wrong entry! - repeat [yes/nno]: "
|
||||
read OK
|
||||
done
|
||||
[[ $OK = "yes" ]] || fatal "Script terminated by user input.."
|
||||
fi
|
||||
|
||||
echononl "\tCreate composer.lock file.."
|
||||
if [[ ${PF_ADMIN_MAJOR_VERSION} -gt 3 ]] ; then
|
||||
if $PHP_DEBIAN_INSTALLATION ; then
|
||||
|
||||
su ${HTTP_USER} -s /bin/bash -c "cd ${WEBSITE_BASEDIR}/postfixadmin-${PF_ADMIN_VERSION} && \
|
||||
php /usr/local/bin/composer config --json \
|
||||
policy.advisories.ignore-id '[\"PKSA-qv5y-crcz-9nxw\",\"PKSA-kbc7-dq62-pt7d\"]'" \
|
||||
> $log_file 2>&1
|
||||
|
||||
|
||||
else
|
||||
|
||||
su ${HTTP_USER} -s /bin/bash -c "cd ${WEBSITE_BASEDIR}/postfixadmin-${PF_ADMIN_VERSION} && \
|
||||
/usr/local/php-${php_latest_ver}/bin/php /usr/local/bin/composer config --json \
|
||||
policy.advisories.ignore-id '[\"PKSA-qv5y-crcz-9nxw\",\"PKSA-kbc7-dq62-pt7d\"]'" \
|
||||
> $log_file 2>&1
|
||||
|
||||
fi
|
||||
|
||||
if [[ $? -eq 0 ]]; then
|
||||
echo_ok
|
||||
else
|
||||
echo_failed
|
||||
|
||||
echo ""
|
||||
echo -e "\tCommand was: "
|
||||
if $PHP_DEBIAN_INSTALLATION ; then
|
||||
cat <<EOF
|
||||
su ${HTTP_USER} -s /bin/bash -c "cd ${WEBSITE_BASEDIR}/postfixadmin-${PF_ADMIN_VERSION} && \
|
||||
php /usr/local/bin/composer config --json \
|
||||
policy.advisories.ignore-id '[\"PKSA-qv5y-crcz-9nxw\",\"PKSA-kbc7-dq62-pt7d\"]'" \
|
||||
> $log_file 2>&1
|
||||
EOF
|
||||
else
|
||||
cat <<EOF
|
||||
su ${HTTP_USER} -s /bin/bash -c "cd ${WEBSITE_BASEDIR}/postfixadmin-${PF_ADMIN_VERSION} && \
|
||||
/usr/local/php-${php_latest_ver}/bin/php /usr/local/bin/composer config --json \
|
||||
policy.advisories.ignore-id '[\"PKSA-qv5y-crcz-9nxw\",\"PKSA-kbc7-dq62-pt7d\"]'" \
|
||||
> $log_file 2>&1
|
||||
EOF
|
||||
fi
|
||||
echo ""
|
||||
|
||||
error "$(cat $log_file)"
|
||||
|
||||
echononl "\tcontinue anyway [yes/no]: "
|
||||
read OK
|
||||
OK="$(echo "$OK" | tr '[:upper:]' '[:lower:]')"
|
||||
while [[ "$OK" != "yes" ]] && [[ "$OK" != "no" ]] ; do
|
||||
echononl "Wrong entry! - repeat [yes/nno]: "
|
||||
read OK
|
||||
done
|
||||
[[ $OK = "yes" ]] || fatal "Script terminated by user input.."
|
||||
fi
|
||||
fi
|
||||
|
||||
|
||||
echononl "\tInstall PHP dependencies ( composer install --no-dev ... ).."
|
||||
if [[ ${PF_ADMIN_MAJOR_VERSION} -gt 3 ]] ; then
|
||||
if $PHP_DEBIAN_INSTALLATION ; then
|
||||
su ${HTTP_USER} -c"cd ${WEBSITE_BASEDIR}/postfixadmin-${PF_ADMIN_VERSION}
|
||||
su ${HTTP_USER} -c"cd ${WEBSITE_BASEDIR}/postfixadmin-${PF_ADMIN_VERSION} && \
|
||||
php /usr/local/bin/composer install --prefer-dist --no-interaction --no-dev" -s /bin/bash \
|
||||
> $log_file 2>&1
|
||||
else
|
||||
su ${HTTP_USER} -c"cd ${WEBSITE_BASEDIR}/postfixadmin-${PF_ADMIN_VERSION}
|
||||
su ${HTTP_USER} -c"cd ${WEBSITE_BASEDIR}/postfixadmin-${PF_ADMIN_VERSION} && \
|
||||
/usr/local/php-${php_latest_ver}/bin/php /usr/local/bin/composer --prefer-dist \
|
||||
--no-interaction install --no-dev" -s /bin/bash > $log_file 2>&1
|
||||
fi
|
||||
@@ -1197,6 +1272,24 @@ if [[ ${PF_ADMIN_MAJOR_VERSION} -gt 3 ]] ; then
|
||||
echo_ok
|
||||
else
|
||||
echo_failed
|
||||
|
||||
echo ""
|
||||
echo -e "\tCommand was: "
|
||||
if $PHP_DEBIAN_INSTALLATION ; then
|
||||
cat <<EOF
|
||||
su ${HTTP_USER} -c"cd ${WEBSITE_BASEDIR}/postfixadmin-${PF_ADMIN_VERSION} && \\
|
||||
php /usr/local/bin/composer --prefer-dist --no-interaction install --no-dev" -s /bin/bash \\
|
||||
> $log_file 2>&1
|
||||
EOF
|
||||
else
|
||||
cat <<EOF
|
||||
su ${HTTP_USER} -c"cd ${WEBSITE_BASEDIR}/postfixadmin-${PF_ADMIN_VERSION} && \\
|
||||
/usr/local/php-${php_latest_ver}/bin/php /usr/local/bin/composer --prefer-dist \\
|
||||
--no-interaction install --no-dev" -s /bin/bash > $log_file 2>&1
|
||||
EOF
|
||||
fi
|
||||
echo ""
|
||||
|
||||
error "$(cat $log_file)"
|
||||
|
||||
echononl "\tcontinue anyway [yes/no]: "
|
||||
|
||||
@@ -1046,11 +1046,11 @@ fi
|
||||
# -
|
||||
echononl "\tInstall a cronjob for cleaning up database"
|
||||
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 "# - Keep roundcube database slick and clean" >> /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
|
||||
if [[ $? -eq 0 ]]; then
|
||||
echo_ok
|
||||
|
||||
Executable
+160
@@ -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 ""
|
||||
@@ -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";
|
||||
}
|
||||
Executable
+96
@@ -0,0 +1,96 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Batch-update quota2 table for all active mailboxes from doveadm quota get."""
|
||||
|
||||
import subprocess
|
||||
import sys
|
||||
|
||||
DOVEADM = '/usr/local/dovecot/bin/doveadm'
|
||||
MYSQL = 'mysql'
|
||||
DB_USER = 'postfix'
|
||||
DB_PASS = 'T3CJnFMJNX9wmhNs'
|
||||
DB_SOCKET = '/run/mysqld/mysqld.sock'
|
||||
DB_NAME = 'postfix'
|
||||
|
||||
|
||||
def mysql_query(sql):
|
||||
return subprocess.run(
|
||||
[MYSQL, '-u', DB_USER, '-p' + DB_PASS, '--socket', DB_SOCKET,
|
||||
DB_NAME, '-sNe', sql],
|
||||
capture_output=True, text=True)
|
||||
|
||||
|
||||
def get_users(limit=None):
|
||||
query = 'SELECT username FROM mailbox WHERE active = 1 ORDER BY username;'
|
||||
if limit:
|
||||
query = 'SELECT username FROM mailbox WHERE active = 1 ORDER BY username LIMIT %d;' % limit
|
||||
r = mysql_query(query)
|
||||
return [u.strip() for u in r.stdout.strip().split('\n') if u.strip()]
|
||||
|
||||
|
||||
def get_quota(user):
|
||||
result = subprocess.run(
|
||||
[DOVEADM, 'quota', 'get', '-u', user],
|
||||
capture_output=True, text=True
|
||||
)
|
||||
bytes_val = None
|
||||
messages_val = None
|
||||
for line in result.stdout.splitlines():
|
||||
parts = line.split()
|
||||
if len(parts) < 4:
|
||||
continue
|
||||
if parts[2] == 'STORAGE' and parts[1] != 'Type':
|
||||
try:
|
||||
bytes_val = int(parts[3]) * 1024
|
||||
except ValueError:
|
||||
bytes_val = 0
|
||||
elif parts[2] == 'MESSAGE' and parts[1] != 'Type':
|
||||
try:
|
||||
messages_val = int(parts[3])
|
||||
except ValueError:
|
||||
messages_val = 0
|
||||
return bytes_val, messages_val
|
||||
|
||||
|
||||
def upsert_quota2(user, bytes_val, messages_val):
|
||||
sql = ("REPLACE INTO quota2 (username, bytes, messages) VALUES ('"
|
||||
+ user.replace("'", "\'") + "', "
|
||||
+ str(bytes_val) + ", " + str(messages_val) + ");")
|
||||
mysql_query(sql)
|
||||
|
||||
|
||||
def main():
|
||||
dry_run = '--dry-run' in sys.argv
|
||||
limit = None
|
||||
if '--limit' in sys.argv:
|
||||
idx = sys.argv.index('--limit')
|
||||
try:
|
||||
limit = int(sys.argv[idx + 1])
|
||||
except (IndexError, ValueError):
|
||||
print('Usage: update_quota2.sh [--dry-run] [--limit N]')
|
||||
sys.exit(1)
|
||||
|
||||
users = get_users(limit)
|
||||
total = len(users)
|
||||
print('Processing %d mailboxes...' % total)
|
||||
|
||||
ok = 0
|
||||
skip = 0
|
||||
for i, user in enumerate(users, 1):
|
||||
bytes_val, messages_val = get_quota(user)
|
||||
if bytes_val is None or messages_val is None:
|
||||
print(' [%d/%d] SKIP %s (no quota data)' % (i, total, user))
|
||||
skip += 1
|
||||
continue
|
||||
if dry_run:
|
||||
print(' [%d/%d] DRY %s: %d bytes, %d messages' % (i, total, user, bytes_val, messages_val))
|
||||
else:
|
||||
upsert_quota2(user, bytes_val, messages_val)
|
||||
if i % 50 == 0 or i == total:
|
||||
print(' [%d/%d] done' % (i, total))
|
||||
ok += 1
|
||||
|
||||
print('Done: %d updated, %d skipped.' % (ok, skip))
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
Executable
+108
@@ -0,0 +1,108 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Batch-update the quota2 table for all active mailboxes.
|
||||
|
||||
Uses 'doveadm quota get' (must run as root) to read current usage and
|
||||
writes it to quota2 via INSERT ... ON CONFLICT DO UPDATE.
|
||||
|
||||
Usage:
|
||||
update_quota2.sh [--dry-run] [--limit N]
|
||||
|
||||
Options:
|
||||
--dry-run Print what would be done without touching the database.
|
||||
--limit N Process only the first N mailboxes (useful for testing).
|
||||
"""
|
||||
|
||||
import subprocess
|
||||
import sys
|
||||
|
||||
DOVEADM = '/usr/local/dovecot/bin/doveadm'
|
||||
PSQL = 'psql'
|
||||
DB_USER = 'postfix'
|
||||
DB_NAME = 'postfix'
|
||||
|
||||
|
||||
def get_users(limit=None):
|
||||
query = 'SELECT username FROM mailbox WHERE active = true ORDER BY username;'
|
||||
if limit:
|
||||
query = ('SELECT username FROM mailbox WHERE active = true '
|
||||
'ORDER BY username LIMIT %d;' % limit)
|
||||
result = subprocess.run(
|
||||
[PSQL, '-U', DB_USER, DB_NAME, '-tAF', '|', '-c', query],
|
||||
capture_output=True, text=True
|
||||
)
|
||||
return [u.strip() for u in result.stdout.strip().split('\n') if u.strip()]
|
||||
|
||||
|
||||
def get_quota(user):
|
||||
"""Returns (bytes, messages) or (None, None) if not available."""
|
||||
result = subprocess.run(
|
||||
[DOVEADM, 'quota', 'get', '-u', user],
|
||||
capture_output=True, text=True
|
||||
)
|
||||
bytes_val = messages_val = None
|
||||
for line in result.stdout.splitlines():
|
||||
parts = line.split()
|
||||
if len(parts) < 4:
|
||||
continue
|
||||
if parts[2] == 'STORAGE' and parts[1] != 'Type':
|
||||
try:
|
||||
bytes_val = int(parts[3]) * 1024 # KiB → bytes
|
||||
except ValueError:
|
||||
bytes_val = 0
|
||||
elif parts[2] == 'MESSAGE' and parts[1] != 'Type':
|
||||
try:
|
||||
messages_val = int(parts[3])
|
||||
except ValueError:
|
||||
messages_val = 0
|
||||
return bytes_val, messages_val
|
||||
|
||||
|
||||
def upsert_quota2(user, bytes_val, messages_val):
|
||||
sql = (
|
||||
"INSERT INTO quota2 (username, bytes, messages) VALUES ('"
|
||||
+ user.replace("'", "''")
|
||||
+ "', " + str(bytes_val) + ", " + str(messages_val) + ") "
|
||||
"ON CONFLICT (username) DO UPDATE "
|
||||
"SET bytes = EXCLUDED.bytes, messages = EXCLUDED.messages;"
|
||||
)
|
||||
subprocess.run([PSQL, '-U', DB_USER, DB_NAME, '-c', sql],
|
||||
capture_output=True)
|
||||
|
||||
|
||||
def main():
|
||||
dry_run = '--dry-run' in sys.argv
|
||||
limit = None
|
||||
if '--limit' in sys.argv:
|
||||
idx = sys.argv.index('--limit')
|
||||
try:
|
||||
limit = int(sys.argv[idx + 1])
|
||||
except (IndexError, ValueError):
|
||||
print('Usage: update_quota2.sh [--dry-run] [--limit N]')
|
||||
sys.exit(1)
|
||||
|
||||
users = get_users(limit)
|
||||
total = len(users)
|
||||
print('Processing %d mailboxes...' % total)
|
||||
|
||||
updated = skipped = 0
|
||||
for i, user in enumerate(users, 1):
|
||||
bytes_val, messages_val = get_quota(user)
|
||||
if bytes_val is None or messages_val is None:
|
||||
print(' [%d/%d] SKIP %s (no quota data)' % (i, total, user))
|
||||
skipped += 1
|
||||
continue
|
||||
if dry_run:
|
||||
print(' [%d/%d] DRY %s: %d bytes, %d messages'
|
||||
% (i, total, user, bytes_val, messages_val))
|
||||
else:
|
||||
upsert_quota2(user, bytes_val, messages_val)
|
||||
if i % 50 == 0 or i == total:
|
||||
print(' [%d/%d] done' % (i, total))
|
||||
updated += 1
|
||||
|
||||
print('Done: %d updated, %d skipped.' % (updated, skipped))
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
+243
-15
@@ -4,6 +4,7 @@ clear
|
||||
echo -e "\n \033[32mStart script for upgrading Roundcube Webmailer..\033[m"
|
||||
|
||||
CUR_IFS="$IFS"
|
||||
export COMPOSER_ALLOW_SUPERUSER=1
|
||||
|
||||
## -----------------------------------------------------------------
|
||||
## ----------------------------------------------------------------
|
||||
@@ -295,6 +296,12 @@ DEFAULT_WEBSITE_BASEDIR="/var/www/${WEBSITE_NAME}"
|
||||
[[ -n "$WEBSITE_BASEDIR" ]] || WEBSITE_BASEDIR=$DEFAULT_WEBSITE_BASEDIR
|
||||
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
|
||||
fatal "No current installation of roundcube found!"
|
||||
fi
|
||||
@@ -429,6 +436,9 @@ do
|
||||
echo -e "\n\t\033[33m\033[1mA version number is required!\033[m\n"
|
||||
fi
|
||||
done
|
||||
# Hauptversionsnummer extrahieren (1.6 oder 1.7)
|
||||
RC_MAIN_VERSION=$(echo "$ROUNDCUBE_VERSION" | cut -d. -f1-2)
|
||||
|
||||
echo ""
|
||||
echo -e "\033[32m--\033[m"
|
||||
echo ""
|
||||
@@ -521,6 +531,7 @@ echo ""
|
||||
echo -e "\033[1;32mSettings for upgrading \033[1;37mRoundcube Webmail\033[m"
|
||||
echo ""
|
||||
echo -e "\tRoundcube Version....................: $ROUNDCUBE_VERSION"
|
||||
echo -e "\tRoundcube Main Version...............: $RC_MAIN_VERSION"
|
||||
echo ""
|
||||
echo -e "\tName of the Website..................: $WEBSITE_NAME"
|
||||
echo ""
|
||||
@@ -891,12 +902,16 @@ fi
|
||||
|
||||
|
||||
echononl "\tRename the composer.json-dist file into composer.json"
|
||||
cp -a "composer.json-dist" "composer.json" > $log_file 2>&1
|
||||
if [[ $? -eq 0 ]]; then
|
||||
if [ -f "composer.json-dist" ] ; then
|
||||
cp -a "composer.json-dist" "composer.json" > $log_file 2>&1
|
||||
if [[ $? -eq 0 ]]; then
|
||||
echo_ok
|
||||
else
|
||||
else
|
||||
echo_failed
|
||||
error "$(cat $log_file)"
|
||||
fi
|
||||
else
|
||||
echo_skipped
|
||||
fi
|
||||
|
||||
echononl "\tInstall composer to ${WEBSITE_BASEDIR}/roundcubemail-${ROUNDCUBE_VERSION}"
|
||||
@@ -940,21 +955,112 @@ else
|
||||
fi
|
||||
|
||||
|
||||
echo ""
|
||||
echo ""
|
||||
echo "You might want to deactivate some plugins in the configuration file, e.g. the plugin 'carddav'.."
|
||||
echo ""
|
||||
# Root-Warning unterdrücken
|
||||
export COMPOSER_ALLOW_SUPERUSER=1
|
||||
export COMPOSER_ROOT_VERSION=${ROUNDCUBE_VERSION}
|
||||
|
||||
echononl "continue [yes/no]: "
|
||||
read OK
|
||||
OK="$(echo "$OK" | tr '[:upper:]' '[:lower:]')"
|
||||
while [[ "$OK" != "yes" ]] && [[ "$OK" != "no" ]] ; do
|
||||
|
||||
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"
|
||||
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 .."
|
||||
php composer.phar --no-interaction config allow-plugins.roundcube/plugin-installer true > $log_file 2>&1
|
||||
if [[ $? -eq 0 ]]; then
|
||||
echo_ok
|
||||
else
|
||||
echo_failed
|
||||
error "command was:\n\t # cd "${WEBSITE_BASEDIR}/roundcubemail-${ROUNDCUBE_VERSION}"\n\t # php composer.phar --no-interaction config allow-plugins.roundcube/plugin-installer truen\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
|
||||
|
||||
|
||||
echononl " Deactivate Security-Advisory-Block (for Dev-Deps).."
|
||||
php composer.phar --no-interaction config policy.advisories.block false > $log_file 2>&1
|
||||
if [[ $? -eq 0 ]]; then
|
||||
echo_ok
|
||||
else
|
||||
echo_failed
|
||||
error "command was:\n\t # cd "${WEBSITE_BASEDIR}/roundcubemail-${ROUNDCUBE_VERSION}"\n\t # php composer.phar --no-interaction config policy.advisories.block false\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
|
||||
|
||||
|
||||
echononl " Update dependencies by running 'php composer.phar update --no-dev'"
|
||||
php composer.phar --no-interaction update --no-dev > ${_log_dir}/update_roundcube-${ROUNDCUBE_VERSION}-dependencies.${backup_date}.log 2>&1
|
||||
@@ -974,6 +1080,125 @@ else
|
||||
[[ $OK = "yes" ]] || fatal "Abbruch durch User"
|
||||
fi
|
||||
|
||||
|
||||
echononl " Reactivate Security-Advisory-Block .."
|
||||
php composer.phar --no-interaction config policy.advisories.block true
|
||||
if [[ $? -eq 0 ]]; then
|
||||
echo_ok
|
||||
else
|
||||
echo_failed
|
||||
error "command was:\n\t # cd "${WEBSITE_BASEDIR}/roundcubemail-${ROUNDCUBE_VERSION}"\n\t #php composer.phar --no-interaction config policy.advisories.block true\ni\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
|
||||
|
||||
|
||||
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"
|
||||
${WEBSITE_BASEDIR}/roundcubemail-${ROUNDCUBE_VERSION}/bin/indexcontacts.sh > $log_file 2>&1
|
||||
if [[ $? -eq 0 ]]; then
|
||||
@@ -1006,7 +1231,11 @@ elif [[ -f "${WEBSITE_BASEDIR}/htdocs" ]]; then
|
||||
fi
|
||||
fi
|
||||
|
||||
ln -s "roundcubemail-${ROUNDCUBE_VERSION}" "${WEBSITE_BASEDIR}/htdocs" >> $log_file 2>&1
|
||||
if [ "$RC_MAIN_VERSION" == "1.7" ]; then
|
||||
ln -s "roundcubemail-${ROUNDCUBE_VERSION}/public_html/" "${WEBSITE_BASEDIR}/htdocs" >> $log_file 2>&1
|
||||
else
|
||||
ln -s "roundcubemail-${ROUNDCUBE_VERSION}" "${WEBSITE_BASEDIR}/htdocs" >> $log_file 2>&1
|
||||
fi
|
||||
if [[ $? -ne 0 ]]; then
|
||||
_failed=true
|
||||
fi
|
||||
@@ -1106,4 +1335,3 @@ fi
|
||||
|
||||
echo ""
|
||||
clean_up 0
|
||||
|
||||
|
||||
Reference in New Issue
Block a user