1: <?php
2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12:
13: class Ingo_Script_Maildrop_Recipe
14: {
15: 16:
17: protected $_action = array();
18:
19: 20:
21: protected $_conditions = array();
22:
23: 24:
25: protected $_disable = '';
26:
27: 28:
29: protected $_flags = '';
30:
31: 32:
33: protected $_params = array();
34:
35: 36:
37: protected $_combine = '';
38:
39: 40:
41: protected $_valid = true;
42:
43: 44:
45: protected $_operators = array(
46: 'less than' => '<',
47: 'less than or equal to' => '<=',
48: 'equal' => '==',
49: 'not equal' => '!=',
50: 'greater than' => '>',
51: 'greater than or equal to' => '>=',
52: );
53:
54: 55: 56: 57: 58: 59: 60: 61: 62: 63: 64: 65:
66: public function __construct($params = array(), $scriptparams = array())
67: {
68: $this->_disable = !empty($params['disable']);
69: $this->_params = $scriptparams;
70: $this->_action[] = 'exception {';
71:
72: switch ($params['action']) {
73: case Ingo_Storage::ACTION_KEEP:
74: $this->_action[] = ' to "${DEFAULT}"';
75: break;
76:
77: case Ingo_Storage::ACTION_MOVE:
78: $this->_action[] = ' to ' . $this->maildropPath($params['action-value']);
79: break;
80:
81: case Ingo_Storage::ACTION_DISCARD:
82: $this->_action[] = ' exit';
83: break;
84:
85: case Ingo_Storage::ACTION_REDIRECT:
86: $this->_action[] = ' to "! ' . $params['action-value'] . '"';
87: break;
88:
89: case Ingo_Storage::ACTION_REDIRECTKEEP:
90: $this->_action[] = ' cc "! ' . $params['action-value'] . '"';
91: $this->_action[] = ' to "${DEFAULT}"';
92: break;
93:
94: case Ingo_Storage::ACTION_REJECT:
95: $this->_action[] = ' EXITCODE=77';
96: $this->_action[] = ' echo "5.7.1 ' . $params['action-value'] . '"';
97: $this->_action[] = ' exit';
98: break;
99:
100: case Ingo_Storage::ACTION_VACATION:
101: $from = reset($params['action-value']['addresses']);
102:
103:
104: if ($params['action-value']['excludes']) {
105: $exclude = implode('|', $params['action-value']['excludes']);
106:
107:
108: $this->addCondition(array('match' => 'filter',
109: 'field' => '',
110: 'value' => '! /^From:.*(' . $exclude . ')/'));
111: }
112:
113: $start = strftime($params['action-value']['start']);
114: if ($start === false) {
115: $start = 0;
116: }
117: $end = strftime($params['action-value']['end']);
118: if ($end === false) {
119: $end = 0;
120: }
121: $days = strftime($params['action-value']['days']);
122: if ($days === false) {
123:
124: $days = 7;
125: }
126:
127:
128: if ($params['action-value']['ignorelist'] == 1) {
129: $params['combine'] = Ingo_Storage::COMBINE_ALL;
130: $this->addCondition(array('match' => 'filter', 'field' => '', 'value' => '! /^Precedence: (bulk|list|junk)/'));
131: $this->addCondition(array('match' => 'filter', 'field' => '', 'value' => '! /^Return-Path:.*<#@\[\]>/'));
132: $this->addCondition(array('match' => 'filter', 'field' => '', 'value' => '! /^Return-Path:.*<>/'));
133: $this->addCondition(array('match' => 'filter', 'field' => '', 'value' => '! /^From:.*MAILER-DAEMON/'));
134: $this->addCondition(array('match' => 'filter', 'field' => '', 'value' => '! /^X-ClamAV-Notice-Flag: *YES/'));
135: $this->addCondition(array('match' => 'filter', 'field' => '', 'value' => '! /^Content-Type:.*message\/delivery-status/'));
136: $this->addCondition(array('match' => 'filter', 'field' => '', 'value' => '! /^Subject:.*Delivery Status Notification/'));
137: $this->addCondition(array('match' => 'filter', 'field' => '', 'value' => '! /^Subject:.*Undelivered Mail Returned to Sender/'));
138: $this->addCondition(array('match' => 'filter', 'field' => '', 'value' => '! /^Subject:.*Delivery failure/'));
139: $this->addCondition(array('match' => 'filter', 'field' => '', 'value' => '! /^Subject:.*Message delay/'));
140: $this->addCondition(array('match' => 'filter', 'field' => '', 'value' => '! /^Subject:.*Mail Delivery Subsystem/'));
141: $this->addCondition(array('match' => 'filter', 'field' => '', 'value' => '! /^Subject:.*Mail System Error.*Returned Mail/'));
142: $this->addCondition(array('match' => 'filter', 'field' => '', 'value' => '! /^X-Spam-Flag: YES/ '));
143: } else {
144: $this->addCondition(array('field' => 'From', 'value' => ''));
145: }
146:
147:
148: if (($start != 0) && ($end !== 0)) {
149: $this->_action[] = ' flock "vacationprocess.lock" {';
150: $this->_action[] = ' current_time=time';
151: $this->_action[] = ' if ( \ ';
152: $this->_action[] = ' ($current_time >= ' . $start . ') && \ ';
153: $this->_action[] = ' ($current_time <= ' . $end . ')) ';
154: $this->_action[] = ' {';
155: }
156: $this->_action[] = ' cc "' . str_replace('"', '\\"', sprintf(
157: '| mailbot %s -D %d -c \'UTF-8\' -t $HOME/vacation.msg -d $HOME/vacation -A %s -s %s /usr/sbin/sendmail -t -f %s',
158: $this->_params['mailbotargs'],
159: $params['action-value']['days'],
160: escapeshellarg('From: ' . $from),
161: escapeshellarg(Horde_Mime::encode($params['action-value']['subject'], 'UTF-8')),
162: escapeshellarg($from)))
163: . '"';
164: if (($start != 0) && ($end !== 0)) {
165: $this->_action[] = ' }';
166: $this->_action[] = ' }';
167: }
168:
169: break;
170:
171: case Ingo_Storage::ACTION_FORWARD:
172: case Ingo_Script_Maildrop::MAILDROP_STORAGE_ACTION_STOREANDFORWARD:
173: foreach ($params['action-value'] as $address) {
174: if (!empty($address)) {
175: $this->_action[] = ' cc "! ' . $address . '"';
176: }
177: }
178:
179: 180:
181: if ($params['action'] == Ingo_Script_Maildrop::MAILDROP_STORAGE_ACTION_STOREANDFORWARD) {
182: $this->_action[] = ' to "${DEFAULT}"';
183: } else {
184: $this->_action[] = ' exit';
185: }
186: break;
187:
188: default:
189: $this->_valid = false;
190: break;
191: }
192:
193: $this->_action[] = '}';
194:
195: if (isset($params['combine']) &&
196: ($params['combine'] == Ingo_Storage::COMBINE_ALL)) {
197: $this->_combine = '&& ';
198: } else {
199: $this->_combine = '|| ';
200: }
201: }
202:
203: 204: 205: 206: 207:
208: public function addFlag($flag)
209: {
210: $this->_flags .= $flag;
211: }
212:
213: 214: 215: 216: 217: 218: 219:
220: public function addCondition($condition = array())
221: {
222: $flag = (!empty($condition['case'])) ? 'D' : '';
223: if (empty($this->_conditions)) {
224: $this->addFlag($flag);
225: }
226:
227: $string = '';
228: $extra = '';
229:
230: $match = (isset($condition['match'])) ? $condition['match'] : null;
231:
232: if ($match != 'not equal' && substr($match, 0, 4) == 'not ') {
233: $string .= '! ';
234: }
235:
236:
237: if (strpos($condition['field'], ',') == false) {
238: $string .= '/^' . $condition['field'] . ':\\s*';
239: } else {
240: $string .= '/^(' . str_replace(',', '|', $condition['field']) . '):\\s*';
241: }
242:
243: switch ($match) {
244: case 'not regex':
245: case 'regex':
246: $string .= $condition['value'] . '/:h';
247: break;
248:
249: case 'filter':
250: $string = $condition['value'];
251: break;
252:
253: case 'exists':
254: case 'not exist':
255:
256: $string .= '/:h';
257: break;
258:
259: case 'less than or equal to':
260: case 'less than':
261: case 'equal':
262: case 'not equal':
263: case 'greater than or equal to':
264: case 'greater than':
265: $string .= '(\d+(\.\d+)?)/:h';
266: $extra = ' && $MATCH1 ' . $this->_operators[$match] . ' ' . (int)$condition['value'];
267: break;
268:
269: case 'begins with':
270: case 'not begins with':
271: $string .= preg_quote($condition['value'], '/') . '/:h';
272: break;
273:
274: case 'ends with':
275: case 'not ends with':
276: $string .= '.*' . preg_quote($condition['value'], '/') . '$/:h';
277: break;
278:
279: case 'is':
280: case 'not is':
281: $string .= preg_quote($condition['value'], '/') . '$/:h';
282: break;
283:
284: case 'matches':
285: case 'not matches':
286: $string .= str_replace(array('\\*', '\\?'), array('.*', '.'), preg_quote($condition['value'], '/') . '$') . '/:h';
287: break;
288:
289: case 'contains':
290: case 'not contain':
291: default:
292: $string .= '.*' . preg_quote($condition['value'], '/') . '/:h';
293: break;
294: }
295:
296: $this->_conditions[] = array('condition' => $string, 'flags' => $flag, 'extra' => $extra);
297: }
298:
299: 300: 301: 302: 303:
304: public function generate()
305: {
306: $text = array();
307:
308: if (!$this->_valid) {
309: return '';
310: }
311:
312: if (count($this->_conditions) > 0) {
313:
314: $text[] = "if( \\";
315:
316: $nest = false;
317: foreach ($this->_conditions as $condition) {
318: $cond = $nest ? $this->_combine : ' ';
319: $text[] = $cond . $condition['condition'] . $condition['flags'] . $condition['extra'] . " \\";
320: $nest = true;
321: }
322:
323: $text[] = ')';
324: }
325:
326: foreach ($this->_action as $val) {
327: $text[] = $val;
328: }
329:
330: if ($this->_disable) {
331: $code = '';
332: foreach ($text as $val) {
333: $comment = new Ingo_Script_Maildrop_Comment($val);
334: $code .= $comment->generate() . "\n";
335: }
336: return $code . "\n";
337: } else {
338: return implode("\n", $text) . "\n";
339: }
340: }
341:
342: 343: 344: 345: 346: 347: 348: 349:
350: public function maildropPath($folder)
351: {
352:
353: if (isset($this->_params) &&
354: ($this->_params['path_style'] == 'maildir')) {
355: if (empty($folder) || ($folder == 'INBOX')) {
356: return '"${DEFAULT}"';
357: }
358: if ($this->_params['strip_inbox'] &&
359: substr($folder, 0, 6) == 'INBOX.') {
360: $folder = substr($folder, 6);
361: }
362: return '"${DEFAULT}/.' . $folder . '/"';
363: } else {
364: if (empty($folder) || ($folder == 'INBOX')) {
365: return '${DEFAULT}';
366: }
367: return str_replace(' ', '\ ', $folder);
368: }
369: }
370:
371: }
372: