Mercurial > hg > rc1
comparison plugins/managesieve/lib/Roundcube/rcube_sieve_vacation.php @ 0:1e000243b222
vanilla 1.3.3 distro, I hope
| author | Charlie Root |
|---|---|
| date | Thu, 04 Jan 2018 15:50:29 -0500 |
| parents | |
| children |
comparison
equal
deleted
inserted
replaced
| -1:000000000000 | 0:1e000243b222 |
|---|---|
| 1 <?php | |
| 2 | |
| 3 /** | |
| 4 * Managesieve Vacation Engine | |
| 5 * | |
| 6 * Engine part of Managesieve plugin implementing UI and backend access. | |
| 7 * | |
| 8 * Copyright (C) 2011-2014, Kolab Systems AG | |
| 9 * | |
| 10 * This program is free software: you can redistribute it and/or modify | |
| 11 * it under the terms of the GNU General Public License as published by | |
| 12 * the Free Software Foundation, either version 3 of the License, or | |
| 13 * (at your option) any later version. | |
| 14 * | |
| 15 * This program is distributed in the hope that it will be useful, | |
| 16 * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
| 17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
| 18 * GNU General Public License for more details. | |
| 19 * | |
| 20 * You should have received a copy of the GNU General Public License | |
| 21 * along with this program. If not, see http://www.gnu.org/licenses/. | |
| 22 */ | |
| 23 | |
| 24 class rcube_sieve_vacation extends rcube_sieve_engine | |
| 25 { | |
| 26 protected $error; | |
| 27 protected $script_name; | |
| 28 protected $vacation = array(); | |
| 29 | |
| 30 function actions() | |
| 31 { | |
| 32 $error = $this->start('vacation'); | |
| 33 | |
| 34 // find current vacation rule | |
| 35 if (!$error) { | |
| 36 $this->vacation_rule(); | |
| 37 $this->vacation_post(); | |
| 38 } | |
| 39 | |
| 40 $this->plugin->add_label('vacation.saving'); | |
| 41 $this->rc->output->add_handlers(array( | |
| 42 'vacationform' => array($this, 'vacation_form'), | |
| 43 )); | |
| 44 | |
| 45 $this->rc->output->set_pagetitle($this->plugin->gettext('vacation')); | |
| 46 $this->rc->output->send('managesieve.vacation'); | |
| 47 } | |
| 48 | |
| 49 /** | |
| 50 * Find and load sieve script with/for vacation rule | |
| 51 * | |
| 52 * @param string $script_name Optional script name | |
| 53 * | |
| 54 * @return int Connection status: 0 on success, >0 on failure | |
| 55 */ | |
| 56 protected function load_script($script_name = null) | |
| 57 { | |
| 58 if ($this->script_name !== null) { | |
| 59 return 0; | |
| 60 } | |
| 61 | |
| 62 $list = $this->list_scripts(); | |
| 63 $master = $this->rc->config->get('managesieve_kolab_master'); | |
| 64 $included = array(); | |
| 65 | |
| 66 $this->script_name = false; | |
| 67 | |
| 68 // first try the active script(s)... | |
| 69 if (!empty($this->active)) { | |
| 70 // Note: there can be more than one active script on KEP:14-enabled server | |
| 71 foreach ($this->active as $script) { | |
| 72 if ($this->sieve->load($script)) { | |
| 73 foreach ($this->sieve->script->as_array() as $rule) { | |
| 74 if (!empty($rule['actions'])) { | |
| 75 if ($rule['actions'][0]['type'] == 'vacation') { | |
| 76 $this->script_name = $script; | |
| 77 return 0; | |
| 78 } | |
| 79 else if (empty($master) && $rule['actions'][0]['type'] == 'include') { | |
| 80 $included[] = $rule['actions'][0]['target']; | |
| 81 } | |
| 82 } | |
| 83 } | |
| 84 } | |
| 85 } | |
| 86 | |
| 87 // ...else try scripts included in active script (not for KEP:14) | |
| 88 foreach ($included as $script) { | |
| 89 if ($this->sieve->load($script)) { | |
| 90 foreach ($this->sieve->script->as_array() as $rule) { | |
| 91 if (!empty($rule['actions']) && $rule['actions'][0]['type'] == 'vacation') { | |
| 92 $this->script_name = $script; | |
| 93 return 0; | |
| 94 } | |
| 95 } | |
| 96 } | |
| 97 } | |
| 98 } | |
| 99 | |
| 100 // try all other scripts | |
| 101 if (!empty($list)) { | |
| 102 // else try included scripts | |
| 103 foreach (array_diff($list, $included, $this->active) as $script) { | |
| 104 if ($this->sieve->load($script)) { | |
| 105 foreach ($this->sieve->script->as_array() as $rule) { | |
| 106 if (!empty($rule['actions']) && $rule['actions'][0]['type'] == 'vacation') { | |
| 107 $this->script_name = $script; | |
| 108 return 0; | |
| 109 } | |
| 110 } | |
| 111 } | |
| 112 } | |
| 113 | |
| 114 // none of the scripts contains existing vacation rule | |
| 115 // use any (first) active or just existing script (in that order) | |
| 116 if (!empty($this->active)) { | |
| 117 $this->sieve->load($this->script_name = $this->active[0]); | |
| 118 } | |
| 119 else { | |
| 120 $this->sieve->load($this->script_name = $list[0]); | |
| 121 } | |
| 122 } | |
| 123 | |
| 124 return $this->sieve->error(); | |
| 125 } | |
| 126 | |
| 127 private function vacation_rule() | |
| 128 { | |
| 129 if ($this->script_name === false || $this->script_name === null || !$this->sieve->load($this->script_name)) { | |
| 130 return; | |
| 131 } | |
| 132 | |
| 133 $list = array(); | |
| 134 $active = in_array($this->script_name, $this->active); | |
| 135 | |
| 136 // find (first) vacation rule | |
| 137 foreach ($this->script as $idx => $rule) { | |
| 138 if (empty($this->vacation) && !empty($rule['actions']) && $rule['actions'][0]['type'] == 'vacation') { | |
| 139 foreach ($rule['actions'] as $act) { | |
| 140 if ($act['type'] == 'discard' || $act['type'] == 'keep') { | |
| 141 $action = $act['type']; | |
| 142 } | |
| 143 else if ($act['type'] == 'redirect') { | |
| 144 $action = $act['copy'] ? 'copy' : 'redirect'; | |
| 145 $target = $act['target']; | |
| 146 } | |
| 147 } | |
| 148 | |
| 149 $this->vacation = array_merge($rule['actions'][0], array( | |
| 150 'idx' => $idx, | |
| 151 'disabled' => $rule['disabled'] || !$active, | |
| 152 'name' => $rule['name'], | |
| 153 'tests' => $rule['tests'], | |
| 154 'action' => $action ?: 'keep', | |
| 155 'target' => $target, | |
| 156 )); | |
| 157 } | |
| 158 else if ($active) { | |
| 159 $list[$idx] = $rule['name']; | |
| 160 } | |
| 161 } | |
| 162 | |
| 163 $this->vacation['list'] = $list; | |
| 164 } | |
| 165 | |
| 166 private function vacation_post() | |
| 167 { | |
| 168 if (empty($_POST)) { | |
| 169 return; | |
| 170 } | |
| 171 | |
| 172 $date_extension = in_array('date', $this->exts); | |
| 173 $regex_extension = in_array('regex', $this->exts); | |
| 174 | |
| 175 // set user's timezone | |
| 176 try { | |
| 177 $timezone = new DateTimeZone($this->rc->config->get('timezone', 'GMT')); | |
| 178 } | |
| 179 catch (Exception $e) { | |
| 180 $timezone = new DateTimeZone('GMT'); | |
| 181 } | |
| 182 | |
| 183 $status = rcube_utils::get_input_value('vacation_status', rcube_utils::INPUT_POST); | |
| 184 $from = rcube_utils::get_input_value('vacation_from', rcube_utils::INPUT_POST); | |
| 185 $subject = rcube_utils::get_input_value('vacation_subject', rcube_utils::INPUT_POST, true); | |
| 186 $reason = rcube_utils::get_input_value('vacation_reason', rcube_utils::INPUT_POST, true); | |
| 187 $addresses = rcube_utils::get_input_value('vacation_addresses', rcube_utils::INPUT_POST, true); | |
| 188 $interval = rcube_utils::get_input_value('vacation_interval', rcube_utils::INPUT_POST); | |
| 189 $interval_type = rcube_utils::get_input_value('vacation_interval_type', rcube_utils::INPUT_POST); | |
| 190 $date_from = rcube_utils::get_input_value('vacation_datefrom', rcube_utils::INPUT_POST); | |
| 191 $date_to = rcube_utils::get_input_value('vacation_dateto', rcube_utils::INPUT_POST); | |
| 192 $time_from = rcube_utils::get_input_value('vacation_timefrom', rcube_utils::INPUT_POST); | |
| 193 $time_to = rcube_utils::get_input_value('vacation_timeto', rcube_utils::INPUT_POST); | |
| 194 $after = rcube_utils::get_input_value('vacation_after', rcube_utils::INPUT_POST); | |
| 195 $action = rcube_utils::get_input_value('vacation_action', rcube_utils::INPUT_POST); | |
| 196 $target = rcube_utils::get_input_value('action_target', rcube_utils::INPUT_POST, true); | |
| 197 $target_domain = rcube_utils::get_input_value('action_domain', rcube_utils::INPUT_POST); | |
| 198 | |
| 199 $interval_type = $interval_type == 'seconds' ? 'seconds' : 'days'; | |
| 200 $vacation_action['type'] = 'vacation'; | |
| 201 $vacation_action['reason'] = $this->strip_value(str_replace("\r\n", "\n", $reason)); | |
| 202 $vacation_action['subject'] = trim($subject); | |
| 203 $vacation_action['from'] = trim($from); | |
| 204 $vacation_action['addresses'] = $addresses; | |
| 205 $vacation_action[$interval_type] = $interval; | |
| 206 $vacation_tests = (array) $this->vacation['tests']; | |
| 207 | |
| 208 foreach ((array) $vacation_action['addresses'] as $aidx => $address) { | |
| 209 $vacation_action['addresses'][$aidx] = $address = trim($address); | |
| 210 | |
| 211 if (empty($address)) { | |
| 212 unset($vacation_action['addresses'][$aidx]); | |
| 213 } | |
| 214 else if (!rcube_utils::check_email($address)) { | |
| 215 $error = 'noemailwarning'; | |
| 216 break; | |
| 217 } | |
| 218 } | |
| 219 | |
| 220 if (!empty($vacation_action['from']) && !rcube_utils::check_email($vacation_action['from'])) { | |
| 221 $error = 'noemailwarning'; | |
| 222 } | |
| 223 | |
| 224 if ($vacation_action['reason'] == '') { | |
| 225 $error = 'managesieve.emptyvacationbody'; | |
| 226 } | |
| 227 | |
| 228 if ($vacation_action[$interval_type] && !preg_match('/^[0-9]+$/', $vacation_action[$interval_type])) { | |
| 229 $error = 'managesieve.forbiddenchars'; | |
| 230 } | |
| 231 | |
| 232 // find and remove existing date/regex/true rules | |
| 233 foreach ((array) $vacation_tests as $idx => $t) { | |
| 234 if ($t['test'] == 'currentdate' || $t['test'] == 'true' | |
| 235 || ($t['test'] == 'header' && $t['type'] == 'regex' && $t['arg1'] == 'received') | |
| 236 ) { | |
| 237 unset($vacation_tests[$idx]); | |
| 238 } | |
| 239 } | |
| 240 | |
| 241 if ($date_extension) { | |
| 242 $date_format = $this->rc->config->get('date_format', 'Y-m-d'); | |
| 243 foreach (array('date_from', 'date_to') as $var) { | |
| 244 $time = ${str_replace('date', 'time', $var)}; | |
| 245 $date = rcube_utils::format_datestr($$var, $date_format); | |
| 246 $date = trim($date . ' ' . $time); | |
| 247 | |
| 248 if ($date && ($dt = rcube_utils::anytodatetime($date, $timezone))) { | |
| 249 if ($time) { | |
| 250 $vacation_tests[] = array( | |
| 251 'test' => 'currentdate', | |
| 252 'part' => 'iso8601', | |
| 253 'type' => 'value-' . ($var == 'date_from' ? 'ge' : 'le'), | |
| 254 'zone' => $dt->format('O'), | |
| 255 'arg' => str_replace('+00:00', 'Z', strtoupper($dt->format('c'))), | |
| 256 ); | |
| 257 } | |
| 258 else { | |
| 259 $vacation_tests[] = array( | |
| 260 'test' => 'currentdate', | |
| 261 'part' => 'date', | |
| 262 'type' => 'value-' . ($var == 'date_from' ? 'ge' : 'le'), | |
| 263 'zone' => $dt->format('O'), | |
| 264 'arg' => $dt->format('Y-m-d'), | |
| 265 ); | |
| 266 } | |
| 267 } | |
| 268 } | |
| 269 } | |
| 270 else if ($regex_extension) { | |
| 271 // Add date range rules if range specified | |
| 272 if ($date_from && $date_to) { | |
| 273 if ($tests = self::build_regexp_tests($date_from, $date_to, $error)) { | |
| 274 $vacation_tests = array_merge($vacation_tests, $tests); | |
| 275 } | |
| 276 } | |
| 277 } | |
| 278 | |
| 279 if ($action == 'redirect' || $action == 'copy') { | |
| 280 if ($target_domain) { | |
| 281 $target .= '@' . $target_domain; | |
| 282 } | |
| 283 | |
| 284 if (empty($target) || !rcube_utils::check_email($target)) { | |
| 285 $error = 'noemailwarning'; | |
| 286 } | |
| 287 } | |
| 288 | |
| 289 if (empty($vacation_tests)) { | |
| 290 $vacation_tests = $this->rc->config->get('managesieve_vacation_test', array(array('test' => 'true'))); | |
| 291 } | |
| 292 | |
| 293 if (!$error) { | |
| 294 $rule = $this->vacation; | |
| 295 $rule['type'] = 'if'; | |
| 296 $rule['name'] = $rule['name'] ?: $this->plugin->gettext('vacation'); | |
| 297 $rule['disabled'] = $status == 'off'; | |
| 298 $rule['tests'] = $vacation_tests; | |
| 299 $rule['join'] = $date_extension ? count($vacation_tests) > 1 : false; | |
| 300 $rule['actions'] = array($vacation_action); | |
| 301 $rule['after'] = $after; | |
| 302 | |
| 303 if ($action && $action != 'keep') { | |
| 304 $rule['actions'][] = array( | |
| 305 'type' => $action == 'discard' ? 'discard' : 'redirect', | |
| 306 'copy' => $action == 'copy', | |
| 307 'target' => $action != 'discard' ? $target : '', | |
| 308 ); | |
| 309 } | |
| 310 | |
| 311 if ($this->save_vacation_script($rule)) { | |
| 312 $this->rc->output->show_message('managesieve.vacationsaved', 'confirmation'); | |
| 313 $this->rc->output->send(); | |
| 314 } | |
| 315 } | |
| 316 | |
| 317 $this->rc->output->show_message($error ?: 'managesieve.saveerror', 'error'); | |
| 318 $this->rc->output->send(); | |
| 319 } | |
| 320 | |
| 321 /** | |
| 322 * Independent vacation form | |
| 323 */ | |
| 324 public function vacation_form($attrib) | |
| 325 { | |
| 326 // check supported extensions | |
| 327 $date_extension = in_array('date', $this->exts); | |
| 328 $regex_extension = in_array('regex', $this->exts); | |
| 329 $seconds_extension = in_array('vacation-seconds', $this->exts); | |
| 330 | |
| 331 // build FORM tag | |
| 332 $form_id = $attrib['id'] ?: 'form'; | |
| 333 $out = $this->rc->output->request_form(array( | |
| 334 'id' => $form_id, | |
| 335 'name' => $form_id, | |
| 336 'method' => 'post', | |
| 337 'task' => 'settings', | |
| 338 'action' => 'plugin.managesieve-vacation', | |
| 339 'noclose' => true | |
| 340 ) + $attrib); | |
| 341 | |
| 342 $from_addr = $this->rc->config->get('managesieve_vacation_from_init'); | |
| 343 $auto_addr = $this->rc->config->get('managesieve_vacation_addresses_init'); | |
| 344 | |
| 345 if (count($this->vacation) < 2) { | |
| 346 if ($auto_addr) { | |
| 347 $this->vacation['addresses'] = $this->user_emails(); | |
| 348 } | |
| 349 if ($from_addr) { | |
| 350 $default_identity = $this->rc->user->list_emails(true); | |
| 351 $this->vacation['from'] = $default_identity['email']; | |
| 352 } | |
| 353 } | |
| 354 | |
| 355 // form elements | |
| 356 $from = new html_inputfield(array('name' => 'vacation_from', 'id' => 'vacation_from', 'size' => 50)); | |
| 357 $subject = new html_inputfield(array('name' => 'vacation_subject', 'id' => 'vacation_subject', 'size' => 50)); | |
| 358 $reason = new html_textarea(array('name' => 'vacation_reason', 'id' => 'vacation_reason', 'cols' => 60, 'rows' => 8)); | |
| 359 $interval = new html_inputfield(array('name' => 'vacation_interval', 'id' => 'vacation_interval', 'size' => 5)); | |
| 360 $addresses = '<textarea name="vacation_addresses" id="vacation_addresses" data-type="list" data-size="30" style="display: none">' | |
| 361 . rcube::Q(implode("\n", (array) $this->vacation['addresses']), 'strict', false) . '</textarea>'; | |
| 362 $status = new html_select(array('name' => 'vacation_status', 'id' => 'vacation_status')); | |
| 363 $action = new html_select(array('name' => 'vacation_action', 'id' => 'vacation_action', 'onchange' => 'vacation_action_select()')); | |
| 364 $addresses_link = new html_inputfield(array( | |
| 365 'type' => 'button', | |
| 366 'href' => '#', | |
| 367 'class' => 'button', | |
| 368 'onclick' => rcmail_output::JS_OBJECT_NAME . '.managesieve_vacation_addresses()' | |
| 369 )); | |
| 370 | |
| 371 $status->add($this->plugin->gettext('vacation.on'), 'on'); | |
| 372 $status->add($this->plugin->gettext('vacation.off'), 'off'); | |
| 373 | |
| 374 $action->add($this->plugin->gettext('vacation.keep'), 'keep'); | |
| 375 $action->add($this->plugin->gettext('vacation.discard'), 'discard'); | |
| 376 $action->add($this->plugin->gettext('vacation.redirect'), 'redirect'); | |
| 377 if (in_array('copy', $this->exts)) { | |
| 378 $action->add($this->plugin->gettext('vacation.copy'), 'copy'); | |
| 379 } | |
| 380 | |
| 381 if ($this->rc->config->get('managesieve_vacation') != 2 && count($this->vacation['list'])) { | |
| 382 $after = new html_select(array('name' => 'vacation_after', 'id' => 'vacation_after')); | |
| 383 | |
| 384 $after->add('', ''); | |
| 385 foreach ($this->vacation['list'] as $idx => $rule) { | |
| 386 $after->add($rule, $idx); | |
| 387 } | |
| 388 } | |
| 389 | |
| 390 $interval_txt = $interval->show(self::vacation_interval($this->vacation)); | |
| 391 if ($seconds_extension) { | |
| 392 $interval_select = new html_select(array('name' => 'vacation_interval_type')); | |
| 393 $interval_select->add($this->plugin->gettext('days'), 'days'); | |
| 394 $interval_select->add($this->plugin->gettext('seconds'), 'seconds'); | |
| 395 $interval_txt .= ' ' . $interval_select->show(isset($this->vacation['seconds']) ? 'seconds' : 'days'); | |
| 396 } | |
| 397 else { | |
| 398 $interval_txt .= ' ' . $this->plugin->gettext('days'); | |
| 399 } | |
| 400 | |
| 401 if ($date_extension || $regex_extension) { | |
| 402 $date_from = new html_inputfield(array('name' => 'vacation_datefrom', 'id' => 'vacation_datefrom', 'class' => 'datepicker', 'size' => 12)); | |
| 403 $date_to = new html_inputfield(array('name' => 'vacation_dateto', 'id' => 'vacation_dateto', 'class' => 'datepicker', 'size' => 12)); | |
| 404 $date_format = $this->rc->config->get('date_format', 'Y-m-d'); | |
| 405 } | |
| 406 | |
| 407 if ($date_extension) { | |
| 408 $time_from = new html_inputfield(array('name' => 'vacation_timefrom', 'id' => 'vacation_timefrom', 'size' => 7)); | |
| 409 $time_to = new html_inputfield(array('name' => 'vacation_timeto', 'id' => 'vacation_timeto', 'size' => 7)); | |
| 410 $time_format = $this->rc->config->get('time_format', 'H:i'); | |
| 411 $date_value = array(); | |
| 412 | |
| 413 foreach ((array) $this->vacation['tests'] as $test) { | |
| 414 if ($test['test'] == 'currentdate') { | |
| 415 $idx = $test['type'] == 'value-ge' ? 'from' : 'to'; | |
| 416 | |
| 417 if ($test['part'] == 'date') { | |
| 418 $date_value[$idx]['date'] = $test['arg']; | |
| 419 } | |
| 420 else if ($test['part'] == 'iso8601') { | |
| 421 $date_value[$idx]['datetime'] = $test['arg']; | |
| 422 } | |
| 423 } | |
| 424 } | |
| 425 | |
| 426 foreach ($date_value as $idx => $value) { | |
| 427 $date = $value['datetime'] ?: $value['date']; | |
| 428 $date_value[$idx] = $this->rc->format_date($date, $date_format, false); | |
| 429 | |
| 430 if (!empty($value['datetime'])) { | |
| 431 $date_value['time_' . $idx] = $this->rc->format_date($date, $time_format, true); | |
| 432 } | |
| 433 } | |
| 434 } | |
| 435 else if ($regex_extension) { | |
| 436 // Sieve 'date' extension not available, read start/end from RegEx based rules instead | |
| 437 if ($date_tests = self::parse_regexp_tests($this->vacation['tests'])) { | |
| 438 $date_value['from'] = $this->rc->format_date($date_tests['from'], $date_format, false); | |
| 439 $date_value['to'] = $this->rc->format_date($date_tests['to'], $date_format, false); | |
| 440 } | |
| 441 } | |
| 442 | |
| 443 // force domain selection in redirect email input | |
| 444 $domains = (array) $this->rc->config->get('managesieve_domains'); | |
| 445 $redirect = $this->vacation['action'] == 'redirect' || $this->vacation['action'] == 'copy'; | |
| 446 | |
| 447 if (!empty($domains)) { | |
| 448 sort($domains); | |
| 449 | |
| 450 $domain_select = new html_select(array('name' => 'action_domain', 'id' => 'action_domain')); | |
| 451 $domain_select->add(array_combine($domains, $domains)); | |
| 452 | |
| 453 if ($redirect && $this->vacation['target']) { | |
| 454 $parts = explode('@', $this->vacation['target']); | |
| 455 if (!empty($parts)) { | |
| 456 $this->vacation['domain'] = array_pop($parts); | |
| 457 $this->vacation['target'] = implode('@', $parts); | |
| 458 } | |
| 459 } | |
| 460 } | |
| 461 | |
| 462 // redirect target | |
| 463 $action_target = ' <span id="action_target_span" style="display:' . ($redirect ? 'inline' : 'none') . '">' | |
| 464 . '<input type="text" name="action_target" id="action_target"' | |
| 465 . ' value="' .($redirect ? rcube::Q($this->vacation['target'], 'strict', false) : '') . '"' | |
| 466 . (!empty($domains) ? ' size="20"' : ' size="35"') . '/>' | |
| 467 . (!empty($domains) ? ' @ ' . $domain_select->show($this->vacation['domain']) : '') | |
| 468 . '</span>'; | |
| 469 | |
| 470 // Message tab | |
| 471 $table = new html_table(array('cols' => 2)); | |
| 472 | |
| 473 $table->add('title', html::label('vacation_subject', $this->plugin->gettext('vacation.subject'))); | |
| 474 $table->add(null, $subject->show($this->vacation['subject'])); | |
| 475 $table->add('title', html::label('vacation_reason', $this->plugin->gettext('vacation.body'))); | |
| 476 $table->add(null, $reason->show($this->vacation['reason'])); | |
| 477 | |
| 478 if ($date_extension || $regex_extension) { | |
| 479 $table->add('title', html::label('vacation_datefrom', $this->plugin->gettext('vacation.start'))); | |
| 480 $table->add(null, $date_from->show($date_value['from']) . ($time_from ? ' ' . $time_from->show($date_value['time_from']) : '')); | |
| 481 $table->add('title', html::label('vacation_dateto', $this->plugin->gettext('vacation.end'))); | |
| 482 $table->add(null, $date_to->show($date_value['to']) . ($time_to ? ' ' . $time_to->show($date_value['time_to']) : '')); | |
| 483 } | |
| 484 | |
| 485 $table->add('title', html::label('vacation_status', $this->plugin->gettext('vacation.status'))); | |
| 486 $table->add(null, $status->show(!isset($this->vacation['disabled']) || $this->vacation['disabled'] ? 'off' : 'on')); | |
| 487 | |
| 488 $out .= html::tag('fieldset', $class, html::tag('legend', null, $this->plugin->gettext('vacation.reply')) . $table->show($attrib)); | |
| 489 | |
| 490 // Advanced tab | |
| 491 $table = new html_table(array('cols' => 2)); | |
| 492 | |
| 493 $table->add('title', html::label('vacation_from', $this->plugin->gettext('vacation.from'))); | |
| 494 $table->add(null, $from->show($this->vacation['from'])); | |
| 495 $table->add('title', html::label('vacation_addresses', $this->plugin->gettext('vacation.addresses'))); | |
| 496 $table->add(null, $addresses . $addresses_link->show($this->plugin->gettext('filladdresses'))); | |
| 497 $table->add('title', html::label('vacation_interval', $this->plugin->gettext('vacation.interval'))); | |
| 498 $table->add(null, $interval_txt); | |
| 499 | |
| 500 if ($after) { | |
| 501 $table->add('title', html::label('vacation_after', $this->plugin->gettext('vacation.after'))); | |
| 502 $table->add(null, $after->show($this->vacation['idx'] - 1)); | |
| 503 } | |
| 504 | |
| 505 $table->add('title', html::label('vacation_action', $this->plugin->gettext('vacation.action'))); | |
| 506 $table->add('vacation', $action->show($this->vacation['action']) . $action_target); | |
| 507 | |
| 508 $out .= html::tag('fieldset', $class, html::tag('legend', null, $this->plugin->gettext('vacation.advanced')) . $table->show($attrib)); | |
| 509 | |
| 510 $out .= '</form>'; | |
| 511 | |
| 512 $this->rc->output->add_gui_object('sieveform', $form_id); | |
| 513 | |
| 514 if ($time_format) { | |
| 515 $this->rc->output->set_env('time_format', $time_format); | |
| 516 } | |
| 517 | |
| 518 return $out; | |
| 519 } | |
| 520 | |
| 521 public static function build_regexp_tests($date_from, $date_to, &$error) | |
| 522 { | |
| 523 $tests = array(); | |
| 524 $dt_from = rcube_utils::anytodatetime($date_from); | |
| 525 $dt_to = rcube_utils::anytodatetime($date_to); | |
| 526 $interval = $dt_from->diff($dt_to); | |
| 527 | |
| 528 if ($interval->invert || $interval->days > 365) { | |
| 529 $error = 'managesieve.invaliddateformat'; | |
| 530 return; | |
| 531 } | |
| 532 | |
| 533 $dt_i = $dt_from; | |
| 534 $interval = new DateInterval('P1D'); | |
| 535 $matchexp = ''; | |
| 536 | |
| 537 while (!$dt_i->diff($dt_to)->invert) { | |
| 538 $days = (int) $dt_i->format('d'); | |
| 539 $matchexp .= $days < 10 ? "[ 0]$days" : $days; | |
| 540 | |
| 541 if ($days == $dt_i->format('t') || $dt_i->diff($dt_to)->days == 0) { | |
| 542 $test = array( | |
| 543 'test' => 'header', | |
| 544 'type' => 'regex', | |
| 545 'arg1' => 'received', | |
| 546 'arg2' => '('.$matchexp.') '.$dt_i->format('M Y') | |
| 547 ); | |
| 548 | |
| 549 $tests[] = $test; | |
| 550 $matchexp = ''; | |
| 551 } | |
| 552 else { | |
| 553 $matchexp .= '|'; | |
| 554 } | |
| 555 | |
| 556 $dt_i->add($interval); | |
| 557 } | |
| 558 | |
| 559 return $tests; | |
| 560 } | |
| 561 | |
| 562 public static function parse_regexp_tests($tests) | |
| 563 { | |
| 564 $rx_from = '/^\(([0-9]{2}).*\)\s([A-Za-z]+)\s([0-9]{4})/'; | |
| 565 $rx_to = '/^\(.*([0-9]{2})\)\s([A-Za-z]+)\s([0-9]{4})/'; | |
| 566 $result = array(); | |
| 567 | |
| 568 foreach ((array) $tests as $test) { | |
| 569 if ($test['test'] == 'header' && $test['type'] == 'regex' && $test['arg1'] == 'received') { | |
| 570 $textexp = preg_replace('/\[ ([^\]]*)\]/', '0', $test['arg2']); | |
| 571 | |
| 572 if (!$result['from'] && preg_match($rx_from, $textexp, $matches)) { | |
| 573 $result['from'] = $matches[1]." ".$matches[2]." ".$matches[3]; | |
| 574 } | |
| 575 | |
| 576 if (preg_match($rx_to, $textexp, $matches)) { | |
| 577 $result['to'] = $matches[1]." ".$matches[2]." ".$matches[3]; | |
| 578 } | |
| 579 } | |
| 580 } | |
| 581 | |
| 582 return $result; | |
| 583 } | |
| 584 | |
| 585 /** | |
| 586 * Get current vacation interval | |
| 587 */ | |
| 588 public static function vacation_interval(&$vacation) | |
| 589 { | |
| 590 $rcube = rcube::get_instance(); | |
| 591 | |
| 592 if (isset($vacation['seconds'])) { | |
| 593 $interval = $vacation['seconds']; | |
| 594 } | |
| 595 else if (isset($vacation['days'])) { | |
| 596 $interval = $vacation['days']; | |
| 597 } | |
| 598 else if ($interval_cfg = $rcube->config->get('managesieve_vacation_interval')) { | |
| 599 if (preg_match('/^([0-9]+)s$/', $interval_cfg, $m)) { | |
| 600 if ($seconds_extension) { | |
| 601 $vacation['seconds'] = ($interval = intval($m[1])) ? $interval : null; | |
| 602 } | |
| 603 else { | |
| 604 $vacation['days'] = $interval = ceil(intval($m[1])/86400); | |
| 605 } | |
| 606 } | |
| 607 else { | |
| 608 $vacation['days'] = $interval = intval($interval_cfg); | |
| 609 } | |
| 610 } | |
| 611 | |
| 612 return $interval ?: ''; | |
| 613 } | |
| 614 | |
| 615 /** | |
| 616 * Saves vacation script (adding some variables) | |
| 617 */ | |
| 618 protected function save_vacation_script($rule) | |
| 619 { | |
| 620 // if script does not exist create a new one | |
| 621 if ($this->script_name === null || $this->script_name === false) { | |
| 622 $this->script_name = $this->rc->config->get('managesieve_script_name'); | |
| 623 if (empty($this->script_name)) { | |
| 624 $this->script_name = 'roundcube'; | |
| 625 } | |
| 626 | |
| 627 // use default script contents | |
| 628 if (!$this->rc->config->get('managesieve_kolab_master')) { | |
| 629 $script_file = $this->rc->config->get('managesieve_default'); | |
| 630 if ($script_file && is_readable($script_file)) { | |
| 631 $content = file_get_contents($script_file); | |
| 632 } | |
| 633 } | |
| 634 | |
| 635 // create and load script | |
| 636 if ($this->sieve->save_script($this->script_name, $content)) { | |
| 637 $this->sieve->load($this->script_name); | |
| 638 } | |
| 639 } | |
| 640 | |
| 641 $script_active = in_array($this->script_name, $this->active); | |
| 642 | |
| 643 // re-order rules if needed | |
| 644 if (isset($rule['after']) && $rule['after'] !== '') { | |
| 645 // reset original vacation rule | |
| 646 if (isset($this->vacation['idx'])) { | |
| 647 $this->script[$this->vacation['idx']] = null; | |
| 648 } | |
| 649 | |
| 650 // add at target position | |
| 651 if ($rule['after'] >= count($this->script) - 1) { | |
| 652 $this->script[] = $rule; | |
| 653 } | |
| 654 else { | |
| 655 $script = array(); | |
| 656 | |
| 657 foreach ($this->script as $idx => $r) { | |
| 658 if ($r) { | |
| 659 $script[] = $r; | |
| 660 } | |
| 661 | |
| 662 if ($idx == $rule['after']) { | |
| 663 $script[] = $rule; | |
| 664 } | |
| 665 } | |
| 666 | |
| 667 $this->script = $script; | |
| 668 } | |
| 669 | |
| 670 $this->script = array_values(array_filter($this->script)); | |
| 671 } | |
| 672 // update original vacation rule if it exists | |
| 673 else if (isset($this->vacation['idx'])) { | |
| 674 $this->script[$this->vacation['idx']] = $rule; | |
| 675 } | |
| 676 // otherwise put vacation rule on top | |
| 677 else { | |
| 678 array_unshift($this->script, $rule); | |
| 679 } | |
| 680 | |
| 681 // if the script was not active, we need to de-activate | |
| 682 // all rules except the vacation rule, but only if it is not disabled | |
| 683 if (!$script_active && !$rule['disabled']) { | |
| 684 foreach ($this->script as $idx => $r) { | |
| 685 if (empty($r['actions']) || $r['actions'][0]['type'] != 'vacation') { | |
| 686 $this->script[$idx]['disabled'] = true; | |
| 687 } | |
| 688 } | |
| 689 } | |
| 690 | |
| 691 if (!$this->sieve->script) { | |
| 692 return false; | |
| 693 } | |
| 694 | |
| 695 $this->sieve->script->content = $this->script; | |
| 696 | |
| 697 // save the script | |
| 698 $saved = $this->save_script($this->script_name); | |
| 699 | |
| 700 // activate the script | |
| 701 if ($saved && !$script_active && !$rule['disabled']) { | |
| 702 $this->activate_script($this->script_name); | |
| 703 } | |
| 704 | |
| 705 return $saved; | |
| 706 } | |
| 707 | |
| 708 /** | |
| 709 * API: get vacation rule | |
| 710 * | |
| 711 * @return array Vacation rule information | |
| 712 */ | |
| 713 public function get_vacation() | |
| 714 { | |
| 715 $this->exts = $this->sieve->get_extensions(); | |
| 716 $this->init_script(); | |
| 717 $this->vacation_rule(); | |
| 718 | |
| 719 // check supported extensions | |
| 720 $date_extension = in_array('date', $this->exts); | |
| 721 $regex_extension = in_array('regex', $this->exts); | |
| 722 $seconds_extension = in_array('vacation-seconds', $this->exts); | |
| 723 | |
| 724 // set user's timezone | |
| 725 try { | |
| 726 $timezone = new DateTimeZone($this->rc->config->get('timezone', 'GMT')); | |
| 727 } | |
| 728 catch (Exception $e) { | |
| 729 $timezone = new DateTimeZone('GMT'); | |
| 730 } | |
| 731 | |
| 732 if ($date_extension) { | |
| 733 $date_value = array(); | |
| 734 foreach ((array) $this->vacation['tests'] as $test) { | |
| 735 if ($test['test'] == 'currentdate') { | |
| 736 $idx = $test['type'] == 'value-ge' ? 'start' : 'end'; | |
| 737 | |
| 738 if ($test['part'] == 'date') { | |
| 739 $date_value[$idx]['date'] = $test['arg']; | |
| 740 } | |
| 741 else if ($test['part'] == 'iso8601') { | |
| 742 $date_value[$idx]['datetime'] = $test['arg']; | |
| 743 } | |
| 744 } | |
| 745 } | |
| 746 | |
| 747 foreach ($date_value as $idx => $value) { | |
| 748 $$idx = new DateTime($value['datetime'] ?: $value['date'], $timezone); | |
| 749 } | |
| 750 } | |
| 751 else if ($regex_extension) { | |
| 752 // Sieve 'date' extension not available, read start/end from RegEx based rules instead | |
| 753 if ($date_tests = self::parse_regexp_tests($this->vacation['tests'])) { | |
| 754 $from = new DateTime($date_tests['from'] . ' ' . '00:00:00', $timezone); | |
| 755 $to = new DateTime($date_tests['to'] . ' ' . '23:59:59', $timezone); | |
| 756 } | |
| 757 } | |
| 758 | |
| 759 if (isset($this->vacation['seconds'])) { | |
| 760 $interval = $this->vacation['seconds'] . 's'; | |
| 761 } | |
| 762 else if (isset($this->vacation['days'])) { | |
| 763 $interval = $this->vacation['days'] . 'd'; | |
| 764 } | |
| 765 | |
| 766 $vacation = array( | |
| 767 'supported' => $this->exts, | |
| 768 'interval' => $interval, | |
| 769 'start' => $start, | |
| 770 'end' => $end, | |
| 771 'enabled' => $this->vacation['reason'] && empty($this->vacation['disabled']), | |
| 772 'message' => $this->vacation['reason'], | |
| 773 'subject' => $this->vacation['subject'], | |
| 774 'action' => $this->vacation['action'], | |
| 775 'target' => $this->vacation['target'], | |
| 776 'addresses' => $this->vacation['addresses'], | |
| 777 'from' => $this->vacation['from'], | |
| 778 ); | |
| 779 | |
| 780 return $vacation; | |
| 781 } | |
| 782 | |
| 783 /** | |
| 784 * API: set vacation rule | |
| 785 * | |
| 786 * @param array $vacation Vacation rule information (see self::get_vacation()) | |
| 787 * | |
| 788 * @return bool True on success, False on failure | |
| 789 */ | |
| 790 public function set_vacation($data) | |
| 791 { | |
| 792 $this->exts = $this->sieve->get_extensions(); | |
| 793 $this->error = false; | |
| 794 | |
| 795 $this->init_script(); | |
| 796 $this->vacation_rule(); | |
| 797 | |
| 798 // check supported extensions | |
| 799 $date_extension = in_array('date', $this->exts); | |
| 800 $regex_extension = in_array('regex', $this->exts); | |
| 801 $seconds_extension = in_array('vacation-seconds', $this->exts); | |
| 802 | |
| 803 $vacation['type'] = 'vacation'; | |
| 804 $vacation['reason'] = $this->strip_value(str_replace("\r\n", "\n", $data['message'])); | |
| 805 $vacation['addresses'] = $data['addresses']; | |
| 806 $vacation['subject'] = trim($data['subject']); | |
| 807 $vacation['from'] = trim($data['from']); | |
| 808 $vacation_tests = (array) $this->vacation['tests']; | |
| 809 | |
| 810 foreach ((array) $vacation['addresses'] as $aidx => $address) { | |
| 811 $vacation['addresses'][$aidx] = $address = trim($address); | |
| 812 | |
| 813 if (empty($address)) { | |
| 814 unset($vacation['addresses'][$aidx]); | |
| 815 } | |
| 816 else if (!rcube_utils::check_email($address)) { | |
| 817 $this->error = "Invalid address in vacation addresses: $address"; | |
| 818 return false; | |
| 819 } | |
| 820 } | |
| 821 | |
| 822 if (!empty($vacation['from']) && !rcube_utils::check_email($vacation['from'])) { | |
| 823 $this->error = "Invalid address in 'from': " . $vacation['from']; | |
| 824 return false; | |
| 825 } | |
| 826 | |
| 827 if ($vacation['reason'] == '') { | |
| 828 $this->error = "No vacation message specified"; | |
| 829 return false; | |
| 830 } | |
| 831 | |
| 832 if ($data['interval']) { | |
| 833 if (!preg_match('/^([0-9]+)\s*([sd])$/', $data['interval'], $m)) { | |
| 834 $this->error = "Invalid vacation interval value: " . $data['interval']; | |
| 835 return false; | |
| 836 } | |
| 837 else if ($m[1]) { | |
| 838 $vacation[strtolower($m[2]) == 's' ? 'seconds' : 'days'] = $m[1]; | |
| 839 } | |
| 840 } | |
| 841 | |
| 842 // find and remove existing date/regex/true rules | |
| 843 foreach ((array) $vacation_tests as $idx => $t) { | |
| 844 if ($t['test'] == 'currentdate' || $t['test'] == 'true' | |
| 845 || ($t['test'] == 'header' && $t['type'] == 'regex' && $t['arg1'] == 'received') | |
| 846 ) { | |
| 847 unset($vacation_tests[$idx]); | |
| 848 } | |
| 849 } | |
| 850 | |
| 851 if ($date_extension) { | |
| 852 foreach (array('start', 'end') as $var) { | |
| 853 if ($dt = $data[$var]) { | |
| 854 $vacation_tests[] = array( | |
| 855 'test' => 'currentdate', | |
| 856 'part' => 'iso8601', | |
| 857 'type' => 'value-' . ($var == 'start' ? 'ge' : 'le'), | |
| 858 'zone' => $dt->format('O'), | |
| 859 'arg' => str_replace('+00:00', 'Z', strtoupper($dt->format('c'))), | |
| 860 ); | |
| 861 } | |
| 862 } | |
| 863 } | |
| 864 else if ($regex_extension) { | |
| 865 // Add date range rules if range specified | |
| 866 if ($data['start'] && $data['end']) { | |
| 867 if ($tests = self::build_regexp_tests($data['start'], $data['end'], $error)) { | |
| 868 $vacation_tests = array_merge($vacation_tests, $tests); | |
| 869 } | |
| 870 | |
| 871 if ($error) { | |
| 872 $this->error = "Invalid dates specified or unsupported period length"; | |
| 873 return false; | |
| 874 } | |
| 875 } | |
| 876 } | |
| 877 | |
| 878 if ($data['action'] == 'redirect' || $data['action'] == 'copy') { | |
| 879 if (empty($data['target']) || !rcube_utils::check_email($data['target'])) { | |
| 880 $this->error = "Invalid address in action taget: " . $data['target']; | |
| 881 return false; | |
| 882 } | |
| 883 } | |
| 884 else if ($data['action'] && $data['action'] != 'keep' && $data['action'] != 'discard') { | |
| 885 $this->error = "Unsupported vacation action: " . $data['action']; | |
| 886 return false; | |
| 887 } | |
| 888 | |
| 889 if (empty($vacation_tests)) { | |
| 890 $vacation_tests = $this->rc->config->get('managesieve_vacation_test', array(array('test' => 'true'))); | |
| 891 } | |
| 892 | |
| 893 $rule = $this->vacation; | |
| 894 $rule['type'] = 'if'; | |
| 895 $rule['name'] = $rule['name'] ?: 'Out-of-Office'; | |
| 896 $rule['disabled'] = isset($data['enabled']) && !$data['enabled']; | |
| 897 $rule['tests'] = $vacation_tests; | |
| 898 $rule['join'] = $date_extension ? count($vacation_tests) > 1 : false; | |
| 899 $rule['actions'] = array($vacation); | |
| 900 | |
| 901 if ($data['action'] && $data['action'] != 'keep') { | |
| 902 $rule['actions'][] = array( | |
| 903 'type' => $data['action'] == 'discard' ? 'discard' : 'redirect', | |
| 904 'copy' => $data['action'] == 'copy', | |
| 905 'target' => $data['action'] != 'discard' ? $data['target'] : '', | |
| 906 ); | |
| 907 } | |
| 908 | |
| 909 return $this->save_vacation_script($rule); | |
| 910 } | |
| 911 | |
| 912 /** | |
| 913 * API: connect to managesieve server | |
| 914 */ | |
| 915 public function connect($username, $password) | |
| 916 { | |
| 917 $error = parent::connect($username, $password); | |
| 918 | |
| 919 if ($error) { | |
| 920 return $error; | |
| 921 } | |
| 922 | |
| 923 return $this->load_script(); | |
| 924 } | |
| 925 | |
| 926 /** | |
| 927 * API: Returns last error | |
| 928 * | |
| 929 * @return string Error message | |
| 930 */ | |
| 931 public function get_error() | |
| 932 { | |
| 933 return $this->error; | |
| 934 } | |
| 935 } |
