Overview

Packages

  • Horde
    • Icalendar
      • UnitTests
  • Ingo
    • UnitTests
  • None

Classes

  • Horde_Core_Ui_VarRenderer_Ingo
  • Ingo
  • Ingo_Api
  • Ingo_Exception
  • Ingo_Exception_Pear
  • Ingo_LoginTasks_SystemTask_Upgrade
  • Ingo_Script
  • Ingo_Script_Imap
  • Ingo_Script_Imap_Api
  • Ingo_Script_Imap_Live
  • Ingo_Script_Maildrop
  • Ingo_Script_Maildrop_Comment
  • Ingo_Script_Maildrop_Recipe
  • Ingo_Script_Maildrop_Variable
  • Ingo_Script_Procmail
  • Ingo_Script_Procmail_Comment
  • Ingo_Script_Procmail_Recipe
  • Ingo_Script_Procmail_Variable
  • Ingo_Script_Sieve
  • Ingo_Script_Sieve_Action
  • Ingo_Script_Sieve_Action_Addflag
  • Ingo_Script_Sieve_Action_Discard
  • Ingo_Script_Sieve_Action_Fileinto
  • Ingo_Script_Sieve_Action_Flag
  • Ingo_Script_Sieve_Action_Keep
  • Ingo_Script_Sieve_Action_Notify
  • Ingo_Script_Sieve_Action_Redirect
  • Ingo_Script_Sieve_Action_Reject
  • Ingo_Script_Sieve_Action_Removeflag
  • Ingo_Script_Sieve_Action_Stop
  • Ingo_Script_Sieve_Action_Vacation
  • Ingo_Script_Sieve_Comment
  • Ingo_Script_Sieve_Else
  • Ingo_Script_Sieve_Elsif
  • Ingo_Script_Sieve_If
  • Ingo_Script_Sieve_Test
  • Ingo_Script_Sieve_Test_Address
  • Ingo_Script_Sieve_Test_Allof
  • Ingo_Script_Sieve_Test_Anyof
  • Ingo_Script_Sieve_Test_Body
  • Ingo_Script_Sieve_Test_Exists
  • Ingo_Script_Sieve_Test_False
  • Ingo_Script_Sieve_Test_Header
  • Ingo_Script_Sieve_Test_Not
  • Ingo_Script_Sieve_Test_Relational
  • Ingo_Script_Sieve_Test_Size
  • Ingo_Script_Sieve_Test_True
  • Ingo_Storage
  • Ingo_Storage_Blacklist
  • Ingo_Storage_Filters
  • Ingo_Storage_Filters_Sql
  • Ingo_Storage_Forward
  • Ingo_Storage_Mock
  • Ingo_Storage_Prefs
  • Ingo_Storage_Rule
  • Ingo_Storage_Spam
  • Ingo_Storage_Sql
  • Ingo_Storage_Vacation
  • Ingo_Storage_VacationTest
  • Ingo_Storage_Whitelist
  • Ingo_Test
  • Ingo_Transport
  • Ingo_Transport_Ldap
  • Ingo_Transport_Null
  • Ingo_Transport_Sivtest
  • Ingo_Transport_Timsieved
  • Ingo_Transport_Vfs
  • Overview
  • Package
  • Class
  • Tree
  1: <?php
  2: /**
  3:  * The Ingo_Script_Maildrop_Recipe:: class represents a maildrop recipe.
  4:  *
  5:  * Copyright 2005-2007 Matt Weyland <mathias@weyland.ch>
  6:  *
  7:  * See the enclosed file LICENSE for license information (ASL).  If you
  8:  * did not receive this file, see http://www.horde.org/licenses/apache.
  9:  *
 10:  * @author  Matt Weyland <mathias@weyland.ch>
 11:  * @package Ingo
 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:      * Constructs a new maildrop recipe.
 56:      *
 57:      * @param array $params        Array of parameters.
 58:      *                             REQUIRED FIELDS:
 59:      *                             'action'
 60:      *                             OPTIONAL FIELDS:
 61:      *                             'action-value' (only used if the
 62:      *                             'action' requires it)
 63:      * @param array $scriptparams  Array of parameters passed to
 64:      *                             Ingo_Script_Maildrop::.
 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'; # EX_NOPERM (permanent failure)
 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:             /* Exclusion of addresses from vacation */
104:             if ($params['action-value']['excludes']) {
105:                 $exclude = implode('|', $params['action-value']['excludes']);
106:                 // Disable wildcard until officially supported.
107:                 // $exclude = str_replace('*', '(.*)', $exclude);
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:                 // Set to same value as $_days in ingo/lib/Storage.php
124:                 $days = 7;
125:             }
126: 
127:             // Rule : Do not send responses to bulk or list messages
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:             // Rule : Start/End of vacation
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:             /* The 'to' must be the last action, because maildrop
180:              * stops processing after it. */
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:      * Adds a flag to the recipe.
205:      *
206:      * @param string $flag  String of flags to append to the current flags.
207:      */
208:     public function addFlag($flag)
209:     {
210:         $this->_flags .= $flag;
211:     }
212: 
213:     /**
214:      * Adds a condition to the recipe.
215:      *
216:      * @param optonal array $condition  Array of parameters. Required keys
217:      *                                  are 'field' and 'value'. 'case' is
218:      *                                  an optional keys.
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:         // negate tests starting with 'not ', except 'not equals', which simply uses the != operator
232:         if ($match != 'not equal' && substr($match, 0, 4) == 'not ') {
233:             $string .= '! ';
234:         }
235: 
236:         // convert 'field' to PCRE pattern matching
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:             // Just run a match for the header name
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:      * Generates maildrop code to represent the recipe.
301:      *
302:      * @return string  maildrop code to represent the recipe.
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:      * Returns a maildrop-ready mailbox path, converting IMAP folder pathname
344:      * conventions as necessary.
345:      *
346:      * @param string $folder  The IMAP folder name.
347:      *
348:      * @return string  The maildrop mailbox path.
349:      */
350:     public function maildropPath($folder)
351:     {
352:         /* NOTE: '$DEFAULT' here is a literal, not a PHP variable. */
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: 
API documentation generated by ApiGen