diff --git a/SQL/mysql.initial.sql b/SQL/mysql.initial.sql index 57e9d03..6b0df0a 100644 --- a/SQL/mysql.initial.sql +++ b/SQL/mysql.initial.sql @@ -37,6 +37,10 @@ CREATE TABLE IF NOT EXISTS `ident_switch` smallint NOT NULL DEFAULT 1, + `smtp_username` + varchar(64), + `smtp_password` + varchar(255), `sieve_host` varchar(64), `sieve_port` @@ -46,6 +50,10 @@ CREATE TABLE IF NOT EXISTS `ident_switch` smallint NOT NULL DEFAULT 1, + `sieve_username` + varchar(64), + `sieve_password` + varchar(255), `notify_check` smallint NOT NULL @@ -59,9 +67,6 @@ CREATE TABLE IF NOT EXISTS `ident_switch` `notify_desktop` smallint DEFAULT NULL, - `notify_sound_url` - varchar(255) - DEFAULT NULL, `drafts_mbox` varchar(64), `sent_mbox` diff --git a/SQL/mysql/2026021000.sql b/SQL/mysql/2026021000.sql index 31c60bb..2a13954 100644 --- a/SQL/mysql/2026021000.sql +++ b/SQL/mysql/2026021000.sql @@ -1,5 +1,76 @@ +-- Upgrade from v4.x to v5.x +-- Increase password column for encrypted values ALTER TABLE `ident_switch` MODIFY `password` varchar(255); +-- Add unique constraint on identity ID ALTER TABLE `ident_switch` ADD UNIQUE (`iid`); + +-- Add Sieve support +ALTER TABLE `ident_switch` + ADD `sieve_host` varchar(64) AFTER `smtp_auth`; + +ALTER TABLE `ident_switch` + ADD `sieve_port` + int + CHECK(`sieve_port` > 0 AND `sieve_port` <= 65535) + AFTER `sieve_host`; + +ALTER TABLE `ident_switch` + ADD `sieve_auth` + smallint + NOT NULL + DEFAULT 1 + AFTER `sieve_port`; + +-- Add custom SMTP/Sieve credentials +ALTER TABLE `ident_switch` + ADD `smtp_username` + varchar(64) + DEFAULT NULL + AFTER `smtp_auth`; + +ALTER TABLE `ident_switch` + ADD `smtp_password` + varchar(255) + DEFAULT NULL + AFTER `smtp_username`; + +ALTER TABLE `ident_switch` + ADD `sieve_username` + varchar(64) + DEFAULT NULL + AFTER `sieve_auth`; + +ALTER TABLE `ident_switch` + ADD `sieve_password` + varchar(255) + DEFAULT NULL + AFTER `sieve_username`; + +-- Add notification settings +ALTER TABLE `ident_switch` + ADD `notify_check` + smallint + NOT NULL + DEFAULT 1 + AFTER `sieve_password`; + +ALTER TABLE `ident_switch` + ADD `notify_basic` + smallint + DEFAULT NULL + AFTER `notify_check`; + +ALTER TABLE `ident_switch` + ADD `notify_sound` + smallint + DEFAULT NULL + AFTER `notify_basic`; + +ALTER TABLE `ident_switch` + ADD `notify_desktop` + smallint + DEFAULT NULL + AFTER `notify_sound`; diff --git a/SQL/mysql/2026021001.sql b/SQL/mysql/2026021001.sql deleted file mode 100644 index 6113d76..0000000 --- a/SQL/mysql/2026021001.sql +++ /dev/null @@ -1,15 +0,0 @@ -ALTER TABLE `ident_switch` - ADD `sieve_host` varchar(64) AFTER `smtp_auth`; - -ALTER TABLE `ident_switch` - ADD `sieve_port` - int - CHECK(`sieve_port` > 0 AND `sieve_port` <= 65535) - AFTER `sieve_host`; - -ALTER TABLE `ident_switch` - ADD `sieve_auth` - smallint - NOT NULL - DEFAULT 1 - AFTER `sieve_port`; diff --git a/SQL/mysql/2026021002.sql b/SQL/mysql/2026021002.sql deleted file mode 100644 index 196d232..0000000 --- a/SQL/mysql/2026021002.sql +++ /dev/null @@ -1,30 +0,0 @@ -ALTER TABLE `ident_switch` - ADD `notify_check` - smallint - NOT NULL - DEFAULT 1 - AFTER `sieve_auth`; - -ALTER TABLE `ident_switch` - ADD `notify_basic` - smallint - DEFAULT NULL - AFTER `notify_check`; - -ALTER TABLE `ident_switch` - ADD `notify_sound` - smallint - DEFAULT NULL - AFTER `notify_basic`; - -ALTER TABLE `ident_switch` - ADD `notify_desktop` - smallint - DEFAULT NULL - AFTER `notify_sound`; - -ALTER TABLE `ident_switch` - ADD `notify_sound_url` - varchar(255) - DEFAULT NULL - AFTER `notify_desktop`; diff --git a/SQL/postgres.initial.sql b/SQL/postgres.initial.sql index 6e330ec..e7001c7 100644 --- a/SQL/postgres.initial.sql +++ b/SQL/postgres.initial.sql @@ -38,6 +38,10 @@ CREATE TABLE ident_switch smallint NOT NULL DEFAULT(1), + smtp_username + varchar(64), + smtp_password + varchar(255), sieve_host varchar(64), sieve_port @@ -47,6 +51,10 @@ CREATE TABLE ident_switch smallint NOT NULL DEFAULT(1), + sieve_username + varchar(64), + sieve_password + varchar(255), notify_check smallint NOT NULL @@ -60,9 +68,6 @@ CREATE TABLE ident_switch notify_desktop smallint DEFAULT NULL, - notify_sound_url - varchar(255) - DEFAULT NULL, drafts_mbox varchar(64), sent_mbox diff --git a/SQL/postgres/2026021000.sql b/SQL/postgres/2026021000.sql index 21a5149..40542a3 100644 --- a/SQL/postgres/2026021000.sql +++ b/SQL/postgres/2026021000.sql @@ -1,4 +1,53 @@ +-- Upgrade from v4.x to v5.x +-- Increase password column for encrypted values ALTER TABLE ident_switch ALTER COLUMN password TYPE varchar(255); +-- Add unique constraint on identity ID +ALTER TABLE ident_switch + ADD CONSTRAINT ident_switch_iid_unique UNIQUE (iid); + CREATE INDEX IF NOT EXISTS IX_ident_switch_iid ON ident_switch(iid); + +-- Add Sieve support +ALTER TABLE ident_switch + ADD sieve_host varchar(64); + +ALTER TABLE ident_switch + ADD sieve_port + integer + CHECK(sieve_port > 0 AND sieve_port <= 65535); + +ALTER TABLE ident_switch + ADD sieve_auth + smallint + NOT NULL + DEFAULT(1); + +-- Add custom SMTP/Sieve credentials +ALTER TABLE ident_switch ADD COLUMN smtp_username varchar(64) DEFAULT NULL; +ALTER TABLE ident_switch ADD COLUMN smtp_password varchar(255) DEFAULT NULL; +ALTER TABLE ident_switch ADD COLUMN sieve_username varchar(64) DEFAULT NULL; +ALTER TABLE ident_switch ADD COLUMN sieve_password varchar(255) DEFAULT NULL; + +-- Add notification settings +ALTER TABLE ident_switch + ADD notify_check + smallint + NOT NULL + DEFAULT 1; + +ALTER TABLE ident_switch + ADD notify_basic + smallint + DEFAULT NULL; + +ALTER TABLE ident_switch + ADD notify_sound + smallint + DEFAULT NULL; + +ALTER TABLE ident_switch + ADD notify_desktop + smallint + DEFAULT NULL; diff --git a/SQL/postgres/2026021001.sql b/SQL/postgres/2026021001.sql deleted file mode 100644 index e1e235a..0000000 --- a/SQL/postgres/2026021001.sql +++ /dev/null @@ -1,13 +0,0 @@ -ALTER TABLE ident_switch - ADD sieve_host varchar(64); - -ALTER TABLE ident_switch - ADD sieve_port - integer - CHECK(sieve_port > 0 AND sieve_port <= 65535); - -ALTER TABLE ident_switch - ADD sieve_auth - smallint - NOT NULL - DEFAULT(1); diff --git a/SQL/postgres/2026021002.sql b/SQL/postgres/2026021002.sql deleted file mode 100644 index 6a9ddd3..0000000 --- a/SQL/postgres/2026021002.sql +++ /dev/null @@ -1,25 +0,0 @@ -ALTER TABLE ident_switch - ADD notify_check - smallint - NOT NULL - DEFAULT 1; - -ALTER TABLE ident_switch - ADD notify_basic - smallint - DEFAULT NULL; - -ALTER TABLE ident_switch - ADD notify_sound - smallint - DEFAULT NULL; - -ALTER TABLE ident_switch - ADD notify_desktop - smallint - DEFAULT NULL; - -ALTER TABLE ident_switch - ADD notify_sound_url - varchar(255) - DEFAULT NULL; diff --git a/SQL/sqlite.initial.sql b/SQL/sqlite.initial.sql index 1e8d9fe..7a9b266 100644 --- a/SQL/sqlite.initial.sql +++ b/SQL/sqlite.initial.sql @@ -38,6 +38,10 @@ CREATE TABLE ident_switch smallint NOT NULL DEFAULT 1, + smtp_username + varchar(64), + smtp_password + varchar(255), sieve_host varchar(64), sieve_port @@ -47,6 +51,10 @@ CREATE TABLE ident_switch smallint NOT NULL DEFAULT 1, + sieve_username + varchar(64), + sieve_password + varchar(255), notify_check smallint NOT NULL @@ -60,9 +68,6 @@ CREATE TABLE ident_switch notify_desktop smallint DEFAULT NULL, - notify_sound_url - varchar(255) - DEFAULT NULL, drafts_mbox varchar(64), sent_mbox diff --git a/SQL/sqlite/2026021000.sql b/SQL/sqlite/2026021000.sql index af92521..c808b7c 100644 --- a/SQL/sqlite/2026021000.sql +++ b/SQL/sqlite/2026021000.sql @@ -1,3 +1,5 @@ +-- Upgrade from v4.x to v5.x +-- SQLite: recreate table with all new columns PRAGMA foreign_keys=off; BEGIN TRANSACTION; @@ -43,6 +45,36 @@ CREATE TABLE ident_switch smallint NOT NULL DEFAULT 1, + smtp_username + varchar(64), + smtp_password + varchar(255), + sieve_host + varchar(64), + sieve_port + integer + CHECK(sieve_port > 0 AND sieve_port <= 65535), + sieve_auth + smallint + NOT NULL + DEFAULT 1, + sieve_username + varchar(64), + sieve_password + varchar(255), + notify_check + smallint + NOT NULL + DEFAULT 1, + notify_basic + smallint + DEFAULT NULL, + notify_sound + smallint + DEFAULT NULL, + notify_desktop + smallint + DEFAULT NULL, drafts_mbox varchar(64), sent_mbox diff --git a/SQL/sqlite/2026021001.sql b/SQL/sqlite/2026021001.sql deleted file mode 100644 index 75a35ad..0000000 --- a/SQL/sqlite/2026021001.sql +++ /dev/null @@ -1,13 +0,0 @@ -ALTER TABLE ident_switch - ADD sieve_host varchar(64); - -ALTER TABLE ident_switch - ADD sieve_port - integer - CHECK(sieve_port > 0 AND sieve_port <= 65535); - -ALTER TABLE ident_switch - ADD sieve_auth - smallint - NOT NULL - DEFAULT 1; diff --git a/SQL/sqlite/2026021002.sql b/SQL/sqlite/2026021002.sql deleted file mode 100644 index 6a9ddd3..0000000 --- a/SQL/sqlite/2026021002.sql +++ /dev/null @@ -1,25 +0,0 @@ -ALTER TABLE ident_switch - ADD notify_check - smallint - NOT NULL - DEFAULT 1; - -ALTER TABLE ident_switch - ADD notify_basic - smallint - DEFAULT NULL; - -ALTER TABLE ident_switch - ADD notify_sound - smallint - DEFAULT NULL; - -ALTER TABLE ident_switch - ADD notify_desktop - smallint - DEFAULT NULL; - -ALTER TABLE ident_switch - ADD notify_sound_url - varchar(255) - DEFAULT NULL; diff --git a/config.inc.php.dist b/config.inc.php.dist index f8c4fea..d88ae33 100644 --- a/config.inc.php.dist +++ b/config.inc.php.dist @@ -82,3 +82,13 @@ $config['ident_switch.round_robin'] = false; * Default: false (warning is displayed). */ $config['ident_switch.hide_notifier_warning'] = false; + +/* + * Restrict account switching to preconfigured domains only. + * When enabled, the ident_switch form is hidden for identities whose email + * domain does not match any entry in 'ident_switch.preconfig'. + * Users can still create Roundcube identities (name, signature, etc.) but + * cannot configure server connections for non-preconfigured domains. + * Default: false (all domains allowed). + */ +$config['ident_switch.preconfig_only'] = false; diff --git a/ident_switch-form.js b/ident_switch-form.js index 870ef50..8ad57f6 100644 --- a/ident_switch-form.js +++ b/ident_switch-form.js @@ -43,6 +43,23 @@ $(function() { }); }); + // IMAP host → update SMTP/Sieve host placeholders + $("INPUT[name='_ident_switch.form.imap.host']").on('input change blur', function() { + var imapHost = $(this).val() || 'localhost'; + $("INPUT[name='_ident_switch.form.smtp.host']").attr('placeholder', imapHost); + $("INPUT[name='_ident_switch.form.sieve.host']").attr('placeholder', imapHost); + }); + + // Bind auth change handlers for SMTP and Sieve custom credentials + $.each(['smtp', 'sieve'], function(i, proto) { + var authSel = "SELECT[name='_ident_switch.form." + proto + ".auth']"; + $(authSel).on('change', function() { + plugin_switchIdent_onAuthChange(proto, $(this).val()); + }); + // Apply initial visibility + plugin_switchIdent_onAuthChange(proto, $(authSel).val()); + }); + // Delimiter mode handler $("SELECT[name='_ident_switch.form.imap.delimiter_mode']").on('change', function() { if ($(this).val() === 'manual') { @@ -52,6 +69,22 @@ $(function() { $("INPUT[name='_ident_switch.form.imap.delimiter']").val(''); } }); + + // Watch email field for dynamic preconfig application + $("INPUT[name='_email']").on('change blur', function() { + plugin_switchIdent_onEmailChange($(this).val()); + }); + + // Set initial placeholders from current field values + var initialEmail = $("INPUT[name='_email']").val(); + if (initialEmail) { + $("INPUT[name='_ident_switch.form.common.label']").attr('placeholder', initialEmail); + } + var initialImapHost = $("INPUT[name='_ident_switch.form.imap.host']").val(); + if (initialImapHost) { + $("INPUT[name='_ident_switch.form.smtp.host']").attr('placeholder', initialImapHost); + $("INPUT[name='_ident_switch.form.sieve.host']").attr('placeholder', initialImapHost); + } }); /** @@ -104,11 +137,29 @@ function plugin_switchIdent_clearIfDefault($field) { } } +/** + * Handle auth dropdown change: show/hide custom credential fields. + * @param {string} proto - Protocol name (smtp, sieve). + * @param {string} authVal - Selected auth value. + */ +function plugin_switchIdent_onAuthChange(proto, authVal) { + var isCustom = (authVal === '3'); // SMTP_AUTH_CUSTOM / SIEVE_AUTH_CUSTOM + var userFld = $("INPUT[name='_ident_switch.form." + proto + ".username']"); + var passFld = $("INPUT[name='_ident_switch.form." + proto + ".password']"); + if (isCustom) { + userFld.parentsUntil("TABLE", "TR").show(); + passFld.parentsUntil("TABLE", "TR").show(); + } else { + userFld.parentsUntil("TABLE", "TR").hide(); + passFld.parentsUntil("TABLE", "TR").hide(); + } +} + function plugin_switchIdent_processPreconfig() { var disFld = $("INPUT[name='_ident_switch.form.common.readonly']"); disFld.parentsUntil("TABLE", "TR").hide(); - var disVal = disFld.val(); + var disVal = parseInt(disFld.val(), 10) || 0; if (disVal > 0) { $("INPUT[name='_ident_switch.form.imap.host']").prop("disabled", true); $("SELECT[name='_ident_switch.form.imap.security']").prop("disabled", true); @@ -117,6 +168,9 @@ function plugin_switchIdent_processPreconfig() { $("INPUT[name='_ident_switch.form.smtp.host']").prop("disabled", true); $("SELECT[name='_ident_switch.form.smtp.security']").prop("disabled", true); $("INPUT[name='_ident_switch.form.smtp.port']").prop("disabled", true); + $("SELECT[name='_ident_switch.form.smtp.auth']").prop("disabled", true); + $("INPUT[name='_ident_switch.form.smtp.username']").prop("disabled", true); + $("INPUT[name='_ident_switch.form.smtp.password']").prop("disabled", true); $("SELECT[name='_ident_switch.form.imap.delimiter_mode']").prop("disabled", true); $("INPUT[name='_ident_switch.form.imap.delimiter']").prop("disabled", true); @@ -124,8 +178,11 @@ function plugin_switchIdent_processPreconfig() { $("INPUT[name='_ident_switch.form.sieve.host']").prop("disabled", true); $("SELECT[name='_ident_switch.form.sieve.security']").prop("disabled", true); $("INPUT[name='_ident_switch.form.sieve.port']").prop("disabled", true); + $("SELECT[name='_ident_switch.form.sieve.auth']").prop("disabled", true); + $("INPUT[name='_ident_switch.form.sieve.username']").prop("disabled", true); + $("INPUT[name='_ident_switch.form.sieve.password']").prop("disabled", true); } - if (2 == disVal) { + if (disVal === 2) { $("INPUT[name='_ident_switch.form.imap.username']").prop("disabled", true); } } @@ -138,3 +195,93 @@ function plugin_switchIdent_enabled_onChange(e) { $("SELECT", $fieldset).prop("disabled", !isEnabled); plugin_switchIdent_processPreconfig(); } + +/** + * Handle email field change: apply preconfig and manage domain restriction. + * @param {string} email - The email address entered by the user. + */ +function plugin_switchIdent_onEmailChange(email) { + var atPos = email.indexOf('@'); + if (atPos < 0) return; + + var domain = email.substring(atPos + 1).toLowerCase(); + if (!domain) return; + + // Update label and username placeholders to match current email + $("INPUT[name='_ident_switch.form.common.label']").attr('placeholder', email); + + var preconfig = rcmail.env.ident_switch_preconfig || {}; + var preconfigOnly = rcmail.env.ident_switch_preconfig_only || false; + var cfg = preconfig[domain] || preconfig['*'] || null; + + // Update username placeholder to match current email + $("INPUT[name='_ident_switch.form.imap.username']").attr('placeholder', email); + + // Show/hide domain warning + if (preconfigOnly && !cfg) { + var tpl = rcmail.env.ident_switch_warning_tpl || ''; + $('#ident-switch-domain-warning').text(tpl.replace('%s', domain)).show(); + $("INPUT[name='_ident_switch.form.common.enabled']").prop('checked', false).prop('disabled', true); + plugin_switchIdent_enabled_onChange(); + return; + } + + $('#ident-switch-domain-warning').hide(); + $("INPUT[name='_ident_switch.form.common.enabled']").prop('disabled', false); + + // Only auto-fill for identities without an existing DB record + if (!rcmail.env.ident_switch_has_record && cfg) { + plugin_switchIdent_applyJsPreconfig(cfg, email); + } +} + +/** + * Apply preconfig values from JS environment to the form fields. + * @param {object} cfg - Preconfig entry for the matched domain. + * @param {string} email - The full email address. + */ +function plugin_switchIdent_applyJsPreconfig(cfg, email) { + // Apply protocol settings + $.each(['imap', 'smtp', 'sieve'], function(_, proto) { + if (!cfg[proto]) return; + + // Set security first (updates port placeholder) + var security = cfg[proto].security || ''; + $("SELECT[name='_ident_switch.form." + proto + ".security']").val(security); + plugin_switchIdent_onSecurityChange(proto, security); + + // Set host + $("INPUT[name='_ident_switch.form." + proto + ".host']").val(cfg[proto].host || ''); + + // Set port (empty if matches default, so placeholder shows) + var port = cfg[proto].port; + var defaultPort = ident_switch_portDefaults[proto][security] || ''; + $("INPUT[name='_ident_switch.form." + proto + ".port']").val( + (port && port != defaultPort) ? port : '' + ); + }); + + // Apply username from preconfig user mode + if (cfg.user) { + var username = ''; + if (cfg.user.toUpperCase() === 'EMAIL') { + username = email; + } else if (cfg.user.toUpperCase() === 'MBOX') { + username = email.split('@')[0]; + } + if (username) { + $("INPUT[name='_ident_switch.form.imap.username']").val(username); + } + } + + // Apply readonly level + var readonlyLevel = 0; + if (cfg.readonly) { + var hasUser = cfg.user && ['EMAIL', 'MBOX'].indexOf(cfg.user.toUpperCase()) >= 0; + readonlyLevel = hasUser ? 2 : 1; + } + $("INPUT[name='_ident_switch.form.common.readonly']").val(readonlyLevel); + + // Re-apply enabled/disabled state (enables fields, then processPreconfig disables readonly ones) + plugin_switchIdent_enabled_onChange(); +} diff --git a/ident_switch.php b/ident_switch.php index a33c43c..36c192c 100644 --- a/ident_switch.php +++ b/ident_switch.php @@ -42,12 +42,18 @@ class ident_switch extends rcube_plugin /** @var int SMTP authentication: no authentication required. */ public const SMTP_AUTH_NONE = 2; + /** @var int SMTP authentication: use custom credentials. */ + public const SMTP_AUTH_CUSTOM = 3; + /** @var int Sieve authentication: use same credentials as IMAP. */ public const SIEVE_AUTH_IMAP = 1; /** @var int Sieve authentication: no authentication required. */ public const SIEVE_AUTH_NONE = 2; + /** @var int Sieve authentication: use custom credentials. */ + public const SIEVE_AUTH_CUSTOM = 3; + /** @var int Notification checking: enabled. */ public const NOTIFY_CHECK_ENABLED = 1; @@ -189,7 +195,7 @@ class ident_switch extends rcube_plugin while ($r = $rc->db->fetch_assoc($qRec)) { $accValues[] = $r['id']; $iidMap[$r['iid']] = $r['id']; - if ($iid == $r['iid']) { + if ($iid === (int)$r['iid']) { $accSelected = $r['id']; } diff --git a/lib/IdentSwitchChecker.php b/lib/IdentSwitchChecker.php index f61a997..336e112 100644 --- a/lib/IdentSwitchChecker.php +++ b/lib/IdentSwitchChecker.php @@ -31,9 +31,9 @@ class IdentSwitchChecker $identities = $this->get_checkable_identities($rc); // Exclude the currently active secondary identity (RC already checks it) - $activeIid = $_SESSION['iid' . ident_switch::MY_POSTFIX] ?? -1; + $activeIid = (int)($_SESSION['iid' . ident_switch::MY_POSTFIX] ?? -1); $identities = array_values(array_filter($identities, function ($id) use ($activeIid) { - return $id['iid'] != $activeIid; + return (int)$id['iid'] !== $activeIid; })); // When impersonating, also check the primary account @@ -132,6 +132,10 @@ class IdentSwitchChecker $username = $identity['username'] ?: $identity['email']; $password = $rc->decrypt($identity['password']); + if ($password === false) { + ident_switch::write_log("Failed to decrypt password for identity {$identity['iid']}"); + return $previousCount; + } $result = $imap->connect($host, $username, $password, [ 'port' => $port, diff --git a/lib/IdentSwitchForm.php b/lib/IdentSwitchForm.php index 7266b5b..3a228fe 100644 --- a/lib/IdentSwitchForm.php +++ b/lib/IdentSwitchForm.php @@ -29,12 +29,25 @@ class IdentSwitchForm * @param array $record Identity record data used for placeholders. * @return array Form field definitions for enabled, label, and readonly. */ - public static function get_common_fields(array &$record): array + public function get_common_fields(array &$record): array { $prefix = 'ident_switch.form.common.'; + + $labelInput = new html_inputfield([ + 'name' => "_{$prefix}label", + 'type' => 'text', + 'size' => 32, + 'placeholder' => $record['email'] ?? '', + ]); + $labelHtml = $labelInput->show($record["{$prefix}label"] ?? '') + . html::span( + ['class' => 'form-text'], + rcube::Q($this->plugin->gettext('form.common.label.hint')) + ); + return [ $prefix . 'enabled' => ['type' => 'checkbox', 'onchange' => 'plugin_switchIdent_enabled_onChange();'], - $prefix . 'label' => ['type' => 'text', 'size' => 32, 'placeholder' => $record['email'] ?? ''], + $prefix . 'label' => ['value' => $labelHtml], $prefix . 'readonly' => ['type' => 'hidden'], ]; } @@ -53,7 +66,7 @@ class IdentSwitchForm $prefix . 'security' => ['value' => $this->build_security_select($prefix, $record, 'ssl')], $prefix . 'port' => ['type' => 'text', 'size' => 5, 'placeholder' => 993], $prefix . 'username' => ['type' => 'text', 'size' => 64, 'placeholder' => $record['email'] ?? ''], - $prefix . 'password' => ['type' => 'password', 'size' => 64], + $prefix . 'password' => ['type' => 'password', 'size' => 64, 'autocomplete' => 'new-password'], $prefix . 'delimiter' => ['value' => $this->build_delimiter_field($prefix, $record)], ]; } @@ -71,12 +84,19 @@ class IdentSwitchForm $authType = new html_select(['name' => "_{$prefix}auth"]); $authType->add($this->plugin->gettext('form.smtp.auth.imap'), ident_switch::SMTP_AUTH_IMAP); $authType->add($this->plugin->gettext('form.smtp.auth.none'), ident_switch::SMTP_AUTH_NONE); + $authType->add($this->plugin->gettext('form.smtp.auth.custom'), ident_switch::SMTP_AUTH_CUSTOM); + + // Cast to int: html_select::show() uses strict comparison (===), + // option values are integers (constants), but POST/DB may return strings. + $authVal = isset($record[$prefix . 'auth']) ? (int)$record[$prefix . 'auth'] : null; return [ $prefix . 'host' => ['type' => 'text', 'size' => 64, 'placeholder' => 'localhost'], $prefix . 'security' => ['value' => $this->build_security_select($prefix, $record, 'tls')], $prefix . 'port' => ['type' => 'text', 'size' => 5, 'placeholder' => 587], - $prefix . 'auth' => ['value' => $authType->show([$record['ident_switch.form.smtp.auth'] ?? null])], + $prefix . 'auth' => ['value' => $authType->show($authVal !== null ? [$authVal] : [])], + $prefix . 'username' => ['type' => 'text', 'size' => 64, 'autocomplete' => 'off'], + $prefix . 'password' => ['type' => 'password', 'size' => 64, 'autocomplete' => 'new-password'], ]; } @@ -93,12 +113,18 @@ class IdentSwitchForm $authType = new html_select(['name' => "_{$prefix}auth"]); $authType->add($this->plugin->gettext('form.sieve.auth.imap'), ident_switch::SIEVE_AUTH_IMAP); $authType->add($this->plugin->gettext('form.sieve.auth.none'), ident_switch::SIEVE_AUTH_NONE); + $authType->add($this->plugin->gettext('form.sieve.auth.custom'), ident_switch::SIEVE_AUTH_CUSTOM); + + // Cast to int: html_select::show() uses strict comparison (===) + $authVal = isset($record[$prefix . 'auth']) ? (int)$record[$prefix . 'auth'] : null; return [ $prefix . 'host' => ['type' => 'text', 'size' => 64, 'placeholder' => 'localhost'], $prefix . 'security' => ['value' => $this->build_security_select($prefix, $record, 'tls')], $prefix . 'port' => ['type' => 'text', 'size' => 5, 'placeholder' => 4190], - $prefix . 'auth' => ['value' => $authType->show([$record['ident_switch.form.sieve.auth'] ?? null])], + $prefix . 'auth' => ['value' => $authType->show($authVal !== null ? [$authVal] : [])], + $prefix . 'username' => ['type' => 'text', 'size' => 64, 'autocomplete' => 'off'], + $prefix . 'password' => ['type' => 'password', 'size' => 64, 'autocomplete' => 'new-password'], ]; } @@ -194,6 +220,75 @@ class IdentSwitchForm . ''; } + /** + * Check if a domain is allowed for ident_switch configuration. + * + * When 'ident_switch.preconfig_only' is enabled, only domains with + * a matching preconfig entry are allowed. + * + * @param string $email Email address to check. + * @return bool True if allowed, false if blocked by preconfig_only. + */ + private function is_domain_allowed(string $email): bool + { + $rc = rcmail::get_instance(); + if (!$rc->config->get('ident_switch.preconfig_only', false)) { + return true; + } + $preconfig = new IdentSwitchPreconfig($this->plugin); + return $preconfig->get($email) !== false; + } + + /** + * Pass preconfig data and settings to JS environment. + * + * Parses each domain's protocol URLs into host/security/port components + * so the client can dynamically populate form fields on email change. + * + * @param rcmail $rc Roundcube instance. + */ + private function pass_preconfig_to_js(rcmail $rc): void + { + $this->plugin->load_config(); + $allPreconfig = $rc->config->get('ident_switch.preconfig', []); + $preconfigOnly = $rc->config->get('ident_switch.preconfig_only', false); + + $jsPreconfig = []; + foreach ($allPreconfig as $domain => $cfg) { + $entry = []; + + $protocols = [ + 'imap' => $cfg['imap_host'] ?? $cfg['host'] ?? '', + 'smtp' => $cfg['smtp_host'] ?? $cfg['host'] ?? '', + 'sieve' => $cfg['sieve_host'] ?? '', + ]; + + foreach ($protocols as $proto => $url) { + if (empty($url)) { + continue; + } + $urlArr = parse_url($url); + if (!is_array($urlArr)) { + continue; + } + $scheme = strtolower($urlArr['scheme'] ?? ''); + $entry[$proto] = [ + 'host' => $urlArr['host'] ?? '', + 'security' => in_array($scheme, ['ssl', 'tls']) ? $scheme : '', + 'port' => !empty($urlArr['port']) ? intval($urlArr['port']) : '', + ]; + } + + $entry['user'] = $cfg['user'] ?? ''; + $entry['readonly'] = !empty($cfg['readonly']); + $jsPreconfig[$domain] = $entry; + } + + $rc->output->set_env('ident_switch_preconfig', $jsPreconfig); + $rc->output->set_env('ident_switch_preconfig_only', $preconfigOnly); + $rc->output->set_env('ident_switch_warning_tpl', $this->plugin->gettext('form.preconfig_only_warning')); + } + /** * Parse scheme prefix from a host string. * @@ -230,6 +325,158 @@ class IdentSwitchForm return $host; } + /** + * Restore plugin form field values from POST data after a save error. + * + * When the identity save is aborted (validation or connection error), + * Roundcube re-renders the form. Core fields are preserved from POST by RC, + * but plugin fields would revert to DB values without this restoration. + * + * @param array &$record Identity record to overlay POST values onto. + */ + private function restore_post_values(array &$record): void + { + // Text and select fields (trimmed) + $fields = [ + ['common', 'label'], + ['imap', 'host'], + ['imap', 'port'], + ['imap', 'username'], + ['imap', 'delimiter'], + ['smtp', 'host'], + ['smtp', 'port'], + ['smtp', 'auth'], + ['smtp', 'username'], + ['sieve', 'host'], + ['sieve', 'port'], + ['sieve', 'auth'], + ['sieve', 'username'], + ['notify', 'basic'], + ['notify', 'sound'], + ['notify', 'desktop'], + ]; + + foreach ($fields as [$section, $field]) { + $rawVal = self::get_field_value($section, $field, false); + if ($rawVal !== null) { + $record["ident_switch.form.{$section}.{$field}"] = self::get_field_value($section, $field); + } + } + + // Security selects: empty string means "None", must not be trimmed to null + foreach (['imap', 'smtp', 'sieve'] as $proto) { + $rawVal = self::get_field_value($proto, 'security', false); + if ($rawVal !== null) { + $record["ident_switch.form.{$proto}.security"] = $rawVal; + } + } + + // Password fields (raw, no trim) + foreach (['imap', 'smtp', 'sieve'] as $proto) { + $rawVal = self::get_field_value($proto, 'password', false, true); + if ($rawVal !== null) { + $record["ident_switch.form.{$proto}.password"] = $rawVal; + } + } + + // Checkboxes: absent from POST means unchecked + $record['ident_switch.form.common.enabled'] = !empty(self::get_field_value('common', 'enabled', false)); + $record['ident_switch.form.notify.check'] = !empty(self::get_field_value('notify', 'check', false)); + } + + /** + * Test IMAP, SMTP, and Sieve connections using validated form data. + * + * @param array $data Validated data from validate(). + * @param string $email Email address (fallback username). + * @param string $imapPass Raw IMAP password (not encrypted). + * @return string|null Error key on failure, null on success. + */ + private function test_connections(array $data, string $email, string $imapPass): ?string + { + $rc = rcmail::get_instance(); + + // --- IMAP test --- + $imapHostFull = $data['imap.host'] ?: 'localhost'; + $parsed = self::parse_host_scheme($imapHostFull); + $imapHost = $parsed['host']; + $imapSsl = $parsed['scheme'] ?: null; + $imapDefPort = ($imapSsl === 'ssl') ? 993 : 143; + $imapPort = $data['imap.port'] ?: $imapDefPort; + $imapUser = $data['imap.user'] ?: $email; + + $imap = new rcube_imap_generic(); + $result = $imap->connect($imapHost, $imapUser, $imapPass, [ + 'port' => (int)$imapPort, + 'ssl_mode' => $imapSsl, + 'timeout' => 10, + ]); + if (!$result) { + ident_switch::write_log("IMAP connection test failed: {$imap->error}"); + return 'imap.connect'; + } + $imap->closeConnection(); + + // --- SMTP test --- + $smtpAuth = (int)($data['smtp.auth'] ?? ident_switch::SMTP_AUTH_IMAP); + if ($smtpAuth !== ident_switch::SMTP_AUTH_NONE) { + $smtpHostFull = $data['smtp.host'] ?: 'localhost'; + $smtpParsed = self::parse_host_scheme($smtpHostFull); + $smtpDefPort = ($smtpParsed['scheme'] === 'ssl') ? 465 : 587; + $smtpPort = $data['smtp.port'] ?: $smtpDefPort; + // Compose host with scheme for rcube_smtp (expects ssl://host:port or tls://host:port) + $smtpConnHost = self::compose_host_scheme($smtpParsed['host'], $smtpParsed['scheme'] ?: null); + $smtpConnHost .= ':' . $smtpPort; + + if ($smtpAuth === ident_switch::SMTP_AUTH_CUSTOM) { + $smtpUser = $data['smtp.user'] ?: ''; + $smtpPass = $data['smtp.pass'] ?: ''; + } else { + $smtpUser = $imapUser; + $smtpPass = $imapPass; + } + + $smtp = new rcube_smtp(); + $result = $smtp->connect($smtpConnHost, null, $smtpUser, $smtpPass); + if (!$result) { + ident_switch::write_log("SMTP connection test failed"); + return 'smtp.connect'; + } + $smtp->disconnect(); + } + + // --- Sieve test (only if host is configured and auth is not None) --- + $sieveAuth = (int)($data['sieve.auth'] ?? ident_switch::SIEVE_AUTH_IMAP); + $sieveHostFull = $data['sieve.host'] ?? ''; + if (!empty($sieveHostFull) && $sieveAuth !== ident_switch::SIEVE_AUTH_NONE) { + $sieveParsed = self::parse_host_scheme($sieveHostFull); + $sievePort = $data['sieve.port'] ?: 4190; + $useTls = ($sieveParsed['scheme'] === 'tls'); + $sieveHost = $sieveParsed['host']; + if ($sieveParsed['scheme'] === 'ssl') { + $sieveHost = 'ssl://' . $sieveHost; + } + + if ($sieveAuth === ident_switch::SIEVE_AUTH_CUSTOM) { + $sieveUser = $data['sieve.user'] ?: ''; + $sievePass = $data['sieve.pass'] ?: ''; + } else { + $sieveUser = $imapUser; + $sievePass = $imapPass; + } + + if (class_exists('rcube_sieve')) { + $sieve = new rcube_sieve($sieveUser, $sievePass, $sieveHost, (int)$sievePort, null, $useTls); + if ($sieve->error()) { + ident_switch::write_log("Sieve connection test failed (error code: {$sieve->error()})"); + return 'sieve.connect'; + } + } + } + + return null; + } + /** * Handle identity_form hook: add plugin-specific fields to the identity editor. * @@ -256,6 +503,42 @@ class IdentSwitchForm $this->plugin->add_texts('localization'); + // Build info section with description and domain warning + $preconfigOnly = $rc->config->get('ident_switch.preconfig_only', false); + $domainAllowed = empty($args['record']['email']) || $this->is_domain_allowed($args['record']['email']); + + $warningVisible = $preconfigOnly && !$domainAllowed; + + // Extract domain from email for warning message + $email = $args['record']['email'] ?? ''; + $domain = ''; + if (!empty($email) && str_contains($email, '@')) { + $domain = substr($email, strpos($email, '@') + 1); + } + + $infoContent = html::div( + ['class' => 'boxinformation', 'id' => 'ident-switch-info'], + rcube::Q($this->plugin->gettext('form.description')) + ); + $warningContent = html::div( + ['class' => 'boxwarning', 'id' => 'ident-switch-domain-warning', + 'style' => $warningVisible ? '' : 'display:none'], + rcube::Q(sprintf($this->plugin->gettext('form.preconfig_only_warning'), $domain)) + ); + + $args['form']['ident_switch'] = [ + 'name' => $this->plugin->gettext('form.caption'), + 'content' => $infoContent . $warningContent, + ]; + + // Pass preconfig data to JS for dynamic form updates + $this->pass_preconfig_to_js($rc); + + // When preconfig_only is enabled, hide field sections for non-preconfigured domains + if (!$domainAllowed) { + return $args; + } + $row = null; if (isset($args['record']['identity_id'])) { $sql = 'SELECT * FROM ' . $rc->db->table_name(ident_switch::TABLE) . ' WHERE iid = ? AND user_id = ?'; @@ -265,6 +548,9 @@ class IdentSwitchForm $record = &$args['record']; + // Tell JS whether this identity has an existing ident_switch record + $rc->output->set_env('ident_switch_has_record', !empty($row)); + // Load data if exists if ($row) { $dbToForm = [ @@ -277,9 +563,13 @@ class IdentSwitchForm 'smtp_host' => 'smtp.host', 'smtp_port' => 'smtp.port', 'smtp_auth' => 'smtp.auth', + 'smtp_username' => 'smtp.username', + 'smtp_password' => 'smtp.password', 'sieve_host' => 'sieve.host', 'sieve_port' => 'sieve.port', 'sieve_auth' => 'sieve.auth', + 'sieve_username' => 'sieve.username', + 'sieve_password' => 'sieve.password', 'notify_check' => 'notify.check', 'notify_basic' => 'notify.basic', 'notify_sound' => 'notify.sound', @@ -324,9 +614,14 @@ class IdentSwitchForm $preconfig->apply($record); } + // Restore POST values when form is re-displayed after a save error + if ($_SERVER['REQUEST_METHOD'] === 'POST') { + $this->restore_post_values($record); + } + $args['form']['ident_switch.common'] = [ - 'name' => $this->plugin->gettext('form.common.caption'), - 'content' => self::get_common_fields($record), + 'name' => $this->plugin->gettext('form.common.general'), + 'content' => $this->get_common_fields($record), ]; $args['form']['ident_switch.imap'] = [ 'name' => $this->plugin->gettext('form.imap.caption'), @@ -375,6 +670,12 @@ class IdentSwitchForm return $args; } + // Block save for non-preconfigured domains when preconfig_only is enabled + if (!$this->is_domain_allowed($args['record']['email'])) { + $this->disable($args['id']); + return $args; + } + if (!self::get_field_value('common', 'enabled', false)) { $this->disable($args['id']); return $args; @@ -389,6 +690,16 @@ class IdentSwitchForm } $this->apply_readonly_preconfig($data, $args['record']['email']); + + // Test connections before saving + $connErr = $this->test_connections($data, $args['record']['email'], $data['imap.pass']); + if ($connErr) { + $this->plugin->add_texts('localization'); + $args['abort'] = true; + $args['message'] = 'ident_switch.err.' . $connErr; + return $args; + } + $data['id'] = $args['id']; $this->save($rc, $data); @@ -412,6 +723,11 @@ class IdentSwitchForm return $args; } + // Block creation for non-preconfigured domains when preconfig_only is enabled + if (!$this->is_domain_allowed($args['record']['email'])) { + return $args; + } + if (!self::get_field_value('common', 'enabled', false)) { return $args; } @@ -421,10 +737,20 @@ class IdentSwitchForm $this->plugin->add_texts('localization'); $args['abort'] = true; $args['message'] = 'ident_switch.err.' . $data['err']; + return $args; } $this->apply_readonly_preconfig($data, $args['record']['email']); + // Test connections before saving + $connErr = $this->test_connections($data, $args['record']['email'], $data['imap.pass']); + if ($connErr) { + $this->plugin->add_texts('localization'); + $args['abort'] = true; + $args['message'] = 'ident_switch.err.' . $connErr; + return $args; + } + // Save data for _after (cannot pass with $args) $_SESSION['createData' . ident_switch::MY_POSTFIX] = $data; @@ -541,7 +867,8 @@ class IdentSwitchForm } // Validate and compose IMAP host with security scheme - $retVal['imap.host'] = self::get_field_value('imap', 'host'); + $imapBareHost = self::get_field_value('imap', 'host'); + $retVal['imap.host'] = $imapBareHost; $imapSecurity = self::get_field_value('imap', 'security') ?? ''; $retVal['imap.host'] = self::compose_host_scheme($retVal['imap.host'], $imapSecurity); if (strlen($retVal['imap.host'] ?? '') > 64) { @@ -576,8 +903,8 @@ class IdentSwitchForm return $retVal; } - // Validate and compose SMTP host with security scheme - $retVal['smtp.host'] = self::get_field_value('smtp', 'host'); + // Validate and compose SMTP host with security scheme (fallback to IMAP host) + $retVal['smtp.host'] = self::get_field_value('smtp', 'host') ?: $imapBareHost; $smtpSecurity = self::get_field_value('smtp', 'security') ?? ''; $retVal['smtp.host'] = self::compose_host_scheme($retVal['smtp.host'], $smtpSecurity); if (strlen($retVal['smtp.host'] ?? '') > 64) { @@ -601,8 +928,21 @@ class IdentSwitchForm return $retVal; } - // Validate and compose Sieve host with security scheme - $retVal['sieve.host'] = self::get_field_value('sieve', 'host'); + // Custom SMTP credentials + if ((int)$retVal['smtp.auth'] === ident_switch::SMTP_AUTH_CUSTOM) { + $retVal['smtp.user'] = self::get_field_value('smtp', 'username'); + if (strlen($retVal['smtp.user'] ?? '') > 64) { + $retVal['err'] = 'user.long'; + return $retVal; + } + $retVal['smtp.pass'] = self::get_field_value('smtp', 'password', false, true); + } else { + $retVal['smtp.user'] = null; + $retVal['smtp.pass'] = null; + } + + // Validate and compose Sieve host with security scheme (fallback to IMAP host) + $retVal['sieve.host'] = self::get_field_value('sieve', 'host') ?: $imapBareHost; $sieveSecurity = self::get_field_value('sieve', 'security') ?? ''; $retVal['sieve.host'] = self::compose_host_scheme($retVal['sieve.host'], $sieveSecurity); if (strlen($retVal['sieve.host'] ?? '') > 64) { @@ -626,6 +966,19 @@ class IdentSwitchForm return $retVal; } + // Custom Sieve credentials + if ((int)$retVal['sieve.auth'] === ident_switch::SIEVE_AUTH_CUSTOM) { + $retVal['sieve.user'] = self::get_field_value('sieve', 'username'); + if (strlen($retVal['sieve.user'] ?? '') > 64) { + $retVal['err'] = 'user.long'; + return $retVal; + } + $retVal['sieve.pass'] = self::get_field_value('sieve', 'password', false, true); + } else { + $retVal['sieve.user'] = null; + $retVal['sieve.pass'] = null; + } + // Notification settings $retVal['notify.check'] = self::get_field_value('notify', 'check', false) ? 1 : 0; @@ -682,7 +1035,7 @@ class IdentSwitchForm */ public function save(rcmail $rc, array $data): bool { - $sql = 'SELECT id, password FROM ' . $rc->db->table_name(ident_switch::TABLE) . ' WHERE iid = ? AND user_id = ?'; + $sql = 'SELECT id, password, smtp_password, sieve_password FROM ' . $rc->db->table_name(ident_switch::TABLE) . ' WHERE iid = ? AND user_id = ?'; $q = $rc->db->query($sql, $data['id'], $rc->user->ID); $r = $rc->db->fetch_assoc($q); if ($r) { @@ -690,7 +1043,8 @@ class IdentSwitchForm $sql = 'UPDATE ' . $rc->db->table_name(ident_switch::TABLE) . ' SET flags = ?, label = ?, imap_host = ?, imap_port = ?, imap_delimiter = ?, username = ?, password = ?,' . - ' smtp_host = ?, smtp_port = ?, smtp_auth = ?, sieve_host = ?, sieve_port = ?, sieve_auth = ?,' . + ' smtp_host = ?, smtp_port = ?, smtp_auth = ?, smtp_username = ?, smtp_password = ?,' . + ' sieve_host = ?, sieve_port = ?, sieve_auth = ?, sieve_username = ?, sieve_password = ?,' . ' notify_check = ?, notify_basic = ?, notify_sound = ?, notify_desktop = ?,' . ' user_id = ?, iid = ?' . ' WHERE id = ?'; @@ -699,18 +1053,38 @@ class IdentSwitchForm $sql = 'INSERT INTO ' . $rc->db->table_name(ident_switch::TABLE) . '(flags, label, imap_host, imap_port, imap_delimiter, username, password,' . - ' smtp_host, smtp_port, smtp_auth, sieve_host, sieve_port, sieve_auth,' . + ' smtp_host, smtp_port, smtp_auth, smtp_username, smtp_password,' . + ' sieve_host, sieve_port, sieve_auth, sieve_username, sieve_password,' . ' notify_check, notify_basic, notify_sound, notify_desktop,' . - ' user_id, iid) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)'; + ' user_id, iid) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)'; } else { return false; } - // Do we need to update pwd? - if ($data['imap.pass'] !== ($r['password'] ?? null)) { + // Encrypt IMAP password (compare raw POST value with decrypted DB value) + $existingImapPass = !empty($r['password']) ? $rc->decrypt($r['password']) : null; + if ($data['imap.pass'] === $existingImapPass && $existingImapPass !== false) { + $data['imap.pass'] = $r['password']; + } else { $data['imap.pass'] = $rc->encrypt($data['imap.pass']); } + // Encrypt SMTP password + $existingSmtpPass = !empty($r['smtp_password']) ? $rc->decrypt($r['smtp_password']) : null; + if ($data['smtp.pass'] === $existingSmtpPass && $existingSmtpPass !== false) { + $data['smtp.pass'] = $r['smtp_password'] ?? null; + } else { + $data['smtp.pass'] = $data['smtp.pass'] ? $rc->encrypt($data['smtp.pass']) : null; + } + + // Encrypt Sieve password + $existingSievePass = !empty($r['sieve_password']) ? $rc->decrypt($r['sieve_password']) : null; + if ($data['sieve.pass'] === $existingSievePass && $existingSievePass !== false) { + $data['sieve.pass'] = $r['sieve_password'] ?? null; + } else { + $data['sieve.pass'] = $data['sieve.pass'] ? $rc->encrypt($data['sieve.pass']) : null; + } + $rc->db->query( $sql, $data['flags'], @@ -723,9 +1097,13 @@ class IdentSwitchForm $data['smtp.host'], $data['smtp.port'], $data['smtp.auth'], + $data['smtp.user'], + $data['smtp.pass'], $data['sieve.host'], $data['sieve.port'], $data['sieve.auth'], + $data['sieve.user'], + $data['sieve.pass'], $data['notify.check'] ?? 1, $data['notify.basic'] ?? null, $data['notify.sound'] ?? null, diff --git a/lib/IdentSwitchSwitcher.php b/lib/IdentSwitchSwitcher.php index 199e485..2b2ab53 100644 --- a/lib/IdentSwitchSwitcher.php +++ b/lib/IdentSwitchSwitcher.php @@ -29,15 +29,15 @@ class IdentSwitchSwitcher $rc = rcmail::get_instance(); $my_postfix_len = strlen(ident_switch::MY_POSTFIX); - $identId = rcube_utils::get_input_value('_ident-id', rcube_utils::INPUT_POST); + $identId = (int)rcube_utils::get_input_value('_ident-id', rcube_utils::INPUT_POST); $rc->session->remove('folders'); $rc->session->remove('unseen_count'); // Reset baseline for the target account so delta goes back to 0 - $this->reset_baseline($identId == -1 ? 0 : null, $rc, $identId); + $this->reset_baseline($identId === -1 ? 0 : null, $rc, $identId); - if ($identId == -1) { + if ($identId === -1) { // Switch to main account ident_switch::write_log('Switching mailbox back to default.'); @@ -153,7 +153,7 @@ class IdentSwitchSwitcher public function configure_smtp(array $args): array { $iid = $_SESSION['iid' . ident_switch::MY_POSTFIX] ?? null; - if (!is_numeric($iid) || $iid == -1) { + if (!is_numeric($iid) || (int)$iid === -1) { ident_switch::write_log('no identity switch is selected... trying to find related smtp server from the from header'); $requestFrom = rcube_utils::get_input_value('_from', rcube_utils::INPUT_POST); if (empty($requestFrom)) { @@ -163,14 +163,14 @@ class IdentSwitchSwitcher $iid = intval($requestFrom); if ($iid === 0) { - ident_switch::write_log('falling back to original default config as _from post field is no integer: ' . $_POST['_from']); + ident_switch::write_log('falling back to original default config as _from post field is not an integer'); return $args; } } $rc = rcmail::get_instance(); - $sql = 'SELECT smtp_host, smtp_port, username, smtp_auth, password FROM ' . $rc->db->table_name(ident_switch::TABLE) . ' WHERE iid = ? AND user_id = ?'; + $sql = 'SELECT smtp_host, smtp_port, username, smtp_auth, smtp_username, smtp_password, password FROM ' . $rc->db->table_name(ident_switch::TABLE) . ' WHERE iid = ? AND user_id = ?'; $q = $rc->db->query($sql, $iid, $rc->user->ID); $r = $rc->db->fetch_assoc($q); if (is_array($r)) { @@ -182,8 +182,16 @@ class IdentSwitchSwitcher $r['username'] = $rIid['email']; } - $args['smtp_user'] = $r['username']; - $args['smtp_pass'] = $r['smtp_auth'] == ident_switch::SMTP_AUTH_IMAP ? $rc->decrypt($r['password']) : ''; + if ((int)$r['smtp_auth'] === ident_switch::SMTP_AUTH_CUSTOM) { + $args['smtp_user'] = $r['smtp_username'] ?: ''; + $args['smtp_pass'] = $r['smtp_password'] ? ($rc->decrypt($r['smtp_password']) ?: '') : ''; + } elseif ((int)$r['smtp_auth'] === ident_switch::SMTP_AUTH_IMAP) { + $args['smtp_user'] = $r['username']; + $args['smtp_pass'] = $rc->decrypt($r['password']) ?: ''; + } else { + $args['smtp_user'] = ''; + $args['smtp_pass'] = ''; + } // Host already contains scheme (ssl:// or tls://) from form $smtpHost = $r['smtp_host'] ?: 'localhost'; @@ -206,13 +214,13 @@ class IdentSwitchSwitcher public function configure_managesieve(array $args): array { $iid = $_SESSION['iid' . ident_switch::MY_POSTFIX] ?? null; - if (!is_numeric($iid) || $iid == -1) { + if (!is_numeric($iid) || (int)$iid === -1) { return $args; } $rc = rcmail::get_instance(); - $sql = 'SELECT sieve_host, sieve_port, sieve_auth, username, password FROM ' . $rc->db->table_name(ident_switch::TABLE) . ' WHERE iid = ? AND user_id = ?'; + $sql = 'SELECT sieve_host, sieve_port, sieve_auth, sieve_username, sieve_password, username, password FROM ' . $rc->db->table_name(ident_switch::TABLE) . ' WHERE iid = ? AND user_id = ?'; $q = $rc->db->query($sql, $iid, $rc->user->ID); $r = $rc->db->fetch_assoc($q); if (is_array($r) && !empty($r['sieve_host'])) { @@ -227,9 +235,12 @@ class IdentSwitchSwitcher $sievePort = $r['sieve_port'] ?: 4190; $args['host'] = $sieveHost . ':' . $sievePort; - if ($r['sieve_auth'] == ident_switch::SIEVE_AUTH_IMAP) { + if ((int)$r['sieve_auth'] === ident_switch::SIEVE_AUTH_CUSTOM) { + $args['user'] = $r['sieve_username'] ?: ''; + $args['password'] = $r['sieve_password'] ? ($rc->decrypt($r['sieve_password']) ?: '') : ''; + } elseif ((int)$r['sieve_auth'] === ident_switch::SIEVE_AUTH_IMAP) { $args['user'] = $r['username']; - $args['password'] = $rc->decrypt($r['password']); + $args['password'] = $rc->decrypt($r['password']) ?: ''; } else { $args['user'] = ''; $args['password'] = ''; @@ -268,7 +279,7 @@ class IdentSwitchSwitcher $sql = 'SELECT label FROM ' . $rc->db->table_name(ident_switch::TABLE) . ' WHERE iid = ? AND user_id = ?'; $q = $rc->db->query($sql, $_SESSION['iid' . ident_switch::MY_POSTFIX], $rc->user->ID); $r = $rc->db->fetch_assoc($q); - $args['blocks']['main']['name'] .= ' (' . ($r['label'] ? rcube::Q($rc->gettext('server')) . ': ' . $r['label'] : 'remote') . ')'; + $args['blocks']['main']['name'] .= ' (' . ($r['label'] ? rcube::Q($rc->gettext('server')) . ': ' . rcube::Q($r['label']) : 'remote') . ')'; foreach (rcube_storage::$folder_types as $type) { if (isset($no_override[$type . '_mbox'])) { diff --git a/localization/de_DE.inc b/localization/de_DE.inc index 8cf2b45..1df321e 100644 --- a/localization/de_DE.inc +++ b/localization/de_DE.inc @@ -4,14 +4,20 @@ */ $labels = array(); -// Plugin ident_switch -$labels['form.common.caption'] = 'Plugin ident_switch'; +// Separate Account +$labels['form.caption'] = 'Separates Konto'; +$labels['form.description'] = 'Konfigurieren Sie diese Identität als separates Konto mit eigenen Mailserver-Verbindungen.'; +$labels['form.preconfig_only_warning'] = 'Die Konfiguration als separates Konto ist für die Domain %s nicht verfügbar.'; + +// General +$labels['form.common.general'] = 'Allgemein'; // Enabled $labels['form.common.enabled'] = 'Aktiviert'; // Label $labels['form.common.label'] = 'Bezeichnung'; +$labels['form.common.label.hint'] = 'Name, der im Kontowechsler angezeigt wird. Wenn leer, wird die E-Mail-Adresse verwendet.'; // Value in \'Label\' field is too long (32 chars max). $labels['err.label.long'] = 'Der Wert in Feld \'Bezeichnung\' ist zu lang (max. 32 Zeichen).'; @@ -20,8 +26,8 @@ $labels['err.label.long'] = 'Der Wert in Feld \'Bezeichnung\' ist zu lang (max. // IMAP $labels['form.imap.caption'] = 'IMAP'; -// Server host name -$labels['form.imap.host'] = 'Servername'; +// Incoming mail server +$labels['form.imap.host'] = 'Posteingangsserver'; // Security $labels['form.imap.security'] = 'Sicherheit'; @@ -47,8 +53,8 @@ $labels['err.user.long'] = 'Der Wert in Feld \'Benutzername\' ist zu lang (max. // SMTP $labels['form.smtp.caption'] = 'SMTP'; -// Server host name -$labels['form.smtp.host'] = 'Servername'; +// Outgoing mail server +$labels['form.smtp.host'] = 'Postausgangsserver'; // Security $labels['form.smtp.security'] = 'Sicherheit'; @@ -65,6 +71,15 @@ $labels['form.smtp.auth.imap'] = 'Wie IMAP'; // None $labels['form.smtp.auth.none'] = 'Keine'; +// Custom +$labels['form.smtp.auth.custom'] = 'Benutzerdefiniert'; + +// Username +$labels['form.smtp.username'] = 'Benutzername'; + +// Password +$labels['form.smtp.password'] = 'Passwort'; + // Sieve $labels['form.sieve.caption'] = 'Sieve'; @@ -87,6 +102,15 @@ $labels['form.sieve.auth.imap'] = 'Wie IMAP'; // None $labels['form.sieve.auth.none'] = 'Keine'; +// Custom +$labels['form.sieve.auth.custom'] = 'Benutzerdefiniert'; + +// Username +$labels['form.sieve.username'] = 'Benutzername'; + +// Password +$labels['form.sieve.password'] = 'Passwort'; + // Notifications $labels['form.notify.caption'] = 'Benachrichtigungen'; @@ -140,3 +164,12 @@ $labels['err.port.num'] = 'Der Wert in Feld \'Port\' muss eine Zahl sein.'; // Value in \'Port\' field must be between 1 and 65535. $labels['err.port.range'] = 'Der Wert in Feld \'Port\' muss zwischen 1 und 65535 liegen.'; + +// IMAP connection test failed. +$labels['err.imap.connect'] = 'IMAP-Verbindung fehlgeschlagen. Bitte überprüfen Sie Serveradresse, Port und Zugangsdaten.'; + +// SMTP connection test failed. +$labels['err.smtp.connect'] = 'SMTP-Verbindung fehlgeschlagen. Bitte überprüfen Sie Serveradresse, Port und Zugangsdaten.'; + +// Sieve connection test failed. +$labels['err.sieve.connect'] = 'Sieve-Verbindung fehlgeschlagen. Bitte überprüfen Sie Serveradresse, Port und Zugangsdaten.'; diff --git a/localization/en_US.inc b/localization/en_US.inc index 4fbd724..045fd1b 100644 --- a/localization/en_US.inc +++ b/localization/en_US.inc @@ -4,14 +4,20 @@ */ $labels = array(); -// Plugin ident_switch -$labels['form.common.caption'] = 'Plugin ident_switch'; +// Separate Account +$labels['form.caption'] = 'Separate Account'; +$labels['form.description'] = 'Configure this identity as a separate account with its own mail server connections.'; +$labels['form.preconfig_only_warning'] = 'Separate account configuration is not available for the domain %s.'; + +// General +$labels['form.common.general'] = 'General'; // Enabled $labels['form.common.enabled'] = 'Enabled'; // Label $labels['form.common.label'] = 'Label'; +$labels['form.common.label.hint'] = 'Name displayed in the account switcher. If empty, the email address is used.'; // Value in \'Label\' field is too long (32 chars max). $labels['err.label.long'] = 'Value in \'Label\' field is too long (32 chars max).'; @@ -20,8 +26,8 @@ $labels['err.label.long'] = 'Value in \'Label\' field is too long (32 chars max) // IMAP $labels['form.imap.caption'] = 'IMAP'; -// Server host name -$labels['form.imap.host'] = 'Server host name'; +// Incoming mail server +$labels['form.imap.host'] = 'Incoming mail server'; // Security $labels['form.imap.security'] = 'Security'; @@ -47,8 +53,8 @@ $labels['err.user.long'] = 'Value in \'Username\' field is too long (64 chars ma // SMTP $labels['form.smtp.caption'] = 'SMTP'; -// Server host name -$labels['form.smtp.host'] = 'Server host name'; +// Outgoing mail server +$labels['form.smtp.host'] = 'Outgoing mail server'; // Security $labels['form.smtp.security'] = 'Security'; @@ -65,6 +71,15 @@ $labels['form.smtp.auth.imap'] = 'As IMAP'; // None $labels['form.smtp.auth.none'] = 'None'; +// Custom +$labels['form.smtp.auth.custom'] = 'Custom'; + +// Username +$labels['form.smtp.username'] = 'Username'; + +// Password +$labels['form.smtp.password'] = 'Password'; + // Sieve $labels['form.sieve.caption'] = 'Sieve'; @@ -87,6 +102,15 @@ $labels['form.sieve.auth.imap'] = 'As IMAP'; // None $labels['form.sieve.auth.none'] = 'None'; +// Custom +$labels['form.sieve.auth.custom'] = 'Custom'; + +// Username +$labels['form.sieve.username'] = 'Username'; + +// Password +$labels['form.sieve.password'] = 'Password'; + // Notifications $labels['form.notify.caption'] = 'Notifications'; @@ -134,3 +158,12 @@ $labels['err.port.num'] = 'Value in \'Port\' field must be a number.'; // Value in \'Port\' field must be between 1 and 65535. $labels['err.port.range'] = 'Value in \'Port\' field must be between 1 and 65535.'; + +// IMAP connection test failed. +$labels['err.imap.connect'] = 'IMAP connection failed. Please check the server address, port, and credentials.'; + +// SMTP connection test failed. +$labels['err.smtp.connect'] = 'SMTP connection failed. Please check the server address, port, and credentials.'; + +// Sieve connection test failed. +$labels['err.sieve.connect'] = 'Sieve connection failed. Please check the server address, port, and credentials.'; diff --git a/localization/fr_FR.inc b/localization/fr_FR.inc index 4c704cc..f073a4f 100644 --- a/localization/fr_FR.inc +++ b/localization/fr_FR.inc @@ -4,14 +4,20 @@ */ $labels = array(); -// Plugin ident_switch -$labels['form.common.caption'] = 'Plugin ident_switch'; +// Separate Account +$labels['form.caption'] = 'Compte séparé'; +$labels['form.description'] = 'Configurez cette identité comme un compte séparé avec ses propres connexions aux serveurs de messagerie.'; +$labels['form.preconfig_only_warning'] = 'La configuration de compte séparé n\'est pas disponible pour le domaine %s.'; + +// General +$labels['form.common.general'] = 'Général'; // Enabled $labels['form.common.enabled'] = 'Activer'; // Label $labels['form.common.label'] = 'Nom à afficher'; +$labels['form.common.label.hint'] = 'Nom affiché dans le sélecteur de compte. Si vide, l\'adresse email est utilisée.'; // Value in \'Label\' field is too long (32 chars max). $labels['err.label.long'] = 'La valeur du champ \'Nom à afficher\' est trop longue (32 caractères max).'; @@ -20,8 +26,8 @@ $labels['err.label.long'] = 'La valeur du champ \'Nom à afficher\' est trop lon // IMAP $labels['form.imap.caption'] = 'IMAP'; -// Server host name -$labels['form.imap.host'] = 'Serveur'; +// Incoming mail server +$labels['form.imap.host'] = 'Serveur de courrier entrant'; // Security $labels['form.imap.security'] = 'Sécurité'; @@ -47,8 +53,8 @@ $labels['err.user.long'] = 'La valeur du champ \'Nom d\'utilisateur\' de la sect // SMTP $labels['form.smtp.caption'] = 'SMTP'; -// Server host name -$labels['form.smtp.host'] = 'Serveur'; +// Outgoing mail server +$labels['form.smtp.host'] = 'Serveur de courrier sortant'; // Security $labels['form.smtp.security'] = 'Sécurité'; @@ -65,6 +71,15 @@ $labels['form.smtp.auth.imap'] = 'Identique à IMAP'; // None $labels['form.smtp.auth.none'] = 'Aucune'; +// Custom +$labels['form.smtp.auth.custom'] = 'Personnalisée'; + +// Username +$labels['form.smtp.username'] = 'Nom d\'utilisateur'; + +// Password +$labels['form.smtp.password'] = 'Mot de passe'; + // Sieve $labels['form.sieve.caption'] = 'Sieve'; @@ -87,6 +102,15 @@ $labels['form.sieve.auth.imap'] = 'Identique à IMAP'; // None $labels['form.sieve.auth.none'] = 'Aucune'; +// Custom +$labels['form.sieve.auth.custom'] = 'Personnalisée'; + +// Username +$labels['form.sieve.username'] = 'Nom d\'utilisateur'; + +// Password +$labels['form.sieve.password'] = 'Mot de passe'; + // Notifications $labels['form.notify.caption'] = 'Notifications'; @@ -133,3 +157,12 @@ $labels['err.port.num'] = 'La valeur du champ \'Port\' doit être un nombre.'; // Value in \'Port\' field must be between 1 and 65535. $labels['err.port.range'] = 'La valeur du champ \'Port\' doit être comprise entre 1 et 65535.'; + +// IMAP connection test failed. +$labels['err.imap.connect'] = 'La connexion IMAP a échoué. Vérifiez l\'adresse du serveur, le port et les identifiants.'; + +// SMTP connection test failed. +$labels['err.smtp.connect'] = 'La connexion SMTP a échoué. Vérifiez l\'adresse du serveur, le port et les identifiants.'; + +// Sieve connection test failed. +$labels['err.sieve.connect'] = 'La connexion Sieve a échoué. Vérifiez l\'adresse du serveur, le port et les identifiants.'; diff --git a/localization/it_IT.inc b/localization/it_IT.inc index 1d374bc..746c7fb 100644 --- a/localization/it_IT.inc +++ b/localization/it_IT.inc @@ -4,14 +4,20 @@ */ $labels = array(); -// Plugin ident_switch -$labels['form.common.caption'] = 'Plugin ident_switch'; +// Separate Account +$labels['form.caption'] = 'Account separato'; +$labels['form.description'] = 'Configura questa identità come un account separato con le proprie connessioni al server di posta.'; +$labels['form.preconfig_only_warning'] = 'La configurazione come account separato non è disponibile per il dominio %s.'; + +// General +$labels['form.common.general'] = 'Generale'; // Enabled $labels['form.common.enabled'] = 'Abilita'; // Label $labels['form.common.label'] = 'Nome visualizzato'; +$labels['form.common.label.hint'] = 'Nome visualizzato nel selettore account. Se vuoto, viene utilizzato l\'indirizzo email.'; // Value in \'Label\' field is too long (32 chars max). $labels['err.label.long'] = 'Il valore del campo \'Nome Visualizzato\' è troppo lungo (massimo 32 caratteri).'; @@ -20,8 +26,8 @@ $labels['err.label.long'] = 'Il valore del campo \'Nome Visualizzato\' è troppo // IMAP $labels['form.imap.caption'] = 'IMAP'; -// Server host name -$labels['form.imap.host'] = 'Nome del server'; +// Incoming mail server +$labels['form.imap.host'] = 'Server posta in arrivo'; // Security $labels['form.imap.security'] = 'Sicurezza'; @@ -47,8 +53,8 @@ $labels['err.user.long'] = 'Il valore del campo \'Username\' è troppo lungo (ma // SMTP $labels['form.smtp.caption'] = 'SMTP'; -// Server host name -$labels['form.smtp.host'] = 'Nome del server'; +// Outgoing mail server +$labels['form.smtp.host'] = 'Server posta in uscita'; // Security $labels['form.smtp.security'] = 'Sicurezza'; @@ -65,6 +71,15 @@ $labels['form.smtp.auth.imap'] = 'Come IMAP'; // None $labels['form.smtp.auth.none'] = 'Nessuna'; +// Custom +$labels['form.smtp.auth.custom'] = 'Personalizzata'; + +// Username +$labels['form.smtp.username'] = 'Username'; + +// Password +$labels['form.smtp.password'] = 'Password'; + // Sieve $labels['form.sieve.caption'] = 'Sieve'; @@ -87,6 +102,15 @@ $labels['form.sieve.auth.imap'] = 'Come IMAP'; // None $labels['form.sieve.auth.none'] = 'Nessuna'; +// Custom +$labels['form.sieve.auth.custom'] = 'Personalizzata'; + +// Username +$labels['form.sieve.username'] = 'Username'; + +// Password +$labels['form.sieve.password'] = 'Password'; + // Notifications $labels['form.notify.caption'] = 'Notifiche'; @@ -140,3 +164,12 @@ $labels['err.port.num'] = 'Il valore del campo \'Porta\' deve essere un numero.' // Value in \'Port\' field must be between 1 and 65535. $labels['err.port.range'] = 'Il valore del campo \'Porta\' deve essere compreso tra 1 e 65535.'; + +// IMAP connection test failed. +$labels['err.imap.connect'] = 'Connessione IMAP fallita. Verificare l\'indirizzo del server, la porta e le credenziali.'; + +// SMTP connection test failed. +$labels['err.smtp.connect'] = 'Connessione SMTP fallita. Verificare l\'indirizzo del server, la porta e le credenziali.'; + +// Sieve connection test failed. +$labels['err.sieve.connect'] = 'Connessione Sieve fallita. Verificare l\'indirizzo del server, la porta e le credenziali.'; diff --git a/localization/nl_NL.inc b/localization/nl_NL.inc index 953cb0b..d4732ba 100644 --- a/localization/nl_NL.inc +++ b/localization/nl_NL.inc @@ -4,14 +4,20 @@ */ $labels = array(); -// Plugin ident_switch -$labels['form.common.caption'] = 'Plugin ident_switch'; +// Separate Account +$labels['form.caption'] = 'Afzonderlijk account'; +$labels['form.description'] = 'Configureer deze identiteit als een afzonderlijk account met eigen mailserververbindingen.'; +$labels['form.preconfig_only_warning'] = 'Configuratie als afzonderlijk account is niet beschikbaar voor het domein %s.'; + +// General +$labels['form.common.general'] = 'Algemeen'; // Enabled $labels['form.common.enabled'] = 'Inschakelen'; // Label $labels['form.common.label'] = 'Label'; +$labels['form.common.label.hint'] = 'Naam weergegeven in de accountkiezer. Indien leeg, wordt het e-mailadres gebruikt.'; // Value in \'Label\' field is too long (32 chars max). $labels['err.label.long'] = 'De waarde in het veld \'Label\' is te lang (maximaal 32 karakter is toegestaan).'; @@ -20,8 +26,8 @@ $labels['err.label.long'] = 'De waarde in het veld \'Label\' is te lang (maximaa // IMAP $labels['form.imap.caption'] = 'IMAP'; -// Server host name -$labels['form.imap.host'] = 'Servernaam'; +// Incoming mail server +$labels['form.imap.host'] = 'Inkomende mailserver'; // Security $labels['form.imap.security'] = 'Beveiliging'; @@ -47,8 +53,8 @@ $labels['err.user.long'] = 'De waarde in het veld \'Gebruikersnaam\' is te lang // SMTP $labels['form.smtp.caption'] = 'SMTP'; -// Server host name -$labels['form.smtp.host'] = 'Servernaam'; +// Outgoing mail server +$labels['form.smtp.host'] = 'Uitgaande mailserver'; // Security $labels['form.smtp.security'] = 'Beveiliging'; @@ -65,6 +71,15 @@ $labels['form.smtp.auth.imap'] = 'Zoals IMAP'; // None $labels['form.smtp.auth.none'] = 'Geen'; +// Custom +$labels['form.smtp.auth.custom'] = 'Aangepast'; + +// Username +$labels['form.smtp.username'] = 'Gebruikersnaam'; + +// Password +$labels['form.smtp.password'] = 'Wachtwoord'; + // Sieve $labels['form.sieve.caption'] = 'Sieve'; @@ -87,6 +102,15 @@ $labels['form.sieve.auth.imap'] = 'Zoals IMAP'; // None $labels['form.sieve.auth.none'] = 'Geen'; +// Custom +$labels['form.sieve.auth.custom'] = 'Aangepast'; + +// Username +$labels['form.sieve.username'] = 'Gebruikersnaam'; + +// Password +$labels['form.sieve.password'] = 'Wachtwoord'; + // Notifications $labels['form.notify.caption'] = 'Meldingen'; @@ -140,3 +164,12 @@ $labels['err.port.num'] = 'De waarde in het veld \'Poortnummer\' moet een nummer // Value in \'Port\' field must be between 1 and 65535. $labels['err.port.range'] = 'De waarde in het veld \'Poortnummer\' moet tussen de 1 en 65535 zijn.'; + +// IMAP connection test failed. +$labels['err.imap.connect'] = 'IMAP-verbinding mislukt. Controleer het serveradres, de poort en de inloggegevens.'; + +// SMTP connection test failed. +$labels['err.smtp.connect'] = 'SMTP-verbinding mislukt. Controleer het serveradres, de poort en de inloggegevens.'; + +// Sieve connection test failed. +$labels['err.sieve.connect'] = 'Sieve-verbinding mislukt. Controleer het serveradres, de poort en de inloggegevens.'; diff --git a/localization/ru_RU.inc b/localization/ru_RU.inc index 92c374f..c778140 100644 --- a/localization/ru_RU.inc +++ b/localization/ru_RU.inc @@ -4,14 +4,20 @@ */ $labels = array(); -// Plugin ident_switch -$labels['form.common.caption'] = 'Плагин ident_switch'; +// Separate Account +$labels['form.caption'] = 'Отдельный аккаунт'; +$labels['form.description'] = 'Настройте эту учётную запись как отдельный аккаунт с собственными подключениями к почтовому серверу.'; +$labels['form.preconfig_only_warning'] = 'Настройка отдельного аккаунта недоступна для домена %s.'; + +// General +$labels['form.common.general'] = 'Общие'; // Enabled $labels['form.common.enabled'] = 'Включено'; // Label $labels['form.common.label'] = 'Название'; +$labels['form.common.label.hint'] = 'Имя, отображаемое в переключателе аккаунтов. Если пусто, используется адрес электронной почты.'; // Value in \'Label\' field is too long (32 chars max). $labels['err.label.long'] = '\'Название\' должно быть не длинее 32 символов.'; @@ -20,8 +26,8 @@ $labels['err.label.long'] = '\'Название\' должно быть не д // IMAP $labels['form.imap.caption'] = 'IMAP'; -// Server host name -$labels['form.imap.host'] = 'Адрес сервера'; +// Incoming mail server +$labels['form.imap.host'] = 'Сервер входящей почты'; // Security $labels['form.imap.security'] = 'Безопасность'; @@ -47,8 +53,8 @@ $labels['err.user.long'] = '\'Имя пользователя\' должно б // SMTP $labels['form.smtp.caption'] = 'SMTP'; -// Server host name -$labels['form.smtp.host'] = 'Адрес сервера'; +// Outgoing mail server +$labels['form.smtp.host'] = 'Сервер исходящей почты'; // Security $labels['form.smtp.security'] = 'Безопасность'; @@ -65,6 +71,15 @@ $labels['form.smtp.auth.imap'] = 'Как IMAP'; // None $labels['form.smtp.auth.none'] = 'Нет'; +// Custom +$labels['form.smtp.auth.custom'] = 'Другие'; + +// Username +$labels['form.smtp.username'] = 'Имя пользователя'; + +// Password +$labels['form.smtp.password'] = 'Пароль'; + // Sieve $labels['form.sieve.caption'] = 'Sieve'; @@ -87,6 +102,15 @@ $labels['form.sieve.auth.imap'] = 'Как IMAP'; // None $labels['form.sieve.auth.none'] = 'Нет'; +// Custom +$labels['form.sieve.auth.custom'] = 'Другие'; + +// Username +$labels['form.sieve.username'] = 'Имя пользователя'; + +// Password +$labels['form.sieve.password'] = 'Пароль'; + // Notifications $labels['form.notify.caption'] = 'Уведомления'; @@ -139,4 +163,13 @@ $labels['err.host.long'] = '\'Адрес сервера\' должен быть $labels['err.port.num'] = '\'Порт\' должен быть числом.'; // Value in \'Port\' field must be between 1 and 65535. -$labels['err.port.range'] = '\'Порт\' должен быть в диапазоне от 1 до 65535.'; \ No newline at end of file +$labels['err.port.range'] = '\'Порт\' должен быть в диапазоне от 1 до 65535.'; + +// IMAP connection test failed. +$labels['err.imap.connect'] = 'Ошибка подключения к IMAP. Проверьте адрес сервера, порт и учётные данные.'; + +// SMTP connection test failed. +$labels['err.smtp.connect'] = 'Ошибка подключения к SMTP. Проверьте адрес сервера, порт и учётные данные.'; + +// Sieve connection test failed. +$labels['err.sieve.connect'] = 'Ошибка подключения к Sieve. Проверьте адрес сервера, порт и учётные данные.'; \ No newline at end of file diff --git a/localization/sl_SI.inc b/localization/sl_SI.inc index 9ef9800..b3f7de2 100644 --- a/localization/sl_SI.inc +++ b/localization/sl_SI.inc @@ -4,14 +4,20 @@ */ $labels = array(); -// Plugin ident_switch -$labels['form.common.caption'] = 'Razširitev ident_switch'; +// Separate Account +$labels['form.caption'] = 'Ločen račun'; +$labels['form.description'] = 'Konfigurirajte to identiteto kot ločen račun z lastnimi povezavami do poštnega strežnika.'; +$labels['form.preconfig_only_warning'] = 'Konfiguracija ločenega računa za domeno %s ni na voljo.'; + +// General +$labels['form.common.general'] = 'Splošno'; // Enabled $labels['form.common.enabled'] = 'Omogočeno'; // Label $labels['form.common.label'] = 'Oznaka'; +$labels['form.common.label.hint'] = 'Ime, prikazano v izbirniku računov. Če je prazno, se uporabi e-poštni naslov.'; // Value in \'Label\' field is too long (32 chars max). $labels['err.label.long'] = 'Število znakov v polju \'Oznaka\' presega maksimalno število znakov (max 32 znakov).'; @@ -20,8 +26,8 @@ $labels['err.label.long'] = 'Število znakov v polju \'Oznaka\' presega maksimal // IMAP $labels['form.imap.caption'] = 'IMAP'; -// Server host name -$labels['form.imap.host'] = 'Ime oz. naslov IMAP strežnika'; +// Incoming mail server +$labels['form.imap.host'] = 'Strežnik dohodne pošte'; // Security $labels['form.imap.security'] = 'Varnost'; @@ -47,8 +53,8 @@ $labels['err.user.long'] = 'Število znakov v polju \'Uporabniško ime\' presega // SMTP $labels['form.smtp.caption'] = 'SMTP'; -// Server host name -$labels['form.smtp.host'] = 'Ime oz. naslov SMTP strežnika'; +// Outgoing mail server +$labels['form.smtp.host'] = 'Strežnik odhodne pošte'; // Security $labels['form.smtp.security'] = 'Varnost'; @@ -65,6 +71,15 @@ $labels['form.smtp.auth.imap'] = 'Tako kot IMAP'; // None $labels['form.smtp.auth.none'] = 'Brez'; +// Custom +$labels['form.smtp.auth.custom'] = 'Po meri'; + +// Username +$labels['form.smtp.username'] = 'Uporabniško ime'; + +// Password +$labels['form.smtp.password'] = 'Geslo'; + // Sieve $labels['form.sieve.caption'] = 'Sieve'; @@ -87,6 +102,15 @@ $labels['form.sieve.auth.imap'] = 'Tako kot IMAP'; // None $labels['form.sieve.auth.none'] = 'Brez'; +// Custom +$labels['form.sieve.auth.custom'] = 'Po meri'; + +// Username +$labels['form.sieve.username'] = 'Uporabniško ime'; + +// Password +$labels['form.sieve.password'] = 'Geslo'; + // Notifications $labels['form.notify.caption'] = 'Obvestila'; @@ -140,3 +164,12 @@ $labels['err.port.num'] = 'Vrednost \'Vrata\' mora biti številka.'; // Value in \'Port\' field must be between 1 and 65535. $labels['err.port.range'] = 'Vrednost v polju \'Vrata\' mora biti med 1 in 65535.'; + +// IMAP connection test failed. +$labels['err.imap.connect'] = 'Povezava IMAP ni uspela. Preverite naslov strežnika, vrata in poverilnice.'; + +// SMTP connection test failed. +$labels['err.smtp.connect'] = 'Povezava SMTP ni uspela. Preverite naslov strežnika, vrata in poverilnice.'; + +// Sieve connection test failed. +$labels['err.sieve.connect'] = 'Povezava Sieve ni uspela. Preverite naslov strežnika, vrata in poverilnice.';