0
|
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 }
|