diff --git a/plugins/managesieve/Changelog b/plugins/managesieve/Changelog
index bca12302b..22e9c7839 100644
--- a/plugins/managesieve/Changelog
+++ b/plugins/managesieve/Changelog
@@ -1,4 +1,5 @@
- Added option managesieve_default_headers
+- Added option managesieve_forward to enable settings dialog for simple forwarding (#6021)
* version 9.0 [2017-10-02]
-----------------------------------------------------------
diff --git a/plugins/managesieve/config.inc.php.dist b/plugins/managesieve/config.inc.php.dist
index 7375f15c6..9efb82713 100644
--- a/plugins/managesieve/config.inc.php.dist
+++ b/plugins/managesieve/config.inc.php.dist
@@ -90,6 +90,12 @@ $config['managesieve_default_headers'] = array('Subject', 'From', 'To');
// 2 - add Vacation section, but hide Filters section
$config['managesieve_vacation'] = 0;
+// Enables separate management interface for setting forwards (redirect to and copy to)
+// 0 - no separate section (default),
+// 1 - add Forward section,
+// 2 - add Forward section, but hide Filters section
+$config['managesieve_forward'] = 0;
+
// Default vacation interval (in days).
// Note: If server supports vacation-seconds extension it is possible
// to define interval in seconds here (as a string), e.g. "3600s".
diff --git a/plugins/managesieve/lib/Roundcube/rcube_sieve_engine.php b/plugins/managesieve/lib/Roundcube/rcube_sieve_engine.php
index f4fa55686..9f1ebb0fb 100644
--- a/plugins/managesieve/lib/Roundcube/rcube_sieve_engine.php
+++ b/plugins/managesieve/lib/Roundcube/rcube_sieve_engine.php
@@ -105,7 +105,7 @@ class rcube_sieve_engine
$this->rc->session->remove('managesieve_current');
}
- if ($mode != 'vacation') {
+ if ($mode != 'vacation' && $mode != 'forward') {
if (!empty($_GET['_set']) || !empty($_POST['_set'])) {
$script_name = rcube_utils::get_input_value('_set', rcube_utils::INPUT_GPC, true);
}
diff --git a/plugins/managesieve/lib/Roundcube/rcube_sieve_forward.php b/plugins/managesieve/lib/Roundcube/rcube_sieve_forward.php
new file mode 100644
index 000000000..f8811e7b7
--- /dev/null
+++ b/plugins/managesieve/lib/Roundcube/rcube_sieve_forward.php
@@ -0,0 +1,517 @@
+start('forward');
+
+ // find current forward rule
+ if (!$error) {
+ $this->forward_rule();
+ $this->forward_post();
+ }
+
+ $this->plugin->add_label('forward.saving');
+ $this->rc->output->add_handlers(array(
+ 'forwardform' => array($this, 'forward_form'),
+ ));
+
+ $this->rc->output->set_pagetitle($this->plugin->gettext('forward'));
+ $this->rc->output->send('managesieve.forward');
+ }
+
+ /**
+ * Find and load sieve script with/for forward rule
+ *
+ * @param string $script_name Optional script name
+ *
+ * @return int Connection status: 0 on success, >0 on failure
+ */
+ protected function load_script($script_name = null)
+ {
+ if ($this->script_name !== null) {
+ return 0;
+ }
+
+ $list = $this->list_scripts();
+ $master = $this->rc->config->get('managesieve_kolab_master');
+ $included = array();
+
+ $this->script_name = false;
+
+ // first try the active script(s)...
+ if (!empty($this->active)) {
+ // Note: there can be more than one active script on KEP:14-enabled server
+ foreach ($this->active as $script) {
+ if ($this->sieve->load($script)) {
+ foreach ($this->sieve->script->as_array() as $rule) {
+ if (!empty($rule['actions'])) {
+ if ($rule['actions'][0]['type'] == 'forward') {
+ $this->script_name = $script;
+ return 0;
+ }
+ else if (empty($master) && $rule['actions'][0]['type'] == 'include') {
+ $included[] = $rule['actions'][0]['target'];
+ }
+ }
+ }
+ }
+ }
+
+ // ...else try scripts included in active script (not for KEP:14)
+ foreach ($included as $script) {
+ if ($this->sieve->load($script)) {
+ foreach ($this->sieve->script->as_array() as $rule) {
+ if (!empty($rule['actions']) && $rule['actions'][0]['type'] == 'forward') {
+ $this->script_name = $script;
+ return 0;
+ }
+ }
+ }
+ }
+ }
+
+ // try all other scripts
+ if (!empty($list)) {
+ // else try included scripts
+ foreach (array_diff($list, $included, $this->active) as $script) {
+ if ($this->sieve->load($script)) {
+ foreach ($this->sieve->script->as_array() as $rule) {
+ if (!empty($rule['actions']) && $rule['actions'][0]['type'] == 'forward') {
+ $this->script_name = $script;
+ return 0;
+ }
+ }
+ }
+ }
+
+ // none of the scripts contains existing forward rule
+ // use any (first) active or just existing script (in that order)
+ if (!empty($this->active)) {
+ $this->sieve->load($this->script_name = $this->active[0]);
+ }
+ else {
+ $this->sieve->load($this->script_name = $list[0]);
+ }
+ }
+
+ return $this->sieve->error();
+ }
+
+ private function forward_rule()
+ {
+ if ($this->script_name === false || $this->script_name === null || !$this->sieve->load($this->script_name)) {
+ return;
+ }
+
+ $list = array();
+ $active = in_array($this->script_name, $this->active);
+
+ // find (first) simple forward rule that can be expressed with the minimal settings
+ foreach ($this->script as $idx => $rule) {
+rcube::write_log('errors', print_r($rule,true));
+ if (empty($this->forward) && !empty($rule['actions']) && $rule['actions'][0]['type'] == 'redirect') {
+ $ignore_rule = false;
+ $target = null;
+ $stop_found = false;
+ foreach ($rule['actions'] as $act) {
+ if ($stop_found) {
+ // we might loose information if there rules after the stop
+ $ignore_rule = true;
+ }
+ if ($act['type'] == 'keep') {
+ $action = 'copy';
+ }
+ else if ($act['type'] == 'stop') {
+ // we might loose information if there rules after the stop
+ $stop_found = true;
+ }
+ else if ($act['type'] == 'discard') {
+ $action = 'redirect';
+ }
+ else if ($act['type'] == 'redirect') {
+ if (!empty($target)) {
+ // we cannot use this rule, because there are multiple targets
+ $ignore_rule = true;
+ }
+ else {
+ $action = $act['copy'] ? 'copy' : 'redirect';
+ $target = $act['target'];
+ }
+ }
+ else {
+ // we cannot use this rule, because there are unknown commands, and we don't want to overwrite them.
+ $ignore_rule = true;
+ }
+ }
+
+ if (count($rule['tests']) != 1 || $rule['tests'][0]['test'] != "true" || $rule['tests'][0]['not'] != null) {
+ // ignore rules that have special conditions
+ $ignore_rule = true;
+ }
+
+ if (!$ignore_rule) {
+ $this->forward = array_merge($rule['actions'][0], array(
+ 'idx' => $idx,
+ 'disabled' => $rule['disabled'] || !$active,
+ 'name' => $rule['name'],
+ 'tests' => $rule['tests'],
+ 'action' => $action ?: 'keep',
+ 'target' => $target,
+ ));
+ }
+ }
+ else if ($active) {
+ $list[$idx] = $rule['name'];
+ }
+ }
+
+ $this->forward['list'] = $list;
+ }
+
+ private function forward_post()
+ {
+ if (empty($_POST)) {
+ return;
+ }
+
+ $date_extension = in_array('date', $this->exts);
+ $regex_extension = in_array('regex', $this->exts);
+
+ $status = rcube_utils::get_input_value('forward_status', rcube_utils::INPUT_POST);
+ $action = rcube_utils::get_input_value('forward_action', rcube_utils::INPUT_POST);
+ $target = rcube_utils::get_input_value('action_target', rcube_utils::INPUT_POST, true);
+
+ $forward_action['type'] = 'forward';
+ $forward_action['reason'] = $this->strip_value(str_replace("\r\n", "\n", $reason));
+ $forward_action['subject'] = trim($subject);
+ $forward_action['from'] = trim($from);
+ $forward_tests = (array) $this->forward['tests'];
+
+ if ($action == 'redirect' || $action == 'copy') {
+ if (empty($target) || !rcube_utils::check_email($target)) {
+ $error = 'noemailwarning';
+ }
+ }
+
+ if (empty($forward_tests)) {
+ $forward_tests = $this->rc->config->get('managesieve_forward_test', array(array('test' => 'true')));
+ }
+
+ if (!$error) {
+ $rule = $this->forward;
+ $rule['type'] = 'if';
+ $rule['name'] = $rule['name'] ?: $this->plugin->gettext('forward');
+ $rule['disabled'] = $status == 'off';
+ $rule['tests'] = $forward_tests;
+ $rule['join'] = $date_extension ? count($forward_tests) > 1 : false;
+ $rule['actions'] = array($forward_action);
+ $rule['after'] = $after;
+
+ if ($action && $action != 'keep') {
+ $rule['actions'][] = array(
+ 'type' => $action == 'discard' ? 'discard' : 'redirect',
+ 'copy' => $action == 'copy',
+ 'target' => $action != 'discard' ? $target : '',
+ );
+ }
+
+ if ($this->save_forward_script($rule)) {
+ $this->rc->output->show_message('managesieve.forwardsaved', 'confirmation');
+ $this->rc->output->send();
+ }
+ }
+
+ $this->rc->output->show_message($error ?: 'managesieve.saveerror', 'error');
+ $this->rc->output->send();
+ }
+
+ /**
+ * Independent forward form
+ */
+ public function forward_form($attrib)
+ {
+ // build FORM tag
+ $form_id = $attrib['id'] ?: 'form';
+ $out = $this->rc->output->request_form(array(
+ 'id' => $form_id,
+ 'name' => $form_id,
+ 'method' => 'post',
+ 'task' => 'settings',
+ 'action' => 'plugin.managesieve-forward',
+ 'noclose' => true
+ ) + $attrib);
+
+
+ // form elements
+ $status = new html_select(array('name' => 'forward_status', 'id' => 'forward_status'));
+ $action = new html_select(array('name' => 'forward_action', 'id' => 'forward_action'));
+
+ $status->add($this->plugin->gettext('forward.on'), 'on');
+ $status->add($this->plugin->gettext('forward.off'), 'off');
+
+ if (in_array('copy', $this->exts)) {
+ $action->add($this->plugin->gettext('forward.copy'), 'copy');
+ }
+ $action->add($this->plugin->gettext('forward.redirect'), 'redirect');
+
+ // force domain selection in redirect email input
+ $domains = (array) $this->rc->config->get('managesieve_domains');
+ $redirect = $this->forward['action'] == 'redirect' || $this->forward['action'] == 'copy';
+
+ if (!empty($domains)) {
+ sort($domains);
+
+ $domain_select = new html_select(array('name' => 'action_domain', 'id' => 'action_domain'));
+ $domain_select->add(array_combine($domains, $domains));
+
+ if ($redirect && $this->forward['target']) {
+ $parts = explode('@', $this->forward['target']);
+ if (!empty($parts)) {
+ $this->forward['domain'] = array_pop($parts);
+ $this->forward['target'] = implode('@', $parts);
+ }
+ }
+ }
+
+ // redirect target
+ $action_target = ' '
+ . ''
+ . (!empty($domains) ? ' @ ' . $domain_select->show($this->forward['domain']) : '')
+ . '';
+
+ // Message tab
+ $table = new html_table(array('cols' => 2));
+
+ $table->add('title', html::label('forward_action', $this->plugin->gettext('forward.action')));
+ $table->add('forward', $action->show($this->forward['action']) . $action_target);
+
+ $table->add('title', html::label('forward_status', $this->plugin->gettext('forward.status')));
+ $table->add(null, $status->show(!isset($this->forward['disabled']) || $this->forward['disabled'] ? 'off' : 'on'));
+
+ $out .= $table->show($attrib);
+
+ $out .= '';
+
+ $this->rc->output->add_gui_object('sieveform', $form_id);
+
+ return $out;
+ }
+
+ /**
+ * Saves forward script (adding some variables)
+ */
+ protected function save_forward_script($rule)
+ {
+ // if script does not exist create a new one
+ if ($this->script_name === null || $this->script_name === false) {
+ $this->script_name = $this->rc->config->get('managesieve_script_name');
+ if (empty($this->script_name)) {
+ $this->script_name = 'roundcube';
+ }
+
+ // use default script contents
+ if (!$this->rc->config->get('managesieve_kolab_master')) {
+ $script_file = $this->rc->config->get('managesieve_default');
+ if ($script_file && is_readable($script_file)) {
+ $content = file_get_contents($script_file);
+ }
+ }
+
+ // create and load script
+ if ($this->sieve->save_script($this->script_name, $content)) {
+ $this->sieve->load($this->script_name);
+ }
+ }
+
+ $script_active = in_array($this->script_name, $this->active);
+
+ // re-order rules if needed
+ if (isset($rule['after']) && $rule['after'] !== '') {
+ // reset original forward rule
+ if (isset($this->forward['idx'])) {
+ $this->script[$this->forward['idx']] = null;
+ }
+
+ // add at target position
+ if ($rule['after'] >= count($this->script) - 1) {
+ $this->script[] = $rule;
+ }
+ else {
+ $script = array();
+
+ foreach ($this->script as $idx => $r) {
+ if ($r) {
+ $script[] = $r;
+ }
+
+ if ($idx == $rule['after']) {
+ $script[] = $rule;
+ }
+ }
+
+ $this->script = $script;
+ }
+
+ $this->script = array_values(array_filter($this->script));
+ }
+ // update original forward rule if it exists
+ else if (isset($this->forward['idx'])) {
+ $this->script[$this->forward['idx']] = $rule;
+ }
+ // otherwise put forward rule on top
+ else {
+ array_unshift($this->script, $rule);
+ }
+
+ // if the script was not active, we need to de-activate
+ // all rules except the forward rule, but only if it is not disabled
+ if (!$script_active && !$rule['disabled']) {
+ foreach ($this->script as $idx => $r) {
+ if (empty($r['actions']) || $r['actions'][0]['type'] != 'forward') {
+ $this->script[$idx]['disabled'] = true;
+ }
+ }
+ }
+
+ if (!$this->sieve->script) {
+ return false;
+ }
+
+ $this->sieve->script->content = $this->script;
+
+ // save the script
+ $saved = $this->save_script($this->script_name);
+
+ // activate the script
+ if ($saved && !$script_active && !$rule['disabled']) {
+ $this->activate_script($this->script_name);
+ }
+
+ return $saved;
+ }
+
+ /**
+ * API: get forward rule
+ *
+ * @return array forward rule information
+ */
+ public function get_forward()
+ {
+ $this->exts = $this->sieve->get_extensions();
+ $this->init_script();
+ $this->forward_rule();
+
+ $forward = array(
+ 'supported' => $this->exts,
+ 'enabled' => empty($this->forward['disabled']),
+ 'action' => $this->forward['action'],
+ 'target' => $this->forward['target'],
+ );
+
+ return $forward;
+ }
+
+ /**
+ * API: set forward rule
+ *
+ * @param array $forward forward rule information (see self::get_forward())
+ *
+ * @return bool True on success, False on failure
+ */
+ public function set_forward($data)
+ {
+ $this->exts = $this->sieve->get_extensions();
+ $this->error = false;
+
+ $this->init_script();
+ $this->forward_rule();
+
+ $forward['type'] = 'forward';
+
+ if ($data['action'] == 'redirect' || $data['action'] == 'copy') {
+ if (empty($data['target']) || !rcube_utils::check_email($data['target'])) {
+ $this->error = "Invalid address in action taget: " . $data['target'];
+ return false;
+ }
+ }
+ else if ($data['action']) {
+ $this->error = "Unsupported forward action: " . $data['action'];
+ return false;
+ }
+
+ if (empty($forward_tests)) {
+ $forward_tests = $this->rc->config->get('managesieve_forward_test', array(array('test' => 'true')));
+ }
+
+ $rule = $this->forward;
+ $rule['type'] = 'if';
+ $rule['name'] = $rule['name'] ?: 'Out-of-Office';
+ $rule['disabled'] = isset($data['enabled']) && !$data['enabled'];
+ $rule['tests'] = $forward_tests;
+ $rule['join'] = $date_extension ? count($forward_tests) > 1 : false;
+ $rule['actions'] = array($forward);
+
+ if ($data['action'] && $data['action'] != 'keep') {
+ $rule['actions'][] = array(
+ 'type' => $data['action'] == 'discard' ? 'discard' : 'redirect',
+ 'copy' => $data['action'] == 'copy',
+ 'target' => $data['action'] != 'discard' ? $data['target'] : '',
+ );
+ }
+
+ return $this->save_forward_script($rule);
+ }
+
+ /**
+ * API: connect to managesieve server
+ */
+ public function connect($username, $password)
+ {
+ $error = parent::connect($username, $password);
+
+ if ($error) {
+ return $error;
+ }
+
+ return $this->load_script();
+ }
+
+ /**
+ * API: Returns last error
+ *
+ * @return string Error message
+ */
+ public function get_error()
+ {
+ return $this->error;
+ }
+}
diff --git a/plugins/managesieve/localization/de_DE.inc b/plugins/managesieve/localization/de_DE.inc
index 99954d66d..c34c5842d 100644
--- a/plugins/managesieve/localization/de_DE.inc
+++ b/plugins/managesieve/localization/de_DE.inc
@@ -183,6 +183,13 @@ $labels['vacation.keep'] = 'Behalten';
$labels['vacation.discard'] = 'Verwerfen';
$labels['vacation.redirect'] = 'Weiterleiten an';
$labels['vacation.copy'] = 'Kopie senden an';
+$labels['forward'] = 'Weiterleitung';
+$labels['forward.redirect'] = 'Umleiten an';
+$labels['forward.copy'] = 'Kopie senden an';
+$labels['forward.on'] = 'An';
+$labels['forward.off'] = 'Aus';
+$labels['forward.status'] = 'Status:';
+$labels['forward.action'] = 'Für eingehende E-Mails führe folgende Aktion aus:';
$labels['filladdresses'] = 'Mit allen meinen Adressen ausfüllen';
$labels['arialabelfiltersetactions'] = 'Aktionen für Filtersätze';
$labels['arialabelfilteractions'] = 'Aktionen für Filter';
@@ -235,6 +242,7 @@ $messages['nodata'] = 'Mindestens eine Position muss ausgewählt werden!';
$messages['invaliddateformat'] = 'Ungültiges Datum oder Teil-Format';
$messages['saveerror'] = 'Ein Serverfehler ist aufgetreten, Speicherung war nicht möglich.';
$messages['vacationsaved'] = 'Urlaubsdaten erfolgreich gespeichert.';
+$messages['forwardsaved'] = 'Weiterleitung erfolgreich gespeichert.';
$messages['emptyvacationbody'] = 'Inhalt der Abwesenheitsbenachrichtigung wird benötigt!';
$messages['duplicate.conflict.err'] = 'Beides, Kopfzeilen und eindeutige Kennzeichner, sind nicht erlaubt.';
$messages['disabledaction'] = 'Aktion nicht erlaubt.';
diff --git a/plugins/managesieve/localization/en_US.inc b/plugins/managesieve/localization/en_US.inc
index 978a0c024..1e090d9e2 100644
--- a/plugins/managesieve/localization/en_US.inc
+++ b/plugins/managesieve/localization/en_US.inc
@@ -185,6 +185,13 @@ $labels['vacation.keep'] = 'Keep';
$labels['vacation.discard'] = 'Discard';
$labels['vacation.redirect'] = 'Redirect to';
$labels['vacation.copy'] = 'Send copy to';
+$labels['forward'] = 'Forwarding';
+$labels['forward.redirect'] = 'Redirect to';
+$labels['forward.copy'] = 'Send copy to';
+$labels['forward.on'] = 'On';
+$labels['forward.off'] = 'Off';
+$labels['forward.status'] = 'Status:';
+$labels['forward.action'] = 'For incoming mails execute the following action:';
$labels['filladdresses'] = 'Fill with all my addresses';
$labels['arialabelfiltersetactions'] = 'Filter set actions';
$labels['arialabelfilteractions'] = 'Filter actions';
@@ -239,6 +246,7 @@ $messages['nodata'] = 'At least one position must be selected!';
$messages['invaliddateformat'] = 'Invalid date or date part format';
$messages['saveerror'] = 'Unable to save data. Server error occurred.';
$messages['vacationsaved'] = 'Vacation data saved successfully.';
+$messages['forwardsaved'] = 'Forward data saved successfully.';
$messages['emptyvacationbody'] = 'Body of vacation message is required!';
$messages['duplicate.conflict.err'] = 'Both header and unique identifier are not allowed.';
$messages['disabledaction'] = 'Action not permitted.';
diff --git a/plugins/managesieve/managesieve.js b/plugins/managesieve/managesieve.js
index c0e78a609..90d12ea6d 100644
--- a/plugins/managesieve/managesieve.js
+++ b/plugins/managesieve/managesieve.js
@@ -492,6 +492,12 @@ rcube_webmail.prototype.managesieve_save = function()
return;
}
+ if (this.env.action == 'plugin.managesieve-forward') {
+ var data = $(this.gui_objects.sieveform).serialize();
+ this.http_post('plugin.managesieve-forward', data, this.display_message(this.get_label('managesieve.forward.saving'), 'loading'));
+ return;
+ }
+
if (this.gui_objects.sieveform) {
if (parent.rcmail && parent.rcmail.filters_list && this.gui_objects.sieveform.name != 'filtersetform') {
var id = parent.rcmail.filters_list.get_single_selection();
@@ -758,6 +764,7 @@ function action_type_select(id)
target_area: document.getElementById('action_target_area' + id),
flags: document.getElementById('action_flags' + id),
vacation: document.getElementById('action_vacation' + id),
+ forward: document.getElementById('action_forward' + id),
set: document.getElementById('action_set' + id),
notify: document.getElementById('action_notify' + id)
};
@@ -777,6 +784,9 @@ function action_type_select(id)
else if (v == 'vacation') {
enabled.vacation = 1;
}
+ else if (v == 'forward') {
+ enabled.forward = 1;
+ }
else if (v == 'set') {
enabled.set = 1;
}
diff --git a/plugins/managesieve/managesieve.php b/plugins/managesieve/managesieve.php
index c8241303c..e97072a8b 100644
--- a/plugins/managesieve/managesieve.php
+++ b/plugins/managesieve/managesieve.php
@@ -42,6 +42,7 @@ class managesieve extends rcube_plugin
$this->register_action('plugin.managesieve', array($this, 'managesieve_actions'));
$this->register_action('plugin.managesieve-action', array($this, 'managesieve_actions'));
$this->register_action('plugin.managesieve-vacation', array($this, 'managesieve_actions'));
+ $this->register_action('plugin.managesieve-forward', array($this, 'managesieve_actions'));
$this->register_action('plugin.managesieve-save', array($this, 'managesieve_save'));
$this->register_action('plugin.managesieve-saveraw', array($this, 'managesieve_saveraw'));
@@ -102,9 +103,10 @@ class managesieve extends rcube_plugin
$this->load_config();
$vacation_mode = (int) $this->rc->config->get('managesieve_vacation');
+ $forward_mode = (int) $this->rc->config->get('managesieve_forward');
// register Filters action
- if ($vacation_mode != 2) {
+ if ($vacation_mode != 2 && $forward_mode != 2) {
$args['actions'][] = array(
'action' => 'plugin.managesieve',
'class' => 'filter',
@@ -125,6 +127,17 @@ class managesieve extends rcube_plugin
);
}
+ // register Forward action
+ if ($forward_mode > 0) {
+ $args['actions'][] = array(
+ 'action' => 'plugin.managesieve-forward',
+ 'class' => 'forward',
+ 'label' => 'forward',
+ 'domain' => 'managesieve',
+ 'title' => 'forwardtitle',
+ );
+ }
+
return $args;
}
@@ -203,8 +216,9 @@ class managesieve extends rcube_plugin
// handle other actions
$engine_type = $this->rc->action == 'plugin.managesieve-vacation' ? 'vacation' : '';
- $engine = $this->get_engine($engine_type);
+ $engine_type = $this->rc->action == 'plugin.managesieve-forward' ? 'forward' : $engine_type;
+ $engine = $this->get_engine($engine_type);
$this->init_ui();
$engine->actions();
}
diff --git a/plugins/managesieve/skins/larry/managesieve.css b/plugins/managesieve/skins/larry/managesieve.css
index fde68a38a..846fc37ab 100644
--- a/plugins/managesieve/skins/larry/managesieve.css
+++ b/plugins/managesieve/skins/larry/managesieve.css
@@ -444,7 +444,7 @@ body.iframe.mail #filter-form
background-position: 7px -23px;
}
-#managesieve-vacation {
+#managesieve-vacation, #managesieve-forward {
position: absolute;
top: 0;
left: 212px;
diff --git a/plugins/managesieve/skins/larry/templates/forward.html b/plugins/managesieve/skins/larry/templates/forward.html
new file mode 100644
index 000000000..cf838076f
--- /dev/null
+++ b/plugins/managesieve/skins/larry/templates/forward.html
@@ -0,0 +1,32 @@
+